summaryrefslogtreecommitdiffstats
path: root/usr.sbin
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/IPXrouted/IPXrouted.8224
-rw-r--r--usr.sbin/IPXrouted/Makefile11
-rw-r--r--usr.sbin/IPXrouted/af.c294
-rw-r--r--usr.sbin/IPXrouted/af.h77
-rw-r--r--usr.sbin/IPXrouted/defs.h108
-rw-r--r--usr.sbin/IPXrouted/if.c151
-rw-r--r--usr.sbin/IPXrouted/input.c304
-rw-r--r--usr.sbin/IPXrouted/interface.h95
-rw-r--r--usr.sbin/IPXrouted/main.c401
-rw-r--r--usr.sbin/IPXrouted/output.c231
-rw-r--r--usr.sbin/IPXrouted/protocol.h92
-rw-r--r--usr.sbin/IPXrouted/sap.h108
-rw-r--r--usr.sbin/IPXrouted/sap_input.c215
-rw-r--r--usr.sbin/IPXrouted/sap_output.c198
-rw-r--r--usr.sbin/IPXrouted/sap_tables.c321
-rw-r--r--usr.sbin/IPXrouted/startup.c278
-rw-r--r--usr.sbin/IPXrouted/table.h115
-rw-r--r--usr.sbin/IPXrouted/tables.c423
-rw-r--r--usr.sbin/IPXrouted/timer.c239
-rw-r--r--usr.sbin/IPXrouted/trace.c520
-rw-r--r--usr.sbin/IPXrouted/trace.h138
-rw-r--r--usr.sbin/Makefile510
-rw-r--r--usr.sbin/Makefile.inc4
-rw-r--r--usr.sbin/ac/Makefile25
-rw-r--r--usr.sbin/ac/ac.8155
-rw-r--r--usr.sbin/ac/ac.c668
-rw-r--r--usr.sbin/accton/Makefile9
-rw-r--r--usr.sbin/accton/accton.842
-rw-r--r--usr.sbin/accton/accton.c87
-rw-r--r--usr.sbin/acpi/Makefile7
-rw-r--r--usr.sbin/acpi/Makefile.inc11
-rw-r--r--usr.sbin/acpi/acpiconf/Makefile8
-rw-r--r--usr.sbin/acpi/acpiconf/acpiconf.892
-rw-r--r--usr.sbin/acpi/acpiconf/acpiconf.c227
-rw-r--r--usr.sbin/acpi/acpidb/Makefile64
-rw-r--r--usr.sbin/acpi/acpidb/acpidb.8167
-rw-r--r--usr.sbin/acpi/acpidb/acpidb.c498
-rw-r--r--usr.sbin/acpi/acpidump/Makefile8
-rw-r--r--usr.sbin/acpi/acpidump/acpi.c898
-rw-r--r--usr.sbin/acpi/acpidump/acpi_user.c218
-rw-r--r--usr.sbin/acpi/acpidump/acpidump.8199
-rw-r--r--usr.sbin/acpi/acpidump/acpidump.c144
-rw-r--r--usr.sbin/acpi/acpidump/acpidump.h349
-rw-r--r--usr.sbin/acpi/iasl/Makefile70
-rw-r--r--usr.sbin/acpi/iasl/iasl.8179
-rw-r--r--usr.sbin/adduser/Makefile6
-rw-r--r--usr.sbin/adduser/adduser.8480
-rw-r--r--usr.sbin/adduser/adduser.conf.5221
-rw-r--r--usr.sbin/adduser/adduser.sh1052
-rw-r--r--usr.sbin/adduser/rmuser.8210
-rw-r--r--usr.sbin/adduser/rmuser.sh361
-rw-r--r--usr.sbin/amd/Makefile11
-rw-r--r--usr.sbin/amd/Makefile.inc45
-rw-r--r--usr.sbin/amd/NOTES3
-rw-r--r--usr.sbin/amd/amd/Makefile53
-rw-r--r--usr.sbin/amd/amq/Makefile20
-rw-r--r--usr.sbin/amd/doc/Makefile14
-rw-r--r--usr.sbin/amd/fixmount/Makefile21
-rw-r--r--usr.sbin/amd/fsinfo/Makefile25
-rw-r--r--usr.sbin/amd/hlfsd/Makefile19
-rw-r--r--usr.sbin/amd/include/Makefile29
-rw-r--r--usr.sbin/amd/include/amu_autofs_prot.h8
-rw-r--r--usr.sbin/amd/include/amu_nfs_prot.h1
-rw-r--r--usr.sbin/amd/include/aux_conf.h72
-rw-r--r--usr.sbin/amd/include/build_version.h8
-rw-r--r--usr.sbin/amd/include/config.h2205
-rw-r--r--usr.sbin/amd/include/newvers.sh43
-rw-r--r--usr.sbin/amd/libamu/Makefile36
-rw-r--r--usr.sbin/amd/mk-amd-map/Makefile16
-rw-r--r--usr.sbin/amd/pawd/Makefile20
-rw-r--r--usr.sbin/amd/scripts/Makefile7
-rw-r--r--usr.sbin/amd/wire-test/Makefile16
-rw-r--r--usr.sbin/ancontrol/Makefile12
-rw-r--r--usr.sbin/ancontrol/ancontrol.8553
-rw-r--r--usr.sbin/ancontrol/ancontrol.c1781
-rw-r--r--usr.sbin/apm/Makefile9
-rw-r--r--usr.sbin/apm/apm.8159
-rw-r--r--usr.sbin/apm/apm.c502
-rw-r--r--usr.sbin/apmd/Makefile21
-rw-r--r--usr.sbin/apmd/README213
-rw-r--r--usr.sbin/apmd/apmd.8322
-rw-r--r--usr.sbin/apmd/apmd.c714
-rw-r--r--usr.sbin/apmd/apmd.h108
-rw-r--r--usr.sbin/apmd/apmdlex.l112
-rw-r--r--usr.sbin/apmd/apmdparse.y205
-rw-r--r--usr.sbin/apmd/contrib/pccardq.c286
-rw-r--r--usr.sbin/arp/Makefile9
-rw-r--r--usr.sbin/arp/arp.4191
-rw-r--r--usr.sbin/arp/arp.8201
-rw-r--r--usr.sbin/arp/arp.c846
-rw-r--r--usr.sbin/asf/Makefile12
-rw-r--r--usr.sbin/asf/asf.8179
-rw-r--r--usr.sbin/asf/asf.c427
-rw-r--r--usr.sbin/asf/asf.h40
-rw-r--r--usr.sbin/asf/asf_kld.c59
-rw-r--r--usr.sbin/asf/asf_kvm.c128
-rw-r--r--usr.sbin/asf/asf_prog.c73
-rw-r--r--usr.sbin/audit/Makefile16
-rw-r--r--usr.sbin/auditd/Makefile17
-rw-r--r--usr.sbin/auditreduce/Makefile16
-rw-r--r--usr.sbin/authpf/Makefile22
-rw-r--r--usr.sbin/bluetooth/Makefile20
-rw-r--r--usr.sbin/bluetooth/Makefile.inc4
-rw-r--r--usr.sbin/bluetooth/bcmfw/BCM-LEGAL.txt8
-rw-r--r--usr.sbin/bluetooth/bcmfw/Makefile8
-rw-r--r--usr.sbin/bluetooth/bcmfw/README22
-rw-r--r--usr.sbin/bluetooth/bcmfw/bcmfw.8105
-rw-r--r--usr.sbin/bluetooth/bcmfw/bcmfw.c308
-rw-r--r--usr.sbin/bluetooth/bt3cfw/Makefile11
-rw-r--r--usr.sbin/bluetooth/bt3cfw/bt3cfw.873
-rw-r--r--usr.sbin/bluetooth/bt3cfw/bt3cfw.c227
-rw-r--r--usr.sbin/bluetooth/bthidcontrol/Makefile15
-rw-r--r--usr.sbin/bluetooth/bthidcontrol/bthidcontrol.8102
-rw-r--r--usr.sbin/bluetooth/bthidcontrol/bthidcontrol.c215
-rw-r--r--usr.sbin/bluetooth/bthidcontrol/bthidcontrol.h50
-rw-r--r--usr.sbin/bluetooth/bthidcontrol/hid.c214
-rw-r--r--usr.sbin/bluetooth/bthidcontrol/sdp.c432
-rw-r--r--usr.sbin/bluetooth/bthidd/Makefile17
-rw-r--r--usr.sbin/bluetooth/bthidd/bthid_config.h70
-rw-r--r--usr.sbin/bluetooth/bthidd/bthidd.8127
-rw-r--r--usr.sbin/bluetooth/bthidd/bthidd.c266
-rw-r--r--usr.sbin/bluetooth/bthidd/bthidd.conf.sample72
-rw-r--r--usr.sbin/bluetooth/bthidd/bthidd.h93
-rw-r--r--usr.sbin/bluetooth/bthidd/client.c256
-rw-r--r--usr.sbin/bluetooth/bthidd/hid.c402
-rw-r--r--usr.sbin/bluetooth/bthidd/kbd.c580
-rw-r--r--usr.sbin/bluetooth/bthidd/kbd.h41
-rw-r--r--usr.sbin/bluetooth/bthidd/lexer.l104
-rw-r--r--usr.sbin/bluetooth/bthidd/parser.y475
-rw-r--r--usr.sbin/bluetooth/bthidd/server.c349
-rw-r--r--usr.sbin/bluetooth/bthidd/session.c184
-rw-r--r--usr.sbin/bluetooth/btpand/Makefile13
-rw-r--r--usr.sbin/bluetooth/btpand/bnep.c755
-rw-r--r--usr.sbin/bluetooth/btpand/bnep.h72
-rw-r--r--usr.sbin/bluetooth/btpand/btpand.8241
-rw-r--r--usr.sbin/bluetooth/btpand/btpand.c293
-rw-r--r--usr.sbin/bluetooth/btpand/btpand.h212
-rw-r--r--usr.sbin/bluetooth/btpand/channel.c335
-rw-r--r--usr.sbin/bluetooth/btpand/client.c192
-rw-r--r--usr.sbin/bluetooth/btpand/event.c309
-rw-r--r--usr.sbin/bluetooth/btpand/event.h147
-rw-r--r--usr.sbin/bluetooth/btpand/packet.c110
-rw-r--r--usr.sbin/bluetooth/btpand/sdp.c209
-rw-r--r--usr.sbin/bluetooth/btpand/sdp.h38
-rw-r--r--usr.sbin/bluetooth/btpand/server.c277
-rw-r--r--usr.sbin/bluetooth/btpand/tap.c167
-rw-r--r--usr.sbin/bluetooth/hccontrol/Makefile14
-rw-r--r--usr.sbin/bluetooth/hccontrol/hccontrol.8184
-rw-r--r--usr.sbin/bluetooth/hccontrol/hccontrol.c322
-rw-r--r--usr.sbin/bluetooth/hccontrol/hccontrol.h79
-rw-r--r--usr.sbin/bluetooth/hccontrol/host_controller_baseband.c1877
-rw-r--r--usr.sbin/bluetooth/hccontrol/info.c216
-rw-r--r--usr.sbin/bluetooth/hccontrol/link_control.c960
-rw-r--r--usr.sbin/bluetooth/hccontrol/link_policy.c305
-rw-r--r--usr.sbin/bluetooth/hccontrol/node.c607
-rw-r--r--usr.sbin/bluetooth/hccontrol/send_recv.c184
-rw-r--r--usr.sbin/bluetooth/hccontrol/status.c245
-rw-r--r--usr.sbin/bluetooth/hccontrol/util.c410
-rw-r--r--usr.sbin/bluetooth/hcsecd/Makefile13
-rw-r--r--usr.sbin/bluetooth/hcsecd/hcsecd.8128
-rw-r--r--usr.sbin/bluetooth/hcsecd/hcsecd.c446
-rw-r--r--usr.sbin/bluetooth/hcsecd/hcsecd.conf64
-rw-r--r--usr.sbin/bluetooth/hcsecd/hcsecd.conf.5131
-rw-r--r--usr.sbin/bluetooth/hcsecd/hcsecd.h65
-rw-r--r--usr.sbin/bluetooth/hcsecd/lexer.l95
-rw-r--r--usr.sbin/bluetooth/hcsecd/parser.y433
-rw-r--r--usr.sbin/bluetooth/hcseriald/Makefile11
-rw-r--r--usr.sbin/bluetooth/hcseriald/hcseriald.886
-rw-r--r--usr.sbin/bluetooth/hcseriald/hcseriald.c268
-rw-r--r--usr.sbin/bluetooth/l2control/Makefile12
-rw-r--r--usr.sbin/bluetooth/l2control/l2cap.c313
-rw-r--r--usr.sbin/bluetooth/l2control/l2control.895
-rw-r--r--usr.sbin/bluetooth/l2control/l2control.c213
-rw-r--r--usr.sbin/bluetooth/l2control/l2control.h49
-rw-r--r--usr.sbin/bluetooth/l2ping/Makefile11
-rw-r--r--usr.sbin/bluetooth/l2ping/l2ping.8115
-rw-r--r--usr.sbin/bluetooth/l2ping/l2ping.c290
-rw-r--r--usr.sbin/bluetooth/rfcomm_pppd/Makefile14
-rw-r--r--usr.sbin/bluetooth/rfcomm_pppd/rfcomm_pppd.8354
-rw-r--r--usr.sbin/bluetooth/rfcomm_pppd/rfcomm_pppd.c471
-rw-r--r--usr.sbin/bluetooth/sdpcontrol/Makefile12
-rw-r--r--usr.sbin/bluetooth/sdpcontrol/sdpcontrol.8118
-rw-r--r--usr.sbin/bluetooth/sdpcontrol/sdpcontrol.c219
-rw-r--r--usr.sbin/bluetooth/sdpcontrol/sdpcontrol.h49
-rw-r--r--usr.sbin/bluetooth/sdpcontrol/search.c713
-rw-r--r--usr.sbin/bluetooth/sdpd/Makefile13
-rw-r--r--usr.sbin/bluetooth/sdpd/bgd.c102
-rw-r--r--usr.sbin/bluetooth/sdpd/dun.c136
-rw-r--r--usr.sbin/bluetooth/sdpd/ftrn.c117
-rw-r--r--usr.sbin/bluetooth/sdpd/gn.c172
-rw-r--r--usr.sbin/bluetooth/sdpd/irmc.c133
-rw-r--r--usr.sbin/bluetooth/sdpd/irmc_command.c117
-rw-r--r--usr.sbin/bluetooth/sdpd/lan.c172
-rw-r--r--usr.sbin/bluetooth/sdpd/log.c127
-rw-r--r--usr.sbin/bluetooth/sdpd/log.h47
-rw-r--r--usr.sbin/bluetooth/sdpd/main.c235
-rw-r--r--usr.sbin/bluetooth/sdpd/nap.c209
-rw-r--r--usr.sbin/bluetooth/sdpd/opush.c133
-rw-r--r--usr.sbin/bluetooth/sdpd/panu.c172
-rw-r--r--usr.sbin/bluetooth/sdpd/profile.c497
-rw-r--r--usr.sbin/bluetooth/sdpd/profile.h96
-rw-r--r--usr.sbin/bluetooth/sdpd/provider.c196
-rw-r--r--usr.sbin/bluetooth/sdpd/provider.h75
-rw-r--r--usr.sbin/bluetooth/sdpd/sar.c317
-rw-r--r--usr.sbin/bluetooth/sdpd/scr.c92
-rw-r--r--usr.sbin/bluetooth/sdpd/sd.c228
-rw-r--r--usr.sbin/bluetooth/sdpd/sdpd.8140
-rw-r--r--usr.sbin/bluetooth/sdpd/server.c589
-rw-r--r--usr.sbin/bluetooth/sdpd/server.h102
-rw-r--r--usr.sbin/bluetooth/sdpd/sp.c117
-rw-r--r--usr.sbin/bluetooth/sdpd/srr.c139
-rw-r--r--usr.sbin/bluetooth/sdpd/ssar.c252
-rw-r--r--usr.sbin/bluetooth/sdpd/ssr.c282
-rw-r--r--usr.sbin/bluetooth/sdpd/sur.c83
-rw-r--r--usr.sbin/bluetooth/sdpd/uuid-private.h39
-rw-r--r--usr.sbin/bluetooth/sdpd/uuid.c56
-rw-r--r--usr.sbin/boot0cfg/Makefile11
-rw-r--r--usr.sbin/boot0cfg/boot0cfg.8214
-rw-r--r--usr.sbin/boot0cfg/boot0cfg.c569
-rw-r--r--usr.sbin/boot98cfg/Makefile11
-rw-r--r--usr.sbin/boot98cfg/boot98cfg.8104
-rw-r--r--usr.sbin/boot98cfg/boot98cfg.c319
-rw-r--r--usr.sbin/bootparamd/Makefile5
-rw-r--r--usr.sbin/bootparamd/Makefile.inc4
-rw-r--r--usr.sbin/bootparamd/bootparamd/Makefile29
-rw-r--r--usr.sbin/bootparamd/bootparamd/README75
-rw-r--r--usr.sbin/bootparamd/bootparamd/bootparamd.876
-rw-r--r--usr.sbin/bootparamd/bootparamd/bootparamd.c358
-rw-r--r--usr.sbin/bootparamd/bootparamd/bootparams.586
-rw-r--r--usr.sbin/bootparamd/bootparamd/main.c118
-rw-r--r--usr.sbin/bootparamd/callbootd/Makefile24
-rw-r--r--usr.sbin/bootparamd/callbootd/callbootd.c205
-rw-r--r--usr.sbin/bsnmpd/Makefile7
-rw-r--r--usr.sbin/bsnmpd/Makefile.inc3
-rw-r--r--usr.sbin/bsnmpd/bsnmpd/Makefile46
-rw-r--r--usr.sbin/bsnmpd/gensnmptree/Makefile13
-rw-r--r--usr.sbin/bsnmpd/modules/Makefile24
-rw-r--r--usr.sbin/bsnmpd/modules/Makefile.inc8
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_atm/BEGEMOT-ATM-FREEBSD-MIB.txt99
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_atm/Makefile21
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_atm/atm_freebsd.def56
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_atm/atm_sys.c301
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_bridge/BEGEMOT-BRIDGE-MIB.txt1166
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_bridge/BRIDGE-MIB.txt1483
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_bridge/Makefile19
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_bridge/RSTP-MIB.txt325
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_bridge/bridge_addrs.c589
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_bridge/bridge_if.c1479
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_bridge/bridge_pf.c116
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_bridge/bridge_port.c1513
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_bridge/bridge_snmp.c338
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_bridge/bridge_snmp.h357
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_bridge/bridge_sys.c1503
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_bridge/bridge_tree.def283
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_bridge/snmp_bridge.3119
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_hostres/BEGEMOT-HOSTRES-MIB.txt125
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_hostres/Makefile82
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_hostres/hostres_begemot.c171
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_hostres/hostres_device_tbl.c690
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_hostres/hostres_diskstorage_tbl.c643
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_hostres/hostres_fs_tbl.c472
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_hostres/hostres_network_tbl.c302
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_hostres/hostres_partition_tbl.c630
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_hostres/hostres_printer_tbl.c398
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_hostres/hostres_processor_tbl.c507
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_hostres/hostres_scalars.c513
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.c209
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.h324
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_hostres/hostres_storage_tbl.c668
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swinstalled_tbl.c555
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swrun_tbl.c792
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_hostres/hostres_tree.def292
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_hostres/snmp_hostres.388
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_mibII/Makefile27
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_netgraph/BEGEMOT-NETGRAPH.txt398
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_netgraph/Makefile17
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_netgraph/netgraph_tree.def78
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.3436
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.c1690
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.h91
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_pf/BEGEMOT-PF-MIB.txt1230
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_pf/Makefile13
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c1260
-rw-r--r--usr.sbin/bsnmpd/modules/snmp_pf/pf_tree.def195
-rw-r--r--usr.sbin/btxld/Makefile9
-rw-r--r--usr.sbin/btxld/btx.h68
-rw-r--r--usr.sbin/btxld/btxld.898
-rw-r--r--usr.sbin/btxld/btxld.c575
-rw-r--r--usr.sbin/btxld/elfh.c125
-rw-r--r--usr.sbin/btxld/elfh.h38
-rw-r--r--usr.sbin/burncd/Makefile8
-rw-r--r--usr.sbin/burncd/burncd.8220
-rw-r--r--usr.sbin/burncd/burncd.c735
-rw-r--r--usr.sbin/cdcontrol/Makefile10
-rw-r--r--usr.sbin/cdcontrol/cdcontrol.1222
-rw-r--r--usr.sbin/cdcontrol/cdcontrol.c1282
-rw-r--r--usr.sbin/chkgrp/Makefile8
-rw-r--r--usr.sbin/chkgrp/chkgrp.886
-rw-r--r--usr.sbin/chkgrp/chkgrp.c169
-rw-r--r--usr.sbin/chown/Makefile10
-rw-r--r--usr.sbin/chown/chgrp.1141
-rw-r--r--usr.sbin/chown/chown.8166
-rw-r--r--usr.sbin/chown/chown.c307
-rw-r--r--usr.sbin/chroot/Makefile9
-rw-r--r--usr.sbin/chroot/chroot.894
-rw-r--r--usr.sbin/chroot/chroot.c183
-rw-r--r--usr.sbin/ckdist/Makefile11
-rw-r--r--usr.sbin/ckdist/ckdist.1133
-rw-r--r--usr.sbin/ckdist/ckdist.c445
-rw-r--r--usr.sbin/clear_locks/Makefile9
-rw-r--r--usr.sbin/clear_locks/clear_locks.851
-rw-r--r--usr.sbin/clear_locks/clear_locks.c70
-rw-r--r--usr.sbin/config/Makefile22
-rw-r--r--usr.sbin/config/SMM.doc/0.t88
-rw-r--r--usr.sbin/config/SMM.doc/1.t61
-rw-r--r--usr.sbin/config/SMM.doc/2.t188
-rw-r--r--usr.sbin/config/SMM.doc/3.t299
-rw-r--r--usr.sbin/config/SMM.doc/4.t442
-rw-r--r--usr.sbin/config/SMM.doc/5.t271
-rw-r--r--usr.sbin/config/SMM.doc/6.t239
-rw-r--r--usr.sbin/config/SMM.doc/a.t162
-rw-r--r--usr.sbin/config/SMM.doc/b.t137
-rw-r--r--usr.sbin/config/SMM.doc/c.t109
-rw-r--r--usr.sbin/config/SMM.doc/d.t272
-rw-r--r--usr.sbin/config/SMM.doc/e.t114
-rw-r--r--usr.sbin/config/SMM.doc/spell.ok306
-rw-r--r--usr.sbin/config/config.5408
-rw-r--r--usr.sbin/config/config.8260
-rw-r--r--usr.sbin/config/config.h201
-rw-r--r--usr.sbin/config/config.y450
-rw-r--r--usr.sbin/config/configvers.h53
-rw-r--r--usr.sbin/config/kernconf.tmpl17
-rw-r--r--usr.sbin/config/lang.l306
-rw-r--r--usr.sbin/config/main.c721
-rw-r--r--usr.sbin/config/mkheaders.c66
-rw-r--r--usr.sbin/config/mkmakefile.c785
-rw-r--r--usr.sbin/config/mkoptions.c357
-rw-r--r--usr.sbin/cpucontrol/Makefile9
-rw-r--r--usr.sbin/cpucontrol/amd.c183
-rw-r--r--usr.sbin/cpucontrol/amd.h49
-rw-r--r--usr.sbin/cpucontrol/cpucontrol.8123
-rw-r--r--usr.sbin/cpucontrol/cpucontrol.c362
-rw-r--r--usr.sbin/cpucontrol/cpucontrol.h56
-rw-r--r--usr.sbin/cpucontrol/intel.c285
-rw-r--r--usr.sbin/cpucontrol/intel.h70
-rw-r--r--usr.sbin/crashinfo/Makefile6
-rw-r--r--usr.sbin/crashinfo/crashinfo.8109
-rwxr-xr-xusr.sbin/crashinfo/crashinfo.sh306
-rw-r--r--usr.sbin/cron/Makefile5
-rw-r--r--usr.sbin/cron/Makefile.inc5
-rw-r--r--usr.sbin/cron/cron/Makefile12
-rw-r--r--usr.sbin/cron/cron/compat.h140
-rw-r--r--usr.sbin/cron/cron/config.h87
-rw-r--r--usr.sbin/cron/cron/cron.8221
-rw-r--r--usr.sbin/cron/cron/cron.c478
-rw-r--r--usr.sbin/cron/cron/cron.h301
-rw-r--r--usr.sbin/cron/cron/database.c263
-rw-r--r--usr.sbin/cron/cron/do_command.c617
-rw-r--r--usr.sbin/cron/cron/externs.h147
-rw-r--r--usr.sbin/cron/cron/job.c76
-rw-r--r--usr.sbin/cron/cron/pathnames.h81
-rw-r--r--usr.sbin/cron/cron/popen.c247
-rw-r--r--usr.sbin/cron/cron/user.c128
-rw-r--r--usr.sbin/cron/crontab/Makefile18
-rw-r--r--usr.sbin/cron/crontab/crontab.1141
-rw-r--r--usr.sbin/cron/crontab/crontab.5310
-rw-r--r--usr.sbin/cron/crontab/crontab.c626
-rw-r--r--usr.sbin/cron/doc/CHANGES155
-rw-r--r--usr.sbin/cron/doc/CONVERSION85
-rw-r--r--usr.sbin/cron/doc/FEATURES84
-rw-r--r--usr.sbin/cron/doc/INSTALL87
-rw-r--r--usr.sbin/cron/doc/MAIL475
-rw-r--r--usr.sbin/cron/doc/Makefile.vixie128
-rw-r--r--usr.sbin/cron/doc/README72
-rw-r--r--usr.sbin/cron/doc/README.1ST4
-rw-r--r--usr.sbin/cron/doc/THANKS29
-rw-r--r--usr.sbin/cron/lib/Makefile12
-rw-r--r--usr.sbin/cron/lib/compat.c237
-rw-r--r--usr.sbin/cron/lib/entry.c644
-rw-r--r--usr.sbin/cron/lib/env.c269
-rw-r--r--usr.sbin/cron/lib/misc.c598
-rw-r--r--usr.sbin/crunch/COPYRIGHT25
-rw-r--r--usr.sbin/crunch/Makefile5
-rw-r--r--usr.sbin/crunch/Makefile.inc4
-rw-r--r--usr.sbin/crunch/README88
-rw-r--r--usr.sbin/crunch/crunchgen/Makefile10
-rw-r--r--usr.sbin/crunch/crunchgen/crunched_main.c125
-rw-r--r--usr.sbin/crunch/crunchgen/crunchgen.1477
-rw-r--r--usr.sbin/crunch/crunchgen/crunchgen.c1196
-rw-r--r--usr.sbin/crunch/crunchgen/mkskel.sh15
-rw-r--r--usr.sbin/crunch/crunchide/Makefile23
-rw-r--r--usr.sbin/crunch/crunchide/crunchide.192
-rw-r--r--usr.sbin/crunch/crunchide/crunchide.c268
-rw-r--r--usr.sbin/crunch/crunchide/exec_aout.c198
-rw-r--r--usr.sbin/crunch/crunchide/exec_elf32.c433
-rw-r--r--usr.sbin/crunch/crunchide/exec_elf64.c40
-rw-r--r--usr.sbin/crunch/crunchide/extern.h51
-rw-r--r--usr.sbin/crunch/examples/Makefile33
-rw-r--r--usr.sbin/crunch/examples/filesystem.conf31
-rw-r--r--usr.sbin/crunch/examples/fixit.conf45
-rw-r--r--usr.sbin/crunch/examples/kcopy.conf21
-rw-r--r--usr.sbin/crunch/examples/really-big.conf158
-rw-r--r--usr.sbin/ctm/Makefile5
-rw-r--r--usr.sbin/ctm/Makefile.inc5
-rw-r--r--usr.sbin/ctm/README85
-rw-r--r--usr.sbin/ctm/ctm/Makefile23
-rw-r--r--usr.sbin/ctm/ctm/ctm.1325
-rw-r--r--usr.sbin/ctm/ctm/ctm.5183
-rw-r--r--usr.sbin/ctm/ctm/ctm.c331
-rw-r--r--usr.sbin/ctm/ctm/ctm.h163
-rw-r--r--usr.sbin/ctm/ctm/ctm_ed.c114
-rw-r--r--usr.sbin/ctm/ctm/ctm_input.c134
-rw-r--r--usr.sbin/ctm/ctm/ctm_pass1.c250
-rw-r--r--usr.sbin/ctm/ctm/ctm_pass2.c301
-rw-r--r--usr.sbin/ctm/ctm/ctm_pass3.c295
-rw-r--r--usr.sbin/ctm/ctm/ctm_passb.c142
-rw-r--r--usr.sbin/ctm/ctm/ctm_syntax.c67
-rw-r--r--usr.sbin/ctm/ctm_dequeue/Makefile11
-rw-r--r--usr.sbin/ctm/ctm_dequeue/ctm_dequeue.c209
-rw-r--r--usr.sbin/ctm/ctm_rmail/Makefile8
-rw-r--r--usr.sbin/ctm/ctm_rmail/ctm_rmail.1510
-rw-r--r--usr.sbin/ctm/ctm_rmail/ctm_rmail.c672
-rw-r--r--usr.sbin/ctm/ctm_rmail/error.c102
-rw-r--r--usr.sbin/ctm/ctm_rmail/error.h5
-rw-r--r--usr.sbin/ctm/ctm_rmail/options.h139
-rw-r--r--usr.sbin/ctm/ctm_smail/Makefile11
-rw-r--r--usr.sbin/ctm/ctm_smail/ctm_smail.c497
-rw-r--r--usr.sbin/ctm/mkCTM/Makefile26
-rw-r--r--usr.sbin/ctm/mkCTM/ctm_conf.cvs-cur9
-rw-r--r--usr.sbin/ctm/mkCTM/ctm_conf.gnats8
-rw-r--r--usr.sbin/ctm/mkCTM/ctm_conf.ports-cur7
-rw-r--r--usr.sbin/ctm/mkCTM/ctm_conf.smp-cur7
-rw-r--r--usr.sbin/ctm/mkCTM/ctm_conf.src-cur9
-rw-r--r--usr.sbin/ctm/mkCTM/ctm_conf.src-special9
-rwxr-xr-xusr.sbin/ctm/mkCTM/dequeue6
-rw-r--r--usr.sbin/ctm/mkCTM/mkCTM188
-rw-r--r--usr.sbin/ctm/mkCTM/mkctm.c597
-rw-r--r--usr.sbin/cxgbtool/Makefile11
-rw-r--r--usr.sbin/cxgbtool/cxgbtool.c1478
-rw-r--r--usr.sbin/cxgbtool/reg_defs.c837
-rw-r--r--usr.sbin/cxgbtool/reg_defs_t3.c2676
-rw-r--r--usr.sbin/cxgbtool/reg_defs_t3b.c2832
-rw-r--r--usr.sbin/cxgbtool/reg_defs_t3c.c3119
-rw-r--r--usr.sbin/cxgbtool/version.h32
-rw-r--r--usr.sbin/daemon/Makefile11
-rw-r--r--usr.sbin/daemon/daemon.895
-rw-r--r--usr.sbin/daemon/daemon.c141
-rw-r--r--usr.sbin/dconschat/Makefile11
-rw-r--r--usr.sbin/dconschat/dconschat.8330
-rw-r--r--usr.sbin/dconschat/dconschat.c1152
-rw-r--r--usr.sbin/devinfo/Makefile11
-rw-r--r--usr.sbin/devinfo/devinfo.876
-rw-r--r--usr.sbin/devinfo/devinfo.c233
-rw-r--r--usr.sbin/digictl/Makefile8
-rw-r--r--usr.sbin/digictl/digictl.8118
-rw-r--r--usr.sbin/digictl/digictl.c171
-rw-r--r--usr.sbin/diskinfo/Makefile16
-rw-r--r--usr.sbin/diskinfo/diskinfo.873
-rw-r--r--usr.sbin/diskinfo/diskinfo.c357
-rw-r--r--usr.sbin/dnssec-keygen/Makefile22
-rw-r--r--usr.sbin/dnssec-signzone/Makefile22
-rw-r--r--usr.sbin/dumpcis/Makefile10
-rw-r--r--usr.sbin/dumpcis/cardinfo.h205
-rw-r--r--usr.sbin/dumpcis/cis.h279
-rw-r--r--usr.sbin/dumpcis/dumpcis.849
-rw-r--r--usr.sbin/dumpcis/main.c58
-rw-r--r--usr.sbin/dumpcis/printcis.c1105
-rw-r--r--usr.sbin/dumpcis/readcis.c377
-rw-r--r--usr.sbin/dumpcis/readcis.h61
-rw-r--r--usr.sbin/editmap/Makefile37
-rw-r--r--usr.sbin/edquota/Makefile9
-rw-r--r--usr.sbin/edquota/edquota.8246
-rw-r--r--usr.sbin/edquota/edquota.c890
-rw-r--r--usr.sbin/edquota/pathnames.h36
-rw-r--r--usr.sbin/eeprom/Makefile12
-rw-r--r--usr.sbin/eeprom/eeprom.8707
-rw-r--r--usr.sbin/eeprom/eeprom.c153
-rw-r--r--usr.sbin/eeprom/ofw_options.c310
-rw-r--r--usr.sbin/eeprom/ofw_options.h34
-rw-r--r--usr.sbin/extattr/Makefile16
-rw-r--r--usr.sbin/extattr/rmextattr.8135
-rw-r--r--usr.sbin/extattr/rmextattr.c286
-rw-r--r--usr.sbin/extattrctl/Makefile8
-rw-r--r--usr.sbin/extattrctl/extattrctl.8181
-rw-r--r--usr.sbin/extattrctl/extattrctl.c259
-rw-r--r--usr.sbin/faithd/Makefile23
-rw-r--r--usr.sbin/faithd/README148
-rw-r--r--usr.sbin/faithd/faithd.8407
-rw-r--r--usr.sbin/faithd/faithd.c908
-rw-r--r--usr.sbin/faithd/faithd.h70
-rw-r--r--usr.sbin/faithd/ftp.c1085
-rw-r--r--usr.sbin/faithd/prefix.c349
-rw-r--r--usr.sbin/faithd/prefix.h52
-rw-r--r--usr.sbin/faithd/tcp.c324
-rw-r--r--usr.sbin/faithd/test/faithd.rb312
-rw-r--r--usr.sbin/fdcontrol/Makefile15
-rw-r--r--usr.sbin/fdcontrol/fdcontrol.8335
-rw-r--r--usr.sbin/fdcontrol/fdcontrol.c214
-rw-r--r--usr.sbin/fdformat/Makefile15
-rw-r--r--usr.sbin/fdformat/fdformat.1180
-rw-r--r--usr.sbin/fdformat/fdformat.c366
-rw-r--r--usr.sbin/fdread/Makefile12
-rw-r--r--usr.sbin/fdread/fdread.1233
-rw-r--r--usr.sbin/fdread/fdread.c337
-rw-r--r--usr.sbin/fdread/fdutil.c523
-rw-r--r--usr.sbin/fdread/fdutil.h37
-rw-r--r--usr.sbin/fdwrite/Makefile13
-rw-r--r--usr.sbin/fdwrite/fdwrite.1129
-rw-r--r--usr.sbin/fdwrite/fdwrite.c199
-rw-r--r--usr.sbin/fifolog/Makefile5
-rw-r--r--usr.sbin/fifolog/Makefile.inc10
-rw-r--r--usr.sbin/fifolog/fifolog_create/Makefile22
-rw-r--r--usr.sbin/fifolog/fifolog_create/fifolog.1218
-rw-r--r--usr.sbin/fifolog/fifolog_create/fifolog_create.c115
-rw-r--r--usr.sbin/fifolog/fifolog_reader/Makefile20
-rw-r--r--usr.sbin/fifolog/fifolog_reader/fifolog_reader.c171
-rw-r--r--usr.sbin/fifolog/fifolog_writer/Makefile16
-rw-r--r--usr.sbin/fifolog/fifolog_writer/fifolog_writer.c114
-rw-r--r--usr.sbin/fifolog/flint.lnt49
-rw-r--r--usr.sbin/fifolog/lib/Makefile11
-rw-r--r--usr.sbin/fifolog/lib/fifolog.h138
-rw-r--r--usr.sbin/fifolog/lib/fifolog_create.c122
-rw-r--r--usr.sbin/fifolog/lib/fifolog_int.c275
-rw-r--r--usr.sbin/fifolog/lib/fifolog_reader.c318
-rw-r--r--usr.sbin/fifolog/lib/fifolog_write.h65
-rw-r--r--usr.sbin/fifolog/lib/fifolog_write_poll.c417
-rw-r--r--usr.sbin/fifolog/lib/getdate.y889
-rw-r--r--usr.sbin/fifolog/lib/libfifolog.h62
-rw-r--r--usr.sbin/fifolog/lib/libfifolog_int.h45
-rw-r--r--usr.sbin/fifolog/lib/miniobj.h66
-rw-r--r--usr.sbin/flowctl/Makefile12
-rw-r--r--usr.sbin/flowctl/flowctl.884
-rw-r--r--usr.sbin/flowctl/flowctl.c281
-rw-r--r--usr.sbin/freebsd-update/Makefile6
-rw-r--r--usr.sbin/freebsd-update/freebsd-update.8173
-rw-r--r--usr.sbin/freebsd-update/freebsd-update.sh3030
-rw-r--r--usr.sbin/ftp-proxy/Makefile5
-rw-r--r--usr.sbin/ftp-proxy/Makefile.inc5
-rw-r--r--usr.sbin/ftp-proxy/ftp-proxy/Makefile18
-rw-r--r--usr.sbin/ftp-proxy/libevent/Makefile25
-rw-r--r--usr.sbin/fwcontrol/Makefile13
-rw-r--r--usr.sbin/fwcontrol/fwcontrol.8220
-rw-r--r--usr.sbin/fwcontrol/fwcontrol.c1089
-rw-r--r--usr.sbin/fwcontrol/fwdv.c418
-rw-r--r--usr.sbin/fwcontrol/fwmethods.h10
-rw-r--r--usr.sbin/fwcontrol/fwmpegts.c274
-rw-r--r--usr.sbin/getfmac/Makefile8
-rw-r--r--usr.sbin/getfmac/getfmac.857
-rw-r--r--usr.sbin/getfmac/getfmac.c116
-rw-r--r--usr.sbin/getpmac/Makefile8
-rw-r--r--usr.sbin/getpmac/getpmac.859
-rw-r--r--usr.sbin/getpmac/getpmac.c127
-rw-r--r--usr.sbin/gssd/Makefile29
-rw-r--r--usr.sbin/gssd/gssd.868
-rw-r--r--usr.sbin/gssd/gssd.c610
-rw-r--r--usr.sbin/gstat/Makefile9
-rw-r--r--usr.sbin/gstat/gstat.896
-rw-r--r--usr.sbin/gstat/gstat.c431
-rw-r--r--usr.sbin/i2c/Makefile8
-rw-r--r--usr.sbin/i2c/i2c.8168
-rw-r--r--usr.sbin/i2c/i2c.c633
-rw-r--r--usr.sbin/ifmcstat/Makefile24
-rw-r--r--usr.sbin/ifmcstat/ifmcstat.8133
-rw-r--r--usr.sbin/ifmcstat/ifmcstat.c1061
-rw-r--r--usr.sbin/ifmcstat/printb.c64
-rw-r--r--usr.sbin/inetd/Makefile29
-rw-r--r--usr.sbin/inetd/builtins.c820
-rw-r--r--usr.sbin/inetd/inetd.8955
-rw-r--r--usr.sbin/inetd/inetd.c2581
-rw-r--r--usr.sbin/inetd/inetd.h145
-rw-r--r--usr.sbin/inetd/pathnames.h36
-rw-r--r--usr.sbin/iostat/Makefile10
-rw-r--r--usr.sbin/iostat/iostat.8466
-rw-r--r--usr.sbin/iostat/iostat.c956
-rw-r--r--usr.sbin/ip6addrctl/Makefile6
-rw-r--r--usr.sbin/ip6addrctl/ip6addrctl.8126
-rw-r--r--usr.sbin/ip6addrctl/ip6addrctl.c467
-rw-r--r--usr.sbin/ip6addrctl/ip6addrctl.conf.sample12
-rw-r--r--usr.sbin/ipfwpcap/Makefile19
-rw-r--r--usr.sbin/ipfwpcap/ipfwpcap.8132
-rw-r--r--usr.sbin/ipfwpcap/ipfwpcap.c306
-rw-r--r--usr.sbin/jail/Makefile16
-rw-r--r--usr.sbin/jail/jail.8701
-rw-r--r--usr.sbin/jail/jail.c412
-rw-r--r--usr.sbin/jexec/Makefile11
-rw-r--r--usr.sbin/jexec/jexec.894
-rw-r--r--usr.sbin/jexec/jexec.c296
-rw-r--r--usr.sbin/jls/Makefile9
-rw-r--r--usr.sbin/jls/jls.874
-rw-r--r--usr.sbin/jls/jls.c257
-rw-r--r--usr.sbin/kbdcontrol/Makefile14
-rw-r--r--usr.sbin/kbdcontrol/kbdcontrol.1276
-rw-r--r--usr.sbin/kbdcontrol/kbdcontrol.c1210
-rw-r--r--usr.sbin/kbdcontrol/kbdmap.5326
-rw-r--r--usr.sbin/kbdcontrol/lex.h72
-rw-r--r--usr.sbin/kbdcontrol/lex.l149
-rw-r--r--usr.sbin/kbdcontrol/path.h4
-rw-r--r--usr.sbin/kbdmap/Languages.phrases37
-rw-r--r--usr.sbin/kbdmap/Makefile7
-rw-r--r--usr.sbin/kbdmap/TODO6
-rw-r--r--usr.sbin/kbdmap/kbdmap.1153
-rw-r--r--usr.sbin/kbdmap/kbdmap.c842
-rw-r--r--usr.sbin/kbdmap/kbdmap.h34
-rw-r--r--usr.sbin/kernbb/Makefile13
-rw-r--r--usr.sbin/kernbb/kernbb.882
-rw-r--r--usr.sbin/kernbb/kernbb.c145
-rw-r--r--usr.sbin/keyserv/Makefile26
-rw-r--r--usr.sbin/keyserv/crypt_server.c276
-rw-r--r--usr.sbin/keyserv/keyserv.880
-rw-r--r--usr.sbin/keyserv/keyserv.c821
-rw-r--r--usr.sbin/keyserv/keyserv.h17
-rw-r--r--usr.sbin/keyserv/setkey.c550
-rw-r--r--usr.sbin/kgmon/Makefile17
-rw-r--r--usr.sbin/kgmon/kgmon.8129
-rw-r--r--usr.sbin/kgmon/kgmon.c534
-rw-r--r--usr.sbin/kgzip/Makefile9
-rw-r--r--usr.sbin/kgzip/aouthdr.c79
-rw-r--r--usr.sbin/kgzip/aouthdr.h59
-rw-r--r--usr.sbin/kgzip/elfhdr.c164
-rw-r--r--usr.sbin/kgzip/elfhdr.h84
-rw-r--r--usr.sbin/kgzip/kgz.h57
-rw-r--r--usr.sbin/kgzip/kgzcmp.c237
-rw-r--r--usr.sbin/kgzip/kgzip.8143
-rw-r--r--usr.sbin/kgzip/kgzip.c176
-rw-r--r--usr.sbin/kgzip/kgzip.h51
-rw-r--r--usr.sbin/kgzip/kgzld.c101
-rw-r--r--usr.sbin/kgzip/xio.c121
-rw-r--r--usr.sbin/kldxref/Makefile16
-rw-r--r--usr.sbin/kldxref/ef.c648
-rw-r--r--usr.sbin/kldxref/ef.h69
-rw-r--r--usr.sbin/kldxref/ef_amd64.c117
-rw-r--r--usr.sbin/kldxref/ef_i386.c97
-rw-r--r--usr.sbin/kldxref/ef_nop.c40
-rw-r--r--usr.sbin/kldxref/ef_obj.c608
-rw-r--r--usr.sbin/kldxref/ef_powerpc.c74
-rw-r--r--usr.sbin/kldxref/ef_sparc64.c69
-rw-r--r--usr.sbin/kldxref/fileformat45
-rw-r--r--usr.sbin/kldxref/kldxref.895
-rw-r--r--usr.sbin/kldxref/kldxref.c360
-rw-r--r--usr.sbin/lastlogin/Makefile8
-rw-r--r--usr.sbin/lastlogin/lastlogin.880
-rw-r--r--usr.sbin/lastlogin/lastlogin.c134
-rw-r--r--usr.sbin/lmcconfig/Makefile9
-rw-r--r--usr.sbin/lmcconfig/lmcconfig.8723
-rw-r--r--usr.sbin/lmcconfig/lmcconfig.c2485
-rw-r--r--usr.sbin/lpr/Makefile9
-rw-r--r--usr.sbin/lpr/Makefile.inc11
-rw-r--r--usr.sbin/lpr/SMM.doc/0.t68
-rw-r--r--usr.sbin/lpr/SMM.doc/1.t77
-rw-r--r--usr.sbin/lpr/SMM.doc/2.t141
-rw-r--r--usr.sbin/lpr/SMM.doc/3.t73
-rw-r--r--usr.sbin/lpr/SMM.doc/4.t206
-rw-r--r--usr.sbin/lpr/SMM.doc/5.t116
-rw-r--r--usr.sbin/lpr/SMM.doc/6.t94
-rw-r--r--usr.sbin/lpr/SMM.doc/7.t226
-rw-r--r--usr.sbin/lpr/SMM.doc/Makefile12
-rw-r--r--usr.sbin/lpr/SMM.doc/spell.ok70
-rw-r--r--usr.sbin/lpr/chkprintcap/Makefile14
-rw-r--r--usr.sbin/lpr/chkprintcap/chkprintcap.899
-rw-r--r--usr.sbin/lpr/chkprintcap/chkprintcap.c318
-rw-r--r--usr.sbin/lpr/chkprintcap/skimprintcap.c260
-rw-r--r--usr.sbin/lpr/chkprintcap/skimprintcap.h44
-rw-r--r--usr.sbin/lpr/common_source/Makefile13
-rw-r--r--usr.sbin/lpr/common_source/common.c771
-rw-r--r--usr.sbin/lpr/common_source/ctlinfo.c914
-rw-r--r--usr.sbin/lpr/common_source/ctlinfo.h73
-rw-r--r--usr.sbin/lpr/common_source/displayq.c540
-rw-r--r--usr.sbin/lpr/common_source/lp.cdefs.h107
-rw-r--r--usr.sbin/lpr/common_source/lp.h307
-rw-r--r--usr.sbin/lpr/common_source/lp.local.h82
-rw-r--r--usr.sbin/lpr/common_source/matchjobs.c568
-rw-r--r--usr.sbin/lpr/common_source/matchjobs.h102
-rw-r--r--usr.sbin/lpr/common_source/net.c299
-rw-r--r--usr.sbin/lpr/common_source/pathnames.h51
-rw-r--r--usr.sbin/lpr/common_source/printcap.c451
-rw-r--r--usr.sbin/lpr/common_source/request.c81
-rw-r--r--usr.sbin/lpr/common_source/rmjob.c397
-rw-r--r--usr.sbin/lpr/common_source/startdaemon.c111
-rw-r--r--usr.sbin/lpr/filters.ru/Makefile8
-rw-r--r--usr.sbin/lpr/filters.ru/Makefile.inc3
-rw-r--r--usr.sbin/lpr/filters.ru/bjc-240.sh.sample64
-rw-r--r--usr.sbin/lpr/filters.ru/koi2855/Makefile8
-rw-r--r--usr.sbin/lpr/filters.ru/koi2855/koi2855.c104
-rw-r--r--usr.sbin/lpr/filters.ru/koi2alt/Makefile8
-rw-r--r--usr.sbin/lpr/filters.ru/koi2alt/koi2alt.c105
-rw-r--r--usr.sbin/lpr/filters/Makefile11
-rw-r--r--usr.sbin/lpr/filters/lpf.c221
-rw-r--r--usr.sbin/lpr/lp/Makefile8
-rw-r--r--usr.sbin/lpr/lp/lp.1120
-rw-r--r--usr.sbin/lpr/lp/lp.sh75
-rw-r--r--usr.sbin/lpr/lpc/Makefile17
-rw-r--r--usr.sbin/lpr/lpc/cmds.c1307
-rw-r--r--usr.sbin/lpr/lpc/cmdtab.c94
-rw-r--r--usr.sbin/lpr/lpc/extern.h81
-rw-r--r--usr.sbin/lpr/lpc/lpc.8313
-rw-r--r--usr.sbin/lpr/lpc/lpc.c419
-rw-r--r--usr.sbin/lpr/lpc/lpc.h55
-rw-r--r--usr.sbin/lpr/lpc/movejobs.c270
-rw-r--r--usr.sbin/lpr/lpd/Makefile13
-rw-r--r--usr.sbin/lpr/lpd/extern.h50
-rw-r--r--usr.sbin/lpr/lpd/lpd.8345
-rw-r--r--usr.sbin/lpr/lpd/lpd.c946
-rw-r--r--usr.sbin/lpr/lpd/lpdchar.c1072
-rw-r--r--usr.sbin/lpr/lpd/modes.c235
-rw-r--r--usr.sbin/lpr/lpd/printjob.c2005
-rw-r--r--usr.sbin/lpr/lpd/recvjob.c409
-rw-r--r--usr.sbin/lpr/lpq/Makefile16
-rw-r--r--usr.sbin/lpr/lpq/lpq.1143
-rw-r--r--usr.sbin/lpr/lpq/lpq.c199
-rw-r--r--usr.sbin/lpr/lpr/Makefile19
-rw-r--r--usr.sbin/lpr/lpr/lpr.1325
-rw-r--r--usr.sbin/lpr/lpr/lpr.c891
-rw-r--r--usr.sbin/lpr/lpr/printcap.5437
-rw-r--r--usr.sbin/lpr/lprm/Makefile18
-rw-r--r--usr.sbin/lpr/lprm/lprm.1152
-rw-r--r--usr.sbin/lpr/lprm/lprm.c165
-rw-r--r--usr.sbin/lpr/lptest/Makefile8
-rw-r--r--usr.sbin/lpr/lptest/lptest.177
-rw-r--r--usr.sbin/lpr/lptest/lptest.c89
-rw-r--r--usr.sbin/lpr/pac/Makefile14
-rw-r--r--usr.sbin/lpr/pac/pac.8109
-rw-r--r--usr.sbin/lpr/pac/pac.c454
-rw-r--r--usr.sbin/lptcontrol/Makefile8
-rw-r--r--usr.sbin/lptcontrol/lptcontrol.890
-rw-r--r--usr.sbin/lptcontrol/lptcontrol.c106
-rw-r--r--usr.sbin/mailstats/Makefile35
-rw-r--r--usr.sbin/mailwrapper/Makefile34
-rw-r--r--usr.sbin/mailwrapper/mailwrapper.8164
-rw-r--r--usr.sbin/mailwrapper/mailwrapper.c162
-rw-r--r--usr.sbin/mailwrapper/pathnames.h35
-rw-r--r--usr.sbin/makefs/Makefile28
-rw-r--r--usr.sbin/makefs/compat/pwcache.c623
-rw-r--r--usr.sbin/makefs/compat/pwcache.h73
-rw-r--r--usr.sbin/makefs/compat/strsuftoll.c229
-rw-r--r--usr.sbin/makefs/ffs.c1093
-rw-r--r--usr.sbin/makefs/ffs/buf.c222
-rw-r--r--usr.sbin/makefs/ffs/buf.h67
-rw-r--r--usr.sbin/makefs/ffs/ffs_alloc.c683
-rw-r--r--usr.sbin/makefs/ffs/ffs_balloc.c578
-rw-r--r--usr.sbin/makefs/ffs/ffs_bswap.c270
-rw-r--r--usr.sbin/makefs/ffs/ffs_extern.h77
-rw-r--r--usr.sbin/makefs/ffs/ffs_subr.c202
-rw-r--r--usr.sbin/makefs/ffs/mkfs.c832
-rw-r--r--usr.sbin/makefs/ffs/newfs_extern.h41
-rw-r--r--usr.sbin/makefs/ffs/ufs_bmap.c142
-rw-r--r--usr.sbin/makefs/ffs/ufs_bswap.h88
-rw-r--r--usr.sbin/makefs/ffs/ufs_inode.h97
-rw-r--r--usr.sbin/makefs/getid.c436
-rw-r--r--usr.sbin/makefs/makefs.8288
-rw-r--r--usr.sbin/makefs/makefs.c314
-rw-r--r--usr.sbin/makefs/makefs.h305
-rw-r--r--usr.sbin/makefs/walk.c568
-rw-r--r--usr.sbin/makemap/Makefile38
-rw-r--r--usr.sbin/manctl/Makefile6
-rw-r--r--usr.sbin/manctl/manctl.858
-rw-r--r--usr.sbin/manctl/manctl.sh380
-rw-r--r--usr.sbin/memcontrol/Makefile7
-rw-r--r--usr.sbin/memcontrol/memcontrol.8111
-rw-r--r--usr.sbin/memcontrol/memcontrol.c344
-rw-r--r--usr.sbin/mergemaster/Makefile7
-rw-r--r--usr.sbin/mergemaster/mergemaster.8457
-rwxr-xr-xusr.sbin/mergemaster/mergemaster.sh1268
-rw-r--r--usr.sbin/mixer/Makefile8
-rw-r--r--usr.sbin/mixer/mixer.8181
-rw-r--r--usr.sbin/mixer/mixer.c331
-rw-r--r--usr.sbin/mld6query/Makefile23
-rw-r--r--usr.sbin/mld6query/mld6.c351
-rw-r--r--usr.sbin/mld6query/mld6query.891
-rw-r--r--usr.sbin/mlxcontrol/Makefile9
-rw-r--r--usr.sbin/mlxcontrol/command.c712
-rw-r--r--usr.sbin/mlxcontrol/config.c157
-rw-r--r--usr.sbin/mlxcontrol/interface.c289
-rw-r--r--usr.sbin/mlxcontrol/mlxcontrol.8148
-rw-r--r--usr.sbin/mlxcontrol/mlxcontrol.h89
-rw-r--r--usr.sbin/mlxcontrol/util.c172
-rw-r--r--usr.sbin/mount_nwfs/Makefile16
-rw-r--r--usr.sbin/mount_nwfs/mount_nwfs.8230
-rw-r--r--usr.sbin/mount_nwfs/mount_nwfs.c363
-rw-r--r--usr.sbin/mount_portalfs/Makefile15
-rw-r--r--usr.sbin/mount_portalfs/activate.c195
-rw-r--r--usr.sbin/mount_portalfs/conf.c318
-rw-r--r--usr.sbin/mount_portalfs/cred.c75
-rw-r--r--usr.sbin/mount_portalfs/mount_portalfs.8216
-rw-r--r--usr.sbin/mount_portalfs/mount_portalfs.c280
-rw-r--r--usr.sbin/mount_portalfs/pathnames.h40
-rw-r--r--usr.sbin/mount_portalfs/portal.conf7
-rw-r--r--usr.sbin/mount_portalfs/portald.h85
-rw-r--r--usr.sbin/mount_portalfs/pt_conf.c50
-rw-r--r--usr.sbin/mount_portalfs/pt_exec.c50
-rw-r--r--usr.sbin/mount_portalfs/pt_file.c91
-rw-r--r--usr.sbin/mount_portalfs/pt_pipe.c229
-rw-r--r--usr.sbin/mount_portalfs/pt_tcp.c157
-rw-r--r--usr.sbin/mount_portalfs/pt_tcplisten.c201
-rw-r--r--usr.sbin/mount_smbfs/Makefile23
-rw-r--r--usr.sbin/mountd/Makefile17
-rw-r--r--usr.sbin/mountd/exports.5449
-rw-r--r--usr.sbin/mountd/mountd.8182
-rw-r--r--usr.sbin/mountd/mountd.c2873
-rw-r--r--usr.sbin/mountd/netgroup.5190
-rw-r--r--usr.sbin/mountd/pathnames.h36
-rw-r--r--usr.sbin/moused/Makefile14
-rw-r--r--usr.sbin/moused/moused.8854
-rw-r--r--usr.sbin/moused/moused.c3439
-rw-r--r--usr.sbin/mptable/Makefile6
-rw-r--r--usr.sbin/mptable/mptable.169
-rw-r--r--usr.sbin/mptable/mptable.c1110
-rw-r--r--usr.sbin/mtest/Makefile6
-rw-r--r--usr.sbin/mtest/mtest.8158
-rw-r--r--usr.sbin/mtest/mtest.c409
-rw-r--r--usr.sbin/mtree/Makefile17
-rw-r--r--usr.sbin/mtree/compare.c388
-rw-r--r--usr.sbin/mtree/create.c428
-rw-r--r--usr.sbin/mtree/excludes.c111
-rw-r--r--usr.sbin/mtree/extern.h59
-rw-r--r--usr.sbin/mtree/misc.c124
-rw-r--r--usr.sbin/mtree/mtree.5272
-rw-r--r--usr.sbin/mtree/mtree.8404
-rw-r--r--usr.sbin/mtree/mtree.c190
-rw-r--r--usr.sbin/mtree/mtree.h98
-rw-r--r--usr.sbin/mtree/spec.c323
-rw-r--r--usr.sbin/mtree/specspec.c256
-rw-r--r--usr.sbin/mtree/test/test00.sh67
-rw-r--r--usr.sbin/mtree/test/test01.sh40
-rw-r--r--usr.sbin/mtree/test/test02.sh36
-rw-r--r--usr.sbin/mtree/test/test03.sh60
-rw-r--r--usr.sbin/mtree/test/test04.sh51
-rw-r--r--usr.sbin/mtree/test/test05.sh25
-rw-r--r--usr.sbin/mtree/verify.c259
-rw-r--r--usr.sbin/named-checkconf/Makefile24
-rw-r--r--usr.sbin/named-checkzone/Makefile26
-rw-r--r--usr.sbin/named.reload/Makefile9
-rw-r--r--usr.sbin/named.reload/named.reload.868
-rw-r--r--usr.sbin/named.reload/named.reload.sh42
-rw-r--r--usr.sbin/named/Makefile40
-rw-r--r--usr.sbin/ndiscvt/Makefile30
-rw-r--r--usr.sbin/ndiscvt/inf-parse.y111
-rw-r--r--usr.sbin/ndiscvt/inf-token.l129
-rw-r--r--usr.sbin/ndiscvt/inf.c910
-rw-r--r--usr.sbin/ndiscvt/inf.h61
-rw-r--r--usr.sbin/ndiscvt/ndiscvt.8283
-rw-r--r--usr.sbin/ndiscvt/ndiscvt.c432
-rw-r--r--usr.sbin/ndiscvt/ndisgen.886
-rw-r--r--usr.sbin/ndiscvt/ndisgen.sh559
-rw-r--r--usr.sbin/ndiscvt/windrv_stub.c266
-rw-r--r--usr.sbin/ndp/Makefile25
-rw-r--r--usr.sbin/ndp/gnuc.h2
-rw-r--r--usr.sbin/ndp/ndp.8258
-rw-r--r--usr.sbin/ndp/ndp.c1631
-rw-r--r--usr.sbin/newsyslog/Makefile9
-rw-r--r--usr.sbin/newsyslog/extern.h68
-rw-r--r--usr.sbin/newsyslog/newsyslog.8257
-rw-r--r--usr.sbin/newsyslog/newsyslog.c2172
-rw-r--r--usr.sbin/newsyslog/newsyslog.conf.5347
-rw-r--r--usr.sbin/newsyslog/pathnames.h28
-rw-r--r--usr.sbin/newsyslog/ptimes.c618
-rw-r--r--usr.sbin/nfsd/Makefile9
-rw-r--r--usr.sbin/nfsd/nfsd.8190
-rw-r--r--usr.sbin/nfsd/nfsd.c890
-rw-r--r--usr.sbin/ngctl/Makefile29
-rw-r--r--usr.sbin/ngctl/config.c111
-rw-r--r--usr.sbin/ngctl/connect.c90
-rw-r--r--usr.sbin/ngctl/debug.c84
-rw-r--r--usr.sbin/ngctl/dot.c200
-rw-r--r--usr.sbin/ngctl/list.c146
-rw-r--r--usr.sbin/ngctl/main.c661
-rw-r--r--usr.sbin/ngctl/mkpeer.c90
-rw-r--r--usr.sbin/ngctl/msg.c163
-rw-r--r--usr.sbin/ngctl/name.c81
-rw-r--r--usr.sbin/ngctl/ngctl.8141
-rw-r--r--usr.sbin/ngctl/ngctl.h82
-rw-r--r--usr.sbin/ngctl/rmhook.c86
-rw-r--r--usr.sbin/ngctl/show.c139
-rw-r--r--usr.sbin/ngctl/shutdown.c79
-rw-r--r--usr.sbin/ngctl/status.c101
-rw-r--r--usr.sbin/ngctl/types.c101
-rw-r--r--usr.sbin/ngctl/write.c121
-rw-r--r--usr.sbin/nghook/Makefile12
-rw-r--r--usr.sbin/nghook/main.c309
-rw-r--r--usr.sbin/nghook/nghook.8145
-rw-r--r--usr.sbin/nologin/Makefile16
-rw-r--r--usr.sbin/nologin/nologin.596
-rw-r--r--usr.sbin/nologin/nologin.857
-rw-r--r--usr.sbin/nologin/nologin.c51
-rw-r--r--usr.sbin/nscd/Makefile17
-rw-r--r--usr.sbin/nscd/agent.c126
-rw-r--r--usr.sbin/nscd/agent.h72
-rw-r--r--usr.sbin/nscd/agents/Makefile.inc3
-rw-r--r--usr.sbin/nscd/agents/group.c259
-rw-r--r--usr.sbin/nscd/agents/group.h32
-rw-r--r--usr.sbin/nscd/agents/passwd.c266
-rw-r--r--usr.sbin/nscd/agents/passwd.h32
-rw-r--r--usr.sbin/nscd/agents/services.c280
-rw-r--r--usr.sbin/nscd/agents/services.h32
-rw-r--r--usr.sbin/nscd/cachelib.c1218
-rw-r--r--usr.sbin/nscd/cachelib.h281
-rw-r--r--usr.sbin/nscd/cacheplcs.c586
-rw-r--r--usr.sbin/nscd/cacheplcs.h137
-rw-r--r--usr.sbin/nscd/config.c579
-rw-r--r--usr.sbin/nscd/config.h156
-rw-r--r--usr.sbin/nscd/debug.c149
-rw-r--r--usr.sbin/nscd/debug.h67
-rw-r--r--usr.sbin/nscd/hashtable.h216
-rw-r--r--usr.sbin/nscd/log.c78
-rw-r--r--usr.sbin/nscd/log.h43
-rw-r--r--usr.sbin/nscd/mp_rs_query.c535
-rw-r--r--usr.sbin/nscd/mp_rs_query.h34
-rw-r--r--usr.sbin/nscd/mp_ws_query.c545
-rw-r--r--usr.sbin/nscd/mp_ws_query.h36
-rw-r--r--usr.sbin/nscd/nscd.8165
-rw-r--r--usr.sbin/nscd/nscd.c869
-rw-r--r--usr.sbin/nscd/nscd.conf.5148
-rw-r--r--usr.sbin/nscd/nscdcli.c283
-rw-r--r--usr.sbin/nscd/nscdcli.h57
-rw-r--r--usr.sbin/nscd/parser.c474
-rw-r--r--usr.sbin/nscd/parser.h35
-rw-r--r--usr.sbin/nscd/protocol.c550
-rw-r--r--usr.sbin/nscd/protocol.h265
-rw-r--r--usr.sbin/nscd/query.c1268
-rw-r--r--usr.sbin/nscd/query.h110
-rw-r--r--usr.sbin/nscd/singletons.c36
-rw-r--r--usr.sbin/nscd/singletons.h47
-rw-r--r--usr.sbin/ntp/Makefile8
-rw-r--r--usr.sbin/ntp/Makefile.inc21
-rw-r--r--usr.sbin/ntp/config.h1362
-rw-r--r--usr.sbin/ntp/doc/Makefile33
-rw-r--r--usr.sbin/ntp/doc/ntp-keygen.8602
-rw-r--r--usr.sbin/ntp/doc/ntp.conf.52715
-rw-r--r--usr.sbin/ntp/doc/ntp.keys.5120
-rw-r--r--usr.sbin/ntp/doc/ntpd.8609
-rw-r--r--usr.sbin/ntp/doc/ntpdate.8280
-rw-r--r--usr.sbin/ntp/doc/ntpdc.8715
-rw-r--r--usr.sbin/ntp/doc/ntpq.8851
-rw-r--r--usr.sbin/ntp/doc/ntptime.869
-rw-r--r--usr.sbin/ntp/doc/ntptrace.876
-rw-r--r--usr.sbin/ntp/libntp/Makefile37
-rw-r--r--usr.sbin/ntp/libopts/Makefile13
-rw-r--r--usr.sbin/ntp/libparse/Makefile16
-rw-r--r--usr.sbin/ntp/ntp-keygen/Makefile24
-rw-r--r--usr.sbin/ntp/ntpd/Makefile48
-rw-r--r--usr.sbin/ntp/ntpdate/Makefile19
-rw-r--r--usr.sbin/ntp/ntpdc/Makefile33
-rw-r--r--usr.sbin/ntp/ntpq/Makefile32
-rw-r--r--usr.sbin/ntp/ntptime/Makefile13
-rw-r--r--usr.sbin/ntp/ntptrace/Makefile9
-rwxr-xr-xusr.sbin/ntp/scripts/mkver46
-rw-r--r--usr.sbin/ntp/scripts/ntptrace62
-rwxr-xr-xusr.sbin/ntp/scripts/ntpver8
-rw-r--r--usr.sbin/ntp/sntp/Makefile16
-rw-r--r--usr.sbin/nvram/Makefile9
-rw-r--r--usr.sbin/nvram/nvram.8118
-rw-r--r--usr.sbin/nvram/nvram.c222
-rw-r--r--usr.sbin/ofwdump/Makefile9
-rw-r--r--usr.sbin/ofwdump/ofw_util.c230
-rw-r--r--usr.sbin/ofwdump/ofw_util.h50
-rw-r--r--usr.sbin/ofwdump/ofwdump.8107
-rw-r--r--usr.sbin/ofwdump/ofwdump.c240
-rw-r--r--usr.sbin/ofwdump/pathnames.h30
-rw-r--r--usr.sbin/pciconf/Makefile10
-rw-r--r--usr.sbin/pciconf/cap.c510
-rw-r--r--usr.sbin/pciconf/pathnames.h3
-rw-r--r--usr.sbin/pciconf/pciconf.8271
-rw-r--r--usr.sbin/pciconf/pciconf.c668
-rw-r--r--usr.sbin/pciconf/pciconf.h39
-rw-r--r--usr.sbin/periodic/Makefile6
-rw-r--r--usr.sbin/periodic/periodic.8258
-rw-r--r--usr.sbin/periodic/periodic.sh109
-rw-r--r--usr.sbin/pkg_install/Makefile20
-rw-r--r--usr.sbin/pkg_install/Makefile.inc17
-rw-r--r--usr.sbin/pkg_install/README8
-rw-r--r--usr.sbin/pkg_install/add/Makefile14
-rw-r--r--usr.sbin/pkg_install/add/add.h48
-rw-r--r--usr.sbin/pkg_install/add/extract.c287
-rw-r--r--usr.sbin/pkg_install/add/futil.c97
-rw-r--r--usr.sbin/pkg_install/add/main.c349
-rw-r--r--usr.sbin/pkg_install/add/perform.c652
-rw-r--r--usr.sbin/pkg_install/add/pkg_add.1603
-rw-r--r--usr.sbin/pkg_install/create/Makefile14
-rw-r--r--usr.sbin/pkg_install/create/create.h58
-rw-r--r--usr.sbin/pkg_install/create/main.c263
-rw-r--r--usr.sbin/pkg_install/create/perform.c588
-rw-r--r--usr.sbin/pkg_install/create/pkg_create.1652
-rw-r--r--usr.sbin/pkg_install/create/pl.c280
-rw-r--r--usr.sbin/pkg_install/delete/Makefile14
-rw-r--r--usr.sbin/pkg_install/delete/delete.h35
-rw-r--r--usr.sbin/pkg_install/delete/main.c179
-rw-r--r--usr.sbin/pkg_install/delete/perform.c417
-rw-r--r--usr.sbin/pkg_install/delete/pkg_delete.1293
-rw-r--r--usr.sbin/pkg_install/info/Makefile14
-rw-r--r--usr.sbin/pkg_install/info/info.h84
-rw-r--r--usr.sbin/pkg_install/info/main.c293
-rw-r--r--usr.sbin/pkg_install/info/perform.c478
-rw-r--r--usr.sbin/pkg_install/info/pkg_info.1294
-rw-r--r--usr.sbin/pkg_install/info/show.c381
-rw-r--r--usr.sbin/pkg_install/lib/Makefile11
-rw-r--r--usr.sbin/pkg_install/lib/deps.c241
-rw-r--r--usr.sbin/pkg_install/lib/exec.c106
-rw-r--r--usr.sbin/pkg_install/lib/file.c427
-rw-r--r--usr.sbin/pkg_install/lib/global.c32
-rw-r--r--usr.sbin/pkg_install/lib/lib.h248
-rw-r--r--usr.sbin/pkg_install/lib/match.c603
-rw-r--r--usr.sbin/pkg_install/lib/msg.c75
-rw-r--r--usr.sbin/pkg_install/lib/pen.c187
-rw-r--r--usr.sbin/pkg_install/lib/pkgwrap.c89
-rw-r--r--usr.sbin/pkg_install/lib/plist.c588
-rw-r--r--usr.sbin/pkg_install/lib/str.c129
-rw-r--r--usr.sbin/pkg_install/lib/url.c166
-rw-r--r--usr.sbin/pkg_install/lib/version.c328
-rwxr-xr-xusr.sbin/pkg_install/tkpkg177
-rw-r--r--usr.sbin/pkg_install/updating/Makefile14
-rw-r--r--usr.sbin/pkg_install/updating/main.c270
-rw-r--r--usr.sbin/pkg_install/updating/pathnames.h17
-rw-r--r--usr.sbin/pkg_install/updating/pkg_updating.194
-rw-r--r--usr.sbin/pkg_install/version/Makefile17
-rw-r--r--usr.sbin/pkg_install/version/main.c137
-rw-r--r--usr.sbin/pkg_install/version/perform.c408
-rw-r--r--usr.sbin/pkg_install/version/pkg_version.1256
-rwxr-xr-xusr.sbin/pkg_install/version/test-pkg_version.sh94
-rw-r--r--usr.sbin/pkg_install/version/version.h43
-rw-r--r--usr.sbin/pmcannotate/Makefile12
-rw-r--r--usr.sbin/pmcannotate/pmcannotate.8108
-rw-r--r--usr.sbin/pmcannotate/pmcannotate.c804
-rw-r--r--usr.sbin/pmccontrol/Makefile15
-rw-r--r--usr.sbin/pmccontrol/pmccontrol.8127
-rw-r--r--usr.sbin/pmccontrol/pmccontrol.c497
-rw-r--r--usr.sbin/pmcstat/Makefile15
-rw-r--r--usr.sbin/pmcstat/pmcstat.8430
-rw-r--r--usr.sbin/pmcstat/pmcstat.c1326
-rw-r--r--usr.sbin/pmcstat/pmcstat.h154
-rw-r--r--usr.sbin/pmcstat/pmcstat_log.c2634
-rw-r--r--usr.sbin/pnpinfo/Makefile14
-rw-r--r--usr.sbin/portsnap/Makefile5
-rw-r--r--usr.sbin/portsnap/Makefile.inc5
-rw-r--r--usr.sbin/portsnap/make_index/Makefile9
-rw-r--r--usr.sbin/portsnap/make_index/make_index.c513
-rw-r--r--usr.sbin/portsnap/phttpget/Makefile9
-rw-r--r--usr.sbin/portsnap/phttpget/phttpget.c724
-rw-r--r--usr.sbin/portsnap/portsnap/Makefile6
-rw-r--r--usr.sbin/portsnap/portsnap/portsnap.8245
-rw-r--r--usr.sbin/portsnap/portsnap/portsnap.sh1054
-rw-r--r--usr.sbin/powerd/Makefile14
-rw-r--r--usr.sbin/powerd/powerd.8140
-rw-r--r--usr.sbin/powerd/powerd.c694
-rw-r--r--usr.sbin/ppp/Makefile119
-rw-r--r--usr.sbin/ppp/README.changes140
-rw-r--r--usr.sbin/ppp/README.nat378
-rw-r--r--usr.sbin/ppp/acf.c116
-rw-r--r--usr.sbin/ppp/acf.h33
-rw-r--r--usr.sbin/ppp/arp.c317
-rw-r--r--usr.sbin/ppp/arp.h36
-rw-r--r--usr.sbin/ppp/async.c220
-rw-r--r--usr.sbin/ppp/async.h53
-rw-r--r--usr.sbin/ppp/atm.c237
-rw-r--r--usr.sbin/ppp/atm.h35
-rw-r--r--usr.sbin/ppp/auth.c479
-rw-r--r--usr.sbin/ppp/auth.h68
-rw-r--r--usr.sbin/ppp/bundle.c2019
-rw-r--r--usr.sbin/ppp/bundle.h216
-rw-r--r--usr.sbin/ppp/cbcp.c763
-rw-r--r--usr.sbin/ppp/cbcp.h65
-rw-r--r--usr.sbin/ppp/ccp.c826
-rw-r--r--usr.sbin/ppp/ccp.h165
-rw-r--r--usr.sbin/ppp/chap.c972
-rw-r--r--usr.sbin/ppp/chap.h75
-rw-r--r--usr.sbin/ppp/chap_ms.c415
-rw-r--r--usr.sbin/ppp/chap_ms.h52
-rw-r--r--usr.sbin/ppp/chat.c797
-rw-r--r--usr.sbin/ppp/chat.h82
-rw-r--r--usr.sbin/ppp/command.c3312
-rw-r--r--usr.sbin/ppp/command.h75
-rw-r--r--usr.sbin/ppp/datalink.c1478
-rw-r--r--usr.sbin/ppp/datalink.h156
-rw-r--r--usr.sbin/ppp/deflate.c601
-rw-r--r--usr.sbin/ppp/deflate.h30
-rw-r--r--usr.sbin/ppp/defs.c450
-rw-r--r--usr.sbin/ppp/defs.h142
-rw-r--r--usr.sbin/ppp/descriptor.h53
-rw-r--r--usr.sbin/ppp/ether.c736
-rw-r--r--usr.sbin/ppp/ether.h37
-rw-r--r--usr.sbin/ppp/exec.c234
-rw-r--r--usr.sbin/ppp/exec.h35
-rw-r--r--usr.sbin/ppp/filter.c604
-rw-r--r--usr.sbin/ppp/filter.h101
-rw-r--r--usr.sbin/ppp/fsm.c1213
-rw-r--r--usr.sbin/ppp/fsm.h201
-rw-r--r--usr.sbin/ppp/hdlc.c438
-rw-r--r--usr.sbin/ppp/hdlc.h116
-rw-r--r--usr.sbin/ppp/i4b.h37
-rw-r--r--usr.sbin/ppp/id.c303
-rw-r--r--usr.sbin/ppp/id.h93
-rw-r--r--usr.sbin/ppp/iface.c729
-rw-r--r--usr.sbin/ppp/iface.h65
-rw-r--r--usr.sbin/ppp/ip.c993
-rw-r--r--usr.sbin/ppp/ip.h44
-rw-r--r--usr.sbin/ppp/ipcp.c1479
-rw-r--r--usr.sbin/ppp/ipcp.h132
-rw-r--r--usr.sbin/ppp/iplist.c225
-rw-r--r--usr.sbin/ppp/iplist.h51
-rw-r--r--usr.sbin/ppp/ipv6cp.c785
-rw-r--r--usr.sbin/ppp/ipv6cp.h83
-rw-r--r--usr.sbin/ppp/layer.h52
-rw-r--r--usr.sbin/ppp/lcp.c1305
-rw-r--r--usr.sbin/ppp/lcp.h143
-rw-r--r--usr.sbin/ppp/link.c412
-rw-r--r--usr.sbin/ppp/link.h81
-rw-r--r--usr.sbin/ppp/log.c521
-rw-r--r--usr.sbin/ppp/log.h105
-rw-r--r--usr.sbin/ppp/lqr.c532
-rw-r--r--usr.sbin/ppp/lqr.h82
-rw-r--r--usr.sbin/ppp/main.c677
-rw-r--r--usr.sbin/ppp/main.h32
-rw-r--r--usr.sbin/ppp/mbuf.c440
-rw-r--r--usr.sbin/ppp/mbuf.h119
-rw-r--r--usr.sbin/ppp/mp.c1209
-rw-r--r--usr.sbin/ppp/mp.h146
-rw-r--r--usr.sbin/ppp/mppe.c817
-rw-r--r--usr.sbin/ppp/mppe.h33
-rw-r--r--usr.sbin/ppp/nat_cmd.c603
-rw-r--r--usr.sbin/ppp/nat_cmd.h42
-rw-r--r--usr.sbin/ppp/ncp.c547
-rw-r--r--usr.sbin/ppp/ncp.h101
-rw-r--r--usr.sbin/ppp/ncpaddr.c1010
-rw-r--r--usr.sbin/ppp/ncpaddr.h109
-rw-r--r--usr.sbin/ppp/netgraph.c743
-rw-r--r--usr.sbin/ppp/netgraph.h37
-rw-r--r--usr.sbin/ppp/pap.c303
-rw-r--r--usr.sbin/ppp/pap.h40
-rw-r--r--usr.sbin/ppp/physical.c1140
-rw-r--r--usr.sbin/ppp/physical.h176
-rw-r--r--usr.sbin/ppp/ppp.8.m46105
-rw-r--r--usr.sbin/ppp/pred.c345
-rw-r--r--usr.sbin/ppp/pred.h31
-rw-r--r--usr.sbin/ppp/probe.c78
-rw-r--r--usr.sbin/ppp/probe.h38
-rw-r--r--usr.sbin/ppp/prompt.c574
-rw-r--r--usr.sbin/ppp/prompt.h96
-rw-r--r--usr.sbin/ppp/proto.c115
-rw-r--r--usr.sbin/ppp/proto.h64
-rw-r--r--usr.sbin/ppp/radius.c1361
-rw-r--r--usr.sbin/ppp/radius.h133
-rw-r--r--usr.sbin/ppp/route.c939
-rw-r--r--usr.sbin/ppp/route.h73
-rw-r--r--usr.sbin/ppp/server.c422
-rw-r--r--usr.sbin/ppp/server.h61
-rw-r--r--usr.sbin/ppp/sig.c119
-rw-r--r--usr.sbin/ppp/sig.h35
-rw-r--r--usr.sbin/ppp/slcompress.c605
-rw-r--r--usr.sbin/ppp/slcompress.h161
-rw-r--r--usr.sbin/ppp/sync.c84
-rw-r--r--usr.sbin/ppp/sync.h29
-rw-r--r--usr.sbin/ppp/systems.c483
-rw-r--r--usr.sbin/ppp/systems.h43
-rw-r--r--usr.sbin/ppp/tcp.c212
-rw-r--r--usr.sbin/ppp/tcp.h34
-rw-r--r--usr.sbin/ppp/tcpmss.c185
-rw-r--r--usr.sbin/ppp/tcpmss.h29
-rw-r--r--usr.sbin/ppp/throughput.c302
-rw-r--r--usr.sbin/ppp/throughput.h70
-rw-r--r--usr.sbin/ppp/timer.c293
-rw-r--r--usr.sbin/ppp/timer.h55
-rw-r--r--usr.sbin/ppp/tty.c769
-rw-r--r--usr.sbin/ppp/tty.h37
-rw-r--r--usr.sbin/ppp/tun.c119
-rw-r--r--usr.sbin/ppp/tun.h39
-rw-r--r--usr.sbin/ppp/ua.h74
-rw-r--r--usr.sbin/ppp/udp.c335
-rw-r--r--usr.sbin/ppp/udp.h35
-rw-r--r--usr.sbin/ppp/vjcomp.c200
-rw-r--r--usr.sbin/ppp/vjcomp.h36
-rw-r--r--usr.sbin/pppctl/Makefile9
-rw-r--r--usr.sbin/pppctl/pppctl.8232
-rw-r--r--usr.sbin/pppctl/pppctl.c680
-rw-r--r--usr.sbin/pppd/Makefile56
-rw-r--r--usr.sbin/pppd/RELNOTES726
-rw-r--r--usr.sbin/pppd/auth.c1637
-rw-r--r--usr.sbin/pppd/cbcp.c411
-rw-r--r--usr.sbin/pppd/cbcp.h26
-rw-r--r--usr.sbin/pppd/ccp.c1113
-rw-r--r--usr.sbin/pppd/ccp.h50
-rw-r--r--usr.sbin/pppd/chap.c870
-rw-r--r--usr.sbin/pppd/chap.h124
-rw-r--r--usr.sbin/pppd/chap_ms.c335
-rw-r--r--usr.sbin/pppd/chap_ms.h33
-rw-r--r--usr.sbin/pppd/demand.c348
-rw-r--r--usr.sbin/pppd/eui64.c46
-rw-r--r--usr.sbin/pppd/eui64.h98
-rw-r--r--usr.sbin/pppd/fsm.c798
-rw-r--r--usr.sbin/pppd/fsm.h144
-rw-r--r--usr.sbin/pppd/ipcp.c1531
-rw-r--r--usr.sbin/pppd/ipcp.h70
-rw-r--r--usr.sbin/pppd/ipv6cp.c1422
-rw-r--r--usr.sbin/pppd/ipv6cp.h129
-rw-r--r--usr.sbin/pppd/ipxcp.c1399
-rw-r--r--usr.sbin/pppd/ipxcp.h71
-rw-r--r--usr.sbin/pppd/lcp.c1859
-rw-r--r--usr.sbin/pppd/lcp.h88
-rw-r--r--usr.sbin/pppd/magic.c87
-rw-r--r--usr.sbin/pppd/magic.h23
-rw-r--r--usr.sbin/pppd/main.c1723
-rw-r--r--usr.sbin/pppd/options.c2683
-rw-r--r--usr.sbin/pppd/patchlevel.h6
-rw-r--r--usr.sbin/pppd/pathnames.h37
-rw-r--r--usr.sbin/pppd/pppd.81247
-rw-r--r--usr.sbin/pppd/pppd.h509
-rw-r--r--usr.sbin/pppd/sys-bsd.c1697
-rw-r--r--usr.sbin/pppd/upap.c618
-rw-r--r--usr.sbin/pppd/upap.h87
-rw-r--r--usr.sbin/pppstats/Makefile6
-rw-r--r--usr.sbin/pppstats/pppstats.8218
-rw-r--r--usr.sbin/pppstats/pppstats.c519
-rw-r--r--usr.sbin/praliases/Makefile38
-rw-r--r--usr.sbin/praudit/Makefile14
-rw-r--r--usr.sbin/procctl/Makefile8
-rw-r--r--usr.sbin/procctl/procctl.834
-rw-r--r--usr.sbin/procctl/procctl.c79
-rw-r--r--usr.sbin/pstat/Makefile14
-rw-r--r--usr.sbin/pstat/pstat.8251
-rw-r--r--usr.sbin/pstat/pstat.c584
-rw-r--r--usr.sbin/pw/Makefile12
-rw-r--r--usr.sbin/pw/README22
-rw-r--r--usr.sbin/pw/bitmap.c132
-rw-r--r--usr.sbin/pw/bitmap.h50
-rw-r--r--usr.sbin/pw/cpdir.c130
-rw-r--r--usr.sbin/pw/edgroup.c229
-rw-r--r--usr.sbin/pw/fileupd.c203
-rw-r--r--usr.sbin/pw/grupd.c171
-rw-r--r--usr.sbin/pw/psdate.c295
-rw-r--r--usr.sbin/pw/psdate.h40
-rw-r--r--usr.sbin/pw/pw.81001
-rw-r--r--usr.sbin/pw/pw.c456
-rw-r--r--usr.sbin/pw/pw.conf.5318
-rw-r--r--usr.sbin/pw/pw.h132
-rw-r--r--usr.sbin/pw/pw_conf.c516
-rw-r--r--usr.sbin/pw/pw_group.c423
-rw-r--r--usr.sbin/pw/pw_log.c68
-rw-r--r--usr.sbin/pw/pw_nis.c72
-rw-r--r--usr.sbin/pw/pw_user.c1279
-rw-r--r--usr.sbin/pw/pw_vpw.c316
-rw-r--r--usr.sbin/pw/pwupd.c213
-rw-r--r--usr.sbin/pw/pwupd.h160
-rw-r--r--usr.sbin/pw/rm_r.c75
-rw-r--r--usr.sbin/pwd_mkdb/Makefile13
-rw-r--r--usr.sbin/pwd_mkdb/pwd_mkdb.8183
-rw-r--r--usr.sbin/pwd_mkdb/pwd_mkdb.c758
-rw-r--r--usr.sbin/quot/Makefile8
-rw-r--r--usr.sbin/quot/quot.8110
-rw-r--r--usr.sbin/quot/quot.c644
-rw-r--r--usr.sbin/quotaon/Makefile11
-rw-r--r--usr.sbin/quotaon/quotaon.8137
-rw-r--r--usr.sbin/quotaon/quotaon.c267
-rw-r--r--usr.sbin/rarpd/Makefile11
-rw-r--r--usr.sbin/rarpd/rarpd.8148
-rw-r--r--usr.sbin/rarpd/rarpd.c951
-rw-r--r--usr.sbin/raycontrol/Makefile8
-rw-r--r--usr.sbin/raycontrol/raycontrol.8307
-rw-r--r--usr.sbin/raycontrol/raycontrol.c489
-rw-r--r--usr.sbin/repquota/Makefile9
-rw-r--r--usr.sbin/repquota/repquota.8106
-rw-r--r--usr.sbin/repquota/repquota.c408
-rw-r--r--usr.sbin/rip6query/Makefile10
-rw-r--r--usr.sbin/rip6query/rip6query.864
-rw-r--r--usr.sbin/rip6query/rip6query.c204
-rw-r--r--usr.sbin/rmt/Makefile14
-rw-r--r--usr.sbin/rmt/rmt.8221
-rw-r--r--usr.sbin/rmt/rmt.c254
-rw-r--r--usr.sbin/rndc-confgen/Makefile29
-rw-r--r--usr.sbin/rndc/Makefile28
-rw-r--r--usr.sbin/route6d/Makefile9
-rwxr-xr-xusr.sbin/route6d/misc/chkrt64
-rw-r--r--usr.sbin/route6d/misc/cksum.c52
-rw-r--r--usr.sbin/route6d/route6d.8255
-rw-r--r--usr.sbin/route6d/route6d.c3631
-rw-r--r--usr.sbin/route6d/route6d.h81
-rw-r--r--usr.sbin/rpc.lockd/Makefile29
-rw-r--r--usr.sbin/rpc.lockd/kern.c606
-rw-r--r--usr.sbin/rpc.lockd/lock_proc.c1415
-rw-r--r--usr.sbin/rpc.lockd/lockd.c839
-rw-r--r--usr.sbin/rpc.lockd/lockd.h41
-rw-r--r--usr.sbin/rpc.lockd/lockd_lock.c2303
-rw-r--r--usr.sbin/rpc.lockd/lockd_lock.h25
-rw-r--r--usr.sbin/rpc.lockd/rpc.lockd.8150
-rw-r--r--usr.sbin/rpc.lockd/test.c365
-rw-r--r--usr.sbin/rpc.statd/Makefile27
-rw-r--r--usr.sbin/rpc.statd/file.c361
-rw-r--r--usr.sbin/rpc.statd/procs.c436
-rw-r--r--usr.sbin/rpc.statd/rpc.statd.8141
-rw-r--r--usr.sbin/rpc.statd/statd.c471
-rw-r--r--usr.sbin/rpc.statd/statd.h111
-rw-r--r--usr.sbin/rpc.statd/test.c144
-rw-r--r--usr.sbin/rpc.umntall/Makefile10
-rw-r--r--usr.sbin/rpc.umntall/mounttab.c224
-rw-r--r--usr.sbin/rpc.umntall/mounttab.h46
-rw-r--r--usr.sbin/rpc.umntall/rpc.umntall.8126
-rw-r--r--usr.sbin/rpc.umntall/rpc.umntall.c266
-rw-r--r--usr.sbin/rpc.yppasswdd/Makefile62
-rw-r--r--usr.sbin/rpc.yppasswdd/rpc.yppasswdd.8359
-rw-r--r--usr.sbin/rpc.yppasswdd/yppasswd_private.x70
-rw-r--r--usr.sbin/rpc.yppasswdd/yppasswdd_extern.h69
-rw-r--r--usr.sbin/rpc.yppasswdd/yppasswdd_main.c353
-rw-r--r--usr.sbin/rpc.yppasswdd/yppasswdd_server.c918
-rw-r--r--usr.sbin/rpc.yppasswdd/yppwupdate34
-rw-r--r--usr.sbin/rpc.ypupdated/Makefile32
-rw-r--r--usr.sbin/rpc.ypupdated/update.c330
-rw-r--r--usr.sbin/rpc.ypupdated/yp_dbdelete.c68
-rw-r--r--usr.sbin/rpc.ypupdated/yp_dbupdate.c146
-rwxr-xr-xusr.sbin/rpc.ypupdated/ypupdate33
-rw-r--r--usr.sbin/rpc.ypupdated/ypupdated_extern.h33
-rw-r--r--usr.sbin/rpc.ypupdated/ypupdated_main.c286
-rw-r--r--usr.sbin/rpc.ypupdated/ypupdated_server.c228
-rw-r--r--usr.sbin/rpc.ypxfrd/Makefile35
-rw-r--r--usr.sbin/rpc.ypxfrd/rpc.ypxfrd.8153
-rw-r--r--usr.sbin/rpc.ypxfrd/ypxfrd_extern.h50
-rw-r--r--usr.sbin/rpc.ypxfrd/ypxfrd_main.c302
-rw-r--r--usr.sbin/rpc.ypxfrd/ypxfrd_server.c145
-rw-r--r--usr.sbin/rpcbind/Makefile20
-rw-r--r--usr.sbin/rpcbind/check_bound.c231
-rw-r--r--usr.sbin/rpcbind/pmap_svc.c368
-rw-r--r--usr.sbin/rpcbind/rpcb_stat.c206
-rw-r--r--usr.sbin/rpcbind/rpcb_svc.c233
-rw-r--r--usr.sbin/rpcbind/rpcb_svc_4.c455
-rw-r--r--usr.sbin/rpcbind/rpcb_svc_com.c1467
-rw-r--r--usr.sbin/rpcbind/rpcbind.8148
-rw-r--r--usr.sbin/rpcbind/rpcbind.c783
-rw-r--r--usr.sbin/rpcbind/rpcbind.h144
-rw-r--r--usr.sbin/rpcbind/security.c288
-rw-r--r--usr.sbin/rpcbind/util.c383
-rw-r--r--usr.sbin/rpcbind/warmstart.c179
-rw-r--r--usr.sbin/rrenumd/Makefile36
-rw-r--r--usr.sbin/rrenumd/lexer.l273
-rw-r--r--usr.sbin/rrenumd/parser.y676
-rw-r--r--usr.sbin/rrenumd/rrenumd.8103
-rw-r--r--usr.sbin/rrenumd/rrenumd.c663
-rw-r--r--usr.sbin/rrenumd/rrenumd.conf.5369
-rw-r--r--usr.sbin/rrenumd/rrenumd.h60
-rw-r--r--usr.sbin/rtadvd/Makefile23
-rw-r--r--usr.sbin/rtadvd/advcap.c451
-rw-r--r--usr.sbin/rtadvd/advcap.h46
-rw-r--r--usr.sbin/rtadvd/config.c1080
-rw-r--r--usr.sbin/rtadvd/config.h47
-rw-r--r--usr.sbin/rtadvd/dump.c252
-rw-r--r--usr.sbin/rtadvd/dump.h33
-rw-r--r--usr.sbin/rtadvd/if.c580
-rw-r--r--usr.sbin/rtadvd/if.h58
-rw-r--r--usr.sbin/rtadvd/pathnames.h4
-rw-r--r--usr.sbin/rtadvd/rrenum.c486
-rw-r--r--usr.sbin/rtadvd/rrenum.h34
-rw-r--r--usr.sbin/rtadvd/rtadvd.8195
-rw-r--r--usr.sbin/rtadvd/rtadvd.c1689
-rw-r--r--usr.sbin/rtadvd/rtadvd.conf21
-rw-r--r--usr.sbin/rtadvd/rtadvd.conf.5427
-rw-r--r--usr.sbin/rtadvd/rtadvd.h161
-rw-r--r--usr.sbin/rtadvd/timer.c209
-rw-r--r--usr.sbin/rtadvd/timer.h65
-rw-r--r--usr.sbin/rtprio/Makefile8
-rw-r--r--usr.sbin/rtprio/rtprio.1192
-rw-r--r--usr.sbin/rtprio/rtprio.c140
-rw-r--r--usr.sbin/rtsold/Makefile27
-rw-r--r--usr.sbin/rtsold/dump.c149
-rw-r--r--usr.sbin/rtsold/if.c385
-rw-r--r--usr.sbin/rtsold/probe.c184
-rw-r--r--usr.sbin/rtsold/rtsock.c175
-rw-r--r--usr.sbin/rtsold/rtsol.c470
-rw-r--r--usr.sbin/rtsold/rtsold.8270
-rw-r--r--usr.sbin/rtsold/rtsold.c861
-rw-r--r--usr.sbin/rtsold/rtsold.h103
-rw-r--r--usr.sbin/rwhod/Makefile10
-rw-r--r--usr.sbin/rwhod/rwhod.8240
-rw-r--r--usr.sbin/rwhod/rwhod.c737
-rw-r--r--usr.sbin/sa/Makefile11
-rw-r--r--usr.sbin/sa/db.c207
-rw-r--r--usr.sbin/sa/extern.h110
-rw-r--r--usr.sbin/sa/main.c533
-rw-r--r--usr.sbin/sa/pathnames.h35
-rw-r--r--usr.sbin/sa/pdb.c374
-rw-r--r--usr.sbin/sa/sa.8262
-rw-r--r--usr.sbin/sa/usrdb.c239
-rw-r--r--usr.sbin/sade/Makefile27
-rw-r--r--usr.sbin/sade/command.c179
-rw-r--r--usr.sbin/sade/config.c333
-rw-r--r--usr.sbin/sade/devices.c345
-rw-r--r--usr.sbin/sade/disks.c1038
-rw-r--r--usr.sbin/sade/dispatch.c161
-rw-r--r--usr.sbin/sade/dmenu.c295
-rw-r--r--usr.sbin/sade/globals.c84
-rw-r--r--usr.sbin/sade/help/partition.hlp169
-rw-r--r--usr.sbin/sade/help/slice.hlp57
-rw-r--r--usr.sbin/sade/install.c257
-rw-r--r--usr.sbin/sade/label.c1703
-rw-r--r--usr.sbin/sade/list.h55
-rw-r--r--usr.sbin/sade/main.c122
-rw-r--r--usr.sbin/sade/menus.c117
-rw-r--r--usr.sbin/sade/misc.c491
-rw-r--r--usr.sbin/sade/msg.c350
-rw-r--r--usr.sbin/sade/sade.873
-rw-r--r--usr.sbin/sade/sade.h487
-rw-r--r--usr.sbin/sade/system.c307
-rw-r--r--usr.sbin/sade/termcap.c104
-rw-r--r--usr.sbin/sade/variable.c324
-rw-r--r--usr.sbin/sade/wizard.c200
-rw-r--r--usr.sbin/sendmail/Makefile77
-rw-r--r--usr.sbin/setfib/Makefile6
-rw-r--r--usr.sbin/setfib/setfib.197
-rw-r--r--usr.sbin/setfib/setfib.c105
-rw-r--r--usr.sbin/setfmac/Makefile9
-rw-r--r--usr.sbin/setfmac/setfmac.866
-rw-r--r--usr.sbin/setfmac/setfmac.c498
-rw-r--r--usr.sbin/setfmac/setfsmac.8129
-rw-r--r--usr.sbin/setpmac/Makefile8
-rw-r--r--usr.sbin/setpmac/setpmac.865
-rw-r--r--usr.sbin/setpmac/setpmac.c92
-rw-r--r--usr.sbin/sicontrol/Makefile9
-rw-r--r--usr.sbin/sicontrol/sicontrol.8109
-rw-r--r--usr.sbin/sicontrol/sicontrol.c726
-rw-r--r--usr.sbin/sliplogin/Makefile11
-rw-r--r--usr.sbin/sliplogin/pathnames.h44
-rw-r--r--usr.sbin/sliplogin/sliplogin.8317
-rw-r--r--usr.sbin/sliplogin/sliplogin.c544
-rw-r--r--usr.sbin/slstat/Makefile9
-rw-r--r--usr.sbin/slstat/slstat.8125
-rw-r--r--usr.sbin/slstat/slstat.c243
-rw-r--r--usr.sbin/smbmsg/Makefile8
-rw-r--r--usr.sbin/smbmsg/pathnames.h29
-rw-r--r--usr.sbin/smbmsg/smbmsg.8287
-rw-r--r--usr.sbin/smbmsg/smbmsg.c344
-rw-r--r--usr.sbin/snapinfo/Makefile10
-rw-r--r--usr.sbin/snapinfo/snapinfo.866
-rw-r--r--usr.sbin/snapinfo/snapinfo.c179
-rw-r--r--usr.sbin/spkrtest/Makefile6
-rw-r--r--usr.sbin/spkrtest/spkrtest.848
-rw-r--r--usr.sbin/spkrtest/spkrtest.sh113
-rw-r--r--usr.sbin/spray/Makefile9
-rw-r--r--usr.sbin/spray/spray.876
-rw-r--r--usr.sbin/spray/spray.c220
-rw-r--r--usr.sbin/sysinstall/Makefile154
-rw-r--r--usr.sbin/sysinstall/acpi.c356
-rw-r--r--usr.sbin/sysinstall/acpidump.h177
-rw-r--r--usr.sbin/sysinstall/anonFTP.c328
-rw-r--r--usr.sbin/sysinstall/biosmptable.c275
-rw-r--r--usr.sbin/sysinstall/cdrom.c221
-rw-r--r--usr.sbin/sysinstall/command.c184
-rw-r--r--usr.sbin/sysinstall/config.c1074
-rw-r--r--usr.sbin/sysinstall/devices.c569
-rw-r--r--usr.sbin/sysinstall/dhcp.c158
-rw-r--r--usr.sbin/sysinstall/disks.c1047
-rw-r--r--usr.sbin/sysinstall/dispatch.c636
-rw-r--r--usr.sbin/sysinstall/dist.c823
-rw-r--r--usr.sbin/sysinstall/dist.h61
-rw-r--r--usr.sbin/sysinstall/dmenu.c393
-rw-r--r--usr.sbin/sysinstall/doc.c123
-rw-r--r--usr.sbin/sysinstall/dos.c94
-rw-r--r--usr.sbin/sysinstall/floppy.c158
-rw-r--r--usr.sbin/sysinstall/ftp.c282
-rw-r--r--usr.sbin/sysinstall/globals.c96
-rw-r--r--usr.sbin/sysinstall/help/anonftp.hlp21
-rw-r--r--usr.sbin/sysinstall/help/configure.hlp10
-rw-r--r--usr.sbin/sysinstall/help/distributions.hlp34
-rw-r--r--usr.sbin/sysinstall/help/drives.hlp92
-rw-r--r--usr.sbin/sysinstall/help/fixit.hlp3
-rw-r--r--usr.sbin/sysinstall/help/html.hlp20
-rw-r--r--usr.sbin/sysinstall/help/media.hlp54
-rw-r--r--usr.sbin/sysinstall/help/network_device.hlp58
-rw-r--r--usr.sbin/sysinstall/help/options.hlp181
-rw-r--r--usr.sbin/sysinstall/help/partition.hlp169
-rw-r--r--usr.sbin/sysinstall/help/securelevel.hlp40
-rw-r--r--usr.sbin/sysinstall/help/shortcuts.hlp117
-rw-r--r--usr.sbin/sysinstall/help/slice.hlp57
-rw-r--r--usr.sbin/sysinstall/help/tcp.hlp42
-rw-r--r--usr.sbin/sysinstall/help/usage.hlp65
-rw-r--r--usr.sbin/sysinstall/help/usermgmt.hlp89
-rw-r--r--usr.sbin/sysinstall/http.c279
-rw-r--r--usr.sbin/sysinstall/index.c892
-rw-r--r--usr.sbin/sysinstall/install.c1278
-rw-r--r--usr.sbin/sysinstall/install.cfg97
-rw-r--r--usr.sbin/sysinstall/installUpgrade.c526
-rw-r--r--usr.sbin/sysinstall/keymap.c172
-rw-r--r--usr.sbin/sysinstall/label.c1690
-rw-r--r--usr.sbin/sysinstall/list.h60
-rw-r--r--usr.sbin/sysinstall/main.c204
-rw-r--r--usr.sbin/sysinstall/media.c834
-rw-r--r--usr.sbin/sysinstall/menus.c2090
-rw-r--r--usr.sbin/sysinstall/misc.c553
-rw-r--r--usr.sbin/sysinstall/modules.c224
-rw-r--r--usr.sbin/sysinstall/mouse.c103
-rw-r--r--usr.sbin/sysinstall/msg.c356
-rw-r--r--usr.sbin/sysinstall/network.c361
-rw-r--r--usr.sbin/sysinstall/nfs.c101
-rw-r--r--usr.sbin/sysinstall/options.c333
-rw-r--r--usr.sbin/sysinstall/package.c270
-rw-r--r--usr.sbin/sysinstall/rtermcap.c15
-rw-r--r--usr.sbin/sysinstall/sysinstall.8908
-rw-r--r--usr.sbin/sysinstall/sysinstall.h886
-rw-r--r--usr.sbin/sysinstall/system.c549
-rw-r--r--usr.sbin/sysinstall/tcpip.c701
-rw-r--r--usr.sbin/sysinstall/termcap.c150
-rw-r--r--usr.sbin/sysinstall/ttys.c162
-rw-r--r--usr.sbin/sysinstall/ufs.c49
-rw-r--r--usr.sbin/sysinstall/user.c752
-rw-r--r--usr.sbin/sysinstall/variable.c329
-rw-r--r--usr.sbin/sysinstall/wizard.c201
-rw-r--r--usr.sbin/syslogd/Makefile23
-rw-r--r--usr.sbin/syslogd/pathnames.h37
-rw-r--r--usr.sbin/syslogd/syslog.conf.5517
-rw-r--r--usr.sbin/syslogd/syslogd.8388
-rw-r--r--usr.sbin/syslogd/syslogd.c2695
-rw-r--r--usr.sbin/tcpdchk/Makefile21
-rw-r--r--usr.sbin/tcpdmatch/Makefile20
-rw-r--r--usr.sbin/tcpdrop/Makefile8
-rw-r--r--usr.sbin/tcpdrop/tcpdrop.864
-rw-r--r--usr.sbin/tcpdrop/tcpdrop.c91
-rw-r--r--usr.sbin/tcpdump/Makefile6
-rw-r--r--usr.sbin/tcpdump/Makefile.inc4
-rw-r--r--usr.sbin/tcpdump/tcpdump/Makefile69
-rw-r--r--usr.sbin/tcpdump/tcpdump/config.h359
-rw-r--r--usr.sbin/timed/Makefile6
-rw-r--r--usr.sbin/timed/SMM.doc/timed/Makefile12
-rw-r--r--usr.sbin/timed/SMM.doc/timed/date53
-rw-r--r--usr.sbin/timed/SMM.doc/timed/loop54
-rw-r--r--usr.sbin/timed/SMM.doc/timed/spell.ok34
-rw-r--r--usr.sbin/timed/SMM.doc/timed/time53
-rw-r--r--usr.sbin/timed/SMM.doc/timed/timed.ms462
-rw-r--r--usr.sbin/timed/SMM.doc/timed/unused53
-rw-r--r--usr.sbin/timed/SMM.doc/timedop/Makefile8
-rw-r--r--usr.sbin/timed/SMM.doc/timedop/timed.ms279
-rw-r--r--usr.sbin/timed/timed/CHANGES145
-rw-r--r--usr.sbin/timed/timed/Makefile17
-rw-r--r--usr.sbin/timed/timed/acksend.c132
-rw-r--r--usr.sbin/timed/timed/byteorder.c86
-rw-r--r--usr.sbin/timed/timed/candidate.c167
-rw-r--r--usr.sbin/timed/timed/cksum.c87
-rw-r--r--usr.sbin/timed/timed/correct.c198
-rw-r--r--usr.sbin/timed/timed/extern.h91
-rw-r--r--usr.sbin/timed/timed/globals.h172
-rw-r--r--usr.sbin/timed/timed/master.c852
-rw-r--r--usr.sbin/timed/timed/measure.c346
-rw-r--r--usr.sbin/timed/timed/networkdelta.c264
-rw-r--r--usr.sbin/timed/timed/pathnames.h41
-rw-r--r--usr.sbin/timed/timed/readmsg.c513
-rw-r--r--usr.sbin/timed/timed/slave.c693
-rw-r--r--usr.sbin/timed/timed/timed.8299
-rw-r--r--usr.sbin/timed/timed/timed.c851
-rw-r--r--usr.sbin/timed/timedc/Makefile15
-rw-r--r--usr.sbin/timed/timedc/cmds.c556
-rw-r--r--usr.sbin/timed/timedc/cmdtab.c61
-rw-r--r--usr.sbin/timed/timedc/extern.h54
-rw-r--r--usr.sbin/timed/timedc/timedc.8145
-rw-r--r--usr.sbin/timed/timedc/timedc.c261
-rw-r--r--usr.sbin/timed/timedc/timedc.h63
-rw-r--r--usr.sbin/traceroute/Makefile40
-rw-r--r--usr.sbin/traceroute6/Makefile26
-rw-r--r--usr.sbin/traceroute6/traceroute6.8178
-rw-r--r--usr.sbin/traceroute6/traceroute6.c1432
-rw-r--r--usr.sbin/trpt/Makefile17
-rw-r--r--usr.sbin/trpt/trpt.8149
-rw-r--r--usr.sbin/trpt/trpt.c463
-rw-r--r--usr.sbin/tzsetup/Makefile11
-rw-r--r--usr.sbin/tzsetup/tzsetup.8132
-rw-r--r--usr.sbin/tzsetup/tzsetup.c734
-rw-r--r--usr.sbin/ugidfw/Makefile9
-rw-r--r--usr.sbin/ugidfw/ugidfw.8361
-rw-r--r--usr.sbin/ugidfw/ugidfw.c214
-rw-r--r--usr.sbin/usbconfig/Makefile9
-rw-r--r--usr.sbin/usbconfig/dump.c322
-rw-r--r--usr.sbin/usbconfig/dump.h34
-rw-r--r--usr.sbin/usbconfig/usbconfig.853
-rw-r--r--usr.sbin/usbconfig/usbconfig.c711
-rw-r--r--usr.sbin/usbdevs/Makefile9
-rw-r--r--usr.sbin/usbdevs/usbdevs.877
-rw-r--r--usr.sbin/usbdevs/usbdevs.c240
-rw-r--r--usr.sbin/vidcontrol/Makefile8
-rw-r--r--usr.sbin/vidcontrol/decode.c99
-rw-r--r--usr.sbin/vidcontrol/decode.h3
-rw-r--r--usr.sbin/vidcontrol/path.h4
-rw-r--r--usr.sbin/vidcontrol/vidcontrol.1540
-rw-r--r--usr.sbin/vidcontrol/vidcontrol.c1288
-rw-r--r--usr.sbin/vipw/Makefile12
-rw-r--r--usr.sbin/vipw/vipw.8119
-rw-r--r--usr.sbin/vipw/vipw.c137
-rw-r--r--usr.sbin/watch/Makefile11
-rw-r--r--usr.sbin/watch/watch.8120
-rw-r--r--usr.sbin/watch/watch.c442
-rw-r--r--usr.sbin/watchdogd/Makefile14
-rw-r--r--usr.sbin/watchdogd/watchdog.873
-rw-r--r--usr.sbin/watchdogd/watchdogd.8127
-rw-r--r--usr.sbin/watchdogd/watchdogd.c295
-rw-r--r--usr.sbin/wlandebug/Makefile6
-rw-r--r--usr.sbin/wlandebug/wlandebug.8185
-rw-r--r--usr.sbin/wlandebug/wlandebug.c243
-rw-r--r--usr.sbin/wlconfig/Makefile7
-rw-r--r--usr.sbin/wlconfig/wlconfig.8143
-rw-r--r--usr.sbin/wlconfig/wlconfig.c417
-rw-r--r--usr.sbin/wpa/Makefile7
-rw-r--r--usr.sbin/wpa/Makefile.inc27
-rw-r--r--usr.sbin/wpa/hostapd/Makefile114
-rw-r--r--usr.sbin/wpa/hostapd/driver_freebsd.c921
-rw-r--r--usr.sbin/wpa/hostapd/hostapd.8134
-rw-r--r--usr.sbin/wpa/hostapd/hostapd.conf.5210
-rw-r--r--usr.sbin/wpa/hostapd_cli/Makefile15
-rw-r--r--usr.sbin/wpa/hostapd_cli/hostapd_cli.8112
-rw-r--r--usr.sbin/wpa/l2_packet.c297
-rw-r--r--usr.sbin/wpa/ndis_events/Makefile8
-rw-r--r--usr.sbin/wpa/ndis_events/ndis_events.8135
-rw-r--r--usr.sbin/wpa/ndis_events/ndis_events.c355
-rw-r--r--usr.sbin/wpa/wpa_cli/Makefile19
-rw-r--r--usr.sbin/wpa/wpa_cli/wpa_cli.8222
-rw-r--r--usr.sbin/wpa/wpa_passphrase/Makefile15
-rw-r--r--usr.sbin/wpa/wpa_passphrase/wpa_passphrase.866
-rw-r--r--usr.sbin/wpa/wpa_supplicant/Makefile150
-rw-r--r--usr.sbin/wpa/wpa_supplicant/Packet32.c415
-rw-r--r--usr.sbin/wpa/wpa_supplicant/Packet32.h67
-rw-r--r--usr.sbin/wpa/wpa_supplicant/driver_freebsd.c909
-rw-r--r--usr.sbin/wpa/wpa_supplicant/driver_wired.c185
-rw-r--r--usr.sbin/wpa/wpa_supplicant/ntddndis.h31
-rw-r--r--usr.sbin/wpa/wpa_supplicant/wpa_supplicant.8155
-rw-r--r--usr.sbin/wpa/wpa_supplicant/wpa_supplicant.conf.5545
-rw-r--r--usr.sbin/yp_mkdb/Makefile12
-rw-r--r--usr.sbin/yp_mkdb/yp_mkdb.8209
-rw-r--r--usr.sbin/yp_mkdb/yp_mkdb.c342
-rw-r--r--usr.sbin/ypbind/Makefile10
-rw-r--r--usr.sbin/ypbind/yp_ping.c310
-rw-r--r--usr.sbin/ypbind/yp_ping.h5
-rw-r--r--usr.sbin/ypbind/ypbind.8202
-rw-r--r--usr.sbin/ypbind/ypbind.c990
-rw-r--r--usr.sbin/yppoll/Makefile7
-rw-r--r--usr.sbin/yppoll/yppoll.884
-rw-r--r--usr.sbin/yppoll/yppoll.c97
-rw-r--r--usr.sbin/yppush/Makefile28
-rw-r--r--usr.sbin/yppush/yppush.8180
-rw-r--r--usr.sbin/yppush/yppush_extern.h44
-rw-r--r--usr.sbin/yppush/yppush_main.c621
-rw-r--r--usr.sbin/ypserv/Makefile42
-rw-r--r--usr.sbin/ypserv/Makefile.yp607
-rw-r--r--usr.sbin/ypserv/yp_access.c330
-rw-r--r--usr.sbin/ypserv/yp_dblookup.c733
-rw-r--r--usr.sbin/ypserv/yp_dnslookup.c551
-rw-r--r--usr.sbin/ypserv/yp_error.c73
-rw-r--r--usr.sbin/ypserv/yp_extern.h115
-rw-r--r--usr.sbin/ypserv/yp_main.c391
-rw-r--r--usr.sbin/ypserv/yp_server.c982
-rw-r--r--usr.sbin/ypserv/yp_svc_udp.c71
-rw-r--r--usr.sbin/ypserv/ypinit.8198
-rw-r--r--usr.sbin/ypserv/ypinit.sh386
-rw-r--r--usr.sbin/ypserv/ypserv.8452
-rw-r--r--usr.sbin/ypset/Makefile7
-rw-r--r--usr.sbin/ypset/ypset.888
-rw-r--r--usr.sbin/ypset/ypset.c150
-rw-r--r--usr.sbin/zic/Arts.htm178
-rw-r--r--usr.sbin/zic/Makefile8
-rw-r--r--usr.sbin/zic/Makefile.inc3
-rw-r--r--usr.sbin/zic/README80
-rw-r--r--usr.sbin/zic/Theory552
-rw-r--r--usr.sbin/zic/ialloc.c86
-rw-r--r--usr.sbin/zic/private.h193
-rw-r--r--usr.sbin/zic/scheck.c64
-rw-r--r--usr.sbin/zic/tz-art.htm278
-rw-r--r--usr.sbin/zic/tz-link.htm443
-rw-r--r--usr.sbin/zic/zdump.849
-rw-r--r--usr.sbin/zic/zdump.c375
-rw-r--r--usr.sbin/zic/zdump/Makefile13
-rw-r--r--usr.sbin/zic/zic.8394
-rw-r--r--usr.sbin/zic/zic.c2254
-rw-r--r--usr.sbin/zic/zic/Makefile14
-rw-r--r--usr.sbin/zzz/Makefile6
-rw-r--r--usr.sbin/zzz/zzz.865
-rw-r--r--usr.sbin/zzz/zzz.sh43
1663 files changed, 455842 insertions, 0 deletions
diff --git a/usr.sbin/IPXrouted/IPXrouted.8 b/usr.sbin/IPXrouted/IPXrouted.8
new file mode 100644
index 0000000..6c3bd54
--- /dev/null
+++ b/usr.sbin/IPXrouted/IPXrouted.8
@@ -0,0 +1,224 @@
+.\" Copyright (c) 1986, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Copyright (c) 1995 John Hay. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd October 11, 1995
+.Dt IPXROUTED 8
+.Os
+.Sh NAME
+.Nm IPXrouted
+.Nd IPX Routing Information Protocol daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl N
+.Op Fl q
+.Op Fl s
+.Op Fl S
+.Op Fl t
+.Op Ar logfile
+.Sh DESCRIPTION
+The
+.Nm
+utility is invoked at boot time to manage the
+.Tn IPX
+routing tables.
+The
+.Tn IPX
+routing daemon uses the Novell
+.Tn IPX
+Routing Information Protocol in maintaining up to date kernel routing
+table entries.
+.Pp
+Available options:
+.Bl -tag -width logfile
+.It Fl N
+Do not reply on GetNearestServer
+.Tn SAP
+request.
+.It Fl q
+Do not supply routing information (opposite of
+.Fl s
+option below).
+.It Fl s
+Forces
+.Nm
+to supply routing information whether it is acting as an internetwork
+router or not.
+.It Fl S
+Do not supply Service Advertising Protocol
+.Pq Tn SAP
+information.
+The default is to supply
+.Tn SAP
+information.
+.It Fl t
+All packets sent or received are
+printed on the standard output.
+In addition,
+.Nm
+will not divorce itself from the controlling terminal
+so that interrupts from the keyboard will kill the process.
+.It Ar logfile
+Name of file in which
+.Nm Ns 's
+actions should be logged.
+This log contains information
+about any changes to the routing tables and a history of
+recent messages sent and received which are related to
+the changed route.
+.El
+.Pp
+In normal operation
+.Nm
+listens
+for routing information packets.
+If the host is connected to
+multiple
+.Tn IPX
+networks, it periodically supplies copies
+of its routing tables to any directly connected hosts
+and networks.
+.Pp
+When
+.Nm
+is started, it uses the
+.Dv SIOCGIFCONF
+.Xr ioctl 2
+to find those
+directly connected interfaces configured into the
+system and marked
+.Dq up
+(the software loopback interface is ignored).
+If multiple interfaces
+are present, it is assumed the host will forward packets
+between networks.
+The
+.Nm
+utility then transmits a
+.Em request
+packet on each interface (using a broadcast packet if
+the interface supports it) and enters a loop, listening
+for
+.Em request
+and
+.Em response
+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
+.Dq hop count
+metric (a count of 16, or greater, is
+considered
+.Dq infinite ) .
+The metric associated with each
+route returned provides a metric
+.Em relative to the sender .
+.Pp
+.Em Response
+packets received by
+.Nm
+are used to update the routing tables if one of the following
+conditions is satisfied:
+.Bl -bullet
+.It
+No routing table entry exists for the destination network
+or host, and the metric indicates the destination is
+.Dq reachable
+(i.e., the hop count is not infinite).
+.It
+The source host of the packet is the same as the router in the
+existing routing table entry.
+That is, updated information is
+being received from the very internetwork router through which
+packets for the destination are being routed.
+.It
+The existing entry in the routing table has not been updated for
+some time (defined to be 90 seconds) and the route is at least
+as cost effective as the current route.
+.It
+The new route describes a shorter route to the destination than
+the one currently stored in the routing tables; the metric of
+the new route is compared against the one stored in the table
+to decide this.
+.El
+.Pp
+When an update is applied,
+.Nm
+records the change in its internal tables and generates a
+.Em response
+packet to all directly connected hosts and networks.
+The
+.Xr routed 8
+utility waits a short period
+of time (no more than 30 seconds) before modifying the kernel's
+routing tables to allow possible unstable situations to settle.
+.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
+an additional 60 seconds to insure the invalidation is propagated
+to other routers.
+.Pp
+Hosts acting as internetwork routers gratuitously supply their
+routing tables every 30 seconds to all directly connected hosts
+and networks.
+.Pp
+If
+.Nm
+receives a
+.Dv SIGINFO
+signal the current contents of the
+.Tn RIP
+and
+.Tn SAP
+tables are appended to the file
+.Pa /var/log/ipxrouted.dmp .
+.Sh SEE ALSO
+.Xr ipx 3
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 2.2 .
diff --git a/usr.sbin/IPXrouted/Makefile b/usr.sbin/IPXrouted/Makefile
new file mode 100644
index 0000000..44605ac
--- /dev/null
+++ b/usr.sbin/IPXrouted/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+PROG= IPXrouted
+MAN= IPXrouted.8
+SRCS= af.c if.c input.c main.c output.c startup.c tables.c timer.c trace.c
+SRCS+= sap_input.c sap_tables.c sap_output.c
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/IPXrouted/af.c b/usr.sbin/IPXrouted/af.c
new file mode 100644
index 0000000..ea61762
--- /dev/null
+++ b/usr.sbin/IPXrouted/af.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * This file includes significant work done at Cornell University by
+ * Bill Nesheim. That work included by permission.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+#ifndef lint
+static const char sccsid[] = "@(#)af.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include "defs.h"
+
+/*
+ * Address family support routines
+ */
+af_hash_t null_hash;
+af_netmatch_t null_netmatch;
+af_output_t null_output;
+af_portmatch_t null_portmatch;
+af_portcheck_t null_portcheck;
+af_checkhost_t null_checkhost;
+af_ishost_t null_ishost;
+af_canon_t null_canon;
+
+void ipxnet_hash(struct sockaddr_ipx *, struct afhash *);
+int ipxnet_netmatch(struct sockaddr_ipx *, struct sockaddr_ipx *);
+void ipxnet_output(int, int, struct sockaddr_ipx *, int);
+int ipxnet_portmatch(struct sockaddr_ipx *);
+int ipxnet_checkhost(struct sockaddr_ipx *);
+int ipxnet_ishost(struct sockaddr_ipx *);
+void ipxnet_canon(struct sockaddr_ipx *);
+
+#define NIL \
+ { null_hash, null_netmatch, null_output, \
+ null_portmatch, null_portcheck, null_checkhost, \
+ null_ishost, null_canon }
+#define IPXNET \
+ { (af_hash_t *)ipxnet_hash, \
+ (af_netmatch_t *)ipxnet_netmatch, \
+ (af_output_t *)ipxnet_output, \
+ (af_portmatch_t *)ipxnet_portmatch, \
+ (af_portcheck_t *)ipxnet_portmatch, \
+ (af_checkhost_t *)ipxnet_checkhost, \
+ (af_ishost_t *)ipxnet_ishost, \
+ (af_canon_t *)ipxnet_canon }
+
+struct afswitch afswitch[AF_MAX] =
+ { NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
+ NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
+ NIL, NIL, NIL, IPXNET, NIL, NIL };
+
+struct sockaddr_ipx ipxnet_default = { sizeof(struct sockaddr_ipx), AF_IPX };
+
+union ipx_net ipx_anynet;
+union ipx_net ipx_zeronet;
+
+void
+ipxnet_hash(sipx, hp)
+ register struct sockaddr_ipx *sipx;
+ struct afhash *hp;
+{
+ long hash;
+#if 0
+ u_short *s = sipx->sipx_addr.x_host.s_host;
+#endif
+ u_char *c;
+
+ c = sipx->sipx_addr.x_net.c_net;
+
+#define IMVAL 33
+ hash = 0;
+ hash = hash * IMVAL + *c++;
+ hash = hash * IMVAL + *c++;
+ hash = hash * IMVAL + *c++;
+ hash = hash * IMVAL + *c++;
+#undef IMVAL
+
+ hp->afh_nethash = hash;
+ hp->afh_nethash ^= (hash >> 8);
+ hp->afh_nethash ^= (hash >> 16);
+ hp->afh_nethash ^= (hash >> 24);
+
+#if 0
+ hash = 0;
+ hash = *s++; hash <<= 8; hash += *s++; hash <<= 8; hash += *s;
+ hp->afh_hosthash = hash;
+#endif
+}
+
+int
+ipxnet_netmatch(sxn1, sxn2)
+ struct sockaddr_ipx *sxn1, *sxn2;
+{
+ return (ipx_neteq(sxn1->sipx_addr, sxn2->sipx_addr));
+}
+
+/*
+ * Verify the message is from the right port.
+ */
+int
+ipxnet_portmatch(sipx)
+ register struct sockaddr_ipx *sipx;
+{
+
+ return (ntohs(sipx->sipx_addr.x_port) == IPXPORT_RIP );
+}
+
+
+/*
+ * ipx output routine.
+ */
+#ifdef DEBUG
+int do_output = 0;
+#endif
+void
+ipxnet_output(s, flags, sipx, size)
+ int s;
+ int flags;
+ struct sockaddr_ipx *sipx;
+ int size;
+{
+ struct sockaddr_ipx dst;
+
+ dst = *sipx;
+ sipx = &dst;
+ if (sipx->sipx_addr.x_port == 0)
+ sipx->sipx_addr.x_port = htons(IPXPORT_RIP);
+#ifdef DEBUG
+ if(do_output || ntohs(msg->rip_cmd) == RIPCMD_REQUEST)
+#endif
+ /*
+ * Kludge to allow us to get routes out to machines that
+ * don't know their addresses yet; send to that address on
+ * ALL connected nets
+ */
+ if (ipx_neteqnn(sipx->sipx_addr.x_net, ipx_zeronet)) {
+ extern struct interface *ifnet;
+ register struct interface *ifp;
+
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ sipx->sipx_addr.x_net =
+ satoipx_addr(ifp->int_addr).x_net;
+ (void) sendto(s, msg, size, flags,
+ (struct sockaddr *)sipx, sizeof (*sipx));
+ }
+ return;
+ }
+
+ (void) sendto(s, msg, size, flags,
+ (struct sockaddr *)sipx, sizeof (*sipx));
+}
+
+/*
+ * Return 1 if we want this route.
+ * We use this to disallow route net G entries for one for multiple
+ * point to point links.
+ */
+int
+ipxnet_checkhost(sipx)
+ struct sockaddr_ipx *sipx;
+{
+ register struct interface *ifp = if_ifwithnet((struct sockaddr *)sipx);
+ /*
+ * We want this route if there is no more than one
+ * point to point interface with this network.
+ */
+ if (ifp == 0 || (ifp->int_flags & IFF_POINTOPOINT)==0) return (1);
+ return (ifp->int_sq.n == ifp->int_sq.p);
+}
+
+/*
+ * Return 1 if the address is
+ * for a host, 0 for a network.
+ */
+int
+ipxnet_ishost(sipx)
+struct sockaddr_ipx *sipx;
+{
+ register u_short *s = sipx->sipx_addr.x_host.s_host;
+
+ if ((s[0]==0x0000) && (s[1]==0x0000) && (s[2]==0x0000))
+ return (0);
+ if ((s[0]==0xffff) && (s[1]==0xffff) && (s[2]==0xffff))
+ return (0);
+
+ return (1);
+}
+
+void
+ipxnet_canon(sipx)
+ struct sockaddr_ipx *sipx;
+{
+
+ sipx->sipx_addr.x_port = 0;
+}
+
+void
+null_hash(addr, hp)
+ struct sockaddr *addr;
+ struct afhash *hp;
+{
+
+ hp->afh_nethash = hp->afh_hosthash = 0;
+}
+
+int
+null_netmatch(a1, a2)
+ struct sockaddr *a1, *a2;
+{
+
+ return (0);
+}
+
+void
+null_output(s, f, a1, n)
+ int s;
+ int f;
+ struct sockaddr *a1;
+ int n;
+{
+
+ ;
+}
+
+int
+null_portmatch(a1)
+ struct sockaddr *a1;
+{
+
+ return (0);
+}
+
+int
+null_portcheck(a1)
+ struct sockaddr *a1;
+{
+
+ return (0);
+}
+
+int
+null_ishost(a1)
+ struct sockaddr *a1;
+{
+
+ return (0);
+}
+
+int
+null_checkhost(a1)
+ struct sockaddr *a1;
+{
+
+ return (0);
+}
+
+void
+null_canon(a1)
+ struct sockaddr *a1;
+{
+
+ ;
+}
+
diff --git a/usr.sbin/IPXrouted/af.h b/usr.sbin/IPXrouted/af.h
new file mode 100644
index 0000000..576a6c7
--- /dev/null
+++ b/usr.sbin/IPXrouted/af.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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.
+ *
+ * @(#)af.h 5.1 (Berkeley) 6/4/85 (routed/af.h)
+ *
+ * @(#)af.h 8.1 (Berkeley) 6/5/93
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Routing table management daemon.
+ */
+
+/*
+ * Structure returned by af_hash routines.
+ */
+struct afhash {
+ u_int afh_hosthash; /* host based hash */
+ u_int afh_nethash; /* network based hash */
+};
+
+/*
+ * Per address family routines.
+ */
+typedef void af_hash_t(struct sockaddr *, struct afhash *);
+typedef int af_netmatch_t(struct sockaddr *, struct sockaddr *);
+typedef void af_output_t(int, int, struct sockaddr *, int);
+typedef int af_portmatch_t(struct sockaddr *);
+typedef int af_portcheck_t(struct sockaddr *);
+typedef int af_checkhost_t(struct sockaddr *);
+typedef int af_ishost_t(struct sockaddr *);
+typedef void af_canon_t(struct sockaddr *);
+
+struct afswitch {
+ af_hash_t *af_hash; /* returns keys based on address */
+ af_netmatch_t *af_netmatch; /* verifies net # matching */
+ af_output_t *af_output; /* interprets address for sending */
+ af_portmatch_t *af_portmatch; /* packet from some other router? */
+ af_portcheck_t *af_portcheck; /* packet from privileged peer? */
+ af_checkhost_t *af_checkhost; /* tells if address for host or net */
+ af_ishost_t *af_ishost; /* tells if address is valid */
+ af_canon_t *af_canon; /* canonicalize address for compares */
+};
+
+struct afswitch afswitch[AF_MAX]; /* table proper */
diff --git a/usr.sbin/IPXrouted/defs.h b/usr.sbin/IPXrouted/defs.h
new file mode 100644
index 0000000..c2b28a7
--- /dev/null
+++ b/usr.sbin/IPXrouted/defs.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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.
+ *
+ * @(#)defs.h 8.1 (Berkeley) 6/5/93
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <arpa/inet.h>
+#include <net/route.h>
+#include <netipx/ipx.h>
+#if defined(vax) || defined(pdp11)
+#define xnnet(x) ((u_long) (x)->rip_dst[1] << 16 | (u_long) (x)->rip_dst[0] )
+#else
+#define xnnet(x) ((u_long) (x)->rip_dst[0] << 16 | (u_long) (x)->rip_dst[1] )
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "protocol.h"
+#include "sap.h"
+#include "table.h"
+#include "trace.h"
+#include "interface.h"
+#include "af.h"
+
+
+/*
+ * When we find any interfaces marked down we rescan the
+ * kernel every CHECK_INTERVAL seconds to see if they've
+ * come up.
+ */
+#define CHECK_INTERVAL (5*60)
+
+#define equal(a1, a2) \
+ (bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0)
+#define min(a,b) ((a)>(b)?(b):(a))
+#define max(a,b) ((a)<(b)?(b):(a))
+
+extern int ripsock; /* Socket to listen on */
+extern int sapsock; /* Socket to listen on */
+extern int kmem;
+extern int supplier; /* process should supply updates */
+extern int dosap; /* SAP is enabled */
+extern int dognreply; /* enable GET_NEAREST response */
+extern int install; /* if 1 call kernel */
+extern int lookforinterfaces; /* if 1 probe kernel for new up ifs */
+extern int performnlist; /* if 1 check if /kernel has changed */
+extern int externalinterfaces; /* # of remote and local interfaces */
+extern int timeval; /* local idea of time */
+extern int noteremoterequests; /* squawk on requests from non-local nets */
+extern int r; /* Routing socket to install updates with */
+extern int gateway;
+extern struct sockaddr_ipx ipx_netmask; /* Used in installing routes */
+
+extern char packet[MAXRXPACKETSIZE+1];
+extern struct rip *msg;
+
+extern char **argv0;
+
+#define ADD 1
+#define DELETE 2
+#define CHANGE 3
+
+void sndmsg(struct sockaddr *, int, struct interface *, int);
+void supply(struct sockaddr *, int, struct interface *, int);
+void addrouteforif(struct interface *);
+void ifinit(void);
+void toall(void (*f)(struct sockaddr *, int, struct interface *, int),
+ struct rt_entry *, int);
+void rip_input(struct sockaddr *, int);
+
diff --git a/usr.sbin/IPXrouted/if.c b/usr.sbin/IPXrouted/if.c
new file mode 100644
index 0000000..2c6e664
--- /dev/null
+++ b/usr.sbin/IPXrouted/if.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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.
+ *
+ * static char sccsid[] = "@(#)if.c 5.1 (Berkeley) 6/4/85"; (routed/if.c)
+ *
+ * $FreeBSD$
+ */
+
+#ifndef lint
+static const char sccsid[] = "@(#)if.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+/*
+ * Routing Table Management Daemon
+ */
+#include "defs.h"
+
+extern struct interface *ifnet;
+
+/*
+ * Find the interface with address addr.
+ */
+struct interface *
+if_ifwithaddr(addr)
+ struct sockaddr *addr;
+{
+ register struct interface *ifp;
+
+#define same(a1, a2) \
+ (bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 10) == 0)
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ if (ifp->int_flags & IFF_REMOTE)
+ continue;
+ if (ifp->int_addr.sa_family != addr->sa_family)
+ continue;
+ if (same(&ifp->int_addr, addr))
+ break;
+ if ((ifp->int_flags & IFF_BROADCAST) &&
+ same(&ifp->int_broadaddr, addr))
+ break;
+ }
+ return (ifp);
+}
+
+/*
+ * Find the point-to-point interface with destination address addr.
+ */
+struct interface *
+if_ifwithdstaddr(addr)
+ struct sockaddr *addr;
+{
+ register struct interface *ifp;
+
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ if ((ifp->int_flags & IFF_POINTOPOINT) == 0)
+ continue;
+ if (same(&ifp->int_dstaddr, addr))
+ break;
+ }
+ return (ifp);
+}
+
+/*
+ * Find the interface on the network
+ * of the specified address.
+ */
+struct interface *
+if_ifwithnet(addr)
+ register struct sockaddr *addr;
+{
+ register struct interface *ifp;
+ register int af = addr->sa_family;
+ register int (*netmatch)();
+
+ if (af >= AF_MAX)
+ return (0);
+ netmatch = afswitch[af].af_netmatch;
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ if (ifp->int_flags & IFF_REMOTE)
+ continue;
+ if (af != ifp->int_addr.sa_family)
+ continue;
+ if ((*netmatch)(addr, &ifp->int_addr))
+ break;
+ }
+ return (ifp);
+}
+
+/*
+ * Find an interface from which the specified address
+ * should have come from. Used for figuring out which
+ * interface a packet came in on -- for tracing.
+ */
+struct interface *
+if_iflookup(addr)
+ struct sockaddr *addr;
+{
+ register struct interface *ifp, *maybe;
+ register int af = addr->sa_family;
+ register int (*netmatch)();
+
+ if (af >= AF_MAX)
+ return (0);
+ maybe = 0;
+ netmatch = afswitch[af].af_netmatch;
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ if (ifp->int_addr.sa_family != af)
+ continue;
+ if (same(&ifp->int_addr, addr))
+ break;
+ if ((ifp->int_flags & IFF_BROADCAST) &&
+ same(&ifp->int_broadaddr, addr))
+ break;
+ if (maybe == 0 && (*netmatch)(addr, &ifp->int_addr))
+ maybe = ifp;
+ }
+ if (ifp == 0)
+ ifp = maybe;
+ return (ifp);
+}
diff --git a/usr.sbin/IPXrouted/input.c b/usr.sbin/IPXrouted/input.c
new file mode 100644
index 0000000..651a8ae
--- /dev/null
+++ b/usr.sbin/IPXrouted/input.c
@@ -0,0 +1,304 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * This file includes significant work done at Cornell University by
+ * Bill Nesheim. That work included by permission.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef lint
+static const char sccsid[] = "@(#)input.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+/*
+ * IPX Routing Table Management Daemon
+ */
+#include "defs.h"
+
+struct sockaddr *
+ipx_nettosa(net)
+union ipx_net net;
+{
+ static struct sockaddr_ipx sxn;
+
+ bzero(&sxn, sizeof (struct sockaddr_ipx));
+ sxn.sipx_family = AF_IPX;
+ sxn.sipx_len = sizeof (sxn);
+ sxn.sipx_addr.x_net = net;
+ return( (struct sockaddr *)&sxn);
+
+}
+
+/*
+ * Process a newly received packet.
+ */
+void
+rip_input(from, size)
+ struct sockaddr *from;
+ int size;
+{
+ int newsize;
+ int rtchanged = 0;
+ struct rt_entry *rt;
+ struct netinfo *n;
+ struct interface *ifp = 0;
+ struct afswitch *afp;
+ struct sockaddr_ipx *ipxp;
+
+ ifp = if_ifwithnet(from);
+ ipxp = (struct sockaddr_ipx *)from;
+ if (ifp == 0) {
+ if(ftrace) {
+ fprintf(ftrace, "Received bogus packet from %s\n",
+ ipxdp_ntoa(&ipxp->sipx_addr));
+ }
+ return;
+ }
+
+ TRACE_INPUT(ifp, from, size);
+ if (from->sa_family >= AF_MAX)
+ return;
+ afp = &afswitch[from->sa_family];
+
+ size -= sizeof (u_short) /* command */;
+ n = msg->rip_nets;
+
+ switch (ntohs(msg->rip_cmd)) {
+
+ case RIPCMD_REQUEST:
+ if (ipx_hosteq(satoipx_addr(ifp->int_addr), ipxp->sipx_addr))
+ return;
+ newsize = 0;
+ while (size > 0) {
+ if (size < sizeof (struct netinfo))
+ break;
+ size -= sizeof (struct netinfo);
+
+ /*
+ * A single entry with rip_dst == DSTNETS_ALL and
+ * metric ``infinity'' means ``all routes''.
+ *
+ * XXX According to the IPX RIP spec the metric
+ * and tick fields can be anything. So maybe we
+ * should not check the metric???
+ */
+ if (ipx_neteqnn(n->rip_dst, ipx_anynet) &&
+ ntohs(n->rip_metric) == HOPCNT_INFINITY &&
+ size == 0) {
+ supply(from, 0, ifp, 0);
+ return;
+ }
+ /*
+ * request for specific nets
+ */
+ rt = rtlookup(ipx_nettosa(n->rip_dst));
+ if (ftrace) {
+ fprintf(ftrace,
+ "specific request for %s",
+ ipxdp_nettoa(n->rip_dst));
+ fprintf(ftrace,
+ " yields route %lx\n",
+ (u_long)rt);
+ }
+ /*
+ * XXX We break out on the first net that isn't
+ * found. The specs is a bit vague here. I'm not
+ * sure what we should do.
+ */
+ if (rt == 0)
+ return;
+ /* XXX
+ * According to the spec we should not include
+ * information about networks for which the number
+ * of hops is 16.
+ */
+ if (rt->rt_metric == (HOPCNT_INFINITY-1))
+ return;
+ n->rip_metric = htons( rt == 0 ? HOPCNT_INFINITY :
+ min(rt->rt_metric+1, HOPCNT_INFINITY));
+ n->rip_ticks = htons(rt->rt_ticks+1);
+
+ /*
+ * We use split horizon with a twist. If the requested
+ * net is the directly connected net we supply an
+ * answer. This is so that the host can learn about
+ * the routers on its net.
+ */
+ {
+ register struct rt_entry *trt = rt;
+
+ while (trt) {
+ if ((trt->rt_ifp == ifp) &&
+ !ipx_neteqnn(n->rip_dst,
+ satoipx_addr(ifp->int_addr).x_net))
+ return;
+ trt = trt->rt_clone;
+ }
+ n++;
+ newsize += sizeof (struct netinfo);
+ }
+ }
+ if (newsize > 0) {
+ msg->rip_cmd = htons(RIPCMD_RESPONSE);
+ newsize += sizeof (u_short);
+ /* should check for if with dstaddr(from) first */
+ (*afp->af_output)(ripsock, 0, from, newsize);
+ TRACE_OUTPUT(ifp, from, newsize);
+ if (ftrace) {
+ /* XXX This should not happen anymore. */
+ if(ifp == 0)
+ fprintf(ftrace, "--- ifp = 0\n");
+ else
+ fprintf(ftrace,
+ "request arrived on interface %s\n",
+ ifp->int_name);
+ }
+ }
+ return;
+
+ case RIPCMD_RESPONSE:
+ /* verify message came from a router */
+ if ((*afp->af_portmatch)(from) == 0)
+ return;
+ (*afp->af_canon)(from);
+ /* are we talking to ourselves? */
+ if ((ifp = if_ifwithaddr(from)) != 0) {
+ rt = rtfind(from);
+ if (rt == 0 || (rt->rt_state & RTS_INTERFACE) == 0) {
+ addrouteforif(ifp);
+ rtchanged = 1;
+ } else
+ rt->rt_timer = 0;
+ return;
+ }
+ /* Update timer for interface on which the packet arrived.
+ * If from other end of a point-to-point link that isn't
+ * in the routing tables, (re-)add the route.
+ */
+ if ((rt = rtfind(from)) && (rt->rt_state & RTS_INTERFACE)) {
+ if(ftrace) fprintf(ftrace, "Got route\n");
+ rt->rt_timer = 0;
+ } else if ((ifp = if_ifwithdstaddr(from)) != 0) {
+ if(ftrace) fprintf(ftrace, "Got partner\n");
+ addrouteforif(ifp);
+ rtchanged = 1;
+ }
+ for (; size > 0; size -= sizeof (struct netinfo), n++) {
+ struct sockaddr *sa;
+ if (size < sizeof (struct netinfo))
+ break;
+ if ((unsigned) ntohs(n->rip_metric) > HOPCNT_INFINITY)
+ continue;
+ rt = rtfind(sa = ipx_nettosa(n->rip_dst));
+ if (rt == 0) {
+ if (ntohs(n->rip_metric) == HOPCNT_INFINITY)
+ continue;
+ rtadd(sa, from, ntohs(n->rip_metric),
+ ntohs(n->rip_ticks), 0);
+ rtchanged = 1;
+ continue;
+ }
+
+ /*
+ * A clone is a different route to the same net
+ * with exactly the same cost (ticks and metric).
+ * They must all be recorded because those interfaces
+ * must be handled in the same way as the first route
+ * to that net. ie When using the split horizon
+ * algorithm we must look at these interfaces also.
+ *
+ * Update if from gateway and different,
+ * from anywhere and less ticks or
+ * if same ticks and shorter,
+ * or getting stale and equivalent.
+ */
+ if (!equal(from, &rt->rt_router) &&
+ ntohs(n->rip_ticks) == rt->rt_ticks &&
+ ntohs(n->rip_metric) == rt->rt_metric &&
+ ntohs(n->rip_metric) != HOPCNT_INFINITY) {
+ register struct rt_entry *trt = rt->rt_clone;
+
+ while (trt) {
+ if (equal(from, &trt->rt_router)) {
+ trt->rt_timer = 0;
+ break;
+ }
+ trt = trt->rt_clone;
+ }
+ if (trt == NULL) {
+ rtadd_clone(rt, sa, from,
+ ntohs(n->rip_metric),
+ ntohs(n->rip_ticks), 0);
+ }
+ continue;
+ }
+ if ((equal(from, &rt->rt_router) &&
+ ((ntohs(n->rip_ticks) != rt->rt_ticks) ||
+ (ntohs(n->rip_metric) != rt->rt_metric))) ||
+ (ntohs(n->rip_ticks) < rt->rt_ticks) ||
+ ((ntohs(n->rip_ticks) == rt->rt_ticks) &&
+ (ntohs(n->rip_metric) < rt->rt_metric)) ||
+ (rt->rt_timer > (EXPIRE_TIME*2/3) &&
+ rt->rt_metric == ntohs(n->rip_metric) &&
+ ntohs(n->rip_metric) != HOPCNT_INFINITY)) {
+ rtchange(rt, from, ntohs(n->rip_metric),
+ ntohs(n->rip_ticks));
+ if (ntohs(n->rip_metric) == HOPCNT_INFINITY)
+ rt->rt_timer = EXPIRE_TIME;
+ else
+ rt->rt_timer = 0;
+ rtchanged = 1;
+ } else if (equal(from, &rt->rt_router) &&
+ (ntohs(n->rip_ticks) == rt->rt_ticks) &&
+ (ntohs(n->rip_metric) == rt->rt_metric) &&
+ (ntohs(n->rip_metric) != HOPCNT_INFINITY)) {
+ rt->rt_timer = 0;
+ }
+ }
+ if (rtchanged) {
+ register struct rthash *rh;
+ register struct rt_entry *rt;
+
+ toall(supply, NULL, 1);
+ for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
+ for (rt = rh->rt_forw;
+ rt != (struct rt_entry *)rh;
+ rt = rt->rt_forw)
+ rt->rt_state &= ~RTS_CHANGED;
+ }
+
+ return;
+ }
+}
diff --git a/usr.sbin/IPXrouted/interface.h b/usr.sbin/IPXrouted/interface.h
new file mode 100644
index 0000000..ed7b988
--- /dev/null
+++ b/usr.sbin/IPXrouted/interface.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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.
+ *
+ * @(#)interface.h 8.1 (Berkeley) 6/5/93
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Routing table management daemon.
+ */
+
+/*
+ * An ``interface'' is similar to an ifnet structure,
+ * except it doesn't contain q'ing info, and it also
+ * handles ``logical'' interfaces (remote gateways
+ * that we want to keep polling even if they go down).
+ * The list of interfaces which we maintain is used
+ * in supplying the gratuitous routing table updates.
+ * We list only one address for each interface, the AF_IPX one.
+ */
+struct interface {
+ struct interface *int_next;
+ struct sockaddr int_addr; /* address on this host */
+ union {
+ struct sockaddr intu_broadaddr;
+ struct sockaddr intu_dstaddr;
+ } int_intu;
+#define int_broadaddr int_intu.intu_broadaddr /* broadcast address */
+#define int_dstaddr int_intu.intu_dstaddr /* other end of p-to-p link */
+ int int_metric; /* init's routing entry */
+ int int_flags; /* see below */
+ struct ifdebug int_input, int_output; /* packet tracing stuff */
+ int int_ipackets; /* input packets received */
+ int int_opackets; /* output packets sent */
+ char *int_name; /* from kernel if structure */
+ u_short int_transitions; /* times gone up-down */
+
+ /* XXX IPX Specific entry */
+ struct sameq {
+ struct sameq *n; /* q of other pt-to-pt links */
+ struct sameq *p; /* with same net # */
+ } int_sq;
+};
+
+/*
+ * 0x1 to 0x10 are reused from the kernel's ifnet definitions,
+ * the others agree with the RTS_ flags defined elsewhere.
+ */
+#define IFF_UP 0x1 /* interface is up */
+#define IFF_BROADCAST 0x2 /* broadcast address valid */
+#define IFF_DEBUG 0x4 /* turn on debugging */
+#define IFF_ROUTE 0x8 /* routing entry installed */
+#define IFF_POINTOPOINT 0x10 /* interface is point-to-point link */
+
+#define IFF_PASSIVE 0x200000 /* can't tell if up/down */
+#define IFF_INTERFACE 0x400000 /* hardware interface */
+#define IFF_REMOTE 0x800000 /* interface isn't on this machine */
+
+struct interface *if_ifwithaddr(struct sockaddr *);
+struct interface *if_ifwithdstaddr(struct sockaddr *);
+struct interface *if_ifwithnet(struct sockaddr *);
+struct interface *if_iflookup(struct sockaddr *);
+
diff --git a/usr.sbin/IPXrouted/main.c b/usr.sbin/IPXrouted/main.c
new file mode 100644
index 0000000..773410a
--- /dev/null
+++ b/usr.sbin/IPXrouted/main.c
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * This file includes significant work done at Cornell University by
+ * Bill Nesheim. That work included by permission.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1985, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static const char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+/*
+ * IPX Routing Information Protocol Daemon
+ */
+#include "defs.h"
+#include <sys/time.h>
+
+#include <net/if.h>
+
+#include <errno.h>
+#include <nlist.h>
+#include <signal.h>
+#include <paths.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define SAP_PKT 0
+#define RIP_PKT 1
+
+struct sockaddr_ipx addr; /* Daemon's Address */
+int ripsock; /* RIP Socket to listen on */
+int sapsock; /* SAP Socket to listen on */
+int kmem;
+int install; /* if 1 call kernel */
+int lookforinterfaces; /* if 1 probe kernel for new up interfaces */
+int performnlist; /* if 1 check if /kernel has changed */
+int externalinterfaces; /* # of remote and local interfaces */
+int timeval; /* local idea of time */
+int noteremoterequests; /* squawk on requests from non-local nets */
+int r; /* Routing socket to install updates with */
+struct sockaddr_ipx ipx_netmask; /* Used in installing routes */
+
+char packet[MAXRXPACKETSIZE+1];
+
+char **argv0;
+
+int supplier = -1; /* process should supply updates */
+int dosap = 1; /* By default do SAP services. */
+int dobcast = 1; /* A RIP/SAP broadcast is needed. */
+time_t lastbcast; /* Time of last RIP/SAP broadcast */
+
+struct rip *msg = (struct rip *) &packet[sizeof (struct ipx)];
+struct sap_packet *sap_msg =
+ (struct sap_packet *) &packet[sizeof (struct ipx)];
+void hup(), fkexit(), timer();
+void process(int fd, int pkt_type);
+int getsocket(int type, int proto, struct sockaddr_ipx *sipx);
+void getinfo();
+void catchtimer();
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int nfds;
+ fd_set fdvar;
+ time_t ttime;
+ struct itimerval tval;
+
+ argv0 = argv;
+ argv++, argc--;
+ while (argc > 0 && **argv == '-') {
+ if (strcmp(*argv, "-s") == 0) {
+ supplier = 1;
+ argv++, argc--;
+ continue;
+ }
+ if (strcmp(*argv, "-q") == 0) {
+ supplier = 0;
+ argv++, argc--;
+ continue;
+ }
+ if (strcmp(*argv, "-R") == 0) {
+ noteremoterequests++;
+ argv++, argc--;
+ continue;
+ }
+ if (strcmp(*argv, "-S") == 0) {
+ dosap = 0;
+ argv++, argc--;
+ continue;
+ }
+ if (strcmp(*argv, "-t") == 0) {
+ tracepackets++;
+ argv++, argc--;
+ ftrace = stderr;
+ tracing = 1;
+ continue;
+ }
+ if (strcmp(*argv, "-g") == 0) {
+ gateway = 1;
+ argv++, argc--;
+ continue;
+ }
+ if (strcmp(*argv, "-l") == 0) {
+ gateway = -1;
+ argv++, argc--;
+ continue;
+ }
+ if (strcmp(*argv, "-N") == 0) {
+ dognreply = 0;
+ argv++, argc--;
+ continue;
+ }
+ fprintf(stderr,
+ "usage: ipxrouted [ -s ] [ -q ] [ -t ] [ -g ] [ -l ] [ -N ]\n");
+ exit(1);
+ }
+
+
+#ifndef DEBUG
+ if (!tracepackets)
+ daemon(0, 0);
+#endif
+ openlog("IPXrouted", LOG_PID, LOG_DAEMON);
+
+ addr.sipx_family = AF_IPX;
+ addr.sipx_len = sizeof(addr);
+ addr.sipx_port = htons(IPXPORT_RIP);
+ ipx_anynet.s_net[0] = ipx_anynet.s_net[1] = -1;
+ ipx_netmask.sipx_addr.x_net = ipx_anynet;
+ ipx_netmask.sipx_len = 6;
+ ipx_netmask.sipx_family = AF_IPX;
+ r = socket(AF_ROUTE, SOCK_RAW, 0);
+ /* later, get smart about lookingforinterfaces */
+ if (r)
+ shutdown(r, SHUT_RD); /* for now, don't want reponses */
+ else {
+ fprintf(stderr, "IPXrouted: no routing socket\n");
+ exit(1);
+ }
+ ripsock = getsocket(SOCK_DGRAM, 0, &addr);
+ if (ripsock < 0)
+ exit(1);
+
+ if (dosap) {
+ addr.sipx_port = htons(IPXPORT_SAP);
+ sapsock = getsocket(SOCK_DGRAM, 0, &addr);
+ if (sapsock < 0)
+ exit(1);
+ } else
+ sapsock = -1;
+
+ /*
+ * Any extra argument is considered
+ * a tracing log file.
+ */
+ if (argc > 0)
+ traceon(*argv);
+ /*
+ * Collect an initial view of the world by
+ * snooping in the kernel. Then, send a request packet on all
+ * directly connected networks to find out what
+ * everyone else thinks.
+ */
+ rtinit();
+ sapinit();
+ ifinit();
+ if (supplier < 0)
+ supplier = 0;
+ /* request the state of the world */
+ msg->rip_cmd = htons(RIPCMD_REQUEST);
+ msg->rip_nets[0].rip_dst = ipx_anynet;
+ msg->rip_nets[0].rip_metric = htons(HOPCNT_INFINITY);
+ msg->rip_nets[0].rip_ticks = htons(-1);
+ toall(sndmsg, NULL, 0);
+
+ if (dosap) {
+ sap_msg->sap_cmd = htons(SAP_REQ);
+ sap_msg->sap[0].ServType = htons(SAP_WILDCARD);
+ toall(sapsndmsg, NULL, 0);
+ }
+
+ signal(SIGALRM, catchtimer);
+ signal(SIGHUP, hup);
+ signal(SIGINT, hup);
+ signal(SIGEMT, fkexit);
+ signal(SIGINFO, getinfo);
+
+ tval.it_interval.tv_sec = TIMER_RATE;
+ tval.it_interval.tv_usec = 0;
+ tval.it_value.tv_sec = TIMER_RATE;
+ tval.it_value.tv_usec = 0;
+ setitimer(ITIMER_REAL, &tval, NULL);
+
+ nfds = 1 + max(sapsock, ripsock);
+
+ for (;;) {
+ if (dobcast) {
+ dobcast = 0;
+ lastbcast = time(NULL);
+ timer();
+ }
+
+ FD_ZERO(&fdvar);
+ if (dosap) {
+ FD_SET(sapsock, &fdvar);
+ }
+ FD_SET(ripsock, &fdvar);
+
+ if(select(nfds, &fdvar, (fd_set *)NULL, (fd_set *)NULL,
+ (struct timeval *)NULL) < 0) {
+ if(errno == EINTR)
+ continue;
+ perror("during select");
+ exit(1);
+ }
+
+ if(FD_ISSET(ripsock, &fdvar))
+ process(ripsock, RIP_PKT);
+
+ if(dosap && FD_ISSET(sapsock, &fdvar))
+ process(sapsock, SAP_PKT);
+
+ ttime = time(NULL);
+ if (ttime > (lastbcast + TIMER_RATE + (TIMER_RATE * 2 / 3))) {
+ dobcast = 1;
+ syslog(LOG_ERR, "Missed alarm");
+ }
+ }
+}
+
+void
+process(fd, pkt_type)
+ int fd;
+ int pkt_type;
+{
+ struct sockaddr from;
+ int cc, omask;
+ socklen_t fromlen = sizeof (from);
+ struct ipx *ipxdp = (struct ipx *)packet;
+
+ cc = recvfrom(fd, packet, sizeof (packet), 0, &from, &fromlen);
+ if (cc <= 0) {
+ if (cc < 0 && errno != EINTR)
+ syslog(LOG_ERR, "recvfrom: %m");
+ return;
+ }
+ if (tracepackets > 1 && ftrace) {
+ fprintf(ftrace,"rcv %d bytes on %s ",
+ cc, ipxdp_ntoa(&ipxdp->ipx_dna));
+ fprintf(ftrace," from %s\n", ipxdp_ntoa(&ipxdp->ipx_sna));
+ }
+
+ if (noteremoterequests &&
+ !ipx_neteqnn(ipxdp->ipx_sna.x_net, ipx_zeronet) &&
+ !ipx_neteq(ipxdp->ipx_sna, ipxdp->ipx_dna))
+ {
+ syslog(LOG_ERR,
+ "net of interface (%s) != net on ether (%s)!\n",
+ ipxdp_nettoa(ipxdp->ipx_dna.x_net),
+ ipxdp_nettoa(ipxdp->ipx_sna.x_net));
+ }
+
+ /* We get the IPX header in front of the RIF packet*/
+ cc -= sizeof (struct ipx);
+#define mask(s) (1<<((s)-1))
+ omask = sigblock(mask(SIGALRM));
+ switch(pkt_type) {
+ case SAP_PKT: sap_input(&from, cc);
+ break;
+ case RIP_PKT: rip_input(&from, cc);
+ break;
+ }
+ sigsetmask(omask);
+}
+
+int
+getsocket(type, proto, sipx)
+ int type, proto;
+ struct sockaddr_ipx *sipx;
+{
+ int domain = sipx->sipx_family;
+ int retry, s, on = 1;
+
+ retry = 1;
+ while ((s = socket(domain, type, proto)) < 0 && retry) {
+ syslog(LOG_ERR, "socket: %m");
+ sleep(5 * retry);
+ retry <<= 1;
+ }
+ if (retry == 0)
+ return (-1);
+ while (bind(s, (struct sockaddr *)sipx, sizeof (*sipx)) < 0 && retry) {
+ syslog(LOG_ERR, "bind: %m");
+ sleep(5 * retry);
+ retry <<= 1;
+ }
+ if (retry == 0)
+ return (-1);
+ if (domain==AF_IPX) {
+ struct ipx ipxdp;
+ if (setsockopt(s, 0, SO_HEADERS_ON_INPUT, &on, sizeof(on))) {
+ syslog(LOG_ERR, "setsockopt SEE HEADERS: %m");
+ exit(1);
+ }
+ if (ntohs(sipx->sipx_addr.x_port) == IPXPORT_RIP)
+ ipxdp.ipx_pt = IPXPROTO_RI;
+ else if (ntohs(sipx->sipx_addr.x_port) == IPXPORT_SAP)
+#ifdef IPXPROTO_SAP
+ ipxdp.ipx_pt = IPXPROTO_SAP;
+#else
+ ipxdp.ipx_pt = IPXPROTO_PXP;
+#endif
+ else {
+ syslog(LOG_ERR, "port should be either RIP or SAP");
+ exit(1);
+ }
+ if (setsockopt(s, 0, SO_DEFAULT_HEADERS, &ipxdp, sizeof(ipxdp))) {
+ syslog(LOG_ERR, "setsockopt SET HEADER: %m");
+ exit(1);
+ }
+ }
+ if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof (on)) < 0) {
+ syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m");
+ exit(1);
+ }
+ return (s);
+}
+
+/*
+ * Fork and exit on EMT-- for profiling.
+ */
+void
+fkexit()
+{
+ if (fork() == 0)
+ exit(0);
+}
+
+void
+catchtimer()
+{
+ dobcast = 1;
+}
+
+void
+getinfo()
+{
+ FILE *fh;
+
+ fh = fopen("/var/log/ipxrouted.dmp", "a");
+ if(fh == NULL)
+ return;
+
+ dumpriptable(fh);
+ dumpsaptable(fh, sap_head);
+
+ fclose(fh);
+}
+
diff --git a/usr.sbin/IPXrouted/output.c b/usr.sbin/IPXrouted/output.c
new file mode 100644
index 0000000..78922c9
--- /dev/null
+++ b/usr.sbin/IPXrouted/output.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * This file includes significant work done at Cornell University by
+ * Bill Nesheim. That work included by permission.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef lint
+static const char sccsid[] = "@(#)output.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+/*
+ * Routing Table Management Daemon
+ */
+#include <unistd.h>
+#include "defs.h"
+
+/*
+ * Apply the function "f" to all non-passive
+ * interfaces. If the interface supports the
+ * use of broadcasting use it, otherwise address
+ * the output to the known router.
+ */
+void
+toall(f, except, changesonly)
+ void (*f)(struct sockaddr *, int, struct interface *, int);
+ struct rt_entry *except;
+ int changesonly;
+{
+ register struct interface *ifp;
+ register struct sockaddr *dst;
+ register int flags;
+ register struct rt_entry *trt;
+ int onlist;
+ extern struct interface *ifnet;
+
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ if (ifp->int_flags & IFF_PASSIVE)
+ continue;
+
+ /*
+ * Don't send it on interfaces in the except list.
+ */
+ onlist = 0;
+ trt = except;
+ while(trt) {
+ if (ifp == trt->rt_ifp) {
+ onlist = 1;
+ break;
+ }
+ trt = trt->rt_clone;
+ }
+ if (onlist)
+ continue;
+
+ dst = ifp->int_flags & IFF_BROADCAST ? &ifp->int_broadaddr :
+ ifp->int_flags & IFF_POINTOPOINT ? &ifp->int_dstaddr :
+ &ifp->int_addr;
+ flags = ifp->int_flags & IFF_INTERFACE ? MSG_DONTROUTE : 0;
+ (*f)(dst, flags, ifp, changesonly);
+ }
+}
+
+/*
+ * Output a preformed packet.
+ */
+void
+sndmsg(dst, flags, ifp, changesonly)
+ struct sockaddr *dst;
+ int flags;
+ struct interface *ifp;
+ int changesonly;
+{
+
+ (*afswitch[dst->sa_family].af_output)
+ (ripsock, flags, dst, sizeof (struct rip));
+ TRACE_OUTPUT(ifp, dst, sizeof (struct rip));
+}
+
+/*
+ * Supply dst with the contents of the routing tables.
+ * If this won't fit in one packet, chop it up into several.
+ *
+ * This must be done using the split horizon algorithm.
+ * 1. Don't send routing info to the interface from where it was received.
+ * 2. Don't publish an interface to itself.
+ * 3. If a route is received from more than one interface and the cost is
+ * the same, don't publish it on either interface. I am calling this
+ * clones.
+ */
+void
+supply(dst, flags, ifp, changesonly)
+ struct sockaddr *dst;
+ int flags;
+ struct interface *ifp;
+ int changesonly;
+{
+ register struct rt_entry *rt;
+ register struct rt_entry *crt; /* Clone route */
+ register struct rthash *rh;
+ register struct netinfo *nn;
+ register struct netinfo *n = msg->rip_nets;
+ struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) dst;
+ af_output_t *output = afswitch[dst->sa_family].af_output;
+ int size, metric, ticks;
+ union ipx_net net;
+ int delay = 0;
+
+ if (sipx->sipx_port == 0)
+ sipx->sipx_port = htons(IPXPORT_RIP);
+
+ msg->rip_cmd = ntohs(RIPCMD_RESPONSE);
+ for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
+ for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
+ size = (char *)n - (char *)msg;
+ if (size >= ((MAXRIPNETS * sizeof (struct netinfo)) +
+ sizeof (msg->rip_cmd))) {
+ (*output)(ripsock, flags, dst, size);
+ TRACE_OUTPUT(ifp, dst, size);
+ n = msg->rip_nets;
+ delay++;
+ if(delay == 2) {
+ usleep(50000);
+ delay = 0;
+ }
+ }
+
+ if (changesonly && !(rt->rt_state & RTS_CHANGED))
+ continue;
+
+ /*
+ * This should do rule one and two of the split horizon
+ * algorithm.
+ */
+ if (rt->rt_ifp == ifp)
+ continue;
+
+ /*
+ * Rule 3.
+ * Look if we have clones (different routes to the same
+ * place with exactly the same cost).
+ *
+ * We should not publish on any of the clone interfaces.
+ */
+ crt = rt->rt_clone;
+ while (crt) {
+ if (crt->rt_ifp == ifp)
+ goto next;
+ crt = crt->rt_clone;
+ }
+
+ sipx = (struct sockaddr_ipx *)&rt->rt_dst;
+ if ((rt->rt_flags & (RTF_HOST|RTF_GATEWAY)) == RTF_HOST)
+ sipx = (struct sockaddr_ipx *)&rt->rt_router;
+ if (rt->rt_metric == HOPCNT_INFINITY)
+ metric = HOPCNT_INFINITY;
+ else {
+ metric = rt->rt_metric + 1;
+ /*
+ * We don't advertize routes with more than 15 hops.
+ */
+ if (metric >= HOPCNT_INFINITY)
+ continue;
+ }
+ /* XXX One day we should cater for slow interfaces also. */
+ ticks = rt->rt_ticks + 1;
+ net = sipx->sipx_addr.x_net;
+
+ /*
+ * Make sure that we don't put out a two net entries
+ * for a pt to pt link (one for the G route, one for the if)
+ * This is a kludge, and won't work if there are lots of nets.
+ */
+ for (nn = msg->rip_nets; nn < n; nn++) {
+ if (ipx_neteqnn(net, nn->rip_dst)) {
+ if (ticks < ntohs(nn->rip_ticks)) {
+ nn->rip_metric = htons(metric);
+ nn->rip_ticks = htons(ticks);
+ } else if ((ticks == ntohs(nn->rip_ticks)) &&
+ (metric < ntohs(nn->rip_metric))) {
+ nn->rip_metric = htons(metric);
+ nn->rip_ticks = htons(ticks);
+ }
+ goto next;
+ }
+ }
+ n->rip_dst = net;
+ n->rip_metric = htons(metric);
+ n->rip_ticks = htons(ticks);
+ n++;
+ next:;
+ }
+ if (n != msg->rip_nets) {
+ size = (char *)n - (char *)msg;
+ (*output)(ripsock, flags, dst, size);
+ TRACE_OUTPUT(ifp, dst, size);
+ }
+}
diff --git a/usr.sbin/IPXrouted/protocol.h b/usr.sbin/IPXrouted/protocol.h
new file mode 100644
index 0000000..7fae536
--- /dev/null
+++ b/usr.sbin/IPXrouted/protocol.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * This file includes significant work done at Cornell University by
+ * Bill Nesheim. That work included by permission.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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.
+ *
+ * @(#)protocol.h 8.1 (Berkeley) 6/5/93
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * IPX Routing Information Protocol
+ *
+ */
+
+struct netinfo {
+ union ipx_net rip_dst; /* destination net */
+ u_short rip_metric; /* cost of route */
+ u_short rip_ticks; /* cost of route */
+};
+
+struct rip {
+ u_short rip_cmd; /* request/response */
+ struct netinfo rip_nets[1]; /* variable length */
+};
+
+/*
+ * Packet types.
+ */
+#define RIPCMD_REQUEST 1 /* want info */
+#define RIPCMD_RESPONSE 2 /* responding to request */
+
+#define RIPCMD_MAX 3
+#ifdef RIPCMDS
+char *ripcmds[RIPCMD_MAX] =
+ { "#0", "REQUEST", "RESPONSE" };
+#endif
+
+#define HOPCNT_INFINITY 16 /* per IPX */
+#define DSTNETS_ALL 0xffffffff /* per IPX */
+#define MAXRXPACKETSIZE 1500 /* max rx broadcast size */
+#define MAXRIPNETS 50 /* max nets in tx packet */
+
+extern union ipx_net ipx_anynet;
+extern union ipx_net ipx_zeronet;
+
+/*
+ * Timer values used in managing the routing table.
+ * Every update forces an entry's timer to be reset. After
+ * EXPIRE_TIME without updates, the entry is marked invalid,
+ * but held onto until GARBAGE_TIME so that others may
+ * see it "be deleted".
+ */
+#define TIMER_RATE 30 /* alarm clocks every 30 seconds */
+
+#define SUPPLY_INTERVAL 30 /* time to supply tables */
+#define RIP_INTERVAL 60 /* time to supply rip tables */
+
+#define EXPIRE_TIME 180 /* time to mark entry invalid */
+#define GARBAGE_TIME 240 /* time to garbage collect */
diff --git a/usr.sbin/IPXrouted/sap.h b/usr.sbin/IPXrouted/sap.h
new file mode 100644
index 0000000..b4e9dcb
--- /dev/null
+++ b/usr.sbin/IPXrouted/sap.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 John Hay.
+ * 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 John Hay AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL John Hay OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 _SAP_H_
+#define _SAP_H_
+
+#define SAP_REQ 1
+#define SAP_RESP 2
+#define SAP_REQ_NEAR 3
+#define SAP_RESP_NEAR 4
+
+#define SAPCMD_MAX 5
+#ifdef SAPCMDS
+char *sapcmds[SAPCMD_MAX] =
+ { "#0", "REQUEST", "RESPONSE", "REQ NEAREST", "RESP NEAREST"};
+#endif
+
+#define MAXSAPENTRIES 7
+#define SAP_WILDCARD 0xFFFF
+#define SERVNAMELEN 48
+typedef struct sap_info {
+ u_short ServType;
+ char ServName[SERVNAMELEN];
+ struct ipx_addr ipx;
+ u_short hops;
+ }sap_info;
+
+typedef struct sap_packet {
+ u_short sap_cmd;
+ sap_info sap[0]; /* Variable length. */
+ }sap_packet;
+
+typedef struct sap_entry {
+ struct sap_entry *forw;
+ struct sap_entry *back;
+ struct sap_entry *clone;
+ struct interface *ifp;
+ struct sap_info sap;
+ struct sockaddr source;
+ int hash;
+ int state;
+ int timer;
+ }sap_entry;
+
+#define SAPHASHSIZ 256 /* Should be a power of 2 */
+#define SAPHASHMASK (SAPHASHSIZ-1)
+typedef struct sap_hash {
+ struct sap_entry *forw;
+ struct sap_entry *back;
+ }sap_hash;
+
+extern sap_hash sap_head[SAPHASHSIZ];
+
+extern struct sap_packet *sap_msg;
+
+void sapinit(void);
+void sap_input(struct sockaddr *from, int size);
+void sapsndmsg(struct sockaddr *dst, int flags, struct interface *ifp,
+ int changesonly);
+void sap_supply_toall(int changesonly);
+void sap_supply(struct sockaddr *dst,
+ int flags,
+ struct interface *ifp,
+ int ServType,
+ int changesonly);
+
+struct sap_entry *sap_lookup(u_short ServType, char *ServName);
+struct sap_entry *sap_nearestserver(ushort ServType, struct interface *ifp);
+void sap_add(struct sap_info *si, struct sockaddr *from);
+void sap_change(struct sap_entry *sap,
+ struct sap_info *si,
+ struct sockaddr *from);
+void sap_add_clone(struct sap_entry *sap,
+ struct sap_info *clone,
+ struct sockaddr *from);
+void sap_delete(struct sap_entry *sap);
+
+#endif /*_SAP_H_*/
+
diff --git a/usr.sbin/IPXrouted/sap_input.c b/usr.sbin/IPXrouted/sap_input.c
new file mode 100644
index 0000000..59281b2
--- /dev/null
+++ b/usr.sbin/IPXrouted/sap_input.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 John Hay.
+ * 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 John Hay AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL John Hay OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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$
+ */
+
+/*
+ * IPX Routing Table Management Daemon
+ */
+#include "defs.h"
+
+int dognreply = 1;
+
+/*
+ * Process a newly received packet.
+ */
+void
+sap_input(from, size)
+ struct sockaddr *from;
+ int size;
+{
+ int newsize;
+ int sapchanged = 0;
+ struct sap_entry *sap;
+ struct sap_info *n;
+ struct interface *ifp = 0;
+ struct afswitch *afp;
+ struct sockaddr_ipx *ipxp;
+
+ ifp = if_ifwithnet(from);
+ ipxp = (struct sockaddr_ipx *)from;
+ if (ifp == 0) {
+ if(ftrace) {
+ fprintf(ftrace, "Received bogus packet from %s\n",
+ ipxdp_ntoa(&ipxp->sipx_addr));
+ }
+ return;
+ }
+
+ if (ftrace)
+ dumpsappacket(ftrace, "received", from, (char *)sap_msg , size);
+
+ if (from->sa_family >= AF_MAX)
+ return;
+ afp = &afswitch[from->sa_family];
+
+ size -= sizeof (u_short) /* command */;
+ n = sap_msg->sap;
+
+ switch (ntohs(sap_msg->sap_cmd)) {
+
+ case SAP_REQ_NEAR:
+ if (ftrace)
+ fprintf(ftrace, "Received a sap REQ_NEAR packet.\n");
+ if (!dognreply)
+ return;
+ sap = sap_nearestserver(n->ServType, ifp);
+ if (sap == NULL)
+ return;
+ sap_msg->sap_cmd = htons(SAP_RESP_NEAR);
+ *n = sap->sap;
+ n->hops = htons(ntohs(n->hops) + 1);
+ if (ntohs(n->hops) >= HOPCNT_INFINITY)
+ return;
+
+ newsize = sizeof(struct sap_info) + sizeof(struct sap_packet);
+ (*afp->af_output)(sapsock, 0, from, newsize);
+ if (ftrace) {
+ fprintf(ftrace, "sap_nearestserver %X %s returned:\n",
+ ntohs(n->ServType),
+ ifp->int_name);
+ fprintf(ftrace, " service %04X %-20.20s "
+ "addr %s.%04X metric %d\n",
+ ntohs(sap->sap.ServType),
+ sap->sap.ServName,
+ ipxdp_ntoa(&sap->sap.ipx),
+ ntohs(sap->sap.ipx.x_port),
+ ntohs(sap->sap.hops));
+ }
+ return;
+
+ case SAP_REQ:
+ if (ftrace)
+ fprintf(ftrace, "Received a sap REQ packet.\n");
+
+ sap_supply(from, 0, ifp, n->ServType, 0);
+ return;
+
+ case SAP_RESP_NEAR:
+ /* XXX We do nothing here, for the moment.
+ * Maybe we should check if the service is in our table?
+ *
+ */
+ if (ftrace)
+ fprintf(ftrace, "Received a sap RESP_NEAR packet.\n");
+
+ return;
+
+ case SAP_RESP:
+ if (ftrace)
+ fprintf(ftrace, "Received a sap RESP packet.\n");
+
+ (*afp->af_canon)(from);
+
+ for (; size > 0; size -= sizeof (struct sap_info), n++) {
+ if (size < sizeof (struct netinfo))
+ break;
+ /*
+ * The idea here is that if the hop count is more
+ * than INFINITY it is bogus and should be discarded.
+ * If it is equal to INFINITY it is a message to say
+ * that a service went down. If we don't allready
+ * have it in our tables discard it. Otherwise
+ * update our table and set the timer to EXPIRE_TIME
+ * so that it is removed next time we go through the
+ * tables.
+ */
+ if (ntohs(n->hops) > HOPCNT_INFINITY)
+ continue;
+ sap = sap_lookup(n->ServType, n->ServName);
+ if (sap == 0) {
+ if (ntohs(n->hops) == HOPCNT_INFINITY)
+ continue;
+ sap_add(n, from);
+ sapchanged = 1;
+ continue;
+ }
+
+ /*
+ * A clone is a different route to the same service
+ * with exactly the same cost (metric).
+ * They must all be recorded because those interfaces
+ * must be handled in the same way as the first route
+ * to that service. ie When using the split horizon
+ * algorithm we must look at these interfaces also.
+ *
+ * Update if from gateway and different,
+ * from anywhere and less hops or
+ * getting stale and equivalent.
+ */
+ if (((ifp != sap->ifp) ||
+ !equal(&sap->source, from)) &&
+ (n->hops == sap->sap.hops) &&
+ (ntohs(n->hops) != HOPCNT_INFINITY)) {
+ register struct sap_entry *tsap = sap->clone;
+
+ while (tsap) {
+ if ((ifp == tsap->ifp) &&
+ equal(&tsap->source, from)) {
+ tsap->timer = 0;
+ break;
+ }
+ tsap = tsap->clone;
+ }
+ if (tsap == NULL) {
+ sap_add_clone(sap, n, from);
+ }
+ continue;
+ }
+ if ((ifp == sap->ifp) &&
+ equal(&sap->source, from) &&
+ (ntohs(n->hops) == ntohs(sap->sap.hops)))
+ sap->timer = 0;
+ else if (((ifp == sap->ifp) &&
+ equal(&sap->source, from) &&
+ (n->hops != sap->sap.hops)) ||
+ (ntohs(n->hops) < ntohs(sap->sap.hops)) ||
+ (sap->timer > (EXPIRE_TIME*2/3) &&
+ ntohs(sap->sap.hops) == ntohs(n->hops) &&
+ ntohs(n->hops) != HOPCNT_INFINITY)) {
+ sap_change(sap, n, from);
+ sapchanged = 1;
+ }
+ }
+ if (sapchanged) {
+ register struct sap_entry *sap;
+ register struct sap_hash *sh;
+ sap_supply_toall(1);
+
+ for (sh = sap_head; sh < &sap_head[SAPHASHSIZ]; sh++)
+ for (sap = sh->forw;
+ sap != (struct sap_entry *)sh;
+ sap = sap->forw)
+ sap->state &= ~RTS_CHANGED;
+ }
+ return;
+ }
+}
diff --git a/usr.sbin/IPXrouted/sap_output.c b/usr.sbin/IPXrouted/sap_output.c
new file mode 100644
index 0000000..d1f1a28
--- /dev/null
+++ b/usr.sbin/IPXrouted/sap_output.c
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 John Hay.
+ * 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 John Hay AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL John Hay OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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$
+ */
+
+/*
+ * Routing Table Management Daemon
+ */
+#include <unistd.h>
+#include "defs.h"
+
+/*
+ * Apply the function "f" to all non-passive
+ * interfaces. If the interface supports the
+ * use of broadcasting use it, otherwise address
+ * the output to the known router.
+ */
+void
+sap_supply_toall(changesonly)
+ int changesonly;
+{
+ register struct interface *ifp;
+ struct sockaddr dst;
+ register struct sockaddr_ipx *ipx_dst;
+ register int flags;
+ extern struct interface *ifnet;
+
+ ipx_dst = (struct sockaddr_ipx *)&dst;
+
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ if (ifp->int_flags & IFF_PASSIVE)
+ continue;
+
+ dst = ifp->int_flags & IFF_BROADCAST ? ifp->int_broadaddr :
+ ifp->int_flags & IFF_POINTOPOINT ? ifp->int_dstaddr :
+ ifp->int_addr;
+
+ ipx_dst->sipx_addr.x_port = htons(IPXPORT_SAP);
+
+ flags = ifp->int_flags & IFF_INTERFACE ? MSG_DONTROUTE : 0;
+ sap_supply(&dst, flags, ifp, SAP_WILDCARD, changesonly);
+ }
+}
+
+void
+sapsndmsg(dst, flags, ifp, changesonly)
+ struct sockaddr *dst;
+ int flags;
+ struct interface *ifp;
+ int changesonly;
+{
+ struct sockaddr t_dst;
+ struct sockaddr_ipx *ipx_dst;
+
+ t_dst = *dst;
+ ipx_dst = (struct sockaddr_ipx *)&t_dst;
+
+ if (ipx_dst->sipx_addr.x_port == 0)
+ ipx_dst->sipx_addr.x_port = htons(IPXPORT_SAP);
+
+ (*afswitch[dst->sa_family].af_output)
+ (sapsock, flags, &t_dst,
+ sizeof (struct sap_packet) + sizeof(u_short));
+ TRACE_SAP_OUTPUT(ifp, &t_dst,
+ sizeof (struct sap_packet) + sizeof(u_short));
+}
+
+/*
+ * Supply dst with the contents of the SAP tables. If the ServType ==
+ * SAP_WILDCARD (0xFFFF) supply the whole table, otherwise only the
+ * services that are of ServType. If this won't fit in one packet, chop
+ * it up into several.
+ *
+ * This must be done using the split horizon algorithm.
+ * 1. Don't send SAP info to the interface from where it was received.
+ * 2. If a service is received from more than one interface and the cost is
+ * the same, don't publish it on either interface. I am calling this
+ * clones.
+ */
+void
+sap_supply(dst, flags, ifp, ServType, changesonly)
+ struct sockaddr *dst;
+ int flags;
+ struct interface *ifp;
+ int ServType;
+ int changesonly;
+{
+ register struct sap_entry *sap;
+ register struct sap_entry *csap; /* Clone route */
+ register struct sap_hash *sh;
+ register struct sap_info *n = sap_msg->sap;
+ struct sap_hash *base = sap_head;
+ struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) dst;
+ af_output_t *output = afswitch[dst->sa_family].af_output;
+ int size, metric;
+ int delay = 0;
+
+ if (sipx->sipx_port == 0)
+ sipx->sipx_port = htons(IPXPORT_SAP);
+
+ sap_msg->sap_cmd = ntohs(SAP_RESP);
+
+ for (sh = base; sh < &base[SAPHASHSIZ]; sh++)
+ for (sap = sh->forw; sap != (struct sap_entry *)sh; sap = sap->forw) {
+ size = (char *)n - (char *)sap_msg;
+ if (size >= ((MAXSAPENTRIES * sizeof (struct sap_info)) +
+ sizeof (sap_msg->sap_cmd))) {
+ (*output)(sapsock, flags, dst, size);
+ TRACE_SAP_OUTPUT(ifp, dst, size);
+ n = sap_msg->sap;
+ delay++;
+ if(delay == 2) {
+ usleep(50000);
+ delay = 0;
+ }
+ }
+
+ if (changesonly && !(sap->state & RTS_CHANGED))
+ continue;
+
+ /*
+ * Check for the servicetype except if the ServType is
+ * a wildcard (0xFFFF).
+ */
+ if ((ServType != SAP_WILDCARD) &&
+ (ServType != sap->sap.ServType))
+ continue;
+
+ /*
+ * This should do rule one and two of the split horizon
+ * algorithm.
+ */
+ if (sap->ifp == ifp)
+ continue;
+
+ /*
+ * Rule 2.
+ * Look if we have clones (different routes to the same
+ * place with exactly the same cost).
+ *
+ * We should not publish on any of the clone interfaces.
+ */
+ csap = sap->clone;
+ while (csap) {
+ if (csap->ifp == ifp)
+ goto next;
+ csap = csap->clone;
+ }
+
+ /*
+ * Don't advertise services with more than 15 hops. It
+ * will be confused with a service that has gone down.
+ */
+ if (ntohs(sap->sap.hops) == (HOPCNT_INFINITY - 1))
+ continue;
+ metric = min(ntohs(sap->sap.hops) + 1, HOPCNT_INFINITY);
+
+ *n = sap->sap;
+ n->hops = htons(metric);
+ n++;
+next:
+ ;
+ }
+ if (n != sap_msg->sap) {
+ size = (char *)n - (char *)sap_msg;
+ (*output)(sapsock, flags, dst, size);
+ TRACE_SAP_OUTPUT(ifp, dst, size);
+ }
+}
+
diff --git a/usr.sbin/IPXrouted/sap_tables.c b/usr.sbin/IPXrouted/sap_tables.c
new file mode 100644
index 0000000..e1572d1
--- /dev/null
+++ b/usr.sbin/IPXrouted/sap_tables.c
@@ -0,0 +1,321 @@
+/*
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 John Hay.
+ * 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 John Hay AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL John Hay OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 <search.h>
+#include <string.h>
+#include <stdlib.h>
+
+#define FIXLEN(s) { if ((s)->sa_len == 0) (s)->sa_len = sizeof (*(s));}
+
+sap_hash sap_head[SAPHASHSIZ];
+
+void
+sapinit(void)
+{
+ int i;
+
+ for (i=0; i<SAPHASHSIZ; i++)
+ sap_head[i].forw = sap_head[i].back =
+ (struct sap_entry *)&sap_head[i];
+}
+
+/*
+ * This hash use the first 14 letters of the ServName and the ServType
+ * to create a 32 bit hash value.
+ */
+int
+saphash(u_short ServType, char *ServName)
+{
+ int hsh, i;
+ char name[SERVNAMELEN];
+
+ bzero(name, SERVNAMELEN);
+ strncpy(name, ServName, SERVNAMELEN);
+ ServName = name;
+
+ hsh = 0;
+
+#define SMVAL 33
+
+ hsh = hsh * SMVAL + (ServType & 0xff);
+ hsh = hsh * SMVAL + (ServType >> 8);
+
+ for (i=0;i<14;i++) {
+ hsh = hsh * SMVAL + *ServName++;
+ ServName++;
+ }
+
+#undef SMVAL
+
+ return hsh;
+}
+
+/*
+ * Look for an exact match on ServType and ServName. It is
+ * mostly used by the function that process SAP RESPONSE packets.
+ *
+ * A hash is created and used to index into the hash table. Then
+ * that list is walk through searching for a match.
+ *
+ * If no match is found NULL is returned.
+ */
+struct sap_entry *
+sap_lookup(u_short ServType, char *ServName)
+{
+ register struct sap_entry *sap;
+ register struct sap_hash *sh;
+ int hsh;
+
+ hsh = saphash(ServType, ServName);
+ sh = &sap_head[hsh & SAPHASHMASK];
+
+ for(sap = sh->forw; sap != (sap_entry *)sh; sap = sap->forw) {
+ if ((hsh == sap->hash) &&
+ (ServType == sap->sap.ServType) &&
+ (strncmp(ServName, sap->sap.ServName, SERVNAMELEN) == 0)) {
+ return sap;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * This returns the nearest service of the specified type. If no
+ * suitable service is found or if that service is on the interface
+ * where the request came from, NULL is returned.
+ *
+ * When checking interfaces clones must be considered also.
+ *
+ * XXX TODO:
+ * Maybe we can use RIP tables to get the fastest service (ticks).
+ */
+struct sap_entry *
+sap_nearestserver(ushort ServType, struct interface *ifp)
+{
+ register struct sap_entry *sap;
+ struct sap_hash *sh;
+ register struct sap_entry *best = NULL;
+ register int besthops = HOPCNT_INFINITY;
+
+ sh = sap_head;
+
+ for (; sh < &sap_head[SAPHASHSIZ]; sh++)
+ for(sap = sh->forw; sap != (sap_entry *)sh; sap = sap->forw) {
+ if (ServType != sap->sap.ServType)
+ continue;
+
+ if (ntohs(sap->sap.hops) < besthops) {
+ best = sap;
+ besthops = ntohs(best->sap.hops);
+ }
+ }
+ return best;
+}
+
+/*
+ * Add an entry to the SAP table.
+ *
+ * If the malloc fail, the entry will silently be thrown away.
+ */
+void
+sap_add(struct sap_info *si, struct sockaddr *from)
+{
+ register struct sap_entry *nsap;
+ register struct sap_hash *sh;
+
+ if (ntohs(si->hops) == HOPCNT_INFINITY)
+ return;
+
+ FIXLEN(from);
+ nsap = malloc(sizeof(struct sap_entry));
+ if (nsap == NULL)
+ return;
+
+ nsap->sap = *si;
+ nsap->source = *from;
+ nsap->clone = NULL;
+ nsap->ifp = if_ifwithnet(from);
+ nsap->state = RTS_CHANGED;
+ nsap->timer = 0;
+ nsap->hash = saphash(si->ServType, si->ServName);
+
+ sh = &sap_head[nsap->hash & SAPHASHMASK];
+
+ insque(nsap, sh);
+ TRACE_SAP_ACTION("ADD", nsap);
+}
+
+/*
+ * Change an existing SAP entry. If a clone exist for the old one,
+ * check if it is cheaper. If it is change tothe clone, otherwise
+ * delete all the clones.
+ */
+void
+sap_change(struct sap_entry *sap,
+ struct sap_info *si,
+ struct sockaddr *from)
+{
+ struct sap_entry *osap = NULL;
+
+ FIXLEN(from);
+ TRACE_SAP_ACTION("CHANGE FROM", sap);
+ /*
+ * If the hopcount (metric) is HOPCNT_INFINITY (16) it means that
+ * a service has gone down. We should keep it like that for 30
+ * seconds, so that it will get broadcast and then change to a
+ * clone if one exist.
+ */
+ if (sap->clone && (ntohs(si->hops) != HOPCNT_INFINITY)) {
+ /*
+ * There are three possibilities:
+ * 1. The new path is cheaper than the old one.
+ * Free all the clones.
+ *
+ * 2. The new path is the same cost as the old ones.
+ * If it is on the list of clones remove it
+ * from the clone list and free it.
+ *
+ * 3. The new path is more expensive than the old one.
+ * Use the values of the first clone and take it
+ * out of the list, to be freed at the end.
+ */
+ osap = sap->clone;
+ if (ntohs(osap->sap.hops) > ntohs(si->hops)) {
+ struct sap_entry *nsap;
+
+ while (osap) {
+ nsap = osap->clone;
+ TRACE_SAP_ACTION("DELETE", osap);
+ free(osap);
+ osap = nsap;
+ }
+ sap->clone = NULL;
+ } else if (ntohs(osap->sap.hops) == ntohs(si->hops)) {
+ struct sap_entry *psap;
+
+ psap = sap;
+ while (osap) {
+ if (equal(&osap->source, from)) {
+ psap->clone = osap->clone;
+ TRACE_SAP_ACTION("DELETE", osap);
+ free(osap);
+ osap = psap->clone;
+ } else {
+ psap = osap;
+ osap = osap->clone;
+ }
+ }
+ } else {
+ from = &osap->source;
+ si = &osap->sap;
+ sap->clone = osap->clone;
+ }
+ }
+ sap->sap = *si;
+ sap->source = *from;
+ sap->ifp = if_ifwithnet(from);
+ sap->state = RTS_CHANGED;
+ if (ntohs(si->hops) == HOPCNT_INFINITY)
+ sap->timer = EXPIRE_TIME;
+ else
+ sap->timer = 0;
+
+ if (osap) {
+ TRACE_SAP_ACTION("DELETE", osap);
+ free(osap);
+ }
+ TRACE_SAP_ACTION("CHANGE TO", sap);
+}
+
+/*
+ * Add a clone to the specified SAP entry. A clone is a different
+ * route to the same service. We must know about them when we use
+ * the split horizon algorithm.
+ *
+ * If the malloc fail, the entry will silently be thrown away.
+ */
+void
+sap_add_clone(struct sap_entry *sap,
+ struct sap_info *clone,
+ struct sockaddr *from)
+{
+ register struct sap_entry *nsap;
+ register struct sap_entry *csap;
+
+ if (ntohs(clone->hops) == HOPCNT_INFINITY)
+ return;
+
+ FIXLEN(from);
+ nsap = malloc(sizeof(struct sap_entry));
+ if (nsap == NULL)
+ return;
+
+ if (ftrace)
+ fprintf(ftrace, "CLONE ADD %4.4X %s.\n",
+ ntohs(clone->ServType),
+ clone->ServName);
+
+ nsap->sap = *clone;
+ nsap->source = *from;
+ nsap->clone = NULL;
+ nsap->ifp = if_ifwithnet(from);
+ nsap->state = RTS_CHANGED;
+ nsap->timer = 0;
+ nsap->hash = saphash(clone->ServType, clone->ServName);
+
+ csap = sap;
+ while (csap->clone)
+ csap = csap->clone;
+ csap->clone = nsap;
+ TRACE_SAP_ACTION("ADD CLONE", nsap);
+}
+
+/*
+ * Remove a SAP entry from the table and free the memory
+ * used by it.
+ *
+ * If the service have clone, do a sap_change to it and free
+ * the clone.
+ */
+void
+sap_delete(struct sap_entry *sap)
+{
+ if (sap->clone) {
+ sap_change(sap, &sap->clone->sap, &sap->clone->source);
+ return;
+ }
+ remque(sap);
+ TRACE_SAP_ACTION("DELETE", sap);
+ free(sap);
+}
diff --git a/usr.sbin/IPXrouted/startup.c b/usr.sbin/IPXrouted/startup.c
new file mode 100644
index 0000000..2b9ccfa
--- /dev/null
+++ b/usr.sbin/IPXrouted/startup.c
@@ -0,0 +1,278 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * This file includes significant work done at Cornell University by
+ * Bill Nesheim. That work included by permission.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef lint
+static const char sccsid[] = "@(#)startup.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+/*
+ * Routing Table Management Daemon
+ */
+#include "defs.h"
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+
+#include <errno.h>
+#include <nlist.h>
+#include <search.h>
+#include <stdlib.h>
+
+struct interface *ifnet;
+int lookforinterfaces = 1;
+int performnlist = 1;
+int gateway = 0;
+int externalinterfaces = 0; /* # of remote and local interfaces */
+
+void
+quit(s)
+ char *s;
+{
+ int sverrno = errno;
+
+ (void) fprintf(stderr, "IPXroute: ");
+ if (s)
+ (void) fprintf(stderr, "%s: ", s);
+ (void) fprintf(stderr, "%s\n", strerror(sverrno));
+ exit(1);
+ /* NOTREACHED */
+}
+
+struct rt_addrinfo info;
+/* XXX Sleazy use of local variables throughout file, warning!!!! */
+#define netmask info.rti_info[RTAX_NETMASK]
+#define ifaaddr info.rti_info[RTAX_IFA]
+#define brdaddr info.rti_info[RTAX_BRD]
+
+void
+rt_xaddrs(cp, cplim, rtinfo)
+ register caddr_t cp, cplim;
+ register struct rt_addrinfo *rtinfo;
+{
+ register struct sockaddr *sa;
+ register int i;
+
+ bzero(rtinfo->rti_info, 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);
+ }
+}
+
+/*
+ * Find the network interfaces which have configured themselves.
+ * If the interface is present but not yet up (for example an
+ * ARPANET IMP), set the lookforinterfaces flag so we'll
+ * come back later and look again.
+ */
+void
+ifinit(void)
+{
+ struct interface ifs, *ifp;
+ size_t needed;
+ int mib[6], no_ipxaddr = 0, flags = 0;
+ char *buf, *cplim, *cp;
+ register struct if_msghdr *ifm;
+ register struct ifa_msghdr *ifam;
+ struct sockaddr_dl *sdl = 0;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_IPX;
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0;
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
+ quit("route-sysctl-estimate");
+ if ((buf = malloc(needed)) == NULL)
+ quit("malloc");
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
+ lookforinterfaces = 0;
+ cplim = buf + needed;
+ for (cp = buf; cp < cplim; cp += ifm->ifm_msglen) {
+ ifm = (struct if_msghdr *)cp;
+ if (ifm->ifm_type == RTM_IFINFO) {
+ bzero(&ifs, sizeof(ifs));
+ ifs.int_flags = flags = ifm->ifm_flags | IFF_INTERFACE;
+ if ((flags & IFF_UP) == 0 || no_ipxaddr)
+ lookforinterfaces = 1;
+ sdl = (struct sockaddr_dl *) (ifm + 1);
+ sdl->sdl_data[sdl->sdl_nlen] = 0;
+ no_ipxaddr = 1;
+ continue;
+ }
+ if (ifm->ifm_type != RTM_NEWADDR)
+ quit("ifinit: out of sync");
+ if ((flags & IFF_UP) == 0)
+ continue;
+ ifam = (struct ifa_msghdr *)ifm;
+ info.rti_addrs = ifam->ifam_addrs;
+ rt_xaddrs((char *)(ifam + 1), cp + ifam->ifam_msglen, &info);
+ if (ifaaddr == 0) {
+ syslog(LOG_ERR, "%s: (get addr)", sdl->sdl_data);
+ continue;
+ }
+ ifs.int_addr = *ifaaddr;
+ if (ifs.int_addr.sa_family != AF_IPX)
+ continue;
+ no_ipxaddr = 0;
+ if (ifs.int_flags & IFF_POINTOPOINT) {
+ if (brdaddr == 0) {
+ syslog(LOG_ERR, "%s: (get dstaddr)",
+ sdl->sdl_data);
+ continue;
+ }
+ if (brdaddr->sa_family == AF_UNSPEC) {
+ lookforinterfaces = 1;
+ continue;
+ }
+ ifs.int_dstaddr = *brdaddr;
+ }
+ if (ifs.int_flags & IFF_BROADCAST) {
+ if (brdaddr == 0) {
+ syslog(LOG_ERR, "%s: (get broadaddr)",
+ sdl->sdl_data);
+ continue;
+ }
+ ifs.int_dstaddr = *brdaddr;
+ }
+ if (ifs.int_flags & IFF_LOOPBACK) {
+ ifs.int_dstaddr = ifs.int_addr;
+ }
+ /*
+ * already known to us?
+ * what makes a POINTOPOINT if unique is its dst addr,
+ * NOT its source address
+ */
+ if ( ((ifs.int_flags & IFF_POINTOPOINT) &&
+ if_ifwithdstaddr(&ifs.int_dstaddr)) ||
+ ( ((ifs.int_flags & IFF_POINTOPOINT) == 0) &&
+ if_ifwithaddr(&ifs.int_addr)))
+ continue;
+ ifp = (struct interface *)
+ malloc(sdl->sdl_nlen + 1 + sizeof(ifs));
+ if (ifp == 0) {
+ syslog(LOG_ERR, "IPXrouted: out of memory\n");
+ lookforinterfaces = 1;
+ break;
+ }
+ *ifp = ifs;
+ /*
+ * Count the # of directly connected networks
+ * and point to point links which aren't looped
+ * back to ourself. This is used below to
+ * decide if we should be a routing ``supplier''.
+ */
+ if ((ifs.int_flags & IFF_POINTOPOINT) == 0 ||
+ if_ifwithaddr(&ifs.int_dstaddr) == 0)
+ externalinterfaces++;
+ /*
+ * If we have a point-to-point link, we want to act
+ * as a supplier even if it's our only interface,
+ * as that's the only way our peer on the other end
+ * can tell that the link is up.
+ */
+ if ((ifs.int_flags & IFF_POINTOPOINT) && supplier < 0)
+ supplier = 1;
+ ifp->int_name = (char *)(ifp + 1);
+ strcpy(ifp->int_name, sdl->sdl_data);
+
+ ifp->int_metric = ifam->ifam_metric;
+ ifp->int_next = ifnet;
+ ifnet = ifp;
+ traceinit(ifp);
+ addrouteforif(ifp);
+ }
+ if (externalinterfaces > 1 && supplier < 0)
+ supplier = 1;
+ free(buf);
+}
+
+void
+addrouteforif(ifp)
+ struct interface *ifp;
+{
+ struct sockaddr_ipx net;
+ struct sockaddr *dst;
+ struct rt_entry *rt;
+
+ if (ifp->int_flags & IFF_POINTOPOINT) {
+ int (*match)();
+ register struct interface *ifp2 = ifnet;
+
+ dst = &ifp->int_dstaddr;
+
+ /* Search for interfaces with the same net */
+ ifp->int_sq.n = ifp->int_sq.p = &(ifp->int_sq);
+ match = afswitch[dst->sa_family].af_netmatch;
+ if (match)
+ for (ifp2 = ifnet; ifp2; ifp2 =ifp2->int_next) {
+ if ((ifp->int_flags & IFF_POINTOPOINT) == 0)
+ continue;
+ if ((*match)(&ifp2->int_dstaddr,&ifp->int_dstaddr)) {
+ insque(&ifp2->int_sq,&ifp->int_sq);
+ break;
+ }
+ }
+ } else {
+ bzero(&net, sizeof(net));
+ net.sipx_family = AF_IPX;
+ net.sipx_len = sizeof (net);
+ net.sipx_addr.x_net = satoipx_addr(ifp->int_broadaddr).x_net;
+ dst = (struct sockaddr *)&net;
+ }
+ rt = rtlookup(dst);
+ if (rt)
+ rtdelete(rt);
+ if (tracing)
+ fprintf(stderr, "Adding route to interface %s\n", ifp->int_name);
+ if (ifp->int_transitions++ > 0)
+ syslog(LOG_ERR, "re-installing interface %s", ifp->int_name);
+ rtadd(dst, &ifp->int_addr, ifp->int_metric, 0,
+ ifp->int_flags & (IFF_INTERFACE|IFF_PASSIVE|IFF_REMOTE));
+}
+
diff --git a/usr.sbin/IPXrouted/table.h b/usr.sbin/IPXrouted/table.h
new file mode 100644
index 0000000..3f9693f
--- /dev/null
+++ b/usr.sbin/IPXrouted/table.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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.
+ *
+ * @(#)table.h 5.1 (Berkeley) 6/4/85 (routed/table.h)
+ *
+ * @(#)table.h 8.1 (Berkeley) 6/5/93
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Routing table management daemon.
+ */
+
+/*
+ * Routing table structure; differs a bit from kernel tables.
+ *
+ * Note: the union below must agree in the first 4 members
+ * so the ioctl's will work.
+ */
+struct rthash {
+ struct rt_entry *rt_forw;
+ struct rt_entry *rt_back;
+};
+
+#ifdef RTM_ADD
+#define rtentry ortentry
+#endif
+
+struct rt_entry {
+ struct rt_entry *rt_forw;
+ struct rt_entry *rt_back;
+ union {
+ struct rtentry rtu_rt;
+ struct rtuentry {
+ u_long rtu_hash;
+ struct sockaddr rtu_dst;
+ struct sockaddr rtu_router;
+ short rtu_rtflags; /* used by old rtioctl */
+ short rtu_wasted; /* XXX routed does it this way. */
+ int rtu_flags;
+ int rtu_state;
+ int rtu_timer;
+ int rtu_metric;
+ int rtu_ticks;
+ struct interface *rtu_ifp;
+ } rtu_entry;
+ } rt_rtu;
+ struct rt_entry *rt_clone;
+};
+
+#define rt_rt rt_rtu.rtu_entry /* pass to ioctl */
+#define rt_hash rt_rtu.rtu_entry.rtu_hash /* for net or host */
+#define rt_dst rt_rtu.rtu_entry.rtu_dst /* match value */
+#define rt_router rt_rtu.rtu_entry.rtu_router /* who to forward to */
+#define rt_flags rt_rtu.rtu_entry.rtu_flags /* kernel flags */
+#define rt_timer rt_rtu.rtu_entry.rtu_timer /* for invalidation */
+#define rt_state rt_rtu.rtu_entry.rtu_state /* see below */
+#define rt_metric rt_rtu.rtu_entry.rtu_metric /* cost of route */
+#define rt_ticks rt_rtu.rtu_entry.rtu_ticks /* time of route */
+#define rt_ifp rt_rtu.rtu_entry.rtu_ifp /* interface to take */
+
+#define ROUTEHASHSIZ 128 /* must be a power of 2 */
+#define ROUTEHASHMASK (ROUTEHASHSIZ - 1)
+
+/*
+ * "State" of routing table entry.
+ */
+#define RTS_CHANGED 0x1 /* route has been altered recently */
+#define RTS_PASSIVE IFF_PASSIVE /* don't time out route */
+#define RTS_INTERFACE IFF_INTERFACE /* route is for network interface */
+#define RTS_REMOTE IFF_REMOTE /* route is for ``remote'' entity */
+
+extern struct rthash nethash[ROUTEHASHSIZ];
+struct rt_entry *rtlookup(struct sockaddr *);
+struct rt_entry *rtfind(struct sockaddr *);
+void rtadd(struct sockaddr *, struct sockaddr *, short, short, int);
+void rtadd_clone(struct rt_entry *, struct sockaddr *, struct sockaddr *,
+ short, short, int);
+void rtchange(struct rt_entry *, struct sockaddr *, short, short);
+void rtdelete(struct rt_entry *);
+int rtioctl(int, struct rtuentry *);
+void rtinit(void);
+
diff --git a/usr.sbin/IPXrouted/tables.c b/usr.sbin/IPXrouted/tables.c
new file mode 100644
index 0000000..fe816b2
--- /dev/null
+++ b/usr.sbin/IPXrouted/tables.c
@@ -0,0 +1,423 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef lint
+static const char sccsid[] = "@(#)tables.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+/*
+ * Routing Table Management Daemon
+ */
+#include "defs.h"
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <search.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifndef DEBUG
+#define DEBUG 0
+#endif
+
+#define FIXLEN(s) { if ((s)->sa_len == 0) (s)->sa_len = sizeof (*(s));}
+
+int install = !DEBUG; /* if 1 call kernel */
+int delete = 1;
+
+struct rthash nethash[ROUTEHASHSIZ];
+
+/*
+ * Lookup dst in the tables for an exact match.
+ */
+struct rt_entry *
+rtlookup(struct sockaddr *dst)
+{
+ register struct rt_entry *rt;
+ register struct rthash *rh;
+ register u_int hash;
+ struct afhash h;
+
+ if (dst->sa_family >= AF_MAX)
+ return (0);
+ (*afswitch[dst->sa_family].af_hash)(dst, &h);
+ hash = h.afh_nethash;
+ rh = &nethash[hash & ROUTEHASHMASK];
+ for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
+ if (rt->rt_hash != hash)
+ continue;
+ if (equal(&rt->rt_dst, dst))
+ return (rt);
+ }
+ return (0);
+}
+
+/*
+ * Find a route to dst as the kernel would.
+ */
+struct rt_entry *
+rtfind(struct sockaddr *dst)
+{
+ register struct rt_entry *rt;
+ register struct rthash *rh;
+ register u_int hash;
+ struct afhash h;
+ int af = dst->sa_family;
+ int (*match)() = 0;
+
+ if (af >= AF_MAX)
+ return (0);
+ (*afswitch[af].af_hash)(dst, &h);
+
+ hash = h.afh_nethash;
+ rh = &nethash[hash & ROUTEHASHMASK];
+ match = afswitch[af].af_netmatch;
+ for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
+ if (rt->rt_hash != hash)
+ continue;
+ if (rt->rt_dst.sa_family == af &&
+ (*match)(&rt->rt_dst, dst))
+ return (rt);
+ }
+ return (0);
+}
+
+void
+rtadd(struct sockaddr *dst, struct sockaddr *gate, short metric,
+ short ticks, int state)
+{
+ struct afhash h;
+ register struct rt_entry *rt;
+ struct rthash *rh;
+ int af = dst->sa_family, flags;
+ u_int hash;
+
+ FIXLEN(dst);
+ FIXLEN(gate);
+ if (af >= AF_MAX)
+ return;
+ (*afswitch[af].af_hash)(dst, &h);
+ flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0;
+ hash = h.afh_nethash;
+ rh = &nethash[hash & ROUTEHASHMASK];
+ rt = (struct rt_entry *)malloc(sizeof (*rt));
+ if (rt == 0)
+ return;
+ rt->rt_hash = hash;
+ rt->rt_dst = *dst;
+ rt->rt_router = *gate;
+ rt->rt_metric = metric;
+ rt->rt_ticks = ticks;
+ rt->rt_timer = 0;
+ rt->rt_flags = RTF_UP | flags;
+ rt->rt_state = state | RTS_CHANGED;
+ rt->rt_ifp = if_ifwithnet(&rt->rt_router);
+ rt->rt_clone = NULL;
+ if (metric)
+ rt->rt_flags |= RTF_GATEWAY;
+ insque(rt, rh);
+ TRACE_ACTION("ADD", rt);
+ /*
+ * If the ioctl fails because the gateway is unreachable
+ * from this host, discard the entry. This should only
+ * occur because of an incorrect entry in /etc/gateways.
+ */
+ if (install && rtioctl(ADD, &rt->rt_rt) < 0) {
+ if (errno != EEXIST)
+ perror("SIOCADDRT");
+ if (errno == ENETUNREACH) {
+ TRACE_ACTION("DELETE", rt);
+ remque(rt);
+ free((char *)rt);
+ }
+ }
+}
+
+void
+rtadd_clone(struct rt_entry *ort, struct sockaddr *dst,
+ struct sockaddr *gate, short metric, short ticks, int state)
+{
+ struct afhash h;
+ register struct rt_entry *rt;
+ struct rthash *rh;
+ int af = dst->sa_family, flags;
+ u_int hash;
+
+ FIXLEN(dst);
+ FIXLEN(gate);
+ if (af >= AF_MAX)
+ return;
+ (*afswitch[af].af_hash)(dst, &h);
+ flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0;
+ hash = h.afh_nethash;
+ rh = &nethash[hash & ROUTEHASHMASK];
+ rt = (struct rt_entry *)malloc(sizeof (*rt));
+ if (rt == 0)
+ return;
+ rt->rt_hash = hash;
+ rt->rt_dst = *dst;
+ rt->rt_router = *gate;
+ rt->rt_metric = metric;
+ rt->rt_ticks = ticks;
+ rt->rt_timer = 0;
+ rt->rt_flags = RTF_UP | flags;
+ rt->rt_state = state | RTS_CHANGED;
+ rt->rt_ifp = if_ifwithnet(&rt->rt_router);
+ rt->rt_clone = NULL;
+ rt->rt_forw = NULL;
+ rt->rt_back = NULL;
+ if (metric)
+ rt->rt_flags |= RTF_GATEWAY;
+
+ while(ort->rt_clone != NULL)
+ ort = ort->rt_clone;
+ ort->rt_clone = rt;
+ TRACE_ACTION("ADD_CLONE", rt);
+}
+
+void
+rtchange(struct rt_entry *rt, struct sockaddr *gate, short metric,
+ short ticks)
+{
+ int doioctl = 0, metricchanged = 0;
+ struct rtuentry oldroute;
+
+ FIXLEN(gate);
+ /*
+ * Handling of clones.
+ * When the route changed and it had clones, handle it special.
+ * 1. If the new route is cheaper than the clone(s), free the clones.
+ * 2. If the new route is the same cost, it may be one of the clones,
+ * search for it and free it.
+ * 3. If the new route is more expensive than the clone(s), use the
+ * values of the clone(s).
+ */
+ if (rt->rt_clone) {
+ if ((ticks < rt->rt_clone->rt_ticks) ||
+ ((ticks == rt->rt_clone->rt_ticks) &&
+ (metric < rt->rt_clone->rt_metric))) {
+ /*
+ * Free all clones.
+ */
+ struct rt_entry *trt, *nrt;
+
+ trt = rt->rt_clone;
+ rt->rt_clone = NULL;
+ while(trt) {
+ nrt = trt->rt_clone;
+ free((char *)trt);
+ trt = nrt;
+ }
+ } else if ((ticks == rt->rt_clone->rt_ticks) &&
+ (metric == rt->rt_clone->rt_metric)) {
+ struct rt_entry *prt, *trt;
+
+ prt = rt;
+ trt = rt->rt_clone;
+
+ while(trt) {
+ if (equal(&trt->rt_router, gate)) {
+ prt->rt_clone = trt->rt_clone;
+ free(trt);
+ trt = prt->rt_clone;
+ } else {
+ prt = trt;
+ trt = trt->rt_clone;
+ }
+ }
+ } else {
+ /*
+ * Use the values of the first clone.
+ * Delete the corresponding clone.
+ */
+ struct rt_entry *trt;
+
+ trt = rt->rt_clone;
+ rt->rt_clone = rt->rt_clone->rt_clone;
+ metric = trt->rt_metric;
+ ticks = trt->rt_ticks;
+ *gate = trt->rt_router;
+ free((char *)trt);
+ }
+ }
+
+ if (!equal(&rt->rt_router, gate))
+ doioctl++;
+ if ((metric != rt->rt_metric) || (ticks != rt->rt_ticks))
+ metricchanged++;
+ if (doioctl || metricchanged) {
+ TRACE_ACTION("CHANGE FROM", rt);
+ if (doioctl) {
+ oldroute = rt->rt_rt;
+ rt->rt_router = *gate;
+ }
+ rt->rt_metric = metric;
+ rt->rt_ticks = ticks;
+ if ((rt->rt_state & RTS_INTERFACE) && metric) {
+ rt->rt_state &= ~RTS_INTERFACE;
+ if(rt->rt_ifp)
+ syslog(LOG_ERR,
+ "changing route from interface %s (timed out)",
+ rt->rt_ifp->int_name);
+ else
+ syslog(LOG_ERR,
+ "changing route from interface ??? (timed out)");
+ }
+ if (metric)
+ rt->rt_flags |= RTF_GATEWAY;
+ else
+ rt->rt_flags &= ~RTF_GATEWAY;
+ rt->rt_ifp = if_ifwithnet(&rt->rt_router);
+ rt->rt_state |= RTS_CHANGED;
+ TRACE_ACTION("CHANGE TO", rt);
+ }
+ if (doioctl && install) {
+#ifndef RTM_ADD
+ if (rtioctl(ADD, &rt->rt_rt) < 0)
+ syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m",
+ ipx_ntoa(&((struct sockaddr_ipx *)&rt->rt_dst)->sipx_addr),
+ ipx_ntoa(&((struct sockaddr_ipx *)&rt->rt_router)->sipx_addr));
+ if (delete && rtioctl(DELETE, &oldroute) < 0)
+ perror("rtioctl DELETE");
+#else
+ if (delete == 0) {
+ if (rtioctl(ADD, &rt->rt_rt) >= 0)
+ return;
+ } else {
+ if (rtioctl(CHANGE, &rt->rt_rt) >= 0)
+ return;
+ }
+ syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m",
+ ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_dst)->sipx_addr),
+ ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_router)->sipx_addr));
+#endif
+ }
+}
+
+void
+rtdelete(struct rt_entry *rt)
+{
+
+ struct sockaddr *sa = &(rt->rt_router);
+ FIXLEN(sa);
+ sa = &(rt->rt_dst);
+ FIXLEN(sa);
+ if (rt->rt_clone) {
+ /*
+ * If there is a clone we just do a rt_change to it.
+ */
+ struct rt_entry *trt = rt->rt_clone;
+ rtchange(rt, &trt->rt_router, trt->rt_metric, trt->rt_ticks);
+ return;
+ }
+ if (rt->rt_state & RTS_INTERFACE) {
+ if (rt->rt_ifp)
+ syslog(LOG_ERR,
+ "deleting route to interface %s (timed out)",
+ rt->rt_ifp->int_name);
+ else
+ syslog(LOG_ERR,
+ "deleting route to interface ??? (timed out)");
+ }
+ TRACE_ACTION("DELETE", rt);
+ if (install && rtioctl(DELETE, &rt->rt_rt) < 0)
+ perror("rtioctl DELETE");
+ remque(rt);
+ free((char *)rt);
+}
+
+void
+rtinit(void)
+{
+ register struct rthash *rh;
+
+ for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
+ rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
+}
+int seqno;
+
+int
+rtioctl(int action, struct rtuentry *ort)
+{
+#ifndef RTM_ADD
+ if (install == 0)
+ return (errno = 0);
+
+ ort->rtu_rtflags = ort->rtu_flags;
+
+ switch (action) {
+
+ case ADD:
+ return (ioctl(s, SIOCADDRT, (char *)ort));
+
+ case DELETE:
+ return (ioctl(s, SIOCDELRT, (char *)ort));
+
+ default:
+ return (-1);
+ }
+#else /* RTM_ADD */
+ struct {
+ struct rt_msghdr w_rtm;
+ struct sockaddr w_dst;
+ struct sockaddr w_gate;
+ struct sockaddr_ipx w_netmask;
+ } w;
+#define rtm w.w_rtm
+
+ bzero((char *)&w, sizeof(w));
+ rtm.rtm_msglen = sizeof(w);
+ rtm.rtm_version = RTM_VERSION;
+ rtm.rtm_type = (action == ADD ? RTM_ADD :
+ (action == DELETE ? RTM_DELETE : RTM_CHANGE));
+ rtm.rtm_flags = ort->rtu_flags;
+ rtm.rtm_seq = ++seqno;
+ rtm.rtm_addrs = RTA_DST|RTA_GATEWAY;
+ bcopy((char *)&ort->rtu_dst, (char *)&w.w_dst, sizeof(w.w_dst));
+ bcopy((char *)&ort->rtu_router, (char *)&w.w_gate, sizeof(w.w_gate));
+ w.w_gate.sa_family = w.w_dst.sa_family = AF_IPX;
+ w.w_gate.sa_len = w.w_dst.sa_len = sizeof(w.w_dst);
+ if (rtm.rtm_flags & RTF_HOST) {
+ rtm.rtm_msglen -= sizeof(w.w_netmask);
+ } else {
+ rtm.rtm_addrs |= RTA_NETMASK;
+ w.w_netmask = ipx_netmask;
+ rtm.rtm_msglen -= sizeof(w.w_netmask) - ipx_netmask.sipx_len;
+ }
+ errno = 0;
+ return write(r, (char *)&w, rtm.rtm_msglen);
+#endif /* RTM_ADD */
+}
diff --git a/usr.sbin/IPXrouted/timer.c b/usr.sbin/IPXrouted/timer.c
new file mode 100644
index 0000000..2159730
--- /dev/null
+++ b/usr.sbin/IPXrouted/timer.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * This file includes significant work done at Cornell University by
+ * Bill Nesheim. That work included by permission.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef lint
+static const char sccsid[] = "@(#)timer.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+/*
+ * Routing Table Management Daemon
+ */
+#include "defs.h"
+#include <unistd.h>
+#include <stdlib.h>
+
+int timeval = -TIMER_RATE;
+
+/*
+ * Timer routine. Performs routing information supply
+ * duties and manages timers on routing and SAP table entries.
+ */
+void
+timer()
+{
+ register struct rthash *rh;
+ register struct rt_entry *rt;
+ register struct sap_hash *sh;
+ register struct sap_entry *sap;
+ struct sap_hash *sap_base = sap_head;
+ int timetobroadcast, ripbroadcast, sapbroadcast;
+
+ timeval += TIMER_RATE;
+ if (lookforinterfaces && (timeval % CHECK_INTERVAL) == 0)
+ ifinit();
+ timetobroadcast = supplier && (timeval % SUPPLY_INTERVAL) == 0;
+ ripbroadcast = supplier && timetobroadcast &&
+ (timeval % RIP_INTERVAL) == 0;
+ sapbroadcast = timetobroadcast && dosap && !ripbroadcast;
+
+ for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++) {
+ rt = rh->rt_forw;
+ for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
+ if (rt->rt_clone) {
+ struct rt_entry *trt, *prt;
+ /*
+ * If a clone expire free it and mark the
+ * main route RTS_CHANGED.
+ */
+ prt = rt;
+ trt = rt->rt_clone;
+ while (trt) {
+ trt->rt_timer += TIMER_RATE;
+ if (trt->rt_timer >= EXPIRE_TIME) {
+ prt->rt_clone = trt->rt_clone;
+ free((char *)trt);
+ trt = prt->rt_clone;
+ rt->rt_state |= RTS_CHANGED;
+ } else {
+ prt = trt;
+ trt = prt->rt_clone;
+ }
+ }
+ }
+ /*
+ * We don't advance time on a routing entry for
+ * a passive gateway or that for our only interface.
+ * The latter is excused because we don't act as
+ * a routing information supplier and hence would
+ * time it out. This is fair as if it's down
+ * we're cut off from the world anyway and it's
+ * not likely we'll grow any new hardware in
+ * the mean time.
+ */
+ if (!(rt->rt_state & RTS_PASSIVE) &&
+ !(rt->rt_state & RTS_INTERFACE))
+ rt->rt_timer += TIMER_RATE;
+ if (rt->rt_timer >= EXPIRE_TIME) {
+ rt->rt_metric = HOPCNT_INFINITY;
+ rt->rt_state |= RTS_CHANGED;
+ }
+ if (rt->rt_timer >= GARBAGE_TIME) {
+ rt = rt->rt_back;
+ /* Perhaps we should send a REQUEST for this route? */
+ rtdelete(rt->rt_forw);
+ continue;
+ }
+ if (rt->rt_state & RTS_CHANGED) {
+ rt->rt_state &= ~RTS_CHANGED;
+ /* don't send extraneous packets */
+ if (!supplier || ripbroadcast)
+ continue;
+ if ((rt->rt_metric + 1) == HOPCNT_INFINITY)
+ continue;
+ msg->rip_cmd = htons(RIPCMD_RESPONSE);
+ msg->rip_nets[0].rip_dst =
+ (satoipx_addr(rt->rt_dst)).x_net;
+ msg->rip_nets[0].rip_metric =
+ htons(min(rt->rt_metric+1, HOPCNT_INFINITY));
+ msg->rip_nets[0].rip_ticks =
+ htons(rt->rt_ticks + 1);
+ toall(sndmsg, rt, 0);
+ }
+ }
+ }
+ if (ripbroadcast)
+ toall(supply, NULL, 0);
+
+ /*
+ * Now do the SAP stuff.
+ */
+ for (sh = sap_base; sh < &sap_base[SAPHASHSIZ]; sh++) {
+ sap = sh->forw;
+ for (; sap != (struct sap_entry *)sh; sap = sap->forw) {
+ if (sap->clone) {
+ struct sap_entry *tsap, *psap;
+ /*
+ * If a clone expire free it and mark the
+ * main sap entry RTS_CHANGED.
+ */
+ psap = sap;
+ tsap = sap->clone;
+ while (tsap) {
+ tsap->timer += TIMER_RATE;
+ if (tsap->timer >= EXPIRE_TIME) {
+ psap->clone = tsap->clone;
+ free((char *)tsap);
+ tsap = psap->clone;
+ sap->state |= RTS_CHANGED;
+ } else {
+ psap = tsap;
+ tsap = psap->clone;
+ }
+ }
+ }
+ sap->timer += TIMER_RATE;
+ if (sap->timer >= EXPIRE_TIME) {
+ sap->sap.hops = htons(HOPCNT_INFINITY);
+ sap->state |= RTS_CHANGED;
+ }
+ if (sap->timer >= GARBAGE_TIME) {
+ sap = sap->back;
+ /* Perhaps we should send a REQUEST for this route? */
+ sap_delete(sap->forw);
+ continue;
+ }
+ /*
+ * XXX sap_sndmsg on RTS_CHANGED
+ */
+ if (sap->state & RTS_CHANGED) {
+ sap->state &= ~RTS_CHANGED;
+#ifdef notyet
+ /* don't send extraneous packets */
+ if (!supplier || sapbroadcast)
+ continue;
+ if ((ntohs(sap->sap.hops) + 1) == HOPCNT_INFINITY)
+ continue;
+ sap_msg->sap_cmd = htons(SAP_RESP);
+ sap_msg->sap[0] = sap->sap;
+ sap_msg->sap[0].hops =
+ htons(min(sap->sap.hops+1, HOPCNT_INFINITY));
+ toall(sapsndmsg, rt, 0);
+#endif
+ }
+ }
+ }
+ if (sapbroadcast)
+ sap_supply_toall(0);
+ if (ftrace && sapbroadcast)
+ dumpsaptable(ftrace, sap_head);
+}
+
+/*
+ * On hangup, let everyone know we're going away.
+ */
+void
+hup()
+{
+ register struct rthash *rh;
+ register struct rt_entry *rt;
+ register struct sap_hash *sh;
+ register struct sap_entry *sap;
+
+ if (supplier) {
+ for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++) {
+ rt = rh->rt_forw;
+ for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw)
+ rt->rt_metric = HOPCNT_INFINITY;
+ }
+ toall(supply, NULL, 0);
+
+ /*
+ * Now for SAP.
+ */
+ for (sh = sap_head; sh < &sap_head[SAPHASHSIZ]; sh++) {
+ sap = sh->forw;
+ for (; sap != (struct sap_entry *)sh; sap = sap->forw)
+ sap->sap.hops = htons(HOPCNT_INFINITY);
+ }
+ if (dosap)
+ sap_supply_toall(0);
+ }
+ exit(1);
+}
diff --git a/usr.sbin/IPXrouted/trace.c b/usr.sbin/IPXrouted/trace.c
new file mode 100644
index 0000000..d9e74b8
--- /dev/null
+++ b/usr.sbin/IPXrouted/trace.c
@@ -0,0 +1,520 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * This file includes significant work done at Cornell University by
+ * Bill Nesheim. That work included by permission.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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
+#if 0
+static char sccsid[] = "@(#)trace.c 8.1 (Berkeley) 6/5/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * Routing Table Management Daemon
+ */
+#define RIPCMDS
+#define SAPCMDS
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <time.h>
+#include "defs.h"
+
+#define NRECORDS 50 /* size of circular trace buffer */
+#ifdef DEBUG
+FILE *ftrace = stdout;
+int tracing = 1;
+#else /* DEBUG */
+FILE *ftrace = NULL;
+int tracing = 0;
+#endif
+
+void dumpif(FILE *fd, struct interface *ifp);
+void dumptrace(FILE *fd, char *dir, struct ifdebug *ifd);
+static int iftraceinit(struct interface *ifp, struct ifdebug *ifd);
+
+void
+traceinit(ifp)
+ register struct interface *ifp;
+{
+ if (iftraceinit(ifp, &ifp->int_input) &&
+ iftraceinit(ifp, &ifp->int_output))
+ return;
+ tracing = 0;
+ syslog(LOG_ERR, "traceinit: can't init %s\n", ifp->int_name);
+}
+
+static int
+iftraceinit(ifp, ifd)
+ struct interface *ifp;
+ register struct ifdebug *ifd;
+{
+ register struct iftrace *t;
+
+ ifd->ifd_records =
+ (struct iftrace *)malloc(NRECORDS * sizeof (struct iftrace));
+ if (ifd->ifd_records == 0)
+ return (0);
+ ifd->ifd_front = ifd->ifd_records;
+ ifd->ifd_count = 0;
+ for (t = ifd->ifd_records; t < ifd->ifd_records + NRECORDS; t++) {
+ t->ift_size = 0;
+ t->ift_packet = 0;
+ }
+ ifd->ifd_if = ifp;
+ return (1);
+}
+
+void
+traceon(file)
+ char *file;
+{
+
+ if (ftrace != NULL)
+ return;
+ ftrace = fopen(file, "a");
+ if (ftrace == NULL)
+ return;
+ dup2(fileno(ftrace), 1);
+ dup2(fileno(ftrace), 2);
+ tracing = 1;
+}
+
+void
+traceoff(void)
+{
+ if (!tracing)
+ return;
+ if (ftrace != NULL)
+ fclose(ftrace);
+ ftrace = NULL;
+ tracing = 0;
+}
+
+void
+trace(ifd, who, p, len, m)
+ register struct ifdebug *ifd;
+ struct sockaddr *who;
+ char *p;
+ int len, m;
+{
+ register struct iftrace *t;
+
+ if (ifd->ifd_records == 0)
+ return;
+ t = ifd->ifd_front++;
+ if (ifd->ifd_front >= ifd->ifd_records + NRECORDS)
+ ifd->ifd_front = ifd->ifd_records;
+ if (ifd->ifd_count < NRECORDS)
+ ifd->ifd_count++;
+ if (t->ift_size > 0 && t->ift_packet)
+ free(t->ift_packet);
+ t->ift_packet = 0;
+ t->ift_stamp = time(0);
+ t->ift_who = *who;
+ if (len > 0) {
+ t->ift_packet = malloc(len);
+ if (t->ift_packet)
+ bcopy(p, t->ift_packet, len);
+ else
+ len = 0;
+ }
+ t->ift_size = len;
+ t->ift_metric = m;
+}
+
+void
+traceaction(fd, action, rt)
+ FILE *fd;
+ char *action;
+ struct rt_entry *rt;
+{
+ struct sockaddr_ipx *dst, *gate;
+ static struct bits {
+ int t_bits;
+ char *t_name;
+ } flagbits[] = {
+ { RTF_UP, "UP" },
+ { RTF_GATEWAY, "GATEWAY" },
+ { RTF_HOST, "HOST" },
+ { 0 }
+ }, statebits[] = {
+ { RTS_PASSIVE, "PASSIVE" },
+ { RTS_REMOTE, "REMOTE" },
+ { RTS_INTERFACE,"INTERFACE" },
+ { RTS_CHANGED, "CHANGED" },
+ { 0 }
+ };
+ register struct bits *p;
+ register int first;
+ char *cp;
+
+ if (fd == NULL)
+ return;
+ fprintf(fd, "%s ", action);
+ dst = (struct sockaddr_ipx *)&rt->rt_dst;
+ gate = (struct sockaddr_ipx *)&rt->rt_router;
+ fprintf(fd, "dst %s, ", ipxdp_ntoa(&dst->sipx_addr));
+ fprintf(fd, "router %s, metric %d, ticks %d, flags",
+ ipxdp_ntoa(&gate->sipx_addr), rt->rt_metric, rt->rt_ticks);
+ cp = " %s";
+ for (first = 1, p = flagbits; p->t_bits > 0; p++) {
+ if ((rt->rt_flags & p->t_bits) == 0)
+ continue;
+ fprintf(fd, cp, p->t_name);
+ if (first) {
+ cp = "|%s";
+ first = 0;
+ }
+ }
+ fprintf(fd, " state");
+ cp = " %s";
+ for (first = 1, p = statebits; p->t_bits > 0; p++) {
+ if ((rt->rt_state & p->t_bits) == 0)
+ continue;
+ fprintf(fd, cp, p->t_name);
+ if (first) {
+ cp = "|%s";
+ first = 0;
+ }
+ }
+ putc('\n', fd);
+ if (!tracepackets && (rt->rt_state & RTS_PASSIVE) == 0 && rt->rt_ifp)
+ dumpif(fd, rt->rt_ifp);
+ fflush(fd);
+}
+
+void
+traceactionlog(action, rt)
+ char *action;
+ struct rt_entry *rt;
+{
+ struct sockaddr_ipx *dst, *gate;
+ static struct bits {
+ int t_bits;
+ char *t_name;
+ } flagbits[] = {
+ { RTF_UP, "UP" },
+ { RTF_GATEWAY, "GATEWAY" },
+ { RTF_HOST, "HOST" },
+ { 0 }
+ }, statebits[] = {
+ { RTS_PASSIVE, "PASSIVE" },
+ { RTS_REMOTE, "REMOTE" },
+ { RTS_INTERFACE,"INTERFACE" },
+ { RTS_CHANGED, "CHANGED" },
+ { 0 }
+ };
+ register struct bits *p;
+ register int first;
+ char *cp;
+ char *lstr, *olstr;
+
+ dst = (struct sockaddr_ipx *)&rt->rt_dst;
+ gate = (struct sockaddr_ipx *)&rt->rt_router;
+ asprintf(&lstr, "%s dst %s,", action, ipxdp_ntoa(&dst->sipx_addr));
+ olstr = lstr;
+ asprintf(&lstr, "%s router %s, metric %d, ticks %d, flags",
+ olstr, ipxdp_ntoa(&gate->sipx_addr), rt->rt_metric, rt->rt_ticks);
+ free(olstr);
+ olstr = lstr;
+ cp = "%s %s";
+ for (first = 1, p = flagbits; p->t_bits > 0; p++) {
+ if ((rt->rt_flags & p->t_bits) == 0)
+ continue;
+ asprintf(&lstr, cp, olstr, p->t_name);
+ free(olstr);
+ olstr = lstr;
+ if (first) {
+ cp = "%s|%s";
+ first = 0;
+ }
+ }
+ asprintf(&lstr, "%s state", olstr);
+ free(olstr);
+ olstr = lstr;
+ cp = "%s %s";
+ for (first = 1, p = statebits; p->t_bits > 0; p++) {
+ if ((rt->rt_state & p->t_bits) == 0)
+ continue;
+ asprintf(&lstr, cp, olstr, p->t_name);
+ free(olstr);
+ olstr = lstr;
+ if (first) {
+ cp = "%s|%s";
+ first = 0;
+ }
+ }
+ syslog(LOG_DEBUG, "%s", lstr);
+ free(lstr);
+}
+
+void
+tracesapactionlog(action, sap)
+ char *action;
+ struct sap_entry *sap;
+{
+ syslog(LOG_DEBUG, "%-12.12s service %04X %-20.20s "
+ "addr %s.%04X %c metric %d\n",
+ action,
+ ntohs(sap->sap.ServType),
+ sap->sap.ServName,
+ ipxdp_ntoa(&sap->sap.ipx),
+ ntohs(sap->sap.ipx.x_port),
+ (sap->clone ? 'C' : ' '),
+ ntohs(sap->sap.hops));
+}
+
+void
+dumpif(fd, ifp)
+ register struct interface *ifp;
+ FILE *fd;
+{
+ if (ifp->int_input.ifd_count || ifp->int_output.ifd_count) {
+ fprintf(fd, "*** Packet history for interface %s ***\n",
+ ifp->int_name);
+ dumptrace(fd, "to", &ifp->int_output);
+ dumptrace(fd, "from", &ifp->int_input);
+ fprintf(fd, "*** end packet history ***\n");
+ }
+}
+
+void
+dumptrace(fd, dir, ifd)
+ FILE *fd;
+ char *dir;
+ register struct ifdebug *ifd;
+{
+ register struct iftrace *t;
+ char *cp = !strcmp(dir, "to") ? "Output" : "Input";
+
+ if (ifd->ifd_front == ifd->ifd_records &&
+ ifd->ifd_front->ift_size == 0) {
+ fprintf(fd, "%s: no packets.\n", cp);
+ return;
+ }
+ fprintf(fd, "%s trace:\n", cp);
+ t = ifd->ifd_front - ifd->ifd_count;
+ if (t < ifd->ifd_records)
+ t += NRECORDS;
+ for ( ; ifd->ifd_count; ifd->ifd_count--, t++) {
+ if (t >= ifd->ifd_records + NRECORDS)
+ t = ifd->ifd_records;
+ if (t->ift_size == 0)
+ continue;
+ fprintf(fd, "%.24s: metric=%d\n", ctime(&t->ift_stamp),
+ t->ift_metric);
+ dumppacket(fd, dir, &t->ift_who, t->ift_packet, t->ift_size);
+ }
+}
+
+void
+dumppacket(fd, dir, source, cp, size)
+ FILE *fd;
+ char *dir;
+ struct sockaddr *source;
+ char *cp;
+ register int size;
+{
+ register struct rip *msg = (struct rip *)cp;
+ register struct netinfo *n;
+ struct sockaddr_ipx *who = (struct sockaddr_ipx *)source;
+
+ if (msg->rip_cmd && ntohs(msg->rip_cmd) < RIPCMD_MAX)
+ fprintf(fd, "%s %s %s#%x", ripcmds[ntohs(msg->rip_cmd)],
+ dir, ipxdp_ntoa(&who->sipx_addr),
+ ntohs(who->sipx_addr.x_port));
+ else {
+ fprintf(fd, "Bad cmd 0x%x %s %s#%x\n", ntohs(msg->rip_cmd),
+ dir, ipxdp_ntoa(&who->sipx_addr),
+ ntohs(who->sipx_addr.x_port));
+ fprintf(fd, "size=%d cp=%p packet=%p\n", size,
+ cp, packet);
+ return;
+ }
+ switch (ntohs(msg->rip_cmd)) {
+
+ case RIPCMD_REQUEST:
+ case RIPCMD_RESPONSE:
+ fprintf(fd, ":\n");
+ size -= sizeof (u_short);
+ n = msg->rip_nets;
+ for (; size > 0; n++, size -= sizeof (struct netinfo)) {
+ if (size < sizeof (struct netinfo))
+ break;
+ fprintf(fd, "\tnet %s metric %d ticks %d\n",
+ ipxdp_nettoa(n->rip_dst),
+ ntohs(n->rip_metric),
+ ntohs(n->rip_ticks));
+ }
+ break;
+
+ }
+}
+
+void
+dumpsappacket(fd, dir, source, cp, size)
+ FILE *fd;
+ char *dir;
+ struct sockaddr *source;
+ char *cp;
+ register int size;
+{
+ register struct sap_packet *msg = (struct sap_packet *)cp;
+ register struct sap_info *n;
+ struct sockaddr_ipx *who = (struct sockaddr_ipx *)source;
+
+ if (msg->sap_cmd && ntohs(msg->sap_cmd) < SAPCMD_MAX)
+ fprintf(fd, "%s %s %s#%x", sapcmds[ntohs(msg->sap_cmd)],
+ dir, ipxdp_ntoa(&who->sipx_addr),
+ ntohs(who->sipx_addr.x_port));
+ else {
+ fprintf(fd, "Bad cmd 0x%x %s %s#%x\n", ntohs(msg->sap_cmd),
+ dir, ipxdp_ntoa(&who->sipx_addr),
+ ntohs(who->sipx_addr.x_port));
+ fprintf(fd, "size=%d cp=%p packet=%p\n", size,
+ cp, packet);
+ return;
+ }
+ switch (ntohs(msg->sap_cmd)) {
+
+ case SAP_REQ:
+ case SAP_RESP:
+ case SAP_REQ_NEAR:
+ case SAP_RESP_NEAR:
+ fprintf(fd, ":\n");
+ size -= sizeof (u_short);
+ n = msg->sap;
+ for (; size > 0; n++, size -= sizeof (struct sap_info)) {
+ if (size < sizeof (struct sap_info))
+ break;
+ fprintf(fd, " service %04X %-20.20s "
+ "addr %s.%04X metric %d\n",
+ ntohs(n->ServType),
+ n->ServName,
+ ipxdp_ntoa(&n->ipx),
+ ntohs(n->ipx.x_port),
+ ntohs(n->hops));
+ }
+ break;
+
+ }
+}
+
+void
+dumpsaptable(fd, sh)
+ FILE *fd;
+ struct sap_hash *sh;
+{
+ register struct sap_entry *sap;
+ struct sap_hash *hash;
+ int x = 0;
+
+ fprintf(fd, "------- SAP table dump. -------\n");
+ for (hash = sh; hash < &sh[SAPHASHSIZ]; hash++, x++) {
+ fprintf(fd, "HASH %d\n", x);
+ sap = hash->forw;
+ for (; sap != (struct sap_entry *)hash; sap = sap->forw) {
+ fprintf(fd, " service %04X %-20.20s "
+ "addr %s.%04X %c metric %d\n",
+ ntohs(sap->sap.ServType),
+ sap->sap.ServName,
+ ipxdp_ntoa(&sap->sap.ipx),
+ ntohs(sap->sap.ipx.x_port),
+ (sap->clone ? 'C' : ' '),
+ ntohs(sap->sap.hops));
+ }
+ }
+ fprintf(fd, "\n");
+}
+
+void
+dumpriptable(fd)
+ FILE *fd;
+{
+ register struct rt_entry *rip;
+ struct rthash *hash;
+ int x;
+ struct rthash *rh = nethash;
+
+ fprintf(fd, "------- RIP table dump. -------\n");
+ x = 0;
+ fprintf(fd, "Network table.\n");
+
+ for (hash = rh; hash < &rh[ROUTEHASHSIZ]; hash++, x++) {
+ fprintf(fd, "HASH %d\n", x);
+ rip = hash->rt_forw;
+ for (; rip != (struct rt_entry *)hash; rip = rip->rt_forw) {
+ fprintf(fd, " dest %s\t",
+ ipxdp_ntoa(&satoipx_addr(rip->rt_dst)));
+ fprintf(fd, "%s metric %d, ticks %d\n",
+ ipxdp_ntoa(&satoipx_addr(rip->rt_router)),
+ rip->rt_metric,
+ rip->rt_ticks);
+ }
+ }
+ fprintf(fd, "\n");
+}
+
+union ipx_net_u net;
+
+char *
+ipxdp_nettoa(val)
+union ipx_net val;
+{
+ static char buf[100];
+ net.net_e = val;
+ (void)sprintf(buf, "%u", ntohl(net.long_e));
+ return (buf);
+}
+
+
+char *
+ipxdp_ntoa(addr)
+struct ipx_addr *addr;
+{
+ static char buf[100];
+
+ (void)sprintf(buf, "%s#%x:%x:%x:%x:%x:%x",
+ ipxdp_nettoa(addr->x_net),
+ addr->x_host.c_host[0], addr->x_host.c_host[1],
+ addr->x_host.c_host[2], addr->x_host.c_host[3],
+ addr->x_host.c_host[4], addr->x_host.c_host[5]);
+
+ return(buf);
+}
diff --git a/usr.sbin/IPXrouted/trace.h b/usr.sbin/IPXrouted/trace.h
new file mode 100644
index 0000000..c83a9d1
--- /dev/null
+++ b/usr.sbin/IPXrouted/trace.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * This file includes significant work done at Cornell University by
+ * Bill Nesheim. That work included by permission.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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.
+ *
+ * @(#)trace.h 8.1 (Berkeley) 6/5/93
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * IPX Routing Information Protocol.
+ */
+
+/*
+ * Trace record format.
+ */
+struct iftrace {
+ time_t ift_stamp; /* time stamp */
+ struct sockaddr ift_who; /* from/to */
+ char *ift_packet; /* pointer to packet */
+ short ift_size; /* size of packet */
+ short ift_metric; /* metric */
+};
+
+/*
+ * Per interface packet tracing buffers. An incoming and
+ * outgoing circular buffer of packets is maintained, per
+ * interface, for debugging. Buffers are dumped whenever
+ * an interface is marked down.
+ */
+struct ifdebug {
+ struct iftrace *ifd_records; /* array of trace records */
+ struct iftrace *ifd_front; /* next empty trace record */
+ int ifd_count; /* number of unprinted records */
+ struct interface *ifd_if; /* for locating stuff */
+};
+
+/*
+ * Packet tracing stuff.
+ */
+int tracepackets; /* watch packets as they go by */
+int tracing; /* on/off */
+FILE *ftrace; /* output trace file */
+
+#define TRACE_ACTION(action, route) { \
+ if (tracing) \
+ traceaction(ftrace, "action", route); \
+ traceactionlog(action, route); \
+ }
+#define TRACE_SAP_ACTION(action, service) { \
+ tracesapactionlog(action, service); \
+ }
+#define TRACE_INPUT(ifp, src, size) { \
+ if (tracing) { \
+ ifp = if_iflookup(src); \
+ if (ifp) \
+ trace(&ifp->int_input, src, \
+ &packet[sizeof(struct ipx)], size, \
+ ntohl(ifp->int_metric)); \
+ } \
+ if (tracepackets && ftrace) \
+ dumppacket(ftrace, "from", src, \
+ &packet[sizeof(struct ipx)], size); \
+ }
+#define TRACE_OUTPUT(ifp, dst, size) { \
+ if (tracing) { \
+ ifp = if_iflookup(dst); \
+ if (ifp) \
+ trace(&ifp->int_output, dst, \
+ &packet[sizeof(struct ipx)], \
+ size, ifp->int_metric); \
+ } \
+ if (tracepackets && ftrace) \
+ dumppacket(ftrace, "to", dst, \
+ &packet[sizeof(struct ipx)], size); \
+ }
+
+#define TRACE_SAP_OUTPUT(ifp, dst, size) { \
+ if (tracing) { \
+ ifp = if_iflookup(dst); \
+ if (ifp) \
+ trace(&ifp->int_output, dst, \
+ &packet[sizeof(struct ipx)], \
+ size, ifp->int_metric); \
+ } \
+ if (tracepackets && ftrace) \
+ dumpsappacket(ftrace, "to", dst, \
+ &packet[sizeof(struct ipx)], size); \
+ }
+
+void traceinit(struct interface *);
+void traceon(char *file);
+void traceoff(void);
+void traceaction(FILE *, char *, struct rt_entry *);
+void traceactionlog(char *, struct rt_entry *);
+void tracesapactionlog(char *action, struct sap_entry *sap);
+void trace(struct ifdebug *, struct sockaddr *, char *, int, int);
+void dumppacket(FILE *, char *, struct sockaddr *, char *, int);
+void dumpsappacket(FILE *, char *, struct sockaddr *, char *, int);
+void dumpsaptable(FILE *fd, struct sap_hash *sh);
+void dumpriptable(FILE *fd);
+
+char *ipxdp_nettoa(union ipx_net);
+char *ipxdp_ntoa(struct ipx_addr *);
+
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile
new file mode 100644
index 0000000..c9fefd8
--- /dev/null
+++ b/usr.sbin/Makefile
@@ -0,0 +1,510 @@
+# From: @(#)Makefile 5.20 (Berkeley) 6/12/93
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+# XXX MISSING: mkproto
+SUBDIR= ${_ac} \
+ ${_accton} \
+ ${_acpi} \
+ adduser \
+ ${_amd} \
+ ${_ancontrol} \
+ ${_apm} \
+ ${_apmd} \
+ arp \
+ ${_asf} \
+ ${_atm} \
+ ${_audit} \
+ ${_auditd} \
+ ${_auditreduce} \
+ ${_authpf} \
+ ${_bluetooth} \
+ ${_boot0cfg} \
+ ${_boot98cfg} \
+ bootparamd \
+ ${_bsnmpd} \
+ ${_btxld} \
+ burncd \
+ cdcontrol \
+ chkgrp \
+ chown \
+ chroot \
+ ckdist \
+ clear_locks \
+ ${_config} \
+ ${_cpucontrol} \
+ crashinfo \
+ cron \
+ ${_crunch} \
+ ${_ctm} \
+ daemon \
+ dconschat \
+ devinfo \
+ digictl \
+ diskinfo \
+ ${_dnssec-keygen} \
+ ${_dnssec-signzone} \
+ dumpcis \
+ ${_editmap} \
+ ${_edquota} \
+ ${_eeprom} \
+ extattr \
+ extattrctl \
+ ${_faithd} \
+ ${_fdcontrol} \
+ ${_fdformat} \
+ ${_fdread} \
+ ${_fdwrite} \
+ fifolog \
+ ${_flowctl} \
+ ${_freebsd-update} \
+ ${_ftp-proxy} \
+ fwcontrol \
+ getfmac \
+ getpmac \
+ gstat \
+ ${_gssd} \
+ i2c \
+ ifmcstat \
+ inetd \
+ iostat \
+ ${_ip6addrctl} \
+ ${_ipfwpcap} \
+ ${_IPXrouted} \
+ ${_jail} \
+ ${_jexec} \
+ ${_jls} \
+ ${_kbdcontrol} \
+ ${_kbdmap} \
+ ${_keyserv} \
+ ${_kgmon} \
+ ${_kgzip} \
+ kldxref \
+ lastlogin \
+ ${_lmcconfig} \
+ ${_lpr} \
+ ${_lptcontrol} \
+ ${_mailstats} \
+ mailwrapper \
+ makefs \
+ ${_makemap} \
+ manctl \
+ memcontrol \
+ mergemaster \
+ mixer \
+ ${_mld6query} \
+ mlxcontrol \
+ mountd \
+ ${_mount_nwfs} \
+ mount_portalfs \
+ ${_mount_smbfs} \
+ ${_moused} \
+ ${_mptable} \
+ mtest \
+ mtree \
+ ${_named} \
+ ${_named-checkconf} \
+ ${_named-checkzone} \
+ ${_named.reload} \
+ ${_ndiscvt} \
+ ${_ndp} \
+ newsyslog \
+ nfsd \
+ ${_ngctl} \
+ ${_nghook} \
+ nologin \
+ ${_nscd} \
+ ${_ntp} \
+ ${_nvram} \
+ ${_ofwdump} \
+ pciconf \
+ periodic \
+ ${_pkg_install} \
+ ${_pmcannotate} \
+ ${_pmccontrol} \
+ ${_pmcstat} \
+ ${_pnpinfo} \
+ ${_portsnap} \
+ powerd \
+ ${_ppp} \
+ ${_pppctl} \
+ ${_pppd} \
+ ${_pppstats} \
+ ${_praliases} \
+ ${_praudit} \
+ procctl \
+ pstat \
+ pw \
+ pwd_mkdb \
+ quot \
+ ${_quotaon} \
+ rarpd \
+ ${_raycontrol} \
+ ${_repquota} \
+ ${_rip6query} \
+ rmt \
+ ${_rndc} \
+ ${_rndc-confgen} \
+ ${_route6d} \
+ rpcbind \
+ rpc.lockd \
+ rpc.statd \
+ rpc.umntall \
+ ${_rpc.yppasswdd} \
+ ${_rpc.ypupdated} \
+ ${_rpc.ypxfrd} \
+ ${_rrenumd} \
+ ${_rtadvd} \
+ rtprio \
+ ${_rtsold} \
+ ${_rwhod} \
+ ${_sa} \
+ ${_sade} \
+ ${_sendmail} \
+ setfib \
+ setfmac \
+ setpmac \
+ ${_sicontrol} \
+ ${_sliplogin} \
+ ${_slstat} \
+ smbmsg \
+ snapinfo \
+ ${_spkrtest} \
+ spray \
+ ${_sysinstall} \
+ syslogd \
+ tcpdchk \
+ tcpdmatch \
+ tcpdrop \
+ tcpdump \
+ timed \
+ traceroute \
+ ${_traceroute6} \
+ trpt \
+ tzsetup \
+ ugidfw \
+ ${_usbdevs} \
+ ${_usbconfig} \
+ ${_vidcontrol} \
+ vipw \
+ watch \
+ watchdogd \
+ ${_wlandebug} \
+ ${_wlconfig} \
+ ${_wpa} \
+ ${_ypbind} \
+ ${_yp_mkdb} \
+ ${_yppoll} \
+ ${_yppush} \
+ ${_ypserv} \
+ ${_ypset} \
+ zic \
+ ${_zzz}
+
+# NB: keep these sorted by MK_* knobs
+
+.if ${MK_ACCT} != "no"
+_ac= ac
+_accton= accton
+_sa= sa
+.endif
+
+.if ${MK_AMD} != "no"
+_amd= amd
+.endif
+
+.if ${MK_AUDIT} != "no"
+_audit= audit
+_auditd= auditd
+_auditreduce= auditreduce
+_praudit= praudit
+.endif
+
+.if ${MK_AUTHPF} != "no"
+_authpf= authpf
+.endif
+
+.if ${MK_BIND_DNSSEC} != "no" && ${MK_OPENSSL} != "no"
+_dnssec-keygen= dnssec-keygen
+_dnssec-signzone= dnssec-signzone
+.endif
+.if ${MK_BIND_NAMED} != "no"
+_named= named
+_named-checkconf= named-checkconf
+_named-checkzone= named-checkzone
+_named.reload= named.reload
+_rndc= rndc
+_rndc-confgen= rndc-confgen
+.endif
+
+.if ${MK_BLUETOOTH} != "no"
+_bluetooth= bluetooth
+.endif
+
+.if ${MK_BSNMP} != "no"
+_bsnmpd= bsnmpd
+.endif
+
+.if ${MK_CTM} != "no"
+_ctm= ctm
+.endif
+
+.if ${MK_FLOPPY} != "no"
+_fdcontrol= fdcontrol
+_fdformat= fdformat
+_fdread= fdread
+_fdwrite= fdwrite
+.endif
+
+.if ${MK_FREEBSD_UPDATE} != "no"
+_freebsd-update= freebsd-update
+.endif
+
+.if ${MK_GSSAPI} != no
+_gssd= gssd
+.endif
+
+.if ${MK_INET6} != "no"
+_faithd= faithd
+_ip6addrctl= ip6addrctl
+_mld6query= mld6query
+_ndp= ndp
+_rip6query= rip6query
+_route6d= route6d
+_rrenumd= rrenumd
+_rtadvd= rtadvd
+_rtsold= rtsold
+_traceroute6= traceroute6
+.endif
+
+.if ${MK_IPFW} != "no"
+_ipfwpcap= ipfwpcap
+.endif
+
+.if ${MK_IPX} != "no"
+_IPXrouted= IPXrouted
+.endif
+
+.if ${MK_JAIL} != "no"
+_jail= jail
+_jexec= jexec
+_jls= jls
+.endif
+
+# XXX MK_SYSCONS
+# XXX is moused w/ usb useful?
+.if ${MK_LEGACY_CONSOLE} != "no"
+_kbdcontrol= kbdcontrol
+_kbdmap= kbdmap
+_moused= moused
+_vidcontrol= vidcontrol
+.endif
+
+.if ${MK_LIBTHR} != "no" || ${MK_LIBPTHREAD} != "no"
+.if ${MK_PPP} != "no"
+_pppctl= pppctl
+.endif
+.if ${MK_NS_CACHING} != "no"
+_nscd= nscd
+.endif
+.endif
+
+.if ${MK_LPR} != "no"
+_lpr= lpr
+.endif
+
+.if ${MK_NETGRAPH} != "no"
+_flowctl= flowctl
+_lmcconfig= lmcconfig
+_ngctl= ngctl
+_nghook= nghook
+.endif
+
+.if ${MK_NIS} != "no"
+_rpc.yppasswdd= rpc.yppasswdd
+_rpc.ypupdated= rpc.ypupdated
+_rpc.ypxfrd= rpc.ypxfrd
+_ypbind= ypbind
+_yp_mkdb= yp_mkdb
+_yppoll= yppoll
+_yppush= yppush
+_ypserv= ypserv
+_ypset= ypset
+.endif
+
+.if ${MK_NTP} != "no"
+_ntp= ntp
+.endif
+
+.if ${MK_OPENSSL} != "no"
+_keyserv= keyserv
+.endif
+
+.if ${MK_PF} != "no"
+_ftp-proxy= ftp-proxy
+.endif
+
+.if ${MK_PKGTOOLS} != "no"
+_pkg_install= pkg_install
+.endif
+
+# XXX MK_TOOLCHAIN?
+.if ${MK_PMC} != "no"
+_pmcannotate= pmcannotate
+_pmccontrol= pmccontrol
+_pmcstat= pmcstat
+.endif
+
+.if ${MK_PORTSNAP} != "no"
+_portsnap= portsnap
+.endif
+
+.if ${MK_PPP} != "no"
+_ppp= ppp
+#_pppctl handled below
+_pppd= pppd
+_pppstats= pppstats
+.endif
+
+.if ${MK_QUOTAS} != "no"
+_edquota= edquota
+_quotaon= quotaon
+_repquota= repquota
+.endif
+
+.if ${MK_RCMDS} != "no"
+_rwhod= rwhod
+.endif
+
+.if ${MK_SENDMAIL} != "no"
+_editmap= editmap
+_mailstats= mailstats
+_makemap= makemap
+_praliases= praliases
+_sendmail= sendmail
+.endif
+
+.if ${MK_SLIP} != "no"
+_sliplogin= sliplogin
+_slstat= slstat
+.endif
+
+.if ${MK_SYSINSTALL} != "no"
+.if ${MACHINE_ARCH} == "amd64" || ${MACHINE_ARCH} == "i386" || \
+ ${MACHINE_ARCH} == "sparc64"
+_sade= sade
+.endif
+.if ${MACHINE_ARCH} != "arm" && ${MACHINE_ARCH} != "mips"
+_sysinstall= sysinstall
+.endif
+.endif
+
+.if ${MK_TOOLCHAIN} != "no"
+_config= config
+_crunch= crunch
+.endif
+
+.if ${MK_USB} != "no"
+#_usbdevs= usbdevs
+_usbconfig= usbconfig
+.endif
+
+.if ${MK_WIRELESS} != "no"
+_ancontrol= ancontrol
+_raycontrol= raycontrol
+_wlandebug= wlandebug
+_wpa= wpa
+.endif
+
+.if ${MACHINE_ARCH} == "arm"
+_kgmon= kgmon
+.endif
+
+.if ${MACHINE_ARCH} == "i386"
+.if ${MK_APM} != "no"
+_apm= apm
+_apmd= apmd
+.endif
+_asf= asf
+.if ${MK_TOOLCHAIN} != "no"
+_btxld= btxld
+.endif
+_cpucontrol= cpucontrol
+_kgmon= kgmon
+_kgzip= kgzip
+_lptcontrol= lptcontrol
+.if ${MK_NCP} != "no"
+_mount_nwfs= mount_nwfs
+.endif
+_mount_smbfs= mount_smbfs
+_mptable= mptable
+.if ${MK_NDIS} != "no"
+_ndiscvt= ndiscvt
+.endif
+_pnpinfo= pnpinfo
+_sicontrol= sicontrol
+_spkrtest= spkrtest
+_zzz= zzz
+.if ${MACHINE} == "i386"
+.if ${MK_ACPI} != "no"
+_acpi= acpi
+.endif
+_boot0cfg= boot0cfg
+.if ${MK_WIRELESS} != "no"
+_wlconfig= wlconfig
+.endif
+.elif ${MACHINE} == "pc98"
+_boot98cfg= boot98cfg
+.endif
+.endif
+
+# kgzip: builds, but missing support files
+# mptable: broken (not 64 bit clean)
+# pnpinfo: crashes (not really useful anyway)
+.if ${MACHINE_ARCH} == "amd64"
+.if ${MK_ACPI} != "no"
+_acpi= acpi
+.endif
+_asf= asf
+_boot0cfg= boot0cfg
+.if ${MK_TOOLCHAIN} != "no"
+_btxld= btxld
+.endif
+_cpucontrol= cpucontrol
+_kgmon= kgmon
+_lptcontrol= lptcontrol
+.if ${MK_NCP} != "no"
+_mount_nwfs= mount_nwfs
+.endif
+_mount_smbfs= mount_smbfs
+_mptable= mptable
+.if ${MK_NDIS} != "no"
+_ndiscvt= ndiscvt
+.endif
+_sicontrol= sicontrol
+_spkrtest= spkrtest
+_zzz= zzz
+.endif
+
+.if ${MACHINE_ARCH} == "ia64"
+.if ${MK_ACPI} != "no"
+_acpi= acpi
+.endif
+_kgmon= kgmon
+_mount_smbfs= mount_smbfs
+_zzz= zzz
+.endif
+
+.if ${MACHINE_ARCH} == "powerpc"
+_mount_smbfs= mount_smbfs
+_nvram= nvram
+_ofwdump= ofwdump
+.endif
+
+.if ${MACHINE_ARCH} == "sparc64"
+_eeprom= eeprom
+_ofwdump= ofwdump
+.endif
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/Makefile.inc b/usr.sbin/Makefile.inc
new file mode 100644
index 0000000..4347591
--- /dev/null
+++ b/usr.sbin/Makefile.inc
@@ -0,0 +1,4 @@
+# @(#)Makefile.inc 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+BINDIR?= /usr/sbin
diff --git a/usr.sbin/ac/Makefile b/usr.sbin/ac/Makefile
new file mode 100644
index 0000000..c45a0d6
--- /dev/null
+++ b/usr.sbin/ac/Makefile
@@ -0,0 +1,25 @@
+# $FreeBSD$
+
+PROG= ac
+MAN= ac.8
+
+WARNS?= 4
+
+# Temporary, while tracking down problem wrt 64-bit time_t's on sparc64
+.if ${MACHINE_ARCH} == "sparc64"
+CFLAGS+=-DDEBUG
+.endif
+
+# If "CONSOLE_TTY" is not defined, this program is compatible with the
+# traditional implementation (using SunOS 4.x as the sample traditional
+# implementation). This is the default.
+#
+# If "CONSOLE_TTY" is defined, it must be defined to the appropriate
+# console name, e.g. "vga". Additionally, the various commented-out
+# sections of the man page should be uncommented. This is not the
+# default because of the inability to detect the proper console name
+# easily, especially on m68k systems, which can share binaries.
+#
+#CFLAGS+=-DCONSOLE_TTY=\"vga\"
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ac/ac.8 b/usr.sbin/ac/ac.8
new file mode 100644
index 0000000..efe3e23
--- /dev/null
+++ b/usr.sbin/ac/ac.8
@@ -0,0 +1,155 @@
+.\"
+.\" Copyright (c) 1994 Simon J. Gerraty
+.\" 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.
+.\" 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 March 15, 1994
+.Dt AC 8
+.Os
+.Sh NAME
+.Nm ac
+.Nd connect time accounting
+.Sh SYNOPSIS
+.Nm
+.Op Fl dp
+.\".Op Fl c Ar console
+.Op Fl t Ar tty
+.Op Fl w Ar wtmp
+.Op Ar users ...
+.Sh DESCRIPTION
+If the file
+.Pa /var/log/wtmp
+exists, a record of individual login and logout
+times are written to it by
+.Xr login 1
+and
+.Xr init 8 ,
+respectively.
+The
+.Nm
+utility
+examines these records and writes the accumulated connect time (in hours)
+for all logins to the standard output.
+.Pp
+The options are as follows:
+.Bl -tag -width indentXXX
+.It Fl d
+Display the connect times in 24 hour chunks.
+.\" .It Fl c Ar console
+.\" Use
+.\" .Ar console
+.\" as the name of the device that local X sessions (ut_host of ":0.0")
+.\" originate from. If any login has been recorded on
+.\" .Ar console
+.\" then these X sessions are ignored unless COMPAT_SUNOS was defined at
+.\" compile time.
+.It Fl p
+Print individual users' totals.
+.It Fl t Ar tty
+Only do accounting logins on certain ttys.
+The
+.Ar tty
+specification can start with '!' to indicate not this
+.Ar tty
+and end with '*' to indicate all similarly named ttys.
+Multiple
+.Fl t
+flags may be specified.
+.It Fl w Ar wtmp
+Read connect time data from
+.Ar wtmp
+instead of the default file,
+.Pa /var/log/wtmp .
+.It Ar users ...
+Display totals for the given individuals only.
+.El
+.Pp
+If no arguments are given,
+.Nm
+displays the total connect time for all
+accounts with login sessions recorded in
+.Pa wtmp .
+.Pp
+The default
+.Pa wtmp
+file will increase without bound unless it is truncated.
+It is normally truncated by the daily scripts run
+by
+.Xr cron 8 ,
+which rename and rotate the
+.Pa wtmp
+files, keeping a week's worth of data on
+hand.
+No login or connect time accounting is performed if
+.Pa /var/log/wtmp
+does not exist.
+.Pp
+For example,
+.Bd -literal -offset
+ac -p -t "ttyd*" > modems
+ac -p -t "!ttyd*" > other
+.Ed
+.Pp
+allows times recorded in
+.Pa modems
+to be charged out at a different rate than
+.Pa other .
+.Sh FILES
+.Bl -tag -width /var/log/wtmp -compact
+.It Pa /var/log/wtmp
+connect time accounting file
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr utmp 5 ,
+.Xr init 8 ,
+.Xr sa 8
+.\" .Sh NOTES
+.\" If COMPAT_SUNOS is defined
+.\" .Nm ac
+.\" ignores the fact that entries with ut_host of ":0.0" are not real
+.\" login sessions. Normally such entries are ignored except in the case
+.\" of a user being logged in when the
+.\" .Pa wtmp
+.\" file was rotated, in which case a login with ut_host of ":0.0" may
+.\" appear without any preceding console logins.
+.\" If no one is logged in on the console, the user is deemed to have
+.\" logged in on at the earliest time stamp found in
+.\" .Pa wtmp .
+.\" Use of
+.\" .Pa console
+.\" allows
+.\" .Nm ac
+.\" to identify and correctly process a logout for the user. The default
+.\" value for
+.\" .Pa console
+.\" is usually correct at compile time.
diff --git a/usr.sbin/ac/ac.c b/usr.sbin/ac/ac.c
new file mode 100644
index 0000000..031099a
--- /dev/null
+++ b/usr.sbin/ac/ac.c
@@ -0,0 +1,668 @@
+/*
+ * Copyright (c) 1994 Christopher G. Demetriou.
+ * @(#)Copyright (c) 1994, Simon J. Gerraty.
+ *
+ * This is free software. It comes with NO WARRANTY.
+ * Permission to use, modify and distribute this source code
+ * is granted subject to the following conditions.
+ * 1/ that the above copyright notice and this notice
+ * are preserved in all copies and that due credit be given
+ * to the author.
+ * 2/ that any changes to this code are clearly commented
+ * as such so that the author does not get blamed for bugs
+ * other than his own.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <err.h>
+#include <errno.h>
+#include <langinfo.h>
+#include <locale.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <timeconv.h>
+#include <unistd.h>
+#include <utmp.h>
+
+/*
+ * this is for our list of currently logged in sessions
+ */
+struct utmp_list {
+ struct utmp_list *next;
+ struct utmp usr;
+};
+
+/*
+ * this is for our list of users that are accumulating time.
+ */
+struct user_list {
+ struct user_list *next;
+ char name[UT_NAMESIZE+1];
+ time_t secs;
+};
+
+/*
+ * this is for chosing whether to ignore a login
+ */
+struct tty_list {
+ struct tty_list *next;
+ char name[UT_LINESIZE+3];
+ size_t len;
+ int ret;
+};
+
+/*
+ * globals - yes yuk
+ */
+#ifdef CONSOLE_TTY
+static char *Console = CONSOLE_TTY;
+#endif
+static time_t Total = 0;
+static time_t FirstTime = 0;
+static int Flags = 0;
+static struct user_list *Users = NULL;
+static struct tty_list *Ttys = NULL;
+
+#define NEW(type) (type *)malloc(sizeof (type))
+
+#define AC_W 1 /* not _PATH_WTMP */
+#define AC_D 2 /* daily totals (ignore -p) */
+#define AC_P 4 /* per-user totals */
+#define AC_U 8 /* specified users only */
+#define AC_T 16 /* specified ttys only */
+
+#ifdef DEBUG
+static int Debug = 0;
+#endif
+
+int main(int, char **);
+int ac(FILE *);
+struct tty_list *add_tty(char *);
+#ifdef DEBUG
+const char *debug_pfx(const struct utmp *, const struct utmp *);
+#endif
+int do_tty(char *);
+FILE *file(const char *);
+struct utmp_list *log_in(struct utmp_list *, struct utmp *);
+struct utmp_list *log_out(struct utmp_list *, struct utmp *);
+int on_console(struct utmp_list *);
+void show(const char *, time_t);
+void show_today(struct user_list *, struct utmp_list *,
+ time_t);
+void show_users(struct user_list *);
+struct user_list *update_user(struct user_list *, char *, time_t);
+void usage(void);
+
+/*
+ * open wtmp or die
+ */
+FILE *
+file(const char *name)
+{
+ FILE *fp;
+
+ if ((fp = fopen(name, "r")) == NULL)
+ err(1, "%s", name);
+ /* in case we want to discriminate */
+ if (strcmp(_PATH_WTMP, name))
+ Flags |= AC_W;
+ return fp;
+}
+
+struct tty_list *
+add_tty(char *name)
+{
+ struct tty_list *tp;
+ char *rcp;
+
+ Flags |= AC_T;
+
+ if ((tp = NEW(struct tty_list)) == NULL)
+ errx(1, "malloc failed");
+ tp->len = 0; /* full match */
+ tp->ret = 1; /* do if match */
+ if (*name == '!') { /* don't do if match */
+ tp->ret = 0;
+ name++;
+ }
+ strlcpy(tp->name, name, sizeof (tp->name));
+ if ((rcp = strchr(tp->name, '*')) != NULL) { /* wild card */
+ *rcp = '\0';
+ tp->len = strlen(tp->name); /* match len bytes only */
+ }
+ tp->next = Ttys;
+ Ttys = tp;
+ return Ttys;
+}
+
+/*
+ * should we process the named tty?
+ */
+int
+do_tty(char *name)
+{
+ struct tty_list *tp;
+ int def_ret = 0;
+
+ for (tp = Ttys; tp != NULL; tp = tp->next) {
+ if (tp->ret == 0) /* specific don't */
+ def_ret = 1; /* default do */
+ if (tp->len != 0) {
+ if (strncmp(name, tp->name, tp->len) == 0)
+ return tp->ret;
+ } else {
+ if (strncmp(name, tp->name, sizeof (tp->name)) == 0)
+ return tp->ret;
+ }
+ }
+ return def_ret;
+}
+
+#ifdef CONSOLE_TTY
+/*
+ * is someone logged in on Console?
+ */
+int
+on_console(struct utmp_list *head)
+{
+ struct utmp_list *up;
+
+ for (up = head; up; up = up->next) {
+ if (strncmp(up->usr.ut_line, Console,
+ sizeof (up->usr.ut_line)) == 0)
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+/*
+ * update user's login time
+ */
+struct user_list *
+update_user(struct user_list *head, char *name, time_t secs)
+{
+ struct user_list *up;
+
+ for (up = head; up != NULL; up = up->next) {
+ if (strncmp(up->name, name, UT_NAMESIZE) == 0) {
+ up->secs += secs;
+ Total += secs;
+ return head;
+ }
+ }
+ /*
+ * not found so add new user unless specified users only
+ */
+ if (Flags & AC_U)
+ return head;
+
+ if ((up = NEW(struct user_list)) == NULL)
+ errx(1, "malloc failed");
+ up->next = head;
+ strlcpy(up->name, name, sizeof (up->name));
+ up->secs = secs;
+ Total += secs;
+ return up;
+}
+
+#ifdef DEBUG
+/*
+ * Create a string which is the standard prefix for a debug line. It
+ * includes a timestamp (perhaps with year), device-name, and user-name.
+ */
+const char *
+debug_pfx(const struct utmp *event_up, const struct utmp *userinf_up)
+{
+ static char str_result[40+UT_LINESIZE+UT_NAMESIZE];
+ static char thisyear[5];
+ size_t maxcopy;
+ time_t ut_timecopy;
+
+ if (thisyear[0] == '\0') {
+ /* Figure out what "this year" is. */
+ time(&ut_timecopy);
+ strlcpy(str_result, ctime(&ut_timecopy), sizeof(str_result));
+ strlcpy(thisyear, &str_result[20], sizeof(thisyear));
+ }
+
+ if (event_up->ut_time == 0)
+ strlcpy(str_result, "*ZeroTime* --:--:-- ", sizeof(str_result));
+ else {
+ /*
+ * The type of utmp.ut_time is not necessary type time_t, as
+ * it is explicitly defined as type int32_t. Copy the value
+ * for platforms where sizeof(time_t) != sizeof(int32_t).
+ */
+ ut_timecopy = _time32_to_time(event_up->ut_time);
+ strlcpy(str_result, ctime(&ut_timecopy), sizeof(str_result));
+ /*
+ * Include the year, if it is not the same year as "now".
+ */
+ if (strncmp(&str_result[20], thisyear, 4) == 0)
+ str_result[20] = '\0';
+ else {
+ str_result[24] = ' '; /* Replace a '\n' */
+ str_result[25] = '\0';
+ }
+ }
+
+ if (userinf_up->ut_line[0] == '\0')
+ strlcat(str_result, "NoDev", sizeof(str_result));
+ else {
+ /* ut_line is not necessarily null-terminated. */
+ maxcopy = strlen(str_result) + UT_LINESIZE + 1;
+ if (maxcopy > sizeof(str_result))
+ maxcopy = sizeof(str_result);
+ strlcat(str_result, userinf_up->ut_line, maxcopy);
+ }
+ strlcat(str_result, ": ", sizeof(str_result));
+
+ if (userinf_up->ut_name[0] == '\0')
+ strlcat(str_result, "LogOff", sizeof(str_result));
+ else {
+ /* ut_name is not necessarily null-terminated. */
+ maxcopy = strlen(str_result) + UT_NAMESIZE + 1;
+ if (maxcopy > sizeof(str_result))
+ maxcopy = sizeof(str_result);
+ strlcat(str_result, userinf_up->ut_name, maxcopy);
+ }
+
+ return (str_result);
+}
+#endif
+
+int
+main(int argc, char *argv[])
+{
+ FILE *fp;
+ int c;
+
+ (void) setlocale(LC_TIME, "");
+
+ fp = NULL;
+ while ((c = getopt(argc, argv, "Dc:dpt:w:")) != -1) {
+ switch (c) {
+#ifdef DEBUG
+ case 'D':
+ Debug++;
+ break;
+#endif
+ case 'c':
+#ifdef CONSOLE_TTY
+ Console = optarg;
+#else
+ usage(); /* XXX */
+#endif
+ break;
+ case 'd':
+ Flags |= AC_D;
+ break;
+ case 'p':
+ Flags |= AC_P;
+ break;
+ case 't': /* only do specified ttys */
+ add_tty(optarg);
+ break;
+ case 'w':
+ fp = file(optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+ if (optind < argc) {
+ /*
+ * initialize user list
+ */
+ for (; optind < argc; optind++) {
+ Users = update_user(Users, argv[optind], (time_t)0);
+ }
+ Flags |= AC_U; /* freeze user list */
+ }
+ if (Flags & AC_D)
+ Flags &= ~AC_P;
+ if (fp == NULL) {
+ /*
+ * if _PATH_WTMP does not exist, exit quietly
+ */
+ if (access(_PATH_WTMP, 0) != 0 && errno == ENOENT)
+ return 0;
+
+ fp = file(_PATH_WTMP);
+ }
+ ac(fp);
+
+ return 0;
+}
+
+/*
+ * print login time in decimal hours
+ */
+void
+show(const char *name, time_t secs)
+{
+ (void)printf("\t%-*s %8.2f\n", UT_NAMESIZE, name,
+ ((double)secs / 3600));
+}
+
+void
+show_users(struct user_list *list)
+{
+ struct user_list *lp;
+
+ for (lp = list; lp; lp = lp->next)
+ show(lp->name, lp->secs);
+}
+
+/*
+ * print total login time for 24hr period in decimal hours
+ */
+void
+show_today(struct user_list *users, struct utmp_list *logins, time_t secs)
+{
+ struct user_list *up;
+ struct utmp_list *lp;
+ char date[64];
+ time_t yesterday = secs - 1;
+ static int d_first = -1;
+
+ if (d_first < 0)
+ d_first = (*nl_langinfo(D_MD_ORDER) == 'd');
+ (void)strftime(date, sizeof (date),
+ d_first ? "%e %b total" : "%b %e total",
+ localtime(&yesterday));
+
+ /* restore the missing second */
+ yesterday++;
+
+ for (lp = logins; lp != NULL; lp = lp->next) {
+ secs = yesterday - lp->usr.ut_time;
+ Users = update_user(Users, lp->usr.ut_name, secs);
+ lp->usr.ut_time = yesterday; /* as if they just logged in */
+ }
+ secs = 0;
+ for (up = users; up != NULL; up = up->next) {
+ secs += up->secs;
+ up->secs = 0; /* for next day */
+ }
+ if (secs)
+ (void)printf("%s %11.2f\n", date, ((double)secs / 3600));
+}
+
+/*
+ * log a user out and update their times.
+ * if ut_line is "~", we log all users out as the system has
+ * been shut down.
+ */
+struct utmp_list *
+log_out(struct utmp_list *head, struct utmp *up)
+{
+ struct utmp_list *lp, *lp2, *tlp;
+ time_t secs;
+
+ for (lp = head, lp2 = NULL; lp != NULL; )
+ if (*up->ut_line == '~' || strncmp(lp->usr.ut_line, up->ut_line,
+ sizeof (up->ut_line)) == 0) {
+ secs = up->ut_time - lp->usr.ut_time;
+ Users = update_user(Users, lp->usr.ut_name, secs);
+#ifdef DEBUG
+ if (Debug)
+ printf("%s logged out (%2d:%02d:%02d)\n",
+ debug_pfx(up, &lp->usr), (int)(secs / 3600),
+ (int)((secs % 3600) / 60),
+ (int)(secs % 60));
+#endif
+ /*
+ * now lose it
+ */
+ tlp = lp;
+ lp = lp->next;
+ if (tlp == head)
+ head = lp;
+ else if (lp2 != NULL)
+ lp2->next = lp;
+ free(tlp);
+ } else {
+ lp2 = lp;
+ lp = lp->next;
+ }
+ return head;
+}
+
+
+/*
+ * if do_tty says ok, login a user
+ */
+struct utmp_list *
+log_in(struct utmp_list *head, struct utmp *up)
+{
+ struct utmp_list *lp;
+
+ /*
+ * this could be a login. if we're not dealing with
+ * the console name, say it is.
+ *
+ * If we are, and if ut_host==":0.0" we know that it
+ * isn't a real login. _But_ if we have not yet recorded
+ * someone being logged in on Console - due to the wtmp
+ * file starting after they logged in, we'll pretend they
+ * logged in, at the start of the wtmp file.
+ */
+
+#ifdef CONSOLE_TTY
+ if (up->ut_host[0] == ':') {
+ /*
+ * SunOS 4.0.2 does not treat ":0.0" as special but we
+ * do.
+ */
+ if (on_console(head))
+ return head;
+ /*
+ * ok, no recorded login, so they were here when wtmp
+ * started! Adjust ut_time!
+ */
+ up->ut_time = FirstTime;
+ /*
+ * this allows us to pick the right logout
+ */
+ strlcpy(up->ut_line, Console, sizeof (up->ut_line));
+ }
+#endif
+ /*
+ * If we are doing specified ttys only, we ignore
+ * anything else.
+ */
+ if (Flags & AC_T)
+ if (!do_tty(up->ut_line))
+ return head;
+
+ /*
+ * go ahead and log them in
+ */
+ if ((lp = NEW(struct utmp_list)) == NULL)
+ errx(1, "malloc failed");
+ lp->next = head;
+ head = lp;
+ memmove((char *)&lp->usr, (char *)up, sizeof (struct utmp));
+#ifdef DEBUG
+ if (Debug) {
+ printf("%s logged in", debug_pfx(&lp->usr, up));
+ if (*up->ut_host)
+ printf(" (%-.*s)", (int)sizeof(up->ut_host),
+ up->ut_host);
+ putchar('\n');
+ }
+#endif
+ return head;
+}
+
+int
+ac(FILE *fp)
+{
+ struct utmp_list *lp, *head = NULL;
+ struct utmp usr;
+ struct tm *ltm;
+ time_t prev_secs, secs, ut_timecopy;
+ int day, rfound, tchanged, tskipped;
+
+ day = -1;
+ prev_secs = 1; /* Minimum acceptable date == 1970 */
+ rfound = tchanged = tskipped = 0;
+ secs = 0;
+ while (fread((char *)&usr, sizeof(usr), 1, fp) == 1) {
+ rfound++;
+ /*
+ * The type of utmp.ut_time is not necessary type time_t, as
+ * it is explicitly defined as type int32_t. Copy the value
+ * for platforms where sizeof(time_t) != size(int32_t).
+ */
+ ut_timecopy = _time32_to_time(usr.ut_time);
+ /*
+ * With sparc64 using 64-bit time_t's, there is some system
+ * routine which sets ut_time==0 (the high-order word of a
+ * 64-bit time) instead of a 32-bit time value. For those
+ * wtmp files, it is "more-accurate" to substitute the most-
+ * recent time found, instead of throwing away the entire
+ * record. While it is still just a guess, it is a better
+ * guess than throwing away a log-off record and therefore
+ * counting a session as if it continued to the end of the
+ * month, or the next system-reboot.
+ */
+ if (ut_timecopy == 0 && prev_secs > 1) {
+#ifdef DEBUG
+ if (Debug)
+ printf("%s - date changed to: %s",
+ debug_pfx(&usr, &usr), ctime(&prev_secs));
+#endif
+ tchanged++;
+ usr.ut_time = ut_timecopy = prev_secs;
+ }
+ /*
+ * Skip records where the time goes backwards.
+ */
+ if (ut_timecopy < prev_secs) {
+#ifdef DEBUG
+ if (Debug)
+ printf("%s - bad date, record skipped\n",
+ debug_pfx(&usr, &usr));
+#endif
+ tskipped++;
+ continue; /* Skip this invalid record. */
+ }
+ prev_secs = ut_timecopy;
+
+ if (!FirstTime)
+ FirstTime = ut_timecopy;
+ if (Flags & AC_D) {
+ ltm = localtime(&ut_timecopy);
+ if (day >= 0 && day != ltm->tm_yday) {
+ day = ltm->tm_yday;
+ /*
+ * print yesterday's total
+ */
+ secs = ut_timecopy;
+ secs -= ltm->tm_sec;
+ secs -= 60 * ltm->tm_min;
+ secs -= 3600 * ltm->tm_hour;
+ show_today(Users, head, secs);
+ } else
+ day = ltm->tm_yday;
+ }
+ switch(*usr.ut_line) {
+ case '|':
+ secs = ut_timecopy;
+ break;
+ case '{':
+ secs -= ut_timecopy;
+ /*
+ * adjust time for those logged in
+ */
+ for (lp = head; lp != NULL; lp = lp->next)
+ lp->usr.ut_time -= secs;
+ break;
+ case '~': /* reboot or shutdown */
+ head = log_out(head, &usr);
+ FirstTime = ut_timecopy; /* shouldn't be needed */
+ break;
+ default:
+ /*
+ * if they came in on tty[p-sP-S]*, then it is only
+ * a login session if the ut_host field is non-empty
+ */
+ if (*usr.ut_name) {
+ if (strncmp(usr.ut_line, "tty", 3) == 0 ||
+ strchr("pqrsPQRS", usr.ut_line[3]) != 0 ||
+ *usr.ut_host != '\0')
+ head = log_in(head, &usr);
+#ifdef DEBUG
+ else if (Debug > 1)
+ /* Things such as 'screen' sessions. */
+ printf("%s - record ignored\n",
+ debug_pfx(&usr, &usr));
+#endif
+ } else
+ head = log_out(head, &usr);
+ break;
+ }
+ }
+ (void)fclose(fp);
+ if (!(Flags & AC_W))
+ usr.ut_time = time((time_t *)0);
+ (void)strcpy(usr.ut_line, "~");
+
+ if (Flags & AC_D) {
+ ut_timecopy = _time32_to_time(usr.ut_time);
+ ltm = localtime(&ut_timecopy);
+ if (day >= 0 && day != ltm->tm_yday) {
+ /*
+ * print yesterday's total
+ */
+ secs = ut_timecopy;
+ secs -= ltm->tm_sec;
+ secs -= 60 * ltm->tm_min;
+ secs -= 3600 * ltm->tm_hour;
+ show_today(Users, head, secs);
+ }
+ }
+ /*
+ * anyone still logged in gets time up to now
+ */
+ head = log_out(head, &usr);
+
+ if (Flags & AC_D)
+ show_today(Users, head, time((time_t *)0));
+ else {
+ if (Flags & AC_P)
+ show_users(Users);
+ show("total", Total);
+ }
+
+ if (tskipped > 0)
+ printf("(Skipped %d of %d records due to invalid time values)\n",
+ tskipped, rfound);
+ if (tchanged > 0)
+ printf("(Changed %d of %d records to have a more likely time value)\n",
+ tchanged, rfound);
+
+ return 0;
+}
+
+void
+usage(void)
+{
+ (void)fprintf(stderr,
+#ifdef CONSOLE_TTY
+ "ac [-dp] [-c console] [-t tty] [-w wtmp] [users ...]\n");
+#else
+ "ac [-dp] [-t tty] [-w wtmp] [users ...]\n");
+#endif
+ exit(1);
+}
diff --git a/usr.sbin/accton/Makefile b/usr.sbin/accton/Makefile
new file mode 100644
index 0000000..7ea45a7
--- /dev/null
+++ b/usr.sbin/accton/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= accton
+MAN= accton.8
+
+WARNS?= 4
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/accton/accton.8 b/usr.sbin/accton/accton.8
new file mode 100644
index 0000000..73c7d29
--- /dev/null
+++ b/usr.sbin/accton/accton.8
@@ -0,0 +1,42 @@
+.\" $FreeBSD$
+.\"
+.Dd May 21, 1993
+.Dt ACCTON 8
+.Os
+.Sh NAME
+.Nm accton
+.Nd enable/disable system accounting
+.Sh SYNOPSIS
+.Nm
+.Op Ar acctfile
+.Sh DESCRIPTION
+The
+.Nm
+utility is used
+for switching system accounting on or off.
+If called with the argument
+.Ar acctfile ,
+system accounting is enabled.
+The
+.Ar acctfile
+specified must exist prior to starting system accounting, or
+.Nm
+will return an error.
+A record of every process that is started by the
+.Xr execve 2
+system call and then later exits the system is stored in
+.Ar acctfile .
+The
+.Xr sa 8
+command may be used to examine the accounting records.
+If no arguments are given, system accounting is disabled.
+.Sh FILES
+.Bl -tag -width /var/account/acct
+.It Pa /var/account/acct
+default accounting file
+.El
+.Sh SEE ALSO
+.Xr lastcomm 1 ,
+.Xr acct 2 ,
+.Xr acct 5 ,
+.Xr sa 8
diff --git a/usr.sbin/accton/accton.c b/usr.sbin/accton/accton.c
new file mode 100644
index 0000000..c1bb8b6
--- /dev/null
+++ b/usr.sbin/accton/accton.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)accton.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ int ch;
+
+ while ((ch = getopt(argc, argv, "")) != -1)
+ switch(ch) {
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ switch(argc) {
+ case 0:
+ if (acct(NULL))
+ err(1, NULL);
+ break;
+ case 1:
+ if (acct(*argv))
+ err(1, "%s", *argv);
+ break;
+ default:
+ usage();
+ }
+ exit(0);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "usage: accton [file]\n");
+ exit(1);
+}
diff --git a/usr.sbin/acpi/Makefile b/usr.sbin/acpi/Makefile
new file mode 100644
index 0000000..8190bc7
--- /dev/null
+++ b/usr.sbin/acpi/Makefile
@@ -0,0 +1,7 @@
+# Makefile for acpi tools
+# $Id: Makefile,v 1.1 2000/07/14 18:16:22 iwasaki Exp $
+# $FreeBSD$
+
+SUBDIR= acpiconf acpidb acpidump iasl
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/acpi/Makefile.inc b/usr.sbin/acpi/Makefile.inc
new file mode 100644
index 0000000..3237afb
--- /dev/null
+++ b/usr.sbin/acpi/Makefile.inc
@@ -0,0 +1,11 @@
+# $Id: Makefile.inc,v 1.1 2000/07/14 18:16:22 iwasaki Exp $
+# $FreeBSD$
+
+ACPICA_DIR= ${.CURDIR}/../../../sys/contrib/dev/acpica
+CFLAGS+= -I${.CURDIR}/../../../sys
+
+.if exists(${.CURDIR}/../../Makefile.inc)
+.include "${.CURDIR}/../../Makefile.inc"
+.endif
+
+.PATH: ${ACPICA_DIR} ${ACPICA_DIR}/compiler ${ACPICA_DIR}/common
diff --git a/usr.sbin/acpi/acpiconf/Makefile b/usr.sbin/acpi/acpiconf/Makefile
new file mode 100644
index 0000000..bb0df4b
--- /dev/null
+++ b/usr.sbin/acpi/acpiconf/Makefile
@@ -0,0 +1,8 @@
+# $Id: Makefile,v 1.2 2000/07/14 18:16:25 iwasaki Exp $
+# $FreeBSD$
+
+PROG= acpiconf
+MAN= acpiconf.8
+WARNS?= 6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/acpi/acpiconf/acpiconf.8 b/usr.sbin/acpi/acpiconf/acpiconf.8
new file mode 100644
index 0000000..05d3a5d
--- /dev/null
+++ b/usr.sbin/acpi/acpiconf/acpiconf.8
@@ -0,0 +1,92 @@
+.\"-
+.\" Copyright (c) 2000 Dag-Erling Coïdan Smørgrav
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer
+.\" in this position and unchanged.
+.\" 2. Redistributions in binary form must 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 August 16, 2004
+.Dt ACPICONF 8
+.Os
+.Sh NAME
+.Nm acpiconf
+.Nd control ACPI power management
+.Sh SYNOPSIS
+.Nm
+.Op Fl h
+.Op Fl i Ar batt
+.Op Fl s Ar type
+.Sh DESCRIPTION
+The
+.Nm
+utility allows the user control of the ACPI power management
+functions.
+The following command-line options are recognized:
+.Bl -tag -width ".Fl s Ar type"
+.It Fl h
+Displays a summary of available options.
+.It Fl i Ar batt
+Get design information about the specified battery.
+.It Fl s Ar type
+Enters the specified sleep mode.
+Recognized types are
+.Cm 1
+(only the CPU clock is stopped),
+.Cm 2
+(not implemented on most systems but similar to S1),
+.Cm 3
+(the CPU context is lost and memory context is preserved),
+.Cm 4
+(the CPU context is lost and memory context is stored to disk)
+and
+.Cm 5
+(soft off).
+Sleep states may also be given as S1, S2, etc.
+The supported states depend on BIOS implementation, including ACPI
+byte code (AML).
+If the
+.Pa /etc/rc.suspend
+and
+.Pa /etc/rc.resume
+scripts are executable, they will be run before and after entering
+the given sleep state.
+.El
+.Sh SEE ALSO
+.Xr acpi 4 ,
+.Xr acpidump 8 ,
+.Xr apm 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 5.0 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility was written by
+.An Mitsuru Iwasaki Aq iwasaki@FreeBSD.org .
+This manual page was written by
+.An Dag-Erling Sm\(/orgrav Aq des@FreeBSD.org .
diff --git a/usr.sbin/acpi/acpiconf/acpiconf.c b/usr.sbin/acpi/acpiconf/acpiconf.c
new file mode 100644
index 0000000..0797892
--- /dev/null
+++ b/usr.sbin/acpi/acpiconf/acpiconf.c
@@ -0,0 +1,227 @@
+/*-
+ * Copyright (c) 1999 Mitsuru IWASAKI <iwasaki@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: acpiconf.c,v 1.5 2000/08/08 14:12:19 iwasaki Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include <dev/acpica/acpiio.h>
+#include <contrib/dev/acpica/acpi.h>
+
+#define ACPIDEV "/dev/acpi"
+
+static int acpifd;
+
+static void
+acpi_init(void)
+{
+ acpifd = open(ACPIDEV, O_RDWR);
+ if (acpifd == -1)
+ acpifd = open(ACPIDEV, O_RDONLY);
+ if (acpifd == -1)
+ err(EX_OSFILE, ACPIDEV);
+}
+
+/* Prepare to sleep and then wait for the signal that sleeping can occur. */
+static void
+acpi_sleep(int sleep_type)
+{
+ int ret;
+
+ /* Notify OS that we want to sleep. devd(8) gets this notify. */
+ ret = ioctl(acpifd, ACPIIO_REQSLPSTATE, &sleep_type);
+ if (ret != 0)
+ err(EX_IOERR, "request sleep type (%d) failed", sleep_type);
+}
+
+/* Ack or abort a pending suspend request. */
+static void
+acpi_sleep_ack(int err_val)
+{
+ int ret;
+
+ ret = ioctl(acpifd, ACPIIO_ACKSLPSTATE, &err_val);
+ if (ret != 0)
+ err(EX_IOERR, "ack sleep type failed");
+}
+
+/* should be a acpi define, but doesn't appear to be */
+#define UNKNOWN_CAP 0xffffffff
+#define UNKNOWN_VOLTAGE 0xffffffff
+
+static int
+acpi_battinfo(int num)
+{
+ union acpi_battery_ioctl_arg battio;
+ const char *pwr_units;
+ int hours, min;
+
+ if (num < 0 || num > 64)
+ err(EX_USAGE, "invalid battery %d", num);
+
+ /* Print battery design information. */
+ battio.unit = num;
+ if (ioctl(acpifd, ACPIIO_BATT_GET_BIF, &battio) == -1)
+ err(EX_IOERR, "get battery info (%d) failed", num);
+ if (battio.bif.units == 0)
+ pwr_units = "mW";
+ else
+ pwr_units = "mA";
+
+ if (battio.bif.dcap == UNKNOWN_CAP)
+ printf("Design capacity:\tunknown\n");
+ else
+ printf("Design capacity:\t%d %sh\n", battio.bif.dcap,
+ pwr_units);
+ if (battio.bif.lfcap == UNKNOWN_CAP)
+ printf("Last full capacity:\tunknown\n");
+ else
+ printf("Last full capacity:\t%d %sh\n", battio.bif.lfcap,
+ pwr_units);
+ printf("Technology:\t\t%s\n", battio.bif.btech == 0 ?
+ "primary (non-rechargeable)" : "secondary (rechargeable)");
+ if (battio.bif.dvol == UNKNOWN_CAP)
+ printf("Design voltage:\t\tunknown\n");
+ else
+ printf("Design voltage:\t\t%d mV\n", battio.bif.dvol);
+ printf("Capacity (warn):\t%d %sh\n", battio.bif.wcap, pwr_units);
+ printf("Capacity (low):\t\t%d %sh\n", battio.bif.lcap, pwr_units);
+ printf("Low/warn granularity:\t%d %sh\n", battio.bif.gra1, pwr_units);
+ printf("Warn/full granularity:\t%d %sh\n", battio.bif.gra2, pwr_units);
+ printf("Model number:\t\t%s\n", battio.bif.model);
+ printf("Serial number:\t\t%s\n", battio.bif.serial);
+ printf("Type:\t\t\t%s\n", battio.bif.type);
+ printf("OEM info:\t\t%s\n", battio.bif.oeminfo);
+
+ /* Print current battery state information. */
+ battio.unit = num;
+ if (ioctl(acpifd, ACPIIO_BATT_GET_BATTINFO, &battio) == -1)
+ err(EX_IOERR, "get battery user info (%d) failed", num);
+ if (battio.battinfo.state != ACPI_BATT_STAT_NOT_PRESENT) {
+ printf("State:\t\t\t");
+ if (battio.battinfo.state == 0)
+ printf("high ");
+ if (battio.battinfo.state & ACPI_BATT_STAT_CRITICAL)
+ printf("critical ");
+ if (battio.battinfo.state & ACPI_BATT_STAT_DISCHARG)
+ printf("discharging ");
+ if (battio.battinfo.state & ACPI_BATT_STAT_CHARGING)
+ printf("charging ");
+ printf("\n");
+ if (battio.battinfo.cap == -1)
+ printf("Remaining capacity:\tunknown\n");
+ else
+ printf("Remaining capacity:\t%d%%\n",
+ battio.battinfo.cap);
+ if (battio.battinfo.min == -1)
+ printf("Remaining time:\t\tunknown\n");
+ else {
+ hours = battio.battinfo.min / 60;
+ min = battio.battinfo.min % 60;
+ printf("Remaining time:\t\t%d:%02d\n", hours, min);
+ }
+ if (battio.battinfo.rate == -1)
+ printf("Present rate:\t\tunknown\n");
+ else
+ printf("Present rate:\t\t%d %s\n",
+ battio.battinfo.rate, pwr_units);
+ } else
+ printf("State:\t\t\tnot present\n");
+
+ /* Print battery voltage information. */
+ battio.unit = num;
+ if (ioctl(acpifd, ACPIIO_BATT_GET_BST, &battio) == -1)
+ err(EX_IOERR, "get battery status (%d) failed", num);
+ if (battio.bst.state != ACPI_BATT_STAT_NOT_PRESENT) {
+ if (battio.bst.volt == UNKNOWN_VOLTAGE)
+ printf("Voltage:\t\tunknown\n");
+ else
+ printf("Voltage:\t\t%d mV\n", battio.bst.volt);
+ }
+
+ return (0);
+}
+
+static void
+usage(const char* prog)
+{
+ printf("usage: %s [-h] [-i batt] [-k ack] [-s 1-4]\n", prog);
+ exit(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char c, *prog;
+ int sleep_type;
+
+ prog = argv[0];
+ if (argc < 2)
+ usage(prog);
+ /* NOTREACHED */
+
+ sleep_type = -1;
+ acpi_init();
+ while ((c = getopt(argc, argv, "hi:k:s:")) != -1) {
+ switch (c) {
+ case 'i':
+ acpi_battinfo(atoi(optarg));
+ break;
+ case 'k':
+ acpi_sleep_ack(atoi(optarg));
+ break;
+ case 's':
+ if (optarg[0] == 'S')
+ sleep_type = optarg[1] - '0';
+ else
+ sleep_type = optarg[0] - '0';
+ if (sleep_type < 1 || sleep_type > 4)
+ errx(EX_USAGE, "invalid sleep type (%d)",
+ sleep_type);
+ break;
+ case 'h':
+ default:
+ usage(prog);
+ /* NOTREACHED */
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (sleep_type != -1)
+ acpi_sleep(sleep_type);
+
+ close(acpifd);
+ exit (0);
+}
diff --git a/usr.sbin/acpi/acpidb/Makefile b/usr.sbin/acpi/acpidb/Makefile
new file mode 100644
index 0000000..b658077
--- /dev/null
+++ b/usr.sbin/acpi/acpidb/Makefile
@@ -0,0 +1,64 @@
+# $FreeBSD$
+
+PROG= acpidb
+SRCS= acpidb.c
+SRCS+= osunixxf.c
+
+# debugger
+SRCS+= dbcmds.c dbdisply.c dbexec.c dbfileio.c dbhistry.c \
+ dbinput.c dbstats.c dbutils.c dbxface.c
+
+# disassembler
+SRCS+= dmbuffer.c dmnames.c dmobject.c dmopcode.c dmresrc.c \
+ dmresrcl.c dmresrcs.c dmutils.c dmwalk.c
+
+# events
+SRCS+= evevent.c evgpe.c evgpeblk.c evmisc.c evregion.c \
+ evrgnini.c evsci.c evxface.c evxfevnt.c evxfregn.c
+
+# hardware
+SRCS+= hwacpi.c hwgpe.c hwregs.c hwsleep.c
+
+# interpreter/dispatcher
+SRCS+= dsfield.c dsinit.c dsmethod.c dsmthdat.c dsobject.c \
+ dsopcode.c dsutils.c dswexec.c dswload.c dswscope.c \
+ dswstate.c
+
+# interpreter/executer
+SRCS+= exconfig.c exconvrt.c excreate.c exdump.c exfield.c \
+ exfldio.c exmisc.c exmutex.c exnames.c exoparg1.c \
+ exoparg2.c exoparg3.c exoparg6.c exprep.c exregion.c \
+ exresnte.c exresolv.c exresop.c exstore.c exstoren.c \
+ exstorob.c exsystem.c exutils.c
+
+# interpreter/parser
+SRCS+= psargs.c psloop.c psopcode.c psparse.c psscope.c \
+ pstree.c psutils.c pswalk.c psxface.c
+
+# namespace
+SRCS+= nsaccess.c nsalloc.c nsdump.c nseval.c nsinit.c \
+ nsload.c nsnames.c nsobject.c nsparse.c nssearch.c \
+ nsutils.c nswalk.c nsxfeval.c nsxfname.c nsxfobj.c
+
+# resources
+SRCS+= rsaddr.c rscalc.c rscreate.c rsdump.c rsinfo.c \
+ rsio.c rsirq.c rslist.c rsmemory.c rsmisc.c \
+ rsutils.c rsxface.c
+
+# tables
+SRCS+= tbfadt.c tbfind.c tbinstal.c tbutils.c tbxface.c \
+ tbxfroot.c
+
+# utilities
+SRCS+= utalloc.c utcache.c utcopy.c utdebug.c utdelete.c \
+ uteval.c utglobal.c utinit.c utmath.c utmisc.c \
+ utmutex.c utobject.c utresrc.c utstate.c uttrack.c \
+ utxface.c
+
+MAN= acpidb.8
+WARNS?= 2
+
+CFLAGS+= -DACPI_EXEC_APP
+CFLAGS+=-fno-strict-aliasing
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/acpi/acpidb/acpidb.8 b/usr.sbin/acpi/acpidb/acpidb.8
new file mode 100644
index 0000000..4ed97a6
--- /dev/null
+++ b/usr.sbin/acpi/acpidb/acpidb.8
@@ -0,0 +1,167 @@
+.\"-
+.\" Copyright (c) 2003 Nate Lawson
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer
+.\" in this position and unchanged.
+.\" 2. Redistributions in binary form must 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 August 7, 2003
+.Dt ACPIDB 8
+.Os
+.Sh NAME
+.Nm acpidb
+.Nd ACPI DSDT debugger
+.Sh SYNOPSIS
+.Nm
+.Ar input-file
+.Sh DESCRIPTION
+The
+.Nm
+utility is a debugger for the ACPI DSDT.
+It can parse and execute various
+AML methods and display the result.
+.Sh COMMANDS
+.Ss General-Purpose Commands
+.Bl -tag -width indent
+.It Ic Allocations
+Display list of current memory allocations
+.It Ic Dump Ar Address | Namepath Op Cm Byte | Word | Dword | Qword
+Display ACPI objects or memory
+.It Ic EnableAcpi
+Enable ACPI (hardware) mode
+.It Ic Help
+Show various help screens
+.It Ic History
+Display command history buffer
+.It Ic Level Ar DebugLevel Op Cm console
+Get/Set debug level for file or console
+.It Ic Locks
+Current status of internal mutexes
+.It Ic Quit No or Ic Exit
+Exit the debugger
+.It Ic Stats Op Cm Allocations | Memory | Misc | Objects | Tables
+Display namespace and memory statistics
+.It Ic Tables
+Display info about loaded ACPI tables
+.It Ic Unload Ar TableSig Op Ar Instance
+Unload an ACPI table
+.It Ic !\& Ar CommandNumber
+Execute command from history buffer
+.It Ic !!
+Execute last command again
+.El
+.Ss Namespace Access Commands
+.Bl -tag -width indent
+.It Ic Event Cm F | G Ar Value
+Generate AcpiEvent (Fixed/GPE)
+.It Ic Find Ar Name
+Find ACPI name(s) with wildcards
+.Ql ( ?\&
+is wildcard)
+.It Ic Method
+Display list of loaded control methods
+.It Ic Namespace Oo Ar Addr | Path Oc Op Ar Depth
+Display loaded namespace tree/subtree
+.It Ic Notify Ar NamePath Value
+Send a notification
+.It Ic Objects Ar ObjectType
+Display all objects of the given type
+.It Ic Owner Ar OwnerId Op Ar Depth
+Display loaded namespace by object owner
+.It Ic Prefix Op Ar NamePath
+Set or Get current execution prefix
+.It Ic References Ar Addr
+Find all references to object at addr
+.It Ic Resources
+Get and display resources
+.It Ic Terminate
+Delete namespace and all internal objects
+.It Ic Thread Ar Threads Loops NamePath
+Spawn threads to execute method(s)
+.El
+.Ss Control Method Execution Commands
+.Bl -tag -width indent
+.It Ic Arguments
+.Pq Ic Args
+Display method arguments
+.It Ic Breakpoint Ar AmlOffset
+Set an AML execution breakpoint
+.It Ic Call
+Run to next control method invocation
+.It Ic Debug Ar Namepath Op Ar Arguments
+Single Step a control method
+.It Ic Execute Ar Namepath Op Arguments
+Execute control method
+.It Ic Go
+Allow method to run to completion
+.It Ic Information
+Display info about the current method
+.It Ic Into
+Step into (not over) a method call
+.It Ic List Op OpcodeCount
+Display method ASL statements
+.It Ic Locals
+Display method local variables
+.It Ic Results
+Display method result stack
+.It Ic Set Cm A | L Ar # Value
+Set method data (Arguments/Locals)
+.It Ic Stop
+Terminate control method
+.It Ic Tree
+Display control method calling tree
+.It Ic <Enter>
+Single step next AML opcode (over calls)
+.El
+.Ss File I/O Commands
+.Bl -tag -width indent
+.It Ic Close
+Close debug output file
+.It Ic Open Ar Filename
+Open a file for debug output
+.It Ic Load Ar Filename
+Load ACPI table from a file
+.El
+.Sh SEE ALSO
+.Xr acpi 4 ,
+.Xr acpidump 8 ,
+.Xr iasl 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in the
+.Nm acpicatools
+port.
+It was imported for
+.Fx 5.2 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility was written by
+.An Mitsuru Iwasaki Aq iwasaki@FreeBSD.org
+and uses Intel ACPI-CA for the backend.
+This manual page was written by
+.An Nate Lawson .
diff --git a/usr.sbin/acpi/acpidb/acpidb.c b/usr.sbin/acpi/acpidb/acpidb.c
new file mode 100644
index 0000000..6893e54
--- /dev/null
+++ b/usr.sbin/acpi/acpidb/acpidb.c
@@ -0,0 +1,498 @@
+/*-
+ * Copyright (c) 2000-2002 Mitsuru IWASAKI <iwasaki@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 <sys/param.h>
+#include <sys/queue.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/stdint.h>
+#include <sys/types.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <contrib/dev/acpica/acpi.h>
+#include <contrib/dev/acpica/acnamesp.h>
+#include <contrib/dev/acpica/acdebug.h>
+
+/*
+ * Dummy DSDT Table Header
+ */
+
+ACPI_TABLE_HEADER dummy_dsdt_table = {
+ "DSDT", 123, 1, 123, "OEMID", "OEMTBLID", 1, "CRID", 1
+};
+
+/*
+ * Region space I/O routines on virtual machine
+ */
+
+int aml_debug_prompt = 1;
+
+struct ACPIRegionContent {
+ TAILQ_ENTRY(ACPIRegionContent) links;
+ int regtype;
+ ACPI_PHYSICAL_ADDRESS addr;
+ UINT8 value;
+};
+
+TAILQ_HEAD(ACPIRegionContentList, ACPIRegionContent);
+struct ACPIRegionContentList RegionContentList;
+
+static int aml_simulation_initialized = 0;
+
+static void aml_simulation_init(void);
+static int aml_simulate_regcontent_add(int regtype,
+ ACPI_PHYSICAL_ADDRESS addr,
+ UINT8 value);
+static int aml_simulate_regcontent_read(int regtype,
+ ACPI_PHYSICAL_ADDRESS addr,
+ UINT8 *valuep);
+static int aml_simulate_regcontent_write(int regtype,
+ ACPI_PHYSICAL_ADDRESS addr,
+ UINT8 *valuep);
+static ACPI_INTEGER aml_simulate_prompt(char *msg, ACPI_INTEGER def_val);
+static void aml_simulation_regload(const char *dumpfile);
+static void aml_simulation_regdump(const char *dumpfile);
+
+/* Stubs to simplify linkage to the ACPI CA core subsystem. */
+ACPI_STATUS
+AeLocalGetRootPointer(void)
+{
+ return AE_ERROR;
+}
+
+static void
+aml_simulation_init(void)
+{
+
+ aml_simulation_initialized = 1;
+ TAILQ_INIT(&RegionContentList);
+ aml_simulation_regload("region.ini");
+}
+
+static int
+aml_simulate_regcontent_add(int regtype, ACPI_PHYSICAL_ADDRESS addr, UINT8 value)
+{
+ struct ACPIRegionContent *rc;
+
+ rc = malloc(sizeof(struct ACPIRegionContent));
+ if (rc == NULL) {
+ return (-1); /* malloc fail */
+ }
+ rc->regtype = regtype;
+ rc->addr = addr;
+ rc->value = value;
+
+ TAILQ_INSERT_TAIL(&RegionContentList, rc, links);
+ return (0);
+}
+
+static int
+aml_simulate_regcontent_read(int regtype, ACPI_PHYSICAL_ADDRESS addr, UINT8 *valuep)
+{
+ struct ACPIRegionContent *rc;
+
+ if (!aml_simulation_initialized) {
+ aml_simulation_init();
+ }
+ TAILQ_FOREACH(rc, &RegionContentList, links) {
+ if (rc->regtype == regtype && rc->addr == addr) {
+ *valuep = rc->value;
+ return (1); /* found */
+ }
+ }
+
+ *valuep = 0;
+ return (aml_simulate_regcontent_add(regtype, addr, *valuep));
+}
+
+static int
+aml_simulate_regcontent_write(int regtype, ACPI_PHYSICAL_ADDRESS addr, UINT8 *valuep)
+{
+ struct ACPIRegionContent *rc;
+
+ if (!aml_simulation_initialized) {
+ aml_simulation_init();
+ }
+ TAILQ_FOREACH(rc, &RegionContentList, links) {
+ if (rc->regtype == regtype && rc->addr == addr) {
+ rc->value = *valuep;
+ return (1); /* exists */
+ }
+ }
+
+ return (aml_simulate_regcontent_add(regtype, addr, *valuep));
+}
+
+static ACPI_INTEGER
+aml_simulate_prompt(char *msg, ACPI_INTEGER def_val)
+{
+ char buf[16], *ep;
+ ACPI_INTEGER val;
+
+ val = def_val;
+ printf("DEBUG");
+ if (msg != NULL) {
+ printf("%s", msg);
+ }
+ printf("(default: 0x%jx ", val);
+ printf(" / %ju) >>", val);
+ fflush(stdout);
+
+ bzero(buf, sizeof buf);
+ while (1) {
+ if (read(0, buf, sizeof buf) == 0) {
+ continue;
+ }
+ if (buf[0] == '\n') {
+ break; /* use default value */
+ }
+ if (buf[0] == '0' && buf[1] == 'x') {
+ val = strtoq(buf, &ep, 16);
+ } else {
+ val = strtoq(buf, &ep, 10);
+ }
+ break;
+ }
+ return (val);
+}
+
+static void
+aml_simulation_regload(const char *dumpfile)
+{
+ char buf[256], *np, *ep;
+ struct ACPIRegionContent rc;
+ FILE *fp;
+
+ if (!aml_simulation_initialized) {
+ return;
+ }
+
+ if ((fp = fopen(dumpfile, "r")) == NULL) {
+ return;
+ }
+
+ while (fgets(buf, sizeof buf, fp) != NULL) {
+ np = buf;
+ /* reading region type */
+ rc.regtype = strtoq(np, &ep, 10);
+ if (np == ep) {
+ continue;
+ }
+ np = ep;
+
+ /* reading address */
+ rc.addr = strtoq(np, &ep, 16);
+ if (np == ep) {
+ continue;
+ }
+ np = ep;
+
+ /* reading value */
+ rc.value = strtoq(np, &ep, 16);
+ if (np == ep) {
+ continue;
+ }
+ aml_simulate_regcontent_write(rc.regtype, rc.addr, &rc.value);
+ }
+
+ fclose(fp);
+}
+
+static void
+aml_simulation_regdump(const char *dumpfile)
+{
+ struct ACPIRegionContent *rc;
+ FILE *fp;
+
+ if (!aml_simulation_initialized) {
+ return;
+ }
+ if ((fp = fopen(dumpfile, "w")) == NULL) {
+ warn("%s", dumpfile);
+ return;
+ }
+ while (!TAILQ_EMPTY(&RegionContentList)) {
+ rc = TAILQ_FIRST(&RegionContentList);
+ fprintf(fp, "%d 0x%jx 0x%x\n",
+ rc->regtype, (uintmax_t)rc->addr, rc->value);
+ TAILQ_REMOVE(&RegionContentList, rc, links);
+ free(rc);
+ }
+
+ fclose(fp);
+ TAILQ_INIT(&RegionContentList);
+}
+
+/*
+ * Space handlers on virtual machine
+ */
+
+static ACPI_STATUS
+aml_vm_space_handler(
+ UINT32 SpaceID,
+ UINT32 Function,
+ ACPI_PHYSICAL_ADDRESS Address,
+ UINT32 BitWidth,
+ ACPI_INTEGER *Value,
+ int Prompt)
+{
+ int state;
+ UINT8 val;
+ ACPI_INTEGER value, i;
+ char msg[256];
+ static const char *space_names[] = {
+ "SYSTEM_MEMORY", "SYSTEM_IO", "PCI_CONFIG",
+ "EC", "SMBUS", "CMOS", "PCI_BAR_TARGET"};
+
+ switch (Function) {
+ case ACPI_READ:
+ value = 0;
+ for (i = 0; (i * 8) < BitWidth; i++) {
+ state = aml_simulate_regcontent_read(SpaceID,
+ Address + i, &val);
+ if (state == -1) {
+ return (AE_NO_MEMORY);
+ }
+ value |= val << (i * 8);
+ }
+ *Value = value;
+ if (Prompt) {
+ sprintf(msg, "[read (%s, %2d, 0x%jx)]",
+ space_names[SpaceID], BitWidth,
+ (uintmax_t)Address);
+ *Value = aml_simulate_prompt(msg, value);
+ if (*Value != value) {
+ return(aml_vm_space_handler(SpaceID,
+ ACPI_WRITE,
+ Address, BitWidth, Value, 0));
+ }
+ }
+ break;
+
+ case ACPI_WRITE:
+ value = *Value;
+ if (Prompt) {
+ sprintf(msg, "[write(%s, %2d, 0x%jx)]",
+ space_names[SpaceID], BitWidth,
+ (uintmax_t)Address);
+ value = aml_simulate_prompt(msg, *Value);
+ }
+ *Value = value;
+ for (i = 0; (i * 8) < BitWidth; i++) {
+ val = value & 0xff;
+ state = aml_simulate_regcontent_write(SpaceID,
+ Address + i, &val);
+ if (state == -1) {
+ return (AE_NO_MEMORY);
+ }
+ value = value >> 8;
+ }
+ }
+
+ return (AE_OK);
+}
+
+#define DECLARE_VM_SPACE_HANDLER(name, id); \
+static ACPI_STATUS \
+aml_vm_space_handler_##name ( \
+ UINT32 Function, \
+ ACPI_PHYSICAL_ADDRESS Address, \
+ UINT32 BitWidth, \
+ ACPI_INTEGER *Value) \
+{ \
+ return (aml_vm_space_handler(id, Function, Address, \
+ BitWidth, Value, aml_debug_prompt)); \
+}
+
+DECLARE_VM_SPACE_HANDLER(system_memory, ACPI_ADR_SPACE_SYSTEM_MEMORY);
+DECLARE_VM_SPACE_HANDLER(system_io, ACPI_ADR_SPACE_SYSTEM_IO);
+DECLARE_VM_SPACE_HANDLER(pci_config, ACPI_ADR_SPACE_PCI_CONFIG);
+DECLARE_VM_SPACE_HANDLER(ec, ACPI_ADR_SPACE_EC);
+DECLARE_VM_SPACE_HANDLER(smbus, ACPI_ADR_SPACE_SMBUS);
+DECLARE_VM_SPACE_HANDLER(cmos, ACPI_ADR_SPACE_CMOS);
+DECLARE_VM_SPACE_HANDLER(pci_bar_target,ACPI_ADR_SPACE_PCI_BAR_TARGET);
+
+/*
+ * Load DSDT data file and invoke debugger
+ */
+
+static int
+load_dsdt(const char *dsdtfile)
+{
+ char filetmp[PATH_MAX];
+ u_int8_t *code;
+ struct stat sb;
+ int fd, fd2;
+ int error;
+
+ fd = open(dsdtfile, O_RDONLY, 0);
+ if (fd == -1) {
+ perror("open");
+ return (-1);
+ }
+ if (fstat(fd, &sb) == -1) {
+ perror("fstat");
+ return (-1);
+ }
+ code = mmap(NULL, (size_t)sb.st_size, PROT_READ, MAP_PRIVATE, fd, (off_t)0);
+ if (code == NULL) {
+ perror("mmap");
+ return (-1);
+ }
+ if ((error = AcpiInitializeSubsystem()) != AE_OK) {
+ return (-1);
+ }
+
+ /*
+ * make sure DSDT data contains table header or not.
+ */
+ if (strncmp((char *)code, "DSDT", 4) == 0) {
+ strncpy(filetmp, dsdtfile, sizeof(filetmp));
+ } else {
+ mode_t mode = (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ dummy_dsdt_table.Length = sizeof(ACPI_TABLE_HEADER) + sb.st_size;
+ snprintf(filetmp, sizeof(filetmp), "%s.tmp", dsdtfile);
+ fd2 = open(filetmp, O_WRONLY | O_CREAT | O_TRUNC, mode);
+ if (fd2 == -1) {
+ perror("open");
+ return (-1);
+ }
+ write(fd2, &dummy_dsdt_table, sizeof(ACPI_TABLE_HEADER));
+
+ write(fd2, code, sb.st_size);
+ close(fd2);
+ }
+
+ /*
+ * Install the virtual machine version of address space handlers.
+ */
+ if ((error = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
+ ACPI_ADR_SPACE_SYSTEM_MEMORY,
+ (ACPI_ADR_SPACE_HANDLER)aml_vm_space_handler_system_memory,
+ NULL, NULL)) != AE_OK) {
+ fprintf(stderr, "could not initialise SystemMemory handler: %d\n", error);
+ return (-1);
+ }
+ if ((error = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
+ ACPI_ADR_SPACE_SYSTEM_IO,
+ (ACPI_ADR_SPACE_HANDLER)aml_vm_space_handler_system_io,
+ NULL, NULL)) != AE_OK) {
+ fprintf(stderr, "could not initialise SystemIO handler: %d\n", error);
+ return (-1);
+ }
+ if ((error = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
+ ACPI_ADR_SPACE_PCI_CONFIG,
+ (ACPI_ADR_SPACE_HANDLER)aml_vm_space_handler_pci_config,
+ NULL, NULL)) != AE_OK) {
+ fprintf(stderr, "could not initialise PciConfig handler: %d\n", error);
+ return (-1);
+ }
+ if ((error = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
+ ACPI_ADR_SPACE_EC,
+ (ACPI_ADR_SPACE_HANDLER)aml_vm_space_handler_ec,
+ NULL, NULL)) != AE_OK) {
+ fprintf(stderr, "could not initialise EC handler: %d\n", error);
+ return (-1);
+ }
+ if ((error = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
+ ACPI_ADR_SPACE_SMBUS,
+ (ACPI_ADR_SPACE_HANDLER)aml_vm_space_handler_smbus,
+ NULL, NULL)) != AE_OK) {
+ fprintf(stderr, "could not initialise SMBUS handler: %d\n", error);
+ return (-1);
+ }
+ if ((error = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
+ ACPI_ADR_SPACE_CMOS,
+ (ACPI_ADR_SPACE_HANDLER)aml_vm_space_handler_cmos,
+ NULL, NULL)) != AE_OK) {
+ fprintf(stderr, "could not initialise CMOS handler: %d\n", error);
+ return (-1);
+ }
+ if ((error = AcpiInstallAddressSpaceHandler(ACPI_ROOT_OBJECT,
+ ACPI_ADR_SPACE_PCI_BAR_TARGET,
+ (ACPI_ADR_SPACE_HANDLER)aml_vm_space_handler_pci_bar_target,
+ NULL, NULL)) != AE_OK) {
+ fprintf(stderr, "could not initialise PCI BAR TARGET handler: %d\n", error);
+ return (-1);
+ }
+
+ AcpiDbGetTableFromFile(filetmp, NULL);
+
+ AcpiDbInitialize();
+ AcpiGbl_DebuggerConfiguration = 0;
+ AcpiDbUserCommands(':', NULL);
+
+ if (strcmp(dsdtfile, filetmp) != 0) {
+ unlink(filetmp);
+ }
+
+ return (0);
+}
+
+static void
+usage(const char *progname)
+{
+
+ printf("usage: %s dsdt_file\n", progname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *progname;
+
+ progname = argv[0];
+
+ if (argc == 1) {
+ usage(progname);
+ }
+
+ AcpiDbgLevel = ACPI_DEBUG_DEFAULT;
+
+ /*
+ * Match kernel options for the interpreter. Global variable names
+ * can be found in acglobal.h.
+ */
+ AcpiGbl_EnableInterpreterSlack = TRUE;
+
+ aml_simulation_regload("region.ini");
+ if (load_dsdt(argv[1]) == 0) {
+ aml_simulation_regdump("region.dmp");
+ }
+
+ return (0);
+}
diff --git a/usr.sbin/acpi/acpidump/Makefile b/usr.sbin/acpi/acpidump/Makefile
new file mode 100644
index 0000000..21a4140
--- /dev/null
+++ b/usr.sbin/acpi/acpidump/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= acpidump
+MAN= acpidump.8
+SRCS= acpi.c acpi_user.c acpidump.c
+WARNS?= 6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/acpi/acpidump/acpi.c b/usr.sbin/acpi/acpidump/acpi.c
new file mode 100644
index 0000000..fed0fb2
--- /dev/null
+++ b/usr.sbin/acpi/acpidump/acpi.c
@@ -0,0 +1,898 @@
+/*-
+ * Copyright (c) 1998 Doug Rabson
+ * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@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 <sys/param.h>
+#include <sys/endian.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <assert.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "acpidump.h"
+
+#define BEGIN_COMMENT "/*\n"
+#define END_COMMENT " */\n"
+
+static void acpi_print_string(char *s, size_t length);
+static void acpi_print_gas(struct ACPIgas *gas);
+static int acpi_get_fadt_revision(struct FADTbody *fadt);
+static void acpi_handle_fadt(struct ACPIsdt *fadt);
+static void acpi_print_cpu(u_char cpu_id);
+static void acpi_print_local_apic(u_char cpu_id, u_char apic_id,
+ u_int32_t flags);
+static void acpi_print_io_apic(u_char apic_id, u_int32_t int_base,
+ u_int64_t apic_addr);
+static void acpi_print_mps_flags(u_int16_t flags);
+static void acpi_print_intr(u_int32_t intr, u_int16_t mps_flags);
+static void acpi_print_apic(struct MADT_APIC *mp);
+static void acpi_handle_apic(struct ACPIsdt *sdp);
+static void acpi_handle_hpet(struct ACPIsdt *sdp);
+static void acpi_print_sdt(struct ACPIsdt *sdp);
+static void acpi_print_fadt(struct ACPIsdt *sdp);
+static void acpi_print_facs(struct FACSbody *facs);
+static void acpi_print_dsdt(struct ACPIsdt *dsdp);
+static struct ACPIsdt *acpi_map_sdt(vm_offset_t pa);
+static void acpi_print_rsd_ptr(struct ACPIrsdp *rp);
+static void acpi_handle_rsdt(struct ACPIsdt *rsdp);
+
+/* Size of an address. 32-bit for ACPI 1.0, 64-bit for ACPI 2.0 and up. */
+static int addr_size;
+
+static void
+acpi_print_string(char *s, size_t length)
+{
+ int c;
+
+ /* Trim trailing spaces and NULLs */
+ while (length > 0 && (s[length - 1] == ' ' || s[length - 1] == '\0'))
+ length--;
+
+ while (length--) {
+ c = *s++;
+ putchar(c);
+ }
+}
+
+static void
+acpi_print_gas(struct ACPIgas *gas)
+{
+ switch(gas->address_space_id) {
+ case ACPI_GAS_MEMORY:
+ printf("0x%08lx:%u[%u] (Memory)", (u_long)gas->address,
+ gas->bit_offset, gas->bit_width);
+ break;
+ case ACPI_GAS_IO:
+ printf("0x%02lx:%u[%u] (IO)", (u_long)gas->address,
+ gas->bit_offset, gas->bit_width);
+ break;
+ case ACPI_GAS_PCI:
+ printf("%x:%x+0x%x (PCI)", (uint16_t)(gas->address >> 32),
+ (uint16_t)((gas->address >> 16) & 0xffff),
+ (uint16_t)gas->address);
+ break;
+ /* XXX How to handle these below? */
+ case ACPI_GAS_EMBEDDED:
+ printf("0x%x:%u[%u] (EC)", (uint16_t)gas->address,
+ gas->bit_offset, gas->bit_width);
+ break;
+ case ACPI_GAS_SMBUS:
+ printf("0x%x:%u[%u] (SMBus)", (uint16_t)gas->address,
+ gas->bit_offset, gas->bit_width);
+ break;
+ case ACPI_GAS_FIXED:
+ default:
+ printf("0x%08lx (?)", (u_long)gas->address);
+ break;
+ }
+}
+
+/* The FADT revision indicates whether we use the DSDT or X_DSDT addresses. */
+static int
+acpi_get_fadt_revision(struct FADTbody *fadt)
+{
+ int fadt_revision;
+
+ /* Set the FADT revision separately from the RSDP version. */
+ if (addr_size == 8) {
+ fadt_revision = 2;
+
+ /*
+ * A few systems (e.g., IBM T23) have an RSDP that claims
+ * revision 2 but the 64 bit addresses are invalid. If
+ * revision 2 and the 32 bit address is non-zero but the
+ * 32 and 64 bit versions don't match, prefer the 32 bit
+ * version for all subsequent tables.
+ */
+ if (fadt->facs_ptr != 0 &&
+ (fadt->x_facs_ptr & 0xffffffff) != fadt->facs_ptr)
+ fadt_revision = 1;
+ } else
+ fadt_revision = 1;
+ return (fadt_revision);
+}
+
+static void
+acpi_handle_fadt(struct ACPIsdt *sdp)
+{
+ struct ACPIsdt *dsdp;
+ struct FACSbody *facs;
+ struct FADTbody *fadt;
+ int fadt_revision;
+
+ fadt = (struct FADTbody *)sdp->body;
+ acpi_print_fadt(sdp);
+
+ fadt_revision = acpi_get_fadt_revision(fadt);
+ if (fadt_revision == 1)
+ facs = (struct FACSbody *)acpi_map_sdt(fadt->facs_ptr);
+ else
+ facs = (struct FACSbody *)acpi_map_sdt(fadt->x_facs_ptr);
+ if (memcmp(facs->signature, "FACS", 4) != 0 || facs->len < 64)
+ errx(1, "FACS is corrupt");
+ acpi_print_facs(facs);
+
+ if (fadt_revision == 1)
+ dsdp = (struct ACPIsdt *)acpi_map_sdt(fadt->dsdt_ptr);
+ else
+ dsdp = (struct ACPIsdt *)acpi_map_sdt(fadt->x_dsdt_ptr);
+ if (acpi_checksum(dsdp, dsdp->len))
+ errx(1, "DSDT is corrupt");
+ acpi_print_dsdt(dsdp);
+}
+
+static void
+acpi_print_cpu(u_char cpu_id)
+{
+
+ printf("\tACPI CPU=");
+ if (cpu_id == 0xff)
+ printf("ALL\n");
+ else
+ printf("%d\n", (u_int)cpu_id);
+}
+
+static void
+acpi_print_local_apic(u_char cpu_id, u_char apic_id, u_int32_t flags)
+{
+ acpi_print_cpu(cpu_id);
+ printf("\tFlags={");
+ if (flags & ACPI_MADT_APIC_LOCAL_FLAG_ENABLED)
+ printf("ENABLED");
+ else
+ printf("DISABLED");
+ printf("}\n");
+ printf("\tAPIC ID=%d\n", (u_int)apic_id);
+}
+
+static void
+acpi_print_io_apic(u_char apic_id, u_int32_t int_base, u_int64_t apic_addr)
+{
+ printf("\tAPIC ID=%d\n", (u_int)apic_id);
+ printf("\tINT BASE=%d\n", int_base);
+ printf("\tADDR=0x%016jx\n", apic_addr);
+}
+
+static void
+acpi_print_mps_flags(u_int16_t flags)
+{
+
+ printf("\tFlags={Polarity=");
+ switch (flags & MPS_INT_FLAG_POLARITY_MASK) {
+ case MPS_INT_FLAG_POLARITY_CONFORM:
+ printf("conforming");
+ break;
+ case MPS_INT_FLAG_POLARITY_HIGH:
+ printf("active-hi");
+ break;
+ case MPS_INT_FLAG_POLARITY_LOW:
+ printf("active-lo");
+ break;
+ default:
+ printf("0x%x", flags & MPS_INT_FLAG_POLARITY_MASK);
+ break;
+ }
+ printf(", Trigger=");
+ switch (flags & MPS_INT_FLAG_TRIGGER_MASK) {
+ case MPS_INT_FLAG_TRIGGER_CONFORM:
+ printf("conforming");
+ break;
+ case MPS_INT_FLAG_TRIGGER_EDGE:
+ printf("edge");
+ break;
+ case MPS_INT_FLAG_TRIGGER_LEVEL:
+ printf("level");
+ break;
+ default:
+ printf("0x%x", (flags & MPS_INT_FLAG_TRIGGER_MASK) >> 2);
+ }
+ printf("}\n");
+}
+
+static void
+acpi_print_intr(u_int32_t intr, u_int16_t mps_flags)
+{
+
+ printf("\tINTR=%d\n", (u_int)intr);
+ acpi_print_mps_flags(mps_flags);
+}
+
+const char *apic_types[] = { "Local APIC", "IO APIC", "INT Override", "NMI",
+ "Local NMI", "Local APIC Override", "IO SAPIC",
+ "Local SAPIC", "Platform Interrupt" };
+const char *platform_int_types[] = { "PMI", "INIT",
+ "Corrected Platform Error" };
+
+static void
+acpi_print_apic(struct MADT_APIC *mp)
+{
+
+ printf("\tType=%s\n", apic_types[mp->type]);
+ switch (mp->type) {
+ case ACPI_MADT_APIC_TYPE_LOCAL_APIC:
+ acpi_print_local_apic(mp->body.local_apic.cpu_id,
+ mp->body.local_apic.apic_id, mp->body.local_apic.flags);
+ break;
+ case ACPI_MADT_APIC_TYPE_IO_APIC:
+ acpi_print_io_apic(mp->body.io_apic.apic_id,
+ mp->body.io_apic.int_base,
+ mp->body.io_apic.apic_addr);
+ break;
+ case ACPI_MADT_APIC_TYPE_INT_OVERRIDE:
+ printf("\tBUS=%d\n", (u_int)mp->body.int_override.bus);
+ printf("\tIRQ=%d\n", (u_int)mp->body.int_override.source);
+ acpi_print_intr(mp->body.int_override.intr,
+ mp->body.int_override.mps_flags);
+ break;
+ case ACPI_MADT_APIC_TYPE_NMI:
+ acpi_print_intr(mp->body.nmi.intr, mp->body.nmi.mps_flags);
+ break;
+ case ACPI_MADT_APIC_TYPE_LOCAL_NMI:
+ acpi_print_cpu(mp->body.local_nmi.cpu_id);
+ printf("\tLINT Pin=%d\n", mp->body.local_nmi.lintpin);
+ acpi_print_mps_flags(mp->body.local_nmi.mps_flags);
+ break;
+ case ACPI_MADT_APIC_TYPE_LOCAL_OVERRIDE:
+ printf("\tLocal APIC ADDR=0x%016jx\n",
+ mp->body.local_apic_override.apic_addr);
+ break;
+ case ACPI_MADT_APIC_TYPE_IO_SAPIC:
+ acpi_print_io_apic(mp->body.io_sapic.apic_id,
+ mp->body.io_sapic.int_base,
+ mp->body.io_sapic.apic_addr);
+ break;
+ case ACPI_MADT_APIC_TYPE_LOCAL_SAPIC:
+ acpi_print_local_apic(mp->body.local_sapic.cpu_id,
+ mp->body.local_sapic.apic_id, mp->body.local_sapic.flags);
+ printf("\tAPIC EID=%d\n", (u_int)mp->body.local_sapic.apic_eid);
+ break;
+ case ACPI_MADT_APIC_TYPE_INT_SRC:
+ printf("\tType=%s\n",
+ platform_int_types[mp->body.int_src.type]);
+ printf("\tCPU ID=%d\n", (u_int)mp->body.int_src.cpu_id);
+ printf("\tCPU EID=%d\n", (u_int)mp->body.int_src.cpu_id);
+ printf("\tSAPIC Vector=%d\n",
+ (u_int)mp->body.int_src.sapic_vector);
+ acpi_print_intr(mp->body.int_src.intr,
+ mp->body.int_src.mps_flags);
+ break;
+ default:
+ printf("\tUnknown type %d\n", (u_int)mp->type);
+ break;
+ }
+}
+
+static void
+acpi_handle_apic(struct ACPIsdt *sdp)
+{
+ struct MADTbody *madtp;
+ struct MADT_APIC *madt_apicp;
+
+ printf(BEGIN_COMMENT);
+ acpi_print_sdt(sdp);
+ madtp = (struct MADTbody *) sdp->body;
+ printf("\tLocal APIC ADDR=0x%08x\n", madtp->lapic_addr);
+ printf("\tFlags={");
+ if (madtp->flags & ACPI_APIC_FLAG_PCAT_COMPAT)
+ printf("PC-AT");
+ printf("}\n");
+ madt_apicp = (struct MADT_APIC *)madtp->body;
+ while (((uintptr_t)madt_apicp) - ((uintptr_t)sdp) < sdp->len) {
+ printf("\n");
+ acpi_print_apic(madt_apicp);
+ madt_apicp = (struct MADT_APIC *) ((char *)madt_apicp +
+ madt_apicp->len);
+ }
+ printf(END_COMMENT);
+}
+
+static void
+acpi_handle_hpet(struct ACPIsdt *sdp)
+{
+ struct HPETbody *hpetp;
+
+ printf(BEGIN_COMMENT);
+ acpi_print_sdt(sdp);
+ hpetp = (struct HPETbody *) sdp->body;
+ printf("\tHPET Number=%d\n", hpetp->hpet_number);
+ printf("\tADDR=");
+ acpi_print_gas(&hpetp->genaddr);
+ printf("\tHW Rev=0x%x\n", hpetp->block_hwrev);
+ printf("\tComparitors=%d\n", hpetp->block_comparitors);
+ printf("\tCounter Size=%d\n", hpetp->block_counter_size);
+ printf("\tLegacy IRQ routing capable={");
+ if (hpetp->block_legacy_capable)
+ printf("TRUE}\n");
+ else
+ printf("FALSE}\n");
+ printf("\tPCI Vendor ID=0x%04x\n", hpetp->block_pcivendor);
+ printf("\tMinimal Tick=%d\n", hpetp->clock_tick);
+ printf(END_COMMENT);
+}
+
+static void
+acpi_handle_ecdt(struct ACPIsdt *sdp)
+{
+ struct ECDTbody *ecdt;
+
+ printf(BEGIN_COMMENT);
+ acpi_print_sdt(sdp);
+ ecdt = (struct ECDTbody *) sdp->body;
+ printf("\tEC_CONTROL=");
+ acpi_print_gas(&ecdt->ec_control);
+ printf("\n\tEC_DATA=");
+ acpi_print_gas(&ecdt->ec_data);
+ printf("\n\tUID=%#x, ", ecdt->uid);
+ printf("GPE_BIT=%#x\n", ecdt->gpe_bit);
+ printf("\tEC_ID=%s\n", ecdt->ec_id);
+ printf(END_COMMENT);
+}
+
+static void
+acpi_handle_mcfg(struct ACPIsdt *sdp)
+{
+ struct MCFGbody *mcfg;
+ u_int i, e;
+
+ printf(BEGIN_COMMENT);
+ acpi_print_sdt(sdp);
+ mcfg = (struct MCFGbody *) sdp->body;
+
+ e = (sdp->len - ((caddr_t)&mcfg->s[0] - (caddr_t)sdp)) /
+ sizeof(*mcfg->s);
+ for (i = 0; i < e; i++, mcfg++) {
+ printf("\n");
+ printf("\tBase Address= 0x%016jx\n", mcfg->s[i].baseaddr);
+ printf("\tSegment Group= 0x%04x\n", mcfg->s[i].seg_grp);
+ printf("\tStart Bus= %d\n", mcfg->s[i].start);
+ printf("\tEnd Bus= %d\n", mcfg->s[i].end);
+ }
+ printf(END_COMMENT);
+}
+
+static void
+acpi_print_sdt(struct ACPIsdt *sdp)
+{
+ printf(" ");
+ acpi_print_string(sdp->signature, 4);
+ printf(": Length=%d, Revision=%d, Checksum=%d,\n",
+ sdp->len, sdp->rev, sdp->check);
+ printf("\tOEMID=");
+ acpi_print_string(sdp->oemid, 6);
+ printf(", OEM Table ID=");
+ acpi_print_string(sdp->oemtblid, 8);
+ printf(", OEM Revision=0x%x,\n", sdp->oemrev);
+ printf("\tCreator ID=");
+ acpi_print_string(sdp->creator, 4);
+ printf(", Creator Revision=0x%x\n", sdp->crerev);
+}
+
+static void
+acpi_print_rsdt(struct ACPIsdt *rsdp)
+{
+ int i, entries;
+ u_long addr;
+
+ printf(BEGIN_COMMENT);
+ acpi_print_sdt(rsdp);
+ entries = (rsdp->len - SIZEOF_SDT_HDR) / addr_size;
+ printf("\tEntries={ ");
+ for (i = 0; i < entries; i++) {
+ if (i > 0)
+ printf(", ");
+ switch (addr_size) {
+ case 4:
+ addr = le32dec((char*)rsdp->body + i * addr_size);
+ break;
+ case 8:
+ addr = le64dec((char*)rsdp->body + i * addr_size);
+ break;
+ default:
+ addr = 0;
+ }
+ assert(addr != 0);
+ printf("0x%08lx", addr);
+ }
+ printf(" }\n");
+ printf(END_COMMENT);
+}
+
+static const char *acpi_pm_profiles[] = {
+ "Unspecified", "Desktop", "Mobile", "Workstation",
+ "Enterprise Server", "SOHO Server", "Appliance PC"
+};
+
+static void
+acpi_print_fadt(struct ACPIsdt *sdp)
+{
+ struct FADTbody *fadt;
+ const char *pm;
+ char sep;
+
+ fadt = (struct FADTbody *)sdp->body;
+ printf(BEGIN_COMMENT);
+ acpi_print_sdt(sdp);
+ printf(" \tFACS=0x%x, DSDT=0x%x\n", fadt->facs_ptr,
+ fadt->dsdt_ptr);
+ printf("\tINT_MODEL=%s\n", fadt->int_model ? "APIC" : "PIC");
+ if (fadt->pm_profile >= sizeof(acpi_pm_profiles) / sizeof(char *))
+ pm = "Reserved";
+ else
+ pm = acpi_pm_profiles[fadt->pm_profile];
+ printf("\tPreferred_PM_Profile=%s (%d)\n", pm, fadt->pm_profile);
+ printf("\tSCI_INT=%d\n", fadt->sci_int);
+ printf("\tSMI_CMD=0x%x, ", fadt->smi_cmd);
+ printf("ACPI_ENABLE=0x%x, ", fadt->acpi_enable);
+ printf("ACPI_DISABLE=0x%x, ", fadt->acpi_disable);
+ printf("S4BIOS_REQ=0x%x\n", fadt->s4biosreq);
+ printf("\tPSTATE_CNT=0x%x\n", fadt->pstate_cnt);
+ printf("\tPM1a_EVT_BLK=0x%x-0x%x\n",
+ fadt->pm1a_evt_blk,
+ fadt->pm1a_evt_blk + fadt->pm1_evt_len - 1);
+ if (fadt->pm1b_evt_blk != 0)
+ printf("\tPM1b_EVT_BLK=0x%x-0x%x\n",
+ fadt->pm1b_evt_blk,
+ fadt->pm1b_evt_blk + fadt->pm1_evt_len - 1);
+ printf("\tPM1a_CNT_BLK=0x%x-0x%x\n",
+ fadt->pm1a_cnt_blk,
+ fadt->pm1a_cnt_blk + fadt->pm1_cnt_len - 1);
+ if (fadt->pm1b_cnt_blk != 0)
+ printf("\tPM1b_CNT_BLK=0x%x-0x%x\n",
+ fadt->pm1b_cnt_blk,
+ fadt->pm1b_cnt_blk + fadt->pm1_cnt_len - 1);
+ if (fadt->pm2_cnt_blk != 0)
+ printf("\tPM2_CNT_BLK=0x%x-0x%x\n",
+ fadt->pm2_cnt_blk,
+ fadt->pm2_cnt_blk + fadt->pm2_cnt_len - 1);
+ printf("\tPM_TMR_BLK=0x%x-0x%x\n",
+ fadt->pm_tmr_blk,
+ fadt->pm_tmr_blk + fadt->pm_tmr_len - 1);
+ if (fadt->gpe0_blk != 0)
+ printf("\tGPE0_BLK=0x%x-0x%x\n",
+ fadt->gpe0_blk,
+ fadt->gpe0_blk + fadt->gpe0_len - 1);
+ if (fadt->gpe1_blk != 0)
+ printf("\tGPE1_BLK=0x%x-0x%x, GPE1_BASE=%d\n",
+ fadt->gpe1_blk,
+ fadt->gpe1_blk + fadt->gpe1_len - 1,
+ fadt->gpe1_base);
+ if (fadt->cst_cnt != 0)
+ printf("\tCST_CNT=0x%x\n", fadt->cst_cnt);
+ printf("\tP_LVL2_LAT=%d us, P_LVL3_LAT=%d us\n",
+ fadt->p_lvl2_lat, fadt->p_lvl3_lat);
+ printf("\tFLUSH_SIZE=%d, FLUSH_STRIDE=%d\n",
+ fadt->flush_size, fadt->flush_stride);
+ printf("\tDUTY_OFFSET=%d, DUTY_WIDTH=%d\n",
+ fadt->duty_off, fadt->duty_width);
+ printf("\tDAY_ALRM=%d, MON_ALRM=%d, CENTURY=%d\n",
+ fadt->day_alrm, fadt->mon_alrm, fadt->century);
+
+#define PRINTFLAG(var, flag) do { \
+ if ((var) & FADT_FLAG_## flag) { \
+ printf("%c%s", sep, #flag); sep = ','; \
+ } \
+} while (0)
+
+ printf("\tIAPC_BOOT_ARCH=");
+ sep = '{';
+ PRINTFLAG(fadt->iapc_boot_arch, LEGACY_DEV);
+ PRINTFLAG(fadt->iapc_boot_arch, 8042);
+ if (fadt->iapc_boot_arch != 0)
+ printf("}");
+ printf("\n");
+
+ printf("\tFlags=");
+ sep = '{';
+ PRINTFLAG(fadt->flags, WBINVD);
+ PRINTFLAG(fadt->flags, WBINVD_FLUSH);
+ PRINTFLAG(fadt->flags, PROC_C1);
+ PRINTFLAG(fadt->flags, P_LVL2_UP);
+ PRINTFLAG(fadt->flags, PWR_BUTTON);
+ PRINTFLAG(fadt->flags, SLP_BUTTON);
+ PRINTFLAG(fadt->flags, FIX_RTC);
+ PRINTFLAG(fadt->flags, RTC_S4);
+ PRINTFLAG(fadt->flags, TMR_VAL_EXT);
+ PRINTFLAG(fadt->flags, DCK_CAP);
+ PRINTFLAG(fadt->flags, RESET_REG);
+ PRINTFLAG(fadt->flags, SEALED_CASE);
+ PRINTFLAG(fadt->flags, HEADLESS);
+ PRINTFLAG(fadt->flags, CPU_SW_SLP);
+ if (fadt->flags != 0)
+ printf("}\n");
+
+#undef PRINTFLAG
+
+ if (fadt->flags & FADT_FLAG_RESET_REG) {
+ printf("\tRESET_REG=");
+ acpi_print_gas(&fadt->reset_reg);
+ printf(", RESET_VALUE=%#x\n", fadt->reset_value);
+ }
+ if (acpi_get_fadt_revision(fadt) > 1) {
+ printf("\tX_FACS=0x%08lx, ", (u_long)fadt->x_facs_ptr);
+ printf("X_DSDT=0x%08lx\n", (u_long)fadt->x_dsdt_ptr);
+ printf("\tX_PM1a_EVT_BLK=");
+ acpi_print_gas(&fadt->x_pm1a_evt_blk);
+ if (fadt->x_pm1b_evt_blk.address != 0) {
+ printf("\n\tX_PM1b_EVT_BLK=");
+ acpi_print_gas(&fadt->x_pm1b_evt_blk);
+ }
+ printf("\n\tX_PM1a_CNT_BLK=");
+ acpi_print_gas(&fadt->x_pm1a_cnt_blk);
+ if (fadt->x_pm1b_cnt_blk.address != 0) {
+ printf("\n\tX_PM1b_CNT_BLK=");
+ acpi_print_gas(&fadt->x_pm1b_cnt_blk);
+ }
+ if (fadt->x_pm1b_cnt_blk.address != 0) {
+ printf("\n\tX_PM2_CNT_BLK=");
+ acpi_print_gas(&fadt->x_pm2_cnt_blk);
+ }
+ printf("\n\tX_PM_TMR_BLK=");
+ acpi_print_gas(&fadt->x_pm_tmr_blk);
+ if (fadt->x_gpe0_blk.address != 0) {
+ printf("\n\tX_GPE0_BLK=");
+ acpi_print_gas(&fadt->x_gpe0_blk);
+ }
+ if (fadt->x_gpe1_blk.address != 0) {
+ printf("\n\tX_GPE1_BLK=");
+ acpi_print_gas(&fadt->x_gpe1_blk);
+ }
+ printf("\n");
+ }
+
+ printf(END_COMMENT);
+}
+
+static void
+acpi_print_facs(struct FACSbody *facs)
+{
+ printf(BEGIN_COMMENT);
+ printf(" FACS:\tLength=%u, ", facs->len);
+ printf("HwSig=0x%08x, ", facs->hw_sig);
+ printf("Firm_Wake_Vec=0x%08x\n", facs->firm_wake_vec);
+
+ printf("\tGlobal_Lock=");
+ if (facs->global_lock != 0) {
+ if (facs->global_lock & FACS_FLAG_LOCK_PENDING)
+ printf("PENDING,");
+ if (facs->global_lock & FACS_FLAG_LOCK_OWNED)
+ printf("OWNED");
+ }
+ printf("\n");
+
+ printf("\tFlags=");
+ if (facs->flags & FACS_FLAG_S4BIOS_F)
+ printf("S4BIOS");
+ printf("\n");
+
+ if (facs->x_firm_wake_vec != 0) {
+ printf("\tX_Firm_Wake_Vec=%08lx\n",
+ (u_long)facs->x_firm_wake_vec);
+ }
+ printf("\tVersion=%u\n", facs->version);
+
+ printf(END_COMMENT);
+}
+
+static void
+acpi_print_dsdt(struct ACPIsdt *dsdp)
+{
+ printf(BEGIN_COMMENT);
+ acpi_print_sdt(dsdp);
+ printf(END_COMMENT);
+}
+
+int
+acpi_checksum(void *p, size_t length)
+{
+ u_int8_t *bp;
+ u_int8_t sum;
+
+ bp = p;
+ sum = 0;
+ while (length--)
+ sum += *bp++;
+
+ return (sum);
+}
+
+static struct ACPIsdt *
+acpi_map_sdt(vm_offset_t pa)
+{
+ struct ACPIsdt *sp;
+
+ sp = acpi_map_physical(pa, sizeof(struct ACPIsdt));
+ sp = acpi_map_physical(pa, sp->len);
+ return (sp);
+}
+
+static void
+acpi_print_rsd_ptr(struct ACPIrsdp *rp)
+{
+ printf(BEGIN_COMMENT);
+ printf(" RSD PTR: OEM=");
+ acpi_print_string(rp->oem, 6);
+ printf(", ACPI_Rev=%s (%d)\n", rp->revision < 2 ? "1.0x" : "2.0x",
+ rp->revision);
+ if (rp->revision < 2) {
+ printf("\tRSDT=0x%08x, cksum=%u\n", rp->rsdt_addr, rp->sum);
+ } else {
+ printf("\tXSDT=0x%08lx, length=%u, cksum=%u\n",
+ (u_long)rp->xsdt_addr, rp->length, rp->xsum);
+ }
+ printf(END_COMMENT);
+}
+
+static void
+acpi_handle_rsdt(struct ACPIsdt *rsdp)
+{
+ struct ACPIsdt *sdp;
+ vm_offset_t addr;
+ int entries, i;
+
+ acpi_print_rsdt(rsdp);
+ entries = (rsdp->len - SIZEOF_SDT_HDR) / addr_size;
+ for (i = 0; i < entries; i++) {
+ switch (addr_size) {
+ case 4:
+ addr = le32dec((char*)rsdp->body + i * addr_size);
+ break;
+ case 8:
+ addr = le64dec((char*)rsdp->body + i * addr_size);
+ break;
+ default:
+ assert((addr = 0));
+ }
+
+ sdp = (struct ACPIsdt *)acpi_map_sdt(addr);
+ if (acpi_checksum(sdp, sdp->len)) {
+ warnx("RSDT entry %d (sig %.4s) is corrupt", i,
+ sdp->signature);
+ continue;
+ }
+ if (!memcmp(sdp->signature, "FACP", 4))
+ acpi_handle_fadt(sdp);
+ else if (!memcmp(sdp->signature, "APIC", 4))
+ acpi_handle_apic(sdp);
+ else if (!memcmp(sdp->signature, "HPET", 4))
+ acpi_handle_hpet(sdp);
+ else if (!memcmp(sdp->signature, "ECDT", 4))
+ acpi_handle_ecdt(sdp);
+ else if (!memcmp(sdp->signature, "MCFG", 4))
+ acpi_handle_mcfg(sdp);
+ else {
+ printf(BEGIN_COMMENT);
+ acpi_print_sdt(sdp);
+ printf(END_COMMENT);
+ }
+ }
+}
+
+struct ACPIsdt *
+sdt_load_devmem(void)
+{
+ struct ACPIrsdp *rp;
+ struct ACPIsdt *rsdp;
+
+ rp = acpi_find_rsd_ptr();
+ if (!rp)
+ errx(1, "Can't find ACPI information");
+
+ if (tflag)
+ acpi_print_rsd_ptr(rp);
+ if (rp->revision < 2) {
+ rsdp = (struct ACPIsdt *)acpi_map_sdt(rp->rsdt_addr);
+ if (memcmp(rsdp->signature, "RSDT", 4) != 0 ||
+ acpi_checksum(rsdp, rsdp->len) != 0)
+ errx(1, "RSDT is corrupted");
+ addr_size = sizeof(uint32_t);
+ } else {
+ rsdp = (struct ACPIsdt *)acpi_map_sdt(rp->xsdt_addr);
+ if (memcmp(rsdp->signature, "XSDT", 4) != 0 ||
+ acpi_checksum(rsdp, rsdp->len) != 0)
+ errx(1, "XSDT is corrupted");
+ addr_size = sizeof(uint64_t);
+ }
+ return (rsdp);
+}
+
+/* Write the DSDT to a file, concatenating any SSDTs (if present). */
+static int
+write_dsdt(int fd, struct ACPIsdt *rsdt, struct ACPIsdt *dsdt)
+{
+ struct ACPIsdt sdt;
+ struct ACPIsdt *ssdt;
+ uint8_t sum;
+
+ /* Create a new checksum to account for the DSDT and any SSDTs. */
+ sdt = *dsdt;
+ if (rsdt != NULL) {
+ sdt.check = 0;
+ sum = acpi_checksum(dsdt->body, dsdt->len - SIZEOF_SDT_HDR);
+ ssdt = sdt_from_rsdt(rsdt, "SSDT", NULL);
+ while (ssdt != NULL) {
+ sdt.len += ssdt->len - SIZEOF_SDT_HDR;
+ sum += acpi_checksum(ssdt->body,
+ ssdt->len - SIZEOF_SDT_HDR);
+ ssdt = sdt_from_rsdt(rsdt, "SSDT", ssdt);
+ }
+ sum += acpi_checksum(&sdt, SIZEOF_SDT_HDR);
+ sdt.check -= sum;
+ }
+
+ /* Write out the DSDT header and body. */
+ write(fd, &sdt, SIZEOF_SDT_HDR);
+ write(fd, dsdt->body, dsdt->len - SIZEOF_SDT_HDR);
+
+ /* Write out any SSDTs (if present.) */
+ if (rsdt != NULL) {
+ ssdt = sdt_from_rsdt(rsdt, "SSDT", NULL);
+ while (ssdt != NULL) {
+ write(fd, ssdt->body, ssdt->len - SIZEOF_SDT_HDR);
+ ssdt = sdt_from_rsdt(rsdt, "SSDT", ssdt);
+ }
+ }
+ return (0);
+}
+
+void
+dsdt_save_file(char *outfile, struct ACPIsdt *rsdt, struct ACPIsdt *dsdp)
+{
+ int fd;
+ mode_t mode;
+
+ assert(outfile != NULL);
+ mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+ fd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, mode);
+ if (fd == -1) {
+ perror("dsdt_save_file");
+ return;
+ }
+ write_dsdt(fd, rsdt, dsdp);
+ close(fd);
+}
+
+void
+aml_disassemble(struct ACPIsdt *rsdt, struct ACPIsdt *dsdp)
+{
+ char tmpstr[32], buf[256];
+ FILE *fp;
+ int fd, len;
+
+ strcpy(tmpstr, "/tmp/acpidump.XXXXXX");
+ fd = mkstemp(tmpstr);
+ if (fd < 0) {
+ perror("iasl tmp file");
+ return;
+ }
+ write_dsdt(fd, rsdt, dsdp);
+ close(fd);
+
+ /* Run iasl -d on the temp file */
+ if (fork() == 0) {
+ close(STDOUT_FILENO);
+ if (vflag == 0)
+ close(STDERR_FILENO);
+ execl("/usr/sbin/iasl", "iasl", "-d", tmpstr, (char *) 0);
+ err(1, "exec");
+ }
+
+ wait(NULL);
+ unlink(tmpstr);
+
+ /* Dump iasl's output to stdout */
+ fp = fopen("acpidump.dsl", "r");
+ unlink("acpidump.dsl");
+ if (fp == NULL) {
+ perror("iasl tmp file (read)");
+ return;
+ }
+ while ((len = fread(buf, 1, sizeof(buf), fp)) > 0)
+ fwrite(buf, 1, len, stdout);
+ fclose(fp);
+}
+
+void
+sdt_print_all(struct ACPIsdt *rsdp)
+{
+ acpi_handle_rsdt(rsdp);
+}
+
+/* Fetch a table matching the given signature via the RSDT. */
+struct ACPIsdt *
+sdt_from_rsdt(struct ACPIsdt *rsdt, const char *sig, struct ACPIsdt *last)
+{
+ struct ACPIsdt *sdt;
+ vm_offset_t addr;
+ int entries, i;
+
+ entries = (rsdt->len - SIZEOF_SDT_HDR) / addr_size;
+ for (i = 0; i < entries; i++) {
+ switch (addr_size) {
+ case 4:
+ addr = le32dec((char*)rsdt->body + i * addr_size);
+ break;
+ case 8:
+ addr = le64dec((char*)rsdt->body + i * addr_size);
+ break;
+ default:
+ assert((addr = 0));
+ }
+ sdt = (struct ACPIsdt *)acpi_map_sdt(addr);
+ if (last != NULL) {
+ if (sdt == last)
+ last = NULL;
+ continue;
+ }
+ if (memcmp(sdt->signature, sig, strlen(sig)))
+ continue;
+ if (acpi_checksum(sdt, sdt->len))
+ errx(1, "RSDT entry %d is corrupt", i);
+ return (sdt);
+ }
+
+ return (NULL);
+}
+
+struct ACPIsdt *
+dsdt_from_fadt(struct FADTbody *fadt)
+{
+ struct ACPIsdt *sdt;
+
+ /* Use the DSDT address if it is version 1, otherwise use X_DSDT. */
+ if (acpi_get_fadt_revision(fadt) == 1)
+ sdt = (struct ACPIsdt *)acpi_map_sdt(fadt->dsdt_ptr);
+ else
+ sdt = (struct ACPIsdt *)acpi_map_sdt(fadt->x_dsdt_ptr);
+ if (acpi_checksum(sdt, sdt->len))
+ errx(1, "DSDT is corrupt\n");
+ return (sdt);
+}
diff --git a/usr.sbin/acpi/acpidump/acpi_user.c b/usr.sbin/acpi/acpidump/acpi_user.c
new file mode 100644
index 0000000..d9d9c24
--- /dev/null
+++ b/usr.sbin/acpi/acpidump/acpi_user.c
@@ -0,0 +1,218 @@
+/*-
+ * Copyright (c) 1999 Doug Rabson
+ * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@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 <sys/param.h>
+#include <sys/mman.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <kenv.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "acpidump.h"
+
+static char hint_acpi_0_rsdp[] = "hint.acpi.0.rsdp";
+static char machdep_acpi_root[] = "machdep.acpi_root";
+static int acpi_mem_fd = -1;
+
+struct acpi_user_mapping {
+ LIST_ENTRY(acpi_user_mapping) link;
+ vm_offset_t pa;
+ caddr_t va;
+ size_t size;
+};
+
+LIST_HEAD(acpi_user_mapping_list, acpi_user_mapping) maplist;
+
+static void
+acpi_user_init(void)
+{
+
+ if (acpi_mem_fd == -1) {
+ acpi_mem_fd = open("/dev/mem", O_RDONLY);
+ if (acpi_mem_fd == -1)
+ err(1, "opening /dev/mem");
+ LIST_INIT(&maplist);
+ }
+}
+
+static struct acpi_user_mapping *
+acpi_user_find_mapping(vm_offset_t pa, size_t size)
+{
+ struct acpi_user_mapping *map;
+
+ /* First search for an existing mapping */
+ for (map = LIST_FIRST(&maplist); map; map = LIST_NEXT(map, link)) {
+ if (map->pa <= pa && map->size >= pa + size - map->pa)
+ return (map);
+ }
+
+ /* Then create a new one */
+ size = round_page(pa + size) - trunc_page(pa);
+ pa = trunc_page(pa);
+ map = malloc(sizeof(struct acpi_user_mapping));
+ if (!map)
+ errx(1, "out of memory");
+ map->pa = pa;
+ map->va = mmap(0, size, PROT_READ, MAP_SHARED, acpi_mem_fd, pa);
+ map->size = size;
+ if ((intptr_t) map->va == -1)
+ err(1, "can't map address");
+ LIST_INSERT_HEAD(&maplist, map, link);
+
+ return (map);
+}
+
+static struct ACPIrsdp *
+acpi_get_rsdp(u_long addr)
+{
+ struct ACPIrsdp rsdp;
+ size_t len;
+
+ /* Read in the table signature and check it. */
+ pread(acpi_mem_fd, &rsdp, 8, addr);
+ if (memcmp(rsdp.signature, "RSD PTR ", 8))
+ return (NULL);
+
+ /* Read the entire table. */
+ pread(acpi_mem_fd, &rsdp, sizeof(rsdp), addr);
+
+ /* Run the checksum only over the version 1 header. */
+ if (acpi_checksum(&rsdp, 20))
+ return (NULL);
+
+ /* If the revision is 0, assume a version 1 length. */
+ if (rsdp.revision == 0)
+ len = 20;
+ else
+ len = rsdp.length;
+
+ /* XXX Should handle ACPI 2.0 RSDP extended checksum here. */
+
+ return (acpi_map_physical(addr, len));
+}
+
+static struct ACPIrsdp *
+acpi_scan_rsd_ptr(void)
+{
+#if defined(__amd64__) || defined(__i386__)
+ struct ACPIrsdp *rsdp;
+ u_long addr, end;
+
+ /*
+ * On ia32, scan physical memory for the RSD PTR if above failed.
+ * According to section 5.2.2 of the ACPI spec, we only consider
+ * two regions for the base address:
+ * 1. EBDA (1 KB area addressed by the 16 bit pointer at 0x40E
+ * 2. High memory (0xE0000 - 0xFFFFF)
+ */
+ addr = RSDP_EBDA_PTR;
+ pread(acpi_mem_fd, &addr, sizeof(uint16_t), addr);
+ addr <<= 4;
+ end = addr + RSDP_EBDA_SIZE;
+ for (; addr < end; addr += 16)
+ if ((rsdp = acpi_get_rsdp(addr)) != NULL)
+ return (rsdp);
+ addr = RSDP_HI_START;
+ end = addr + RSDP_HI_SIZE;
+ for (; addr < end; addr += 16)
+ if ((rsdp = acpi_get_rsdp(addr)) != NULL)
+ return (rsdp);
+#endif /* __amd64__ || __i386__ */
+ return (NULL);
+}
+
+/*
+ * Public interfaces
+ */
+struct ACPIrsdp *
+acpi_find_rsd_ptr(void)
+{
+ struct ACPIrsdp *rsdp;
+ char buf[20];
+ u_long addr;
+ size_t len;
+
+ acpi_user_init();
+
+ addr = 0;
+
+ /* Attempt to use kenv or sysctl to find RSD PTR record. */
+ if (kenv(KENV_GET, hint_acpi_0_rsdp, buf, 20) == 0)
+ addr = strtoul(buf, NULL, 0);
+ if (addr == 0) {
+ len = sizeof(addr);
+ if (sysctlbyname(machdep_acpi_root, &addr, &len, NULL, 0) != 0)
+ addr = 0;
+ }
+ if (addr != 0 && (rsdp = acpi_get_rsdp(addr)) != NULL)
+ return (rsdp);
+
+ return (acpi_scan_rsd_ptr());
+}
+
+void *
+acpi_map_physical(vm_offset_t pa, size_t size)
+{
+ struct acpi_user_mapping *map;
+
+ map = acpi_user_find_mapping(pa, size);
+ return (map->va + (pa - map->pa));
+}
+
+struct ACPIsdt *
+dsdt_load_file(char *infile)
+{
+ struct ACPIsdt *sdt;
+ uint8_t *dp;
+ struct stat sb;
+
+ if ((acpi_mem_fd = open(infile, O_RDONLY)) == -1)
+ errx(1, "opening %s", infile);
+
+ LIST_INIT(&maplist);
+
+ if (fstat(acpi_mem_fd, &sb) == -1)
+ errx(1, "fstat %s", infile);
+
+ dp = mmap(0, sb.st_size, PROT_READ, MAP_PRIVATE, acpi_mem_fd, 0);
+ if (dp == NULL)
+ errx(1, "mmap %s", infile);
+
+ sdt = (struct ACPIsdt *)dp;
+ if (strncmp(dp, "DSDT", 4) != 0 || acpi_checksum(sdt, sdt->len) != 0)
+ return (NULL);
+
+ return (sdt);
+}
diff --git a/usr.sbin/acpi/acpidump/acpidump.8 b/usr.sbin/acpi/acpidump/acpidump.8
new file mode 100644
index 0000000..1401e38
--- /dev/null
+++ b/usr.sbin/acpi/acpidump/acpidump.8
@@ -0,0 +1,199 @@
+.\" ACPI (ACPI Package)
+.\"
+.\" Copyright (c) 1999 Doug Rabson <dfr@FreeBSD.org>
+.\" Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+.\" Copyright (c) 2000 Yasuo YOKOYAMA <yokoyama@jp.FreeBSD.org>
+.\" Copyright (c) 2000 Hiroki Sato <hrs@FreeBSD.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd February 14, 2005
+.Dt ACPIDUMP 8
+.Os
+.Sh NAME
+.Nm acpidump
+.Nd dump ACPI tables and ASL
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl t
+.Op Fl h
+.Op Fl v
+.Op Fl f Ar dsdt_input
+.Op Fl o Ar dsdt_output
+.Sh DESCRIPTION
+The
+.Nm
+utility analyzes ACPI tables in physical memory and can dump them to a file.
+In addition,
+.Nm
+can call
+.Xr iasl 8
+to disassemble AML
+(ACPI Machine Language)
+found in these tables and dump them as ASL
+(ACPI Source Language)
+to stdout.
+.Pp
+ACPI tables have an essential data block (the DSDT,
+Differentiated System Description Table)
+that includes information used on the kernel side such as
+detailed information about PnP hardware, procedures for controlling
+power management support, and so on.
+The
+.Nm
+utility can extract the DSDT data block from physical memory and store it into
+an output file and optionally also disassemble it.
+If any Secondary System Description Table
+(SSDT)
+entries exist, they will also be included in the output file and disassembly.
+.Pp
+When
+.Nm
+is invoked without the
+.Fl f
+option, it will read ACPI tables from physical memory via
+.Pa /dev/mem .
+First it searches for the RSDP
+(Root System Description Pointer),
+which has the signature
+.Qq RSD PTR\ \& ,
+and then gets the RSDT
+(Root System Description Table),
+which includes a list of pointers to physical memory addresses
+for other tables.
+The RSDT itself and all other tables linked from RSDT are generically
+called SDTs
+(System Description Tables)
+and their header has a common format which consists of items
+such as Signature, Length, Revision, Checksum, OEMID, OEM Table ID,
+OEM Revision, Creator ID and Creator Revision.
+When invoked with the
+.Fl t
+flag, the
+.Nm
+utility dumps contents of the following tables:
+.Pp
+.Bl -tag -offset indent -width 12345 -compact
+.It DSDT
+.It ECDT
+.It FACS
+.It FADT
+.It HPET
+.It MADT
+.It MCFG
+.It RSD PTR
+.It RSDT
+.El
+.Pp
+The RSDT contains a pointer to the physical memory address of the FACP
+(Fixed ACPI Description Table).
+The FACP defines static system information about power management support
+(ACPI Hardware Register Implementation)
+such as interrupt mode (INT_MODEL),
+SCI interrupt number, SMI command port (SMI_CMD)
+and the location of ACPI registers.
+The FACP also has a pointer to a physical memory address for the DSDT.
+While the other tables are fixed format,
+the DSDT consists of free-formatted AML data.
+.Sh OPTIONS
+The following options are supported by
+.Nm :
+.Bl -tag -width indent
+.It Fl d
+Disassemble the DSDT into ASL using
+.Xr iasl 8
+and print the results to stdout.
+.It Fl t
+Dump the contents of the various fixed tables listed above.
+.It Fl h
+Displays usage and exit.
+.It Fl v
+Enable verbose messages.
+.It Fl f Ar dsdt_input
+Load the DSDT from the specified file instead of physical memory.
+Since only the DSDT is stored in the file, the
+.Fl t
+flag may not be used with this option.
+.It Fl o Ar dsdt_output
+Store the DSDT data block from physical memory into the specified file.
+.El
+.Sh FILES
+.Bl -tag -width /dev/mem
+.It Pa /dev/mem
+.El
+.Sh EXAMPLES
+If a developer requests a copy of your ASL, please use the following
+command to dump all tables and compress the result.
+.Bd -literal -offset indent
+# acpidump -dt | gzip -c9 > my_computer.asl.gz
+.Ed
+.Pp
+This example dumps the DSDT from physical memory to foo.dsdt.
+It also prints the contents of various system tables and disassembles
+the AML contained in the DSDT to stdout, redirecting the output
+to foo.asl.
+.Bd -literal -offset indent
+# acpidump -t -d -o foo.dsdt > foo.asl
+.Ed
+.Pp
+This example reads a DSDT file and disassembles it to stdout.
+Verbose messages are enabled.
+.Bd -literal -offset indent
+# acpidump -v -d -f foo.dsdt
+.Ed
+.Sh SEE ALSO
+.Xr acpi 4 ,
+.Xr mem 4 ,
+.Xr acpiconf 8 ,
+.Xr acpidb 8 ,
+.Xr iasl 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 5.0
+and was rewritten to use
+.Xr iasl 8
+for
+.Fx 5.2 .
+.Sh AUTHORS
+.An Doug Rabson Aq dfr@FreeBSD.org
+.An Mitsuru IWASAKI Aq iwasaki@FreeBSD.org
+.An Yasuo YOKOYAMA Aq yokoyama@jp.FreeBSD.org
+.An Nate Lawson Aq njl@FreeBSD.org
+.Pp
+.An -nosplit
+Some contributions made by
+.An Chitoshi Ohsawa Aq ohsawa@catv1.ccn-net.ne.jp ,
+.An Takayasu IWANASHI Aq takayasu@wendy.a.perfect-liberty.or.jp ,
+.An Yoshihiko SARUMARU Aq mistral@imasy.or.jp ,
+.An Hiroki Sato Aq hrs@FreeBSD.org ,
+.An Michael Lucas Aq mwlucas@blackhelicopters.org
+and
+.An Michael Smith Aq msmith@FreeBSD.org .
+.Sh BUGS
+The current implementation does not dump the BOOT structure or
+other miscellaneous tables.
diff --git a/usr.sbin/acpi/acpidump/acpidump.c b/usr.sbin/acpi/acpidump/acpidump.c
new file mode 100644
index 0000000..a601ac2
--- /dev/null
+++ b/usr.sbin/acpi/acpidump/acpidump.c
@@ -0,0 +1,144 @@
+/*-
+ * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@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 <sys/param.h>
+#include <assert.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "acpidump.h"
+
+int dflag; /* Disassemble AML using iasl(8) */
+int tflag; /* Dump contents of SDT tables */
+int vflag; /* Use verbose messages */
+
+static void
+usage(const char *progname)
+{
+
+ fprintf(stderr, "usage: %s [-d] [-t] [-h] [-v] [-f dsdt_input] "
+ "[-o dsdt_output]\n", progname);
+ fprintf(stderr, "To send ASL:\n\t%s -dt | gzip -c9 > foo.asl.gz\n",
+ progname);
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char c, *progname;
+ char *dsdt_input_file, *dsdt_output_file;
+ struct ACPIsdt *rsdt, *sdt;
+
+ dsdt_input_file = dsdt_output_file = NULL;
+ progname = argv[0];
+
+ if (argc < 2)
+ usage(progname);
+
+ while ((c = getopt(argc, argv, "dhtvf:o:")) != -1) {
+ switch (c) {
+ case 'd':
+ dflag = 1;
+ break;
+ case 't':
+ tflag = 1;
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ case 'f':
+ dsdt_input_file = optarg;
+ break;
+ case 'o':
+ dsdt_output_file = optarg;
+ break;
+ case 'h':
+ default:
+ usage(progname);
+ /* NOTREACHED */
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* Get input either from file or /dev/mem */
+ if (dsdt_input_file != NULL) {
+ if (dflag == 0 && tflag == 0) {
+ warnx("Need to specify -d or -t with DSDT input file");
+ usage(progname);
+ } else if (tflag != 0) {
+ warnx("Can't use -t with DSDT input file");
+ usage(progname);
+ }
+ if (vflag)
+ warnx("loading DSDT file: %s", dsdt_input_file);
+ rsdt = dsdt_load_file(dsdt_input_file);
+ } else {
+ if (vflag)
+ warnx("loading RSD PTR from /dev/mem");
+ rsdt = sdt_load_devmem();
+ }
+
+ /* Display misc. SDT tables (only available when using /dev/mem) */
+ if (tflag) {
+ if (vflag)
+ warnx("printing various SDT tables");
+ sdt_print_all(rsdt);
+ }
+
+ /* Translate RSDT to DSDT pointer */
+ if (dsdt_input_file == NULL) {
+ sdt = sdt_from_rsdt(rsdt, "FACP", NULL);
+ sdt = dsdt_from_fadt((struct FADTbody *)sdt->body);
+ } else {
+ sdt = rsdt;
+ rsdt = NULL;
+ }
+
+ /* Dump the DSDT and SSDTs to a file */
+ if (dsdt_output_file != NULL) {
+ if (vflag)
+ warnx("saving DSDT file: %s", dsdt_output_file);
+ dsdt_save_file(dsdt_output_file, rsdt, sdt);
+ }
+
+ /* Disassemble the DSDT into ASL */
+ if (dflag) {
+ if (vflag)
+ warnx("disassembling DSDT, iasl messages follow");
+ aml_disassemble(rsdt, sdt);
+ if (vflag)
+ warnx("iasl processing complete");
+ }
+
+ exit(0);
+}
diff --git a/usr.sbin/acpi/acpidump/acpidump.h b/usr.sbin/acpi/acpidump/acpidump.h
new file mode 100644
index 0000000..8d79168
--- /dev/null
+++ b/usr.sbin/acpi/acpidump/acpidump.h
@@ -0,0 +1,349 @@
+/*-
+ * Copyright (c) 1999 Doug Rabson
+ * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@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 _ACPIDUMP_H_
+#define _ACPIDUMP_H_
+
+/* Generic Address structure */
+struct ACPIgas {
+ u_int8_t address_space_id;
+#define ACPI_GAS_MEMORY 0
+#define ACPI_GAS_IO 1
+#define ACPI_GAS_PCI 2
+#define ACPI_GAS_EMBEDDED 3
+#define ACPI_GAS_SMBUS 4
+#define ACPI_GAS_FIXED 0x7f
+ u_int8_t bit_width;
+ u_int8_t bit_offset;
+ u_int8_t _reserved;
+ u_int64_t address;
+} __packed;
+
+/* Root System Description Pointer */
+struct ACPIrsdp {
+ u_char signature[8];
+ u_char sum;
+ u_char oem[6];
+ u_char revision;
+ u_int32_t rsdt_addr;
+ u_int32_t length;
+ u_int64_t xsdt_addr;
+ u_char xsum;
+ u_char _reserved_[3];
+} __packed;
+
+/* System Description Table */
+struct ACPIsdt {
+ u_char signature[4];
+ u_int32_t len;
+ u_char rev;
+ u_char check;
+ u_char oemid[6];
+ u_char oemtblid[8];
+ u_int32_t oemrev;
+ u_char creator[4];
+ u_int32_t crerev;
+#define SIZEOF_SDT_HDR 36 /* struct size except body */
+ u_int32_t body[1];/* This member should be casted */
+} __packed;
+
+/* Fixed ACPI Description Table (body) */
+struct FADTbody {
+ u_int32_t facs_ptr;
+ u_int32_t dsdt_ptr;
+ u_int8_t int_model;
+#define ACPI_FADT_INTMODEL_PIC 0 /* Standard PC-AT PIC */
+#define ACPI_FADT_INTMODEL_APIC 1 /* Multiple APIC */
+ u_int8_t pm_profile;
+ u_int16_t sci_int;
+ u_int32_t smi_cmd;
+ u_int8_t acpi_enable;
+ u_int8_t acpi_disable;
+ u_int8_t s4biosreq;
+ u_int8_t pstate_cnt;
+ u_int32_t pm1a_evt_blk;
+ u_int32_t pm1b_evt_blk;
+ u_int32_t pm1a_cnt_blk;
+ u_int32_t pm1b_cnt_blk;
+ u_int32_t pm2_cnt_blk;
+ u_int32_t pm_tmr_blk;
+ u_int32_t gpe0_blk;
+ u_int32_t gpe1_blk;
+ u_int8_t pm1_evt_len;
+ u_int8_t pm1_cnt_len;
+ u_int8_t pm2_cnt_len;
+ u_int8_t pm_tmr_len;
+ u_int8_t gpe0_len;
+ u_int8_t gpe1_len;
+ u_int8_t gpe1_base;
+ u_int8_t cst_cnt;
+ u_int16_t p_lvl2_lat;
+ u_int16_t p_lvl3_lat;
+ u_int16_t flush_size;
+ u_int16_t flush_stride;
+ u_int8_t duty_off;
+ u_int8_t duty_width;
+ u_int8_t day_alrm;
+ u_int8_t mon_alrm;
+ u_int8_t century;
+ u_int16_t iapc_boot_arch;
+#define FADT_FLAG_LEGACY_DEV 1 /* System has legacy devices */
+#define FADT_FLAG_8042 2 /* 8042 keyboard controller */
+ u_char reserved4[1];
+ u_int32_t flags;
+#define FADT_FLAG_WBINVD 1 /* WBINVD is correctly supported */
+#define FADT_FLAG_WBINVD_FLUSH 2 /* WBINVD flushes caches */
+#define FADT_FLAG_PROC_C1 4 /* C1 power state supported */
+#define FADT_FLAG_P_LVL2_UP 8 /* C2 power state works on SMP */
+#define FADT_FLAG_PWR_BUTTON 16 /* Power button uses control method */
+#define FADT_FLAG_SLP_BUTTON 32 /* Sleep button uses control method */
+#define FADT_FLAG_FIX_RTC 64 /* RTC wakeup not supported */
+#define FADT_FLAG_RTC_S4 128 /* RTC can wakeup from S4 state */
+#define FADT_FLAG_TMR_VAL_EXT 256 /* TMR_VAL is 32bit */
+#define FADT_FLAG_DCK_CAP 512 /* Can support docking */
+#define FADT_FLAG_RESET_REG 1024 /* Supports RESET_REG */
+#define FADT_FLAG_SEALED_CASE 2048 /* Case cannot be opened */
+#define FADT_FLAG_HEADLESS 4096 /* No monitor */
+#define FADT_FLAG_CPU_SW_SLP 8192 /* Supports CPU software sleep */
+ struct ACPIgas reset_reg;
+ u_int8_t reset_value;
+ u_int8_t reserved5[3];
+ u_int64_t x_facs_ptr;
+ u_int64_t x_dsdt_ptr;
+ struct ACPIgas x_pm1a_evt_blk;
+ struct ACPIgas x_pm1b_evt_blk;
+ struct ACPIgas x_pm1a_cnt_blk;
+ struct ACPIgas x_pm1b_cnt_blk;
+ struct ACPIgas x_pm2_cnt_blk;
+ struct ACPIgas x_pm_tmr_blk;
+ struct ACPIgas x_gpe0_blk;
+ struct ACPIgas x_gpe1_blk;
+} __packed;
+
+/* Firmware ACPI Control Structure */
+struct FACSbody {
+ u_char signature[4];
+ u_int32_t len;
+ u_int32_t hw_sig;
+ /*
+ * NOTE This should be filled with physical address below 1MB!!
+ * sigh....
+ */
+ u_int32_t firm_wake_vec;
+ u_int32_t global_lock;
+#define FACS_FLAG_LOCK_PENDING 1 /* 5.2.6.1 Global Lock */
+#define FACS_FLAG_LOCK_OWNED 2
+ u_int32_t flags;
+#define FACS_FLAG_S4BIOS_F 1 /* Supports S4BIOS_SEQ */
+ u_int64_t x_firm_wake_vec;
+ u_int8_t version;
+ char reserved[31];
+} __packed;
+
+struct MADT_local_apic {
+ u_char cpu_id;
+ u_char apic_id;
+ u_int32_t flags;
+#define ACPI_MADT_APIC_LOCAL_FLAG_ENABLED 1
+} __packed;
+
+struct MADT_io_apic {
+ u_char apic_id;
+ u_char reserved;
+ u_int32_t apic_addr;
+ u_int32_t int_base;
+} __packed;
+
+struct MADT_int_override {
+ u_char bus;
+ u_char source;
+ u_int32_t intr;
+ u_int16_t mps_flags;
+#define MPS_INT_FLAG_POLARITY_MASK 0x3
+#define MPS_INT_FLAG_POLARITY_CONFORM 0x0
+#define MPS_INT_FLAG_POLARITY_HIGH 0x1
+#define MPS_INT_FLAG_POLARITY_LOW 0x3
+#define MPS_INT_FLAG_TRIGGER_MASK 0xc
+#define MPS_INT_FLAG_TRIGGER_CONFORM 0x0
+#define MPS_INT_FLAG_TRIGGER_EDGE 0x4
+#define MPS_INT_FLAG_TRIGGER_LEVEL 0xc
+} __packed;
+
+struct MADT_nmi {
+ u_int16_t mps_flags;
+ u_int32_t intr;
+} __packed;
+
+struct MADT_local_nmi {
+ u_char cpu_id;
+ u_int16_t mps_flags;
+ u_char lintpin;
+} __packed;
+
+struct MADT_local_apic_override {
+ u_char reserved[2];
+ u_int64_t apic_addr;
+} __packed;
+
+struct MADT_io_sapic {
+ u_char apic_id;
+ u_char reserved;
+ u_int32_t int_base;
+ u_int64_t apic_addr;
+} __packed;
+
+struct MADT_local_sapic {
+ u_char cpu_id;
+ u_char apic_id;
+ u_char apic_eid;
+ u_char reserved[3];
+ u_int32_t flags;
+} __packed;
+
+struct MADT_int_src {
+ u_int16_t mps_flags;
+ u_char type;
+#define ACPI_MADT_APIC_INT_SOURCE_PMI 1
+#define ACPI_MADT_APIC_INT_SOURCE_INIT 2
+#define ACPI_MADT_APIC_INT_SOURCE_CPEI 3 /* Corrected Platform Error */
+ u_char cpu_id;
+ u_char cpu_eid;
+ u_char sapic_vector;
+ u_int32_t intr;
+ u_char reserved[4];
+} __packed;
+
+struct MADT_APIC {
+ u_char type;
+#define ACPI_MADT_APIC_TYPE_LOCAL_APIC 0
+#define ACPI_MADT_APIC_TYPE_IO_APIC 1
+#define ACPI_MADT_APIC_TYPE_INT_OVERRIDE 2
+#define ACPI_MADT_APIC_TYPE_NMI 3
+#define ACPI_MADT_APIC_TYPE_LOCAL_NMI 4
+#define ACPI_MADT_APIC_TYPE_LOCAL_OVERRIDE 5
+#define ACPI_MADT_APIC_TYPE_IO_SAPIC 6
+#define ACPI_MADT_APIC_TYPE_LOCAL_SAPIC 7
+#define ACPI_MADT_APIC_TYPE_INT_SRC 8
+ u_char len;
+ union {
+ struct MADT_local_apic local_apic;
+ struct MADT_io_apic io_apic;
+ struct MADT_int_override int_override;
+ struct MADT_nmi nmi;
+ struct MADT_local_nmi local_nmi;
+ struct MADT_local_apic_override local_apic_override;
+ struct MADT_io_sapic io_sapic;
+ struct MADT_local_sapic local_sapic;
+ struct MADT_int_src int_src;
+ } body;
+} __packed;
+
+struct MADTbody {
+ u_int32_t lapic_addr;
+ u_int32_t flags;
+#define ACPI_APIC_FLAG_PCAT_COMPAT 1 /* System has dual-8259 setup. */
+ u_char body[1];
+} __packed;
+
+struct HPETbody {
+ u_int32_t block_hwrev:8,
+ block_comparitors:5,
+ block_counter_size:1,
+ :1,
+ block_legacy_capable:1,
+ block_pcivendor:16;
+ struct ACPIgas genaddr;
+ u_int8_t hpet_number;
+ u_int16_t clock_tick __packed;
+} __packed;
+
+/* Embedded Controller Description Table */
+struct ECDTbody {
+ struct ACPIgas ec_control; /* Control register */
+ struct ACPIgas ec_data; /* Data register */
+ uint32_t uid; /* Same value as _UID in namespace */
+ uint8_t gpe_bit; /* GPE bit for the EC */
+ u_char ec_id[1]; /* Variable length name string */
+} __packed;
+
+/* Memory Mapped PCI config space base allocation structure */
+struct MCFGbody {
+ uint8_t rsvd[8];
+ struct {
+ uint64_t baseaddr; /* Base Address */
+ uint16_t seg_grp; /* Segment group number */
+ uint8_t start; /* Starting bus number */
+ uint8_t end; /* Ending bus number */
+ uint8_t rsvd[4]; /* Reserved */
+ } s[1] __packed;
+} __packed;
+
+/*
+ * Addresses to scan on ia32 for the RSD PTR. According to section 5.2.2
+ * of the ACPI spec, we only consider two regions for the base address:
+ * 1. EBDA (1 KB area addressed to by 16 bit pointer at 0x40E)
+ * 2. High memory (0xE0000 - 0xFFFFF)
+ */
+#define RSDP_EBDA_PTR 0x40E
+#define RSDP_EBDA_SIZE 0x400
+#define RSDP_HI_START 0xE0000
+#define RSDP_HI_SIZE 0x20000
+
+/* Find and map the RSD PTR structure and return it for parsing */
+struct ACPIsdt *sdt_load_devmem(void);
+
+/*
+ * Load the DSDT from a previous save file. Note that other tables are
+ * not saved (i.e. FADT)
+ */
+struct ACPIsdt *dsdt_load_file(char *);
+
+/* Save the DSDT to a file */
+void dsdt_save_file(char *, struct ACPIsdt *, struct ACPIsdt *);
+
+/* Print out as many fixed tables as possible, given the RSD PTR */
+void sdt_print_all(struct ACPIsdt *);
+
+/* Disassemble the AML in the DSDT */
+void aml_disassemble(struct ACPIsdt *, struct ACPIsdt *);
+
+/* Routines for accessing tables in physical memory */
+struct ACPIrsdp *acpi_find_rsd_ptr(void);
+void *acpi_map_physical(vm_offset_t, size_t);
+struct ACPIsdt *sdt_from_rsdt(struct ACPIsdt *, const char *,
+ struct ACPIsdt *);
+struct ACPIsdt *dsdt_from_fadt(struct FADTbody *);
+int acpi_checksum(void *, size_t);
+
+/* Command line flags */
+extern int dflag;
+extern int tflag;
+extern int vflag;
+
+#endif /* !_ACPIDUMP_H_ */
diff --git a/usr.sbin/acpi/iasl/Makefile b/usr.sbin/acpi/iasl/Makefile
new file mode 100644
index 0000000..aed6631
--- /dev/null
+++ b/usr.sbin/acpi/iasl/Makefile
@@ -0,0 +1,70 @@
+# $FreeBSD$
+
+PROG= iasl
+SRCS= adfile.c adisasm.c adwalk.c
+SRCS+= osunixxf.c
+
+# common
+SRCS+= dmrestag.c dmtable.c dmtbdump.c dmtbinfo.c getopt.c
+
+# compiler
+SRCS+= aslanalyze.c aslcodegen.c aslcompile.c aslcompiler.y.h \
+ aslcompilerlex.l aslcompilerparse.y aslerror.c \
+ aslfiles.c aslfold.c asllength.c asllisting.c \
+ aslload.c asllookup.c aslmain.c aslmap.c aslopcodes.c \
+ asloperands.c aslopt.c aslresource.c aslrestype1.c \
+ aslrestype2.c aslstubs.c asltransform.c asltree.c \
+ aslutils.c
+
+# debugger
+SRCS+= dbfileio.c
+
+# disassembler
+SRCS+= dmbuffer.c dmnames.c dmobject.c dmopcode.c dmresrc.c \
+ dmresrcl.c dmresrcs.c dmutils.c dmwalk.c
+
+# interpreter/dispatcher
+SRCS+= dsfield.c dsobject.c dsopcode.c dsutils.c dswexec.c \
+ dswload.c dswscope.c dswstate.c
+
+# interpreter/executer
+SRCS+= exconvrt.c excreate.c exdump.c exmisc.c exmutex.c \
+ exnames.c exoparg1.c exoparg2.c exoparg3.c exoparg6.c \
+ exprep.c exregion.c exresnte.c exresolv.c exresop.c \
+ exstore.c exstoren.c exstorob.c exsystem.c exutils.c
+
+# interpreter/parser
+SRCS+= psargs.c psloop.c psopcode.c psparse.c psscope.c \
+ pstree.c psutils.c pswalk.c
+
+# namespace
+SRCS+= nsaccess.c nsalloc.c nsdump.c nsnames.c nsobject.c \
+ nsparse.c nssearch.c nsutils.c nswalk.c nsxfobj.c
+
+# tables
+SRCS+= tbfadt.c tbinstal.c tbutils.c tbxface.c
+
+# utilities
+SRCS+= utalloc.c utcache.c utcopy.c utdebug.c utdelete.c \
+ utglobal.c utmath.c utmisc.c utmutex.c utobject.c \
+ utresrc.c utstate.c
+
+MAN= iasl.8
+
+CFLAGS+= -DACPI_ASL_COMPILER -I.
+CFLAGS+= -D_USE_BERKELEY_YACC
+LFLAGS= -i -PAslCompiler
+YFLAGS= -d -pAslCompiler
+
+CLEANFILES= aslcompiler.y.h aslcompilerlex.l aslcompilerparse.y
+
+aslcompiler.y.h: aslcompilerparse.h
+ cat ${.ALLSRC} > ${.TARGET}
+
+aslcompilerlex.l: aslcompiler.l
+ cat ${.ALLSRC} > ${.TARGET}
+
+aslcompilerparse.y: aslcompiler.y
+ cat ${.ALLSRC} > ${.TARGET}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/acpi/iasl/iasl.8 b/usr.sbin/acpi/iasl/iasl.8
new file mode 100644
index 0000000..bf16c8c
--- /dev/null
+++ b/usr.sbin/acpi/iasl/iasl.8
@@ -0,0 +1,179 @@
+.\"-
+.\" Copyright (c) 2003 Nate Lawson
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer
+.\" in this position and unchanged.
+.\" 2. Redistributions in binary form must 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 May 20, 2008
+.Dt IASL 8
+.Os
+.Sh NAME
+.Nm iasl
+.Nd Intel ACPI compiler/decompiler
+.Sh SYNOPSIS
+.Nm
+.Op Fl 2cefghl
+.Op Fl b Ar type
+.Op Fl d Ar file
+.Op Fl dc Ar file
+.Op Fl hc
+.Op Fl hr
+.Op Fl i Ar type
+.Op Fl ln
+.Op Fl ls
+.Op Fl oa
+.Op Fl of
+.Op Fl oi
+.Op Fl on
+.Op Fl ot
+.Op Fl p Ar prefix
+.Op Fl s Ar type
+.Op Fl t Ar type
+.Op Fl vi
+.Op Fl vo
+.Op Fl vr
+.Op Fl vs
+.Op Fl x Ar level
+.Op Fl w Ar level
+.Ar input-file
+.Sh DESCRIPTION
+The
+.Nm
+utility is a compiler/decompiler for ACPI Source Language (ASL)
+and ACPI Machine Language (AML).
+Major features of
+.Nm
+include:
+.Bl -bullet -offset indent
+.It
+Full support for the ACPI 3.0b Specification including ASL grammar
+elements and operators.
+.It
+Extensive compiler syntax and semantic error checking, especially in
+the area of control methods.
+This reduces the number of errors that are
+not discovered until the AML code is actually interpreted (i.e., the
+compile-time error checking reduces the number of run-time errors).
+.It
+Multiple types of output files, including formatted listing files with
+intermixed source, several types of AML files, and error messages.
+.El
+.Sh OPTIONS
+.Bl -tag -width indent
+.It Fl 2
+Emit ACPI 2.0 compatible ASL code.
+.It Fl b Sm Cm p | t | b Sm
+Create compiler debug/trace file
+.Pq Pa *.txt .
+Types: Parse/Tree/Both.
+.It Fl c
+Parse only, no output generation.
+.It Fl d Ar file
+Disassemble AML to ASL source code file
+.Pq Pa *.dsl .
+.It Fl dc Ar file
+Disassemble AML and immediately compile it.
+(Obtain DSDT from current system if no input file.)
+.It Fl e
+Generate
+.Fn External
+statements for unresolved symbols.
+.It Fl f
+Ignore errors, force creation of AML output file(s).
+.It Fl g
+Get ACPI tables and write to files
+.Pq Pa *.dat .
+.It Fl h
+Additional help and compiler debug options.
+.It Fl hc
+Display operators allowed in constant expressions.
+.It Fl hr
+Display ACPI reserved method names.
+.It Fl i Sm Cm a | c Sm
+Create assembler or C include file
+.Pa ( *.inc
+or
+.Pa *.h ) .
+.It Fl l
+Create mixed listing file (ASL source and AML)
+.Pq Pa *.lst .
+.It Fl ln
+Create namespace file
+.Pq Pa *.nsp .
+.It Fl ls
+Create combined source file (expanded includes)
+.Pq Pa *.src .
+.It Fl oa
+Disable all optimizations (compatibility mode).
+.It Fl of
+Disable constant folding.
+.It Fl oi
+Disable integer optimization to Zero/One/Ones.
+.It Fl on
+Disable named reference string optimization.
+.It Fl ot
+Display compile times.
+.It Fl p Ar prefix
+Specify filename prefix for all output files (including
+.Pa .aml ) .
+.It Fl s Sm Cm a | c Sm
+Create AML in assembler or C source file
+.Pa ( *.asm
+or
+.Pa *.c ) .
+.It Fl t Ar a|c
+Create AML in assembler or C hex table
+.Pq Pa *.hex .
+.It Fl vi
+Less verbose errors and warnings for use with IDEs.
+.It Fl vo
+Enable optimization comments.
+.It Fl vr
+Disable remarks.
+.It Fl vs
+Disable signon.
+.It Fl x Ar level
+Set debug level for trace output.
+.It Fl w Ar level
+Set warning level.
+.El
+.Sh SEE ALSO
+.Xr acpi 4 ,
+.Xr acpidump 8
+.Sh HISTORY
+The
+.Nm
+utility is provided with Intel ACPI-CA.
+It first appeared in
+.Fx 5.2 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility was written by
+.An Intel .
+This manual page was written by
+.An Nate Lawson .
diff --git a/usr.sbin/adduser/Makefile b/usr.sbin/adduser/Makefile
new file mode 100644
index 0000000..0ca2dae
--- /dev/null
+++ b/usr.sbin/adduser/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+SCRIPTS=adduser.sh rmuser.sh
+MAN= adduser.conf.5 adduser.8 rmuser.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/adduser/adduser.8 b/usr.sbin/adduser/adduser.8
new file mode 100644
index 0000000..03f7e34
--- /dev/null
+++ b/usr.sbin/adduser/adduser.8
@@ -0,0 +1,480 @@
+.\" Copyright (c) 1995-1996 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+.\" All rights reserved.
+.\" Copyright (c) 2002-2004 Michael Telahun Makonnen <mtm@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 March 16, 2008
+.Dt ADDUSER 8
+.Os
+.Sh NAME
+.Nm adduser
+.Nd command for adding new users
+.Sh SYNOPSIS
+.Nm
+.Op Fl CDENShq
+.Op Fl G Ar groups
+.Op Fl L Ar login_class
+.Op Fl M Ar mode
+.Op Fl d Ar partition
+.Op Fl f Ar file
+.Op Fl g Ar login_group
+.Op Fl k Ar dotdir
+.Op Fl m Ar message_file
+.Op Fl s Ar shell
+.Op Fl u Ar uid_start
+.Op Fl w Ar type
+.Sh DESCRIPTION
+The
+.Nm
+utility is a shell script, implemented around the
+.Xr pw 8
+command, for adding new users.
+It creates passwd/group entries, a home directory,
+copies dotfiles and sends the new user a welcome message.
+It supports two modes of operation.
+It may be used interactively
+at the command line to add one user at a time, or it may be directed
+to get the list of new users from a file and operate in batch mode
+without requiring any user interaction.
+.Sh RESTRICTIONS
+.Bl -tag -width indent
+.It username
+Login name.
+The user name is restricted to whatever
+.Xr pw 8
+will accept.
+Generally this means it
+may contain only lowercase characters or digits but cannot begin with the
+.Ql -
+character.
+Maximum length
+is 16 characters.
+The reasons for this limit are historical.
+Given that people have traditionally wanted to break this
+limit for aesthetic reasons, it has never been of great importance to break
+such a basic fundamental parameter in
+.Ux .
+You can change
+.Dv UT_NAMESIZE
+in
+.In utmp.h
+and recompile the
+world; people have done this and it works, but you will have problems
+with any precompiled programs, or source that assumes the 8-character
+name limit, such as NIS.
+The NIS protocol mandates an 8-character username.
+If you need a longer login name for e-mail addresses,
+you can define an alias in
+.Pa /etc/mail/aliases .
+.It "full name"
+This is typically known as the gecos field and usually contains
+the user's full name.
+Additionally, it may contain a comma separated
+list of values such as office number and work and home phones.
+If the
+name contains an ampersand it will be replaced by the capitalized
+login name when displayed by other programs.
+The
+.Ql \&:
+character is not allowed.
+.It shell
+Unless the
+.Fl S
+argument is supplied only valid shells from the shell database
+.Pq Pa /etc/shells
+are allowed.
+In addition,
+either the base name or the full path of the shell may be supplied.
+.It UID
+Automatically generated or your choice.
+It must be less than 32000.
+.It "GID/login group"
+Automatically generated or your choice.
+It must be less than 32000.
+.It password
+You may choose an empty password, disable the password, use a
+randomly generated password or specify your own plaintext password,
+which will be encrypted before being stored in the user database.
+.El
+.Sh UNIQUE GROUPS
+Perhaps you are missing what
+.Em can
+be done with this scheme that falls apart
+with most other schemes.
+With each user in their own group,
+they can safely run with a umask of 002 instead of the usual 022
+and create files in their home directory
+without worrying about others being able to change them.
+.Pp
+For a shared area you create a separate UID/GID (like cvs or ncvs on freefall),
+you place each person that should be able to access this area into that new
+group.
+.Pp
+This model of UID/GID administration allows far greater flexibility than lumping
+users into groups and having to muck with the umask when working in a shared
+area.
+.Pp
+I have been using this model for almost 10 years and found that it works
+for most situations, and has never gotten in the way.
+(Rod Grimes)
+.Sh CONFIGURATION
+The
+.Nm
+utility reads its configuration information from
+.Pa /etc/adduser.conf .
+If this file does not exist, it will use predefined defaults.
+While this file may be edited by hand,
+the safer option is to use the
+.Fl C
+command line argument.
+With this argument,
+.Nm
+will start interactive input, save the answers to its prompts in
+.Pa /etc/adduser.conf ,
+and promptly exit without modifying the user
+database.
+Options specified on the command line will take precedence over
+any values saved in this file.
+.Sh OPTIONS
+.Bl -tag -width indent
+.It Fl C
+Create new configuration file and exit.
+This option is mutually exclusive with the
+.Fl f
+option.
+.It Fl d Ar partition
+Home partition.
+Default partition, under which all user directories
+will be located.
+The
+.Pa /nonexistent
+partition is considered special.
+The
+.Nm
+script will not create and populate a home directory by that name.
+Otherwise,
+by default it attempts to create a home directory.
+.It Fl D
+Do not attempt to create the home directory.
+.It Fl E
+Disable the account.
+This option will lock the account by prepending the string
+.Dq Li *LOCKED*
+to the password field.
+The account may be unlocked
+by the super-user with the
+.Xr pw 8
+command:
+.Pp
+.D1 Nm pw Cm unlock Op Ar name | uid
+.It Fl f Ar file
+Get the list of accounts to create from
+.Ar file .
+If
+.Ar file
+is
+.Dq Fl ,
+then get the list from standard input.
+If this option is specified,
+.Nm
+will operate in batch mode and will not seek any user input.
+If an error is encountered while processing an account, it will write a
+message to standard error and move to the next account.
+The format
+of the input file is described below.
+.It Fl g Ar login_group
+Normally,
+if no login group is specified,
+it is assumed to be the same as the username.
+This option makes
+.Ar login_group
+the default.
+.It Fl G Ar groups
+Space-separated list of additional groups.
+This option allows the user to specify additional groups to add users to.
+The user is a member of these groups in addition to their login group.
+.It Fl h
+Print a summary of options and exit.
+.It Fl k Ar directory
+Copy files from
+.Ar directory
+into the home
+directory of new users;
+.Pa dot.foo
+will be renamed to
+.Pa .foo .
+.It Fl L Ar login_class
+Set default login class.
+.It Fl m Ar file
+Send new users a welcome message from
+.Ar file .
+Specifying a value of
+.Cm no
+for
+.Ar file
+causes no message to be sent to new users.
+Please note that the message
+file can reference the internal variables of the
+.Nm
+script.
+.It Fl M Ar mode
+Create the home directory with permissions set to
+.Ar mode .
+.It Fl N
+Do not read the default configuration file.
+.It Fl q
+Minimal user feedback.
+In particular, the random password will not be echoed to
+standard output.
+.It Fl s Ar shell
+Default shell for new users.
+The
+.Ar shell
+argument may be the base name of the shell or the full path.
+Unless the
+.Fl S
+argument is supplied the shell must exist in
+.Pa /etc/shells
+or be the special shell
+.Em nologin
+to be considered a valid shell.
+.It Fl S
+The existence or validity of the specified shell will not be checked.
+.It Fl u Ar uid
+Use UIDs from
+.Ar uid
+on up.
+.It Fl w Ar type
+Password type.
+The
+.Nm
+utility allows the user to specify what type of password to create.
+The
+.Ar type
+argument may have one of the following values:
+.Bl -tag -width ".Cm random"
+.It Cm no
+Disable the password.
+Instead of an encrypted string, the password field will contain a single
+.Ql *
+character.
+The user may not log in until the super-user
+manually enables the password.
+.It Cm none
+Use an empty string as the password.
+.It Cm yes
+Use a user-supplied string as the password.
+In interactive mode,
+the user will be prompted for the password.
+In batch mode, the
+last (10th) field in the line is assumed to be the password.
+.It Cm random
+Generate a random string and use it as a password.
+The password will be echoed to standard output.
+In addition, it will be available for inclusion in the message file in the
+.Va randompass
+variable.
+.El
+.El
+.Sh FORMAT
+When the
+.Fl f
+option is used, the account information must be stored in a specific
+format.
+All empty lines or lines beginning with a
+.Ql #
+will be ignored.
+All other lines must contain ten colon
+.Pq Ql \&:
+separated fields as described below.
+Command line options do not take precedence
+over values in the fields.
+Only the password field may contain a
+.Ql \&:
+character as part of the string.
+.Pp
+.Sm off
+.D1 Ar name : uid : gid : class : change : expire : gecos : home_dir : shell : password
+.Sm on
+.Bl -tag -width ".Ar password"
+.It Ar name
+Login name.
+This field may not be empty.
+.It Ar uid
+Numeric login user ID.
+If this field is left empty, it will be automatically generated.
+.It Ar gid
+Numeric primary group ID.
+If this field is left empty, a group with the
+same name as the user name will be created and its GID will be used
+instead.
+.It Ar class
+Login class.
+This field may be left empty.
+.It Ar change
+Password ageing.
+This field denotes the password change date for the account.
+The format of this field is the same as the format of the
+.Fl p
+argument to
+.Xr pw 8 .
+It may be
+.Ar dd Ns - Ns Ar mmm Ns - Ns Ar yy Ns Op Ar yy ,
+where
+.Ar dd
+is for the day,
+.Ar mmm
+is for the month in numeric or alphabetical format:
+.Dq Li 10
+or
+.Dq Li Oct ,
+and
+.Ar yy Ns Op Ar yy
+is the four or two digit year.
+To denote a time relative to the current date the format is:
+.No + Ns Ar n Ns Op Ar mhdwoy ,
+where
+.Ar n
+denotes a number, followed by the minutes, hours, days, weeks,
+months or years after which the password must be changed.
+This field may be left empty to turn it off.
+.It Ar expire
+Account expiration.
+This field denotes the expiry date of the account.
+The account may not be used after the specified date.
+The format of this field is the same as that for password ageing.
+This field may be left empty to turn it off.
+.It Ar gecos
+Full name and other extra information about the user.
+.It Ar home_dir
+Home directory.
+If this field is left empty, it will be automatically
+created by appending the username to the home partition.
+The
+.Pa /nonexistent
+home directory is considered special and
+is understood to mean that no home directory is to be
+created for the user.
+.It Ar shell
+Login shell.
+This field should contain either the base name or
+the full path to a valid login shell.
+.It Ar password
+User password.
+This field should contain a plaintext string, which will
+be encrypted before being placed in the user database.
+If the password type is
+.Cm yes
+and this field is empty, it is assumed the account will have an empty password.
+If the password type is
+.Cm random
+and this field is
+.Em not
+empty, its contents will be used
+as a password.
+This field will be ignored if the
+.Fl w
+option is used with a
+.Cm no
+or
+.Cm none
+argument.
+Be careful not to terminate this field with a closing
+.Ql \&:
+because it will be treated as part of the password.
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /etc/adduser.message" -compact
+.It Pa /etc/master.passwd
+user database
+.It Pa /etc/group
+group database
+.It Pa /etc/shells
+shell database
+.It Pa /etc/login.conf
+login classes database
+.It Pa /etc/adduser.conf
+configuration file for
+.Nm
+.It Pa /etc/adduser.message
+message file for
+.Nm
+.It Pa /usr/share/skel
+skeletal login directory
+.It Pa /var/log/adduser
+logfile for
+.Nm
+.El
+.Sh SEE ALSO
+.Xr chpass 1 ,
+.Xr passwd 1 ,
+.Xr adduser.conf 5 ,
+.Xr aliases 5 ,
+.Xr group 5 ,
+.Xr login.conf 5 ,
+.Xr passwd 5 ,
+.Xr shells 5 ,
+.Xr adding_user 8 ,
+.Xr pw 8 ,
+.Xr pwd_mkdb 8 ,
+.Xr rmuser 8 ,
+.Xr vipw 8 ,
+.Xr yp 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Fx 2.1 .
+.Sh AUTHORS
+.An -nosplit
+This manual page and the original script, in Perl, was written by
+.An Wolfram Schneider Aq wosch@FreeBSD.org .
+The replacement script, written as a Bourne
+shell script with some enhancements, and the man page modification that
+came with it were done by
+.An Mike Makonnen Aq mtm@identd.net .
+.Sh BUGS
+In order for
+.Nm
+to correctly expand variables such as
+.Va $username
+and
+.Va $randompass
+in the message sent to new users, it must let the shell evaluate
+each line of the message file.
+This means that shell commands can also be embedded in the message file.
+The
+.Nm
+utility attempts to mitigate the possibility of an attacker using this
+feature by refusing to evaluate the file if it is not owned and writable
+only by the root user.
+In addition, shell special characters and operators will have to be
+escaped when used in the message file.
+.Pp
+Also, password ageing and account expiry times are currently settable
+only in batch mode or when specified in
+.Pa /etc/adduser.conf .
+The user should be able to set them in interactive mode as well.
diff --git a/usr.sbin/adduser/adduser.conf.5 b/usr.sbin/adduser/adduser.conf.5
new file mode 100644
index 0000000..a78aeea
--- /dev/null
+++ b/usr.sbin/adduser/adduser.conf.5
@@ -0,0 +1,221 @@
+.\"
+.\" Copyright (c) 2004 Tom Rhodes
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 12, 2007
+.Dt ADDUSER.CONF 5
+.Os
+.Sh NAME
+.Nm adduser.conf
+.Nd
+.Xr adduser 8
+configuration file
+.Sh DESCRIPTION
+The
+.Pa /etc/adduser.conf
+file is automatically generated by the
+.Xr adduser 8
+utility when invoked with the
+.Fl C
+command-line option.
+It is not meant to be edited by hand.
+.Pp
+The
+.Pa /etc/adduser.conf
+file is used to pre-set certain configuration options for
+the
+.Xr adduser 8
+utility.
+When
+.Xr adduser 8
+is invoked, it will check to see if this file exists, and
+if so, the configuration will be used or offered as the
+default settings.
+The
+.Nm
+file offers three types of configuration:
+.Bl -bullet
+.It
+Default settings offered by
+.Xr adduser 8 .
+These options are specified in the configuration file and offered
+as the default during every invocation of the
+.Xr adduser 8
+utility.
+.It
+Configuration options which can be set in
+.Nm ,
+but overridden by passing a flag to
+.Xr adduser 8 .
+.It
+Configuration supported by
+.Xr adduser 8
+but not offered by a flag or during initial invocation.
+.El
+.Pp
+In the first case, these options can be set in
+.Nm
+but will still be offered when
+.Xr adduser 8
+is invoked.
+In the second case,
+.Xr adduser 8
+will read the configuration data unless a flag
+has been passed to override it.
+For example, the
+.Va defaultshell
+option.
+In the third case, the configuration will be utilized, but the
+user will never be prompted to modify the default setting by
+either a flag or an
+.Xr adduser 8
+prompt.
+For example, the
+.Va upwexpire
+setting.
+.Pp
+The following configuration options can be set in
+.Nm :
+.Bl -tag -width ".Va defaultgroups" -offset indent
+.It Va defaultLgroup
+The default group new users will be added to.
+.It Va defaultclass
+The default class to place users in as described in
+.Xr login.conf 5 .
+.It Va defaultgroups
+This option is used to specify what other groups the new account
+should be added to.
+.It Va passwdtype
+May be one of
+.Cm no , none , random ,
+or
+.Cm yes ,
+as described in
+.Xr adduser 8 .
+As such, the text is not duplicated here and may be
+read in
+.Xr adduser 8 .
+.It Va homeprefix
+The default home directory prefix, usually
+.Pa /home .
+.It Va defaultshell
+The user's default shell which may be any of the shells listed in
+.Xr shells 5 .
+.It Va udotdir
+Defines the location of the default shell and environment
+configuration files.
+.It Va msgfile
+Location of the default new user message file.
+This message will be sent to all new users if specified
+here or at the
+.Xr adduser 8
+prompt.
+.It Va disableflag
+The default message enclosed in brackets for the
+lock account prompt.
+.It Va upwexpire
+The default password expiration time.
+Format of the date is either a
+.Ux
+time in decimal, or a date in
+.Sm off
+.Ar dd No - Ar mmm No - Ar yy Op Ar yy
+.Sm on
+format, where
+.Ar dd
+is the day,
+.Ar mmm
+is the month in either numeric or
+alphabetic format, and
+.Ar yy Ns Op Ar yy
+is either a two or four digit year.
+This option also accepts a relative date in the form of
+.Sm off
+.Ar n Op Ar m h d w o y
+.Sm on
+where
+.Ar n
+is a decimal, octal (leading 0) or hexadecimal (leading 0x) digit
+followed by the number of Minutes, Hours, Days, Weeks, Months or
+Years from the current date at
+which the expiration time is to be set.
+.It Va uexpire
+The default account expire time.
+The format is similar to the
+.Va upwexpire
+option.
+.It Va ugecos
+The default information to be held in the GECOS field of
+.Pa /etc/master.passwd .
+.It Va uidstart
+The default user ID setting.
+This must be a number above 1000 and fewer than 65534.
+.El
+.Sh EXAMPLES
+The following is an example
+.Nm
+file created with the
+.Fl C
+.Xr adduser 8
+flag and modified.
+.Bd -literal -offset indent
+# Configuration file for adduser(8).
+# NOTE: only *some* variables are saved.
+# Last Modified on Fri Mar 30 14:04:05 EST 2004.
+
+defaultLgroup=
+defaultclass=
+defaultgroups=
+passwdtype=yes
+homeprefix=/home
+defaultshell=/bin/csh
+udotdir=/usr/share/skel
+msgfile=/etc/adduser.msg
+disableflag=
+upwexpire=91d # Expire passwords 91 days after creation.
+.Ed
+.Sh SEE ALSO
+.Xr group 5 ,
+.Xr passwd 5 ,
+.Xr adduser 8 ,
+.Xr pw 8 ,
+.Xr rmuser 8
+.Sh HISTORY
+The
+.Nm
+manual page first appeared in
+.Fx 5.3 .
+.Sh BUGS
+The internal variables documented here may change without notice.
+Do not rely on them.
+To modify this file invoke
+.Xr adduser 8
+with the
+.Fl C
+option instead.
+.Sh AUTHORS
+This manual page was written by
+.An Tom Rhodes Aq trhodes@FreeBSD.org .
diff --git a/usr.sbin/adduser/adduser.sh b/usr.sbin/adduser/adduser.sh
new file mode 100644
index 0000000..3d1d6f8
--- /dev/null
+++ b/usr.sbin/adduser/adduser.sh
@@ -0,0 +1,1052 @@
+#!/bin/sh
+#
+# Copyright (c) 2002-2004 Michael Telahun Makonnen. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must 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.
+#
+# Email: Mike Makonnen <mtm@FreeBSD.Org>
+#
+# $FreeBSD$
+#
+
+# err msg
+# Display $msg on stderr, unless we're being quiet.
+#
+err() {
+ if [ -z "$quietflag" ]; then
+ echo 1>&2 ${THISCMD}: ERROR: $*
+ fi
+}
+
+# info msg
+# Display $msg on stdout, unless we're being quiet.
+#
+info() {
+ if [ -z "$quietflag" ]; then
+ echo ${THISCMD}: INFO: $*
+ fi
+}
+
+# get_nextuid
+# Output the value of $_uid if it is available for use. If it
+# is not, output the value of the next higher uid that is available.
+# If a uid is not specified, output the first available uid, as indicated
+# by pw(8).
+#
+get_nextuid () {
+ _uid=$1
+ _nextuid=
+
+ if [ -z "$_uid" ]; then
+ _nextuid="`${PWCMD} usernext | cut -f1 -d:`"
+ else
+ while : ; do
+ ${PWCMD} usershow $_uid > /dev/null 2>&1
+ if [ ! "$?" -eq 0 ]; then
+ _nextuid=$_uid
+ break
+ fi
+ _uid=$(($_uid + 1))
+ done
+ fi
+ echo $_nextuid
+}
+
+# show_usage
+# Display usage information for this utility.
+#
+show_usage() {
+ echo "usage: ${THISCMD} [options]"
+ echo " options may include:"
+ echo " -C save to the configuration file only"
+ echo " -D do not attempt to create the home directory"
+ echo " -E disable this account after creation"
+ echo " -G additional groups to add accounts to"
+ echo " -L login class of the user"
+ echo " -M file permission for home directory"
+ echo " -N do not read configuration file"
+ echo " -S a nonexistent shell is not an error"
+ echo " -d home directory"
+ echo " -f file from which input will be received"
+ echo " -g default login group"
+ echo " -h display this usage message"
+ echo " -k path to skeleton home directory"
+ echo " -m user welcome message file"
+ echo " -q absolute minimal user feedback"
+ echo " -s shell"
+ echo " -u uid to start at"
+ echo " -w password type: no, none, yes or random"
+}
+
+# valid_shells
+# Outputs a list of valid shells from /etc/shells. Only the
+# basename of the shell is output.
+#
+valid_shells() {
+ _prefix=
+ cat ${ETCSHELLS} |
+ while read _path _junk ; do
+ case $_path in
+ \#*|'')
+ ;;
+ *)
+ echo -n "${_prefix}`basename $_path`"
+ _prefix=' '
+ ;;
+ esac
+ done
+
+ # /usr/sbin/nologin is a special case
+ [ -x "${NOLOGIN_PATH}" ] && echo -n " ${NOLOGIN}"
+}
+
+# fullpath_from_shell shell
+# Given $shell, which is either the full path to a shell or
+# the basename component of a valid shell, get the
+# full path to the shell from the /etc/shells file.
+#
+fullpath_from_shell() {
+ _shell=$1
+ [ -z "$_shell" ] && return 1
+
+ # /usr/sbin/nologin is a special case; it needs to be handled
+ # before the cat | while loop, since a 'return' from within
+ # a subshell will not terminate the function's execution, and
+ # the path to the nologin shell might be printed out twice.
+ #
+ if [ "$_shell" = "${NOLOGIN}" -o \
+ "$_shell" = "${NOLOGIN_PATH}" ]; then
+ echo ${NOLOGIN_PATH}
+ return 0;
+ fi
+
+ cat ${ETCSHELLS} |
+ while read _path _junk ; do
+ case "$_path" in
+ \#*|'')
+ ;;
+ *)
+ if [ "$_path" = "$_shell" -o \
+ "`basename $_path`" = "$_shell" ]; then
+ echo $_path
+ return 0
+ fi
+ ;;
+ esac
+ done
+
+ return 1
+}
+
+# shell_exists shell
+# If the given shell is listed in ${ETCSHELLS} or it is
+# the nologin shell this function will return 0.
+# Otherwise, it will return 1. If shell is valid but
+# the path is invalid or it is not executable it
+# will emit an informational message saying so.
+#
+shell_exists()
+{
+ _sh="$1"
+ _shellchk="${GREPCMD} '^$_sh$' ${ETCSHELLS} > /dev/null 2>&1"
+
+ if ! eval $_shellchk; then
+ # The nologin shell is not listed in /etc/shells.
+ if [ "$_sh" != "${NOLOGIN_PATH}" ]; then
+ err "Invalid shell ($_sh) for user $username."
+ return 1
+ fi
+ fi
+ ! [ -x "$_sh" ] &&
+ info "The shell ($_sh) does not exist or is not executable."
+
+ return 0
+}
+
+# save_config
+# Save some variables to a configuration file.
+# Note: not all script variables are saved, only those that
+# it makes sense to save.
+#
+save_config() {
+ echo "# Configuration file for adduser(8)." > ${ADDUSERCONF}
+ echo "# NOTE: only *some* variables are saved." >> ${ADDUSERCONF}
+ echo "# Last Modified on `${DATECMD}`." >> ${ADDUSERCONF}
+ echo '' >> ${ADDUSERCONF}
+ echo "defaultHomePerm=$uhomeperm" >> ${ADDUSERCONF}
+ echo "defaultLgroup=$ulogingroup" >> ${ADDUSERCONF}
+ echo "defaultclass=$uclass" >> ${ADDUSERCONF}
+ echo "defaultgroups=$ugroups" >> ${ADDUSERCONF}
+ echo "passwdtype=$passwdtype" >> ${ADDUSERCONF}
+ echo "homeprefix=$homeprefix" >> ${ADDUSERCONF}
+ echo "defaultshell=$ushell" >> ${ADDUSERCONF}
+ echo "udotdir=$udotdir" >> ${ADDUSERCONF}
+ echo "msgfile=$msgfile" >> ${ADDUSERCONF}
+ echo "disableflag=$disableflag" >> ${ADDUSERCONF}
+ echo "uidstart=$uidstart" >> ${ADDUSERCONF}
+}
+
+# add_user
+# Add a user to the user database. If the user chose to send a welcome
+# message or lock the account, do so.
+#
+add_user() {
+
+ # Is this a configuration run? If so, don't modify user database.
+ #
+ if [ -n "$configflag" ]; then
+ save_config
+ return
+ fi
+
+ _uid=
+ _name=
+ _comment=
+ _gecos=
+ _home=
+ _group=
+ _grouplist=
+ _shell=
+ _class=
+ _dotdir=
+ _expire=
+ _pwexpire=
+ _passwd=
+ _upasswd=
+ _passwdmethod=
+
+ _name="-n '$username'"
+ [ -n "$uuid" ] && _uid='-u "$uuid"'
+ [ -n "$ulogingroup" ] && _group='-g "$ulogingroup"'
+ [ -n "$ugroups" ] && _grouplist='-G "$ugroups"'
+ [ -n "$ushell" ] && _shell='-s "$ushell"'
+ [ -n "$uclass" ] && _class='-L "$uclass"'
+ [ -n "$ugecos" ] && _comment='-c "$ugecos"'
+ [ -n "$udotdir" ] && _dotdir='-k "$udotdir"'
+ [ -n "$uexpire" ] && _expire='-e "$uexpire"'
+ [ -n "$upwexpire" ] && _pwexpire='-p "$upwexpire"'
+ if [ -z "$Dflag" -a -n "$uhome" ]; then
+ # The /nonexistent home directory is special. It
+ # means the user has no home directory.
+ if [ "$uhome" = "$NOHOME" ]; then
+ _home='-d "$uhome"'
+ else
+ # Use home directory permissions if specified
+ if [ -n "$uhomeperm" ]; then
+ _home='-m -d "$uhome" -M "$uhomeperm"'
+ else
+ _home='-m -d "$uhome"'
+ fi
+ fi
+ elif [ -n "$Dflag" -a -n "$uhome" ]; then
+ _home='-d "$uhome"'
+ fi
+ case $passwdtype in
+ no)
+ _passwdmethod="-w no"
+ _passwd="-h -"
+ ;;
+ yes)
+ # Note on processing the password: The outer double quotes
+ # make literal everything except ` and \ and $.
+ # The outer single quotes make literal ` and $.
+ # We can ensure the \ isn't treated specially by specifying
+ # the -r switch to the read command used to obtain the input.
+ #
+ _passwdmethod="-w yes"
+ _passwd="-h 0"
+ _upasswd='echo "$upass" |'
+ ;;
+ none)
+ _passwdmethod="-w none"
+ ;;
+ random)
+ _passwdmethod="-w random"
+ ;;
+ esac
+
+ _pwcmd="$_upasswd ${PWCMD} useradd $_uid $_name $_group $_grouplist $_comment"
+ _pwcmd="$_pwcmd $_shell $_class $_home $_dotdir $_passwdmethod $_passwd"
+ _pwcmd="$_pwcmd $_expire $_pwexpire"
+
+ if ! _output=`eval $_pwcmd` ; then
+ err "There was an error adding user ($username)."
+ return 1
+ else
+ info "Successfully added ($username) to the user database."
+ if [ "random" = "$passwdtype" ]; then
+ randompass="$_output"
+ info "Password for ($username) is: $randompass"
+ fi
+ fi
+
+ if [ -n "$disableflag" ]; then
+ if ${PWCMD} lock $username ; then
+ info "Account ($username) is locked."
+ else
+ info "Account ($username) could NOT be locked."
+ fi
+ fi
+
+ _line=
+ _owner=
+ _perms=
+ if [ -n "$msgflag" ]; then
+ [ -r "$msgfile" ] && {
+ # We're evaluating the contents of an external file.
+ # Let's not open ourselves up for attack. _perms will
+ # be empty if it's writeable only by the owner. _owner
+ # will *NOT* be empty if the file is owned by root.
+ #
+ _dir="`dirname $msgfile`"
+ _file="`basename $msgfile`"
+ _perms=`/usr/bin/find $_dir -name $_file -perm +07022 -prune`
+ _owner=`/usr/bin/find $_dir -name $_file -user 0 -prune`
+ if [ -z "$_owner" -o -n "$_perms" ]; then
+ err "The message file ($msgfile) may be writeable only by root."
+ return 1
+ fi
+ cat "$msgfile" |
+ while read _line ; do
+ eval echo "$_line"
+ done | ${MAILCMD} -s"Welcome" ${username}
+ info "Sent welcome message to ($username)."
+ }
+ fi
+}
+
+# get_user
+# Reads username of the account from standard input or from a global
+# variable containing an account line from a file. The username is
+# required. If this is an interactive session it will prompt in
+# a loop until a username is entered. If it is batch processing from
+# a file it will output an error message and return to the caller.
+#
+get_user() {
+ _input=
+
+ # No need to take down user names if this is a configuration saving run.
+ [ -n "$configflag" ] && return
+
+ while : ; do
+ if [ -z "$fflag" ]; then
+ echo -n "Username: "
+ read _input
+ else
+ _input="`echo "$fileline" | cut -f1 -d:`"
+ fi
+
+ # There *must* be a username, and it must not exist. If
+ # this is an interactive session give the user an
+ # opportunity to retry.
+ #
+ if [ -z "$_input" ]; then
+ err "You must enter a username!"
+ [ -z "$fflag" ] && continue
+ fi
+ ${PWCMD} usershow $_input > /dev/null 2>&1
+ if [ "$?" -eq 0 ]; then
+ err "User exists!"
+ [ -z "$fflag" ] && continue
+ fi
+ break
+ done
+ username="$_input"
+}
+
+# get_gecos
+# Reads extra information about the user. Can be used both in interactive
+# and batch (from file) mode.
+#
+get_gecos() {
+ _input=
+
+ # No need to take down additional user information for a configuration run.
+ [ -n "$configflag" ] && return
+
+ if [ -z "$fflag" ]; then
+ echo -n "Full name: "
+ read _input
+ else
+ _input="`echo "$fileline" | cut -f7 -d:`"
+ fi
+ ugecos="$_input"
+}
+
+# get_shell
+# Get the account's shell. Works in interactive and batch mode. It
+# accepts either the base name of the shell or the full path.
+# If an invalid shell is entered it will simply use the default shell.
+#
+get_shell() {
+ _input=
+ _fullpath=
+ ushell="$defaultshell"
+
+ # Make sure the current value of the shell is a valid one
+ if [ -z "$Sflag" ]; then
+ if ! shell_exists $ushell ; then
+ info "Using default shell ${defaultshell}."
+ ushell="$defaultshell"
+ fi
+ fi
+
+ if [ -z "$fflag" ]; then
+ echo -n "Shell ($shells) [`basename $ushell`]: "
+ read _input
+ else
+ _input="`echo "$fileline" | cut -f9 -d:`"
+ fi
+ if [ -n "$_input" ]; then
+ if [ -n "$Sflag" ]; then
+ ushell="$_input"
+ else
+ _fullpath=`fullpath_from_shell $_input`
+ if [ -n "$_fullpath" ]; then
+ ushell="$_fullpath"
+ else
+ err "Invalid shell ($_input) for user $username."
+ info "Using default shell ${defaultshell}."
+ ushell="$defaultshell"
+ fi
+ fi
+ fi
+}
+
+# get_homedir
+# Reads the account's home directory. Used both with interactive input
+# and batch input.
+#
+get_homedir() {
+ _input=
+ if [ -z "$fflag" ]; then
+ echo -n "Home directory [${homeprefix}/${username}]: "
+ read _input
+ else
+ _input="`echo "$fileline" | cut -f8 -d:`"
+ fi
+
+ if [ -n "$_input" ]; then
+ uhome="$_input"
+ # if this is a configuration run, then user input is the home
+ # directory prefix. Otherwise it is understood to
+ # be $prefix/$user
+ #
+ [ -z "$configflag" ] && homeprefix="`dirname $uhome`" || homeprefix="$uhome"
+ else
+ uhome="${homeprefix}/${username}"
+ fi
+}
+
+# get_homeperm
+# Reads the account's home directory permissions.
+#
+get_homeperm() {
+ uhomeperm=$defaultHomePerm
+ _input=
+ _prompt=
+
+ if [ -n "$uhomeperm" ]; then
+ _prompt="Home directory permissions [${uhomeperm}]: "
+ else
+ _prompt="Home directory permissions (Leave empty for default): "
+ fi
+ if [ -z "$fflag" ]; then
+ echo -n "$_prompt"
+ read _input
+ fi
+
+ if [ -n "$_input" ]; then
+ uhomeperm="$_input"
+ fi
+}
+
+# get_uid
+# Reads a numeric userid in an interactive or batch session. Automatically
+# allocates one if it is not specified.
+#
+get_uid() {
+ uuid=${uidstart}
+ _input=
+ _prompt=
+
+ if [ -n "$uuid" ]; then
+ _prompt="Uid [$uuid]: "
+ else
+ _prompt="Uid (Leave empty for default): "
+ fi
+ if [ -z "$fflag" ]; then
+ echo -n "$_prompt"
+ read _input
+ else
+ _input="`echo "$fileline" | cut -f2 -d:`"
+ fi
+
+ [ -n "$_input" ] && uuid=$_input
+ uuid=`get_nextuid $uuid`
+ uidstart=$uuid
+}
+
+# get_class
+# Reads login class of account. Can be used in interactive or batch mode.
+#
+get_class() {
+ uclass="$defaultclass"
+ _input=
+ _class=${uclass:-"default"}
+
+ if [ -z "$fflag" ]; then
+ echo -n "Login class [$_class]: "
+ read _input
+ else
+ _input="`echo "$fileline" | cut -f4 -d:`"
+ fi
+
+ [ -n "$_input" ] && uclass="$_input"
+}
+
+# get_logingroup
+# Reads user's login group. Can be used in both interactive and batch
+# modes. The specified value can be a group name or its numeric id.
+# This routine leaves the field blank if nothing is provided and
+# a default login group has not been set. The pw(8) command
+# will then provide a login group with the same name as the username.
+#
+get_logingroup() {
+ ulogingroup="$defaultLgroup"
+ _input=
+
+ if [ -z "$fflag" ]; then
+ echo -n "Login group [${ulogingroup:-$username}]: "
+ read _input
+ else
+ _input="`echo "$fileline" | cut -f3 -d:`"
+ fi
+
+ # Pw(8) will use the username as login group if it's left empty
+ [ -n "$_input" ] && ulogingroup="$_input"
+}
+
+# get_groups
+# Read additional groups for the user. It can be used in both interactive
+# and batch modes.
+#
+get_groups() {
+ ugroups="$defaultgroups"
+ _input=
+ _group=${ulogingroup:-"${username}"}
+
+ if [ -z "$configflag" ]; then
+ [ -z "$fflag" ] && echo -n "Login group is $_group. Invite $username"
+ [ -z "$fflag" ] && echo -n " into other groups? [$ugroups]: "
+ else
+ [ -z "$fflag" ] && echo -n "Enter additional groups [$ugroups]: "
+ fi
+ read _input
+
+ [ -n "$_input" ] && ugroups="$_input"
+}
+
+# get_expire_dates
+# Read expiry information for the account and also for the password. This
+# routine is used only from batch processing mode.
+#
+get_expire_dates() {
+ upwexpire="`echo "$fileline" | cut -f5 -d:`"
+ uexpire="`echo "$fileline" | cut -f6 -d:`"
+}
+
+# get_password
+# Read the password in batch processing mode. The password field matters
+# only when the password type is "yes" or "random". If the field is empty and the
+# password type is "yes", then it assumes the account has an empty passsword
+# and changes the password type accordingly. If the password type is "random"
+# and the password field is NOT empty, then it assumes the account will NOT
+# have a random password and set passwdtype to "yes."
+#
+get_password() {
+ # We may temporarily change a password type. Make sure it's changed
+ # back to whatever it was before we process the next account.
+ #
+ [ -n "$savedpwtype" ] && {
+ passwdtype=$savedpwtype
+ savedpwtype=
+ }
+
+ # There may be a ':' in the password
+ upass=${fileline#*:*:*:*:*:*:*:*:*:}
+
+ if [ -z "$upass" ]; then
+ case $passwdtype in
+ yes)
+ # if it's empty, assume an empty password
+ passwdtype=none
+ savedpwtype=yes
+ ;;
+ esac
+ else
+ case $passwdtype in
+ random)
+ passwdtype=yes
+ savedpwtype=random
+ ;;
+ esac
+ fi
+}
+
+# input_from_file
+# Reads a line of account information from standard input and
+# adds it to the user database.
+#
+input_from_file() {
+ _field=
+
+ while read -r fileline ; do
+ case "$fileline" in
+ \#*|'')
+ ;;
+ *)
+ get_user || continue
+ get_gecos
+ get_uid
+ get_logingroup
+ get_class
+ get_shell
+ get_homedir
+ get_homeperm
+ get_password
+ get_expire_dates
+ ugroups="$defaultgroups"
+
+ add_user
+ ;;
+ esac
+ done
+}
+
+# input_interactive
+# Prompts for user information interactively, and commits to
+# the user database.
+#
+input_interactive() {
+
+ _disable=
+ _pass=
+ _passconfirm=
+ _random="no"
+ _emptypass="no"
+ _usepass="yes"
+ _logingroup_ok="no"
+ _groups_ok="no"
+ case $passwdtype in
+ none)
+ _emptypass="yes"
+ _usepass="yes"
+ ;;
+ no)
+ _usepass="no"
+ ;;
+ random)
+ _random="yes"
+ ;;
+ esac
+
+ get_user
+ get_gecos
+ get_uid
+
+ # The case where group = user is handled elsewhere, so
+ # validate any other groups the user is invited to.
+ until [ "$_logingroup_ok" = yes ]; do
+ get_logingroup
+ _logingroup_ok=yes
+ if [ -n "$ulogingroup" -a "$username" != "$ulogingroup" ]; then
+ if ! ${PWCMD} show group $ulogingroup > /dev/null 2>&1; then
+ echo "Group $ulogingroup does not exist!"
+ _logingroup_ok=no
+ fi
+ fi
+ done
+ until [ "$_groups_ok" = yes ]; do
+ get_groups
+ _groups_ok=yes
+ for i in $ugroups; do
+ if [ "$username" != "$i" ]; then
+ if ! ${PWCMD} show group $i > /dev/null 2>&1; then
+ echo "Group $i does not exist!"
+ _groups_ok=no
+ fi
+ fi
+ done
+ done
+
+ get_class
+ get_shell
+ get_homedir
+ get_homeperm
+
+ while : ; do
+ echo -n "Use password-based authentication? [$_usepass]: "
+ read _input
+ [ -z "$_input" ] && _input=$_usepass
+ case $_input in
+ [Nn][Oo]|[Nn])
+ passwdtype="no"
+ ;;
+ [Yy][Ee][Ss]|[Yy][Ee]|[Yy])
+ while : ; do
+ echo -n "Use an empty password? (yes/no) [$_emptypass]: "
+ read _input
+ [ -n "$_input" ] && _emptypass=$_input
+ case $_emptypass in
+ [Nn][Oo]|[Nn])
+ echo -n "Use a random password? (yes/no) [$_random]: "
+ read _input
+ [ -n "$_input" ] && _random="$_input"
+ case $_random in
+ [Yy][Ee][Ss]|[Yy][Ee]|[Yy])
+ passwdtype="random"
+ break
+ ;;
+ esac
+ passwdtype="yes"
+ [ -n "$configflag" ] && break
+ trap 'stty echo; exit' 0 1 2 3 15
+ stty -echo
+ echo -n "Enter password: "
+ read -r upass
+ echo''
+ echo -n "Enter password again: "
+ read -r _passconfirm
+ echo ''
+ stty echo
+ # if user entered a blank password
+ # explicitly ask again.
+ [ -z "$upass" -a -z "$_passconfirm" ] \
+ && continue
+ ;;
+ [Yy][Ee][Ss]|[Yy][Ee]|[Yy])
+ passwdtype="none"
+ break;
+ ;;
+ *)
+ # invalid answer; repeat the loop
+ continue
+ ;;
+ esac
+ if [ "$upass" != "$_passconfirm" ]; then
+ echo "Passwords did not match!"
+ continue
+ fi
+ break
+ done
+ ;;
+ *)
+ # invalid answer; repeat loop
+ continue
+ ;;
+ esac
+ break;
+ done
+ _disable=${disableflag:-"no"}
+ while : ; do
+ echo -n "Lock out the account after creation? [$_disable]: "
+ read _input
+ [ -z "$_input" ] && _input=$_disable
+ case $_input in
+ [Nn][Oo]|[Nn])
+ disableflag=
+ ;;
+ [Yy][Ee][Ss]|[Yy][Ee]|[Yy])
+ disableflag=yes
+ ;;
+ *)
+ # invalid answer; repeat loop
+ continue
+ ;;
+ esac
+ break
+ done
+
+ # Display the information we have so far and prompt to
+ # commit it.
+ #
+ _disable=${disableflag:-"no"}
+ [ -z "$configflag" ] && printf "%-10s : %s\n" Username $username
+ case $passwdtype in
+ yes)
+ _pass='*****'
+ ;;
+ no)
+ _pass='<disabled>'
+ ;;
+ none)
+ _pass='<blank>'
+ ;;
+ random)
+ _pass='<random>'
+ ;;
+ esac
+ [ -z "$configflag" ] && printf "%-10s : %s\n" "Password" "$_pass"
+ [ -n "$configflag" ] && printf "%-10s : %s\n" "Pass Type" "$passwdtype"
+ [ -z "$configflag" ] && printf "%-10s : %s\n" "Full Name" "$ugecos"
+ [ -z "$configflag" ] && printf "%-10s : %s\n" "Uid" "$uuid"
+ printf "%-10s : %s\n" "Class" "$uclass"
+ printf "%-10s : %s %s\n" "Groups" "${ulogingroup:-$username}" "$ugroups"
+ printf "%-10s : %s\n" "Home" "$uhome"
+ printf "%-10s : %s\n" "Home Mode" "$uhomeperm"
+ printf "%-10s : %s\n" "Shell" "$ushell"
+ printf "%-10s : %s\n" "Locked" "$_disable"
+ while : ; do
+ echo -n "OK? (yes/no): "
+ read _input
+ case $_input in
+ [Nn][Oo]|[Nn])
+ return 1
+ ;;
+ [Yy][Ee][Ss]|[Yy][Ee]|[Yy])
+ add_user
+ ;;
+ *)
+ continue
+ ;;
+ esac
+ break
+ done
+ return 0
+}
+
+#### END SUBROUTINE DEFINITION ####
+
+THISCMD=`/usr/bin/basename $0`
+DEFAULTSHELL=/bin/sh
+ADDUSERCONF="${ADDUSERCONF:-/etc/adduser.conf}"
+PWCMD="${PWCMD:-/usr/sbin/pw}"
+MAILCMD="${MAILCMD:-mail}"
+ETCSHELLS="${ETCSHELLS:-/etc/shells}"
+NOHOME="/nonexistent"
+NOLOGIN="nologin"
+NOLOGIN_PATH="/usr/sbin/nologin"
+GREPCMD="/usr/bin/grep"
+DATECMD="/bin/date"
+
+# Set default values
+#
+username=
+uuid=
+uidstart=
+ugecos=
+ulogingroup=
+uclass=
+uhome=
+uhomeperm=
+upass=
+ushell=
+udotdir=/usr/share/skel
+ugroups=
+uexpire=
+upwexpire=
+shells="`valid_shells`"
+passwdtype="yes"
+msgfile=/etc/adduser.msg
+msgflag=
+quietflag=
+configflag=
+fflag=
+infile=
+disableflag=
+Dflag=
+Sflag=
+readconfig="yes"
+homeprefix="/home"
+randompass=
+fileline=
+savedpwtype=
+defaultclass=
+defaultLgroup=
+defaultgroups=
+defaultshell="${DEFAULTSHELL}"
+defaultHomePerm=
+
+# Make sure the user running this program is root. This isn't a security
+# measure as much as it is a usefull method of reminding the user to
+# 'su -' before he/she wastes time entering data that won't be saved.
+#
+procowner=${procowner:-`/usr/bin/id -u`}
+if [ "$procowner" != "0" ]; then
+ err 'you must be the super-user (uid 0) to use this utility.'
+ exit 1
+fi
+
+# Overide from our conf file
+# Quickly go through the commandline line to see if we should read
+# from our configuration file. The actual parsing of the commandline
+# arguments happens after we read in our configuration file (commandline
+# should override configuration file).
+#
+for _i in $* ; do
+ if [ "$_i" = "-N" ]; then
+ readconfig=
+ break;
+ fi
+done
+if [ -n "$readconfig" ]; then
+ # On a long-lived system, the first time this script is run it
+ # will barf upon reading the configuration file for its perl predecessor.
+ if ( . ${ADDUSERCONF} > /dev/null 2>&1 ); then
+ [ -r ${ADDUSERCONF} ] && . ${ADDUSERCONF} > /dev/null 2>&1
+ fi
+fi
+
+# Proccess command-line options
+#
+for _switch ; do
+ case $_switch in
+ -L)
+ defaultclass="$2"
+ shift; shift
+ ;;
+ -C)
+ configflag=yes
+ shift
+ ;;
+ -D)
+ Dflag=yes
+ shift
+ ;;
+ -E)
+ disableflag=yes
+ shift
+ ;;
+ -k)
+ udotdir="$2"
+ shift; shift
+ ;;
+ -f)
+ [ "$2" != "-" ] && infile="$2"
+ fflag=yes
+ shift; shift
+ ;;
+ -g)
+ defaultLgroup="$2"
+ shift; shift
+ ;;
+ -G)
+ defaultgroups="$2"
+ shift; shift
+ ;;
+ -h)
+ show_usage
+ exit 0
+ ;;
+ -d)
+ homeprefix="$2"
+ shift; shift
+ ;;
+ -m)
+ case "$2" in
+ [Nn][Oo])
+ msgflag=
+ ;;
+ *)
+ msgflag=yes
+ msgfile="$2"
+ ;;
+ esac
+ shift; shift
+ ;;
+ -M)
+ defaultHomePerm=$2
+ shift; shift
+ ;;
+ -N)
+ readconfig=
+ shift
+ ;;
+ -w)
+ case "$2" in
+ no|none|random|yes)
+ passwdtype=$2
+ ;;
+ *)
+ show_usage
+ exit 1
+ ;;
+ esac
+ shift; shift
+ ;;
+ -q)
+ quietflag=yes
+ shift
+ ;;
+ -s)
+ defaultshell="`fullpath_from_shell $2`"
+ shift; shift
+ ;;
+ -S)
+ Sflag=yes
+ shift
+ ;;
+ -u)
+ uidstart=$2
+ shift; shift
+ ;;
+ esac
+done
+
+# If the -f switch was used, get input from a file. Otherwise,
+# this is an interactive session.
+#
+if [ -n "$fflag" ]; then
+ if [ -z "$infile" ]; then
+ input_from_file
+ elif [ -n "$infile" ]; then
+ if [ -r "$infile" ]; then
+ input_from_file < $infile
+ else
+ err "File ($infile) is unreadable or does not exist."
+ fi
+ fi
+else
+ input_interactive
+ while : ; do
+ if [ -z "$configflag" ]; then
+ echo -n "Add another user? (yes/no): "
+ else
+ echo -n "Re-edit the default configuration? (yes/no): "
+ fi
+ read _input
+ case $_input in
+ [Yy][Ee][Ss]|[Yy][Ee]|[Yy])
+ uidstart=`get_nextuid $uidstart`
+ input_interactive
+ continue
+ ;;
+ [Nn][Oo]|[Nn])
+ echo "Goodbye!"
+ ;;
+ *)
+ continue
+ ;;
+ esac
+ break
+ done
+fi
diff --git a/usr.sbin/adduser/rmuser.8 b/usr.sbin/adduser/rmuser.8
new file mode 100644
index 0000000..68a99b5
--- /dev/null
+++ b/usr.sbin/adduser/rmuser.8
@@ -0,0 +1,210 @@
+.\" Copyright 1995, 1996, 1997
+.\" Guy Helmer, Ames, Iowa 50014. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer as
+.\" the first lines of this file unmodified.
+.\" 2. Redistributions in binary form must 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 GUY HELMER ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL GUY HELMER BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (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 10, 2002
+.Dt RMUSER 8
+.Os
+.Sh NAME
+.Nm rmuser
+.Nd remove users from the system
+.Sh SYNOPSIS
+.Nm
+.Op Fl yv
+.Op Fl f Ar file
+.Op Ar username ...
+.Sh DESCRIPTION
+The
+.Nm
+utility removes one or more users submitted on the command line
+or from a file.
+In removing a user from the system, this utility:
+.Bl -enum
+.It
+Removes the user's
+.Xr crontab 1
+entry (if any).
+.It
+Removes any
+.Xr at 1
+jobs belonging to the user.
+.It
+Sends a
+.Dv SIGKILL
+signal to all processes owned by the user.
+.It
+Removes the user from the system's local password file.
+.It
+Removes the user's home directory (if it is owned by the user),
+including handling of symbolic links in the path to the actual home
+directory.
+.It
+Removes the incoming mail and POP daemon mail files belonging to the
+user from
+.Pa /var/mail .
+.It
+Removes all files owned by the user from
+.Pa /tmp , /var/tmp ,
+and
+.Pa /var/tmp/vi.recover .
+.It
+Removes the username from all groups to which it belongs in
+.Pa /etc/group .
+(If a group becomes empty and the group name is the same as the username,
+the group is removed; this complements
+.Xr adduser 8 Ns 's
+per-user unique groups.)
+.It
+Removes all message queues, shared memory segments and
+semaphores owned by the user.
+.El
+.Pp
+The
+.Nm
+utility refuses to remove users whose UID is 0 (typically root), since
+certain actions (namely, killing all the user's processes, and perhaps
+removing the user's home directory) would cause damage to a running system.
+If it is necessary to remove a user whose UID is 0, see
+.Xr vipw 8
+for information on directly editing the password file.
+.Pp
+If
+.Nm
+was not invoked with the
+.Fl y
+option, it will
+show the selected user's password file entry and ask for confirmation
+that the user be removed.
+It will then ask for confirmation to delete
+the user's home directory.
+If the answer is in the affirmative, the home
+directory and any files and subdirectories under it will be deleted only if
+they are owned by the user.
+See
+.Xr pw 8
+for more details.
+.Pp
+As
+.Nm
+operates, it informs the user regarding the current activity.
+If any
+errors occur, they are posted to standard error and, if it is possible for
+.Nm
+to continue, it will.
+.Pp
+The options are as follows:
+.Bl -tag -width ".Ar username"
+.It Fl f Ar file
+The
+.Nm
+utility will get a list of users to be removed from
+.Ar file ,
+which will contain one user per line.
+Anything following a hash mark
+.Pq Ql # ,
+including the hash mark itself, is considered a comment and will not
+be processed.
+If the file is owned by anyone other than a user with
+UID 0, or is writable by anyone other than the owner,
+.Nm
+will refuse to continue.
+.It Fl y
+Implicitly answer
+.Dq Li yes
+to any and all prompts.
+Currently, this includes
+prompts on whether to remove the specified user and whether to remove
+the home directory.
+This option requires that either the
+.Fl f
+option be used, or one or more user names be given as command line
+arguments.
+.It Fl v
+Enable verbose mode.
+Normally,
+the output includes one line per removed user;
+however,
+with this option
+.Nm
+will be much more chatty about the steps taken.
+.It Ar username
+Identifies one or more users to be removed; if not present,
+.Nm
+interactively asks for one or more users to be removed.
+.El
+.Sh FILES
+.Bl -tag -compact
+.It Pa /etc/master.passwd
+.It Pa /etc/passwd
+.It Pa /etc/group
+.It Pa /etc/spwd.db
+.It Pa /etc/pwd.db
+.El
+.Sh SEE ALSO
+.Xr at 1 ,
+.Xr chpass 1 ,
+.Xr crontab 1 ,
+.Xr finger 1 ,
+.Xr passwd 1 ,
+.Xr group 5 ,
+.Xr passwd 5 ,
+.Xr adduser 8 ,
+.Xr pw 8 ,
+.Xr pwd_mkdb 8 ,
+.Xr vipw 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 2.2 .
+.Sh BUGS
+The
+.Nm
+utility does not comprehensively search the file system for all files
+owned by the removed user and remove them; to do so on a system
+of any size is prohibitively slow and I/O intensive.
+It is also unable to remove symbolic links that were created by the
+user in
+.Pa /tmp
+or
+.Pa /var/tmp ,
+as symbolic links on
+.Bx 4.4
+file systems do not contain information
+as to who created them.
+Also, there may be other files created in
+.Pa /var/mail
+other than
+.Pa /var/mail/ Ns Ar username
+and
+.Pa /var/mail/.pop. Ns Ar username
+that are not owned by the removed user but should be removed.
+.Pp
+The
+.Nm
+utility has no knowledge of YP/NIS, and it operates only on the
+local password file.
diff --git a/usr.sbin/adduser/rmuser.sh b/usr.sbin/adduser/rmuser.sh
new file mode 100644
index 0000000..6b09225
--- /dev/null
+++ b/usr.sbin/adduser/rmuser.sh
@@ -0,0 +1,361 @@
+#!/bin/sh
+#
+# Copyright (c) 2002, 2003 Michael Telahun Makonnen. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must 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.
+#
+# Email: Mike Makonnen <mtm@FreeBSD.Org>
+#
+# $FreeBSD$
+#
+
+ATJOBDIR="/var/at/jobs"
+CRONJOBDIR="/var/cron/tabs"
+MAILSPOOL="/var/mail"
+SIGKILL="-KILL"
+TEMPDIRS="/tmp /var/tmp"
+THISCMD=`/usr/bin/basename $0`
+PWCMD="${PWCMD:-/usr/sbin/pw}"
+
+# err msg
+# Display $msg on stderr.
+#
+err() {
+ echo 1>&2 ${THISCMD}: $*
+}
+
+# verbose
+# Returns 0 if verbose mode is set, 1 if it is not.
+#
+verbose() {
+ [ -n "$vflag" ] && return 0 || return 1
+}
+
+# rm_files login
+# Removes files or empty directories belonging to $login from various
+# temporary directories.
+#
+rm_files() {
+ # The argument is required
+ [ -n $1 ] && login=$1 || return
+
+ totalcount=0
+ for _dir in ${TEMPDIRS} ; do
+ filecount=0
+ if [ ! -d $_dir ]; then
+ err "$_dir is not a valid directory."
+ continue
+ fi
+ verbose && echo -n "Removing files owned by ($login) in $_dir:"
+ filecount=`find 2>/dev/null "$_dir" -user "$login" -delete -print |
+ wc -l | sed 's/ *//'`
+ verbose && echo " $filecount removed."
+ totalcount=$(($totalcount + $filecount))
+ done
+ ! verbose && [ $totalcount -ne 0 ] && echo -n " files($totalcount)"
+}
+
+# rm_mail login
+# Removes unix mail and pop daemon files belonging to the user
+# specified in the $login argument.
+#
+rm_mail() {
+ # The argument is required
+ [ -n $1 ] && login=$1 || return
+
+ verbose && echo -n "Removing mail spool(s) for ($login):"
+ if [ -f ${MAILSPOOL}/$login ]; then
+ verbose && echo -n " ${MAILSPOOL}/$login" ||
+ echo -n " mailspool"
+ rm ${MAILSPOOL}/$login
+ fi
+ if [ -f ${MAILSPOOL}/.${login}.pop ]; then
+ verbose && echo -n " ${MAILSPOOL}/.${login}.pop" ||
+ echo -n " pop3"
+ rm ${MAILSPOOL}/.${login}.pop
+ fi
+ verbose && echo '.'
+}
+
+# kill_procs login
+# Send a SIGKILL to all processes owned by $login.
+#
+kill_procs() {
+ # The argument is required
+ [ -n $1 ] && login=$1 || return
+
+ verbose && echo -n "Terminating all processes owned by ($login):"
+ killcount=0
+ proclist=`ps 2>/dev/null -U $login | grep -v '^\ *PID' | awk '{print $1}'`
+ for _pid in $proclist ; do
+ kill 2>/dev/null ${SIGKILL} $_pid
+ killcount=$(($killcount + 1))
+ done
+ verbose && echo " ${SIGKILL} signal sent to $killcount processes."
+ ! verbose && [ $killcount -ne 0 ] && echo -n " processes(${killcount})"
+}
+
+# rm_at_jobs login
+# Remove at (1) jobs belonging to $login.
+#
+rm_at_jobs() {
+ # The argument is required
+ [ -n $1 ] && login=$1 || return
+
+ atjoblist=`find 2>/dev/null ${ATJOBDIR} -maxdepth 1 -user $login -print`
+ jobcount=0
+ verbose && echo -n "Removing at(1) jobs owned by ($login):"
+ for _atjob in $atjoblist ; do
+ rm -f $_atjob
+ jobcount=$(($jobcount + 1))
+ done
+ verbose && echo " $jobcount removed."
+ ! verbose && [ $jobcount -ne 0 ] && echo -n " at($jobcount)"
+}
+
+# rm_crontab login
+# Removes crontab file belonging to user $login.
+#
+rm_crontab() {
+ # The argument is required
+ [ -n $1 ] && login=$1 || return
+
+ verbose && echo -n "Removing crontab for ($login):"
+ if [ -f ${CRONJOBDIR}/$login ]; then
+ verbose && echo -n " ${CRONJOBDIR}/$login" || echo -n " crontab"
+ rm -f ${CRONJOBDIR}/$login
+ fi
+ verbose && echo '.'
+}
+
+# rm_ipc login
+# Remove all IPC mechanisms which are owned by $login.
+#
+rm_ipc() {
+ verbose && echo -n "Removing IPC mechanisms"
+ for i in s m q; do
+ ipcs -$i |
+ awk -v i=$i -v login=$1 '$1 == i && $5 == login { print $2 }' |
+ xargs -n 1 ipcrm -$i
+ done
+ verbose && echo '.'
+}
+
+# rm_user login
+# Remove user $login from the system. This subroutine makes use
+# of the pw(8) command to remove a user from the system. The pw(8)
+# command will remove the specified user from the user database
+# and group file and remove any crontabs. His home
+# directory will be removed if it is owned by him and contains no
+# files or subdirectories owned by other users. Mail spool files will
+# also be removed.
+#
+rm_user() {
+ # The argument is required
+ [ -n $1 ] && login=$1 || return
+
+ verbose && echo -n "Removing user ($login)"
+ [ -n "$pw_rswitch" ] && {
+ verbose && echo -n " (including home directory)"
+ ! verbose && echo -n " home"
+ }
+ ! verbose && echo -n " passwd"
+ verbose && echo -n " from the system:"
+ ${PWCMD} userdel -n $login $pw_rswitch
+ verbose && echo ' Done.'
+}
+
+# prompt_yesno msg
+# Prompts the user with a $msg. The answer is expected to be
+# yes, no, or some variation thereof. This subroutine returns 0
+# if the answer was yes, 1 if it was not.
+#
+prompt_yesno() {
+ # The argument is required
+ [ -n "$1" ] && msg="$1" || return
+
+ while : ; do
+ echo -n "$msg"
+ read _ans
+ case $_ans in
+ [Nn][Oo]|[Nn])
+ return 1
+ ;;
+ [Yy][Ee][Ss]|[Yy][Ee]|[Yy])
+ return 0
+ ;;
+ *)
+ ;;
+ esac
+ done
+}
+
+# show_usage
+# (no arguments)
+# Display usage message.
+#
+show_usage() {
+ echo "usage: ${THISCMD} [-yv] [-f file] [user ...]"
+ echo " if the -y switch is used, either the -f switch or"
+ echo " one or more user names must be given"
+}
+
+#### END SUBROUTINE DEFENITION ####
+
+ffile=
+fflag=
+procowner=
+pw_rswitch=
+userlist=
+yflag=
+vflag=
+
+procowner=`/usr/bin/id -u`
+if [ "$procowner" != "0" ]; then
+ err 'you must be root (0) to use this utility.'
+ exit 1
+fi
+
+args=`getopt 2>/dev/null yvf: $*`
+if [ "$?" != "0" ]; then
+ show_usage
+ exit 1
+fi
+set -- $args
+for _switch ; do
+ case $_switch in
+ -y)
+ yflag=1
+ shift
+ ;;
+ -v)
+ vflag=1
+ shift
+ ;;
+ -f)
+ fflag=1
+ ffile="$2"
+ shift; shift
+ ;;
+ --)
+ shift
+ break
+ ;;
+ esac
+done
+
+# Get user names from a file if the -f switch was used. Otherwise,
+# get them from the commandline arguments. If we're getting it
+# from a file, the file must be owned by and writable only by root.
+#
+if [ $fflag ]; then
+ _insecure=`find $ffile ! -user 0 -or -perm +0022`
+ if [ -n "$_insecure" ]; then
+ err "file ($ffile) must be owned by and writeable only by root."
+ exit 1
+ fi
+ if [ -r "$ffile" ]; then
+ userlist=`cat $ffile | while read _user _junk ; do
+ case $_user in
+ \#*|'')
+ ;;
+ *)
+ echo -n "$userlist $_user"
+ ;;
+ esac
+ done`
+ fi
+else
+ while [ $1 ] ; do
+ userlist="$userlist $1"
+ shift
+ done
+fi
+
+# If the -y or -f switch has been used and the list of users to remove
+# is empty it is a fatal error. Otherwise, prompt the user for a list
+# of one or more user names.
+#
+if [ ! "$userlist" ]; then
+ if [ $fflag ]; then
+ err "($ffile) does not exist or does not contain any user names."
+ exit 1
+ elif [ $yflag ]; then
+ show_usage
+ exit 1
+ else
+ echo -n "Please enter one or more usernames: "
+ read userlist
+ fi
+fi
+
+_user=
+_uid=
+for _user in $userlist ; do
+ # Make sure the name exists in the passwd database and that it
+ # does not have a uid of 0
+ #
+ userrec=`pw 2>/dev/null usershow -n $_user`
+ if [ "$?" != "0" ]; then
+ err "user ($_user) does not exist in the password database."
+ continue
+ fi
+ _uid=`echo $userrec | awk -F: '{print $3}'`
+ if [ "$_uid" = "0" ]; then
+ err "user ($_user) has uid 0. You may not remove this user."
+ continue
+ fi
+
+ # If the -y switch was not used ask for confirmation to remove the
+ # user and home directory.
+ #
+ if [ -z "$yflag" ]; then
+ echo "Matching password entry:"
+ echo
+ echo $userrec
+ echo
+ if ! prompt_yesno "Is this the entry you wish to remove? " ; then
+ continue
+ fi
+ _homedir=`echo $userrec | awk -F: '{print $9}'`
+ if prompt_yesno "Remove user's home directory ($_homedir)? "; then
+ pw_rswitch="-r"
+ fi
+ else
+ pw_rswitch="-r"
+ fi
+
+ # Disable any further attempts to log into this account
+ ${PWCMD} 2>/dev/null lock $_user
+
+ # Remove crontab, mail spool, etc. Then obliterate the user from
+ # the passwd and group database.
+ #
+ ! verbose && echo -n "Removing user ($_user):"
+ rm_crontab $_user
+ rm_at_jobs $_user
+ rm_ipc $_user
+ kill_procs $_user
+ rm_files $_user
+ rm_mail $_user
+ rm_user $_user
+ ! verbose && echo "."
+done
diff --git a/usr.sbin/amd/Makefile b/usr.sbin/amd/Makefile
new file mode 100644
index 0000000..37e4200
--- /dev/null
+++ b/usr.sbin/amd/Makefile
@@ -0,0 +1,11 @@
+# ex:ts=8
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998
+#
+# $FreeBSD$
+
+SUBDIR= include libamu amd amq doc fixmount fsinfo hlfsd mk-amd-map pawd \
+ scripts wire-test
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/amd/Makefile.inc b/usr.sbin/amd/Makefile.inc
new file mode 100644
index 0000000..c8451f6
--- /dev/null
+++ b/usr.sbin/amd/Makefile.inc
@@ -0,0 +1,45 @@
+# ex:ts=8
+#
+# $FreeBSD$
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998.
+# Portions derived from
+# $NetBSD: Makefile.inc,v 1.10 1998/08/08 22:33:27 christos Exp $
+# Portions derived from amd/libamu/Makefile
+# $NetBSD: Makefile,v 1.8 1998/08/08 22:33:37 christos Exp $
+
+.include <bsd.own.mk>
+
+CFLAGS+= -I. -I${.CURDIR}
+CFLAGS+= -I${.CURDIR}/../include
+.if exists(${.OBJDIR}/../include)
+CFLAGS+= -I${.OBJDIR}/../include
+.endif
+CFLAGS+= -I${.CURDIR}/../../../contrib/amd/include
+CFLAGS+= -I${.CURDIR}/../../../contrib/amd
+CFLAGS+= -DHAVE_CONFIG_H
+.if ${MK_NIS} == "no"
+CFLAGS+= -DHAVE_LOCALCONFIG_H
+.endif
+
+.if ${MK_HESIOD} != "no"
+CFLAGS+= -DYES_HESIOD
+.endif
+
+CFLAGS+= -DHOST_CPU=\"${MACHINE_ARCH}\" -DHOST_ARCH=\"${MACHINE_ARCH}\"
+
+.if exists(${.OBJDIR}/../libamu)
+LIBAMUDIR= ${.OBJDIR}/../libamu
+.else
+LIBAMUDIR= ${.CURDIR}/../libamu
+.endif
+LIBAMU= ${LIBAMUDIR}/libamu.a
+
+RPCCOM= rpcgen
+MOUNT_X= ${DESTDIR}/usr/include/rpcsvc/mount.x
+NFS_PROT_X= ${DESTDIR}/usr/include/rpcsvc/nfs_prot.x
+
+.if exists(${.CURDIR}/../../Makefile.inc)
+.include "${.CURDIR}/../../Makefile.inc"
+.endif
diff --git a/usr.sbin/amd/NOTES b/usr.sbin/amd/NOTES
new file mode 100644
index 0000000..b04244b
--- /dev/null
+++ b/usr.sbin/amd/NOTES
@@ -0,0 +1,3 @@
+amd/amd supports HESIOD, LDAP, and NIS+ which we don't presently.
+If they are added to FreeBSD, add info_hesiod.c, info_ldap.c, info_nisplus.c
+to amd/amd's Makefile.
diff --git a/usr.sbin/amd/amd/Makefile b/usr.sbin/amd/amd/Makefile
new file mode 100644
index 0000000..c720003
--- /dev/null
+++ b/usr.sbin/amd/amd/Makefile
@@ -0,0 +1,53 @@
+# ex:ts=8
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998
+#
+# $FreeBSD$
+#
+
+.include <bsd.own.mk>
+
+.PATH: ${.CURDIR}/../../../contrib/amd/amd
+
+PROG= amd
+MAN= amd.8
+SRCS= conf_parse.y conf_tok.l
+SRCS+= am_ops.c amd.c amfs_auto.c amfs_direct.c amfs_error.c amfs_generic.c
+SRCS+= amfs_host.c amfs_link.c amfs_linkx.c amfs_nfsl.c
+SRCS+= amfs_nfsx.c amfs_program.c amfs_root.c amfs_toplvl.c
+SRCS+= amfs_union.c amq_subr.c amq_svc.c autil.c clock.c conf.c
+SRCS+= get_args.c info_exec.c info_file.c info_ndbm.c info_passwd.c
+SRCS+= info_union.c map.c mapc.c mntfs.c nfs_prot_svc.c nfs_start.c
+SRCS+= nfs_subr.c ops_cdfs.c ops_mfs.c ops_nfs.c ops_nfs3.c
+SRCS+= ops_nullfs.c ops_pcfs.c ops_tfs.c ops_ufs.c ops_umapfs.c
+SRCS+= ops_unionfs.c opts.c readdir.c restart.c rpc_fwd.c sched.c
+SRCS+= srvr_amfs_auto.c srvr_nfs.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/amd/amd \
+ -I${DESTDIR}/usr/include/rpcsvc
+
+DPADD= ${LIBAMU} ${LIBWRAP}
+LDADD= ${LIBAMU} -lwrap
+
+CLEANFILES+= conf_parse.c conf_parse.h conf_tok.c
+
+conf_tok.o: conf_parse.h
+
+# These are generated at compile time
+SRCS+= mount_xdr.c
+CLEANFILES+= mount_xdr.c
+
+mount_xdr.c: ${MOUNT_X}
+ ${RPCCOM} -c -DWANT_NFS3 ${MOUNT_X} -o ${.TARGET}
+
+.if ${MK_HESIOD} != "no"
+SRCS+= info_hesiod.c
+CFLAGS+= -DHAVE_MAP_HESIOD
+.endif
+
+.if ${MK_NIS} != "no"
+SRCS+= info_nis.c
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/amd/amq/Makefile b/usr.sbin/amd/amq/Makefile
new file mode 100644
index 0000000..74fc749
--- /dev/null
+++ b/usr.sbin/amd/amq/Makefile
@@ -0,0 +1,20 @@
+# ex:ts=8
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998
+#
+# $FreeBSD$
+#
+
+.PATH: ${.CURDIR}/../../../contrib/amd/amq
+
+PROG= amq
+MAN= amq.8
+SRCS= amq.c amq_clnt.c amq_xdr.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/amd/amq
+
+DPADD= ${LIBAMU}
+LDADD= ${LIBAMU}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/amd/doc/Makefile b/usr.sbin/amd/doc/Makefile
new file mode 100644
index 0000000..e9c7707
--- /dev/null
+++ b/usr.sbin/amd/doc/Makefile
@@ -0,0 +1,14 @@
+# This file is under a "BSD" copyright (c) by David O'Brien 1998.
+
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/amd/doc
+
+INFO= am-utils
+
+INFOSECTION= "AMD Documentation"
+INFOENTRY= "* Am-utils: (am-utils). The Amd automounter suite of utilities"
+
+MAKEINFOFLAGS+= -I ${.CURDIR}/../../../contrib/amd/doc
+
+.include <bsd.info.mk>
diff --git a/usr.sbin/amd/fixmount/Makefile b/usr.sbin/amd/fixmount/Makefile
new file mode 100644
index 0000000..7f96a45
--- /dev/null
+++ b/usr.sbin/amd/fixmount/Makefile
@@ -0,0 +1,21 @@
+# ex:ts=8
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998
+#
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/amd/fixmount \
+ ${.CURDIR}/../../../contrib/amd/conf/checkmount
+
+PROG= fixmount
+MAN= fixmount.8
+SRCS= fixmount.c
+
+# These would be links created by the GNU-style configure
+SRCS+= checkmount_bsd44.c
+
+DPADD= ${LIBAMU} ${LIBRPCSVC}
+LDADD= ${LIBAMU} -lrpcsvc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/amd/fsinfo/Makefile b/usr.sbin/amd/fsinfo/Makefile
new file mode 100644
index 0000000..1695a46
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/Makefile
@@ -0,0 +1,25 @@
+# ex:ts=8
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998
+#
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/amd/fsinfo
+
+PROG= fsinfo
+MAN= fsinfo.8
+SRCS= fsi_gram.y fsi_lex.l
+SRCS+= fsi_analyze.c fsi_dict.c fsi_util.c fsinfo.c wr_atab.c
+SRCS+= wr_bparam.c wr_dumpset.c wr_exportfs.c wr_fstab.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/amd/fsinfo
+
+DPADD= ${LIBAMU}
+LDADD= ${LIBAMU}
+
+CLEANFILES+= fsi_gram.c fsi_gram.h fsi_lex.c
+
+fsi_lex.o: fsi_gram.h
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/amd/hlfsd/Makefile b/usr.sbin/amd/hlfsd/Makefile
new file mode 100644
index 0000000..5b863dd
--- /dev/null
+++ b/usr.sbin/amd/hlfsd/Makefile
@@ -0,0 +1,19 @@
+# ex:ts=8
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998
+#
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/amd/hlfsd
+
+PROG= hlfsd
+MAN= hlfsd.8
+SRCS= hlfsd.c homedir.c nfs_prot_svc.c stubs.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/amd/hlfsd
+
+DPADD= ${LIBAMU}
+LDADD= ${LIBAMU}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/amd/include/Makefile b/usr.sbin/amd/include/Makefile
new file mode 100644
index 0000000..df770d1
--- /dev/null
+++ b/usr.sbin/amd/include/Makefile
@@ -0,0 +1,29 @@
+# ex:ts=8
+#
+# $FreeBSD$
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998.
+# Portions derived from amd/libamu/Makefile
+# $NetBSD: Makefile,v 1.8 1998/08/08 22:33:37 christos Exp $
+
+.include <bsd.own.mk>
+
+SRCS= config_local.h
+.if ${MK_NIS} == "no"
+SRCS+= localconfig.h
+.endif
+CLEANFILES= ${SRCS}
+
+all depend: ${SRCS}
+
+config_local.h: newvers.sh
+ @rm -f ${.TARGET}
+ sh ${.ALLSRC} ${.CURDIR}/../../../sys/conf/newvers.sh > ${.TARGET}
+
+localconfig.h:
+ @rm -f ${.TARGET}
+ @echo "/* NIS disabled by WITHOUT_NIS src.conf option */" >> ${.TARGET}
+ @echo "#undef HAVE_MAP_NIS" >> ${.TARGET}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/amd/include/amu_autofs_prot.h b/usr.sbin/amd/include/amu_autofs_prot.h
new file mode 100644
index 0000000..12f5b95
--- /dev/null
+++ b/usr.sbin/amd/include/amu_autofs_prot.h
@@ -0,0 +1,8 @@
+// $FreeBSD$
+
+/* Adjust once we have some form of Autofs support. */
+
+#if 0
+#include "conf/autofs/autofs_default.h"
+#endif
+
diff --git a/usr.sbin/amd/include/amu_nfs_prot.h b/usr.sbin/amd/include/amu_nfs_prot.h
new file mode 100644
index 0000000..8e8856f
--- /dev/null
+++ b/usr.sbin/amd/include/amu_nfs_prot.h
@@ -0,0 +1 @@
+#include "conf/nfs_prot/nfs_prot_freebsd3.h"
diff --git a/usr.sbin/amd/include/aux_conf.h b/usr.sbin/amd/include/aux_conf.h
new file mode 100644
index 0000000..1529ba3
--- /dev/null
+++ b/usr.sbin/amd/include/aux_conf.h
@@ -0,0 +1,72 @@
+/* $FreeBSD$ */
+
+/*
+ * aux_conf.h:
+ * This file gets "filled in" for each architecture.
+ * aux_conf.h. Generated from aux_conf.h.in by configure.
+ */
+
+#ifndef _AUX_CONF_H
+#define _AUX_CONF_H
+
+/*
+ * The next line is a literal inclusion of a file which includes a
+ * definition for the MOUNT_TRAP macro for a particular architecture.
+ * If it defines the wrong entry, check the AC_CHECK_MOUNT_TRAP m4 macro
+ * in $srcdir/m4/macros.
+ */
+
+/* $srcdir/conf/trap/trap_default.h */
+#define MOUNT_TRAP(type, mnt, flags, mnt_data) mount(type, mnt->mnt_dir, flags, mnt_data)
+/* End of included MOUNT_TRAP macro definition file */
+
+/*
+ * The next line is a literal replacement of a variable which defines the
+ * the UNMOUNT_TRAP macro for a particular architecture.
+ * If it defines the wrong entry, check the AC_CHECK_UNMOUNT_CALL m4 macro
+ * in $srcdir/aclocal.m4. If the arguments are being defined wrong, check
+ * the macro AC_CHECK_UNMOUNT_ARGS in $srcdir/m4/macros.
+ */
+#define UNMOUNT_TRAP(mnt) unmount(mnt->mnt_dir)
+/* End of replaced UNMOUNT_TRAP macro definition */
+/* umount(8) executable path, for type:=program */
+#define UNMOUNT_PROGRAM "/sbin/umount"
+
+/*
+ * The next line is a literal inclusion of a file which includes a
+ * definition for the NFS_FH_DREF macro for a particular architecture.
+ * If it defines the wrong entry, check the AC_CHECK_NFS_FH_DREF m4 macro
+ * in $srcdir/m4/macros.
+ */
+
+/* $srcdir/conf/fh_dref/fh_dref_freebsd22.h */
+#define NFS_FH_DREF(dst, src) (dst) = (u_char *) (src)
+/* End of included NFS_FH_DREF macro definition file */
+
+/*
+ * The next line is a literal inclusion of a file which includes a
+ * definition for the NFS_SA_DREF macro for a particular architecture.
+ * If it defines the wrong entry, check the AC_CHECK_NFS_SA_DREF m4 macro
+ * in $srcdir/m4/macros.
+ */
+
+/* $srcdir/conf/sa_dref/sa_dref_bsd44.h */
+#define NFS_SA_DREF(dst, src) { \
+ (dst)->addr = (struct sockaddr *) (src); \
+ (dst)->addrlen = sizeof(*src); \
+ }
+#define NFS_ARGS_T_ADDR_IS_POINTER 1
+/* End of included NFS_SA_DREF macro definition file */
+
+/*
+ * The next line is a literal inclusion of a file which includes a
+ * definition for the NFS_HN_DREF macro for a particular architecture.
+ * If it defines the wrong entry, check the AC_CHECK_NFS_HN_DREF m4 macro
+ * in $srcdir/m4/macros.
+ */
+
+/* $srcdir/conf/hn_dref/hn_dref_default.h */
+#define NFS_HN_DREF(dst, src) (dst) = (src)
+/* End of included NFS_HN_DREF macro definition file */
+
+#endif /* not _AUX_CONF_H */
diff --git a/usr.sbin/amd/include/build_version.h b/usr.sbin/amd/include/build_version.h
new file mode 100644
index 0000000..09eb1fc
--- /dev/null
+++ b/usr.sbin/amd/include/build_version.h
@@ -0,0 +1,8 @@
+/* $FreeBSD$ */
+
+#include <sys/param.h>
+/*#define AMU_BUILD_VERSION 1 */
+#define AMU_BUILD_VERSION __FreeBSD_version
+#define USER_NAME "David O'Brien <obrien"
+#define HOST_NAME "FreeBSD.org>"
+#define CONFIG_DATE "4-December-2007 PST"
diff --git a/usr.sbin/amd/include/config.h b/usr.sbin/amd/include/config.h
new file mode 100644
index 0000000..e1dc8ef
--- /dev/null
+++ b/usr.sbin/amd/include/config.h
@@ -0,0 +1,2205 @@
+/*
+ * $FreeBSD$
+ *
+ * portions derived from
+ * $NetBSD: config.h,v 1.11 1998/08/08 22:33:37 christos Exp $
+ */
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+
+/* We [FREEBSD-NATIVE] pick some parameters from our local config file */
+#include "config_local.h"
+
+/* define name of am-utils' NFS protocol header */
+#define AMU_NFS_PROTOCOL_HEADER "./conf/nfs_prot/nfs_prot_freebsd3.h"
+
+/* Type of the 5rd argument to authunix_create() */
+#define AUTH_CREATE_GIDLIST_TYPE gid_t
+
+/* Define configuration date */
+/* #define CONFIG_DATE "Tue Dec 4 21:39:00 PST 2007" */
+
+/* Turn off general debugging by default */
+/* #undef DEBUG */
+
+/* Turn off memory debugging by default */
+/* #undef DEBUG_MEM */
+
+/* Define name of host OS's distribution name (eg. debian, redhat, suse, etc.)
+ */
+#define DISTRO_NAME "The FreeBSD Project"
+
+/* Define to the type of elements in the array set by `getgroups'. Usually
+ this is either `int' or `gid_t'. */
+#define GETGROUPS_T gid_t
+
+/* Define to 1 if the `getpgrp' function requires zero arguments. */
+#define GETPGRP_VOID 1
+
+/* Define if have automount filesystem */
+#define HAVE_AMU_FS_AUTO 1
+
+/* Define if have direct automount filesystem */
+#define HAVE_AMU_FS_DIRECT 1
+
+/* Define if have error filesystem */
+#define HAVE_AMU_FS_ERROR 1
+
+/* Define if have NFS host-tree filesystem */
+#define HAVE_AMU_FS_HOST 1
+
+/* Define if have symbolic-link filesystem */
+#define HAVE_AMU_FS_LINK 1
+
+/* Define if have symlink with existence check filesystem */
+#define HAVE_AMU_FS_LINKX 1
+
+/* Define if have nfsl (NFS with local link check) filesystem */
+#define HAVE_AMU_FS_NFSL 1
+
+/* Define if have multi-NFS filesystem */
+#define HAVE_AMU_FS_NFSX 1
+
+/* Define if have program filesystem */
+#define HAVE_AMU_FS_PROGRAM 1
+
+/* Define if have "top-level" filesystem */
+#define HAVE_AMU_FS_TOPLVL 1
+
+/* Define if have union filesystem */
+#define HAVE_AMU_FS_UNION 1
+
+/* Define to 1 if you have the <arpa/inet.h> header file. */
+#define HAVE_ARPA_INET_H 1
+
+/* Define to 1 if you have the <arpa/nameser.h> header file. */
+#define HAVE_ARPA_NAMESER_H 1
+
+/* Define to 1 if you have the <assert.h> header file. */
+#define HAVE_ASSERT_H 1
+
+/* Define to 1 if `addr' is member of `autofs_args_t'. */
+/* #undef HAVE_AUTOFS_ARGS_T_ADDR */
+
+/* define if have a bad version of hasmntopt() */
+/* #undef HAVE_BAD_HASMNTOPT */
+
+/* define if have a bad version of memcmp() */
+/* #undef HAVE_BAD_MEMCMP */
+
+/* define if have a bad version of yp_all() */
+/* #undef HAVE_BAD_YP_ALL */
+
+/* Define to 1 if you have the `bcmp' function. */
+#define HAVE_BCMP 1
+
+/* Define to 1 if you have the `bcopy' function. */
+#define HAVE_BCOPY 1
+
+/* Define to 1 if you have the <bsd/rpc/rpc.h> header file. */
+/* #undef HAVE_BSD_RPC_RPC_H */
+
+/* Define to 1 if you have the `bzero' function. */
+#define HAVE_BZERO 1
+
+/* System supports C99-style variable-length argument macros */
+#define HAVE_C99_VARARGS_MACROS 1
+
+/* Define to 1 if `flags' is member of `cdfs_args_t'. */
+#define HAVE_CDFS_ARGS_T_FLAGS 1
+
+/* Define to 1 if `fspec' is member of `cdfs_args_t'. */
+#define HAVE_CDFS_ARGS_T_FSPEC 1
+
+/* Define to 1 if `iso_flags' is member of `cdfs_args_t'. */
+/* #undef HAVE_CDFS_ARGS_T_ISO_FLAGS */
+
+/* Define to 1 if `iso_pgthresh' is member of `cdfs_args_t'. */
+/* #undef HAVE_CDFS_ARGS_T_ISO_PGTHRESH */
+
+/* Define to 1 if `norrip' is member of `cdfs_args_t'. */
+/* #undef HAVE_CDFS_ARGS_T_NORRIP */
+
+/* Define to 1 if `ssector' is member of `cdfs_args_t'. */
+#define HAVE_CDFS_ARGS_T_SSECTOR 1
+
+/* Define to 1 if you have the <cdfs/cdfsmount.h> header file. */
+/* #undef HAVE_CDFS_CDFSMOUNT_H */
+
+/* Define to 1 if you have the <cdfs/cdfs_mount.h> header file. */
+/* #undef HAVE_CDFS_CDFS_MOUNT_H */
+
+/* Define to 1 if you have the `clnt_create' function. */
+#define HAVE_CLNT_CREATE 1
+
+/* Define to 1 if you have the `clnt_create_vers' function. */
+#define HAVE_CLNT_CREATE_VERS 1
+
+/* Define to 1 if you have the `clnt_create_vers_timed' function. */
+#define HAVE_CLNT_CREATE_VERS_TIMED 1
+
+/* Define to 1 if you have the `clnt_spcreateerror' function. */
+#define HAVE_CLNT_SPCREATEERROR 1
+
+/* Define to 1 if you have the `clnt_sperrno' function. */
+#define HAVE_CLNT_SPERRNO 1
+
+/* Define to 1 if you have the `clock_gettime' function. */
+#define HAVE_CLOCK_GETTIME 1
+
+/* Define to 1 if you have the <cluster.h> header file. */
+/* #undef HAVE_CLUSTER_H */
+
+/* Define to 1 if you have the `cnodeid' function. */
+/* #undef HAVE_CNODEID */
+
+/* Define to 1 if you have the <ctype.h> header file. */
+#define HAVE_CTYPE_H 1
+
+/* Define to 1 if you have the <db1/ndbm.h> header file. */
+/* #undef HAVE_DB1_NDBM_H */
+
+/* Define to 1 if you have the `dbm_open' function. */
+#define HAVE_DBM_OPEN 1
+
+/* Define to 1 if you have the `dg_mount' function. */
+/* #undef HAVE_DG_MOUNT */
+
+/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
+ */
+#define HAVE_DIRENT_H 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if `flags' is member of `efs_args_t'. */
+/* #undef HAVE_EFS_ARGS_T_FLAGS */
+
+/* Define to 1 if `fspec' is member of `efs_args_t'. */
+/* #undef HAVE_EFS_ARGS_T_FSPEC */
+
+/* Define to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* does extern definition for clnt_spcreateerror() exist? */
+#define HAVE_EXTERN_CLNT_SPCREATEERROR 1
+
+/* does extern definition for clnt_sperrno() exist? */
+#define HAVE_EXTERN_CLNT_SPERRNO 1
+
+/* does extern definition for free() exist? */
+#define HAVE_EXTERN_FREE 1
+
+/* does extern definition for getccent() (hpux) exist? */
+/* #undef HAVE_EXTERN_GETCCENT */
+
+/* does extern definition for getdomainname() exist? */
+#define HAVE_EXTERN_GETDOMAINNAME 1
+
+/* does extern definition for getdtablesize() exist? */
+#define HAVE_EXTERN_GETDTABLESIZE 1
+
+/* does extern definition for gethostname() exist? */
+#define HAVE_EXTERN_GETHOSTNAME 1
+
+/* does extern definition for getlogin() exist? */
+#define HAVE_EXTERN_GETLOGIN 1
+
+/* does extern definition for getpagesize() exist? */
+#define HAVE_EXTERN_GETPAGESIZE 1
+
+/* does extern definition for gettablesize() exist? */
+/* #undef HAVE_EXTERN_GETTABLESIZE */
+
+/* does extern definition for getwd() exist? */
+#define HAVE_EXTERN_GETWD 1
+
+/* does extern definition for get_myaddress() exist? */
+#define HAVE_EXTERN_GET_MYADDRESS 1
+
+/* does extern definition for hosts_ctl() exist? */
+/* #undef HAVE_EXTERN_HOSTS_CTL */
+
+/* does extern definition for innetgr() exist? */
+#define HAVE_EXTERN_INNETGR 1
+
+/* does extern definition for ldap_enable_cache() exist? */
+/* #undef HAVE_EXTERN_LDAP_ENABLE_CACHE */
+
+/* does extern definition for mkstemp() exist? */
+#define HAVE_EXTERN_MKSTEMP 1
+
+/* does extern definition for mntctl() exist? */
+/* #undef HAVE_EXTERN_MNTCTL */
+
+/* does extern definition for optarg exist? */
+#define HAVE_EXTERN_OPTARG 1
+
+/* does extern definition for sbrk() exist? */
+#define HAVE_EXTERN_SBRK 1
+
+/* does extern definition for seteuid() exist? */
+#define HAVE_EXTERN_SETEUID 1
+
+/* does extern definition for setitimer() exist? */
+#define HAVE_EXTERN_SETITIMER 1
+
+/* does extern definition for sleep() exist? */
+#define HAVE_EXTERN_SLEEP 1
+
+/* does extern definition for strcasecmp() exist? */
+#define HAVE_EXTERN_STRCASECMP 1
+
+/* does extern definition for strdup() exist? */
+#define HAVE_EXTERN_STRDUP 1
+
+/* does extern definition for strlcat() exist? */
+#define HAVE_EXTERN_STRLCAT 1
+
+/* does extern definition for strlcpy() exist? */
+#define HAVE_EXTERN_STRLCPY 1
+
+/* does extern definition for strstr() exist? */
+#define HAVE_EXTERN_STRSTR 1
+
+/* does extern definition for sys_errlist[] exist? */
+#define HAVE_EXTERN_SYS_ERRLIST 1
+
+/* does extern definition for ualarm() exist? */
+#define HAVE_EXTERN_UALARM 1
+
+/* does extern definition for usleep() exist? */
+#define HAVE_EXTERN_USLEEP 1
+
+/* does extern definition for vsnprintf() exist? */
+#define HAVE_EXTERN_VSNPRINTF 1
+
+/* does extern definition for wait3() exist? */
+#define HAVE_EXTERN_WAIT3 1
+
+/* does extern definition for xdr_callmsg() exist? */
+#define HAVE_EXTERN_XDR_CALLMSG 1
+
+/* does extern definition for xdr_opaque_auth() exist? */
+#define HAVE_EXTERN_XDR_OPAQUE_AUTH 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if `fds_bits' is member of `fd_set'. */
+#define HAVE_FD_SET_FDS_BITS 1
+
+/* Define to 1 if you have the `fgets' function. */
+#define HAVE_FGETS 1
+
+/* Define if plain fhandle type exists */
+#define HAVE_FHANDLE 1
+
+/* Define to 1 if you have the `flock' function. */
+#define HAVE_FLOCK 1
+
+/* Define to 1 if you have the `fork' function. */
+#define HAVE_FORK 1
+
+/* Define to 1 if you have the `fsmount' function. */
+/* #undef HAVE_FSMOUNT */
+
+/* Define if have AUTOFS filesystem */
+/* #undef HAVE_FS_AUTOFS */
+
+/* Define if have CACHEFS filesystem */
+/* #undef HAVE_FS_CACHEFS */
+
+/* Define if have CDFS filesystem */
+#define HAVE_FS_CDFS 1
+
+/* Define if have CFS (crypto) filesystem */
+/* #undef HAVE_FS_CFS */
+
+/* Define if have EFS filesystem (irix) */
+/* #undef HAVE_FS_EFS */
+
+/* Define if have FFS filesystem */
+/* #undef HAVE_FS_FFS */
+
+/* Define if have HSFS filesystem */
+/* #undef HAVE_FS_HSFS */
+
+/* Define if have LOFS filesystem */
+/* #undef HAVE_FS_LOFS */
+
+/* Define if have MFS filesystem */
+/* #undef HAVE_FS_MFS */
+
+/* Define if have NFS filesystem */
+#define HAVE_FS_NFS 1
+
+/* Define if have NFS3 filesystem */
+#define HAVE_FS_NFS3 1
+
+/* Define if have NULLFS (loopback on bsd44) filesystem */
+#define HAVE_FS_NULLFS 1
+
+/* Define if have PCFS filesystem */
+#define HAVE_FS_PCFS 1
+
+/* Define if have TFS filesystem */
+/* #undef HAVE_FS_TFS */
+
+/* Define if have TMPFS filesystem */
+/* #undef HAVE_FS_TMPFS */
+
+/* Define if have UFS filesystem */
+#define HAVE_FS_UFS 1
+
+/* Define if have UMAPFS (uid/gid mapping) filesystem */
+/* #undef HAVE_FS_UMAPFS */
+
+/* Define if have UNIONFS filesystem */
+#define HAVE_FS_UNIONFS 1
+
+/* Define if have XFS filesystem (irix) */
+/* #undef HAVE_FS_XFS */
+
+/* System supports GCC-style variable-length argument macros */
+/* #undef HAVE_GCC_VARARGS_MACROS */
+
+/* Define to 1 if you have the <gdbm-ndbm.h> header file. */
+/* #undef HAVE_GDBM_NDBM_H */
+
+/* Define to 1 if you have the `getccent' function. */
+/* #undef HAVE_GETCCENT */
+
+/* Define to 1 if you have the `getcwd' function. */
+#define HAVE_GETCWD 1
+
+/* Define to 1 if you have the `getdomainname' function. */
+#define HAVE_GETDOMAINNAME 1
+
+/* Define to 1 if you have the `getdtablesize' function. */
+#define HAVE_GETDTABLESIZE 1
+
+/* Define to 1 if you have the `gethostname' function. */
+#define HAVE_GETHOSTNAME 1
+
+/* Define to 1 if you have the `getifaddrs' function. */
+#define HAVE_GETIFADDRS 1
+
+/* Define to 1 if you have the `getmntinfo' function. */
+#define HAVE_GETMNTINFO 1
+
+/* Define to 1 if you have the `getmountent' function. */
+/* #undef HAVE_GETMOUNTENT */
+
+/* Define to 1 if you have the `getpagesize' function. */
+#define HAVE_GETPAGESIZE 1
+
+/* Define to 1 if you have the `getpwnam' function. */
+#define HAVE_GETPWNAM 1
+
+/* Define to 1 if you have the `gettimeofday' function. */
+#define HAVE_GETTIMEOFDAY 1
+
+/* Define to 1 if you have the `get_myaddress' function. */
+#define HAVE_GET_MYADDRESS 1
+
+/* define if your system's getopt() is GNU getopt() (are you using glibc) */
+/* #undef HAVE_GNU_GETOPT */
+
+/* Define to 1 if you have the <grp.h> header file. */
+#define HAVE_GRP_H 1
+
+/* Define to 1 if you have the `hasmntopt' function. */
+/* #undef HAVE_HASMNTOPT */
+
+#ifdef YES_HESIOD
+/* Define to 1 if you have the <hesiod.h> header file. */
+#define HAVE_HESIOD_H 1
+
+/* Define to 1 if you have the `hesiod_init' function. */
+#define HAVE_HESIOD_INIT 1
+
+/* Define to 1 if you have the `hesiod_reload' function. */
+/* #undef HAVE_HESIOD_RELOAD */
+
+/* Define to 1 if you have the `hesiod_to_bind' function. */
+#define HAVE_HESIOD_TO_BIND 1
+
+/* Define to 1 if you have the `hes_init' function. */
+#define HAVE_HES_INIT 1
+#else
+#undef HAVE_HESIOD_H
+#undef HAVE_HESIOD_INIT
+#undef HAVE_HESIOD_RELOAD
+#undef HAVE_HESIOD_TO_BIND
+#undef HAVE_HES_INIT
+#endif
+
+/* Define to 1 if you have the <hsfs/hsfs.h> header file. */
+/* #undef HAVE_HSFS_HSFS_H */
+
+/* Define to 1 if you have the `hstrerror' function. */
+#define HAVE_HSTRERROR 1
+
+/* Define to 1 if you have the <ifaddrs.h> header file. */
+#define HAVE_IFADDRS_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <irs.h> header file. */
+/* #undef HAVE_IRS_H */
+
+/* Define to 1 if you have the <isofs/cd9660/cd9660_mount.h> header file. */
+#define HAVE_ISOFS_CD9660_CD9660_MOUNT_H 1
+
+/* Define to 1 if you have the <lber.h> header file. */
+/* #undef HAVE_LBER_H */
+
+/* Define to 1 if you have the `ldap_enable_cache' function. */
+/* #undef HAVE_LDAP_ENABLE_CACHE */
+
+/* Define to 1 if you have the <ldap.h> header file. */
+/* #undef HAVE_LDAP_H */
+
+/* Define to 1 if you have the `ldap_open' function. */
+/* #undef HAVE_LDAP_OPEN */
+
+/* Define to 1 if you have the <libgen.h> header file. */
+#define HAVE_LIBGEN_H 1
+
+/* Define to 1 if you have the `malloc' library (-lmalloc). */
+/* #undef HAVE_LIBMALLOC */
+
+/* Define to 1 if you have the `mapmalloc' library (-lmapmalloc). */
+/* #undef HAVE_LIBMAPMALLOC */
+
+/* Define to 1 if you have the `nsl' library (-lnsl). */
+/* #undef HAVE_LIBNSL */
+
+/* Define to 1 if you have the `posix4' library (-lposix4). */
+/* #undef HAVE_LIBPOSIX4 */
+
+/* Define to 1 if you have the `resolv' library (-lresolv). */
+/* #undef HAVE_LIBRESOLV */
+
+/* Define to 1 if you have the `rpc' library (-lrpc). */
+/* #undef HAVE_LIBRPC */
+
+/* Define to 1 if you have the `rpcsvc' library (-lrpcsvc). */
+#define HAVE_LIBRPCSVC 1
+
+/* Define to 1 if you have the `rt' library (-lrt). */
+/* #undef HAVE_LIBRT */
+
+/* does libwrap exist? */
+#define HAVE_LIBWRAP 1
+
+/* Define to 1 if you have the <limits.h> header file. */
+#define HAVE_LIMITS_H 1
+
+/* Define to 1 if you have the <linux/auto_fs4.h> header file. */
+/* #undef HAVE_LINUX_AUTO_FS4_H */
+
+/* Define to 1 if you have the <linux/auto_fs.h> header file. */
+/* #undef HAVE_LINUX_AUTO_FS_H */
+
+/* Define to 1 if you have the <linux/fs.h> header file. */
+/* #undef HAVE_LINUX_FS_H */
+
+/* Define to 1 if you have the <linux/kdev_t.h> header file. */
+/* #undef HAVE_LINUX_KDEV_T_H */
+
+/* Define to 1 if you have the <linux/list.h> header file. */
+/* #undef HAVE_LINUX_LIST_H */
+
+/* Define to 1 if you have the <linux/loop.h> header file. */
+/* #undef HAVE_LINUX_LOOP_H */
+
+/* Define to 1 if you have the <linux/nfs2.h> header file. */
+/* #undef HAVE_LINUX_NFS2_H */
+
+/* Define to 1 if you have the <linux/nfs.h> header file. */
+/* #undef HAVE_LINUX_NFS_H */
+
+/* Define to 1 if you have the <linux/nfs_mount.h> header file. */
+/* #undef HAVE_LINUX_NFS_MOUNT_H */
+
+/* Define to 1 if you have the <linux/posix_types.h> header file. */
+/* #undef HAVE_LINUX_POSIX_TYPES_H */
+
+/* Define to 1 if you have the <linux/socket.h> header file. */
+/* #undef HAVE_LINUX_SOCKET_H */
+
+/* Define to 1 if you support file names longer than 14 characters. */
+#define HAVE_LONG_FILE_NAMES 1
+
+/* Define to 1 if you have the <machine/endian.h> header file. */
+#define HAVE_MACHINE_ENDIAN_H 1
+
+/* Define to 1 if you have the `madvise' function. */
+#define HAVE_MADVISE 1
+
+/* Define to 1 if you have the <malloc.h> header file. */
+/* #undef HAVE_MALLOC_H */
+
+/* Define if have DBM maps */
+/* #undef HAVE_MAP_DBM */
+
+/* Define if have executable maps */
+#define HAVE_MAP_EXEC 1
+
+/* Define if have file maps (everyone should have it!) */
+#define HAVE_MAP_FILE 1
+
+#ifdef YES_HESIOD
+/* Define if have HESIOD maps */
+#define HAVE_MAP_HESIOD 1
+#else
+#undef HAVE_MAP_HESIOD
+#endif
+
+/* Define if have LDAP maps */
+/* #undef HAVE_MAP_LDAP */
+
+/* Define if have NDBM maps */
+#define HAVE_MAP_NDBM 1
+
+/* Define if have NIS maps */
+#define HAVE_MAP_NIS 1
+
+/* Define if have NIS+ maps */
+/* #undef HAVE_MAP_NISPLUS */
+
+/* Define if have PASSWD maps */
+#define HAVE_MAP_PASSWD 1
+
+/* Define if have UNION maps */
+#define HAVE_MAP_UNION 1
+
+/* Define to 1 if you have the `memcmp' function. */
+#define HAVE_MEMCMP 1
+
+/* Define to 1 if you have the `memcpy' function. */
+#define HAVE_MEMCPY 1
+
+/* Define to 1 if you have the `memmove' function. */
+#define HAVE_MEMMOVE 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `memset' function. */
+#define HAVE_MEMSET 1
+
+/* Define to 1 if you have the `mkdir' function. */
+#define HAVE_MKDIR 1
+
+/* Define to 1 if you have the `mkstemp' function. */
+#define HAVE_MKSTEMP 1
+
+/* Define to 1 if you have the `mlockall' function. */
+#define HAVE_MLOCKALL 1
+
+/* Define to 1 if you have the `mntctl' function. */
+/* #undef HAVE_MNTCTL */
+
+/* Define to 1 if you have the <mntent.h> header file. */
+/* #undef HAVE_MNTENT_H */
+
+/* Define to 1 if `mnt_cnode' is member of `mntent_t'. */
+/* #undef HAVE_MNTENT_T_MNT_CNODE */
+
+/* Define to 1 if `mnt_ro' is member of `mntent_t'. */
+/* #undef HAVE_MNTENT_T_MNT_RO */
+
+/* Define to 1 if `mnt_time' is member of `mntent_t'. */
+/* #undef HAVE_MNTENT_T_MNT_TIME */
+
+/* does mntent_t have mnt_time field and is of type "char *" ? */
+/* #undef HAVE_MNTENT_T_MNT_TIME_STRING */
+
+/* Define to 1 if you have the <mnttab.h> header file. */
+/* #undef HAVE_MNTTAB_H */
+
+/* Define to 1 if you have the `mount' function. */
+#define HAVE_MOUNT 1
+
+/* Define to 1 if `optptr' is member of `mounta'. */
+/* #undef HAVE_MOUNTA_OPTPTR */
+
+/* Define to 1 if you have the `mountsyscall' function. */
+/* #undef HAVE_MOUNTSYSCALL */
+
+/* Define to 1 if you have the <mount.h> header file. */
+/* #undef HAVE_MOUNT_H */
+
+/* Define to 1 if you have the <msdosfs/msdosfsmount.h> header file. */
+/* #undef HAVE_MSDOSFS_MSDOSFSMOUNT_H */
+
+/* Define to 1 if you have the <fs/msdosfs/msdosfsmount.h> header file. */
+#define HAVE_FS_MSDOSFS_MSDOSFSMOUNT_H 1
+
+/* Define to 1 if you have the <ndbm.h> header file. */
+#define HAVE_NDBM_H 1
+
+/* Define to 1 if you have the <ndir.h> header file. */
+/* #undef HAVE_NDIR_H */
+
+/* Define to 1 if you have the <netconfig.h> header file. */
+#define HAVE_NETCONFIG_H 1
+
+/* Define to 1 if you have the <netdb.h> header file. */
+#define HAVE_NETDB_H 1
+
+/* Define to 1 if you have the <netdir.h> header file. */
+/* #undef HAVE_NETDIR_H */
+
+/* Define to 1 if you have the <netinet/if_ether.h> header file. */
+#define HAVE_NETINET_IF_ETHER_H 1
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#define HAVE_NETINET_IN_H 1
+
+/* Define to 1 if you have the <net/errno.h> header file. */
+/* #undef HAVE_NET_ERRNO_H */
+
+/* Define to 1 if you have the <net/if.h> header file. */
+#define HAVE_NET_IF_H 1
+
+/* Define to 1 if you have the <net/if_var.h> header file. */
+#define HAVE_NET_IF_VAR_H 1
+
+/* Define to 1 if you have the <net/route.h> header file. */
+#define HAVE_NET_ROUTE_H 1
+
+/* Define to 1 if you have the <nfsclient/nfsargs.h> header file. */
+#define HAVE_NFSCLIENT_NFSARGS_H 1
+
+/* Define to 1 if `acdirmax' is member of `nfs_args_t'. */
+#define HAVE_NFS_ARGS_T_ACDIRMAX 1
+
+/* Define to 1 if `acdirmin' is member of `nfs_args_t'. */
+#define HAVE_NFS_ARGS_T_ACDIRMIN 1
+
+/* Define to 1 if `acregmax' is member of `nfs_args_t'. */
+#define HAVE_NFS_ARGS_T_ACREGMAX 1
+
+/* Define to 1 if `acregmin' is member of `nfs_args_t'. */
+#define HAVE_NFS_ARGS_T_ACREGMIN 1
+
+/* Define to 1 if `addrlen' is member of `nfs_args_t'. */
+#define HAVE_NFS_ARGS_T_ADDRLEN 1
+
+/* Define to 1 if `bsize' is member of `nfs_args_t'. */
+/* #undef HAVE_NFS_ARGS_T_BSIZE */
+
+/* Define to 1 if `fhsize' is member of `nfs_args_t'. */
+#define HAVE_NFS_ARGS_T_FHSIZE 1
+
+/* Define to 1 if `fh_len' is member of `nfs_args_t'. */
+/* #undef HAVE_NFS_ARGS_T_FH_LEN */
+
+/* Define to 1 if `gfs_flags' is member of `nfs_args_t'. */
+/* #undef HAVE_NFS_ARGS_T_GFS_FLAGS */
+
+/* Define to 1 if `namlen' is member of `nfs_args_t'. */
+/* #undef HAVE_NFS_ARGS_T_NAMLEN */
+
+/* Define to 1 if `optstr' is member of `nfs_args_t'. */
+/* #undef HAVE_NFS_ARGS_T_OPTSTR */
+
+/* Define to 1 if `proto' is member of `nfs_args_t'. */
+#define HAVE_NFS_ARGS_T_PROTO 1
+
+/* Define to 1 if `sotype' is member of `nfs_args_t'. */
+#define HAVE_NFS_ARGS_T_SOTYPE 1
+
+/* Define to 1 if `version' is member of `nfs_args_t'. */
+#define HAVE_NFS_ARGS_T_VERSION 1
+
+/* Define to 1 if you have the <nfs/export.h> header file. */
+/* #undef HAVE_NFS_EXPORT_H */
+
+/* Define to 1 if you have the <nfs/mount.h> header file. */
+/* #undef HAVE_NFS_MOUNT_H */
+
+/* Define to 1 if you have the <nfs/nfsmount.h> header file. */
+/* #undef HAVE_NFS_NFSMOUNT_H */
+
+/* Define to 1 if you have the <nfs/nfsproto.h> header file. */
+#define HAVE_NFS_NFSPROTO_H 1
+
+/* Define to 1 if you have the <nfs/nfsv2.h> header file. */
+/* #undef HAVE_NFS_NFSV2_H */
+
+/* Define to 1 if you have the <nfs/nfs_clnt.h> header file. */
+/* #undef HAVE_NFS_NFS_CLNT_H */
+
+/* Define to 1 if you have the <nfs/nfs_gfs.h> header file. */
+/* #undef HAVE_NFS_NFS_GFS_H */
+
+/* Define to 1 if you have the <nfs/nfs.h> header file. */
+/* #undef HAVE_NFS_NFS_H */
+
+/* Define to 1 if you have the <nfs/nfs_mount.h> header file. */
+/* #undef HAVE_NFS_NFS_MOUNT_H */
+
+/* Define to 1 if you have the <nfs/pathconf.h> header file. */
+/* #undef HAVE_NFS_PATHCONF_H */
+
+/* define if the host has NFS protocol headers in system headers */
+/* #undef HAVE_NFS_PROT_HEADERS */
+
+/* Define to 1 if you have the <nfs/rpcv2.h> header file. */
+#define HAVE_NFS_RPCV2_H 1
+
+/* Define to 1 if you have the `nis_domain_of' function. */
+/* #undef HAVE_NIS_DOMAIN_OF */
+
+/* Define to 1 if you have the <nsswitch.h> header file. */
+#define HAVE_NSSWITCH_H 1
+
+/* Define to 1 if you have the `opendir' function. */
+#define HAVE_OPENDIR 1
+/* Define to 1 if `dsttime' is member of `pcfs_args_t'. */
+/* #undef HAVE_PCFS_ARGS_T_DSTTIME */
+
+/* Define to 1 if `fspec' is member of `pcfs_args_t'. */
+#define HAVE_PCFS_ARGS_T_FSPEC 1
+
+/* Define to 1 if `gid' is member of `pcfs_args_t'. */
+#define HAVE_PCFS_ARGS_T_GID 1
+
+/* Define to 1 if `mask' is member of `pcfs_args_t'. */
+#define HAVE_PCFS_ARGS_T_MASK 1
+
+/* Define to 1 if `dirmask' is member of `pcfs_args_t'. */
+#define HAVE_PCFS_ARGS_T_DIRMASK 1
+
+/* Define to 1 if `secondswest' is member of `pcfs_args_t'. */
+/* #undef HAVE_PCFS_ARGS_T_SECONDSWEST */
+
+/* Define to 1 if `uid' is member of `pcfs_args_t'. */
+#define HAVE_PCFS_ARGS_T_UID 1
+
+/* Define to 1 if you have the `plock' function. */
+/* #undef HAVE_PLOCK */
+
+/* Define to 1 if you have the <pwd.h> header file. */
+#define HAVE_PWD_H 1
+
+/* Define to 1 if you have the `regcomp' function. */
+#define HAVE_REGCOMP 1
+
+/* Define to 1 if you have the `regexec' function. */
+#define HAVE_REGEXEC 1
+
+/* Define to 1 if you have the <regex.h> header file. */
+#define HAVE_REGEX_H 1
+
+/* Define to 1 if you have the <resolv.h> header file. */
+#define HAVE_RESOLV_H 1
+
+/* Define to 1 if system calls automatically restart after interruption by a
+ signal. */
+#define HAVE_RESTARTABLE_SYSCALLS 1
+
+/* Define to 1 if you have the `rmdir' function. */
+#define HAVE_RMDIR 1
+
+/* Define to 1 if you have the <rpcsvc/autofs_prot.h> header file. */
+/* #undef HAVE_RPCSVC_AUTOFS_PROT_H */
+
+/* Define to 1 if you have the <rpcsvc/mountv3.h> header file. */
+/* #undef HAVE_RPCSVC_MOUNTV3_H */
+
+/* Define to 1 if you have the <rpcsvc/mount.h> header file. */
+#define HAVE_RPCSVC_MOUNT_H 1
+
+/* Define to 1 if you have the <rpcsvc/nfs_prot.h> header file. */
+#define HAVE_RPCSVC_NFS_PROT_H 1
+
+/* Define to 1 if you have the <rpcsvc/nis.h> header file. */
+#define HAVE_RPCSVC_NIS_H 1
+
+/* Define to 1 if you have the <rpcsvc/ypclnt.h> header file. */
+#define HAVE_RPCSVC_YPCLNT_H 1
+
+/* Define to 1 if you have the <rpcsvc/yp_prot.h> header file. */
+#define HAVE_RPCSVC_YP_PROT_H 1
+
+/* Define to 1 if you have the <rpc/auth_des.h> header file. */
+#define HAVE_RPC_AUTH_DES_H 1
+
+/* Define to 1 if you have the <rpc/pmap_clnt.h> header file. */
+#define HAVE_RPC_PMAP_CLNT_H 1
+
+/* Define to 1 if you have the <rpc/pmap_prot.h> header file. */
+#define HAVE_RPC_PMAP_PROT_H 1
+
+/* Define to 1 if you have the <rpc/rpc.h> header file. */
+#define HAVE_RPC_RPC_H 1
+
+/* Define to 1 if you have the <rpc/types.h> header file. */
+#define HAVE_RPC_TYPES_H 1
+
+/* Define to 1 if you have the <rpc/xdr.h> header file. */
+#define HAVE_RPC_XDR_H 1
+
+/* Define to 1 if you have the `select' function. */
+#define HAVE_SELECT 1
+
+/* Define to 1 if you have the `seteuid' function. */
+#define HAVE_SETEUID 1
+
+/* Define to 1 if you have the `setitimer' function. */
+#define HAVE_SETITIMER 1
+
+/* Define to 1 if you have the <setjmp.h> header file. */
+#define HAVE_SETJMP_H 1
+
+/* Define to 1 if you have the `setresuid' function. */
+#define HAVE_SETRESUID 1
+
+/* Define to 1 if you have the `setsid' function. */
+#define HAVE_SETSID 1
+
+/* Define to 1 if you have the `sigaction' function. */
+#define HAVE_SIGACTION 1
+
+/* Define to 1 if you have the `signal' function. */
+#define HAVE_SIGNAL 1
+
+/* Define to 1 if you have the <signal.h> header file. */
+#define HAVE_SIGNAL_H 1
+
+/* Define to 1 if you have the `sigsuspend' function. */
+#define HAVE_SIGSUSPEND 1
+
+/* Define to 1 if you have the `socket' function. */
+#define HAVE_SOCKET 1
+
+/* Define to 1 if you have the <socketbits.h> header file. */
+/* #undef HAVE_SOCKETBITS_H */
+
+/* Define to 1 if you have the <statbuf.h> header file. */
+/* #undef HAVE_STATBUF_H */
+
+/* Define to 1 if you have the `statfs' function. */
+#define HAVE_STATFS 1
+
+/* Define to 1 if you have the `statvfs' function. */
+#define HAVE_STATVFS 1
+
+/* Define to 1 if you have the <stdarg.h> header file. */
+#define HAVE_STDARG_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the `strcasecmp' function. */
+#define HAVE_STRCASECMP 1
+
+/* Define to 1 if you have the `strchr' function. */
+#define HAVE_STRCHR 1
+
+/* Define to 1 if you have the `strcspn' function. */
+#define HAVE_STRCSPN 1
+
+/* Define to 1 if you have the `strdup' function. */
+#define HAVE_STRDUP 1
+
+/* Define to 1 if you have the `strerror' function. */
+#define HAVE_STRERROR 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the `strlcat' function. */
+#define HAVE_STRLCAT 1
+
+/* Define to 1 if you have the `strlcpy' function. */
+#define HAVE_STRLCPY 1
+
+/* Define to 1 if you have the `strspn' function. */
+#define HAVE_STRSPN 1
+
+/* Define to 1 if you have the `strstr' function. */
+#define HAVE_STRSTR 1
+
+/* Define to 1 if `fhs_fh' is member of `struct fhstatus'. */
+/* #undef HAVE_STRUCT_FHSTATUS_FHS_FH */
+
+/* Define to 1 if `ifa_next' is member of `struct ifaddrs'. */
+#define HAVE_STRUCT_IFADDRS_IFA_NEXT 1
+
+/* Define to 1 if `ifr_addr' is member of `struct ifreq'. */
+#define HAVE_STRUCT_IFREQ_IFR_ADDR 1
+
+/* Define if have struct mntent in one of the standard headers */
+/* #undef HAVE_STRUCT_MNTENT */
+
+/* Define if have struct mnttab in one of the standard headers */
+/* #undef HAVE_STRUCT_MNTTAB */
+
+/* Define if have struct nfs_args in one of the standard nfs headers */
+#define HAVE_STRUCT_NFS_ARGS 1
+
+/* Define if have struct nfs_gfs_mount in one of the standard nfs headers */
+/* #undef HAVE_STRUCT_NFS_GFS_MOUNT */
+
+/* Define to 1 if `sa_len' is member of `struct sockaddr'. */
+#define HAVE_STRUCT_SOCKADDR_SA_LEN 1
+
+/* Define to 1 if `f_fstypename' is member of `struct statfs'. */
+#define HAVE_STRUCT_STATFS_F_FSTYPENAME 1
+
+/* Define to 1 if `devid' is member of `struct umntrequest'. */
+/* #undef HAVE_STRUCT_UMNTREQUEST_DEVID */
+
+/* Define to 1 if you have the `svc_getreq' function. */
+#define HAVE_SVC_GETREQ 1
+
+/* Define to 1 if you have the `svc_getreqset' function. */
+#define HAVE_SVC_GETREQSET 1
+
+/* Define to 1 if you have the `sysfs' function. */
+/* #undef HAVE_SYSFS */
+
+/* Define to 1 if you have the `syslog' function. */
+#define HAVE_SYSLOG 1
+
+/* Define to 1 if you have the <syslog.h> header file. */
+#define HAVE_SYSLOG_H 1
+
+/* Define to 1 if you have the <sys/config.h> header file. */
+/* #undef HAVE_SYS_CONFIG_H */
+
+/* Define to 1 if you have the <sys/dg_mount.h> header file. */
+/* #undef HAVE_SYS_DG_MOUNT_H */
+
+/* Define to 1 if you have the <sys/dir.h> header file. */
+#define HAVE_SYS_DIR_H 1
+
+/* Define to 1 if you have the <sys/errno.h> header file. */
+#define HAVE_SYS_ERRNO_H 1
+
+/* Define to 1 if you have the <sys/file.h> header file. */
+#define HAVE_SYS_FILE_H 1
+
+/* Define to 1 if you have the <sys/fsid.h> header file. */
+/* #undef HAVE_SYS_FSID_H */
+
+/* Define to 1 if you have the <sys/fstyp.h> header file. */
+/* #undef HAVE_SYS_FSTYP_H */
+
+/* Define to 1 if you have the <sys/fs/autofs.h> header file. */
+/* #undef HAVE_SYS_FS_AUTOFS_H */
+
+/* Define to 1 if you have the <sys/fs/autofs_prot.h> header file. */
+/* #undef HAVE_SYS_FS_AUTOFS_PROT_H */
+
+/* Define to 1 if you have the <sys/fs/cachefs_fs.h> header file. */
+/* #undef HAVE_SYS_FS_CACHEFS_FS_H */
+
+/* Define to 1 if you have the <sys/fs/efs_clnt.h> header file. */
+/* #undef HAVE_SYS_FS_EFS_CLNT_H */
+
+/* Define to 1 if you have the <sys/fs/nfs_clnt.h> header file. */
+/* #undef HAVE_SYS_FS_NFS_CLNT_H */
+
+/* Define to 1 if you have the <sys/fs/nfs.h> header file. */
+/* #undef HAVE_SYS_FS_NFS_H */
+
+/* Define to 1 if you have the <sys/fs/nfs/mount.h> header file. */
+/* #undef HAVE_SYS_FS_NFS_MOUNT_H */
+
+/* Define to 1 if you have the <sys/fs/nfs/nfs_clnt.h> header file. */
+/* #undef HAVE_SYS_FS_NFS_NFS_CLNT_H */
+
+/* Define to 1 if you have the <sys/fs/pc_fs.h> header file. */
+/* #undef HAVE_SYS_FS_PC_FS_H */
+
+/* Define to 1 if you have the <sys/fs/tmp.h> header file. */
+/* #undef HAVE_SYS_FS_TMP_H */
+
+/* Define to 1 if you have the <sys/fs_types.h> header file. */
+/* #undef HAVE_SYS_FS_TYPES_H */
+
+/* Define to 1 if you have the <sys/fs/ufs_mount.h> header file. */
+/* #undef HAVE_SYS_FS_UFS_MOUNT_H */
+
+/* Define to 1 if you have the <sys/fs/xfs_clnt.h> header file. */
+/* #undef HAVE_SYS_FS_XFS_CLNT_H */
+
+/* Define to 1 if you have the <sys/immu.h> header file. */
+/* #undef HAVE_SYS_IMMU_H */
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#define HAVE_SYS_IOCTL_H 1
+
+/* Define to 1 if you have the <sys/lock.h> header file. */
+#define HAVE_SYS_LOCK_H 1
+
+/* Define to 1 if you have the <sys/machine.h> header file. */
+/* #undef HAVE_SYS_MACHINE_H */
+
+/* Define to 1 if you have the <sys/mbuf.h> header file. */
+#define HAVE_SYS_MBUF_H 1
+
+/* Define to 1 if you have the <sys/mman.h> header file. */
+#define HAVE_SYS_MMAN_H 1
+
+/* Define to 1 if you have the <sys/mntctl.h> header file. */
+/* #undef HAVE_SYS_MNTCTL_H */
+
+/* Define to 1 if you have the <sys/mntent.h> header file. */
+/* #undef HAVE_SYS_MNTENT_H */
+
+/* Define to 1 if you have the <sys/mnttab.h> header file. */
+/* #undef HAVE_SYS_MNTTAB_H */
+
+/* Define to 1 if you have the <sys/mount.h> header file. */
+#define HAVE_SYS_MOUNT_H 1
+
+/* Define to 1 if you have the <sys/ndir.h> header file. */
+/* #undef HAVE_SYS_NDIR_H */
+
+/* Define to 1 if you have the <sys/netconfig.h> header file. */
+/* #undef HAVE_SYS_NETCONFIG_H */
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#define HAVE_SYS_PARAM_H 1
+
+/* Define to 1 if you have the <sys/pathconf.h> header file. */
+/* #undef HAVE_SYS_PATHCONF_H */
+
+/* Define to 1 if you have the <sys/proc.h> header file. */
+#define HAVE_SYS_PROC_H 1
+
+/* Define to 1 if you have the <sys/resource.h> header file. */
+#define HAVE_SYS_RESOURCE_H 1
+
+/* Define to 1 if you have the <sys/sema.h> header file. */
+#define HAVE_SYS_SEMA_H 1
+
+/* Define to 1 if you have the <sys/signal.h> header file. */
+#define HAVE_SYS_SIGNAL_H 1
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#define HAVE_SYS_SOCKET_H 1
+
+/* Define to 1 if you have the <sys/sockio.h> header file. */
+#define HAVE_SYS_SOCKIO_H 1
+
+/* Define to 1 if you have the <sys/statfs.h> header file. */
+/* #undef HAVE_SYS_STATFS_H */
+
+/* Define to 1 if you have the <sys/statvfs.h> header file. */
+#define HAVE_SYS_STATVFS_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/syscall.h> header file. */
+#define HAVE_SYS_SYSCALL_H 1
+
+/* Define to 1 if you have the <sys/syslimits.h> header file. */
+#define HAVE_SYS_SYSLIMITS_H 1
+
+/* Define to 1 if you have the <sys/syslog.h> header file. */
+#define HAVE_SYS_SYSLOG_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have the <sys/tiuser.h> header file. */
+/* #undef HAVE_SYS_TIUSER_H */
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <sys/ucred.h> header file. */
+#define HAVE_SYS_UCRED_H 1
+
+/* Define to 1 if you have the <sys/uio.h> header file. */
+#define HAVE_SYS_UIO_H 1
+
+/* Define to 1 if you have the <sys/utsname.h> header file. */
+#define HAVE_SYS_UTSNAME_H 1
+
+/* Define to 1 if you have the <sys/vfs.h> header file. */
+/* #undef HAVE_SYS_VFS_H */
+
+/* Define to 1 if you have the <sys/vmount.h> header file. */
+/* #undef HAVE_SYS_VMOUNT_H */
+
+/* Define to 1 if you have the <sys/vnode.h> header file. */
+#define HAVE_SYS_VNODE_H 1
+
+/* Define to 1 if you have <sys/wait.h> that is POSIX.1 compatible. */
+#define HAVE_SYS_WAIT_H 1
+
+/* Define to 1 if you have the <tcpd.h> header file. */
+#define HAVE_TCPD_H 1
+
+/* Define to 1 if you have the <time.h> header file. */
+#define HAVE_TIME_H 1
+
+/* Define to 1 if you have the <tiuser.h> header file. */
+/* #undef HAVE_TIUSER_H */
+
+/* Define to 1 if you have the <tmpfs/tmp.h> header file. */
+/* #undef HAVE_TMPFS_TMP_H */
+
+/* what type of network transport type is in use? TLI or sockets? */
+/* #undef HAVE_TRANSPORT_TYPE_TLI */
+
+/* Define to 1 if you have the `ualarm' function. */
+#define HAVE_UALARM 1
+
+/* Define to 1 if `flags' is member of `ufs_args_t'. */
+/* #undef HAVE_UFS_ARGS_T_FLAGS */
+
+/* Define to 1 if `fspec' is member of `ufs_args_t'. */
+#define HAVE_UFS_ARGS_T_FSPEC 1
+
+/* Define to 1 if `ufs_flags' is member of `ufs_args_t'. */
+/* #undef HAVE_UFS_ARGS_T_UFS_FLAGS */
+
+/* Define to 1 if `ufs_pgthresh' is member of `ufs_args_t'. */
+/* #undef HAVE_UFS_ARGS_T_UFS_PGTHRESH */
+
+/* Define to 1 if you have the <ufs/ufs/extattr.h> header file. */
+#define HAVE_UFS_UFS_EXTATTR_H 1
+
+/* Define to 1 if you have the <ufs/ufs_mount.h> header file. */
+/* #undef HAVE_UFS_UFS_MOUNT_H */
+
+/* Define to 1 if you have the <ufs/ufs/ufsmount.h> header file. */
+#define HAVE_UFS_UFS_UFSMOUNT_H 1
+
+/* Define to 1 if you have the `umount' function. */
+/* #undef HAVE_UMOUNT */
+
+/* Define to 1 if you have the `umount2' function. */
+/* #undef HAVE_UMOUNT2 */
+
+/* Define to 1 if you have the `uname' function. */
+#define HAVE_UNAME 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the `unmount' function. */
+#define HAVE_UNMOUNT 1
+
+/* Define to 1 if you have the `uvmount' function. */
+/* #undef HAVE_UVMOUNT */
+
+/* Define to 1 if you have the <varargs.h> header file. */
+/* #undef HAVE_VARARGS_H */
+
+/* Define to 1 if you have the `vfork' function. */
+#define HAVE_VFORK 1
+
+/* Define to 1 if you have the <vfork.h> header file. */
+/* #undef HAVE_VFORK_H */
+
+/* Define to 1 if you have the `vfsmount' function. */
+/* #undef HAVE_VFSMOUNT */
+
+/* Define to 1 if you have the `vmount' function. */
+/* #undef HAVE_VMOUNT */
+
+/* Define to 1 if you have the `vsnprintf' function. */
+#define HAVE_VSNPRINTF 1
+
+/* Define to 1 if you have the `wait3' function. */
+#define HAVE_WAIT3 1
+
+/* Define to 1 if you have the `waitpid' function. */
+#define HAVE_WAITPID 1
+
+/* Define to 1 if `fork' works. */
+#define HAVE_WORKING_FORK 1
+
+/* Define to 1 if `vfork' works. */
+#define HAVE_WORKING_VFORK 1
+
+/* Define to 1 if you have the `xdr_attrstat' function. */
+#define HAVE_XDR_ATTRSTAT 1
+
+/* Define to 1 if you have the `xdr_createargs' function. */
+#define HAVE_XDR_CREATEARGS 1
+
+/* Define to 1 if you have the `xdr_dirlist' function. */
+#define HAVE_XDR_DIRLIST 1
+
+/* Define to 1 if you have the `xdr_diropargs' function. */
+#define HAVE_XDR_DIROPARGS 1
+
+/* Define to 1 if you have the `xdr_diropokres' function. */
+#define HAVE_XDR_DIROPOKRES 1
+
+/* Define to 1 if you have the `xdr_diropres' function. */
+#define HAVE_XDR_DIROPRES 1
+
+/* Define to 1 if you have the `xdr_dirpath' function. */
+#define HAVE_XDR_DIRPATH 1
+
+/* Define to 1 if you have the `xdr_entry' function. */
+#define HAVE_XDR_ENTRY 1
+
+/* Define to 1 if you have the `xdr_exportnode' function. */
+#define HAVE_XDR_EXPORTNODE 1
+
+/* Define to 1 if you have the `xdr_exports' function. */
+#define HAVE_XDR_EXPORTS 1
+
+/* Define to 1 if you have the `xdr_fattr' function. */
+#define HAVE_XDR_FATTR 1
+
+/* Define to 1 if you have the `xdr_fhandle' function. */
+#define HAVE_XDR_FHANDLE 1
+
+/* Define to 1 if you have the `xdr_fhstatus' function. */
+#define HAVE_XDR_FHSTATUS 1
+
+/* Define to 1 if you have the `xdr_filename' function. */
+#define HAVE_XDR_FILENAME 1
+
+/* Define to 1 if you have the `xdr_ftype' function. */
+#define HAVE_XDR_FTYPE 1
+
+/* Define to 1 if you have the `xdr_groupnode' function. */
+#define HAVE_XDR_GROUPNODE 1
+
+/* Define to 1 if you have the `xdr_groups' function. */
+#define HAVE_XDR_GROUPS 1
+
+/* Define to 1 if you have the `xdr_linkargs' function. */
+#define HAVE_XDR_LINKARGS 1
+
+/* Define to 1 if you have the `xdr_mountbody' function. */
+#define HAVE_XDR_MOUNTBODY 1
+
+/* Define to 1 if you have the `xdr_mountlist' function. */
+#define HAVE_XDR_MOUNTLIST 1
+
+/* Define to 1 if you have the `xdr_name' function. */
+#define HAVE_XDR_NAME 1
+
+/* Define to 1 if you have the `xdr_nfscookie' function. */
+#define HAVE_XDR_NFSCOOKIE 1
+
+/* Define to 1 if you have the `xdr_nfspath' function. */
+#define HAVE_XDR_NFSPATH 1
+
+/* Define to 1 if you have the `xdr_nfsstat' function. */
+#define HAVE_XDR_NFSSTAT 1
+
+/* Define to 1 if you have the `xdr_nfstime' function. */
+#define HAVE_XDR_NFSTIME 1
+
+/* Define to 1 if you have the `xdr_nfs_fh' function. */
+#define HAVE_XDR_NFS_FH 1
+
+/* Define to 1 if you have the `xdr_pointer' function. */
+#define HAVE_XDR_POINTER 1
+
+/* Define to 1 if you have the `xdr_readargs' function. */
+#define HAVE_XDR_READARGS 1
+
+/* Define to 1 if you have the `xdr_readdirargs' function. */
+#define HAVE_XDR_READDIRARGS 1
+
+/* Define to 1 if you have the `xdr_readdirres' function. */
+#define HAVE_XDR_READDIRRES 1
+
+/* Define to 1 if you have the `xdr_readlinkres' function. */
+#define HAVE_XDR_READLINKRES 1
+
+/* Define to 1 if you have the `xdr_readokres' function. */
+#define HAVE_XDR_READOKRES 1
+
+/* Define to 1 if you have the `xdr_readres' function. */
+#define HAVE_XDR_READRES 1
+
+/* Define to 1 if you have the `xdr_renameargs' function. */
+#define HAVE_XDR_RENAMEARGS 1
+
+/* Define to 1 if you have the `xdr_sattr' function. */
+#define HAVE_XDR_SATTR 1
+
+/* Define to 1 if you have the `xdr_sattrargs' function. */
+#define HAVE_XDR_SATTRARGS 1
+
+/* Define to 1 if you have the `xdr_statfsokres' function. */
+#define HAVE_XDR_STATFSOKRES 1
+
+/* Define to 1 if you have the `xdr_statfsres' function. */
+#define HAVE_XDR_STATFSRES 1
+
+/* Define to 1 if you have the `xdr_symlinkargs' function. */
+#define HAVE_XDR_SYMLINKARGS 1
+
+/* Define to 1 if you have the `xdr_writeargs' function. */
+#define HAVE_XDR_WRITEARGS 1
+
+/* Define to 1 if `flags' is member of `xfs_args_t'. */
+/* #undef HAVE_XFS_ARGS_T_FLAGS */
+
+/* Define to 1 if `fspec' is member of `xfs_args_t'. */
+/* #undef HAVE_XFS_ARGS_T_FSPEC */
+
+/* Define to 1 if you have the `yp_all' function. */
+/* #undef HAVE_YP_ALL */
+
+/* Define to 1 if you have the `yp_get_default_domain' function. */
+#define HAVE_YP_GET_DEFAULT_DOMAIN 1
+
+/* Define to 1 if you have the `_seterr_reply' function. */
+#define HAVE__SETERR_REPLY 1
+
+/* Define to 1 if you have the `__rpc_get_local_uid' function. */
+#define HAVE___RPC_GET_LOCAL_UID 1
+
+/* Define to 1 if you have the `__seterr_reply' function. */
+/* #undef HAVE___SETERR_REPLY */
+
+/* Name of mount type to hide amd mount from df(1) */
+#define HIDE_MOUNT_TYPE "nfs"
+
+/* Define name of host machine's architecture (eg. sun4) */
+/* #define HOST_ARCH "i386" */
+
+/* Define name of host machine's cpu (eg. sparc) */
+/* #define HOST_CPU "i386" */
+
+/* Define the header version of (linux) hosts (eg. 2.2.10) */
+// #undef define HOST_HEADER_VERSION */
+
+/* Define name of host */
+/* #define HOST_NAME "trang.nuxi.org" */
+
+/* Define name and version of host machine (eg. solaris2.5.1) */
+/* #define HOST_OS "freebsd8.0" */
+
+/* Define only name of host machine OS (eg. solaris2) */
+/* #define HOST_OS_NAME "freebsd8" */
+
+/* Define only version of host machine (eg. 2.5.1) */
+/* #define HOST_OS_VERSION "8.0" */
+
+/* Define name of host machine's vendor (eg. sun) */
+#define HOST_VENDOR "undermydesk"
+
+/* Ignore permission bits */
+/* #undef MNT2_CDFS_OPT_DEFPERM */
+
+/* Use on-disk permission bits */
+/* #undef MNT2_CDFS_OPT_NODEFPERM */
+
+/* Strip off extension from version string */
+/* #undef MNT2_CDFS_OPT_NOVERSION */
+
+/* Use Rock Ridge Interchange Protocol (RRIP) extensions */
+/* #undef MNT2_CDFS_OPT_RRIP */
+
+/* asynchronous filesystem access */
+#define MNT2_GEN_OPT_ASYNC 0x40
+
+/* automounter filesystem (ignore) flag, used in bsdi-4.1 */
+/* #undef MNT2_GEN_OPT_AUTOMNTFS */
+
+/* automounter filesystem flag, used in Mac OS X / Darwin */
+/* #undef MNT2_GEN_OPT_AUTOMOUNTED */
+
+/* directory hardlink */
+/* #undef MNT2_GEN_OPT_BIND */
+
+/* cache (what?) */
+/* #undef MNT2_GEN_OPT_CACHE */
+
+/* 6-argument mount */
+/* #undef MNT2_GEN_OPT_DATA */
+
+/* Use a lazy unmount (detach) */
+/* #undef MNT2_GEN_OPT_DETACH */
+
+/* Use a forced unmount */
+#define MNT2_GEN_OPT_FORCE 0x80000
+
+/* old (4-argument) mount (compatibility) */
+/* #undef MNT2_GEN_OPT_FSS */
+
+/* old BSD group-id on create */
+/* #undef MNT2_GEN_OPT_GRPID */
+
+/* ignore mount entry in df output */
+#define MNT2_GEN_OPT_IGNORE 0x800000
+
+/* journaling filesystem (AIX's UFS/FFS) */
+/* #undef MNT2_GEN_OPT_JFS */
+
+/* do multi-component lookup on files */
+/* #undef MNT2_GEN_OPT_MULTI */
+
+/* use type string instead of int */
+/* #undef MNT2_GEN_OPT_NEWTYPE */
+
+/* NFS mount */
+/* #undef MNT2_GEN_OPT_NFS */
+
+/* nocache (what?) */
+/* #undef MNT2_GEN_OPT_NOCACHE */
+
+/* do not interpret special device files */
+#define MNT2_GEN_OPT_NODEV 0x0
+
+/* no exec calls allowed */
+#define MNT2_GEN_OPT_NOEXEC 0x4
+
+/* do not interpret special device files */
+/* #undef MNT2_GEN_OPT_NONDEV */
+
+/* Disallow mounts beneath this mount */
+/* #undef MNT2_GEN_OPT_NOSUB */
+
+/* Setuid programs disallowed */
+#define MNT2_GEN_OPT_NOSUID 0x8
+
+/* Return ENAMETOOLONG for long filenames */
+/* #undef MNT2_GEN_OPT_NOTRUNC */
+
+/* Pass mount option string to kernel */
+/* #undef MNT2_GEN_OPT_OPTIONSTR */
+
+/* allow overlay mounts */
+/* #undef MNT2_GEN_OPT_OVERLAY */
+
+/* check quotas */
+#define MNT2_GEN_OPT_QUOTA 0x2000
+
+/* Read-only */
+#define MNT2_GEN_OPT_RDONLY 0x1
+
+/* change options on an existing mount */
+/* #undef MNT2_GEN_OPT_REMOUNT */
+
+/* read only */
+/* #undef MNT2_GEN_OPT_RONLY */
+
+/* synchronize data immediately to filesystem */
+/* #undef MNT2_GEN_OPT_SYNC */
+
+/* synchronous filesystem access (same as SYNC) */
+#define MNT2_GEN_OPT_SYNCHRONOUS 0x2
+
+/* Mount with Sys 5-specific semantics */
+/* #undef MNT2_GEN_OPT_SYS5 */
+
+/* Union mount */
+/* #undef MNT2_GEN_OPT_UNION */
+
+/* set max secs for dir attr cache */
+#define MNT2_NFS_OPT_ACDIRMAX 0x200000
+
+/* set min secs for dir attr cache */
+#define MNT2_NFS_OPT_ACDIRMIN 0x100000
+
+/* set max secs for file attr cache */
+#define MNT2_NFS_OPT_ACREGMAX 0x80000
+
+/* set min secs for file attr cache */
+#define MNT2_NFS_OPT_ACREGMIN 0x40000
+
+/* Authentication error */
+/* #undef MNT2_NFS_OPT_AUTHERR */
+
+/* hide mount type from df(1) */
+/* #undef MNT2_NFS_OPT_AUTO */
+
+/* set dead server retry thresh */
+#define MNT2_NFS_OPT_DEADTHRESH 0x4000
+
+/* Dismount in progress */
+/* #undef MNT2_NFS_OPT_DISMINPROG */
+
+/* Dismounted */
+/* #undef MNT2_NFS_OPT_DISMNT */
+
+/* Don't estimate rtt dynamically */
+#define MNT2_NFS_OPT_DUMBTIMR 0x800
+
+/* provide name of server's fs to system */
+/* #undef MNT2_NFS_OPT_FSNAME */
+
+/* System V-style gid inheritance */
+/* #undef MNT2_NFS_OPT_GRPID */
+
+/* Has authenticator */
+/* #undef MNT2_NFS_OPT_HASAUTH */
+
+/* set hostname for error printf */
+/* #undef MNT2_NFS_OPT_HOSTNAME */
+
+/* ignore mount point */
+/* #undef MNT2_NFS_OPT_IGNORE */
+
+/* allow interrupts on hard mount */
+#define MNT2_NFS_OPT_INT 0x40
+
+/* Bits set internally */
+/* #undef MNT2_NFS_OPT_INTERNAL */
+
+/* allow interrupts on hard mount */
+/* #undef MNT2_NFS_OPT_INTR */
+
+/* Use Kerberos authentication */
+/* #undef MNT2_NFS_OPT_KERB */
+
+/* use kerberos credentials */
+/* #undef MNT2_NFS_OPT_KERBEROS */
+
+/* transport's knetconfig structure */
+/* #undef MNT2_NFS_OPT_KNCONF */
+
+/* set lease term (nqnfs) */
+/* #undef MNT2_NFS_OPT_LEASETERM */
+
+/* Local locking (no lock manager) */
+/* #undef MNT2_NFS_OPT_LLOCK */
+
+/* set maximum grouplist size */
+#define MNT2_NFS_OPT_MAXGRPS 0x20
+
+/* Mnt server for mnt point */
+/* #undef MNT2_NFS_OPT_MNTD */
+
+/* Assume writes were mine */
+/* #undef MNT2_NFS_OPT_MYWRITE */
+
+/* mount NFS Version 3 */
+#define MNT2_NFS_OPT_NFSV3 0x200
+
+/* don't cache attributes */
+/* #undef MNT2_NFS_OPT_NOAC */
+
+/* Don't Connect the socket */
+#define MNT2_NFS_OPT_NOCONN 0x80
+
+/* no close-to-open consistency */
+/* #undef MNT2_NFS_OPT_NOCTO */
+
+/* disallow interrupts on hard mounts */
+/* #undef MNT2_NFS_OPT_NOINT */
+
+/* Don't use locking */
+/* #undef MNT2_NFS_OPT_NONLM */
+
+/* Get lease for lookup */
+/* #undef MNT2_NFS_OPT_NQLOOKLEASE */
+
+/* Use Nqnfs protocol */
+/* #undef MNT2_NFS_OPT_NQNFS */
+
+/* paging threshold */
+/* #undef MNT2_NFS_OPT_PGTHRESH */
+
+/* static pathconf kludge info */
+/* #undef MNT2_NFS_OPT_POSIX */
+
+/* Use local locking */
+/* #undef MNT2_NFS_OPT_PRIVATE */
+
+/* allow property list operations (ACLs over NFS) */
+/* #undef MNT2_NFS_OPT_PROPLIST */
+
+/* Rcv socket lock */
+/* #undef MNT2_NFS_OPT_RCVLOCK */
+
+/* Do lookup with readdir (nqnfs) */
+/* #undef MNT2_NFS_OPT_RDIRALOOK */
+
+/* Use Readdirplus for NFSv3 */
+#define MNT2_NFS_OPT_RDIRPLUS 0x10000
+
+/* set read ahead */
+#define MNT2_NFS_OPT_READAHEAD 0x2000
+
+/* Set readdir size */
+#define MNT2_NFS_OPT_READDIRSIZE 0x20000
+
+/* Allocate a reserved port */
+#define MNT2_NFS_OPT_RESVPORT 0x8000
+
+/* set number of request retries */
+#define MNT2_NFS_OPT_RETRANS 0x10
+
+/* read only */
+/* #undef MNT2_NFS_OPT_RONLY */
+
+/* use RPC to do secure NFS time sync */
+/* #undef MNT2_NFS_OPT_RPCTIMESYNC */
+
+/* set read size */
+#define MNT2_NFS_OPT_RSIZE 0x4
+
+/* secure mount */
+/* #undef MNT2_NFS_OPT_SECURE */
+
+/* Send socket lock */
+/* #undef MNT2_NFS_OPT_SNDLOCK */
+
+/* soft mount (hard is default) */
+#define MNT2_NFS_OPT_SOFT 0x1
+
+/* spongy mount */
+/* #undef MNT2_NFS_OPT_SPONGY */
+
+/* set symlink cache time-to-live */
+/* #undef MNT2_NFS_OPT_SYMTTL */
+
+/* use TCP for mounts */
+/* #undef MNT2_NFS_OPT_TCP */
+
+/* set initial timeout */
+#define MNT2_NFS_OPT_TIMEO 0x8
+
+/* linux NFSv3 */
+/* #undef MNT2_NFS_OPT_VER3 */
+
+/* Wait for authentication */
+/* #undef MNT2_NFS_OPT_WAITAUTH */
+
+/* Wants an authenticator */
+/* #undef MNT2_NFS_OPT_WANTAUTH */
+
+/* Want receive socket lock */
+/* #undef MNT2_NFS_OPT_WANTRCV */
+
+/* Want send socket lock */
+/* #undef MNT2_NFS_OPT_WANTSND */
+
+/* set write size */
+#define MNT2_NFS_OPT_WSIZE 0x2
+
+/* 32<->64 dir cookie translation */
+/* #undef MNT2_NFS_OPT_XLATECOOKIE */
+
+/* Force Win95 long names */
+#define MNT2_PCFS_OPT_LONGNAME 0x2
+
+/* Completely ignore Win95 entries */
+#define MNT2_PCFS_OPT_NOWIN95 0x4
+
+/* Force old DOS short names only */
+#define MNT2_PCFS_OPT_SHORTNAME 0x1
+
+/* Name of mount table file name */
+/* #undef MNTTAB_FILE_NAME */
+
+/* Mount Table option string: Max attr cache timeout (dirs) */
+/* #undef MNTTAB_OPT_ACDIRMAX */
+
+/* Mount Table option string: Min attr cache timeout (dirs) */
+/* #undef MNTTAB_OPT_ACDIRMIN */
+
+/* Mount Table option string: Max attr cache timeout (files) */
+/* #undef MNTTAB_OPT_ACREGMAX */
+
+/* Mount Table option string: Min attr cache timeout (files) */
+/* #undef MNTTAB_OPT_ACREGMIN */
+
+/* Mount Table option string: Attr cache timeout (sec) */
+/* #undef MNTTAB_OPT_ACTIMEO */
+
+/* Mount Table option string: Do mount retries in background */
+/* #undef MNTTAB_OPT_BG */
+
+/* Mount Table option string: compress */
+/* #undef MNTTAB_OPT_COMPRESS */
+
+/* Mount Table option string: Device id of mounted fs */
+/* #undef MNTTAB_OPT_DEV */
+
+/* Mount Table option string: Automount direct map mount */
+/* #undef MNTTAB_OPT_DIRECT */
+
+/* Mount Table option string: Do mount retries in foreground */
+/* #undef MNTTAB_OPT_FG */
+
+/* Mount Table option string: Filesystem id of mounted fs */
+/* #undef MNTTAB_OPT_FSID */
+
+/* Mount Table option string: SysV-compatible gid on create */
+/* #undef MNTTAB_OPT_GRPID */
+
+/* Mount Table option string: Hard mount */
+/* #undef MNTTAB_OPT_HARD */
+
+/* Mount Table option string: Ignore this entry */
+/* #undef MNTTAB_OPT_IGNORE */
+
+/* Mount Table option string: Automount indirect map mount */
+/* #undef MNTTAB_OPT_INDIRECT */
+
+/* Mount Table option string: Allow NFS ops to be interrupted */
+/* #undef MNTTAB_OPT_INTR */
+
+/* Mount Table option string: Secure (AUTH_Kerb) mounting */
+/* #undef MNTTAB_OPT_KERB */
+
+/* Mount Table option string: Local locking (no lock manager) */
+/* #undef MNTTAB_OPT_LLOCK */
+
+/* Force Win95 long names */
+/* #undef MNTTAB_OPT_LONGNAME */
+
+/* Mount Table option string: Automount map */
+/* #undef MNTTAB_OPT_MAP */
+
+/* Mount Table option string: max groups */
+/* #undef MNTTAB_OPT_MAXGROUPS */
+
+/* Mount Table option string: Do multi-component lookup */
+/* #undef MNTTAB_OPT_MULTI */
+
+/* Mount Table option string: Don't cache attributes at all */
+/* #undef MNTTAB_OPT_NOAC */
+
+/* Mount Table option string: No auto (what?) */
+/* #undef MNTTAB_OPT_NOAUTO */
+
+/* Mount Table option string: No connection */
+/* #undef MNTTAB_OPT_NOCONN */
+
+/* Mount Table option string: No close-to-open consistency */
+/* #undef MNTTAB_OPT_NOCTO */
+
+/* Mount Table option string: Don't allow interrupted ops */
+/* #undef MNTTAB_OPT_NOINTR */
+
+/* Mount Table option string: Don't check quotas */
+/* #undef MNTTAB_OPT_NOQUOTA */
+
+/* Mount Table option string: Do no allow setting sec attrs */
+/* #undef MNTTAB_OPT_NOSETSEC */
+
+/* Mount Table option string: Disallow mounts on subdirs */
+/* #undef MNTTAB_OPT_NOSUB */
+
+/* Mount Table option string: Set uid not allowed */
+/* #undef MNTTAB_OPT_NOSUID */
+
+/* Completely ignore Win95 entries */
+/* #undef MNTTAB_OPT_NOWIN95 */
+
+/* Mount Table option string: action to taken on error */
+/* #undef MNTTAB_OPT_ONERROR */
+
+/* Mount Table option string: paging threshold */
+/* #undef MNTTAB_OPT_PGTHRESH */
+
+/* Mount Table option string: NFS server IP port number */
+/* #undef MNTTAB_OPT_PORT */
+
+/* Mount Table option string: Get static pathconf for mount */
+/* #undef MNTTAB_OPT_POSIX */
+
+/* Mount Table option string: Use local locking */
+/* #undef MNTTAB_OPT_PRIVATE */
+
+/* Mount Table option string: support property lists (ACLs) */
+/* #undef MNTTAB_OPT_PROPLIST */
+
+/* Mount Table option string: protocol network_id indicator */
+/* #undef MNTTAB_OPT_PROTO */
+
+/* Mount Table option string: Check quotas */
+/* #undef MNTTAB_OPT_QUOTA */
+
+/* Mount Table option string: Change mount options */
+/* #undef MNTTAB_OPT_REMOUNT */
+
+/* Mount Table option string: Max retransmissions (soft mnts) */
+/* #undef MNTTAB_OPT_RETRANS */
+
+/* Mount Table option string: Number of mount retries */
+/* #undef MNTTAB_OPT_RETRY */
+
+/* Mount Table option string: Read only */
+/* #undef MNTTAB_OPT_RO */
+
+/* Mount Table option string: Read/write with quotas */
+/* #undef MNTTAB_OPT_RQ */
+
+/* Mount Table option string: Max NFS read size (bytes) */
+/* #undef MNTTAB_OPT_RSIZE */
+
+/* Mount Table option string: Read/write */
+/* #undef MNTTAB_OPT_RW */
+
+/* Mount Table option string: Secure (AUTH_DES) mounting */
+/* #undef MNTTAB_OPT_SECURE */
+
+/* Force old DOS short names only */
+/* #undef MNTTAB_OPT_SHORTNAME */
+
+/* Mount Table option string: Soft mount */
+/* #undef MNTTAB_OPT_SOFT */
+
+/* Mount Table option string: spongy mount */
+/* #undef MNTTAB_OPT_SPONGY */
+
+/* Mount Table option string: Set uid allowed */
+/* #undef MNTTAB_OPT_SUID */
+
+/* Mount Table option string: set symlink cache time-to-live */
+/* #undef MNTTAB_OPT_SYMTTL */
+
+/* Mount Table option string: Synchronous local directory ops */
+/* #undef MNTTAB_OPT_SYNCDIR */
+
+/* Mount Table option string: NFS timeout (1/10 sec) */
+/* #undef MNTTAB_OPT_TIMEO */
+
+/* Mount Table option string: min. time between inconsistencies */
+/* #undef MNTTAB_OPT_TOOSOON */
+
+/* Mount Table option string: protocol version number indicator */
+/* #undef MNTTAB_OPT_VERS */
+
+/* Mount Table option string: Max NFS write size (bytes) */
+/* #undef MNTTAB_OPT_WSIZE */
+
+/* Mount-table entry name for AUTOFS filesystem */
+/* #undef MNTTAB_TYPE_AUTOFS */
+
+/* Mount-table entry name for CACHEFS filesystem */
+/* #undef MNTTAB_TYPE_CACHEFS */
+
+/* Mount-table entry name for CDFS filesystem */
+#define MNTTAB_TYPE_CDFS "cd9660"
+
+/* Mount-table entry name for CFS (crypto) filesystem */
+/* #undef MNTTAB_TYPE_CFS */
+
+/* Mount-table entry name for EFS filesystem (irix) */
+/* #undef MNTTAB_TYPE_EFS */
+
+/* Mount-table entry name for FFS filesystem */
+/* #undef MNTTAB_TYPE_FFS */
+
+/* Mount-table entry name for LOFS filesystem */
+/* #undef MNTTAB_TYPE_LOFS */
+
+/* Mount-table entry name for MFS filesystem */
+/* #undef MNTTAB_TYPE_MFS */
+
+/* Mount-table entry name for NFS filesystem */
+#define MNTTAB_TYPE_NFS "nfs"
+
+/* Mount-table entry name for NFS3 filesystem */
+#define MNTTAB_TYPE_NFS3 "nfs3"
+
+/* Mount-table entry name for NULLFS (loopback on bsd44) filesystem */
+#define MNTTAB_TYPE_NULLFS "nullfs"
+
+/* Mount-table entry name for PCFS filesystem */
+#define MNTTAB_TYPE_PCFS "msdosfs"
+
+/* Mount-table entry name for TFS filesystem */
+/* #undef MNTTAB_TYPE_TFS */
+
+/* Mount-table entry name for TMPFS filesystem */
+/* #undef MNTTAB_TYPE_TMPFS */
+
+/* Mount-table entry name for UFS filesystem */
+#define MNTTAB_TYPE_UFS "ufs"
+
+/* Mount-table entry name for UMAPFS (uid/gid mapping) filesystem */
+/* #undef MNTTAB_TYPE_UMAPFS */
+
+/* Mount-table entry name for UNIONFS filesystem */
+#define MNTTAB_TYPE_UNIONFS "unionfs"
+
+/* Mount-table entry name for XFS filesystem (irix) */
+/* #undef MNTTAB_TYPE_XFS */
+
+/* Define if mount table is on file, undefine if in kernel */
+/* #undef MOUNT_TABLE_ON_FILE */
+
+/* Mount(2) type/name for AUTOFS filesystem */
+/* #undef MOUNT_TYPE_AUTOFS */
+
+/* Mount(2) type/name for CACHEFS filesystem */
+/* #undef MOUNT_TYPE_CACHEFS */
+
+/* Mount(2) type/name for CDFS filesystem */
+#define MOUNT_TYPE_CDFS "cd9660"
+
+/* Mount(2) type/name for CFS (crypto) filesystem */
+/* #undef MOUNT_TYPE_CFS */
+
+/* Mount(2) type/name for EFS filesystem (irix) */
+/* #undef MOUNT_TYPE_EFS */
+
+/* Mount(2) type/name for FFS filesystem */
+/* #undef MOUNT_TYPE_FFS */
+
+/* Mount(2) type/name for IGNORE filesystem (not real just ignore for df) */
+#define MOUNT_TYPE_IGNORE MNT_IGNORE
+
+/* Mount(2) type/name for LOFS filesystem */
+/* #undef MOUNT_TYPE_LOFS */
+
+/* Mount(2) type/name for MFS filesystem */
+/* #undef MOUNT_TYPE_MFS */
+
+/* Mount(2) type/name for NFS filesystem */
+#define MOUNT_TYPE_NFS "nfs"
+
+/* Mount(2) type/name for NFS3 filesystem */
+#define MOUNT_TYPE_NFS3 MOUNT_NFS3
+
+/* Mount(2) type/name for NULLFS (loopback on bsd44) filesystem */
+#define MOUNT_TYPE_NULLFS "nullfs"
+
+/* Mount(2) type/name for PCFS filesystem. XXX: conf/trap/trap_hpux.h may
+ override this definition for HPUX 9.0 */
+#define MOUNT_TYPE_PCFS "msdosfs"
+
+/* Mount(2) type/name for TFS filesystem */
+/* #undef MOUNT_TYPE_TFS */
+
+/* Mount(2) type/name for TMPFS filesystem */
+/* #undef MOUNT_TYPE_TMPFS */
+
+/* Mount(2) type/name for UFS filesystem */
+#define MOUNT_TYPE_UFS "ufs"
+
+/* Mount(2) type/name for UMAPFS (uid/gid mapping) filesystem */
+/* #undef MOUNT_TYPE_UMAPFS */
+
+/* Mount(2) type/name for UNIONFS filesystem */
+#define MOUNT_TYPE_UNIONFS MNT_UNION
+
+/* Mount(2) type/name for XFS filesystem (irix) */
+/* #undef MOUNT_TYPE_XFS */
+
+/* The string used in printf to print the mount-type field of mount(2) */
+#define MTYPE_PRINTF_TYPE "%s"
+
+/* Type of the mount-type field in the mount() system call */
+#define MTYPE_TYPE char *
+
+/* does libwrap expect caller to define the variables allow_severity and
+ deny_severity */
+/* #undef NEED_LIBWRAP_SEVERITY_VARIABLES */
+
+/* Defined to the header file containing ndbm-compatible definitions */
+#define NEW_DBM_H <ndbm.h>
+
+/* Define the field name for the filehandle within nfs_args_t */
+#define NFS_FH_FIELD fh
+
+/* Define to 1 if your C compiler doesn't accept -c and -o together. */
+/* #undef NO_MINUS_C_MINUS_O */
+
+/* Name of package */
+#define PACKAGE "am-utils"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "https://bugzilla.am-utils.org/ or am-utils@am-utils.org"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "am-utils"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "am-utils 6.1.5"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "am-utils"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "6.1.5"
+
+/* Type of the 6th argument to recvfrom() */
+#define RECVFROM_FROMLEN_TYPE int
+
+/* should signal handlers be reinstalled? */
+/* #undef REINSTALL_SIGNAL_HANDLER */
+
+/* Define as the return type of signal handlers (`int' or `void'). */
+#define RETSIGTYPE void
+
+/* Define to 1 if the `setpgrp' function takes no argument. */
+/* #undef SETPGRP_VOID */
+
+/* Define to 1 if the `S_IS*' macros in <sys/stat.h> do not work properly. */
+/* #undef STAT_MACROS_BROKEN */
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define the type of the 3rd argument ('in') to svc_getargs() */
+#define SVC_IN_ARG_TYPE caddr_t
+
+/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+#define TIME_WITH_SYS_TIME 1
+
+/* Define to 1 if your <sys/time.h> declares `struct tm'. */
+/* #undef TM_IN_SYS_TIME */
+
+/* Define user name */
+/* #define USER_NAME "obrien" */
+
+/* define if must NOT use NFS "noconn" option */
+#define USE_CONNECTED_NFS_SOCKETS 1
+
+/* define if must use NFS "noconn" option */
+/* #undef USE_UNCONNECTED_NFS_SOCKETS */
+
+/* Version number of package */
+#define VERSION "6.1.5"
+
+/* Define to 1 if your processor stores words with the most significant byte
+ first (like Motorola and SPARC, unlike Intel and VAX). */
+/* #undef WORDS_BIGENDIAN */
+
+/* Define to the type of xdr procedure type */
+#define XDRPROC_T_TYPE xdrproc_t
+
+/* Type of the 3rd argument to yp_order() */
+#define YP_ORDER_OUTORDER_TYPE int
+
+/* Define to 1 if `lex' declares `yytext' as a `char *' by default, not a
+ `char[]'. */
+#define YYTEXT_POINTER 1
+
+/* Define to 1 if on AIX 3.
+ System headers sometimes define this.
+ We just want to avoid a redefinition error message. */
+#ifndef _ALL_SOURCE
+/* # undef _ALL_SOURCE */
+#endif
+
+/* Define a type/structure for an NFS V2 filehandle */
+#define am_nfs_fh nfs_fh
+
+/* Define a type/structure for an NFS V3 filehandle */
+#define am_nfs_fh3 nfs_fh3_freebsd3
+
+/* Define a type for the autofs_args structure */
+/* #undef autofs_args_t */
+
+/* Define a type for the cachefs_args structure */
+/* #undef cachefs_args_t */
+
+/* Define a type for the cdfs_args structure */
+#define cdfs_args_t struct iso_args
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define a type for the efs_args structure */
+/* #undef efs_args_t */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef gid_t */
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+ calls it, or to nothing if 'inline' is not supported under any name. */
+#ifndef __cplusplus
+/* #undef inline */
+#endif
+
+/* Define a type for the lofs_args structure */
+/* #undef lofs_args_t */
+
+/* Define a type for the mfs_args structure */
+/* #undef mfs_args_t */
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef mode_t */
+
+/* Define a type for the nfs_args structure */
+#define nfs_args_t struct nfs_args
+
+/* Define a type for the pcfs_args structure */
+#define pcfs_args_t struct msdosfs_args
+
+/* Define to `int' if <sys/types.h> does not define. */
+/* #undef pid_t */
+
+/* Check if pte_t is defined in <sys/immu.h> */
+/* #undef pte_t */
+
+/* Define a type for the rfs_args structure */
+/* #undef rfs_args_t */
+
+/* Check if rpcvers_t is defined in <rpc/types.h> */
+/* #undef rpcvers_t */
+
+/* Define to `unsigned' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define to `long' if <sys/types.h> does not define. */
+/* #undef time_t */
+
+/* Define a type for the tmpfs_args structure */
+/* #undef tmpfs_args_t */
+
+/* Define a type for the ufs_args structure */
+#define ufs_args_t struct ufs_args
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef uid_t */
+
+/* Define as `fork' if `vfork' does not work. */
+/* #undef vfork */
+
+/* Define to "void *" if compiler can handle, otherwise "char *" */
+#define voidp void *
+
+/* Define to empty if the keyword `volatile' does not work. Warning: valid
+ code using `volatile' can become incorrect without. Disable with care. */
+/* #undef volatile */
+
+/* Define a type for the xfs_args structure */
+/* #undef xfs_args_t */
+
+
+/****************************************************************************/
+/*** INCLUDE localconfig.h if it exists, to allow users to make some ***/
+/*** compile time configuration changes. ***/
+/****************************************************************************/
+/* does a local configuration file exist? */
+/* #undef HAVE_LOCALCONFIG_H */
+#ifdef HAVE_LOCALCONFIG_H
+# include <localconfig.h>
+#endif /* HAVE_LOCALCONFIG_H */
+
+#endif /* not _CONFIG_H */
+
+/*
+ * Local Variables:
+ * mode: c
+ * End:
+ */
+
+/* End of am-utils-6.x config.h file */
diff --git a/usr.sbin/amd/include/newvers.sh b/usr.sbin/amd/include/newvers.sh
new file mode 100644
index 0000000..e192ee9
--- /dev/null
+++ b/usr.sbin/amd/include/newvers.sh
@@ -0,0 +1,43 @@
+# $NetBSD: mkconf,v 1.1.1.1 1997/07/24 21:20:12 christos Exp $
+# $FreeBSD$
+# mkconf
+# Generate local configuration parameters for amd
+#
+
+if [ -e $1 ]; then
+ eval `LC_ALL=C egrep '^[A-Z]+=' $1 | grep -v COPYRIGHT`
+ OS=`echo ${TYPE} | LC_ALL=C tr 'A-Z' 'a-z'`
+ echo '/* Define name and version of host machine (eg. solaris2.5.1) */'
+ echo "#define HOST_OS \"${OS}${REVISION}\""
+ echo '/* Define only name of host machine OS (eg. solaris2) */'
+ echo "#define HOST_OS_NAME \"${OS}${REVISION}\"" \
+ | sed -e 's/\.[-._0-9]*//'
+ echo '/* Define only version of host machine (eg. 2.5.1) */'
+ echo "#define HOST_OS_VERSION \"${REVISION}\""
+else
+cat << __NO_newvers_sh
+
+/* Define name and version of host machine (eg. solaris2.5.1) */
+#define HOST_OS "`uname -s | LC_ALL=C tr 'A-Z' 'a-z'``uname -r`"
+
+/* Define only name of host machine OS (eg. solaris2) */
+#define HOST_OS_NAME "`uname -s | LC_ALL=C tr 'A-Z' 'a-z'``uname -r | sed -e 's/\..*$//'`"
+
+/* Define only version of host machine (eg. 2.5.1) */
+#define HOST_OS_VERSION "`uname -r | sed -e 's/[-([:alpha:]].*//'`"
+
+__NO_newvers_sh
+fi
+
+cat << __EOF
+
+/* Define name of host */
+#define BUILD_HOST "`hostname`"
+
+/* Define user name */
+#define BUILD_USER "`whoami`"
+
+/* Define configuration date */
+#define BUILD_DATE "`LC_ALL=C date`"
+
+__EOF
diff --git a/usr.sbin/amd/libamu/Makefile b/usr.sbin/amd/libamu/Makefile
new file mode 100644
index 0000000..fa8e3ab
--- /dev/null
+++ b/usr.sbin/amd/libamu/Makefile
@@ -0,0 +1,36 @@
+# ex:ts=8
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998
+#
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/amd/libamu \
+ ${.CURDIR}/../../../contrib/amd/conf/transp \
+ ${.CURDIR}/../../../contrib/amd/conf/mtab \
+ ${.CURDIR}/../../../contrib/amd/conf/umount
+
+LIB= amu
+INTERNALLIB=
+SRCS= hasmntopt.c misc_rpc.c mount_fs.c mtab.c nfs_prot_xdr.c \
+ strutil.c wire.c xutil.c
+
+# These would be links created by the GNU-style configure
+SRCS+= transp_sockets.c mtab_bsd.c umount_bsd44.c
+
+# Generated at compile time (replaces supplied xdr_func.c)
+SRCS+= nfs_prot_x.c xdr_func_%undef.c
+CLEANFILES+= nfs_prot_x.c xdr_func_%undef.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/amd/libamu \
+ -I${DESTDIR}/usr/include/rpcsvc
+
+nfs_prot_x.c: ${NFS_PROT_X}
+ ${RPCCOM} -c -C -DWANT_NFS3 ${NFS_PROT_X} -o ${.TARGET}
+
+XDRDEFS!= grep 'ifndef.*HAVE_XDR' ${.CURDIR}/../../../contrib/amd/libamu/xdr_func.c | awk '{print "-D"$$2}'
+
+xdr_func_%undef.c: xdr_func.c
+ -unifdef ${XDRDEFS} < ${.ALLSRC} > ${.TARGET}
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/amd/mk-amd-map/Makefile b/usr.sbin/amd/mk-amd-map/Makefile
new file mode 100644
index 0000000..57fd6a5
--- /dev/null
+++ b/usr.sbin/amd/mk-amd-map/Makefile
@@ -0,0 +1,16 @@
+# ex:ts=8
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998
+#
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/amd/mk-amd-map
+
+PROG= mk-amd-map
+MAN= mk-amd-map.8
+
+DPADD= ${LIBAMU}
+LDADD= ${LIBAMU}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/amd/pawd/Makefile b/usr.sbin/amd/pawd/Makefile
new file mode 100644
index 0000000..c6bb1cc
--- /dev/null
+++ b/usr.sbin/amd/pawd/Makefile
@@ -0,0 +1,20 @@
+# ex:ts=8
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998
+#
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/amd/amq
+
+BINDIR= /usr/bin
+
+PROG= pawd
+SRCS= pawd.c amq_clnt.c amq_xdr.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/amd/amq
+
+DPADD= ${LIBAMU}
+LDADD= ${LIBAMU}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/amd/scripts/Makefile b/usr.sbin/amd/scripts/Makefile
new file mode 100644
index 0000000..822b951
--- /dev/null
+++ b/usr.sbin/amd/scripts/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/amd/scripts
+
+MAN= amd.conf.5
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/amd/wire-test/Makefile b/usr.sbin/amd/wire-test/Makefile
new file mode 100644
index 0000000..a07e690
--- /dev/null
+++ b/usr.sbin/amd/wire-test/Makefile
@@ -0,0 +1,16 @@
+# ex:ts=8
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998
+#
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/amd/wire-test
+
+PROG= wire-test
+MAN= wire-test.8
+
+DPADD= ${LIBAMU}
+LDADD= ${LIBAMU}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ancontrol/Makefile b/usr.sbin/ancontrol/Makefile
new file mode 100644
index 0000000..b0f66cd
--- /dev/null
+++ b/usr.sbin/ancontrol/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+PROG= ancontrol
+MAN= ancontrol.8
+
+WARNS?= 3
+CFLAGS+= -DANCACHE -I${.CURDIR}/../../sys
+
+DPADD= ${LIBMD}
+LDADD= -lmd
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ancontrol/ancontrol.8 b/usr.sbin/ancontrol/ancontrol.8
new file mode 100644
index 0000000..5ce4377
--- /dev/null
+++ b/usr.sbin/ancontrol/ancontrol.8
@@ -0,0 +1,553 @@
+.\" Copyright (c) 1997, 1998, 1999
+.\" Bill Paul <wpaul@ee.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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd September 10, 1999
+.Dt ANCONTROL 8
+.Os
+.Sh NAME
+.Nm ancontrol
+.Nd configure Aironet 4500/4800 devices
+.Sh SYNOPSIS
+.Nm
+.Fl i Ar iface Fl A
+.Nm
+.Fl i Ar iface Fl N
+.Nm
+.Fl i Ar iface Fl S
+.Nm
+.Fl i Ar iface Fl I
+.Nm
+.Fl i Ar iface Fl T
+.Nm
+.Fl i Ar iface Fl C
+.Nm
+.Fl i Ar iface Fl Q
+.Nm
+.Fl i Ar iface Fl Z
+.Nm
+.Fl i Ar iface Fl R
+.Nm
+.Fl i Ar iface Fl t Cm 0 Ns - Ns Cm 4
+.Nm
+.Fl i Ar iface Fl s Cm 0 Ns - Ns Cm 3
+.Nm
+.Fl i Ar iface
+.Op Fl v Cm 1 Ns - Ns Cm 4
+.Fl a Ar AP
+.Nm
+.Fl i Ar iface Fl b Ar beacon_period
+.Nm
+.Fl i Ar iface
+.Op Fl v Cm 0 | 1
+.Fl d Cm 0 Ns - Ns Cm 3
+.Nm
+.Fl i Ar iface Fl e Cm 0 Ns - Ns Cm 4
+.Nm
+.Fl i Ar iface
+.Op Fl v Cm 0 Ns - Ns Cm 8
+.Fl k Ar key
+.Nm
+.Fl i Ar iface
+.Fl K Cm 0 Ns - Ns Cm 2
+.Nm
+.Fl i Ar iface
+.Fl W Cm 0 Ns - Ns Cm 2
+.Nm
+.Fl i Ar iface
+.Fl L Ar user_name
+.Nm
+.Fl i Ar iface Fl j Ar netjoin_timeout
+.Nm
+.Fl i Ar iface Fl l Ar station_name
+.Nm
+.Fl i Ar iface Fl m Ar mac_address
+.Nm
+.Fl i Ar iface
+.Op Fl v Cm 1 Ns - Ns Cm 3
+.Fl n Ar SSID
+.Nm
+.Fl i Ar iface Fl o Cm 0 | 1
+.Nm
+.Fl i Ar iface Fl p Ar tx_power
+.Nm
+.Fl i Ar iface Fl c Ar frequency
+.Nm
+.Fl i Ar iface Fl f Ar fragmentation_threshold
+.Nm
+.Fl i Ar iface Fl r Ar RTS_threshold
+.Nm
+.Fl i Ar iface Fl M Cm 0 Ns - Ns Cm 15
+.Nm
+.Fl h
+.Sh DESCRIPTION
+The
+.Nm
+utility controls the operation of Aironet wireless networking
+devices via the
+.Xr an 4
+driver.
+Most of the parameters that can be changed relate to the
+IEEE 802.11 protocol which the Aironet cards implement.
+This includes such things as
+the station name, whether the station is operating in ad-hoc (point
+to point) or infrastructure mode, and the network name of a service
+set to join.
+The
+.Nm
+utility can also be used to view the current NIC status, configuration
+and to dump out the values of the card's statistics counters.
+.Pp
+The
+.Ar iface
+argument given to
+.Nm
+should be the logical interface name associated with the Aironet
+device
+.Li ( an0 , an1 ,
+etc.).
+If one is not specified the device
+.Dq Li an0
+will be assumed.
+.Pp
+The
+.Nm
+utility is not designed to support the combination of arguments from different
+.Sx SYNOPSIS
+lines in a single
+.Nm
+invocation, and such combinations are not recommended.
+.Sh OPTIONS
+The options are as follows:
+.Bl -tag -width indent
+.It Fl i Ar iface Fl A
+Display the preferred access point list.
+The AP list can be used by
+stations to specify the MAC address of access points with which it
+wishes to associate.
+If no AP list is specified (the default) then
+the station will associate with the first access point that it finds
+which serves the SSID(s) specified in the SSID list.
+The AP list can
+be modified with the
+.Fl a
+option.
+.It Fl i Ar iface Fl N
+Display the SSID list.
+This is a list of service set IDs (i.e., network names)
+with which the station wishes to associate.
+There may be up to three SSIDs
+in the list: the station will go through the list in ascending order and
+associate with the first matching SSID that it finds.
+.It Fl i Ar iface Fl S
+Display NIC status information.
+This includes the current operating
+status, current BSSID, SSID, channel, beacon period and currently
+associated access point.
+The operating mode indicates the state of
+the NIC, MAC status and receiver status.
+When the
+.Qq Li synced
+keyword
+appears, it means the NIC has successfully associated with an access
+point, associated with an ad-hoc
+.Dq master
+station, or become a
+.Dq master
+itself.
+The beacon period can be anything between 20 and 976 milliseconds.
+The default is 100.
+.It Fl i Ar iface Fl I
+Display NIC capability information.
+This shows the device type,
+frequency, speed and power level capabilities and firmware revision levels.
+.It Fl i Ar iface Fl T
+Display the NIC's internal statistics counters.
+.It Fl i Ar iface Fl C
+Display current NIC configuration.
+This shows the current operation mode,
+receive mode, MAC address, power save settings, various timing settings,
+channel selection, diversity, transmit power and transmit speed.
+.It Fl i Ar iface Fl Q
+Display the cached signal strength information maintained by the
+.Xr an 4
+driver.
+The driver retains information about signal strength and
+noise level for packets received from different hosts.
+The signal strength and noise level values are displayed in units of dBms by
+default.
+The
+.Va hw.an.an_cache_mode
+.Xr sysctl 8
+variable can be set to
+.Cm raw , dbm
+or
+.Cm per .
+.It Fl i Ar iface Fl Z
+Clear the signal strength cache maintained internally by the
+.Xr an 4
+driver.
+.It Fl i Ar iface Fl R
+Display RSSI map that converts from the RSSI index to percent and dBm.
+.It Fl i Ar iface Fl t Cm 0 Ns - Ns Cm 4
+Select transmit speed.
+The available settings are as follows:
+.Bl -column ".Em TX rate" -offset indent
+.Em "TX rate NIC speed"
+.It Cm 0 Ta "Auto -- NIC selects optimal speed"
+.It Cm 1 Ta "1Mbps fixed"
+.It Cm 2 Ta "2Mbps fixed"
+.It Cm 3 Ta "5.5Mbps fixed"
+.It Cm 4 Ta "11Mbps fixed"
+.El
+.Pp
+Note that the 5.5 and 11Mbps settings are only supported on the 4800
+series adapters: the 4500 series adapters have a maximum speed of 2Mbps.
+.It Fl i Ar iface Fl s Cm 0 Ns - Ns Cm 3
+Set power save mode.
+Valid selections are as follows:
+.Bl -column ".Em Selection" -offset indent
+.Em "Selection Power save mode"
+.It Cm 0 Ta "None - power save disabled"
+.It Cm 1 Ta "Constantly awake mode (CAM)"
+.It Cm 2 Ta "Power Save Polling (PSP)"
+.It Cm 3 Ta "Fast Power Save Polling (PSP-CAM)"
+.El
+.Pp
+Note that for IBSS (ad-hoc) mode, only PSP mode is supported, and only
+if the ATIM window is non-zero.
+.It Fl i Ar iface Oo Fl v Cm 1 Ns - Ns Cm 4 Oc Fl a Ar AP
+Set preferred access point.
+The
+.Ar AP
+is specified as a MAC address consisting of 6 hexadecimal values
+separated by colons.
+By default, the
+.Fl a
+option only sets the first entry in the AP list.
+The
+.Fl v
+modifier can be used to specify exactly which AP list entry is to be
+modified.
+If the
+.Fl v
+flag is not used, the first AP list entry will be changed.
+.It Fl i Ar iface Fl b Ar beacon_period
+Set the ad-hoc mode beacon period.
+The
+.Ar beacon_period
+is specified in milliseconds.
+The default is 100ms.
+.It Fl i Ar iface Oo Fl v Cm 0 | 1 Oc Fl d Cm 0 Ns - Ns Cm 3
+Select the antenna diversity.
+Aironet devices can be configured with up
+to two antennas, and transmit and receive diversity can be configured
+accordingly.
+Valid selections are as follows:
+.Bl -column ".Em Selection" -offset indent
+.Em "Selection Diversity"
+.It Cm 0 Ta "Select factory default diversity"
+.It Cm 1 Ta "Antenna 1 only"
+.It Cm 2 Ta "Antenna 2 only"
+.It Cm 3 Ta "Antenna 1 and 2"
+.El
+.Pp
+The receive and transmit diversity can be set independently.
+The user
+must specify which diversity setting is to be modified by using the
+.Fl v
+option: selection
+.Cm 0
+sets the receive diversity and
+.Cm 1
+sets the transmit diversity.
+.It Fl i Ar iface Fl e Cm 0 Ns - Ns Cm 4
+Set the transmit WEP key to use.
+Note that until this command is issued, the device will use the
+last key programmed.
+The transmit key is stored in NVRAM.
+Currently
+set transmit key can be checked via
+.Fl C
+option.
+Selection
+.Cm 4
+sets the card in
+.Dq "Home Network Mode"
+and uses the home key.
+.It Fl i Ar iface Oo Fl v Cm 0 Ns - Ns Cm 8 Oc Fl k Ar key
+Set a WEP key.
+For 40 bit prefix 10 hex character with 0x.
+For 128 bit prefix 26 hex character with 0x.
+Use
+.Qq
+as the key to erase the key.
+Supports 4 keys; even numbers are for permanent keys
+and odd number are for temporary keys.
+For example,
+.Fl v Cm 1
+sets the first temporary key.
+(A
+.Dq permanent
+key is stored in NVRAM; a
+.Dq temporary
+key is not.)
+Note that the device will use the most recently-programmed key by default.
+Currently set keys can be checked via
+.Fl C
+option, only the sizes of the
+keys are returned.
+The value of
+.Cm 8
+is for the home key.
+Note that the value for the home key can be read back from firmware.
+.It Fl i Ar iface Fl K Cm 0 Ns - Ns Cm 2
+Set authorization type.
+Use
+.Cm 0
+for none,
+.Cm 1
+for
+.Dq Open ,
+.Cm 2
+for
+.Dq "Shared Key" .
+.It Fl i Ar iface Fl W Cm 0 Ns - Ns Cm 2
+Enable WEP.
+Use
+.Cm 0
+for no WEP,
+.Cm 1
+to enable full WEP,
+.Cm 2
+for mixed cell.
+.It Fl i Ar iface Fl L Ar user_name
+Enable LEAP and query for password.
+It will check to see if it has authenticated for up to 60s.
+To disable LEAP, set WEP mode.
+.It Fl i Ar iface Fl j Ar netjoin_timeout
+Set the ad-hoc network join timeout.
+When a station is first activated
+in ad-hoc mode, it will search out a
+.Dq master
+station with the desired
+SSID and associate with it.
+If the station is unable to locate another
+station with the same SSID after a suitable timeout, it sets itself up
+as the
+.Dq master
+so that other stations may associate with it.
+This
+timeout defaults to 10000 milliseconds (10 seconds) but may be changed
+with this option.
+The timeout should be specified in milliseconds.
+.It Fl i Ar iface Fl l Ar station_name
+Set the station name used internally by the NIC.
+The
+.Ar station_name
+can be any text string up to 16 characters in length.
+The default name
+is set by the driver to
+.Dq Li FreeBSD .
+.It Fl i Ar iface Fl m Ar mac_address
+Set the station address for the specified interface.
+The
+.Ar mac_address
+is specified as a series of six hexadecimal values separated by colons,
+e.g.:
+.Li 00:60:1d:12:34:56 .
+This programs the new address into the card
+and updates the interface as well.
+.It Fl i Ar iface Oo Fl v Cm 1 Ns - Ns Cm 3 Oc Fl n Ar SSID
+Set the desired SSID (network name).
+There are three SSIDs which allows
+the NIC to work with access points at several locations without needing
+to be reconfigured.
+The NIC checks each SSID in sequence when searching
+for a match.
+The SSID to be changed can be specified with the
+.Fl v
+modifier option.
+If the
+.Fl v
+flag is not used, the first SSID in the list is set.
+.It Fl i Ar iface Fl o Cm 0 | 1
+Set the operating mode of the Aironet interface.
+Valid selections are
+.Cm 0
+for ad-hoc mode and
+.Cm 1
+for infrastructure mode.
+The default driver setting is for infrastructure
+mode.
+.It Fl i Ar iface Fl p Ar tx_power
+Set the transmit power level in milliwatts.
+Valid power settings
+vary depending on the actual NIC and can be viewed by dumping the
+device capabilities with the
+.Fl I
+flag.
+Typical values are 1, 5, 20, 50 and 100mW.
+Selecting 0 sets
+the factory default.
+.It Fl i Ar iface Fl c Ar frequency
+Set the radio frequency of a given interface.
+The
+.Ar frequency
+should be specified as a channel ID as shown in the table below.
+The
+list of available frequencies is dependent on radio regulations specified
+by regional authorities.
+Recognized regulatory authorities include
+the FCC (United States), ETSI (Europe), France and Japan.
+Frequencies
+in the table are specified in MHz.
+.Bl -column ".Em Channel ID" ".Em FCC" ".Em ETSI" ".Em France" ".Em Japan" -offset indent
+.Em "Channel ID FCC ETSI France Japan"
+.It Cm 1 Ta 2412 Ta 2412 Ta - Ta -
+.It Cm 2 Ta 2417 Ta 2417 Ta - Ta -
+.It Cm 3 Ta 2422 Ta 2422 Ta - Ta -
+.It Cm 4 Ta 2427 Ta 2427 Ta - Ta -
+.It Cm 5 Ta 2432 Ta 2432 Ta - Ta -
+.It Cm 6 Ta 2437 Ta 2437 Ta - Ta -
+.It Cm 7 Ta 2442 Ta 2442 Ta - Ta -
+.It Cm 8 Ta 2447 Ta 2447 Ta - Ta -
+.It Cm 9 Ta 2452 Ta 2452 Ta - Ta -
+.It Cm 10 Ta 2457 Ta 2457 Ta 2457 Ta -
+.It Cm 11 Ta 2462 Ta 2462 Ta 2462 Ta -
+.It Cm 12 Ta - Ta 2467 Ta 2467 Ta -
+.It Cm 13 Ta - Ta 2472 Ta 2472 Ta -
+.It Cm 14 Ta - Ta - Ta - Ta 2484
+.El
+.Pp
+If an illegal channel is specified, the
+NIC will revert to its default channel.
+For NICs sold in the United States
+and Europe, the default channel is 3.
+For NICs sold in France, the default
+channel is 11.
+For NICs sold in Japan, the only available channel is 14.
+Note that two stations must be set to the same channel in order to
+communicate.
+.It Fl i Ar iface Fl f Ar fragmentation_threshold
+Set the fragmentation threshold in bytes.
+This threshold controls the
+point at which outgoing packets will be split into multiple fragments.
+If a single fragment is not sent successfully, only that fragment will
+need to be retransmitted instead of the whole packet.
+The fragmentation
+threshold can be anything from 64 to 2312 bytes.
+The default is 2312.
+.It Fl i Ar iface Fl r Ar RTS_threshold
+Set the RTS/CTS threshold for a given interface.
+This controls the
+number of bytes used for the RTS/CTS handshake boundary.
+The
+.Ar RTS_threshold
+can be any value between 0 and 2312.
+The default is 2312.
+.It Fl i Ar iface Fl M Cm 0 Ns - Ns Cm 15
+Set monitor mode via bit mask, meaning:
+.Pp
+.Bl -tag -width indent -offset indent -compact
+.It Em Bit
+.Em Meaning
+.It 0
+to not dump 802.11 packet.
+.It 1
+to enable 802.11 monitor.
+.It 2
+to monitor any SSID.
+.It 4
+to not skip beacons, monitor beacons produces a high system load.
+.It 8
+to enable full Aironet header returned via BPF.
+Note it appears that a SSID must be set.
+.El
+.It Fl h
+Print a list of available options and sample usage.
+.El
+.Sh SECURITY NOTES
+WEP
+.Pq Dq "wired equivalent privacy"
+is based on the RC4 algorithm,
+using a 24 bit initialization vector.
+.Pp
+RC4 is supposedly vulnerable to certain known plaintext attacks,
+especially with 40 bit keys.
+So the security of WEP in part depends on how much known plaintext
+is transmitted.
+.Pp
+Because of this, although counter-intuitive, using
+.Dq "shared key"
+authentication (which involves sending known plaintext) is less
+secure than using
+.Dq open
+authentication when WEP is enabled.
+.Pp
+Devices may alternate among all of the configured WEP keys when
+transmitting packets.
+Therefore, all configured keys (up to four) must agree.
+.Sh EXAMPLES
+.Bd -literal -offset indent
+ancontrol -i an0 -v 0 -k 0x12345678901234567890123456
+ancontrol -i an0 -K 2
+ancontrol -i an0 -W 1
+ancontrol -i an0 -e 0
+.Ed
+.Pp
+Sets a WEP key 0, enables
+.Dq "Shared Key"
+authentication, enables full WEP
+and uses transmit key 0.
+.Sh SEE ALSO
+.Xr an 4 ,
+.Xr ifconfig 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 4.0 .
+.Sh AUTHORS
+The
+.Nm
+utility was written by
+.An Bill Paul Aq wpaul@ee.columbia.edu .
+.Sh BUGS
+The statistics counters do not seem to show the amount of transmit
+and received frames as increasing.
+This is likely due to the fact that
+the
+.Xr an 4
+driver uses unmodified packet mode instead of letting the NIC perform
+802.11/ethernet encapsulation itself.
+.Pp
+Setting the channel does not seem to have any effect.
diff --git a/usr.sbin/ancontrol/ancontrol.c b/usr.sbin/ancontrol/ancontrol.c
new file mode 100644
index 0000000..08d13d3
--- /dev/null
+++ b/usr.sbin/ancontrol/ancontrol.c
@@ -0,0 +1,1781 @@
+/*
+ * Copyright 1997, 1998, 1999
+ * Bill Paul <wpaul@ee.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.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] = "@(#) Copyright (c) 1997, 1998, 1999\
+ Bill Paul. All rights reserved.";
+#endif
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+
+#include <arpa/inet.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/ethernet.h>
+
+#include <dev/an/if_aironet_ieee.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <err.h>
+#include <md4.h>
+#include <ctype.h>
+
+static int an_getval(const char *, struct an_req *);
+static void an_setval(const char *, struct an_req *);
+static void an_printwords(const u_int16_t *, int);
+static void an_printspeeds(const u_int8_t *, int);
+static void an_printbool(int);
+static void an_printhex(const char *, int);
+static void an_printstr(char *, int);
+static void an_dumpstatus(const char *);
+static void an_dumpstats(const char *);
+static void an_dumpconfig(const char *);
+static void an_dumpcaps(const char *);
+static void an_dumpssid(const char *);
+static void an_dumpap(const char *);
+static void an_setconfig(const char *, int, void *);
+static void an_setssid(const char *, int, void *);
+static void an_setap(const char *, int, void *);
+static void an_setspeed(const char *, int, void *);
+static void an_readkeyinfo(const char *);
+#ifdef ANCACHE
+static void an_zerocache(const char *);
+static void an_readcache(const char *);
+#endif
+static int an_hex2int(char);
+static void an_str2key(const char *, struct an_ltv_key *);
+static void an_setkeys(const char *, const char *, int);
+static void an_enable_tx_key(const char *, const char *);
+static void an_enable_leap_mode(const char *, const char *);
+static void an_dumprssimap(const char *);
+static void usage(const char *);
+
+#define ACT_DUMPSTATS 1
+#define ACT_DUMPCONFIG 2
+#define ACT_DUMPSTATUS 3
+#define ACT_DUMPCAPS 4
+#define ACT_DUMPSSID 5
+#define ACT_DUMPAP 6
+
+#define ACT_SET_OPMODE 7
+#define ACT_SET_SSID 8
+#define ACT_SET_FREQ 11
+#define ACT_SET_AP1 12
+#define ACT_SET_AP2 13
+#define ACT_SET_AP3 14
+#define ACT_SET_AP4 15
+#define ACT_SET_DRIVERNAME 16
+#define ACT_SET_SCANMODE 17
+#define ACT_SET_TXRATE 18
+#define ACT_SET_RTS_THRESH 19
+#define ACT_SET_PWRSAVE 20
+#define ACT_SET_DIVERSITY_RX 21
+#define ACT_SET_DIVERSITY_TX 22
+#define ACT_SET_RTS_RETRYLIM 23
+#define ACT_SET_WAKE_DURATION 24
+#define ACT_SET_BEACON_PERIOD 25
+#define ACT_SET_TXPWR 26
+#define ACT_SET_FRAG_THRESH 27
+#define ACT_SET_NETJOIN 28
+#define ACT_SET_MYNAME 29
+#define ACT_SET_MAC 30
+
+#define ACT_DUMPCACHE 31
+#define ACT_ZEROCACHE 32
+
+#define ACT_ENABLE_WEP 33
+#define ACT_SET_KEY_TYPE 34
+#define ACT_SET_KEYS 35
+#define ACT_ENABLE_TX_KEY 36
+#define ACT_SET_MONITOR_MODE 37
+#define ACT_SET_LEAP_MODE 38
+
+#define ACT_DUMPRSSIMAP 39
+
+static int
+an_getval(const char *iface, struct an_req *areq)
+{
+ struct ifreq ifr;
+ int s, okay = 1;
+
+ bzero(&ifr, sizeof(ifr));
+
+ strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (caddr_t)areq;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (s == -1)
+ err(1, "socket");
+
+ if (ioctl(s, SIOCGAIRONET, &ifr) == -1) {
+ okay = 0;
+ err(1, "SIOCGAIRONET");
+ }
+
+ close(s);
+
+ return (okay);
+}
+
+static void
+an_setval(const char *iface, struct an_req *areq)
+{
+ struct ifreq ifr;
+ int s;
+
+ bzero(&ifr, sizeof(ifr));
+
+ strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (caddr_t)areq;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (s == -1)
+ err(1, "socket");
+
+ if (ioctl(s, SIOCSAIRONET, &ifr) == -1)
+ err(1, "SIOCSAIRONET");
+
+ close(s);
+
+ return;
+}
+
+static void
+an_printstr(char *str, int len)
+{
+ int i;
+
+ for (i = 0; i < len - 1; i++) {
+ if (str[i] == '\0')
+ str[i] = ' ';
+ }
+
+ printf("[ %.*s ]", len, str);
+}
+
+static void
+an_printwords(const u_int16_t *w, int len)
+{
+ int i;
+
+ printf("[ ");
+ for (i = 0; i < len; i++)
+ printf("%u ", w[i]);
+ printf("]");
+}
+
+static void
+an_printspeeds(const u_int8_t *w, int len)
+{
+ int i;
+
+ printf("[ ");
+ for (i = 0; i < len && w[i]; i++)
+ printf("%2.1fMbps ", w[i] * 0.500);
+ printf("]");
+}
+
+static void
+an_printbool(int val)
+{
+ if (val)
+ printf("[ On ]");
+ else
+ printf("[ Off ]");
+}
+
+static void
+an_printhex(const char *ptr, int len)
+{
+ int i;
+
+ printf("[ ");
+ for (i = 0; i < len; i++) {
+ printf("%02x", ptr[i] & 0xFF);
+ if (i < (len - 1))
+ printf(":");
+ }
+
+ printf(" ]");
+}
+
+
+
+static void
+an_dumpstatus(const char *iface)
+{
+ struct an_ltv_status *sts;
+ struct an_req areq;
+ struct an_ltv_rssi_map an_rssimap;
+ int rssimap_valid = 0;
+
+ /*
+ * Try to get RSSI to percent and dBM table
+ */
+
+ an_rssimap.an_len = sizeof(an_rssimap);
+ an_rssimap.an_type = AN_RID_RSSI_MAP;
+ rssimap_valid = an_getval(iface, (struct an_req*)&an_rssimap);
+
+ if (rssimap_valid)
+ printf("RSSI table:\t\t[ present ]\n");
+ else
+ printf("RSSI table:\t\t[ not available ]\n");
+
+ areq.an_len = sizeof(areq);
+ areq.an_type = AN_RID_STATUS;
+
+ an_getval(iface, &areq);
+
+ sts = (struct an_ltv_status *)&areq;
+
+ printf("MAC address:\t\t");
+ an_printhex((char *)&sts->an_macaddr, ETHER_ADDR_LEN);
+ printf("\nOperating mode:\t\t[ ");
+ if (sts->an_opmode & AN_STATUS_OPMODE_CONFIGURED)
+ printf("configured ");
+ if (sts->an_opmode & AN_STATUS_OPMODE_MAC_ENABLED)
+ printf("MAC ON ");
+ if (sts->an_opmode & AN_STATUS_OPMODE_RX_ENABLED)
+ printf("RX ON ");
+ if (sts->an_opmode & AN_STATUS_OPMODE_IN_SYNC)
+ printf("synced ");
+ if (sts->an_opmode & AN_STATUS_OPMODE_ASSOCIATED)
+ printf("associated ");
+ if (sts->an_opmode & AN_STATUS_OPMODE_LEAP)
+ printf("LEAP ");
+ if (sts->an_opmode & AN_STATUS_OPMODE_ERROR)
+ printf("error ");
+ printf("]\n");
+ printf("Error code:\t\t");
+ an_printhex((char *)&sts->an_errcode, 1);
+ if (rssimap_valid)
+ printf("\nSignal strength:\t[ %u%% ]",
+ an_rssimap.an_entries[
+ sts->an_normalized_strength].an_rss_pct);
+ else
+ printf("\nSignal strength:\t[ %u%% ]",
+ sts->an_normalized_strength);
+ printf("\nAverage Noise:\t\t[ %u%% ]", sts->an_avg_noise_prev_min_pc);
+ if (rssimap_valid)
+ printf("\nSignal quality:\t\t[ %u%% ]",
+ an_rssimap.an_entries[
+ sts->an_cur_signal_quality].an_rss_pct);
+ else
+ printf("\nSignal quality:\t\t[ %u ]",
+ sts->an_cur_signal_quality);
+ printf("\nMax Noise:\t\t[ %u%% ]", sts->an_max_noise_prev_min_pc);
+ /*
+ * XXX: This uses the old definition of the rate field (units of
+ * 500kbps). Technically the new definition is that this field
+ * contains arbitrary values, but no devices which need this
+ * support exist and the IEEE seems to intend to use the old
+ * definition until they get something big so we'll keep using
+ * it as well because this will work with new cards with
+ * rate <= 63.5Mbps.
+ */
+ printf("\nCurrent TX rate:\t[ %u%s ]", sts->an_current_tx_rate / 2,
+ (sts->an_current_tx_rate % 2) ? ".5" : "");
+ printf("\nCurrent SSID:\t\t");
+ an_printstr((char *)&sts->an_ssid, sts->an_ssidlen);
+ printf("\nCurrent AP name:\t");
+ an_printstr((char *)&sts->an_ap_name, 16);
+ printf("\nCurrent BSSID:\t\t");
+ an_printhex((char *)&sts->an_cur_bssid, ETHER_ADDR_LEN);
+ printf("\nBeacon period:\t\t");
+ an_printwords(&sts->an_beacon_period, 1);
+ printf("\nDTIM period:\t\t");
+ an_printwords(&sts->an_dtim_period, 1);
+ printf("\nATIM duration:\t\t");
+ an_printwords(&sts->an_atim_duration, 1);
+ printf("\nHOP period:\t\t");
+ an_printwords(&sts->an_hop_period, 1);
+ printf("\nChannel set:\t\t");
+ an_printwords(&sts->an_channel_set, 1);
+ printf("\nCurrent channel:\t");
+ an_printwords(&sts->an_cur_channel, 1);
+ printf("\nHops to backbone:\t");
+ an_printwords(&sts->an_hops_to_backbone, 1);
+ printf("\nTotal AP load:\t\t");
+ an_printwords(&sts->an_ap_total_load, 1);
+ printf("\nOur generated load:\t");
+ an_printwords(&sts->an_our_generated_load, 1);
+ printf("\nAccumulated ARL:\t");
+ an_printwords(&sts->an_accumulated_arl, 1);
+ printf("\n");
+ return;
+}
+
+static void
+an_dumpcaps(const char *iface)
+{
+ struct an_ltv_caps *caps;
+ struct an_req areq;
+ u_int16_t tmp;
+
+ areq.an_len = sizeof(areq);
+ areq.an_type = AN_RID_CAPABILITIES;
+
+ an_getval(iface, &areq);
+
+ caps = (struct an_ltv_caps *)&areq;
+
+ printf("OUI:\t\t\t");
+ an_printhex((char *)&caps->an_oui, 3);
+ printf("\nProduct number:\t\t");
+ an_printwords(&caps->an_prodnum, 1);
+ printf("\nManufacturer name:\t");
+ an_printstr((char *)&caps->an_manufname, 32);
+ printf("\nProduce name:\t\t");
+ an_printstr((char *)&caps->an_prodname, 16);
+ printf("\nFirmware version:\t");
+ an_printstr((char *)&caps->an_prodvers, 1);
+ printf("\nOEM MAC address:\t");
+ an_printhex((char *)&caps->an_oemaddr, ETHER_ADDR_LEN);
+ printf("\nAironet MAC address:\t");
+ an_printhex((char *)&caps->an_aironetaddr, ETHER_ADDR_LEN);
+ printf("\nRadio type:\t\t[ ");
+ if (caps->an_radiotype & AN_RADIOTYPE_80211_FH)
+ printf("802.11 FH");
+ else if (caps->an_radiotype & AN_RADIOTYPE_80211_DS)
+ printf("802.11 DS");
+ else if (caps->an_radiotype & AN_RADIOTYPE_LM2000_DS)
+ printf("LM2000 DS");
+ else
+ printf("unknown (%x)", caps->an_radiotype);
+ printf(" ]");
+ printf("\nRegulatory domain:\t");
+ an_printwords(&caps->an_regdomain, 1);
+ printf("\nAssigned CallID:\t");
+ an_printhex((char *)&caps->an_callid, 6);
+ printf("\nSupported speeds:\t");
+ an_printspeeds(caps->an_rates, 8);
+ printf("\nRX Diversity:\t\t[ ");
+ if (caps->an_rx_diversity == AN_DIVERSITY_FACTORY_DEFAULT)
+ printf("factory default");
+ else if (caps->an_rx_diversity == AN_DIVERSITY_ANTENNA_1_ONLY)
+ printf("antenna 1 only");
+ else if (caps->an_rx_diversity == AN_DIVERSITY_ANTENNA_2_ONLY)
+ printf("antenna 2 only");
+ else if (caps->an_rx_diversity == AN_DIVERSITY_ANTENNA_1_AND_2)
+ printf("antenna 1 and 2");
+ printf(" ]");
+ printf("\nTX Diversity:\t\t[ ");
+ if (caps->an_tx_diversity == AN_DIVERSITY_FACTORY_DEFAULT)
+ printf("factory default");
+ else if (caps->an_tx_diversity == AN_DIVERSITY_ANTENNA_1_ONLY)
+ printf("antenna 1 only");
+ else if (caps->an_tx_diversity == AN_DIVERSITY_ANTENNA_2_ONLY)
+ printf("antenna 2 only");
+ else if (caps->an_tx_diversity == AN_DIVERSITY_ANTENNA_1_AND_2)
+ printf("antenna 1 and 2");
+ printf(" ]");
+ printf("\nSupported power levels:\t");
+ an_printwords(caps->an_tx_powerlevels, 8);
+ printf("\nHardware revision:\t");
+ tmp = ntohs(caps->an_hwrev);
+ an_printhex((char *)&tmp, 2);
+ printf("\nSoftware revision:\t");
+ tmp = ntohs(caps->an_fwrev);
+ an_printhex((char *)&tmp, 2);
+ printf("\nSoftware subrevision:\t");
+ tmp = ntohs(caps->an_fwsubrev);
+ an_printhex((char *)&tmp, 2);
+ printf("\nInterface revision:\t");
+ tmp = ntohs(caps->an_ifacerev);
+ an_printhex((char *)&tmp, 2);
+ printf("\nBootblock revision:\t");
+ tmp = ntohs(caps->an_bootblockrev);
+ an_printhex((char *)&tmp, 2);
+ printf("\n");
+ return;
+}
+
+static void
+an_dumpstats(const char *iface)
+{
+ struct an_ltv_stats *stats;
+ struct an_req areq;
+
+ areq.an_len = sizeof(areq);
+ areq.an_type = AN_RID_32BITS_CUM;
+
+ an_getval(iface, &areq);
+
+ stats = (struct an_ltv_stats *)((uint16_t *)&areq - 1);
+
+ printf("RX overruns:\t\t\t\t\t[ %u ]\n", stats->an_rx_overruns);
+ printf("RX PLCP CSUM errors:\t\t\t\t[ %u ]\n",
+ stats->an_rx_plcp_csum_errs);
+ printf("RX PLCP format errors:\t\t\t\t[ %u ]\n",
+ stats->an_rx_plcp_format_errs);
+ printf("RX PLCP length errors:\t\t\t\t[ %u ]\n",
+ stats->an_rx_plcp_len_errs);
+ printf("RX MAC CRC errors:\t\t\t\t[ %u ]\n",
+ stats->an_rx_mac_crc_errs);
+ printf("RX MAC CRC OK:\t\t\t\t\t[ %u ]\n",
+ stats->an_rx_mac_crc_ok);
+ printf("RX WEP errors:\t\t\t\t\t[ %u ]\n",
+ stats->an_rx_wep_errs);
+ printf("RX WEP OK:\t\t\t\t\t[ %u ]\n",
+ stats->an_rx_wep_ok);
+ printf("Long retries:\t\t\t\t\t[ %u ]\n",
+ stats->an_retry_long);
+ printf("Short retries:\t\t\t\t\t[ %u ]\n",
+ stats->an_retry_short);
+ printf("Retries exhausted:\t\t\t\t[ %u ]\n",
+ stats->an_retry_max);
+ printf("Bad ACK:\t\t\t\t\t[ %u ]\n",
+ stats->an_no_ack);
+ printf("Bad CTS:\t\t\t\t\t[ %u ]\n",
+ stats->an_no_cts);
+ printf("RX good ACKs:\t\t\t\t\t[ %u ]\n",
+ stats->an_rx_ack_ok);
+ printf("RX good CTSs:\t\t\t\t\t[ %u ]\n",
+ stats->an_rx_cts_ok);
+ printf("TX good ACKs:\t\t\t\t\t[ %u ]\n",
+ stats->an_tx_ack_ok);
+ printf("TX good RTSs:\t\t\t\t\t[ %u ]\n",
+ stats->an_tx_rts_ok);
+ printf("TX good CTSs:\t\t\t\t\t[ %u ]\n",
+ stats->an_tx_cts_ok);
+ printf("LMAC multicasts transmitted:\t\t\t[ %u ]\n",
+ stats->an_tx_lmac_mcasts);
+ printf("LMAC broadcasts transmitted:\t\t\t[ %u ]\n",
+ stats->an_tx_lmac_bcasts);
+ printf("LMAC unicast frags transmitted:\t\t\t[ %u ]\n",
+ stats->an_tx_lmac_ucast_frags);
+ printf("LMAC unicasts transmitted:\t\t\t[ %u ]\n",
+ stats->an_tx_lmac_ucasts);
+ printf("Beacons transmitted:\t\t\t\t[ %u ]\n",
+ stats->an_tx_beacons);
+ printf("Beacons received:\t\t\t\t[ %u ]\n",
+ stats->an_rx_beacons);
+ printf("Single transmit collisions:\t\t\t[ %u ]\n",
+ stats->an_tx_single_cols);
+ printf("Multiple transmit collisions:\t\t\t[ %u ]\n",
+ stats->an_tx_multi_cols);
+ printf("Transmits without deferrals:\t\t\t[ %u ]\n",
+ stats->an_tx_defers_no);
+ printf("Transmits deferred due to protocol:\t\t[ %u ]\n",
+ stats->an_tx_defers_prot);
+ printf("Transmits deferred due to energy detect:\t\t[ %u ]\n",
+ stats->an_tx_defers_energy);
+ printf("RX duplicate frames/frags:\t\t\t[ %u ]\n",
+ stats->an_rx_dups);
+ printf("RX partial frames:\t\t\t\t[ %u ]\n",
+ stats->an_rx_partial);
+ printf("TX max lifetime exceeded:\t\t\t[ %u ]\n",
+ stats->an_tx_too_old);
+ printf("RX max lifetime exceeded:\t\t\t[ %u ]\n",
+ stats->an_tx_too_old);
+ printf("Sync lost due to too many missed beacons:\t[ %u ]\n",
+ stats->an_lostsync_missed_beacons);
+ printf("Sync lost due to ARL exceeded:\t\t\t[ %u ]\n",
+ stats->an_lostsync_arl_exceeded);
+ printf("Sync lost due to deauthentication:\t\t[ %u ]\n",
+ stats->an_lostsync_deauthed);
+ printf("Sync lost due to disassociation:\t\t[ %u ]\n",
+ stats->an_lostsync_disassociated);
+ printf("Sync lost due to excess change in TSF timing:\t[ %u ]\n",
+ stats->an_lostsync_tsf_timing);
+ printf("Host transmitted multicasts:\t\t\t[ %u ]\n",
+ stats->an_tx_host_mcasts);
+ printf("Host transmitted broadcasts:\t\t\t[ %u ]\n",
+ stats->an_tx_host_bcasts);
+ printf("Host transmitted unicasts:\t\t\t[ %u ]\n",
+ stats->an_tx_host_ucasts);
+ printf("Host transmission failures:\t\t\t[ %u ]\n",
+ stats->an_tx_host_failed);
+ printf("Host received multicasts:\t\t\t[ %u ]\n",
+ stats->an_rx_host_mcasts);
+ printf("Host received broadcasts:\t\t\t[ %u ]\n",
+ stats->an_rx_host_bcasts);
+ printf("Host received unicasts:\t\t\t\t[ %u ]\n",
+ stats->an_rx_host_ucasts);
+ printf("Host receive discards:\t\t\t\t[ %u ]\n",
+ stats->an_rx_host_discarded);
+ printf("HMAC transmitted multicasts:\t\t\t[ %u ]\n",
+ stats->an_tx_hmac_mcasts);
+ printf("HMAC transmitted broadcasts:\t\t\t[ %u ]\n",
+ stats->an_tx_hmac_bcasts);
+ printf("HMAC transmitted unicasts:\t\t\t[ %u ]\n",
+ stats->an_tx_hmac_ucasts);
+ printf("HMAC transmissions failed:\t\t\t[ %u ]\n",
+ stats->an_tx_hmac_failed);
+ printf("HMAC received multicasts:\t\t\t[ %u ]\n",
+ stats->an_rx_hmac_mcasts);
+ printf("HMAC received broadcasts:\t\t\t[ %u ]\n",
+ stats->an_rx_hmac_bcasts);
+ printf("HMAC received unicasts:\t\t\t\t[ %u ]\n",
+ stats->an_rx_hmac_ucasts);
+ printf("HMAC receive discards:\t\t\t\t[ %u ]\n",
+ stats->an_rx_hmac_discarded);
+ printf("HMAC transmits accepted:\t\t\t[ %u ]\n",
+ stats->an_tx_hmac_accepted);
+ printf("SSID mismatches:\t\t\t\t[ %u ]\n",
+ stats->an_ssid_mismatches);
+ printf("Access point mismatches:\t\t\t[ %u ]\n",
+ stats->an_ap_mismatches);
+ printf("Speed mismatches:\t\t\t\t[ %u ]\n",
+ stats->an_rates_mismatches);
+ printf("Authentication rejects:\t\t\t\t[ %u ]\n",
+ stats->an_auth_rejects);
+ printf("Authentication timeouts:\t\t\t[ %u ]\n",
+ stats->an_auth_timeouts);
+ printf("Association rejects:\t\t\t\t[ %u ]\n",
+ stats->an_assoc_rejects);
+ printf("Association timeouts:\t\t\t\t[ %u ]\n",
+ stats->an_assoc_timeouts);
+ printf("Management frames received:\t\t\t[ %u ]\n",
+ stats->an_rx_mgmt_pkts);
+ printf("Management frames transmitted:\t\t\t[ %u ]\n",
+ stats->an_tx_mgmt_pkts);
+ printf("Refresh frames received:\t\t\t[ %u ]\n",
+ stats->an_rx_refresh_pkts),
+ printf("Refresh frames transmitted:\t\t\t[ %u ]\n",
+ stats->an_tx_refresh_pkts),
+ printf("Poll frames received:\t\t\t\t[ %u ]\n",
+ stats->an_rx_poll_pkts);
+ printf("Poll frames transmitted:\t\t\t[ %u ]\n",
+ stats->an_tx_poll_pkts);
+ printf("Host requested sync losses:\t\t\t[ %u ]\n",
+ stats->an_lostsync_hostreq);
+ printf("Host transmitted bytes:\t\t\t\t[ %u ]\n",
+ stats->an_host_tx_bytes);
+ printf("Host received bytes:\t\t\t\t[ %u ]\n",
+ stats->an_host_rx_bytes);
+ printf("Uptime in microseconds:\t\t\t\t[ %u ]\n",
+ stats->an_uptime_usecs);
+ printf("Uptime in seconds:\t\t\t\t[ %u ]\n",
+ stats->an_uptime_secs);
+ printf("Sync lost due to better AP:\t\t\t[ %u ]\n",
+ stats->an_lostsync_better_ap);
+}
+
+static void
+an_dumpap(const char *iface)
+{
+ struct an_ltv_aplist *ap;
+ struct an_req areq;
+
+ areq.an_len = sizeof(areq);
+ areq.an_type = AN_RID_APLIST;
+
+ an_getval(iface, &areq);
+
+ ap = (struct an_ltv_aplist *)&areq;
+ printf("Access point 1:\t\t\t");
+ an_printhex((char *)&ap->an_ap1, ETHER_ADDR_LEN);
+ printf("\nAccess point 2:\t\t\t");
+ an_printhex((char *)&ap->an_ap2, ETHER_ADDR_LEN);
+ printf("\nAccess point 3:\t\t\t");
+ an_printhex((char *)&ap->an_ap3, ETHER_ADDR_LEN);
+ printf("\nAccess point 4:\t\t\t");
+ an_printhex((char *)&ap->an_ap4, ETHER_ADDR_LEN);
+ printf("\n");
+
+ return;
+}
+
+static void
+an_dumpssid(const char *iface)
+{
+ struct an_ltv_ssidlist_new *ssid;
+ struct an_req areq;
+ int i, max;
+
+ areq.an_len = sizeof(areq);
+ areq.an_type = AN_RID_SSIDLIST;
+
+ an_getval(iface, &areq);
+
+ max = (areq.an_len - 4) / sizeof(struct an_ltv_ssid_entry);
+ if ( max > MAX_SSIDS ) {
+ printf("Too many SSIDs only printing %d of %d\n",
+ MAX_SSIDS, max);
+ max = MAX_SSIDS;
+ }
+ ssid = (struct an_ltv_ssidlist_new *)&areq;
+ for (i = 0; i < max; i++)
+ printf("SSID %2d:\t\t\t[ %.*s ]\n", i + 1,
+ ssid->an_entry[i].an_len,
+ ssid->an_entry[i].an_ssid);
+
+ return;
+}
+
+static void
+an_dumpconfig(const char *iface)
+{
+ struct an_ltv_genconfig *cfg;
+ struct an_req areq;
+ unsigned char diversity;
+
+ areq.an_len = sizeof(areq);
+ areq.an_type = AN_RID_ACTUALCFG;
+
+ an_getval(iface, &areq);
+
+ cfg = (struct an_ltv_genconfig *)&areq;
+
+ printf("Operating mode:\t\t\t\t[ ");
+ if ((cfg->an_opmode & 0x7) == AN_OPMODE_IBSS_ADHOC)
+ printf("ad-hoc");
+ if ((cfg->an_opmode & 0x7) == AN_OPMODE_INFRASTRUCTURE_STATION)
+ printf("infrastructure");
+ if ((cfg->an_opmode & 0x7) == AN_OPMODE_AP)
+ printf("access point");
+ if ((cfg->an_opmode & 0x7) == AN_OPMODE_AP_REPEATER)
+ printf("access point repeater");
+ printf(" ]");
+ printf("\nReceive mode:\t\t\t\t[ ");
+ if ((cfg->an_rxmode & 0x7) == AN_RXMODE_BC_MC_ADDR)
+ printf("broadcast/multicast/unicast");
+ if ((cfg->an_rxmode & 0x7) == AN_RXMODE_BC_ADDR)
+ printf("broadcast/unicast");
+ if ((cfg->an_rxmode & 0x7) == AN_RXMODE_ADDR)
+ printf("unicast");
+ if ((cfg->an_rxmode & 0x7) == AN_RXMODE_80211_MONITOR_CURBSS)
+ printf("802.11 monitor, current BSSID");
+ if ((cfg->an_rxmode & 0x7) == AN_RXMODE_80211_MONITOR_ANYBSS)
+ printf("802.11 monitor, any BSSID");
+ if ((cfg->an_rxmode & 0x7) == AN_RXMODE_LAN_MONITOR_CURBSS)
+ printf("LAN monitor, current BSSID");
+ printf(" ]");
+ printf("\nFragment threshold:\t\t\t");
+ an_printwords(&cfg->an_fragthresh, 1);
+ printf("\nRTS threshold:\t\t\t\t");
+ an_printwords(&cfg->an_rtsthresh, 1);
+ printf("\nMAC address:\t\t\t\t");
+ an_printhex((char *)&cfg->an_macaddr, ETHER_ADDR_LEN);
+ printf("\nSupported rates:\t\t\t");
+ an_printspeeds(cfg->an_rates, 8);
+ printf("\nShort retry limit:\t\t\t");
+ an_printwords(&cfg->an_shortretry_limit, 1);
+ printf("\nLong retry limit:\t\t\t");
+ an_printwords(&cfg->an_longretry_limit, 1);
+ printf("\nTX MSDU lifetime:\t\t\t");
+ an_printwords(&cfg->an_tx_msdu_lifetime, 1);
+ printf("\nRX MSDU lifetime:\t\t\t");
+ an_printwords(&cfg->an_rx_msdu_lifetime, 1);
+ printf("\nStationary:\t\t\t\t");
+ an_printbool(cfg->an_stationary);
+ printf("\nOrdering:\t\t\t\t");
+ an_printbool(cfg->an_ordering);
+ printf("\nDevice type:\t\t\t\t[ ");
+ if (cfg->an_devtype == AN_DEVTYPE_PC4500)
+ printf("PC4500");
+ else if (cfg->an_devtype == AN_DEVTYPE_PC4800)
+ printf("PC4800");
+ else
+ printf("unknown (%x)", cfg->an_devtype);
+ printf(" ]");
+ printf("\nScanning mode:\t\t\t\t[ ");
+ if (cfg->an_scanmode == AN_SCANMODE_ACTIVE)
+ printf("active");
+ if (cfg->an_scanmode == AN_SCANMODE_PASSIVE)
+ printf("passive");
+ if (cfg->an_scanmode == AN_SCANMODE_AIRONET_ACTIVE)
+ printf("Aironet active");
+ printf(" ]");
+ printf("\nProbe delay:\t\t\t\t");
+ an_printwords(&cfg->an_probedelay, 1);
+ printf("\nProbe energy timeout:\t\t\t");
+ an_printwords(&cfg->an_probe_energy_timeout, 1);
+ printf("\nProbe response timeout:\t\t\t");
+ an_printwords(&cfg->an_probe_response_timeout, 1);
+ printf("\nBeacon listen timeout:\t\t\t");
+ an_printwords(&cfg->an_beacon_listen_timeout, 1);
+ printf("\nIBSS join network timeout:\t\t");
+ an_printwords(&cfg->an_ibss_join_net_timeout, 1);
+ printf("\nAuthentication timeout:\t\t\t");
+ an_printwords(&cfg->an_auth_timeout, 1);
+ printf("\nWEP enabled:\t\t\t\t[ ");
+ if (cfg->an_authtype & AN_AUTHTYPE_PRIVACY_IN_USE)
+ {
+ if (cfg->an_authtype & AN_AUTHTYPE_LEAP)
+ printf("LEAP");
+ else if (cfg->an_authtype & AN_AUTHTYPE_ALLOW_UNENCRYPTED)
+ printf("mixed cell");
+ else
+ printf("full");
+ }
+ else
+ printf("no");
+ printf(" ]");
+ printf("\nAuthentication type:\t\t\t[ ");
+ if ((cfg->an_authtype & AN_AUTHTYPE_MASK) == AN_AUTHTYPE_NONE)
+ printf("none");
+ if ((cfg->an_authtype & AN_AUTHTYPE_MASK) == AN_AUTHTYPE_OPEN)
+ printf("open");
+ if ((cfg->an_authtype & AN_AUTHTYPE_MASK) == AN_AUTHTYPE_SHAREDKEY)
+ printf("shared key");
+ printf(" ]");
+ printf("\nAssociation timeout:\t\t\t");
+ an_printwords(&cfg->an_assoc_timeout, 1);
+ printf("\nSpecified AP association timeout:\t");
+ an_printwords(&cfg->an_specified_ap_timeout, 1);
+ printf("\nOffline scan interval:\t\t\t");
+ an_printwords(&cfg->an_offline_scan_interval, 1);
+ printf("\nOffline scan duration:\t\t\t");
+ an_printwords(&cfg->an_offline_scan_duration, 1);
+ printf("\nLink loss delay:\t\t\t");
+ an_printwords(&cfg->an_link_loss_delay, 1);
+ printf("\nMax beacon loss time:\t\t\t");
+ an_printwords(&cfg->an_max_beacon_lost_time, 1);
+ printf("\nRefresh interval:\t\t\t");
+ an_printwords(&cfg->an_refresh_interval, 1);
+ printf("\nPower save mode:\t\t\t[ ");
+ if (cfg->an_psave_mode == AN_PSAVE_NONE)
+ printf("none");
+ if (cfg->an_psave_mode == AN_PSAVE_CAM)
+ printf("constantly awake mode");
+ if (cfg->an_psave_mode == AN_PSAVE_PSP)
+ printf("PSP");
+ if (cfg->an_psave_mode == AN_PSAVE_PSP_CAM)
+ printf("PSP-CAM (fast PSP)");
+ printf(" ]");
+ printf("\nSleep through DTIMs:\t\t\t");
+ an_printbool(cfg->an_sleep_for_dtims);
+ printf("\nPower save listen interval:\t\t");
+ an_printwords(&cfg->an_listen_interval, 1);
+ printf("\nPower save fast listen interval:\t");
+ an_printwords(&cfg->an_fast_listen_interval, 1);
+ printf("\nPower save listen decay:\t\t");
+ an_printwords(&cfg->an_listen_decay, 1);
+ printf("\nPower save fast listen decay:\t\t");
+ an_printwords(&cfg->an_fast_listen_decay, 1);
+ printf("\nAP/ad-hoc Beacon period:\t\t");
+ an_printwords(&cfg->an_beacon_period, 1);
+ printf("\nAP/ad-hoc ATIM duration:\t\t");
+ an_printwords(&cfg->an_atim_duration, 1);
+ printf("\nAP/ad-hoc current channel:\t\t");
+ an_printwords(&cfg->an_ds_channel, 1);
+ printf("\nAP/ad-hoc DTIM period:\t\t\t");
+ an_printwords(&cfg->an_dtim_period, 1);
+ printf("\nRadio type:\t\t\t\t[ ");
+ if (cfg->an_radiotype & AN_RADIOTYPE_80211_FH)
+ printf("802.11 FH");
+ else if (cfg->an_radiotype & AN_RADIOTYPE_80211_DS)
+ printf("802.11 DS");
+ else if (cfg->an_radiotype & AN_RADIOTYPE_LM2000_DS)
+ printf("LM2000 DS");
+ else
+ printf("unknown (%x)", cfg->an_radiotype);
+ printf(" ]");
+ printf("\nRX Diversity:\t\t\t\t[ ");
+ diversity = cfg->an_diversity & 0xFF;
+ if (diversity == AN_DIVERSITY_FACTORY_DEFAULT)
+ printf("factory default");
+ else if (diversity == AN_DIVERSITY_ANTENNA_1_ONLY)
+ printf("antenna 1 only");
+ else if (diversity == AN_DIVERSITY_ANTENNA_2_ONLY)
+ printf("antenna 2 only");
+ else if (diversity == AN_DIVERSITY_ANTENNA_1_AND_2)
+ printf("antenna 1 and 2");
+ printf(" ]");
+ printf("\nTX Diversity:\t\t\t\t[ ");
+ diversity = (cfg->an_diversity >> 8) & 0xFF;
+ if (diversity == AN_DIVERSITY_FACTORY_DEFAULT)
+ printf("factory default");
+ else if (diversity == AN_DIVERSITY_ANTENNA_1_ONLY)
+ printf("antenna 1 only");
+ else if (diversity == AN_DIVERSITY_ANTENNA_2_ONLY)
+ printf("antenna 2 only");
+ else if (diversity == AN_DIVERSITY_ANTENNA_1_AND_2)
+ printf("antenna 1 and 2");
+ printf(" ]");
+ printf("\nTransmit power level:\t\t\t");
+ an_printwords(&cfg->an_tx_power, 1);
+ printf("\nRSS threshold:\t\t\t\t");
+ an_printwords(&cfg->an_rss_thresh, 1);
+ printf("\nNode name:\t\t\t\t");
+ an_printstr((char *)&cfg->an_nodename, 16);
+ printf("\nARL threshold:\t\t\t\t");
+ an_printwords(&cfg->an_arl_thresh, 1);
+ printf("\nARL decay:\t\t\t\t");
+ an_printwords(&cfg->an_arl_decay, 1);
+ printf("\nARL delay:\t\t\t\t");
+ an_printwords(&cfg->an_arl_delay, 1);
+ printf("\nConfiguration:\t\t\t\t[ ");
+ if (cfg->an_home_product & AN_HOME_NETWORK)
+ printf("Home Configuration");
+ else
+ printf("Enterprise Configuration");
+ printf(" ]");
+
+ printf("\n");
+ printf("\n");
+ an_readkeyinfo(iface);
+}
+
+static void
+an_dumprssimap(const char *iface)
+{
+ struct an_ltv_rssi_map *rssi;
+ struct an_req areq;
+ int i;
+
+ areq.an_len = sizeof(areq);
+ areq.an_type = AN_RID_RSSI_MAP;
+
+ an_getval(iface, &areq);
+
+ rssi = (struct an_ltv_rssi_map *)&areq;
+
+ printf("idx\tpct\t dBm\n");
+
+ for (i = 0; i < 0xFF; i++) {
+ /*
+ * negate the dBm value: it's the only way the power
+ * level makes sense
+ */
+ printf("%3d\t%3d\t%4d\n", i,
+ rssi->an_entries[i].an_rss_pct,
+ - rssi->an_entries[i].an_rss_dbm);
+ }
+}
+
+static void
+usage(const char *p)
+{
+ fprintf(stderr, "usage: %s -i iface -A (show specified APs)\n", p);
+ fprintf(stderr, "\t%s -i iface -N (show specified SSIDss)\n", p);
+ fprintf(stderr, "\t%s -i iface -S (show NIC status)\n", p);
+ fprintf(stderr, "\t%s -i iface -I (show NIC capabilities)\n", p);
+ fprintf(stderr, "\t%s -i iface -T (show stats counters)\n", p);
+ fprintf(stderr, "\t%s -i iface -C (show current config)\n", p);
+ fprintf(stderr, "\t%s -i iface -R (show RSSI map)\n", p);
+ fprintf(stderr, "\t%s -i iface -t 0-4 (set TX speed)\n", p);
+ fprintf(stderr, "\t%s -i iface -s 0-3 (set power save mode)\n", p);
+ fprintf(stderr, "\t%s -i iface [-v 1-4] -a AP (specify AP)\n", p);
+ fprintf(stderr, "\t%s -i iface -b val (set beacon period)\n", p);
+ fprintf(stderr, "\t%s -i iface [-v 0|1] -d val (set diversity)\n", p);
+ fprintf(stderr, "\t%s -i iface -j val (set netjoin timeout)\n", p);
+ fprintf(stderr, "\t%s -i iface -e 0-4 (enable transmit key)\n", p);
+ fprintf(stderr, "\t%s -i iface [-v 0-8] -k key (set key)\n", p);
+ fprintf(stderr, "\t%s -i iface -K 0-2 (no auth/open/shared secret)\n", p);
+ fprintf(stderr, "\t%s -i iface -W 0-2 (no WEP/full WEP/mixed cell)\n", p);
+ fprintf(stderr, "\t%s -i iface -l val (set station name)\n", p);
+ fprintf(stderr, "\t%s -i iface -m val (set MAC address)\n", p);
+ fprintf(stderr, "\t%s -i iface [-v 1-3] -n SSID "
+ "(specify SSID)\n", p);
+ fprintf(stderr, "\t%s -i iface -o 0|1 (set operating mode)\n", p);
+ fprintf(stderr, "\t%s -i iface -c val (set ad-hoc channel)\n", p);
+ fprintf(stderr, "\t%s -i iface -f val (set frag threshold)\n", p);
+ fprintf(stderr, "\t%s -i iface -r val (set RTS threshold)\n", p);
+ fprintf(stderr, "\t%s -i iface -M 0-15 (set monitor mode)\n", p);
+ fprintf(stderr, "\t%s -i iface -L user (enter LEAP authentication mode)\n", p);
+#ifdef ANCACHE
+ fprintf(stderr, "\t%s -i iface -Q print signal quality cache\n", p);
+ fprintf(stderr, "\t%s -i iface -Z zero out signal cache\n", p);
+#endif
+
+ fprintf(stderr, "\t%s -h (display this message)\n", p);
+
+ exit(1);
+}
+
+static void
+an_setconfig(const char *iface, int act, void *arg)
+{
+ struct an_ltv_genconfig *cfg;
+ struct an_ltv_caps *caps;
+ struct an_req areq;
+ struct an_req areq_caps;
+ u_int16_t diversity = 0;
+ struct ether_addr *addr;
+ int i;
+
+ areq.an_len = sizeof(areq);
+ areq.an_type = AN_RID_GENCONFIG;
+ an_getval(iface, &areq);
+ cfg = (struct an_ltv_genconfig *)&areq;
+
+ areq_caps.an_len = sizeof(areq);
+ areq_caps.an_type = AN_RID_CAPABILITIES;
+ an_getval(iface, &areq_caps);
+ caps = (struct an_ltv_caps *)&areq_caps;
+
+ switch(act) {
+ case ACT_SET_OPMODE:
+ cfg->an_opmode = atoi(arg);
+ break;
+ case ACT_SET_FREQ:
+ cfg->an_ds_channel = atoi(arg);
+ break;
+ case ACT_SET_PWRSAVE:
+ cfg->an_psave_mode = atoi(arg);
+ break;
+ case ACT_SET_SCANMODE:
+ cfg->an_scanmode = atoi(arg);
+ break;
+ case ACT_SET_DIVERSITY_RX:
+ case ACT_SET_DIVERSITY_TX:
+ switch(atoi(arg)) {
+ case 0:
+ diversity = AN_DIVERSITY_FACTORY_DEFAULT;
+ break;
+ case 1:
+ diversity = AN_DIVERSITY_ANTENNA_1_ONLY;
+ break;
+ case 2:
+ diversity = AN_DIVERSITY_ANTENNA_2_ONLY;
+ break;
+ case 3:
+ diversity = AN_DIVERSITY_ANTENNA_1_AND_2;
+ break;
+ default:
+ errx(1, "bad diversity setting: %u", diversity);
+ break;
+ }
+ if (act == ACT_SET_DIVERSITY_RX) {
+ cfg->an_diversity &= 0xFF00;
+ cfg->an_diversity |= diversity;
+ } else {
+ cfg->an_diversity &= 0x00FF;
+ cfg->an_diversity |= (diversity << 8);
+ }
+ break;
+ case ACT_SET_TXPWR:
+ for (i = 0; i < 8; i++) {
+ if (caps->an_tx_powerlevels[i] == atoi(arg))
+ break;
+ }
+ if (i == 8)
+ errx(1, "unsupported power level: %dmW", atoi(arg));
+
+ cfg->an_tx_power = atoi(arg);
+ break;
+ case ACT_SET_RTS_THRESH:
+ cfg->an_rtsthresh = atoi(arg);
+ break;
+ case ACT_SET_RTS_RETRYLIM:
+ cfg->an_shortretry_limit =
+ cfg->an_longretry_limit = atoi(arg);
+ break;
+ case ACT_SET_BEACON_PERIOD:
+ cfg->an_beacon_period = atoi(arg);
+ break;
+ case ACT_SET_WAKE_DURATION:
+ cfg->an_atim_duration = atoi(arg);
+ break;
+ case ACT_SET_FRAG_THRESH:
+ cfg->an_fragthresh = atoi(arg);
+ break;
+ case ACT_SET_NETJOIN:
+ cfg->an_ibss_join_net_timeout = atoi(arg);
+ break;
+ case ACT_SET_MYNAME:
+ bzero(cfg->an_nodename, 16);
+ strncpy((char *)&cfg->an_nodename, optarg, 16);
+ break;
+ case ACT_SET_MAC:
+ addr = ether_aton((char *)arg);
+
+ if (addr == NULL)
+ errx(1, "badly formatted address");
+ bzero(cfg->an_macaddr, ETHER_ADDR_LEN);
+ bcopy(addr, &cfg->an_macaddr, ETHER_ADDR_LEN);
+ break;
+ case ACT_ENABLE_WEP:
+ switch (atoi (arg)) {
+ case 0:
+ /* no WEP */
+ cfg->an_authtype &= ~(AN_AUTHTYPE_PRIVACY_IN_USE
+ | AN_AUTHTYPE_ALLOW_UNENCRYPTED
+ | AN_AUTHTYPE_LEAP);
+ break;
+ case 1:
+ /* full WEP */
+ cfg->an_authtype |= AN_AUTHTYPE_PRIVACY_IN_USE;
+ cfg->an_authtype &= ~AN_AUTHTYPE_ALLOW_UNENCRYPTED;
+ cfg->an_authtype &= ~AN_AUTHTYPE_LEAP;
+ break;
+ case 2:
+ /* mixed cell */
+ cfg->an_authtype = AN_AUTHTYPE_PRIVACY_IN_USE
+ | AN_AUTHTYPE_ALLOW_UNENCRYPTED;
+ break;
+ }
+ break;
+ case ACT_SET_KEY_TYPE:
+ cfg->an_authtype = (cfg->an_authtype & ~AN_AUTHTYPE_MASK)
+ | atoi(arg);
+ break;
+ case ACT_SET_MONITOR_MODE:
+ areq.an_type = AN_RID_MONITOR_MODE;
+ cfg->an_len = atoi(arg); /* mode is put in length */
+ break;
+ default:
+ errx(1, "unknown action");
+ break;
+ }
+
+ an_setval(iface, &areq);
+ exit(0);
+}
+
+static void
+an_setspeed(const char *iface, int act __unused, void *arg)
+{
+ struct an_req areq;
+ struct an_ltv_caps *caps;
+ u_int16_t speed;
+
+ areq.an_len = sizeof(areq);
+ areq.an_type = AN_RID_CAPABILITIES;
+
+ an_getval(iface, &areq);
+ caps = (struct an_ltv_caps *)&areq;
+
+ switch(atoi(arg)) {
+ case 0:
+ speed = 0;
+ break;
+ case 1:
+ speed = AN_RATE_1MBPS;
+ break;
+ case 2:
+ speed = AN_RATE_2MBPS;
+ break;
+ case 3:
+ if (caps->an_rates[2] != AN_RATE_5_5MBPS)
+ errx(1, "5.5Mbps not supported on this card");
+ speed = AN_RATE_5_5MBPS;
+ break;
+ case 4:
+ if (caps->an_rates[3] != AN_RATE_11MBPS)
+ errx(1, "11Mbps not supported on this card");
+ speed = AN_RATE_11MBPS;
+ break;
+ default:
+ errx(1, "unsupported speed");
+ break;
+ }
+
+ areq.an_len = 6;
+ areq.an_type = AN_RID_TX_SPEED;
+ areq.an_val[0] = speed;
+
+ an_setval(iface, &areq);
+ exit(0);
+}
+
+static void
+an_setap(const char *iface, int act, void *arg)
+{
+ struct an_ltv_aplist *ap;
+ struct an_req areq;
+ struct ether_addr *addr;
+
+ areq.an_len = sizeof(areq);
+ areq.an_type = AN_RID_APLIST;
+
+ an_getval(iface, &areq);
+ ap = (struct an_ltv_aplist *)&areq;
+
+ addr = ether_aton((char *)arg);
+
+ if (addr == NULL)
+ errx(1, "badly formatted address");
+
+ switch(act) {
+ case ACT_SET_AP1:
+ bzero(ap->an_ap1, ETHER_ADDR_LEN);
+ bcopy(addr, &ap->an_ap1, ETHER_ADDR_LEN);
+ break;
+ case ACT_SET_AP2:
+ bzero(ap->an_ap2, ETHER_ADDR_LEN);
+ bcopy(addr, &ap->an_ap2, ETHER_ADDR_LEN);
+ break;
+ case ACT_SET_AP3:
+ bzero(ap->an_ap3, ETHER_ADDR_LEN);
+ bcopy(addr, &ap->an_ap3, ETHER_ADDR_LEN);
+ break;
+ case ACT_SET_AP4:
+ bzero(ap->an_ap4, ETHER_ADDR_LEN);
+ bcopy(addr, &ap->an_ap4, ETHER_ADDR_LEN);
+ break;
+ default:
+ errx(1, "unknown action");
+ break;
+ }
+
+ an_setval(iface, &areq);
+ exit(0);
+}
+
+static void
+an_setssid(const char *iface, int act, void *arg)
+{
+ struct an_ltv_ssidlist_new *ssid;
+ struct an_req areq;
+ int max;
+
+ areq.an_len = sizeof(areq);
+ areq.an_type = AN_RID_SSIDLIST;
+
+ an_getval(iface, &areq);
+ ssid = (struct an_ltv_ssidlist_new *)&areq;
+
+ max = (areq.an_len - 4) / sizeof(struct an_ltv_ssid_entry);
+ if ( max > MAX_SSIDS ) {
+ printf("Too many SSIDs only printing %d of %d\n",
+ MAX_SSIDS, max);
+ max = MAX_SSIDS;
+ }
+
+ if ( act > max ) {
+ errx(1, "bad modifier %d: there "
+ "are only %d SSID settings", act, max);
+ exit(1);
+ }
+
+ bzero(ssid->an_entry[act-1].an_ssid,
+ sizeof(ssid->an_entry[act-1].an_ssid));
+ strlcpy(ssid->an_entry[act-1].an_ssid, (char *)arg,
+ sizeof(ssid->an_entry[act-1].an_ssid));
+ ssid->an_entry[act-1].an_len
+ = strlen(ssid->an_entry[act-1].an_ssid);
+
+ an_setval(iface, &areq);
+
+ exit(0);
+}
+
+#ifdef ANCACHE
+static void
+an_zerocache(const char *iface)
+{
+ struct an_req areq;
+
+ bzero(&areq, sizeof(areq));
+ areq.an_len = 0;
+ areq.an_type = AN_RID_ZERO_CACHE;
+
+ an_getval(iface, &areq);
+}
+
+static void
+an_readcache(const char *iface)
+{
+ struct an_req areq;
+ uint16_t *an_sigitems;
+ struct an_sigcache *sc;
+ int i;
+
+ if (iface == NULL)
+ errx(1, "must specify interface name");
+
+ bzero(&areq, sizeof(areq));
+ areq.an_len = AN_MAX_DATALEN;
+ areq.an_type = AN_RID_READ_CACHE;
+
+ an_getval(iface, &areq);
+
+ an_sigitems = areq.an_val;
+ sc = (struct an_sigcache *)((int32_t *)areq.an_val + 1);
+
+ for (i = 0; i < *an_sigitems; i++) {
+ printf("[%d/%d]:", i+1, *an_sigitems);
+ printf(" %02x:%02x:%02x:%02x:%02x:%02x,",
+ sc->macsrc[0]&0xff,
+ sc->macsrc[1]&0xff,
+ sc->macsrc[2]&0xff,
+ sc->macsrc[3]&0xff,
+ sc->macsrc[4]&0xff,
+ sc->macsrc[5]&0xff);
+ printf(" %d.%d.%d.%d,",((sc->ipsrc >> 0) & 0xff),
+ ((sc->ipsrc >> 8) & 0xff),
+ ((sc->ipsrc >> 16) & 0xff),
+ ((sc->ipsrc >> 24) & 0xff));
+ printf(" sig: %d, noise: %d, qual: %d\n",
+ sc->signal,
+ sc->noise,
+ sc->quality);
+ sc++;
+ }
+}
+#endif
+
+static int
+an_hex2int(char c)
+{
+ if (c >= '0' && c <= '9')
+ return (c - '0');
+ if (c >= 'A' && c <= 'F')
+ return (c - 'A' + 10);
+ if (c >= 'a' && c <= 'f')
+ return (c - 'a' + 10);
+
+ return (0);
+}
+
+static void
+an_str2key(const char *s, struct an_ltv_key *k)
+{
+ int n, i;
+ char *p;
+
+ /* Is this a hex string? */
+ if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
+ /* Yes, convert to int. */
+ n = 0;
+ p = (char *)&k->key[0];
+ for (i = 2; s[i] != '\0' && s[i + 1] != '\0'; i+= 2) {
+ *p++ = (an_hex2int(s[i]) << 4) + an_hex2int(s[i + 1]);
+ n++;
+ }
+ if (s[i] != '\0')
+ errx(1, "hex strings must be of even length");
+ k->klen = n;
+ } else {
+ /* No, just copy it in. */
+ bcopy(s, k->key, strlen(s));
+ k->klen = strlen(s);
+ }
+
+ return;
+}
+
+static void
+an_setkeys(const char *iface, const char *key, int keytype)
+{
+ struct an_req areq;
+ struct an_ltv_key *k;
+
+ bzero(&areq, sizeof(areq));
+ k = (struct an_ltv_key *)&areq;
+
+ if (strlen(key) > 28) {
+ err(1, "encryption key must be no "
+ "more than 18 characters long");
+ }
+
+ an_str2key(key, k);
+
+ k->kindex=keytype/2;
+
+ if (!(k->klen==0 || k->klen==5 || k->klen==13)) {
+ err(1, "encryption key must be 0, 5 or 13 bytes long");
+ }
+
+ /* default mac and only valid one (from manual) 1.0.0.0.0.0 */
+ k->mac[0]=1;
+ k->mac[1]=0;
+ k->mac[2]=0;
+ k->mac[3]=0;
+ k->mac[4]=0;
+ k->mac[5]=0;
+
+ switch(keytype & 1) {
+ case 0:
+ areq.an_len = sizeof(struct an_ltv_key);
+ areq.an_type = AN_RID_WEP_PERM;
+ an_setval(iface, &areq);
+ break;
+ case 1:
+ areq.an_len = sizeof(struct an_ltv_key);
+ areq.an_type = AN_RID_WEP_TEMP;
+ an_setval(iface, &areq);
+ break;
+ }
+}
+
+static void an_readkeyinfo(iface)
+ const char *iface;
+{
+ struct an_req areq;
+ struct an_ltv_genconfig *cfg;
+ struct an_ltv_key *k;
+ int i;
+ int home;
+
+ areq.an_len = sizeof(areq);
+ areq.an_type = AN_RID_ACTUALCFG;
+ an_getval(iface, &areq);
+ cfg = (struct an_ltv_genconfig *)&areq;
+ if (cfg->an_home_product & AN_HOME_NETWORK)
+ home = 1;
+ else
+ home = 0;
+
+ bzero(&areq, sizeof(areq));
+ k = (struct an_ltv_key *)&areq;
+
+ printf("WEP Key status:\n");
+ areq.an_type = AN_RID_WEP_TEMP; /* read first key */
+ for(i=0; i<5; i++) {
+ areq.an_len = sizeof(struct an_ltv_key);
+ an_getval(iface, &areq);
+ if (k->kindex == 0xffff)
+ break;
+ switch (k->klen) {
+ case 0:
+ printf("\tKey %u is unset\n", k->kindex);
+ break;
+ case 5:
+ printf("\tKey %u is set 40 bits\n", k->kindex);
+ break;
+ case 13:
+ printf("\tKey %u is set 128 bits\n", k->kindex);
+ break;
+ default:
+ printf("\tWEP Key %d has an unknown size %u\n",
+ i, k->klen);
+ }
+
+ areq.an_type = AN_RID_WEP_PERM; /* read next key */
+ }
+ k->kindex = 0xffff;
+ areq.an_len = sizeof(struct an_ltv_key);
+ an_getval(iface, &areq);
+ printf("\tThe active transmit key is %d\n", 4 * home + k->mac[0]);
+
+ return;
+}
+
+static void
+an_enable_tx_key(const char *iface, const char *arg)
+{
+ struct an_req areq;
+ struct an_ltv_key *k;
+ struct an_ltv_genconfig *config;
+
+ bzero(&areq, sizeof(areq));
+
+ /* set home or not home mode */
+ areq.an_len = sizeof(struct an_ltv_genconfig);
+ areq.an_type = AN_RID_GENCONFIG;
+ an_getval(iface, &areq);
+ config = (struct an_ltv_genconfig *)&areq;
+ if (atoi(arg) == 4) {
+ config->an_home_product |= AN_HOME_NETWORK;
+ }else{
+ config->an_home_product &= ~AN_HOME_NETWORK;
+ }
+ an_setval(iface, &areq);
+
+ bzero(&areq, sizeof(areq));
+
+ k = (struct an_ltv_key *)&areq;
+
+ /* From a Cisco engineer write the transmit key to use in the
+ first MAC, index is FFFF*/
+ k->kindex=0xffff;
+ k->klen=0;
+
+ k->mac[0]=atoi(arg);
+ k->mac[1]=0;
+ k->mac[2]=0;
+ k->mac[3]=0;
+ k->mac[4]=0;
+ k->mac[5]=0;
+
+ areq.an_len = sizeof(struct an_ltv_key);
+ areq.an_type = AN_RID_WEP_PERM;
+ an_setval(iface, &areq);
+}
+
+static void
+an_enable_leap_mode(const char *iface, const char *username)
+{
+ struct an_req areq;
+ struct an_ltv_status *sts;
+ struct an_ltv_genconfig *cfg;
+ struct an_ltv_caps *caps;
+ struct an_ltv_leap_username an_username;
+ struct an_ltv_leap_password an_password;
+ char *password;
+ MD4_CTX context;
+ int len;
+ int i;
+ char unicode_password[LEAP_PASSWORD_MAX * 2];
+
+ areq.an_len = sizeof(areq);
+ areq.an_type = AN_RID_CAPABILITIES;
+
+ an_getval(iface, &areq);
+
+ caps = (struct an_ltv_caps *)&areq;
+
+ if (!caps->an_softcaps & AN_AUTHTYPE_LEAP) {
+ fprintf(stderr, "Firmware does not support LEAP\n");
+ exit(1);
+ }
+
+ bzero(&an_username, sizeof(an_username));
+ bzero(&an_password, sizeof(an_password));
+
+ len = strlen(username);
+ if (len > LEAP_USERNAME_MAX) {
+ printf("Username too long (max %d)\n", LEAP_USERNAME_MAX);
+ exit(1);
+ }
+ strncpy(an_username.an_username, username, len);
+ an_username.an_username_len = len;
+ an_username.an_len = sizeof(an_username);
+ an_username.an_type = AN_RID_LEAPUSERNAME;
+
+ password = getpass("Enter LEAP password:");
+
+ len = strlen(password);
+ if (len > LEAP_PASSWORD_MAX) {
+ printf("Password too long (max %d)\n", LEAP_PASSWORD_MAX);
+ exit(1);
+ }
+
+ bzero(&unicode_password, sizeof(unicode_password));
+ for(i = 0; i < len; i++) {
+ unicode_password[i * 2] = *password++;
+ }
+
+ /* First half */
+ MD4Init(&context);
+ MD4Update(&context, unicode_password, len * 2);
+ MD4Final(&an_password.an_password[0], &context);
+
+ /* Second half */
+ MD4Init (&context);
+ MD4Update (&context, &an_password.an_password[0], 16);
+ MD4Final (&an_password.an_password[16], &context);
+
+ an_password.an_password_len = 32;
+ an_password.an_len = sizeof(an_password);
+ an_password.an_type = AN_RID_LEAPPASSWORD;
+
+ an_setval(iface, (struct an_req *)&an_username);
+ an_setval(iface, (struct an_req *)&an_password);
+
+ areq.an_len = sizeof(areq);
+ areq.an_type = AN_RID_GENCONFIG;
+ an_getval(iface, &areq);
+ cfg = (struct an_ltv_genconfig *)&areq;
+ cfg->an_authtype = (AN_AUTHTYPE_PRIVACY_IN_USE | AN_AUTHTYPE_LEAP);
+ an_setval(iface, &areq);
+
+ sts = (struct an_ltv_status *)&areq;
+ areq.an_type = AN_RID_STATUS;
+
+ for (i = 60; i > 0; i--) {
+ an_getval(iface, &areq);
+ if (sts->an_opmode & AN_STATUS_OPMODE_LEAP) {
+ printf("Authenticated\n");
+ break;
+ }
+ sleep(1);
+ }
+
+ if (i == 0) {
+ fprintf(stderr, "Failed LEAP authentication\n");
+ exit(1);
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ int ch;
+ int act = 0;
+ const char *iface = NULL;
+ int modifier = 0;
+ char *key = NULL;
+ void *arg = NULL;
+ char *p = argv[0];
+
+ /* Get the interface name */
+ opterr = 0;
+ ch = getopt(argc, argv, "i:");
+ if (ch == 'i') {
+ iface = optarg;
+ } else {
+ if (argc > 1 && *argv[1] != '-') {
+ iface = argv[1];
+ optind = 2;
+ } else {
+ iface = "an0";
+ optind = 1;
+ }
+ optreset = 1;
+ }
+ opterr = 1;
+
+ while ((ch = getopt(argc, argv,
+ "ANISCTRht:a:e:o:s:n:v:d:j:b:c:f:r:p:w:m:l:k:K:W:QZM:L:")) != -1) {
+ switch(ch) {
+ case 'Z':
+#ifdef ANCACHE
+ act = ACT_ZEROCACHE;
+#else
+ errx(1, "ANCACHE not available");
+#endif
+ break;
+ case 'Q':
+#ifdef ANCACHE
+ act = ACT_DUMPCACHE;
+#else
+ errx(1, "ANCACHE not available");
+#endif
+ break;
+ case 'A':
+ act = ACT_DUMPAP;
+ break;
+ case 'N':
+ act = ACT_DUMPSSID;
+ break;
+ case 'S':
+ act = ACT_DUMPSTATUS;
+ break;
+ case 'I':
+ act = ACT_DUMPCAPS;
+ break;
+ case 'T':
+ act = ACT_DUMPSTATS;
+ break;
+ case 'C':
+ act = ACT_DUMPCONFIG;
+ break;
+ case 'R':
+ act = ACT_DUMPRSSIMAP;
+ break;
+ case 't':
+ act = ACT_SET_TXRATE;
+ arg = optarg;
+ break;
+ case 's':
+ act = ACT_SET_PWRSAVE;
+ arg = optarg;
+ break;
+ case 'p':
+ act = ACT_SET_TXPWR;
+ arg = optarg;
+ break;
+ case 'v':
+ modifier = atoi(optarg);
+ break;
+ case 'a':
+ switch(modifier) {
+ case 0:
+ case 1:
+ act = ACT_SET_AP1;
+ break;
+ case 2:
+ act = ACT_SET_AP2;
+ break;
+ case 3:
+ act = ACT_SET_AP3;
+ break;
+ case 4:
+ act = ACT_SET_AP4;
+ break;
+ default:
+ errx(1, "bad modifier %d: there "
+ "are only 4 access point settings",
+ modifier);
+ usage(p);
+ break;
+ }
+ arg = optarg;
+ break;
+ case 'b':
+ act = ACT_SET_BEACON_PERIOD;
+ arg = optarg;
+ break;
+ case 'd':
+ switch(modifier) {
+ case 0:
+ act = ACT_SET_DIVERSITY_RX;
+ break;
+ case 1:
+ act = ACT_SET_DIVERSITY_TX;
+ break;
+ default:
+ errx(1, "must specify RX or TX diversity");
+ break;
+ }
+ if (!isdigit(*optarg)) {
+ errx(1, "%s is not numeric", optarg);
+ exit(1);
+ }
+ arg = optarg;
+ break;
+ case 'j':
+ act = ACT_SET_NETJOIN;
+ arg = optarg;
+ break;
+ case 'l':
+ act = ACT_SET_MYNAME;
+ arg = optarg;
+ break;
+ case 'm':
+ act = ACT_SET_MAC;
+ arg = optarg;
+ break;
+ case 'n':
+ if (modifier == 0)
+ modifier = 1;
+ act = ACT_SET_SSID;
+ arg = optarg;
+ break;
+ case 'o':
+ act = ACT_SET_OPMODE;
+ arg = optarg;
+ break;
+ case 'c':
+ act = ACT_SET_FREQ;
+ arg = optarg;
+ break;
+ case 'f':
+ act = ACT_SET_FRAG_THRESH;
+ arg = optarg;
+ break;
+ case 'W':
+ act = ACT_ENABLE_WEP;
+ arg = optarg;
+ break;
+ case 'K':
+ act = ACT_SET_KEY_TYPE;
+ arg = optarg;
+ break;
+ case 'k':
+ act = ACT_SET_KEYS;
+ key = optarg;
+ break;
+ case 'e':
+ act = ACT_ENABLE_TX_KEY;
+ arg = optarg;
+ break;
+ case 'q':
+ act = ACT_SET_RTS_RETRYLIM;
+ arg = optarg;
+ break;
+ case 'r':
+ act = ACT_SET_RTS_THRESH;
+ arg = optarg;
+ break;
+ case 'w':
+ act = ACT_SET_WAKE_DURATION;
+ arg = optarg;
+ break;
+ case 'M':
+ act = ACT_SET_MONITOR_MODE;
+ arg = optarg;
+ break;
+ case 'L':
+ act = ACT_SET_LEAP_MODE;
+ arg = optarg;
+ break;
+ case 'h':
+ default:
+ usage(p);
+ }
+ }
+
+ if (iface == NULL || (!act && !key))
+ usage(p);
+
+ switch(act) {
+ case ACT_DUMPSTATUS:
+ an_dumpstatus(iface);
+ break;
+ case ACT_DUMPCAPS:
+ an_dumpcaps(iface);
+ break;
+ case ACT_DUMPSTATS:
+ an_dumpstats(iface);
+ break;
+ case ACT_DUMPCONFIG:
+ an_dumpconfig(iface);
+ break;
+ case ACT_DUMPSSID:
+ an_dumpssid(iface);
+ break;
+ case ACT_DUMPAP:
+ an_dumpap(iface);
+ break;
+ case ACT_DUMPRSSIMAP:
+ an_dumprssimap(iface);
+ break;
+ case ACT_SET_SSID:
+ an_setssid(iface, modifier, arg);
+ break;
+ case ACT_SET_AP1:
+ case ACT_SET_AP2:
+ case ACT_SET_AP3:
+ case ACT_SET_AP4:
+ an_setap(iface, act, arg);
+ break;
+ case ACT_SET_TXRATE:
+ an_setspeed(iface, act, arg);
+ break;
+#ifdef ANCACHE
+ case ACT_ZEROCACHE:
+ an_zerocache(iface);
+ break;
+ case ACT_DUMPCACHE:
+ an_readcache(iface);
+ break;
+
+#endif
+ case ACT_SET_KEYS:
+ an_setkeys(iface, key, modifier);
+ break;
+ case ACT_ENABLE_TX_KEY:
+ an_enable_tx_key(iface, arg);
+ break;
+ case ACT_SET_LEAP_MODE:
+ an_enable_leap_mode(iface, arg);
+ break;
+ default:
+ an_setconfig(iface, act, arg);
+ break;
+ }
+
+ exit(0);
+}
+
diff --git a/usr.sbin/apm/Makefile b/usr.sbin/apm/Makefile
new file mode 100644
index 0000000..043c3c7
--- /dev/null
+++ b/usr.sbin/apm/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= apm
+MAN= apm.8
+MLINKS= apm.8 apmconf.8
+MANSUBDIR= /i386
+WARNS?= 6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/apm/apm.8 b/usr.sbin/apm/apm.8
new file mode 100644
index 0000000..1e36f63
--- /dev/null
+++ b/usr.sbin/apm/apm.8
@@ -0,0 +1,159 @@
+.\" LP (Laptop Package)
+.\"
+.\" Copyright (c) 1994 by Tatsumi Hosokawa <hosokawa@jp.FreeBSD.org>
+.\"
+.\" This software may be used, modified, copied, and distributed, in
+.\" both source and binary form provided that the above copyright and
+.\" these terms are retained. Under no circumstances is the author
+.\" responsible for the proper functioning of this software, nor does
+.\" the author assume any responsibility for damages incurred with its
+.\"
+.\" $FreeBSD$
+.\"
+.\" use.
+.Dd November 1, 1994
+.Dt APM 8 i386
+.Os
+.Sh NAME
+.Nm apm , zzz
+.Nd control the APM BIOS and display its information
+.Sh SYNOPSIS
+.Nm
+.Op Fl ablstzZ
+.Op Fl d Ar enable
+.Op Fl e Ar enable
+.Op Fl h Ar enable
+.Op Fl r Ar delta
+.Pp
+.Nm zzz
+.Sh DESCRIPTION
+The
+.Nm
+utility
+controls the Intel / Microsoft APM (Advanced Power Management) BIOS and
+displays the current status of APM on laptop PCs.
+The
+.Nm zzz
+utility suspends the system by controlling APM.
+.Pp
+The following options are available for
+.Nm
+(no options are available for
+.Nm zzz ) .
+If no options are specified,
+.Nm
+displays information and current status of APM in verbose mode.
+If multiple display options are given, the values are displayed one
+per line in the order given here.
+.Bl -tag -width indent
+.It Fl a
+Display the current AC-line status as an integer value.
+The values
+0, 1 and 2 correspond to the
+.Dq off-line
+state,
+.Dq on-line
+state or
+.Dq backup power
+state, respectively.
+.It Fl b
+Display an integer value reflecting the current battery status.
+The values 0, 1, 2, 3, correspond to the
+.Dq high
+status,
+.Dq low
+status,
+.Dq critical
+status,
+.Dq charging
+status respectively.
+.It Fl d Ar enable
+Disable/enable suspending of the display separately from a normal suspend
+using the boolean value for
+.Ar enable .
+This feature seems to not work on many different laptops,
+including the Libretto 30CT and 50CT.
+.It Fl e Ar enable
+Enable or disable APM functions of the computer,
+depending on the boolean
+.Ar enable
+argument.
+.It Fl h Ar enable
+Depending on the boolean value of
+.Ar enable ,
+enable or disable the HLT instruction in the kernel context switch routine.
+These options are not necessary for almost all APM implementations,
+but for some implementations whose
+.Dq Pa Idle CPU
+call executes both CPU clock slowdown and HLT instruction,
+.Fl h Cm false
+is necessary to prevent the system from reducing its peak performance.
+See
+.Xr apm 4
+for details.
+.It Fl l
+Display the remaining battery percentage.
+If your laptop does not
+support this function, 255 is displayed.
+.It Fl r Ar delta
+Enable the resume wakeup timer, if the laptop supports it.
+This
+does not actually suspend the laptop, but if the laptop is suspended,
+and it supports resume from suspend, then it will be resumed after
+.Ar delta
+seconds (from when you run this command, not from when you suspend).
+.It Fl s
+Display the status of the APM support as an integer value.
+The values
+0 and 1 correspond to the
+.Dq disabled
+state or
+.Dq enabled
+state respectively.
+.It Fl t
+Display the estimated remaining battery lifetime in seconds.
+If
+it is unknown, -1 is displayed.
+.It Fl Z
+Transition the system into standby mode.
+This mode uses less power than
+full power mode, but more than suspend mode.
+Some laptops support
+resuming from this state on timer or Ring Indicator events.
+The
+output of
+.Nm
+tells what your laptop claims to support.
+.It Fl z
+Suspend the system.
+It is equivalent to
+.Nm zzz .
+.El
+.Sh NOTES
+.Xr apmconf 8
+has been merged in
+.Nm
+and thus
+.Nm
+replaces all of its functionality.
+.Sh SEE ALSO
+.Xr apm 4
+.Sh AUTHORS
+.An Tatsumi Hosokawa Aq hosokawa@jp.FreeBSD.org
+.Sh BUGS
+Some APM implementations do not support parameters needed by
+.Nm .
+On such systems,
+.Nm
+displays them as unknown.
+.Pp
+Some APM implementations cannot handle events such as pushing the
+power button or closing the cover.
+On such implementations, the system
+.Ar must
+be suspended
+.Ar only
+by using
+.Nm
+or
+.Nm zzz .
diff --git a/usr.sbin/apm/apm.c b/usr.sbin/apm/apm.c
new file mode 100644
index 0000000..56432a4
--- /dev/null
+++ b/usr.sbin/apm/apm.c
@@ -0,0 +1,502 @@
+/*
+ * apm / zzz APM BIOS utility for FreeBSD
+ *
+ * Copyright (C) 1994-1996 by Tatsumi Hosokawa <hosokawa@jp.FreeBSD.org>
+ *
+ * This software may be used, modified, copied, distributed, and sold,
+ * in both source and binary form provided that the above copyright and
+ * these terms are retained. Under no circumstances is the author
+ * responsible for the proper functioning of this software, nor does
+ * the author assume any responsibility for damages incurred with its
+ * use.
+ *
+ * Sep., 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD)
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+#include <machine/apm_bios.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#define APMDEV "/dev/apm"
+
+#define APM_UNKNOWN 255
+
+#define xh(a) (((a) & 0xff00) >> 8)
+#define xl(a) ((a) & 0xff)
+#define APMERR(a) xh(a)
+
+int cmos_wall = 0; /* True when wall time is in cmos clock, else UTC */
+
+static void
+usage(void)
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: apm [-ablstzZ] [-d enable ] [ -e enable ] "
+ "[ -h enable ] [-r delta]",
+ " zzz");
+ exit(1);
+}
+
+/*
+ * Return 1 for boolean true, and 0 for false, according to the
+ * interpretation of the string argument given.
+ */
+static int
+is_true(const char *boolean)
+{
+ char *endp;
+ long val;
+
+ val = strtoul(boolean, &endp, 0);
+ if (*endp == '\0')
+ return (val != 0 ? 1 : 0);
+ if (strcasecmp(boolean, "true") == 0 ||
+ strcasecmp(boolean, "yes") == 0 ||
+ strcasecmp(boolean, "enable") == 0)
+ return (1);
+ if (strcasecmp(boolean, "false") == 0 ||
+ strcasecmp(boolean, "no") == 0 ||
+ strcasecmp(boolean, "disable") == 0)
+ return (0);
+ /* Well, I have no idea what the user wants, so... */
+ warnx("invalid boolean argument \"%s\"", boolean);
+ usage();
+ /* NOTREACHED */
+
+ return (0);
+}
+
+static int
+int2bcd(int i)
+{
+ int retval = 0;
+ int base = 0;
+
+ if (i >= 10000)
+ return -1;
+
+ while (i) {
+ retval |= (i % 10) << base;
+ i /= 10;
+ base += 4;
+ }
+ return retval;
+}
+
+static int
+bcd2int(int bcd)
+{
+ int retval = 0;
+ int place = 1;
+
+ if (bcd > 0x9999)
+ return -1;
+
+ while (bcd) {
+ retval += (bcd & 0xf) * place;
+ bcd >>= 4;
+ place *= 10;
+ }
+ return retval;
+}
+
+static void
+apm_suspend(int fd)
+{
+ if (ioctl(fd, APMIO_SUSPEND, NULL) == -1)
+ err(1, "ioctl(APMIO_SUSPEND)");
+}
+
+static void
+apm_standby(int fd)
+{
+ if (ioctl(fd, APMIO_STANDBY, NULL) == -1)
+ err(1, "ioctl(APMIO_STANDBY)");
+}
+
+static void
+apm_getinfo(int fd, apm_info_t aip)
+{
+ if (ioctl(fd, APMIO_GETINFO, aip) == -1)
+ err(1, "ioctl(APMIO_GETINFO)");
+}
+
+static void
+apm_enable(int fd, int enable)
+{
+ if (enable) {
+ if (ioctl(fd, APMIO_ENABLE) == -1)
+ err(1, "ioctl(APMIO_ENABLE)");
+ } else {
+ if (ioctl(fd, APMIO_DISABLE) == -1)
+ err(1, "ioctl(APMIO_DISABLE)");
+ }
+}
+
+static void
+print_batt_time(int batt_time)
+{
+ printf("Remaining battery time: ");
+ if (batt_time == -1)
+ printf("unknown\n");
+ else {
+ int h, m, s;
+
+ h = batt_time;
+ s = h % 60;
+ h /= 60;
+ m = h % 60;
+ h /= 60;
+ printf("%2d:%02d:%02d\n", h, m, s);
+ }
+}
+
+static void
+print_batt_life(u_int batt_life)
+{
+ printf("Remaining battery life: ");
+ if (batt_life == APM_UNKNOWN)
+ printf("unknown\n");
+ else if (batt_life <= 100)
+ printf("%d%%\n", batt_life);
+ else
+ printf("invalid value (0x%x)\n", batt_life);
+}
+
+static void
+print_batt_stat(u_int batt_stat)
+{
+ const char *batt_msg[] = { "high", "low", "critical", "charging" };
+
+ printf("Battery Status: ");
+ if (batt_stat == APM_UNKNOWN)
+ printf("unknown\n");
+ else if (batt_stat > 3)
+ printf("invalid value (0x%x)\n", batt_stat);
+ else
+ printf("%s\n", batt_msg[batt_stat]);
+}
+
+static void
+print_all_info(int fd, apm_info_t aip, int bioscall_available)
+{
+ struct apm_bios_arg args;
+ int apmerr;
+ const char *line_msg[] = { "off-line", "on-line" , "backup power"};
+
+ printf("APM version: %d.%d\n", aip->ai_major, aip->ai_minor);
+ printf("APM Management: %s\n", aip->ai_status ? "Enabled" : "Disabled");
+ printf("AC Line status: ");
+ if (aip->ai_acline == APM_UNKNOWN)
+ printf("unknown\n");
+ else if (aip->ai_acline > 2)
+ printf("invalid value (0x%x)\n", aip->ai_acline);
+ else
+ printf("%s\n", line_msg[aip->ai_acline]);
+
+ print_batt_stat(aip->ai_batt_stat);
+ print_batt_life(aip->ai_batt_life);
+ print_batt_time(aip->ai_batt_time);
+
+ if (aip->ai_infoversion >= 1) {
+ printf("Number of batteries: ");
+ if (aip->ai_batteries == ~0U)
+ printf("unknown\n");
+ else {
+ u_int i;
+ struct apm_pwstatus aps;
+
+ printf("%d\n", aip->ai_batteries);
+ for (i = 0; i < aip->ai_batteries; ++i) {
+ bzero(&aps, sizeof(aps));
+ aps.ap_device = PMDV_BATT0 + i;
+ if (ioctl(fd, APMIO_GETPWSTATUS, &aps) == -1)
+ continue;
+ printf("Battery %d:\n", i);
+ if (aps.ap_batt_flag & APM_BATT_NOT_PRESENT) {
+ printf("not present\n");
+ continue;
+ }
+ printf("\t");
+ print_batt_stat(aps.ap_batt_stat);
+ printf("\t");
+ print_batt_life(aps.ap_batt_life);
+ printf("\t");
+ print_batt_time(aps.ap_batt_time);
+ }
+ }
+ }
+
+ if (bioscall_available) {
+ /*
+ * try to get the suspend timer
+ */
+ bzero(&args, sizeof(args));
+ args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER;
+ args.ebx = PMDV_APMBIOS;
+ args.ecx = 0x0001;
+ if (ioctl(fd, APMIO_BIOS, &args)) {
+ printf("Resume timer: unknown\n");
+ } else {
+ apmerr = APMERR(args.eax);
+ if (apmerr == 0x0d || apmerr == 0x86)
+ printf("Resume timer: disabled\n");
+ else if (apmerr)
+ warnx(
+ "failed to get the resume timer: APM error0x%x", apmerr);
+ else {
+ /*
+ * OK. We have the time (all bcd).
+ * CH - seconds
+ * DH - hours
+ * DL - minutes
+ * xh(SI) - month (1-12)
+ * xl(SI) - day of month (1-31)
+ * DI - year
+ */
+ struct tm tm;
+ char buf[1024];
+ time_t t;
+
+ tm.tm_sec = bcd2int(xh(args.ecx));
+ tm.tm_min = bcd2int(xl(args.edx));
+ tm.tm_hour = bcd2int(xh(args.edx));
+ tm.tm_mday = bcd2int(xl(args.esi));
+ tm.tm_mon = bcd2int(xh(args.esi)) - 1;
+ tm.tm_year = bcd2int(args.edi) - 1900;
+ if (cmos_wall)
+ t = mktime(&tm);
+ else
+ t = timegm(&tm);
+ if (t != -1) {
+ tm = *localtime(&t);
+ strftime(buf, sizeof(buf), "%c", &tm);
+ printf("Resume timer: %s\n", buf);
+ } else
+ printf("Resume timer: unknown\n");
+ }
+ }
+
+ /*
+ * Get the ring indicator resume state
+ */
+ bzero(&args, sizeof(args));
+ args.eax = (APM_BIOS) << 8 | APM_RESUMEONRING;
+ args.ebx = PMDV_APMBIOS;
+ args.ecx = 0x0002;
+ if (ioctl(fd, APMIO_BIOS, &args) == 0) {
+ printf("Resume on ring indicator: %sabled\n",
+ args.ecx ? "en" : "dis");
+ }
+ }
+
+ if (aip->ai_infoversion >= 1) {
+ if (aip->ai_capabilities == 0xff00)
+ return;
+ printf("APM Capabilities:\n");
+ if (aip->ai_capabilities & 0x01)
+ printf("\tglobal standby state\n");
+ if (aip->ai_capabilities & 0x02)
+ printf("\tglobal suspend state\n");
+ if (aip->ai_capabilities & 0x04)
+ printf("\tresume timer from standby\n");
+ if (aip->ai_capabilities & 0x08)
+ printf("\tresume timer from suspend\n");
+ if (aip->ai_capabilities & 0x10)
+ printf("\tRI resume from standby\n");
+ if (aip->ai_capabilities & 0x20)
+ printf("\tRI resume from suspend\n");
+ if (aip->ai_capabilities & 0x40)
+ printf("\tPCMCIA RI resume from standby\n");
+ if (aip->ai_capabilities & 0x80)
+ printf("\tPCMCIA RI resume from suspend\n");
+ }
+
+}
+
+/*
+ * currently, it can turn off the display, but the display never comes
+ * back until the machine suspend/resumes :-).
+ */
+static void
+apm_display(int fd, int newstate)
+{
+ if (ioctl(fd, APMIO_DISPLAY, &newstate) == -1)
+ err(1, "ioctl(APMIO_DISPLAY)");
+}
+
+static void
+apm_haltcpu(int fd, int enable)
+{
+ if (enable) {
+ if (ioctl(fd, APMIO_HALTCPU, NULL) == -1)
+ err(1, "ioctl(APMIO_HALTCPU)");
+ } else {
+ if (ioctl(fd, APMIO_NOTHALTCPU, NULL) == -1)
+ err(1, "ioctl(APMIO_NOTHALTCPU)");
+ }
+}
+
+static void
+apm_set_timer(int fd, int delta)
+{
+ time_t tmr;
+ struct tm *tm;
+ struct apm_bios_arg args;
+
+ tmr = time(NULL) + delta;
+ if (cmos_wall)
+ tm = localtime(&tmr);
+ else
+ tm = gmtime(&tmr);
+ bzero(&args, sizeof(args));
+ args.eax = (APM_BIOS) << 8 | APM_RESUMETIMER;
+ args.ebx = PMDV_APMBIOS;
+ if (delta > 0) {
+ args.ecx = (int2bcd(tm->tm_sec) << 8) | 0x02;
+ args.edx = (int2bcd(tm->tm_hour) << 8) | int2bcd(tm->tm_min);
+ args.esi = (int2bcd(tm->tm_mon + 1) << 8) | int2bcd(tm->tm_mday);
+ args.edi = int2bcd(tm->tm_year + 1900);
+ } else {
+ args.ecx = 0x0000;
+ }
+ if (ioctl(fd, APMIO_BIOS, &args)) {
+ err(1,"set resume timer");
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c, fd;
+ int dosleep = 0, all_info = 1, apm_status = 0, batt_status = 0;
+ int display = -1, batt_life = 0, ac_status = 0, standby = 0;
+ int batt_time = 0, delta = 0, enable = -1, haltcpu = -1;
+ char *cmdname;
+ int bioscall_available = 0;
+ size_t cmos_wall_len = sizeof(cmos_wall);
+
+ if (sysctlbyname("machdep.wall_cmos_clock", &cmos_wall, &cmos_wall_len,
+ NULL, 0) == -1)
+ err(1, "sysctlbyname(machdep.wall_cmos_clock)");
+ if ((cmdname = strrchr(argv[0], '/')) != NULL)
+ cmdname++;
+ else
+ cmdname = argv[0];
+
+ if (strcmp(cmdname, "zzz") == 0) {
+ dosleep = 1;
+ all_info = 0;
+ goto finish_option;
+ }
+ while ((c = getopt(argc, argv, "abe:h:lRr:stzd:Z")) != -1) {
+ switch (c) {
+ case 'a':
+ ac_status = 1;
+ all_info = 0;
+ break;
+ case 'b':
+ batt_status = 1;
+ all_info = 0;
+ break;
+ case 'd':
+ display = is_true(optarg);
+ all_info = 0;
+ break;
+ case 'l':
+ batt_life = 1;
+ all_info = 0;
+ break;
+ case 'R':
+ delta = -1;
+ break;
+ case 'r':
+ delta = atoi(optarg);
+ break;
+ case 's':
+ apm_status = 1;
+ all_info = 0;
+ break;
+ case 'e':
+ enable = is_true(optarg);
+ all_info = 0;
+ break;
+ case 'h':
+ haltcpu = is_true(optarg);
+ all_info = 0;
+ break;
+ case 't':
+ batt_time = 1;
+ all_info = 0;
+ break;
+ case 'z':
+ dosleep = 1;
+ all_info = 0;
+ break;
+ case 'Z':
+ standby = 1;
+ all_info = 0;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+ }
+finish_option:
+ if (haltcpu != -1 || enable != -1 || display != -1 || delta || dosleep
+ || standby) {
+ fd = open(APMDEV, O_RDWR);
+ bioscall_available = 1;
+ } else if ((fd = open(APMDEV, O_RDWR)) >= 0)
+ bioscall_available = 1;
+ else
+ fd = open(APMDEV, O_RDONLY);
+ if (fd == -1)
+ err(1, "can't open %s", APMDEV);
+ if (enable != -1)
+ apm_enable(fd, enable);
+ if (haltcpu != -1)
+ apm_haltcpu(fd, haltcpu);
+ if (delta)
+ apm_set_timer(fd, delta);
+ if (dosleep)
+ apm_suspend(fd);
+ else if (standby)
+ apm_standby(fd);
+ else if (delta == 0) {
+ struct apm_info info;
+
+ apm_getinfo(fd, &info);
+ if (all_info)
+ print_all_info(fd, &info, bioscall_available);
+ if (ac_status)
+ printf("%d\n", info.ai_acline);
+ if (batt_status)
+ printf("%d\n", info.ai_batt_stat);
+ if (batt_life)
+ printf("%d\n", info.ai_batt_life);
+ if (apm_status)
+ printf("%d\n", info.ai_status);
+ if (batt_time)
+ printf("%d\n", info.ai_batt_time);
+ if (display != -1)
+ apm_display(fd, display);
+ }
+ close(fd);
+ exit(0);
+}
diff --git a/usr.sbin/apmd/Makefile b/usr.sbin/apmd/Makefile
new file mode 100644
index 0000000..7e81b35
--- /dev/null
+++ b/usr.sbin/apmd/Makefile
@@ -0,0 +1,21 @@
+# $FreeBSD$
+
+PROG= apmd
+MAN= apmd.8
+MANSUBDIR= /i386
+SRCS= apmd.c apmdlex.l apmdparse.y y.tab.h
+
+DPADD= ${LIBL}
+LDADD= -ll
+
+YFLAGS+=-v
+CFLAGS+=-I. -I${.CURDIR} #-DYY_STACK_USED
+# for debug:
+#CFLAGS+= -g -DDEBUG
+
+CLEANFILES= y.output
+
+test:
+ ./apmd -d -f etc/apmd.conf -n
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/apmd/README b/usr.sbin/apmd/README
new file mode 100644
index 0000000..59a56e3
--- /dev/null
+++ b/usr.sbin/apmd/README
@@ -0,0 +1,213 @@
+FreeBSD apmd Package Release Notes (19990711 version)
+
+1. What is "apmd"?
+==================
+
+The apmd package provides a means of handling various APM events from
+userland code. Using apmd.conf, the apmd(8) configuration file, you
+can select the APM events to be handled from userland and specify the
+commands for a given event, allowing APM behaviour to be configured
+flexibly.
+
+
+2. How to install the apmd package
+==================================
+
+2.1 Making the apmd control device file
+---------------------------------------
+
+apmd(8) uses the new special device file /dev/apmctl. This should be
+created as follows:
+
+# cd /dev
+# mknod apmctl c 39 8
+
+2.2 Applying the kernel patch and building a new kernel
+-------------------------------------------------------
+
+The next step is to apply the patch against the sys source tree.
+Go to the source directory (eg. /usr/src/ or /usr/PAO3/src/) and run
+the patch command as follows:
+
+# gzip -cd [somewhere]/apmd-sys-R320.diff | patch
+
+For PAO3 users, the patch file name would be apmd-sys-PAO3.diff
+instead of apmd-sys-R320.diff. After this step has completed
+successfully, build and install a new kernel and reboot your system.
+
+2.3 Making the apmd program
+---------------------------
+
+Go to src/usr.sbin/ and extract the apmd tarball as follows:
+
+# tar xzpvf [somewhere]/apmd-usr.sbin.tar.gz
+
+Before doing a make all, you need to copy apm_bios.h in the sys source
+tree to /usr/include/machine/ first:
+
+# cp /sys/i386/include/apm_bios.h /usr/include/machine/
+
+Then do the build and install steps in the apmd directory:
+
+# cd src/usr.sbin/apmd
+# make depend all install
+
+2.4 Setting up the configuration file and userland script
+---------------------------------------------------------
+
+In src/usr.sbin/apm/etc/ there are example configuration and userland
+script files which are invoked automatically when the APM BIOS informs
+apmd of an event, such as suspend request. Copy these files to
+/etc/ as follows:
+
+# cp src/usr.sbin/apm/etc/* /etc/
+
+
+3. Running the apmd daemon program
+==================================
+
+To run apmd(8) in background mode, simply type ``apmd''.
+
+# apmd
+
+To make a running apmd reload /etc/apmd.conf, send a SIGHUP signal to
+the apmd(8) process.
+
+# kill -HUP [apmd pid]
+or
+# killall -HUP apmd
+
+apmd has some command line options. For the details, please
+refer to the manpage of apmd.
+
+4. Configuration file
+=====================
+
+The structure of the apmd configuration file is quite simple. For
+example:
+
+apm_event SUSPENDREQ {
+ exec "sync && sync && sync";
+ exec "sleep 1";
+ exec "zzz";
+}
+
+Will cause apmd to recieve the APM event SUSPENDREQ (which may be
+posted by an LCD close), run the sync command 3 times and wait for a
+while, then execute zzz (apm -z) to put the system in the suspend
+state.
+
+4.1 The apm_event keyword
+-------------------------
+`apm_event' is the keyword which indicates the start of configuration for
+each events.
+
+4.2 APM events
+--------------
+
+If you wish to execute the same commands for different events, the
+event names should be delimited by a comma. The following are valid
+event names:
+
+o Events ignored by the kernel if apmd is running:
+
+STANDBYREQ
+SUSPENDREQ
+USERSUSPENDREQ
+BATTERYLOW
+
+o Events passed to apmd after kernel handling:
+
+NORMRESUME
+CRITRESUME
+STANDBYRESUME
+POWERSTATECHANGE
+UPDATETIME
+
+
+Other events will not be sent to apmd.
+
+4.3 command line syntax
+-----------------------
+
+In the example above, the three lines begining with `exec' are commands
+for the event. Each line should be terminated with a semicolon. The
+command list for the event should be enclosed by `{' and `}'. apmd(8)
+uses /bin/sh for double-quotation enclosed command execution, just as
+with system(3). Each command is executed in order until the end of
+the list is reached or a command finishes with a non-zero status code.
+apmd(8) will report any failed command's status code via syslog(3)
+and will then reject the request event posted by APM BIOS.
+
+4.4 Built-in functions
+----------------------
+
+You can also specify apmd built-in functions instead of command lines.
+A built-in function name should be terminated with a semicolon, just as
+with a command line.
+The following built-in functions are currently supported:
+
+o reject;
+
+ Reject last request posted by the APM BIOS. This can be used to reject a
+ SUSPEND request when the LCD is closed and put the system in a STANDBY
+ state instead.
+
+
+
+5. EXAMPLES
+===========
+
+Sample configuration commands include:
+
+apm_event SUSPENDREQ {
+ exec "/etc/rc.suspend";
+}
+
+apm_event USERSUSPENDREQ {
+ exec "sync && sync && sync";
+ exec "sleep 1";
+ exec "apm -z";
+}
+
+apm_event NORMRESUME, STANDBYRESUME {
+ exec "/etc/rc.resume";
+}
+
+# resume event configuration for serial mouse users by
+# reinitializing a moused(8) connected to a serial port.
+#
+#apm_event NORMRESUME {
+# exec "kill -HUP `cat /var/run/moused.pid`";
+#}
+
+# suspend request event configuration for ATA HDD users:
+# execute standby instead of suspend.
+#
+#apm_event SUSPENDREQ {
+# reject;
+# exec "sync && sync && sync";
+# exec "sleep 1";
+# exec "apm -Z";
+#}
+
+
+6. Call for developers
+======================
+
+The initial version of apmd(8) was implemented primarily to test the
+kernel support code and was ALPHA quality. Based on that code, the
+current version was developed by KOIE Hidetaka <hide@koie.org>.
+However, we're still looking around for interesting new features and
+ideas, so if you have any thoughts, please let us know.
+Documentation is also sparse, and the manpage have just written.
+If you wish to collaborate on this work, please e-mail me:
+iwasaki@freebsd.org.
+
+
+June 1, 1999
+Created by: iwasaki@FreeBSD.org
+Edited by: jkh@FreeBSD.org
+ nick@foobar.org
+
+$FreeBSD$
diff --git a/usr.sbin/apmd/apmd.8 b/usr.sbin/apmd/apmd.8
new file mode 100644
index 0000000..f94ea5d
--- /dev/null
+++ b/usr.sbin/apmd/apmd.8
@@ -0,0 +1,322 @@
+.\" Copyright (c) 1999 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+.\" Copyright (c) 1999 KOIE Hidetaka <koie@suri.co.jp>
+.\" Copyright (c) 1999 Yoshihiko SARUMARU Aq <mistral@imasy.or.jp>
+.\" Copyright (c) 1999 Norihiro Kumagai <kuma@nk.rim.or.jp>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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.
+.\"
+.\" @(#)apmd.8 1.1 (FreeBSD) 6/28/99
+.\" $FreeBSD$
+.\"
+.Dd June 28, 1999
+.Dt APMD 8 i386
+.Os
+.Sh NAME
+.Nm apmd
+.Nd Advanced Power Management monitor daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl f file
+.Op Fl s
+.Op Fl v
+.Sh DESCRIPTION
+The
+.Nm
+utility
+monitors the occurrence of the specified Advanced Power Management
+.Pq Tn APM
+events and, if one of the events occurs, it executes the sequence of
+commands corresponding to the event.
+Only the events specified in the
+configuration file are notified to
+.Nm ;
+all other events are ignored.
+For each event posted by the APM BIOS,
+.Nm
+invokes the sequence of commands specified in the configuration file.
+When
+.Nm
+is running with monitoring suspend/standby requests,
+the kernel will not process those requests.
+Therefore, if you wish action to be taken when these events
+occur, you need to explicitly configure the appropriate commands or
+built-in functions in the configuration file.
+.Pp
+The
+.Nm
+utility recognizes the following runtime options:
+.Bl -tag -width -f_file
+.It Fl d
+Starts in debug mode.
+This causes
+.Nm
+to execute in the foreground instead of in daemon mode.
+.It Fl f Ar file
+Specifies a different configuration file
+.Ar file
+to be used in place of the default
+.Pa /etc/apmd.conf .
+.It Fl s
+Causes
+.Nm
+to simulate a POWERSTATECHANGE event when a power state change is detected
+(AC_POWER_STATE) but the bios of the laptop does not report it.
+This enables you to do things like dimming the LCD backlight when you unplug
+the power cord.
+.It Fl v
+Verbose mode.
+.El
+.Pp
+When
+.Nm
+starts, it reads the configuration file
+.Pa ( /etc/apmd.conf
+as default)
+and notifies the set of events to be monitored to the APM device driver.
+When it terminates, the APM device driver automatically cancels
+monitored events.
+.Pp
+If the
+.Nm
+process receives a SIGHUP, it will reread its configuration file and
+notify the APM device driver of any changes to its configuration.
+.Pp
+The
+.Nm
+utility uses the device
+.Pa /dev/apmctl
+to issue
+.Xr ioctl 2
+requests for monitoring events and for controlling the APM system.
+This device file is opened exclusively, so only a single
+.Nm
+process can be running at any time.
+.Pp
+When
+.Nm
+receives an APM event, it forks a child process to execute the
+commands specified in the configuration file and then continues
+listening for more events.
+The child process executes the commands
+specified, one at a time and in the order that they are listed.
+.Pp
+While
+.Nm
+is processing the command list for SUSPEND/STANDBY requests, the APM kernel
+device driver issues notifications to APM BIOS once per second so that the
+BIOS knows that there are still some commands pending, and that it should not
+complete the request just yet.
+.Pp
+The
+.Nm
+utility creates the file
+.Pa /var/run/apmd.pid ,
+and stores its process
+id there.
+This can be used to kill or reconfigure
+.Nm .
+.Sh CONFIGURATION FILE
+The structure of the
+.Nm
+configuration file is quite simple.
+For example:
+.Pp
+.Bd -literal
+apm_event SUSPENDREQ {
+ exec "sync && sync && sync";
+ exec "sleep 1";
+ exec "zzz";
+}
+.Ed
+.Pp
+will cause
+.Nm
+to receive the APM event
+.Ql SUSPENDREQ
+(which may be posted by an LCD close), run the
+.Ql sync
+command 3 times and wait for a while, then execute
+.Nm zzz ( Ns Nm apm Fl z )
+to put the system in the suspend state.
+.Pp
+.Bl -bullet
+.It
+The apm_event keyword
+.Bd -ragged -offset indent
+.Ql apm_event
+is the keyword which indicates the start of configuration for
+each events.
+.Ed
+.It
+APM events
+.Bd -ragged -offset indent
+If you wish to execute the same commands for different events, the
+event names should be delimited by a comma.
+The following are
+valid event names:
+.Bl -item
+.It
+- Events ignored by the kernel if
+.Nm
+is running:
+.Pp
+.Bl -tag -width USERSUSPENDREQ -compact -offset indent
+.It STANDBYREQ
+.It USERSTANDBYREQ
+.It SUSPENDREQ
+should include sync in the command list,
+.It USERSUSPENDREQ
+should include sync in the command list,
+.It BATTERYLOW
+only zzz should be specified in the command list.
+.El
+.It
+- Events passed to
+.Nm
+after kernel handling:
+.Pp
+.Bl -tag -width USERSUSPENDREQ -compact -offset indent
+.It NORMRESUME
+.It CRITRESUME
+.It STANDBYRESUME
+.It POWERSTATECHANGE
+.It UPDATETIME
+.It CAPABILITIESCHANGE
+.El
+.Pp
+Other events will not be sent to
+.Nm .
+.El
+.Ed
+.It
+command line syntax
+.Bd -ragged -offset indent
+In the example above, the three lines beginning with
+.Ql exec
+are commands for the event.
+Each line should be terminated with a semicolon.
+The command list for the event should be enclosed by
+.Ql {
+and
+.Ql } .
+The
+.Nm
+utility uses
+.Pa /bin/sh
+for double-quotation enclosed command execution, just as with
+.Xr system 3 .
+Each command is executed in order until the end of
+the list is reached or a command finishes with a non-zero status code.
+The
+.Nm
+utility will report any failed command's status code via
+.Xr syslog 3
+and will then reject the request event posted by the APM BIOS.
+.Ed
+.It
+Built-in functions
+.Bd -ragged -offset indent
+You can also specify
+.Nm
+built-in functions instead of command lines.
+A built-in function name should be terminated with a semicolon,
+just as with a command line.
+The following built-in functions are currently supported:
+.Bl -item
+.It
+- reject:
+.Bd -ragged -offset indent
+Reject last request posted by APM BIOS.
+This can be used to reject
+a SUSPEND request when the LCD is closed and put the system in a
+STANDBY state instead.
+.Ed
+.El
+.Ed
+.El
+.Sh FILES
+.Bl -tag -width /etc/apmd.conf -compact
+.It Pa /etc/apmd.conf
+.It Pa /dev/apmctl
+.It Pa /var/run/apmd.pid
+.El
+.Sh EXAMPLES
+Sample configuration commands include:
+.Bd -literal
+apm_event SUSPENDREQ {
+ exec "/etc/rc.suspend apm suspend";
+}
+
+apm_event USERSUSPENDREQ {
+ exec "sync && sync && sync";
+ exec "sleep 1";
+ exec "apm -z";
+}
+
+apm_event NORMRESUME {
+ exec "/etc/rc.resume apm suspend";
+}
+
+apm_event STANDBYRESUME {
+ exec "/etc/rc.resume apm standby";
+}
+
+# resume event configuration for serial mouse users by
+# reinitializing a moused(8) connected to a serial port.
+#
+#apm_event NORMRESUME {
+# exec "kill -HUP `cat /var/run/moused.pid`";
+#}
+#
+# suspend request event configuration for ATA HDD users:
+# execute standby instead of suspend.
+#
+#apm_event SUSPENDREQ {
+# reject;
+# exec "sync && sync && sync";
+# exec "sleep 1";
+# exec "apm -Z";
+#}
+.Ed
+.Sh SEE ALSO
+.Xr apm 4 ,
+.Xr apm 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 3.3 .
+.Sh AUTHORS
+.An Mitsuru IWASAKI Aq iwasaki@FreeBSD.org
+.An KOIE Hidetaka Aq koie@suri.co.jp
+.Pp
+.An -nosplit
+Some contributions made by
+.An Warner Losh Aq imp@FreeBSD.org ,
+.An Hiroshi Yamashita Aq bluemoon@msj.biglobe.ne.jp ,
+.An Yoshihiko SARUMARU Aq mistral@imasy.or.jp ,
+.An Norihiro Kumagai Aq kuma@nk.rim.or.jp ,
+.An NAKAGAWA Yoshihisa Aq nakagawa@jp.FreeBSD.org ,
+and
+.An Nick Hilliard Aq nick@foobar.org .
diff --git a/usr.sbin/apmd/apmd.c b/usr.sbin/apmd/apmd.c
new file mode 100644
index 0000000..67351f8
--- /dev/null
+++ b/usr.sbin/apmd/apmd.c
@@ -0,0 +1,714 @@
+/*-
+ * APM (Advanced Power Management) Event Dispatcher
+ *
+ * Copyright (c) 1999 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * Copyright (c) 1999 KOIE Hidetaka <koie@suri.co.jp>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <assert.h>
+#include <bitstring.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <machine/apm_bios.h>
+
+#include "apmd.h"
+
+extern int yyparse(void);
+
+int debug_level = 0;
+int verbose = 0;
+int soft_power_state_change = 0;
+const char *apmd_configfile = APMD_CONFIGFILE;
+const char *apmd_pidfile = APMD_PIDFILE;
+int apmctl_fd = -1, apmnorm_fd = -1;
+
+/*
+ * table of event handlers
+ */
+#define EVENT_CONFIG_INITIALIZER(EV,R) { #EV, NULL, R },
+struct event_config events[EVENT_MAX] = {
+ EVENT_CONFIG_INITIALIZER(NOEVENT, 0)
+ EVENT_CONFIG_INITIALIZER(STANDBYREQ, 1)
+ EVENT_CONFIG_INITIALIZER(SUSPENDREQ, 1)
+ EVENT_CONFIG_INITIALIZER(NORMRESUME, 0)
+ EVENT_CONFIG_INITIALIZER(CRITRESUME, 0)
+ EVENT_CONFIG_INITIALIZER(BATTERYLOW, 0)
+ EVENT_CONFIG_INITIALIZER(POWERSTATECHANGE, 0)
+ EVENT_CONFIG_INITIALIZER(UPDATETIME, 0)
+ EVENT_CONFIG_INITIALIZER(CRITSUSPEND, 1)
+ EVENT_CONFIG_INITIALIZER(USERSTANDBYREQ, 1)
+ EVENT_CONFIG_INITIALIZER(USERSUSPENDREQ, 1)
+ EVENT_CONFIG_INITIALIZER(STANDBYRESUME, 0)
+ EVENT_CONFIG_INITIALIZER(CAPABILITIESCHANGE, 0)
+};
+
+/*
+ * List of battery events
+ */
+struct battery_watch_event *battery_watch_list = NULL;
+
+#define BATT_CHK_INTV 10 /* how many seconds between battery state checks? */
+
+/*
+ * default procedure
+ */
+struct event_cmd *
+event_cmd_default_clone(void *this)
+{
+ struct event_cmd * oldone = this;
+ struct event_cmd * newone = malloc(oldone->len);
+
+ newone->next = NULL;
+ newone->len = oldone->len;
+ newone->name = oldone->name;
+ newone->op = oldone->op;
+ return newone;
+}
+
+/*
+ * exec command
+ */
+int
+event_cmd_exec_act(void *this)
+{
+ struct event_cmd_exec * p = this;
+ int status = -1;
+ pid_t pid;
+
+ switch ((pid = fork())) {
+ case -1:
+ (void) warn("cannot fork");
+ goto out;
+ case 0:
+ /* child process */
+ signal(SIGHUP, SIG_DFL);
+ signal(SIGCHLD, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ execl(_PATH_BSHELL, "sh", "-c", p->line, (char *)NULL);
+ _exit(127);
+ default:
+ /* parent process */
+ do {
+ pid = waitpid(pid, &status, 0);
+ } while (pid == -1 && errno == EINTR);
+ break;
+ }
+ out:
+ return status;
+}
+void
+event_cmd_exec_dump(void *this, FILE *fp)
+{
+ fprintf(fp, " \"%s\"", ((struct event_cmd_exec *)this)->line);
+}
+struct event_cmd *
+event_cmd_exec_clone(void *this)
+{
+ struct event_cmd_exec * newone = (struct event_cmd_exec *) event_cmd_default_clone(this);
+ struct event_cmd_exec * oldone = this;
+
+ newone->evcmd.next = NULL;
+ newone->evcmd.len = oldone->evcmd.len;
+ newone->evcmd.name = oldone->evcmd.name;
+ newone->evcmd.op = oldone->evcmd.op;
+ if ((newone->line = strdup(oldone->line)) == NULL)
+ err(1, "out of memory");
+ return (struct event_cmd *) newone;
+}
+void
+event_cmd_exec_free(void *this)
+{
+ free(((struct event_cmd_exec *)this)->line);
+}
+struct event_cmd_op event_cmd_exec_ops = {
+ event_cmd_exec_act,
+ event_cmd_exec_dump,
+ event_cmd_exec_clone,
+ event_cmd_exec_free
+};
+
+/*
+ * reject commad
+ */
+int
+event_cmd_reject_act(void *this)
+{
+ int rc = -1;
+
+ if (ioctl(apmctl_fd, APMIO_REJECTLASTREQ, NULL)) {
+ syslog(LOG_NOTICE, "fail to reject\n");
+ goto out;
+ }
+ rc = 0;
+ out:
+ return rc;
+}
+struct event_cmd_op event_cmd_reject_ops = {
+ event_cmd_reject_act,
+ NULL,
+ event_cmd_default_clone,
+ NULL
+};
+
+/*
+ * manipulate event_config
+ */
+struct event_cmd *
+clone_event_cmd_list(struct event_cmd *p)
+{
+ struct event_cmd dummy;
+ struct event_cmd *q = &dummy;
+ for ( ;p; p = p->next) {
+ assert(p->op->clone);
+ if ((q->next = p->op->clone(p)) == NULL)
+ (void) err(1, "out of memory");
+ q = q->next;
+ }
+ q->next = NULL;
+ return dummy.next;
+}
+void
+free_event_cmd_list(struct event_cmd *p)
+{
+ struct event_cmd * q;
+ for ( ; p ; p = q) {
+ q = p->next;
+ if (p->op->free)
+ p->op->free(p);
+ free(p);
+ }
+}
+int
+register_battery_handlers(
+ int level, int direction,
+ struct event_cmd *cmdlist)
+{
+ /*
+ * level is negative if it's in "minutes", non-negative if
+ * percentage.
+ *
+ * direction =1 means we care about this level when charging,
+ * direction =-1 means we care about it when discharging.
+ */
+ if (level>100) /* percentage > 100 */
+ return -1;
+ if (abs(direction) != 1) /* nonsense direction value */
+ return -1;
+
+ if (cmdlist) {
+ struct battery_watch_event *we;
+
+ if ((we = malloc(sizeof(struct battery_watch_event))) == NULL)
+ (void) err(1, "out of memory");
+
+ we->next = battery_watch_list; /* starts at NULL */
+ battery_watch_list = we;
+ we->level = abs(level);
+ we->type = (level<0)?BATTERY_MINUTES:BATTERY_PERCENT;
+ we->direction = (direction<0)?BATTERY_DISCHARGING:
+ BATTERY_CHARGING;
+ we->done = 0;
+ we->cmdlist = clone_event_cmd_list(cmdlist);
+ }
+ return 0;
+}
+int
+register_apm_event_handlers(
+ bitstr_t bit_decl(evlist, EVENT_MAX),
+ struct event_cmd *cmdlist)
+{
+ if (cmdlist) {
+ bitstr_t bit_decl(tmp, EVENT_MAX);
+ memcpy(&tmp, evlist, bitstr_size(EVENT_MAX));
+
+ for (;;) {
+ int n;
+ struct event_cmd *p;
+ struct event_cmd *q;
+ bit_ffs(tmp, EVENT_MAX, &n);
+ if (n < 0)
+ break;
+ p = events[n].cmdlist;
+ if ((q = clone_event_cmd_list(cmdlist)) == NULL)
+ (void) err(1, "out of memory");
+ if (p) {
+ while (p->next != NULL)
+ p = p->next;
+ p->next = q;
+ } else {
+ events[n].cmdlist = q;
+ }
+ bit_clear(tmp, n);
+ }
+ }
+ return 0;
+}
+
+/*
+ * execute command
+ */
+int
+exec_run_cmd(struct event_cmd *p)
+{
+ int status = 0;
+
+ for (; p; p = p->next) {
+ assert(p->op->act);
+ if (verbose)
+ syslog(LOG_INFO, "action: %s", p->name);
+ status = p->op->act(p);
+ if (status) {
+ syslog(LOG_NOTICE, "command finished with %d\n", status);
+ break;
+ }
+ }
+ return status;
+}
+
+/*
+ * execute command -- the event version
+ */
+int
+exec_event_cmd(struct event_config *ev)
+{
+ int status = 0;
+
+ status = exec_run_cmd(ev->cmdlist);
+ if (status && ev->rejectable) {
+ syslog(LOG_ERR, "canceled");
+ (void) event_cmd_reject_act(NULL);
+ }
+ return status;
+}
+
+/*
+ * read config file
+ */
+extern FILE * yyin;
+extern int yydebug;
+
+void
+read_config(void)
+{
+ int i;
+
+ if ((yyin = fopen(apmd_configfile, "r")) == NULL) {
+ (void) err(1, "cannot open config file");
+ }
+
+#ifdef DEBUG
+ yydebug = debug_level;
+#endif
+
+ if (yyparse() != 0)
+ (void) err(1, "cannot parse config file");
+
+ fclose(yyin);
+
+ /* enable events */
+ for (i = 0; i < EVENT_MAX; i++) {
+ if (events[i].cmdlist) {
+ u_int event_type = i;
+ if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) {
+ (void) err(1, "cannot enable event 0x%x", event_type);
+ }
+ }
+ }
+}
+
+void
+dump_config()
+{
+ int i;
+ struct battery_watch_event *q;
+
+ for (i = 0; i < EVENT_MAX; i++) {
+ struct event_cmd * p;
+ if ((p = events[i].cmdlist)) {
+ fprintf(stderr, "apm_event %s {\n", events[i].name);
+ for ( ; p ; p = p->next) {
+ fprintf(stderr, "\t%s", p->name);
+ if (p->op->dump)
+ p->op->dump(p, stderr);
+ fprintf(stderr, ";\n");
+ }
+ fprintf(stderr, "}\n");
+ }
+ }
+ for (q = battery_watch_list ; q != NULL ; q = q -> next) {
+ struct event_cmd * p;
+ fprintf(stderr, "apm_battery %d%s %s {\n",
+ q -> level,
+ (q -> type == BATTERY_PERCENT)?"%":"m",
+ (q -> direction == BATTERY_CHARGING)?"charging":
+ "discharging");
+ for ( p = q -> cmdlist; p ; p = p->next) {
+ fprintf(stderr, "\t%s", p->name);
+ if (p->op->dump)
+ p->op->dump(p, stderr);
+ fprintf(stderr, ";\n");
+ }
+ fprintf(stderr, "}\n");
+ }
+}
+
+void
+destroy_config()
+{
+ int i;
+ struct battery_watch_event *q;
+
+ /* disable events */
+ for (i = 0; i < EVENT_MAX; i++) {
+ if (events[i].cmdlist) {
+ u_int event_type = i;
+ if (write(apmctl_fd, &event_type, sizeof(u_int)) == -1) {
+ (void) err(1, "cannot disable event 0x%x", event_type);
+ }
+ }
+ }
+
+ for (i = 0; i < EVENT_MAX; i++) {
+ struct event_cmd * p;
+ if ((p = events[i].cmdlist))
+ free_event_cmd_list(p);
+ events[i].cmdlist = NULL;
+ }
+
+ for( ; battery_watch_list; battery_watch_list = battery_watch_list -> next) {
+ free_event_cmd_list(battery_watch_list->cmdlist);
+ q = battery_watch_list->next;
+ free(battery_watch_list);
+ battery_watch_list = q;
+ }
+}
+
+void
+restart()
+{
+ destroy_config();
+ read_config();
+ if (verbose)
+ dump_config();
+}
+
+/*
+ * write pid file
+ */
+static void
+write_pid()
+{
+ FILE *fp = fopen(apmd_pidfile, "w");
+
+ if (fp) {
+ fprintf(fp, "%d\n", getpid());
+ fclose(fp);
+ }
+}
+
+/*
+ * handle signals
+ */
+static int signal_fd[2];
+
+void
+enque_signal(int sig)
+{
+ if (write(signal_fd[1], &sig, sizeof sig) != sizeof sig)
+ (void) err(1, "cannot process signal.");
+}
+
+void
+wait_child()
+{
+ int status;
+ while (waitpid(-1, &status, WNOHANG) > 0)
+ ;
+}
+
+int
+proc_signal(int fd)
+{
+ int rc = -1;
+ int sig;
+
+ while (read(fd, &sig, sizeof sig) == sizeof sig) {
+ syslog(LOG_INFO, "caught signal: %d", sig);
+ switch (sig) {
+ case SIGHUP:
+ syslog(LOG_NOTICE, "restart by SIG");
+ restart();
+ break;
+ case SIGTERM:
+ syslog(LOG_NOTICE, "going down on signal %d", sig);
+ rc = -1;
+ goto out;
+ case SIGCHLD:
+ wait_child();
+ break;
+ default:
+ (void) warn("unexpected signal(%d) received.", sig);
+ break;
+ }
+ }
+ rc = 0;
+ out:
+ return rc;
+}
+void
+proc_apmevent(int fd)
+{
+ struct apm_event_info apmevent;
+
+ while (ioctl(fd, APMIO_NEXTEVENT, &apmevent) == 0) {
+ int status;
+ syslog(LOG_NOTICE, "apmevent %04x index %d\n",
+ apmevent.type, apmevent.index);
+ syslog(LOG_INFO, "apm event: %s", events[apmevent.type].name);
+ if (fork() == 0) {
+ status = exec_event_cmd(&events[apmevent.type]);
+ exit(status);
+ }
+ }
+}
+
+#define AC_POWER_STATE ((pw_info.ai_acline == 1) ? BATTERY_CHARGING :\
+ BATTERY_DISCHARGING)
+
+void
+check_battery()
+{
+
+ static int first_time=1, last_state;
+ int status;
+
+ struct apm_info pw_info;
+ struct battery_watch_event *p;
+
+ /* If we don't care, don't bother */
+ if (battery_watch_list == NULL)
+ return;
+
+ if (first_time) {
+ if ( ioctl(apmnorm_fd, APMIO_GETINFO, &pw_info) < 0)
+ (void) err(1, "cannot check battery state.");
+/*
+ * This next statement isn't entirely true. The spec does not tie AC
+ * line state to battery charging or not, but this is a bit lazier to do.
+ */
+ last_state = AC_POWER_STATE;
+ first_time = 0;
+ return; /* We can't process events, we have no baseline */
+ }
+
+ /*
+ * XXX - should we do this a bunch of times and perform some sort
+ * of smoothing or correction?
+ */
+ if ( ioctl(apmnorm_fd, APMIO_GETINFO, &pw_info) < 0)
+ (void) err(1, "cannot check battery state.");
+
+ /*
+ * If we're not in the state now that we were in last time,
+ * then it's a transition, which means we must clean out
+ * the event-caught state.
+ */
+ if (last_state != AC_POWER_STATE) {
+ if (soft_power_state_change && fork() == 0) {
+ status = exec_event_cmd(&events[PMEV_POWERSTATECHANGE]);
+ exit(status);
+ }
+ last_state = AC_POWER_STATE;
+ for (p = battery_watch_list ; p!=NULL ; p = p -> next)
+ p->done = 0;
+ }
+ for (p = battery_watch_list ; p != NULL ; p = p -> next)
+ if (p -> direction == AC_POWER_STATE &&
+ !(p -> done) &&
+ ((p -> type == BATTERY_PERCENT &&
+ p -> level == pw_info.ai_batt_life) ||
+ (p -> type == BATTERY_MINUTES &&
+ p -> level == (pw_info.ai_batt_time / 60)))) {
+ p -> done++;
+ if (verbose)
+ syslog(LOG_NOTICE, "Caught battery event: %s, %d%s",
+ (p -> direction == BATTERY_CHARGING)?"charging":"discharging",
+ p -> level,
+ (p -> type == BATTERY_PERCENT)?"%":" minutes");
+ if (fork() == 0) {
+ status = exec_run_cmd(p -> cmdlist);
+ exit(status);
+ }
+ }
+}
+void
+event_loop(void)
+{
+ int fdmax = 0;
+ struct sigaction nsa;
+ fd_set master_rfds;
+ sigset_t sigmask, osigmask;
+
+ FD_ZERO(&master_rfds);
+ FD_SET(apmctl_fd, &master_rfds);
+ fdmax = apmctl_fd > fdmax ? apmctl_fd : fdmax;
+
+ FD_SET(signal_fd[0], &master_rfds);
+ fdmax = signal_fd[0] > fdmax ? signal_fd[0] : fdmax;
+
+ memset(&nsa, 0, sizeof nsa);
+ nsa.sa_handler = enque_signal;
+ sigfillset(&nsa.sa_mask);
+ nsa.sa_flags = SA_RESTART;
+ sigaction(SIGHUP, &nsa, NULL);
+ sigaction(SIGCHLD, &nsa, NULL);
+ sigaction(SIGTERM, &nsa, NULL);
+
+ sigemptyset(&sigmask);
+ sigaddset(&sigmask, SIGHUP);
+ sigaddset(&sigmask, SIGCHLD);
+ sigaddset(&sigmask, SIGTERM);
+ sigprocmask(SIG_SETMASK, &sigmask, &osigmask);
+
+ while (1) {
+ fd_set rfds;
+ int res;
+ struct timeval to;
+
+ to.tv_sec = BATT_CHK_INTV;
+ to.tv_usec = 0;
+
+ memcpy(&rfds, &master_rfds, sizeof rfds);
+ sigprocmask(SIG_SETMASK, &osigmask, NULL);
+ if ((res=select(fdmax + 1, &rfds, 0, 0, &to)) < 0) {
+ if (errno != EINTR)
+ (void) err(1, "select");
+ }
+ sigprocmask(SIG_SETMASK, &sigmask, NULL);
+
+ if (res == 0) { /* time to check the battery */
+ check_battery();
+ continue;
+ }
+
+ if (FD_ISSET(signal_fd[0], &rfds)) {
+ if (proc_signal(signal_fd[0]) < 0)
+ goto out;
+ }
+
+ if (FD_ISSET(apmctl_fd, &rfds))
+ proc_apmevent(apmctl_fd);
+ }
+out:
+ return;
+}
+
+int
+main(int ac, char* av[])
+{
+ int ch;
+ int daemonize = 1;
+ char *prog;
+ int logopt = LOG_NDELAY | LOG_PID;
+
+ while ((ch = getopt(ac, av, "df:sv")) != -1) {
+ switch (ch) {
+ case 'd':
+ daemonize = 0;
+ debug_level++;
+ break;
+ case 'f':
+ apmd_configfile = optarg;
+ break;
+ case 's':
+ soft_power_state_change = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ (void) err(1, "unknown option `%c'", ch);
+ }
+ }
+
+ if (daemonize)
+ daemon(0, 0);
+
+#ifdef NICE_INCR
+ (void) nice(NICE_INCR);
+#endif
+
+ if (!daemonize)
+ logopt |= LOG_PERROR;
+
+ prog = strrchr(av[0], '/');
+ openlog(prog ? prog+1 : av[0], logopt, LOG_DAEMON);
+
+ syslog(LOG_NOTICE, "start");
+
+ if (pipe(signal_fd) < 0)
+ (void) err(1, "pipe");
+ if (fcntl(signal_fd[0], F_SETFL, O_NONBLOCK) < 0)
+ (void) err(1, "fcntl");
+
+ if ((apmnorm_fd = open(APM_NORM_DEVICEFILE, O_RDWR)) == -1) {
+ (void) err(1, "cannot open device file `%s'", APM_NORM_DEVICEFILE);
+ }
+
+ if (fcntl(apmnorm_fd, F_SETFD, 1) == -1) {
+ (void) err(1, "cannot set close-on-exec flag for device file '%s'", APM_NORM_DEVICEFILE);
+ }
+
+ if ((apmctl_fd = open(APM_CTL_DEVICEFILE, O_RDWR)) == -1) {
+ (void) err(1, "cannot open device file `%s'", APM_CTL_DEVICEFILE);
+ }
+
+ if (fcntl(apmctl_fd, F_SETFD, 1) == -1) {
+ (void) err(1, "cannot set close-on-exec flag for device file '%s'", APM_CTL_DEVICEFILE);
+ }
+
+ restart();
+ write_pid();
+ event_loop();
+ exit(EXIT_SUCCESS);
+}
+
diff --git a/usr.sbin/apmd/apmd.h b/usr.sbin/apmd/apmd.h
new file mode 100644
index 0000000..1be5afa
--- /dev/null
+++ b/usr.sbin/apmd/apmd.h
@@ -0,0 +1,108 @@
+/*-
+ * APM (Advanced Power Management) Event Dispatcher
+ *
+ * Copyright (c) 1999 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * Copyright (c) 1999 KOIE Hidetaka <koie@suri.co.jp>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+#define APMD_CONFIGFILE "/etc/apmd.conf"
+#define APM_CTL_DEVICEFILE "/dev/apmctl"
+#define APM_NORM_DEVICEFILE "/dev/apm"
+#define APMD_PIDFILE "/var/run/apmd.pid"
+#define NICE_INCR -20
+
+enum {
+ EVENT_NOEVENT,
+ EVENT_STANDBYREQ,
+ EVENT_SUSPENDREQ,
+ EVENT_NORMRESUME,
+ EVENT_CRITRESUME,
+ EVENT_BATTERYLOW,
+ EVENT_POWERSTATECHANGE,
+ EVENT_UPDATETIME,
+ EVENT_CRITSUSPEND,
+ EVENT_USERSTANDBYREQ,
+ EVENT_USERSUSPENDREQ,
+ EVENT_STANDBYRESUME,
+ EVENT_CAPABILITIESCHANGE,
+ EVENT_MAX
+};
+
+struct event_cmd_op {
+ int (* act)(void *this);
+ void (* dump)(void *this, FILE * fp);
+ struct event_cmd * (* clone)(void *this);
+ void (* free)(void *this);
+};
+struct event_cmd {
+ struct event_cmd * next;
+ size_t len;
+ char * name;
+ struct event_cmd_op * op;
+};
+struct event_cmd_exec {
+ struct event_cmd evcmd;
+ char * line; /* Command line */
+};
+struct event_cmd_reject {
+ struct event_cmd evcmd;
+};
+
+struct event_config {
+ const char *name;
+ struct event_cmd * cmdlist;
+ int rejectable;
+};
+
+struct battery_watch_event {
+ struct battery_watch_event *next;
+ int level;
+ enum {
+ BATTERY_CHARGING,
+ BATTERY_DISCHARGING
+ } direction;
+ enum {
+ BATTERY_MINUTES,
+ BATTERY_PERCENT
+ } type;
+ int done;
+ struct event_cmd *cmdlist;
+};
+
+
+extern struct event_cmd_op event_cmd_exec_ops;
+extern struct event_cmd_op event_cmd_reject_ops;
+extern struct event_config events[EVENT_MAX];
+extern struct battery_watch_event *battery_watch_list;
+
+extern int register_battery_handlers(
+ int level, int direction,
+ struct event_cmd *cmdlist);
+extern int register_apm_event_handlers(
+ bitstr_t bit_decl(evlist, EVENT_MAX),
+ struct event_cmd *cmdlist);
+extern void free_event_cmd_list(struct event_cmd *p);
diff --git a/usr.sbin/apmd/apmdlex.l b/usr.sbin/apmd/apmdlex.l
new file mode 100644
index 0000000..0e5cff3
--- /dev/null
+++ b/usr.sbin/apmd/apmdlex.l
@@ -0,0 +1,112 @@
+%{
+/*-
+ * APM (Advanced Power Management) Event Dispatcher
+ *
+ * Copyright (c) 1999 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * Copyright (c) 1999 KOIE Hidetaka <koie@suri.co.jp>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <string.h>
+#include <syslog.h>
+#include <bitstring.h>
+#include "apmd.h"
+#include "y.tab.h"
+
+int lineno;
+int first_time;
+%}
+
+%s TOP
+
+%%
+
+%{
+ if (first_time) {
+ BEGIN TOP;
+ lineno = 1;
+ first_time = 0;
+ }
+%}
+
+<TOP>[ \t]+ ;
+<TOP>\n lineno++;
+<TOP>, { return COMMA; }
+<TOP>; { return SEMICOLON; }
+<TOP>#.*$ ;
+
+<TOP>apm_event { return APMEVENT; }
+
+<TOP>NOEVENT { yylval.ev = EVENT_NOEVENT; return EVENT; }
+<TOP>STANDBYREQ { yylval.ev = EVENT_STANDBYREQ; return EVENT; }
+<TOP>SUSPENDREQ { yylval.ev = EVENT_SUSPENDREQ; return EVENT; }
+<TOP>NORMRESUME { yylval.ev = EVENT_NORMRESUME; return EVENT; }
+<TOP>CRITRESUME { yylval.ev = EVENT_CRITRESUME; return EVENT; }
+<TOP>BATTERYLOW { yylval.ev = EVENT_BATTERYLOW; return EVENT; }
+<TOP>POWERSTATECHANGE { yylval.ev = EVENT_POWERSTATECHANGE; return EVENT; }
+<TOP>UPDATETIME { yylval.ev = EVENT_UPDATETIME; return EVENT; }
+<TOP>CRITSUSPEND { yylval.ev = EVENT_CRITSUSPEND; return EVENT; }
+<TOP>USERSTANDBYREQ { yylval.ev = EVENT_USERSTANDBYREQ; return EVENT; }
+<TOP>USERSUSPENDREQ { yylval.ev = EVENT_USERSUSPENDREQ; return EVENT; }
+<TOP>STANDBYRESUME { yylval.ev = EVENT_STANDBYRESUME; return EVENT; }
+<TOP>CAPABILITIESCHANGE { yylval.ev = EVENT_CAPABILITIESCHANGE; return EVENT; }
+
+<TOP>apm_battery { return APMBATT; }
+
+<TOP>charging { return BATTCHARGE; }
+<TOP>discharging { return BATTDISCHARGE; }
+<TOP>[0-9]+% {
+ yylval.i = atoi(yytext);
+ return BATTPERCENT;
+ }
+<TOP>[0-9]+[Mm] {
+ yylval.i = -atoi(yytext);
+ return BATTTIME;
+ }
+
+<TOP>exec { return EXECCMD; }
+<TOP>reject { return REJECTCMD; }
+
+<TOP>\{ { return BEGINBLOCK; }
+<TOP>\} { return ENDBLOCK; }
+<TOP>\"[^"]+\" {
+ int len = strlen(yytext) - 2;
+ if ((yylval.str = (char *) malloc(len + 1)) == NULL)
+ goto out;
+ memcpy(yylval.str, yytext + 1, len);
+ yylval.str[len] = '\0';
+ out:
+ return STRING;
+ }
+
+<TOP>[^"{},;#\n\t ]+ { yylval.str = strdup(yytext); return UNKNOWN; }
+%%
+
+void
+yyerror(const char *s)
+{
+ syslog(LOG_ERR, "line %d: %s%s %s.\n", lineno, yytext, yytext?":":"", s);
+}
diff --git a/usr.sbin/apmd/apmdparse.y b/usr.sbin/apmd/apmdparse.y
new file mode 100644
index 0000000..597982b
--- /dev/null
+++ b/usr.sbin/apmd/apmdparse.y
@@ -0,0 +1,205 @@
+%{
+/*-
+ * APM (Advanced Power Management) Event Dispatcher
+ *
+ * Copyright (c) 1999 Mitsuru IWASAKI <iwasaki@FreeBSD.org>
+ * Copyright (c) 1999 KOIE Hidetaka <koie@suri.co.jp>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <stdio.h>
+#include <bitstring.h>
+#include <stdlib.h>
+#include <string.h>
+#include "apmd.h"
+
+#ifdef DEBUG
+#define YYDEBUG 1
+#endif
+
+extern int first_time;
+
+%}
+
+%union {
+ char *str;
+ bitstr_t bit_decl(evlist, EVENT_MAX);
+ int ev;
+ struct event_cmd * evcmd;
+ int i;
+}
+
+%token BEGINBLOCK ENDBLOCK
+%token COMMA SEMICOLON
+%token APMEVENT
+%token APMBATT
+%token BATTCHARGE BATTDISCHARGE
+%token <i> BATTTIME BATTPERCENT
+%token EXECCMD REJECTCMD
+%token <ev> EVENT
+%token <str> STRING UNKNOWN
+
+%type <i> apm_battery_level
+%type <i> apm_battery_direction
+%type <str> string
+%type <str> unknown
+%type <evlist> event_list
+%type <evcmd> cmd_list
+%type <evcmd> cmd
+%type <evcmd> exec_cmd reject_cmd
+
+%%
+
+config_file
+ : { first_time = 1; } config_list
+ ;
+
+config_list
+ : config
+ | config_list config
+ ;
+
+config
+ : apm_event_statement
+ | apm_battery_statement
+ ;
+
+apm_event_statement
+ : APMEVENT event_list BEGINBLOCK cmd_list ENDBLOCK
+ {
+ if (register_apm_event_handlers($2, $4) < 0)
+ abort(); /* XXX */
+ free_event_cmd_list($4);
+ }
+ ;
+
+apm_battery_level
+ : BATTPERCENT
+ {
+ $$ = $1;
+ }
+ | BATTTIME
+ {
+ $$ = $1;
+ }
+ ;
+
+apm_battery_direction
+ : BATTCHARGE
+ {
+ $$ = 1;
+ }
+ | BATTDISCHARGE
+ {
+ $$ = -1;
+ }
+ ;
+apm_battery_statement
+ : APMBATT apm_battery_level apm_battery_direction
+ BEGINBLOCK cmd_list ENDBLOCK
+ {
+ if (register_battery_handlers($2, $3, $5) < 0)
+ abort(); /* XXX */
+ free_event_cmd_list($5);
+ }
+ ;
+
+event_list
+ : EVENT
+ {
+ bit_nclear($$, 0, EVENT_MAX - 1);
+ bit_set($$, $1);
+ }
+ | event_list COMMA EVENT
+ {
+ memcpy(&($$), &($1), bitstr_size(EVENT_MAX));
+ bit_set($$, $3);
+ }
+ ;
+
+cmd_list
+ : /* empty */
+ {
+ $$ = NULL;
+ }
+ | cmd_list cmd
+ {
+ struct event_cmd * p = $1;
+ if (p) {
+ while (p->next != NULL)
+ p = p->next;
+ p->next = $2;
+ $$ = $1;
+ } else {
+ $$ = $2;
+ }
+ }
+ ;
+
+cmd
+ : exec_cmd SEMICOLON { $$ = $1; }
+ | reject_cmd SEMICOLON { $$ = $1; }
+ ;
+
+exec_cmd
+ : EXECCMD string
+ {
+ size_t len = sizeof (struct event_cmd_exec);
+ struct event_cmd_exec *cmd = malloc(len);
+ cmd->evcmd.next = NULL;
+ cmd->evcmd.len = len;
+ cmd->evcmd.name = "exec";
+ cmd->evcmd.op = &event_cmd_exec_ops;
+ cmd->line = $2;
+ $$ = (struct event_cmd *) cmd;
+ }
+ ;
+
+reject_cmd
+ : REJECTCMD
+ {
+ size_t len = sizeof (struct event_cmd_reject);
+ struct event_cmd_reject *cmd = malloc(len);
+ cmd->evcmd.next = NULL;
+ cmd->evcmd.len = len;
+ cmd->evcmd.name = "reject";
+ cmd->evcmd.op = &event_cmd_reject_ops;
+ $$ = (struct event_cmd *) cmd;
+ }
+ ;
+
+string
+ : STRING { $$ = $1; }
+ ;
+
+unknown
+ : UNKNOWN
+ {
+ $$ = $1;
+ }
+ ;
+%%
+
diff --git a/usr.sbin/apmd/contrib/pccardq.c b/usr.sbin/apmd/contrib/pccardq.c
new file mode 100644
index 0000000..e619770
--- /dev/null
+++ b/usr.sbin/apmd/contrib/pccardq.c
@@ -0,0 +1,286 @@
+/* $FreeBSD$ */
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+const char *const pccardd_file = "/var/tmp/.pccardd";
+const char *prog = "pccardq";
+const char *tmp_dir = "/tmp";
+unsigned slot_map = ~0;
+
+void
+usage()
+{
+ fprintf(stderr, "usage: %s [-a] [-n] [-s slot]\n", prog);
+}
+
+int
+proc_arg(int ac, char **av)
+{
+ int rc = -1;
+ int ch;
+
+ char *p = strrchr(av[0], '/');
+ prog = p ? p + 1 : av[0];
+
+ tmp_dir = getenv("TMPDIR") ? getenv("TMPDIR") : tmp_dir;
+
+ while ((ch = getopt(ac, av, "ans:")) != -1) {
+ switch (ch) {
+ case 'a':
+ slot_map = ~0;
+ break;
+ case 'n':
+ slot_map = 0;
+ break;
+ case 's':
+ {
+ int n = atoi(optarg);
+ if (n < 0 || n >= CHAR_BIT * sizeof slot_map) {
+ warnc(0, "Invalid slot number.");
+ usage();
+ goto out;
+ }
+ if (slot_map == ~0)
+ slot_map = 0;
+ slot_map |= 1 << n;
+ }
+ break;
+ default:
+ usage();
+ goto out;
+ }
+ }
+
+ rc = 0;
+ out:
+ return rc;
+}
+
+int
+connect_to_pccardd(char **path)
+{
+ int so = -1;
+ int pccardd_len;
+ struct sockaddr_un pccardq;
+ struct sockaddr_un pccardd;
+
+ if ((so = socket(PF_UNIX, SOCK_DGRAM, 0)) < 0) {
+ warn("socket");
+ goto err;
+ }
+
+ snprintf(pccardq.sun_path, sizeof pccardq.sun_path,
+ "%s/%s%ld%ld", tmp_dir, prog, (long) getpid(), (long) time(0));
+ pccardq.sun_family = AF_UNIX;
+ pccardq.sun_len = offsetof(struct sockaddr_un, sun_path) + strlen(pccardq.sun_path);
+ if (bind(so, (struct sockaddr *) &pccardq, pccardq.sun_len) < 0) {
+ warn("bind: %s", pccardq.sun_path);
+ goto err;
+ }
+ if ((*path = strdup(pccardq.sun_path)) == NULL) {
+ warn("strdup");
+ goto err;
+ }
+
+ pccardd_len = strlen(pccardd_file) + 1;
+ if (pccardd_len > sizeof pccardd.sun_path) {
+ warnc(0, "%s: too long", pccardd_file);
+ goto err;
+ }
+ pccardd.sun_len = offsetof(struct sockaddr_un, sun_path) + pccardd_len;
+ pccardd.sun_family = AF_UNIX;
+ strcpy(pccardd.sun_path, pccardd_file);
+ if (connect(so, (struct sockaddr *) &pccardd, pccardd.sun_len) < 0) {
+ warn("connect: %s", pccardd_file);
+ goto err;
+ }
+ return so;
+ err:
+ if (so >= 0)
+ close(so);
+ return -1;
+}
+
+int
+get_slot_number(int so)
+{
+ char buf[8];
+ int rv;
+ int nslot;
+
+ if ((rv = write(so, "S", 1)) < 1) {
+ warn("write");
+ goto err;
+ } else if (rv != 1) {
+ warnc(0, "write: fail.");
+ goto err;
+ }
+
+ if ((rv = read(so, buf, sizeof buf)) < 0) {
+ warn("read");
+ goto err;
+ }
+ buf[sizeof buf - 1] = 0;
+ if (sscanf(buf, "%d", &nslot) != 1) {
+ warnc(0, "Invalid response.");
+ goto err;
+ }
+ return nslot;
+ err:
+ return -1;
+}
+
+enum {
+ SLOT_EMPTY = 0,
+ SLOT_FILLED = 1,
+ SLOT_INACTIVE = 2,
+ SLOT_UNDEFINED = 9
+};
+
+int
+get_slot_info(int so, int slot, char **manuf, char **version, char
+ **device, int *state)
+{
+ int rc = -1;
+ int rv;
+ static char buf[1024];
+ int slen;
+ char *s;
+ char *sl;
+
+ char *_manuf;
+ char *_version;
+ char *_device;
+
+ if ((slen = snprintf(buf, sizeof buf, "N%d", slot)) < 0) {
+ warnc(0, "write");
+ goto err;
+ }
+
+ if ((rv = write(so, buf, slen)) < 0) {
+ warn("write");
+ goto err;
+ } else if (rv != slen) {
+ warnc(0, "write");
+ goto err;
+ }
+
+ if ((rv = read(so, buf, sizeof buf)) < 0) {
+ warn("read");
+ goto err;
+ }
+
+ s = buf;
+ if ((sl = strsep(&s, "~")) == NULL)
+ goto parse_err;
+ if (atoi(sl) != slot)
+ goto parse_err;
+ if ((_manuf = strsep(&s, "~")) == NULL)
+ goto parse_err;
+ if ((_version = strsep(&s, "~")) == NULL)
+ goto parse_err;
+ if ((_device = strsep(&s, "~")) == NULL)
+ goto parse_err;
+ if (sscanf(s, "%1d", state) != 1)
+ goto parse_err;
+ if (s != NULL && strchr(s, '~') != NULL)
+ goto parse_err;
+
+ if ((*manuf = strdup(_manuf)) == NULL) {
+ warn("strdup");
+ goto err;
+ }
+ if ((*version = strdup(_version)) == NULL) {
+ warn("strdup");
+ goto err;
+ }
+ if ((*device = strdup(_device)) == NULL) {
+ warn("strdup");
+ goto err;
+ }
+ if (*manuf == NULL || *version == NULL || *device == NULL) {
+ warn("strdup");
+ goto err;
+ }
+
+ rc = 0;
+ err:
+ return rc;
+ parse_err:
+ warnc(0, "Invalid response: %*s", rv, buf);
+ return rc;
+}
+
+const char *
+strstate(int state)
+{
+ switch (state) {
+ case 0:
+ return "empty";
+ case 1:
+ return "filled";
+ case 2:
+ return "inactive";
+ default:
+ return "unknown";
+ }
+}
+
+int
+main(int ac, char **av)
+{
+ char *path = NULL;
+ int so = -1;
+ int nslot;
+ int i;
+
+ if (proc_arg(ac, av) < 0)
+ goto out;
+ if ((so = connect_to_pccardd(&path)) < 0)
+ goto out;
+ if ((nslot = get_slot_number(so)) < 0)
+ goto out;
+ if (slot_map == 0) {
+ printf("%d\n", nslot);
+ } else {
+ for (i = 0; i < nslot; i++) {
+ if ((slot_map & (1 << i))) {
+ char *manuf;
+ char *version;
+ char *device;
+ int state;
+
+ if (get_slot_info(so, i, &manuf, &version, &device,
+ &state) < 0)
+ goto out;
+ if (manuf == NULL || version == NULL || device == NULL)
+ goto out;
+ printf("%d~%s~%s~%s~%s\n",
+ i, manuf, version, device, strstate(state));
+ free(manuf);
+ free(version);
+ free(device);
+ }
+ }
+ }
+ out:
+ if (path) {
+ unlink(path);
+ free(path);
+ }
+ if (so >= 0)
+ close(so);
+ exit(0);
+}
diff --git a/usr.sbin/arp/Makefile b/usr.sbin/arp/Makefile
new file mode 100644
index 0000000..ea1c507
--- /dev/null
+++ b/usr.sbin/arp/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.2 (Berkeley) 4/18/94
+# $FreeBSD$
+
+PROG= arp
+MAN= arp.4 arp.8
+
+WARNS?= 1
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/arp/arp.4 b/usr.sbin/arp/arp.4
new file mode 100644
index 0000000..335a521
--- /dev/null
+++ b/usr.sbin/arp/arp.4
@@ -0,0 +1,191 @@
+.\" Copyright (c) 1985, 1986, 1988, 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.
+.\"
+.\" @(#)arp4.4 6.5 (Berkeley) 4/18/94
+.\" $FreeBSD$
+.\"
+.Dd March 28, 2007
+.Dt ARP 4
+.Os
+.Sh NAME
+.Nm arp
+.Nd Address Resolution Protocol
+.Sh SYNOPSIS
+.Cd "device ether"
+.Sh DESCRIPTION
+The Address Resolution Protocol (ARP) is used to dynamically
+map between Protocol Addresses (such as IP addresses) and
+Local Network Addresses (such as Ethernet addresses).
+This implementation maps IP addresses to Ethernet,
+ARCnet,
+or Token Ring addresses.
+It is used by all the Ethernet interface drivers.
+.Pp
+ARP caches Internet-Ethernet address mappings.
+When an interface requests a mapping for an address not in the cache,
+ARP queues the message which requires the mapping and broadcasts
+a message on the associated network requesting the address mapping.
+If a response is provided, the new mapping is cached and any pending
+message is transmitted.
+ARP will queue at most one packet while waiting for a response to a
+mapping request;
+only the most recently ``transmitted'' packet is kept.
+If the target host does not respond after several requests,
+the host is considered to be down allowing an error to be returned to
+transmission attempts.
+Further demand for this mapping causes ARP request retransmissions, that
+are ratelimited to one packet per second.
+The error is
+.Er EHOSTDOWN
+for a non-responding destination host, and
+.Er EHOSTUNREACH
+for a non-responding router.
+.Pp
+The ARP cache is stored in the system routing table as
+dynamically-created host routes.
+The route to a directly-attached Ethernet network is installed as a
+.Dq cloning
+route (one with the
+.Li RTF_CLONING
+flag set),
+causing routes to individual hosts on that network to be created on
+demand.
+These routes time out periodically (normally 20 minutes after validated;
+entries are not validated when not in use).
+.Pp
+ARP entries may be added, deleted or changed with the
+.Xr arp 8
+utility.
+Manually-added entries may be temporary or permanent,
+and may be
+.Dq published ,
+in which case the system will respond to ARP requests for that host
+as if it were the target of the request.
+.Pp
+In the past,
+ARP was used to negotiate the use of a trailer encapsulation.
+This is no longer supported.
+.Pp
+ARP watches passively for hosts impersonating the local host (i.e., a host
+which responds to an ARP mapping request for the local host's address).
+.Pp
+Proxy ARP is a feature whereby the local host will respond to requests
+for addresses other than itself, with its own address.
+Normally, proxy ARP in
+.Fx
+is set up on a host-by-host basis using the
+.Xr arp 8
+utility, by adding an entry for each host inside a given subnet for
+which proxying of ARP requests is desired.
+However, the
+.Dq "proxy all"
+feature causes the local host to act as a proxy for
+.Em all
+hosts reachable through some other network interface,
+different from the one the request came in from.
+It may be enabled by setting the
+.Xr sysctl 8
+MIB variable
+.Va net.link.ether.inet.proxyall
+to 1.
+.Sh MIB Variables
+The ARP protocol implements a number of configrable variables in
+.Va net.link.ether.inet
+branch
+of the
+.Xr sysctl 3
+MIB.
+.Bl -tag
+.It Va max_age
+How long an ARP entry is held in the cache until it needs to be refreshed.
+.It Va maxtries
+Number of retransmits before host is considered down and error is returned.
+.It Va useloopback
+If an ARP entry is added for local address, force the traffic to go through
+the loopback interface.
+.It Va proxyall
+Enables ARP proxying for all hosts on net.
+.El
+.Sh DIAGNOSTICS
+.Bl -diag
+.It "arp: %x:%x:%x:%x:%x:%x is using my IP address %d.%d.%d.%d!"
+ARP has discovered another host on the local network which responds to
+mapping requests for its own Internet address with a different Ethernet
+address, generally indicating that two hosts are attempting to use the
+same Internet address.
+.It "arp: link address is broadcast for IP address %d.%d.%d.%d!"
+ARP requested information for a host, and received an answer indicating
+that the host's ethernet address is the ethernet broadcast address.
+This indicates a misconfigured or broken device.
+.It "arp: %d.%d.%d.%d moved from %x:%x:%x:%x:%x:%x to %x:%x:%x:%x:%x:%x on %s"
+ARP had a cached value for the ethernet address of the referenced host,
+but received a reply indicating that the host is at a new address.
+This can happen normally when host hardware addresses change,
+or when a mobile node arrives or leaves the local subnet.
+It can also indicate a problem with proxy ARP.
+This message can only be issued if the sysctl
+.Va net.link.ether.inet.log_arp_movements
+is set to 1, which is the system's default behaviour.
+.It "arpresolve: can't allocate llinfo for %d.%d.%d.%d"
+The route for the referenced host points to a device upon which ARP is
+required, but ARP was unable to allocate a routing table entry in which
+to store the host's MAC address.
+This usually points to a misconfigured routing table.
+It can also occur if the kernel cannot allocate memory.
+.It "arp: %d.%d.%d.%d is on if0 but got reply from %x:%x:%x:%x:%x:%x on if1"
+Physical connections exist to the same logical IP network on both if0 and if1.
+It can also occur if an entry already exists in the ARP cache for the IP
+address above, and the cable has been disconnected from if0, then reconnected
+to if1.
+This message can only be issued if the sysctl
+.Va net.link.ether.inet.log_arp_wrong_iface
+is set to 1, which is the system's default behaviour.
+.It "arp: %x:%x:%x:%x:%x:%x attempts to modify permanent entry for %d.%d.%d.%d on %s"
+ARP has received an ARP reply that attempts to overwrite a permanent
+entry in the local ARP table.
+This error will only be logged if the sysctl
+.Va net.link.ether.inet.log_arp_permanent_modify
+is set to 1, which is the system's default behaviour.
+.El
+.Sh SEE ALSO
+.Xr inet 4 ,
+.Xr route 4 ,
+.Xr arp 8 ,
+.Xr ifconfig 8 ,
+.Xr route 8 ,
+.Xr sysctl 8
+.Rs
+.%A Plummer, D.
+.%B "An Ethernet Address Resolution Protocol"
+.%T RFC826
+.Re
+.Rs
+.%A Leffler, S.J.
+.%A Karels, M.J.
+.%B "Trailer Encapsulations
+.%T RFC893
+.Re
diff --git a/usr.sbin/arp/arp.8 b/usr.sbin/arp/arp.8
new file mode 100644
index 0000000..9dbe45a
--- /dev/null
+++ b/usr.sbin/arp/arp.8
@@ -0,0 +1,201 @@
+.\" 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.
+.\"
+.\" @(#)arp.8 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\"
+.Dd December 25, 2008
+.Dt ARP 8
+.Os
+.Sh NAME
+.Nm arp
+.Nd address resolution display and control
+.Sh SYNOPSIS
+.Nm
+.Op Fl n
+.Op Fl i Ar interface
+.Ar hostname
+.Nm
+.Op Fl n
+.Op Fl i Ar interface
+.Fl a
+.Nm
+.Fl d Ar hostname
+.Op Cm pub
+.Nm
+.Fl d
+.Op Fl i Ar interface
+.Fl a
+.Nm
+.Fl s Ar hostname ether_addr
+.Op Cm temp
+.Op Cm blackhole No \&| Cm reject
+.Op Cm pub Op Cm only
+.Nm
+.Fl S Ar hostname ether_addr
+.Op Cm temp
+.Op Cm blackhole No \&| Cm reject
+.Op Cm pub Op Cm only
+.Nm
+.Fl f Ar filename
+.Sh DESCRIPTION
+The
+.Nm
+utility displays and modifies the Internet-to-Ethernet address translation
+tables used by the address resolution protocol
+.Pq Xr arp 4 .
+With no flags, the program displays the current
+.Tn ARP
+entry for
+.Ar hostname .
+The host may be specified by name or by number,
+using Internet dot notation.
+.Pp
+Available options:
+.Bl -tag -width indent
+.It Fl a
+The program displays or deletes all of the current
+.Tn ARP
+entries.
+.It Fl d
+A super-user may delete an entry for the host called
+.Ar hostname
+with the
+.Fl d
+flag.
+If the
+.Cm pub
+keyword is specified, only the
+.Dq published
+.Tn ARP
+entry
+for this host will be deleted.
+.Pp
+Alternatively, the
+.Fl d
+flag may be combined with the
+.Fl a
+flag to delete all entries.
+.It Fl i Ar interface
+Limit the operation scope to the
+.Tn ARP
+entries on
+.Ar interface .
+Applicable only to the following operations:
+display one, display all, delete all.
+.It Fl n
+Show network addresses as numbers (normally
+.Nm
+attempts to display addresses symbolically).
+.It Fl s Ar hostname ether_addr
+Create an
+.Tn ARP
+entry for the host called
+.Ar hostname
+with the Ethernet address
+.Ar ether_addr .
+The Ethernet address is given as six hex bytes separated by colons.
+The entry will be permanent unless the word
+.Cm temp
+is given in the command.
+If the word
+.Cm pub
+is given, the entry will be
+.Dq published ;
+i.e., this system will
+act as an
+.Tn ARP
+server,
+responding to requests for
+.Ar hostname
+even though the host address is not its own.
+In this case the
+.Ar ether_addr
+can be given as
+.Cm auto
+in which case the interfaces on this host will be examined,
+and if one of them is found to occupy the same subnet, its
+Ethernet address will be used.
+If the
+.Cm only
+keyword is also specified, this will create a
+.Dq "published (proxy only)"
+entry.
+This type of entry is created automatically if
+.Nm
+detects that a routing table entry for
+.Ar hostname
+already exists.
+.Pp
+If the
+.Cm reject
+keyword is specified the entry will be marked so that traffic to
+the host will be discarded and the sender will be notified the
+host is unreachable.
+The
+.Cm blackhole
+keyword is similar in that traffic is discarded but the sender is
+not notified.
+These can be used to block external traffic to a host without
+using a firewall.
+.It Fl S Ar hostname ether_addr
+Is just like
+.Fl s
+except any existing
+.Tn ARP
+entry for this host will be deleted first.
+.It Fl f Ar filename
+Cause the file
+.Ar filename
+to be read and multiple entries to be set in the
+.Tn ARP
+tables.
+Entries
+in the file should be of the form
+.Pp
+.Bd -ragged -offset indent -compact
+.Ar hostname ether_addr
+.Op Cm temp
+.Op Cm blackhole No \&| Cm reject
+.Op Cm pub Op Cm only
+.Ed
+.Pp
+with argument meanings as given above.
+Leading whitespace and empty lines are ignored.
+A
+.Ql #
+character will mark the rest of the line as a comment.
+.El
+.Sh SEE ALSO
+.Xr inet 3 ,
+.Xr arp 4 ,
+.Xr ifconfig 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.3 .
diff --git a/usr.sbin/arp/arp.c b/usr.sbin/arp/arp.c
new file mode 100644
index 0000000..f99cf0f
--- /dev/null
+++ b/usr.sbin/arp/arp.c
@@ -0,0 +1,846 @@
+/*
+ * Copyright (c) 1984, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Sun Microsystems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 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 const copyright[] =
+"@(#) Copyright (c) 1984, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char const sccsid[] = "@(#)from: arp.c 8.2 (Berkeley) 1/2/94";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * arp - display, set, and delete arp table entries
+ */
+
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/sockio.h>
+#include <sys/sysctl.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/route.h>
+#include <net/iso88025.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <nlist.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+
+typedef void (action_fn)(struct sockaddr_dl *sdl,
+ struct sockaddr_inarp *s_in, struct rt_msghdr *rtm);
+
+static int search(u_long addr, action_fn *action);
+static action_fn print_entry;
+static action_fn nuke_entry;
+
+static int delete(char *host, int do_proxy);
+static void usage(void);
+static int set(int argc, char **argv);
+static int get(char *host);
+static int file(char *name);
+static struct rt_msghdr *rtmsg(int cmd,
+ struct sockaddr_inarp *dst, struct sockaddr_dl *sdl);
+static int get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr);
+static struct sockaddr_inarp *getaddr(char *host);
+static int valid_type(int type);
+
+static int nflag; /* no reverse dns lookups */
+static char *rifname;
+
+static int expire_time, flags, doing_proxy, proxy_only;
+
+/* which function we're supposed to do */
+#define F_GET 1
+#define F_SET 2
+#define F_FILESET 3
+#define F_REPLACE 4
+#define F_DELETE 5
+
+#define SETFUNC(f) { if (func) usage(); func = (f); }
+
+int
+main(int argc, char *argv[])
+{
+ int ch, func = 0;
+ int rtn = 0;
+ int aflag = 0; /* do it for all entries */
+
+ while ((ch = getopt(argc, argv, "andfsSi:")) != -1)
+ switch((char)ch) {
+ case 'a':
+ aflag = 1;
+ break;
+ case 'd':
+ SETFUNC(F_DELETE);
+ break;
+ case 'n':
+ nflag = 1;
+ break;
+ case 'S':
+ SETFUNC(F_REPLACE);
+ break;
+ case 's':
+ SETFUNC(F_SET);
+ break;
+ case 'f' :
+ SETFUNC(F_FILESET);
+ break;
+ case 'i':
+ rifname = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!func)
+ func = F_GET;
+ if (rifname) {
+ if (func != F_GET && !(func == F_DELETE && aflag))
+ errx(1, "-i not applicable to this operation");
+ if (if_nametoindex(rifname) == 0) {
+ if (errno == ENXIO)
+ errx(1, "interface %s does not exist", rifname);
+ else
+ err(1, "if_nametoindex(%s)", rifname);
+ }
+ }
+ switch (func) {
+ case F_GET:
+ if (aflag) {
+ if (argc != 0)
+ usage();
+ search(0, print_entry);
+ } else {
+ if (argc != 1)
+ usage();
+ rtn = get(argv[0]);
+ }
+ break;
+ case F_SET:
+ case F_REPLACE:
+ if (argc < 2 || argc > 6)
+ usage();
+ if (func == F_REPLACE)
+ (void)delete(argv[0], 0);
+ rtn = set(argc, argv) ? 1 : 0;
+ break;
+ case F_DELETE:
+ if (aflag) {
+ if (argc != 0)
+ usage();
+ search(0, nuke_entry);
+ } else {
+ if (argc == 2 && strncmp(argv[1], "pub", 3) == 0)
+ ch = SIN_PROXY;
+ else if (argc == 1)
+ ch = 0;
+ else
+ usage();
+ rtn = delete(argv[0], ch);
+ }
+ break;
+ case F_FILESET:
+ if (argc != 1)
+ usage();
+ rtn = file(argv[0]);
+ break;
+ }
+
+ return (rtn);
+}
+
+/*
+ * Process a file to set standard arp entries
+ */
+static int
+file(char *name)
+{
+ FILE *fp;
+ int i, retval;
+ char line[100], arg[5][50], *args[5], *p;
+
+ if ((fp = fopen(name, "r")) == NULL)
+ err(1, "cannot open %s", name);
+ args[0] = &arg[0][0];
+ args[1] = &arg[1][0];
+ args[2] = &arg[2][0];
+ args[3] = &arg[3][0];
+ args[4] = &arg[4][0];
+ retval = 0;
+ while(fgets(line, sizeof(line), fp) != NULL) {
+ if ((p = strchr(line, '#')) != NULL)
+ *p = '\0';
+ for (p = line; isblank(*p); p++);
+ if (*p == '\n' || *p == '\0')
+ continue;
+ i = sscanf(p, "%49s %49s %49s %49s %49s", arg[0], arg[1],
+ arg[2], arg[3], arg[4]);
+ if (i < 2) {
+ warnx("bad line: %s", line);
+ retval = 1;
+ continue;
+ }
+ if (set(i, args))
+ retval = 1;
+ }
+ fclose(fp);
+ return (retval);
+}
+
+/*
+ * Given a hostname, fills up a (static) struct sockaddr_inarp with
+ * the address of the host and returns a pointer to the
+ * structure.
+ */
+static struct sockaddr_inarp *
+getaddr(char *host)
+{
+ struct hostent *hp;
+ static struct sockaddr_inarp reply;
+
+ bzero(&reply, sizeof(reply));
+ reply.sin_len = sizeof(reply);
+ reply.sin_family = AF_INET;
+ reply.sin_addr.s_addr = inet_addr(host);
+ if (reply.sin_addr.s_addr == INADDR_NONE) {
+ if (!(hp = gethostbyname(host))) {
+ warnx("%s: %s", host, hstrerror(h_errno));
+ return (NULL);
+ }
+ bcopy((char *)hp->h_addr, (char *)&reply.sin_addr,
+ sizeof reply.sin_addr);
+ }
+ return (&reply);
+}
+
+/*
+ * Returns true if the type is a valid one for ARP.
+ */
+static int
+valid_type(int type)
+{
+
+ switch (type) {
+ case IFT_ETHER:
+ case IFT_FDDI:
+ case IFT_ISO88023:
+ case IFT_ISO88024:
+ case IFT_ISO88025:
+ case IFT_L2VLAN:
+ case IFT_BRIDGE:
+ return (1);
+ default:
+ return (0);
+ }
+}
+
+/*
+ * Set an individual arp entry
+ */
+static int
+set(int argc, char **argv)
+{
+ struct sockaddr_inarp *addr;
+ struct sockaddr_inarp *dst; /* what are we looking for */
+ struct sockaddr_dl *sdl;
+ struct rt_msghdr *rtm;
+ struct ether_addr *ea;
+ char *host = argv[0], *eaddr = argv[1];
+ struct sockaddr_dl sdl_m;
+
+ argc -= 2;
+ argv += 2;
+
+ bzero(&sdl_m, sizeof(sdl_m));
+ sdl_m.sdl_len = sizeof(sdl_m);
+ sdl_m.sdl_family = AF_LINK;
+
+ dst = getaddr(host);
+ if (dst == NULL)
+ return (1);
+ doing_proxy = flags = proxy_only = expire_time = 0;
+ while (argc-- > 0) {
+ if (strncmp(argv[0], "temp", 4) == 0) {
+ struct timeval tv;
+ gettimeofday(&tv, 0);
+ expire_time = tv.tv_sec + 20 * 60;
+ } else if (strncmp(argv[0], "pub", 3) == 0) {
+ flags |= RTF_ANNOUNCE;
+ doing_proxy = 1;
+ if (argc && strncmp(argv[1], "only", 3) == 0) {
+ proxy_only = 1;
+ dst->sin_other = SIN_PROXY;
+ argc--; argv++;
+ }
+ } else if (strncmp(argv[0], "blackhole", 9) == 0) {
+ if (flags & RTF_REJECT) {
+ printf("Choose one of blackhole or reject, not both.\n");
+ }
+ flags |= RTF_BLACKHOLE;
+ } else if (strncmp(argv[0], "reject", 6) == 0) {
+ if (flags & RTF_BLACKHOLE) {
+ printf("Choose one of blackhole or reject, not both.\n");
+ }
+ flags |= RTF_REJECT;
+ } else if (strncmp(argv[0], "trail", 5) == 0) {
+ /* XXX deprecated and undocumented feature */
+ printf("%s: Sending trailers is no longer supported\n",
+ host);
+ }
+ argv++;
+ }
+ ea = (struct ether_addr *)LLADDR(&sdl_m);
+ if (doing_proxy && !strcmp(eaddr, "auto")) {
+ if (!get_ether_addr(dst->sin_addr.s_addr, ea)) {
+ printf("no interface found for %s\n",
+ inet_ntoa(dst->sin_addr));
+ return (1);
+ }
+ sdl_m.sdl_alen = ETHER_ADDR_LEN;
+ } else {
+ struct ether_addr *ea1 = ether_aton(eaddr);
+
+ if (ea1 == NULL) {
+ warnx("invalid Ethernet address '%s'", eaddr);
+ return (1);
+ } else {
+ *ea = *ea1;
+ sdl_m.sdl_alen = ETHER_ADDR_LEN;
+ }
+ }
+ for (;;) { /* try at most twice */
+ rtm = rtmsg(RTM_GET, dst, &sdl_m);
+ if (rtm == NULL) {
+ warn("%s", host);
+ return (1);
+ }
+ addr = (struct sockaddr_inarp *)(rtm + 1);
+ sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr);
+ if (addr->sin_addr.s_addr != dst->sin_addr.s_addr)
+ break;
+ if (sdl->sdl_family == AF_LINK &&
+ !(rtm->rtm_flags & RTF_GATEWAY) &&
+ valid_type(sdl->sdl_type) )
+ break;
+ if (doing_proxy == 0) {
+ printf("set: can only proxy for %s\n", host);
+ return (1);
+ }
+ if (dst->sin_other & SIN_PROXY) {
+ printf("set: proxy entry exists for non 802 device\n");
+ return (1);
+ }
+ dst->sin_other = SIN_PROXY;
+ proxy_only = 1;
+ }
+
+ if (sdl->sdl_family != AF_LINK) {
+ printf("cannot intuit interface index and type for %s\n", host);
+ return (1);
+ }
+ sdl_m.sdl_type = sdl->sdl_type;
+ sdl_m.sdl_index = sdl->sdl_index;
+ return (rtmsg(RTM_ADD, dst, &sdl_m) == NULL);
+}
+
+/*
+ * Display an individual arp entry
+ */
+static int
+get(char *host)
+{
+ struct sockaddr_inarp *addr;
+
+ addr = getaddr(host);
+ if (addr == NULL)
+ return (1);
+ if (0 == search(addr->sin_addr.s_addr, print_entry)) {
+ printf("%s (%s) -- no entry",
+ host, inet_ntoa(addr->sin_addr));
+ if (rifname)
+ printf(" on %s", rifname);
+ printf("\n");
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Delete an arp entry
+ */
+static int
+delete(char *host, int do_proxy)
+{
+ struct sockaddr_inarp *addr, *dst;
+ struct rt_msghdr *rtm;
+ struct sockaddr_dl *sdl;
+ struct sockaddr_dl sdl_m;
+
+ dst = getaddr(host);
+ if (dst == NULL)
+ return (1);
+ dst->sin_other = do_proxy;
+
+ /*
+ * setup the data structure to notify the kernel
+ * it is the ARP entry the RTM_GET is interested
+ * in
+ */
+ bzero(&sdl_m, sizeof(sdl_m));
+ sdl_m.sdl_len = sizeof(sdl_m);
+ sdl_m.sdl_family = AF_LINK;
+
+ for (;;) { /* try twice */
+ rtm = rtmsg(RTM_GET, dst, &sdl_m);
+ if (rtm == NULL) {
+ warn("%s", host);
+ return (1);
+ }
+ addr = (struct sockaddr_inarp *)(rtm + 1);
+ sdl = (struct sockaddr_dl *)(SA_SIZE(addr) + (char *)addr);
+
+ /*
+ * With the new L2/L3 restructure, the route
+ * returned is a prefix route. The important
+ * piece of information from the previous
+ * RTM_GET is the interface index. In the
+ * case of ECMP, the kernel will traverse
+ * the route group for the given entry.
+ */
+ if (sdl->sdl_family == AF_LINK &&
+ !(rtm->rtm_flags & RTF_GATEWAY) &&
+ valid_type(sdl->sdl_type) ) {
+ addr->sin_addr.s_addr = dst->sin_addr.s_addr;
+ break;
+ }
+
+ if (dst->sin_other & SIN_PROXY) {
+ fprintf(stderr, "delete: cannot locate %s\n",host);
+ return (1);
+ }
+ dst->sin_other = SIN_PROXY;
+ }
+ rtm->rtm_flags |= RTF_LLDATA;
+ if (rtmsg(RTM_DELETE, dst, NULL) != NULL) {
+ printf("%s (%s) deleted\n", host, inet_ntoa(addr->sin_addr));
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * Search the arp table and do some action on matching entries
+ */
+static int
+search(u_long addr, action_fn *action)
+{
+ int mib[6];
+ size_t needed;
+ char *lim, *buf, *newbuf, *next;
+ struct rt_msghdr *rtm;
+ struct sockaddr_inarp *sin2;
+ struct sockaddr_dl *sdl;
+ char ifname[IF_NAMESIZE];
+ int st, found_entry = 0;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET;
+ mib[4] = NET_RT_FLAGS;
+#ifdef RTF_LLINFO
+ mib[5] = RTF_LLINFO;
+#else
+ mib[5] = 0;
+#endif
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
+ err(1, "route-sysctl-estimate");
+ if (needed == 0) /* empty table */
+ return 0;
+ buf = NULL;
+ for (;;) {
+ newbuf = realloc(buf, needed);
+ if (newbuf == NULL) {
+ if (buf != NULL)
+ free(buf);
+ errx(1, "could not reallocate memory");
+ }
+ buf = newbuf;
+ st = sysctl(mib, 6, buf, &needed, NULL, 0);
+ if (st == 0 || errno != ENOMEM)
+ break;
+ needed += needed / 8;
+ }
+ if (st == -1)
+ err(1, "actual retrieval of routing table");
+ lim = buf + needed;
+ for (next = buf; next < lim; next += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)next;
+ sin2 = (struct sockaddr_inarp *)(rtm + 1);
+ sdl = (struct sockaddr_dl *)((char *)sin2 + SA_SIZE(sin2));
+ if (rifname && if_indextoname(sdl->sdl_index, ifname) &&
+ strcmp(ifname, rifname))
+ continue;
+ if (addr) {
+ if (addr != sin2->sin_addr.s_addr)
+ continue;
+ found_entry = 1;
+ }
+ (*action)(sdl, sin2, rtm);
+ }
+ free(buf);
+ return (found_entry);
+}
+
+/*
+ * Display an arp entry
+ */
+static void
+print_entry(struct sockaddr_dl *sdl,
+ struct sockaddr_inarp *addr, struct rt_msghdr *rtm)
+{
+ const char *host;
+ struct hostent *hp;
+ struct iso88025_sockaddr_dl_data *trld;
+ char ifname[IF_NAMESIZE];
+ int seg;
+
+ if (nflag == 0)
+ hp = gethostbyaddr((caddr_t)&(addr->sin_addr),
+ sizeof addr->sin_addr, AF_INET);
+ else
+ hp = 0;
+ if (hp)
+ host = hp->h_name;
+ else {
+ host = "?";
+ if (h_errno == TRY_AGAIN)
+ nflag = 1;
+ }
+ printf("%s (%s) at ", host, inet_ntoa(addr->sin_addr));
+ if (sdl->sdl_alen) {
+ if ((sdl->sdl_type == IFT_ETHER ||
+ sdl->sdl_type == IFT_L2VLAN ||
+ sdl->sdl_type == IFT_BRIDGE) &&
+ sdl->sdl_alen == ETHER_ADDR_LEN)
+ printf("%s", ether_ntoa((struct ether_addr *)LLADDR(sdl)));
+ else {
+ int n = sdl->sdl_nlen > 0 ? sdl->sdl_nlen + 1 : 0;
+
+ printf("%s", link_ntoa(sdl) + n);
+ }
+ } else
+ printf("(incomplete)");
+ if (if_indextoname(sdl->sdl_index, ifname) != NULL)
+ printf(" on %s", ifname);
+ if (rtm->rtm_rmx.rmx_expire == 0)
+ printf(" permanent");
+ if (addr->sin_other & SIN_PROXY)
+ printf(" published (proxy only)");
+ if (rtm->rtm_flags & RTF_ANNOUNCE)
+ printf(" published");
+ switch(sdl->sdl_type) {
+ case IFT_ETHER:
+ printf(" [ethernet]");
+ break;
+ case IFT_ISO88025:
+ printf(" [token-ring]");
+ trld = SDL_ISO88025(sdl);
+ if (trld->trld_rcf != 0) {
+ printf(" rt=%x", ntohs(trld->trld_rcf));
+ for (seg = 0;
+ seg < ((TR_RCF_RIFLEN(trld->trld_rcf) - 2 ) / 2);
+ seg++)
+ printf(":%x", ntohs(*(trld->trld_route[seg])));
+ }
+ break;
+ case IFT_FDDI:
+ printf(" [fddi]");
+ break;
+ case IFT_ATM:
+ printf(" [atm]");
+ break;
+ case IFT_L2VLAN:
+ printf(" [vlan]");
+ break;
+ case IFT_IEEE1394:
+ printf(" [firewire]");
+ break;
+ case IFT_BRIDGE:
+ printf(" [bridge]");
+ break;
+ default:
+ break;
+ }
+
+ printf("\n");
+
+}
+
+/*
+ * Nuke an arp entry
+ */
+static void
+nuke_entry(struct sockaddr_dl *sdl __unused,
+ struct sockaddr_inarp *addr, struct rt_msghdr *rtm __unused)
+{
+ char ip[20];
+
+ snprintf(ip, sizeof(ip), "%s", inet_ntoa(addr->sin_addr));
+ (void)delete(ip, 0);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
+ "usage: arp [-n] [-i interface] hostname",
+ " arp [-n] [-i interface] -a",
+ " arp -d hostname [pub]",
+ " arp -d [-i interface] -a",
+ " arp -s hostname ether_addr [temp] [reject | blackhole] [pub [only]]",
+ " arp -S hostname ether_addr [temp] [reject | blackhole] [pub [only]]",
+ " arp -f filename");
+ exit(1);
+}
+
+static struct rt_msghdr *
+rtmsg(int cmd, struct sockaddr_inarp *dst, struct sockaddr_dl *sdl)
+{
+ static int seq;
+ int rlen;
+ int l;
+ struct sockaddr_in so_mask, *som = &so_mask;
+ static int s = -1;
+ static pid_t pid;
+
+ static struct {
+ struct rt_msghdr m_rtm;
+ char m_space[512];
+ } m_rtmsg;
+
+ struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
+ char *cp = m_rtmsg.m_space;
+
+ if (s < 0) { /* first time: open socket, get pid */
+ s = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (s < 0)
+ err(1, "socket");
+ pid = getpid();
+ }
+ bzero(&so_mask, sizeof(so_mask));
+ so_mask.sin_len = 8;
+ so_mask.sin_addr.s_addr = 0xffffffff;
+
+ errno = 0;
+ /*
+ * XXX RTM_DELETE relies on a previous RTM_GET to fill the buffer
+ * appropriately.
+ */
+ if (cmd == RTM_DELETE)
+ goto doit;
+ bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
+ rtm->rtm_flags = flags;
+ rtm->rtm_version = RTM_VERSION;
+
+ switch (cmd) {
+ default:
+ errx(1, "internal wrong cmd");
+ case RTM_ADD:
+ rtm->rtm_addrs |= RTA_GATEWAY;
+ rtm->rtm_rmx.rmx_expire = expire_time;
+ rtm->rtm_inits = RTV_EXPIRE;
+ rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA);
+ dst->sin_other = 0;
+ if (doing_proxy) {
+ if (proxy_only)
+ dst->sin_other = SIN_PROXY;
+ else {
+ rtm->rtm_addrs |= RTA_NETMASK;
+ rtm->rtm_flags &= ~RTF_HOST;
+ }
+ }
+ /* FALLTHROUGH */
+ case RTM_GET:
+ rtm->rtm_addrs |= RTA_DST;
+ }
+#define NEXTADDR(w, s) \
+ if ((s) != NULL && rtm->rtm_addrs & (w)) { \
+ bcopy((s), cp, sizeof(*(s))); cp += SA_SIZE(s);}
+
+ NEXTADDR(RTA_DST, dst);
+ NEXTADDR(RTA_GATEWAY, sdl);
+ NEXTADDR(RTA_NETMASK, som);
+
+ rtm->rtm_msglen = cp - (char *)&m_rtmsg;
+doit:
+ l = rtm->rtm_msglen;
+ rtm->rtm_seq = ++seq;
+ rtm->rtm_type = cmd;
+ if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
+ if (errno != ESRCH || cmd != RTM_DELETE) {
+ warn("writing to routing socket");
+ return (NULL);
+ }
+ }
+ 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");
+ return (rtm);
+}
+
+/*
+ * get_ether_addr - get the hardware address of an interface on the
+ * the same subnet as ipaddr.
+ */
+#define MAX_IFS 32
+
+static int
+get_ether_addr(in_addr_t ipaddr, struct ether_addr *hwaddr)
+{
+ struct ifreq *ifr, *ifend, *ifp;
+ in_addr_t ina, mask;
+ struct sockaddr_dl *dla;
+ struct ifreq ifreq;
+ struct ifconf ifc;
+ struct ifreq ifs[MAX_IFS];
+ int sock;
+ int retval = 0;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0)
+ err(1, "socket");
+
+ ifc.ifc_len = sizeof(ifs);
+ ifc.ifc_req = ifs;
+ if (ioctl(sock, SIOCGIFCONF, &ifc) < 0) {
+ warnx("ioctl(SIOCGIFCONF)");
+ goto done;
+ }
+
+#define NEXTIFR(i) \
+ ((struct ifreq *)((char *)&(i)->ifr_addr \
+ + MAX((i)->ifr_addr.sa_len, sizeof((i)->ifr_addr))) )
+
+ /*
+ * Scan through looking for an interface with an Internet
+ * address on the same subnet as `ipaddr'.
+ */
+ ifend = (struct ifreq *)(ifc.ifc_buf + ifc.ifc_len);
+ for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr) ) {
+ if (ifr->ifr_addr.sa_family != AF_INET)
+ continue;
+ strncpy(ifreq.ifr_name, ifr->ifr_name,
+ sizeof(ifreq.ifr_name));
+ ifreq.ifr_addr = ifr->ifr_addr;
+ /*
+ * Check that the interface is up,
+ * and not point-to-point or loopback.
+ */
+ if (ioctl(sock, SIOCGIFFLAGS, &ifreq) < 0)
+ continue;
+ if ((ifreq.ifr_flags &
+ (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|
+ IFF_LOOPBACK|IFF_NOARP))
+ != (IFF_UP|IFF_BROADCAST))
+ continue;
+ /*
+ * Get its netmask and check that it's on
+ * the right subnet.
+ */
+ if (ioctl(sock, SIOCGIFNETMASK, &ifreq) < 0)
+ continue;
+ mask = ((struct sockaddr_in *)
+ &ifreq.ifr_addr)->sin_addr.s_addr;
+ ina = ((struct sockaddr_in *)
+ &ifr->ifr_addr)->sin_addr.s_addr;
+ if ((ipaddr & mask) == (ina & mask))
+ break; /* ok, we got it! */
+ }
+
+ if (ifr >= ifend)
+ goto done;
+
+ /*
+ * Now scan through again looking for a link-level address
+ * for this interface.
+ */
+ ifp = ifr;
+ for (ifr = ifc.ifc_req; ifr < ifend; ifr = NEXTIFR(ifr))
+ if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0 &&
+ ifr->ifr_addr.sa_family == AF_LINK)
+ break;
+ if (ifr >= ifend)
+ goto done;
+ /*
+ * Found the link-level address - copy it out
+ */
+ dla = (struct sockaddr_dl *) &ifr->ifr_addr;
+ memcpy(hwaddr, LLADDR(dla), dla->sdl_alen);
+ printf("using interface %s for proxy with address ",
+ ifp->ifr_name);
+ printf("%s\n", ether_ntoa(hwaddr));
+ retval = dla->sdl_alen;
+done:
+ close(sock);
+ return (retval);
+}
diff --git a/usr.sbin/asf/Makefile b/usr.sbin/asf/Makefile
new file mode 100644
index 0000000..b4b7af3
--- /dev/null
+++ b/usr.sbin/asf/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+PROG= asf
+SRCS= asf.c asf_kld.c asf_kvm.c asf_prog.c
+MAN= asf.8
+
+DPADD= ${LIBKVM}
+LDADD= -lkvm
+
+WARNS?= 4
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/asf/asf.8 b/usr.sbin/asf/asf.8
new file mode 100644
index 0000000..25b0308
--- /dev/null
+++ b/usr.sbin/asf/asf.8
@@ -0,0 +1,179 @@
+.\" Copyright (c) 2003 Greg Lehey. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 Greg Lehey ``as is'' and
+.\" any express or implied warranties, including, but not limited to, the
+.\" implied warranties of merchantability and fitness for a particular purpose
+.\" are disclaimed. in no event shall Greg Lehey be liable
+.\" for any direct, indirect, incidental, special, exemplary, or consequential
+.\" damages (including, but not limited to, procurement of substitute goods
+.\" or services; loss of use, data, or profits; or business interruption)
+.\" however caused and on any theory of liability, whether in contract, strict
+.\" liability, or tort (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 20, 2006
+.Os
+.Dt ASF 8
+.Sh NAME
+.Nm asf
+.Nd add symbol files
+.Sh SYNOPSIS
+.Nm
+.Op Fl afKksVx
+.Op Fl M Ar core
+.Op Fl N Ar system
+.Op Fl o Ar outfile
+.Op Fl X Ar suffix
+.Op Ar modules-path Op Ar outfile
+.Sh DESCRIPTION
+By default,
+.Nm
+reads
+.Xr kldstat 8
+output from standard input and writes to the
+.Pa .asf
+file a list of
+.Xr gdb 1
+commands to add symbol files from KLDs in subdirectories of the subdirectory
+.Pa modules
+of the current directory, which is intended to be a kernel build directory.
+This allows
+.Xr gdb 1
+to load the symbols into the debugging environment.
+.Pp
+An optional
+.Ar modules-path
+argument can specify a semicolon-separated list of directory pathnames
+similar to the
+.Va kern.module_path
+sysctl.
+Each directory in the list will be searched in turn for modules.
+The default list consists of just one element,
+.Pa modules ,
+which is suitable if the current directory is a kernel build directory.
+.Pp
+If
+.Ar outfile
+is specified,
+.Nm
+writes to it instead of
+.Pa .asf .
+If
+.Ar outfile
+is a single dash
+.Pq Sq Fl ,
+standard output is used.
+.Sh OPTIONS
+The following options modify the function of
+.Nm :
+.Bl -tag -width indent
+.It Fl a
+When writing to an explicit
+.Ar outfile ,
+append to the file rather than overwriting it.
+.It Fl f
+Instead of trying to simplistically guess the path for each module, perform
+a traversal in the same way that
+.Xr find 1
+does to locate an exact path for each module, no matter where in
+.Ar modules-path
+it is located.
+.It Fl K
+Instead of reading from standard input, use the conventional
+system interface to get the list of modules currently loaded.
+.It Fl k
+Instead of reading from standard input, start a
+.Xr kldstat 8
+and read the information from it.
+.It Fl M
+Specify the core file for
+.Xr kvm 3 .
+Implies
+.Fl V .
+.It Fl N
+Specify the system file for
+.Xr kvm 3 .
+Implies
+.Fl V .
+.It Fl o
+Specify the file for
+.Nm
+to write or append its output to.
+If
+.Ar outfile
+is a single dash
+.Pq Sq Fl ,
+standard output is used.
+.It Fl s
+Do not prepend a (guessed) subdirectory of the module path.
+.It Fl V
+Instead of reading from standard input, use the
+.Xr kvm 3
+interface to get the list of modules.
+This interface allows for inspecting system crash dumps,
+as well as the live system.
+The
+.Fl M
+and
+.Fl N
+options will be of use if inspecting a crash dump.
+Elevated privileges, e.g., those of a superuser,
+may be needed to use this option.
+.It Fl X
+Add
+.Ar suffix
+to the list of suffixes
+.Nm
+tries to append to KLD file names.
+The default list consists of
+.Pa .debug ,
+.Pa .symbols ,
+and the null suffix.
+The null suffix always stays at the list tail, after the suffix added.
+Should it be needed in the middle of the list,
+a blank suffix can be specified to
+.Fl X
+instead.
+.It Fl x
+Clear the list of suffixes
+.Nm
+tries to append to KLD file names.
+Only the null suffix is left in the list.
+.El
+.Sh EXAMPLES
+To add symbol files from the system search path specified by the
+.Va kern.module_path
+sysctl, the following command can be used:
+.Pp
+.Dl asf -s `sysctl -n kern.module_path`
+.Sh SEE ALSO
+.Xr gdb 1 ,
+.Xr kvm 3 ,
+.Xr kld 4 ,
+.Xr kldstat 8 ,
+.Xr sysctl 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 5.2 .
+.Sh AUTHORS
+.An Greg Lehey Aq grog@FreeBSD.org
+.Sh BUGS
+Module paths are guessed in a rather naive way by default.
+It is likely to lag behind the changes to the build tree layout.
+Using
+.Fl f
+is recommended.
diff --git a/usr.sbin/asf/asf.c b/usr.sbin/asf/asf.c
new file mode 100644
index 0000000..c913e3d
--- /dev/null
+++ b/usr.sbin/asf/asf.c
@@ -0,0 +1,427 @@
+/*-
+ * Copyright (c) 2002, 2003 Greg Lehey
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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.
+ */
+/* $Id: asf.c,v 1.4 2003/05/04 02:55:20 grog Exp grog $ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "asf.h"
+
+struct kfile {
+ char *name;
+ caddr_t addr;
+ int seen;
+ STAILQ_ENTRY(kfile) link;
+};
+
+static STAILQ_HEAD(,kfile) kfile_head = STAILQ_HEAD_INITIALIZER(kfile_head);
+
+void
+kfile_add(const char *name, caddr_t addr)
+{
+ struct kfile *kfp;
+
+ if ((kfp = malloc(sizeof(*kfp))) == NULL ||
+ (kfp->name = strdup(name)) == NULL)
+ errx(2, "out of memory");
+ kfp->addr = addr;
+ kfp->seen = 0;
+ STAILQ_INSERT_TAIL(&kfile_head, kfp, link);
+}
+
+static struct kfile *
+kfile_find(const char *name)
+{
+ struct kfile *kfp;
+
+ STAILQ_FOREACH(kfp, &kfile_head, link)
+ if (strcmp(kfp->name, name) == 0)
+ return (kfp); /* found */
+
+ return (NULL); /* not found */
+}
+
+static int
+kfile_allseen(void)
+{
+ struct kfile *kfp;
+
+ STAILQ_FOREACH(kfp, &kfile_head, link)
+ if (!kfp->seen)
+ return (0); /* at least one unseen */
+
+ return (1); /* all seen */
+}
+
+static int
+kfile_empty(void)
+{
+ return (STAILQ_EMPTY(&kfile_head));
+}
+
+/*
+ * Take a blank separated list of tokens and turn it into a list of
+ * individual nul-delimited strings. Build a list of pointers at
+ * token, which must have enough space for the tokens. Return the
+ * number of tokens, or -1 on error (typically a missing string
+ * delimiter).
+ */
+int
+tokenize(char *cptr, char *token[], int maxtoken)
+{
+ char delim; /* delimiter to search for */
+ int tokennr; /* index of this token */
+
+ for (tokennr = 0; tokennr < maxtoken;) {
+ while (isspace(*cptr))
+ cptr++; /* skip initial white space */
+ if ((*cptr == '\0') || (*cptr == '\n')
+ || (*cptr == '#')) /* end of line */
+ return tokennr; /* return number of tokens found */
+ delim = *cptr;
+ token[tokennr] = cptr; /* point to it */
+ tokennr++; /* one more */
+ if (tokennr == maxtoken) /* run off the end? */
+ return tokennr;
+ if ((delim == '\'') || (delim == '"')) { /* delimitered */
+ for (;;) {
+ cptr++;
+ if ((*cptr == delim)
+ && (cptr[-1] != '\\')) { /* found the partner */
+ cptr++; /* move on past */
+ if (!isspace(*cptr)) /* no space after closing quote */
+ return -1;
+ *cptr++ = '\0'; /* delimit */
+ } else if ((*cptr == '\0')
+ || (*cptr == '\n')) /* end of line */
+ return -1;
+ }
+ } else { /* not quoted */
+ while ((*cptr != '\0') && (!isspace(*cptr)) && (*cptr != '\n'))
+ cptr++;
+ if (*cptr != '\0') /* not end of the line, */
+ *cptr++ = '\0'; /* delimit and move to the next */
+ }
+ }
+ return maxtoken; /* can't get here */
+}
+
+static void
+doobj(const char *path, caddr_t addr, FILE *out)
+{
+ uintmax_t base = (uintptr_t)addr;
+ uintmax_t textaddr = 0;
+ uintmax_t dataaddr = 0;
+ uintmax_t bssaddr = 0;
+ uintmax_t *up;
+ int octokens;
+ char *octoken[MAXTOKEN];
+ char ocbuf[LINE_MAX + PATH_MAX];
+ FILE *objcopy;
+
+ snprintf(ocbuf, sizeof(ocbuf),
+ "/usr/bin/objdump --section-headers %s", path);
+ if ((objcopy = popen(ocbuf, "r")) == NULL)
+ err(2, "can't start %s", ocbuf);
+ while (fgets(ocbuf, sizeof(ocbuf), objcopy)) {
+ octokens = tokenize(ocbuf, octoken, MAXTOKEN);
+ if (octokens <= 1)
+ continue;
+ up = NULL;
+ if (strcmp(octoken[1], ".text") == 0)
+ up = &textaddr;
+ else if (strcmp(octoken[1], ".data") == 0)
+ up = &dataaddr;
+ else if (strcmp(octoken[1], ".bss") == 0)
+ up = &bssaddr;
+ if (up == NULL)
+ continue;
+ *up = strtoumax(octoken[3], NULL, 16) + base;
+ }
+ if (textaddr) { /* we must have a text address */
+ fprintf(out, "add-symbol-file %s 0x%jx", path, textaddr);
+ if (dataaddr)
+ fprintf(out, " -s .data 0x%jx", dataaddr);
+ if (bssaddr)
+ fprintf(out, " -s .bss 0x%jx", bssaddr);
+ fprintf(out, "\n");
+ }
+}
+
+static void
+findmodules(char *path_argv[], const char *sfx[], FILE *out)
+{
+ char *p;
+ FTS *fts;
+ FTSENT *ftsent;
+ struct kfile *kfp;
+ int i;
+ int sl;
+
+ /* Have to fts once per suffix to find preferred suffixes first */
+ do {
+ sl = *sfx ? strlen(*sfx) : 0; /* current suffix length */
+ fts = fts_open(path_argv, FTS_PHYSICAL | FTS_NOCHDIR, NULL);
+ if (fts == NULL)
+ err(2, "can't begin traversing module path");
+ while ((ftsent = fts_read(fts)) != NULL) {
+ if (ftsent->fts_info == FTS_DNR ||
+ ftsent->fts_info == FTS_ERR ||
+ ftsent->fts_info == FTS_NS) {
+ errno = ftsent->fts_errno;
+ err(2, "error while traversing path %s", ftsent->fts_path);
+ }
+ if (ftsent->fts_info != FTS_F)
+ continue; /* not a plain file */
+
+ if (sl > 0) {
+ /* non-blank suffix; see if file name has it */
+ i = ftsent->fts_namelen - sl;
+ if (i <= 0 || strcmp(ftsent->fts_name + i, *sfx) != 0)
+ continue; /* no such suffix */
+ if ((p = strdup(ftsent->fts_name)) == NULL)
+ errx(2, "out of memory");
+ p[i] = '\0'; /* remove suffix in the copy */
+ kfp = kfile_find(p);
+ free(p);
+ } else
+ kfp = kfile_find(ftsent->fts_name);
+
+ if (kfp && !kfp->seen) {
+ doobj(ftsent->fts_path, kfp->addr, out);
+ kfp->seen = 1;
+ /* Optimization: stop fts as soon as seen all loaded modules */
+ if (kfile_allseen()) {
+ fts_close(fts);
+ return;
+ }
+ }
+ }
+ if (ftsent == NULL && errno != 0)
+ err(2, "couldn't complete traversing module path");
+ fts_close(fts);
+ } while (*sfx++);
+}
+
+static void
+usage(const char *myname)
+{
+ fprintf(stderr,
+ "Usage:\n"
+ "%s [-afKksVx] [-M core] [-N system] [-o outfile] [-X suffix]\n"
+ "%*s [modules-path [outfile]]\n\n"
+ "\t-a\tappend to outfile\n"
+ "\t-f\tfind the module in any subdirectory of modules-path\n"
+ "\t-K\tuse kld(2) to get the list of modules\n"
+ "\t-k\ttake input from kldstat(8)\n"
+ "\t-M\tspecify core name for kvm(3)\n"
+ "\t-N\tspecify system name for kvm(3)\n"
+ "\t-o\tuse outfile instead of \".asf\"\n"
+ "\t-s\tdon't prepend subdir for module path\n"
+ "\t-V\tuse kvm(3) to get the list of modules\n"
+ "\t-X\tappend suffix to list of possible module file name suffixes\n"
+ "\t-x\tclear list of possible module file name suffixes\n",
+ myname, (int)strlen(myname), "");
+ exit(2);
+}
+
+#define MAXPATHS 15
+#define MAXSUFFIXES 15
+
+/* KLD file names end in this */
+static int nsuffixes = 2;
+static const char *suffixes[MAXSUFFIXES + 1] = {
+ ".debug",
+ ".symbols",
+ NULL
+};
+
+int
+main(int argc, char *argv[])
+{
+ char basename[PATH_MAX];
+ char path[PATH_MAX];
+ char *modules_argv[MAXPATHS + 1];
+ char *copy, *p;
+ char **ap;
+ const char *filemode = "w"; /* mode for outfile */
+ const char *modules_path = "modules"; /* path to kernel build directory */
+ const char *outfile = ".asf"; /* and where to write the output */
+ const char *corefile = NULL; /* for kvm(3) */
+ const char *sysfile = NULL; /* for kvm(3) */
+ const char **sfx;
+ struct kfile *kfp;
+ struct stat st;
+ FILE *out; /* output file */
+ int dofind = 0;
+ int dokld = 0;
+ int dokvm = 0;
+ int nosubdir = 0;
+ int runprog = 0;
+ int i;
+ const int sl = strlen(KLDSUFFIX);
+
+ while ((i = getopt(argc, argv, "afKkM:N:o:sVX:x")) != -1)
+ switch (i) {
+ case 'a':
+ filemode = "a"; /* append to outfile */
+ break;
+ case 'f':
+ dofind = 1; /* find .ko (recursively) */
+ break;
+ case 'K':
+ dokld = 1; /* use kld(2) interface */
+ break;
+ case 'k':
+ runprog = 1; /* get input from kldstat(8) */
+ break;
+ case 'M':
+ corefile = optarg; /* core file for kvm(3) */
+ break;
+ case 'N':
+ sysfile = optarg; /* system file (kernel) for kvm(3) */
+ break;
+ case 'o':
+ outfile = optarg; /* output file name */
+ break;
+ case 's':
+ nosubdir = 1; /* don't descend into subdirs */
+ break;
+ case 'V':
+ dokvm = 1; /* use kvm(3) interface */
+ break;
+ case 'X':
+ if (nsuffixes >= MAXSUFFIXES)
+ errx(2, "only %d suffixes can be specified", MAXSUFFIXES);
+ suffixes[nsuffixes++] = optarg;
+ suffixes[nsuffixes] = NULL;
+ break;
+ case 'x':
+ nsuffixes = 0;
+ suffixes[0] = NULL;
+ break;
+ default:
+ usage(argv[0]);
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 0) {
+ modules_path = argv[0];
+ argc--, argv++;
+ }
+ if (argc > 0) {
+ outfile = argv[0];
+ argc--, argv++;
+ }
+ if (argc > 0)
+ usage(argv[0]);
+
+ if (strcmp(outfile, "-") == 0)
+ out = stdout;
+ else
+ if ((out = fopen(outfile, filemode)) == NULL)
+ err(2, "can't open output file %s", outfile);
+
+ if (dokvm || corefile || sysfile) {
+ if (dokld || runprog)
+ warnx("using kvm(3) instead");
+ asf_kvm(sysfile, corefile);
+ } else if (dokld) {
+ if (runprog)
+ warnx("using kld(2) instead");
+ asf_kld();
+ } else
+ asf_prog(runprog);
+
+ /* Avoid long operations like module tree traversal when nothing to do */
+ if (kfile_empty()) {
+ warnx("no kernel modules loaded");
+ return (0);
+ }
+
+ if ((copy = strdup(modules_path)) == NULL)
+ errx(2, "out of memory");
+ for (
+ ap = modules_argv, p = copy;
+ (*ap = strsep(&p, ";")) != NULL && ap < &modules_argv[MAXPATHS];
+ ap++
+ );
+ if (*ap)
+ errx(2, "only %d module path elements can be specified", MAXPATHS);
+
+ if (!dofind)
+ STAILQ_FOREACH(kfp, &kfile_head, link) {
+ for (ap = modules_argv; *ap; ap++) {
+ if (!nosubdir) {
+ /* prepare basename of KLD, w/o suffix */
+ strlcpy(basename, kfp->name, sizeof(basename) - 1);
+ i = strlen(basename);
+ if (i > sl && strcmp(basename + i - sl, KLDSUFFIX) == 0)
+ i -= sl;
+ basename[i] = '/';
+ basename[i + 1] = '\0';
+ }
+ for (sfx = suffixes;; sfx++) {
+ snprintf(path, sizeof(path),
+ "%s/%s%s%s",
+ *ap,
+ nosubdir ? "" : basename,
+ kfp->name,
+ *sfx ? *sfx : "");
+ if (stat(path, &st) == 0) {
+ doobj(path, kfp->addr, out);
+ goto found;
+ }
+ if (*sfx == NULL)
+ break;
+ }
+ }
+ warnx("module %s not found in search path", kfp->name);
+found:
+ ;
+ }
+ else
+ findmodules(modules_argv, suffixes, out);
+
+ free(copy);
+ return (0);
+}
diff --git a/usr.sbin/asf/asf.h b/usr.sbin/asf/asf.h
new file mode 100644
index 0000000..d034936
--- /dev/null
+++ b/usr.sbin/asf/asf.h
@@ -0,0 +1,40 @@
+/*-
+ * Copyright (c) 2006 The FreeBSD Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define KERNFILE "kernel"
+#define KLDSUFFIX ".ko"
+
+#define MAXTOKEN 10
+
+int tokenize(char *cptr, char *token[], int maxtoken);
+
+void kfile_add(const char *name, caddr_t addr);
+
+void asf_kld(void);
+void asf_kvm(const char *kernfile, const char *corefile);
+void asf_prog(int run);
diff --git a/usr.sbin/asf/asf_kld.c b/usr.sbin/asf/asf_kld.c
new file mode 100644
index 0000000..1d6a2b7
--- /dev/null
+++ b/usr.sbin/asf/asf_kld.c
@@ -0,0 +1,59 @@
+/*-
+ * Copyright (c) 2006 The FreeBSD Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <err.h>
+#include <string.h>
+
+#include "asf.h"
+
+/*
+ * Get the linker file list using the kld interface.
+ * Works with a live kernel only.
+ */
+void
+asf_kld()
+{
+ struct kld_file_stat kfs;
+ int fid = 0; /* indicates the beginning of the linker file list */
+
+ while ((fid = kldnext(fid)) != 0) {
+ if (fid == -1)
+ err(2, "kldnext");
+ kfs.version = sizeof(kfs); /* must be set for kldstat(2) */
+ /* Get info on this linker file */
+ if (kldstat(fid, &kfs) == -1)
+ err(2, "kldstat");
+ if (strcmp(kfs.name, KERNFILE) == 0)
+ continue;
+ /* Add to our list of linker files */
+ kfile_add(kfs.name, kfs.address);
+ }
+}
diff --git a/usr.sbin/asf/asf_kvm.c b/usr.sbin/asf/asf_kvm.c
new file mode 100644
index 0000000..aa4effe
--- /dev/null
+++ b/usr.sbin/asf/asf_kvm.c
@@ -0,0 +1,128 @@
+/*-
+ * Copyright (c) 2006 The FreeBSD Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/queue.h> /* for <sys/linker.h> with _KERNEL defined */
+#include <err.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <limits.h>
+#include <nlist.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define _KERNEL
+#include <sys/linker.h>
+#undef _KERNEL
+
+#include "asf.h"
+
+/* Name of the head of the linker file list in /sys/kern/kern_linker.c */
+#define LINKER_HEAD "linker_files"
+
+/*
+ * Get the list of linker files using kvm(3).
+ * Can work with a live kernel as well as with a crash dump.
+ */
+void
+asf_kvm(const char *kernfile, const char *corefile)
+{
+ char errbuf[LINE_MAX];
+ char name[PATH_MAX];
+ kvm_t *kd;
+ struct nlist nl[2];
+ struct linker_file lf;
+ linker_file_list_t linker_files;
+ ssize_t n;
+ void *kp;
+
+ kd = kvm_openfiles(kernfile, corefile, NULL, O_RDONLY, errbuf);
+ if (kd == NULL)
+ errx(2, "open kernel memory: %s", errbuf);
+
+ /*
+ * Locate the head of the linker file list using kernel symbols.
+ */
+ strcpy(name, LINKER_HEAD);
+ nl[0].n_name = name; /* can't use LINKER_HEAD here because it's const */
+ nl[1].n_name = NULL; /* terminate the array for kvm_nlist() */
+ switch (kvm_nlist(kd, nl)) {
+ case 0:
+ break;
+ case -1:
+ warnx("%s: %s", LINKER_HEAD, kvm_geterr(kd));
+ kvm_close(kd);
+ exit(2);
+ default:
+ kvm_close(kd);
+ errx(2, "%s: symbol not found", LINKER_HEAD);
+ }
+
+ /*
+ * Read the head of the linker file list from kernel memory.
+ */
+ n = kvm_read(kd, nl[0].n_value, &linker_files, sizeof(linker_files));
+ if (n == -1)
+ goto read_err;
+ if (n != sizeof(linker_files)) {
+ kvm_close(kd);
+ errx(2, "%s: short read", LINKER_HEAD);
+ }
+
+ /*
+ * Traverse the linker file list starting at its head.
+ */
+ for (kp = linker_files.tqh_first; kp; kp = lf.link.tqe_next) {
+ /* Read a linker file structure */
+ n = kvm_read(kd, (u_long)kp, &lf, sizeof(lf));
+ if (n == -1)
+ goto read_err;
+ if (n != sizeof(lf)) {
+ kvm_close(kd);
+ errx(2, "kvm: short read");
+ }
+ /* Read the name of the file stored separately */
+ bzero(name, sizeof(name));
+ n = kvm_read(kd, (u_long)lf.filename, name, sizeof(name) - 1);
+ if (n == -1)
+ goto read_err;
+ if (strcmp(name, KERNFILE) == 0)
+ continue;
+ /* Add this file to our list of linker files */
+ kfile_add(name, lf.address);
+ }
+ kvm_close(kd);
+ return;
+
+read_err: /* A common error case */
+ warnx("read kernel memory: %s", kvm_geterr(kd));
+ kvm_close(kd);
+ exit(2);
+}
diff --git a/usr.sbin/asf/asf_prog.c b/usr.sbin/asf/asf_prog.c
new file mode 100644
index 0000000..9a13cd6
--- /dev/null
+++ b/usr.sbin/asf/asf_prog.c
@@ -0,0 +1,73 @@
+/*-
+ * Copyright (c) 2002, 2003 Greg Lehey
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <inttypes.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "asf.h"
+
+/*
+ * Get the linker file list from kldstat(8) output.
+ * The "run" flag tells if kldstat(8) should run now.
+ * Of course, kldstat(1) can run in a live system only, but its output
+ * can be saved beforehand and fed to this function later via stdin.
+ */
+void
+asf_prog(int run)
+{
+ char buf[LINE_MAX];
+ char *token[MAXTOKEN];
+ char *endp;
+ FILE *kldstat;
+ caddr_t base;
+ int tokens;
+
+ if (run) {
+ if ((kldstat = popen("kldstat", "r")) == NULL)
+ err(2, "can't start kldstat");
+ } else
+ kldstat = stdin;
+
+ while (fgets(buf, sizeof(buf), kldstat)) {
+ /* Skip header line and main kernel file */
+ if (buf[0] == 'I' || strstr(buf, KERNFILE))
+ continue;
+ tokens = tokenize(buf, token, MAXTOKEN);
+ if (tokens < 4)
+ continue;
+ base = (caddr_t)(uintptr_t)strtoumax(token[2], &endp, 16);
+ if (endp == NULL || *endp != '\0')
+ continue;
+ kfile_add(token[4], base);
+ }
+}
diff --git a/usr.sbin/audit/Makefile b/usr.sbin/audit/Makefile
new file mode 100644
index 0000000..9f31741
--- /dev/null
+++ b/usr.sbin/audit/Makefile
@@ -0,0 +1,16 @@
+#
+# $FreeBSD$
+#
+
+OPENBSMDIR=${.CURDIR}/../../contrib/openbsm
+.PATH: ${OPENBSMDIR}/bin/audit
+
+CFLAGS+= -I${OPENBSMDIR}
+
+PROG= audit
+MAN= audit.8
+
+DPADD= ${LIBBSM}
+LDADD= -lbsm
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/auditd/Makefile b/usr.sbin/auditd/Makefile
new file mode 100644
index 0000000..839458d
--- /dev/null
+++ b/usr.sbin/auditd/Makefile
@@ -0,0 +1,17 @@
+#
+# $FreeBSD$
+#
+
+OPENBSMDIR=${.CURDIR}/../../contrib/openbsm
+.PATH: ${OPENBSMDIR}/bin/auditd
+
+CFLAGS+= -I${OPENBSMDIR}
+
+PROG= auditd
+SRCS= auditd.c audit_warn.c auditd_fbsd.c
+MAN= auditd.8
+
+DPADD= ${LIBBSM} ${LIBAUDITD}
+LDADD= -lbsm -lauditd
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/auditreduce/Makefile b/usr.sbin/auditreduce/Makefile
new file mode 100644
index 0000000..73b355d
--- /dev/null
+++ b/usr.sbin/auditreduce/Makefile
@@ -0,0 +1,16 @@
+#
+# $FreeBSD$
+#
+
+OPENBSMDIR=${.CURDIR}/../../contrib/openbsm
+.PATH: ${OPENBSMDIR}/bin/auditreduce
+
+CFLAGS+= -I${OPENBSMDIR}
+
+PROG= auditreduce
+MAN= auditreduce.1
+
+DPADD= ${LIBBSM}
+LDADD= -lbsm
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/authpf/Makefile b/usr.sbin/authpf/Makefile
new file mode 100644
index 0000000..b4c9b4f
--- /dev/null
+++ b/usr.sbin/authpf/Makefile
@@ -0,0 +1,22 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../contrib/pf/authpf
+.PATH: ${.CURDIR}/../../contrib/pf/pfctl
+
+PROG= authpf
+MAN= authpf.8
+BINOWN= root
+BINGRP= authpf
+BINMODE= 6555
+
+SRCS= authpf.c
+
+CFLAGS+= -I${.CURDIR}/../../contrib/pf/pfctl
+
+# XXX ALTQ:
+CFLAGS+= -DENABLE_ALTQ
+
+LDADD+= -lm -lmd -lutil
+DPADD+= ${LIBM} ${LIBMD} ${LIBUTIL}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bluetooth/Makefile b/usr.sbin/bluetooth/Makefile
new file mode 100644
index 0000000..ad193d0
--- /dev/null
+++ b/usr.sbin/bluetooth/Makefile
@@ -0,0 +1,20 @@
+# $Id: Makefile,v 1.5 2003/09/08 02:28:35 max Exp $
+# $FreeBSD$
+
+SUBDIR= \
+ bcmfw \
+ bt3cfw \
+ bthidcontrol \
+ bthidd \
+ btpand \
+ hccontrol \
+ hcsecd \
+ hcseriald \
+ l2control \
+ l2ping \
+ rfcomm_pppd \
+ sdpcontrol \
+ sdpd
+
+.include <bsd.subdir.mk>
+
diff --git a/usr.sbin/bluetooth/Makefile.inc b/usr.sbin/bluetooth/Makefile.inc
new file mode 100644
index 0000000..c0e05cf
--- /dev/null
+++ b/usr.sbin/bluetooth/Makefile.inc
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+.include "${.CURDIR}/../../Makefile.inc"
+
diff --git a/usr.sbin/bluetooth/bcmfw/BCM-LEGAL.txt b/usr.sbin/bluetooth/bcmfw/BCM-LEGAL.txt
new file mode 100644
index 0000000..e3a235e
--- /dev/null
+++ b/usr.sbin/bluetooth/bcmfw/BCM-LEGAL.txt
@@ -0,0 +1,8 @@
+$FreeBSD$
+
+BCM firmware version 2.15
+Copyright (c) 2000-2002 Broadcom Corporation
+
+Permission to distribute from..
+Contact info:
+ bluetooth_help@broadcom.com
diff --git a/usr.sbin/bluetooth/bcmfw/Makefile b/usr.sbin/bluetooth/bcmfw/Makefile
new file mode 100644
index 0000000..2427cf4
--- /dev/null
+++ b/usr.sbin/bluetooth/bcmfw/Makefile
@@ -0,0 +1,8 @@
+# $Id: Makefile,v 1.6 2003/08/14 20:05:58 max Exp $
+# $FreeBSD$
+
+PROG= bcmfw
+MAN= bcmfw.8
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bluetooth/bcmfw/README b/usr.sbin/bluetooth/bcmfw/README
new file mode 100644
index 0000000..0d769d8
--- /dev/null
+++ b/usr.sbin/bluetooth/bcmfw/README
@@ -0,0 +1,22 @@
+$FreeBSD$
+
+This directory will eventually also contain copies of the broadcom firmware.
+
+It WILL look like:
+total 190
+drwxr-xr-x 3 julian wheel 512 May 10 00:40 .
+drwxr-xr-x 11 julian wheel 512 May 10 14:48 ..
+-rw-r--r-- 1 julian wheel 154 May 10 00:41 BCM-LEGAL.txt
+-rw-r--r-- 1 julian wheel 56 May 10 00:14 BCM2033-FW.bin.md5
+-rw-r--r-- 1 julian wheel 158049 May 10 00:14 BCM2033-FW.bin.uue
+-rw-r--r-- 1 julian wheel 56 May 10 00:14 BCM2033-MD.hex.md5
+-rw-r--r-- 1 julian wheel 4505 May 10 00:14 BCM2033-MD.hex.uue
+drwxr-xr-x 2 julian wheel 512 May 10 00:52 CVS
+-rw-r--r-- 1 julian wheel 516 May 10 00:14 Makefile
+-rw-r--r-- 1 julian wheel 3013 May 10 00:14 bcmfw.8
+-rw-r--r-- 1 julian wheel 6806 May 10 00:14 bcmfw.c
+
+Until then, the firmware files can be fetched as part of
+ http://bluez.sourceforge.net/download/bluez-bluefw-0.9.tar.gz
+
+
diff --git a/usr.sbin/bluetooth/bcmfw/bcmfw.8 b/usr.sbin/bluetooth/bcmfw/bcmfw.8
new file mode 100644
index 0000000..e799350
--- /dev/null
+++ b/usr.sbin/bluetooth/bcmfw/bcmfw.8
@@ -0,0 +1,105 @@
+.\" Copyright (c) 2003 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: bcmfw.8,v 1.7 2003/05/21 00:33:40 max Exp $
+.\" $FreeBSD$
+.\"
+.Dd March 31, 2003
+.Dt BCMFW 8
+.Os
+.Sh NAME
+.Nm bcmfw
+.Nd firmware download utility for Broadcom BCM2033 chip based Bluetooth USB devices
+.Sh SYNOPSIS
+.Nm
+.Op Fl h
+.Fl f Ar firmware_file_name
+.Fl m Ar mini-driver_file_name
+.Fl n Ar device_name
+.Sh DESCRIPTION
+The
+.Nm
+utility downloads the specified mini-driver and firmware files to the specified
+device.
+.Pp
+This utility will
+.Em only
+work with Broadcom BCM2033 chip based Bluetooth USB devices.
+The identification is currently based on USB vendor ID/product ID pair.
+The vendor ID should be 0x0a5c
+.Pq Dv USB_VENDOR_BROADCOM
+and the product ID should be 0x2033.
+.Pp
+Due to copyright issues I will no longer provide mini-driver and firmware
+files for the device.
+These files can be obtained from the Linux BlueZ bluez-firmware package.
+.Pp
+Visit
+.Pa http://www.bluez.org/download.html
+for details.
+.Pp
+I am using the following files from the bluez-firmware-1.0 package:
+.Pp
+.Dl "MD5 (BCM2033-MD.hex) = 5580317158d07fc4ace90af04f8e1c73"
+.Dl "MD5 (BCM2033-FW.bin) = b4e142b3272cfe5a84b32fda6b4b032f"
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl f Ar firmware_file_name
+Specify firmware file name for download.
+.It Fl h
+Display usage message and exit.
+.It Fl m Ar mini-driver_file_name
+Specify mini-driver file name for download.
+.It Fl n Ar device_name
+Specify device name.
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /dev/ubtbcmfw Ns Ar N Ns Pa \&. Ns Ar EE" -compact
+.It Pa BCM2033-MD.hex
+Mini-driver image.
+.It Pa BCM2033-FW.bin
+Firmware image.
+.It Pa /dev/ubtbcmfw Ns Ar N Ns Pa \&. Ns Ar EE
+Endpoint
+.Ar EE
+of device
+.Ar N .
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+To download the firmware into the
+.Pa /dev/ubtbcmfw0
+device:
+.Pp
+.Dl "bcmfw -n ubtbcmfw0 -m BCM2033-MD.hex -f BCM2033-FW.bin"
+.Sh SEE ALSO
+.Xr ubtbcmfw 4 ,
+.Xr ugen 4
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
+.Sh BUGS
+Most likely.
+Please report if found.
diff --git a/usr.sbin/bluetooth/bcmfw/bcmfw.c b/usr.sbin/bluetooth/bcmfw/bcmfw.c
new file mode 100644
index 0000000..29d9996c
--- /dev/null
+++ b/usr.sbin/bluetooth/bcmfw/bcmfw.c
@@ -0,0 +1,308 @@
+/*
+ * bcmfw.c
+ *
+ * Copyright (c) 2003 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: bcmfw.c,v 1.4 2003/04/27 19:28:09 max Exp $
+ * $FreeBSD$
+ *
+ * Based on Linux BlueZ BlueFW-0.9 package
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usb_ioctl.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netgraph.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#define BCMFW "bcmfw"
+#define BCMFW_INTR_EP 1
+#define BCMFW_BULK_EP 2
+#define BCMFW_BSIZE 4096
+
+#define USB_VENDOR_BROADCOM 0x0a5c
+#define USB_PRODUCT_BROADCOM_BCM2033 0x2033
+
+static int bcmfw_check_device
+ (char const *name);
+static int bcmfw_load_firmware
+ (char const *name, char const *md, char const *fw);
+static void bcmfw_usage
+ (void);
+
+/*
+ * Main
+ */
+
+int
+main(int argc, char *argv[])
+{
+ char *name = NULL, *md = NULL, *fw = NULL;
+ int x;
+
+ while ((x = getopt(argc, argv, "f:hn:m:")) != -1) {
+ switch (x) {
+ case 'f': /* firmware file */
+ fw = optarg;
+ break;
+
+ case 'n': /* name */
+ name = optarg;
+ break;
+
+ case 'm': /* Mini-driver */
+ md = optarg;
+ break;
+
+ case 'h':
+ default:
+ bcmfw_usage();
+ /* NOT REACHED */
+ }
+ }
+
+ if (name == NULL || md == NULL || fw == NULL)
+ bcmfw_usage();
+ /* NOT REACHED */
+
+ openlog(BCMFW, LOG_NDELAY|LOG_PERROR|LOG_PID, LOG_USER);
+
+ if (bcmfw_check_device(name) < 0)
+ exit(1);
+
+ if (bcmfw_load_firmware(name, md, fw) < 0)
+ exit(1);
+
+ closelog();
+
+ return (0);
+} /* main */
+
+/*
+ * Check device VendorID/ProductID
+ */
+
+static int
+bcmfw_check_device(char const *name)
+{
+ usb_device_descriptor_t desc;
+ char path[BCMFW_BSIZE];
+ int fd = -1, error = -1;
+
+ snprintf(path, sizeof(path), "/dev/%s", name);
+
+ if ((fd = open(path, O_WRONLY)) < 0) {
+ syslog(LOG_ERR, "Could not open(%s). %s (%d)",
+ path, strerror(errno), errno);
+ goto out;
+ }
+
+ if (ioctl(fd, USB_GET_DEVICE_DESC, &desc) < 0) {
+ syslog(LOG_ERR, "Could not ioctl(%d, %ld, %p). %s (%d)",
+ fd, USB_GET_DEVICE_DESC, &desc,
+ strerror(errno), errno);
+ goto out;
+ }
+
+ if (UGETW(desc.idVendor) != USB_VENDOR_BROADCOM ||
+ UGETW(desc.idProduct) != USB_PRODUCT_BROADCOM_BCM2033) {
+ syslog(LOG_ERR, "Unsupported device, VendorID=%#x, " \
+ "ProductID=%#x", UGETW(desc.idVendor),
+ UGETW(desc.idProduct));
+ error = -1;
+ } else
+ error = 0;
+out:
+ if (fd != -1)
+ close(fd);
+
+ return (error);
+} /* bcmfw_check_device */
+
+/*
+ * Download minidriver and firmware
+ */
+
+static int
+bcmfw_load_firmware(char const *name, char const *md, char const *fw)
+{
+ char buf[BCMFW_BSIZE];
+ int intr = -1, bulk = -1, fd = -1, error = -1, len;
+
+ /* Open interrupt endpoint device */
+ snprintf(buf, sizeof(buf), "/dev/%s.%d", name, BCMFW_INTR_EP);
+ if ((intr = open(buf, O_RDONLY)) < 0) {
+ syslog(LOG_ERR, "Could not open(%s). %s (%d)",
+ buf, strerror(errno), errno);
+ goto out;
+ }
+
+ /* Open bulk endpoint device */
+ snprintf(buf, sizeof(buf), "/dev/%s.%d", name, BCMFW_BULK_EP);
+ if ((bulk = open(buf, O_WRONLY)) < 0) {
+ syslog(LOG_ERR, "Could not open(%s). %s (%d)",
+ buf, strerror(errno), errno);
+ goto out;
+ }
+
+ /*
+ * Load mini-driver
+ */
+
+ if ((fd = open(md, O_RDONLY)) < 0) {
+ syslog(LOG_ERR, "Could not open(%s). %s (%d)",
+ md, strerror(errno), errno);
+ goto out;
+ }
+
+ for (;;) {
+ len = read(fd, buf, sizeof(buf));
+ if (len < 0) {
+ syslog(LOG_ERR, "Could not read(%s). %s (%d)",
+ md, strerror(errno), errno);
+ goto out;
+ }
+ if (len == 0)
+ break;
+
+ len = write(bulk, buf, len);
+ if (len < 0) {
+ syslog(LOG_ERR, "Could not write(/dev/%s.%d). %s (%d)",
+ name, BCMFW_BULK_EP, strerror(errno),
+ errno);
+ goto out;
+ }
+ }
+
+ close(fd);
+ fd = -1;
+
+ usleep(10);
+
+ /*
+ * Memory select
+ */
+
+ if (write(bulk, "#", 1) < 0) {
+ syslog(LOG_ERR, "Could not write(/dev/%s.%d). %s (%d)",
+ name, BCMFW_BULK_EP, strerror(errno), errno);
+ goto out;
+ }
+
+ if (read(intr, buf, sizeof(buf)) < 0) {
+ syslog(LOG_ERR, "Could not read(/dev/%s.%d). %s (%d)",
+ name, BCMFW_INTR_EP, strerror(errno), errno);
+ goto out;
+ }
+
+ if (buf[0] != '#') {
+ syslog(LOG_ERR, "%s: Memory select failed (%c)", name, buf[0]);
+ goto out;
+ }
+
+ /*
+ * Load firmware
+ */
+
+ if ((fd = open(fw, O_RDONLY)) < 0) {
+ syslog(LOG_ERR, "Could not open(%s). %s (%d)",
+ fw, strerror(errno), errno);
+ goto out;
+ }
+
+ for (;;) {
+ len = read(fd, buf, sizeof(buf));
+ if (len < 0) {
+ syslog(LOG_ERR, "Could not read(%s). %s (%d)",
+ fw, strerror(errno), errno);
+ goto out;
+ }
+ if (len == 0)
+ break;
+
+ len = write(bulk, buf, len);
+ if (len < 0) {
+ syslog(LOG_ERR, "Could not write(/dev/%s.%d). %s (%d)",
+ name, BCMFW_BULK_EP, strerror(errno),
+ errno);
+ goto out;
+ }
+ }
+
+ close(fd);
+ fd = -1;
+
+ if (read(intr, buf, sizeof(buf)) < 0) {
+ syslog(LOG_ERR, "Could not read(/dev/%s.%d). %s (%d)",
+ name, BCMFW_INTR_EP, strerror(errno), errno);
+ goto out;
+ }
+
+ if (buf[0] != '.') {
+ syslog(LOG_ERR, "%s: Could not load firmware (%c)",
+ name, buf[0]);
+ goto out;
+ }
+
+ usleep(500000);
+ error = 0;
+out:
+ if (fd != -1)
+ close(fd);
+ if (bulk != -1)
+ close(bulk);
+ if (intr != -1)
+ close(intr);
+
+ return (error);
+} /* bcmfw_load_firmware */
+
+/*
+ * Display usage message and quit
+ */
+
+static void
+bcmfw_usage(void)
+{
+ fprintf(stdout,
+"Usage: %s -n name -m md_file -f fw_file\n"
+"Where:\n" \
+"\t-n name device name\n" \
+"\t-m mini-driver image mini-driver image file name for download\n" \
+"\t-f firmware image firmware image file name for download\n" \
+"\t-h display this message\n", BCMFW);
+
+ exit(255);
+} /* bcmfw_usage */
+
diff --git a/usr.sbin/bluetooth/bt3cfw/Makefile b/usr.sbin/bluetooth/bt3cfw/Makefile
new file mode 100644
index 0000000..90bf751
--- /dev/null
+++ b/usr.sbin/bluetooth/bt3cfw/Makefile
@@ -0,0 +1,11 @@
+# $Id: Makefile,v 1.5 2003/08/14 20:06:00 max Exp $
+# $FreeBSD$
+
+PROG= bt3cfw
+MAN= bt3cfw.8
+WARNS?= 2
+
+DPADD= ${LIBNETGRAPH}
+LDADD= -lnetgraph
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bluetooth/bt3cfw/bt3cfw.8 b/usr.sbin/bluetooth/bt3cfw/bt3cfw.8
new file mode 100644
index 0000000..54df178
--- /dev/null
+++ b/usr.sbin/bluetooth/bt3cfw/bt3cfw.8
@@ -0,0 +1,73 @@
+.\" Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: bt3cfw.8,v 1.4 2003/05/21 00:34:51 max Exp $
+.\" $FreeBSD$
+.\"
+.Dd November 11, 2002
+.Dt BT3CFW 8
+.Os
+.Sh NAME
+.Nm bt3cfw
+.Nd firmware download utility for 3Com Bluetooth PC card driver
+.Sh SYNOPSIS
+.Nm
+.Op Fl h
+.Fl f Ar Firmware_file_name
+.Fl n Ar Netgraph_node_name
+.Sh DESCRIPTION
+The
+.Nm
+utility connects to the specified Netgraph driver node of type
+.Dv BTCCC
+and downloads the specified firmware file.
+.Pp
+Due to copyright issues, I will no longer provide firmware with the card
+driver.
+The firmware can be obtained from the Windows driver package that
+can be downloaded from the 3COM web site at no charge.
+The firmware name is
+.Pa BT3CPCC.BIN .
+I am using the original firmware that came with the card on CD-ROM.
+.Pp
+.Dl "MD5 (BT3CPCC.BIN) = 36170fda56ea9fdbf1702c966f8a97f1"
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl f Ar Firmware_file_name
+Specify firmware file name for download.
+.It Fl h
+Display usage message and exit.
+.It Fl n Ar Netgraph_node_name
+Connect to the specified Netgraph driver node of type
+.Dv BTCCC .
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr ng_bt3c 4
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
+.Sh BUGS
+Please report if found.
diff --git a/usr.sbin/bluetooth/bt3cfw/bt3cfw.c b/usr.sbin/bluetooth/bt3cfw/bt3cfw.c
new file mode 100644
index 0000000..aadb6b6
--- /dev/null
+++ b/usr.sbin/bluetooth/bt3cfw/bt3cfw.c
@@ -0,0 +1,227 @@
+/*
+ * bt3cfw.c
+ *
+ * Copyright (c) 2001 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: bt3cfw.c,v 1.2 2003/05/21 22:40:29 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <netgraph.h>
+#include <netgraph/bluetooth/include/ng_bt3c.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#define BT3CFW_IDENT "bt3cfw"
+#define BT3CFW_MAX_FIRMWARE_SIZE 0xffff
+
+/* Convert hex ASCII to int4 */
+static int
+hexa2int4(const char *a)
+{
+ if ('0' <= *a && *a <= '9')
+ return (*a - '0');
+
+ if ('A' <= *a && *a <= 'F')
+ return (*a - 'A' + 0xa);
+
+ if ('a' <= *a && *a <= 'f')
+ return (*a - 'a' + 0xa);
+
+ syslog(LOG_ERR, "Invalid hex character: '%c' (%#x)", *a, *a);
+ exit(255);
+}
+
+/* Convert hex ASCII to int8 */
+static int
+hexa2int8(const char *a)
+{
+ return ((hexa2int4(a) << 4) | hexa2int4(a + 1));
+}
+
+/* Convert hex ASCII to int16 */
+static int
+hexa2int16(const char *a)
+{
+ return ((hexa2int8(a) << 8) | hexa2int8(a + 2));
+}
+
+/* Convert hex ASCII to int32 */
+static int
+hexa2int32(const char *a)
+{
+ return ((hexa2int16(a) << 16) | hexa2int16(a + 4));
+}
+
+/* Display usage() and exit */
+static void
+usage(void)
+{
+ syslog(LOG_ERR, "Usage: %s -f FirmwareFile -n NodeName", BT3CFW_IDENT);
+ exit(255);
+}
+
+/* Main */
+int
+main(int argc, char *argv[])
+{
+ FILE *firmware_file = NULL;
+ char buffer[80], path[NG_PATHSIZ],
+ *firmware_filename = NULL;
+ uint8_t *firmware = NULL;
+ int firmware_size, opt, cs, ds;
+
+ memset(path, 0, sizeof(path));
+ openlog(BT3CFW_IDENT, LOG_NDELAY|LOG_PID|LOG_PERROR, LOG_USER);
+
+ while ((opt = getopt(argc, argv, "f:hn:")) != -1) {
+ switch (opt) {
+ case 'f':
+ firmware_filename = optarg;
+ break;
+
+ case 'n':
+ snprintf(path, sizeof(path), "%s:", optarg);
+ break;
+
+ case 'h':
+ default:
+ usage();
+ /* NOT REACHED */
+ }
+ }
+
+ if (firmware_filename == NULL || path[0] == 0)
+ usage();
+ /* NOT REACHED */
+
+ firmware = (uint8_t *) calloc(BT3CFW_MAX_FIRMWARE_SIZE,
+ sizeof(uint8_t));
+ if (firmware == NULL) {
+ syslog(LOG_ERR, "Could not allocate firmware buffer");
+ exit(255);
+ }
+
+ if ((firmware_file = fopen(firmware_filename, "r")) == NULL) {
+ syslog(LOG_ERR, "Could not open BT3C firmware file %s. %s (%d)",
+ firmware_filename, strerror(errno), errno);
+ exit(255);
+ }
+
+ firmware_size = 0;
+
+ while (fgets(buffer, sizeof(buffer), firmware_file)) {
+ int i, size, address, cs, fcs;
+
+ size = hexa2int8(buffer + 2);
+ address = hexa2int32(buffer + 4);
+ fcs = hexa2int8(buffer + 2 + size * 2);
+
+ if (buffer[1] == '3') {
+ ng_bt3c_firmware_block_ep *block = NULL;
+ uint16_t *data = NULL;
+
+ block = (ng_bt3c_firmware_block_ep *)
+ (firmware + firmware_size);
+
+ firmware_size += sizeof(*block);
+ if (firmware_size >= BT3CFW_MAX_FIRMWARE_SIZE) {
+ syslog(LOG_ERR, "Could not add new firmware " \
+ "block. Firmware file %s is " \
+ "too big, firmware_size=%d",
+ firmware_filename,
+ firmware_size);
+ exit(255);
+ }
+
+ block->block_address = address;
+ block->block_size = (size - 4) / 2;
+ block->block_alignment = (block->block_size * 2) % 3;
+ if (block->block_alignment != 0)
+ block->block_alignment = 3 - block->block_alignment;
+
+ firmware_size += (block->block_size * 2);
+ firmware_size += block->block_alignment;
+ if (firmware_size >= BT3CFW_MAX_FIRMWARE_SIZE) {
+ syslog(LOG_ERR, "Could not add new firmware " \
+ "data. Firmware file %s is " \
+ "too big, firmware_size=%d",
+ firmware_filename,
+ firmware_size);
+ exit(255);
+ }
+
+ /* First part of the cheksum: size and address */
+ cs = 0;
+ for (i = 0; i < 5; i++)
+ cs += hexa2int8(buffer + 2 + i * 2);
+
+ /* Data + second part of the cheksum: data */
+ data = (uint16_t *)(block + 1);
+ for (i = 0; i < block->block_size; i++) {
+ data[i] = hexa2int16(buffer + (i * 4) + 12);
+ cs += (((data[i] & 0xff00) >> 8) & 0xff);
+ cs += (data[i] & 0x00ff);
+ }
+ } else
+ for (cs = 0, i = 0; i < size; i++)
+ cs += hexa2int8(buffer + 2 + i * 2);
+
+ if (((cs + fcs) & 0xff) != 0xff) {
+ syslog(LOG_ERR, "Invalid firmware file %s. Checksum " \
+ "error, cs=%#x, fcs=%#x, checksum=%#x",
+ firmware_filename, (cs & 0xff), fcs,
+ ((cs + fcs) & 0xff));
+ exit(255);
+ }
+ }
+
+ /* Send firmware to the card */
+ if (NgMkSockNode(NULL, &cs, &ds) < 0) {
+ syslog(LOG_ERR, "Could not create Netgraph socket. %s (%d)",
+ strerror(errno), errno);
+ exit(255);
+ }
+
+ if (NgSendMsg(cs, path, NGM_BT3C_COOKIE,
+ NGM_BT3C_NODE_DOWNLOAD_FIRMWARE,
+ (void const *) firmware, firmware_size) < 0) {
+ syslog(LOG_ERR, "Could not send Netgraph message. %s (%d)",
+ strerror(errno), errno);
+ exit(255);
+ }
+
+ free(firmware);
+ firmware = NULL;
+
+ return (0);
+}
+
diff --git a/usr.sbin/bluetooth/bthidcontrol/Makefile b/usr.sbin/bluetooth/bthidcontrol/Makefile
new file mode 100644
index 0000000..6c9eafb
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidcontrol/Makefile
@@ -0,0 +1,15 @@
+# $Id: Makefile,v 1.2 2004/02/13 21:44:41 max Exp $
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../bthidd
+
+PROG= bthidcontrol
+MAN= bthidcontrol.8
+SRCS= bthidcontrol.c hid.c lexer.l parser.y sdp.c
+WARNS?= 1
+CFLAGS+= -DBTHIDCONTROL=1 -I${.CURDIR}/../bthidd
+
+DPADD= ${LIBBLUETOOTH} ${LIBSDP} ${LIBUSBHID}
+LDADD= -lbluetooth -lsdp -lusbhid
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bluetooth/bthidcontrol/bthidcontrol.8 b/usr.sbin/bluetooth/bthidcontrol/bthidcontrol.8
new file mode 100644
index 0000000..7625b39
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidcontrol/bthidcontrol.8
@@ -0,0 +1,102 @@
+.\" Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: bthidcontrol.8,v 1.1 2004/02/13 21:44:41 max Exp $
+.\" $FreeBSD$
+.\"
+.Dd October 30, 2006
+.Dt BTHIDCONTROL 8
+.Os
+.Sh NAME
+.Nm bthidcontrol
+.Nd Bluetooth HID control utility
+.Sh SYNOPSIS
+.Nm
+.Fl h
+.Nm
+.Op Fl a Ar BD_ADDR
+.Op Fl c Ar file
+.Op Fl H Ar file
+.Op Fl v
+.Ar command
+.Sh DESCRIPTION
+The
+.Nm
+utility can be used to query remote Bluetooth HID devices, dump HID descriptors
+in human readable form and perform simple manipulations on the Bluetooth HID
+daemon configuration files.
+.Pp
+The
+.Nm
+utility will print results to the standard output and error messages to the
+standard error.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl a Ar BD_ADDR
+Specify BD_ADDR for the HID device.
+Example:
+.Fl a Li 00:01:02:03:04:05 .
+.It Fl c Ar file
+Specify path to the Bluetooth HID daemon configuration file.
+The default path is
+.Pa /etc/bluetooth/bthidd.conf .
+.It Fl H Ar file
+Specify path to the Bluetooth HID daemon known HIDs file.
+The default path is
+.Pa /var/db/bthidd.hids .
+.It Fl h
+Display usage message and exit.
+.It Fl v
+Be verbose and show items that are being used for padding.
+.It Ar command
+One of the supported commands (see below).
+Special command
+.Cm help
+can be used to obtain the list of all supported commands.
+To get more information about specific command use
+.Cm help Ar command .
+.El
+.Sh COMMANDS
+The currently supported node commands in
+.Nm
+are:
+.Pp
+.Bl -tag -offset indent -compact
+.It Cm Query
+.It Cm Dump
+.It Cm Known
+.It Cm Forget
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /etc/bluetooth/bthidd.conf" -compact
+.It Pa /etc/bluetooth/bthidd.conf
+.It Pa /var/db/bthidd.hids
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr bthidd 8
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
diff --git a/usr.sbin/bluetooth/bthidcontrol/bthidcontrol.c b/usr.sbin/bluetooth/bthidcontrol/bthidcontrol.c
new file mode 100644
index 0000000..900bda5
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidcontrol/bthidcontrol.c
@@ -0,0 +1,215 @@
+/*
+ * bthidcontrol.c
+ *
+ * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: bthidcontrol.c,v 1.2 2004/02/13 21:44:41 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <assert.h>
+#include <bluetooth.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <usbhid.h>
+#include "bthid_config.h"
+#include "bthidcontrol.h"
+
+static int do_bthid_command(bdaddr_p bdaddr, int argc, char **argv);
+static struct bthid_command * find_bthid_command(char const *command, struct bthid_command *category);
+static void print_bthid_command(struct bthid_command *category);
+static void usage(void);
+
+int32_t hid_sdp_query(bdaddr_t const *local, bdaddr_t const *remote, int32_t *error);
+
+uint32_t verbose = 0;
+
+/*
+ * bthidcontrol
+ */
+
+int
+main(int argc, char *argv[])
+{
+ bdaddr_t bdaddr;
+ int opt;
+
+ hid_init(NULL);
+ memcpy(&bdaddr, NG_HCI_BDADDR_ANY, sizeof(bdaddr));
+
+ while ((opt = getopt(argc, argv, "a:c:H:hv")) != -1) {
+ switch (opt) {
+ case 'a': /* bdaddr */
+ if (!bt_aton(optarg, &bdaddr)) {
+ struct hostent *he = NULL;
+
+ if ((he = bt_gethostbyname(optarg)) == NULL)
+ errx(1, "%s: %s", optarg, hstrerror(h_errno));
+
+ memcpy(&bdaddr, he->h_addr, sizeof(bdaddr));
+ }
+ break;
+
+ case 'c': /* config file */
+ config_file = optarg;
+ break;
+
+ case 'H': /* HIDs file */
+ hids_file = optarg;
+ break;
+
+ case 'v': /* verbose */
+ verbose++;
+ break;
+
+ case 'h':
+ default:
+ usage();
+ /* NOT REACHED */
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (*argv == NULL)
+ usage();
+
+ return (do_bthid_command(&bdaddr, argc, argv));
+} /* main */
+
+/* Execute commands */
+static int
+do_bthid_command(bdaddr_p bdaddr, int argc, char **argv)
+{
+ char *cmd = argv[0];
+ struct bthid_command *c = NULL;
+ int e, help;
+
+ help = 0;
+ if (strcasecmp(cmd, "help") == 0) {
+ argc --;
+ argv ++;
+
+ if (argc <= 0) {
+ fprintf(stdout, "Supported commands:\n");
+ print_bthid_command(sdp_commands);
+ print_bthid_command(hid_commands);
+ fprintf(stdout, "\nFor more information use " \
+ "'help command'\n");
+
+ return (OK);
+ }
+
+ help = 1;
+ cmd = argv[0];
+ }
+
+ c = find_bthid_command(cmd, sdp_commands);
+ if (c == NULL)
+ c = find_bthid_command(cmd, hid_commands);
+
+ if (c == NULL) {
+ fprintf(stdout, "Unknown command: \"%s\"\n", cmd);
+ return (ERROR);
+ }
+
+ if (!help)
+ e = (c->handler)(bdaddr, -- argc, ++ argv);
+ else
+ e = USAGE;
+
+ switch (e) {
+ case OK:
+ case FAILED:
+ break;
+
+ case ERROR:
+ fprintf(stdout, "Could not execute command \"%s\". %s\n",
+ cmd, strerror(errno));
+ break;
+
+ case USAGE:
+ fprintf(stdout, "Usage: %s\n%s\n", c->command, c->description);
+ break;
+
+ default: assert(0); break;
+ }
+
+ return (e);
+} /* do_bthid_command */
+
+/* Try to find command in specified category */
+static struct bthid_command *
+find_bthid_command(char const *command, struct bthid_command *category)
+{
+ struct bthid_command *c = NULL;
+
+ for (c = category; c->command != NULL; c++) {
+ char *c_end = strchr(c->command, ' ');
+
+ if (c_end != NULL) {
+ int len = c_end - c->command;
+
+ if (strncasecmp(command, c->command, len) == 0)
+ return (c);
+ } else if (strcasecmp(command, c->command) == 0)
+ return (c);
+ }
+
+ return (NULL);
+} /* find_bthid_command */
+
+/* Print commands in specified category */
+static void
+print_bthid_command(struct bthid_command *category)
+{
+ struct bthid_command *c = NULL;
+
+ for (c = category; c->command != NULL; c++)
+ fprintf(stdout, "\t%s\n", c->command);
+} /* print_bthid_command */
+
+/* Usage */
+static void
+usage(void)
+{
+ fprintf(stderr,
+"Usage: bthidcontrol options command\n" \
+"Where options are:\n"
+" -a bdaddr specify bdaddr\n" \
+" -c file specify path to the bthidd config file\n" \
+" -H file specify path to the bthidd HIDs file\n" \
+" -h display usage and quit\n" \
+" -v be verbose\n" \
+" command one of the supported commands\n");
+ exit(255);
+} /* usage */
+
diff --git a/usr.sbin/bluetooth/bthidcontrol/bthidcontrol.h b/usr.sbin/bluetooth/bthidcontrol/bthidcontrol.h
new file mode 100644
index 0000000..50ed2fa
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidcontrol/bthidcontrol.h
@@ -0,0 +1,50 @@
+/*
+ * bthidcontrol.h
+ *
+ * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: bthidcontrol.h,v 1.1 2004/02/12 23:25:51 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef __BTHIDCONTROL_H__
+#define __BTHIDCONTROL_H__
+
+#define OK 0 /* everything was OK */
+#define ERROR 1 /* could not execute command */
+#define FAILED 2 /* error was reported */
+#define USAGE 3 /* invalid parameters */
+
+struct bthid_command {
+ char const *command;
+ char const *description;
+ int (*handler)(bdaddr_t *, int, char **);
+};
+
+extern struct bthid_command hid_commands[];
+extern struct bthid_command sdp_commands[];
+
+#endif /* __BTHIDCONTROL_H__ */
+
diff --git a/usr.sbin/bluetooth/bthidcontrol/hid.c b/usr.sbin/bluetooth/bthidcontrol/hid.c
new file mode 100644
index 0000000..9fb6220
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidcontrol/hid.c
@@ -0,0 +1,214 @@
+/*
+ * hid.c
+ *
+ * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: hid.c,v 1.3 2004/02/17 22:14:57 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <bluetooth.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+#include <stdio.h>
+#include <string.h>
+#include <usbhid.h>
+#include "bthid_config.h"
+#include "bthidcontrol.h"
+
+extern uint32_t verbose;
+
+static void hid_dump_descriptor (report_desc_t r);
+static void hid_dump_item (char const *label, struct hid_item *h);
+
+static int
+hid_dump(bdaddr_t *bdaddr, int argc, char **argv)
+{
+ struct hid_device *hd = NULL;
+ int e = FAILED;
+
+ if (read_config_file() == 0) {
+ if ((hd = get_hid_device(bdaddr)) != NULL) {
+ hid_dump_descriptor(hd->desc);
+ e = OK;
+ }
+
+ clean_config();
+ }
+
+ return (e);
+}
+
+static int
+hid_forget(bdaddr_t *bdaddr, int argc, char **argv)
+{
+ struct hid_device *hd = NULL;
+ int e = FAILED;
+
+ if (read_config_file() == 0) {
+ if (read_hids_file() == 0) {
+ if ((hd = get_hid_device(bdaddr)) != NULL) {
+ hd->new_device = 1;
+ if (write_hids_file() == 0)
+ e = OK;
+ }
+ }
+
+ clean_config();
+ }
+
+ return (e);
+}
+
+static int
+hid_known(bdaddr_t *bdaddr, int argc, char **argv)
+{
+ struct hid_device *hd = NULL;
+ struct hostent *he = NULL;
+ int e = FAILED;
+
+ if (read_config_file() == 0) {
+ if (read_hids_file() == 0) {
+ e = OK;
+
+ for (hd = get_next_hid_device(hd);
+ hd != NULL;
+ hd = get_next_hid_device(hd)) {
+ if (hd->new_device)
+ continue;
+
+ he = bt_gethostbyaddr((char *) &hd->bdaddr,
+ sizeof(hd->bdaddr),
+ AF_BLUETOOTH);
+
+ fprintf(stdout,
+"%s %s\n", bt_ntoa(&hd->bdaddr, NULL),
+ (he != NULL && he->h_name != NULL)?
+ he->h_name : "");
+ }
+ }
+
+ clean_config();
+ }
+
+ return (e);
+}
+
+static void
+hid_dump_descriptor(report_desc_t r)
+{
+ struct hid_data *d = NULL;
+ struct hid_item h;
+
+ for (d = hid_start_parse(r, ~0, -1); hid_get_item(d, &h); ) {
+ switch (h.kind) {
+ case hid_collection:
+ fprintf(stdout,
+"Collection page=%s usage=%s\n", hid_usage_page(HID_PAGE(h.usage)),
+ hid_usage_in_page(h.usage));
+ break;
+
+ case hid_endcollection:
+ fprintf(stdout, "End collection\n");
+ break;
+
+ case hid_input:
+ hid_dump_item("Input ", &h);
+ break;
+
+ case hid_output:
+ hid_dump_item("Output ", &h);
+ break;
+
+ case hid_feature:
+ hid_dump_item("Feature", &h);
+ break;
+ }
+ }
+
+ hid_end_parse(d);
+}
+
+static void
+hid_dump_item(char const *label, struct hid_item *h)
+{
+ if ((h->flags & HIO_CONST) && !verbose)
+ return;
+
+ fprintf(stdout,
+"%s id=%u size=%u count=%u page=%s usage=%s%s%s%s%s%s%s%s%s%s",
+ label, (uint8_t) h->report_ID, h->report_size, h->report_count,
+ hid_usage_page(HID_PAGE(h->usage)),
+ hid_usage_in_page(h->usage),
+ h->flags & HIO_CONST ? " Const" : "",
+ h->flags & HIO_VARIABLE ? " Variable" : "",
+ h->flags & HIO_RELATIVE ? " Relative" : "",
+ h->flags & HIO_WRAP ? " Wrap" : "",
+ h->flags & HIO_NONLINEAR ? " NonLinear" : "",
+ h->flags & HIO_NOPREF ? " NoPref" : "",
+ h->flags & HIO_NULLSTATE ? " NullState" : "",
+ h->flags & HIO_VOLATILE ? " Volatile" : "",
+ h->flags & HIO_BUFBYTES ? " BufBytes" : "");
+
+ fprintf(stdout,
+", logical range %d..%d",
+ h->logical_minimum, h->logical_maximum);
+
+ if (h->physical_minimum != h->physical_maximum)
+ fprintf(stdout,
+", physical range %d..%d",
+ h->physical_minimum, h->physical_maximum);
+
+ if (h->unit)
+ fprintf(stdout,
+", unit=0x%02x exp=%d", h->unit, h->unit_exponent);
+
+ fprintf(stdout, "\n");
+}
+
+struct bthid_command hid_commands[] = {
+{
+"Dump",
+"Dump HID descriptor for the specified device in human readable form. The\n" \
+"device must have an entry in the Bluetooth HID daemon configuration file.\n",
+hid_dump
+},
+{
+"Known",
+"List all known to the Bluetooth HID daemon devices.\n",
+hid_known
+},
+{
+"Forget",
+"Forget (mark as new) specified HID device. This command is useful when it\n" \
+"is required to remove device from the known HIDs file. This should be done\n" \
+"when reset button was pressed on the device or the battery was changed. The\n"\
+"Bluetooth HID daemon should be restarted.\n",
+hid_forget
+},
+{ NULL, NULL, NULL }
+};
+
diff --git a/usr.sbin/bluetooth/bthidcontrol/sdp.c b/usr.sbin/bluetooth/bthidcontrol/sdp.c
new file mode 100644
index 0000000..032863e
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidcontrol/sdp.c
@@ -0,0 +1,432 @@
+/*
+ * sdp.c
+ *
+ * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: sdp.c,v 1.3 2004/02/17 22:14:57 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <bluetooth.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+#include <errno.h>
+#include <sdp.h>
+#include <stdio.h>
+#include <string.h>
+#include <usbhid.h>
+#include "bthid_config.h"
+#include "bthidcontrol.h"
+
+static int32_t hid_sdp_query (bdaddr_t const *local, struct hid_device *hd, int32_t *error);
+static int32_t hid_sdp_parse_protocol_descriptor_list (sdp_attr_p a);
+static int32_t hid_sdp_parse_hid_descriptor (sdp_attr_p a);
+static int32_t hid_sdp_parse_boolean (sdp_attr_p a);
+
+static uint16_t service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE;
+
+static uint32_t attrs[] = {
+SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
+ SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
+SDP_ATTR_RANGE (SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS,
+ SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS),
+SDP_ATTR_RANGE( 0x0205, /* HIDReconnectInitiate */
+ 0x0206), /* HIDDescriptorList */
+SDP_ATTR_RANGE( 0x0209, /* HIDBatteryPower */
+ 0x0209),
+SDP_ATTR_RANGE( 0x020d, /* HIDNormallyConnectable */
+ 0x020d)
+ };
+#define nattrs (sizeof(attrs)/sizeof(attrs[0]))
+
+static sdp_attr_t values[8];
+#define nvalues (sizeof(values)/sizeof(values[0]))
+
+static uint8_t buffer[nvalues][512];
+
+/*
+ * Query remote device
+ */
+
+#undef hid_sdp_query_exit
+#define hid_sdp_query_exit(e) { \
+ if (error != NULL) \
+ *error = (e); \
+ if (ss != NULL) { \
+ sdp_close(ss); \
+ ss = NULL; \
+ } \
+ return (((e) == 0)? 0 : -1); \
+}
+
+static int32_t
+hid_sdp_query(bdaddr_t const *local, struct hid_device *hd, int32_t *error)
+{
+ void *ss = NULL;
+ uint8_t *hid_descriptor = NULL;
+ int32_t i, control_psm = -1, interrupt_psm = -1,
+ reconnect_initiate = -1,
+ normally_connectable = 0, battery_power = 0,
+ hid_descriptor_length = -1;
+
+ if (local == NULL)
+ local = NG_HCI_BDADDR_ANY;
+ if (hd == NULL)
+ hid_sdp_query_exit(EINVAL);
+
+ for (i = 0; i < nvalues; i ++) {
+ values[i].flags = SDP_ATTR_INVALID;
+ values[i].attr = 0;
+ values[i].vlen = sizeof(buffer[i]);
+ values[i].value = buffer[i];
+ }
+
+ if ((ss = sdp_open(local, &hd->bdaddr)) == NULL)
+ hid_sdp_query_exit(ENOMEM);
+ if (sdp_error(ss) != 0)
+ hid_sdp_query_exit(sdp_error(ss));
+ if (sdp_search(ss, 1, &service, nattrs, attrs, nvalues, values) != 0)
+ hid_sdp_query_exit(sdp_error(ss));
+
+ sdp_close(ss);
+ ss = NULL;
+
+ for (i = 0; i < nvalues; i ++) {
+ if (values[i].flags != SDP_ATTR_OK)
+ continue;
+
+ switch (values[i].attr) {
+ case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
+ control_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
+ break;
+
+ case SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS:
+ interrupt_psm = hid_sdp_parse_protocol_descriptor_list(&values[i]);
+ break;
+
+ case 0x0205: /* HIDReconnectInitiate */
+ reconnect_initiate = hid_sdp_parse_boolean(&values[i]);
+ break;
+
+ case 0x0206: /* HIDDescriptorList */
+ if (hid_sdp_parse_hid_descriptor(&values[i]) == 0) {
+ hid_descriptor = values[i].value;
+ hid_descriptor_length = values[i].vlen;
+ }
+ break;
+
+ case 0x0209: /* HIDBatteryPower */
+ battery_power = hid_sdp_parse_boolean(&values[i]);
+ break;
+
+ case 0x020d: /* HIDNormallyConnectable */
+ normally_connectable = hid_sdp_parse_boolean(&values[i]);
+ break;
+ }
+ }
+
+ if (control_psm == -1 || interrupt_psm == -1 ||
+ reconnect_initiate == -1 || normally_connectable == -1 ||
+ hid_descriptor == NULL || hid_descriptor_length == -1)
+ hid_sdp_query_exit(ENOATTR);
+
+ hd->control_psm = control_psm;
+ hd->interrupt_psm = interrupt_psm;
+ hd->reconnect_initiate = reconnect_initiate? 1 : 0;
+ hd->battery_power = battery_power? 1 : 0;
+ hd->normally_connectable = normally_connectable? 1 : 0;
+ hd->desc = hid_use_report_desc(hid_descriptor, hid_descriptor_length);
+ if (hd->desc == NULL)
+ hid_sdp_query_exit(ENOMEM);
+
+ return (0);
+}
+
+/*
+ * seq len 2
+ * seq len 2
+ * uuid value 3
+ * uint16 value 3
+ * seq len 2
+ * uuid value 3
+ */
+
+static int32_t
+hid_sdp_parse_protocol_descriptor_list(sdp_attr_p a)
+{
+ uint8_t *ptr = a->value;
+ uint8_t *end = a->value + a->vlen;
+ int32_t type, len, uuid, psm;
+
+ if (end - ptr < 15)
+ return (-1);
+
+ if (a->attr == SDP_ATTR_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS) {
+ SDP_GET8(type, ptr);
+ switch (type) {
+ case SDP_DATA_SEQ8:
+ SDP_GET8(len, ptr);
+ break;
+
+ case SDP_DATA_SEQ16:
+ SDP_GET16(len, ptr);
+ break;
+
+ case SDP_DATA_SEQ32:
+ SDP_GET32(len, ptr);
+ break;
+
+ default:
+ return (-1);
+ }
+ if (ptr + len > end)
+ return (-1);
+ }
+
+ SDP_GET8(type, ptr);
+ switch (type) {
+ case SDP_DATA_SEQ8:
+ SDP_GET8(len, ptr);
+ break;
+
+ case SDP_DATA_SEQ16:
+ SDP_GET16(len, ptr);
+ break;
+
+ case SDP_DATA_SEQ32:
+ SDP_GET32(len, ptr);
+ break;
+
+ default:
+ return (-1);
+ }
+ if (ptr + len > end)
+ return (-1);
+
+ /* Protocol */
+ SDP_GET8(type, ptr);
+ switch (type) {
+ case SDP_DATA_SEQ8:
+ SDP_GET8(len, ptr);
+ break;
+
+ case SDP_DATA_SEQ16:
+ SDP_GET16(len, ptr);
+ break;
+
+ case SDP_DATA_SEQ32:
+ SDP_GET32(len, ptr);
+ break;
+
+ default:
+ return (-1);
+ }
+ if (ptr + len > end)
+ return (-1);
+
+ /* UUID */
+ if (ptr + 3 > end)
+ return (-1);
+ SDP_GET8(type, ptr);
+ switch (type) {
+ case SDP_DATA_UUID16:
+ SDP_GET16(uuid, ptr);
+ if (uuid != SDP_UUID_PROTOCOL_L2CAP)
+ return (-1);
+ break;
+
+ case SDP_DATA_UUID32: /* XXX FIXME can we have 32-bit UUID */
+ case SDP_DATA_UUID128: /* XXX FIXME can we have 128-bit UUID */
+ default:
+ return (-1);
+ }
+
+ /* PSM */
+ if (ptr + 3 > end)
+ return (-1);
+ SDP_GET8(type, ptr);
+ if (type != SDP_DATA_UINT16)
+ return (-1);
+ SDP_GET16(psm, ptr);
+
+ return (psm);
+}
+
+/*
+ * seq len 2
+ * seq len 2
+ * uint8 value8 2
+ * str value 3
+ */
+
+static int32_t
+hid_sdp_parse_hid_descriptor(sdp_attr_p a)
+{
+ uint8_t *ptr = a->value;
+ uint8_t *end = a->value + a->vlen;
+ int32_t type, len, descriptor_type;
+
+ if (end - ptr < 9)
+ return (-1);
+
+ SDP_GET8(type, ptr);
+ switch (type) {
+ case SDP_DATA_SEQ8:
+ SDP_GET8(len, ptr);
+ break;
+
+ case SDP_DATA_SEQ16:
+ SDP_GET16(len, ptr);
+ break;
+
+ case SDP_DATA_SEQ32:
+ SDP_GET32(len, ptr);
+ break;
+
+ default:
+ return (-1);
+ }
+ if (ptr + len > end)
+ return (-1);
+
+ while (ptr < end) {
+ /* Descriptor */
+ SDP_GET8(type, ptr);
+ switch (type) {
+ case SDP_DATA_SEQ8:
+ if (ptr + 1 > end)
+ return (-1);
+ SDP_GET8(len, ptr);
+ break;
+
+ case SDP_DATA_SEQ16:
+ if (ptr + 2 > end)
+ return (-1);
+ SDP_GET16(len, ptr);
+ break;
+
+ case SDP_DATA_SEQ32:
+ if (ptr + 4 > end)
+ return (-1);
+ SDP_GET32(len, ptr);
+ break;
+
+ default:
+ return (-1);
+ }
+
+ /* Descripor type */
+ if (ptr + 1 > end)
+ return (-1);
+ SDP_GET8(type, ptr);
+ if (type != SDP_DATA_UINT8 || ptr + 1 > end)
+ return (-1);
+ SDP_GET8(descriptor_type, ptr);
+
+ /* Descriptor value */
+ if (ptr + 1 > end)
+ return (-1);
+ SDP_GET8(type, ptr);
+ switch (type) {
+ case SDP_DATA_STR8:
+ if (ptr + 1 > end)
+ return (-1);
+ SDP_GET8(len, ptr);
+ break;
+
+ case SDP_DATA_STR16:
+ if (ptr + 2 > end)
+ return (-1);
+ SDP_GET16(len, ptr);
+ break;
+
+ case SDP_DATA_STR32:
+ if (ptr + 4 > end)
+ return (-1);
+ SDP_GET32(len, ptr);
+ break;
+
+ default:
+ return (-1);
+ }
+ if (ptr + len > end)
+ return (-1);
+
+ if (descriptor_type == UDESC_REPORT && len > 0) {
+ a->value = ptr;
+ a->vlen = len;
+
+ return (0);
+ }
+
+ ptr += len;
+ }
+
+ return (-1);
+}
+
+/* bool8 int8 */
+static int32_t
+hid_sdp_parse_boolean(sdp_attr_p a)
+{
+ if (a->vlen != 2 || a->value[0] != SDP_DATA_BOOL)
+ return (-1);
+
+ return (a->value[1]);
+}
+
+/* Perform SDP query */
+static int32_t
+hid_query(bdaddr_t *bdaddr, int argc, char **argv)
+{
+ struct hid_device hd;
+ int e;
+
+ memcpy(&hd.bdaddr, bdaddr, sizeof(hd.bdaddr));
+ if (hid_sdp_query(NULL, &hd, &e) < 0) {
+ fprintf(stderr, "Could not perform SDP query on the " \
+ "device %s. %s (%d)\n", bt_ntoa(bdaddr, NULL),
+ strerror(e), e);
+ return (FAILED);
+ }
+
+ print_hid_device(&hd, stdout);
+
+ return (OK);
+}
+
+struct bthid_command sdp_commands[] =
+{
+{
+"Query",
+"Perform SDP query to the specified device and print HID configuration entry\n"\
+"for the device. The configuration entry should be appended to the Bluetooth\n"\
+"HID daemon configuration file and the daemon should be restarted.\n",
+hid_query
+},
+{ NULL, NULL, NULL }
+};
+
diff --git a/usr.sbin/bluetooth/bthidd/Makefile b/usr.sbin/bluetooth/bthidd/Makefile
new file mode 100644
index 0000000..128bd20
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidd/Makefile
@@ -0,0 +1,17 @@
+# $Id: Makefile,v 1.6 2006/09/07 21:36:55 max Exp $
+# $FreeBSD$
+
+PROG= bthidd
+MAN= bthidd.8
+# bthidd.conf.5
+SRCS= bthidd.c client.c hid.c kbd.c lexer.l parser.y server.c \
+ session.c
+
+CFLAGS+= -I${.CURDIR}
+WARNS?= 6
+DEBUG_FLAGS= -g
+
+DPADD= ${LIBBLUETOOTH} ${LIBUSBHID}
+LDADD= -lbluetooth -lusbhid
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bluetooth/bthidd/bthid_config.h b/usr.sbin/bluetooth/bthidd/bthid_config.h
new file mode 100644
index 0000000..71cb425
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidd/bthid_config.h
@@ -0,0 +1,70 @@
+/*
+ * bthid_config.h
+ */
+
+/*-
+ * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: bthid_config.h,v 1.4 2006/09/07 21:06:53 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _BTHID_CONFIG_H_
+#define _BTHID_CONFIG_H_ 1
+
+#define BTHIDD_CONFFILE "/etc/bluetooth/bthidd.conf"
+#define BTHIDD_HIDSFILE "/var/db/bthidd.hids"
+
+struct hid_device
+{
+ bdaddr_t bdaddr; /* HID device BDADDR */
+ uint16_t control_psm; /* control PSM */
+ uint16_t interrupt_psm; /* interrupt PSM */
+ unsigned new_device : 1;
+ unsigned reconnect_initiate : 1;
+ unsigned battery_power : 1;
+ unsigned normally_connectable : 1;
+ unsigned keyboard : 1;
+ unsigned reserved : 11;
+ report_desc_t desc; /* HID report descriptor */
+ LIST_ENTRY(hid_device) next; /* link to the next */
+};
+typedef struct hid_device hid_device_t;
+typedef struct hid_device * hid_device_p;
+
+extern char const *config_file;
+extern char const *hids_file;
+
+int32_t read_config_file (void);
+void clean_config (void);
+hid_device_p get_hid_device (bdaddr_p bdaddr);
+hid_device_p get_next_hid_device (hid_device_p d);
+void print_hid_device (hid_device_p hid_device, FILE *f);
+
+int32_t read_hids_file (void);
+int32_t write_hids_file (void);
+
+#endif /* ndef _BTHID_CONFIG_H_ */
+
diff --git a/usr.sbin/bluetooth/bthidd/bthidd.8 b/usr.sbin/bluetooth/bthidd/bthidd.8
new file mode 100644
index 0000000..37aaa3d
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidd/bthidd.8
@@ -0,0 +1,127 @@
+.\" Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: bthidd.8,v 1.1 2006/09/07 21:36:55 max Exp $
+.\" $FreeBSD$
+.\"
+.Dd September 7, 2006
+.Dt BTHIDD 8
+.Os
+.Sh NAME
+.Nm bthidd
+.Nd Bluetooth HID daemon
+.Sh SYNOPSIS
+.Nm
+.Fl h
+.Nm
+.Op Fl a Ar BD_ADDR
+.Op Fl c Ar file
+.Op Fl H Ar file
+.Op Fl p Ar file
+.Op Fl t Ar val
+.Sh DESCRIPTION
+The
+.Nm
+daemon handles remote Bluetooth HID devices.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl a Ar BD_ADDR
+Specify the local address to listen on.
+By default, the server will listen on
+.Dv ANY
+address.
+The address can be specified as BD_ADDR or name.
+If a name was specified, the
+.Nm
+daemon will attempt to resolve the name via
+.Xr bt_gethostbyname 3 .
+.It Fl c Ar file
+Specify path to the configuration file.
+The default path is
+.Pa /etc/bluetooth/bthidd.conf .
+.It Fl d
+Do not detach from the controlling terminal, i.e., run in foreground.
+.It Fl H Ar file
+Specify path to the known HIDs file.
+The default path is
+.Pa /var/db/bthidd.hids .
+.It Fl h
+Display usage message and exit.
+.It Fl p Ar file
+Specify path to the PID file.
+The default path is
+.Pa /var/run/bthidd.pid .
+.It Fl t Ar val
+Specify client rescan interval in seconds.
+The
+.Nm
+daemon will periodically scan for newly configured Bluetooth HID devices or
+disconnected
+.Dq passive
+Bluetooth HID devices and will attempt to establish an outgoing connection.
+The default rescan interval is 10 seconds.
+.El
+.Sh CAVEATS
+Any Bluetooth HID device that has
+.Dv HUP_KEYBOARD
+or
+.Dv HUP_CONSUMER
+entries in its descriptor is considered as
+.Dq keyboard .
+For each
+.Dq keyboard
+Bluetooth HID device,
+the
+.Nm
+daemon will use a separate instance of the virtual keyboard interface
+.Xr vkbd 4 .
+Therefore the
+.Xr kbdmux 4
+driver must be used to properly multiplex input from multiple keyboards.
+.Sh KNOWN LIMITATIONS
+The
+.Nm
+daemon currently does not handle key auto repeat and double click mouse events.
+Those events work under
+.Xr X 7
+just fine,
+but not in text console.
+.Pp
+This manual page needs more work.
+A manual page documenting the format of the
+.Pa /etc/bluetooth/bthidd.conf
+configuration file is needed as well.
+.Sh FILES
+.Bl -tag -width ".Pa /etc/bluetooth/bthidd.conf" -compact
+.It Pa /etc/bluetooth/bthidd.conf
+.It Pa /var/db/bthidd.hids
+.It Pa /var/run/bthidd.pid
+.El
+.Sh SEE ALSO
+.Xr kbdmux 4 ,
+.Xr vkbd 4 ,
+.Xr bthidcontrol 8
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
diff --git a/usr.sbin/bluetooth/bthidd/bthidd.c b/usr.sbin/bluetooth/bthidd/bthidd.c
new file mode 100644
index 0000000..b93fd3d
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidd/bthidd.c
@@ -0,0 +1,266 @@
+/*
+ * bthidd.c
+ */
+
+/*-
+ * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: bthidd.c,v 1.8 2006/09/07 21:06:53 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/time.h>
+#include <sys/queue.h>
+#include <assert.h>
+#include <bluetooth.h>
+#include <err.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <usbhid.h>
+#include "bthid_config.h"
+#include "bthidd.h"
+
+static int32_t write_pid_file (char const *file);
+static int32_t remove_pid_file (char const *file);
+static int32_t elapsed (int32_t tval);
+static void sighandler (int32_t s);
+static void usage (void);
+
+/*
+ * bthidd
+ */
+
+static int32_t done = 0; /* are we done? */
+
+int32_t
+main(int32_t argc, char *argv[])
+{
+ struct bthid_server srv;
+ struct sigaction sa;
+ char const *pid_file = BTHIDD_PIDFILE;
+ char *ep;
+ int32_t opt, detach, tval;
+
+ memset(&srv, 0, sizeof(srv));
+ memset(&srv.bdaddr, 0, sizeof(srv.bdaddr));
+ detach = 1;
+ tval = 10; /* sec */
+
+ while ((opt = getopt(argc, argv, "a:c:dH:hp:t:")) != -1) {
+ switch (opt) {
+ case 'a': /* BDADDR */
+ if (!bt_aton(optarg, &srv.bdaddr)) {
+ struct hostent *he;
+
+ if ((he = bt_gethostbyname(optarg)) == NULL)
+ errx(1, "%s: %s", optarg, hstrerror(h_errno));
+
+ memcpy(&srv.bdaddr, he->h_addr, sizeof(srv.bdaddr));
+ }
+ break;
+
+ case 'c': /* config file */
+ config_file = optarg;
+ break;
+
+ case 'd': /* do not detach */
+ detach = 0;
+ break;
+
+ case 'H': /* hids file */
+ hids_file = optarg;
+ break;
+
+ case 'p': /* pid file */
+ pid_file = optarg;
+ break;
+
+ case 't': /* rescan interval */
+ tval = strtol(optarg, (char **) &ep, 10);
+ if (*ep != '\0' || tval <= 0)
+ usage();
+ break;
+
+ case 'h':
+ default:
+ usage();
+ /* NOT REACHED */
+ }
+ }
+
+ openlog(BTHIDD_IDENT, LOG_PID|LOG_PERROR|LOG_NDELAY, LOG_USER);
+
+ /* Become daemon if required */
+ if (detach && daemon(0, 0) < 0) {
+ syslog(LOG_CRIT, "Could not become daemon. %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ /* Install signal handler */
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = sighandler;
+
+ if (sigaction(SIGTERM, &sa, NULL) < 0 ||
+ sigaction(SIGHUP, &sa, NULL) < 0 ||
+ sigaction(SIGINT, &sa, NULL) < 0) {
+ syslog(LOG_CRIT, "Could not install signal handlers. %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ sa.sa_handler = SIG_IGN;
+ if (sigaction(SIGPIPE, &sa, NULL) < 0) {
+ syslog(LOG_CRIT, "Could not install signal handlers. %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ sa.sa_handler = SIG_IGN;
+ sa.sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT;
+ if (sigaction(SIGCHLD, &sa, NULL) < 0) {
+ syslog(LOG_CRIT, "Could not install signal handlers. %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ if (read_config_file() < 0 || read_hids_file() < 0 ||
+ server_init(&srv) < 0 || write_pid_file(pid_file) < 0)
+ exit(1);
+
+ for (done = 0; !done; ) {
+ if (elapsed(tval))
+ client_rescan(&srv);
+
+ if (server_do(&srv) < 0)
+ break;
+ }
+
+ server_shutdown(&srv);
+ remove_pid_file(pid_file);
+ clean_config();
+ closelog();
+
+ return (0);
+}
+
+/*
+ * Write pid file
+ */
+
+static int32_t
+write_pid_file(char const *file)
+{
+ FILE *pid;
+
+ assert(file != NULL);
+
+ if ((pid = fopen(file, "w")) == NULL) {
+ syslog(LOG_ERR, "Could not open file %s. %s (%d)",
+ file, strerror(errno), errno);
+ return (-1);
+ }
+
+ fprintf(pid, "%d", getpid());
+ fclose(pid);
+
+ return (0);
+}
+
+/*
+ * Remote pid file
+ */
+
+static int32_t
+remove_pid_file(char const *file)
+{
+ assert(file != NULL);
+
+ if (unlink(file) < 0) {
+ syslog(LOG_ERR, "Could not unlink file %s. %s (%d)",
+ file, strerror(errno), errno);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Returns true if desired time interval has elapsed
+ */
+
+static int32_t
+elapsed(int32_t tval)
+{
+ static struct timeval last = { 0, 0 };
+ struct timeval now;
+
+ gettimeofday(&now, NULL);
+
+ if (now.tv_sec - last.tv_sec >= tval) {
+ last = now;
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * Signal handler
+ */
+
+static void
+sighandler(int32_t s)
+{
+ syslog(LOG_NOTICE, "Got signal %d, total number of signals %d",
+ s, ++ done);
+}
+
+/*
+ * Display usage and exit
+ */
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+"Usage: %s [options]\n" \
+"Where options are:\n" \
+" -a address specify address to listen on (default ANY)\n" \
+" -c file specify config file name\n" \
+" -d run in foreground\n" \
+" -H file specify known HIDs file name\n" \
+" -h display this message\n" \
+" -p file specify PID file name\n" \
+" -t tval specify client rescan interval (sec)\n" \
+"", BTHIDD_IDENT);
+ exit(255);
+}
+
diff --git a/usr.sbin/bluetooth/bthidd/bthidd.conf.sample b/usr.sbin/bluetooth/bthidd/bthidd.conf.sample
new file mode 100644
index 0000000..e23dc12
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidd/bthidd.conf.sample
@@ -0,0 +1,72 @@
+# $FreeBSD$
+
+device {
+ bdaddr 00:50:f2:e5:68:84;
+ control_psm 0x11;
+ interrupt_psm 0x13;
+ reconnect_initiate true;
+ normally_connectable false;
+ hid_descriptor {
+ 0x05 0x01 0x09 0x02 0xa1 0x01 0x85 0x02
+ 0x09 0x01 0xa1 0x00 0x05 0x09 0x19 0x01
+ 0x29 0x05 0x15 0x00 0x25 0x01 0x75 0x01
+ 0x95 0x05 0x81 0x02 0x75 0x03 0x95 0x01
+ 0x81 0x01 0x05 0x01 0x09 0x30 0x09 0x31
+ 0x09 0x38 0x15 0x81 0x25 0x7f 0x75 0x08
+ 0x95 0x03 0x81 0x06 0xc0 0xc0 0x05 0x0c
+ 0x09 0x01 0xa1 0x01 0x85 0x03 0x05 0x01
+ 0x09 0x02 0xa1 0x02 0x06 0x00 0xff 0x15
+ 0x00 0x25 0x03 0x95 0x01 0x75 0x02 0x0a
+ 0x01 0xfe 0x81 0x02 0x75 0x06 0x81 0x01
+ 0xc0 0xc0
+ };
+}
+
+device {
+ bdaddr 00:50:f2:e3:fb:e1;
+ control_psm 0x11;
+ interrupt_psm 0x13;
+ reconnect_initiate true;
+ normally_connectable false;
+ hid_descriptor {
+ 0x05 0x01 0x09 0x06 0xa1 0x01 0x85 0x01
+ 0x05 0x08 0x19 0x01 0x29 0x03 0x15 0x00
+ 0x25 0x01 0x75 0x01 0x95 0x03 0x91 0x02
+ 0x09 0x4b 0x95 0x01 0x91 0x02 0x95 0x04
+ 0x91 0x01 0x05 0x07 0x19 0xe0 0x29 0xe7
+ 0x95 0x08 0x81 0x02 0x75 0x08 0x95 0x01
+ 0x81 0x01 0x19 0x00 0x29 0x91 0x26 0xff
+ 0x00 0x95 0x06 0x81 0x00 0xc0 0x05 0x0c
+ 0x09 0x01 0xa1 0x01 0x85 0x02 0x05 0x0c
+ 0x15 0x00 0x25 0x01 0x75 0x01 0x95 0x1c
+ 0x09 0xe2 0x09 0xb7 0x09 0xcd 0x09 0xea
+ 0x09 0xe9 0x09 0xb6 0x09 0xb5 0x0a 0x83
+ 0x01 0x0a 0x1a 0x02 0x0a 0x79 0x02 0x0a
+ 0xab 0x01 0x0a 0x08 0x02 0x0a 0x02 0x02
+ 0x0a 0x03 0x02 0x0a 0x07 0x02 0x0a 0x01
+ 0x02 0x0a 0x92 0x01 0x0a 0x9c 0x01 0x09
+ 0x95 0x0a 0x23 0x02 0x0a 0x89 0x02 0x0a
+ 0x8b 0x02 0x0a 0x8c 0x02 0x0a 0x8a 0x01
+ 0x0a 0x99 0x01 0x0a 0xa7 0x01 0x0a 0xb6
+ 0x01 0x0a 0xb7 0x01 0x81 0x02 0x75 0x01
+ 0x95 0x04 0x81 0x01 0x06 0x00 0xff 0x0a
+ 0x02 0xff 0x26 0xff 0x00 0x95 0x01 0x75
+ 0x08 0x81 0x02 0xc0 0x05 0x01 0x09 0x80
+ 0xa1 0x01 0x85 0x03 0x19 0x81 0x29 0x83
+ 0x25 0x01 0x95 0x03 0x75 0x01 0x81 0x02
+ 0x95 0x05 0x81 0x01 0xc0 0x05 0x0c 0x09
+ 0x01 0xa1 0x01 0x85 0x04 0x05 0x01 0x09
+ 0x06 0xa1 0x02 0x06 0x00 0xff 0x15 0x00
+ 0x25 0x03 0x95 0x01 0x75 0x02 0x0a 0x01
+ 0xfe 0x81 0x02 0x75 0x06 0x81 0x01 0xc0
+ 0xc0 0x05 0x0c 0x09 0x01 0xa1 0x01 0x85
+ 0x05 0x05 0x01 0x09 0x06 0xa1 0x02 0x06
+ 0x00 0xff 0x25 0x01 0x75 0x01 0x95 0x02
+ 0x0a 0x03 0xfe 0x0a 0x04 0xfe 0x81 0x02
+ 0x95 0x06 0x81 0x01 0xc0 0xc0 0x05 0x0c
+ 0x09 0x01 0xa1 0x01 0x85 0xff 0x05 0x06
+ 0x95 0x01 0x75 0x02 0x19 0x24 0x29 0x26
+ 0x81 0x02 0x75 0x06 0x81 0x01 0xc0
+ };
+}
+
diff --git a/usr.sbin/bluetooth/bthidd/bthidd.h b/usr.sbin/bluetooth/bthidd/bthidd.h
new file mode 100644
index 0000000..3485fc3
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidd/bthidd.h
@@ -0,0 +1,93 @@
+/*
+ * bthidd.h
+ */
+
+/*-
+ * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: bthidd.h,v 1.7 2006/09/07 21:06:53 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _BTHIDD_H_
+#define _BTHIDD_H_ 1
+
+#define BTHIDD_IDENT "bthidd"
+#define BTHIDD_PIDFILE "/var/run/" BTHIDD_IDENT ".pid"
+
+struct bthid_session;
+
+struct bthid_server
+{
+ bdaddr_t bdaddr; /* local bdaddr */
+ int32_t cons; /* /dev/consolectl */
+ int32_t ctrl; /* control channel (listen) */
+ int32_t intr; /* intr. channel (listen) */
+ int32_t maxfd; /* max fd in sets */
+ fd_set rfdset; /* read descriptor set */
+ fd_set wfdset; /* write descriptor set */
+ LIST_HEAD(, bthid_session) sessions;
+};
+
+typedef struct bthid_server bthid_server_t;
+typedef struct bthid_server * bthid_server_p;
+
+struct bthid_session
+{
+ bthid_server_p srv; /* pointer back to server */
+ int32_t ctrl; /* control channel */
+ int32_t intr; /* interrupt channel */
+ int32_t vkbd; /* virual keyboard */
+ bdaddr_t bdaddr;/* remote bdaddr */
+ uint16_t state; /* session state */
+#define CLOSED 0
+#define W4CTRL 1
+#define W4INTR 2
+#define OPEN 3
+ bitstr_t *keys1; /* keys map (new) */
+ bitstr_t *keys2; /* keys map (old) */
+ LIST_ENTRY(bthid_session) next; /* link to next */
+};
+
+typedef struct bthid_session bthid_session_t;
+typedef struct bthid_session * bthid_session_p;
+
+int32_t server_init (bthid_server_p srv);
+void server_shutdown (bthid_server_p srv);
+int32_t server_do (bthid_server_p srv);
+
+int32_t client_rescan (bthid_server_p srv);
+int32_t client_connect (bthid_server_p srv, int fd);
+
+bthid_session_p session_open (bthid_server_p srv, hid_device_p const d);
+bthid_session_p session_by_bdaddr(bthid_server_p srv, bdaddr_p bdaddr);
+bthid_session_p session_by_fd (bthid_server_p srv, int32_t fd);
+void session_close (bthid_session_p s);
+
+int32_t hid_control (bthid_session_p s, uint8_t *data, int32_t len);
+int32_t hid_interrupt (bthid_session_p s, uint8_t *data, int32_t len);
+
+#endif /* ndef _BTHIDD_H_ */
+
diff --git a/usr.sbin/bluetooth/bthidd/client.c b/usr.sbin/bluetooth/bthidd/client.c
new file mode 100644
index 0000000..59f0d19
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidd/client.c
@@ -0,0 +1,256 @@
+/*
+ * client.c
+ */
+
+/*-
+ * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: client.c,v 1.7 2006/09/07 21:06:53 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <assert.h>
+#include <bluetooth.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <usbhid.h>
+#include "bthid_config.h"
+#include "bthidd.h"
+
+static int32_t client_socket(bdaddr_p bdaddr, uint16_t psm);
+
+/*
+ * Get next config entry and create outbound connection (if required)
+ *
+ * XXX Do only one device at a time. At least one of my devices (3COM
+ * Bluetooth PCCARD) rejects Create_Connection command if another
+ * Create_Connection command is still pending. Weird...
+ */
+
+static int32_t connect_in_progress = 0;
+
+int32_t
+client_rescan(bthid_server_p srv)
+{
+ static hid_device_p d;
+ bthid_session_p s;
+
+ assert(srv != NULL);
+
+ if (connect_in_progress)
+ return (0); /* another connect is still pending */
+
+ d = get_next_hid_device(d);
+ if (d == NULL)
+ return (0); /* XXX should not happen? empty config? */
+
+ if ((s = session_by_bdaddr(srv, &d->bdaddr)) != NULL)
+ return (0); /* session already active */
+
+ if (!d->new_device) {
+ if (d->reconnect_initiate)
+ return (0); /* device will initiate reconnect */
+ }
+
+ syslog(LOG_NOTICE, "Opening outbound session for %s " \
+ "(new_device=%d, reconnect_initiate=%d)",
+ bt_ntoa(&d->bdaddr, NULL), d->new_device, d->reconnect_initiate);
+
+ if ((s = session_open(srv, d)) == NULL) {
+ syslog(LOG_CRIT, "Could not create outbound session for %s",
+ bt_ntoa(&d->bdaddr, NULL));
+ return (-1);
+ }
+
+ /* Open control channel */
+ s->ctrl = client_socket(&s->bdaddr, d->control_psm);
+ if (s->ctrl < 0) {
+ syslog(LOG_ERR, "Could not open control channel to %s. %s (%d)",
+ bt_ntoa(&s->bdaddr, NULL), strerror(errno), errno);
+ session_close(s);
+ return (-1);
+ }
+
+ s->state = W4CTRL;
+
+ FD_SET(s->ctrl, &srv->wfdset);
+ if (s->ctrl > srv->maxfd)
+ srv->maxfd = s->ctrl;
+
+ connect_in_progress = 1;
+
+ return (0);
+}
+
+/*
+ * Process connect on the socket
+ */
+
+int32_t
+client_connect(bthid_server_p srv, int32_t fd)
+{
+ bthid_session_p s;
+ hid_device_p d;
+ int32_t error;
+ socklen_t len;
+
+ assert(srv != NULL);
+ assert(fd >= 0);
+
+ s = session_by_fd(srv, fd);
+ assert(s != NULL);
+
+ d = get_hid_device(&s->bdaddr);
+ assert(d != NULL);
+
+ error = 0;
+ len = sizeof(error);
+ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0) {
+ syslog(LOG_ERR, "Could not get socket error for %s. %s (%d)",
+ bt_ntoa(&s->bdaddr, NULL), strerror(errno), errno);
+ session_close(s);
+ connect_in_progress = 0;
+
+ return (-1);
+ }
+
+ if (error != 0) {
+ syslog(LOG_ERR, "Could not connect to %s. %s (%d)",
+ bt_ntoa(&s->bdaddr, NULL), strerror(error), error);
+ session_close(s);
+ connect_in_progress = 0;
+
+ return (0);
+ }
+
+ switch (s->state) {
+ case W4CTRL: /* Control channel is open */
+ assert(s->ctrl == fd);
+ assert(s->intr == -1);
+
+ /* Open interrupt channel */
+ s->intr = client_socket(&s->bdaddr, d->interrupt_psm);
+ if (s->intr < 0) {
+ syslog(LOG_ERR, "Could not open interrupt channel " \
+ "to %s. %s (%d)", bt_ntoa(&s->bdaddr, NULL),
+ strerror(errno), errno);
+ session_close(s);
+ connect_in_progress = 0;
+
+ return (-1);
+ }
+
+ s->state = W4INTR;
+
+ FD_SET(s->intr, &srv->wfdset);
+ if (s->intr > srv->maxfd)
+ srv->maxfd = s->intr;
+
+ d->new_device = 0; /* reset new device flag */
+ write_hids_file();
+ break;
+
+ case W4INTR: /* Interrupt channel is open */
+ assert(s->ctrl != -1);
+ assert(s->intr == fd);
+
+ s->state = OPEN;
+ connect_in_progress = 0;
+
+ /* Register session's vkbd descriptor (if any) for read */
+ if (s->state == OPEN && d->keyboard) {
+ assert(s->vkbd != -1);
+
+ FD_SET(s->vkbd, &srv->rfdset);
+ if (s->vkbd > srv->maxfd)
+ srv->maxfd = s->vkbd;
+ }
+ break;
+
+ default:
+ assert(0);
+ break;
+ }
+
+ /* Move fd to from the write fd set into read fd set */
+ FD_CLR(fd, &srv->wfdset);
+ FD_SET(fd, &srv->rfdset);
+
+ return (0);
+}
+
+/*
+ * Create bound non-blocking socket and initiate connect
+ */
+
+static int
+client_socket(bdaddr_p bdaddr, uint16_t psm)
+{
+ struct sockaddr_l2cap l2addr;
+ int32_t s, m;
+
+ s = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP);
+ if (s < 0)
+ return (-1);
+
+ m = fcntl(s, F_GETFL);
+ if (m < 0) {
+ close(s);
+ return (-1);
+ }
+
+ if (fcntl(s, F_SETFL, (m|O_NONBLOCK)) < 0) {
+ close(s);
+ return (-1);
+ }
+
+ l2addr.l2cap_len = sizeof(l2addr);
+ l2addr.l2cap_family = AF_BLUETOOTH;
+ memset(&l2addr.l2cap_bdaddr, 0, sizeof(l2addr.l2cap_bdaddr));
+ l2addr.l2cap_psm = 0;
+
+ if (bind(s, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) {
+ close(s);
+ return (-1);
+ }
+
+ memcpy(&l2addr.l2cap_bdaddr, bdaddr, sizeof(l2addr.l2cap_bdaddr));
+ l2addr.l2cap_psm = htole16(psm);
+
+ if (connect(s, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0 &&
+ errno != EINPROGRESS) {
+ close(s);
+ return (-1);
+ }
+
+ return (s);
+}
+
diff --git a/usr.sbin/bluetooth/bthidd/hid.c b/usr.sbin/bluetooth/bthidd/hid.c
new file mode 100644
index 0000000..9b5e5ec
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidd/hid.c
@@ -0,0 +1,402 @@
+/*
+ * hid.c
+ */
+
+/*-
+ * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: hid.c,v 1.5 2006/09/07 21:06:53 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/consio.h>
+#include <sys/mouse.h>
+#include <sys/queue.h>
+#include <assert.h>
+#include <bluetooth.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <usbhid.h>
+#include "bthid_config.h"
+#include "bthidd.h"
+#include "kbd.h"
+
+#undef min
+#define min(x, y) (((x) < (y))? (x) : (y))
+
+#undef ASIZE
+#define ASIZE(a) (sizeof(a)/sizeof(a[0]))
+
+/*
+ * Process data from control channel
+ */
+
+int32_t
+hid_control(bthid_session_p s, uint8_t *data, int32_t len)
+{
+ assert(s != NULL);
+ assert(data != NULL);
+ assert(len > 0);
+
+ switch (data[0] >> 4) {
+ case 0: /* Handshake (response to command) */
+ if (data[0] & 0xf)
+ syslog(LOG_ERR, "Got handshake message with error " \
+ "response 0x%x from %s",
+ data[0], bt_ntoa(&s->bdaddr, NULL));
+ break;
+
+ case 1: /* HID Control */
+ switch (data[0] & 0xf) {
+ case 0: /* NOP */
+ break;
+
+ case 1: /* Hard reset */
+ case 2: /* Soft reset */
+ syslog(LOG_WARNING, "Device %s requested %s reset",
+ bt_ntoa(&s->bdaddr, NULL),
+ ((data[0] & 0xf) == 1)? "hard" : "soft");
+ break;
+
+ case 3: /* Suspend */
+ syslog(LOG_NOTICE, "Device %s requested Suspend",
+ bt_ntoa(&s->bdaddr, NULL));
+ break;
+
+ case 4: /* Exit suspend */
+ syslog(LOG_NOTICE, "Device %s requested Exit Suspend",
+ bt_ntoa(&s->bdaddr, NULL));
+ break;
+
+ case 5: /* Virtual cable unplug */
+ syslog(LOG_NOTICE, "Device %s unplugged virtual cable",
+ bt_ntoa(&s->bdaddr, NULL));
+ session_close(s);
+ break;
+
+ default:
+ syslog(LOG_WARNING, "Device %s sent unknown " \
+ "HID_Control message 0x%x",
+ bt_ntoa(&s->bdaddr, NULL), data[0]);
+ break;
+ }
+ break;
+
+ default:
+ syslog(LOG_WARNING, "Got unexpected message 0x%x on Control " \
+ "channel from %s", data[0], bt_ntoa(&s->bdaddr, NULL));
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * Process data from the interrupt channel
+ */
+
+int32_t
+hid_interrupt(bthid_session_p s, uint8_t *data, int32_t len)
+{
+ hid_device_p hid_device;
+ hid_data_t d;
+ hid_item_t h;
+ int32_t report_id, usage, page, val,
+ mouse_x, mouse_y, mouse_z, mouse_butt,
+ mevents, kevents;
+
+ assert(s != NULL);
+ assert(s->srv != NULL);
+ assert(data != NULL);
+
+ if (len < 3) {
+ syslog(LOG_ERR, "Got short message (%d bytes) on Interrupt " \
+ "channel from %s", len, bt_ntoa(&s->bdaddr, NULL));
+ return (-1);
+ }
+
+ if (data[0] != 0xa1) {
+ syslog(LOG_ERR, "Got unexpected message 0x%x on " \
+ "Interrupt channel from %s",
+ data[0], bt_ntoa(&s->bdaddr, NULL));
+ return (-1);
+ }
+
+ report_id = data[1];
+ data += 2;
+ len -= 2;
+
+ hid_device = get_hid_device(&s->bdaddr);
+ assert(hid_device != NULL);
+
+ mouse_x = mouse_y = mouse_z = mouse_butt = mevents = kevents = 0;
+
+ for (d = hid_start_parse(hid_device->desc, 1 << hid_input, -1);
+ hid_get_item(d, &h) > 0; ) {
+ if ((h.flags & HIO_CONST) || (h.report_ID != report_id))
+ continue;
+
+ page = HID_PAGE(h.usage);
+ usage = HID_USAGE(h.usage);
+ val = hid_get_data(data, &h);
+
+ switch (page) {
+ case HUP_GENERIC_DESKTOP:
+ switch (usage) {
+ case HUG_X:
+ mouse_x = val;
+ mevents ++;
+ break;
+
+ case HUG_Y:
+ mouse_y = val;
+ mevents ++;
+ break;
+
+ case HUG_WHEEL:
+ mouse_z = -val;
+ mevents ++;
+ break;
+
+ case HUG_SYSTEM_SLEEP:
+ if (val)
+ syslog(LOG_NOTICE, "Sleep button pressed");
+ break;
+ }
+ break;
+
+ case HUP_KEYBOARD:
+ kevents ++;
+
+ if (h.flags & HIO_VARIABLE) {
+ if (val && usage < kbd_maxkey())
+ bit_set(s->keys1, usage);
+ } else {
+ if (val && val < kbd_maxkey())
+ bit_set(s->keys1, val);
+
+ data ++;
+ len --;
+
+ len = min(len, h.report_size);
+ while (len > 0) {
+ val = hid_get_data(data, &h);
+ if (val && val < kbd_maxkey())
+ bit_set(s->keys1, val);
+
+ data ++;
+ len --;
+ }
+ }
+ break;
+
+ case HUP_BUTTON:
+ if (usage != 0) {
+ if (usage == 2)
+ usage = 3;
+ else if (usage == 3)
+ usage = 2;
+
+ mouse_butt |= (val << (usage - 1));
+ mevents ++;
+ }
+ break;
+
+ case HUP_CONSUMER:
+ if (!val)
+ break;
+
+ switch (usage) {
+ case 0xb5: /* Scan Next Track */
+ val = 0x19;
+ break;
+
+ case 0xb6: /* Scan Previous Track */
+ val = 0x10;
+ break;
+
+ case 0xb7: /* Stop */
+ val = 0x24;
+ break;
+
+ case 0xcd: /* Play/Pause */
+ val = 0x22;
+ break;
+
+ case 0xe2: /* Mute */
+ val = 0x20;
+ break;
+
+ case 0xe9: /* Volume Up */
+ val = 0x30;
+ break;
+
+ case 0xea: /* Volume Down */
+ val = 0x2E;
+ break;
+
+ case 0x183: /* Media Select */
+ val = 0x6D;
+ break;
+
+ case 0x018a: /* Mail */
+ val = 0x6C;
+ break;
+
+ case 0x192: /* Calculator */
+ val = 0x21;
+ break;
+
+ case 0x194: /* My Computer */
+ val = 0x6B;
+ break;
+
+ case 0x221: /* WWW Search */
+ val = 0x65;
+ break;
+
+ case 0x223: /* WWW Home */
+ val = 0x32;
+ break;
+
+ case 0x224: /* WWW Back */
+ val = 0x6A;
+ break;
+
+ case 0x225: /* WWW Forward */
+ val = 0x69;
+ break;
+
+ case 0x226: /* WWW Stop */
+ val = 0x68;
+ break;
+
+ case 0x227: /* WWW Refresh */
+ val = 0x67;
+ break;
+
+ case 0x22a: /* WWW Favorites */
+ val = 0x66;
+ break;
+
+ default:
+ val = 0;
+ break;
+ }
+
+ /* XXX FIXME - UGLY HACK */
+ if (val != 0) {
+ if (hid_device->keyboard) {
+ int32_t buf[4] = { 0xe0, val,
+ 0xe0, val|0x80 };
+
+ assert(s->vkbd != -1);
+ write(s->vkbd, buf, sizeof(buf));
+ } else
+ syslog(LOG_ERR, "Keyboard events " \
+ "received from non-keyboard " \
+ "device %s. Please report",
+ bt_ntoa(&s->bdaddr, NULL));
+ }
+ break;
+
+ case HUP_MICROSOFT:
+ switch (usage) {
+ case 0xfe01:
+ if (!hid_device->battery_power)
+ break;
+
+ switch (val) {
+ case 1:
+ syslog(LOG_INFO, "Battery is OK on %s",
+ bt_ntoa(&s->bdaddr, NULL));
+ break;
+
+ case 2:
+ syslog(LOG_NOTICE, "Low battery on %s",
+ bt_ntoa(&s->bdaddr, NULL));
+ break;
+
+ case 3:
+ syslog(LOG_WARNING, "Very low battery "\
+ "on %s",
+ bt_ntoa(&s->bdaddr, NULL));
+ break;
+ }
+ break;
+ }
+ break;
+ }
+ }
+ hid_end_parse(d);
+
+ /*
+ * XXX FIXME Feed keyboard events into kernel.
+ * The code below works, bit host also needs to track
+ * and handle repeat.
+ *
+ * Key repeat currently works in X, but not in console.
+ */
+
+ if (kevents > 0) {
+ if (hid_device->keyboard) {
+ assert(s->vkbd != -1);
+ kbd_process_keys(s);
+ } else
+ syslog(LOG_ERR, "Keyboard events received from " \
+ "non-keyboard device %s. Please report",
+ bt_ntoa(&s->bdaddr, NULL));
+ }
+
+ /*
+ * XXX FIXME Feed mouse events into kernel.
+ * The code block below works, but it is not good enough.
+ * Need to track double-clicks etc.
+ *
+ * Double click currently works in X, but not in console.
+ */
+
+ if (mevents > 0) {
+ struct mouse_info mi;
+
+ mi.operation = MOUSE_ACTION;
+ mi.u.data.x = mouse_x;
+ mi.u.data.y = mouse_y;
+ mi.u.data.z = mouse_z;
+ mi.u.data.buttons = mouse_butt;
+
+ if (ioctl(s->srv->cons, CONS_MOUSECTL, &mi) < 0)
+ syslog(LOG_ERR, "Could not process mouse events from " \
+ "%s. %s (%d)", bt_ntoa(&s->bdaddr, NULL),
+ strerror(errno), errno);
+ }
+
+ return (0);
+}
diff --git a/usr.sbin/bluetooth/bthidd/kbd.c b/usr.sbin/bluetooth/bthidd/kbd.c
new file mode 100644
index 0000000..e1ab870
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidd/kbd.c
@@ -0,0 +1,580 @@
+/*
+ * kbd.c
+ */
+
+/*-
+ * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: kbd.c,v 1.4 2006/09/07 21:06:53 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/consio.h>
+#include <sys/ioctl.h>
+#include <sys/kbio.h>
+#include <sys/queue.h>
+#include <sys/wait.h>
+#include <assert.h>
+#include <bluetooth.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+#include <dev/vkbd/vkbd_var.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <usbhid.h>
+#include "bthid_config.h"
+#include "bthidd.h"
+#include "kbd.h"
+
+static void kbd_write(bitstr_t *m, int32_t fb, int32_t make, int32_t fd);
+static int32_t kbd_xlate(int32_t code, int32_t make, int32_t *b, int32_t const *eob);
+
+/*
+ * HID code to PS/2 set 1 code translation table.
+ *
+ * http://www.microsoft.com/whdc/device/input/Scancode.mspx
+ *
+ * The table only contains "make" (key pressed) codes.
+ * The "break" (key released) code is generated as "make" | 0x80
+ */
+
+#define E0PREFIX (1 << 31)
+#define NOBREAK (1 << 30)
+#define CODEMASK (~(E0PREFIX|NOBREAK))
+
+static int32_t const x[] =
+{
+/*==================================================*/
+/* Name HID code Make Break*/
+/*==================================================*/
+/* No Event 00 */ -1, /* None */
+/* Overrun Error 01 */ NOBREAK|0xFF, /* None */
+/* POST Fail 02 */ NOBREAK|0xFC, /* None */
+/* ErrorUndefined 03 */ -1, /* Unassigned */
+/* a A 04 */ 0x1E, /* 9E */
+/* b B 05 */ 0x30, /* B0 */
+/* c C 06 */ 0x2E, /* AE */
+/* d D 07 */ 0x20, /* A0 */
+/* e E 08 */ 0x12, /* 92 */
+/* f F 09 */ 0x21, /* A1 */
+/* g G 0A */ 0x22, /* A2 */
+/* h H 0B */ 0x23, /* A3 */
+/* i I 0C */ 0x17, /* 97 */
+/* j J 0D */ 0x24, /* A4 */
+/* k K 0E */ 0x25, /* A5 */
+/* l L 0F */ 0x26, /* A6 */
+/* m M 10 */ 0x32, /* B2 */
+/* n N 11 */ 0x31, /* B1 */
+/* o O 12 */ 0x18, /* 98 */
+/* p P 13 */ 0x19, /* 99 */
+/* q Q 14 */ 0x10, /* 90 */
+/* r R 15 */ 0x13, /* 93 */
+/* s S 16 */ 0x1F, /* 9F */
+/* t T 17 */ 0x14, /* 94 */
+/* u U 18 */ 0x16, /* 96 */
+/* v V 19 */ 0x2F, /* AF */
+/* w W 1A */ 0x11, /* 91 */
+/* x X 1B */ 0x2D, /* AD */
+/* y Y 1C */ 0x15, /* 95 */
+/* z Z 1D */ 0x2C, /* AC */
+/* 1 ! 1E */ 0x02, /* 82 */
+/* 2 @ 1F */ 0x03, /* 83 */
+/* 3 # 20 */ 0x04, /* 84 */
+/* 4 $ 21 */ 0x05, /* 85 */
+/* 5 % 22 */ 0x06, /* 86 */
+/* 6 ^ 23 */ 0x07, /* 87 */
+/* 7 & 24 */ 0x08, /* 88 */
+/* 8 * 25 */ 0x09, /* 89 */
+/* 9 ( 26 */ 0x0A, /* 8A */
+/* 0 ) 27 */ 0x0B, /* 8B */
+/* Return 28 */ 0x1C, /* 9C */
+/* Escape 29 */ 0x01, /* 81 */
+/* Backspace 2A */ 0x0E, /* 8E */
+/* Tab 2B */ 0x0F, /* 8F */
+/* Space 2C */ 0x39, /* B9 */
+/* - _ 2D */ 0x0C, /* 8C */
+/* = + 2E */ 0x0D, /* 8D */
+/* [ { 2F */ 0x1A, /* 9A */
+/* ] } 30 */ 0x1B, /* 9B */
+/* \ | 31 */ 0x2B, /* AB */
+/* Europe 1 32 */ 0x2B, /* AB */
+/* ; : 33 */ 0x27, /* A7 */
+/* " ' 34 */ 0x28, /* A8 */
+/* ` ~ 35 */ 0x29, /* A9 */
+/* comma < 36 */ 0x33, /* B3 */
+/* . > 37 */ 0x34, /* B4 */
+/* / ? 38 */ 0x35, /* B5 */
+/* Caps Lock 39 */ 0x3A, /* BA */
+/* F1 3A */ 0x3B, /* BB */
+/* F2 3B */ 0x3C, /* BC */
+/* F3 3C */ 0x3D, /* BD */
+/* F4 3D */ 0x3E, /* BE */
+/* F5 3E */ 0x3F, /* BF */
+/* F6 3F */ 0x40, /* C0 */
+/* F7 40 */ 0x41, /* C1 */
+/* F8 41 */ 0x42, /* C2 */
+/* F9 42 */ 0x43, /* C3 */
+/* F10 43 */ 0x44, /* C4 */
+/* F11 44 */ 0x57, /* D7 */
+/* F12 45 */ 0x58, /* D8 */
+/* Print Screen 46 */ E0PREFIX|0x37, /* E0 B7 */
+/* Scroll Lock 47 */ 0x46, /* C6 */
+#if 0
+/* Break (Ctrl-Pause) 48 */ E0 46 E0 C6, /* None */
+/* Pause 48 */ E1 1D 45 E1 9D C5, /* None */
+#else
+/* Break (Ctrl-Pause)/Pause 48 */ NOBREAK /* Special case */, /* None */
+#endif
+/* Insert 49 */ E0PREFIX|0x52, /* E0 D2 */
+/* Home 4A */ E0PREFIX|0x47, /* E0 C7 */
+/* Page Up 4B */ E0PREFIX|0x49, /* E0 C9 */
+/* Delete 4C */ E0PREFIX|0x53, /* E0 D3 */
+/* End 4D */ E0PREFIX|0x4F, /* E0 CF */
+/* Page Down 4E */ E0PREFIX|0x51, /* E0 D1 */
+/* Right Arrow 4F */ E0PREFIX|0x4D, /* E0 CD */
+/* Left Arrow 50 */ E0PREFIX|0x4B, /* E0 CB */
+/* Down Arrow 51 */ E0PREFIX|0x50, /* E0 D0 */
+/* Up Arrow 52 */ E0PREFIX|0x48, /* E0 C8 */
+/* Num Lock 53 */ 0x45, /* C5 */
+/* Keypad / 54 */ E0PREFIX|0x35, /* E0 B5 */
+/* Keypad * 55 */ 0x37, /* B7 */
+/* Keypad - 56 */ 0x4A, /* CA */
+/* Keypad + 57 */ 0x4E, /* CE */
+/* Keypad Enter 58 */ E0PREFIX|0x1C, /* E0 9C */
+/* Keypad 1 End 59 */ 0x4F, /* CF */
+/* Keypad 2 Down 5A */ 0x50, /* D0 */
+/* Keypad 3 PageDn 5B */ 0x51, /* D1 */
+/* Keypad 4 Left 5C */ 0x4B, /* CB */
+/* Keypad 5 5D */ 0x4C, /* CC */
+/* Keypad 6 Right 5E */ 0x4D, /* CD */
+/* Keypad 7 Home 5F */ 0x47, /* C7 */
+/* Keypad 8 Up 60 */ 0x48, /* C8 */
+/* Keypad 9 PageUp 61 */ 0x49, /* C9 */
+/* Keypad 0 Insert 62 */ 0x52, /* D2 */
+/* Keypad . Delete 63 */ 0x53, /* D3 */
+/* Europe 2 64 */ 0x56, /* D6 */
+/* App 65 */ E0PREFIX|0x5D, /* E0 DD */
+/* Keyboard Power 66 */ E0PREFIX|0x5E, /* E0 DE */
+/* Keypad = 67 */ 0x59, /* D9 */
+/* F13 68 */ 0x64, /* E4 */
+/* F14 69 */ 0x65, /* E5 */
+/* F15 6A */ 0x66, /* E6 */
+/* F16 6B */ 0x67, /* E7 */
+/* F17 6C */ 0x68, /* E8 */
+/* F18 6D */ 0x69, /* E9 */
+/* F19 6E */ 0x6A, /* EA */
+/* F20 6F */ 0x6B, /* EB */
+/* F21 70 */ 0x6C, /* EC */
+/* F22 71 */ 0x6D, /* ED */
+/* F23 72 */ 0x6E, /* EE */
+/* F24 73 */ 0x76, /* F6 */
+/* Keyboard Execute 74 */ -1, /* Unassigned */
+/* Keyboard Help 75 */ -1, /* Unassigned */
+/* Keyboard Menu 76 */ -1, /* Unassigned */
+/* Keyboard Select 77 */ -1, /* Unassigned */
+/* Keyboard Stop 78 */ -1, /* Unassigned */
+/* Keyboard Again 79 */ -1, /* Unassigned */
+/* Keyboard Undo 7A */ -1, /* Unassigned */
+/* Keyboard Cut 7B */ -1, /* Unassigned */
+/* Keyboard Copy 7C */ -1, /* Unassigned */
+/* Keyboard Paste 7D */ -1, /* Unassigned */
+/* Keyboard Find 7E */ -1, /* Unassigned */
+/* Keyboard Mute 7F */ -1, /* Unassigned */
+/* Keyboard Volume Up 80 */ -1, /* Unassigned */
+/* Keyboard Volume Dn 81 */ -1, /* Unassigned */
+/* Keyboard Locking Caps Lock 82 */ -1, /* Unassigned */
+/* Keyboard Locking Num Lock 83 */ -1, /* Unassigned */
+/* Keyboard Locking Scroll Lock 84 */ -1, /* Unassigned */
+/* Keypad comma 85 */ 0x7E, /* FE */
+/* Keyboard Equal Sign 86 */ -1, /* Unassigned */
+/* Keyboard Int'l 1 87 */ 0x73, /* F3 */
+/* Keyboard Int'l 2 88 */ 0x70, /* F0 */
+/* Keyboard Int'l 2 89 */ 0x7D, /* FD */
+/* Keyboard Int'l 4 8A */ 0x79, /* F9 */
+/* Keyboard Int'l 5 8B */ 0x7B, /* FB */
+/* Keyboard Int'l 6 8C */ 0x5C, /* DC */
+/* Keyboard Int'l 7 8D */ -1, /* Unassigned */
+/* Keyboard Int'l 8 8E */ -1, /* Unassigned */
+/* Keyboard Int'l 9 8F */ -1, /* Unassigned */
+/* Keyboard Lang 1 90 */ NOBREAK|0xF2, /* None */
+/* Keyboard Lang 2 91 */ NOBREAK|0xF1, /* None */
+/* Keyboard Lang 3 92 */ 0x78, /* F8 */
+/* Keyboard Lang 4 93 */ 0x77, /* F7 */
+/* Keyboard Lang 5 94 */ 0x76, /* F6 */
+/* Keyboard Lang 6 95 */ -1, /* Unassigned */
+/* Keyboard Lang 7 96 */ -1, /* Unassigned */
+/* Keyboard Lang 8 97 */ -1, /* Unassigned */
+/* Keyboard Lang 9 98 */ -1, /* Unassigned */
+/* Keyboard Alternate Erase 99 */ -1, /* Unassigned */
+/* Keyboard SysReq/Attention 9A */ -1, /* Unassigned */
+/* Keyboard Cancel 9B */ -1, /* Unassigned */
+/* Keyboard Clear 9C */ -1, /* Unassigned */
+/* Keyboard Prior 9D */ -1, /* Unassigned */
+/* Keyboard Return 9E */ -1, /* Unassigned */
+/* Keyboard Separator 9F */ -1, /* Unassigned */
+/* Keyboard Out A0 */ -1, /* Unassigned */
+/* Keyboard Oper A1 */ -1, /* Unassigned */
+/* Keyboard Clear/Again A2 */ -1, /* Unassigned */
+/* Keyboard CrSel/Props A3 */ -1, /* Unassigned */
+/* Keyboard ExSel A4 */ -1, /* Unassigned */
+/* Reserved A5 */ -1, /* Reserved */
+/* Reserved A6 */ -1, /* Reserved */
+/* Reserved A7 */ -1, /* Reserved */
+/* Reserved A8 */ -1, /* Reserved */
+/* Reserved A9 */ -1, /* Reserved */
+/* Reserved AA */ -1, /* Reserved */
+/* Reserved AB */ -1, /* Reserved */
+/* Reserved AC */ -1, /* Reserved */
+/* Reserved AD */ -1, /* Reserved */
+/* Reserved AE */ -1, /* Reserved */
+/* Reserved AF */ -1, /* Reserved */
+/* Reserved B0 */ -1, /* Reserved */
+/* Reserved B1 */ -1, /* Reserved */
+/* Reserved B2 */ -1, /* Reserved */
+/* Reserved B3 */ -1, /* Reserved */
+/* Reserved B4 */ -1, /* Reserved */
+/* Reserved B5 */ -1, /* Reserved */
+/* Reserved B6 */ -1, /* Reserved */
+/* Reserved B7 */ -1, /* Reserved */
+/* Reserved B8 */ -1, /* Reserved */
+/* Reserved B9 */ -1, /* Reserved */
+/* Reserved BA */ -1, /* Reserved */
+/* Reserved BB */ -1, /* Reserved */
+/* Reserved BC */ -1, /* Reserved */
+/* Reserved BD */ -1, /* Reserved */
+/* Reserved BE */ -1, /* Reserved */
+/* Reserved BF */ -1, /* Reserved */
+/* Reserved C0 */ -1, /* Reserved */
+/* Reserved C1 */ -1, /* Reserved */
+/* Reserved C2 */ -1, /* Reserved */
+/* Reserved C3 */ -1, /* Reserved */
+/* Reserved C4 */ -1, /* Reserved */
+/* Reserved C5 */ -1, /* Reserved */
+/* Reserved C6 */ -1, /* Reserved */
+/* Reserved C7 */ -1, /* Reserved */
+/* Reserved C8 */ -1, /* Reserved */
+/* Reserved C9 */ -1, /* Reserved */
+/* Reserved CA */ -1, /* Reserved */
+/* Reserved CB */ -1, /* Reserved */
+/* Reserved CC */ -1, /* Reserved */
+/* Reserved CD */ -1, /* Reserved */
+/* Reserved CE */ -1, /* Reserved */
+/* Reserved CF */ -1, /* Reserved */
+/* Reserved D0 */ -1, /* Reserved */
+/* Reserved D1 */ -1, /* Reserved */
+/* Reserved D2 */ -1, /* Reserved */
+/* Reserved D3 */ -1, /* Reserved */
+/* Reserved D4 */ -1, /* Reserved */
+/* Reserved D5 */ -1, /* Reserved */
+/* Reserved D6 */ -1, /* Reserved */
+/* Reserved D7 */ -1, /* Reserved */
+/* Reserved D8 */ -1, /* Reserved */
+/* Reserved D9 */ -1, /* Reserved */
+/* Reserved DA */ -1, /* Reserved */
+/* Reserved DB */ -1, /* Reserved */
+/* Reserved DC */ -1, /* Reserved */
+/* Reserved DD */ -1, /* Reserved */
+/* Reserved DE */ -1, /* Reserved */
+/* Reserved DF */ -1, /* Reserved */
+/* Left Control E0 */ 0x1D, /* 9D */
+/* Left Shift E1 */ 0x2A, /* AA */
+/* Left Alt E2 */ 0x38, /* B8 */
+/* Left GUI E3 */ E0PREFIX|0x5B, /* E0 DB */
+/* Right Control E4 */ E0PREFIX|0x1D, /* E0 9D */
+/* Right Shift E5 */ 0x36, /* B6 */
+/* Right Alt E6 */ E0PREFIX|0x38, /* E0 B8 */
+/* Right GUI E7 */ E0PREFIX|0x5C /* E0 DC */
+};
+
+#define xsize ((int32_t)(sizeof(x)/sizeof(x[0])))
+
+/*
+ * Get a max HID keycode (aligned)
+ */
+
+int32_t
+kbd_maxkey(void)
+{
+ return (xsize);
+}
+
+/*
+ * Process keys
+ */
+
+int32_t
+kbd_process_keys(bthid_session_p s)
+{
+ bitstr_t diff[bitstr_size(xsize)];
+ int32_t f1, f2, i;
+
+ assert(s != NULL);
+ assert(s->srv != NULL);
+
+ /* Check if the new keys have been pressed */
+ bit_ffs(s->keys1, xsize, &f1);
+
+ /* Check if old keys still pressed */
+ bit_ffs(s->keys2, xsize, &f2);
+
+ if (f1 == -1) {
+ /* no new key pressed */
+ if (f2 != -1) {
+ /* release old keys */
+ kbd_write(s->keys2, f2, 0, s->vkbd);
+ memset(s->keys2, 0, bitstr_size(xsize));
+ }
+
+ return (0);
+ }
+
+ if (f2 == -1) {
+ /* no old keys, but new keys pressed */
+ assert(f1 != -1);
+
+ memcpy(s->keys2, s->keys1, bitstr_size(xsize));
+ kbd_write(s->keys1, f1, 1, s->vkbd);
+ memset(s->keys1, 0, bitstr_size(xsize));
+
+ return (0);
+ }
+
+ /* new keys got pressed, old keys got released */
+ memset(diff, 0, bitstr_size(xsize));
+
+ for (i = f2; i < xsize; i ++) {
+ if (bit_test(s->keys2, i)) {
+ if (!bit_test(s->keys1, i)) {
+ bit_clear(s->keys2, i);
+ bit_set(diff, i);
+ }
+ }
+ }
+
+ for (i = f1; i < xsize; i++) {
+ if (bit_test(s->keys1, i)) {
+ if (!bit_test(s->keys2, i))
+ bit_set(s->keys2, i);
+ else
+ bit_clear(s->keys1, i);
+ }
+ }
+
+ bit_ffs(diff, xsize, &f2);
+ if (f2 > 0)
+ kbd_write(diff, f2, 0, s->vkbd);
+
+ bit_ffs(s->keys1, xsize, &f1);
+ if (f1 > 0) {
+ kbd_write(s->keys1, f1, 1, s->vkbd);
+ memset(s->keys1, 0, bitstr_size(xsize));
+ }
+
+ return (0);
+}
+
+/*
+ * Translate given keymap and write keyscodes
+ */
+
+static void
+kbd_write(bitstr_t *m, int32_t fb, int32_t make, int32_t fd)
+{
+ int32_t i, *b, *eob, n, buf[64];
+
+ b = buf;
+ eob = b + sizeof(buf)/sizeof(buf[0]);
+ i = fb;
+
+ while (i < xsize) {
+ if (bit_test(m, i)) {
+ n = kbd_xlate(i, make, b, eob);
+ if (n == -1) {
+ write(fd, buf, (b - buf) * sizeof(buf[0]));
+ b = buf;
+ continue;
+ }
+
+ b += n;
+ }
+
+ i ++;
+ }
+
+ if (b != buf)
+ write(fd, buf, (b - buf) * sizeof(buf[0]));
+}
+
+/*
+ * Translate HID code into PS/2 code and put codes into buffer b.
+ * Returns the number of codes put in b. Return -1 if buffer has not
+ * enough space.
+ */
+
+#undef PUT
+#define PUT(c, n, b, eob) \
+do { \
+ if ((b) >= (eob)) \
+ return (-1); \
+ *(b) = (c); \
+ (b) ++; \
+ (n) ++; \
+} while (0)
+
+static int32_t
+kbd_xlate(int32_t code, int32_t make, int32_t *b, int32_t const *eob)
+{
+ int32_t c, n;
+
+ n = 0;
+
+ if (code >= xsize)
+ return (0); /* HID code is not in the table */
+
+ /* Handle special case - Pause/Break */
+ if (code == 0x48) {
+ if (!make)
+ return (0); /* No break code */
+
+#if 0
+XXX FIXME
+ if (ctrl_is_pressed) {
+ /* Break (Ctrl-Pause) */
+ PUT(0xe0, n, b, eob);
+ PUT(0x46, n, b, eob);
+ PUT(0xe0, n, b, eob);
+ PUT(0xc6, n, b, eob);
+ } else {
+ /* Pause */
+ PUT(0xe1, n, b, eob);
+ PUT(0x1d, n, b, eob);
+ PUT(0x45, n, b, eob);
+ PUT(0xe1, n, b, eob);
+ PUT(0x9d, n, b, eob);
+ PUT(0xc5, n, b, eob);
+ }
+#endif
+
+ return (n);
+ }
+
+ if ((c = x[code]) == -1)
+ return (0); /* HID code translation is not defined */
+
+ if (make) {
+ if (c & E0PREFIX)
+ PUT(0xe0, n, b, eob);
+
+ PUT((c & CODEMASK), n, b, eob);
+ } else if (!(c & NOBREAK)) {
+ if (c & E0PREFIX)
+ PUT(0xe0, n, b, eob);
+
+ PUT((0x80|(c & CODEMASK)), n, b, eob);
+ }
+
+ return (n);
+}
+
+/*
+ * Process status change from vkbd(4)
+ */
+
+int32_t
+kbd_status_changed(bthid_session_p s, uint8_t *data, int32_t len)
+{
+ vkbd_status_t st;
+ uint8_t leds, report_id;
+ hid_device_p hid_device;
+ hid_data_t d;
+ hid_item_t h;
+
+ assert(s != NULL);
+ assert(len == sizeof(vkbd_status_t));
+
+ memcpy(&st, data, sizeof(st));
+ leds = 0;
+ report_id = NO_REPORT_ID;
+
+ hid_device = get_hid_device(&s->bdaddr);
+ assert(hid_device != NULL);
+
+ for (d = hid_start_parse(hid_device->desc, 1 << hid_output, -1);
+ hid_get_item(d, &h) > 0; ) {
+ if (HID_PAGE(h.usage) == HUP_LEDS) {
+ if (report_id == NO_REPORT_ID)
+ report_id = h.report_ID;
+ else if (h.report_ID != report_id)
+ syslog(LOG_WARNING, "Output HID report IDs " \
+ "for %s do not match: %d vs. %d. " \
+ "Please report",
+ bt_ntoa(&s->bdaddr, NULL),
+ h.report_ID, report_id);
+
+ switch(HID_USAGE(h.usage)) {
+ case 0x01: /* Num Lock LED */
+ if (st.leds & LED_NUM)
+ hid_set_data(&leds, &h, 1);
+ break;
+
+ case 0x02: /* Caps Lock LED */
+ if (st.leds & LED_CAP)
+ hid_set_data(&leds, &h, 1);
+ break;
+
+ case 0x03: /* Scroll Lock LED */
+ if (st.leds & LED_SCR)
+ hid_set_data(&leds, &h, 1);
+ break;
+
+ /* XXX add other LEDs ? */
+ }
+ }
+ }
+ hid_end_parse(d);
+
+ data[0] = 0xa2; /* DATA output (HID output report) */
+
+ if (report_id != NO_REPORT_ID) {
+ data[1] = report_id;
+ data[2] = leds;
+ len = 3;
+ } else {
+ data[1] = leds;
+ len = 2;
+ }
+
+ write(s->intr, data, len);
+
+ return (0);
+}
+
diff --git a/usr.sbin/bluetooth/bthidd/kbd.h b/usr.sbin/bluetooth/bthidd/kbd.h
new file mode 100644
index 0000000..552f310
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidd/kbd.h
@@ -0,0 +1,41 @@
+/*
+ * kbd.h
+ */
+
+/*-
+ * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: kbd.h,v 1.3 2006/09/07 21:06:53 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _KBD_H_
+#define _KBD_H_
+
+int32_t kbd_maxkey (void);
+int32_t kbd_process_keys (bthid_session_p s);
+int32_t kbd_status_changed(bthid_session_p s, uint8_t *data, int32_t len);
+
+#endif /* ndef _KBD_H_ */
diff --git a/usr.sbin/bluetooth/bthidd/lexer.l b/usr.sbin/bluetooth/bthidd/lexer.l
new file mode 100644
index 0000000..a25ae92
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidd/lexer.l
@@ -0,0 +1,104 @@
+%{
+/*
+ * lexer.l
+ */
+
+/*-
+ * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: lexer.l,v 1.3 2006/09/07 21:06:53 max Exp $
+ * $FreeBSD$
+ */
+
+#include <bluetooth.h>
+#include <stdlib.h>
+#include "parser.h"
+
+ int yylex (void);
+%}
+
+%option yylineno noyywrap nounput
+
+delim [ \t\n]
+ws {delim}+
+empty {delim}*
+comment \#.*
+
+hexdigit [0-9a-fA-F]
+hexbyte {hexdigit}{hexdigit}?
+
+device_word device
+bdaddr_word bdaddr
+control_psm_word control_psm
+interrupt_psm_word interrupt_psm
+reconnect_initiate_word reconnect_initiate
+battery_power_word battery_power
+normally_connectable_word normally_connectable
+hid_descriptor_word hid_descriptor
+true_word true
+false_word false
+
+bdaddrstring {hexbyte}:{hexbyte}:{hexbyte}:{hexbyte}:{hexbyte}:{hexbyte}
+hexbytestring 0x{hexbyte}
+
+%%
+
+\; return (';');
+\: return (':');
+\{ return ('{');
+\} return ('}');
+
+{ws} ;
+{empty} ;
+{comment} ;
+
+{device_word} return (T_DEVICE);
+{bdaddr_word} return (T_BDADDR);
+{control_psm_word} return (T_CONTROL_PSM);
+{interrupt_psm_word} return (T_INTERRUPT_PSM);
+{reconnect_initiate_word} return (T_RECONNECT_INITIATE);
+{battery_power_word} return (T_BATTERY_POWER);
+{normally_connectable_word} return (T_NORMALLY_CONNECTABLE);
+{hid_descriptor_word} return (T_HID_DESCRIPTOR);
+{true_word} return (T_TRUE);
+{false_word} return (T_FALSE);
+
+{bdaddrstring} {
+ return (bt_aton(yytext, &yylval.bdaddr)?
+ T_BDADDRSTRING : T_ERROR);
+ }
+
+{hexbytestring} {
+ char *ep;
+
+ yylval.num = strtoul(yytext, &ep, 16);
+
+ return (*ep == '\0'? T_HEXBYTE : T_ERROR);
+ }
+
+. return (T_ERROR);
+
+%%
+
diff --git a/usr.sbin/bluetooth/bthidd/parser.y b/usr.sbin/bluetooth/bthidd/parser.y
new file mode 100644
index 0000000..ca49059
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidd/parser.y
@@ -0,0 +1,475 @@
+%{
+/*
+ * parser.y
+ */
+
+/*-
+ * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: parser.y,v 1.7 2006/09/07 21:06:53 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <bluetooth.h>
+#include <dev/usb/usb.h>
+#include <dev/usb/usbhid.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <usbhid.h>
+
+#ifndef BTHIDCONTROL
+#include <stdarg.h>
+#include <syslog.h>
+#define SYSLOG syslog
+#define LOGCRIT LOG_CRIT
+#define LOGERR LOG_ERR
+#define LOGWARNING LOG_WARNING
+#define EOL
+#else
+#define SYSLOG fprintf
+#define LOGCRIT stderr
+#define LOGERR stderr
+#define LOGWARNING stderr
+#define EOL "\n"
+#endif /* ndef BTHIDCONTROL */
+
+#include "bthid_config.h"
+
+ int yyparse (void);
+ int yylex (void);
+ void yyerror (char const *);
+static int32_t check_hid_device(hid_device_p hid_device);
+static void free_hid_device (hid_device_p hid_device);
+
+extern FILE *yyin;
+extern int yylineno;
+ char const *config_file = BTHIDD_CONFFILE;
+ char const *hids_file = BTHIDD_HIDSFILE;
+
+static char buffer[1024];
+static int32_t hid_descriptor_size;
+static hid_device_t *hid_device = NULL;
+static LIST_HEAD(, hid_device) hid_devices;
+
+%}
+
+%union {
+ bdaddr_t bdaddr;
+ int32_t num;
+}
+
+%token <bdaddr> T_BDADDRSTRING
+%token <num> T_HEXBYTE
+%token T_DEVICE T_BDADDR T_CONTROL_PSM T_INTERRUPT_PSM T_RECONNECT_INITIATE
+%token T_BATTERY_POWER T_NORMALLY_CONNECTABLE T_HID_DESCRIPTOR
+%token T_TRUE T_FALSE T_ERROR
+
+%%
+
+config: line
+ | config line
+ ;
+
+line: T_DEVICE
+ {
+ hid_device = (hid_device_t *) calloc(1, sizeof(*hid_device));
+ if (hid_device == NULL) {
+ SYSLOG(LOGCRIT, "Could not allocate new " \
+ "config entry" EOL);
+ YYABORT;
+ }
+
+ hid_device->new_device = 1;
+ }
+ '{' options '}'
+ {
+ if (check_hid_device(hid_device))
+ LIST_INSERT_HEAD(&hid_devices,hid_device,next);
+ else
+ free_hid_device(hid_device);
+
+ hid_device = NULL;
+ }
+ ;
+
+options: option ';'
+ | options option ';'
+ ;
+
+option: bdaddr
+ | control_psm
+ | interrupt_psm
+ | reconnect_initiate
+ | battery_power
+ | normally_connectable
+ | hid_descriptor
+ | parser_error
+ ;
+
+bdaddr: T_BDADDR T_BDADDRSTRING
+ {
+ memcpy(&hid_device->bdaddr, &$2, sizeof(hid_device->bdaddr));
+ }
+ ;
+
+control_psm: T_CONTROL_PSM T_HEXBYTE
+ {
+ hid_device->control_psm = $2;
+ }
+ ;
+
+interrupt_psm: T_INTERRUPT_PSM T_HEXBYTE
+ {
+ hid_device->interrupt_psm = $2;
+ }
+ ;
+
+reconnect_initiate: T_RECONNECT_INITIATE T_TRUE
+ {
+ hid_device->reconnect_initiate = 1;
+ }
+ | T_RECONNECT_INITIATE T_FALSE
+ {
+ hid_device->reconnect_initiate = 0;
+ }
+ ;
+
+battery_power: T_BATTERY_POWER T_TRUE
+ {
+ hid_device->battery_power = 1;
+ }
+ | T_BATTERY_POWER T_FALSE
+ {
+ hid_device->battery_power = 0;
+ }
+ ;
+
+normally_connectable: T_NORMALLY_CONNECTABLE T_TRUE
+ {
+ hid_device->normally_connectable = 1;
+ }
+ | T_NORMALLY_CONNECTABLE T_FALSE
+ {
+ hid_device->normally_connectable = 0;
+ }
+ ;
+
+hid_descriptor: T_HID_DESCRIPTOR
+ {
+ hid_descriptor_size = 0;
+ }
+ '{' hid_descriptor_bytes '}'
+ {
+ if (hid_device->desc != NULL)
+ hid_dispose_report_desc(hid_device->desc);
+
+ hid_device->desc = hid_use_report_desc((unsigned char *) buffer, hid_descriptor_size);
+ if (hid_device->desc == NULL) {
+ SYSLOG(LOGCRIT, "Could not use HID descriptor" EOL);
+ YYABORT;
+ }
+ }
+ ;
+
+hid_descriptor_bytes: hid_descriptor_byte
+ | hid_descriptor_bytes hid_descriptor_byte
+ ;
+
+hid_descriptor_byte: T_HEXBYTE
+ {
+ if (hid_descriptor_size >= (int32_t) sizeof(buffer)) {
+ SYSLOG(LOGCRIT, "HID descriptor is too big" EOL);
+ YYABORT;
+ }
+
+ buffer[hid_descriptor_size ++] = $1;
+ }
+ ;
+
+parser_error: T_ERROR
+ {
+ YYABORT;
+ }
+
+%%
+
+/* Display parser error message */
+void
+yyerror(char const *message)
+{
+ SYSLOG(LOGERR, "%s in line %d" EOL, message, yylineno);
+}
+
+/* Re-read config file */
+int32_t
+read_config_file(void)
+{
+ int32_t e;
+
+ if (config_file == NULL) {
+ SYSLOG(LOGERR, "Unknown config file name!" EOL);
+ return (-1);
+ }
+
+ if ((yyin = fopen(config_file, "r")) == NULL) {
+ SYSLOG(LOGERR, "Could not open config file '%s'. %s (%d)" EOL,
+ config_file, strerror(errno), errno);
+ return (-1);
+ }
+
+ clean_config();
+ if (yyparse() < 0) {
+ SYSLOG(LOGERR, "Could not parse config file '%s'" EOL,
+ config_file);
+ e = -1;
+ } else
+ e = 0;
+
+ fclose(yyin);
+ yyin = NULL;
+
+ return (e);
+}
+
+/* Clean config */
+void
+clean_config(void)
+{
+ while (!LIST_EMPTY(&hid_devices)) {
+ hid_device_p d = LIST_FIRST(&hid_devices);
+
+ LIST_REMOVE(d, next);
+ free_hid_device(d);
+ }
+}
+
+/* Lookup config entry */
+hid_device_p
+get_hid_device(bdaddr_p bdaddr)
+{
+ hid_device_p d;
+
+ LIST_FOREACH(d, &hid_devices, next)
+ if (memcmp(&d->bdaddr, bdaddr, sizeof(bdaddr_t)) == 0)
+ break;
+
+ return (d);
+}
+
+/* Get next config entry */
+hid_device_p
+get_next_hid_device(hid_device_p d)
+{
+ return ((d == NULL)? LIST_FIRST(&hid_devices) : LIST_NEXT(d, next));
+}
+
+/* Print config entry */
+void
+print_hid_device(hid_device_p d, FILE *f)
+{
+ /* XXX FIXME hack! */
+ struct report_desc {
+ unsigned int size;
+ unsigned char data[1];
+ };
+ /* XXX FIXME hack! */
+
+ struct report_desc *desc = (struct report_desc *) d->desc;
+ uint32_t i;
+
+ fprintf(f,
+"device {\n" \
+" bdaddr %s;\n" \
+" control_psm 0x%x;\n" \
+" interrupt_psm 0x%x;\n" \
+" reconnect_initiate %s;\n" \
+" battery_power %s;\n" \
+" normally_connectable %s;\n" \
+" hid_descriptor {",
+ bt_ntoa(&d->bdaddr, NULL),
+ d->control_psm, d->interrupt_psm,
+ d->reconnect_initiate? "true" : "false",
+ d->battery_power? "true" : "false",
+ d->normally_connectable? "true" : "false");
+
+ for (i = 0; i < desc->size; i ++) {
+ if ((i % 8) == 0)
+ fprintf(f, "\n ");
+
+ fprintf(f, "0x%2.2x ", desc->data[i]);
+ }
+
+ fprintf(f,
+"\n" \
+" };\n" \
+"}\n");
+}
+
+/* Check config entry */
+static int32_t
+check_hid_device(hid_device_p d)
+{
+ hid_data_t hd;
+ hid_item_t hi;
+ int32_t page;
+
+ if (get_hid_device(&d->bdaddr) != NULL) {
+ SYSLOG(LOGERR, "Ignoring duplicated entry for bdaddr %s" EOL,
+ bt_ntoa(&d->bdaddr, NULL));
+ return (0);
+ }
+
+ if (d->control_psm == 0) {
+ SYSLOG(LOGERR, "Ignoring entry with invalid control PSM" EOL);
+ return (0);
+ }
+
+ if (d->interrupt_psm == 0) {
+ SYSLOG(LOGERR, "Ignoring entry with invalid interrupt PSM" EOL);
+ return (0);
+ }
+
+ if (d->desc == NULL) {
+ SYSLOG(LOGERR, "Ignoring entry without HID descriptor" EOL);
+ return (0);
+ }
+
+ /* XXX somehow need to make sure descriptor is valid */
+ for (hd = hid_start_parse(d->desc, ~0, -1); hid_get_item(hd, &hi) > 0; ) {
+ switch (hi.kind) {
+ case hid_collection:
+ case hid_endcollection:
+ case hid_output:
+ case hid_feature:
+ break;
+
+ case hid_input:
+ /* Check if the device may send keystrokes */
+ page = HID_PAGE(hi.usage);
+ if (page == HUP_KEYBOARD)
+ d->keyboard = 1;
+ break;
+ }
+ }
+ hid_end_parse(hd);
+
+ return (1);
+}
+
+/* Free config entry */
+static void
+free_hid_device(hid_device_p d)
+{
+ if (d->desc != NULL)
+ hid_dispose_report_desc(d->desc);
+
+ memset(d, 0, sizeof(*d));
+ free(d);
+}
+
+/* Re-read hids file */
+int32_t
+read_hids_file(void)
+{
+ FILE *f;
+ hid_device_t *d;
+ char *line;
+ bdaddr_t bdaddr;
+ int32_t lineno;
+
+ if (hids_file == NULL) {
+ SYSLOG(LOGERR, "Unknown HIDs file name!" EOL);
+ return (-1);
+ }
+
+ if ((f = fopen(hids_file, "r")) == NULL) {
+ if (errno == ENOENT)
+ return (0);
+
+ SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL,
+ hids_file, strerror(errno), errno);
+ return (-1);
+ }
+
+ for (lineno = 1; fgets(buffer, sizeof(buffer), f) != NULL; lineno ++) {
+ if ((line = strtok(buffer, "\r\n\t ")) == NULL)
+ continue; /* ignore empty lines */
+
+ if (!bt_aton(line, &bdaddr)) {
+ SYSLOG(LOGWARNING, "Ignoring unparseable BD_ADDR in " \
+ "%s:%d" EOL, hids_file, lineno);
+ continue;
+ }
+
+ if ((d = get_hid_device(&bdaddr)) != NULL)
+ d->new_device = 0;
+ }
+
+ fclose(f);
+
+ return (0);
+}
+
+/* Write hids file */
+int32_t
+write_hids_file(void)
+{
+ char path[PATH_MAX];
+ FILE *f;
+ hid_device_t *d;
+
+ if (hids_file == NULL) {
+ SYSLOG(LOGERR, "Unknown HIDs file name!" EOL);
+ return (-1);
+ }
+
+ snprintf(path, sizeof(path), "%s.new", hids_file);
+
+ if ((f = fopen(path, "w")) == NULL) {
+ SYSLOG(LOGERR, "Could not open HIDs file '%s'. %s (%d)" EOL,
+ path, strerror(errno), errno);
+ return (-1);
+ }
+
+ LIST_FOREACH(d, &hid_devices, next)
+ if (!d->new_device)
+ fprintf(f, "%s\n", bt_ntoa(&d->bdaddr, NULL));
+
+ fclose(f);
+
+ if (rename(path, hids_file) < 0) {
+ SYSLOG(LOGERR, "Could not rename new HIDs file '%s' to '%s'. " \
+ "%s (%d)" EOL, path, hids_file, strerror(errno), errno);
+ unlink(path);
+ return (-1);
+ }
+
+ return (0);
+}
+
diff --git a/usr.sbin/bluetooth/bthidd/server.c b/usr.sbin/bluetooth/bthidd/server.c
new file mode 100644
index 0000000..d76bd62
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidd/server.c
@@ -0,0 +1,349 @@
+/*
+ * server.c
+ */
+
+/*-
+ * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: server.c,v 1.9 2006/09/07 21:06:53 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <assert.h>
+#include <bluetooth.h>
+#include <dev/vkbd/vkbd_var.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <usbhid.h>
+#include "bthid_config.h"
+#include "bthidd.h"
+#include "kbd.h"
+
+#undef max
+#define max(x, y) (((x) > (y))? (x) : (y))
+
+static int32_t server_accept (bthid_server_p srv, int32_t fd);
+static int32_t server_process(bthid_server_p srv, int32_t fd);
+
+/*
+ * Initialize server
+ */
+
+int32_t
+server_init(bthid_server_p srv)
+{
+ struct sockaddr_l2cap l2addr;
+
+ assert(srv != NULL);
+
+ srv->ctrl = srv->intr = -1;
+ FD_ZERO(&srv->rfdset);
+ FD_ZERO(&srv->wfdset);
+ LIST_INIT(&srv->sessions);
+
+ /* Open /dev/consolectl */
+ srv->cons = open("/dev/consolectl", O_RDWR);
+ if (srv->cons < 0) {
+ syslog(LOG_ERR, "Could not open /dev/consolectl. %s (%d)",
+ strerror(errno), errno);
+ return (-1);
+ }
+
+ /* Create control socket */
+ srv->ctrl = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP);
+ if (srv->ctrl < 0) {
+ syslog(LOG_ERR, "Could not create control L2CAP socket. " \
+ "%s (%d)", strerror(errno), errno);
+ close(srv->cons);
+ return (-1);
+ }
+
+ l2addr.l2cap_len = sizeof(l2addr);
+ l2addr.l2cap_family = AF_BLUETOOTH;
+ memcpy(&l2addr.l2cap_bdaddr, &srv->bdaddr, sizeof(l2addr.l2cap_bdaddr));
+ l2addr.l2cap_psm = htole16(0x11);
+
+ if (bind(srv->ctrl, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) {
+ syslog(LOG_ERR, "Could not bind control L2CAP socket. " \
+ "%s (%d)", strerror(errno), errno);
+ close(srv->ctrl);
+ close(srv->cons);
+ return (-1);
+ }
+
+ if (listen(srv->ctrl, 10) < 0) {
+ syslog(LOG_ERR, "Could not listen on control L2CAP socket. " \
+ "%s (%d)", strerror(errno), errno);
+ close(srv->ctrl);
+ close(srv->cons);
+ return (-1);
+ }
+
+ /* Create intrrupt socket */
+ srv->intr = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP);
+ if (srv->intr < 0) {
+ syslog(LOG_ERR, "Could not create interrupt L2CAP socket. " \
+ "%s (%d)", strerror(errno), errno);
+ close(srv->ctrl);
+ close(srv->cons);
+ return (-1);
+ }
+
+ l2addr.l2cap_psm = htole16(0x13);
+
+ if (bind(srv->intr, (struct sockaddr *) &l2addr, sizeof(l2addr)) < 0) {
+ syslog(LOG_ERR, "Could not bind interrupt L2CAP socket. " \
+ "%s (%d)", strerror(errno), errno);
+ close(srv->intr);
+ close(srv->ctrl);
+ close(srv->cons);
+ return (-1);
+ }
+
+ if (listen(srv->intr, 10) < 0) {
+ syslog(LOG_ERR, "Could not listen on interrupt L2CAP socket. "\
+ "%s (%d)", strerror(errno), errno);
+ close(srv->intr);
+ close(srv->ctrl);
+ close(srv->cons);
+ return (-1);
+ }
+
+ FD_SET(srv->ctrl, &srv->rfdset);
+ FD_SET(srv->intr, &srv->rfdset);
+ srv->maxfd = max(srv->ctrl, srv->intr);
+
+ return (0);
+}
+
+/*
+ * Shutdown server
+ */
+
+void
+server_shutdown(bthid_server_p srv)
+{
+ assert(srv != NULL);
+
+ close(srv->cons);
+ close(srv->ctrl);
+ close(srv->intr);
+
+ while (!LIST_EMPTY(&srv->sessions))
+ session_close(LIST_FIRST(&srv->sessions));
+
+ memset(srv, 0, sizeof(*srv));
+}
+
+/*
+ * Do one server iteration
+ */
+
+int32_t
+server_do(bthid_server_p srv)
+{
+ struct timeval tv;
+ fd_set rfdset, wfdset;
+ int32_t n, fd;
+
+ assert(srv != NULL);
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+
+ /* Copy cached version of the fd sets and call select */
+ memcpy(&rfdset, &srv->rfdset, sizeof(rfdset));
+ memcpy(&wfdset, &srv->wfdset, sizeof(wfdset));
+
+ n = select(srv->maxfd + 1, &rfdset, &wfdset, NULL, &tv);
+ if (n < 0) {
+ if (errno == EINTR)
+ return (0);
+
+ syslog(LOG_ERR, "Could not select(%d, %p, %p). %s (%d)",
+ srv->maxfd + 1, &rfdset, &wfdset, strerror(errno), errno);
+
+ return (-1);
+ }
+
+ /* Process descriptors (if any) */
+ for (fd = 0; fd < srv->maxfd + 1 && n > 0; fd ++) {
+ if (FD_ISSET(fd, &rfdset)) {
+ n --;
+
+ if (fd == srv->ctrl || fd == srv->intr)
+ server_accept(srv, fd);
+ else
+ server_process(srv, fd);
+ } else if (FD_ISSET(fd, &wfdset)) {
+ n --;
+
+ client_connect(srv, fd);
+ }
+ }
+
+ return (0);
+}
+
+/*
+ * Accept new connection
+ */
+
+static int32_t
+server_accept(bthid_server_p srv, int32_t fd)
+{
+ bthid_session_p s;
+ hid_device_p d;
+ struct sockaddr_l2cap l2addr;
+ int32_t new_fd;
+ socklen_t len;
+
+ len = sizeof(l2addr);
+ if ((new_fd = accept(fd, (struct sockaddr *) &l2addr, &len)) < 0) {
+ syslog(LOG_ERR, "Could not accept %s connection. %s (%d)",
+ (fd == srv->ctrl)? "control" : "interrupt",
+ strerror(errno), errno);
+ return (-1);
+ }
+
+ /* Is device configured? */
+ if ((d = get_hid_device(&l2addr.l2cap_bdaddr)) == NULL) {
+ syslog(LOG_ERR, "Rejecting %s connection from %s. " \
+ "Device not configured",
+ (fd == srv->ctrl)? "control" : "interrupt",
+ bt_ntoa(&l2addr.l2cap_bdaddr, NULL));
+ close(new_fd);
+ return (-1);
+ }
+
+ /* Check if we have session for the device */
+ if ((s = session_by_bdaddr(srv, &l2addr.l2cap_bdaddr)) == NULL) {
+ d->new_device = 0; /* reset new device flag */
+ write_hids_file();
+
+ /* Create new inbound session */
+ if ((s = session_open(srv, d)) == NULL) {
+ syslog(LOG_CRIT, "Could not open inbound session "
+ "for %s", bt_ntoa(&l2addr.l2cap_bdaddr, NULL));
+ close(new_fd);
+ return (-1);
+ }
+ }
+
+ /* Update descriptors */
+ if (fd == srv->ctrl) {
+ assert(s->ctrl == -1);
+ s->ctrl = new_fd;
+ s->state = (s->intr == -1)? W4INTR : OPEN;
+ } else {
+ assert(s->intr == -1);
+ s->intr = new_fd;
+ s->state = (s->ctrl == -1)? W4CTRL : OPEN;
+ }
+
+ FD_SET(new_fd, &srv->rfdset);
+ if (new_fd > srv->maxfd)
+ srv->maxfd = new_fd;
+
+ syslog(LOG_NOTICE, "Accepted %s connection from %s",
+ (fd == srv->ctrl)? "control" : "interrupt",
+ bt_ntoa(&l2addr.l2cap_bdaddr, NULL));
+
+ /* Register session's vkbd descriptor (if needed) for read */
+ if (s->state == OPEN && d->keyboard) {
+ assert(s->vkbd != -1);
+
+ FD_SET(s->vkbd, &srv->rfdset);
+ if (s->vkbd > srv->maxfd)
+ srv->maxfd = s->vkbd;
+ }
+
+ return (0);
+}
+
+/*
+ * Process data on the connection
+ */
+
+static int32_t
+server_process(bthid_server_p srv, int32_t fd)
+{
+ bthid_session_p s = session_by_fd(srv, fd);
+ int32_t len, to_read;
+ int32_t (*cb)(bthid_session_p, uint8_t *, int32_t);
+ union {
+ uint8_t b[1024];
+ vkbd_status_t s;
+ } data;
+
+ if (s == NULL)
+ return (0); /* can happen on device disconnect */
+
+
+ if (fd == s->ctrl) {
+ cb = hid_control;
+ to_read = sizeof(data.b);
+ } else if (fd == s->intr) {
+ cb = hid_interrupt;
+ to_read = sizeof(data.b);
+ } else {
+ assert(fd == s->vkbd);
+
+ cb = kbd_status_changed;
+ to_read = sizeof(data.s);
+ }
+
+ do {
+ len = read(fd, &data, to_read);
+ } while (len < 0 && errno == EINTR);
+
+ if (len < 0) {
+ syslog(LOG_ERR, "Could not read data from %s (%s). %s (%d)",
+ bt_ntoa(&s->bdaddr, NULL),
+ (fd == s->ctrl)? "control" : "interrupt",
+ strerror(errno), errno);
+ session_close(s);
+ return (0);
+ }
+
+ if (len == 0) {
+ syslog(LOG_NOTICE, "Remote device %s has closed %s connection",
+ bt_ntoa(&s->bdaddr, NULL),
+ (fd == s->ctrl)? "control" : "interrupt");
+ session_close(s);
+ return (0);
+ }
+
+ (*cb)(s, (uint8_t *) &data, len);
+
+ return (0);
+}
+
diff --git a/usr.sbin/bluetooth/bthidd/session.c b/usr.sbin/bluetooth/bthidd/session.c
new file mode 100644
index 0000000..b9f331b
--- /dev/null
+++ b/usr.sbin/bluetooth/bthidd/session.c
@@ -0,0 +1,184 @@
+/*
+ * session.c
+ */
+
+/*-
+ * Copyright (c) 2006 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: session.c,v 1.3 2006/09/07 21:06:53 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <assert.h>
+#include <bluetooth.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <usbhid.h>
+#include "bthid_config.h"
+#include "bthidd.h"
+#include "kbd.h"
+
+/*
+ * Create new session
+ */
+
+bthid_session_p
+session_open(bthid_server_p srv, hid_device_p const d)
+{
+ bthid_session_p s;
+
+ assert(srv != NULL);
+ assert(d != NULL);
+
+ if ((s = (bthid_session_p) malloc(sizeof(*s))) == NULL)
+ return (NULL);
+
+ s->srv = srv;
+ memcpy(&s->bdaddr, &d->bdaddr, sizeof(s->bdaddr));
+ s->ctrl = -1;
+ s->intr = -1;
+
+ if (d->keyboard) {
+ /* Open /dev/vkbdctl */
+ s->vkbd = open("/dev/vkbdctl", O_RDWR);
+ if (s->vkbd < 0) {
+ syslog(LOG_ERR, "Could not open /dev/vkbdctl " \
+ "for %s. %s (%d)", bt_ntoa(&s->bdaddr, NULL),
+ strerror(errno), errno);
+ free(s);
+ return (NULL);
+ }
+ } else
+ s->vkbd = -1;
+
+ s->state = CLOSED;
+
+ s->keys1 = bit_alloc(kbd_maxkey());
+ if (s->keys1 == NULL) {
+ free(s);
+ return (NULL);
+ }
+
+ s->keys2 = bit_alloc(kbd_maxkey());
+ if (s->keys2 == NULL) {
+ free(s->keys1);
+ free(s);
+ return (NULL);
+ }
+
+ LIST_INSERT_HEAD(&srv->sessions, s, next);
+
+ return (s);
+}
+
+/*
+ * Lookup session by bdaddr
+ */
+
+bthid_session_p
+session_by_bdaddr(bthid_server_p srv, bdaddr_p bdaddr)
+{
+ bthid_session_p s;
+
+ assert(srv != NULL);
+ assert(bdaddr != NULL);
+
+ LIST_FOREACH(s, &srv->sessions, next)
+ if (memcmp(&s->bdaddr, bdaddr, sizeof(s->bdaddr)) == 0)
+ break;
+
+ return (s);
+}
+
+/*
+ * Lookup session by fd
+ */
+
+bthid_session_p
+session_by_fd(bthid_server_p srv, int32_t fd)
+{
+ bthid_session_p s;
+
+ assert(srv != NULL);
+ assert(fd >= 0);
+
+ LIST_FOREACH(s, &srv->sessions, next)
+ if (s->ctrl == fd || s->intr == fd || s->vkbd == fd)
+ break;
+
+ return (s);
+}
+
+/*
+ * Close session
+ */
+
+void
+session_close(bthid_session_p s)
+{
+ assert(s != NULL);
+ assert(s->srv != NULL);
+
+ LIST_REMOVE(s, next);
+
+ if (s->intr != -1) {
+ FD_CLR(s->intr, &s->srv->rfdset);
+ FD_CLR(s->intr, &s->srv->wfdset);
+ close(s->intr);
+
+ if (s->srv->maxfd == s->intr)
+ s->srv->maxfd --;
+ }
+
+ if (s->ctrl != -1) {
+ FD_CLR(s->ctrl, &s->srv->rfdset);
+ FD_CLR(s->ctrl, &s->srv->wfdset);
+ close(s->ctrl);
+
+ if (s->srv->maxfd == s->ctrl)
+ s->srv->maxfd --;
+ }
+
+ if (s->vkbd != -1) {
+ FD_CLR(s->vkbd, &s->srv->rfdset);
+ close(s->vkbd);
+
+ if (s->srv->maxfd == s->vkbd)
+ s->srv->maxfd --;
+ }
+
+ free(s->keys1);
+ free(s->keys2);
+
+ memset(s, 0, sizeof(*s));
+ free(s);
+}
+
diff --git a/usr.sbin/bluetooth/btpand/Makefile b/usr.sbin/bluetooth/btpand/Makefile
new file mode 100644
index 0000000..5e4bb0b
--- /dev/null
+++ b/usr.sbin/bluetooth/btpand/Makefile
@@ -0,0 +1,13 @@
+# $NetBSD: Makefile,v 1.2 2008/08/18 08:25:32 plunky Exp $
+# $FreeBSD$
+
+PROG= btpand
+MAN= btpand.8
+SRCS= btpand.c bnep.c channel.c client.c event.c packet.c server.c sdp.c tap.c
+
+WARNS?= 3
+
+DPADD+= ${LIBBLUETOOTH} ${LIBSDP} ${LIBUTIL}
+LDADD+= -lbluetooth -lsdp -lutil
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bluetooth/btpand/bnep.c b/usr.sbin/bluetooth/btpand/bnep.c
new file mode 100644
index 0000000..200a723
--- /dev/null
+++ b/usr.sbin/bluetooth/btpand/bnep.c
@@ -0,0 +1,755 @@
+/* $NetBSD: bnep.c,v 1.1 2008/08/17 13:20:57 plunky Exp $ */
+
+/*-
+ * Copyright (c) 2008 Iain Hibbert
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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/cdefs.h>
+__RCSID("$NetBSD: bnep.c,v 1.1 2008/08/17 13:20:57 plunky Exp $");
+
+#include <sys/uio.h>
+#include <bluetooth.h>
+#include <sdp.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "btpand.h"
+#include "bnep.h"
+
+static bool bnep_recv_extension(packet_t *);
+static size_t bnep_recv_control(channel_t *, uint8_t *, size_t, bool);
+static size_t bnep_recv_control_command_not_understood(channel_t *, uint8_t *, size_t);
+static size_t bnep_recv_setup_connection_req(channel_t *, uint8_t *, size_t);
+static size_t bnep_recv_setup_connection_rsp(channel_t *, uint8_t *, size_t);
+static size_t bnep_recv_filter_net_type_set(channel_t *, uint8_t *, size_t);
+static size_t bnep_recv_filter_net_type_rsp(channel_t *, uint8_t *, size_t);
+static size_t bnep_recv_filter_multi_addr_set(channel_t *, uint8_t *, size_t);
+static size_t bnep_recv_filter_multi_addr_rsp(channel_t *, uint8_t *, size_t);
+
+static bool bnep_pfilter(channel_t *, packet_t *);
+static bool bnep_mfilter(channel_t *, packet_t *);
+
+static uint8_t NAP_UUID[] = {
+ 0x00, 0x00, 0x11, 0x16,
+ 0x00, 0x00,
+ 0x10, 0x00,
+ 0x80, 0x00,
+ 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb
+};
+
+static uint8_t GN_UUID[] = {
+ 0x00, 0x00, 0x11, 0x17,
+ 0x00, 0x00,
+ 0x10, 0x00,
+ 0x80, 0x00,
+ 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb,
+};
+
+static uint8_t PANU_UUID[] = {
+ 0x00, 0x00, 0x11, 0x15,
+ 0x00, 0x00,
+ 0x10, 0x00,
+ 0x80, 0x00,
+ 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb
+};
+
+/*
+ * receive BNEP packet
+ * return true if packet is to be forwarded
+ */
+bool
+bnep_recv(packet_t *pkt)
+{
+ size_t len;
+ uint8_t type;
+
+ if (pkt->len < 1)
+ return false;
+
+ type = pkt->ptr[0];
+ packet_adj(pkt, 1);
+
+ switch (BNEP_TYPE(type)) {
+ case BNEP_GENERAL_ETHERNET:
+ if (pkt->len < (ETHER_ADDR_LEN * 2) + ETHER_TYPE_LEN) {
+ log_debug("dropped short packet (type 0x%2.2x)", type);
+ return false;
+ }
+
+ pkt->dst = pkt->ptr;
+ packet_adj(pkt, ETHER_ADDR_LEN);
+ pkt->src = pkt->ptr;
+ packet_adj(pkt, ETHER_ADDR_LEN);
+ pkt->type = pkt->ptr;
+ packet_adj(pkt, ETHER_TYPE_LEN);
+ break;
+
+ case BNEP_CONTROL:
+ len = bnep_recv_control(pkt->chan, pkt->ptr, pkt->len, false);
+ if (len == 0)
+ return false;
+
+ packet_adj(pkt, len);
+ break;
+
+ case BNEP_COMPRESSED_ETHERNET:
+ if (pkt->len < ETHER_TYPE_LEN) {
+ log_debug("dropped short packet (type 0x%2.2x)", type);
+ return false;
+ }
+
+ pkt->dst = pkt->chan->laddr;
+ pkt->src = pkt->chan->raddr;
+ pkt->type = pkt->ptr;
+ packet_adj(pkt, ETHER_TYPE_LEN);
+ break;
+
+ case BNEP_COMPRESSED_ETHERNET_SRC_ONLY:
+ if (pkt->len < ETHER_ADDR_LEN + ETHER_TYPE_LEN) {
+ log_debug("dropped short packet (type 0x%2.2x)", type);
+ return false;
+ }
+
+ pkt->dst = pkt->chan->laddr;
+ pkt->src = pkt->ptr;
+ packet_adj(pkt, ETHER_ADDR_LEN);
+ pkt->type = pkt->ptr;
+ packet_adj(pkt, ETHER_TYPE_LEN);
+ break;
+
+ case BNEP_COMPRESSED_ETHERNET_DST_ONLY:
+ if (pkt->len < ETHER_ADDR_LEN + ETHER_TYPE_LEN) {
+ log_debug("dropped short packet (type 0x%2.2x)", type);
+ return false;
+ }
+
+ pkt->dst = pkt->ptr;
+ packet_adj(pkt, ETHER_ADDR_LEN);
+ pkt->src = pkt->chan->raddr;
+ pkt->type = pkt->ptr;
+ packet_adj(pkt, ETHER_TYPE_LEN);
+ break;
+
+ default:
+ /*
+ * Any packet containing a reserved BNEP
+ * header packet type SHALL be dropped.
+ */
+
+ log_debug("dropped packet with reserved type 0x%2.2x", type);
+ return false;
+ }
+
+ if (BNEP_TYPE_EXT(type)
+ && !bnep_recv_extension(pkt))
+ return false; /* invalid extensions */
+
+ if (BNEP_TYPE(type) == BNEP_CONTROL
+ || pkt->chan->state != CHANNEL_OPEN)
+ return false; /* no forwarding */
+
+ return true;
+}
+
+static bool
+bnep_recv_extension(packet_t *pkt)
+{
+ exthdr_t *eh;
+ size_t len, size;
+ uint8_t type;
+
+ do {
+ if (pkt->len < 2)
+ return false;
+
+ type = pkt->ptr[0];
+ size = pkt->ptr[1];
+
+ if (pkt->len < size + 2)
+ return false;
+
+ switch (type) {
+ case BNEP_EXTENSION_CONTROL:
+ len = bnep_recv_control(pkt->chan, pkt->ptr + 2, size, true);
+ if (len != size)
+ log_err("ignored spurious data in exthdr");
+
+ break;
+
+ default:
+ /* Unknown extension headers in data packets */
+ /* SHALL be forwarded irrespective of any */
+ /* network protocol or multicast filter settings */
+ /* and any local filtering policy. */
+
+ eh = malloc(sizeof(exthdr_t));
+ if (eh == NULL) {
+ log_err("exthdr malloc() failed: %m");
+ break;
+ }
+
+ eh->ptr = pkt->ptr;
+ eh->len = size;
+ STAILQ_INSERT_TAIL(&pkt->extlist, eh, next);
+ break;
+ }
+
+ packet_adj(pkt, size + 2);
+ } while (BNEP_TYPE_EXT(type));
+
+ return true;
+}
+
+static size_t
+bnep_recv_control(channel_t *chan, uint8_t *ptr, size_t size, bool isext)
+{
+ uint8_t type;
+ size_t len;
+
+ if (size-- < 1)
+ return 0;
+
+ type = *ptr++;
+
+ switch (type) {
+ case BNEP_CONTROL_COMMAND_NOT_UNDERSTOOD:
+ len = bnep_recv_control_command_not_understood(chan, ptr, size);
+ break;
+
+ case BNEP_SETUP_CONNECTION_REQUEST:
+ if (isext)
+ return 0; /* not allowed in extension headers */
+
+ len = bnep_recv_setup_connection_req(chan, ptr, size);
+ break;
+
+ case BNEP_SETUP_CONNECTION_RESPONSE:
+ if (isext)
+ return 0; /* not allowed in extension headers */
+
+ len = bnep_recv_setup_connection_rsp(chan, ptr, size);
+ break;
+
+ case BNEP_FILTER_NET_TYPE_SET:
+ len = bnep_recv_filter_net_type_set(chan, ptr, size);
+ break;
+
+ case BNEP_FILTER_NET_TYPE_RESPONSE:
+ len = bnep_recv_filter_net_type_rsp(chan, ptr, size);
+ break;
+
+ case BNEP_FILTER_MULTI_ADDR_SET:
+ len = bnep_recv_filter_multi_addr_set(chan, ptr, size);
+ break;
+
+ case BNEP_FILTER_MULTI_ADDR_RESPONSE:
+ len = bnep_recv_filter_multi_addr_rsp(chan, ptr, size);
+ break;
+
+ default:
+ len = 0;
+ break;
+ }
+
+ if (len == 0)
+ bnep_send_control(chan, BNEP_CONTROL_COMMAND_NOT_UNDERSTOOD, type);
+
+ return len;
+}
+
+static size_t
+bnep_recv_control_command_not_understood(channel_t *chan, uint8_t *ptr, size_t size)
+{
+ uint8_t type;
+
+ if (size < 1)
+ return 0;
+
+ type = *ptr++;
+ log_err("received Control Command Not Understood (0x%2.2x)", type);
+
+ /* we didn't send any reserved commands, just cut them off */
+ channel_close(chan);
+
+ return 1;
+}
+
+static size_t
+bnep_recv_setup_connection_req(channel_t *chan, uint8_t *ptr, size_t size)
+{
+ uint8_t off;
+ int src, dst, rsp;
+ size_t len;
+
+ if (size < 1)
+ return 0;
+
+ len = *ptr++;
+ if (size < (len * 2 + 1))
+ return 0;
+
+ if (chan->state != CHANNEL_WAIT_CONNECT_REQ
+ && chan->state != CHANNEL_OPEN) {
+ log_debug("ignored");
+ return (len * 2 + 1);
+ }
+
+ if (len == 2)
+ off = 2;
+ else if (len == 4)
+ off = 0;
+ else if (len == 16)
+ off = 0;
+ else {
+ rsp = BNEP_SETUP_INVALID_UUID_SIZE;
+ goto done;
+ }
+
+ if (memcmp(ptr, NAP_UUID + off, len) == 0)
+ dst = SDP_SERVICE_CLASS_NAP;
+ else if (memcmp(ptr, GN_UUID + off, len) == 0)
+ dst = SDP_SERVICE_CLASS_GN;
+ else if (memcmp(ptr, PANU_UUID + off, len) == 0)
+ dst = SDP_SERVICE_CLASS_PANU;
+ else
+ dst = 0;
+
+ if (dst != service_class) {
+ rsp = BNEP_SETUP_INVALID_DST_UUID;
+ goto done;
+ }
+
+ ptr += len;
+
+ if (memcmp(ptr, NAP_UUID + off, len) == 0)
+ src = SDP_SERVICE_CLASS_NAP;
+ else if (memcmp(ptr, GN_UUID + off, len) == 0)
+ src = SDP_SERVICE_CLASS_GN;
+ else if (memcmp(ptr, PANU_UUID + off, len) == 0)
+ src = SDP_SERVICE_CLASS_PANU;
+ else
+ src = 0;
+
+ if ((dst != SDP_SERVICE_CLASS_PANU && src != SDP_SERVICE_CLASS_PANU)
+ || src == 0) {
+ rsp = BNEP_SETUP_INVALID_SRC_UUID;
+ goto done;
+ }
+
+ rsp = BNEP_SETUP_SUCCESS;
+ chan->state = CHANNEL_OPEN;
+ channel_timeout(chan, 0);
+
+done:
+ log_debug("addr %s response 0x%2.2x",
+ ether_ntoa((struct ether_addr *)chan->raddr), rsp);
+
+ bnep_send_control(chan, BNEP_SETUP_CONNECTION_RESPONSE, rsp);
+ return (len * 2 + 1);
+}
+
+static size_t
+bnep_recv_setup_connection_rsp(channel_t *chan, uint8_t *ptr, size_t size)
+{
+ int rsp;
+
+ if (size < 2)
+ return 0;
+
+ rsp = be16dec(ptr);
+
+ if (chan->state != CHANNEL_WAIT_CONNECT_RSP) {
+ log_debug("ignored");
+ return 2;
+ }
+
+ log_debug("addr %s response 0x%2.2x",
+ ether_ntoa((struct ether_addr *)chan->raddr), rsp);
+
+ if (rsp == BNEP_SETUP_SUCCESS) {
+ chan->state = CHANNEL_OPEN;
+ channel_timeout(chan, 0);
+ } else {
+ channel_close(chan);
+ }
+
+ return 2;
+}
+
+static size_t
+bnep_recv_filter_net_type_set(channel_t *chan, uint8_t *ptr, size_t size)
+{
+ pfilter_t *pf;
+ int i, nf, rsp;
+ size_t len;
+
+ if (size < 2)
+ return 0;
+
+ len = be16dec(ptr);
+ ptr += 2;
+
+ if (size < (len + 2))
+ return 0;
+
+ if (chan->state != CHANNEL_OPEN) {
+ log_debug("ignored");
+ return (len + 2);
+ }
+
+ nf = len / 4;
+ pf = malloc(nf * sizeof(pfilter_t));
+ if (pf == NULL) {
+ rsp = BNEP_FILTER_TOO_MANY_FILTERS;
+ goto done;
+ }
+
+ log_debug("nf = %d", nf);
+
+ for (i = 0; i < nf; i++) {
+ pf[i].start = be16dec(ptr);
+ ptr += 2;
+ pf[i].end = be16dec(ptr);
+ ptr += 2;
+
+ if (pf[i].start > pf[i].end) {
+ free(pf);
+ rsp = BNEP_FILTER_INVALID_RANGE;
+ goto done;
+ }
+
+ log_debug("pf[%d] = %#4.4x, %#4.4x", i, pf[i].start, pf[i].end);
+ }
+
+ if (chan->pfilter)
+ free(chan->pfilter);
+
+ chan->pfilter = pf;
+ chan->npfilter = nf;
+
+ rsp = BNEP_FILTER_SUCCESS;
+
+done:
+ log_debug("addr %s response 0x%2.2x",
+ ether_ntoa((struct ether_addr *)chan->raddr), rsp);
+
+ bnep_send_control(chan, BNEP_FILTER_NET_TYPE_RESPONSE, rsp);
+ return (len + 2);
+}
+
+static size_t
+bnep_recv_filter_net_type_rsp(channel_t *chan, uint8_t *ptr, size_t size)
+{
+ int rsp;
+
+ if (size < 2)
+ return 0;
+
+ if (chan->state != CHANNEL_OPEN) {
+ log_debug("ignored");
+ return 2;
+ }
+
+ rsp = be16dec(ptr);
+
+ log_debug("addr %s response 0x%2.2x",
+ ether_ntoa((struct ether_addr *)chan->raddr), rsp);
+
+ /* we did not send any filter_net_type_set message */
+ return 2;
+}
+
+static size_t
+bnep_recv_filter_multi_addr_set(channel_t *chan, uint8_t *ptr, size_t size)
+{
+ mfilter_t *mf;
+ int i, nf, rsp;
+ size_t len;
+
+ if (size < 2)
+ return 0;
+
+ len = be16dec(ptr);
+ ptr += 2;
+
+ if (size < (len + 2))
+ return 0;
+
+ if (chan->state != CHANNEL_OPEN) {
+ log_debug("ignored");
+ return (len + 2);
+ }
+
+ nf = len / (ETHER_ADDR_LEN * 2);
+ mf = malloc(nf * sizeof(mfilter_t));
+ if (mf == NULL) {
+ rsp = BNEP_FILTER_TOO_MANY_FILTERS;
+ goto done;
+ }
+
+ log_debug("nf = %d", nf);
+
+ for (i = 0; i < nf; i++) {
+ memcpy(mf[i].start, ptr, ETHER_ADDR_LEN);
+ ptr += ETHER_ADDR_LEN;
+
+ memcpy(mf[i].end, ptr, ETHER_ADDR_LEN);
+ ptr += ETHER_ADDR_LEN;
+
+ if (memcmp(mf[i].start, mf[i].end, ETHER_ADDR_LEN) > 0) {
+ free(mf);
+ rsp = BNEP_FILTER_INVALID_RANGE;
+ goto done;
+ }
+
+ log_debug("pf[%d] = "
+ "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x, "
+ "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x", i,
+ mf[i].start[0], mf[i].start[1], mf[i].start[2],
+ mf[i].start[3], mf[i].start[4], mf[i].start[5],
+ mf[i].end[0], mf[i].end[1], mf[i].end[2],
+ mf[i].end[3], mf[i].end[4], mf[i].end[5]);
+ }
+
+ if (chan->mfilter)
+ free(chan->mfilter);
+
+ chan->mfilter = mf;
+ chan->nmfilter = nf;
+
+ rsp = BNEP_FILTER_SUCCESS;
+
+done:
+ log_debug("addr %s response 0x%2.2x",
+ ether_ntoa((struct ether_addr *)chan->raddr), rsp);
+
+ bnep_send_control(chan, BNEP_FILTER_MULTI_ADDR_RESPONSE, rsp);
+ return (len + 2);
+}
+
+static size_t
+bnep_recv_filter_multi_addr_rsp(channel_t *chan, uint8_t *ptr, size_t size)
+{
+ int rsp;
+
+ if (size < 2)
+ return false;
+
+ if (chan->state != CHANNEL_OPEN) {
+ log_debug("ignored");
+ return 2;
+ }
+
+ rsp = be16dec(ptr);
+ log_debug("addr %s response 0x%2.2x",
+ ether_ntoa((struct ether_addr *)chan->raddr), rsp);
+
+ /* we did not send any filter_multi_addr_set message */
+ return 2;
+}
+
+void
+bnep_send_control(channel_t *chan, uint8_t type, ...)
+{
+ packet_t *pkt;
+ uint8_t *p;
+ va_list ap;
+
+ assert(chan->state != CHANNEL_CLOSED);
+
+ pkt = packet_alloc(chan);
+ if (pkt == NULL)
+ return;
+
+ p = pkt->ptr;
+ va_start(ap, type);
+
+ *p++ = BNEP_CONTROL;
+ *p++ = type;
+
+ switch(type) {
+ case BNEP_CONTROL_COMMAND_NOT_UNDERSTOOD:
+ *p++ = va_arg(ap, int);
+ break;
+
+ case BNEP_SETUP_CONNECTION_REQUEST:
+ *p++ = va_arg(ap, int);
+ be16enc(p, va_arg(ap, int));
+ p += 2;
+ be16enc(p, va_arg(ap, int));
+ p += 2;
+ break;
+
+ case BNEP_SETUP_CONNECTION_RESPONSE:
+ case BNEP_FILTER_NET_TYPE_RESPONSE:
+ case BNEP_FILTER_MULTI_ADDR_RESPONSE:
+ be16enc(p, va_arg(ap, int));
+ p += 2;
+ break;
+
+ case BNEP_FILTER_NET_TYPE_SET: /* TODO */
+ case BNEP_FILTER_MULTI_ADDR_SET: /* TODO */
+ default:
+ log_err("Can't send control type 0x%2.2x", type);
+ break;
+ }
+
+ va_end(ap);
+ pkt->len = p - pkt->ptr;
+
+ channel_put(chan, pkt);
+ packet_free(pkt);
+}
+
+/*
+ * BNEP send packet routine
+ * return true if packet can be removed from queue
+ */
+bool
+bnep_send(channel_t *chan, packet_t *pkt)
+{
+ struct iovec iov[2];
+ uint8_t *p, *type, *proto;
+ exthdr_t *eh;
+ bool src, dst;
+ size_t nw;
+
+ if (pkt->type == NULL) {
+ iov[0].iov_base = pkt->ptr;
+ iov[0].iov_len = pkt->len;
+ iov[1].iov_base = NULL;
+ iov[1].iov_len = 0;
+ } else {
+ p = chan->sendbuf;
+
+ dst = (memcmp(pkt->dst, chan->raddr, ETHER_ADDR_LEN) != 0);
+ src = (memcmp(pkt->src, chan->laddr, ETHER_ADDR_LEN) != 0);
+
+ type = p;
+ p += 1;
+
+ if (dst && src)
+ *type = BNEP_GENERAL_ETHERNET;
+ else if (dst && !src)
+ *type = BNEP_COMPRESSED_ETHERNET_DST_ONLY;
+ else if (!dst && src)
+ *type = BNEP_COMPRESSED_ETHERNET_SRC_ONLY;
+ else /* (!dst && !src) */
+ *type = BNEP_COMPRESSED_ETHERNET;
+
+ if (dst) {
+ memcpy(p, pkt->dst, ETHER_ADDR_LEN);
+ p += ETHER_ADDR_LEN;
+ }
+
+ if (src) {
+ memcpy(p, pkt->src, ETHER_ADDR_LEN);
+ p += ETHER_ADDR_LEN;
+ }
+
+ proto = p;
+ memcpy(p, pkt->type, ETHER_TYPE_LEN);
+ p += ETHER_TYPE_LEN;
+
+ STAILQ_FOREACH(eh, &pkt->extlist, next) {
+ if (p + eh->len > chan->sendbuf + chan->mtu)
+ break;
+
+ *type |= BNEP_EXT;
+ type = p;
+
+ memcpy(p, eh->ptr, eh->len);
+ p += eh->len;
+ }
+
+ *type &= ~BNEP_EXT;
+
+ iov[0].iov_base = chan->sendbuf;
+ iov[0].iov_len = (p - chan->sendbuf);
+
+ if ((chan->npfilter == 0 || bnep_pfilter(chan, pkt))
+ && (chan->nmfilter == 0 || bnep_mfilter(chan, pkt))) {
+ iov[1].iov_base = pkt->ptr;
+ iov[1].iov_len = pkt->len;
+ } else if (be16dec(proto) == ETHERTYPE_VLAN
+ && pkt->len >= ETHER_VLAN_ENCAP_LEN) {
+ iov[1].iov_base = pkt->ptr;
+ iov[1].iov_len = ETHER_VLAN_ENCAP_LEN;
+ } else {
+ iov[1].iov_base = NULL;
+ iov[1].iov_len = 0;
+ memset(proto, 0, ETHER_TYPE_LEN);
+ }
+ }
+
+ if (iov[0].iov_len + iov[1].iov_len > chan->mtu) {
+ log_err("packet exceeded MTU (dropped)");
+ return false;
+ }
+
+ nw = writev(chan->fd, iov, __arraycount(iov));
+ return (nw > 0);
+}
+
+static bool
+bnep_pfilter(channel_t *chan, packet_t *pkt)
+{
+ int proto, i;
+
+ proto = be16dec(pkt->type);
+ if (proto == ETHERTYPE_VLAN) { /* IEEE 802.1Q tag header */
+ if (pkt->len < 4)
+ return false;
+
+ proto = be16dec(pkt->ptr + 2);
+ }
+
+ for (i = 0; i < chan->npfilter; i++) {
+ if (chan->pfilter[i].start <= proto
+ && chan->pfilter[i].end >=proto)
+ return true;
+ }
+
+ return false;
+}
+
+static bool
+bnep_mfilter(channel_t *chan, packet_t *pkt)
+{
+ int i;
+
+ if (!ETHER_IS_MULTICAST(pkt->dst))
+ return true;
+
+ for (i = 0; i < chan->nmfilter; i++) {
+ if (memcmp(pkt->dst, chan->mfilter[i].start, ETHER_ADDR_LEN) >= 0
+ && memcmp(pkt->dst, chan->mfilter[i].end, ETHER_ADDR_LEN) <= 0)
+ return true;
+ }
+
+ return false;
+}
diff --git a/usr.sbin/bluetooth/btpand/bnep.h b/usr.sbin/bluetooth/btpand/bnep.h
new file mode 100644
index 0000000..2fc6a77
--- /dev/null
+++ b/usr.sbin/bluetooth/btpand/bnep.h
@@ -0,0 +1,72 @@
+/* $NetBSD: bnep.h,v 1.1 2008/08/17 13:20:57 plunky Exp $ */
+
+/*-
+ * Copyright (c) 2008 Iain Hibbert
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$ */
+
+/*
+ * Constants defined in the Bluetooth Network Encapsulation
+ * Protocol (BNEP) specification v1.0
+ */
+
+#define BNEP_MTU_MIN 1691
+
+#define BNEP_EXT 0x80
+#define BNEP_TYPE(x) ((x) & 0x7f)
+#define BNEP_TYPE_EXT(x) (((x) & BNEP_EXT) == BNEP_EXT)
+
+/* BNEP packet types */
+#define BNEP_GENERAL_ETHERNET 0x00
+#define BNEP_CONTROL 0x01
+#define BNEP_COMPRESSED_ETHERNET 0x02
+#define BNEP_COMPRESSED_ETHERNET_SRC_ONLY 0x03
+#define BNEP_COMPRESSED_ETHERNET_DST_ONLY 0x04
+
+/* BNEP extension header types */
+#define BNEP_EXTENSION_CONTROL 0x00
+
+/* BNEP control types */
+#define BNEP_CONTROL_COMMAND_NOT_UNDERSTOOD 0x00
+#define BNEP_SETUP_CONNECTION_REQUEST 0x01
+#define BNEP_SETUP_CONNECTION_RESPONSE 0x02
+#define BNEP_FILTER_NET_TYPE_SET 0x03
+#define BNEP_FILTER_NET_TYPE_RESPONSE 0x04
+#define BNEP_FILTER_MULTI_ADDR_SET 0x05
+#define BNEP_FILTER_MULTI_ADDR_RESPONSE 0x06
+
+/* BNEP setup response codes */
+#define BNEP_SETUP_SUCCESS 0x0000
+#define BNEP_SETUP_INVALID_SRC_UUID 0x0001
+#define BNEP_SETUP_INVALID_DST_UUID 0x0002
+#define BNEP_SETUP_INVALID_UUID_SIZE 0x0003
+#define BNEP_SETUP_NOT_ALLOWED 0x0004
+
+/* BNEP filter return codes */
+#define BNEP_FILTER_SUCCESS 0x0000
+#define BNEP_FILTER_UNSUPPORTED_REQUEST 0x0001
+#define BNEP_FILTER_INVALID_RANGE 0x0002
+#define BNEP_FILTER_TOO_MANY_FILTERS 0x0003
+#define BNEP_FILTER_SECURITY_FAILURE 0x0004
diff --git a/usr.sbin/bluetooth/btpand/btpand.8 b/usr.sbin/bluetooth/btpand/btpand.8
new file mode 100644
index 0000000..ece16c2
--- /dev/null
+++ b/usr.sbin/bluetooth/btpand/btpand.8
@@ -0,0 +1,241 @@
+.\" $NetBSD: btpand.8,v 1.3 2008/08/17 14:43:07 plunky Exp $
+.\" $FreeBSD$
+.\"
+.\" Copyright (c) 2008 Iain Hibbert
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd August 17, 2008
+.Dt BTPAND 8
+.Os
+.Sh NAME
+.Nm btpand
+.Nd Bluetooth PAN daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl i Ar ifname
+.Op Fl m Ar mode
+.Fl a Ar addr
+.Fl d Ar device
+.Brq Fl s Ar service | Fl S Ar service Op Fl p Ar psm
+.Nm
+.Op Fl c Ar path
+.Op Fl i Ar ifname
+.Op Fl l Ar limit
+.Op Fl m Ar mode
+.Op Fl p Ar psm
+.Fl d Ar device
+.Brq Fl s Ar service | Fl S Ar service
+.Sh DESCRIPTION
+The
+.Nm
+daemon handles Bluetooth Personal Area Networking services
+in the system.
+It can operate in client mode as a Personal Area Networking User
+.Pq PANU
+or in server mode as Network Access Point
+.Pq NAP ,
+Group ad-hoc Network
+.Pq GN
+or PANU host.
+.Nm
+connects to the system via a
+.Xr tap 4
+virtual Ethernet device and forwards Ethernet packets to
+remote Bluetooth devices using the Bluetooth Network Encapsulation
+Protocol
+.Pq BNEP .
+.Pp
+The PANU client is the device that uses either the NAP or GN
+service, or can talk directly to a PANU host in a crossover
+cable fashion.
+.Pp
+A GN host forwards Ethernet packets to each of the connected PAN
+users as needed but does not provide access to any additional networks.
+.Pp
+The NAP service provides some of the features of an Ethernet bridge,
+with the NAP host forwarding Ethernet packets between each of the
+connected PAN users, and a different network
+media.
+.Pp
+Note, the only differences between NAP and GN services as implemented by
+.Nm
+are in the SDP service record.
+The bridging of packets by the NAP must be configured separately.
+.Pp
+The options are as follows:
+.Bl -tag -width ".Fl a Ar address"
+.It Fl a Ar address
+In client mode, address of remote server.
+May be given as BDADDR or name, in which case
+.Nm
+will attempt to resolve the address via the
+.Xr bt_gethostbyname 3
+call.
+.It Fl c Ar path
+In server mode, specify
+.Ar path
+to the
+.Xr sdpd 8
+control socket.
+The default path is
+.Pa /var/run/sdp .
+.It Fl d Ar device
+Restrict connections to the local
+.Ar device .
+May be given as BDADDR or name, in which case
+.Nm
+will attempt to resolve the address via the
+.Xr bt_devaddr 3
+call.
+.Nm
+will set the
+.Xr tap 4
+interface physical address to the BDADDR
+of the Bluetooth radio.
+.It Fl i Ar ifname
+.Nm
+uses the
+.Xr tap 4
+driver to create a new network interface for use.
+Use this option to select a specific
+.Xr tap 4
+device interface which must already be created.
+.It Fl l Ar limit
+In server mode, limit the number of simultaneous connections.
+The default limit is 7 for NAP and GN servers,
+and 1 for a PANU server.
+.It Fl m Ar mode
+Set L2CAP connection link mode.
+Supported modes are:
+.Pp
+.Bl -tag -compact
+.It auth
+require devices to be paired.
+.It encrypt
+auth, plus enable encryption.
+.It secure
+encryption, plus change of link key.
+.El
+.Pp
+NOT YET SUPPORTED.
+Use global device settings to set authentication and encryption.
+.It Fl p Ar psm
+Use an alternative L2CAP Protocol/Service Multiplexer
+.Pq PSM
+for server mode or client mode
+.Pq when not using Service Discovery .
+The default PSM for BNEP is 15
+.Pq 0x000f .
+.It Fl s Ar service
+Name of
+.Ar service
+to provide or connect to, the following services are recognised:
+.Pp
+.Bl -tag -compact
+.It GN
+Group ad-hoc Network.
+.It NAP
+Network Access Point.
+.It PANU
+Personal Area Networking User.
+.El
+.Pp
+.It Fl S Ar service
+As per
+.Fl s
+except that
+.Nm
+will not use SDP services for connection setup.
+.El
+.Pp
+When providing networking services, the Bluetooth PAN profile says that the
+.Sq Class of Device
+property of the bluetooth controller SHALL include Networking capability
+.Pq set bit 0x020000 .
+See
+.Xr hccontrol 8
+for details.
+.Pp
+After
+.Nm
+has set up the client or server connection and opened the
+.Xr tap 4
+interface, it will create a pid file and detach.
+.Sh EXIT STATUS
+.Ex -std
+.Sh FILES
+.Bl -tag -compact
+.It Pa /dev/tap
+.It Pa /etc/bluetooth/hosts
+.It Pa /var/run/sdp
+.It Pa /var/run/tap Ns Em N Ns No .pid
+.El
+.Sh EXAMPLES
+.Dl ifconfig tap1 create
+.Dl btpand -a host -d mydevice -s NAP -i tap1
+.Dl dhclient tap1
+.Pp
+Will create a connection to the NAP on
+.Ar host ,
+and link that to the
+.Ar tap1
+interface.
+.Pp
+.Dl btpand -d mydevice -s GN
+.Pp
+Will create a Group Network and register the GN service with the local
+SDP server.
+.Sh SEE ALSO
+.Xr bluetooth 3 ,
+.Xr tap 4 ,
+.Xr bridge 4 ,
+.Xr hccontrol 8 ,
+.Xr dhclient 8 ,
+.Xr ifconfig 8 ,
+.Xr sdpd 8
+.Pp
+The
+.Qq Personal Area Networking Profile
+and
+.Qq Bluetooth Network Encapsulation Protocol
+specifications are available at
+.Dl http://www.bluetooth.com/
+.Sh AUTHORS
+.An Iain Hibbert
+.Sh BUGS
+There is no way to supply alternative values for the SDP record.
+.Pp
+There is no way to set net type or multicast address filters.
+.Pp
+.Nm
+does not do any address routing except to directly connected
+unicast addresses.
+All other packets are multicast.
+.Pp
+As
+.Nm
+uses the BDADDR of the Bluetooth radio as the physical address
+of the tap, only one instance can be run per radio.
+.Pp
+.Nm
+can only provide a single service.
diff --git a/usr.sbin/bluetooth/btpand/btpand.c b/usr.sbin/bluetooth/btpand/btpand.c
new file mode 100644
index 0000000..99a3bc7
--- /dev/null
+++ b/usr.sbin/bluetooth/btpand/btpand.c
@@ -0,0 +1,293 @@
+/* $NetBSD: btpand.c,v 1.1 2008/08/17 13:20:57 plunky Exp $ */
+
+/*-
+ * Copyright (c) 2008 Iain Hibbert
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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/cdefs.h>
+__COPYRIGHT("@(#) Copyright (c) 2008 Iain Hibbert. All rights reserved.");
+__RCSID("$NetBSD: btpand.c,v 1.1 2008/08/17 13:20:57 plunky Exp $");
+
+#include <sys/wait.h>
+
+#include <bluetooth.h>
+#include <err.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <sdp.h>
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "btpand.h"
+
+/* global variables */
+const char * control_path; /* -c <path> */
+const char * interface_name; /* -i <ifname> */
+const char * service_name; /* -s <service> */
+uint16_t service_class;
+
+bdaddr_t local_bdaddr; /* -d <addr> */
+bdaddr_t remote_bdaddr; /* -a <addr> */
+uint16_t l2cap_psm; /* -p <psm> */
+int l2cap_mode; /* -m <mode> */
+
+int server_limit; /* -n <limit> */
+
+static const struct {
+ const char * name;
+ uint16_t class;
+ const char * desc;
+} services[] = {
+ { "PANU", SDP_SERVICE_CLASS_PANU, "Personal Area Networking User" },
+ { "NAP", SDP_SERVICE_CLASS_NAP, "Network Acess Point" },
+ { "GN", SDP_SERVICE_CLASS_GN, "Group Network" },
+};
+
+static void main_exit(int);
+static void main_detach(void);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ unsigned long ul;
+ char * ep;
+ int ch, status;
+
+ while ((ch = getopt(argc, argv, "a:c:d:i:l:m:p:S:s:")) != -1) {
+ switch (ch) {
+ case 'a': /* remote address */
+ if (!bt_aton(optarg, &remote_bdaddr)) {
+ struct hostent *he;
+
+ if ((he = bt_gethostbyname(optarg)) == NULL)
+ errx(EXIT_FAILURE, "%s: %s",
+ optarg, hstrerror(h_errno));
+
+ bdaddr_copy(&remote_bdaddr,
+ (bdaddr_t *)he->h_addr);
+ }
+
+ break;
+
+ case 'c': /* control socket path */
+ control_path = optarg;
+ break;
+
+ case 'd': /* local address */
+ if (!bt_aton(optarg, &local_bdaddr)) {
+ struct hostent *he;
+
+ if ((he = bt_gethostbyname(optarg)) == NULL)
+ errx(EXIT_FAILURE, "%s: %s",
+ optarg, hstrerror(h_errno));
+
+ bdaddr_copy(&local_bdaddr,
+ (bdaddr_t *)he->h_addr);
+ }
+ break;
+
+ case 'i': /* tap interface name */
+ if (strchr(optarg, '/') == NULL) {
+ asprintf(&ep, "/dev/%s", optarg);
+ interface_name = ep;
+ } else
+ interface_name = optarg;
+ break;
+
+ case 'l': /* limit server sessions */
+ ul = strtoul(optarg, &ep, 10);
+ if (*optarg == '\0' || *ep != '\0' || ul == 0)
+ errx(EXIT_FAILURE, "%s: invalid session limit",
+ optarg);
+
+ server_limit = ul;
+ break;
+
+ case 'm': /* link mode */
+ warnx("Setting link mode is not yet supported");
+ break;
+
+ case 'p': /* protocol/service multiplexer */
+ ul = strtoul(optarg, &ep, 0);
+ if (*optarg == '\0' || *ep != '\0'
+ || ul > 0xffff || L2CAP_PSM_INVALID(ul))
+ errx(EXIT_FAILURE, "%s: invalid PSM", optarg);
+
+ l2cap_psm = ul;
+ break;
+
+ case 's': /* service */
+ case 'S': /* service (no SDP) */
+ for (ul = 0; strcasecmp(optarg, services[ul].name); ul++) {
+ if (ul == __arraycount(services))
+ errx(EXIT_FAILURE, "%s: unknown service", optarg);
+ }
+
+ if (ch == 's')
+ service_name = services[ul].name;
+
+ service_class = services[ul].class;
+ break;
+
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* validate options */
+ if (bdaddr_any(&local_bdaddr) || service_class == 0)
+ usage();
+
+ if (!bdaddr_any(&remote_bdaddr) && (server_limit != 0 ||
+ control_path != 0 || (service_name != NULL && l2cap_psm != 0)))
+ usage();
+
+ /* default options */
+ if (interface_name == NULL)
+ interface_name = "/dev/tap";
+
+ if (l2cap_psm == 0)
+ l2cap_psm = L2CAP_PSM_BNEP;
+
+ if (bdaddr_any(&remote_bdaddr) && server_limit == 0) {
+ if (service_class == SDP_SERVICE_CLASS_PANU)
+ server_limit = 1;
+ else
+ server_limit = 7;
+ }
+
+#ifdef L2CAP_LM_MASTER
+ if (server_limit > 1 && service_class != SDP_SERVICE_CLASS_PANU)
+ l2cap_mode |= L2CAP_LM_MASTER;
+#endif
+
+ /*
+ * fork() now so that the setup can be done in the child process
+ * (as kqueue is not inherited) but block in the parent until the
+ * setup is finished so we can return an error if necessary.
+ */
+ switch(fork()) {
+ case -1: /* bad */
+ err(EXIT_FAILURE, "fork() failed");
+
+ case 0: /* child */
+ signal(SIGPIPE, SIG_IGN);
+
+ openlog(getprogname(), LOG_NDELAY | LOG_PERROR | LOG_PID, LOG_DAEMON);
+
+ channel_init();
+ server_init();
+ event_init();
+ client_init();
+ tap_init();
+
+ main_detach();
+
+ event_dispatch();
+ break;
+
+ default: /* parent */
+ signal(SIGUSR1, main_exit);
+ wait(&status);
+
+ if (WIFEXITED(status))
+ exit(WEXITSTATUS(status));
+
+ break;
+ }
+
+ err(EXIT_FAILURE, "exiting");
+}
+
+static void
+main_exit(int s)
+{
+
+ /* child is all grown up */
+ _exit(EXIT_SUCCESS);
+}
+
+static void
+main_detach(void)
+{
+ int fd;
+
+ if (kill(getppid(), SIGUSR1) == -1)
+ log_err("Could not signal main process: %m");
+
+ if (setsid() == -1)
+ log_err("setsid() failed");
+
+ fd = open(_PATH_DEVNULL, O_RDWR, 0);
+ if (fd == -1) {
+ log_err("Could not open %s", _PATH_DEVNULL);
+ } else {
+ (void)dup2(fd, STDIN_FILENO);
+ (void)dup2(fd, STDOUT_FILENO);
+ (void)dup2(fd, STDERR_FILENO);
+ close(fd);
+ }
+}
+
+static void
+usage(void)
+{
+ const char *p = getprogname();
+ int n = strlen(p);
+
+ fprintf(stderr,
+ "usage: %s [-i ifname] [-m mode] -a address -d device\n"
+ " %*s {-s service | -S service [-p psm]}\n"
+ " %s [-c path] [-i ifname] [-l limit] [-m mode] [-p psm] -d device\n"
+ " %*s {-s service | -S service}\n"
+ "\n"
+ "Where:\n"
+ "\t-a address remote bluetooth device\n"
+ "\t-c path SDP server socket\n"
+ "\t-d device local bluetooth device\n"
+ "\t-i ifname tap interface\n"
+ "\t-l limit limit server sessions\n"
+ "\t-m mode L2CAP link mode (NOT YET SUPPORTED)\n"
+ "\t-p psm L2CAP PSM\n"
+ "\t-S service service name (no SDP)\n"
+ "\t-s service service name\n"
+ "\n"
+ "Known services:\n"
+ "", p, n, "", p, n, "");
+
+ for (n = 0; n < __arraycount(services); n++)
+ fprintf(stderr, "\t%s\t%s\n", services[n].name, services[n].desc);
+
+ exit(EXIT_FAILURE);
+}
diff --git a/usr.sbin/bluetooth/btpand/btpand.h b/usr.sbin/bluetooth/btpand/btpand.h
new file mode 100644
index 0000000..c5f7204
--- /dev/null
+++ b/usr.sbin/bluetooth/btpand/btpand.h
@@ -0,0 +1,212 @@
+/* $NetBSD: btpand.h,v 1.1 2008/08/17 13:20:57 plunky Exp $ */
+
+/*-
+ * Copyright (c) 2008 Iain Hibbert
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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/queue.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/ethernet.h>
+
+#include <assert.h>
+#include <bluetooth.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "event.h"
+
+#ifndef __arraycount
+#define __arraycount(__x) (int)(sizeof((__x)) / sizeof((__x)[0]))
+#endif
+
+#ifndef L2CAP_PSM_INVALID
+#define L2CAP_PSM_INVALID(psm) (((psm) & 0x0101) != 0x0001)
+#endif
+
+#ifndef L2CAP_PSM_BNEP
+#define L2CAP_PSM_BNEP 15
+#endif
+
+typedef struct channel channel_t;
+typedef struct pfilter pfilter_t;
+typedef struct mfilter mfilter_t;
+typedef struct packet packet_t;
+typedef struct pkthdr pkthdr_t;
+typedef struct pktlist pktlist_t;
+typedef struct exthdr exthdr_t;
+typedef struct extlist extlist_t;
+
+LIST_HEAD(chlist, channel);
+STAILQ_HEAD(extlist, exthdr);
+STAILQ_HEAD(pktlist, pkthdr);
+
+enum channel_state {
+ CHANNEL_CLOSED,
+ CHANNEL_WAIT_CONNECT_REQ,
+ CHANNEL_WAIT_CONNECT_RSP,
+ CHANNEL_OPEN,
+};
+
+#define CHANNEL_MAXQLEN 128
+
+/* BNEP or tap channel */
+struct channel {
+ enum channel_state state;
+ bool oactive;
+
+ uint8_t laddr[ETHER_ADDR_LEN];
+ uint8_t raddr[ETHER_ADDR_LEN];
+ size_t mru;
+ size_t mtu;
+
+ int npfilter;
+ pfilter_t * pfilter;
+
+ int nmfilter;
+ mfilter_t * mfilter;
+
+ pktlist_t pktlist;
+ int qlen;
+
+ int fd;
+ struct event rd_ev;
+ struct event wr_ev;
+ uint8_t * sendbuf;
+
+ bool (*send)(channel_t *, packet_t *);
+ bool (*recv)(packet_t *);
+
+ int tick;
+
+ struct pidfh *pfh;
+
+ int refcnt;
+ LIST_ENTRY(channel) next;
+};
+
+/* network protocol type filter */
+struct pfilter {
+ uint16_t start;
+ uint16_t end;
+};
+
+/* multicast address filter */
+struct mfilter {
+ uint8_t start[ETHER_ADDR_LEN];
+ uint8_t end[ETHER_ADDR_LEN];
+};
+
+/* packet data buffer */
+struct packet {
+ channel_t * chan; /* source channel */
+ uint8_t * dst; /* dest address */
+ uint8_t * src; /* source address */
+ uint8_t * type; /* protocol type */
+ uint8_t * ptr; /* data pointer */
+ size_t len; /* data length */
+ int refcnt; /* reference count */
+ extlist_t extlist;/* extension headers */
+ uint8_t buf[0]; /* data starts here */
+};
+
+/* extension header */
+struct exthdr {
+ STAILQ_ENTRY(exthdr) next;
+ uint8_t * ptr;
+ uint8_t len;
+};
+
+/* packet header */
+struct pkthdr {
+ STAILQ_ENTRY(pkthdr) next;
+ packet_t * data;
+};
+
+/* global variables */
+extern const char * control_path;
+extern const char * service_name;
+extern const char * interface_name;
+extern bdaddr_t local_bdaddr;
+extern bdaddr_t remote_bdaddr;
+extern uint16_t l2cap_psm;
+extern int l2cap_mode;
+extern uint16_t service_class;
+extern int server_limit;
+
+/*
+ * Bluetooth addresses are stored the other way around than
+ * Ethernet addresses even though they are of the same family
+ */
+static inline void
+b2eaddr(void *dst, bdaddr_t *src)
+{
+ uint8_t *d = dst;
+ int i;
+
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ d[i] = src->b[ETHER_ADDR_LEN - i - 1];
+}
+
+#define log_err(fmt, args...) syslog(LOG_ERR, fmt , ##args)
+#define log_info(fmt, args...) syslog(LOG_INFO, fmt , ##args)
+#define log_notice(fmt, args...) syslog(LOG_NOTICE, fmt , ##args)
+#define log_debug(fmt, args...) syslog(LOG_DEBUG, "%s: " fmt, __func__ , ##args)
+
+/* bnep.c */
+bool bnep_send(channel_t *, packet_t *);
+bool bnep_recv(packet_t *);
+void bnep_send_control(channel_t *, uint8_t, ...);
+
+/* channel.c */
+void channel_init(void);
+channel_t * channel_alloc(void);
+bool channel_open(channel_t *, int);
+void channel_close(channel_t *);
+void channel_free(channel_t *);
+void channel_timeout(channel_t *, int);
+void channel_put(channel_t *, packet_t *);
+
+/* client.c */
+void client_init(void);
+
+/* packet.c */
+packet_t * packet_alloc(channel_t *);
+void packet_free(packet_t *);
+void packet_adj(packet_t *, size_t);
+pkthdr_t * pkthdr_alloc(packet_t *);
+void pkthdr_free(pkthdr_t *);
+
+/* server.c */
+void server_init(void);
+void server_update(int);
+
+/* tap.c */
+void tap_init(void);
diff --git a/usr.sbin/bluetooth/btpand/channel.c b/usr.sbin/bluetooth/btpand/channel.c
new file mode 100644
index 0000000..b4eb4ab
--- /dev/null
+++ b/usr.sbin/bluetooth/btpand/channel.c
@@ -0,0 +1,335 @@
+/* $NetBSD: channel.c,v 1.1 2008/08/17 13:20:57 plunky Exp $ */
+
+/*-
+ * Copyright (c) 2008 Iain Hibbert
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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/cdefs.h>
+__RCSID("$NetBSD: channel.c,v 1.1 2008/08/17 13:20:57 plunky Exp $");
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+
+#include <libutil.h>
+#include <unistd.h>
+
+#include "btpand.h"
+
+static struct chlist channel_list;
+static int channel_count;
+static int channel_tick;
+
+static void channel_start(int, short, void *);
+static void channel_read(int, short, void *);
+static void channel_dispatch(packet_t *);
+static void channel_watchdog(int, short, void *);
+
+void
+channel_init(void)
+{
+
+ LIST_INIT(&channel_list);
+}
+
+channel_t *
+channel_alloc(void)
+{
+ channel_t *chan;
+
+ chan = malloc(sizeof(channel_t));
+ if (chan == NULL) {
+ log_err("%s() failed: %m", __func__);
+ return NULL;
+ }
+
+ memset(chan, 0, sizeof(channel_t));
+ STAILQ_INIT(&chan->pktlist);
+ chan->state = CHANNEL_CLOSED;
+ LIST_INSERT_HEAD(&channel_list, chan, next);
+
+ server_update(++channel_count);
+
+ return chan;
+}
+
+bool
+channel_open(channel_t *chan, int fd)
+{
+ int n;
+
+ assert(chan->refcnt == 0);
+ assert(chan->state != CHANNEL_CLOSED);
+
+ if (chan->mtu > 0) {
+ chan->sendbuf = malloc(chan->mtu);
+ if (chan->sendbuf == NULL) {
+ log_err("Could not malloc channel sendbuf: %m");
+ return false;
+ }
+ }
+
+ n = 1;
+ if (ioctl(fd, FIONBIO, &n) == -1) {
+ log_err("Could not set non-blocking IO: %m");
+ return false;
+ }
+
+ event_set(&chan->rd_ev, fd, EV_READ | EV_PERSIST, channel_read, chan);
+ if (event_add(&chan->rd_ev, NULL) == -1) {
+ log_err("Could not add channel read event: %m");
+ return false;
+ }
+
+ event_set(&chan->wr_ev, fd, EV_WRITE, channel_start, chan);
+
+ chan->refcnt++;
+ chan->fd = fd;
+
+ log_debug("(fd#%d)", chan->fd);
+
+ return true;
+}
+
+void
+channel_close(channel_t *chan)
+{
+ pkthdr_t *ph;
+
+ assert(chan->state != CHANNEL_CLOSED);
+
+ log_debug("(fd#%d)", chan->fd);
+
+ chan->state = CHANNEL_CLOSED;
+ event_del(&chan->rd_ev);
+ event_del(&chan->wr_ev);
+ close(chan->fd);
+ chan->refcnt--;
+ chan->tick = 0;
+
+ while ((ph = STAILQ_FIRST(&chan->pktlist)) != NULL) {
+ STAILQ_REMOVE_HEAD(&chan->pktlist, next);
+ pkthdr_free(ph);
+ chan->qlen--;
+ }
+
+ if (chan->pfh != NULL) {
+ pidfile_remove(chan->pfh);
+ chan->pfh = NULL;
+ }
+
+ if (chan->refcnt == 0)
+ channel_free(chan);
+}
+
+void
+channel_free(channel_t *chan)
+{
+
+ assert(chan->refcnt == 0);
+ assert(chan->state == CHANNEL_CLOSED);
+ assert(chan->qlen == 0);
+ assert(STAILQ_EMPTY(&chan->pktlist));
+
+ LIST_REMOVE(chan, next);
+ free(chan->pfilter);
+ free(chan->mfilter);
+ free(chan->sendbuf);
+ free(chan);
+
+ server_update(--channel_count);
+
+ if (server_limit == 0) {
+ log_info("connection closed, exiting");
+ exit(EXIT_SUCCESS);
+ }
+}
+
+static void
+channel_start(int fd, short ev, void *arg)
+{
+ channel_t *chan = arg;
+ pkthdr_t *ph;
+
+ chan->oactive = true;
+
+ while (chan->qlen > 0) {
+ ph = STAILQ_FIRST(&chan->pktlist);
+
+ channel_timeout(chan, 10);
+ if (chan->send(chan, ph->data) == false) {
+ if (event_add(&chan->wr_ev, NULL) == -1) {
+ log_err("Could not add channel write event: %m");
+ channel_close(chan);
+ }
+ return;
+ }
+
+ STAILQ_REMOVE_HEAD(&chan->pktlist, next);
+ pkthdr_free(ph);
+ chan->qlen--;
+ }
+
+ channel_timeout(chan, 0);
+ chan->oactive = false;
+}
+
+static void
+channel_read(int fd, short ev, void *arg)
+{
+ channel_t *chan = arg;
+ packet_t *pkt;
+ ssize_t nr;
+
+ pkt = packet_alloc(chan);
+ if (pkt == NULL) {
+ channel_close(chan);
+ return;
+ }
+
+ nr = read(fd, pkt->buf, chan->mru);
+ if (nr == -1) {
+ log_err("channel read error: %m");
+ packet_free(pkt);
+ channel_close(chan);
+ return;
+ }
+ if (nr == 0) { /* EOF */
+ log_debug("(fd#%d) EOF", fd);
+ packet_free(pkt);
+ channel_close(chan);
+ return;
+ }
+ pkt->len = nr;
+
+ if (chan->recv(pkt) == true)
+ channel_dispatch(pkt);
+
+ packet_free(pkt);
+}
+
+static void
+channel_dispatch(packet_t *pkt)
+{
+ channel_t *chan;
+
+ /*
+ * This is simple routing. I'm not sure if its allowed by
+ * the PAN or BNEP specifications, but it seems logical
+ * to send unicast packets to connected destinations where
+ * possible.
+ */
+ if (!ETHER_IS_MULTICAST(pkt->dst)) {
+ LIST_FOREACH(chan, &channel_list, next) {
+ if (chan == pkt->chan
+ || chan->state != CHANNEL_OPEN)
+ continue;
+
+ if (memcmp(pkt->dst, chan->raddr, ETHER_ADDR_LEN) == 0) {
+ if (chan->qlen > CHANNEL_MAXQLEN)
+ log_notice("Queue overflow");
+ else
+ channel_put(chan, pkt);
+
+ return;
+ }
+ }
+ }
+
+ LIST_FOREACH(chan, &channel_list, next) {
+ if (chan == pkt->chan
+ || chan->state != CHANNEL_OPEN)
+ continue;
+
+ if (chan->qlen > CHANNEL_MAXQLEN) {
+ log_notice("Queue overflow");
+ continue;
+ }
+
+ channel_put(chan, pkt);
+ }
+}
+
+void
+channel_put(channel_t *chan, packet_t *pkt)
+{
+ pkthdr_t *ph;
+
+ ph = pkthdr_alloc(pkt);
+ if (ph == NULL)
+ return;
+
+ chan->qlen++;
+ STAILQ_INSERT_TAIL(&chan->pktlist, ph, next);
+
+ if (!chan->oactive)
+ channel_start(chan->fd, EV_WRITE, chan);
+}
+
+/*
+ * Simple watchdog timer, only ticks when it is required and
+ * closes the channel down if it times out.
+ */
+void
+channel_timeout(channel_t *chan, int to)
+{
+ static struct event ev;
+
+ if (to == 0)
+ chan->tick = 0;
+ else
+ chan->tick = (channel_tick + to) % 60;
+
+ if (channel_tick == 0) {
+ evtimer_set(&ev, channel_watchdog, &ev);
+ channel_watchdog(0, 0, &ev);
+ }
+}
+
+static void
+channel_watchdog(int fd, short ev, void *arg)
+{
+ static struct timeval tv = { .tv_sec = 1 };
+ channel_t *chan, *next;
+ int tick;
+
+ tick = (channel_tick % 60) + 1;
+ channel_tick = 0;
+
+ next = LIST_FIRST(&channel_list);
+ while ((chan = next) != NULL) {
+ next = LIST_NEXT(chan, next);
+
+ if (chan->tick == tick)
+ channel_close(chan);
+ else if (chan->tick != 0)
+ channel_tick = tick;
+ }
+
+ if (channel_tick != 0 && evtimer_add(arg, &tv) < 0) {
+ log_err("Could not add watchdog event: %m");
+ exit(EXIT_FAILURE);
+ }
+}
diff --git a/usr.sbin/bluetooth/btpand/client.c b/usr.sbin/bluetooth/btpand/client.c
new file mode 100644
index 0000000..97064db
--- /dev/null
+++ b/usr.sbin/bluetooth/btpand/client.c
@@ -0,0 +1,192 @@
+/* $NetBSD: client.c,v 1.2 2008/12/06 20:01:14 plunky Exp $ */
+
+/*-
+ * Copyright (c) 2008 Iain Hibbert
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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/cdefs.h>
+__RCSID("$NetBSD: client.c,v 1.2 2008/12/06 20:01:14 plunky Exp $");
+
+#include <bluetooth.h>
+#include <errno.h>
+#include <sdp.h>
+#include <unistd.h>
+
+#include "btpand.h"
+#include "bnep.h"
+#include "sdp.h"
+
+static void client_query(void);
+
+void
+client_init(void)
+{
+ struct sockaddr_l2cap sa;
+ channel_t *chan;
+ socklen_t len;
+ int fd;
+ uint16_t mru, mtu;
+
+ if (bdaddr_any(&remote_bdaddr))
+ return;
+
+ if (service_name)
+ client_query();
+
+ fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP);
+ if (fd == -1) {
+ log_err("Could not open L2CAP socket: %m");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(&sa, 0, sizeof(sa));
+ sa.l2cap_family = AF_BLUETOOTH;
+ sa.l2cap_len = sizeof(sa);
+ bdaddr_copy(&sa.l2cap_bdaddr, &local_bdaddr);
+ if (bind(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
+ log_err("Could not bind client socket: %m");
+ exit(EXIT_FAILURE);
+ }
+
+ mru = BNEP_MTU_MIN;
+ if (setsockopt(fd, SOL_L2CAP, SO_L2CAP_IMTU, &mru, sizeof(mru)) == -1) {
+ log_err("Could not set L2CAP IMTU (%d): %m", mru);
+ exit(EXIT_FAILURE);
+ }
+
+ log_info("Opening connection to service 0x%4.4x at %s",
+ service_class, bt_ntoa(&remote_bdaddr, NULL));
+
+ sa.l2cap_psm = htole16(l2cap_psm);
+ bdaddr_copy(&sa.l2cap_bdaddr, &remote_bdaddr);
+ if (connect(fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
+ log_err("Could not connect: %m");
+ exit(EXIT_FAILURE);
+ }
+
+ len = sizeof(mru);
+ if (getsockopt(fd, SOL_L2CAP, SO_L2CAP_IMTU, &mru, &len) == -1) {
+ log_err("Could not get IMTU: %m");
+ exit(EXIT_FAILURE);
+ }
+ if (mru < BNEP_MTU_MIN) {
+ log_err("L2CAP IMTU too small (%d)", mru);
+ exit(EXIT_FAILURE);
+ }
+
+ len = sizeof(mtu);
+ if (getsockopt(fd, SOL_L2CAP, SO_L2CAP_OMTU, &mtu, &len) == -1) {
+ log_err("Could not get L2CAP OMTU: %m");
+ exit(EXIT_FAILURE);
+ }
+ if (mtu < BNEP_MTU_MIN) {
+ log_err("L2CAP OMTU too small (%d)", mtu);
+ exit(EXIT_FAILURE);
+ }
+
+ chan = channel_alloc();
+ if (chan == NULL)
+ exit(EXIT_FAILURE);
+
+ chan->send = bnep_send;
+ chan->recv = bnep_recv;
+ chan->mru = mru;
+ chan->mtu = mtu;
+ b2eaddr(chan->raddr, &remote_bdaddr);
+ b2eaddr(chan->laddr, &local_bdaddr);
+ chan->state = CHANNEL_WAIT_CONNECT_RSP;
+ channel_timeout(chan, 10);
+ if (!channel_open(chan, fd))
+ exit(EXIT_FAILURE);
+
+ bnep_send_control(chan, BNEP_SETUP_CONNECTION_REQUEST,
+ 2, service_class, SDP_SERVICE_CLASS_PANU);
+}
+
+static void
+client_query(void)
+{
+ uint8_t buffer[512];
+ sdp_attr_t attr;
+ uint32_t range;
+ void *ss;
+ int rv;
+ uint8_t *seq0, *seq1;
+
+ attr.flags = SDP_ATTR_INVALID;
+ attr.attr = 0;
+ attr.vlen = sizeof(buffer);
+ attr.value = buffer;
+
+ range = SDP_ATTR_RANGE(SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
+ SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST);
+
+ ss = sdp_open(&local_bdaddr, &remote_bdaddr);
+ if (ss == NULL || (errno = sdp_error(ss)) != 0) {
+ log_err("%s: %m", service_name);
+ exit(EXIT_FAILURE);
+ }
+
+ log_info("Searching for %s service at %s",
+ service_name, bt_ntoa(&remote_bdaddr, NULL));
+
+ rv = sdp_search(ss, 1, &service_class, 1, &range, 1, &attr);
+ if (rv != 0) {
+ log_err("%s: %s", service_name, strerror(sdp_error(ss)));
+ exit(EXIT_FAILURE);
+ }
+
+ sdp_close(ss);
+
+ if (attr.flags != SDP_ATTR_OK
+ || attr.attr != SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST) {
+ log_err("%s service not found", service_name);
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * we expect the following protocol descriptor list
+ *
+ * seq len
+ * seq len
+ * uuid value == L2CAP
+ * uint16 value16 => PSM
+ * seq len
+ * uuid value == BNEP
+ */
+ if (_sdp_get_seq(&attr.value, attr.value + attr.vlen, &seq0)
+ && _sdp_get_seq(&seq0, attr.value, &seq1)
+ && _sdp_match_uuid16(&seq1, seq0, SDP_UUID_PROTOCOL_L2CAP)
+ && _sdp_get_uint16(&seq1, seq0, &l2cap_psm)
+ && _sdp_get_seq(&seq0, attr.value, &seq1)
+ && _sdp_match_uuid16(&seq1, seq0, SDP_UUID_PROTOCOL_BNEP)) {
+ log_info("Found PSM %d for service %s", l2cap_psm, service_name);
+ return;
+ }
+
+ log_err("%s query failed", service_name);
+ exit(EXIT_FAILURE);
+}
diff --git a/usr.sbin/bluetooth/btpand/event.c b/usr.sbin/bluetooth/btpand/event.c
new file mode 100644
index 0000000..253084e
--- /dev/null
+++ b/usr.sbin/bluetooth/btpand/event.c
@@ -0,0 +1,309 @@
+/*
+ * event.h
+ */
+
+/*-
+ * Copyright (c) 2009 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $FreeBSD$ */
+
+/*
+ * Hack to provide libevent (see devel/libevent port) like API.
+ * Should be removed if FreeBSD ever decides to import libevent into base.
+ */
+
+#include <sys/select.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+#include <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "event.h"
+#include "btpand.h"
+
+#define __event_link(ev) \
+do { \
+ TAILQ_INSERT_TAIL(&pending, ev, next); \
+ ev->flags |= EV_PENDING; \
+} while (0)
+
+static void tv_add(struct timeval *, struct timeval const *);
+static void tv_sub(struct timeval *, struct timeval const *);
+static int tv_cmp(struct timeval const *, struct timeval const *);
+static int __event_dispatch(void);
+static void __event_add_current(struct event *);
+static void __event_del_current(struct event *);
+
+
+static TAILQ_HEAD(, event) pending;
+static TAILQ_HEAD(, event) current;
+
+void
+event_init(void)
+{
+ TAILQ_INIT(&pending);
+}
+
+int
+event_dispatch(void)
+{
+ while (__event_dispatch() == 0)
+ ;
+
+ return (-1);
+}
+
+static int
+__event_dispatch(void)
+{
+ fd_set r, w;
+ int nfd;
+ struct event *ev;
+ struct timeval now, timeout, t;
+
+ FD_ZERO(&r);
+ FD_ZERO(&w);
+
+ nfd = 0;
+
+ gettimeofday(&now, NULL);
+
+ timeout.tv_sec = 10; /* arbitrary */
+ timeout.tv_usec = 0;
+
+ TAILQ_INIT(&current);
+
+ /*
+ * Build fd_set's
+ */
+
+ event_log_debug("%s: building fd set...", __func__);
+
+ while (!TAILQ_EMPTY(&pending)) {
+ ev = TAILQ_FIRST(&pending);
+ event_del(ev);
+
+ if (ev->flags & EV_HAS_TIMEOUT) {
+ t = now;
+
+ if (tv_cmp(&t, &ev->expire) <= 0)
+ t.tv_sec = t.tv_usec = 0;
+ else
+ tv_sub(&t, &ev->expire);
+
+ if (tv_cmp(&t, &timeout) < 0)
+ timeout = t;
+ }
+
+ if (ev->fd >= 0) {
+ if (ev->flags & EV_READ) {
+ FD_SET(ev->fd, &r);
+ nfd = (nfd > ev->fd) ? nfd : ev->fd;
+ }
+
+ if (ev->flags & EV_WRITE) {
+ FD_SET(ev->fd, &w);
+ nfd = (nfd > ev->fd) ? nfd : ev->fd;
+ }
+ }
+
+ __event_add_current(ev);
+ }
+
+ event_log_debug("%s: waiting for events...", __func__);
+
+ nfd = select(nfd + 1, &r, &w, NULL, &timeout);
+ if (nfd < 0)
+ return (-1);
+
+ /*
+ * Process current pending
+ */
+
+ event_log_debug("%s: processing events...", __func__);
+
+ gettimeofday(&now, NULL);
+
+ while (!TAILQ_EMPTY(&current)) {
+ ev = TAILQ_FIRST(&current);
+ __event_del_current(ev);
+
+ /* check if fd is ready for reading/writing */
+ if (nfd > 0 && ev->fd >= 0) {
+ if (FD_ISSET(ev->fd, &r) || FD_ISSET(ev->fd, &w)) {
+ if (ev->flags & EV_PERSIST) {
+ if (ev->flags & EV_HAS_TIMEOUT)
+ event_add(ev, &ev->timeout);
+ else
+ event_add(ev, NULL);
+ }
+
+ nfd --;
+
+ event_log_debug("%s: calling %p(%d, %p), " \
+ "ev=%p", __func__, ev->cb, ev->fd,
+ ev->cbarg, ev);
+
+ (ev->cb)(ev->fd,
+ (ev->flags & (EV_READ|EV_WRITE)),
+ ev->cbarg);
+
+ continue;
+ }
+ }
+
+ /* if event has no timeout - just requeue */
+ if ((ev->flags & EV_HAS_TIMEOUT) == 0) {
+ event_add(ev, NULL);
+ continue;
+ }
+
+ /* check if event has expired */
+ if (tv_cmp(&now, &ev->expire) >= 0) {
+ if (ev->flags & EV_PERSIST)
+ event_add(ev, &ev->timeout);
+
+ event_log_debug("%s: calling %p(%d, %p), ev=%p",
+ __func__, ev->cb, ev->fd, ev->cbarg, ev);
+
+ (ev->cb)(ev->fd,
+ (ev->flags & (EV_READ|EV_WRITE)),
+ ev->cbarg);
+
+ continue;
+ }
+
+ assert((ev->flags & (EV_PENDING|EV_CURRENT)) == 0);
+ __event_link(ev);
+ }
+
+ return (0);
+}
+
+void
+__event_set(struct event *ev, int fd, short flags,
+ void (*cb)(int, short, void *), void *cbarg)
+{
+ ev->fd = fd;
+ ev->flags = flags;
+ ev->cb = cb;
+ ev->cbarg = cbarg;
+}
+
+int
+__event_add(struct event *ev, const struct timeval *timeout)
+{
+ assert((ev->flags & (EV_PENDING|EV_CURRENT)) == 0);
+
+ if (timeout != NULL) {
+ gettimeofday(&ev->expire, NULL);
+ tv_add(&ev->expire, timeout);
+ ev->timeout = *timeout;
+ ev->flags |= EV_HAS_TIMEOUT;
+ } else
+ ev->flags &= ~EV_HAS_TIMEOUT;
+
+ __event_link(ev);
+
+ return (0);
+}
+
+int
+__event_del(struct event *ev)
+{
+ assert((ev->flags & EV_CURRENT) == 0);
+
+ if ((ev->flags & EV_PENDING) != 0) {
+ TAILQ_REMOVE(&pending, ev, next);
+ ev->flags &= ~EV_PENDING;
+ }
+
+ return (0);
+}
+
+static void
+__event_add_current(struct event *ev)
+{
+ assert((ev->flags & (EV_PENDING|EV_CURRENT)) == 0);
+
+ TAILQ_INSERT_TAIL(&current, ev, next);
+ ev->flags |= EV_CURRENT;
+}
+
+static void
+__event_del_current(struct event *ev)
+{
+ assert((ev->flags & (EV_CURRENT|EV_PENDING)) == EV_CURRENT);
+
+ TAILQ_REMOVE(&current, ev, next);
+ ev->flags &= ~EV_CURRENT;
+}
+
+static void
+tv_add(struct timeval *a, struct timeval const *b)
+{
+ a->tv_sec += b->tv_sec;
+ a->tv_usec += b->tv_usec;
+
+ if(a->tv_usec >= 1000000) {
+ a->tv_usec -= 1000000;
+ a->tv_sec += 1;
+ }
+}
+
+static void
+tv_sub(struct timeval *a, struct timeval const *b)
+{
+ if (a->tv_usec < b->tv_usec) {
+ a->tv_usec += 1000000;
+ a->tv_sec -= 1;
+ }
+
+ a->tv_usec -= b->tv_usec;
+ a->tv_sec -= b->tv_sec;
+}
+
+static int
+tv_cmp(struct timeval const *a, struct timeval const *b)
+{
+ if (a->tv_sec > b->tv_sec)
+ return (1);
+
+ if (a->tv_sec < b->tv_sec)
+ return (-1);
+
+ if (a->tv_usec > b->tv_usec)
+ return (1);
+
+ if (a->tv_usec < b->tv_usec)
+ return (-1);
+
+ return (0);
+}
+
diff --git a/usr.sbin/bluetooth/btpand/event.h b/usr.sbin/bluetooth/btpand/event.h
new file mode 100644
index 0000000..75515e3
--- /dev/null
+++ b/usr.sbin/bluetooth/btpand/event.h
@@ -0,0 +1,147 @@
+/*
+ * event.h
+ */
+
+/*-
+ * Copyright (c) 2009 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* $FreeBSD$ */
+
+/*
+ * Hack to provide libevent (see devel/libevent port) like API.
+ * Should be removed if FreeBSD ever decides to import libevent into base.
+ */
+
+#ifndef _EVENT_H_
+#define _EVENT_H_ 1
+
+#define EV_READ 0x02
+#define EV_WRITE 0x04
+#define EV_PERSIST 0x10 /* Persistant event */
+#define EV_PENDING (1 << 13) /* internal use only! */
+#define EV_HAS_TIMEOUT (1 << 14) /* internal use only! */
+#define EV_CURRENT (1 << 15) /* internal use only! */
+
+struct event
+{
+ int fd;
+ short flags;
+ void (*cb)(int, short, void *);
+ void *cbarg;
+ struct timeval timeout;
+ struct timeval expire;
+
+#ifdef EVENT_DEBUG
+ char const *files[3];
+ int lines[3];
+#endif
+
+ TAILQ_ENTRY(event) next;
+};
+
+void event_init (void);
+int event_dispatch (void);
+
+void __event_set (struct event *, int, short,
+ void (*)(int, short, void *), void *);
+int __event_add (struct event *, struct timeval const *);
+int __event_del (struct event *);
+
+#ifdef EVENT_DEBUG
+#define event_log_err(fmt, args...) syslog(LOG_ERR, fmt, ##args)
+#define event_log_info(fmt, args...) syslog(LOG_INFO, fmt, ##args)
+#define event_log_notice(fmt, args...) syslog(LOG_NOTICE, fmt, ##args)
+#define event_log_debug(fmt, args...) syslog(LOG_DEBUG, fmt, ##args)
+
+#define event_set(ev, fd, flags, cb, cbarg) \
+ _event_set(__FILE__, __LINE__, ev, fd, flags, cb, cbarg)
+#define event_add(ev, timeout) \
+ _event_add(__FILE__, __LINE__, ev, timeout)
+#define event_del(ev) \
+ _event_del(__FILE__, __LINE__, ev)
+
+#define evtimer_set(ev, cb, cbarg) \
+ _event_set(__FILE__, __LINE__, ev, -1, 0, cb, cbarg)
+#define evtimer_add(ev, timeout) \
+ _event_add(__FILE__, __LINE__, ev, timeout)
+
+static inline void
+_event_set(char const *file, int line, struct event *ev, int fd, short flags,
+ void (*cb)(int, short, void *), void *cbarg)
+{
+ event_log_debug("set %s:%d ev=%p, fd=%d, flags=%#x, cb=%p, cbarg=%p",
+ file, line, ev, fd, flags, cb, cbarg);
+
+ ev->files[0] = file;
+ ev->lines[0] = line;
+
+ __event_set(ev, fd, flags, cb, cbarg);
+}
+
+static inline int
+_event_add(char const *file, int line, struct event *ev,
+ struct timeval const *timeout) {
+ event_log_debug("add %s:%d ev=%p, fd=%d, flags=%#x, cb=%p, cbarg=%p, " \
+ "timeout=%p", file, line, ev, ev->fd, ev->flags, ev->cb,
+ ev->cbarg, timeout);
+
+ ev->files[1] = file;
+ ev->lines[1] = line;
+
+ return (__event_add(ev, timeout));
+}
+
+static inline int
+_event_del(char const *file, int line, struct event *ev)
+{
+ event_log_debug("del %s:%d ev=%p, fd=%d, flags=%#x, cb=%p, cbarg=%p",
+ file, line, ev, ev->fd, ev->flags, ev->cb, ev->cbarg);
+
+ ev->files[2] = file;
+ ev->lines[2] = line;
+
+ return (__event_del(ev));
+}
+#else
+#define event_log_err(fmt, args...)
+#define event_log_info(fmt, args...)
+#define event_log_notice(fmt, args...)
+#define event_log_debug(fmt, args...)
+
+#define event_set(ev, fd, flags, cb, cbarg) \
+ __event_set(ev, fd, flags, cb, cbarg)
+#define event_add(ev, timeout) \
+ __event_add(ev, timeout)
+#define event_del(ev) \
+ __event_del(ev)
+
+#define evtimer_set(ev, cb, cbarg) \
+ __event_set(ev, -1, 0, cb, cbarg)
+#define evtimer_add(ev, timeout) \
+ __event_add(ev, timeout)
+#endif /* EVENT_DEBUG */
+
+#endif /* ndef _EVENT_H_ */
diff --git a/usr.sbin/bluetooth/btpand/packet.c b/usr.sbin/bluetooth/btpand/packet.c
new file mode 100644
index 0000000..e42e5c5
--- /dev/null
+++ b/usr.sbin/bluetooth/btpand/packet.c
@@ -0,0 +1,110 @@
+/* $NetBSD: packet.c,v 1.1 2008/08/17 13:20:57 plunky Exp $ */
+
+/*-
+ * Copyright (c) 2008 Iain Hibbert
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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/cdefs.h>
+__RCSID("$NetBSD: packet.c,v 1.1 2008/08/17 13:20:57 plunky Exp $");
+
+#include "btpand.h"
+
+packet_t *
+packet_alloc(channel_t *chan)
+{
+ packet_t *pkt;
+
+ pkt = malloc(sizeof(packet_t) + chan->mru);
+ if (pkt == NULL) {
+ log_err("%s() failed: %m", __func__);
+ return NULL;
+ }
+
+ memset(pkt, 0, sizeof(packet_t));
+ STAILQ_INIT(&pkt->extlist);
+ pkt->ptr = pkt->buf;
+
+ pkt->chan = chan;
+ chan->refcnt++;
+
+ return pkt;
+}
+
+void
+packet_free(packet_t *pkt)
+{
+ exthdr_t *eh;
+
+ if (pkt->refcnt-- > 0)
+ return;
+
+ while ((eh = STAILQ_FIRST(&pkt->extlist)) != NULL) {
+ STAILQ_REMOVE_HEAD(&pkt->extlist, next);
+ free(eh);
+ }
+
+ pkt->chan->refcnt--;
+ if (pkt->chan->refcnt == 0)
+ channel_free(pkt->chan);
+
+ free(pkt);
+}
+
+void
+packet_adj(packet_t *pkt, size_t size)
+{
+
+ assert(pkt->refcnt == 0);
+ assert(pkt->len >= size);
+
+ pkt->ptr += size;
+ pkt->len -= size;
+}
+
+pkthdr_t *
+pkthdr_alloc(packet_t *pkt)
+{
+ pkthdr_t *ph;
+
+ ph = malloc(sizeof(pkthdr_t));
+ if (ph == NULL) {
+ log_err("%s() failed: %m", __func__);
+ return NULL;
+ }
+
+ ph->data = pkt;
+ pkt->refcnt++;
+
+ return ph;
+}
+
+void
+pkthdr_free(pkthdr_t *ph)
+{
+
+ packet_free(ph->data);
+ free(ph);
+}
diff --git a/usr.sbin/bluetooth/btpand/sdp.c b/usr.sbin/bluetooth/btpand/sdp.c
new file mode 100644
index 0000000..e5aec1c
--- /dev/null
+++ b/usr.sbin/bluetooth/btpand/sdp.c
@@ -0,0 +1,209 @@
+/* $NetBSD: sdp.c,v 1.2 2008/12/06 20:01:14 plunky Exp $ */
+
+/*-
+ * Copyright (c) 2008 Iain Hibbert
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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/cdefs.h>
+__RCSID("$NetBSD: sdp.c,v 1.2 2008/12/06 20:01:14 plunky Exp $");
+
+#include <string.h>
+
+#include "sdp.h"
+
+/*
+ * SDP data stream manipulation routines
+ */
+
+/* Bluetooth Base UUID */
+static const uuid_t BASE_UUID = {
+ 0x00000000,
+ 0x0000,
+ 0x1000,
+ 0x80,
+ 0x00,
+ { 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb }
+};
+
+/*
+ * _sdp_match_uuid16(ptr, limit, uuid)
+ *
+ * examine SDP data stream at ptr for a UUID, and return
+ * true if it matches the supplied short alias bluetooth UUID.
+ * limit is the first address past the end of valid data.
+ */
+bool
+_sdp_match_uuid16(uint8_t **ptr, uint8_t *limit, uint16_t uuid)
+{
+ uint8_t *p = *ptr;
+ uuid_t u1, u2;
+
+ memcpy(&u1, &BASE_UUID, sizeof(uuid_t));
+ u1.time_low = uuid;
+
+ if (!_sdp_get_uuid(&p, limit, &u2)
+ || !uuid_equal(&u1, &u2, NULL))
+ return false;
+
+ *ptr = p;
+ return true;
+}
+
+/*
+ * _sdp_get_uuid(ptr, limit, uuid)
+ *
+ * examine SDP data stream at ptr for a UUID, and extract
+ * to given storage, advancing ptr.
+ * limit is the first address past the end of valid data.
+ */
+bool
+_sdp_get_uuid(uint8_t **ptr, uint8_t *limit, uuid_t *uuid)
+{
+ uint8_t *p = *ptr;
+
+ if (p + 1 > limit)
+ return false;
+
+ switch (*p++) {
+ case SDP_DATA_UUID16:
+ if (p + 2 > limit)
+ return false;
+
+ memcpy(uuid, &BASE_UUID, sizeof(uuid_t));
+ uuid->time_low = be16dec(p);
+ p += 2;
+ break;
+
+ case SDP_DATA_UUID32:
+ if (p + 4 > limit)
+ return false;
+
+ memcpy(uuid, &BASE_UUID, sizeof(uuid_t));
+ uuid->time_low = be32dec(p);
+ p += 4;
+ break;
+
+ case SDP_DATA_UUID128:
+ if (p + 16 > limit)
+ return false;
+
+ uuid_dec_be(p, uuid);
+ p += 16;
+ break;
+
+ default:
+ return false;
+ }
+
+ *ptr = p;
+ return true;
+}
+
+/*
+ * _sdp_get_seq(ptr, limit, seq)
+ *
+ * examine SDP data stream at ptr for a sequence. return
+ * seq pointer if found and advance ptr to next object.
+ * limit is the first address past the end of valid data.
+ */
+bool
+_sdp_get_seq(uint8_t **ptr, uint8_t *limit, uint8_t **seq)
+{
+ uint8_t *p = *ptr;
+ int32_t l;
+
+ if (p + 1 > limit)
+ return false;
+
+ switch (*p++) {
+ case SDP_DATA_SEQ8:
+ if (p + 1 > limit)
+ return false;
+
+ l = *p;
+ p += 1;
+ break;
+
+ case SDP_DATA_SEQ16:
+ if (p + 2 > limit)
+ return false;
+
+ l = be16dec(p);
+ p += 2;
+ break;
+
+ case SDP_DATA_SEQ32:
+ if (p + 4 > limit)
+ return false;
+
+ l = be32dec(p);
+ p += 4;
+ break;
+
+ default:
+ return false;
+ }
+ if (p + l > limit)
+ return false;
+
+ *seq = p;
+ *ptr = p + l;
+ return true;
+}
+
+/*
+ * _sdp_get_uint16(ptr, limit, value)
+ *
+ * examine SDP data stream at ptr for a uint16_t, and
+ * extract to given storage, advancing ptr.
+ * limit is the first address past the end of valid data.
+ */
+bool
+_sdp_get_uint16(uint8_t **ptr, uint8_t *limit, uint16_t *value)
+{
+ uint8_t *p = *ptr;
+ uint16_t v;
+
+ if (p + 1 > limit)
+ return false;
+
+ switch (*p++) {
+ case SDP_DATA_UINT16:
+ if (p + 2 > limit)
+ return false;
+
+ v = be16dec(p);
+ p += 2;
+ break;
+
+ default:
+ return false;
+ }
+
+ *value = v;
+ *ptr = p;
+ return true;
+}
diff --git a/usr.sbin/bluetooth/btpand/sdp.h b/usr.sbin/bluetooth/btpand/sdp.h
new file mode 100644
index 0000000..32dd95d
--- /dev/null
+++ b/usr.sbin/bluetooth/btpand/sdp.h
@@ -0,0 +1,38 @@
+/* $NetBSD: sdp.h,v 1.2 2008/12/06 20:01:15 plunky Exp $ */
+
+/*-
+ * Copyright (c) 2008 Iain Hibbert
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <bluetooth.h>
+#include <sdp.h>
+#include <stdbool.h>
+#include <uuid.h>
+
+bool _sdp_match_uuid16(uint8_t **, uint8_t *, uint16_t);
+bool _sdp_get_uuid(uint8_t **, uint8_t *, uuid_t *);
+bool _sdp_get_seq(uint8_t **, uint8_t *, uint8_t **);
+bool _sdp_get_uint16(uint8_t **, uint8_t *, uint16_t *);
diff --git a/usr.sbin/bluetooth/btpand/server.c b/usr.sbin/bluetooth/btpand/server.c
new file mode 100644
index 0000000..0843d0c
--- /dev/null
+++ b/usr.sbin/bluetooth/btpand/server.c
@@ -0,0 +1,277 @@
+/* $NetBSD: server.c,v 1.2 2009/01/24 17:29:28 plunky Exp $ */
+
+/*-
+ * Copyright (c) 2008 Iain Hibbert
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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/cdefs.h>
+__RCSID("$NetBSD: server.c,v 1.2 2009/01/24 17:29:28 plunky Exp $");
+
+#include <sys/ioctl.h>
+
+#include <bluetooth.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <sdp.h>
+#include <unistd.h>
+
+#include "btpand.h"
+#include "bnep.h"
+
+static struct event server_ev;
+static int server_fd;
+static int server_avail;
+
+static void * server_ss;
+static uint32_t server_handle;
+
+static void server_open(void);
+static void server_close(void);
+static void server_read(int, short, void *);
+static void server_register(void);
+
+void
+server_init(void)
+{
+
+ server_fd = -1;
+}
+
+/*
+ * The server_update() function is called whenever the channel count is
+ * changed. We maintain the SDP record and open or close the server socket
+ * as required.
+ */
+void
+server_update(int count)
+{
+
+ if (server_limit == 0)
+ return;
+
+ log_debug("count %d", count);
+
+ server_avail = UINT8_MAX - (count - 1) * UINT8_MAX / server_limit;
+ log_info("Service Availability: %d/%d", server_avail, UINT8_MAX);
+
+ if (server_avail == 0 && server_fd != -1)
+ server_close();
+
+ if (server_avail > 0 && server_fd == -1)
+ server_open();
+
+ if (service_name)
+ server_register();
+}
+
+static void
+server_open(void)
+{
+ struct sockaddr_l2cap sa;
+ uint16_t mru;
+
+ server_fd = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP);
+ if (server_fd == -1) {
+ log_err("Could not open L2CAP socket: %m");
+ exit(EXIT_FAILURE);
+ }
+
+ memset(&sa, 0, sizeof(sa));
+ sa.l2cap_family = AF_BLUETOOTH;
+ sa.l2cap_len = sizeof(sa);
+ sa.l2cap_psm = htole16(l2cap_psm);
+ bdaddr_copy(&sa.l2cap_bdaddr, &local_bdaddr);
+ if (bind(server_fd, (struct sockaddr *)&sa, sizeof(sa)) == -1) {
+ log_err("Could not bind server socket: %m");
+ exit(EXIT_FAILURE);
+ }
+
+ mru = BNEP_MTU_MIN;
+ if (setsockopt(server_fd, SOL_L2CAP,
+ SO_L2CAP_IMTU, &mru, sizeof(mru)) == -1) {
+ log_err("Could not set L2CAP IMTU (%d): %m", mru);
+ exit(EXIT_FAILURE);
+ }
+
+ if (listen(server_fd, 0) == -1) {
+ log_err("Could not listen on server socket: %m");
+ exit(EXIT_FAILURE);
+ }
+
+ event_set(&server_ev, server_fd, EV_READ | EV_PERSIST, server_read, NULL);
+ if (event_add(&server_ev, NULL) == -1) {
+ log_err("Could not add server event: %m");
+ exit(EXIT_FAILURE);
+ }
+
+ log_info("server socket open");
+}
+
+static void
+server_close(void)
+{
+
+ event_del(&server_ev);
+ close(server_fd);
+ server_fd = -1;
+
+ log_info("server socket closed");
+}
+
+/*
+ * handle connection request
+ */
+static void
+server_read(int s, short ev, void *arg)
+{
+ struct sockaddr_l2cap ra, la;
+ channel_t *chan;
+ socklen_t len;
+ int fd, n;
+ uint16_t mru, mtu;
+
+ len = sizeof(ra);
+ fd = accept(s, (struct sockaddr *)&ra, &len);
+ if (fd == -1)
+ return;
+
+ n = 1;
+ if (ioctl(fd, FIONBIO, &n) == -1) {
+ log_err("Could not set NonBlocking IO: %m");
+ close(fd);
+ return;
+ }
+
+ len = sizeof(mru);
+ if (getsockopt(fd, SOL_L2CAP, SO_L2CAP_IMTU, &mru, &len) == -1) {
+ log_err("Could not get L2CAP IMTU: %m");
+ close(fd);
+ return;
+ }
+ if(mru < BNEP_MTU_MIN) {
+ log_err("L2CAP IMTU too small (%d)", mru);
+ close(fd);
+ return;
+ }
+
+ len = sizeof(mtu);
+ if (getsockopt(fd, SOL_L2CAP, SO_L2CAP_OMTU, &mtu, &len) == -1) {
+ log_err("Could not get L2CAP OMTU: %m");
+ close(fd);
+ return;
+ }
+ if (mtu < BNEP_MTU_MIN) {
+ log_err("L2CAP OMTU too small (%d)", mtu);
+ close(fd);
+ return;
+ }
+
+ len = sizeof(n);
+ if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &n, &len) == -1) {
+ log_err("Could not get socket send buffer size: %m");
+ close(fd);
+ return;
+ }
+
+ if (n < (mtu * 2)) {
+ n = mtu * 2;
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &n, sizeof(n)) == -1) {
+ log_err("Could not set socket send buffer size (%d): %m", n);
+ close(fd);
+ return;
+ }
+ }
+
+ n = mtu;
+ if (setsockopt(fd, SOL_SOCKET, SO_SNDLOWAT, &n, sizeof(n)) == -1) {
+ log_err("Could not set socket low water mark (%d): %m", n);
+ close(fd);
+ return;
+ }
+
+ len = sizeof(la);
+ if (getsockname(fd, (struct sockaddr *)&la, &len) == -1) {
+ log_err("Could not get socket address: %m");
+ close(fd);
+ return;
+ }
+
+ log_info("Accepted connection from %s", bt_ntoa(&ra.l2cap_bdaddr, NULL));
+
+ chan = channel_alloc();
+ if (chan == NULL) {
+ close(fd);
+ return;
+ }
+
+ chan->send = bnep_send;
+ chan->recv = bnep_recv;
+ chan->mru = mru;
+ chan->mtu = mtu;
+ b2eaddr(chan->raddr, &ra.l2cap_bdaddr);
+ b2eaddr(chan->laddr, &la.l2cap_bdaddr);
+ chan->state = CHANNEL_WAIT_CONNECT_REQ;
+ channel_timeout(chan, 10);
+ if (!channel_open(chan, fd)) {
+ chan->state = CHANNEL_CLOSED;
+ channel_free(chan);
+ close(fd);
+ return;
+ }
+}
+
+static void
+server_register(void)
+{
+ sdp_nap_profile_t p;
+ int rv;
+
+ if (server_ss == NULL) {
+ server_ss = sdp_open_local(control_path);
+ if (server_ss == NULL || sdp_error(server_ss) != 0) {
+ log_err("failed to contact SDP server");
+ return;
+ }
+ }
+
+ memset(&p, 0, sizeof(p));
+ p.psm = l2cap_psm;
+ p.load_factor = server_avail;
+ p.security_description = (l2cap_mode == 0 ? 0x0000 : 0x0001);
+
+ if (server_handle)
+ rv = sdp_change_service(server_ss, server_handle,
+ (uint8_t *)&p, sizeof(p));
+ else
+ rv = sdp_register_service(server_ss, service_class,
+ &local_bdaddr, (uint8_t *)&p, sizeof(p), &server_handle);
+
+ if (rv != 0) {
+ errno = sdp_error(server_ss);
+ log_err("%s: %m", service_name);
+ exit(EXIT_FAILURE);
+ }
+}
diff --git a/usr.sbin/bluetooth/btpand/tap.c b/usr.sbin/bluetooth/btpand/tap.c
new file mode 100644
index 0000000..c965633
--- /dev/null
+++ b/usr.sbin/bluetooth/btpand/tap.c
@@ -0,0 +1,167 @@
+/* $NetBSD: tap.c,v 1.1 2008/08/17 13:20:57 plunky Exp $ */
+
+/*-
+ * Copyright (c) 2008 Iain Hibbert
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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/cdefs.h>
+__RCSID("$NetBSD: tap.c,v 1.1 2008/08/17 13:20:57 plunky Exp $");
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+
+#include <net/if_tap.h>
+
+#include <fcntl.h>
+#include <libutil.h>
+#include <paths.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "btpand.h"
+
+static bool tap_send(channel_t *, packet_t *);
+static bool tap_recv(packet_t *);
+
+void
+tap_init(void)
+{
+ channel_t *chan;
+ struct ifreq ifr;
+ int fd, s;
+ char pidfile[PATH_MAX];
+
+ fd = open(interface_name, O_RDWR);
+ if (fd == -1) {
+ log_err("Could not open \"%s\": %m", interface_name);
+ exit(EXIT_FAILURE);
+ }
+
+ memset(&ifr, 0, sizeof(ifr));
+ if (ioctl(fd, TAPGIFNAME, &ifr) == -1) {
+ log_err("Could not get interface name: %m");
+ exit(EXIT_FAILURE);
+ }
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s == -1) {
+ log_err("Could not open PF_LINK socket: %m");
+ exit(EXIT_FAILURE);
+ }
+
+ ifr.ifr_addr.sa_family = AF_LINK;
+ ifr.ifr_addr.sa_len = ETHER_ADDR_LEN;
+ b2eaddr(ifr.ifr_addr.sa_data, &local_bdaddr);
+
+ if (ioctl(s, SIOCSIFLLADDR, &ifr) == -1) {
+ log_err("Could not set %s physical address: %m", ifr.ifr_name);
+ exit(EXIT_FAILURE);
+ }
+
+ if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1) {
+ log_err("Could not get interface flags: %m");
+ exit(EXIT_FAILURE);
+ }
+
+ if ((ifr.ifr_flags & IFF_UP) == 0) {
+ ifr.ifr_flags |= IFF_UP;
+
+ if (ioctl(s, SIOCSIFFLAGS, &ifr) == -1) {
+ log_err("Could not set IFF_UP: %m");
+ exit(EXIT_FAILURE);
+ }
+ }
+
+ close(s);
+
+ log_info("Using interface %s with addr %s", ifr.ifr_name,
+ ether_ntoa((struct ether_addr *)&ifr.ifr_addr.sa_data));
+
+ chan = channel_alloc();
+ if (chan == NULL)
+ exit(EXIT_FAILURE);
+
+ chan->send = tap_send;
+ chan->recv = tap_recv;
+ chan->mru = ETHER_HDR_LEN + ETHER_MAX_LEN;
+ memcpy(chan->raddr, ifr.ifr_addr.sa_data, ETHER_ADDR_LEN);
+ memcpy(chan->laddr, ifr.ifr_addr.sa_data, ETHER_ADDR_LEN);
+ chan->state = CHANNEL_OPEN;
+ if (!channel_open(chan, fd))
+ exit(EXIT_FAILURE);
+
+ snprintf(pidfile, sizeof(pidfile), "%s/%s.pid",
+ _PATH_VARRUN, ifr.ifr_name);
+ chan->pfh = pidfile_open(pidfile, 0600, NULL);
+ if (chan->pfh == NULL)
+ log_err("can't create pidfile");
+ else if (pidfile_write(chan->pfh) < 0) {
+ log_err("can't write pidfile");
+ pidfile_remove(chan->pfh);
+ chan->pfh = NULL;
+ }
+}
+
+static bool
+tap_send(channel_t *chan, packet_t *pkt)
+{
+ struct iovec iov[4];
+ ssize_t nw;
+
+ iov[0].iov_base = pkt->dst;
+ iov[0].iov_len = ETHER_ADDR_LEN;
+ iov[1].iov_base = pkt->src;
+ iov[1].iov_len = ETHER_ADDR_LEN;
+ iov[2].iov_base = pkt->type;
+ iov[2].iov_len = ETHER_TYPE_LEN;
+ iov[3].iov_base = pkt->ptr;
+ iov[3].iov_len = pkt->len;
+
+ /* tap device write never fails */
+ nw = writev(chan->fd, iov, __arraycount(iov));
+ assert(nw > 0);
+
+ return true;
+}
+
+static bool
+tap_recv(packet_t *pkt)
+{
+
+ if (pkt->len < ETHER_HDR_LEN)
+ return false;
+
+ pkt->dst = pkt->ptr;
+ packet_adj(pkt, ETHER_ADDR_LEN);
+ pkt->src = pkt->ptr;
+ packet_adj(pkt, ETHER_ADDR_LEN);
+ pkt->type = pkt->ptr;
+ packet_adj(pkt, ETHER_TYPE_LEN);
+
+ return true;
+}
diff --git a/usr.sbin/bluetooth/hccontrol/Makefile b/usr.sbin/bluetooth/hccontrol/Makefile
new file mode 100644
index 0000000..592247e
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/Makefile
@@ -0,0 +1,14 @@
+# $Id: Makefile,v 1.7 2003/08/14 20:06:17 max Exp $
+# $FreeBSD$
+
+PROG= hccontrol
+MAN= hccontrol.8
+SRCS= send_recv.c link_policy.c link_control.c \
+ host_controller_baseband.c info.c status.c node.c hccontrol.c \
+ util.c
+WARNS?= 2
+
+DPADD= ${LIBBLUETOOTH}
+LDADD= -lbluetooth
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bluetooth/hccontrol/hccontrol.8 b/usr.sbin/bluetooth/hccontrol/hccontrol.8
new file mode 100644
index 0000000..b98a0fc
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/hccontrol.8
@@ -0,0 +1,184 @@
+.\" Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: hccontrol.8,v 1.6 2003/08/06 21:26:38 max Exp $
+.\" $FreeBSD$
+.\"
+.Dd June 14, 2002
+.Dt HCCONTROL 8
+.Os
+.Sh NAME
+.Nm hccontrol
+.Nd HCI configuration utility
+.Sh SYNOPSIS
+.Nm
+.Op Fl hN
+.Op Fl n Ar HCI_node_name
+.Ar command
+.Op Ar parameters ...
+.Sh DESCRIPTION
+The
+.Nm
+utility connects to the specified Netgraph node of type
+.Dv HCI
+or the first one found if none is specified and attempts to send the specified
+command to the HCI Netgraph node or to the associated Bluetooth device.
+The
+.Nm
+utility will print results to the standard output and error messages to
+the standard error.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl h
+Display usage message and exit.
+.It Fl N
+Show Bluetooth addresses as numbers.
+Normally
+.Nm
+attempts to resolve Bluetooth addresses, and display them symbolically.
+.It Fl n Ar HCI_node_name
+Connect to the specified HCI Netgraph node.
+.It Ar command
+One of the supported commands (see below).
+The special command
+.Cm help
+can be used to obtain the list of all supported commands.
+To get more information about a specific command use
+.Cm help Ar command .
+.It Ar parameters
+One or more optional space separated command parameters.
+Many commands require a remote device address as one of the parameters.
+The remote device address can be specified as BD_ADDR or a name.
+If a name was specified then the
+.Nm
+utility will attempt to resolve the name via
+.Xr bt_gethostbyname 3 .
+.El
+.Sh COMMANDS
+The currently supported HCI commands in
+.Nm
+are:
+.Pp
+.Bl -tag -offset indent -compact
+.It Cm Inquiry
+.It Cm Create_Connection
+.It Cm Disconnect
+.It Cm Add_SCO_Connection
+.It Cm Change_Connection_Packet_Type
+.It Cm Remote_Name_Request
+.It Cm Read_Remote_Supported_Features
+.It Cm Read_Remote_Version_Information
+.It Cm Read_Clock_Offset
+.It Cm Role_Discovery
+.It Cm Switch_Role
+.It Cm Read_Link_Policy_Settings
+.It Cm Write_Link_Policy_Settings
+.It Cm Reset
+.It Cm Read_Pin_Type
+.It Cm Write_Pin_Type
+.It Cm Read_Stored_Link_Key
+.It Cm Write_Stored_Link_Key
+.It Cm Delete_Stored_Link_Key
+.It Cm Change_Local_Name
+.It Cm Read_Local_Name
+.It Cm Read_Connection_Accept_Timeout
+.It Cm Write_Connection_Accept_Timeout
+.It Cm Read_Page_Timeout
+.It Cm Write_Page_Timeout
+.It Cm Read_Scan_Enable
+.It Cm Write_Scan_Enable
+.It Cm Read_Page_Scan_Activity
+.It Cm Write_Page_Scan_Activity
+.It Cm Read_Inquiry_Scan_Activity
+.It Cm Write_Inquiry_Scan_Activity
+.It Cm Read_Authentication_Enable
+.It Cm Write_Authentication_Enable
+.It Cm Read_Encryption_Mode
+.It Cm Write_Encryption_Mode
+.It Cm Read_Class_Of_Device
+.It Cm Write_Class_Of_Device
+.It Cm Read_Voice_Settings
+.It Cm Write_Voice_Settings
+.It Cm Read_Number_Broadcast_Retransmissions
+.It Cm Write_Number_Broadcast_Retransmissions
+.It Cm Read_Hold_Mode_Activity
+.It Cm Write_Hold_Mode_Activity
+.It Cm Read_SCO_Flow_Control_Enable
+.It Cm Write_SCO_Flow_Control_Enable
+.It Cm Read_Link_Supervision_Timeout
+.It Cm Write_Link_Supervision_Timeout
+.It Cm Read_Page_Scan_Period_Mode
+.It Cm Write_Page_Scan_Period_Mode
+.It Cm Read_Page_Scan_Mode
+.It Cm Write_Page_Scan_Mode
+.It Cm Read_Local_Version_Information
+.It Cm Read_Local_Supported_Features
+.It Cm Read_Buffer_Size
+.It Cm Read_Country_Code
+.It Cm Read_BD_ADDR
+.It Cm Read_Failed_Contact_Counter
+.It Cm Reset_Failed_Contact_Counter
+.It Cm Get_Link_Quality
+.It Cm Read_RSSI
+.El
+.Pp
+The currently supported node commands in
+.Nm
+are:
+.Pp
+.Bl -tag -offset indent -compact
+.It Cm Read_Node_State
+.It Cm Initialize
+.It Cm Read_Debug_Level
+.It Cm Write_Debug_Level
+.It Cm Read_Node_Buffer_Size
+.It Cm Read_Node_BD_ADDR
+.It Cm Read_Node_Features
+.It Cm Read_Node_Stat
+.It Cm Reset_Node_Stat
+.It Cm Flush_Neighbor_Cache
+.It Cm Read_Neighbor_Cache
+.It Cm Read_Connection_List
+.It Cm Read_Node_Link_Policy_Settings_Mask
+.It Cm Write_Node_Link_Policy_Settings_Mask
+.It Cm Read_Node_Packet_Mask
+.It Cm Write_Node_Packet_Mask
+.It Cm Read_Node_Role_Switch
+.It Cm Write_Node_Role_Switch
+.It Cm Read_Node_List
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr bluetooth 3 ,
+.Xr netgraph 3 ,
+.Xr netgraph 4 ,
+.Xr ng_hci 4 ,
+.Xr hcseriald 8
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
+.Sh BUGS
+Most likely.
+Please report if found.
diff --git a/usr.sbin/bluetooth/hccontrol/hccontrol.c b/usr.sbin/bluetooth/hccontrol/hccontrol.c
new file mode 100644
index 0000000..089869b
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/hccontrol.c
@@ -0,0 +1,322 @@
+/*
+ * hccontrol.c
+ *
+ * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: hccontrol.c,v 1.5 2003/09/05 00:38:24 max Exp $
+ * $FreeBSD$
+ */
+
+#include <bluetooth.h>
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <netgraph/ng_message.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "hccontrol.h"
+
+/* Prototypes */
+static int do_hci_command (char const *, int, char **);
+static struct hci_command * find_hci_command (char const *, struct hci_command *);
+static int find_hci_nodes (struct nodeinfo **);
+static void print_hci_command (struct hci_command *);
+static void usage (void);
+
+/* Globals */
+int verbose = 0;
+int timeout;
+int numeric_bdaddr = 0;
+
+/* Main */
+int
+main(int argc, char *argv[])
+{
+ char *node = NULL;
+ int n;
+
+ /* Process command line arguments */
+ while ((n = getopt(argc, argv, "n:Nvh")) != -1) {
+ switch (n) {
+ case 'n':
+ node = optarg;
+ break;
+
+ case 'N':
+ numeric_bdaddr = 1;
+ break;
+
+ case 'v':
+ verbose = 1;
+ break;
+
+ case 'h':
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (*argv == NULL)
+ usage();
+
+ n = do_hci_command(node, argc, argv);
+
+ return (n);
+} /* main */
+
+/* Create socket and bind it */
+static int
+socket_open(char const *node)
+{
+ struct sockaddr_hci addr;
+ struct ng_btsocket_hci_raw_filter filter;
+ int s, mib[4], num;
+ size_t size;
+ struct nodeinfo *nodes;
+
+ num = find_hci_nodes(&nodes);
+ if (num == 0)
+ errx(7, "Could not find HCI nodes");
+
+ if (node == NULL) {
+ node = strdup(nodes[0].name);
+ if (num > 1)
+ fprintf(stdout, "Using HCI node: %s\n", node);
+ }
+
+ free(nodes);
+
+ s = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_HCI);
+ if (s < 0)
+ err(1, "Could not create socket");
+
+ memset(&addr, 0, sizeof(addr));
+ addr.hci_len = sizeof(addr);
+ addr.hci_family = AF_BLUETOOTH;
+ strncpy(addr.hci_node, node, sizeof(addr.hci_node));
+ if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+ err(2, "Could not bind socket, node=%s", node);
+
+ if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+ err(3, "Could not connect socket, node=%s", node);
+
+ memset(&filter, 0, sizeof(filter));
+ bit_set(filter.event_mask, NG_HCI_EVENT_COMMAND_COMPL - 1);
+ bit_set(filter.event_mask, NG_HCI_EVENT_COMMAND_STATUS - 1);
+ bit_set(filter.event_mask, NG_HCI_EVENT_INQUIRY_COMPL - 1);
+ bit_set(filter.event_mask, NG_HCI_EVENT_INQUIRY_RESULT - 1);
+ bit_set(filter.event_mask, NG_HCI_EVENT_CON_COMPL - 1);
+ bit_set(filter.event_mask, NG_HCI_EVENT_DISCON_COMPL - 1);
+ bit_set(filter.event_mask, NG_HCI_EVENT_REMOTE_NAME_REQ_COMPL - 1);
+ bit_set(filter.event_mask, NG_HCI_EVENT_READ_REMOTE_FEATURES_COMPL - 1);
+ bit_set(filter.event_mask, NG_HCI_EVENT_READ_REMOTE_VER_INFO_COMPL - 1);
+ bit_set(filter.event_mask, NG_HCI_EVENT_RETURN_LINK_KEYS - 1);
+ bit_set(filter.event_mask, NG_HCI_EVENT_READ_CLOCK_OFFSET_COMPL - 1);
+ bit_set(filter.event_mask, NG_HCI_EVENT_CON_PKT_TYPE_CHANGED - 1);
+ bit_set(filter.event_mask, NG_HCI_EVENT_ROLE_CHANGE - 1);
+
+ if (setsockopt(s, SOL_HCI_RAW, SO_HCI_RAW_FILTER,
+ (void * const) &filter, sizeof(filter)) < 0)
+ err(4, "Could not setsockopt()");
+
+ size = (sizeof(mib)/sizeof(mib[0]));
+ if (sysctlnametomib("net.bluetooth.hci.command_timeout",mib,&size) < 0)
+ err(5, "Could not sysctlnametomib()");
+
+ if (sysctl(mib, sizeof(mib)/sizeof(mib[0]),
+ (void *) &timeout, &size, NULL, 0) < 0)
+ err(6, "Could not sysctl()");
+
+ timeout ++;
+
+ return (s);
+} /* socket_open */
+
+/* Execute commands */
+static int
+do_hci_command(char const *node, int argc, char **argv)
+{
+ char *cmd = argv[0];
+ struct hci_command *c = NULL;
+ int s, e, help;
+
+ help = 0;
+ if (strcasecmp(cmd, "help") == 0) {
+ argc --;
+ argv ++;
+
+ if (argc <= 0) {
+ fprintf(stdout, "Supported commands:\n");
+ print_hci_command(link_control_commands);
+ print_hci_command(link_policy_commands);
+ print_hci_command(host_controller_baseband_commands);
+ print_hci_command(info_commands);
+ print_hci_command(status_commands);
+ print_hci_command(node_commands);
+ fprintf(stdout, "\nFor more information use " \
+ "'help command'\n");
+
+ return (OK);
+ }
+
+ help = 1;
+ cmd = argv[0];
+ }
+
+ c = find_hci_command(cmd, link_control_commands);
+ if (c != NULL)
+ goto execute;
+
+ c = find_hci_command(cmd, link_policy_commands);
+ if (c != NULL)
+ goto execute;
+
+ c = find_hci_command(cmd, host_controller_baseband_commands);
+ if (c != NULL)
+ goto execute;
+
+ c = find_hci_command(cmd, info_commands);
+ if (c != NULL)
+ goto execute;
+
+ c = find_hci_command(cmd, status_commands);
+ if (c != NULL)
+ goto execute;
+
+ c = find_hci_command(cmd, node_commands);
+ if (c == NULL) {
+ fprintf(stdout, "Unknown command: \"%s\"\n", cmd);
+ return (ERROR);
+ }
+execute:
+ if (!help) {
+ s = socket_open(node);
+ e = (c->handler)(s, -- argc, ++ argv);
+ close(s);
+ } else
+ e = USAGE;
+
+ switch (e) {
+ case OK:
+ case FAILED:
+ break;
+
+ case ERROR:
+ fprintf(stdout, "Could not execute command \"%s\". %s\n",
+ cmd, strerror(errno));
+ break;
+
+ case USAGE:
+ fprintf(stdout, "Usage: %s\n%s\n", c->command, c->description);
+ break;
+
+ default: assert(0); break;
+ }
+
+
+ return (e);
+} /* do_hci_command */
+
+/* Try to find command in specified category */
+static struct hci_command *
+find_hci_command(char const *command, struct hci_command *category)
+{
+ struct hci_command *c = NULL;
+
+ for (c = category; c->command != NULL; c++) {
+ char *c_end = strchr(c->command, ' ');
+
+ if (c_end != NULL) {
+ int len = c_end - c->command;
+
+ if (strncasecmp(command, c->command, len) == 0)
+ return (c);
+ } else if (strcasecmp(command, c->command) == 0)
+ return (c);
+ }
+
+ return (NULL);
+} /* find_hci_command */
+
+/* Find all HCI nodes */
+static int
+find_hci_nodes(struct nodeinfo** nodes)
+{
+ struct ng_btsocket_hci_raw_node_list_names r;
+ struct sockaddr_hci addr;
+ int s;
+ const char * node = "ubt0hci";
+
+ r.num_names = MAX_NODE_NUM;
+ r.names = (struct nodeinfo*)calloc(MAX_NODE_NUM, sizeof(struct nodeinfo));
+ if (r.names == NULL)
+ err(8, "Could not allocate memory");
+
+ s = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_HCI);
+ if (s < 0)
+ err(9, "Could not create socket");
+
+ memset(&addr, 0, sizeof(addr));
+ addr.hci_len = sizeof(addr);
+ addr.hci_family = AF_BLUETOOTH;
+ strncpy(addr.hci_node, node, sizeof(addr.hci_node));
+ if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0)
+ err(10, "Could not bind socket");
+
+ if (ioctl(s, SIOC_HCI_RAW_NODE_LIST_NAMES, &r, sizeof(r)) < 0)
+ err(11, "Could not get list of HCI nodes");
+
+ close(s);
+
+ *nodes = r.names;
+
+ return (r.num_names);
+} /* find_hci_nodes */
+
+/* Print commands in specified category */
+static void
+print_hci_command(struct hci_command *category)
+{
+ struct hci_command *c = NULL;
+
+ for (c = category; c->command != NULL; c++)
+ fprintf(stdout, "\t%s\n", c->command);
+} /* print_hci_command */
+
+/* Usage */
+static void
+usage(void)
+{
+ fprintf(stdout, "Usage: hccontrol [-hN] [-n HCI_node_name] cmd [p1] [..]\n");
+ exit(255);
+} /* usage */
+
diff --git a/usr.sbin/bluetooth/hccontrol/hccontrol.h b/usr.sbin/bluetooth/hccontrol/hccontrol.h
new file mode 100644
index 0000000..cd56ebf
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/hccontrol.h
@@ -0,0 +1,79 @@
+/*
+ * hccontrol.h
+ *
+ * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: hccontrol.h,v 1.2 2003/05/19 17:29:29 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _HCCONTROL_H_
+#define _HCCONTROL_H_
+
+#define OK 0 /* everything was OK */
+#define ERROR 1 /* could not execute command */
+#define FAILED 2 /* error was reported */
+#define USAGE 3 /* invalid parameters */
+
+#define MAX_NODE_NUM 16 /* max number of nodes */
+
+struct hci_command {
+ char const *command;
+ char const *description;
+ int (*handler)(int, int, char **);
+};
+
+extern int timeout;
+extern int verbose;
+extern struct hci_command link_control_commands[];
+extern struct hci_command link_policy_commands[];
+extern struct hci_command host_controller_baseband_commands[];
+extern struct hci_command info_commands[];
+extern struct hci_command status_commands[];
+extern struct hci_command node_commands[];
+
+int hci_request (int, int, char const *, int, char *, int *);
+int hci_simple_request (int, int, char *, int *);
+int hci_send (int, char const *, int);
+int hci_recv (int, char *, int *);
+
+char const * hci_link2str (int);
+char const * hci_pin2str (int);
+char const * hci_scan2str (int);
+char const * hci_encrypt2str (int, int);
+char const * hci_coding2str (int);
+char const * hci_vdata2str (int);
+char const * hci_hmode2str (int, char *, int);
+char const * hci_ver2str (int);
+char const * hci_lmpver2str (int);
+char const * hci_manufacturer2str(int);
+char const * hci_features2str (uint8_t *, char *, int);
+char const * hci_cc2str (int);
+char const * hci_con_state2str (int);
+char const * hci_status2str (int);
+char const * hci_bdaddr2str (bdaddr_t const *);
+
+#endif /* _HCCONTROL_H_ */
+
diff --git a/usr.sbin/bluetooth/hccontrol/host_controller_baseband.c b/usr.sbin/bluetooth/hccontrol/host_controller_baseband.c
new file mode 100644
index 0000000..38f77eb
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/host_controller_baseband.c
@@ -0,0 +1,1877 @@
+/*
+ * host_controller_baseband.c
+ *
+ * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: host_controller_baseband.c,v 1.4 2003/08/18 19:19:53 max Exp $
+ * $FreeBSD$
+ */
+
+#include <bluetooth.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include "hccontrol.h"
+
+/* Convert hex ASCII to int4 */
+static int
+hci_hexa2int4(const char *a)
+{
+ if ('0' <= *a && *a <= '9')
+ return (*a - '0');
+
+ if ('A' <= *a && *a <= 'F')
+ return (*a - 'A' + 0xa);
+
+ if ('a' <= *a && *a <= 'f')
+ return (*a - 'a' + 0xa);
+
+ return (-1);
+}
+
+/* Convert hex ASCII to int8 */
+static int
+hci_hexa2int8(const char *a)
+{
+ int hi = hci_hexa2int4(a);
+ int lo = hci_hexa2int4(a + 1);
+
+ if (hi < 0 || lo < 0)
+ return (-1);
+
+ return ((hi << 4) | lo);
+}
+
+/* Convert ascii hex string to the uint8_t[] */
+static int
+hci_hexstring2array(char const *s, uint8_t *a, int asize)
+{
+ int i, l, b;
+
+ l = strlen(s) / 2;
+ if (l > asize)
+ l = asize;
+
+ for (i = 0; i < l; i++) {
+ b = hci_hexa2int8(s + i * 2);
+ if (b < 0)
+ return (-1);
+
+ a[i] = (b & 0xff);
+ }
+
+ return (0);
+}
+
+/* Send RESET to the unit */
+static int
+hci_reset(int s, int argc, char **argv)
+{
+ ng_hci_status_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_RESET), (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_reset */
+
+/* Send Read_PIN_Type command to the unit */
+static int
+hci_read_pin_type(int s, int argc, char **argv)
+{
+ ng_hci_read_pin_type_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_PIN_TYPE),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "PIN type: %s [%#02x]\n",
+ hci_pin2str(rp.pin_type), rp.pin_type);
+
+ return (OK);
+} /* hci_read_pin_type */
+
+/* Send Write_PIN_Type command to the unit */
+static int
+hci_write_pin_type(int s, int argc, char **argv)
+{
+ ng_hci_write_pin_type_cp cp;
+ ng_hci_write_pin_type_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 1)
+ return (USAGE);
+
+ cp.pin_type = (uint8_t) n;
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_PIN_TYPE),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp , &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_pin_type */
+
+/* Send Read_Stored_Link_Key command to the unit */
+static int
+hci_read_stored_link_key(int s, int argc, char **argv)
+{
+ struct {
+ ng_hci_cmd_pkt_t hdr;
+ ng_hci_read_stored_link_key_cp cp;
+ } __attribute__ ((packed)) cmd;
+
+ struct {
+ ng_hci_event_pkt_t hdr;
+ union {
+ ng_hci_command_compl_ep cc;
+ ng_hci_return_link_keys_ep key;
+ uint8_t b[NG_HCI_EVENT_PKT_SIZE];
+ } ep;
+ } __attribute__ ((packed)) event;
+
+ int n, n1;
+
+ /* Send command */
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.hdr.type = NG_HCI_CMD_PKT;
+ cmd.hdr.opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_STORED_LINK_KEY));
+ cmd.hdr.length = sizeof(cmd.cp);
+
+ switch (argc) {
+ case 1:
+ /* parse BD_ADDR */
+ if (!bt_aton(argv[0], &cmd.cp.bdaddr)) {
+ struct hostent *he = NULL;
+
+ if ((he = bt_gethostbyname(argv[0])) == NULL)
+ return (USAGE);
+
+ memcpy(&cmd.cp.bdaddr, he->h_addr, sizeof(cmd.cp.bdaddr));
+ }
+ break;
+
+ default:
+ cmd.cp.read_all = 1;
+ break;
+ }
+
+ if (hci_send(s, (char const *) &cmd, sizeof(cmd)) != OK)
+ return (ERROR);
+
+ /* Receive events */
+again:
+ memset(&event, 0, sizeof(event));
+ n = sizeof(event);
+ if (hci_recv(s, (char *) &event, &n) != OK)
+ return (ERROR);
+
+ if (n <= sizeof(event.hdr)) {
+ errno = EMSGSIZE;
+ return (ERROR);
+ }
+
+ if (event.hdr.type != NG_HCI_EVENT_PKT) {
+ errno = EIO;
+ return (ERROR);
+ }
+
+ /* Parse event */
+ switch (event.hdr.event) {
+ case NG_HCI_EVENT_COMMAND_COMPL: {
+ ng_hci_read_stored_link_key_rp *rp = NULL;
+
+ if (event.ep.cc.opcode == 0x0000 ||
+ event.ep.cc.opcode != cmd.hdr.opcode)
+ goto again;
+
+ rp = (ng_hci_read_stored_link_key_rp *)(event.ep.b +
+ sizeof(event.ep.cc));
+
+ fprintf(stdout, "Complete: Status: %s [%#x]\n",
+ hci_status2str(rp->status), rp->status);
+ fprintf(stdout, "Maximum Number of keys: %d\n",
+ le16toh(rp->max_num_keys));
+ fprintf(stdout, "Number of keys read: %d\n",
+ le16toh(rp->num_keys_read));
+ } break;
+
+ case NG_HCI_EVENT_RETURN_LINK_KEYS: {
+ struct _key {
+ bdaddr_t bdaddr;
+ uint8_t key[NG_HCI_KEY_SIZE];
+ } __attribute__ ((packed)) *k = NULL;
+
+ fprintf(stdout, "Event: Number of keys: %d\n",
+ event.ep.key.num_keys);
+
+ k = (struct _key *)(event.ep.b + sizeof(event.ep.key));
+ for (n = 0; n < event.ep.key.num_keys; n++) {
+ fprintf(stdout, "\t%d: %s ",
+ n + 1, hci_bdaddr2str(&k->bdaddr));
+
+ for (n1 = 0; n1 < sizeof(k->key); n1++)
+ fprintf(stdout, "%02x", k->key[n1]);
+ fprintf(stdout, "\n");
+
+ k ++;
+ }
+
+ goto again;
+
+ } break;
+
+ default:
+ goto again;
+ }
+
+ return (OK);
+} /* hci_read_store_link_key */
+
+/* Send Write_Stored_Link_Key command to the unit */
+static int
+hci_write_stored_link_key(int s, int argc, char **argv)
+{
+ struct {
+ ng_hci_write_stored_link_key_cp p;
+ bdaddr_t bdaddr;
+ uint8_t key[NG_HCI_KEY_SIZE];
+ } cp;
+ ng_hci_write_stored_link_key_rp rp;
+ int32_t n;
+
+ memset(&cp, 0, sizeof(cp));
+
+ switch (argc) {
+ case 2:
+ cp.p.num_keys_write = 1;
+
+ /* parse BD_ADDR */
+ if (!bt_aton(argv[0], &cp.bdaddr)) {
+ struct hostent *he = NULL;
+
+ if ((he = bt_gethostbyname(argv[0])) == NULL)
+ return (USAGE);
+
+ memcpy(&cp.bdaddr, he->h_addr, sizeof(cp.bdaddr));
+ }
+
+ /* parse key */
+ if (hci_hexstring2array(argv[1], cp.key, sizeof(cp.key)) < 0)
+ return (USAGE);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_STORED_LINK_KEY),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Number of keys written: %d\n", rp.num_keys_written);
+
+ return (OK);
+} /* hci_write_stored_link_key */
+
+
+/* Send Delete_Stored_Link_Key command to the unit */
+static int
+hci_delete_stored_link_key(int s, int argc, char **argv)
+{
+ ng_hci_delete_stored_link_key_cp cp;
+ ng_hci_delete_stored_link_key_rp rp;
+ int32_t n;
+
+ memset(&cp, 0, sizeof(cp));
+
+ switch (argc) {
+ case 1:
+ /* parse BD_ADDR */
+ if (!bt_aton(argv[0], &cp.bdaddr)) {
+ struct hostent *he = NULL;
+
+ if ((he = bt_gethostbyname(argv[0])) == NULL)
+ return (USAGE);
+
+ memcpy(&cp.bdaddr, he->h_addr, sizeof(cp.bdaddr));
+ }
+ break;
+
+ default:
+ cp.delete_all = 1;
+ break;
+ }
+
+ /* send command */
+ n = sizeof(cp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_DELETE_STORED_LINK_KEY),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Number of keys deleted: %d\n", rp.num_keys_deleted);
+
+ return (OK);
+} /* hci_delete_stored_link_key */
+
+/* Send Change_Local_Name command to the unit */
+static int
+hci_change_local_name(int s, int argc, char **argv)
+{
+ ng_hci_change_local_name_cp cp;
+ ng_hci_change_local_name_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ snprintf(cp.name, sizeof(cp.name), "%s", argv[0]);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_CHANGE_LOCAL_NAME),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_change_local_name */
+
+/* Send Read_Local_Name command to the unit */
+static int
+hci_read_local_name(int s, int argc, char **argv)
+{
+ ng_hci_read_local_name_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_LOCAL_NAME),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Local name: %s\n", rp.name);
+
+ return (OK);
+} /* hci_read_local_name */
+
+/* Send Read_Connection_Accept_Timeout to the unit */
+static int
+hci_read_connection_accept_timeout(int s, int argc, char **argv)
+{
+ ng_hci_read_con_accept_timo_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_CON_ACCEPT_TIMO),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ rp.timeout = le16toh(rp.timeout);
+ fprintf(stdout, "Connection accept timeout: %.2f msec [%d slots]\n",
+ rp.timeout * 0.625, rp.timeout);
+
+ return (OK);
+} /* hci_read_connection_accept_timeout */
+
+/* Send Write_Connection_Accept_Timeout to the unit */
+static int
+hci_write_connection_accept_timeout(int s, int argc, char **argv)
+{
+ ng_hci_write_con_accept_timo_cp cp;
+ ng_hci_write_con_accept_timo_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 1 || n > 0xb540)
+ return (USAGE);
+
+ cp.timeout = (uint16_t) n;
+ cp.timeout = htole16(cp.timeout);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_CON_ACCEPT_TIMO),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_connection_accept_timeout */
+
+/* Send Read_Page_Timeout command to the unit */
+static int
+hci_read_page_timeout(int s, int argc, char **argv)
+{
+ ng_hci_read_page_timo_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_PAGE_TIMO),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ rp.timeout = le16toh(rp.timeout);
+ fprintf(stdout, "Page timeout: %.2f msec [%d slots]\n",
+ rp.timeout * 0.625, rp.timeout);
+
+ return (OK);
+} /* hci_read_page_timeoout */
+
+/* Send Write_Page_Timeout command to the unit */
+static int
+hci_write_page_timeout(int s, int argc, char **argv)
+{
+ ng_hci_write_page_timo_cp cp;
+ ng_hci_write_page_timo_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 1 || n > 0xffff)
+ return (USAGE);
+
+ cp.timeout = (uint16_t) n;
+ cp.timeout = htole16(cp.timeout);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_PAGE_TIMO),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_page_timeout */
+
+/* Send Read_Scan_Enable command to the unit */
+static int
+hci_read_scan_enable(int s, int argc, char **argv)
+{
+ ng_hci_read_scan_enable_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_SCAN_ENABLE),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Scan enable: %s [%#02x]\n",
+ hci_scan2str(rp.scan_enable), rp.scan_enable);
+
+ return (OK);
+} /* hci_read_scan_enable */
+
+/* Send Write_Scan_Enable command to the unit */
+static int
+hci_write_scan_enable(int s, int argc, char **argv)
+{
+ ng_hci_write_scan_enable_cp cp;
+ ng_hci_write_scan_enable_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 3)
+ return (USAGE);
+
+ cp.scan_enable = (uint8_t) n;
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_SCAN_ENABLE),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_scan_enable */
+
+/* Send Read_Page_Scan_Activity command to the unit */
+static int
+hci_read_page_scan_activity(int s, int argc, char **argv)
+{
+ ng_hci_read_page_scan_activity_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_PAGE_SCAN_ACTIVITY),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ rp.page_scan_interval = le16toh(rp.page_scan_interval);
+ rp.page_scan_window = le16toh(rp.page_scan_window);
+
+ fprintf(stdout, "Page Scan Interval: %.2f msec [%d slots]\n",
+ rp.page_scan_interval * 0.625, rp.page_scan_interval);
+ fprintf(stdout, "Page Scan Window: %.2f msec [%d slots]\n",
+ rp.page_scan_window * 0.625, rp.page_scan_window);
+
+ return (OK);
+} /* hci_read_page_scan_activity */
+
+/* Send Write_Page_Scan_Activity command to the unit */
+static int
+hci_write_page_scan_activity(int s, int argc, char **argv)
+{
+ ng_hci_write_page_scan_activity_cp cp;
+ ng_hci_write_page_scan_activity_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 2:
+ /* page scan interval */
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 0x12 || n > 0x1000)
+ return (USAGE);
+
+ cp.page_scan_interval = (uint16_t) n;
+
+ /* page scan window */
+ if (sscanf(argv[1], "%d", &n) != 1 || n < 0x12 || n > 0x1000)
+ return (USAGE);
+
+ cp.page_scan_window = (uint16_t) n;
+
+ if (cp.page_scan_window > cp.page_scan_interval)
+ return (USAGE);
+
+ cp.page_scan_interval = htole16(cp.page_scan_interval);
+ cp.page_scan_window = htole16(cp.page_scan_window);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_PAGE_SCAN_ACTIVITY),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_page_scan_activity */
+
+/* Send Read_Inquiry_Scan_Activity command to the unit */
+static int
+hci_read_inquiry_scan_activity(int s, int argc, char **argv)
+{
+ ng_hci_read_inquiry_scan_activity_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_INQUIRY_SCAN_ACTIVITY),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ rp.inquiry_scan_interval = le16toh(rp.inquiry_scan_interval);
+ rp.inquiry_scan_window = le16toh(rp.inquiry_scan_window);
+
+ fprintf(stdout, "Inquiry Scan Interval: %.2f msec [%d slots]\n",
+ rp.inquiry_scan_interval * 0.625, rp.inquiry_scan_interval);
+ fprintf(stdout, "Inquiry Scan Window: %.2f msec [%d slots]\n",
+ rp.inquiry_scan_window * 0.625, rp.inquiry_scan_interval);
+
+ return (OK);
+} /* hci_read_inquiry_scan_activity */
+
+/* Send Write_Inquiry_Scan_Activity command to the unit */
+static int
+hci_write_inquiry_scan_activity(int s, int argc, char **argv)
+{
+ ng_hci_write_inquiry_scan_activity_cp cp;
+ ng_hci_write_inquiry_scan_activity_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 2:
+ /* inquiry scan interval */
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 0x12 || n > 0x1000)
+ return (USAGE);
+
+ cp.inquiry_scan_interval = (uint16_t) n;
+
+ /* inquiry scan window */
+ if (sscanf(argv[1], "%d", &n) != 1 || n < 0x12 || n > 0x1000)
+ return (USAGE);
+
+ cp.inquiry_scan_window = (uint16_t) n;
+
+ if (cp.inquiry_scan_window > cp.inquiry_scan_interval)
+ return (USAGE);
+
+ cp.inquiry_scan_interval =
+ htole16(cp.inquiry_scan_interval);
+ cp.inquiry_scan_window = htole16(cp.inquiry_scan_window);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_INQUIRY_SCAN_ACTIVITY),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_inquiry_scan_activity */
+
+/* Send Read_Authentication_Enable command to the unit */
+static int
+hci_read_authentication_enable(int s, int argc, char **argv)
+{
+ ng_hci_read_auth_enable_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_AUTH_ENABLE),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Authentication Enable: %s [%d]\n",
+ rp.auth_enable? "Enabled" : "Disabled", rp.auth_enable);
+
+ return (OK);
+} /* hci_read_authentication_enable */
+
+/* Send Write_Authentication_Enable command to the unit */
+static int
+hci_write_authentication_enable(int s, int argc, char **argv)
+{
+ ng_hci_write_auth_enable_cp cp;
+ ng_hci_write_auth_enable_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 1)
+ return (USAGE);
+
+ cp.auth_enable = (uint8_t) n;
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_AUTH_ENABLE),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_authentication_enable */
+
+/* Send Read_Encryption_Mode command to the unit */
+static int
+hci_read_encryption_mode(int s, int argc, char **argv)
+{
+ ng_hci_read_encryption_mode_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_ENCRYPTION_MODE),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Encryption mode: %s [%#02x]\n",
+ hci_encrypt2str(rp.encryption_mode, 0), rp.encryption_mode);
+
+ return (OK);
+} /* hci_read_encryption_mode */
+
+/* Send Write_Encryption_Mode command to the unit */
+static int
+hci_write_encryption_mode(int s, int argc, char **argv)
+{
+ ng_hci_write_encryption_mode_cp cp;
+ ng_hci_write_encryption_mode_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 2)
+ return (USAGE);
+
+ cp.encryption_mode = (uint8_t) n;
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_ENCRYPTION_MODE),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_encryption_mode */
+
+/* Send Read_Class_Of_Device command to the unit */
+static int
+hci_read_class_of_device(int s, int argc, char **argv)
+{
+ ng_hci_read_unit_class_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_UNIT_CLASS),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Class: %02x:%02x:%02x\n",
+ rp.uclass[2], rp.uclass[1], rp.uclass[0]);
+
+ return (0);
+} /* hci_read_class_of_device */
+
+/* Send Write_Class_Of_Device command to the unit */
+static int
+hci_write_class_of_device(int s, int argc, char **argv)
+{
+ ng_hci_write_unit_class_cp cp;
+ ng_hci_write_unit_class_rp rp;
+ int n0, n1, n2;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ if (sscanf(argv[0], "%x:%x:%x", &n2, &n1, &n0) != 3)
+ return (USAGE);
+
+ cp.uclass[0] = (n0 & 0xff);
+ cp.uclass[1] = (n1 & 0xff);
+ cp.uclass[2] = (n2 & 0xff);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n0 = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_UNIT_CLASS),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n0) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_class_of_device */
+
+/* Send Read_Voice_Settings command to the unit */
+static int
+hci_read_voice_settings(int s, int argc, char **argv)
+{
+ ng_hci_read_voice_settings_rp rp;
+ int n,
+ input_coding,
+ input_data_format,
+ input_sample_size;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_VOICE_SETTINGS),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ rp.settings = le16toh(rp.settings);
+
+ input_coding = (rp.settings & 0x0300) >> 8;
+ input_data_format = (rp.settings & 0x00c0) >> 6;
+ input_sample_size = (rp.settings & 0x0020) >> 5;
+
+ fprintf(stdout, "Voice settings: %#04x\n", rp.settings);
+ fprintf(stdout, "Input coding: %s [%d]\n",
+ hci_coding2str(input_coding), input_coding);
+ fprintf(stdout, "Input data format: %s [%d]\n",
+ hci_vdata2str(input_data_format), input_data_format);
+
+ if (input_coding == 0x00) /* Only for Linear PCM */
+ fprintf(stdout, "Input sample size: %d bit [%d]\n",
+ input_sample_size? 16 : 8, input_sample_size);
+
+ return (OK);
+} /* hci_read_voice_settings */
+
+/* Send Write_Voice_Settings command to the unit */
+static int
+hci_write_voice_settings(int s, int argc, char **argv)
+{
+ ng_hci_write_voice_settings_cp cp;
+ ng_hci_write_voice_settings_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ if (sscanf(argv[0], "%x", &n) != 1)
+ return (USAGE);
+
+ cp.settings = (uint16_t) n;
+ cp.settings = htole16(cp.settings);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_VOICE_SETTINGS),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_voice_settings */
+
+/* Send Read_Number_Broadcast_Restransmissions */
+static int
+hci_read_number_broadcast_retransmissions(int s, int argc, char **argv)
+{
+ ng_hci_read_num_broadcast_retrans_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_NUM_BROADCAST_RETRANS),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Number of broadcast retransmissions: %d\n",
+ rp.counter);
+
+ return (OK);
+} /* hci_read_number_broadcast_retransmissions */
+
+/* Send Write_Number_Broadcast_Restransmissions */
+static int
+hci_write_number_broadcast_retransmissions(int s, int argc, char **argv)
+{
+ ng_hci_write_num_broadcast_retrans_cp cp;
+ ng_hci_write_num_broadcast_retrans_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 0xff)
+ return (USAGE);
+
+ cp.counter = (uint8_t) n;
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_NUM_BROADCAST_RETRANS),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_number_broadcast_retransmissions */
+
+/* Send Read_Hold_Mode_Activity command to the unit */
+static int
+hci_read_hold_mode_activity(int s, int argc, char **argv)
+{
+ ng_hci_read_hold_mode_activity_rp rp;
+ int n;
+ char buffer[1024];
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_HOLD_MODE_ACTIVITY),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Hold Mode Activities: %#02x\n", rp.hold_mode_activity);
+ if (rp.hold_mode_activity == 0)
+ fprintf(stdout, "Maintain current Power State");
+ else
+ fprintf(stdout, "%s", hci_hmode2str(rp.hold_mode_activity,
+ buffer, sizeof(buffer)));
+
+ fprintf(stdout, "\n");
+
+ return (OK);
+} /* hci_read_hold_mode_activity */
+
+/* Send Write_Hold_Mode_Activity command to the unit */
+static int
+hci_write_hold_mode_activity(int s, int argc, char **argv)
+{
+ ng_hci_write_hold_mode_activity_cp cp;
+ ng_hci_write_hold_mode_activity_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 4)
+ return (USAGE);
+
+ cp.hold_mode_activity = (uint8_t) n;
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_HOLD_MODE_ACTIVITY),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_hold_mode_activity */
+
+/* Send Read_SCO_Flow_Control_Enable command to the unit */
+static int
+hci_read_sco_flow_control_enable(int s, int argc, char **argv)
+{
+ ng_hci_read_sco_flow_control_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_SCO_FLOW_CONTROL),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "SCO flow control %s [%d]\n",
+ rp.flow_control? "enabled" : "disabled", rp.flow_control);
+
+ return (OK);
+} /* hci_read_sco_flow_control_enable */
+
+/* Send Write_SCO_Flow_Control_Enable command to the unit */
+static int
+hci_write_sco_flow_control_enable(int s, int argc, char **argv)
+{
+ ng_hci_write_sco_flow_control_cp cp;
+ ng_hci_write_sco_flow_control_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 1)
+ return (USAGE);
+
+ cp.flow_control = (uint8_t) n;
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_SCO_FLOW_CONTROL),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_sco_flow_control_enable */
+
+/* Send Read_Link_Supervision_Timeout command to the unit */
+static int
+hci_read_link_supervision_timeout(int s, int argc, char **argv)
+{
+ ng_hci_read_link_supervision_timo_cp cp;
+ ng_hci_read_link_supervision_timo_rp rp;
+ int n;
+
+ switch (argc) {
+ case 1:
+ /* connection handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (uint16_t) (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_LINK_SUPERVISION_TIMO),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ rp.timeout = le16toh(rp.timeout);
+
+ fprintf(stdout, "Connection handle: %d\n", le16toh(rp.con_handle));
+ fprintf(stdout, "Link supervision timeout: %.2f msec [%d slots]\n",
+ rp.timeout * 0.625, rp.timeout);
+
+ return (OK);
+} /* hci_read_link_supervision_timeout */
+
+/* Send Write_Link_Supervision_Timeout command to the unit */
+static int
+hci_write_link_supervision_timeout(int s, int argc, char **argv)
+{
+ ng_hci_write_link_supervision_timo_cp cp;
+ ng_hci_write_link_supervision_timo_rp rp;
+ int n;
+
+ switch (argc) {
+ case 2:
+ /* connection handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (uint16_t) (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+
+ /* link supervision timeout */
+ if (sscanf(argv[1], "%d", &n) != 1 || n < 0 || n > 0xffff)
+ return (USAGE);
+
+ cp.timeout = (uint16_t) (n & 0x0fff);
+ cp.timeout = htole16(cp.timeout);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_LINK_SUPERVISION_TIMO),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_link_supervision_timeout */
+
+/* Send Read_Page_Scan_Period_Mode command to the unit */
+static int
+hci_read_page_scan_period_mode(int s, int argc, char **argv)
+{
+ ng_hci_read_page_scan_period_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_PAGE_SCAN_PERIOD),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Page scan period mode: %#02x\n",
+ rp.page_scan_period_mode);
+
+ return (OK);
+} /* hci_read_page_scan_period_mode */
+
+/* Send Write_Page_Scan_Period_Mode command to the unit */
+static int
+hci_write_page_scan_period_mode(int s, int argc, char **argv)
+{
+ ng_hci_write_page_scan_period_cp cp;
+ ng_hci_write_page_scan_period_rp rp;
+ int n;
+
+ /* parse command arguments */
+ switch (argc) {
+ case 1:
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 2)
+ return (USAGE);
+
+ cp.page_scan_period_mode = (n & 0xff);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_PAGE_SCAN_PERIOD),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_page_scan_period_mode */
+
+/* Send Read_Page_Scan_Mode command to the unit */
+static int
+hci_read_page_scan_mode(int s, int argc, char **argv)
+{
+ ng_hci_read_page_scan_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_READ_PAGE_SCAN),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Page scan mode: %#02x\n", rp.page_scan_mode);
+
+ return (OK);
+} /* hci_read_page_scan_mode */
+
+/* Send Write_Page_Scan_Mode command to the unit */
+static int
+hci_write_page_scan_mode(int s, int argc, char **argv)
+{
+ ng_hci_write_page_scan_cp cp;
+ ng_hci_write_page_scan_rp rp;
+ int n;
+
+ /* parse command arguments */
+ switch (argc) {
+ case 1:
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 3)
+ return (USAGE);
+
+ cp.page_scan_mode = (n & 0xff);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_HC_BASEBAND,
+ NG_HCI_OCF_WRITE_PAGE_SCAN),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_page_scan_mode */
+
+struct hci_command host_controller_baseband_commands[] = {
+{
+"reset",
+"\nThe Reset command will reset the Host Controller and the Link Manager.\n" \
+"After the reset is completed, the current operational state will be lost,\n" \
+"the Bluetooth unit will enter standby mode and the Host Controller will\n" \
+"automatically revert to the default values for the parameters for which\n" \
+"default values are defined in the specification.",
+&hci_reset
+},
+{
+"read_pin_type",
+"\nThe Read_PIN_Type command is used for the Host to read whether the Link\n" \
+"Manager assumes that the Host supports variable PIN codes only a fixed PIN\n" \
+"code.",
+&hci_read_pin_type
+},
+{
+"write_pin_type <pin_type>",
+"\nThe Write_PIN_Type command is used for the Host to write to the Host\n" \
+"Controller whether the Host supports variable PIN codes or only a fixed PIN\n"\
+"code.\n\n" \
+"\t<pin_type> - dd; 0 - Variable; 1 - Fixed",
+&hci_write_pin_type
+},
+{
+"read_stored_link_key [<BD_ADDR>]",
+"\nThe Read_Stored_Link_Key command provides the ability to read one or\n" \
+"more link keys stored in the Bluetooth Host Controller. The Bluetooth Host\n" \
+"Controller can store a limited number of link keys for other Bluetooth\n" \
+"devices.\n\n" \
+"\t<BD_ADDR> - xx:xx:xx:xx:xx:xx BD_ADDR or name",
+&hci_read_stored_link_key
+},
+{
+"write_stored_link_key <BD_ADDR> <key>",
+"\nThe Write_Stored_Link_Key command provides the ability to write one\n" \
+"or more link keys to be stored in the Bluetooth Host Controller. The\n" \
+"Bluetooth Host Controller can store a limited number of link keys for other\n"\
+"Bluetooth devices. If no additional space is available in the Bluetooth\n"\
+"Host Controller then no additional link keys will be stored.\n\n" \
+"\t<BD_ADDR> - xx:xx:xx:xx:xx:xx BD_ADDR or name\n" \
+"\t<key> - xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx up to 16 bytes link key",
+&hci_write_stored_link_key
+},
+{
+"delete_stored_link_key [<BD_ADDR>]",
+"\nThe Delete_Stored_Link_Key command provides the ability to remove one\n" \
+"or more of the link keys stored in the Bluetooth Host Controller. The\n" \
+"Bluetooth Host Controller can store a limited number of link keys for other\n"\
+"Bluetooth devices.\n\n" \
+"\t<BD_ADDR> - xx:xx:xx:xx:xx:xx BD_ADDR or name",
+&hci_delete_stored_link_key
+},
+{
+"change_local_name <name>",
+"\nThe Change_Local_Name command provides the ability to modify the user\n" \
+"friendly name for the Bluetooth unit.\n\n" \
+"\t<name> - string",
+&hci_change_local_name
+},
+{
+"read_local_name",
+"\nThe Read_Local_Name command provides the ability to read the\n" \
+"stored user-friendly name for the Bluetooth unit.",
+&hci_read_local_name
+},
+{
+"read_connection_accept_timeout",
+"\nThis command will read the value for the Connection_Accept_Timeout\n" \
+"configuration parameter. The Connection_Accept_Timeout configuration\n" \
+"parameter allows the Bluetooth hardware to automatically deny a\n" \
+"connection request after a specified time period has occurred and\n" \
+"the new connection is not accepted. Connection Accept Timeout\n" \
+"measured in Number of Baseband slots.",
+&hci_read_connection_accept_timeout
+},
+{
+"write_connection_accept_timeout <timeout>",
+"\nThis command will write the value for the Connection_Accept_Timeout\n" \
+"configuration parameter.\n\n" \
+"\t<timeout> - dddd; measured in number of baseband slots.",
+&hci_write_connection_accept_timeout
+},
+{
+"read_page_timeout",
+"\nThis command will read the value for the Page_Timeout configuration\n" \
+"parameter. The Page_Timeout configuration parameter defines the\n" \
+"maximum time the local Link Manager will wait for a baseband page\n" \
+"response from the remote unit at a locally initiated connection\n" \
+"attempt. Page Timeout measured in Number of Baseband slots.",
+&hci_read_page_timeout
+},
+{
+"write_page_timeout <timeout>",
+"\nThis command will write the value for the Page_Timeout configuration\n" \
+"parameter.\n\n" \
+"\t<timeout> - dddd; measured in number of baseband slots.",
+&hci_write_page_timeout
+},
+{
+"read_scan_enable",
+"\nThis command will read the value for the Scan_Enable parameter. The\n" \
+"Scan_Enable parameter controls whether or not the Bluetooth uint\n" \
+"will periodically scan for page attempts and/or inquiry requests\n" \
+"from other Bluetooth unit.\n\n" \
+"\t0x00 - No Scans enabled.\n" \
+"\t0x01 - Inquiry Scan enabled. Page Scan disabled.\n" \
+"\t0x02 - Inquiry Scan disabled. Page Scan enabled.\n" \
+"\t0x03 - Inquiry Scan enabled. Page Scan enabled.",
+&hci_read_scan_enable
+},
+{
+"write_scan_enable <scan_enable>",
+"\nThis command will write the value for the Scan_Enable parameter.\n" \
+"The Scan_Enable parameter controls whether or not the Bluetooth\n" \
+"unit will periodically scan for page attempts and/or inquiry\n" \
+"requests from other Bluetooth unit.\n\n" \
+"\t<scan_enable> - dd;\n" \
+"\t0 - No Scans enabled.\n" \
+"\t1 - Inquiry Scan enabled. Page Scan disabled.\n" \
+"\t2 - Inquiry Scan disabled. Page Scan enabled.\n" \
+"\t3 - Inquiry Scan enabled. Page Scan enabled.",
+&hci_write_scan_enable
+},
+{
+"read_page_scan_activity",
+"\nThis command will read the value for Page_Scan_Activity configuration\n" \
+"parameters. The Page_Scan_Interval configuration parameter defines the\n" \
+"amount of time between consecutive page scans. This time interval is \n" \
+"defined from when the Host Controller started its last page scan until\n" \
+"it begins the next page scan. The Page_Scan_Window configuration parameter\n" \
+"defines the amount of time for the duration of the page scan. The\n" \
+"Page_Scan_Window can only be less than or equal to the Page_Scan_Interval.",
+&hci_read_page_scan_activity
+},
+{
+"write_page_scan_activity interval(dddd) window(dddd)",
+"\nThis command will write the value for Page_Scan_Activity configuration\n" \
+"parameter. The Page_Scan_Interval configuration parameter defines the\n" \
+"amount of time between consecutive page scans. This is defined as the time\n" \
+"interval from when the Host Controller started its last page scan until it\n" \
+"begins the next page scan. The Page_Scan_Window configuration parameter\n" \
+"defines the amount of time for the duration of the page scan. \n" \
+"The Page_Scan_Window can only be less than or equal to the Page_Scan_Interval.\n\n" \
+"\t<interval> - Range: 0x0012 -– 0x100, Time = N * 0.625 msec\n" \
+"\t<window> - Range: 0x0012 -– 0x100, Time = N * 0.625 msen",
+&hci_write_page_scan_activity
+},
+{
+"read_inquiry_scan_activity",
+"\nThis command will read the value for Inquiry_Scan_Activity configuration\n" \
+"parameter. The Inquiry_Scan_Interval configuration parameter defines the\n" \
+"amount of time between consecutive inquiry scans. This is defined as the\n" \
+"time interval from when the Host Controller started its last inquiry scan\n" \
+"until it begins the next inquiry scan.",
+&hci_read_inquiry_scan_activity
+},
+{
+"write_inquiry_scan_activity interval(dddd) window(dddd)",
+"\nThis command will write the value for Inquiry_Scan_Activity configuration\n"\
+"parameter. The Inquiry_Scan_Interval configuration parameter defines the\n" \
+"amount of time between consecutive inquiry scans. This is defined as the\n" \
+"time interval from when the Host Controller started its last inquiry scan\n" \
+"until it begins the next inquiry scan. The Inquiry_Scan_Window configuration\n" \
+"parameter defines the amount of time for the duration of the inquiry scan.\n" \
+"The Inquiry_Scan_Window can only be less than or equal to the Inquiry_Scan_Interval.\n\n" \
+"\t<interval> - Range: 0x0012 -– 0x100, Time = N * 0.625 msec\n" \
+"\t<window> - Range: 0x0012 -– 0x100, Time = N * 0.625 msen",
+&hci_write_inquiry_scan_activity
+},
+{
+"read_authentication_enable",
+"\nThis command will read the value for the Authentication_Enable parameter.\n"\
+"The Authentication_Enable parameter controls if the local unit requires\n"\
+"to authenticate the remote unit at connection setup (between the\n" \
+"Create_Connection command or acceptance of an incoming ACL connection\n"\
+"and the corresponding Connection Complete event). At connection setup, only\n"\
+"the unit(s) with the Authentication_Enable parameter enabled will try to\n"\
+"authenticate the other unit.",
+&hci_read_authentication_enable
+},
+{
+"write_authentication_enable enable(0|1)",
+"\nThis command will write the value for the Authentication_Enable parameter.\n"\
+"The Authentication_Enable parameter controls if the local unit requires to\n"\
+"authenticate the remote unit at connection setup (between the\n" \
+"Create_Connection command or acceptance of an incoming ACL connection\n" \
+"and the corresponding Connection Complete event). At connection setup, only\n"\
+"the unit(s) with the Authentication_Enable parameter enabled will try to\n"\
+"authenticate the other unit.",
+&hci_write_authentication_enable
+},
+{
+"read_encryption_mode",
+"\nThis command will read the value for the Encryption_Mode parameter. The\n" \
+"Encryption_Mode parameter controls if the local unit requires encryption\n" \
+"to the remote unit at connection setup (between the Create_Connection\n" \
+"command or acceptance of an incoming ACL connection and the corresponding\n" \
+"Connection Complete event). At connection setup, only the unit(s) with\n" \
+"the Authentication_Enable parameter enabled and Encryption_Mode parameter\n" \
+"enabled will try to encrypt the connection to the other unit.\n\n" \
+"\t<encryption_mode>:\n" \
+"\t0x00 - Encryption disabled.\n" \
+"\t0x01 - Encryption only for point-to-point packets.\n" \
+"\t0x02 - Encryption for both point-to-point and broadcast packets.",
+&hci_read_encryption_mode
+},
+{
+"write_encryption_mode mode(0|1|2)",
+"\tThis command will write the value for the Encryption_Mode parameter.\n" \
+"The Encryption_Mode parameter controls if the local unit requires\n" \
+"encryption to the remote unit at connection setup (between the\n" \
+"Create_Connection command or acceptance of an incoming ACL connection\n" \
+"and the corresponding Connection Complete event). At connection setup,\n" \
+"only the unit(s) with the Authentication_Enable parameter enabled and\n" \
+"Encryption_Mode parameter enabled will try to encrypt the connection to\n" \
+"the other unit.\n\n" \
+"\t<encryption_mode> (dd)\n" \
+"\t0 - Encryption disabled.\n" \
+"\t1 - Encryption only for point-to-point packets.\n" \
+"\t2 - Encryption for both point-to-point and broadcast packets.",
+&hci_write_encryption_mode
+},
+{
+"read_class_of_device",
+"\nThis command will read the value for the Class_of_Device parameter.\n" \
+"The Class_of_Device parameter is used to indicate the capabilities of\n" \
+"the local unit to other units.",
+&hci_read_class_of_device
+},
+{
+"write_class_of_device class(xx:xx:xx)",
+"\nThis command will write the value for the Class_of_Device parameter.\n" \
+"The Class_of_Device parameter is used to indicate the capabilities of \n" \
+"the local unit to other units.\n\n" \
+"\t<class> (xx:xx:xx) - class of device",
+&hci_write_class_of_device
+},
+{
+"read_voice_settings",
+"\nThis command will read the values for the Voice_Setting parameter.\n" \
+"The Voice_Setting parameter controls all the various settings for voice\n" \
+"connections. These settings apply to all voice connections, and cannot be\n" \
+"set for individual voice connections. The Voice_Setting parameter controls\n" \
+"the configuration for voice connections: Input Coding, Air coding format,\n" \
+"input data format, Input sample size, and linear PCM parameter.",
+&hci_read_voice_settings
+},
+{
+"write_voice_settings settings(xxxx)",
+"\nThis command will write the values for the Voice_Setting parameter.\n" \
+"The Voice_Setting parameter controls all the various settings for voice\n" \
+"connections. These settings apply to all voice connections, and cannot be\n" \
+"set for individual voice connections. The Voice_Setting parameter controls\n" \
+"the configuration for voice connections: Input Coding, Air coding format,\n" \
+"input data format, Input sample size, and linear PCM parameter.\n\n" \
+"\t<voice_settings> (xxxx) - voice settings",
+&hci_write_voice_settings
+},
+{
+"read_number_broadcast_retransmissions",
+"\nThis command will read the unit's parameter value for the Number of\n" \
+"Broadcast Retransmissions. Broadcast packets are not acknowledged and are\n" \
+"unreliable.",
+&hci_read_number_broadcast_retransmissions
+},
+{
+"write_number_broadcast_retransmissions count(dd)",
+"\nThis command will write the unit's parameter value for the Number of\n" \
+"Broadcast Retransmissions. Broadcast packets are not acknowledged and are\n" \
+"unreliable.\n\n" \
+"\t<count> (dd) - number of broadcast retransimissions",
+&hci_write_number_broadcast_retransmissions
+},
+{
+"read_hold_mode_activity",
+"\nThis command will read the value for the Hold_Mode_Activity parameter.\n" \
+"The Hold_Mode_Activity value is used to determine what activities should\n" \
+"be suspended when the unit is in hold mode.",
+&hci_read_hold_mode_activity
+},
+{
+"write_hold_mode_activity settings(0|1|2|4)",
+"\nThis command will write the value for the Hold_Mode_Activity parameter.\n" \
+"The Hold_Mode_Activity value is used to determine what activities should\n" \
+"be suspended when the unit is in hold mode.\n\n" \
+"\t<settings> (dd) - bit mask:\n" \
+"\t0 - Maintain current Power State. Default\n" \
+"\t1 - Suspend Page Scan.\n" \
+"\t2 - Suspend Inquiry Scan.\n" \
+"\t4 - Suspend Periodic Inquiries.",
+&hci_write_hold_mode_activity
+},
+{
+"read_sco_flow_control_enable",
+"\nThe Read_SCO_Flow_Control_Enable command provides the ability to read\n" \
+"the SCO_Flow_Control_Enable setting. By using this setting, the Host can\n" \
+"decide if the Host Controller will send Number Of Completed Packets events\n" \
+"for SCO Connection Handles. This setting allows the Host to enable and\n" \
+"disable SCO flow control.",
+&hci_read_sco_flow_control_enable
+},
+{
+"write_sco_flow_control_enable enable(0|1)",
+"\nThe Write_SCO_Flow_Control_Enable command provides the ability to write\n" \
+"the SCO_Flow_Control_Enable setting. By using this setting, the Host can\n" \
+"decide if the Host Controller will send Number Of Completed Packets events\n" \
+"for SCO Connection Handles. This setting allows the Host to enable and\n" \
+"disable SCO flow control. The SCO_Flow_Control_Enable setting can only be\n" \
+"changed if no connections exist.",
+&hci_write_sco_flow_control_enable
+},
+{
+"read_link_supervision_timeout <connection_handle>",
+"\nThis command will read the value for the Link_Supervision_Timeout\n" \
+"parameter for the device. The Link_Supervision_Timeout parameter is used\n" \
+"by the master or slave Bluetooth device to monitor link loss. If, for any\n" \
+"reason, no Baseband packets are received from that Connection Handle for a\n" \
+"duration longer than the Link_Supervision_Timeout, the connection is\n"
+"disconnected.\n\n" \
+"\t<connection_handle> - dddd; connection handle\n",
+&hci_read_link_supervision_timeout
+},
+{
+"write_link_supervision_timeout <connection_handle> <timeout>",
+"\nThis command will write the value for the Link_Supervision_Timeout\n" \
+"parameter for the device. The Link_Supervision_Timeout parameter is used\n" \
+"by the master or slave Bluetooth device to monitor link loss. If, for any\n" \
+"reason, no Baseband packets are received from that connection handle for a\n" \
+"duration longer than the Link_Supervision_Timeout, the connection is\n" \
+"disconnected.\n\n" \
+"\t<connection_handle> - dddd; connection handle\n" \
+"\t<timeout> - dddd; timeout measured in number of baseband slots\n",
+&hci_write_link_supervision_timeout
+},
+{
+"read_page_scan_period_mode",
+"\nThis command is used to read the mandatory Page_Scan_Period_Mode of the\n" \
+"local Bluetooth device. Every time an inquiry response message is sent, the\n"\
+"Bluetooth device will start a timer (T_mandatory_pscan), the value of which\n"\
+"is dependent on the Page_Scan_Period_Mode. As long as this timer has not\n" \
+"expired, the Bluetooth device will use the Page_Scan_Period_Mode for all\n" \
+"following page scans.",
+&hci_read_page_scan_period_mode
+},
+{
+"write_page_scan_period_mode <page_scan_period_mode>",
+"\nThis command is used to write the mandatory Page_Scan_Period_Mode of the\n" \
+"local Bluetooth device. Every time an inquiry response message is sent, the\n"\
+"Bluetooth device will start a timer (T_mandatory_pscan), the value of which\n"\
+"is dependent on the Page_Scan_Period_Mode. As long as this timer has not\n" \
+"expired, the Bluetooth device will use the Page_Scan_Period_Mode for all\n" \
+"following page scans.\n\n" \
+"\t<page_scan_period_mode> - dd; page scan period mode:\n" \
+"\t0x00 - P0 (Default)\n" \
+"\t0x01 - P1\n" \
+"\t0x02 - P2",
+&hci_write_page_scan_period_mode
+},
+{
+"read_page_scan_mode",
+"\nThis command is used to read the default page scan mode of the local\n" \
+"Bluetooth device. The Page_Scan_Mode parameter indicates the page scan mode\n"\
+"that is used for the default page scan. Currently one mandatory page scan\n"\
+"mode and three optional page scan modes are defined. Following an inquiry\n" \
+"response, if the Baseband timer T_mandatory_pscan has not expired, the\n" \
+"mandatory page scan mode must be applied.",
+&hci_read_page_scan_mode
+},
+{
+"write_page_scan_mode <page_scan_mode>",
+"\nThis command is used to write the default page scan mode of the local\n" \
+"Bluetooth device. The Page_Scan_Mode parameter indicates the page scan mode\n"\
+"that is used for the default page scan. Currently, one mandatory page scan\n"\
+"mode and three optional page scan modes are defined. Following an inquiry\n"\
+"response, if the Baseband timer T_mandatory_pscan has not expired, the\n" \
+"mandatory page scan mode must be applied.\n\n" \
+"\t<page_scan_mode> - dd; page scan mode:\n" \
+"\t0x00 - Mandatory Page Scan Mode (Default)\n" \
+"\t0x01 - Optional Page Scan Mode I\n" \
+"\t0x02 - Optional Page Scan Mode II\n" \
+"\t0x03 - Optional Page Scan Mode III",
+&hci_write_page_scan_mode
+},
+{ NULL, }
+};
+
diff --git a/usr.sbin/bluetooth/hccontrol/info.c b/usr.sbin/bluetooth/hccontrol/info.c
new file mode 100644
index 0000000..d7bad36
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/info.c
@@ -0,0 +1,216 @@
+/*
+ * info.c
+ *
+ * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: info.c,v 1.3 2003/08/18 19:19:54 max Exp $
+ * $FreeBSD$
+ */
+
+#include <bluetooth.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include "hccontrol.h"
+
+/* Send Read_Local_Version_Information command to the unit */
+static int
+hci_read_local_version_information(int s, int argc, char **argv)
+{
+ ng_hci_read_local_ver_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_INFO,
+ NG_HCI_OCF_READ_LOCAL_VER), (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ rp.manufacturer = le16toh(rp.manufacturer);
+
+ fprintf(stdout, "HCI version: %s [%#02x]\n",
+ hci_ver2str(rp.hci_version), rp.hci_version);
+ fprintf(stdout, "HCI revision: %#04x\n",
+ le16toh(rp.hci_revision));
+ fprintf(stdout, "LMP version: %s [%#02x]\n",
+ hci_lmpver2str(rp.lmp_version), rp.lmp_version);
+ fprintf(stdout, "LMP sub-version: %#04x\n",
+ le16toh(rp.lmp_subversion));
+ fprintf(stdout, "Manufacturer: %s [%#04x]\n",
+ hci_manufacturer2str(rp.manufacturer), rp.manufacturer);
+
+ return (OK);
+} /* hci_read_local_version_information */
+
+/* Send Read_Local_Supported_Features command to the unit */
+static int
+hci_read_local_supported_features(int s, int argc, char **argv)
+{
+ ng_hci_read_local_features_rp rp;
+ int n;
+ char buffer[1024];
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_INFO,
+ NG_HCI_OCF_READ_LOCAL_FEATURES),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Features: ");
+ for (n = 0; n < sizeof(rp.features); n++)
+ fprintf(stdout, "%#02x ", rp.features[n]);
+ fprintf(stdout, "\n%s\n", hci_features2str(rp.features,
+ buffer, sizeof(buffer)));
+
+ return (OK);
+} /* hci_read_local_supported_features */
+
+/* Sent Read_Buffer_Size command to the unit */
+static int
+hci_read_buffer_size(int s, int argc, char **argv)
+{
+ ng_hci_read_buffer_size_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_INFO,
+ NG_HCI_OCF_READ_BUFFER_SIZE),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Max. ACL packet size: %d bytes\n",
+ le16toh(rp.max_acl_size));
+ fprintf(stdout, "Number of ACL packets: %d\n",
+ le16toh(rp.num_acl_pkt));
+ fprintf(stdout, "Max. SCO packet size: %d bytes\n",
+ rp.max_sco_size);
+ fprintf(stdout, "Number of SCO packets: %d\n",
+ le16toh(rp.num_sco_pkt));
+
+ return (OK);
+} /* hci_read_buffer_size */
+
+/* Send Read_Country_Code command to the unit */
+static int
+hci_read_country_code(int s, int argc, char **argv)
+{
+ ng_hci_read_country_code_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_INFO,
+ NG_HCI_OCF_READ_COUNTRY_CODE),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Country code: %s [%#02x]\n",
+ hci_cc2str(rp.country_code), rp.country_code);
+
+ return (OK);
+} /* hci_read_country_code */
+
+/* Send Read_BD_ADDR command to the unit */
+static int
+hci_read_bd_addr(int s, int argc, char **argv)
+{
+ ng_hci_read_bdaddr_rp rp;
+ int n;
+
+ n = sizeof(rp);
+ if (hci_simple_request(s, NG_HCI_OPCODE(NG_HCI_OGF_INFO,
+ NG_HCI_OCF_READ_BDADDR), (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "BD_ADDR: %s\n", bt_ntoa(&rp.bdaddr, NULL));
+
+ return (OK);
+} /* hci_read_bd_addr */
+
+struct hci_command info_commands[] = {
+{
+"read_local_version_information",
+"\nThis command will read the values for the version information for the\n" \
+"local Bluetooth unit.",
+&hci_read_local_version_information
+},
+{
+"read_local_supported_features",
+"\nThis command requests a list of the supported features for the local\n" \
+"unit. This command will return a list of the LMP features.",
+&hci_read_local_supported_features
+},
+{
+"read_buffer_size",
+"\nThe Read_Buffer_Size command is used to read the maximum size of the\n" \
+"data portion of HCI ACL and SCO Data Packets sent from the Host to the\n" \
+"Host Controller.",
+&hci_read_buffer_size
+},
+{
+"read_country_code",
+"\nThis command will read the value for the Country_Code return parameter.\n" \
+"The Country_Code defines which range of frequency band of the ISM 2.4 GHz\n" \
+"band will be used by the unit.",
+&hci_read_country_code
+},
+{
+"read_bd_addr",
+"\nThis command will read the value for the BD_ADDR parameter. The BD_ADDR\n" \
+"is a 48-bit unique identifier for a Bluetooth unit.",
+&hci_read_bd_addr
+},
+{
+NULL,
+}};
+
diff --git a/usr.sbin/bluetooth/hccontrol/link_control.c b/usr.sbin/bluetooth/hccontrol/link_control.c
new file mode 100644
index 0000000..ea880cd
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/link_control.c
@@ -0,0 +1,960 @@
+/*
+ * link_control.c
+ *
+ * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: link_control.c,v 1.4 2003/08/18 19:19:54 max Exp $
+ * $FreeBSD$
+ */
+
+#include <bluetooth.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include "hccontrol.h"
+
+static void hci_inquiry_response (int n, uint8_t **b);
+
+/* Send Inquiry command to the unit */
+static int
+hci_inquiry(int s, int argc, char **argv)
+{
+ int n0, n1, n2, timo;
+ char b[512];
+ ng_hci_inquiry_cp cp;
+ ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b;
+
+ /* set defaults */
+ cp.lap[2] = 0x9e;
+ cp.lap[1] = 0x8b;
+ cp.lap[0] = 0x33;
+ cp.inquiry_length = 5;
+ cp.num_responses = 8;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 3:
+ /* number of responses, range 0x00 - 0xff */
+ if (sscanf(argv[2], "%d", &n0) != 1 || n0 < 0 || n0 > 0xff)
+ return (USAGE);
+
+ cp.num_responses = (n0 & 0xff);
+
+ case 2:
+ /* inquiry length (N * 1.28) sec, range 0x01 - 0x30 */
+ if (sscanf(argv[1], "%d", &n0) != 1 || n0 < 0x1 || n0 > 0x30)
+ return (USAGE);
+
+ cp.inquiry_length = (n0 & 0xff);
+
+ case 1:
+ /* LAP */
+ if (sscanf(argv[0], "%x:%x:%x", &n2, &n1, &n0) != 3)
+ return (USAGE);
+
+ cp.lap[0] = (n0 & 0xff);
+ cp.lap[1] = (n1 & 0xff);
+ cp.lap[2] = (n2 & 0xff);
+
+ case 0:
+ /* use defaults */
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send request and expect status back */
+ n0 = sizeof(b);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
+ NG_HCI_OCF_INQUIRY), (char const *) &cp, sizeof(cp),
+ b, &n0) == ERROR)
+ return (ERROR);
+
+ if (*b != 0x00)
+ return (FAILED);
+
+ timo = timeout;
+ timeout = cp.inquiry_length * 1.28 + 1;
+
+wait_for_more:
+ /* wait for inquiry events */
+ n0 = sizeof(b);
+ if (hci_recv(s, b, &n0) == ERROR) {
+ timeout = timo;
+ return (ERROR);
+ }
+
+ if (n0 < sizeof(*e)) {
+ timeout = timo;
+ errno = EIO;
+ return (ERROR);
+ }
+
+ switch (e->event) {
+ case NG_HCI_EVENT_INQUIRY_RESULT: {
+ ng_hci_inquiry_result_ep *ir =
+ (ng_hci_inquiry_result_ep *)(e + 1);
+ uint8_t *r = (uint8_t *)(ir + 1);
+
+ fprintf(stdout, "Inquiry result, num_responses=%d\n",
+ ir->num_responses);
+
+ for (n0 = 0; n0 < ir->num_responses; n0++)
+ hci_inquiry_response(n0, &r);
+
+ goto wait_for_more;
+ }
+
+ case NG_HCI_EVENT_INQUIRY_COMPL:
+ fprintf(stdout, "Inquiry complete. Status: %s [%#02x]\n",
+ hci_status2str(*(b + sizeof(*e))), *(b + sizeof(*e)));
+ break;
+
+ default:
+ goto wait_for_more;
+ }
+
+ timeout = timo;
+
+ return (OK);
+} /* hci_inquiry */
+
+/* Print Inquiry_Result event */
+static void
+hci_inquiry_response(int n, uint8_t **b)
+{
+ ng_hci_inquiry_response *ir = (ng_hci_inquiry_response *)(*b);
+
+ fprintf(stdout, "Inquiry result #%d\n", n);
+ fprintf(stdout, "\tBD_ADDR: %s\n", hci_bdaddr2str(&ir->bdaddr));
+ fprintf(stdout, "\tPage Scan Rep. Mode: %#02x\n",
+ ir->page_scan_rep_mode);
+ fprintf(stdout, "\tPage Scan Period Mode: %#02x\n",
+ ir->page_scan_period_mode);
+ fprintf(stdout, "\tPage Scan Mode: %#02x\n",
+ ir->page_scan_mode);
+ fprintf(stdout, "\tClass: %02x:%02x:%02x\n",
+ ir->uclass[2], ir->uclass[1], ir->uclass[0]);
+ fprintf(stdout, "\tClock offset: %#04x\n",
+ le16toh(ir->clock_offset));
+
+ *b += sizeof(*ir);
+} /* hci_inquiry_response */
+
+/* Send Create_Connection command to the unit */
+static int
+hci_create_connection(int s, int argc, char **argv)
+{
+ int n0;
+ char b[512];
+ ng_hci_create_con_cp cp;
+ ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b;
+
+ /* Set defaults */
+ memset(&cp, 0, sizeof(cp));
+ cp.pkt_type = htole16( NG_HCI_PKT_DM1 | NG_HCI_PKT_DH1 |
+ NG_HCI_PKT_DM3 | NG_HCI_PKT_DH3 |
+ NG_HCI_PKT_DM5);
+ cp.page_scan_rep_mode = NG_HCI_SCAN_REP_MODE0;
+ cp.page_scan_mode = NG_HCI_MANDATORY_PAGE_SCAN_MODE;
+ cp.clock_offset = 0;
+ cp.accept_role_switch = 1;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 6:
+ /* accept role switch */
+ if (sscanf(argv[5], "%d", &n0) != 1)
+ return (USAGE);
+
+ cp.accept_role_switch = n0 ? 1 : 0;
+
+ case 5:
+ /* clock offset */
+ if (sscanf(argv[4], "%d", &n0) != 1)
+ return (USAGE);
+
+ cp.clock_offset = (n0 & 0xffff);
+ cp.clock_offset = htole16(cp.clock_offset);
+
+ case 4:
+ /* page scan mode */
+ if (sscanf(argv[3], "%d", &n0) != 1 || n0 < 0 || n0 > 3)
+ return (USAGE);
+
+ cp.page_scan_mode = (n0 & 0xff);
+
+ case 3:
+ /* page scan rep mode */
+ if (sscanf(argv[2], "%d", &n0) != 1 || n0 < 0 || n0 > 2)
+ return (USAGE);
+
+ cp.page_scan_rep_mode = (n0 & 0xff);
+
+ case 2:
+ /* packet type */
+ if (sscanf(argv[1], "%x", &n0) != 1)
+ return (USAGE);
+
+ n0 &= ( NG_HCI_PKT_DM1 | NG_HCI_PKT_DH1 |
+ NG_HCI_PKT_DM3 | NG_HCI_PKT_DH3 |
+ NG_HCI_PKT_DM5);
+ if (n0 == 0)
+ return (USAGE);
+
+ cp.pkt_type = (n0 & 0xffff);
+ cp.pkt_type = htole16(cp.pkt_type);
+
+ case 1:
+ /* BD_ADDR */
+ if (!bt_aton(argv[0], &cp.bdaddr)) {
+ struct hostent *he = NULL;
+
+ if ((he = bt_gethostbyname(argv[0])) == NULL)
+ return (USAGE);
+
+ memcpy(&cp.bdaddr, he->h_addr, sizeof(cp.bdaddr));
+ }
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send request and expect status response */
+ n0 = sizeof(b);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
+ NG_HCI_OCF_CREATE_CON),
+ (char const *) &cp, sizeof(cp), b, &n0) == ERROR)
+ return (ERROR);
+
+ if (*b != 0x00)
+ return (FAILED);
+
+ /* wait for event */
+again:
+ n0 = sizeof(b);
+ if (hci_recv(s, b, &n0) == ERROR)
+ return (ERROR);
+ if (n0 < sizeof(*e)) {
+ errno = EIO;
+ return (ERROR);
+ }
+
+ if (e->event == NG_HCI_EVENT_CON_COMPL) {
+ ng_hci_con_compl_ep *ep = (ng_hci_con_compl_ep *)(e + 1);
+
+ if (ep->status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(ep->status), ep->status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "BD_ADDR: %s\n", hci_bdaddr2str(&ep->bdaddr));
+ fprintf(stdout, "Connection handle: %d\n",
+ le16toh(ep->con_handle));
+ fprintf(stdout, "Encryption mode: %s [%d]\n",
+ hci_encrypt2str(ep->encryption_mode, 0),
+ ep->encryption_mode);
+ } else
+ goto again;
+
+ return (OK);
+} /* hci_create_connection */
+
+/* Send Disconnect command to the unit */
+static int
+hci_disconnect(int s, int argc, char **argv)
+{
+ int n;
+ char b[512];
+ ng_hci_discon_cp cp;
+ ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b;
+
+ /* Set defaults */
+ memset(&cp, 0, sizeof(cp));
+ cp.reason = 0x13;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 2:
+ /* reason */
+ if (sscanf(argv[1], "%d", &n) != 1 || n <= 0x00 || n > 0xff)
+ return (USAGE);
+
+ cp.reason = (uint8_t) (n & 0xff);
+
+ case 1:
+ /* connection handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (uint16_t) (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send request and expect status response */
+ n = sizeof(b);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
+ NG_HCI_OCF_DISCON),
+ (char const *) &cp, sizeof(cp), b, &n) == ERROR)
+ return (ERROR);
+
+ if (*b != 0x00)
+ return (FAILED);
+
+ /* wait for event */
+again:
+ n = sizeof(b);
+ if (hci_recv(s, b, &n) == ERROR)
+ return (ERROR);
+ if (n < sizeof(*e)) {
+ errno = EIO;
+ return (ERROR);
+ }
+
+ if (e->event == NG_HCI_EVENT_DISCON_COMPL) {
+ ng_hci_discon_compl_ep *ep = (ng_hci_discon_compl_ep *)(e + 1);
+
+ if (ep->status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(ep->status), ep->status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Connection handle: %d\n",
+ le16toh(ep->con_handle));
+ fprintf(stdout, "Reason: %s [%#02x]\n",
+ hci_status2str(ep->reason), ep->reason);
+ } else
+ goto again;
+
+ return (OK);
+} /* hci_diconnect */
+
+/* Send Add_SCO_Connection command to the unit */
+static int
+hci_add_sco_connection(int s, int argc, char **argv)
+{
+ int n;
+ char b[512];
+ ng_hci_add_sco_con_cp cp;
+ ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b;
+
+ /* Set defaults */
+ memset(&cp, 0, sizeof(cp));
+ cp.pkt_type = htole16(NG_HCI_PKT_HV1 | NG_HCI_PKT_HV2 | NG_HCI_PKT_HV3);
+
+ /* parse command parameters */
+ switch (argc) {
+ case 2:
+ /* packet type */
+ if (sscanf(argv[1], "%x", &n) != 1)
+ return (USAGE);
+
+ n &= (NG_HCI_PKT_HV1 | NG_HCI_PKT_HV2 | NG_HCI_PKT_HV3);
+ if (n == 0)
+ return (USAGE);
+
+ cp.pkt_type = (uint16_t) (n & 0x0fff);
+ cp.pkt_type = htole16(cp.pkt_type);
+
+ case 1:
+ /* acl connection handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (uint16_t) (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send request and expect status response */
+ n = sizeof(b);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
+ NG_HCI_OCF_ADD_SCO_CON),
+ (char const *) &cp, sizeof(cp), b, &n) == ERROR)
+ return (ERROR);
+
+ if (*b != 0x00)
+ return (FAILED);
+
+ /* wait for event */
+again:
+ n = sizeof(b);
+ if (hci_recv(s, b, &n) == ERROR)
+ return (ERROR);
+ if (n < sizeof(*e)) {
+ errno = EIO;
+ return (ERROR);
+ }
+
+ if (e->event == NG_HCI_EVENT_CON_COMPL) {
+ ng_hci_con_compl_ep *ep = (ng_hci_con_compl_ep *)(e + 1);
+
+ if (ep->status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(ep->status), ep->status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "BD_ADDR: %s\n", hci_bdaddr2str(&ep->bdaddr));
+ fprintf(stdout, "Connection handle: %d\n",
+ le16toh(ep->con_handle));
+ fprintf(stdout, "Encryption mode: %s [%d]\n",
+ hci_encrypt2str(ep->encryption_mode, 0),
+ ep->encryption_mode);
+ } else
+ goto again;
+
+ return (OK);
+} /* Add_SCO_Connection */
+
+/* Send Change_Connection_Packet_Type command to the unit */
+static int
+hci_change_connection_packet_type(int s, int argc, char **argv)
+{
+ int n;
+ char b[512];
+ ng_hci_change_con_pkt_type_cp cp;
+ ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b;
+
+ switch (argc) {
+ case 2:
+ /* connection handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (uint16_t) (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+
+ /* packet type */
+ if (sscanf(argv[1], "%x", &n) != 1)
+ return (USAGE);
+
+ cp.pkt_type = (uint16_t) (n & 0xffff);
+ cp.pkt_type = htole16(cp.pkt_type);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send request and expect status response */
+ n = sizeof(b);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
+ NG_HCI_OCF_CHANGE_CON_PKT_TYPE),
+ (char const *) &cp, sizeof(cp), b, &n) == ERROR)
+ return (ERROR);
+
+ if (*b != 0x00)
+ return (FAILED);
+
+ /* wait for event */
+again:
+ n = sizeof(b);
+ if (hci_recv(s, b, &n) == ERROR)
+ return (ERROR);
+ if (n < sizeof(*e)) {
+ errno = EIO;
+ return (ERROR);
+ }
+
+ if (e->event == NG_HCI_EVENT_CON_PKT_TYPE_CHANGED) {
+ ng_hci_con_pkt_type_changed_ep *ep =
+ (ng_hci_con_pkt_type_changed_ep *)(e + 1);
+
+ if (ep->status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(ep->status), ep->status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Connection handle: %d\n",
+ le16toh(ep->con_handle));
+ fprintf(stdout, "Packet type: %#04x\n",
+ le16toh(ep->pkt_type));
+ } else
+ goto again;
+
+ return (OK);
+} /* hci_change_connection_packet_type */
+
+/* Send Remote_Name_Request command to the unit */
+static int
+hci_remote_name_request(int s, int argc, char **argv)
+{
+ int n0;
+ char b[512];
+ ng_hci_remote_name_req_cp cp;
+ ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.page_scan_rep_mode = NG_HCI_SCAN_REP_MODE0;
+ cp.page_scan_mode = NG_HCI_MANDATORY_PAGE_SCAN_MODE;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 4:
+ /* clock_offset */
+ if (sscanf(argv[3], "%x", &n0) != 1)
+ return (USAGE);
+
+ cp.clock_offset = (n0 & 0xffff);
+ cp.clock_offset = htole16(cp.clock_offset);
+
+ case 3:
+ /* page_scan_mode */
+ if (sscanf(argv[2], "%d", &n0) != 1 || n0 < 0x00 || n0 > 0x03)
+ return (USAGE);
+
+ cp.page_scan_mode = (n0 & 0xff);
+
+ case 2:
+ /* page_scan_rep_mode */
+ if (sscanf(argv[1], "%d", &n0) != 1 || n0 < 0x00 || n0 > 0x02)
+ return (USAGE);
+
+ cp.page_scan_rep_mode = (n0 & 0xff);
+
+ case 1:
+ /* BD_ADDR */
+ if (!bt_aton(argv[0], &cp.bdaddr)) {
+ struct hostent *he = NULL;
+
+ if ((he = bt_gethostbyname(argv[0])) == NULL)
+ return (USAGE);
+
+ memcpy(&cp.bdaddr, he->h_addr, sizeof(cp.bdaddr));
+ }
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send request and expect status response */
+ n0 = sizeof(b);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
+ NG_HCI_OCF_REMOTE_NAME_REQ),
+ (char const *) &cp, sizeof(cp), b, &n0) == ERROR)
+ return (ERROR);
+
+ if (*b != 0x00)
+ return (FAILED);
+
+ /* wait for event */
+again:
+ n0 = sizeof(b);
+ if (hci_recv(s, b, &n0) == ERROR)
+ return (ERROR);
+ if (n0 < sizeof(*e)) {
+ errno = EIO;
+ return (ERROR);
+ }
+
+ if (e->event == NG_HCI_EVENT_REMOTE_NAME_REQ_COMPL) {
+ ng_hci_remote_name_req_compl_ep *ep =
+ (ng_hci_remote_name_req_compl_ep *)(e + 1);
+
+ if (ep->status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(ep->status), ep->status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "BD_ADDR: %s\n", hci_bdaddr2str(&ep->bdaddr));
+ fprintf(stdout, "Name: %s\n", ep->name);
+ } else
+ goto again;
+
+ return (OK);
+} /* hci_remote_name_request */
+
+/* Send Read_Remote_Supported_Features command to the unit */
+static int
+hci_read_remote_supported_features(int s, int argc, char **argv)
+{
+ int n;
+ char b[512];
+ ng_hci_read_remote_features_cp cp;
+ ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b;
+ char buffer[1024];
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ /* connecton handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send request and expect status response */
+ n = sizeof(b);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
+ NG_HCI_OCF_READ_REMOTE_FEATURES),
+ (char const *) &cp, sizeof(cp), b, &n) == ERROR)
+ return (ERROR);
+
+ if (*b != 0x00)
+ return (FAILED);
+
+ /* wait for event */
+again:
+ n = sizeof(b);
+ if (hci_recv(s, b, &n) == ERROR)
+ return (ERROR);
+
+ if (n < sizeof(*e)) {
+ errno = EIO;
+ return (ERROR);
+ }
+
+ if (e->event == NG_HCI_EVENT_READ_REMOTE_FEATURES_COMPL) {
+ ng_hci_read_remote_features_compl_ep *ep =
+ (ng_hci_read_remote_features_compl_ep *)(e + 1);
+
+ if (ep->status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(ep->status), ep->status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Connection handle: %d\n",
+ le16toh(ep->con_handle));
+ fprintf(stdout, "Features: ");
+ for (n = 0; n < sizeof(ep->features); n++)
+ fprintf(stdout, "%#02x ", ep->features[n]);
+ fprintf(stdout, "\n%s\n", hci_features2str(ep->features,
+ buffer, sizeof(buffer)));
+ } else
+ goto again;
+
+ return (OK);
+} /* hci_read_remote_supported_features */
+
+/* Send Read_Remote_Version_Information command to the unit */
+static int
+hci_read_remote_version_information(int s, int argc, char **argv)
+{
+ int n;
+ char b[512];
+ ng_hci_read_remote_ver_info_cp cp;
+ ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ /* connecton handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send request and expect status response */
+ n = sizeof(b);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
+ NG_HCI_OCF_READ_REMOTE_VER_INFO),
+ (char const *) &cp, sizeof(cp), b, &n) == ERROR)
+ return (ERROR);
+
+ if (*b != 0x00)
+ return (FAILED);
+
+ /* wait for event */
+again:
+ n = sizeof(b);
+ if (hci_recv(s, b, &n) == ERROR)
+ return (ERROR);
+
+ if (n < sizeof(*e)) {
+ errno = EIO;
+ return (ERROR);
+ }
+
+ if (e->event == NG_HCI_EVENT_READ_REMOTE_VER_INFO_COMPL) {
+ ng_hci_read_remote_ver_info_compl_ep *ep =
+ (ng_hci_read_remote_ver_info_compl_ep *)(e + 1);
+
+ if (ep->status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(ep->status), ep->status);
+ return (FAILED);
+ }
+
+ ep->manufacturer = le16toh(ep->manufacturer);
+
+ fprintf(stdout, "Connection handle: %d\n",
+ le16toh(ep->con_handle));
+ fprintf(stdout, "LMP version: %s [%#02x]\n",
+ hci_lmpver2str(ep->lmp_version), ep->lmp_version);
+ fprintf(stdout, "LMP sub-version: %#04x\n",
+ le16toh(ep->lmp_subversion));
+ fprintf(stdout, "Manufacturer: %s [%#04x]\n",
+ hci_manufacturer2str(ep->manufacturer),
+ ep->manufacturer);
+ } else
+ goto again;
+
+ return (OK);
+} /* hci_read_remote_version_information */
+
+/* Send Read_Clock_Offset command to the unit */
+static int
+hci_read_clock_offset(int s, int argc, char **argv)
+{
+ int n;
+ char b[512];
+ ng_hci_read_clock_offset_cp cp;
+ ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ /* connecton handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n < 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send request and expect status response */
+ n = sizeof(b);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
+ NG_HCI_OCF_READ_CLOCK_OFFSET),
+ (char const *) &cp, sizeof(cp), b, &n) == ERROR)
+ return (ERROR);
+
+ if (*b != 0x00)
+ return (FAILED);
+
+ /* wait for event */
+again:
+ n = sizeof(b);
+ if (hci_recv(s, b, &n) == ERROR)
+ return (ERROR);
+
+ if (n < sizeof(*e)) {
+ errno = EIO;
+ return (ERROR);
+ }
+
+ if (e->event == NG_HCI_EVENT_READ_CLOCK_OFFSET_COMPL) {
+ ng_hci_read_clock_offset_compl_ep *ep =
+ (ng_hci_read_clock_offset_compl_ep *)(e + 1);
+
+ if (ep->status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(ep->status), ep->status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Connection handle: %d\n",
+ le16toh(ep->con_handle));
+ fprintf(stdout, "Clock offset: %#04x\n",
+ le16toh(ep->clock_offset));
+ } else
+ goto again;
+
+ return (OK);
+} /* hci_read_clock_offset */
+
+struct hci_command link_control_commands[] = {
+{
+"inquiry <LAP> <inquiry_length> <num_reponses>",
+"\nThis command will cause the Bluetooth unit to enter Inquiry Mode.\n" \
+"Inquiry Mode is used to discover other nearby Bluetooth units. The LAP\n" \
+"input parameter contains the LAP from which the inquiry access code shall\n" \
+"be derived when the inquiry procedure is made. The Inquiry_Length parameter\n"\
+"specifies the total duration of the Inquiry Mode and, when this time\n" \
+"expires, Inquiry will be halted. The Num_Responses parameter specifies the\n" \
+"number of responses that can be received before the Inquiry is halted.\n\n" \
+"\t<LAP> - xx:xx:xx; 9e:8b:33 (GIAC), 93:8b:00 (LDIAC)\n" \
+"\t<inquiry_length> - dd; total length == dd * 1.28 sec\n" \
+"\t<num_responses> - dd",
+&hci_inquiry
+},
+{
+"create_connection <BD_ADDR> <pkt> <rep_mode> <ps_mode> <clck_off> <role_sw>",
+"" \
+"\t<BD_ADDR> - xx:xx:xx:xx:xx:xx BD_ADDR or name\n\n" \
+"\t<pkt> - xxxx; packet type\n" \
+"" \
+"\t\tACL packets\n" \
+"\t\t-----------\n" \
+"\t\t0x0008 DM1\n" \
+"\t\t0x0010 DH1\n" \
+"\t\t0x0400 DM3\n" \
+"\t\t0x0800 DH3\n" \
+"\t\t0x4000 DM5\n" \
+"\t\t0x8000 DH5\n\n" \
+"" \
+"\trep_mode - d; page scan repetition mode\n" \
+"" \
+"\t\tPage scan repetition modes\n" \
+"\t\t--------------------------\n" \
+"\t\t0 Page scan repetition mode 0\n" \
+"\t\t1 Page scan repetition mode 1\n" \
+"\t\t2 Page scan repetition mode 2\n" \
+"\n" \
+"\tps_mode - d; Page scan mode\n" \
+"" \
+"\t\tPage scan modes\n" \
+"\t\t---------------\n" \
+"\t\t0 Mandatory page scan mode\n" \
+"\t\t1 Optional page scan mode1\n" \
+"\t\t2 Optional page scan mode2\n" \
+"\t\t3 Optional page scan mode3\n" \
+"\n" \
+"\tclck_off - dddd; clock offset. Use 0 if unknown\n\n" \
+"\trole_sw - d; allow (1) or deny role switch\n",
+&hci_create_connection
+},
+{
+"disconnect <connection_handle> <reason>",
+"\nThe Disconnection command is used to terminate an existing connection.\n" \
+"The connection handle command parameter indicates which connection is to\n" \
+"be disconnected. The Reason command parameter indicates the reason for\n" \
+"ending the connection.\n\n" \
+"\t<connection_handle> - dddd; connection handle\n" \
+"\t<reason> - dd; reason; usually 19 (0x13) - user ended;\n" \
+"\t also 0x05, 0x13-0x15, 0x1A, 0x29",
+&hci_disconnect
+},
+{
+"add_sco_connection <acl connection handle> <packet type>",
+"This command will cause the link manager to create a SCO connection using\n" \
+"the ACL connection specified by the connection handle command parameter.\n" \
+"The Link Manager will determine how the new connection is established. This\n"\
+"connection is determined by the current state of the device, its piconet,\n" \
+"and the state of the device to be connected. The packet type command parameter\n" \
+"specifies which packet types the Link Manager should use for the connection.\n"\
+"The Link Manager must only use the packet type(s) specified by the packet\n" \
+"type command parameter for sending HCI SCO data packets. Multiple packet\n" \
+"types may be specified for the packet type command parameter by performing\n" \
+"a bitwise OR operation of the different packet types. Note: An SCO connection\n" \
+"can only be created when an ACL connection already exists and when it is\n" \
+"not put in park mode.\n\n" \
+"\t<connection_handle> - dddd; ACL connection handle\n" \
+"\t<packet_type> - xxxx; packet type\n" \
+"" \
+"\t\tSCO packets\n" \
+"\t\t-----------\n" \
+"\t\t0x0020 HV1\n" \
+"\t\t0x0040 HV2\n" \
+"\t\t0x0080 HV3\n",
+&hci_add_sco_connection
+},
+{
+"change_connection_packet_type <connection_hande> <packet_type>",
+"The Change_Connection_Packet_Type command is used to change which packet\n" \
+"types can be used for a connection that is currently established. This\n" \
+"allows current connections to be dynamically modified to support different\n" \
+"types of user data. The Packet_Type command parameter specifies which\n" \
+"packet types the Link Manager can use for the connection. Multiple packet\n" \
+"types may be specified for the Packet_Type command parameter by bitwise OR\n" \
+"operation of the different packet types.\n\n" \
+"\t<connection_handle> - dddd; connection handle\n" \
+"\t<packet_type> - xxxx; packet type mask\n" \
+"" \
+"\t\tACL packets\n" \
+"\t\t-----------\n" \
+"\t\t0x0008 DM1\n" \
+"\t\t0x0010 DH1\n" \
+"\t\t0x0400 DM3\n" \
+"\t\t0x0800 DH3\n" \
+"\t\t0x4000 DM5\n" \
+"\t\t0x8000 DH5\n\n" \
+"" \
+"\t\tSCO packets\n" \
+"\t\t-----------\n" \
+"\t\t0x0020 HV1\n" \
+"\t\t0x0040 HV2\n" \
+"\t\t0x0080 HV3\n" \
+"",
+&hci_change_connection_packet_type
+},
+{
+"remote_name_request <BD_ADDR> <ps_rep_mode> <ps_mode> <clock_offset>",
+"\nThe Remote_Name_Request command is used to obtain the user-friendly\n" \
+"name of another Bluetooth unit.\n\n" \
+"\t<BD_ADDR> - xx:xx:xx:xx:xx:xx BD_ADDR or name\n" \
+"\t<ps_rep_mode> - dd; page scan repetition mode [0-2]\n" \
+"\t<ps_mode> - dd; page scan mode [0-3]\n" \
+"\t<clock_offset> - xxxx; clock offset [0 - 0xffff]",
+&hci_remote_name_request
+},
+{
+"read_remote_supported_features <connection_handle>",
+"\nThis command requests a list of the supported features for the remote\n" \
+"unit identified by the connection handle parameter. The connection handle\n" \
+"must be a connection handle for an ACL connection.\n\n" \
+"\t<connection_handle> - dddd; connection handle",
+&hci_read_remote_supported_features
+},
+{
+"read_remote_version_information <connection_handle>",
+"\nThis command will obtain the values for the version information for the\n" \
+"remote Bluetooth unit identified by the connection handle parameter. The\n" \
+"connection handle must be a connection handle for an ACL connection.\n\n" \
+"\t<connection_handle> - dddd; connection handle",
+&hci_read_remote_version_information
+},
+{
+"read_clock_offset <connection_handle>",
+"\nThis command allows the Host to read the clock offset from the remote unit.\n" \
+"\t<connection_handle> - dddd; connection handle",
+&hci_read_clock_offset
+},
+{
+NULL,
+}};
+
diff --git a/usr.sbin/bluetooth/hccontrol/link_policy.c b/usr.sbin/bluetooth/hccontrol/link_policy.c
new file mode 100644
index 0000000..67b32d5
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/link_policy.c
@@ -0,0 +1,305 @@
+/*
+ * link_policy.c
+ *
+ * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: link_policy.c,v 1.3 2003/08/18 19:19:54 max Exp $
+ * $FreeBSD$
+ */
+
+#include <bluetooth.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include "hccontrol.h"
+
+/* Send Role Discovery to the unit */
+static int
+hci_role_discovery(int s, int argc, char **argv)
+{
+ ng_hci_role_discovery_cp cp;
+ ng_hci_role_discovery_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ /* connection handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (uint16_t) (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send request */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY,
+ NG_HCI_OCF_ROLE_DISCOVERY),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Connection handle: %d\n", le16toh(rp.con_handle));
+ fprintf(stdout, "Role: %s [%#x]\n",
+ (rp.role == NG_HCI_ROLE_MASTER)? "Master" : "Slave", rp.role);
+
+ return (OK);
+} /* hci_role_discovery */
+
+/* Send Swith Role to the unit */
+static int
+hci_switch_role(int s, int argc, char **argv)
+{
+ int n0;
+ char b[512];
+ ng_hci_switch_role_cp cp;
+ ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) b;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 2:
+ /* bdaddr */
+ if (!bt_aton(argv[0], &cp.bdaddr)) {
+ struct hostent *he = NULL;
+
+ if ((he = bt_gethostbyname(argv[0])) == NULL)
+ return (USAGE);
+
+ memcpy(&cp.bdaddr, he->h_addr, sizeof(cp.bdaddr));
+ }
+
+ /* role */
+ if (sscanf(argv[1], "%d", &n0) != 1)
+ return (USAGE);
+
+ cp.role = n0? NG_HCI_ROLE_SLAVE : NG_HCI_ROLE_MASTER;
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send request and expect status response */
+ n0 = sizeof(b);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY,
+ NG_HCI_OCF_SWITCH_ROLE),
+ (char const *) &cp, sizeof(cp), b, &n0) == ERROR)
+ return (ERROR);
+
+ if (*b != 0x00)
+ return (FAILED);
+
+ /* wait for event */
+again:
+ n0 = sizeof(b);
+ if (hci_recv(s, b, &n0) == ERROR)
+ return (ERROR);
+ if (n0 < sizeof(*e)) {
+ errno = EIO;
+ return (ERROR);
+ }
+
+ if (e->event == NG_HCI_EVENT_ROLE_CHANGE) {
+ ng_hci_role_change_ep *ep = (ng_hci_role_change_ep *)(e + 1);
+
+ if (ep->status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(ep->status), ep->status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "BD_ADDR: %s\n", hci_bdaddr2str(&ep->bdaddr));
+ fprintf(stdout, "Role: %s [%#x]\n",
+ (ep->role == NG_HCI_ROLE_MASTER)? "Master" : "Slave",
+ ep->role);
+ } else
+ goto again;
+
+ return (OK);
+} /* hci_switch_role */
+
+/* Send Read_Link_Policy_Settings command to the unit */
+static int
+hci_read_link_policy_settings(int s, int argc, char **argv)
+{
+ ng_hci_read_link_policy_settings_cp cp;
+ ng_hci_read_link_policy_settings_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 1:
+ /* connection handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (uint16_t) (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send request */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY,
+ NG_HCI_OCF_READ_LINK_POLICY_SETTINGS),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Connection handle: %d\n", le16toh(rp.con_handle));
+ fprintf(stdout, "Link policy settings: %#x\n", le16toh(rp.settings));
+
+ return (OK);
+} /* hci_read_link_policy_settings */
+
+/* Send Write_Link_Policy_Settings command to the unit */
+static int
+hci_write_link_policy_settings(int s, int argc, char **argv)
+{
+ ng_hci_write_link_policy_settings_cp cp;
+ ng_hci_write_link_policy_settings_rp rp;
+ int n;
+
+ /* parse command parameters */
+ switch (argc) {
+ case 2:
+ /* connection handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (uint16_t) (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+
+ /* link policy settings */
+ if (sscanf(argv[1], "%x", &n) != 1)
+ return (USAGE);
+
+ cp.settings = (uint16_t) (n & 0x0ffff);
+ cp.settings = htole16(cp.settings);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send request */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_LINK_POLICY,
+ NG_HCI_OCF_WRITE_LINK_POLICY_SETTINGS),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_write_link_policy_settings */
+
+struct hci_command link_policy_commands[] = {
+{
+"role_discovery <connection_handle>",
+"\nThe Role_Discovery command is used for a Bluetooth device to determine\n" \
+"which role the device is performing for a particular Connection Handle.\n" \
+"The connection handle must be a connection handle for an ACL connection.\n\n" \
+"\t<connection_handle> - dddd; connection handle",
+&hci_role_discovery
+},
+{
+"switch_role <BD_ADDR> <role>",
+"\nThe Switch_Role command is used for a Bluetooth device to switch the\n" \
+"current role the device is performing for a particular connection with\n" \
+"another specified Bluetooth device. The BD_ADDR command parameter indicates\n"\
+"for which connection the role switch is to be performed. The Role indicates\n"\
+"the requested new role that the local device performs. Note: the BD_ADDR\n" \
+"command parameter must specify a Bluetooth device for which a connection\n"
+"already exists.\n\n" \
+"\t<BD_ADDR> - xx:xx:xx:xx:xx:xx BD_ADDR or name\n" \
+"\t<role> - dd; role; 0 - Master, 1 - Slave",
+&hci_switch_role
+},
+{
+"read_link_policy_settings <connection_handle>",
+"\nThis command will read the Link Policy setting for the specified connection\n"\
+"handle. The link policy settings parameter determines the behavior of the\n" \
+"local Link Manager when it receives a request from a remote device or it\n" \
+"determines itself to change the master-slave role or to enter the hold,\n" \
+"sniff, or park mode. The local Link Manager will automatically accept or\n" \
+"reject such a request from the remote device, and may even autonomously\n" \
+"request itself, depending on the value of the link policy settings parameter\n"\
+"for the corresponding connection handle. The connection handle must be a\n" \
+"connection handle for an ACL connection.\n\n" \
+"\t<connection_handle> - dddd; connection handle",
+&hci_read_link_policy_settings
+},
+{
+"write_link_policy_settings <connection_handle> <settings>",
+"\nThis command will write the Link Policy setting for the specified connection\n"\
+"handle. The link policy settings parameter determines the behavior of the\n" \
+"local Link Manager when it receives a request from a remote device or it\n" \
+"determines itself to change the master-slave role or to enter the hold,\n" \
+"sniff, or park mode. The local Link Manager will automatically accept or\n" \
+"reject such a request from the remote device, and may even autonomously\n" \
+"request itself, depending on the value of the link policy settings parameter\n"\
+"for the corresponding connection handle. The connection handle must be a\n" \
+"connection handle for an ACL connection. Multiple Link Manager policies may\n"\
+"be specified for the link policy settings parameter by performing a bitwise\n"\
+"OR operation of the different activity types.\n\n" \
+"\t<connection_handle> - dddd; connection handle\n" \
+"\t<settings> - xxxx; settings\n" \
+"\t\t0x0000 - Disable All LM Modes (Default)\n" \
+"\t\t0x0001 - Enable Master Slave Switch\n" \
+"\t\t0x0002 - Enable Hold Mode\n" \
+"\t\t0x0004 - Enable Sniff Mode\n" \
+"\t\t0x0008 - Enable Park Mode\n",
+&hci_write_link_policy_settings
+},
+{
+NULL,
+}};
+
diff --git a/usr.sbin/bluetooth/hccontrol/node.c b/usr.sbin/bluetooth/hccontrol/node.c
new file mode 100644
index 0000000..ede2153
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/node.c
@@ -0,0 +1,607 @@
+/*
+ * node.c
+ *
+ * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: node.c,v 1.6 2003/07/22 21:14:02 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/ioctl.h>
+#include <bluetooth.h>
+#include <errno.h>
+#include <netgraph/ng_message.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "hccontrol.h"
+
+/* Send Read_Node_State command to the node */
+static int
+hci_read_node_state(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_state r;
+
+ memset(&r, 0, sizeof(r));
+ if (ioctl(s, SIOC_HCI_RAW_NODE_GET_STATE, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ fprintf(stdout, "State: %#x\n", r.state);
+
+ return (OK);
+} /* hci_read_node_state */
+
+/* Send Intitialize command to the node */
+static int
+hci_node_initialize(int s, int argc, char **argv)
+{
+ if (ioctl(s, SIOC_HCI_RAW_NODE_INIT) < 0)
+ return (ERROR);
+
+ return (OK);
+} /* hci_node_initialize */
+
+/* Send Read_Debug_Level command to the node */
+static int
+hci_read_debug_level(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_debug r;
+
+ memset(&r, 0, sizeof(r));
+ if (ioctl(s, SIOC_HCI_RAW_NODE_GET_DEBUG, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ fprintf(stdout, "Debug level: %d\n", r.debug);
+
+ return (OK);
+} /* hci_read_debug_level */
+
+/* Send Write_Debug_Level command to the node */
+static int
+hci_write_debug_level(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_debug r;
+
+ memset(&r, 0, sizeof(r));
+ switch (argc) {
+ case 1:
+ r.debug = atoi(argv[0]);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ if (ioctl(s, SIOC_HCI_RAW_NODE_SET_DEBUG, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ return (OK);
+} /* hci_write_debug_level */
+
+/* Send Read_Node_Buffer_Size command to the node */
+static int
+hci_read_node_buffer_size(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_buffer r;
+
+ memset(&r, 0, sizeof(r));
+ if (ioctl(s, SIOC_HCI_RAW_NODE_GET_BUFFER, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ fprintf(stdout, "Number of free command buffers: %d\n",
+ r.buffer.cmd_free);
+ fprintf(stdout, "Max. ACL packet size: %d\n",
+ r.buffer.acl_size);
+ fprintf(stdout, "Numbef of free ACL buffers: %d\n",
+ r.buffer.acl_free);
+ fprintf(stdout, "Total number of ACL buffers: %d\n",
+ r.buffer.acl_pkts);
+ fprintf(stdout, "Max. SCO packet size: %d\n",
+ r.buffer.sco_size);
+ fprintf(stdout, "Numbef of free SCO buffers: %d\n",
+ r.buffer.sco_free);
+ fprintf(stdout, "Total number of SCO buffers: %d\n",
+ r.buffer.sco_pkts);
+
+ return (OK);
+} /* hci_read_node_buffer_size */
+
+/* Send Read_Node_BD_ADDR command to the node */
+static int
+hci_read_node_bd_addr(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_bdaddr r;
+
+ memset(&r, 0, sizeof(r));
+ if (ioctl(s, SIOC_HCI_RAW_NODE_GET_BDADDR, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ fprintf(stdout, "BD_ADDR: %s\n", bt_ntoa(&r.bdaddr, NULL));
+
+ return (OK);
+} /* hci_read_node_bd_addr */
+
+/* Send Read_Node_Features command to the node */
+static int
+hci_read_node_features(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_features r;
+ int n;
+ char buffer[1024];
+
+ memset(&r, 0, sizeof(r));
+ if (ioctl(s, SIOC_HCI_RAW_NODE_GET_FEATURES, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ fprintf(stdout, "Features: ");
+ for (n = 0; n < sizeof(r.features)/sizeof(r.features[0]); n++)
+ fprintf(stdout, "%#02x ", r.features[n]);
+ fprintf(stdout, "\n%s\n", hci_features2str(r.features,
+ buffer, sizeof(buffer)));
+
+ return (OK);
+} /* hci_read_node_features */
+
+/* Send Read_Node_Stat command to the node */
+static int
+hci_read_node_stat(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_stat r;
+
+ memset(&r, 0, sizeof(r));
+ if (ioctl(s, SIOC_HCI_RAW_NODE_GET_STAT, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ fprintf(stdout, "Commands sent: %d\n", r.stat.cmd_sent);
+ fprintf(stdout, "Events received: %d\n", r.stat.evnt_recv);
+ fprintf(stdout, "ACL packets received: %d\n", r.stat.acl_recv);
+ fprintf(stdout, "ACL packets sent: %d\n", r.stat.acl_sent);
+ fprintf(stdout, "SCO packets received: %d\n", r.stat.sco_recv);
+ fprintf(stdout, "SCO packets sent: %d\n", r.stat.sco_sent);
+ fprintf(stdout, "Bytes received: %d\n", r.stat.bytes_recv);
+ fprintf(stdout, "Bytes sent: %d\n", r.stat.bytes_sent);
+
+ return (OK);
+} /* hci_read_node_stat */
+
+/* Send Reset_Node_Stat command to the node */
+static int
+hci_reset_node_stat(int s, int argc, char **argv)
+{
+ if (ioctl(s, SIOC_HCI_RAW_NODE_RESET_STAT) < 0)
+ return (ERROR);
+
+ return (OK);
+} /* hci_reset_node_stat */
+
+/* Send Flush_Neighbor_Cache command to the node */
+static int
+hci_flush_neighbor_cache(int s, int argc, char **argv)
+{
+ if (ioctl(s, SIOC_HCI_RAW_NODE_FLUSH_NEIGHBOR_CACHE) < 0)
+ return (ERROR);
+
+ return (OK);
+} /* hci_flush_neighbor_cache */
+
+/* Send Read_Neighbor_Cache command to the node */
+static int
+hci_read_neighbor_cache(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_neighbor_cache r;
+ int n, error = OK;
+
+ memset(&r, 0, sizeof(r));
+ r.num_entries = NG_HCI_MAX_NEIGHBOR_NUM;
+ r.entries = calloc(NG_HCI_MAX_NEIGHBOR_NUM,
+ sizeof(ng_hci_node_neighbor_cache_entry_ep));
+ if (r.entries == NULL) {
+ errno = ENOMEM;
+ return (ERROR);
+ }
+
+ if (ioctl(s, SIOC_HCI_RAW_NODE_GET_NEIGHBOR_CACHE, &r,
+ sizeof(r)) < 0) {
+ error = ERROR;
+ goto out;
+ }
+
+ fprintf(stdout,
+"BD_ADDR " \
+"Features " \
+"Clock offset " \
+"Page scan " \
+"Rep. scan\n");
+
+ for (n = 0; n < r.num_entries; n++) {
+ fprintf(stdout,
+"%-17.17s " \
+"%02x %02x %02x %02x %02x %02x %02x %02x " \
+"%#12x " \
+"%#9x " \
+"%#9x\n",
+ hci_bdaddr2str(&r.entries[n].bdaddr),
+ r.entries[n].features[0], r.entries[n].features[1],
+ r.entries[n].features[2], r.entries[n].features[3],
+ r.entries[n].features[4], r.entries[n].features[5],
+ r.entries[n].features[6], r.entries[n].features[7],
+ r.entries[n].clock_offset, r.entries[n].page_scan_mode,
+ r.entries[n].page_scan_rep_mode);
+ }
+out:
+ free(r.entries);
+
+ return (error);
+} /* hci_read_neightbor_cache */
+
+/* Send Read_Connection_List command to the node */
+static int
+hci_read_connection_list(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_con_list r;
+ int n, error = OK;
+
+ memset(&r, 0, sizeof(r));
+ r.num_connections = NG_HCI_MAX_CON_NUM;
+ r.connections = calloc(NG_HCI_MAX_CON_NUM, sizeof(ng_hci_node_con_ep));
+ if (r.connections == NULL) {
+ errno = ENOMEM;
+ return (ERROR);
+ }
+
+ if (ioctl(s, SIOC_HCI_RAW_NODE_GET_CON_LIST, &r, sizeof(r)) < 0) {
+ error = ERROR;
+ goto out;
+ }
+
+ fprintf(stdout,
+"Remote BD_ADDR " \
+"Handle " \
+"Type " \
+"Mode " \
+"Role " \
+"Encrypt " \
+"Pending " \
+"Queue " \
+"State\n");
+
+ for (n = 0; n < r.num_connections; n++) {
+ fprintf(stdout,
+"%-17.17s " \
+"%6d " \
+"%4.4s " \
+"%4d " \
+"%4.4s " \
+"%7.7s " \
+"%7d " \
+"%5d " \
+"%s\n",
+ hci_bdaddr2str(&r.connections[n].bdaddr),
+ r.connections[n].con_handle,
+ (r.connections[n].link_type == NG_HCI_LINK_ACL)?
+ "ACL" : "SCO",
+ r.connections[n].mode,
+ (r.connections[n].role == NG_HCI_ROLE_MASTER)?
+ "MAST" : "SLAV",
+ hci_encrypt2str(r.connections[n].encryption_mode, 1),
+ r.connections[n].pending,
+ r.connections[n].queue_len,
+ hci_con_state2str(r.connections[n].state));
+ }
+out:
+ free(r.connections);
+
+ return (error);
+} /* hci_read_connection_list */
+
+/* Send Read_Node_Link_Policy_Settings_Mask command to the node */
+int
+hci_read_node_link_policy_settings_mask(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_link_policy_mask r;
+
+ memset(&r, 0, sizeof(r));
+ if (ioctl(s, SIOC_HCI_RAW_NODE_GET_LINK_POLICY_MASK, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ fprintf(stdout, "Link Policy Settings mask: %#04x\n", r.policy_mask);
+
+ return (OK);
+} /* hci_read_node_link_policy_settings_mask */
+
+/* Send Write_Node_Link_Policy_Settings_Mask command to the node */
+int
+hci_write_node_link_policy_settings_mask(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_link_policy_mask r;
+ int m;
+
+ memset(&r, 0, sizeof(r));
+
+ switch (argc) {
+ case 1:
+ if (sscanf(argv[0], "%x", &m) != 1)
+ return (USAGE);
+
+ r.policy_mask = (m & 0xffff);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ if (ioctl(s, SIOC_HCI_RAW_NODE_SET_LINK_POLICY_MASK, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ return (OK);
+} /* hci_write_node_link_policy_settings_mask */
+
+/* Send Read_Node_Packet_Mask command to the node */
+int
+hci_read_node_packet_mask(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_packet_mask r;
+
+ memset(&r, 0, sizeof(r));
+ if (ioctl(s, SIOC_HCI_RAW_NODE_GET_PACKET_MASK, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ fprintf(stdout, "Packet mask: %#04x\n", r.packet_mask);
+
+ return (OK);
+} /* hci_read_node_packet_mask */
+
+/* Send Write_Node_Packet_Mask command to the node */
+int
+hci_write_node_packet_mask(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_packet_mask r;
+ int m;
+
+ memset(&r, 0, sizeof(r));
+
+ switch (argc) {
+ case 1:
+ if (sscanf(argv[0], "%x", &m) != 1)
+ return (USAGE);
+
+ r.packet_mask = (m & 0xffff);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ if (ioctl(s, SIOC_HCI_RAW_NODE_SET_PACKET_MASK, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ return (OK);
+} /* hci_write_node_packet_mask */
+
+/* Send Read_Node_Role_Switch command to the node */
+int
+hci_read_node_role_switch(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_role_switch r;
+
+ memset(&r, 0, sizeof(r));
+ if (ioctl(s, SIOC_HCI_RAW_NODE_GET_ROLE_SWITCH, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ fprintf(stdout, "Role switch: %d\n", r.role_switch);
+
+ return (OK);
+} /* hci_read_node_role_switch */
+
+/* Send Write_Node_Role_Switch command to the node */
+int
+hci_write_node_role_switch(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_role_switch r;
+ int m;
+
+ memset(&r, 0, sizeof(r));
+
+ switch (argc) {
+ case 1:
+ if (sscanf(argv[0], "%d", &m) != 1)
+ return (USAGE);
+
+ r.role_switch = m? 1 : 0;
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ if (ioctl(s, SIOC_HCI_RAW_NODE_SET_ROLE_SWITCH, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ return (OK);
+} /* hci_write_node_role_switch */
+
+/* Send Read_Node_List command to the node */
+int
+hci_read_node_list(int s, int argc, char **argv)
+{
+ struct ng_btsocket_hci_raw_node_list_names r;
+ int i;
+
+ r.num_names = MAX_NODE_NUM;
+ r.names = (struct nodeinfo*)calloc(MAX_NODE_NUM, sizeof(struct nodeinfo));
+ if (r.names == NULL)
+ return (ERROR);
+
+ if (ioctl(s, SIOC_HCI_RAW_NODE_LIST_NAMES, &r, sizeof(r)) < 0) {
+ free(r.names);
+ return (ERROR);
+ }
+
+ fprintf(stdout, "Name ID Num hooks\n");
+ for (i = 0; i < r.num_names; ++i)
+ fprintf(stdout, "%-15s %08x %9d\n",
+ r.names[i].name, r.names[i].id, r.names[i].hooks);
+
+ free(r.names);
+
+ return (OK);
+} /* hci_read_node_list */
+
+struct hci_command node_commands[] = {
+{
+"read_node_state",
+"Get the HCI node state",
+&hci_read_node_state
+},
+{
+"initialize",
+"Initialize the HCI node",
+&hci_node_initialize
+},
+{
+"read_debug_level",
+"Read the HCI node debug level",
+&hci_read_debug_level
+},
+{
+"write_debug_level <level>",
+"Write the HCI node debug level",
+&hci_write_debug_level
+},
+{
+"read_node_buffer_size",
+"Read the HCI node buffer information. This will return current state of the\n"\
+"HCI buffer for the HCI node",
+&hci_read_node_buffer_size
+},
+{
+"read_node_bd_addr",
+"Read the HCI node BD_ADDR. Returns device BD_ADDR as cached by the HCI node",
+&hci_read_node_bd_addr
+},
+{
+"read_node_features",
+"Read the HCI node features. This will return list of supported features as\n" \
+"cached by the HCI node",
+&hci_read_node_features
+},
+{
+"read_node_stat",
+"Read packets and bytes counters for the HCI node",
+&hci_read_node_stat
+},
+{
+"reset_node_stat",
+"Reset packets and bytes counters for the HCI node",
+&hci_reset_node_stat
+},
+{
+"flush_neighbor_cache",
+"Flush content of the HCI node neighbor cache",
+&hci_flush_neighbor_cache
+},
+{
+"read_neighbor_cache",
+"Read content of the HCI node neighbor cache",
+&hci_read_neighbor_cache
+},
+{
+"read_connection_list",
+"Read the baseband connection descriptors list for the HCI node",
+&hci_read_connection_list
+},
+{
+"read_node_link_policy_settings_mask",
+"Read the value of the Link Policy Settinngs mask for the HCI node",
+&hci_read_node_link_policy_settings_mask
+},
+{
+"write_node_link_policy_settings_mask <policy_mask>",
+"Write the value of the Link Policy Settings mask for the HCI node. By default\n" \
+"all supported Link Policy modes (as reported by the local device features) are\n"\
+"enabled. The particular Link Policy mode is enabled if local device supports\n"\
+"it and correspinding bit in the mask was set\n\n" \
+"\t<policy_mask> - xxxx; Link Policy mask\n" \
+"\t\t0x0000 - Disable All LM Modes\n" \
+"\t\t0x0001 - Enable Master Slave Switch\n" \
+"\t\t0x0002 - Enable Hold Mode\n" \
+"\t\t0x0004 - Enable Sniff Mode\n" \
+"\t\t0x0008 - Enable Park Mode\n",
+&hci_write_node_link_policy_settings_mask
+},
+{
+"read_node_packet_mask",
+"Read the value of the Packet mask for the HCI node",
+&hci_read_node_packet_mask
+},
+{
+"write_node_packet_mask <packet_mask>",
+"Write the value of the Packet mask for the HCI node. By default all supported\n" \
+"packet types (as reported by the local device features) are enabled. The\n" \
+"particular packet type is enabled if local device supports it and corresponding\n" \
+"bit in the mask was set\n\n" \
+"\t<packet_mask> - xxxx; packet type mask\n" \
+"" \
+"\t\tACL packets\n" \
+"\t\t-----------\n" \
+"\t\t0x0008 DM1\n" \
+"\t\t0x0010 DH1\n" \
+"\t\t0x0400 DM3\n" \
+"\t\t0x0800 DH3\n" \
+"\t\t0x4000 DM5\n" \
+"\t\t0x8000 DH5\n" \
+"\n" \
+"\t\tSCO packets\n" \
+"\t\t-----------\n" \
+"\t\t0x0020 HV1\n" \
+"\t\t0x0040 HV2\n" \
+"\t\t0x0080 HV3\n",
+&hci_write_node_packet_mask
+},
+{
+"read_node_role_switch",
+"Read the value of the Role Switch parameter for the HCI node",
+&hci_read_node_role_switch
+},
+{
+"write_node_role_switch {0|1}",
+"Write the value of the Role Switch parameter for the HCI node. By default,\n" \
+"if Role Switch is supported, local device will try to perform Role Switch\n" \
+"and become Master on incoming connection. Some devices do not support Role\n" \
+"Switch and thus incomming connections from such devices will fail. Setting\n" \
+"this parameter to zero will prevent Role Switch and thus accepting device\n" \
+"will remain Slave",
+&hci_write_node_role_switch
+},
+{
+"read_node_list",
+"Get a list of HCI nodes, their Netgraph IDs and connected hooks.",
+&hci_read_node_list
+},
+{
+NULL,
+}};
+
diff --git a/usr.sbin/bluetooth/hccontrol/send_recv.c b/usr.sbin/bluetooth/hccontrol/send_recv.c
new file mode 100644
index 0000000..5c2aea9
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/send_recv.c
@@ -0,0 +1,184 @@
+/*
+ * send_recv.c
+ *
+ * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: send_recv.c,v 1.2 2003/05/21 22:40:30 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/endian.h>
+#include <assert.h>
+#include <errno.h>
+#include <netgraph/bluetooth/include/ng_hci.h>
+#include <string.h>
+#include <unistd.h>
+#include "hccontrol.h"
+
+/* Send HCI request to the unit */
+int
+hci_request(int s, int opcode, char const *cp, int cp_size, char *rp, int *rp_size)
+{
+ char buffer[512];
+ int n;
+ ng_hci_cmd_pkt_t *c = (ng_hci_cmd_pkt_t *) buffer;
+ ng_hci_event_pkt_t *e = (ng_hci_event_pkt_t *) buffer;
+
+ assert(rp != NULL);
+ assert(rp_size != NULL);
+ assert(*rp_size > 0);
+
+ c->type = NG_HCI_CMD_PKT;
+ c->opcode = (uint16_t) opcode;
+ c->opcode = htole16(c->opcode);
+
+ if (cp != NULL) {
+ assert(0 < cp_size && cp_size <= NG_HCI_CMD_PKT_SIZE);
+
+ c->length = (uint8_t) cp_size;
+ memcpy(buffer + sizeof(*c), cp, cp_size);
+ } else
+ c->length = 0;
+
+ if (hci_send(s, buffer, sizeof(*c) + cp_size) == ERROR)
+ return (ERROR);
+
+again:
+ n = sizeof(buffer);
+ if (hci_recv(s, buffer, &n) == ERROR)
+ return (ERROR);
+
+ if (n < sizeof(*e)) {
+ errno = EMSGSIZE;
+ return (ERROR);
+ }
+
+ if (e->type != NG_HCI_EVENT_PKT) {
+ errno = EIO;
+ return (ERROR);
+ }
+
+ switch (e->event) {
+ case NG_HCI_EVENT_COMMAND_COMPL: {
+ ng_hci_command_compl_ep *cc =
+ (ng_hci_command_compl_ep *)(e + 1);
+
+ cc->opcode = le16toh(cc->opcode);
+
+ if (cc->opcode == 0x0000 || cc->opcode != opcode)
+ goto again;
+
+ n -= (sizeof(*e) + sizeof(*cc));
+ if (n < *rp_size)
+ *rp_size = n;
+
+ memcpy(rp, buffer + sizeof(*e) + sizeof(*cc), *rp_size);
+ } break;
+
+ case NG_HCI_EVENT_COMMAND_STATUS: {
+ ng_hci_command_status_ep *cs =
+ (ng_hci_command_status_ep *)(e + 1);
+
+ cs->opcode = le16toh(cs->opcode);
+
+ if (cs->opcode == 0x0000 || cs->opcode != opcode)
+ goto again;
+
+ *rp_size = 1;
+ *rp = cs->status;
+ } break;
+
+ default:
+ goto again;
+ }
+
+ return (OK);
+} /* hci_request */
+
+/* Send simple HCI request - Just HCI command packet (no parameters) */
+int
+hci_simple_request(int s, int opcode, char *rp, int *rp_size)
+{
+ return (hci_request(s, opcode, NULL, 0, rp, rp_size));
+} /* hci_simple_request */
+
+/* Send HCI data to the unit */
+int
+hci_send(int s, char const *buffer, int size)
+{
+ assert(buffer != NULL);
+ assert(size >= sizeof(ng_hci_cmd_pkt_t));
+ assert(size <= sizeof(ng_hci_cmd_pkt_t) + NG_HCI_CMD_PKT_SIZE);
+
+ if (send(s, buffer, size, 0) < 0)
+ return (ERROR);
+
+ return (OK);
+} /* hci_send */
+
+/* Receive HCI data from the unit */
+int
+hci_recv(int s, char *buffer, int *size)
+{
+ struct timeval tv;
+ fd_set rfd;
+ int n;
+
+ assert(buffer != NULL);
+ assert(size != NULL);
+ assert(*size > sizeof(ng_hci_event_pkt_t));
+
+again:
+ FD_ZERO(&rfd);
+ FD_SET(s, &rfd);
+
+ tv.tv_sec = timeout;
+ tv.tv_usec = 0;
+
+ n = select(s + 1, &rfd, NULL, NULL, &tv);
+ if (n <= 0) {
+ if (n < 0) {
+ if (errno == EINTR)
+ goto again;
+ } else
+ errno = ETIMEDOUT;
+
+ return (ERROR);
+ }
+
+ assert(FD_ISSET(s, &rfd));
+
+ n = recv(s, buffer, *size, 0);
+ if (n < 0)
+ return (ERROR);
+
+ *size = n;
+
+ return (OK);
+} /* hci_recv */
+
diff --git a/usr.sbin/bluetooth/hccontrol/status.c b/usr.sbin/bluetooth/hccontrol/status.c
new file mode 100644
index 0000000..3364576
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/status.c
@@ -0,0 +1,245 @@
+/*
+ * status.c
+ *
+ * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: status.c,v 1.2 2003/05/21 22:40:30 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/endian.h>
+#include <errno.h>
+#include <netgraph/bluetooth/include/ng_hci.h>
+#include <stdio.h>
+#include "hccontrol.h"
+
+/* Send Read_Failed_Contact_Counter command to the unit */
+static int
+hci_read_failed_contact_counter(int s, int argc, char **argv)
+{
+ ng_hci_read_failed_contact_cntr_cp cp;
+ ng_hci_read_failed_contact_cntr_rp rp;
+ int n;
+
+ switch (argc) {
+ case 1:
+ /* connection handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (uint16_t) (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_STATUS,
+ NG_HCI_OCF_READ_FAILED_CONTACT_CNTR),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Connection handle: %d\n", le16toh(rp.con_handle));
+ fprintf(stdout, "Failed contact counter: %d\n", le16toh(rp.counter));
+
+ return (OK);
+} /* hci_read_failed_contact_counter */
+
+/* Send Reset_Failed_Contact_Counter command to the unit */
+static int
+hci_reset_failed_contact_counter(int s, int argc, char **argv)
+{
+ ng_hci_reset_failed_contact_cntr_cp cp;
+ ng_hci_reset_failed_contact_cntr_rp rp;
+ int n;
+
+ switch (argc) {
+ case 1:
+ /* connection handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (uint16_t) (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_STATUS,
+ NG_HCI_OCF_RESET_FAILED_CONTACT_CNTR),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ return (OK);
+} /* hci_reset_failed_contact_counter */
+
+/* Sent Get_Link_Quality command to the unit */
+static int
+hci_get_link_quality(int s, int argc, char **argv)
+{
+ ng_hci_get_link_quality_cp cp;
+ ng_hci_get_link_quality_rp rp;
+ int n;
+
+ switch (argc) {
+ case 1:
+ /* connection handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (uint16_t) (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_STATUS,
+ NG_HCI_OCF_GET_LINK_QUALITY),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Connection handle: %d\n", le16toh(rp.con_handle));
+ fprintf(stdout, "Link quality: %d\n", le16toh(rp.quality));
+
+ return (OK);
+} /* hci_get_link_quality */
+
+/* Send Read_RSSI command to the unit */
+static int
+hci_read_rssi(int s, int argc, char **argv)
+{
+ ng_hci_read_rssi_cp cp;
+ ng_hci_read_rssi_rp rp;
+ int n;
+
+ switch (argc) {
+ case 1:
+ /* connection handle */
+ if (sscanf(argv[0], "%d", &n) != 1 || n <= 0 || n > 0x0eff)
+ return (USAGE);
+
+ cp.con_handle = (uint16_t) (n & 0x0fff);
+ cp.con_handle = htole16(cp.con_handle);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* send command */
+ n = sizeof(rp);
+ if (hci_request(s, NG_HCI_OPCODE(NG_HCI_OGF_STATUS,
+ NG_HCI_OCF_READ_RSSI),
+ (char const *) &cp, sizeof(cp),
+ (char *) &rp, &n) == ERROR)
+ return (ERROR);
+
+ if (rp.status != 0x00) {
+ fprintf(stdout, "Status: %s [%#02x]\n",
+ hci_status2str(rp.status), rp.status);
+ return (FAILED);
+ }
+
+ fprintf(stdout, "Connection handle: %d\n", le16toh(rp.con_handle));
+ fprintf(stdout, "RSSI: %d dB\n", (int) rp.rssi);
+
+ return (OK);
+} /* hci_read_rssi */
+
+struct hci_command status_commands[] = {
+{
+"read_failed_contact_counter <connection_handle>",
+"\nThis command will read the value for the Failed_Contact_Counter\n" \
+"parameter for a particular ACL connection to another device.\n\n" \
+"\t<connection_handle> - dddd; ACL connection handle\n",
+&hci_read_failed_contact_counter
+},
+{
+"reset_failed_contact_counter <connection_handle>",
+"\nThis command will reset the value for the Failed_Contact_Counter\n" \
+"parameter for a particular ACL connection to another device.\n\n" \
+"\t<connection_handle> - dddd; ACL connection handle\n",
+&hci_reset_failed_contact_counter
+},
+{
+"get_link_quality <connection_handle>",
+"\nThis command will return the value for the Link_Quality for the\n" \
+"specified ACL connection handle. This command will return a Link_Quality\n" \
+"value from 0-255, which represents the quality of the link between two\n" \
+"Bluetooth devices. The higher the value, the better the link quality is.\n" \
+"Each Bluetooth module vendor will determine how to measure the link quality." \
+"\n\n" \
+"\t<connection_handle> - dddd; ACL connection handle\n",
+&hci_get_link_quality
+},
+{
+"read_rssi <connection_handle>",
+"\nThis command will read the value for the difference between the\n" \
+"measured Received Signal Strength Indication (RSSI) and the limits of\n" \
+"the Golden Receive Power Range for a ACL connection handle to another\n" \
+"Bluetooth device. Any positive RSSI value returned by the Host Controller\n" \
+"indicates how many dB the RSSI is above the upper limit, any negative\n" \
+"value indicates how many dB the RSSI is below the lower limit. The value\n" \
+"zero indicates that the RSSI is inside the Golden Receive Power Range.\n\n" \
+"\t<connection_handle> - dddd; ACL connection handle\n",
+&hci_read_rssi
+},
+{
+NULL,
+}};
+
diff --git a/usr.sbin/bluetooth/hccontrol/util.c b/usr.sbin/bluetooth/hccontrol/util.c
new file mode 100644
index 0000000..4bb5000
--- /dev/null
+++ b/usr.sbin/bluetooth/hccontrol/util.c
@@ -0,0 +1,410 @@
+/*
+ * util.c
+ *
+ * Copyright (c) 2001 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: util.c,v 1.2 2003/05/19 17:29:29 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <bluetooth.h>
+#include <stdio.h>
+#include <string.h>
+
+#define SIZE(x) (sizeof((x))/sizeof((x)[0]))
+
+char const *
+hci_link2str(int link_type)
+{
+ static char const * const t[] = {
+ /* NG_HCI_LINK_SCO */ "SCO",
+ /* NG_HCI_LINK_ACL */ "ACL"
+ };
+
+ return (link_type >= SIZE(t)? "?" : t[link_type]);
+} /* hci_link2str */
+
+char const *
+hci_pin2str(int type)
+{
+ static char const * const t[] = {
+ /* 0x00 */ "Variable PIN",
+ /* 0x01 */ "Fixed PIN"
+ };
+
+ return (type >= SIZE(t)? "?" : t[type]);
+} /* hci_pin2str */
+
+char const *
+hci_scan2str(int scan)
+{
+ static char const * const t[] = {
+ /* 0x00 */ "No Scan enabled",
+ /* 0x01 */ "Inquiry Scan enabled. Page Scan disabled",
+ /* 0x02 */ "Inquiry Scan disabled. Page Scan enabled",
+ /* 0x03 */ "Inquiry Scan enabled. Page Scan enabled"
+ };
+
+ return (scan >= SIZE(t)? "?" : t[scan]);
+} /* hci_scan2str */
+
+char const *
+hci_encrypt2str(int encrypt, int brief)
+{
+ static char const * const t[] = {
+ /* 0x00 */ "Disabled",
+ /* 0x01 */ "Only for point-to-point packets",
+ /* 0x02 */ "Both point-to-point and broadcast packets"
+ };
+
+ static char const * const t1[] = {
+ /* NG_HCI_ENCRYPTION_MODE_NONE */ "NONE",
+ /* NG_HCI_ENCRYPTION_MODE_P2P */ "P2P",
+ /* NG_HCI_ENCRYPTION_MODE_ALL */ "ALL",
+ };
+
+ if (brief)
+ return (encrypt >= SIZE(t1)? "?" : t1[encrypt]);
+
+ return (encrypt >= SIZE(t)? "?" : t[encrypt]);
+} /* hci_encrypt2str */
+
+char const *
+hci_coding2str(int coding)
+{
+ static char const * const t[] = {
+ /* 0x00 */ "Linear",
+ /* 0x01 */ "u-law",
+ /* 0x02 */ "A-law",
+ /* 0x03 */ "Reserved"
+ };
+
+ return (coding >= SIZE(t)? "?" : t[coding]);
+} /* hci_coding2str */
+
+char const *
+hci_vdata2str(int data)
+{
+ static char const * const t[] = {
+ /* 0x00 */ "1's complement",
+ /* 0x01 */ "2's complement",
+ /* 0x02 */ "Sign-Magnitude",
+ /* 0x03 */ "Reserved"
+ };
+
+ return (data >= SIZE(t)? "?" : t[data]);
+} /* hci_vdata2str */
+
+char const *
+hci_hmode2str(int mode, char *buffer, int size)
+{
+ static char const * const t[] = {
+ /* 0x01 */ "Suspend Page Scan ",
+ /* 0x02 */ "Suspend Inquiry Scan ",
+ /* 0x04 */ "Suspend Periodic Inquiries "
+ };
+
+ if (buffer != NULL && size > 0) {
+ int n;
+
+ memset(buffer, 0, size);
+ for (n = 0; n < SIZE(t); n++) {
+ int len = strlen(buffer);
+
+ if (len >= size)
+ break;
+ if (mode & (1 << n))
+ strncat(buffer, t[n], size - len);
+ }
+ }
+
+ return (buffer);
+} /* hci_hmode2str */
+
+char const *
+hci_ver2str(int ver)
+{
+ static char const * const t[] = {
+ /* 0x00 */ "Bluetooth HCI Specification 1.0B",
+ /* 0x01 */ "Bluetooth HCI Specification 1.1",
+ /* 0x02 */ "Bluetooth HCI Specification 1.2",
+ /* 0x03 */ "Bluetooth HCI Specification 2.0"
+ };
+
+ return (ver >= SIZE(t)? "?" : t[ver]);
+} /* hci_ver2str */
+
+char const *
+hci_lmpver2str(int ver)
+{
+ static char const * const t[] = {
+ /* 0x00 */ "Bluetooth LMP 1.0",
+ /* 0x01 */ "Bluetooth LMP 1.1",
+ /* 0x02 */ "Bluetooth LMP 1.2",
+ /* 0x03 */ "Bluetooth LMP 2.0"
+ };
+
+ return (ver >= SIZE(t)? "?" : t[ver]);
+} /* hci_lmpver2str */
+
+char const *
+hci_manufacturer2str(int m)
+{
+ static char const * const t[] = {
+ /* 0000 */ "Ericsson Technology Licensing",
+ /* 0001 */ "Nokia Mobile Phones",
+ /* 0002 */ "Intel Corp.",
+ /* 0003 */ "IBM Corp.",
+ /* 0004 */ "Toshiba Corp.",
+ /* 0005 */ "3Com",
+ /* 0006 */ "Microsoft",
+ /* 0007 */ "Lucent",
+ /* 0008 */ "Motorola",
+ /* 0009 */ "Infineon Technologies AG",
+ /* 0010 */ "Cambridge Silicon Radio",
+ /* 0011 */ "Silicon Wave",
+ /* 0012 */ "Digianswer A/S",
+ /* 0013 */ "Texas Instruments Inc.",
+ /* 0014 */ "Parthus Technologies Inc.",
+ /* 0015 */ "Broadcom Corporation",
+ /* 0016 */ "Mitel Semiconductor",
+ /* 0017 */ "Widcomm, Inc.",
+ /* 0018 */ "Zeevo, Inc.",
+ /* 0019 */ "Atmel Corporation",
+ /* 0020 */ "Mitsubishi Electric Corporation",
+ /* 0021 */ "RTX Telecom A/S",
+ /* 0022 */ "KC Technology Inc.",
+ /* 0023 */ "Newlogic",
+ /* 0024 */ "Transilica, Inc.",
+ /* 0025 */ "Rohde & Schwartz GmbH & Co. KG",
+ /* 0026 */ "TTPCom Limited",
+ /* 0027 */ "Signia Technologies, Inc.",
+ /* 0028 */ "Conexant Systems Inc.",
+ /* 0029 */ "Qualcomm",
+ /* 0030 */ "Inventel",
+ /* 0031 */ "AVM Berlin",
+ /* 0032 */ "BandSpeed, Inc.",
+ /* 0033 */ "Mansella Ltd",
+ /* 0034 */ "NEC Corporation",
+ /* 0035 */ "WavePlus Technology Co., Ltd.",
+ /* 0036 */ "Alcatel",
+ /* 0037 */ "Philips Semiconductors",
+ /* 0038 */ "C Technologies",
+ /* 0039 */ "Open Interface",
+ /* 0040 */ "R F Micro Devices",
+ /* 0041 */ "Hitachi Ltd",
+ /* 0042 */ "Symbol Technologies, Inc.",
+ /* 0043 */ "Tenovis",
+ /* 0044 */ "Macronix International Co. Ltd.",
+ /* 0045 */ "GCT Semiconductor",
+ /* 0046 */ "Norwood Systems",
+ /* 0047 */ "MewTel Technology Inc.",
+ /* 0048 */ "ST Microelectronics",
+ /* 0049 */ "Synopsys",
+ /* 0050 */ "Red-M (Communications) Ltd",
+ /* 0051 */ "Commil Ltd",
+ /* 0052 */ "Computer Access Technology Corporation (CATC)",
+ /* 0053 */ "Eclipse (HQ Espana) S.L.",
+ /* 0054 */ "Renesas Technology Corp.",
+ /* 0055 */ "Mobilian Corporation",
+ /* 0056 */ "Terax",
+ /* 0057 */ "Integrated System Solution Corp.",
+ /* 0058 */ "Matsushita Electric Industrial Co., Ltd.",
+ /* 0059 */ "Gennum Corporation",
+ /* 0060 */ "Research In Motion",
+ /* 0061 */ "IPextreme, Inc.",
+ /* 0062 */ "Systems and Chips, Inc",
+ /* 0063 */ "Bluetooth SIG, Inc",
+ /* 0064 */ "Seiko Epson Corporation"
+ };
+
+ return (m >= SIZE(t)? "?" : t[m]);
+} /* hci_manufacturer2str */
+
+char const *
+hci_features2str(uint8_t *features, char *buffer, int size)
+{
+ static char const * const t[][8] = {
+ { /* byte 0 */
+ /* 0 */ "<3-Slot> ",
+ /* 1 */ "<5-Slot> ",
+ /* 2 */ "<Encryption> ",
+ /* 3 */ "<Slot offset> ",
+ /* 4 */ "<Timing accuracy> ",
+ /* 5 */ "<Switch> ",
+ /* 6 */ "<Hold mode> ",
+ /* 7 */ "<Sniff mode> "
+ },
+ { /* byte 1 */
+ /* 0 */ "<Park mode> ",
+ /* 1 */ "<RSSI> ",
+ /* 2 */ "<Channel quality> ",
+ /* 3 */ "<SCO link> ",
+ /* 4 */ "<HV2 packets> ",
+ /* 5 */ "<HV3 packets> ",
+ /* 6 */ "<u-law log> ",
+ /* 7 */ "<A-law log> "
+ },
+ { /* byte 2 */
+ /* 0 */ "<CVSD> ",
+ /* 1 */ "<Paging scheme> ",
+ /* 2 */ "<Power control> ",
+ /* 3 */ "<Transparent SCO data> ",
+ /* 4 */ "<Flow control lag (bit0)> ",
+ /* 5 */ "<Flow control lag (bit1)> ",
+ /* 6 */ "<Flow control lag (bit2)> ",
+ /* 7 */ "<Unknown2.7> "
+ }};
+
+ if (buffer != NULL && size > 0) {
+ int n, i, len0, len1;
+
+ memset(buffer, 0, size);
+ len1 = 0;
+
+ for (n = 0; n < SIZE(t); n++) {
+ for (i = 0; i < SIZE(t[n]); i++) {
+ len0 = strlen(buffer);
+ if (len0 >= size)
+ goto done;
+
+ if (features[n] & (1 << i)) {
+ if (len1 + strlen(t[n][i]) > 60) {
+ len1 = 0;
+ buffer[len0 - 1] = '\n';
+ }
+
+ len1 += strlen(t[n][i]);
+ strncat(buffer, t[n][i], size - len0);
+ }
+ }
+ }
+ }
+done:
+ return (buffer);
+} /* hci_features2str */
+
+char const *
+hci_cc2str(int cc)
+{
+ static char const * const t[] = {
+ /* 0x00 */ "North America, Europe, Japan",
+ /* 0x01 */ "France"
+ };
+
+ return (cc >= SIZE(t)? "?" : t[cc]);
+} /* hci_cc2str */
+
+char const *
+hci_con_state2str(int state)
+{
+ static char const * const t[] = {
+ /* NG_HCI_CON_CLOSED */ "CLOSED",
+ /* NG_HCI_CON_W4_LP_CON_RSP */ "W4_LP_CON_RSP",
+ /* NG_HCI_CON_W4_CONN_COMPLETE */ "W4_CONN_COMPLETE",
+ /* NG_HCI_CON_OPEN */ "OPEN"
+ };
+
+ return (state >= SIZE(t)? "UNKNOWN" : t[state]);
+} /* hci_con_state2str */
+
+char const *
+hci_status2str(int status)
+{
+ static char const * const t[] = {
+ /* 0x00 */ "No error",
+ /* 0x01 */ "Unknown HCI command",
+ /* 0x02 */ "No connection",
+ /* 0x03 */ "Hardware failure",
+ /* 0x04 */ "Page timeout",
+ /* 0x05 */ "Authentication failure",
+ /* 0x06 */ "Key missing",
+ /* 0x07 */ "Memory full",
+ /* 0x08 */ "Connection timeout",
+ /* 0x09 */ "Max number of connections",
+ /* 0x0a */ "Max number of SCO connections to a unit",
+ /* 0x0b */ "ACL connection already exists",
+ /* 0x0c */ "Command disallowed",
+ /* 0x0d */ "Host rejected due to limited resources",
+ /* 0x0e */ "Host rejected due to security reasons",
+ /* 0x0f */ "Host rejected due to remote unit is a personal unit",
+ /* 0x10 */ "Host timeout",
+ /* 0x11 */ "Unsupported feature or parameter value",
+ /* 0x12 */ "Invalid HCI command parameter",
+ /* 0x13 */ "Other end terminated connection: User ended connection",
+ /* 0x14 */ "Other end terminated connection: Low resources",
+ /* 0x15 */ "Other end terminated connection: About to power off",
+ /* 0x16 */ "Connection terminated by local host",
+ /* 0x17 */ "Repeated attempts",
+ /* 0x18 */ "Pairing not allowed",
+ /* 0x19 */ "Unknown LMP PDU",
+ /* 0x1a */ "Unsupported remote feature",
+ /* 0x1b */ "SCO offset rejected",
+ /* 0x1c */ "SCO interval rejected",
+ /* 0x1d */ "SCO air mode rejected",
+ /* 0x1e */ "Invalid LMP parameters",
+ /* 0x1f */ "Unspecified error",
+ /* 0x20 */ "Unsupported LMP parameter value",
+ /* 0x21 */ "Role change not allowed",
+ /* 0x22 */ "LMP response timeout",
+ /* 0x23 */ "LMP error transaction collision",
+ /* 0x24 */ "LMP PSU not allowed",
+ /* 0x25 */ "Encryption mode not acceptable",
+ /* 0x26 */ "Unit key used",
+ /* 0x27 */ "QoS is not supported",
+ /* 0x28 */ "Instant passed",
+ /* 0x29 */ "Pairing with unit key not supported"
+ };
+
+ return (status >= SIZE(t)? "Unknown error" : t[status]);
+} /* hci_status2str */
+
+char const *
+hci_bdaddr2str(bdaddr_t const *ba)
+{
+ extern int numeric_bdaddr;
+ static char buffer[MAXHOSTNAMELEN];
+ struct hostent *he = NULL;
+
+ if (memcmp(ba, NG_HCI_BDADDR_ANY, sizeof(*ba)) == 0) {
+ buffer[0] = '*';
+ buffer[1] = 0;
+
+ return (buffer);
+ }
+
+ if (!numeric_bdaddr &&
+ (he = bt_gethostbyaddr((char *)ba, sizeof(*ba), AF_BLUETOOTH)) != NULL) {
+ strlcpy(buffer, he->h_name, sizeof(buffer));
+
+ return (buffer);
+ }
+
+ bt_ntoa(ba, buffer);
+
+ return (buffer);
+} /* hci_bdaddr2str */
+
diff --git a/usr.sbin/bluetooth/hcsecd/Makefile b/usr.sbin/bluetooth/hcsecd/Makefile
new file mode 100644
index 0000000..e544ed4
--- /dev/null
+++ b/usr.sbin/bluetooth/hcsecd/Makefile
@@ -0,0 +1,13 @@
+# $Id: Makefile,v 1.8 2003/08/14 20:06:20 max Exp $
+# $FreeBSD$
+
+PROG= hcsecd
+MAN= hcsecd.8 hcsecd.conf.5
+SRCS= hcsecd.c lexer.l parser.y
+WARNS?= 2
+CFLAGS+= -I${.CURDIR}
+
+DPADD= ${LIBBLUETOOTH}
+LDADD= -lbluetooth
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bluetooth/hcsecd/hcsecd.8 b/usr.sbin/bluetooth/hcsecd/hcsecd.8
new file mode 100644
index 0000000..8b7084d
--- /dev/null
+++ b/usr.sbin/bluetooth/hcsecd/hcsecd.8
@@ -0,0 +1,128 @@
+.\" Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: hcsecd.8,v 1.8 2003/09/08 18:54:20 max Exp $
+.\" $FreeBSD$
+.\"
+.Dd November 16, 2002
+.Dt HCSECD 8
+.Os
+.Sh NAME
+.Nm hcsecd
+.Nd control link keys and PIN codes for Bluetooth devices
+.Sh SYNOPSIS
+.Nm
+.Op Fl dh
+.Fl f Ar configfile
+.Sh DESCRIPTION
+The
+.Nm
+daemon controls link keys and PIN codes for Bluetooth devices.
+It opens a raw HCI socket and listens for
+.Dv Link_Key_Request ,
+.Dv PIN_Code_Request
+and
+.Dv Link_Key_Notification
+HCI events.
+.Pp
+Once a
+.Dv Link_Key_Request
+or
+.Dv PIN_Code_Request
+HCI event is received, the daemon scans the configuration file for a
+matching entry.
+The remote device BD_ADDR is used as a key.
+If no matching entry was found, the default entry will be used.
+If no default entry was found then it is assumed that no link key and no
+PIN code exists.
+For any given entry, the link key takes precedence over the PIN code.
+If a link key was not specified, the device must generate the link key from
+the PIN code.
+If an entry was found and the link key (or PIN code) exists, the
+.Dv Link_Key_Request_Reply
+(or
+.Dv PIN_Code_Request_Reply )
+command will be sent back to the device.
+Otherwise, the
+.Dv Link_Key_Request_Negative_Reply
+(or
+.Dv PIN_Code_Request_Negative_Reply )
+command will be sent back to the device.
+.Pp
+The
+.Nm
+daemon also handles HCI
+.Dv Link_Key_Notification
+events and caches link keys created from the PIN codes in memory.
+To preserve link keys between restarts the
+.Nm
+daemon dumps link keys for all entries in the
+.Pa /var/db/hcsecd.keys
+link keys file.
+If it exists, the link keys file gets processed by the
+.Nm
+daemon after it processes its main configuration file.
+The link keys file gets written every time the
+.Nm
+daemon shuts down gracefully.
+It is possible to force the
+.Nm
+daemon to re-read its main configuration file and dump the link keys file by
+sending the
+.Dv HUP
+signal to the
+.Nm
+process.
+The user is expected to not modify the link keys file by hand.
+.Pp
+The command line options are as follows:
+.Bl -tag -width indent
+.It Fl d
+Do not detach from the controlling terminal.
+.It Fl f Ar configfile
+Specify the name of the configuration file.
+The default is
+.Pa /etc/bluetooth/hcsecd.conf .
+.It Fl h
+Display usage message and exit.
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /etc/bluetooth/hcsecd.conf" -compact
+.It Pa /etc/bluetooth/hcsecd.conf
+.It Pa /var/db/hcsecd.keys
+.It Pa /var/run/hcsecd.pid
+.El
+.Sh SEE ALSO
+.Xr ng_btsocket 4 ,
+.Xr ng_hci 4 ,
+.Xr hcsecd.conf 5 ,
+.Xr hccontrol 8 ,
+.Xr hcseriald 8
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
+.Sh BUGS
+Currently there is no way to select the link key or the PIN code based on
+which local device received the request.
+Everything is based on the remote device BD_ADDR.
+An interface for external helpers to obtain link keys and PIN codes is missing.
diff --git a/usr.sbin/bluetooth/hcsecd/hcsecd.c b/usr.sbin/bluetooth/hcsecd/hcsecd.c
new file mode 100644
index 0000000..72f9c8c
--- /dev/null
+++ b/usr.sbin/bluetooth/hcsecd/hcsecd.c
@@ -0,0 +1,446 @@
+/*
+ * hcsecd.c
+ *
+ * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: hcsecd.c,v 1.6 2003/08/18 19:19:55 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <bluetooth.h>
+#include <err.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include "hcsecd.h"
+
+static int done = 0;
+
+static int process_pin_code_request_event
+ (int sock, struct sockaddr_hci *addr, bdaddr_p bdaddr);
+static int process_link_key_request_event
+ (int sock, struct sockaddr_hci *addr, bdaddr_p bdaddr);
+static int send_pin_code_reply
+ (int sock, struct sockaddr_hci *addr, bdaddr_p bdaddr, char const *pin);
+static int send_link_key_reply
+ (int sock, struct sockaddr_hci *addr, bdaddr_p bdaddr, uint8_t *key);
+static int process_link_key_notification_event
+ (int sock, struct sockaddr_hci *addr, ng_hci_link_key_notification_ep *ep);
+static void sighup
+ (int s);
+static void sigint
+ (int s);
+static void usage
+ (void);
+
+/* Main */
+int
+main(int argc, char *argv[])
+{
+ int n, detach, sock;
+ socklen_t size;
+ struct sigaction sa;
+ struct sockaddr_hci addr;
+ struct ng_btsocket_hci_raw_filter filter;
+ char buffer[HCSECD_BUFFER_SIZE];
+ ng_hci_event_pkt_t *event = NULL;
+
+ detach = 1;
+
+ while ((n = getopt(argc, argv, "df:h")) != -1) {
+ switch (n) {
+ case 'd':
+ detach = 0;
+ break;
+
+ case 'f':
+ config_file = optarg;
+ break;
+
+ case 'h':
+ default:
+ usage();
+ /* NOT REACHED */
+ }
+ }
+
+ if (config_file == NULL)
+ usage();
+ /* NOT REACHED */
+
+ if (getuid() != 0)
+ errx(1, "** ERROR: You should run %s as privileged user!",
+ HCSECD_IDENT);
+
+ /* Set signal handlers */
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = sigint;
+ sa.sa_flags = SA_NOCLDWAIT;
+ if (sigaction(SIGINT, &sa, NULL) < 0)
+ err(1, "Could not sigaction(SIGINT)");
+ if (sigaction(SIGTERM, &sa, NULL) < 0)
+ err(1, "Could not sigaction(SIGINT)");
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = sighup;
+ if (sigaction(SIGHUP, &sa, NULL) < 0)
+ err(1, "Could not sigaction(SIGHUP)");
+
+ /* Open socket and set filter */
+ sock = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_HCI);
+ if (sock < 0)
+ err(1, "Could not create HCI socket");
+
+ memset(&filter, 0, sizeof(filter));
+ bit_set(filter.event_mask, NG_HCI_EVENT_PIN_CODE_REQ - 1);
+ bit_set(filter.event_mask, NG_HCI_EVENT_LINK_KEY_REQ - 1);
+ bit_set(filter.event_mask, NG_HCI_EVENT_LINK_KEY_NOTIFICATION - 1);
+
+ if (setsockopt(sock, SOL_HCI_RAW, SO_HCI_RAW_FILTER,
+ (void * const) &filter, sizeof(filter)) < 0)
+ err(1, "Could not set HCI socket filter");
+
+ if (detach && daemon(0, 0) < 0)
+ err(1, "Could not daemon()ize");
+
+ openlog(HCSECD_IDENT, LOG_NDELAY|LOG_PERROR|LOG_PID, LOG_DAEMON);
+
+ read_config_file();
+ read_keys_file();
+
+ if (detach) {
+ FILE *pid = NULL;
+
+ if ((pid = fopen(HCSECD_PIDFILE, "w")) == NULL) {
+ syslog(LOG_ERR, "Could not create PID file %s. %s (%d)",
+ HCSECD_PIDFILE, strerror(errno), errno);
+ exit(1);
+ }
+
+ fprintf(pid, "%d", getpid());
+ fclose(pid);
+ }
+
+ event = (ng_hci_event_pkt_t *) buffer;
+ while (!done) {
+ size = sizeof(addr);
+ n = recvfrom(sock, buffer, sizeof(buffer), 0,
+ (struct sockaddr *) &addr, &size);
+ if (n < 0) {
+ if (errno == EINTR)
+ continue;
+
+ syslog(LOG_ERR, "Could not receive from HCI socket. " \
+ "%s (%d)", strerror(errno), errno);
+ exit(1);
+ }
+
+ if (event->type != NG_HCI_EVENT_PKT) {
+ syslog(LOG_ERR, "Received unexpected HCI packet, " \
+ "type=%#x", event->type);
+ continue;
+ }
+
+ switch (event->event) {
+ case NG_HCI_EVENT_PIN_CODE_REQ:
+ process_pin_code_request_event(sock, &addr,
+ (bdaddr_p)(event + 1));
+ break;
+
+ case NG_HCI_EVENT_LINK_KEY_REQ:
+ process_link_key_request_event(sock, &addr,
+ (bdaddr_p)(event + 1));
+ break;
+
+ case NG_HCI_EVENT_LINK_KEY_NOTIFICATION:
+ process_link_key_notification_event(sock, &addr,
+ (ng_hci_link_key_notification_ep *)(event + 1));
+ break;
+
+ default:
+ syslog(LOG_ERR, "Received unexpected HCI event, " \
+ "event=%#x", event->event);
+ break;
+ }
+ }
+
+ if (detach)
+ if (remove(HCSECD_PIDFILE) < 0)
+ syslog(LOG_ERR, "Could not remove PID file %s. %s (%d)",
+ HCSECD_PIDFILE, strerror(errno), errno);
+
+ dump_keys_file();
+ clean_config();
+ closelog();
+ close(sock);
+
+ return (0);
+}
+
+/* Process PIN_Code_Request event */
+static int
+process_pin_code_request_event(int sock, struct sockaddr_hci *addr,
+ bdaddr_p bdaddr)
+{
+ link_key_p key = NULL;
+
+ syslog(LOG_DEBUG, "Got PIN_Code_Request event from '%s', " \
+ "remote bdaddr %s", addr->hci_node,
+ bt_ntoa(bdaddr, NULL));
+
+ if ((key = get_key(bdaddr, 0)) != NULL) {
+ syslog(LOG_DEBUG, "Found matching entry, " \
+ "remote bdaddr %s, name '%s', PIN code %s",
+ bt_ntoa(&key->bdaddr, NULL),
+ (key->name != NULL)? key->name : "No name",
+ (key->pin != NULL)? "exists" : "doesn't exist");
+
+ return (send_pin_code_reply(sock, addr, bdaddr, key->pin));
+ }
+
+ syslog(LOG_DEBUG, "Could not PIN code for remote bdaddr %s",
+ bt_ntoa(bdaddr, NULL));
+
+ return (send_pin_code_reply(sock, addr, bdaddr, NULL));
+}
+
+/* Process Link_Key_Request event */
+static int
+process_link_key_request_event(int sock, struct sockaddr_hci *addr,
+ bdaddr_p bdaddr)
+{
+ link_key_p key = NULL;
+
+ syslog(LOG_DEBUG, "Got Link_Key_Request event from '%s', " \
+ "remote bdaddr %s", addr->hci_node,
+ bt_ntoa(bdaddr, NULL));
+
+ if ((key = get_key(bdaddr, 0)) != NULL) {
+ syslog(LOG_DEBUG, "Found matching entry, " \
+ "remote bdaddr %s, name '%s', link key %s",
+ bt_ntoa(&key->bdaddr, NULL),
+ (key->name != NULL)? key->name : "No name",
+ (key->key != NULL)? "exists" : "doesn't exist");
+
+ return (send_link_key_reply(sock, addr, bdaddr, key->key));
+ }
+
+ syslog(LOG_DEBUG, "Could not find link key for remote bdaddr %s",
+ bt_ntoa(bdaddr, NULL));
+
+ return (send_link_key_reply(sock, addr, bdaddr, NULL));
+}
+
+/* Send PIN_Code_[Negative]_Reply */
+static int
+send_pin_code_reply(int sock, struct sockaddr_hci *addr,
+ bdaddr_p bdaddr, char const *pin)
+{
+ uint8_t buffer[HCSECD_BUFFER_SIZE];
+ ng_hci_cmd_pkt_t *cmd = NULL;
+
+ memset(buffer, 0, sizeof(buffer));
+
+ cmd = (ng_hci_cmd_pkt_t *) buffer;
+ cmd->type = NG_HCI_CMD_PKT;
+
+ if (pin != NULL) {
+ ng_hci_pin_code_rep_cp *cp = NULL;
+
+ cmd->opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
+ NG_HCI_OCF_PIN_CODE_REP));
+ cmd->length = sizeof(*cp);
+
+ cp = (ng_hci_pin_code_rep_cp *)(cmd + 1);
+ memcpy(&cp->bdaddr, bdaddr, sizeof(cp->bdaddr));
+ strncpy((char *) cp->pin, pin, sizeof(cp->pin));
+ cp->pin_size = strlen((char const *) cp->pin);
+
+ syslog(LOG_DEBUG, "Sending PIN_Code_Reply to '%s' " \
+ "for remote bdaddr %s",
+ addr->hci_node, bt_ntoa(bdaddr, NULL));
+ } else {
+ ng_hci_pin_code_neg_rep_cp *cp = NULL;
+
+ cmd->opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
+ NG_HCI_OCF_PIN_CODE_NEG_REP));
+ cmd->length = sizeof(*cp);
+
+ cp = (ng_hci_pin_code_neg_rep_cp *)(cmd + 1);
+ memcpy(&cp->bdaddr, bdaddr, sizeof(cp->bdaddr));
+
+ syslog(LOG_DEBUG, "Sending PIN_Code_Negative_Reply to '%s' " \
+ "for remote bdaddr %s",
+ addr->hci_node, bt_ntoa(bdaddr, NULL));
+ }
+
+again:
+ if (sendto(sock, buffer, sizeof(*cmd) + cmd->length, 0,
+ (struct sockaddr *) addr, sizeof(*addr)) < 0) {
+ if (errno == EINTR)
+ goto again;
+
+ syslog(LOG_ERR, "Could not send PIN code reply to '%s' " \
+ "for remote bdaddr %s. %s (%d)",
+ addr->hci_node, bt_ntoa(bdaddr, NULL),
+ strerror(errno), errno);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/* Send Link_Key_[Negative]_Reply */
+static int
+send_link_key_reply(int sock, struct sockaddr_hci *addr,
+ bdaddr_p bdaddr, uint8_t *key)
+{
+ uint8_t buffer[HCSECD_BUFFER_SIZE];
+ ng_hci_cmd_pkt_t *cmd = NULL;
+
+ memset(buffer, 0, sizeof(buffer));
+
+ cmd = (ng_hci_cmd_pkt_t *) buffer;
+ cmd->type = NG_HCI_CMD_PKT;
+
+ if (key != NULL) {
+ ng_hci_link_key_rep_cp *cp = NULL;
+
+ cmd->opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
+ NG_HCI_OCF_LINK_KEY_REP));
+ cmd->length = sizeof(*cp);
+
+ cp = (ng_hci_link_key_rep_cp *)(cmd + 1);
+ memcpy(&cp->bdaddr, bdaddr, sizeof(cp->bdaddr));
+ memcpy(&cp->key, key, sizeof(cp->key));
+
+ syslog(LOG_DEBUG, "Sending Link_Key_Reply to '%s' " \
+ "for remote bdaddr %s",
+ addr->hci_node, bt_ntoa(bdaddr, NULL));
+ } else {
+ ng_hci_link_key_neg_rep_cp *cp = NULL;
+
+ cmd->opcode = htole16(NG_HCI_OPCODE(NG_HCI_OGF_LINK_CONTROL,
+ NG_HCI_OCF_LINK_KEY_NEG_REP));
+ cmd->length = sizeof(*cp);
+
+ cp = (ng_hci_link_key_neg_rep_cp *)(cmd + 1);
+ memcpy(&cp->bdaddr, bdaddr, sizeof(cp->bdaddr));
+
+ syslog(LOG_DEBUG, "Sending Link_Key_Negative_Reply to '%s' " \
+ "for remote bdaddr %s",
+ addr->hci_node, bt_ntoa(bdaddr, NULL));
+ }
+
+again:
+ if (sendto(sock, buffer, sizeof(*cmd) + cmd->length, 0,
+ (struct sockaddr *) addr, sizeof(*addr)) < 0) {
+ if (errno == EINTR)
+ goto again;
+
+ syslog(LOG_ERR, "Could not send link key reply to '%s' " \
+ "for remote bdaddr %s. %s (%d)",
+ addr->hci_node, bt_ntoa(bdaddr, NULL),
+ strerror(errno), errno);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/* Process Link_Key_Notification event */
+static int
+process_link_key_notification_event(int sock, struct sockaddr_hci *addr,
+ ng_hci_link_key_notification_ep *ep)
+{
+ link_key_p key = NULL;
+
+ syslog(LOG_DEBUG, "Got Link_Key_Notification event from '%s', " \
+ "remote bdaddr %s", addr->hci_node,
+ bt_ntoa(&ep->bdaddr, NULL));
+
+ if ((key = get_key(&ep->bdaddr, 1)) == NULL) {
+ syslog(LOG_ERR, "Could not find entry for remote bdaddr %s",
+ bt_ntoa(&ep->bdaddr, NULL));
+ return (-1);
+ }
+
+ syslog(LOG_DEBUG, "Updating link key for the entry, " \
+ "remote bdaddr %s, name '%s', link key %s",
+ bt_ntoa(&key->bdaddr, NULL),
+ (key->name != NULL)? key->name : "No name",
+ (key->key != NULL)? "exists" : "doesn't exist");
+
+ if (key->key == NULL) {
+ key->key = (uint8_t *) malloc(NG_HCI_KEY_SIZE);
+ if (key->key == NULL) {
+ syslog(LOG_ERR, "Could not allocate link key");
+ exit(1);
+ }
+ }
+
+ memcpy(key->key, &ep->key, NG_HCI_KEY_SIZE);
+
+ return (0);
+}
+
+/* Signal handlers */
+static void
+sighup(int s)
+{
+ syslog(LOG_DEBUG, "Got SIGHUP (%d)", s);
+
+ dump_keys_file();
+ read_config_file();
+ read_keys_file();
+}
+
+static void
+sigint(int s)
+{
+ syslog(LOG_DEBUG, "Got signal %d, total number of signals %d",
+ s, ++ done);
+}
+
+/* Display usage and exit */
+static void
+usage(void)
+{
+ fprintf(stderr,
+"Usage: %s [-d] -f config_file [-h]\n" \
+"Where:\n" \
+"\t-d do not detach from terminal\n" \
+"\t-f config_file use <config_file>\n" \
+"\t-h display this message\n", HCSECD_IDENT);
+
+ exit(255);
+}
+
diff --git a/usr.sbin/bluetooth/hcsecd/hcsecd.conf b/usr.sbin/bluetooth/hcsecd/hcsecd.conf
new file mode 100644
index 0000000..127ce04
--- /dev/null
+++ b/usr.sbin/bluetooth/hcsecd/hcsecd.conf
@@ -0,0 +1,64 @@
+#
+# $Id: hcsecd.conf,v 1.1 2002/11/24 20:22:39 max Exp $
+# $FreeBSD$
+#
+# HCI security daemon configuration file
+#
+# Format:
+#
+# device {
+# option value ;
+# }
+#
+# Possible options and values
+#
+# Options Values
+# ----------------------------------
+# bdaddr xx:xx:xx:xx:xx:xx ; - remote device BD_ADDR
+# name "any char" ; - to set user friendly device name
+# key 0x11223344 | nokey ; - to set link key for the device
+# pin "secret" | nopin ; - to PIN code for the device
+#
+# Notes:
+#
+# Currently there is no way to select keys/PIN code based on which
+# local device received the request. Everything is based on remote
+# device BD_ADDR.
+#
+# "nokey" means that no link key has been defined and we should
+# send Link_Key_Negative_Reply command to the device.
+#
+# "nopin" means that no PIN code has been defined and we should
+# send PIN_Code_Negative_Reply command to the device
+#
+
+# Default entry applied if no better match found
+# It MUST have 00:00:00:00:00:00 as bdaddr
+device {
+ bdaddr 00:00:00:00:00:00;
+ name "Default entry";
+ key nokey;
+ pin nopin;
+}
+
+device {
+ bdaddr 00:80:37:5e:4d:d4;
+ name "Ericsson T68 phone";
+ key nokey;
+ pin "0000"; # PIN code (string up to 16 character)
+}
+
+device {
+ bdaddr 00:01:03:fc:6e:ec;
+ name "3COM PCCARD";
+ key nokey;
+ pin "0000";
+}
+
+device {
+ bdaddr 00:11:22:33:44:55;
+ name "Dummy";
+ key 0x00112233445566778899aabbccddeeff; # 16 bytes key (hex string)
+ pin nopin;
+}
+
diff --git a/usr.sbin/bluetooth/hcsecd/hcsecd.conf.5 b/usr.sbin/bluetooth/hcsecd/hcsecd.conf.5
new file mode 100644
index 0000000..189ca66
--- /dev/null
+++ b/usr.sbin/bluetooth/hcsecd/hcsecd.conf.5
@@ -0,0 +1,131 @@
+.\" Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: hcsecd.conf.5,v 1.1 2003/05/26 22:49:23 max Exp $
+.\" $FreeBSD$
+.\"
+.Dd May 26, 2003
+.Dt HCSECD.CONF 5
+.Os
+.Sh NAME
+.Nm hcsecd.conf
+.Nd
+.Xr hcsecd 8
+configuration file
+.Sh DESCRIPTION
+The
+.Nm
+file is the configuration file for the
+.Xr hcsecd 8
+Bluetooth link keys/PIN codes management daemon.
+.Pp
+The
+.Nm
+file is a free-form
+.Tn ASCII
+text file.
+It is parsed by the recursive-descent parser built into
+.Xr hcsecd 8 .
+The file may contain extra tabs and newlines for formatting purposes.
+Keywords in the file are case-sensitive.
+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.
+.Sh FILE FORMAT
+The
+.Nm
+file consists of a list of
+.Cm device
+entries.
+Each
+.Cm device
+entry defines a link key or PIN code for a remote Bluetooth device.
+Each remote Bluetooth device is identified by its unique BD_ADDR.
+.Pp
+The
+.Cm device
+entry
+.Pp
+.Cm device
+{
+.Cm option Ar argument ;
+.Oo
+.Cm option Ar argument ;
+.Oc
+}
+.Pp
+The following section describes all supported options and arguments.
+.Bl -tag -width indent
+.It Cm bdaddr Ar BD_ADDR
+Specify remote device BD_ADDR for the entry.
+.It Cm name Ar device_name
+Specify user friendly name for the entry.
+Name is a string in straight double quotes.
+.It Cm key Ar link_key
+Specify link key for the entry.
+Link key is hexadecimal string up to 32 characters in length starting with
+.Ql 0x .
+.It Cm key nokey
+Specify no link key for the entry.
+.It Cm pin Ar PIN_code
+Specify PIN code for the entry.
+PIN code is a string up to 16 characters in length in straight double quotes.
+.It Cm pin nopin
+Specify no PIN code for the entry.
+.El
+.Sh EXAMPLES
+A sample
+.Nm
+file:
+.Bd -literal
+# Default entry is applied if no better match found
+# It MUST have 00:00:00:00:00:00 as bdaddr
+device {
+ bdaddr 00:00:00:00:00:00;
+ name "Default entry";
+ key nokey;
+ pin nopin;
+}
+
+# Ericsson T68 phone
+device {
+ bdaddr 00:80:37:5e:4d:d4;
+ name "Ericsson T68 phone";
+ key nokey;
+ pin "0000"; # PIN code
+}
+
+# Dummy device
+device {
+ bdaddr 00:11:22:33:44:55;
+ name "Dummy";
+ key 0x00112233445566778899aabbccddeeff; # 16 bytes key
+ pin nopin;
+}
+.Ed
+.Sh SEE ALSO
+.Xr hcsecd 8
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
diff --git a/usr.sbin/bluetooth/hcsecd/hcsecd.h b/usr.sbin/bluetooth/hcsecd/hcsecd.h
new file mode 100644
index 0000000..acfc887
--- /dev/null
+++ b/usr.sbin/bluetooth/hcsecd/hcsecd.h
@@ -0,0 +1,65 @@
+/*
+ * hcsecd.h
+ *
+ * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: hcsecd.h,v 1.3 2003/09/08 18:54:21 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _HCSECD_H_
+#define _HCSECD_H_ 1
+
+#define HCSECD_BUFFER_SIZE 512
+#define HCSECD_IDENT "hcsecd"
+#define HCSECD_PIDFILE "/var/run/" HCSECD_IDENT ".pid"
+#define HCSECD_KEYSFILE "/var/db/" HCSECD_IDENT ".keys"
+
+struct link_key
+{
+ bdaddr_t bdaddr; /* remote device BDADDR */
+ char *name; /* remote device name */
+ uint8_t *key; /* link key (or NULL if no key) */
+ char *pin; /* pin (or NULL if no pin) */
+ LIST_ENTRY(link_key) next; /* link to the next */
+};
+typedef struct link_key link_key_t;
+typedef struct link_key * link_key_p;
+
+extern char *config_file;
+
+#if __config_debug__
+void dump_config (void);
+#endif
+
+void read_config_file(void);
+void clean_config (void);
+link_key_p get_key (bdaddr_p bdaddr, int exact_match);
+
+int read_keys_file (void);
+int dump_keys_file (void);
+
+#endif /* ndef _HCSECD_H_ */
+
diff --git a/usr.sbin/bluetooth/hcsecd/lexer.l b/usr.sbin/bluetooth/hcsecd/lexer.l
new file mode 100644
index 0000000..2430d7e
--- /dev/null
+++ b/usr.sbin/bluetooth/hcsecd/lexer.l
@@ -0,0 +1,95 @@
+%{
+/*
+ * lexer.l
+ *
+ * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: lexer.l,v 1.1 2002/11/24 20:22:39 max Exp $
+ * $FreeBSD$
+ */
+
+#include <string.h>
+#include "parser.h"
+%}
+
+%option yylineno noyywrap nounput
+
+delim [ \t\n]
+ws {delim}+
+empty {delim}*
+comment \#.*
+
+hexdigit [0-9a-fA-F]
+hexbyte {hexdigit}{hexdigit}
+
+device_word device
+bdaddr_word bdaddr
+name_word name
+key_word key
+nokey_word nokey
+pin_word pin
+nopin_word nopin
+
+bdaddrstring {hexbyte}:{hexbyte}:{hexbyte}:{hexbyte}:{hexbyte}:{hexbyte}
+hexstring 0x{hexbyte}+
+string \".+\"
+
+%%
+
+\; return (';');
+\: return (':');
+\{ return ('{');
+\} return ('}');
+
+{ws} ;
+{empty} ;
+{comment} ;
+
+{device_word} return (T_DEVICE);
+{bdaddr_word} return (T_BDADDR);
+{name_word} return (T_NAME);
+{key_word} return (T_KEY);
+{nokey_word} return (T_NOKEY);
+{pin_word} return (T_PIN);
+{nopin_word} return (T_NOPIN);
+
+{bdaddrstring} {
+ yylval.string = yytext;
+ return (T_BDADDRSTRING);
+ }
+
+{hexstring} {
+ yylval.string = &yytext[2];
+ return (T_HEXSTRING);
+ }
+
+{string} {
+ yytext[strlen(yytext) - 1] = 0;
+ yylval.string = &yytext[1];
+ return (T_STRING);
+ }
+
+%%
+
diff --git a/usr.sbin/bluetooth/hcsecd/parser.y b/usr.sbin/bluetooth/hcsecd/parser.y
new file mode 100644
index 0000000..385bff2
--- /dev/null
+++ b/usr.sbin/bluetooth/hcsecd/parser.y
@@ -0,0 +1,433 @@
+%{
+/*
+ * parser.y
+ *
+ * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: parser.y,v 1.5 2003/06/07 21:22:30 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/fcntl.h>
+#include <sys/queue.h>
+#include <bluetooth.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include "hcsecd.h"
+
+ int yyparse (void);
+ int yylex (void);
+
+static void free_key (link_key_p key);
+static int hexa2int4(char *a);
+static int hexa2int8(char *a);
+
+extern int yylineno;
+static LIST_HEAD(, link_key) link_keys;
+ char *config_file = "/etc/bluetooth/hcsecd.conf";
+
+static link_key_p key = NULL;
+%}
+
+%union {
+ char *string;
+}
+
+%token <string> T_BDADDRSTRING T_HEXSTRING T_STRING
+%token T_DEVICE T_BDADDR T_NAME T_KEY T_PIN T_NOKEY T_NOPIN T_JUNK
+
+%%
+
+config: line
+ | config line
+ ;
+
+line: T_DEVICE
+ {
+ key = (link_key_p) malloc(sizeof(*key));
+ if (key == NULL) {
+ syslog(LOG_ERR, "Could not allocate new " \
+ "config entry");
+ exit(1);
+ }
+
+ memset(key, 0, sizeof(*key));
+ }
+ '{' options '}'
+ {
+ if (get_key(&key->bdaddr, 1) != NULL) {
+ syslog(LOG_ERR, "Ignoring duplicated entry " \
+ "for bdaddr %s",
+ bt_ntoa(&key->bdaddr, NULL));
+ free_key(key);
+ } else
+ LIST_INSERT_HEAD(&link_keys, key, next);
+
+ key = NULL;
+ }
+ ;
+
+options: option ';'
+ | options option ';'
+ ;
+
+option: bdaddr
+ | name
+ | key
+ | pin
+ ;
+
+bdaddr: T_BDADDR T_BDADDRSTRING
+ {
+ if (!bt_aton($2, &key->bdaddr)) {
+ syslog(LOG_ERR, "Cound not parse BD_ADDR " \
+ "'%s'", $2);
+ exit(1);
+ }
+ }
+ ;
+
+name: T_NAME T_STRING
+ {
+ if (key->name != NULL)
+ free(key->name);
+
+ key->name = strdup($2);
+ if (key->name == NULL) {
+ syslog(LOG_ERR, "Could not allocate new " \
+ "device name");
+ exit(1);
+ }
+ }
+ ;
+
+key: T_KEY T_HEXSTRING
+ {
+ int i, len;
+
+ if (key->key != NULL)
+ free(key->key);
+
+ key->key = (uint8_t *) malloc(NG_HCI_KEY_SIZE);
+ if (key->key == NULL) {
+ syslog(LOG_ERR, "Could not allocate new " \
+ "link key");
+ exit(1);
+ }
+
+ memset(key->key, 0, NG_HCI_KEY_SIZE);
+
+ len = strlen($2) / 2;
+ if (len > NG_HCI_KEY_SIZE)
+ len = NG_HCI_KEY_SIZE;
+
+ for (i = 0; i < len; i ++)
+ key->key[i] = hexa2int8((char *)($2) + 2*i);
+ }
+ | T_KEY T_NOKEY
+ {
+ if (key->key != NULL)
+ free(key->key);
+
+ key->key = NULL;
+ }
+ ;
+
+pin: T_PIN T_STRING
+ {
+ if (key->pin != NULL)
+ free(key->pin);
+
+ key->pin = strdup($2);
+ if (key->pin == NULL) {
+ syslog(LOG_ERR, "Could not allocate new " \
+ "PIN code");
+ exit(1);
+ }
+ }
+ | T_PIN T_NOPIN
+ {
+ if (key->pin != NULL)
+ free(key->pin);
+
+ key->pin = NULL;
+ }
+ ;
+
+%%
+
+/* Display parser error message */
+void
+yyerror(char const *message)
+{
+ syslog(LOG_ERR, "%s in line %d", message, yylineno);
+}
+
+/* Re-read config file */
+void
+read_config_file(void)
+{
+ extern FILE *yyin;
+
+ if (config_file == NULL) {
+ syslog(LOG_ERR, "Unknown config file name!");
+ exit(1);
+ }
+
+ if ((yyin = fopen(config_file, "r")) == NULL) {
+ syslog(LOG_ERR, "Could not open config file '%s'. %s (%d)",
+ config_file, strerror(errno), errno);
+ exit(1);
+ }
+
+ clean_config();
+ if (yyparse() < 0) {
+ syslog(LOG_ERR, "Could not parse config file '%s'",config_file);
+ exit(1);
+ }
+
+ fclose(yyin);
+ yyin = NULL;
+
+#if __config_debug__
+ dump_config();
+#endif
+}
+
+/* Clean config */
+void
+clean_config(void)
+{
+ link_key_p key = NULL;
+
+ while ((key = LIST_FIRST(&link_keys)) != NULL) {
+ LIST_REMOVE(key, next);
+ free_key(key);
+ }
+}
+
+/* Find link key entry in the list. Return exact or default match */
+link_key_p
+get_key(bdaddr_p bdaddr, int exact_match)
+{
+ link_key_p key = NULL, defkey = NULL;
+
+ LIST_FOREACH(key, &link_keys, next) {
+ if (memcmp(bdaddr, &key->bdaddr, sizeof(key->bdaddr)) == 0)
+ break;
+
+ if (!exact_match)
+ if (memcmp(NG_HCI_BDADDR_ANY, &key->bdaddr,
+ sizeof(key->bdaddr)) == 0)
+ defkey = key;
+ }
+
+ return ((key != NULL)? key : defkey);
+}
+
+#if __config_debug__
+/* Dump config */
+void
+dump_config(void)
+{
+ link_key_p key = NULL;
+ char buffer[64];
+
+ LIST_FOREACH(key, &link_keys, next) {
+ if (key->key != NULL)
+ snprintf(buffer, sizeof(buffer),
+"0x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
+ key->key[0], key->key[1], key->key[2],
+ key->key[3], key->key[4], key->key[5],
+ key->key[6], key->key[7], key->key[8],
+ key->key[9], key->key[10], key->key[11],
+ key->key[12], key->key[13], key->key[14],
+ key->key[15]);
+
+ syslog(LOG_DEBUG,
+"device %s " \
+"bdaddr %s " \
+"pin %s " \
+"key %s",
+ (key->name != NULL)? key->name : "noname",
+ bt_ntoa(&key->bdaddr, NULL),
+ (key->pin != NULL)? key->pin : "nopin",
+ (key->key != NULL)? buffer : "nokey");
+ }
+}
+#endif
+
+/* Read keys file */
+int
+read_keys_file(void)
+{
+ FILE *f = NULL;
+ link_key_t *key = NULL;
+ char buf[HCSECD_BUFFER_SIZE], *p = NULL, *cp = NULL;
+ bdaddr_t bdaddr;
+ int i, len;
+
+ if ((f = fopen(HCSECD_KEYSFILE, "r")) == NULL) {
+ if (errno == ENOENT)
+ return (0);
+
+ syslog(LOG_ERR, "Could not open keys file %s. %s (%d)\n",
+ HCSECD_KEYSFILE, strerror(errno), errno);
+
+ return (-1);
+ }
+
+ while ((p = fgets(buf, sizeof(buf), f)) != NULL) {
+ if (*p == '#')
+ continue;
+ if ((cp = strpbrk(p, " ")) == NULL)
+ continue;
+
+ *cp++ = '\0';
+
+ if (!bt_aton(p, &bdaddr))
+ continue;
+
+ if ((key = get_key(&bdaddr, 1)) == NULL)
+ continue;
+
+ if (key->key == NULL) {
+ key->key = (uint8_t *) malloc(NG_HCI_KEY_SIZE);
+ if (key->key == NULL) {
+ syslog(LOG_ERR, "Could not allocate link key");
+ exit(1);
+ }
+ }
+
+ memset(key->key, 0, NG_HCI_KEY_SIZE);
+
+ len = strlen(cp) / 2;
+ if (len > NG_HCI_KEY_SIZE)
+ len = NG_HCI_KEY_SIZE;
+
+ for (i = 0; i < len; i ++)
+ key->key[i] = hexa2int8(cp + 2*i);
+
+ syslog(LOG_DEBUG, "Restored link key for the entry, " \
+ "remote bdaddr %s, name '%s'",
+ bt_ntoa(&key->bdaddr, NULL),
+ (key->name != NULL)? key->name : "No name");
+ }
+
+ fclose(f);
+
+ return (0);
+}
+
+/* Dump keys file */
+int
+dump_keys_file(void)
+{
+ link_key_p key = NULL;
+ char tmp[PATH_MAX], buf[HCSECD_BUFFER_SIZE];
+ int f;
+
+ snprintf(tmp, sizeof(tmp), "%s.tmp", HCSECD_KEYSFILE);
+ if ((f = open(tmp, O_RDWR|O_CREAT|O_TRUNC|O_EXCL, 0600)) < 0) {
+ syslog(LOG_ERR, "Could not create temp keys file %s. %s (%d)\n",
+ tmp, strerror(errno), errno);
+ return (-1);
+ }
+
+ LIST_FOREACH(key, &link_keys, next) {
+ if (key->key == NULL)
+ continue;
+
+ snprintf(buf, sizeof(buf),
+"%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
+ bt_ntoa(&key->bdaddr, NULL),
+ key->key[0], key->key[1], key->key[2], key->key[3],
+ key->key[4], key->key[5], key->key[6], key->key[7],
+ key->key[8], key->key[9], key->key[10], key->key[11],
+ key->key[12], key->key[13], key->key[14], key->key[15]);
+
+ if (write(f, buf, strlen(buf)) < 0) {
+ syslog(LOG_ERR, "Could not write temp keys file. " \
+ "%s (%d)\n", strerror(errno), errno);
+ break;
+ }
+ }
+
+ close(f);
+
+ if (rename(tmp, HCSECD_KEYSFILE) < 0) {
+ syslog(LOG_ERR, "Could not rename(%s, %s). %s (%d)\n",
+ tmp, HCSECD_KEYSFILE, strerror(errno), errno);
+ unlink(tmp);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/* Free key entry */
+static void
+free_key(link_key_p key)
+{
+ if (key->name != NULL)
+ free(key->name);
+ if (key->key != NULL)
+ free(key->key);
+ if (key->pin != NULL)
+ free(key->pin);
+
+ memset(key, 0, sizeof(*key));
+ free(key);
+}
+
+/* Convert hex ASCII to int4 */
+static int
+hexa2int4(char *a)
+{
+ if ('0' <= *a && *a <= '9')
+ return (*a - '0');
+
+ if ('A' <= *a && *a <= 'F')
+ return (*a - 'A' + 0xa);
+
+ if ('a' <= *a && *a <= 'f')
+ return (*a - 'a' + 0xa);
+
+ syslog(LOG_ERR, "Invalid hex character: '%c' (%#x)", *a, *a);
+ exit(1);
+}
+
+/* Convert hex ASCII to int8 */
+static int
+hexa2int8(char *a)
+{
+ return ((hexa2int4(a) << 4) | hexa2int4(a + 1));
+}
+
diff --git a/usr.sbin/bluetooth/hcseriald/Makefile b/usr.sbin/bluetooth/hcseriald/Makefile
new file mode 100644
index 0000000..ed0bf32
--- /dev/null
+++ b/usr.sbin/bluetooth/hcseriald/Makefile
@@ -0,0 +1,11 @@
+# $Id: Makefile,v 1.5 2003/08/14 20:06:21 max Exp $
+# $FreeBSD$
+
+PROG= hcseriald
+MAN= hcseriald.8
+WARNS?= 2
+
+DPADD= ${LIBNETGRAPH}
+LDADD= -lnetgraph
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bluetooth/hcseriald/hcseriald.8 b/usr.sbin/bluetooth/hcseriald/hcseriald.8
new file mode 100644
index 0000000..111b28c
--- /dev/null
+++ b/usr.sbin/bluetooth/hcseriald/hcseriald.8
@@ -0,0 +1,86 @@
+.\" Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: hcseriald.8,v 1.3 2003/05/21 00:47:26 max Exp $
+.\" $FreeBSD$
+.\"
+.Dd June 14, 2002
+.Dt HCSERIALD 8
+.Os
+.Sh NAME
+.Nm hcseriald
+.Nd supervise serial Bluetooth devices
+.Sh SYNOPSIS
+.Nm
+.Op Fl dh
+.Fl f Ar device
+.Fl n Ar node_name
+.Op Fl s Ar speed
+.Sh DESCRIPTION
+The
+.Nm
+utility handles serial Bluetooth devices.
+It does one simple thing:
+it opens the specified serial device, sets the device parameters, and pushes
+the
+.Dv H4
+line discipline.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl d
+Do not disassociate from the controlling terminal, i.e., run in foreground.
+.It Fl f Ar device
+Callout device name.
+Example:
+.Fl f Pa /dev/cuad0 .
+.It Fl h
+Display usage message and exit.
+.It Fl n Ar node_name
+Set H4 Netgraph node name.
+Example:
+.Fl n Li sio0 .
+.It Fl s Ar speed
+Set serial device speed to
+.Ar speed .
+Example:
+.Fl s Li 115200 .
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /var/run/hcserial. Ns Ar * Ns Pa .pid" -compact
+.It Pa /var/run/hcserial. Ns Ar * Ns Pa .pid
+Process ID of the currently running
+.Nm
+daemon.
+Where
+.Ar *
+is an H4 Netgraph node name.
+.El
+.Sh SEE ALSO
+.Xr ng_h4 4 ,
+.Xr ng_hci 4 ,
+.Xr tty 4 ,
+.Xr hccontrol 8
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
diff --git a/usr.sbin/bluetooth/hcseriald/hcseriald.c b/usr.sbin/bluetooth/hcseriald/hcseriald.c
new file mode 100644
index 0000000..b811c1d
--- /dev/null
+++ b/usr.sbin/bluetooth/hcseriald/hcseriald.c
@@ -0,0 +1,268 @@
+/*
+ * hcseriald.c
+ *
+ * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: hcseriald.c,v 1.3 2003/05/21 22:40:32 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <netgraph/ng_message.h>
+#include <netgraph.h>
+#include <netgraph/bluetooth/include/ng_h4.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <termios.h>
+#include <unistd.h>
+
+/* Prototypes */
+static int open_device (char const *, speed_t, char const *);
+static void sighandler (int);
+static void usage ();
+
+static char const * const hcseriald = "hcseriald";
+static int done = 0;
+
+int
+main(int argc, char *argv[])
+{
+ char *device = NULL, *name = NULL;
+ speed_t speed = 115200;
+ int n, detach = 1;
+ char p[FILENAME_MAX];
+ FILE *f = NULL;
+ struct sigaction sa;
+
+ /* Process command line arguments */
+ while ((n = getopt(argc, argv, "df:n:s:h")) != -1) {
+ switch (n) {
+ case 'd':
+ detach = 0;
+ break;
+
+ case 'f':
+ device = optarg;
+ break;
+
+ case 'n':
+ name = optarg;
+ break;
+
+ case 's':
+ speed = atoi(optarg);
+ if (speed < 0)
+ usage(argv[0]);
+ break;
+
+ case 'h':
+ default:
+ usage(argv[0]);
+ break;
+ }
+ }
+
+ if (device == NULL || name == NULL)
+ usage(argv[0]);
+
+ openlog(hcseriald, LOG_PID | LOG_NDELAY, LOG_USER);
+
+ /* Open device */
+ n = open_device(device, speed, name);
+
+ if (detach && daemon(0, 0) < 0) {
+ syslog(LOG_ERR, "Could not daemon(0, 0). %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ /* Write PID file */
+ snprintf(p, sizeof(p), "/var/run/%s.%s.pid", hcseriald, name);
+ f = fopen(p, "w");
+ if (f == NULL) {
+ syslog(LOG_ERR, "Could not fopen(%s). %s (%d)",
+ p, strerror(errno), errno);
+ exit(1);
+ }
+ fprintf(f, "%d", getpid());
+ fclose(f);
+
+ /* Install signal handler */
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = sighandler;
+
+ if (sigaction(SIGTERM, &sa, NULL) < 0) {
+ syslog(LOG_ERR, "Could not sigaction(SIGTERM). %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ if (sigaction(SIGHUP, &sa, NULL) < 0) {
+ syslog(LOG_ERR, "Could not sigaction(SIGHUP). %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ if (sigaction(SIGINT, &sa, NULL) < 0) {
+ syslog(LOG_ERR, "Could not sigaction(SIGINT). %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ /* Keep running */
+ while (!done)
+ select(0, NULL, NULL, NULL, NULL);
+
+ /* Remove PID file and close device */
+ unlink(p);
+ close(n);
+ closelog();
+
+ return (0);
+} /* main */
+
+/* Open terminal, set settings, push H4 line discipline and set node name */
+static int
+open_device(char const *device, speed_t speed, char const *name)
+{
+ int fd, disc, cs, ds;
+ struct termios t;
+ struct nodeinfo ni;
+ struct ngm_name n;
+ char p[NG_NODESIZ];
+
+ /* Open terminal device and setup H4 line discipline */
+ fd = open(device, O_RDWR|O_NOCTTY);
+ if (fd < 0) {
+ syslog(LOG_ERR, "Could not open(%s). %s (%d)",
+ device, strerror(errno), errno);
+ exit(1);
+ }
+
+ tcflush(fd, TCIOFLUSH);
+
+ if (tcgetattr(fd, &t) < 0) {
+ syslog(LOG_ERR, "Could not tcgetattr(%s). %s (%d)",
+ device, strerror(errno), errno);
+ exit(1);
+ }
+
+ cfmakeraw(&t);
+
+ t.c_cflag |= CLOCAL; /* clocal */
+ t.c_cflag &= ~CSIZE; /* cs8 */
+ t.c_cflag |= CS8; /* cs8 */
+ t.c_cflag &= ~PARENB; /* -parenb */
+ t.c_cflag &= ~CSTOPB; /* -cstopb */
+ t.c_cflag |= CRTSCTS; /* crtscts */
+
+ if (tcsetattr(fd, TCSANOW, &t) < 0) {
+ syslog(LOG_ERR, "Could not tcsetattr(%s). %s (%d)",
+ device, strerror(errno), errno);
+ exit(1);
+ }
+
+ tcflush(fd, TCIOFLUSH);
+
+ if (cfsetspeed(&t, speed) < 0) {
+ syslog(LOG_ERR, "Could not cfsetspeed(%s). %s (%d)",
+ device, strerror(errno), errno);
+ exit(1);
+ }
+
+ if (tcsetattr(fd, TCSANOW, &t) < 0) {
+ syslog(LOG_ERR, "Could not tcsetattr(%s). %s (%d)",
+ device, strerror(errno), errno);
+ exit(1);
+ }
+
+ disc = H4DISC;
+ if (ioctl(fd, TIOCSETD, &disc) < 0) {
+ syslog(LOG_ERR, "Could not ioctl(%s, TIOCSETD, %d). %s (%d)",
+ device, disc, strerror(errno), errno);
+ exit(1);
+ }
+
+ /* Get default name of the Netgraph node */
+ memset(&ni, 0, sizeof(ni));
+ if (ioctl(fd, NGIOCGINFO, &ni) < 0) {
+ syslog(LOG_ERR, "Could not ioctl(%d, NGIOGINFO). %s (%d)",
+ fd, strerror(errno), errno);
+ exit(1);
+ }
+
+ /* Assign new name to the Netgraph node */
+ snprintf(p, sizeof(p), "%s:", ni.name);
+ snprintf(n.name, sizeof(n.name), "%s", name);
+
+ if (NgMkSockNode(NULL, &cs, &ds) < 0) {
+ syslog(LOG_ERR, "Could not NgMkSockNode(). %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ if (NgSendMsg(cs, p, NGM_GENERIC_COOKIE, NGM_NAME, &n, sizeof(n)) < 0) {
+ syslog(LOG_ERR, "Could not NgSendMsg(%d, %s, NGM_NAME, %s). " \
+ "%s (%d)", cs, p, n.name, strerror(errno), errno);
+ exit(1);
+ }
+
+ close(cs);
+ close(ds);
+
+ return (fd);
+} /* open_device */
+
+/* Signal handler */
+static void
+sighandler(int s)
+{
+ done = 1;
+} /* sighandler */
+
+/* Usage */
+static void
+usage(void)
+{
+ fprintf(stderr, "Usage: %s -f device -n node_name [-s speed -d -h]\n" \
+ "Where:\n" \
+ "\t-f device tty device name, ex. /dev/cuad1\n" \
+ "\t-n node_name set Netgraph node name to node_name\n" \
+ "\t-s speed set tty speed, ex. 115200\n" \
+ "\t-d run in foreground\n" \
+ "\t-h display this message\n",
+ hcseriald);
+ exit(255);
+} /* usage */
+
diff --git a/usr.sbin/bluetooth/l2control/Makefile b/usr.sbin/bluetooth/l2control/Makefile
new file mode 100644
index 0000000..847ff4b
--- /dev/null
+++ b/usr.sbin/bluetooth/l2control/Makefile
@@ -0,0 +1,12 @@
+# $Id: Makefile,v 1.7 2003/08/14 20:06:22 max Exp $
+# $FreeBSD$
+
+PROG= l2control
+MAN= l2control.8
+SRCS= l2cap.c l2control.c
+WARNS?= 2
+
+DPADD= ${LIBBLUETOOTH}
+LDADD= -lbluetooth
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bluetooth/l2control/l2cap.c b/usr.sbin/bluetooth/l2control/l2cap.c
new file mode 100644
index 0000000..c23106c
--- /dev/null
+++ b/usr.sbin/bluetooth/l2control/l2cap.c
@@ -0,0 +1,313 @@
+/*
+ * l2cap.c
+ *
+ * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: l2cap.c,v 1.5 2003/05/16 19:52:37 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/ioctl.h>
+#include <bluetooth.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "l2control.h"
+
+#define SIZE(x) (sizeof((x))/sizeof((x)[0]))
+
+/* Print BDADDR */
+static char *
+bdaddrpr(bdaddr_t const *ba)
+{
+ extern int numeric_bdaddr;
+ static char str[24];
+ struct hostent *he = NULL;
+
+ if (memcmp(ba, NG_HCI_BDADDR_ANY, sizeof(*ba)) == 0) {
+ str[0] = '*';
+ str[1] = 0;
+
+ return (str);
+ }
+
+ if (!numeric_bdaddr &&
+ (he = bt_gethostbyaddr((char *)ba, sizeof(*ba), AF_BLUETOOTH)) != NULL) {
+ strlcpy(str, he->h_name, sizeof(str));
+
+ return (str);
+ }
+
+ bt_ntoa(ba, str);
+
+ return (str);
+} /* bdaddrpr */
+
+/* Send read_node_flags command to the node */
+static int
+l2cap_read_node_flags(int s, int argc, char **argv)
+{
+ struct ng_btsocket_l2cap_raw_node_flags r;
+
+ memset(&r, 0, sizeof(r));
+ if (ioctl(s, SIOC_L2CAP_NODE_GET_FLAGS, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ fprintf(stdout, "Connectionless traffic flags:\n");
+ fprintf(stdout, "\tSDP: %s\n",
+ (r.flags & NG_L2CAP_CLT_SDP_DISABLED)? "disabled" : "enabled");
+ fprintf(stdout, "\tRFCOMM: %s\n",
+ (r.flags & NG_L2CAP_CLT_RFCOMM_DISABLED)? "disabled":"enabled");
+ fprintf(stdout, "\tTCP: %s\n",
+ (r.flags & NG_L2CAP_CLT_TCP_DISABLED)? "disabled" : "enabled");
+
+ return (OK);
+} /* l2cap_read_node_flags */
+
+/* Send read_debug_level command to the node */
+static int
+l2cap_read_debug_level(int s, int argc, char **argv)
+{
+ struct ng_btsocket_l2cap_raw_node_debug r;
+
+ memset(&r, 0, sizeof(r));
+ if (ioctl(s, SIOC_L2CAP_NODE_GET_DEBUG, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ fprintf(stdout, "Debug level: %d\n", r.debug);
+
+ return (OK);
+} /* l2cap_read_debug_level */
+
+/* Send write_debug_level command to the node */
+static int
+l2cap_write_debug_level(int s, int argc, char **argv)
+{
+ struct ng_btsocket_l2cap_raw_node_debug r;
+
+ memset(&r, 0, sizeof(r));
+ switch (argc) {
+ case 1:
+ r.debug = atoi(argv[0]);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ if (ioctl(s, SIOC_L2CAP_NODE_SET_DEBUG, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ return (OK);
+} /* l2cap_write_debug_level */
+
+/* Send read_connection_list command to the node */
+static int
+l2cap_read_connection_list(int s, int argc, char **argv)
+{
+ static char const * const state[] = {
+ /* NG_L2CAP_CON_CLOSED */ "CLOSED",
+ /* NG_L2CAP_W4_LP_CON_CFM */ "W4_LP_CON_CFM",
+ /* NG_L2CAP_CON_OPEN */ "OPEN"
+ };
+#define con_state2str(x) ((x) >= SIZE(state)? "UNKNOWN" : state[(x)])
+
+ struct ng_btsocket_l2cap_raw_con_list r;
+ int n, error = OK;
+
+ memset(&r, 0, sizeof(r));
+ r.num_connections = NG_L2CAP_MAX_CON_NUM;
+ r.connections = calloc(NG_L2CAP_MAX_CON_NUM,
+ sizeof(ng_l2cap_node_con_ep));
+ if (r.connections == NULL) {
+ errno = ENOMEM;
+ return (ERROR);
+ }
+
+ if (ioctl(s, SIOC_L2CAP_NODE_GET_CON_LIST, &r, sizeof(r)) < 0) {
+ error = ERROR;
+ goto out;
+ }
+
+ fprintf(stdout, "L2CAP connections:\n");
+ fprintf(stdout,
+"Remote BD_ADDR Handle Flags Pending State\n");
+ for (n = 0; n < r.num_connections; n++) {
+ fprintf(stdout,
+ "%-17.17s " \
+ "%6d " \
+ "%c%c%c%c%c " \
+ "%7d " \
+ "%s\n",
+ bdaddrpr(&r.connections[n].remote),
+ r.connections[n].con_handle,
+ ((r.connections[n].flags & NG_L2CAP_CON_OUTGOING)? 'O' : 'I'),
+ ((r.connections[n].flags & NG_L2CAP_CON_LP_TIMO)? 'L' : ' '),
+ ((r.connections[n].flags & NG_L2CAP_CON_AUTO_DISCON_TIMO)? 'D' : ' '),
+ ((r.connections[n].flags & NG_L2CAP_CON_TX)? 'T' : ' '),
+ ((r.connections[n].flags & NG_L2CAP_CON_RX)? 'R' : ' '),
+ r.connections[n].pending,
+ con_state2str(r.connections[n].state));
+ }
+out:
+ free(r.connections);
+
+ return (error);
+} /* l2cap_read_connection_list */
+
+/* Send read_channel_list command to the node */
+static int
+l2cap_read_channel_list(int s, int argc, char **argv)
+{
+ static char const * const state[] = {
+ /* NG_L2CAP_CLOSED */ "CLOSED",
+ /* NG_L2CAP_W4_L2CAP_CON_RSP */ "W4_L2CAP_CON_RSP",
+ /* NG_L2CAP_W4_L2CA_CON_RSP */ "W4_L2CA_CON_RSP",
+ /* NG_L2CAP_CONFIG */ "CONFIG",
+ /* NG_L2CAP_OPEN */ "OPEN",
+ /* NG_L2CAP_W4_L2CAP_DISCON_RSP */ "W4_L2CAP_DISCON_RSP",
+ /* NG_L2CAP_W4_L2CA_DISCON_RSP */ "W4_L2CA_DISCON_RSP"
+ };
+#define ch_state2str(x) ((x) >= SIZE(state)? "UNKNOWN" : state[(x)])
+
+ struct ng_btsocket_l2cap_raw_chan_list r;
+ int n, error = OK;
+
+ memset(&r, 0, sizeof(r));
+ r.num_channels = NG_L2CAP_MAX_CHAN_NUM;
+ r.channels = calloc(NG_L2CAP_MAX_CHAN_NUM,
+ sizeof(ng_l2cap_node_chan_ep));
+ if (r.channels == NULL) {
+ errno = ENOMEM;
+ return (ERROR);
+ }
+
+ if (ioctl(s, SIOC_L2CAP_NODE_GET_CHAN_LIST, &r, sizeof(r)) < 0) {
+ error = ERROR;
+ goto out;
+ }
+
+ fprintf(stdout, "L2CAP channels:\n");
+ fprintf(stdout,
+"Remote BD_ADDR SCID/ DCID PSM IMTU/ OMTU State\n");
+ for (n = 0; n < r.num_channels; n++) {
+ fprintf(stdout,
+ "%-17.17s " \
+ "%5d/%5d %5d " \
+ "%5d/%5d " \
+ "%s\n",
+ bdaddrpr(&r.channels[n].remote),
+ r.channels[n].scid, r.channels[n].dcid,
+ r.channels[n].psm, r.channels[n].imtu,
+ r.channels[n].omtu,
+ ch_state2str(r.channels[n].state));
+ }
+out:
+ free(r.channels);
+
+ return (error);
+} /* l2cap_read_channel_list */
+
+/* Send read_auto_disconnect_timeout command to the node */
+static int
+l2cap_read_auto_disconnect_timeout(int s, int argc, char **argv)
+{
+ struct ng_btsocket_l2cap_raw_auto_discon_timo r;
+
+ memset(&r, 0, sizeof(r));
+ if (ioctl(s, SIOC_L2CAP_NODE_GET_AUTO_DISCON_TIMO, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ if (r.timeout != 0)
+ fprintf(stdout, "Auto disconnect timeout: %d sec\n", r.timeout);
+ else
+ fprintf(stdout, "Auto disconnect disabled\n");
+
+ return (OK);
+} /* l2cap_read_auto_disconnect_timeout */
+
+/* Send write_auto_disconnect_timeout command to the node */
+static int
+l2cap_write_auto_disconnect_timeout(int s, int argc, char **argv)
+{
+ struct ng_btsocket_l2cap_raw_auto_discon_timo r;
+
+ memset(&r, 0, sizeof(r));
+ switch (argc) {
+ case 1:
+ r.timeout = atoi(argv[0]);
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ if (ioctl(s, SIOC_L2CAP_NODE_SET_AUTO_DISCON_TIMO, &r, sizeof(r)) < 0)
+ return (ERROR);
+
+ return (OK);
+} /* l2cap_write_auto_disconnect_timeout */
+
+struct l2cap_command l2cap_commands[] = {
+{
+"read_node_flags",
+"Get L2CAP node flags",
+&l2cap_read_node_flags
+},
+{
+"read_debug_level",
+"Get L2CAP node debug level",
+&l2cap_read_debug_level
+},
+{
+"write_debug_level <level>",
+"Set L2CAP node debug level",
+&l2cap_write_debug_level
+},
+{
+"read_connection_list",
+"Read list of the L2CAP connections",
+&l2cap_read_connection_list
+},
+{
+"read_channel_list",
+"Read list of the L2CAP channels",
+&l2cap_read_channel_list
+},
+{
+"read_auto_disconnect_timeout",
+"Get L2CAP node auto disconnect timeout (in sec)",
+&l2cap_read_auto_disconnect_timeout
+},
+{
+"write_auto_disconnect_timeout <timeout>",
+"Set L2CAP node auto disconnect timeout (in sec)",
+&l2cap_write_auto_disconnect_timeout
+},
+{
+NULL,
+}};
+
diff --git a/usr.sbin/bluetooth/l2control/l2control.8 b/usr.sbin/bluetooth/l2control/l2control.8
new file mode 100644
index 0000000..794b6a7
--- /dev/null
+++ b/usr.sbin/bluetooth/l2control/l2control.8
@@ -0,0 +1,95 @@
+.\" Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: l2control.8,v 1.5 2003/05/21 00:53:00 max Exp $
+.\" $FreeBSD$
+.\"
+.Dd June 14, 2002
+.Dt L2CONTROL 8
+.Os
+.Sh NAME
+.Nm l2control
+.Nd L2CAP configuration utility
+.Sh SYNOPSIS
+.Nm
+.Op Fl hn
+.Fl a Ar BD_ADDR
+.Ar command
+.Op Ar parameters ...
+.Sh DESCRIPTION
+The
+.Nm
+utility connects to the local device with the specified BD_ADDR and attempts
+to send the specified command.
+The
+.Nm
+utility will print results to the standard output and error messages to
+the standard error.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl a Ar BD_ADDR
+Connect to the local device with the specified BD_ADDR.
+Example:
+.Fl a Li 00:01:02:03:04:05 .
+.It Fl h
+Display usage message and exit.
+.It Fl n
+Show Bluetooth addresses as numbers.
+Normally
+.Nm
+attempts to resolve Bluetooth addresses, and display them symbolically.
+.It Ar command
+One of the supported commands (see below).
+Special command
+.Cm help
+can be used to obtain the list of all supported commands.
+To get more information about specific command use
+.Cm help Ar command .
+.It Ar parameters
+One or more optional space separated command parameters.
+.El
+.Sh COMMANDS
+The currently supported node commands in
+.Nm
+are:
+.Pp
+.Bl -tag -offset indent -compact
+.It Cm Read_Node_Flags
+.It Cm Read_Debug_Level
+.It Cm Write_Debug_Level
+.It Cm Read_Connection_List
+.It Cm Read_Channel_List
+.It Cm Read_Auto_Disconnect_Timeout
+.It Cm Write_Auto_Disconnect_Timeout
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr netgraph 3 ,
+.Xr netgraph 4 ,
+.Xr ng_l2cap 4 ,
+.Xr l2ping 8
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
diff --git a/usr.sbin/bluetooth/l2control/l2control.c b/usr.sbin/bluetooth/l2control/l2control.c
new file mode 100644
index 0000000..585021c
--- /dev/null
+++ b/usr.sbin/bluetooth/l2control/l2control.c
@@ -0,0 +1,213 @@
+/*
+ * l2control.c
+ *
+ * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: l2control.c,v 1.6 2003/09/05 00:38:25 max Exp $
+ * $FreeBSD$
+ */
+
+#include <assert.h>
+#include <bluetooth.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "l2control.h"
+
+/* Prototypes */
+static int do_l2cap_command (bdaddr_p, int, char **);
+static struct l2cap_command * find_l2cap_command (char const *,
+ struct l2cap_command *);
+static void print_l2cap_command (struct l2cap_command *);
+static void usage (void);
+
+/* Main */
+
+int numeric_bdaddr = 0;
+
+int
+main(int argc, char *argv[])
+{
+ int n;
+ bdaddr_t bdaddr;
+
+ memset(&bdaddr, 0, sizeof(bdaddr));
+
+ /* Process command line arguments */
+ while ((n = getopt(argc, argv, "a:nh")) != -1) {
+ switch (n) {
+ case 'a':
+ if (!bt_aton(optarg, &bdaddr)) {
+ struct hostent *he = NULL;
+
+ if ((he = bt_gethostbyname(optarg)) == NULL)
+ errx(1, "%s: %s", optarg, hstrerror(h_errno));
+
+ memcpy(&bdaddr, he->h_addr, sizeof(bdaddr));
+ }
+ break;
+
+ case 'n':
+ numeric_bdaddr = 1;
+ break;
+
+ case 'h':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (*argv == NULL)
+ usage();
+
+ return (do_l2cap_command(&bdaddr, argc, argv));
+} /* main */
+
+/* Execute commands */
+static int
+do_l2cap_command(bdaddr_p bdaddr, int argc, char **argv)
+{
+ char *cmd = argv[0];
+ struct l2cap_command *c = NULL;
+ struct sockaddr_l2cap sa;
+ int s, e, help;
+
+ help = 0;
+ if (strcasecmp(cmd, "help") == 0) {
+ argc --;
+ argv ++;
+
+ if (argc <= 0) {
+ fprintf(stdout, "Supported commands:\n");
+ print_l2cap_command(l2cap_commands);
+ fprintf(stdout, "\nFor more information use " \
+ "'help command'\n");
+
+ return (OK);
+ }
+
+ help = 1;
+ cmd = argv[0];
+ }
+
+ c = find_l2cap_command(cmd, l2cap_commands);
+ if (c == NULL) {
+ fprintf(stdout, "Unknown command: \"%s\"\n", cmd);
+ return (ERROR);
+ }
+
+ if (!help) {
+ if (memcmp(bdaddr, NG_HCI_BDADDR_ANY, sizeof(*bdaddr)) == 0)
+ usage();
+
+ memset(&sa, 0, sizeof(sa));
+ sa.l2cap_len = sizeof(sa);
+ sa.l2cap_family = AF_BLUETOOTH;
+ memcpy(&sa.l2cap_bdaddr, bdaddr, sizeof(sa.l2cap_bdaddr));
+
+ s = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_L2CAP);
+ if (s < 0)
+ err(1, "Could not create socket");
+
+ if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0)
+ err(2,
+"Could not bind socket, bdaddr=%s", bt_ntoa(&sa.l2cap_bdaddr, NULL));
+
+ e = 0x0ffff;
+ if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &e, sizeof(e)) < 0)
+ err(3, "Coult not setsockopt(RCVBUF, %d)", e);
+
+ e = (c->handler)(s, -- argc, ++ argv);
+
+ close(s);
+ } else
+ e = USAGE;
+
+ switch (e) {
+ case OK:
+ case FAILED:
+ break;
+
+ case ERROR:
+ fprintf(stdout, "Could not execute command \"%s\". %s\n",
+ cmd, strerror(errno));
+ break;
+
+ case USAGE:
+ fprintf(stdout, "Usage: %s\n%s\n", c->command, c->description);
+ break;
+
+ default: assert(0); break;
+ }
+
+ return (e);
+} /* do_l2cap_command */
+
+/* Try to find command in specified category */
+static struct l2cap_command *
+find_l2cap_command(char const *command, struct l2cap_command *category)
+{
+ struct l2cap_command *c = NULL;
+
+ for (c = category; c->command != NULL; c++) {
+ char *c_end = strchr(c->command, ' ');
+
+ if (c_end != NULL) {
+ int len = c_end - c->command;
+
+ if (strncasecmp(command, c->command, len) == 0)
+ return (c);
+ } else if (strcasecmp(command, c->command) == 0)
+ return (c);
+ }
+
+ return (NULL);
+} /* find_l2cap_command */
+
+/* Print commands in specified category */
+static void
+print_l2cap_command(struct l2cap_command *category)
+{
+ struct l2cap_command *c = NULL;
+
+ for (c = category; c->command != NULL; c++)
+ fprintf(stdout, "\t%s\n", c->command);
+} /* print_l2cap_command */
+
+/* Usage */
+static void
+usage(void)
+{
+ fprintf(stdout, "Usage: l2control -a BD_ADDR [-n] [-h] cmd [p1] [..]]\n");
+ exit(255);
+} /* usage */
+
diff --git a/usr.sbin/bluetooth/l2control/l2control.h b/usr.sbin/bluetooth/l2control/l2control.h
new file mode 100644
index 0000000..43d4561
--- /dev/null
+++ b/usr.sbin/bluetooth/l2control/l2control.h
@@ -0,0 +1,49 @@
+/*
+ * l2control.h
+ *
+ * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: l2control.h,v 1.1 2002/11/24 20:22:41 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _L2CONTROL_H_
+#define _L2CONTROL_H_
+
+#define OK 0 /* everything was OK */
+#define ERROR 1 /* could not execute command */
+#define FAILED 2 /* error was reported */
+#define USAGE 3 /* invalid parameters */
+
+struct l2cap_command {
+ char const *command;
+ char const *description;
+ int (*handler)(int, int, char **);
+};
+
+extern struct l2cap_command l2cap_commands[];
+
+#endif /* _L2CONTROL_H_ */
+
diff --git a/usr.sbin/bluetooth/l2ping/Makefile b/usr.sbin/bluetooth/l2ping/Makefile
new file mode 100644
index 0000000..bc130e3
--- /dev/null
+++ b/usr.sbin/bluetooth/l2ping/Makefile
@@ -0,0 +1,11 @@
+# $Id: Makefile,v 1.6 2003/08/14 20:06:24 max Exp $
+# $FreeBSD$
+
+PROG= l2ping
+MAN= l2ping.8
+WARNS?= 2
+
+DPADD= ${LIBBLUETOOTH}
+LDADD= -lbluetooth
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bluetooth/l2ping/l2ping.8 b/usr.sbin/bluetooth/l2ping/l2ping.8
new file mode 100644
index 0000000..477f4ec
--- /dev/null
+++ b/usr.sbin/bluetooth/l2ping/l2ping.8
@@ -0,0 +1,115 @@
+.\" Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: l2ping.8,v 1.3 2003/05/21 01:00:19 max Exp $
+.\" $FreeBSD$
+.\"
+.Dd June 14, 2002
+.Dt L2PING 8
+.Os
+.Sh NAME
+.Nm l2ping
+.Nd send L2CAP ECHO_REQUEST to remote devices
+.Sh SYNOPSIS
+.Nm
+.Op Fl fhn
+.Fl a Ar remote
+.Op Fl c Ar count
+.Op Fl i Ar delay
+.Op Fl S Ar source
+.Op Fl s Ar size
+.Sh DESCRIPTION
+The
+.Nm
+utility uses L2CAP
+.Dv ECHO_REQUEST
+datagram to elicit an L2CAP
+.Dv ECHO_RESPONSE
+datagram from a remote device.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl a Ar remote
+Specify the remote device to ping.
+The remote device can be specified by either its BD_ADDR or name.
+If name was specified then the
+.Nm
+utility will attempt to resolve the name via
+.Xr bt_gethostbyname 3 .
+.It Fl c Ar count
+Number of packets to send.
+If this option is not specified,
+.Nm
+will operate until interrupted.
+.It Fl f
+.Dq Flood
+ping, i.e., no delay between packets.
+.It Fl h
+Display usage message and exit.
+.It Fl i Ar wait
+Wait
+.Ar wait
+seconds between sending each packet.
+The default is to wait for one second between each packet.
+This option is ignored if
+.Fl f
+has been specified.
+.It Fl n
+Numeric output only.
+No attempt will be made to look up symbolic names for host addresses.
+.It Fl S Ar source
+Specify the local device which should be used to send L2CAP
+.Dv ECHO_REQUEST
+datagrams.
+The local device can be specified by either its BD_ADDR or name.
+If name was specified then the
+.Nm
+utility will attempt to resolve the name via
+.Xr bt_gethostbyname 3 .
+.It Fl s Ar size
+Specify the number of payload bytes to be sent.
+The default size is 44 bytes.
+It is calculated as minimum L2CAP MTU (48 bytes) minus the size of the L2CAP
+signalling command header (4 bytes).
+The maximum size is 65531 bytes.
+Is is calculated as maximum L2CAP MTU
+(65535 bytes) minus four bytes of payload reserved for
+.Nm
+internal use.
+Use this option with caution.
+Some implementations may not like large sizes and may hang or even crash.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr bluetooth 3 ,
+.Xr netgraph 3 ,
+.Xr netgraph 4 ,
+.Xr ng_l2cap 4 ,
+.Xr l2control 8
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
+.Sh BUGS
+Could collect more statistic.
+Could check for duplicated, corrupted and lost packets.
diff --git a/usr.sbin/bluetooth/l2ping/l2ping.c b/usr.sbin/bluetooth/l2ping/l2ping.c
new file mode 100644
index 0000000..d7e1b1e
--- /dev/null
+++ b/usr.sbin/bluetooth/l2ping/l2ping.c
@@ -0,0 +1,290 @@
+/*
+ * l2ping.c
+ *
+ * Copyright (c) 2001-2002 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: l2ping.c,v 1.5 2003/05/16 19:54:40 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <assert.h>
+#include <bluetooth.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static void usage (void);
+static void tv_sub (struct timeval *, struct timeval const *);
+static double tv2msec (struct timeval const *);
+
+#undef min
+#define min(x, y) (((x) > (y))? (y) : (x))
+
+static char const pattern[] = "1234567890-";
+#define PATTERN_SIZE (sizeof(pattern) - 1)
+
+/*
+ * Main
+ */
+
+int
+main(int argc, char *argv[])
+{
+ bdaddr_t src, dst;
+ struct hostent *he = NULL;
+ uint8_t *echo_data = NULL;
+ struct sockaddr_l2cap sa;
+ int32_t n, s, count, wait, flood, echo_size, numeric;
+ char *rname = NULL;
+
+ /* Set defaults */
+ memcpy(&src, NG_HCI_BDADDR_ANY, sizeof(src));
+ memcpy(&dst, NG_HCI_BDADDR_ANY, sizeof(dst));
+
+ echo_data = (uint8_t *) calloc(NG_L2CAP_MAX_ECHO_SIZE, sizeof(uint8_t));
+ if (echo_data == NULL) {
+ fprintf(stderr, "Failed to allocate echo data buffer");
+ exit(1);
+ }
+
+ /*
+ * Set default echo size to the NG_L2CAP_MTU_MINIMUM minus
+ * the size of the L2CAP signalling command header.
+ */
+
+ echo_size = NG_L2CAP_MTU_MINIMUM - sizeof(ng_l2cap_cmd_hdr_t);
+ count = -1; /* unimited */
+ wait = 1; /* sec */
+ flood = 0;
+ numeric = 0;
+
+ /* Parse command line arguments */
+ while ((n = getopt(argc, argv, "a:c:fi:nS:s:h")) != -1) {
+ switch (n) {
+ case 'a':
+ if (!bt_aton(optarg, &dst)) {
+ if ((he = bt_gethostbyname(optarg)) == NULL)
+ errx(1, "%s: %s", optarg, hstrerror(h_errno));
+
+ memcpy(&dst, he->h_addr, sizeof(dst));
+ }
+ break;
+
+ case 'c':
+ count = atoi(optarg);
+ if (count <= 0)
+ usage();
+ break;
+
+ case 'f':
+ flood = 1;
+ break;
+
+ case 'i':
+ wait = atoi(optarg);
+ if (wait <= 0)
+ usage();
+ break;
+
+ case 'n':
+ numeric = 1;
+ break;
+
+ case 'S':
+ if (!bt_aton(optarg, &src)) {
+ if ((he = bt_gethostbyname(optarg)) == NULL)
+ errx(1, "%s: %s", optarg, hstrerror(h_errno));
+
+ memcpy(&src, he->h_addr, sizeof(src));
+ }
+ break;
+
+ case 's':
+ echo_size = atoi(optarg);
+ if (echo_size < sizeof(int32_t) ||
+ echo_size > NG_L2CAP_MAX_ECHO_SIZE)
+ usage();
+ break;
+
+ case 'h':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if (memcmp(&dst, NG_HCI_BDADDR_ANY, sizeof(dst)) == 0)
+ usage();
+
+ he = bt_gethostbyaddr((const char *)&dst, sizeof(dst), AF_BLUETOOTH);
+ if (he == NULL || he->h_name == NULL || he->h_name[0] == '\0' || numeric)
+ asprintf(&rname, "%s", bt_ntoa(&dst, NULL));
+ else
+ rname = strdup(he->h_name);
+
+ if (rname == NULL)
+ errx(1, "Failed to create remote hostname");
+
+ s = socket(PF_BLUETOOTH, SOCK_RAW, BLUETOOTH_PROTO_L2CAP);
+ if (s < 0)
+ err(2, "Could not create socket");
+
+ memset(&sa, 0, sizeof(sa));
+ sa.l2cap_len = sizeof(sa);
+ sa.l2cap_family = AF_BLUETOOTH;
+ memcpy(&sa.l2cap_bdaddr, &src, sizeof(sa.l2cap_bdaddr));
+
+ if (bind(s, (struct sockaddr *) &sa, sizeof(sa)) < 0)
+ err(3,
+"Could not bind socket, src bdaddr=%s", bt_ntoa(&sa.l2cap_bdaddr, NULL));
+
+ memset(&sa, 0, sizeof(sa));
+ sa.l2cap_len = sizeof(sa);
+ sa.l2cap_family = AF_BLUETOOTH;
+ memcpy(&sa.l2cap_bdaddr, &dst, sizeof(sa.l2cap_bdaddr));
+
+ if (connect(s, (struct sockaddr *) &sa, sizeof(sa)) < 0)
+ err(4,
+"Could not connect socket, dst bdaddr=%s", bt_ntoa(&sa.l2cap_bdaddr, NULL));
+
+ /* Fill pattern */
+ for (n = 0; n < echo_size; ) {
+ int32_t avail = min(echo_size - n, PATTERN_SIZE);
+
+ memcpy(echo_data + n, pattern, avail);
+ n += avail;
+ }
+
+ /* Start ping'ing */
+ for (n = 0; count == -1 || count > 0; n ++) {
+ struct ng_btsocket_l2cap_raw_ping r;
+ struct timeval a, b;
+ int32_t fail;
+
+ if (gettimeofday(&a, NULL) < 0)
+ err(5, "Could not gettimeofday(a)");
+
+ fail = 0;
+ *((int32_t *) echo_data) = htonl(n);
+
+ r.result = 0;
+ r.echo_size = echo_size;
+ r.echo_data = echo_data;
+ if (ioctl(s, SIOC_L2CAP_L2CA_PING, &r, sizeof(r)) < 0) {
+ r.result = errno;
+ fail = 1;
+/*
+ warn("Could not ping, dst bdaddr=%s",
+ bt_ntoa(&r.echo_dst, NULL));
+*/
+ }
+
+ if (gettimeofday(&b, NULL) < 0)
+ err(7, "Could not gettimeofday(b)");
+
+ tv_sub(&b, &a);
+
+ fprintf(stdout,
+"%d bytes from %s seq_no=%d time=%.3f ms result=%#x %s\n",
+ r.echo_size,
+ rname,
+ ntohl(*((int32_t *)(r.echo_data))),
+ tv2msec(&b), r.result,
+ ((fail == 0)? "" : strerror(errno)));
+
+ if (!flood) {
+ /* Wait */
+ a.tv_sec = wait;
+ a.tv_usec = 0;
+ select(0, NULL, NULL, NULL, &a);
+ }
+
+ if (count != -1)
+ count --;
+ }
+
+ free(rname);
+ free(echo_data);
+ close(s);
+
+ return (0);
+} /* main */
+
+/*
+ * a -= b, for timevals
+ */
+
+static void
+tv_sub(struct timeval *a, struct timeval const *b)
+{
+ if (a->tv_usec < b->tv_usec) {
+ a->tv_usec += 1000000;
+ a->tv_sec -= 1;
+ }
+
+ a->tv_usec -= b->tv_usec;
+ a->tv_sec -= b->tv_sec;
+} /* tv_sub */
+
+/*
+ * convert tv to msec
+ */
+
+static double
+tv2msec(struct timeval const *tvp)
+{
+ return(((double)tvp->tv_usec)/1000.0 + ((double)tvp->tv_sec)*1000.0);
+} /* tv2msec */
+
+/*
+ * Usage
+ */
+
+static void
+usage(void)
+{
+ fprintf(stderr, "Usage: l2ping -a bd_addr " \
+ "[-S bd_addr -c count -i wait -n -s size -h]\n");
+ fprintf(stderr, "Where:\n");
+ fprintf(stderr, " -a remote Specify remote device to ping\n");
+ fprintf(stderr, " -c count Number of packets to send\n");
+ fprintf(stderr, " -f No delay (sort of flood)\n");
+ fprintf(stderr, " -h Display this message\n");
+ fprintf(stderr, " -i wait Delay between packets (sec)\n");
+ fprintf(stderr, " -n Numeric output only\n");
+ fprintf(stderr, " -S source Specify source device\n");
+ fprintf(stderr, " -s size Packet size (bytes), " \
+ "between %zd and %zd\n", sizeof(int32_t), NG_L2CAP_MAX_ECHO_SIZE);
+
+ exit(255);
+} /* usage */
+
diff --git a/usr.sbin/bluetooth/rfcomm_pppd/Makefile b/usr.sbin/bluetooth/rfcomm_pppd/Makefile
new file mode 100644
index 0000000..adac64e
--- /dev/null
+++ b/usr.sbin/bluetooth/rfcomm_pppd/Makefile
@@ -0,0 +1,14 @@
+# $Id: Makefile,v 1.7 2003/09/07 18:32:11 max Exp $
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../usr.bin/bluetooth/rfcomm_sppd
+
+PROG= rfcomm_pppd
+MAN= rfcomm_pppd.8
+SRCS= rfcomm_pppd.c rfcomm_sdp.c
+WARNS?= 2
+
+DPADD= ${LIBBLUETOOTH} ${LIBSDP}
+LDADD= -lbluetooth -lsdp
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bluetooth/rfcomm_pppd/rfcomm_pppd.8 b/usr.sbin/bluetooth/rfcomm_pppd/rfcomm_pppd.8
new file mode 100644
index 0000000..e74e113
--- /dev/null
+++ b/usr.sbin/bluetooth/rfcomm_pppd/rfcomm_pppd.8
@@ -0,0 +1,354 @@
+.\" Copyright (c) 2001-2003 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: rfcomm_pppd.8,v 1.7 2003/09/07 18:32:11 max Exp $
+.\" $FreeBSD$
+.\"
+.Dd February 4, 2003
+.Dt RFCOMM_PPPD 8
+.Os
+.Sh NAME
+.Nm rfcomm_pppd
+.Nd RFCOMM PPP daemon
+.Sh SYNOPSIS
+.Nm
+.Fl c
+.Op Fl dh
+.Fl a Ar address
+.Fl C Ar channel
+.Fl l Ar label
+.Fl u Ar N
+.Nm
+.Fl s
+.Op Fl dDhS
+.Op Fl a Ar address
+.Fl C Ar channel
+.Fl l Ar label
+.Sh DESCRIPTION
+The
+.Nm
+daemon is a simple wrapper daemon that allows the use of
+.Xr ppp 8
+via an RFCOMM connection.
+It can operate in two modes: client and server.
+.Pp
+In client mode,
+.Nm
+opens an RFCOMM connection to the specified server's
+.Ar BD_ADRR
+and
+.Ar channel .
+Once the RFCOMM connection is established,
+.Nm
+executes
+.Xr ppp 8
+in
+.Fl direct
+mode with the specified
+.Ar label .
+Likewise,
+.Xr ppp 8
+operates over the RFCOMM connection just like it would over a standard serial
+port, thus allowing a user to
+.Dq "dial out"
+and connect to the Internet.
+.Pp
+In server mode,
+.Nm
+opens an RFCOMM socket and listens for incoming connections from remote clients.
+Once the new incoming connection is accepted,
+.Nm
+forks and executes
+.Xr ppp 8
+in
+.Fl direct
+mode with the specified
+.Ar label .
+Likewise,
+.Xr ppp 8
+operates over the RFCOMM connection just like it would over a standard serial
+port, thus providing network connectivity to remote clients.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl a Ar address
+In client mode, this required option specifies the address of the remote
+RFCOMM server.
+In server mode, this option can be used to specify the local
+address to listen on.
+By default, in server mode, the daemon will listen on
+.Dv ANY
+address.
+The address can be specified as BD_ADDR or name.
+If a name was specified, the
+.Nm
+utility will attempt to resolve the name via
+.Xr bt_gethostbyname 3 .
+.It Fl C Ar channel
+In both client and server mode, this required option specifies the RFCOMM
+channel to connect to or listen on.
+In server mode, the channel should be a number between 1 and 30.
+In client mode, the channel could either be a number between 1 and 30
+or a service name.
+Supported service names are:
+.Cm DUN
+(Dial-Up Networking) and
+.Cm LAN
+(LAN Access Using PPP).
+If a service name is used instead of a numeric channel number, then
+.Nm
+will try to obtain an RFCOMM channel number via SDP
+(Service Discovery Protocol).
+.It Fl c
+Act as an RFCOMM client.
+This is the default mode.
+.It Fl d
+Do not detach from the controlling terminal, i.e., run in foreground.
+.It Fl D
+In server mode, register the
+.Cm DUN
+(Dial-Up Networking) service in addition to the
+.Cm LAN
+(LAN Access Using PPP) service.
+AT-command exchange can be faked with
+.Xr ppp 8
+chat script.
+.It Fl h
+Display usage message and exit.
+.It Fl l Ar label
+In both client and server mode, this required option specifies which
+.Xr ppp 8
+label will be used.
+.It Fl S
+In server mode, register the
+.Cm SP
+(Serial Port) service in addition to the
+.Cm LAN
+(LAN Access Using PPP) service.
+.Pp
+It appears that some cell phones are using the so-called
+.Dq "callback mechanism" .
+In this scenario, the user is trying to connect his cell phone to the Internet,
+while the user's host computer is acting as the gateway server.
+It seems that it is not possible to tell the phone to just connect and start
+using the
+.Cm LAN
+service.
+Instead, the user's host computer must
+.Dq "jump start"
+the phone by connecting to the phone's
+.Cm SP
+service.
+What happens next is the phone kills the existing connection and opens another
+connection back to the user's host computer.
+The phone really wants to use the
+.Cm LAN
+service, but for whatever reason it looks for the
+.Cm SP
+service on the user's host computer.
+This brain-damaged behavior was reported for the Nokia 6600 and the
+Sony/Ericsson P900.
+.It Fl s
+Act as an RFCOMM server.
+.It Fl u Ar N
+This option maps directly to the
+.Fl unit
+.Xr ppp 8
+command-line option and tells
+.Nm
+to instruct
+.Xr ppp 8
+to only attempt to open
+.Pa /dev/tun Ns Ar N .
+This option only works in client mode.
+.El
+.Sh PPP CONFIGURATION
+.Ss Important Notes on PPP Configuration
+Special attention is required when adding new RFCOMM configurations to the
+existing PPP configuration.
+Please keep in mind that PPP will
+.Em always
+execute commands in the
+.Dq Li default
+label of your
+.Pa /etc/ppp/ppp.conf
+file.
+Please make sure that the
+.Dq Li default
+label
+.Em only
+contains commands that apply to
+.Em every
+other label.
+If you need to use PPP for both dialing out and accepting incoming
+RFCOMM connections, please make sure you have moved all commands related to
+dialing out from the
+.Dq Li default
+section into an appropriate outgoing label.
+.Ss RFCOMM Server
+One of the typical examples is the LAN access.
+In this example, an RFCOMM connection
+is used as a null-modem connection between a client and a server.
+Both client and server will start talking PPP right after the RFCOMM
+connection has been established.
+.Bd -literal -offset indent
+rfcomm-server:
+ set timeout 0
+ set lqrperiod 10
+ set ifaddr 10.0.0.1 10.0.0.2 255.255.255.0
+ enable lqr
+ accept lqr
+ accept dns
+ # Do not use PPP authentication. Assume that
+ # Bluetooth connection was authenticated already
+ disable pap
+ deny pap
+ disable chap
+ deny chap
+.Ed
+.Ss RFCOMM Client
+The
+.Nm
+utility supports both
+.Cm LAN
+(LAN Access Using PPP) and
+.Cm DUN
+(Dial-Up Networking) access.
+The client's configuration for
+.Cm LAN
+access is very similar to the server's and might look like this:
+.Bd -literal -offset indent
+rfcomm-client:
+ enable lqr
+ accept lqr
+ set dial
+ set timeout 0
+ disable iface-alias
+ set ifaddr 10.0.0.1/0 10.0.0.2/0 255.255.255.0 0.0.0.0
+ # Do not use PPP authentication. Assume that
+ # Bluetooth connection was authenticated already
+ deny pap
+ disable pap
+ deny chap
+ disable chap
+.Ed
+.Pp
+The client's configuration for
+.Cm DUN
+access is different.
+In this scenario, the client gets connected to the virtual serial port on the
+server.
+To open a PPP session, the client must dial a number.
+Note that by default
+.Xr ppp 8
+will not execute any configured chat scripts.
+The
+.Ic force-scripts
+option can be used to override this behavior.
+An example configuration is shown below:
+.Bd -literal -offset indent
+rfcomm-dialup:
+ # This is IMPORTANT option
+ enable force-scripts
+
+ # You might want to change these
+ set authname
+ set authkey
+ set phone "*99***1#"
+
+ # You might want to adjust dial string as well
+ set dial "ABORT BUSY ABORT NO\\\\sCARRIER TIMEOUT 5 \\
+ \\"\\" AT OK-AT-OK ATE1Q0 OK \\\\dATDT\\\\T TIMEOUT 40 CONNECT"
+ set login
+ set timeout 30
+ enable dns
+ resolv rewrite
+
+ set ifaddr 10.0.0.1/0 10.0.0.2/0 255.255.255.0 0.0.0.0
+ add default HISADDR
+.Ed
+.Pp
+Note that by adjusting the initialization string, one can make a CSD (Circuit
+Switched Data), HSCSD (High Speed Circuit Switched Data) or GPRS (General
+Packet Radio Service) connection.
+The availability of the particular connection
+type depends on the phone model and service plan activated on the phone.
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+.Dl "rfcomm_pppd -s -a 00:01:02:03:04:05 -C 1 -l rfcomm-server"
+.Pp
+This command will start
+.Nm
+in the server mode.
+The RFCOMM server will listen on local address
+.Li 00:01:02:03:04:05
+and channel
+.Li 1 .
+Once the incoming connection has been accepted,
+.Nm
+will execute
+.Xr ppp 8
+in
+.Fl direct
+mode with the
+.Dq Li rfcomm-server
+label.
+.Pp
+.Dl "rfcomm_pppd -c -a 00:01:02:03:04:05 -C 1 -l rfcomm-client"
+.Pp
+This command will start
+.Nm
+in the client mode.
+.Nm
+will try to connect to the RFCOMM server at
+.Li 00:01:02:03:04:05
+address and channel
+.Li 1 .
+Once connected,
+.Nm
+will execute
+.Xr ppp 8
+in
+.Fl direct
+mode with the
+.Dq Li rfcomm-client
+label.
+.Sh CAVEATS
+The
+.Nm
+utility in server mode will try to register the Bluetooth LAN Access Over PPP
+service with the local SPD daemon.
+If the local SDP daemon is not running,
+.Nm
+will exit with an error.
+.Sh SEE ALSO
+.Xr rfcomm_sppd 1 ,
+.Xr bluetooth 3 ,
+.Xr ng_btsocket 4 ,
+.Xr ppp 8 ,
+.Xr sdpcontrol 8 ,
+.Xr sdpd 8
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
diff --git a/usr.sbin/bluetooth/rfcomm_pppd/rfcomm_pppd.c b/usr.sbin/bluetooth/rfcomm_pppd/rfcomm_pppd.c
new file mode 100644
index 0000000..956dc4d
--- /dev/null
+++ b/usr.sbin/bluetooth/rfcomm_pppd/rfcomm_pppd.c
@@ -0,0 +1,471 @@
+/*
+ * rfcomm_pppd.c
+ */
+
+/*-
+ * Copyright (c) 2001-2008 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: rfcomm_pppd.c,v 1.5 2003/09/07 18:32:11 max Exp $
+ * $FreeBSD$
+ */
+
+#include <bluetooth.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sdp.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#define RFCOMM_PPPD "rfcomm_pppd"
+
+int rfcomm_channel_lookup (bdaddr_t const *local,
+ bdaddr_t const *remote,
+ int service, int *channel, int *error);
+
+static void exec_ppp (int s, char *unit, char *label);
+static void sighandler (int s);
+static void usage (void);
+
+static int done;
+
+/* Main */
+int
+main(int argc, char *argv[])
+{
+ struct sockaddr_rfcomm sock_addr;
+ char *label = NULL, *unit = NULL, *ep = NULL;
+ bdaddr_t addr;
+ int s, channel, detach, server, service,
+ regdun, regsp;
+ pid_t pid;
+
+ memcpy(&addr, NG_HCI_BDADDR_ANY, sizeof(addr));
+ channel = 0;
+ detach = 1;
+ server = 0;
+ service = 0;
+ regdun = 0;
+ regsp = 0;
+
+ /* Parse command line arguments */
+ while ((s = getopt(argc, argv, "a:cC:dDhl:sSu:")) != -1) {
+ switch (s) {
+ case 'a': /* BDADDR */
+ if (!bt_aton(optarg, &addr)) {
+ struct hostent *he = NULL;
+
+ if ((he = bt_gethostbyname(optarg)) == NULL)
+ errx(1, "%s: %s", optarg, hstrerror(h_errno));
+
+ memcpy(&addr, he->h_addr, sizeof(addr));
+ }
+ break;
+
+ case 'c': /* client */
+ server = 0;
+ break;
+
+ case 'C': /* RFCOMM channel */
+ channel = strtoul(optarg, &ep, 10);
+ if (*ep != '\0') {
+ channel = 0;
+ switch (tolower(optarg[0])) {
+ case 'd': /* DialUp Networking */
+ service = SDP_SERVICE_CLASS_DIALUP_NETWORKING;
+ break;
+
+ case 'l': /* LAN Access Using PPP */
+ service = SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP;
+ break;
+ }
+ }
+ break;
+
+ case 'd': /* do not detach */
+ detach = 0;
+ break;
+
+ case 'D': /* Register DUN service as well as LAN service */
+ regdun = 1;
+ break;
+
+ case 'l': /* PPP label */
+ label = optarg;
+ break;
+
+ case 's': /* server */
+ server = 1;
+ break;
+
+ case 'S': /* Register SP service as well as LAN service */
+ regsp = 1;
+ break;
+
+ case 'u': /* PPP -unit option */
+ strtoul(optarg, &ep, 10);
+ if (*ep != '\0')
+ usage();
+ /* NOT REACHED */
+
+ unit = optarg;
+ break;
+
+ case 'h':
+ default:
+ usage();
+ /* NOT REACHED */
+ }
+ }
+
+ /* Check if we got everything we wanted */
+ if (label == NULL)
+ errx(1, "Must specify PPP label");
+
+ if (!server) {
+ if (memcmp(&addr, NG_HCI_BDADDR_ANY, sizeof(addr)) == 0)
+ errx(1, "Must specify server BD_ADDR");
+
+ /* Check channel, if was not set then obtain it via SDP */
+ if (channel == 0 && service != 0)
+ if (rfcomm_channel_lookup(NULL, &addr, service,
+ &channel, &s) != 0)
+ errc(1, s, "Could not obtain RFCOMM channel");
+ }
+
+ if (channel <= 0 || channel > 30)
+ errx(1, "Invalid RFCOMM channel number %d", channel);
+
+ openlog(RFCOMM_PPPD, LOG_PID | LOG_PERROR | LOG_NDELAY, LOG_USER);
+
+ if (detach && daemon(0, 0) < 0) {
+ syslog(LOG_ERR, "Could not daemon(0, 0). %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ s = socket(PF_BLUETOOTH, SOCK_STREAM, BLUETOOTH_PROTO_RFCOMM);
+ if (s < 0) {
+ syslog(LOG_ERR, "Could not create socket. %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ if (server) {
+ struct sigaction sa;
+ void *ss = NULL;
+ sdp_lan_profile_t lan;
+
+ /* Install signal handler */
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = sighandler;
+
+ if (sigaction(SIGTERM, &sa, NULL) < 0) {
+ syslog(LOG_ERR, "Could not sigaction(SIGTERM). %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ if (sigaction(SIGHUP, &sa, NULL) < 0) {
+ syslog(LOG_ERR, "Could not sigaction(SIGHUP). %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ if (sigaction(SIGINT, &sa, NULL) < 0) {
+ syslog(LOG_ERR, "Could not sigaction(SIGINT). %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ sa.sa_handler = SIG_IGN;
+ sa.sa_flags = SA_NOCLDWAIT;
+
+ if (sigaction(SIGCHLD, &sa, NULL) < 0) {
+ syslog(LOG_ERR, "Could not sigaction(SIGCHLD). %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ /* bind socket and listen for incoming connections */
+ sock_addr.rfcomm_len = sizeof(sock_addr);
+ sock_addr.rfcomm_family = AF_BLUETOOTH;
+ memcpy(&sock_addr.rfcomm_bdaddr, &addr,
+ sizeof(sock_addr.rfcomm_bdaddr));
+ sock_addr.rfcomm_channel = channel;
+
+ if (bind(s, (struct sockaddr *) &sock_addr,
+ sizeof(sock_addr)) < 0) {
+ syslog(LOG_ERR, "Could not bind socket. %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ if (listen(s, 10) < 0) {
+ syslog(LOG_ERR, "Could not listen on socket. %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ ss = sdp_open_local(NULL);
+ if (ss == NULL) {
+ syslog(LOG_ERR, "Unable to create local SDP session");
+ exit(1);
+ }
+
+ if (sdp_error(ss) != 0) {
+ syslog(LOG_ERR, "Unable to open local SDP session. " \
+ "%s (%d)", strerror(sdp_error(ss)),
+ sdp_error(ss));
+ exit(1);
+ }
+
+ memset(&lan, 0, sizeof(lan));
+ lan.server_channel = channel;
+
+ if (sdp_register_service(ss,
+ SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP,
+ &addr, (void *) &lan, sizeof(lan), NULL) != 0) {
+ syslog(LOG_ERR, "Unable to register LAN service with " \
+ "local SDP daemon. %s (%d)",
+ strerror(sdp_error(ss)), sdp_error(ss));
+ exit(1);
+ }
+
+ /*
+ * Register DUN (Dial-Up Networking) service on the same
+ * RFCOMM channel if requested. There is really no good reason
+ * to not to support this. AT-command exchange can be faked
+ * with chat script in ppp.conf
+ */
+
+ if (regdun) {
+ sdp_dun_profile_t dun;
+
+ memset(&dun, 0, sizeof(dun));
+ dun.server_channel = channel;
+
+ if (sdp_register_service(ss,
+ SDP_SERVICE_CLASS_DIALUP_NETWORKING,
+ &addr, (void *) &dun, sizeof(dun),
+ NULL) != 0) {
+ syslog(LOG_ERR, "Unable to register DUN " \
+ "service with local SDP daemon. " \
+ "%s (%d)", strerror(sdp_error(ss)),
+ sdp_error(ss));
+ exit(1);
+ }
+ }
+
+ /*
+ * Register SP (Serial Port) service on the same RFCOMM channel
+ * if requested. It appears that some cell phones are using so
+ * called "callback mechanism". In this scenario user is trying
+ * to connect his cell phone to the Internet, and, user's host
+ * computer is acting as the gateway server. It seems that it
+ * is not possible to tell the phone to just connect and start
+ * using the LAN service. Instead the user's host computer must
+ * "jump start" the phone by connecting to the phone's SP
+ * service. What happens next is the phone kills the existing
+ * connection and opens another connection back to the user's
+ * host computer. The phone really wants to use LAN service,
+ * but for whatever reason it looks for SP service on the
+ * user's host computer. This brain damaged behavior was
+ * reported for Nokia 6600 and Sony/Ericsson P900. Both phones
+ * are Symbian-based phones. Perhaps this is a Symbian problem?
+ */
+
+ if (regsp) {
+ sdp_sp_profile_t sp;
+
+ memset(&sp, 0, sizeof(sp));
+ sp.server_channel = channel;
+
+ if (sdp_register_service(ss,
+ SDP_SERVICE_CLASS_SERIAL_PORT,
+ &addr, (void *) &sp, sizeof(sp),
+ NULL) != 0) {
+ syslog(LOG_ERR, "Unable to register SP " \
+ "service with local SDP daemon. " \
+ "%s (%d)", strerror(sdp_error(ss)),
+ sdp_error(ss));
+ exit(1);
+ }
+ }
+
+ for (done = 0; !done; ) {
+ socklen_t len = sizeof(sock_addr);
+ int s1 = accept(s, (struct sockaddr *) &sock_addr, &len);
+
+ if (s1 < 0) {
+ syslog(LOG_ERR, "Could not accept connection " \
+ "on socket. %s (%d)", strerror(errno),
+ errno);
+ exit(1);
+ }
+
+ pid = fork();
+ if (pid == (pid_t) -1) {
+ syslog(LOG_ERR, "Could not fork(). %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ if (pid == 0) {
+ sdp_close(ss);
+ close(s);
+
+ /* Reset signal handler */
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = SIG_DFL;
+
+ sigaction(SIGTERM, &sa, NULL);
+ sigaction(SIGHUP, &sa, NULL);
+ sigaction(SIGINT, &sa, NULL);
+ sigaction(SIGCHLD, &sa, NULL);
+
+ /* Become daemon */
+ daemon(0, 0);
+
+ /*
+ * XXX Make sure user does not shoot himself
+ * in the foot. Do not pass unit option to the
+ * PPP when operating in the server mode.
+ */
+
+ exec_ppp(s1, NULL, label);
+ } else
+ close(s1);
+ }
+ } else {
+ sock_addr.rfcomm_len = sizeof(sock_addr);
+ sock_addr.rfcomm_family = AF_BLUETOOTH;
+ memcpy(&sock_addr.rfcomm_bdaddr, NG_HCI_BDADDR_ANY,
+ sizeof(sock_addr.rfcomm_bdaddr));
+ sock_addr.rfcomm_channel = 0;
+
+ if (bind(s, (struct sockaddr *) &sock_addr,
+ sizeof(sock_addr)) < 0) {
+ syslog(LOG_ERR, "Could not bind socket. %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ memcpy(&sock_addr.rfcomm_bdaddr, &addr,
+ sizeof(sock_addr.rfcomm_bdaddr));
+ sock_addr.rfcomm_channel = channel;
+
+ if (connect(s, (struct sockaddr *) &sock_addr,
+ sizeof(sock_addr)) < 0) {
+ syslog(LOG_ERR, "Could not connect socket. %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ exec_ppp(s, unit, label);
+ }
+
+ exit(0);
+} /* main */
+
+/*
+ * Redirects stdin/stdout to s, stderr to /dev/null and exec
+ * 'ppp -direct -quiet [-unit N] label'. Never returns.
+ */
+
+static void
+exec_ppp(int s, char *unit, char *label)
+{
+ char ppp[] = "/usr/sbin/ppp";
+ char *ppp_args[] = { ppp, "-direct", "-quiet",
+ NULL, NULL, NULL, NULL };
+
+ close(0);
+ if (dup(s) < 0) {
+ syslog(LOG_ERR, "Could not dup(0). %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ close(1);
+ if (dup(s) < 0) {
+ syslog(LOG_ERR, "Could not dup(1). %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ close(2);
+ open("/dev/null", O_RDWR);
+
+ if (unit != NULL) {
+ ppp_args[3] = "-unit";
+ ppp_args[4] = unit;
+ ppp_args[5] = label;
+ } else
+ ppp_args[3] = label;
+
+ if (execv(ppp, ppp_args) < 0) {
+ syslog(LOG_ERR, "Could not exec(%s -direct -quiet%s%s %s). " \
+ "%s (%d)", ppp, (unit != NULL)? " -unit " : "",
+ (unit != NULL)? unit : "", label,
+ strerror(errno), errno);
+ exit(1);
+ }
+} /* run_ppp */
+
+/* Signal handler */
+static void
+sighandler(int s)
+{
+ done = 1;
+} /* sighandler */
+
+/* Display usage and exit */
+static void
+usage(void)
+{
+ fprintf(stdout,
+"Usage: %s options\n" \
+"Where options are:\n" \
+"\t-a address Address to listen on or connect to (required for client)\n" \
+"\t-c Act as a clinet (default)\n" \
+"\t-C channel RFCOMM channel to listen on or connect to (required)\n" \
+"\t-d Run in foreground\n" \
+"\t-D Register Dial-Up Networking service (server mode only)\n" \
+"\t-l label Use PPP label (required)\n" \
+"\t-s Act as a server\n" \
+"\t-S Register Serial Port service (server mode only)\n" \
+"\t-u N Tell PPP to operate on /dev/tunN (client mode only)\n" \
+"\t-h Display this message\n", RFCOMM_PPPD);
+
+ exit(255);
+} /* usage */
+
diff --git a/usr.sbin/bluetooth/sdpcontrol/Makefile b/usr.sbin/bluetooth/sdpcontrol/Makefile
new file mode 100644
index 0000000..c0ec8d4
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpcontrol/Makefile
@@ -0,0 +1,12 @@
+# $Id: Makefile,v 1.1 2003/09/08 02:27:27 max Exp $
+# $FreeBSD$
+
+PROG= sdpcontrol
+MAN= sdpcontrol.8
+SRCS= sdpcontrol.c search.c
+WARNS?= 2
+
+DPADD= ${LIBBLUETOOTH} ${LIBSDP}
+LDADD= -lbluetooth -lsdp
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bluetooth/sdpcontrol/sdpcontrol.8 b/usr.sbin/bluetooth/sdpcontrol/sdpcontrol.8
new file mode 100644
index 0000000..d1b9ae5
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpcontrol/sdpcontrol.8
@@ -0,0 +1,118 @@
+.\" Copyright (c) 2003 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: sdpcontrol.8,v 1.1 2003/09/08 02:27:27 max Exp $
+.\" $FreeBSD$
+.\"
+.Dd September 7, 2003
+.Dt SDPCONTROL 8
+.Os
+.Sh NAME
+.Nm sdpcontrol
+.Nd SDP query utility
+.Sh SYNOPSIS
+.Nm
+.Fl h
+.Nm
+.Fl a Ar address
+.Ar command
+.Op Ar parameters ...
+.Nm
+.Fl l
+.Op Fl c Ar path
+.Ar command
+.Op Ar parameters ...
+.Sh DESCRIPTION
+The
+.Nm
+utility attempts to query the specified Service Discovery Protocol (SDP) server.
+Remote SDP servers are identified by their address.
+Connection to the local SDP server is made via the control socket.
+The
+.Nm
+utility uses Service Search Attribute Requests and prints results to
+standard output and error messages to standard error.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl a Ar address
+Connect to the remote device with the specified address.
+The address can be specified as BD_ADDR or a name.
+If a name was specified, the
+.Nm
+utility attempts to resolve the name via
+.Xr bt_gethostbyname 3 .
+.It Fl c Ar path
+Specify path to the control socket.
+The default path is
+.Pa /var/run/sdp .
+.It Fl h
+Display usage message and exit.
+.It Fl l
+Query the local SDP server via the control socket.
+.It Ar command
+One of the supported commands (see below).
+The special command
+.Cm help
+can be used to obtain a list of all supported commands.
+To get more information about a specific command, use
+.Cm help Ar command .
+.It Ar parameters
+One or more optional space separated command parameters.
+.El
+.Sh COMMANDS
+The currently supported node commands in
+.Nm
+are:
+.Pp
+.Bl -tag -offset indent -compact
+.It Cm Browse
+.It Cm Search
+.El
+.Sh CAVEATS
+The
+.Nm
+utility only implements client side functionality.
+.Pp
+The
+.Nm
+utility only requests the following attributes from the SDP server:
+.Pp
+.Bl -enum -offset indent -compact
+.It
+Service Record Handle
+.It
+Service Class ID List
+.It
+Protocol Descriptor List
+.It
+Bluetooth Profile Descriptor List
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr bluetooth 3 ,
+.Xr sdp 3
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
diff --git a/usr.sbin/bluetooth/sdpcontrol/sdpcontrol.c b/usr.sbin/bluetooth/sdpcontrol/sdpcontrol.c
new file mode 100644
index 0000000..fca2015
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpcontrol/sdpcontrol.c
@@ -0,0 +1,219 @@
+/*
+ * sdpcontrol.c
+ *
+ * Copyright (c) 2001-2003 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: sdpcontrol.c,v 1.1 2003/09/08 02:27:27 max Exp $
+ * $FreeBSD$
+ */
+
+#include <assert.h>
+#include <bluetooth.h>
+#include <err.h>
+#include <errno.h>
+#include <sdp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "sdpcontrol.h"
+
+/* Prototypes */
+static int do_sdp_command (bdaddr_p, char const *, int,
+ int, char **);
+static struct sdp_command * find_sdp_command (char const *,
+ struct sdp_command *);
+static void print_sdp_command (struct sdp_command *);
+static void usage (void);
+
+/* Main */
+int
+main(int argc, char *argv[])
+{
+ char const *control = SDP_LOCAL_PATH;
+ int n, local;
+ bdaddr_t bdaddr;
+
+ memset(&bdaddr, 0, sizeof(bdaddr));
+ local = 0;
+
+ /* Process command line arguments */
+ while ((n = getopt(argc, argv, "a:c:lh")) != -1) {
+ switch (n) {
+ case 'a': /* bdaddr */
+ if (!bt_aton(optarg, &bdaddr)) {
+ struct hostent *he = NULL;
+
+ if ((he = bt_gethostbyname(optarg)) == NULL)
+ errx(1, "%s: %s", optarg, hstrerror(h_errno));
+
+ memcpy(&bdaddr, he->h_addr, sizeof(bdaddr));
+ }
+ break;
+
+ case 'c': /* control socket */
+ control = optarg;
+ break;
+
+ case 'l': /* local sdpd */
+ local = 1;
+ break;
+
+ case 'h':
+ default:
+ usage();
+ /* NOT REACHED */
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (*argv == NULL)
+ usage();
+
+ return (do_sdp_command(&bdaddr, control, local, argc, argv));
+}
+
+/* Execute commands */
+static int
+do_sdp_command(bdaddr_p bdaddr, char const *control, int local,
+ int argc, char **argv)
+{
+ char *cmd = argv[0];
+ struct sdp_command *c = NULL;
+ void *xs = NULL;
+ int e, help;
+
+ help = 0;
+ if (strcasecmp(cmd, "help") == 0) {
+ argc --;
+ argv ++;
+
+ if (argc <= 0) {
+ fprintf(stdout, "Supported commands:\n");
+ print_sdp_command(sdp_commands);
+ fprintf(stdout, "\nFor more information use " \
+ "'help command'\n");
+
+ return (OK);
+ }
+
+ help = 1;
+ cmd = argv[0];
+ }
+
+ c = find_sdp_command(cmd, sdp_commands);
+ if (c == NULL) {
+ fprintf(stdout, "Unknown command: \"%s\"\n", cmd);
+ return (ERROR);
+ }
+
+ if (!help) {
+ if (!local) {
+ if (memcmp(bdaddr, NG_HCI_BDADDR_ANY, sizeof(*bdaddr)) == 0)
+ usage();
+
+ xs = sdp_open(NG_HCI_BDADDR_ANY, bdaddr);
+ } else
+ xs = sdp_open_local(control);
+
+ if (xs == NULL)
+ errx(1, "Could not create SDP session object");
+ if (sdp_error(xs) == 0)
+ e = (c->handler)(xs, -- argc, ++ argv);
+ else
+ e = ERROR;
+ } else
+ e = USAGE;
+
+ switch (e) {
+ case OK:
+ case FAILED:
+ break;
+
+ case ERROR:
+ fprintf(stdout, "Could not execute command \"%s\". %s\n",
+ cmd, strerror(sdp_error(xs)));
+ break;
+
+ case USAGE:
+ fprintf(stdout, "Usage: %s\n%s\n", c->command, c->description);
+ break;
+
+ default: assert(0); break;
+ }
+
+ sdp_close(xs);
+
+ return (e);
+} /* do_sdp_command */
+
+/* Try to find command in specified category */
+static struct sdp_command *
+find_sdp_command(char const *command, struct sdp_command *category)
+{
+ struct sdp_command *c = NULL;
+
+ for (c = category; c->command != NULL; c++) {
+ char *c_end = strchr(c->command, ' ');
+
+ if (c_end != NULL) {
+ int len = c_end - c->command;
+
+ if (strncasecmp(command, c->command, len) == 0)
+ return (c);
+ } else if (strcasecmp(command, c->command) == 0)
+ return (c);
+ }
+
+ return (NULL);
+} /* find_sdp_command */
+
+/* Print commands in specified category */
+static void
+print_sdp_command(struct sdp_command *category)
+{
+ struct sdp_command *c = NULL;
+
+ for (c = category; c->command != NULL; c++)
+ fprintf(stdout, "\t%s\n", c->command);
+} /* print_sdp_command */
+
+/* Usage */
+static void
+usage(void)
+{
+ fprintf(stderr,
+"Usage: sdpcontrol options command\n" \
+"Where options are:\n"
+" -a address address to connect to\n" \
+" -c path path to the control socket (default is %s)\n" \
+" -h display usage and quit\n" \
+" -l connect to the local SDP server via control socket\n" \
+" command one of the supported commands\n", SDP_LOCAL_PATH);
+ exit(255);
+} /* usage */
+
diff --git a/usr.sbin/bluetooth/sdpcontrol/sdpcontrol.h b/usr.sbin/bluetooth/sdpcontrol/sdpcontrol.h
new file mode 100644
index 0000000..4a1ee76f
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpcontrol/sdpcontrol.h
@@ -0,0 +1,49 @@
+/*
+ * sdpcontrol.h
+ *
+ * Copyright (c) 2001-2003 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: sdpcontrol.h,v 1.1 2003/09/08 02:27:27 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef __SDPCONTROL_H__
+#define __SDPCONTROL_H__
+
+#define OK 0 /* everything was OK */
+#define ERROR 1 /* could not execute command */
+#define FAILED 2 /* error was reported */
+#define USAGE 3 /* invalid parameters */
+
+struct sdp_command {
+ char const *command;
+ char const *description;
+ int (*handler)(void *, int, char **);
+};
+
+extern struct sdp_command sdp_commands[];
+
+#endif /* __SDPCONTROL_H__ */
+
diff --git a/usr.sbin/bluetooth/sdpcontrol/search.c b/usr.sbin/bluetooth/sdpcontrol/search.c
new file mode 100644
index 0000000..e7d8244
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpcontrol/search.c
@@ -0,0 +1,713 @@
+/*
+ * search.c
+ *
+ * Copyright (c) 2001-2003 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: search.c,v 1.2 2003/09/08 17:35:15 max Exp $
+ * $FreeBSD$
+ */
+
+#include <netinet/in.h>
+#include <bluetooth.h>
+#include <ctype.h>
+#include <sdp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "sdpcontrol.h"
+
+/* List of the attributes we are looking for */
+static uint32_t attrs[] =
+{
+ SDP_ATTR_RANGE( SDP_ATTR_SERVICE_RECORD_HANDLE,
+ SDP_ATTR_SERVICE_RECORD_HANDLE),
+ SDP_ATTR_RANGE( SDP_ATTR_SERVICE_CLASS_ID_LIST,
+ SDP_ATTR_SERVICE_CLASS_ID_LIST),
+ SDP_ATTR_RANGE( SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
+ SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST),
+ SDP_ATTR_RANGE( SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST,
+ SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST)
+};
+#define attrs_len (sizeof(attrs)/sizeof(attrs[0]))
+
+/* Buffer for the attributes */
+#define NRECS 25 /* request this much records from the SDP server */
+#define BSIZE 256 /* one attribute buffer size */
+static uint8_t buffer[NRECS * attrs_len][BSIZE];
+
+/* SDP attributes */
+static sdp_attr_t values[NRECS * attrs_len];
+#define values_len (sizeof(values)/sizeof(values[0]))
+
+/*
+ * Print Service Class ID List
+ *
+ * The ServiceClassIDList attribute consists of a data element sequence in
+ * which each data element is a UUID representing the service classes that
+ * a given service record conforms to. The UUIDs are listed in order from
+ * the most specific class to the most general class. The ServiceClassIDList
+ * must contain at least one service class UUID.
+ */
+
+static void
+print_service_class_id_list(uint8_t const *start, uint8_t const *end)
+{
+ uint32_t type, len, value;
+
+ if (end - start < 2) {
+ fprintf(stderr, "Invalid Service Class ID List. " \
+ "Too short, len=%zd\n", end - start);
+ return;
+ }
+
+ SDP_GET8(type, start);
+ switch (type) {
+ case SDP_DATA_SEQ8:
+ SDP_GET8(len, start);
+ break;
+
+ case SDP_DATA_SEQ16:
+ SDP_GET16(len, start);
+ break;
+
+ case SDP_DATA_SEQ32:
+ SDP_GET32(len, start);
+ break;
+
+ default:
+ fprintf(stderr, "Invalid Service Class ID List. " \
+ "Not a sequence, type=%#x\n", type);
+ return;
+ /* NOT REACHED */
+ }
+
+ while (start < end) {
+ SDP_GET8(type, start);
+ switch (type) {
+ case SDP_DATA_UUID16:
+ SDP_GET16(value, start);
+ fprintf(stdout, "\t%s (%#4.4x)\n",
+ sdp_uuid2desc(value), value);
+ break;
+
+ case SDP_DATA_UUID32:
+ SDP_GET32(value, start);
+ fprintf(stdout, "\t%#8.8x\n", value);
+ break;
+
+ case SDP_DATA_UUID128: {
+ int128_t uuid;
+
+ SDP_GET_UUID128(&uuid, start);
+ fprintf(stdout, "\t%#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n",
+ ntohl(*(uint32_t *)&uuid.b[0]),
+ ntohs(*(uint16_t *)&uuid.b[4]),
+ ntohs(*(uint16_t *)&uuid.b[6]),
+ ntohs(*(uint16_t *)&uuid.b[8]),
+ ntohs(*(uint16_t *)&uuid.b[10]),
+ ntohl(*(uint32_t *)&uuid.b[12]));
+ } break;
+
+ default:
+ fprintf(stderr, "Invalid Service Class ID List. " \
+ "Not a UUID, type=%#x\n", type);
+ return;
+ /* NOT REACHED */
+ }
+ }
+} /* print_service_class_id_list */
+
+/*
+ * Print Protocol Descriptor List
+ *
+ * If the ProtocolDescriptorList describes a single stack, it takes the form
+ * of a data element sequence in which each element of the sequence is a
+ * protocol descriptor. Each protocol descriptor is, in turn, a data element
+ * sequence whose first element is a UUID identifying the protocol and whose
+ * successive elements are protocol-specific parameters. The protocol
+ * descriptors are listed in order from the lowest layer protocol to the
+ * highest layer protocol used to gain access to the service. If it is possible
+ * for more than one kind of protocol stack to be used to gain access to the
+ * service, the ProtocolDescriptorList takes the form of a data element
+ * alternative where each member is a data element sequence as described above.
+ */
+
+static void
+print_protocol_descriptor(uint8_t const *start, uint8_t const *end)
+{
+ union {
+ uint8_t uint8;
+ uint16_t uint16;
+ uint32_t uint32;
+ uint64_t uint64;
+ int128_t int128;
+ } value;
+ uint32_t type, len, param;
+
+ /* Get Protocol UUID */
+ SDP_GET8(type, start);
+ switch (type) {
+ case SDP_DATA_UUID16:
+ SDP_GET16(value.uint16, start);
+ fprintf(stdout, "\t%s (%#4.4x)\n", sdp_uuid2desc(value.uint16),
+ value.uint16);
+ break;
+
+ case SDP_DATA_UUID32:
+ SDP_GET32(value.uint32, start);
+ fprintf(stdout, "\t%#8.8x\n", value.uint32);
+ break;
+
+ case SDP_DATA_UUID128:
+ SDP_GET_UUID128(&value.int128, start);
+ fprintf(stdout, "\t%#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n",
+ ntohl(*(uint32_t *)&value.int128.b[0]),
+ ntohs(*(uint16_t *)&value.int128.b[4]),
+ ntohs(*(uint16_t *)&value.int128.b[6]),
+ ntohs(*(uint16_t *)&value.int128.b[8]),
+ ntohs(*(uint16_t *)&value.int128.b[10]),
+ ntohl(*(uint32_t *)&value.int128.b[12]));
+ break;
+
+ default:
+ fprintf(stderr, "Invalid Protocol Descriptor. " \
+ "Not a UUID, type=%#x\n", type);
+ return;
+ /* NOT REACHED */
+ }
+
+ /* Protocol specific parameters */
+ for (param = 1; start < end; param ++) {
+ fprintf(stdout, "\t\tProtocol specific parameter #%d: ", param);
+
+ SDP_GET8(type, start);
+ switch (type) {
+ case SDP_DATA_NIL:
+ fprintf(stdout, "nil\n");
+ break;
+
+ case SDP_DATA_UINT8:
+ case SDP_DATA_INT8:
+ case SDP_DATA_BOOL:
+ SDP_GET8(value.uint8, start);
+ fprintf(stdout, "u/int8/bool %u\n", value.uint8);
+ break;
+
+ case SDP_DATA_UINT16:
+ case SDP_DATA_INT16:
+ case SDP_DATA_UUID16:
+ SDP_GET16(value.uint16, start);
+ fprintf(stdout, "u/int/uuid16 %u\n", value.uint16);
+ break;
+
+ case SDP_DATA_UINT32:
+ case SDP_DATA_INT32:
+ case SDP_DATA_UUID32:
+ SDP_GET32(value.uint32, start);
+ fprintf(stdout, "u/int/uuid32 %u\n", value.uint32);
+ break;
+
+ case SDP_DATA_UINT64:
+ case SDP_DATA_INT64:
+ SDP_GET64(value.uint64, start);
+ fprintf(stdout, "u/int64 %ju\n", value.uint64);
+ break;
+
+ case SDP_DATA_UINT128:
+ case SDP_DATA_INT128:
+ SDP_GET128(&value.int128, start);
+ fprintf(stdout, "u/int128 %#8.8x%8.8x%8.8x%8.8x\n",
+ *(uint32_t *)&value.int128.b[0],
+ *(uint32_t *)&value.int128.b[4],
+ *(uint32_t *)&value.int128.b[8],
+ *(uint32_t *)&value.int128.b[12]);
+ break;
+
+ case SDP_DATA_UUID128:
+ SDP_GET_UUID128(&value.int128, start);
+ fprintf(stdout, "uuid128 %#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x\n",
+ ntohl(*(uint32_t *)&value.int128.b[0]),
+ ntohs(*(uint16_t *)&value.int128.b[4]),
+ ntohs(*(uint16_t *)&value.int128.b[6]),
+ ntohs(*(uint16_t *)&value.int128.b[8]),
+ ntohs(*(uint16_t *)&value.int128.b[10]),
+ ntohl(*(uint32_t *)&value.int128.b[12]));
+ break;
+
+ case SDP_DATA_STR8:
+ case SDP_DATA_URL8:
+ SDP_GET8(len, start);
+ fprintf(stdout, "%*.*s\n", len, len, (char *) start);
+ start += len;
+ break;
+
+ case SDP_DATA_STR16:
+ case SDP_DATA_URL16:
+ SDP_GET16(len, start);
+ fprintf(stdout, "%*.*s\n", len, len, (char *) start);
+ start += len;
+ break;
+
+ case SDP_DATA_STR32:
+ case SDP_DATA_URL32:
+ SDP_GET32(len, start);
+ fprintf(stdout, "%*.*s\n", len, len, (char *) start);
+ start += len;
+ break;
+
+ case SDP_DATA_SEQ8:
+ case SDP_DATA_ALT8:
+ SDP_GET8(len, start);
+ for (; len > 0; start ++, len --)
+ fprintf(stdout, "%#2.2x ", *start);
+ fprintf(stdout, "\n");
+ break;
+
+ case SDP_DATA_SEQ16:
+ case SDP_DATA_ALT16:
+ SDP_GET16(len, start);
+ for (; len > 0; start ++, len --)
+ fprintf(stdout, "%#2.2x ", *start);
+ fprintf(stdout, "\n");
+ break;
+
+ case SDP_DATA_SEQ32:
+ case SDP_DATA_ALT32:
+ SDP_GET32(len, start);
+ for (; len > 0; start ++, len --)
+ fprintf(stdout, "%#2.2x ", *start);
+ fprintf(stdout, "\n");
+ break;
+
+ default:
+ fprintf(stderr, "Invalid Protocol Descriptor. " \
+ "Unknown data type: %#02x\n", type);
+ return;
+ /* NOT REACHED */
+ }
+ }
+} /* print_protocol_descriptor */
+
+static void
+print_protocol_descriptor_list(uint8_t const *start, uint8_t const *end)
+{
+ uint32_t type, len;
+
+ if (end - start < 2) {
+ fprintf(stderr, "Invalid Protocol Descriptor List. " \
+ "Too short, len=%zd\n", end - start);
+ return;
+ }
+
+ SDP_GET8(type, start);
+ switch (type) {
+ case SDP_DATA_SEQ8:
+ SDP_GET8(len, start);
+ break;
+
+ case SDP_DATA_SEQ16:
+ SDP_GET16(len, start);
+ break;
+
+ case SDP_DATA_SEQ32:
+ SDP_GET32(len, start);
+ break;
+
+ default:
+ fprintf(stderr, "Invalid Protocol Descriptor List. " \
+ "Not a sequence, type=%#x\n", type);
+ return;
+ /* NOT REACHED */
+ }
+
+ while (start < end) {
+ SDP_GET8(type, start);
+ switch (type) {
+ case SDP_DATA_SEQ8:
+ SDP_GET8(len, start);
+ break;
+
+ case SDP_DATA_SEQ16:
+ SDP_GET16(len, start);
+ break;
+
+ case SDP_DATA_SEQ32:
+ SDP_GET32(len, start);
+ break;
+
+ default:
+ fprintf(stderr, "Invalid Protocol Descriptor List. " \
+ "Not a sequence, type=%#x\n", type);
+ return;
+ /* NOT REACHED */
+ }
+
+ print_protocol_descriptor(start, start + len);
+ start += len;
+ }
+} /* print_protocol_descriptor_list */
+
+/*
+ * Print Bluetooth Profile Descriptor List
+ *
+ * The BluetoothProfileDescriptorList attribute consists of a data element
+ * sequence in which each element is a profile descriptor that contains
+ * information about a Bluetooth profile to which the service represented by
+ * this service record conforms. Each profile descriptor is a data element
+ * sequence whose first element is the UUID assigned to the profile and whose
+ * second element is a 16-bit profile version number. Each version of a profile
+ * is assigned a 16-bit unsigned integer profile version number, which consists
+ * of two 8-bit fields. The higher-order 8 bits contain the major version
+ * number field and the lower-order 8 bits contain the minor version number
+ * field.
+ */
+
+static void
+print_bluetooth_profile_descriptor_list(uint8_t const *start, uint8_t const *end)
+{
+ uint32_t type, len, value;
+
+ if (end - start < 2) {
+ fprintf(stderr, "Invalid Bluetooth Profile Descriptor List. " \
+ "Too short, len=%zd\n", end - start);
+ return;
+ }
+
+ SDP_GET8(type, start);
+ switch (type) {
+ case SDP_DATA_SEQ8:
+ SDP_GET8(len, start);
+ break;
+
+ case SDP_DATA_SEQ16:
+ SDP_GET16(len, start);
+ break;
+
+ case SDP_DATA_SEQ32:
+ SDP_GET32(len, start);
+ break;
+
+ default:
+ fprintf(stderr, "Invalid Bluetooth Profile Descriptor List. " \
+ "Not a sequence, type=%#x\n", type);
+ return;
+ /* NOT REACHED */
+ }
+
+ while (start < end) {
+ SDP_GET8(type, start);
+ switch (type) {
+ case SDP_DATA_SEQ8:
+ SDP_GET8(len, start);
+ break;
+
+ case SDP_DATA_SEQ16:
+ SDP_GET16(len, start);
+ break;
+
+ case SDP_DATA_SEQ32:
+ SDP_GET32(len, start);
+ break;
+
+ default:
+ fprintf(stderr, "Invalid Bluetooth Profile " \
+ "Descriptor List. " \
+ "Not a sequence, type=%#x\n", type);
+ return;
+ /* NOT REACHED */
+ }
+
+ /* Get UUID */
+ SDP_GET8(type, start);
+ switch (type) {
+ case SDP_DATA_UUID16:
+ SDP_GET16(value, start);
+ fprintf(stdout, "\t%s (%#4.4x) ",
+ sdp_uuid2desc(value), value);
+ break;
+
+ case SDP_DATA_UUID32:
+ SDP_GET32(value, start);
+ fprintf(stdout, "\t%#8.8x ", value);
+ break;
+
+ case SDP_DATA_UUID128: {
+ int128_t uuid;
+
+ SDP_GET_UUID128(&uuid, start);
+ fprintf(stdout, "\t%#8.8x-%4.4x-%4.4x-%4.4x-%4.4x%8.8x ",
+ ntohl(*(uint32_t *)&uuid.b[0]),
+ ntohs(*(uint16_t *)&uuid.b[4]),
+ ntohs(*(uint16_t *)&uuid.b[6]),
+ ntohs(*(uint16_t *)&uuid.b[8]),
+ ntohs(*(uint16_t *)&uuid.b[10]),
+ ntohl(*(uint32_t *)&uuid.b[12]));
+ } break;
+
+ default:
+ fprintf(stderr, "Invalid Bluetooth Profile " \
+ "Descriptor List. " \
+ "Not a UUID, type=%#x\n", type);
+ return;
+ /* NOT REACHED */
+ }
+
+ /* Get version */
+ SDP_GET8(type, start);
+ if (type != SDP_DATA_UINT16) {
+ fprintf(stderr, "Invalid Bluetooth Profile " \
+ "Descriptor List. " \
+ "Invalid version type=%#x\n", type);
+ return;
+ }
+
+ SDP_GET16(value, start);
+ fprintf(stdout, "ver. %d.%d\n",
+ (value >> 8) & 0xff, value & 0xff);
+ }
+} /* print_bluetooth_profile_descriptor_list */
+
+/* Perform SDP search command */
+static int
+do_sdp_search(void *xs, int argc, char **argv)
+{
+ char *ep = NULL;
+ int32_t n, type, value;
+ uint16_t service;
+
+ /* Parse command line arguments */
+ switch (argc) {
+ case 1:
+ n = strtoul(argv[0], &ep, 16);
+ if (*ep != 0) {
+ switch (tolower(argv[0][0])) {
+ case 'c': /* CIP/CTP */
+ switch (tolower(argv[0][1])) {
+ case 'i':
+ service = SDP_SERVICE_CLASS_COMMON_ISDN_ACCESS;
+ break;
+
+ case 't':
+ service = SDP_SERVICE_CLASS_CORDLESS_TELEPHONY;
+ break;
+
+ default:
+ return (USAGE);
+ /* NOT REACHED */
+ }
+ break;
+
+ case 'd': /* DialUp Networking */
+ service = SDP_SERVICE_CLASS_DIALUP_NETWORKING;
+ break;
+
+ case 'f': /* Fax/OBEX File Transfer */
+ switch (tolower(argv[0][1])) {
+ case 'a':
+ service = SDP_SERVICE_CLASS_FAX;
+ break;
+
+ case 't':
+ service = SDP_SERVICE_CLASS_OBEX_FILE_TRANSFER;
+ break;
+
+ default:
+ return (USAGE);
+ /* NOT REACHED */
+ }
+ break;
+
+ case 'g': /* GN */
+ service = SDP_SERVICE_CLASS_GN;
+ break;
+
+ case 'h': /* Headset/HID */
+ switch (tolower(argv[0][1])) {
+ case 'i':
+ service = SDP_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE;
+ break;
+
+ case 's':
+ service = SDP_SERVICE_CLASS_HEADSET;
+ break;
+
+ default:
+ return (USAGE);
+ /* NOT REACHED */
+ }
+ break;
+
+ case 'l': /* LAN Access Using PPP */
+ service = SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP;
+ break;
+
+ case 'n': /* NAP */
+ service = SDP_SERVICE_CLASS_NAP;
+ break;
+
+ case 'o': /* OBEX Object Push */
+ service = SDP_SERVICE_CLASS_OBEX_OBJECT_PUSH;
+ break;
+
+ case 's': /* Serial Port */
+ service = SDP_SERVICE_CLASS_SERIAL_PORT;
+ break;
+
+ default:
+ return (USAGE);
+ /* NOT REACHED */
+ }
+ } else
+ service = (uint16_t) n;
+ break;
+
+ default:
+ return (USAGE);
+ }
+
+ /* Initialize attribute values array */
+ for (n = 0; n < values_len; n ++) {
+ values[n].flags = SDP_ATTR_INVALID;
+ values[n].attr = 0;
+ values[n].vlen = BSIZE;
+ values[n].value = buffer[n];
+ }
+
+ /* Do SDP Service Search Attribute Request */
+ n = sdp_search(xs, 1, &service, attrs_len, attrs, values_len, values);
+ if (n != 0)
+ return (ERROR);
+
+ /* Print attributes values */
+ for (n = 0; n < values_len; n ++) {
+ if (values[n].flags != SDP_ATTR_OK)
+ break;
+
+ switch (values[n].attr) {
+ case SDP_ATTR_SERVICE_RECORD_HANDLE:
+ fprintf(stdout, "\n");
+ if (values[n].vlen == 5) {
+ SDP_GET8(type, values[n].value);
+ if (type == SDP_DATA_UINT32) {
+ SDP_GET32(value, values[n].value);
+ fprintf(stdout, "Record Handle: " \
+ "%#8.8x\n", value);
+ } else
+ fprintf(stderr, "Invalid type=%#x " \
+ "Record Handle " \
+ "attribute!\n", type);
+ } else
+ fprintf(stderr, "Invalid size=%d for Record " \
+ "Handle attribute\n",
+ values[n].vlen);
+ break;
+
+ case SDP_ATTR_SERVICE_CLASS_ID_LIST:
+ fprintf(stdout, "Service Class ID List:\n");
+ print_service_class_id_list(values[n].value,
+ values[n].value + values[n].vlen);
+ break;
+
+ case SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST:
+ fprintf(stdout, "Protocol Descriptor List:\n");
+ print_protocol_descriptor_list(values[n].value,
+ values[n].value + values[n].vlen);
+ break;
+
+ case SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST:
+ fprintf(stdout, "Bluetooth Profile Descriptor List:\n");
+ print_bluetooth_profile_descriptor_list(values[n].value,
+ values[n].value + values[n].vlen);
+ break;
+
+ default:
+ fprintf(stderr, "Unexpected attribute ID=%#4.4x\n",
+ values[n].attr);
+ break;
+ }
+ }
+
+ return (OK);
+} /* do_sdp_search */
+
+/* Perform SDP browse command */
+static int
+do_sdp_browse(void *xs, int argc, char **argv)
+{
+#undef _STR
+#undef STR
+#define _STR(x) #x
+#define STR(x) _STR(x)
+
+ static char const * const av[] = {
+ STR(SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP),
+ NULL
+ };
+
+ switch (argc) {
+ case 0:
+ argc = 1;
+ argv = (char **) av;
+ /* FALL THROUGH */
+ case 1:
+ return (do_sdp_search(xs, argc, argv));
+ }
+
+ return (USAGE);
+} /* do_sdp_browse */
+
+/* List of SDP commands */
+struct sdp_command sdp_commands[] = {
+{
+"Browse [<Group>]",
+"Browse for services. The <Group> parameter is a 16-bit UUID of the group\n" \
+"to browse. If omitted <Group> is set to Public Browse Group.\n\n" \
+"\t<Group> - xxxx; 16-bit UUID of the group to browse\n",
+do_sdp_browse
+},
+{
+"Search <Service>",
+"Search for the <Service>. The <Service> parameter is a 16-bit UUID of the\n" \
+"service to search for. For some services it is possible to use service name\n"\
+"instead of service UUID\n\n" \
+"\t<Service> - xxxx; 16-bit UUID of the service to search for\n\n" \
+"\tKnown service names\n" \
+"\t===================\n" \
+"\tCIP - Common ISDN Access\n" \
+"\tCTP - Cordless Telephony\n" \
+"\tDUN - DialUp Networking\n" \
+"\tFAX - Fax\n" \
+"\tFTRN - OBEX File Transfer\n" \
+"\tGN - GN\n" \
+"\tHID - Human Interface Device\n" \
+"\tHSET - Headset\n" \
+"\tLAN - LAN Access Using PPP\n" \
+"\tNAP - Network Access Point\n" \
+"\tOPUSH - OBEX Object Push\n" \
+"\tSP - Serial Port\n",
+do_sdp_search
+},
+{ NULL, NULL, NULL }
+};
+
diff --git a/usr.sbin/bluetooth/sdpd/Makefile b/usr.sbin/bluetooth/sdpd/Makefile
new file mode 100644
index 0000000..c47441f
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/Makefile
@@ -0,0 +1,13 @@
+# $Id: Makefile,v 1.1 2004/01/20 21:27:55 max Exp $
+# $FreeBSD$
+
+PROG= sdpd
+MAN= sdpd.8
+SRCS= bgd.c dun.c ftrn.c gn.c irmc.c irmc_command.c lan.c log.c \
+ main.c nap.c opush.c panu.c profile.c provider.c sar.c scr.c \
+ sd.c server.c sp.c srr.c ssar.c ssr.c sur.c uuid.c
+
+CFLAGS+= -I${.CURDIR}
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bluetooth/sdpd/bgd.c b/usr.sbin/bluetooth/sdpd/bgd.c
new file mode 100644
index 0000000..70dda89
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/bgd.c
@@ -0,0 +1,102 @@
+/*
+ * bgd.c
+ *
+ * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: bgd.c,v 1.4 2004/01/13 01:54:39 max Exp $
+ * $FreeBSD$
+ */
+
+#include <bluetooth.h>
+#include <sdp.h>
+#include <string.h>
+#include "profile.h"
+
+static int32_t
+bgd_profile_create_service_class_id_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static uint16_t service_classes[] = {
+ SDP_SERVICE_CLASS_BROWSE_GROUP_DESCRIPTOR
+ };
+
+ return (common_profile_create_service_class_id_list(
+ buf, eob,
+ (uint8_t const *) service_classes,
+ sizeof(service_classes)));
+}
+
+static int32_t
+bgd_profile_create_service_name(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static char service_name[] = "Public Browse Group Root";
+
+ return (common_profile_create_string8(
+ buf, eob,
+ (uint8_t const *) service_name, strlen(service_name)));
+}
+
+static int32_t
+bgd_profile_create_group_id(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ if (buf + 3 > eob)
+ return (-1);
+
+ SDP_PUT8(SDP_DATA_UUID16, buf);
+ SDP_PUT16(SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP, buf);
+
+ return (3);
+}
+
+static attr_t bgd_profile_attrs[] = {
+ { SDP_ATTR_SERVICE_RECORD_HANDLE,
+ common_profile_create_service_record_handle },
+ { SDP_ATTR_SERVICE_CLASS_ID_LIST,
+ bgd_profile_create_service_class_id_list },
+ { SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST,
+ common_profile_create_language_base_attribute_id_list },
+ { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET,
+ bgd_profile_create_service_name },
+ { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_DESCRIPTION_OFFSET,
+ bgd_profile_create_service_name },
+ { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_PROVIDER_NAME_OFFSET,
+ common_profile_create_service_provider_name },
+ { SDP_ATTR_GROUP_ID,
+ bgd_profile_create_group_id },
+ { 0, NULL } /* end entry */
+};
+
+profile_t bgd_profile_descriptor = {
+ SDP_SERVICE_CLASS_BROWSE_GROUP_DESCRIPTOR,
+ 0,
+ (profile_data_valid_p) NULL,
+ (attr_t const * const) &bgd_profile_attrs
+};
+
diff --git a/usr.sbin/bluetooth/sdpd/dun.c b/usr.sbin/bluetooth/sdpd/dun.c
new file mode 100644
index 0000000..e7aeb78
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/dun.c
@@ -0,0 +1,136 @@
+/*
+ * dun.c
+ *
+ * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: dun.c,v 1.5 2004/01/13 01:54:39 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <bluetooth.h>
+#include <sdp.h>
+#include <string.h>
+#include "profile.h"
+#include "provider.h"
+
+static int32_t
+dun_profile_create_service_class_id_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static uint16_t service_classes[] = {
+ SDP_SERVICE_CLASS_DIALUP_NETWORKING
+ };
+
+ return (common_profile_create_service_class_id_list(
+ buf, eob,
+ (uint8_t const *) service_classes,
+ sizeof(service_classes)));
+}
+
+static int32_t
+dun_profile_create_bluetooth_profile_descriptor_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static uint16_t profile_descriptor_list[] = {
+ SDP_SERVICE_CLASS_DIALUP_NETWORKING,
+ 0x0100
+ };
+
+ return (common_profile_create_bluetooth_profile_descriptor_list(
+ buf, eob,
+ (uint8_t const *) profile_descriptor_list,
+ sizeof(profile_descriptor_list)));
+}
+
+static int32_t
+dun_profile_create_service_name(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static char service_name[] = "DialUp networking";
+
+ return (common_profile_create_string8(
+ buf, eob,
+ (uint8_t const *) service_name, strlen(service_name)));
+}
+
+static int32_t
+dun_profile_create_protocol_descriptor_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ provider_p provider = (provider_p) data;
+ sdp_dun_profile_p dun = (sdp_dun_profile_p) provider->data;
+
+ return (rfcomm_profile_create_protocol_descriptor_list(
+ buf, eob,
+ (uint8_t const *) &dun->server_channel, 1));
+}
+
+static int32_t
+dun_profile_create_audio_feedback_support(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ provider_p provider = (provider_p) data;
+ sdp_dun_profile_p dun = (sdp_dun_profile_p) provider->data;
+
+ if (buf + 2 > eob)
+ return (-1);
+
+ SDP_PUT8(SDP_DATA_BOOL, buf);
+ SDP_PUT8(dun->audio_feedback_support, buf);
+
+ return (2);
+}
+
+static attr_t dun_profile_attrs[] = {
+ { SDP_ATTR_SERVICE_RECORD_HANDLE,
+ common_profile_create_service_record_handle },
+ { SDP_ATTR_SERVICE_CLASS_ID_LIST,
+ dun_profile_create_service_class_id_list },
+ { SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST,
+ dun_profile_create_bluetooth_profile_descriptor_list },
+ { SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST,
+ common_profile_create_language_base_attribute_id_list },
+ { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET,
+ dun_profile_create_service_name },
+ { SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
+ dun_profile_create_protocol_descriptor_list },
+ { SDP_ATTR_AUDIO_FEEDBACK_SUPPORT,
+ dun_profile_create_audio_feedback_support },
+ { 0, NULL } /* end entry */
+};
+
+profile_t dun_profile_descriptor = {
+ SDP_SERVICE_CLASS_DIALUP_NETWORKING,
+ sizeof(sdp_dun_profile_t),
+ common_profile_server_channel_valid,
+ (attr_t const * const) &dun_profile_attrs
+};
+
diff --git a/usr.sbin/bluetooth/sdpd/ftrn.c b/usr.sbin/bluetooth/sdpd/ftrn.c
new file mode 100644
index 0000000..bcebfc7
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/ftrn.c
@@ -0,0 +1,117 @@
+/*
+ * ftrn.c
+ *
+ * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: ftrn.c,v 1.5 2004/01/13 01:54:39 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <bluetooth.h>
+#include <sdp.h>
+#include <string.h>
+#include "profile.h"
+#include "provider.h"
+
+static int32_t
+ftrn_profile_create_service_class_id_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static uint16_t service_classes[] = {
+ SDP_SERVICE_CLASS_OBEX_FILE_TRANSFER
+ };
+
+ return (common_profile_create_service_class_id_list(
+ buf, eob,
+ (uint8_t const *) service_classes,
+ sizeof(service_classes)));
+}
+
+static int32_t
+ftrn_profile_create_bluetooth_profile_descriptor_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static uint16_t profile_descriptor_list[] = {
+ SDP_SERVICE_CLASS_OBEX_FILE_TRANSFER,
+ 0x0100
+ };
+
+ return (common_profile_create_bluetooth_profile_descriptor_list(
+ buf, eob,
+ (uint8_t const *) profile_descriptor_list,
+ sizeof(profile_descriptor_list)));
+}
+
+static int32_t
+ftrn_profile_create_service_name(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static char service_name[] = "OBEX File Transfer";
+
+ return (common_profile_create_string8(
+ buf, eob,
+ (uint8_t const *) service_name, strlen(service_name)));
+}
+
+static int32_t
+ftrn_profile_create_protocol_descriptor_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ provider_p provider = (provider_p) data;
+ sdp_ftrn_profile_p ftrn = (sdp_ftrn_profile_p) provider->data;
+
+ return (obex_profile_create_protocol_descriptor_list(
+ buf, eob,
+ (uint8_t const *) &ftrn->server_channel, 1));
+}
+
+static attr_t ftrn_profile_attrs[] = {
+ { SDP_ATTR_SERVICE_RECORD_HANDLE,
+ common_profile_create_service_record_handle },
+ { SDP_ATTR_SERVICE_CLASS_ID_LIST,
+ ftrn_profile_create_service_class_id_list },
+ { SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST,
+ ftrn_profile_create_bluetooth_profile_descriptor_list },
+ { SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST,
+ common_profile_create_language_base_attribute_id_list },
+ { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET,
+ ftrn_profile_create_service_name },
+ { SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
+ ftrn_profile_create_protocol_descriptor_list },
+ { 0, NULL } /* end entry */
+};
+
+profile_t ftrn_profile_descriptor = {
+ SDP_SERVICE_CLASS_OBEX_FILE_TRANSFER,
+ sizeof(sdp_ftrn_profile_t),
+ common_profile_server_channel_valid,
+ (attr_t const * const) &ftrn_profile_attrs
+};
+
diff --git a/usr.sbin/bluetooth/sdpd/gn.c b/usr.sbin/bluetooth/sdpd/gn.c
new file mode 100644
index 0000000..d35c0ee
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/gn.c
@@ -0,0 +1,172 @@
+/*
+ * gn.c
+ */
+
+/*-
+ * Copyright (c) 2008 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: gn.c,v 1.1 2008/03/11 00:02:42 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <bluetooth.h>
+#include <sdp.h>
+#include <string.h>
+#include "profile.h"
+#include "provider.h"
+
+static int32_t
+gn_profile_create_service_class_id_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static uint16_t service_classes[] = {
+ SDP_SERVICE_CLASS_GN
+ };
+
+ return (common_profile_create_service_class_id_list(
+ buf, eob,
+ (uint8_t const *) service_classes,
+ sizeof(service_classes)));
+}
+
+static int32_t
+gn_profile_create_bluetooth_profile_descriptor_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static uint16_t profile_descriptor_list[] = {
+ SDP_SERVICE_CLASS_GN,
+ 0x0100
+ };
+
+ return (common_profile_create_bluetooth_profile_descriptor_list(
+ buf, eob,
+ (uint8_t const *) profile_descriptor_list,
+ sizeof(profile_descriptor_list)));
+}
+
+static int32_t
+gn_profile_create_service_name(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static char service_name[] = "Group Ad-hoc Network";
+
+ return (common_profile_create_string8(
+ buf, eob,
+ (uint8_t const *) service_name, strlen(service_name)));
+}
+
+static int32_t
+gn_profile_create_service_description(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static char service_descr[] =
+ "Personal Group Ad-hoc Network Service";
+
+ return (common_profile_create_string8(
+ buf, eob,
+ (uint8_t const *) service_descr,
+ strlen(service_descr)));
+}
+
+static int32_t
+gn_profile_create_protocol_descriptor_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ provider_p provider = (provider_p) data;
+ sdp_gn_profile_p gn = (sdp_gn_profile_p) provider->data;
+
+ return (bnep_profile_create_protocol_descriptor_list(
+ buf, eob, (uint8_t const *) &gn->psm,
+ sizeof(gn->psm)));
+}
+
+static int32_t
+gn_profile_create_security_description(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ provider_p provider = (provider_p) data;
+ sdp_gn_profile_p gn = (sdp_gn_profile_p) provider->data;
+
+ return (bnep_profile_create_security_description(buf, eob,
+ (uint8_t const *) &gn->security_description,
+ sizeof(gn->security_description)));
+}
+
+static int32_t
+gn_profile_create_service_availability(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ provider_p provider = (provider_p) data;
+ sdp_gn_profile_p gn = (sdp_gn_profile_p) provider->data;
+
+ return (common_profile_create_service_availability(buf, eob,
+ &gn->load_factor, 1));
+}
+
+static int32_t
+gn_profile_data_valid(uint8_t const *data, uint32_t datalen)
+{
+ sdp_gn_profile_p gn = (sdp_gn_profile_p) data;
+
+ return ((gn->psm == 0)? 0 : 1);
+}
+
+static attr_t gn_profile_attrs[] = {
+ { SDP_ATTR_SERVICE_RECORD_HANDLE,
+ common_profile_create_service_record_handle },
+ { SDP_ATTR_SERVICE_CLASS_ID_LIST,
+ gn_profile_create_service_class_id_list },
+ { SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
+ gn_profile_create_protocol_descriptor_list },
+ { SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST,
+ common_profile_create_language_base_attribute_id_list },
+ { SDP_ATTR_SERVICE_AVAILABILITY,
+ gn_profile_create_service_availability },
+ { SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST,
+ gn_profile_create_bluetooth_profile_descriptor_list },
+ { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET,
+ gn_profile_create_service_name },
+ { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_DESCRIPTION_OFFSET,
+ gn_profile_create_service_description },
+ { SDP_ATTR_SECURITY_DESCRIPTION,
+ gn_profile_create_security_description },
+ { 0, NULL } /* end entry */
+};
+
+profile_t gn_profile_descriptor = {
+ SDP_SERVICE_CLASS_GN,
+ sizeof(sdp_gn_profile_t),
+ gn_profile_data_valid,
+ (attr_t const * const) &gn_profile_attrs
+};
+
diff --git a/usr.sbin/bluetooth/sdpd/irmc.c b/usr.sbin/bluetooth/sdpd/irmc.c
new file mode 100644
index 0000000..d28a120
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/irmc.c
@@ -0,0 +1,133 @@
+/*
+ * irmc.c
+ *
+ * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: irmc.c,v 1.6 2004/01/13 19:31:54 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <bluetooth.h>
+#include <sdp.h>
+#include <string.h>
+#include "profile.h"
+#include "provider.h"
+
+static int32_t
+irmc_profile_create_service_class_id_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static uint16_t service_classes[] = {
+ SDP_SERVICE_CLASS_IR_MC_SYNC
+ };
+
+ return (common_profile_create_service_class_id_list(
+ buf, eob,
+ (uint8_t const *) service_classes,
+ sizeof(service_classes)));
+}
+
+static int32_t
+irmc_profile_create_bluetooth_profile_descriptor_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static uint16_t profile_descriptor_list[] = {
+ SDP_SERVICE_CLASS_IR_MC_SYNC,
+ 0x0100
+ };
+
+ return (common_profile_create_bluetooth_profile_descriptor_list(
+ buf, eob,
+ (uint8_t const *) profile_descriptor_list,
+ sizeof(profile_descriptor_list)));
+}
+
+static int32_t
+irmc_profile_create_service_name(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static char service_name[] = "IrMC Synchronization";
+
+ return (common_profile_create_string8(
+ buf, eob,
+ (uint8_t const *) service_name, strlen(service_name)));
+}
+
+static int32_t
+irmc_profile_create_protocol_descriptor_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ provider_p provider = (provider_p) data;
+ sdp_irmc_profile_p irmc = (sdp_irmc_profile_p) provider->data;
+
+ return (obex_profile_create_protocol_descriptor_list(
+ buf, eob,
+ (uint8_t const *) &irmc->server_channel, 1));
+}
+
+static int32_t
+irmc_profile_create_supported_formats_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ provider_p provider = (provider_p) data;
+ sdp_irmc_profile_p irmc = (sdp_irmc_profile_p) provider->data;
+
+ return (obex_profile_create_supported_formats_list(
+ buf, eob,
+ (uint8_t const *) irmc->supported_formats,
+ irmc->supported_formats_size));
+}
+
+static attr_t irmc_profile_attrs[] = {
+ { SDP_ATTR_SERVICE_RECORD_HANDLE,
+ common_profile_create_service_record_handle },
+ { SDP_ATTR_SERVICE_CLASS_ID_LIST,
+ irmc_profile_create_service_class_id_list },
+ { SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST,
+ irmc_profile_create_bluetooth_profile_descriptor_list },
+ { SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST,
+ common_profile_create_language_base_attribute_id_list },
+ { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET,
+ irmc_profile_create_service_name },
+ { SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
+ irmc_profile_create_protocol_descriptor_list },
+ { SDP_ATTR_SUPPORTED_FORMATS_LIST,
+ irmc_profile_create_supported_formats_list },
+ { 0, NULL } /* end entry */
+};
+
+profile_t irmc_profile_descriptor = {
+ SDP_SERVICE_CLASS_IR_MC_SYNC,
+ sizeof(sdp_irmc_profile_t),
+ obex_profile_data_valid,
+ (attr_t const * const) &irmc_profile_attrs
+};
+
diff --git a/usr.sbin/bluetooth/sdpd/irmc_command.c b/usr.sbin/bluetooth/sdpd/irmc_command.c
new file mode 100644
index 0000000..10dafe0
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/irmc_command.c
@@ -0,0 +1,117 @@
+/*
+ * irmc_command_command_command.c
+ *
+ * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: irmc_command.c,v 1.5 2004/01/13 01:54:39 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <bluetooth.h>
+#include <sdp.h>
+#include <string.h>
+#include "profile.h"
+#include "provider.h"
+
+static int32_t
+irmc_command_profile_create_service_class_id_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static uint16_t service_classes[] = {
+ SDP_SERVICE_CLASS_IR_MC_SYNC_COMMAND
+ };
+
+ return (common_profile_create_service_class_id_list(
+ buf, eob,
+ (uint8_t const *) service_classes,
+ sizeof(service_classes)));
+}
+
+static int32_t
+irmc_command_profile_create_bluetooth_profile_descriptor_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static uint16_t profile_descriptor_list[] = {
+ SDP_SERVICE_CLASS_IR_MC_SYNC_COMMAND,
+ 0x0100
+ };
+
+ return (common_profile_create_bluetooth_profile_descriptor_list(
+ buf, eob,
+ (uint8_t const *) profile_descriptor_list,
+ sizeof(profile_descriptor_list)));
+}
+
+static int32_t
+irmc_command_profile_create_service_name(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static char service_name[] = "Sync Command Service";
+
+ return (common_profile_create_string8(
+ buf, eob,
+ (uint8_t const *) service_name, strlen(service_name)));
+}
+
+static int32_t
+irmc_command_profile_create_protocol_descriptor_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ provider_p provider = (provider_p) data;
+ sdp_irmc_command_profile_p irmc_command = (sdp_irmc_command_profile_p) provider->data;
+
+ return (obex_profile_create_protocol_descriptor_list(
+ buf, eob,
+ (uint8_t const *) &irmc_command->server_channel, 1));
+}
+
+static attr_t irmc_command_profile_attrs[] = {
+ { SDP_ATTR_SERVICE_RECORD_HANDLE,
+ common_profile_create_service_record_handle },
+ { SDP_ATTR_SERVICE_CLASS_ID_LIST,
+ irmc_command_profile_create_service_class_id_list },
+ { SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST,
+ irmc_command_profile_create_bluetooth_profile_descriptor_list },
+ { SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST,
+ common_profile_create_language_base_attribute_id_list },
+ { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET,
+ irmc_command_profile_create_service_name },
+ { SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
+ irmc_command_profile_create_protocol_descriptor_list },
+ { 0, NULL } /* end entry */
+};
+
+profile_t irmc_command_profile_descriptor = {
+ SDP_SERVICE_CLASS_IR_MC_SYNC_COMMAND,
+ sizeof(sdp_irmc_command_profile_t),
+ common_profile_server_channel_valid,
+ (attr_t const * const) &irmc_command_profile_attrs
+};
+
diff --git a/usr.sbin/bluetooth/sdpd/lan.c b/usr.sbin/bluetooth/sdpd/lan.c
new file mode 100644
index 0000000..2425a89
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/lan.c
@@ -0,0 +1,172 @@
+/*
+ * lan.c
+ *
+ * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: lan.c,v 1.5 2004/01/13 01:54:39 max Exp $
+ * $FreeBSD$
+ */
+
+#include <arpa/inet.h>
+#include <sys/queue.h>
+#include <bluetooth.h>
+#include <sdp.h>
+#include <stdio.h>
+#include <string.h>
+#include "profile.h"
+#include "provider.h"
+
+static int32_t
+lan_profile_create_service_class_id_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static uint16_t service_classes[] = {
+ SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP
+ };
+
+ return (common_profile_create_service_class_id_list(
+ buf, eob,
+ (uint8_t const *) service_classes,
+ sizeof(service_classes)));
+}
+
+static int32_t
+lan_profile_create_bluetooth_profile_descriptor_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static uint16_t profile_descriptor_list[] = {
+ SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP,
+ 0x0100
+ };
+
+ return (common_profile_create_bluetooth_profile_descriptor_list(
+ buf, eob,
+ (uint8_t const *) profile_descriptor_list,
+ sizeof(profile_descriptor_list)));
+}
+
+static int32_t
+lan_profile_create_service_name(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static char service_name[] = "LAN Access using PPP";
+
+ return (common_profile_create_string8(
+ buf, eob,
+ (uint8_t const *) service_name, strlen(service_name)));
+}
+
+static int32_t
+lan_profile_create_protocol_descriptor_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ provider_p provider = (provider_p) data;
+ sdp_lan_profile_p lan = (sdp_lan_profile_p) provider->data;
+
+ return (rfcomm_profile_create_protocol_descriptor_list(
+ buf, eob,
+ (uint8_t const *) &lan->server_channel, 1));
+}
+
+static int32_t
+lan_profile_create_service_availability(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ provider_p provider = (provider_p) data;
+ sdp_lan_profile_p lan = (sdp_lan_profile_p) provider->data;
+
+ return (common_profile_create_service_availability(buf, eob,
+ &lan->load_factor, 1));
+}
+
+static int32_t
+lan_profile_create_ip_subnet(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ provider_p provider = (provider_p) data;
+ sdp_lan_profile_p lan = (sdp_lan_profile_p) provider->data;
+ char net[32];
+ int32_t len;
+
+ len = snprintf(net, sizeof(net), "%s/%d",
+ inet_ntoa(* (struct in_addr *) &lan->ip_subnet),
+ lan->ip_subnet_radius);
+
+ if (len < 0 || buf + 2 + len > eob)
+ return (-1);
+
+ SDP_PUT8(SDP_DATA_STR8, buf);
+ SDP_PUT8(len, buf);
+ memcpy(buf, net, len);
+
+ return (2 + len);
+}
+
+static int32_t
+lan_profile_data_valid(uint8_t const *data, uint32_t datalen)
+{
+ sdp_lan_profile_p lan = (sdp_lan_profile_p) data;
+
+ if (lan->server_channel < 1 ||
+ lan->server_channel > 30 ||
+ lan->ip_subnet_radius > 32)
+ return (0);
+
+ return (1);
+}
+
+static attr_t lan_profile_attrs[] = {
+ { SDP_ATTR_SERVICE_RECORD_HANDLE,
+ common_profile_create_service_record_handle },
+ { SDP_ATTR_SERVICE_CLASS_ID_LIST,
+ lan_profile_create_service_class_id_list },
+ { SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST,
+ lan_profile_create_bluetooth_profile_descriptor_list },
+ { SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST,
+ common_profile_create_language_base_attribute_id_list },
+ { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET,
+ lan_profile_create_service_name },
+ { SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
+ lan_profile_create_protocol_descriptor_list },
+ { SDP_ATTR_SERVICE_AVAILABILITY,
+ lan_profile_create_service_availability },
+ { SDP_ATTR_IP_SUBNET,
+ lan_profile_create_ip_subnet },
+ { 0, NULL } /* end entry */
+};
+
+profile_t lan_profile_descriptor = {
+ SDP_SERVICE_CLASS_LAN_ACCESS_USING_PPP,
+ sizeof(sdp_lan_profile_t),
+ lan_profile_data_valid,
+ (attr_t const * const) &lan_profile_attrs
+};
+
diff --git a/usr.sbin/bluetooth/sdpd/log.c b/usr.sbin/bluetooth/sdpd/log.c
new file mode 100644
index 0000000..b03f7e1
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/log.c
@@ -0,0 +1,127 @@
+/*
+ * log.c
+ *
+ * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: log.c,v 1.1 2004/01/07 23:15:00 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <stdarg.h>
+#include <syslog.h>
+
+void
+log_open(char const *prog, int32_t log2stderr)
+{
+ openlog(prog, LOG_PID|LOG_NDELAY|(log2stderr? LOG_PERROR:0), LOG_USER);
+}
+
+void
+log_close(void)
+{
+ closelog();
+}
+
+void
+log_emerg(char const *message, ...)
+{
+ va_list ap;
+
+ va_start(ap, message);
+ vsyslog(LOG_EMERG, message, ap);
+ va_end(ap);
+}
+
+void
+log_alert(char const *message, ...)
+{
+ va_list ap;
+
+ va_start(ap, message);
+ vsyslog(LOG_ALERT, message, ap);
+ va_end(ap);
+}
+
+void
+log_crit(char const *message, ...)
+{
+ va_list ap;
+
+ va_start(ap, message);
+ vsyslog(LOG_CRIT, message, ap);
+ va_end(ap);
+}
+
+void
+log_err(char const *message, ...)
+{
+ va_list ap;
+
+ va_start(ap, message);
+ vsyslog(LOG_ERR, message, ap);
+ va_end(ap);
+}
+
+void
+log_warning(char const *message, ...)
+{
+ va_list ap;
+
+ va_start(ap, message);
+ vsyslog(LOG_WARNING, message, ap);
+ va_end(ap);
+}
+
+void
+log_notice(char const *message, ...)
+{
+ va_list ap;
+
+ va_start(ap, message);
+ vsyslog(LOG_NOTICE, message, ap);
+ va_end(ap);
+}
+
+void
+log_info(char const *message, ...)
+{
+ va_list ap;
+
+ va_start(ap, message);
+ vsyslog(LOG_INFO, message, ap);
+ va_end(ap);
+}
+
+void
+log_debug(char const *message, ...)
+{
+ va_list ap;
+
+ va_start(ap, message);
+ vsyslog(LOG_DEBUG, message, ap);
+ va_end(ap);
+}
+
diff --git a/usr.sbin/bluetooth/sdpd/log.h b/usr.sbin/bluetooth/sdpd/log.h
new file mode 100644
index 0000000..8c9ce77
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/log.h
@@ -0,0 +1,47 @@
+/*
+ * log.h
+ *
+ * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: log.h,v 1.1 2004/01/07 23:15:00 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _LOG_H_
+#define _LOG_H_
+
+void log_open (char const *prog, int32_t log2stderr);
+void log_close (void);
+void log_emerg (char const *message, ...);
+void log_alert (char const *message, ...);
+void log_crit (char const *message, ...);
+void log_err (char const *message, ...);
+void log_warning (char const *message, ...);
+void log_notice (char const *message, ...);
+void log_info (char const *message, ...);
+void log_debug (char const *message, ...);
+
+#endif /* ndef _LOG_H_ */
+
diff --git a/usr.sbin/bluetooth/sdpd/main.c b/usr.sbin/bluetooth/sdpd/main.c
new file mode 100644
index 0000000..1df3bf0
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/main.c
@@ -0,0 +1,235 @@
+/*
+ * main.c
+ *
+ * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: main.c,v 1.8 2004/01/13 19:31:54 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/select.h>
+#include <bluetooth.h>
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+#include <signal.h>
+#include <sdp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "log.h"
+#include "server.h"
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/queue.h>
+#include "profile.h"
+#include "provider.h"
+
+#define SDPD "sdpd"
+
+static int32_t drop_root (char const *user, char const *group);
+static void sighandler (int32_t s);
+static void usage (void);
+
+static int32_t done;
+
+/*
+ * Bluetooth Service Discovery Procotol (SDP) daemon
+ */
+
+int
+main(int argc, char *argv[])
+{
+ server_t server;
+ char const *control = SDP_LOCAL_PATH;
+ char const *user = "nobody", *group = "nobody";
+ int32_t detach = 1, opt;
+ struct sigaction sa;
+
+ while ((opt = getopt(argc, argv, "c:dg:hu:")) != -1) {
+ switch (opt) {
+ case 'c': /* control */
+ control = optarg;
+ break;
+
+ case 'd': /* do not detach */
+ detach = 0;
+ break;
+
+ case 'g': /* group */
+ group = optarg;
+ break;
+
+ case 'u': /* user */
+ user = optarg;
+ break;
+
+ case 'h':
+ default:
+ usage();
+ /* NOT REACHED */
+ }
+ }
+
+ log_open(SDPD, !detach);
+
+ /* Become daemon if required */
+ if (detach && daemon(0, 0) < 0) {
+ log_crit("Could not become daemon. %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ /* Set signal handlers */
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = sighandler;
+
+ if (sigaction(SIGTERM, &sa, NULL) < 0 ||
+ sigaction(SIGHUP, &sa, NULL) < 0 ||
+ sigaction(SIGINT, &sa, NULL) < 0) {
+ log_crit("Could not install signal handlers. %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ sa.sa_handler = SIG_IGN;
+ if (sigaction(SIGPIPE, &sa, NULL) < 0) {
+ log_crit("Could not install signal handlers. %s (%d)",
+ strerror(errno), errno);
+ exit(1);
+ }
+
+ /* Initialize server */
+ if (server_init(&server, control) < 0)
+ exit(1);
+
+ if ((user != NULL || group != NULL) && drop_root(user, group) < 0)
+ exit(1);
+
+ for (done = 0; !done; ) {
+ if (server_do(&server) != 0)
+ done ++;
+ }
+
+ server_shutdown(&server);
+ log_close();
+
+ return (0);
+}
+
+/*
+ * Drop root
+ */
+
+static int32_t
+drop_root(char const *user, char const *group)
+{
+ int uid, gid;
+ char *ep;
+
+ if ((uid = getuid()) != 0) {
+ log_notice("Cannot set uid/gid. Not a superuser");
+ return (0); /* dont do anything unless root */
+ }
+
+ gid = getgid();
+
+ if (user != NULL) {
+ uid = strtol(user, &ep, 10);
+ if (*ep != '\0') {
+ struct passwd *pwd = getpwnam(user);
+
+ if (pwd == NULL) {
+ log_err("Could not find passwd entry for " \
+ "user %s", user);
+ return (-1);
+ }
+
+ uid = pwd->pw_uid;
+ }
+ }
+
+ if (group != NULL) {
+ gid = strtol(group, &ep, 10);
+ if (*ep != '\0') {
+ struct group *grp = getgrnam(group);
+
+ if (grp == NULL) {
+ log_err("Could not find group entry for " \
+ "group %s", group);
+ return (-1);
+ }
+
+ gid = grp->gr_gid;
+ }
+ }
+
+ if (setgid(gid) < 0) {
+ log_err("Could not setgid(%s). %s (%d)",
+ group, strerror(errno), errno);
+ return (-1);
+ }
+
+ if (setuid(uid) < 0) {
+ log_err("Could not setuid(%s). %s (%d)",
+ user, strerror(errno), errno);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Signal handler
+ */
+
+static void
+sighandler(int32_t s)
+{
+ log_notice("Got signal %d. Total number of signals received %d",
+ s, ++ done);
+}
+
+/*
+ * Display usage information and quit
+ */
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+"Usage: %s [options]\n" \
+"Where options are:\n" \
+" -c specify control socket name (default %s)\n" \
+" -d do not detach (run in foreground)\n" \
+" -g grp specify group\n" \
+" -h display usage and exit\n" \
+" -u usr specify user\n",
+ SDPD, SDP_LOCAL_PATH);
+ exit(255);
+}
+
diff --git a/usr.sbin/bluetooth/sdpd/nap.c b/usr.sbin/bluetooth/sdpd/nap.c
new file mode 100644
index 0000000..5a857d8
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/nap.c
@@ -0,0 +1,209 @@
+/*
+ * nap.c
+ */
+
+/*-
+ * Copyright (c) 2008 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: nap.c,v 1.1 2008/03/11 00:02:42 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <bluetooth.h>
+#include <sdp.h>
+#include <string.h>
+#include "profile.h"
+#include "provider.h"
+
+static int32_t
+nap_profile_create_service_class_id_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static uint16_t service_classes[] = {
+ SDP_SERVICE_CLASS_NAP
+ };
+
+ return (common_profile_create_service_class_id_list(
+ buf, eob,
+ (uint8_t const *) service_classes,
+ sizeof(service_classes)));
+}
+
+static int32_t
+nap_profile_create_bluetooth_profile_descriptor_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static uint16_t profile_descriptor_list[] = {
+ SDP_SERVICE_CLASS_NAP,
+ 0x0100
+ };
+
+ return (common_profile_create_bluetooth_profile_descriptor_list(
+ buf, eob,
+ (uint8_t const *) profile_descriptor_list,
+ sizeof(profile_descriptor_list)));
+}
+
+static int32_t
+nap_profile_create_service_name(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static char service_name[] = "Network Access Point";
+
+ return (common_profile_create_string8(
+ buf, eob,
+ (uint8_t const *) service_name, strlen(service_name)));
+}
+
+static int32_t
+nap_profile_create_service_description(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static char service_descr[] = "Personal Ad-hoc Network Service";
+
+ return (common_profile_create_string8(
+ buf, eob,
+ (uint8_t const *) service_descr,
+ strlen(service_descr)));
+}
+
+static int32_t
+nap_profile_create_protocol_descriptor_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ provider_p provider = (provider_p) data;
+ sdp_nap_profile_p nap = (sdp_nap_profile_p) provider->data;
+
+ return (bnep_profile_create_protocol_descriptor_list(
+ buf, eob, (uint8_t const *) &nap->psm,
+ sizeof(nap->psm)));
+}
+
+static int32_t
+nap_profile_create_security_description(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ provider_p provider = (provider_p) data;
+ sdp_nap_profile_p nap = (sdp_nap_profile_p) provider->data;
+
+ return (bnep_profile_create_security_description(buf, eob,
+ (uint8_t const *) &nap->security_description,
+ sizeof(nap->security_description)));
+}
+
+static int32_t
+nap_profile_create_net_access_type(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ provider_p provider = (provider_p) data;
+ sdp_nap_profile_p nap = (sdp_nap_profile_p) provider->data;
+
+ if (buf + 3 > eob)
+ return (-1);
+
+ SDP_PUT8(SDP_DATA_UINT16, buf);
+ SDP_PUT16(nap->net_access_type, buf);
+
+ return (3);
+}
+
+static int32_t
+nap_profile_create_max_net_access_rate(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ provider_p provider = (provider_p) data;
+ sdp_nap_profile_p nap = (sdp_nap_profile_p) provider->data;
+
+ if (buf + 3 > eob)
+ return (-1);
+
+ SDP_PUT8(SDP_DATA_UINT16, buf);
+ SDP_PUT16(nap->max_net_access_rate, buf);
+
+ return (3);
+}
+
+static int32_t
+nap_profile_create_service_availability(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ provider_p provider = (provider_p) data;
+ sdp_nap_profile_p nap = (sdp_nap_profile_p) provider->data;
+
+ return (common_profile_create_service_availability(buf, eob,
+ &nap->load_factor, 1));
+}
+
+static int32_t
+nap_profile_data_valid(uint8_t const *data, uint32_t datalen)
+{
+ sdp_nap_profile_p nap = (sdp_nap_profile_p) data;
+
+ return ((nap->psm == 0)? 0 : 1);
+}
+
+static attr_t nap_profile_attrs[] = {
+ { SDP_ATTR_SERVICE_RECORD_HANDLE,
+ common_profile_create_service_record_handle },
+ { SDP_ATTR_SERVICE_CLASS_ID_LIST,
+ nap_profile_create_service_class_id_list },
+ { SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
+ nap_profile_create_protocol_descriptor_list },
+ { SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST,
+ common_profile_create_language_base_attribute_id_list },
+ { SDP_ATTR_SERVICE_AVAILABILITY,
+ nap_profile_create_service_availability },
+ { SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST,
+ nap_profile_create_bluetooth_profile_descriptor_list },
+ { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET,
+ nap_profile_create_service_name },
+ { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_DESCRIPTION_OFFSET,
+ nap_profile_create_service_description },
+ { SDP_ATTR_SECURITY_DESCRIPTION,
+ nap_profile_create_security_description },
+ { SDP_ATTR_NET_ACCESS_TYPE,
+ nap_profile_create_net_access_type },
+ { SDP_ATTR_MAX_NET_ACCESS_RATE,
+ nap_profile_create_max_net_access_rate },
+ { 0, NULL } /* end entry */
+};
+
+profile_t nap_profile_descriptor = {
+ SDP_SERVICE_CLASS_NAP,
+ sizeof(sdp_nap_profile_t),
+ nap_profile_data_valid,
+ (attr_t const * const) &nap_profile_attrs
+};
+
diff --git a/usr.sbin/bluetooth/sdpd/opush.c b/usr.sbin/bluetooth/sdpd/opush.c
new file mode 100644
index 0000000..36359da
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/opush.c
@@ -0,0 +1,133 @@
+/*
+ * opush.c
+ *
+ * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: opush.c,v 1.6 2004/01/13 19:31:54 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <bluetooth.h>
+#include <sdp.h>
+#include <string.h>
+#include "profile.h"
+#include "provider.h"
+
+static int32_t
+opush_profile_create_service_class_id_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static uint16_t service_classes[] = {
+ SDP_SERVICE_CLASS_OBEX_OBJECT_PUSH
+ };
+
+ return (common_profile_create_service_class_id_list(
+ buf, eob,
+ (uint8_t const *) service_classes,
+ sizeof(service_classes)));
+}
+
+static int32_t
+opush_profile_create_bluetooth_profile_descriptor_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static uint16_t profile_descriptor_list[] = {
+ SDP_SERVICE_CLASS_OBEX_OBJECT_PUSH,
+ 0x0100
+ };
+
+ return (common_profile_create_bluetooth_profile_descriptor_list(
+ buf, eob,
+ (uint8_t const *) profile_descriptor_list,
+ sizeof(profile_descriptor_list)));
+}
+
+static int32_t
+opush_profile_create_service_name(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static char service_name[] = "OBEX Object Push";
+
+ return (common_profile_create_string8(
+ buf, eob,
+ (uint8_t const *) service_name, strlen(service_name)));
+}
+
+static int32_t
+opush_profile_create_protocol_descriptor_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ provider_p provider = (provider_p) data;
+ sdp_opush_profile_p opush = (sdp_opush_profile_p) provider->data;
+
+ return (obex_profile_create_protocol_descriptor_list(
+ buf, eob,
+ (uint8_t const *) &opush->server_channel, 1));
+}
+
+static int32_t
+opush_profile_create_supported_formats_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ provider_p provider = (provider_p) data;
+ sdp_opush_profile_p opush = (sdp_opush_profile_p) provider->data;
+
+ return (obex_profile_create_supported_formats_list(
+ buf, eob,
+ (uint8_t const *) opush->supported_formats,
+ opush->supported_formats_size));
+}
+
+static attr_t opush_profile_attrs[] = {
+ { SDP_ATTR_SERVICE_RECORD_HANDLE,
+ common_profile_create_service_record_handle },
+ { SDP_ATTR_SERVICE_CLASS_ID_LIST,
+ opush_profile_create_service_class_id_list },
+ { SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST,
+ opush_profile_create_bluetooth_profile_descriptor_list },
+ { SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST,
+ common_profile_create_language_base_attribute_id_list },
+ { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET,
+ opush_profile_create_service_name },
+ { SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
+ opush_profile_create_protocol_descriptor_list },
+ { SDP_ATTR_SUPPORTED_FORMATS_LIST,
+ opush_profile_create_supported_formats_list },
+ { 0, NULL } /* end entry */
+};
+
+profile_t opush_profile_descriptor = {
+ SDP_SERVICE_CLASS_OBEX_OBJECT_PUSH,
+ sizeof(sdp_opush_profile_t),
+ obex_profile_data_valid,
+ (attr_t const * const) &opush_profile_attrs
+};
+
diff --git a/usr.sbin/bluetooth/sdpd/panu.c b/usr.sbin/bluetooth/sdpd/panu.c
new file mode 100644
index 0000000..e00f650
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/panu.c
@@ -0,0 +1,172 @@
+/*
+ * panu.c
+ */
+
+/*-
+ * Copyright (c) 2008 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: panu.c,v 1.1 2008/03/11 00:02:42 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <bluetooth.h>
+#include <sdp.h>
+#include <string.h>
+#include "profile.h"
+#include "provider.h"
+
+static int32_t
+panu_profile_create_service_class_id_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static uint16_t service_classes[] = {
+ SDP_SERVICE_CLASS_PANU
+ };
+
+ return (common_profile_create_service_class_id_list(
+ buf, eob,
+ (uint8_t const *) service_classes,
+ sizeof(service_classes)));
+}
+
+static int32_t
+panu_profile_create_bluetooth_profile_descriptor_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static uint16_t profile_descriptor_list[] = {
+ SDP_SERVICE_CLASS_PANU,
+ 0x0100
+ };
+
+ return (common_profile_create_bluetooth_profile_descriptor_list(
+ buf, eob,
+ (uint8_t const *) profile_descriptor_list,
+ sizeof(profile_descriptor_list)));
+}
+
+static int32_t
+panu_profile_create_service_name(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static char service_name[] = "Personal Ad-hoc User Service";
+
+ return (common_profile_create_string8(
+ buf, eob,
+ (uint8_t const *) service_name, strlen(service_name)));
+}
+
+static int32_t
+panu_profile_create_service_description(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static char service_descr[] =
+ "Personal Ad-hoc User Service";
+
+ return (common_profile_create_string8(
+ buf, eob,
+ (uint8_t const *) service_descr,
+ strlen(service_descr)));
+}
+
+static int32_t
+panu_profile_create_protocol_descriptor_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ provider_p provider = (provider_p) data;
+ sdp_panu_profile_p panu = (sdp_panu_profile_p) provider->data;
+
+ return (bnep_profile_create_protocol_descriptor_list(
+ buf, eob, (uint8_t const *) &panu->psm,
+ sizeof(panu->psm)));
+}
+
+static int32_t
+panu_profile_data_valid(uint8_t const *data, uint32_t datalen)
+{
+ sdp_panu_profile_p panu = (sdp_panu_profile_p) data;
+
+ return ((panu->psm == 0)? 0 : 1);
+}
+
+static int32_t
+panu_profile_create_service_availability(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ provider_p provider = (provider_p) data;
+ sdp_panu_profile_p panu = (sdp_panu_profile_p) provider->data;
+
+ return (common_profile_create_service_availability( buf, eob,
+ &panu->load_factor, 1));
+}
+
+static int32_t
+panu_profile_create_security_description(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ provider_p provider = (provider_p) data;
+ sdp_panu_profile_p panu = (sdp_panu_profile_p) provider->data;
+
+ return (bnep_profile_create_security_description(buf, eob,
+ (uint8_t const *) &panu->security_description,
+ sizeof(panu->security_description)));
+}
+
+static attr_t panu_profile_attrs[] = {
+ { SDP_ATTR_SERVICE_RECORD_HANDLE,
+ common_profile_create_service_record_handle },
+ { SDP_ATTR_SERVICE_CLASS_ID_LIST,
+ panu_profile_create_service_class_id_list },
+ { SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
+ panu_profile_create_protocol_descriptor_list },
+ { SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST,
+ common_profile_create_language_base_attribute_id_list },
+ { SDP_ATTR_SERVICE_AVAILABILITY,
+ panu_profile_create_service_availability },
+ { SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST,
+ panu_profile_create_bluetooth_profile_descriptor_list },
+ { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET,
+ panu_profile_create_service_name },
+ { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_DESCRIPTION_OFFSET,
+ panu_profile_create_service_description },
+ { SDP_ATTR_SECURITY_DESCRIPTION,
+ panu_profile_create_security_description },
+ { 0, NULL } /* end entry */
+};
+
+profile_t panu_profile_descriptor = {
+ SDP_SERVICE_CLASS_PANU,
+ sizeof(sdp_panu_profile_t),
+ panu_profile_data_valid,
+ (attr_t const * const) &panu_profile_attrs
+};
+
diff --git a/usr.sbin/bluetooth/sdpd/profile.c b/usr.sbin/bluetooth/sdpd/profile.c
new file mode 100644
index 0000000..f3dfaa7
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/profile.c
@@ -0,0 +1,497 @@
+/*
+ * profile.c
+ */
+
+/*-
+ * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: profile.c,v 1.6 2004/01/13 19:31:54 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <bluetooth.h>
+#include <sdp.h>
+#include <string.h>
+#include "profile.h"
+#include "provider.h"
+
+/*
+ * Lookup profile descriptor
+ */
+
+profile_p
+profile_get_descriptor(uint16_t uuid)
+{
+ extern profile_t dun_profile_descriptor;
+ extern profile_t ftrn_profile_descriptor;
+ extern profile_t irmc_profile_descriptor;
+ extern profile_t irmc_command_profile_descriptor;
+ extern profile_t lan_profile_descriptor;
+ extern profile_t opush_profile_descriptor;
+ extern profile_t sp_profile_descriptor;
+ extern profile_t nap_profile_descriptor;
+ extern profile_t gn_profile_descriptor;
+ extern profile_t panu_profile_descriptor;
+
+ static const profile_p profiles[] = {
+ &dun_profile_descriptor,
+ &ftrn_profile_descriptor,
+ &irmc_profile_descriptor,
+ &irmc_command_profile_descriptor,
+ &lan_profile_descriptor,
+ &opush_profile_descriptor,
+ &sp_profile_descriptor,
+ &nap_profile_descriptor,
+ &gn_profile_descriptor,
+ &panu_profile_descriptor
+ };
+
+ int32_t i;
+
+ for (i = 0; i < sizeof(profiles)/sizeof(profiles[0]); i++)
+ if (profiles[i]->uuid == uuid)
+ return (profiles[i]);
+
+ return (NULL);
+}
+
+/*
+ * Look attribute in the profile descripror
+ */
+
+profile_attr_create_p
+profile_get_attr(const profile_p profile, uint16_t attr)
+{
+ attr_p ad = (attr_p) profile->attrs;
+
+ for (; ad->create != NULL; ad ++)
+ if (ad->attr == attr)
+ return (ad->create);
+
+ return (NULL);
+}
+
+/*
+ * uint32 value32 - 5 bytes
+ */
+
+int32_t
+common_profile_create_service_record_handle(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ if (buf + 5 > eob)
+ return (-1);
+
+ SDP_PUT8(SDP_DATA_UINT32, buf);
+ SDP_PUT32(((provider_p) data)->handle, buf);
+
+ return (5);
+}
+
+/*
+ * seq8 len8 - 2 bytes
+ * uuid16 value16 - 3 bytes
+ * [ uuid16 value ]
+ */
+
+int32_t
+common_profile_create_service_class_id_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ int32_t len = 3 * (datalen >>= 1);
+
+ if (len <= 0 || len > 0xff || buf + 2 + len > eob)
+ return (-1);
+
+ SDP_PUT8(SDP_DATA_SEQ8, buf);
+ SDP_PUT8(len, buf);
+
+ for (; datalen > 0; datalen --) {
+ SDP_PUT8(SDP_DATA_UUID16, buf);
+ SDP_PUT16(*((uint16_t const *)data), buf);
+ data += sizeof(uint16_t);
+ }
+
+ return (2 + len);
+}
+
+/*
+ * seq8 len8 - 2 bytes
+ * seq 8 len8 - 2 bytes
+ * uuid16 value16 - 3 bytes
+ * uint16 value16 - 3 bytes
+ * [ seq 8 len8
+ * uuid16 value16
+ * uint16 value16 ]
+ */
+
+int32_t
+common_profile_create_bluetooth_profile_descriptor_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ int32_t len = 8 * (datalen >>= 2);
+
+ if (len <= 0 || len > 0xff || buf + 2 + len > eob)
+ return (-1);
+
+ SDP_PUT8(SDP_DATA_SEQ8, buf);
+ SDP_PUT8(len, buf);
+
+ for (; datalen > 0; datalen --) {
+ SDP_PUT8(SDP_DATA_SEQ8, buf);
+ SDP_PUT8(6, buf);
+ SDP_PUT8(SDP_DATA_UUID16, buf);
+ SDP_PUT16(*((uint16_t const *)data), buf);
+ data += sizeof(uint16_t);
+ SDP_PUT8(SDP_DATA_UINT16, buf);
+ SDP_PUT16(*((uint16_t const *)data), buf);
+ data += sizeof(uint16_t);
+ }
+
+ return (2 + len);
+}
+
+/*
+ * seq8 len8 - 2 bytes
+ * uint16 value16 - 3 bytes
+ * uint16 value16 - 3 bytes
+ * uint16 value16 - 3 bytes
+ */
+
+int32_t
+common_profile_create_language_base_attribute_id_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ if (buf + 11 > eob)
+ return (-1);
+
+ SDP_PUT8(SDP_DATA_SEQ8, buf);
+ SDP_PUT8(9, buf);
+
+ /*
+ * Language code per ISO 639:1988. Use "en".
+ */
+
+ SDP_PUT8(SDP_DATA_UINT16, buf);
+ SDP_PUT16(((0x65 << 8) | 0x6e), buf);
+
+ /*
+ * Encoding. Recommended is UTF-8. ISO639 UTF-8 MIBenum is 106
+ * (http://www.iana.org/assignments/character-sets)
+ */
+
+ SDP_PUT8(SDP_DATA_UINT16, buf);
+ SDP_PUT16(106, buf);
+
+ /*
+ * Offset (Primary Language Base is 0x100)
+ */
+
+ SDP_PUT8(SDP_DATA_UINT16, buf);
+ SDP_PUT16(SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID, buf);
+
+ return (11);
+}
+
+/*
+ * Common provider name is "FreeBSD"
+ */
+
+int32_t
+common_profile_create_service_provider_name(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ char provider_name[] = "FreeBSD";
+
+ return (common_profile_create_string8(buf, eob,
+ (uint8_t const *) provider_name,
+ strlen(provider_name)));
+}
+
+/*
+ * str8 len8 string
+ */
+
+int32_t
+common_profile_create_string8(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ if (datalen == 0 || datalen > 0xff || buf + 2 + datalen > eob)
+ return (-1);
+
+ SDP_PUT8(SDP_DATA_STR8, buf);
+ SDP_PUT8(datalen, buf);
+ memcpy(buf, data, datalen);
+
+ return (2 + datalen);
+}
+
+/*
+ * Service Availability
+ */
+
+int32_t
+common_profile_create_service_availability(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ if (datalen != 1 || buf + 2 > eob)
+ return (-1);
+
+ SDP_PUT8(SDP_DATA_UINT8, buf);
+ SDP_PUT8(data[0], buf);
+
+ return (2);
+}
+
+/*
+ * seq8 len8 - 2 bytes
+ * seq8 len8 - 2 bytes
+ * uuid16 value16 - 3 bytes
+ * seq8 len8 - 2 bytes
+ * uuid16 value16 - 3 bytes
+ * uint8 value8 - 2 bytes
+ */
+
+int32_t
+rfcomm_profile_create_protocol_descriptor_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ if (datalen != 1 || buf + 14 > eob)
+ return (-1);
+
+ SDP_PUT8(SDP_DATA_SEQ8, buf);
+ SDP_PUT8(12, buf);
+
+ SDP_PUT8(SDP_DATA_SEQ8, buf);
+ SDP_PUT8(3, buf);
+ SDP_PUT8(SDP_DATA_UUID16, buf);
+ SDP_PUT16(SDP_UUID_PROTOCOL_L2CAP, buf);
+
+ SDP_PUT8(SDP_DATA_SEQ8, buf);
+ SDP_PUT8(5, buf);
+ SDP_PUT8(SDP_DATA_UUID16, buf);
+ SDP_PUT16(SDP_UUID_PROTOCOL_RFCOMM, buf);
+ SDP_PUT8(SDP_DATA_UINT8, buf);
+ SDP_PUT8(*data, buf);
+
+ return (14);
+}
+
+/*
+ * seq8 len8 - 2 bytes
+ * seq8 len8 - 2 bytes
+ * uuid16 value16 - 3 bytes
+ * seq8 len8 - 2 bytes
+ * uuid16 value16 - 3 bytes
+ * uint8 value8 - 2 bytes
+ * seq8 len8 - 2 bytes
+ * uuid16 value16 - 3 bytes
+ */
+
+int32_t
+obex_profile_create_protocol_descriptor_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ if (datalen != 1 || buf + 19 > eob)
+ return (-1);
+
+ SDP_PUT8(SDP_DATA_SEQ8, buf);
+ SDP_PUT8(17, buf);
+
+ SDP_PUT8(SDP_DATA_SEQ8, buf);
+ SDP_PUT8(3, buf);
+ SDP_PUT8(SDP_DATA_UUID16, buf);
+ SDP_PUT16(SDP_UUID_PROTOCOL_L2CAP, buf);
+
+ SDP_PUT8(SDP_DATA_SEQ8, buf);
+ SDP_PUT8(5, buf);
+ SDP_PUT8(SDP_DATA_UUID16, buf);
+ SDP_PUT16(SDP_UUID_PROTOCOL_RFCOMM, buf);
+ SDP_PUT8(SDP_DATA_UINT8, buf);
+ SDP_PUT8(*data, buf);
+
+ SDP_PUT8(SDP_DATA_SEQ8, buf);
+ SDP_PUT8(3, buf);
+ SDP_PUT8(SDP_DATA_UUID16, buf);
+ SDP_PUT16(SDP_UUID_PROTOCOL_OBEX, buf);
+
+ return (19);
+}
+
+/*
+ * seq8 len8
+ * uint8 value8 - bytes
+ * [ uint8 value 8 ]
+ */
+
+int32_t
+obex_profile_create_supported_formats_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ int32_t len = 2 * datalen;
+
+ if (len <= 0 || len > 0xff || buf + 2 + len > eob)
+ return (-1);
+
+ SDP_PUT8(SDP_DATA_SEQ8, buf);
+ SDP_PUT8(len, buf);
+
+ for (; datalen > 0; datalen --) {
+ SDP_PUT8(SDP_DATA_UINT8, buf);
+ SDP_PUT8(*data++, buf);
+ }
+
+ return (2 + len);
+}
+
+/*
+ * do not check anything
+ */
+
+int32_t
+common_profile_always_valid(uint8_t const *data, uint32_t datalen)
+{
+ return (1);
+}
+
+/*
+ * verify server channel number (the first byte in the data)
+ */
+
+int32_t
+common_profile_server_channel_valid(uint8_t const *data, uint32_t datalen)
+{
+ if (data[0] < 1 || data[0] > 30)
+ return (0);
+
+ return (1);
+}
+
+/*
+ * verify server channel number and supported_formats_size
+ * sdp_opush_profile and sdp_irmc_profile
+ */
+
+int32_t
+obex_profile_data_valid(uint8_t const *data, uint32_t datalen)
+{
+ sdp_opush_profile_p opush = (sdp_opush_profile_p) data;
+
+ if (opush->server_channel < 1 ||
+ opush->server_channel > 30 ||
+ opush->supported_formats_size == 0 ||
+ opush->supported_formats_size > sizeof(opush->supported_formats))
+ return (0);
+
+ return (1);
+}
+
+/*
+ * BNEP protocol descriptor
+ */
+
+int32_t
+bnep_profile_create_protocol_descriptor_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ /* supported protocol types */
+ uint16_t ptype[] = {
+ 0x0800, /* IPv4 */
+ 0x0806, /* ARP */
+#ifdef INET6
+ 0x86dd, /* IPv6 */
+#endif
+ };
+
+ uint16_t i, psm, version = 0x0100,
+ nptypes = sizeof(ptype)/sizeof(ptype[0]),
+ nptypes_size = nptypes * 3;
+
+ if (datalen != 2 || 18 + nptypes_size > 255 ||
+ buf + 20 + nptypes_size > eob)
+ return (-1);
+
+ memcpy(&psm, data, sizeof(psm));
+
+ SDP_PUT8(SDP_DATA_SEQ8, buf);
+ SDP_PUT8(18 + nptypes_size, buf);
+
+ SDP_PUT8(SDP_DATA_SEQ8, buf);
+ SDP_PUT8(6, buf);
+ SDP_PUT8(SDP_DATA_UUID16, buf);
+ SDP_PUT16(SDP_UUID_PROTOCOL_L2CAP, buf);
+ SDP_PUT8(SDP_DATA_UINT16, buf);
+ SDP_PUT16(psm, buf);
+
+ SDP_PUT8(SDP_DATA_SEQ8, buf);
+ SDP_PUT8(8 + nptypes_size, buf);
+ SDP_PUT8(SDP_DATA_UUID16, buf);
+ SDP_PUT16(SDP_UUID_PROTOCOL_BNEP, buf);
+ SDP_PUT8(SDP_DATA_UINT16, buf);
+ SDP_PUT16(version, buf);
+ SDP_PUT8(SDP_DATA_SEQ8, buf);
+ SDP_PUT8(nptypes_size, buf);
+ for (i = 0; i < nptypes; i ++) {
+ SDP_PUT8(SDP_DATA_UINT16, buf);
+ SDP_PUT16(ptype[i], buf);
+ }
+
+ return (20 + nptypes_size);
+}
+
+/*
+ * BNEP security description
+ */
+
+int32_t
+bnep_profile_create_security_description(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ uint16_t security_descr;
+
+ if (datalen != 2 || buf + 3 > eob)
+ return (-1);
+
+ memcpy(&security_descr, data, sizeof(security_descr));
+
+ SDP_PUT8(SDP_DATA_UINT16, buf);
+ SDP_PUT16(security_descr, buf);
+
+ return (3);
+}
+
diff --git a/usr.sbin/bluetooth/sdpd/profile.h b/usr.sbin/bluetooth/sdpd/profile.h
new file mode 100644
index 0000000..0b0d86d
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/profile.h
@@ -0,0 +1,96 @@
+/*
+ * profile.h
+ */
+
+/*-
+ * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: profile.h,v 1.6 2004/01/13 19:31:54 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _PROFILE_H_
+#define _PROFILE_H_
+
+/*
+ * Attribute descriptor
+ */
+
+typedef int32_t (profile_attr_create_t)(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen);
+typedef profile_attr_create_t * profile_attr_create_p;
+
+typedef int32_t (profile_data_valid_t)(
+ uint8_t const *data, uint32_t datalen);
+typedef profile_data_valid_t * profile_data_valid_p;
+
+struct attr
+{
+ uint16_t attr; /* attribute id */
+ profile_attr_create_p create; /* create attr value */
+};
+
+typedef struct attr attr_t;
+typedef struct attr * attr_p;
+
+/*
+ * Profile descriptor
+ */
+
+
+struct profile
+{
+ uint16_t uuid; /* profile uuid */
+ uint16_t dsize; /* profile data size */
+ profile_data_valid_p valid; /* profile data validator */
+ attr_t const * const attrs; /* supported attributes */
+};
+
+typedef struct profile profile_t;
+typedef struct profile *profile_p;
+
+profile_p profile_get_descriptor(uint16_t uuid);
+profile_attr_create_p profile_get_attr(const profile_p profile, uint16_t attr);
+
+profile_attr_create_t common_profile_create_service_record_handle;
+profile_attr_create_t common_profile_create_service_class_id_list;
+profile_attr_create_t common_profile_create_bluetooth_profile_descriptor_list;
+profile_attr_create_t common_profile_create_language_base_attribute_id_list;
+profile_attr_create_t common_profile_create_service_provider_name;
+profile_attr_create_t common_profile_create_string8;
+profile_attr_create_t common_profile_create_service_availability;
+profile_attr_create_t rfcomm_profile_create_protocol_descriptor_list;
+profile_attr_create_t obex_profile_create_protocol_descriptor_list;
+profile_attr_create_t obex_profile_create_supported_formats_list;
+profile_attr_create_t bnep_profile_create_protocol_descriptor_list;
+profile_attr_create_t bnep_profile_create_security_description;
+
+profile_data_valid_t common_profile_always_valid;
+profile_data_valid_t common_profile_server_channel_valid;
+profile_data_valid_t obex_profile_data_valid;
+
+#endif /* ndef _PROFILE_H_ */
+
diff --git a/usr.sbin/bluetooth/sdpd/provider.c b/usr.sbin/bluetooth/sdpd/provider.c
new file mode 100644
index 0000000..b0f5347
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/provider.c
@@ -0,0 +1,196 @@
+/*
+ * provider.c
+ *
+ * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: provider.c,v 1.5 2004/01/13 01:54:39 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <bluetooth.h>
+#include <string.h>
+#include <stdlib.h>
+#include "profile.h"
+#include "provider.h"
+
+static TAILQ_HEAD(, provider) providers = TAILQ_HEAD_INITIALIZER(providers);
+static uint32_t change_state = 0;
+static uint32_t handle = 0;
+
+/*
+ * Register Service Discovery provider.
+ * Should not be called more the once.
+ */
+
+int32_t
+provider_register_sd(int32_t fd)
+{
+ extern profile_t sd_profile_descriptor;
+ extern profile_t bgd_profile_descriptor;
+
+ provider_p sd = calloc(1, sizeof(*sd));
+ provider_p bgd = calloc(1, sizeof(*bgd));
+
+ if (sd == NULL || bgd == NULL) {
+ if (sd != NULL)
+ free(sd);
+
+ if (bgd != NULL)
+ free(bgd);
+
+ return (-1);
+ }
+
+ sd->profile = &sd_profile_descriptor;
+ bgd->handle = 0;
+ sd->fd = fd;
+ TAILQ_INSERT_HEAD(&providers, sd, provider_next);
+
+ bgd->profile = &bgd_profile_descriptor;
+ bgd->handle = 1;
+ sd->fd = fd;
+ TAILQ_INSERT_AFTER(&providers, sd, bgd, provider_next);
+
+ change_state ++;
+
+ return (0);
+}
+
+/*
+ * Register new provider for a given profile, bdaddr and session.
+ */
+
+provider_p
+provider_register(profile_p const profile, bdaddr_p const bdaddr, int32_t fd,
+ uint8_t const *data, uint32_t datalen)
+{
+ provider_p provider = calloc(1, sizeof(*provider));
+
+ if (provider != NULL) {
+ provider->data = malloc(datalen);
+ if (provider->data != NULL) {
+ provider->profile = profile;
+ memcpy(provider->data, data, datalen);
+
+ /*
+ * Record handles 0x0 and 0x1 are reserved
+ * for SDP itself
+ */
+
+ if (++ handle <= 1)
+ handle = 2;
+
+ provider->handle = handle;
+
+ memcpy(&provider->bdaddr, bdaddr,
+ sizeof(provider->bdaddr));
+ provider->fd = fd;
+
+ TAILQ_INSERT_TAIL(&providers, provider, provider_next);
+ change_state ++;
+ } else {
+ free(provider);
+ provider = NULL;
+ }
+ }
+
+ return (provider);
+}
+
+/*
+ * Unregister provider
+ */
+
+void
+provider_unregister(provider_p provider)
+{
+ TAILQ_REMOVE(&providers, provider, provider_next);
+ if (provider->data != NULL)
+ free(provider->data);
+ free(provider);
+ change_state ++;
+}
+
+/*
+ * Update provider data
+ */
+
+int32_t
+provider_update(provider_p provider, uint8_t const *data, uint32_t datalen)
+{
+ uint8_t *new_data = (uint8_t *) realloc(provider->data, datalen);
+
+ if (new_data == NULL)
+ return (-1);
+
+ memcpy(new_data, data, datalen);
+ provider->data = new_data;
+
+ return (0);
+}
+
+/*
+ * Get a provider for given record handle
+ */
+
+provider_p
+provider_by_handle(uint32_t handle)
+{
+ provider_p provider = NULL;
+
+ TAILQ_FOREACH(provider, &providers, provider_next)
+ if (provider->handle == handle)
+ break;
+
+ return (provider);
+}
+
+/*
+ * Cursor access
+ */
+
+provider_p
+provider_get_first(void)
+{
+ return (TAILQ_FIRST(&providers));
+}
+
+provider_p
+provider_get_next(provider_p provider)
+{
+ return (TAILQ_NEXT(provider, provider_next));
+}
+
+/*
+ * Return change state
+ */
+
+uint32_t
+provider_get_change_state(void)
+{
+ return (change_state);
+}
+
diff --git a/usr.sbin/bluetooth/sdpd/provider.h b/usr.sbin/bluetooth/sdpd/provider.h
new file mode 100644
index 0000000..b48bc8d
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/provider.h
@@ -0,0 +1,75 @@
+/*
+ * provider.h
+ *
+ * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: provider.h,v 1.6 2004/01/13 01:54:39 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _PROVIDER_H_
+#define _PROVIDER_H_
+
+/*
+ * Provider of service
+ */
+
+struct profile;
+
+struct provider
+{
+ struct profile *profile; /* profile */
+ void *data; /* profile data */
+ uint32_t handle; /* record handle */
+ bdaddr_t bdaddr; /* provider's BDADDR */
+ int32_t fd; /* session descriptor */
+ TAILQ_ENTRY(provider) provider_next; /* all providers */
+};
+
+typedef struct provider provider_t;
+typedef struct provider * provider_p;
+
+#define provider_match_bdaddr(p, b) \
+ (memcmp(b, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0 || \
+ memcmp(&(p)->bdaddr, NG_HCI_BDADDR_ANY, sizeof(bdaddr_t)) == 0 || \
+ memcmp(&(p)->bdaddr, b, sizeof(bdaddr_t)) == 0)
+
+int32_t provider_register_sd (int32_t fd);
+provider_p provider_register (profile_p const profile,
+ bdaddr_p const bdaddr,
+ int32_t fd,
+ uint8_t const *data,
+ uint32_t datalen);
+
+void provider_unregister (provider_p provider);
+int32_t provider_update (provider_p provider,
+ uint8_t const *data,
+ uint32_t datalen);
+provider_p provider_by_handle (uint32_t handle);
+provider_p provider_get_first (void);
+provider_p provider_get_next (provider_p provider);
+uint32_t provider_get_change_state (void);
+
+#endif /* ndef _PROVIDER_H_ */
diff --git a/usr.sbin/bluetooth/sdpd/sar.c b/usr.sbin/bluetooth/sdpd/sar.c
new file mode 100644
index 0000000..4fc25d9
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/sar.c
@@ -0,0 +1,317 @@
+/*
+ * sar.c
+ *
+ * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: sar.c,v 1.2 2004/01/08 23:46:51 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <sys/uio.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <assert.h>
+#include <bluetooth.h>
+#include <errno.h>
+#include <sdp.h>
+#include <stdio.h> /* for NULL */
+#include "profile.h"
+#include "provider.h"
+#include "server.h"
+
+/*
+ * Prepare SDP attr/value pair. Check if profile implements the attribute
+ * and if so call the attribute value function.
+ *
+ * uint16 value16 - 3 bytes (attribute)
+ * value - N bytes (value)
+ */
+
+static int32_t
+server_prepare_attr_value_pair(
+ provider_p const provider, uint16_t attr,
+ uint8_t *buf, uint8_t const * const eob)
+{
+ profile_attr_create_p cf = profile_get_attr(provider->profile, attr);
+ int32_t len;
+
+ if (cf == NULL)
+ return (0); /* no attribute */
+
+ if (buf + 3 > eob)
+ return (-1);
+
+ SDP_PUT8(SDP_DATA_UINT16, buf);
+ SDP_PUT16(attr, buf);
+
+ len = cf(buf, eob, (uint8_t const *) provider, sizeof(*provider));
+ if (len < 0)
+ return (-1);
+
+ return (3 + len);
+}
+
+/*
+ * seq16 value16 - 3 bytes
+ * attr value - 3+ bytes
+ * [ attr value ]
+ */
+
+int32_t
+server_prepare_attr_list(provider_p const provider,
+ uint8_t const *req, uint8_t const * const req_end,
+ uint8_t *rsp, uint8_t const * const rsp_end)
+{
+ uint8_t *ptr = rsp + 3;
+ int32_t type, hi, lo, len;
+
+ if (ptr > rsp_end)
+ return (-1);
+
+ while (req < req_end) {
+ SDP_GET8(type, req);
+
+ switch (type) {
+ case SDP_DATA_UINT16:
+ if (req + 2 > req_end)
+ return (-1);
+
+ SDP_GET16(lo, req);
+ hi = lo;
+ break;
+
+ case SDP_DATA_UINT32:
+ if (req + 4 > req_end)
+ return (-1);
+
+ SDP_GET16(lo, req);
+ SDP_GET16(hi, req);
+ break;
+
+ default:
+ return (-1);
+ /* NOT REACHED */
+ }
+
+ for (; lo <= hi; lo ++) {
+ len = server_prepare_attr_value_pair(provider, lo, ptr, rsp_end);
+ if (len < 0)
+ return (-1);
+
+ ptr += len;
+ }
+ }
+
+ len = ptr - rsp; /* we put this much bytes in rsp */
+
+ /* Fix SEQ16 header for the rsp */
+ SDP_PUT8(SDP_DATA_SEQ16, rsp);
+ SDP_PUT16(len - 3, rsp);
+
+ return (len);
+}
+
+/*
+ * Prepare SDP Service Attribute Response
+ */
+
+int32_t
+server_prepare_service_attribute_response(server_p srv, int32_t fd)
+{
+ uint8_t const *req = srv->req + sizeof(sdp_pdu_t);
+ uint8_t const *req_end = req + ((sdp_pdu_p)(srv->req))->len;
+ uint8_t *rsp = srv->fdidx[fd].rsp;
+ uint8_t const *rsp_end = rsp + NG_L2CAP_MTU_MAXIMUM;
+
+ uint8_t *ptr = NULL;
+ provider_t *provider = NULL;
+ uint32_t handle;
+ int32_t type, rsp_limit, aidlen, cslen, cs;
+
+ /*
+ * Minimal Service Attribute Request request
+ *
+ * value32 - 4 bytes ServiceRecordHandle
+ * value16 - 2 bytes MaximumAttributeByteCount
+ * seq8 len8 - 2 bytes
+ * uint16 value16 - 3 bytes AttributeIDList
+ * value8 - 1 byte ContinuationState
+ */
+
+ if (req_end - req < 12)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ /* Get ServiceRecordHandle and MaximumAttributeByteCount */
+ SDP_GET32(handle, req);
+ SDP_GET16(rsp_limit, req);
+ if (rsp_limit <= 0)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ /* Get size of AttributeIDList */
+ aidlen = 0;
+ SDP_GET8(type, req);
+ switch (type) {
+ case SDP_DATA_SEQ8:
+ SDP_GET8(aidlen, req);
+ break;
+
+ case SDP_DATA_SEQ16:
+ SDP_GET16(aidlen, req);
+ break;
+
+ case SDP_DATA_SEQ32:
+ SDP_GET32(aidlen, req);
+ break;
+ }
+ if (aidlen <= 0)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ ptr = (uint8_t *) req + aidlen;
+
+ /* Get ContinuationState */
+ if (ptr + 1 > req_end)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ SDP_GET8(cslen, ptr);
+ if (cslen != 0) {
+ if (cslen != 2 || req_end - ptr != 2)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ SDP_GET16(cs, ptr);
+ } else
+ cs = 0;
+
+ /* Process the request. First, check continuation state */
+ if (srv->fdidx[fd].rsp_cs != cs)
+ return (SDP_ERROR_CODE_INVALID_CONTINUATION_STATE);
+ if (srv->fdidx[fd].rsp_size > 0)
+ return (0);
+
+ /* Lookup record handle */
+ if ((provider = provider_by_handle(handle)) == NULL)
+ return (SDP_ERROR_CODE_INVALID_SERVICE_RECORD_HANDLE);
+
+ /*
+ * Service Attribute Response format
+ *
+ * value16 - 2 bytes AttributeListByteCount (not incl.)
+ * seq8 len16 - 3 bytes
+ * attr value - 3+ bytes AttributeList
+ * [ attr value ]
+ */
+
+ cs = server_prepare_attr_list(provider, req, req+aidlen, rsp, rsp_end);
+ if (cs < 0)
+ return (SDP_ERROR_CODE_INSUFFICIENT_RESOURCES);
+
+ /* Set reply size (not counting PDU header and continuation state) */
+ srv->fdidx[fd].rsp_limit = srv->fdidx[fd].omtu - sizeof(sdp_pdu_t) - 2;
+ if (srv->fdidx[fd].rsp_limit > rsp_limit)
+ srv->fdidx[fd].rsp_limit = rsp_limit;
+
+ srv->fdidx[fd].rsp_size = cs;
+ srv->fdidx[fd].rsp_cs = 0;
+
+ return (0);
+}
+
+/*
+ * Send SDP Service [Search] Attribute Response
+ */
+
+int32_t
+server_send_service_attribute_response(server_p srv, int32_t fd)
+{
+ uint8_t *rsp = srv->fdidx[fd].rsp + srv->fdidx[fd].rsp_cs;
+ uint8_t *rsp_end = srv->fdidx[fd].rsp + srv->fdidx[fd].rsp_size;
+
+ struct iovec iov[4];
+ sdp_pdu_t pdu;
+ uint16_t bcount;
+ uint8_t cs[3];
+ int32_t size;
+
+ /* First update continuation state (assume we will send all data) */
+ size = rsp_end - rsp;
+ srv->fdidx[fd].rsp_cs += size;
+
+ if (size + 1 > srv->fdidx[fd].rsp_limit) {
+ /*
+ * We need to split out response. Add 3 more bytes for the
+ * continuation state and move rsp_end and rsp_cs backwards.
+ */
+
+ while ((rsp_end - rsp) + 3 > srv->fdidx[fd].rsp_limit) {
+ rsp_end --;
+ srv->fdidx[fd].rsp_cs --;
+ }
+
+ cs[0] = 2;
+ cs[1] = srv->fdidx[fd].rsp_cs >> 8;
+ cs[2] = srv->fdidx[fd].rsp_cs & 0xff;
+ } else
+ cs[0] = 0;
+
+ assert(rsp_end >= rsp);
+
+ bcount = rsp_end - rsp;
+
+ if (((sdp_pdu_p)(srv->req))->pid == SDP_PDU_SERVICE_ATTRIBUTE_REQUEST)
+ pdu.pid = SDP_PDU_SERVICE_ATTRIBUTE_RESPONSE;
+ else
+ pdu.pid = SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_RESPONSE;
+
+ pdu.tid = ((sdp_pdu_p)(srv->req))->tid;
+ pdu.len = htons(sizeof(bcount) + bcount + 1 + cs[0]);
+
+ bcount = htons(bcount);
+
+ iov[0].iov_base = &pdu;
+ iov[0].iov_len = sizeof(pdu);
+
+ iov[1].iov_base = &bcount;
+ iov[1].iov_len = sizeof(bcount);
+
+ iov[2].iov_base = rsp;
+ iov[2].iov_len = rsp_end - rsp;
+
+ iov[3].iov_base = cs;
+ iov[3].iov_len = 1 + cs[0];
+
+ do {
+ size = writev(fd, (struct iovec const *) &iov, sizeof(iov)/sizeof(iov[0]));
+ } while (size < 0 && errno == EINTR);
+
+ /* Check if we have sent (or failed to sent) last response chunk */
+ if (srv->fdidx[fd].rsp_cs == srv->fdidx[fd].rsp_size) {
+ srv->fdidx[fd].rsp_cs = 0;
+ srv->fdidx[fd].rsp_size = 0;
+ srv->fdidx[fd].rsp_limit = 0;
+ }
+
+ return ((size < 0)? errno : 0);
+}
+
diff --git a/usr.sbin/bluetooth/sdpd/scr.c b/usr.sbin/bluetooth/sdpd/scr.c
new file mode 100644
index 0000000..d0c9ec5
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/scr.c
@@ -0,0 +1,92 @@
+/*
+ * scr.c
+ *
+ * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: scr.c,v 1.1 2004/01/13 01:54:39 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <bluetooth.h>
+#include <errno.h>
+#include <sdp.h>
+#include <string.h>
+#include "profile.h"
+#include "provider.h"
+#include "server.h"
+
+/*
+ * Prepare Service Change response
+ */
+
+int32_t
+server_prepare_service_change_response(server_p srv, int32_t fd)
+{
+ uint8_t const *req = srv->req + sizeof(sdp_pdu_t);
+ uint8_t const *req_end = req + ((sdp_pdu_p)(srv->req))->len;
+ uint8_t *rsp = srv->fdidx[fd].rsp;
+
+ provider_t *provider = NULL;
+ uint32_t handle;
+
+ /*
+ * Minimal Service Change Request
+ *
+ * value32 - handle 4 bytes
+ */
+
+ if (!srv->fdidx[fd].control ||
+ !srv->fdidx[fd].priv || req_end - req < 4)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ /* Get handle */
+ SDP_GET32(handle, req);
+
+ /* Lookup provider */
+ provider = provider_by_handle(handle);
+ if (provider == NULL || provider->fd != fd)
+ return (SDP_ERROR_CODE_INVALID_SERVICE_RECORD_HANDLE);
+
+ /* Validate user data */
+ if (req_end - req < provider->profile->dsize ||
+ provider->profile->valid == NULL ||
+ (provider->profile->valid)(req, req_end - req) == 0)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ /* Update provider */
+ if (provider_update(provider, req, req_end - req) < 0)
+ return (SDP_ERROR_CODE_INSUFFICIENT_RESOURCES);
+
+ SDP_PUT16(0, rsp);
+
+ /* Set reply size */
+ srv->fdidx[fd].rsp_limit = srv->fdidx[fd].omtu - sizeof(sdp_pdu_t);
+ srv->fdidx[fd].rsp_size = rsp - srv->fdidx[fd].rsp;
+ srv->fdidx[fd].rsp_cs = 0;
+
+ return (0);
+}
+
diff --git a/usr.sbin/bluetooth/sdpd/sd.c b/usr.sbin/bluetooth/sdpd/sd.c
new file mode 100644
index 0000000..1743ea7
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/sd.c
@@ -0,0 +1,228 @@
+/*
+ * sd.c
+ *
+ * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: sd.c,v 1.4 2004/01/13 01:54:39 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <bluetooth.h>
+#include <sdp.h>
+#include <string.h>
+#include "profile.h"
+#include "provider.h"
+
+static int32_t
+sd_profile_create_service_class_id_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static uint16_t service_classes[] = {
+ SDP_SERVICE_CLASS_SERVICE_DISCOVERY_SERVER
+ };
+
+ return (common_profile_create_service_class_id_list(
+ buf, eob,
+ (uint8_t const *) service_classes,
+ sizeof(service_classes)));
+}
+
+static int32_t
+sd_profile_create_bluetooth_profile_descriptor_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static uint16_t profile_descriptor_list[] = {
+ SDP_SERVICE_CLASS_SERVICE_DISCOVERY_SERVER,
+ 0x0100
+ };
+
+ return (common_profile_create_bluetooth_profile_descriptor_list(
+ buf, eob,
+ (uint8_t const *) profile_descriptor_list,
+ sizeof(profile_descriptor_list)));
+}
+
+static int32_t
+sd_profile_create_service_id(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ if (buf + 3 > eob)
+ return (-1);
+
+ /*
+ * The ServiceID is a UUID that universally and uniquely identifies
+ * the service instance described by the service record. This service
+ * attribute is particularly useful if the same service is described
+ * by service records in more than one SDP server
+ */
+
+ SDP_PUT8(SDP_DATA_UUID16, buf);
+ SDP_PUT16(SDP_UUID_PROTOCOL_SDP, buf); /* XXX ??? */
+
+ return (3);
+}
+
+static int32_t
+sd_profile_create_service_name(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static char service_name[] = "Bluetooth service discovery";
+
+ return (common_profile_create_string8(
+ buf, eob,
+ (uint8_t const *) service_name, strlen(service_name)));
+}
+
+static int32_t
+sd_profile_create_protocol_descriptor_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ if (buf + 12 > eob)
+ return (-1);
+
+ SDP_PUT8(SDP_DATA_SEQ8, buf);
+ SDP_PUT8(10, buf);
+
+ SDP_PUT8(SDP_DATA_SEQ8, buf);
+ SDP_PUT8(3, buf);
+ SDP_PUT8(SDP_DATA_UUID16, buf);
+ SDP_PUT16(SDP_UUID_PROTOCOL_L2CAP, buf);
+
+ SDP_PUT8(SDP_DATA_SEQ8, buf);
+ SDP_PUT8(3, buf);
+ SDP_PUT8(SDP_DATA_UUID16, buf);
+ SDP_PUT16(SDP_UUID_PROTOCOL_SDP, buf);
+
+ return (12);
+}
+
+static int32_t
+sd_profile_create_browse_group_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ if (buf + 5 > eob)
+ return (-1);
+
+ SDP_PUT8(SDP_DATA_SEQ8, buf);
+ SDP_PUT8(3, buf);
+
+ /*
+ * The top-level browse group ID, called PublicBrowseRoot and
+ * representing the root of the browsing hierarchy, has the value
+ * 00001002-0000-1000-8000-00805F9B34FB (UUID16: 0x1002) from the
+ * Bluetooth Assigned Numbers document
+ */
+
+ SDP_PUT8(SDP_DATA_UUID16, buf);
+ SDP_PUT16(SDP_SERVICE_CLASS_PUBLIC_BROWSE_GROUP, buf);
+
+ return (5);
+}
+
+static int32_t
+sd_profile_create_version_number_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ if (buf + 5 > eob)
+ return (-1);
+
+ SDP_PUT8(SDP_DATA_SEQ8, buf);
+ SDP_PUT8(3, buf);
+
+ /*
+ * The VersionNumberList is a data element sequence in which each
+ * element of the sequence is a version number supported by the SDP
+ * server. A version number is a 16-bit unsigned integer consisting
+ * of two fields. The higher-order 8 bits contain the major version
+ * number field and the low-order 8 bits contain the minor version
+ * number field. The initial version of SDP has a major version of
+ * 1 and a minor version of 0
+ */
+
+ SDP_PUT8(SDP_DATA_UINT16, buf);
+ SDP_PUT16(0x0100, buf);
+
+ return (5);
+}
+
+static int32_t
+sd_profile_create_service_database_state(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ uint32_t change_state = provider_get_change_state();
+
+ if (buf + 5 > eob)
+ return (-1);
+
+ SDP_PUT8(SDP_DATA_UINT32, buf);
+ SDP_PUT32(change_state, buf);
+
+ return (5);
+}
+
+static attr_t sd_profile_attrs[] = {
+ { SDP_ATTR_SERVICE_RECORD_HANDLE,
+ common_profile_create_service_record_handle },
+ { SDP_ATTR_SERVICE_CLASS_ID_LIST,
+ sd_profile_create_service_class_id_list },
+ { SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST,
+ sd_profile_create_bluetooth_profile_descriptor_list },
+ { SDP_ATTR_SERVICE_ID,
+ sd_profile_create_service_id },
+ { SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST,
+ common_profile_create_language_base_attribute_id_list },
+ { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET,
+ sd_profile_create_service_name },
+ { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_DESCRIPTION_OFFSET,
+ sd_profile_create_service_name },
+ { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_PROVIDER_NAME_OFFSET,
+ common_profile_create_service_provider_name },
+ { SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
+ sd_profile_create_protocol_descriptor_list },
+ { SDP_ATTR_BROWSE_GROUP_LIST,
+ sd_profile_create_browse_group_list },
+ { SDP_ATTR_VERSION_NUMBER_LIST,
+ sd_profile_create_version_number_list },
+ { SDP_ATTR_SERVICE_DATABASE_STATE,
+ sd_profile_create_service_database_state },
+ { 0, NULL } /* end entry */
+};
+
+profile_t sd_profile_descriptor = {
+ SDP_SERVICE_CLASS_SERVICE_DISCOVERY_SERVER,
+ 0,
+ (profile_data_valid_p) NULL,
+ (attr_t const * const) &sd_profile_attrs
+};
+
diff --git a/usr.sbin/bluetooth/sdpd/sdpd.8 b/usr.sbin/bluetooth/sdpd/sdpd.8
new file mode 100644
index 0000000..0844e30
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/sdpd.8
@@ -0,0 +1,140 @@
+.\" Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: sdpd.8,v 1.1 2004/01/13 19:31:54 max Exp $
+.\" $FreeBSD$
+.\"
+.Dd January 13, 2004
+.Dt SDPD 8
+.Os
+.Sh NAME
+.Nm sdpd
+.Nd Bluetooth Service Discovery Protocol daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl dh
+.Op Fl c Ar path
+.Op Fl g Ar group
+.Op Fl u Ar user
+.Sh DESCRIPTION
+The
+.Nm
+daemon keeps track of the Bluetooth services registered on the host
+and responds to Service Discovery inquiries from the remote Bluetooth devices.
+.Pp
+In order to use any service remote Bluetooth device need to send Service
+Search and Service Attribute or Service Search Attribute request over
+Bluetooth L2CAP connection on SDP PSM (0x0001).
+The
+.Nm
+daemon will try to find matching Service Record in its Service Database
+and will send appropriate response back.
+The remote device then will process the response, extract all required
+information and will make a separate connection in order to use the service.
+.Pp
+Bluetooth applications, running on the host, register services with
+the local
+.Nm
+daemon.
+Operation like service registration, service removal and service change are
+performed over the control socket.
+It is possible to query entire content of the
+.Nm
+Service Database with
+.Xr sdpcontrol 8
+by issuing
+.Cm browse
+command on the control socket.
+.Pp
+The command line options are as follows:
+.Bl -tag -width indent
+.It Fl d
+Do not detach from the controlling terminal.
+.It Fl c Ar path
+Specify path to the control socket.
+The default path is
+.Pa /var/run/sdp .
+.It Fl g Ar group
+Specifies the group the
+.Nm
+should run as after it initializes.
+The value specified may be either a group name or a numeric group ID.
+This only works if
+.Nm
+was started as root.
+The default group name is
+.Dq Li nobody .
+.It Fl h
+Display usage message and exit.
+.It Fl u Ar user
+Specifies the user the
+.Nm
+should run as after it initializes.
+The value specified may be either a user name or a numeric user ID.
+This only works if
+.Nm
+was started as root.
+The default user name is
+.Dq Li nobody .
+.El
+.Sh CAVEAT
+The
+.Nm
+daemon
+will listen for incoming L2CAP connections on a wildcard BD_ADDR.
+.Pp
+In case of multiple Bluetooth devices connected to the same host it is
+possible to specify which services should be
+.Dq bound
+to which Bluetooth device.
+Such assignment should be done at service registration time.
+.Pp
+Requests to register, remove or change service can only be made via the
+control socket.
+The
+.Nm
+daemon will check peer's credentials and will only accept the request if
+the application has the same effective user ID as the
+.Dq Li root
+user ID.
+.Pp
+The
+.Nm
+daemon does not check for duplicated Service Records.
+It only performs minimal checking on the service data sent in the Service
+Register request.
+It is assumed that application must obtain all required resources such
+as RFCOMM channels etc., before registering the service.
+.Sh FILES
+.Bl -tag -width ".Pa /var/run/sdp" -compact
+.It Pa /var/run/sdp
+.El
+.Sh SEE ALSO
+.Xr sdp 3 ,
+.Xr sdpcontrol 8
+.Sh AUTHORS
+.An Maksim Yevmenkin Aq m_evmenkin@yahoo.com
+.Sh BUGS
+Most likely.
+Please report if found.
diff --git a/usr.sbin/bluetooth/sdpd/server.c b/usr.sbin/bluetooth/sdpd/server.c
new file mode 100644
index 0000000..816c6f5
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/server.c
@@ -0,0 +1,589 @@
+/*
+ * server.c
+ *
+ * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: server.c,v 1.6 2004/01/13 01:54:39 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/select.h>
+#include <sys/stat.h>
+#include <sys/queue.h>
+#include <sys/ucred.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <assert.h>
+#include <bluetooth.h>
+#include <errno.h>
+#include <pwd.h>
+#include <sdp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "log.h"
+#include "profile.h"
+#include "provider.h"
+#include "server.h"
+
+static void server_accept_client (server_p srv, int32_t fd);
+static int32_t server_process_request (server_p srv, int32_t fd);
+static int32_t server_send_error_response (server_p srv, int32_t fd,
+ uint16_t error);
+static void server_close_fd (server_p srv, int32_t fd);
+
+/*
+ * Initialize server
+ */
+
+int32_t
+server_init(server_p srv, char const *control)
+{
+ struct sockaddr_un un;
+ struct sockaddr_l2cap l2;
+ int32_t unsock, l2sock;
+ socklen_t size;
+ uint16_t imtu;
+
+ assert(srv != NULL);
+ assert(control != NULL);
+
+ memset(srv, 0, sizeof(srv));
+
+ /* Open control socket */
+ if (unlink(control) < 0 && errno != ENOENT) {
+ log_crit("Could not unlink(%s). %s (%d)",
+ control, strerror(errno), errno);
+ return (-1);
+ }
+
+ unsock = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (unsock < 0) {
+ log_crit("Could not create control socket. %s (%d)",
+ strerror(errno), errno);
+ return (-1);
+ }
+
+ memset(&un, 0, sizeof(un));
+ un.sun_len = sizeof(un);
+ un.sun_family = AF_LOCAL;
+ strlcpy(un.sun_path, control, sizeof(un.sun_path));
+
+ if (bind(unsock, (struct sockaddr *) &un, sizeof(un)) < 0) {
+ log_crit("Could not bind control socket. %s (%d)",
+ strerror(errno), errno);
+ close(unsock);
+ return (-1);
+ }
+
+ if (chmod(control, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) < 0) {
+ log_crit("Could not change permissions on control socket. " \
+ "%s (%d)", strerror(errno), errno);
+ close(unsock);
+ return (-1);
+ }
+
+ if (listen(unsock, 10) < 0) {
+ log_crit("Could not listen on control socket. %s (%d)",
+ strerror(errno), errno);
+ close(unsock);
+ return (-1);
+ }
+
+ /* Open L2CAP socket */
+ l2sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BLUETOOTH_PROTO_L2CAP);
+ if (l2sock < 0) {
+ log_crit("Could not create L2CAP socket. %s (%d)",
+ strerror(errno), errno);
+ close(unsock);
+ return (-1);
+ }
+
+ size = sizeof(imtu);
+ if (getsockopt(l2sock, SOL_L2CAP, SO_L2CAP_IMTU, &imtu, &size) < 0) {
+ log_crit("Could not get L2CAP IMTU. %s (%d)",
+ strerror(errno), errno);
+ close(unsock);
+ close(l2sock);
+ return (-1);
+ }
+
+ memset(&l2, 0, sizeof(l2));
+ l2.l2cap_len = sizeof(l2);
+ l2.l2cap_family = AF_BLUETOOTH;
+ memcpy(&l2.l2cap_bdaddr, NG_HCI_BDADDR_ANY, sizeof(l2.l2cap_bdaddr));
+ l2.l2cap_psm = htole16(NG_L2CAP_PSM_SDP);
+
+ if (bind(l2sock, (struct sockaddr *) &l2, sizeof(l2)) < 0) {
+ log_crit("Could not bind L2CAP socket. %s (%d)",
+ strerror(errno), errno);
+ close(unsock);
+ close(l2sock);
+ return (-1);
+ }
+
+ if (listen(l2sock, 10) < 0) {
+ log_crit("Could not listen on L2CAP socket. %s (%d)",
+ strerror(errno), errno);
+ close(unsock);
+ close(l2sock);
+ return (-1);
+ }
+
+ /* Allocate incoming buffer */
+ srv->imtu = (imtu > SDP_LOCAL_MTU)? imtu : SDP_LOCAL_MTU;
+ srv->req = (uint8_t *) calloc(srv->imtu, sizeof(srv->req[0]));
+ if (srv->req == NULL) {
+ log_crit("Could not allocate request buffer");
+ close(unsock);
+ close(l2sock);
+ return (-1);
+ }
+
+ /* Allocate memory for descriptor index */
+ srv->fdidx = (fd_idx_p) calloc(FD_SETSIZE, sizeof(srv->fdidx[0]));
+ if (srv->fdidx == NULL) {
+ log_crit("Could not allocate fd index");
+ free(srv->req);
+ close(unsock);
+ close(l2sock);
+ return (-1);
+ }
+
+ /* Register Service Discovery profile (attach it to control socket) */
+ if (provider_register_sd(unsock) < 0) {
+ log_crit("Could not register Service Discovery profile");
+ free(srv->fdidx);
+ free(srv->req);
+ close(unsock);
+ close(l2sock);
+ return (-1);
+ }
+
+ /*
+ * If we got here then everything is fine. Add both control sockets
+ * to the index.
+ */
+
+ FD_ZERO(&srv->fdset);
+ srv->maxfd = (unsock > l2sock)? unsock : l2sock;
+
+ FD_SET(unsock, &srv->fdset);
+ srv->fdidx[unsock].valid = 1;
+ srv->fdidx[unsock].server = 1;
+ srv->fdidx[unsock].control = 1;
+ srv->fdidx[unsock].priv = 0;
+ srv->fdidx[unsock].rsp_cs = 0;
+ srv->fdidx[unsock].rsp_size = 0;
+ srv->fdidx[unsock].rsp_limit = 0;
+ srv->fdidx[unsock].omtu = SDP_LOCAL_MTU;
+ srv->fdidx[unsock].rsp = NULL;
+
+ FD_SET(l2sock, &srv->fdset);
+ srv->fdidx[l2sock].valid = 1;
+ srv->fdidx[l2sock].server = 1;
+ srv->fdidx[l2sock].control = 0;
+ srv->fdidx[l2sock].priv = 0;
+ srv->fdidx[l2sock].rsp_cs = 0;
+ srv->fdidx[l2sock].rsp_size = 0;
+ srv->fdidx[l2sock].rsp_limit = 0;
+ srv->fdidx[l2sock].omtu = 0; /* unknown */
+ srv->fdidx[l2sock].rsp = NULL;
+
+ return (0);
+}
+
+/*
+ * Shutdown server
+ */
+
+void
+server_shutdown(server_p srv)
+{
+ int fd;
+
+ assert(srv != NULL);
+
+ for (fd = 0; fd < srv->maxfd + 1; fd ++)
+ if (srv->fdidx[fd].valid)
+ server_close_fd(srv, fd);
+
+ free(srv->req);
+ free(srv->fdidx);
+
+ memset(srv, 0, sizeof(*srv));
+}
+
+/*
+ * Do one server iteration
+ */
+
+int32_t
+server_do(server_p srv)
+{
+ fd_set fdset;
+ int32_t n, fd;
+
+ assert(srv != NULL);
+
+ /* Copy cached version of the fd set and call select */
+ memcpy(&fdset, &srv->fdset, sizeof(fdset));
+ n = select(srv->maxfd + 1, &fdset, NULL, NULL, NULL);
+ if (n < 0) {
+ if (errno == EINTR)
+ return (0);
+
+ log_err("Could not select(%d, %p). %s (%d)",
+ srv->maxfd + 1, &fdset, strerror(errno), errno);
+
+ return (-1);
+ }
+
+ /* Process descriptors */
+ for (fd = 0; fd < srv->maxfd + 1 && n > 0; fd ++) {
+ if (!FD_ISSET(fd, &fdset))
+ continue;
+
+ assert(srv->fdidx[fd].valid);
+ n --;
+
+ if (srv->fdidx[fd].server)
+ server_accept_client(srv, fd);
+ else if (server_process_request(srv, fd) != 0)
+ server_close_fd(srv, fd);
+ }
+
+ return (0);
+
+}
+
+/*
+ * Accept new client connection and register it with index
+ */
+
+static void
+server_accept_client(server_p srv, int32_t fd)
+{
+ uint8_t *rsp = NULL;
+ int32_t cfd, priv;
+ uint16_t omtu;
+ socklen_t size;
+
+ do {
+ cfd = accept(fd, NULL, NULL);
+ } while (cfd < 0 && errno == EINTR);
+
+ if (cfd < 0) {
+ log_err("Could not accept connection on %s socket. %s (%d)",
+ srv->fdidx[fd].control? "control" : "L2CAP",
+ strerror(errno), errno);
+ return;
+ }
+
+ assert(!FD_ISSET(cfd, &srv->fdset));
+ assert(!srv->fdidx[cfd].valid);
+
+ priv = 0;
+
+ if (!srv->fdidx[fd].control) {
+ /* Get local BD_ADDR */
+ size = sizeof(srv->req_sa);
+ if (getsockname(cfd,(struct sockaddr*)&srv->req_sa,&size) < 0) {
+ log_err("Could not get local BD_ADDR. %s (%d)",
+ strerror(errno), errno);
+ close(cfd);
+ return;
+ }
+
+ /* Get outgoing MTU */
+ size = sizeof(omtu);
+ if (getsockopt(cfd,SOL_L2CAP,SO_L2CAP_OMTU,&omtu,&size) < 0) {
+ log_err("Could not get L2CAP OMTU. %s (%d)",
+ strerror(errno), errno);
+ close(cfd);
+ return;
+ }
+
+ /*
+ * The maximum size of the L2CAP packet is 65536 bytes.
+ * The minimum L2CAP MTU is 43 bytes. That means we need
+ * 65536 / 43 = ~1524 chunks to transfer maximum packet
+ * size with minimum MTU. The "rsp_cs" field in fd_idx_t
+ * is 11 bit wide that gives us upto 2048 chunks.
+ */
+
+ if (omtu < NG_L2CAP_MTU_MINIMUM) {
+ log_err("L2CAP OMTU is too small (%d bytes)", omtu);
+ close(cfd);
+ return;
+ }
+ } else {
+ struct xucred cr;
+ struct passwd *pw;
+
+ /* Get peer's credentials */
+ memset(&cr, 0, sizeof(cr));
+ size = sizeof(cr);
+
+ if (getsockopt(cfd, 0, LOCAL_PEERCRED, &cr, &size) < 0) {
+ log_err("Could not get peer's credentials. %s (%d)",
+ strerror(errno), errno);
+ close(cfd);
+ return;
+ }
+
+ /* Check credentials */
+ pw = getpwuid(cr.cr_uid);
+ if (pw != NULL)
+ priv = (strcmp(pw->pw_name, "root") == 0);
+ else
+ log_warning("Could not verify credentials for uid %d",
+ cr.cr_uid);
+
+ memcpy(&srv->req_sa.l2cap_bdaddr, NG_HCI_BDADDR_ANY,
+ sizeof(srv->req_sa.l2cap_bdaddr));
+
+ omtu = srv->fdidx[fd].omtu;
+ }
+
+ /*
+ * Allocate buffer. This is an overkill, but we can not know how
+ * big our reply is going to be.
+ */
+
+ rsp = (uint8_t *) calloc(NG_L2CAP_MTU_MAXIMUM, sizeof(rsp[0]));
+ if (rsp == NULL) {
+ log_crit("Could not allocate response buffer");
+ close(cfd);
+ return;
+ }
+
+ /* Add client descriptor to the index */
+ FD_SET(cfd, &srv->fdset);
+ if (srv->maxfd < cfd)
+ srv->maxfd = cfd;
+ srv->fdidx[cfd].valid = 1;
+ srv->fdidx[cfd].server = 0;
+ srv->fdidx[cfd].control = srv->fdidx[fd].control;
+ srv->fdidx[cfd].priv = priv;
+ srv->fdidx[cfd].rsp_cs = 0;
+ srv->fdidx[cfd].rsp_size = 0;
+ srv->fdidx[cfd].rsp_limit = 0;
+ srv->fdidx[cfd].omtu = omtu;
+ srv->fdidx[cfd].rsp = rsp;
+}
+
+/*
+ * Process request from the client
+ */
+
+static int32_t
+server_process_request(server_p srv, int32_t fd)
+{
+ sdp_pdu_p pdu = (sdp_pdu_p) srv->req;
+ int32_t len, error;
+
+ assert(srv->imtu > 0);
+ assert(srv->req != NULL);
+ assert(FD_ISSET(fd, &srv->fdset));
+ assert(srv->fdidx[fd].valid);
+ assert(!srv->fdidx[fd].server);
+ assert(srv->fdidx[fd].rsp != NULL);
+ assert(srv->fdidx[fd].omtu >= NG_L2CAP_MTU_MINIMUM);
+
+ do {
+ len = read(fd, srv->req, srv->imtu);
+ } while (len < 0 && errno == EINTR);
+
+ if (len < 0) {
+ log_err("Could not receive SDP request from %s socket. %s (%d)",
+ srv->fdidx[fd].control? "control" : "L2CAP",
+ strerror(errno), errno);
+ return (-1);
+ }
+ if (len == 0) {
+ log_info("Client on %s socket has disconnected",
+ srv->fdidx[fd].control? "control" : "L2CAP");
+ return (-1);
+ }
+
+ if (len >= sizeof(*pdu) &&
+ sizeof(*pdu) + (pdu->len = ntohs(pdu->len)) == len) {
+ switch (pdu->pid) {
+ case SDP_PDU_SERVICE_SEARCH_REQUEST:
+ error = server_prepare_service_search_response(srv, fd);
+ break;
+
+ case SDP_PDU_SERVICE_ATTRIBUTE_REQUEST:
+ error = server_prepare_service_attribute_response(srv, fd);
+ break;
+
+ case SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_REQUEST:
+ error = server_prepare_service_search_attribute_response(srv, fd);
+ break;
+
+ case SDP_PDU_SERVICE_REGISTER_REQUEST:
+ error = server_prepare_service_register_response(srv, fd);
+ break;
+
+ case SDP_PDU_SERVICE_UNREGISTER_REQUEST:
+ error = server_prepare_service_unregister_response(srv, fd);
+ break;
+
+ case SDP_PDU_SERVICE_CHANGE_REQUEST:
+ error = server_prepare_service_change_response(srv, fd);
+ break;
+
+ default:
+ error = SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
+ break;
+ }
+ } else
+ error = SDP_ERROR_CODE_INVALID_PDU_SIZE;
+
+ if (error == 0) {
+ switch (pdu->pid) {
+ case SDP_PDU_SERVICE_SEARCH_REQUEST:
+ error = server_send_service_search_response(srv, fd);
+ break;
+
+ case SDP_PDU_SERVICE_ATTRIBUTE_REQUEST:
+ error = server_send_service_attribute_response(srv, fd);
+ break;
+
+ case SDP_PDU_SERVICE_SEARCH_ATTRIBUTE_REQUEST:
+ error = server_send_service_search_attribute_response(srv, fd);
+ break;
+
+ case SDP_PDU_SERVICE_REGISTER_REQUEST:
+ error = server_send_service_register_response(srv, fd);
+ break;
+
+ case SDP_PDU_SERVICE_UNREGISTER_REQUEST:
+ error = server_send_service_unregister_response(srv, fd);
+ break;
+
+ case SDP_PDU_SERVICE_CHANGE_REQUEST:
+ error = server_send_service_change_response(srv, fd);
+ break;
+
+ default:
+ error = SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX;
+ break;
+ }
+
+ if (error != 0)
+ log_err("Could not send SDP response to %s socket, " \
+ "pdu->pid=%d, pdu->tid=%d, error=%d",
+ srv->fdidx[fd].control? "control" : "L2CAP",
+ pdu->pid, ntohs(pdu->tid), error);
+ } else {
+ log_err("Could not process SDP request from %s socket, " \
+ "pdu->pid=%d, pdu->tid=%d, pdu->len=%d, len=%d, " \
+ "error=%d",
+ srv->fdidx[fd].control? "control" : "L2CAP",
+ pdu->pid, ntohs(pdu->tid), pdu->len, len, error);
+
+ error = server_send_error_response(srv, fd, error);
+ if (error != 0)
+ log_err("Could not send SDP error response to %s " \
+ "socket, pdu->pid=%d, pdu->tid=%d, error=%d",
+ srv->fdidx[fd].control? "control" : "L2CAP",
+ pdu->pid, ntohs(pdu->tid), error);
+ }
+
+ /* On error forget response (if any) */
+ if (error != 0) {
+ srv->fdidx[fd].rsp_cs = 0;
+ srv->fdidx[fd].rsp_size = 0;
+ srv->fdidx[fd].rsp_limit = 0;
+ }
+
+ return (error);
+}
+
+/*
+ * Send SDP_Error_Response PDU
+ */
+
+static int32_t
+server_send_error_response(server_p srv, int32_t fd, uint16_t error)
+{
+ int32_t size;
+
+ struct {
+ sdp_pdu_t pdu;
+ uint16_t error;
+ } __attribute__ ((packed)) rsp;
+
+ /* Prepare and send SDP error response */
+ rsp.pdu.pid = SDP_PDU_ERROR_RESPONSE;
+ rsp.pdu.tid = ((sdp_pdu_p)(srv->req))->tid;
+ rsp.pdu.len = htons(sizeof(rsp.error));
+ rsp.error = htons(error);
+
+ do {
+ size = write(fd, &rsp, sizeof(rsp));
+ } while (size < 0 && errno == EINTR);
+
+ return ((size < 0)? errno : 0);
+}
+
+/*
+ * Close descriptor and remove it from index
+ */
+
+static void
+server_close_fd(server_p srv, int32_t fd)
+{
+ provider_p provider = NULL, provider_next = NULL;
+
+ assert(FD_ISSET(fd, &srv->fdset));
+ assert(srv->fdidx[fd].valid);
+
+ close(fd);
+
+ FD_CLR(fd, &srv->fdset);
+ if (fd == srv->maxfd)
+ srv->maxfd --;
+
+ if (srv->fdidx[fd].rsp != NULL)
+ free(srv->fdidx[fd].rsp);
+
+ memset(&srv->fdidx[fd], 0, sizeof(srv->fdidx[fd]));
+
+ for (provider = provider_get_first();
+ provider != NULL;
+ provider = provider_next) {
+ provider_next = provider_get_next(provider);
+
+ if (provider->fd == fd)
+ provider_unregister(provider);
+ }
+}
+
diff --git a/usr.sbin/bluetooth/sdpd/server.h b/usr.sbin/bluetooth/sdpd/server.h
new file mode 100644
index 0000000..d7ef55e
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/server.h
@@ -0,0 +1,102 @@
+/*
+ * server.h
+ *
+ * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: server.h,v 1.5 2004/01/13 01:54:39 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _SERVER_H_
+#define _SERVER_H_
+
+/*
+ * File descriptor index entry
+ */
+
+struct fd_idx
+{
+ unsigned valid : 1; /* descriptor is valid */
+ unsigned server : 1; /* descriptor is listening */
+ unsigned control : 1; /* descriptor is a control socket */
+ unsigned priv : 1; /* descriptor is privileged */
+ unsigned reserved : 1;
+ unsigned rsp_cs : 11; /* response continuation state */
+ uint16_t rsp_size; /* response size */
+ uint16_t rsp_limit; /* response limit */
+ uint16_t omtu; /* outgoing MTU */
+ uint8_t *rsp; /* outgoing buffer */
+};
+
+typedef struct fd_idx fd_idx_t;
+typedef struct fd_idx * fd_idx_p;
+
+/*
+ * SDP server
+ */
+
+struct server
+{
+ uint32_t imtu; /* incoming MTU */
+ uint8_t *req; /* incoming buffer */
+ int32_t maxfd; /* max. descriptor is the set */
+ fd_set fdset; /* current descriptor set */
+ fd_idx_p fdidx; /* descriptor index */
+ struct sockaddr_l2cap req_sa; /* local address */
+};
+
+typedef struct server server_t;
+typedef struct server * server_p;
+
+/*
+ * External API
+ */
+
+int32_t server_init(server_p srv, const char *control);
+void server_shutdown(server_p srv);
+int32_t server_do(server_p srv);
+
+int32_t server_prepare_service_search_response(server_p srv, int32_t fd);
+int32_t server_send_service_search_response(server_p srv, int32_t fd);
+
+int32_t server_prepare_service_attribute_response(server_p srv, int32_t fd);
+int32_t server_send_service_attribute_response(server_p srv, int32_t fd);
+
+int32_t server_prepare_service_search_attribute_response(server_p srv, int32_t fd);
+#define server_send_service_search_attribute_response \
+ server_send_service_attribute_response
+
+int32_t server_prepare_service_register_response(server_p srv, int32_t fd);
+int32_t server_send_service_register_response(server_p srv, int32_t fd);
+
+int32_t server_prepare_service_unregister_response(server_p srv, int32_t fd);
+#define server_send_service_unregister_response \
+ server_send_service_register_response
+
+int32_t server_prepare_service_change_response(server_p srv, int32_t fd);
+#define server_send_service_change_response \
+ server_send_service_register_response
+
+#endif /* ndef _SERVER_H_ */
diff --git a/usr.sbin/bluetooth/sdpd/sp.c b/usr.sbin/bluetooth/sdpd/sp.c
new file mode 100644
index 0000000..31a9585a
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/sp.c
@@ -0,0 +1,117 @@
+/*
+ * sp.c
+ *
+ * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: sp.c,v 1.5 2004/01/13 01:54:39 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <bluetooth.h>
+#include <sdp.h>
+#include <string.h>
+#include "profile.h"
+#include "provider.h"
+
+static int32_t
+sp_profile_create_service_class_id_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static uint16_t service_classes[] = {
+ SDP_SERVICE_CLASS_SERIAL_PORT
+ };
+
+ return (common_profile_create_service_class_id_list(
+ buf, eob,
+ (uint8_t const *) service_classes,
+ sizeof(service_classes)));
+}
+
+static int32_t
+sp_profile_create_bluetooth_profile_descriptor_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static uint16_t profile_descriptor_list[] = {
+ SDP_SERVICE_CLASS_SERIAL_PORT,
+ 0x0100
+ };
+
+ return (common_profile_create_bluetooth_profile_descriptor_list(
+ buf, eob,
+ (uint8_t const *) profile_descriptor_list,
+ sizeof(profile_descriptor_list)));
+}
+
+static int32_t
+sp_profile_create_service_name(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ static char service_name[] = "Serial Port";
+
+ return (common_profile_create_string8(
+ buf, eob,
+ (uint8_t const *) service_name, strlen(service_name)));
+}
+
+static int32_t
+sp_profile_create_protocol_descriptor_list(
+ uint8_t *buf, uint8_t const * const eob,
+ uint8_t const *data, uint32_t datalen)
+{
+ provider_p provider = (provider_p) data;
+ sdp_sp_profile_p sp = (sdp_sp_profile_p) provider->data;
+
+ return (rfcomm_profile_create_protocol_descriptor_list(
+ buf, eob,
+ (uint8_t const *) &sp->server_channel, 1));
+}
+
+static attr_t sp_profile_attrs[] = {
+ { SDP_ATTR_SERVICE_RECORD_HANDLE,
+ common_profile_create_service_record_handle },
+ { SDP_ATTR_SERVICE_CLASS_ID_LIST,
+ sp_profile_create_service_class_id_list },
+ { SDP_ATTR_BLUETOOTH_PROFILE_DESCRIPTOR_LIST,
+ sp_profile_create_bluetooth_profile_descriptor_list },
+ { SDP_ATTR_LANGUAGE_BASE_ATTRIBUTE_ID_LIST,
+ common_profile_create_language_base_attribute_id_list },
+ { SDP_ATTR_PRIMARY_LANGUAGE_BASE_ID + SDP_ATTR_SERVICE_NAME_OFFSET,
+ sp_profile_create_service_name },
+ { SDP_ATTR_PROTOCOL_DESCRIPTOR_LIST,
+ sp_profile_create_protocol_descriptor_list },
+ { 0, NULL } /* end entry */
+};
+
+profile_t sp_profile_descriptor = {
+ SDP_SERVICE_CLASS_SERIAL_PORT,
+ sizeof(sdp_sp_profile_t),
+ common_profile_server_channel_valid,
+ (attr_t const * const) &sp_profile_attrs
+};
+
diff --git a/usr.sbin/bluetooth/sdpd/srr.c b/usr.sbin/bluetooth/sdpd/srr.c
new file mode 100644
index 0000000..fd636d5
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/srr.c
@@ -0,0 +1,139 @@
+/*
+ * srr.c
+ *
+ * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: srr.c,v 1.1 2004/01/13 01:54:39 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <sys/uio.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <assert.h>
+#include <bluetooth.h>
+#include <errno.h>
+#include <sdp.h>
+#include <string.h>
+#include "profile.h"
+#include "provider.h"
+#include "server.h"
+
+/*
+ * Prepare Service Register response
+ */
+
+int32_t
+server_prepare_service_register_response(server_p srv, int32_t fd)
+{
+ uint8_t const *req = srv->req + sizeof(sdp_pdu_t);
+ uint8_t const *req_end = req + ((sdp_pdu_p)(srv->req))->len;
+ uint8_t *rsp = srv->fdidx[fd].rsp;
+
+ profile_t *profile = NULL;
+ provider_t *provider = NULL;
+ bdaddr_t *bdaddr = NULL;
+ int32_t uuid;
+
+ /*
+ * Minimal Service Register Request
+ *
+ * value16 - uuid 2 bytes
+ * bdaddr - BD_ADDR 6 bytes
+ */
+
+ if (!srv->fdidx[fd].control ||
+ !srv->fdidx[fd].priv || req_end - req < 8)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ /* Get ServiceClass UUID */
+ SDP_GET16(uuid, req);
+
+ /* Get BD_ADDR */
+ bdaddr = (bdaddr_p) req;
+ req += sizeof(*bdaddr);
+
+ /* Lookup profile descriptror */
+ profile = profile_get_descriptor(uuid);
+ if (profile == NULL)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ /* Validate user data */
+ if (req_end - req < profile->dsize ||
+ profile->valid == NULL ||
+ (profile->valid)(req, req_end - req) == 0)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ /* Register provider */
+ provider = provider_register(profile, bdaddr, fd, req, req_end - req);
+ if (provider == NULL)
+ return (SDP_ERROR_CODE_INSUFFICIENT_RESOURCES);
+
+ SDP_PUT16(0, rsp);
+ SDP_PUT32(provider->handle, rsp);
+
+ /* Set reply size */
+ srv->fdidx[fd].rsp_limit = srv->fdidx[fd].omtu - sizeof(sdp_pdu_t);
+ srv->fdidx[fd].rsp_size = rsp - srv->fdidx[fd].rsp;
+ srv->fdidx[fd].rsp_cs = 0;
+
+ return (0);
+}
+
+/*
+ * Send Service Register Response
+ */
+
+int32_t
+server_send_service_register_response(server_p srv, int32_t fd)
+{
+ struct iovec iov[2];
+ sdp_pdu_t pdu;
+ int32_t size;
+
+ assert(srv->fdidx[fd].rsp_size < srv->fdidx[fd].rsp_limit);
+
+ pdu.pid = SDP_PDU_ERROR_RESPONSE;
+ pdu.tid = ((sdp_pdu_p)(srv->req))->tid;
+ pdu.len = htons(srv->fdidx[fd].rsp_size);
+
+ iov[0].iov_base = &pdu;
+ iov[0].iov_len = sizeof(pdu);
+
+ iov[1].iov_base = srv->fdidx[fd].rsp;
+ iov[1].iov_len = srv->fdidx[fd].rsp_size;
+
+ do {
+ size = writev(fd, (struct iovec const *) &iov, sizeof(iov)/sizeof(iov[0]));
+ } while (size < 0 && errno == EINTR);
+
+ srv->fdidx[fd].rsp_cs = 0;
+ srv->fdidx[fd].rsp_size = 0;
+ srv->fdidx[fd].rsp_limit = 0;
+
+ return ((size < 0)? errno : 0);
+}
+
diff --git a/usr.sbin/bluetooth/sdpd/ssar.c b/usr.sbin/bluetooth/sdpd/ssar.c
new file mode 100644
index 0000000..eac9235
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/ssar.c
@@ -0,0 +1,252 @@
+/*
+ * ssar.c
+ *
+ * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: ssar.c,v 1.4 2004/01/12 22:54:31 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <bluetooth.h>
+#include <sdp.h>
+#include <string.h>
+#include "profile.h"
+#include "provider.h"
+#include "server.h"
+#include "uuid-private.h"
+
+/* from sar.c */
+int32_t server_prepare_attr_list(provider_p const provider,
+ uint8_t const *req, uint8_t const * const req_end,
+ uint8_t *rsp, uint8_t const * const rsp_end);
+
+/*
+ * Prepare SDP Service Search Attribute Response
+ */
+
+int32_t
+server_prepare_service_search_attribute_response(server_p srv, int32_t fd)
+{
+ uint8_t const *req = srv->req + sizeof(sdp_pdu_t);
+ uint8_t const *req_end = req + ((sdp_pdu_p)(srv->req))->len;
+ uint8_t *rsp = srv->fdidx[fd].rsp;
+ uint8_t const *rsp_end = rsp + NG_L2CAP_MTU_MAXIMUM;
+
+ uint8_t const *sspptr = NULL, *aidptr = NULL;
+ uint8_t *ptr = NULL;
+
+ provider_t *provider = NULL;
+ int32_t type, rsp_limit, ssplen, aidlen, cslen, cs;
+ uint128_t uuid, puuid;
+
+ /*
+ * Minimal Service Search Attribute Request request
+ *
+ * seq8 len8 - 2 bytes
+ * uuid16 value16 - 3 bytes ServiceSearchPattern
+ * value16 - 2 bytes MaximumAttributeByteCount
+ * seq8 len8 - 2 bytes
+ * uint16 value16 - 3 bytes AttributeIDList
+ * value8 - 1 byte ContinuationState
+ */
+
+ if (req_end - req < 13)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ /* Get size of ServiceSearchPattern */
+ ssplen = 0;
+ SDP_GET8(type, req);
+ switch (type) {
+ case SDP_DATA_SEQ8:
+ SDP_GET8(ssplen, req);
+ break;
+
+ case SDP_DATA_SEQ16:
+ SDP_GET16(ssplen, req);
+ break;
+
+ case SDP_DATA_SEQ32:
+ SDP_GET32(ssplen, req);
+ break;
+ }
+ if (ssplen <= 0)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ sspptr = req;
+ req += ssplen;
+
+ /* Get MaximumAttributeByteCount */
+ if (req + 2 > req_end)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ SDP_GET16(rsp_limit, req);
+ if (rsp_limit <= 0)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ /* Get size of AttributeIDList */
+ if (req + 1 > req_end)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ aidlen = 0;
+ SDP_GET8(type, req);
+ switch (type) {
+ case SDP_DATA_SEQ8:
+ if (req + 1 > req_end)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ SDP_GET8(aidlen, req);
+ break;
+
+ case SDP_DATA_SEQ16:
+ if (req + 2 > req_end)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ SDP_GET16(aidlen, req);
+ break;
+
+ case SDP_DATA_SEQ32:
+ if (req + 4 > req_end)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ SDP_GET32(aidlen, req);
+ break;
+ }
+ if (aidlen <= 0)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ aidptr = req;
+ req += aidlen;
+
+ /* Get ContinuationState */
+ if (req + 1 > req_end)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ SDP_GET8(cslen, req);
+ if (cslen != 0) {
+ if (cslen != 2 || req_end - req != 2)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ SDP_GET16(cs, req);
+ } else
+ cs = 0;
+
+ /* Process the request. First, check continuation state */
+ if (srv->fdidx[fd].rsp_cs != cs)
+ return (SDP_ERROR_CODE_INVALID_CONTINUATION_STATE);
+ if (srv->fdidx[fd].rsp_size > 0)
+ return (0);
+
+ /*
+ * Service Search Attribute Response format
+ *
+ * value16 - 2 bytes AttributeListByteCount (not incl.)
+ * seq8 len16 - 3 bytes
+ * attr list - 3+ bytes AttributeLists
+ * [ attr list ]
+ */
+
+ ptr = rsp + 3;
+
+ while (ssplen > 0) {
+ SDP_GET8(type, sspptr);
+ ssplen --;
+
+ switch (type) {
+ case SDP_DATA_UUID16:
+ if (ssplen < 2)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ memcpy(&uuid, &uuid_base, sizeof(uuid));
+ uuid.b[2] = *sspptr ++;
+ uuid.b[3] = *sspptr ++;
+ ssplen -= 2;
+ break;
+
+ case SDP_DATA_UUID32:
+ if (ssplen < 4)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ memcpy(&uuid, &uuid_base, sizeof(uuid));
+ uuid.b[0] = *sspptr ++;
+ uuid.b[1] = *sspptr ++;
+ uuid.b[2] = *sspptr ++;
+ uuid.b[3] = *sspptr ++;
+ ssplen -= 4;
+ break;
+
+ case SDP_DATA_UUID128:
+ if (ssplen < 16)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ memcpy(uuid.b, sspptr, 16);
+ sspptr += 16;
+ ssplen -= 16;
+ break;
+
+ default:
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+ /* NOT REACHED */
+ }
+
+ for (provider = provider_get_first();
+ provider != NULL;
+ provider = provider_get_next(provider)) {
+ if (!provider_match_bdaddr(provider, &srv->req_sa.l2cap_bdaddr))
+ continue;
+
+ memcpy(&puuid, &uuid_base, sizeof(puuid));
+ puuid.b[2] = provider->profile->uuid >> 8;
+ puuid.b[3] = provider->profile->uuid;
+
+ if (memcmp(&uuid, &puuid, sizeof(uuid)) != 0 &&
+ memcmp(&uuid, &uuid_public_browse_group, sizeof(uuid)) != 0)
+ continue;
+
+ cs = server_prepare_attr_list(provider,
+ aidptr, aidptr + aidlen, ptr, rsp_end);
+ if (cs < 0)
+ return (SDP_ERROR_CODE_INSUFFICIENT_RESOURCES);
+
+ ptr += cs;
+ }
+ }
+
+ /* Set reply size (not counting PDU header and continuation state) */
+ srv->fdidx[fd].rsp_limit = srv->fdidx[fd].omtu - sizeof(sdp_pdu_t) - 2;
+ if (srv->fdidx[fd].rsp_limit > rsp_limit)
+ srv->fdidx[fd].rsp_limit = rsp_limit;
+
+ srv->fdidx[fd].rsp_size = ptr - rsp;
+ srv->fdidx[fd].rsp_cs = 0;
+
+ /* Fix AttributeLists sequence header */
+ ptr = rsp;
+ SDP_PUT8(SDP_DATA_SEQ16, ptr);
+ SDP_PUT16(srv->fdidx[fd].rsp_size - 3, ptr);
+
+ return (0);
+}
+
diff --git a/usr.sbin/bluetooth/sdpd/ssr.c b/usr.sbin/bluetooth/sdpd/ssr.c
new file mode 100644
index 0000000..ac27548
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/ssr.c
@@ -0,0 +1,282 @@
+/*
+ * ssr.c
+ *
+ * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: ssr.c,v 1.5 2004/01/13 01:54:39 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <sys/uio.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <assert.h>
+#include <bluetooth.h>
+#include <errno.h>
+#include <sdp.h>
+#include <string.h>
+#include "profile.h"
+#include "provider.h"
+#include "server.h"
+#include "uuid-private.h"
+
+/*
+ * Prepare SDP Service Search Response
+ */
+
+int32_t
+server_prepare_service_search_response(server_p srv, int32_t fd)
+{
+ uint8_t const *req = srv->req + sizeof(sdp_pdu_t);
+ uint8_t const *req_end = req + ((sdp_pdu_p)(srv->req))->len;
+ uint8_t *rsp = srv->fdidx[fd].rsp;
+ uint8_t const *rsp_end = rsp + NG_L2CAP_MTU_MAXIMUM;
+
+ uint8_t *ptr = NULL;
+ provider_t *provider = NULL;
+ int32_t type, ssplen, rsp_limit, rcount, cslen, cs;
+ uint128_t uuid, puuid;
+
+ /*
+ * Minimal SDP Service Search Request
+ *
+ * seq8 len8 - 2 bytes
+ * uuid16 value16 - 3 bytes ServiceSearchPattern
+ * value16 - 2 bytes MaximumServiceRecordCount
+ * value8 - 1 byte ContinuationState
+ */
+
+ if (req_end - req < 8)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ /* Get size of ServiceSearchPattern */
+ ssplen = 0;
+ SDP_GET8(type, req);
+ switch (type) {
+ case SDP_DATA_SEQ8:
+ SDP_GET8(ssplen, req);
+ break;
+
+ case SDP_DATA_SEQ16:
+ SDP_GET16(ssplen, req);
+ break;
+
+ case SDP_DATA_SEQ32:
+ SDP_GET32(ssplen, req);
+ break;
+ }
+ if (ssplen <= 0)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ ptr = (uint8_t *) req + ssplen;
+
+ /* Get MaximumServiceRecordCount */
+ if (ptr + 2 > req_end)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ SDP_GET16(rsp_limit, ptr);
+ if (rsp_limit <= 0)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ /* Get ContinuationState */
+ if (ptr + 1 > req_end)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ SDP_GET8(cslen, ptr);
+ if (cslen != 0) {
+ if (cslen != 2 || req_end - ptr != 2)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ SDP_GET16(cs, ptr);
+ } else
+ cs = 0;
+
+ /* Process the request. First, check continuation state */
+ if (srv->fdidx[fd].rsp_cs != cs)
+ return (SDP_ERROR_CODE_INVALID_CONTINUATION_STATE);
+ if (srv->fdidx[fd].rsp_size > 0)
+ return (0);
+
+ /*
+ * Service Search Response format
+ *
+ * value16 - 2 bytes TotalServiceRecordCount (not incl.)
+ * value16 - 2 bytes CurrentServiceRecordCount (not incl.)
+ * value32 - 4 bytes handle
+ * [ value32 ]
+ *
+ * Calculate how many record handles we can fit
+ * in our reply buffer and adjust rlimit.
+ */
+
+ ptr = rsp;
+ rcount = (rsp_end - ptr) / 4;
+ if (rcount < rsp_limit)
+ rsp_limit = rcount;
+
+ /* Look for the record handles */
+ for (rcount = 0; ssplen > 0 && rcount < rsp_limit; ) {
+ SDP_GET8(type, req);
+ ssplen --;
+
+ switch (type) {
+ case SDP_DATA_UUID16:
+ if (ssplen < 2)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ memcpy(&uuid, &uuid_base, sizeof(uuid));
+ uuid.b[2] = *req ++;
+ uuid.b[3] = *req ++;
+ ssplen -= 2;
+ break;
+
+ case SDP_DATA_UUID32:
+ if (ssplen < 4)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ memcpy(&uuid, &uuid_base, sizeof(uuid));
+ uuid.b[0] = *req ++;
+ uuid.b[1] = *req ++;
+ uuid.b[2] = *req ++;
+ uuid.b[3] = *req ++;
+ ssplen -= 4;
+ break;
+
+ case SDP_DATA_UUID128:
+ if (ssplen < 16)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ memcpy(uuid.b, req, 16);
+ req += 16;
+ ssplen -= 16;
+ break;
+
+ default:
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+ /* NOT REACHED */
+ }
+
+ for (provider = provider_get_first();
+ provider != NULL && rcount < rsp_limit;
+ provider = provider_get_next(provider)) {
+ if (!provider_match_bdaddr(provider, &srv->req_sa.l2cap_bdaddr))
+ continue;
+
+ memcpy(&puuid, &uuid_base, sizeof(puuid));
+ puuid.b[2] = provider->profile->uuid >> 8;
+ puuid.b[3] = provider->profile->uuid;
+
+ if (memcmp(&uuid, &puuid, sizeof(uuid)) == 0 ||
+ memcmp(&uuid, &uuid_public_browse_group, sizeof(uuid)) == 0) {
+ SDP_PUT32(provider->handle, ptr);
+ rcount ++;
+ }
+ }
+ }
+
+ /* Set reply size (not counting PDU header and continuation state) */
+ srv->fdidx[fd].rsp_limit = srv->fdidx[fd].omtu - sizeof(sdp_pdu_t) - 4;
+ srv->fdidx[fd].rsp_size = ptr - rsp;
+ srv->fdidx[fd].rsp_cs = 0;
+
+ return (0);
+}
+
+/*
+ * Send SDP Service Search Response
+ */
+
+int32_t
+server_send_service_search_response(server_p srv, int32_t fd)
+{
+ uint8_t *rsp = srv->fdidx[fd].rsp + srv->fdidx[fd].rsp_cs;
+ uint8_t *rsp_end = srv->fdidx[fd].rsp + srv->fdidx[fd].rsp_size;
+
+ struct iovec iov[4];
+ sdp_pdu_t pdu;
+ uint16_t rcounts[2];
+ uint8_t cs[3];
+ int32_t size;
+
+ /* First update continuation state (assume we will send all data) */
+ size = rsp_end - rsp;
+ srv->fdidx[fd].rsp_cs += size;
+
+ if (size + 1 > srv->fdidx[fd].rsp_limit) {
+ /*
+ * We need to split out response. Add 3 more bytes for the
+ * continuation state and move rsp_end and rsp_cs backwards.
+ */
+
+ while ((rsp_end - rsp) + 3 > srv->fdidx[fd].rsp_limit) {
+ rsp_end -= 4;
+ srv->fdidx[fd].rsp_cs -= 4;
+ }
+
+ cs[0] = 2;
+ cs[1] = srv->fdidx[fd].rsp_cs >> 8;
+ cs[2] = srv->fdidx[fd].rsp_cs & 0xff;
+ } else
+ cs[0] = 0;
+
+ assert(rsp_end >= rsp);
+
+ rcounts[0] = srv->fdidx[fd].rsp_size / 4; /* TotalServiceRecordCount */
+ rcounts[1] = (rsp_end - rsp) / 4; /* CurrentServiceRecordCount */
+
+ pdu.pid = SDP_PDU_SERVICE_SEARCH_RESPONSE;
+ pdu.tid = ((sdp_pdu_p)(srv->req))->tid;
+ pdu.len = htons(sizeof(rcounts) + rcounts[1] * 4 + 1 + cs[0]);
+
+ rcounts[0] = htons(rcounts[0]);
+ rcounts[1] = htons(rcounts[1]);
+
+ iov[0].iov_base = &pdu;
+ iov[0].iov_len = sizeof(pdu);
+
+ iov[1].iov_base = rcounts;
+ iov[1].iov_len = sizeof(rcounts);
+
+ iov[2].iov_base = rsp;
+ iov[2].iov_len = rsp_end - rsp;
+
+ iov[3].iov_base = cs;
+ iov[3].iov_len = 1 + cs[0];
+
+ do {
+ size = writev(fd, (struct iovec const *) &iov, sizeof(iov)/sizeof(iov[0]));
+ } while (size < 0 && errno == EINTR);
+
+ /* Check if we have sent (or failed to sent) last response chunk */
+ if (srv->fdidx[fd].rsp_cs == srv->fdidx[fd].rsp_size) {
+ srv->fdidx[fd].rsp_cs = 0;
+ srv->fdidx[fd].rsp_size = 0;
+ srv->fdidx[fd].rsp_limit = 0;
+ }
+
+ return ((size < 0)? errno : 0);
+}
+
diff --git a/usr.sbin/bluetooth/sdpd/sur.c b/usr.sbin/bluetooth/sdpd/sur.c
new file mode 100644
index 0000000..143eaf3
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/sur.c
@@ -0,0 +1,83 @@
+/*
+ * sur.c
+ *
+ * Copyright (c) 2004 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: sur.c,v 1.1 2004/01/13 01:54:39 max Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <bluetooth.h>
+#include <errno.h>
+#include <sdp.h>
+#include <string.h>
+#include "profile.h"
+#include "provider.h"
+#include "server.h"
+
+/*
+ * Prepare Service Unregister response
+ */
+
+int32_t
+server_prepare_service_unregister_response(server_p srv, int32_t fd)
+{
+ uint8_t const *req = srv->req + sizeof(sdp_pdu_t);
+ uint8_t const *req_end = req + ((sdp_pdu_p)(srv->req))->len;
+ uint8_t *rsp = srv->fdidx[fd].rsp;
+
+ provider_t *provider = NULL;
+ uint32_t handle;
+
+ /*
+ * Minimal Service Unregister Request
+ *
+ * value32 - uuid 4 bytes
+ */
+
+ if (!srv->fdidx[fd].control ||
+ !srv->fdidx[fd].priv || req_end - req < 4)
+ return (SDP_ERROR_CODE_INVALID_REQUEST_SYNTAX);
+
+ /* Get handle */
+ SDP_GET32(handle, req);
+
+ /* Lookup provider */
+ provider = provider_by_handle(handle);
+ if (provider == NULL || provider->fd != fd)
+ return (SDP_ERROR_CODE_INVALID_SERVICE_RECORD_HANDLE);
+
+ provider_unregister(provider);
+ SDP_PUT16(0, rsp);
+
+ /* Set reply size */
+ srv->fdidx[fd].rsp_limit = srv->fdidx[fd].omtu - sizeof(sdp_pdu_t);
+ srv->fdidx[fd].rsp_size = rsp - srv->fdidx[fd].rsp;
+ srv->fdidx[fd].rsp_cs = 0;
+
+ return (0);
+}
+
diff --git a/usr.sbin/bluetooth/sdpd/uuid-private.h b/usr.sbin/bluetooth/sdpd/uuid-private.h
new file mode 100644
index 0000000..db8d13b
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/uuid-private.h
@@ -0,0 +1,39 @@
+/*
+ * uuid-private.h
+ *
+ * Copyright (c) 2005 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: uuid-private.h,v 1.1 2004/12/09 18:20:26 max Exp $
+ * $FreeBSD$
+ */
+
+#ifndef _UUID_PRIVATE_H_
+#define _UUID_PRIVATE_H_
+
+extern uint128_t uuid_base;
+extern uint128_t uuid_public_browse_group;
+
+#endif /* ndef _UUID_PRIVATE_H_ */
+
diff --git a/usr.sbin/bluetooth/sdpd/uuid.c b/usr.sbin/bluetooth/sdpd/uuid.c
new file mode 100644
index 0000000..1989bc5
--- /dev/null
+++ b/usr.sbin/bluetooth/sdpd/uuid.c
@@ -0,0 +1,56 @@
+/*
+ * uuid.c
+ *
+ * Copyright (c) 2005 Maksim Yevmenkin <m_evmenkin@yahoo.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: uuid.c,v 1.1 2004/12/09 18:20:26 max Exp $
+ * $FreeBSD$
+ */
+
+#include <bluetooth.h>
+#include <sdp.h>
+#include <uuid.h>
+#include "uuid-private.h"
+
+uint128_t uuid_base = {
+ .b = {
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+ 0x10, 0x00,
+ 0x80, 0x00,
+ 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb
+ }
+};
+
+uint128_t uuid_public_browse_group = {
+ .b = {
+ 0x00, 0x00, 0x10, 0x02,
+ 0x00, 0x00,
+ 0x10, 0x00,
+ 0x80, 0x00,
+ 0x00, 0x80, 0x5f, 0x9b, 0x34, 0xfb
+ }
+};
+
diff --git a/usr.sbin/boot0cfg/Makefile b/usr.sbin/boot0cfg/Makefile
new file mode 100644
index 0000000..dc02cad
--- /dev/null
+++ b/usr.sbin/boot0cfg/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+PROG= boot0cfg
+MAN= boot0cfg.8
+
+WARNS?= 2
+
+DPADD= ${LIBGEOM}
+LDADD= -lgeom
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/boot0cfg/boot0cfg.8 b/usr.sbin/boot0cfg/boot0cfg.8
new file mode 100644
index 0000000..c7e664c
--- /dev/null
+++ b/usr.sbin/boot0cfg/boot0cfg.8
@@ -0,0 +1,214 @@
+.\" Copyright (c) 1999 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 AND CONTRIBUTORS``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED 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 13, 2009
+.Dt BOOT0CFG 8
+.Os
+.Sh NAME
+.Nm boot0cfg
+.Nd boot manager installation/configuration utility
+.Sh SYNOPSIS
+.Nm
+.Op Fl Bv
+.Op Fl b Ar boot0
+.Op Fl d Ar drive
+.Op Fl e Ar bell character
+.Op Fl f Ar file
+.Op Fl i Ar volume-id
+.Op Fl m Ar mask
+.Op Fl o Ar options
+.Op Fl s Ar slice
+.Op Fl t Ar ticks
+.Ar disk
+.Sh DESCRIPTION
+The
+.Fx
+.Sq boot0
+boot manager permits the operator to select from which disk and
+slice an i386 machine (PC) is booted.
+.Pp
+Note that what are referred to here as
+.Dq slices
+are typically called
+.Dq partitions
+in
+.No non- Ns Bx
+documentation relating to the PC.
+Typically, only non-removable disks are sliced.
+.Pp
+The
+.Nm
+utility optionally installs the
+.Sq boot0
+boot manager on the specified
+.Ar disk ;
+and allows various operational parameters to be configured.
+.Pp
+On PCs, a boot manager typically occupies sector 0 of a disk, which is
+known as the Master Boot Record (MBR).
+The MBR contains both code (to which control is passed by the PC BIOS)
+and data (an embedded table of defined slices).
+.Pp
+The options are:
+.Bl -tag -width indent
+.It Fl B
+Install the
+.Sq boot0
+boot manager.
+This option causes MBR code to be replaced, without
+affecting the embedded slice table.
+.It Fl b Ar boot0
+Specify which
+.Sq boot0
+image to use.
+The default is
+.Pa /boot/boot0
+which will use the video card as output, alternatively
+.Pa /boot/boot0sio
+can be used for output to the COM1 port.
+(Be aware that nothing will be output to the COM1 port unless the
+modem signals DSR and CTS are active.)
+.It Fl d Ar drive
+Specify the drive number used by the PC BIOS in referencing the drive
+which contains the specified
+.Ar disk .
+Typically this will be 0x80 for the first hard drive, 0x81 for the
+second hard drive, and so on; however any integer between 0 and 0xff
+is acceptable here.
+.It Fl e Ar bell character
+Set the character to be printed in case of input error.
+.It Fl f Ar file
+Specify that a backup copy of the preexisting MBR should be written to
+.Ar file .
+This file is created if it does not exist, and replaced if it does.
+.It Fl i Ar volume-id
+Specifies a volume-id (in the form XXXX-XXXX) to be saved at location
+0x1b8 in the MBR. This information is sometimes used by NT, XP and Vista
+to identify the disk drive. The option is only compatible with version 2.00
+of the 512-byte boot block.
+.It Fl m Ar mask
+Specify slices to be enabled/disabled, where
+.Ar mask
+is an integer between 0 (no slices enabled) and 0xf (all four slices
+enabled).
+.It Fl o Ar options
+A comma-separated string of any of the following options may be
+specified (with
+.Dq no
+prepended as necessary):
+.Bl -tag -width indent
+.It packet
+Use the disk packet (BIOS INT 0x13 extensions) interface,
+as opposed to the legacy (CHS) interface, when doing disk I/O.
+This allows booting above cylinder 1023, but requires specific
+BIOS support.
+The default is
+.Sq packet .
+.It setdrv
+Forces the drive containing the disk to be referenced using drive
+number definable by means of the -d option.
+The default is
+.Sq nosetdrv .
+.It update
+Allow the MBR to be updated by the boot manager.
+(The MBR may be updated to flag slices as
+.Sq active ,
+and to save slice selection information.)
+This is the default; a
+.Sq noupdate
+option causes the MBR to be treated as read-only.
+.El
+.It Fl s Ar slice
+Set the default boot selection to
+.Ar slice .
+Values between 1 and 4 refer to slices; a value of 5 refers to the
+option of booting from a second disk.
+.It Fl t Ar ticks
+Set the timeout value to
+.Ar ticks .
+(There are approximately 18.2 ticks per second.)
+.It Fl v
+Verbose: display information about the slices defined, etc.
+.El
+.Sh NOTE
+Protection mechanisms in the
+.Xr geom 4
+subsystem might prevent
+.Nm
+from being able to update the MBR on a mounted disk.
+Instructions for temporarily disabling these protection mechanisms
+can be found in the
+.Xr geom 4
+manpage. Specifically, do a
+.Pp
+.Dl sysctl kern.geom.debugflags=0x10
+.Pp
+to allow writing to the MBR, and restore it to 0 afterwards.
+.Pp
+.Sh FILES
+.Bl -tag -width /boot/boot0sio -compact
+.It Pa /boot/boot0
+The default
+.Sq boot0
+image
+.It Pa /boot/boot0sio
+Image for serial consoles (COM1,9600,8,N,1,MODEM)
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+To boot slice 2 on the next boot:
+.Pp
+.Dl "boot0cfg -s 2 ad0"
+.Pp
+To enable just slices 1 and 2 in the menu:
+.Pp
+.Dl "boot0cfg -m 0x3 ad0"
+.Pp
+To go back to non-interactive booting, use
+.Xr fdisk 8
+to install the default MBR:
+.Pp
+.Dl "fdisk -B ad0"
+.Pp
+.Sh SEE ALSO
+.Xr geom 4 ,
+.Xr boot 8 ,
+.Xr fdisk 8
+.Sh AUTHORS
+.An Robert Nordier Aq rnordier@FreeBSD.org .
+.Sh BUGS
+Use of the
+.Sq packet
+option may cause
+.Sq boot0
+to fail, depending on the nature of BIOS support.
+.Pp
+Use of the
+.Sq setdrv
+option with an incorrect -d operand may cause the boot0 code
+to write the MBR to the wrong disk, thus trashing its previous
+content. Be careful.
diff --git a/usr.sbin/boot0cfg/boot0cfg.c b/usr.sbin/boot0cfg/boot0cfg.c
new file mode 100644
index 0000000..cd3bfe2
--- /dev/null
+++ b/usr.sbin/boot0cfg/boot0cfg.c
@@ -0,0 +1,569 @@
+/*
+ * Copyright (c) 2008 Luigi Rizzo
+ * Copyright (c) 1999 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 AND CONTRIBUTORS``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED 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/disklabel.h>
+#include <sys/diskmbr.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgeom.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define MBRSIZE 512 /* master boot record size */
+
+#define OFF_VERSION 0x1b0 /* offset: version number, only boot0version */
+#define OFF_SERIAL 0x1b8 /* offset: volume serial number */
+#define OFF_PTBL 0x1be /* offset: partition table */
+#define OFF_MAGIC 0x1fe /* offset: magic number */
+/*
+ * Offsets to the parameters of the 512-byte boot block.
+ * For historical reasons they are set as macros
+ */
+struct opt_offsets {
+ int opt;
+ int drive;
+ int flags;
+ int ticks;
+};
+
+struct opt_offsets b0_ofs[] = {
+ { 0x0, 0x0, 0x0, 0x0 }, /* no boot block */
+ { 0x1b9, 0x1ba, 0x1bb, 0x1bc }, /* original block */
+ { 0x1b5, 0x1b6, 0x1b7, 0x1bc }, /* NT_SERIAL block */
+};
+
+int b0_ver; /* boot block version set by boot0bs */
+
+#define OFF_OPT (b0_ofs[b0_ver].opt) /* default boot option */
+#define OFF_DRIVE (b0_ofs[b0_ver].drive) /* setdrv drive */
+#define OFF_FLAGS (b0_ofs[b0_ver].flags) /* option flags */
+#define OFF_TICKS (b0_ofs[b0_ver].ticks) /* clock ticks */
+
+
+#define cv2(p) ((p)[0] | (p)[1] << 010)
+
+#define mk2(p, x) \
+ (p)[0] = (u_int8_t)(x), \
+ (p)[1] = (u_int8_t)((x) >> 010)
+
+static const struct {
+ const char *tok;
+ int def;
+} opttbl[] = {
+ {"packet", 0},
+ {"update", 1},
+ {"setdrv", 0}
+};
+static const int nopt = sizeof(opttbl) / sizeof(opttbl[0]);
+
+static const char fmt0[] = "# flag start chs type"
+ " end chs offset size\n";
+
+static const char fmt1[] = "%d 0x%02x %4u:%3u:%2u 0x%02x"
+ " %4u:%3u:%2u %10u %10u\n";
+
+static int read_mbr(const char *, u_int8_t **, int);
+static void write_mbr(const char *, int, u_int8_t *, int);
+static void display_mbr(u_int8_t *);
+static int boot0version(const u_int8_t *);
+static int boot0bs(const u_int8_t *);
+static void stropt(const char *, int *, int *);
+static int argtoi(const char *, int, int, int);
+static int set_bell(u_int8_t *, int, int);
+static void usage(void);
+
+unsigned vol_id[5]; /* 4 plus 1 for flag */
+
+int v_flag;
+/*
+ * Boot manager installation/configuration utility.
+ */
+int
+main(int argc, char *argv[])
+{
+ u_int8_t *mbr, *boot0;
+ int boot0_size, mbr_size;
+ const char *bpath, *fpath;
+ char *disk;
+ int B_flag, o_flag;
+ int d_arg, m_arg, s_arg, t_arg;
+ int o_and, o_or, o_e = -1;
+ int up, c;
+
+ bpath = "/boot/boot0";
+ fpath = NULL;
+ B_flag = v_flag = o_flag = 0;
+ d_arg = m_arg = s_arg = t_arg = -1;
+ o_and = 0xff;
+ o_or = 0;
+ while ((c = getopt(argc, argv, "Bvb:d:e:f:i:m:o:s:t:")) != -1)
+ switch (c) {
+ case 'B':
+ B_flag = 1;
+ break;
+ case 'v':
+ v_flag = 1;
+ break;
+ case 'b':
+ bpath = optarg;
+ break;
+ case 'd':
+ d_arg = argtoi(optarg, 0, 0xff, 'd');
+ break;
+ case 'e':
+ if (optarg[0] == '0' && optarg[1] == 'x')
+ sscanf(optarg, "0x%02x", &o_e);
+ else
+ o_e = optarg[0];
+ break;
+ case 'f':
+ fpath = optarg;
+ break;
+ case 'i':
+ if (sscanf(optarg, "%02x%02x-%02x%02x",
+ vol_id, vol_id+1, vol_id+2, vol_id+3) == 4)
+ vol_id[4] = 1;
+ else
+ errx(1, "bad argument %s", optarg);
+ break;
+ case 'm':
+ m_arg = argtoi(optarg, 0, 0xf, 'm');
+ break;
+ case 'o':
+ stropt(optarg, &o_and, &o_or);
+ o_flag = 1;
+ break;
+ case 's':
+ s_arg = argtoi(optarg, 1, 5, 's');
+ break;
+ case 't':
+ t_arg = argtoi(optarg, 1, 0xffff, 't');
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 1)
+ usage();
+ disk = g_device_path(*argv);
+ if (disk == NULL)
+ errx(1, "Unable to get providername for %s\n", *argv);
+ up = B_flag || d_arg != -1 || m_arg != -1 || o_flag || s_arg != -1
+ || t_arg != -1;
+
+ /* open the disk and read in the existing mbr. Either here or
+ * when reading the block from disk, we do check for the version
+ * and abort if a suitable block is not found.
+ */
+ mbr_size = read_mbr(disk, &mbr, !B_flag);
+
+ /* save the existing MBR if we are asked to do so */
+ if (fpath)
+ write_mbr(fpath, O_CREAT | O_TRUNC, mbr, mbr_size);
+
+ /*
+ * If we are installing the boot loader, read it from disk and copy the
+ * slice table over from the existing MBR. If not, then point boot0
+ * back at the MBR we just read in. After this, boot0 is the data to
+ * write back to disk if we are going to do a write.
+ */
+ if (B_flag) {
+ boot0_size = read_mbr(bpath, &boot0, 1);
+ memcpy(boot0 + OFF_PTBL, mbr + OFF_PTBL,
+ sizeof(struct dos_partition) * NDOSPART);
+ if (b0_ver == 2) /* volume serial number support */
+ memcpy(boot0 + OFF_SERIAL, mbr + OFF_SERIAL, 4);
+ } else {
+ boot0 = mbr;
+ boot0_size = mbr_size;
+ }
+
+ /* set the drive */
+ if (d_arg != -1)
+ boot0[OFF_DRIVE] = d_arg;
+
+ /* set various flags */
+ if (m_arg != -1) {
+ boot0[OFF_FLAGS] &= 0xf0;
+ boot0[OFF_FLAGS] |= m_arg;
+ }
+ if (o_flag) {
+ boot0[OFF_FLAGS] &= o_and;
+ boot0[OFF_FLAGS] |= o_or;
+ }
+
+ /* set the default boot selection */
+ if (s_arg != -1)
+ boot0[OFF_OPT] = s_arg - 1;
+
+ /* set the timeout */
+ if (t_arg != -1)
+ mk2(boot0 + OFF_TICKS, t_arg);
+
+ /* set the bell char */
+ if (o_e != -1 && set_bell(boot0, o_e, 0) != -1)
+ up = 1;
+
+ if (vol_id[4]) {
+ if (b0_ver != 2)
+ errx(1, "incompatible boot block, cannot set volume ID");
+ boot0[OFF_SERIAL] = vol_id[0];
+ boot0[OFF_SERIAL+1] = vol_id[1];
+ boot0[OFF_SERIAL+2] = vol_id[2];
+ boot0[OFF_SERIAL+3] = vol_id[3];
+ up = 1; /* force update */
+ }
+ /* write the MBR back to disk */
+ if (up)
+ write_mbr(disk, 0, boot0, boot0_size);
+
+ /* display the MBR */
+ if (v_flag)
+ display_mbr(boot0);
+
+ /* clean up */
+ if (mbr != boot0)
+ free(boot0);
+ free(mbr);
+ free(disk);
+
+ return 0;
+}
+
+/* get or set the 'bell' character to be used in case of errors.
+ * Lookup for a certain code sequence, return -1 if not found.
+ */
+static int
+set_bell(u_int8_t *mbr, int new_bell, int report)
+{
+ /* lookup sequence: 0x100 means skip, 0x200 means done */
+ static unsigned seq[] =
+ { 0xb0, 0x100, 0xe8, 0x100, 0x100, 0x30, 0xe4, 0x200 };
+ int ofs, i, c;
+ for (ofs = 0x60; ofs < 0x180; ofs++) { /* search range */
+ if (mbr[ofs] != seq[0]) /* search initial pattern */
+ continue;
+ for (i=0;; i++) {
+ if (seq[i] == 0x200) { /* found */
+ c = mbr[ofs+1];
+ if (!report)
+ mbr[ofs+1] = c = new_bell;
+ else
+ printf(" bell=%c (0x%x)",
+ (c >= ' ' && c < 0x7f) ? c : ' ', c);
+ return c;
+ }
+ if (seq[i] != 0x100 && seq[i] != mbr[ofs+i])
+ break;
+ }
+ }
+ warn("bell not found");
+ return -1;
+}
+/*
+ * Read in the MBR of the disk. If it is boot0, then use the version to
+ * read in all of it if necessary. Use pointers to return a malloc'd
+ * buffer containing the MBR and then return its size.
+ */
+static int
+read_mbr(const char *disk, u_int8_t **mbr, int check_version)
+{
+ u_int8_t buf[MBRSIZE];
+ int mbr_size, fd;
+ int ver;
+ ssize_t n;
+
+ if ((fd = open(disk, O_RDONLY)) == -1)
+ err(1, "open %s", disk);
+ if ((n = read(fd, buf, MBRSIZE)) == -1)
+ err(1, "read %s", disk);
+ if (n != MBRSIZE)
+ errx(1, "%s: short read", disk);
+ if (cv2(buf + OFF_MAGIC) != 0xaa55)
+ errx(1, "%s: bad magic", disk);
+
+ if (! (ver = boot0bs(buf))) {
+ if (check_version)
+ errx(1, "%s: unknown or incompatible boot code", disk);
+ } else if (boot0version(buf) == 0x101) {
+ mbr_size = 1024;
+ if ((*mbr = malloc(mbr_size)) == NULL)
+ errx(1, "%s: unable to allocate read buffer", disk);
+ if (lseek(fd, 0, SEEK_SET) == -1 ||
+ (n = read(fd, *mbr, mbr_size)) == -1)
+ err(1, "%s", disk);
+ if (n != mbr_size)
+ errx(1, "%s: short read", disk);
+ return (mbr_size);
+ }
+ *mbr = malloc(sizeof(buf));
+ memcpy(*mbr, buf, sizeof(buf));
+
+ return sizeof(buf);
+}
+
+/*
+ * Write out the mbr to the specified file.
+ */
+static void
+write_mbr(const char *fname, int flags, u_int8_t *mbr, int mbr_size)
+{
+ int fd;
+ ssize_t n;
+ const char *errmsg;
+ char *pname;
+ struct gctl_req *grq;
+
+ fd = open(fname, O_WRONLY | flags, 0666);
+ if (fd != -1) {
+ n = write(fd, mbr, mbr_size);
+ close(fd);
+ if (n != mbr_size)
+ errx(1, "%s: short write", fname);
+ return;
+ }
+
+ /*
+ * If we're called to write to a backup file, don't try to
+ * write through GEOM. It only generates additional errors.
+ */
+ if (flags != 0)
+ return;
+
+ /* Try open it read only. */
+ fd = open(fname, O_RDONLY);
+ if (fd == -1) {
+ warn("error opening %s", fname);
+ return;
+ }
+ pname = g_providername(fd);
+ if (pname == NULL) {
+ warn("error getting providername for %s", fname);
+ return;
+ }
+ grq = gctl_get_handle();
+ gctl_ro_param(grq, "class", -1, "PART");
+ gctl_ro_param(grq, "geom", -1, pname);
+ gctl_ro_param(grq, "verb", -1, "bootcode");
+ gctl_ro_param(grq, "bootcode", mbr_size, mbr);
+ gctl_ro_param(grq, "flags", -1, "C");
+ errmsg = gctl_issue(grq);
+ if (errmsg == NULL)
+ goto out;
+
+ grq = gctl_get_handle();
+ gctl_ro_param(grq, "verb", -1, "write MBR");
+ gctl_ro_param(grq, "class", -1, "MBR");
+ gctl_ro_param(grq, "geom", -1, pname);
+ gctl_ro_param(grq, "data", mbr_size, mbr);
+ errmsg = gctl_issue(grq);
+ if (errmsg != NULL)
+ err(1, "write_mbr: %s", fname);
+
+out:
+ free(pname);
+ gctl_free(grq);
+}
+
+/*
+ * Outputs an informative dump of the data in the MBR to stdout.
+ */
+static void
+display_mbr(u_int8_t *mbr)
+{
+ struct dos_partition *part;
+ int i, version;
+
+ part = (struct dos_partition *)(mbr + DOSPARTOFF);
+ printf(fmt0);
+ for (i = 0; i < NDOSPART; i++)
+ if (part[i].dp_typ)
+ printf(fmt1, 1 + i, part[i].dp_flag,
+ part[i].dp_scyl + ((part[i].dp_ssect & 0xc0) << 2),
+ part[i].dp_shd, part[i].dp_ssect & 0x3f, part[i].dp_typ,
+ part[i].dp_ecyl + ((part[i].dp_esect & 0xc0) << 2),
+ part[i].dp_ehd, part[i].dp_esect & 0x3f, part[i].dp_start,
+ part[i].dp_size);
+ printf("\n");
+ version = boot0version(mbr);
+ printf("version=%d.%d drive=0x%x mask=0x%x ticks=%u",
+ version >> 8, version & 0xff, mbr[OFF_DRIVE],
+ mbr[OFF_FLAGS] & 0xf, cv2(mbr + OFF_TICKS));
+ set_bell(mbr, 0, 1);
+ printf("\noptions=");
+ for (i = 0; i < nopt; i++) {
+ if (i)
+ printf(",");
+ if (!(mbr[OFF_FLAGS] & 1 << (7 - i)) ^ opttbl[i].def)
+ printf("no");
+ printf("%s", opttbl[i].tok);
+ }
+ printf("\n");
+ if (b0_ver == 2)
+ printf("volume serial ID %02x%02x-%02x%02x\n",
+ mbr[OFF_SERIAL], mbr[OFF_SERIAL+1],
+ mbr[OFF_SERIAL+2], mbr[OFF_SERIAL+3]);
+ printf("default_selection=F%d (", mbr[OFF_OPT] + 1);
+ if (mbr[OFF_OPT] < 4)
+ printf("Slice %d", mbr[OFF_OPT] + 1);
+ else
+ printf("Drive 1");
+ printf(")\n");
+}
+
+/*
+ * Return the boot0 version with the minor revision in the low byte, and
+ * the major revision in the next higher byte.
+ */
+static int
+boot0version(const u_int8_t *bs)
+{
+ /* Check for old version, and return 0x100 if found. */
+ int v = boot0bs(bs);
+ if (v != 0)
+ return v << 8;
+
+ /* We have a newer boot0, so extract the version number and return it. */
+ return *(const int *)(bs + OFF_VERSION) & 0xffff;
+}
+
+/* descriptor of a pattern to match.
+ * Start from the first entry trying to match the chunk of bytes,
+ * if you hit an entry with len=0 terminate the search and report
+ * off as the version. Otherwise skip to the next block after len=0
+ * An entry with len=0, off=0 is the end marker.
+ */
+struct byte_pattern {
+ unsigned off;
+ unsigned len;
+ u_int8_t *key;
+};
+
+/*
+ * Decide if we have valid boot0 boot code by looking for
+ * characteristic byte sequences at fixed offsets.
+ */
+static int
+boot0bs(const u_int8_t *bs)
+{
+ /* the initial code sequence */
+ static u_int8_t id0[] = {0xfc, 0x31, 0xc0, 0x8e, 0xc0, 0x8e, 0xd8,
+ 0x8e, 0xd0, 0xbc, 0x00, 0x7c };
+ /* the drive id */
+ static u_int8_t id1[] = {'D', 'r', 'i', 'v', 'e', ' '};
+ static struct byte_pattern patterns[] = {
+ {0x0, sizeof(id0), id0},
+ {0x1b2, sizeof(id1), id1},
+ {1, 0, NULL},
+ {0x0, sizeof(id0), id0}, /* version with NT support */
+ {0x1ae, sizeof(id1), id1},
+ {2, 0, NULL},
+ {0, 0, NULL},
+ };
+ struct byte_pattern *p = patterns;
+
+ for (; p->off || p->len; p++) {
+ if (p->len == 0)
+ break;
+ if (!memcmp(bs + p->off, p->key, p->len)) /* match */
+ continue;
+ while (p->len) /* skip to next block */
+ p++;
+ }
+ b0_ver = p->off; /* XXX ugly side effect */
+ return p->off;
+}
+
+/*
+ * Adjust "and" and "or" masks for a -o option argument.
+ */
+static void
+stropt(const char *arg, int *xa, int *xo)
+{
+ const char *q;
+ char *s, *s1;
+ int inv, i, x;
+
+ if (!(s = strdup(arg)))
+ err(1, NULL);
+ for (s1 = s; (q = strtok(s1, ",")); s1 = NULL) {
+ if ((inv = !strncmp(q, "no", 2)))
+ q += 2;
+ for (i = 0; i < nopt; i++)
+ if (!strcmp(q, opttbl[i].tok))
+ break;
+ if (i == nopt)
+ errx(1, "%s: Unknown -o option", q);
+ if (opttbl[i].def)
+ inv ^= 1;
+ x = 1 << (7 - i);
+ if (inv)
+ *xa &= ~x;
+ else
+ *xo |= x;
+ }
+ free(s);
+}
+
+/*
+ * Convert and check an option argument.
+ */
+static int
+argtoi(const char *arg, int lo, int hi, int opt)
+{
+ char *s;
+ long x;
+
+ errno = 0;
+ x = strtol(arg, &s, 0);
+ if (errno || !*arg || *s || x < lo || x > hi)
+ errx(1, "%s: Bad argument to -%c option", arg, opt);
+ return x;
+}
+
+/*
+ * Display usage information.
+ */
+static void
+usage(void)
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: boot0cfg [-Bv] [-b boot0] [-d drive] [-f file] [-m mask]",
+ " [-o options] [-s slice] [-t ticks] disk");
+ exit(1);
+}
diff --git a/usr.sbin/boot98cfg/Makefile b/usr.sbin/boot98cfg/Makefile
new file mode 100644
index 0000000..9b0e703
--- /dev/null
+++ b/usr.sbin/boot98cfg/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+PROG= boot98cfg
+MAN= boot98cfg.8
+
+WARNS?= 2
+
+DPADD= ${LIBGEOM}
+LDADD= -lgeom
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/boot98cfg/boot98cfg.8 b/usr.sbin/boot98cfg/boot98cfg.8
new file mode 100644
index 0000000..96ebbcd
--- /dev/null
+++ b/usr.sbin/boot98cfg/boot98cfg.8
@@ -0,0 +1,104 @@
+.\" Copyright (c) KATO Takenori, 2000.
+.\"
+.\" All rights reserved. Unpublished rights reserved under the copyright
+.\" laws of Japan.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer as
+.\" the first lines of this file unmodified.
+.\" 2. Redistributions in binary form must 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 July 10, 2000
+.Dt BOOT98CFG 8
+.Os
+.Sh NAME
+.Nm boot98cfg
+.Nd HDD boot manager installation utility
+.Sh SYNOPSIS
+.Nm
+.Op Fl B
+.Op Fl i Ar boot0
+.Op Fl m Ar boot0.5
+.Op Fl s Ar secsize
+.Op Fl v Ar version
+.Op Fl f Ar boot0.bak
+.Op Fl F Ar boot0.5.bak
+.Ar disk
+.Sh DESCRIPTION
+On NEC PC-98s,
+.Sq boot loader
+consists of the
+.Sq IPL
+and
+.Sq HDD boot menu .
+The IPL occupies sector 0 of a disk and is followed by the partition
+table.
+The IPL loads the HDD boot menu that starts from 0x400.
+.Pp
+The
+.Nm
+utility installs and makes backup copy of the IPL and the HDD boot menu; and
+allows changing the version number field in the sector 0.
+.Pp
+Note that the format command in NEC's OSs replaces the HDD boot menu
+with its own HDD boot menu when the version number field is smaller
+than that in the format command.
+.Pp
+The options are:
+.Bl -tag -width indent
+.It Fl B
+Install the IPL and HDD boot menu.
+This option causes the IPL and HDD
+boot menu code to be replaced.
+.It Fl i Ar boot0
+Specify which IPL image to use.
+The default is /boot/boot0.
+.It Fl m Ar boot0.5
+Specify which HDD boot menu image to use.
+The default is
+/boot/boot0.5.
+.It Fl f Ar boot0.bak
+Specify that a backup copy of the preexisting IPL should be written to
+.Ar boot0.bak .
+This file is created if it does not exist, and truncated if it does.
+.It Fl F Ar boot0.5.bak
+Specify that a backup copy of the preexisting HDD boot menu should be
+written to
+.Ar boot0.5.bak .
+This file is created if it does not exist, and truncated if it does.
+.It Fl v Ar version
+Specify the version number.
+.It Fl s Ar secsize
+Specify the sector size.
+The default sector size is 512
+(bytes/sector).
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr boot 8 ,
+.Xr fdisk 8
+.Sh AUTHORS
+.An NOKUBI Hirotaka ,
+.An KATO Takenori
diff --git a/usr.sbin/boot98cfg/boot98cfg.c b/usr.sbin/boot98cfg/boot98cfg.c
new file mode 100644
index 0000000..f1ae8329
--- /dev/null
+++ b/usr.sbin/boot98cfg/boot98cfg.c
@@ -0,0 +1,319 @@
+/*
+ * Copyright (c) KATO Takenori, 2000.
+ *
+ * All rights reserved. Unpublished rights reserved under the copyright
+ * laws of Japan.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer as
+ * the first lines of this file unmodified.
+ * 2. Redistributions in binary form must 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.
+ */
+
+/*
+ * Copyright (c) 1999 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 AND CONTRIBUTORS``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED 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/diskpc98.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgeom.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define BOOTSIZE 0x2000
+#define IPLSIZE 512 /* IPL size */
+#define BOOTMENUSIZE 7168 /* Max HDD boot menu size */
+#define BOOTMENUOFF 0x400
+
+u_char boot0buf[BOOTSIZE];
+u_char ipl[IPLSIZE];
+u_char menu[BOOTMENUSIZE];
+
+static int read_boot(const char *, u_char *);
+static int write_boot(const char *, u_char *);
+static char *mkrdev(const char *);
+static void usage(void);
+
+/*
+ * Boot manager installation/configuration utility.
+ */
+int
+main(int argc, char *argv[])
+{
+ char *endptr;
+ const char *iplpath = "/boot/boot0", *menupath = "/boot/boot0.5";
+ char *iplbakpath = NULL, *menubakpath = NULL;
+ char *disk;
+ int B_flag = 0;
+ int c;
+ int fd1;
+ int n;
+ int secsize = 512;
+ int v_flag = 0, version;
+
+ while ((c = getopt(argc, argv, "BF:f:i:m:s:v:")) != -1) {
+ switch (c) {
+ case 'B':
+ B_flag = 1;
+ break;
+ case 'F':
+ menubakpath = optarg;
+ break;
+ case 'f':
+ iplbakpath = optarg;
+ break;
+ case 'i':
+ iplpath = optarg;
+ break;
+ case 'm':
+ menupath = optarg;
+ break;
+ case 's':
+ secsize = strtol(optarg, &endptr, 0);
+ if (errno || *optarg == '\0' || *endptr)
+ errx(1, "%s: Bad argument to -s option",
+ optarg);
+ switch (secsize) {
+ case 256:
+ case 512:
+ case 1024:
+ case 2048:
+ break;
+ default:
+ errx(1, "%s: unsupported sector size", optarg);
+ break;
+ }
+ break;
+ case 'v':
+ v_flag = 1;
+ version = strtol(optarg, &endptr, 0);
+ if (errno || *optarg == '\0' || *endptr ||
+ version < 0 || version > 255)
+ errx(1, "%s: Bad argument to -v option",
+ optarg);
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 1)
+ usage();
+ disk = mkrdev(*argv);
+
+ read_boot(disk, boot0buf);
+
+ if (iplbakpath != NULL) {
+ fd1 = open(iplbakpath, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (fd1 < 0)
+ err(1, "%s", iplbakpath);
+ n = write(fd1, boot0buf, IPLSIZE);
+ if (n == -1)
+ err(1, "%s", iplbakpath);
+ if (n != IPLSIZE)
+ errx(1, "%s: short write", iplbakpath);
+ close(fd1);
+ }
+
+ if (menubakpath != NULL) {
+ fd1 = open(menubakpath, O_WRONLY | O_CREAT | O_TRUNC, 0666);
+ if (fd1 < 0)
+ err(1, "%s", menubakpath);
+ n = write(fd1, boot0buf + BOOTMENUOFF, BOOTMENUSIZE);
+ if (n == -1)
+ err(1, "%s", menubakpath);
+ if (n != BOOTMENUSIZE)
+ errx(1, "%s: short write", menubakpath);
+ close(fd1);
+ }
+
+ if (B_flag) {
+ /* Read IPL (boot0). */
+ fd1 = open(iplpath, O_RDONLY);
+ if (fd1 < 0)
+ err(1, "%s", disk);
+ n = read(fd1, ipl, IPLSIZE);
+ if (n < 0)
+ err(1, "%s", iplpath);
+ if (n != IPLSIZE)
+ errx(1, "%s: invalid file", iplpath);
+ close(fd1);
+
+ /* Read HDD boot menu (boot0.5). */
+ fd1 = open(menupath, O_RDONLY);
+ if (fd1 < 0)
+ err(1, "%s", disk);
+ n = read(fd1, menu, BOOTMENUSIZE);
+ if (n < 0)
+ err(1, "%s", menupath);
+ if (n != BOOTMENUSIZE)
+ errx(1, "%s: invalid file", menupath);
+ close(fd1);
+
+ memcpy(boot0buf, ipl, IPLSIZE);
+ memcpy(boot0buf + BOOTMENUOFF, menu, BOOTMENUSIZE);
+ }
+
+ /* Set version number field. */
+ if (v_flag)
+ *(boot0buf + secsize - 4) = (u_char)version;
+
+ if (B_flag || v_flag)
+ write_boot(disk, boot0buf);
+
+ return 0;
+}
+
+static int
+read_boot(const char *disk, u_char *boot)
+{
+ int fd, n;
+
+ /* Read IPL, partition table and HDD boot menu. */
+ fd = open(disk, O_RDONLY);
+ if (fd < 0)
+ err(1, "%s", disk);
+ n = read(fd, boot, BOOTSIZE);
+ if (n != BOOTSIZE)
+ errx(1, "%s: short read", disk);
+ close(fd);
+
+ return 0;
+}
+
+static int
+write_boot(const char *disk, u_char *boot)
+{
+ int fd, n, i;
+ char buf[MAXPATHLEN];
+ const char *q;
+ struct gctl_req *grq;
+
+ fd = open(disk, O_WRONLY, 0666);
+ if (fd != -1) {
+ if ((n = write(fd, boot, BOOTSIZE)) < 0)
+ err(1, "%s", disk);
+ if (n != BOOTSIZE)
+ errx(1, "%s: short write", disk);
+ close(fd);
+ return 0;
+ }
+
+ 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", BOOTSIZE, boot);
+ q = gctl_issue(grq);
+ if (q == NULL)
+ return 0;
+
+ warnx("%s: %s", disk, q);
+ gctl_free(grq);
+
+ for (i = 0; i < NDOSPART; i++) {
+ snprintf(buf, sizeof(buf), "%ss%d", disk, i + 1);
+ fd = open(buf, O_RDONLY);
+ if (fd < 0)
+ continue;
+ n = ioctl(fd, DIOCSPC98, boot);
+ if (n != 0)
+ err(1, "%s: ioctl DIOCSPC98", disk);
+ close(fd);
+ return 0;
+ }
+
+ err(1, "%s", disk);
+}
+
+/*
+ * Produce a device path for a "canonical" name, where appropriate.
+ */
+static char *
+mkrdev(const char *fname)
+{
+ char buf[MAXPATHLEN];
+ char *s;
+
+ if (!strchr(fname, '/')) {
+ snprintf(buf, sizeof(buf), "%s%s", _PATH_DEV, fname);
+ s = strdup(buf);
+ } else
+ s = strdup(fname);
+
+ if (s == NULL)
+ errx(1, "No more memory");
+ return s;
+}
+
+/*
+ * Display usage information.
+ */
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "boot98cfg [-B][-i boot0][-m boot0.5][-s secsize][-v version]\n"
+ " [-f ipl.bak][-F menu.bak] disk\n");
+ exit(1);
+}
diff --git a/usr.sbin/bootparamd/Makefile b/usr.sbin/bootparamd/Makefile
new file mode 100644
index 0000000..c4e3361
--- /dev/null
+++ b/usr.sbin/bootparamd/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+SUBDIR= bootparamd callbootd
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/bootparamd/Makefile.inc b/usr.sbin/bootparamd/Makefile.inc
new file mode 100644
index 0000000..1e4d46f
--- /dev/null
+++ b/usr.sbin/bootparamd/Makefile.inc
@@ -0,0 +1,4 @@
+# @(#)Makefile.inc 5.1 (Berkeley) 5/11/90
+# $FreeBSD$
+
+BINDIR?= /usr/sbin
diff --git a/usr.sbin/bootparamd/bootparamd/Makefile b/usr.sbin/bootparamd/bootparamd/Makefile
new file mode 100644
index 0000000..5d2047b
--- /dev/null
+++ b/usr.sbin/bootparamd/bootparamd/Makefile
@@ -0,0 +1,29 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+PROG= bootparamd
+MAN= bootparams.5 bootparamd.8
+SRCS= bootparamd.c main.c ${GENSRCS}
+GENSRCS=bootparam_prot.h bootparam_prot_svc.c bootparam_prot_xdr.c
+
+CFLAGS+= -DTFTP_DIR=\"/tftpboot\" -I.
+.if ${MK_NIS} != "no"
+CFLAGS+= -DYP
+.endif
+
+CLEANFILES= ${GENSRCS}
+
+RPCSRC= ${DESTDIR}/usr/include/rpcsvc/bootparam_prot.x
+
+bootparam_prot_svc.c: ${RPCSRC}
+ rpcgen -C -m -o ${.TARGET} ${RPCSRC}
+
+bootparam_prot_xdr.c: ${RPCSRC}
+ rpcgen -C -c -o ${.TARGET} ${RPCSRC}
+
+bootparam_prot.h: ${RPCSRC}
+ rpcgen -C -h -o ${.TARGET} ${RPCSRC}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bootparamd/bootparamd/README b/usr.sbin/bootparamd/bootparamd/README
new file mode 100644
index 0000000..508ee0f
--- /dev/null
+++ b/usr.sbin/bootparamd/bootparamd/README
@@ -0,0 +1,75 @@
+$FreeBSD$
+
+This directory contains a version of the rpc.bootparamd, which have been
+written using the Sun's RPC protocol for bootparamd. To use it you must
+have a copy of the bootparam_prot.x file which on Sun systems you find in
+
+ /usr/include/rpcsvc/bootparam_prot.x
+
+(( This file was retrieved from the Sun-RPC source package ))
+
+
+This code is not copyright, and is placed in the public domain. Feel free to
+use and modify. Please send modifications and/or suggestions + bug fixes to
+
+ Klas Heggemann <klas@nada.kth.se>
+
+
+RPC.BOOTPARAMD
+
+
+The rpc.bootparamd program does NOT use the yellow pages for the bootparams
+database. This data should recide in /etc/bootparams on the local host,
+or another file given when the server is started.
+
+The default router is set to the address of the machine running the server.
+This may not be a good thing to do, so it can be modified using the -r
+option when startning the daemon.
+
+This program was written with the need to keep short hostnames in the
+/etc/bootparams file and long (canonical) names in the hosts database.
+It probably also will work in conjunction with a nameserver, since matching
+is done by comparing the canonical name of the booting machine with the
+canonical name of the hosts found in the bootparams database.
+
+It is kept simple, e g there is no caching of data, but the bootparameter file
+is read at each request.
+
+
+CALLBOOTD
+
+The debugging tool callbootd is used to check the response you get
+to a specific (booting) request. It can be used as
+ callbootd server inet-address
+or
+ callbootd server hostname file
+where "server" is a machine running the rpc.bootparamd program, "inet-address"
+is the internet address of a booting machine, "hostname" is the name of a
+booting machine and "file" the requested file, typically "root", "swap" or
+"dump".
+
+You may also use "all" instead of a specific server, in which case a RPC
+broadcast is performed. The broadcast is performed 4 times and then the
+program times out, after printing all responses.
+
+
+TODO
+
+Cache the date, instead of rereading it.
+Maybe match by comparing the inet address instead. (But beware that caching
+will prevent the server from detecting that a machine has changed name
+or address.)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/usr.sbin/bootparamd/bootparamd/bootparamd.8 b/usr.sbin/bootparamd/bootparamd/bootparamd.8
new file mode 100644
index 0000000..6048a8f
--- /dev/null
+++ b/usr.sbin/bootparamd/bootparamd/bootparamd.8
@@ -0,0 +1,76 @@
+.\" @(#)bootparamd.8
+.\" $FreeBSD$
+.Dd December 14, 2000
+.Dt BOOTPARAMD 8
+.Os
+.Sh NAME
+.Nm bootparamd
+.Nd boot parameter server
+.Sh SYNOPSIS
+.Nm
+.Op Fl ds
+.Op Fl r Ar router
+.Op Fl f Ar file
+.Sh DESCRIPTION
+The
+.Nm
+utility is a server process that provides information to
+.Xr diskless 8
+clients necessary for booting.
+It consults the
+.Pa /etc/bootparams
+file.
+.Pp
+This version will allow the use of aliases on the hostname in the
+.Pa /etc/bootparams
+file.
+The returned hostname to the
+.Em whoami
+request done by the booting client
+will be the name that appears in
+.Pa /etc/bootparams
+and not the canonical name.
+In this way you can keep the answer short enough
+so that machines that cannot handle long hostnames will not fail during boot.
+.Sh OPTIONS
+.Bl -tag -width Fl
+.It Fl d
+Display the debugging information.
+.It Fl s
+Log the debugging information with
+.Xr syslog 3 .
+.It Fl r Ar router
+The default router (a machine or an IP-address).
+This defaults to the machine running the server.
+.It Fl f Ar file
+The file to use as boot parameter file instead of
+.Pa /etc/bootparams .
+.El
+.Sh FILES
+.Bl -tag -width /etc/bootparams -compact
+.It /etc/bootparams
+default boot parameter file
+.El
+.Sh EXAMPLES
+When netbooting diskless SunOS/Xkernel SPARCstations the booted SunOS kernel
+also broadcasts to the all-0 address.
+The SunOS kernel hangs until it receives a reply.
+To accommodate this behaviour add an alias address
+that responds to an all-0 broadcast.
+So, add something like
+.Ql "ifconfig xl0 192.168.200.254 netmask 255.255.255.255 broadcast 192.168.200.0 alias
+on the relevant network interface on your
+.Nm
+server.
+The alias address must of course be free for use.
+.Sh SEE ALSO
+.Xr syslog 3 ,
+.Xr bootparams 5 ,
+.Xr diskless 8
+.Sh AUTHORS
+Written by
+.An Klas Heggemann Aq klas@nada.kth.se .
+.Sh BUGS
+You may find the
+.Xr syslog 3
+loggings to be verbose.
diff --git a/usr.sbin/bootparamd/bootparamd/bootparamd.c b/usr.sbin/bootparamd/bootparamd/bootparamd.c
new file mode 100644
index 0000000..c638730
--- /dev/null
+++ b/usr.sbin/bootparamd/bootparamd/bootparamd.c
@@ -0,0 +1,358 @@
+/*
+
+This code is not copyright, and is placed in the public domain. Feel free to
+use and modify. Please send modifications and/or suggestions + bug fixes to
+
+ Klas Heggemann <klas@nada.kth.se>
+
+*/
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#ifdef YP
+#include <rpc/rpc.h>
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+#endif
+#include "bootparam_prot.h"
+#include <ctype.h>
+#include <err.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+extern int debug, dolog;
+extern in_addr_t route_addr;
+extern char *bootpfile;
+
+#define MAXLEN 800
+
+struct hostent *he;
+static char buffer[MAXLEN];
+static char hostname[MAX_MACHINE_NAME];
+static char askname[MAX_MACHINE_NAME];
+static char path[MAX_PATH_LEN];
+static char domain_name[MAX_MACHINE_NAME];
+
+int getthefile(char *, char *, char *, int);
+int checkhost(char *, char *, int);
+
+bp_whoami_res *
+bootparamproc_whoami_1_svc(whoami, req)
+bp_whoami_arg *whoami;
+struct svc_req *req;
+{
+ in_addr_t haddr;
+ static bp_whoami_res res;
+ if (debug)
+ fprintf(stderr,"whoami got question for %d.%d.%d.%d\n",
+ 255 & whoami->client_address.bp_address_u.ip_addr.net,
+ 255 & whoami->client_address.bp_address_u.ip_addr.host,
+ 255 & whoami->client_address.bp_address_u.ip_addr.lh,
+ 255 & whoami->client_address.bp_address_u.ip_addr.impno);
+ if (dolog)
+ syslog(LOG_NOTICE, "whoami got question for %d.%d.%d.%d\n",
+ 255 & whoami->client_address.bp_address_u.ip_addr.net,
+ 255 & whoami->client_address.bp_address_u.ip_addr.host,
+ 255 & whoami->client_address.bp_address_u.ip_addr.lh,
+ 255 & whoami->client_address.bp_address_u.ip_addr.impno);
+
+ bcopy((char *)&whoami->client_address.bp_address_u.ip_addr, (char *)&haddr,
+ sizeof(haddr));
+ he = gethostbyaddr((char *)&haddr,sizeof(haddr),AF_INET);
+ if ( ! he ) goto failed;
+
+ if (debug) warnx("this is host %s", he->h_name);
+ if (dolog) syslog(LOG_NOTICE,"This is host %s\n", he->h_name);
+
+ strncpy(askname, he->h_name, sizeof(askname));
+ askname[sizeof(askname)-1] = 0;
+
+ if (checkhost(askname, hostname, sizeof hostname) ) {
+ res.client_name = hostname;
+ getdomainname(domain_name, MAX_MACHINE_NAME);
+ res.domain_name = domain_name;
+
+ if ( res.router_address.address_type != IP_ADDR_TYPE ) {
+ res.router_address.address_type = IP_ADDR_TYPE;
+ bcopy( &route_addr, &res.router_address.bp_address_u.ip_addr, sizeof(in_addr_t));
+ }
+ if (debug) fprintf(stderr,
+ "Returning %s %s %d.%d.%d.%d\n",
+ res.client_name,
+ res.domain_name,
+ 255 & res.router_address.bp_address_u.ip_addr.net,
+ 255 & res.router_address.bp_address_u.ip_addr.host,
+ 255 & res.router_address.bp_address_u.ip_addr.lh,
+ 255 & res.router_address.bp_address_u.ip_addr.impno);
+ if (dolog) syslog(LOG_NOTICE,
+ "Returning %s %s %d.%d.%d.%d\n",
+ res.client_name,
+ res.domain_name,
+ 255 & res.router_address.bp_address_u.ip_addr.net,
+ 255 & res.router_address.bp_address_u.ip_addr.host,
+ 255 & res.router_address.bp_address_u.ip_addr.lh,
+ 255 & res.router_address.bp_address_u.ip_addr.impno);
+
+ return(&res);
+ }
+ failed:
+ if (debug) warnx("whoami failed");
+ if (dolog) syslog(LOG_NOTICE,"whoami failed\n");
+ return(NULL);
+}
+
+
+bp_getfile_res *
+ bootparamproc_getfile_1_svc(getfile, req)
+bp_getfile_arg *getfile;
+struct svc_req *req;
+{
+ char *where, *index();
+ static bp_getfile_res res;
+
+ if (debug)
+ warnx("getfile got question for \"%s\" and file \"%s\"",
+ getfile->client_name, getfile->file_id);
+
+ if (dolog)
+ syslog(LOG_NOTICE,"getfile got question for \"%s\" and file \"%s\"\n",
+ getfile->client_name, getfile->file_id);
+
+ he = NULL;
+ he = gethostbyname(getfile->client_name);
+ if (! he ) goto failed;
+
+ strncpy(askname, he->h_name, sizeof(askname));
+ askname[sizeof(askname)-1] = 0;
+
+ if (getthefile(askname, getfile->file_id,buffer,sizeof(buffer))) {
+ if ( (where = index(buffer,':')) ) {
+ /* buffer is re-written to contain the name of the info of file */
+ strncpy(hostname, buffer, where - buffer);
+ hostname[where - buffer] = '\0';
+ where++;
+ strcpy(path, where);
+ he = gethostbyname(hostname);
+ if ( !he ) goto failed;
+ bcopy( he->h_addr, &res.server_address.bp_address_u.ip_addr, 4);
+ res.server_name = hostname;
+ res.server_path = path;
+ res.server_address.address_type = IP_ADDR_TYPE;
+ }
+ else { /* special for dump, answer with null strings */
+ if (!strcmp(getfile->file_id, "dump")) {
+ res.server_name = "";
+ res.server_path = "";
+ res.server_address.address_type = IP_ADDR_TYPE;
+ bzero(&res.server_address.bp_address_u.ip_addr,4);
+ } else goto failed;
+ }
+ if (debug)
+ fprintf(stderr, "returning server:%s path:%s address: %d.%d.%d.%d\n",
+ res.server_name, res.server_path,
+ 255 & res.server_address.bp_address_u.ip_addr.net,
+ 255 & res.server_address.bp_address_u.ip_addr.host,
+ 255 & res.server_address.bp_address_u.ip_addr.lh,
+ 255 & res.server_address.bp_address_u.ip_addr.impno);
+ if (dolog)
+ syslog(LOG_NOTICE, "returning server:%s path:%s address: %d.%d.%d.%d\n",
+ res.server_name, res.server_path,
+ 255 & res.server_address.bp_address_u.ip_addr.net,
+ 255 & res.server_address.bp_address_u.ip_addr.host,
+ 255 & res.server_address.bp_address_u.ip_addr.lh,
+ 255 & res.server_address.bp_address_u.ip_addr.impno);
+ return(&res);
+ }
+ failed:
+ if (debug) warnx("getfile failed for %s", getfile->client_name);
+ if (dolog) syslog(LOG_NOTICE,
+ "getfile failed for %s\n", getfile->client_name);
+ return(NULL);
+}
+
+/* getthefile return 1 and fills the buffer with the information
+ of the file, e g "host:/export/root/client" if it can be found.
+ If the host is in the database, but the file is not, the buffer
+ will be empty. (This makes it possible to give the special
+ empty answer for the file "dump") */
+
+int
+getthefile(askname,fileid,buffer,blen)
+char *askname;
+char *fileid, *buffer;
+int blen;
+{
+ FILE *bpf;
+ char *where;
+ static char *result;
+ int resultlen;
+#ifdef YP
+ static char *yp_domain;
+#endif
+
+ int ch, pch, fid_len, res = 0;
+ int match = 0;
+ char info[MAX_FILEID + MAX_PATH_LEN+MAX_MACHINE_NAME + 3];
+
+ bpf = fopen(bootpfile, "r");
+ if ( ! bpf )
+ errx(1, "no %s", bootpfile);
+
+ /* XXX see comment below */
+ while ( fscanf(bpf, "%255s", hostname) > 0 && !match ) {
+ if ( *hostname != '#' ) { /* comment */
+ if ( ! strcmp(hostname, askname) ) {
+ match = 1;
+ } else {
+ he = gethostbyname(hostname);
+ if (he && !strcmp(he->h_name, askname)) match = 1;
+ }
+ }
+ if (*hostname == '+' ) { /* NIS */
+#ifdef YP
+ if (yp_get_default_domain(&yp_domain)) {
+ if (debug) warn("NIS");
+ return(0);
+ }
+ if (yp_match(yp_domain, "bootparams", askname, strlen(askname),
+ &result, &resultlen))
+ return (0);
+ if (strstr(result, fileid) == NULL) {
+ buffer[0] = '\0';
+ } else {
+ snprintf(buffer, blen,
+ "%s",strchr(strstr(result,fileid), '=') + 1);
+ if (strchr(buffer, ' ') != NULL)
+ *(char *)(strchr(buffer, ' ')) = '\0';
+ }
+ if (fclose(bpf))
+ warnx("could not close %s", bootpfile);
+ return(1);
+#else
+ return(0); /* ENOTSUP */
+#endif
+ }
+ /* skip to next entry */
+ if ( match ) break;
+ pch = ch = getc(bpf);
+ while ( ! ( ch == '\n' && pch != '\\') && ch != EOF) {
+ pch = ch; ch = getc(bpf);
+ }
+ }
+
+ /* if match is true we read the rest of the line to get the
+ info of the file */
+
+ if (match) {
+ fid_len = strlen(fileid);
+ while ( ! res && (fscanf(bpf,"%s", info)) > 0) { /* read a string */
+ ch = getc(bpf); /* and a character */
+ if ( *info != '#' ) { /* Comment ? */
+ if (! strncmp(info, fileid, fid_len) && *(info + fid_len) == '=') {
+ where = info + fid_len + 1;
+ if ( isprint( *where )) {
+ strcpy(buffer, where); /* found file */
+ res = 1; break;
+ }
+ } else {
+ while (isspace(ch) && ch != '\n') ch = getc(bpf);
+ /* read to end of line */
+ if ( ch == '\n' ) { /* didn't find it */
+ res = -1; break; /* but host is there */
+ }
+ if ( ch == '\\' ) { /* more info */
+ ch = getc(bpf); /* maybe on next line */
+ if (ch == '\n') continue; /* read it in next loop */
+ ungetc(ch, bpf); ungetc('\\',bpf); /* push the character(s) back */
+ } else ungetc(ch, bpf); /* but who know what a `\` is */
+ } /* needed for. */
+ } else break; /* a commented rest-of-line */
+ }
+ }
+ if (fclose(bpf)) { warnx("could not close %s", bootpfile); }
+ if ( res == -1) buffer[0] = '\0'; /* host found, file not */
+ return(match);
+}
+
+/* checkhost puts the hostname found in the database file in
+ the hostname-variable and returns 1, if askname is a valid
+ name for a host in the database */
+
+int
+checkhost(askname, hostname, len)
+char *askname;
+char *hostname;
+int len;
+{
+ int ch, pch;
+ FILE *bpf;
+ int res = 0;
+ static char *result;
+ int resultlen;
+#ifdef YP
+ static char *yp_domain;
+#endif
+
+/* struct hostent *cmp_he;*/
+
+ bpf = fopen(bootpfile, "r");
+ if ( ! bpf )
+ errx(1, "no %s", bootpfile);
+
+ /* XXX there is no way in ISO C to specify the maximal length for a
+ conversion in a variable way */
+ while ( fscanf(bpf, "%254s", hostname) > 0 ) {
+ if ( *hostname != '#' ) { /* comment */
+ if ( ! strcmp(hostname, askname) ) {
+ /* return true for match of hostname */
+ res = 1;
+ break;
+ } else {
+ /* check the alias list */
+ he = NULL;
+ he = gethostbyname(hostname);
+ if (he && !strcmp(askname, he->h_name)) {
+ res = 1;
+ break;
+ }
+ }
+ }
+ if (*hostname == '+' ) { /* NIS */
+#ifdef YP
+ if (yp_get_default_domain(&yp_domain)) {
+ if (debug) warn("NIS");
+ return(0);
+ }
+ if (!yp_match(yp_domain, "bootparams", askname, strlen(askname),
+ &result, &resultlen)) {
+ /* return true for match of hostname */
+ he = NULL;
+ he = gethostbyname(askname);
+ if (he && !strcmp(askname, he->h_name)) {
+ res = 1;
+ snprintf(hostname, len, "%s", he->h_name);
+ }
+ }
+ if (fclose(bpf))
+ warnx("could not close %s", bootpfile);
+ return(res);
+#else
+ return(0); /* ENOTSUP */
+#endif
+ }
+ /* skip to next entry */
+ pch = ch = getc(bpf);
+ while ( ! ( ch == '\n' && pch != '\\') && ch != EOF) {
+ pch = ch; ch = getc(bpf);
+ }
+ }
+ if (fclose(bpf)) { warnx("could not close %s", bootpfile); }
+ return(res);
+}
diff --git a/usr.sbin/bootparamd/bootparamd/bootparams.5 b/usr.sbin/bootparamd/bootparamd/bootparams.5
new file mode 100644
index 0000000..c7c6785
--- /dev/null
+++ b/usr.sbin/bootparamd/bootparamd/bootparams.5
@@ -0,0 +1,86 @@
+.\"
+.\" Copyright (c) 1994 Gordon W. Ross
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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.
+.\"
+.\" from: Id: bootparams.5,v 1.2 1994/10/03 19:26:13 gwr Exp
+.\" $FreeBSD$
+.\"
+.Dd October 2, 1994
+.Dt BOOTPARAMS 5
+.Os
+.Sh NAME
+.Nm bootparams
+.Nd boot parameter database
+.Sh SYNOPSIS
+.Nm /etc/bootparams
+.Sh DESCRIPTION
+The
+.Nm
+file specifies the boot parameters that
+.Xr diskless 8
+clients may request when booting over the network.
+Each client supported by this server must have an entry in the
+.Nm
+file containing the pathnames for its
+.Dq root
+and (optionally)
+.Dq swap
+areas.
+.Pp
+Each line in the file
+(other than comment lines that begin with a
+.Ql # )
+specifies the client name followed by the pathnames that
+the client may request by their logical names.
+The components of the line are delimited with blank or tab,
+and may be continued onto multiple lines with a backslash.
+.Pp
+For example:
+.Bd -literal -offset indent
+dummy root=host:/export/dummy/root \\
+ swap=host:/export/dummy/swap \\
+ dump=host:/export/dummy/swap
+.Ed
+.Pp
+When the client named
+.Dq dummy
+requests the pathname for its logical
+.Dq root
+it will be given the pathname
+.Dq Pa host:/export/dummy/root
+as the response to its
+.Tn RPC
+request.
+The
+.Dq host:
+component must be supplied.
+.Sh FILES
+.Bl -tag -width /etc/bootparams -compact
+.It Pa /etc/bootparams
+default configuration file
+.El
+.Sh SEE ALSO
+.Xr bootparamd 8 ,
+.Xr diskless 8
diff --git a/usr.sbin/bootparamd/bootparamd/main.c b/usr.sbin/bootparamd/bootparamd/main.c
new file mode 100644
index 0000000..8c93c1f
--- /dev/null
+++ b/usr.sbin/bootparamd/bootparamd/main.c
@@ -0,0 +1,118 @@
+/*
+
+This code is not copyright, and is placed in the public domain. Feel free to
+use and modify. Please send modifications and/or suggestions + bug fixes to
+
+ Klas Heggemann <klas@nada.kth.se>
+
+*/
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include "bootparam_prot.h"
+
+int debug = 0;
+int dolog = 0;
+in_addr_t route_addr = -1;
+struct sockaddr_in my_addr;
+char *bootpfile = "/etc/bootparams";
+
+extern void bootparamprog_1();
+static void usage(void);
+
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+ SVCXPRT *transp;
+ struct hostent *he;
+ struct stat buf;
+ int c;
+
+ while ((c = getopt(argc, argv,"dsr:f:")) != -1)
+ switch (c) {
+ case 'd':
+ debug = 1;
+ break;
+ case 'r':
+ if (isdigit((unsigned char)*optarg)) {
+ route_addr = inet_addr(optarg);
+ break;
+ } else {
+ he = gethostbyname(optarg);
+ if (he) {
+ bcopy(he->h_addr, (char *)&route_addr, sizeof(route_addr));
+ break;
+ } else {
+ errx(1, "no such host %s", optarg);
+ }
+ }
+ case 'f':
+ bootpfile = optarg;
+ break;
+ case 's':
+ dolog = 1;
+#ifndef LOG_DAEMON
+ openlog("bootparamd", 0 , 0);
+#else
+ openlog("bootparamd", 0 , LOG_DAEMON);
+ setlogmask(LOG_UPTO(LOG_NOTICE));
+#endif
+ break;
+ default:
+ usage();
+ }
+
+ if ( stat(bootpfile, &buf ) )
+ err(1, "%s", bootpfile);
+
+ if (route_addr == -1) {
+ get_myaddress(&my_addr);
+ bcopy(&my_addr.sin_addr.s_addr, &route_addr, sizeof (route_addr));
+ }
+
+ if (!debug) {
+ if (daemon(0,0))
+ err(1, "fork");
+ }
+
+
+ (void)pmap_unset(BOOTPARAMPROG, BOOTPARAMVERS);
+
+ transp = svcudp_create(RPC_ANYSOCK);
+ if (transp == NULL)
+ errx(1, "cannot create udp service");
+ if (!svc_register(transp, BOOTPARAMPROG, BOOTPARAMVERS, bootparamprog_1, IPPROTO_UDP))
+ errx(1, "unable to register (BOOTPARAMPROG, BOOTPARAMVERS, udp)");
+
+ svc_run();
+ errx(1, "svc_run returned");
+}
+
+static void
+usage()
+{
+ fprintf(stderr,
+ "usage: bootparamd [-d] [-s] [-r router] [-f bootparmsfile]\n");
+ exit(1);
+}
diff --git a/usr.sbin/bootparamd/callbootd/Makefile b/usr.sbin/bootparamd/callbootd/Makefile
new file mode 100644
index 0000000..5a08d60
--- /dev/null
+++ b/usr.sbin/bootparamd/callbootd/Makefile
@@ -0,0 +1,24 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $FreeBSD$
+
+PROG= callbootd
+NO_MAN=
+SRCS= callbootd.c ${GENSRCS}
+GENSRCS=bootparam_prot.h bootparam_prot_clnt.c bootparam_prot_xdr.c
+
+CFLAGS+= -I.
+
+CLEANFILES= ${GENSRCS}
+
+RPCSRC= ${DESTDIR}/usr/include/rpcsvc/bootparam_prot.x
+
+bootparam_prot_clnt.c: ${RPCSRC}
+ rpcgen -C -l -o ${.TARGET} ${RPCSRC}
+
+bootparam_prot_xdr.c: ${RPCSRC}
+ rpcgen -C -c -o ${.TARGET} ${RPCSRC}
+
+bootparam_prot.h: ${RPCSRC}
+ rpcgen -C -h -o ${.TARGET} ${RPCSRC}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bootparamd/callbootd/callbootd.c b/usr.sbin/bootparamd/callbootd/callbootd.c
new file mode 100644
index 0000000..4ec16df
--- /dev/null
+++ b/usr.sbin/bootparamd/callbootd/callbootd.c
@@ -0,0 +1,205 @@
+/*
+
+This code is not copyright, and is placed in the public domain. Feel free to
+use and modify. Please send modifications and/or suggestions + bug fixes to
+
+ Klas Heggemann <klas@nada.kth.se>
+
+*/
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "bootparam_prot.h"
+#include <rpc/rpc.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <err.h>
+#include <netdb.h>
+#include <stdlib.h>
+
+
+/* #define bp_address_u bp_address */
+#include <stdio.h>
+#include <string.h>
+
+int broadcast;
+
+char cln[MAX_MACHINE_NAME+1];
+char dmn[MAX_MACHINE_NAME+1];
+char path[MAX_PATH_LEN+1];
+extern char *inet_ntoa();
+static void usage(void);
+int printgetfile(bp_getfile_res *);
+int printwhoami(bp_whoami_res *);
+
+bool_t
+eachres_whoami(resultp, raddr)
+bp_whoami_res *resultp;
+struct sockaddr_in *raddr;
+{
+ struct hostent *he;
+
+ he = gethostbyaddr((char *)&raddr->sin_addr.s_addr,4,AF_INET);
+ printf("%s answered:\n", he ? he->h_name : inet_ntoa(raddr->sin_addr));
+ printwhoami(resultp);
+ printf("\n");
+ return(0);
+}
+
+bool_t
+eachres_getfile(resultp, raddr)
+bp_getfile_res *resultp;
+struct sockaddr_in *raddr;
+{
+ struct hostent *he;
+
+ he = gethostbyaddr((char *)&raddr->sin_addr.s_addr,4,AF_INET);
+ printf("%s answered:\n", he ? he->h_name : inet_ntoa(raddr->sin_addr));
+ printgetfile(resultp);
+ printf("\n");
+ return(0);
+}
+
+
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+ char *server;
+
+ bp_whoami_arg whoami_arg;
+ bp_whoami_res *whoami_res, stat_whoami_res;
+ bp_getfile_arg getfile_arg;
+ bp_getfile_res *getfile_res, stat_getfile_res;
+
+
+ long the_inet_addr;
+ CLIENT *clnt;
+ enum clnt_stat clnt_stat;
+
+ stat_whoami_res.client_name = cln;
+ stat_whoami_res.domain_name = dmn;
+
+ stat_getfile_res.server_name = cln;
+ stat_getfile_res.server_path = path;
+
+ if (argc < 3)
+ usage();
+
+ server = argv[1];
+ if ( ! strcmp(server , "all") ) broadcast = 1;
+
+ if ( ! broadcast ) {
+ clnt = clnt_create(server,BOOTPARAMPROG, BOOTPARAMVERS, "udp");
+ if ( clnt == NULL )
+ errx(1, "could not contact bootparam server on host %s", server);
+ }
+
+ switch (argc) {
+ case 3:
+ whoami_arg.client_address.address_type = IP_ADDR_TYPE;
+ the_inet_addr = inet_addr(argv[2]);
+ if ( the_inet_addr == -1)
+ errx(2, "bogus addr %s", argv[2]);
+ bcopy(&the_inet_addr,&whoami_arg.client_address.bp_address_u.ip_addr,4);
+
+ if (! broadcast ) {
+ whoami_res = bootparamproc_whoami_1(&whoami_arg, clnt);
+ printf("Whoami returning:\n");
+ if (printwhoami(whoami_res)) {
+ errx(1, "bad answer returned from server %s", server);
+ } else
+ exit(0);
+ } else {
+ clnt_stat=clnt_broadcast(BOOTPARAMPROG, BOOTPARAMVERS,
+ BOOTPARAMPROC_WHOAMI,
+ (xdrproc_t)xdr_bp_whoami_arg,
+ (char *)&whoami_arg,
+ (xdrproc_t)xdr_bp_whoami_res,
+ (char *)&stat_whoami_res,
+ (resultproc_t)eachres_whoami);
+ exit(0);
+ }
+
+ case 4:
+
+ getfile_arg.client_name = argv[2];
+ getfile_arg.file_id = argv[3];
+
+ if (! broadcast ) {
+ getfile_res = bootparamproc_getfile_1(&getfile_arg,clnt);
+ printf("getfile returning:\n");
+ if (printgetfile(getfile_res)) {
+ errx(1, "bad answer returned from server %s", server);
+ } else
+ exit(0);
+ } else {
+ clnt_stat=clnt_broadcast(BOOTPARAMPROG, BOOTPARAMVERS,
+ BOOTPARAMPROC_GETFILE,
+ (xdrproc_t)xdr_bp_getfile_arg,
+ (char *)&getfile_arg,
+ (xdrproc_t)xdr_bp_getfile_res,
+ (char *)&stat_getfile_res,
+ (resultproc_t)eachres_getfile);
+ exit(0);
+ }
+
+ default:
+
+ usage();
+ }
+
+}
+
+
+static void
+usage()
+{
+ fprintf(stderr,
+ "usage: callbootd server procnum (IP-addr | host fileid)\n");
+ exit(1);
+}
+
+int
+printwhoami(res)
+bp_whoami_res *res;
+{
+ if ( res) {
+ printf("client_name:\t%s\ndomain_name:\t%s\n",
+ res->client_name, res->domain_name);
+ printf("router:\t%d.%d.%d.%d\n",
+ 255 & res->router_address.bp_address_u.ip_addr.net,
+ 255 & res->router_address.bp_address_u.ip_addr.host,
+ 255 & res->router_address.bp_address_u.ip_addr.lh,
+ 255 & res->router_address.bp_address_u.ip_addr.impno);
+ return(0);
+ } else {
+ warnx("null answer!!!");
+ return(1);
+ }
+ }
+
+
+
+
+int
+printgetfile(res)
+bp_getfile_res *res;
+{
+ if (res) {
+ printf("server_name:\t%s\nserver_address:\t%s\npath:\t%s\n",
+ res->server_name,
+ inet_ntoa(*(struct in_addr *)&res->server_address.bp_address_u.ip_addr),
+ res->server_path);
+ return(0);
+ } else {
+ warnx("null answer!!!");
+ return(1);
+ }
+ }
diff --git a/usr.sbin/bsnmpd/Makefile b/usr.sbin/bsnmpd/Makefile
new file mode 100644
index 0000000..c948108
--- /dev/null
+++ b/usr.sbin/bsnmpd/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+SUBDIR= gensnmptree \
+ bsnmpd \
+ modules
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/bsnmpd/Makefile.inc b/usr.sbin/bsnmpd/Makefile.inc
new file mode 100644
index 0000000..265f86d
--- /dev/null
+++ b/usr.sbin/bsnmpd/Makefile.inc
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+.include "../Makefile.inc"
diff --git a/usr.sbin/bsnmpd/bsnmpd/Makefile b/usr.sbin/bsnmpd/bsnmpd/Makefile
new file mode 100644
index 0000000..9fbf366
--- /dev/null
+++ b/usr.sbin/bsnmpd/bsnmpd/Makefile
@@ -0,0 +1,46 @@
+# $FreeBSD$
+#
+# Author: Harti Brandt <harti@freebsd.org>
+
+CONTRIB=${.CURDIR}/../../../contrib/bsnmp
+.PATH: ${CONTRIB}/snmpd
+
+PROG= bsnmpd
+SRCS= main.c action.c config.c export.c trap.c trans_udp.c trans_lsock.c
+SRCS+= oid.h tree.c tree.h
+XSYM= snmpMIB begemotSnmpdModuleTable begemotSnmpd begemotTrapSinkTable \
+ sysUpTime snmpTrapOID coldStart authenticationFailure \
+ begemotSnmpdTransUdp begemotSnmpdTransLsock begemotSnmpdLocalPortTable \
+ freeBSDVersion
+CLEANFILES= oid.h tree.c tree.h
+MAN= bsnmpd.1 snmpmod.3
+WARNS?= 6
+NO_WERROR=
+
+FILESGROUPS= BMIBS DEFS
+
+BMIBS= FOKUS-MIB.txt BEGEMOT-MIB.txt BEGEMOT-SNMPD.txt
+BMIBSDIR= ${SHAREDIR}/snmp/mibs
+DEFS= tree.def
+DEFSDIR= ${SHAREDIR}/snmp/defs
+
+CFLAGS+= -I${CONTRIB}/lib -I${CONTRIB}/snmpd -I. -DUSE_LIBBEGEMOT
+CFLAGS+= -DUSE_TCPWRAPPERS -DQUADFMT='"llu"' -DQUADXFMT='"llx"'
+CFLAGS+= -DHAVE_STDINT_H -DHAVE_INTTYPES_H -DHAVE_ERR_H -DHAVE_STRLCPY
+DPADD= ${LIBBEGEMOT} ${LIBBSNMP} ${LIBWRAP}
+LDADD= -lbegemot -lbsnmp -lwrap
+
+LDFLAGS= -export-dynamic
+
+oid.h: tree.def Makefile
+ gensnmptree -e ${XSYM} < ${.ALLSRC:M*.def} > ${.TARGET}
+
+.ORDER: tree.c tree.h
+tree.c tree.h: tree.def
+ gensnmptree -l < ${.ALLSRC}
+
+MANFILTER= sed -e 's%@MODPATH@%${LIBDIR}/%g' \
+ -e 's%@DEFPATH@%${DEFSDIR}/%g' \
+ -e 's%@MIBSPATH@%${BMIBSDIR}/%g'
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bsnmpd/gensnmptree/Makefile b/usr.sbin/bsnmpd/gensnmptree/Makefile
new file mode 100644
index 0000000..11c6a3b
--- /dev/null
+++ b/usr.sbin/bsnmpd/gensnmptree/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+#
+# Author: Harti Brandt <harti@freebsd.org>
+
+CONTRIB=${.CURDIR}/../../../contrib/bsnmp
+.PATH: ${CONTRIB}/gensnmptree
+
+PROG= gensnmptree
+CFLAGS+= -I${CONTRIB}/lib
+CFLAGS+= -DQUADFMT='"llu"' -DQUADXFMT='"llx"' -DHAVE_STDINT_H
+CFLAGS+= -DHAVE_INTTYPES_H
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bsnmpd/modules/Makefile b/usr.sbin/bsnmpd/modules/Makefile
new file mode 100644
index 0000000..0ef4b7a
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/Makefile
@@ -0,0 +1,24 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+.PATH: ${.CURDIR}/../../../contrib/bsnmp/snmpd
+
+.if ${MK_ATM} != "no"
+_snmp_atm= snmp_atm
+.endif
+
+SUBDIR= ${_snmp_atm} \
+ snmp_bridge \
+ snmp_hostres \
+ snmp_mibII \
+ snmp_pf
+
+.if ${MK_NETGRAPH_SUPPORT} != "no"
+SUBDIR+=snmp_netgraph
+.endif
+
+INCS= snmpmod.h
+INCSDIR= ${INCLUDEDIR}/bsnmp
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bsnmpd/modules/Makefile.inc b/usr.sbin/bsnmpd/modules/Makefile.inc
new file mode 100644
index 0000000..ecd2538
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/Makefile.inc
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+SHLIB_MAJOR= 5
+WARNS?= 6
+
+MANFILTER= sed -e 's%@MODPATH@%${LIBDIR}/%g' \
+ -e 's%@DEFPATH@%${DEFSDIR}/%g' \
+ -e 's%@MIBSPATH@%${BMIBSDIR}/%g'
diff --git a/usr.sbin/bsnmpd/modules/snmp_atm/BEGEMOT-ATM-FREEBSD-MIB.txt b/usr.sbin/bsnmpd/modules/snmp_atm/BEGEMOT-ATM-FREEBSD-MIB.txt
new file mode 100644
index 0000000..83c4e5c
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_atm/BEGEMOT-ATM-FREEBSD-MIB.txt
@@ -0,0 +1,99 @@
+--
+-- 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 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$
+--
+-- Private Begemot MIB for ATM interfaces on FreeBSD
+--
+BEGEMOT-ATM-FREEBSD-MIB DEFINITIONS ::= BEGIN
+
+IMPORTS
+ MODULE-IDENTITY, OBJECT-TYPE
+ FROM SNMPv2-SMI
+ NgNodeIdOrZero
+ FROM BEGEMOT-NETGRAPH-MIB
+ begemotAtmSysGroup, begemotAtmIfEntry
+ FROM BEGEMOT-ATM-MIB;
+
+begemotAtmFreeBSDGroup MODULE-IDENTITY
+ LAST-UPDATED "200408060000Z"
+ ORGANIZATION "German Aerospace Centre"
+ CONTACT-INFO
+ " Hartmut Brandt
+
+ Postal: German Aerospace Centre (DLR)
+ Institute of Communications and Navigation
+ 82234 Wessling
+ Germany
+
+ Fax: +49 8153 28 2844
+
+ E-mail: harti@freebsd.org"
+ DESCRIPTION
+ "The FreeBSD specific Begemot MIB for ATM interfaces."
+
+ ::= { begemotAtmSysGroup 1 }
+
+-- Netgraph
+begemotAtmNgGroup OBJECT IDENTIFIER ::= { begemotAtmFreeBSDGroup 1 }
+
+--
+-- Interfaces table
+--
+begemotAtmNgIfTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF BegemotAtmNgIfEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This table contains an entry for each hardware ATM
+ interface. The table is indexed by the interface index."
+ ::= { begemotAtmNgGroup 1 }
+
+begemotAtmNgIfEntry OBJECT-TYPE
+ SYNTAX BegemotAtmNgIfEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "This is a table entry describing one ATM hardware interface."
+ AUGMENTS { begemotAtmIfEntry }
+ ::= { begemotAtmNgIfTable 1 }
+
+BegemotAtmNgIfEntry ::= SEQUENCE {
+ begemotAtmNgIfNodeId NgNodeIdOrZero
+}
+
+begemotAtmNgIfNodeId OBJECT-TYPE
+ SYNTAX NgNodeIdOrZero
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The netgraph node id of the interface. If there is no
+ node corresponding to the interface, this is 0."
+ ::= { begemotAtmNgIfEntry 1 }
+
+END
diff --git a/usr.sbin/bsnmpd/modules/snmp_atm/Makefile b/usr.sbin/bsnmpd/modules/snmp_atm/Makefile
new file mode 100644
index 0000000..4bba7cb
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_atm/Makefile
@@ -0,0 +1,21 @@
+# $FreeBSD$
+#
+# Author: Harti Brandt <harti@freebsd.org>
+
+CONTRIB= ${.CURDIR}/../../../../contrib/ngatm
+.PATH: ${CONTRIB}/snmp_atm
+
+MOD= atm
+SRCS= snmp_atm.c atm_sys.c
+XSYM= begemotAtm
+MAN= snmp_atm.3
+
+BMIBS= BEGEMOT-ATM.txt BEGEMOT-ATM-FREEBSD-MIB.txt
+DEFS= ${MOD}_tree.def atm_freebsd.def
+INCS= snmp_${MOD}.h
+
+EXTRAMIBDEFS= atm_freebsd.def
+
+CFLAGS+= -I${CONTRIB}/snmp_atm
+
+.include <bsd.snmpmod.mk>
diff --git a/usr.sbin/bsnmpd/modules/snmp_atm/atm_freebsd.def b/usr.sbin/bsnmpd/modules/snmp_atm/atm_freebsd.def
new file mode 100644
index 0000000..6cc61d0
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_atm/atm_freebsd.def
@@ -0,0 +1,56 @@
+#
+# 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 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$
+#
+# SNMP module for ATM hardware interfaces.
+#
+# $Begemot: libunimsg/snmp_atm/atm_tree.def,v 1.2 2004/08/05 07:14:22 brandt Exp $
+#
+(1 internet
+ (4 private
+ (1 enterprises
+ (12325 fokus
+ (1 begemot
+ (101 begemotAtm
+ (1 begemotAtmObjects
+ (4 begemotAtmSysGroup
+ (1 begemotAtmFreeBSDGroup
+ (1 begemotAtmNgGroup
+ (1 begemotAtmNgIfTable
+ (1 begemotAtmNgIfEntry : INTEGER op_atmif_ng
+ (1 begemotAtmIfNodeId UNSIGNED32 GET)
+ ))
+ )
+ )
+ )
+ )
+ ))
+ )
+ )
+))
diff --git a/usr.sbin/bsnmpd/modules/snmp_atm/atm_sys.c b/usr.sbin/bsnmpd/modules/snmp_atm/atm_sys.c
new file mode 100644
index 0000000..525e805
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_atm/atm_sys.c
@@ -0,0 +1,301 @@
+/*
+ * 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 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$
+ *
+ * SNMP module for ATM hardware interfaces - FreeBSD/Ng specific part.
+ */
+
+#include "atm.h"
+#include "atm_tree.h"
+#include "atm_oid.h"
+
+#include <stdlib.h>
+#include <syslog.h>
+#include <string.h>
+
+#include <net/if_atm.h>
+
+#include <bsnmp/snmp_netgraph.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/atm/ng_atm.h>
+
+static const struct hwinfo {
+ const char *device;
+ const char *vendor;
+} hwinfo[] = {
+ ATM_DEVICE_NAMES
+};
+
+struct atmif_sys {
+ ng_ID_t atm_node;
+ void *regc; /* cookie registration */
+};
+
+/*
+ * Find the interface for a given node
+ */
+struct atmif *
+atm_node2if(u_int node)
+{
+ struct atmif_priv *aif;
+
+ if (node != 0)
+ TAILQ_FOREACH(aif, &atmif_list, link)
+ if (aif->sys->atm_node == node)
+ return (&aif->pub);
+ return (NULL);
+}
+
+u_int
+atm_if2node(struct atmif *pub)
+{
+ struct atmif_priv *aif = (struct atmif_priv *)pub;
+
+ return (aif->sys->atm_node);
+}
+
+/*
+ * Destroy system dependend stuff.
+ */
+void
+atmif_sys_destroy(struct atmif_priv *aif)
+{
+
+ ng_unregister_cookie(aif->sys->regc);
+ free(aif->sys);
+ free(aif->pub.mib);
+}
+
+/*
+ * Handle a message from the ATM node
+ */
+static void
+handle_atm_message(const struct ng_mesg *mesg, const char *path __unused,
+ ng_ID_t node, void *uarg)
+{
+ struct atmif_priv *aif = uarg;
+ enum atmif_carrier_state ost;
+
+ switch (mesg->header.cmd) {
+
+ case NGM_ATM_IF_CHANGE:
+ {
+ const struct ngm_atm_if_change *arg;
+
+ ost = aif->pub.carrier;
+ if (mesg->header.arglen != sizeof(*arg)) {
+ syslog(LOG_ERR, "ATM_IF_CHANGE: wrong size");
+ atmif_check_carrier(aif);
+ return;
+ }
+ arg = (const struct ngm_atm_if_change *)
+ (const void *)mesg->data;
+
+ if (arg->carrier)
+ aif->pub.carrier = ATMIF_CARRIER_ON;
+ else
+ aif->pub.carrier = ATMIF_CARRIER_OFF;
+
+ if (ost != aif->pub.carrier)
+ atmif_send_notification(aif, ATMIF_NOTIFY_CARRIER,
+ (uintptr_t)ost);
+ return;
+ }
+
+ case NGM_ATM_VCC_CHANGE:
+ {
+ const struct ngm_atm_vcc_change *arg;
+
+ if (mesg->header.arglen != sizeof(*arg)) {
+ syslog(LOG_ERR, "ATM_VCC_CHANGE: wrong size");
+ return;
+ }
+ arg = (const struct ngm_atm_vcc_change *)
+ (const void *)mesg->data;
+ atmif_send_notification(aif, ATMIF_NOTIFY_VCC,
+ (uintptr_t)(((arg->vpi & 0xff) << 24) |
+ ((arg->vci & 0xffff) << 8) | (arg->state & 1)));
+ return;
+ }
+ }
+ syslog(LOG_WARNING, "spurious message %u from node [%x]",
+ mesg->header.cmd, node);
+}
+
+/*
+ * Attach to an ATM interface
+ */
+int
+atmif_sys_attach_if(struct atmif_priv *aif)
+{
+ struct ng_mesg *resp, *resp1;
+ struct namelist *list;
+ u_int i;
+
+ if ((aif->sys = malloc(sizeof(*aif->sys))) == NULL) {
+ syslog(LOG_CRIT, "out of memory");
+ return (-1);
+ }
+ memset(aif->sys, 0, sizeof(*aif->sys));
+
+ if ((aif->pub.mib = malloc(sizeof(*aif->pub.mib))) == NULL) {
+ free(aif->sys);
+ syslog(LOG_CRIT, "out of memory");
+ return (-1);
+ }
+
+ atmif_sys_fill_mib(aif);
+
+ /*
+ * Get ATM node Id. Must do it the hard way by scanning all nodes
+ * because the name may be wrong.
+ */
+ if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE, NGM_LISTNODES,
+ NULL, 0)) == NULL) {
+ syslog(LOG_ERR, "cannot fetch node list: %m");
+ free(aif->sys);
+ return (-1);
+ }
+ list = (struct namelist *)(void *)resp->data;
+
+ for (i = 0; i < list->numnames; i++) {
+ if (strcmp(list->nodeinfo[i].type, NG_ATM_NODE_TYPE) != 0)
+ continue;
+ if ((resp1 = ng_dialog_id(list->nodeinfo[i].id,
+ NGM_ATM_COOKIE, NGM_ATM_GET_IFNAME, NULL, 0)) == NULL)
+ continue;
+ if (strcmp(resp1->data, aif->pub.ifp->name) == 0) {
+ free(resp1);
+ break;
+ }
+ free(resp1);
+ }
+ if (i == list->numnames)
+ aif->sys->atm_node = 0;
+ else
+ aif->sys->atm_node = list->nodeinfo[i].id;
+
+ free(resp);
+
+ if ((aif->sys->regc = ng_register_cookie(module, NGM_ATM_COOKIE,
+ aif->sys->atm_node, handle_atm_message, aif)) == NULL) {
+ syslog(LOG_ERR, "cannot register cookie: %m");
+ free(aif->sys);
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Table of all ATM interfaces - Ng part
+ */
+int
+op_atmif_ng(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int vindex __unused, enum snmp_op op)
+{
+ struct atmif_priv *aif;
+ int err;
+
+ if ((err = atmif_get_aif(value, sub, op, &aif)) != SNMP_ERR_NOERROR)
+ return (err);
+
+ if (op == SNMP_OP_SET) {
+ switch (value->var.subs[sub - 1]) {
+
+ default:
+ return (SNMP_ERR_NOT_WRITEABLE);
+ }
+ }
+
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_begemotAtmIfNodeId:
+ value->v.uint32 = aif->sys->atm_node;
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+}
+
+/*
+ * Get vendor string
+ */
+int
+atm_sys_get_hw_vendor(struct atmif_priv *aif, struct snmp_value *value)
+{
+
+ if (aif->pub.mib->device >= sizeof(hwinfo) / sizeof(hwinfo[0]))
+ return (string_get(value, "unknown", -1));
+ return (string_get(value, hwinfo[aif->pub.mib->device].vendor, -1));
+}
+
+/*
+ * Get device string
+ */
+int
+atm_sys_get_hw_device(struct atmif_priv *aif, struct snmp_value *value)
+{
+
+ if (aif->pub.mib->device >= sizeof(hwinfo) / sizeof(hwinfo[0]))
+ return (string_get(value, "unknown", -1));
+ return (string_get(value, hwinfo[aif->pub.mib->device].device, -1));
+}
+
+/*
+ * Extract the ATM MIB from the interface's private MIB
+ */
+void
+atmif_sys_fill_mib(struct atmif_priv *aif)
+{
+ struct ifatm_mib *mib;
+
+ if (aif->pub.ifp->specmiblen != sizeof(struct ifatm_mib)) {
+ syslog(LOG_ERR, "atmif MIB has wrong size %zu",
+ aif->pub.ifp->specmiblen);
+ memset(aif->pub.mib, 0, sizeof(*aif->pub.mib));
+ aif->pub.mib->version = 0;
+ return;
+ }
+ mib = (struct ifatm_mib *)aif->pub.ifp->specmib;
+
+ aif->pub.mib->device = mib->device;
+ aif->pub.mib->serial = mib->serial;
+ aif->pub.mib->hw_version = mib->hw_version;
+ aif->pub.mib->sw_version = mib->sw_version;
+ aif->pub.mib->media = mib->media;
+
+ memcpy(aif->pub.mib->esi, mib->esi, 6);
+ aif->pub.mib->pcr = mib->pcr;
+ aif->pub.mib->vpi_bits = mib->vpi_bits;
+ aif->pub.mib->vci_bits = mib->vci_bits;
+ aif->pub.mib->max_vpcs = mib->max_vpcs;
+ aif->pub.mib->max_vccs = mib->max_vccs;
+}
diff --git a/usr.sbin/bsnmpd/modules/snmp_bridge/BEGEMOT-BRIDGE-MIB.txt b/usr.sbin/bsnmpd/modules/snmp_bridge/BEGEMOT-BRIDGE-MIB.txt
new file mode 100644
index 0000000..d55ea3c
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_bridge/BEGEMOT-BRIDGE-MIB.txt
@@ -0,0 +1,1166 @@
+--
+-- Copyright (C) 2006 Shteryana Shopova <syrinx@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 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$
+--
+
+BEGEMOT-BRIDGE-MIB DEFINITIONS ::= BEGIN
+
+IMPORTS
+ MODULE-IDENTITY, OBJECT-TYPE, NOTIFICATION-TYPE,
+ Counter32, Integer32, TimeTicks, mib-2
+ FROM SNMPv2-SMI
+ TEXTUAL-CONVENTION, MacAddress, TruthValue, RowStatus
+ FROM SNMPv2-TC
+ BridgeId, Timeout
+ FROM BRIDGE-MIB
+ InterfaceIndex FROM IF-MIB
+ begemot
+ FROM BEGEMOT-MIB;
+
+begemotBridge MODULE-IDENTITY
+ LAST-UPDATED "200708060000Z"
+ ORGANIZATION "Sofia University St. Kliment Ohridski"
+ CONTACT-INFO
+ " Shteryana Shopova
+
+ Postal: Faculty of Mathematics and Informatics
+ 5 James Bourchier Blvd.
+ 1164 Sofia
+ Bulgaria
+
+ Fax: +359 2 687 180
+
+ E-Mail: syrinx@FreeBSD.org"
+ DESCRIPTION
+ "The Begemot MIB for managing bridge interfaces."
+ REVISION "200708060000Z"
+ DESCRIPTION
+ "Third revision adds begemotBridgeBasePortPrivate
+ object."
+ REVISION "200611210000Z"
+ DESCRIPTION
+ "Second revision adds support for monitoring RSTP
+ specific variables."
+ REVISION "200607270000Z"
+ DESCRIPTION
+ "Initial revision."
+ ::= { begemot 205 }
+
+-- ---------------------------------------------------------- --
+BridgeIfName ::= TEXTUAL-CONVENTION
+ DISPLAY-HINT "16a"
+ STATUS current
+ DESCRIPTION
+ "Name of a bridge interface."
+ SYNTAX OCTET STRING (SIZE(1..16))
+
+BridgeIfNameOrEmpty ::= TEXTUAL-CONVENTION
+ DISPLAY-HINT "16a"
+ STATUS current
+ DESCRIPTION
+ "Name of a bridge interface."
+ SYNTAX OCTET STRING (SIZE(0..16))
+
+BridgePortId ::= TEXTUAL-CONVENTION
+ DISPLAY-HINT "1x.1x"
+ STATUS current
+ DESCRIPTION
+ "A port identifier that contains a bridge port's STP priority
+ in the first octet and the port number in the second octet."
+ SYNTAX OCTET STRING (SIZE(2))
+
+-- ---------------------------------------------------------- --
+-- subtrees in the Begemot Bridge MIB
+-- ---------------------------------------------------------- --
+begemotBridgeNotifications OBJECT IDENTIFIER ::= { begemotBridge 0 }
+
+begemotBridgeBase OBJECT IDENTIFIER ::= { begemotBridge 1 }
+
+begemotBridgeStp OBJECT IDENTIFIER ::= { begemotBridge 2 }
+
+begemotBridgeTp OBJECT IDENTIFIER ::= { begemotBridge 3 }
+
+begemotBridgePf OBJECT IDENTIFIER ::= { begemotBridge 4 }
+
+begemotBridgeConfigObjects OBJECT IDENTIFIER ::= { begemotBridge 5 }
+
+-- ---------------------------------------------------------- --
+-- the base Bridge interface table
+-- ---------------------------------------------------------- --
+
+begemotBridgeBaseTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF BegemotBridgeBaseEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A table that contains generic information for each
+ bridge interface on the managed device."
+ ::= { begemotBridgeBase 1 }
+
+begemotBridgeBaseEntry OBJECT-TYPE
+ SYNTAX BegemotBridgeBaseEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A list of information for the bridge interfaces on
+ the managed device."
+ INDEX { begemotBridgeBaseName }
+ ::= { begemotBridgeBaseTable 1 }
+
+BegemotBridgeBaseEntry ::= SEQUENCE {
+ begemotBridgeBaseName BridgeIfName,
+ begemotBridgeBaseAddress MacAddress,
+ begemotBridgeBaseNumPorts Integer32,
+ begemotBridgeBaseType INTEGER,
+ begemotBridgeBaseStatus RowStatus
+}
+
+begemotBridgeBaseName OBJECT-TYPE
+ SYNTAX BridgeIfName
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The name of the bridge interface for which this
+ entry contains management information."
+ ::= { begemotBridgeBaseEntry 1 }
+
+begemotBridgeBaseAddress OBJECT-TYPE
+ SYNTAX MacAddress
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The MAC address of the bridge interface."
+ ::= { begemotBridgeBaseEntry 2 }
+
+begemotBridgeBaseNumPorts OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of ports, members of this bridge."
+ ::= { begemotBridgeBaseEntry 3 }
+
+begemotBridgeBaseType OBJECT-TYPE
+ SYNTAX INTEGER {
+ unknown(1),
+ transparent-only(2),
+ sourceroute-only(3),
+ srt(4)
+ }
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Indicates what type of bridging this bridge can
+ perform."
+ ::= { begemotBridgeBaseEntry 4 }
+
+begemotBridgeBaseStatus OBJECT-TYPE
+ SYNTAX RowStatus
+ MAX-ACCESS read-create
+ STATUS current
+ DESCRIPTION
+ "Used to create/destroy bridge interfaces on the
+ managed device."
+ ::= { begemotBridgeBaseEntry 5 }
+
+-- ---------------------------------------------------------- --
+-- the base Bridge ports table
+-- ---------------------------------------------------------- --
+
+begemotBridgeBasePortTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF BegemotBridgeBasePortEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A table containing generic information about ports,
+ members of each bridge interface."
+ ::= { begemotBridgeBase 2 }
+
+begemotBridgeBasePortEntry OBJECT-TYPE
+ SYNTAX BegemotBridgeBasePortEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A list of information about a specific port, member of
+ a bridge interface."
+ INDEX { begemotBridgeBaseName, begemotBridgeBasePortIfIndex }
+ ::= { begemotBridgeBasePortTable 1 }
+
+BegemotBridgeBasePortEntry ::= SEQUENCE {
+ begemotBridgeBasePort Integer32,
+ begemotBridgeBasePortIfIndex InterfaceIndex,
+ begemotBridgeBaseSpanEnabled INTEGER,
+ begemotBridgeBasePortDelayExceededDiscards Counter32,
+ begemotBridgeBasePortMtuExceededDiscards Counter32,
+ begemotBridgeBasePortStatus RowStatus,
+ begemotBridgeBasePortPrivate TruthValue
+}
+
+begemotBridgeBasePort OBJECT-TYPE
+ SYNTAX Integer32 (1..65535)
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The system interface index of the interface corresponding
+ to this port."
+ ::= { begemotBridgeBasePortEntry 1 }
+
+begemotBridgeBasePortIfIndex OBJECT-TYPE
+ SYNTAX InterfaceIndex
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The value of the instance of the ifIndex object,
+ defined in IF-MIB, for the interface corresponding
+ to this port."
+ ::= { begemotBridgeBasePortEntry 2 }
+
+begemotBridgeBaseSpanEnabled OBJECT-TYPE
+ SYNTAX INTEGER {
+ enabled(1),
+ disabled(2)
+ }
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The value of this objects reflects whether the port
+ is a span port on the specified bridge interface."
+ ::= { begemotBridgeBasePortEntry 3 }
+
+begemotBridgeBasePortDelayExceededDiscards OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of frames discarded by this port due
+ to excessive transit delay through the bridge."
+ ::= { begemotBridgeBasePortEntry 4 }
+
+begemotBridgeBasePortMtuExceededDiscards OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of frames discarded by this port due
+ to an excessive size."
+ ::= { begemotBridgeBasePortEntry 5 }
+
+begemotBridgeBasePortStatus OBJECT-TYPE
+ SYNTAX RowStatus
+ MAX-ACCESS read-create
+ STATUS current
+ DESCRIPTION
+ "Used to control addition of member ports to or
+ removal of member ports from a specified bridge."
+ ::= { begemotBridgeBasePortEntry 6 }
+
+begemotBridgeBasePortPrivate OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The value of this objects reflects whether the port
+ has a PRIVATE flag set. A port with this flags set
+ can only communicate with ports not having the
+ PRIVATE flag set."
+ ::= { begemotBridgeBasePortEntry 7 }
+
+-- ---------------------------------------------------------- --
+-- the Bridge interface STP table
+-- ---------------------------------------------------------- --
+
+begemotBridgeStpTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF BegemotBridgeStpEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A table that contains Spanning Tree Protocol information
+ for each bridge interface on the managed device."
+ ::= { begemotBridgeStp 1 }
+
+begemotBridgeStpEntry OBJECT-TYPE
+ SYNTAX BegemotBridgeStpEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A list of information about the Spanning Tree Protocol
+ operation on a bridge interface."
+ AUGMENTS { begemotBridgeBaseEntry }
+ ::= { begemotBridgeStpTable 1 }
+
+BegemotBridgeStpEntry ::= SEQUENCE {
+ begemotBridgeStpProtocolSpecification INTEGER,
+ begemotBridgeStpPriority Integer32,
+ begemotBridgeStpTimeSinceTopologyChange TimeTicks,
+ begemotBridgeStpTopChanges Counter32,
+ begemotBridgeStpDesignatedRoot BridgeId,
+ begemotBridgeStpRootCost Integer32,
+ begemotBridgeStpRootPort Integer32,
+ begemotBridgeStpMaxAge Timeout,
+ begemotBridgeStpHelloTime Timeout,
+ begemotBridgeStpHoldTime Integer32,
+ begemotBridgeStpForwardDelay Timeout,
+ begemotBridgeStpBridgeMaxAge Timeout,
+ begemotBridgeStpBridgeHelloTime Timeout,
+ begemotBridgeStpBridgeForwardDelay Timeout,
+ begemotBridgeStpVersion INTEGER,
+ begemotBridgeStpTxHoldCount Integer32
+}
+
+begemotBridgeStpProtocolSpecification OBJECT-TYPE
+ SYNTAX INTEGER {
+ unknown(1),
+ decLb100(2),
+ ieee8021d(3)
+ }
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The Spanning Tree Protocol version being run on the
+ bridge interface. The value 'decLb100(2)' indicates the
+ DEC LANbridge 100 Spanning Tree protocol, 'ieee8021d(3)'
+ indicates the bridge is running IEEE 802.1D STP
+ implementation."
+ ::= { begemotBridgeStpEntry 1 }
+
+begemotBridgeStpPriority OBJECT-TYPE
+ SYNTAX Integer32 (0..65535)
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The priority value of the bridge interface forming the
+ first two octets of the bridge identifier. Acceptable
+ values are 0-61440, in steps of 4096."
+ ::= { begemotBridgeStpEntry 2 }
+
+begemotBridgeStpTimeSinceTopologyChange OBJECT-TYPE
+ SYNTAX TimeTicks
+ UNITS "centi-seconds"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The time (in hundreds of a second) since a topology change
+ was last detected by this bridge."
+ ::= { begemotBridgeStpEntry 3 }
+
+begemotBridgeStpTopChanges OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of times a topology change was detected by the
+ bridge interface since the management entity was initialized
+ or reset."
+ ::= { begemotBridgeStpEntry 4 }
+
+begemotBridgeStpDesignatedRoot OBJECT-TYPE
+ SYNTAX BridgeId
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The bridge identifier of the root of the spanning tree as
+ calculated by the Spanning Tree Protocol."
+ ::= { begemotBridgeStpEntry 5 }
+
+begemotBridgeStpRootCost OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The cost of the path from this bridge to the root bridge."
+ ::= { begemotBridgeStpEntry 6 }
+
+begemotBridgeStpRootPort OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The port number of the port that offers the lowest
+ cost path from this bridge to the root bridge of
+ the spanning tree. If this bridge is the root bridge,
+ this object shall have a value of zero."
+ ::= { begemotBridgeStpEntry 7 }
+
+begemotBridgeStpMaxAge OBJECT-TYPE
+ SYNTAX Timeout
+ UNITS "centi-seconds"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The maximum age of Spanning Tree Protocol information
+ received from the network on any port, before that
+ information is discarded. This is the actual value that
+ the bridge is currently using."
+ ::= { begemotBridgeStpEntry 8 }
+
+begemotBridgeStpHelloTime OBJECT-TYPE
+ SYNTAX Timeout
+ UNITS "centi-seconds"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The amount of time between transmission of
+ Configuration BPDUs by this bridge on any port,
+ when it is the root of the spanning tree or is
+ trying to become so. This is the actual value that
+ this bridge is currently using."
+ ::= { begemotBridgeStpEntry 9 }
+
+begemotBridgeStpHoldTime OBJECT-TYPE
+ SYNTAX Integer32
+ UNITS "centi-seconds"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This time value determines the interval length
+ during which no more than two Configuration BPDUs
+ shall be transmitted by this node, in units of
+ hundredths of a second."
+ ::= { begemotBridgeStpEntry 10 }
+
+begemotBridgeStpForwardDelay OBJECT-TYPE
+ SYNTAX Timeout
+ UNITS "centi-seconds"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This value, measured in units of hundredths of a second
+ determines how long a port will stay consecutively in the
+ Listening and Learning states before transitioning to
+ Forwarding state.
+ This is the actual value currently used by the bridge
+ as opposed to begemotBridgeStpBridgeForwardDelay, which
+ is the value this and all bridges participating in the
+ spanning tree were to use, if this was the root bridge."
+ ::= { begemotBridgeStpEntry 11 }
+
+begemotBridgeStpBridgeMaxAge OBJECT-TYPE
+ SYNTAX Timeout (600..4000)
+ UNITS "centi-seconds"
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The value that all bridges participating in the
+ spanning tree would use for MaxAge if this bridge
+ was the root of the spanning tree."
+ ::= { begemotBridgeStpEntry 12 }
+
+begemotBridgeStpBridgeHelloTime OBJECT-TYPE
+ SYNTAX Timeout (100..1000)
+ UNITS "centi-seconds"
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The value that all bridges participating in the
+ spanning tree would use for HelloTime if this
+ bridge was the root of the spanning tree."
+ ::= { begemotBridgeStpEntry 13 }
+
+begemotBridgeStpBridgeForwardDelay OBJECT-TYPE
+ SYNTAX Timeout (400..3000)
+ UNITS "centi-seconds"
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The value that all bridges participating in the
+ spanning tree would use for ForwardDelay if this
+ bridge was the root of the spanning tree."
+ ::= { begemotBridgeStpEntry 14 }
+
+begemotBridgeStpVersion OBJECT-TYPE
+ SYNTAX INTEGER {
+ stpCompatible(0),
+ rstp(2)
+ }
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The version of Spanning Tree Protocol the bridge is
+ currently running. The value 'stpCompatible(0)'
+ indicates the Spanning Tree Protocol specified in
+ IEEE 802.1D-1998 and 'rstp(2)' indicates the Rapid
+ Spanning Tree Protocol specified in IEEE 802.1w and
+ clause 17 of 802.1D-2004. The values are directly from
+ the IEEE standard. New values may be defined as future
+ versions of the protocol become available.
+
+ The value of this object MUST be retained across
+ reinitializations of the management system."
+ DEFVAL { rstp }
+ ::= { begemotBridgeStpEntry 15 }
+
+begemotBridgeStpTxHoldCount OBJECT-TYPE
+ SYNTAX Integer32 (1..10)
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The value used by the Port Transmit state machine to limit
+ the maximum transmission rate of BPDUs on the bridge interface.
+
+ The value of this object MUST be retained across
+ reinitializations of the management system."
+ DEFVAL { 3 }
+ ::= { begemotBridgeStpEntry 16 }
+
+-- ---------------------------------------------------------- --
+-- the Bridge STP ports table
+-- ---------------------------------------------------------- --
+
+begemotBridgeStpPortTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF BegemotBridgeStpPortEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A table containing Spanning Tree Protocol information
+ about the members of each bridge interface."
+ ::= { begemotBridgeStp 2 }
+
+begemotBridgeStpPortEntry OBJECT-TYPE
+ SYNTAX BegemotBridgeStpPortEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A list of Spanning Tree Protocol information about
+ a specific member of a bridge interface."
+ INDEX { begemotBridgeBaseName, begemotBridgeBasePortIfIndex }
+ ::= { begemotBridgeStpPortTable 1 }
+
+BegemotBridgeStpPortEntry ::= SEQUENCE {
+ begemotBridgeStpPort Integer32,
+ begemotBridgeStpPortPriority Integer32,
+ begemotBridgeStpPortState INTEGER,
+ begemotBridgeStpPortEnable INTEGER,
+ begemotBridgeStpPortPathCost Integer32,
+ begemotBridgeStpPortDesignatedRoot BridgeId,
+ begemotBridgeStpPortDesignatedCost Integer32,
+ begemotBridgeStpPortDesignatedBridge BridgeId,
+ begemotBridgeStpPortDesignatedPort BridgePortId,
+ begemotBridgeStpPortForwardTransitions Counter32
+}
+
+begemotBridgeStpPort OBJECT-TYPE
+ SYNTAX Integer32 (1..65535)
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The system interface index of the interface corresponding
+ to this port, for which the management entity has Spanning
+ Tree Protocol information."
+ ::= { begemotBridgeStpPortEntry 1 }
+
+begemotBridgeStpPortPriority OBJECT-TYPE
+ SYNTAX Integer32 (0..255)
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The STP priority of this port that is contained in the first
+ octet of its Port Identifier. The second octet contains the
+ value of begemotBridgeStpPort."
+ ::= { begemotBridgeStpPortEntry 2 }
+
+begemotBridgeStpPortState OBJECT-TYPE
+ SYNTAX INTEGER {
+ disabled(1),
+ blocking(2),
+ listening(3),
+ learning(4),
+ forwarding(5),
+ broken(6)
+ }
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The current state of the port as defined by the operation
+ of the Spanning Tree Protocol. If the Spanning Tree Protocol
+ is administratively disabled on the port, this object shall
+ have value disabled(1). A value of broken(6) does not correspond
+ to any legal state of a port, and if present should indicate
+ error in the operation of either the Spanning Tree Protocol
+ implementation running on the device or the management entity."
+ ::= { begemotBridgeStpPortEntry 3 }
+
+begemotBridgeStpPortEnable OBJECT-TYPE
+ SYNTAX INTEGER {
+ enabled(1),
+ disabled(2)
+ }
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The administrative Spanning Tree Protocol state of the
+ port - value of enabled(1) indicates that the port is
+ participating in the Spanning Tree Protocol operation."
+ ::= { begemotBridgeStpPortEntry 4 }
+
+begemotBridgeStpPortPathCost OBJECT-TYPE
+ SYNTAX Integer32 (1..65535)
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The contribution of the path through this port, when the port
+ is the Root Port, to the total cost of the path to the root
+ bridge for this bridge."
+ ::= { begemotBridgeStpPortEntry 5 }
+
+begemotBridgeStpPortDesignatedRoot OBJECT-TYPE
+ SYNTAX BridgeId
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The unique Bridge Identifier of the bridge recorded as the
+ root in the Root Identifier parameter of Configuration BPDUs
+ transmitted by the Designated Bridge for the LAN to which
+ the port is attached."
+ ::= { begemotBridgeStpPortEntry 6 }
+
+begemotBridgeStpPortDesignatedCost OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "For a Designated port, the path cost (equal to the Root
+ Path Cost of the bridge) offered to the LAN to which the
+ port is attached otherwise the cost of the path to the Root
+ offered by the Designated Port on the LAN to which this
+ Port is attached."
+ ::= { begemotBridgeStpPortEntry 7 }
+
+begemotBridgeStpPortDesignatedBridge OBJECT-TYPE
+ SYNTAX BridgeId
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The unique Bridge Identifier of the bridge to which the
+ port belongs, in the case when the port is a designated
+ port, otherwise the bridge believed to be the Designated
+ Bridge for the LAN to which this port is attached."
+ ::= { begemotBridgeStpPortEntry 8 }
+
+begemotBridgeStpPortDesignatedPort OBJECT-TYPE
+ SYNTAX BridgePortId
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The Port Identifier of the Bridge port, on the Designated
+ Bridge, through which the Designated Bridge transmits the
+ Configuration Message information stored by this port."
+ ::= { begemotBridgeStpPortEntry 9 }
+
+begemotBridgeStpPortForwardTransitions OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of times this port has transitioned
+ from the Learning state to the Forwarding state."
+ ::= { begemotBridgeStpPortEntry 10 }
+
+-- ---------------------------------------------------------- --
+-- the Bridge STP extended ports table
+-- ---------------------------------------------------------- --
+
+begemotBridgeStpExtPortTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF BegemotBridgeStpExtPortEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A table that contains port-specific Rapid Spanning Tree
+ information for the bridge interface members."
+ ::= { begemotBridgeStp 3 }
+
+begemotBridgeStpExtPortEntry OBJECT-TYPE
+ SYNTAX BegemotBridgeStpExtPortEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A list of Rapid Spanning Tree information maintained by
+ each bridge interface member."
+ AUGMENTS { begemotBridgeStpPortEntry }
+ ::= { begemotBridgeStpExtPortTable 1 }
+
+BegemotBridgeStpExtPortEntry ::= SEQUENCE {
+ begemotBridgeStpPortProtocolMigration TruthValue,
+ begemotBridgeStpPortAdminEdgePort TruthValue,
+ begemotBridgeStpPortOperEdgePort TruthValue,
+ begemotBridgeStpPortAdminPointToPoint INTEGER,
+ begemotBridgeStpPortOperPointToPoint TruthValue,
+ begemotBridgeStpPortAdminPathCost Integer32
+}
+
+begemotBridgeStpPortProtocolMigration OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "When operating in RSTP (version 2) mode, writing true(1)
+ to this object forces this port to transmit RSTP BPDUs.
+ Any other operation on this object has no effect and
+ it always returns false(2) when read."
+ ::= { begemotBridgeStpExtPortEntry 1 }
+
+begemotBridgeStpPortAdminEdgePort OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The administrative value of the Edge Port parameter. A
+ value of true(1) indicates that this port should be
+ assumed as an edge-port, and a value of false(2) indicates
+ that this port should be assumed as a non-edge-port.
+ Setting this object will also cause the corresponding
+ instance of begemotBridgeStpPortOperEdgePort to change to
+ the same value. Note that even when this object's value
+ is true, the value of the corresponding instance of
+ begemotBridgeStpPortOperEdgePort can be false if a BPDU
+ has been received.
+
+ The value of this object MUST be retained across
+ reinitializations of the management system."
+ ::= { begemotBridgeStpExtPortEntry 2 }
+
+begemotBridgeStpPortOperEdgePort OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The operational value of the Edge Port parameter. The
+ object is initialized to the value of the corresponding
+ instance of begemotBridgeStpPortAdminEdgePort. When the
+ corresponding instance of begemotBridgeStpPortAdminEdgePort
+ is set, this object will be changed as well. This object
+ will also be changed to false on reception of a BPDU."
+ ::= { begemotBridgeStpExtPortEntry 3 }
+
+begemotBridgeStpPortAdminPointToPoint OBJECT-TYPE
+ SYNTAX INTEGER {
+ forceTrue(0),
+ forceFalse(1),
+ auto(2)
+ }
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The administrative point-to-point status of the LAN segment
+ attached to this port, using the enumeration values of the
+ IEEE 802.1w clause. A value of forceTrue(0) indicates
+ that this port should always be treated as if it is
+ connected to a point-to-point link. A value of
+ forceFalse(1) indicates that this port should be treated as
+ having a shared media connection. A value of auto(2)
+ indicates that this port is considered to have a
+ point-to-point link if it is an Aggregator and all of its
+ members are aggregatable, or if the MAC entity
+ is configured for full duplex operation, either through
+ auto-negotiation or by management means. Manipulating this
+ object changes the underlying adminPortToPortMAC.
+
+ The value of this object MUST be retained across
+ reinitializations of the management system."
+ ::= { begemotBridgeStpExtPortEntry 4 }
+
+begemotBridgeStpPortOperPointToPoint OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The operational point-to-point status of the LAN segment
+ attached to this port. It indicates whether a port is
+ considered to have a point-to-point connection.
+ If adminPointToPointMAC is set to auto(2), then the value
+ of operPointToPointMAC is determined in accordance with the
+ specific procedures defined for the MAC entity concerned,
+ as defined in IEEE 802.1w, clause 6.5. The value is
+ determined dynamically; that is, it is re-evaluated whenever
+ the value of adminPointToPointMAC changes, and whenever
+ the specific procedures defined for the MAC entity evaluates
+ a change in its point-to-point status."
+ ::= { begemotBridgeStpExtPortEntry 5 }
+
+begemotBridgeStpPortAdminPathCost OBJECT-TYPE
+ SYNTAX Integer32 (0..200000000)
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The administratively assigned value for the contribution
+ of this port to the path cost of paths toward the spanning
+ tree root.
+
+ Writing a value of '0' assigns the automatically calculated
+ default Path Cost value to the port. If the default Path
+ Cost is being used, this object returns '0' when read.
+
+ This complements the object begemotBridgeStpPortPathCost or
+ begemotBridgeStpPortPathCost32, which returns the operational
+ value of the path cost.
+
+ The value of this object MUST be retained across
+ reinitializations of the management system."
+ ::= { begemotBridgeStpExtPortEntry 6 }
+
+-- ---------------------------------------------------------- --
+-- the Bridge interface Transparent bridging table
+-- ---------------------------------------------------------- --
+
+begemotBridgeTpTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF BegemotBridgeTpEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A table that contains information regarding transparent
+ bridging for each bridge interface on the managed device."
+ ::= { begemotBridgeTp 1 }
+
+begemotBridgeTpEntry OBJECT-TYPE
+ SYNTAX BegemotBridgeTpEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A list of information regarding transparent bridging
+ on a bridge interface."
+ AUGMENTS { begemotBridgeBaseEntry }
+ ::= { begemotBridgeTpTable 1 }
+
+BegemotBridgeTpEntry ::= SEQUENCE {
+ begemotBridgeTpLearnedEntryDiscards Counter32,
+ begemotBridgeTpAgingTime Integer32,
+ begemotBridgeTpMaxAddresses Integer32
+}
+
+begemotBridgeTpLearnedEntryDiscards OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The total number of Forwarding Database entries that would
+ have been learnt, but have been discarded due to Forwarding
+ Address Table having reached it's maximum entries limit."
+ ::= { begemotBridgeTpEntry 1 }
+
+begemotBridgeTpAgingTime OBJECT-TYPE
+ SYNTAX Integer32 (10..1000000)
+ UNITS "seconds"
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The timeout period in seconds before aging out
+ dynamically learnt forwarding entries."
+ ::= { begemotBridgeTpEntry 2 }
+
+begemotBridgeTpMaxAddresses OBJECT-TYPE
+ SYNTAX Integer32 (1..10000)
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The maximum number of entires that this bridge can
+ learn in it's Forwarding Address Table and use for
+ making forwarding decisions."
+ ::= { begemotBridgeTpEntry 3 }
+
+-- ---------------------------------------------------------- --
+-- The Forwarding Database for Transparent Bridging interfaces
+-- ---------------------------------------------------------- --
+
+begemotBridgeTpFdbTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF BegemotBridgeTpFdbEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A table that contains information about unicast entries
+ for which the bridge interfaces have forwarding and/or
+ filtering information. This information is used by the
+ bridge interfaces to make forwarding decisions."
+ ::= { begemotBridgeTp 2 }
+
+begemotBridgeTpFdbEntry OBJECT-TYPE
+ SYNTAX BegemotBridgeTpFdbEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Information about a specific unicast MAC address
+ for which the bridge interface has some forwarding
+ and/or filtering information."
+ INDEX { begemotBridgeBaseName, begemotBridgeTpFdbAddress }
+ ::= { begemotBridgeTpFdbTable 1 }
+
+BegemotBridgeTpFdbEntry ::= SEQUENCE {
+ begemotBridgeTpFdbAddress MacAddress,
+ begemotBridgeTpFdbPort Integer32,
+ begemotBridgeTpFdbStatus INTEGER
+}
+
+begemotBridgeTpFdbAddress OBJECT-TYPE
+ SYNTAX MacAddress
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "A unicast MAC address for which the bridge has which the
+ bridge interface has some forwarding and/or filtering
+ information."
+ ::= { begemotBridgeTpFdbEntry 1 }
+
+begemotBridgeTpFdbPort OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The port number of the bridge port on which a frame having
+ a source address equal to the value of the corresponding
+ instance of begemotBridgeTpFdbAddress has been seen."
+ ::= { begemotBridgeTpFdbEntry 2 }
+
+begemotBridgeTpFdbStatus OBJECT-TYPE
+ SYNTAX INTEGER {
+ other(1),
+ invalid(2),
+ learned(3),
+ self(4),
+ mgmt(5)
+ }
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The status of this entry. The meanings of the
+ values are:
+ other(1) - none of the following.
+ invalid(2) - this entry is no longer valid (e.g.,
+ it was learned but has since aged out), but has
+ not yet been flushed from the table.
+ learned(3) - the value of the corresponding instance
+ of begemotBridgeTpFdbPort was learned, and is being
+ used.
+ self(4) - the value of the corresponding instance of
+ begemotBridgeTpFdbAddress represents one of the
+ bridge's addresses. The corresponding instance of
+ begemotBridgeTpFdbPort indicates which of the bridge's
+ ports has this address.
+ mgmt(5) - the value of the corresponding instance of
+ begemotBridgeTpFdbAddress has been added to the
+ bridge's Forwarding Database by some management
+ means."
+ ::= { begemotBridgeTpFdbEntry 3 }
+
+-- ---------------------------------------------------------- --
+-- Ports table for Transparent Bridging interfaces
+-- ---------------------------------------------------------- --
+
+begemotBridgeTpPortTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF BegemotBridgeTpPortEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A table that contains information about every bridge port,
+ member of a bridge interface, associated with the transparent
+ bridging function of the bridge."
+ ::= { begemotBridgeTp 3 }
+
+begemotBridgeTpPortEntry OBJECT-TYPE
+ SYNTAX BegemotBridgeTpPortEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A list of information about every bridge port, member of a
+ bridge interface, associated with the bridge's transparent
+ bridging function."
+ INDEX { begemotBridgeBaseName, begemotBridgeBasePortIfIndex }
+ ::= { begemotBridgeTpPortTable 1 }
+
+BegemotBridgeTpPortEntry ::= SEQUENCE {
+ begemotBridgeTpPort Integer32,
+ begemotBridgeTpPortMaxInfo Integer32,
+ begemotBridgeTpPortInFrames Counter32,
+ begemotBridgeTpPortOutFrames Counter32,
+ begemotBridgeTpPortInDiscards Counter32
+}
+
+begemotBridgeTpPort OBJECT-TYPE
+ SYNTAX Integer32 (1..65535)
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The system interface index of the port for which this entry
+ contains Transparent bridging management information."
+ ::= { begemotBridgeTpPortEntry 1 }
+
+begemotBridgeTpPortMaxInfo OBJECT-TYPE
+ SYNTAX Integer32
+ UNITS "bytes"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The maximum size of the INFO (non-MAC) field that this port
+ will receive or transmit."
+ ::= { begemotBridgeTpPortEntry 2 }
+
+begemotBridgeTpPortInFrames OBJECT-TYPE
+ SYNTAX Counter32
+ UNITS "frames"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of frames that have been received by this
+ port from its segment. Note that a frame received on the
+ interface corresponding to this port is only counted by
+ this object if and only if it is for a protocol being
+ processed by the local bridging function, including
+ bridge management frames."
+ ::= { begemotBridgeTpPortEntry 3 }
+
+begemotBridgeTpPortOutFrames OBJECT-TYPE
+ SYNTAX Counter32
+ UNITS "frames"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of frames that have been transmitted by this
+ port to its segment. Note that a frame transmitted on
+ the interface corresponding to this port is only counted
+ by this object if and only if it is for a protocol being
+ processed by the local bridging function, including
+ bridge management frames."
+ ::= { begemotBridgeTpPortEntry 4 }
+
+begemotBridgeTpPortInDiscards OBJECT-TYPE
+ SYNTAX Counter32
+ UNITS "frames"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Count of received valid frames that were discarded
+ (i.e., filtered) by the Forwarding Process."
+ ::= { begemotBridgeTpPortEntry 5 }
+
+-- ---------------------------------------------------------- --
+-- the begemotBridgePf objects
+-- ---------------------------------------------------------- --
+
+begemotBridgePfilStatus OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "Indicates whether packet filtering by some firewall
+ package is enabled on the bridge interface."
+ ::= { begemotBridgePf 1 }
+
+begemotBridgePfilMembers OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "A value of true(1) indicates that packet filtering is
+ enabled on both incoming and outgoing bridge member
+ interfaces."
+ ::= { begemotBridgePf 2 }
+
+begemotBridgePfilIpOnly OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "This value controls the handling of non-IP packets which
+ are not passed on for further processing to a firewall
+ package. A value of false(0) indicates that all non-IP
+ Ethernet frames are passed unconditionally."
+ ::= { begemotBridgePf 3 }
+
+begemotBridgeLayer2PfStatus OBJECT-TYPE
+ SYNTAX INTEGER {
+ enabled(1),
+ disabled(2)
+ }
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "This value indicates whether layer2 filtering by a
+ firewall package is enabled for bridge interfaces."
+ ::= { begemotBridgePf 4 }
+
+-- ---------------------------------------------------------- --
+-- the begemotBridgeConfigObjects objects
+-- ---------------------------------------------------------- --
+
+begemotBridgeDefaultBridgeIf OBJECT-TYPE
+
+ SYNTAX BridgeIfNameOrEmpty
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The name of the bridge interface that will be managed
+ via objects in IETF BRIDGE-MIB (RFC4188). If the
+ object's value is set to an empty string, bridge interfaces
+ will only be managed via objects in this MIB module."
+ DEFVAL { "bridge0" }
+ ::= { begemotBridgeConfigObjects 1 }
+
+begemotBridgeDataUpdate OBJECT-TYPE
+
+ SYNTAX Timeout (1..300)
+ UNITS "seconds"
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The maximum age in seconds of the cached data."
+ DEFVAL { 10 }
+ ::= { begemotBridgeConfigObjects 2 }
+
+begemotBridgeDataPoll OBJECT-TYPE
+
+ SYNTAX Timeout (1..3600)
+ UNITS "seconds"
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The polling rate of data when the module is idle."
+ DEFVAL { 300 }
+ ::= { begemotBridgeConfigObjects 3 }
+
+-- ---------------------------------------------------------- --
+-- Notifications for the Spanning Tree Protocol
+-- ---------------------------------------------------------- --
+
+begemotBridgeNewRoot NOTIFICATION-TYPE
+ OBJECTS { begemotBridgeBaseName }
+ STATUS current
+ DESCRIPTION
+ "The begemotBridgeNewRoot trap indicates that one of the
+ bridge interfaces on the sending agent's device has
+ become the new root of the spanning tree topology it is
+ participating in."
+ ::= { begemotBridgeNotifications 1 }
+
+begemotBridgeTopologyChange NOTIFICATION-TYPE
+ OBJECTS { begemotBridgeBaseName }
+ STATUS current
+ DESCRIPTION
+ "A begemotBridgeTopologyChange trap is send when a member
+ port on one of the bridge interfaces, monitored by the agent,
+ transitions from the Learning state to the Forwarding state,
+ or from the Forwarding state to the Blocking state. The trap
+ is not sent if a begemotBridgeNewRoot trap is sent for the
+ same transition."
+ ::= { begemotBridgeNotifications 2 }
+
+END
diff --git a/usr.sbin/bsnmpd/modules/snmp_bridge/BRIDGE-MIB.txt b/usr.sbin/bsnmpd/modules/snmp_bridge/BRIDGE-MIB.txt
new file mode 100644
index 0000000..9f87b65
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_bridge/BRIDGE-MIB.txt
@@ -0,0 +1,1483 @@
+--
+-- Copyright (C) The Internet Society (2005).
+--
+-- This document is subject to the rights, licenses and restrictions
+-- contained in BCP 78, and except as set forth therein, the authors
+-- retain all their rights.
+--
+-- This document and the information contained herein are provided on an
+-- "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+-- OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+-- ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+-- INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+-- INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+-- WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+--
+-- $FreeBSD$
+--
+
+BRIDGE-MIB DEFINITIONS ::= BEGIN
+
+-- ---------------------------------------------------------- --
+-- MIB for IEEE 802.1D devices
+-- ---------------------------------------------------------- --
+IMPORTS
+ MODULE-IDENTITY, OBJECT-TYPE, NOTIFICATION-TYPE,
+ Counter32, Integer32, TimeTicks, mib-2
+ FROM SNMPv2-SMI
+ TEXTUAL-CONVENTION, MacAddress
+ FROM SNMPv2-TC
+ MODULE-COMPLIANCE, OBJECT-GROUP, NOTIFICATION-GROUP
+ FROM SNMPv2-CONF
+ InterfaceIndex FROM IF-MIB
+ ;
+
+dot1dBridge MODULE-IDENTITY
+ LAST-UPDATED "200509190000Z"
+ ORGANIZATION "IETF Bridge MIB Working Group"
+ CONTACT-INFO
+ "Email: bridge-mib@ietf.org
+
+ K.C. Norseth (Editor)
+ L-3 Communications
+ Tel: +1 801-594-2809
+ Email: kenyon.c.norseth@L-3com.com
+ Postal: 640 N. 2200 West.
+ Salt Lake City, Utah 84116-0850
+ Les Bell (Editor)
+ 3Com Europe Limited
+ Phone: +44 1442 438025
+ Email: elbell@ntlworld.com
+ Postal: 3Com Centre, Boundary Way
+ Hemel Hempstead
+ Herts. HP2 7YU
+ UK
+
+ Send comments to <bridge-mib@ietf.org>"
+ DESCRIPTION
+ "The Bridge MIB module for managing devices that support
+ IEEE 802.1D.
+
+ Copyright (C) The Internet Society (2005). This version of
+ this MIB module is part of RFC 4188; see the RFC itself for
+ full legal notices."
+ REVISION "200509190000Z"
+ DESCRIPTION
+ "Third revision, published as part of RFC 4188.
+
+ The MIB module has been converted to SMIv2 format.
+ Conformance statements have been added and some
+ description and reference clauses have been updated.
+
+ The object dot1dStpPortPathCost32 was added to
+ support IEEE 802.1t and the permissible values of
+ dot1dStpPriority and dot1dStpPortPriority have been
+ clarified for bridges supporting IEEE 802.1t or
+ IEEE 802.1w.
+
+ The interpretation of dot1dStpTimeSinceTopologyChange
+ has been clarified for bridges supporting the Rapid
+ Spanning Tree Protocol (RSTP)."
+ REVISION "199307310000Z"
+ DESCRIPTION
+ "Second revision, published as part of RFC 1493."
+ REVISION "199112310000Z"
+ DESCRIPTION
+ "Initial revision, published as part of RFC 1286."
+ ::= { mib-2 17 }
+
+
+-- ---------------------------------------------------------- --
+-- Textual Conventions
+-- ---------------------------------------------------------- --
+
+BridgeId ::= TEXTUAL-CONVENTION
+ STATUS current
+ DESCRIPTION
+ "The Bridge-Identifier, as used in the Spanning Tree
+ Protocol, to uniquely identify a bridge. Its first two
+ octets (in network byte order) contain a priority value,
+ and its last 6 octets contain the MAC address used to
+ refer to a bridge in a unique fashion (typically, the
+ numerically smallest MAC address of all ports on the
+ bridge)."
+ SYNTAX OCTET STRING (SIZE (8))
+
+Timeout ::= TEXTUAL-CONVENTION
+ DISPLAY-HINT "d"
+ STATUS current
+ DESCRIPTION
+ "A Spanning Tree Protocol (STP) timer in units of 1/100
+ seconds. Several objects in this MIB module represent
+ values of timers used by the Spanning Tree Protocol.
+ In this MIB, these timers have values in units of
+ hundredths of a second (i.e., 1/100 secs).
+
+ These timers, when stored in a Spanning Tree Protocol's
+ BPDU, are in units of 1/256 seconds. Note, however, that
+ 802.1D-1998 specifies a settable granularity of no more
+ than one second for these timers. To avoid ambiguity,
+ a conversion algorithm is defined below for converting
+ between the different units, which ensures a timer's
+ value is not distorted by multiple conversions.
+
+ To convert a Timeout value into a value in units of
+ 1/256 seconds, the following algorithm should be used:
+
+ b = floor( (n * 256) / 100)
+
+ where:
+ floor = quotient [ignore remainder]
+ n is the value in 1/100 second units
+ b is the value in 1/256 second units
+
+ To convert the value from 1/256 second units back to
+ 1/100 seconds, the following algorithm should be used:
+
+ n = ceiling( (b * 100) / 256)
+
+ where:
+ ceiling = quotient [if remainder is 0], or
+ quotient + 1 [if remainder is nonzero]
+ n is the value in 1/100 second units
+ b is the value in 1/256 second units
+
+ Note: it is important that the arithmetic operations are
+ done in the order specified (i.e., multiply first,
+ divide second)."
+ SYNTAX Integer32
+
+-- ---------------------------------------------------------- --
+-- subtrees in the Bridge MIB
+-- ---------------------------------------------------------- --
+
+dot1dNotifications OBJECT IDENTIFIER ::= { dot1dBridge 0 }
+
+dot1dBase OBJECT IDENTIFIER ::= { dot1dBridge 1 }
+dot1dStp OBJECT IDENTIFIER ::= { dot1dBridge 2 }
+
+dot1dSr OBJECT IDENTIFIER ::= { dot1dBridge 3 }
+-- documented in RFC 1525
+
+dot1dTp OBJECT IDENTIFIER ::= { dot1dBridge 4 }
+dot1dStatic OBJECT IDENTIFIER ::= { dot1dBridge 5 }
+
+-- Subtrees used by Bridge MIB Extensions:
+-- pBridgeMIB MODULE-IDENTITY ::= { dot1dBridge 6 }
+-- qBridgeMIB MODULE-IDENTITY ::= { dot1dBridge 7 }
+-- Note that the practice of registering related MIB modules
+-- below dot1dBridge has been discouraged since there is no
+-- robust mechanism to track such registrations.
+
+dot1dConformance OBJECT IDENTIFIER ::= { dot1dBridge 8 }
+
+-- ---------------------------------------------------------- --
+-- the dot1dBase subtree
+-- ---------------------------------------------------------- --
+-- Implementation of the dot1dBase subtree is mandatory for all
+-- bridges.
+-- ---------------------------------------------------------- --
+
+dot1dBaseBridgeAddress OBJECT-TYPE
+
+ SYNTAX MacAddress
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The MAC address used by this bridge when it must be
+ referred to in a unique fashion. It is recommended
+ that this be the numerically smallest MAC address of
+ all ports that belong to this bridge. However, it is only
+ required to be unique. When concatenated with
+ dot1dStpPriority, a unique BridgeIdentifier is formed,
+ which is used in the Spanning Tree Protocol."
+ REFERENCE
+ "IEEE 802.1D-1998: clauses 14.4.1.1.3 and 7.12.5"
+ ::= { dot1dBase 1 }
+
+dot1dBaseNumPorts OBJECT-TYPE
+ SYNTAX Integer32
+ UNITS "ports"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of ports controlled by this bridging
+ entity."
+ REFERENCE
+ "IEEE 802.1D-1998: clause 14.4.1.1.3"
+ ::= { dot1dBase 2 }
+
+dot1dBaseType OBJECT-TYPE
+ SYNTAX INTEGER {
+ unknown(1),
+ transparent-only(2),
+ sourceroute-only(3),
+ srt(4)
+ }
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Indicates what type of bridging this bridge can
+ perform. If a bridge is actually performing a
+ certain type of bridging, this will be indicated by
+ entries in the port table for the given type."
+ ::= { dot1dBase 3 }
+
+-- ---------------------------------------------------------- --
+-- The Generic Bridge Port Table
+-- ---------------------------------------------------------- --
+dot1dBasePortTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF Dot1dBasePortEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A table that contains generic information about every
+ port that is associated with this bridge. Transparent,
+ source-route, and srt ports are included."
+ ::= { dot1dBase 4 }
+
+dot1dBasePortEntry OBJECT-TYPE
+ SYNTAX Dot1dBasePortEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+
+ DESCRIPTION
+ "A list of information for each port of the bridge."
+ REFERENCE
+ "IEEE 802.1D-1998: clause 14.4.2, 14.6.1"
+ INDEX { dot1dBasePort }
+ ::= { dot1dBasePortTable 1 }
+
+Dot1dBasePortEntry ::=
+ SEQUENCE {
+ dot1dBasePort
+ Integer32,
+ dot1dBasePortIfIndex
+ InterfaceIndex,
+ dot1dBasePortCircuit
+ OBJECT IDENTIFIER,
+ dot1dBasePortDelayExceededDiscards
+ Counter32,
+ dot1dBasePortMtuExceededDiscards
+ Counter32
+ }
+
+dot1dBasePort OBJECT-TYPE
+ SYNTAX Integer32 (1..65535)
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The port number of the port for which this entry
+ contains bridge management information."
+ ::= { dot1dBasePortEntry 1 }
+
+dot1dBasePortIfIndex OBJECT-TYPE
+ SYNTAX InterfaceIndex
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The value of the instance of the ifIndex object,
+ defined in IF-MIB, for the interface corresponding
+ to this port."
+ ::= { dot1dBasePortEntry 2 }
+
+dot1dBasePortCircuit OBJECT-TYPE
+ SYNTAX OBJECT IDENTIFIER
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "For a port that (potentially) has the same value of
+ dot1dBasePortIfIndex as another port on the same bridge.
+ This object contains the name of an object instance
+ unique to this port. For example, in the case where
+ multiple ports correspond one-to-one with multiple X.25
+ virtual circuits, this value might identify an (e.g.,
+ the first) object instance associated with the X.25
+ virtual circuit corresponding to this port.
+
+ For a port which has a unique value of
+ dot1dBasePortIfIndex, this object can have the value
+ { 0 0 }."
+ ::= { dot1dBasePortEntry 3 }
+
+dot1dBasePortDelayExceededDiscards OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of frames discarded by this port due
+ to excessive transit delay through the bridge. It
+ is incremented by both transparent and source
+ route bridges."
+ REFERENCE
+ "IEEE 802.1D-1998: clause 14.6.1.1.3"
+ ::= { dot1dBasePortEntry 4 }
+
+dot1dBasePortMtuExceededDiscards OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of frames discarded by this port due
+ to an excessive size. It is incremented by both
+ transparent and source route bridges."
+ REFERENCE
+ "IEEE 802.1D-1998: clause 14.6.1.1.3"
+ ::= { dot1dBasePortEntry 5 }
+
+-- ---------------------------------------------------------- --
+-- the dot1dStp subtree
+-- ---------------------------------------------------------- --
+-- Implementation of the dot1dStp subtree is optional. It is
+-- implemented by those bridges that support the Spanning Tree
+-- Protocol.
+-- ---------------------------------------------------------- --
+dot1dStpProtocolSpecification OBJECT-TYPE
+ SYNTAX INTEGER {
+ unknown(1),
+ decLb100(2),
+ ieee8021d(3)
+ }
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "An indication of what version of the Spanning Tree
+ Protocol is being run. The value 'decLb100(2)'
+ indicates the DEC LANbridge 100 Spanning Tree protocol.
+ IEEE 802.1D implementations will return 'ieee8021d(3)'.
+ If future versions of the IEEE Spanning Tree Protocol
+ that are incompatible with the current version
+ are released a new value will be defined."
+ ::= { dot1dStp 1 }
+
+dot1dStpPriority OBJECT-TYPE
+ SYNTAX Integer32 (0..65535)
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The value of the write-able portion of the Bridge ID
+ (i.e., the first two octets of the (8 octet long) Bridge
+ ID). The other (last) 6 octets of the Bridge ID are
+ given by the value of dot1dBaseBridgeAddress.
+ On bridges supporting IEEE 802.1t or IEEE 802.1w,
+ permissible values are 0-61440, in steps of 4096."
+ REFERENCE
+ "IEEE 802.1D-1998 clause 8.10.2, Table 8-4,
+ IEEE 802.1t clause 8.10.2, Table 8-4, clause 14.3."
+ ::= { dot1dStp 2 }
+
+dot1dStpTimeSinceTopologyChange OBJECT-TYPE
+ SYNTAX TimeTicks
+ UNITS "centi-seconds"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The time (in hundredths of a second) since the
+ last time a topology change was detected by the
+ bridge entity.
+ For RSTP, this reports the time since the tcWhile
+ timer for any port on this Bridge was nonzero."
+ REFERENCE
+ "IEEE 802.1D-1998 clause 14.8.1.1.,
+ IEEE 802.1w clause 14.8.1.1."
+ ::= { dot1dStp 3 }
+
+dot1dStpTopChanges OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The total number of topology changes detected by
+ this bridge since the management entity was last
+ reset or initialized."
+ REFERENCE
+ "IEEE 802.1D-1998 clause 14.8.1.1."
+ ::= { dot1dStp 4 }
+
+dot1dStpDesignatedRoot OBJECT-TYPE
+ SYNTAX BridgeId
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The bridge identifier of the root of the spanning
+ tree, as determined by the Spanning Tree Protocol,
+ as executed by this node. This value is used as
+ the Root Identifier parameter in all Configuration
+ Bridge PDUs originated by this node."
+ REFERENCE
+ "IEEE 802.1D-1998: clause 8.5.3.1"
+ ::= { dot1dStp 5 }
+
+dot1dStpRootCost OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The cost of the path to the root as seen from
+ this bridge."
+ REFERENCE
+ "IEEE 802.1D-1998: clause 8.5.3.2"
+ ::= { dot1dStp 6 }
+
+dot1dStpRootPort OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The port number of the port that offers the lowest
+ cost path from this bridge to the root bridge."
+ REFERENCE
+ "IEEE 802.1D-1998: clause 8.5.3.3"
+ ::= { dot1dStp 7 }
+
+dot1dStpMaxAge OBJECT-TYPE
+ SYNTAX Timeout
+ UNITS "centi-seconds"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The maximum age of Spanning Tree Protocol information
+ learned from the network on any port before it is
+ discarded, in units of hundredths of a second. This is
+ the actual value that this bridge is currently using."
+ REFERENCE
+ "IEEE 802.1D-1998: clause 8.5.3.4"
+ ::= { dot1dStp 8 }
+
+dot1dStpHelloTime OBJECT-TYPE
+ SYNTAX Timeout
+ UNITS "centi-seconds"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The amount of time between the transmission of
+ Configuration bridge PDUs by this node on any port when
+ it is the root of the spanning tree, or trying to become
+ so, in units of hundredths of a second. This is the
+ actual value that this bridge is currently using."
+ REFERENCE
+ "IEEE 802.1D-1998: clause 8.5.3.5"
+ ::= { dot1dStp 9 }
+
+dot1dStpHoldTime OBJECT-TYPE
+ SYNTAX Integer32
+ UNITS "centi-seconds"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This time value determines the interval length
+ during which no more than two Configuration bridge
+ PDUs shall be transmitted by this node, in units
+ of hundredths of a second."
+ REFERENCE
+ "IEEE 802.1D-1998: clause 8.5.3.14"
+ ::= { dot1dStp 10 }
+
+dot1dStpForwardDelay OBJECT-TYPE
+ SYNTAX Timeout
+ UNITS "centi-seconds"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "This time value, measured in units of hundredths of a
+ second, controls how fast a port changes its spanning
+ state when moving towards the Forwarding state. The
+ value determines how long the port stays in each of the
+ Listening and Learning states, which precede the
+ Forwarding state. This value is also used when a
+ topology change has been detected and is underway, to
+ age all dynamic entries in the Forwarding Database.
+ [Note that this value is the one that this bridge is
+ currently using, in contrast to
+ dot1dStpBridgeForwardDelay, which is the value that this
+ bridge and all others would start using if/when this
+ bridge were to become the root.]"
+ REFERENCE
+ "IEEE 802.1D-1998: clause 8.5.3.6"
+ ::= { dot1dStp 11 }
+
+dot1dStpBridgeMaxAge OBJECT-TYPE
+ SYNTAX Timeout (600..4000)
+ UNITS "centi-seconds"
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The value that all bridges use for MaxAge when this
+ bridge is acting as the root. Note that 802.1D-1998
+ specifies that the range for this parameter is related
+ to the value of dot1dStpBridgeHelloTime. The
+ granularity of this timer is specified by 802.1D-1998 to
+ be 1 second. An agent may return a badValue error if a
+ set is attempted to a value that is not a whole number
+ of seconds."
+ REFERENCE
+ "IEEE 802.1D-1998: clause 8.5.3.8"
+ ::= { dot1dStp 12 }
+
+dot1dStpBridgeHelloTime OBJECT-TYPE
+ SYNTAX Timeout (100..1000)
+ UNITS "centi-seconds"
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The value that all bridges use for HelloTime when this
+ bridge is acting as the root. The granularity of this
+ timer is specified by 802.1D-1998 to be 1 second. An
+ agent may return a badValue error if a set is attempted
+ to a value that is not a whole number of seconds."
+ REFERENCE
+ "IEEE 802.1D-1998: clause 8.5.3.9"
+ ::= { dot1dStp 13 }
+
+dot1dStpBridgeForwardDelay OBJECT-TYPE
+ SYNTAX Timeout (400..3000)
+ UNITS "centi-seconds"
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The value that all bridges use for ForwardDelay when
+ this bridge is acting as the root. Note that
+ 802.1D-1998 specifies that the range for this parameter
+ is related to the value of dot1dStpBridgeMaxAge. The
+ granularity of this timer is specified by 802.1D-1998 to
+ be 1 second. An agent may return a badValue error if a
+ set is attempted to a value that is not a whole number
+ of seconds."
+ REFERENCE
+ "IEEE 802.1D-1998: clause 8.5.3.10"
+ ::= { dot1dStp 14 }
+
+-- ---------------------------------------------------------- --
+-- The Spanning Tree Port Table
+-- ---------------------------------------------------------- --
+
+dot1dStpPortTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF Dot1dStpPortEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A table that contains port-specific information
+ for the Spanning Tree Protocol."
+ ::= { dot1dStp 15 }
+
+dot1dStpPortEntry OBJECT-TYPE
+ SYNTAX Dot1dStpPortEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A list of information maintained by every port about
+ the Spanning Tree Protocol state for that port."
+ INDEX { dot1dStpPort }
+ ::= { dot1dStpPortTable 1 }
+
+Dot1dStpPortEntry ::=
+ SEQUENCE {
+ dot1dStpPort
+ Integer32,
+ dot1dStpPortPriority
+ Integer32,
+ dot1dStpPortState
+ INTEGER,
+ dot1dStpPortEnable
+ INTEGER,
+ dot1dStpPortPathCost
+ Integer32,
+ dot1dStpPortDesignatedRoot
+ BridgeId,
+ dot1dStpPortDesignatedCost
+ Integer32,
+ dot1dStpPortDesignatedBridge
+ BridgeId,
+ dot1dStpPortDesignatedPort
+ OCTET STRING,
+ dot1dStpPortForwardTransitions
+ Counter32,
+ dot1dStpPortPathCost32
+ Integer32
+ }
+
+dot1dStpPort OBJECT-TYPE
+ SYNTAX Integer32 (1..65535)
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The port number of the port for which this entry
+ contains Spanning Tree Protocol management information."
+ REFERENCE
+ "IEEE 802.1D-1998: clause 14.8.2.1.2"
+ ::= { dot1dStpPortEntry 1 }
+
+dot1dStpPortPriority OBJECT-TYPE
+ SYNTAX Integer32 (0..255)
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The value of the priority field that is contained in
+ the first (in network byte order) octet of the (2 octet
+ long) Port ID. The other octet of the Port ID is given
+ by the value of dot1dStpPort.
+ On bridges supporting IEEE 802.1t or IEEE 802.1w,
+ permissible values are 0-240, in steps of 16."
+ REFERENCE
+ "IEEE 802.1D-1998 clause 8.10.2, Table 8-4,
+ IEEE 802.1t clause 8.10.2, Table 8-4, clause 14.3."
+ ::= { dot1dStpPortEntry 2 }
+
+dot1dStpPortState OBJECT-TYPE
+ SYNTAX INTEGER {
+ disabled(1),
+ blocking(2),
+ listening(3),
+ learning(4),
+ forwarding(5),
+ broken(6)
+ }
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The port's current state, as defined by application of
+ the Spanning Tree Protocol. This state controls what
+ action a port takes on reception of a frame. If the
+ bridge has detected a port that is malfunctioning, it
+ will place that port into the broken(6) state. For
+ ports that are disabled (see dot1dStpPortEnable), this
+ object will have a value of disabled(1)."
+ REFERENCE
+ "IEEE 802.1D-1998: clause 8.5.5.2"
+ ::= { dot1dStpPortEntry 3 }
+
+dot1dStpPortEnable OBJECT-TYPE
+ SYNTAX INTEGER {
+ enabled(1),
+ disabled(2)
+ }
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The enabled/disabled status of the port."
+ REFERENCE
+ "IEEE 802.1D-1998: clause 8.5.5.2"
+ ::= { dot1dStpPortEntry 4 }
+
+dot1dStpPortPathCost OBJECT-TYPE
+ SYNTAX Integer32 (1..65535)
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The contribution of this port to the path cost of
+ paths towards the spanning tree root which include
+ this port. 802.1D-1998 recommends that the default
+ value of this parameter be in inverse proportion to
+ the speed of the attached LAN.
+
+ New implementations should support dot1dStpPortPathCost32.
+ If the port path costs exceeds the maximum value of this
+ object then this object should report the maximum value,
+ namely 65535. Applications should try to read the
+ dot1dStpPortPathCost32 object if this object reports
+ the maximum value."
+ REFERENCE "IEEE 802.1D-1998: clause 8.5.5.3"
+ ::= { dot1dStpPortEntry 5 }
+
+dot1dStpPortDesignatedRoot OBJECT-TYPE
+ SYNTAX BridgeId
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The unique Bridge Identifier of the Bridge
+ recorded as the Root in the Configuration BPDUs
+ transmitted by the Designated Bridge for the
+ segment to which the port is attached."
+ REFERENCE
+ "IEEE 802.1D-1998: clause 8.5.5.4"
+ ::= { dot1dStpPortEntry 6 }
+
+dot1dStpPortDesignatedCost OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The path cost of the Designated Port of the segment
+ connected to this port. This value is compared to the
+ Root Path Cost field in received bridge PDUs."
+ REFERENCE
+ "IEEE 802.1D-1998: clause 8.5.5.5"
+ ::= { dot1dStpPortEntry 7 }
+
+dot1dStpPortDesignatedBridge OBJECT-TYPE
+ SYNTAX BridgeId
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The Bridge Identifier of the bridge that this
+ port considers to be the Designated Bridge for
+ this port's segment."
+ REFERENCE
+ "IEEE 802.1D-1998: clause 8.5.5.6"
+ ::= { dot1dStpPortEntry 8 }
+
+dot1dStpPortDesignatedPort OBJECT-TYPE
+ SYNTAX OCTET STRING (SIZE (2))
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The Port Identifier of the port on the Designated
+ Bridge for this port's segment."
+ REFERENCE
+ "IEEE 802.1D-1998: clause 8.5.5.7"
+ ::= { dot1dStpPortEntry 9 }
+
+dot1dStpPortForwardTransitions OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of times this port has transitioned
+ from the Learning state to the Forwarding state."
+ ::= { dot1dStpPortEntry 10 }
+
+dot1dStpPortPathCost32 OBJECT-TYPE
+ SYNTAX Integer32 (1..200000000)
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The contribution of this port to the path cost of
+ paths towards the spanning tree root which include
+ this port. 802.1D-1998 recommends that the default
+ value of this parameter be in inverse proportion to
+ the speed of the attached LAN.
+
+ This object replaces dot1dStpPortPathCost to support
+ IEEE 802.1t."
+ REFERENCE
+ "IEEE 802.1t clause 8.10.2, Table 8-5."
+ ::= { dot1dStpPortEntry 11 }
+
+-- ---------------------------------------------------------- --
+-- the dot1dTp subtree
+-- ---------------------------------------------------------- --
+-- Implementation of the dot1dTp subtree is optional. It is
+-- implemented by those bridges that support the transparent
+-- bridging mode. A transparent or SRT bridge will implement
+-- this subtree.
+-- ---------------------------------------------------------- --
+
+dot1dTpLearnedEntryDiscards OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The total number of Forwarding Database entries that
+ have been or would have been learned, but have been
+ discarded due to a lack of storage space in the
+ Forwarding Database. If this counter is increasing, it
+ indicates that the Forwarding Database is regularly
+ becoming full (a condition that has unpleasant
+ performance effects on the subnetwork). If this counter
+ has a significant value but is not presently increasing,
+ it indicates that the problem has been occurring but is
+ not persistent."
+ REFERENCE
+ "IEEE 802.1D-1998: clause 14.7.1.1.3"
+ ::= { dot1dTp 1 }
+
+dot1dTpAgingTime OBJECT-TYPE
+ SYNTAX Integer32 (10..1000000)
+ UNITS "seconds"
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The timeout period in seconds for aging out
+ dynamically-learned forwarding information.
+ 802.1D-1998 recommends a default of 300 seconds."
+ REFERENCE
+ "IEEE 802.1D-1998: clause 14.7.1.1.3"
+ ::= { dot1dTp 2 }
+
+
+-- ---------------------------------------------------------- --
+-- The Forwarding Database for Transparent Bridges
+-- ---------------------------------------------------------- --
+
+dot1dTpFdbTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF Dot1dTpFdbEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A table that contains information about unicast
+ entries for which the bridge has forwarding and/or
+ filtering information. This information is used
+ by the transparent bridging function in
+ determining how to propagate a received frame."
+ ::= { dot1dTp 3 }
+
+dot1dTpFdbEntry OBJECT-TYPE
+ SYNTAX Dot1dTpFdbEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Information about a specific unicast MAC address
+ for which the bridge has some forwarding and/or
+ filtering information."
+ INDEX { dot1dTpFdbAddress }
+ ::= { dot1dTpFdbTable 1 }
+
+Dot1dTpFdbEntry ::=
+ SEQUENCE {
+ dot1dTpFdbAddress
+ MacAddress,
+ dot1dTpFdbPort
+ Integer32,
+ dot1dTpFdbStatus
+ INTEGER
+ }
+
+dot1dTpFdbAddress OBJECT-TYPE
+ SYNTAX MacAddress
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "A unicast MAC address for which the bridge has
+ forwarding and/or filtering information."
+ REFERENCE
+ "IEEE 802.1D-1998: clause 7.9.1, 7.9.2"
+ ::= { dot1dTpFdbEntry 1 }
+
+dot1dTpFdbPort OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Either the value '0', or the port number of the port on
+ which a frame having a source address equal to the value
+ of the corresponding instance of dot1dTpFdbAddress has
+ been seen. A value of '0' indicates that the port
+ number has not been learned, but that the bridge does
+ have some forwarding/filtering information about this
+ address (e.g., in the dot1dStaticTable). Implementors
+ are encouraged to assign the port value to this object
+ whenever it is learned, even for addresses for which the
+ corresponding value of dot1dTpFdbStatus is not
+ learned(3)."
+ ::= { dot1dTpFdbEntry 2 }
+dot1dTpFdbStatus OBJECT-TYPE
+ SYNTAX INTEGER {
+ other(1),
+ invalid(2),
+ learned(3),
+ self(4),
+ mgmt(5)
+ }
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The status of this entry. The meanings of the
+ values are:
+ other(1) - none of the following. This would
+ include the case where some other MIB object
+ (not the corresponding instance of
+ dot1dTpFdbPort, nor an entry in the
+ dot1dStaticTable) is being used to determine if
+ and how frames addressed to the value of the
+ corresponding instance of dot1dTpFdbAddress are
+ being forwarded.
+ invalid(2) - this entry is no longer valid (e.g.,
+ it was learned but has since aged out), but has
+ not yet been flushed from the table.
+ learned(3) - the value of the corresponding instance
+ of dot1dTpFdbPort was learned, and is being
+ used.
+ self(4) - the value of the corresponding instance of
+ dot1dTpFdbAddress represents one of the bridge's
+ addresses. The corresponding instance of
+ dot1dTpFdbPort indicates which of the bridge's
+ ports has this address.
+ mgmt(5) - the value of the corresponding instance of
+ dot1dTpFdbAddress is also the value of an
+ existing instance of dot1dStaticAddress."
+ ::= { dot1dTpFdbEntry 3 }
+
+-- ---------------------------------------------------------- --
+-- Port Table for Transparent Bridges
+-- ---------------------------------------------------------- --
+
+dot1dTpPortTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF Dot1dTpPortEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A table that contains information about every port that
+ is associated with this transparent bridge."
+ ::= { dot1dTp 4 }
+
+dot1dTpPortEntry OBJECT-TYPE
+ SYNTAX Dot1dTpPortEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A list of information for each port of a transparent
+ bridge."
+ INDEX { dot1dTpPort }
+ ::= { dot1dTpPortTable 1 }
+
+Dot1dTpPortEntry ::=
+ SEQUENCE {
+ dot1dTpPort
+ Integer32,
+ dot1dTpPortMaxInfo
+ Integer32,
+ dot1dTpPortInFrames
+ Counter32,
+ dot1dTpPortOutFrames
+ Counter32,
+ dot1dTpPortInDiscards
+ Counter32
+ }
+
+dot1dTpPort OBJECT-TYPE
+ SYNTAX Integer32 (1..65535)
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The port number of the port for which this entry
+ contains Transparent bridging management information."
+ ::= { dot1dTpPortEntry 1 }
+
+-- It would be nice if we could use ifMtu as the size of the
+-- largest INFO field, but we can't because ifMtu is defined
+-- to be the size that the (inter-)network layer can use, which
+-- can differ from the MAC layer (especially if several layers
+-- of encapsulation are used).
+
+dot1dTpPortMaxInfo OBJECT-TYPE
+ SYNTAX Integer32
+ UNITS "bytes"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The maximum size of the INFO (non-MAC) field that
+ this port will receive or transmit."
+ ::= { dot1dTpPortEntry 2 }
+
+dot1dTpPortInFrames OBJECT-TYPE
+ SYNTAX Counter32
+ UNITS "frames"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of frames that have been received by this
+ port from its segment. Note that a frame received on the
+ interface corresponding to this port is only counted by
+ this object if and only if it is for a protocol being
+ processed by the local bridging function, including
+ bridge management frames."
+ REFERENCE
+ "IEEE 802.1D-1998: clause 14.6.1.1.3"
+ ::= { dot1dTpPortEntry 3 }
+
+dot1dTpPortOutFrames OBJECT-TYPE
+ SYNTAX Counter32
+ UNITS "frames"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of frames that have been transmitted by this
+ port to its segment. Note that a frame transmitted on
+ the interface corresponding to this port is only counted
+ by this object if and only if it is for a protocol being
+ processed by the local bridging function, including
+ bridge management frames."
+ REFERENCE
+ "IEEE 802.1D-1998: clause 14.6.1.1.3"
+ ::= { dot1dTpPortEntry 4 }
+
+dot1dTpPortInDiscards OBJECT-TYPE
+ SYNTAX Counter32
+ UNITS "frames"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Count of received valid frames that were discarded
+ (i.e., filtered) by the Forwarding Process."
+ REFERENCE
+ "IEEE 802.1D-1998: clause 14.6.1.1.3"
+ ::= { dot1dTpPortEntry 5 }
+
+-- ---------------------------------------------------------- --
+-- The Static (Destination-Address Filtering) Database
+-- ---------------------------------------------------------- --
+-- Implementation of this subtree is optional.
+-- ---------------------------------------------------------- --
+
+dot1dStaticTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF Dot1dStaticEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A table containing filtering information configured
+ into the bridge by (local or network) management
+ specifying the set of ports to which frames received
+ from specific ports and containing specific destination
+ addresses are allowed to be forwarded. The value of
+ zero in this table, as the port number from which frames
+ with a specific destination address are received, is
+ used to specify all ports for which there is no specific
+ entry in this table for that particular destination
+ address. Entries are valid for unicast and for
+ group/broadcast addresses."
+ REFERENCE
+ "IEEE 802.1D-1998: clause 14.7.2"
+ ::= { dot1dStatic 1 }
+
+dot1dStaticEntry OBJECT-TYPE
+ SYNTAX Dot1dStaticEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Filtering information configured into the bridge by
+ (local or network) management specifying the set of
+ ports to which frames received from a specific port and
+ containing a specific destination address are allowed to
+ be forwarded."
+ REFERENCE
+ "IEEE 802.1D-1998: clause 14.7.2"
+ INDEX { dot1dStaticAddress, dot1dStaticReceivePort }
+ ::= { dot1dStaticTable 1 }
+
+Dot1dStaticEntry ::=
+ SEQUENCE {
+ dot1dStaticAddress MacAddress,
+ dot1dStaticReceivePort Integer32,
+ dot1dStaticAllowedToGoTo OCTET STRING,
+ dot1dStaticStatus INTEGER
+ }
+
+dot1dStaticAddress OBJECT-TYPE
+ SYNTAX MacAddress
+ MAX-ACCESS read-create
+ STATUS current
+ DESCRIPTION
+ "The destination MAC address in a frame to which this
+ entry's filtering information applies. This object can
+ take the value of a unicast address, a group address, or
+ the broadcast address."
+ REFERENCE
+ "IEEE 802.1D-1998: clause 7.9.1, 7.9.2"
+ ::= { dot1dStaticEntry 1 }
+
+dot1dStaticReceivePort OBJECT-TYPE
+ SYNTAX Integer32 (0..65535)
+ MAX-ACCESS read-create
+ STATUS current
+ DESCRIPTION
+ "Either the value '0', or the port number of the port
+ from which a frame must be received in order for this
+ entry's filtering information to apply. A value of zero
+ indicates that this entry applies on all ports of the
+ bridge for which there is no other applicable entry."
+ ::= { dot1dStaticEntry 2 }
+
+dot1dStaticAllowedToGoTo OBJECT-TYPE
+ SYNTAX OCTET STRING (SIZE (0..512))
+ MAX-ACCESS read-create
+ STATUS current
+ DESCRIPTION
+ "The set of ports to which frames received from a
+ specific port and destined for a specific MAC address,
+ are allowed to be forwarded. Each octet within the
+ value of this object specifies a set of eight ports,
+ with the first octet specifying ports 1 through 8, the
+ second octet specifying ports 9 through 16, etc. Within
+ each octet, the most significant bit represents the
+ lowest numbered port, and the least significant bit
+ represents the highest numbered port. Thus, each port
+ of the bridge is represented by a single bit within the
+ value of this object. If that bit has a value of '1',
+ then that port is included in the set of ports; the port
+ is not included if its bit has a value of '0'. (Note
+ that the setting of the bit corresponding to the port
+ from which a frame is received is irrelevant.) The
+ default value of this object is a string of ones of
+ appropriate length.
+
+ The value of this object may exceed the required minimum
+ maximum message size of some SNMP transport (484 bytes,
+ in the case of SNMP over UDP, see RFC 3417, section 3.2).
+ SNMP engines on bridges supporting a large number of
+ ports must support appropriate maximum message sizes."
+ ::= { dot1dStaticEntry 3 }
+
+dot1dStaticStatus OBJECT-TYPE
+ SYNTAX INTEGER {
+ other(1),
+ invalid(2),
+ permanent(3),
+ deleteOnReset(4),
+ deleteOnTimeout(5)
+ }
+ MAX-ACCESS read-create
+ STATUS current
+ DESCRIPTION
+ "This object indicates the status of this entry.
+ The default value is permanent(3).
+ other(1) - this entry is currently in use but the
+ conditions under which it will remain so are
+ different from each of the following values.
+ invalid(2) - writing this value to the object
+ removes the corresponding entry.
+ permanent(3) - this entry is currently in use and
+ will remain so after the next reset of the
+ bridge.
+ deleteOnReset(4) - this entry is currently in use
+ and will remain so until the next reset of the
+ bridge.
+ deleteOnTimeout(5) - this entry is currently in use
+ and will remain so until it is aged out."
+ ::= { dot1dStaticEntry 4 }
+
+-- ---------------------------------------------------------- --
+-- Notifications for use by Bridges
+-- ---------------------------------------------------------- --
+-- Notifications for the Spanning Tree Protocol
+-- ---------------------------------------------------------- --
+
+newRoot NOTIFICATION-TYPE
+ -- OBJECTS { }
+ STATUS current
+ DESCRIPTION
+ "The newRoot trap indicates that the sending agent has
+ become the new root of the Spanning Tree; the trap is
+ sent by a bridge soon after its election as the new
+ root, e.g., upon expiration of the Topology Change Timer,
+ immediately subsequent to its election. Implementation
+ of this trap is optional."
+ ::= { dot1dNotifications 1 }
+
+topologyChange NOTIFICATION-TYPE
+ -- OBJECTS { }
+ STATUS current
+ DESCRIPTION
+ "A topologyChange trap is sent by a bridge when any of
+ its configured ports transitions from the Learning state
+ to the Forwarding state, or from the Forwarding state to
+ the Blocking state. The trap is not sent if a newRoot
+ trap is sent for the same transition. Implementation of
+ this trap is optional."
+ ::= { dot1dNotifications 2 }
+
+-- ---------------------------------------------------------- --
+-- IEEE 802.1D MIB - Conformance Information
+-- ---------------------------------------------------------- --
+
+dot1dGroups OBJECT IDENTIFIER ::= { dot1dConformance 1 }
+dot1dCompliances OBJECT IDENTIFIER ::= { dot1dConformance 2 }
+
+-- ---------------------------------------------------------- --
+-- units of conformance
+-- ---------------------------------------------------------- --
+
+-- ---------------------------------------------------------- --
+-- the dot1dBase group
+-- ---------------------------------------------------------- --
+
+dot1dBaseBridgeGroup OBJECT-GROUP
+ OBJECTS {
+ dot1dBaseBridgeAddress,
+ dot1dBaseNumPorts,
+ dot1dBaseType
+ }
+ STATUS current
+ DESCRIPTION
+ "Bridge level information for this device."
+ ::= { dot1dGroups 1 }
+
+dot1dBasePortGroup OBJECT-GROUP
+ OBJECTS {
+ dot1dBasePort,
+ dot1dBasePortIfIndex,
+ dot1dBasePortCircuit,
+ dot1dBasePortDelayExceededDiscards,
+ dot1dBasePortMtuExceededDiscards
+ }
+ STATUS current
+ DESCRIPTION
+ "Information for each port on this device."
+ ::= { dot1dGroups 2 }
+
+-- ---------------------------------------------------------- --
+-- the dot1dStp group
+-- ---------------------------------------------------------- --
+
+dot1dStpBridgeGroup OBJECT-GROUP
+ OBJECTS {
+ dot1dStpProtocolSpecification,
+ dot1dStpPriority,
+ dot1dStpTimeSinceTopologyChange,
+ dot1dStpTopChanges,
+ dot1dStpDesignatedRoot,
+ dot1dStpRootCost,
+ dot1dStpRootPort,
+ dot1dStpMaxAge,
+ dot1dStpHelloTime,
+ dot1dStpHoldTime,
+ dot1dStpForwardDelay,
+ dot1dStpBridgeMaxAge,
+ dot1dStpBridgeHelloTime,
+ dot1dStpBridgeForwardDelay
+ }
+ STATUS current
+ DESCRIPTION
+ "Bridge level Spanning Tree data for this device."
+ ::= { dot1dGroups 3 }
+
+dot1dStpPortGroup OBJECT-GROUP
+ OBJECTS {
+ dot1dStpPort,
+ dot1dStpPortPriority,
+ dot1dStpPortState,
+ dot1dStpPortEnable,
+ dot1dStpPortPathCost,
+ dot1dStpPortDesignatedRoot,
+ dot1dStpPortDesignatedCost,
+ dot1dStpPortDesignatedBridge,
+ dot1dStpPortDesignatedPort,
+ dot1dStpPortForwardTransitions
+ }
+ STATUS current
+ DESCRIPTION
+ "Spanning Tree data for each port on this device."
+ ::= { dot1dGroups 4 }
+
+dot1dStpPortGroup2 OBJECT-GROUP
+ OBJECTS {
+ dot1dStpPort,
+ dot1dStpPortPriority,
+ dot1dStpPortState,
+ dot1dStpPortEnable,
+ dot1dStpPortDesignatedRoot,
+ dot1dStpPortDesignatedCost,
+ dot1dStpPortDesignatedBridge,
+ dot1dStpPortDesignatedPort,
+ dot1dStpPortForwardTransitions,
+ dot1dStpPortPathCost32
+ }
+ STATUS current
+ DESCRIPTION
+ "Spanning Tree data for each port on this device."
+ ::= { dot1dGroups 5 }
+
+dot1dStpPortGroup3 OBJECT-GROUP
+ OBJECTS {
+ dot1dStpPortPathCost32
+ }
+ STATUS current
+ DESCRIPTION
+ "Spanning Tree data for devices supporting 32-bit
+ path costs."
+ ::= { dot1dGroups 6 }
+
+-- ---------------------------------------------------------- --
+-- the dot1dTp group
+-- ---------------------------------------------------------- --
+
+dot1dTpBridgeGroup OBJECT-GROUP
+ OBJECTS {
+ dot1dTpLearnedEntryDiscards,
+ dot1dTpAgingTime
+ }
+ STATUS current
+ DESCRIPTION
+ "Bridge level Transparent Bridging data."
+ ::= { dot1dGroups 7 }
+
+dot1dTpFdbGroup OBJECT-GROUP
+ OBJECTS {
+ dot1dTpFdbAddress,
+ dot1dTpFdbPort,
+ dot1dTpFdbStatus
+ }
+
+ STATUS current
+ DESCRIPTION
+ "Filtering Database information for the Bridge."
+ ::= { dot1dGroups 8 }
+
+dot1dTpGroup OBJECT-GROUP
+ OBJECTS {
+ dot1dTpPort,
+ dot1dTpPortMaxInfo,
+ dot1dTpPortInFrames,
+ dot1dTpPortOutFrames,
+ dot1dTpPortInDiscards
+ }
+ STATUS current
+ DESCRIPTION
+ "Dynamic Filtering Database information for each port of
+ the Bridge."
+ ::= { dot1dGroups 9 }
+
+-- ---------------------------------------------------------- --
+-- The Static (Destination-Address Filtering) Database
+-- ---------------------------------------------------------- --
+
+dot1dStaticGroup OBJECT-GROUP
+ OBJECTS {
+ dot1dStaticAddress,
+ dot1dStaticReceivePort,
+ dot1dStaticAllowedToGoTo,
+ dot1dStaticStatus
+ }
+ STATUS current
+ DESCRIPTION
+ "Static Filtering Database information for each port of
+ the Bridge."
+ ::= { dot1dGroups 10 }
+
+-- ---------------------------------------------------------- --
+-- The Trap Notification Group
+-- ---------------------------------------------------------- --
+
+dot1dNotificationGroup NOTIFICATION-GROUP
+ NOTIFICATIONS {
+ newRoot,
+ topologyChange
+ }
+ STATUS current
+ DESCRIPTION
+ "Group of objects describing notifications (traps)."
+ ::= { dot1dGroups 11 }
+
+-- ---------------------------------------------------------- --
+-- compliance statements
+-- ---------------------------------------------------------- --
+
+bridgeCompliance1493 MODULE-COMPLIANCE
+ STATUS current
+ DESCRIPTION
+ "The compliance statement for device support of bridging
+ services, as per RFC1493."
+
+ MODULE
+ MANDATORY-GROUPS {
+ dot1dBaseBridgeGroup,
+ dot1dBasePortGroup
+ }
+
+ GROUP dot1dStpBridgeGroup
+ DESCRIPTION
+ "Implementation of this group is mandatory for bridges
+ that support the Spanning Tree Protocol."
+
+ GROUP dot1dStpPortGroup
+ DESCRIPTION
+ "Implementation of this group is mandatory for bridges
+ that support the Spanning Tree Protocol."
+
+ GROUP dot1dTpBridgeGroup
+ DESCRIPTION
+ "Implementation of this group is mandatory for bridges
+ that support the transparent bridging mode. A
+ transparent or SRT bridge will implement this group."
+
+ GROUP dot1dTpFdbGroup
+ DESCRIPTION
+ "Implementation of this group is mandatory for bridges
+ that support the transparent bridging mode. A
+ transparent or SRT bridge will implement this group."
+
+ GROUP dot1dTpGroup
+ DESCRIPTION
+ "Implementation of this group is mandatory for bridges
+ that support the transparent bridging mode. A
+ transparent or SRT bridge will implement this group."
+
+ GROUP dot1dStaticGroup
+ DESCRIPTION
+ "Implementation of this group is optional."
+
+ GROUP dot1dNotificationGroup
+ DESCRIPTION
+ "Implementation of this group is optional."
+ ::= { dot1dCompliances 1 }
+
+bridgeCompliance4188 MODULE-COMPLIANCE
+ STATUS current
+ DESCRIPTION
+ "The compliance statement for device support of bridging
+ services. This supports 32-bit Path Cost values and the
+ more restricted bridge and port priorities, as per IEEE
+ 802.1t.
+
+ Full support for the 802.1D management objects requires that
+ the SNMPv2-MIB [RFC3418] objects sysDescr, and sysUpTime, as
+ well as the IF-MIB [RFC2863] objects ifIndex, ifType,
+ ifDescr, ifPhysAddress, and ifLastChange are implemented."
+
+ MODULE
+ MANDATORY-GROUPS {
+ dot1dBaseBridgeGroup,
+ dot1dBasePortGroup
+ }
+
+ GROUP dot1dStpBridgeGroup
+ DESCRIPTION
+ "Implementation of this group is mandatory for
+ bridges that support the Spanning Tree Protocol."
+
+ OBJECT dot1dStpPriority
+ SYNTAX Integer32 (0|4096|8192|12288|16384|20480|24576
+ |28672|32768|36864|40960|45056|49152
+ |53248|57344|61440)
+ DESCRIPTION
+ "The possible values defined by IEEE 802.1t."
+
+ GROUP dot1dStpPortGroup2
+ DESCRIPTION
+ "Implementation of this group is mandatory for
+ bridges that support the Spanning Tree Protocol."
+
+ GROUP dot1dStpPortGroup3
+ DESCRIPTION
+ "Implementation of this group is mandatory for bridges
+ that support the Spanning Tree Protocol and 32-bit path
+ costs. In particular, this includes devices supporting
+ IEEE 802.1t and IEEE 802.1w."
+
+ OBJECT dot1dStpPortPriority
+ SYNTAX Integer32 (0|16|32|48|64|80|96|112|128
+ |144|160|176|192|208|224|240)
+ DESCRIPTION
+ "The possible values defined by IEEE 802.1t."
+
+ GROUP dot1dTpBridgeGroup
+ DESCRIPTION
+ "Implementation of this group is mandatory for
+ bridges that support the transparent bridging
+ mode. A transparent or SRT bridge will implement
+ this group."
+
+ GROUP dot1dTpFdbGroup
+ DESCRIPTION
+ "Implementation of this group is mandatory for
+ bridges that support the transparent bridging
+ mode. A transparent or SRT bridge will implement
+ this group."
+
+ GROUP dot1dTpGroup
+ DESCRIPTION
+ "Implementation of this group is mandatory for
+ bridges that support the transparent bridging
+ mode. A transparent or SRT bridge will implement
+ this group."
+
+ GROUP dot1dStaticGroup
+ DESCRIPTION
+ "Implementation of this group is optional."
+
+ GROUP dot1dNotificationGroup
+ DESCRIPTION
+ "Implementation of this group is optional."
+
+ ::= { dot1dCompliances 2 }
+
+END
diff --git a/usr.sbin/bsnmpd/modules/snmp_bridge/Makefile b/usr.sbin/bsnmpd/modules/snmp_bridge/Makefile
new file mode 100644
index 0000000..5d0afec
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_bridge/Makefile
@@ -0,0 +1,19 @@
+#
+# $FreeBSD$
+#
+
+MOD= bridge
+SRCS= bridge_snmp.c bridge_if.c bridge_port.c bridge_addrs.c \
+ bridge_pf.c bridge_sys.c
+CFLAGS+= -DSNMPTREE_TYPES
+
+XSYM= dot1dBridge newRoot topologyChange begemotBridgeNewRoot \
+ begemotBridgeTopologyChange begemotBridgeBaseName
+
+MAN= snmp_bridge.3
+
+BMIBS= BRIDGE-MIB.txt BEGEMOT-BRIDGE-MIB.txt RSTP-MIB.txt
+DEFS= ${MOD}_tree.def
+INCS= ${MOD}_snmp.h
+
+.include <bsd.snmpmod.mk>
diff --git a/usr.sbin/bsnmpd/modules/snmp_bridge/RSTP-MIB.txt b/usr.sbin/bsnmpd/modules/snmp_bridge/RSTP-MIB.txt
new file mode 100644
index 0000000..ea6648e
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_bridge/RSTP-MIB.txt
@@ -0,0 +1,325 @@
+--
+-- Copyright (C) The Internet Society (2005).
+--
+-- This document is subject to the rights, licenses and restrictions
+-- contained in BCP 78, and except as set forth therein, the authors
+-- retain all their rights.
+--
+-- This document and the information contained herein are provided on an
+-- "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
+-- OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
+-- ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
+-- INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
+-- INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
+-- WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
+--
+-- $FreeBSD$
+--
+
+RSTP-MIB DEFINITIONS ::= BEGIN
+
+-- -------------------------------------------------------------
+-- MIB for IEEE 802.1w Rapid Spanning Tree Protocol
+-- -------------------------------------------------------------
+
+IMPORTS
+ MODULE-IDENTITY, OBJECT-TYPE, Integer32, mib-2
+ FROM SNMPv2-SMI
+ TruthValue
+ FROM SNMPv2-TC
+ MODULE-COMPLIANCE, OBJECT-GROUP
+ FROM SNMPv2-CONF
+ dot1dStp, dot1dStpPortEntry
+ FROM BRIDGE-MIB;
+
+rstpMIB MODULE-IDENTITY
+ LAST-UPDATED "200512070000Z"
+ ORGANIZATION "IETF Bridge MIB Working Group"
+ CONTACT-INFO
+ "Email: Bridge-mib@ietf.org"
+ DESCRIPTION
+ "The Bridge MIB Extension module for managing devices
+ that support the Rapid Spanning Tree Protocol defined
+ by IEEE 802.1w.
+
+ Copyright (C) The Internet Society (2005). This version of
+ this MIB module is part of RFC 4318; See the RFC itself for
+ full legal notices."
+
+ REVISION "200512070000Z"
+ DESCRIPTION
+ "The initial version of this MIB module as published in
+ RFC 4318."
+ ::= { mib-2 134 }
+
+-- ---------------------------------------------------------- --
+-- subtrees in the RSTP-MIB
+-- ---------------------------------------------------------- --
+
+rstpNotifications OBJECT IDENTIFIER ::= { rstpMIB 0 }
+rstpObjects OBJECT IDENTIFIER ::= { rstpMIB 1 }
+rstpConformance OBJECT IDENTIFIER ::= { rstpMIB 2 }
+
+-- -------------------------------------------------------------
+-- Addition to the dot1dStp group
+-- -------------------------------------------------------------
+
+dot1dStpVersion OBJECT-TYPE
+ SYNTAX INTEGER {
+ stpCompatible(0),
+ rstp(2)
+ }
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The version of Spanning Tree Protocol the bridge is
+ currently running. The value 'stpCompatible(0)'
+ indicates the Spanning Tree Protocol specified in
+ IEEE 802.1D-1998 and 'rstp(2)' indicates the Rapid
+ Spanning Tree Protocol specified in IEEE 802.1w and
+ clause 17 of 802.1D-2004. The values are directly from
+ the IEEE standard. New values may be defined as future
+ versions of the protocol become available.
+
+ The value of this object MUST be retained across
+ reinitializations of the management system."
+ REFERENCE
+ "IEEE 802.1w clause 14.8.1, 17.12, 17.16.1"
+ DEFVAL { rstp }
+ ::= { dot1dStp 16 }
+
+dot1dStpTxHoldCount OBJECT-TYPE
+ SYNTAX Integer32 (1..10)
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The value used by the Port Transmit state machine to limit
+ the maximum transmission rate.
+
+ The value of this object MUST be retained across
+ reinitializations of the management system."
+
+ REFERENCE
+ "IEEE 802.1w clause 17.16.6"
+ DEFVAL { 3 }
+ ::= { dot1dStp 17 }
+
+--
+-- { dot1dStp 18 } was used to represent dot1dStpPathCostDefault
+-- in an earlier version of this MIB. It has since been
+-- obsoleted, and should not be used.
+--
+
+dot1dStpExtPortTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF Dot1dStpExtPortEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A table that contains port-specific Rapid Spanning Tree
+ information."
+ ::= { dot1dStp 19 }
+
+dot1dStpExtPortEntry OBJECT-TYPE
+ SYNTAX Dot1dStpExtPortEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A list of Rapid Spanning Tree information maintained by
+ each port."
+ AUGMENTS { dot1dStpPortEntry }
+ ::= { dot1dStpExtPortTable 1 }
+
+Dot1dStpExtPortEntry ::=
+ SEQUENCE {
+ dot1dStpPortProtocolMigration
+ TruthValue,
+ dot1dStpPortAdminEdgePort
+ TruthValue,
+ dot1dStpPortOperEdgePort
+ TruthValue,
+ dot1dStpPortAdminPointToPoint
+ INTEGER,
+ dot1dStpPortOperPointToPoint
+ TruthValue,
+ dot1dStpPortAdminPathCost
+ Integer32
+ }
+
+dot1dStpPortProtocolMigration OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "When operating in RSTP (version 2) mode, writing true(1)
+ to this object forces this port to transmit RSTP BPDUs.
+ Any other operation on this object has no effect and
+ it always returns false(2) when read."
+ REFERENCE
+ "IEEE 802.1w clause 14.8.2.4, 17.18.10, 17.26"
+ ::= { dot1dStpExtPortEntry 1 }
+
+dot1dStpPortAdminEdgePort OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The administrative value of the Edge Port parameter. A
+ value of true(1) indicates that this port should be
+ assumed as an edge-port, and a value of false(2) indicates
+ that this port should be assumed as a non-edge-port.
+ Setting this object will also cause the corresponding
+ instance of dot1dStpPortOperEdgePort to change to the
+ same value. Note that even when this object's value
+ is true, the value of the corresponding instance of
+ dot1dStpPortOperEdgePort can be false if a BPDU has
+ been received.
+
+ The value of this object MUST be retained across
+ reinitializations of the management system."
+
+ REFERENCE
+ "IEEE 802.1t clause 14.8.2, 18.3.3"
+ ::= { dot1dStpExtPortEntry 2 }
+
+dot1dStpPortOperEdgePort OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The operational value of the Edge Port parameter. The
+ object is initialized to the value of the corresponding
+ instance of dot1dStpPortAdminEdgePort. When the
+ corresponding instance of dot1dStpPortAdminEdgePort is
+ set, this object will be changed as well. This object
+ will also be changed to false on reception of a BPDU."
+
+ REFERENCE
+ "IEEE 802.1t clause 14.8.2, 18.3.4"
+ ::= { dot1dStpExtPortEntry 3 }
+
+dot1dStpPortAdminPointToPoint OBJECT-TYPE
+ SYNTAX INTEGER {
+ forceTrue(0),
+ forceFalse(1),
+ auto(2)
+ }
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The administrative point-to-point status of the LAN segment
+ attached to this port, using the enumeration values of the
+ IEEE 802.1w clause. A value of forceTrue(0) indicates
+ that this port should always be treated as if it is
+ connected to a point-to-point link. A value of
+ forceFalse(1) indicates that this port should be treated as
+ having a shared media connection. A value of auto(2)
+ indicates that this port is considered to have a
+ point-to-point link if it is an Aggregator and all of its
+ members are aggregatable, or if the MAC entity
+ is configured for full duplex operation, either through
+ auto-negotiation or by management means. Manipulating this
+ object changes the underlying adminPortToPortMAC.
+
+ The value of this object MUST be retained across
+ reinitializations of the management system."
+
+ REFERENCE
+ "IEEE 802.1w clause 6.4.3, 6.5, 14.8.2"
+ ::= { dot1dStpExtPortEntry 4 }
+
+dot1dStpPortOperPointToPoint OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The operational point-to-point status of the LAN segment
+ attached to this port. It indicates whether a port is
+ considered to have a point-to-point connection.
+ If adminPointToPointMAC is set to auto(2), then the value
+ of operPointToPointMAC is determined in accordance with the
+ specific procedures defined for the MAC entity concerned,
+ as defined in IEEE 802.1w, clause 6.5. The value is
+ determined dynamically; that is, it is re-evaluated whenever
+ the value of adminPointToPointMAC changes, and whenever
+ the specific procedures defined for the MAC entity evaluate
+ a change in its point-to-point status."
+ REFERENCE
+ "IEEE 802.1w clause 6.4.3, 6.5, 14.8.2"
+ ::= { dot1dStpExtPortEntry 5 }
+
+dot1dStpPortAdminPathCost OBJECT-TYPE
+ SYNTAX Integer32 (0..200000000)
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The administratively assigned value for the contribution
+ of this port to the path cost of paths toward the spanning
+ tree root.
+
+ Writing a value of '0' assigns the automatically calculated
+ default Path Cost value to the port. If the default Path
+ Cost is being used, this object returns '0' when read.
+
+ This complements the object dot1dStpPortPathCost or
+ dot1dStpPortPathCost32, which returns the operational value
+ of the path cost.
+
+ The value of this object MUST be retained across
+ reinitializations of the management system."
+ REFERENCE
+ "IEEE 802.1D-1998: Section 8.5.5.3"
+ ::= { dot1dStpExtPortEntry 6 }
+
+-- -------------------------------------------------------------
+-- rstpMIB - Conformance Information
+-- -------------------------------------------------------------
+
+rstpGroups OBJECT IDENTIFIER ::= { rstpConformance 1 }
+
+rstpCompliances OBJECT IDENTIFIER ::= { rstpConformance 2 }
+
+-- -------------------------------------------------------------
+-- Units of conformance
+-- -------------------------------------------------------------
+
+rstpBridgeGroup OBJECT-GROUP
+ OBJECTS {
+ dot1dStpVersion,
+ dot1dStpTxHoldCount
+ }
+ STATUS current
+ DESCRIPTION
+ "Rapid Spanning Tree information for the bridge."
+ ::= { rstpGroups 1 }
+
+rstpPortGroup OBJECT-GROUP
+ OBJECTS {
+ dot1dStpPortProtocolMigration,
+ dot1dStpPortAdminEdgePort,
+ dot1dStpPortOperEdgePort,
+ dot1dStpPortAdminPointToPoint,
+ dot1dStpPortOperPointToPoint,
+ dot1dStpPortAdminPathCost
+ }
+ STATUS current
+ DESCRIPTION
+ "Rapid Spanning Tree information for individual ports."
+ ::= { rstpGroups 2 }
+
+-- -------------------------------------------------------------
+-- Compliance statements
+-- -------------------------------------------------------------
+
+rstpCompliance MODULE-COMPLIANCE
+ STATUS current
+ DESCRIPTION
+ "The compliance statement for device support of Rapid
+ Spanning Tree Protocol (RSTP) bridging services."
+ MODULE
+ MANDATORY-GROUPS {
+ rstpBridgeGroup,
+ rstpPortGroup
+ }
+ ::= { rstpCompliances 1 }
+
+END
diff --git a/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_addrs.c b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_addrs.c
new file mode 100644
index 0000000..781517f
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_addrs.c
@@ -0,0 +1,589 @@
+/*-
+ * Copyright (c) 2006 Shteryana Shopova <syrinx@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.
+ *
+ * Bridge MIB implementation for SNMPd.
+ * Bridge addresses.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_mib.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+
+#include <bsnmp/snmpmod.h>
+#include <bsnmp/snmp_mibII.h>
+
+#include "bridge_tree.h"
+#include "bridge_snmp.h"
+
+TAILQ_HEAD(tp_entries, tp_entry);
+
+/*
+ * Free the bridge address list.
+ */
+static void
+bridge_tpe_free(struct tp_entries *headp)
+{
+ struct tp_entry *t;
+
+ while ((t = TAILQ_FIRST(headp)) != NULL) {
+ TAILQ_REMOVE(headp, t, tp_e);
+ free(t);
+ }
+}
+
+/*
+ * Free the bridge address entries from the address list,
+ * for the specified bridge interface only.
+ */
+static void
+bridge_tpe_bif_free(struct tp_entries *headp,
+ struct bridge_if *bif)
+{
+ struct tp_entry *tp;
+
+ while (bif->f_tpa != NULL && bif->sysindex == bif->f_tpa->sysindex) {
+ tp = TAILQ_NEXT(bif->f_tpa, tp_e);
+ TAILQ_REMOVE(headp, bif->f_tpa, tp_e);
+ free(bif->f_tpa);
+ bif->f_tpa = tp;
+ }
+}
+
+/*
+ * Compare two mac addresses.
+ * m1 < m2 : -1
+ * m1 > m2 : +1
+ * m1 = m2 : 0
+ */
+static int
+bridge_compare_macs(const uint8_t *m1, const uint8_t *m2)
+{
+ int i;
+
+ for (i = 0; i < ETHER_ADDR_LEN; i++) {
+ if (m1[i] < m2[i])
+ return (-1);
+ if (m1[i] > m2[i])
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * Insert an address entry in the bridge address TAILQ starting to search
+ * for its place from the position of the first bridge address for the bridge
+ * interface. Update the first bridge address if neccessary.
+ */
+static void
+bridge_addrs_insert_at(struct tp_entries *headp,
+ struct tp_entry *ta, struct tp_entry **f_tpa)
+{
+ struct tp_entry *t1;
+
+ assert(f_tpa != NULL);
+
+ for (t1 = *f_tpa;
+ t1 != NULL && ta->sysindex == t1->sysindex;
+ t1 = TAILQ_NEXT(t1, tp_e)) {
+ if (bridge_compare_macs(ta->tp_addr, t1->tp_addr) < 0) {
+ TAILQ_INSERT_BEFORE(t1, ta, tp_e);
+ if (*f_tpa == t1)
+ (*f_tpa) = ta;
+ return;
+ }
+ }
+
+ if (t1 == NULL)
+ TAILQ_INSERT_TAIL(headp, ta, tp_e);
+ else
+ TAILQ_INSERT_BEFORE(t1, ta, tp_e);
+}
+
+/*
+ * Find an address entry's possition in the address list
+ * according to bridge interface name.
+ */
+static struct tp_entry *
+bridge_addrs_find_pos(struct tp_entries *headp, uint32_t b_idx)
+{
+ uint32_t t_idx;
+ struct tp_entry *t1;
+
+ if ((t1 = TAILQ_FIRST(headp)) == NULL ||
+ bridge_compare_sysidx(b_idx, t1->sysindex) < 0)
+ return (NULL);
+
+ t_idx = t1->sysindex;
+
+ for (t1 = TAILQ_NEXT(t1, tp_e); t1 != NULL; t1 = TAILQ_NEXT(t1, tp_e)) {
+
+ if (t1->sysindex != t_idx) {
+ if (bridge_compare_sysidx(b_idx, t1->sysindex) < 0)
+ return (TAILQ_PREV(t1, tp_entries, tp_e));
+ else
+ t_idx = t1->sysindex;
+ }
+ }
+
+ if (t1 == NULL)
+ t1 = TAILQ_LAST(headp, tp_entries);
+
+ return (t1);
+}
+
+/*
+ * Insert a bridge address in the bridge addresses list.
+ */
+static void
+bridge_addrs_bif_insert(struct tp_entries *headp, struct tp_entry *te,
+ struct tp_entry **f_tpa)
+{
+ struct tp_entry *temp;
+
+ if (*f_tpa != NULL)
+ bridge_addrs_insert_at(headp, te, f_tpa);
+ else {
+ temp = bridge_addrs_find_pos(headp, te->sysindex);
+
+ if (temp == NULL)
+ TAILQ_INSERT_HEAD(headp, te, tp_e);
+ else
+ TAILQ_INSERT_AFTER(headp, temp, te, tp_e);
+ *f_tpa = te;
+ }
+}
+
+static struct tp_entries tp_entries = TAILQ_HEAD_INITIALIZER(tp_entries);
+static time_t address_list_age;
+
+void
+bridge_addrs_update_listage(void)
+{
+ address_list_age = time(NULL);
+}
+
+void
+bridge_addrs_fini(void)
+{
+ bridge_tpe_free(&tp_entries);
+}
+
+void
+bridge_addrs_free(struct bridge_if *bif)
+{
+ bridge_tpe_bif_free(&tp_entries, bif);
+}
+
+/*
+ * Find the first address in the list.
+ */
+static struct tp_entry *
+bridge_addrs_first(void)
+{
+ return (TAILQ_FIRST(&tp_entries));
+}
+
+/*
+ * Find the next address in the list.
+ */
+static struct tp_entry *
+bridge_addrs_next(struct tp_entry *te)
+{
+ return (TAILQ_NEXT(te, tp_e));
+}
+
+/*
+ * Find the first address, learnt by the specified bridge interface.
+ */
+struct tp_entry *
+bridge_addrs_bif_first(struct bridge_if *bif)
+{
+ return (bif->f_tpa);
+}
+
+/*
+ * Find the next address, learnt by the specified bridge interface.
+ */
+struct tp_entry *
+bridge_addrs_bif_next(struct tp_entry *te)
+{
+ struct tp_entry *te_next;
+
+ if ((te_next = TAILQ_NEXT(te, tp_e)) == NULL ||
+ te_next->sysindex != te->sysindex)
+ return (NULL);
+
+ return (te_next);
+}
+
+/*
+ * Remove a bridge address from the list.
+ */
+void
+bridge_addrs_remove(struct tp_entry *te, struct bridge_if *bif)
+{
+ if (bif->f_tpa == te)
+ bif->f_tpa = bridge_addrs_bif_next(te);
+
+ TAILQ_REMOVE(&tp_entries, te, tp_e);
+ free(te);
+}
+
+/*
+ * Allocate memory for a new bridge address and insert it in the list.
+ */
+struct tp_entry *
+bridge_new_addrs(uint8_t *mac, struct bridge_if *bif)
+{
+ struct tp_entry *te;
+
+ if ((te = (struct tp_entry *) malloc(sizeof(*te))) == NULL) {
+ syslog(LOG_ERR, "bridge new address: failed: %s",
+ strerror(errno));
+ return (NULL);
+ }
+
+ bzero(te, sizeof(*te));
+
+ te->sysindex = bif->sysindex;
+ bcopy(mac, te->tp_addr, ETHER_ADDR_LEN);
+ bridge_addrs_bif_insert(&tp_entries, te, &(bif->f_tpa));
+
+ return (te);
+}
+
+/*
+ * Given a mac address, learnt on a bridge,
+ * find the corrsponding TP entry for it.
+ */
+struct tp_entry *
+bridge_addrs_find(uint8_t *mac, struct bridge_if *bif)
+{
+ struct tp_entry *te;
+
+ for (te = bif->f_tpa; te != NULL; te = TAILQ_NEXT(te, tp_e)) {
+ if (te->sysindex != bif->sysindex) {
+ te = NULL;
+ break;
+ }
+
+ if (bridge_compare_macs(te->tp_addr, mac) == 0)
+ break;
+ }
+
+ return (te);
+}
+
+void
+bridge_addrs_dump(struct bridge_if *bif)
+{
+ struct tp_entry *te;
+
+ syslog(LOG_ERR, "Addresses count - %d", bif->num_addrs);
+ for (te = bridge_addrs_bif_first(bif); te != NULL;
+ te = bridge_addrs_bif_next(te)) {
+ syslog(LOG_ERR, "address %x:%x:%x:%x:%x:%x on port %d.%d",
+ te->tp_addr[0], te->tp_addr[1], te->tp_addr[2],
+ te->tp_addr[3], te->tp_addr[4], te->tp_addr[5],
+ te->sysindex, te->port_no);
+ }
+}
+
+/*
+ * RFC4188 specifics.
+ */
+
+/*
+ * Construct the SNMP index from the address DST Mac.
+ */
+static void
+bridge_addrs_index_append(struct asn_oid *oid, uint sub,
+ const struct tp_entry *te)
+{
+ int i;
+
+ oid->len = sub + ETHER_ADDR_LEN + 1;
+ oid->subs[sub] = ETHER_ADDR_LEN;
+
+ for (i = 1; i <= ETHER_ADDR_LEN; i++)
+ oid->subs[sub + i] = te->tp_addr[i - 1];
+}
+
+/*
+ * Find the address entry for the SNMP index from the default bridge only.
+ */
+static struct tp_entry *
+bridge_addrs_get(const struct asn_oid *oid, uint sub,
+ struct bridge_if *bif)
+{
+ int i;
+ uint8_t tp_addr[ETHER_ADDR_LEN];
+
+ if (oid->len - sub != ETHER_ADDR_LEN + 1 ||
+ oid->subs[sub] != ETHER_ADDR_LEN)
+ return (NULL);
+
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ tp_addr[i] = oid->subs[sub + i + 1];
+
+ return (bridge_addrs_find(tp_addr, bif));
+}
+
+/*
+ * Find the next address entry for the SNMP index
+ * from the default bridge only.
+ */
+static struct tp_entry *
+bridge_addrs_getnext(const struct asn_oid *oid, uint sub,
+ struct bridge_if *bif)
+{
+ int i;
+ uint8_t tp_addr[ETHER_ADDR_LEN];
+ static struct tp_entry *te;
+
+ if (oid->len - sub == 0)
+ return (bridge_addrs_bif_first(bif));
+
+ if (oid->len - sub != ETHER_ADDR_LEN + 1 ||
+ oid->subs[sub] != ETHER_ADDR_LEN)
+ return (NULL);
+
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ tp_addr[i] = oid->subs[sub + i + 1];
+
+ if ((te = bridge_addrs_find(tp_addr, bif)) == NULL)
+ return (NULL);
+
+ return (bridge_addrs_bif_next(te));
+}
+
+int
+op_dot1d_tp_fdb(struct snmp_context *c __unused, struct snmp_value *val,
+ uint sub, uint iidx __unused, enum snmp_op op)
+{
+ struct bridge_if *bif;
+ struct tp_entry *te;
+
+ if ((bif = bridge_get_default()) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+
+ if (time(NULL) - bif->addrs_age > bridge_get_data_maxage() &&
+ bridge_update_addrs(bif) <= 0)
+ return (SNMP_ERR_NOSUCHNAME);
+
+ switch (op) {
+ case SNMP_OP_GET:
+ if ((te = bridge_addrs_get(&val->var, sub, bif)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_GETNEXT:
+ if ((te = bridge_addrs_getnext(&val->var, sub, bif)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ bridge_addrs_index_append(&val->var, sub, te);
+ goto get;
+
+ case SNMP_OP_SET:
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ break;
+ }
+ abort();
+
+get:
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_dot1dTpFdbAddress:
+ return (string_get(val, te->tp_addr, ETHER_ADDR_LEN));
+ case LEAF_dot1dTpFdbPort :
+ val->v.integer = te->port_no;
+ return (SNMP_ERR_NOERROR);
+ case LEAF_dot1dTpFdbStatus:
+ val->v.integer = te->status;
+ return (SNMP_ERR_NOERROR);
+ }
+
+ abort();
+}
+
+/*
+ * Private BEGEMOT-BRIDGE-MIB specifics.
+ */
+
+/*
+ * Construct the SNMP index from the bridge interface name
+ * and the address DST Mac.
+ */
+static int
+bridge_addrs_begemot_index_append(struct asn_oid *oid, uint sub,
+ const struct tp_entry *te)
+{
+ uint i, n_len;
+ const char *b_name;
+
+ if ((b_name = bridge_if_find_name(te->sysindex)) == NULL)
+ return (-1);
+
+ n_len = strlen(b_name);
+ oid->len = sub++;
+ oid->subs[oid->len++] = n_len;
+
+ for (i = 1; i <= n_len; i++)
+ oid->subs[oid->len++] = b_name[i - 1];
+
+ oid->subs[oid->len++] = ETHER_ADDR_LEN;
+ for (i = 1 ; i <= ETHER_ADDR_LEN; i++)
+ oid->subs[oid->len++] = te->tp_addr[i - 1];
+
+ return (0);
+}
+
+/*
+ * Find a bridge address entry by the bridge interface name
+ * and the address DST Mac.
+ */
+static struct tp_entry *
+bridge_addrs_begemot_get(const struct asn_oid *oid, uint sub)
+{
+ uint i, n_len;
+ uint8_t tp_addr[ETHER_ADDR_LEN];
+ char bif_name[IFNAMSIZ];
+ struct bridge_if *bif;
+
+ n_len = oid->subs[sub];
+ if (oid->len - sub != n_len + ETHER_ADDR_LEN + 3 ||
+ n_len >= IFNAMSIZ || oid->subs[sub + n_len + 1] != ETHER_ADDR_LEN)
+ return (NULL);
+
+ for (i = 0; i < n_len; i++)
+ bif_name[i] = oid->subs[n_len + i + 1];
+ bif_name[i] = '\0';
+
+ for (i = 1; i <= ETHER_ADDR_LEN; i++)
+ tp_addr[i - 1] = oid->subs[n_len + i + 1];
+
+ if ((bif = bridge_if_find_ifname(bif_name)) == NULL)
+ return (NULL);
+
+ return (bridge_addrs_find(tp_addr, bif));
+}
+
+/*
+ * Find the next bridge address entry by the bridge interface name
+ * and the address DST Mac.
+ */
+static struct tp_entry *
+bridge_addrs_begemot_getnext(const struct asn_oid *oid, uint sub)
+{
+ uint i, n_len;
+ uint8_t tp_addr[ETHER_ADDR_LEN];
+ char bif_name[IFNAMSIZ];
+ struct bridge_if *bif;
+ struct tp_entry *tp;
+
+ if (oid->len - sub == 0)
+ return (bridge_addrs_first());
+
+ n_len = oid->subs[sub];
+ if (oid->len - sub != n_len + ETHER_ADDR_LEN + 2 ||
+ n_len >= IFNAMSIZ || oid->subs[sub + n_len + 1] != ETHER_ADDR_LEN)
+ return (NULL);
+
+ for (i = 1; i <= n_len; i++)
+ bif_name[i - 1] = oid->subs[sub + i];
+
+ bif_name[i - 1] = '\0';
+
+ for (i = 1; i <= ETHER_ADDR_LEN; i++)
+ tp_addr[i - 1] = oid->subs[sub + n_len + i + 1];
+
+ if ((bif = bridge_if_find_ifname(bif_name)) == NULL ||
+ (tp = bridge_addrs_find(tp_addr, bif)) == NULL)
+ return (NULL);
+
+ return (bridge_addrs_next(tp));
+}
+
+int
+op_begemot_tp_fdb(struct snmp_context *c __unused, struct snmp_value *val,
+ uint sub, uint iidx __unused, enum snmp_op op)
+{
+ struct tp_entry *te;
+
+ if (time(NULL) - address_list_age > bridge_get_data_maxage())
+ bridge_update_all_addrs();
+
+ switch (op) {
+ case SNMP_OP_GET:
+ if ((te = bridge_addrs_begemot_get(&val->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_GETNEXT:
+ if ((te = bridge_addrs_begemot_getnext(&val->var,
+ sub)) == NULL ||
+ bridge_addrs_begemot_index_append(&val->var,
+ sub, te) < 0)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_SET:
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ break;
+ }
+ abort();
+
+get:
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_begemotBridgeTpFdbAddress:
+ return (string_get(val, te->tp_addr, ETHER_ADDR_LEN));
+ case LEAF_begemotBridgeTpFdbPort:
+ val->v.integer = te->port_no;
+ return (SNMP_ERR_NOERROR);
+ case LEAF_begemotBridgeTpFdbStatus:
+ val->v.integer = te->status;
+ return (SNMP_ERR_NOERROR);
+ }
+
+ abort();
+}
diff --git a/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_if.c b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_if.c
new file mode 100644
index 0000000..4586e32
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_if.c
@@ -0,0 +1,1479 @@
+/*-
+ * Copyright (c) 2006 Shteryana Shopova <syrinx@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.
+ *
+ * Bridge MIB implementation for SNMPd.
+ * Bridge interface objects.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_mib.h>
+#include <net/if_types.h>
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include <bsnmp/snmpmod.h>
+#include <bsnmp/snmp_mibII.h>
+
+#include "bridge_tree.h"
+#include "bridge_snmp.h"
+#include "bridge_oid.h"
+
+static const struct asn_oid oid_newRoot = OIDX_newRoot;
+static const struct asn_oid oid_TopologyChange = OIDX_topologyChange;
+static const struct asn_oid oid_begemotBrigeName = \
+ OIDX_begemotBridgeBaseName;
+static const struct asn_oid oid_begemotNewRoot = OIDX_begemotBridgeNewRoot;
+static const struct asn_oid oid_begemotTopologyChange = \
+ OIDX_begemotBridgeTopologyChange;
+
+TAILQ_HEAD(bridge_ifs, bridge_if);
+
+/*
+ * Free the bridge interface list.
+ */
+static void
+bridge_ifs_free(struct bridge_ifs *headp)
+{
+ struct bridge_if *b;
+
+ while ((b = TAILQ_FIRST(headp)) != NULL) {
+ TAILQ_REMOVE(headp, b, b_if);
+ free(b);
+ }
+}
+
+/*
+ * Insert an entry in the bridge interface TAILQ. Keep the
+ * TAILQ sorted by the bridge's interface name.
+ */
+static void
+bridge_ifs_insert(struct bridge_ifs *headp,
+ struct bridge_if *b)
+{
+ struct bridge_if *temp;
+
+ if ((temp = TAILQ_FIRST(headp)) == NULL ||
+ strcmp(b->bif_name, temp->bif_name) < 0) {
+ TAILQ_INSERT_HEAD(headp, b, b_if);
+ return;
+ }
+
+ TAILQ_FOREACH(temp, headp, b_if)
+ if(strcmp(b->bif_name, temp->bif_name) < 0)
+ TAILQ_INSERT_BEFORE(temp, b, b_if);
+
+ TAILQ_INSERT_TAIL(headp, b, b_if);
+}
+
+/* The global bridge interface list. */
+static struct bridge_ifs bridge_ifs = TAILQ_HEAD_INITIALIZER(bridge_ifs);
+static time_t bridge_list_age;
+
+/*
+ * Free the global list.
+ */
+void
+bridge_ifs_fini(void)
+{
+ bridge_ifs_free(&bridge_ifs);
+}
+
+/*
+ * Find a bridge interface entry by the bridge interface system index.
+ */
+struct bridge_if *
+bridge_if_find_ifs(uint32_t sysindex)
+{
+ struct bridge_if *b;
+
+ TAILQ_FOREACH(b, &bridge_ifs, b_if)
+ if (b->sysindex == sysindex)
+ return (b);
+
+ return (NULL);
+}
+
+/*
+ * Find a bridge interface entry by the bridge interface name.
+ */
+struct bridge_if *
+bridge_if_find_ifname(const char *b_name)
+{
+ struct bridge_if *b;
+
+ TAILQ_FOREACH(b, &bridge_ifs, b_if)
+ if (strcmp(b_name, b->bif_name) == 0)
+ return (b);
+
+ return (NULL);
+}
+
+/*
+ * Find a bridge name by the bridge interface system index.
+ */
+const char *
+bridge_if_find_name(uint32_t sysindex)
+{
+ struct bridge_if *b;
+
+ TAILQ_FOREACH(b, &bridge_ifs, b_if)
+ if (b->sysindex == sysindex)
+ return (b->bif_name);
+
+ return (NULL);
+}
+
+/*
+ * Given two bridge interfaces' system indexes, find their
+ * corresponding names and return the result of the name
+ * comparison. Returns:
+ * error : -2
+ * i1 < i2 : -1
+ * i1 > i2 : +1
+ * i1 = i2 : 0
+ */
+int
+bridge_compare_sysidx(uint32_t i1, uint32_t i2)
+{
+ int c;
+ const char *b1, *b2;
+
+ if (i1 == i2)
+ return (0);
+
+ if ((b1 = bridge_if_find_name(i1)) == NULL) {
+ syslog(LOG_ERR, "Bridge interface %d does not exist", i1);
+ return (-2);
+ }
+
+ if ((b2 = bridge_if_find_name(i2)) == NULL) {
+ syslog(LOG_ERR, "Bridge interface %d does not exist", i2);
+ return (-2);
+ }
+
+ if ((c = strcmp(b1, b2)) < 0)
+ return (-1);
+ else if (c > 0)
+ return (1);
+
+ return (0);
+}
+
+/*
+ * Fetch the first bridge interface from the list.
+ */
+struct bridge_if *
+bridge_first_bif(void)
+{
+ return (TAILQ_FIRST(&bridge_ifs));
+}
+
+/*
+ * Fetch the next bridge interface from the list.
+ */
+struct bridge_if *
+bridge_next_bif(struct bridge_if *b_pr)
+{
+ return (TAILQ_NEXT(b_pr, b_if));
+}
+
+/*
+ * Create a new entry for a bridge interface and insert
+ * it in the list.
+ */
+static struct bridge_if *
+bridge_new_bif(const char *bif_n, uint32_t sysindex, const u_char *physaddr)
+{
+ struct bridge_if *bif;
+
+ if ((bif = (struct bridge_if *) malloc(sizeof(*bif)))== NULL) {
+ syslog(LOG_ERR, "bridge new interface failed: %s",
+ strerror(errno));
+ return (NULL);
+ }
+
+ bzero(bif, sizeof(struct bridge_if));
+ strlcpy(bif->bif_name, bif_n, IFNAMSIZ);
+ bcopy(physaddr, bif->br_addr.octet, ETHER_ADDR_LEN);
+ bif->sysindex = sysindex;
+ bif->br_type = BaseType_transparent_only;
+ /* 1 - all bridges default hold time * 100 - centi-seconds */
+ bif->hold_time = 1 * 100;
+ bif->prot_spec = dot1dStpProtocolSpecification_ieee8021d;
+ bridge_ifs_insert(&bridge_ifs, bif);
+
+ return (bif);
+}
+
+/*
+ * Remove a bridge interface from the list, freeing all it's ports
+ * and address entries.
+ */
+void
+bridge_remove_bif(struct bridge_if *bif)
+{
+ bridge_members_free(bif);
+ bridge_addrs_free(bif);
+ TAILQ_REMOVE(&bridge_ifs, bif, b_if);
+ free(bif);
+}
+
+
+/*
+ * Prepare the variable (bridge interface name) for the private
+ * begemot notifications.
+ */
+static struct snmp_value*
+bridge_basename_var(struct bridge_if *bif, struct snmp_value* b_val)
+{
+ uint i;
+
+ b_val->var = oid_begemotBrigeName;
+ b_val->var.subs[b_val->var.len++] = strlen(bif->bif_name);
+
+ if ((b_val->v.octetstring.octets = (u_char *)
+ malloc(strlen(bif->bif_name))) == NULL)
+ return (NULL);
+
+ for (i = 0; i < strlen(bif->bif_name); i++)
+ b_val->var.subs[b_val->var.len++] = bif->bif_name[i];
+
+ b_val->v.octetstring.len = strlen(bif->bif_name);
+ bcopy(bif->bif_name, b_val->v.octetstring.octets,
+ strlen(bif->bif_name));
+ b_val->syntax = SNMP_SYNTAX_OCTETSTRING;
+
+ return (b_val);
+}
+
+/*
+ * Compare the values of the old and the new root port and
+ * send a new root notification, if they are not matching.
+ */
+static void
+bridge_new_root(struct bridge_if *bif)
+{
+ struct snmp_value bif_idx;
+
+ if (bridge_get_default() == bif)
+ snmp_send_trap(&oid_newRoot, (struct snmp_value *) NULL);
+
+ if (bridge_basename_var(bif, &bif_idx) == NULL)
+ return;
+
+ snmp_send_trap(&oid_begemotTopologyChange,
+ &bif_idx, (struct snmp_value *) NULL);
+}
+
+/*
+ * Compare the new and old topology change times and send a
+ * topology change notification if necessary.
+ */
+static void
+bridge_top_change(struct bridge_if *bif)
+{
+ struct snmp_value bif_idx;
+
+ if (bridge_get_default() == bif)
+ snmp_send_trap(&oid_TopologyChange,
+ (struct snmp_value *) NULL);
+
+ if (bridge_basename_var(bif, &bif_idx) == NULL)
+ return;
+
+ snmp_send_trap(&oid_begemotNewRoot,
+ &bif_idx, (struct snmp_value *) NULL);
+}
+
+static int
+bridge_if_create(const char* b_name, int8_t up)
+{
+ if (bridge_create(b_name) < 0)
+ return (-1);
+
+ if (up == 1 && (bridge_set_if_up(b_name, 1) < 0))
+ return (-1);
+
+ /*
+ * Do not create a new bridge entry here -
+ * wait until the mibII module notifies us.
+ */
+ return (0);
+}
+
+static int
+bridge_if_destroy(struct bridge_if *bif)
+{
+ if (bridge_destroy(bif->bif_name) < 0)
+ return (-1);
+
+ bridge_remove_bif(bif);
+
+ return (0);
+}
+
+/*
+ * Calculate the timeticks since the last topology change.
+ */
+static int
+bridge_get_time_since_tc(struct bridge_if *bif, uint32_t *ticks)
+{
+ struct timeval ct;
+
+ if (gettimeofday(&ct, NULL) < 0) {
+ syslog(LOG_ERR, "bridge get time since last TC:"
+ "getttimeofday failed: %s", strerror(errno));
+ return (-1);
+ }
+
+ if (ct.tv_usec - bif->last_tc_time.tv_usec < 0) {
+ ct.tv_sec -= 1;
+ ct.tv_usec += 1000000;
+ }
+
+ ct.tv_sec -= bif->last_tc_time.tv_sec;
+ ct.tv_usec -= bif->last_tc_time.tv_usec;
+
+ *ticks = ct.tv_sec * 100 + ct.tv_usec/10000;
+
+ return (0);
+}
+
+/*
+ * Update the info we have for a single bridge interface.
+ * Return:
+ * 1, if successful
+ * 0, if the interface was deleted
+ * -1, error occured while fetching the info from the kernel.
+ */
+static int
+bridge_update_bif(struct bridge_if *bif)
+{
+ struct mibif *ifp;
+
+ /* Walk through the mibII interface list. */
+ for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp))
+ if (strcmp(ifp->name, bif->bif_name) == 0)
+ break;
+
+ if (ifp == NULL) {
+ /* Ops, we do not exist anymore. */
+ bridge_remove_bif(bif);
+ return (0);
+ }
+
+ if (ifp->physaddr != NULL )
+ bcopy(ifp->physaddr, bif->br_addr.octet, ETHER_ADDR_LEN);
+ else
+ bridge_get_basemac(bif->bif_name, bif->br_addr.octet,
+ ETHER_ADDR_LEN);
+
+ if (ifp->mib.ifmd_flags & IFF_RUNNING)
+ bif->if_status = RowStatus_active;
+ else
+ bif->if_status = RowStatus_notInService;
+
+ switch (bridge_getinfo_bif(bif)) {
+ case 2:
+ bridge_new_root(bif);
+ break;
+ case 1:
+ bridge_top_change(bif);
+ break;
+ case -1:
+ bridge_remove_bif(bif);
+ return (-1);
+ default:
+ break;
+ }
+
+ /*
+ * The number of ports is accessible via SNMP -
+ * update the ports each time the bridge interface data
+ * is refreshed too.
+ */
+ bif->num_ports = bridge_update_memif(bif);
+ bif->entry_age = time(NULL);
+
+ return (1);
+}
+
+/*
+ * Update all bridge interfaces' ports only -
+ * make sure each bridge interface exists first.
+ */
+void
+bridge_update_all_ports(void)
+{
+ struct mibif *ifp;
+ struct bridge_if *bif, *t_bif;
+
+ for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) {
+ t_bif = bridge_next_bif(bif);
+
+ for (ifp = mib_first_if(); ifp != NULL;
+ ifp = mib_next_if(ifp))
+ if (strcmp(ifp->name, bif->bif_name) == 0)
+ break;
+
+ if (ifp != NULL)
+ bif->num_ports = bridge_update_memif(bif);
+ else /* Ops, we do not exist anymore. */
+ bridge_remove_bif(bif);
+ }
+
+ bridge_ports_update_listage();
+}
+
+/*
+ * Update all addresses only.
+ */
+void
+bridge_update_all_addrs(void)
+{
+ struct mibif *ifp;
+ struct bridge_if *bif, *t_bif;
+
+ for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) {
+ t_bif = bridge_next_bif(bif);
+
+ for (ifp = mib_first_if(); ifp != NULL;
+ ifp = mib_next_if(ifp))
+ if (strcmp(ifp->name, bif->bif_name) == 0)
+ break;
+
+ if (ifp != NULL)
+ bif->num_addrs = bridge_update_addrs(bif);
+ else /* Ops, we don't exist anymore. */
+ bridge_remove_bif(bif);
+ }
+
+ bridge_addrs_update_listage();
+}
+
+/*
+ * Update only the bridge interfaces' data - skip addresses.
+ */
+void
+bridge_update_all_ifs(void)
+{
+ struct bridge_if *bif, *t_bif;
+
+ for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) {
+ t_bif = bridge_next_bif(bif);
+ bridge_update_bif(bif);
+ }
+
+ bridge_ports_update_listage();
+ bridge_list_age = time(NULL);
+}
+
+/*
+ * Update all info we have for all bridges.
+ */
+void
+bridge_update_all(void *arg __unused)
+{
+ struct bridge_if *bif, *t_bif;
+
+ for (bif = bridge_first_bif(); bif != NULL; bif = t_bif) {
+ t_bif = bridge_next_bif(bif);
+ if (bridge_update_bif(bif) <= 0)
+ continue;
+
+ /* Update our learnt addresses. */
+ bif->num_addrs = bridge_update_addrs(bif);
+ }
+
+ bridge_list_age = time(NULL);
+ bridge_ports_update_listage();
+ bridge_addrs_update_listage();
+}
+
+/*
+ * Callback for polling our last topology change time -
+ * check whether we are root or whether a TC was detected once every
+ * 30 seconds, so that we can send the newRoot and TopologyChange traps
+ * on time. The rest of the data is polled only once every 5 min.
+ */
+void
+bridge_update_tc_time(void *arg __unused)
+{
+ struct bridge_if *bif;
+ struct mibif *ifp;
+
+ TAILQ_FOREACH(bif, &bridge_ifs, b_if) {
+ /* Walk through the mibII interface list. */
+ for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp))
+ if (strcmp(ifp->name, bif->bif_name) == 0)
+ break;
+
+ if (ifp == NULL) {
+ bridge_remove_bif(bif);
+ continue;
+ }
+
+ switch (bridge_get_op_param(bif)) {
+ case 2:
+ bridge_new_root(bif);
+ break;
+ case 1:
+ bridge_top_change(bif);
+ break;
+ }
+ }
+}
+
+/*
+ * Callback for handling new bridge interface creation.
+ */
+int
+bridge_attach_newif(struct mibif *ifp)
+{
+ u_char mac[ETHER_ADDR_LEN];
+ struct bridge_if *bif;
+
+ if (ifp->mib.ifmd_data.ifi_type != IFT_BRIDGE)
+ return (0);
+
+ /* Make sure it does not exist in our list. */
+ TAILQ_FOREACH(bif, &bridge_ifs, b_if)
+ if(strcmp(bif->bif_name, ifp->name) == 0) {
+ syslog(LOG_ERR, "bridge interface %s already "
+ "in list", bif->bif_name);
+ return (-1);
+ }
+
+ if (ifp->physaddr == NULL) {
+ if (bridge_get_basemac(ifp->name, mac, sizeof(mac)) == NULL) {
+ syslog(LOG_ERR, "bridge attach new %s failed - "
+ "no bridge mac address", ifp->name);
+ return (-1);
+ }
+ } else
+ bcopy(ifp->physaddr, &mac, sizeof(mac));
+
+ if ((bif = bridge_new_bif(ifp->name, ifp->sysindex, mac)) == NULL)
+ return (-1);
+
+ if (ifp->mib.ifmd_flags & IFF_RUNNING)
+ bif->if_status = RowStatus_active;
+ else
+ bif->if_status = RowStatus_notInService;
+
+ /* Skip sending notifications if the interface was just created. */
+ if (bridge_getinfo_bif(bif) < 0 ||
+ (bif->num_ports = bridge_getinfo_bif_ports(bif)) < 0 ||
+ (bif->num_addrs = bridge_getinfo_bif_addrs(bif)) < 0) {
+ bridge_remove_bif(bif);
+ return (-1);
+ }
+
+ /* Check whether we are the default bridge interface. */
+ if (strcmp(ifp->name, bridge_get_default_name()) == 0)
+ bridge_set_default(bif);
+
+ return (0);
+}
+
+void
+bridge_ifs_dump(void)
+{
+ struct bridge_if *bif;
+
+ for (bif = bridge_first_bif(); bif != NULL;
+ bif = bridge_next_bif(bif)) {
+ syslog(LOG_ERR, "Bridge %s, index - %d", bif->bif_name,
+ bif->sysindex);
+ bridge_ports_dump(bif);
+ bridge_addrs_dump(bif);
+ }
+}
+
+/*
+ * RFC4188 specifics.
+ */
+int
+op_dot1d_base(struct snmp_context *ctx __unused, struct snmp_value *value,
+ uint sub, uint iidx __unused, enum snmp_op op)
+{
+ struct bridge_if *bif;
+
+ if ((bif = bridge_get_default()) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+
+ if (time(NULL) - bif->entry_age > bridge_get_data_maxage() &&
+ bridge_update_bif(bif) <= 0) /* It was just deleted. */
+ return (SNMP_ERR_NOSUCHNAME);
+
+ switch (op) {
+ case SNMP_OP_GET:
+ switch (value->var.subs[sub - 1]) {
+ case LEAF_dot1dBaseBridgeAddress:
+ return (string_get(value, bif->br_addr.octet,
+ ETHER_ADDR_LEN));
+ case LEAF_dot1dBaseNumPorts:
+ value->v.integer = bif->num_ports;
+ return (SNMP_ERR_NOERROR);
+ case LEAF_dot1dBaseType:
+ value->v.integer = bif->br_type;
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+
+ case SNMP_OP_SET:
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_GETNEXT:
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ break;
+ }
+
+ abort();
+}
+
+int
+op_dot1d_stp(struct snmp_context *ctx, struct snmp_value *val, uint sub,
+ uint iidx __unused, enum snmp_op op)
+{
+ struct bridge_if *bif;
+
+ if ((bif = bridge_get_default()) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+
+ if (time(NULL) - bif->entry_age > bridge_get_data_maxage() &&
+ bridge_update_bif(bif) <= 0) /* It was just deleted. */
+ return (SNMP_ERR_NOSUCHNAME);
+
+ switch (op) {
+ case SNMP_OP_GET:
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_dot1dStpProtocolSpecification:
+ val->v.integer = bif->prot_spec;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpPriority:
+ val->v.integer = bif->priority;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpTimeSinceTopologyChange:
+ if (bridge_get_time_since_tc(bif,
+ &(val->v.uint32)) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpTopChanges:
+ val->v.uint32 = bif->top_changes;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpDesignatedRoot:
+ return (string_get(val, bif->design_root,
+ SNMP_BRIDGE_ID_LEN));
+
+ case LEAF_dot1dStpRootCost:
+ val->v.integer = bif->root_cost;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpRootPort:
+ val->v.integer = bif->root_port;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpMaxAge:
+ val->v.integer = bif->max_age;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpHelloTime:
+ val->v.integer = bif->hello_time;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpHoldTime:
+ val->v.integer = bif->hold_time;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpForwardDelay:
+ val->v.integer = bif->fwd_delay;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpBridgeMaxAge:
+ val->v.integer = bif->bridge_max_age;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpBridgeHelloTime:
+ val->v.integer = bif->bridge_hello_time;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpBridgeForwardDelay:
+ val->v.integer = bif->bridge_fwd_delay;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpVersion:
+ val->v.integer = bif->stp_version;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpTxHoldCount:
+ val->v.integer = bif->tx_hold_count;
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+
+ case SNMP_OP_GETNEXT:
+ abort();
+
+ case SNMP_OP_SET:
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_dot1dStpPriority:
+ if (val->v.integer > SNMP_BRIDGE_MAX_PRIORITY ||
+ val->v.integer % 4096 != 0)
+ return (SNMP_ERR_WRONG_VALUE);
+
+ ctx->scratch->int1 = bif->priority;
+ if (bridge_set_priority(bif, val->v.integer) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpBridgeMaxAge:
+ if (val->v.integer < SNMP_BRIDGE_MIN_MAGE ||
+ val->v.integer > SNMP_BRIDGE_MAX_MAGE)
+ return (SNMP_ERR_WRONG_VALUE);
+
+ ctx->scratch->int1 = bif->bridge_max_age;
+ if (bridge_set_maxage(bif, val->v.integer) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpBridgeHelloTime:
+ if (val->v.integer < SNMP_BRIDGE_MIN_HTIME ||
+ val->v.integer > SNMP_BRIDGE_MAX_HTIME)
+ return (SNMP_ERR_WRONG_VALUE);
+
+ ctx->scratch->int1 = bif->bridge_hello_time;
+ if (bridge_set_hello_time(bif, val->v.integer) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpBridgeForwardDelay:
+ if (val->v.integer < SNMP_BRIDGE_MIN_FDELAY ||
+ val->v.integer > SNMP_BRIDGE_MAX_FDELAY)
+ return (SNMP_ERR_WRONG_VALUE);
+
+ ctx->scratch->int1 = bif->bridge_fwd_delay;
+ if (bridge_set_forward_delay(bif, val->v.integer) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpVersion:
+ if (val->v.integer != dot1dStpVersion_stpCompatible &&
+ val->v.integer != dot1dStpVersion_rstp)
+ return (SNMP_ERR_WRONG_VALUE);
+
+ ctx->scratch->int1 = bif->stp_version;
+ if (bridge_set_stp_version(bif, val->v.integer) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpTxHoldCount:
+ if (val->v.integer < SNMP_BRIDGE_MIN_TXHC ||
+ val->v.integer > SNMP_BRIDGE_MAX_TXHC)
+ return (SNMP_ERR_WRONG_VALUE);
+
+ ctx->scratch->int1 = bif->tx_hold_count;
+ if (bridge_set_tx_hold_count(bif, val->v.integer) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpProtocolSpecification:
+ case LEAF_dot1dStpTimeSinceTopologyChange:
+ case LEAF_dot1dStpTopChanges:
+ case LEAF_dot1dStpDesignatedRoot:
+ case LEAF_dot1dStpRootCost:
+ case LEAF_dot1dStpRootPort:
+ case LEAF_dot1dStpMaxAge:
+ case LEAF_dot1dStpHelloTime:
+ case LEAF_dot1dStpHoldTime:
+ case LEAF_dot1dStpForwardDelay:
+ return (SNMP_ERR_NOT_WRITEABLE);
+ }
+ abort();
+
+ case SNMP_OP_ROLLBACK:
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_dot1dStpPriority:
+ bridge_set_priority(bif, ctx->scratch->int1);
+ break;
+ case LEAF_dot1dStpBridgeMaxAge:
+ bridge_set_maxage(bif, ctx->scratch->int1);
+ break;
+ case LEAF_dot1dStpBridgeHelloTime:
+ bridge_set_hello_time(bif, ctx->scratch->int1);
+ break;
+ case LEAF_dot1dStpBridgeForwardDelay:
+ bridge_set_forward_delay(bif, ctx->scratch->int1);
+ break;
+ case LEAF_dot1dStpVersion:
+ bridge_set_stp_version(bif, ctx->scratch->int1);
+ break;
+ case LEAF_dot1dStpTxHoldCount:
+ bridge_set_tx_hold_count(bif, ctx->scratch->int1);
+ break;
+ }
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_COMMIT:
+ return (SNMP_ERR_NOERROR);
+ }
+
+ abort();
+}
+
+int
+op_dot1d_tp(struct snmp_context *ctx, struct snmp_value *value,
+ uint sub, uint iidx __unused, enum snmp_op op)
+{
+ struct bridge_if *bif;
+
+ if ((bif = bridge_get_default()) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+
+ if (time(NULL) - bif->entry_age > bridge_get_data_maxage() &&
+ bridge_update_bif(bif) <= 0) /* It was just deleted. */
+ return (SNMP_ERR_NOSUCHNAME);
+
+ switch (op) {
+ case SNMP_OP_GET:
+ switch (value->var.subs[sub - 1]) {
+ case LEAF_dot1dTpLearnedEntryDiscards:
+ value->v.uint32 = bif->lrnt_drops;
+ return (SNMP_ERR_NOERROR);
+ case LEAF_dot1dTpAgingTime:
+ value->v.integer = bif->age_time;
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+
+ case SNMP_OP_GETNEXT:
+ abort();
+
+ case SNMP_OP_SET:
+ switch (value->var.subs[sub - 1]) {
+ case LEAF_dot1dTpLearnedEntryDiscards:
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case LEAF_dot1dTpAgingTime:
+ if (value->v.integer < SNMP_BRIDGE_MIN_AGE_TIME ||
+ value->v.integer > SNMP_BRIDGE_MAX_AGE_TIME)
+ return (SNMP_ERR_WRONG_VALUE);
+
+ ctx->scratch->int1 = bif->age_time;
+ if (bridge_set_aging_time(bif, value->v.integer) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+
+ case SNMP_OP_ROLLBACK:
+ if (value->var.subs[sub - 1] == LEAF_dot1dTpAgingTime)
+ bridge_set_aging_time(bif, ctx->scratch->int1);
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_COMMIT:
+ return (SNMP_ERR_NOERROR);
+ }
+
+ abort();
+}
+
+/*
+ * Private BEGEMOT-BRIDGE-MIB specifics.
+ */
+
+/*
+ * Get the bridge name from an OID index.
+ */
+static char *
+bridge_name_index_get(const struct asn_oid *oid, uint sub, char *b_name)
+{
+ uint i;
+
+ if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ)
+ return (NULL);
+
+ for (i = 0; i < oid->subs[sub]; i++)
+ b_name[i] = oid->subs[sub + i + 1];
+ b_name[i] = '\0';
+
+ return (b_name);
+}
+
+static void
+bridge_if_index_append(struct asn_oid *oid, uint sub,
+ const struct bridge_if *bif)
+{
+ uint i;
+
+ oid->len = sub + strlen(bif->bif_name) + 1;
+ oid->subs[sub] = strlen(bif->bif_name);
+
+ for (i = 1; i <= strlen(bif->bif_name); i++)
+ oid->subs[sub + i] = bif->bif_name[i - 1];
+}
+
+static struct bridge_if *
+bridge_if_index_get(const struct asn_oid *oid, uint sub)
+{
+ uint i;
+ char bif_name[IFNAMSIZ];
+
+ if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ)
+ return (NULL);
+
+ for (i = 0; i < oid->subs[sub]; i++)
+ bif_name[i] = oid->subs[sub + i + 1];
+ bif_name[i] = '\0';
+
+ return (bridge_if_find_ifname(bif_name));
+}
+
+static struct bridge_if *
+bridge_if_index_getnext(const struct asn_oid *oid, uint sub)
+{
+ uint i;
+ char bif_name[IFNAMSIZ];
+ struct bridge_if *bif;
+
+ if (oid->len - sub == 0)
+ return (bridge_first_bif());
+
+ if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= IFNAMSIZ)
+ return (NULL);
+
+ for (i = 0; i < oid->subs[sub]; i++)
+ bif_name[i] = oid->subs[sub + i + 1];
+ bif_name[i] = '\0';
+
+ if ((bif = bridge_if_find_ifname(bif_name)) == NULL)
+ return (NULL);
+
+ return (bridge_next_bif(bif));
+}
+
+static int
+bridge_set_if_status(struct snmp_context *ctx,
+ struct snmp_value *val, uint sub)
+{
+ struct bridge_if *bif;
+ char bif_name[IFNAMSIZ];
+
+ bif = bridge_if_index_get(&val->var, sub);
+
+ switch (val->v.integer) {
+ case RowStatus_active:
+ if (bif == NULL)
+ return (SNMP_ERR_INCONS_VALUE);
+
+ ctx->scratch->int1 = bif->if_status;
+
+ switch (bif->if_status) {
+ case RowStatus_active:
+ return (SNMP_ERR_NOERROR);
+ case RowStatus_notInService:
+ if (bridge_set_if_up(bif->bif_name, 1) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+ default:
+ break;
+ }
+ return (SNMP_ERR_INCONS_VALUE);
+
+ case RowStatus_notInService:
+ if (bif == NULL)
+ return (SNMP_ERR_INCONS_VALUE);
+
+ ctx->scratch->int1 = bif->if_status;
+
+ switch (bif->if_status) {
+ case RowStatus_active:
+ if (bridge_set_if_up(bif->bif_name, 1) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+ case RowStatus_notInService:
+ return (SNMP_ERR_NOERROR);
+ default:
+ break;
+ }
+ return (SNMP_ERR_INCONS_VALUE);
+
+ case RowStatus_notReady:
+ return (SNMP_ERR_INCONS_VALUE);
+
+ case RowStatus_createAndGo:
+ if (bif != NULL)
+ return (SNMP_ERR_INCONS_VALUE);
+
+ ctx->scratch->int1 = RowStatus_destroy;
+
+ if (bridge_name_index_get(&val->var, sub, bif_name) == NULL)
+ return (SNMP_ERR_BADVALUE);
+ if (bridge_if_create(bif_name, 1) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case RowStatus_createAndWait:
+ if (bif != NULL)
+ return (SNMP_ERR_INCONS_VALUE);
+
+ if (bridge_name_index_get(&val->var, sub, bif_name) == NULL)
+ return (SNMP_ERR_BADVALUE);
+
+ ctx->scratch->int1 = RowStatus_destroy;
+
+ if (bridge_if_create(bif_name, 0) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case RowStatus_destroy:
+ if (bif == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+
+ ctx->scratch->int1 = bif->if_status;
+ bif->if_status = RowStatus_destroy;
+ }
+
+ return (SNMP_ERR_NOERROR);
+}
+
+static int
+bridge_rollback_if_status(struct snmp_context *ctx,
+ struct snmp_value *val, uint sub)
+{
+ struct bridge_if *bif;
+
+ if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
+ return (SNMP_ERR_GENERR);
+
+ switch (ctx->scratch->int1) {
+ case RowStatus_destroy:
+ bridge_if_destroy(bif);
+ return (SNMP_ERR_NOERROR);
+
+ case RowStatus_notInService:
+ if (bif->if_status != ctx->scratch->int1)
+ bridge_set_if_up(bif->bif_name, 0);
+ bif->if_status = RowStatus_notInService;
+ return (SNMP_ERR_NOERROR);
+
+ case RowStatus_active:
+ if (bif->if_status != ctx->scratch->int1)
+ bridge_set_if_up(bif->bif_name, 1);
+ bif->if_status = RowStatus_active;
+ return (SNMP_ERR_NOERROR);
+ }
+
+ abort();
+}
+
+static int
+bridge_commit_if_status(struct snmp_value *val, uint sub)
+{
+ struct bridge_if *bif;
+
+ if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
+ return (SNMP_ERR_GENERR);
+
+ if (bif->if_status == RowStatus_destroy &&
+ bridge_if_destroy(bif) < 0)
+ return (SNMP_ERR_COMMIT_FAILED);
+
+ return (SNMP_ERR_NOERROR);
+}
+
+int
+op_begemot_base_bridge(struct snmp_context *ctx, struct snmp_value *val,
+ uint sub, uint iidx __unused, enum snmp_op op)
+{
+ struct bridge_if *bif;
+
+ if (time(NULL) - bridge_list_age > bridge_get_data_maxage())
+ bridge_update_all_ifs();
+
+ switch (op) {
+ case SNMP_OP_GET:
+ if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_GETNEXT:
+ if ((bif = bridge_if_index_getnext(&val->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ bridge_if_index_append(&val->var, sub, bif);
+ goto get;
+
+ case SNMP_OP_SET:
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_begemotBridgeBaseStatus:
+ return (bridge_set_if_status(ctx, val, sub));
+ case LEAF_begemotBridgeBaseName:
+ case LEAF_begemotBridgeBaseAddress:
+ case LEAF_begemotBridgeBaseNumPorts:
+ case LEAF_begemotBridgeBaseType:
+ return (SNMP_ERR_NOT_WRITEABLE);
+ }
+ abort();
+
+ case SNMP_OP_ROLLBACK:
+ return (bridge_rollback_if_status(ctx, val, sub));
+
+ case SNMP_OP_COMMIT:
+ return (bridge_commit_if_status(val, sub));
+ }
+ abort();
+
+get:
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_begemotBridgeBaseName:
+ return (string_get(val, bif->bif_name, -1));
+
+ case LEAF_begemotBridgeBaseAddress:
+ return (string_get(val, bif->br_addr.octet, ETHER_ADDR_LEN));
+
+ case LEAF_begemotBridgeBaseNumPorts:
+ val->v.integer = bif->num_ports;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeBaseType:
+ val->v.integer = bif->br_type;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeBaseStatus:
+ val->v.integer = bif->if_status;
+ return (SNMP_ERR_NOERROR);
+ }
+
+ abort();
+}
+
+int
+op_begemot_stp(struct snmp_context *ctx, struct snmp_value *val,
+ uint sub, uint iidx __unused, enum snmp_op op)
+{
+ struct bridge_if *bif;
+
+ if (time(NULL) - bridge_list_age > bridge_get_data_maxage())
+ bridge_update_all_ifs();
+
+ switch (op) {
+ case SNMP_OP_GET:
+ if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_GETNEXT:
+ if ((bif = bridge_if_index_getnext(&val->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ bridge_if_index_append(&val->var, sub, bif);
+ goto get;
+
+ case SNMP_OP_SET:
+ if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_begemotBridgeStpPriority:
+ if (val->v.integer > SNMP_BRIDGE_MAX_PRIORITY ||
+ val->v.integer % 4096 != 0)
+ return (SNMP_ERR_WRONG_VALUE);
+
+ ctx->scratch->int1 = bif->priority;
+ if (bridge_set_priority(bif, val->v.integer) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpBridgeMaxAge:
+ if (val->v.integer < SNMP_BRIDGE_MIN_MAGE ||
+ val->v.integer > SNMP_BRIDGE_MAX_MAGE)
+ return (SNMP_ERR_WRONG_VALUE);
+
+ ctx->scratch->int1 = bif->bridge_max_age;
+ if (bridge_set_maxage(bif, val->v.integer) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpBridgeHelloTime:
+ if (val->v.integer < SNMP_BRIDGE_MIN_HTIME ||
+ val->v.integer > SNMP_BRIDGE_MAX_HTIME)
+ return (SNMP_ERR_WRONG_VALUE);
+
+ ctx->scratch->int1 = bif->bridge_hello_time;
+ if (bridge_set_hello_time(bif, val->v.integer) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpBridgeForwardDelay:
+ if (val->v.integer < SNMP_BRIDGE_MIN_FDELAY ||
+ val->v.integer > SNMP_BRIDGE_MAX_FDELAY)
+ return (SNMP_ERR_WRONG_VALUE);
+
+ ctx->scratch->int1 = bif->bridge_fwd_delay;
+ if (bridge_set_forward_delay(bif, val->v.integer) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpVersion:
+ if (val->v.integer !=
+ begemotBridgeStpVersion_stpCompatible &&
+ val->v.integer != begemotBridgeStpVersion_rstp)
+ return (SNMP_ERR_WRONG_VALUE);
+
+ ctx->scratch->int1 = bif->stp_version;
+ if (bridge_set_stp_version(bif, val->v.integer) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpTxHoldCount:
+ if (val->v.integer < SNMP_BRIDGE_MIN_TXHC ||
+ val->v.integer > SNMP_BRIDGE_MAX_TXHC)
+ return (SNMP_ERR_WRONG_VALUE);
+
+ ctx->scratch->int1 = bif->tx_hold_count;
+ if (bridge_set_tx_hold_count(bif, val->v.integer) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpProtocolSpecification:
+ case LEAF_begemotBridgeStpTimeSinceTopologyChange:
+ case LEAF_begemotBridgeStpTopChanges:
+ case LEAF_begemotBridgeStpDesignatedRoot:
+ case LEAF_begemotBridgeStpRootCost:
+ case LEAF_begemotBridgeStpRootPort:
+ case LEAF_begemotBridgeStpMaxAge:
+ case LEAF_begemotBridgeStpHelloTime:
+ case LEAF_begemotBridgeStpHoldTime:
+ case LEAF_begemotBridgeStpForwardDelay:
+ return (SNMP_ERR_NOT_WRITEABLE);
+ }
+ abort();
+
+ case SNMP_OP_ROLLBACK:
+ if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_begemotBridgeStpPriority:
+ bridge_set_priority(bif, ctx->scratch->int1);
+ break;
+
+ case LEAF_begemotBridgeStpBridgeMaxAge:
+ bridge_set_maxage(bif, ctx->scratch->int1);
+ break;
+
+ case LEAF_begemotBridgeStpBridgeHelloTime:
+ bridge_set_hello_time(bif, ctx->scratch->int1);
+ break;
+
+ case LEAF_begemotBridgeStpBridgeForwardDelay:
+ bridge_set_forward_delay(bif, ctx->scratch->int1);
+ break;
+
+ case LEAF_begemotBridgeStpVersion:
+ bridge_set_stp_version(bif, ctx->scratch->int1);
+ break;
+
+ case LEAF_begemotBridgeStpTxHoldCount:
+ bridge_set_tx_hold_count(bif, ctx->scratch->int1);
+ break;
+ }
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_COMMIT:
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+
+get:
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_begemotBridgeStpProtocolSpecification:
+ val->v.integer = bif->prot_spec;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpPriority:
+ val->v.integer = bif->priority;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpTimeSinceTopologyChange:
+ if (bridge_get_time_since_tc(bif, &(val->v.uint32)) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpTopChanges:
+ val->v.uint32 = bif->top_changes;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpDesignatedRoot:
+ return (string_get(val, bif->design_root, SNMP_BRIDGE_ID_LEN));
+
+ case LEAF_begemotBridgeStpRootCost:
+ val->v.integer = bif->root_cost;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpRootPort:
+ val->v.integer = bif->root_port;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpMaxAge:
+ val->v.integer = bif->max_age;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpHelloTime:
+ val->v.integer = bif->hello_time;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpHoldTime:
+ val->v.integer = bif->hold_time;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpForwardDelay:
+ val->v.integer = bif->fwd_delay;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpBridgeMaxAge:
+ val->v.integer = bif->bridge_max_age;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpBridgeHelloTime:
+ val->v.integer = bif->bridge_hello_time;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpBridgeForwardDelay:
+ val->v.integer = bif->bridge_fwd_delay;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpVersion:
+ val->v.integer = bif->stp_version;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpTxHoldCount:
+ val->v.integer = bif->tx_hold_count;
+ return (SNMP_ERR_NOERROR);
+ }
+
+ abort();
+}
+
+int
+op_begemot_tp(struct snmp_context *ctx, struct snmp_value *val,
+ uint sub, uint iidx __unused, enum snmp_op op)
+{
+ struct bridge_if *bif;
+
+ if (time(NULL) - bridge_list_age > bridge_get_data_maxage())
+ bridge_update_all_ifs();
+
+ switch (op) {
+ case SNMP_OP_GET:
+ if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_GETNEXT:
+ if ((bif = bridge_if_index_getnext(&val->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ bridge_if_index_append(&val->var, sub, bif);
+ goto get;
+
+ case SNMP_OP_SET:
+ if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_begemotBridgeTpAgingTime:
+ if (val->v.integer < SNMP_BRIDGE_MIN_AGE_TIME ||
+ val->v.integer > SNMP_BRIDGE_MAX_AGE_TIME)
+ return (SNMP_ERR_WRONG_VALUE);
+
+ ctx->scratch->int1 = bif->age_time;
+ if (bridge_set_aging_time(bif, val->v.integer) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeTpMaxAddresses:
+ ctx->scratch->int1 = bif->max_addrs;
+ if (bridge_set_max_cache(bif, val->v.integer) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeTpLearnedEntryDiscards:
+ return (SNMP_ERR_NOT_WRITEABLE);
+ }
+ abort();
+
+ case SNMP_OP_ROLLBACK:
+ if ((bif = bridge_if_index_get(&val->var, sub)) == NULL)
+ return (SNMP_ERR_GENERR);
+
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_begemotBridgeTpAgingTime:
+ bridge_set_aging_time(bif, ctx->scratch->int1);
+ break;
+
+ case LEAF_begemotBridgeTpMaxAddresses:
+ bridge_set_max_cache(bif, ctx->scratch->int1);
+ break;
+ }
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_COMMIT:
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+
+get:
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_begemotBridgeTpLearnedEntryDiscards:
+ val->v.uint32 = bif->lrnt_drops;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeTpAgingTime:
+ val->v.integer = bif->age_time;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeTpMaxAddresses:
+ val->v.integer = bif->max_addrs;
+ return (SNMP_ERR_NOERROR);
+ }
+
+ abort();
+}
diff --git a/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_pf.c b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_pf.c
new file mode 100644
index 0000000..0ad8cbf
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_pf.c
@@ -0,0 +1,116 @@
+/*-
+ * Copyright (c) 2006 Shteryana Shopova <syrinx@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.
+ *
+ * Bridge MIB implementation for SNMPd.
+ * Bridge pfil controls.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/socket.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_mib.h>
+#include <net/if_types.h>
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+
+#include <bsnmp/snmpmod.h>
+#include <bsnmp/snmp_mibII.h>
+
+#include "bridge_tree.h"
+#include "bridge_snmp.h"
+
+static int
+val2snmp_truth(uint8_t val)
+{
+ if (val == 0)
+ return (2);
+
+ return (1);
+}
+
+static int
+snmp_truth2val(int32_t truth)
+{
+ if (truth == 2)
+ return (0);
+ else if (truth == 1)
+ return (1);
+
+ return (-1);
+}
+
+int
+op_begemot_bridge_pf(struct snmp_context *ctx, struct snmp_value *val,
+ uint sub, uint iidx __unused, enum snmp_op op)
+{
+ int k_val;
+
+ if (val->var.subs[sub - 1] > LEAF_begemotBridgeLayer2PfStatus)
+ return (SNMP_ERR_NOSUCHNAME);
+
+ switch (op) {
+ case SNMP_OP_GETNEXT:
+ abort();
+ case SNMP_OP_ROLLBACK:
+ bridge_do_pfctl(val->var.subs[sub - 1] - 1,
+ op, &(ctx->scratch->int1));
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_COMMIT:
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_SET:
+ ctx->scratch->int1 =
+ bridge_get_pfval(val->var.subs[sub - 1]);
+
+ if ((k_val = snmp_truth2val(val->v.integer)) < 0)
+ return (SNMP_ERR_BADVALUE);
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_GET:
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_begemotBridgePfilStatus:
+ case LEAF_begemotBridgePfilMembers:
+ case LEAF_begemotBridgePfilIpOnly:
+ case LEAF_begemotBridgeLayer2PfStatus:
+ if (bridge_do_pfctl(val->var.subs[sub - 1] - 1,
+ op, &k_val) < 0)
+ return (SNMP_ERR_GENERR);
+ val->v.integer = val2snmp_truth(k_val);
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+ }
+
+ abort();
+}
diff --git a/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_port.c b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_port.c
new file mode 100644
index 0000000..fe2af04
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_port.c
@@ -0,0 +1,1513 @@
+/*-
+ * Copyright (c) 2006 Shteryana Shopova <syrinx@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.
+ *
+ * Bridge MIB implementation for SNMPd.
+ * Bridge ports.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_mib.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+
+#include <bsnmp/snmpmod.h>
+#include <bsnmp/snmp_mibII.h>
+
+#include "bridge_tree.h"
+#include "bridge_snmp.h"
+
+TAILQ_HEAD(bridge_ports, bridge_port);
+
+/*
+ * Free the bridge base ports list.
+ */
+static void
+bridge_ports_free(struct bridge_ports *headp)
+{
+ struct bridge_port *bp;
+
+ while ((bp = TAILQ_FIRST(headp)) != NULL) {
+ TAILQ_REMOVE(headp, bp, b_p);
+ free(bp);
+ }
+}
+
+/*
+ * Free the bridge base ports from the base ports list,
+ * members of a specified bridge interface only.
+ */
+static void
+bridge_port_memif_free(struct bridge_ports *headp,
+ struct bridge_if *bif)
+{
+ struct bridge_port *bp;
+
+ while (bif->f_bp != NULL && bif->sysindex == bif->f_bp->sysindex) {
+ bp = TAILQ_NEXT(bif->f_bp, b_p);
+ TAILQ_REMOVE(headp, bif->f_bp, b_p);
+ free(bif->f_bp);
+ bif->f_bp = bp;
+ }
+}
+
+/*
+ * Insert a port entry in the base port TAILQ starting to search
+ * for its place from the position of the first bridge port for the bridge
+ * interface. Update the first bridge port if neccessary.
+ */
+static void
+bridge_port_insert_at(struct bridge_ports *headp,
+ struct bridge_port *bp, struct bridge_port **f_bp)
+{
+ struct bridge_port *t1;
+
+ assert(f_bp != NULL);
+
+ for (t1 = *f_bp;
+ t1 != NULL && bp->sysindex == t1->sysindex;
+ t1 = TAILQ_NEXT(t1, b_p)) {
+ if (bp->if_idx < t1->if_idx) {
+ TAILQ_INSERT_BEFORE(t1, bp, b_p);
+ if (*f_bp == t1)
+ *f_bp = bp;
+ return;
+ }
+ }
+
+ /*
+ * Handle the case when our first port was actually the
+ * last element of the TAILQ.
+ */
+ if (t1 == NULL)
+ TAILQ_INSERT_TAIL(headp, bp, b_p);
+ else
+ TAILQ_INSERT_BEFORE(t1, bp, b_p);
+}
+
+/*
+ * Find a port entry's possition in the ports list according
+ * to it's parent bridge interface name. Returns a NULL if
+ * we should be at the TAILQ head, otherwise the entry after
+ * which we should be inserted.
+ */
+static struct bridge_port *
+bridge_port_find_pos(struct bridge_ports *headp, uint32_t b_idx)
+{
+ uint32_t t_idx;
+ struct bridge_port *t1;
+
+ if ((t1 = TAILQ_FIRST(headp)) == NULL ||
+ bridge_compare_sysidx(b_idx, t1->sysindex) < 0)
+ return (NULL);
+
+ t_idx = t1->sysindex;
+
+ for (t1 = TAILQ_NEXT(t1, b_p); t1 != NULL; t1 = TAILQ_NEXT(t1, b_p)) {
+ if (t1->sysindex != t_idx) {
+ if (bridge_compare_sysidx(b_idx, t1->sysindex) < 0)
+ return (TAILQ_PREV(t1, bridge_ports, b_p));
+ else
+ t_idx = t1->sysindex;
+ }
+ }
+
+ if (t1 == NULL)
+ t1 = TAILQ_LAST(headp, bridge_ports);
+
+ return (t1);
+}
+
+/*
+ * Insert a bridge member interface in the ports TAILQ.
+ */
+static void
+bridge_port_memif_insert(struct bridge_ports *headp,
+ struct bridge_port *bp, struct bridge_port **f_bp)
+{
+ struct bridge_port *temp;
+
+ if (*f_bp != NULL)
+ bridge_port_insert_at(headp, bp, f_bp);
+ else {
+ temp = bridge_port_find_pos(headp, bp->sysindex);
+
+ if (temp == NULL)
+ TAILQ_INSERT_HEAD(headp, bp, b_p);
+ else
+ TAILQ_INSERT_AFTER(headp, temp, bp, b_p);
+ *f_bp = bp;
+ }
+}
+
+/* The global ports list. */
+static struct bridge_ports bridge_ports = TAILQ_HEAD_INITIALIZER(bridge_ports);
+static time_t ports_list_age;
+
+void
+bridge_ports_update_listage(void)
+{
+ ports_list_age = time(NULL);
+}
+
+void
+bridge_ports_fini(void)
+{
+ bridge_ports_free(&bridge_ports);
+}
+
+void
+bridge_members_free(struct bridge_if *bif)
+{
+ bridge_port_memif_free(&bridge_ports, bif);
+}
+
+/*
+ * Find the first port in the ports list.
+ */
+static struct bridge_port *
+bridge_port_first(void)
+{
+ return (TAILQ_FIRST(&bridge_ports));
+}
+
+/*
+ * Find the next port in the ports list.
+ */
+static struct bridge_port *
+bridge_port_next(struct bridge_port *bp)
+{
+ return (TAILQ_NEXT(bp, b_p));
+}
+
+/*
+ * Find the first member of the specified bridge interface.
+ */
+struct bridge_port *
+bridge_port_bif_first(struct bridge_if *bif)
+{
+ return (bif->f_bp);
+}
+
+/*
+ * Find the next member of the specified bridge interface.
+ */
+struct bridge_port *
+bridge_port_bif_next(struct bridge_port *bp)
+{
+ struct bridge_port *bp_next;
+
+ if ((bp_next = TAILQ_NEXT(bp, b_p)) == NULL ||
+ bp_next->sysindex != bp->sysindex)
+ return (NULL);
+
+ return (bp_next);
+}
+
+/*
+ * Remove a bridge port from the ports list.
+ */
+void
+bridge_port_remove(struct bridge_port *bp, struct bridge_if *bif)
+{
+ if (bif->f_bp == bp)
+ bif->f_bp = bridge_port_bif_next(bp);
+
+ TAILQ_REMOVE(&bridge_ports, bp, b_p);
+ free(bp);
+}
+
+/*
+ * Allocate memory for a new bridge port and insert it
+ * in the base ports list. Return a pointer to the port's
+ * structure in case we want to do anything else with it.
+ */
+struct bridge_port *
+bridge_new_port(struct mibif *mif, struct bridge_if *bif)
+{
+ struct bridge_port *bp;
+
+ if ((bp = (struct bridge_port *) malloc(sizeof(*bp))) == NULL) {
+ syslog(LOG_ERR, "bridge new member: failed: %s",
+ strerror(errno));
+ return (NULL);
+ }
+
+ bzero(bp, sizeof(*bp));
+
+ bp->sysindex = bif->sysindex;
+ bp->if_idx = mif->index;
+ bp->port_no = mif->sysindex;
+ strlcpy(bp->p_name, mif->name, IFNAMSIZ);
+ bp->circuit = oid_zeroDotZero;
+
+ /*
+ * Initialize all rstpMib specific values to false/default.
+ * These will be set to their true values later if the bridge
+ * supports RSTP.
+ */
+ bp->proto_migr = TruthValue_false;
+ bp->admin_edge = TruthValue_false;
+ bp->oper_edge = TruthValue_false;
+ bp->oper_ptp = TruthValue_false;
+ bp->admin_ptp = StpPortAdminPointToPointType_auto;
+
+ bridge_port_memif_insert(&bridge_ports, bp, &(bif->f_bp));
+
+ return (bp);
+}
+
+/*
+ * Update our info from the corresponding mibII interface info.
+ */
+void
+bridge_port_getinfo_mibif(struct mibif *m_if, struct bridge_port *bp)
+{
+ bp->max_info = m_if->mib.ifmd_data.ifi_mtu;
+ bp->in_frames = m_if->mib.ifmd_data.ifi_ipackets;
+ bp->out_frames = m_if->mib.ifmd_data.ifi_opackets;
+ bp->in_drops = m_if->mib.ifmd_data.ifi_iqdrops;
+}
+
+/*
+ * Find a port, whose SNMP's mibII ifIndex matches one of the ports,
+ * members of the specified bridge interface.
+ */
+struct bridge_port *
+bridge_port_find(int32_t if_idx, struct bridge_if *bif)
+{
+ struct bridge_port *bp;
+
+ for (bp = bif->f_bp; bp != NULL; bp = TAILQ_NEXT(bp, b_p)) {
+ if (bp->sysindex != bif->sysindex) {
+ bp = NULL;
+ break;
+ }
+
+ if (bp->if_idx == if_idx)
+ break;
+ }
+
+ return (bp);
+}
+
+void
+bridge_ports_dump(struct bridge_if *bif)
+{
+ struct bridge_port *bp;
+
+ for (bp = bridge_port_bif_first(bif); bp != NULL;
+ bp = bridge_port_bif_next(bp)) {
+ syslog(LOG_ERR, "memif - %s, index - %d",
+ bp->p_name, bp->port_no);
+ }
+}
+
+/*
+ * RFC4188 specifics.
+ */
+int
+op_dot1d_base_port(struct snmp_context *c __unused, struct snmp_value *val,
+ uint sub, uint iidx __unused, enum snmp_op op)
+{
+ struct bridge_if *bif;
+ struct bridge_port *bp;
+
+ if ((bif = bridge_get_default()) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+
+ if (time(NULL) - bif->ports_age > bridge_get_data_maxage() &&
+ bridge_update_memif(bif) <= 0)
+ return (SNMP_ERR_NOSUCHNAME);
+
+ switch (op) {
+ case SNMP_OP_GET:
+ if (val->var.len - sub != 1)
+ return (SNMP_ERR_NOSUCHNAME);
+ if ((bp = bridge_port_find(val->var.subs[sub],
+ bif)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_GETNEXT:
+ if (val->var.len - sub == 0) {
+ if ((bp = bridge_port_bif_first(bif)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ } else {
+ if ((bp = bridge_port_find(val->var.subs[sub],
+ bif)) == NULL ||
+ (bp = bridge_port_bif_next(bp)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ }
+ val->var.len = sub + 1;
+ val->var.subs[sub] = bp->port_no;
+ goto get;
+
+ case SNMP_OP_SET:
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ break;
+ }
+ abort();
+
+get:
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_dot1dBasePort:
+ val->v.integer = bp->port_no;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dBasePortIfIndex:
+ val->v.integer = bp->if_idx;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dBasePortCircuit:
+ val->v.oid = bp->circuit;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dBasePortDelayExceededDiscards:
+ val->v.uint32 = bp->dly_ex_drops;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dBasePortMtuExceededDiscards:
+ val->v.uint32 = bp->dly_mtu_drops;
+ return (SNMP_ERR_NOERROR);
+ }
+
+ abort();
+}
+
+int
+op_dot1d_stp_port(struct snmp_context *ctx, struct snmp_value *val,
+ uint sub, uint iidx __unused, enum snmp_op op)
+{
+ struct bridge_if *bif;
+ struct bridge_port *bp;
+
+ if ((bif = bridge_get_default()) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+
+ if (time(NULL) - bif->ports_age > bridge_get_data_maxage() &&
+ bridge_update_memif(bif) <= 0)
+ return (SNMP_ERR_NOSUCHNAME);
+
+ switch (op) {
+ case SNMP_OP_GET:
+ if (val->var.len - sub != 1)
+ return (SNMP_ERR_NOSUCHNAME);
+ if ((bp = bridge_port_find(val->var.subs[sub],
+ bif)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_GETNEXT:
+ if (val->var.len - sub == 0) {
+ if ((bp = bridge_port_bif_first(bif)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ } else {
+ if ((bp = bridge_port_find(val->var.subs[sub],
+ bif)) == NULL ||
+ (bp = bridge_port_bif_next(bp)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ }
+ val->var.len = sub + 1;
+ val->var.subs[sub] = bp->port_no;
+ goto get;
+
+ case SNMP_OP_SET:
+ if (val->var.len - sub != 1)
+ return (SNMP_ERR_NOSUCHNAME);
+ if ((bp = bridge_port_find(val->var.subs[sub],
+ bif)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_dot1dStpPortPriority:
+ if (val->v.integer < 0 || val->v.integer > 255)
+ return (SNMP_ERR_WRONG_VALUE);
+
+ ctx->scratch->int1 = bp->priority;
+ if (bridge_port_set_priority(bif->bif_name, bp,
+ val->v.integer) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpPortEnable:
+ if (val->v.integer != dot1dStpPortEnable_enabled &&
+ val->v.integer != dot1dStpPortEnable_disabled)
+ return (SNMP_ERR_WRONG_VALUE);
+
+ ctx->scratch->int1 = bp->enable;
+ if (bridge_port_set_stp_enable(bif->bif_name,
+ bp, val->v.integer) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpPortPathCost:
+ if (val->v.integer < SNMP_PORT_MIN_PATHCOST ||
+ val->v.integer > SNMP_PORT_MAX_PATHCOST)
+ return (SNMP_ERR_WRONG_VALUE);
+
+ ctx->scratch->int1 = bp->path_cost;
+ if (bridge_port_set_path_cost(bif->bif_name, bp,
+ val->v.integer) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpPort:
+ case LEAF_dot1dStpPortState:
+ case LEAF_dot1dStpPortDesignatedRoot:
+ case LEAF_dot1dStpPortDesignatedCost:
+ case LEAF_dot1dStpPortDesignatedBridge:
+ case LEAF_dot1dStpPortDesignatedPort:
+ case LEAF_dot1dStpPortForwardTransitions:
+ return (SNMP_ERR_NOT_WRITEABLE);
+ }
+ abort();
+
+ case SNMP_OP_ROLLBACK:
+ if ((bp = bridge_port_find(val->var.subs[sub],
+ bif)) == NULL)
+ return (SNMP_ERR_GENERR);
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_dot1dStpPortPriority:
+ bridge_port_set_priority(bif->bif_name, bp,
+ ctx->scratch->int1);
+ break;
+ case LEAF_dot1dStpPortEnable:
+ bridge_port_set_stp_enable(bif->bif_name, bp,
+ ctx->scratch->int1);
+ break;
+ case LEAF_dot1dStpPortPathCost:
+ bridge_port_set_path_cost(bif->bif_name, bp,
+ ctx->scratch->int1);
+ break;
+ }
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_COMMIT:
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+
+get:
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_dot1dStpPort:
+ val->v.integer = bp->port_no;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpPortPriority:
+ val->v.integer = bp->priority;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpPortState:
+ val->v.integer = bp->state;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpPortEnable:
+ val->v.integer = bp->enable;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpPortPathCost:
+ val->v.integer = bp->path_cost;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpPortDesignatedRoot:
+ return (string_get(val, bp->design_root,
+ SNMP_BRIDGE_ID_LEN));
+
+ case LEAF_dot1dStpPortDesignatedCost:
+ val->v.integer = bp->design_cost;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpPortDesignatedBridge:
+ return (string_get(val, bp->design_bridge,
+ SNMP_BRIDGE_ID_LEN));
+
+ case LEAF_dot1dStpPortDesignatedPort:
+ return (string_get(val, bp->design_port, 2));
+
+ case LEAF_dot1dStpPortForwardTransitions:
+ val->v.uint32 = bp->fwd_trans;
+ return (SNMP_ERR_NOERROR);
+ }
+
+ abort();
+}
+
+int
+op_dot1d_stp_ext_port(struct snmp_context *ctx, struct snmp_value *val,
+ uint sub, uint iidx __unused, enum snmp_op op)
+{
+ struct bridge_if *bif;
+ struct bridge_port *bp;
+
+ if ((bif = bridge_get_default()) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+
+ if (time(NULL) - bif->ports_age > bridge_get_data_maxage() &&
+ bridge_update_memif(bif) <= 0)
+ return (SNMP_ERR_NOSUCHNAME);
+
+ switch (op) {
+ case SNMP_OP_GET:
+ if (val->var.len - sub != 1)
+ return (SNMP_ERR_NOSUCHNAME);
+ if ((bp = bridge_port_find(val->var.subs[sub],
+ bif)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_GETNEXT:
+ if (val->var.len - sub == 0) {
+ if ((bp = bridge_port_bif_first(bif)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ } else {
+ if ((bp = bridge_port_find(val->var.subs[sub],
+ bif)) == NULL ||
+ (bp = bridge_port_bif_next(bp)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ }
+ val->var.len = sub + 1;
+ val->var.subs[sub] = bp->port_no;
+ goto get;
+
+ case SNMP_OP_SET:
+ if (val->var.len - sub != 1)
+ return (SNMP_ERR_NOSUCHNAME);
+ if ((bp = bridge_port_find(val->var.subs[sub],
+ bif)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_dot1dStpPortAdminEdgePort:
+ if (val->v.integer != TruthValue_true &&
+ val->v.integer != TruthValue_false)
+ return (SNMP_ERR_WRONG_VALUE);
+
+ ctx->scratch->int1 = bp->admin_edge;
+ if (bridge_port_set_admin_edge(bif->bif_name, bp,
+ val->v.integer) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpPortAdminPointToPoint:
+ if (val->v.integer < 0 || val->v.integer >
+ StpPortAdminPointToPointType_auto)
+ return (SNMP_ERR_WRONG_VALUE);
+
+ ctx->scratch->int1 = bp->admin_ptp;
+ if (bridge_port_set_admin_ptp(bif->bif_name, bp,
+ val->v.integer) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpPortAdminPathCost:
+ if (val->v.integer < SNMP_PORT_MIN_PATHCOST ||
+ val->v.integer > SNMP_PORT_MAX_PATHCOST)
+ return (SNMP_ERR_WRONG_VALUE);
+
+ ctx->scratch->int1 = bp->admin_path_cost;
+ if (bridge_port_set_path_cost(bif->bif_name, bp,
+ val->v.integer) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpPortProtocolMigration:
+ case LEAF_dot1dStpPortOperEdgePort:
+ case LEAF_dot1dStpPortOperPointToPoint:
+ return (SNMP_ERR_NOT_WRITEABLE);
+ }
+ abort();
+
+ case SNMP_OP_ROLLBACK:
+ if ((bp = bridge_port_find(val->var.subs[sub],
+ bif)) == NULL)
+ return (SNMP_ERR_GENERR);
+
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_dot1dStpPortAdminEdgePort:
+ bridge_port_set_admin_edge(bif->bif_name, bp,
+ ctx->scratch->int1);
+ break;
+ case LEAF_dot1dStpPortAdminPointToPoint:
+ bridge_port_set_admin_ptp(bif->bif_name, bp,
+ ctx->scratch->int1);
+ break;
+ case LEAF_dot1dStpPortAdminPathCost:
+ bridge_port_set_path_cost(bif->bif_name, bp,
+ ctx->scratch->int1);
+ break;
+ }
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_COMMIT:
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+
+get:
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_dot1dStpPortProtocolMigration:
+ val->v.integer = bp->proto_migr;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpPortAdminEdgePort:
+ val->v.integer = bp->admin_edge;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpPortOperEdgePort:
+ val->v.integer = bp->oper_edge;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpPortAdminPointToPoint:
+ val->v.integer = bp->admin_ptp;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpPortOperPointToPoint:
+ val->v.integer = bp->oper_ptp;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dStpPortAdminPathCost:
+ val->v.integer = bp->admin_path_cost;
+ return (SNMP_ERR_NOERROR);
+ }
+
+ abort();
+}
+
+int
+op_dot1d_tp_port(struct snmp_context *c __unused, struct snmp_value *val,
+ uint sub, uint iidx __unused, enum snmp_op op)
+{
+ struct bridge_if *bif;
+ struct bridge_port *bp;
+
+ if ((bif = bridge_get_default()) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+
+ if (time(NULL) - bif->ports_age > bridge_get_data_maxage() &&
+ bridge_update_memif(bif) <= 0)
+ return (SNMP_ERR_NOSUCHNAME);
+
+ switch (op) {
+ case SNMP_OP_GET:
+ if (val->var.len - sub != 1)
+ return (SNMP_ERR_NOSUCHNAME);
+ if ((bp = bridge_port_find(val->var.subs[sub],
+ bif)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_GETNEXT:
+ if (val->var.len - sub == 0) {
+ if ((bp = bridge_port_bif_first(bif)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ } else {
+ if ((bp = bridge_port_find(val->var.subs[sub],
+ bif)) == NULL ||
+ (bp = bridge_port_bif_next(bp)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ }
+ val->var.len = sub + 1;
+ val->var.subs[sub] = bp->port_no;
+ goto get;
+
+ case SNMP_OP_SET:
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ break;
+ }
+ abort();
+
+get:
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_dot1dTpPort:
+ val->v.integer = bp->port_no;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dTpPortMaxInfo:
+ val->v.integer = bp->max_info;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dTpPortInFrames:
+ val->v.uint32 = bp->in_frames;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dTpPortOutFrames:
+ val->v.uint32 = bp->out_frames;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_dot1dTpPortInDiscards:
+ val->v.uint32 = bp->in_drops;
+ return (SNMP_ERR_NOERROR);
+ }
+
+ abort();
+}
+
+/*
+ * Private BEGEMOT-BRIDGE-MIB specifics.
+ */
+
+/*
+ * Construct a bridge port entry index.
+ */
+static int
+bridge_port_index_append(struct asn_oid *oid, uint sub,
+ const struct bridge_port *bp)
+{
+ uint i;
+ const char *b_name;
+
+ if ((b_name = bridge_if_find_name(bp->sysindex)) == NULL)
+ return (-1);
+
+ oid->len = sub + strlen(b_name) + 1 + 1;
+ oid->subs[sub] = strlen(b_name);
+
+ for (i = 1; i <= strlen(b_name); i++)
+ oid->subs[sub + i] = b_name[i - 1];
+
+ oid->subs[sub + i] = bp->port_no;
+
+ return (0);
+}
+
+/*
+ * Get the port entry from an entry's index.
+ */
+static struct bridge_port *
+bridge_port_index_get(const struct asn_oid *oid, uint sub, int8_t status)
+{
+ uint i;
+ int32_t port_no;
+ char bif_name[IFNAMSIZ];
+ struct bridge_if *bif;
+ struct bridge_port *bp;
+
+ if (oid->len - sub != oid->subs[sub] + 2 ||
+ oid->subs[sub] >= IFNAMSIZ)
+ return (NULL);
+
+ for (i = 0; i < oid->subs[sub]; i++)
+ bif_name[i] = oid->subs[sub + i + 1];
+ bif_name[i] = '\0';
+
+ port_no = oid->subs[sub + i + 1];
+
+ if ((bif = bridge_if_find_ifname(bif_name)) == NULL)
+ return (NULL);
+
+ if ((bp = bridge_port_find(port_no, bif)) == NULL ||
+ (status == 0 && bp->status != RowStatus_active))
+ return (NULL);
+
+ return (bp);
+}
+
+/*
+ * Get the next port entry from an entry's index.
+ */
+static struct bridge_port *
+bridge_port_index_getnext(const struct asn_oid *oid, uint sub, int8_t status)
+{
+ uint i;
+ int32_t port_no;
+ char bif_name[IFNAMSIZ];
+ struct bridge_if *bif;
+ struct bridge_port *bp;
+
+ if (oid->len - sub == 0)
+ bp = bridge_port_first();
+ else {
+ if (oid->len - sub != oid->subs[sub] + 2 ||
+ oid->subs[sub] >= IFNAMSIZ)
+ return (NULL);
+
+ for (i = 0; i < oid->subs[sub]; i++)
+ bif_name[i] = oid->subs[sub + i + 1];
+ bif_name[i] = '\0';
+
+ port_no = oid->subs[sub + i + 1];
+
+ if ((bif = bridge_if_find_ifname(bif_name)) == NULL ||
+ (bp = bridge_port_find(port_no, bif)) == NULL)
+ return (NULL);
+
+ bp = bridge_port_next(bp);
+ }
+
+ if (status == 1)
+ return (bp);
+
+ while (bp != NULL) {
+ if (bp->status == RowStatus_active)
+ break;
+ bp = bridge_port_next(bp);
+ }
+
+ return (bp);
+}
+
+/*
+ * Read the bridge name and port index from a ASN OID structure.
+ */
+static int
+bridge_port_index_decode(const struct asn_oid *oid, uint sub,
+ char *b_name, int32_t *idx)
+{
+ uint i;
+
+ if (oid->len - sub != oid->subs[sub] + 2 ||
+ oid->subs[sub] >= IFNAMSIZ)
+ return (-1);
+
+ for (i = 0; i < oid->subs[sub]; i++)
+ b_name[i] = oid->subs[sub + i + 1];
+ b_name[i] = '\0';
+
+ *idx = oid->subs[sub + i + 1];
+ return (0);
+}
+
+static int
+bridge_port_set_status(struct snmp_context *ctx,
+ struct snmp_value *val, uint sub)
+{
+ int32_t if_idx;
+ char b_name[IFNAMSIZ];
+ struct bridge_if *bif;
+ struct bridge_port *bp;
+ struct mibif *mif;
+
+ if (bridge_port_index_decode(&val->var, sub, b_name, &if_idx) < 0)
+ return (SNMP_ERR_INCONS_VALUE);
+
+ if ((bif = bridge_if_find_ifname(b_name)) == NULL ||
+ (mif = mib_find_if(if_idx)) == NULL)
+ return (SNMP_ERR_INCONS_VALUE);
+
+ bp = bridge_port_find(if_idx, bif);
+
+ switch (val->v.integer) {
+ case RowStatus_active:
+ if (bp == NULL)
+ return (SNMP_ERR_INCONS_VALUE);
+
+ if (bp->span_enable == 0)
+ return (SNMP_ERR_INCONS_VALUE);
+
+ ctx->scratch->int1 = bp->status;
+ bp->status = RowStatus_active;
+ break;
+
+ case RowStatus_notInService:
+ if (bp == NULL || bp->span_enable == 0 ||
+ bp->status == RowStatus_active)
+ return (SNMP_ERR_INCONS_VALUE);
+
+ ctx->scratch->int1 = bp->status;
+ bp->status = RowStatus_notInService;
+
+ case RowStatus_notReady:
+ /* FALLTHROUGH */
+ case RowStatus_createAndGo:
+ return (SNMP_ERR_INCONS_VALUE);
+
+ case RowStatus_createAndWait:
+ if (bp != NULL)
+ return (SNMP_ERR_INCONS_VALUE);
+
+ if ((bp = bridge_new_port(mif, bif)) == NULL)
+ return (SNMP_ERR_GENERR);
+
+ ctx->scratch->int1 = RowStatus_destroy;
+ bp->status = RowStatus_notReady;
+ break;
+
+ case RowStatus_destroy:
+ if (bp == NULL)
+ return (SNMP_ERR_INCONS_VALUE);
+
+ ctx->scratch->int1 = bp->status;
+ bp->status = RowStatus_destroy;
+ break;
+ }
+
+ return (SNMP_ERR_NOERROR);
+}
+
+static int
+bridge_port_rollback_status(struct snmp_context *ctx,
+ struct snmp_value *val, uint sub)
+{
+ int32_t if_idx;
+ char b_name[IFNAMSIZ];
+ struct bridge_if *bif;
+ struct bridge_port *bp;
+
+ if (bridge_port_index_decode(&val->var, sub, b_name, &if_idx) < 0)
+ return (SNMP_ERR_GENERR);
+
+ if ((bif = bridge_if_find_ifname(b_name)) == NULL ||
+ (bp = bridge_port_find(if_idx, bif)) == NULL)
+ return (SNMP_ERR_GENERR);
+
+ if (ctx->scratch->int1 == RowStatus_destroy)
+ bridge_port_remove(bp, bif);
+ else
+ bp->status = ctx->scratch->int1;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+static int
+bridge_port_commit_status(struct snmp_value *val, uint sub)
+{
+ int32_t if_idx;
+ char b_name[IFNAMSIZ];
+ struct bridge_if *bif;
+ struct bridge_port *bp;
+
+ if (bridge_port_index_decode(&val->var, sub, b_name, &if_idx) < 0)
+ return (SNMP_ERR_GENERR);
+
+ if ((bif = bridge_if_find_ifname(b_name)) == NULL ||
+ (bp = bridge_port_find(if_idx, bif)) == NULL)
+ return (SNMP_ERR_GENERR);
+
+ switch (bp->status) {
+ case RowStatus_active:
+ if (bridge_port_addm(bp, b_name) < 0)
+ return (SNMP_ERR_COMMIT_FAILED);
+ break;
+
+ case RowStatus_destroy:
+ if (bridge_port_delm(bp, b_name) < 0)
+ return (SNMP_ERR_COMMIT_FAILED);
+ bridge_port_remove(bp, bif);
+ break;
+ }
+
+ return (SNMP_ERR_NOERROR);
+}
+
+static int
+bridge_port_set_span_enable(struct snmp_context *ctx,
+ struct snmp_value *val, uint sub)
+{
+ int32_t if_idx;
+ char b_name[IFNAMSIZ];
+ struct bridge_if *bif;
+ struct bridge_port *bp;
+ struct mibif *mif;
+
+ if (val->v.integer != begemotBridgeBaseSpanEnabled_enabled &&
+ val->v.integer != begemotBridgeBaseSpanEnabled_disabled)
+ return (SNMP_ERR_BADVALUE);
+
+ if (bridge_port_index_decode(&val->var, sub, b_name, &if_idx) < 0)
+ return (SNMP_ERR_INCONS_VALUE);
+
+ if ((bif = bridge_if_find_ifname(b_name)) == NULL)
+ return (SNMP_ERR_INCONS_VALUE);
+
+ if ((bp = bridge_port_find(if_idx, bif)) == NULL) {
+ if ((mif = mib_find_if(if_idx)) == NULL)
+ return (SNMP_ERR_INCONS_VALUE);
+
+ if ((bp = bridge_new_port(mif, bif)) == NULL)
+ return (SNMP_ERR_GENERR);
+
+ ctx->scratch->int1 = RowStatus_destroy;
+ } else if (bp->status == RowStatus_active) {
+ return (SNMP_ERR_INCONS_VALUE);
+ } else {
+ ctx->scratch->int1 = bp->status;
+ }
+
+ bp->span_enable = val->v.integer;
+ bp->status = RowStatus_notInService;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+int
+op_begemot_base_port(struct snmp_context *ctx, struct snmp_value *val,
+ uint sub, uint iidx __unused, enum snmp_op op)
+{
+ int8_t status, which;
+ const char *bname;
+ struct bridge_port *bp;
+
+ if (time(NULL) - ports_list_age > bridge_get_data_maxage())
+ bridge_update_all_ports();
+
+ which = val->var.subs[sub - 1];
+ status = 0;
+
+ switch (op) {
+ case SNMP_OP_GET:
+ if (which == LEAF_begemotBridgeBaseSpanEnabled ||
+ which == LEAF_begemotBridgeBasePortStatus)
+ status = 1;
+ if ((bp = bridge_port_index_get(&val->var, sub,
+ status)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_GETNEXT:
+ if (which == LEAF_begemotBridgeBaseSpanEnabled ||
+ which == LEAF_begemotBridgeBasePortStatus)
+ status = 1;
+ if ((bp = bridge_port_index_getnext(&val->var, sub,
+ status)) == NULL ||
+ bridge_port_index_append(&val->var, sub, bp) < 0)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_SET:
+ switch (which) {
+ case LEAF_begemotBridgeBaseSpanEnabled:
+ return (bridge_port_set_span_enable(ctx, val, sub));
+
+ case LEAF_begemotBridgeBasePortStatus:
+ return (bridge_port_set_status(ctx, val, sub));
+
+ case LEAF_begemotBridgeBasePortPrivate:
+ if ((bp = bridge_port_index_get(&val->var, sub,
+ status)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ if ((bname = bridge_if_find_name(bp->sysindex)) == NULL)
+ return (SNMP_ERR_GENERR);
+ ctx->scratch->int1 = bp->priv_set;
+ return (bridge_port_set_private(bname, bp,
+ val->v.integer));
+
+ case LEAF_begemotBridgeBasePort:
+ case LEAF_begemotBridgeBasePortIfIndex:
+ case LEAF_begemotBridgeBasePortDelayExceededDiscards:
+ case LEAF_begemotBridgeBasePortMtuExceededDiscards:
+ return (SNMP_ERR_NOT_WRITEABLE);
+ }
+ abort();
+
+ case SNMP_OP_ROLLBACK:
+ switch (which) {
+ case LEAF_begemotBridgeBaseSpanEnabled:
+ /* FALLTHROUGH */
+ case LEAF_begemotBridgeBasePortStatus:
+ return (bridge_port_rollback_status(ctx, val, sub));
+ case LEAF_begemotBridgeBasePortPrivate:
+ if ((bp = bridge_port_index_get(&val->var, sub,
+ status)) == NULL)
+ return (SNMP_ERR_GENERR);
+ if ((bname = bridge_if_find_name(bp->sysindex)) == NULL)
+ return (SNMP_ERR_GENERR);
+ return (bridge_port_set_private(bname, bp,
+ ctx->scratch->int1));
+ }
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_COMMIT:
+ if (which == LEAF_begemotBridgeBasePortStatus)
+ return (bridge_port_commit_status(val, sub));
+
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+
+get:
+ switch (which) {
+ case LEAF_begemotBridgeBasePort:
+ val->v.integer = bp->port_no;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeBasePortIfIndex:
+ val->v.integer = bp->if_idx;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeBaseSpanEnabled:
+ val->v.integer = bp->span_enable;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeBasePortDelayExceededDiscards:
+ val->v.uint32 = bp->dly_ex_drops;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeBasePortMtuExceededDiscards:
+ val->v.uint32 = bp->dly_mtu_drops;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeBasePortStatus:
+ val->v.integer = bp->status;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeBasePortPrivate:
+ val->v.integer = bp->priv_set;
+ return (SNMP_ERR_NOERROR);
+ }
+
+ abort();
+}
+
+int
+op_begemot_stp_port(struct snmp_context *ctx, struct snmp_value *val,
+ uint sub, uint iidx __unused, enum snmp_op op)
+{
+ struct bridge_port *bp;
+ const char *b_name;
+
+ if (time(NULL) - ports_list_age > bridge_get_data_maxage())
+ bridge_update_all_ports();
+
+ switch (op) {
+ case SNMP_OP_GET:
+ if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_GETNEXT:
+ if ((bp = bridge_port_index_getnext(&val->var, sub, 0)) ==
+ NULL || bridge_port_index_append(&val->var, sub, bp) < 0)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_SET:
+ if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ if ((b_name = bridge_if_find_name(bp->sysindex)) == NULL)
+ return (SNMP_ERR_GENERR);
+
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_begemotBridgeStpPortPriority:
+ if (val->v.integer < 0 || val->v.integer > 255)
+ return (SNMP_ERR_WRONG_VALUE);
+
+ ctx->scratch->int1 = bp->priority;
+ if (bridge_port_set_priority(b_name, bp,
+ val->v.integer) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpPortEnable:
+ if (val->v.integer !=
+ begemotBridgeStpPortEnable_enabled ||
+ val->v.integer !=
+ begemotBridgeStpPortEnable_disabled)
+ return (SNMP_ERR_WRONG_VALUE);
+
+ ctx->scratch->int1 = bp->enable;
+ if (bridge_port_set_stp_enable(b_name, bp,
+ val->v.integer) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpPortPathCost:
+ if (val->v.integer < SNMP_PORT_MIN_PATHCOST ||
+ val->v.integer > SNMP_PORT_MAX_PATHCOST)
+ return (SNMP_ERR_WRONG_VALUE);
+
+ ctx->scratch->int1 = bp->path_cost;
+ if (bridge_port_set_path_cost(b_name, bp,
+ val->v.integer) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpPort:
+ case LEAF_begemotBridgeStpPortState:
+ case LEAF_begemotBridgeStpPortDesignatedRoot:
+ case LEAF_begemotBridgeStpPortDesignatedCost:
+ case LEAF_begemotBridgeStpPortDesignatedBridge:
+ case LEAF_begemotBridgeStpPortDesignatedPort:
+ case LEAF_begemotBridgeStpPortForwardTransitions:
+ return (SNMP_ERR_NOT_WRITEABLE);
+ }
+ abort();
+
+ case SNMP_OP_ROLLBACK:
+ if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL ||
+ (b_name = bridge_if_find_name(bp->sysindex)) == NULL)
+ return (SNMP_ERR_GENERR);
+
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_begemotBridgeStpPortPriority:
+ bridge_port_set_priority(b_name, bp,
+ ctx->scratch->int1);
+ break;
+ case LEAF_begemotBridgeStpPortEnable:
+ bridge_port_set_stp_enable(b_name, bp,
+ ctx->scratch->int1);
+ break;
+ case LEAF_begemotBridgeStpPortPathCost:
+ bridge_port_set_path_cost(b_name, bp,
+ ctx->scratch->int1);
+ break;
+ }
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_COMMIT:
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+
+get:
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_begemotBridgeStpPort:
+ val->v.integer = bp->port_no;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpPortPriority:
+ val->v.integer = bp->priority;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpPortState:
+ val->v.integer = bp->state;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpPortEnable:
+ val->v.integer = bp->enable;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpPortPathCost:
+ val->v.integer = bp->path_cost;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpPortDesignatedRoot:
+ return (string_get(val, bp->design_root, SNMP_BRIDGE_ID_LEN));
+
+ case LEAF_begemotBridgeStpPortDesignatedCost:
+ val->v.integer = bp->design_cost;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpPortDesignatedBridge:
+ return (string_get(val, bp->design_bridge, SNMP_BRIDGE_ID_LEN));
+
+ case LEAF_begemotBridgeStpPortDesignatedPort:
+ return (string_get(val, bp->design_port, 2));
+
+ case LEAF_begemotBridgeStpPortForwardTransitions:
+ val->v.uint32 = bp->fwd_trans;
+ return (SNMP_ERR_NOERROR);
+ }
+
+ abort();
+}
+
+int
+op_begemot_stp_ext_port(struct snmp_context *ctx, struct snmp_value *val,
+ uint sub, uint iidx __unused, enum snmp_op op)
+{
+ struct bridge_port *bp;
+ const char *b_name;
+
+ if (time(NULL) - ports_list_age > bridge_get_data_maxage())
+ bridge_update_all_ports();
+
+ switch (op) {
+ case SNMP_OP_GET:
+ if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_GETNEXT:
+ if ((bp = bridge_port_index_getnext(&val->var, sub, 0)) ==
+ NULL || bridge_port_index_append(&val->var, sub, bp) < 0)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_SET:
+ if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ if ((b_name = bridge_if_find_name(bp->sysindex)) == NULL)
+ return (SNMP_ERR_GENERR);
+
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_begemotBridgeStpPortAdminEdgePort:
+ if (val->v.integer != TruthValue_true &&
+ val->v.integer != TruthValue_false)
+ return (SNMP_ERR_WRONG_VALUE);
+
+ ctx->scratch->int1 = bp->admin_edge;
+ if (bridge_port_set_admin_edge(b_name, bp,
+ val->v.integer) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpPortAdminPointToPoint:
+ if (val->v.integer < 0 || val->v.integer >
+ StpPortAdminPointToPointType_auto)
+ return (SNMP_ERR_WRONG_VALUE);
+
+ ctx->scratch->int1 = bp->admin_ptp;
+ if (bridge_port_set_admin_ptp(b_name, bp,
+ val->v.integer) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpPortAdminPathCost:
+ if (val->v.integer < SNMP_PORT_MIN_PATHCOST ||
+ val->v.integer > SNMP_PORT_MAX_PATHCOST)
+ return (SNMP_ERR_WRONG_VALUE);
+
+ ctx->scratch->int1 = bp->admin_path_cost;
+ if (bridge_port_set_path_cost(b_name, bp,
+ val->v.integer) < 0)
+ return (SNMP_ERR_GENERR);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpPortProtocolMigration:
+ case LEAF_begemotBridgeStpPortOperEdgePort:
+ case LEAF_begemotBridgeStpPortOperPointToPoint:
+ return (SNMP_ERR_NOT_WRITEABLE);
+ }
+ abort();
+
+ case SNMP_OP_ROLLBACK:
+ if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL ||
+ (b_name = bridge_if_find_name(bp->sysindex)) == NULL)
+ return (SNMP_ERR_GENERR);
+
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_begemotBridgeStpPortAdminEdgePort:
+ bridge_port_set_admin_edge(b_name, bp,
+ ctx->scratch->int1);
+ break;
+ case LEAF_begemotBridgeStpPortAdminPointToPoint:
+ bridge_port_set_admin_ptp(b_name, bp,
+ ctx->scratch->int1);
+ break;
+ case LEAF_begemotBridgeStpPortAdminPathCost:
+ bridge_port_set_path_cost(b_name, bp,
+ ctx->scratch->int1);
+ break;
+ }
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_COMMIT:
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+
+get:
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_begemotBridgeStpPortProtocolMigration:
+ val->v.integer = bp->proto_migr;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpPortAdminEdgePort:
+ val->v.integer = bp->admin_edge;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpPortOperEdgePort:
+ val->v.integer = bp->oper_edge;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpPortAdminPointToPoint:
+ val->v.integer = bp->admin_ptp;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpPortOperPointToPoint:
+ val->v.integer = bp->oper_ptp;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeStpPortAdminPathCost:
+ val->v.integer = bp->admin_path_cost;
+ return (SNMP_ERR_NOERROR);
+ }
+
+ abort();
+}
+
+int
+op_begemot_tp_port(struct snmp_context *c __unused, struct snmp_value *val,
+ uint sub, uint iidx __unused, enum snmp_op op)
+{
+ struct bridge_port *bp;
+
+ if (time(NULL) - ports_list_age > bridge_get_data_maxage())
+ bridge_update_all_ports();
+
+ switch (op) {
+ case SNMP_OP_GET:
+ if ((bp = bridge_port_index_get(&val->var, sub, 0)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_GETNEXT:
+ if ((bp = bridge_port_index_getnext(&val->var, sub, 0)) ==
+ NULL || bridge_port_index_append(&val->var, sub, bp) < 0)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_SET:
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ break;
+ }
+ abort();
+
+get:
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_begemotBridgeTpPort:
+ val->v.integer = bp->port_no;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeTpPortMaxInfo:
+ val->v.integer = bp->max_info;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeTpPortInFrames:
+ val->v.uint32 = bp->in_frames;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeTpPortOutFrames:
+ val->v.uint32 = bp->out_frames;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeTpPortInDiscards:
+ val->v.uint32 = bp->in_drops;
+ return (SNMP_ERR_NOERROR);
+ }
+
+ abort();
+}
diff --git a/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_snmp.c b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_snmp.c
new file mode 100644
index 0000000..81acc4d
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_snmp.c
@@ -0,0 +1,338 @@
+/*-
+ * Copyright (c) 2006 Shteryana Shopova <syrinx@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.
+ *
+ * Bridge MIB implementation for SNMPd.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_mib.h>
+#include <net/if_types.h>
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+
+#include <bsnmp/snmpmod.h>
+#include <bsnmp/snmp_mibII.h>
+
+#include "bridge_tree.h"
+#include "bridge_snmp.h"
+#include "bridge_oid.h"
+
+static struct lmodule *bridge_module;
+
+/* For the registration. */
+static const struct asn_oid oid_dot1Bridge = OIDX_dot1dBridge;
+/* The registration. */
+static uint reg_bridge;
+
+/* Periodic timer for polling all bridges' data. */
+static void *bridge_data_timer;
+static void *bridge_tc_timer;
+
+static int bridge_data_maxage = SNMP_BRIDGE_DATA_MAXAGE;
+static int bridge_poll_ticks = SNMP_BRIDGE_POLL_INTERVAL * 100;
+static int bridge_tc_poll_ticks = SNMP_BRIDGE_TC_POLL_INTERVAL * 100;
+
+/*
+ * Our default bridge, whose info will be visible under
+ * the dot1dBridge subtree and functions to set/fetch it.
+ */
+static char bif_default_name[IFNAMSIZ] = "bridge0";
+static struct bridge_if *bif_default;
+
+struct bridge_if *
+bridge_get_default(void)
+{
+ struct mibif *ifp;
+
+ if (bif_default != NULL) {
+
+ /* Walk through the mibII interface list. */
+ for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp))
+ if (strcmp(ifp->name, bif_default->bif_name) == 0)
+ break;
+
+ if (ifp == NULL)
+ bif_default = NULL;
+ }
+
+ return (bif_default);
+}
+
+void
+bridge_set_default(struct bridge_if *bif)
+{
+ bif_default = bif;
+
+ syslog(LOG_ERR, "Set default bridge interface to: %s",
+ bif == NULL ? "(none)" : bif->bif_name);
+}
+
+const char *
+bridge_get_default_name(void)
+{
+ return (bif_default_name);
+}
+
+static int
+bridge_set_default_name(const char *bif_name, uint len)
+{
+ struct bridge_if *bif;
+
+ if (len >= IFNAMSIZ)
+ return (-1);
+
+ bcopy(bif_name, bif_default_name, len);
+ bif_default_name[len] = '\0';
+
+ if ((bif = bridge_if_find_ifname(bif_default_name)) == NULL) {
+ bif_default = NULL;
+ return (0);
+ }
+
+ bif_default = bif;
+ return (1);
+}
+
+int
+bridge_get_data_maxage(void)
+{
+ return (bridge_data_maxage);
+}
+
+static void
+bridge_set_poll_ticks(int poll_ticks)
+{
+ if (bridge_data_timer != NULL)
+ timer_stop(bridge_data_timer);
+
+ bridge_poll_ticks = poll_ticks;
+ bridge_data_timer = timer_start_repeat(bridge_poll_ticks,
+ bridge_poll_ticks, bridge_update_all, NULL, bridge_module);
+}
+/*
+ * The bridge module configuration via SNMP.
+ */
+static int
+bridge_default_name_save(struct snmp_context *ctx, const char *bridge_default)
+{
+ if ((ctx->scratch->int1 = strlen(bridge_default)) >= IFNAMSIZ)
+ return (-1);
+
+ if ((ctx->scratch->ptr1 = malloc(IFNAMSIZ)) == NULL)
+ return (-1);
+
+ strncpy(ctx->scratch->ptr1, bridge_default, ctx->scratch->int1);
+ return (0);
+}
+
+int
+op_begemot_bridge_config(struct snmp_context *ctx, struct snmp_value *val,
+ uint sub, uint iidx __unused, enum snmp_op op)
+{
+ switch (op) {
+ case SNMP_OP_GET:
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_begemotBridgeDefaultBridgeIf:
+ return (string_get(val, bridge_get_default_name(), -1));
+
+ case LEAF_begemotBridgeDataUpdate:
+ val->v.integer = bridge_data_maxage;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeDataPoll:
+ val->v.integer = bridge_poll_ticks / 100;
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+
+ case SNMP_OP_GETNEXT:
+ abort();
+
+ case SNMP_OP_SET:
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_begemotBridgeDefaultBridgeIf:
+ /*
+ * Cannot use string_save() here - requires either
+ * a fixed-sized or var-length string - not less
+ * than or equal.
+ */
+ if (bridge_default_name_save(ctx,
+ bridge_get_default_name()) < 0)
+ return (SNMP_ERR_RES_UNAVAIL);
+
+ if (bridge_set_default_name(val->v.octetstring.octets,
+ val->v.octetstring.len) < 0)
+ return (SNMP_ERR_BADVALUE);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeDataUpdate:
+ if (val->v.integer < SNMP_BRIDGE_DATA_MAXAGE_MIN ||
+ val->v.integer > SNMP_BRIDGE_DATA_MAXAGE_MAX)
+ return (SNMP_ERR_WRONG_VALUE);
+ ctx->scratch->int1 = bridge_data_maxage;
+ bridge_data_maxage = val->v.integer;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotBridgeDataPoll:
+ if (val->v.integer < SNMP_BRIDGE_POLL_INTERVAL_MIN ||
+ val->v.integer > SNMP_BRIDGE_POLL_INTERVAL_MAX)
+ return (SNMP_ERR_WRONG_VALUE);
+ ctx->scratch->int1 = val->v.integer;
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+
+ case SNMP_OP_ROLLBACK:
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_begemotBridgeDefaultBridgeIf:
+ bridge_set_default_name(ctx->scratch->ptr1,
+ ctx->scratch->int1);
+ free(ctx->scratch->ptr1);
+ break;
+ case LEAF_begemotBridgeDataUpdate:
+ bridge_data_maxage = ctx->scratch->int1;
+ break;
+ }
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_COMMIT:
+ switch (val->var.subs[sub - 1]) {
+ case LEAF_begemotBridgeDefaultBridgeIf:
+ free(ctx->scratch->ptr1);
+ break;
+ case LEAF_begemotBridgeDataPoll:
+ bridge_set_poll_ticks(ctx->scratch->int1 * 100);
+ break;
+ }
+ return (SNMP_ERR_NOERROR);
+ }
+
+ abort();
+}
+
+/*
+ * Bridge mib module initialization hook.
+ * Returns 0 on success, < 0 on error.
+ */
+static int
+bridge_init(struct lmodule * mod, int argc __unused, char *argv[] __unused)
+{
+ bridge_module = mod;
+
+ if (bridge_kmod_load() < 0)
+ return (-1);
+
+ if (bridge_ioctl_init() < 0)
+ return (-1);
+
+ /* Register to get creation messages for bridge interfaces. */
+ if (mib_register_newif(bridge_attach_newif, bridge_module)) {
+ syslog(LOG_ERR, "Cannot register newif function: %s",
+ strerror(errno));
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Bridge mib module finalization hook.
+ */
+static int
+bridge_fini(void)
+{
+ mib_unregister_newif(bridge_module);
+ or_unregister(reg_bridge);
+
+ if (bridge_data_timer != NULL) {
+ timer_stop(bridge_data_timer);
+ bridge_data_timer = NULL;
+ }
+
+ if (bridge_tc_timer != NULL) {
+ timer_stop(bridge_tc_timer);
+ bridge_tc_timer = NULL;
+ }
+
+ bridge_ifs_fini();
+ bridge_ports_fini();
+ bridge_addrs_fini();
+
+ return (0);
+}
+
+/*
+ * Bridge mib module start operation.
+ */
+static void
+bridge_start(void)
+{
+ reg_bridge = or_register(&oid_dot1Bridge,
+ "The IETF MIB for Bridges (RFC 4188).", bridge_module);
+
+ bridge_data_timer = timer_start_repeat(bridge_poll_ticks,
+ bridge_poll_ticks, bridge_update_all, NULL, bridge_module);
+
+ bridge_tc_timer = timer_start_repeat(bridge_tc_poll_ticks,
+ bridge_tc_poll_ticks, bridge_update_tc_time, NULL, bridge_module);
+}
+
+static void
+bridge_dump(void)
+{
+ struct bridge_if *bif;
+
+ if ((bif = bridge_get_default()) == NULL)
+ syslog(LOG_ERR, "Dump: no default bridge interface");
+ else
+ syslog(LOG_ERR, "Dump: default bridge interface %s",
+ bif->bif_name);
+
+ bridge_ifs_dump();
+ bridge_pf_dump();
+}
+
+const struct snmp_module config = {
+ .comment = "This module implements the bridge mib (RFC 4188).",
+ .init = bridge_init,
+ .fini = bridge_fini,
+ .start = bridge_start,
+ .tree = bridge_ctree,
+ .dump = bridge_dump,
+ .tree_size = bridge_CTREE_SIZE,
+};
diff --git a/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_snmp.h b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_snmp.h
new file mode 100644
index 0000000..7f48950
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_snmp.h
@@ -0,0 +1,357 @@
+/*-
+ * Copyright (c) 2006 Shteryana Shopova <syrinx@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.
+ *
+ * Bridge MIB implementation for SNMPd.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef SNMP_BRIDGE_H
+#define SNMP_BRIDGE_H
+
+#define SNMP_BRIDGE_ID_LEN 8
+
+typedef uint8_t port_id[2];
+typedef u_char bridge_id[SNMP_BRIDGE_ID_LEN];
+
+#define SNMP_BRIDGE_MAX_PRIORITY 65535
+
+#define SNMP_BRIDGE_MIN_AGE_TIME 10
+#define SNMP_BRIDGE_MAX_AGE_TIME 1000000
+
+#define SNMP_BRIDGE_MIN_TXHC 1
+#define SNMP_BRIDGE_MAX_TXHC 10
+
+#define SNMP_BRIDGE_MIN_MAGE 600
+#define SNMP_BRIDGE_MAX_MAGE 4000
+
+#define SNMP_BRIDGE_MIN_HTIME 100
+#define SNMP_BRIDGE_MAX_HTIME 1000
+
+#define SNMP_BRIDGE_MIN_FDELAY 400
+#define SNMP_BRIDGE_MAX_FDELAY 3000
+
+#define SNMP_PORT_PATHCOST_OBSOLETE 65535
+#define SNMP_PORT_MIN_PATHCOST 0
+#define SNMP_PORT_MAX_PATHCOST 200000000
+#define SNMP_PORT_PATHCOST_AUTO 0
+
+#define SNMP_BRIDGE_DATA_MAXAGE 10
+#define SNMP_BRIDGE_DATA_MAXAGE_MIN 1
+#define SNMP_BRIDGE_DATA_MAXAGE_MAX 300
+
+/* By default poll kernel data every 5 minutes. */
+#define SNMP_BRIDGE_POLL_INTERVAL (5 * 60)
+#define SNMP_BRIDGE_POLL_INTERVAL_MIN 1
+#define SNMP_BRIDGE_POLL_INTERVAL_MAX 3600
+
+/* Poll for a topology change once every 30 seconds. */
+#define SNMP_BRIDGE_TC_POLL_INTERVAL 30
+
+struct bridge_if *bridge_get_default(void);
+
+void bridge_set_default(struct bridge_if *bif);
+
+const char *bridge_get_default_name(void);
+
+int bridge_get_data_maxage(void);
+
+/*
+ * Bridge Addresses Table.
+ */
+struct tp_entry {
+ uint32_t sysindex; /* The bridge if sysindex. */
+ int32_t port_no;
+ enum TpFdbStatus status;
+ uint8_t tp_addr[ETHER_ADDR_LEN];
+ uint8_t flags;
+ TAILQ_ENTRY(tp_entry) tp_e;
+};
+
+/*
+ * Bridge ports.
+ * The bridge port system interface index is used for a
+ * port number. Transparent bridging statistics and STP
+ * information for a port are also contained here.
+ */
+struct bridge_port {
+ /* dot1dBase subtree objects. */
+ uint32_t sysindex; /* The bridge interface sysindex. */
+ int32_t port_no; /* The bridge member system index. */
+ int32_t if_idx; /* SNMP ifIndex from mibII. */
+ int8_t span_enable; /* Span flag set - private MIB. */
+ struct asn_oid circuit; /* Unused. */
+ uint32_t dly_ex_drops; /* Drops on output. */
+ uint32_t dly_mtu_drops; /* MTU exceeded drops. */
+ int32_t status; /* The entry status. */
+ enum TruthValue priv_set; /* The private flag. */
+
+ /* dot1dStp subtree objects. */
+ int32_t path_cost;
+ int32_t priority;
+ int32_t design_cost;
+ uint32_t fwd_trans;
+ char p_name[IFNAMSIZ]; /* Not in BRIDGE-MIB. */
+ enum StpPortState state;
+ enum dot1dStpPortEnable enable;
+ port_id design_port;
+ bridge_id design_root;
+ bridge_id design_bridge;
+
+ /* rstpMib extensions. */
+ int32_t admin_path_cost;
+ enum TruthValue proto_migr;
+ enum TruthValue admin_edge;
+ enum TruthValue oper_edge;
+ enum TruthValue oper_ptp;
+ enum StpPortAdminPointToPointType admin_ptp;
+
+ /* dot1dTp subtree objects. */
+ int32_t max_info;
+ int32_t in_frames;
+ int32_t out_frames;
+ int32_t in_drops;
+
+ uint8_t flags;
+ TAILQ_ENTRY(bridge_port) b_p;
+};
+
+/*
+ * A bridge interface.
+ * The system interface index of the bridge is not required neither by the
+ * standard BRIDGE-MIB nor by the private BEGEMOT-BRIDGE-MIB, but is used
+ * as key for looking up the other info for this bridge.
+ */
+struct bridge_if {
+ /* dot1dBase subtree objects. */
+ uint32_t sysindex; /* The system interface index. */
+ int32_t num_ports; /* Number of ports. */
+ enum BaseType br_type; /* Bridge type. */
+ enum RowStatus if_status; /* Bridge status. */
+ char bif_name[IFNAMSIZ]; /* Bridge interface name. */
+ struct ether_addr br_addr; /* Bridge address. */
+ struct bridge_port *f_bp; /* This bridge's first entry
+ * in the base ports TAILQ. */
+ /* dot1dStp subtree objects. */
+ int32_t priority;
+ int32_t root_cost;
+ int32_t root_port;
+ int32_t max_age; /* Current max age. */
+ int32_t hello_time; /* Current hello time. */
+ int32_t fwd_delay; /* Current forward delay. */
+ int32_t hold_time;
+ int32_t bridge_max_age; /* Configured max age. */
+ int32_t bridge_hello_time; /* Configured hello time. */
+ int32_t bridge_fwd_delay; /* Configured forward delay. */
+ int32_t tx_hold_count;
+ uint32_t top_changes;
+ enum dot1dStpVersion stp_version;
+ enum dot1dStpProtocolSpecification prot_spec;
+ struct timeval last_tc_time;
+ bridge_id design_root;
+
+ /* dot1dTp subtree objects. */
+ int32_t lrnt_drops; /* Dropped addresses. */
+ int32_t age_time; /* Address entry timeout. */
+ int32_t num_addrs; /* Current # of addresses in cache. */
+ int32_t max_addrs; /* Max # of addresses in cache. */
+ struct tp_entry *f_tpa; /* This bridge's first entry in
+ * the tp addresses TAILQ. */
+
+ time_t entry_age;
+ time_t ports_age;
+ time_t addrs_age;
+ TAILQ_ENTRY(bridge_if) b_if;
+};
+
+void bridge_ifs_fini(void);
+
+struct bridge_if *bridge_if_find_ifs(uint32_t sysindex);
+
+struct bridge_if *bridge_if_find_ifname(const char *b_name);
+
+const char *bridge_if_find_name(uint32_t sysindex);
+
+int bridge_compare_sysidx(uint32_t i1, uint32_t i2);
+
+int bridge_attach_newif(struct mibif *ifp);
+
+struct bridge_if *bridge_first_bif(void);
+
+struct bridge_if *bridge_next_bif(struct bridge_if *b_pr);
+
+void bridge_remove_bif(struct bridge_if *bif);
+
+void bridge_update_all_ports(void);
+
+void bridge_update_all_addrs(void);
+
+void bridge_update_all_ifs(void);
+
+void bridge_update_all(void *arg);
+
+void bridge_update_tc_time(void *arg);
+
+void bridge_ifs_dump(void);
+
+/* Bridge ports. */
+void bridge_ports_update_listage(void);
+
+void bridge_ports_fini(void);
+
+void bridge_members_free(struct bridge_if *bif);
+
+struct bridge_port *bridge_new_port(struct mibif *mif, struct bridge_if *bif);
+
+void bridge_port_remove(struct bridge_port *bp, struct bridge_if *bif);
+
+struct bridge_port *bridge_port_bif_first(struct bridge_if *bif);
+
+struct bridge_port *bridge_port_bif_next(struct bridge_port *bp);
+
+struct bridge_port *bridge_port_find(int32_t if_idx, struct bridge_if *bif);
+
+void bridge_port_getinfo_mibif(struct mibif *m_if, struct bridge_port *bp);
+
+int bridge_getinfo_bif_ports(struct bridge_if *bif);
+
+int bridge_update_memif(struct bridge_if *bif);
+
+void bridge_ports_dump(struct bridge_if *bif);
+
+/* Bridge addresses. */
+void bridge_addrs_update_listage(void);
+
+void bridge_addrs_fini(void);
+
+void bridge_addrs_free(struct bridge_if *bif);
+
+struct tp_entry *bridge_new_addrs(uint8_t *mac, struct bridge_if *bif);
+
+void bridge_addrs_remove(struct tp_entry *te, struct bridge_if *bif);
+
+struct tp_entry *bridge_addrs_find(uint8_t *mac, struct bridge_if *bif);
+
+struct tp_entry *bridge_addrs_bif_first(struct bridge_if *bif);
+
+struct tp_entry *bridge_addrs_bif_next(struct tp_entry *te);
+
+int bridge_getinfo_bif_addrs(struct bridge_if *bif);
+
+int bridge_update_addrs(struct bridge_if *bif);
+
+void bridge_addrs_dump(struct bridge_if *bif);
+
+/* Bridge PF. */
+
+void bridge_pf_dump(void);
+
+/* System specific. */
+
+/* Open the socket for the ioctls. */
+int bridge_ioctl_init(void);
+
+/* Load bridge kernel module. */
+int bridge_kmod_load(void);
+
+/* Get the bridge interface information. */
+int bridge_getinfo_bif(struct bridge_if *bif);
+
+/* Get the bridge interface STP parameters. */
+int bridge_get_op_param(struct bridge_if *bif);
+
+/* Set the bridge priority. */
+int bridge_set_priority(struct bridge_if *bif, int32_t priority);
+
+/* Set the bridge max age. */
+int bridge_set_maxage(struct bridge_if *bif, int32_t max_age);
+
+/* Set the bridge hello time.*/
+int bridge_set_hello_time(struct bridge_if *bif, int32_t hello_time);
+
+/* Set the bridge forward delay.*/
+int bridge_set_forward_delay(struct bridge_if *bif, int32_t fwd_delay);
+
+/* Set the bridge address cache max age. */
+int bridge_set_aging_time(struct bridge_if *bif, int32_t age_time);
+
+/* Set the max number of entries in the bridge address cache. */
+int bridge_set_max_cache(struct bridge_if *bif, int32_t max_cache);
+
+/* Set the bridge TX hold count. */
+int bridge_set_tx_hold_count(struct bridge_if *bif, int32_t tx_hc);
+
+/* Set the bridge STP protocol version. */
+int bridge_set_stp_version(struct bridge_if *bif, int32_t stp_proto);
+
+/* Set the bridge interface status to up/down. */
+int bridge_set_if_up(const char* b_name, int8_t up);
+
+/* Create a bridge interface. */
+int bridge_create(const char *b_name);
+
+/* Destroy a bridge interface. */
+int bridge_destroy(const char *b_name);
+
+/* Fetch the bridge mac address. */
+u_char *bridge_get_basemac(const char *bif_name, u_char *mac, size_t mlen);
+
+/* Set a bridge member priority. */
+int bridge_port_set_priority(const char *bif_name, struct bridge_port *bp,
+ int32_t priority);
+
+/* Set a bridge member STP-enabled flag. */
+int bridge_port_set_stp_enable(const char *bif_name, struct bridge_port *bp,
+ uint32_t enable);
+
+/* Set a bridge member STP path cost. */
+int bridge_port_set_path_cost(const char *bif_name, struct bridge_port *bp,
+ int32_t path_cost);
+
+/* Set admin point-to-point link. */
+int bridge_port_set_admin_ptp(const char *bif_name, struct bridge_port *bp,
+ uint32_t admin_ptp);
+
+/* Set admin edge. */
+int bridge_port_set_admin_edge(const char *bif_name, struct bridge_port *bp,
+ uint32_t enable);
+
+/* Set 'private' flag. */
+int bridge_port_set_private(const char *bif_name, struct bridge_port *bp,
+ uint32_t priv_set);
+
+/* Add a bridge member port. */
+int bridge_port_addm(struct bridge_port *bp, const char *b_name);
+
+/* Delete a bridge member port. */
+int bridge_port_delm(struct bridge_port *bp, const char *b_name);
+
+/* Get the current value from the module for bridge PF control. */
+int32_t bridge_get_pfval(uint8_t which);
+
+/* Get/Set a bridge PF control. */
+int32_t bridge_do_pfctl(int32_t bridge_ctl, enum snmp_op op, int32_t *val);
+
+#endif /* SNMP_BRIDGE_H */
diff --git a/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_sys.c b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_sys.c
new file mode 100644
index 0000000..9684d68
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_sys.c
@@ -0,0 +1,1503 @@
+/*-
+ * Copyright (c) 2006 Shteryana Shopova <syrinx@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.
+ *
+ * Bridge MIB implementation for SNMPd.
+ * Bridge OS specific ioctls.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <sys/module.h>
+#include <sys/linker.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <net/bridgestp.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_bridgevar.h>
+#include <net/if_dl.h>
+#include <net/if_mib.h>
+#include <net/if_types.h>
+#include <netinet/in.h>
+
+#include <errno.h>
+#include <ifaddrs.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+
+#include <bsnmp/snmpmod.h>
+#include <bsnmp/snmp_mibII.h>
+
+#include "bridge_tree.h"
+#include "bridge_snmp.h"
+
+int sock = -1;
+
+int
+bridge_ioctl_init(void)
+{
+ if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "cannot open socket : %s", strerror(errno));
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Load the if_bridge.ko module in kernel if not already there.
+ */
+int
+bridge_kmod_load(void)
+{
+ int fileid, modid;
+ const char mod_name[] = "if_bridge";
+ struct module_stat mstat;
+
+ /* 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;
+
+ if (strcmp(mod_name, mstat.name) == 0)
+ return (0);
+ }
+ }
+
+ /* Not present - load it. */
+ if (kldload(mod_name) < 0) {
+ syslog(LOG_ERR, "failed to load %s kernel module", mod_name);
+ return (-1);
+ }
+
+ return (1);
+}
+
+/************************************************************************
+ * Bridge interfaces.
+ */
+
+/*
+ * Convert the kernel uint64_t value for a bridge id
+ */
+static void
+snmp_uint64_to_bridgeid(uint64_t id, bridge_id b_id)
+{
+ int i;
+ u_char *o;
+
+ o = (u_char *) &id;
+
+ for (i = 0; i < SNMP_BRIDGE_ID_LEN; i++, o++)
+ b_id[SNMP_BRIDGE_ID_LEN - i - 1] = *o;
+}
+
+/*
+ * Fetch the bridge configuration parameters from the kernel excluding
+ * it's base MAC address.
+ */
+static int
+bridge_get_conf_param(struct bridge_if *bif)
+{
+ struct ifdrv ifd;
+ struct ifbrparam b_param;
+
+ strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ);
+ ifd.ifd_len = sizeof(b_param);
+ ifd.ifd_data = &b_param;
+
+ /* Bridge priority. */
+ ifd.ifd_cmd = BRDGGPRI;
+ if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "update bridge: ioctl(BRDGGPRI) failed: %s",
+ strerror(errno));
+ return (-1);
+ }
+
+ bif->priority = b_param.ifbrp_prio;
+
+ /* Configured max age. */
+ ifd.ifd_cmd = BRDGGMA;
+ if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "update bridge: ioctl(BRDGGMA) failed: %s",
+ strerror(errno));
+ return (-1);
+ }
+
+ /* Centi-seconds. */
+ bif->bridge_max_age = 100 * b_param.ifbrp_maxage;
+
+ /* Configured hello time. */
+ ifd.ifd_cmd = BRDGGHT;
+ if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "update bridge: ioctl(BRDGGHT) failed: %s",
+ strerror(errno));
+ return (-1);
+ }
+ bif->bridge_hello_time = 100 * b_param.ifbrp_hellotime;
+
+ /* Forward delay. */
+ ifd.ifd_cmd = BRDGGFD;
+ if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "update bridge: ioctl(BRDGGFD) failed: %s",
+ strerror(errno));
+ return (-1);
+ }
+ bif->bridge_fwd_delay = 100 * b_param.ifbrp_fwddelay;
+
+ /* Number of dropped addresses. */
+ ifd.ifd_cmd = BRDGGRTE;
+ if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "update bridge: ioctl(BRDGGRTE) failed: %s",
+ strerror(errno));
+ return (-1);
+ }
+ bif->lrnt_drops = b_param.ifbrp_cexceeded;
+
+ /* Address table timeout. */
+ ifd.ifd_cmd = BRDGGTO;
+ if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "update bridge: ioctl(BRDGGTO) failed: %s",
+ strerror(errno));
+ return (-1);
+ }
+ bif->age_time = b_param.ifbrp_ctime;
+
+ /* Address table size. */
+ ifd.ifd_cmd = BRDGGCACHE;
+ if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "update bridge: ioctl(BRDGGCACHE) "
+ "failed: %s", strerror(errno));
+ return (-1);
+ }
+ bif->max_addrs = b_param.ifbrp_csize;
+
+ return (0);
+}
+
+/*
+ * Fetch the current bridge STP operational parameters.
+ * Returns: -1 - on error;
+ * 0 - old TC time and Root Port values are same;
+ * 1 - topologyChange notification should be sent;
+ * 2 - newRoot notification should be sent.
+ */
+int
+bridge_get_op_param(struct bridge_if *bif)
+{
+ int new_root_send;
+ struct ifdrv ifd;
+ struct ifbropreq b_req;
+
+ strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ);
+ ifd.ifd_len = sizeof(b_req);
+ ifd.ifd_data = &b_req;
+ ifd.ifd_cmd = BRDGPARAM;
+
+ if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "update bridge: ioctl(BRDGPARAM) failed: %s",
+ strerror(errno));
+ return (-1);
+ }
+
+ bif->max_age = 100 * b_req.ifbop_maxage;
+ bif->hello_time = 100 * b_req.ifbop_hellotime;
+ bif->fwd_delay = 100 * b_req.ifbop_fwddelay;
+ bif->stp_version = b_req.ifbop_protocol;
+ bif->tx_hold_count = b_req.ifbop_holdcount;
+
+ if (b_req.ifbop_root_port == 0 &&
+ bif->root_port != b_req.ifbop_root_port)
+ new_root_send = 2;
+ else
+ new_root_send = 0;
+
+ bif->root_port = b_req.ifbop_root_port;
+ bif->root_cost = b_req.ifbop_root_path_cost;
+ snmp_uint64_to_bridgeid(b_req.ifbop_designated_root,
+ bif->design_root);
+
+ if (bif->last_tc_time.tv_sec != b_req.ifbop_last_tc_time.tv_sec) {
+ bif->top_changes++;
+ bif->last_tc_time.tv_sec = b_req.ifbop_last_tc_time.tv_sec;
+ bif->last_tc_time.tv_usec = b_req.ifbop_last_tc_time.tv_usec;
+
+ /*
+ * "The trap is not sent if a (begemotBridge)NewRoot
+ * trap is sent for the same transition."
+ */
+ if (new_root_send == 0)
+ return (1);
+ }
+
+ return (new_root_send);
+}
+
+int
+bridge_getinfo_bif(struct bridge_if *bif)
+{
+ if (bridge_get_conf_param(bif) < 0)
+ return (-1);
+
+ return (bridge_get_op_param(bif));
+}
+
+int
+bridge_set_priority(struct bridge_if *bif, int32_t priority)
+{
+ struct ifdrv ifd;
+ struct ifbrparam b_param;
+
+ strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ);
+ ifd.ifd_len = sizeof(b_param);
+ ifd.ifd_data = &b_param;
+ b_param.ifbrp_prio = (uint32_t) priority;
+ ifd.ifd_cmd = BRDGSPRI;
+
+ if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "set bridge param: ioctl(BRDGSPRI) "
+ "failed: %s", strerror(errno));
+ return (-1);
+ }
+
+ /*
+ * Re-fetching the data from the driver after that might be a good
+ * idea, since changing our bridge's priority should invoke
+ * recalculation of the active spanning tree topology in the network.
+ */
+ bif->priority = priority;
+ return (0);
+}
+
+/*
+ * Convert 1/100 of seconds to 1/256 of seconds.
+ * Timeout ::= TEXTUAL-CONVENTION.
+ * To convert a Timeout value into a value in units of
+ * 1/256 seconds, the following algorithm should be used:
+ * b = floor( (n * 256) / 100)
+ * The conversion to 1/256 of a second happens in the kernel -
+ * just make sure we correctly convert the seconds to Timout
+ * and vice versa.
+ */
+static uint32_t
+snmp_timeout2_sec(int32_t secs)
+{
+ return (secs / 100);
+}
+
+int
+bridge_set_maxage(struct bridge_if *bif, int32_t max_age)
+{
+ struct ifdrv ifd;
+ struct ifbrparam b_param;
+
+ strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ);
+ ifd.ifd_len = sizeof(b_param);
+ ifd.ifd_data = &b_param;
+ b_param.ifbrp_maxage = snmp_timeout2_sec(max_age);
+ ifd.ifd_cmd = BRDGSMA;
+
+ if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "set bridge param: ioctl(BRDGSMA) "
+ "failed: %s", strerror(errno));
+ return (-1);
+ }
+
+ bif->bridge_max_age = max_age;
+ return (0);
+}
+
+int
+bridge_set_hello_time(struct bridge_if *bif, int32_t hello_time)
+{
+ struct ifdrv ifd;
+ struct ifbrparam b_param;
+
+ strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ);
+ ifd.ifd_len = sizeof(b_param);
+ ifd.ifd_data = &b_param;
+ b_param.ifbrp_hellotime = snmp_timeout2_sec(hello_time);
+ ifd.ifd_cmd = BRDGSHT;
+
+ if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "set bridge param: ioctl(BRDGSHT) "
+ "failed: %s", strerror(errno));
+ return (-1);
+ }
+
+ bif->bridge_hello_time = b_param.ifbrp_hellotime;
+ return (0);
+}
+
+int
+bridge_set_forward_delay(struct bridge_if *bif, int32_t fwd_delay)
+{
+ struct ifdrv ifd;
+ struct ifbrparam b_param;
+
+ strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ);
+ ifd.ifd_len = sizeof(b_param);
+ ifd.ifd_data = &b_param;
+ b_param.ifbrp_fwddelay = snmp_timeout2_sec(fwd_delay);
+ ifd.ifd_cmd = BRDGSFD;
+
+ if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "set bridge param: ioctl(BRDGSFD) "
+ "failed: %s", strerror(errno));
+ return (-1);
+ }
+
+ bif->bridge_fwd_delay = b_param.ifbrp_fwddelay;
+ return (0);
+}
+
+int
+bridge_set_aging_time(struct bridge_if *bif, int32_t age_time)
+{
+ struct ifdrv ifd;
+ struct ifbrparam b_param;
+
+ strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ);
+ ifd.ifd_len = sizeof(b_param);
+ ifd.ifd_data = &b_param;
+ b_param.ifbrp_ctime = (uint32_t) age_time;
+ ifd.ifd_cmd = BRDGSTO;
+
+ if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "set bridge param: ioctl(BRDGSTO) "
+ "failed: %s", strerror(errno));
+ return (-1);
+ }
+
+ bif->age_time = age_time;
+ return (0);
+}
+
+int
+bridge_set_max_cache(struct bridge_if *bif, int32_t max_cache)
+{
+ struct ifdrv ifd;
+ struct ifbrparam b_param;
+
+ strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ);
+ ifd.ifd_len = sizeof(b_param);
+ ifd.ifd_data = &b_param;
+ b_param.ifbrp_csize = max_cache;
+ ifd.ifd_cmd = BRDGSCACHE;
+
+ if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "set bridge param: ioctl(BRDGSCACHE) "
+ "failed: %s", strerror(errno));
+ return (-1);
+ }
+
+ bif->max_addrs = b_param.ifbrp_csize;
+ return (0);
+}
+
+int
+bridge_set_tx_hold_count(struct bridge_if *bif, int32_t tx_hc)
+{
+ struct ifdrv ifd;
+ struct ifbrparam b_param;
+
+ if (tx_hc < SNMP_BRIDGE_MIN_TXHC || tx_hc > SNMP_BRIDGE_MAX_TXHC)
+ return (-1);
+
+ strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ);
+ ifd.ifd_len = sizeof(b_param);
+ ifd.ifd_data = &b_param;
+ b_param.ifbrp_txhc = tx_hc;
+ ifd.ifd_cmd = BRDGSTXHC;
+
+ if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "set bridge param: ioctl(BRDGSTXHC) "
+ "failed: %s", strerror(errno));
+ return (-1);
+ }
+
+ bif->tx_hold_count = b_param.ifbrp_txhc;
+ return (0);
+}
+
+int
+bridge_set_stp_version(struct bridge_if *bif, int32_t stp_proto)
+{
+ struct ifdrv ifd;
+ struct ifbrparam b_param;
+
+ strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ);
+ ifd.ifd_len = sizeof(b_param);
+ ifd.ifd_data = &b_param;
+ b_param.ifbrp_proto = stp_proto;
+ ifd.ifd_cmd = BRDGSPROTO;
+
+ if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "set bridge param: ioctl(BRDGSPROTO) "
+ "failed: %s", strerror(errno));
+ return (-1);
+ }
+
+ bif->stp_version = b_param.ifbrp_proto;
+ return (0);
+}
+
+/*
+ * Set the bridge interface status to up/down.
+ */
+int
+bridge_set_if_up(const char* b_name, int8_t up)
+{
+ int flags;
+ struct ifreq ifr;
+
+ bzero(&ifr, sizeof(ifr));
+ strcpy(ifr.ifr_name, b_name);
+ if (ioctl(sock, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
+ syslog(LOG_ERR, "set bridge up: ioctl(SIOCGIFFLAGS) "
+ "failed: %s", strerror(errno));
+ return (-1);
+ }
+
+ flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16);
+ if (up == 1)
+ flags |= IFF_UP;
+ else
+ flags &= ~IFF_UP;
+
+ ifr.ifr_flags = flags & 0xffff;
+ ifr.ifr_flagshigh = flags >> 16;
+ if (ioctl(sock, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
+ syslog(LOG_ERR, "set bridge up: ioctl(SIOCSIFFLAGS) "
+ "failed: %s", strerror(errno));
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+bridge_create(const char *b_name)
+{
+ char *new_name;
+ struct ifreq ifr;
+
+ bzero(&ifr, sizeof(ifr));
+ strcpy(ifr.ifr_name, b_name);
+
+ if (ioctl(sock, SIOCIFCREATE, &ifr) < 0) {
+ syslog(LOG_ERR, "create bridge: ioctl(SIOCIFCREATE) "
+ "failed: %s", strerror(errno));
+ return (-1);
+ }
+
+ if (strcmp(b_name, ifr.ifr_name) == 0)
+ return (0);
+
+ if ((new_name = strdup(b_name)) == NULL) {
+ syslog(LOG_ERR, "create bridge: strdup() failed");
+ return (-1);
+ }
+
+ ifr.ifr_data = new_name;
+ if (ioctl(sock, SIOCSIFNAME, (caddr_t) &ifr) < 0) {
+ syslog(LOG_ERR, "create bridge: ioctl(SIOCSIFNAME) "
+ "failed: %s", strerror(errno));
+ free(new_name);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+bridge_destroy(const char *b_name)
+{
+ struct ifreq ifr;
+
+ bzero(&ifr, sizeof(ifr));
+ strcpy(ifr.ifr_name, b_name);
+
+ if (ioctl(sock, SIOCIFDESTROY, &ifr) < 0) {
+ syslog(LOG_ERR, "destroy bridge: ioctl(SIOCIFDESTROY) "
+ "failed: %s", strerror(errno));
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Fetch the bridge base MAC address. Return pointer to the
+ * buffer containing the MAC address, NULL on failure.
+ */
+u_char *
+bridge_get_basemac(const char *bif_name, u_char *mac, size_t mlen)
+{
+ int len;
+ char if_name[IFNAMSIZ];
+ struct ifaddrs *ifap, *ifa;
+ struct sockaddr_dl sdl;
+
+ if (getifaddrs(&ifap) != 0) {
+ syslog(LOG_ERR, "bridge get mac: getifaddrs() failed - %s",
+ strerror(errno));
+ return (NULL);
+ }
+
+ for (ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr->sa_family != AF_LINK)
+ continue;
+
+ /*
+ * Not just casting because of alignment constraints
+ * on sparc64 and ia64.
+ */
+ bcopy(ifa->ifa_addr, &sdl, sizeof(struct sockaddr_dl));
+
+ if (sdl.sdl_alen > mlen)
+ continue;
+
+ if ((len = sdl.sdl_nlen) >= IFNAMSIZ)
+ len = IFNAMSIZ - 1;
+
+ bcopy(sdl.sdl_data, if_name, len);
+ if_name[len] = '\0';
+
+ if (strcmp(bif_name, if_name) == 0) {
+ bcopy(sdl.sdl_data + sdl.sdl_nlen, mac, sdl.sdl_alen);
+ freeifaddrs(ifap);
+ return (mac);
+ }
+ }
+
+ freeifaddrs(ifap);
+ return (NULL);
+}
+
+/************************************************************************
+ * Bridge ports.
+ */
+
+/*
+ * Convert the kernel STP port state into
+ * the corresopnding enumerated type from SNMP Bridge MIB.
+ */
+static int
+state2snmp_st(uint8_t ifbr_state)
+{
+ switch (ifbr_state) {
+ case BSTP_IFSTATE_DISABLED:
+ return (StpPortState_disabled);
+ case BSTP_IFSTATE_LISTENING:
+ return (StpPortState_listening);
+ case BSTP_IFSTATE_LEARNING:
+ return (StpPortState_learning);
+ case BSTP_IFSTATE_FORWARDING:
+ return (StpPortState_forwarding);
+ case BSTP_IFSTATE_BLOCKING:
+ case BSTP_IFSTATE_DISCARDING:
+ return (StpPortState_blocking);
+ }
+
+ return (StpPortState_broken);
+}
+
+/*
+ * Fill in a bridge member information according to data polled from kernel.
+ */
+static void
+bridge_port_getinfo_conf(struct ifbreq *k_info, struct bridge_port *bp)
+{
+ bp->state = state2snmp_st(k_info->ifbr_state);
+ bp->priority = k_info->ifbr_priority;
+
+ /*
+ * RFC 4188:
+ * "New implementations should support dot1dStpPortPathCost32.
+ * If the port path costs exceeds the maximum value of this
+ * object then this object should report the maximum value,
+ * namely 65535. Applications should try to read the
+ * dot1dStpPortPathCost32 object if this object reports
+ * the maximum value."
+ */
+
+ if (k_info->ifbr_ifsflags & IFBIF_BSTP_ADMCOST)
+ bp->admin_path_cost = k_info->ifbr_path_cost;
+ else
+ bp->admin_path_cost = 0;
+
+ bp->path_cost = k_info->ifbr_path_cost;
+
+ if (k_info->ifbr_ifsflags & IFBIF_STP)
+ bp->enable = dot1dStpPortEnable_enabled;
+ else
+ bp->enable = dot1dStpPortEnable_disabled;
+
+ /* Begemot Bridge MIB only. */
+ if (k_info->ifbr_ifsflags & IFBIF_SPAN)
+ bp->span_enable = begemotBridgeBaseSpanEnabled_enabled;
+ else
+ bp->span_enable = begemotBridgeBaseSpanEnabled_disabled;
+
+ if (k_info->ifbr_ifsflags & IFBIF_PRIVATE)
+ bp->priv_set = TruthValue_true;
+ else
+ bp->priv_set = TruthValue_false;
+
+ if (k_info->ifbr_ifsflags & IFBIF_BSTP_ADMEDGE)
+ bp->admin_edge = TruthValue_true;
+ else
+ bp->admin_edge = TruthValue_false;
+
+ if (k_info->ifbr_ifsflags & IFBIF_BSTP_EDGE)
+ bp->oper_edge = TruthValue_true;
+ else
+ bp->oper_edge = TruthValue_false;
+
+ if (k_info->ifbr_ifsflags & IFBIF_BSTP_AUTOPTP) {
+ bp->admin_ptp = StpPortAdminPointToPointType_auto;
+ if (k_info->ifbr_ifsflags & IFBIF_BSTP_PTP)
+ bp->oper_ptp = TruthValue_true;
+ else
+ bp->oper_ptp = TruthValue_false;
+ } else if (k_info->ifbr_ifsflags & IFBIF_BSTP_PTP) {
+ bp->admin_ptp = StpPortAdminPointToPointType_forceTrue;
+ bp->oper_ptp = TruthValue_true;
+ } else {
+ bp->admin_ptp = StpPortAdminPointToPointType_forceFalse;
+ bp->oper_ptp = TruthValue_false;
+ }
+}
+
+/*
+ * Fill in a bridge interface STP information according to
+ * data polled from kernel.
+ */
+static void
+bridge_port_getinfo_opstp(struct ifbpstpreq *bp_stp, struct bridge_port *bp)
+{
+ bp->enable = dot1dStpPortEnable_enabled;
+ bp->fwd_trans = bp_stp->ifbp_fwd_trans;
+ bp->design_cost = bp_stp->ifbp_design_cost;
+ snmp_uint64_to_bridgeid(bp_stp->ifbp_design_root, bp->design_root);
+ snmp_uint64_to_bridgeid(bp_stp->ifbp_design_bridge, bp->design_bridge);
+ bcopy(&(bp_stp->ifbp_design_port), &(bp->design_port),
+ sizeof(uint16_t));
+}
+
+/*
+ * Clear a bridge interface STP information.
+ */
+static void
+bridge_port_clearinfo_opstp(struct bridge_port *bp)
+{
+ if (bp->enable == dot1dStpPortEnable_enabled) {
+ bp->design_cost = 0;
+ bzero(&(bp->design_root), sizeof(bridge_id));
+ bzero(&(bp->design_bridge), sizeof(bridge_id));
+ bzero(&(bp->design_port), sizeof(port_id));
+ bp->fwd_trans = 0;
+ }
+
+ bp->enable = dot1dStpPortEnable_disabled;
+}
+
+/*
+ * Set a bridge member priority.
+ */
+int
+bridge_port_set_priority(const char *bif_name, struct bridge_port *bp,
+ int32_t priority)
+{
+ struct ifdrv ifd;
+ struct ifbreq b_req;
+
+ strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name));
+ ifd.ifd_len = sizeof(b_req);
+ ifd.ifd_data = &b_req;
+ strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname));
+
+ b_req.ifbr_priority = (uint8_t) priority;
+ ifd.ifd_cmd = BRDGSIFPRIO;
+
+ if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFPRIO) "
+ "failed: %s", bp->p_name, strerror(errno));
+ return (-1);
+ }
+
+ bp->priority = priority;
+ return (0);
+}
+
+/*
+ * Set a bridge member STP-enabled flag.
+ */
+int
+bridge_port_set_stp_enable(const char *bif_name, struct bridge_port *bp,
+ uint32_t enable)
+{
+ struct ifdrv ifd;
+ struct ifbreq b_req;
+
+ if (bp->enable == enable)
+ return (0);
+
+ bzero(&b_req, sizeof(b_req));
+ strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name));
+ ifd.ifd_len = sizeof(b_req);
+ ifd.ifd_data = &b_req;
+ strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname));
+ ifd.ifd_cmd = BRDGGIFFLGS;
+
+ if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "get member %s param: ioctl(BRDGGIFFLGS) "
+ "failed: %s", bp->p_name, strerror(errno));
+ return (-1);
+ }
+
+ if (enable == dot1dStpPortEnable_enabled)
+ b_req.ifbr_ifsflags |= IFBIF_STP;
+ else
+ b_req.ifbr_ifsflags &= ~IFBIF_STP;
+
+ ifd.ifd_cmd = BRDGSIFFLGS;
+ if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFFLGS) "
+ "failed: %s", bp->p_name, strerror(errno));
+ return (-1);
+ }
+
+ bp->enable = enable;
+ return (0);
+}
+
+/*
+ * Set a bridge member STP path cost.
+ */
+int
+bridge_port_set_path_cost(const char *bif_name, struct bridge_port *bp,
+ int32_t path_cost)
+{
+ struct ifdrv ifd;
+ struct ifbreq b_req;
+
+ if (path_cost < SNMP_PORT_MIN_PATHCOST ||
+ path_cost > SNMP_PORT_PATHCOST_OBSOLETE)
+ return (-2);
+
+ strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name));
+ ifd.ifd_len = sizeof(b_req);
+ ifd.ifd_data = &b_req;
+ strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname));
+
+ b_req.ifbr_path_cost = path_cost;
+ ifd.ifd_cmd = BRDGSIFCOST;
+
+ if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFCOST) "
+ "failed: %s", bp->p_name, strerror(errno));
+ return (-1);
+ }
+
+ bp->admin_path_cost = path_cost;
+
+ return (0);
+}
+
+/*
+ * Set the PonitToPoint status of the link administratively.
+ */
+int
+bridge_port_set_admin_ptp(const char *bif_name, struct bridge_port *bp,
+ uint32_t admin_ptp)
+{
+ struct ifdrv ifd;
+ struct ifbreq b_req;
+
+ if (bp->admin_ptp == admin_ptp)
+ return (0);
+
+ bzero(&b_req, sizeof(b_req));
+ strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name));
+ ifd.ifd_len = sizeof(b_req);
+ ifd.ifd_data = &b_req;
+ strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname));
+ ifd.ifd_cmd = BRDGGIFFLGS;
+
+ if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "get member %s param: ioctl(BRDGGIFFLGS) "
+ "failed: %s", bp->p_name, strerror(errno));
+ return (-1);
+ }
+
+ switch (admin_ptp) {
+ case StpPortAdminPointToPointType_forceTrue:
+ b_req.ifbr_ifsflags &= ~IFBIF_BSTP_AUTOPTP;
+ b_req.ifbr_ifsflags |= IFBIF_BSTP_PTP;
+ break;
+ case StpPortAdminPointToPointType_forceFalse:
+ b_req.ifbr_ifsflags &= ~IFBIF_BSTP_AUTOPTP;
+ b_req.ifbr_ifsflags &= ~IFBIF_BSTP_PTP;
+ break;
+ case StpPortAdminPointToPointType_auto:
+ b_req.ifbr_ifsflags |= IFBIF_BSTP_AUTOPTP;
+ break;
+ }
+
+ ifd.ifd_cmd = BRDGSIFFLGS;
+ if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFFLGS) "
+ "failed: %s", bp->p_name, strerror(errno));
+ return (-1);
+ }
+
+ bp->admin_ptp = admin_ptp;
+ return (0);
+}
+
+/*
+ * Set admin edge.
+ */
+int
+bridge_port_set_admin_edge(const char *bif_name, struct bridge_port *bp,
+ uint32_t enable)
+{
+ struct ifdrv ifd;
+ struct ifbreq b_req;
+
+ if (bp->admin_edge == enable)
+ return (0);
+
+ bzero(&b_req, sizeof(b_req));
+ strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name));
+ ifd.ifd_len = sizeof(b_req);
+ ifd.ifd_data = &b_req;
+ strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname));
+ ifd.ifd_cmd = BRDGGIFFLGS;
+
+ if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "get member %s param: ioctl(BRDGGIFFLGS) "
+ "failed: %s", bp->p_name, strerror(errno));
+ return (-1);
+ }
+
+ if (enable == TruthValue_true) {
+ b_req.ifbr_ifsflags &= ~IFBIF_BSTP_AUTOEDGE;
+ b_req.ifbr_ifsflags |= IFBIF_BSTP_EDGE;
+ } else
+ b_req.ifbr_ifsflags &= ~IFBIF_BSTP_EDGE;
+
+ ifd.ifd_cmd = BRDGSIFFLGS;
+ if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFFLGS) "
+ "failed: %s", bp->p_name, strerror(errno));
+ return (-1);
+ }
+
+ bp->admin_edge = enable;
+
+ return (0);
+}
+
+/*
+ * Set 'private' flag.
+ */
+int
+bridge_port_set_private(const char *bif_name, struct bridge_port *bp,
+ uint32_t priv_set)
+{
+ struct ifdrv ifd;
+ struct ifbreq b_req;
+
+ if (bp->priv_set == priv_set)
+ return (0);
+
+ bzero(&b_req, sizeof(b_req));
+ strlcpy(ifd.ifd_name, bif_name, sizeof(ifd.ifd_name));
+ ifd.ifd_len = sizeof(b_req);
+ ifd.ifd_data = &b_req;
+ strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname));
+ ifd.ifd_cmd = BRDGGIFFLGS;
+
+ if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "get member %s param: ioctl(BRDGGIFFLGS) "
+ "failed: %s", bp->p_name, strerror(errno));
+ return (-1);
+ }
+
+ if (priv_set == TruthValue_true)
+ b_req.ifbr_ifsflags |= IFBIF_PRIVATE;
+ else if (priv_set == TruthValue_false)
+ b_req.ifbr_ifsflags &= ~IFBIF_PRIVATE;
+ else
+ return (SNMP_ERR_WRONG_VALUE);
+
+ ifd.ifd_cmd = BRDGSIFFLGS;
+ if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "set member %s param: ioctl(BRDGSIFFLGS) "
+ "failed: %s", bp->p_name, strerror(errno));
+ return (-1);
+ }
+
+ bp->priv_set = priv_set;
+
+ return (0);
+}
+
+
+/*
+ * Add a bridge member port.
+ */
+int
+bridge_port_addm(struct bridge_port *bp, const char *b_name)
+{
+ struct ifdrv ifd;
+ struct ifbreq b_req;
+
+ bzero(&ifd, sizeof(ifd));
+ bzero(&b_req, sizeof(b_req));
+
+ strlcpy(ifd.ifd_name, b_name, sizeof(ifd.ifd_name));
+ ifd.ifd_len = sizeof(b_req);
+ ifd.ifd_data = &b_req;
+ strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname));
+
+ if (bp->span_enable == begemotBridgeBaseSpanEnabled_enabled)
+ ifd.ifd_cmd = BRDGADDS;
+ else
+ ifd.ifd_cmd = BRDGADD;
+
+ if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "%s - add member : ioctl(%s) failed: %s",
+ bp->p_name,
+ (ifd.ifd_cmd == BRDGADDS ? "BRDGADDS" : "BRDGADD"),
+ strerror(errno));
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Delete a bridge member port.
+ */
+int
+bridge_port_delm(struct bridge_port *bp, const char *b_name)
+{
+ struct ifdrv ifd;
+ struct ifbreq b_req;
+
+ bzero(&ifd, sizeof(ifd));
+ bzero(&b_req, sizeof(b_req));
+
+ strlcpy(ifd.ifd_name, b_name, sizeof(ifd.ifd_name));
+ ifd.ifd_len = sizeof(b_req);
+ ifd.ifd_data = &b_req;
+ strlcpy(b_req.ifbr_ifsname, bp->p_name, sizeof(b_req.ifbr_ifsname));
+
+ if (bp->span_enable == begemotBridgeBaseSpanEnabled_enabled)
+ ifd.ifd_cmd = BRDGDELS;
+ else
+ ifd.ifd_cmd = BRDGDEL;
+
+ if (ioctl(sock, SIOCSDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "%s - add member : ioctl(%s) failed: %s",
+ bp->p_name,
+ (ifd.ifd_cmd == BRDGDELS ? "BRDGDELS" : "BRDGDEL"),
+ strerror(errno));
+ return (-1);
+ }
+
+ return (0);
+}
+
+/*
+ * Fetch the bridge member list from kernel.
+ * Return -1 on error, or buffer len if successful.
+ */
+static int32_t
+bridge_port_get_iflist(struct bridge_if *bif, struct ifbreq **buf)
+{
+ int n = 128;
+ uint32_t len;
+ struct ifbreq *ninbuf;
+ struct ifbifconf ifbc;
+ struct ifdrv ifd;
+
+ *buf = NULL;
+ strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ);
+ ifd.ifd_cmd = BRDGGIFS;
+ ifd.ifd_len = sizeof(ifbc);
+ ifd.ifd_data = &ifbc;
+
+ for ( ; ; ) {
+ len = n * sizeof(struct ifbreq);
+ if ((ninbuf = (struct ifbreq *)realloc(*buf, len)) == NULL) {
+ syslog(LOG_ERR, "get bridge member list: "
+ "realloc failed: %s", strerror(errno));
+ free(*buf);
+ *buf = NULL;
+ return (-1);
+ }
+
+ ifbc.ifbic_len = len;
+ ifbc.ifbic_req = *buf = ninbuf;
+
+ if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "get bridge member list: ioctl "
+ "(BRDGGIFS) failed: %s", strerror(errno));
+ free(*buf);
+ buf = NULL;
+ return (-1);
+ }
+
+ if ((ifbc.ifbic_len + sizeof(struct ifbreq)) < len)
+ break;
+
+ n += 64;
+ }
+
+ return (ifbc.ifbic_len);
+}
+
+/*
+ * Fetch the bridge STP member list from kernel.
+ * Return -1 on error, or buffer len if successful.
+ */
+static int32_t
+bridge_port_get_ifstplist(struct bridge_if *bif, struct ifbpstpreq **buf)
+{
+ int n = 128;
+ uint32_t len;
+ struct ifbpstpreq *ninbuf;
+ struct ifbpstpconf ifbstp;
+ struct ifdrv ifd;
+
+ *buf = NULL;
+ strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ);
+ ifd.ifd_cmd = BRDGGIFSSTP;
+ ifd.ifd_len = sizeof(ifbstp);
+ ifd.ifd_data = &ifbstp;
+
+ for ( ; ; ) {
+ len = n * sizeof(struct ifbpstpreq);
+ if ((ninbuf = (struct ifbpstpreq *)
+ realloc(*buf, len)) == NULL) {
+ syslog(LOG_ERR, "get bridge STP ports list: "
+ "realloc failed: %s", strerror(errno));
+ free(*buf);
+ *buf = NULL;
+ return (-1);
+ }
+
+ ifbstp.ifbpstp_len = len;
+ ifbstp.ifbpstp_req = *buf = ninbuf;
+
+ if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "get bridge STP ports list: ioctl "
+ "(BRDGGIFSSTP) failed: %s", strerror(errno));
+ free(*buf);
+ buf = NULL;
+ return (-1);
+ }
+
+ if ((ifbstp.ifbpstp_len + sizeof(struct ifbpstpreq)) < len)
+ break;
+
+ n += 64;
+ }
+
+ return (ifbstp.ifbpstp_len);
+}
+
+/*
+ * Locate a bridge if STP params structure in a buffer.
+ */
+static struct ifbpstpreq *
+bridge_port_find_ifstplist(uint8_t port_no, struct ifbpstpreq *buf,
+ uint32_t buf_len)
+{
+ uint32_t i;
+ struct ifbpstpreq *bstp;
+
+ for (i = 0; i < buf_len / sizeof(struct ifbpstpreq); i++) {
+ bstp = buf + i;
+ if (bstp->ifbp_portno == port_no)
+ return (bstp);
+ }
+
+ return (NULL);
+}
+
+/*
+ * Read the initial info for all members of a bridge interface.
+ * Returns the number of ports, 0 - if none, otherwise
+ * -1 if some other error occured.
+ */
+int
+bridge_getinfo_bif_ports(struct bridge_if *bif)
+{
+ uint32_t i;
+ int32_t buf_len;
+ struct ifbreq *b_req_buf, *b_req;
+ struct ifbpstpreq *bs_req_buf, *bs_req;
+ struct bridge_port *bp;
+ struct mibif *m_if;
+
+ if ((buf_len = bridge_port_get_iflist(bif, &b_req_buf)) < 0)
+ return (-1);
+
+ for (i = 0; i < buf_len / sizeof(struct ifbreq); i++) {
+ b_req = b_req_buf + i;
+
+ if ((m_if = mib_find_if_sys(b_req->ifbr_portno)) != NULL) {
+ /* Hopefully we will not fail here. */
+ if ((bp = bridge_new_port(m_if, bif)) != NULL) {
+ bp->status = RowStatus_active;
+ bridge_port_getinfo_conf(b_req, bp);
+ bridge_port_getinfo_mibif(m_if, bp);
+ }
+ } else {
+ syslog(LOG_ERR, "bridge member %s not present "
+ "in mibII ifTable", b_req->ifbr_ifsname);
+ }
+ }
+ free(b_req_buf);
+
+ if ((buf_len = bridge_port_get_ifstplist(bif, &bs_req_buf)) < 0)
+ return (-1);
+
+ for (bp = bridge_port_bif_first(bif); bp != NULL;
+ bp = bridge_port_bif_next(bp)) {
+ if ((bs_req = bridge_port_find_ifstplist(bp->port_no,
+ bs_req_buf, buf_len)) == NULL)
+ bridge_port_clearinfo_opstp(bp);
+ else
+ bridge_port_getinfo_opstp(bs_req, bp);
+ }
+ free(bs_req_buf);
+
+ return (i);
+}
+
+/*
+ * Update the information for the bridge interface members.
+ */
+int
+bridge_update_memif(struct bridge_if *bif)
+{
+ int added, updated;
+ uint32_t i;
+ int32_t buf_len;
+ struct ifbreq *b_req_buf, *b_req;
+ struct ifbpstpreq *bs_req_buf, *bs_req;
+ struct bridge_port *bp, *bp_next;
+ struct mibif *m_if;
+
+ if ((buf_len = bridge_port_get_iflist(bif, &b_req_buf)) < 0)
+ return (-1);
+
+ added = updated = 0;
+
+#define BP_FOUND 0x01
+ for (i = 0; i < buf_len / sizeof(struct ifbreq); i++) {
+ b_req = b_req_buf + i;
+
+ if ((m_if = mib_find_if_sys(b_req->ifbr_portno)) == NULL) {
+ syslog(LOG_ERR, "bridge member %s not present "
+ "in mibII ifTable", b_req->ifbr_ifsname);
+ continue;
+ }
+
+ if ((bp = bridge_port_find(m_if->index, bif)) == NULL &&
+ (bp = bridge_new_port(m_if, bif)) != NULL) {
+ bp->status = RowStatus_active;
+ added++;
+ }
+
+ if (bp != NULL) {
+ updated++;
+ bridge_port_getinfo_conf(b_req, bp);
+ bridge_port_getinfo_mibif(m_if, bp);
+ bp->flags |= BP_FOUND;
+ }
+ }
+ free(b_req_buf);
+
+ /* Clean up list. */
+ for (bp = bridge_port_bif_first(bif); bp != NULL; bp = bp_next) {
+ bp_next = bridge_port_bif_next(bp);
+
+ if ((bp->flags & BP_FOUND) == 0 &&
+ bp->status == RowStatus_active)
+ bridge_port_remove(bp, bif);
+ else
+ bp->flags |= ~BP_FOUND;
+ }
+#undef BP_FOUND
+
+ if ((buf_len = bridge_port_get_ifstplist(bif, &bs_req_buf)) < 0)
+ return (-1);
+
+ for (bp = bridge_port_bif_first(bif); bp != NULL;
+ bp = bridge_port_bif_next(bp)) {
+ if ((bs_req = bridge_port_find_ifstplist(bp->port_no,
+ bs_req_buf, buf_len)) == NULL)
+ bridge_port_clearinfo_opstp(bp);
+ else
+ bridge_port_getinfo_opstp(bs_req, bp);
+ }
+ free(bs_req_buf);
+ bif->ports_age = time(NULL);
+
+ return (updated);
+}
+
+/************************************************************************
+ * Bridge addresses.
+ */
+
+/*
+ * Update the bridge address info according to the polled data.
+ */
+static void
+bridge_addrs_info_ifaddrlist(struct ifbareq *ifba, struct tp_entry *tpe)
+{
+ tpe->port_no = if_nametoindex(ifba->ifba_ifsname);
+
+ if ((ifba->ifba_flags & IFBAF_TYPEMASK) == IFBAF_STATIC)
+ tpe->status = TpFdbStatus_mgmt;
+ else
+ tpe->status = TpFdbStatus_learned;
+}
+
+/*
+ * Read the bridge addresses from kernel.
+ * Return -1 on error, or buffer len if successful.
+ */
+static int32_t
+bridge_addrs_getinfo_ifalist(struct bridge_if *bif, struct ifbareq **buf)
+{
+ int n = 128;
+ uint32_t len;
+ struct ifbareq *ninbuf;
+ struct ifbaconf bac;
+ struct ifdrv ifd;
+
+ *buf = NULL;
+ strlcpy(ifd.ifd_name, bif->bif_name, IFNAMSIZ);
+ ifd.ifd_cmd = BRDGRTS;
+ ifd.ifd_len = sizeof(bac);
+ ifd.ifd_data = &bac;
+
+ for ( ; ; ) {
+ len = n * sizeof(struct ifbareq);
+ if ((ninbuf = (struct ifbareq *)realloc(*buf, len)) == NULL) {
+ syslog(LOG_ERR, "get bridge address list: "
+ " realloc failed: %s", strerror(errno));
+ free(*buf);
+ *buf = NULL;
+ return (-1);
+ }
+
+ bac.ifbac_len = len;
+ bac.ifbac_req = *buf = ninbuf;
+
+ if (ioctl(sock, SIOCGDRVSPEC, &ifd) < 0) {
+ syslog(LOG_ERR, "get bridge address list: "
+ "ioctl(BRDGRTS) failed: %s", strerror(errno));
+ free(*buf);
+ buf = NULL;
+ return (-1);
+ }
+
+ if ((bac.ifbac_len + sizeof(struct ifbareq)) < len)
+ break;
+
+ n += 64;
+ }
+
+ return (bac.ifbac_len);
+}
+
+/*
+ * Read the initial info for all addresses on a bridge interface.
+ * Returns the number of addresses, 0 - if none, otherwise
+ * -1 if some other error occured.
+ */
+int
+bridge_getinfo_bif_addrs(struct bridge_if *bif)
+{
+ uint32_t i;
+ int32_t buf_len;
+ struct ifbareq *addr_req_buf, *addr_req;
+ struct tp_entry *te;
+
+ if ((buf_len = bridge_addrs_getinfo_ifalist(bif, &addr_req_buf)) < 0)
+ return (-1);
+
+ for (i = 0; i < buf_len / sizeof(struct ifbareq); i++) {
+ addr_req = addr_req_buf + i;
+
+ if ((te = bridge_new_addrs(addr_req->ifba_dst, bif)) != NULL)
+ bridge_addrs_info_ifaddrlist(addr_req, te);
+ }
+
+ free(addr_req_buf);
+ return (i);
+}
+
+/*
+ * Update the addresses for the bridge interface.
+ */
+int
+bridge_update_addrs(struct bridge_if *bif)
+{
+ int added, updated;
+ uint32_t i;
+ int32_t buf_len;
+ struct tp_entry *te, *te_next;
+ struct ifbareq *addr_req_buf, *addr_req;
+
+ if ((buf_len = bridge_addrs_getinfo_ifalist(bif, &addr_req_buf)) < 0)
+ return (-1);
+
+ added = updated = 0;
+
+#define BA_FOUND 0x01
+ for (i = 0; i < buf_len / sizeof(struct ifbareq); i++) {
+ addr_req = addr_req_buf + i;
+
+ if ((te = bridge_addrs_find(addr_req->ifba_dst, bif)) == NULL) {
+ added++;
+
+ if ((te = bridge_new_addrs(addr_req->ifba_dst, bif))
+ == NULL)
+ continue;
+ } else
+ updated++;
+
+ bridge_addrs_info_ifaddrlist(addr_req, te);
+ te-> flags |= BA_FOUND;
+ }
+ free(addr_req_buf);
+
+ for (te = bridge_addrs_bif_first(bif); te != NULL; te = te_next) {
+ te_next = bridge_addrs_bif_next(te);
+
+ if ((te-> flags & BA_FOUND) == 0)
+ bridge_addrs_remove(te, bif);
+ else
+ te-> flags &= ~BA_FOUND;
+ }
+#undef BA_FOUND
+
+ bif->addrs_age = time(NULL);
+ return (updated + added);
+}
+
+/************************************************************************
+ * Bridge packet filtering.
+ */
+const char bridge_sysctl[] = "net.link.bridge.";
+
+static struct {
+ int32_t val;
+ const char *name;
+} bridge_pf_sysctl[] = {
+ { 1, "pfil_bridge" },
+ { 1, "pfil_member" },
+ { 1, "pfil_onlyip" },
+ { 0, "ipfw" },
+};
+
+int32_t
+bridge_get_pfval(uint8_t which)
+{
+ if (which > sizeof(bridge_pf_sysctl) / sizeof(bridge_pf_sysctl[0])
+ || which < 1)
+ return (-1);
+
+ return (bridge_pf_sysctl[which - 1].val);
+}
+
+int32_t
+bridge_do_pfctl(int32_t bridge_ctl, enum snmp_op op, int32_t *val)
+{
+ char mib_name[100];
+ int32_t i, s_i;
+ size_t len, s_len;
+
+ if (bridge_ctl >= LEAF_begemotBridgeLayer2PfStatus)
+ return (-2);
+
+ if (op == SNMP_OP_SET) {
+ s_i = *val;
+ s_len = sizeof(s_i);
+ } else
+ s_len = 0;
+
+ len = sizeof(i);
+
+ strcpy(mib_name, bridge_sysctl);
+
+ if (sysctlbyname(strcat(mib_name,
+ bridge_pf_sysctl[bridge_ctl].name), &i, &len,
+ (op == SNMP_OP_SET ? &s_i : NULL), s_len) == -1) {
+ syslog(LOG_ERR, "sysctl(%s%s) failed - %s", bridge_sysctl,
+ bridge_pf_sysctl[bridge_ctl].name, strerror(errno));
+ return (-1);
+ }
+
+ bridge_pf_sysctl[bridge_ctl].val = i;
+ *val = i;
+
+ return (i);
+}
+
+void
+bridge_pf_dump(void)
+{
+ uint8_t i;
+
+ for (i = 0; i < sizeof(bridge_pf_sysctl) / sizeof(bridge_pf_sysctl[0]);
+ i++) {
+ syslog(LOG_ERR, "%s%s = %d", bridge_sysctl,
+ bridge_pf_sysctl[i].name, bridge_pf_sysctl[i].val);
+ }
+}
diff --git a/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_tree.def b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_tree.def
new file mode 100644
index 0000000..cbebda0
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_bridge/bridge_tree.def
@@ -0,0 +1,283 @@
+#-
+# Copyright (c) 2006 Shteryana Shopova <syrinx@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 "tc.def"
+
+typedef TruthValue ENUM (
+ 1 true
+ 2 false
+)
+
+typedef RowStatus ENUM (
+ 1 active
+ 2 notInService
+ 3 notReady
+ 4 createAndGo
+ 5 createAndWait
+ 6 destroy
+)
+
+typedef StpPortState ENUM (
+ 1 disabled
+ 2 blocking
+ 3 listening
+ 4 learning
+ 5 forwarding
+ 6 broken
+)
+
+typedef StpPortAdminPointToPointType ENUM (
+ 0 forceTrue
+ 1 forceFalse
+ 2 auto
+)
+
+typedef BaseType ENUM (
+ 1 unknown
+ 2 transparent-only
+ 3 sourceroute-only
+ 4 srt
+)
+
+typedef TpFdbStatus ENUM (
+ 1 other
+ 2 invalid
+ 3 learned
+ 4 self
+ 5 mgmt
+)
+
+(1 internet
+ (2 mgmt
+ (1 mib_2
+ (17 dot1dBridge
+ (0 dot1dNotifications
+ (1 newRoot OID op_snmp_trap)
+ (2 topologyChange OID op_snmp_trap)
+ )
+ (1 dot1dBase
+ (1 dot1dBaseBridgeAddress OCTETSTRING | MacAddress op_dot1d_base GET)
+ (2 dot1dBaseNumPorts INTEGER32 op_dot1d_base GET)
+ (3 dot1dBaseType BaseType op_dot1d_base GET)
+ (4 dot1dBasePortTable
+ (1 dot1dBasePortEntry : INTEGER op_dot1d_base_port
+ (1 dot1dBasePort INTEGER GET)
+ (2 dot1dBasePortIfIndex INTEGER GET)
+ (3 dot1dBasePortCircuit OID GET)
+ (4 dot1dBasePortDelayExceededDiscards COUNTER GET)
+ (5 dot1dBasePortMtuExceededDiscards COUNTER GET)
+ ))
+ )
+ (2 dot1dStp
+ (1 dot1dStpProtocolSpecification ENUM ( 1 unknown 2 decLb100 3 ieee8021d ) op_dot1d_stp GET)
+ (2 dot1dStpPriority INTEGER op_dot1d_stp GET SET)
+ (3 dot1dStpTimeSinceTopologyChange TIMETICKS op_dot1d_stp GET)
+ (4 dot1dStpTopChanges COUNTER op_dot1d_stp GET)
+ (5 dot1dStpDesignatedRoot OCTETSTRING | BridgeId op_dot1d_stp GET)
+ (6 dot1dStpRootCost INTEGER32 op_dot1d_stp GET)
+ (7 dot1dStpRootPort INTEGER32 op_dot1d_stp GET)
+ (8 dot1dStpMaxAge INTEGER op_dot1d_stp GET)
+ (9 dot1dStpHelloTime INTEGER op_dot1d_stp GET)
+ (10 dot1dStpHoldTime INTEGER32 op_dot1d_stp GET)
+ (11 dot1dStpForwardDelay INTEGER op_dot1d_stp GET)
+ (12 dot1dStpBridgeMaxAge INTEGER op_dot1d_stp GET SET)
+ (13 dot1dStpBridgeHelloTime INTEGER op_dot1d_stp GET SET)
+ (14 dot1dStpBridgeForwardDelay INTEGER op_dot1d_stp GET SET)
+ (15 dot1dStpPortTable
+ (1 dot1dStpPortEntry : INTEGER op_dot1d_stp_port
+ (1 dot1dStpPort INTEGER GET)
+ (2 dot1dStpPortPriority INTEGER GET SET)
+ (3 dot1dStpPortState StpPortState GET)
+ (4 dot1dStpPortEnable ENUM ( 1 enabled 2 disabled ) GET SET)
+ (5 dot1dStpPortPathCost INTEGER GET SET)
+ (6 dot1dStpPortDesignatedRoot OCTETSTRING | BridgeId GET)
+ (7 dot1dStpPortDesignatedCost INTEGER32 GET)
+ (8 dot1dStpPortDesignatedBridge OCTETSTRING | BridgeId GET)
+ (9 dot1dStpPortDesignatedPort OCTETSTRING | BridgePortId GET)
+ (10 dot1dStpPortForwardTransitions COUNTER GET)
+ ))
+ (16 dot1dStpVersion ENUM ( 0 stpCompatible 2 rstp ) op_dot1d_stp GET SET)
+ (17 dot1dStpTxHoldCount INTEGER op_dot1d_stp GET SET)
+ (19 dot1dStpExtPortTable
+ (1 dot1dStpExtPortEntry : INTEGER op_dot1d_stp_ext_port
+ (1 dot1dStpPortProtocolMigration TruthValue GET) # SET
+ (2 dot1dStpPortAdminEdgePort TruthValue GET SET)
+ (3 dot1dStpPortOperEdgePort TruthValue GET)
+ (4 dot1dStpPortAdminPointToPoint StpPortAdminPointToPointType GET SET)
+ (5 dot1dStpPortOperPointToPoint TruthValue GET)
+ (6 dot1dStpPortAdminPathCost INTEGER GET SET)
+ ))
+ )
+ (3 dot1dSr
+ )
+ (4 dot1dTp
+ (1 dot1dTpLearnedEntryDiscards COUNTER op_dot1d_tp GET)
+ (2 dot1dTpAgingTime INTEGER op_dot1d_tp GET SET)
+ (3 dot1dTpFdbTable
+ (1 dot1dTpFdbEntry : OCTETSTRING | MacAddress op_dot1d_tp_fdb
+ (1 dot1dTpFdbAddress OCTETSTRING | MacAddress GET)
+ (2 dot1dTpFdbPort INTEGER32 GET)
+ (3 dot1dTpFdbStatus TpFdbStatus GET)
+ ))
+ (4 dot1dTpPortTable
+ (1 dot1dTpPortEntry : INTEGER op_dot1d_tp_port
+ (1 dot1dTpPort INTEGER GET)
+ (2 dot1dTpPortMaxInfo INTEGER32 GET)
+ (3 dot1dTpPortInFrames COUNTER GET)
+ (4 dot1dTpPortOutFrames COUNTER GET)
+ (5 dot1dTpPortInDiscards COUNTER GET)
+ ))
+ )
+ (5 dot1dStatic
+ )
+ (8 dot1dConformance
+ (1 dot1dGroups
+ )
+ (2 dot1dCompliances
+ )
+ )
+ )
+ (134 rstpMIB
+ (0 rstpNotifications
+ )
+ (1 rstpObjects
+ )
+ (2 rstpConformance
+ (1 rstpGroups
+ )
+ (2 rstpCompliances
+ )
+ )
+ )))
+ (4 private
+ (1 enterprises
+ (12325 fokus
+ (1 begemot
+ (205 begemotBridge
+ (0 begemotBridgeNotifications
+ (1 begemotBridgeNewRoot OID op_snmp_trap)
+ (2 begemotBridgeTopologyChange OID op_snmp_trap)
+ )
+ (1 begemotBridgeBase
+ (1 begemotBridgeBaseTable
+ (1 begemotBridgeBaseEntry : OCTETSTRING | BridgeIfName op_begemot_base_bridge
+ (1 begemotBridgeBaseName OCTETSTRING | BridgeIfName GET)
+ (2 begemotBridgeBaseAddress OCTETSTRING | MacAddress GET)
+ (3 begemotBridgeBaseNumPorts INTEGER32 GET)
+ (4 begemotBridgeBaseType BaseType GET)
+ (5 begemotBridgeBaseStatus RowStatus GET SET)
+ ))
+ (2 begemotBridgeBasePortTable
+ (1 begemotBridgeBasePortEntry : OCTETSTRING | BridgeIfName INTEGER op_begemot_base_port
+ (1 begemotBridgeBasePort INTEGER GET)
+ (2 begemotBridgeBasePortIfIndex INTEGER GET)
+ (3 begemotBridgeBaseSpanEnabled ENUM ( 1 enabled 2 disabled ) GET SET)
+ (4 begemotBridgeBasePortDelayExceededDiscards COUNTER GET)
+ (5 begemotBridgeBasePortMtuExceededDiscards COUNTER GET)
+ (6 begemotBridgeBasePortStatus RowStatus GET SET)
+ (7 begemotBridgeBasePortPrivate TruthValue GET SET)
+ ))
+ )
+ (2 begemotBridgeStp
+ (1 begemotBridgeStpTable
+ (1 begemotBridgeStpEntry : OCTETSTRING | BridgeIfName op_begemot_stp
+ (1 begemotBridgeStpProtocolSpecification ENUM ( 1 unknown 2 decLb100 3 ieee8021d ) GET)
+ (2 begemotBridgeStpPriority INTEGER GET SET)
+ (3 begemotBridgeStpTimeSinceTopologyChange TIMETICKS GET)
+ (4 begemotBridgeStpTopChanges COUNTER GET)
+ (5 begemotBridgeStpDesignatedRoot OCTETSTRING | BridgeId GET)
+ (6 begemotBridgeStpRootCost INTEGER32 GET)
+ (7 begemotBridgeStpRootPort INTEGER32 GET)
+ (8 begemotBridgeStpMaxAge INTEGER GET)
+ (9 begemotBridgeStpHelloTime INTEGER GET)
+ (10 begemotBridgeStpHoldTime INTEGER32 GET)
+ (11 begemotBridgeStpForwardDelay INTEGER GET)
+ (12 begemotBridgeStpBridgeMaxAge INTEGER GET SET)
+ (13 begemotBridgeStpBridgeHelloTime INTEGER GET SET)
+ (14 begemotBridgeStpBridgeForwardDelay INTEGER GET SET)
+ (15 begemotBridgeStpVersion ENUM ( 0 stpCompatible 2 rstp ) GET SET)
+ (16 begemotBridgeStpTxHoldCount INTEGER GET SET)
+ ))
+ (2 begemotBridgeStpPortTable
+ (1 begemotBridgeStpPortEntry : OCTETSTRING | BridgeIfName INTEGER op_begemot_stp_port
+ (1 begemotBridgeStpPort INTEGER GET)
+ (2 begemotBridgeStpPortPriority INTEGER GET SET)
+ (3 begemotBridgeStpPortState StpPortState GET)
+ (4 begemotBridgeStpPortEnable ENUM ( 1 enabled 2 disabled ) GET SET)
+ (5 begemotBridgeStpPortPathCost INTEGER GET SET)
+ (6 begemotBridgeStpPortDesignatedRoot OCTETSTRING | BridgeId GET)
+ (7 begemotBridgeStpPortDesignatedCost INTEGER32 GET)
+ (8 begemotBridgeStpPortDesignatedBridge OCTETSTRING | BridgeId GET)
+ (9 begemotBridgeStpPortDesignatedPort OCTETSTRING | BridgePortId GET)
+ (10 begemotBridgeStpPortForwardTransitions COUNTER GET)
+ ))
+ (3 begemotBridgeStpExtPortTable
+ (1 begemotBridgeStpExtPortEntry : OCTETSTRING | BridgeIfName INTEGER op_begemot_stp_ext_port
+ (1 begemotBridgeStpPortProtocolMigration TruthValue GET) # SET
+ (2 begemotBridgeStpPortAdminEdgePort TruthValue GET SET)
+ (3 begemotBridgeStpPortOperEdgePort TruthValue GET)
+ (4 begemotBridgeStpPortAdminPointToPoint StpPortAdminPointToPointType GET SET)
+ (5 begemotBridgeStpPortOperPointToPoint TruthValue GET)
+ (6 begemotBridgeStpPortAdminPathCost INTEGER GET SET)
+ ))
+ )
+ (3 begemotBridgeTp
+ (1 begemotBridgeTpTable
+ (1 begemotBridgeTpEntry : OCTETSTRING | BridgeIfName op_begemot_tp
+ (1 begemotBridgeTpLearnedEntryDiscards COUNTER GET)
+ (2 begemotBridgeTpAgingTime INTEGER GET SET)
+ (3 begemotBridgeTpMaxAddresses INTEGER GET SET)
+ ))
+ (2 begemotBridgeTpFdbTable
+ (1 begemotBridgeTpFdbEntry : OCTETSTRING | BridgeIfName OCTETSTRING | MacAddress op_begemot_tp_fdb
+ (1 begemotBridgeTpFdbAddress OCTETSTRING | MacAddress GET)
+ (2 begemotBridgeTpFdbPort INTEGER32 GET)
+ (3 begemotBridgeTpFdbStatus TpFdbStatus GET)
+ ))
+ (3 begemotBridgeTpPortTable
+ (1 begemotBridgeTpPortEntry : OCTETSTRING | BridgeIfName INTEGER op_begemot_tp_port
+ (1 begemotBridgeTpPort INTEGER GET)
+ (2 begemotBridgeTpPortMaxInfo INTEGER32 GET)
+ (3 begemotBridgeTpPortInFrames COUNTER GET)
+ (4 begemotBridgeTpPortOutFrames COUNTER GET)
+ (5 begemotBridgeTpPortInDiscards COUNTER GET)
+ ))
+ )
+ (4 begemotBridgePf
+ (1 begemotBridgePfilStatus TruthValue op_begemot_bridge_pf GET SET)
+ (2 begemotBridgePfilMembers TruthValue op_begemot_bridge_pf GET SET)
+ (3 begemotBridgePfilIpOnly TruthValue op_begemot_bridge_pf GET SET)
+ (4 begemotBridgeLayer2PfStatus ENUM ( 1 enabled 2 disabled ) op_begemot_bridge_pf GET SET)
+ )
+ (5 begemotBridgeConfigObjects
+ (1 begemotBridgeDefaultBridgeIf OCTETSTRING | BridgeIfNameOrEmpty op_begemot_bridge_config GET SET)
+ (2 begemotBridgeDataUpdate INTEGER op_begemot_bridge_config GET SET)
+ (3 begemotBridgeDataPoll INTEGER op_begemot_bridge_config GET SET)
+ )
+ )))))
+)
diff --git a/usr.sbin/bsnmpd/modules/snmp_bridge/snmp_bridge.3 b/usr.sbin/bsnmpd/modules/snmp_bridge/snmp_bridge.3
new file mode 100644
index 0000000..24c8969
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_bridge/snmp_bridge.3
@@ -0,0 +1,119 @@
+.\"-
+.\" Copyright (C) 2006 Shteryana Shopova <syrinx@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 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 August 6, 2007
+.Dt snmp_bridge 3
+.Os
+.Sh NAME
+.Nm snmp_bridge
+.Nd "bridge module for snmpd.
+.Sh LIBRARY
+.Pq begemotSnmpdModulePath."bridge" = "/usr/lib/snmp_bridge.so"
+.Sh DESCRIPTION
+The
+.Nm snmp_bridge
+module implements the BRIDGE-MIB as standardized in RFC 4188, the RSTP-MIB
+standardized in RFC4318 and a private BEGEMOT-BRIDGE-MIB, which allows
+management of multiple bridge interfaces.
+Most of the objects defined in the private BEGEMOT-BRIDGE-MIB are duplicates
+of the original objects defined by the standard BRIDGE-MIB, but the private
+MIB also defines additional objects which make the functionality of
+.Nm
+similar to
+.Xr ifconfig 8
+for configuring bridge interfaces.
+Therefore one should consider adding write comminities or loading the
+.Nm
+module on systems where security is crucial.
+.Sh IMPLEMENTATION NOTES
+The additional objects to configure a bridge are:
+.Bl -tag -width "XXXXXXXXX"
+.It Va begemotBridgeBaseStatus
+Bridge interfaces can be created and destroyed via this object.
+SNMP SET operations with the following values are allowed:
+.Bl -tag -width ".It Va createAndWait"
+.It Va createAndWait
+will attempt to create a bridge interface with the name given by the table
+index.
+.It Va createAndGo
+will attempt to create a bridge interface with the name given by the table
+index and set the status of the interface to "active/up".
+.It Va destroy
+will attempt to destroy the bridge interface.
+.El
+.It Va begemotBridgeBaseSpanEnabled
+A SNMP SET operation on this object is only successfull if the corresponding
+port has not been added as member of the bridge interface on the system.
+.It Va begemotBridgeBasePortStatus
+SNMP SET operations with the following values are allowed:
+.Bl -tag -width ".It Va createAndWait"
+.It Va createAndWait
+will create a new row for the bridge member in the SNMP
+.Va begemotBridgeBasePortTable
+but will not try to commit the information to the system.
+.It Va active
+will attempt to commit the information to the system and will be successful
+only if a value for
+.Va begemotBridgeBaseSpanEnabled
+has been SET already.
+.It Va destroy
+will attempt to remove the interface from the system bridge interface.
+.El
+.It Va begemotBridgeBasePortPrivate
+This object controls a bridge interface flag called PRIVATE where any private
+port can not communicate with another private port.
+.El
+.Sh RESTRICTIONS
+Not all information in the MIBs is currently available in FreeBSD.
+The following variables carry no information:
+.Bl -tag -width "XXXXXXXXX"
+.It Va dot1dBasePortCircuit
+.It Va dot1dBasePortDelayExceededDiscards
+.It Va dot1dBasePortMtuExceededDiscards
+.It Va begemotBridgeBasePortDelayExceededDiscards
+.It Va begemotBridgeBasePortMtuExceededDiscards
+.El
+.Sh FILES
+.Bl -tag -width "XXXXXXXXX"
+.It Pa /usr/share/snmp/defs/bridge_tree.def
+The description of the MIB tree implemented by
+.Nm .
+.It Pa /usr/share/snmp/mibs/BRIDGE-MIB.txt
+This is the BRIDGE-MIB that is implemented by this module.
+.It Pa /usr/share/snmp/mibs/RSTP-MIB.txt
+This is the RSTP-MIB implemented by this module.
+.It Pa /usr/share/snmp/mibs/BEGEMOT-BRIDGE-MIB.txt
+This is the private BEGEMOT-BRIDGE-MIB that is implemented by this module.
+.El
+.Sh SEE ALSO
+.Xr bsnmpd 1 ,
+.Xr gensnmptree 1 ,
+.Xr if_bridge 4 ,
+.Xr ifconfig 8 ,
+.Xr snmpmod 3
+.Sh AUTHORS
+.An Shteryana Shopova Aq syrinx@FreeBSD.org
diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/BEGEMOT-HOSTRES-MIB.txt b/usr.sbin/bsnmpd/modules/snmp_hostres/BEGEMOT-HOSTRES-MIB.txt
new file mode 100644
index 0000000..ee8d284
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_hostres/BEGEMOT-HOSTRES-MIB.txt
@@ -0,0 +1,125 @@
+--
+-- Copyright (c) 2005-2006
+-- Hartmut Brandt
+-- All rights reserved.
+--
+-- Author: Harti 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 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$
+--
+-- Additional stuff for the HOST-RESOURCES MIB.
+--
+BEGEMOT-HOSTRES-MIB DEFINITIONS ::= BEGIN
+
+IMPORTS
+ MODULE-IDENTITY, OBJECT-TYPE, TimeTicks
+ FROM SNMPv2-SMI
+ begemot
+ FROM BEGEMOT-MIB;
+
+begemotHostres MODULE-IDENTITY
+ LAST-UPDATED "200601030000Z"
+ ORGANIZATION "German Aerospace Center"
+ CONTACT-INFO
+ " Hartmut Brandt
+
+ Postal: German Aerospace Center
+ Oberpfaffenhofen
+ 82234 Wessling
+ Germany
+
+ Fax: +49 8153 28 2843
+
+ E-mail: harti@freebsd.org"
+ DESCRIPTION
+ "The MIB for additional HOST-RESOURCES data."
+ ::= { begemot 202 }
+
+begemotHostresObjects OBJECT IDENTIFIER ::= { begemotHostres 1 }
+
+begemotHrStorageUpdate OBJECT-TYPE
+ SYNTAX TimeTicks
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The maximum number of ticks the storage table is cached."
+ DEFVAL { 700 }
+ ::= { begemotHostresObjects 1 }
+
+begemotHrFSUpdate OBJECT-TYPE
+ SYNTAX TimeTicks
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The maximum number of ticks the FS table is cached."
+ DEFVAL { 700 }
+ ::= { begemotHostresObjects 2 }
+
+begemotHrDiskStorageUpdate OBJECT-TYPE
+ SYNTAX TimeTicks
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The maximum number of ticks the disk storage table is cached."
+ DEFVAL { 300 }
+ ::= { begemotHostresObjects 3 }
+
+begemotHrNetworkUpdate OBJECT-TYPE
+ SYNTAX TimeTicks
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The maximum number of ticks the network table is cached."
+ DEFVAL { 700 }
+ ::= { begemotHostresObjects 4 }
+
+begemotHrSWInstalledUpdate OBJECT-TYPE
+ SYNTAX TimeTicks
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The maximum number of ticks the hrSWInstalledTable is cached."
+ DEFVAL { 1200 }
+ ::= { begemotHostresObjects 5 }
+
+begemotHrSWRunUpdate OBJECT-TYPE
+ SYNTAX TimeTicks
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The maximum number of ticks the hrSWRunTable and
+ hrSWRunPerfTable are cached."
+ DEFVAL { 300 }
+ ::= { begemotHostresObjects 6 }
+
+begemotHrPkgDir OBJECT-TYPE
+ SYNTAX OCTET STRING
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The path to the package DB directory."
+ DEFVAL { "/var/db/pkg" }
+ ::= { begemotHostresObjects 7 }
+
+END
diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/Makefile b/usr.sbin/bsnmpd/modules/snmp_hostres/Makefile
new file mode 100644
index 0000000..2922f45
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_hostres/Makefile
@@ -0,0 +1,82 @@
+#
+# Copyright (c) 2005-2006 The FreeBSD Project
+# All rights reserved.
+# Author: Victor Cruceru <soc-victor@freebsd.org>
+#
+# Redistribution of this software and documentation and use in source and
+# binary forms, with or without modification, are permitted provided that
+# the following conditions are met:
+#
+# 1. Redistributions of source code or documentation must retain the above
+# copyright notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must 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$
+#
+
+LPRSRC= ${.CURDIR}/../../../lpr/common_source
+.PATH: ${LPRSRC}
+
+MOD= hostres
+SRCS= hostres_begemot.c \
+ hostres_device_tbl.c \
+ hostres_diskstorage_tbl.c \
+ hostres_fs_tbl.c \
+ hostres_network_tbl.c \
+ hostres_partition_tbl.c \
+ hostres_printer_tbl.c \
+ hostres_processor_tbl.c \
+ hostres_scalars.c \
+ hostres_snmp.c \
+ hostres_storage_tbl.c \
+ hostres_swinstalled_tbl.c \
+ hostres_swrun_tbl.c \
+ printcap.c
+
+#Not having NDEBUG defined will enable assertions and a lot of output on stderr
+CFLAGS+= -DNDEBUG -I${LPRSRC}
+XSYM= host hrStorageOther hrStorageRam hrStorageVirtualMemory \
+ hrStorageFixedDisk hrStorageRemovableDisk hrStorageFloppyDisk \
+ hrStorageCompactDisc hrStorageRamDisk hrStorageFlashMemory \
+ hrStorageNetworkDisk hrDeviceOther hrDeviceUnknown \
+ hrDeviceProcessor hrDeviceNetwork hrDevicePrinter \
+ hrDeviceDiskStorage hrDeviceVideo hrDeviceAudio \
+ hrDeviceCoprocessor hrDeviceKeyboard hrDeviceModem \
+ hrDeviceParallelPort hrDevicePointing \
+ hrDeviceSerialPort hrDeviceTape hrDeviceClock \
+ hrDeviceVolatileMemory hrDeviceNonVolatileMemory \
+ hrFSOther hrFSUnknown hrFSBerkeleyFFS hrFSSys5FS hrFSFat\
+ hrFSHPFS hrFSHFS hrFSMFS hrFSNTFS hrFSVNode hrFSJournaled \
+ hrFSiso9660 hrFSRockRidge hrFSNFS hrFSNetware hrFSAFS hrFSDFS \
+ hrFSAppleshare hrFSRFS hrFSDGCFS hrFSBFS hrFSFAT32 hrFSLinuxExt2
+
+MAN= snmp_hostres.3
+
+DEFS= ${MOD}_tree.def
+BMIBS= BEGEMOT-HOSTRES-MIB.txt
+
+DPADD= ${LIBKVM} ${LIBDEVINFO} ${LIBM} ${LIBGEOM} ${LIBMEMSTAT}
+LDADD= -lkvm -ldevinfo -lm -lgeom -lmemstat
+
+.include <bsd.snmpmod.mk>
+
+printcap.So: printcap.c
+ ${CC} ${PICFLAG} -DPIC ${CFLAGS:C/^-W.*//} -c ${.IMPSRC} -o ${.TARGET}
+
+smilint:
+ env SMIPATH=.:/usr/share/snmp/mibs:/usr/local/share/snmp/mibs \
+ smilint -c /dev/null -l6 -i group-membership BEGEMOT-HOSTRES-MIB
diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_begemot.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_begemot.c
new file mode 100644
index 0000000..f1cc5e3
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_begemot.c
@@ -0,0 +1,171 @@
+/*-
+ * Copyright (c) 2005-2006.
+ * Hartmut Brandt.
+ * All rights reserved.
+ *
+ * Author: Hartmut Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <stdlib.h>
+
+#include "hostres_snmp.h"
+#include "hostres_oid.h"
+#include "hostres_tree.h"
+
+int
+op_begemot(struct snmp_context *ctx, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op op)
+{
+
+ switch (op) {
+
+ case SNMP_OP_GET:
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_begemotHrStorageUpdate:
+ value->v.uint32 = storage_tbl_refresh;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotHrFSUpdate:
+ value->v.uint32 = fs_tbl_refresh;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotHrDiskStorageUpdate:
+ value->v.uint32 = disk_storage_tbl_refresh;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotHrNetworkUpdate:
+ value->v.uint32 = network_tbl_refresh;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotHrSWInstalledUpdate:
+ value->v.uint32 = swins_tbl_refresh;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotHrSWRunUpdate:
+ value->v.uint32 = swrun_tbl_refresh;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotHrPkgDir:
+ return (string_get(value, pkg_dir, -1));
+ }
+ abort();
+
+ case SNMP_OP_GETNEXT:
+ abort();
+
+ case SNMP_OP_SET:
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_begemotHrStorageUpdate:
+ ctx->scratch->int1 = storage_tbl_refresh;
+ storage_tbl_refresh = value->v.uint32;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotHrFSUpdate:
+ ctx->scratch->int1 = fs_tbl_refresh;
+ fs_tbl_refresh = value->v.uint32;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotHrDiskStorageUpdate:
+ ctx->scratch->int1 = disk_storage_tbl_refresh;
+ disk_storage_tbl_refresh = value->v.uint32;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotHrNetworkUpdate:
+ ctx->scratch->int1 = network_tbl_refresh;
+ network_tbl_refresh = value->v.uint32;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotHrSWInstalledUpdate:
+ ctx->scratch->int1 = swins_tbl_refresh;
+ swins_tbl_refresh = value->v.uint32;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotHrSWRunUpdate:
+ ctx->scratch->int1 = swrun_tbl_refresh;
+ swrun_tbl_refresh = value->v.uint32;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotHrPkgDir:
+ return (string_save(value, ctx, -1, &pkg_dir));
+ }
+ abort();
+
+ case SNMP_OP_COMMIT:
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_begemotHrStorageUpdate:
+ case LEAF_begemotHrFSUpdate:
+ case LEAF_begemotHrDiskStorageUpdate:
+ case LEAF_begemotHrNetworkUpdate:
+ case LEAF_begemotHrSWInstalledUpdate:
+ case LEAF_begemotHrSWRunUpdate:
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotHrPkgDir:
+ string_commit(ctx);
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+
+ case SNMP_OP_ROLLBACK:
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_begemotHrStorageUpdate:
+ storage_tbl_refresh = ctx->scratch->int1;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotHrFSUpdate:
+ fs_tbl_refresh = ctx->scratch->int1;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotHrDiskStorageUpdate:
+ disk_storage_tbl_refresh = ctx->scratch->int1;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotHrNetworkUpdate:
+ network_tbl_refresh = ctx->scratch->int1;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotHrSWInstalledUpdate:
+ swins_tbl_refresh = ctx->scratch->int1;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotHrSWRunUpdate:
+ swrun_tbl_refresh = ctx->scratch->int1;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotHrPkgDir:
+ string_rollback(ctx, &pkg_dir);
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+ }
+
+ abort();
+}
diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_device_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_device_tbl.c
new file mode 100644
index 0000000..7e81436
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_device_tbl.c
@@ -0,0 +1,690 @@
+ /*-
+ * Copyright (c) 2005-2006 The FreeBSD Project
+ * All rights reserved.
+ *
+ * Author: Victor Cruceru <soc-victor@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+/*
+ * Host Resources MIB: hrDeviceTable implementation for SNMPd.
+ */
+
+#include <sys/un.h>
+#include <sys/limits.h>
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sysexits.h>
+
+#include "hostres_snmp.h"
+#include "hostres_oid.h"
+#include "hostres_tree.h"
+
+#define FREE_DEV_STRUCT(entry_p) do { \
+ free(entry_p->name); \
+ free(entry_p->location); \
+ free(entry_p->descr); \
+ free(entry_p); \
+} while (0)
+
+/*
+ * Status of a device
+ */
+enum DeviceStatus {
+ DS_UNKNOWN = 1,
+ DS_RUNNING = 2,
+ DS_WARNING = 3,
+ DS_TESTING = 4,
+ DS_DOWN = 5
+};
+
+TAILQ_HEAD(device_tbl, device_entry);
+
+/* the head of the list with hrDeviceTable's entries */
+static struct device_tbl device_tbl = TAILQ_HEAD_INITIALIZER(device_tbl);
+
+/* Table used for consistent device table indexing. */
+struct device_map device_map = STAILQ_HEAD_INITIALIZER(device_map);
+
+/* next int available for indexing the hrDeviceTable */
+static uint32_t next_device_index = 1;
+
+/* last (agent) tick when hrDeviceTable was updated */
+static uint64_t device_tick = 0;
+
+/* maximum number of ticks between updates of device table */
+uint32_t device_tbl_refresh = 10 * 100;
+
+/* socket for /var/run/devd.pipe */
+static int devd_sock = -1;
+
+/* used to wait notifications from /var/run/devd.pipe */
+static void *devd_fd;
+
+/* some constants */
+static const struct asn_oid OIDX_hrDeviceProcessor_c = OIDX_hrDeviceProcessor;
+static const struct asn_oid OIDX_hrDeviceOther_c = OIDX_hrDeviceOther;
+
+/**
+ * Create a new entry out of thin air.
+ */
+struct device_entry *
+device_entry_create(const char *name, const char *location, const char *descr)
+{
+ struct device_entry *entry = NULL;
+ struct device_map_entry *map = NULL;
+ size_t name_len;
+ size_t location_len;
+
+ assert((name[0] != 0) || (location[0] != 0));
+
+ if (name[0] == 0 && location[0] == 0)
+ return (NULL);
+
+ STAILQ_FOREACH(map, &device_map, link) {
+ assert(map->name_key != NULL);
+ assert(map->location_key != NULL);
+
+ if (strcmp(map->name_key, name) == 0 &&
+ strcmp(map->location_key, location) == 0) {
+ break;
+ }
+ }
+
+ if (map == NULL) {
+ /* new object - get a new index */
+ if (next_device_index > INT_MAX) {
+ syslog(LOG_ERR,
+ "%s: hrDeviceTable index wrap", __func__);
+ /* There isn't much we can do here.
+ * If the next_swins_index is consumed
+ * then we can't add entries to this table
+ * So it is better to exit - if the table is sparsed
+ * at the next agent run we can fill it fully.
+ */
+ errx(EX_SOFTWARE, "hrDeviceTable index wrap");
+ /* not reachable */
+ }
+
+ if ((map = malloc(sizeof(*map))) == NULL) {
+ syslog(LOG_ERR, "hrDeviceTable: %s: %m", __func__ );
+ return (NULL);
+ }
+
+ map->entry_p = NULL;
+
+ name_len = strlen(name) + 1;
+ if (name_len > DEV_NAME_MLEN)
+ name_len = DEV_NAME_MLEN;
+
+ if ((map->name_key = malloc(name_len)) == NULL) {
+ syslog(LOG_ERR, "hrDeviceTable: %s: %m", __func__ );
+ free(map);
+ return (NULL);
+ }
+
+ location_len = strlen(location) + 1;
+ if (location_len > DEV_LOC_MLEN)
+ location_len = DEV_LOC_MLEN;
+
+ if ((map->location_key = malloc(location_len )) == NULL) {
+ syslog(LOG_ERR, "hrDeviceTable: %s: %m", __func__ );
+ free(map->name_key);
+ free(map);
+ return (NULL);
+ }
+
+ map->hrIndex = next_device_index++;
+
+ strlcpy(map->name_key, name, name_len);
+ strlcpy(map->location_key, location, location_len);
+
+ STAILQ_INSERT_TAIL(&device_map, map, link);
+ HRDBG("%s at %s added into hrDeviceMap at index=%d",
+ name, location, map->hrIndex);
+ } else {
+ HRDBG("%s at %s exists in hrDeviceMap index=%d",
+ name, location, map->hrIndex);
+ }
+
+ if ((entry = malloc(sizeof(*entry))) == NULL) {
+ syslog(LOG_WARNING, "hrDeviceTable: %s: %m", __func__);
+ return (NULL);
+ }
+ memset(entry, 0, sizeof(*entry));
+
+ entry->index = map->hrIndex;
+ map->entry_p = entry;
+
+ if ((entry->name = strdup(map->name_key)) == NULL) {
+ syslog(LOG_ERR, "hrDeviceTable: %s: %m", __func__ );
+ free(entry);
+ return (NULL);
+ }
+
+ if ((entry->location = strdup(map->location_key)) == NULL) {
+ syslog(LOG_ERR, "hrDeviceTable: %s: %m", __func__ );
+ free(entry->name);
+ free(entry);
+ return (NULL);
+ }
+
+ /*
+ * From here till the end of this function we reuse name_len
+ * for a diferrent purpose - for device_entry::descr
+ */
+ if (name[0] != '\0')
+ name_len = strlen(name) + strlen(descr) +
+ strlen(": ") + 1;
+ else
+ name_len = strlen(location) + strlen(descr) +
+ strlen("unknown at : ") + 1;
+
+ if (name_len > DEV_DESCR_MLEN)
+ name_len = DEV_DESCR_MLEN;
+
+ if ((entry->descr = malloc(name_len )) == NULL) {
+ syslog(LOG_ERR, "hrDeviceTable: %s: %m", __func__ );
+ free(entry->name);
+ free(entry->location);
+ free(entry);
+ return (NULL);
+ }
+
+ memset(&entry->descr[0], '\0', name_len);
+
+ if (name[0] != '\0')
+ snprintf(entry->descr, name_len,
+ "%s: %s", name, descr);
+ else
+ snprintf(entry->descr, name_len,
+ "unknown at %s: %s", location, descr);
+
+ entry->id = &oid_zeroDotZero; /* unknown id - FIXME */
+ entry->status = (u_int)DS_UNKNOWN;
+ entry->errors = 0;
+ entry->type = &OIDX_hrDeviceOther_c;
+
+ INSERT_OBJECT_INT(entry, &device_tbl);
+
+ return (entry);
+}
+
+/**
+ * Create a new entry into the device table.
+ */
+static struct device_entry *
+device_entry_create_devinfo(const struct devinfo_dev *dev_p)
+{
+
+ assert(dev_p->dd_name != NULL);
+ assert(dev_p->dd_location != NULL);
+
+ return (device_entry_create(dev_p->dd_name, dev_p->dd_location,
+ dev_p->dd_desc));
+}
+
+/**
+ * Delete an entry from the device table.
+ */
+void
+device_entry_delete(struct device_entry *entry)
+{
+ struct device_map_entry *map;
+
+ assert(entry != NULL);
+
+ TAILQ_REMOVE(&device_tbl, entry, link);
+
+ STAILQ_FOREACH(map, &device_map, link)
+ if (map->entry_p == entry) {
+ map->entry_p = NULL;
+ break;
+ }
+
+ FREE_DEV_STRUCT(entry);
+}
+
+/**
+ * Find an entry given its name and location
+ */
+static struct device_entry *
+device_find_by_dev(const struct devinfo_dev *dev_p)
+{
+ struct device_map_entry *map;
+
+ assert(dev_p != NULL);
+
+ STAILQ_FOREACH(map, &device_map, link)
+ if (strcmp(map->name_key, dev_p->dd_name) == 0 &&
+ strcmp(map->location_key, dev_p->dd_location) == 0)
+ return (map->entry_p);
+ return (NULL);
+}
+
+/**
+ * Find an entry given its index.
+ */
+struct device_entry *
+device_find_by_index(int32_t idx)
+{
+ struct device_entry *entry;
+
+ TAILQ_FOREACH(entry, &device_tbl, link)
+ if (entry->index == idx)
+ return (entry);
+ return (NULL);
+}
+
+/**
+ * Find an device entry given its name.
+ */
+struct device_entry *
+device_find_by_name(const char *dev_name)
+{
+ struct device_map_entry *map;
+
+ assert(dev_name != NULL);
+
+ STAILQ_FOREACH(map, &device_map, link)
+ if (strcmp(map->name_key, dev_name) == 0)
+ return (map->entry_p);
+
+ return (NULL);
+}
+
+/**
+ * Find out the type of device. CPU only currently.
+ */
+static void
+device_get_type(struct devinfo_dev *dev_p, const struct asn_oid **out_type_p)
+{
+
+ assert(dev_p != NULL);
+ assert(out_type_p != NULL);
+
+ if (dev_p == NULL)
+ return;
+
+ if (strncmp(dev_p->dd_name, "cpu", strlen("cpu")) == 0 &&
+ strstr(dev_p->dd_location, ".CPU") != NULL) {
+ *out_type_p = &OIDX_hrDeviceProcessor_c;
+ return;
+ }
+}
+
+/**
+ * Get the status of a device
+ */
+static enum DeviceStatus
+device_get_status(struct devinfo_dev *dev)
+{
+
+ assert(dev != NULL);
+
+ switch (dev->dd_state) {
+ case DIS_ALIVE: /* probe succeeded */
+ case DIS_NOTPRESENT: /* not probed or probe failed */
+ return (DS_DOWN);
+ case DIS_ATTACHED: /* attach method called */
+ case DIS_BUSY: /* device is open */
+ return (DS_RUNNING);
+ default:
+ return (DS_UNKNOWN);
+ }
+}
+
+/**
+ * Get the info for the given device and then recursively process all
+ * child devices.
+ */
+static int
+device_collector(struct devinfo_dev *dev, void *arg)
+{
+ struct device_entry *entry;
+
+ HRDBG("%llu/%llu name='%s' desc='%s' drivername='%s' location='%s'",
+ (unsigned long long)dev->dd_handle,
+ (unsigned long long)dev->dd_parent, dev->dd_name, dev->dd_desc,
+ dev->dd_drivername, dev->dd_location);
+
+ if (dev->dd_name[0] != '\0' || dev->dd_location[0] != '\0') {
+ HRDBG("ANALYZING dev %s at %s",
+ dev->dd_name, dev->dd_location);
+
+ if ((entry = device_find_by_dev(dev)) != NULL) {
+ entry->flags |= HR_DEVICE_FOUND;
+ entry->status = (u_int)device_get_status(dev);
+ } else if ((entry = device_entry_create_devinfo(dev)) != NULL) {
+ device_get_type(dev, &entry->type);
+
+ entry->flags |= HR_DEVICE_FOUND;
+ entry->status = (u_int)device_get_status(dev);
+ }
+ } else {
+ HRDBG("SKIPPED unknown device at location '%s'",
+ dev->dd_location );
+ }
+
+ return (devinfo_foreach_device_child(dev, device_collector, arg));
+}
+
+/**
+ * Create the socket to the device daemon.
+ */
+static int
+create_devd_socket(void)
+{
+ int d_sock;
+ struct sockaddr_un devd_addr;
+
+ bzero(&devd_addr, sizeof(struct sockaddr_un));
+
+ if ((d_sock = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
+ syslog(LOG_ERR, "Failed to create the socket for %s: %m",
+ PATH_DEVD_PIPE);
+ return (-1);
+ }
+
+ devd_addr.sun_family = PF_LOCAL;
+ devd_addr.sun_len = sizeof(devd_addr);
+ strlcpy(devd_addr.sun_path, PATH_DEVD_PIPE,
+ sizeof(devd_addr.sun_path) - 1);
+
+ if (connect(d_sock, (struct sockaddr *)&devd_addr,
+ sizeof(devd_addr)) == -1) {
+ syslog(LOG_ERR,"Failed to connect socket for %s: %m",
+ PATH_DEVD_PIPE);
+ if (close(d_sock) < 0 )
+ syslog(LOG_ERR,"Failed to close socket for %s: %m",
+ PATH_DEVD_PIPE);
+ return (-1);
+ }
+
+ return (d_sock);
+}
+
+/*
+ * Event on the devd socket.
+ *
+ * We should probably directly process entries here. For simplicity just
+ * call the refresh routine with the force flag for now.
+ */
+static void
+devd_socket_callback(int fd, void *arg __unused)
+{
+ char buf[512];
+ int read_len = -1;
+
+ assert(fd == devd_sock);
+
+ HRDBG("called");
+
+ read_len = read(fd, buf, sizeof(buf) - 1);
+ if (read_len < 0) {
+ if (errno == EBADF) {
+ devd_sock = -1;
+ if (devd_fd != NULL) {
+ fd_deselect(devd_fd);
+ devd_fd = NULL;
+ }
+ syslog(LOG_ERR, "Closing devd_fd, revert to "
+ "devinfo polling");
+ }
+
+ } else if (read_len == 0) {
+ syslog(LOG_ERR, "zero bytes read from devd pipe... "
+ "closing socket!");
+
+ if (close(devd_sock) < 0 )
+ syslog(LOG_ERR, "Failed to close devd socket: %m");
+
+ devd_sock = -1;
+ if (devd_fd != NULL) {
+ fd_deselect(devd_fd);
+ devd_fd = NULL;
+ }
+ syslog(LOG_ERR, "Closing devd_fd, revert to devinfo polling");
+
+ } else {
+ switch (buf[0]) {
+ case '+':
+ case '-':
+ case '?':
+ case '!':
+ refresh_device_tbl(1);
+ return;
+ default:
+ syslog(LOG_ERR, "unknown message from devd socket");
+ }
+ }
+}
+
+/**
+ * Initialize and populate the device table.
+ */
+void
+init_device_tbl(void)
+{
+
+ /* initially populate table for the other tables */
+ refresh_device_tbl(1);
+
+ /* no problem if that fails - just use polling mode */
+ devd_sock = create_devd_socket();
+}
+
+/**
+ * Start devd(8) monitoring.
+ */
+void
+start_device_tbl(struct lmodule *mod)
+{
+
+ if (devd_sock > 0) {
+ devd_fd = fd_select(devd_sock, devd_socket_callback, NULL, mod);
+ if (devd_fd == NULL)
+ syslog(LOG_ERR, "fd_select failed on devd socket: %m");
+ }
+}
+
+/**
+ * Finalization routine for hrDeviceTable
+ * It destroys the lists and frees any allocated heap memory
+ */
+void
+fini_device_tbl(void)
+{
+ struct device_map_entry *n1;
+
+ if (devd_fd != NULL)
+ fd_deselect(devd_fd);
+
+ if (devd_sock != -1)
+ (void)close(devd_sock);
+
+ devinfo_free();
+
+ while ((n1 = STAILQ_FIRST(&device_map)) != NULL) {
+ STAILQ_REMOVE_HEAD(&device_map, link);
+ if (n1->entry_p != NULL) {
+ TAILQ_REMOVE(&device_tbl, n1->entry_p, link);
+ FREE_DEV_STRUCT(n1->entry_p);
+ }
+ free(n1->name_key);
+ free(n1->location_key);
+ free(n1);
+ }
+ assert(TAILQ_EMPTY(&device_tbl));
+}
+
+/**
+ * Refresh routine for hrDeviceTable. We don't refresh here if the devd socket
+ * is open, because in this case we have the actual information always. We
+ * also don't refresh when the table is new enough (if we don't have a devd
+ * socket). In either case a refresh can be forced by passing a non-zero value.
+ */
+void
+refresh_device_tbl(int force)
+{
+ struct device_entry *entry, *entry_tmp;
+ struct devinfo_dev *dev_root;
+ static int act = 0;
+
+ if (!force && (devd_sock >= 0 ||
+ (device_tick != 0 && this_tick - device_tick < device_tbl_refresh))){
+ HRDBG("no refresh needed");
+ return;
+ }
+
+ if (act) {
+ syslog(LOG_ERR, "%s: recursive call", __func__);
+ return;
+ }
+
+ if (devinfo_init() != 0) {
+ syslog(LOG_ERR,"%s: devinfo_init failed: %m", __func__);
+ return;
+ }
+
+ act = 1;
+ if ((dev_root = devinfo_handle_to_device(DEVINFO_ROOT_DEVICE)) == NULL){
+ syslog(LOG_ERR, "%s: can't get the root device: %m", __func__);
+ goto out;
+ }
+
+ /* mark each entry as missing */
+ TAILQ_FOREACH(entry, &device_tbl, link)
+ entry->flags &= ~HR_DEVICE_FOUND;
+
+ if (devinfo_foreach_device_child(dev_root, device_collector, NULL))
+ syslog(LOG_ERR, "%s: devinfo_foreach_device_child failed",
+ __func__);
+
+ /*
+ * Purge items that disappeared
+ */
+ TAILQ_FOREACH_SAFE(entry, &device_tbl, link, entry_tmp) {
+ /*
+ * If HR_DEVICE_IMMUTABLE bit is set then this means that
+ * this entry was not detected by the above
+ * devinfo_foreach_device() call. So we are not deleting
+ * it there.
+ */
+ if (!(entry->flags & HR_DEVICE_FOUND) &&
+ !(entry->flags & HR_DEVICE_IMMUTABLE))
+ device_entry_delete(entry);
+ }
+
+ device_tick = this_tick;
+
+ /*
+ * Force a refresh for the hrDiskStorageTable
+ * XXX Why not the other dependen tables?
+ */
+ refresh_disk_storage_tbl(1);
+
+ out:
+ devinfo_free();
+ act = 0;
+}
+
+/**
+ * This is the implementation for a generated (by a SNMP tool)
+ * function prototype, see hostres_tree.h
+ * It handles the SNMP operations for hrDeviceTable
+ */
+int
+op_hrDeviceTable(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op curr_op)
+{
+ struct device_entry *entry;
+
+ refresh_device_tbl(0);
+
+ switch (curr_op) {
+
+ case SNMP_OP_GETNEXT:
+ if ((entry = NEXT_OBJECT_INT(&device_tbl,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ value->var.len = sub + 1;
+ value->var.subs[sub] = entry->index;
+ goto get;
+
+ case SNMP_OP_GET:
+ if ((entry = FIND_OBJECT_INT(&device_tbl,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_SET:
+ if ((entry = FIND_OBJECT_INT(&device_tbl,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NO_CREATION);
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ abort();
+ }
+ abort();
+
+ get:
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_hrDeviceIndex:
+ value->v.integer = entry->index;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrDeviceType:
+ assert(entry->type != NULL);
+ value->v.oid = *(entry->type);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrDeviceDescr:
+ return (string_get(value, entry->descr, -1));
+
+ case LEAF_hrDeviceID:
+ value->v.oid = *(entry->id);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrDeviceStatus:
+ value->v.integer = entry->status;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrDeviceErrors:
+ value->v.uint32 = entry->errors;
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+}
diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_diskstorage_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_diskstorage_tbl.c
new file mode 100644
index 0000000..200f1ec
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_diskstorage_tbl.c
@@ -0,0 +1,643 @@
+/*-
+ * Copyright (c) 2005-2006 The FreeBSD Project
+ * All rights reserved.
+ *
+ * Author: Victor Cruceru <soc-victor@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+/*
+ * Host Resources MIB for SNMPd. Implementation for the hrDiskStorageTable
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/ata.h>
+#include <sys/disk.h>
+#include <sys/linker.h>
+#include <sys/mdioctl.h>
+#include <sys/module.h>
+#include <sys/sysctl.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <paths.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "hostres_snmp.h"
+#include "hostres_oid.h"
+#include "hostres_tree.h"
+
+enum hrDiskStrorageAccess {
+ DS_READ_WRITE = 1,
+ DS_READ_ONLY = 2
+};
+
+enum hrDiskStrorageMedia {
+ DSM_OTHER = 1,
+ DSM_UNKNOWN = 2,
+ DSM_HARDDISK = 3,
+ DSM_FLOPPYDISK = 4,
+ DSM_OPTICALDISKROM= 5,
+ DSM_OPTICALDISKWORM= 6,
+ DSM_OPTICALDISKRW= 7,
+ DSM_RAMDISK = 8
+};
+
+/*
+ * This structure is used to hold a SNMP table entry for HOST-RESOURCES-MIB's
+ * hrDiskStorageTable. Note that index is external being allocated and
+ * maintained by the hrDeviceTable code.
+ *
+ * NOTE: according to MIB removable means removable media, not the
+ * device itself (like a USB card reader)
+ */
+struct disk_entry {
+ int32_t index;
+ int32_t access; /* enum hrDiskStrorageAccess */
+ int32_t media; /* enum hrDiskStrorageMedia*/
+ int32_t removable; /* enum snmpTCTruthValue*/
+ int32_t capacity;
+ TAILQ_ENTRY(disk_entry) link;
+ /*
+ * next items are not from the SNMP mib table, only to be used
+ * internally
+ */
+#define HR_DISKSTORAGE_FOUND 0x001
+#define HR_DISKSTORAGE_ATA 0x002 /* belongs to the ATA subsystem */
+#define HR_DISKSTORAGE_MD 0x004 /* it is a MD (memory disk) */
+ uint32_t flags;
+ uint64_t r_tick;
+ u_char dev_name[32]; /* device name, i.e. "ad4" or "acd0" */
+};
+TAILQ_HEAD(disk_tbl, disk_entry);
+
+/* the head of the list with hrDiskStorageTable's entries */
+static struct disk_tbl disk_tbl =
+ TAILQ_HEAD_INITIALIZER(disk_tbl);
+
+/* last tick when hrFSTable was updated */
+static uint64_t disk_storage_tick;
+
+/* minimum number of ticks between refreshs */
+uint32_t disk_storage_tbl_refresh = HR_DISK_TBL_REFRESH * 100;
+
+/* fd for "/dev/mdctl"*/
+static int md_fd = -1;
+
+/* buffer for sysctl("kern.disks") */
+static char *disk_list;
+static size_t disk_list_len;
+
+/* some constants */
+static const struct asn_oid OIDX_hrDeviceDiskStorage_c =
+ OIDX_hrDeviceDiskStorage;
+
+/**
+ * Load the MD driver if it isn't loaded already.
+ */
+static 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);
+ }
+ }
+ }
+}
+
+/**
+ * Create a new entry into the DiskStorageTable.
+ */
+static struct disk_entry *
+disk_entry_create(const struct device_entry *devEntry)
+{
+ struct disk_entry *entry;
+
+ assert(devEntry != NULL);
+ if (devEntry == NULL)
+ return NULL;
+
+ if ((entry = malloc(sizeof(*entry))) == NULL) {
+ syslog(LOG_WARNING, "hrDiskStorageTable: %s: %m", __func__);
+ return (NULL);
+ }
+
+ memset(entry, 0, sizeof(*entry));
+ entry->index = devEntry->index;
+ INSERT_OBJECT_INT(entry, &disk_tbl);
+
+ return (entry);
+}
+
+/**
+ * Delete a disk table entry.
+ */
+static void
+disk_entry_delete(struct disk_entry *entry)
+{
+ struct device_entry *devEntry;
+
+ assert(entry != NULL);
+ TAILQ_REMOVE(&disk_tbl, entry, link);
+
+ devEntry = device_find_by_index(entry->index);
+
+ free(entry);
+
+ /*
+ * Also delete the respective device entry -
+ * this is needed for disk devices that are not
+ * detected by libdevinfo
+ */
+ if (devEntry != NULL &&
+ (devEntry->flags & HR_DEVICE_IMMUTABLE) == HR_DEVICE_IMMUTABLE)
+ device_entry_delete(devEntry);
+}
+
+/**
+ * Find a disk storage entry given its index.
+ */
+static struct disk_entry *
+disk_find_by_index(int32_t idx)
+{
+ struct disk_entry *entry;
+
+ TAILQ_FOREACH(entry, &disk_tbl, link)
+ if (entry->index == idx)
+ return (entry);
+
+ return (NULL);
+}
+
+/**
+ * Get the disk parameters
+ */
+static void
+disk_query_disk(struct disk_entry *entry)
+{
+ char dev_path[128];
+ int fd;
+ off_t mediasize;
+
+ if (entry == NULL || entry->dev_name[0] == '\0')
+ return;
+
+ snprintf(dev_path, sizeof(dev_path),
+ "%s%s", _PATH_DEV, entry->dev_name);
+ entry->capacity = 0;
+
+ HRDBG("OPENING device %s", dev_path);
+ if ((fd = open(dev_path, O_RDONLY|O_NONBLOCK)) == -1) {
+ HRDBG("OPEN device %s failed: %s", dev_path, strerror(errno));
+ return;
+ }
+
+ if (ioctl(fd, DIOCGMEDIASIZE, &mediasize) < 0) {
+ HRDBG("DIOCGMEDIASIZE for device %s failed: %s",
+ dev_path, strerror(errno));
+ (void)close(fd);
+ return;
+ }
+
+ mediasize = mediasize / 1024;
+ entry->capacity = (mediasize > INT_MAX ? INT_MAX : mediasize);
+ partition_tbl_handle_disk(entry->index, entry->dev_name);
+
+ (void)close(fd);
+}
+
+/**
+ * Find all ATA disks in the device table.
+ */
+static void
+disk_OS_get_ATA_disks(void)
+{
+ struct device_map_entry *map;
+ struct device_entry *entry;
+ struct disk_entry *disk_entry;
+ const struct disk_entry *found;
+
+ /* Things we know are ata disks */
+ static const struct disk_entry lookup[] = {
+ {
+ .dev_name = "ad",
+ .media = DSM_HARDDISK,
+ .removable = SNMP_FALSE
+ },
+ {
+ .dev_name = "ar",
+ .media = DSM_OTHER,
+ .removable = SNMP_FALSE
+ },
+ {
+ .dev_name = "acd",
+ .media = DSM_OPTICALDISKROM,
+ .removable = SNMP_TRUE
+ },
+ {
+ .dev_name = "afd",
+ .media = DSM_FLOPPYDISK,
+ .removable = SNMP_TRUE
+ },
+ {
+ .dev_name = "ast",
+ .media = DSM_OTHER,
+ .removable = SNMP_TRUE
+ },
+
+ { .media = DSM_UNKNOWN }
+ };
+
+ /* Walk over the device table looking for ata disks */
+ STAILQ_FOREACH(map, &device_map, link) {
+ for (found = lookup; found->media != DSM_UNKNOWN; found++) {
+ if (strncmp(map->name_key, found->dev_name,
+ strlen(found->dev_name)) != 0)
+ continue;
+
+ /*
+ * Avoid false disk devices. For example adw(4) and
+ * adv(4) - they are not disks!
+ */
+ if (strlen(map->name_key) > strlen(found->dev_name) &&
+ !isdigit(map->name_key[strlen(found->dev_name)]))
+ continue;
+
+ /* First get the entry from the hrDeviceTbl */
+ entry = map->entry_p;
+ entry->type = &OIDX_hrDeviceDiskStorage_c;
+
+ /* Then check hrDiskStorage table for this device */
+ disk_entry = disk_find_by_index(entry->index);
+ if (disk_entry == NULL) {
+ disk_entry = disk_entry_create(entry);
+ if (disk_entry == NULL)
+ continue;
+
+ disk_entry->access = DS_READ_WRITE;
+ strlcpy(disk_entry->dev_name, entry->name,
+ sizeof(disk_entry->dev_name));
+
+ disk_entry->media = found->media;
+ disk_entry->removable = found->removable;
+ }
+
+ disk_entry->flags |= HR_DISKSTORAGE_FOUND;
+ disk_entry->flags |= HR_DISKSTORAGE_ATA;
+
+ disk_query_disk(disk_entry);
+ disk_entry->r_tick = this_tick;
+ }
+ }
+}
+
+/**
+ * Find MD disks in the device table.
+ */
+static void
+disk_OS_get_MD_disks(void)
+{
+ struct device_map_entry *map;
+ struct device_entry *entry;
+ struct disk_entry *disk_entry;
+ struct md_ioctl mdio;
+ int unit;
+
+ /* Look for md devices */
+ STAILQ_FOREACH(map, &device_map, link) {
+ if (sscanf(map->name_key, "md%d", &unit) != 1)
+ continue;
+
+ /* First get the entry from the hrDeviceTbl */
+ entry = device_find_by_index(map->hrIndex);
+ entry->type = &OIDX_hrDeviceDiskStorage_c;
+
+ /* Then check hrDiskStorage table for this device */
+ disk_entry = disk_find_by_index(entry->index);
+ if (disk_entry == NULL) {
+ disk_entry = disk_entry_create(entry);
+ if (disk_entry == NULL)
+ continue;
+
+ memset(&mdio, 0, sizeof(mdio));
+ mdio.md_version = MDIOVERSION;
+ mdio.md_unit = unit;
+
+ if (ioctl(md_fd, MDIOCQUERY, &mdio) < 0) {
+ syslog(LOG_ERR,
+ "hrDiskStorageTable: Couldnt ioctl");
+ continue;
+ }
+
+ if ((mdio.md_options & MD_READONLY) == MD_READONLY)
+ disk_entry->access = DS_READ_ONLY;
+ else
+ disk_entry->access = DS_READ_WRITE;
+
+ strlcpy(disk_entry->dev_name, entry->name,
+ sizeof(disk_entry->dev_name));
+
+ disk_entry->media = DSM_RAMDISK;
+ disk_entry->removable = SNMP_FALSE;
+ }
+
+ disk_entry->flags |= HR_DISKSTORAGE_FOUND;
+ disk_entry->flags |= HR_DISKSTORAGE_MD;
+ disk_entry->r_tick = this_tick;
+ }
+}
+
+/**
+ * Find rest of disks
+ */
+static void
+disk_OS_get_disks(void)
+{
+ size_t disk_cnt = 0;
+ struct device_entry *entry;
+ struct disk_entry *disk_entry;
+
+ size_t need = 0;
+
+ if (sysctlbyname("kern.disks", NULL, &need, NULL, 0) == -1) {
+ syslog(LOG_ERR, "%s: sysctl_1 kern.disks failed: %m", __func__);
+ return;
+ }
+
+ if (need == 0)
+ return;
+
+ if (disk_list_len != need + 1 || disk_list == NULL) {
+ disk_list_len = need + 1;
+ disk_list = reallocf(disk_list, disk_list_len);
+ }
+
+ if (disk_list == NULL) {
+ syslog(LOG_ERR, "%s: reallocf failed", __func__);
+ disk_list_len = 0;
+ return;
+ }
+
+ memset(disk_list, 0, disk_list_len);
+
+ if (sysctlbyname("kern.disks", disk_list, &need, NULL, 0) == -1 ||
+ disk_list[0] == 0) {
+ syslog(LOG_ERR, "%s: sysctl_2 kern.disks failed: %m", __func__);
+ return;
+ }
+
+ for (disk_cnt = 0; disk_cnt < need; disk_cnt++) {
+ char *disk = NULL;
+ char disk_device[128] = "";
+
+ disk = strsep(&disk_list, " ");
+ if (disk == NULL)
+ break;
+
+ snprintf(disk_device, sizeof(disk_device),
+ "%s%s", _PATH_DEV, disk);
+
+ /* First check if the disk is in the hrDeviceTable. */
+ if ((entry = device_find_by_name(disk)) == NULL) {
+ /*
+ * not found there - insert it as immutable
+ */
+ syslog(LOG_WARNING, "%s: device '%s' not in "
+ "device list", __func__, disk);
+
+ if ((entry = device_entry_create(disk, "", "")) == NULL)
+ continue;
+
+ entry->flags |= HR_DEVICE_IMMUTABLE;
+ }
+
+ entry->type = &OIDX_hrDeviceDiskStorage_c;
+
+ /* Then check hrDiskStorage table for this device */
+ disk_entry = disk_find_by_index(entry->index);
+ if (disk_entry == NULL) {
+ disk_entry = disk_entry_create(entry);
+ if (disk_entry == NULL)
+ continue;
+ }
+
+ disk_entry->flags |= HR_DISKSTORAGE_FOUND;
+
+ if ((disk_entry->flags & HR_DISKSTORAGE_ATA) ||
+ (disk_entry->flags & HR_DISKSTORAGE_MD)) {
+ /*
+ * ATA/MD detection is running before this one,
+ * so don't waste the time here
+ */
+ continue;
+ }
+
+ disk_entry->access = DS_READ_WRITE;
+ disk_entry->media = DSM_UNKNOWN;
+ disk_entry->removable = SNMP_FALSE;
+
+ if (strncmp(disk_entry->dev_name, "da", 2) == 0) {
+ disk_entry->media = DSM_HARDDISK;
+ disk_entry->removable = SNMP_FALSE;
+ } else if (strncmp(disk_entry->dev_name, "cd", 2) == 0) {
+ disk_entry->media = DSM_OPTICALDISKROM;
+ disk_entry->removable = SNMP_TRUE;
+ } else {
+ disk_entry->media = DSM_UNKNOWN;
+ disk_entry->removable = SNMP_FALSE;
+ }
+
+ strlcpy((char *)disk_entry->dev_name, disk,
+ sizeof(disk_entry->dev_name));
+
+ disk_query_disk(disk_entry);
+ disk_entry->r_tick = this_tick;
+ }
+}
+
+/**
+ * Refresh routine for hrDiskStorageTable
+ * Usable for polling the system for any changes.
+ */
+void
+refresh_disk_storage_tbl(int force)
+{
+ struct disk_entry *entry, *entry_tmp;
+
+ if (disk_storage_tick != 0 && !force &&
+ this_tick - disk_storage_tick < disk_storage_tbl_refresh) {
+ HRDBG("no refresh needed");
+ return;
+ }
+
+ partition_tbl_pre_refresh();
+
+ /* mark each entry as missing */
+ TAILQ_FOREACH(entry, &disk_tbl, link)
+ entry->flags &= ~HR_DISKSTORAGE_FOUND;
+
+ disk_OS_get_ATA_disks(); /* this must be called first ! */
+ disk_OS_get_MD_disks();
+ disk_OS_get_disks();
+
+ /*
+ * Purge items that disappeared
+ */
+ TAILQ_FOREACH_SAFE(entry, &disk_tbl, link, entry_tmp)
+ if (!(entry->flags & HR_DISKSTORAGE_FOUND))
+ /* XXX remove IMMUTABLE entries that have disappeared */
+ disk_entry_delete(entry);
+
+ disk_storage_tick = this_tick;
+
+ partition_tbl_post_refresh();
+
+ HRDBG("refresh DONE");
+}
+
+/*
+ * Init the things for both of hrDiskStorageTable
+ */
+int
+init_disk_storage_tbl(void)
+{
+ char mddev[32] = "";
+
+ /* Try to load md.ko if not loaded already */
+ mdmaybeload();
+
+ md_fd = -1;
+ snprintf(mddev, sizeof(mddev) - 1, "%s%s", _PATH_DEV, MDCTL_NAME);
+ if ((md_fd = open(mddev, O_RDWR)) == -1) {
+ syslog(LOG_ERR, "open %s failed: %m", mddev);
+ return (-1);
+ }
+
+ refresh_disk_storage_tbl(1);
+
+ return (0);
+}
+
+/*
+ * Finalization routine for hrDiskStorageTable
+ * It destroys the lists and frees any allocated heap memory
+ */
+void
+fini_disk_storage_tbl(void)
+{
+ struct disk_entry *n1;
+
+ while ((n1 = TAILQ_FIRST(&disk_tbl)) != NULL) {
+ TAILQ_REMOVE(&disk_tbl, n1, link);
+ free(n1);
+ }
+
+ free(disk_list);
+
+ if (md_fd > 0) {
+ if (close(md_fd) == -1)
+ syslog(LOG_ERR,"close (/dev/mdctl) failed: %m");
+ md_fd = -1;
+ }
+}
+
+/*
+ * This is the implementation for a generated (by our SNMP "compiler" tool)
+ * function prototype, see hostres_tree.h
+ * It handles the SNMP operations for hrDiskStorageTable
+ */
+int
+op_hrDiskStorageTable(struct snmp_context *ctx __unused,
+ struct snmp_value *value, u_int sub, u_int iidx __unused,
+ enum snmp_op curr_op)
+{
+ struct disk_entry *entry;
+
+ refresh_disk_storage_tbl(0);
+
+ switch (curr_op) {
+
+ case SNMP_OP_GETNEXT:
+ if ((entry = NEXT_OBJECT_INT(&disk_tbl,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ value->var.len = sub + 1;
+ value->var.subs[sub] = entry->index;
+ goto get;
+
+ case SNMP_OP_GET:
+ if ((entry = FIND_OBJECT_INT(&disk_tbl,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_SET:
+ if ((entry = FIND_OBJECT_INT(&disk_tbl,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NO_CREATION);
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ abort();
+ }
+ abort();
+
+ get:
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_hrDiskStorageAccess:
+ value->v.integer = entry->access;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrDiskStorageMedia:
+ value->v.integer = entry->media;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrDiskStorageRemoveble:
+ value->v.integer = entry->removable;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrDiskStorageCapacity:
+ value->v.integer = entry->capacity;
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+}
diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_fs_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_fs_tbl.c
new file mode 100644
index 0000000..a35743b
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_fs_tbl.c
@@ -0,0 +1,472 @@
+/*-
+ * Copyright (c) 2005-2006 The FreeBSD Project
+ * All rights reserved.
+ *
+ * Author: Victor Cruceru <soc-victor@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+/*
+ * Host Resources MIB for SNMPd. Implementation for hrFSTable
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/mount.h>
+
+#include <assert.h>
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <sysexits.h>
+
+#include "hostres_snmp.h"
+#include "hostres_oid.h"
+#include "hostres_tree.h"
+
+/*
+ * File system access enum
+ */
+enum hrFSAccess {
+ FS_READ_WRITE = 1,
+ FS_READ_ONLY = 2
+};
+
+/* maximum length (according to MIB) for fs_entry::mountPoint */
+#define FS_MP_MLEN (128 + 1)
+
+/* maximum length (according to MIB) for fs_entry::remoteMountPoint */
+#define FS_RMP_MLEN (128 + 1)
+
+/*
+ * This structure is used to hold a SNMP table entry
+ * for HOST-RESOURCES-MIB's hrFSTable
+ */
+struct fs_entry {
+ int32_t index;
+ u_char *mountPoint;
+ u_char *remoteMountPoint;
+ const struct asn_oid *type;
+ int32_t access; /* enum hrFSAccess, see above */
+ int32_t bootable; /* TruthValue */
+ int32_t storageIndex; /* hrStorageTblEntry::index */
+ u_char lastFullBackupDate[11];
+ u_char lastPartialBackupDate[11];
+#define HR_FS_FOUND 0x001
+ uint32_t flags; /* not in mib table, for internal use */
+ TAILQ_ENTRY(fs_entry) link;
+};
+TAILQ_HEAD(fs_tbl, fs_entry);
+
+/*
+ * Next structure is used to keep o list of mappings from a specific name
+ * (a_name) to an entry in the hrFSTblEntry. We are trying to keep the same
+ * index for a specific name at least for the duration of one SNMP agent run.
+ */
+struct fs_map_entry {
+ int32_t hrIndex; /* used for fs_entry::index */
+ u_char *a_name; /* map key same as fs_entry::mountPoint */
+
+ /* may be NULL if the respective hrFSTblEntry is (temporally) gone */
+ struct fs_entry *entry;
+ STAILQ_ENTRY(fs_map_entry) link;
+};
+STAILQ_HEAD(fs_map, fs_map_entry);
+
+/* head of the list with hrFSTable's entries */
+static struct fs_tbl fs_tbl = TAILQ_HEAD_INITIALIZER(fs_tbl);
+
+/* for consistent table indexing */
+static struct fs_map fs_map = STAILQ_HEAD_INITIALIZER(fs_map);
+
+/* next index available for hrFSTable */
+static uint32_t next_fs_index = 1;
+
+/* last tick when hrFSTable was updated */
+static uint64_t fs_tick;
+
+/* maximum number of ticks between refreshs */
+uint32_t fs_tbl_refresh = HR_FS_TBL_REFRESH * 100;
+
+/* some constants */
+static const struct asn_oid OIDX_hrFSBerkeleyFFS_c = OIDX_hrFSBerkeleyFFS;
+static const struct asn_oid OIDX_hrFSiso9660_c = OIDX_hrFSiso9660;
+static const struct asn_oid OIDX_hrFSNFS_c = OIDX_hrFSNFS;
+static const struct asn_oid OIDX_hrFSLinuxExt2_c = OIDX_hrFSLinuxExt2;
+static const struct asn_oid OIDX_hrFSOther_c = OIDX_hrFSOther;
+static const struct asn_oid OIDX_hrFSFAT32_c = OIDX_hrFSFAT32;
+static const struct asn_oid OIDX_hrFSNTFS_c = OIDX_hrFSNTFS;
+static const struct asn_oid OIDX_hrFSNetware_c = OIDX_hrFSNetware;
+static const struct asn_oid OIDX_hrFSHPFS_c = OIDX_hrFSHPFS;
+static const struct asn_oid OIDX_hrFSUnknown_c = OIDX_hrFSUnknown;
+
+/* file system type map */
+static const struct {
+ const char *str; /* the type string */
+ const struct asn_oid *oid; /* the OID to return */
+} fs_type_map[] = {
+ { "ufs", &OIDX_hrFSBerkeleyFFS_c },
+ { "cd9660", &OIDX_hrFSiso9660_c },
+ { "nfs", &OIDX_hrFSNFS_c },
+ { "ext2fs", &OIDX_hrFSLinuxExt2_c },
+ { "procfs", &OIDX_hrFSOther_c },
+ { "devfs", &OIDX_hrFSOther_c },
+ { "msdosfs", &OIDX_hrFSFAT32_c },
+ { "ntfs", &OIDX_hrFSNTFS_c },
+ { "nwfs", &OIDX_hrFSNetware_c },
+ { "hpfs", &OIDX_hrFSHPFS_c },
+ { "smbfs", &OIDX_hrFSOther_c },
+};
+#define N_FS_TYPE_MAP (sizeof(fs_type_map) / sizeof(fs_type_map[0]))
+
+/**
+ * Create an entry into the FS table and an entry in the map (if needed).
+ */
+static struct fs_entry *
+fs_entry_create(const char *name)
+{
+ struct fs_entry *entry;
+ struct fs_map_entry *map;
+
+ assert(name != NULL);
+ assert(strlen(name) > 0);
+
+ STAILQ_FOREACH(map, &fs_map, link)
+ if (strcmp(map->a_name, name) == 0)
+ break;
+
+ if (map == NULL) {
+ size_t mount_point_len;
+
+ /* new object - get a new index */
+ if (next_fs_index > INT_MAX) {
+ /* Unrecoverable error - die clean and quicly*/
+ syslog(LOG_ERR, "%s: hrFSTable index wrap", __func__);
+ errx(EX_SOFTWARE, "hrFSTable index wrap");
+ }
+
+ if ((map = malloc(sizeof(*map))) == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ return (NULL);
+ }
+
+ mount_point_len = strlen(name) + 1;
+ if (mount_point_len > FS_MP_MLEN)
+ mount_point_len = FS_MP_MLEN;
+
+ if ((map->a_name = malloc(mount_point_len)) == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ free(map);
+ return (NULL);
+ }
+
+ strlcpy(map->a_name, name, mount_point_len);
+
+ map->hrIndex = next_fs_index++;
+ map->entry = NULL;
+ STAILQ_INSERT_TAIL(&fs_map, map, link);
+
+ HRDBG("%s added into hrFSMap at index=%d", name, map->hrIndex);
+ } else {
+ HRDBG("%s exists in hrFSMap index=%d", name, map->hrIndex);
+ }
+
+ if ((entry = malloc(sizeof(*entry))) == NULL) {
+ syslog(LOG_WARNING, "%s: %m", __func__);
+ return (NULL);
+ }
+
+ if ((entry->mountPoint = strdup(name)) == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__);
+ free(entry);
+ return (NULL);
+ }
+
+ entry->index = map->hrIndex;
+ map->entry = entry;
+
+ INSERT_OBJECT_INT(entry, &fs_tbl);
+ return (entry);
+}
+
+/**
+ * Delete an entry in the FS table.
+ */
+static void
+fs_entry_delete(struct fs_entry* entry)
+{
+ struct fs_map_entry *map;
+
+ assert(entry != NULL);
+
+ TAILQ_REMOVE(&fs_tbl, entry, link);
+ STAILQ_FOREACH(map, &fs_map, link)
+ if (map->entry == entry) {
+ map->entry = NULL;
+ break;
+ }
+ free(entry->mountPoint);
+ free(entry->remoteMountPoint);
+ free(entry);
+}
+
+/**
+ * Find a table entry by its name
+ */
+static struct fs_entry *
+fs_find_by_name(const char *name)
+{
+ struct fs_entry *entry;
+
+ TAILQ_FOREACH(entry, &fs_tbl, link)
+ if (strcmp(entry->mountPoint, name) == 0)
+ return (entry);
+
+ return (NULL);
+}
+
+/**
+ * Get rid of all data
+ */
+void
+fini_fs_tbl(void)
+{
+ struct fs_map_entry *n1;
+
+ while ((n1 = STAILQ_FIRST(&fs_map)) != NULL) {
+ STAILQ_REMOVE_HEAD(&fs_map, link);
+ if (n1->entry != NULL) {
+ TAILQ_REMOVE(&fs_tbl, n1->entry, link);
+ free(n1->entry->mountPoint);
+ free(n1->entry->remoteMountPoint);
+ free(n1->entry);
+ }
+ free(n1->a_name);
+ free(n1);
+ }
+ assert(TAILQ_EMPTY(&fs_tbl));
+}
+
+/**
+ * Called before the refreshing is started from the storage table.
+ */
+void
+fs_tbl_pre_refresh(void)
+{
+ struct fs_entry *entry;
+
+ /* mark each entry as missisng */
+ TAILQ_FOREACH(entry, &fs_tbl, link)
+ entry->flags &= ~HR_FS_FOUND;
+}
+
+/**
+ * Called after refreshing from the storage table.
+ */
+void
+fs_tbl_post_refresh(void)
+{
+ struct fs_entry *entry, *entry_tmp;
+
+ /*
+ * Purge items that disappeared
+ */
+ TAILQ_FOREACH_SAFE(entry, &fs_tbl, link, entry_tmp)
+ if (!(entry->flags & HR_FS_FOUND))
+ fs_entry_delete(entry);
+
+ fs_tick = this_tick;
+}
+
+/*
+ * Refresh the FS table. This is done by forcing a refresh of the storage table.
+ */
+void
+refresh_fs_tbl(void)
+{
+
+ if (fs_tick == 0 || this_tick - fs_tick >= fs_tbl_refresh) {
+ refresh_storage_tbl(1);
+ HRDBG("refresh DONE");
+ }
+}
+
+/**
+ * Get the type OID for a given file system
+ */
+const struct asn_oid *
+fs_get_type(const struct statfs *fs_p)
+{
+ u_int t;
+
+ assert(fs_p != NULL);
+
+ for (t = 0; t < N_FS_TYPE_MAP; t++)
+ if (strcmp(fs_type_map[t].str, fs_p->f_fstypename) == 0)
+ return (fs_type_map[t].oid);
+
+ return (&OIDX_hrFSUnknown_c);
+}
+
+/*
+ * Given information returned from statfs(2) either create a new entry into
+ * the fs_tbl or refresh the entry if it is already there.
+ */
+void
+fs_tbl_process_statfs_entry(const struct statfs *fs_p, int32_t storage_idx)
+{
+ struct fs_entry *entry;
+
+ assert(fs_p != 0);
+
+ HRDBG("for hrStorageEntry::index %d", storage_idx);
+
+ if (fs_p == NULL)
+ return;
+
+ if ((entry = fs_find_by_name(fs_p->f_mntonname)) != NULL ||
+ (entry = fs_entry_create(fs_p->f_mntonname)) != NULL) {
+ entry->flags |= HR_FS_FOUND;
+
+ if (!(fs_p->f_flags & MNT_LOCAL)) {
+ /* this is a remote mount */
+ entry->remoteMountPoint = strdup(fs_p->f_mntfromname);
+ /* if strdup failed, let it be NULL */
+
+ } else {
+ entry->remoteMountPoint = strdup("");
+ /* if strdup failed, let it be NULL */
+ }
+
+ entry->type = fs_get_type(fs_p);
+
+ if ((fs_p->f_flags & MNT_RDONLY) == MNT_RDONLY)
+ entry->access = FS_READ_ONLY;
+ else
+ entry->access = FS_READ_WRITE;
+
+ /* FIXME - bootable fs ?! */
+ entry->bootable = TRUTH_MK((fs_p->f_flags & MNT_ROOTFS)
+ == MNT_ROOTFS);
+
+ entry->storageIndex = storage_idx;
+
+ /* Info not available */
+ memset(entry->lastFullBackupDate, 0,
+ sizeof(entry->lastFullBackupDate));
+
+ /* Info not available */
+ memset(entry->lastPartialBackupDate, 0,
+ sizeof(entry->lastPartialBackupDate));
+
+ handle_partition_fs_index(fs_p->f_mntfromname, entry->index);
+ }
+}
+
+/*
+ * This is the implementation for a generated (by our SNMP "compiler" tool)
+ * function prototype, see hostres_tree.h
+ * It handles the SNMP operations for hrFSTable
+ */
+int
+op_hrFSTable(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op curr_op)
+{
+ struct fs_entry *entry;
+
+ refresh_fs_tbl();
+
+ switch (curr_op) {
+
+ case SNMP_OP_GETNEXT:
+ if ((entry = NEXT_OBJECT_INT(&fs_tbl,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ value->var.len = sub + 1;
+ value->var.subs[sub] = entry->index;
+ goto get;
+
+ case SNMP_OP_GET:
+ if ((entry = FIND_OBJECT_INT(&fs_tbl,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_SET:
+ if ((entry = FIND_OBJECT_INT(&fs_tbl,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NO_CREATION);
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ abort();
+ }
+ abort();
+ get:
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_hrFSIndex:
+ value->v.integer = entry->index;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrFSMountPoint:
+ return (string_get(value, entry->mountPoint, -1));
+
+ case LEAF_hrFSRemoteMountPoint:
+ if (entry->remoteMountPoint == NULL)
+ return (string_get(value, "", -1));
+ else
+ return (string_get(value, entry->remoteMountPoint, -1));
+ break;
+
+ case LEAF_hrFSType:
+ assert(entry->type != NULL);
+ value->v.oid = *(entry->type);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrFSAccess:
+ value->v.integer = entry->access;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrFSBootable:
+ value->v.integer = entry->bootable;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrFSStorageIndex:
+ value->v.integer = entry->storageIndex;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrFSLastFullBackupDate:
+ return (string_get(value, entry->lastFullBackupDate, 8));
+
+ case LEAF_hrFSLastPartialBackupDate:
+ return (string_get(value, entry->lastPartialBackupDate, 8));
+ }
+ abort();
+}
diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_network_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_network_tbl.c
new file mode 100644
index 0000000..4329a1c
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_network_tbl.c
@@ -0,0 +1,302 @@
+/*-
+ * Copyright (c) 2005-2006 The FreeBSD Project
+ * All rights reserved.
+ *
+ * Author: Victor Cruceru <soc-victor@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+/*
+ * Host Resources MIB implementation for SNMPd: instrumentation for
+ * hrNetworkTable
+ */
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_mib.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <ifaddrs.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "hostres_snmp.h"
+#include "hostres_oid.h"
+#include "hostres_tree.h"
+
+#include <bsnmp/snmp_mibII.h>
+
+/*
+ * This structure is used to hold a SNMP table entry
+ * for HOST-RESOURCES-MIB's hrNetworkTable
+ */
+struct network_entry {
+ int32_t index;
+ int32_t ifIndex;
+ TAILQ_ENTRY(network_entry) link;
+#define HR_NETWORK_FOUND 0x001
+ uint32_t flags;
+
+};
+TAILQ_HEAD(network_tbl, network_entry);
+
+/* the head of the list with hrNetworkTable's entries */
+static struct network_tbl network_tbl = TAILQ_HEAD_INITIALIZER(network_tbl);
+
+/* last (agent) tick when hrNetworkTable was updated */
+static uint64_t network_tick;
+
+/* maximum number of ticks between updates of network table */
+uint32_t network_tbl_refresh = HR_NETWORK_TBL_REFRESH * 100;
+
+/* Constants */
+static const struct asn_oid OIDX_hrDeviceNetwork_c = OIDX_hrDeviceNetwork;
+
+/**
+ * Create a new entry into the network table
+ */
+static struct network_entry *
+network_entry_create(const struct device_entry *devEntry)
+{
+ struct network_entry *entry;
+
+ assert(devEntry != NULL);
+ if (devEntry == NULL)
+ return (NULL);
+
+ if ((entry = malloc(sizeof(*entry))) == NULL) {
+ syslog(LOG_WARNING, "%s: %m", __func__);
+ return (NULL);
+ }
+
+ memset(entry, 0, sizeof(*entry));
+ entry->index = devEntry->index;
+ INSERT_OBJECT_INT(entry, &network_tbl);
+
+ return (entry);
+}
+
+/**
+ * Delete an entry in the network table
+ */
+static void
+network_entry_delete(struct network_entry* entry)
+{
+
+ TAILQ_REMOVE(&network_tbl, entry, link);
+ free(entry);
+}
+
+/**
+ * Fetch the interfaces from the mibII module, get their real name from the
+ * kernel and try to find it in the device table.
+ */
+static void
+network_get_interfaces(void)
+{
+ struct device_entry *dev;
+ struct network_entry *net;
+ struct mibif *ifp;
+ int name[6];
+ size_t len;
+ char *dname;
+
+ name[0] = CTL_NET;
+ name[1] = PF_LINK;
+ name[2] = NETLINK_GENERIC;
+ name[3] = IFMIB_IFDATA;
+ name[5] = IFDATA_DRIVERNAME;
+
+ for (ifp = mib_first_if(); ifp != NULL; ifp = mib_next_if(ifp)) {
+ HRDBG("%s %s", ifp->name, ifp->descr);
+
+ name[4] = ifp->sysindex;
+
+ /* get the original name */
+ len = 0;
+ if (sysctl(name, 6, NULL, &len, 0, 0) < 0) {
+ syslog(LOG_ERR, "sysctl(net.link.ifdata.%d."
+ "drivername): %m", ifp->sysindex);
+ continue;
+ }
+ if ((dname = malloc(len)) == NULL) {
+ syslog(LOG_ERR, "malloc: %m");
+ continue;
+ }
+ if (sysctl(name, 6, dname, &len, 0, 0) < 0) {
+ syslog(LOG_ERR, "sysctl(net.link.ifdata.%d."
+ "drivername): %m", ifp->sysindex);
+ free(dname);
+ continue;
+ }
+
+ HRDBG("got device %s (%s)", ifp->name, dname);
+
+ if ((dev = device_find_by_name(dname)) == NULL) {
+ HRDBG("%s not in hrDeviceTable", dname);
+ free(dname);
+ continue;
+ }
+ HRDBG("%s found in hrDeviceTable", dname);
+
+ dev->type = &OIDX_hrDeviceNetwork_c;
+ dev->flags |= HR_DEVICE_IMMUTABLE;
+
+ free(dname);
+
+ /* Then check hrNetworkTable for this device */
+ TAILQ_FOREACH(net, &network_tbl, link)
+ if (net->index == dev->index)
+ break;
+
+ if (net == NULL && (net = network_entry_create(dev)) == NULL)
+ continue;
+
+ net->flags |= HR_NETWORK_FOUND;
+ net->ifIndex = ifp->index;
+ }
+
+ network_tick = this_tick;
+}
+
+/**
+ * Finalization routine for hrNetworkTable.
+ * It destroys the lists and frees any allocated heap memory.
+ */
+void
+fini_network_tbl(void)
+{
+ struct network_entry *n1;
+
+ while ((n1 = TAILQ_FIRST(&network_tbl)) != NULL) {
+ TAILQ_REMOVE(&network_tbl, n1, link);
+ free(n1);
+ }
+}
+
+/**
+ * Get the interface list from mibII only at this point to be sure that
+ * it is there already.
+ */
+void
+start_network_tbl(void)
+{
+
+ mib_refresh_iflist();
+ network_get_interfaces();
+}
+
+/**
+ * Refresh the table.
+ */
+static void
+refresh_network_tbl(void)
+{
+ struct network_entry *entry, *entry_tmp;
+
+ if (this_tick - network_tick < network_tbl_refresh) {
+ HRDBG("no refresh needed");
+ return;
+ }
+
+ /* mark each entry as missing */
+ TAILQ_FOREACH(entry, &network_tbl, link)
+ entry->flags &= ~HR_NETWORK_FOUND;
+
+ network_get_interfaces();
+
+ /*
+ * Purge items that disappeared
+ */
+ TAILQ_FOREACH_SAFE(entry, &network_tbl, link, entry_tmp) {
+ if (!(entry->flags & HR_NETWORK_FOUND))
+ network_entry_delete(entry);
+ }
+
+ HRDBG("refresh DONE");
+}
+
+/*
+ * This is the implementation for a generated (by our SNMP tool)
+ * function prototype, see hostres_tree.h
+ * It handles the SNMP operations for hrNetworkTable
+ */
+int
+op_hrNetworkTable(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op curr_op)
+{
+ struct network_entry *entry;
+
+ refresh_network_tbl();
+
+ switch (curr_op) {
+
+ case SNMP_OP_GETNEXT:
+ if ((entry = NEXT_OBJECT_INT(&network_tbl,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ value->var.len = sub + 1;
+ value->var.subs[sub] = entry->index;
+ goto get;
+
+ case SNMP_OP_GET:
+ if ((entry = FIND_OBJECT_INT(&network_tbl,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_SET:
+ if ((entry = FIND_OBJECT_INT(&network_tbl,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NO_CREATION);
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ abort();
+ }
+ abort();
+
+ get:
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_hrNetworkIfIndex:
+ value->v.integer = entry->ifIndex;
+ return (SNMP_ERR_NOERROR);
+
+ }
+ abort();
+}
diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_partition_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_partition_tbl.c
new file mode 100644
index 0000000..65c0012
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_partition_tbl.c
@@ -0,0 +1,630 @@
+/*-
+ * Copyright (c) 2005-2006 The FreeBSD Project
+ * All rights reserved.
+ *
+ * Author: Victor Cruceru <soc-victor@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+/*
+ * Host Resources MIB: hrPartitionTable implementation for SNMPd.
+ */
+
+#include <sys/types.h>
+#include <sys/limits.h>
+
+#include <assert.h>
+#include <err.h>
+#include <inttypes.h>
+#include <libgeom.h>
+#include <paths.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <sysexits.h>
+
+#include "hostres_snmp.h"
+#include "hostres_oid.h"
+#include "hostres_tree.h"
+
+#ifdef PC98
+#define HR_FREEBSD_PART_TYPE 0xc494
+#else
+#define HR_FREEBSD_PART_TYPE 165
+#endif
+
+/* Maximum length for label and id including \0 */
+#define PART_STR_MLEN (128 + 1)
+
+/*
+ * One row in the hrPartitionTable
+ */
+struct partition_entry {
+ asn_subid_t index[2];
+ u_char *label; /* max allocated len will be PART_STR_MLEN */
+ u_char *id; /* max allocated len will be PART_STR_MLEN */
+ int32_t size;
+ int32_t fs_Index;
+ TAILQ_ENTRY(partition_entry) link;
+#define HR_PARTITION_FOUND 0x001
+ uint32_t flags;
+};
+TAILQ_HEAD(partition_tbl, partition_entry);
+
+/*
+ * This table is used to get a consistent indexing. It saves the name -> index
+ * mapping while we rebuild the partition table.
+ */
+struct partition_map_entry {
+ int32_t index; /* partition_entry::index */
+ u_char *id; /* max allocated len will be PART_STR_MLEN */
+
+ /*
+ * next may be NULL if the respective partition_entry
+ * is (temporally) gone.
+ */
+ struct partition_entry *entry;
+ STAILQ_ENTRY(partition_map_entry) link;
+};
+STAILQ_HEAD(partition_map, partition_map_entry);
+
+/* Mapping table for consistent indexing */
+static struct partition_map partition_map =
+ STAILQ_HEAD_INITIALIZER(partition_map);
+
+/* THE partition table. */
+static struct partition_tbl partition_tbl =
+ TAILQ_HEAD_INITIALIZER(partition_tbl);
+
+/* next int available for indexing the hrPartitionTable */
+static uint32_t next_partition_index = 1;
+
+/*
+ * Partition_entry_cmp is used for INSERT_OBJECT_FUNC_LINK
+ * macro.
+ */
+static int
+partition_entry_cmp(const struct partition_entry *a,
+ const struct partition_entry *b)
+{
+ assert(a != NULL);
+ assert(b != NULL);
+
+ if (a->index[0] < b->index[0])
+ return (-1);
+
+ if (a->index[0] > b->index[0])
+ return (+1);
+
+ if (a->index[1] < b->index[1])
+ return (-1);
+
+ if (a->index[1] > b->index[1])
+ return (+1);
+
+ return (0);
+}
+
+/*
+ * Partition_idx_cmp is used for NEXT_OBJECT_FUNC and FIND_OBJECT_FUNC
+ * macros
+ */
+static int
+partition_idx_cmp(const struct asn_oid *oid, u_int sub,
+ const struct partition_entry *entry)
+{
+ u_int i;
+
+ for (i = 0; i < 2 && i < oid->len - sub; i++) {
+ if (oid->subs[sub + i] < entry->index[i])
+ return (-1);
+ if (oid->subs[sub + i] > entry->index[i])
+ return (+1);
+ }
+ if (oid->len - sub < 2)
+ return (-1);
+ if (oid->len - sub > 2)
+ return (+1);
+
+ return (0);
+}
+
+/**
+ * Create a new partition table entry
+ */
+static struct partition_entry *
+partition_entry_create(int32_t ds_index, const char *chunk_name)
+{
+ struct partition_entry *entry;
+ struct partition_map_entry *map;
+ size_t id_len;
+
+ /* sanity checks */
+ assert(chunk_name != NULL);
+ if (chunk_name == NULL || chunk_name[0] == '\0')
+ return (NULL);
+
+ /* check whether we already have seen this partition */
+ STAILQ_FOREACH(map, &partition_map, link)
+ if (strcmp(map->id, chunk_name) == 0)
+ break;
+
+ if (map == NULL) {
+ /* new object - get a new index and create a map */
+
+ if (next_partition_index > INT_MAX) {
+ /* Unrecoverable error - die clean and quicly*/
+ syslog(LOG_ERR, "%s: hrPartitionTable index wrap",
+ __func__);
+ errx(EX_SOFTWARE, "hrPartitionTable index wrap");
+ }
+
+ if ((map = malloc(sizeof(*map))) == NULL) {
+ syslog(LOG_ERR, "hrPartitionTable: %s: %m", __func__);
+ return (NULL);
+ }
+
+ id_len = strlen(chunk_name) + 1;
+ if (id_len > PART_STR_MLEN)
+ id_len = PART_STR_MLEN;
+
+ if ((map->id = malloc(id_len)) == NULL) {
+ free(map);
+ return (NULL);
+ }
+
+ map->index = next_partition_index++;
+
+ strlcpy(map->id, chunk_name, id_len);
+
+ map->entry = NULL;
+
+ STAILQ_INSERT_TAIL(&partition_map, map, link);
+
+ HRDBG("%s added into hrPartitionMap at index=%d",
+ chunk_name, map->index);
+
+ } else {
+ HRDBG("%s exists in hrPartitionMap index=%d",
+ chunk_name, map->index);
+ }
+
+ if ((entry = malloc(sizeof(*entry))) == NULL) {
+ syslog(LOG_WARNING, "hrPartitionTable: %s: %m", __func__);
+ return (NULL);
+ }
+ memset(entry, 0, sizeof(*entry));
+
+ /* create the index */
+ entry->index[0] = ds_index;
+ entry->index[1] = map->index;
+
+ map->entry = entry;
+
+ if ((entry->id = strdup(map->id)) == NULL) {
+ free(entry);
+ return (NULL);
+ }
+
+ /*
+ * reuse id_len from here till the end of this function
+ * for partition_entry::label
+ */
+ id_len = strlen(_PATH_DEV) + strlen(chunk_name) + 1;
+
+ if (id_len > PART_STR_MLEN)
+ id_len = PART_STR_MLEN;
+
+ if ((entry->label = malloc(id_len )) == NULL) {
+ free(entry->id);
+ free(entry);
+ return (NULL);
+ }
+
+ snprintf(entry->label, id_len, "%s%s", _PATH_DEV, chunk_name);
+
+ INSERT_OBJECT_FUNC_LINK(entry, &partition_tbl, link,
+ partition_entry_cmp);
+
+ return (entry);
+}
+
+/**
+ * Delete a partition table entry but keep the map entry intact.
+ */
+static void
+partition_entry_delete(struct partition_entry *entry)
+{
+ struct partition_map_entry *map;
+
+ assert(entry != NULL);
+
+ TAILQ_REMOVE(&partition_tbl, entry, link);
+ STAILQ_FOREACH(map, &partition_map, link)
+ if (map->entry == entry) {
+ map->entry = NULL;
+ break;
+ }
+ free(entry->id);
+ free(entry->label);
+ free(entry);
+}
+
+/**
+ * Find a partition table entry by name. If none is found, return NULL.
+ */
+static struct partition_entry *
+partition_entry_find_by_name(const char *name)
+{
+ struct partition_entry *entry = NULL;
+
+ TAILQ_FOREACH(entry, &partition_tbl, link)
+ if (strcmp(entry->id, name) == 0)
+ return (entry);
+
+ return (NULL);
+}
+
+/**
+ * Find a partition table entry by label. If none is found, return NULL.
+ */
+static struct partition_entry *
+partition_entry_find_by_label(const char *name)
+{
+ struct partition_entry *entry = NULL;
+
+ TAILQ_FOREACH(entry, &partition_tbl, link)
+ if (strcmp(entry->label, name) == 0)
+ return (entry);
+
+ return (NULL);
+}
+
+/**
+ * Process a chunk from libgeom(4). A chunk is either a slice or a partition.
+ * If necessary create a new partition table entry for it. In any case
+ * set the size field of the entry and set the FOUND flag.
+ */
+static void
+handle_chunk(int32_t ds_index, const char *chunk_name, off_t chunk_size)
+{
+ struct partition_entry *entry;
+ daddr_t k_size;
+
+ assert(chunk_name != NULL);
+ assert(chunk_name[0] != '\0');
+ if (chunk_name == NULL || chunk_name == '\0')
+ return;
+
+ HRDBG("ANALYZE chunk %s", chunk_name);
+
+ if ((entry = partition_entry_find_by_name(chunk_name)) == NULL)
+ if ((entry = partition_entry_create(ds_index,
+ chunk_name)) == NULL)
+ return;
+
+ entry->flags |= HR_PARTITION_FOUND;
+
+ /* actual size may overflow the SNMP type */
+ k_size = chunk_size / 1024;
+ entry->size = (k_size > (off_t)INT_MAX ? INT_MAX : k_size);
+}
+
+/**
+ * Start refreshing the partition table. A call to this function will
+ * be followed by a call to handleDiskStorage() for every disk, followed
+ * by a single call to the post_refresh function.
+ */
+void
+partition_tbl_pre_refresh(void)
+{
+ struct partition_entry *entry;
+
+ /* mark each entry as missing */
+ TAILQ_FOREACH(entry, &partition_tbl, link)
+ entry->flags &= ~HR_PARTITION_FOUND;
+}
+
+/**
+ * Try to find a geom(4) class by its name. Returns a pointer to that
+ * class if found NULL otherways.
+ */
+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);
+}
+
+/**
+ * Process all MBR-type partitions from the given disk.
+ */
+static void
+get_mbr(struct gclass *classp, int32_t ds_index, const char *disk_dev_name)
+{
+ struct ggeom *gp;
+ struct gprovider *pp;
+ struct gconfig *conf;
+ long part_type;
+
+ LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
+ /* We are only interested in partitions from this disk */
+ if (strcmp(gp->lg_name, disk_dev_name) != 0)
+ continue;
+
+ /*
+ * Find all the non-BSD providers (these are handled in get_bsd)
+ */
+ LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
+ LIST_FOREACH(conf, &pp->lg_config, lg_config) {
+ if (conf->lg_name == NULL ||
+ conf->lg_val == NULL ||
+ strcmp(conf->lg_name, "type") != 0)
+ continue;
+
+ /*
+ * We are not interested in BSD partitions
+ * (ie ad0s1 is not interesting at this point).
+ * We'll take care of them in detail (slice
+ * by slice) in get_bsd.
+ */
+ part_type = strtol(conf->lg_val, NULL, 10);
+ if (part_type == HR_FREEBSD_PART_TYPE)
+ break;
+ HRDBG("-> MBR PROVIDER Name: %s", pp->lg_name);
+ HRDBG("Mediasize: %jd",
+ (intmax_t)pp->lg_mediasize / 1024);
+ HRDBG("Sectorsize: %u", pp->lg_sectorsize);
+ HRDBG("Mode: %s", pp->lg_mode);
+ HRDBG("CONFIG: %s: %s",
+ conf->lg_name, conf->lg_val);
+
+ handle_chunk(ds_index, pp->lg_name,
+ pp->lg_mediasize);
+ }
+ }
+ }
+}
+
+/**
+ * Process all BSD-type partitions from the given disk.
+ */
+static void
+get_bsd_sun(struct gclass *classp, int32_t ds_index, const char *disk_dev_name)
+{
+ struct ggeom *gp;
+ struct gprovider *pp;
+
+ LIST_FOREACH(gp, &classp->lg_geom, lg_geom) {
+ /*
+ * We are only interested in those geoms starting with
+ * the disk_dev_name passed as parameter to this function.
+ */
+ if (strncmp(gp->lg_name, disk_dev_name,
+ strlen(disk_dev_name)) != 0)
+ continue;
+
+ LIST_FOREACH(pp, &gp->lg_provider, lg_provider) {
+ if (pp->lg_name == NULL)
+ continue;
+ handle_chunk(ds_index, pp->lg_name, pp->lg_mediasize);
+ }
+ }
+}
+
+/**
+ * Called from the DiskStorage table for every row. Open the GEOM(4) framework
+ * and process all the partitions in it.
+ * ds_index is the index into the DiskStorage table.
+ * This is done in two steps: for non BSD partitions the geom class "MBR" is
+ * used, for our BSD slices the "BSD" geom class.
+ */
+void
+partition_tbl_handle_disk(int32_t ds_index, const char *disk_dev_name)
+{
+ struct gmesh mesh; /* GEOM userland tree */
+ struct gclass *classp;
+ int error;
+
+ assert(disk_dev_name != NULL);
+ assert(ds_index > 0);
+
+ HRDBG("===> getting partitions for %s <===", disk_dev_name);
+
+ /* try to construct the GEOM tree */
+ if ((error = geom_gettree(&mesh)) != 0) {
+ syslog(LOG_WARNING, "cannot get GEOM tree: %m");
+ return;
+ }
+
+ /*
+ * First try the GEOM "MBR" class.
+ * This is needed for non-BSD slices (aka partitions)
+ * on PC architectures.
+ */
+ if ((classp = find_class(&mesh, "MBR")) != NULL) {
+ get_mbr(classp, ds_index, disk_dev_name);
+ } else {
+ HRDBG("cannot find \"MBR\" geom class");
+ }
+
+ /*
+ * Get the "BSD" GEOM class.
+ * Here we'll find all the info needed about the BSD slices.
+ */
+ if ((classp = find_class(&mesh, "BSD")) != NULL) {
+ get_bsd_sun(classp, ds_index, disk_dev_name);
+ } else {
+ /* no problem on sparc64 */
+ HRDBG("cannot find \"BSD\" geom class");
+ }
+
+ /*
+ * Get the "SUN" GEOM class.
+ * Here we'll find all the info needed about the BSD slices.
+ */
+ if ((classp = find_class(&mesh, "SUN")) != NULL) {
+ get_bsd_sun(classp, ds_index, disk_dev_name);
+ } else {
+ /* no problem on i386 */
+ HRDBG("cannot find \"SUN\" geom class");
+ }
+
+ geom_deletetree(&mesh);
+}
+
+/**
+ * Finish refreshing the table.
+ */
+void
+partition_tbl_post_refresh(void)
+{
+ struct partition_entry *e, *etmp;
+
+ /*
+ * Purge items that disappeared
+ */
+ TAILQ_FOREACH_SAFE(e, &partition_tbl, link, etmp)
+ if (!(e->flags & HR_PARTITION_FOUND))
+ partition_entry_delete(e);
+}
+
+/*
+ * Finalization routine for hrPartitionTable
+ * It destroys the lists and frees any allocated heap memory
+ */
+void
+fini_partition_tbl(void)
+{
+ struct partition_map_entry *m;
+
+ while ((m = STAILQ_FIRST(&partition_map)) != NULL) {
+ STAILQ_REMOVE_HEAD(&partition_map, link);
+ if(m->entry != NULL) {
+ TAILQ_REMOVE(&partition_tbl, m->entry, link);
+ free(m->entry->id);
+ free(m->entry->label);
+ free(m->entry);
+ }
+ free(m->id);
+ free(m);
+ }
+ assert(TAILQ_EMPTY(&partition_tbl));
+}
+
+/**
+ * Called from the file system code to insert the file system table index
+ * into the partition table entry. Note, that an partition table entry exists
+ * only for local file systems.
+ */
+void
+handle_partition_fs_index(const char *name, int32_t fs_idx)
+{
+ struct partition_entry *entry;
+
+ if ((entry = partition_entry_find_by_label(name)) == NULL) {
+ HRDBG("%s IS MISSING from hrPartitionTable", name);
+ return;
+ }
+ HRDBG("%s [FS index = %d] IS in hrPartitionTable", name, fs_idx);
+ entry->fs_Index = fs_idx;
+}
+
+/*
+ * This is the implementation for a generated (by our SNMP tool)
+ * function prototype, see hostres_tree.h
+ * It handles the SNMP operations for hrPartitionTable
+ */
+int
+op_hrPartitionTable(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op op)
+{
+ struct partition_entry *entry;
+
+ /*
+ * Refresh the disk storage table (which refreshes the partition
+ * table) if necessary.
+ */
+ refresh_disk_storage_tbl(0);
+
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ if ((entry = NEXT_OBJECT_FUNC(&partition_tbl,
+ &value->var, sub, partition_idx_cmp)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+
+ value->var.len = sub + 2;
+ value->var.subs[sub] = entry->index[0];
+ value->var.subs[sub + 1] = entry->index[1];
+
+ goto get;
+
+ case SNMP_OP_GET:
+ if ((entry = FIND_OBJECT_FUNC(&partition_tbl,
+ &value->var, sub, partition_idx_cmp)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_SET:
+ if ((entry = FIND_OBJECT_FUNC(&partition_tbl,
+ &value->var, sub, partition_idx_cmp)) == NULL)
+ return (SNMP_ERR_NOT_WRITEABLE);
+ return (SNMP_ERR_NO_CREATION);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ abort();
+ }
+ abort();
+
+ get:
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_hrPartitionIndex:
+ value->v.integer = entry->index[1];
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrPartitionLabel:
+ return (string_get(value, entry->label, -1));
+
+ case LEAF_hrPartitionID:
+ return(string_get(value, entry->id, -1));
+
+ case LEAF_hrPartitionSize:
+ value->v.integer = entry->size;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrPartitionFSIndex:
+ value->v.integer = entry->fs_Index;
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+}
diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_printer_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_printer_tbl.c
new file mode 100644
index 0000000..883d67a
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_printer_tbl.c
@@ -0,0 +1,398 @@
+/*-
+ * Copyright (c) 2005-2006 The FreeBSD Project
+ * All rights reserved.
+ *
+ * Author: Victor Cruceru <soc-victor@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+/*
+ * Host Resources MIB implementation for SNMPd: instrumentation for
+ * hrPrinterTable
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <paths.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "hostres_snmp.h"
+#include "hostres_oid.h"
+#include "hostres_tree.h"
+
+#include <sys/dirent.h>
+#include "lp.h"
+
+/* Constants */
+static const struct asn_oid OIDX_hrDevicePrinter_c = OIDX_hrDevicePrinter;
+
+enum PrinterStatus {
+ PS_OTHER = 1,
+ PS_UNKNOWN = 2,
+ PS_IDLE = 3,
+ PS_PRINTING = 4,
+ PS_WARMUP = 5
+};
+
+/*
+ * This structure is used to hold a SNMP table entry
+ * for HOST-RESOURCES-MIB's hrPrinterTable.
+ */
+struct printer_entry {
+ int32_t index;
+ int32_t status; /* values from PrinterStatus enum above */
+ u_char detectedErrorState[2];
+ TAILQ_ENTRY(printer_entry) link;
+#define HR_PRINTER_FOUND 0x001
+ uint32_t flags;
+
+};
+TAILQ_HEAD(printer_tbl, printer_entry);
+
+/* the hrPrinterTable */
+static struct printer_tbl printer_tbl = TAILQ_HEAD_INITIALIZER(printer_tbl);
+
+/* last (agent) tick when hrPrinterTable was updated */
+static uint64_t printer_tick;
+
+/**
+ * Create entry into the printer table.
+ */
+static struct printer_entry *
+printer_entry_create(const struct device_entry *devEntry)
+{
+ struct printer_entry *entry = NULL;
+
+ assert(devEntry != NULL);
+ if (devEntry == NULL)
+ return (NULL);
+
+ if ((entry = malloc(sizeof(*entry))) == NULL) {
+ syslog(LOG_WARNING, "hrPrinterTable: %s: %m", __func__);
+ return (NULL);
+ }
+ memset(entry, 0, sizeof(*entry));
+ entry->index = devEntry->index;
+ INSERT_OBJECT_INT(entry, &printer_tbl);
+ return (entry);
+}
+
+/**
+ * Delete entry from the printer table.
+ */
+static void
+printer_entry_delete(struct printer_entry *entry)
+{
+
+ assert(entry != NULL);
+ if (entry == NULL)
+ return;
+
+ TAILQ_REMOVE(&printer_tbl, entry, link);
+ free(entry);
+}
+
+/**
+ * Find a printer by its index
+ */
+static struct printer_entry *
+printer_find_by_index(int32_t idx)
+{
+ struct printer_entry *entry;
+
+ TAILQ_FOREACH(entry, &printer_tbl, link)
+ if (entry->index == idx)
+ return (entry);
+
+ return (NULL);
+}
+
+/**
+ * Get the status of a printer
+ */
+static enum PrinterStatus
+get_printer_status(const struct printer *pp)
+{
+ char statfile[MAXPATHLEN];
+ char lockfile[MAXPATHLEN];
+ char fline[128];
+ int fd;
+ FILE *f = NULL;
+ enum PrinterStatus ps = PS_UNKNOWN;
+
+ if (pp->lock_file[0] == '/')
+ strlcpy(lockfile, pp->lock_file, sizeof(lockfile));
+ else
+ snprintf(lockfile, sizeof(lockfile), "%s/%s",
+ pp->spool_dir, pp->lock_file);
+
+ fd = open(lockfile, O_RDONLY);
+ if (fd < 0 || flock(fd, LOCK_SH | LOCK_NB) == 0) {
+ ps = PS_IDLE;
+ goto LABEL_DONE;
+ }
+
+ if (pp->status_file[0] == '/')
+ strlcpy(statfile, pp->status_file, sizeof(statfile));
+ else
+ snprintf(statfile, sizeof(statfile), "%s/%s",
+ pp->spool_dir, pp->status_file);
+
+ f = fopen(statfile, "r");
+ if (f == NULL) {
+ syslog(LOG_ERR, "cannot open status file: %s", strerror(errno));
+ ps = PS_UNKNOWN;
+ goto LABEL_DONE;
+ }
+
+ memset(&fline[0], '\0', sizeof(line));
+ if (fgets(fline, sizeof(fline) -1, f) == NULL) {
+ ps = PS_UNKNOWN;
+ goto LABEL_DONE;
+ }
+
+ if (strstr(fline, "is ready and printing") != NULL) {
+ ps = PS_PRINTING;
+ goto LABEL_DONE;
+ }
+
+ if (strstr(fline, "to become ready (offline?)") != NULL) {
+ ps = PS_OTHER;
+ goto LABEL_DONE;
+ }
+
+LABEL_DONE:
+ if (fd >= 0)
+ (void)close(fd); /* unlocks as well */
+
+ if (f != NULL)
+ (void)fclose(f);
+
+ return (ps);
+}
+
+/**
+ * Called for each printer found in /etc/printcap.
+ */
+static void
+handle_printer(struct printer *pp)
+{
+ struct device_entry *dev_entry;
+ struct printer_entry *printer_entry;
+ char dev_only[128];
+ struct stat sb;
+
+ if (pp->remote_host != NULL) {
+ HRDBG("skipped %s -- remote", pp->printer);
+ return;
+ }
+
+ if (strncmp(pp->lp, _PATH_DEV, strlen(_PATH_DEV)) != 0) {
+ HRDBG("skipped %s [device %s] -- remote", pp->printer, pp->lp);
+ return;
+ }
+
+ memset(dev_only, '\0', sizeof(dev_only));
+ snprintf(dev_only, sizeof(dev_only), "%s", pp->lp + strlen(_PATH_DEV));
+
+ HRDBG("printer %s has device %s", pp->printer, dev_only);
+
+ if (stat(pp->lp, &sb) < 0) {
+ if (errno == ENOENT) {
+ HRDBG("skipped %s -- device %s missing",
+ pp->printer, pp->lp);
+ return;
+ }
+ }
+
+ if ((dev_entry = device_find_by_name(dev_only)) == NULL) {
+ HRDBG("%s not in hrDeviceTable", pp->lp);
+ return;
+ }
+ HRDBG("%s found in hrDeviceTable", pp->lp);
+ dev_entry->type = &OIDX_hrDevicePrinter_c;
+
+ dev_entry->flags |= HR_DEVICE_IMMUTABLE;
+
+ /* Then check hrPrinterTable for this device */
+ if ((printer_entry = printer_find_by_index(dev_entry->index)) == NULL &&
+ (printer_entry = printer_entry_create(dev_entry)) == NULL)
+ return;
+
+ printer_entry->flags |= HR_PRINTER_FOUND;
+ printer_entry->status = get_printer_status(pp);
+ memset(printer_entry->detectedErrorState, 0,
+ sizeof(printer_entry->detectedErrorState));
+}
+
+static void
+hrPrinter_get_OS_entries(void)
+{
+ int status, more;
+ struct printer myprinter, *pp = &myprinter;
+
+ init_printer(pp);
+ HRDBG("---->Getting printers .....");
+ more = firstprinter(pp, &status);
+ if (status)
+ goto errloop;
+
+ while (more) {
+ do {
+ HRDBG("---->Got printer %s", pp->printer);
+
+ handle_printer(pp);
+ more = nextprinter(pp, &status);
+errloop:
+ if (status)
+ syslog(LOG_WARNING,
+ "hrPrinterTable: printcap entry for %s "
+ "has errors, skipping",
+ pp->printer ? pp->printer : "<noname?>");
+ } while (more && status);
+ }
+
+ lastprinter();
+ printer_tick = this_tick;
+}
+
+/**
+ * Init the things for hrPrinterTable
+ */
+void
+init_printer_tbl(void)
+{
+
+ hrPrinter_get_OS_entries();
+}
+
+/**
+ * Finalization routine for hrPrinterTable
+ * It destroys the lists and frees any allocated heap memory
+ */
+void
+fini_printer_tbl(void)
+{
+ struct printer_entry *n1;
+
+ while ((n1 = TAILQ_FIRST(&printer_tbl)) != NULL) {
+ TAILQ_REMOVE(&printer_tbl, n1, link);
+ free(n1);
+ }
+}
+
+/**
+ * Refresh the printer table if needed.
+ */
+void
+refresh_printer_tbl(void)
+{
+ struct printer_entry *entry;
+ struct printer_entry *entry_tmp;
+
+ if (this_tick <= printer_tick) {
+ HRDBG("no refresh needed");
+ return;
+ }
+
+ /* mark each entry as missing */
+ TAILQ_FOREACH(entry, &printer_tbl, link)
+ entry->flags &= ~HR_PRINTER_FOUND;
+
+ hrPrinter_get_OS_entries();
+
+ /*
+ * Purge items that disappeared
+ */
+ entry = TAILQ_FIRST(&printer_tbl);
+ while (entry != NULL) {
+ entry_tmp = TAILQ_NEXT(entry, link);
+ if (!(entry->flags & HR_PRINTER_FOUND))
+ printer_entry_delete(entry);
+ entry = entry_tmp;
+ }
+
+ printer_tick = this_tick;
+
+ HRDBG("refresh DONE ");
+}
+
+int
+op_hrPrinterTable(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op curr_op)
+{
+ struct printer_entry *entry;
+
+ refresh_printer_tbl();
+
+ switch (curr_op) {
+
+ case SNMP_OP_GETNEXT:
+ if ((entry = NEXT_OBJECT_INT(&printer_tbl, &value->var,
+ sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ value->var.len = sub + 1;
+ value->var.subs[sub] = entry->index;
+ goto get;
+
+ case SNMP_OP_GET:
+ if ((entry = FIND_OBJECT_INT(&printer_tbl, &value->var,
+ sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_SET:
+ if ((entry = FIND_OBJECT_INT(&printer_tbl, &value->var,
+ sub)) == NULL)
+ return (SNMP_ERR_NO_CREATION);
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ abort();
+ }
+ abort();
+
+ get:
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_hrPrinterStatus:
+ value->v.integer = entry->status;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrPrinterDetectedErrorState:
+ return (string_get(value, entry->detectedErrorState,
+ sizeof(entry->detectedErrorState)));
+ }
+ abort();
+}
diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_processor_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_processor_tbl.c
new file mode 100644
index 0000000..33f7b2d
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_processor_tbl.c
@@ -0,0 +1,507 @@
+/*-
+ * Copyright (c) 2005-2006 The FreeBSD Project
+ * All rights reserved.
+ *
+ * Author: Victor Cruceru <soc-victor@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+/*
+ * Host Resources MIB for SNMPd. Implementation for hrProcessorTable
+ */
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+
+#include <assert.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "hostres_snmp.h"
+#include "hostres_oid.h"
+#include "hostres_tree.h"
+
+/*
+ * This structure is used to hold a SNMP table entry
+ * for HOST-RESOURCES-MIB's hrProcessorTable.
+ * Note that index is external being allocated & maintained
+ * by the hrDeviceTable code..
+ */
+struct processor_entry {
+ int32_t index;
+ const struct asn_oid *frwId;
+ int32_t load;
+ TAILQ_ENTRY(processor_entry) link;
+ u_char cpu_no; /* which cpu, counted from 0 */
+ pid_t idle_pid; /* PID of idle process for this CPU */
+
+ /* the samples from the last minute, as required by MIB */
+ double samples[MAX_CPU_SAMPLES];
+
+ /* current sample to fill in next time, must be < MAX_CPU_SAMPLES */
+ uint32_t cur_sample_idx;
+
+ /* number of useful samples */
+ uint32_t sample_cnt;
+};
+TAILQ_HEAD(processor_tbl, processor_entry);
+
+/* the head of the list with hrDeviceTable's entries */
+static struct processor_tbl processor_tbl =
+ TAILQ_HEAD_INITIALIZER(processor_tbl);
+
+/* number of processors in dev tbl */
+static int32_t detected_processor_count;
+
+/* sysctlbyname(hw.ncpu) */
+static int hw_ncpu;
+
+/* sysctlbyname(kern.{ccpu,fscale}) */
+static fixpt_t ccpu;
+static int fscale;
+
+/* tick of PDU where we have refreshed the processor table last */
+static uint64_t proctbl_tick;
+
+/* periodic timer used to get cpu load stats */
+static void *cpus_load_timer;
+
+/*
+ * Average the samples. The entire algorithm seems to be wrong XXX.
+ */
+static int
+get_avg_load(struct processor_entry *e)
+{
+ u_int i;
+ double sum = 0.0;
+
+ assert(e != NULL);
+
+ if (e->sample_cnt == 0)
+ return (0);
+
+ for (i = 0; i < e->sample_cnt; i++)
+ sum += e->samples[i];
+
+ return ((int)floor((double)sum/(double)e->sample_cnt));
+}
+
+/*
+ * Stolen from /usr/src/bin/ps/print.c. The idle process should never
+ * be swapped out :-)
+ */
+static double
+processor_getpcpu(struct kinfo_proc *ki_p)
+{
+
+ if (ccpu == 0 || fscale == 0)
+ return (0.0);
+
+#define fxtofl(fixpt) ((double)(fixpt) / fscale)
+ return (100.0 * fxtofl(ki_p->ki_pctcpu) /
+ (1.0 - exp(ki_p->ki_swtime * log(fxtofl(ccpu)))));
+}
+
+/**
+ * Save a new sample
+ */
+static void
+save_sample(struct processor_entry *e, struct kinfo_proc *kp)
+{
+
+ e->samples[e->cur_sample_idx] = 100.0 - processor_getpcpu(kp);
+ e->load = get_avg_load(e);
+ e->cur_sample_idx = (e->cur_sample_idx + 1) % MAX_CPU_SAMPLES;
+
+ if (++e->sample_cnt > MAX_CPU_SAMPLES)
+ e->sample_cnt = MAX_CPU_SAMPLES;
+}
+
+/**
+ * Create a new entry into the processor table.
+ */
+static struct processor_entry *
+proc_create_entry(u_int cpu_no, struct device_map_entry *map)
+{
+ struct device_entry *dev;
+ struct processor_entry *entry;
+ char name[128];
+
+ /*
+ * If there is no map entry create one by creating a device table
+ * entry.
+ */
+ if (map == NULL) {
+ snprintf(name, sizeof(name), "cpu%u", cpu_no);
+ if ((dev = device_entry_create(name, "", "")) == NULL)
+ return (NULL);
+ dev->flags |= HR_DEVICE_IMMUTABLE;
+ STAILQ_FOREACH(map, &device_map, link)
+ if (strcmp(map->name_key, name) == 0)
+ break;
+ if (map == NULL)
+ abort();
+ }
+
+ if ((entry = malloc(sizeof(*entry))) == NULL) {
+ syslog(LOG_ERR, "hrProcessorTable: %s malloc "
+ "failed: %m", __func__);
+ return (NULL);
+ }
+ memset(entry, 0, sizeof(*entry));
+
+ entry->index = map->hrIndex;
+ entry->load = 0;
+ entry->cpu_no = (u_char)cpu_no;
+ entry->idle_pid = 0;
+ entry->frwId = &oid_zeroDotZero; /* unknown id FIXME */
+
+ INSERT_OBJECT_INT(entry, &processor_tbl);
+
+ HRDBG("CPU %d added with SNMP index=%d",
+ entry->cpu_no, entry->index);
+
+ return (entry);
+}
+
+/**
+ * Get the PIDs for the idle processes of the CPUs.
+ */
+static void
+processor_get_pids(void)
+{
+ struct kinfo_proc *plist, *kp;
+ int i;
+ int nproc;
+ int cpu;
+ int nchars;
+ struct processor_entry *entry;
+
+ plist = kvm_getprocs(hr_kd, KERN_PROC_ALL, 0, &nproc);
+ if (plist == NULL || nproc < 0) {
+ syslog(LOG_ERR, "hrProcessor: kvm_getprocs() failed: %m");
+ return;
+ }
+
+ for (i = 0, kp = plist; i < nproc; i++, kp++) {
+ if (!IS_KERNPROC(kp))
+ continue;
+
+ if (strcmp(kp->ki_comm, "idle") == 0) {
+ /* single processor system */
+ cpu = 0;
+ } else if (sscanf(kp->ki_comm, "idle: cpu%d%n", &cpu, &nchars)
+ == 1 && (u_int)nchars == strlen(kp->ki_comm)) {
+ /* MP system */
+ } else
+ /* not an idle process */
+ continue;
+
+ HRDBG("'%s' proc with pid %d is on CPU #%d (last on #%d)",
+ kp->ki_comm, kp->ki_pid, kp->ki_oncpu, kp->ki_lastcpu);
+
+ TAILQ_FOREACH(entry, &processor_tbl, link)
+ if (entry->cpu_no == kp->ki_lastcpu)
+ break;
+
+ if (entry == NULL) {
+ /* create entry on non-ACPI systems */
+ if ((entry = proc_create_entry(cpu, NULL)) == NULL)
+ continue;
+
+ detected_processor_count++;
+ }
+
+ entry->idle_pid = kp->ki_pid;
+ HRDBG("CPU no. %d with SNMP index=%d has idle PID %d",
+ entry->cpu_no, entry->index, entry->idle_pid);
+
+ save_sample(entry, kp);
+ }
+}
+
+/**
+ * Scan the device map table for CPUs and create an entry into the
+ * processor table for each CPU. Then fetch the idle PIDs for all CPUs.
+ */
+static void
+create_proc_table(void)
+{
+ struct device_map_entry *map;
+ struct processor_entry *entry;
+ int cpu_no;
+
+ detected_processor_count = 0;
+
+ /*
+ * Because hrProcessorTable depends on hrDeviceTable,
+ * the device detection must be performed at this point.
+ * If not, no entries will be present in the hrProcessor Table.
+ *
+ * For non-ACPI system the processors are not in the device table,
+ * therefor insert them when getting the idle pids. XXX
+ */
+ STAILQ_FOREACH(map, &device_map, link)
+ if (strncmp(map->name_key, "cpu", strlen("cpu")) == 0 &&
+ strstr(map->location_key, ".CPU") != NULL) {
+ if (sscanf(map->name_key,"cpu%d", &cpu_no) != 1) {
+ syslog(LOG_ERR, "hrProcessorTable: Failed to "
+ "get cpu no. from device named '%s'",
+ map->name_key);
+ continue;
+ }
+
+ if ((entry = proc_create_entry(cpu_no, map)) == NULL)
+ continue;
+
+ detected_processor_count++;
+ }
+
+ HRDBG("%d CPUs detected", detected_processor_count);
+
+ processor_get_pids();
+}
+
+/**
+ * Free the processor table
+ */
+static void
+free_proc_table(void)
+{
+ struct processor_entry *n1;
+
+ while ((n1 = TAILQ_FIRST(&processor_tbl)) != NULL) {
+ TAILQ_REMOVE(&processor_tbl, n1, link);
+ free(n1);
+ detected_processor_count--;
+ }
+
+ assert(detected_processor_count == 0);
+ detected_processor_count = 0;
+}
+
+/**
+ * Init the things for hrProcessorTable.
+ * Scan the device table for processor entries.
+ */
+void
+init_processor_tbl(void)
+{
+ size_t len;
+
+ /* get various parameters from the kernel */
+ len = sizeof(ccpu);
+ if (sysctlbyname("kern.ccpu", &ccpu, &len, NULL, 0) == -1) {
+ syslog(LOG_ERR, "hrProcessorTable: sysctl(kern.ccpu) failed");
+ ccpu = 0;
+ }
+
+ len = sizeof(fscale);
+ if (sysctlbyname("kern.fscale", &fscale, &len, NULL, 0) == -1) {
+ syslog(LOG_ERR, "hrProcessorTable: sysctl(kern.fscale) failed");
+ fscale = 0;
+ }
+
+ /* create the initial processor table */
+ create_proc_table();
+}
+
+/**
+ * Finalization routine for hrProcessorTable.
+ * It destroys the lists and frees any allocated heap memory.
+ */
+void
+fini_processor_tbl(void)
+{
+
+ if (cpus_load_timer != NULL) {
+ timer_stop(cpus_load_timer);
+ cpus_load_timer = NULL;
+ }
+
+ free_proc_table();
+}
+
+/**
+ * Make sure that the number of processors announced by the kernel hw.ncpu
+ * is equal to the number of processors we have found in the device table.
+ * If they differ rescan the device table.
+ */
+static void
+processor_refill_tbl(void)
+{
+
+ HRDBG("hw_ncpu=%d detected_processor_count=%d", hw_ncpu,
+ detected_processor_count);
+
+ if (hw_ncpu <= 0) {
+ size_t size = sizeof(hw_ncpu);
+
+ if (sysctlbyname("hw.ncpu", &hw_ncpu, &size, NULL, 0) == -1 ||
+ size != sizeof(hw_ncpu)) {
+ syslog(LOG_ERR, "hrProcessorTable: "
+ "sysctl(hw.ncpu) failed: %m");
+ hw_ncpu = 0;
+ return;
+ }
+ }
+
+ if (hw_ncpu != detected_processor_count) {
+ free_proc_table();
+ create_proc_table();
+ }
+}
+
+/**
+ * Refresh all values in the processor table. We call this once for
+ * every PDU that accesses the table.
+ */
+static void
+refresh_processor_tbl(void)
+{
+ struct processor_entry *entry;
+ int need_pids;
+ struct kinfo_proc *plist;
+ int nproc;
+
+ processor_refill_tbl();
+
+ need_pids = 0;
+ TAILQ_FOREACH(entry, &processor_tbl, link) {
+ if (entry->idle_pid <= 0) {
+ need_pids = 1;
+ continue;
+ }
+
+ assert(hr_kd != NULL);
+
+ plist = kvm_getprocs(hr_kd, KERN_PROC_PID,
+ entry->idle_pid, &nproc);
+ if (plist == NULL || nproc != 1) {
+ syslog(LOG_ERR, "%s: missing item with "
+ "PID = %d for CPU #%d\n ", __func__,
+ entry->idle_pid, entry->cpu_no);
+ need_pids = 1;
+ continue;
+ }
+ save_sample(entry, plist);
+ }
+
+ if (need_pids == 1)
+ processor_get_pids();
+
+ proctbl_tick = this_tick;
+}
+
+/**
+ * This function is called MAX_CPU_SAMPLES times per minute to collect the
+ * CPU load.
+ */
+static void
+get_cpus_samples(void *arg __unused)
+{
+
+ HRDBG("[%llu] ENTER", (unsigned long long)get_ticks());
+ refresh_processor_tbl();
+ HRDBG("[%llu] EXIT", (unsigned long long)get_ticks());
+}
+
+/**
+ * Called to start this table. We need to start the periodic idle
+ * time collection.
+ */
+void
+start_processor_tbl(struct lmodule *mod)
+{
+
+ /*
+ * Start the cpu stats collector
+ * The semantics of timer_start parameters is in "SNMP ticks";
+ * we have 100 "SNMP ticks" per second, thus we are trying below
+ * to get MAX_CPU_SAMPLES per minute
+ */
+ cpus_load_timer = timer_start_repeat(100, 100 * 60 / MAX_CPU_SAMPLES,
+ get_cpus_samples, NULL, mod);
+}
+
+/**
+ * Access routine for the processor table.
+ */
+int
+op_hrProcessorTable(struct snmp_context *ctx __unused,
+ struct snmp_value *value, u_int sub, u_int iidx __unused,
+ enum snmp_op curr_op)
+{
+ struct processor_entry *entry;
+
+ if (this_tick != proctbl_tick)
+ refresh_processor_tbl();
+
+ switch (curr_op) {
+
+ case SNMP_OP_GETNEXT:
+ if ((entry = NEXT_OBJECT_INT(&processor_tbl,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ value->var.len = sub + 1;
+ value->var.subs[sub] = entry->index;
+ goto get;
+
+ case SNMP_OP_GET:
+ if ((entry = FIND_OBJECT_INT(&processor_tbl,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_SET:
+ if ((entry = FIND_OBJECT_INT(&processor_tbl,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NO_CREATION);
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ abort();
+ }
+ abort();
+
+ get:
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_hrProcessorFrwID:
+ assert(entry->frwId != NULL);
+ value->v.oid = *entry->frwId;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrProcessorLoad:
+ value->v.integer = entry->load;
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+}
diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_scalars.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_scalars.c
new file mode 100644
index 0000000..945a9bd
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_scalars.c
@@ -0,0 +1,513 @@
+/*-
+ * Copyright (c) 2005-2006 The FreeBSD Project
+ * All rights reserved.
+ *
+ * Author: Victor Cruceru <soc-victor@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+/*
+ * Host Resources MIB scalars implementation for SNMPd.
+ */
+
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+#include <pwd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <syslog.h>
+#include <utmp.h>
+
+#include "hostres_snmp.h"
+#include "hostres_oid.h"
+#include "hostres_tree.h"
+
+/* file pointer to keep an open instance of utmp */
+static FILE *utmp_fp;
+
+/* boot timestamp in centi-seconds */
+static uint64_t kernel_boot;
+
+/* physical memory size in Kb */
+static uint64_t phys_mem_size;
+
+/* boot line (malloced) */
+static u_char *boot_line;
+
+/* maximum number of processes */
+static uint32_t max_proc;
+
+/**
+ * Free all static data
+ */
+void
+fini_scalars(void)
+{
+
+ free(boot_line);
+
+ if (utmp_fp != NULL)
+ (void)fclose(utmp_fp);
+}
+
+/**
+ * Get system uptime in hundredths of seconds since the epoch
+ * Returns 0 in case of an error
+ */
+static int
+OS_getSystemUptime(uint32_t *ut)
+{
+ struct timeval right_now;
+ uint64_t now;
+
+ if (kernel_boot == 0) {
+ /* first time, do the sysctl */
+ struct timeval kernel_boot_timestamp;
+ int mib[2] = { CTL_KERN, KERN_BOOTTIME };
+ size_t len = sizeof(kernel_boot_timestamp);
+
+ if (sysctl(mib, 2, &kernel_boot_timestamp,
+ &len, NULL, 0) == -1) {
+ syslog(LOG_ERR, "sysctl KERN_BOOTTIME failed: %m");
+ return (SNMP_ERR_GENERR);
+ }
+
+ HRDBG("boot timestamp from kernel: {%lld, %ld}",
+ (long long)kernel_boot_timestamp.tv_sec,
+ (long)kernel_boot_timestamp.tv_usec);
+
+ kernel_boot = ((uint64_t)kernel_boot_timestamp.tv_sec * 100) +
+ (kernel_boot_timestamp.tv_usec / 10000);
+ }
+
+ if (gettimeofday(&right_now, NULL) < 0) {
+ syslog(LOG_ERR, "gettimeofday failed: %m");
+ return (SNMP_ERR_GENERR);
+ }
+ now = ((uint64_t)right_now.tv_sec * 100) + (right_now.tv_usec / 10000);
+
+ if (now - kernel_boot > UINT32_MAX)
+ *ut = UINT32_MAX;
+ else
+ *ut = now - kernel_boot;
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/**
+ * Get system local date and time in a foramt suitable for DateAndTime TC:
+ * field octets contents range
+ * ----- ------ -------- -----
+ * 1 1-2 year* 0..65536
+ * 2 3 month 1..12
+ * 3 4 day 1..31
+ * 4 5 hour 0..23
+ * 5 6 minutes 0..59
+ * 6 7 seconds 0..60
+ * (use 60 for leap-second)
+ * 7 8 deci-seconds 0..9
+ * 8 9 direction from UTC '+' / '-'
+ * 9 10 hours from UTC* 0..13
+ * 10 11 minutes from UTC 0..59
+ *
+ * * Notes:
+ * - the value of year is in network-byte order
+ * - daylight saving time in New Zealand is +13
+ *
+ * For example, Tuesday May 26, 1992 at 1:30:15 PM EDT would be
+ * displayed as:
+ *
+ * 1992-5-26,13:30:15.0,-4:0
+ *
+ * Returns -1 in case of an error or the length of the string (8 or 11)
+ * Actually returns always 11 on freebsd
+ */
+static int
+OS_getSystemDate(struct snmp_value *value)
+{
+ u_char s_date_time[11];
+ struct tm tloc_tm;
+ time_t tloc_time_t;
+ struct timeval right_now;
+ int string_len;
+
+ if (gettimeofday(&right_now, NULL) < 0) {
+ syslog(LOG_ERR, "gettimeofday failed: %m");
+ return (SNMP_ERR_GENERR);
+ }
+
+ tloc_time_t = right_now.tv_sec;
+
+ if (localtime_r(&tloc_time_t, &tloc_tm) == NULL) {
+ syslog(LOG_ERR, "localtime_r() failed: %m ");
+ return (SNMP_ERR_GENERR);
+ }
+
+ string_len = make_date_time(s_date_time, &tloc_tm,
+ right_now.tv_usec / 100000);
+
+ return (string_get(value, s_date_time, string_len));
+}
+
+/**
+ * Get kernel boot path. For FreeBSD it seems that no arguments are
+ * present. Returns NULL if an error occured. The returned data is a
+ * pointer to a global strorage.
+ */
+int
+OS_getSystemInitialLoadParameters(u_char **params)
+{
+
+ if (boot_line == NULL) {
+ int mib[2] = { CTL_KERN, KERN_BOOTFILE };
+ char *buf;
+ size_t buf_len = 0;
+
+ /* get the needed buffer len */
+ if (sysctl(mib, 2, NULL, &buf_len, NULL, 0) != 0) {
+ syslog(LOG_ERR,
+ "sysctl({CTL_KERN,KERN_BOOTFILE}) failed: %m");
+ return (SNMP_ERR_GENERR);
+ }
+
+ if ((buf = malloc(buf_len)) == NULL) {
+ syslog(LOG_ERR, "malloc failed");
+ return (SNMP_ERR_GENERR);
+ }
+ if (sysctl(mib, 2, buf, &buf_len, NULL, 0)) {
+ syslog(LOG_ERR,
+ "sysctl({CTL_KERN,KERN_BOOTFILE}) failed: %m");
+ free(buf);
+ return (SNMP_ERR_GENERR);
+ }
+
+ boot_line = buf;
+ HRDBG("kernel boot file: %s", boot_line);
+ }
+
+ *params = boot_line;
+ return (SNMP_ERR_NOERROR);
+}
+
+/**
+ * Get number of current users which are logged in
+ */
+static int
+OS_getSystemNumUsers(uint32_t *nu)
+{
+ struct utmp utmp;
+ static int first_time = 1;
+
+ if (utmp_fp == NULL) {
+ if (!first_time)
+ return (SNMP_ERR_GENERR);
+ first_time = 0;
+ if ((utmp_fp = fopen(_PATH_UTMP, "r")) == NULL) {
+ syslog(LOG_ERR, "fopen(%s) failed: %m", _PATH_UTMP);
+ return (SNMP_ERR_GENERR);
+ }
+ }
+
+ /* start with the begining of the utmp file */
+ (void)rewind(utmp_fp);
+
+ *nu = 0;
+ while (fread(&utmp, sizeof(utmp), 1, utmp_fp) == 1) {
+ if (utmp.ut_name[0] != '\0' && utmp.ut_line[0] != '\0') {
+ if (getpwnam(utmp.ut_name) == NULL)
+ continue;
+ (*nu)++;
+ }
+ }
+
+ return (SNMP_ERR_NOERROR);
+}
+
+/**
+ * Get number of current processes existing into the system
+ */
+static int
+OS_getSystemProcesses(uint32_t *proc_count)
+{
+ int pc;
+
+ if (hr_kd == NULL)
+ return (SNMP_ERR_GENERR);
+
+ if (kvm_getprocs(hr_kd, KERN_PROC_ALL, 0, &pc) == NULL) {
+ syslog(LOG_ERR, "kvm_getprocs failed: %m");
+ return (SNMP_ERR_GENERR);
+ }
+
+ *proc_count = pc;
+ return (SNMP_ERR_NOERROR);
+}
+
+/**
+ * Get maximum number of processes allowed on this system
+ */
+static int
+OS_getSystemMaxProcesses(uint32_t *mproc)
+{
+
+ if (max_proc == 0) {
+ int mib[2] = { CTL_KERN, KERN_MAXPROC };
+ int mp;
+ size_t len = sizeof(mp);
+
+ if (sysctl(mib, 2, &mp, &len, NULL, 0) == -1) {
+ syslog(LOG_ERR, "sysctl KERN_MAXPROC failed: %m");
+ return (SNMP_ERR_GENERR);
+ }
+ max_proc = mp;
+ }
+
+ *mproc = max_proc;
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Get the physical memeory size in Kbytes.
+ * Returns SNMP error code.
+ */
+static int
+OS_getMemorySize(uint32_t *ms)
+{
+
+ if (phys_mem_size == 0) {
+ int mib[2] = { CTL_HW, HW_PHYSMEM };
+ u_long physmem;
+ size_t len = sizeof(physmem);
+
+ if (sysctl(mib, 2, &physmem, &len, NULL, 0) == -1) {
+ syslog(LOG_ERR,
+ "sysctl({ CTL_HW, HW_PHYSMEM }) failed: %m");
+ return (SNMP_ERR_GENERR);
+ }
+
+ phys_mem_size = physmem / 1024;
+ }
+
+ if (phys_mem_size > UINT32_MAX)
+ *ms = UINT32_MAX;
+ else
+ *ms = phys_mem_size;
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Try to use the s_date_time parameter as a DateAndTime TC to fill in
+ * the second parameter.
+ * Returns 0 on succes and -1 for an error.
+ * Bug: time zone info is not used
+ */
+static struct timeval *
+OS_checkSystemDateInput(const u_char *str, u_int len)
+{
+ struct tm tm_to_set;
+ time_t t;
+ struct timeval *tv;
+
+ if (len != 8 && len != 11)
+ return (NULL);
+
+ if (str[2] == 0 || str[2] > 12 ||
+ str[3] == 0 || str[3] > 31 ||
+ str[4] > 23 || str[5] > 59 || str[6] > 60 || str[7] > 9)
+ return (NULL);
+
+ tm_to_set.tm_year = ((str[0] << 8) + str[1]) - 1900;
+ tm_to_set.tm_mon = str[2] - 1;
+ tm_to_set.tm_mday = str[3];
+ tm_to_set.tm_hour = str[4];
+ tm_to_set.tm_min = str[5];
+ tm_to_set.tm_sec = str[6];
+ tm_to_set.tm_isdst = 0;
+
+ /* now make UTC from it */
+ if ((t = timegm(&tm_to_set)) == (time_t)-1)
+ return (NULL);
+
+ /* now apply timezone if specified */
+ if (len == 11) {
+ if (str[9] > 13 || str[10] > 59)
+ return (NULL);
+ if (str[8] == '+')
+ t += 3600 * str[9] + 60 * str[10];
+ else
+ t -= 3600 * str[9] + 60 * str[10];
+ }
+
+ if ((tv = malloc(sizeof(*tv))) == NULL)
+ return (NULL);
+
+ tv->tv_sec = t;
+ tv->tv_usec = (int32_t)str[7] * 100000;
+
+ return (tv);
+}
+
+/*
+ * Set system date and time. Timezone is not changed
+ */
+static int
+OS_setSystemDate(const struct timeval *timeval_to_set)
+{
+ if (settimeofday(timeval_to_set, NULL) == -1) {
+ syslog(LOG_ERR, "settimeofday failed: %m");
+ return (SNMP_ERR_GENERR);
+ }
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * prototype of this function was genrated by gensnmptree tool in header file
+ * hostres_tree.h
+ * Returns SNMP_ERR_NOERROR on success
+ */
+int
+op_hrSystem(struct snmp_context *ctx, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op curr_op)
+{
+ int err;
+ u_char *str;
+
+ switch (curr_op) {
+
+ case SNMP_OP_GET:
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_hrSystemUptime:
+ return (OS_getSystemUptime(&value->v.uint32));
+
+ case LEAF_hrSystemDate:
+ return (OS_getSystemDate(value));
+
+ case LEAF_hrSystemInitialLoadDevice:
+ value->v.uint32 = 0; /* FIXME */
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrSystemInitialLoadParameters:
+ if ((err = OS_getSystemInitialLoadParameters(&str)) !=
+ SNMP_ERR_NOERROR)
+ return (err);
+ return (string_get(value, str, -1));
+
+ case LEAF_hrSystemNumUsers:
+ return (OS_getSystemNumUsers(&value->v.uint32));
+
+ case LEAF_hrSystemProcesses:
+ return (OS_getSystemProcesses(&value->v.uint32));
+
+ case LEAF_hrSystemMaxProcesses:
+ return (OS_getSystemMaxProcesses(&value->v.uint32));
+ }
+ abort();
+
+ case SNMP_OP_SET:
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_hrSystemDate:
+ if ((ctx->scratch->ptr1 =
+ OS_checkSystemDateInput(value->v.octetstring.octets,
+ value->v.octetstring.len)) == NULL)
+ return (SNMP_ERR_WRONG_VALUE);
+
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrSystemInitialLoadDevice:
+ case LEAF_hrSystemInitialLoadParameters:
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ }
+ abort();
+
+ case SNMP_OP_ROLLBACK:
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_hrSystemDate:
+ free(ctx->scratch->ptr1);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrSystemInitialLoadDevice:
+ case LEAF_hrSystemInitialLoadParameters:
+ abort();
+ }
+ abort();
+
+ case SNMP_OP_COMMIT:
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_hrSystemDate:
+ (void)OS_setSystemDate(ctx->scratch->ptr1);
+ free(ctx->scratch->ptr1);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrSystemInitialLoadDevice:
+ case LEAF_hrSystemInitialLoadParameters:
+ abort();
+ }
+ abort();
+
+ case SNMP_OP_GETNEXT:
+ abort();
+ }
+ abort();
+}
+
+/*
+ * prototype of this function was genrated by gensnmptree tool
+ * in the header file hostres_tree.h
+ * Returns SNMP_ERR_NOERROR on success
+ */
+int
+op_hrStorage(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op curr_op)
+{
+
+ /* only GET is possible */
+ switch (curr_op) {
+
+ case SNMP_OP_GET:
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_hrMemorySize:
+ return (OS_getMemorySize(&value->v.uint32));
+ }
+ abort();
+
+ case SNMP_OP_SET:
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ case SNMP_OP_GETNEXT:
+ abort();
+ }
+ abort();
+}
diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.c
new file mode 100644
index 0000000..47112da
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.c
@@ -0,0 +1,209 @@
+/*-
+ * Copyright (c) 2005-2006 The FreeBSD Project
+ * All rights reserved.
+ *
+ * Author: Victor Cruceru <soc-victor@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This C file contains code developed by Poul-Henning Kamp under the
+ * following license:
+ *
+ * FreeBSD: src/sbin/mdconfig/mdconfig.c,v 1.33.2.1 2004/09/14 03:32:21 jmg Exp
+ * ----------------------------------------------------------------------------
+ * "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$
+ */
+
+/*
+ * Host Resources MIB implementation for bsnmpd.
+ */
+
+#include <paths.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <utmp.h>
+
+#include "hostres_snmp.h"
+#include "hostres_oid.h"
+#include "hostres_tree.h"
+
+/* Internal id got after we'll register this module with the agent */
+static u_int host_registration_id = 0;
+
+/* This our hostres module */
+static struct lmodule *hostres_module;
+
+/* See the generated file hostres_oid.h */
+static const struct asn_oid oid_host = OIDX_host;
+
+/* descriptor to access kernel memory */
+kvm_t *hr_kd;
+
+/*
+ * HOST RESOURCES mib module finalization hook.
+ * Returns 0 on success, < 0 on error
+ */
+static int
+hostres_fini(void)
+{
+
+ if (hr_kd != NULL)
+ (void)kvm_close(hr_kd);
+
+ fini_storage_tbl();
+ fini_fs_tbl();
+ fini_processor_tbl();
+ fini_disk_storage_tbl();
+ fini_device_tbl();
+ fini_partition_tbl();
+ fini_network_tbl();
+ fini_printer_tbl();
+
+ fini_swrun_tbl();
+ fini_swins_tbl();
+
+ fini_scalars();
+
+ if (host_registration_id > 0)
+ or_unregister(host_registration_id);
+
+ HRDBG("done.");
+ return (0);
+}
+
+/*
+ * HOST RESOURCES mib module initialization hook.
+ * Returns 0 on success, < 0 on error
+ */
+static int
+hostres_init(struct lmodule *mod, int argc __unused, char *argv[] __unused)
+{
+
+ hostres_module = mod;
+
+ /*
+ * NOTE: order of these calls is important here!
+ */
+ if ((hr_kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY,
+ "kvm_open")) == NULL) {
+ syslog(LOG_ERR, "kvm_open failed: %m ");
+ return (-1);
+ }
+
+ /*
+ * The order is relevant here, because some table depend on each other.
+ */
+ init_device_tbl();
+
+ /* populates partition table too */
+ if (init_disk_storage_tbl()) {
+ hostres_fini();
+ return (-1);
+ }
+ init_processor_tbl();
+ init_printer_tbl();
+
+ /*
+ * populate storage and FS tables. Must be done after device
+ * initialisation because the FS refresh code calls into the
+ * partition refresh code.
+ */
+ init_storage_tbl();
+
+
+ /* also the hrSWRunPerfTable's support is initialized here */
+ init_swrun_tbl();
+ init_swins_tbl();
+
+ HRDBG("done.");
+
+ return (0);
+}
+
+/*
+ * HOST RESOURCES mib module start operation
+ * returns nothing
+ */
+static void
+hostres_start(void)
+{
+
+ host_registration_id = or_register(&oid_host,
+ "The MIB module for Host Resource MIB (RFC 2790).",
+ hostres_module);
+
+ start_device_tbl(hostres_module);
+ start_processor_tbl(hostres_module);
+ start_network_tbl();
+
+ HRDBG("done.");
+}
+
+/* this identifies the HOST RESOURCES mib module */
+const struct snmp_module config = {
+ "This module implements the host resource mib (rfc 2790)",
+ hostres_init,
+ hostres_fini,
+ NULL, /* idle function, do not use it */
+ NULL,
+ NULL,
+ hostres_start,
+ NULL, /* proxy a PDU */
+ hostres_ctree, /* see the generated hostres_tree.h */
+ hostres_CTREE_SIZE, /* see the generated hostres_tree.h */
+ NULL
+};
+
+/**
+ * Make an SNMP DateAndTime from a struct tm. This should be in the library.
+ */
+int
+make_date_time(u_char *str, const struct tm *tm, u_int decisecs)
+{
+
+ str[0] = (u_char)((tm->tm_year + 1900) >> 8);
+ str[1] = (u_char)(tm->tm_year + 1900);
+ str[2] = tm->tm_mon + 1;
+ str[3] = tm->tm_mday;
+ str[4] = tm->tm_hour;
+ str[5] = tm->tm_min;
+ str[6] = tm->tm_sec;
+ str[7] = decisecs;
+ if (tm->tm_gmtoff < 0)
+ str[8] = '-';
+ else
+ str[8] = '+';
+
+ str[9] = (u_char)(abs(tm->tm_gmtoff) / 3600);
+ str[10] = (u_char)((abs(tm->tm_gmtoff) % 3600) / 60);
+
+ return (11);
+}
diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.h b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.h
new file mode 100644
index 0000000..567368a
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_snmp.h
@@ -0,0 +1,324 @@
+/*
+ * Copyright (c) 2005-2006 The FreeBSD Project
+ * All rights reserved.
+ *
+ * Author: Victor Cruceru <soc-victor@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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.
+ *
+ * Host Resources MIB for SNMPd.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef HOSTRES_SNMP_H_1132245017
+#define HOSTRES_SNMP_H_1132245017
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <devinfo.h>
+
+#include <bsnmp/asn1.h>
+#include <bsnmp/snmp.h>
+
+#include <bsnmp/snmpmod.h>
+
+/*
+ * Default package directory for hrSWInstalledTable. Can be overridden
+ * via SNMP or configuration file.
+ */
+#define PATH_PKGDIR "/var/db/pkg"
+
+/*
+ * These are the default maximum caching intervals for the various tables
+ * in seconds. They can be overridden from the configuration file.
+ */
+#define HR_STORAGE_TBL_REFRESH 7
+#define HR_FS_TBL_REFRESH 7
+#define HR_DISK_TBL_REFRESH 7
+#define HR_NETWORK_TBL_REFRESH 7
+#define HR_SWINS_TBL_REFRESH 120
+#define HR_SWRUN_TBL_REFRESH 3
+
+struct tm;
+struct statfs;
+
+/* a debug macro */
+#ifndef NDEBUG
+
+#define HRDBG(...) do { \
+ fprintf(stderr, "HRDEBUG: %s: ", __func__); \
+ fprintf(stderr, __VA_ARGS__); \
+ fprintf(stderr, "\n"); \
+ } while (0)
+
+#else
+
+#define HRDBG(...) do { } while (0)
+
+#endif /*NDEBUG*/
+
+/* path to devd(8) output pipe */
+#define PATH_DEVD_PIPE "/var/run/devd.pipe"
+
+#define IS_KERNPROC(kp) (((kp)->ki_flag & P_KTHREAD) == P_KTHREAD)
+
+enum snmpTCTruthValue {
+ SNMP_TRUE = 1,
+ SNMP_FALSE= 2
+};
+
+/* The number of CPU load samples per one minute, per each CPU */
+#define MAX_CPU_SAMPLES 4
+
+
+/*
+ * max len (including '\0'), for device_entry::descr field below,
+ * according to MIB
+ */
+#define DEV_DESCR_MLEN (64 + 1)
+
+/*
+ * max len (including '\0'), for device_entry::name and
+ * device_map_entry::name_key fields below, according to MIB
+ */
+#define DEV_NAME_MLEN (32 + 1)
+
+/*
+ * max len (including '\0'), for device_entry::location and
+ * device_map_entry::location_key fields below, according to MIB
+ */
+#define DEV_LOC_MLEN (128 + 1)
+
+/*
+ * This structure is used to hold a SNMP table entry
+ * for HOST-RESOURCES-MIB's hrDeviceTable
+ */
+struct device_entry {
+ int32_t index;
+ const struct asn_oid *type;
+ u_char *descr;
+ const struct asn_oid *id; /* only oid_zeroDotZero as (*id) value*/
+ int32_t status; /* enum DeviceStatus */
+ uint32_t errors;
+
+#define HR_DEVICE_FOUND 0x001
+ /* not dectected by libdevice, so don't try to refresh it*/
+#define HR_DEVICE_IMMUTABLE 0x002
+
+ /* next 3 are not from the SNMP mib table, only to be used internally */
+ uint32_t flags;
+
+ u_char *name;
+ u_char *location;
+ TAILQ_ENTRY(device_entry) link;
+};
+
+/*
+ * Next structure is used to keep o list of mappings from a specific
+ * name (a_name) to an entry in the hrFSTblEntry;
+ * We are trying to keep the same index for a specific name at least
+ * for the duration of one SNMP agent run.
+ */
+struct device_map_entry {
+ int32_t hrIndex; /* used for hrDeviceTblEntry::index */
+
+ /* map key is the pair (name_key, location_key) */
+ u_char *name_key; /* copy of device name */
+ u_char *location_key;
+
+ /*
+ * Next may be NULL if the respective hrDeviceTblEntry
+ * is (temporally) gone.
+ */
+ struct device_entry *entry_p;
+ STAILQ_ENTRY(device_map_entry) link;
+};
+STAILQ_HEAD(device_map, device_map_entry);
+
+/* descriptor to access kernel memory */
+extern kvm_t *hr_kd;
+
+/* Table used for consistent device table indexing. */
+extern struct device_map device_map;
+
+/* Maximum number of ticks between two updates for hrStorageTable */
+extern uint32_t storage_tbl_refresh;
+
+/* Maximum number of ticks between updated of FS table */
+extern uint32_t fs_tbl_refresh;
+
+/* maximum number of ticks between updates of SWRun and SWRunPerf table */
+extern uint32_t swrun_tbl_refresh;
+
+/* Maximum number of ticks between device table refreshs. */
+extern uint32_t device_tbl_refresh;
+
+/* maximum number of ticks between refreshs */
+extern uint32_t disk_storage_tbl_refresh;
+
+/* maximum number of ticks between updates of network table */
+extern uint32_t swins_tbl_refresh;
+
+/* maximum number of ticks between updates of network table */
+extern uint32_t network_tbl_refresh;
+
+/* package directory */
+extern u_char *pkg_dir;
+
+/* Initialize and populate storage table */
+void init_storage_tbl(void);
+
+/* Finalization routine for hrStorageTable. */
+void fini_storage_tbl(void);
+
+/* Refresh routine for hrStorageTable. */
+void refresh_storage_tbl(int);
+
+/*
+ * Get the type of filesystem referenced in a struct statfs * -
+ * used by FSTbl and StorageTbl functions.
+ */
+const struct asn_oid *fs_get_type(const struct statfs *);
+
+/*
+ * Because hrFSTable depends to hrStorageTable we are
+ * refreshing hrFSTable by refreshing hrStorageTable.
+ * When one entry "of type" fs from hrStorageTable is refreshed
+ * then the corresponding entry from hrFSTable is refreshed
+ * FS_tbl_pre_refresh_v() is called before refeshing fs part of hrStorageTable
+ */
+void fs_tbl_pre_refresh(void);
+void fs_tbl_process_statfs_entry(const struct statfs *, int32_t);
+
+/* Called after refreshing fs part of hrStorageTable */
+void fs_tbl_post_refresh(void);
+
+/* Refresh the FS table if neccessary. */
+void refresh_fs_tbl(void);
+
+/* Finalization routine for hrFSTable. */
+void fini_fs_tbl(void);
+
+/* Init the things for both of hrSWRunTable and hrSWRunPerfTable */
+void init_swrun_tbl(void);
+
+/* Finalization routine for both of hrSWRunTable and hrSWRunPerfTable */
+void fini_swrun_tbl(void);
+
+/* Init and populate hrDeviceTable */
+void init_device_tbl(void);
+
+/* start devd monitoring */
+void start_device_tbl(struct lmodule *);
+
+/* Finalization routine for hrDeviceTable */
+void fini_device_tbl(void);
+
+/* Refresh routine for hrDeviceTable. */
+void refresh_device_tbl(int);
+
+/* Find an item in hrDeviceTbl by its entry->index. */
+struct device_entry *device_find_by_index(int32_t);
+
+/* Find an item in hrDeviceTbl by name. */
+struct device_entry *device_find_by_name(const char *);
+
+/* Create a new entry out of thin air. */
+struct device_entry *device_entry_create(const char *, const char *,
+ const char *);
+
+/* Delete an entry from hrDeviceTbl */
+void device_entry_delete(struct device_entry *entry);
+
+/* Init the things for hrProcessorTable. */
+void init_processor_tbl(void);
+
+/* Finalization routine for hrProcessorTable. */
+void fini_processor_tbl(void);
+
+/* Start the processor table CPU load collector. */
+void start_processor_tbl(struct lmodule *);
+
+/* Init the things for hrDiskStorageTable */
+int init_disk_storage_tbl(void);
+
+/* Finalization routine for hrDiskStorageTable. */
+void fini_disk_storage_tbl(void);
+
+/* Refresh routine for hrDiskStorageTable. */
+void refresh_disk_storage_tbl(int);
+
+/* Finalization routine for hrPartitionTable. */
+void fini_partition_tbl(void);
+
+/* Finalization routine for hrNetworkTable. */
+void fini_network_tbl(void);
+
+/* populate network table */
+void start_network_tbl(void);
+
+/* initialize installed software table */
+void init_swins_tbl(void);
+
+/* finalize installed software table */
+void fini_swins_tbl(void);
+
+/* refresh the hrSWInstalledTable if necessary */
+void refresh_swins_tbl(void);
+
+/* Init the things for hrPrinterTable */
+void init_printer_tbl(void);
+
+/* Finalization routine for hrPrinterTable. */
+void fini_printer_tbl(void);
+
+/* Refresh printer table */
+void refresh_printer_tbl(void);
+
+/* get boot command line */
+int OS_getSystemInitialLoadParameters(u_char **);
+
+/* Start refreshing the partition table */
+void partition_tbl_post_refresh(void);
+
+/* Handle refresh for the given disk */
+void partition_tbl_handle_disk(int32_t, const char *);
+
+/* Finish refreshing the partition table. */
+void partition_tbl_pre_refresh(void);
+
+/* Set the FS index in a partition table entry */
+void handle_partition_fs_index(const char *, int32_t);
+
+/* Make an SNMP DateAndTime from a struct tm. */
+int make_date_time(u_char *, const struct tm *, u_int);
+
+/* Free all static data */
+void fini_scalars(void);
+
+#endif /* HOSTRES_SNMP_H_1132245017 */
diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_storage_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_storage_tbl.c
new file mode 100644
index 0000000..488d9f5
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_storage_tbl.c
@@ -0,0 +1,668 @@
+/*-
+ * Copyright (c) 2005-2006 The FreeBSD Project
+ * All rights reserved.
+ *
+ * Author: Victor Cruceru <soc-victor@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+/*
+ * Host Resources MIB for SNMPd. Implementation for hrStorageTable
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/vmmeter.h>
+#include <sys/mount.h>
+
+#include <vm/vm_param.h>
+
+#include <assert.h>
+#include <err.h>
+#include <limits.h>
+#include <memstat.h>
+#include <paths.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h> /* for getpagesize() */
+#include <sysexits.h>
+
+#include "hostres_snmp.h"
+#include "hostres_oid.h"
+#include "hostres_tree.h"
+
+/* maximum length for descritpion string according to MIB */
+#define SE_DESC_MLEN (255 + 1)
+
+/*
+ * This structure is used to hold a SNMP table entry
+ * for HOST-RESOURCES-MIB's hrStorageTable
+ */
+struct storage_entry {
+ int32_t index;
+ const struct asn_oid *type;
+ u_char *descr;
+ int32_t allocationUnits;
+ int32_t size;
+ int32_t used;
+ uint32_t allocationFailures;
+#define HR_STORAGE_FOUND 0x001
+ uint32_t flags; /* to be used internally*/
+ TAILQ_ENTRY(storage_entry) link;
+};
+TAILQ_HEAD(storage_tbl, storage_entry);
+
+/*
+ * Next structure is used to keep o list of mappings from a specific name
+ * (a_name) to an entry in the hrStorageTblEntry. We are trying to keep the
+ * same index for a specific name at least for the duration of one SNMP agent
+ * run.
+ */
+struct storage_map_entry {
+ int32_t hrIndex; /* used for storage_entry::index */
+
+ /* map key, also used for storage_entry::descr */
+ u_char *a_name;
+
+ /*
+ * next may be NULL if the respective storage_entry
+ * is (temporally) gone
+ */
+ struct storage_entry *entry;
+ STAILQ_ENTRY(storage_map_entry) link;
+};
+STAILQ_HEAD(storage_map, storage_map_entry);
+
+/* the head of the list with table's entries */
+static struct storage_tbl storage_tbl = TAILQ_HEAD_INITIALIZER(storage_tbl);
+
+/*for consistent table indexing*/
+static struct storage_map storage_map =
+ STAILQ_HEAD_INITIALIZER(storage_map);
+
+/* last (agent) tick when hrStorageTable was updated */
+static uint64_t storage_tick;
+
+/* maximum number of ticks between two refreshs */
+uint32_t storage_tbl_refresh = HR_STORAGE_TBL_REFRESH * 100;
+
+/* for kvm_getswapinfo, malloc'd */
+static struct kvm_swap *swap_devs;
+static size_t swap_devs_len; /* item count for swap_devs */
+
+/* for getfsstat, malloc'd */
+static struct statfs *fs_buf;
+static size_t fs_buf_count; /* item count for fs_buf */
+
+static struct vmtotal mem_stats;
+
+/* next int available for indexing the hrStorageTable */
+static uint32_t next_storage_index = 1;
+
+/* start of list for memory detailed stats */
+static struct memory_type_list *mt_list;
+
+/* Constants */
+static const struct asn_oid OIDX_hrStorageRam_c = OIDX_hrStorageRam;
+static const struct asn_oid OIDX_hrStorageVirtualMemory_c =
+ OIDX_hrStorageVirtualMemory;
+
+/**
+ * Create a new entry into the storage table and, if neccessary, an
+ * entry into the storage map.
+ */
+static struct storage_entry *
+storage_entry_create(const char *name)
+{
+ struct storage_entry *entry;
+ struct storage_map_entry *map;
+ size_t name_len;
+
+ assert(name != NULL);
+ assert(strlen(name) > 0);
+
+ STAILQ_FOREACH(map, &storage_map, link)
+ if (strcmp(map->a_name, name) == 0)
+ break;
+
+ if (map == NULL) {
+ /* new object - get a new index */
+ if (next_storage_index > INT_MAX) {
+ syslog(LOG_ERR,
+ "%s: hrStorageTable index wrap", __func__);
+ errx(EX_SOFTWARE, "hrStorageTable index wrap");
+ }
+
+ if ((map = malloc(sizeof(*map))) == NULL) {
+ syslog(LOG_ERR, "hrStorageTable: %s: %m", __func__ );
+ return (NULL);
+ }
+
+ name_len = strlen(name) + 1;
+ if (name_len > SE_DESC_MLEN)
+ name_len = SE_DESC_MLEN;
+
+ if ((map->a_name = malloc(name_len)) == NULL) {
+ free(map);
+ return (NULL);
+ }
+
+ strlcpy(map->a_name, name, name_len);
+ map->hrIndex = next_storage_index++;
+
+ STAILQ_INSERT_TAIL(&storage_map, map, link);
+
+ HRDBG("%s added into hrStorageMap at index=%d",
+ name, map->hrIndex);
+ } else {
+ HRDBG("%s exists in hrStorageMap index=%d\n",
+ name, map->hrIndex);
+ }
+
+ if ((entry = malloc(sizeof(*entry))) == NULL) {
+ syslog(LOG_WARNING, "%s: %m", __func__);
+ return (NULL);
+ }
+ memset(entry, 0, sizeof(*entry));
+
+ entry->index = map->hrIndex;
+
+ if ((entry->descr = strdup(map->a_name)) == NULL) {
+ free(entry);
+ return (NULL);
+ }
+
+ map->entry = entry;
+
+ INSERT_OBJECT_INT(entry, &storage_tbl);
+
+ return (entry);
+}
+
+/**
+ * Delete an entry from the storage table.
+ */
+static void
+storage_entry_delete(struct storage_entry *entry)
+{
+ struct storage_map_entry *map;
+
+ assert(entry != NULL);
+
+ TAILQ_REMOVE(&storage_tbl, entry, link);
+ STAILQ_FOREACH(map, &storage_map, link)
+ if (map->entry == entry) {
+ map->entry = NULL;
+ break;
+ }
+ free(entry->descr);
+ free(entry);
+}
+
+/**
+ * Find a table entry by its name.
+ */
+static struct storage_entry *
+storage_find_by_name(const char *name)
+{
+ struct storage_entry *entry;
+
+ TAILQ_FOREACH(entry, &storage_tbl, link)
+ if (strcmp(entry->descr, name) == 0)
+ return (entry);
+
+ return (NULL);
+}
+
+/*
+ * VM info.
+ */
+static void
+storage_OS_get_vm(void)
+{
+ int mib[2] = { CTL_VM, VM_TOTAL };
+ size_t len = sizeof(mem_stats);
+ int page_size_bytes;
+ struct storage_entry *entry;
+
+ if (sysctl(mib, 2, &mem_stats, &len, NULL, 0) < 0) {
+ syslog(LOG_ERR,
+ "hrStoragetable: %s: sysctl({CTL_VM, VM_METER}) "
+ "failed: %m", __func__);
+ assert(0);
+ return;
+ }
+
+ page_size_bytes = getpagesize();
+
+ /* Real Memory Metrics */
+ if ((entry = storage_find_by_name("Real Memory Metrics")) == NULL &&
+ (entry = storage_entry_create("Real Memory Metrics")) == NULL)
+ return; /* I'm out of luck now, maybe next time */
+
+ entry->flags |= HR_STORAGE_FOUND;
+ entry->type = &OIDX_hrStorageRam_c;
+ entry->allocationUnits = page_size_bytes;
+ entry->size = mem_stats.t_rm;
+ entry->used = mem_stats.t_arm; /* ACTIVE is not USED - FIXME */
+ entry->allocationFailures = 0;
+
+ /* Shared Real Memory Metrics */
+ if ((entry = storage_find_by_name("Shared Real Memory Metrics")) ==
+ NULL &&
+ (entry = storage_entry_create("Shared Real Memory Metrics")) ==
+ NULL)
+ return;
+
+ entry->flags |= HR_STORAGE_FOUND;
+ entry->type = &OIDX_hrStorageRam_c;
+ entry->allocationUnits = page_size_bytes;
+ entry->size = mem_stats.t_rmshr;
+ /* ACTIVE is not USED - FIXME */
+ entry->used = mem_stats.t_armshr;
+ entry->allocationFailures = 0;
+}
+
+static void
+storage_OS_get_memstat(void)
+{
+ struct memory_type *mt_item;
+ struct storage_entry *entry;
+
+ if (mt_list == NULL) {
+ if ((mt_list = memstat_mtl_alloc()) == NULL)
+ /* again? we have a serious problem */
+ return;
+ }
+
+ if (memstat_sysctl_all(mt_list, 0) < 0) {
+ syslog(LOG_ERR, "memstat_sysctl_all failed: %s",
+ memstat_strerror(memstat_mtl_geterror(mt_list)) );
+ return;
+ }
+
+ if ((mt_item = memstat_mtl_first(mt_list)) == NULL) {
+ /* usually this is not an error, no errno for this failure*/
+ HRDBG("memstat_mtl_first failed");
+ return;
+ }
+
+ do {
+ const char *memstat_name;
+ uint64_t tmp_size;
+ int allocator;
+ char alloc_descr[SE_DESC_MLEN];
+
+ memstat_name = memstat_get_name(mt_item);
+
+ if (memstat_name == NULL || strlen(memstat_name) == 0)
+ continue;
+
+ switch (allocator = memstat_get_allocator(mt_item)) {
+
+ case ALLOCATOR_MALLOC:
+ snprintf(alloc_descr, sizeof(alloc_descr),
+ "MALLOC: %s", memstat_name);
+ break;
+
+ case ALLOCATOR_UMA:
+ snprintf(alloc_descr, sizeof(alloc_descr),
+ "UMA: %s", memstat_name);
+ break;
+
+ default:
+ snprintf(alloc_descr, sizeof(alloc_descr),
+ "UNKNOWN%d: %s", allocator, memstat_name);
+ break;
+ }
+
+ if ((entry = storage_find_by_name(alloc_descr)) == NULL &&
+ (entry = storage_entry_create(alloc_descr)) == NULL)
+ return;
+
+ entry->flags |= HR_STORAGE_FOUND;
+ entry->type = &OIDX_hrStorageRam_c;
+
+ if ((tmp_size = memstat_get_size(mt_item)) == 0)
+ tmp_size = memstat_get_sizemask(mt_item);
+ entry->allocationUnits =
+ (tmp_size > INT_MAX ? INT_MAX : (int32_t)tmp_size);
+
+ tmp_size = memstat_get_countlimit(mt_item);
+ entry->size =
+ (tmp_size > INT_MAX ? INT_MAX : (int32_t)tmp_size);
+
+ tmp_size = memstat_get_count(mt_item);
+ entry->used =
+ (tmp_size > INT_MAX ? INT_MAX : (int32_t)tmp_size);
+
+ tmp_size = memstat_get_failures(mt_item);
+ entry->allocationFailures =
+ (tmp_size > INT_MAX ? INT_MAX : (int32_t)tmp_size);
+
+ } while((mt_item = memstat_mtl_next(mt_item)) != NULL);
+}
+
+/**
+ * Get swap info
+ */
+static void
+storage_OS_get_swap(void)
+{
+ int nswapdev = 0;
+ size_t len = sizeof(nswapdev);
+ struct storage_entry *entry;
+ char swap_w_prefix[SE_DESC_MLEN];
+
+ if (sysctlbyname("vm.nswapdev", &nswapdev, &len, NULL,0 ) < 0) {
+ syslog(LOG_ERR,
+ "hrStorageTable: sysctlbyname(\"vm.nswapdev\") "
+ "failed. %m");
+ assert(0);
+ return;
+ }
+
+ if (nswapdev <= 0) {
+ HRDBG("vm.nswapdev is %d", nswapdev);
+ return;
+ }
+
+ if (nswapdev + 1 != (int)swap_devs_len || swap_devs == NULL) {
+ swap_devs_len = nswapdev + 1;
+ swap_devs = reallocf(swap_devs,
+ swap_devs_len * sizeof(struct kvm_swap));
+
+ assert(swap_devs != NULL);
+ if (swap_devs == NULL) {
+ swap_devs_len = 0;
+ return;
+ }
+ }
+
+ nswapdev = kvm_getswapinfo(hr_kd, swap_devs, swap_devs_len, 0);
+ if (nswapdev < 0) {
+ syslog(LOG_ERR,
+ "hrStorageTable: kvm_getswapinfo failed. %m\n");
+ assert(0);
+ return;
+ }
+
+ for (len = 0; len < (size_t)nswapdev; len++) {
+ memset(&swap_w_prefix[0], '\0', sizeof(swap_w_prefix));
+ snprintf(swap_w_prefix, sizeof(swap_w_prefix) - 1,
+ "Swap:%s%s", _PATH_DEV, swap_devs[len].ksw_devname);
+
+ entry = storage_find_by_name(swap_w_prefix);
+ if (entry == NULL)
+ entry = storage_entry_create(swap_w_prefix);
+
+ assert (entry != NULL);
+ if (entry == NULL)
+ return; /* Out of luck */
+
+ entry->flags |= HR_STORAGE_FOUND;
+ entry->type = &OIDX_hrStorageVirtualMemory_c;
+ entry->allocationUnits = getpagesize();
+ entry->size = swap_devs[len].ksw_total;
+ entry->used = swap_devs[len].ksw_used;
+ entry->allocationFailures = 0;
+ }
+}
+
+/**
+ * Query the underlaying OS for the mounted file systems
+ * anf fill in the respective lists (for hrStorageTable and for hrFSTable)
+ */
+static void
+storage_OS_get_fs(void)
+{
+ struct storage_entry *entry;
+ uint64_t used_blocks_count = 0;
+ char fs_string[SE_DESC_MLEN];
+ int mounted_fs_count;
+ int i = 0;
+
+ if ((mounted_fs_count = getfsstat(NULL, 0, MNT_NOWAIT)) < 0) {
+ syslog(LOG_ERR, "hrStorageTable: getfsstat() failed: %m");
+ return; /* out of luck this time */
+ }
+
+ if (mounted_fs_count != (int)fs_buf_count || fs_buf == NULL) {
+ fs_buf_count = mounted_fs_count;
+ fs_buf = reallocf(fs_buf, fs_buf_count * sizeof(struct statfs));
+ if (fs_buf == NULL) {
+ fs_buf_count = 0;
+ assert(0);
+ return;
+ }
+ }
+
+ if ((mounted_fs_count = getfsstat(fs_buf,
+ fs_buf_count * sizeof(struct statfs), MNT_NOWAIT)) < 0) {
+ syslog(LOG_ERR, "hrStorageTable: getfsstat() failed: %m");
+ return; /* out of luck this time */
+ }
+
+ HRDBG("got %d mounted FS", mounted_fs_count);
+
+ fs_tbl_pre_refresh();
+
+ for (i = 0; i < mounted_fs_count; i++) {
+ snprintf(fs_string, sizeof(fs_string),
+ "%s, type: %s, dev: %s", fs_buf[i].f_mntonname,
+ fs_buf[i].f_fstypename, fs_buf[i].f_mntfromname);
+
+ entry = storage_find_by_name(fs_string);
+ if (entry == NULL)
+ entry = storage_entry_create(fs_string);
+
+ assert (entry != NULL);
+ if (entry == NULL)
+ return; /* Out of luck */
+
+ entry->flags |= HR_STORAGE_FOUND;
+ entry->type = fs_get_type(&fs_buf[i]); /*XXX - This is wrong*/
+
+ if (fs_buf[i].f_bsize > INT_MAX)
+ entry->allocationUnits = INT_MAX;
+ else
+ entry->allocationUnits = fs_buf[i].f_bsize;
+
+ if (fs_buf[i].f_blocks > INT_MAX)
+ entry->size = INT_MAX;
+ else
+ entry->size = fs_buf[i].f_blocks;
+
+ used_blocks_count = fs_buf[i].f_blocks - fs_buf[i].f_bfree;
+
+ if (used_blocks_count > INT_MAX)
+ entry->used = INT_MAX;
+ else
+ entry->used = used_blocks_count;
+
+ entry->allocationFailures = 0;
+
+ /* take care of hrFSTable */
+ fs_tbl_process_statfs_entry(&fs_buf[i], entry->index);
+ }
+
+ fs_tbl_post_refresh();
+}
+
+/**
+ * Initialize storage table and populate it.
+ */
+void
+init_storage_tbl(void)
+{
+ if ((mt_list = memstat_mtl_alloc()) == NULL)
+ syslog(LOG_ERR,
+ "hrStorageTable: memstat_mtl_alloc() failed: %m");
+
+ refresh_storage_tbl(1);
+}
+
+void
+fini_storage_tbl(void)
+{
+ struct storage_map_entry *n1;
+
+ if (swap_devs != NULL) {
+ free(swap_devs);
+ swap_devs = NULL;
+ }
+ swap_devs_len = 0;
+
+ if (fs_buf != NULL) {
+ free(fs_buf);
+ fs_buf = NULL;
+ }
+ fs_buf_count = 0;
+
+ while ((n1 = STAILQ_FIRST(&storage_map)) != NULL) {
+ STAILQ_REMOVE_HEAD(&storage_map, link);
+ if (n1->entry != NULL) {
+ TAILQ_REMOVE(&storage_tbl, n1->entry, link);
+ free(n1->entry->descr);
+ free(n1->entry);
+ }
+ free(n1->a_name);
+ free(n1);
+ }
+ assert(TAILQ_EMPTY(&storage_tbl));
+}
+
+void
+refresh_storage_tbl(int force)
+{
+ struct storage_entry *entry, *entry_tmp;
+
+ if (!force && storage_tick != 0 &&
+ this_tick - storage_tick < storage_tbl_refresh) {
+ HRDBG("no refresh needed");
+ return;
+ }
+
+ /* mark each entry as missing */
+ TAILQ_FOREACH(entry, &storage_tbl, link)
+ entry->flags &= ~HR_STORAGE_FOUND;
+
+ storage_OS_get_vm();
+ storage_OS_get_swap();
+ storage_OS_get_fs();
+ storage_OS_get_memstat();
+
+ /*
+ * Purge items that disappeared
+ */
+ TAILQ_FOREACH_SAFE(entry, &storage_tbl, link, entry_tmp)
+ if (!(entry->flags & HR_STORAGE_FOUND))
+ storage_entry_delete(entry);
+
+ storage_tick = this_tick;
+
+ HRDBG("refresh DONE");
+}
+
+/*
+ * This is the implementation for a generated (by our SNMP tool)
+ * function prototype, see hostres_tree.h
+ * It handles the SNMP operations for hrStorageTable
+ */
+int
+op_hrStorageTable(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op curr_op)
+{
+ struct storage_entry *entry;
+
+ refresh_storage_tbl(0);
+
+ switch (curr_op) {
+
+ case SNMP_OP_GETNEXT:
+ if ((entry = NEXT_OBJECT_INT(&storage_tbl,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+
+ value->var.len = sub + 1;
+ value->var.subs[sub] = entry->index;
+ goto get;
+
+ case SNMP_OP_GET:
+ if ((entry = FIND_OBJECT_INT(&storage_tbl,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_SET:
+ if ((entry = FIND_OBJECT_INT(&storage_tbl,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NO_CREATION);
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ abort();
+ }
+ abort();
+
+ get:
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_hrStorageIndex:
+ value->v.integer = entry->index;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrStorageType:
+ assert(entry->type != NULL);
+ value->v.oid = *entry->type;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrStorageDescr:
+ assert(entry->descr != NULL);
+ return (string_get(value, entry->descr, -1));
+ break;
+
+ case LEAF_hrStorageAllocationUnits:
+ value->v.integer = entry->allocationUnits;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrStorageSize:
+ value->v.integer = entry->size;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrStorageUsed:
+ value->v.integer = entry->used;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrStorageAllocationFailures:
+ value->v.uint32 = entry->allocationFailures;
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+}
diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swinstalled_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swinstalled_tbl.c
new file mode 100644
index 0000000..5fa5b4c
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swinstalled_tbl.c
@@ -0,0 +1,555 @@
+/*
+ * Copyright (c) 2005-2006 The FreeBSD Project
+ * All rights reserved.
+ *
+ * Author: Victor Cruceru <soc-victor@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ *
+ * Host Resources MIB implementation for SNMPd: instrumentation for
+ * hrSWInstalledTable
+ */
+
+#include <sys/limits.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/utsname.h>
+
+#include <assert.h>
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <sysexits.h>
+
+#include "hostres_snmp.h"
+#include "hostres_oid.h"
+#include "hostres_tree.h"
+
+#define CONTENTS_FNAME "+CONTENTS"
+
+enum SWInstalledType {
+ SWI_UNKNOWN = 1,
+ SWI_OPERATING_SYSTEM = 2,
+ SWI_DEVICE_DRIVER = 3,
+ SWI_APPLICATION = 4
+};
+
+#define SW_NAME_MLEN (64 + 1)
+
+/*
+ * This structure is used to hold a SNMP table entry
+ * for HOST-RESOURCES-MIB's hrSWInstalledTable
+ */
+struct swins_entry {
+ int32_t index;
+ u_char *name; /* max len for this is SW_NAME_MLEN */
+ const struct asn_oid *id;
+ int32_t type; /* from enum SWInstalledType */
+ u_char date[11];
+ u_int date_len;
+
+#define HR_SWINSTALLED_FOUND 0x001
+#define HR_SWINSTALLED_IMMUTABLE 0x002
+ uint32_t flags;
+
+ TAILQ_ENTRY(swins_entry) link;
+};
+TAILQ_HEAD(swins_tbl, swins_entry);
+
+/*
+ * Table to keep a conistent mapping between software and indexes.
+ */
+struct swins_map_entry {
+ int32_t index; /* swins_entry::index */
+ u_char *name; /* map key,a copy of swins_entry::name*/
+
+ /*
+ * next may be NULL if the respective hrSWInstalledTblEntry
+ * is (temporally) gone
+ */
+ struct swins_entry *entry;
+
+ STAILQ_ENTRY(swins_map_entry) link;
+};
+STAILQ_HEAD(swins_map, swins_map_entry);
+
+/* map for consistent indexing */
+static struct swins_map swins_map = STAILQ_HEAD_INITIALIZER(swins_map);
+
+/* the head of the list with hrSWInstalledTable's entries */
+static struct swins_tbl swins_tbl = TAILQ_HEAD_INITIALIZER(swins_tbl);
+
+/* next int available for indexing the hrSWInstalledTable */
+static uint32_t next_swins_index = 1;
+
+/* last (agent) tick when hrSWInstalledTable was updated */
+static uint64_t swins_tick;
+
+/* maximum number of ticks between updates of network table */
+uint32_t swins_tbl_refresh = HR_SWINS_TBL_REFRESH * 100;
+
+/* package directory */
+u_char *pkg_dir;
+
+/* last change of package list */
+static time_t os_pkg_last_change;
+
+/**
+ * Create a new entry into the hrSWInstalledTable
+ */
+static struct swins_entry *
+swins_entry_create(const char *name)
+{
+ struct swins_entry *entry;
+ struct swins_map_entry *map;
+
+ STAILQ_FOREACH(map, &swins_map, link)
+ if (strcmp((const char *)map->name, name) == 0)
+ break;
+
+ if (map == NULL) {
+ size_t name_len;
+ /* new object - get a new index */
+ if (next_swins_index > INT_MAX) {
+ syslog(LOG_ERR, "%s: hrSWInstalledTable index wrap",
+ __func__ );
+ /* There isn't much we can do here.
+ * If the next_swins_index is consumed
+ * then we can't add entries to this table
+ * So it is better to exit - if the table is sparsed
+ * at the next agent run we can fill it fully.
+ */
+ errx(EX_SOFTWARE, "hrSWInstalledTable index wrap");
+ }
+
+ if ((map = malloc(sizeof(*map))) == NULL) {
+ syslog(LOG_ERR, "%s: %m", __func__ );
+ return (NULL);
+ }
+
+ name_len = strlen(name) + 1;
+ if (name_len > SW_NAME_MLEN)
+ name_len = SW_NAME_MLEN;
+
+ if ((map->name = malloc(name_len)) == NULL) {
+ syslog(LOG_WARNING, "%s: %m", __func__);
+ free(map);
+ return (NULL);
+ }
+
+ map->index = next_swins_index++;
+ strlcpy((char *)map->name, name, name_len);
+
+ STAILQ_INSERT_TAIL(&swins_map, map, link);
+
+ HRDBG("%s added into hrSWInstalled at %d", name, map->index);
+ }
+
+ if ((entry = malloc(sizeof(*entry))) == NULL) {
+ syslog(LOG_WARNING, "%s: %m", __func__);
+ return (NULL);
+ }
+ memset(entry, 0, sizeof(*entry));
+
+ if ((entry->name = strdup(map->name)) == NULL) {
+ syslog(LOG_WARNING, "%s: %m", __func__);
+ free(entry);
+ return (NULL);
+ }
+
+ entry->index = map->index;
+ map->entry = entry;
+
+ INSERT_OBJECT_INT(entry, &swins_tbl);
+
+ return (entry);
+}
+
+/**
+ * Delete an entry in the hrSWInstalledTable
+ */
+static void
+swins_entry_delete(struct swins_entry *entry)
+{
+ struct swins_map_entry *map;
+
+ assert(entry != NULL);
+
+ TAILQ_REMOVE(&swins_tbl, entry, link);
+
+ STAILQ_FOREACH(map, &swins_map, link)
+ if (map->entry == entry) {
+ map->entry = NULL;
+ break;
+ }
+
+ free(entry->name);
+ free(entry);
+}
+
+/**
+ * Find an entry given it's name
+ */
+static struct swins_entry *
+swins_find_by_name(const char *name)
+{
+ struct swins_entry *entry;
+
+ TAILQ_FOREACH(entry, &swins_tbl, link)
+ if (strcmp((const char*)entry->name, name) == 0)
+ return (entry);
+ return (NULL);
+}
+
+/**
+ * Finalize this table
+ */
+void
+fini_swins_tbl(void)
+{
+ struct swins_map_entry *n1;
+
+ while ((n1 = STAILQ_FIRST(&swins_map)) != NULL) {
+ STAILQ_REMOVE_HEAD(&swins_map, link);
+ if (n1->entry != NULL) {
+ TAILQ_REMOVE(&swins_tbl, n1->entry, link);
+ free(n1->entry->name);
+ free(n1->entry);
+ }
+ free(n1->name);
+ free(n1);
+ }
+ assert(TAILQ_EMPTY(&swins_tbl));
+}
+
+/**
+ * Get the *running* O/S identification
+ */
+static void
+swins_get_OS_ident(void)
+{
+ struct utsname os_id;
+ char os_string[SW_NAME_MLEN] = "";
+ struct swins_entry *entry;
+ u_char *boot;
+ struct stat sb;
+ struct tm k_ts;
+
+ if (uname(&os_id) == -1) {
+ syslog(LOG_WARNING, "%s: %m", __func__);
+ return;
+ }
+
+ snprintf(os_string, sizeof(os_string), "%s: %s",
+ os_id.sysname, os_id.version);
+
+ if ((entry = swins_find_by_name(os_string)) != NULL ||
+ (entry = swins_entry_create(os_string)) == NULL)
+ return;
+
+ entry->flags |= (HR_SWINSTALLED_FOUND | HR_SWINSTALLED_IMMUTABLE);
+ entry->id = &oid_zeroDotZero;
+ entry->type = (int32_t)SWI_OPERATING_SYSTEM;
+ memset(entry->date, 0, sizeof(entry->date));
+
+ if (OS_getSystemInitialLoadParameters(&boot) == SNMP_ERR_NOERROR &&
+ strlen(boot) > 0 && stat(boot, &sb) == 0 &&
+ localtime_r(&sb.st_ctime, &k_ts) != NULL)
+ entry->date_len = make_date_time(entry->date, &k_ts, 0);
+}
+
+/**
+ * Read the installed packages
+ */
+static int
+swins_get_packages(void)
+{
+ struct stat sb;
+ DIR *p_dir;
+ struct dirent *ent;
+ struct tm k_ts;
+ char *pkg_file;
+ struct swins_entry *entry;
+ int ret = 0;
+
+ if (pkg_dir == NULL)
+ /* initialisation may have failed */
+ return (-1);
+
+ if (stat(pkg_dir, &sb) != 0) {
+ syslog(LOG_ERR, "hrSWInstalledTable: stat(\"%s\") failed: %m",
+ pkg_dir);
+ return (-1);
+ }
+ if (!S_ISDIR(sb.st_mode)) {
+ syslog(LOG_ERR, "hrSWInstalledTable: \"%s\" is not a directory",
+ pkg_dir);
+ return (-1);
+ }
+ if (sb.st_ctime <= os_pkg_last_change) {
+ HRDBG("no need to rescan installed packages -- "
+ "directory time-stamp unmodified");
+
+ TAILQ_FOREACH(entry, &swins_tbl, link)
+ entry->flags |= HR_SWINSTALLED_FOUND;
+
+ return (0);
+ }
+
+ if ((p_dir = opendir(pkg_dir)) == NULL) {
+ syslog(LOG_ERR, "hrSWInstalledTable: opendir(\"%s\") failed: "
+ "%m", pkg_dir);
+ return (-1);
+ }
+
+ while (errno = 0, (ent = readdir(p_dir)) != NULL) {
+ HRDBG(" pkg file: %s", ent->d_name);
+
+ /* check that the contents file is a regular file */
+ if (asprintf(&pkg_file, "%s/%s/%s", pkg_dir, ent->d_name,
+ CONTENTS_FNAME) == -1)
+ continue;
+
+ if (stat(pkg_file, &sb) != 0 ) {
+ free(pkg_file);
+ continue;
+ }
+
+ if (!S_ISREG(sb.st_mode)) {
+ syslog(LOG_ERR, "hrSWInstalledTable: \"%s\" not a "
+ "regular file -- skipped", pkg_file);
+ free(pkg_file);
+ continue;
+ }
+ free(pkg_file);
+
+ /* read directory timestamp on package */
+ if (asprintf(&pkg_file, "%s/%s", pkg_dir, ent->d_name) == -1)
+ continue;
+
+ if (stat(pkg_file, &sb) == -1 ||
+ localtime_r(&sb.st_ctime, &k_ts) == NULL) {
+ free(pkg_file);
+ continue;
+ }
+ free(pkg_file);
+
+ /* update or create entry */
+ if ((entry = swins_find_by_name(ent->d_name)) == NULL &&
+ (entry = swins_entry_create(ent->d_name)) == NULL) {
+ ret = -1;
+ goto PKG_LOOP_END;
+ }
+
+ entry->flags |= HR_SWINSTALLED_FOUND;
+ entry->id = &oid_zeroDotZero;
+ entry->type = (int32_t)SWI_APPLICATION;
+
+ entry->date_len = make_date_time(entry->date, &k_ts, 0);
+ }
+
+ if (errno != 0) {
+ syslog(LOG_ERR, "hrSWInstalledTable: readdir_r(\"%s\") failed:"
+ " %m", pkg_dir);
+ ret = -1;
+ } else {
+ /*
+ * save the timestamp of directory
+ * to avoid any further scanning
+ */
+ os_pkg_last_change = sb.st_ctime;
+ }
+ PKG_LOOP_END:
+ (void)closedir(p_dir);
+ return (ret);
+}
+
+/**
+ * Refresh the installed software table.
+ */
+void
+refresh_swins_tbl(void)
+{
+ int ret;
+ struct swins_entry *entry, *entry_tmp;
+
+ if (this_tick - swins_tick < swins_tbl_refresh) {
+ HRDBG("no refresh needed");
+ return;
+ }
+
+ /* mark each entry as missing */
+ TAILQ_FOREACH(entry, &swins_tbl, link)
+ entry->flags &= ~HR_SWINSTALLED_FOUND;
+
+ ret = swins_get_packages();
+
+ TAILQ_FOREACH_SAFE(entry, &swins_tbl, link, entry_tmp)
+ if (!(entry->flags & HR_SWINSTALLED_FOUND) &&
+ !(entry->flags & HR_SWINSTALLED_IMMUTABLE))
+ swins_entry_delete(entry);
+
+ if (ret == 0)
+ swins_tick = this_tick;
+}
+
+/**
+ * Create and populate the package table
+ */
+void
+init_swins_tbl(void)
+{
+
+ if ((pkg_dir = malloc(sizeof(PATH_PKGDIR))) == NULL)
+ syslog(LOG_ERR, "%s: %m", __func__);
+ else
+ strcpy(pkg_dir, PATH_PKGDIR);
+
+ swins_get_OS_ident();
+ refresh_swins_tbl();
+
+ HRDBG("init done");
+}
+
+/**
+ * SNMP handler
+ */
+int
+op_hrSWInstalledTable(struct snmp_context *ctx __unused,
+ struct snmp_value *value, u_int sub, u_int iidx __unused,
+ enum snmp_op curr_op)
+{
+ struct swins_entry *entry;
+
+ refresh_swins_tbl();
+
+ switch (curr_op) {
+
+ case SNMP_OP_GETNEXT:
+ if ((entry = NEXT_OBJECT_INT(&swins_tbl,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ value->var.len = sub + 1;
+ value->var.subs[sub] = entry->index;
+ goto get;
+
+ case SNMP_OP_GET:
+ if ((entry = FIND_OBJECT_INT(&swins_tbl,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_SET:
+ if ((entry = FIND_OBJECT_INT(&swins_tbl,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NO_CREATION);
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ abort();
+ }
+ abort();
+
+ get:
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_hrSWInstalledIndex:
+ value->v.integer = entry->index;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrSWInstalledName:
+ return (string_get(value, entry->name, -1));
+ break;
+
+ case LEAF_hrSWInstalledID:
+ assert(entry->id != NULL);
+ value->v.oid = *entry->id;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrSWInstalledType:
+ value->v.integer = entry->type;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrSWInstalledDate:
+ return (string_get(value, entry->date, entry->date_len));
+ }
+ abort();
+}
+
+/**
+ * Scalars
+ */
+int
+op_hrSWInstalled(struct snmp_context *ctx __unused,
+ struct snmp_value *value __unused, u_int sub,
+ u_int iidx __unused, enum snmp_op curr_op)
+{
+
+ /* only SNMP GET is possible */
+ switch (curr_op) {
+
+ case SNMP_OP_GET:
+ goto get;
+
+ case SNMP_OP_SET:
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ case SNMP_OP_GETNEXT:
+ abort();
+ }
+ abort();
+
+ get:
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_hrSWInstalledLastChange:
+ case LEAF_hrSWInstalledLastUpdateTime:
+ /*
+ * We always update the entire table so these two tick
+ * values should be equal.
+ */
+ refresh_swins_tbl();
+ if (swins_tick <= start_tick)
+ value->v.uint32 = 0;
+ else {
+ uint64_t lastChange = swins_tick - start_tick;
+
+ /* may overflow the SNMP type */
+ value->v.uint32 =
+ (lastChange > UINT_MAX ? UINT_MAX : lastChange);
+ }
+
+ return (SNMP_ERR_NOERROR);
+
+ default:
+ abort();
+ }
+}
diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swrun_tbl.c b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swrun_tbl.c
new file mode 100644
index 0000000..1f82648
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_swrun_tbl.c
@@ -0,0 +1,792 @@
+/*
+ * Copyright (c) 2005-2006 The FreeBSD Project
+ * All rights reserved.
+ *
+ * Author: Victor Cruceru <soc-victor@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ *
+ * Host Resources MIB for SNMPd. Implementation for hrSWRunTable
+ */
+
+#include <sys/param.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#include <sys/linker.h>
+
+#include <assert.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "hostres_snmp.h"
+#include "hostres_oid.h"
+#include "hostres_tree.h"
+
+/*
+ * Ugly thing: PID_MAX, NO_PID defined only in kernel
+ */
+#define NO_PID 100000
+
+enum SWRunType {
+ SRT_UNKNOWN = 1,
+ SRT_OPERATING_SYSTEM = 2,
+ SRT_DEVICE_DRIVER = 3,
+ SRT_APPLICATION = 4
+
+};
+
+enum SWRunStatus {
+ SRS_RUNNING = 1,
+ SRS_RUNNABLE = 2,
+ SRS_NOT_RUNNABLE = 3,
+ SRS_INVALID = 4
+};
+
+/* Maximum lengths for the strings according to the MIB */
+#define SWR_NAME_MLEN (64 + 1)
+#define SWR_PATH_MLEN (128 + 1)
+#define SWR_PARAM_MLEN (128 + 1)
+
+/*
+ * This structure is used to hold a SNMP table entry
+ * for both hrSWRunTable and hrSWRunPerfTable because
+ * hrSWRunPerfTable AUGMENTS hrSWRunTable
+ */
+struct swrun_entry {
+ int32_t index;
+ u_char *name; /* it may be NULL */
+ const struct asn_oid *id;
+ u_char *path; /* it may be NULL */
+ u_char *parameters; /* it may be NULL */
+ int32_t type; /* enum SWRunType */
+ int32_t status; /* enum SWRunStatus */
+ int32_t perfCPU;
+ int32_t perfMemory;
+#define HR_SWRUN_FOUND 0x001
+ uint32_t flags;
+ uint64_t r_tick; /* tick when entry refreshed */
+ TAILQ_ENTRY(swrun_entry) link;
+};
+TAILQ_HEAD(swrun_tbl, swrun_entry);
+
+/* the head of the list with hrSWRunTable's entries */
+static struct swrun_tbl swrun_tbl = TAILQ_HEAD_INITIALIZER(swrun_tbl);
+
+/* last (agent) tick when hrSWRunTable and hrSWRunPerTable was updated */
+static uint64_t swrun_tick;
+
+/* maximum number of ticks between updates of SWRun and SWRunPerf table */
+uint32_t swrun_tbl_refresh = HR_SWRUN_TBL_REFRESH * 100;
+
+/* the value of the MIB object with the same name */
+static int32_t SWOSIndex;
+
+/**
+ * Malloc a new entry and add it to the list
+ * associated to this table. The item identified by
+ * the index parameter must not exist in this list.
+ */
+static struct swrun_entry *
+swrun_entry_create(int32_t idx)
+{
+ struct swrun_entry *entry;
+
+ if ((entry = malloc(sizeof(*entry))) == NULL) {
+ syslog(LOG_WARNING, "%s: %m", __func__);
+ return (NULL);
+ }
+ memset(entry, 0, sizeof(*entry));
+ entry->index = idx;
+
+ INSERT_OBJECT_INT(entry, &swrun_tbl);
+ return (entry);
+}
+
+/**
+ * Unlink the entry from the list and then free its heap memory
+ */
+static void
+swrun_entry_delete(struct swrun_entry *entry)
+{
+
+ assert(entry != NULL);
+
+ TAILQ_REMOVE(&swrun_tbl, entry, link);
+
+ free(entry->name);
+ free(entry->path);
+ free(entry->parameters);
+ free(entry);
+}
+
+/**
+ * Search one item by its index, return NULL if none found
+ */
+static struct swrun_entry *
+swrun_entry_find_by_index(int32_t idx)
+{
+ struct swrun_entry *entry;
+
+ TAILQ_FOREACH(entry, &swrun_tbl, link)
+ if (entry->index == idx)
+ return (entry);
+ return (NULL);
+}
+
+/**
+ * Translate the kernel's process status to SNMP.
+ */
+static enum SWRunStatus
+swrun_OS_get_proc_status(const struct kinfo_proc *kp)
+{
+
+ assert(kp != NULL);
+ if(kp == NULL) {
+ return (SRS_INVALID);
+ }
+
+ /*
+ * I'm using the old style flags - they look cleaner to me,
+ * at least for the purpose of this SNMP table
+ */
+ switch (kp->ki_stat) {
+
+ case SSTOP:
+ return (SRS_NOT_RUNNABLE);
+
+ case SWAIT:
+ case SLOCK:
+ case SSLEEP:
+ return (SRS_RUNNABLE);
+
+ case SZOMB:
+ return (SRS_INVALID);
+
+ case SIDL:
+ case SRUN:
+ return (SRS_RUNNING);
+
+ default:
+ syslog(LOG_ERR,"Unknown process state: %d", kp->ki_stat);
+ return (SRS_INVALID);
+ }
+}
+
+/**
+ * Make an SNMP table entry from a kernel one.
+ */
+static void
+kinfo_proc_to_swrun_entry(const struct kinfo_proc *kp,
+ struct swrun_entry *entry)
+{
+ char **argv = NULL;
+ uint64_t cpu_time = 0;
+ size_t pname_len;
+
+ pname_len = strlen(kp->ki_comm) + 1;
+ entry->name = reallocf(entry->name, pname_len);
+ if (entry->name != NULL)
+ strlcpy(entry->name, kp->ki_comm, pname_len);
+
+ entry->id = &oid_zeroDotZero; /* unknown id - FIXME */
+
+ assert(hr_kd != NULL);
+
+ argv = kvm_getargv(hr_kd, kp, SWR_PARAM_MLEN - 1);
+ if(argv != NULL){
+ u_char param[SWR_PARAM_MLEN];
+
+ memset(param, '\0', sizeof(param));
+
+ /*
+ * FIXME
+ * Path seems to not be available.
+ * Try to hack the info in argv[0];
+ * this argv is under control of the program so this info
+ * is not realiable
+ */
+ if(*argv != NULL && (*argv)[0] == '/') {
+ size_t path_len;
+
+ path_len = strlen(*argv) + 1;
+ if (path_len > SWR_PATH_MLEN)
+ path_len = SWR_PATH_MLEN;
+
+ entry->path = reallocf(entry->path, path_len);
+ if (entry->path != NULL) {
+ memset(entry->path, '\0', path_len);
+ strlcpy((char*)entry->path, *argv, path_len);
+ }
+ }
+
+ argv++; /* skip the first one which was used for path */
+
+ while (argv != NULL && *argv != NULL ) {
+ if (param[0] != 0) {
+ /*
+ * add a space between parameters,
+ * except before the first one
+ */
+ strlcat((char *)param, " ", sizeof(param));
+ }
+ strlcat((char *)param, *argv, sizeof(param));
+ argv++;
+ }
+ /* reuse pname_len */
+ pname_len = strlen(param) + 1;
+ if (pname_len > SWR_PARAM_MLEN)
+ pname_len = SWR_PARAM_MLEN;
+
+ entry->parameters = reallocf(entry->parameters, pname_len);
+ strlcpy(entry->parameters, param, pname_len);
+ }
+
+ entry->type = (int32_t)(IS_KERNPROC(kp) ? SRT_OPERATING_SYSTEM :
+ SRT_APPLICATION);
+
+ entry->status = (int32_t)swrun_OS_get_proc_status(kp);
+ cpu_time = kp->ki_runtime / 100000; /* centi-seconds */
+
+ /* may overflow the snmp type */
+ entry->perfCPU = (cpu_time > (uint64_t)INT_MAX ? INT_MAX : cpu_time);
+ entry->perfMemory = kp->ki_size / 1024; /* in kilo-bytes */
+ entry->r_tick = get_ticks();
+}
+
+/**
+ * Create a table entry for a KLD
+ */
+static void
+kld_file_stat_to_swrun(const struct kld_file_stat *kfs,
+ struct swrun_entry *entry)
+{
+ size_t name_len;
+
+ assert(kfs != NULL);
+ assert(entry != NULL);
+
+ name_len = strlen(kfs->name) + 1;
+ if (name_len > SWR_NAME_MLEN)
+ name_len = SWR_NAME_MLEN;
+
+ entry->name = reallocf(entry->name, name_len);
+ if (entry->name != NULL)
+ strlcpy((char *)entry->name, kfs->name, name_len);
+
+ /* FIXME: can we find the location where the module was loaded from? */
+ entry->path = NULL;
+
+ /* no parameters for kernel files (.ko) of for the kernel */
+ entry->parameters = NULL;
+
+ entry->id = &oid_zeroDotZero; /* unknown id - FIXME */
+
+ if (strcmp(kfs->name, "kernel") == 0) {
+ entry->type = (int32_t)SRT_OPERATING_SYSTEM;
+ SWOSIndex = entry->index;
+ } else {
+ entry->type = (int32_t)SRT_DEVICE_DRIVER; /* well, not really */
+ }
+ entry->status = (int32_t)SRS_RUNNING;
+ entry->perfCPU = 0; /* Info not available */
+ entry->perfMemory = kfs->size / 1024; /* in kilo-bytes */
+ entry->r_tick = get_ticks();
+}
+
+/**
+ * Get all visible proceses including the kernel visible threads
+ */
+static void
+swrun_OS_get_procs(void)
+{
+ struct kinfo_proc *plist, *kp;
+ int i;
+ int nproc;
+ struct swrun_entry *entry;
+
+ plist = kvm_getprocs(hr_kd, KERN_PROC_ALL, 0, &nproc);
+ if (plist == NULL || nproc < 0) {
+ syslog(LOG_ERR, "kvm_getprocs() failed: %m");
+ return;
+ }
+ for (i = 0, kp = plist; i < nproc; i++, kp++) {
+ /*
+ * The SNMP table's index must begin from 1 (as specified by
+ * this table definition), the PIDs are starting from 0
+ * so we are translating the PIDs to +1
+ */
+ entry = swrun_entry_find_by_index((int32_t)kp->ki_pid + 1);
+ if (entry == NULL) {
+ /* new entry - get memory for it */
+ entry = swrun_entry_create((int32_t)kp->ki_pid + 1);
+ if (entry == NULL)
+ continue;
+ }
+ entry->flags |= HR_SWRUN_FOUND; /* mark it as found */
+
+ kinfo_proc_to_swrun_entry(kp, entry);
+ }
+}
+
+/*
+ * Get kernel items: first the kernel itself, then the loaded modules.
+ */
+static void
+swrun_OS_get_kinfo(void)
+{
+ int fileid;
+ struct swrun_entry *entry;
+ struct kld_file_stat stat;
+
+ for (fileid = kldnext(0); fileid > 0; fileid = kldnext(fileid)) {
+ stat.version = sizeof(struct kld_file_stat);
+ if (kldstat(fileid, &stat) < 0) {
+ syslog(LOG_ERR, "kldstat() failed: %m");
+ continue;
+ }
+
+ /*
+ * kernel and kernel files (*.ko) will be indexed starting with
+ * NO_PID + 1; NO_PID is PID_MAX + 1 thus it will be no risk to
+ * overlap with real PIDs which are in range of 1 .. NO_PID
+ */
+ entry = swrun_entry_find_by_index(NO_PID + 1 + stat.id);
+ if (entry == NULL) {
+ /* new entry - get memory for it */
+ entry = swrun_entry_create(NO_PID + 1 + stat.id);
+ if (entry == NULL)
+ continue;
+ }
+ entry->flags |= HR_SWRUN_FOUND; /* mark it as found */
+
+ kld_file_stat_to_swrun(&stat, entry);
+ }
+}
+
+/**
+ * Refresh the hrSWRun and hrSWRunPert tables.
+ */
+static void
+refresh_swrun_tbl(void)
+{
+
+ struct swrun_entry *entry, *entry_tmp;
+
+ if (this_tick - swrun_tick < swrun_tbl_refresh) {
+ HRDBG("no refresh needed ");
+ return;
+ }
+
+ /* mark each entry as missing */
+ TAILQ_FOREACH(entry, &swrun_tbl, link)
+ entry->flags &= ~HR_SWRUN_FOUND;
+
+ swrun_OS_get_procs();
+ swrun_OS_get_kinfo();
+
+ /*
+ * Purge items that disappeared
+ */
+ TAILQ_FOREACH_SAFE(entry, &swrun_tbl, link, entry_tmp)
+ if (!(entry->flags & HR_SWRUN_FOUND))
+ swrun_entry_delete(entry);
+
+ swrun_tick = this_tick;
+
+ HRDBG("refresh DONE");
+}
+
+/**
+ * Update the information in this entry
+ */
+static void
+fetch_swrun_entry(struct swrun_entry *entry)
+{
+ struct kinfo_proc *plist;
+ int nproc;
+ struct kld_file_stat stat;
+
+ assert(entry != NULL);
+
+ if (entry->index >= NO_PID + 1) {
+ /*
+ * kernel and kernel files (*.ko) will be indexed
+ * starting with NO_PID + 1; NO_PID is PID_MAX + 1
+ * thus it will be no risk to overlap with real PIDs
+ * which are in range of 1 .. NO_PID
+ */
+ stat.version = sizeof(stat);
+ if (kldstat(entry->index - NO_PID - 1, &stat) == -1) {
+ /*
+ * not found, it's gone. Mark it as invalid for now, it
+ * will be removed from the list at next global refersh
+ */
+ HRDBG("missing item with kid=%d",
+ entry->index - NO_PID - 1);
+ entry->status = (int32_t)SRS_INVALID;
+ } else
+ kld_file_stat_to_swrun(&stat, entry);
+
+ } else {
+ /* this is a process */
+ assert(hr_kd != NULL);
+ plist = kvm_getprocs(hr_kd, KERN_PROC_PID,
+ entry->index - 1, &nproc);
+ if (plist == NULL || nproc != 1) {
+ HRDBG("missing item with PID=%d", entry->index - 1);
+ entry->status = (int32_t)SRS_INVALID;
+ } else
+ kinfo_proc_to_swrun_entry(plist, entry);
+ }
+}
+
+/**
+ * Invalidate entry. For KLDs we try to unload it, for processes we SIGKILL it.
+ */
+static int
+invalidate_swrun_entry(struct swrun_entry *entry, int commit)
+{
+ struct kinfo_proc *plist;
+ int nproc;
+ struct kld_file_stat stat;
+
+ assert(entry != NULL);
+
+ if (entry->index >= NO_PID + 1) {
+ /* this is a kernel item */
+ HRDBG("atempt to unload KLD %d",
+ entry->index - NO_PID - 1);
+
+ if (entry->index == SWOSIndex) {
+ /* can't invalidate the kernel itself */
+ return (SNMP_ERR_NOT_WRITEABLE);
+ }
+
+ stat.version = sizeof(stat);
+ if (kldstat(entry->index - NO_PID - 1, &stat) == -1) {
+ /*
+ * not found, it's gone. Mark it as invalid for now, it
+ * will be removed from the list at next global
+ * refresh
+ */
+ HRDBG("missing item with kid=%d",
+ entry->index - NO_PID - 1);
+ entry->status = (int32_t)SRS_INVALID;
+ return (SNMP_ERR_NOERROR);
+ }
+ /*
+ * There is no way to try to unload a module. There seems
+ * also no way to find out whether it is busy without unloading
+ * it. We can assume that it is busy, if the reference count
+ * is larger than 2, but if it is 1 nothing helps.
+ */
+ if (!commit) {
+ if (stat.refs > 1)
+ return (SNMP_ERR_NOT_WRITEABLE);
+ return (SNMP_ERR_NOERROR);
+ }
+ if (kldunload(stat.id) == -1) {
+ syslog(LOG_ERR,"kldunload for %d/%s failed: %m",
+ stat.id, stat.name);
+ if (errno == EBUSY)
+ return (SNMP_ERR_NOT_WRITEABLE);
+ else
+ return (SNMP_ERR_RES_UNAVAIL);
+ }
+ } else {
+ /* this is a process */
+ assert(hr_kd != NULL);
+
+ plist = kvm_getprocs(hr_kd, KERN_PROC_PID,
+ entry->index - 1, &nproc);
+ if (plist == NULL || nproc != 1) {
+ HRDBG("missing item with PID=%d", entry->index - 1);
+ entry->status = (int32_t)SRS_INVALID;
+ return (SNMP_ERR_NOERROR);
+ }
+ if (IS_KERNPROC(plist)) {
+ /* you don't want to do this */
+ return (SNMP_ERR_NOT_WRITEABLE);
+ }
+ if (kill(entry->index - 1, commit ? SIGKILL : 0) < 0) {
+ syslog(LOG_ERR,"kill (%d, SIGKILL) failed: %m",
+ entry->index - 1);
+ if (errno == ESRCH) {
+ /* race: just gone */
+ entry->status = (int32_t)SRS_INVALID;
+ return (SNMP_ERR_NOERROR);
+ }
+ return (SNMP_ERR_GENERR);
+ }
+ }
+ return (SNMP_ERR_NOERROR);
+}
+
+/**
+ * Popuplate the hrSWRunTable.
+ */
+void
+init_swrun_tbl(void)
+{
+
+ refresh_swrun_tbl();
+ HRDBG("done");
+}
+
+/**
+ * Finalize the hrSWRunTable.
+ */
+void
+fini_swrun_tbl(void)
+{
+ struct swrun_entry *n1;
+
+ while ((n1 = TAILQ_FIRST(&swrun_tbl)) != NULL) {
+ TAILQ_REMOVE(&swrun_tbl, n1, link);
+ free(n1);
+ }
+}
+
+/*
+ * This is the implementation for a generated (by a SNMP tool)
+ * function prototype, see hostres_tree.h
+ * It hanldes the SNMP operations for hrSWRunTable
+ */
+int
+op_hrSWRunTable(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op curr_op)
+{
+ struct swrun_entry *entry;
+ int ret;
+
+ refresh_swrun_tbl();
+
+ switch (curr_op) {
+
+ case SNMP_OP_GETNEXT:
+ if ((entry = NEXT_OBJECT_INT(&swrun_tbl,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ value->var.len = sub + 1;
+ value->var.subs[sub] = entry->index;
+ goto get;
+
+ case SNMP_OP_GET:
+ if ((entry = FIND_OBJECT_INT(&swrun_tbl,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_SET:
+ if ((entry = FIND_OBJECT_INT(&swrun_tbl,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NO_CREATION);
+
+ if (entry->r_tick < this_tick)
+ fetch_swrun_entry(entry);
+
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_hrSWRunStatus:
+ if (value->v.integer != (int32_t)SRS_INVALID)
+ return (SNMP_ERR_WRONG_VALUE);
+
+ if (entry->status == (int32_t)SRS_INVALID)
+ return (SNMP_ERR_NOERROR);
+
+ /*
+ * Here we have a problem with the entire SNMP
+ * model: if we kill now, we cannot rollback.
+ * If we kill in the commit code, we cannot
+ * return an error. Because things may change between
+ * SET and COMMIT this is impossible to handle
+ * correctly.
+ */
+ return (invalidate_swrun_entry(entry, 0));
+ }
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_COMMIT:
+ if ((entry = FIND_OBJECT_INT(&swrun_tbl,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOERROR);
+
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_hrSWRunStatus:
+ if (value->v.integer == (int32_t)SRS_INVALID &&
+ entry->status != (int32_t)SRS_INVALID)
+ (void)invalidate_swrun_entry(entry, 1);
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+ }
+ abort();
+
+ get:
+ ret = SNMP_ERR_NOERROR;
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_hrSWRunIndex:
+ value->v.integer = entry->index;
+ break;
+
+ case LEAF_hrSWRunName:
+ if (entry->name != NULL)
+ ret = string_get(value, entry->name, -1);
+ else
+ ret = string_get(value, "", -1);
+ break;
+
+ case LEAF_hrSWRunID:
+ assert(entry->id != NULL);
+ value->v.oid = *entry->id;
+ break;
+
+ case LEAF_hrSWRunPath:
+ if (entry->path != NULL)
+ ret = string_get(value, entry->path, -1);
+ else
+ ret = string_get(value, "", -1);
+ break;
+
+ case LEAF_hrSWRunParameters:
+ if (entry->parameters != NULL)
+ ret = string_get(value, entry->parameters, -1);
+ else
+ ret = string_get(value, "", -1);
+ break;
+
+ case LEAF_hrSWRunType:
+ value->v.integer = entry->type;
+ break;
+
+ case LEAF_hrSWRunStatus:
+ value->v.integer = entry->status;
+ break;
+
+ default:
+ abort();
+ }
+ return (ret);
+}
+
+/**
+ * Scalar(s) in the SWRun group
+ */
+int
+op_hrSWRun(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op curr_op)
+{
+
+ /* only SNMP GET is possible */
+ switch (curr_op) {
+
+ case SNMP_OP_GET:
+ goto get;
+
+ case SNMP_OP_SET:
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ case SNMP_OP_GETNEXT:
+ abort();
+ }
+ abort();
+
+ get:
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_hrSWOSIndex:
+ value->v.uint32 = SWOSIndex;
+ return (SNMP_ERR_NOERROR);
+
+ default:
+ abort();
+ }
+}
+
+/*
+ * This is the implementation for a generated (by a SNMP tool)
+ * function prototype, see hostres_tree.h
+ * It handles the SNMP operations for hrSWRunPerfTable
+ */
+int
+op_hrSWRunPerfTable(struct snmp_context *ctx __unused,
+ struct snmp_value *value, u_int sub, u_int iidx __unused,
+ enum snmp_op curr_op )
+{
+ struct swrun_entry *entry;
+
+ refresh_swrun_tbl();
+
+ switch (curr_op) {
+
+ case SNMP_OP_GETNEXT:
+ if ((entry = NEXT_OBJECT_INT(&swrun_tbl,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ value->var.len = sub + 1;
+ value->var.subs[sub] = entry->index;
+ goto get;
+
+ case SNMP_OP_GET:
+ if ((entry = FIND_OBJECT_INT(&swrun_tbl,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ goto get;
+
+ case SNMP_OP_SET:
+ if ((entry = FIND_OBJECT_INT(&swrun_tbl,
+ &value->var, sub)) == NULL)
+ return (SNMP_ERR_NO_CREATION);
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ abort();
+ }
+ abort();
+
+ get:
+ switch (value->var.subs[sub - 1]) {
+
+ case LEAF_hrSWRunPerfCPU:
+ value->v.integer = entry->perfCPU;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_hrSWRunPerfMem:
+ value->v.integer = entry->perfMemory;
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+}
diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_tree.def b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_tree.def
new file mode 100644
index 0000000..e92d903
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_hostres/hostres_tree.def
@@ -0,0 +1,292 @@
+#
+# Copyright (c) 2005-2006 The FreeBSD Project
+# All rights reserved.
+#
+# Author: Victor Cruceru <soc-victor@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$
+#
+# This is the .def file for both HOST-RESOURCES-MIB and HOST-RESOURCES-TYPES
+#
+
+(1 internet
+ (2 mgmt
+ (1 mib_2
+ (25 host
+ (1 hrSystem
+ (1 hrSystemUptime TIMETICKS op_hrSystem GET)
+ (2 hrSystemDate OCTETSTRING op_hrSystem GET SET)
+ (3 hrSystemInitialLoadDevice INTEGER op_hrSystem GET SET)
+ (4 hrSystemInitialLoadParameters OCTETSTRING op_hrSystem GET SET)
+ (5 hrSystemNumUsers GAUGE op_hrSystem GET)
+ (6 hrSystemProcesses GAUGE op_hrSystem GET)
+ (7 hrSystemMaxProcesses INTEGER op_hrSystem GET)
+ )
+ (2 hrStorage
+ (1 hrStorageTypes
+ (1 hrStorageOther
+ )
+ (2 hrStorageRam
+ )
+ (3 hrStorageVirtualMemory
+ )
+ (4 hrStorageFixedDisk
+ )
+ (5 hrStorageRemovableDisk
+ )
+ (6 hrStorageFloppyDisk
+ )
+ (7 hrStorageCompactDisc
+ )
+ (8 hrStorageRamDisk
+ )
+ (9 hrStorageFlashMemory
+ )
+ (10 hrStorageNetworkDisk
+ )
+ )
+ (2 hrMemorySize INTEGER op_hrStorage GET)
+ (3 hrStorageTable
+ (1 hrStorageEntry : INTEGER op_hrStorageTable
+ (1 hrStorageIndex INTEGER GET)
+ (2 hrStorageType OID GET)
+ (3 hrStorageDescr OCTETSTRING GET)
+ (4 hrStorageAllocationUnits INTEGER GET)
+ (5 hrStorageSize INTEGER GET SET)
+ (6 hrStorageUsed INTEGER GET)
+ (7 hrStorageAllocationFailures COUNTER GET)
+ )
+ )
+ )
+ (3 hrDevice
+ (1 hrDeviceTypes
+ (1 hrDeviceOther
+ )
+ (2 hrDeviceUnknown
+ )
+ (3 hrDeviceProcessor
+ )
+ (4 hrDeviceNetwork
+ )
+ (5 hrDevicePrinter
+ )
+ (6 hrDeviceDiskStorage
+ )
+ (10 hrDeviceVideo
+ )
+ (11 hrDeviceAudio
+ )
+ (12 hrDeviceCoprocessor
+ )
+ (13 hrDeviceKeyboard
+ )
+ (14 hrDeviceModem
+ )
+ (15 hrDeviceParallelPort
+ )
+ (16 hrDevicePointing
+ )
+ (17 hrDeviceSerialPort
+ )
+ (18 hrDeviceTape
+ )
+ (19 hrDeviceClock
+ )
+ (20 hrDeviceVolatileMemory
+ )
+ (21 hrDeviceNonVolatileMemory
+ )
+ )
+ (2 hrDeviceTable
+ (1 hrDeviceEntry : INTEGER op_hrDeviceTable
+ (1 hrDeviceIndex INTEGER GET)
+ (2 hrDeviceType OID GET)
+ (3 hrDeviceDescr OCTETSTRING GET)
+ (4 hrDeviceID OID GET)
+ (5 hrDeviceStatus INTEGER GET)
+ (6 hrDeviceErrors COUNTER GET)
+ )
+ )
+ (3 hrProcessorTable
+ (1 hrProcessorEntry : INTEGER op_hrProcessorTable
+ (1 hrProcessorFrwID OID GET)
+ (2 hrProcessorLoad INTEGER GET)
+ )
+ )
+ (4 hrNetworkTable
+ (1 hrNetworkEntry : INTEGER op_hrNetworkTable
+ (1 hrNetworkIfIndex INTEGER GET)
+ )
+ )
+ (5 hrPrinterTable
+ (1 hrPrinterEntry : INTEGER op_hrPrinterTable
+ (1 hrPrinterStatus INTEGER GET)
+ (2 hrPrinterDetectedErrorState OCTETSTRING GET)
+ )
+ )
+ (6 hrDiskStorageTable
+ (1 hrDiskStorageEntry : INTEGER op_hrDiskStorageTable
+ (1 hrDiskStorageAccess INTEGER GET)
+ (2 hrDiskStorageMedia INTEGER GET)
+ (3 hrDiskStorageRemoveble INTEGER GET)
+ (4 hrDiskStorageCapacity INTEGER GET)
+ )
+ )
+ (7 hrPartitionTable
+ (1 hrPartitionEntry : INTEGER INTEGER op_hrPartitionTable
+ (1 hrPartitionIndex INTEGER GET)
+ (2 hrPartitionLabel OCTETSTRING GET)
+ (3 hrPartitionID OCTETSTRING GET)
+ (4 hrPartitionSize INTEGER GET)
+ (5 hrPartitionFSIndex INTEGER GET)
+ )
+ )
+ (8 hrFSTable
+ (1 hrFSEntry : INTEGER op_hrFSTable
+ (1 hrFSIndex INTEGER GET)
+ (2 hrFSMountPoint OCTETSTRING GET)
+ (3 hrFSRemoteMountPoint OCTETSTRING GET)
+ (4 hrFSType OID GET)
+ (5 hrFSAccess INTEGER GET)
+ (6 hrFSBootable INTEGER GET)
+ (7 hrFSStorageIndex INTEGER GET)
+ (8 hrFSLastFullBackupDate OCTETSTRING GET SET)
+ (9 hrFSLastPartialBackupDate OCTETSTRING GET SET)
+ )
+ )
+ (9 hrFSTypes
+ (1 hrFSOther
+ )
+ (2 hrFSUnknown
+ )
+ (3 hrFSBerkeleyFFS
+ )
+ (4 hrFSSys5FS
+ )
+ (5 hrFSFat
+ )
+ (6 hrFSHPFS
+ )
+ (7 hrFSHFS
+ )
+ (8 hrFSMFS
+ )
+ (9 hrFSNTFS
+ )
+ (10 hrFSVNode
+ )
+ (11 hrFSJournaled
+ )
+ (12 hrFSiso9660
+ )
+ (13 hrFSRockRidge
+ )
+ (14 hrFSNFS
+ )
+ (15 hrFSNetware
+ )
+ (16 hrFSAFS
+ )
+ (17 hrFSDFS
+ )
+ (18 hrFSAppleshare
+ )
+ (19 hrFSRFS
+ )
+ (20 hrFSDGCFS
+ )
+ (21 hrFSBFS
+ )
+ (22 hrFSFAT32
+ )
+ (23 hrFSLinuxExt2
+ )
+ )
+ )
+ (4 hrSWRun
+ (1 hrSWOSIndex INTEGER op_hrSWRun GET)
+ (2 hrSWRunTable
+ (1 hrSWRunEntry : INTEGER op_hrSWRunTable
+ (1 hrSWRunIndex INTEGER GET)
+ (2 hrSWRunName OCTETSTRING GET)
+ (3 hrSWRunID OID GET)
+ (4 hrSWRunPath OCTETSTRING GET)
+ (5 hrSWRunParameters OCTETSTRING GET)
+ (6 hrSWRunType INTEGER GET)
+ (7 hrSWRunStatus INTEGER GET SET)
+ )
+ )
+ )
+ (5 hrSWRunPerf
+ (1 hrSWRunPerfTable
+ (1 hrSWRunPerfEntry : INTEGER op_hrSWRunPerfTable
+ (1 hrSWRunPerfCPU INTEGER GET)
+ (2 hrSWRunPerfMem INTEGER GET)
+ )
+ )
+ )
+ (6 hrSWInstalled
+ (1 hrSWInstalledLastChange TIMETICKS op_hrSWInstalled GET)
+ (2 hrSWInstalledLastUpdateTime TIMETICKS op_hrSWInstalled GET)
+ (3 hrSWInstalledTable
+ (1 hrSWInstalledEntry : INTEGER op_hrSWInstalledTable
+ (1 hrSWInstalledIndex INTEGER GET)
+ (2 hrSWInstalledName OCTETSTRING GET)
+ (3 hrSWInstalledID OID GET)
+ (4 hrSWInstalledType INTEGER GET)
+ (5 hrSWInstalledDate OCTETSTRING GET)
+ )
+ )
+ )
+ (7 hrMIBAdminInfo
+ (1 hostResourcesMibModule
+ )
+ (2 hrMIBCompliances
+ )
+ (3 hrMIBGroups
+ )
+ )
+ )
+ )
+ )
+ (4 private
+ (1 enterprises
+ (12325 fokus
+ (1 begemot
+ (202 begemotHostres
+ (1 begemotHostresObjects
+ (1 begemotHrStorageUpdate TIMETICKS op_begemot GET SET)
+ (2 begemotHrFSUpdate TIMETICKS op_begemot GET SET)
+ (3 begemotHrDiskStorageUpdate TIMETICKS op_begemot GET SET)
+ (4 begemotHrNetworkUpdate TIMETICKS op_begemot GET SET)
+ (5 begemotHrSWInstalledUpdate TIMETICKS op_begemot GET SET)
+ (6 begemotHrSWRunUpdate TIMETICKS op_begemot GET SET)
+ (7 begemotHrPkgDir OCTETSTRING op_begemot GET SET)
+ )
+ )
+ )
+ )
+ )
+ )
+)
diff --git a/usr.sbin/bsnmpd/modules/snmp_hostres/snmp_hostres.3 b/usr.sbin/bsnmpd/modules/snmp_hostres/snmp_hostres.3
new file mode 100644
index 0000000..05c247d
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_hostres/snmp_hostres.3
@@ -0,0 +1,88 @@
+.\"
+.\" Copyright (C) 2005-2006
+.\" The FreeBSD Project.
+.\" All rights reserved.
+.\"
+.\" Author: Harti 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 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 January 3, 2006
+.Dt snmp_hostres 3
+.Os
+.Sh NAME
+.Nm snmp_hostres
+.Nd host resources module for
+.Xr bsnmpd 8
+.Sh LIBRARY
+.Pq begemotSnmpdModulePath."hostres" = "/usr/lib/snmp_hostres.so"
+.Sh DESCRIPTION
+The
+.Nm
+module implements the HOST-RESOURCES-MIB as standardized in RFC 2790.
+.Sh RESTRICTIONS
+Not all information in the MIB is meaningful in FreeBSD or is available.
+The following variables are not implemented or carry no information:
+.Bl -tag -width indent
+.It Va hrFSType
+There are several types of file systems for which no appropriate OID
+exists yet which are supported by
+.Fx .
+For smbfs, procfs and devfs ,
+.Va hrFSOther
+is returned.
+In all other cases,
+.Va hrFSUnknown .
+.It Va hrFSBootable
+It is questionable what bootable means here.
+Does it mean that the BIOS is available to start a boot on that file system
+or does it mean that there is something bootable?
+In either case this information is not available so this variable returns True
+for the root file system (which is not necessarily correct) and False for
+all others.
+.It Va hrFSLastFullBackupDate , hrFSLastPartialBackupDate
+This is not available and always returns an empty string.
+Theoretically, this could be retrieved from
+.Pa /etc/dumpdates ,
+which would
+hardly be correct given the different ways of doing backups.
+.It Va hrDiskStorageTable
+Floppy devices are currently not reported.
+Also the names of the disks are hard-coded in the module.
+.El
+.Sh FILES
+.Bl -tag -width indent
+.It Pa /usr/share/snmp/defs/hostres_tree.def
+The description of the MIB tree implemented by
+.Nm .
+.It Pa /usr/share/snmp/mibs/HOST-RESOURCES-TYPES.txt
+.It Pa /usr/share/snmp/mibs/HOST-RESOURCES-MIB.txt
+.It Pa /usr/share/snmp/mibs/BEGEMOT-HOSTRES-MIB.txt
+This is the MIB that is implemented by this module.
+.El
+.Sh SEE ALSO
+.Xr gensnmptree 1 ,
+.Xr snmpmod 3
+.Sh AUTHORS
+.An Victor Cruceru Aq soc-victor@FreeBSD.org
diff --git a/usr.sbin/bsnmpd/modules/snmp_mibII/Makefile b/usr.sbin/bsnmpd/modules/snmp_mibII/Makefile
new file mode 100644
index 0000000..278dc24
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_mibII/Makefile
@@ -0,0 +1,27 @@
+# $FreeBSD$
+#
+# Author: Harti Brandt <harti@freebsd.org>
+
+CONTRIB= ${.CURDIR}/../../../../contrib/bsnmp
+.PATH: ${CONTRIB}/snmp_mibII
+
+MOD= mibII
+SRCS= mibII.c mibII_begemot.c mibII_ifmib.c mibII_ifstack.c \
+ mibII_interfaces.c mibII_ip.c mibII_ipaddr.c mibII_nettomedia.c \
+ mibII_rcvaddr.c mibII_route.c mibII_tcp.c mibII_udp.c
+XSYM= ipAddrTable ifTable ifRcvAddressEntry ifMIB ipMIB tcpMIB udpMIB \
+ ipForward ifIndex linkDown linkUp
+MAN= snmp_mibII.3
+
+CFLAGS+= -I${CONTRIB}/lib -I${CONTRIB}/snmpd
+CFLAGS+= -DHAVE_ERR_H -DHAVE_GETADDRINFO -DHAVE_STRLCPY -DHAVE_SYS_TREE_H
+
+DEFS= ${MOD}_tree.def
+INCS= snmp_${MOD}.h
+BMIBS= BEGEMOT-IP-MIB.txt BEGEMOT-MIB2-MIB.txt
+
+.include <bsd.snmpmod.mk>
+
+smilint:
+ env SMIPATH=/usr/share/snmp/mibs:/usr/local/share/snmp/mibs \
+ smilint -c /dev/null -l6 -i group-membership ${BMIBS:C/^/${CONTRIB}\/snmp_mibII\//}
diff --git a/usr.sbin/bsnmpd/modules/snmp_netgraph/BEGEMOT-NETGRAPH.txt b/usr.sbin/bsnmpd/modules/snmp_netgraph/BEGEMOT-NETGRAPH.txt
new file mode 100644
index 0000000..1896fe6
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_netgraph/BEGEMOT-NETGRAPH.txt
@@ -0,0 +1,398 @@
+--
+-- Copyright (c) 2001-2003
+-- Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+-- All rights reserved.
+--
+-- Author: Harti Brandt <harti@freebsd.org>
+--
+-- Redistribution of this software and documentation and use in source and
+-- binary forms, with or without modification, are permitted provided that
+-- the following conditions are met:
+--
+-- 1. Redistributions of source code or documentation must retain the above
+-- copyright notice, this list of conditions and the following disclaimer.
+-- 2. Redistributions in binary form must 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 AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+-- AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+-- INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+-- FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+-- FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+-- INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+-- LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+-- OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+-- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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$
+--
+-- Private MIB for netgraph part of Begemot SNMP daemon.
+--
+BEGEMOT-NETGRAPH-MIB DEFINITIONS ::= BEGIN
+
+IMPORTS
+ MODULE-IDENTITY, OBJECT-TYPE, Counter32, Unsigned32
+ FROM SNMPv2-SMI
+ TEXTUAL-CONVENTION, TruthValue
+ FROM SNMPv2-TC
+ MODULE-COMPLIANCE, OBJECT-GROUP
+ FROM SNMPv2-CONF
+ begemot
+ FROM BEGEMOT-MIB;
+
+begemotNg MODULE-IDENTITY
+ LAST-UPDATED "200311140000Z"
+ ORGANIZATION "Fraunhofer FOKUS, CATS"
+ CONTACT-INFO
+ " Hartmut Brandt
+
+ Postal: Fraunhofer Institute for Open Communication Systems
+ Kaiserin-Augusta-Allee 31
+ 10589 Berlin
+ Germany
+
+ Fax: +49 30 3463 7352
+
+ E-mail: harti@freebsd.org"
+ DESCRIPTION
+ "The MIB for the NetGraph access module for SNMP."
+ ::= { begemot 2 }
+
+begemotNgObjects OBJECT IDENTIFIER ::= { begemotNg 1 }
+
+-- --------------------------------------------------------------------------
+
+NgTypeName ::= TEXTUAL-CONVENTION
+ DISPLAY-HINT "31a"
+ STATUS current
+ DESCRIPTION
+ "Name of a netgraph type."
+ SYNTAX OCTET STRING (SIZE(1..31))
+
+NgNodeName ::= TEXTUAL-CONVENTION
+ DISPLAY-HINT "31a"
+ STATUS current
+ DESCRIPTION
+ "Name of a netgraph node."
+ SYNTAX OCTET STRING (SIZE(1..31))
+
+NgNodeNameOrEmpty ::= TEXTUAL-CONVENTION
+ DISPLAY-HINT "31a"
+ STATUS current
+ DESCRIPTION
+ "Name of a netgraph node."
+ SYNTAX OCTET STRING (SIZE(0..31))
+
+NgHookName ::= TEXTUAL-CONVENTION
+ DISPLAY-HINT "31a"
+ STATUS current
+ DESCRIPTION
+ "Name of a netgraph hook."
+ SYNTAX OCTET STRING (SIZE(1..31))
+
+NgNodeId ::= TEXTUAL-CONVENTION
+ DISPLAY-HINT "x"
+ STATUS current
+ DESCRIPTION
+ "Node identifier."
+ SYNTAX Unsigned32 (1..4294967295)
+
+NgNodeIdOrZero ::= TEXTUAL-CONVENTION
+ DISPLAY-HINT "x"
+ STATUS current
+ DESCRIPTION
+ "Node identifier or 0 for 'no-node'."
+ SYNTAX Unsigned32 (0..4294967295)
+
+-- --------------------------------------------------------------------------
+--
+-- Configuration parameters
+--
+begemotNgConfig OBJECT IDENTIFIER ::= { begemotNgObjects 1 }
+
+begemotNgControlNodeName OBJECT-TYPE
+ SYNTAX NgNodeName
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The name of the netgraph node of this daemon. The name is
+ writeable during initialisation. If the name is set from
+ the empty string to the non-empty string, the netgraph socket
+ is created. Once set it cannot be changed."
+ ::= { begemotNgConfig 1 }
+
+begemotNgResBufSiz OBJECT-TYPE
+ SYNTAX INTEGER (1024..65536)
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The size of the receive buffers for netgraph messages."
+ DEFVAL { 20000 }
+ ::= { begemotNgConfig 2 }
+
+begemotNgTimeout OBJECT-TYPE
+ SYNTAX INTEGER (10..10000)
+ UNITS "milliseconds"
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The maximum time to wait for a response to a netgraph message."
+ DEFVAL { 1000 }
+ ::= { begemotNgConfig 3 }
+
+begemotNgDebugLevel OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-write
+ STATUS current
+ DESCRIPTION
+ "The netgraph library debug level. This should be set only
+ if the daemon is run with a terminal attached."
+ DEFVAL { 0 }
+ ::= { begemotNgConfig 4 }
+
+-- --------------------------------------------------------------------------
+--
+-- The STATISTICS Group
+--
+begemotNgStats OBJECT IDENTIFIER ::= { begemotNgObjects 2 }
+
+begemotNgNoMems OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of times a memory allocation has failed for buffers
+ or the message queue."
+ ::= { begemotNgStats 1 }
+
+begemotNgMsgReadErrs OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of times reading a netgraph message has failed."
+ ::= { begemotNgStats 2 }
+
+begemotNgTooLargeMsgs OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of times a netgraph message was too large for
+ the buffer. Try increasing begemotNgResBufSiz if
+ this happens."
+ ::= { begemotNgStats 3 }
+
+begemotNgDataReadErrs OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of times reading a netgraph data message has failed."
+ ::= { begemotNgStats 4 }
+
+begemotNgTooLargeDatas OBJECT-TYPE
+ SYNTAX Counter32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of times a netgraph data message was too large.
+ You need to increase begemotNgResBufSiz."
+ ::= { begemotNgStats 5 }
+
+-- -----------------------------------------------------
+--
+-- The NODE table
+--
+begemotNgTypeTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF BegemotNgTypeEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A table containing information about all netgraph node types."
+ ::= { begemotNgObjects 3 }
+
+begemotNgTypeEntry OBJECT-TYPE
+ SYNTAX BegemotNgTypeEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table entry that describes one netgraph node."
+ INDEX { begemotNgTypeName }
+ ::= { begemotNgTypeTable 1 }
+
+BegemotNgTypeEntry ::= SEQUENCE {
+ begemotNgTypeName NgTypeName,
+ begemotNgTypeStatus INTEGER
+}
+
+begemotNgTypeName OBJECT-TYPE
+ SYNTAX NgTypeName
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "The name of the type. Used as index."
+ ::= { begemotNgTypeEntry 1 }
+
+begemotNgTypeStatus OBJECT-TYPE
+ SYNTAX INTEGER { loaded(1), unloaded(2) }
+ MAX-ACCESS read-create
+ STATUS current
+ DESCRIPTION
+ "If loaded then the node type is available. A type can be load
+ by setting this field to loaded. It is unload if the field is
+ set to unloaded. Note, that a type cannot be unloaded if it
+ is compiled into the kernel or has nodes of this type. The name
+ of the file containing the type implementation is constructed by
+ prepending ng_ to the type name."
+ ::= { begemotNgTypeEntry 2 }
+
+--
+-- Node table
+--
+begemotNgNodeTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF BegemotNgNodeEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A table containing information about all netgraph nodes."
+ ::= { begemotNgObjects 4 }
+
+begemotNgNodeEntry OBJECT-TYPE
+ SYNTAX BegemotNgNodeEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table entry that describes one netgraph node."
+ INDEX { begemotNgNodeId }
+ ::= { begemotNgNodeTable 1 }
+
+BegemotNgNodeEntry ::= SEQUENCE {
+ begemotNgNodeId NgNodeId,
+ begemotNgNodeStatus INTEGER,
+ begemotNgNodeName NgNodeNameOrEmpty,
+ begemotNgNodeType NgTypeName,
+ begemotNgNodeHooks Unsigned32
+}
+
+begemotNgNodeId OBJECT-TYPE
+ SYNTAX NgNodeId
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "The 32bit node id of this node. 0 is an illegal value."
+ ::= { begemotNgNodeEntry 1 }
+
+begemotNgNodeStatus OBJECT-TYPE
+ SYNTAX INTEGER { valid(1), invalid(2) }
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Indicates whether the node exists or not."
+ ::= { begemotNgNodeEntry 2 }
+
+begemotNgNodeName OBJECT-TYPE
+ SYNTAX NgNodeNameOrEmpty
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Name of the node (if any)."
+ ::= { begemotNgNodeEntry 3 }
+
+begemotNgNodeType OBJECT-TYPE
+ SYNTAX NgTypeName
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Type name of the node."
+ ::= { begemotNgNodeEntry 4 }
+
+begemotNgNodeHooks OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of hooks on this node."
+ ::= { begemotNgNodeEntry 5 }
+
+--
+-- Hook table
+--
+begemotNgHookTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF BegemotNgHookEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A table containing information about all netgraph hooks."
+ ::= { begemotNgObjects 5 }
+
+begemotNgHookEntry OBJECT-TYPE
+ SYNTAX BegemotNgHookEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table entry that describes one netgraph node."
+ INDEX { begemotNgHookNodeId, begemotNgHookHook }
+ ::= { begemotNgHookTable 1 }
+
+BegemotNgHookEntry ::= SEQUENCE {
+ begemotNgHookNodeId NgNodeId,
+ begemotNgHookHook NgHookName,
+ begemotNgHookStatus INTEGER,
+ begemotNgHookPeerNodeId NgNodeId,
+ begemotNgHookPeerHook NgHookName,
+ begemotNgHookPeerType NgTypeName
+}
+
+begemotNgHookNodeId OBJECT-TYPE
+ SYNTAX NgNodeId
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "The 32bit node id of this node."
+ ::= { begemotNgHookEntry 1 }
+
+begemotNgHookHook OBJECT-TYPE
+ SYNTAX NgHookName
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Name of the hook."
+ ::= { begemotNgHookEntry 2 }
+
+begemotNgHookStatus OBJECT-TYPE
+ SYNTAX INTEGER { valid(1), invalid(2) }
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Indicates whether the hook exists or not."
+ ::= { begemotNgHookEntry 3 }
+
+begemotNgHookPeerNodeId OBJECT-TYPE
+ SYNTAX NgNodeId
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The 32bit node id of the peer node of this hook."
+ ::= { begemotNgHookEntry 4 }
+
+begemotNgHookPeerHook OBJECT-TYPE
+ SYNTAX NgHookName
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Name of the peer hook."
+ ::= { begemotNgHookEntry 5 }
+
+begemotNgHookPeerType OBJECT-TYPE
+ SYNTAX NgTypeName
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Name of the peer type."
+ ::= { begemotNgHookEntry 6 }
+
+END
diff --git a/usr.sbin/bsnmpd/modules/snmp_netgraph/Makefile b/usr.sbin/bsnmpd/modules/snmp_netgraph/Makefile
new file mode 100644
index 0000000..7caf7e4
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_netgraph/Makefile
@@ -0,0 +1,17 @@
+# $FreeBSD$
+#
+# Author: Harti Brandt <harti@freebsd.org>
+
+MOD= netgraph
+SRCS= snmp_netgraph.c
+XSYM= begemotNg
+MAN= snmp_netgraph.3
+
+BMIBS= BEGEMOT-NETGRAPH.txt
+DEFS= ${MOD}_tree.def
+INCS= snmp_${MOD}.h
+
+DPADD= ${LIBNETGRAPH}
+LDADD= -lnetgraph
+
+.include <bsd.snmpmod.mk>
diff --git a/usr.sbin/bsnmpd/modules/snmp_netgraph/netgraph_tree.def b/usr.sbin/bsnmpd/modules/snmp_netgraph/netgraph_tree.def
new file mode 100644
index 0000000..eff59ff
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_netgraph/netgraph_tree.def
@@ -0,0 +1,78 @@
+#
+# Copyright (c) 2001-2003
+# Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+# All rights reserved.
+#
+# Author: Harti Brandt <harti@freebsd.org>
+#
+# Redistribution of this software and documentation and use in source and
+# binary forms, with or without modification, are permitted provided that
+# the following conditions are met:
+#
+# 1. Redistributions of source code or documentation must retain the above
+# copyright notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must 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 AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+# AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+# FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+# FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+# OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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$
+#
+# Definition of the tree implemented by snmp_netgraph.
+#
+(1 internet
+ (4 private
+ (1 enterprises
+ (12325 fokus
+ (1 begemot
+ (2 begemotNg
+ (1 begemotNgObjects
+ (1 begemotNgConfig
+ (1 begemotNgControlNodeName OCTETSTRING op_ng_config GET SET)
+ (2 begemotNgResBufSiz INTEGER op_ng_config GET SET)
+ (3 begemotNgTimeout INTEGER op_ng_config GET SET)
+ (4 begemotNgDebugLevel UNSIGNED32 op_ng_config GET SET)
+ )
+# Change definition of stats array if you change StatsGroup
+ (2 begemotNgStats
+ (1 begemotNgNoMems COUNTER op_ng_stats GET)
+ (2 begemotNgMsgReadErrs COUNTER op_ng_stats GET)
+ (3 begemotNgTooLargeMsgs COUNTER op_ng_stats GET)
+ (4 begemotNgDataReadErrs COUNTER op_ng_stats GET)
+ (5 begemotNgTooLargeDatas COUNTER op_ng_stats GET)
+ )
+ (3 begemotNgTypeTable
+ (1 begemotNgTypeEntry : OCTETSTRING op_ng_type
+ (1 begemotNgTypeName OCTETSTRING)
+ (2 begemotNgTypeStatus INTEGER GET SET)
+ ))
+ (4 begemotNgNodeTable
+ (1 begemotNgNodeEntry : INTEGER op_ng_node
+ (1 begemotNgNodeId UNSIGNED32)
+ (2 begemotNgNodeStatus INTEGER GET)
+ (3 begemotNgNodeName OCTETSTRING GET)
+ (4 begemotNgNodeType OCTETSTRING GET)
+ (5 begemotNgNodeHooks UNSIGNED32 GET)
+ ))
+ (5 begemotNgHookTable
+ (1 begemotNgHookEntry : UNSIGNED32 OCTETSTRING op_ng_hook
+ (1 begemotNgHookNodeId UNSIGNED32)
+ (2 begemotNgHookHook OCTETSTRING)
+ (3 begemotNgHookStatus INTEGER GET)
+ (4 begemotNgHookPeerNodeId UNSIGNED32 GET)
+ (5 begemotNgHookPeerHook OCTETSTRING GET)
+ (6 begemotNgHookPeerType OCTETSTRING GET)
+ ))
+ ))
+)))))
diff --git a/usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.3 b/usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.3
new file mode 100644
index 0000000..8742e15
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.3
@@ -0,0 +1,436 @@
+.\"
+.\" Copyright (c) 2001-2003
+.\" Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+.\" All rights reserved.
+.\"
+.\" Author: Harti Brandt <harti@freebsd.org>
+.\"
+.\" Redistribution of this software and documentation and use in source and
+.\" binary forms, with or without modification, are permitted provided that
+.\" the following conditions are met:
+.\"
+.\" 1. Redistributions of source code or documentation must retain the above
+.\" copyright notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+.\" AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+.\" FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+.\" FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+.\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+.\" OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+.\" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 14, 2003
+.Dt SNMP_NETGRAPH 3
+.Os
+.Sh NAME
+.Nm snmp_netgraph ,
+.Nm snmp_node ,
+.Nm snmp_nodename ,
+.Nm ng_cookie_f ,
+.Nm ng_hook_f ,
+.Nm ng_register_cookie ,
+.Nm ng_unregister_cookie ,
+.Nm ng_register_hook ,
+.Nm ng_unregister_hook ,
+.Nm ng_unregister_module ,
+.Nm ng_output ,
+.Nm ng_output_node ,
+.Nm ng_output_id ,
+.Nm ng_dialog ,
+.Nm ng_dialog_node ,
+.Nm ng_dialog_id ,
+.Nm ng_send_data ,
+.Nm ng_mkpeer_id ,
+.Nm ng_connect_node ,
+.Nm ng_connect_id ,
+.Nm ng_connect2_id ,
+.Nm ng_connect2_tee_id ,
+.Nm ng_rmhook ,
+.Nm ng_rmhook_id ,
+.Nm ng_rmhook_tee_id ,
+.Nm ng_shutdown_id ,
+.Nm ng_next_node_id ,
+.Nm ng_node_id ,
+.Nm ng_node_id_node ,
+.Nm ng_node_name ,
+.Nm ng_node_type ,
+.Nm ng_peer_hook_id
+.Nd "netgraph module for snmpd.
+.Sh LIBRARY
+.Pq begemotSnmpdModulePath."netgraph" = "/usr/lib/snmp_netgraph.so"
+.Sh SYNOPSIS
+.In bsnmp/snmpmod.h
+.In bsnmp/snmp_netgraph.h
+.Vt extern ng_ID_t snmp_node ;
+.Vt extern u_char *snmp_nodename ;
+.Ft typedef void
+.Fn ng_cookie_f "const struct ng_mesg *mesg" "const char *path" "ng_ID_t id" "void *uarg"
+.Ft typedef void
+.Fn ng_hook_f "const char *hook" "const u_char *mesg" "size_t len" "void *uarg"
+.Ft void *
+.Fn ng_register_cookie "const struct lmodule *mod" "u_int32_t cookie" "ng_ID_t id" "ng_cookie_f *func" "void *uarg"
+.Ft void
+.Fn ng_unregister_cookie "void *reg"
+.Ft void *
+.Fn ng_register_hook "const struct lmodule *mod" "const char *hook" "ng_hook_f *func" "void *uarg"
+.Ft void
+.Fn ng_unregister_hook "void *reg"
+.Ft void
+.Fn ng_unregister_module "const struct lmodule *mod"
+.Ft int
+.Fn ng_output "const char *path" "u_int cookie" "u_int opcode" "const void *arg" "size_t arglen"
+.Ft int
+.Fn ng_output_node "const char *node" "u_int cookie" "u_int opcode" "const void *arg" "size_t arglen"
+.Ft int
+.Fn ng_output_id "ng_ID_t node" "u_int cookie" "u_int opcode" "const void *arg" "size_t arglen"
+.Ft struct ng_mesg *
+.Fn ng_dialog "const char *path" "u_int cookie" "u_int opcode" "const void *arg" "size_t arglen"
+.Ft struct ng_mesg *
+.Fn ng_dialog_node "const char *node" "u_int cookie" "u_int opcode" "const void *arg" "size_t arglen"
+.Ft struct ng_mesg *
+.Fn ng_dialog_id "ng_ID_t id" "u_int cookie" "u_int opcode" "const void *arg" "size_t arglen"
+.Ft int
+.Fn ng_send_data "const char *hook" "const void *sndbuf" "size_t sndlen"
+.Ft ng_ID_t
+.Fn ng_mkpeer_id "ng_ID_t id" "const char *name" "const char *type" "const char *hook" "const char *peerhook"
+.Ft int
+.Fn ng_connect_node "const char *node" "const char *ourhook" "const char *peerhook"
+.Ft int
+.Fn ng_connect_id "ng_ID_t id" "const char *ourhook" "const char *peerhook"
+.Ft int
+.Fn ng_connect2_id "ng_ID_t id" "ng_ID_t peer" "const char *ourhook" "const char *peerhook"
+.Ft int
+.Fn ng_connect2_tee_id "ng_ID_t id" "ng_ID_t peer" "const char *ourhook" "const char *peerhook"
+.Ft int
+.Fn ng_rmhook "const char *ourhook"
+.Ft int
+.Fn ng_rmhook_id "ng_ID_t id" "const char *hook"
+.Ft int
+.Fn ng_rmhook_tee_id "ng_ID_t id" "const char *hook"
+.Ft int
+.Fn ng_shutdown_id "ng_ID_t id"
+.Ft ng_ID_t
+.Fn ng_next_node_id "ng_ID_t node" "const char *type" "const char *hook"
+.Ft ng_ID_t
+.Fn ng_node_id "const char *path"
+.Ft ng_ID_t
+.Fn ng_node_id_node "const char *node"
+.Ft ng_ID_t
+.Fn ng_node_name "ng_ID_t id" "char *name"
+.Ft ng_ID_t
+.Fn ng_node_type "ng_ID_t id" "char *type"
+.Ft int
+.Fn ng_peer_hook_id "ng_ID_t id" "const char *hook" "char *peerhook"
+.Sh DESCRIPTION
+The
+.Nm snmp_netgraph
+module implements a number of tables and scalars that enable remote access to
+the netgraph subsystem.
+It also exports a number of global variables and
+functions, that allow other modules to easily use the netgraph system.
+.Pp
+If upon start up of the module the variable
+.Va begemotNgControlNodeName
+is not empty the module opens a netgraph socket and names that socket node.
+If the variable is empty, the socket is created, as soon as the variable is
+written with a non-empty name.
+The socket can be closed by writing an empty
+string to the variable.
+The socket itself and its name are available in
+.Va snmp_node
+and
+.Va snmp_nodename .
+.Ss SENDING AND RECEIVING MESSAGES AND DATA
+There are three functions for sending control message:
+.Bl -tag -width ".It Fn ng_output_node"
+.It Fn ng_output
+sends a control message along the given
+.Fa path .
+.It Fn ng_output_node
+sends a control message to the node with name
+.Fa node
+and
+.It Fn ng_output_id
+sends a control message to the node with node id
+.Fa id .
+.El
+.Pp
+Each of these functions takes the following arguments:
+.Bl -tag -width ".It Fa cookie"
+.It Fa cookie
+is the node specific command cookie,
+.It Fa opcode
+is the node specific code for the operation to perform,
+.It Fa arg
+is a pointer to the message itself.
+This message must start with a
+.Vt struct ng_mesg .
+.It Fa arglen
+is the overall length of the message (header plus arguments).
+.El
+The functions return the message id that can be used to match incoming responses
+or -1 if an error occurs.
+.Pp
+Another class of functions is used to send a control message and to wait for
+a matching response.
+Note, that this operation blocks the daemon, so use it
+only if you are sure that the response will happen.
+There is a maximum timeout
+that is configurable in the MIB variable
+.Va begemotNgTimeout .
+Other messages arriving while the functions are waiting for the response are
+queued and delivered on the next call to the module's idle function.
+.Bl -tag -width ".It Fn ng_output_node"
+.It Fn ng_dialog
+sends a control message along the given
+.Fa path
+and waits for a matching response.
+.It Fn ng_dialog_node
+sends a control message to the node with name
+.Fa node
+and waits for a matching response.
+.It Fn ng_dialog_id
+sends a control message to the node with id
+.Fa id
+and waits for a matching response.
+.El
+.Pp
+All three functions take the same arguments as the
+.Fn ng_output*
+functions.
+The functions return the response message in a buffer allocated by
+.Xr malloc 3
+or NULL in case of an error.
+The maximum size of the response buffer can be
+configured in the variable
+.Va begemotNgResBufSiz .
+.Pp
+A data message can be send with the function
+.Fn ng_send_data .
+This function takes the name of the
+.Va snmp_node Ns 's
+hook through which to send the data, a pointer to the message buffer and
+the size of the message.
+It returns -1 if an error happens.
+.Ss ASYNCHRONOUS CONTROL AND DATA MESSAGES
+A module can register functions to asynchronously receive control and data
+message.
+.Pp
+The function
+.Fn ng_register_cookie
+registers a control message receive function.
+If a control message is
+received, that is not consumed by the dialog functions, the list of registered
+control message receive functions is scanned.
+If the cookie in the received
+message is the same as the
+.Fa cookie
+argument to the
+.Fn ng_register_cookie
+call and the
+.Fa id
+argument to the
+.Fn ng_register_cookie
+call was either 0 or equals the node id which sent the control message, the
+handler function
+.Fa func
+is called with a pointer to the received message, the hook on which the
+message was received (or NULL if it was received not on a hook), the id
+of the sender's node and the
+.Fa uarg
+argument of the registration call.
+The handler function should not modify
+the contents of the message, because more than one function may be registered
+to the same cookie and node id.
+.Pp
+A control message registration can be undone by calling
+.Fn ng_unregister_cookie
+with the return value of the registration call.
+If an error occurs while registering,
+.Fn ng_register_cookie
+returns NULL.
+.Pp
+A module can call
+.Fn ng_register_hook
+to register a callback for data messages on one of the
+.Va snmp_node Ns 's
+hooks.
+If a data message is received on that hook, the callback function
+.Fa func
+is called with the hook name, a pointer to the data message, the size of
+the message and the argument
+.Fa uarg
+to the registration function.
+The message should be treated as read-only.
+A data message registration can be undone by calling
+.Fn ng_unregister_hook
+with the return value of the registration call.
+If an error occurs while registering,
+.Fn ng_register_hook
+returns NULL.
+.Pp
+The function
+.Fn ng_unregister_module
+removes all control and data registrations for that module.
+.Ss FINDING NODES AND NODE CHARACTERISTICS
+.Pp
+The function
+.Fn ng_node_id
+returns the id of the node addressed by
+.Fa path
+or 0 if the node does not exists.
+.Pp
+The function
+.Fn ng_node_id_node
+returns the id of the node with name
+.Fa node
+or 0 if the node does not exist.
+.Pp
+The function
+.Fn ng_node_node
+retrieves the name of the node with id
+.Fa id
+and writes it to the buffer pointed to by
+.Fa name .
+This buffer should be at least
+.Li NG_NODESIZ
+bytes long.
+The function returns the node id or 0 if the
+node is not found
+.Pp
+The function
+.Fn ng_node_type
+retrieves the name of the node with id
+.Fa id
+and writes it to the buffer pointed to by
+.Fa type .
+This buffer should be at least
+.Li NG_TYPESIZ
+bytes long.
+The function returns the node id or 0 if the
+node is not found.
+.Pp
+The function
+.Fn ng_peer_hook_id
+writes the name of the peer hook of the hook
+.Fa hook
+on the node with
+.Fa id
+to the buffer pointed to by
+.Fa peer_hook .
+The buffer should be at least
+.Li NG_HOOKSIZ
+bytes long.
+The function returns 0 if the node and the hook is found, -1
+otherwise.
+The function skips intermediate tee nodes (see
+.Xr ng_tee 4 ) .
+.Pp
+The function
+.Fn ng_next_node_id
+returns the node id of the peer node that is on the other side of hook
+.Fa hook
+of node
+.Fa id .
+If
+.Fa type
+is not NULL, the function checks, that the peer node's type is
+.Fa type .
+The function skips intermediate tee nodes (see
+.Xr ng_tee 4 ) .
+It returns the node id of the peer node or 0 if an error occurs or the
+types do not match.
+.Ss CHANGING THE GRAPH
+A number of functions can be used to create or destroy nodes and hooks.
+.Pp
+The function
+.Fn ng_mkpeer_id
+creates a new node of type
+.Fa type
+whose hook
+.Fa peerhook
+will be connected to
+.Fa hook
+of node
+.Fa id .
+If
+.Fa name
+is not NULL the new node is named with this name.
+The function returns
+The node id of the new node or 0 if an error happens.
+.Pp
+The functions
+.Fn ng_connect_node
+and
+.Fn ng_connect_id
+make a new hook connecting
+.Fa ourhook
+of the modules socket node
+.Va snmp_node
+to
+.Fa peerhook
+of the node identified by id
+.Fa id
+or name
+.Fa node .
+The functions return 0 on success or -1 otherwise.
+.Pp
+The function
+.Fn ng_connect2_id
+connects hook
+.Fa ourhook
+of the node with id
+.Fa id
+to hook
+.Fa peerhook
+of the node with id
+.Fa peer .
+The functions return 0 on success or -1 otherwise.
+The function
+.Fn ng_connect2_tee_id does the same as
+.Fn ng_connect2_id
+except, that it puts an unnamed tee node between the two nodes.
+.Pp
+The function
+.Fn ng_rmhook
+removes hook
+.Fa hook
+on the module's
+.Va snmp_node .
+The function
+.Fn ng_rmhook_id
+removes hook
+.Fa hook
+on the node with id
+.Fa id .
+The function
+.Fn ng_rmhook_tee_id
+additionally shuts down all tee nodes between the node and the first non-tee
+peer.
+.Pp
+The function
+.Fn ng_shutdown_id
+destroys the given node.
+.Sh FILES
+.Bl -tag -width "XXXXXXXXX"
+.It Pa /usr/share/bsnmp/defs/netgraph_tree.def
+The description of the MIB tree implemented by
+.Nm .
+.It Pa /usr/share/bsnmp/mibs/BEGEMOT-NETGRAPH.txt
+This is the MIB that is implemented by this module.
+.El
+.Sh SEE ALSO
+.Xr gensnmptree 1 ,
+.Xr snmpmod 3
+.Sh AUTHORS
+.An Hartmut Brandt Aq harti@freebsd.org
diff --git a/usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.c b/usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.c
new file mode 100644
index 0000000..d9d136a
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.c
@@ -0,0 +1,1690 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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$
+ *
+ * Netgraph interface for SNMPd.
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <sys/socket.h>
+#include <sys/syslog.h>
+#include <sys/queue.h>
+#include <sys/sysctl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <netgraph.h>
+#include <bsnmp/snmpmod.h>
+#include "snmp_netgraph.h"
+#include "netgraph_tree.h"
+#include "netgraph_oid.h"
+
+/* maximum message size */
+#define RESBUFSIZ 20000
+
+/* default node name */
+#define NODENAME "NgSnmpd"
+
+/* my node Id */
+ng_ID_t snmp_node;
+u_char *snmp_nodename;
+
+/* the Object Resource registration index */
+static u_int reg_index;
+static const struct asn_oid oid_begemotNg = OIDX_begemotNg;
+
+/* configuration */
+/* this must be smaller than int32_t because the functions in libnetgraph
+ * falsely return an int */
+static size_t resbufsiz = RESBUFSIZ;
+static u_int timeout = 1000;
+static u_int debug_level;
+
+/* number of microseconds per clock tick */
+static struct clockinfo clockinfo;
+
+/* Csock buffers. Communication on the csock is asynchronuous. This means
+ * if we wait for a specific response, we may get other messages. Put these
+ * into a queue and execute them when we are idle. */
+struct csock_buf {
+ STAILQ_ENTRY(csock_buf) link;
+ struct ng_mesg *mesg;
+ char path[NG_PATHSIZ];
+};
+static STAILQ_HEAD(, csock_buf) csock_bufs =
+ STAILQ_HEAD_INITIALIZER(csock_bufs);
+
+/*
+ * We dispatch unsolicieted messages by node cookies and ids.
+ * So we must keep a list of hook names and dispatch functions.
+ */
+struct msgreg {
+ u_int32_t cookie;
+ ng_ID_t id;
+ ng_cookie_f *func;
+ void *arg;
+ const struct lmodule *mod;
+ SLIST_ENTRY(msgreg) link;
+};
+static SLIST_HEAD(, msgreg) msgreg_list =
+ SLIST_HEAD_INITIALIZER(msgreg_list);
+
+/*
+ * Data messages are dispatched by hook names.
+ */
+struct datareg {
+ char hook[NG_HOOKSIZ];
+ ng_hook_f *func;
+ void *arg;
+ const struct lmodule *mod;
+ SLIST_ENTRY(datareg) link;
+};
+static SLIST_HEAD(, datareg) datareg_list =
+ SLIST_HEAD_INITIALIZER(datareg_list);
+
+/* the netgraph sockets */
+static int csock, dsock;
+static void *csock_fd, *dsock_fd;
+
+/* our module handle */
+static struct lmodule *module;
+
+/* statistics */
+static u_int32_t stats[LEAF_begemotNgTooLargeDatas+1];
+
+/* netgraph type list */
+struct ngtype {
+ char name[NG_TYPESIZ];
+ struct asn_oid index;
+ TAILQ_ENTRY(ngtype) link;
+};
+TAILQ_HEAD(ngtype_list, ngtype);
+
+static struct ngtype_list ngtype_list;
+static uint64_t ngtype_tick;
+
+
+/*
+ * Register a function to receive unsolicited messages
+ */
+void *
+ng_register_cookie(const struct lmodule *mod, u_int32_t cookie, ng_ID_t id,
+ ng_cookie_f *func, void *arg)
+{
+ struct msgreg *d;
+
+ if ((d = malloc(sizeof(*d))) == NULL)
+ return (NULL);
+
+ d->cookie = cookie;
+ d->id = id;
+ d->func = func;
+ d->arg = arg;
+ d->mod = mod;
+
+ SLIST_INSERT_HEAD(&msgreg_list, d, link);
+
+ return (d);
+}
+
+/*
+ * Remove a registration.
+ */
+void
+ng_unregister_cookie(void *dd)
+{
+ struct msgreg *d = dd;
+
+ SLIST_REMOVE(&msgreg_list, d, msgreg, link);
+ free(d);
+}
+
+/*
+ * Register a function for hook data.
+ */
+void *
+ng_register_hook(const struct lmodule *mod, const char *hook,
+ ng_hook_f *func, void *arg)
+{
+ struct datareg *d;
+
+ if ((d = malloc(sizeof(*d))) == NULL)
+ return (NULL);
+
+ strcpy(d->hook, hook);
+ d->func = func;
+ d->arg = arg;
+ d->mod = mod;
+
+ SLIST_INSERT_HEAD(&datareg_list, d, link);
+
+ return (d);
+}
+
+/*
+ * Unregister a hook function
+ */
+void
+ng_unregister_hook(void *dd)
+{
+ struct datareg *d = dd;
+
+ SLIST_REMOVE(&datareg_list, d, datareg, link);
+ free(d);
+}
+
+/*
+ * Unregister all hooks and cookies for that module. Note: doesn't disconnect
+ * any hooks!
+ */
+void
+ng_unregister_module(const struct lmodule *mod)
+{
+ struct msgreg *m, *m1;
+ struct datareg *d, *d1;
+
+ m = SLIST_FIRST(&msgreg_list);
+ while (m != NULL) {
+ m1 = SLIST_NEXT(m, link);
+ if (m->mod == mod) {
+ SLIST_REMOVE(&msgreg_list, m, msgreg, link);
+ free(m);
+ }
+ m = m1;
+ }
+
+ d = SLIST_FIRST(&datareg_list);
+ while (d != NULL) {
+ d1 = SLIST_NEXT(d, link);
+ if (d->mod == mod) {
+ SLIST_REMOVE(&datareg_list, d, datareg, link);
+ free(d);
+ }
+ d = d1;
+ }
+}
+
+/*
+ * Dispatch a message to the correct module and delete it. More than one
+ * module can get a message.
+ */
+static void
+csock_handle(struct ng_mesg *mesg, const char *path)
+{
+ struct msgreg *d, *d1;
+ u_int id;
+ int len;
+
+ if (sscanf(path, "[%x]:%n", &id, &len) != 1 ||
+ (u_int)len != strlen(path)) {
+ syslog(LOG_ERR, "cannot parse message path '%s'", path);
+ id = 0;
+ }
+
+ d = SLIST_FIRST(&msgreg_list);
+ while (d != NULL) {
+ d1 = SLIST_NEXT(d, link);
+ if (d->cookie == mesg->header.typecookie &&
+ (d->id == 0 || d->id == id || id == 0))
+ (*d->func)(mesg, path, id, d->arg);
+ d = d1;
+ }
+ free(mesg);
+}
+
+/*
+ * Input from the control socket.
+ */
+static struct ng_mesg *
+csock_read(char *path)
+{
+ struct ng_mesg *mesg;
+ int ret, err;
+
+ if ((mesg = malloc(resbufsiz + 1)) == NULL) {
+ stats[LEAF_begemotNgNoMems]++;
+ syslog(LOG_CRIT, "out of memory");
+ errno = ENOMEM;
+ return (NULL);
+ }
+ if ((ret = NgRecvMsg(csock, mesg, resbufsiz + 1, path)) < 0) {
+ err = errno;
+ free(mesg);
+ if (errno == EWOULDBLOCK) {
+ errno = err;
+ return (NULL);
+ }
+ stats[LEAF_begemotNgMsgReadErrs]++;
+ syslog(LOG_WARNING, "read from csock: %m");
+ errno = err;
+ return (NULL);
+ }
+ if (ret == 0) {
+ syslog(LOG_DEBUG, "node closed -- exiting");
+ exit(0);
+ }
+ if ((size_t)ret > resbufsiz) {
+ stats[LEAF_begemotNgTooLargeMsgs]++;
+ syslog(LOG_WARNING, "ng message too large");
+ free(mesg);
+ errno = EFBIG;
+ return (NULL);
+ }
+ return (mesg);
+}
+
+static void
+csock_input(int fd __unused, void *udata __unused)
+{
+ struct ng_mesg *mesg;
+ char path[NG_PATHSIZ];
+
+ if ((mesg = csock_read(path)) == NULL)
+ return;
+
+ csock_handle(mesg, path);
+}
+
+/*
+ * Write a message to a node.
+ */
+int
+ng_output(const char *path, u_int cookie, u_int opcode,
+ const void *arg, size_t arglen)
+{
+ return (NgSendMsg(csock, path, (int)cookie, (int)opcode, arg, arglen));
+}
+int
+ng_output_node(const char *node, u_int cookie, u_int opcode,
+ const void *arg, size_t arglen)
+{
+ char path[NG_PATHSIZ];
+
+ sprintf(path, "%s:", node);
+ return (ng_output(path, cookie, opcode, arg, arglen));
+}
+int
+ng_output_id(ng_ID_t node, u_int cookie, u_int opcode,
+ const void *arg, size_t arglen)
+{
+ char path[NG_PATHSIZ];
+
+ sprintf(path, "[%x]:", node);
+ return (ng_output(path, cookie, opcode, arg, arglen));
+}
+
+
+
+/*
+ * Execute a synchronuous dialog with the csock. All message we receive, that
+ * do not match our request, are queue until the next call to the IDLE function.
+ */
+struct ng_mesg *
+ng_dialog(const char *path, u_int cookie, u_int opcode,
+ const void *arg, size_t arglen)
+{
+ int token, err;
+ struct ng_mesg *mesg;
+ char rpath[NG_PATHSIZ];
+ struct csock_buf *b;
+ struct timeval end, tv;
+
+ if ((token = ng_output(path, cookie, opcode, arg, arglen)) < 0)
+ return (NULL);
+
+ if (csock_fd)
+ fd_suspend(csock_fd);
+
+ gettimeofday(&end, NULL);
+ tv.tv_sec = timeout / 1000;
+ tv.tv_usec = (timeout % 1000) * 1000;
+ timeradd(&end, &tv, &end);
+ for (;;) {
+ mesg = NULL;
+ gettimeofday(&tv, NULL);
+ if (timercmp(&tv, &end, >=)) {
+ block:
+ syslog(LOG_WARNING, "no response for request %u/%u",
+ cookie, opcode);
+ errno = EWOULDBLOCK;
+ break;
+ }
+ timersub(&end, &tv, &tv);
+ if (tv.tv_sec == 0 && tv.tv_usec < clockinfo.tick)
+ goto block;
+
+ if (setsockopt(csock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1)
+ syslog(LOG_WARNING, "setsockopt(SO_RCVTIMEO): %m");
+ if ((mesg = csock_read(rpath)) == NULL) {
+ if (errno == EWOULDBLOCK)
+ continue;
+ break;
+ }
+ if (mesg->header.token == (u_int)token)
+ break;
+ if ((b = malloc(sizeof(*b))) == NULL) {
+ stats[LEAF_begemotNgNoMems]++;
+ syslog(LOG_ERR, "out of memory");
+ free(mesg);
+ continue;
+ }
+ b->mesg = mesg;
+ strcpy(b->path, rpath);
+ STAILQ_INSERT_TAIL(&csock_bufs, b, link);
+ }
+
+ tv.tv_sec = 0;
+ tv.tv_usec = 0;
+ if (setsockopt(csock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) == -1)
+ syslog(LOG_WARNING, "setsockopt(SO_RCVTIMEO,0): %m");
+
+ if (csock_fd) {
+ err = errno;
+ fd_resume(csock_fd);
+ errno = err;
+ }
+
+ return (mesg);
+}
+struct ng_mesg *
+ng_dialog_node(const char *node, u_int cookie, u_int opcode,
+ const void *arg, size_t arglen)
+{
+ char path[NG_PATHSIZ];
+
+ sprintf(path, "%s:", node);
+ return (ng_dialog(path, cookie, opcode, arg, arglen));
+}
+struct ng_mesg *
+ng_dialog_id(ng_ID_t id, u_int cookie, u_int opcode,
+ const void *arg, size_t arglen)
+{
+ char path[NG_PATHSIZ];
+
+ sprintf(path, "[%x]:", id);
+ return (ng_dialog(path, cookie, opcode, arg, arglen));
+}
+
+
+/*
+ * Send a data message to a given hook.
+ */
+int
+ng_send_data(const char *hook, const void *sndbuf, size_t sndlen)
+{
+ return (NgSendData(dsock, hook, sndbuf, sndlen));
+}
+
+/*
+ * Input from a data socket. Dispatch to the function for that hook.
+ */
+static void
+dsock_input(int fd __unused, void *udata __unused)
+{
+ u_char *resbuf, embuf[100];
+ ssize_t len;
+ char hook[NG_HOOKSIZ];
+ struct datareg *d, *d1;
+
+ if ((resbuf = malloc(resbufsiz + 1)) == NULL) {
+ stats[LEAF_begemotNgNoMems]++;
+ syslog(LOG_CRIT, "out of memory");
+ (void)NgRecvData(fd, embuf, sizeof(embuf), hook);
+ errno = ENOMEM;
+ return;
+ }
+ if ((len = NgRecvData(fd, resbuf, resbufsiz + 1, hook)) == -1) {
+ stats[LEAF_begemotNgDataReadErrs]++;
+ syslog(LOG_ERR, "reading message: %m");
+ free(resbuf);
+ return;
+ }
+ if (len == 0) {
+ free(resbuf);
+ return;
+ }
+ if ((size_t)len == resbufsiz + 1) {
+ stats[LEAF_begemotNgTooLargeDatas]++;
+ syslog(LOG_WARNING, "message too long");
+ free(resbuf);
+ return;
+ }
+
+ /*
+ * Dispatch message. Maybe dispatched to more than one function.
+ */
+ d = SLIST_FIRST(&datareg_list);
+ while (d != NULL) {
+ d1 = SLIST_NEXT(d, link);
+ if (strcmp(hook, d->hook) == 0)
+ (*d->func)(hook, resbuf, len, d->arg);
+ d = d1;
+ }
+
+ free(resbuf);
+}
+
+/*
+ * The SNMP daemon is about to wait for an event. Look whether we have
+ * netgraph messages waiting. If yes, drain the queue.
+ */
+static void
+ng_idle(void)
+{
+ struct csock_buf *b;
+
+ /* execute waiting csock_bufs */
+ while ((b = STAILQ_FIRST(&csock_bufs)) != NULL) {
+ STAILQ_REMOVE_HEAD(&csock_bufs, link);
+ csock_handle(b->mesg, b->path);
+ free(b);
+ }
+}
+
+/*
+ * Called when the module is loaded. Returning a non-zero value means,
+ * rejecting the initialisation.
+ *
+ * We make the netgraph socket.
+ */
+static int
+ng_init(struct lmodule *mod, int argc, char *argv[])
+{
+ int name[2];
+ size_t len;
+
+ module = mod;
+
+ if (argc == 0) {
+ if ((snmp_nodename = malloc(strlen(NODENAME) + 1)) == NULL)
+ return (ENOMEM);
+ strcpy(snmp_nodename, NODENAME);
+ } else {
+ if ((snmp_nodename = malloc(NG_NODESIZ)) == NULL)
+ return (ENOMEM);
+ strlcpy(snmp_nodename, argv[0], NG_NODESIZ);
+ }
+
+ /* fetch clockinfo (for the number of microseconds per tick) */
+ name[0] = CTL_KERN;
+ name[1] = KERN_CLOCKRATE;
+ len = sizeof(clockinfo);
+ if (sysctl(name, 2, &clockinfo, &len, NULL, 0) == -1)
+ return (errno);
+
+ TAILQ_INIT(&ngtype_list);
+
+ return (0);
+}
+
+/*
+ * Get the node Id/name/type of a node.
+ */
+ng_ID_t
+ng_node_id(const char *path)
+{
+ struct ng_mesg *resp;
+ ng_ID_t id;
+
+ if ((resp = ng_dialog(path, NGM_GENERIC_COOKIE, NGM_NODEINFO,
+ NULL, 0)) == NULL)
+ return (0);
+ id = ((struct nodeinfo *)(void *)resp->data)->id;
+ free(resp);
+ return (id);
+}
+ng_ID_t
+ng_node_id_node(const char *node)
+{
+ struct ng_mesg *resp;
+ ng_ID_t id;
+
+ if ((resp = ng_dialog_node(node, NGM_GENERIC_COOKIE, NGM_NODEINFO,
+ NULL, 0)) == NULL)
+ return (0);
+ id = ((struct nodeinfo *)(void *)resp->data)->id;
+ free(resp);
+ return (id);
+}
+ng_ID_t
+ng_node_name(ng_ID_t id, char *name)
+{
+ struct ng_mesg *resp;
+
+ if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE, NGM_NODEINFO,
+ NULL, 0)) == NULL)
+ return (0);
+ strcpy(name, ((struct nodeinfo *)(void *)resp->data)->name);
+ free(resp);
+ return (id);
+
+}
+ng_ID_t
+ng_node_type(ng_ID_t id, char *type)
+{
+ struct ng_mesg *resp;
+
+ if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE, NGM_NODEINFO,
+ NULL, 0)) == NULL)
+ return (0);
+ strcpy(type, ((struct nodeinfo *)(void *)resp->data)->type);
+ free(resp);
+ return (id);
+}
+
+/*
+ * Connect our node to some other node
+ */
+int
+ng_connect_node(const char *node, const char *ourhook, const char *peerhook)
+{
+ struct ngm_connect conn;
+
+ snprintf(conn.path, NG_PATHSIZ, "%s:", node);
+ strlcpy(conn.ourhook, ourhook, NG_HOOKSIZ);
+ strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ);
+ return (NgSendMsg(csock, ".:",
+ NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn)));
+}
+int
+ng_connect_id(ng_ID_t id, const char *ourhook, const char *peerhook)
+{
+ struct ngm_connect conn;
+
+ snprintf(conn.path, NG_PATHSIZ, "[%x]:", id);
+ strlcpy(conn.ourhook, ourhook, NG_HOOKSIZ);
+ strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ);
+ return (NgSendMsg(csock, ".:",
+ NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn)));
+}
+
+int
+ng_connect2_id(ng_ID_t id, ng_ID_t peer, const char *ourhook,
+ const char *peerhook)
+{
+ struct ngm_connect conn;
+ char path[NG_PATHSIZ];
+
+ snprintf(path, NG_PATHSIZ, "[%x]:", id);
+
+ snprintf(conn.path, NG_PATHSIZ, "[%x]:", peer);
+ strlcpy(conn.ourhook, ourhook, NG_HOOKSIZ);
+ strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ);
+ return (NgSendMsg(csock, path,
+ NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn)));
+}
+
+int
+ng_connect2_tee_id(ng_ID_t id, ng_ID_t peer, const char *ourhook,
+ const char *peerhook)
+{
+ struct ngm_connect conn;
+ char path[NG_PATHSIZ];
+ ng_ID_t tee;
+
+ if ((tee = ng_mkpeer_id(id, NULL, "tee", ourhook, "left")) == 0)
+ return (-1);
+
+ snprintf(path, NG_PATHSIZ, "[%x]:", tee);
+
+ snprintf(conn.path, NG_PATHSIZ, "[%x]:", peer);
+ strlcpy(conn.ourhook, "right", NG_HOOKSIZ);
+ strlcpy(conn.peerhook, peerhook, NG_HOOKSIZ);
+ return (NgSendMsg(csock, path,
+ NGM_GENERIC_COOKIE, NGM_CONNECT, &conn, sizeof(conn)));
+}
+
+/*
+ * Ensure that a node of type 'type' is connected to 'hook' of 'node'
+ * and return its node id. tee nodes between node and the target node
+ * are skipped. If the type is wrong, or the hook is a dead-end return 0.
+ * If type is NULL, it is not checked.
+ */
+static ng_ID_t
+ng_next_node_id_internal(ng_ID_t node, const char *type, const char *hook,
+ int skip_tee)
+{
+ struct ng_mesg *resp;
+ struct hooklist *hooklist;
+ u_int i;
+
+ if ((resp = ng_dialog_id(node, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,
+ NULL, 0)) == NULL) {
+ syslog(LOG_ERR, "get hook list: %m");
+ exit(1);
+ }
+ hooklist = (struct hooklist *)(void *)resp->data;
+
+ for (i = 0; i < hooklist->nodeinfo.hooks; i++)
+ if (strcmp(hooklist->link[i].ourhook, hook) == 0)
+ break;
+
+ if (i == hooklist->nodeinfo.hooks) {
+ free(resp);
+ return (0);
+ }
+
+ node = hooklist->link[i].nodeinfo.id;
+
+ if (skip_tee && strcmp(hooklist->link[i].nodeinfo.type, "tee") == 0) {
+ if (strcmp(hooklist->link[i].peerhook, "left") == 0)
+ node = ng_next_node_id(node, type, "right");
+ else if (strcmp(hooklist->link[i].peerhook, "right") == 0)
+ node = ng_next_node_id(node, type, "left");
+ else if (type != NULL &&
+ strcmp(hooklist->link[i].nodeinfo.type, type) != 0)
+ node = 0;
+
+ } else if (type != NULL &&
+ strcmp(hooklist->link[i].nodeinfo.type, type) != 0)
+ node = 0;
+
+ free(resp);
+
+ return (node);
+}
+
+/*
+ * Ensure that a node of type 'type' is connected to 'hook' of 'node'
+ * and return its node id. tee nodes between node and the target node
+ * are skipped. If the type is wrong, or the hook is a dead-end return 0.
+ * If type is NULL, it is not checked.
+ */
+ng_ID_t
+ng_next_node_id(ng_ID_t node, const char *type, const char *hook)
+{
+ return (ng_next_node_id_internal(node, type, hook, 1));
+}
+
+ng_ID_t
+ng_mkpeer_id(ng_ID_t id, const char *nodename, const char *type,
+ const char *hook, const char *peerhook)
+{
+ char path[NG_PATHSIZ];
+ struct ngm_mkpeer mkpeer;
+ struct ngm_name name;
+
+ strlcpy(mkpeer.type, type, NG_TYPESIZ);
+ strlcpy(mkpeer.ourhook, hook, NG_HOOKSIZ);
+ strlcpy(mkpeer.peerhook, peerhook, NG_HOOKSIZ);
+
+ sprintf(path, "[%x]:", id);
+ if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_MKPEER,
+ &mkpeer, sizeof(mkpeer)) == -1)
+ return (0);
+
+ if ((id = ng_next_node_id_internal(id, NULL, hook, 0)) == 0)
+ return (0);
+
+ if (nodename != NULL) {
+ strcpy(name.name, nodename);
+ sprintf(path, "[%x]:", id);
+ if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_NAME,
+ &name, sizeof(name)) == -1)
+ return (0);
+ }
+ return (id);
+}
+
+/*
+ * SHutdown node
+ */
+int
+ng_shutdown_id(ng_ID_t id)
+{
+ char path[NG_PATHSIZ];
+
+ snprintf(path, NG_PATHSIZ, "[%x]:", id);
+ return (NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
+ NGM_SHUTDOWN, NULL, 0));
+}
+
+/*
+ * Disconnect one of our hooks
+ */
+int
+ng_rmhook(const char *ourhook)
+{
+ struct ngm_rmhook rmhook;
+
+ strlcpy(rmhook.ourhook, ourhook, NG_HOOKSIZ);
+ return (NgSendMsg(csock, ".:",
+ NGM_GENERIC_COOKIE, NGM_RMHOOK, &rmhook, sizeof(rmhook)));
+}
+
+/*
+ * Disconnect a hook of a node
+ */
+int
+ng_rmhook_id(ng_ID_t id, const char *hook)
+{
+ struct ngm_rmhook rmhook;
+ char path[NG_PATHSIZ];
+
+ strlcpy(rmhook.ourhook, hook, NG_HOOKSIZ);
+ snprintf(path, NG_PATHSIZ, "[%x]:", id);
+ return (NgSendMsg(csock, path,
+ NGM_GENERIC_COOKIE, NGM_RMHOOK, &rmhook, sizeof(rmhook)));
+}
+
+/*
+ * Disconnect a hook and shutdown all tee nodes that were connected to that
+ * hook.
+ */
+int
+ng_rmhook_tee_id(ng_ID_t node, const char *hook)
+{
+ struct ng_mesg *resp;
+ struct hooklist *hooklist;
+ u_int i;
+ int first = 1;
+ ng_ID_t next_node;
+ const char *next_hook;
+
+ again:
+ /* if we have just shutdown a tee node, which had no other hooks
+ * connected, the node id may already be wrong here. */
+ if ((resp = ng_dialog_id(node, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,
+ NULL, 0)) == NULL)
+ return (0);
+
+ hooklist = (struct hooklist *)(void *)resp->data;
+
+ for (i = 0; i < hooklist->nodeinfo.hooks; i++)
+ if (strcmp(hooklist->link[i].ourhook, hook) == 0)
+ break;
+
+ if (i == hooklist->nodeinfo.hooks) {
+ free(resp);
+ return (0);
+ }
+
+ next_node = 0;
+ next_hook = NULL;
+ if (strcmp(hooklist->link[i].nodeinfo.type, "tee") == 0) {
+ if (strcmp(hooklist->link[i].peerhook, "left") == 0) {
+ next_node = hooklist->link[i].nodeinfo.id;
+ next_hook = "right";
+ } else if (strcmp(hooklist->link[i].peerhook, "right") == 0) {
+ next_node = hooklist->link[i].nodeinfo.id;
+ next_hook = "left";
+ }
+ }
+ free(resp);
+
+ if (first) {
+ ng_rmhook_id(node, hook);
+ first = 0;
+ } else {
+ ng_shutdown_id(node);
+ }
+ if ((node = next_node) == 0)
+ return (0);
+ hook = next_hook;
+
+ goto again;
+}
+
+/*
+ * Get the peer hook of a hook on a given node. Skip any tee nodes in between
+ */
+int
+ng_peer_hook_id(ng_ID_t node, const char *hook, char *peerhook)
+{
+ struct ng_mesg *resp;
+ struct hooklist *hooklist;
+ u_int i;
+ int ret;
+
+ if ((resp = ng_dialog_id(node, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,
+ NULL, 0)) == NULL) {
+ syslog(LOG_ERR, "get hook list: %m");
+ exit(1);
+ }
+ hooklist = (struct hooklist *)(void *)resp->data;
+
+ for (i = 0; i < hooklist->nodeinfo.hooks; i++)
+ if (strcmp(hooklist->link[i].ourhook, hook) == 0)
+ break;
+
+ if (i == hooklist->nodeinfo.hooks) {
+ free(resp);
+ return (-1);
+ }
+
+ node = hooklist->link[i].nodeinfo.id;
+
+ ret = 0;
+ if (strcmp(hooklist->link[i].nodeinfo.type, "tee") == 0) {
+ if (strcmp(hooklist->link[i].peerhook, "left") == 0)
+ ret = ng_peer_hook_id(node, "right", peerhook);
+ else if (strcmp(hooklist->link[i].peerhook, "right") == 0)
+ ret = ng_peer_hook_id(node, "left", peerhook);
+ else
+ strcpy(peerhook, hooklist->link[i].peerhook);
+
+ } else
+ strcpy(peerhook, hooklist->link[i].peerhook);
+
+ free(resp);
+
+ return (ret);
+}
+
+
+/*
+ * Now the module is started. Select on the sockets, so that we can get
+ * unsolicited input.
+ */
+static void
+ng_start(void)
+{
+ if (snmp_node == 0) {
+ if (NgMkSockNode(snmp_nodename, &csock, &dsock) < 0) {
+ syslog(LOG_ERR, "NgMkSockNode: %m");
+ exit(1);
+ }
+ snmp_node = ng_node_id(".:");
+ }
+
+ if ((csock_fd = fd_select(csock, csock_input, NULL, module)) == NULL) {
+ syslog(LOG_ERR, "fd_select failed on csock: %m");
+ return;
+ }
+ if ((dsock_fd = fd_select(dsock, dsock_input, NULL, module)) == NULL) {
+ syslog(LOG_ERR, "fd_select failed on dsock: %m");
+ return;
+ }
+
+ reg_index = or_register(&oid_begemotNg,
+ "The MIB for the NetGraph access module for SNMP.", module);
+}
+
+/*
+ * Called, when the module is to be unloaded after it was successfully loaded
+ */
+static int
+ng_fini(void)
+{
+ struct ngtype *t;
+
+ while ((t = TAILQ_FIRST(&ngtype_list)) != NULL) {
+ TAILQ_REMOVE(&ngtype_list, t, link);
+ free(t);
+ }
+
+ if (csock_fd != NULL)
+ fd_deselect(csock_fd);
+ (void)close(csock);
+
+ if (dsock_fd != NULL)
+ fd_deselect(dsock_fd);
+ (void)close(dsock);
+
+ free(snmp_nodename);
+
+ or_unregister(reg_index);
+
+ return (0);
+}
+
+const struct snmp_module config = {
+ "This module implements access to the netgraph sub-system",
+ ng_init,
+ ng_fini,
+ ng_idle,
+ NULL,
+ NULL,
+ ng_start,
+ NULL,
+ netgraph_ctree,
+ netgraph_CTREE_SIZE,
+ NULL
+};
+
+int
+op_ng_config(struct snmp_context *ctx, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op op)
+{
+ asn_subid_t which = value->var.subs[sub - 1];
+ int ret;
+
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ abort();
+
+ case SNMP_OP_GET:
+ /*
+ * Come here for GET, GETNEXT and COMMIT
+ */
+ switch (which) {
+
+ case LEAF_begemotNgControlNodeName:
+ return (string_get(value, snmp_nodename, -1));
+
+ case LEAF_begemotNgResBufSiz:
+ value->v.integer = resbufsiz;
+ break;
+
+ case LEAF_begemotNgTimeout:
+ value->v.integer = timeout;
+ break;
+
+ case LEAF_begemotNgDebugLevel:
+ value->v.uint32 = debug_level;
+ break;
+
+ default:
+ abort();
+ }
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_SET:
+ switch (which) {
+
+ case LEAF_begemotNgControlNodeName:
+ /* only at initialisation */
+ if (community != COMM_INITIALIZE)
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ if (snmp_node != 0)
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ if ((ret = string_save(value, ctx, -1, &snmp_nodename))
+ != SNMP_ERR_NOERROR)
+ return (ret);
+
+ if (NgMkSockNode(snmp_nodename, &csock, &dsock) < 0) {
+ syslog(LOG_ERR, "NgMkSockNode: %m");
+ string_rollback(ctx, &snmp_nodename);
+ return (SNMP_ERR_GENERR);
+ }
+ snmp_node = ng_node_id(".:");
+
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotNgResBufSiz:
+ ctx->scratch->int1 = resbufsiz;
+ if (value->v.integer < 1024 ||
+ value->v.integer > 0x10000)
+ return (SNMP_ERR_WRONG_VALUE);
+ resbufsiz = value->v.integer;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotNgTimeout:
+ ctx->scratch->int1 = timeout;
+ if (value->v.integer < 10 ||
+ value->v.integer > 10000)
+ return (SNMP_ERR_WRONG_VALUE);
+ timeout = value->v.integer;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotNgDebugLevel:
+ ctx->scratch->int1 = debug_level;
+ debug_level = value->v.uint32;
+ NgSetDebug(debug_level);
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+
+ case SNMP_OP_ROLLBACK:
+ switch (which) {
+
+ case LEAF_begemotNgControlNodeName:
+ string_rollback(ctx, &snmp_nodename);
+ close(csock);
+ close(dsock);
+ snmp_node = 0;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotNgResBufSiz:
+ resbufsiz = ctx->scratch->int1;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotNgTimeout:
+ timeout = ctx->scratch->int1;
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotNgDebugLevel:
+ debug_level = ctx->scratch->int1;
+ NgSetDebug(debug_level);
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+
+ case SNMP_OP_COMMIT:
+ switch (which) {
+
+ case LEAF_begemotNgControlNodeName:
+ string_commit(ctx);
+ return (SNMP_ERR_NOERROR);
+
+ case LEAF_begemotNgResBufSiz:
+ case LEAF_begemotNgTimeout:
+ case LEAF_begemotNgDebugLevel:
+ return (SNMP_ERR_NOERROR);
+ }
+ abort();
+ }
+ abort();
+}
+
+int
+op_ng_stats(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op op)
+{
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ abort();
+
+ case SNMP_OP_GET:
+ value->v.uint32 = stats[value->var.subs[sub - 1] - 1];
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_SET:
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ abort();
+ }
+ abort();
+}
+
+/*
+ * Netgraph type table
+ */
+static int
+fetch_types(void)
+{
+ struct ngtype *t;
+ struct typelist *typelist;
+ struct ng_mesg *resp;
+ u_int u, i;
+
+ if (this_tick <= ngtype_tick)
+ return (0);
+
+ while ((t = TAILQ_FIRST(&ngtype_list)) != NULL) {
+ TAILQ_REMOVE(&ngtype_list, t, link);
+ free(t);
+ }
+
+ if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE,
+ NGM_LISTTYPES, NULL, 0)) == NULL)
+ return (SNMP_ERR_GENERR);
+ typelist = (struct typelist *)(void *)resp->data;
+
+ for (u = 0; u < typelist->numtypes; u++) {
+ if ((t = malloc(sizeof(*t))) == NULL) {
+ free(resp);
+ return (SNMP_ERR_GENERR);
+ }
+ strcpy(t->name, typelist->typeinfo[u].type_name);
+ t->index.subs[0] = strlen(t->name);
+ t->index.len = t->index.subs[0] + 1;
+ for (i = 0; i < t->index.subs[0]; i++)
+ t->index.subs[i + 1] = t->name[i];
+
+ INSERT_OBJECT_OID(t, &ngtype_list);
+ }
+
+ ngtype_tick = this_tick;
+
+ free(resp);
+ return (0);
+}
+
+/*
+ * Try to load the netgraph type with the given name. We assume, that
+ * type 'type' is implemented in the kernel module 'ng_type'.
+ */
+static int
+ngtype_load(const u_char *name, size_t namelen)
+{
+ char *mod;
+ int ret;
+
+ if ((mod = malloc(namelen + 4)) == NULL)
+ return (-1);
+ strcpy(mod, "ng_");
+ strncpy(mod + 3, name, namelen);
+ mod[namelen + 3] = '\0';
+
+ ret = kldload(mod);
+ free(mod);
+ return (ret);
+}
+
+/*
+ * Unload a netgraph type.
+ */
+static int
+ngtype_unload(const u_char *name, size_t namelen)
+{
+ char *mod;
+ int id;
+
+ if ((mod = malloc(namelen + 4)) == NULL)
+ return (-1);
+ strcpy(mod, "ng_");
+ strncpy(mod + 3, name, namelen);
+ mod[namelen + 3] = '\0';
+
+ if ((id = kldfind(mod)) == -1) {
+ free(mod);
+ return (-1);
+ }
+ free(mod);
+ return (kldunload(id));
+}
+
+int
+op_ng_type(struct snmp_context *ctx, struct snmp_value *value,
+ u_int sub, u_int iidx, enum snmp_op op)
+{
+ asn_subid_t which = value->var.subs[sub - 1];
+ struct ngtype *t;
+ u_char *name;
+ size_t namelen;
+ int status = 1;
+ int ret;
+
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ if ((ret = fetch_types()) != 0)
+ return (ret);
+ if ((t = NEXT_OBJECT_OID(&ngtype_list, &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ index_append(&value->var, sub, &t->index);
+ break;
+
+ case SNMP_OP_GET:
+ if ((ret = fetch_types()) != 0)
+ return (ret);
+ if ((t = FIND_OBJECT_OID(&ngtype_list, &value->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ break;
+
+ case SNMP_OP_SET:
+ if (index_decode(&value->var, sub, iidx, &name, &namelen))
+ return (SNMP_ERR_NO_CREATION);
+ if (namelen == 0 || namelen >= NG_TYPESIZ) {
+ free(name);
+ return (SNMP_ERR_NO_CREATION);
+ }
+ if ((ret = fetch_types()) != 0) {
+ free(name);
+ return (ret);
+ }
+ t = FIND_OBJECT_OID(&ngtype_list, &value->var, sub);
+
+ if (which != LEAF_begemotNgTypeStatus) {
+ free(name);
+ if (t != NULL)
+ return (SNMP_ERR_NOT_WRITEABLE);
+ return (SNMP_ERR_NO_CREATION);
+ }
+ if (!TRUTH_OK(value->v.integer)) {
+ free(name);
+ return (SNMP_ERR_WRONG_VALUE);
+ }
+ ctx->scratch->int1 = TRUTH_GET(value->v.integer);
+ ctx->scratch->int1 |= (t != NULL) << 1;
+ ctx->scratch->ptr2 = name;
+ ctx->scratch->int2 = namelen;
+
+ if (t == NULL) {
+ /* type not loaded */
+ if (ctx->scratch->int1 & 1) {
+ /* request to load */
+ if (ngtype_load(name, namelen) == -1) {
+ free(name);
+ if (errno == ENOENT)
+ return (SNMP_ERR_INCONS_NAME);
+ else
+ return (SNMP_ERR_GENERR);
+ }
+ }
+ } else {
+ /* is type loaded */
+ if (!(ctx->scratch->int1 & 1)) {
+ /* request to unload */
+ if (ngtype_unload(name, namelen) == -1) {
+ free(name);
+ return (SNMP_ERR_GENERR);
+ }
+ }
+ }
+ return (SNMP_ERR_NOERROR);
+
+ case SNMP_OP_ROLLBACK:
+ ret = SNMP_ERR_NOERROR;
+ if (!(ctx->scratch->int1 & 2)) {
+ /* did not exist */
+ if (ctx->scratch->int1 & 1) {
+ /* request to load - unload */
+ if (ngtype_unload(ctx->scratch->ptr2,
+ ctx->scratch->int2) == -1)
+ ret = SNMP_ERR_UNDO_FAILED;
+ }
+ } else {
+ /* did exist */
+ if (!(ctx->scratch->int1 & 1)) {
+ /* request to unload - reload */
+ if (ngtype_load(ctx->scratch->ptr2,
+ ctx->scratch->int2) == -1)
+ ret = SNMP_ERR_UNDO_FAILED;
+ }
+ }
+ free(ctx->scratch->ptr2);
+ return (ret);
+
+ case SNMP_OP_COMMIT:
+ free(ctx->scratch->ptr2);
+ return (SNMP_ERR_NOERROR);
+
+ default:
+ abort();
+ }
+
+ /*
+ * Come here for GET and COMMIT
+ */
+ switch (which) {
+
+ case LEAF_begemotNgTypeStatus:
+ value->v.integer = status;
+ break;
+
+ default:
+ abort();
+ }
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Implement the node table
+ */
+static int
+find_node(const struct asn_oid *oid, u_int sub, struct nodeinfo *info)
+{
+ ng_ID_t id = oid->subs[sub];
+ struct ng_mesg *resp;
+
+ if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE, NGM_NODEINFO,
+ NULL, 0)) == NULL)
+ return (-1);
+
+ *info = *(struct nodeinfo *)(void *)resp->data;
+ free(resp);
+ return (0);
+}
+
+static int
+ncmp(const void *p1, const void *p2)
+{
+ const struct nodeinfo *i1 = p1;
+ const struct nodeinfo *i2 = p2;
+
+ if (i1->id < i2->id)
+ return (-1);
+ if (i1->id > i2->id)
+ return (+1);
+ return (0);
+}
+
+static int
+find_node_next(const struct asn_oid *oid, u_int sub, struct nodeinfo *info)
+{
+ u_int idxlen = oid->len - sub;
+ struct ng_mesg *resp;
+ struct namelist *list;
+ ng_ID_t id;
+ u_int i;
+
+ if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE, NGM_LISTNODES,
+ NULL, 0)) == NULL)
+ return (-1);
+ list = (struct namelist *)(void *)resp->data;
+
+ qsort(list->nodeinfo, list->numnames, sizeof(list->nodeinfo[0]), ncmp);
+
+ if (idxlen == 0) {
+ if (list->numnames == 0) {
+ free(resp);
+ return (-1);
+ }
+ *info = list->nodeinfo[0];
+ free(resp);
+ return (0);
+ }
+ id = oid->subs[sub];
+
+ for (i = 0; i < list->numnames; i++)
+ if (list->nodeinfo[i].id > id) {
+ *info = list->nodeinfo[i];
+ free(resp);
+ return (0);
+ }
+
+ free(resp);
+ return (-1);
+}
+
+int
+op_ng_node(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int iidx __unused, enum snmp_op op)
+{
+ asn_subid_t which = value->var.subs[sub - 1];
+ u_int idxlen = value->var.len - sub;
+ struct nodeinfo nodeinfo;
+
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ if (find_node_next(&value->var, sub, &nodeinfo) == -1)
+ return (SNMP_ERR_NOSUCHNAME);
+ value->var.len = sub + 1;
+ value->var.subs[sub] = nodeinfo.id;
+ break;
+
+ case SNMP_OP_GET:
+ if (idxlen != 1)
+ return (SNMP_ERR_NOSUCHNAME);
+ if (find_node(&value->var, sub, &nodeinfo) == -1)
+ return (SNMP_ERR_NOSUCHNAME);
+ break;
+
+ case SNMP_OP_SET:
+ if (idxlen != 1)
+ return (SNMP_ERR_NO_CREATION);
+ if (find_node(&value->var, sub, &nodeinfo) == -1)
+ return (SNMP_ERR_NO_CREATION);
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ default:
+ abort();
+ }
+
+ /*
+ * Come here for GET and COMMIT
+ */
+ switch (which) {
+
+ case LEAF_begemotNgNodeStatus:
+ value->v.integer = 1;
+ break;
+ case LEAF_begemotNgNodeName:
+ return (string_get(value, nodeinfo.name, -1));
+ case LEAF_begemotNgNodeType:
+ return (string_get(value, nodeinfo.type, -1));
+ case LEAF_begemotNgNodeHooks:
+ value->v.uint32 = nodeinfo.hooks;
+ break;
+
+ default:
+ abort();
+ }
+ return (SNMP_ERR_NOERROR);
+}
+
+/*
+ * Implement the hook table
+ */
+static int
+find_hook(int32_t id, const u_char *hook, size_t hooklen, struct linkinfo *info)
+{
+ struct ng_mesg *resp;
+ struct hooklist *list;
+ u_int i;
+
+ if ((resp = ng_dialog_id(id, NGM_GENERIC_COOKIE,
+ NGM_LISTHOOKS, NULL, 0)) == NULL)
+ return (-1);
+
+ list = (struct hooklist *)(void *)resp->data;
+
+ for (i = 0; i < list->nodeinfo.hooks; i++) {
+ if (strlen(list->link[i].ourhook) == hooklen &&
+ strncmp(list->link[i].ourhook, hook, hooklen) == 0) {
+ *info = list->link[i];
+ free(resp);
+ return (0);
+ }
+ }
+ free(resp);
+ return (-1);
+}
+
+static int
+hook_cmp(const void *p1, const void *p2)
+{
+ const struct linkinfo *i1 = p1;
+ const struct linkinfo *i2 = p2;
+
+ if (strlen(i1->ourhook) < strlen(i2->ourhook))
+ return (-1);
+ if (strlen(i1->ourhook) > strlen(i2->ourhook))
+ return (+1);
+ return (strcmp(i1->ourhook, i2->ourhook));
+}
+
+static int
+find_hook_next(const struct asn_oid *oid, u_int sub, struct nodeinfo *nodeinfo,
+ struct linkinfo *linkinfo)
+{
+ u_int idxlen = oid->len - sub;
+ struct namelist *list;
+ struct ng_mesg *resp;
+ struct hooklist *hooks;
+ struct ng_mesg *resp1;
+ u_int node_index;
+ struct asn_oid idx;
+ u_int i, j;
+
+ /*
+ * Get and sort Node list
+ */
+ if ((resp = ng_dialog_id(snmp_node, NGM_GENERIC_COOKIE, NGM_LISTNODES,
+ NULL, 0)) == NULL)
+ return (-1);
+ list = (struct namelist *)(void *)resp->data;
+
+ qsort(list->nodeinfo, list->numnames, sizeof(list->nodeinfo[0]), ncmp);
+
+ /*
+ * If we have no index, take the first node and return the
+ * first hook.
+ */
+ if (idxlen == 0) {
+ node_index = 0;
+ goto return_first_hook;
+ }
+
+ /*
+ * Locate node
+ */
+ for (node_index = 0; node_index < list->numnames; node_index++)
+ if (list->nodeinfo[node_index].id >= oid->subs[sub])
+ break;
+
+ /*
+ * If we have only the node part of the index take, or
+ * there is no node with that Id, take the first hook of that node.
+ */
+ if (idxlen == 1 || node_index >= list->numnames ||
+ list->nodeinfo[node_index].id > oid->subs[sub])
+ goto return_first_hook;
+
+ /*
+ * We had an exact match on the node id and have (at last part)
+ * of the hook name index. Loop through the hooks of the node
+ * and find the next one.
+ */
+ if ((resp1 = ng_dialog_id(list->nodeinfo[node_index].id,
+ NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0)) == NULL) {
+ free(resp);
+ return (-1);
+ }
+ hooks = (struct hooklist *)(void *)resp1->data;
+ if (hooks->nodeinfo.hooks > 0) {
+ qsort(hooks->link, hooks->nodeinfo.hooks,
+ sizeof(hooks->link[0]), hook_cmp);
+ for (i = 0; i < hooks->nodeinfo.hooks; i++) {
+ idx.len = strlen(hooks->link[i].ourhook) + 1;
+ idx.subs[0] = idx.len - 1;
+ for (j = 0; j < idx.len; j++)
+ idx.subs[j + 1] = hooks->link[i].ourhook[j];
+ if (index_compare(oid, sub + 1, &idx) < 0)
+ break;
+ }
+ if (i < hooks->nodeinfo.hooks) {
+ *nodeinfo = hooks->nodeinfo;
+ *linkinfo = hooks->link[i];
+
+ free(resp);
+ free(resp1);
+ return (0);
+ }
+ }
+
+ /* no hook found larger than the index on the index node - take
+ * first hook of next node */
+ free(resp1);
+ node_index++;
+
+ return_first_hook:
+ while (node_index < list->numnames) {
+ if ((resp1 = ng_dialog_id(list->nodeinfo[node_index].id,
+ NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0)) == NULL)
+ break;
+ hooks = (struct hooklist *)(void *)resp1->data;
+ if (hooks->nodeinfo.hooks > 0) {
+ qsort(hooks->link, hooks->nodeinfo.hooks,
+ sizeof(hooks->link[0]), hook_cmp);
+
+ *nodeinfo = hooks->nodeinfo;
+ *linkinfo = hooks->link[0];
+
+ free(resp);
+ free(resp1);
+ return (0);
+ }
+
+ /* if we don't have hooks, try next node */
+ free(resp1);
+ node_index++;
+ }
+
+ free(resp);
+ return (-1);
+}
+
+int
+op_ng_hook(struct snmp_context *ctx __unused, struct snmp_value *value,
+ u_int sub, u_int iidx, enum snmp_op op)
+{
+ asn_subid_t which = value->var.subs[sub - 1];
+ struct linkinfo linkinfo;
+ struct nodeinfo nodeinfo;
+ u_int32_t lid;
+ u_char *hook;
+ size_t hooklen;
+ u_int i;
+
+ switch (op) {
+
+ case SNMP_OP_GETNEXT:
+ if (find_hook_next(&value->var, sub, &nodeinfo, &linkinfo) == -1)
+ return (SNMP_ERR_NOSUCHNAME);
+
+ value->var.len = sub + 1 + 1 + strlen(linkinfo.ourhook);
+ value->var.subs[sub] = nodeinfo.id;
+ value->var.subs[sub + 1] = strlen(linkinfo.ourhook);
+ for (i = 0; i < strlen(linkinfo.ourhook); i++)
+ value->var.subs[sub + i + 2] =
+ linkinfo.ourhook[i];
+ break;
+
+ case SNMP_OP_GET:
+ if (index_decode(&value->var, sub, iidx, &lid,
+ &hook, &hooklen))
+ return (SNMP_ERR_NOSUCHNAME);
+ if (find_hook(lid, hook, hooklen, &linkinfo) == -1) {
+ free(hook);
+ return (SNMP_ERR_NOSUCHNAME);
+ }
+ free(hook);
+ break;
+
+ case SNMP_OP_SET:
+ if (index_decode(&value->var, sub, iidx, &lid,
+ &hook, &hooklen))
+ return (SNMP_ERR_NO_CREATION);
+ if (find_hook(lid, hook, hooklen, &linkinfo) == -1) {
+ free(hook);
+ return (SNMP_ERR_NO_CREATION);
+ }
+ free(hook);
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ case SNMP_OP_ROLLBACK:
+ case SNMP_OP_COMMIT:
+ default:
+ abort();
+
+ }
+
+ switch (which) {
+
+ case LEAF_begemotNgHookStatus:
+ value->v.integer = 1;
+ break;
+ case LEAF_begemotNgHookPeerNodeId:
+ value->v.uint32 = linkinfo.nodeinfo.id;
+ break;
+ case LEAF_begemotNgHookPeerHook:
+ return (string_get(value, linkinfo.peerhook, -1));
+ case LEAF_begemotNgHookPeerType:
+ return (string_get(value, linkinfo.nodeinfo.type, -1));
+ default:
+ abort();
+ }
+ return (SNMP_ERR_NOERROR);
+}
diff --git a/usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.h b/usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.h
new file mode 100644
index 0000000..21e553c
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_netgraph/snmp_netgraph.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2001-2003
+ * Fraunhofer Institute for Open Communication Systems (FhG Fokus).
+ * All rights reserved.
+ *
+ * Author: Harti Brandt <harti@freebsd.org>
+ *
+ * Redistribution of this software and documentation and use in source and
+ * binary forms, with or without modification, are permitted provided that
+ * the following conditions are met:
+ *
+ * 1. Redistributions of source code or documentation must retain the above
+ * copyright notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 AND DOCUMENTATION IS PROVIDED BY FRAUNHOFER FOKUS
+ * AND ITS CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * FRAUNHOFER FOKUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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$
+ *
+ * Netgraph interface for SNMPd. Exported stuff.
+ */
+#ifndef SNMP_NETGRAPH_H_
+#define SNMP_NETGRAPH_H_
+
+#include <netgraph/ng_message.h>
+
+extern ng_ID_t snmp_node;
+extern u_char *snmp_nodename;
+
+typedef void ng_cookie_f(const struct ng_mesg *, const char *, ng_ID_t, void *);
+typedef void ng_hook_f(const char *, const u_char *, size_t, void *);
+
+void *ng_register_cookie(const struct lmodule *, u_int32_t cookie,
+ ng_ID_t, ng_cookie_f *, void *);
+void ng_unregister_cookie(void *reg);
+
+void *ng_register_hook(const struct lmodule *, const char *,
+ ng_hook_f *, void *);
+void ng_unregister_hook(void *reg);
+
+void ng_unregister_module(const struct lmodule *);
+
+int ng_output(const char *path, u_int cookie, u_int opcode,
+ const void *arg, size_t arglen);
+int ng_output_node(const char *node, u_int cookie, u_int opcode,
+ const void *arg, size_t arglen);
+int ng_output_id(ng_ID_t node, u_int cookie, u_int opcode,
+ const void *arg, size_t arglen);
+
+struct ng_mesg *ng_dialog(const char *path, u_int cookie, u_int opcode,
+ const void *arg, size_t arglen);
+struct ng_mesg *ng_dialog_node(const char *node, u_int cookie, u_int opcode,
+ const void *arg, size_t arglen);
+struct ng_mesg *ng_dialog_id(ng_ID_t id, u_int cookie, u_int opcode,
+ const void *arg, size_t arglen);
+
+int ng_send_data(const char *hook, const void *sndbuf, size_t sndlen);
+
+ng_ID_t ng_mkpeer_id(ng_ID_t, const char *name, const char *type,
+ const char *hook, const char *peerhook);
+int ng_connect_node(const char *node, const char *ourhook, const char *peerhook);
+int ng_connect_id(ng_ID_t id, const char *ourhook, const char *peerhook);
+int ng_connect2_id(ng_ID_t id, ng_ID_t peer, const char *ourhook,
+ const char *peerhook);
+int ng_connect2_tee_id(ng_ID_t id, ng_ID_t peer, const char *ourhook,
+ const char *peerhook);
+int ng_rmhook(const char *ourhook);
+int ng_rmhook_id(ng_ID_t, const char *);
+int ng_rmhook_tee_id(ng_ID_t, const char *);
+int ng_shutdown_id(ng_ID_t);
+
+ng_ID_t ng_next_node_id(ng_ID_t node, const char *type, const char *hook);
+ng_ID_t ng_node_id(const char *path);
+ng_ID_t ng_node_id_node(const char *node);
+ng_ID_t ng_node_name(ng_ID_t, char *);
+ng_ID_t ng_node_type(ng_ID_t, char *);
+int ng_peer_hook_id(ng_ID_t, const char *, char *);
+
+#endif
diff --git a/usr.sbin/bsnmpd/modules/snmp_pf/BEGEMOT-PF-MIB.txt b/usr.sbin/bsnmpd/modules/snmp_pf/BEGEMOT-PF-MIB.txt
new file mode 100644
index 0000000..72ebdb7
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_pf/BEGEMOT-PF-MIB.txt
@@ -0,0 +1,1230 @@
+--
+-- ----------------------------------------------------------------------------
+-- "THE BEER-WARE LICENSE" (Revision 42):
+-- <philip@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. -Philip Paeps
+-- ----------------------------------------------------------------------------
+--
+-- $FreeBSD$
+--
+
+BEGEMOT-PF-MIB DEFINITIONS ::= BEGIN
+
+IMPORTS
+ MODULE-IDENTITY, OBJECT-TYPE, Counter64, Integer32,
+ TimeTicks, Unsigned32
+ FROM SNMPv2-SMI
+ TruthValue
+ FROM SNMPv2-TC
+ begemot
+ FROM BEGEMOT-MIB;
+
+begemotPf MODULE-IDENTITY
+ LAST-UPDATED "200501240000Z"
+ ORGANIZATION "NixSys BVBA"
+ CONTACT-INFO
+ " Philip Paeps
+
+ Postal: NixSys BVBA
+ Louizastraat 14
+ BE-2800 Mechelen
+ Belgium
+
+ E-Mail: philip@FreeBSD.org"
+ DESCRIPTION
+ "The Begemot MIB for the pf packet filter."
+
+ ::= { begemot 200 }
+
+begemotPfObjects OBJECT IDENTIFIER ::= { begemotPf 1 }
+
+-- --------------------------------------------------------------------------
+
+pfStatus OBJECT IDENTIFIER ::= { begemotPfObjects 1 }
+pfCounter OBJECT IDENTIFIER ::= { begemotPfObjects 2 }
+pfStateTable OBJECT IDENTIFIER ::= { begemotPfObjects 3 }
+pfSrcNodes OBJECT IDENTIFIER ::= { begemotPfObjects 4 }
+pfLimits OBJECT IDENTIFIER ::= { begemotPfObjects 5 }
+pfTimeouts OBJECT IDENTIFIER ::= { begemotPfObjects 6 }
+pfLogInterface OBJECT IDENTIFIER ::= { begemotPfObjects 7 }
+pfInterfaces OBJECT IDENTIFIER ::= { begemotPfObjects 8 }
+pfTables OBJECT IDENTIFIER ::= { begemotPfObjects 9 }
+pfAltq OBJECT IDENTIFIER ::= { begemotPfObjects 10 }
+
+-- --------------------------------------------------------------------------
+
+--
+-- status information
+--
+
+pfStatusRunning OBJECT-TYPE
+ SYNTAX TruthValue
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "True if pf is currently enabled."
+ ::= { pfStatus 1 }
+
+pfStatusRuntime OBJECT-TYPE
+ SYNTAX TimeTicks
+ UNITS "1/100th of a Second"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Indicates how long pf has been enabled. If pf is not currently
+ enabled, indicates how long it has been disabled. If pf has not
+ been enabled or disabled since the system was started, the value
+ will be 0."
+ ::= { pfStatus 2 }
+
+pfStatusDebug OBJECT-TYPE
+ SYNTAX INTEGER { none(0), urgent(1), misc(2), loud(3) }
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Indicates the debug level at which pf is running."
+ ::= { pfStatus 3 }
+
+pfStatusHostId OBJECT-TYPE
+ SYNTAX OCTET STRING
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The (unique) host identifier of the machine running pf."
+ ::= { pfStatus 4 }
+
+-- --------------------------------------------------------------------------
+
+--
+-- counters
+--
+
+pfCounterMatch OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of packets that matched a filter rule."
+ ::= { pfCounter 1 }
+
+pfCounterBadOffset OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of packets with bad offset."
+ ::= { pfCounter 2 }
+
+pfCounterFragment OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of fragmented packets."
+ ::= { pfCounter 3 }
+
+pfCounterShort OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of short packets."
+ ::= { pfCounter 4 }
+
+pfCounterNormalize OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of normalized packets."
+ ::= { pfCounter 5 }
+
+pfCounterMemDrop OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of packets dropped due to memory limitations."
+ ::= { pfCounter 6 }
+
+-- --------------------------------------------------------------------------
+
+--
+-- state table
+--
+
+pfStateTableCount OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of entries in the state table."
+ ::= { pfStateTable 1 }
+
+pfStateTableSearches OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of searches against the state table."
+ ::= { pfStateTable 2 }
+
+pfStateTableInserts OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of entries inserted into the state table."
+ ::= { pfStateTable 3 }
+
+pfStateTableRemovals OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of entries removed from the state table."
+ ::= { pfStateTable 4 }
+
+-- --------------------------------------------------------------------------
+
+--
+-- source nodes
+--
+
+pfSrcNodesCount OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of entries in the source tracking table."
+ ::= { pfSrcNodes 1 }
+
+pfSrcNodesSearches OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of searches against the source tracking table."
+ ::= { pfSrcNodes 2 }
+
+pfSrcNodesInserts OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of entries inserted into the source tracking table."
+ ::= { pfSrcNodes 3 }
+
+pfSrcNodesRemovals OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of entries removed from the source tracking table."
+ ::= { pfSrcNodes 4 }
+
+-- --------------------------------------------------------------------------
+
+--
+-- limits
+--
+
+pfLimitsStates OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Maximum number of 'keep state' rules in the ruleset."
+ ::= { pfLimits 1 }
+
+pfLimitsSrcNodes OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Maximum number of 'sticky-address' or 'source-track' rules
+ in the ruleset."
+ ::= { pfLimits 2 }
+
+pfLimitsFrags OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Maximum number of 'scrub' rules in the ruleset."
+ ::= { pfLimits 3 }
+
+-- --------------------------------------------------------------------------
+
+--
+-- timeouts
+--
+
+pfTimeoutsTcpFirst OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "State after the first packet in a connection."
+ ::= { pfTimeouts 1 }
+
+pfTimeoutsTcpOpening OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "State before the destination host ever sends a packet."
+ ::= { pfTimeouts 2 }
+
+pfTimeoutsTcpEstablished OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The fully established state."
+ ::= { pfTimeouts 3 }
+
+pfTimeoutsTcpClosing OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "State after the first FIN has been sent."
+ ::= { pfTimeouts 4 }
+
+pfTimeoutsTcpFinWait OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "State after both FINs have been exchanged and the
+ connection is closed."
+ ::= { pfTimeouts 5 }
+
+pfTimeoutsTcpClosed OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "State after one endpoint sends an RST."
+ ::= { pfTimeouts 6 }
+
+pfTimeoutsUdpFirst OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "State after the first packet."
+ ::= { pfTimeouts 7 }
+
+pfTimeoutsUdpSingle OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "State if the source host sends more than one packet but
+ the destination host has never sent one back."
+ ::= { pfTimeouts 8 }
+
+pfTimeoutsUdpMultiple OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "State if both hosts have sent packets."
+ ::= { pfTimeouts 9 }
+
+pfTimeoutsIcmpFirst OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "State after the first packet."
+ ::= { pfTimeouts 10 }
+
+pfTimeoutsIcmpError OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "State after an ICMP error came back in response to an
+ ICMP packet."
+ ::= { pfTimeouts 11 }
+
+pfTimeoutsOtherFirst OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "State after the first packet."
+ ::= { pfTimeouts 12 }
+
+pfTimeoutsOtherSingle OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "State if the source host sends more than one packet but
+ the destination host has never sent one back."
+ ::= { pfTimeouts 13 }
+
+pfTimeoutsOtherMultiple OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "State if both hosts have sent packets."
+ ::= { pfTimeouts 14 }
+
+pfTimeoutsFragment OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Seconds before an unassembled fragment is expired."
+ ::= { pfTimeouts 15 }
+
+pfTimeoutsInterval OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Interval between purging expired states and fragments."
+ ::= { pfTimeouts 16 }
+
+pfTimeoutsAdaptiveStart OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "When the number of state entries exceeds this value,
+ adaptive scaling begins."
+ ::= { pfTimeouts 17 }
+
+pfTimeoutsAdaptiveEnd OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "When reaching this number of state entries, all timeout
+ values become zero, effectively purging all state entries
+ immediately."
+ ::= { pfTimeouts 18 }
+
+pfTimeoutsSrcNode OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Length of time to retain a source tracking entry after
+ the last state expires."
+ ::= { pfTimeouts 19 }
+
+-- --------------------------------------------------------------------------
+
+--
+-- log interface
+--
+
+pfLogInterfaceName OBJECT-TYPE
+ SYNTAX OCTET STRING
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The name of the interface configured with 'set loginterface'.
+ If no interface has been configured, the object will be empty."
+ ::= { pfLogInterface 1 }
+
+pfLogInterfaceIp4BytesIn OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of IPv4 bytes passed in on the loginterface."
+ ::= { pfLogInterface 2 }
+
+pfLogInterfaceIp4BytesOut OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of IPv4 bytes passed out on the loginterface."
+ ::= { pfLogInterface 3 }
+
+pfLogInterfaceIp4PktsInPass OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of IPv4 packets passed in on the loginterface."
+ ::= { pfLogInterface 4 }
+
+pfLogInterfaceIp4PktsInDrop OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of IPv4 packets dropped coming in on the loginterface."
+ ::= { pfLogInterface 5 }
+
+pfLogInterfaceIp4PktsOutPass OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of IPv4 packets passed out on the loginterface."
+ ::= { pfLogInterface 6 }
+
+pfLogInterfaceIp4PktsOutDrop OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of IPv4 packets dropped going out on the loginterface."
+ ::= { pfLogInterface 7 }
+
+pfLogInterfaceIp6BytesIn OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of IPv6 bytes passed in on the loginterface."
+ ::= { pfLogInterface 8 }
+
+pfLogInterfaceIp6BytesOut OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of IPv6 bytes passed out on the loginterface."
+ ::= { pfLogInterface 9 }
+
+pfLogInterfaceIp6PktsInPass OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of IPv6 packets passed in on the loginterface."
+ ::= { pfLogInterface 10 }
+
+pfLogInterfaceIp6PktsInDrop OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of IPv6 packets dropped coming in on the loginterface."
+ ::= { pfLogInterface 11 }
+
+pfLogInterfaceIp6PktsOutPass OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of IPv6 packets passed out on the loginterface."
+ ::= { pfLogInterface 12 }
+
+pfLogInterfaceIp6PktsOutDrop OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Number of IPv6 packets dropped going out on the loginterface."
+ ::= { pfLogInterface 13 }
+
+-- --------------------------------------------------------------------------
+
+--
+-- interfaces
+--
+
+pfInterfacesIfNumber OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of network interfaces on this system."
+ ::= { pfInterfaces 1 }
+
+pfInterfacesIfTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PfInterfacesIfEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of network interfaces, indexed on pfInterfacesIfNumber."
+ ::= { pfInterfaces 2 }
+
+pfInterfacesIfEntry OBJECT-TYPE
+ SYNTAX PfInterfacesIfEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the pfInterfacesIfTable containing information
+ about a particular network interface in the machine."
+ INDEX { pfInterfacesIfIndex }
+ ::= { pfInterfacesIfTable 1 }
+
+PfInterfacesIfEntry ::= SEQUENCE {
+ pfInterfacesIfIndex Integer32,
+ pfInterfacesIfDescr OCTET STRING,
+ pfInterfacesIfType INTEGER,
+ pfInterfacesIfTZero TimeTicks,
+ pfInterfacesIfRefsState Unsigned32,
+ pfInterfacesIfRefsRule Unsigned32,
+ pfInterfacesIf4BytesInPass Counter64,
+ pfInterfacesIf4BytesInBlock Counter64,
+ pfInterfacesIf4BytesOutPass Counter64,
+ pfInterfacesIf4BytesOutBlock Counter64,
+ pfInterfacesIf4PktsInPass Counter64,
+ pfInterfacesIf4PktsInBlock Counter64,
+ pfInterfacesIf4PktsOutPass Counter64,
+ pfInterfacesIf4PktsOutBlock Counter64,
+ pfInterfacesIf6BytesInPass Counter64,
+ pfInterfacesIf6BytesInBlock Counter64,
+ pfInterfacesIf6BytesOutPass Counter64,
+ pfInterfacesIf6BytesOutBlock Counter64,
+ pfInterfacesIf6PktsInPass Counter64,
+ pfInterfacesIf6PktsInBlock Counter64,
+ pfInterfacesIf6PktsOutPass Counter64,
+ pfInterfacesIf6PktsOutBlock Counter64
+}
+
+pfInterfacesIfIndex OBJECT-TYPE
+ SYNTAX Integer32 (1..2147483647)
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A unique value, greater than zero, for each interface."
+ ::= { pfInterfacesIfEntry 1 }
+
+pfInterfacesIfDescr OBJECT-TYPE
+ SYNTAX OCTET STRING
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The name of the interface."
+ ::= { pfInterfacesIfEntry 2 }
+
+pfInterfacesIfType OBJECT-TYPE
+ SYNTAX INTEGER { group(0), instance(1), detached(2) }
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Indicates whether the interface is a group inteface, an
+ interface instance, or whether it has been removed or
+ destroyed."
+ ::= { pfInterfacesIfEntry 3 }
+
+pfInterfacesIfTZero OBJECT-TYPE
+ SYNTAX TimeTicks
+ UNITS "1/100th of a Second"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Time since statistics were last reset or since the
+ interface was loaded."
+ ::= { pfInterfacesIfEntry 4 }
+
+pfInterfacesIfRefsState OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of state and/or source track entries referencing
+ this interface."
+ ::= { pfInterfacesIfEntry 5 }
+
+pfInterfacesIfRefsRule OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of rules referencing this interface."
+ ::= { pfInterfacesIfEntry 6 }
+
+pfInterfacesIf4BytesInPass OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of IPv4 bytes passed coming in on this interface."
+ ::= { pfInterfacesIfEntry 7 }
+
+pfInterfacesIf4BytesInBlock OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of IPv4 bytes blocked coming in on this interface."
+ ::= { pfInterfacesIfEntry 8 }
+
+pfInterfacesIf4BytesOutPass OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of IPv4 bytes passed going out on this interface."
+ ::= { pfInterfacesIfEntry 9 }
+
+pfInterfacesIf4BytesOutBlock OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of IPv4 bytes blocked going out on this interface."
+ ::= { pfInterfacesIfEntry 10 }
+
+pfInterfacesIf4PktsInPass OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of IPv4 packets passed coming in on this interface."
+ ::= { pfInterfacesIfEntry 11 }
+
+pfInterfacesIf4PktsInBlock OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of IPv4 packets blocked coming in on this interface."
+ ::= { pfInterfacesIfEntry 12 }
+
+pfInterfacesIf4PktsOutPass OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of IPv4 packets passed going out on this interface."
+ ::= { pfInterfacesIfEntry 13 }
+
+pfInterfacesIf4PktsOutBlock OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of IPv4 packets blocked going out on this interface."
+ ::= { pfInterfacesIfEntry 14 }
+
+pfInterfacesIf6BytesInPass OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of IPv6 bytes passed coming in on this interface."
+ ::= { pfInterfacesIfEntry 15 }
+
+pfInterfacesIf6BytesInBlock OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of IPv6 bytes blocked coming in on this interface."
+ ::= { pfInterfacesIfEntry 16 }
+
+pfInterfacesIf6BytesOutPass OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of IPv6 bytes passed going out on this interface."
+ ::= { pfInterfacesIfEntry 17 }
+
+pfInterfacesIf6BytesOutBlock OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of IPv6 bytes blocked going out on this interface."
+ ::= { pfInterfacesIfEntry 18 }
+
+
+pfInterfacesIf6PktsInPass OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of IPv6 packets passed coming in on this interface."
+ ::= { pfInterfacesIfEntry 19 }
+
+pfInterfacesIf6PktsInBlock OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of IPv6 packets blocked coming in on this interface."
+ ::= { pfInterfacesIfEntry 20 }
+
+pfInterfacesIf6PktsOutPass OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of IPv6 packets passed going out on this interface."
+ ::= { pfInterfacesIfEntry 21 }
+
+pfInterfacesIf6PktsOutBlock OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of IPv6 packets blocked going out on this interface."
+ ::= { pfInterfacesIfEntry 22 }
+
+-- --------------------------------------------------------------------------
+
+--
+-- tables
+--
+
+pfTablesTblNumber OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of tables on this system."
+ ::= { pfTables 1 }
+
+pfTablesTblTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PfTablesTblEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of tables, index on pfTablesTblIndex."
+ ::= { pfTables 2 }
+
+pfTablesTblEntry OBJECT-TYPE
+ SYNTAX PfTablesTblEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Any entry in the pfTablesTblTable containing information
+ about a particular table on the system."
+ INDEX { pfTablesTblIndex }
+ ::= { pfTablesTblTable 1 }
+
+PfTablesTblEntry ::= SEQUENCE {
+ pfTablesTblIndex Integer32,
+ pfTablesTblDescr OCTET STRING,
+ pfTablesTblCount Integer32,
+ pfTablesTblTZero TimeTicks,
+ pfTablesTblRefsAnchor Integer32,
+ pfTablesTblRefsRule Integer32,
+ pfTablesTblEvalMatch Counter64,
+ pfTablesTblEvalNoMatch Counter64,
+ pfTablesTblBytesInPass Counter64,
+ pfTablesTblBytesInBlock Counter64,
+ pfTablesTblBytesInXPass Counter64,
+ pfTablesTblBytesOutPass Counter64,
+ pfTablesTblBytesOutBlock Counter64,
+ pfTablesTblBytesOutXPass Counter64,
+ pfTablesTblPktsInPass Counter64,
+ pfTablesTblPktsInBlock Counter64,
+ pfTablesTblPktsInXPass Counter64,
+ pfTablesTblPktsOutPass Counter64,
+ pfTablesTblPktsOutBlock Counter64,
+ pfTablesTblPktsOutXPass Counter64
+}
+
+pfTablesTblIndex OBJECT-TYPE
+ SYNTAX Integer32 (1..2147483647)
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A unique value, greater than zero, for each table."
+ ::= { pfTablesTblEntry 1 }
+
+pfTablesTblDescr OBJECT-TYPE
+ SYNTAX OCTET STRING
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The name of the table."
+ ::= { pfTablesTblEntry 2 }
+
+pfTablesTblCount OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of addresses in the table."
+ ::= { pfTablesTblEntry 3 }
+
+pfTablesTblTZero OBJECT-TYPE
+ SYNTAX TimeTicks
+ UNITS "1/100th of a Second"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The time passed since the statistics of this table were last
+ cleared or the time since this table was loaded, whichever is
+ sooner."
+ ::= { pfTablesTblEntry 4 }
+
+pfTablesTblRefsAnchor OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of anchors referencing this table."
+ ::= { pfTablesTblEntry 5 }
+
+pfTablesTblRefsRule OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of rules referencing this table."
+ ::= { pfTablesTblEntry 6 }
+
+pfTablesTblEvalMatch OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of evaluations returning a match."
+ ::= { pfTablesTblEntry 7 }
+
+pfTablesTblEvalNoMatch OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of evaluations not returning a match."
+ ::= { pfTablesTblEntry 8 }
+
+pfTablesTblBytesInPass OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of bytes passed in matching the table."
+ ::= { pfTablesTblEntry 9 }
+
+pfTablesTblBytesInBlock OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of bytes blocked coming in matching the table."
+ ::= { pfTablesTblEntry 10 }
+
+pfTablesTblBytesInXPass OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of bytes statefully passed in where the state
+ entry refers to the table, but the table no longer contains
+ the address in question."
+ ::= { pfTablesTblEntry 11 }
+
+pfTablesTblBytesOutPass OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of bytes passed out matching the table."
+ ::= { pfTablesTblEntry 12 }
+
+pfTablesTblBytesOutBlock OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of bytes blocked going out matching the table."
+ ::= { pfTablesTblEntry 13 }
+
+pfTablesTblBytesOutXPass OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of bytes statefully passed out where the state
+ entry refers to the table, but the table no longer contains
+ the address in question."
+ ::= { pfTablesTblEntry 14 }
+
+pfTablesTblPktsInPass OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of packets passed in matching the table."
+ ::= { pfTablesTblEntry 15 }
+
+pfTablesTblPktsInBlock OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of packets blocked coming in matching the table."
+ ::= { pfTablesTblEntry 16 }
+
+pfTablesTblPktsInXPass OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of packets statefully passed in where the state
+ entry refers to the table, but the table no longer contains
+ the address in question."
+ ::= { pfTablesTblEntry 17 }
+
+pfTablesTblPktsOutPass OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of packets passed out matching the table."
+ ::= { pfTablesTblEntry 18 }
+
+pfTablesTblPktsOutBlock OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of packets blocked going out matching the table."
+ ::= { pfTablesTblEntry 19 }
+
+pfTablesTblPktsOutXPass OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of packets statefully passed out where the state
+ entry refers to the table, but the table no longer contains
+ the address in question."
+ ::= { pfTablesTblEntry 20 }
+
+pfTablesAddrTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PfTablesAddrEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table of addresses from every table on the system."
+ ::= { pfTables 3 }
+
+pfTablesAddrEntry OBJECT-TYPE
+ SYNTAX PfTablesAddrEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the pfTablesAddrTable containing information
+ about a particular entry in a table."
+ INDEX { pfTablesAddrIndex }
+ ::= { pfTablesAddrTable 1 }
+
+PfTablesAddrEntry ::= SEQUENCE {
+ pfTablesAddrIndex Integer32,
+ pfTablesAddrNet IpAddress,
+ pfTablesAddrMask Integer32,
+ pfTablesAddrTZero TimeTicks,
+ pfTablesAddrBytesInPass Counter64,
+ pfTablesAddrBytesInBlock Counter64,
+ pfTablesAddrBytesOutPass Counter64,
+ pfTablesAddrBytesOutBlock Counter64,
+ pfTablesAddrPktsInPass Counter64,
+ pfTablesAddrPktsInBlock Counter64,
+ pfTablesAddrPktsOutPass Counter64,
+ pfTablesAddrPktsOutBlock Counter64
+}
+
+pfTablesAddrIndex OBJECT-TYPE
+ SYNTAX Integer32 (1..2147483647)
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A unique value, greater than zero, for each address."
+ ::= { pfTablesAddrEntry 1 }
+
+pfTablesAddrNet OBJECT-TYPE
+ SYNTAX IpAddress
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The IP address of this particular table entry."
+ ::= { pfTablesAddrEntry 2 }
+
+pfTablesAddrMask OBJECT-TYPE
+ SYNTAX Integer32 (0..32)
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The CIDR netmask of this particular table entry."
+ ::= { pfTablesAddrEntry 3 }
+
+pfTablesAddrTZero OBJECT-TYPE
+ SYNTAX TimeTicks
+ UNITS "1/100th of a Second"
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The time passed since this entry's statistics were last
+ cleared, or the time passed since this entry was loaded
+ into the table, whichever is sooner."
+ ::= { pfTablesAddrEntry 4 }
+
+pfTablesAddrBytesInPass OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of inbound bytes passed as a result of this entry."
+ ::= { pfTablesAddrEntry 5 }
+
+pfTablesAddrBytesInBlock OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of inbound bytes blocked as a result of this entry."
+ ::= { pfTablesAddrEntry 6 }
+
+pfTablesAddrBytesOutPass OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of outbound bytes passed as a result of this entry."
+ ::= { pfTablesAddrEntry 7 }
+
+pfTablesAddrBytesOutBlock OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of outbound bytes blocked as a result of this entry."
+ ::= { pfTablesAddrEntry 8 }
+
+pfTablesAddrPktsInPass OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of inbound packets passed as a result of this entry."
+ ::= { pfTablesAddrEntry 9 }
+
+pfTablesAddrPktsInBlock OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of inbound packets blocked as a result of this entry."
+ ::= { pfTablesAddrEntry 10 }
+
+pfTablesAddrPktsOutPass OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of outbound packets passed as a result of this entry."
+ ::= { pfTablesAddrEntry 11 }
+
+pfTablesAddrPktsOutBlock OBJECT-TYPE
+ SYNTAX Counter64
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of outbound packets blocked as a result of this
+ entry."
+ ::= { pfTablesAddrEntry 12 }
+
+-- --------------------------------------------------------------------------
+
+--
+-- Altq information
+--
+
+pfAltqQueueNumber OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The number of queues in the active set."
+ ::= { pfAltq 1 }
+
+pfAltqQueueTable OBJECT-TYPE
+ SYNTAX SEQUENCE OF PfAltqQueueEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "Table containing the rules that are active on this system."
+ ::= { pfAltq 2 }
+
+pfAltqQueueEntry OBJECT-TYPE
+ SYNTAX PfAltqQueueEntry
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "An entry in the pfAltqQueueTable table."
+ INDEX { pfAltqQueueIndex }
+ ::= { pfAltqQueueTable 1 }
+
+PfAltqQueueEntry ::= SEQUENCE {
+ pfAltqQueueIndex Integer32,
+ pfAltqQueueDescr OCTET STRING,
+ pfAltqQueueParent OCTET STRING,
+ pfAltqQueueScheduler INTEGER,
+ pfAltqQueueBandwidth Unsigned32,
+ pfAltqQueuePriority Integer32,
+ pfAltqQueueLimit Integer32
+}
+
+pfAltqQueueIndex OBJECT-TYPE
+ SYNTAX Integer32 (1..2147483647)
+ MAX-ACCESS not-accessible
+ STATUS current
+ DESCRIPTION
+ "A unique value, greater than zero, for each queue."
+ ::= { pfAltqQueueEntry 1 }
+
+pfAltqQueueDescr OBJECT-TYPE
+ SYNTAX OCTET STRING
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "The name of the queue."
+ ::= { pfAltqQueueEntry 2 }
+
+pfAltqQueueParent OBJECT-TYPE
+ SYNTAX OCTET STRING
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Name of the queue's parent if it has one."
+ ::= { pfAltqQueueEntry 3 }
+
+pfAltqQueueScheduler OBJECT-TYPE
+ SYNTAX INTEGER { cbq(1), hfsc(8), priq(11) }
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Scheduler algorithm implemented by this queue."
+ ::= { pfAltqQueueEntry 4 }
+
+pfAltqQueueBandwidth OBJECT-TYPE
+ SYNTAX Unsigned32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Bandwitch assigned to this queue."
+ ::= { pfAltqQueueEntry 5 }
+
+pfAltqQueuePriority OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Priority level of the queue."
+ ::= { pfAltqQueueEntry 6 }
+
+pfAltqQueueLimit OBJECT-TYPE
+ SYNTAX Integer32
+ MAX-ACCESS read-only
+ STATUS current
+ DESCRIPTION
+ "Maximum number of packets in the queue."
+ ::= { pfAltqQueueEntry 7 }
+
+END
diff --git a/usr.sbin/bsnmpd/modules/snmp_pf/Makefile b/usr.sbin/bsnmpd/modules/snmp_pf/Makefile
new file mode 100644
index 0000000..7472464
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_pf/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+#
+# Author: Philip Paeps <philip@freebsd.org>
+
+MOD= pf
+SRCS= pf_snmp.c
+WARNS?= 6
+
+XSYM= begemotPf
+DEFS= ${MOD}_tree.def
+BMIBS= BEGEMOT-PF-MIB.txt
+
+.include <bsd.snmpmod.mk>
diff --git a/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c b/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c
new file mode 100644
index 0000000..919e5d4
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_pf/pf_snmp.c
@@ -0,0 +1,1260 @@
+/*-
+ * Copyright (c) 2005 Philip Paeps <philip@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 <bsnmp/snmpmod.h>
+
+#include <net/pfvar.h>
+#include <sys/ioctl.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "pf_oid.h"
+#include "pf_tree.h"
+
+struct lmodule *module;
+
+static int dev = -1;
+static int started;
+static uint64_t pf_tick;
+
+static struct pf_status pfs;
+
+enum { IN, OUT };
+enum { IPV4, IPV6 };
+enum { PASS, BLOCK };
+
+#define PFI_IFTYPE_GROUP 0
+#define PFI_IFTYPE_INSTANCE 1
+#define PFI_IFTYPE_DETACHED 2
+
+struct pfi_entry {
+ struct pfi_kif pfi;
+ u_int index;
+ TAILQ_ENTRY(pfi_entry) link;
+};
+TAILQ_HEAD(pfi_table, pfi_entry);
+
+static struct pfi_table pfi_table;
+static time_t pfi_table_age;
+static int pfi_table_count;
+
+#define PFI_TABLE_MAXAGE 5
+
+struct pft_entry {
+ struct pfr_tstats pft;
+ u_int index;
+ TAILQ_ENTRY(pft_entry) link;
+};
+TAILQ_HEAD(pft_table, pft_entry);
+
+static struct pft_table pft_table;
+static time_t pft_table_age;
+static int pft_table_count;
+
+#define PFT_TABLE_MAXAGE 5
+
+struct pfq_entry {
+ struct pf_altq altq;
+ u_int index;
+ TAILQ_ENTRY(pfq_entry) link;
+};
+TAILQ_HEAD(pfq_table, pfq_entry);
+
+static struct pfq_table pfq_table;
+static time_t pfq_table_age;
+static int pfq_table_count;
+
+static int altq_enabled = 0;
+
+#define PFQ_TABLE_MAXAGE 5
+
+/* Forward declarations */
+static int pfi_refresh(void);
+static int pfq_refresh(void);
+static int pfs_refresh(void);
+static int pft_refresh(void);
+static struct pfi_entry * pfi_table_find(u_int idx);
+static struct pfq_entry * pfq_table_find(u_int idx);
+static struct pft_entry * pft_table_find(u_int idx);
+
+static int altq_is_enabled(int pfdevice);
+
+int
+pf_status(struct snmp_context __unused *ctx, struct snmp_value *val,
+ u_int sub, u_int __unused vindex, enum snmp_op op)
+{
+ asn_subid_t which = val->var.subs[sub - 1];
+ time_t runtime;
+ unsigned char str[128];
+
+ if (op == SNMP_OP_SET)
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ if (op == SNMP_OP_GET) {
+ if (pfs_refresh() == -1)
+ return (SNMP_ERR_GENERR);
+
+ switch (which) {
+ case LEAF_pfStatusRunning:
+ val->v.uint32 = pfs.running;
+ break;
+ case LEAF_pfStatusRuntime:
+ runtime = (pfs.since > 0) ?
+ time(NULL) - pfs.since : 0;
+ val->v.uint32 = runtime * 100;
+ break;
+ case LEAF_pfStatusDebug:
+ val->v.uint32 = pfs.debug;
+ break;
+ case LEAF_pfStatusHostId:
+ sprintf(str, "0x%08x", ntohl(pfs.hostid));
+ return (string_get(val, str, strlen(str)));
+
+ default:
+ return (SNMP_ERR_NOSUCHNAME);
+ }
+
+ return (SNMP_ERR_NOERROR);
+ }
+
+ abort();
+}
+
+int
+pf_counter(struct snmp_context __unused *ctx, struct snmp_value *val,
+ u_int sub, u_int __unused vindex, enum snmp_op op)
+{
+ asn_subid_t which = val->var.subs[sub - 1];
+
+ if (op == SNMP_OP_SET)
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ if (op == SNMP_OP_GET) {
+ if (pfs_refresh() == -1)
+ return (SNMP_ERR_GENERR);
+
+ switch (which) {
+ case LEAF_pfCounterMatch:
+ val->v.counter64 = pfs.counters[PFRES_MATCH];
+ break;
+ case LEAF_pfCounterBadOffset:
+ val->v.counter64 = pfs.counters[PFRES_BADOFF];
+ break;
+ case LEAF_pfCounterFragment:
+ val->v.counter64 = pfs.counters[PFRES_FRAG];
+ break;
+ case LEAF_pfCounterShort:
+ val->v.counter64 = pfs.counters[PFRES_SHORT];
+ break;
+ case LEAF_pfCounterNormalize:
+ val->v.counter64 = pfs.counters[PFRES_NORM];
+ break;
+ case LEAF_pfCounterMemDrop:
+ val->v.counter64 = pfs.counters[PFRES_MEMORY];
+ break;
+
+ default:
+ return (SNMP_ERR_NOSUCHNAME);
+ }
+
+ return (SNMP_ERR_NOERROR);
+ }
+
+ abort();
+}
+
+int
+pf_statetable(struct snmp_context __unused *ctx, struct snmp_value *val,
+ u_int sub, u_int __unused vindex, enum snmp_op op)
+{
+ asn_subid_t which = val->var.subs[sub - 1];
+
+ if (op == SNMP_OP_SET)
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ if (op == SNMP_OP_GET) {
+ if (pfs_refresh() == -1)
+ return (SNMP_ERR_GENERR);
+
+ switch (which) {
+ case LEAF_pfStateTableCount:
+ val->v.uint32 = pfs.states;
+ break;
+ case LEAF_pfStateTableSearches:
+ val->v.counter64 =
+ pfs.fcounters[FCNT_STATE_SEARCH];
+ break;
+ case LEAF_pfStateTableInserts:
+ val->v.counter64 =
+ pfs.fcounters[FCNT_STATE_INSERT];
+ break;
+ case LEAF_pfStateTableRemovals:
+ val->v.counter64 =
+ pfs.fcounters[FCNT_STATE_REMOVALS];
+ break;
+
+ default:
+ return (SNMP_ERR_NOSUCHNAME);
+ }
+
+ return (SNMP_ERR_NOERROR);
+ }
+
+ abort();
+}
+
+int
+pf_srcnodes(struct snmp_context __unused *ctx, struct snmp_value *val,
+ u_int sub, u_int __unused vindex, enum snmp_op op)
+{
+ asn_subid_t which = val->var.subs[sub - 1];
+
+ if (op == SNMP_OP_SET)
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ if (op == SNMP_OP_GET) {
+ if (pfs_refresh() == -1)
+ return (SNMP_ERR_GENERR);
+
+ switch (which) {
+ case LEAF_pfSrcNodesCount:
+ val->v.uint32 = pfs.src_nodes;
+ break;
+ case LEAF_pfSrcNodesSearches:
+ val->v.counter64 =
+ pfs.scounters[SCNT_SRC_NODE_SEARCH];
+ break;
+ case LEAF_pfSrcNodesInserts:
+ val->v.counter64 =
+ pfs.scounters[SCNT_SRC_NODE_INSERT];
+ break;
+ case LEAF_pfSrcNodesRemovals:
+ val->v.counter64 =
+ pfs.scounters[SCNT_SRC_NODE_REMOVALS];
+ break;
+
+ default:
+ return (SNMP_ERR_NOSUCHNAME);
+ }
+
+ return (SNMP_ERR_NOERROR);
+ }
+
+ abort();
+}
+
+int
+pf_limits(struct snmp_context __unused *ctx, struct snmp_value *val,
+ u_int sub, u_int __unused vindex, enum snmp_op op)
+{
+ asn_subid_t which = val->var.subs[sub - 1];
+ struct pfioc_limit pl;
+
+ if (op == SNMP_OP_SET)
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ if (op == SNMP_OP_GET) {
+ bzero(&pl, sizeof(struct pfioc_limit));
+
+ switch (which) {
+ case LEAF_pfLimitsStates:
+ pl.index = PF_LIMIT_STATES;
+ break;
+ case LEAF_pfLimitsSrcNodes:
+ pl.index = PF_LIMIT_SRC_NODES;
+ break;
+ case LEAF_pfLimitsFrags:
+ pl.index = PF_LIMIT_FRAGS;
+ break;
+
+ default:
+ return (SNMP_ERR_NOSUCHNAME);
+ }
+
+ if (ioctl(dev, DIOCGETLIMIT, &pl)) {
+ syslog(LOG_ERR, "pf_limits(): ioctl(): %s",
+ strerror(errno));
+ return (SNMP_ERR_GENERR);
+ }
+
+ val->v.uint32 = pl.limit;
+
+ return (SNMP_ERR_NOERROR);
+ }
+
+ abort();
+}
+
+int
+pf_timeouts(struct snmp_context __unused *ctx, struct snmp_value *val,
+ u_int sub, u_int __unused vindex, enum snmp_op op)
+{
+ asn_subid_t which = val->var.subs[sub - 1];
+ struct pfioc_tm pt;
+
+ if (op == SNMP_OP_SET)
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ if (op == SNMP_OP_GET) {
+ bzero(&pt, sizeof(struct pfioc_tm));
+
+ switch (which) {
+ case LEAF_pfTimeoutsTcpFirst:
+ pt.timeout = PFTM_TCP_FIRST_PACKET;
+ break;
+ case LEAF_pfTimeoutsTcpOpening:
+ pt.timeout = PFTM_TCP_OPENING;
+ break;
+ case LEAF_pfTimeoutsTcpEstablished:
+ pt.timeout = PFTM_TCP_ESTABLISHED;
+ break;
+ case LEAF_pfTimeoutsTcpClosing:
+ pt.timeout = PFTM_TCP_CLOSING;
+ break;
+ case LEAF_pfTimeoutsTcpFinWait:
+ pt.timeout = PFTM_TCP_FIN_WAIT;
+ break;
+ case LEAF_pfTimeoutsTcpClosed:
+ pt.timeout = PFTM_TCP_CLOSED;
+ break;
+ case LEAF_pfTimeoutsUdpFirst:
+ pt.timeout = PFTM_UDP_FIRST_PACKET;
+ break;
+ case LEAF_pfTimeoutsUdpSingle:
+ pt.timeout = PFTM_UDP_SINGLE;
+ break;
+ case LEAF_pfTimeoutsUdpMultiple:
+ pt.timeout = PFTM_UDP_MULTIPLE;
+ break;
+ case LEAF_pfTimeoutsIcmpFirst:
+ pt.timeout = PFTM_ICMP_FIRST_PACKET;
+ break;
+ case LEAF_pfTimeoutsIcmpError:
+ pt.timeout = PFTM_ICMP_ERROR_REPLY;
+ break;
+ case LEAF_pfTimeoutsOtherFirst:
+ pt.timeout = PFTM_OTHER_FIRST_PACKET;
+ break;
+ case LEAF_pfTimeoutsOtherSingle:
+ pt.timeout = PFTM_OTHER_SINGLE;
+ break;
+ case LEAF_pfTimeoutsOtherMultiple:
+ pt.timeout = PFTM_OTHER_MULTIPLE;
+ break;
+ case LEAF_pfTimeoutsFragment:
+ pt.timeout = PFTM_FRAG;
+ break;
+ case LEAF_pfTimeoutsInterval:
+ pt.timeout = PFTM_INTERVAL;
+ break;
+ case LEAF_pfTimeoutsAdaptiveStart:
+ pt.timeout = PFTM_ADAPTIVE_START;
+ break;
+ case LEAF_pfTimeoutsAdaptiveEnd:
+ pt.timeout = PFTM_ADAPTIVE_END;
+ break;
+ case LEAF_pfTimeoutsSrcNode:
+ pt.timeout = PFTM_SRC_NODE;
+ break;
+
+ default:
+ return (SNMP_ERR_NOSUCHNAME);
+ }
+
+ if (ioctl(dev, DIOCGETTIMEOUT, &pt)) {
+ syslog(LOG_ERR, "pf_timeouts(): ioctl(): %s",
+ strerror(errno));
+ return (SNMP_ERR_GENERR);
+ }
+
+ val->v.integer = pt.seconds;
+
+ return (SNMP_ERR_NOERROR);
+ }
+
+ abort();
+}
+
+int
+pf_logif(struct snmp_context __unused *ctx, struct snmp_value *val,
+ u_int sub, u_int __unused vindex, enum snmp_op op)
+{
+ asn_subid_t which = val->var.subs[sub - 1];
+ unsigned char str[IFNAMSIZ];
+
+ if (op == SNMP_OP_SET)
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ if (op == SNMP_OP_GET) {
+ if (pfs_refresh() == -1)
+ return (SNMP_ERR_GENERR);
+
+ switch (which) {
+ case LEAF_pfLogInterfaceName:
+ strlcpy(str, pfs.ifname, sizeof str);
+ return (string_get(val, str, strlen(str)));
+ case LEAF_pfLogInterfaceIp4BytesIn:
+ val->v.counter64 = pfs.bcounters[IPV4][IN];
+ break;
+ case LEAF_pfLogInterfaceIp4BytesOut:
+ val->v.counter64 = pfs.bcounters[IPV4][OUT];
+ break;
+ case LEAF_pfLogInterfaceIp4PktsInPass:
+ val->v.counter64 =
+ pfs.pcounters[IPV4][IN][PF_PASS];
+ break;
+ case LEAF_pfLogInterfaceIp4PktsInDrop:
+ val->v.counter64 =
+ pfs.pcounters[IPV4][IN][PF_DROP];
+ break;
+ case LEAF_pfLogInterfaceIp4PktsOutPass:
+ val->v.counter64 =
+ pfs.pcounters[IPV4][OUT][PF_PASS];
+ break;
+ case LEAF_pfLogInterfaceIp4PktsOutDrop:
+ val->v.counter64 =
+ pfs.pcounters[IPV4][OUT][PF_DROP];
+ break;
+ case LEAF_pfLogInterfaceIp6BytesIn:
+ val->v.counter64 = pfs.bcounters[IPV6][IN];
+ break;
+ case LEAF_pfLogInterfaceIp6BytesOut:
+ val->v.counter64 = pfs.bcounters[IPV6][OUT];
+ break;
+ case LEAF_pfLogInterfaceIp6PktsInPass:
+ val->v.counter64 =
+ pfs.pcounters[IPV6][IN][PF_PASS];
+ break;
+ case LEAF_pfLogInterfaceIp6PktsInDrop:
+ val->v.counter64 =
+ pfs.pcounters[IPV6][IN][PF_DROP];
+ break;
+ case LEAF_pfLogInterfaceIp6PktsOutPass:
+ val->v.counter64 =
+ pfs.pcounters[IPV6][OUT][PF_PASS];
+ break;
+ case LEAF_pfLogInterfaceIp6PktsOutDrop:
+ val->v.counter64 =
+ pfs.pcounters[IPV6][OUT][PF_DROP];
+ break;
+
+ default:
+ return (SNMP_ERR_NOSUCHNAME);
+ }
+
+ return (SNMP_ERR_NOERROR);
+ }
+
+ abort();
+}
+
+int
+pf_interfaces(struct snmp_context __unused *ctx, struct snmp_value *val,
+ u_int sub, u_int __unused vindex, enum snmp_op op)
+{
+ asn_subid_t which = val->var.subs[sub - 1];
+
+ if (op == SNMP_OP_SET)
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ if (op == SNMP_OP_GET) {
+ if ((time(NULL) - pfi_table_age) > PFI_TABLE_MAXAGE)
+ if (pfi_refresh() == -1)
+ return (SNMP_ERR_GENERR);
+
+ switch (which) {
+ case LEAF_pfInterfacesIfNumber:
+ val->v.uint32 = pfi_table_count;
+ break;
+
+ default:
+ return (SNMP_ERR_NOSUCHNAME);
+ }
+
+ return (SNMP_ERR_NOERROR);
+ }
+
+ abort();
+}
+
+int
+pf_iftable(struct snmp_context __unused *ctx, struct snmp_value *val,
+ u_int sub, u_int __unused vindex, enum snmp_op op)
+{
+ asn_subid_t which = val->var.subs[sub - 1];
+ struct pfi_entry *e = NULL;
+
+ switch (op) {
+ case SNMP_OP_SET:
+ return (SNMP_ERR_NOT_WRITEABLE);
+ case SNMP_OP_GETNEXT:
+ if ((e = NEXT_OBJECT_INT(&pfi_table,
+ &val->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ val->var.len = sub + 1;
+ val->var.subs[sub] = e->index;
+ break;
+ case SNMP_OP_GET:
+ if (val->var.len - sub != 1)
+ return (SNMP_ERR_NOSUCHNAME);
+ if ((e = pfi_table_find(val->var.subs[sub])) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ break;
+
+ case SNMP_OP_COMMIT:
+ case SNMP_OP_ROLLBACK:
+ default:
+ abort();
+ }
+
+ if ((time(NULL) - pfi_table_age) > PFI_TABLE_MAXAGE)
+ pfi_refresh();
+
+ switch (which) {
+ case LEAF_pfInterfacesIfDescr:
+ return (string_get(val, e->pfi.pfik_name, -1));
+ case LEAF_pfInterfacesIfType:
+ val->v.integer = PFI_IFTYPE_INSTANCE;
+ break;
+ case LEAF_pfInterfacesIfTZero:
+ val->v.uint32 =
+ (time(NULL) - e->pfi.pfik_tzero) * 100;
+ break;
+ case LEAF_pfInterfacesIfRefsState:
+ val->v.uint32 = e->pfi.pfik_states;
+ break;
+ case LEAF_pfInterfacesIfRefsRule:
+ val->v.uint32 = e->pfi.pfik_rules;
+ break;
+ case LEAF_pfInterfacesIf4BytesInPass:
+ val->v.counter64 =
+ e->pfi.pfik_bytes[IPV4][IN][PASS];
+ break;
+ case LEAF_pfInterfacesIf4BytesInBlock:
+ val->v.counter64 =
+ e->pfi.pfik_bytes[IPV4][IN][BLOCK];
+ break;
+ case LEAF_pfInterfacesIf4BytesOutPass:
+ val->v.counter64 =
+ e->pfi.pfik_bytes[IPV4][OUT][PASS];
+ break;
+ case LEAF_pfInterfacesIf4BytesOutBlock:
+ val->v.counter64 =
+ e->pfi.pfik_bytes[IPV4][OUT][BLOCK];
+ break;
+ case LEAF_pfInterfacesIf4PktsInPass:
+ val->v.counter64 =
+ e->pfi.pfik_packets[IPV4][IN][PASS];
+ break;
+ case LEAF_pfInterfacesIf4PktsInBlock:
+ val->v.counter64 =
+ e->pfi.pfik_packets[IPV4][IN][BLOCK];
+ break;
+ case LEAF_pfInterfacesIf4PktsOutPass:
+ val->v.counter64 =
+ e->pfi.pfik_packets[IPV4][OUT][PASS];
+ break;
+ case LEAF_pfInterfacesIf4PktsOutBlock:
+ val->v.counter64 =
+ e->pfi.pfik_packets[IPV4][OUT][BLOCK];
+ break;
+ case LEAF_pfInterfacesIf6BytesInPass:
+ val->v.counter64 =
+ e->pfi.pfik_bytes[IPV6][IN][PASS];
+ break;
+ case LEAF_pfInterfacesIf6BytesInBlock:
+ val->v.counter64 =
+ e->pfi.pfik_bytes[IPV6][IN][BLOCK];
+ break;
+ case LEAF_pfInterfacesIf6BytesOutPass:
+ val->v.counter64 =
+ e->pfi.pfik_bytes[IPV6][OUT][PASS];
+ break;
+ case LEAF_pfInterfacesIf6BytesOutBlock:
+ val->v.counter64 =
+ e->pfi.pfik_bytes[IPV6][OUT][BLOCK];
+ break;
+ case LEAF_pfInterfacesIf6PktsInPass:
+ val->v.counter64 =
+ e->pfi.pfik_packets[IPV6][IN][PASS];
+ break;
+ case LEAF_pfInterfacesIf6PktsInBlock:
+ val->v.counter64 =
+ e->pfi.pfik_packets[IPV6][IN][BLOCK];
+ break;
+ case LEAF_pfInterfacesIf6PktsOutPass:
+ val->v.counter64 =
+ e->pfi.pfik_packets[IPV6][OUT][PASS];
+ break;
+ case LEAF_pfInterfacesIf6PktsOutBlock:
+ val->v.counter64 =
+ e->pfi.pfik_packets[IPV6][OUT][BLOCK];
+ break;
+
+ default:
+ return (SNMP_ERR_NOSUCHNAME);
+ }
+
+ return (SNMP_ERR_NOERROR);
+}
+
+int
+pf_tables(struct snmp_context __unused *ctx, struct snmp_value *val,
+ u_int sub, u_int __unused vindex, enum snmp_op op)
+{
+ asn_subid_t which = val->var.subs[sub - 1];
+
+ if (op == SNMP_OP_SET)
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ if (op == SNMP_OP_GET) {
+ if ((time(NULL) - pft_table_age) > PFT_TABLE_MAXAGE)
+ if (pft_refresh() == -1)
+ return (SNMP_ERR_GENERR);
+
+ switch (which) {
+ case LEAF_pfTablesTblNumber:
+ val->v.uint32 = pft_table_count;
+ break;
+
+ default:
+ return (SNMP_ERR_NOSUCHNAME);
+ }
+
+ return (SNMP_ERR_NOERROR);
+ }
+
+ abort();
+}
+
+int
+pf_tbltable(struct snmp_context __unused *ctx, struct snmp_value *val,
+ u_int sub, u_int __unused vindex, enum snmp_op op)
+{
+ asn_subid_t which = val->var.subs[sub - 1];
+ struct pft_entry *e = NULL;
+
+ switch (op) {
+ case SNMP_OP_SET:
+ return (SNMP_ERR_NOT_WRITEABLE);
+ case SNMP_OP_GETNEXT:
+ if ((e = NEXT_OBJECT_INT(&pft_table,
+ &val->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ val->var.len = sub + 1;
+ val->var.subs[sub] = e->index;
+ break;
+ case SNMP_OP_GET:
+ if (val->var.len - sub != 1)
+ return (SNMP_ERR_NOSUCHNAME);
+ if ((e = pft_table_find(val->var.subs[sub])) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ break;
+
+ case SNMP_OP_COMMIT:
+ case SNMP_OP_ROLLBACK:
+ default:
+ abort();
+ }
+
+ if ((time(NULL) - pft_table_age) > PFT_TABLE_MAXAGE)
+ pft_refresh();
+
+ switch (which) {
+ case LEAF_pfTablesTblDescr:
+ return (string_get(val, e->pft.pfrts_name, -1));
+ case LEAF_pfTablesTblCount:
+ val->v.integer = e->pft.pfrts_cnt;
+ break;
+ case LEAF_pfTablesTblTZero:
+ val->v.uint32 =
+ (time(NULL) - e->pft.pfrts_tzero) * 100;
+ break;
+ case LEAF_pfTablesTblRefsAnchor:
+ val->v.integer =
+ e->pft.pfrts_refcnt[PFR_REFCNT_ANCHOR];
+ break;
+ case LEAF_pfTablesTblRefsRule:
+ val->v.integer =
+ e->pft.pfrts_refcnt[PFR_REFCNT_RULE];
+ break;
+ case LEAF_pfTablesTblEvalMatch:
+ val->v.counter64 = e->pft.pfrts_match;
+ break;
+ case LEAF_pfTablesTblEvalNoMatch:
+ val->v.counter64 = e->pft.pfrts_nomatch;
+ break;
+ case LEAF_pfTablesTblBytesInPass:
+ val->v.counter64 =
+ e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_PASS];
+ break;
+ case LEAF_pfTablesTblBytesInBlock:
+ val->v.counter64 =
+ e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_BLOCK];
+ break;
+ case LEAF_pfTablesTblBytesInXPass:
+ val->v.counter64 =
+ e->pft.pfrts_bytes[PFR_DIR_IN][PFR_OP_XPASS];
+ break;
+ case LEAF_pfTablesTblBytesOutPass:
+ val->v.counter64 =
+ e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_PASS];
+ break;
+ case LEAF_pfTablesTblBytesOutBlock:
+ val->v.counter64 =
+ e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_BLOCK];
+ break;
+ case LEAF_pfTablesTblBytesOutXPass:
+ val->v.counter64 =
+ e->pft.pfrts_bytes[PFR_DIR_OUT][PFR_OP_XPASS];
+ break;
+ case LEAF_pfTablesTblPktsInPass:
+ val->v.counter64 =
+ e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_PASS];
+ break;
+ case LEAF_pfTablesTblPktsInBlock:
+ val->v.counter64 =
+ e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_BLOCK];
+ break;
+ case LEAF_pfTablesTblPktsInXPass:
+ val->v.counter64 =
+ e->pft.pfrts_packets[PFR_DIR_IN][PFR_OP_XPASS];
+ break;
+ case LEAF_pfTablesTblPktsOutPass:
+ val->v.counter64 =
+ e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_PASS];
+ break;
+ case LEAF_pfTablesTblPktsOutBlock:
+ val->v.counter64 =
+ e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_BLOCK];
+ break;
+ case LEAF_pfTablesTblPktsOutXPass:
+ val->v.counter64 =
+ e->pft.pfrts_packets[PFR_DIR_OUT][PFR_OP_XPASS];
+ break;
+
+ default:
+ return (SNMP_ERR_NOSUCHNAME);
+ }
+
+ return (SNMP_ERR_NOERROR);
+}
+
+int
+pf_tbladdr(struct snmp_context __unused *ctx, struct snmp_value __unused *val,
+ u_int __unused sub, u_int __unused vindex, enum snmp_op __unused op)
+{
+ return (SNMP_ERR_GENERR);
+}
+
+int
+pf_altq(struct snmp_context __unused *ctx, struct snmp_value *val,
+ u_int sub, u_int __unused vindex, enum snmp_op op)
+{
+ asn_subid_t which = val->var.subs[sub - 1];
+
+ if (!altq_enabled) {
+ return (SNMP_ERR_NOERROR);
+ }
+
+ if (op == SNMP_OP_SET)
+ return (SNMP_ERR_NOT_WRITEABLE);
+
+ if (op == SNMP_OP_GET) {
+ if ((time(NULL) - pfq_table_age) > PFQ_TABLE_MAXAGE)
+ if (pfq_refresh() == -1)
+ return (SNMP_ERR_GENERR);
+
+ switch (which) {
+ case LEAF_pfAltqQueueNumber:
+ val->v.uint32 = pfq_table_count;
+ break;
+
+ default:
+ return (SNMP_ERR_NOSUCHNAME);
+ }
+
+ return (SNMP_ERR_NOERROR);
+ }
+
+ abort();
+ return (SNMP_ERR_GENERR);
+}
+
+int
+pf_altqq(struct snmp_context __unused *ctx, struct snmp_value *val,
+ u_int sub, u_int __unused vindex, enum snmp_op op)
+{
+ asn_subid_t which = val->var.subs[sub - 1];
+ struct pfq_entry *e = NULL;
+
+ if (!altq_enabled) {
+ return (SNMP_ERR_NOERROR);
+ }
+
+ switch (op) {
+ case SNMP_OP_SET:
+ return (SNMP_ERR_NOT_WRITEABLE);
+ case SNMP_OP_GETNEXT:
+ if ((e = NEXT_OBJECT_INT(&pfq_table,
+ &val->var, sub)) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ val->var.len = sub + 1;
+ val->var.subs[sub] = e->index;
+ break;
+ case SNMP_OP_GET:
+ if (val->var.len - sub != 1)
+ return (SNMP_ERR_NOSUCHNAME);
+ if ((e = pfq_table_find(val->var.subs[sub])) == NULL)
+ return (SNMP_ERR_NOSUCHNAME);
+ break;
+
+ case SNMP_OP_COMMIT:
+ case SNMP_OP_ROLLBACK:
+ default:
+ abort();
+ }
+
+ if ((time(NULL) - pfq_table_age) > PFQ_TABLE_MAXAGE)
+ pfq_refresh();
+
+ switch (which) {
+ case LEAF_pfAltqQueueDescr:
+ return (string_get(val, e->altq.qname, -1));
+ case LEAF_pfAltqQueueParent:
+ return (string_get(val, e->altq.parent, -1));
+ case LEAF_pfAltqQueueScheduler:
+ val->v.integer = e->altq.scheduler;
+ break;
+ case LEAF_pfAltqQueueBandwidth:
+ val->v.uint32 = e->altq.bandwidth;
+ break;
+ case LEAF_pfAltqQueuePriority:
+ val->v.integer = e->altq.priority;
+ break;
+ case LEAF_pfAltqQueueLimit:
+ val->v.integer = e->altq.qlimit;
+ break;
+
+ default:
+ return (SNMP_ERR_NOSUCHNAME);
+ }
+
+ return (SNMP_ERR_NOERROR);
+}
+
+static struct pfi_entry *
+pfi_table_find(u_int idx)
+{
+ struct pfi_entry *e;
+
+ TAILQ_FOREACH(e, &pfi_table, link)
+ if (e->index == idx)
+ return (e);
+ return (NULL);
+}
+
+static struct pfq_entry *
+pfq_table_find(u_int idx)
+{
+ struct pfq_entry *e;
+ TAILQ_FOREACH(e, &pfq_table, link)
+ if (e->index == idx)
+ return (e);
+ return (NULL);
+}
+
+static struct pft_entry *
+pft_table_find(u_int idx)
+{
+ struct pft_entry *e;
+
+ TAILQ_FOREACH(e, &pft_table, link)
+ if (e->index == idx)
+ return (e);
+ return (NULL);
+}
+
+static int
+pfi_refresh(void)
+{
+ struct pfioc_iface io;
+ struct pfi_kif *p = NULL;
+ struct pfi_entry *e;
+ int i, numifs = 1;
+
+ if (started && this_tick <= pf_tick)
+ return (0);
+
+ while (!TAILQ_EMPTY(&pfi_table)) {
+ e = TAILQ_FIRST(&pfi_table);
+ TAILQ_REMOVE(&pfi_table, e, link);
+ free(e);
+ }
+
+ bzero(&io, sizeof(io));
+ io.pfiio_esize = sizeof(struct pfi_kif);
+
+ for (;;) {
+ p = reallocf(p, numifs * sizeof(struct pfi_kif));
+ if (p == NULL) {
+ syslog(LOG_ERR, "pfi_refresh(): reallocf() numifs=%d: %s",
+ numifs, strerror(errno));
+ goto err2;
+ }
+ io.pfiio_size = numifs;
+ io.pfiio_buffer = p;
+
+ if (ioctl(dev, DIOCIGETIFACES, &io)) {
+ syslog(LOG_ERR, "pfi_refresh(): ioctl(): %s",
+ strerror(errno));
+ goto err2;
+ }
+
+ if (numifs >= io.pfiio_size)
+ break;
+
+ numifs = io.pfiio_size;
+ }
+
+ for (i = 0; i < numifs; i++) {
+ e = malloc(sizeof(struct pfi_entry));
+ if (e == NULL)
+ goto err1;
+ e->index = i + 1;
+ memcpy(&e->pfi, p+i, sizeof(struct pfi_kif));
+ TAILQ_INSERT_TAIL(&pfi_table, e, link);
+ }
+
+ pfi_table_age = time(NULL);
+ pfi_table_count = numifs;
+ pf_tick = this_tick;
+
+ free(p);
+ return (0);
+
+err1:
+ while (!TAILQ_EMPTY(&pfi_table)) {
+ e = TAILQ_FIRST(&pfi_table);
+ TAILQ_REMOVE(&pfi_table, e, link);
+ free(e);
+ }
+err2:
+ free(p);
+ return(-1);
+}
+
+static int
+pfq_refresh(void)
+{
+ struct pfioc_altq pa;
+ struct pfq_entry *e;
+ int i, numqs, ticket;
+
+ if (started && this_tick <= pf_tick)
+ return (0);
+
+ while (!TAILQ_EMPTY(&pfq_table)) {
+ e = TAILQ_FIRST(&pfq_table);
+ TAILQ_REMOVE(&pfq_table, e, link);
+ free(e);
+ }
+
+ bzero(&pa, sizeof(pa));
+
+ if (ioctl(dev, DIOCGETALTQS, &pa)) {
+ syslog(LOG_ERR, "pfq_refresh: ioctl(DIOCGETALTQS): %s",
+ strerror(errno));
+ return (-1);
+ }
+
+ numqs = pa.nr;
+ ticket = pa.ticket;
+
+ for (i = 0; i < numqs; i++) {
+ e = malloc(sizeof(struct pfq_entry));
+ if (e == NULL) {
+ syslog(LOG_ERR, "pfq_refresh(): "
+ "malloc(): %s",
+ strerror(errno));
+ goto err;
+ }
+ pa.ticket = ticket;
+ pa.nr = i;
+
+ if (ioctl(dev, DIOCGETALTQ, &pa)) {
+ syslog(LOG_ERR, "pfq_refresh(): "
+ "ioctl(DIOCGETALTQ): %s",
+ strerror(errno));
+ goto err;
+ }
+
+ if (pa.altq.qid > 0) {
+ memcpy(&e->altq, &pa.altq, sizeof(struct pf_altq));
+ e->index = pa.altq.qid;
+ pfq_table_count = i;
+ INSERT_OBJECT_INT_LINK_INDEX(e, &pfq_table, link, index);
+ }
+ }
+
+ pfq_table_age = time(NULL);
+ pf_tick = this_tick;
+
+ return (0);
+err:
+ free(e);
+ while (!TAILQ_EMPTY(&pfq_table)) {
+ e = TAILQ_FIRST(&pfq_table);
+ TAILQ_REMOVE(&pfq_table, e, link);
+ free(e);
+ }
+ return(-1);
+}
+
+static int
+pfs_refresh(void)
+{
+ if (started && this_tick <= pf_tick)
+ return (0);
+
+ bzero(&pfs, sizeof(struct pf_status));
+
+ if (ioctl(dev, DIOCGETSTATUS, &pfs)) {
+ syslog(LOG_ERR, "pfs_refresh(): ioctl(): %s",
+ strerror(errno));
+ return (-1);
+ }
+
+ pf_tick = this_tick;
+ return (0);
+}
+
+static int
+pft_refresh(void)
+{
+ struct pfioc_table io;
+ struct pfr_tstats *t = NULL;
+ struct pft_entry *e;
+ int i, numtbls = 1;
+
+ if (started && this_tick <= pf_tick)
+ return (0);
+
+ while (!TAILQ_EMPTY(&pft_table)) {
+ e = TAILQ_FIRST(&pft_table);
+ TAILQ_REMOVE(&pft_table, e, link);
+ free(e);
+ }
+
+ bzero(&io, sizeof(io));
+ io.pfrio_esize = sizeof(struct pfr_tstats);
+
+ for (;;) {
+ t = reallocf(t, numtbls * sizeof(struct pfr_tstats));
+ if (t == NULL) {
+ syslog(LOG_ERR, "pft_refresh(): reallocf() numtbls=%d: %s",
+ numtbls, strerror(errno));
+ goto err2;
+ }
+ io.pfrio_size = numtbls;
+ io.pfrio_buffer = t;
+
+ if (ioctl(dev, DIOCRGETTSTATS, &io)) {
+ syslog(LOG_ERR, "pft_refresh(): ioctl(): %s",
+ strerror(errno));
+ goto err2;
+ }
+
+ if (numtbls >= io.pfrio_size)
+ break;
+
+ numtbls = io.pfrio_size;
+ }
+
+ for (i = 0; i < numtbls; i++) {
+ e = malloc(sizeof(struct pfr_tstats));
+ if (e == NULL)
+ goto err1;
+ e->index = i + 1;
+ memcpy(&e->pft, t+i, sizeof(struct pfr_tstats));
+ TAILQ_INSERT_TAIL(&pft_table, e, link);
+ }
+
+ pft_table_age = time(NULL);
+ pft_table_count = numtbls;
+ pf_tick = this_tick;
+
+ free(t);
+ return (0);
+err1:
+ while (!TAILQ_EMPTY(&pft_table)) {
+ e = TAILQ_FIRST(&pft_table);
+ TAILQ_REMOVE(&pft_table, e, link);
+ free(e);
+ }
+err2:
+ free(t);
+ return(-1);
+}
+
+/*
+ * check whether altq support is enabled in kernel
+ */
+
+static int
+altq_is_enabled(int pfdev)
+{
+ struct pfioc_altq pa;
+
+ errno = 0;
+ if (ioctl(pfdev, DIOCGETALTQS, &pa)) {
+ if (errno == ENODEV) {
+ syslog(LOG_INFO, "No ALTQ support in kernel\n"
+ "ALTQ related functions disabled\n");
+ return (0);
+ } else
+ syslog(LOG_ERR, "DIOCGETALTQS returned an error: %s",
+ strerror(errno));
+ return (-1);
+ }
+ return (1);
+}
+
+/*
+ * Implement the bsnmpd module interface
+ */
+static int
+pf_init(struct lmodule *mod, int __unused argc, char __unused *argv[])
+{
+ module = mod;
+
+ if ((dev = open("/dev/pf", O_RDONLY)) == -1) {
+ syslog(LOG_ERR, "pf_init(): open(): %s\n",
+ strerror(errno));
+ return (-1);
+ }
+
+ if ((altq_enabled = altq_is_enabled(dev)) == -1) {
+ syslog(LOG_ERR, "pf_init(): altq test failed");
+ return (-1);
+ }
+
+ /* Prepare internal state */
+ TAILQ_INIT(&pfi_table);
+ TAILQ_INIT(&pfq_table);
+ TAILQ_INIT(&pft_table);
+
+ pfi_refresh();
+ if (altq_enabled) {
+ pfq_refresh();
+ }
+
+ pfs_refresh();
+ pft_refresh();
+
+ started = 1;
+
+ return (0);
+}
+
+static int
+pf_fini(void)
+{
+ struct pfi_entry *i1, *i2;
+ struct pfq_entry *q1, *q2;
+ struct pft_entry *t1, *t2;
+
+ /* Empty the list of interfaces */
+ i1 = TAILQ_FIRST(&pfi_table);
+ while (i1 != NULL) {
+ i2 = TAILQ_NEXT(i1, link);
+ free(i1);
+ i1 = i2;
+ }
+
+ /* List of queues */
+ q1 = TAILQ_FIRST(&pfq_table);
+ while (q1 != NULL) {
+ q2 = TAILQ_NEXT(q1, link);
+ free(q1);
+ q1 = q2;
+ }
+
+ /* And the list of tables */
+ t1 = TAILQ_FIRST(&pft_table);
+ while (t1 != NULL) {
+ t2 = TAILQ_NEXT(t1, link);
+ free(t1);
+ t1 = t2;
+ }
+
+ close(dev);
+ return (0);
+}
+
+static void
+pf_dump(void)
+{
+ pfi_refresh();
+ if (altq_enabled) {
+ pfq_refresh();
+ }
+ pft_refresh();
+
+ syslog(LOG_ERR, "Dump: pfi_table_age = %jd",
+ (intmax_t)pfi_table_age);
+ syslog(LOG_ERR, "Dump: pfi_table_count = %d",
+ pfi_table_count);
+
+ syslog(LOG_ERR, "Dump: pfq_table_age = %jd",
+ (intmax_t)pfq_table_age);
+ syslog(LOG_ERR, "Dump: pfq_table_count = %d",
+ pfq_table_count);
+
+ syslog(LOG_ERR, "Dump: pft_table_age = %jd",
+ (intmax_t)pft_table_age);
+
+ syslog(LOG_ERR, "Dump: pft_table_count = %d",
+ pft_table_count);
+}
+
+const struct snmp_module config = {
+ .comment = "This module implements a MIB for the pf packet filter.",
+ .init = pf_init,
+ .fini = pf_fini,
+ .tree = pf_ctree,
+ .dump = pf_dump,
+ .tree_size = pf_CTREE_SIZE,
+};
diff --git a/usr.sbin/bsnmpd/modules/snmp_pf/pf_tree.def b/usr.sbin/bsnmpd/modules/snmp_pf/pf_tree.def
new file mode 100644
index 0000000..003a3ec
--- /dev/null
+++ b/usr.sbin/bsnmpd/modules/snmp_pf/pf_tree.def
@@ -0,0 +1,195 @@
+#
+# Copyright (c) 2005 Philip Paeps <philip@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$
+#
+
+(1 internet
+ (4 private
+ (1 enterprises
+ (12325 fokus
+ (1 begemot
+ (200 begemotPf
+ (1 begemotPfObjects
+ (1 pfStatus
+ (1 pfStatusRunning INTEGER pf_status GET)
+ (2 pfStatusRuntime TIMETICKS pf_status GET)
+ (3 pfStatusDebug INTEGER pf_status GET)
+ (4 pfStatusHostId OCTETSTRING pf_status GET)
+ )
+ (2 pfCounter
+ (1 pfCounterMatch COUNTER64 pf_counter GET)
+ (2 pfCounterBadOffset COUNTER64 pf_counter GET)
+ (3 pfCounterFragment COUNTER64 pf_counter GET)
+ (4 pfCounterShort COUNTER64 pf_counter GET)
+ (5 pfCounterNormalize COUNTER64 pf_counter GET)
+ (6 pfCounterMemDrop COUNTER64 pf_counter GET)
+ )
+ (3 pfStateTable
+ (1 pfStateTableCount UNSIGNED32 pf_statetable GET)
+ (2 pfStateTableSearches COUNTER64 pf_statetable GET)
+ (3 pfStateTableInserts COUNTER64 pf_statetable GET)
+ (4 pfStateTableRemovals COUNTER64 pf_statetable GET)
+ )
+ (4 pfSrcNodes
+ (1 pfSrcNodesCount UNSIGNED32 pf_srcnodes GET)
+ (2 pfSrcNodesSearches COUNTER64 pf_srcnodes GET)
+ (3 pfSrcNodesInserts COUNTER64 pf_srcnodes GET)
+ (4 pfSrcNodesRemovals COUNTER64 pf_srcnodes GET)
+ )
+ (5 pfLimits
+ (1 pfLimitsStates UNSIGNED32 pf_limits GET)
+ (2 pfLimitsSrcNodes UNSIGNED32 pf_limits GET)
+ (3 pfLimitsFrags UNSIGNED32 pf_limits GET)
+ )
+ (6 pfTimeouts
+ (1 pfTimeoutsTcpFirst INTEGER32 pf_timeouts GET)
+ (2 pfTimeoutsTcpOpening INTEGER32 pf_timeouts GET)
+ (3 pfTimeoutsTcpEstablished INTEGER32 pf_timeouts GET)
+ (4 pfTimeoutsTcpClosing INTEGER32 pf_timeouts GET)
+ (5 pfTimeoutsTcpFinWait INTEGER32 pf_timeouts GET)
+ (6 pfTimeoutsTcpClosed INTEGER32 pf_timeouts GET)
+ (7 pfTimeoutsUdpFirst INTEGER32 pf_timeouts GET)
+ (8 pfTimeoutsUdpSingle INTEGER32 pf_timeouts GET)
+ (9 pfTimeoutsUdpMultiple INTEGER32 pf_timeouts GET)
+ (10 pfTimeoutsIcmpFirst INTEGER32 pf_timeouts GET)
+ (11 pfTimeoutsIcmpError INTEGER32 pf_timeouts GET)
+ (12 pfTimeoutsOtherFirst INTEGER32 pf_timeouts GET)
+ (13 pfTimeoutsOtherSingle INTEGER32 pf_timeouts GET)
+ (14 pfTimeoutsOtherMultiple INTEGER32 pf_timeouts GET)
+ (15 pfTimeoutsFragment INTEGER32 pf_timeouts GET)
+ (16 pfTimeoutsInterval INTEGER32 pf_timeouts GET)
+ (17 pfTimeoutsAdaptiveStart INTEGER32 pf_timeouts GET)
+ (18 pfTimeoutsAdaptiveEnd INTEGER32 pf_timeouts GET)
+ (19 pfTimeoutsSrcNode INTEGER32 pf_timeouts GET)
+ )
+ (7 pfLogInterface
+ (1 pfLogInterfaceName OCTETSTRING pf_logif GET)
+ (2 pfLogInterfaceIp4BytesIn COUNTER64 pf_logif GET)
+ (3 pfLogInterfaceIp4BytesOut COUNTER64 pf_logif GET)
+ (4 pfLogInterfaceIp4PktsInPass COUNTER64 pf_logif GET)
+ (5 pfLogInterfaceIp4PktsInDrop COUNTER64 pf_logif GET)
+ (6 pfLogInterfaceIp4PktsOutPass COUNTER64 pf_logif GET)
+ (7 pfLogInterfaceIp4PktsOutDrop COUNTER64 pf_logif GET)
+ (8 pfLogInterfaceIp6BytesIn COUNTER64 pf_logif GET)
+ (9 pfLogInterfaceIp6BytesOut COUNTER64 pf_logif GET)
+ (10 pfLogInterfaceIp6PktsInPass COUNTER64 pf_logif GET)
+ (11 pfLogInterfaceIp6PktsInDrop COUNTER64 pf_logif GET)
+ (12 pfLogInterfaceIp6PktsOutPass COUNTER64 pf_logif GET)
+ (13 pfLogInterfaceIp6PktsOutDrop COUNTER64 pf_logif GET)
+ )
+ (8 pfInterfaces
+ (1 pfInterfacesIfNumber INTEGER32 pf_interfaces GET)
+ (2 pfInterfacesIfTable
+ (1 pfInterfacesIfEntry : INTEGER32 pf_iftable
+ (1 pfInterfacesIfIndex INTEGER32)
+ (2 pfInterfacesIfDescr OCTETSTRING GET)
+ (3 pfInterfacesIfType INTEGER GET)
+ (4 pfInterfacesIfTZero TIMETICKS GET)
+ (5 pfInterfacesIfRefsState UNSIGNED32 GET)
+ (6 pfInterfacesIfRefsRule UNSIGNED32 GET)
+ (7 pfInterfacesIf4BytesInPass COUNTER64 GET)
+ (8 pfInterfacesIf4BytesInBlock COUNTER64 GET)
+ (9 pfInterfacesIf4BytesOutPass COUNTER64 GET)
+ (10 pfInterfacesIf4BytesOutBlock COUNTER64 GET)
+ (11 pfInterfacesIf4PktsInPass COUNTER64 GET)
+ (12 pfInterfacesIf4PktsInBlock COUNTER64 GET)
+ (13 pfInterfacesIf4PktsOutPass COUNTER64 GET)
+ (14 pfInterfacesIf4PktsOutBlock COUNTER64 GET)
+ (15 pfInterfacesIf6BytesInPass COUNTER64 GET)
+ (16 pfInterfacesIf6BytesInBlock COUNTER64 GET)
+ (17 pfInterfacesIf6BytesOutPass COUNTER64 GET)
+ (18 pfInterfacesIf6BytesOutBlock COUNTER64 GET)
+ (19 pfInterfacesIf6PktsInPass COUNTER64 GET)
+ (20 pfInterfacesIf6PktsInBlock COUNTER64 GET)
+ (21 pfInterfacesIf6PktsOutPass COUNTER64 GET)
+ (22 pfInterfacesIf6PktsOutBlock COUNTER64 GET)
+ )
+ )
+ )
+ (9 pfTables
+ (1 pfTablesTblNumber INTEGER32 pf_tables GET)
+ (2 pfTablesTblTable
+ (1 pfTablesTblEntry : INTEGER32 pf_tbltable
+ (1 pfTablesTblIndex INTEGER32)
+ (2 pfTablesTblDescr OCTETSTRING GET)
+ (3 pfTablesTblCount INTEGER32 GET)
+ (4 pfTablesTblTZero TIMETICKS GET)
+ (5 pfTablesTblRefsAnchor INTEGER32 GET)
+ (6 pfTablesTblRefsRule INTEGER32 GET)
+ (7 pfTablesTblEvalMatch COUNTER64 GET)
+ (8 pfTablesTblEvalNoMatch COUNTER64 GET)
+ (9 pfTablesTblBytesInPass COUNTER64 GET)
+ (10 pfTablesTblBytesInBlock COUNTER64 GET)
+ (11 pfTablesTblBytesInXPass COUNTER64 GET)
+ (12 pfTablesTblBytesOutPass COUNTER64 GET)
+ (13 pfTablesTblBytesOutBlock COUNTER64 GET)
+ (14 pfTablesTblBytesOutXPass COUNTER64 GET)
+ (15 pfTablesTblPktsInPass COUNTER64 GET)
+ (16 pfTablesTblPktsInBlock COUNTER64 GET)
+ (17 pfTablesTblPktsInXPass COUNTER64 GET)
+ (18 pfTablesTblPktsOutPass COUNTER64 GET)
+ (19 pfTablesTblPktsOutBlock COUNTER64 GET)
+ (20 pfTablesTblPktsOutXPass COUNTER64 GET)
+ )
+ )
+ (3 pfTablesAddrTable
+ (1 pfTablesAddrEntry : INTEGER32 pf_tbladdr
+ (1 pfTablesAddrIndex INTEGER32)
+ (2 pfTablesAddrNet IPADDRESS GET)
+ (3 pfTablesAddrMask INTEGER32 GET)
+ (4 pfTablesAddrTZero TIMETICKS GET)
+ (5 pfTablesAddrBytesInPass COUNTER64 GET)
+ (6 pfTablesAddrBytesInBlock COUNTER64 GET)
+ (7 pfTablesAddrBytesOutPass COUNTER64 GET)
+ (8 pfTablesAddrBytesOutBlock COUNTER64 GET)
+ (9 pfTablesAddrPktsInPass COUNTER64 GET)
+ (10 pfTablesAddrPktsInBlock COUNTER64 GET)
+ (11 pfTablesAddrPktsOutPass COUNTER64 GET)
+ (12 pfTablesAddrPktsOutBlock COUNTER64 GET)
+ )
+ )
+ )
+ (10 pfAltq
+ (1 pfAltqQueueNumber INTEGER32 pf_altq GET)
+ (2 pfAltqQueueTable
+ (1 pfAltqQueueEntry : INTEGER32 pf_altqq
+ (1 pfAltqQueueIndex INTEGER32)
+ (2 pfAltqQueueDescr OCTETSTRING GET)
+ (3 pfAltqQueueParent OCTETSTRING GET)
+ (4 pfAltqQueueScheduler INTEGER GET)
+ (5 pfAltqQueueBandwidth UNSIGNED32 GET)
+ (6 pfAltqQueuePriority INTEGER32 GET)
+ (7 pfAltqQueueLimit INTEGER32 GET)
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+)
diff --git a/usr.sbin/btxld/Makefile b/usr.sbin/btxld/Makefile
new file mode 100644
index 0000000..6ca452e
--- /dev/null
+++ b/usr.sbin/btxld/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= btxld
+MAN= btxld.8
+SRCS= btxld.c elfh.c
+
+WARNS?= 6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/btxld/btx.h b/usr.sbin/btxld/btx.h
new file mode 100644
index 0000000..86f0ede
--- /dev/null
+++ b/usr.sbin/btxld/btx.h
@@ -0,0 +1,68 @@
+/*
+ * 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 AND CONTRIBUTORS``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED 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 _BTX_H_
+#define _BTX_H_
+
+#include <sys/types.h>
+
+#define BTX_PGSIZE 0x1000 /* Page size */
+#define BTX_PGBASE 0x5000 /* Start of page tables */
+#define BTX_MAXCWR 0x3bc /* Max. btx_pgctl adjustment */
+
+/*
+ * BTX image header.
+ */
+struct btx_hdr {
+ uint8_t btx_machid; /* Machine ID */
+ uint8_t btx_hdrsz; /* Header size */
+ uint8_t btx_magic[3]; /* Magic */
+ uint8_t btx_majver; /* Major version */
+ uint8_t btx_minver; /* Minor version */
+ uint8_t btx_flags; /* Flags */
+ uint16_t btx_pgctl; /* Paging control */
+ uint16_t btx_textsz; /* Text size */
+ uint32_t btx_entry; /* Client entry address */
+};
+
+/* btx_machid */
+#define BTX_I386 0xeb /* Intel i386 or compatible */
+
+/* btx_magic */
+#define BTX_MAG0 'B'
+#define BTX_MAG1 'T'
+#define BTX_MAG2 'X'
+
+/* btx_flags */
+#define BTX_MAPONE 0x80 /* Start mapping at page 1 */
+
+#define BTX_MAPPED(btx) (((btx).btx_pgctl | (BTX_PGSIZE / 4 - 1)) + 1)
+#define BTX_ORIGIN(btx) (BTX_PGBASE + BTX_MAPPED(btx) * 4)
+#define BTX_ENTRY(btx) (BTX_ORIGIN(btx) + 2 + (btx).btx_hdrsz)
+
+#endif /* !_BTX_H_ */
diff --git a/usr.sbin/btxld/btxld.8 b/usr.sbin/btxld/btxld.8
new file mode 100644
index 0000000..5b514f6
--- /dev/null
+++ b/usr.sbin/btxld/btxld.8
@@ -0,0 +1,98 @@
+.\" 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 AND CONTRIBUTORS``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED 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 12, 1998
+.Dt BTXLD 8
+.Os
+.Sh NAME
+.Nm btxld
+.Nd link editor for BTX clients
+.Sh SYNOPSIS
+.Nm
+.Op Fl qv
+.Op Fl b Ar file
+.Op Fl E Ar address
+.Op Fl e Ar address
+.Op Fl f Ar format
+.Op Fl l Ar file
+.Op Fl o Ar filename
+.Op Fl P Ar page
+.Op Fl W Ar page
+.Ar file
+.Sh DESCRIPTION
+The
+.Nm
+utility binds the specified client executable together with a BTX
+loader program and the BTX kernel, and creates a composite object file
+suitable for loading during the boot process.
+.Pp
+The options are:
+.Bl -tag -width indent
+.It Fl q
+Quiet: inhibit warnings.
+.It Fl v
+Verbose: display information about the files processed.
+.It Fl b Ar file
+Specify the BTX kernel to be bound with the client.
+.It Fl E Ar address
+Set the client entry point.
+.It Fl e Ar address
+Set the BTX loader entry point.
+.It Fl f Ar format
+Specify the output format, where
+.Ar format
+is one of
+.Sq bin ,
+.Sq aout ,
+or
+.Sq elf .
+.It Fl l Ar file
+Specify the BTX loader to be bound with the client.
+.It Fl o Ar filename
+Name the output file.
+The default is
+.Dq a.out .
+.It Fl P Ar page
+Specify the first page of the client's segment to be marked
+.Sq present ,
+where
+.Ar page
+may be 0 or 1.
+.It Fl W Ar page
+Specify the first page of the client's segment to be marked
+.Sq writable ,
+where
+.Ar page
+may be 0, and should not exceed the number of pages occupied by the
+combined .text and .data segments of the client image.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr ld 1 ,
+.Xr boot 8
+.Sh AUTHORS
+.An Robert Nordier Aq rnordier@FreeBSD.org .
diff --git a/usr.sbin/btxld/btxld.c b/usr.sbin/btxld/btxld.c
new file mode 100644
index 0000000..43fa4a9
--- /dev/null
+++ b/usr.sbin/btxld/btxld.c
@@ -0,0 +1,575 @@
+/*
+ * 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 AND CONTRIBUTORS``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/endian.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+
+/* XXX make this work as an i386/amd64 cross-tool */
+#include <machine/exec.h>
+#undef __LDPGSZ
+#define __LDPGSZ 4096
+
+#include <netinet/in.h>
+
+#include <a.out.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "btx.h"
+#include "elfh.h"
+
+#define BTX_PATH "/sys/boot/i386/btx"
+
+#define I_LDR 0 /* BTX loader */
+#define I_BTX 1 /* BTX kernel */
+#define I_CLNT 2 /* Client program */
+
+#define F_BIN 0 /* Binary */
+#define F_AOUT 1 /* ZMAGIC a.out */
+#define F_ELF 2 /* 32-bit ELF */
+#define F_CNT 3 /* Number of formats */
+
+#define IMPURE 1 /* Writable text */
+#define MAXU32 0xffffffff /* Maximum unsigned 32-bit quantity */
+
+#define align(x, y) (((x) + (y) - 1) & ~((y) - 1))
+
+struct hdr {
+ uint32_t fmt; /* Format */
+ uint32_t flags; /* Bit flags */
+ uint32_t size; /* Size of file */
+ uint32_t text; /* Size of text segment */
+ uint32_t data; /* Size of data segment */
+ uint32_t bss; /* Size of bss segment */
+ uint32_t org; /* Program origin */
+ uint32_t entry; /* Program entry point */
+};
+
+static const char *const fmtlist[] = {"bin", "aout", "elf"};
+
+static const char binfo[] =
+ "kernel: ver=%u.%02u size=%x load=%x entry=%x map=%uM "
+ "pgctl=%x:%x\n";
+static const char cinfo[] =
+ "client: fmt=%s size=%x text=%x data=%x bss=%x entry=%x\n";
+static const char oinfo[] =
+ "output: fmt=%s size=%x text=%x data=%x org=%x entry=%x\n";
+
+static const char *lname =
+ BTX_PATH "/btxldr/btxldr"; /* BTX loader */
+static const char *bname =
+ BTX_PATH "/btx/btx"; /* BTX kernel */
+static const char *oname =
+ "a.out"; /* Output filename */
+
+static int ppage = -1; /* First page present */
+static int wpage = -1; /* First page writable */
+
+static unsigned int format; /* Output format */
+
+static uint32_t centry; /* Client entry address */
+static uint32_t lentry; /* Loader entry address */
+
+static int Eflag; /* Client entry option */
+
+static int quiet; /* Inhibit warnings */
+static int verbose; /* Display information */
+
+static const char *tname; /* Temporary output file */
+static const char *fname; /* Current input file */
+
+static void cleanup(void);
+static void btxld(const char *);
+static void getbtx(int, struct btx_hdr *);
+static void gethdr(int, struct hdr *);
+static void puthdr(int, struct hdr *);
+static void copy(int, int, size_t, off_t);
+static size_t readx(int, void *, size_t, off_t);
+static void writex(int, const void *, size_t);
+static void seekx(int, off_t);
+static unsigned int optfmt(const char *);
+static uint32_t optaddr(const char *);
+static int optpage(const char *, int);
+static void Warn(const char *, const char *, ...);
+static void usage(void);
+
+/*
+ * A link editor for BTX clients.
+ */
+int
+main(int argc, char *argv[])
+{
+ int c;
+
+ while ((c = getopt(argc, argv, "qvb:E:e:f:l:o:P:W:")) != -1)
+ switch (c) {
+ case 'q':
+ quiet = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'b':
+ bname = optarg;
+ break;
+ case 'E':
+ centry = optaddr(optarg);
+ Eflag = 1;
+ break;
+ case 'e':
+ lentry = optaddr(optarg);
+ break;
+ case 'f':
+ format = optfmt(optarg);
+ break;
+ case 'l':
+ lname = optarg;
+ break;
+ case 'o':
+ oname = optarg;
+ break;
+ case 'P':
+ ppage = optpage(optarg, 1);
+ break;
+ case 'W':
+ wpage = optpage(optarg, BTX_MAXCWR);
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 1)
+ usage();
+ atexit(cleanup);
+ btxld(*argv);
+ return 0;
+}
+
+/*
+ * Clean up after errors.
+ */
+static void
+cleanup(void)
+{
+ if (tname)
+ remove(tname);
+}
+
+/*
+ * Read the input files; write the output file; display information.
+ */
+static void
+btxld(const char *iname)
+{
+ char name[FILENAME_MAX];
+ struct btx_hdr btx, btxle;
+ struct hdr ihdr, ohdr;
+ unsigned int ldr_size, cwr;
+ int fdi[3], fdo, i;
+
+ ldr_size = 0;
+
+ for (i = I_LDR; i <= I_CLNT; i++) {
+ fname = i == I_LDR ? lname : i == I_BTX ? bname : iname;
+ if ((fdi[i] = open(fname, O_RDONLY)) == -1)
+ err(2, "%s", fname);
+ switch (i) {
+ case I_LDR:
+ gethdr(fdi[i], &ihdr);
+ if (ihdr.fmt != F_BIN)
+ Warn(fname, "Loader format is %s; processing as %s",
+ fmtlist[ihdr.fmt], fmtlist[F_BIN]);
+ ldr_size = ihdr.size;
+ break;
+ case I_BTX:
+ getbtx(fdi[i], &btx);
+ break;
+ case I_CLNT:
+ gethdr(fdi[i], &ihdr);
+ if (ihdr.org && ihdr.org != BTX_PGSIZE)
+ Warn(fname,
+ "Client origin is 0x%x; expecting 0 or 0x%x",
+ ihdr.org, BTX_PGSIZE);
+ }
+ }
+ memset(&ohdr, 0, sizeof(ohdr));
+ ohdr.fmt = format;
+ ohdr.text = ldr_size;
+ ohdr.data = btx.btx_textsz + ihdr.size;
+ ohdr.org = lentry;
+ ohdr.entry = lentry;
+ cwr = 0;
+ if (wpage > 0 || (wpage == -1 && !(ihdr.flags & IMPURE))) {
+ if (wpage > 0)
+ cwr = wpage;
+ else {
+ cwr = howmany(ihdr.text, BTX_PGSIZE);
+ if (cwr > BTX_MAXCWR)
+ cwr = BTX_MAXCWR;
+ }
+ }
+ if (ppage > 0 || (ppage && wpage && ihdr.org >= BTX_PGSIZE)) {
+ btx.btx_flags |= BTX_MAPONE;
+ if (!cwr)
+ cwr++;
+ }
+ btx.btx_pgctl -= cwr;
+ btx.btx_entry = Eflag ? centry : ihdr.entry;
+ if ((size_t)snprintf(name, sizeof(name), "%s.tmp", oname) >= sizeof(name))
+ errx(2, "%s: Filename too long", oname);
+ if ((fdo = open(name, O_CREAT | O_TRUNC | O_WRONLY, 0666)) == -1)
+ err(2, "%s", name);
+ if (!(tname = strdup(name)))
+ err(2, NULL);
+ puthdr(fdo, &ohdr);
+ for (i = I_LDR; i <= I_CLNT; i++) {
+ fname = i == I_LDR ? lname : i == I_BTX ? bname : iname;
+ switch (i) {
+ case I_LDR:
+ copy(fdi[i], fdo, ldr_size, 0);
+ seekx(fdo, ohdr.size += ohdr.text);
+ break;
+ case I_BTX:
+ btxle = btx;
+ btxle.btx_pgctl = htole16(btxle.btx_pgctl);
+ btxle.btx_textsz = htole16(btxle.btx_textsz);
+ btxle.btx_entry = htole32(btxle.btx_entry);
+ writex(fdo, &btxle, sizeof(btxle));
+ copy(fdi[i], fdo, btx.btx_textsz - sizeof(btx),
+ sizeof(btx));
+ break;
+ case I_CLNT:
+ copy(fdi[i], fdo, ihdr.size, 0);
+ if (ftruncate(fdo, ohdr.size += ohdr.data))
+ err(2, "%s", tname);
+ }
+ if (close(fdi[i]))
+ err(2, "%s", fname);
+ }
+ if (close(fdo))
+ err(2, "%s", tname);
+ if (rename(tname, oname))
+ err(2, "%s: Can't rename to %s", tname, oname);
+ tname = NULL;
+ if (verbose) {
+ printf(binfo, btx.btx_majver, btx.btx_minver, btx.btx_textsz,
+ BTX_ORIGIN(btx), BTX_ENTRY(btx), BTX_MAPPED(btx) *
+ BTX_PGSIZE / 0x100000, !!(btx.btx_flags & BTX_MAPONE),
+ BTX_MAPPED(btx) - btx.btx_pgctl - BTX_PGBASE /
+ BTX_PGSIZE - BTX_MAPPED(btx) * 4 / BTX_PGSIZE);
+ printf(cinfo, fmtlist[ihdr.fmt], ihdr.size, ihdr.text,
+ ihdr.data, ihdr.bss, ihdr.entry);
+ printf(oinfo, fmtlist[ohdr.fmt], ohdr.size, ohdr.text,
+ ohdr.data, ohdr.org, ohdr.entry);
+ }
+}
+
+/*
+ * Read BTX file header.
+ */
+static void
+getbtx(int fd, struct btx_hdr * btx)
+{
+ if (readx(fd, btx, sizeof(*btx), 0) != sizeof(*btx) ||
+ btx->btx_magic[0] != BTX_MAG0 ||
+ btx->btx_magic[1] != BTX_MAG1 ||
+ btx->btx_magic[2] != BTX_MAG2)
+ errx(1, "%s: Not a BTX kernel", fname);
+ btx->btx_pgctl = le16toh(btx->btx_pgctl);
+ btx->btx_textsz = le16toh(btx->btx_textsz);
+ btx->btx_entry = le32toh(btx->btx_entry);
+}
+
+/*
+ * Get file size and read a.out or ELF header.
+ */
+static void
+gethdr(int fd, struct hdr *hdr)
+{
+ struct stat sb;
+ const struct exec *ex;
+ const Elf32_Ehdr *ee;
+ const Elf32_Phdr *ep;
+ void *p;
+ unsigned int fmt, x, n, i;
+
+ memset(hdr, 0, sizeof(*hdr));
+ if (fstat(fd, &sb))
+ err(2, "%s", fname);
+ if (sb.st_size > MAXU32)
+ errx(1, "%s: Too big", fname);
+ hdr->size = sb.st_size;
+ if (!hdr->size)
+ return;
+ if ((p = mmap(NULL, hdr->size, PROT_READ, MAP_SHARED, fd,
+ 0)) == MAP_FAILED)
+ err(2, "%s", fname);
+ for (fmt = F_CNT - 1; !hdr->fmt && fmt; fmt--)
+ switch (fmt) {
+ case F_AOUT:
+ ex = p;
+ if (hdr->size >= sizeof(struct exec) && !N_BADMAG(*ex)) {
+ hdr->fmt = fmt;
+ x = N_GETMAGIC(*ex);
+ if (x == OMAGIC || x == NMAGIC) {
+ if (x == NMAGIC)
+ Warn(fname, "Treating %s NMAGIC as OMAGIC",
+ fmtlist[fmt]);
+ hdr->flags |= IMPURE;
+ }
+ hdr->text = le32toh(ex->a_text);
+ hdr->data = le32toh(ex->a_data);
+ hdr->bss = le32toh(ex->a_bss);
+ hdr->entry = le32toh(ex->a_entry);
+ if (le32toh(ex->a_entry) >= BTX_PGSIZE)
+ hdr->org = BTX_PGSIZE;
+ }
+ break;
+ case F_ELF:
+ ee = p;
+ if (hdr->size >= sizeof(Elf32_Ehdr) && IS_ELF(*ee)) {
+ hdr->fmt = fmt;
+ for (n = i = 0; i < le16toh(ee->e_phnum); i++) {
+ ep = (void *)((uint8_t *)p + le32toh(ee->e_phoff) +
+ le16toh(ee->e_phentsize) * i);
+ if (le32toh(ep->p_type) == PT_LOAD)
+ switch (n++) {
+ case 0:
+ hdr->text = le32toh(ep->p_filesz);
+ hdr->org = le32toh(ep->p_paddr);
+ if (le32toh(ep->p_flags) & PF_W)
+ hdr->flags |= IMPURE;
+ break;
+ case 1:
+ hdr->data = le32toh(ep->p_filesz);
+ hdr->bss = le32toh(ep->p_memsz) -
+ le32toh(ep->p_filesz);
+ break;
+ case 2:
+ Warn(fname,
+ "Ignoring extra %s PT_LOAD segments",
+ fmtlist[fmt]);
+ }
+ }
+ hdr->entry = le32toh(ee->e_entry);
+ }
+ }
+ if (munmap(p, hdr->size))
+ err(2, "%s", fname);
+}
+
+/*
+ * Write a.out or ELF header.
+ */
+static void
+puthdr(int fd, struct hdr *hdr)
+{
+ struct exec ex;
+ struct elfh eh;
+
+ switch (hdr->fmt) {
+ case F_AOUT:
+ memset(&ex, 0, sizeof(ex));
+ N_SETMAGIC(ex, ZMAGIC, MID_ZERO, 0);
+ hdr->text = N_ALIGN(ex, hdr->text);
+ ex.a_text = htole32(hdr->text);
+ hdr->data = N_ALIGN(ex, hdr->data);
+ ex.a_data = htole32(hdr->data);
+ ex.a_entry = htole32(hdr->entry);
+ writex(fd, &ex, sizeof(ex));
+ hdr->size = N_ALIGN(ex, sizeof(ex));
+ seekx(fd, hdr->size);
+ break;
+ case F_ELF:
+ eh = elfhdr;
+ eh.e.e_entry = htole32(hdr->entry);
+ eh.p[0].p_vaddr = eh.p[0].p_paddr = htole32(hdr->org);
+ eh.p[0].p_filesz = eh.p[0].p_memsz = htole32(hdr->text);
+ eh.p[1].p_offset = htole32(le32toh(eh.p[0].p_offset) +
+ le32toh(eh.p[0].p_filesz));
+ eh.p[1].p_vaddr = eh.p[1].p_paddr =
+ htole32(align(le32toh(eh.p[0].p_paddr) + le32toh(eh.p[0].p_memsz),
+ 4));
+ eh.p[1].p_filesz = eh.p[1].p_memsz = htole32(hdr->data);
+ eh.sh[2].sh_addr = eh.p[0].p_vaddr;
+ eh.sh[2].sh_offset = eh.p[0].p_offset;
+ eh.sh[2].sh_size = eh.p[0].p_filesz;
+ eh.sh[3].sh_addr = eh.p[1].p_vaddr;
+ eh.sh[3].sh_offset = eh.p[1].p_offset;
+ eh.sh[3].sh_size = eh.p[1].p_filesz;
+ writex(fd, &eh, sizeof(eh));
+ hdr->size = sizeof(eh);
+ }
+}
+
+/*
+ * Safe copy from input file to output file.
+ */
+static void
+copy(int fdi, int fdo, size_t nbyte, off_t offset)
+{
+ char buf[8192];
+ size_t n;
+
+ while (nbyte) {
+ if ((n = sizeof(buf)) > nbyte)
+ n = nbyte;
+ if (readx(fdi, buf, n, offset) != n)
+ errx(2, "%s: Short read", fname);
+ writex(fdo, buf, n);
+ nbyte -= n;
+ offset = -1;
+ }
+}
+
+/*
+ * Safe read from input file.
+ */
+static size_t
+readx(int fd, void *buf, size_t nbyte, off_t offset)
+{
+ ssize_t n;
+
+ if (offset != -1 && lseek(fd, offset, SEEK_SET) != offset)
+ err(2, "%s", fname);
+ if ((n = read(fd, buf, nbyte)) == -1)
+ err(2, "%s", fname);
+ return n;
+}
+
+/*
+ * Safe write to output file.
+ */
+static void
+writex(int fd, const void *buf, size_t nbyte)
+{
+ ssize_t n;
+
+ if ((n = write(fd, buf, nbyte)) == -1)
+ err(2, "%s", tname);
+ if ((size_t)n != nbyte)
+ errx(2, "%s: Short write", tname);
+}
+
+/*
+ * Safe seek in output file.
+ */
+static void
+seekx(int fd, off_t offset)
+{
+ if (lseek(fd, offset, SEEK_SET) != offset)
+ err(2, "%s", tname);
+}
+
+/*
+ * Convert an option argument to a format code.
+ */
+static unsigned int
+optfmt(const char *arg)
+{
+ unsigned int i;
+
+ for (i = 0; i < F_CNT && strcmp(arg, fmtlist[i]); i++);
+ if (i == F_CNT)
+ errx(1, "%s: Unknown format", arg);
+ return i;
+}
+
+/*
+ * Convert an option argument to an address.
+ */
+static uint32_t
+optaddr(const char *arg)
+{
+ char *s;
+ unsigned long x;
+
+ errno = 0;
+ x = strtoul(arg, &s, 0);
+ if (errno || !*arg || *s || x > MAXU32)
+ errx(1, "%s: Illegal address", arg);
+ return x;
+}
+
+/*
+ * Convert an option argument to a page number.
+ */
+static int
+optpage(const char *arg, int hi)
+{
+ char *s;
+ long x;
+
+ errno = 0;
+ x = strtol(arg, &s, 0);
+ if (errno || !*arg || *s || x < 0 || x > hi)
+ errx(1, "%s: Illegal page number", arg);
+ return x;
+}
+
+/*
+ * Display a warning.
+ */
+static void
+Warn(const char *locus, const char *fmt, ...)
+{
+ va_list ap;
+ char *s;
+
+ if (!quiet) {
+ asprintf(&s, "%s: Warning: %s", locus, fmt);
+ va_start(ap, fmt);
+ vwarnx(s, ap);
+ va_end(ap);
+ free(s);
+ }
+}
+
+/*
+ * Display usage information.
+ */
+static void
+usage(void)
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: btxld [-qv] [-b file] [-E address] [-e address] [-f format]",
+ " [-l file] [-o filename] [-P page] [-W page] file");
+ exit(1);
+}
diff --git a/usr.sbin/btxld/elfh.c b/usr.sbin/btxld/elfh.c
new file mode 100644
index 0000000..2790d5a
--- /dev/null
+++ b/usr.sbin/btxld/elfh.c
@@ -0,0 +1,125 @@
+/*
+ * 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 AND CONTRIBUTORS``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED 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/endian.h>
+
+#include <stddef.h>
+#include "elfh.h"
+
+#define SET_ME 0xeeeeeeee /* filled in by btxld */
+
+/*
+ * ELF header template.
+ */
+const struct elfh elfhdr = {
+ {
+ {
+ ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3, /* e_ident */
+ ELFCLASS32, ELFDATA2LSB, EV_CURRENT, 0,
+ 'F', 'r', 'e', 'e', 'B', 'S', 'D', 0
+ },
+ htole16(ET_EXEC), /* e_type */
+ htole16(EM_386), /* e_machine */
+ htole32(EV_CURRENT), /* e_version */
+ htole32(SET_ME), /* e_entry */
+ htole32(offsetof(struct elfh, p)), /* e_phoff */
+ htole32(offsetof(struct elfh, sh)), /* e_shoff */
+ 0, /* e_flags */
+ htole16(sizeof(elfhdr.e)), /* e_ehsize */
+ htole16(sizeof(elfhdr.p[0])), /* e_phentsize */
+ htole16(sizeof(elfhdr.p) / sizeof(elfhdr.p[0])), /* e_phnum */
+ htole16(sizeof(elfhdr.sh[0])), /* e_shentsize */
+ htole16(sizeof(elfhdr.sh) / sizeof(elfhdr.sh[0])), /* e_shnum */
+ htole16(1) /* e_shstrndx */
+ },
+ {
+ {
+ htole32(PT_LOAD), /* p_type */
+ htole32(sizeof(elfhdr)), /* p_offset */
+ htole32(SET_ME), /* p_vaddr */
+ htole32(SET_ME), /* p_paddr */
+ htole32(SET_ME), /* p_filesz */
+ htole32(SET_ME), /* p_memsz */
+ htole32(PF_R | PF_X), /* p_flags */
+ htole32(0x1000) /* p_align */
+ },
+ {
+ htole32(PT_LOAD), /* p_type */
+ htole32(SET_ME), /* p_offset */
+ htole32(SET_ME), /* p_vaddr */
+ htole32(SET_ME), /* p_paddr */
+ htole32(SET_ME), /* p_filesz */
+ htole32(SET_ME), /* p_memsz */
+ htole32(PF_R | PF_W), /* p_flags */
+ htole32(0x1000) /* p_align */
+ }
+ },
+ {
+ {
+ 0, htole32(SHT_NULL), 0, 0, 0, 0, htole32(SHN_UNDEF), 0, 0, 0
+ },
+ {
+ htole32(1), /* sh_name */
+ htole32(SHT_STRTAB), /* sh_type */
+ 0, /* sh_flags */
+ 0, /* sh_addr */
+ htole32(offsetof(struct elfh, shstrtab)), /* sh_offset */
+ htole32(sizeof(elfhdr.shstrtab)), /* sh_size */
+ htole32(SHN_UNDEF), /* sh_link */
+ 0, /* sh_info */
+ htole32(1), /* sh_addralign */
+ 0 /* sh_entsize */
+ },
+ {
+ htole32(0xb), /* sh_name */
+ htole32(SHT_PROGBITS), /* sh_type */
+ htole32(SHF_EXECINSTR | SHF_ALLOC), /* sh_flags */
+ htole32(SET_ME), /* sh_addr */
+ htole32(SET_ME), /* sh_offset */
+ htole32(SET_ME), /* sh_size */
+ htole32(SHN_UNDEF), /* sh_link */
+ 0, /* sh_info */
+ htole32(4), /* sh_addralign */
+ 0 /* sh_entsize */
+ },
+ {
+ htole32(0x11), /* sh_name */
+ htole32(SHT_PROGBITS), /* sh_type */
+ htole32(SHF_ALLOC | SHF_WRITE), /* sh_flags */
+ htole32(SET_ME), /* sh_addr */
+ htole32(SET_ME), /* sh_offset */
+ htole32(SET_ME), /* sh_size */
+ htole32(SHN_UNDEF), /* sh_link */
+ 0, /* sh_info */
+ htole32(4), /* sh_addralign */
+ 0 /* sh_entsize */
+ }
+ },
+ "\0.shstrtab\0.text\0.data" /* shstrtab */
+};
diff --git a/usr.sbin/btxld/elfh.h b/usr.sbin/btxld/elfh.h
new file mode 100644
index 0000000..a4ab134
--- /dev/null
+++ b/usr.sbin/btxld/elfh.h
@@ -0,0 +1,38 @@
+/*
+ * 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 AND CONTRIBUTORS``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED 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/elf32.h>
+
+struct elfh {
+ Elf32_Ehdr e; /* ELF header */
+ Elf32_Phdr p[2]; /* program header */
+ Elf32_Shdr sh[4]; /* section header */
+ char shstrtab[28]; /* section header string table */
+};
+
+extern const struct elfh elfhdr; /* ELF header template */
diff --git a/usr.sbin/burncd/Makefile b/usr.sbin/burncd/Makefile
new file mode 100644
index 0000000..bd4f285
--- /dev/null
+++ b/usr.sbin/burncd/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= burncd
+MAN= burncd.8
+
+WARNS?= 6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/burncd/burncd.8 b/usr.sbin/burncd/burncd.8
new file mode 100644
index 0000000..18c1bc4
--- /dev/null
+++ b/usr.sbin/burncd/burncd.8
@@ -0,0 +1,220 @@
+.\"
+.\" 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,
+.\" 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.
+.\" 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 2, 2005
+.Os
+.Dt BURNCD 8
+.Sh NAME
+.Nm burncd
+.Nd control the ATAPI CD-R/RW driver
+.Sh SYNOPSIS
+.Nm
+.Op Fl deFlmnpqtv
+.Op Fl f Ar device
+.Op Fl s Ar speed
+.Op Ar command
+.Op Ar command Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to burn CD-R/RW media using the ATAPI cd driver.
+.Pp
+Available options and operands:
+.Pp
+.Bl -tag -width XXXXXXXXXXXX
+.It Fl d
+burn the CD-R/RW in DAO (disk at once) mode.
+.It Fl e
+eject the medium when done.
+.It Fl f Ar device
+set the device to use for the burning process.
+.It Fl F
+force operation regardless of warnings.
+.It Fl l
+read a list of image files from filename.
+.It Fl m
+close disk in multisession mode (otherwise disk is closed as singlesession).
+.It Fl n
+do not write gaps between data tracks in DAO mode.
+.It Fl p
+use preemphasis on audio tracks.
+.It Fl q
+quiet, do not print progress messages.
+.It Fl s Ar speed
+set the speed of the burner device.
+Defaults to 4.
+Specify
+.Dq Li max
+to use the drive's fastest speed.
+.It Fl t
+test write, do not actually write on the media.
+.It Fl v
+verbose, print extra progress messages.
+.El
+.Pp
+.Ar command
+may be one of:
+.Pp
+.Bl -tag -width XXXXXXXXXXXX
+.It Cm msinfo
+Show the first LBA of the last track on the media
+and the next writeable address on the media for use with the
+.Xr mkisofs 8 Ns 's Pq Pa ports/sysutils/cdrtools
+.Fl C
+switch when adding additional data to ISO file systems with extra sessions.
+.It Cm blank
+Blank a CD-RW medium.
+This uses the fast blanking method, so data are not physically overwritten,
+only those areas that make the media appear blank for further usage are erased.
+.It Cm eject
+Eject the medium when done.
+This is equivalent to the
+.Fl e
+option.
+.It Cm erase
+Erase a CD-RW medium.
+This erases the entire media.
+Can take up to 1 hour to finish.
+.It Cm format Brq Cm dvd+rw | dvd-rw
+Formats a DVD+RW or DVD-RW media to the default max size and 2048 byte blocks.
+This operation can take a long time to finish.
+Progress reporting is done during the process.
+.It Cm fixate
+Fixate the medium so that the TOC is generated and the media can be used
+in an ordinary CD drive.
+The driver defaults to creating singlesession media (see
+.Fl m
+option).
+Ignored in DAO mode (see
+.Fl d
+option).
+.It Cm raw | audio
+Set the write mode to produce audio (raw mode) tracks for the following
+images on the command line.
+.It Cm data | mode1
+Set the write mode to produce data (mode1) tracks for the following
+image files
+on the command line.
+.It Cm mode2
+Set the write mode to produce data (mode2) tracks for the following
+image files
+on the command line.
+.It Cm XAmode1
+Set the write mode to produce data (XAmode1) tracks for the following image
+files on the command line.
+.It Cm XAmode2
+Set the write mode to produce data (XAmode2) tracks for the following image
+files on the command line.
+.It Cm vcd
+Set the write mode to produce VCD/SVCD tracks for the following image files
+on the command line.
+This automatically sets DAO
+.Pq Fl d
+and
+.Dq "no gaps"
+.Pq Fl n
+modes.
+.It Cm dvdrw
+Set the write mode to write a DVD+RW from the following image.
+DVDs only have one track.
+.It Ar file
+All other arguments are treated as filenames of images to write to the media,
+or in case the
+.Fl l
+option is used as files containing lists of images.
+.El
+.Pp
+Files whose length are not a multiple of the current media blocksize are
+quietly zero padded to fit the blocksize requirement.
+The conventional filename
+.Fl
+refers to stdin, and can only be used once.
+.Sh ENVIRONMENT
+The following environment variables affect the execution of
+.Nm :
+.Bl -tag -width ".Ev CDROM"
+.It Ev CDROM
+The CD device to use if one is not specified with the
+.Fl f
+flag.
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /dev/acd0"
+.It Pa /dev/acd0
+The default device, if not overridden by the
+.Ev CDROM
+environment variable or the
+.Fl f
+option.
+.El
+.Sh EXAMPLES
+The typical usage for burning a data CD-R:
+.Pp
+.Dl "burncd -f /dev/acd0 data file1 fixate"
+.Pp
+The typical usage for burning an audio CD-R:
+.Pp
+.Dl "burncd -f /dev/acd0 audio file1 file2 file3 fixate"
+.Pp
+The typical usage for burning an audio CD-R in DAO mode:
+.Pp
+.Dl "burncd -f /dev/acd0 -d audio file1 file2 file3"
+.Pp
+The typical usage for burning a mixed mode CD-R:
+.Pp
+.Dl "burncd -f /dev/acd0 data file1 audio file2 file3 fixate"
+.Pp
+The typical usage for burning from a compressed image file on stdin:
+.Pp
+.Dl "gunzip -c file.iso.gz | burncd -f /dev/acd0 data - fixate"
+.Pp
+In the examples above, the files burned to data CD-Rs are assumed to
+be ISO9660 file systems.
+.Xr mkisofs 8 ,
+available in the
+.Fx
+Ports Collection,
+as part of the
+.Pa sysutils/cdrtools
+port, is commonly used to create ISO9660 file system images
+from a given directory tree.
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 4.0 .
+.Sh AUTHORS
+The
+.Nm
+utility and this manpage was contributed by
+.An S\(/oren Schmidt ,
+Denmark
+.Aq sos@FreeBSD.org .
+.Sh BUGS
+Probably, please report when found.
diff --git a/usr.sbin/burncd/burncd.c b/usr.sbin/burncd/burncd.c
new file mode 100644
index 0000000..c4d1648
--- /dev/null
+++ b/usr.sbin/burncd/burncd.c
@@ -0,0 +1,735 @@
+/*-
+ * 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,
+ * 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.
+ * 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$
+ */
+
+#include <unistd.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <err.h>
+#include <sysexits.h>
+#include <fcntl.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/cdio.h>
+#include <sys/cdrio.h>
+#include <sys/dvdio.h>
+#include <sys/param.h>
+#include <arpa/inet.h>
+
+#define BLOCKS 16
+
+struct track_info {
+ int file;
+ char file_name[MAXPATHLEN + 1];
+ off_t file_size;
+ int block_size;
+ int block_type;
+ int pregap;
+ int addr;
+};
+static struct track_info tracks[100];
+static int quiet, verbose, saved_block_size, notracks;
+static volatile sig_atomic_t global_fd_for_cleanup;
+
+void add_track(char *, int, int, int);
+void do_DAO(int fd, int, int);
+void do_TAO(int fd, int, int, int);
+void do_format(int, int, char *);
+int write_file(int fd, struct track_info *);
+int roundup_blocks(struct track_info *);
+void cue_ent(struct cdr_cue_entry *, int, int, int, int, int, int, int);
+void cleanup(int);
+void cleanup_flush(void);
+void cleanup_signal(int);
+void usage(void);
+
+int
+main(int argc, char **argv)
+{
+ int arg, addr, ch, fd;
+ int dao = 0, eject = 0, fixate = 0, list = 0, multi = 0, preemp = 0;
+ int nogap = 0, speed = 4 * 177, test_write = 0, force = 0;
+ int block_size = 0, block_type = 0, cdopen = 0, dvdrw = 0;
+ const char *dev;
+
+ if ((dev = getenv("CDROM")) == NULL)
+ dev = "/dev/acd0";
+
+ while ((ch = getopt(argc, argv, "def:Flmnpqs:tv")) != -1) {
+ switch (ch) {
+ case 'd':
+ dao = 1;
+ break;
+
+ case 'e':
+ eject = 1;
+ break;
+
+ case 'f':
+ dev = optarg;
+ break;
+
+ case 'F':
+ force = 1;
+ break;
+
+ case 'l':
+ list = 1;
+ break;
+
+ case 'm':
+ multi = 1;
+ break;
+
+ case 'n':
+ nogap = 1;
+ break;
+
+ case 'p':
+ preemp = 1;
+ break;
+
+ case 'q':
+ quiet = 1;
+ break;
+
+ case 's':
+ if (strcasecmp("max", optarg) == 0)
+ speed = CDR_MAX_SPEED;
+ else
+ speed = atoi(optarg) * 177;
+ if (speed <= 0)
+ errx(EX_USAGE, "Invalid speed: %s", optarg);
+ break;
+
+ case 't':
+ test_write = 1;
+ break;
+
+ case 'v':
+ verbose = 1;
+ break;
+
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0)
+ usage();
+
+ if ((fd = open(dev, O_RDWR, 0)) < 0)
+ err(EX_NOINPUT, "open(%s)", dev);
+
+ if (ioctl(fd, CDRIOCGETBLOCKSIZE, &saved_block_size) < 0)
+ err(EX_IOERR, "ioctl(CDRIOCGETBLOCKSIZE)");
+
+ if (ioctl(fd, CDRIOCWRITESPEED, &speed) < 0)
+ err(EX_IOERR, "ioctl(CDRIOCWRITESPEED)");
+
+ global_fd_for_cleanup = fd;
+ err_set_exit(cleanup);
+ signal(SIGHUP, cleanup_signal);
+ signal(SIGINT, cleanup_signal);
+ signal(SIGTERM, cleanup_signal);
+
+ for (arg = 0; arg < argc; arg++) {
+ if (!strcasecmp(argv[arg], "fixate")) {
+ fixate = 1;
+ continue;
+ }
+ if (!strcasecmp(argv[arg], "eject")) {
+ eject = 1;
+ break;
+ }
+ if (!strcasecmp(argv[arg], "msinfo")) {
+ struct ioc_read_toc_single_entry entry;
+ struct ioc_toc_header header;
+
+ if (ioctl(fd, CDIOREADTOCHEADER, &header) < 0)
+ err(EX_IOERR, "ioctl(CDIOREADTOCHEADER)");
+ bzero(&entry, sizeof(struct ioc_read_toc_single_entry));
+ entry.address_format = CD_LBA_FORMAT;
+ entry.track = header.ending_track;
+ if (ioctl(fd, CDIOREADTOCENTRY, &entry) < 0)
+ err(EX_IOERR, "ioctl(CDIOREADTOCENTRY)");
+ if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &addr) < 0)
+ err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
+ fprintf(stdout, "%d,%d\n",
+ ntohl(entry.entry.addr.lba), addr);
+
+ break;
+ }
+ if ((!strcasecmp(argv[arg], "erase") ||
+ !strcasecmp(argv[arg], "blank")) && !test_write) {
+ int blank, pct, last = 0;
+
+ if (!strcasecmp(argv[arg], "erase"))
+ blank = CDR_B_ALL;
+ else
+ blank = CDR_B_MIN;
+ if (!quiet)
+ fprintf(stderr, "%sing CD, please wait..\r",
+ blank == CDR_B_ALL ? "eras" : "blank");
+
+ if (ioctl(fd, CDRIOCBLANK, &blank) < 0)
+ err(EX_IOERR, "ioctl(CDRIOCBLANK)");
+ while (1) {
+ sleep(1);
+ if (ioctl(fd, CDRIOCGETPROGRESS, &pct) == -1)
+ err(EX_IOERR,"ioctl(CDRIOGETPROGRESS)");
+ if (pct > 0 && !quiet)
+ fprintf(stderr,
+ "%sing CD - %d %% done \r",
+ blank == CDR_B_ALL ?
+ "eras" : "blank", pct);
+ if (pct == 100 || (pct == 0 && last > 90))
+ break;
+ last = pct;
+ }
+ if (!quiet)
+ printf("\n");
+ continue;
+ }
+ if (!strcasecmp(argv[arg], "format") && !test_write) {
+ if (arg + 1 < argc &&
+ (!strcasecmp(argv[arg + 1], "dvd+rw") ||
+ !strcasecmp(argv[arg + 1], "dvd-rw")))
+ do_format(fd, force, argv[arg + 1]);
+ else
+ errx(EX_NOINPUT, "format media type invalid");
+ arg++;
+ continue;
+ }
+ if (!strcasecmp(argv[arg], "audio") || !strcasecmp(argv[arg], "raw")) {
+ block_type = CDR_DB_RAW;
+ block_size = 2352;
+ continue;
+ }
+ if (!strcasecmp(argv[arg], "data") || !strcasecmp(argv[arg], "mode1")) {
+ block_type = CDR_DB_ROM_MODE1;
+ block_size = 2048;
+ continue;
+ }
+ if (!strcasecmp(argv[arg], "mode2")) {
+ block_type = CDR_DB_ROM_MODE2;
+ block_size = 2336;
+ continue;
+ }
+ if (!strcasecmp(argv[arg], "xamode1")) {
+ block_type = CDR_DB_XA_MODE1;
+ block_size = 2048;
+ continue;
+ }
+ if (!strcasecmp(argv[arg], "xamode2")) {
+ block_type = CDR_DB_XA_MODE2_F2;
+ block_size = 2324;
+ continue;
+ }
+ if (!strcasecmp(argv[arg], "vcd")) {
+ block_type = CDR_DB_XA_MODE2_F2;
+ block_size = 2352;
+ dao = 1;
+ nogap = 1;
+ continue;
+ }
+ if (!strcasecmp(argv[arg], "dvdrw")) {
+ block_type = CDR_DB_ROM_MODE1;
+ block_size = 2048;
+ dvdrw = 1;
+ continue;
+ }
+
+ if (!block_size)
+ errx(EX_NOINPUT, "no data format selected");
+ if (list) {
+ char file_buf[MAXPATHLEN + 1], *eol;
+ FILE *fp;
+
+ if ((fp = fopen(argv[arg], "r")) == NULL)
+ err(EX_NOINPUT, "fopen(%s)", argv[arg]);
+
+ while (fgets(file_buf, sizeof(file_buf), fp) != NULL) {
+ if (*file_buf == '#' || *file_buf == '\n')
+ continue;
+ if ((eol = strchr(file_buf, '\n')))
+ *eol = '\0';
+ add_track(file_buf, block_size, block_type, nogap);
+ }
+ if (feof(fp))
+ fclose(fp);
+ else
+ err(EX_IOERR, "fgets(%s)", file_buf);
+ }
+ else
+ add_track(argv[arg], block_size, block_type, nogap);
+ }
+ if (notracks) {
+ if (dvdrw && notracks > 1)
+ errx(EX_USAGE, "DVD's only have 1 track");
+ if (ioctl(fd, CDIOCSTART, 0) < 0)
+ err(EX_IOERR, "ioctl(CDIOCSTART)");
+ if (!cdopen) {
+ if (ioctl(fd, CDRIOCINITWRITER, &test_write) < 0)
+ err(EX_IOERR, "ioctl(CDRIOCINITWRITER)");
+ cdopen = 1;
+ }
+ if (dao)
+ do_DAO(fd, test_write, multi);
+ else
+ do_TAO(fd, test_write, preemp, dvdrw);
+ }
+ if (!test_write && fixate && !dao && !dvdrw) {
+ if (!quiet)
+ fprintf(stderr, "fixating CD, please wait..\n");
+ if (ioctl(fd, CDRIOCFIXATE, &multi) < 0)
+ err(EX_IOERR, "ioctl(CDRIOCFIXATE)");
+ }
+
+ if (ioctl(fd, CDRIOCSETBLOCKSIZE, &saved_block_size) < 0) {
+ err_set_exit(NULL);
+ err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
+ }
+
+ if (eject)
+ if (ioctl(fd, CDIOCEJECT) < 0)
+ err(EX_IOERR, "ioctl(CDIOCEJECT)");
+
+ signal(SIGHUP, SIG_DFL);
+ signal(SIGINT, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ close(fd);
+ exit(EX_OK);
+}
+
+void
+add_track(char *name, int block_size, int block_type, int nogap)
+{
+ struct stat sb;
+ int file;
+ static int done_stdin = 0;
+
+ if (!strcmp(name, "-")) {
+ if (done_stdin) {
+ warn("skipping multiple usages of stdin");
+ return;
+ }
+ file = STDIN_FILENO;
+ done_stdin = 1;
+ }
+ else if ((file = open(name, O_RDONLY, 0)) < 0)
+ err(EX_NOINPUT, "open(%s)", name);
+ if (fstat(file, &sb) < 0)
+ err(EX_IOERR, "fstat(%s)", name);
+ tracks[notracks].file = file;
+ strncpy(tracks[notracks].file_name, name, MAXPATHLEN);
+ if (file == STDIN_FILENO)
+ tracks[notracks].file_size = -1;
+ else
+ tracks[notracks].file_size = sb.st_size;
+ tracks[notracks].block_size = block_size;
+ tracks[notracks].block_type = block_type;
+
+ if (nogap && notracks)
+ tracks[notracks].pregap = 0;
+ else {
+ if (tracks[notracks - (notracks > 0)].block_type == block_type)
+ tracks[notracks].pregap = 150;
+ else
+ tracks[notracks].pregap = 255;
+ }
+
+ if (verbose) {
+ int pad = 0;
+
+ if (tracks[notracks].file_size / tracks[notracks].block_size !=
+ roundup_blocks(&tracks[notracks]))
+ pad = 1;
+ fprintf(stderr,
+ "adding type 0x%02x file %s size %jd KB %d blocks %s\n",
+ tracks[notracks].block_type, name,
+ (intmax_t)sb.st_size/1024,
+ roundup_blocks(&tracks[notracks]),
+ pad ? "(0 padded)" : "");
+ }
+ notracks++;
+}
+
+void
+do_DAO(int fd, int test_write, int multi)
+{
+ struct cdr_cuesheet sheet;
+ struct cdr_cue_entry cue[100];
+ int format = CDR_SESS_CDROM;
+ int addr, i, j = 0;
+
+ int bt2ctl[16] = { 0x0, -1, -1, -1, -1, -1, -1, -1,
+ 0x4, 0x4, 0x4, 0x4, 0x4, 0x4, -1, -1 };
+
+ int bt2df[16] = { 0x0, -1, -1, -1, -1, -1, -1, -1,
+ 0x10, 0x30, 0x20, -1, 0x21, -1, -1, -1 };
+
+ if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &addr) < 0)
+ err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
+ if (verbose)
+ fprintf(stderr, "next writeable LBA %d\n", addr);
+
+ cue_ent(&cue[j++], bt2ctl[tracks[0].block_type], 0x01, 0x00, 0x0,
+ (bt2df[tracks[0].block_type] & 0xf0) |
+ (tracks[0].block_type < 8 ? 0x01 : 0x04), 0x00, addr);
+
+ for (i = 0; i < notracks; i++) {
+ if (bt2ctl[tracks[i].block_type] < 0 ||
+ bt2df[tracks[i].block_type] < 0)
+ errx(EX_IOERR, "track type not supported in DAO mode");
+
+ if (tracks[i].block_type >= CDR_DB_XA_MODE1)
+ format = CDR_SESS_CDROM_XA;
+
+ if (i == 0) {
+ addr += tracks[i].pregap;
+ tracks[i].addr = addr;
+
+ cue_ent(&cue[j++], bt2ctl[tracks[i].block_type],
+ 0x01, i+1, 0x1, bt2df[tracks[i].block_type],
+ 0x00, addr);
+
+ }
+ else {
+ if (tracks[i].pregap) {
+ if (tracks[i].block_type > 0x7) {
+ cue_ent(&cue[j++],bt2ctl[tracks[i].block_type],
+ 0x01, i+1, 0x0,
+ (bt2df[tracks[i].block_type] & 0xf0) |
+ (tracks[i].block_type < 8 ? 0x01 :0x04),
+ 0x00, addr);
+ }
+ else
+ cue_ent(&cue[j++],bt2ctl[tracks[i].block_type],
+ 0x01, i+1, 0x0,
+ bt2df[tracks[i].block_type],
+ 0x00, addr);
+ }
+ tracks[i].addr = tracks[i - 1].addr +
+ roundup_blocks(&tracks[i - 1]);
+
+ cue_ent(&cue[j++], bt2ctl[tracks[i].block_type],
+ 0x01, i+1, 0x1, bt2df[tracks[i].block_type],
+ 0x00, addr + tracks[i].pregap);
+
+ if (tracks[i].block_type > 0x7)
+ addr += tracks[i].pregap;
+ }
+ addr += roundup_blocks(&tracks[i]);
+ }
+
+ cue_ent(&cue[j++], bt2ctl[tracks[i - 1].block_type], 0x01, 0xaa, 0x01,
+ (bt2df[tracks[i - 1].block_type] & 0xf0) |
+ (tracks[i - 1].block_type < 8 ? 0x01 : 0x04), 0x00, addr);
+
+ sheet.len = j * 8;
+ sheet.entries = cue;
+ sheet.test_write = test_write;
+ sheet.session_type = multi ? CDR_SESS_MULTI : CDR_SESS_NONE;
+ sheet.session_format = format;
+ if (verbose) {
+ u_int8_t *ptr = (u_int8_t *)sheet.entries;
+
+ fprintf(stderr,"CUE sheet:");
+ for (i = 0; i < sheet.len; i++)
+ if (i % 8)
+ fprintf(stderr," %02x", ptr[i]);
+ else
+ fprintf(stderr,"\n%02x", ptr[i]);
+ fprintf(stderr,"\n");
+ }
+
+ if (ioctl(fd, CDRIOCSENDCUE, &sheet) < 0)
+ err(EX_IOERR, "ioctl(CDRIOCSENDCUE)");
+
+ for (i = 0; i < notracks; i++) {
+ if (write_file(fd, &tracks[i])) {
+ cleanup_flush();
+ err(EX_IOERR, "write_file");
+ }
+ }
+
+ ioctl(fd, CDRIOCFLUSH);
+}
+
+void
+do_TAO(int fd, int test_write, int preemp, int dvdrw)
+{
+ struct cdr_track track;
+ int i;
+
+ for (i = 0; i < notracks; i++) {
+ track.test_write = test_write;
+ track.datablock_type = tracks[i].block_type;
+ track.preemp = preemp;
+ if (ioctl(fd, CDRIOCINITTRACK, &track) < 0)
+ err(EX_IOERR, "ioctl(CDRIOCINITTRACK)");
+
+ if (dvdrw)
+ tracks[i].addr = 0;
+ else
+ if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR,
+ &tracks[i].addr) < 0)
+ err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
+
+ if (!quiet)
+ fprintf(stderr, "next writeable LBA %d\n",
+ tracks[i].addr);
+ if (write_file(fd, &tracks[i])) {
+ cleanup_flush();
+ err(EX_IOERR, "write_file");
+ }
+ if (ioctl(fd, CDRIOCFLUSH) < 0)
+ err(EX_IOERR, "ioctl(CDRIOCFLUSH)");
+ }
+}
+
+#define NTOH3B(x) ((x&0x0000ff)<<16) | (x&0x00ff00) | ((x&0xff0000)>>16)
+
+void
+do_format(int the_fd, int force, char *type)
+{
+ struct cdr_format_capacities capacities;
+ struct cdr_format_params format_params;
+ int count, i, pct, last = 0;
+
+ if (ioctl(the_fd, CDRIOCREADFORMATCAPS, &capacities) == -1)
+ err(EX_IOERR, "ioctl(CDRIOCREADFORMATCAPS)");
+
+ if (verbose) {
+ fprintf(stderr, "format list entries=%zd\n",
+ capacities.length / sizeof(struct cdr_format_capacity));
+ fprintf(stderr, "current format: blocks=%u type=0x%x block_size=%u\n",
+ ntohl(capacities.blocks), capacities.type,
+ NTOH3B(capacities.block_size));
+ }
+
+ count = capacities.length / sizeof(struct cdr_format_capacity);
+ if (verbose) {
+ for (i = 0; i < count; ++i)
+ fprintf(stderr,
+ "format %d: blocks=%u type=0x%x param=%u\n",
+ i, ntohl(capacities.format[i].blocks),
+ capacities.format[i].type,
+ NTOH3B(capacities.format[i].param));
+ }
+
+ for (i = 0; i < count; ++i) {
+ if (!strcasecmp(type, "dvd+rw")) {
+ if (capacities.format[i].type == 0x26) {
+ break;
+ }
+ }
+ if (!strcasecmp(type, "dvd-rw")) {
+ if (capacities.format[i].type == 0x0) {
+ break;
+ }
+ }
+ }
+ if (i == count)
+ errx(EX_IOERR, "could not find a valid format capacity");
+
+ if (!quiet)
+ fprintf(stderr,"formatting with blocks=%u type=0x%x param=%u\n",
+ ntohl(capacities.format[i].blocks),
+ capacities.format[i].type,
+ NTOH3B(capacities.format[i].param));
+
+ if (!force && capacities.type == 2)
+ errx(EX_IOERR, "media already formatted (use -F to override)");
+
+ memset(&format_params, 0, sizeof(struct cdr_format_params));
+ format_params.fov = 1;
+ format_params.immed = 1;
+ format_params.length = ntohs(sizeof(struct cdr_format_capacity));
+ memcpy(&format_params.format, &capacities.format[i],
+ sizeof(struct cdr_format_capacity));
+
+ if(ioctl(the_fd, CDRIOCFORMAT, &format_params) == -1)
+ err(EX_IOERR, "ioctl(CDRIOCFORMAT)");
+
+ while (1) {
+ sleep(1);
+ if (ioctl(the_fd, CDRIOCGETPROGRESS, &pct) == -1)
+ err(EX_IOERR, "ioctl(CDRIOGETPROGRESS)");
+ if (pct > 0 && !quiet)
+ fprintf(stderr, "formatting DVD - %d %% done \r",
+ pct);
+ if (pct == 100 || (pct == 0 && last > 90))
+ break;
+ last = pct;
+ }
+ if (!quiet)
+ fprintf(stderr, "\n");
+}
+
+int
+write_file(int fd, struct track_info *track_info)
+{
+ off_t size, count, filesize;
+ char buf[2352*BLOCKS];
+ static off_t tot_size = 0;
+
+ filesize = track_info->file_size / 1024;
+
+ if (ioctl(fd, CDRIOCSETBLOCKSIZE, &track_info->block_size) < 0)
+ err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
+
+ if (track_info->addr >= 0)
+ lseek(fd, track_info->addr * track_info->block_size, SEEK_SET);
+
+ if (verbose)
+ fprintf(stderr, "addr = %d size = %jd blocks = %d\n",
+ track_info->addr, (intmax_t)track_info->file_size,
+ roundup_blocks(track_info));
+
+ if (!quiet) {
+ if (track_info->file == STDIN_FILENO)
+ fprintf(stderr, "writing from stdin\n");
+ else
+ fprintf(stderr,
+ "writing from file %s size %jd KB\n",
+ track_info->file_name, (intmax_t)filesize);
+ }
+ size = 0;
+
+ while ((count = read(track_info->file, buf,
+ track_info->file_size == -1
+ ? track_info->block_size * BLOCKS
+ : MIN((track_info->file_size - size),
+ track_info->block_size * BLOCKS))) > 0) {
+ int res;
+
+ if (count % track_info->block_size) {
+ /* pad file to % block_size */
+ bzero(&buf[count],
+ (track_info->block_size * BLOCKS) - count);
+ count = ((count / track_info->block_size) + 1) *
+ track_info->block_size;
+ }
+ if ((res = write(fd, buf, count)) != count) {
+ if (res == -1) {
+ fprintf(stderr, "\n");
+ close(track_info->file);
+ return errno;
+ } else
+ fprintf(stderr, "\nonly wrote %d of %jd"
+ " bytes\n", res, (intmax_t)count);
+ break;
+ }
+ size += count;
+ tot_size += count;
+ if (!quiet) {
+ int pct;
+
+ fprintf(stderr, "written this track %jd KB",
+ (intmax_t)size/1024);
+ if (track_info->file != STDIN_FILENO && filesize) {
+ pct = (size / 1024) * 100 / filesize;
+ fprintf(stderr, " (%d%%)", pct);
+ }
+ fprintf(stderr, " total %jd KB\r",
+ (intmax_t)tot_size / 1024);
+ }
+ if (track_info->file_size != -1
+ && size >= track_info->file_size)
+ break;
+ }
+
+ if (!quiet)
+ fprintf(stderr, "\n");
+ close(track_info->file);
+ return 0;
+}
+
+int
+roundup_blocks(struct track_info *track)
+{
+ return ((track->file_size + track->block_size - 1) / track->block_size);
+}
+
+void
+cue_ent(struct cdr_cue_entry *cue, int ctl, int adr, int track, int idx,
+ int dataform, int scms, int lba)
+{
+ cue->adr = adr;
+ cue->ctl = ctl;
+ cue->track = track;
+ cue->index = idx;
+ cue->dataform = dataform;
+ cue->scms = scms;
+ lba += 150;
+ cue->min = lba / (60*75);
+ cue->sec = (lba % (60*75)) / 75;
+ cue->frame = (lba % (60*75)) % 75;
+}
+
+void
+cleanup(int dummy __unused)
+{
+ if (ioctl(global_fd_for_cleanup, CDRIOCSETBLOCKSIZE,
+ &saved_block_size) < 0)
+ err(EX_IOERR, "ioctl(CDRIOCSETBLOCKSIZE)");
+}
+
+void
+cleanup_flush(void)
+{
+ if (ioctl(global_fd_for_cleanup, CDRIOCFLUSH) < 0)
+ err(EX_IOERR, "ioctl(CDRIOCFLUSH)");
+}
+
+void
+cleanup_signal(int sig)
+{
+ signal(sig, SIG_IGN);
+ ioctl(global_fd_for_cleanup, CDRIOCFLUSH);
+ write(STDERR_FILENO, "\nAborted\n", 10);
+ _exit(EXIT_FAILURE);
+}
+
+void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: %s [-deFlmnpqtv] [-f device] [-s speed] [command]"
+ " [command file ...]\n", getprogname());
+ exit(EX_USAGE);
+}
diff --git a/usr.sbin/cdcontrol/Makefile b/usr.sbin/cdcontrol/Makefile
new file mode 100644
index 0000000..40addad
--- /dev/null
+++ b/usr.sbin/cdcontrol/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+PROG= cdcontrol
+
+WARNS?= 2
+
+DPADD= ${LIBEDIT} ${LIBTERMCAP}
+LDADD= -ledit -ltermcap
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/cdcontrol/cdcontrol.1 b/usr.sbin/cdcontrol/cdcontrol.1
new file mode 100644
index 0000000..b8cf92d
--- /dev/null
+++ b/usr.sbin/cdcontrol/cdcontrol.1
@@ -0,0 +1,222 @@
+.\" $FreeBSD$
+.\"
+.Dd June 27, 2008
+.Dt CDCONTROL 1
+.Os
+.Sh NAME
+.Nm cdcontrol
+.Nd compact disc control utility
+.Sh SYNOPSIS
+.Nm
+.Op Fl sv
+.Op Fl f Ar device
+.Op Ar command ...
+.Sh DESCRIPTION
+The
+.Nm
+utility is a program to control audio features of a CD drive.
+The device is a name such
+as
+.Pa cd0
+or
+.Pa acd0 .
+.Pp
+If no
+.Ar command
+is given, then
+.Nm
+enters an interactive mode, reading commands from the standard input.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl s
+Silent mode.
+Do not print table headers and human readable comments.
+.It Fl v
+Verbose mode.
+Print as much information as possible.
+.It Fl f Ar device
+Specify a device, such as
+.Pa /dev/cd0
+or
+.Pa acd0 .
+Both absolute path and relative to
+.Pa /dev
+filename are possible.
+The
+.Fl f
+option overrides
+.Ev CDROM .
+If neither
+.Ev CDROM
+nor the
+.Fl f
+option is specified,
+.Nm
+tries opening first
+.Pa /dev/cdrom ,
+then
+.Pa /dev/cd0 ,
+and finally
+.Pa /dev/acd0 .
+.El
+.Pp
+The available commands are listed below.
+Only as many
+characters as are required to uniquely identify a command
+need be specified.
+The word
+.Ic play
+can be omitted or the characters
+.Ic +
+and
+.Ic -
+can be used in the
+place of
+.Ic next
+and
+.Ic prev .
+.Bl -tag -width indent
+.It Ic play Ar first_track Op Ar last_track
+Play from track
+.Ar first_track
+to track
+.Ar last_track .
+The first track has number 1.
+Can be omitted in all cases.
+.It Xo
+.Ic play
+.Ar start_m : Ns Ar start_s . Ns Ar start_f
+.Op Ar end_m : Ns Ar end_s . Ns Ar end_f
+.Xc
+Play from the absolute address
+(MSF) defined by
+.Ar start_m
+in minutes,
+.Ar start_s ,
+in seconds and
+.Ar start_f
+(frame number) to the absolute address defined by
+.Ar end_m
+in minutes,
+.Ar end_s ,
+in seconds and
+.Ar end_f
+(frame number).
+Minutes are in the range 0-99.
+Seconds are in the range 0-59.
+Frame numbers are in the range 0-74.
+.It Ic play Op # Ns Ar start_block Op Ar length
+Play starting from the logical block
+.Ar start_block
+using
+.Ar length
+logical blocks.
+.It Ic next Op Ar tracks
+Skip forward a number of tracks (default 1).
+.It Ic prev Op Ar tracks
+Skip backward a number of tracks (default 1).
+.It Ic pause
+Stop playing.
+Do not stop the disc.
+.It Ic resume
+Resume playing.
+Used after the
+.Ic pause
+command.
+.It Ic stop
+Stop the disc.
+.It Ic eject
+Eject the disc.
+.It Ic close
+Inject the disc.
+.It Ic volume
+Same as
+.Em status volume
+command.
+.It Ic volume Ar level
+Set the volume of both channels to
+.Ar level .
+Allowed values are in the range 0-255.
+.It Ic volume Ar left_channel right_channel
+Set the volume of left channel to
+.Ar left_channel
+and the volume of right channel to
+.Ar right_channel .
+Allowed values are in the range 0-255.
+.It Ic volume Cm mute
+Turn the sound off.
+.It Ic volume Cm mono
+Set the mono mode.
+.It Ic volume Cm stereo
+Set the stereo mode.
+.It Ic volume Cm left
+Play the left subtrack on both left and right channels.
+.It Ic volume Cm right
+Play the right subtrack on both left and right channels.
+.It Ic info
+Print the table of contents.
+.It Ic status Op Cm audio | media | volume
+Print the information about the disc:
+.Pp
+.Bl -tag -width ".Cm volume" -compact
+.It Cm audio
+the current playing status and position
+.It Cm media
+the current media catalog status
+.It Cm volume
+the current values of the volume for left and right channels.
+.El
+.It Ic cdid
+Display the serial number of the CD using the method used by the
+.Tn CDDB
+project
+.Pq Pa http://www.cddb.org/ .
+.It Ic help
+Print the list of available commands.
+.It Ic debug Cm on
+Enable the debugging mode of the CD device driver.
+.It Ic debug Cm off
+Disable the driver debugging mode.
+.It Ic reset
+Perform the hardware reset of the device.
+.It Ic set Cm msf
+Set minute-second-frame ioctl mode (default).
+.It Ic set Cm lba
+Set LBA ioctl mode.
+.It Ic speed Ar s
+Set the highest speed that the drive should use for reading data.
+The units are multiples of a single speed CDROM (150 KB/s).
+Specify
+.Dq Li max
+to use the drive's fastest speed.
+.It Ic quit
+Quit the program.
+.El
+.Sh ENVIRONMENT
+The following environment variables affect the execution of
+.Nm :
+.Bl -tag -width ".Ev CD_DRIVE"
+.It Ev CDROM
+The CD device to use, if one is not specified with the
+.Fl f
+option.
+.It Ev CDPLAY , CD_DRIVE , DISC , MUSIC_CD
+These variables have been deprecated in favour of
+.Ev CDROM .
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /dev/mcd0" -compact
+.It Pa /dev/cd0
+.It Pa /dev/mcd0
+.It Pa /dev/acd0
+.El
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Fx 2.1 .
+.Sh AUTHORS
+.An Jean-Marc Zucconi
+.An Andrey A. Chernov
+.An Serge V. Vakulenko
diff --git a/usr.sbin/cdcontrol/cdcontrol.c b/usr.sbin/cdcontrol/cdcontrol.c
new file mode 100644
index 0000000..ede5904
--- /dev/null
+++ b/usr.sbin/cdcontrol/cdcontrol.c
@@ -0,0 +1,1282 @@
+/*
+ * Compact Disc Control Utility by Serge V. Vakulenko <vak@cronyx.ru>.
+ * Based on the non-X based CD player by Jean-Marc Zucconi and
+ * Andrey A. Chernov.
+ *
+ * Fixed and further modified on 5-Sep-1995 by Jukka Ukkonen <jau@funet.fi>.
+ *
+ * 11-Sep-1995: Jukka A. Ukkonen <jau@funet.fi>
+ * A couple of further fixes to my own earlier "fixes".
+ *
+ * 18-Sep-1995: Jukka A. Ukkonen <jau@funet.fi>
+ * Added an ability to specify addresses relative to the
+ * beginning of a track. This is in fact a variation of
+ * doing the simple play_msf() call.
+ *
+ * 11-Oct-1995: Serge V.Vakulenko <vak@cronyx.ru>
+ * New eject algorithm.
+ * Some code style reformatting.
+ *
+ * 13-Dec-1999: Knut A. Syed <kas@kas.no>
+ * Volume-command modified. If used with only one
+ * parameter it now sets both channels. If used without
+ * parameters it will print volume-info.
+ * Version 2.0.1
+ *
+ * 27-Jun-2008 Pietro Cerutti <gahr@FreeBSD.org>
+ * Further enhancement to volume. Values not in range 0-255
+ * are now reduced to be in range. This prevents overflow in
+ * the uchar storing the volume (256 -> 0, -20 -> 236, ...).
+ * Version 2.0.2
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/cdio.h>
+#include <sys/cdrio.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/param.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <histedit.h>
+#include <limits.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <vis.h>
+
+#define VERSION "2.0.2"
+
+#define ASTS_INVALID 0x00 /* Audio status byte not valid */
+#define ASTS_PLAYING 0x11 /* Audio play operation in progress */
+#define ASTS_PAUSED 0x12 /* Audio play operation paused */
+#define ASTS_COMPLETED 0x13 /* Audio play operation successfully completed */
+#define ASTS_ERROR 0x14 /* Audio play operation stopped due to error */
+#define ASTS_VOID 0x15 /* No current audio status to return */
+
+#ifdef DEFAULT_CD_DRIVE
+# error "Setting DEFAULT_CD_DRIVE is no longer supported"
+#endif
+
+#define CMD_DEBUG 1
+#define CMD_EJECT 2
+#define CMD_HELP 3
+#define CMD_INFO 4
+#define CMD_PAUSE 5
+#define CMD_PLAY 6
+#define CMD_QUIT 7
+#define CMD_RESUME 8
+#define CMD_STOP 9
+#define CMD_VOLUME 10
+#define CMD_CLOSE 11
+#define CMD_RESET 12
+#define CMD_SET 13
+#define CMD_STATUS 14
+#define CMD_CDID 15
+#define CMD_NEXT 16
+#define CMD_PREVIOUS 17
+#define CMD_SPEED 18
+#define STATUS_AUDIO 0x1
+#define STATUS_MEDIA 0x2
+#define STATUS_VOLUME 0x4
+
+struct cmdtab {
+ int command;
+ const char *name;
+ unsigned min;
+ const char *args;
+} cmdtab[] = {
+{ CMD_CLOSE, "close", 1, "" },
+{ CMD_DEBUG, "debug", 1, "on | off" },
+{ CMD_EJECT, "eject", 1, "" },
+{ CMD_HELP, "?", 1, 0 },
+{ CMD_HELP, "help", 1, "" },
+{ CMD_INFO, "info", 1, "" },
+{ CMD_NEXT, "next", 1, "" },
+{ CMD_PAUSE, "pause", 2, "" },
+{ CMD_PLAY, "play", 1, "min1:sec1[.fram1] [min2:sec2[.fram2]]" },
+{ CMD_PLAY, "play", 1, "track1[.index1] [track2[.index2]]" },
+{ CMD_PLAY, "play", 1, "tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]" },
+{ CMD_PLAY, "play", 1, "[#block [len]]" },
+{ CMD_PREVIOUS, "previous", 2, "" },
+{ CMD_QUIT, "quit", 1, "" },
+{ CMD_RESET, "reset", 4, "" },
+{ CMD_RESUME, "resume", 1, "" },
+{ CMD_SET, "set", 2, "msf | lba" },
+{ CMD_STATUS, "status", 1, "[audio | media | volume]" },
+{ CMD_STOP, "stop", 3, "" },
+{ CMD_VOLUME, "volume", 1,
+ "<l&r> <l> <r> | left | right | mute | mono | stereo" },
+{ CMD_CDID, "cdid", 2, "" },
+{ CMD_SPEED, "speed", 2, "speed" },
+{ 0, NULL, 0, NULL }
+};
+
+struct cd_toc_entry toc_buffer[100];
+
+const char *cdname;
+int fd = -1;
+int verbose = 1;
+int msf = 1;
+
+int setvol(int, int);
+int read_toc_entrys(int);
+int play_msf(int, int, int, int, int, int);
+int play_track(int, int, int, int);
+int get_vol(int *, int *);
+int status(int *, int *, int *, int *);
+int open_cd(void);
+int next_prev(char *arg, int);
+int play(char *arg);
+int info(char *arg);
+int cdid(void);
+int pstatus(char *arg);
+char *input(int *);
+void prtrack(struct cd_toc_entry *e, int lastflag);
+void lba2msf(unsigned long lba, u_char *m, u_char *s, u_char *f);
+unsigned int msf2lba(u_char m, u_char s, u_char f);
+int play_blocks(int blk, int len);
+int run(int cmd, char *arg);
+char *parse(char *buf, int *cmd);
+void help(void);
+void usage(void);
+char *use_cdrom_instead(const char *);
+__const char *strstatus(int);
+static u_int dbprog_discid(void);
+__const char *cdcontrol_prompt(void);
+
+void help ()
+{
+ struct cmdtab *c;
+ const char *s;
+ char n;
+ int i;
+
+ for (c=cmdtab; c->name; ++c) {
+ if (! c->args)
+ continue;
+ printf("\t");
+ for (i = c->min, s = c->name; *s; s++, i--) {
+ if (i > 0)
+ n = toupper(*s);
+ else
+ n = *s;
+ putchar(n);
+ }
+ if (*c->args)
+ printf (" %s", c->args);
+ printf ("\n");
+ }
+ printf ("\n\tThe word \"play\" is not required for the play commands.\n");
+ printf ("\tThe plain target address is taken as a synonym for play.\n");
+}
+
+void usage ()
+{
+ fprintf (stderr, "usage: cdcontrol [-sv] [-f device] [command ...]\n");
+ exit (1);
+}
+
+char *use_cdrom_instead(const char *old_envvar)
+{
+ char *device;
+
+ device = getenv(old_envvar);
+ if (device)
+ warnx("%s environment variable deprecated, "
+ "please use CDROM in the future.", old_envvar);
+ return device;
+}
+
+
+int main (int argc, char **argv)
+{
+ int cmd;
+ char *arg;
+
+ for (;;) {
+ switch (getopt (argc, argv, "svhf:")) {
+ case -1:
+ break;
+ case 's':
+ verbose = 0;
+ continue;
+ case 'v':
+ verbose = 2;
+ continue;
+ case 'f':
+ cdname = optarg;
+ continue;
+ case 'h':
+ default:
+ usage ();
+ }
+ break;
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 0 && ! strcasecmp (*argv, "help"))
+ usage ();
+
+ if (! cdname) {
+ cdname = getenv("CDROM");
+ }
+
+ if (! cdname)
+ cdname = use_cdrom_instead("MUSIC_CD");
+ if (! cdname)
+ cdname = use_cdrom_instead("CD_DRIVE");
+ if (! cdname)
+ cdname = use_cdrom_instead("DISC");
+ if (! cdname)
+ cdname = use_cdrom_instead("CDPLAY");
+
+ if (argc > 0) {
+ char buf[80], *p;
+ int len;
+
+ for (p=buf; argc-->0; ++argv) {
+ len = strlen (*argv);
+
+ if (p + len >= buf + sizeof (buf) - 1)
+ usage ();
+
+ if (p > buf)
+ *p++ = ' ';
+
+ strcpy (p, *argv);
+ p += len;
+ }
+ *p = 0;
+ arg = parse (buf, &cmd);
+ return (run (cmd, arg));
+ }
+
+ if (verbose == 1)
+ verbose = isatty (0);
+
+ if (verbose) {
+ printf ("Compact Disc Control utility, version %s\n", VERSION);
+ printf ("Type `?' for command list\n\n");
+ }
+
+ for (;;) {
+ arg = input (&cmd);
+ if (run (cmd, arg) < 0) {
+ if (verbose)
+ warn(NULL);
+ close (fd);
+ fd = -1;
+ }
+ fflush (stdout);
+ }
+}
+
+int run (int cmd, char *arg)
+{
+ long speed;
+ int l, r, rc, count;
+
+ switch (cmd) {
+
+ case CMD_QUIT:
+ exit (0);
+
+ case CMD_INFO:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ return info (arg);
+
+ case CMD_CDID:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ return cdid ();
+
+ case CMD_STATUS:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ return pstatus (arg);
+
+ case CMD_NEXT:
+ case CMD_PREVIOUS:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ while (isspace (*arg))
+ arg++;
+
+ return next_prev (arg, cmd);
+
+ case CMD_PAUSE:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ return ioctl (fd, CDIOCPAUSE);
+
+ case CMD_RESUME:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ return ioctl (fd, CDIOCRESUME);
+
+ case CMD_STOP:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ rc = ioctl (fd, CDIOCSTOP);
+
+ (void) ioctl (fd, CDIOCALLOW);
+
+ return (rc);
+
+ case CMD_RESET:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ rc = ioctl (fd, CDIOCRESET);
+ if (rc < 0)
+ return rc;
+ close(fd);
+ fd = -1;
+ return (0);
+
+ case CMD_DEBUG:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ if (! strcasecmp (arg, "on"))
+ return ioctl (fd, CDIOCSETDEBUG);
+
+ if (! strcasecmp (arg, "off"))
+ return ioctl (fd, CDIOCCLRDEBUG);
+
+ warnx("invalid command arguments");
+
+ return (0);
+
+ case CMD_EJECT:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ (void) ioctl (fd, CDIOCALLOW);
+ rc = ioctl (fd, CDIOCEJECT);
+ if (rc < 0)
+ return (rc);
+ return (0);
+
+ case CMD_CLOSE:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ (void) ioctl (fd, CDIOCALLOW);
+ rc = ioctl (fd, CDIOCCLOSE);
+ if (rc < 0)
+ return (rc);
+ close(fd);
+ fd = -1;
+ return (0);
+
+ case CMD_PLAY:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ while (isspace (*arg))
+ arg++;
+
+ return play (arg);
+
+ case CMD_SET:
+ if (! strcasecmp (arg, "msf"))
+ msf = 1;
+ else if (! strcasecmp (arg, "lba"))
+ msf = 0;
+ else
+ warnx("invalid command arguments");
+ return (0);
+
+ case CMD_VOLUME:
+ if (fd < 0 && !open_cd ())
+ return (0);
+
+ if (! strlen (arg))
+ return pstatus ("volume");
+
+ if (! strncasecmp (arg, "left", strlen(arg)))
+ return ioctl (fd, CDIOCSETLEFT);
+
+ if (! strncasecmp (arg, "right", strlen(arg)))
+ return ioctl (fd, CDIOCSETRIGHT);
+
+ if (! strncasecmp (arg, "mono", strlen(arg)))
+ return ioctl (fd, CDIOCSETMONO);
+
+ if (! strncasecmp (arg, "stereo", strlen(arg)))
+ return ioctl (fd, CDIOCSETSTERIO);
+
+ if (! strncasecmp (arg, "mute", strlen(arg)))
+ return ioctl (fd, CDIOCSETMUTE);
+
+ count = sscanf (arg, "%d %d", &l, &r);
+ if (count == 1)
+ return setvol (l, l);
+ if (count == 2)
+ return setvol (l, r);
+ warnx("invalid command arguments");
+ return (0);
+
+ case CMD_SPEED:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ errno = 0;
+ if (strcasecmp("max", arg) == 0)
+ speed = CDR_MAX_SPEED;
+ else
+ speed = strtol(arg, NULL, 10) * 177;
+ if (speed <= 0 || speed > INT_MAX) {
+ warnx("invalid command arguments %s", arg);
+ return (0);
+ }
+ return ioctl(fd, CDRIOCREADSPEED, &speed);
+
+ default:
+ case CMD_HELP:
+ help ();
+ return (0);
+
+ }
+}
+
+int play (char *arg)
+{
+ struct ioc_toc_header h;
+ unsigned int n;
+ int rc, start, end = 0, istart = 1, iend = 1;
+
+ rc = ioctl (fd, CDIOREADTOCHEADER, &h);
+
+ if (rc < 0)
+ return (rc);
+
+ n = h.ending_track - h.starting_track + 1;
+ rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry));
+
+ if (rc < 0)
+ return (rc);
+
+ if (! arg || ! *arg) {
+ /* Play the whole disc */
+ if (msf)
+ return play_blocks (0, msf2lba (toc_buffer[n].addr.msf.minute,
+ toc_buffer[n].addr.msf.second,
+ toc_buffer[n].addr.msf.frame));
+ else
+ return play_blocks (0, ntohl(toc_buffer[n].addr.lba));
+ }
+
+ if (strchr (arg, '#')) {
+ /* Play block #blk [ len ] */
+ int blk, len = 0;
+
+ if (2 != sscanf (arg, "#%d%d", &blk, &len) &&
+ 1 != sscanf (arg, "#%d", &blk))
+ goto Clean_up;
+
+ if (len == 0) {
+ if (msf)
+ len = msf2lba (toc_buffer[n].addr.msf.minute,
+ toc_buffer[n].addr.msf.second,
+ toc_buffer[n].addr.msf.frame) - blk;
+ else
+ len = ntohl(toc_buffer[n].addr.lba) - blk;
+ }
+ return play_blocks (blk, len);
+ }
+
+ if (strchr (arg, ':')) {
+ /*
+ * Play MSF m1:s1 [ .f1 ] [ m2:s2 [ .f2 ] ]
+ *
+ * Will now also undestand timed addresses relative
+ * to the beginning of a track in the form...
+ *
+ * tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]
+ */
+ unsigned tr1, tr2;
+ unsigned m1, m2, s1, s2, f1, f2;
+ unsigned char tm, ts, tf;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (8 == sscanf (arg, "%d %d:%d.%d %d %d:%d.%d",
+ &tr1, &m1, &s1, &f1, &tr2, &m2, &s2, &f2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (7 == sscanf (arg, "%d %d:%d %d %d:%d.%d",
+ &tr1, &m1, &s1, &tr2, &m2, &s2, &f2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (7 == sscanf (arg, "%d %d:%d.%d %d %d:%d",
+ &tr1, &m1, &s1, &f1, &tr2, &m2, &s2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (7 == sscanf (arg, "%d %d:%d.%d %d:%d.%d",
+ &tr1, &m1, &s1, &f1, &m2, &s2, &f2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (6 == sscanf (arg, "%d %d:%d.%d %d:%d",
+ &tr1, &m1, &s1, &f1, &m2, &s2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (6 == sscanf (arg, "%d %d:%d %d:%d.%d",
+ &tr1, &m1, &s1, &m2, &s2, &f2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (6 == sscanf (arg, "%d %d:%d.%d %d %d",
+ &tr1, &m1, &s1, &f1, &tr2, &m2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (5 == sscanf (arg, "%d %d:%d %d:%d", &tr1, &m1, &s1, &m2, &s2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (5 == sscanf (arg, "%d %d:%d %d %d",
+ &tr1, &m1, &s1, &tr2, &m2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (5 == sscanf (arg, "%d %d:%d.%d %d",
+ &tr1, &m1, &s1, &f1, &tr2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (4 == sscanf (arg, "%d %d:%d %d", &tr1, &m1, &s1, &tr2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (4 == sscanf (arg, "%d %d:%d.%d", &tr1, &m1, &s1, &f1))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (3 == sscanf (arg, "%d %d:%d", &tr1, &m1, &s1))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ goto Try_Absolute_Timed_Addresses;
+
+Play_Relative_Addresses:
+ if (! tr1)
+ tr1 = 1;
+ else if (tr1 > n)
+ tr1 = n;
+
+ tr1--;
+
+ if (msf) {
+ tm = toc_buffer[tr1].addr.msf.minute;
+ ts = toc_buffer[tr1].addr.msf.second;
+ tf = toc_buffer[tr1].addr.msf.frame;
+ } else
+ lba2msf(ntohl(toc_buffer[tr1].addr.lba),
+ &tm, &ts, &tf);
+ if ((m1 > tm)
+ || ((m1 == tm)
+ && ((s1 > ts)
+ || ((s1 == ts)
+ && (f1 > tf))))) {
+ printf ("Track %d is not that long.\n", tr1);
+ return (0);
+ }
+
+ f1 += tf;
+ if (f1 >= 75) {
+ s1 += f1 / 75;
+ f1 %= 75;
+ }
+
+ s1 += ts;
+ if (s1 >= 60) {
+ m1 += s1 / 60;
+ s1 %= 60;
+ }
+
+ m1 += tm;
+
+ if (! tr2) {
+ if (m2 || s2 || f2) {
+ tr2 = tr1;
+ f2 += f1;
+ if (f2 >= 75) {
+ s2 += f2 / 75;
+ f2 %= 75;
+ }
+
+ s2 += s1;
+ if (s2 > 60) {
+ m2 += s2 / 60;
+ s2 %= 60;
+ }
+
+ m2 += m1;
+ } else {
+ tr2 = n;
+ if (msf) {
+ m2 = toc_buffer[n].addr.msf.minute;
+ s2 = toc_buffer[n].addr.msf.second;
+ f2 = toc_buffer[n].addr.msf.frame;
+ } else {
+ lba2msf(ntohl(toc_buffer[n].addr.lba),
+ &tm, &ts, &tf);
+ m2 = tm;
+ s2 = ts;
+ f2 = tf;
+ }
+ }
+ } else if (tr2 > n) {
+ tr2 = n;
+ m2 = s2 = f2 = 0;
+ } else {
+ if (m2 || s2 || f2)
+ tr2--;
+ if (msf) {
+ tm = toc_buffer[tr2].addr.msf.minute;
+ ts = toc_buffer[tr2].addr.msf.second;
+ tf = toc_buffer[tr2].addr.msf.frame;
+ } else
+ lba2msf(ntohl(toc_buffer[tr2].addr.lba),
+ &tm, &ts, &tf);
+ f2 += tf;
+ if (f2 >= 75) {
+ s2 += f2 / 75;
+ f2 %= 75;
+ }
+
+ s2 += ts;
+ if (s2 > 60) {
+ m2 += s2 / 60;
+ s2 %= 60;
+ }
+
+ m2 += tm;
+ }
+
+ if (msf) {
+ tm = toc_buffer[n].addr.msf.minute;
+ ts = toc_buffer[n].addr.msf.second;
+ tf = toc_buffer[n].addr.msf.frame;
+ } else
+ lba2msf(ntohl(toc_buffer[n].addr.lba),
+ &tm, &ts, &tf);
+ if ((tr2 < n)
+ && ((m2 > tm)
+ || ((m2 == tm)
+ && ((s2 > ts)
+ || ((s2 == ts)
+ && (f2 > tf)))))) {
+ printf ("The playing time of the disc is not that long.\n");
+ return (0);
+ }
+ return (play_msf (m1, s1, f1, m2, s2, f2));
+
+Try_Absolute_Timed_Addresses:
+ if (6 != sscanf (arg, "%d:%d.%d%d:%d.%d",
+ &m1, &s1, &f1, &m2, &s2, &f2) &&
+ 5 != sscanf (arg, "%d:%d.%d%d:%d", &m1, &s1, &f1, &m2, &s2) &&
+ 5 != sscanf (arg, "%d:%d%d:%d.%d", &m1, &s1, &m2, &s2, &f2) &&
+ 3 != sscanf (arg, "%d:%d.%d", &m1, &s1, &f1) &&
+ 4 != sscanf (arg, "%d:%d%d:%d", &m1, &s1, &m2, &s2) &&
+ 2 != sscanf (arg, "%d:%d", &m1, &s1))
+ goto Clean_up;
+
+ if (m2 == 0) {
+ if (msf) {
+ m2 = toc_buffer[n].addr.msf.minute;
+ s2 = toc_buffer[n].addr.msf.second;
+ f2 = toc_buffer[n].addr.msf.frame;
+ } else {
+ lba2msf(ntohl(toc_buffer[n].addr.lba),
+ &tm, &ts, &tf);
+ m2 = tm;
+ s2 = ts;
+ f2 = tf;
+ }
+ }
+ return play_msf (m1, s1, f1, m2, s2, f2);
+ }
+
+ /*
+ * Play track trk1 [ .idx1 ] [ trk2 [ .idx2 ] ]
+ */
+ if (4 != sscanf (arg, "%d.%d%d.%d", &start, &istart, &end, &iend) &&
+ 3 != sscanf (arg, "%d.%d%d", &start, &istart, &end) &&
+ 3 != sscanf (arg, "%d%d.%d", &start, &end, &iend) &&
+ 2 != sscanf (arg, "%d.%d", &start, &istart) &&
+ 2 != sscanf (arg, "%d%d", &start, &end) &&
+ 1 != sscanf (arg, "%d", &start))
+ goto Clean_up;
+
+ if (end == 0)
+ end = n;
+ return (play_track (start, istart, end, iend));
+
+Clean_up:
+ warnx("invalid command arguments");
+ return (0);
+}
+
+int next_prev (char *arg, int cmd)
+{
+ struct ioc_toc_header h;
+ int dir, junk, n, off, rc, trk;
+
+ dir = (cmd == CMD_NEXT) ? 1 : -1;
+ rc = ioctl (fd, CDIOREADTOCHEADER, &h);
+ if (rc < 0)
+ return (rc);
+
+ n = h.ending_track - h.starting_track + 1;
+ rc = status (&trk, &junk, &junk, &junk);
+ if (rc < 0)
+ return (-1);
+
+ if (arg && *arg) {
+ if (sscanf (arg, "%u", &off) != 1) {
+ warnx("invalid command argument");
+ return (0);
+ } else
+ trk += off * dir;
+ } else
+ trk += dir;
+
+ if (trk > h.ending_track)
+ trk = 1;
+
+ return (play_track (trk, 1, n, 1));
+}
+
+const char *strstatus (int sts)
+{
+ switch (sts) {
+ case ASTS_INVALID: return ("invalid");
+ case ASTS_PLAYING: return ("playing");
+ case ASTS_PAUSED: return ("paused");
+ case ASTS_COMPLETED: return ("completed");
+ case ASTS_ERROR: return ("error");
+ case ASTS_VOID: return ("void");
+ default: return ("??");
+ }
+}
+
+int pstatus (char *arg)
+{
+ struct ioc_vol v;
+ struct ioc_read_subchannel ss;
+ struct cd_sub_channel_info data;
+ int rc, trk, m, s, f;
+ int what = 0;
+ char *p, vmcn[(4 * 15) + 1];
+
+ while ((p = strtok(arg, " \t"))) {
+ arg = 0;
+ if (!strncasecmp(p, "audio", strlen(p)))
+ what |= STATUS_AUDIO;
+ else if (!strncasecmp(p, "media", strlen(p)))
+ what |= STATUS_MEDIA;
+ else if (!strncasecmp(p, "volume", strlen(p)))
+ what |= STATUS_VOLUME;
+ else {
+ warnx("invalid command arguments");
+ return 0;
+ }
+ }
+ if (!what)
+ what = STATUS_AUDIO|STATUS_MEDIA|STATUS_VOLUME;
+ if (what & STATUS_AUDIO) {
+ rc = status (&trk, &m, &s, &f);
+ if (rc >= 0)
+ if (verbose)
+ printf ("Audio status = %d<%s>, current track = %d, current position = %d:%02d.%02d\n",
+ rc, strstatus (rc), trk, m, s, f);
+ else
+ printf ("%d %d %d:%02d.%02d\n", rc, trk, m, s, f);
+ else
+ printf ("No current status info available\n");
+ }
+ if (what & STATUS_MEDIA) {
+ bzero (&ss, sizeof (ss));
+ ss.data = &data;
+ ss.data_len = sizeof (data);
+ ss.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
+ ss.data_format = CD_MEDIA_CATALOG;
+ rc = ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &ss);
+ if (rc >= 0) {
+ printf("Media catalog is %sactive",
+ ss.data->what.media_catalog.mc_valid ? "": "in");
+ if (ss.data->what.media_catalog.mc_valid &&
+ ss.data->what.media_catalog.mc_number[0])
+ {
+ strvisx (vmcn, ss.data->what.media_catalog.mc_number,
+ (sizeof (vmcn) - 1) / 4, VIS_OCTAL | VIS_NL);
+ printf(", number \"%.*s\"", (int)sizeof (vmcn), vmcn);
+ }
+ putchar('\n');
+ } else
+ printf("No media catalog info available\n");
+ }
+ if (what & STATUS_VOLUME) {
+ rc = ioctl (fd, CDIOCGETVOL, &v);
+ if (rc >= 0)
+ if (verbose)
+ printf ("Left volume = %d, right volume = %d\n",
+ v.vol[0], v.vol[1]);
+ else
+ printf ("%d %d\n", v.vol[0], v.vol[1]);
+ else
+ printf ("No volume level info available\n");
+ }
+ return(0);
+}
+
+/*
+ * dbprog_sum
+ * Convert an integer to its text string representation, and
+ * compute its checksum. Used by dbprog_discid to derive the
+ * disc ID.
+ *
+ * Args:
+ * n - The integer value.
+ *
+ * Return:
+ * The integer checksum.
+ */
+static int
+dbprog_sum(int n)
+{
+ char buf[12],
+ *p;
+ int ret = 0;
+
+ /* For backward compatibility this algorithm must not change */
+ sprintf(buf, "%u", n);
+ for (p = buf; *p != '\0'; p++)
+ ret += (*p - '0');
+
+ return(ret);
+}
+
+
+/*
+ * dbprog_discid
+ * Compute a magic disc ID based on the number of tracks,
+ * the length of each track, and a checksum of the string
+ * that represents the offset of each track.
+ *
+ * Args:
+ * s - Pointer to the curstat_t structure.
+ *
+ * Return:
+ * The integer disc ID.
+ */
+static u_int
+dbprog_discid()
+{
+ struct ioc_toc_header h;
+ int rc;
+ int i, ntr,
+ t = 0,
+ n = 0;
+
+ rc = ioctl (fd, CDIOREADTOCHEADER, &h);
+ if (rc < 0)
+ return 0;
+ ntr = h.ending_track - h.starting_track + 1;
+ i = msf;
+ msf = 1;
+ rc = read_toc_entrys ((ntr + 1) * sizeof (struct cd_toc_entry));
+ msf = i;
+ if (rc < 0)
+ return 0;
+ /* For backward compatibility this algorithm must not change */
+ for (i = 0; i < ntr; i++) {
+#define TC_MM(a) toc_buffer[a].addr.msf.minute
+#define TC_SS(a) toc_buffer[a].addr.msf.second
+ n += dbprog_sum((TC_MM(i) * 60) + TC_SS(i));
+
+ t += ((TC_MM(i+1) * 60) + TC_SS(i+1)) -
+ ((TC_MM(i) * 60) + TC_SS(i));
+ }
+
+ return((n % 0xff) << 24 | t << 8 | ntr);
+}
+
+int cdid ()
+{
+ u_int id;
+
+ id = dbprog_discid();
+ if (id)
+ {
+ if (verbose)
+ printf ("CDID=");
+ printf ("%08x\n",id);
+ }
+ return id ? 0 : 1;
+}
+
+int info (char *arg __unused)
+{
+ struct ioc_toc_header h;
+ int rc, i, n;
+
+ rc = ioctl (fd, CDIOREADTOCHEADER, &h);
+ if (rc >= 0) {
+ if (verbose)
+ printf ("Starting track = %d, ending track = %d, TOC size = %d bytes\n",
+ h.starting_track, h.ending_track, h.len);
+ else
+ printf ("%d %d %d\n", h.starting_track,
+ h.ending_track, h.len);
+ } else {
+ warn("getting toc header");
+ return (rc);
+ }
+
+ n = h.ending_track - h.starting_track + 1;
+ rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry));
+ if (rc < 0)
+ return (rc);
+
+ if (verbose) {
+ printf ("track start duration block length type\n");
+ printf ("-------------------------------------------------\n");
+ }
+
+ for (i = 0; i < n; i++) {
+ printf ("%5d ", toc_buffer[i].track);
+ prtrack (toc_buffer + i, 0);
+ }
+ printf ("%5d ", toc_buffer[n].track);
+ prtrack (toc_buffer + n, 1);
+ return (0);
+}
+
+void lba2msf (unsigned long lba, u_char *m, u_char *s, u_char *f)
+{
+ lba += 150; /* block start offset */
+ lba &= 0xffffff; /* negative lbas use only 24 bits */
+ *m = lba / (60 * 75);
+ lba %= (60 * 75);
+ *s = lba / 75;
+ *f = lba % 75;
+}
+
+unsigned int msf2lba (u_char m, u_char s, u_char f)
+{
+ return (((m * 60) + s) * 75 + f) - 150;
+}
+
+void prtrack (struct cd_toc_entry *e, int lastflag)
+{
+ int block, next, len;
+ u_char m, s, f;
+
+ if (msf) {
+ /* Print track start */
+ printf ("%2d:%02d.%02d ", e->addr.msf.minute,
+ e->addr.msf.second, e->addr.msf.frame);
+
+ block = msf2lba (e->addr.msf.minute, e->addr.msf.second,
+ e->addr.msf.frame);
+ } else {
+ block = ntohl(e->addr.lba);
+ lba2msf(block, &m, &s, &f);
+ /* Print track start */
+ printf ("%2d:%02d.%02d ", m, s, f);
+ }
+ if (lastflag) {
+ /* Last track -- print block */
+ printf (" - %6d - -\n", block);
+ return;
+ }
+
+ if (msf)
+ next = msf2lba (e[1].addr.msf.minute, e[1].addr.msf.second,
+ e[1].addr.msf.frame);
+ else
+ next = ntohl(e[1].addr.lba);
+ len = next - block;
+ /* Take into account a start offset time. */
+ lba2msf (len - 150, &m, &s, &f);
+
+ /* Print duration, block, length, type */
+ printf ("%2d:%02d.%02d %6d %6d %5s\n", m, s, f, block, len,
+ (e->control & 4) ? "data" : "audio");
+}
+
+int play_track (int tstart, int istart, int tend, int iend)
+{
+ struct ioc_play_track t;
+
+ t.start_track = tstart;
+ t.start_index = istart;
+ t.end_track = tend;
+ t.end_index = iend;
+
+ return ioctl (fd, CDIOCPLAYTRACKS, &t);
+}
+
+int play_blocks (int blk, int len)
+{
+ struct ioc_play_blocks t;
+
+ t.blk = blk;
+ t.len = len;
+
+ return ioctl (fd, CDIOCPLAYBLOCKS, &t);
+}
+
+int setvol (int left, int right)
+{
+ struct ioc_vol v;
+
+ left = left < 0 ? 0 : left > 255 ? 255 : left;
+ right = right < 0 ? 0 : right > 255 ? 255 : right;
+
+ v.vol[0] = left;
+ v.vol[1] = right;
+ v.vol[2] = 0;
+ v.vol[3] = 0;
+
+ return ioctl (fd, CDIOCSETVOL, &v);
+}
+
+int read_toc_entrys (int len)
+{
+ struct ioc_read_toc_entry t;
+
+ t.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
+ t.starting_track = 0;
+ t.data_len = len;
+ t.data = toc_buffer;
+
+ return (ioctl (fd, CDIOREADTOCENTRYS, (char *) &t));
+}
+
+int play_msf (int start_m, int start_s, int start_f,
+ int end_m, int end_s, int end_f)
+{
+ struct ioc_play_msf a;
+
+ a.start_m = start_m;
+ a.start_s = start_s;
+ a.start_f = start_f;
+ a.end_m = end_m;
+ a.end_s = end_s;
+ a.end_f = end_f;
+
+ return ioctl (fd, CDIOCPLAYMSF, (char *) &a);
+}
+
+int status (int *trk, int *min, int *sec, int *frame)
+{
+ struct ioc_read_subchannel s;
+ struct cd_sub_channel_info data;
+ u_char mm, ss, ff;
+
+ bzero (&s, sizeof (s));
+ s.data = &data;
+ s.data_len = sizeof (data);
+ s.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
+ s.data_format = CD_CURRENT_POSITION;
+
+ if (ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &s) < 0)
+ return -1;
+
+ *trk = s.data->what.position.track_number;
+ if (msf) {
+ *min = s.data->what.position.reladdr.msf.minute;
+ *sec = s.data->what.position.reladdr.msf.second;
+ *frame = s.data->what.position.reladdr.msf.frame;
+ } else {
+ lba2msf(ntohl(s.data->what.position.reladdr.lba),
+ &mm, &ss, &ff);
+ *min = mm;
+ *sec = ss;
+ *frame = ff;
+ }
+
+ return s.data->header.audio_status;
+}
+
+const char *
+cdcontrol_prompt()
+{
+ return ("cdcontrol> ");
+}
+
+char *
+input (int *cmd)
+{
+#define MAXLINE 80
+ static EditLine *el = NULL;
+ static History *hist = NULL;
+ HistEvent he;
+ static char buf[MAXLINE];
+ int num = 0;
+ int len;
+ const char *bp = NULL;
+ char *p;
+
+ do {
+ if (verbose) {
+ if (!el) {
+ el = el_init("cdcontrol", stdin, stdout,
+ stderr);
+ hist = history_init();
+ history(hist, &he, H_SETSIZE, 100);
+ el_set(el, EL_HIST, history, hist);
+ el_set(el, EL_EDITOR, "emacs");
+ el_set(el, EL_PROMPT, cdcontrol_prompt);
+ el_set(el, EL_SIGNAL, 1);
+ el_source(el, NULL);
+ }
+ if ((bp = el_gets(el, &num)) == NULL || num == 0) {
+ *cmd = CMD_QUIT;
+ fprintf (stderr, "\r\n");
+ return (0);
+ }
+
+ len = (num > MAXLINE) ? MAXLINE : num;
+ memcpy(buf, bp, len);
+ buf[len] = 0;
+ history(hist, &he, H_ENTER, bp);
+#undef MAXLINE
+
+ } else {
+ if (! fgets (buf, sizeof (buf), stdin)) {
+ *cmd = CMD_QUIT;
+ fprintf (stderr, "\r\n");
+ return (0);
+ }
+ }
+ p = parse (buf, cmd);
+ } while (! p);
+ return (p);
+}
+
+char *parse (char *buf, int *cmd)
+{
+ struct cmdtab *c;
+ char *p;
+ unsigned int len;
+
+ for (p=buf; isspace (*p); p++)
+ continue;
+
+ if (isdigit (*p) || (p[0] == '#' && isdigit (p[1]))) {
+ *cmd = CMD_PLAY;
+ return (p);
+ } else if (*p == '+') {
+ *cmd = CMD_NEXT;
+ return (p + 1);
+ } else if (*p == '-') {
+ *cmd = CMD_PREVIOUS;
+ return (p + 1);
+ }
+
+ for (buf = p; *p && ! isspace (*p); p++)
+ continue;
+
+ len = p - buf;
+ if (! len)
+ return (0);
+
+ if (*p) { /* It must be a spacing character! */
+ char *q;
+
+ *p++ = 0;
+ for (q=p; *q && *q != '\n' && *q != '\r'; q++)
+ continue;
+ *q = 0;
+ }
+
+ *cmd = -1;
+ for (c=cmdtab; c->name; ++c) {
+ /* Is it an exact match? */
+ if (! strcasecmp (buf, c->name)) {
+ *cmd = c->command;
+ break;
+ }
+
+ /* Try short hand forms then... */
+ if (len >= c->min && ! strncasecmp (buf, c->name, len)) {
+ if (*cmd != -1 && *cmd != c->command) {
+ warnx("ambiguous command");
+ return (0);
+ }
+ *cmd = c->command;
+ }
+ }
+
+ if (*cmd == -1) {
+ warnx("invalid command, enter ``help'' for commands");
+ return (0);
+ }
+
+ while (isspace (*p))
+ p++;
+ return p;
+}
+
+int open_cd ()
+{
+ char devbuf[MAXPATHLEN];
+ const char *dev;
+
+ if (fd > -1)
+ return (1);
+
+ if (cdname) {
+ if (*cdname == '/') {
+ snprintf (devbuf, MAXPATHLEN, "%s", cdname);
+ } else {
+ snprintf (devbuf, MAXPATHLEN, "%s%s", _PATH_DEV, cdname);
+ }
+ fd = open (dev = devbuf, O_RDONLY);
+ } else {
+ fd = open(dev = "/dev/cdrom", O_RDONLY);
+ if (fd < 0 && errno == ENOENT)
+ fd = open(dev = "/dev/cd0", O_RDONLY);
+ if (fd < 0 && errno == ENOENT)
+ fd = open(dev = "/dev/acd0", O_RDONLY);
+ }
+
+ if (fd < 0) {
+ if (errno == ENXIO) {
+ /* ENXIO has an overloaded meaning here.
+ * The original "Device not configured" should
+ * be interpreted as "No disc in drive %s". */
+ warnx("no disc in drive %s", dev);
+ return (0);
+ }
+ err(1, "%s", dev);
+ }
+ return (1);
+}
diff --git a/usr.sbin/chkgrp/Makefile b/usr.sbin/chkgrp/Makefile
new file mode 100644
index 0000000..1c52f46
--- /dev/null
+++ b/usr.sbin/chkgrp/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= chkgrp
+MAN= chkgrp.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/chkgrp/chkgrp.8 b/usr.sbin/chkgrp/chkgrp.8
new file mode 100644
index 0000000..581fd64
--- /dev/null
+++ b/usr.sbin/chkgrp/chkgrp.8
@@ -0,0 +1,86 @@
+.\" Copyright (c) 1998 Dag-Erling Coïdan Smørgrav
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer
+.\" in this position and unchanged.
+.\" 2. Redistributions in binary form must 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 May 26, 2005
+.Dt CHKGRP 8
+.Os
+.Sh NAME
+.Nm chkgrp
+.Nd check the syntax of the group file
+.Sh SYNOPSIS
+.Nm
+.Op Ar groupfile
+.Sh DESCRIPTION
+The
+.Nm
+utility
+scans the given file or, failing that, the system-wide group file for
+errors.
+Specifically, it checks that every non-blank, non-comment
+entry is composed of four colon-separated fields, that none of them
+contains whitespace, and that the third field (the group ID) is
+numeric.
+It will also check for invalid characters in the group names
+and group members.
+.Sh FILES
+.Bl -tag -width /etc/group -compact
+.It Pa /etc/group
+group database file
+.El
+.Sh EXIT STATUS
+The
+.Nm
+utility returns
+.Dv EX_DATAERR
+if errors were found in the group file,
+and
+.Dv EX_OK
+otherwise.
+.Sh DIAGNOSTICS
+For each error found,
+.Nm
+will print an error message containing the name of the file being
+scanned and the line number on which the error was found.
+.Sh SEE ALSO
+.Xr getgrent 3 ,
+.Xr group 5
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 3.0 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility and this manual page were written by
+.An Dag-Erling Sm\(/orgrav Aq des@FreeBSD.org .
+Further functionality was added by
+.An Liam J. Foy Aq liamfoy@dragonflybsd.org .
+.Sh BUGS
+Should check the range of the group ID.
diff --git a/usr.sbin/chkgrp/chkgrp.c b/usr.sbin/chkgrp/chkgrp.c
new file mode 100644
index 0000000..eca26af
--- /dev/null
+++ b/usr.sbin/chkgrp/chkgrp.c
@@ -0,0 +1,169 @@
+/*-
+ * Copyright (c) 1998 Dag-Erling Coïdan Smørgrav
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+static char empty[] = { 0 };
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: chkgrp [groupfile]\n");
+ exit(EX_USAGE);
+}
+
+int
+main(int argc, char *argv[])
+{
+ unsigned int i;
+ size_t len;
+ int n = 0, k, e = 0;
+ char *line, *f[4], *p;
+ const char *cp, *gfn;
+ FILE *gf;
+
+ /* check arguments */
+ switch (argc) {
+ case 1:
+ gfn = "/etc/group";
+ break;
+ case 2:
+ gfn = argv[1];
+ break;
+ default:
+ gfn = NULL; /* silence compiler */
+ usage();
+ }
+
+ /* open group file */
+ if ((gf = fopen(gfn, "r")) == NULL)
+ err(EX_IOERR, "%s", gfn); /* XXX - is IO_ERR the correct exit code? */
+
+ /* check line by line */
+ while (++n) {
+ if ((line = fgetln(gf, &len)) == NULL)
+ break;
+ if (len > 0 && line[len - 1] != '\n') {
+ warnx("%s: line %d: no newline character", gfn, n);
+ e++;
+ }
+ while (len && isspace(line[len-1]))
+ len--;
+
+ /* ignore blank lines and comments */
+ for (p = line; p < (line + len); p++)
+ if (!isspace(*p)) break;
+ if (!len || (*p == '#')) {
+#if 0
+ /* entry is correct, so print it */
+ printf("%*.*s\n", len, len, line);
+#endif
+ continue;
+ }
+
+ /*
+ * A correct group entry has four colon-separated fields, the third
+ * of which must be entirely numeric and the fourth of which may
+ * be empty.
+ */
+ for (i = k = 0; k < 4; k++) {
+ for (f[k] = line+i; (i < len) && (line[i] != ':'); i++)
+ /* nothing */ ;
+ if ((k < 3) && (line[i] != ':'))
+ break;
+ line[i++] = 0;
+ }
+
+ if (k < 4) {
+ warnx("%s: line %d: missing field(s)", gfn, n);
+ for ( ; k < 4; k++)
+ f[k] = empty;
+ e++;
+ }
+
+ for (cp = f[0] ; *cp ; cp++) {
+ if (!isalnum(*cp) && *cp != '.' && *cp != '_' && *cp != '-' &&
+ (cp > f[0] || *cp != '+')) {
+ warnx("%s: line %d: '%c' invalid character", gfn, n, *cp);
+ e++;
+ }
+ }
+
+ for (cp = f[3] ; *cp ; cp++) {
+ if (!isalnum(*cp) && *cp != '.' && *cp != '_' && *cp != '-' &&
+ *cp != ',') {
+ warnx("%s: line %d: '%c' invalid character", gfn, n, *cp);
+ e++;
+ }
+ }
+
+ /* check if fourth field ended with a colon */
+ if (i < len) {
+ warnx("%s: line %d: too many fields", gfn, n);
+ e++;
+ }
+
+ /* check that none of the fields contain whitespace */
+ for (k = 0; k < 4; k++) {
+ if (strcspn(f[k], " \t") != strlen(f[k])) {
+ warnx("%s: line %d: field %d contains whitespace",
+ gfn, n, k+1);
+ e++;
+ }
+ }
+
+ /* check that the GID is numeric */
+ if (strspn(f[2], "0123456789") != strlen(f[2])) {
+ warnx("%s: line %d: GID is not numeric", gfn, n);
+ e++;
+ }
+
+#if 0
+ /* entry is correct, so print it */
+ printf("%s:%s:%s:%s\n", f[0], f[1], f[2], f[3]);
+#endif
+ }
+
+ /* check what broke the loop */
+ if (ferror(gf))
+ err(EX_IOERR, "%s: line %d", gfn, n);
+
+ /* done */
+ fclose(gf);
+ if (e == 0)
+ printf("%s is fine\n", gfn);
+ exit(e ? EX_DATAERR : EX_OK);
+}
diff --git a/usr.sbin/chown/Makefile b/usr.sbin/chown/Makefile
new file mode 100644
index 0000000..63c5a22
--- /dev/null
+++ b/usr.sbin/chown/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= chown
+LINKS= ${BINDIR}/chown /usr/bin/chgrp
+MAN= chgrp.1 chown.8
+
+WARNS?= 5
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/chown/chgrp.1 b/usr.sbin/chown/chgrp.1
new file mode 100644
index 0000000..0b3a789
--- /dev/null
+++ b/usr.sbin/chown/chgrp.1
@@ -0,0 +1,141 @@
+.\" Copyright (c) 1983, 1990, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, 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.
+.\"
+.\" @(#)chgrp.1 8.3 (Berkeley) 3/31/94
+.\" $FreeBSD$
+.\"
+.Dd April 25, 2003
+.Dt CHGRP 1
+.Os
+.Sh NAME
+.Nm chgrp
+.Nd change group
+.Sh SYNOPSIS
+.Nm
+.Op Fl fhv
+.Oo
+.Fl R
+.Op Fl H | Fl L | Fl P
+.Oc
+.Ar group
+.Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility sets the group ID of the file named by each
+.Ar file
+operand to the
+.Ar group
+ID specified by the group operand.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl H
+If the
+.Fl R
+option is specified, symbolic links on the command line are followed.
+(Symbolic links encountered in the tree traversal are not followed).
+.It Fl L
+If the
+.Fl R
+option is specified, all symbolic links are followed.
+.It Fl P
+If the
+.Fl R
+option is specified, no symbolic links are followed.
+This is the default.
+.It Fl R
+Change the group ID for the file hierarchies rooted
+in the files instead of just the files themselves.
+.It Fl f
+The force option ignores errors, except for usage errors and does not
+query about strange modes (unless the user does not have proper permissions).
+.It Fl h
+If the file is a symbolic link, the group ID of the link itself is changed
+rather than the file that is pointed to.
+.It Fl v
+Cause
+.Nm
+to be verbose, showing files as the group is modified.
+If the
+.Fl v
+flag is specified more than once,
+.Nm
+will print the filename, followed by the old and new numeric group ID.
+.El
+.Pp
+The
+.Fl H ,
+.Fl L
+and
+.Fl P
+options are ignored unless the
+.Fl R
+option is specified.
+In addition, these options override each other and the
+command's actions are determined by the last one specified.
+.Pp
+The
+.Ar group
+operand can be either a group name from the group database,
+or a numeric group ID.
+If a group name is also a numeric group ID, the operand is used as a
+group name.
+.Pp
+The user invoking
+.Nm
+must belong to the specified group and be the owner of the file,
+or be the super-user.
+.Sh FILES
+.Bl -tag -width /etc/group -compact
+.It Pa /etc/group
+group ID file
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh COMPATIBILITY
+In previous versions of this system, symbolic links did not have groups.
+.Pp
+The
+.Fl v
+option is non-standard and its use in scripts is not recommended.
+.Sh SEE ALSO
+.Xr chown 2 ,
+.Xr fts 3 ,
+.Xr group 5 ,
+.Xr passwd 5 ,
+.Xr symlink 7 ,
+.Xr chown 8
+.Sh STANDARDS
+The
+.Nm
+utility is expected to be
+.St -p1003.2
+compatible.
diff --git a/usr.sbin/chown/chown.8 b/usr.sbin/chown/chown.8
new file mode 100644
index 0000000..f617f73
--- /dev/null
+++ b/usr.sbin/chown/chown.8
@@ -0,0 +1,166 @@
+.\" Copyright (c) 1990, 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.
+.\"
+.\" @(#)chown.8 8.3 (Berkeley) 3/31/94
+.\" $FreeBSD$
+.\"
+.Dd April 25, 2003
+.Dt CHOWN 8
+.Os
+.Sh NAME
+.Nm chown
+.Nd change file owner and group
+.Sh SYNOPSIS
+.Nm
+.Op Fl fhv
+.Oo
+.Fl R
+.Op Fl H | Fl L | Fl P
+.Oc
+.Ar owner Ns Op : Ns Ar group
+.Ar
+.Nm
+.Op Fl fhv
+.Oo
+.Fl R
+.Op Fl H | Fl L | Fl P
+.Oc
+.No : Ns Ar group
+.Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility changes the user ID and/or the group ID of the specified files.
+Symbolic links named by arguments are silently left unchanged unless
+.Fl h
+is used.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl H
+If the
+.Fl R
+option is specified, symbolic links on the command line are followed.
+(Symbolic links encountered in the tree traversal are not followed.)
+.It Fl L
+If the
+.Fl R
+option is specified, all symbolic links are followed.
+.It Fl P
+If the
+.Fl R
+option is specified, no symbolic links are followed.
+This is the default.
+.It Fl R
+Change the user ID and/or the group ID of the specified directory trees
+(recursively, including their contents) and files.
+Beware of unintentionally matching the
+.Dq Pa ".."
+hard link to the parent directory when using wildcards like
+.Dq Li ".*" .
+.It Fl f
+Do not report any failure to change file owner or group, nor modify
+the exit status to reflect such failures.
+.It Fl h
+If the file is a symbolic link, change the user ID and/or the
+group ID of the link itself.
+.It Fl v
+Cause
+.Nm
+to be verbose, showing files as the owner is modified.
+If the
+.Fl v
+flag is specified more than once,
+.Nm
+will print the filename, followed by the old and new numeric user/group ID.
+.El
+.Pp
+The
+.Fl H ,
+.Fl L
+and
+.Fl P
+options are ignored unless the
+.Fl R
+option is specified.
+In addition, these options override each other and the
+command's actions are determined by the last one specified.
+.Pp
+The
+.Ar owner
+and
+.Ar group
+operands are both optional, however, one must be specified.
+If the
+.Ar group
+operand is specified, it must be preceded by a colon (``:'') character.
+.Pp
+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.
+If a group name is also a numeric group ID, the operand is used as a
+group name.
+.Pp
+The ownership of a file may only be altered by a super-user for
+obvious security reasons.
+.Sh EXIT STATUS
+.Ex -std
+.Sh COMPATIBILITY
+Previous versions of the
+.Nm
+utility used the dot (``.'') character to distinguish the group name.
+This has been changed to be a colon (``:'') character so that user and
+group names may contain the dot character.
+.Pp
+On previous versions of this system, symbolic links did not have
+owners.
+.Pp
+The
+.Fl v
+option is non-standard and its use in scripts is not recommended.
+.Sh SEE ALSO
+.Xr chgrp 1 ,
+.Xr find 1 ,
+.Xr chown 2 ,
+.Xr fts 3 ,
+.Xr symlink 7
+.Sh STANDARDS
+The
+.Nm
+utility is expected to be
+.St -p1003.2
+compliant.
+.Sh HISTORY
+A
+.Nm
+utility appeared in
+.At v1 .
diff --git a/usr.sbin/chown/chown.c b/usr.sbin/chown/chown.c
new file mode 100644
index 0000000..0918265
--- /dev/null
+++ b/usr.sbin/chown/chown.c
@@ -0,0 +1,307 @@
+/*
+ * Copyright (c) 1988, 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) 1988, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)chown.c 8.8 (Berkeley) 4/4/94";
+#endif /* not lint */
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <grp.h>
+#include <libgen.h>
+#include <pwd.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void a_gid(const char *);
+void a_uid(const char *);
+void chownerr(const char *);
+uid_t id(const char *, const char *);
+void usage(void);
+
+uid_t uid;
+gid_t gid;
+int ischown;
+const char *gname;
+
+int
+main(int argc, char **argv)
+{
+ FTS *ftsp;
+ FTSENT *p;
+ int Hflag, Lflag, Rflag, fflag, hflag, vflag;
+ int ch, fts_options, rval;
+ char *cp;
+
+ ischown = (strcmp(basename(argv[0]), "chown") == 0);
+
+ Hflag = Lflag = Rflag = fflag = hflag = vflag = 0;
+ while ((ch = getopt(argc, argv, "HLPRfhv")) != -1)
+ switch (ch) {
+ case 'H':
+ Hflag = 1;
+ Lflag = 0;
+ break;
+ case 'L':
+ Lflag = 1;
+ Hflag = 0;
+ break;
+ case 'P':
+ Hflag = Lflag = 0;
+ break;
+ case 'R':
+ Rflag = 1;
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 'h':
+ hflag = 1;
+ break;
+ case 'v':
+ vflag++;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (argc < 2)
+ usage();
+
+ if (Rflag) {
+ fts_options = FTS_PHYSICAL;
+ if (hflag && (Hflag || Lflag))
+ errx(1, "the -R%c and -h options may not be "
+ "specified together", Hflag ? 'H' : 'L');
+ if (Hflag)
+ fts_options |= FTS_COMFOLLOW;
+ else if (Lflag) {
+ fts_options &= ~FTS_PHYSICAL;
+ fts_options |= FTS_LOGICAL;
+ }
+ } else
+ fts_options = hflag ? FTS_PHYSICAL : FTS_LOGICAL;
+
+ uid = (uid_t)-1;
+ gid = (gid_t)-1;
+ if (ischown) {
+ if ((cp = strchr(*argv, ':')) != NULL) {
+ *cp++ = '\0';
+ a_gid(cp);
+ }
+#ifdef SUPPORT_DOT
+ else if ((cp = strchr(*argv, '.')) != NULL) {
+ warnx("separation of user and group with a period is deprecated");
+ *cp++ = '\0';
+ a_gid(cp);
+ }
+#endif
+ a_uid(*argv);
+ } else
+ a_gid(*argv);
+
+ if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL)
+ err(1, NULL);
+
+ for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
+ switch (p->fts_info) {
+ case FTS_D: /* Change it at FTS_DP. */
+ if (!Rflag)
+ fts_set(ftsp, p, FTS_SKIP);
+ continue;
+ case FTS_DNR: /* Warn, chown. */
+ warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+ rval = 1;
+ break;
+ case FTS_ERR: /* Warn, continue. */
+ case FTS_NS:
+ warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+ rval = 1;
+ continue;
+ case FTS_SL:
+ case FTS_SLNONE:
+ /*
+ * The only symlinks that end up here are ones that
+ * don't point to anything and ones that we found
+ * doing a physical walk.
+ */
+ if (hflag)
+ break;
+ else
+ continue;
+ default:
+ break;
+ }
+ if ((uid == (uid_t)-1 || uid == p->fts_statp->st_uid) &&
+ (gid == (gid_t)-1 || gid == p->fts_statp->st_gid))
+ continue;
+ if ((hflag ? lchown : chown)(p->fts_accpath, uid, gid) == -1) {
+ if (!fflag) {
+ chownerr(p->fts_path);
+ rval = 1;
+ }
+ } else {
+ if (vflag) {
+ printf("%s", p->fts_path);
+ if (vflag > 1) {
+ if (ischown) {
+ printf(": %ju:%ju -> %ju:%ju",
+ (uintmax_t)
+ p->fts_statp->st_uid,
+ (uintmax_t)
+ p->fts_statp->st_gid,
+ (uid == (uid_t)-1) ?
+ (uintmax_t)
+ p->fts_statp->st_uid :
+ (uintmax_t)uid,
+ (gid == (gid_t)-1) ?
+ (uintmax_t)
+ p->fts_statp->st_gid :
+ (uintmax_t)gid);
+ } else {
+ printf(": %ju -> %ju",
+ (uintmax_t)
+ p->fts_statp->st_gid,
+ (gid == (gid_t)-1) ?
+ (uintmax_t)
+ p->fts_statp->st_gid :
+ (uintmax_t)gid);
+ }
+ }
+ printf("\n");
+ }
+ }
+ }
+ if (errno)
+ err(1, "fts_read");
+ exit(rval);
+}
+
+void
+a_gid(const char *s)
+{
+ struct group *gr;
+
+ if (*s == '\0') /* Argument was "uid[:.]". */
+ return;
+ gname = s;
+ gid = ((gr = getgrnam(s)) != NULL) ? gr->gr_gid : id(s, "group");
+}
+
+void
+a_uid(const char *s)
+{
+ struct passwd *pw;
+
+ if (*s == '\0') /* Argument was "[:.]gid". */
+ return;
+ uid = ((pw = getpwnam(s)) != NULL) ? pw->pw_uid : id(s, "user");
+}
+
+uid_t
+id(const char *name, const char *type)
+{
+ uid_t 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);
+}
+
+void
+chownerr(const char *file)
+{
+ static uid_t euid = -1;
+ static int ngroups = -1;
+ gid_t groups[NGROUPS_MAX];
+
+ /* Check for chown without being root. */
+ if (errno != EPERM || (uid != (uid_t)-1 &&
+ euid == (uid_t)-1 && (euid = geteuid()) != 0)) {
+ warn("%s", file);
+ return;
+ }
+
+ /* Check group membership; kernel just returns EPERM. */
+ if (gid != (gid_t)-1 && ngroups == -1 &&
+ euid == (uid_t)-1 && (euid = geteuid()) != 0) {
+ ngroups = getgroups(NGROUPS_MAX, groups);
+ while (--ngroups >= 0 && gid != groups[ngroups]);
+ if (ngroups < 0) {
+ warnx("you are not a member of group %s", gname);
+ return;
+ }
+ }
+ warn("%s", file);
+}
+
+void
+usage(void)
+{
+
+ if (ischown)
+ (void)fprintf(stderr, "%s\n%s\n",
+ "usage: chown [-fhv] [-R [-H | -L | -P]] owner[:group]"
+ " file ...",
+ " chown [-fhv] [-R [-H | -L | -P]] :group file ...");
+ else
+ (void)fprintf(stderr, "%s\n",
+ "usage: chgrp [-fhv] [-R [-H | -L | -P]] group file ...");
+ exit(1);
+}
diff --git a/usr.sbin/chroot/Makefile b/usr.sbin/chroot/Makefile
new file mode 100644
index 0000000..d28de66
--- /dev/null
+++ b/usr.sbin/chroot/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= chroot
+MAN= chroot.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/chroot/chroot.8 b/usr.sbin/chroot/chroot.8
new file mode 100644
index 0000000..d847175
--- /dev/null
+++ b/usr.sbin/chroot/chroot.8
@@ -0,0 +1,94 @@
+.\" 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.
+.\"
+.\" @(#)chroot.8 8.1 (Berkeley) 6/9/93
+.\" $FreeBSD$
+.\"
+.Dd June 7, 2003
+.Dt CHROOT 8
+.Os
+.Sh NAME
+.Nm chroot
+.Nd change root directory
+.Sh SYNOPSIS
+.Nm
+.Op Fl u Ar user
+.Op Fl g Ar group
+.Op Fl G Ar group,group,...
+.Ar newroot
+.Op Ar command
+.Sh DESCRIPTION
+The
+.Nm
+utility changes its current and root directories to the supplied directory
+.Ar newroot
+and then exec's
+.Ar command ,
+if supplied,
+or an interactive copy of the user's login shell.
+.Pp
+If the
+.Fl u ,
+.Fl g
+or
+.Fl G
+options are given,
+the user,
+group and group list of the process are set to
+these values after the
+.Xr chroot 8
+has taken place.
+.Sh ENVIRONMENT
+The following environment variable is referenced by
+.Nm :
+.Bl -tag -width ".Ev SHELL"
+.It Ev SHELL
+If set,
+the string specified by
+.Ev SHELL
+is interpreted as the name of
+the shell to exec.
+If the variable
+.Ev SHELL
+is not set,
+.Pa /bin/sh
+is used.
+.El
+.Sh SEE ALSO
+.Xr chdir 2 ,
+.Xr chroot 2 ,
+.Xr setgid 2 ,
+.Xr setgroups 2 ,
+.Xr setuid 2 ,
+.Xr getgrnam 3 ,
+.Xr environ 7 ,
+.Xr jail 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Bx 4.4 .
diff --git a/usr.sbin/chroot/chroot.c b/usr.sbin/chroot/chroot.c
new file mode 100644
index 0000000..f33db7a
--- /dev/null
+++ b/usr.sbin/chroot/chroot.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 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.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)chroot.c 8.1 (Berkeley) 6/9/93";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <grp.h>
+#include <limits.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static void usage(void);
+
+char *user; /* user to switch to before running program */
+char *group; /* group to switch to ... */
+char *grouplist; /* group list to switch to ... */
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct group *gp;
+ struct passwd *pw;
+ char *endp, *p;
+ const char *shell;
+ gid_t gid, gidlist[NGROUPS_MAX];
+ uid_t uid;
+ int ch, gids;
+
+ gid = 0;
+ uid = 0;
+ while ((ch = getopt(argc, argv, "G:g:u:")) != -1) {
+ switch(ch) {
+ case 'u':
+ user = optarg;
+ if (*user == '\0')
+ usage();
+ break;
+ case 'g':
+ group = optarg;
+ if (*group == '\0')
+ usage();
+ break;
+ case 'G':
+ grouplist = optarg;
+ if (*grouplist == '\0')
+ usage();
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1)
+ usage();
+
+ if (group != NULL) {
+ if (isdigit((unsigned char)*group)) {
+ gid = (gid_t)strtoul(group, &endp, 0);
+ if (*endp != '\0')
+ goto getgroup;
+ } else {
+ getgroup:
+ if ((gp = getgrnam(group)) != NULL)
+ gid = gp->gr_gid;
+ else
+ errx(1, "no such group `%s'", group);
+ }
+ }
+
+ for (gids = 0;
+ (p = strsep(&grouplist, ",")) != NULL && gids < NGROUPS_MAX; ) {
+ if (*p == '\0')
+ continue;
+
+ if (isdigit((unsigned char)*p)) {
+ gidlist[gids] = (gid_t)strtoul(p, &endp, 0);
+ if (*endp != '\0')
+ goto getglist;
+ } else {
+ getglist:
+ if ((gp = getgrnam(p)) != NULL)
+ gidlist[gids] = gp->gr_gid;
+ else
+ errx(1, "no such group `%s'", p);
+ }
+ gids++;
+ }
+ if (p != NULL && gids == NGROUPS_MAX)
+ errx(1, "too many supplementary groups provided");
+
+ if (user != NULL) {
+ if (isdigit((unsigned char)*user)) {
+ uid = (uid_t)strtoul(user, &endp, 0);
+ if (*endp != '\0')
+ goto getuser;
+ } else {
+ getuser:
+ if ((pw = getpwnam(user)) != NULL)
+ uid = pw->pw_uid;
+ else
+ errx(1, "no such user `%s'", user);
+ }
+ }
+
+ if (chdir(argv[0]) == -1 || chroot(".") == -1)
+ err(1, "%s", argv[0]);
+
+ if (gids && setgroups(gids, gidlist) == -1)
+ err(1, "setgroups");
+ if (group && setgid(gid) == -1)
+ err(1, "setgid");
+ if (user && setuid(uid) == -1)
+ err(1, "setuid");
+
+ if (argv[1]) {
+ execvp(argv[1], &argv[1]);
+ err(1, "%s", argv[1]);
+ }
+
+ if (!(shell = getenv("SHELL")))
+ shell = _PATH_BSHELL;
+ execlp(shell, shell, "-i", (char *)NULL);
+ err(1, "%s", shell);
+ /* NOTREACHED */
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "usage: chroot [-g group] [-G group,group,...] "
+ "[-u user] newroot [command]\n");
+ exit(1);
+}
diff --git a/usr.sbin/ckdist/Makefile b/usr.sbin/ckdist/Makefile
new file mode 100644
index 0000000..23430a8
--- /dev/null
+++ b/usr.sbin/ckdist/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../usr.bin/cksum
+
+PROG= ckdist
+SRCS= ckdist.c crc.c
+
+DPADD= ${LIBMD}
+LDADD= -lmd
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ckdist/ckdist.1 b/usr.sbin/ckdist/ckdist.1
new file mode 100644
index 0000000..91a8091
--- /dev/null
+++ b/usr.sbin/ckdist/ckdist.1
@@ -0,0 +1,133 @@
+.\" Copyright (c) 1997 Robert Nordier
+.\" All rights reserved.
+.\"
+.\" $FreeBSD$
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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.
+.\"
+.Dd January 20, 1997
+.Dt CKDIST 1
+.Os
+.Sh NAME
+.Nm ckdist
+.Nd check software distributions
+.Sh SYNOPSIS
+.Nm
+.Bq Fl airsx
+.Bq Fl d Ar dir
+.Bq Fl n Ar name
+.Bq Fl t Ar type
+.Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility reads
+.Dq checksum
+files (which are assumed to specify components
+of a software distribution) and verifies the integrity of the
+distribution by validating the checksum of each component file.
+Both MD5 (128-bit
+.Dq "message digest" )
+and
+.Pa .inf
+(32-bit CRC) checksum
+formats are supported.
+.Pp
+The
+.Ar file
+operands may refer to regular files or to directories.
+Regular files
+named
+.Pa md5 ,
+or which have an
+.Pa .md5
+or an
+.Pa .inf
+extension, are
+assumed to be of the implied type, otherwise format is determined from
+content.
+If a directory is specified, it is searched for
+appropriately-named files only.
+.Pp
+The options are as follows:
+.\"Bl -tag -width ".Fl n Ar name"
+.Bl -tag -width 8n -offset indent
+.It Fl a
+Report on all distribution components, not just those in respect of
+which errors are detected.
+.It Fl i
+Ignore missing distribution components.
+.It Fl r
+Search specified directories recursively.
+.It Fl s
+Suppress complaints about inaccessible checksum files and directories.
+.It Fl x
+Verify the existence of distribution components (and also check sizes,
+in the case of
+.Pa .inf
+files), but omit the more time-consuming step of
+actually computing and comparing checksums.
+.It Fl d Ar dir
+Look for distribution components in the directory
+.Ar dir .
+.It Fl n Ar name
+Access distribution components using the filename
+.Ar name .
+When accessing
+.Pa .inf
+file components, append the appropriate
+extension to the filename.
+.It Fl t Ar type
+Assume that all specified checksum files are of the format
+.Ar type ,
+and search directories only for files in this format (where
+.Ar type
+is either
+.Cm md5
+or
+.Cm inf ) .
+.El
+.Sh EXIT STATUS
+The
+.Nm
+utility exits with one of the following values:
+.Pp
+.Bl -tag -width indent
+.It 0
+No errors were detected.
+.It 1
+Errors were found in a distribution.
+.It 2
+Usage errors, inaccessible input files, or
+other system errors were encountered.
+.El
+.Sh SEE ALSO
+.Xr cksum 1 ,
+.Xr md5 1
+.Sh NOTES
+Both
+.Bx
+and
+.Tn DOS
+versions of
+.Nm
+are available.
diff --git a/usr.sbin/ckdist/ckdist.c b/usr.sbin/ckdist/ckdist.c
new file mode 100644
index 0000000..dcaa9f3
--- /dev/null
+++ b/usr.sbin/ckdist/ckdist.c
@@ -0,0 +1,445 @@
+/*
+ * Copyright (c) 1997 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/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <md5.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+extern int crc(int fd, uint32_t *cval, off_t *clen);
+
+#define DISTMD5 1 /* MD5 format */
+#define DISTINF 2 /* .inf format */
+#define DISTTYPES 2 /* types supported */
+
+#define E_UNKNOWN 1 /* Unknown format */
+#define E_BADMD5 2 /* Invalid MD5 format */
+#define E_BADINF 3 /* Invalid .inf format */
+#define E_NAME 4 /* Can't derive component name */
+#define E_LENGTH 5 /* Length mismatch */
+#define E_CHKSUM 6 /* Checksum mismatch */
+#define E_ERRNO 7 /* sys_errlist[errno] */
+
+#define isfatal(err) ((err) && (err) <= E_NAME)
+
+#define NAMESIZE 256 /* filename buffer size */
+#define MDSUMLEN 32 /* length of MD5 message digest */
+
+#define isstdin(path) ((path)[0] == '-' && !(path)[1])
+
+static const char *opt_dir; /* where to look for components */
+static const char *opt_name; /* name for accessing components */
+static int opt_all; /* report on all components */
+static int opt_ignore; /* ignore missing components */
+static int opt_recurse; /* search directories recursively */
+static int opt_silent; /* silent about inaccessible files */
+static int opt_type; /* dist type: md5 or inf */
+static int opt_exist; /* just verify existence */
+
+static int ckdist(const char *path, int type);
+static int chkmd5(FILE * fp, const char *path);
+static int chkinf(FILE * fp, const char *path);
+static int report(const char *path, const char *name, int error);
+static const char *distname(const char *path, const char *name,
+ const char *ext);
+static const char *stripath(const char *path);
+static int distfile(const char *path);
+static int disttype(const char *name);
+static int fail(const char *path, const char *msg);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ static char *arg[2];
+ struct stat sb;
+ FTS *ftsp;
+ FTSENT *f;
+ int rval, c, type;
+
+ while ((c = getopt(argc, argv, "ad:in:rst:x")) != -1)
+ switch (c) {
+ case 'a':
+ opt_all = 1;
+ break;
+ case 'd':
+ opt_dir = optarg;
+ break;
+ case 'i':
+ opt_ignore = 1;
+ break;
+ case 'n':
+ opt_name = optarg;
+ break;
+ case 'r':
+ opt_recurse = 1;
+ break;
+ case 's':
+ opt_silent = 1;
+ break;
+ case 't':
+ if ((opt_type = disttype(optarg)) == 0) {
+ warnx("illegal argument to -t option");
+ usage();
+ }
+ break;
+ case 'x':
+ opt_exist = 1;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc < 1)
+ usage();
+ if (opt_dir) {
+ if (stat(opt_dir, &sb))
+ err(2, "%s", opt_dir);
+ if (!S_ISDIR(sb.st_mode))
+ errx(2, "%s: not a directory", opt_dir);
+ }
+ rval = 0;
+ do {
+ if (isstdin(*argv))
+ rval |= ckdist(*argv, opt_type);
+ else if (stat(*argv, &sb))
+ rval |= fail(*argv, NULL);
+ else if (S_ISREG(sb.st_mode))
+ rval |= ckdist(*argv, opt_type);
+ else {
+ arg[0] = *argv;
+ if ((ftsp = fts_open(arg, FTS_LOGICAL, NULL)) == NULL)
+ err(2, "fts_open");
+ while ((f = fts_read(ftsp)) != NULL)
+ switch (f->fts_info) {
+ case FTS_DC:
+ rval = fail(f->fts_path, "Directory causes a cycle");
+ break;
+ case FTS_DNR:
+ case FTS_ERR:
+ case FTS_NS:
+ rval = fail(f->fts_path, sys_errlist[f->fts_errno]);
+ break;
+ case FTS_D:
+ if (!opt_recurse && f->fts_level > FTS_ROOTLEVEL &&
+ fts_set(ftsp, f, FTS_SKIP))
+ err(2, "fts_set");
+ break;
+ case FTS_F:
+ if ((type = distfile(f->fts_name)) != 0 &&
+ (!opt_type || type == opt_type))
+ rval |= ckdist(f->fts_path, type);
+ break;
+ default: ;
+ }
+ if (errno)
+ err(2, "fts_read");
+ if (fts_close(ftsp))
+ err(2, "fts_close");
+ }
+ } while (*++argv);
+ return rval;
+}
+
+static int
+ckdist(const char *path, int type)
+{
+ FILE *fp;
+ int rval, c;
+
+ if (isstdin(path)) {
+ path = "(stdin)";
+ fp = stdin;
+ } else if ((fp = fopen(path, "r")) == NULL)
+ return fail(path, NULL);
+ if (!type) {
+ if (fp != stdin)
+ type = distfile(path);
+ if (!type)
+ if ((c = fgetc(fp)) != EOF) {
+ type = c == 'M' ? DISTMD5 : c == 'P' ? DISTINF : 0;
+ (void)ungetc(c, fp);
+ }
+ }
+ switch (type) {
+ case DISTMD5:
+ rval = chkmd5(fp, path);
+ break;
+ case DISTINF:
+ rval = chkinf(fp, path);
+ break;
+ default:
+ rval = report(path, NULL, E_UNKNOWN);
+ }
+ if (ferror(fp))
+ warn("%s", path);
+ if (fp != stdin && fclose(fp))
+ err(2, "%s", path);
+ return rval;
+}
+
+static int
+chkmd5(FILE * fp, const char *path)
+{
+ char buf[298]; /* "MD5 (NAMESIZE = MDSUMLEN" */
+ char name[NAMESIZE + 1];
+ char sum[MDSUMLEN + 1], chk[MDSUMLEN + 1];
+ const char *dname;
+ char *s;
+ int rval, error, c, fd;
+ char ch;
+
+ rval = 0;
+ while (fgets(buf, sizeof(buf), fp)) {
+ dname = NULL;
+ error = 0;
+ if (((c = sscanf(buf, "MD5 (%256s = %32s%c", name, sum,
+ &ch)) != 3 && (!feof(fp) || c != 2)) ||
+ (c == 3 && ch != '\n') ||
+ (s = strrchr(name, ')')) == NULL ||
+ strlen(sum) != MDSUMLEN)
+ error = E_BADMD5;
+ else {
+ *s = 0;
+ if ((dname = distname(path, name, NULL)) == NULL)
+ error = E_NAME;
+ else if (opt_exist) {
+ if ((fd = open(dname, O_RDONLY)) == -1)
+ error = E_ERRNO;
+ else if (close(fd))
+ err(2, "%s", dname);
+ } else if (!MD5File(dname, chk))
+ error = E_ERRNO;
+ else if (strcmp(chk, sum))
+ error = E_CHKSUM;
+ }
+ if (opt_ignore && error == E_ERRNO && errno == ENOENT)
+ continue;
+ if (error || opt_all)
+ rval |= report(path, dname, error);
+ if (isfatal(error))
+ break;
+ }
+ return rval;
+}
+
+static int
+chkinf(FILE * fp, const char *path)
+{
+ char buf[30]; /* "cksum.2 = 10 6" */
+ char ext[3];
+ struct stat sb;
+ const char *dname;
+ off_t len;
+ u_long sum;
+ intmax_t sumlen;
+ uint32_t chk;
+ int rval, error, c, pieces, cnt, fd;
+ char ch;
+
+ rval = 0;
+ for (cnt = -1; fgets(buf, sizeof(buf), fp); cnt++) {
+ fd = -1;
+ dname = NULL;
+ error = 0;
+ if (cnt == -1) {
+ if ((c = sscanf(buf, "Pieces = %d%c", &pieces, &ch)) != 2 ||
+ ch != '\n' || pieces < 1)
+ error = E_BADINF;
+ } else if (((c = sscanf(buf, "cksum.%2s = %lu %jd%c", ext, &sum,
+ &sumlen, &ch)) != 4 &&
+ (!feof(fp) || c != 3)) || (c == 4 && ch != '\n') ||
+ ext[0] != 'a' + cnt / 26 || ext[1] != 'a' + cnt % 26)
+ error = E_BADINF;
+ else if ((dname = distname(fp == stdin ? NULL : path, NULL,
+ ext)) == NULL)
+ error = E_NAME;
+ else if ((fd = open(dname, O_RDONLY)) == -1)
+ error = E_ERRNO;
+ else if (fstat(fd, &sb))
+ error = E_ERRNO;
+ else if (sb.st_size != (off_t)sumlen)
+ error = E_LENGTH;
+ else if (!opt_exist) {
+ if (crc(fd, &chk, &len))
+ error = E_ERRNO;
+ else if (chk != sum)
+ error = E_CHKSUM;
+ }
+ if (fd != -1 && close(fd))
+ err(2, "%s", dname);
+ if (opt_ignore && error == E_ERRNO && errno == ENOENT)
+ continue;
+ if (error || (opt_all && cnt >= 0))
+ rval |= report(path, dname, error);
+ if (isfatal(error))
+ break;
+ }
+ return rval;
+}
+
+static int
+report(const char *path, const char *name, int error)
+{
+ if (name)
+ name = stripath(name);
+ switch (error) {
+ case E_UNKNOWN:
+ printf("%s: Unknown format\n", path);
+ break;
+ case E_BADMD5:
+ printf("%s: Invalid MD5 format\n", path);
+ break;
+ case E_BADINF:
+ printf("%s: Invalid .inf format\n", path);
+ break;
+ case E_NAME:
+ printf("%s: Can't derive component name\n", path);
+ break;
+ case E_LENGTH:
+ printf("%s: %s: Size mismatch\n", path, name);
+ break;
+ case E_CHKSUM:
+ printf("%s: %s: Checksum mismatch\n", path, name);
+ break;
+ case E_ERRNO:
+ printf("%s: %s: %s\n", path, name, sys_errlist[errno]);
+ break;
+ default:
+ printf("%s: %s: OK\n", path, name);
+ }
+ return error != 0;
+}
+
+static const char *
+distname(const char *path, const char *name, const char *ext)
+{
+ static char buf[NAMESIZE];
+ size_t plen, nlen;
+ char *s;
+
+ if (opt_name)
+ name = opt_name;
+ else if (!name) {
+ if (!path)
+ return NULL;
+ name = stripath(path);
+ }
+ nlen = strlen(name);
+ if (ext && nlen > 4 && name[nlen - 4] == '.' &&
+ disttype(name + nlen - 3) == DISTINF)
+ nlen -= 4;
+ if (opt_dir) {
+ path = opt_dir;
+ plen = strlen(path);
+ } else
+ plen = path && (s = strrchr(path, '/')) != NULL ?
+ (size_t)(s - path) : 0;
+ if (plen + (plen > 0) + nlen + (ext ? 3 : 0) >= sizeof(buf))
+ return NULL;
+ s = buf;
+ if (plen) {
+ memcpy(s, path, plen);
+ s += plen;
+ *s++ = '/';
+ }
+ memcpy(s, name, nlen);
+ s += nlen;
+ if (ext) {
+ *s++ = '.';
+ memcpy(s, ext, 2);
+ s += 2;
+ }
+ *s = 0;
+ return buf;
+}
+
+static const char *
+stripath(const char *path)
+{
+ const char *s;
+
+ return ((s = strrchr(path, '/')) != NULL && s[1] ?
+ s + 1 : path);
+}
+
+static int
+distfile(const char *path)
+{
+ const char *s;
+ int type;
+
+ if ((type = disttype(path)) == DISTMD5 ||
+ ((s = strrchr(path, '.')) != NULL && s > path &&
+ (type = disttype(s + 1)) != 0))
+ return type;
+ return 0;
+}
+
+static int
+disttype(const char *name)
+{
+ static const char dname[DISTTYPES][4] = {"md5", "inf"};
+ int i;
+
+ for (i = 0; i < DISTTYPES; i++)
+ if (!strcmp(dname[i], name))
+ return 1 + i;
+ return 0;
+}
+
+static int
+fail(const char *path, const char *msg)
+{
+ if (opt_silent)
+ return 0;
+ warnx("%s: %s", path, msg ? msg : sys_errlist[errno]);
+ return 2;
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: ckdist [-airsx] [-d dir] [-n name] [-t type] file ...\n");
+ exit(2);
+}
diff --git a/usr.sbin/clear_locks/Makefile b/usr.sbin/clear_locks/Makefile
new file mode 100644
index 0000000..dace0e1
--- /dev/null
+++ b/usr.sbin/clear_locks/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= clear_locks
+MAN= clear_locks.8
+DPADD= ${LIBRPCSVC}
+LDADD= -lrpcsvc
+WARNS= 6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/clear_locks/clear_locks.8 b/usr.sbin/clear_locks/clear_locks.8
new file mode 100644
index 0000000..9f6cafe
--- /dev/null
+++ b/usr.sbin/clear_locks/clear_locks.8
@@ -0,0 +1,51 @@
+.\" Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+.\" Authors: Doug Rabson <dfr@rabson.org>
+.\" Developed with Red Inc: Alfred Perlstein <alfred@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$
+.\"
+.Dd March 19, 2008
+.Dt CLEAR_LOCKS 8
+.Os
+.Sh NAME
+.Nm clear_locks
+.Nd clear locks held on behalf of an NFS client
+.Sh SYNOPSIS
+.Nm
+.Ar hostname
+.Sh DESCRIPTION
+The
+.Nm
+command can be used to clear file locks held by an NFS client.
+This should only be used to handle problems caused by an NFS client
+crashing while holding locks and failing to clear them itself when it
+reboots.
+.Sh SEE ALSO
+.Xr rpc.lockd 8
+.Sh HISTORY
+A version of
+.Nm
+appeared in
+.Tn SunOS
+4.
diff --git a/usr.sbin/clear_locks/clear_locks.c b/usr.sbin/clear_locks/clear_locks.c
new file mode 100644
index 0000000..1249c12
--- /dev/null
+++ b/usr.sbin/clear_locks/clear_locks.c
@@ -0,0 +1,70 @@
+/*-
+ * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+ * Authors: Doug Rabson <dfr@rabson.org>
+ * Developed with Red Inc: Alfred Perlstein <alfred@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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <rpc/rpc.h>
+#include <rpcsvc/nlm_prot.h>
+
+int
+main(int argc, char **argv)
+{
+ enum clnt_stat stat;
+ char *hostname;
+ nlm4_notify notify;
+
+ if (argc != 2) {
+ fprintf(stderr, "Usage: clear_locks <hostname>\n");
+ exit(1);
+ }
+ hostname = argv[1];
+
+ if (geteuid() != 0) {
+ fprintf(stderr, "clear_locks: must be root\n");
+ exit(1);
+ }
+
+ notify.name = hostname;
+ notify.state = 0;
+ stat = rpc_call("localhost", NLM_PROG, NLM_VERS4, NLM4_FREE_ALL,
+ (xdrproc_t) xdr_nlm4_notify, (void *) &notify,
+ (xdrproc_t) xdr_void, NULL, NULL);
+
+ if (stat != RPC_SUCCESS) {
+ clnt_perrno(stat);
+ exit(1);
+ }
+ fprintf(stderr, "clear_locks: cleared locks for hostname %s\n",
+ hostname);
+
+ return (0);
+}
diff --git a/usr.sbin/config/Makefile b/usr.sbin/config/Makefile
new file mode 100644
index 0000000..ac82881
--- /dev/null
+++ b/usr.sbin/config/Makefile
@@ -0,0 +1,22 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= config
+MAN= config.5 config.8
+SRCS= config.y main.c lang.l mkmakefile.c mkheaders.c \
+ mkoptions.c y.tab.h kernconf.c
+
+kernconf.c: kernconf.tmpl
+ file2c 'char kernconfstr[] = {' ',0};' < ${.CURDIR}/kernconf.tmpl > kernconf.c
+
+WARNS?= 6
+CFLAGS+= -I. -I${.CURDIR}
+
+DPADD= ${LIBL} ${LIBSBUF}
+LDADD= -ll -lsbuf
+
+CLEANFILES+= kernconf.c
+
+mkmakefile.o: configvers.h
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/config/SMM.doc/0.t b/usr.sbin/config/SMM.doc/0.t
new file mode 100644
index 0000000..ae5bf77
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/0.t
@@ -0,0 +1,88 @@
+.\" 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.
+.\"
+.\" @(#)0.t 8.1 (Berkeley) 7/5/93
+.\"
+.bd S B 3
+.de UX
+.ie \\n(GA>0 \\$2UNIX\\$1
+.el \{\
+.if n \\$2UNIX\\$1*
+.if t \\$2UNIX\\$1\\f1\(dg\\fP
+.FS
+.if n *UNIX
+.if t \(dgUNIX
+.ie \\$3=1 is a Footnote of Bell Laboratories.
+.el is a Trademark of Bell Laboratories.
+.FE
+.nr GA 1\}
+..
+.de BR
+\fB\\$1\fP\\$2
+..
+.TL
+Building 4.4BSD Kernels with Config
+.AU
+Samuel J. Leffler and Michael J. Karels
+.AI
+Computer Systems Research Group
+Department of Electrical Engineering and Computer Science
+University of California, Berkeley
+Berkeley, California 94720
+.de IR
+\fI\\$1\fP\\$2
+..
+.de DT
+.TA 8 16 24 32 40 48 56 64 72 80
+..
+.AB
+.PP
+This document describes the use of
+\fIconfig\fP\|(8) to configure and create bootable
+4.4BSD system images.
+It discusses the structure of system
+configuration files and how to configure
+systems with non-standard hardware configurations.
+Sections describing the preferred way to
+add new code to the system and how the system's autoconfiguration
+process operates are included. An appendix
+contains a summary of the rules used by the system
+in calculating the size of system data structures,
+and also indicates some of the standard system size
+limitations (and how to change them).
+Other configuration options are also listed.
+.sp
+.LP
+Revised July 5, 1993
+.AE
+.LP
+.OH 'Building 4.4BSD Kernels with Config''SMM:2-%'
+.EH 'SMM:2-%''Building 4.4BSD Kernels with Config'
diff --git a/usr.sbin/config/SMM.doc/1.t b/usr.sbin/config/SMM.doc/1.t
new file mode 100644
index 0000000..453041b
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/1.t
@@ -0,0 +1,61 @@
+.\" 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.
+.\"
+.\" @(#)1.t 8.1 (Berkeley) 6/8/93
+.\"
+.\".ds RH Introduction
+.ne 2i
+.sp 3
+.NH
+INTRODUCTION
+.PP
+.I Config
+is a tool used in building 4.4BSD system images (the UNIX kernel).
+It takes a file describing a system's tunable parameters and
+hardware support, and generates a collection
+of files which are then used to build a copy of UNIX appropriate
+to that configuration.
+.I Config
+simplifies system maintenance by isolating system dependencies
+in a single, easy to understand, file.
+.PP
+This document describes the content and
+format of system configuration
+files and the rules which must be followed when creating
+these files. Example configuration files are constructed
+and discussed.
+.PP
+Later sections suggest guidelines to be used in modifying
+system source and explain some of the inner workings of the
+autoconfiguration process. Appendix D summarizes the rules
+used in calculating the most important system data structures
+and indicates some inherent system data structure size
+limitations (and how to go about modifying them).
diff --git a/usr.sbin/config/SMM.doc/2.t b/usr.sbin/config/SMM.doc/2.t
new file mode 100644
index 0000000..34e6b63
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/2.t
@@ -0,0 +1,188 @@
+.\" 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.
+.\"
+.\" @(#)2.t 8.1 (Berkeley) 6/8/93
+.\"
+.\".ds RH "Configuration File Contents
+.ne 2i
+.NH
+CONFIGURATION FILE CONTENTS
+.PP
+A system configuration must include at least the following
+pieces of information:
+.IP \(bu 3
+machine type
+.IP \(bu 3
+cpu type
+.IP \(bu 3
+system identification
+.IP \(bu 3
+timezone
+.IP \(bu 3
+maximum number of users
+.IP \(bu 3
+location of the root file system
+.IP \(bu 3
+available hardware
+.PP
+.I Config
+allows multiple system images to be generated from a single
+configuration description. Each system image is configured
+for identical hardware, but may have different locations for the root
+file system and, possibly, other system devices.
+.NH 2
+Machine type
+.PP
+The
+.I "machine type"
+indicates if the system is going to operate on a DEC VAX-11\(dg computer,
+.FS
+\(dg DEC, VAX, UNIBUS, MASSBUS and MicroVAX are trademarks of Digital
+Equipment Corporation.
+.FE
+or some other machine on which 4.4BSD operates. The machine type
+is used to locate certain data files which are machine specific, and
+also to select rules used in constructing the resultant
+configuration files.
+.NH 2
+Cpu type
+.PP
+The
+.I "cpu type"
+indicates which, of possibly many, cpu's the system is to operate on.
+For example, if the system is being configured for a VAX-11, it could
+be running on a VAX 8600, VAX-11/780, VAX-11/750, VAX-11/730 or MicroVAX II.
+(Other VAX cpu types, including the 8650, 785 and 725, are configured using
+the cpu designation for compatible machines introduced earlier.)
+Specifying
+more than one cpu type implies that the system should be configured to run
+on any of the cpu's specified. For some types of machines this is not
+possible and
+.I config
+will print a diagnostic indicating such.
+.NH 2
+System identification
+.PP
+The
+.I "system identification"
+is a moniker attached to the system, and often the machine on which the
+system is to run. For example, at Berkeley we have machines named Ernie
+(Co-VAX), Kim (No-VAX), and so on. The system identifier selected is used to
+create a global C ``#define'' which may be used to isolate system dependent
+pieces of code in the kernel. For example, Ernie's Varian driver used
+to be special cased because its interrupt vectors were wired together. The
+code in the driver which understood how to handle this non-standard hardware
+configuration was conditionally compiled in only if the system
+was for Ernie.
+.PP
+The system identifier ``GENERIC'' is given to a system which
+will run on any cpu of a particular machine type; it should not
+otherwise be used for a system identifier.
+.NH 2
+Timezone
+.PP
+The timezone in which the system is to run is used to define the
+information returned by the \fIgettimeofday\fP\|(2)
+system call. This value is specified as the number of hours east
+or west of GMT. Negative numbers indicate a value east of GMT.
+The timezone specification may also indicate the
+type of daylight savings time rules to be applied.
+.NH 2
+Maximum number of users
+.PP
+The system allocates many system data structures at boot time
+based on the maximum number of users the system will support.
+This number is normally between 8 and 40, depending
+on the hardware and expected job mix. The rules
+used to calculate system data structures are discussed in
+Appendix D.
+.NH 2
+Root file system location
+.PP
+When the system boots it must know the location of
+the root of the file system
+tree. This location and the part(s) of the disk(s) to be used
+for paging and swapping must be specified in order to create
+a complete configuration description.
+.I Config
+uses many rules to calculate default locations for these items;
+these are described in Appendix B.
+.PP
+When a generic system is configured, the root file system is left
+undefined until the system is booted. In this case, the root file
+system need not be specified, only that the system is a generic system.
+.NH 2
+Hardware devices
+.PP
+When the system boots it goes through an
+.I autoconfiguration
+phase. During this period, the system searches for all
+those hardware devices
+which the system builder has indicated might be present. This probing
+sequence requires certain pieces of information such as register
+addresses, bus interconnects, etc. A system's hardware may be configured
+in a very flexible manner or be specified without any flexibility
+whatsoever. Most people do not configure hardware devices into the
+system unless they are currently present on the machine, expect
+them to be present in the near future, or are simply guarding
+against a hardware
+failure somewhere else at the site (it is often wise to configure in
+extra disks in case an emergency requires moving one off a machine which
+has hardware problems).
+.PP
+The specification of hardware devices usually occupies the majority of
+the configuration file. As such, a large portion of this document will
+be spent understanding it. Section 6.3 contains a description of
+the autoconfiguration process, as it applies to those planning to
+write, or modify existing, device drivers.
+.NH 2
+Pseudo devices
+.PP
+Several system facilities are configured in a manner like that used
+for hardware devices although they are not associated with specific hardware.
+These system options are configured as
+.IR pseudo-devices .
+Some pseudo devices allow an optional parameter that sets the limit
+on the number of instances of the device that are active simultaneously.
+.NH 2
+System options
+.PP
+Other than the mandatory pieces of information described above, it
+is also possible to include various optional system facilities
+or to modify system behavior and/or limits.
+For example, 4.4BSD can be configured to support binary compatibility for
+programs built under 4.3BSD. Also, optional support is provided
+for disk quotas and tracing the performance of the virtual memory
+subsystem. Any optional facilities to be configured into
+the system are specified in the configuration file. The resultant
+files generated by
+.I config
+will automatically include the necessary pieces of the system.
diff --git a/usr.sbin/config/SMM.doc/3.t b/usr.sbin/config/SMM.doc/3.t
new file mode 100644
index 0000000..e0b6234
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/3.t
@@ -0,0 +1,299 @@
+.\" 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.
+.\"
+.\" @(#)3.t 8.1 (Berkeley) 6/8/93
+.\"
+.\".ds RH "System Building Process
+.ne 2i
+.NH
+SYSTEM BUILDING PROCESS
+.PP
+In this section we consider the steps necessary to build a bootable system
+image. We assume the system source is located in the ``/sys'' directory
+and that, initially, the system is being configured from source code.
+.PP
+Under normal circumstances there are 5 steps in building a system.
+.IP 1) 3
+Create a configuration file for the system.
+.IP 2) 3
+Make a directory for the system to be constructed in.
+.IP 3) 3
+Run
+.I config
+on the configuration file to generate the files required
+to compile and load the system image.
+.IP 4)
+Construct the source code interdependency rules for the
+configured system with
+.I make depend
+using
+.IR make (1).
+.IP 5)
+Compile and load the system with
+.IR make .
+.PP
+Steps 1 and 2 are usually done only once. When a system configuration
+changes it usually suffices to just run
+.I config
+on the modified configuration file, rebuild the source code dependencies,
+and remake the system. Sometimes,
+however, configuration dependencies may not be noticed in which case
+it is necessary to clean out the relocatable object files saved
+in the system's directory; this will be discussed later.
+.NH 2
+Creating a configuration file
+.PP
+Configuration files normally reside in the directory ``/sys/conf''.
+A configuration file is most easily constructed by copying an
+existing configuration file and modifying it. The 4.4BSD distribution
+contains a number of configuration files for machines at Berkeley;
+one may be suitable or, in worst case, a copy
+of the generic configuration file may be edited.
+.PP
+The configuration file must have the same name as the directory in
+which the configured system is to be built.
+Further,
+.I config
+assumes this directory is located in the parent directory of
+the directory in which it
+is run. For example, the generic
+system has a configuration file ``/sys/conf/GENERIC'' and an accompanying
+directory named ``/sys/GENERIC''.
+Although it is not required that the system sources and configuration
+files reside in ``/sys,'' the configuration and compilation procedure
+depends on the relative locations of directories within that hierarchy,
+as most of the system code and the files created by
+.I config
+use pathnames of the form ``../''.
+If the system files are not located in ``/sys,''
+it is desirable to make a symbolic link there for use in installation
+of other parts of the system that share files with the kernel.
+.PP
+When building the configuration file, be sure to include the items
+described in section 2. In particular, the machine type,
+cpu type, timezone, system identifier, maximum users, and root device
+must be specified. The specification of the hardware present may take
+a bit of work; particularly if your hardware is configured at non-standard
+places (e.g. device registers located at funny places or devices not
+supported by the system). Section 4 of this document
+gives a detailed description of the configuration file syntax,
+section 5 explains some sample configuration files, and
+section 6 discusses how to add new devices to
+the system. If the devices to be configured are not already
+described in one of the existing configuration files you should check
+the manual pages in section 4 of the UNIX Programmers Manual. For each
+supported device, the manual page synopsis entry gives a
+sample configuration line.
+.PP
+Once the configuration file is complete, run it through
+.I config
+and look for any errors. Never try and use a system which
+.I config
+has complained about; the results are unpredictable.
+For the most part,
+.IR config 's
+error diagnostics are self explanatory. It may be the case that
+the line numbers given with the error messages are off by one.
+.PP
+A successful run of
+.I config
+on your configuration file will generate a number of files in
+the configuration directory. These files are:
+.IP \(bu 3
+A file to be used by \fImake\fP\|(1)
+in compiling and loading the system,
+.IR Makefile .
+.IP \(bu 3
+One file for each possible system image for this machine,
+.IR swapxxx.c ,
+where
+.I xxx
+is the name of the system image,
+which describes where swapping, the root file system, and other
+miscellaneous system devices are located.
+.IP \(bu 3
+A collection of header files, one per possible device the
+system supports, which define the hardware configured.
+.IP \(bu 3
+A file containing the I/O configuration tables used by the system
+during its
+.I autoconfiguration
+phase,
+.IR ioconf.c .
+.IP \(bu 3
+An assembly language file of interrupt vectors which
+connect interrupts from the machine's external buses to the main
+system path for handling interrupts,
+and a file that contains counters and names for the interrupt vectors.
+.PP
+Unless you have reason to doubt
+.IR config ,
+or are curious how the system's autoconfiguration scheme
+works, you should never have to look at any of these files.
+.NH 2
+Constructing source code dependencies
+.PP
+When
+.I config
+is done generating the files needed to compile and link your system it
+will terminate with a message of the form ``Don't forget to run make depend''.
+This is a reminder that you should change over to the configuration
+directory for the system just configured and type ``make depend''
+to build the rules used by
+.I make
+to recognize interdependencies in the system source code.
+This will insure that any changes to a piece of the system
+source code will result in the proper modules being recompiled
+the next time
+.I make
+is run.
+.PP
+This step is particularly important if your site makes changes
+to the system include files. The rules generated specify which source code
+files are dependent on which include files. Without these rules,
+.I make
+will not recognize when it must rebuild modules
+due to the modification of a system header file.
+The dependency rules are generated by a pass of the C preprocessor
+and reflect the global system options.
+This step must be repeated when the configuration file is changed
+and
+.I config
+is used to regenerate the system makefile.
+.NH 2
+Building the system
+.PP
+The makefile constructed by
+.I config
+should allow a new system to be rebuilt by simply typing ``make image-name''.
+For example, if you have named your bootable system image ``kernel'',
+then ``make kernel''
+will generate a bootable image named ``kernel''. Alternate system image names
+are used when the root file system location and/or swapping configuration
+is done in more than one way. The makefile which
+.I config
+creates has entry points for each system image defined in
+the configuration file.
+Thus, if you have configured ``kernel'' to be a system with the root file
+system on an ``hp'' device and ``hkkernel'' to be a system with the root
+file system on an ``hk'' device, then ``make kernel hkkernel'' will generate
+binary images for each.
+As the system will generally use the disk from which it is loaded
+as the root filesystem, separate system images are only required
+to support different swap configurations.
+.PP
+Note that the name of a bootable image is different from the system
+identifier. All bootable images are configured for the same system;
+only the information about the root file system and paging devices differ.
+(This is described in more detail in section 4.)
+.PP
+The last step in the system building process is to rearrange certain commonly
+used symbols in the symbol table of the system image; the makefile
+generated by
+.I config
+does this automatically for you.
+This is advantageous for programs such as
+\fInetstat\fP\|(1) and \fIvmstat\fP\|(1),
+which run much faster when the symbols they need are located at
+the front of the symbol table.
+Remember also that many programs expect
+the currently executing system to be named ``/kernel''. If you install
+a new system and name it something other than ``/kernel'', many programs
+are likely to give strange results.
+.NH 2
+Sharing object modules
+.PP
+If you have many systems which are all built on a single machine
+there are at least two approaches to saving time in building system
+images. The best way is to have a single system image which is run on
+all machines. This is attractive since it minimizes disk space used
+and time required to rebuild systems after making changes. However,
+it is often the case that one or more systems will require a separately
+configured system image. This may be due to limited memory (building
+a system with many unused device drivers can be expensive), or to
+configuration requirements (one machine may be a development machine
+where disk quotas are not needed, while another is a production machine
+where they are), etc. In these cases it is possible
+for common systems to share relocatable object modules which are not
+configuration dependent; most of the modules in the directory ``/sys/sys''
+are of this sort.
+.PP
+To share object modules, a generic system should be built. Then, for
+each system configure the system as before, but before recompiling and
+linking the system, type ``make links'' in the system compilation directory.
+This will cause the system
+to be searched for source modules which are safe to share between systems
+and generate symbolic links in the current directory to the appropriate
+object modules in the directory ``../GENERIC''. A shell script,
+``makelinks'' is generated with this request and may be checked for
+correctness. The file ``/sys/conf/defines'' contains a list of symbols
+which we believe are safe to ignore when checking the source code
+for modules which may be shared. Note that this list includes the definitions
+used to conditionally compile in the virtual memory tracing facilities, and
+the trace point support used only rarely (even at Berkeley).
+It may be necessary
+to modify this file to reflect local needs. Note further that
+interdependencies which are not directly visible
+in the source code are not caught. This means that if you place
+per-system dependencies in an include file, they will not be recognized
+and the shared code may be selected in an unexpected fashion.
+.NH 2
+Building profiled systems
+.PP
+It is simple to configure a system which will automatically
+collect profiling information as it operates. The profiling data
+may be collected with \fIkgmon\fP\|(8) and processed with
+\fIgprof\fP\|(1)
+to obtain information regarding the system's operation. Profiled
+systems maintain histograms of the program counter as well as the
+number of invocations of each routine. The \fIgprof\fP
+command will also generate a dynamic call graph of the executing
+system and propagate time spent in each routine along the arcs
+of the call graph (consult the \fIgprof\fP documentation for elaboration).
+The program counter sampling can be driven by the system clock, or
+if you have an alternate real time clock, this can be used. The
+latter is highly recommended, as use of the system clock will result
+in statistical anomalies, and time spent in the clock routine will
+not be accurately attributed.
+.PP
+To configure a profiled system, the
+.B \-p
+option should be supplied to \fIconfig\fP.
+A profiled system is about 5-10% larger in its text space due to
+the calls to count the subroutine invocations. When the system
+executes, the profiling data is stored in a buffer which is 1.2
+times the size of the text space. The overhead for running a
+profiled system varies; under normal load we see anywhere from 5-25%
+of the system time spent in the profiling code.
+.PP
+Note that systems configured for profiling should not be shared as
+described above unless all the other shared systems are also to be
+profiled.
diff --git a/usr.sbin/config/SMM.doc/4.t b/usr.sbin/config/SMM.doc/4.t
new file mode 100644
index 0000000..7498185
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/4.t
@@ -0,0 +1,442 @@
+.\" 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.
+.\"
+.\" @(#)4.t 8.1 (Berkeley) 6/8/93
+.\"
+.\".ds RH "Configuration File Syntax
+.ne 2i
+.NH
+CONFIGURATION FILE SYNTAX
+.PP
+In this section we consider the specific rules used in writing
+a configuration file. A complete grammar for the input language
+can be found in Appendix A and may be of use if you should have
+problems with syntax errors.
+.PP
+A configuration file is broken up into three logical pieces:
+.IP \(bu 3
+configuration parameters global to all system images
+specified in the configuration file,
+.IP \(bu 3
+parameters specific to each
+system image to be generated, and
+.IP \(bu 3
+device specifications.
+.NH 2
+Global configuration parameters
+.PP
+The global configuration parameters are the type of machine,
+cpu types, options, timezone, system identifier, and maximum users.
+Each is specified with a separate line in the configuration file.
+.IP "\fBmachine\fP \fItype\fP"
+.br
+The system is to run on the machine type specified. No more than
+one machine type can appear in the configuration file. Legal values
+are
+.B vax
+and
+\fBsun\fP.
+.IP "\fBcpu\fP ``\fItype\fP''"
+.br
+This system is to run on the cpu type specified.
+More than one cpu type specification
+can appear in a configuration file.
+Legal types for a
+.B vax
+machine are
+\fBVAX8600\fP, \fBVAX780\fP, \fBVAX750\fP,
+\fBVAX730\fP
+and
+\fBVAX630\fP (MicroVAX II).
+The 8650 is listed as an 8600, the 785 as a 780, and a 725 as a 730.
+.IP "\fBoptions\fP \fIoptionlist\fP"
+.br
+Compile the listed optional code into the system.
+Options in this list are separated by commas.
+Possible options are listed at the top of the generic makefile.
+A line of the form ``options FUNNY,HAHA'' generates global ``#define''s
+\-DFUNNY \-DHAHA in the resultant makefile.
+An option may be given a value by following its name with ``\fB=\fP'',
+then the value enclosed in (double) quotes.
+The following are major options are currently in use:
+COMPAT (include code for compatibility with 4.1BSD binaries),
+INET (Internet communication protocols),
+NS (Xerox NS communication protocols),
+and
+QUOTA (enable disk quotas).
+Other kernel options controlling system sizes and limits
+are listed in Appendix D;
+options for the network are found in Appendix E.
+There are additional options which are associated with certain
+peripheral devices; those are listed in the Synopsis section
+of the manual page for the device.
+.IP "\fBmakeoptions\fP \fIoptionlist\fP"
+.br
+Options that are used within the system makefile
+and evaluated by
+.I make
+are listed as
+.IR makeoptions .
+Options are listed with their values with the form
+``makeoptions name=value,name2=value2.''
+The values must be enclosed in double quotes if they include numerals
+or begin with a dash.
+.IP "\fBtimezone\fP \fInumber\fP [ \fBdst\fP [ \fInumber\fP ] ]"
+.br
+Specifies the timezone used by the system. This is measured in the
+number of hours your timezone is west of GMT.
+EST is 5 hours west of GMT, PST is 8. Negative numbers
+indicate hours east of GMT. If you specify
+\fBdst\fP, the system will operate under daylight savings time.
+An optional integer or floating point number may be included
+to specify a particular daylight saving time correction algorithm;
+the default value is 1, indicating the United States.
+Other values are: 2 (Australian style), 3 (Western European),
+4 (Middle European), and 5 (Eastern European). See
+\fIgettimeofday\fP\|(2) and \fIctime\fP\|(3) for more information.
+.IP "\fBident\fP \fIname\fP"
+.br
+This system is to be known as
+.IR name .
+This is usually a cute name like ERNIE (short for Ernie Co-Vax) or
+VAXWELL (for Vaxwell Smart).
+This value is defined for use in conditional compilation,
+and is also used to locate an optional list of source files specific
+to this system.
+.IP "\fBmaxusers\fP \fInumber\fP"
+.br
+The maximum expected number of simultaneously active user on this system is
+.IR number .
+This number is used to size several system data structures.
+.NH 2
+System image parameters
+.PP
+Multiple bootable images may be specified in a single configuration
+file. The systems will have the same global configuration parameters
+and devices, but the location of the root file system and other
+system specific devices may be different. A system image is specified
+with a ``config'' line:
+.IP
+\fBconfig\fP\ \fIsysname\fP\ \fIconfig-clauses\fP
+.LP
+The
+.I sysname
+field is the name given to the loaded system image; almost everyone
+names their standard system image ``kernel''. The configuration clauses
+are one or more specifications indicating where the root file system
+is located and the number and location of paging devices.
+The device used by the system to process argument lists during
+.IR execve (2)
+calls may also be specified, though in practice this is almost
+always selected by
+.I config
+using one of its rules for selecting default locations for
+system devices.
+.PP
+A configuration clause is one of the following
+.IP
+.nf
+\fBroot\fP [ \fBon\fP ] \fIroot-device\fP
+\fBswap\fP [ \fBon\fP ] \fIswap-device\fP [ \fBand\fP \fIswap-device\fP ] ...
+\fBdumps\fP [ \fBon\fP ] \fIdump-device\fP
+\fBargs\fP [ \fBon\fP ] \fIarg-device\fP
+.LP
+(the ``on'' is optional.) Multiple configuration clauses
+are separated by white space;
+.I config
+allows specifications to be continued across multiple lines
+by beginning the continuation line with a tab character.
+The ``root'' clause specifies where the root file system
+is located, the ``swap'' clause indicates swapping and paging
+area(s), the ``dumps'' clause can be used to force system dumps
+to be taken on a particular device, and the ``args'' clause
+can be used to specify that argument list processing for
+.I execve
+should be done on a particular device.
+.PP
+The device names supplied in the clauses may be fully specified
+as a device, unit, and file system partition; or underspecified
+in which case
+.I config
+will use builtin rules to select default unit numbers and file
+system partitions. The defaulting rules are a bit complicated
+as they are dependent on the overall system configuration.
+For example, the swap area need not be specified at all if
+the root device is specified; in this case the swap area is
+placed in the ``b'' partition of the same disk where the root
+file system is located. Appendix B contains a complete list
+of the defaulting rules used in selecting system configuration
+devices.
+.PP
+The device names are translated to the
+appropriate major and minor device
+numbers on a per-machine basis. A file,
+``/sys/conf/devices.machine'' (where ``machine''
+is the machine type specified in the configuration file),
+is used to map a device name to its major block device number.
+The minor device number is calculated using the standard
+disk partitioning rules: on unit 0, partition ``a'' is minor device
+0, partition ``b'' is minor device 1, and so on; for units
+other than 0, add 8 times the unit number to get the minor
+device.
+.PP
+If the default mapping of device name to major/minor device
+number is incorrect for your configuration, it can be replaced
+by an explicit specification of the major/minor device.
+This is done by substituting
+.IP
+\fBmajor\fP \fIx\fP \fBminor\fP \fIy\fP
+.LP
+where the device name would normally be found. For example,
+.IP
+.nf
+\fBconfig\fP kernel \fBroot\fP \fBon\fP \fBmajor\fP 99 \fBminor\fP 1
+.fi
+.PP
+Normally, the areas configured for swap space are sized by the system
+at boot time. If a non-standard size is to be used for one
+or more swap areas (less than the full partition),
+this can also be specified. To do this, the
+device name specified for a swap area should have a ``size''
+specification appended. For example,
+.IP
+.nf
+\fBconfig\fP kernel \fBroot\fP \fBon\fP hp0 \fBswap\fP \fBon\fP hp0b \fBsize\fP 1200
+.fi
+.LP
+would force swapping to be done in partition ``b'' of ``hp0'' and
+the swap partition size would be set to 1200 sectors. A swap area
+sized larger than the associated disk partition is trimmed to the
+partition size.
+.PP
+To create a generic configuration, only the clause ``swap generic''
+should be specified; any extra clauses will cause an error.
+.NH 2
+Device specifications
+.PP
+Each device attached to a machine must be specified
+to
+.I config
+so that the system generated will know to probe for it during
+the autoconfiguration process carried out at boot time. Hardware
+specified in the configuration need not actually be present on
+the machine where the generated system is to be run. Only the
+hardware actually found at boot time will be used by the system.
+.PP
+The specification of hardware devices in the configuration file
+parallels the interconnection hierarchy of the machine to be
+configured. On the VAX, this means that a configuration file must
+indicate what MASSBUS and UNIBUS adapters are present, and to
+which \fInexi\fP they might be connected.*
+.FS
+* While VAX-11/750's and VAX-11/730 do not actually have
+nexi, the system treats them as having
+.I "simulated nexi"
+to simplify device configuration.
+.FE
+Similarly, devices
+and controllers must be indicated as possibly being connected
+to one or more adapters. A device description may provide a
+complete definition of the possible configuration parameters
+or it may leave certain parameters undefined and make the system
+probe for all the possible values. The latter allows a single
+device configuration list to match many possible physical
+configurations. For example, a disk may be indicated as present
+at UNIBUS adapter 0, or at any UNIBUS adapter which the system
+locates at boot time. The latter scheme, termed
+.IR wildcarding ,
+allows more flexibility in the physical configuration of a system;
+if a disk must be moved around for some reason, the system will
+still locate it at the alternate location.
+.PP
+A device specification takes one of the following forms:
+.IP
+.nf
+\fBmaster\fP \fIdevice-name\fP \fIdevice-info\fP
+\fBcontroller\fP \fIdevice-name\fP \fIdevice-info\fP [ \fIinterrupt-spec\fP ]
+\fBdevice\fP \fIdevice-name\fP \fIdevice-info\fP \fIinterrupt-spec\fP
+\fBdisk\fP \fIdevice-name\fP \fIdevice-info\fP
+\fBtape\fP \fIdevice-name\fP \fIdevice-info\fP
+.fi
+.LP
+A ``master'' is a MASSBUS tape controller; a ``controller'' is a
+disk controller, a UNIBUS tape controller, a MASSBUS adapter, or
+a UNIBUS adapter. A ``device'' is an autonomous device which
+connects directly to a UNIBUS adapter (as opposed to something
+like a disk which connects through a disk controller). ``Disk''
+and ``tape'' identify disk drives and tape drives connected to
+a ``controller'' or ``master.''
+.PP
+The
+.I device-name
+is one of the standard device names, as
+indicated in section 4 of the UNIX Programmers Manual,
+concatenated with the
+.I logical
+unit number to be assigned the device (the
+.I logical
+unit number may be different than the
+.I physical
+unit number indicated on the front of something
+like a disk; the
+.I logical
+unit number is used to refer to the UNIX device, not
+the physical unit number). For example, ``hp0'' is logical
+unit 0 of a MASSBUS storage device, even though it might
+be physical unit 3 on MASSBUS adapter 1.
+.PP
+The
+.I device-info
+clause specifies how the hardware is
+connected in the interconnection hierarchy. On the VAX,
+UNIBUS and MASSBUS adapters are connected to the internal
+system bus through
+a \fInexus\fP.
+Thus, one of the following
+specifications would be used:
+.IP
+.ta 1.5i 2.5i 4.0i
+.nf
+\fBcontroller\fP mba0 \fBat\fP \fBnexus\fP \fIx\fP
+\fBcontroller\fP uba0 \fBat\fP \fBnexus\fP \fIx\fP
+.fi
+.LP
+To tie a controller to a specific nexus, ``x'' would be supplied
+as the number of that nexus; otherwise ``x'' may be specified as
+``?'', in which
+case the system will probe all nexi present looking
+for the specified controller.
+.PP
+The remaining interconnections on the VAX are:
+.IP \(bu 3
+a controller
+may be connected to another controller (e.g. a disk controller attached
+to a UNIBUS adapter),
+.IP \(bu 3
+a master is always attached to a controller (a MASSBUS adapter),
+.IP \(bu 3
+a tape is always attached to a master (for MASSBUS
+tape drives),
+.IP \(bu 3
+a disk is always attached to a controller, and
+.IP \(bu 3
+devices
+are always attached to controllers (e.g. UNIBUS controllers attached
+to UNIBUS adapters).
+.LP
+The following lines give an example of each of these interconnections:
+.IP
+.ta 1.5i 2.5i 4.0i
+.nf
+\fBcontroller\fP hk0 \fBat\fP uba0 ...
+\fBmaster\fP ht0 \fBat\fP mba0 ...
+\fBdisk\fP hp0 \fBat\fP mba0 ...
+\fBtape\fP tu0 \fBat\fP ht0 ...
+\fBdisk\fP rk1 \fBat\fP hk0 ...
+\fBdevice\fP dz0 \fBat\fP uba0 ...
+.fi
+.LP
+Any piece of hardware which may be connected to a specific
+controller may also be wildcarded across multiple controllers.
+.PP
+The final piece of information needed by the system to configure
+devices is some indication of where or how a device will interrupt.
+For tapes and disks, simply specifying the \fIslave\fP or \fIdrive\fP
+number is sufficient to locate the control status register for the
+device.
+\fIDrive\fP numbers may be wildcarded
+on MASSBUS devices, but not on disks on a UNIBUS controller.
+For controllers, the control status register must be
+given explicitly, as well the number of interrupt vectors used and
+the names of the routines to which they should be bound.
+Thus the example lines given above might be completed as:
+.IP
+.ta 1.5i 2.5i 4.0i
+.nf
+\fBcontroller\fP hk0 \fBat\fP uba0 \fBcsr\fP 0177440 \fBvector\fP rkintr
+\fBmaster\fP ht0 \fBat\fP mba0 \fBdrive\fP 0
+\fBdisk\fP hp0 \fBat\fP mba0 \fBdrive\fP ?
+\fBtape\fP tu0 \fBat\fP ht0 \fBslave\fP 0
+\fBdisk\fP rk1 \fBat\fP hk0 \fBdrive\fP 1
+\fBdevice\fP dz0 \fBat\fP uba0 \fBcsr\fP 0160100 \fBvector\fP dzrint dzxint
+.fi
+.PP
+Certain device drivers require extra information passed to them
+at boot time to tailor their operation to the actual hardware present.
+The line printer driver, for example, needs to know how many columns
+are present on each non-standard line printer (i.e. a line printer
+with other than 80 columns). The drivers for the terminal multiplexors
+need to know which lines are attached to modem lines so that no one will
+be allowed to use them unless a connection is present. For this reason,
+one last parameter may be specified to a
+.IR device ,
+a
+.I flags
+field. It has the syntax
+.IP
+\fBflags\fP \fInumber\fP
+.LP
+and is usually placed after the
+.I csr
+specification. The
+.I number
+is passed directly to the associated driver. The manual pages
+in section 4 should be consulted to determine how each driver
+uses this value (if at all).
+Communications interface drivers commonly use the flags
+to indicate whether modem control signals are in use.
+.PP
+The exact syntax for each specific device is given in the Synopsis
+section of its manual page in section 4 of the manual.
+.NH 2
+Pseudo-devices
+.PP
+A number of drivers and software subsystems
+are treated like device drivers without any associated hardware.
+To include any of these pieces, a ``pseudo-device'' specification
+must be used. A specification for a pseudo device takes the form
+.IP
+.DT
+.nf
+\fBpseudo-device\fP \fIdevice-name\fP [ \fIhowmany\fP ]
+.fi
+.PP
+Examples of pseudo devices are
+\fBpty\fP, the pseudo terminal driver (where the optional
+.I howmany
+value indicates the number of pseudo terminals to configure, 32 default),
+and \fBloop\fP, the software loopback network pseudo-interface.
+Other pseudo devices for the network include
+\fBimp\fP (required when a CSS or ACC imp is configured)
+and \fBether\fP (used by the Address Resolution Protocol
+on 10 Mb/sec Ethernets).
+More information on configuring each of these can also be found
+in section 4 of the manual.
diff --git a/usr.sbin/config/SMM.doc/5.t b/usr.sbin/config/SMM.doc/5.t
new file mode 100644
index 0000000..81f2a52
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/5.t
@@ -0,0 +1,271 @@
+.\" 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.
+.\"
+.\" @(#)5.t 8.1 (Berkeley) 6/8/93
+.\"
+.\".ds RH "Sample Configuration Files
+.ne 2i
+.NH
+SAMPLE CONFIGURATION FILES
+.PP
+In this section we will consider how to configure a
+sample VAX-11/780 system on which the hardware can be
+reconfigured to guard against various hardware mishaps.
+We then study the rules needed to configure a VAX-11/750
+to run in a networking environment.
+.NH 2
+VAX-11/780 System
+.PP
+Our VAX-11/780 is configured with hardware
+recommended in the document ``Hints on Configuring a VAX for 4.2BSD''
+(this is one of the high-end configurations).
+Table 1 lists the pertinent hardware to be configured.
+.DS B
+.TS
+box;
+l | l | l | l | l
+l | l | l | l | l.
+Item Vendor Connection Name Reference
+_
+cpu DEC VAX780
+MASSBUS controller Emulex nexus ? mba0 hp(4)
+disk Fujitsu mba0 hp0
+disk Fujitsu mba0 hp1
+MASSBUS controller Emulex nexus ? mba1
+disk Fujitsu mba1 hp2
+disk Fujitsu mba1 hp3
+UNIBUS adapter DEC nexus ?
+tape controller Emulex uba0 tm0 tm(4)
+tape drive Kennedy tm0 te0
+tape drive Kennedy tm0 te1
+terminal multiplexor Emulex uba0 dh0 dh(4)
+terminal multiplexor Emulex uba0 dh1
+terminal multiplexor Emulex uba0 dh2
+.TE
+.DE
+.ce
+Table 1. VAX-11/780 Hardware support.
+.LP
+We will call this machine ANSEL and construct a configuration
+file one step at a time.
+.PP
+The first step is to fill in the global configuration parameters.
+The machine is a VAX, so the
+.I "machine type"
+is ``vax''. We will assume this system will
+run only on this one processor, so the
+.I "cpu type"
+is ``VAX780''. The options are empty since this is going to
+be a ``vanilla'' VAX. The system identifier, as mentioned before,
+is ``ANSEL,'' and the maximum number of users we plan to support is
+about 40. Thus the beginning of the configuration file looks like
+this:
+.DS
+.ta 1.5i 2.5i 4.0i
+#
+# ANSEL VAX (a picture perfect machine)
+#
+machine vax
+cpu VAX780
+timezone 8 dst
+ident ANSEL
+maxusers 40
+.DE
+.PP
+To this we must then add the specifications for three
+system images. The first will be our standard system with the
+root on ``hp0'' and swapping on the same drive as the root.
+The second will have the root file system in the same location,
+but swap space interleaved among drives on each controller.
+Finally, the third will be a generic system,
+to allow us to boot off any of the four disk drives.
+.DS
+.ta 1.5i 2.5i
+config kernel root on hp0
+config hpkernel root on hp0 swap on hp0 and hp2
+config genkernel swap generic
+.DE
+.PP
+Finally, the hardware must be specified. Let us first just try
+transcribing the information from Table 1.
+.DS
+.ta 1.5i 2.5i 4.0i
+controller mba0 at nexus ?
+disk hp0 at mba0 disk 0
+disk hp1 at mba0 disk 1
+controller mba1 at nexus ?
+disk hp2 at mba1 disk 2
+disk hp3 at mba1 disk 3
+controller uba0 at nexus ?
+controller tm0 at uba0 csr 0172520 vector tmintr
+tape te0 at tm0 drive 0
+tape te1 at tm0 drive 1
+device dh0 at uba0 csr 0160020 vector dhrint dhxint
+device dm0 at uba0 csr 0170500 vector dmintr
+device dh1 at uba0 csr 0160040 vector dhrint dhxint
+device dh2 at uba0 csr 0160060 vector dhrint dhxint
+.DE
+.LP
+(Oh, I forgot to mention one panel of the terminal multiplexor
+has modem control, thus the ``dm0'' device.)
+.PP
+This will suffice, but leaves us with little flexibility. Suppose
+our first disk controller were to break. We would like to recable the
+drives normally on the second controller so that all our disks could
+still be used without reconfiguring the system. To do this we wildcard
+the MASSBUS adapter connections and also the slave numbers. Further,
+we wildcard the UNIBUS adapter connections in case we decide some time
+in the future to purchase another adapter to offload the single UNIBUS
+we currently have. The revised device specifications would then be:
+.DS
+.ta 1.5i 2.5i 4.0i
+controller mba0 at nexus ?
+disk hp0 at mba? disk ?
+disk hp1 at mba? disk ?
+controller mba1 at nexus ?
+disk hp2 at mba? disk ?
+disk hp3 at mba? disk ?
+controller uba0 at nexus ?
+controller tm0 at uba? csr 0172520 vector tmintr
+tape te0 at tm0 drive 0
+tape te1 at tm0 drive 1
+device dh0 at uba? csr 0160020 vector dhrint dhxint
+device dm0 at uba? csr 0170500 vector dmintr
+device dh1 at uba? csr 0160040 vector dhrint dhxint
+device dh2 at uba? csr 0160060 vector dhrint dhxint
+.DE
+.LP
+The completed configuration file for ANSEL is shown in Appendix C.
+.NH 2
+VAX-11/750 with network support
+.PP
+Our VAX-11/750 system will be located on two 10Mb/s Ethernet
+local area networks and also the DARPA Internet. The system
+will have a MASSBUS drive for the root file system and two
+UNIBUS drives. Paging is interleaved among all three drives.
+We have sold our standard DEC terminal multiplexors since this
+machine will be accessed solely through the network. This
+machine is not intended to have a large user community, it
+does not have a great deal of memory. First the global parameters:
+.DS
+.ta 1.5i 2.5i 4.0i
+#
+# UCBVAX (Gateway to the world)
+#
+machine vax
+cpu "VAX780"
+cpu "VAX750"
+ident UCBVAX
+timezone 8 dst
+maxusers 32
+options INET
+options NS
+.DE
+.PP
+The multiple cpu types allow us to replace UCBVAX with a
+more powerful cpu without reconfiguring the system. The
+value of 32 given for the maximum number of users is done to
+force the system data structures to be over-allocated. That
+is desirable on this machine because, while it is not expected
+to support many users, it is expected to perform a great deal
+of work.
+The ``INET'' indicates that we plan to use the
+DARPA standard Internet protocols on this machine,
+and ``NS'' also includes support for Xerox NS protocols.
+Note that unlike 4.2BSD configuration files,
+the network protocol options do not require corresponding pseudo devices.
+.PP
+The system images and disks are configured next.
+.DS
+.ta 1.5i 2.5i 4.0i
+config kernel root on hp swap on hp and rk0 and rk1
+config upkernel root on up
+config hkkernel root on hk swap on rk0 and rk1
+
+controller mba0 at nexus ?
+controller uba0 at nexus ?
+disk hp0 at mba? drive 0
+disk hp1 at mba? drive 1
+controller sc0 at uba? csr 0176700 vector upintr
+disk up0 at sc0 drive 0
+disk up1 at sc0 drive 1
+controller hk0 at uba? csr 0177440 vector rkintr
+disk rk0 at hk0 drive 0
+disk rk1 at hk0 drive 1
+.DE
+.PP
+UCBVAX requires heavy interleaving of its paging area to keep up
+with all the mail traffic it handles. The limiting factor on this
+system's performance is usually the number of disk arms, as opposed
+to memory or cpu cycles. The extra UNIBUS controller, ``sc0'',
+is in case the MASSBUS controller breaks and a spare controller
+must be installed (most of our old UNIBUS controllers have been
+replaced with the newer MASSBUS controllers, so we have a number
+of these around as spares).
+.PP
+Finally, we add in the network devices.
+Pseudo terminals are needed to allow users to
+log in across the network (remember the only hardwired terminal
+is the console).
+The software loopback device is used for on-machine communications.
+The connection to the Internet is through
+an IMP, this requires yet another
+.I pseudo-device
+(in addition to the actual hardware device used by the
+IMP software). And, finally, there are the two Ethernet devices.
+These use a special protocol, the Address Resolution Protocol (ARP),
+to map between Internet and Ethernet addresses. Thus, yet another
+.I pseudo-device
+is needed. The additional device specifications are show below.
+.DS
+.ta 1.5i 2.5i 4.0i
+pseudo-device pty
+pseudo-device loop
+pseudo-device imp
+device acc0 at uba? csr 0167600 vector accrint accxint
+pseudo-device ether
+device ec0 at uba? csr 0164330 vector ecrint eccollide ecxint
+device il0 at uba? csr 0164000 vector ilrint ilcint
+.DE
+.LP
+The completed configuration file for UCBVAX is shown in Appendix C.
+.NH 2
+Miscellaneous comments
+.PP
+It should be noted in these examples that neither system was
+configured to use disk quotas or the 4.1BSD compatibility mode.
+To use these optional facilities, and others, we would probably
+clean out our current configuration, reconfigure the system, then
+recompile and relink the system image(s). This could, of course,
+be avoided by figuring out which relocatable object files are
+affected by the reconfiguration, then reconfiguring and recompiling
+only those files affected by the configuration change. This technique
+should be used carefully.
diff --git a/usr.sbin/config/SMM.doc/6.t b/usr.sbin/config/SMM.doc/6.t
new file mode 100644
index 0000000..f02baed
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/6.t
@@ -0,0 +1,239 @@
+.\" 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.
+.\"
+.\" @(#)6.t 8.1 (Berkeley) 6/8/93
+.\"
+.\".ds RH "Adding New Devices
+.ne 2i
+.NH
+ADDING NEW SYSTEM SOFTWARE
+.PP
+This section is not for the novice, it describes
+some of the inner workings of the configuration process as
+well as the pertinent parts of the system autoconfiguration process.
+It is intended to give
+those people who intend to install new device drivers and/or
+other system facilities sufficient information to do so in the
+manner which will allow others to easily share the changes.
+.PP
+This section is broken into four parts:
+.IP \(bu 3
+general guidelines to be followed in modifying system code,
+.IP \(bu 3
+how to add non-standard system facilities to 4.4BSD,
+.IP \(bu 3
+how to add a device driver to 4.4BSD, and
+.NH 2
+Modifying system code
+.PP
+If you wish to make site-specific modifications to the system
+it is best to bracket them with
+.DS
+#ifdef SITENAME
+\&...
+#endif
+.DE
+to allow your source to be easily distributed to others, and
+also to simplify \fIdiff\fP\|(1) listings. If you choose not
+to use a source code control system (e.g. SCCS, RCS), and
+perhaps even if you do, it is
+recommended that you save the old code with something
+of the form:
+.DS
+#ifndef SITENAME
+\&...
+#endif
+.DE
+We try to isolate our site-dependent code in individual files
+which may be configured with pseudo-device specifications.
+.PP
+Indicate machine-specific code with ``#ifdef vax'' (or other machine,
+as appropriate).
+4.4BSD underwent extensive work to make it extremely portable to
+machines with similar architectures\- you may someday find
+yourself trying to use a single copy of the source code on
+multiple machines.
+.NH 2
+Adding non-standard system facilities
+.PP
+This section considers the work needed to augment
+.IR config 's
+data base files for non-standard system facilities.
+.I Config
+uses a set of files that list the source modules that may be required
+when building a system.
+The data bases are taken from the directory in which
+.I config
+is run, normally /sys/conf.
+Three such files may be used:
+.IR files ,
+.IR files .machine,
+and
+.IR files .ident.
+The first is common to all systems,
+the second contains files unique to a single machine type,
+and the third is an optional list of modules for use on a specific machine.
+This last file may override specifications in the first two.
+The format of the
+.I files
+file has grown somewhat complex over time. Entries are normally of
+the form
+.IP
+.nf
+.DT
+\fIdir/source.c\fP \fItype\fP \fIoption-list\fP \fImodifiers\fP
+.LP
+for example,
+.IP
+.nf
+.DT
+\fIvaxuba/foo.c\fP \fBoptional\fP foo \fBdevice-driver\fP
+.LP
+The
+.I type
+is one of
+.B standard
+or
+.BR optional .
+Files marked as standard are included in all system configurations.
+Optional file specifications include a list of one or more system
+options that together require the inclusion of this module.
+The options in the list may be either names of devices that may
+be in the configuration file,
+or the names of system options that may be defined.
+An optional file may be listed multiple times with different options;
+if all of the options for any of the entries are satisfied,
+the module is included.
+.PP
+If a file is specified as a
+.IR device-driver ,
+any special compilation options for device drivers will be invoked.
+On the VAX this results in the use of the
+.B \-i
+option for the C optimizer. This is required when pointer references
+are made to memory locations in the VAX I/O address space.
+.PP
+Two other optional keywords modify the usage of the file.
+.I Config
+understands that certain files are used especially for
+kernel profiling. These files are indicated in the
+.I files
+files with a
+.I profiling-routine
+keyword. For example, the current profiling subroutines
+are sequestered off in a separate file with the following
+entry:
+.IP
+.nf
+.DT
+\fIsys/subr_mcount.c\fP \fBoptional\fP \fBprofiling-routine\fP
+.fi
+.LP
+The
+.I profiling-routine
+keyword forces
+.I config
+not to compile the source file with the
+.B \-pg
+option.
+.PP
+The second keyword which can be of use is the
+.I config-dependent
+keyword. This causes
+.I config
+to compile the indicated module with the global configuration
+parameters. This allows certain modules, such as
+.I machdep.c
+to size system data structures based on the maximum number
+of users configured for the system.
+.NH 2
+Adding device drivers to 4.4BSD
+.PP
+The I/O system and
+.I config
+have been designed to easily allow new device support to be added.
+The system source directories are organized as follows:
+.DS
+.TS
+lw(1.0i) l.
+/sys/h machine independent include files
+/sys/sys machine-independent system source files
+/sys/conf site configuration files and basic templates
+/sys/net network-protocol-independent, but network-related code
+/sys/netinet DARPA Internet code
+/sys/netimp IMP support code
+/sys/netns Xerox NS code
+/sys/vax VAX-specific mainline code
+/sys/vaxif VAX network interface code
+/sys/vaxmba VAX MASSBUS device drivers and related code
+/sys/vaxuba VAX UNIBUS device drivers and related code
+.TE
+.DE
+.PP
+Existing block and character device drivers for the VAX
+reside in ``/sys/vax'', ``/sys/vaxmba'', and ``/sys/vaxuba''. Network
+interface drivers reside in ``/sys/vaxif''. Any new device
+drivers should be placed in the appropriate source code directory
+and named so as not to conflict with existing devices.
+Normally, definitions for things like device registers are placed in
+a separate file in the same directory. For example, the ``dh''
+device driver is named ``dh.c'' and its associated include file is
+named ``dhreg.h''.
+.PP
+Once the source for the device driver has been placed in a directory,
+the file ``/sys/conf/files.machine'', and possibly
+``/sys/conf/devices.machine'' should be modified. The
+.I files
+files in the conf directory contain a line for each C source or binary-only
+file in the system. Those files which are machine independent are
+located in ``/sys/conf/files,'' while machine specific files
+are in ``/sys/conf/files.machine.'' The ``devices.machine'' file
+is used to map device names to major block device numbers. If the device
+driver being added provides support for a new disk
+you will want to modify this file (the format is obvious).
+.PP
+In addition to including the driver in the
+.I files
+file, it must also be added to the device configuration tables. These
+are located in ``/sys/vax/conf.c'', or similar for machines other than
+the VAX. If you don't understand what to add to this file, you should
+study an entry for an existing driver.
+Remember that the position in the
+device table specifies the major device number.
+The block major number is needed in the ``devices.machine'' file
+if the device is a disk.
+.PP
+With the configuration information in place, your configuration
+file appropriately modified, and a system reconfigured and rebooted
+you should incorporate the shell commands needed to install the special
+files in the file system to the file ``/dev/MAKEDEV'' or
+``/dev/MAKEDEV.local''. This is discussed in the document ``Installing
+and Operating 4.4BSD''.
diff --git a/usr.sbin/config/SMM.doc/a.t b/usr.sbin/config/SMM.doc/a.t
new file mode 100644
index 0000000..dfcb954
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/a.t
@@ -0,0 +1,162 @@
+.\" 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.
+.\"
+.\" @(#)a.t 8.1 (Berkeley) 6/8/93
+.\"
+.\".ds RH "Configuration File Grammar
+.bp
+.LG
+.B
+.ce
+APPENDIX A. CONFIGURATION FILE GRAMMAR
+.sp
+.R
+.NL
+.PP
+The following grammar is a compressed form of the actual
+\fIyacc\fP\|(1) grammar used by
+.I config
+to parse configuration files.
+Terminal symbols are shown all in upper case, literals
+are emboldened; optional clauses are enclosed in brackets, ``[''
+and ``]''; zero or more instantiations are denoted with ``*''.
+.sp
+.nf
+.DT
+Configuration ::= [ Spec \fB;\fP ]*
+
+Spec ::= Config_spec
+ | Device_spec
+ | \fBtrace\fP
+ | /* lambda */
+
+/* configuration specifications */
+
+Config_spec ::= \fBmachine\fP ID
+ | \fBcpu\fP ID
+ | \fBoptions\fP Opt_list
+ | \fBident\fP ID
+ | System_spec
+ | \fBtimezone\fP [ \fB\-\fP ] NUMBER [ \fBdst\fP [ NUMBER ] ]
+ | \fBtimezone\fP [ \fB\-\fP ] FPNUMBER [ \fBdst\fP [ NUMBER ] ]
+ | \fBmaxusers\fP NUMBER
+
+/* system configuration specifications */
+
+System_spec ::= \fBconfig\fP ID System_parameter [ System_parameter ]*
+
+System_parameter ::= swap_spec | root_spec | dump_spec | arg_spec
+
+swap_spec ::= \fBswap\fP [ \fBon\fP ] swap_dev [ \fBand\fP swap_dev ]*
+
+swap_dev ::= dev_spec [ \fBsize\fP NUMBER ]
+
+root_spec ::= \fBroot\fP [ \fBon\fP ] dev_spec
+
+dump_spec ::= \fBdumps\fP [ \fBon\fP ] dev_spec
+
+arg_spec ::= \fBargs\fP [ \fBon\fP ] dev_spec
+
+dev_spec ::= dev_name | major_minor
+
+major_minor ::= \fBmajor\fP NUMBER \fBminor\fP NUMBER
+
+dev_name ::= ID [ NUMBER [ ID ] ]
+
+/* option specifications */
+
+Opt_list ::= Option [ \fB,\fP Option ]*
+
+Option ::= ID [ \fB=\fP Opt_value ]
+
+Opt_value ::= ID | NUMBER
+
+Mkopt_list ::= Mkoption [ \fB,\fP Mkoption ]*
+
+Mkoption ::= ID \fB=\fP Opt_value
+
+/* device specifications */
+
+Device_spec ::= \fBdevice\fP Dev_name Dev_info Int_spec
+ | \fBmaster\fP Dev_name Dev_info
+ | \fBdisk\fP Dev_name Dev_info
+ | \fBtape\fP Dev_name Dev_info
+ | \fBcontroller\fP Dev_name Dev_info [ Int_spec ]
+ | \fBpseudo-device\fP Dev [ NUMBER ]
+
+Dev_name ::= Dev NUMBER
+
+Dev ::= \fBuba\fP | \fBmba\fP | ID
+
+Dev_info ::= Con_info [ Info ]*
+
+Con_info ::= \fBat\fP Dev NUMBER
+ | \fBat\fP \fBnexus\fP NUMBER
+
+Info ::= \fBcsr\fP NUMBER
+ | \fBdrive\fP NUMBER
+ | \fBslave\fP NUMBER
+ | \fBflags\fP NUMBER
+
+Int_spec ::= \fBvector\fP ID [ ID ]*
+ | \fBpriority\fP NUMBER
+.fi
+.sp
+.SH
+Lexical Conventions
+.LP
+The terminal symbols are loosely defined as:
+.IP ID
+.br
+One or more alphabetics, either upper or lower case, and underscore,
+``_''.
+.IP NUMBER
+.br
+Approximately the C language specification for an integer number.
+That is, a leading ``0x'' indicates a hexadecimal value,
+a leading ``0'' indicates an octal value, otherwise the number is
+expected to be a decimal value. Hexadecimal numbers may use either
+upper or lower case alphabetics.
+.IP FPNUMBER
+.br
+A floating point number without exponent. That is a number of the
+form ``nnn.ddd'', where the fractional component is optional.
+.LP
+In special instances a question mark, ``?'', can be substituted for
+a ``NUMBER'' token. This is used to effect wildcarding in device
+interconnection specifications.
+.LP
+Comments in configuration files are indicated by a ``#'' character
+at the beginning of the line; the remainder of the line is discarded.
+.LP
+A specification
+is interpreted as a continuation of the previous line
+if the first character of the line is tab.
diff --git a/usr.sbin/config/SMM.doc/b.t b/usr.sbin/config/SMM.doc/b.t
new file mode 100644
index 0000000..901a009
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/b.t
@@ -0,0 +1,137 @@
+.\" 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.
+.\"
+.\" @(#)b.t 8.1 (Berkeley) 6/8/93
+.\"
+.\".ds RH "Device Defaulting Rules
+.bp
+.LG
+.B
+.ce
+APPENDIX B. RULES FOR DEFAULTING SYSTEM DEVICES
+.sp
+.R
+.NL
+.PP
+When \fIconfig\fP processes a ``config'' rule which does
+not fully specify the location of the root file system,
+paging area(s), device for system dumps, and device for
+argument list processing it applies a set of rules to
+define those values left unspecified. The following list
+of rules are used in defaulting system devices.
+.IP 1) 3
+If a root device is not specified, the swap
+specification must indicate a ``generic'' system is to be built.
+.IP 2) 3
+If the root device does not specify a unit number, it
+defaults to unit 0.
+.IP 3) 3
+If the root device does not include a partition specification,
+it defaults to the ``a'' partition.
+.IP 4) 3
+If no swap area is specified, it defaults to the ``b''
+partition of the root device.
+.IP 5) 3
+If no device is specified for processing argument lists, the
+first swap partition is selected.
+.IP 6) 3
+If no device is chosen for system dumps, the first swap
+partition is selected (see below to find out where dumps are
+placed within the partition).
+.PP
+The following table summarizes the default partitions selected
+when a device specification is incomplete, e.g. ``hp0''.
+.DS
+.TS
+l l.
+Type Partition
+_
+root ``a''
+swap ``b''
+args ``b''
+dumps ``b''
+.TE
+.DE
+.SH
+Multiple swap/paging areas
+.PP
+When multiple swap partitions are specified, the system treats the
+first specified as a ``primary'' swap area which is always used.
+The remaining partitions are then interleaved into the paging
+system at the time a
+.IR swapon (2)
+system call is made. This is normally done at boot time with
+a call to
+.IR swapon (8)
+from the /etc/rc file.
+.SH
+System dumps
+.PP
+System dumps are automatically taken after a system crash,
+provided the device driver for the ``dumps'' device supports
+this. The dump contains the contents of memory, but not
+the swap areas. Normally the dump device is a disk in
+which case the information is copied to a location at the
+back of the partition. The dump is placed in the back of the
+partition because the primary swap and dump device are commonly
+the same device and this allows the system to be rebooted without
+immediately overwriting the saved information. When a dump has
+occurred, the system variable \fIdumpsize\fP
+is set to a non-zero value indicating the size (in bytes) of
+the dump. The \fIsavecore\fP\|(8)
+program then copies the information from the dump partition to
+a file in a ``crash'' directory and also makes a copy of the
+system which was running at the time of the crash (usually
+``/kernel''). The offset to the system dump is defined in the
+system variable \fIdumplo\fP (a sector offset from
+the front of the dump partition). The
+.I savecore
+program operates by reading the contents of \fIdumplo\fP, \fIdumpdev\fP,
+and \fIdumpmagic\fP from /dev/kmem, then comparing the value
+of \fIdumpmagic\fP read from /dev/kmem to that located in
+corresponding location in the dump area of the dump partition.
+If a match is found,
+.I savecore
+assumes a crash occurred and reads \fIdumpsize\fP from the dump area
+of the dump partition. This value is then used in copying the
+system dump. Refer to
+\fIsavecore\fP\|(8)
+for more information about its operation.
+.PP
+The value \fIdumplo\fP is calculated to be
+.DS
+\fIdumpdev-size\fP \- \fImemsize\fP
+.DE
+where \fIdumpdev-size\fP is the size of the disk partition
+where system dumps are to be placed, and
+\fImemsize\fP is the size of physical memory.
+If the disk partition is not large enough to hold a full
+dump, \fIdumplo\fP is set to 0 (the start of the partition).
diff --git a/usr.sbin/config/SMM.doc/c.t b/usr.sbin/config/SMM.doc/c.t
new file mode 100644
index 0000000..67b63ec
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/c.t
@@ -0,0 +1,109 @@
+.\" 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.
+.\"
+.\" @(#)c.t 8.1 (Berkeley) 6/8/93
+.\"
+.\".ds RH "Sample Config Files
+.bp
+.LG
+.B
+.ce
+APPENDIX C. SAMPLE CONFIGURATION FILES
+.sp
+.R
+.NL
+.PP
+The following configuration files are developed in section 5;
+they are included here for completeness.
+.sp 2
+.nf
+.ta 1.5i 2.5i 4.0i
+#
+# ANSEL VAX (a picture perfect machine)
+#
+machine vax
+cpu VAX780
+timezone 8 dst
+ident ANSEL
+maxusers 40
+
+config kernel root on hp0
+config hpkernel root on hp0 swap on hp0 and hp2
+config genkernel swap generic
+
+controller mba0 at nexus ?
+disk hp0 at mba? disk ?
+disk hp1 at mba? disk ?
+controller mba1 at nexus ?
+disk hp2 at mba? disk ?
+disk hp3 at mba? disk ?
+controller uba0 at nexus ?
+controller tm0 at uba? csr 0172520 vector tmintr
+tape te0 at tm0 drive 0
+tape te1 at tm0 drive 1
+device dh0 at uba? csr 0160020 vector dhrint dhxint
+device dm0 at uba? csr 0170500 vector dmintr
+device dh1 at uba? csr 0160040 vector dhrint dhxint
+device dh2 at uba? csr 0160060 vector dhrint dhxint
+.bp
+#
+# UCBVAX - Gateway to the world
+#
+machine vax
+cpu "VAX780"
+cpu "VAX750"
+ident UCBVAX
+timezone 8 dst
+maxusers 32
+options INET
+options NS
+
+config kernel root on hp swap on hp and rk0 and rk1
+config upkernel root on up
+config hkkernel root on hk swap on rk0 and rk1
+
+controller mba0 at nexus ?
+controller uba0 at nexus ?
+disk hp0 at mba? drive 0
+disk hp1 at mba? drive 1
+controller sc0 at uba? csr 0176700 vector upintr
+disk up0 at sc0 drive 0
+disk up1 at sc0 drive 1
+controller hk0 at uba? csr 0177440 vector rkintr
+disk rk0 at hk0 drive 0
+disk rk1 at hk0 drive 1
+pseudo-device pty
+pseudo-device loop
+pseudo-device imp
+device acc0 at uba? csr 0167600 vector accrint accxint
+pseudo-device ether
+device ec0 at uba? csr 0164330 vector ecrint eccollide ecxint
+device il0 at uba? csr 0164000 vector ilrint ilcint
diff --git a/usr.sbin/config/SMM.doc/d.t b/usr.sbin/config/SMM.doc/d.t
new file mode 100644
index 0000000..db9ab80
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/d.t
@@ -0,0 +1,272 @@
+.\" 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.
+.\"
+.\" @(#)d.t 8.1 (Berkeley) 6/8/93
+.\"
+.\".ds RH "Data Structure Sizing Rules
+.bp
+.LG
+.B
+.ce
+APPENDIX D. VAX KERNEL DATA STRUCTURE SIZING RULES
+.sp
+.R
+.NL
+.PP
+Certain system data structures are sized at compile time
+according to the maximum number of simultaneous users expected,
+while others are calculated at boot time based on the
+physical resources present, e.g. memory. This appendix lists
+both sets of rules and also includes some hints on changing
+built-in limitations on certain data structures.
+.SH
+Compile time rules
+.PP
+The file \fI/sys/conf\|/param.c\fP contains the definitions of
+almost all data structures sized at compile time. This file
+is copied into the directory of each configured system to allow
+configuration-dependent rules and values to be maintained.
+(Each copy normally depends on the copy in /sys/conf,
+and global modifications cause the file to be recopied unless
+the makefile is modified.)
+The rules implied by its contents are summarized below (here
+MAXUSERS refers to the value defined in the configuration file
+in the ``maxusers'' rule).
+Most limits are computed at compile time and stored in global variables
+for use by other modules; they may generally be patched in the system
+binary image before rebooting to test new values.
+.IP \fBnproc\fP
+.br
+The maximum number of processes which may be running at any time.
+It is referred to in other calculations as NPROC and is defined to be
+.DS
+20 + 8 * MAXUSERS
+.DE
+.IP \fBntext\fP
+.br
+The maximum number of active shared text segments.
+The constant is intended to allow for network servers and common commands
+that remain in the table.
+It is defined as
+.DS
+36 + MAXUSERS.
+.DE
+.IP \fBninode\fP
+.br
+The maximum number of files in the file system which may be
+active at any time. This includes files in use by users, as
+well as directory files being read or written by the system
+and files associated with bound sockets in the UNIX IPC domain.
+It is defined as
+.DS
+(NPROC + 16 + MAXUSERS) + 32
+.DE
+.IP \fBnfile\fP
+.br
+The number of ``file table'' structures. One file
+table structure is used for each open, unshared, file descriptor.
+Multiple file descriptors may reference a single file table
+entry when they are created through a \fIdup\fP call, or as the
+result of a \fIfork\fP. This is defined to be
+.DS
+16 * (NPROC + 16 + MAXUSERS) / 10 + 32
+.DE
+.IP \fBncallout\fP
+.br
+The number of ``callout'' structures. One callout
+structure is used per internal system event handled with
+a timeout. Timeouts are used for terminal delays,
+watchdog routines in device drivers, protocol timeout processing, etc.
+This is defined as
+.DS
+16 + NPROC
+.DE
+.IP \fBnclist\fP
+.br
+The number of ``c-list'' structures. C-list structures are
+used in terminal I/O, and currently each holds 60 characters.
+Their number is defined as
+.DS
+60 + 12 * MAXUSERS
+.DE
+.IP \fBnmbclusters\fP
+.br
+The maximum number of pages which may be allocated by the network.
+This is defined as 256 (a quarter megabyte of memory) in /sys/h/mbuf.h.
+In practice, the network rarely uses this much memory. It starts off
+by allocating 8 kilobytes of memory, then requesting more as
+required. This value represents an upper bound.
+.IP \fBnquota\fP
+.br
+The number of ``quota'' structures allocated. Quota structures
+are present only when disc quotas are configured in the system. One
+quota structure is kept per user. This is defined to be
+.DS
+(MAXUSERS * 9) / 7 + 3
+.DE
+.IP \fBndquot\fP
+.br
+The number of ``dquot'' structures allocated. Dquot structures
+are present only when disc quotas are configured in the system.
+One dquot structure is required per user, per active file system quota.
+That is, when a user manipulates a file on a file system on which
+quotas are enabled, the information regarding the user's quotas on
+that file system must be in-core. This information is cached, so
+that not all information must be present in-core all the time.
+This is defined as
+.DS
+NINODE + (MAXUSERS * NMOUNT) / 4
+.DE
+where NMOUNT is the maximum number of mountable file systems.
+.LP
+In addition to the above values, the system page tables (used to
+map virtual memory in the kernel's address space) are sized at
+compile time by the SYSPTSIZE definition in the file /sys/vax/vmparam.h.
+This is defined to be
+.DS
+20 + MAXUSERS
+.DE
+pages of page tables.
+Its definition affects
+the size of many data structures allocated at boot time because
+it constrains the amount of virtual memory which may be addressed
+by the running system. This is often the limiting factor
+in the size of the buffer cache, in which case a message is printed
+when the system configures at boot time.
+.SH
+Run-time calculations
+.PP
+The most important data structures sized at run-time are those used in
+the buffer cache. Allocation is done by allocating physical memory
+(and system virtual memory) immediately after the system
+has been started up; look in the file /sys/vax/machdep.c.
+The amount of physical memory which may be allocated to the buffer
+cache is constrained by the size of the system page tables, among
+other things. While the system may calculate
+a large amount of memory to be allocated to the buffer cache,
+if the system page
+table is too small to map this physical
+memory into the virtual address space
+of the system, only as much as can be mapped will be used.
+.PP
+The buffer cache is comprised of a number of ``buffer headers''
+and a pool of pages attached to these headers. Buffer headers
+are divided into two categories: those used for swapping and
+paging, and those used for normal file I/O. The system tries
+to allocate 10% of the first two megabytes and 5% of the remaining
+available physical memory for the buffer
+cache (where \fIavailable\fP does not count that space occupied by
+the system's text and data segments). If this results in fewer
+than 16 pages of memory allocated, then 16 pages are allocated.
+This value is kept in the initialized variable \fIbufpages\fP
+so that it may be patched in the binary image (to allow tuning
+without recompiling the system),
+or the default may be overridden with a configuration-file option.
+For example, the option \fBoptions BUFPAGES="3200"\fP
+causes 3200 pages (3.2M bytes) to be used by the buffer cache.
+A sufficient number of file I/O buffer headers are then allocated
+to allow each to hold 2 pages each.
+Each buffer maps 8K bytes.
+If the number of buffer pages is larger than can be mapped
+by the buffer headers, the number of pages is reduced.
+The number of buffer headers allocated
+is stored in the global variable \fInbuf\fP,
+which may be patched before the system is booted.
+The system option \fBoptions NBUF="1000"\fP forces the allocation
+of 1000 buffer headers.
+Half as many swap I/O buffer headers as file I/O buffers
+are allocated,
+but no more than 256.
+.SH
+System size limitations
+.PP
+As distributed, the sum of the virtual sizes of the core-resident
+processes is limited to 256M bytes. The size of the text
+segment of a single process is currently limited to 6M bytes.
+It may be increased to no greater than the data segment size limit
+(see below) by redefining MAXTSIZ.
+This may be done with a configuration file option,
+e.g. \fBoptions MAXTSIZ="(10*1024*1024)"\fP
+to set the limit to 10 million bytes.
+Other per-process limits discussed here may be changed with similar options
+with names given in parentheses.
+Soft, user-changeable limits are set to 512K bytes for stack (DFLSSIZ)
+and 6M bytes for the data segment (DFLDSIZ) by default;
+these may be increased up to the hard limit
+with the \fIsetrlimit\fP\|(2) system call.
+The data and stack segment size hard limits are set by a system configuration
+option to one of 17M, 33M or 64M bytes.
+One of these sizes is chosen based on the definition of MAXDSIZ;
+with no option, the limit is 17M bytes; with an option
+\fBoptions MAXDSIZ="(32*1024*1024)"\fP (or any value between 17M and 33M),
+the limit is increased to 33M bytes, and values larger than 33M
+result in a limit of 64M bytes.
+You must be careful in doing this that you have adequate paging space.
+As normally configured , the system has 16M or 32M bytes per paging area,
+depending on disk size.
+The best way to get more space is to provide multiple, thereby
+interleaved, paging areas.
+Increasing the virtual memory limits results in interleaving of
+swap space in larger sections (from 500K bytes to 1M or 2M bytes).
+.PP
+By default, the virtual memory system allocates enough memory
+for system page tables mapping user page tables
+to allow 256 megabytes of simultaneous active virtual memory.
+That is, the sum of the virtual memory sizes of all (completely- or partially-)
+resident processes can not exceed this limit.
+If the limit is exceeded, some process(es) must be swapped out.
+To increase the amount of resident virtual space possible,
+you can alter the constant USRPTSIZE (in
+/sys/vax/vmparam.h).
+Each page of system page tables allows 8 megabytes of user virtual memory.
+.PP
+Because the file system block numbers are stored in
+page table \fIpg_blkno\fP
+entries, the maximum size of a file system is limited to
+2^24 1024 byte blocks. Thus no file system can be larger than 8 gigabytes.
+.PP
+The number of mountable file systems is set at 20 by the definition
+of NMOUNT in /sys/h/param.h.
+This should be sufficient; if not, the value can be increased up to 255.
+If you have many disks, it makes sense to make some of
+them single file systems, and the paging areas don't count in this total.
+.PP
+The limit to the number of files that a process may have open simultaneously
+is set to 64.
+This limit is set by the NOFILE definition in /sys/h/param.h.
+It may be increased arbitrarily, with the caveat that the user structure
+expands by 5 bytes for each file, and thus UPAGES (/sys/vax/machparam.h)
+must be increased accordingly.
+.PP
+The amount of physical memory is currently limited to 64 Mb
+by the size of the index fields in the core-map (/sys/h/cmap.h).
+The limit may be increased by following instructions in that file
+to enlarge those fields.
diff --git a/usr.sbin/config/SMM.doc/e.t b/usr.sbin/config/SMM.doc/e.t
new file mode 100644
index 0000000..0a9505b
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/e.t
@@ -0,0 +1,114 @@
+.\" 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.
+.\"
+.\" @(#)e.t 8.1 (Berkeley) 6/8/93
+.\"
+.\".ds RH "Network configuration options
+.bp
+.LG
+.B
+.ce
+APPENDIX E. NETWORK CONFIGURATION OPTIONS
+.sp
+.R
+.NL
+.PP
+The network support in the kernel is self-configuring
+according to the protocol support options (INET and NS) and the network
+hardware discovered during autoconfiguration.
+There are several changes that may be made to customize network behavior
+due to local restrictions.
+Within the Internet protocol routines, the following options
+set in the system configuration file are supported:
+.IP \fBGATEWAY\fP
+.br
+The machine is to be used as a gateway.
+This option currently makes only minor changes.
+First, the size of the network routing hash table is increased.
+Secondly, machines that have only a single hardware network interface
+will not forward IP packets; without this option, they will also refrain
+from sending any error indication to the source of unforwardable packets.
+Gateways with only a single interface are assumed to have missing
+or broken interfaces, and will return ICMP unreachable errors to hosts
+sending them packets to be forwarded.
+.IP \fBTCP_COMPAT_42\fP
+.br
+This option forces the system to limit its initial TCP sequence numbers
+to positive numbers.
+Without this option, 4.4BSD systems may have problems with TCP connections
+to 4.2BSD systems that connect but never transfer data.
+The problem is a bug in the 4.2BSD TCP.
+.IP \fBIPFORWARDING\fP
+.br
+Normally, 4.4BSD machines with multiple network interfaces
+will forward IP packets received that should be resent to another host.
+If the line ``options IPFORWARDING="0"'' is in the system configuration
+file, IP packet forwarding will be disabled.
+.IP \fBIPSENDREDIRECTS\fP
+.br
+When forwarding IP packets, 4.4BSD IP will note when a packet is forwarded
+using the same interface on which it arrived.
+When this is noted, if the source machine is on the directly-attached
+network, an ICMP redirect is sent to the source host.
+If the packet was forwarded using a route to a host or to a subnet,
+a host redirect is sent, otherwise a network redirect is sent.
+The generation of redirects may be inhibited with the configuration
+option ``options IPSENDREDIRECTS="0".''
+.br
+.IP \fBSUBNETSARELOCAL\fP
+TCP calculates a maximum segment size to use for each connection,
+and sends no datagrams larger than that size.
+This size will be no larger than that supported on the outgoing
+interface.
+Furthermore, if the destination is not on the local network,
+the size will be no larger than 576 bytes.
+For this test, other subnets of a directly-connected subnetted
+network are considered to be local unless the line
+``options SUBNETSARELOCAL="0"'' is used in the system configuration file.
+.LP
+The following options are supported by the Xerox NS protocols:
+.IP \fBNSIP\fP
+.br
+This option allows NS IDP datagrams to be encapsulated in Internet IP
+packets for transmission to a collaborating NSIP host.
+This may be used to pass IDP packets through IP-only link layer networks.
+See
+.IR nsip (4P)
+for details.
+.IP \fBTHREEWAYSHAKE\fP
+.br
+The NS Sequenced Packet Protocol does not require a three-way handshake
+before considering a connection to be in the established state.
+(A three-way handshake consists of a connection request, an acknowledgement
+of the request along with a symmetrical opening indication,
+and then an acknowledgement of the reciprocal opening packet.)
+This option forces a three-way handshake before data may be transmitted
+on Sequenced Packet sockets.
diff --git a/usr.sbin/config/SMM.doc/spell.ok b/usr.sbin/config/SMM.doc/spell.ok
new file mode 100644
index 0000000..50c4ef2
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/spell.ok
@@ -0,0 +1,306 @@
+ACC
+ANSEL
+ARP
+Autoconfiguration
+BUFPAGES
+CANTWAIT
+CH
+COMPAT
+CSS
+Co
+Config
+Config''SMM:2
+DCLR
+DFLDSIZ
+DFLSSIZ
+DFUNNY
+DHAHA
+DMA
+Dev
+Dquot
+ECC
+EMULEX
+Emulex
+Ethernet
+FPNUMBER
+FUNNY,HAHA
+HAVEBDP
+ICMP
+IDP
+IE
+INET
+IP
+IPC
+IPFORWARDING
+IPL
+IPSENDREDIRECTS
+Info
+Karels
+LH
+Leffler
+MAKEDEV
+MAKEDEV.local
+MASSBUS
+MAXDSIZ
+MAXTSIZ
+Makefile
+Mb
+MicroVAX
+Mkopt
+Mkoption
+NBUF
+NEED16
+NEEDBDP
+NINODE
+NMOUNT
+NOFILE
+NPROC
+NS
+NSC
+NSIP
+NUP
+PST
+RCS
+RDY
+RH
+RK07
+RK611
+SCCS
+SITENAME
+SMM:2
+SUBNETSARELOCAL
+SYSPTSIZE
+TCP
+THREEWAYSHAKE
+Timezone
+UCBVAX
+UDP
+UNIBUS
+UPAGES
+UPCS2
+USRPTSIZE
+VAX
+VAX630
+VAX730
+VAX750
+VAX780
+VAX8600
+VAXWELL
+VAXen
+Vax
+Vaxwell
+acc0
+accrint
+accxint
+addr
+arg
+args
+assym.s
+autoconfiguration
+autoconfigure
+autoconfigured
+backpointer
+badaddr
+blkno
+br
+br5
+buf
+bufpages
+buses
+caddr
+callout
+catchall
+cmap.h
+cmd
+conf
+conf.c
+config
+csr
+ct.c
+ctlr
+cvec
+datagrams
+define''s
+dev
+devices.machine
+dgo
+dh.c
+dh0
+dh1
+dh2
+dhreg.h
+dhrint
+dhxint
+dinfo
+dk
+dk.h
+dm0
+dmintr
+dname
+dquot
+dst
+dumpdev
+dumplo
+dumpmagic
+dumpsize
+dz.c
+dz0
+dzrint
+dzxint
+ec0
+eccollide
+ecrint
+ecxint
+endif
+es
+files.machine
+filesystem
+foo
+foo.c
+genkernel
+gettimeofday
+gigabytes
+gprof
+hardwired
+hd
+hk
+hk0
+hkkernel
+howmany
+hp0
+hp0b
+hp1
+hp2
+hp3
+hpkernel
+ht0
+hz
+ident
+ifdef
+ifndef
+il0
+ilcint
+ilrint
+info
+intr
+ioconf.c
+kgmon
+linterrs
+loopback
+machdep.c
+machparam.h
+makefile
+makelinks
+makeoptions
+maxusers
+mba
+mba0
+mba1
+mbuf.h
+mcount.c
+memsize
+minfo
+mname
+moniker
+mspw
+nbuf
+ncallout
+nclist
+ndquot
+ndrive
+netimp
+netinet
+netns
+netstat
+nexi
+nexus
+nfile
+ninode
+nmbclusters
+nnn.ddd
+nproc
+nquota
+nsip
+ntext
+optionlist
+param.c
+param.h
+pathnames
+pg
+physaddr
+pty
+rc
+reg
+rk.c
+rk0
+rk1
+rkintr
+savecore
+sc
+sc0
+sc1
+scdriver
+setrlimit
+sizeof
+softc
+source.c
+subr
+swapxxx.c
+sysname
+te0
+te1
+timezone
+tm0
+tmintr
+tu0
+uba
+uba.c
+uba0
+ubago
+uballoc
+ubamem
+ubanum
+ubareg.h
+ubarelse
+ubavar.h
+ubglue.s
+ubinfo
+ud
+ui
+um
+up.c
+up0
+up1
+up2
+upaddr
+upattach
+upba
+upcs1
+upcs2
+updevice
+updgo
+updinfo
+updtab
+upintr
+upip
+upmaptype
+upminfo
+upprobe
+upslave
+upstd
+upkernel
+upwatch
+upwstart
+value,name2
+value2
+vax
+vaxif
+vaxmba
+vaxuba
+vmparam.h
+kernel
+wildcard
+wildcarded
+wildcarding
+xclu
+xxx
diff --git a/usr.sbin/config/config.5 b/usr.sbin/config/config.5
new file mode 100644
index 0000000..cee1914
--- /dev/null
+++ b/usr.sbin/config/config.5
@@ -0,0 +1,408 @@
+.\" Copyright (c) 2003 Joseph Koshy
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 December 3, 2005
+.Dt CONFIG 5
+.Os
+.Sh NAME
+.Nm config
+.Nd kernel configuration file format
+.Sh DESCRIPTION
+A kernel configuration file specifies the configuration of a
+.Fx
+kernel.
+It is processed by
+.Xr config 8
+to create a build environment where a kernel may be built using
+.Xr make 1 .
+.Ss Lexical Structure
+A kernel configuration file comprises a sequence of specification
+directives.
+.Pp
+A specification directive starts with a keyword at the beginning
+of the line and is followed by additional parameters.
+.Pp
+A specification directive may be terminated by a semicolon
+.Ql \&;
+or by a newline.
+Long input lines may be broken into shorter lines by starting the
+second and subsequent lines with a white space character.
+.Pp
+Case is significant,
+.Dq Li machine
+and
+.Dq Li MACHINE
+are different tokens.
+.Pp
+A double quote character
+.Ql \[dq]
+starts a quoted string.
+All characters up to the next quote character form the value
+of the quoted string.
+A
+.Ql \[dq]
+character may be inserted into a quoted string by
+using the sequence
+.Ql \e\[dq] .
+.Pp
+Numbers are specified using
+.Tn C Ns -style
+syntax.
+.Pp
+A
+.Ql #
+character starts a comment; all characters from the
+.Ql #
+character till the end of the current line are ignored.
+.Pp
+Whitespace between tokens is ignored, except inside quoted strings.
+Whitespace following a comment line is ignored.
+.Ss Configuration Directives
+Kernel configuration directives may appear in any order
+in a kernel configuration file.
+Directives are processed in order of appearance with subsequent
+directive lines overriding the effect of prior ones.
+.Pp
+The list of keywords and their meanings are as follows:
+.Bl -tag -width indent -compact
+.\" -------- CPU --------
+.Pp
+.It Ic cpu Ar cputype
+Specify the CPU this kernel will run on.
+There can be more than one
+.Ic cpu
+directive in a configuration file.
+The allowed list of CPU names is architecture specific and is
+defined in the file
+.Pa sys/conf/options. Ns Aq Ar arch .
+.\" -------- DEVICE --------
+.Pp
+.It Ic device Ar name Op , Ar name Op ...
+.It Ic devices Ar name Op , Ar name Op ...
+Configures the specified devices
+for inclusion into the kernel image.
+Devices that are common to all architectures are
+defined in the file
+.Pa sys/conf/files .
+Devices that are specific to architecture
+.Ar arch
+are defined in the file
+.Pa sys/conf/files. Ns Aq Ar arch .
+.\" -------- ENV --------
+.Pp
+.It Ic env Ar filename
+Specifies a filename containing a kernel environment definition.
+The kernel normally uses an environment prepared for it at boot time
+by
+.Xr loader 8 .
+This directive makes the kernel ignore the boot environment and use
+the compiled-in environment instead.
+.Pp
+This directive is useful for setting kernel tunables in
+embedded environments that do not start from
+.Xr loader 8 .
+.\" -------- FILES --------
+.Pp
+.It Ic files Ar filename
+Specifies a file containing a list of files specific to that kernel
+configuration file (a la
+.Pa files. Ns Aq Ar arch ) .
+.\" -------- HINTS --------
+.Pp
+.It Ic hints Ar filename
+Specifies a file to load a static device configuration specification
+from.
+From
+.Fx 5.0
+onwards, the kernel reads the system's device configuration at boot
+time (see
+.Xr device.hints 5 ) .
+This directive configures the kernel to use the static device configuration
+listed in
+.Ar filename .
+The file
+.Ar filename
+must conform to the syntax specified by
+.Xr device.hints 5 .
+Multiple hints lines are allowed.
+The resulting hints will be the files concatenated in the order of appearance.
+.\" -------- IDENT --------
+.Pp
+.It Ic ident Ar name
+Set the kernel name to
+.Ar name .
+At least one
+.Ic ident
+directive is required.
+.\" -------- INCLUDE --------
+.Pp
+.It Ic include Ar filename
+Read subsequent text from file
+.Ar filename
+and return to the current file after
+.Ar filename
+is successfully processed.
+.\" -------- MACHINE --------
+.Pp
+.It Ic machine Ar arch Op Ar cpuarch
+Specifies the architecture of the machine the kernel is being
+compiled for.
+Legal values for
+.Ar arch
+include:
+.Pp
+.Bl -tag -width ".Cm powerpc" -compact
+.It Cm alpha
+The DEC Alpha architecture.
+.It Cm arm
+The ARM architecture.
+.It Cm amd64
+The AMD x86-64 architecture.
+.It Cm i386
+The Intel x86 based PC architecture.
+.It Cm ia64
+The Intel IA64 architecture.
+.It Cm mips
+The MIPS architecture.
+.It Cm pc98
+The PC98 architecture.
+.It Cm powerpc
+The IBM PowerPC architecture.
+.It Cm sparc64
+The Sun Sparc64 architecture.
+.El
+.Pp
+If argument
+.Ar cpuarch
+is specified, it points
+.Xr config 8
+to the cpu architecture of the machine.
+Currently the
+.Cm pc98
+architecture requires its cpu architecture
+to be set to
+.Cm i386 .
+When
+.Ar cpuarch
+is not specified, it is assumed to be the same as
+.Ar arch .
+.Ar arch
+corresponds to MACHINE.
+.Ar cpuarch
+corresponds to MACHINE_ARCH.
+.Pp
+A kernel configuration file may have only one
+.Ic machine
+directive.
+.\" -------- MAKEOPTION --------
+.Pp
+.It Ic makeoption Ar options
+.It Ic makeoptions Ar options
+Add
+.Ar options
+to the generated makefile.
+.Pp
+The
+.Ar options
+argument is a comma separated list of one or more option
+specifications.
+Each option specification has the form
+.Pp
+.D1 Ar MakeVariableName Ns Op = Ns Ar Value
+.D1 Ar MakeVariableName Ns += Ns Ar Value
+.Pp
+and results in the appropriate
+.Xr make 1
+variable definition being inserted into the generated makefile.
+If only the name of the
+.Xr make 1
+variable is specified,
+.Ar value
+is assumed to be the empty string.
+.Pp
+Example:
+.Bd -literal -offset indent -compact
+makeoptions MYMAKEOPTION="foo"
+makeoptions MYMAKEOPTION+="bar"
+makeoptions MYNULLMAKEOPTION
+.Ed
+.\" -------- MAXUSERS --------
+.Pp
+.It Ic maxusers Ar number
+This optional directive is used to configure the size
+of some kernel data structures.
+The parameter
+.Ar number
+can be 0 (the default) or an integer greater than or equal to 2.
+A value of 0 indicates that the kernel should configure
+its data structures according to the size of available
+physical memory.
+If auto configuration is requested, the kernel will set
+this tunable to a value between 32 and 384.
+.Pp
+As explained in
+.Xr tuning 7 ,
+this tunable can also be set at boot time using
+.Xr loader 8 .
+.\" -------- NOCPU --------
+.Pp
+.It Ic nocpu Ar cputype
+Remove the specified CPU
+from the list of previously selected CPUs.
+This directive can be used to cancel the effect of
+.Ic cpu
+directives in files included using
+.Ic include .
+.\" -------- NODEVICE --------
+.Pp
+.It Ic nodevice Ar name Op , Ar name Op ...
+.It Ic nodevices Ar name Op , Ar name Op ...
+Remove the specified devices
+from the list of previously selected devices.
+This directive can be used to cancel the effects of
+.Ic device
+or
+.Ic devices
+directives in files included using
+.Ic include .
+.\" -------- NOMAKEOPTION --------
+.Pp
+.It Ic nomakeoption Ar name
+.It Ic nomakeoptions Ar name
+Removes previously defined
+.Xr make 1
+option
+.Ar name
+from the kernel build.
+This directive can be used to cancel the effects of
+.Ic makeoption
+directives in files included using
+.Ic include .
+.\" -------- NOOPTION --------
+.Pp
+.It Ic nooption Ar name Op , Ar name Op ...
+.It Ic nooptions Ar name Op , Ar name Op ...
+Remove the specified kernel options
+from the list of previously defined options.
+This directive can be used to cancel the effects of
+.Ic option
+or
+.Ic options
+directives in files included using
+.Ic include .
+.\" -------- OPTIONS --------
+.Pp
+.It Ic option Ar optionspec Op , Ar optionspec Op ...
+.It Ic options Ar optionspec Op , Ar optionspec Op ...
+Add compile time kernel options to the kernel build.
+Each option specification has the form
+.Pp
+.D1 Ar name Ns Op = Ns Ar value
+.Pp
+If
+.Ar value
+is not specified, it is assumed to be
+.Dv NULL .
+Options common to all architectures are specified in
+the file
+.Pa sys/conf/options .
+Options specific to architecture
+.Ar arch
+are specified in the file
+.Pa sys/conf/options. Ns Aq Ar arch .
+.\" -------- PROFILE --------
+.Pp
+.It Ic profile Ar number
+Enables kernel profiling if
+.Ar number
+is non-zero.
+If
+.Ar number
+is 2 or greater, the kernel is configured for
+high-resolution profiling.
+Kernels can also be built for profiling using the
+.Fl p
+option to
+.Xr config 8 .
+.El
+.Ss Obsolete Directives
+The following kernel configuration directives are obsolete.
+.Bl -tag -width indent
+.\" -------- CONFIG --------
+.Pp
+.It Ic config
+This directive was used to specify the device to be used for the root
+file system.
+From
+.Fx 4.0
+onwards, this information is passed to a booting kernel by
+.Xr loader 8 .
+.El
+.Sh FILES
+.Bl -tag -width ".Pa sys/conf/Makefile. Ns Ar arch" -compact
+.It Pa sys/compile/ Ns Ar NAME
+Compile directory created from a kernel configuration.
+.It Pa sys/conf/Makefile. Ns Ar arch
+.Pa Makefile
+fragments for architecture
+.Ar arch .
+.It Pa sys/conf/files
+Devices common to all architectures.
+.It Pa sys/conf/files. Ns Ar arch
+Devices for architecture
+.Ar arch .
+.It Pa sys/conf/options
+Options common to all architectures.
+.It Pa sys/conf/options. Ns Ar arch
+Options for architecture
+.Ar arch .
+.El
+.Sh SEE ALSO
+.Xr kenv 1 ,
+.Xr make 1 ,
+.Xr device.hints 5 ,
+.Xr loader.conf 5 ,
+.Xr config 8 ,
+.Xr kldload 8 ,
+.Xr loader 8
+.Rs
+.%T "Building 4.4BSD Kernels with Config"
+.%A "Samuel J. Leffler"
+.%A "Michael J. Karels"
+.Re
+.Sh HISTORY
+The
+.Xr config 8
+utility first appeared in
+.Bx 4.1 ,
+and was subsequently revised in
+.Bx 4.4 .
+.Pp
+The kernel configuration mechanism changed further in
+.Fx 4.0
+and
+.Fx 5.0 ,
+moving toward an architecture supporting dynamic kernel
+configuration.
diff --git a/usr.sbin/config/config.8 b/usr.sbin/config/config.8
new file mode 100644
index 0000000..904ef6b
--- /dev/null
+++ b/usr.sbin/config/config.8
@@ -0,0 +1,260 @@
+.\" 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.
+.\"
+.\" @(#)config.8 8.2 (Berkeley) 4/19/94
+.\" $FreeBSD$
+.\"
+.Dd May 8, 2007
+.Dt CONFIG 8
+.Os
+.Sh NAME
+.Nm config
+.Nd build system configuration files
+.Sh SYNOPSIS
+.Nm
+.Op Fl CVgp
+.Op Fl d Ar destdir
+.Ar SYSTEM_NAME
+.Nm
+.Op Fl x Ar kernel
+.Sh DESCRIPTION
+The
+.Nm
+utility builds a set of system configuration files from the file
+.Ar SYSTEM_NAME
+which describes
+the system to configure.
+A second file
+tells
+.Nm
+what files are needed to generate a system and
+can be augmented by configuration specific set of files
+that give alternate files for a specific machine
+(see the
+.Sx FILES
+section below).
+.Pp
+Available options and operands:
+.Bl -tag -width ".Ar SYSTEM_NAME"
+.It Fl V
+Print the
+.Nm
+version number.
+.It Fl C
+If the INCLUDE_CONFIG_FILE is present in a configuration file,
+kernel image will contain full configuration files included
+literally (preserving comments).
+This flag is kept for backward compatibility.
+.It Fl d Ar destdir
+Use
+.Ar destdir
+as the output directory, instead of the default one.
+Note that
+.Nm
+does not append
+.Ar SYSTEM_NAME
+to the directory given.
+.It Fl g
+Configure a system for debugging.
+.It Fl x Ar kernel
+Print kernel configuration file embedded into a kernel
+file.
+This option makes sense only if
+.Cd "options INCLUDE_CONFIG_FILE"
+entry was present in your configuration file.
+.It Fl p
+Configure a system for profiling; for example,
+.Xr kgmon 8
+and
+.Xr gprof 1 .
+If two or more
+.Fl p
+options are supplied,
+.Nm
+configures a system for high resolution profiling.
+.It Ar SYSTEM_NAME
+Specify the name of the system configuration file
+containing device specifications, configuration options
+and other system parameters for one system configuration.
+.El
+.Pp
+The
+.Nm
+utility should be run from the
+.Pa conf
+subdirectory of the system source (usually
+.Pa /sys/ Ns Va ARCH Ns Pa /conf ) ,
+where
+.Va ARCH
+represents one of the architectures supported by
+.Fx .
+The
+.Nm
+utility creates the directory
+.Pa ../compile/ Ns Ar SYSTEM_NAME
+or the one given with the
+.Fl d
+option
+as necessary and places all output files there.
+The output of
+.Nm
+consists of a number of files; for the
+.Tn i386 ,
+they are:
+.Pa Makefile ,
+used by
+.Xr make 1
+in building the system;
+header files,
+definitions of
+the number of various devices that will be compiled into the system.
+.Pp
+After running
+.Nm ,
+it is necessary to run
+.Dq Li make depend
+in the directory where the new makefile
+was created.
+The
+.Nm
+utility prints a reminder of this when it completes.
+.Pp
+If any other error messages are produced by
+.Nm ,
+the problems in the configuration file should be corrected and
+.Nm
+should be run again.
+Attempts to compile a system that had configuration errors
+are likely to fail.
+.Sh DEBUG KERNELS
+Traditional
+.Bx
+kernels are compiled without symbols due to the heavy load on the
+system when compiling a
+.Dq debug
+kernel.
+A debug kernel contains complete symbols for all the source files, and
+enables an experienced kernel programmer to analyse the cause of a problem.
+The
+debuggers available prior to
+.Bx 4.4 Lite
+were able to find some information
+from a normal kernel;
+.Xr gdb 1
+provides very little support for normal kernels, and a debug kernel is needed
+for any meaningful analysis.
+.Pp
+For reasons of history, time and space, building a debug kernel is not the
+default with
+.Fx :
+a debug kernel takes up to 30% longer to build and
+requires about 30 MB of disk storage in the build directory, compared to about 6
+MB for a non-debug kernel.
+A debug kernel is about 11 MB in size, compared to
+about 2 MB for a non-debug kernel.
+This space is used both in the root file
+system and at run time in memory.
+Use the
+.Fl g
+option to build a debug kernel.
+With this option,
+.Nm
+causes two kernel files to be built in the kernel build directory:
+.Bl -bullet
+.It
+.Pa kernel.debug
+is the complete debug kernel.
+.It
+.Pa kernel
+is a copy of the kernel with the debug symbols stripped off.
+This is equivalent
+to the normal non-debug kernel.
+.El
+.Pp
+There is currently little sense in installing and booting from a debug kernel,
+since the only tools available which use the symbols do not run on-line.
+There
+are therefore two options for installing a debug kernel:
+.Bl -bullet
+.It
+.Dq Li "make install"
+installs
+.Pa kernel
+in the root file system.
+.It
+.Dq Li "make install.debug"
+installs
+.Pa kernel.debug
+in the root file system.
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /sys/ Ns Va ARCH Ns Pa /compile/ Ns Ar SYSTEM_NAME" -compact
+.It Pa /sys/conf/files
+list of common files system is built from
+.It Pa /sys/conf/Makefile. Ns Va ARCH
+generic makefile for the
+.Va ARCH
+.It Pa /sys/conf/files. Ns Va ARCH
+list of
+.Va ARCH
+specific files
+.It Pa /sys/ Ns Va ARCH Ns Pa /compile/ Ns Ar SYSTEM_NAME
+default kernel build directory for system
+.Ar SYSTEM_NAME
+on
+.Va ARCH .
+.El
+.Sh SEE ALSO
+.Xr config 5
+.Pp
+The
+.Sx SYNOPSIS
+portion of each device in section 4.
+.Rs
+.%T "Building 4.3 BSD UNIX System with Config"
+.Re
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.1 .
+.Pp
+Before support for
+.Fl x
+was introduced,
+.Cd "options INCLUDE_CONFIG_FILE"
+included entire configuration file that used to be embedded in
+the new kernel.
+This meant that
+.Xr strings 1
+could be used to extract it from a kernel:
+to extract the configuration information, you had to use
+the command:
+.Pp
+.Dl "strings -n 3 kernel | sed -n 's/^___//p'"
+.Sh BUGS
+The line numbers reported in error messages are usually off by one.
diff --git a/usr.sbin/config/config.h b/usr.sbin/config/config.h
new file mode 100644
index 0000000..d55c96b
--- /dev/null
+++ b/usr.sbin/config/config.h
@@ -0,0 +1,201 @@
+/*
+ * 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.
+ *
+ * @(#)config.h 8.1 (Berkeley) 6/6/93
+ * $FreeBSD$
+ */
+
+/*
+ * Config.
+ */
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct cfgfile {
+ STAILQ_ENTRY(cfgfile) cfg_next;
+ char *cfg_path;
+};
+STAILQ_HEAD(, cfgfile) cfgfiles;
+
+struct file_list {
+ STAILQ_ENTRY(file_list) f_next;
+ char *f_fn; /* the name */
+ int f_type; /* type */
+ u_char f_flags; /* see below */
+ char *f_compilewith; /* special make rule if present */
+ char *f_depends; /* additional dependancies */
+ char *f_clean; /* File list to add to clean rule */
+ char *f_warn; /* warning message */
+};
+
+struct files_name {
+ char *f_name;
+ STAILQ_ENTRY(files_name) f_next;
+};
+
+/*
+ * Types.
+ */
+#define NORMAL 1
+#define PROFILING 3
+#define NODEPEND 4
+#define LOCAL 5
+#define DEVDONE 0x80000000
+#define TYPEMASK 0x7fffffff
+
+/*
+ * Attributes (flags).
+ */
+#define NO_IMPLCT_RULE 1
+#define NO_OBJ 2
+#define BEFORE_DEPEND 4
+#define NOWERROR 16
+
+struct device {
+ int d_done; /* processed */
+ char *d_name; /* name of device (e.g. rk11) */
+#define UNKNOWN -2 /* -2 means not set yet */
+ STAILQ_ENTRY(device) d_next; /* Next one in list */
+};
+
+struct config {
+ char *s_sysname;
+};
+
+/*
+ * Config has a global notion of which machine type is
+ * being used. It uses the name of the machine in choosing
+ * files and directories. Thus if the name of the machine is ``i386'',
+ * it will build from ``Makefile.i386'' and use ``../i386/inline''
+ * in the makerules, etc. machinearch is the global notion of the
+ * MACHINE_ARCH for this MACHINE.
+ */
+char *machinename;
+char *machinearch;
+
+/*
+ * For each machine, a set of CPU's may be specified as supported.
+ * These and the options (below) are put in the C flags in the makefile.
+ */
+struct cputype {
+ char *cpu_name;
+ SLIST_ENTRY(cputype) cpu_next;
+};
+
+SLIST_HEAD(, cputype) cputype;
+
+/*
+ * A set of options may also be specified which are like CPU types,
+ * but which may also specify values for the options.
+ * A separate set of options may be defined for make-style options.
+ */
+struct opt {
+ char *op_name;
+ char *op_value;
+ int op_ownfile; /* true = own file, false = makefile */
+ SLIST_ENTRY(opt) op_next;
+ SLIST_ENTRY(opt) op_append;
+};
+
+SLIST_HEAD(opt_head, opt) opt, mkopt, rmopts;
+
+struct opt_list {
+ char *o_name;
+ char *o_file;
+ SLIST_ENTRY(opt_list) o_next;
+};
+
+SLIST_HEAD(, opt_list) otab;
+
+struct hint {
+ char *hint_name;
+ STAILQ_ENTRY(hint) hint_next;
+};
+
+STAILQ_HEAD(hint_head, hint) hints;
+
+/*
+ * Tag present in the kernelconf.tmlp template file. It's mandatory for those
+ * two strings to be the same. Otherwise you'll get into trouble.
+ */
+#define KERNCONFTAG "%%KERNCONFFILE%%"
+
+/*
+ * Faked option to note, that the configuration file has been taken from the
+ * kernel file and inclusion of DEFAULTS etc.. isn't nessesery, because we
+ * already have a list of all required devices.
+ */
+#define OPT_AUTOGEN "CONFIG_AUTOGENERATED"
+
+extern char *ident;
+extern char *env;
+extern char kernconfstr[];
+extern int do_trace;
+extern int envmode;
+extern int hintmode;
+extern int incignore;
+
+char *get_word(FILE *);
+char *get_quoted_word(FILE *);
+char *path(const char *);
+char *raisestr(char *);
+void remember(const char *);
+void moveifchanged(const char *, const char *);
+int yyparse(void);
+int yylex(void);
+void options(void);
+void makefile(void);
+void makeenv(void);
+void makehints(void);
+void headers(void);
+void cfgfile_add(const char *);
+void cfgfile_removeall(void);
+
+extern STAILQ_HEAD(device_head, device) dtab;
+
+extern char errbuf[80];
+extern int yyline;
+extern const char *yyfile;
+
+extern STAILQ_HEAD(file_list_head, file_list) ftab;
+
+extern STAILQ_HEAD(files_name_head, files_name) fntab;
+
+extern int profiling;
+extern int debugging;
+extern int found_defaults;
+
+extern int maxusers;
+
+extern char *PREFIX; /* Config file name - for error messages */
+extern char srcdir[]; /* root of the kernel source tree */
+
+#define eq(a,b) (!strcmp(a,b))
+#define ns(s) strdup(s)
diff --git a/usr.sbin/config/config.y b/usr.sbin/config/config.y
new file mode 100644
index 0000000..9425daf
--- /dev/null
+++ b/usr.sbin/config/config.y
@@ -0,0 +1,450 @@
+%union {
+ char *str;
+ int val;
+ struct file_list *file;
+}
+
+%token ARCH
+%token COMMA
+%token CONFIG
+%token CPU
+%token NOCPU
+%token DEVICE
+%token NODEVICE
+%token ENV
+%token EQUALS
+%token PLUSEQUALS
+%token HINTS
+%token IDENT
+%token MAXUSERS
+%token PROFILE
+%token OPTIONS
+%token NOOPTION
+%token MAKEOPTIONS
+%token NOMAKEOPTION
+%token SEMICOLON
+%token INCLUDE
+%token FILES
+
+%token <str> ID
+%token <val> NUMBER
+
+%type <str> Save_id
+%type <str> Opt_value
+%type <str> Dev
+%token <str> PATH
+
+%{
+
+/*
+ * Copyright (c) 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.
+ * 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.
+ *
+ * @(#)config.y 8.1 (Berkeley) 6/6/93
+ * $FreeBSD$
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "config.h"
+
+struct device_head dtab;
+char *ident;
+char *env;
+int envmode;
+int hintmode;
+int yyline;
+const char *yyfile;
+struct file_list_head ftab;
+struct files_name_head fntab;
+char errbuf[80];
+int maxusers;
+
+#define ns(s) strdup(s)
+int include(const char *, int);
+void yyerror(const char *s);
+int yywrap(void);
+
+static void newdev(char *name);
+static void newfile(char *name);
+static void rmdev_schedule(struct device_head *dh, char *name);
+static void newopt(struct opt_head *list, char *name, char *value, int append);
+static void rmopt_schedule(struct opt_head *list, char *name);
+
+static char *
+devopt(char *dev)
+{
+ char *ret = malloc(strlen(dev) + 5);
+
+ sprintf(ret, "DEV_%s", dev);
+ raisestr(ret);
+ return ret;
+}
+
+%}
+%%
+Configuration:
+ Many_specs
+ ;
+
+Many_specs:
+ Many_specs Spec
+ |
+ /* lambda */
+ ;
+
+Spec:
+ Device_spec SEMICOLON
+ |
+ Config_spec SEMICOLON
+ |
+ INCLUDE PATH SEMICOLON {
+ if (incignore == 0)
+ include($2, 0);
+ };
+ |
+ INCLUDE ID SEMICOLON {
+ if (incignore == 0)
+ include($2, 0);
+ };
+ |
+ FILES ID SEMICOLON { newfile($2); };
+ |
+ SEMICOLON
+ |
+ error SEMICOLON
+ ;
+
+Config_spec:
+ ARCH Save_id {
+ if (machinename != NULL && !eq($2, machinename))
+ errx(1, "%s:%d: only one machine directive is allowed",
+ yyfile, yyline);
+ machinename = $2;
+ machinearch = $2;
+ } |
+ ARCH Save_id Save_id {
+ if (machinename != NULL &&
+ !(eq($2, machinename) && eq($3, machinearch)))
+ errx(1, "%s:%d: only one machine directive is allowed",
+ yyfile, yyline);
+ machinename = $2;
+ machinearch = $3;
+ } |
+ CPU Save_id {
+ struct cputype *cp =
+ (struct cputype *)calloc(1, sizeof (struct cputype));
+ cp->cpu_name = $2;
+ SLIST_INSERT_HEAD(&cputype, cp, cpu_next);
+ } |
+ NOCPU Save_id {
+ struct cputype *cp, *cp2;
+ SLIST_FOREACH_SAFE(cp, &cputype, cpu_next, cp2) {
+ if (eq(cp->cpu_name, $2)) {
+ SLIST_REMOVE(&cputype, cp, cputype, cpu_next);
+ free(cp);
+ }
+ }
+ } |
+ OPTIONS Opt_list
+ |
+ NOOPTION Save_id { rmopt_schedule(&opt, $2); } |
+ MAKEOPTIONS Mkopt_list
+ |
+ NOMAKEOPTION Save_id { rmopt_schedule(&mkopt, $2); } |
+ IDENT ID { ident = $2; } |
+ System_spec
+ |
+ MAXUSERS NUMBER { maxusers = $2; } |
+ PROFILE NUMBER { profiling = $2; } |
+ ENV ID {
+ env = $2;
+ envmode = 1;
+ } |
+ HINTS ID {
+ struct hint *hint;
+
+ hint = (struct hint *)calloc(1, sizeof (struct hint));
+ hint->hint_name = $2;
+ STAILQ_INSERT_TAIL(&hints, hint, hint_next);
+ hintmode = 1;
+ }
+
+System_spec:
+ CONFIG System_id System_parameter_list {
+ errx(1, "%s:%d: root/dump/swap specifications obsolete",
+ yyfile, yyline);
+ }
+ |
+ CONFIG System_id
+ ;
+
+System_id:
+ Save_id { newopt(&mkopt, ns("KERNEL"), $1, 0); };
+
+System_parameter_list:
+ System_parameter_list ID
+ | ID
+ ;
+
+Opt_list:
+ Opt_list COMMA Option
+ |
+ Option
+ ;
+
+Option:
+ Save_id {
+ newopt(&opt, $1, NULL, 0);
+ if (strchr($1, '=') != NULL)
+ errx(1, "%s:%d: The `=' in options should not be "
+ "quoted", yyfile, yyline);
+ } |
+ Save_id EQUALS Opt_value {
+ newopt(&opt, $1, $3, 0);
+ } ;
+
+Opt_value:
+ ID { $$ = $1; } |
+ NUMBER {
+ char buf[80];
+
+ (void) snprintf(buf, sizeof(buf), "%d", $1);
+ $$ = ns(buf);
+ } ;
+
+Save_id:
+ ID { $$ = $1; }
+ ;
+
+Mkopt_list:
+ Mkopt_list COMMA Mkoption
+ |
+ Mkoption
+ ;
+
+Mkoption:
+ Save_id { newopt(&mkopt, $1, ns(""), 0); } |
+ Save_id EQUALS Opt_value { newopt(&mkopt, $1, $3, 0); } |
+ Save_id PLUSEQUALS Opt_value { newopt(&mkopt, $1, $3, 1); } ;
+
+Dev:
+ ID { $$ = $1; }
+ ;
+
+Device_spec:
+ DEVICE Dev_list
+ |
+ NODEVICE NoDev_list
+ ;
+
+Dev_list:
+ Dev_list COMMA Device
+ |
+ Device
+ ;
+
+NoDev_list:
+ NoDev_list COMMA NoDevice
+ |
+ NoDevice
+ ;
+
+Device:
+ Dev {
+ newopt(&opt, devopt($1), ns("1"), 0);
+ /* and the device part */
+ newdev($1);
+ }
+
+NoDevice:
+ Dev {
+ char *s = devopt($1);
+
+ rmopt_schedule(&opt, s);
+ free(s);
+ /* and the device part */
+ rmdev_schedule(&dtab, $1);
+ } ;
+
+%%
+
+void
+yyerror(const char *s)
+{
+
+ errx(1, "%s:%d: %s", yyfile, yyline + 1, s);
+}
+
+int
+yywrap(void)
+{
+ if (found_defaults) {
+ if (freopen(PREFIX, "r", stdin) == NULL)
+ err(2, "%s", PREFIX);
+ yyfile = PREFIX;
+ yyline = 0;
+ found_defaults = 0;
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Add a new file to the list of files.
+ */
+static void
+newfile(char *name)
+{
+ struct files_name *nl;
+
+ nl = (struct files_name *) calloc(1, sizeof *nl);
+ nl->f_name = name;
+ STAILQ_INSERT_TAIL(&fntab, nl, f_next);
+}
+
+/*
+ * Find a device in the list of devices.
+ */
+static struct device *
+finddev(struct device_head *dlist, char *name)
+{
+ struct device *dp;
+
+ STAILQ_FOREACH(dp, dlist, d_next)
+ if (eq(dp->d_name, name))
+ return (dp);
+
+ return (NULL);
+}
+
+/*
+ * Add a device to the list of devices.
+ */
+static void
+newdev(char *name)
+{
+ struct device *np;
+
+ if (finddev(&dtab, name)) {
+ printf("WARNING: duplicate device `%s' encountered.\n", name);
+ return;
+ }
+
+ np = (struct device *) calloc(1, sizeof *np);
+ np->d_name = name;
+ STAILQ_INSERT_TAIL(&dtab, np, d_next);
+}
+
+/*
+ * Schedule a device to removal.
+ */
+static void
+rmdev_schedule(struct device_head *dh, char *name)
+{
+ struct device *dp;
+
+ dp = finddev(dh, name);
+ if (dp != NULL) {
+ STAILQ_REMOVE(dh, dp, device, d_next);
+ free(dp->d_name);
+ free(dp);
+ }
+}
+
+/*
+ * Find an option in the list of options.
+ */
+static struct opt *
+findopt(struct opt_head *list, char *name)
+{
+ struct opt *op;
+
+ SLIST_FOREACH(op, list, op_next)
+ if (eq(op->op_name, name))
+ return (op);
+
+ return (NULL);
+}
+
+/*
+ * Add an option to the list of options.
+ */
+static void
+newopt(struct opt_head *list, char *name, char *value, int append)
+{
+ struct opt *op, *op2;
+
+ /*
+ * Ignore inclusions listed explicitly for configuration files.
+ */
+ if (eq(name, OPT_AUTOGEN)) {
+ incignore = 1;
+ return;
+ }
+
+ op2 = findopt(list, name);
+ if (op2 != NULL && !append) {
+ printf("WARNING: duplicate option `%s' encountered.\n", name);
+ return;
+ }
+
+ op = (struct opt *)calloc(1, sizeof (struct opt));
+ op->op_name = name;
+ op->op_ownfile = 0;
+ op->op_value = value;
+ if (op2 != NULL) {
+ while (SLIST_NEXT(op2, op_append) != NULL)
+ op2 = SLIST_NEXT(op2, op_append);
+ SLIST_NEXT(op2, op_append) = op;
+ } else
+ SLIST_INSERT_HEAD(list, op, op_next);
+}
+
+/*
+ * Remove an option from the list of options.
+ */
+static void
+rmopt_schedule(struct opt_head *list, char *name)
+{
+ struct opt *op;
+
+ op = findopt(list, name);
+ if (op != NULL) {
+ SLIST_REMOVE(list, op, opt, op_next);
+ free(op->op_name);
+ free(op);
+ }
+}
diff --git a/usr.sbin/config/configvers.h b/usr.sbin/config/configvers.h
new file mode 100644
index 0000000..28451585
--- /dev/null
+++ b/usr.sbin/config/configvers.h
@@ -0,0 +1,53 @@
+/*-
+ * This file is in the public domain
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * 6 digits of version. The most significant are branch indicators at the
+ * time when the last incompatible change was made (which is why it is
+ * presently 6 on 7-current). The least significant digits are incremented
+ * as described below. The format is similar to the __FreeBSD_version, but
+ * not tied to it.
+ *
+ * DO NOT CASUALLY BUMP THIS NUMBER! The rules are not the same as shared
+ * libs or param.h/osreldate.
+ *
+ * It is the version number of the protocol between config(8) and the
+ * sys/conf/ Makefiles (the kernel build system).
+ *
+ * It is now also used to trap certain problems that the syntax parser cannot
+ * detect.
+ *
+ * Unfortunately, there is no version number for user supplied config files.
+ *
+ * Once, config(8) used to silently report errors and continue anyway. This
+ * was a huge problem for 'make buildkernel' which was run with the installed
+ * /usr/sbin/config, not a cross built one. We started bumping the version
+ * number as a way to trap cases where the previous installworld was not
+ * compatable with the new buildkernel. The buildtools phase and much more
+ * comprehensive error code returns solved this original problem.
+ *
+ * Most end-users will use buildkernel and the build tools from buildworld.
+ * The people that are inconvenienced by gratuitous bumps are developers
+ * who run config by hand. However, developers shouldn't gratuitously be
+ * inconvenienced.
+ *
+ * One should bump the CONFIGVERS in the following ways:
+ *
+ * (1) If you change config such that it won't read old config files,
+ * then bump the major number. You shouldn't be doing this unless
+ * you are overhauling config. Do not casually bump this number
+ * and by implication do not make changes that would force a bump
+ * of this number casually. You should limit major bumps to once
+ * per branch.
+ * (2) For each new feature added, bump the minor version of this file.
+ * When a new feature is actually used by the build system, update the
+ * %VERSREQ field in the Makefile.$ARCH of all the affected makefiles
+ * (typically all of them).
+ *
+ * $FreeBSD$
+ */
+#define CONFIGVERS 600007
+#define MAJOR_VERS(x) ((x) / 100000)
diff --git a/usr.sbin/config/kernconf.tmpl b/usr.sbin/config/kernconf.tmpl
new file mode 100644
index 0000000..182614b
--- /dev/null
+++ b/usr.sbin/config/kernconf.tmpl
@@ -0,0 +1,17 @@
+/*
+ * This file acts as a template for config.c that will be generated in the
+ * kernel build directory after config(8) has been successfully run.
+ *
+ * $FreeBSD$
+ */
+#include "opt_config.h"
+#ifdef INCLUDE_CONFIG_FILE
+
+/*
+ * For !INCLUDE_CONFIG_FILE case, you should look at kern_mib.c. This is
+ * where kernconfstring is defined then.
+ */
+const char kernconfstring[] __attribute__ ((section("kern_conf"))) =
+"%%KERNCONFFILE%%";
+
+#endif /* INCLUDE_CONFIG_FILE */
diff --git a/usr.sbin/config/lang.l b/usr.sbin/config/lang.l
new file mode 100644
index 0000000..075f21f
--- /dev/null
+++ b/usr.sbin/config/lang.l
@@ -0,0 +1,306 @@
+%{
+/*-
+ * 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.
+ *
+ * @(#)lang.l 8.1 (Berkeley) 6/6/93
+ * $FreeBSD$
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <string.h>
+#include "y.tab.h"
+#include "config.h"
+
+#define YY_NO_UNPUT
+
+/*
+ * Data for returning to previous files from include files.
+ */
+struct incl {
+ struct incl *in_prev; /* previous includes in effect, if any */
+ YY_BUFFER_STATE in_buf; /* previous lex state */
+ const char *in_fname; /* previous file name */
+ int in_lineno; /* previous line number */
+ int in_ateof; /* token to insert at EOF */
+};
+static struct incl *inclp;
+static const char *lastfile;
+
+/*
+ * Key word table
+ */
+
+struct kt {
+ const char *kt_name;
+ int kt_val;
+} key_words[] = {
+ { "config", CONFIG },
+ { "cpu", CPU },
+ { "nocpu", NOCPU },
+ { "device", DEVICE },
+ { "devices", DEVICE },
+ { "nodevice", NODEVICE },
+ { "nodevices", NODEVICE },
+ { "env", ENV },
+ { "hints", HINTS },
+ { "ident", IDENT },
+ { "machine", ARCH }, /* MACHINE is defined in /sys/param.h */
+ { "makeoption", MAKEOPTIONS },
+ { "makeoptions", MAKEOPTIONS },
+ { "nomakeoption", NOMAKEOPTION },
+ { "nomakeoptions", NOMAKEOPTION },
+ { "maxusers", MAXUSERS },
+ { "profile", PROFILE },
+ { "option", OPTIONS },
+ { "options", OPTIONS },
+ { "nooption", NOOPTION },
+ { "nooptions", NOOPTION },
+ { "include", INCLUDE },
+ { "files", FILES },
+ { 0, 0 },
+};
+
+
+static int endinclude(void);
+int include(const char *, int);
+int kw_lookup(char *);
+unsigned int octal(const char *);
+unsigned int hex(const char *);
+int yyerror(const char *);
+
+%}
+ID [A-Za-z_][-A-Za-z_0-9]*
+PATH [./][-/.%^A-Za-z_0-9]+
+%START TOEOL
+%%
+{ID} {
+ int i;
+
+ BEGIN 0;
+ if ((i = kw_lookup(yytext)) == -1)
+ {
+ yylval.str = strdup(yytext);
+ return ID;
+ }
+ return i;
+ }
+\\\"[^"]+\\\" {
+ BEGIN 0;
+ yytext[yyleng-2] = '"';
+ yytext[yyleng-1] = '\0';
+ yylval.str = strdup(yytext + 1);
+ return ID;
+ }
+\"[^"]+\" {
+ BEGIN 0;
+ yytext[yyleng-1] = '\0';
+ yylval.str = strdup(yytext + 1);
+ return ID;
+ }
+<TOEOL>[^# \t\n]* {
+ BEGIN 0;
+ yylval.str = strdup(yytext);
+ return ID;
+ }
+0[0-7]* {
+ yylval.val = octal(yytext);
+ return NUMBER;
+ }
+0x[0-9a-fA-F]+ {
+ yylval.val = hex(yytext);
+ return NUMBER;
+ }
+-?[1-9][0-9]* {
+ yylval.val = atoi(yytext);
+ return NUMBER;
+ }
+"?" {
+ yylval.val = -1;
+ return NUMBER;
+ }
+\n/[ \t] {
+ yyline++;
+ }
+\n {
+ yyline++;
+ return SEMICOLON;
+ }
+#.* { /* Ignored (comment) */; }
+[ \t\f]* { /* Ignored (white space) */; }
+";" { return SEMICOLON; }
+"," { return COMMA; }
+"=" { BEGIN TOEOL; return EQUALS; }
+"+=" { BEGIN TOEOL; return PLUSEQUALS; }
+<<EOF>> {
+ int tok;
+
+ if (inclp == NULL)
+ return YY_NULL;
+ tok = endinclude();
+ if (tok != 0)
+ return tok;
+ /* otherwise continue scanning */
+ }
+{PATH} {
+ BEGIN 0;
+ yylval.str = strdup(yytext);
+ return PATH;
+ }
+. { return yytext[0]; }
+
+%%
+/*
+ * kw_lookup
+ * Look up a string in the keyword table. Returns a -1 if the
+ * string is not a keyword otherwise it returns the keyword number
+ */
+
+int
+kw_lookup(char *word)
+{
+ struct kt *kp;
+
+ for (kp = key_words; kp->kt_name != 0; kp++)
+ if (eq(word, kp->kt_name))
+ return kp->kt_val;
+ return -1;
+}
+
+/*
+ * Number conversion routines
+ */
+
+unsigned int
+octal(const char *str)
+{
+ unsigned int num;
+
+ (void) sscanf(str, "%o", &num);
+ return num;
+}
+
+unsigned int
+hex(const char *str)
+{
+ unsigned int num;
+
+ (void) sscanf(str+2, "%x", &num);
+ return num;
+}
+
+void
+cfgfile_add(const char *fname)
+{
+ struct cfgfile *cf;
+
+ cf = calloc(1, sizeof(*cf));
+ assert(cf != NULL);
+ asprintf(&cf->cfg_path, "%s", fname);
+ STAILQ_INSERT_TAIL(&cfgfiles, cf, cfg_next);
+}
+
+void
+cfgfile_removeall(void)
+{
+ struct cfgfile *cf;
+
+ while (!STAILQ_EMPTY(&cfgfiles)) {
+ cf = STAILQ_FIRST(&cfgfiles);
+ STAILQ_REMOVE_HEAD(&cfgfiles, cfg_next);
+ if (cf->cfg_path != NULL)
+ free(cf->cfg_path);
+ free(cf);
+ }
+}
+
+/*
+ * Open the named file for inclusion at the current point. Returns 0 on
+ * success (file opened and previous state pushed), nonzero on failure
+ * (fopen failed, complaint made). The `ateof' parameter controls the
+ * token to be inserted at the end of the include file. If ateof == 0,
+ * then nothing is inserted.
+ */
+int
+include(const char *fname, int ateof)
+{
+ FILE *fp;
+ struct incl *in;
+ char *fnamebuf;
+
+ fnamebuf = NULL;
+ fp = fopen(fname, "r");
+ if (fp == NULL && fname[0] != '.' && fname[0] != '/') {
+ asprintf(&fnamebuf, "../../conf/%s", fname);
+ if (fnamebuf != NULL) {
+ fp = fopen(fnamebuf, "r");
+ free(fnamebuf);
+ }
+ }
+ if (fp == NULL) {
+ yyerror("cannot open included file");
+ return (-1);
+ }
+ cfgfile_add(fnamebuf == NULL ? fname : fnamebuf);
+ in = malloc(sizeof(*in));
+ assert(in != NULL);
+ in->in_prev = inclp;
+ in->in_buf = YY_CURRENT_BUFFER;
+ in->in_fname = yyfile;
+ in->in_lineno = yyline;
+ in->in_ateof = ateof;
+ inclp = in;
+ yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE));
+ yyfile = fname;
+ yyline = 0;
+ return (0);
+}
+
+/*
+ * Terminate the most recent inclusion.
+ */
+static int
+endinclude()
+{
+ struct incl *in;
+ int ateof;
+
+ in = inclp;
+ assert(in != NULL);
+ inclp = in->in_prev;
+ lastfile = yyfile;
+ yy_delete_buffer(YY_CURRENT_BUFFER);
+ (void)fclose(yyin);
+ yy_switch_to_buffer(in->in_buf);
+ yyfile = in->in_fname;
+ yyline = in->in_lineno;
+ ateof = in->in_ateof;
+ free(in);
+
+ return (ateof);
+}
diff --git a/usr.sbin/config/main.c b/usr.sbin/config/main.c
new file mode 100644
index 0000000..34806ab
--- /dev/null
+++ b/usr.sbin/config/main.c
@@ -0,0 +1,721 @@
+/*
+ * 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
+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
+#if 0
+static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/sbuf.h>
+#include <sys/file.h>
+#include <sys/mman.h>
+#include <sys/param.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <dirent.h>
+#include "y.tab.h"
+#include "config.h"
+#include "configvers.h"
+
+#ifndef TRUE
+#define TRUE (1)
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#define CDIR "../compile/"
+
+char * PREFIX;
+char destdir[MAXPATHLEN];
+char srcdir[MAXPATHLEN];
+
+int debugging;
+int profiling;
+int found_defaults;
+int incignore;
+
+/*
+ * Preserve old behaviour in INCLUDE_CONFIG_FILE handling (files are included
+ * literally).
+ */
+int filebased = 0;
+
+static void configfile(void);
+static void get_srcdir(void);
+static void usage(void);
+static void cleanheaders(char *);
+static void kernconfdump(const char *);
+
+struct hdr_list {
+ char *h_name;
+ struct hdr_list *h_next;
+} *htab;
+
+/*
+ * Config builds a set of files for building a UNIX
+ * system given a description of the desired system.
+ */
+int
+main(int argc, char **argv)
+{
+
+ struct stat buf;
+ int ch, len;
+ char *p;
+ char xxx[MAXPATHLEN];
+ char *kernfile;
+
+ kernfile = NULL;
+ while ((ch = getopt(argc, argv, "Cd:gpVx:")) != -1)
+ switch (ch) {
+ case 'C':
+ filebased = 1;
+ break;
+ case 'd':
+ if (*destdir == '\0')
+ strlcpy(destdir, optarg, sizeof(destdir));
+ else
+ errx(2, "directory already set");
+ break;
+ case 'g':
+ debugging++;
+ break;
+ case 'p':
+ profiling++;
+ break;
+ case 'V':
+ printf("%d\n", CONFIGVERS);
+ exit(0);
+ case 'x':
+ kernfile = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (kernfile != NULL) {
+ kernconfdump(kernfile);
+ exit(EXIT_SUCCESS);
+ }
+
+ if (argc != 1)
+ usage();
+
+ PREFIX = *argv;
+ if (stat(PREFIX, &buf) != 0 || !S_ISREG(buf.st_mode))
+ err(2, "%s", PREFIX);
+ if (freopen("DEFAULTS", "r", stdin) != NULL) {
+ found_defaults = 1;
+ yyfile = "DEFAULTS";
+ } else {
+ if (freopen(PREFIX, "r", stdin) == NULL)
+ err(2, "%s", PREFIX);
+ yyfile = PREFIX;
+ }
+ if (*destdir != '\0') {
+ len = strlen(destdir);
+ while (len > 1 && destdir[len - 1] == '/')
+ destdir[--len] = '\0';
+ get_srcdir();
+ } else {
+ strlcpy(destdir, CDIR, sizeof(destdir));
+ strlcat(destdir, PREFIX, sizeof(destdir));
+ }
+
+ p = path((char *)NULL);
+ if (stat(p, &buf)) {
+ if (mkdir(p, 0777))
+ err(2, "%s", p);
+ } else if (!S_ISDIR(buf.st_mode))
+ errx(2, "%s isn't a directory", p);
+
+ SLIST_INIT(&cputype);
+ SLIST_INIT(&mkopt);
+ SLIST_INIT(&opt);
+ SLIST_INIT(&rmopts);
+ STAILQ_INIT(&cfgfiles);
+ STAILQ_INIT(&dtab);
+ STAILQ_INIT(&fntab);
+ STAILQ_INIT(&ftab);
+ STAILQ_INIT(&hints);
+ if (yyparse())
+ exit(3);
+
+ /*
+ * Ensure that required elements (machine, cpu, ident) are present.
+ */
+ if (machinename == NULL) {
+ printf("Specify machine type, e.g. ``machine i386''\n");
+ exit(1);
+ }
+ if (ident == NULL) {
+ printf("no ident line specified\n");
+ exit(1);
+ }
+ if (SLIST_EMPTY(&cputype)) {
+ printf("cpu type must be specified\n");
+ exit(1);
+ }
+
+ /*
+ * make symbolic links in compilation directory
+ * for "sys" (to make genassym.c work along with #include <sys/xxx>)
+ * and similarly for "machine".
+ */
+ if (*srcdir == '\0')
+ (void)snprintf(xxx, sizeof(xxx), "../../include");
+ else
+ (void)snprintf(xxx, sizeof(xxx), "%s/%s/include",
+ srcdir, machinename);
+ (void) unlink(path("machine"));
+ (void) symlink(xxx, path("machine"));
+ if (strcmp(machinename, machinearch) != 0) {
+ /*
+ * make symbolic links in compilation directory for
+ * machinearch, if it is different than machinename.
+ */
+ if (*srcdir == '\0')
+ (void)snprintf(xxx, sizeof(xxx), "../../../%s/include",
+ machinearch);
+ else
+ (void)snprintf(xxx, sizeof(xxx), "%s/%s/include",
+ srcdir, machinearch);
+ (void) unlink(path(machinearch));
+ (void) symlink(xxx, path(machinearch));
+ }
+ configfile(); /* put config file into kernel*/
+ options(); /* make options .h files */
+ makefile(); /* build Makefile */
+ makeenv(); /* build env.c */
+ makehints(); /* build hints.c */
+ headers(); /* make a lot of .h files */
+ cleanheaders(p);
+ printf("Kernel build directory is %s\n", p);
+ printf("Don't forget to do ``make cleandepend && make depend''\n");
+ exit(0);
+}
+
+/*
+ * get_srcdir
+ * determine the root of the kernel source tree
+ * and save that in srcdir.
+ */
+static void
+get_srcdir(void)
+{
+ struct stat lg, phy;
+ char *p, *pwd;
+ int i;
+
+ if (realpath("../..", srcdir) == NULL)
+ errx(2, "Unable to find root of source tree");
+ if ((pwd = getenv("PWD")) != NULL && *pwd == '/' &&
+ (pwd = strdup(pwd)) != NULL) {
+ /* Remove the last two path components. */
+ for (i = 0; i < 2; i++) {
+ if ((p = strrchr(pwd, '/')) == NULL) {
+ free(pwd);
+ return;
+ }
+ *p = '\0';
+ }
+ if (stat(pwd, &lg) != -1 && stat(srcdir, &phy) != -1 &&
+ lg.st_dev == phy.st_dev && lg.st_ino == phy.st_ino)
+ strlcpy(srcdir, pwd, MAXPATHLEN);
+ free(pwd);
+ }
+}
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "usage: config [-CgpV] [-d destdir] sysname\n");
+ fprintf(stderr, " config -x kernel\n");
+ exit(EX_USAGE);
+}
+
+/*
+ * get_word
+ * returns EOF on end of file
+ * NULL on end of line
+ * pointer to the word otherwise
+ */
+char *
+get_word(FILE *fp)
+{
+ static char line[80];
+ int ch;
+ char *cp;
+ int escaped_nl = 0;
+
+begin:
+ while ((ch = getc(fp)) != EOF)
+ if (ch != ' ' && ch != '\t')
+ break;
+ if (ch == EOF)
+ return ((char *)EOF);
+ if (ch == '\\'){
+ escaped_nl = 1;
+ goto begin;
+ }
+ if (ch == '\n') {
+ if (escaped_nl){
+ escaped_nl = 0;
+ goto begin;
+ }
+ else
+ return (NULL);
+ }
+ cp = line;
+ *cp++ = ch;
+ while ((ch = getc(fp)) != EOF) {
+ if (isspace(ch))
+ break;
+ *cp++ = ch;
+ }
+ *cp = 0;
+ if (ch == EOF)
+ return ((char *)EOF);
+ (void) ungetc(ch, fp);
+ return (line);
+}
+
+/*
+ * get_quoted_word
+ * like get_word but will accept something in double or single quotes
+ * (to allow embedded spaces).
+ */
+char *
+get_quoted_word(FILE *fp)
+{
+ static char line[256];
+ int ch;
+ char *cp;
+ int escaped_nl = 0;
+
+begin:
+ while ((ch = getc(fp)) != EOF)
+ if (ch != ' ' && ch != '\t')
+ break;
+ if (ch == EOF)
+ return ((char *)EOF);
+ if (ch == '\\'){
+ escaped_nl = 1;
+ goto begin;
+ }
+ if (ch == '\n') {
+ if (escaped_nl){
+ escaped_nl = 0;
+ goto begin;
+ }
+ else
+ return (NULL);
+ }
+ cp = line;
+ if (ch == '"' || ch == '\'') {
+ int quote = ch;
+
+ while ((ch = getc(fp)) != EOF) {
+ if (ch == quote)
+ break;
+ if (ch == '\n') {
+ *cp = 0;
+ printf("config: missing quote reading `%s'\n",
+ line);
+ exit(2);
+ }
+ *cp++ = ch;
+ }
+ } else {
+ *cp++ = ch;
+ while ((ch = getc(fp)) != EOF) {
+ if (isspace(ch))
+ break;
+ *cp++ = ch;
+ }
+ if (ch != EOF)
+ (void) ungetc(ch, fp);
+ }
+ *cp = 0;
+ if (ch == EOF)
+ return ((char *)EOF);
+ return (line);
+}
+
+/*
+ * prepend the path to a filename
+ */
+char *
+path(const char *file)
+{
+ char *cp = NULL;
+
+ if (file)
+ asprintf(&cp, "%s/%s", destdir, file);
+ else
+ cp = strdup(destdir);
+ return (cp);
+}
+
+/*
+ * Generate configuration file based on actual settings. With this mode, user
+ * will be able to obtain and build conifguration file with one command.
+ */
+static void
+configfile_dynamic(struct sbuf *sb)
+{
+ struct cputype *cput;
+ struct device *d;
+ struct opt *ol;
+ char *lend;
+ unsigned int i;
+
+ asprintf(&lend, "\\n\\\n");
+ assert(lend != NULL);
+ sbuf_printf(sb, "options\t%s%s", OPT_AUTOGEN, lend);
+ sbuf_printf(sb, "ident\t%s%s", ident, lend);
+ sbuf_printf(sb, "machine\t%s%s", machinename, lend);
+ SLIST_FOREACH(cput, &cputype, cpu_next)
+ sbuf_printf(sb, "cpu\t%s%s", cput->cpu_name, lend);
+ SLIST_FOREACH(ol, &mkopt, op_next)
+ sbuf_printf(sb, "makeoptions\t%s=%s%s", ol->op_name,
+ ol->op_value, lend);
+ SLIST_FOREACH(ol, &opt, op_next) {
+ if (strncmp(ol->op_name, "DEV_", 4) == 0)
+ continue;
+ sbuf_printf(sb, "options\t%s", ol->op_name);
+ if (ol->op_value != NULL) {
+ sbuf_putc(sb, '=');
+ for (i = 0; i < strlen(ol->op_value); i++) {
+ if (ol->op_value[i] == '"')
+ sbuf_printf(sb, "\\%c",
+ ol->op_value[i]);
+ else
+ sbuf_printf(sb, "%c",
+ ol->op_value[i]);
+ }
+ sbuf_printf(sb, "%s", lend);
+ } else {
+ sbuf_printf(sb, "%s", lend);
+ }
+ }
+ /*
+ * Mark this file as containing everything we need.
+ */
+ STAILQ_FOREACH(d, &dtab, d_next)
+ sbuf_printf(sb, "device\t%s%s", d->d_name, lend);
+ free(lend);
+}
+
+/*
+ * Generate file from the configuration files.
+ */
+static void
+configfile_filebased(struct sbuf *sb)
+{
+ FILE *cff;
+ struct cfgfile *cf;
+ int i;
+
+ /*
+ * Try to read all configuration files. Since those will be present as
+ * C string in the macro, we have to slash their ends then the line
+ * wraps.
+ */
+ STAILQ_FOREACH(cf, &cfgfiles, cfg_next) {
+ cff = fopen(cf->cfg_path, "r");
+ if (cff == NULL) {
+ warn("Couldn't open file %s", cf->cfg_path);
+ continue;
+ }
+ while ((i = getc(cff)) != EOF) {
+ if (i == '\n')
+ sbuf_printf(sb, "\\n\\\n");
+ else if (i == '"' || i == '\'')
+ sbuf_printf(sb, "\\%c", i);
+ else
+ sbuf_putc(sb, i);
+ }
+ fclose(cff);
+ }
+}
+
+static void
+configfile(void)
+{
+ FILE *fo;
+ struct sbuf *sb;
+ char *p;
+
+ /* Add main configuration file to the list of files to be included */
+ cfgfile_add(PREFIX);
+ p = path("config.c.new");
+ fo = fopen(p, "w");
+ if (!fo)
+ err(2, "%s", p);
+ sb = sbuf_new(NULL, NULL, 2048, SBUF_AUTOEXTEND);
+ assert(sb != NULL);
+ sbuf_clear(sb);
+ if (filebased) {
+ /* Is needed, can be used for backward compatibility. */
+ configfile_filebased(sb);
+ } else {
+ configfile_dynamic(sb);
+ }
+ sbuf_finish(sb);
+ /*
+ * We print first part of the tamplate, replace our tag with
+ * configuration files content and later continue writing our
+ * template.
+ */
+ p = strstr(kernconfstr, KERNCONFTAG);
+ if (p == NULL)
+ errx(EXIT_FAILURE, "Something went terribly wrong!");
+ *p = '\0';
+ fprintf(fo, "%s", kernconfstr);
+ fprintf(fo, "%s", sbuf_data(sb));
+ p += strlen(KERNCONFTAG);
+ fprintf(fo, "%s", p);
+ sbuf_delete(sb);
+ fclose(fo);
+ moveifchanged(path("config.c.new"), path("config.c"));
+ cfgfile_removeall();
+}
+
+/*
+ * moveifchanged --
+ * compare two files; rename if changed.
+ */
+void
+moveifchanged(const char *from_name, const char *to_name)
+{
+ char *p, *q;
+ int changed;
+ size_t tsize;
+ struct stat from_sb, to_sb;
+ int from_fd, to_fd;
+
+ changed = 0;
+
+ if ((from_fd = open(from_name, O_RDONLY)) < 0)
+ err(EX_OSERR, "moveifchanged open(%s)", from_name);
+
+ if ((to_fd = open(to_name, O_RDONLY)) < 0)
+ changed++;
+
+ if (!changed && fstat(from_fd, &from_sb) < 0)
+ err(EX_OSERR, "moveifchanged fstat(%s)", from_name);
+
+ if (!changed && fstat(to_fd, &to_sb) < 0)
+ err(EX_OSERR, "moveifchanged fstat(%s)", to_name);
+
+ if (!changed && from_sb.st_size != to_sb.st_size)
+ changed++;
+
+ tsize = (size_t)from_sb.st_size;
+
+ if (!changed) {
+ p = mmap(NULL, tsize, PROT_READ, MAP_SHARED, from_fd, (off_t)0);
+ if (p == MAP_FAILED)
+ err(EX_OSERR, "mmap %s", from_name);
+ q = mmap(NULL, tsize, PROT_READ, MAP_SHARED, to_fd, (off_t)0);
+ if (q == MAP_FAILED)
+ err(EX_OSERR, "mmap %s", to_name);
+
+ changed = memcmp(p, q, tsize);
+ munmap(p, tsize);
+ munmap(q, tsize);
+ }
+ if (changed) {
+ if (rename(from_name, to_name) < 0)
+ err(EX_OSERR, "rename(%s, %s)", from_name, to_name);
+ } else {
+ if (unlink(from_name) < 0)
+ err(EX_OSERR, "unlink(%s)", from_name);
+ }
+}
+
+static void
+cleanheaders(char *p)
+{
+ DIR *dirp;
+ struct dirent *dp;
+ struct file_list *fl;
+ struct hdr_list *hl;
+ int i;
+
+ remember("y.tab.h");
+ remember("setdefs.h");
+ STAILQ_FOREACH(fl, &ftab, f_next)
+ remember(fl->f_fn);
+
+ /*
+ * Scan the build directory and clean out stuff that looks like
+ * it might have been a leftover NFOO header, etc.
+ */
+ if ((dirp = opendir(p)) == NULL)
+ err(EX_OSERR, "opendir %s", p);
+ while ((dp = readdir(dirp)) != NULL) {
+ i = dp->d_namlen - 2;
+ /* Skip non-headers */
+ if (dp->d_name[i] != '.' || dp->d_name[i + 1] != 'h')
+ continue;
+ /* Skip special stuff, eg: bus_if.h, but check opt_*.h */
+ if (index(dp->d_name, '_') &&
+ strncmp(dp->d_name, "opt_", 4) != 0)
+ continue;
+ /* Check if it is a target file */
+ for (hl = htab; hl != NULL; hl = hl->h_next) {
+ if (eq(dp->d_name, hl->h_name)) {
+ break;
+ }
+ }
+ if (hl)
+ continue;
+ printf("Removing stale header: %s\n", dp->d_name);
+ if (unlink(path(dp->d_name)) == -1)
+ warn("unlink %s", dp->d_name);
+ }
+ (void)closedir(dirp);
+}
+
+void
+remember(const char *file)
+{
+ char *s;
+ struct hdr_list *hl;
+
+ if ((s = strrchr(file, '/')) != NULL)
+ s = ns(s + 1);
+ else
+ s = ns(file);
+
+ if (index(s, '_') && strncmp(s, "opt_", 4) != 0) {
+ free(s);
+ return;
+ }
+ for (hl = htab; hl != NULL; hl = hl->h_next) {
+ if (eq(s, hl->h_name)) {
+ free(s);
+ return;
+ }
+ }
+ hl = calloc(1, sizeof(*hl));
+ hl->h_name = s;
+ hl->h_next = htab;
+ htab = hl;
+}
+
+/*
+ * This one is quick hack. Will be probably moved to elf(3) interface.
+ * It takes kernel configuration file name, passes it as an argument to
+ * elfdump -a, which output is parsed by some UNIX tools...
+ */
+static void
+kernconfdump(const char *file)
+{
+ struct stat st;
+ FILE *fp, *pp;
+ int error, len, osz, r;
+ unsigned int i, off, size;
+ char *cmd, *o;
+
+ r = open(file, O_RDONLY);
+ if (r == -1)
+ errx(EXIT_FAILURE, "Couldn't open file '%s'", file);
+ error = fstat(r, &st);
+ if (error == -1)
+ errx(EXIT_FAILURE, "fstat() failed");
+ if (S_ISDIR(st.st_mode))
+ errx(EXIT_FAILURE, "'%s' is a directory", file);
+ fp = fdopen(r, "r");
+ if (fp == NULL)
+ errx(EXIT_FAILURE, "fdopen() failed");
+ osz = 1024;
+ o = calloc(1, osz);
+ if (o == NULL)
+ errx(EXIT_FAILURE, "Couldn't allocate memory");
+ /* ELF note section header. */
+ asprintf(&cmd, "/usr/bin/elfdump -c %s | grep -A 5 kern_conf"
+ "| tail -2 | cut -d ' ' -f 2 | paste - - -", file);
+ if (cmd == NULL)
+ errx(EXIT_FAILURE, "asprintf() failed");
+ pp = popen(cmd, "r");
+ if (pp == NULL)
+ errx(EXIT_FAILURE, "popen() failed");
+ free(cmd);
+ len = fread(o, osz, 1, pp);
+ pclose(pp);
+ r = sscanf(o, "%d\t%d", &off, &size);
+ free(o);
+ if (r != 2)
+ errx(EXIT_FAILURE, "File %s doesn't contain configuration "
+ "file. Either unsupported, or not compiled with "
+ "INCLUDE_CONFIG_FILE", file);
+ r = fseek(fp, off, SEEK_CUR);
+ if (r != 0)
+ errx(EXIT_FAILURE, "fseek() failed");
+ for (i = 0; i < size - 1; i++) {
+ r = fgetc(fp);
+ if (r == EOF)
+ break;
+ /*
+ * If '\0' is present in the middle of the configuration
+ * string, this means something very weird is happening.
+ * Make such case very visible.
+ */
+ assert(r != '\0' && ("Char present in the configuration "
+ "string mustn't be equal to 0"));
+ fputc(r, stdout);
+ }
+ fclose(fp);
+}
diff --git a/usr.sbin/config/mkheaders.c b/usr.sbin/config/mkheaders.c
new file mode 100644
index 0000000..fd7f1b3
--- /dev/null
+++ b/usr.sbin/config/mkheaders.c
@@ -0,0 +1,66 @@
+/*
+ * 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[] = "@(#)mkheaders.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * Make all the .h files for the optional entries
+ */
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/param.h>
+#include "config.h"
+#include "y.tab.h"
+
+void
+headers(void)
+{
+ struct device *dp;
+ int errors;
+
+ errors = 0;
+ STAILQ_FOREACH(dp, &dtab, d_next) {
+ if (!(dp->d_done & DEVDONE)) {
+ warnx("Error: device \"%s\" is unknown",
+ dp->d_name);
+ errors++;
+ }
+ }
+ if (errors)
+ errx(1, "%d errors", errors);
+}
diff --git a/usr.sbin/config/mkmakefile.c b/usr.sbin/config/mkmakefile.c
new file mode 100644
index 0000000..a89db8e
--- /dev/null
+++ b/usr.sbin/config/mkmakefile.c
@@ -0,0 +1,785 @@
+/*
+ * Copyright (c) 1993, 19801990
+ * 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[] = "@(#)mkmakefile.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * Build the makefile for the system, from
+ * the information in the files files and the
+ * additional files for the machine being compiled to.
+ */
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/param.h>
+#include "y.tab.h"
+#include "config.h"
+#include "configvers.h"
+
+#define next_word(fp, wd) \
+ { char *word = get_word(fp); \
+ if (word == (char *)EOF) \
+ return; \
+ else \
+ wd = word; \
+ }
+#define next_quoted_word(fp, wd) \
+ { char *word = get_quoted_word(fp); \
+ if (word == (char *)EOF) \
+ return; \
+ else \
+ wd = word; \
+ }
+
+static char *tail(char *);
+static void do_clean(FILE *);
+static void do_rules(FILE *);
+static void do_xxfiles(char *, FILE *);
+static void do_objs(FILE *);
+static void do_before_depend(FILE *);
+static int opteq(const char *, const char *);
+static void read_files(void);
+
+/*
+ * Lookup a file, by name.
+ */
+static struct file_list *
+fl_lookup(char *file)
+{
+ struct file_list *fp;
+
+ STAILQ_FOREACH(fp, &ftab, f_next) {
+ if (eq(fp->f_fn, file))
+ return (fp);
+ }
+ return (0);
+}
+
+/*
+ * Make a new file list entry
+ */
+static struct file_list *
+new_fent(void)
+{
+ struct file_list *fp;
+
+ fp = (struct file_list *) calloc(1, sizeof *fp);
+ STAILQ_INSERT_TAIL(&ftab, fp, f_next);
+ return (fp);
+}
+
+/*
+ * Build the makefile from the skeleton
+ */
+void
+makefile(void)
+{
+ FILE *ifp, *ofp;
+ char line[BUFSIZ];
+ struct opt *op, *t;
+ int versreq;
+
+ read_files();
+ snprintf(line, sizeof(line), "../../conf/Makefile.%s", machinename);
+ ifp = fopen(line, "r");
+ if (ifp == 0) {
+ snprintf(line, sizeof(line), "Makefile.%s", machinename);
+ ifp = fopen(line, "r");
+ }
+ if (ifp == 0)
+ err(1, "%s", line);
+
+ ofp = fopen(path("Makefile.new"), "w");
+ if (ofp == 0)
+ err(1, "%s", path("Makefile.new"));
+ fprintf(ofp, "KERN_IDENT=%s\n", ident);
+ SLIST_FOREACH_SAFE(op, &mkopt, op_next, t) {
+ fprintf(ofp, "%s=%s", op->op_name, op->op_value);
+ while ((op = SLIST_NEXT(op, op_append)) != NULL)
+ fprintf(ofp, " %s", op->op_value);
+ fprintf(ofp, "\n");
+ }
+ if (debugging)
+ fprintf(ofp, "DEBUG=-g\n");
+ if (profiling)
+ fprintf(ofp, "PROFLEVEL=%d\n", profiling);
+ if (*srcdir != '\0')
+ fprintf(ofp,"S=%s\n", srcdir);
+ while (fgets(line, BUFSIZ, ifp) != 0) {
+ if (*line != '%') {
+ fprintf(ofp, "%s", line);
+ continue;
+ }
+ if (eq(line, "%BEFORE_DEPEND\n"))
+ do_before_depend(ofp);
+ else if (eq(line, "%OBJS\n"))
+ do_objs(ofp);
+ else if (strncmp(line, "%FILES.", 7) == 0)
+ do_xxfiles(line, ofp);
+ else if (eq(line, "%RULES\n"))
+ do_rules(ofp);
+ else if (eq(line, "%CLEAN\n"))
+ do_clean(ofp);
+ else if (strncmp(line, "%VERSREQ=", sizeof("%VERSREQ=") - 1) == 0) {
+ versreq = atoi(line + sizeof("%VERSREQ=") - 1);
+ if (MAJOR_VERS(versreq) != MAJOR_VERS(CONFIGVERS) ||
+ versreq > CONFIGVERS) {
+ fprintf(stderr, "ERROR: version of config(8) does not match kernel!\n");
+ fprintf(stderr, "config version = %d, ", CONFIGVERS);
+ fprintf(stderr, "version required = %d\n\n", versreq);
+ fprintf(stderr, "Make sure that /usr/src/usr.sbin/config is in sync\n");
+ fprintf(stderr, "with your /usr/src/sys and install a new config binary\n");
+ fprintf(stderr, "before trying this again.\n\n");
+ fprintf(stderr, "If running the new config fails check your config\n");
+ fprintf(stderr, "file against the GENERIC or LINT config files for\n");
+ fprintf(stderr, "changes in config syntax, or option/device naming\n");
+ fprintf(stderr, "conventions\n\n");
+ exit(1);
+ }
+ } else
+ fprintf(stderr,
+ "Unknown %% construct in generic makefile: %s",
+ line);
+ }
+ (void) fclose(ifp);
+ (void) fclose(ofp);
+ moveifchanged(path("Makefile.new"), path("Makefile"));
+}
+
+/*
+ * Build hints.c from the skeleton
+ */
+void
+makehints(void)
+{
+ FILE *ifp, *ofp;
+ char line[BUFSIZ];
+ char *s;
+ struct hint *hint;
+
+ ofp = fopen(path("hints.c.new"), "w");
+ if (ofp == NULL)
+ err(1, "%s", path("hints.c.new"));
+ fprintf(ofp, "#include <sys/types.h>\n");
+ fprintf(ofp, "#include <sys/systm.h>\n");
+ fprintf(ofp, "\n");
+ fprintf(ofp, "int hintmode = %d;\n", hintmode);
+ fprintf(ofp, "char static_hints[] = {\n");
+ STAILQ_FOREACH(hint, &hints, hint_next) {
+ ifp = fopen(hint->hint_name, "r");
+ if (ifp == NULL)
+ err(1, "%s", hint->hint_name);
+ while (fgets(line, BUFSIZ, ifp) != 0) {
+ /* zap trailing CR and/or LF */
+ while ((s = rindex(line, '\n')) != NULL)
+ *s = '\0';
+ while ((s = rindex(line, '\r')) != NULL)
+ *s = '\0';
+ /* remove # comments */
+ s = index(line, '#');
+ if (s)
+ *s = '\0';
+ /* remove any whitespace and " characters */
+ s = line;
+ while (*s) {
+ if (*s == ' ' || *s == '\t' || *s == '"') {
+ while (*s) {
+ s[0] = s[1];
+ s++;
+ }
+ /* start over */
+ s = line;
+ continue;
+ }
+ s++;
+ }
+ /* anything left? */
+ if (*line == '\0')
+ continue;
+ fprintf(ofp, "\"%s\\0\"\n", line);
+ }
+ fclose(ifp);
+ }
+ fprintf(ofp, "\"\\0\"\n};\n");
+ fclose(ofp);
+ moveifchanged(path("hints.c.new"), path("hints.c"));
+}
+
+/*
+ * Build env.c from the skeleton
+ */
+void
+makeenv(void)
+{
+ FILE *ifp, *ofp;
+ char line[BUFSIZ];
+ char *s;
+
+ if (env) {
+ ifp = fopen(env, "r");
+ if (ifp == NULL)
+ err(1, "%s", env);
+ } else {
+ ifp = NULL;
+ }
+ ofp = fopen(path("env.c.new"), "w");
+ if (ofp == NULL)
+ err(1, "%s", path("env.c.new"));
+ fprintf(ofp, "#include <sys/types.h>\n");
+ fprintf(ofp, "#include <sys/systm.h>\n");
+ fprintf(ofp, "\n");
+ fprintf(ofp, "int envmode = %d;\n", envmode);
+ fprintf(ofp, "char static_env[] = {\n");
+ if (ifp) {
+ while (fgets(line, BUFSIZ, ifp) != 0) {
+ /* zap trailing CR and/or LF */
+ while ((s = rindex(line, '\n')) != NULL)
+ *s = '\0';
+ while ((s = rindex(line, '\r')) != NULL)
+ *s = '\0';
+ /* remove # comments */
+ s = index(line, '#');
+ if (s)
+ *s = '\0';
+ /* remove any whitespace and " characters */
+ s = line;
+ while (*s) {
+ if (*s == ' ' || *s == '\t' || *s == '"') {
+ while (*s) {
+ s[0] = s[1];
+ s++;
+ }
+ /* start over */
+ s = line;
+ continue;
+ }
+ s++;
+ }
+ /* anything left? */
+ if (*line == '\0')
+ continue;
+ fprintf(ofp, "\"%s\\0\"\n", line);
+ }
+ }
+ fprintf(ofp, "\"\\0\"\n};\n");
+ if (ifp)
+ fclose(ifp);
+ fclose(ofp);
+ moveifchanged(path("env.c.new"), path("env.c"));
+}
+
+static void
+read_file(char *fname)
+{
+ char ifname[MAXPATHLEN];
+ FILE *fp;
+ struct file_list *tp;
+ struct device *dp;
+ struct opt *op;
+ char *wd, *this, *compilewith, *depends, *clean, *warning;
+ int compile, match, nreqs, std, filetype,
+ imp_rule, no_obj, before_depend, mandatory, nowerror;
+
+ fp = fopen(fname, "r");
+ if (fp == 0)
+ err(1, "%s", fname);
+next:
+ /*
+ * include "filename"
+ * filename [ standard | mandatory | optional ]
+ * [ dev* [ | dev* ... ] | profiling-routine ] [ no-obj ]
+ * [ compile-with "compile rule" [no-implicit-rule] ]
+ * [ dependency "dependency-list"] [ before-depend ]
+ * [ clean "file-list"] [ warning "text warning" ]
+ */
+ wd = get_word(fp);
+ if (wd == (char *)EOF) {
+ (void) fclose(fp);
+ return;
+ }
+ if (wd == 0)
+ goto next;
+ if (wd[0] == '#')
+ {
+ while (((wd = get_word(fp)) != (char *)EOF) && wd)
+ ;
+ goto next;
+ }
+ if (eq(wd, "include")) {
+ next_quoted_word(fp, wd);
+ if (wd == 0) {
+ printf("%s: missing include filename.\n", fname);
+ exit(1);
+ }
+ (void) snprintf(ifname, sizeof(ifname), "../../%s", wd);
+ read_file(ifname);
+ while (((wd = get_word(fp)) != (char *)EOF) && wd)
+ ;
+ goto next;
+ }
+ this = ns(wd);
+ next_word(fp, wd);
+ if (wd == 0) {
+ printf("%s: No type for %s.\n",
+ fname, this);
+ exit(1);
+ }
+ tp = fl_lookup(this);
+ compile = 0;
+ match = 1;
+ nreqs = 0;
+ compilewith = 0;
+ depends = 0;
+ clean = 0;
+ warning = 0;
+ std = mandatory = 0;
+ imp_rule = 0;
+ no_obj = 0;
+ before_depend = 0;
+ nowerror = 0;
+ filetype = NORMAL;
+ if (eq(wd, "standard")) {
+ std = 1;
+ /*
+ * If an entry is marked "mandatory", config will abort if it's
+ * not called by a configuration line in the config file. Apart
+ * from this, the device is handled like one marked "optional".
+ */
+ } else if (eq(wd, "mandatory")) {
+ mandatory = 1;
+ } else if (!eq(wd, "optional")) {
+ printf("%s: %s must be optional, mandatory or standard\n",
+ fname, this);
+ exit(1);
+ }
+nextparam:
+ next_word(fp, wd);
+ if (wd == 0) {
+ compile += match;
+ if (compile && tp == NULL)
+ goto doneparam;
+ goto next;
+ }
+ if (eq(wd, "|")) {
+ if (nreqs == 0) {
+ printf("%s: syntax error describing %s\n",
+ fname, this);
+ exit(1);
+ }
+ compile += match;
+ match = 1;
+ nreqs = 0;
+ goto nextparam;
+ }
+ if (eq(wd, "no-obj")) {
+ no_obj++;
+ goto nextparam;
+ }
+ if (eq(wd, "no-implicit-rule")) {
+ if (compilewith == 0) {
+ printf("%s: alternate rule required when "
+ "\"no-implicit-rule\" is specified.\n",
+ fname);
+ }
+ imp_rule++;
+ goto nextparam;
+ }
+ if (eq(wd, "before-depend")) {
+ before_depend++;
+ goto nextparam;
+ }
+ if (eq(wd, "dependency")) {
+ next_quoted_word(fp, wd);
+ if (wd == 0) {
+ printf("%s: %s missing compile command string.\n",
+ fname, this);
+ exit(1);
+ }
+ depends = ns(wd);
+ goto nextparam;
+ }
+ if (eq(wd, "clean")) {
+ next_quoted_word(fp, wd);
+ if (wd == 0) {
+ printf("%s: %s missing clean file list.\n",
+ fname, this);
+ exit(1);
+ }
+ clean = ns(wd);
+ goto nextparam;
+ }
+ if (eq(wd, "compile-with")) {
+ next_quoted_word(fp, wd);
+ if (wd == 0) {
+ printf("%s: %s missing compile command string.\n",
+ fname, this);
+ exit(1);
+ }
+ compilewith = ns(wd);
+ goto nextparam;
+ }
+ if (eq(wd, "warning")) {
+ next_quoted_word(fp, wd);
+ if (wd == 0) {
+ printf("%s: %s missing warning text string.\n",
+ fname, this);
+ exit(1);
+ }
+ warning = ns(wd);
+ goto nextparam;
+ }
+ nreqs++;
+ if (eq(wd, "local")) {
+ filetype = LOCAL;
+ goto nextparam;
+ }
+ if (eq(wd, "no-depend")) {
+ filetype = NODEPEND;
+ goto nextparam;
+ }
+ if (eq(wd, "profiling-routine")) {
+ filetype = PROFILING;
+ goto nextparam;
+ }
+ if (eq(wd, "nowerror")) {
+ nowerror = 1;
+ goto nextparam;
+ }
+ STAILQ_FOREACH(dp, &dtab, d_next)
+ if (eq(dp->d_name, wd)) {
+ dp->d_done |= DEVDONE;
+ goto nextparam;
+ }
+ if (mandatory) {
+ printf("%s: mandatory device \"%s\" not found\n",
+ fname, wd);
+ exit(1);
+ }
+ if (std) {
+ printf("standard entry %s has a device keyword - %s!\n",
+ this, wd);
+ exit(1);
+ }
+ SLIST_FOREACH(op, &opt, op_next)
+ if (op->op_value == 0 && opteq(op->op_name, wd))
+ goto nextparam;
+ match = 0;
+ goto nextparam;
+
+doneparam:
+ if (std == 0 && nreqs == 0) {
+ printf("%s: what is %s optional on?\n",
+ fname, this);
+ exit(1);
+ }
+
+ if (wd) {
+ printf("%s: syntax error describing %s\n",
+ fname, this);
+ exit(1);
+ }
+ if (filetype == PROFILING && profiling == 0)
+ goto next;
+ tp = new_fent();
+ tp->f_fn = this;
+ tp->f_type = filetype;
+ if (imp_rule)
+ tp->f_flags |= NO_IMPLCT_RULE;
+ if (no_obj)
+ tp->f_flags |= NO_OBJ;
+ if (before_depend)
+ tp->f_flags |= BEFORE_DEPEND;
+ if (nowerror)
+ tp->f_flags |= NOWERROR;
+ tp->f_compilewith = compilewith;
+ tp->f_depends = depends;
+ tp->f_clean = clean;
+ tp->f_warn = warning;
+ goto next;
+}
+
+/*
+ * Read in the information about files used in making the system.
+ * Store it in the ftab linked list.
+ */
+static void
+read_files(void)
+{
+ char fname[MAXPATHLEN];
+ struct files_name *nl, *tnl;
+
+ (void) snprintf(fname, sizeof(fname), "../../conf/files");
+ read_file(fname);
+ (void) snprintf(fname, sizeof(fname),
+ "../../conf/files.%s", machinename);
+ read_file(fname);
+ for (nl = STAILQ_FIRST(&fntab); nl != NULL; nl = tnl) {
+ read_file(nl->f_name);
+ tnl = STAILQ_NEXT(nl, f_next);
+ free(nl->f_name);
+ free(nl);
+ }
+}
+
+static int
+opteq(const char *cp, const char *dp)
+{
+ char c, d;
+
+ for (; ; cp++, dp++) {
+ if (*cp != *dp) {
+ c = isupper(*cp) ? tolower(*cp) : *cp;
+ d = isupper(*dp) ? tolower(*dp) : *dp;
+ if (c != d)
+ return (0);
+ }
+ if (*cp == 0)
+ return (1);
+ }
+}
+
+static void
+do_before_depend(FILE *fp)
+{
+ struct file_list *tp;
+ int lpos, len;
+
+ fputs("BEFORE_DEPEND=", fp);
+ lpos = 15;
+ STAILQ_FOREACH(tp, &ftab, f_next)
+ if (tp->f_flags & BEFORE_DEPEND) {
+ len = strlen(tp->f_fn);
+ if ((len = 3 + len) + lpos > 72) {
+ lpos = 8;
+ fputs("\\\n\t", fp);
+ }
+ if (tp->f_flags & NO_IMPLCT_RULE)
+ fprintf(fp, "%s ", tp->f_fn);
+ else
+ fprintf(fp, "$S/%s ", tp->f_fn);
+ lpos += len + 1;
+ }
+ if (lpos != 8)
+ putc('\n', fp);
+}
+
+static void
+do_objs(FILE *fp)
+{
+ struct file_list *tp;
+ int lpos, len;
+ char *cp, och, *sp;
+
+ fprintf(fp, "OBJS=");
+ lpos = 6;
+ STAILQ_FOREACH(tp, &ftab, f_next) {
+ if (tp->f_flags & NO_OBJ)
+ continue;
+ sp = tail(tp->f_fn);
+ cp = sp + (len = strlen(sp)) - 1;
+ och = *cp;
+ *cp = 'o';
+ if (len + lpos > 72) {
+ lpos = 8;
+ fprintf(fp, "\\\n\t");
+ }
+ fprintf(fp, "%s ", sp);
+ lpos += len + 1;
+ *cp = och;
+ }
+ if (lpos != 8)
+ putc('\n', fp);
+}
+
+static void
+do_xxfiles(char *tag, FILE *fp)
+{
+ struct file_list *tp;
+ int lpos, len, slen;
+ char *suff, *SUFF;
+
+ if (tag[strlen(tag) - 1] == '\n')
+ tag[strlen(tag) - 1] = '\0';
+
+ suff = ns(tag + 7);
+ SUFF = ns(suff);
+ raisestr(SUFF);
+ slen = strlen(suff);
+
+ fprintf(fp, "%sFILES=", SUFF);
+ lpos = 8;
+ STAILQ_FOREACH(tp, &ftab, f_next)
+ if (tp->f_type != NODEPEND) {
+ len = strlen(tp->f_fn);
+ if (tp->f_fn[len - slen - 1] != '.')
+ continue;
+ if (strcasecmp(&tp->f_fn[len - slen], suff) != 0)
+ continue;
+ if ((len = 3 + len) + lpos > 72) {
+ lpos = 8;
+ fputs("\\\n\t", fp);
+ }
+ if (tp->f_type != LOCAL)
+ fprintf(fp, "$S/%s ", tp->f_fn);
+ else
+ fprintf(fp, "%s ", tp->f_fn);
+ lpos += len + 1;
+ }
+ if (lpos != 8)
+ putc('\n', fp);
+}
+
+static char *
+tail(char *fn)
+{
+ char *cp;
+
+ cp = rindex(fn, '/');
+ if (cp == 0)
+ return (fn);
+ return (cp+1);
+}
+
+/*
+ * Create the makerules for each file
+ * which is part of the system.
+ */
+static void
+do_rules(FILE *f)
+{
+ char *cp, *np, och;
+ struct file_list *ftp;
+ char *compilewith;
+
+ STAILQ_FOREACH(ftp, &ftab, f_next) {
+ if (ftp->f_warn)
+ printf("WARNING: %s\n", ftp->f_warn);
+ cp = (np = ftp->f_fn) + strlen(ftp->f_fn) - 1;
+ och = *cp;
+ if (ftp->f_flags & NO_IMPLCT_RULE) {
+ if (ftp->f_depends)
+ fprintf(f, "%s: %s\n", np, ftp->f_depends);
+ else
+ fprintf(f, "%s: \n", np);
+ }
+ else {
+ *cp = '\0';
+ if (och == 'o') {
+ fprintf(f, "%so:\n\t-cp $S/%so .\n\n",
+ tail(np), np);
+ continue;
+ }
+ if (ftp->f_depends) {
+ fprintf(f, "%sln: $S/%s%c %s\n", tail(np),
+ np, och, ftp->f_depends);
+ fprintf(f, "\t${NORMAL_LINT}\n\n");
+ fprintf(f, "%so: $S/%s%c %s\n", tail(np),
+ np, och, ftp->f_depends);
+ }
+ else {
+ fprintf(f, "%sln: $S/%s%c\n", tail(np),
+ np, och);
+ fprintf(f, "\t${NORMAL_LINT}\n\n");
+ fprintf(f, "%so: $S/%s%c\n", tail(np),
+ np, och);
+ }
+ }
+ compilewith = ftp->f_compilewith;
+ if (compilewith == 0) {
+ const char *ftype = NULL;
+ static char cmd[128];
+
+ switch (ftp->f_type) {
+
+ case NORMAL:
+ ftype = "NORMAL";
+ break;
+
+ case PROFILING:
+ if (!profiling)
+ continue;
+ ftype = "PROFILE";
+ break;
+
+ default:
+ printf("config: don't know rules for %s\n", np);
+ break;
+ }
+ snprintf(cmd, sizeof(cmd), "${%s_%c%s}\n.if defined(NORMAL_CTFCONVERT) && !empty(NORMAL_CTFCONVERT)\n\t${NORMAL_CTFCONVERT}\n.endif", ftype,
+ toupper(och),
+ ftp->f_flags & NOWERROR ? "_NOWERROR" : "");
+ compilewith = cmd;
+ }
+ *cp = och;
+ fprintf(f, "\t%s\n\n", compilewith);
+ }
+}
+
+static void
+do_clean(FILE *fp)
+{
+ struct file_list *tp;
+ int lpos, len;
+
+ fputs("CLEAN=", fp);
+ lpos = 7;
+ STAILQ_FOREACH(tp, &ftab, f_next)
+ if (tp->f_clean) {
+ len = strlen(tp->f_clean);
+ if (len + lpos > 72) {
+ lpos = 8;
+ fputs("\\\n\t", fp);
+ }
+ fprintf(fp, "%s ", tp->f_clean);
+ lpos += len + 1;
+ }
+ if (lpos != 8)
+ putc('\n', fp);
+}
+
+char *
+raisestr(char *str)
+{
+ char *cp = str;
+
+ while (*str) {
+ if (islower(*str))
+ *str = toupper(*str);
+ str++;
+ }
+ return (cp);
+}
diff --git a/usr.sbin/config/mkoptions.c b/usr.sbin/config/mkoptions.c
new file mode 100644
index 0000000..c4bd624
--- /dev/null
+++ b/usr.sbin/config/mkoptions.c
@@ -0,0 +1,357 @@
+/*
+ * Copyright (c) 1995 Peter Wemm
+ * 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[] = "@(#)mkheaders.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * Make all the .h files for the optional entries
+ */
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/param.h>
+#include "config.h"
+#include "y.tab.h"
+
+static struct users {
+ int u_default;
+ int u_min;
+ int u_max;
+} users = { 8, 2, 512 };
+
+static char *lower(char *);
+static void read_options(void);
+static void do_option(char *);
+static char *tooption(char *);
+
+void
+options(void)
+{
+ char buf[40];
+ struct cputype *cp;
+ struct opt_list *ol;
+ struct opt *op;
+
+ /* Fake the cpu types as options. */
+ SLIST_FOREACH(cp, &cputype, cpu_next) {
+ op = (struct opt *)calloc(1, sizeof(*op));
+ op->op_name = ns(cp->cpu_name);
+ SLIST_INSERT_HEAD(&opt, op, op_next);
+ }
+
+ if (maxusers == 0) {
+ /* printf("maxusers not specified; will auto-size\n"); */
+ } else if (maxusers < users.u_min) {
+ printf("minimum of %d maxusers assumed\n", users.u_min);
+ maxusers = users.u_min;
+ } else if (maxusers > users.u_max)
+ printf("warning: maxusers > %d (%d)\n", users.u_max, maxusers);
+
+ /* Fake MAXUSERS as an option. */
+ op = (struct opt *)calloc(1, sizeof(*op));
+ op->op_name = ns("MAXUSERS");
+ snprintf(buf, sizeof(buf), "%d", maxusers);
+ op->op_value = ns(buf);
+ SLIST_INSERT_HEAD(&opt, op, op_next);
+
+ read_options();
+ SLIST_FOREACH(ol, &otab, o_next)
+ do_option(ol->o_name);
+ SLIST_FOREACH(op, &opt, op_next) {
+ if (!op->op_ownfile && strncmp(op->op_name, "DEV_", 4)) {
+ printf("%s: unknown option \"%s\"\n",
+ PREFIX, op->op_name);
+ exit(1);
+ }
+ }
+}
+
+/*
+ * Generate an <options>.h file
+ */
+
+static void
+do_option(char *name)
+{
+ char *file, *inw;
+ const char *basefile;
+ struct opt_list *ol;
+ struct opt *op;
+ struct opt_head op_head;
+ FILE *inf, *outf;
+ char *value;
+ char *oldvalue;
+ int seen;
+ int tidy;
+
+ file = tooption(name);
+
+ /*
+ * Check to see if the option was specified..
+ */
+ value = NULL;
+ SLIST_FOREACH(op, &opt, op_next) {
+ if (eq(name, op->op_name)) {
+ oldvalue = value;
+ value = op->op_value;
+ if (value == NULL)
+ value = ns("1");
+ if (oldvalue != NULL && !eq(value, oldvalue))
+ printf(
+ "%s: option \"%s\" redefined from %s to %s\n",
+ PREFIX, op->op_name, oldvalue,
+ value);
+ op->op_ownfile++;
+ }
+ }
+
+ remember(file);
+ inf = fopen(file, "r");
+ if (inf == 0) {
+ outf = fopen(file, "w");
+ if (outf == 0)
+ err(1, "%s", file);
+
+ /* was the option in the config file? */
+ if (value) {
+ fprintf(outf, "#define %s %s\n", name, value);
+ } /* else empty file */
+
+ (void) fclose(outf);
+ return;
+ }
+ basefile = "";
+ SLIST_FOREACH(ol, &otab, o_next)
+ if (eq(name, ol->o_name)) {
+ basefile = ol->o_file;
+ break;
+ }
+ oldvalue = NULL;
+ SLIST_INIT(&op_head);
+ seen = 0;
+ tidy = 0;
+ for (;;) {
+ char *cp;
+ char *invalue;
+
+ /* get the #define */
+ if ((inw = get_word(inf)) == 0 || inw == (char *)EOF)
+ break;
+ /* get the option name */
+ if ((inw = get_word(inf)) == 0 || inw == (char *)EOF)
+ break;
+ inw = ns(inw);
+ /* get the option value */
+ if ((cp = get_word(inf)) == 0 || cp == (char *)EOF)
+ break;
+ /* option value */
+ invalue = ns(cp); /* malloced */
+ if (eq(inw, name)) {
+ oldvalue = invalue;
+ invalue = value;
+ seen++;
+ }
+ SLIST_FOREACH(ol, &otab, o_next)
+ if (eq(inw, ol->o_name))
+ break;
+ if (!eq(inw, name) && !ol) {
+ printf("WARNING: unknown option `%s' removed from %s\n",
+ inw, file);
+ tidy++;
+ } else if (ol != NULL && !eq(basefile, ol->o_file)) {
+ printf("WARNING: option `%s' moved from %s to %s\n",
+ inw, basefile, ol->o_file);
+ tidy++;
+ } else {
+ op = (struct opt *) calloc(1, sizeof *op);
+ op->op_name = inw;
+ op->op_value = invalue;
+ SLIST_INSERT_HEAD(&op_head, op, op_next);
+ }
+
+ /* EOL? */
+ cp = get_word(inf);
+ if (cp == (char *)EOF)
+ break;
+ }
+ (void) fclose(inf);
+ if (!tidy && ((value == NULL && oldvalue == NULL) ||
+ (value && oldvalue && eq(value, oldvalue)))) {
+ while (!SLIST_EMPTY(&op_head)) {
+ op = SLIST_FIRST(&op_head);
+ SLIST_REMOVE_HEAD(&op_head, op_next);
+ free(op->op_name);
+ free(op->op_value);
+ free(op);
+ }
+ return;
+ }
+
+ if (value && !seen) {
+ /* New option appears */
+ op = (struct opt *) calloc(1, sizeof *op);
+ op->op_name = ns(name);
+ op->op_value = value ? ns(value) : NULL;
+ SLIST_INSERT_HEAD(&op_head, op, op_next);
+ }
+
+ outf = fopen(file, "w");
+ if (outf == 0)
+ err(1, "%s", file);
+ while (!SLIST_EMPTY(&op_head)) {
+ op = SLIST_FIRST(&op_head);
+ /* was the option in the config file? */
+ if (op->op_value) {
+ fprintf(outf, "#define %s %s\n",
+ op->op_name, op->op_value);
+ }
+ SLIST_REMOVE_HEAD(&op_head, op_next);
+ free(op->op_name);
+ free(op->op_value);
+ free(op);
+ }
+ (void) fclose(outf);
+}
+
+/*
+ * Find the filename to store the option spec into.
+ */
+static char *
+tooption(char *name)
+{
+ static char hbuf[MAXPATHLEN];
+ char nbuf[MAXPATHLEN];
+ struct opt_list *po;
+
+ /* "cannot happen"? the otab list should be complete.. */
+ (void) strlcpy(nbuf, "options.h", sizeof(nbuf));
+
+ SLIST_FOREACH(po, &otab, o_next) {
+ if (eq(po->o_name, name)) {
+ strlcpy(nbuf, po->o_file, sizeof(nbuf));
+ break;
+ }
+ }
+
+ (void) strlcpy(hbuf, path(nbuf), sizeof(hbuf));
+ return (hbuf);
+}
+
+/*
+ * read the options and options.<machine> files
+ */
+static void
+read_options(void)
+{
+ FILE *fp;
+ char fname[MAXPATHLEN];
+ char *wd, *this, *val;
+ struct opt_list *po;
+ int first = 1;
+ char genopt[MAXPATHLEN];
+
+ SLIST_INIT(&otab);
+ (void) snprintf(fname, sizeof(fname), "../../conf/options");
+openit:
+ fp = fopen(fname, "r");
+ if (fp == 0) {
+ return;
+ }
+next:
+ wd = get_word(fp);
+ if (wd == (char *)EOF) {
+ (void) fclose(fp);
+ if (first == 1) {
+ first++;
+ (void) snprintf(fname, sizeof fname, "../../conf/options.%s", machinename);
+ fp = fopen(fname, "r");
+ if (fp != 0)
+ goto next;
+ (void) snprintf(fname, sizeof fname, "options.%s", machinename);
+ goto openit;
+ }
+ return;
+ }
+ if (wd == 0)
+ goto next;
+ if (wd[0] == '#')
+ {
+ while (((wd = get_word(fp)) != (char *)EOF) && wd)
+ ;
+ goto next;
+ }
+ this = ns(wd);
+ val = get_word(fp);
+ if (val == (char *)EOF)
+ return;
+ if (val == 0) {
+ char *s = ns(this);
+ (void) snprintf(genopt, sizeof(genopt), "opt_%s.h", lower(s));
+ val = genopt;
+ free(s);
+ }
+ val = ns(val);
+
+ SLIST_FOREACH(po, &otab, o_next) {
+ if (eq(po->o_name, this)) {
+ printf("%s: Duplicate option %s.\n",
+ fname, this);
+ exit(1);
+ }
+ }
+
+ po = (struct opt_list *) calloc(1, sizeof *po);
+ po->o_name = this;
+ po->o_file = val;
+ SLIST_INSERT_HEAD(&otab, po, o_next);
+
+ goto next;
+}
+
+static char *
+lower(char *str)
+{
+ char *cp = str;
+
+ while (*str) {
+ if (isupper(*str))
+ *str = tolower(*str);
+ str++;
+ }
+ return (cp);
+}
diff --git a/usr.sbin/cpucontrol/Makefile b/usr.sbin/cpucontrol/Makefile
new file mode 100644
index 0000000..00e7214
--- /dev/null
+++ b/usr.sbin/cpucontrol/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= cpucontrol
+MAN= cpucontrol.8
+SRCS= cpucontrol.c intel.c amd.c
+
+WARNS?= 6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/cpucontrol/amd.c b/usr.sbin/cpucontrol/amd.c
new file mode 100644
index 0000000..bd36c9c
--- /dev/null
+++ b/usr.sbin/cpucontrol/amd.c
@@ -0,0 +1,183 @@
+/*-
+ * Copyright (c) 2006, 2008 Stanislav Sedov <stas@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <err.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/ioccom.h>
+#include <sys/cpuctl.h>
+
+#include <machine/cpufunc.h>
+#include <machine/specialreg.h>
+
+#include "cpucontrol.h"
+#include "amd.h"
+
+int
+amd_probe(int fd)
+{
+ char vendor[13];
+ int error;
+ cpuctl_cpuid_args_t idargs = {
+ .level = 0,
+ };
+
+ error = ioctl(fd, CPUCTL_CPUID, &idargs);
+ if (error < 0) {
+ WARN(0, "ioctl()");
+ return (1);
+ }
+ ((uint32_t *)vendor)[0] = idargs.data[1];
+ ((uint32_t *)vendor)[1] = idargs.data[3];
+ ((uint32_t *)vendor)[2] = idargs.data[2];
+ vendor[12] = '\0';
+ if (strncmp(vendor, AMD_VENDOR_ID, sizeof(AMD_VENDOR_ID)) != 0)
+ return (1);
+ return (0);
+}
+
+void
+amd_update(const char *dev, const char *path)
+{
+ int fd, devfd;
+ unsigned int i;
+ struct stat st;
+ uint32_t *fw_image;
+ amd_fw_header_t *fw_header;
+ uint32_t sum;
+ uint32_t signature;
+ uint32_t *fw_data;
+ size_t fw_size;
+ cpuctl_cpuid_args_t idargs = {
+ .level = 1, /* Request signature. */
+ };
+ cpuctl_update_args_t args;
+ int error;
+
+ assert(path);
+ assert(dev);
+
+ fd = -1;
+ devfd = -1;
+ fw_image = MAP_FAILED;
+ error = 0;
+ devfd = open(dev, O_RDWR);
+ if (devfd < 0) {
+ WARN(0, "could not open %s for writing", dev);
+ return;
+ }
+ error = ioctl(devfd, CPUCTL_CPUID, &idargs);
+ if (error < 0) {
+ WARN(0, "ioctl()");
+ goto fail;
+ }
+ signature = idargs.data[0];
+ WARNX(2, "found cpu family %#x model %#x "
+ "stepping %#x extfamily %#x extmodel %#x.",
+ (signature >> 8) & 0x0f, (signature >> 4) & 0x0f,
+ (signature >> 0) & 0x0f, (signature >> 20) & 0xff,
+ (signature >> 16) & 0x0f);
+
+ /*
+ * Open the firmware file.
+ */
+ fd = open(path, O_RDONLY, 0);
+ if (fd < 0) {
+ WARN(0, "open(%s)", path);
+ goto fail;
+ }
+ error = fstat(fd, &st);
+ if (error != 0) {
+ WARN(0, "fstat(%s)", path);
+ goto fail;
+ }
+ if (st.st_size < 0 || (unsigned)st.st_size < sizeof(*fw_header)) {
+ WARNX(2, "file too short: %s", path);
+ goto fail;
+ }
+ /*
+ * mmap the whole image.
+ */
+ fw_image = (uint32_t *)mmap(NULL, st.st_size, PROT_READ,
+ MAP_PRIVATE, fd, 0);
+ if (fw_image == MAP_FAILED) {
+ WARN(0, "mmap(%s)", path);
+ goto fail;
+ }
+ fw_header = (amd_fw_header_t *)fw_image;
+ if ((fw_header->magic >> 8) != AMD_MAGIC) {
+ WARNX(2, "%s is not a valid amd firmware: version mismatch",
+ path);
+ goto fail;
+ }
+ fw_data = (uint32_t *)(fw_header + 1);
+ fw_size = (st.st_size - sizeof(*fw_header)) / sizeof(uint32_t);
+
+ /*
+ * Check the primary checksum.
+ */
+ sum = 0;
+ for (i = 0; i < fw_size; i++)
+ sum += fw_data[i];
+ if (sum != fw_header->checksum) {
+ WARNX(2, "%s: update data checksum invalid", path);
+ goto fail;
+ }
+ if (signature == fw_header->signature) {
+ fprintf(stderr, "%s: updating cpu %s... ", path, dev);
+
+ args.data = fw_image;
+ args.size = st.st_size;
+ error = ioctl(fd, CPUCTL_UPDATE, &args);
+ if (error < 0) {
+ fprintf(stderr, "failed.\n");
+ warn("ioctl()");
+ goto fail;
+ }
+ fprintf(stderr, "done.\n");
+ }
+
+fail:
+ if (fd >= 0)
+ close(fd);
+ if (devfd >= 0)
+ close(devfd);
+ if (fw_image != MAP_FAILED)
+ if(munmap(fw_image, st.st_size) != 0)
+ warn("munmap(%s)", path);
+ return;
+}
diff --git a/usr.sbin/cpucontrol/amd.h b/usr.sbin/cpucontrol/amd.h
new file mode 100644
index 0000000..cf109c2
--- /dev/null
+++ b/usr.sbin/cpucontrol/amd.h
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 2006, 2008 Stanislav Sedov <stas@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef AMD_H
+#define AMD_H
+
+/*
+ * Prototypes.
+ */
+ucode_probe_t amd_probe;
+ucode_update_t amd_update;
+
+typedef struct amd_fw_header {
+ uint32_t date; /* Update creation date. */
+ uint32_t xz0[2];
+ uint32_t checksum; /* ucode checksum. */
+ uint32_t xz1[2];
+ uint32_t signature; /* Low byte of cpuid(0). */
+ uint32_t magic; /* 0x0Xaaaaaa */
+ uint32_t xz2[8];
+} amd_fw_header_t;
+
+#define AMD_MAGIC 0xaaaaaa
+
+#endif /* !AMD_H */
diff --git a/usr.sbin/cpucontrol/cpucontrol.8 b/usr.sbin/cpucontrol/cpucontrol.8
new file mode 100644
index 0000000..b049f14
--- /dev/null
+++ b/usr.sbin/cpucontrol/cpucontrol.8
@@ -0,0 +1,123 @@
+.\" Copyright (c) 2006, 2008 Stanislav Sedov <stas@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 4, 2008
+.Dt CPUCONTROL 8
+.Os
+.Sh NAME
+.Nm cpucontrol
+.Nd control utility for the
+.Xr cpuctl 4
+device.
+.Sh SYNOPSIS
+.Nm
+.Op Fl vh
+.Fl m Ar msr Ns Op = Ns Ar value
+.Bk
+.Ar device
+.Ek
+.Nm
+.Op Fl vh
+.Fl i Ar level
+.Bk
+.Ar device
+.Ek
+.Nm
+.Op Fl vh
+.Op Fl d Ar datadir
+.Fl u
+.Bk
+.Ar device
+.Ek
+.Sh DESCRIPTION
+The
+.Nm
+utility can be used to read and write arbitrary machine-specific
+CPU registers via the
+.Xr cpuctl 4
+special device.
+It can also be used to apply CPU firmware updates.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d Ar datadir
+Where to look for microcode images.
+The option can be specified multiple times.
+.It Fl m Ar msr Ns Op = Ns Ar value
+Read/write the specified MSR.
+Both the MSR and the value should be given as a hex number.
+.It Fl i Ar level
+Retrieve CPUID info.
+Level should be given as a hex number.
+.It Fl u
+Apply CPU firmware updates.
+The
+.Nm
+utility will walk through the configured data directories
+and apply all firmware updates available for this CPU.
+.It Fl v
+Increase the verbosity level.
+.It Fl h
+Show help message.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+The command
+.Pp
+.Dq Li "cpucontrol -m 0x10 /dev/cpuctl0"
+.Pp
+will read the contents of TSC MSR from CPU 0.
+.Pp
+To set the CPU 0 TSC MSR register value to 0x1 issue
+.Pp
+.Dq Li "cpucontrol -m 0x10=0x1 /dev/cpuctl0"
+.Pp
+The command
+.Pp
+.Dq Li "cpucontrol -i 0x1 /dev/cpuctl1"
+.Pp
+will retrieve the CPUID level 0x1 from CPU 1.
+.Pp
+To perform firmware updates on CPU 0 from images located at
+.Pa /usr/local/share/cpuctl/
+use the following command:
+.Pp
+.Dq Li "cpucontrol -d /usr/local/share/cpuctl/ -u /dev/cpuctl0"
+.Sh SEE ALSO
+.Xr cpuctl 4
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 8.0 .
+.Sh BUGS
+Yes, probably, report if any.
+.Sh AUTHORS
+The
+.Nm
+utility and this manual page was written by
+.An Stanislav Sedov Aq stas@FreeBSD.org .
diff --git a/usr.sbin/cpucontrol/cpucontrol.c b/usr.sbin/cpucontrol/cpucontrol.c
new file mode 100644
index 0000000..5469ee2
--- /dev/null
+++ b/usr.sbin/cpucontrol/cpucontrol.c
@@ -0,0 +1,362 @@
+/*-
+ * Copyright (c) 2008 Stanislav Sedov <stas@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This utility provides userland access to the cpuctl(4) pseudo-device
+ * features.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <err.h>
+#include <sysexits.h>
+#include <dirent.h>
+
+#include <sys/queue.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/cpuctl.h>
+
+#include "cpucontrol.h"
+#include "amd.h"
+#include "intel.h"
+
+int verbosity_level = 0;
+
+#define DEFAULT_DATADIR "/usr/local/share/cpucontrol"
+
+#define FLAG_I 0x01
+#define FLAG_M 0x02
+#define FLAG_U 0x04
+
+#define HIGH(val) (uint32_t)(((val) >> 32) & 0xffffffff)
+#define LOW(val) (uint32_t)((val) & 0xffffffff)
+
+/*
+ * Macros for freeing SLISTs, probably must be in /sys/queue.h
+ */
+#define SLIST_FREE(head, field, freef) do { \
+ typeof(SLIST_FIRST(head)) __elm0; \
+ typeof(SLIST_FIRST(head)) __elm; \
+ SLIST_FOREACH_SAFE(__elm, (head), field, __elm0) \
+ (void)(freef)(__elm); \
+} while(0);
+
+struct datadir {
+ const char *path;
+ SLIST_ENTRY(datadir) next;
+};
+static SLIST_HEAD(, datadir) datadirs = SLIST_HEAD_INITIALIZER(&datadirs);
+
+struct ucode_handler {
+ ucode_probe_t *probe;
+ ucode_update_t *update;
+} handlers[] = {
+ { intel_probe, intel_update },
+ { amd_probe, amd_update },
+};
+#define NHANDLERS (sizeof(handlers) / sizeof(*handlers))
+
+static void usage(void);
+static int isdir(const char *path);
+static int do_cpuid(const char *cmdarg, const char *dev);
+static int do_msr(const char *cmdarg, const char *dev);
+static int do_update(const char *dev);
+static void datadir_add(const char *path);
+
+static void __dead2
+usage()
+{
+ const char *name;
+
+ name = getprogname();
+ if (name == NULL)
+ name = "cpuctl";
+ fprintf(stderr, "Usage: %s [-vh] [-d datadir] [-m msr[=value] | "
+ "-i level | -u] device\n", name);
+ exit(EX_USAGE);
+}
+
+static int
+isdir(const char *path)
+{
+ int error;
+ struct stat st;
+
+ error = stat(path, &st);
+ if (error < 0) {
+ WARN(0, "stat(%s)", path);
+ return (error);
+ }
+ return (st.st_mode & S_IFDIR);
+}
+
+static int
+do_cpuid(const char *cmdarg, const char *dev)
+{
+ unsigned int level;
+ cpuctl_cpuid_args_t args;
+ int fd, error;
+ char *endptr;
+
+ assert(cmdarg != NULL);
+ assert(dev != NULL);
+
+ level = strtoul(cmdarg, &endptr, 16);
+ if (*cmdarg == '\0' || *endptr != '\0') {
+ WARNX(0, "incorrect operand: %s", cmdarg);
+ usage();
+ /* NOTREACHED */
+ }
+
+ /*
+ * Fill ioctl argument structure.
+ */
+ args.level = level;
+ fd = open(dev, O_RDONLY);
+ if (fd < 0) {
+ WARN(0, "error opening %s for reading", dev);
+ return (1);
+ }
+ error = ioctl(fd, CPUCTL_CPUID, &args);
+ if (error < 0) {
+ WARN(0, "ioctl(%s, CPUCTL_CPUID)", dev);
+ close(fd);
+ return (error);
+ }
+ fprintf(stdout, "cpuid level 0x%x: 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n",
+ level, args.data[0], args.data[1], args.data[2], args.data[3]);
+ close(fd);
+ return (0);
+}
+
+static int
+do_msr(const char *cmdarg, const char *dev)
+{
+ unsigned int msr;
+ cpuctl_msr_args_t args;
+ int fd, error;
+ int wr = 0;
+ char *p;
+ char *endptr;
+
+ assert(cmdarg != NULL);
+ assert(dev != NULL);
+
+ p = strchr(cmdarg, '=');
+ if (p != NULL) {
+ wr = 1;
+ *p++ = '\0';
+ args.data = strtoull(p, &endptr, 16);
+ if (*p == '\0' || *endptr != '\0') {
+ WARNX(0, "incorrect MSR value: %s", p);
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ msr = strtoul(cmdarg, &endptr, 16);
+ if (*cmdarg == '\0' || *endptr != '\0') {
+ WARNX(0, "incorrect MSR register: %s", cmdarg);
+ usage();
+ /* NOTREACHED */
+ }
+
+ /*
+ * Fill ioctl argument structure.
+ */
+ args.msr = msr;
+ fd = open(dev, wr == 0 ? O_RDONLY : O_WRONLY);
+ if (fd < 0) {
+ WARN(0, "error opening %s for %s", dev,
+ wr == 0 ? "reading" : "writing");
+ return (1);
+ }
+ error = ioctl(fd, wr == 0 ? CPUCTL_RDMSR : CPUCTL_WRMSR, &args);
+ if (error < 0) {
+ WARN(0, "ioctl(%s, %s)", dev,
+ wr == 0 ? "CPUCTL_RDMSR" : "CPUCTL_WRMSR");
+ close(fd);
+ return (1);
+ }
+ if (wr == 0)
+ fprintf(stdout, "MSR 0x%x: 0x%.8x 0x%.8x\n", msr,
+ HIGH(args.data), LOW(args.data));
+ close(fd);
+ return (0);
+}
+
+static int
+do_update(const char *dev)
+{
+ int fd;
+ unsigned int i;
+ int error;
+ struct ucode_handler *handler;
+ struct datadir *dir;
+ DIR *dirfd;
+ struct dirent *direntry;
+ char buf[MAXPATHLEN];
+
+ fd = open(dev, O_RDONLY);
+ if (fd < 0) {
+ WARN(0, "error opening %s for reading", dev);
+ return (1);
+ }
+
+ /*
+ * Find the appropriate handler for device.
+ */
+ for (i = 0; i < NHANDLERS; i++)
+ if (handlers[i].probe(fd) == 0)
+ break;
+ if (i < NHANDLERS)
+ handler = &handlers[i];
+ else {
+ WARNX(0, "cannot find the appropriate handler for device");
+ close(fd);
+ return (1);
+ }
+ close(fd);
+
+ /*
+ * Process every image in specified data directories.
+ */
+ SLIST_FOREACH(dir, &datadirs, next) {
+ dirfd = opendir(dir->path);
+ if (dirfd == NULL) {
+ WARNX(1, "skipping directory %s: not accessible", dir->path);
+ continue;
+ }
+ while ((direntry = readdir(dirfd)) != NULL) {
+ if (direntry->d_namlen == 0)
+ continue;
+ error = snprintf(buf, sizeof(buf), "%s/%s", dir->path,
+ direntry->d_name);
+ if ((unsigned)error >= sizeof(buf))
+ WARNX(0, "skipping %s, buffer too short",
+ direntry->d_name);
+ if (isdir(buf) != 0) {
+ WARNX(2, "skipping %s: is a directory", buf);
+ continue;
+ }
+ handler->update(dev, buf);
+ }
+ error = closedir(dirfd);
+ if (error != 0)
+ WARN(0, "closedir(%s)", dir->path);
+ }
+ return (0);
+}
+
+/*
+ * Add new data directory to the search list.
+ */
+static void
+datadir_add(const char *path)
+{
+ struct datadir *newdir;
+
+ newdir = (struct datadir *)malloc(sizeof(*newdir));
+ if (newdir == NULL)
+ err(EX_OSERR, "cannot allocate memory");
+ newdir->path = path;
+ SLIST_INSERT_HEAD(&datadirs, newdir, next);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c, flags;
+ const char *cmdarg;
+ const char *dev;
+ int error;
+
+ flags = 0;
+ error = 0;
+ cmdarg = ""; /* To keep gcc3 happy. */
+
+ /*
+ * Add all default data dirs to the list first.
+ */
+ datadir_add(DEFAULT_DATADIR);
+ while ((c = getopt(argc, argv, "d:hi:m:uv")) != -1) {
+ switch (c) {
+ case 'd':
+ datadir_add(optarg);
+ break;
+ case 'i':
+ flags |= FLAG_I;
+ cmdarg = optarg;
+ break;
+ case 'm':
+ flags |= FLAG_M;
+ cmdarg = optarg;
+ break;
+ case 'u':
+ flags |= FLAG_U;
+ break;
+ case 'v':
+ verbosity_level++;
+ break;
+ case 'h':
+ /* FALLTHROUGH */
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc < 1) {
+ usage();
+ /* NOTREACHED */
+ }
+ dev = argv[0];
+ c = flags & (FLAG_I | FLAG_M | FLAG_U);
+ switch (c) {
+ case FLAG_I:
+ error = do_cpuid(cmdarg, dev);
+ break;
+ case FLAG_M:
+ error = do_msr(cmdarg, dev);
+ break;
+ case FLAG_U:
+ error = do_update(dev);
+ break;
+ default:
+ usage(); /* Only one command can be selected. */
+ }
+ SLIST_FREE(&datadirs, next, free);
+ return (error);
+}
diff --git a/usr.sbin/cpucontrol/cpucontrol.h b/usr.sbin/cpucontrol/cpucontrol.h
new file mode 100644
index 0000000..63d3995
--- /dev/null
+++ b/usr.sbin/cpucontrol/cpucontrol.h
@@ -0,0 +1,56 @@
+/*-
+ * Copyright (c) 2008 Stanislav Sedov <stas@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef CPUCONTROL_H
+#define CPUCONTROL_H
+
+typedef int ucode_probe_t(int fd);
+typedef void ucode_update_t(const char *dev, const char *image);
+
+extern int verbosity_level;
+
+#ifdef DEBUG
+# define WARNX(level, ...) \
+ if ((level) <= verbosity_level) { \
+ fprintf(stderr, "%s:%d ", __FILE__, __LINE__); \
+ warnx(__VA_ARGS__); \
+ }
+# define WARN(level, ...) \
+ if ((level) <= verbosity_level) { \
+ fprintf(stderr, "%s:%d ", __FILE__, __LINE__); \
+ warn(__VA_ARGS__); \
+ }
+#else
+# define WARNX(level, ...) \
+ if ((level) <= verbosity_level) \
+ warnx(__VA_ARGS__);
+# define WARN(level, ...) \
+ if ((level) <= verbosity_level) \
+ warn(__VA_ARGS__);
+#endif
+
+#endif /* !CPUCONTROL_H */
diff --git a/usr.sbin/cpucontrol/intel.c b/usr.sbin/cpucontrol/intel.c
new file mode 100644
index 0000000..18c6e5e
--- /dev/null
+++ b/usr.sbin/cpucontrol/intel.c
@@ -0,0 +1,285 @@
+/*-
+ * Copyright (c) 2006, 2008 Stanislav Sedov <stas@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <err.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/ioccom.h>
+#include <sys/cpuctl.h>
+
+#include <machine/cpufunc.h>
+#include <machine/specialreg.h>
+
+#include "cpucontrol.h"
+#include "intel.h"
+
+#define DEFAULT_UCODE_SIZE 2000 /* Size of update data if not specified. */
+
+int
+intel_probe(int fd)
+{
+ char vendor[13];
+ int error;
+ cpuctl_cpuid_args_t idargs = {
+ .level = 0,
+ };
+
+ error = ioctl(fd, CPUCTL_CPUID, &idargs);
+ if (error < 0) {
+ WARN(0, "ioctl()");
+ return (1);
+ }
+ ((uint32_t *)vendor)[0] = idargs.data[1];
+ ((uint32_t *)vendor)[1] = idargs.data[3];
+ ((uint32_t *)vendor)[2] = idargs.data[2];
+ vendor[12] = '\0';
+ if (strncmp(vendor, INTEL_VENDOR_ID, sizeof(INTEL_VENDOR_ID)) != 0)
+ return (1);
+ return (0);
+}
+
+void
+intel_update(const char *dev, const char *path)
+{
+ int fd, devfd;
+ struct stat st;
+ uint32_t *fw_image;
+ int have_ext_table;
+ uint32_t sum;
+ unsigned int i;
+ size_t payload_size;
+ intel_fw_header_t *fw_header;
+ intel_cpu_signature_t *ext_table;
+ intel_ext_header_t *ext_header;
+ uint32_t signature, flags;
+ int32_t revision;
+ ssize_t ext_size;
+ size_t ext_table_size;
+ void *fw_data;
+ size_t data_size, total_size;
+ cpuctl_msr_args_t msrargs = {
+ .msr = MSR_IA32_PLATFORM_ID,
+ };
+ cpuctl_cpuid_args_t idargs = {
+ .level = 1, /* Signature. */
+ };
+ cpuctl_update_args_t args;
+ int error;
+
+ assert(path);
+ assert(dev);
+
+ fd = -1;
+ devfd = -1;
+ fw_image = MAP_FAILED;
+ ext_table = NULL;
+ ext_header = NULL;
+ devfd = open(dev, O_RDWR);
+ if (devfd < 0) {
+ WARN(0, "could not open %s for writing", dev);
+ return;
+ }
+ error = ioctl(devfd, CPUCTL_CPUID, &idargs);
+ if (error < 0) {
+ WARN(0, "ioctl(%s)", dev);
+ goto fail;
+ }
+ signature = idargs.data[0];
+ error = ioctl(devfd, CPUCTL_RDMSR, &msrargs);
+ if (error < 0) {
+ WARN(0, "ioctl(%s)", dev);
+ goto fail;
+ }
+
+ /*
+ * MSR_IA32_PLATFORM_ID contains flag in BCD in bits 52-50.
+ */
+ flags = 1 << ((msrargs.data >> 50) & 7);
+ msrargs.msr = MSR_BIOS_SIGN;
+ error = ioctl(devfd, CPUCTL_RDMSR, &msrargs);
+ if (error < 0) {
+ WARN(0, "ioctl(%s)", dev);
+ goto fail;
+ }
+ revision = msrargs.data >> 32; /* Revision in the high dword. */
+ WARNX(2, "found cpu type %#x family %#x model %#x stepping %#x.",
+ (signature >> 12) & 0x03, (signature >> 8) & 0x0f,
+ (signature >> 4) & 0x0f, (signature >> 0) & 0x0f);
+ /*
+ * Open firmware image.
+ */
+ fd = open(path, O_RDONLY, 0);
+ if (fd < 0) {
+ WARN(0, "open(%s)", path);
+ return;
+ }
+ error = fstat(fd, &st);
+ if (error != 0) {
+ WARN(0, "fstat(%s)", path);
+ goto fail;
+ }
+ if (st.st_size < 0 || (unsigned)st.st_size < sizeof(*fw_header)) {
+ WARNX(2, "file too short: %s", path);
+ goto fail;
+ }
+
+ /*
+ * mmap the whole image.
+ */
+ fw_image = (uint32_t *)mmap(NULL, st.st_size, PROT_READ,
+ MAP_PRIVATE, fd, 0);
+ if (fw_image == MAP_FAILED) {
+ WARN(0, "mmap(%s)", path);
+ goto fail;
+ }
+ fw_header = (intel_fw_header_t *)fw_image;
+ if (fw_header->header_version != INTEL_HEADER_VERSION ||
+ fw_header->loader_revision != INTEL_LOADER_REVISION) {
+ WARNX(2, "%s is not a valid intel firmware: version mismatch",
+ path);
+ goto fail;
+ }
+ /*
+ * According to spec, if data_size == 0, then size of ucode = 2000.
+ */
+ if (fw_header->data_size == 0)
+ data_size = DEFAULT_UCODE_SIZE;
+ else
+ data_size = fw_header->data_size;
+ if (fw_header->total_size == 0)
+ total_size = data_size + sizeof(*fw_header);
+ else
+ total_size = fw_header->total_size;
+ if (total_size > (unsigned)st.st_size || st.st_size < 0) {
+ WARNX(2, "file too short: %s", path);
+ goto fail;
+ }
+ payload_size = data_size + sizeof(*fw_header);
+
+ /*
+ * Check the primary checksum.
+ */
+ sum = 0;
+ for (i = 0; i < (payload_size / sizeof(uint32_t)); i++)
+ sum += *((uint32_t *)fw_image + i);
+ if (sum != 0) {
+ WARNX(2, "%s: update data checksum invalid", path);
+ goto fail;
+ }
+
+ /*
+ * Check if there is an extended signature table.
+ */
+ ext_size = total_size - payload_size;
+ have_ext_table = 0;
+
+ if (ext_size > (signed)sizeof(*ext_header)) {
+ ext_header =
+ (intel_ext_header_t *)((char *)fw_image + payload_size);
+ ext_table = (intel_cpu_signature_t *)(ext_header + 1);
+
+ /*
+ * Check the extended table size.
+ */
+ ext_table_size = sizeof(*ext_header) +
+ ext_header->sig_count * sizeof(*ext_table);
+ if (ext_table_size + payload_size > total_size) {
+ WARNX(2, "%s: broken extended signature table", path);
+ goto no_table;
+ }
+
+ /*
+ * Check the extended table signature.
+ */
+ sum = 0;
+ for (i = 0; i < (ext_table_size / sizeof(uint32_t)); i++)
+ sum += *((uint32_t *)ext_header + i);
+ if (sum != 0) {
+ WARNX(2, "%s: extended signature table checksum invalid",
+ path);
+ goto no_table;
+ }
+ have_ext_table = 1;
+ }
+
+no_table:
+ fw_data = fw_header + 1; /* Pointer to the update data. */
+
+ /*
+ * Check if the given image is ok for this cpu.
+ */
+ if (signature == fw_header->cpu_signature &&
+ (flags & fw_header->cpu_flags) != 0)
+ goto matched;
+ else if (have_ext_table != 0) {
+ for (i = 0; i < ext_header->sig_count; i++) {
+ uint32_t sig = ext_table[i].cpu_signature;
+ if (signature == sig &&
+ (flags & ext_table[i].cpu_flags) != 0)
+ goto matched;
+ }
+ } else
+ goto fail;
+
+matched:
+ if (revision >= fw_header->revision) {
+ WARNX(1, "skipping %s of rev %#x: up to date",
+ path, fw_header->revision);
+ return;
+ }
+ fprintf(stderr, "%s: updating cpu %s from rev %#x to rev %#x... ",
+ path, dev, revision, fw_header->revision);
+ args.data = fw_data;
+ args.size = data_size;
+ error = ioctl(devfd, CPUCTL_UPDATE, &args);
+ if (error < 0) {
+ fprintf(stderr, "failed.\n");
+ WARN(0, "ioctl()");
+ goto fail;
+ }
+ fprintf(stderr, "done.\n");
+
+fail:
+ if (fw_image != MAP_FAILED)
+ if (munmap(fw_image, st.st_size) != 0)
+ warn("munmap(%s)", path);
+ if (devfd >= 0)
+ close(devfd);
+ if (fd >= 0)
+ close(fd);
+ return;
+}
diff --git a/usr.sbin/cpucontrol/intel.h b/usr.sbin/cpucontrol/intel.h
new file mode 100644
index 0000000..0303e69
--- /dev/null
+++ b/usr.sbin/cpucontrol/intel.h
@@ -0,0 +1,70 @@
+/*-
+ * Copyright (c) 2006, 2008 Stanislav Sedov <stas@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef INTEL_H
+#define INTEL_H
+
+/*
+ * Prototypes.
+ */
+ucode_probe_t intel_probe;
+ucode_update_t intel_update;
+
+typedef struct intel_fw_header {
+ uint32_t header_version; /* Version of the header. */
+ int32_t revision; /* Unique version number. */
+ uint32_t date; /* Date of creation in BCD. */
+ uint32_t cpu_signature; /* Extended family, extended
+ model, type, family, model
+ and stepping. */
+ uint32_t checksum; /* Sum of all DWORDS should
+ be 0. */
+ uint32_t loader_revision; /* Version of the loader
+ required to load update. */
+ uint32_t cpu_flags; /* Platform IDs encoded in
+ the lower 8 bits. */
+ uint32_t data_size;
+ uint32_t total_size;
+ uint8_t reserved[12];
+} intel_fw_header_t;
+
+typedef struct intel_cpu_signature {
+ uint32_t cpu_signature;
+ uint32_t cpu_flags;
+ uint32_t checksum;
+} intel_cpu_signature_t;
+
+typedef struct intel_ext_header {
+ uint32_t sig_count;
+ uint32_t checksum;
+ uint8_t reserved[12];
+} intel_ext_header_t;
+
+#define INTEL_HEADER_VERSION 0x00000001
+#define INTEL_LOADER_REVISION 0x00000001
+
+#endif /* !INTEL_H */
diff --git a/usr.sbin/crashinfo/Makefile b/usr.sbin/crashinfo/Makefile
new file mode 100644
index 0000000..fa2a19c
--- /dev/null
+++ b/usr.sbin/crashinfo/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+SCRIPTS= crashinfo.sh
+MAN= crashinfo.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/crashinfo/crashinfo.8 b/usr.sbin/crashinfo/crashinfo.8
new file mode 100644
index 0000000..e4ae67b
--- /dev/null
+++ b/usr.sbin/crashinfo/crashinfo.8
@@ -0,0 +1,109 @@
+.\" Copyright (c) 2008 Yahoo!, 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.
+.\" 3. 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 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 28, 2008
+.Dt CRASHINFO 8
+.Os
+.Sh NAME
+.Nm crashinfo
+.Nd "analyze a core dump of the operating system"
+.Sh SYNOPSIS
+.Nm
+.Op Fl d Ar crashdir
+.Op Fl n Ar dumpnr
+.Op Fl k Ar kernel
+.Op Ar core
+.Sh DESCRIPTION
+The
+.Nm
+utility analyzes a core dump saved by
+.Xr savecore 8 .
+It generates a text file containing the analysis in the same directory as
+the core dump.
+For a given core dump file named
+.Pa vmcore.XX
+the generated text file will be named
+.Pa core.txt.XX .
+.Pp
+By default,
+.Nm
+analyzes the most recent core dump in the core dump directory.
+A specific core dump may be specified via either the
+.Ar core
+or
+.Ar dumpnr
+arguments.
+Once
+.Nm
+has located a core dump,
+it analyzes the core dump to determine the exact version of the kernel
+that generated the core.
+It then looks for a matching kernel file under each of the subdirectories in
+.Pa /boot .
+The location of the kernel file can also be explicitly provided via the
+.Ar kernel
+argument.
+.Pp
+Once
+.Nm
+has located a core dump and kernel,
+it uses several utilities to analyze the core including
+.Xr dmesg 8 ,
+.Xr fstat 1 ,
+.Xr iostat 8 ,
+.Xr ipcs 1 ,
+.Xr kgdb 1 ,
+.Xr netstat 1 ,
+.Xr nfsstat 1 ,
+.Xr ps 1 ,
+.Xr pstat 8 ,
+and
+.Xr vmstat 8 .
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl d Ar crashdir
+Specify an alternate core dump directory.
+The default crash dump directory is
+.Pa /var/crash .
+.It Fl n Ar dumpnr
+Use the core dump saved in
+.Pa vmcore. Ns Ar dumpnr
+instead of the latest core in the core dump directory.
+.It Fl k Ar kernel
+Specify an explicit kernel file.
+.El
+.Sh SEE ALSO
+.Xr savecore 8 ,
+.Xr textdump 4
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 8.0 .
diff --git a/usr.sbin/crashinfo/crashinfo.sh b/usr.sbin/crashinfo/crashinfo.sh
new file mode 100755
index 0000000..cd41009
--- /dev/null
+++ b/usr.sbin/crashinfo/crashinfo.sh
@@ -0,0 +1,306 @@
+#!/bin/sh
+#
+# Copyright (c) 2008 Yahoo!, 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.
+# 3. 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 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$
+
+usage()
+{
+ echo "usage: crashinfo [-d crashdir] [-n dumpnr] [-k kernel] [core]"
+ exit 1
+}
+
+find_kernel()
+{
+ local ivers k kvers
+
+ ivers=$(awk '
+ /Version String/ {
+ print
+ nextline=1
+ next
+ }
+ // {
+ if (nextline) {
+ print
+ nextline=0
+ }
+ }' $INFO)
+
+ # Look for a matching kernel version.
+ for k in /boot/kernel/kernel $(ls -t /boot/*/kernel); do
+ kvers=$(echo 'printf " Version String: %s", version' | \
+ gdb -x /dev/stdin -batch $k 2>/dev/null)
+ if [ "$ivers" = "$kvers" ]; then
+ KERNEL=$k
+ break
+ fi
+ done
+}
+
+CRASHDIR=/var/crash
+DUMPNR=
+KERNEL=
+
+while getopts "d:n:k:" opt; do
+ case "$opt" in
+ d)
+ CRASHDIR=$OPTARG
+ ;;
+ n)
+ DUMPNR=$OPTARG
+ ;;
+ k)
+ KERNEL=$OPTARG
+ ;;
+ \?)
+ usage
+ ;;
+ esac
+done
+
+shift $((OPTIND - 1))
+
+if [ $# -eq 1 ]; then
+ if [ -n "$DUMPNR" ]; then
+ echo "-n and an explicit vmcore are mutually exclusive"
+ usage
+ fi
+
+ # Figure out the crash directory and number from the vmcore name.
+ CRASHDIR=`dirname $1`
+ DUMPNR=$(expr $(basename $1) : 'vmcore\.\([0-9]*\)$')
+ if [ -z "$DUMPNR" ]; then
+ echo "Unable to determine dump number from vmcore file $1."
+ exit 1
+ fi
+elif [ $# -gt 1 ]; then
+ usage
+else
+ # If we don't have an explicit dump number, operate on the most
+ # recent dump.
+ if [ -z "$DUMPNR" ]; then
+ if ! [ -r $CRASHDIR/bounds ]; then
+ echo "No crash dumps in $CRASHDIR."
+ exit 1
+ fi
+ next=`cat $CRASHDIR/bounds`
+ if [ -z "$next" ] || [ "$next" -eq 0 ]; then
+ echo "No crash dumps in $CRASHDIR."
+ exit 1
+ fi
+ DUMPNR=$(($next - 1))
+ fi
+fi
+
+VMCORE=$CRASHDIR/vmcore.$DUMPNR
+INFO=$CRASHDIR/info.$DUMPNR
+FILE=$CRASHDIR/core.txt.$DUMPNR
+HOSTNAME=`hostname`
+
+if [ ! -e $VMCORE ]; then
+ echo "$VMCORE not found"
+ exit 1
+fi
+
+if [ ! -e $INFO ]; then
+ echo "$INFO not found"
+ exit 1
+fi
+
+# If the user didn't specify a kernel, then try to find one.
+if [ -z "$KERNEL" ]; then
+ find_kernel
+ if [ -z "$KERNEL" ]; then
+ echo "Unable to find matching kernel for $VMCORE"
+ exit 1
+ fi
+elif [ ! -e $KERNEL ]; then
+ echo "$KERNEL not found"
+ exit 1
+fi
+
+echo "Writing crash summary to $FILE."
+
+# Simulate uname
+ostype=$(echo -e printf '"%s", ostype' | gdb -x /dev/stdin -batch $KERNEL)
+osrelease=$(echo -e printf '"%s", osrelease' | gdb -x /dev/stdin -batch $KERNEL)
+version=$(echo -e printf '"%s", version' | gdb -x /dev/stdin -batch $KERNEL | \
+ tr '\t\n' ' ')
+machine=$(echo -e printf '"%s", machine' | gdb -x /dev/stdin -batch $KERNEL)
+
+exec > $FILE 2>&1
+
+echo "$HOSTNAME dumped core - see $VMCORE"
+echo
+date
+echo
+echo "$ostype $HOSTNAME $osrelease $version $machine"
+echo
+sed -ne '/^ Panic String: /{s//panic: /;p;}' $INFO
+echo
+
+# XXX: /bin/sh on 7.0+ is broken so we can't simply pipe the commands to
+# kgdb via stdin and have to use a temporary file instead.
+file=`mktemp /tmp/crashinfo.XXXXXX`
+if [ $? -eq 0 ]; then
+ echo "bt" >> $file
+ echo "quit" >> $file
+ kgdb $KERNEL $VMCORE < $file
+ rm -f $file
+ echo
+fi
+echo
+
+echo "------------------------------------------------------------------------"
+echo "ps -axl"
+echo
+ps -M $VMCORE -N $KERNEL -axl
+echo
+
+echo "------------------------------------------------------------------------"
+echo "vmstat -s"
+echo
+vmstat -M $VMCORE -N $KERNEL -s
+echo
+
+echo "------------------------------------------------------------------------"
+echo "vmstat -m"
+echo
+vmstat -M $VMCORE -N $KERNEL -m
+echo
+
+echo "------------------------------------------------------------------------"
+echo "vmstat -z"
+echo
+vmstat -M $VMCORE -N $KERNEL -z
+echo
+
+echo "------------------------------------------------------------------------"
+echo "vmstat -i"
+echo
+vmstat -M $VMCORE -N $KERNEL -i
+echo
+
+echo "------------------------------------------------------------------------"
+echo "pstat -T"
+echo
+pstat -M $VMCORE -N $KERNEL -T
+echo
+
+echo "------------------------------------------------------------------------"
+echo "pstat -s"
+echo
+pstat -M $VMCORE -N $KERNEL -s
+echo
+
+echo "------------------------------------------------------------------------"
+echo "iostat"
+echo
+iostat -M $VMCORE -N $KERNEL
+echo
+
+echo "------------------------------------------------------------------------"
+echo "ipcs -a"
+echo
+ipcs -C $VMCORE -N $KERNEL -a
+echo
+
+echo "------------------------------------------------------------------------"
+echo "ipcs -T"
+echo
+ipcs -C $VMCORE -N $KERNEL -T
+echo
+
+# XXX: This doesn't actually work in 5.x+
+if false; then
+echo "------------------------------------------------------------------------"
+echo "w -dn"
+echo
+w -M $VMCORE -N $KERNEL -dn
+echo
+fi
+
+echo "------------------------------------------------------------------------"
+echo "nfsstat"
+echo
+nfsstat -M $VMCORE -N $KERNEL
+echo
+
+echo "------------------------------------------------------------------------"
+echo "netstat -s"
+echo
+netstat -M $VMCORE -N $KERNEL -s
+echo
+
+echo "------------------------------------------------------------------------"
+echo "netstat -m"
+echo
+netstat -M $VMCORE -N $KERNEL -m
+echo
+
+echo "------------------------------------------------------------------------"
+echo "netstat -id"
+echo
+netstat -M $VMCORE -N $KERNEL -id
+echo
+
+echo "------------------------------------------------------------------------"
+echo "netstat -anr"
+echo
+netstat -M $VMCORE -N $KERNEL -anr
+echo
+
+echo "------------------------------------------------------------------------"
+echo "netstat -anA"
+echo
+netstat -M $VMCORE -N $KERNEL -anA
+echo
+
+echo "------------------------------------------------------------------------"
+echo "netstat -aL"
+echo
+netstat -M $VMCORE -N $KERNEL -aL
+echo
+
+echo "------------------------------------------------------------------------"
+echo "fstat"
+echo
+fstat -M $VMCORE -N $KERNEL
+echo
+
+echo "------------------------------------------------------------------------"
+echo "dmesg"
+echo
+dmesg -a -M $VMCORE -N $KERNEL
+echo
+
+echo "------------------------------------------------------------------------"
+echo "kernel config"
+echo
+config -x $KERNEL
diff --git a/usr.sbin/cron/Makefile b/usr.sbin/cron/Makefile
new file mode 100644
index 0000000..62f853c
--- /dev/null
+++ b/usr.sbin/cron/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+SUBDIR= lib cron crontab
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/cron/Makefile.inc b/usr.sbin/cron/Makefile.inc
new file mode 100644
index 0000000..900730b
--- /dev/null
+++ b/usr.sbin/cron/Makefile.inc
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+LIBCRON= ${.OBJDIR}/../lib/libcron.a
+
+.include "../Makefile.inc"
diff --git a/usr.sbin/cron/cron/Makefile b/usr.sbin/cron/cron/Makefile
new file mode 100644
index 0000000..77ca47a
--- /dev/null
+++ b/usr.sbin/cron/cron/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+PROG= cron
+MAN= cron.8
+SRCS= cron.c database.c do_command.c job.c user.c popen.c
+
+CFLAGS+= -DLOGIN_CAP -DPAM
+
+DPADD= ${LIBCRON} ${LIBPAM} ${LIBUTIL}
+LDADD= ${LIBCRON} ${MINUSLPAM} -lutil
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/cron/cron/compat.h b/usr.sbin/cron/cron/compat.h
new file mode 100644
index 0000000..905c3aa
--- /dev/null
+++ b/usr.sbin/cron/cron/compat.h
@@ -0,0 +1,140 @@
+/* Copyright 1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+/*
+ * $FreeBSD$
+ */
+
+#ifndef __P
+# ifdef __STDC__
+# define __P(x) x
+# else
+# define __P(x) ()
+# define const
+# endif
+#endif
+
+#if defined(UNIXPC) || defined(unixpc)
+# define UNIXPC 1
+# define ATT 1
+#endif
+
+#if defined(hpux) || defined(_hpux) || defined(__hpux)
+# define HPUX 1
+# define seteuid(e) setresuid(-1,e,-1)
+# define setreuid(r,e) setresuid(r,e,-1)
+#endif
+
+#if defined(_IBMR2)
+# define AIX 1
+#endif
+
+#if defined(__convex__)
+# define CONVEX 1
+#endif
+
+#if defined(sgi) || defined(_sgi) || defined(__sgi)
+# define IRIX 1
+/* IRIX 4 hdrs are broken: one cannot #include both <stdio.h>
+ * and <stdlib.h> because they disagree on system(), perror().
+ * Therefore we must zap the "const" keyword BEFORE including
+ * either of them.
+ */
+# define const
+#endif
+
+#if defined(_UNICOS)
+# define UNICOS 1
+#endif
+
+#ifndef POSIX
+# if (BSD >= 199103) || defined(__linux) || defined(ultrix) || defined(AIX) ||\
+ defined(HPUX) || defined(CONVEX) || defined(IRIX)
+# define POSIX
+# endif
+#endif
+
+#ifndef BSD
+# if defined(ultrix)
+# define BSD 198902
+# endif
+#endif
+
+/*****************************************************************/
+
+#if !defined(BSD) && !defined(HPUX) && !defined(CONVEX) && !defined(__linux)
+# define NEED_VFORK
+#endif
+
+#if (!defined(BSD) || (BSD < 198902)) && !defined(__linux) && \
+ !defined(IRIX) && !defined(NeXT) && !defined(HPUX)
+# define NEED_STRCASECMP
+#endif
+
+#if (!defined(BSD) || (BSD < 198911)) && !defined(__linux) &&\
+ !defined(IRIX) && !defined(UNICOS) && !defined(HPUX)
+# define NEED_STRDUP
+#endif
+
+#if (!defined(BSD) || (BSD < 198911)) && !defined(POSIX) && !defined(NeXT)
+# define NEED_STRERROR
+#endif
+
+#if defined(HPUX) || defined(AIX) || defined(UNIXPC)
+# define NEED_FLOCK
+#endif
+
+#ifndef POSIX
+# define NEED_SETSID
+#endif
+
+#if (defined(POSIX) && !defined(BSD)) && !defined(__linux)
+# define NEED_GETDTABLESIZE
+#endif
+
+#ifdef POSIX
+#include <unistd.h>
+#ifdef _POSIX_SAVED_IDS
+# define HAVE_SAVED_UIDS
+#endif
+#endif
+
+#if !defined(ATT) && !defined(__linux) && !defined(IRIX) && !defined(UNICOS)
+# define USE_SIGCHLD
+#endif
+
+#if !defined(AIX) && !defined(UNICOS)
+# define SYS_TIME_H 1
+#else
+# define SYS_TIME_H 0
+#endif
+
+#if defined(BSD) && !defined(POSIX)
+# define USE_UTIMES
+#endif
+
+#if defined(AIX) || defined(HPUX) || defined(IRIX)
+# define NEED_SETENV
+#endif
+
+#if !defined(UNICOS) && !defined(UNIXPC)
+# define HAS_FCHOWN
+#endif
+
+#if !defined(UNICOS) && !defined(UNIXPC)
+# define HAS_FCHMOD
+#endif
diff --git a/usr.sbin/cron/cron/config.h b/usr.sbin/cron/cron/config.h
new file mode 100644
index 0000000..6d77da2
--- /dev/null
+++ b/usr.sbin/cron/cron/config.h
@@ -0,0 +1,87 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+/* config.h - configurables for Vixie Cron
+ *
+ * $FreeBSD$
+ */
+
+#if !defined(_PATH_SENDMAIL)
+# define _PATH_SENDMAIL "/usr/lib/sendmail"
+#endif /*SENDMAIL*/
+
+/*
+ * these are site-dependent
+ */
+
+#ifndef DEBUGGING
+#define DEBUGGING 1 /* 1 or 0 -- do you want debugging code built in? */
+#endif
+
+ /*
+ * choose one of these MAILCMD commands. I use
+ * /bin/mail for speed; it makes biff bark but doesn't
+ * do aliasing. /usr/lib/sendmail does aliasing but is
+ * a hog for short messages. aliasing is not needed
+ * if you make use of the MAILTO= feature in crontabs.
+ * (hint: MAILTO= was added for this reason).
+ */
+
+#define MAILCMD _PATH_SENDMAIL /*-*/
+#define MAILARGS "%s -FCronDaemon -odi -oem -oi -t" /*-*/
+ /* -Fx = set full-name of sender
+ * -odi = Option Deliverymode Interactive
+ * -oem = Option Errors Mailedtosender
+ * -oi = Option dot message terminator
+ * -t = read recipients from header of message
+ */
+
+/* #define MAILCMD "/bin/mail" */ /*-*/
+/* #define MAILARGS "%s -d %s" */ /*-*/
+ /* -d = undocumented but common flag: deliver locally?
+ */
+
+/* #define MAILCMD "/usr/mmdf/bin/submit" */ /*-*/
+/* #define MAILARGS "%s -mlrxto %s" */ /*-*/
+
+/* #define MAIL_DATE */ /*-*/
+ /* should we include an ersatz Date: header in
+ * generated mail? if you are using sendmail
+ * for MAILCMD, it is better to let sendmail
+ * generate the Date: header.
+ */
+
+ /* if ALLOW_FILE and DENY_FILE are not defined or are
+ * defined but neither exists, should crontab(1) be
+ * usable only by root?
+ */
+/* #define ALLOW_ONLY_ROOT */ /*-*/
+
+ /* if you want to use syslog(3) instead of appending
+ * to CRONDIR/LOG_FILE (/var/cron/log, e.g.), define
+ * SYSLOG here. Note that quite a bit of logging
+ * info is written, and that you probably don't want
+ * to use this on 4.2bsd since everything goes in
+ * /usr/spool/mqueue/syslog. On 4.[34]bsd you can
+ * tell /etc/syslog.conf to send cron's logging to
+ * a separate file.
+ *
+ * Note that if this and LOG_FILE in "pathnames.h"
+ * are both defined, then logging will go to both
+ * places.
+ */
+#define SYSLOG /*-*/
diff --git a/usr.sbin/cron/cron/cron.8 b/usr.sbin/cron/cron/cron.8
new file mode 100644
index 0000000..5d5a8d9
--- /dev/null
+++ b/usr.sbin/cron/cron/cron.8
@@ -0,0 +1,221 @@
+.\"/* Copyright 1988,1990,1993 by Paul Vixie
+.\" * All rights reserved
+.\" *
+.\" * Distribute freely, except: don't remove my name from the source or
+.\" * documentation (don't take credit for my work), mark your changes (don't
+.\" * get me blamed for your possible bugs), don't alter or remove this
+.\" * notice. May be sold if buildable source is provided to buyer. No
+.\" * warrantee of any kind, express or implied, is included with this
+.\" * software; use at your own risk, responsibility for damages (if any) to
+.\" * anyone resulting from the use of this software rests entirely with the
+.\" * user.
+.\" *
+.\" * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+.\" * I'll try to keep a version up to date. I can be reached as follows:
+.\" * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+.\" */
+.\"
+.\" $FreeBSD$
+.\"
+.Dd June 29, 2008
+.Dt CRON 8
+.Os
+.Sh NAME
+.Nm cron
+.Nd daemon to execute scheduled commands (Vixie Cron)
+.Sh SYNOPSIS
+.Nm
+.Op Fl j Ar jitter
+.Op Fl J Ar rootjitter
+.Op Fl m Ar mailto
+.Op Fl s
+.Op Fl o
+.Op Fl x Ar debugflag Ns Op , Ns Ar ...
+.Sh DESCRIPTION
+The
+.Nm
+utility should be started from
+.Pa /etc/rc
+or
+.Pa /etc/rc.local .
+It will return immediately,
+so you do not need to start it with '&'.
+.Pp
+The
+.Nm
+utility searches
+.Pa /var/cron/tabs
+for crontab files which are named after accounts in
+.Pa /etc/passwd ;
+crontabs found are loaded into memory.
+The
+.Nm
+utility also searches for
+.Pa /etc/crontab
+which is in a different format (see
+.Xr crontab 5 ) .
+.Pp
+The
+.Nm
+utility
+then wakes up every minute, examining all stored crontabs, checking each
+command to see if it should be run in the current minute.
+Before running a command from a per-account crontab file,
+.Nm
+checks the status of the account with
+.Xr pam 3
+and skips the command if the account is unavailable,
+e.g., locked out or expired.
+Commands from
+.Pa /etc/crontab
+bypass this check.
+When executing
+commands, any output is mailed to the owner of the crontab (or to the user
+named in the
+.Ev MAILTO
+environment variable in the crontab, if such exists).
+.Pp
+Additionally,
+.Nm
+checks each minute to see if its spool directory's modification time (or
+the modification time on
+.Pa /etc/crontab )
+has changed, and if it has,
+.Nm
+will then examine the modification time on all crontabs and reload those
+which have changed.
+Thus
+.Nm
+need not be restarted whenever a crontab file is modified.
+Note that the
+.Xr crontab 1
+command updates the modification time of the spool directory whenever it
+changes a crontab.
+.Pp
+Available options:
+.Bl -tag -width indent
+.It Fl j Ar jitter
+Enable time jitter.
+Prior to executing commands,
+.Nm
+will sleep a random number of seconds in the range from 0 to
+.Ar jitter .
+This will not affect superuser jobs (see
+.Fl J ) .
+A value for
+.Ar jitter
+must be between 0 and 60 inclusive.
+Default is 0, which effectively disables time jitter.
+.Pp
+This option can help to smooth down system load spikes during
+moments when a lot of jobs are likely to start at once, e.g.,
+at the beginning of the first minute of each hour.
+.It Fl J Ar rootjitter
+Enable time jitter for superuser jobs.
+The same as
+.Fl j
+except that it will affect jobs run by the superuser only.
+.It Fl m Ar mailto
+Overrides the default recipient for
+.Nm
+mail.
+Each
+.Xr crontab 5
+without
+.Ev MAILTO
+explicitly set will send mail to the
+.Ar mailto
+mailbox.
+Sending mail will be disabled by default if
+.Ar mailto
+set to a null string, usually specified in a shell as
+.Li ''
+or
+.Li \*q\*q .
+.It Fl s
+Enable special handling of situations when the GMT offset of the local
+timezone changes, such as the switches between the standard time and
+daylight saving time.
+.Pp
+The jobs run during the GMT offset changes time as
+intuitively expected.
+If a job falls into a time interval that disappears
+(for example, during the switch from
+standard time) to daylight saving time or is
+duplicated (for example, during the reverse switch), then it is handled
+in one of two ways:
+.Pp
+The first case is for the jobs that run every at hour of a time interval
+overlapping with the disappearing or duplicated interval.
+In other words, if the job had run within one hour before the GMT offset change
+(and cron was not restarted nor the
+.Xr crontab 5
+changed after that)
+or would run after the change at the next hour.
+They work as always, skip the skipped time or run in the added
+time as usual.
+.Pp
+The second case is for the jobs that run less frequently.
+They are executed exactly once, they are not skipped nor
+executed twice (unless cron is restarted or the user's
+.Xr crontab 5
+is changed during such a time interval).
+If an interval disappears
+due to the GMT offset change, such jobs are
+executed at the same absolute point of time as they would be in the
+old time zone.
+For example, if exactly one hour disappears, this
+point would be during the next hour at the first minute that is
+specified for them in
+.Xr crontab 5 .
+.It Fl o
+Disable the special handling of situations when the GMT offset of the local
+timezone changes, to be compatible with the old (default) behavior.
+If both options
+.Fl o
+and
+.Fl s
+are specified, the option specified last wins.
+.It Fl x Ar debugflag Ns Op , Ns Ar ...
+Enable writing of debugging information to standard output.
+One or more of the following comma separated
+.Ar debugflag
+identifiers must be specified:
+.Pp
+.Bl -tag -width ".Cm proc" -compact
+.It Cm bit
+currently not used
+.It Cm ext
+make the other debug flags more verbose
+.It Cm load
+be verbose when loading crontab files
+.It Cm misc
+be verbose about miscellaneous one-off events
+.It Cm pars
+be verbose about parsing individual crontab lines
+.It Cm proc
+be verbose about the state of the process, including all of its offspring
+.It Cm sch
+be verbose when iterating through the scheduling algorithms
+.It Cm test
+trace through the execution, but do not perform any actions
+.El
+.El
+.Sh FILES
+.Bl -tag -width /etc/pam.d/cron -compact
+.It Pa /etc/crontab
+System crontab file
+.It Pa /etc/pam.d/cron
+.Xr pam.conf 5
+configuration file for
+.Nm
+.It Pa /var/cron/tabs
+Directory for personal crontab files
+.El
+.Sh SEE ALSO
+.Xr crontab 1 ,
+.Xr pam 3 ,
+.Xr crontab 5 ,
+.Xr pam.conf 5
+.Sh AUTHORS
+.An Paul Vixie Aq paul@vix.com
diff --git a/usr.sbin/cron/cron/cron.c b/usr.sbin/cron/cron/cron.c
new file mode 100644
index 0000000..101989c
--- /dev/null
+++ b/usr.sbin/cron/cron/cron.c
@@ -0,0 +1,478 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if !defined(lint) && !defined(LINT)
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+
+#define MAIN_PROGRAM
+
+
+#include "cron.h"
+#include <sys/signal.h>
+#if SYS_TIME_H
+# include <sys/time.h>
+#else
+# include <time.h>
+#endif
+
+
+static void usage(void),
+ run_reboot_jobs(cron_db *),
+ cron_tick(cron_db *),
+ cron_sync(void),
+ cron_sleep(cron_db *),
+ cron_clean(cron_db *),
+#ifdef USE_SIGCHLD
+ sigchld_handler(int),
+#endif
+ sighup_handler(int),
+ parse_args(int c, char *v[]);
+
+static time_t last_time = 0;
+static int dst_enabled = 0;
+struct pidfh *pfh;
+
+static void
+usage() {
+ char **dflags;
+
+ fprintf(stderr, "usage: cron [-j jitter] [-J rootjitter] "
+ "[-m mailto] [-s] [-o] [-x debugflag[,...]]\n");
+ fprintf(stderr, "\ndebugflags: ");
+
+ for(dflags = DebugFlagNames; *dflags; dflags++) {
+ fprintf(stderr, "%s ", *dflags);
+ }
+ fprintf(stderr, "\n");
+
+ exit(ERROR_EXIT);
+}
+
+static void
+open_pidfile(void)
+{
+ char pidfile[MAX_FNAME];
+ char buf[MAX_TEMPSTR];
+ int otherpid;
+
+ (void) snprintf(pidfile, sizeof(pidfile), PIDFILE, PIDDIR);
+ pfh = pidfile_open(pidfile, 0600, &otherpid);
+ if (pfh == NULL) {
+ if (errno == EEXIST) {
+ snprintf(buf, sizeof(buf),
+ "cron already running, pid: %d", otherpid);
+ } else {
+ snprintf(buf, sizeof(buf),
+ "can't open or create %s: %s", pidfile,
+ strerror(errno));
+ }
+ log_it("CRON", getpid(), "DEATH", buf);
+ errx(ERROR_EXIT, "%s", buf);
+ }
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ cron_db database;
+
+ ProgramName = argv[0];
+
+#if defined(BSD)
+ setlinebuf(stdout);
+ setlinebuf(stderr);
+#endif
+
+ parse_args(argc, argv);
+
+#ifdef USE_SIGCHLD
+ (void) signal(SIGCHLD, sigchld_handler);
+#else
+ (void) signal(SIGCLD, SIG_IGN);
+#endif
+ (void) signal(SIGHUP, sighup_handler);
+
+ open_pidfile();
+ set_cron_uid();
+ set_cron_cwd();
+
+#if defined(POSIX)
+ setenv("PATH", _PATH_DEFPATH, 1);
+#endif
+
+ /* if there are no debug flags turned on, fork as a daemon should.
+ */
+# if DEBUGGING
+ if (DebugFlags) {
+# else
+ if (0) {
+# endif
+ (void) fprintf(stderr, "[%d] cron started\n", getpid());
+ } else {
+ if (daemon(1, 0) == -1) {
+ pidfile_remove(pfh);
+ log_it("CRON",getpid(),"DEATH","can't become daemon");
+ exit(0);
+ }
+ }
+
+ pidfile_write(pfh);
+ database.head = NULL;
+ database.tail = NULL;
+ database.mtime = (time_t) 0;
+ load_database(&database);
+ run_reboot_jobs(&database);
+ cron_sync();
+ while (TRUE) {
+# if DEBUGGING
+ /* if (!(DebugFlags & DTEST)) */
+# endif /*DEBUGGING*/
+ cron_sleep(&database);
+
+ load_database(&database);
+
+ /* do this iteration
+ */
+ cron_tick(&database);
+
+ /* sleep 1 minute
+ */
+ TargetTime += 60;
+ }
+}
+
+
+static void
+run_reboot_jobs(db)
+ cron_db *db;
+{
+ register user *u;
+ register entry *e;
+
+ for (u = db->head; u != NULL; u = u->next) {
+ for (e = u->crontab; e != NULL; e = e->next) {
+ if (e->flags & WHEN_REBOOT) {
+ job_add(e, u);
+ }
+ }
+ }
+ (void) job_runqueue();
+}
+
+
+static void
+cron_tick(db)
+ cron_db *db;
+{
+ static struct tm lasttm;
+ static time_t diff = 0, /* time difference in seconds from the last offset change */
+ difflimit = 0; /* end point for the time zone correction */
+ struct tm otztm; /* time in the old time zone */
+ int otzminute, otzhour, otzdom, otzmonth, otzdow;
+ register struct tm *tm = localtime(&TargetTime);
+ register int minute, hour, dom, month, dow;
+ register user *u;
+ register entry *e;
+
+ /* make 0-based values out of these so we can use them as indicies
+ */
+ minute = tm->tm_min -FIRST_MINUTE;
+ hour = tm->tm_hour -FIRST_HOUR;
+ dom = tm->tm_mday -FIRST_DOM;
+ month = tm->tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH;
+ dow = tm->tm_wday -FIRST_DOW;
+
+ Debug(DSCH, ("[%d] tick(%d,%d,%d,%d,%d)\n",
+ getpid(), minute, hour, dom, month, dow))
+
+ if (dst_enabled && last_time != 0
+ && TargetTime > last_time /* exclude stepping back */
+ && tm->tm_gmtoff != lasttm.tm_gmtoff ) {
+
+ diff = tm->tm_gmtoff - lasttm.tm_gmtoff;
+
+ if ( diff > 0 ) { /* ST->DST */
+ /* mark jobs for an earlier run */
+ difflimit = TargetTime + diff;
+ for (u = db->head; u != NULL; u = u->next) {
+ for (e = u->crontab; e != NULL; e = e->next) {
+ e->flags &= ~NOT_UNTIL;
+ if ( e->lastrun >= TargetTime )
+ e->lastrun = 0;
+ /* not include the ends of hourly ranges */
+ if ( e->lastrun < TargetTime - 3600 )
+ e->flags |= RUN_AT;
+ else
+ e->flags &= ~RUN_AT;
+ }
+ }
+ } else { /* diff < 0 : DST->ST */
+ /* mark jobs for skipping */
+ difflimit = TargetTime - diff;
+ for (u = db->head; u != NULL; u = u->next) {
+ for (e = u->crontab; e != NULL; e = e->next) {
+ e->flags |= NOT_UNTIL;
+ e->flags &= ~RUN_AT;
+ }
+ }
+ }
+ }
+
+ if (diff != 0) {
+ /* if the time was reset of the end of special zone is reached */
+ if (last_time == 0 || TargetTime >= difflimit) {
+ /* disable the TZ switch checks */
+ diff = 0;
+ difflimit = 0;
+ for (u = db->head; u != NULL; u = u->next) {
+ for (e = u->crontab; e != NULL; e = e->next) {
+ e->flags &= ~(RUN_AT|NOT_UNTIL);
+ }
+ }
+ } else {
+ /* get the time in the old time zone */
+ time_t difftime = TargetTime + tm->tm_gmtoff - diff;
+ gmtime_r(&difftime, &otztm);
+
+ /* make 0-based values out of these so we can use them as indicies
+ */
+ otzminute = otztm.tm_min -FIRST_MINUTE;
+ otzhour = otztm.tm_hour -FIRST_HOUR;
+ otzdom = otztm.tm_mday -FIRST_DOM;
+ otzmonth = otztm.tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH;
+ otzdow = otztm.tm_wday -FIRST_DOW;
+ }
+ }
+
+ /* the dom/dow situation is odd. '* * 1,15 * Sun' will run on the
+ * first and fifteenth AND every Sunday; '* * * * Sun' will run *only*
+ * on Sundays; '* * 1,15 * *' will run *only* the 1st and 15th. this
+ * is why we keep 'e->dow_star' and 'e->dom_star'. yes, it's bizarre.
+ * like many bizarre things, it's the standard.
+ */
+ for (u = db->head; u != NULL; u = u->next) {
+ for (e = u->crontab; e != NULL; e = e->next) {
+ Debug(DSCH|DEXT, ("user [%s:%d:%d:...] cmd=\"%s\"\n",
+ env_get("LOGNAME", e->envp),
+ e->uid, e->gid, e->cmd))
+
+ if ( diff != 0 && (e->flags & (RUN_AT|NOT_UNTIL)) ) {
+ if (bit_test(e->minute, otzminute)
+ && bit_test(e->hour, otzhour)
+ && bit_test(e->month, otzmonth)
+ && ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR))
+ ? (bit_test(e->dow,otzdow) && bit_test(e->dom,otzdom))
+ : (bit_test(e->dow,otzdow) || bit_test(e->dom,otzdom))
+ )
+ ) {
+ if ( e->flags & RUN_AT ) {
+ e->flags &= ~RUN_AT;
+ e->lastrun = TargetTime;
+ job_add(e, u);
+ continue;
+ } else
+ e->flags &= ~NOT_UNTIL;
+ } else if ( e->flags & NOT_UNTIL )
+ continue;
+ }
+
+ if (bit_test(e->minute, minute)
+ && bit_test(e->hour, hour)
+ && bit_test(e->month, month)
+ && ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR))
+ ? (bit_test(e->dow,dow) && bit_test(e->dom,dom))
+ : (bit_test(e->dow,dow) || bit_test(e->dom,dom))
+ )
+ ) {
+ e->flags &= ~RUN_AT;
+ e->lastrun = TargetTime;
+ job_add(e, u);
+ }
+ }
+ }
+
+ last_time = TargetTime;
+ lasttm = *tm;
+}
+
+
+/* the task here is to figure out how long it's going to be until :00 of the
+ * following minute and initialize TargetTime to this value. TargetTime
+ * will subsequently slide 60 seconds at a time, with correction applied
+ * implicitly in cron_sleep(). it would be nice to let cron execute in
+ * the "current minute" before going to sleep, but by restarting cron you
+ * could then get it to execute a given minute's jobs more than once.
+ * instead we have the chance of missing a minute's jobs completely, but
+ * that's something sysadmin's know to expect what with crashing computers..
+ */
+static void
+cron_sync() {
+ register struct tm *tm;
+
+ TargetTime = time((time_t*)0);
+ tm = localtime(&TargetTime);
+ TargetTime += (60 - tm->tm_sec);
+}
+
+
+static void
+cron_sleep(db)
+ cron_db *db;
+{
+ int seconds_to_wait = 0;
+
+ /*
+ * Loop until we reach the top of the next minute, sleep when possible.
+ */
+
+ for (;;) {
+ seconds_to_wait = (int) (TargetTime - time((time_t*)0));
+
+ /*
+ * If the seconds_to_wait value is insane, jump the cron
+ */
+
+ if (seconds_to_wait < -600 || seconds_to_wait > 600) {
+ cron_clean(db);
+ cron_sync();
+ continue;
+ }
+
+ Debug(DSCH, ("[%d] TargetTime=%ld, sec-to-wait=%d\n",
+ getpid(), (long)TargetTime, seconds_to_wait))
+
+ /*
+ * If we've run out of wait time or there are no jobs left
+ * to run, break
+ */
+
+ if (seconds_to_wait <= 0)
+ break;
+ if (job_runqueue() == 0) {
+ Debug(DSCH, ("[%d] sleeping for %d seconds\n",
+ getpid(), seconds_to_wait))
+
+ sleep(seconds_to_wait);
+ }
+ }
+}
+
+
+/* if the time was changed abruptly, clear the flags related
+ * to the daylight time switch handling to avoid strange effects
+ */
+
+static void
+cron_clean(db)
+ cron_db *db;
+{
+ user *u;
+ entry *e;
+
+ last_time = 0;
+
+ for (u = db->head; u != NULL; u = u->next) {
+ for (e = u->crontab; e != NULL; e = e->next) {
+ e->flags &= ~(RUN_AT|NOT_UNTIL);
+ }
+ }
+}
+
+#ifdef USE_SIGCHLD
+static void
+sigchld_handler(int x)
+{
+ WAIT_T waiter;
+ PID_T pid;
+
+ for (;;) {
+#ifdef POSIX
+ pid = waitpid(-1, &waiter, WNOHANG);
+#else
+ pid = wait3(&waiter, WNOHANG, (struct rusage *)0);
+#endif
+ switch (pid) {
+ case -1:
+ Debug(DPROC,
+ ("[%d] sigchld...no children\n", getpid()))
+ return;
+ case 0:
+ Debug(DPROC,
+ ("[%d] sigchld...no dead kids\n", getpid()))
+ return;
+ default:
+ Debug(DPROC,
+ ("[%d] sigchld...pid #%d died, stat=%d\n",
+ getpid(), pid, WEXITSTATUS(waiter)))
+ }
+ }
+}
+#endif /*USE_SIGCHLD*/
+
+
+static void
+sighup_handler(int x)
+{
+ log_close();
+}
+
+
+static void
+parse_args(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int argch;
+ char *endp;
+
+ while ((argch = getopt(argc, argv, "j:J:m:osx:")) != -1) {
+ switch (argch) {
+ case 'j':
+ Jitter = strtoul(optarg, &endp, 10);
+ if (*optarg == '\0' || *endp != '\0' || Jitter > 60)
+ errx(ERROR_EXIT,
+ "bad value for jitter: %s", optarg);
+ break;
+ case 'J':
+ RootJitter = strtoul(optarg, &endp, 10);
+ if (*optarg == '\0' || *endp != '\0' || RootJitter > 60)
+ errx(ERROR_EXIT,
+ "bad value for root jitter: %s", optarg);
+ break;
+ case 'm':
+ defmailto = optarg;
+ break;
+ case 'o':
+ dst_enabled = 0;
+ break;
+ case 's':
+ dst_enabled = 1;
+ break;
+ case 'x':
+ if (!set_debug_flags(optarg))
+ usage();
+ break;
+ default:
+ usage();
+ }
+ }
+}
+
diff --git a/usr.sbin/cron/cron/cron.h b/usr.sbin/cron/cron/cron.h
new file mode 100644
index 0000000..38eb33b
--- /dev/null
+++ b/usr.sbin/cron/cron/cron.h
@@ -0,0 +1,301 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+/* cron.h - header for vixie's cron
+ *
+ * $FreeBSD$
+ *
+ * vix 14nov88 [rest of log is in RCS]
+ * vix 14jan87 [0 or 7 can be sunday; thanks, mwm@berkeley]
+ * vix 30dec86 [written]
+ */
+
+/* reorder these #include's at your peril */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include "compat.h"
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <libutil.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <time.h>
+#include <sys/wait.h>
+
+#include "pathnames.h"
+#include "config.h"
+#include "externs.h"
+
+ /* these are really immutable, and are
+ * defined for symbolic convenience only
+ * TRUE, FALSE, and ERR must be distinct
+ * ERR must be < OK.
+ */
+#define TRUE 1
+#define FALSE 0
+ /* system calls return this on success */
+#define OK 0
+ /* or this on error */
+#define ERR (-1)
+
+ /* turn this on to get '-x' code */
+#ifndef DEBUGGING
+#define DEBUGGING FALSE
+#endif
+
+#define READ_PIPE 0 /* which end of a pipe pair do you read? */
+#define WRITE_PIPE 1 /* or write to? */
+#define STDIN 0 /* what is stdin's file descriptor? */
+#define STDOUT 1 /* stdout's? */
+#define STDERR 2 /* stderr's? */
+#define ERROR_EXIT 1 /* exit() with this will scare the shell */
+#define OK_EXIT 0 /* exit() with this is considered 'normal' */
+#define MAX_FNAME 100 /* max length of internally generated fn */
+#define MAX_COMMAND 1000 /* max length of internally generated cmd */
+#define MAX_ENVSTR 1000 /* max length of envvar=value\0 strings */
+#define MAX_TEMPSTR 100 /* obvious */
+#define MAX_UNAME 20 /* max length of username, should be overkill */
+#define ROOT_UID 0 /* don't change this, it really must be root */
+#define ROOT_USER "root" /* ditto */
+#define SYS_NAME "*system*" /* magic owner name for system crontab */
+
+ /* NOTE: these correspond to DebugFlagNames,
+ * defined below.
+ */
+#define DEXT 0x0001 /* extend flag for other debug masks */
+#define DSCH 0x0002 /* scheduling debug mask */
+#define DPROC 0x0004 /* process control debug mask */
+#define DPARS 0x0008 /* parsing debug mask */
+#define DLOAD 0x0010 /* database loading debug mask */
+#define DMISC 0x0020 /* misc debug mask */
+#define DTEST 0x0040 /* test mode: don't execute any commands */
+#define DBIT 0x0080 /* bit twiddling shown (long) */
+
+#define CRON_TAB(u) "%s/%s", SPOOL_DIR, u
+#define REG register
+#define PPC_NULL ((char **)NULL)
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 256
+#endif
+
+#define Skip_Blanks(c, f) \
+ while (c == '\t' || c == ' ') \
+ c = get_char(f);
+
+#define Skip_Nonblanks(c, f) \
+ while (c!='\t' && c!=' ' && c!='\n' && c != EOF) \
+ c = get_char(f);
+
+#define Skip_Line(c, f) \
+ do {c = get_char(f);} while (c != '\n' && c != EOF);
+
+#if DEBUGGING
+# define Debug(mask, message) \
+ if ( (DebugFlags & (mask) ) == (mask) ) \
+ printf message;
+#else /* !DEBUGGING */
+# define Debug(mask, message) \
+ ;
+#endif /* DEBUGGING */
+
+#define MkLower(ch) (isupper(ch) ? tolower(ch) : ch)
+#define MkUpper(ch) (islower(ch) ? toupper(ch) : ch)
+#define Set_LineNum(ln) {Debug(DPARS|DEXT,("linenum=%d\n",ln)); \
+ LineNumber = ln; \
+ }
+
+#define FIRST_MINUTE 0
+#define LAST_MINUTE 59
+#define MINUTE_COUNT (LAST_MINUTE - FIRST_MINUTE + 1)
+
+#define FIRST_HOUR 0
+#define LAST_HOUR 23
+#define HOUR_COUNT (LAST_HOUR - FIRST_HOUR + 1)
+
+#define FIRST_DOM 1
+#define LAST_DOM 31
+#define DOM_COUNT (LAST_DOM - FIRST_DOM + 1)
+
+#define FIRST_MONTH 1
+#define LAST_MONTH 12
+#define MONTH_COUNT (LAST_MONTH - FIRST_MONTH + 1)
+
+/* note on DOW: 0 and 7 are both Sunday, for compatibility reasons. */
+#define FIRST_DOW 0
+#define LAST_DOW 7
+#define DOW_COUNT (LAST_DOW - FIRST_DOW + 1)
+
+#ifdef LOGIN_CAP
+/* see init.c */
+#define RESOURCE_RC "daemon"
+#endif
+
+ /* each user's crontab will be held as a list of
+ * the following structure.
+ *
+ * These are the cron commands.
+ */
+
+typedef struct _entry {
+ struct _entry *next;
+ uid_t uid;
+ gid_t gid;
+#ifdef LOGIN_CAP
+ char *class;
+#endif
+ char **envp;
+ char *cmd;
+ bitstr_t bit_decl(minute, MINUTE_COUNT);
+ bitstr_t bit_decl(hour, HOUR_COUNT);
+ bitstr_t bit_decl(dom, DOM_COUNT);
+ bitstr_t bit_decl(month, MONTH_COUNT);
+ bitstr_t bit_decl(dow, DOW_COUNT);
+ int flags;
+#define DOM_STAR 0x01
+#define DOW_STAR 0x02
+#define WHEN_REBOOT 0x04
+#define RUN_AT 0x08
+#define NOT_UNTIL 0x10
+ time_t lastrun;
+} entry;
+
+ /* the crontab database will be a list of the
+ * following structure, one element per user
+ * plus one for the system.
+ *
+ * These are the crontabs.
+ */
+
+typedef struct _user {
+ struct _user *next, *prev; /* links */
+ char *name;
+ time_t mtime; /* last modtime of crontab */
+ entry *crontab; /* this person's crontab */
+} user;
+
+typedef struct _cron_db {
+ user *head, *tail; /* links */
+ time_t mtime; /* last modtime on spooldir */
+} cron_db;
+
+
+void set_cron_uid(void),
+ set_cron_cwd(void),
+ load_database(cron_db *),
+ open_logfile(void),
+ sigpipe_func(void),
+ job_add(entry *, user *),
+ do_command(entry *, user *),
+ link_user(cron_db *, user *),
+ unlink_user(cron_db *, user *),
+ free_user(user *),
+ env_free(char **),
+ unget_char(int, FILE *),
+ free_entry(entry *),
+ skip_comments(FILE *),
+ log_it(char *, int, char *, char *),
+ log_close(void);
+
+int job_runqueue(void),
+ set_debug_flags(char *),
+ get_char(FILE *),
+ get_string(char *, int, FILE *, char *),
+ swap_uids(void),
+ swap_uids_back(void),
+ load_env(char *, FILE *),
+ cron_pclose(FILE *),
+ strcmp_until(char *, char *, int),
+ allowed(char *),
+ strdtb(char *);
+
+char *env_get(char *, char **),
+ *arpadate(time_t *),
+ *mkprints(unsigned char *, unsigned int),
+ *first_word(char *, char *),
+ **env_init(void),
+ **env_copy(char **),
+ **env_set(char **, char *);
+
+user *load_user(int, struct passwd *, char *),
+ *find_user(cron_db *, char *);
+
+entry *load_entry(FILE *, void (*)(char *),
+ struct passwd *, char **);
+
+FILE *cron_popen(char *, char *, entry *);
+
+
+ /* in the C tradition, we only create
+ * variables for the main program, just
+ * extern them elsewhere.
+ */
+
+#ifdef MAIN_PROGRAM
+# if !defined(LINT) && !defined(lint)
+char *copyright[] = {
+ "@(#) Copyright 1988,1989,1990,1993,1994 by Paul Vixie",
+ "@(#) All rights reserved"
+ };
+# endif
+
+char *MonthNames[] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+ NULL
+ };
+
+char *DowNames[] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun",
+ NULL
+ };
+
+char *ProgramName,
+ *defmailto;
+int LineNumber;
+unsigned Jitter,
+ RootJitter;
+time_t TargetTime;
+
+# if DEBUGGING
+int DebugFlags;
+char *DebugFlagNames[] = { /* sync with #defines */
+ "ext", "sch", "proc", "pars", "load", "misc", "test", "bit",
+ NULL /* NULL must be last element */
+ };
+# endif /* DEBUGGING */
+#else /*MAIN_PROGRAM*/
+extern char *copyright[],
+ *MonthNames[],
+ *DowNames[],
+ *ProgramName,
+ *defmailto;
+extern int LineNumber;
+extern unsigned Jitter,
+ RootJitter;
+extern time_t TargetTime;
+extern struct pidfh *pfh;
+# if DEBUGGING
+extern int DebugFlags;
+extern char *DebugFlagNames[];
+# endif /* DEBUGGING */
+#endif /*MAIN_PROGRAM*/
diff --git a/usr.sbin/cron/cron/database.c b/usr.sbin/cron/cron/database.c
new file mode 100644
index 0000000..7a44d14
--- /dev/null
+++ b/usr.sbin/cron/cron/database.c
@@ -0,0 +1,263 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if !defined(lint) && !defined(LINT)
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+
+/* vix 26jan87 [RCS has the log]
+ */
+
+
+#include "cron.h"
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+
+
+#define TMAX(a,b) ((a)>(b)?(a):(b))
+
+
+static void process_crontab(char *, char *, char *,
+ struct stat *,
+ cron_db *, cron_db *);
+
+
+void
+load_database(old_db)
+ cron_db *old_db;
+{
+ DIR *dir;
+ struct stat statbuf;
+ struct stat syscron_stat;
+ DIR_T *dp;
+ cron_db new_db;
+ user *u, *nu;
+
+ Debug(DLOAD, ("[%d] load_database()\n", getpid()))
+
+ /* before we start loading any data, do a stat on SPOOL_DIR
+ * so that if anything changes as of this moment (i.e., before we've
+ * cached any of the database), we'll see the changes next time.
+ */
+ if (stat(SPOOL_DIR, &statbuf) < OK) {
+ log_it("CRON", getpid(), "STAT FAILED", SPOOL_DIR);
+ (void) exit(ERROR_EXIT);
+ }
+
+ /* track system crontab file
+ */
+ if (stat(SYSCRONTAB, &syscron_stat) < OK)
+ syscron_stat.st_mtime = 0;
+
+ /* if spooldir's mtime has not changed, we don't need to fiddle with
+ * the database.
+ *
+ * Note that old_db->mtime is initialized to 0 in main(), and
+ * so is guaranteed to be different than the stat() mtime the first
+ * time this function is called.
+ */
+ if (old_db->mtime == TMAX(statbuf.st_mtime, syscron_stat.st_mtime)) {
+ Debug(DLOAD, ("[%d] spool dir mtime unch, no load needed.\n",
+ getpid()))
+ return;
+ }
+
+ /* something's different. make a new database, moving unchanged
+ * elements from the old database, reloading elements that have
+ * actually changed. Whatever is left in the old database when
+ * we're done is chaff -- crontabs that disappeared.
+ */
+ new_db.mtime = TMAX(statbuf.st_mtime, syscron_stat.st_mtime);
+ new_db.head = new_db.tail = NULL;
+
+ if (syscron_stat.st_mtime) {
+ process_crontab("root", SYS_NAME,
+ SYSCRONTAB, &syscron_stat,
+ &new_db, old_db);
+ }
+
+ /* we used to keep this dir open all the time, for the sake of
+ * efficiency. however, we need to close it in every fork, and
+ * we fork a lot more often than the mtime of the dir changes.
+ */
+ if (!(dir = opendir(SPOOL_DIR))) {
+ log_it("CRON", getpid(), "OPENDIR FAILED", SPOOL_DIR);
+ (void) exit(ERROR_EXIT);
+ }
+
+ while (NULL != (dp = readdir(dir))) {
+ char fname[MAXNAMLEN+1],
+ tabname[MAXNAMLEN+1];
+
+ /* avoid file names beginning with ".". this is good
+ * because we would otherwise waste two guaranteed calls
+ * to getpwnam() for . and .., and also because user names
+ * starting with a period are just too nasty to consider.
+ */
+ if (dp->d_name[0] == '.')
+ continue;
+
+ (void) strncpy(fname, dp->d_name, sizeof(fname));
+ fname[sizeof(fname)-1] = '\0';
+ (void) snprintf(tabname, sizeof tabname, CRON_TAB(fname));
+
+ process_crontab(fname, fname, tabname,
+ &statbuf, &new_db, old_db);
+ }
+ closedir(dir);
+
+ /* if we don't do this, then when our children eventually call
+ * getpwnam() in do_command.c's child_process to verify MAILTO=,
+ * they will screw us up (and v-v).
+ */
+ endpwent();
+
+ /* whatever's left in the old database is now junk.
+ */
+ Debug(DLOAD, ("unlinking old database:\n"))
+ for (u = old_db->head; u != NULL; u = nu) {
+ Debug(DLOAD, ("\t%s\n", u->name))
+ nu = u->next;
+ unlink_user(old_db, u);
+ free_user(u);
+ }
+
+ /* overwrite the database control block with the new one.
+ */
+ *old_db = new_db;
+ Debug(DLOAD, ("load_database is done\n"))
+}
+
+
+void
+link_user(db, u)
+ cron_db *db;
+ user *u;
+{
+ if (db->head == NULL)
+ db->head = u;
+ if (db->tail)
+ db->tail->next = u;
+ u->prev = db->tail;
+ u->next = NULL;
+ db->tail = u;
+}
+
+
+void
+unlink_user(db, u)
+ cron_db *db;
+ user *u;
+{
+ if (u->prev == NULL)
+ db->head = u->next;
+ else
+ u->prev->next = u->next;
+
+ if (u->next == NULL)
+ db->tail = u->prev;
+ else
+ u->next->prev = u->prev;
+}
+
+
+user *
+find_user(db, name)
+ cron_db *db;
+ char *name;
+{
+ char *env_get();
+ user *u;
+
+ for (u = db->head; u != NULL; u = u->next)
+ if (!strcmp(u->name, name))
+ break;
+ return u;
+}
+
+
+static void
+process_crontab(uname, fname, tabname, statbuf, new_db, old_db)
+ char *uname;
+ char *fname;
+ char *tabname;
+ struct stat *statbuf;
+ cron_db *new_db;
+ cron_db *old_db;
+{
+ struct passwd *pw = NULL;
+ int crontab_fd = OK - 1;
+ user *u;
+
+ if (strcmp(fname, SYS_NAME) && !(pw = getpwnam(uname))) {
+ /* file doesn't have a user in passwd file.
+ */
+ log_it(fname, getpid(), "ORPHAN", "no passwd entry");
+ goto next_crontab;
+ }
+
+ if ((crontab_fd = open(tabname, O_RDONLY, 0)) < OK) {
+ /* crontab not accessible?
+ */
+ log_it(fname, getpid(), "CAN'T OPEN", tabname);
+ goto next_crontab;
+ }
+
+ if (fstat(crontab_fd, statbuf) < OK) {
+ log_it(fname, getpid(), "FSTAT FAILED", tabname);
+ goto next_crontab;
+ }
+
+ Debug(DLOAD, ("\t%s:", fname))
+ u = find_user(old_db, fname);
+ if (u != NULL) {
+ /* if crontab has not changed since we last read it
+ * in, then we can just use our existing entry.
+ */
+ if (u->mtime == statbuf->st_mtime) {
+ Debug(DLOAD, (" [no change, using old data]"))
+ unlink_user(old_db, u);
+ link_user(new_db, u);
+ goto next_crontab;
+ }
+
+ /* before we fall through to the code that will reload
+ * the user, let's deallocate and unlink the user in
+ * the old database. This is more a point of memory
+ * efficiency than anything else, since all leftover
+ * users will be deleted from the old database when
+ * we finish with the crontab...
+ */
+ Debug(DLOAD, (" [delete old data]"))
+ unlink_user(old_db, u);
+ free_user(u);
+ log_it(fname, getpid(), "RELOAD", tabname);
+ }
+ u = load_user(crontab_fd, pw, fname);
+ if (u != NULL) {
+ u->mtime = statbuf->st_mtime;
+ link_user(new_db, u);
+ }
+
+next_crontab:
+ if (crontab_fd >= OK) {
+ Debug(DLOAD, (" [done]\n"))
+ close(crontab_fd);
+ }
+}
diff --git a/usr.sbin/cron/cron/do_command.c b/usr.sbin/cron/cron/do_command.c
new file mode 100644
index 0000000..daf695f
--- /dev/null
+++ b/usr.sbin/cron/cron/do_command.c
@@ -0,0 +1,617 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if !defined(lint) && !defined(LINT)
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+
+
+#include "cron.h"
+#include <sys/signal.h>
+#if defined(sequent)
+# include <sys/universe.h>
+#endif
+#if defined(SYSLOG)
+# include <syslog.h>
+#endif
+#if defined(LOGIN_CAP)
+# include <login_cap.h>
+#endif
+#ifdef PAM
+# include <security/pam_appl.h>
+# include <security/openpam.h>
+#endif
+
+
+static void child_process(entry *, user *),
+ do_univ(user *);
+
+
+void
+do_command(e, u)
+ entry *e;
+ user *u;
+{
+ Debug(DPROC, ("[%d] do_command(%s, (%s,%d,%d))\n",
+ getpid(), e->cmd, u->name, e->uid, e->gid))
+
+ /* fork to become asynchronous -- parent process is done immediately,
+ * and continues to run the normal cron code, which means return to
+ * tick(). the child and grandchild don't leave this function, alive.
+ *
+ * vfork() is unsuitable, since we have much to do, and the parent
+ * needs to be able to run off and fork other processes.
+ */
+ switch (fork()) {
+ case -1:
+ log_it("CRON",getpid(),"error","can't fork");
+ break;
+ case 0:
+ /* child process */
+ pidfile_close(pfh);
+ child_process(e, u);
+ Debug(DPROC, ("[%d] child process done, exiting\n", getpid()))
+ _exit(OK_EXIT);
+ break;
+ default:
+ /* parent process */
+ break;
+ }
+ Debug(DPROC, ("[%d] main process returning to work\n", getpid()))
+}
+
+
+static void
+child_process(e, u)
+ entry *e;
+ user *u;
+{
+ int stdin_pipe[2], stdout_pipe[2];
+ register char *input_data;
+ char *usernm, *mailto;
+ int children = 0;
+# if defined(LOGIN_CAP)
+ struct passwd *pwd;
+ login_cap_t *lc;
+# endif
+
+ Debug(DPROC, ("[%d] child_process('%s')\n", getpid(), e->cmd))
+
+ /* mark ourselves as different to PS command watchers by upshifting
+ * our program name. This has no effect on some kernels.
+ */
+ setproctitle("running job");
+
+ /* discover some useful and important environment settings
+ */
+ usernm = env_get("LOGNAME", e->envp);
+ mailto = env_get("MAILTO", e->envp);
+
+#ifdef PAM
+ /* use PAM to see if the user's account is available,
+ * i.e., not locked or expired or whatever. skip this
+ * for system tasks from /etc/crontab -- they can run
+ * as any user.
+ */
+ if (strcmp(u->name, SYS_NAME)) { /* not equal */
+ pam_handle_t *pamh = NULL;
+ int pam_err;
+ struct pam_conv pamc = {
+ .conv = openpam_nullconv,
+ .appdata_ptr = NULL
+ };
+
+ Debug(DPROC, ("[%d] checking account with PAM\n", getpid()))
+
+ /* u->name keeps crontab owner name while LOGNAME is the name
+ * of user to run command on behalf of. they should be the
+ * same for a task from a per-user crontab.
+ */
+ if (strcmp(u->name, usernm)) {
+ log_it(usernm, getpid(), "username ambiguity", u->name);
+ exit(ERROR_EXIT);
+ }
+
+ pam_err = pam_start("cron", usernm, &pamc, &pamh);
+ if (pam_err != PAM_SUCCESS) {
+ log_it("CRON", getpid(), "error", "can't start PAM");
+ exit(ERROR_EXIT);
+ }
+
+ pam_err = pam_acct_mgmt(pamh, PAM_SILENT);
+ /* Expired password shouldn't prevent the job from running. */
+ if (pam_err != PAM_SUCCESS && pam_err != PAM_NEW_AUTHTOK_REQD) {
+ log_it(usernm, getpid(), "USER", "account unavailable");
+ exit(ERROR_EXIT);
+ }
+
+ pam_end(pamh, pam_err);
+ }
+#endif
+
+#ifdef USE_SIGCHLD
+ /* our parent is watching for our death by catching SIGCHLD. we
+ * do not care to watch for our children's deaths this way -- we
+ * use wait() explictly. so we have to disable the signal (which
+ * was inherited from the parent).
+ */
+ (void) signal(SIGCHLD, SIG_DFL);
+#else
+ /* on system-V systems, we are ignoring SIGCLD. we have to stop
+ * ignoring it now or the wait() in cron_pclose() won't work.
+ * because of this, we have to wait() for our children here, as well.
+ */
+ (void) signal(SIGCLD, SIG_DFL);
+#endif /*BSD*/
+
+ /* create some pipes to talk to our future child
+ */
+ pipe(stdin_pipe); /* child's stdin */
+ pipe(stdout_pipe); /* child's stdout */
+
+ /* since we are a forked process, we can diddle the command string
+ * we were passed -- nobody else is going to use it again, right?
+ *
+ * if a % is present in the command, previous characters are the
+ * command, and subsequent characters are the additional input to
+ * the command. Subsequent %'s will be transformed into newlines,
+ * but that happens later.
+ *
+ * If there are escaped %'s, remove the escape character.
+ */
+ /*local*/{
+ register int escaped = FALSE;
+ register int ch;
+ register char *p;
+
+ for (input_data = p = e->cmd; (ch = *input_data);
+ input_data++, p++) {
+ if (p != input_data)
+ *p = ch;
+ if (escaped) {
+ if (ch == '%' || ch == '\\')
+ *--p = ch;
+ escaped = FALSE;
+ continue;
+ }
+ if (ch == '\\') {
+ escaped = TRUE;
+ continue;
+ }
+ if (ch == '%') {
+ *input_data++ = '\0';
+ break;
+ }
+ }
+ *p = '\0';
+ }
+
+ /* fork again, this time so we can exec the user's command.
+ */
+ switch (vfork()) {
+ case -1:
+ log_it("CRON",getpid(),"error","can't vfork");
+ exit(ERROR_EXIT);
+ /*NOTREACHED*/
+ case 0:
+ Debug(DPROC, ("[%d] grandchild process Vfork()'ed\n",
+ getpid()))
+
+ if (e->uid == ROOT_UID)
+ Jitter = RootJitter;
+ if (Jitter != 0) {
+ srandom(getpid());
+ sleep(random() % Jitter);
+ }
+
+ /* write a log message. we've waited this long to do it
+ * because it was not until now that we knew the PID that
+ * the actual user command shell was going to get and the
+ * PID is part of the log message.
+ */
+ /*local*/{
+ char *x = mkprints((u_char *)e->cmd, strlen(e->cmd));
+
+ log_it(usernm, getpid(), "CMD", x);
+ free(x);
+ }
+
+ /* that's the last thing we'll log. close the log files.
+ */
+#ifdef SYSLOG
+ closelog();
+#endif
+
+ /* get new pgrp, void tty, etc.
+ */
+ (void) setsid();
+
+ /* close the pipe ends that we won't use. this doesn't affect
+ * the parent, who has to read and write them; it keeps the
+ * kernel from recording us as a potential client TWICE --
+ * which would keep it from sending SIGPIPE in otherwise
+ * appropriate circumstances.
+ */
+ close(stdin_pipe[WRITE_PIPE]);
+ close(stdout_pipe[READ_PIPE]);
+
+ /* grandchild process. make std{in,out} be the ends of
+ * pipes opened by our daddy; make stderr go to stdout.
+ */
+ close(STDIN); dup2(stdin_pipe[READ_PIPE], STDIN);
+ close(STDOUT); dup2(stdout_pipe[WRITE_PIPE], STDOUT);
+ close(STDERR); dup2(STDOUT, STDERR);
+
+ /* close the pipes we just dup'ed. The resources will remain.
+ */
+ close(stdin_pipe[READ_PIPE]);
+ close(stdout_pipe[WRITE_PIPE]);
+
+ /* set our login universe. Do this in the grandchild
+ * so that the child can invoke /usr/lib/sendmail
+ * without surprises.
+ */
+ do_univ(u);
+
+# if defined(LOGIN_CAP)
+ /* Set user's entire context, but skip the environment
+ * as cron provides a separate interface for this
+ */
+ if ((pwd = getpwnam(usernm)) == NULL)
+ pwd = getpwuid(e->uid);
+ lc = NULL;
+ if (pwd != NULL) {
+ pwd->pw_gid = e->gid;
+ if (e->class != NULL)
+ lc = login_getclass(e->class);
+ }
+ if (pwd &&
+ setusercontext(lc, pwd, e->uid,
+ LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETENV)) == 0)
+ (void) endpwent();
+ else {
+ /* fall back to the old method */
+ (void) endpwent();
+# endif
+ /* set our directory, uid and gid. Set gid first,
+ * since once we set uid, we've lost root privileges.
+ */
+ if (setgid(e->gid) != 0) {
+ log_it(usernm, getpid(),
+ "error", "setgid failed");
+ exit(ERROR_EXIT);
+ }
+# if defined(BSD)
+ if (initgroups(usernm, e->gid) != 0) {
+ log_it(usernm, getpid(),
+ "error", "initgroups failed");
+ exit(ERROR_EXIT);
+ }
+# endif
+ if (setlogin(usernm) != 0) {
+ log_it(usernm, getpid(),
+ "error", "setlogin failed");
+ exit(ERROR_EXIT);
+ }
+ if (setuid(e->uid) != 0) {
+ log_it(usernm, getpid(),
+ "error", "setuid failed");
+ exit(ERROR_EXIT);
+ }
+ /* we aren't root after this..*/
+#if defined(LOGIN_CAP)
+ }
+ if (lc != NULL)
+ login_close(lc);
+#endif
+ chdir(env_get("HOME", e->envp));
+
+ /* exec the command.
+ */
+ {
+ char *shell = env_get("SHELL", e->envp);
+
+# if DEBUGGING
+ if (DebugFlags & DTEST) {
+ fprintf(stderr,
+ "debug DTEST is on, not exec'ing command.\n");
+ fprintf(stderr,
+ "\tcmd='%s' shell='%s'\n", e->cmd, shell);
+ _exit(OK_EXIT);
+ }
+# endif /*DEBUGGING*/
+ execle(shell, shell, "-c", e->cmd, (char *)0, e->envp);
+ warn("execl: couldn't exec `%s'", shell);
+ _exit(ERROR_EXIT);
+ }
+ break;
+ default:
+ /* parent process */
+ break;
+ }
+
+ children++;
+
+ /* middle process, child of original cron, parent of process running
+ * the user's command.
+ */
+
+ Debug(DPROC, ("[%d] child continues, closing pipes\n", getpid()))
+
+ /* close the ends of the pipe that will only be referenced in the
+ * grandchild process...
+ */
+ close(stdin_pipe[READ_PIPE]);
+ close(stdout_pipe[WRITE_PIPE]);
+
+ /*
+ * write, to the pipe connected to child's stdin, any input specified
+ * after a % in the crontab entry. while we copy, convert any
+ * additional %'s to newlines. when done, if some characters were
+ * written and the last one wasn't a newline, write a newline.
+ *
+ * Note that if the input data won't fit into one pipe buffer (2K
+ * or 4K on most BSD systems), and the child doesn't read its stdin,
+ * we would block here. thus we must fork again.
+ */
+
+ if (*input_data && fork() == 0) {
+ register FILE *out = fdopen(stdin_pipe[WRITE_PIPE], "w");
+ register int need_newline = FALSE;
+ register int escaped = FALSE;
+ register int ch;
+
+ if (out == NULL) {
+ warn("fdopen failed in child2");
+ _exit(ERROR_EXIT);
+ }
+
+ Debug(DPROC, ("[%d] child2 sending data to grandchild\n", getpid()))
+
+ /* close the pipe we don't use, since we inherited it and
+ * are part of its reference count now.
+ */
+ close(stdout_pipe[READ_PIPE]);
+
+ /* translation:
+ * \% -> %
+ * % -> \n
+ * \x -> \x for all x != %
+ */
+ while ((ch = *input_data++)) {
+ if (escaped) {
+ if (ch != '%')
+ putc('\\', out);
+ } else {
+ if (ch == '%')
+ ch = '\n';
+ }
+
+ if (!(escaped = (ch == '\\'))) {
+ putc(ch, out);
+ need_newline = (ch != '\n');
+ }
+ }
+ if (escaped)
+ putc('\\', out);
+ if (need_newline)
+ putc('\n', out);
+
+ /* close the pipe, causing an EOF condition. fclose causes
+ * stdin_pipe[WRITE_PIPE] to be closed, too.
+ */
+ fclose(out);
+
+ Debug(DPROC, ("[%d] child2 done sending to grandchild\n", getpid()))
+ exit(0);
+ }
+
+ /* close the pipe to the grandkiddie's stdin, since its wicked uncle
+ * ernie back there has it open and will close it when he's done.
+ */
+ close(stdin_pipe[WRITE_PIPE]);
+
+ children++;
+
+ /*
+ * read output from the grandchild. it's stderr has been redirected to
+ * it's stdout, which has been redirected to our pipe. if there is any
+ * output, we'll be mailing it to the user whose crontab this is...
+ * when the grandchild exits, we'll get EOF.
+ */
+
+ Debug(DPROC, ("[%d] child reading output from grandchild\n", getpid()))
+
+ /*local*/{
+ register FILE *in = fdopen(stdout_pipe[READ_PIPE], "r");
+ register int ch;
+
+ if (in == NULL) {
+ warn("fdopen failed in child");
+ _exit(ERROR_EXIT);
+ }
+
+ ch = getc(in);
+ if (ch != EOF) {
+ register FILE *mail;
+ register int bytes = 1;
+ int status = 0;
+
+ Debug(DPROC|DEXT,
+ ("[%d] got data (%x:%c) from grandchild\n",
+ getpid(), ch, ch))
+
+ /* get name of recipient. this is MAILTO if set to a
+ * valid local username; USER otherwise.
+ */
+ if (mailto == NULL) {
+ /* MAILTO not present, set to USER,
+ * unless globally overriden.
+ */
+ if (defmailto)
+ mailto = defmailto;
+ else
+ mailto = usernm;
+ }
+ if (mailto && *mailto == '\0')
+ mailto = NULL;
+
+ /* if we are supposed to be mailing, MAILTO will
+ * be non-NULL. only in this case should we set
+ * up the mail command and subjects and stuff...
+ */
+
+ if (mailto) {
+ register char **env;
+ auto char mailcmd[MAX_COMMAND];
+ auto char hostname[MAXHOSTNAMELEN];
+
+ (void) gethostname(hostname, MAXHOSTNAMELEN);
+ (void) snprintf(mailcmd, sizeof(mailcmd),
+ MAILARGS, MAILCMD);
+ if (!(mail = cron_popen(mailcmd, "w", e))) {
+ warn("%s", MAILCMD);
+ (void) _exit(ERROR_EXIT);
+ }
+ fprintf(mail, "From: %s (Cron Daemon)\n", usernm);
+ fprintf(mail, "To: %s\n", mailto);
+ fprintf(mail, "Subject: Cron <%s@%s> %s\n",
+ usernm, first_word(hostname, "."),
+ e->cmd);
+# if defined(MAIL_DATE)
+ fprintf(mail, "Date: %s\n",
+ arpadate(&TargetTime));
+# endif /* MAIL_DATE */
+ for (env = e->envp; *env; env++)
+ fprintf(mail, "X-Cron-Env: <%s>\n",
+ *env);
+ fprintf(mail, "\n");
+
+ /* this was the first char from the pipe
+ */
+ putc(ch, mail);
+ }
+
+ /* we have to read the input pipe no matter whether
+ * we mail or not, but obviously we only write to
+ * mail pipe if we ARE mailing.
+ */
+
+ while (EOF != (ch = getc(in))) {
+ bytes++;
+ if (mailto)
+ putc(ch, mail);
+ }
+
+ /* only close pipe if we opened it -- i.e., we're
+ * mailing...
+ */
+
+ if (mailto) {
+ Debug(DPROC, ("[%d] closing pipe to mail\n",
+ getpid()))
+ /* Note: the pclose will probably see
+ * the termination of the grandchild
+ * in addition to the mail process, since
+ * it (the grandchild) is likely to exit
+ * after closing its stdout.
+ */
+ status = cron_pclose(mail);
+ }
+
+ /* if there was output and we could not mail it,
+ * log the facts so the poor user can figure out
+ * what's going on.
+ */
+ if (mailto && status) {
+ char buf[MAX_TEMPSTR];
+
+ snprintf(buf, sizeof(buf),
+ "mailed %d byte%s of output but got status 0x%04x\n",
+ bytes, (bytes==1)?"":"s",
+ status);
+ log_it(usernm, getpid(), "MAIL", buf);
+ }
+
+ } /*if data from grandchild*/
+
+ Debug(DPROC, ("[%d] got EOF from grandchild\n", getpid()))
+
+ fclose(in); /* also closes stdout_pipe[READ_PIPE] */
+ }
+
+ /* wait for children to die.
+ */
+ for (; children > 0; children--)
+ {
+ WAIT_T waiter;
+ PID_T pid;
+
+ Debug(DPROC, ("[%d] waiting for grandchild #%d to finish\n",
+ getpid(), children))
+ pid = wait(&waiter);
+ if (pid < OK) {
+ Debug(DPROC, ("[%d] no more grandchildren--mail written?\n",
+ getpid()))
+ break;
+ }
+ Debug(DPROC, ("[%d] grandchild #%d finished, status=%04x",
+ getpid(), pid, WEXITSTATUS(waiter)))
+ if (WIFSIGNALED(waiter) && WCOREDUMP(waiter))
+ Debug(DPROC, (", dumped core"))
+ Debug(DPROC, ("\n"))
+ }
+}
+
+
+static void
+do_univ(u)
+ user *u;
+{
+#if defined(sequent)
+/* Dynix (Sequent) hack to put the user associated with
+ * the passed user structure into the ATT universe if
+ * necessary. We have to dig the gecos info out of
+ * the user's password entry to see if the magic
+ * "universe(att)" string is present.
+ */
+
+ struct passwd *p;
+ char *s;
+ int i;
+
+ p = getpwuid(u->uid);
+ (void) endpwent();
+
+ if (p == NULL)
+ return;
+
+ s = p->pw_gecos;
+
+ for (i = 0; i < 4; i++)
+ {
+ if ((s = strchr(s, ',')) == NULL)
+ return;
+ s++;
+ }
+ if (strcmp(s, "universe(att)"))
+ return;
+
+ (void) universe(U_ATT);
+#endif
+}
diff --git a/usr.sbin/cron/cron/externs.h b/usr.sbin/cron/cron/externs.h
new file mode 100644
index 0000000..64bc8ac
--- /dev/null
+++ b/usr.sbin/cron/cron/externs.h
@@ -0,0 +1,147 @@
+/* $FreeBSD$ */
+
+/* Copyright 1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if defined(POSIX) || defined(ATT)
+# include <stdlib.h>
+# include <unistd.h>
+# include <string.h>
+# include <dirent.h>
+# define DIR_T struct dirent
+# define WAIT_T int
+# define WAIT_IS_INT 1
+extern char *tzname[2];
+# define TZONE(tm) tzname[(tm).tm_isdst]
+#endif
+
+#if defined(UNIXPC)
+# undef WAIT_T
+# undef WAIT_IS_INT
+# define WAIT_T union wait
+#endif
+
+#if defined(POSIX)
+# define SIG_T sig_t
+# define TIME_T time_t
+# define PID_T pid_t
+#endif
+
+#if defined(ATT)
+# define SIG_T void
+# define TIME_T long
+# define PID_T int
+#endif
+
+#if !defined(POSIX) && !defined(ATT)
+/* classic BSD */
+extern time_t time();
+extern unsigned sleep();
+extern struct tm *localtime();
+extern struct passwd *getpwnam();
+extern int errno;
+extern void perror(), exit(), free();
+extern char *getenv(), *strcpy(), *strchr(), *strtok();
+extern void *malloc(), *realloc();
+# define SIG_T void
+# define TIME_T long
+# define PID_T int
+# define WAIT_T union wait
+# define DIR_T struct direct
+# include <sys/dir.h>
+# define TZONE(tm) (tm).tm_zone
+#endif
+
+/* getopt() isn't part of POSIX. some systems define it in <stdlib.h> anyway.
+ * of those that do, some complain that our definition is different and some
+ * do not. to add to the misery and confusion, some systems define getopt()
+ * in ways that we cannot predict or comprehend, yet do not define the adjunct
+ * external variables needed for the interface.
+ */
+#if (!defined(BSD) || (BSD < 198911)) && !defined(ATT) && !defined(UNICOS)
+int getopt(int, char * const *, const char *);
+#endif
+
+#if (!defined(BSD) || (BSD < 199103))
+extern char *optarg;
+extern int optind, opterr, optopt;
+#endif
+
+#if WAIT_IS_INT
+# ifndef WEXITSTATUS
+# define WEXITSTATUS(x) (((x) >> 8) & 0xff)
+# endif
+# ifndef WTERMSIG
+# define WTERMSIG(x) ((x) & 0x7f)
+# endif
+# ifndef WCOREDUMP
+# define WCOREDUMP(x) ((x) & 0x80)
+# endif
+#else /*WAIT_IS_INT*/
+# ifndef WEXITSTATUS
+# define WEXITSTATUS(x) ((x).w_retcode)
+# endif
+# ifndef WTERMSIG
+# define WTERMSIG(x) ((x).w_termsig)
+# endif
+# ifndef WCOREDUMP
+# define WCOREDUMP(x) ((x).w_coredump)
+# endif
+#endif /*WAIT_IS_INT*/
+
+#ifndef WIFSIGNALED
+#define WIFSIGNALED(x) (WTERMSIG(x) != 0)
+#endif
+#ifndef WIFEXITED
+#define WIFEXITED(x) (WTERMSIG(x) == 0)
+#endif
+
+#ifdef NEED_STRCASECMP
+extern int strcasecmp(char *, char *);
+#endif
+
+#ifdef NEED_STRDUP
+extern char *strdup(char *);
+#endif
+
+#ifdef NEED_STRERROR
+extern char *strerror(int);
+#endif
+
+#ifdef NEED_FLOCK
+extern int flock(int, int);
+# define LOCK_SH 1
+# define LOCK_EX 2
+# define LOCK_NB 4
+# define LOCK_UN 8
+#endif
+
+#ifdef NEED_SETSID
+extern int setsid(void);
+#endif
+
+#ifdef NEED_GETDTABLESIZE
+extern int getdtablesize(void);
+#endif
+
+#ifdef NEED_SETENV
+extern int setenv(char *, char *, int);
+#endif
+
+#ifdef NEED_VFORK
+extern PID_T vfork(void);
+#endif
diff --git a/usr.sbin/cron/cron/job.c b/usr.sbin/cron/cron/job.c
new file mode 100644
index 0000000..6d1b1af
--- /dev/null
+++ b/usr.sbin/cron/cron/job.c
@@ -0,0 +1,76 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if !defined(lint) && !defined(LINT)
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+
+
+#include "cron.h"
+
+
+typedef struct _job {
+ struct _job *next;
+ entry *e;
+ user *u;
+} job;
+
+
+static job *jhead = NULL, *jtail = NULL;
+
+
+void
+job_add(e, u)
+ register entry *e;
+ register user *u;
+{
+ register job *j;
+
+ /* if already on queue, keep going */
+ for (j=jhead; j; j=j->next)
+ if (j->e == e && j->u == u) { return; }
+
+ /* build a job queue element */
+ if ((j = (job*)malloc(sizeof(job))) == NULL)
+ return;
+ j->next = (job*) NULL;
+ j->e = e;
+ j->u = u;
+
+ /* add it to the tail */
+ if (!jhead) { jhead=j; }
+ else { jtail->next=j; }
+ jtail = j;
+}
+
+
+int
+job_runqueue()
+{
+ register job *j, *jn;
+ register int run = 0;
+
+ for (j=jhead; j; j=jn) {
+ do_command(j->e, j->u);
+ jn = j->next;
+ free(j);
+ run++;
+ }
+ jhead = jtail = NULL;
+ return run;
+}
diff --git a/usr.sbin/cron/cron/pathnames.h b/usr.sbin/cron/cron/pathnames.h
new file mode 100644
index 0000000..ba91bfd
--- /dev/null
+++ b/usr.sbin/cron/cron/pathnames.h
@@ -0,0 +1,81 @@
+/* Copyright 1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+/*
+ * $FreeBSD$
+ */
+
+#if (defined(BSD)) && (BSD >= 199103) || defined(__linux) || defined(AIX)
+# include <paths.h>
+#endif /*BSD*/
+
+#ifndef CRONDIR
+ /* CRONDIR is where crond(8) and crontab(1) both chdir
+ * to; SPOOL_DIR, ALLOW_FILE, DENY_FILE, and LOG_FILE
+ * are all relative to this directory.
+ */
+#define CRONDIR "/var/cron"
+#endif
+
+ /* SPOOLDIR is where the crontabs live.
+ * This directory will have its modtime updated
+ * whenever crontab(1) changes a crontab; this is
+ * the signal for crond(8) to look at each individual
+ * crontab file and reload those whose modtimes are
+ * newer than they were last time around (or which
+ * didn't exist last time around...)
+ */
+#define SPOOL_DIR "tabs"
+
+ /* undefining these turns off their features. note
+ * that ALLOW_FILE and DENY_FILE must both be defined
+ * in order to enable the allow/deny code. If neither
+ * LOG_FILE or SYSLOG is defined, we don't log. If
+ * both are defined, we log both ways.
+ */
+#define ALLOW_FILE "allow" /*-*/
+#define DENY_FILE "deny" /*-*/
+/*#define LOG_FILE "log"*/ /*-*/
+
+ /* where should the daemon stick its PID?
+ */
+#ifdef _PATH_VARRUN
+# define PIDDIR _PATH_VARRUN
+#else
+# define PIDDIR "/etc/"
+#endif
+#define PIDFILE "%scron.pid"
+
+ /* 4.3BSD-style crontab */
+#define SYSCRONTAB "/etc/crontab"
+
+ /* what editor to use if no EDITOR or VISUAL
+ * environment variable specified.
+ */
+#if defined(_PATH_VI)
+# define EDITOR _PATH_VI
+#else
+# define EDITOR "/usr/ucb/vi"
+#endif
+
+#ifndef _PATH_BSHELL
+# define _PATH_BSHELL "/bin/sh"
+#endif
+
+#ifndef _PATH_DEFPATH
+# define _PATH_DEFPATH "/usr/bin:/bin"
+#endif
diff --git a/usr.sbin/cron/cron/popen.c b/usr.sbin/cron/cron/popen.c
new file mode 100644
index 0000000..428de2e
--- /dev/null
+++ b/usr.sbin/cron/cron/popen.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 1988 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software written by Ken Arnold and
+ * published in UNIX Review, Vol. 6, No. 8.
+ *
+ * Redistribution and use in source and binary forms 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 University of California, Berkeley. The name of the
+ * University 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.
+ *
+ */
+
+/* this came out of the ftpd sources; it's been modified to avoid the
+ * globbing stuff since we don't need it. also execvp instead of execv.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)popen.c 5.7 (Berkeley) 2/14/89";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "cron.h"
+#include <sys/signal.h>
+#include <fcntl.h>
+#include <paths.h>
+#if defined(SYSLOG)
+# include <syslog.h>
+#endif
+#if defined(LOGIN_CAP)
+# include <login_cap.h>
+#endif
+
+
+#define MAX_ARGS 100
+#define WANT_GLOBBING 0
+
+/*
+ * Special version of popen which avoids call to shell. This insures noone
+ * may create a pipe to a hidden program as a side effect of a list or dir
+ * command.
+ */
+static PID_T *pids;
+static int fds;
+
+FILE *
+cron_popen(program, type, e)
+ char *program, *type;
+ entry *e;
+{
+ register char *cp;
+ FILE *iop;
+ int argc, pdes[2];
+ PID_T pid;
+ char *usernm;
+ char *argv[MAX_ARGS + 1];
+# if defined(LOGIN_CAP)
+ struct passwd *pwd;
+ login_cap_t *lc;
+# endif
+#if WANT_GLOBBING
+ char **pop, *vv[2];
+ int gargc;
+ char *gargv[1000];
+ extern char **glob(), **copyblk();
+#endif
+
+ if ((*type != 'r' && *type != 'w') || type[1])
+ return(NULL);
+
+ if (!pids) {
+ if ((fds = getdtablesize()) <= 0)
+ return(NULL);
+ if (!(pids = (PID_T *)malloc((u_int)(fds * sizeof(PID_T)))))
+ return(NULL);
+ bzero((char *)pids, fds * sizeof(PID_T));
+ }
+ if (pipe(pdes) < 0)
+ return(NULL);
+
+ /* break up string into pieces */
+ for (argc = 0, cp = program; argc < MAX_ARGS; cp = NULL)
+ if (!(argv[argc++] = strtok(cp, " \t\n")))
+ break;
+ argv[MAX_ARGS] = NULL;
+
+#if WANT_GLOBBING
+ /* glob each piece */
+ gargv[0] = argv[0];
+ for (gargc = argc = 1; argv[argc]; argc++) {
+ if (!(pop = glob(argv[argc]))) { /* globbing failed */
+ vv[0] = argv[argc];
+ vv[1] = NULL;
+ pop = copyblk(vv);
+ }
+ argv[argc] = (char *)pop; /* save to free later */
+ while (*pop && gargc < 1000)
+ gargv[gargc++] = *pop++;
+ }
+ gargv[gargc] = NULL;
+#endif
+
+ iop = NULL;
+ switch(pid = vfork()) {
+ case -1: /* error */
+ (void)close(pdes[0]);
+ (void)close(pdes[1]);
+ goto pfree;
+ /* NOTREACHED */
+ case 0: /* child */
+ if (e != NULL) {
+#ifdef SYSLOG
+ closelog();
+#endif
+
+ /* get new pgrp, void tty, etc.
+ */
+ (void) setsid();
+ }
+ if (*type == 'r') {
+ /* Do not share our parent's stdin */
+ (void)close(0);
+ (void)open(_PATH_DEVNULL, O_RDWR);
+ if (pdes[1] != 1) {
+ dup2(pdes[1], 1);
+ dup2(pdes[1], 2); /* stderr, too! */
+ (void)close(pdes[1]);
+ }
+ (void)close(pdes[0]);
+ } else {
+ if (pdes[0] != 0) {
+ dup2(pdes[0], 0);
+ (void)close(pdes[0]);
+ }
+ /* Hack: stdout gets revoked */
+ (void)close(1);
+ (void)open(_PATH_DEVNULL, O_RDWR);
+ (void)close(2);
+ (void)open(_PATH_DEVNULL, O_RDWR);
+ (void)close(pdes[1]);
+ }
+ if (e != NULL) {
+ /* Set user's entire context, but skip the environment
+ * as cron provides a separate interface for this
+ */
+ usernm = env_get("LOGNAME", e->envp);
+# if defined(LOGIN_CAP)
+ if ((pwd = getpwnam(usernm)) == NULL)
+ pwd = getpwuid(e->uid);
+ lc = NULL;
+ if (pwd != NULL) {
+ pwd->pw_gid = e->gid;
+ if (e->class != NULL)
+ lc = login_getclass(e->class);
+ }
+ if (pwd &&
+ setusercontext(lc, pwd, e->uid,
+ LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETENV)) == 0)
+ (void) endpwent();
+ else {
+ /* fall back to the old method */
+ (void) endpwent();
+# endif
+ /*
+ * Set our directory, uid and gid. Set gid
+ * first since once we set uid, we've lost
+ * root privileges.
+ */
+ if (setgid(e->gid) != 0)
+ _exit(ERROR_EXIT);
+# if defined(BSD)
+ if (initgroups(usernm, e->gid) != 0)
+ _exit(ERROR_EXIT);
+# endif
+ if (setlogin(usernm) != 0)
+ _exit(ERROR_EXIT);
+ if (setuid(e->uid) != 0)
+ _exit(ERROR_EXIT);
+ /* we aren't root after this..*/
+#if defined(LOGIN_CAP)
+ }
+ if (lc != NULL)
+ login_close(lc);
+#endif
+ chdir(env_get("HOME", e->envp));
+ }
+#if WANT_GLOBBING
+ execvp(gargv[0], gargv);
+#else
+ execvp(argv[0], argv);
+#endif
+ _exit(1);
+ }
+ /* parent; assume fdopen can't fail... */
+ if (*type == 'r') {
+ iop = fdopen(pdes[0], type);
+ (void)close(pdes[1]);
+ } else {
+ iop = fdopen(pdes[1], type);
+ (void)close(pdes[0]);
+ }
+ pids[fileno(iop)] = pid;
+
+pfree:
+#if WANT_GLOBBING
+ for (argc = 1; argv[argc] != NULL; argc++) {
+/* blkfree((char **)argv[argc]); */
+ free((char *)argv[argc]);
+ }
+#endif
+ return(iop);
+}
+
+int
+cron_pclose(iop)
+ FILE *iop;
+{
+ register int fdes;
+ int omask;
+ WAIT_T stat_loc;
+ PID_T pid;
+
+ /*
+ * pclose returns -1 if stream is not associated with a
+ * `popened' command, or, if already `pclosed'.
+ */
+ if (pids == 0 || pids[fdes = fileno(iop)] == 0)
+ return(-1);
+ (void)fclose(iop);
+ omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
+ while ((pid = wait(&stat_loc)) != pids[fdes] && pid != -1)
+ ;
+ (void)sigsetmask(omask);
+ pids[fdes] = 0;
+ return (pid == -1 ? -1 : WEXITSTATUS(stat_loc));
+}
diff --git a/usr.sbin/cron/cron/user.c b/usr.sbin/cron/cron/user.c
new file mode 100644
index 0000000..16fd617
--- /dev/null
+++ b/usr.sbin/cron/cron/user.c
@@ -0,0 +1,128 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if !defined(lint) && !defined(LINT)
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+
+/* vix 26jan87 [log is in RCS file]
+ */
+
+
+#include "cron.h"
+
+static char *User_name;
+
+void
+free_user(u)
+ user *u;
+{
+ entry *e, *ne;
+
+ free(u->name);
+ for (e = u->crontab; e != NULL; e = ne) {
+ ne = e->next;
+ free_entry(e);
+ }
+ free(u);
+}
+
+static void
+log_error(msg)
+ char *msg;
+{
+ log_it(User_name, getpid(), "PARSE", msg);
+}
+
+user *
+load_user(crontab_fd, pw, name)
+ int crontab_fd;
+ struct passwd *pw; /* NULL implies syscrontab */
+ char *name;
+{
+ char envstr[MAX_ENVSTR];
+ FILE *file;
+ user *u;
+ entry *e;
+ int status;
+ char **envp, **tenvp;
+
+ if (!(file = fdopen(crontab_fd, "r"))) {
+ warn("fdopen on crontab_fd in load_user");
+ return NULL;
+ }
+
+ Debug(DPARS, ("load_user()\n"))
+
+ /* file is open. build user entry, then read the crontab file.
+ */
+ if ((u = (user *) malloc(sizeof(user))) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ if ((u->name = strdup(name)) == NULL) {
+ free(u);
+ errno = ENOMEM;
+ return NULL;
+ }
+ u->crontab = NULL;
+
+ /*
+ * init environment. this will be copied/augmented for each entry.
+ */
+ if ((envp = env_init()) == NULL) {
+ free(u->name);
+ free(u);
+ return NULL;
+ }
+
+ /*
+ * load the crontab
+ */
+ while ((status = load_env(envstr, file)) >= OK) {
+ switch (status) {
+ case ERR:
+ free_user(u);
+ u = NULL;
+ goto done;
+ case FALSE:
+ User_name = u->name; /* for log_error */
+ e = load_entry(file, log_error, pw, envp);
+ if (e) {
+ e->next = u->crontab;
+ u->crontab = e;
+ }
+ break;
+ case TRUE:
+ if ((tenvp = env_set(envp, envstr))) {
+ envp = tenvp;
+ } else {
+ free_user(u);
+ u = NULL;
+ goto done;
+ }
+ break;
+ }
+ }
+
+ done:
+ env_free(envp);
+ fclose(file);
+ Debug(DPARS, ("...load_user() done\n"))
+ return u;
+}
diff --git a/usr.sbin/cron/crontab/Makefile b/usr.sbin/cron/crontab/Makefile
new file mode 100644
index 0000000..829128e
--- /dev/null
+++ b/usr.sbin/cron/crontab/Makefile
@@ -0,0 +1,18 @@
+# $FreeBSD$
+
+BINDIR= /usr/bin
+
+PROG= crontab
+MAN= crontab.1 crontab.5
+BINOWN= root
+BINMODE=4555
+PRECIOUSPROG=
+
+WARNS?= 3
+
+CFLAGS+= -I${.CURDIR}/../cron
+
+DPADD= ${LIBCRON} ${LIBMD} ${LIBUTIL}
+LDADD= ${LIBCRON} -lmd -lutil
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/cron/crontab/crontab.1 b/usr.sbin/cron/crontab/crontab.1
new file mode 100644
index 0000000..b634d31
--- /dev/null
+++ b/usr.sbin/cron/crontab/crontab.1
@@ -0,0 +1,141 @@
+.\"/* Copyright 1988,1990,1993 by Paul Vixie
+.\" * All rights reserved
+.\" *
+.\" * Distribute freely, except: don't remove my name from the source or
+.\" * documentation (don't take credit for my work), mark your changes (don't
+.\" * get me blamed for your possible bugs), don't alter or remove this
+.\" * notice. May be sold if buildable source is provided to buyer. No
+.\" * warrantee of any kind, express or implied, is included with this
+.\" * software; use at your own risk, responsibility for damages (if any) to
+.\" * anyone resulting from the use of this software rests entirely with the
+.\" * user.
+.\" *
+.\" * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+.\" * I'll try to keep a version up to date. I can be reached as follows:
+.\" * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+.\" */
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 29, 1993
+.Dt CRONTAB 1
+.Os
+.Sh NAME
+.Nm crontab
+.Nd maintain crontab files for individual users (V3)
+.Sh SYNOPSIS
+.Nm
+.Op Fl u Ar user
+.Ar file
+.Nm
+.Op Fl u Ar user
+{
+.Fl l |
+.Fl r |
+.Fl e
+}
+.Sh DESCRIPTION
+The
+.Nm
+utility is the program used to install, deinstall or list the tables
+used to drive the
+.Xr cron 8
+daemon in Vixie Cron.
+Each user can have their own crontab, and though
+these are files in
+.Pa /var ,
+they are not intended to be edited directly.
+.Pp
+If the
+.Pa allow
+file exists, then you must be listed therein in order to be allowed to use
+this command.
+If the
+.Pa allow
+file does not exist but the
+.Pa deny
+file does exist, then you must
+.Em not
+be listed in the
+.Pa deny
+file in order to use this command.
+If neither of these files exists, then
+depending on site-dependent configuration parameters, only the super user
+will be allowed to use this command, or all users will be able to use this
+command.
+The format of these files is one username per line,
+with no leading or trailing whitespace.
+Lines of other formats will be ignored,
+and so can be used for comments.
+.Pp
+The first form of this command is used to install a new crontab from some
+named file or standard input if the pseudo-filename
+.Sq Fl
+is given.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl u
+Specify the name of the user whose crontab is to be
+tweaked.
+If this option is not given,
+.Nm
+examines
+.Dq your
+crontab, i.e., the crontab of the person executing the
+command.
+Note that
+.Xr su 1
+can confuse
+.Nm
+and that if you are running inside of
+.Xr su 1
+you should always use the
+.Fl u
+option for safety's sake.
+.It Fl l
+Display the current crontab on standard output.
+.It Fl r
+Remove the current crontab.
+.It Fl e
+Edit the current crontab using the editor specified by
+the
+.Ev VISUAL
+or
+.Ev EDITOR
+environment variables.
+The specified editor
+.Em must
+edit the file in place;
+any editor that unlinks the file and recreates it cannot be used.
+After you exit
+from the editor, the modified crontab will be installed automatically.
+.El
+.Sh FILES
+.Bl -tag -width /var/cron/allow -compact
+.It Pa /var/cron/allow
+.It Pa /var/cron/deny
+.El
+.Sh DIAGNOSTICS
+A fairly informative usage message appears if you run it with a bad command
+line.
+.Sh SEE ALSO
+.Xr crontab 5 ,
+.Xr cron 8
+.Sh STANDARDS
+The
+.Nm
+command conforms to
+.St -p1003.2
+with the exception that the dangerous variant of calling
+.Nm
+without a file name in the first form of the command is not allowed by
+this implementation.
+The pseudo-filename
+.Sq Fl
+must be specified to read from standard input.
+The new command syntax
+differs from previous versions of Vixie Cron, as well as from the classic
+SVR3 syntax.
+.Sh AUTHORS
+.An Paul Vixie Aq paul@vix.com
diff --git a/usr.sbin/cron/crontab/crontab.5 b/usr.sbin/cron/crontab/crontab.5
new file mode 100644
index 0000000..ba88569
--- /dev/null
+++ b/usr.sbin/cron/crontab/crontab.5
@@ -0,0 +1,310 @@
+.\"/* Copyright 1988,1990,1993,1994 by Paul Vixie
+.\" * All rights reserved
+.\" *
+.\" * Distribute freely, except: don't remove my name from the source or
+.\" * documentation (don't take credit for my work), mark your changes (don't
+.\" * get me blamed for your possible bugs), don't alter or remove this
+.\" * notice. May be sold if buildable source is provided to buyer. No
+.\" * warrantee of any kind, express or implied, is included with this
+.\" * software; use at your own risk, responsibility for damages (if any) to
+.\" * anyone resulting from the use of this software rests entirely with the
+.\" * user.
+.\" *
+.\" * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+.\" * I'll try to keep a version up to date. I can be reached as follows:
+.\" * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+.\" */
+.\"
+.\" $FreeBSD$
+.\"
+.Dd July 31, 2005
+.Dt CRONTAB 5
+.Os
+.Sh NAME
+.Nm crontab
+.Nd tables for driving cron
+.Sh DESCRIPTION
+A
+.Nm
+file contains instructions to the
+.Xr cron 8
+daemon of the general form: ``run this command at this time on this date''.
+Each user has their own crontab, and commands in any given crontab will be
+executed as the user who owns the crontab.
+Uucp and News will usually have
+their own crontabs, eliminating the need for explicitly running
+.Xr su 1
+as part of a cron command.
+.Pp
+Blank lines and leading spaces and tabs are ignored.
+Lines whose first
+non-space character is a pound-sign (#) are comments, and are ignored.
+Note that comments are not allowed on the same line as cron commands, since
+they will be taken to be part of the command.
+Similarly, comments are not
+allowed on the same line as environment variable settings.
+.Pp
+An active line in a crontab will be either an environment setting or a cron
+command.
+An environment setting is of the form,
+.Bd -literal
+ name = value
+.Ed
+.Pp
+where the spaces around the equal-sign (=) are optional, and any subsequent
+non-leading spaces in
+.Em value
+will be part of the value assigned to
+.Em name .
+The
+.Em value
+string may be placed in quotes (single or double, but matching) to preserve
+leading or trailing blanks.
+The
+.Em name
+string may also be placed in quote (single or double, but matching)
+to preserve leading, trailing or inner blanks.
+.Pp
+Several environment variables are set up
+automatically by the
+.Xr cron 8
+daemon.
+.Ev SHELL
+is set to
+.Pa /bin/sh ,
+.Ev PATH
+is set to
+.Pa /usr/bin:/bin ,
+and
+.Ev LOGNAME
+and
+.Ev HOME
+are set from the
+.Pa /etc/passwd
+line of the crontab's owner.
+.Ev HOME ,
+.Ev PATH
+and
+.Ev SHELL
+may be overridden by settings in the crontab;
+.Ev LOGNAME
+may not.
+.Pp
+(Another note: the
+.Ev LOGNAME
+variable is sometimes called
+.Ev USER
+on
+.Bx
+systems...
+On these systems,
+.Ev USER
+will be set also).
+.Pp
+In addition to
+.Ev LOGNAME ,
+.Ev HOME ,
+.Ev PATH ,
+and
+.Ev SHELL ,
+.Xr cron 8
+will look at
+.Ev MAILTO
+if it has any reason to send mail as a result of running
+commands in ``this'' crontab.
+If
+.Ev MAILTO
+is defined (and non-empty), mail is
+sent to the user so named.
+.Ev MAILTO
+may also be used to direct mail to multiple recipients
+by separating recipient users with a comma.
+If
+.Ev MAILTO
+is defined but empty (MAILTO=""), no
+mail will be sent.
+Otherwise mail is sent to the owner of the crontab.
+This
+option is useful if you decide on
+.Pa /bin/mail
+instead of
+.Pa /usr/lib/sendmail
+as
+your mailer when you install cron --
+.Pa /bin/mail
+does not do aliasing, and UUCP
+usually does not read its mail.
+.Pp
+The format of a cron command is very much the V7 standard, with a number of
+upward-compatible extensions.
+Each line has five time and date fields,
+followed by a user name
+(with optional ``:<group>'' and ``/<login-class>'' suffixes)
+if this is the system crontab file,
+followed by a command.
+Commands are executed by
+.Xr cron 8
+when the minute, hour, and month of year fields match the current time,
+.Em and
+when at least one of the two day fields (day of month, or day of week)
+matches the current time (see ``Note'' below).
+.Xr cron 8
+examines cron entries once every minute.
+The time and date fields are:
+.Bd -literal -offset indent
+field allowed values
+----- --------------
+minute 0-59
+hour 0-23
+day of month 1-31
+month 1-12 (or names, see below)
+day of week 0-7 (0 or 7 is Sun, or use names)
+.Ed
+.Pp
+A field may be an asterisk (*), which always stands for ``first\-last''.
+.Pp
+Ranges of numbers are allowed.
+Ranges are two numbers separated
+with a hyphen.
+The specified range is inclusive.
+For example,
+8-11 for an ``hours'' entry specifies execution at hours 8, 9, 10
+and 11.
+.Pp
+Lists are allowed.
+A list is a set of numbers (or ranges)
+separated by commas.
+Examples: ``1,2,5,9'', ``0-4,8-12''.
+.Pp
+Step values can be used in conjunction with ranges.
+Following
+a range with ``/<number>'' specifies skips of the number's value
+through the range.
+For example, ``0-23/2'' can be used in the hours
+field to specify command execution every other hour (the alternative
+in the V7 standard is ``0,2,4,6,8,10,12,14,16,18,20,22'').
+Steps are
+also permitted after an asterisk, so if you want to say ``every two
+hours'', just use ``*/2''.
+.Pp
+Names can also be used for the ``month'' and ``day of week''
+fields.
+Use the first three letters of the particular
+day or month (case does not matter).
+Ranges or
+lists of names are not allowed.
+.Pp
+The ``sixth'' field (the rest of the line) specifies the command to be
+run.
+The entire command portion of the line, up to a newline or %
+character, will be executed by
+.Pa /bin/sh
+or by the shell
+specified in the
+.Ev SHELL
+variable of the cronfile.
+Percent-signs (%) in the command, unless escaped with backslash
+(\\), will be changed into newline characters, and all data
+after the first % will be sent to the command as standard
+input.
+.Pp
+Note: The day of a command's execution can be specified by two
+fields \(em day of month, and day of week.
+If both fields are
+restricted (ie, are not *), the command will be run when
+.Em either
+field matches the current time.
+For example,
+``30 4 1,15 * 5''
+would cause a command to be run at 4:30 am on the 1st and 15th of each
+month, plus every Friday.
+.Pp
+Instead of the first five fields,
+one of eight special strings may appear:
+.Bd -literal -offset indent
+string meaning
+------ -------
+@reboot Run once, at startup.
+@yearly Run once a year, "0 0 1 1 *".
+@annually (same as @yearly)
+@monthly Run once a month, "0 0 1 * *".
+@weekly Run once a week, "0 0 * * 0".
+@daily Run once a day, "0 0 * * *".
+@midnight (same as @daily)
+@hourly Run once an hour, "0 * * * *".
+.Ed
+.Sh EXAMPLE CRON FILE
+.Bd -literal
+
+# use /bin/sh to run commands, overriding the default set by cron
+SHELL=/bin/sh
+# mail any output to `paul', no matter whose crontab this is
+MAILTO=paul
+#
+# run five minutes after midnight, every day
+5 0 * * * $HOME/bin/daily.job >> $HOME/tmp/out 2>&1
+# run at 2:15pm on the first of every month -- output mailed to paul
+15 14 1 * * $HOME/bin/monthly
+# run at 10 pm on weekdays, annoy Joe
+0 22 * * 1-5 mail -s "It's 10pm" joe%Joe,%%Where are your kids?%
+23 0-23/2 * * * echo "run 23 minutes after midn, 2am, 4am ..., everyday"
+5 4 * * sun echo "run at 5 after 4 every sunday"
+.Ed
+.Sh SEE ALSO
+.Xr crontab 1 ,
+.Xr cron 8
+.Sh EXTENSIONS
+When specifying day of week, both day 0 and day 7 will be considered Sunday.
+.Bx
+and
+.Tn ATT
+seem to disagree about this.
+.Pp
+Lists and ranges are allowed to co-exist in the same field.
+"1-3,7-9" would
+be rejected by
+.Tn ATT
+or
+.Bx
+cron -- they want to see "1-3" or "7,8,9" ONLY.
+.Pp
+Ranges can include "steps", so "1-9/2" is the same as "1,3,5,7,9".
+.Pp
+Names of months or days of the week can be specified by name.
+.Pp
+Environment variables can be set in the crontab.
+In
+.Bx
+or
+.Tn ATT ,
+the
+environment handed to child processes is basically the one from
+.Pa /etc/rc .
+.Pp
+Command output is mailed to the crontab owner
+.No ( Bx
+cannot do this), can be
+mailed to a person other than the crontab owner (SysV cannot do this), or the
+feature can be turned off and no mail will be sent at all (SysV cannot do this
+either).
+.Pp
+All of the
+.Sq @
+commands that can appear in place of the first five fields
+are extensions.
+.Sh AUTHORS
+.An Paul Vixie Aq paul@vix.com
+.Sh BUGS
+If you are in one of the 70-odd countries that observe Daylight
+Savings Time, jobs scheduled during the rollback or advance will be
+affected.
+In general, it is not a good idea to schedule jobs during
+this period.
+.Pp
+For US timezones (except parts of IN, AZ, and HI) the time shift occurs at
+2AM local time.
+For others, the output of the
+.Xr zdump 8
+program's verbose
+.Fl ( v )
+option can be used to determine the moment of time shift.
diff --git a/usr.sbin/cron/crontab/crontab.c b/usr.sbin/cron/crontab/crontab.c
new file mode 100644
index 0000000..f7af626
--- /dev/null
+++ b/usr.sbin/cron/crontab/crontab.c
@@ -0,0 +1,626 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ * From Id: crontab.c,v 2.13 1994/01/17 03:20:37 vixie Exp
+ */
+
+#if !defined(lint) && !defined(LINT)
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+
+/* crontab - install and manage per-user crontab files
+ * vix 02may87 [RCS has the rest of the log]
+ * vix 26jan87 [original]
+ */
+
+#define MAIN_PROGRAM
+
+#include "cron.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <md5.h>
+#include <paths.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#ifdef USE_UTIMES
+# include <sys/time.h>
+#else
+# include <time.h>
+# include <utime.h>
+#endif
+#if defined(POSIX)
+# include <locale.h>
+#endif
+
+#define MD5_SIZE 33
+#define NHEADER_LINES 3
+
+
+enum opt_t { opt_unknown, opt_list, opt_delete, opt_edit, opt_replace };
+
+#if DEBUGGING
+static char *Options[] = { "???", "list", "delete", "edit", "replace" };
+#endif
+
+
+static PID_T Pid;
+static char User[MAX_UNAME], RealUser[MAX_UNAME];
+static char Filename[MAX_FNAME];
+static FILE *NewCrontab;
+static int CheckErrorCount;
+static enum opt_t Option;
+static struct passwd *pw;
+static void list_cmd(void),
+ delete_cmd(void),
+ edit_cmd(void),
+ poke_daemon(void),
+ check_error(char *),
+ parse_args(int c, char *v[]);
+static int replace_cmd(void);
+
+
+static void
+usage(char *msg)
+{
+ fprintf(stderr, "crontab: usage error: %s\n", msg);
+ fprintf(stderr, "%s\n%s\n",
+ "usage: crontab [-u user] file",
+ " crontab [-u user] { -e | -l | -r }");
+ exit(ERROR_EXIT);
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ int exitstatus;
+
+ Pid = getpid();
+ ProgramName = argv[0];
+
+#if defined(POSIX)
+ setlocale(LC_ALL, "");
+#endif
+
+#if defined(BSD)
+ setlinebuf(stderr);
+#endif
+ parse_args(argc, argv); /* sets many globals, opens a file */
+ set_cron_uid();
+ set_cron_cwd();
+ if (!allowed(User)) {
+ warnx("you (%s) are not allowed to use this program", User);
+ log_it(RealUser, Pid, "AUTH", "crontab command not allowed");
+ exit(ERROR_EXIT);
+ }
+ exitstatus = OK_EXIT;
+ switch (Option) {
+ case opt_list: list_cmd();
+ break;
+ case opt_delete: delete_cmd();
+ break;
+ case opt_edit: edit_cmd();
+ break;
+ case opt_replace: if (replace_cmd() < 0)
+ exitstatus = ERROR_EXIT;
+ break;
+ case opt_unknown:
+ break;
+ }
+ exit(exitstatus);
+ /*NOTREACHED*/
+}
+
+
+static void
+parse_args(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int argch;
+ char resolved_path[PATH_MAX];
+
+ if (!(pw = getpwuid(getuid())))
+ errx(ERROR_EXIT, "your UID isn't in the passwd file, bailing out");
+ bzero(pw->pw_passwd, strlen(pw->pw_passwd));
+ (void) strncpy(User, pw->pw_name, (sizeof User)-1);
+ User[(sizeof User)-1] = '\0';
+ strcpy(RealUser, User);
+ Filename[0] = '\0';
+ Option = opt_unknown;
+ while ((argch = getopt(argc, argv, "u:lerx:")) != -1) {
+ switch (argch) {
+ case 'x':
+ if (!set_debug_flags(optarg))
+ usage("bad debug option");
+ break;
+ case 'u':
+ if (getuid() != ROOT_UID)
+ errx(ERROR_EXIT, "must be privileged to use -u");
+ if (!(pw = getpwnam(optarg)))
+ errx(ERROR_EXIT, "user `%s' unknown", optarg);
+ bzero(pw->pw_passwd, strlen(pw->pw_passwd));
+ (void) strncpy(User, pw->pw_name, (sizeof User)-1);
+ User[(sizeof User)-1] = '\0';
+ break;
+ case 'l':
+ if (Option != opt_unknown)
+ usage("only one operation permitted");
+ Option = opt_list;
+ break;
+ case 'r':
+ if (Option != opt_unknown)
+ usage("only one operation permitted");
+ Option = opt_delete;
+ break;
+ case 'e':
+ if (Option != opt_unknown)
+ usage("only one operation permitted");
+ Option = opt_edit;
+ break;
+ default:
+ usage("unrecognized option");
+ }
+ }
+
+ endpwent();
+
+ if (Option != opt_unknown) {
+ if (argv[optind] != NULL) {
+ usage("no arguments permitted after this option");
+ }
+ } else {
+ if (argv[optind] != NULL) {
+ Option = opt_replace;
+ (void) strncpy (Filename, argv[optind], (sizeof Filename)-1);
+ Filename[(sizeof Filename)-1] = '\0';
+
+ } else {
+ usage("file name must be specified for replace");
+ }
+ }
+
+ if (Option == opt_replace) {
+ /* we have to open the file here because we're going to
+ * chdir(2) into /var/cron before we get around to
+ * reading the file.
+ */
+ if (!strcmp(Filename, "-")) {
+ NewCrontab = stdin;
+ } else if (realpath(Filename, resolved_path) != NULL &&
+ !strcmp(resolved_path, SYSCRONTAB)) {
+ err(ERROR_EXIT, SYSCRONTAB " must be edited manually");
+ } else {
+ /* relinquish the setuid status of the binary during
+ * the open, lest nonroot users read files they should
+ * not be able to read. we can't use access() here
+ * since there's a race condition. thanks go out to
+ * Arnt Gulbrandsen <agulbra@pvv.unit.no> for spotting
+ * the race.
+ */
+
+ if (swap_uids() < OK)
+ err(ERROR_EXIT, "swapping uids");
+ if (!(NewCrontab = fopen(Filename, "r")))
+ err(ERROR_EXIT, "%s", Filename);
+ if (swap_uids_back() < OK)
+ err(ERROR_EXIT, "swapping uids back");
+ }
+ }
+
+ Debug(DMISC, ("user=%s, file=%s, option=%s\n",
+ User, Filename, Options[(int)Option]))
+}
+
+static void
+copy_file(FILE *in, FILE *out) {
+ int x, ch;
+
+ Set_LineNum(1)
+ /* ignore the top few comments since we probably put them there.
+ */
+ for (x = 0; x < NHEADER_LINES; x++) {
+ ch = get_char(in);
+ if (EOF == ch)
+ break;
+ if ('#' != ch) {
+ putc(ch, out);
+ break;
+ }
+ while (EOF != (ch = get_char(in)))
+ if (ch == '\n')
+ break;
+ if (EOF == ch)
+ break;
+ }
+
+ /* copy the rest of the crontab (if any) to the output file.
+ */
+ if (EOF != ch)
+ while (EOF != (ch = get_char(in)))
+ putc(ch, out);
+}
+
+static void
+list_cmd() {
+ char n[MAX_FNAME];
+ FILE *f;
+
+ log_it(RealUser, Pid, "LIST", User);
+ (void) snprintf(n, sizeof(n), CRON_TAB(User));
+ if (!(f = fopen(n, "r"))) {
+ if (errno == ENOENT)
+ errx(ERROR_EXIT, "no crontab for %s", User);
+ else
+ err(ERROR_EXIT, "%s", n);
+ }
+
+ /* file is open. copy to stdout, close.
+ */
+ copy_file(f, stdout);
+ fclose(f);
+}
+
+
+static void
+delete_cmd() {
+ char n[MAX_FNAME];
+ int ch, first;
+
+ if (isatty(STDIN_FILENO)) {
+ (void)fprintf(stderr, "remove crontab for %s? ", User);
+ first = ch = getchar();
+ while (ch != '\n' && ch != EOF)
+ ch = getchar();
+ if (first != 'y' && first != 'Y')
+ return;
+ }
+
+ log_it(RealUser, Pid, "DELETE", User);
+ (void) snprintf(n, sizeof(n), CRON_TAB(User));
+ if (unlink(n)) {
+ if (errno == ENOENT)
+ errx(ERROR_EXIT, "no crontab for %s", User);
+ else
+ err(ERROR_EXIT, "%s", n);
+ }
+ poke_daemon();
+}
+
+
+static void
+check_error(msg)
+ char *msg;
+{
+ CheckErrorCount++;
+ fprintf(stderr, "\"%s\":%d: %s\n", Filename, LineNumber-1, msg);
+}
+
+
+static void
+edit_cmd() {
+ char n[MAX_FNAME], q[MAX_TEMPSTR], *editor;
+ FILE *f;
+ int t;
+ struct stat statbuf, fsbuf;
+ WAIT_T waiter;
+ PID_T pid, xpid;
+ mode_t um;
+ int syntax_error = 0;
+ char orig_md5[MD5_SIZE];
+ char new_md5[MD5_SIZE];
+
+ log_it(RealUser, Pid, "BEGIN EDIT", User);
+ (void) snprintf(n, sizeof(n), CRON_TAB(User));
+ if (!(f = fopen(n, "r"))) {
+ if (errno != ENOENT)
+ err(ERROR_EXIT, "%s", n);
+ warnx("no crontab for %s - using an empty one", User);
+ if (!(f = fopen(_PATH_DEVNULL, "r")))
+ err(ERROR_EXIT, _PATH_DEVNULL);
+ }
+
+ um = umask(077);
+ (void) snprintf(Filename, sizeof(Filename), "/tmp/crontab.XXXXXXXXXX");
+ if ((t = mkstemp(Filename)) == -1) {
+ warn("%s", Filename);
+ (void) umask(um);
+ goto fatal;
+ }
+ (void) umask(um);
+#ifdef HAS_FCHOWN
+ if (fchown(t, getuid(), getgid()) < 0) {
+#else
+ if (chown(Filename, getuid(), getgid()) < 0) {
+#endif
+ warn("fchown");
+ goto fatal;
+ }
+ if (!(NewCrontab = fdopen(t, "r+"))) {
+ warn("fdopen");
+ goto fatal;
+ }
+
+ copy_file(f, NewCrontab);
+ fclose(f);
+ if (fflush(NewCrontab))
+ err(ERROR_EXIT, "%s", Filename);
+ if (fstat(t, &fsbuf) < 0) {
+ warn("unable to fstat temp file");
+ goto fatal;
+ }
+ again:
+ if (stat(Filename, &statbuf) < 0) {
+ warn("stat");
+ fatal: unlink(Filename);
+ exit(ERROR_EXIT);
+ }
+ if (statbuf.st_dev != fsbuf.st_dev || statbuf.st_ino != fsbuf.st_ino)
+ errx(ERROR_EXIT, "temp file must be edited in place");
+ if (MD5File(Filename, orig_md5) == NULL) {
+ warn("MD5");
+ goto fatal;
+ }
+
+ if ((!(editor = getenv("VISUAL")))
+ && (!(editor = getenv("EDITOR")))
+ ) {
+ editor = EDITOR;
+ }
+
+ /* we still have the file open. editors will generally rewrite the
+ * original file rather than renaming/unlinking it and starting a
+ * new one; even backup files are supposed to be made by copying
+ * rather than by renaming. if some editor does not support this,
+ * then don't use it. the security problems are more severe if we
+ * close and reopen the file around the edit.
+ */
+
+ switch (pid = fork()) {
+ case -1:
+ warn("fork");
+ goto fatal;
+ case 0:
+ /* child */
+ if (setuid(getuid()) < 0)
+ err(ERROR_EXIT, "setuid(getuid())");
+ if (chdir("/tmp") < 0)
+ err(ERROR_EXIT, "chdir(/tmp)");
+ if (strlen(editor) + strlen(Filename) + 2 >= MAX_TEMPSTR)
+ errx(ERROR_EXIT, "editor or filename too long");
+ execlp(editor, editor, Filename, (char *)NULL);
+ err(ERROR_EXIT, "%s", editor);
+ /*NOTREACHED*/
+ default:
+ /* parent */
+ break;
+ }
+
+ /* parent */
+ {
+ void (*sig[3])(int signal);
+ sig[0] = signal(SIGHUP, SIG_IGN);
+ sig[1] = signal(SIGINT, SIG_IGN);
+ sig[2] = signal(SIGTERM, SIG_IGN);
+ xpid = wait(&waiter);
+ signal(SIGHUP, sig[0]);
+ signal(SIGINT, sig[1]);
+ signal(SIGTERM, sig[2]);
+ }
+ if (xpid != pid) {
+ warnx("wrong PID (%d != %d) from \"%s\"", xpid, pid, editor);
+ goto fatal;
+ }
+ if (WIFEXITED(waiter) && WEXITSTATUS(waiter)) {
+ warnx("\"%s\" exited with status %d", editor, WEXITSTATUS(waiter));
+ goto fatal;
+ }
+ if (WIFSIGNALED(waiter)) {
+ warnx("\"%s\" killed; signal %d (%score dumped)",
+ editor, WTERMSIG(waiter), WCOREDUMP(waiter) ?"" :"no ");
+ goto fatal;
+ }
+ if (stat(Filename, &statbuf) < 0) {
+ warn("stat");
+ goto fatal;
+ }
+ if (statbuf.st_dev != fsbuf.st_dev || statbuf.st_ino != fsbuf.st_ino)
+ errx(ERROR_EXIT, "temp file must be edited in place");
+ if (MD5File(Filename, new_md5) == NULL) {
+ warn("MD5");
+ goto fatal;
+ }
+ if (strcmp(orig_md5, new_md5) == 0 && !syntax_error) {
+ warnx("no changes made to crontab");
+ goto remove;
+ }
+ warnx("installing new crontab");
+ switch (replace_cmd()) {
+ case 0: /* Success */
+ break;
+ case -1: /* Syntax error */
+ for (;;) {
+ printf("Do you want to retry the same edit? ");
+ fflush(stdout);
+ q[0] = '\0';
+ (void) fgets(q, sizeof q, stdin);
+ switch (islower(q[0]) ? q[0] : tolower(q[0])) {
+ case 'y':
+ syntax_error = 1;
+ goto again;
+ case 'n':
+ goto abandon;
+ default:
+ fprintf(stderr, "Enter Y or N\n");
+ }
+ }
+ /*NOTREACHED*/
+ case -2: /* Install error */
+ abandon:
+ warnx("edits left in %s", Filename);
+ goto done;
+ default:
+ warnx("panic: bad switch() in replace_cmd()");
+ goto fatal;
+ }
+ remove:
+ unlink(Filename);
+ done:
+ log_it(RealUser, Pid, "END EDIT", User);
+}
+
+
+/* returns 0 on success
+ * -1 on syntax error
+ * -2 on install error
+ */
+static int
+replace_cmd() {
+ char n[MAX_FNAME], envstr[MAX_ENVSTR], tn[MAX_FNAME];
+ FILE *tmp;
+ int ch, eof;
+ entry *e;
+ time_t now = time(NULL);
+ char **envp = env_init();
+
+ if (envp == NULL) {
+ warnx("cannot allocate memory");
+ return (-2);
+ }
+
+ (void) snprintf(n, sizeof(n), "tmp.%d", Pid);
+ (void) snprintf(tn, sizeof(tn), CRON_TAB(n));
+
+ if (!(tmp = fopen(tn, "w+"))) {
+ warn("%s", tn);
+ return (-2);
+ }
+
+ /* write a signature at the top of the file.
+ *
+ * VERY IMPORTANT: make sure NHEADER_LINES agrees with this code.
+ */
+ fprintf(tmp, "# DO NOT EDIT THIS FILE - edit the master and reinstall.\n");
+ fprintf(tmp, "# (%s installed on %-24.24s)\n", Filename, ctime(&now));
+ fprintf(tmp, "# (Cron version -- %s)\n", rcsid);
+
+ /* copy the crontab to the tmp
+ */
+ rewind(NewCrontab);
+ Set_LineNum(1)
+ while (EOF != (ch = get_char(NewCrontab)))
+ putc(ch, tmp);
+ ftruncate(fileno(tmp), ftell(tmp));
+ fflush(tmp); rewind(tmp);
+
+ if (ferror(tmp)) {
+ warnx("error while writing new crontab to %s", tn);
+ fclose(tmp); unlink(tn);
+ return (-2);
+ }
+
+ /* check the syntax of the file being installed.
+ */
+
+ /* BUG: was reporting errors after the EOF if there were any errors
+ * in the file proper -- kludged it by stopping after first error.
+ * vix 31mar87
+ */
+ Set_LineNum(1 - NHEADER_LINES)
+ CheckErrorCount = 0; eof = FALSE;
+ while (!CheckErrorCount && !eof) {
+ switch (load_env(envstr, tmp)) {
+ case ERR:
+ eof = TRUE;
+ break;
+ case FALSE:
+ e = load_entry(tmp, check_error, pw, envp);
+ if (e)
+ free(e);
+ break;
+ case TRUE:
+ break;
+ }
+ }
+
+ if (CheckErrorCount != 0) {
+ warnx("errors in crontab file, can't install");
+ fclose(tmp); unlink(tn);
+ return (-1);
+ }
+
+#ifdef HAS_FCHOWN
+ if (fchown(fileno(tmp), ROOT_UID, -1) < OK)
+#else
+ if (chown(tn, ROOT_UID, -1) < OK)
+#endif
+ {
+ warn("chown");
+ fclose(tmp); unlink(tn);
+ return (-2);
+ }
+
+#ifdef HAS_FCHMOD
+ if (fchmod(fileno(tmp), 0600) < OK)
+#else
+ if (chmod(tn, 0600) < OK)
+#endif
+ {
+ warn("chown");
+ fclose(tmp); unlink(tn);
+ return (-2);
+ }
+
+ if (fclose(tmp) == EOF) {
+ warn("fclose");
+ unlink(tn);
+ return (-2);
+ }
+
+ (void) snprintf(n, sizeof(n), CRON_TAB(User));
+ if (rename(tn, n)) {
+ warn("error renaming %s to %s", tn, n);
+ unlink(tn);
+ return (-2);
+ }
+
+ log_it(RealUser, Pid, "REPLACE", User);
+
+ poke_daemon();
+
+ return (0);
+}
+
+
+static void
+poke_daemon() {
+#ifdef USE_UTIMES
+ struct timeval tvs[2];
+ struct timezone tz;
+
+ (void) gettimeofday(&tvs[0], &tz);
+ tvs[1] = tvs[0];
+ if (utimes(SPOOL_DIR, tvs) < OK) {
+ warn("can't update mtime on spooldir %s", SPOOL_DIR);
+ return;
+ }
+#else
+ if (utime(SPOOL_DIR, NULL) < OK) {
+ warn("can't update mtime on spooldir %s", SPOOL_DIR);
+ return;
+ }
+#endif /*USE_UTIMES*/
+}
diff --git a/usr.sbin/cron/doc/CHANGES b/usr.sbin/cron/doc/CHANGES
new file mode 100644
index 0000000..59f6803
--- /dev/null
+++ b/usr.sbin/cron/doc/CHANGES
@@ -0,0 +1,155 @@
+Vixie Cron Changes from V2 to V3
+Paul Vixie
+29-Dec-1993
+
+The crontab command now conforms to POSIX 1003.2. This means that when you
+install it, if you have any "crontab" command lines floating around in shell
+scripts (such as /etc/rc or /etc/rc.local), you will need to change them.
+
+I have integrated several changes made by BSDi for their BSD/386 operating
+system; these were offerred to me before I started consulting for them, so
+it is safe to say that they were intended for publication. Most notably,
+the name of the cron daemon has changed from "crond" to "cron". This was
+done for compatibility with 4.3BSD. Another change made for the same reason
+is the ability to read in an /etc/crontab file which has an extra field in
+each entry, between the time fields and the command. This field is a user
+name, and it permits the /etc/crontab command to contain commands which are
+to be run by any user on the system. /etc/crontab is not "installed" via
+the crontab(1) command; it is automatically read at startup time and it will
+be reread whenever it changes.
+
+I also added a "-e" option to crontab(1). Nine people also sent me diffs
+to add this option, but I had already implemented it on my own. I actually
+released an interrim version (V2.2, I think) for limited testing, and got a
+chance to fix a bad security bug in the "-e" option thanks to XXX.
+
+The daemon used to be extraordinarily sloppy in its use of file descriptors.
+A heck of a lot of them were left open in spawned jobs, which caused problems
+for the daemon and also caused problems with the spawned jobs if they were
+shell scripts since "sh" and "csh" have traditionally used hidden file
+descriptors to pass information to subshells, and cron was causing them to
+think they were subshells. If you had trouble with "sh" or "csh" scripts in
+V2, chances are good that V3 will fix your problems.
+
+About a dozen people have reminded me that I forgot to initialize
+"crontab_fd" in database.c. Keith Cantrell was the first, so he gets the
+point.
+
+Steve Simmons reminded me that once an account has been deleted from the
+system, "crontab -u USER -d" will not work. My solution is to suggest to
+all of you that before you delete a user's account, you first delete that
+user's crontab file if any. From cron's point of view, usernames can never
+be treated as arbitrary strings. Either they are valid user names, or they
+are not. I will not make an exception for the "-d" case, for security
+reasons that I consider reasonable. It is trivial for a root user to delete
+the entry by hand if necessary.
+
+Dan O'Neil reminded me that I forgot to reset "log_fd" in misc.c. A lot of
+others also reminded me of this, but Dan gets the point. I didn't fix it
+there, since the real bug was that it should have been open in the parent.
+
+Peter Kabal reminded me that I forgot to "#ifdef DEBUGGING" some code in
+misc.c. Hans Trompert actually told me first, but Peter sent the patch so
+he gets the point.
+
+Russell Nelson told me that I'd forgotten to "#include <syslog.h>" in misc.c,
+which explains why a lot of other people complained that it wasn't using
+syslog even when they configured it that way :-). Steve Simmons told me
+first, though, so he gets the point.
+
+An interrim version of the daemon tried to "stat" every file before
+executing it; this turned out to be a horribly bad idea since finding the
+name of a file from a shell command is a hard job (that's why we have
+shells, right?) I removed this bogus code. Dave Burgess gets the point.
+
+Dennis R. Conley sent a suggestion for MMDF systems, which I've added to the
+comments in cron.h.
+
+Mike Heisler noted that I use comments in the CONVERSION file which are
+documented as illegal in the man pages. Thanks, Mike.
+
+Irving Wolfe sent me some very cheerful changes for a NeXT system, but I
+consider the system itself broken and I can't bring myself to #ifdef for
+something as screwed up as this system seems to be. However, various others
+did send me smaller patches which appear to have cause cron to build and run
+correctly on (the latest) NeXT machines, with or without the "-posix" CFLAG.
+Irving also asked for a per-job MAILTO, and this was finally added later when
+I integrated the BSD/386 changes contributed by BSDi, and generalized some of
+the parsing.
+
+Lots of folks complained that the autogenerated "Date:" header wasn't in
+ARPA format. I didn't understand this -- either folks will use Sendmail and
+not generate a Date: at all (since Sendmail will do it), or folks will use
+something other than Sendmail which won't care about Date: formats. But
+I've "fixed" it anyway...
+
+Several people suggested that "*" should be able to take a "/step". One person
+suggested that "N/step" ought to mean "N-last/step", but that's stretching things
+a bit far. "*/step" seems quite intuitive to me, so I've added it. Colin Plumb
+sent in the first and most polite request for this feature.
+
+As with every release of Cron, BIND, and seemingly everything else I do, one
+user stands out with the most critical but also the most useful analysis.
+Cron V3's high score belongs to Peter Holzer, who sent in the nicest looking
+patch for the "%" interpretation problem and also helped me understand a
+tricky bit of badness in the "log_fd" problem.
+
+agulbra@flode.nvg.unit.no wins the honors for being the first to point out the
+nasty security hole in "crontab -r". 'Nuff said.
+
+Several folks pointed out that log_it() needed to exist even if logging was
+disabled. Some day I will create a tool that will compile a subsystem with
+every possible combination and permutation of #ifdef options, but meanwhile
+thanks to everybody.
+
+job_runqueue() was using storage after freeing it, since Jordan told me back
+in 1983 that C let you do that, and I believed him in 1986 when I wrote all
+this junk. Linux was the first to die from this error, and the Linux people
+sent me the most amazing, um, collection of patches for this problem. Thanks
+for all the fish.
+
+Jeremy Bettis reminded me that popen() isn't safe. I grabbed Ken Arnold's
+version of popen/pclose from the ftpd and hacked it to taste. We're safe now,
+from this at least.
+
+Branko Lankester sent me a very timely and helpful fix for a looming security
+problem in my "crontab -e" implementation.
+
+--------
+
+Vixie Cron Changes from V1 to V2
+Paul Vixie
+8-Feb-1988
+
+Many changes were made in a rash of activity about six months ago, the exact
+list of which is no longer clear in my memory. I know that V1 used a file
+called POKECRON in /usr/spool/cron to tell it that it was time to re-read
+all the crontab files; V2 uses the modtime the crontab directory as a flag to
+check out the crontab files; those whose modtime has changed will be re-read,
+and the others left alone. Note that the crontab(1) command will do a utimes
+call to make sure the mtime of the dir changes, since the filename/inode will
+often remain the same after a replacement and the mtime wouldn't change in
+that case.
+
+8-Feb-88: made it possible to use much larger environment variable strings.
+ V1 allowed 100 characters; V2 allows 1000. This was needed for PATH
+ variables on some systems. Thanks to Toerless Eckert for this idea.
+ E-mail: UUCP: ...pyramid!fauern!faui10!eckert
+
+16-Feb-88: added allow/deny, moved /usr/spool/cron/crontabs to
+ /usr/lib/cron/tabs. allow and deny are /usr/lib/cron/{allow,deny},
+ since the sysv naming for this depends on 'at' using the same
+ dir, which would be stupid (hint: use /usr/{lib,spool}/at).
+
+22-Feb-88: made it read the spool directory for crontabs and look each one
+ up using getpwnam() rather than reading all passwds with getpwent()
+ and trying to open each crontab.
+
+9-Dec-88: made it sync to :00 after the minute, makes cron predictable.
+ added logging to /var/cron/log.
+
+14-Apr-90: (actually, changes since December 1989)
+ fixed a number of bugs reported from the net and from John Gilmore.
+ added syslog per Keith Bostic. security features including not
+ being willing to run a command owned or writable by other than
+ the owner of the crontab 9not working well yet)
diff --git a/usr.sbin/cron/doc/CONVERSION b/usr.sbin/cron/doc/CONVERSION
new file mode 100644
index 0000000..f30df7d
--- /dev/null
+++ b/usr.sbin/cron/doc/CONVERSION
@@ -0,0 +1,85 @@
+$FreeBSD$
+
+Conversion of BSD 4.[23] crontab files:
+
+Edit your current crontab (/usr/lib/crontab) into little pieces, with each
+users' commands in a different file. This is different on 4.2 and 4.3,
+but I'll get to that below. The biggest feature of this cron is that you
+can move 'news' and 'uucp' cron commands into files owned and maintainable
+by those two users. You also get to rip all the fancy 'su' footwork out
+of the cron commands. On 4.3, there's no need for the 'su' stuff since the
+user name appears on each command -- but I'd still rather have separate
+crontabs with separate environments and so on.
+
+Leave the original /usr/lib/crontab! This cron doesn't use it, so you may
+as well keep it around for a while in case something goes wakko with this
+fancy version.
+
+Most commands in most crontabs are run by root, have to run by root, and
+should continue to be run by root. They still have to be in their own file;
+I recommend /etc/crontab.src or /usr/adm/crontab.src.
+
+'uucp's commands need their own file; how about /usr/lib/uucp/crontab.src?
+'news' also, perhaps in /usr/lib/news/crontab.src...
+
+I say `how about' and `perhaps' because it really doesn't matter to anyone
+(except you) where you put the crontab source files. The `crontab' command
+COPIES them into a protected directory (CRONDIR/SPOOL_DIR in cron.h), named
+after the user whose crontab it is. If you want to examine, replace, or
+delete a crontab, the `crontab' command does all of those things. The
+various `crontab.src' (my suggested name for them) files are just source
+files---they have to be copied to SPOOLDIR using `crontab' before they'll be
+executed.
+
+On 4.2, your crontab might have a few lines like this:
+
+ 5 * * * * su uucp < /usr/lib/uucp/uudemon.hr
+ 10 4 * * * su uucp < /usr/lib/uucp/uudemon.day
+ 15 5 * * 0 su uucp < /usr/lib/uucp/uudemon.wk
+
+...or like this:
+
+ 5 * * * * echo /usr/lib/uucp/uudemon.hr | su uucp
+ 10 4 * * * echo /usr/lib/uucp/uudemon.day | su uucp
+ 15 5 * * 0 echo /usr/lib/uucp/uudemon.wk | su uucp
+
+On 4.3, they'd look a little bit better, but not much:
+
+ 5 * * * * uucp /usr/lib/uucp/uudemon.hr
+ 10 4 * * * uucp /usr/lib/uucp/uudemon.day
+ 15 5 * * 0 uucp /usr/lib/uucp/uudemon.wk
+
+For this cron, you'd create /usr/lib/uucp/crontab.src (or wherever you want
+to keep uucp's commands) which would look like this:
+
+ # /usr/lib/uucp/crontab.src - uucp's crontab
+ #
+ PATH=/usr/lib/uucp:/bin:/usr/bin
+ SHELL=/bin/sh
+ HOME=/usr/lib/uucp
+ #
+ 5 * * * * uudemon.hr
+ 10 4 * * * uudemon.day
+ 15 5 * * 0 uudemon.wk
+
+The application to the `news' cron commands (if any) is left for you to
+figure out. Likewise if there are any other cruddy-looking 'su' commands in
+your crontab commands, you don't need them anymore: just find a good place
+to put the `crontab.src' (or whatever you want to call it) file for that
+user, put the cron commands into it, and install it using the `crontab'
+command (probably with "-u USERNAME", but see the man page).
+
+If you run a 4.2-derived cron, you could of course just install your current
+crontab in toto as root's crontab. It would work exactly the way your
+current one does, barring the extra steps in installing or changing it.
+There would still be advantages to this cron, mostly that you get mail if
+there is any output from your cron commands.
+
+One note about getting mail from cron: you will probably find, after you
+install this version of cron, that your cron commands are generating a lot
+of irritating output. The work-around for this is to redirect all EXPECTED
+output to a per-execution log file, which you can examine if you want to
+see the output from the "last time" a command was executed; if you get any
+UNEXPECTED output, it will be mailed to you. This takes a while to get
+right, but it's amazingly convenient. Trust me.
+
diff --git a/usr.sbin/cron/doc/FEATURES b/usr.sbin/cron/doc/FEATURES
new file mode 100644
index 0000000..821d1f3
--- /dev/null
+++ b/usr.sbin/cron/doc/FEATURES
@@ -0,0 +1,84 @@
+$FreeBSD$
+
+Features of Vixie's cron relative to BSD 4.[23] and SysV crons:
+
+-- Environment variables can be set in each crontab. SHELL, USER,
+ LOGNAME, and HOME are set from the user's passwd entry; all except
+ USER can be changed in the crontab. PATH is especially useful to
+ set there. TZ can be set, but cron ignores it other than passing
+ it on through to the commands it runs. Format is
+
+ variable=value
+
+ Blanks surrounding the '=' will be eaten; other blanks in value are
+ okay. Leading or trailing blanks can be preserved by quoting, single
+ or double quotes are okay, just so they match.
+
+ PATH=.:/bin:/usr/bin
+ SHELL=/bin/sh
+ FOOBAR = this is a long blanky example
+
+ Above, FOOBAR would get "this is a long blanky example" as its value.
+
+ SHELL and HOME will be used when it's time to run a command; if
+ you don't set them, HOME defaults to your /etc/passwd entry
+ and SHELL defaults to /bin/sh.
+
+ MAILTO, if set to the login name of a user on your system, will be the
+ person that cron mails the output of commands in that crontab. This is
+ useful if you decide on BINMAIL when configuring cron.h, since binmail
+ doesn't know anything about aliasing.
+
+-- Weekdays can be specified by name. Case is not significant, but only
+ the first three letters should be specified.
+
+-- Months can likewise be specified by name. Three letters only.
+
+-- Ranges and lists can be mixed. Standard crons won't allow '1,3-5'.
+
+-- Ranges can specify 'step' values. '10-16/2' is like '10,12,14,16'.
+
+-- Sunday is both day 0 and day 7 -- apparently BSD and ATT disagree
+ about this.
+
+-- Each user gets their own crontab file. This is a win over BSD 4.2,
+ where only root has one, and over BSD 4.3, where they made the crontab
+ format incompatible and although the commands can be run by non-root
+ uid's, root is still the only one who can edit the crontab file. This
+ feature mimics the SysV cron.
+
+-- The 'crontab' command is loosely compatible with SysV, but has more
+ options which just generally make more sense. Running crontab with
+ no arguments will print a cute little summary of the command syntax.
+
+-- Comments and blank lines are allowed in the crontab file. Comments
+ must be on a line by themselves; leading whitespace is ignored, and
+ a '#' introduces the comment.
+
+-- (big win) If the `crontab' command changes anything in any crontab,
+ the 'cron' daemon will reload all the tables before running the
+ next iteration. In some crons, you have to kill and restart the
+ daemon whenever you change a crontab. In other crons, the crontab
+ file is reread and reparsed every minute even if it didn't change.
+
+-- In order to support the automatic reload, the crontab files are not
+ readable or writable except by 'crontab' or 'cron'. This is not a
+ problem, since 'crontab' will let you do pretty much whatever you
+ want to your own crontab, or if you are root, to anybody's crontab.
+
+-- If any output is generated by a command (on stdout OR stderr), it will
+ be mailed to the owner of the crontab that contained the command (or
+ MAILTO, see discussion of environment variables, above). The headers
+ of the mail message will include the command that was run, and a
+ complete list of the environment that was passed to it, which will
+ contain (at least) the USER (LOGNAME on SysV), HOME, and SHELL.
+
+-- the dom/dow situation is odd. '* * 1,15 * Sun' will run on the
+ first and fifteenth AND every Sunday; '* * * * Sun' will run *only*
+ on Sundays; '* * 1,15 * *' will run *only* the 1st and 15th. this
+ is why we keep 'e->dow_star' and 'e->dom_star'. I didn't think up
+ this behaviour; it's how cron has always worked but the documentation
+ hasn't been very clear. I have been told that some AT&T crons do not
+ act this way and do the more reasonable thing, which is (IMHO) to "or"
+ the various field-matches together. In that sense this cron may not
+ be completely similar to some AT&T crons.
diff --git a/usr.sbin/cron/doc/INSTALL b/usr.sbin/cron/doc/INSTALL
new file mode 100644
index 0000000..7f4c997
--- /dev/null
+++ b/usr.sbin/cron/doc/INSTALL
@@ -0,0 +1,87 @@
+/* Copyright 1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+$FreeBSD$
+
+Read the comments at the top of the Makefile, then edit the area marked
+'configurable stuff'.
+
+Edit config.h. The stuff I expect you to change is down a bit from the
+top of the file, but it's clearly marked. Also look at pathnames.h.
+
+You don't have to create the /var/cron or /var/cron/tabs directories, since
+both the daemon and the `crontab' program will do this the first time they
+run if they don't exist. You do need to have a /var, though -- just "mkdir
+/var" if you don't have one, or you can "mkdir /usr/var; ln -s /usr/var /var"
+if you expect your /var to have a lot of stuff in it.
+
+You will also need /usr/local/etc and /usr/local/bin directories unless you
+change the Makefile. These will have to be created by hand, but if you are
+a long-time Usenet user you probably have them already. /usr/local/man is
+where I keep my man pages, but I have the source for `man' and you probably
+do not. Therefore you may have to put the man pages into /usr/man/manl,
+which will be hard since there will be name collisions. (Note that the man
+command was originally written by Bill Joy before he left Berkeley, and it
+contains no AT&T code, so it is in UUNET's archive of freely-distributable
+BSD code.)
+
+LINUX note: /usr/include/paths.h on some linux systems shows _PATH_SENDMAIL
+ to be /usr/bin/sendmail even though sendmail is installed in /usr/lib.
+ you should check this out.
+
+say:
+ make all
+
+su and say:
+ make install
+
+Note that if I can get you to "su and say" something just by asking, you have
+a very serious security problem on your system and you should look into it.
+
+Edit your /usr/lib/crontab file into little pieces -- see the CONVERSION file
+for help on this.
+
+Use the `crontab' command to install all the little pieces you just created.
+Some examples (see below before trying any of these!)
+
+ crontab -u uucp -r /usr/lib/uucp/crontab.src
+ crontab -u news -r /usr/lib/news/crontab.src
+ crontab -u root -r /usr/adm/crontab.src
+
+Notes on above examples: (1) the .src files are copied at the time the
+command is issued; changing the source files later will have no effect until
+they are reinstalled with another `crontab -r' command. (2) The crontab
+command will affect the crontab of the person using the command unless `-u
+USER' is given; `-u' only works for root. When using most `su' commands
+under most BSD's, `crontab' will still think of you as yourself even though
+you may think of yourself as root -- so use `-u' liberally. (3) the `-r'
+option stands for `replace'; check the man page for crontab(1) for other
+possibilities.
+
+Kill your existing cron daemon -- do `ps aux' and look for /etc/cron.
+
+Edit your /etc/rc or /etc/rc.local, looking for the line that starts up
+/etc/cron. Comment it out and add a line to start the new cron daemon
+-- usually /usr/local/etc/cron, unless you changed it in the Makefile.
+
+Start up this cron daemon yourself as root. Just type /usr/local/etc/cron
+(or whatever); no '&' is needed since the daemon forks itself and the
+process you executed returns immediately.
+
+ATT notes: for those people unfortunate enough to be stuck on a AT&T UNIX,
+you will need the public-domain "libndir", found in the B News source and in
+any comp.sources.unix archive. You will also need to hack the code some.
diff --git a/usr.sbin/cron/doc/MAIL b/usr.sbin/cron/doc/MAIL
new file mode 100644
index 0000000..3549e94
--- /dev/null
+++ b/usr.sbin/cron/doc/MAIL
@@ -0,0 +1,475 @@
+[ this is really old mail that came to me in response to my 1986 posting
+ to usenet asking for feature suggestions before releasing the first
+ version of cron. it is presented here for its entertainment value.
+ --vix ]
+
+$FreeBSD$
+
+From ptsfa!lll-crg!ames!acornrc!bob Wed Dec 31 10:07:08 1986
+Date: Wed, 31 Dec 86 08:59:31 pst
+From: lll-crg!ames!acornrc!bob (Bob Weissman)
+To: ptsfa!vixie!paul
+Status: RO
+
+Sure, here's a suggestion: I'd like to be able to run a program, say,
+every two hours. Current cron requires me to write
+0,2,4,6,8,10,12,14,16,18,20,22 in the hours field. How about a notation
+to handle this more elegantly?
+
+<< Okay, I've allowed 0-22/2 as a means of handling this.
+ The time specification for my cron is as follows:
+ specification = range {"," range}
+ range = (start "-" finish ["/" step]) | single-unit
+ This allows "1,3,5-7", which the current cron doesn't (it won't
+ do a range inside a list), and handles your specific need. >>
+
+From drw@mit-eddie Wed Dec 31 18:25:27 1986
+Date: Wed, 31 Dec 86 14:28:19 est
+From: drw@mit-eddie (Dale Worley)
+To: mit-eddie!vixie!paul
+Status: RO
+
+We have a lot of lines in our crontab of the form
+
+ 00 12 * * * su user < /usr/users/user/script.file
+
+This barfs (silently!) on our system (Dec Ultrix 1.2 == 4.2bsd) if
+user's shell is csh. This, I am told, is because csh requires that
+the environment be set up in certain ways, which cron doesn't do.
+(Actually, I believe, it is because /etc/rc, which runs cron, doesn't
+set up the environment enough for csh to run, and cron just inherits
+the situation.) Anyway, the point is that if you find out what csh
+really needs in its environment, you might want to set up cron to
+provide some reasonable defaults (if it isn't supplied by cron's
+parent). Also, could you tell me what csh needs, if you find out, so
+we can hack our /etc/rc?
+
+<< well, the environment IS a problem. processes that cron forks
+ will inherit the environment of the person who ran the cron
+ daemon... I plan to edit out such useless things as TERMCAP,
+ TERM, and the like; supply correct values for HOME, USER, CWD,
+ and whatever else comes to mind. I'll make sure csh works... >>
+From ptsfa!ames!seismo!dgis!generous Thu Jan 1 07:33:17 1987
+Date: Thu Jan 1 10:29:20 1987
+From: ames!seismo!dgis!generous (Curtis Generous)
+To: nike!ptsfa!vixie!paul
+Status: RO
+
+Paul:
+
+One of the limitations of the present versions of cron is the lack
+of the capability of specifying a way to execute a command every
+n units of time.
+
+Here is a good example:
+
+# Present method to start up uucico
+02,12,22,32,42,52 * * * * exec /usr/lib/uucp/uucico -r1
+
+# New method ?? (the ':' here is just one possibility for syntax)
+02:10 * * * * exec /usr/lib/uucp/uucico -r1
+
+This method would prove very helpful for those programs that get started
+every few minutes, making the entry long and not easily readable. The first
+number would specify the base time, and the second number the repetition
+interval.
+
+<< Good idea, but bob@acornrc beat you to it. I used '/' instead of
+ ':'. This is my personal preference, and seems intuitive when you
+ think of the divide operator in C... Does anyone have a preference? >>
+
+From ptsfa!lll-lcc!seismo!decuac!c3pe!c3engr!charles Thu Jan 1 17:04:24 1987
+From: lll-lcc!seismo!c3pe!c3engr!charles (Charles Green)
+To: c3pe!decuac!dolqci!vrdxhq!seismo!lll-lcc!ptsfa!vixie!paul
+Date: Thu Jan 1 19:22:47 1987
+Status: RO
+
+Well, this isn't a compatible extension, but I have in times past wondered
+about a facility to let you start a process at intervals of, say, 17 minutes,
+instead of particular minutes out of each hour.
+
+<< This was a popular request! >>
+
+From seismo!uwvax!astroatc!nicmad!norvax!mann Sun Jan 4 13:04:01 1987
+Date: Fri, 2 Jan 87 09:23:53 cst
+From: lll-lcc!seismo!uwvax!astroatc!nicmad!norvax!mann (Tom Mann)
+To: ptsfa!vixie!paul
+Status: RO
+
+I'm not sure if it is in cron (either SysV or BSD ... if it is, I haven't
+figured it out ) but a comment feature would SURE BE NICE!.
+There are times when I want to comment out an entry
+for a period of time; it might also make it a lot more legible.
+
+<< My cron allows blank lines and standard #-type comments. I know
+ that one BSD4.2 cron I've used had it. I don't know about SysV. >>
+
+From ptsfa!hoptoad!hugh Mon Jan 5 10:26:46 1987
+Date: Mon, 5 Jan 87 01:22:17 PST
+From: hoptoad!hugh (Hugh Daniel)
+To: ptsfa!vixie!paul
+Status: RO
+
+ Hi, I do have a BIG one that I would like. I want to log ALL output
+from command lines into a file for each line. Thus I might have a chance
+of finding out why my crontab entry did not work.
+ This would seem to work best if done by cron, as it is now I have a google
+of shell scripts laying about just to put the error output where I can see
+it.
+
+<< My cron (and the SysV cron) will send mail to the owner of the
+ particular crontab file if a command generates any output on stdout
+ or stderr. This can be irritating, but if you write a script such
+ that any output means a problem occurred, you can avoid most logfile
+ needs, and not generate mail except in unforeseen circumstances. >>
+
+From ptsfa!dual!ucbvax!ihnp4!anvil!es!Robert_Toxen Mon Jan 5 13:08:46 1987
+From: dual!ucbvax!ihnp4!anvil!es!Robert_Toxen
+Date: Fri, 2 Jan 87 14:25:29 EST
+To: anvil!ihnp4!ucbvax!dual!ptsfa!vixie!paul
+Status: RO
+
+Here are some suggestions:
+1. Run it through the C preprocessor via "/lib/<whatever>".
+
+<< hmmm. this seems of limited utility, and if you really wanted
+ to do it that way, you could do it yourself (since users can
+ write to their own crontab files). I'll add '-' (read stdin)
+ to the crontab installer program to facilitate this. >>
+
+2. Allow specifying every Nth day of week, i.e., every second Wednesday.
+ I did this to calendar by separating the day of week (Wed=4, which one
+ to start on and N with slashes). I took modulo the day of year as a
+ starting point so that someone with a desk calendar documenting such
+ things can easily determine the offset (second number). I did this
+ while at SGI; alas I don't have a copy of the code.
+
+<< I can see how this could be useful, but I'm not sure how I'd
+ implement it. Cron currently doesn't keep track of the last time
+ a given command was run; whether the current Wednesday is the first
+ or second since the command was last run would be pretty hard to
+ figure out. I'd have to keep a database of commands and their
+ execution around, and purge it when the crontab was overwritten.
+ This is too much work for me, but if someone adds it, let me know. >>
+
+From ptsfa!ames!seismo!cbmvax!devon!paul Tue Jan 6 05:50:17 1987
+From: ames!seismo!cbmvax!devon!paul
+To: cbmvax!seismo!nike!ptsfa!vixie!paul
+Date: Mon Jan 5 09:29:57 1987
+Status: RO
+
+One problem that has always plagued me with cron is the assumed ORing.
+I'd like to see some type of ANDing implemented. I guess I can best
+describe this by example. Say I have the following line in my crontab
+file:
+
+* * 4-31 * 1-6 /usr/bin/command
+
+What this does is run 'command' on the 4th thru 31st days of the
+month, AND on Monday thru Saturday; which probably means running it
+every day of the month (unless Sunday falls on days 1-3). This
+happens because cron runs the command if the day-of-month OR the
+day-of-week is true.
+
+What I'd like to happen with the above line is to run the command ONLY
+on Monday thru Saturday any time after the 3rd of the month, e.g. if
+the day-of-month AND the day-of-week are true.
+
+My proposal to you is to implement some special chars for the first
+five fields. Examples:
+
+* * !1-3 * 1-6 /usr/bin/command
+
+(run command Mon-Sat, but NOT [!] on the first 3 days of the month)
+
+* * &4-31 * &1-6 /usr/bin/command
+
+(run command if day-of-month AND day-of-week are true)
+
+Get the picture? This would be compatable with existing versions of
+cron (which wouldn't currently be using any special characters, so
+that old crontabs would be handled correctly).
+
+<< This message made me aware of the actual boolean expression involved
+ in a crontab entry. I'd assumed that it was
+ (minute && hour && DoM && month && DoW)
+ But it's really
+ (minute && hour && month && (DoM || DoW))
+
+ I can see some value in changing this, but with a fixed order of
+ fields, operators get to be kindof unary, which && and || really
+ aren't. If someone has an idea on a syntax that allows useful
+ variations to the standard (&& && && (||)) default, please suggest. >>
+
+From bobkat!pedz Tue Jan 6 20:02:10 1987
+From: pedz@bobkat.UUCP (Pedz Thing)
+Date: 2 Jan 87 17:34:44 GMT
+Status: RO
+
+Log files! It would be nice to be able to specify a log for cron
+itself and also a log for each program's stdout and stderr to go to.
+The latter can of course be done with > and 2> but it would be nice if
+there could be a single line with some sort of pattern like
+`> /usr/spool/log/%' and the command would be substituted for the %.
+Another thing which would be nice is to be able to specify which shell
+to call to give the command to.
+
+<< Log files are done with mail. The '%' idea could be useful if
+ a different character were used (% is special to cron, see man
+ page); a different directory would have to be chosen, since each
+ user has their own crontab file; and something intelligent would
+ have to be done in the file naming, since the first word of the
+ command might be ambiguous (with other commands). In short, it's
+ too much work. Sorry. >>
+
+From guy%gorodish@sun Tue Jan 6 20:03:13 1987
+From: guy%gorodish@sun (Guy Harris)
+Message-ID: <10944@sun.uucp>
+Date: 5 Jan 87 12:09:09 GMT
+References: <429@vixie.UUCP> <359@bobkat.UUCP>
+Sender: news@sun.uucp
+Status: RO
+
+> Another thing which would be nice is to be able to specify which shell
+> to call to give the command to.
+
+Well, the obvious choice would be the user's shell, but this wouldn't work
+for accounts like "uucico".
+
+<< I use the owning user's shell, and to handle "uucico" I check a
+ list of "acceptable shells" (currently compiled in, does anybody
+ mind?), substituting a default (compiled in) shell if the user's
+ shell isn't on the list.
+
+ BTW, "compiled in" means that it's in a .h file, easily changed
+ during installation, but requiring recompilation to modify. You
+ don't have to go digging through the code to find it... >>
+
+From qantel!hplabs!ucbvax!mwm@violet.berkeley.edu Tue Jan 6 21:24:48 1987
+To: hplabs!qantel!vixie!paul (Paul Vixie Esq)
+Date: 04 Jan 87 00:42:35 PST (Sun)
+From: Mike Meyer <mwm@violet.berkeley.edu>
+Status: RO
+
+<<[Discussion of RMS/FSF, and mwm's GNU Cron deleted]>>
+
+Oh, yeah - here are the extensions on my cron:
+
+1) Sunday is both day 0 and day 7, so it complies with both SysV and
+BSD cron.
+
+<< Good idea. I did it too, thanks for informing me. >>
+
+2) At is integrated into the cron. Instead of atrun to scan the
+/usr/spool/at directory, at files are put into the /usr/lib/cron
+directory along with users cron files, and cron fabricates a line from
+a crontab file to run them. This is considered a major win by all who
+use it.
+
+<< I don't use 'at', and my cron doesn't do anything with it. To run
+ 'at', I use 'atrun' the same way the current BSD cron does. My
+ crontab files are in /usr/spool/cron/crontabs, in the SysV
+ tradition -- not in /usr/lib/cron. This is a configuration
+ parameter, of course. >>
+
+There are two known restrictions:
+
+1) I don't support any of the SysV security hooks. I don't have a use
+for them, and RMS didn't like the idea at all :-).
+
+<< This means cron.allow and cron.deny. I plan to support them, as
+ they've been quite helpful at various HPUX sites I've administered. >>
+
+2) Cron expects to be able to create files with names longer than 14
+characters, which makes it hard to run on SysV. At least one person
+was working on a port, but I don't know how it's going. That might
+make for a good reason for releasing yours, right there.
+
+<< If someone has SysV (with the 14-character limit), they probably
+ won't want my cron, since it doesn't add much to the standard
+ version (which they may have support for). My cron is not currently
+ portable to non-BSD systems, since it relies on interval timers (I
+ needed to sleep for intervals more granular than seconds alone would
+ allow). The port would be trivial, and I will do it if a lot of
+ people ask for it... >>
+
+Oh, yeah - I'm going to see about getting this cron integrated into
+the next 4BSD release.
+
+<< How does one go about this? I have a few nifty gadgets I'd like
+ to contribute, this cron being one of them... >>
+
+<<[more FSF/GNU discussion deleted]>>
+
+From qantel!hplabs!ames!ut-sally!ut-ngp!melpad!bigtex!james Tue Jan 6 21:24:57 1987
+Posted-Date: Fri, 2 Jan 87 19:26:16 est
+Date: Fri, 2 Jan 87 19:26:16 est
+From: hplabs!ames!ut-sally!ut-ngp!bigtex!james
+To: vixie!paul
+Status: RO
+
+Yes!!! There are several critical failures in System V cron...
+
+1. Pass all variables in cron's environment into the environment of things
+ cron starts up, or at least into the crontab entries started up (at jobs
+ will inherit the environment of the user). If nothing else it is critically
+ important that the TZ variable be passed on. PATH should be passed on too.
+ Basically, passing environment values allows one to design a standard
+ environment with TZ and PATH and have that run by everything. If anyone
+ tells you this is no big deal, consider what happens when uucico is
+ started by cron in CA to make a long distance phone link... Unless the
+ administrator is really on his/her toes, calls scheduled at 5pm will really
+ go at two in the afternoon, needlessly incurring huge phone bills, all
+ because System V refuses to pass the TZ from its environment down. There
+ are work arounds, but only putting it in cron will really work. This is
+ not a security hole.
+
+<< delete TERM and TERMCAP; modify HOME, USER, and CWD; pass TZ and
+ PATH through undisturbed. any other requests out there?
+
+ BSD doesn't have this problem -- TZ is passed right on through if
+ you define it in the shell before starting my cron daemon. However,
+ the BSD I'm running this on doesn't need TZ to be defined anyway...
+ The default in the kernel has been just fine so far... But just the
+ same, if/when I port to SysV (I guess I really should), I'll make
+ sure this works right.
+
+ I guess I've been spoiled. HPUX is SysV-based, and I never had a
+ problem with cron and TZ when I used it. >>
+
+2. A way to avoid logging stuff in /usr/lib/cron/log. I have a cron entry
+ run uudemon.hr every 10 minutes. This is 144 times/day. Each run generates
+ three lines of text, for a total of 432 lines of text I don't want to see.
+ Obviously this should be optional, but it would be nice if there were a
+ way to flag an entry so that it wasn't logged at all unless there was an
+ error.
+
+<< I don't know nothin' 'bout no /usr/lib/cron/log. What is this file?
+ I don't see any reason to create log entries, given the mail-the-
+ output behaviour. Opinions, anyone? >>
+
+I will come up with other ideas no doubt, but I can always implement them
+myself.
+
+<< That's what I like about PD software. Please send me the diffs! >>
+
+The other problem you have is making sure you can run standard
+crontabs. I would suggest something like this: if the command part of the
+entry starts with an unescaped -, then flags and options follow immediately
+thereafter. As in:
+
+2,12,22,32,42,52 * * * * -q /usr/lib/uucp/uudemon.hr
+
+This could mean do not log the uudemon.hr run unless there is a problem of
+some kind. This is probably safe as not many filenames start with "-", and
+those that do are already a problem for people.
+
+<< Since I don't plan on supporting /usr/lib/cron/log in ANY form unless
+ many people request it, I won't be needing -q as you've defined it.
+ I could use something like this to avoid sending mail on errors, for
+ the occasional script that you don't want to bullet-proof.
+
+ The compatibility issue is CRITICAL. The 4.3BSD crontab format is
+ a crime against the whole philosophy of Unix(TM), in my opinion. >>
+
+One other minor thing to consider is the ulimit: can different users get
+different ulimits for their crontab entries?
+
+<< Boy I'm ignorant today. What's a ulimit, and what should I do with
+ it in a crontab? Suggestions, enlightenment, etc ?? >>
+
+From qantel!lll-crg!ames!uw-beaver!uw-nsr!john Tue Jan 6 23:32:44 1987
+Date: Thu, 1 Jan 87 10:53:05 pst
+From: lll-crg!ames!uw-beaver!uw-nsr!john (John Sambrook 5-7433)
+To: vixie!paul
+Status: RO
+
+How about not hardwiring the default environment that cron builds for its
+children in the cron program itself? Our cron does this and it's the pits
+because we are TZ=PST8PDT not TZ=EST5EDT !
+
+<< yeachk. I assure you, I will not hardwire the TZ! >>
+From ptsfa!well!dv Fri Jan 9 04:01:50 1987
+Date: Thu, 8 Jan 87 23:50:40 pst
+From: well!dv (David W. Vezie)
+To: ptsfa!vixie!paul
+Status: RO
+
+6, have a special notation called 'H' which would expand to weekends
+ and holidays (you'd have to keep a database somewhere of real
+ holidays), and also 'W' for workdays (neither weekend or holiday).
+
+<< Too much work. There should be a standard way to define and
+ detect holidays under Unix(TM); if there were, I'd use it. As
+ it is, I'll leave this for someone else to add.
+
+ I can see the usefulness; it just doesn't quite seem worth it. >>
+From qantel!gatech!akgua!blnt1!jat Wed Jan 14 20:00:40 1987
+Date: Tue, 13 Jan 87 16:39:38 EST
+From: gatech!akgua!blnt1!jat
+Status: RO
+
+1) Add some way to tell cron to reread the files, say kill -1 <pid>
+
+<< whenever the 'crontab' program is run and updates a crontab file,
+ a file /usr/spool/cron/POKECRON is created; next time the cron
+ daemon wakes up, it sees it, and re-reads the crontab files.
+
+ I thought of handling the signal; even implemented it. Then this
+ clever idea hit me and I ripped it all out and added a single
+ IF-statement to handle the POKECRON file. >>
+
+2) Have some kind of retry time so that if a command fails, cron will try to
+ execute it again after a certain period. This is useful if you have some
+ type of cleanup program that can run at the scheduled time for some reason
+ (such as locked device, unmounted filesystem, etc).
+
+<< Hmmm, sounds useful. I could do this by submitting an 'at' job...
+ I'll think about it. >>
+From ptsfa!dual!ucbvax!ihnp4!mtuxo!ender Sat Jan 3 16:54:00 1987
+From: dual!ucbvax!ihnp4!mtuxo!ender
+Date: Sat, 3 Jan 87 14:05:13 PST
+To: ucbvax!dual!ptsfa!vixie!paul
+Status: RO
+
+It would be nice if nonprivileged users can setup personal crontab files
+(~/.cronrc, say) and be able to run personal jobs at regular intervals.
+
+<< this is done, but in the SysV style: the 'crontab' program installs
+ a new crontab file for the executing user (can be overridden by root
+ for setup of uucp and news). the advantage of this is that (1) when
+ a crontab is changed, the daemon can be informed automatically; and
+ (2) the file can be syntax-checked before installation. >>
+From ptsfa!ames!seismo!ihnp4!lcc!richard Fri Jan 16 04:47:33 1987
+Date: Fri, 16 Jan 87 07:44:57 EST
+To: nike!ptsfa!vixie!paul
+Status: RO
+
+The System V cron is nice, but it has a few annoying features. One is that
+its mail files will say that the previous message is the output of "one of your
+cron commands." I wish it would say WHICH cron commmand.
+
+<< Done. Also which shell, which user (useful when the mail gets
+ forwarded), which home directory, and other useful crud. >>
+
+Another problem is with timezones. It is necessary to specify TZ=PST8PDT (or
+whatever) when you invoke cron (from inittab, or /etc/rc) and it is also
+necessary to add TZ=PST8PDT to each crontab line which might need it. Cron
+should automatically export its idea of the "TZ" to each invoked command, and
+it should be possible to put a line in the crontab file which overrides that
+for every command in the file (e.g., most users are on EST, so cron is run
+with TZ=EST5EDT; but one user is usually on PST and wants all of his cron
+commands to run with TZ=PST8PDT). This might be extended to allow any
+environment variable to be specified once for the whole crontab file (e.g.,
+PATH).
+
+<< Well, since I run the user's shell, you could put this into .cshrc.
+ generic environment-variable setting could be useful, though. Since
+ I have to modify the environment anyway, I'll consider this. >>
+
+A log file might be a nice idea, but the System V cron log is too verbose.
+I seem to remember that cron keeps it open, too; so you can't even have
+something go and periodically clean it out.
+
+<< I don't do /usr/lib/cron/log. I wasn't aware of this file until I
+ got all these suggestions. Do people want this file? Tell me! >>
diff --git a/usr.sbin/cron/doc/Makefile.vixie b/usr.sbin/cron/doc/Makefile.vixie
new file mode 100644
index 0000000..8df1301
--- /dev/null
+++ b/usr.sbin/cron/doc/Makefile.vixie
@@ -0,0 +1,128 @@
+#/* Copyright 1988,1990,1993,1994 by Paul Vixie
+# * All rights reserved
+# *
+# * Distribute freely, except: don't remove my name from the source or
+# * documentation (don't take credit for my work), mark your changes (don't
+# * get me blamed for your possible bugs), don't alter or remove this
+# * notice. May be sold if buildable source is provided to buyer. No
+# * warrantee of any kind, express or implied, is included with this
+# * software; use at your own risk, responsibility for damages (if any) to
+# * anyone resulting from the use of this software rests entirely with the
+# * user.
+# *
+# * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+# * I'll try to keep a version up to date. I can be reached as follows:
+# * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+# */
+
+# Makefile for vixie's cron
+#
+# $FreeBSD$
+#
+# vix 03mar88 [moved to RCS, rest of log is in there]
+# vix 30mar87 [goodbye, time.c; hello, getopt]
+# vix 12feb87 [cleanup for distribution]
+# vix 30dec86 [written]
+
+# NOTES:
+# 'make' can be done by anyone
+# 'make install' must be done by root
+#
+# this package needs getopt(3), bitstring(3), and BSD install(8).
+#
+# the configurable stuff in this makefile consists of compilation
+# options (use -O, cron runs forever) and destination directories.
+# SHELL is for the 'augumented make' systems where 'make' imports
+# SHELL from the environment and then uses it to run its commands.
+# if your environment SHELL variable is /bin/csh, make goes real
+# slow and sometimes does the wrong thing.
+#
+# this package needs the 'bitstring macros' library, which is
+# available from me or from the comp.sources.unix archive. if you
+# put 'bitstring.h' in a non-standard place (i.e., not intuited by
+# cc(1)), you will have to define INCLUDE to set the include
+# directory for cc. INCLUDE should be `-Isomethingorother'.
+#
+# there's more configuration info in config.h; edit that first!
+
+#################################### begin configurable stuff
+#<<DESTROOT is assumed to have ./etc, ./bin, and ./man subdirectories>>
+DESTROOT = $(DESTDIR)/usr
+DESTSBIN = $(DESTROOT)/sbin
+DESTBIN = $(DESTROOT)/bin
+DESTMAN = $(DESTROOT)/share/man
+#<<need bitstring.h>>
+INCLUDE = -I.
+#INCLUDE =
+#<<need getopt()>>
+LIBS =
+#<<optimize or debug?>>
+#OPTIM = -O
+OPTIM = -g
+#<<ATT or BSD or POSIX?>>
+# (ATT untested)
+#COMPAT = -DATT
+#(BSD is only needed if <sys/params.h> does not define it, as on ULTRIX)
+#COMPAT = -DBSD
+# (POSIX)
+#COMPAT = -DPOSIX
+#<<lint flags of choice?>>
+LINTFLAGS = -hbxa $(INCLUDE) $(COMPAT) $(DEBUGGING)
+#<<want to use a nonstandard CC?>>
+#CC = vcc
+#<<manifest defines>>
+DEFS =
+#(SGI IRIX systems need this)
+#DEFS = -D_BSD_SIGNALS -Dconst=
+#<<the name of the BSD-like install program>>
+#INSTALL = installbsd
+INSTALL = install
+#<<any special load flags>>
+LDFLAGS =
+#################################### end configurable stuff
+
+SHELL = /bin/sh
+CFLAGS = $(OPTIM) $(INCLUDE) $(COMPAT) $(DEFS)
+
+INFOS = README CHANGES FEATURES INSTALL CONVERSION THANKS MAIL
+MANPAGES = bitstring.3 crontab.5 crontab.1 cron.8 putman.sh
+HEADERS = bitstring.h cron.h config.h pathnames.h \
+ externs.h compat.h
+SOURCES = cron.c crontab.c database.c do_command.c entry.c \
+ env.c job.c user.c popen.c misc.c compat.c
+SHAR_SOURCE = $(INFOS) $(MANPAGES) Makefile $(HEADERS) $(SOURCES)
+LINT_CRON = cron.c database.c user.c entry.c compat.c \
+ misc.c job.c do_command.c env.c popen.c
+LINT_CRONTAB = crontab.c misc.c entry.c env.c compat.c
+CRON_OBJ = cron.o database.o user.o entry.o job.o do_command.o \
+ misc.o env.o popen.o compat.o
+CRONTAB_OBJ = crontab.o misc.o entry.o env.o compat.o
+
+all : cron crontab
+
+lint :
+ lint $(LINTFLAGS) $(LINT_CRON) $(LIBS) \
+ |grep -v "constant argument to NOT" 2>&1
+ lint $(LINTFLAGS) $(LINT_CRONTAB) $(LIBS) \
+ |grep -v "constant argument to NOT" 2>&1
+
+cron : $(CRON_OBJ)
+ $(CC) $(LDFLAGS) -o cron $(CRON_OBJ) $(LIBS)
+
+crontab : $(CRONTAB_OBJ)
+ $(CC) $(LDFLAGS) -o crontab $(CRONTAB_OBJ) $(LIBS)
+
+install : all
+ $(INSTALL) -c -m 111 -o root -s cron $(DESTSBIN)/
+ $(INSTALL) -c -m 4111 -o root -s crontab $(DESTBIN)/
+ sh putman.sh crontab.1 $(DESTMAN)
+ sh putman.sh cron.8 $(DESTMAN)
+ sh putman.sh crontab.5 $(DESTMAN)
+
+clean :; rm -f *.o cron crontab a.out core tags *~ #*
+
+kit : $(SHAR_SOURCE)
+ makekit -m -s99k $(SHAR_SOURCE)
+
+$(CRON_OBJ) : cron.h compat.h config.h externs.h pathnames.h Makefile
+$(CRONTAB_OBJ) : cron.h compat.h config.h externs.h pathnames.h Makefile
diff --git a/usr.sbin/cron/doc/README b/usr.sbin/cron/doc/README
new file mode 100644
index 0000000..eaced9e
--- /dev/null
+++ b/usr.sbin/cron/doc/README
@@ -0,0 +1,72 @@
+#/* Copyright 1988,1990,1993 by Paul Vixie
+# * All rights reserved
+# *
+# * Distribute freely, except: don't remove my name from the source or
+# * documentation (don't take credit for my work), mark your changes (don't
+# * get me blamed for your possible bugs), don't alter or remove this
+# * notice. May be sold if buildable source is provided to buyer. No
+# * warrantee of any kind, express or implied, is included with this
+# * software; use at your own risk, responsibility for damages (if any) to
+# * anyone resulting from the use of this software rests entirely with the
+# * user.
+# *
+# * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+# * I'll try to keep a version up to date. I can be reached as follows:
+# * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+# */
+
+Vixie Cron V3.0
+December 27, 1993
+[V2.2 was some time in 1992]
+[V2.1 was May 29, 1991]
+[V2.0 was July 5, 1990]
+[V2.0-beta was December 9, 1988]
+[V1.0 was May 6, 1987]
+Paul Vixie
+
+This is a version of 'cron' that is known to run on BSD 4.[23] systems. It
+is functionally based on the SysV cron, which means that each user can have
+their own crontab file (all crontab files are stored in a read-protected
+directory, usually /var/cron/tabs). No direct support is provided for
+'at'; you can continue to run 'atrun' from the crontab as you have been
+doing. If you don't have atrun (i.e., System V) you are in trouble.
+
+A messages is logged each time a command is executed; also, the files
+"allow" and "deny" in /var/cron can be used to control access to the
+"crontab" command (which installs crontabs). It hasn't been tested on
+SysV, although some effort has gone into making the port an easy one.
+
+This is more or less the copyright that USENET contributed software usually
+has. Since ATT couldn't use this version if they had to freely distribute
+source, and since I'd love to see them use it, I'll offer some rediculously
+low license fee just to have them take it. In the unlikely event that they
+do this, I will continue to support and distribute the pseudo-PD version, so
+please, don't flame me for wanting my work to see a wider distribution.
+
+To use this: Sorry, folks, there is no cutesy 'Configure' script. You'll
+have to go edit a couple of files... So, here's the checklist:
+
+ Read all the FEATURES, INSTALL, and CONVERSION files
+ Edit config.h
+ Edit Makefile
+ (both of these files have instructions inside; note that
+ some things in config.h are definable in Makefile and are
+ therefore surrounded by #ifndef...#endif)
+ 'make'
+ 'su' and 'make install'
+ (you may have to install the man pages by hand)
+ kill your existing cron process
+ (actually you can run your existing cron if you want, but why?)
+ build new crontabs using /usr/lib/{crontab,crontab.local}
+ (either put them all in "root"'s crontab, or divide it up
+ and rip out all the 'su' commands, collapse the lengthy
+ lists into ranges with steps -- basically, this step is
+ as much work as you want to make it)
+ start up the new cron
+ (must be done as root)
+ watch it. test it with 'crontab -r' and watch the daemon track your
+ changes.
+ if you like it, change your /etc/{rc,rc.local} to use it instead of
+ the old one.
+
+$FreeBSD$
diff --git a/usr.sbin/cron/doc/README.1ST b/usr.sbin/cron/doc/README.1ST
new file mode 100644
index 0000000..66cd0b2
--- /dev/null
+++ b/usr.sbin/cron/doc/README.1ST
@@ -0,0 +1,4 @@
+Sources substantially rearranged 26 Aug 94, Jordan Hubbard. Content
+remains faithful to the original 3.0 cron source from Paul Vixie, but
+any bugs introduced by this reorganization or the port to FreeBSD remain
+purely my own. - Jordan
diff --git a/usr.sbin/cron/doc/THANKS b/usr.sbin/cron/doc/THANKS
new file mode 100644
index 0000000..3787c29
--- /dev/null
+++ b/usr.sbin/cron/doc/THANKS
@@ -0,0 +1,29 @@
+15 January 1990
+Paul Vixie
+
+Many people have contributed to cron. Many more than I can remember, in fact.
+Rich Salz and Carl Gutekunst were each of enormous help to me in V1; Carl for
+helping me understand UNIX well enough to write it, and Rich for helping me
+get the features right.
+
+John Gilmore wrote me a wonderful review of V2, which took me a whole year to
+answer even though it made me clean up some really awful things in the code.
+(According to John the most awful things are still in here, of course.)
+
+Paul Close made a suggestion which led to /etc/crond.pid and the mutex locking
+on it. Kevin Braunsdorf of Purdue made a suggestion that led to @reboot and
+its brothers and sisters; he also sent some diffs that lead cron toward compil-
+ability with System V, though without at(1) capabilities, this cron isn't going
+to be that useful on System V. Bob Alverson fixed a silly bug in the line
+number counting. Brian Reid made suggestions which led to the run queue and
+the source-file labelling in installed crontabs.
+
+Scott Narveson ported V2 to a Sequent, and sent in the most useful single batch
+of diffs I got from anybody. Changes attributable to Scott are:
+ -> sendmail won't time out if the command is slow to generate output
+ -> day-of-week names aren't off by one anymore
+ -> crontab says the right thing if you do something you shouldn't do
+ -> crontab(5) man page is longer and more informative
+ -> misc changes related to the side effects of fclose()
+ -> Sequent "universe" support added (may also help on Pyramids)
+ -> null pw_shell is dealt with now; default is /bin/sh
diff --git a/usr.sbin/cron/lib/Makefile b/usr.sbin/cron/lib/Makefile
new file mode 100644
index 0000000..51316c1
--- /dev/null
+++ b/usr.sbin/cron/lib/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+LIB= cron
+INTERNALLIB=
+SRCS= entry.c env.c misc.c
+
+WARNS?= 3
+
+CFLAGS+= -I${.CURDIR}/../cron
+CFLAGS+= -DLOGIN_CAP -DPAM
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/cron/lib/compat.c b/usr.sbin/cron/lib/compat.c
new file mode 100644
index 0000000..9686012
--- /dev/null
+++ b/usr.sbin/cron/lib/compat.c
@@ -0,0 +1,237 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if !defined(lint) && !defined(LINT)
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+/* vix 30dec93 [broke this out of misc.c - see RCS log for history]
+ * vix 15jan87 [added TIOCNOTTY, thanks csg@pyramid]
+ */
+
+
+#include "cron.h"
+#ifdef NEED_GETDTABLESIZE
+# include <limits.h>
+#endif
+#if defined(NEED_SETSID) && defined(BSD)
+# include <sys/ioctl.h>
+#endif
+#include <errno.h>
+#include <paths.h>
+
+
+/* the code does not depend on any of vfork's
+ * side-effects; it just uses it as a quick
+ * fork-and-exec.
+ */
+#ifdef NEED_VFORK
+PID_T
+vfork() {
+ return (fork());
+}
+#endif
+
+
+#ifdef NEED_STRDUP
+char *
+strdup(str)
+ char *str;
+{
+ char *temp;
+
+ if ((temp = malloc(strlen(str) + 1)) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ (void) strcpy(temp, str);
+ return temp;
+}
+#endif
+
+
+#ifdef NEED_STRERROR
+char *
+strerror(error)
+ int error;
+{
+ extern char *sys_errlist[];
+ extern int sys_nerr;
+ static char buf[32];
+
+ if ((error <= sys_nerr) && (error > 0)) {
+ return sys_errlist[error];
+ }
+
+ sprintf(buf, "Unknown error: %d", error);
+ return buf;
+}
+#endif
+
+
+#ifdef NEED_STRCASECMP
+int
+strcasecmp(left, right)
+ char *left;
+ char *right;
+{
+ while (*left && (MkLower(*left) == MkLower(*right))) {
+ left++;
+ right++;
+ }
+ return MkLower(*left) - MkLower(*right);
+}
+#endif
+
+
+#ifdef NEED_SETSID
+int
+setsid()
+{
+ int newpgrp;
+# if defined(BSD)
+ int fd;
+# if defined(POSIX)
+ newpgrp = setpgid((pid_t)0, getpid());
+# else
+ newpgrp = setpgrp(0, getpid());
+# endif
+ if ((fd = open(_PATH_TTY, 2)) >= 0)
+ {
+ (void) ioctl(fd, TIOCNOTTY, (char*)0);
+ (void) close(fd);
+ }
+# else /*BSD*/
+ newpgrp = setpgrp();
+
+ (void) close(STDIN); (void) open(_PATH_DEVNULL, 0);
+ (void) close(STDOUT); (void) open(_PATH_DEVNULL, 1);
+ (void) close(STDERR); (void) open(_PATH_DEVNULL, 2);
+# endif /*BSD*/
+ return newpgrp;
+}
+#endif /*NEED_SETSID*/
+
+
+#ifdef NEED_GETDTABLESIZE
+int
+getdtablesize() {
+#ifdef _SC_OPEN_MAX
+ return sysconf(_SC_OPEN_MAX);
+#else
+ return _POSIX_OPEN_MAX;
+#endif
+}
+#endif
+
+
+#ifdef NEED_FLOCK
+/* The following flock() emulation snarfed intact *) from the HP-UX
+ * "BSD to HP-UX porting tricks" maintained by
+ * system@alchemy.chem.utoronto.ca (System Admin (Mike Peterson))
+ * from the version "last updated: 11-Jan-1993"
+ * Snarfage done by Jarkko Hietaniemi <Jarkko.Hietaniemi@hut.fi>
+ * *) well, almost, had to K&R the function entry, HPUX "cc"
+ * does not grok ANSI function prototypes */
+
+/*
+ * flock (fd, operation)
+ *
+ * This routine performs some file locking like the BSD 'flock'
+ * on the object described by the int file descriptor 'fd',
+ * which must already be open.
+ *
+ * The operations that are available are:
+ *
+ * LOCK_SH - get a shared lock.
+ * LOCK_EX - get an exclusive lock.
+ * LOCK_NB - don't block (must be ORed with LOCK_SH or LOCK_EX).
+ * LOCK_UN - release a lock.
+ *
+ * Return value: 0 if lock successful, -1 if failed.
+ *
+ * Note that whether the locks are enforced or advisory is
+ * controlled by the presence or absence of the SETGID bit on
+ * the executable.
+ *
+ * Note that there is no difference between shared and exclusive
+ * locks, since the 'lockf' system call in SYSV doesn't make any
+ * distinction.
+ *
+ * The file "<sys/file.h>" should be modified to contain the definitions
+ * of the available operations, which must be added manually (see below
+ * for the values).
+ */
+
+/* this code has been reformatted by vixie */
+
+int
+flock(fd, operation)
+ int fd;
+ int operation;
+{
+ int i;
+
+ switch (operation) {
+ case LOCK_SH: /* get a shared lock */
+ case LOCK_EX: /* get an exclusive lock */
+ i = lockf (fd, F_LOCK, 0);
+ break;
+
+ case LOCK_SH|LOCK_NB: /* get a non-blocking shared lock */
+ case LOCK_EX|LOCK_NB: /* get a non-blocking exclusive lock */
+ i = lockf (fd, F_TLOCK, 0);
+ if (i == -1)
+ if ((errno == EAGAIN) || (errno == EACCES))
+ errno = EWOULDBLOCK;
+ break;
+
+ case LOCK_UN: /* unlock */
+ i = lockf (fd, F_ULOCK, 0);
+ break;
+
+ default: /* can't decipher operation */
+ i = -1;
+ errno = EINVAL;
+ break;
+ }
+
+ return (i);
+}
+#endif /*NEED_FLOCK*/
+
+
+#ifdef NEED_SETENV
+int
+setenv(name, value, overwrite)
+ char *name, *value;
+ int overwrite;
+{
+ char *tmp;
+
+ if (overwrite && getenv(name))
+ return -1;
+
+ if (!(tmp = malloc(strlen(name) + strlen(value) + 2))) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ sprintf(tmp, "%s=%s", name, value);
+ return putenv(tmp); /* intentionally orphan 'tmp' storage */
+}
+#endif
diff --git a/usr.sbin/cron/lib/entry.c b/usr.sbin/cron/lib/entry.c
new file mode 100644
index 0000000..b6be69c
--- /dev/null
+++ b/usr.sbin/cron/lib/entry.c
@@ -0,0 +1,644 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if !defined(lint) && !defined(LINT)
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+
+/* vix 26jan87 [RCS'd; rest of log is in RCS file]
+ * vix 01jan87 [added line-level error recovery]
+ * vix 31dec86 [added /step to the from-to range, per bob@acornrc]
+ * vix 30dec86 [written]
+ */
+
+
+#include "cron.h"
+#include <grp.h>
+#ifdef LOGIN_CAP
+#include <login_cap.h>
+#endif
+
+typedef enum ecode {
+ e_none, e_minute, e_hour, e_dom, e_month, e_dow,
+ e_cmd, e_timespec, e_username, e_group, e_mem
+#ifdef LOGIN_CAP
+ , e_class
+#endif
+} ecode_e;
+
+static char get_list(bitstr_t *, int, int, char *[], int, FILE *),
+ get_range(bitstr_t *, int, int, char *[], int, FILE *),
+ get_number(int *, int, char *[], int, FILE *);
+static int set_element(bitstr_t *, int, int, int);
+
+static char *ecodes[] =
+ {
+ "no error",
+ "bad minute",
+ "bad hour",
+ "bad day-of-month",
+ "bad month",
+ "bad day-of-week",
+ "bad command",
+ "bad time specifier",
+ "bad username",
+ "bad group name",
+ "out of memory",
+#ifdef LOGIN_CAP
+ "bad class name",
+#endif
+ };
+
+
+void
+free_entry(e)
+ entry *e;
+{
+#ifdef LOGIN_CAP
+ if (e->class != NULL)
+ free(e->class);
+#endif
+ if (e->cmd != NULL)
+ free(e->cmd);
+ if (e->envp != NULL)
+ env_free(e->envp);
+ free(e);
+}
+
+
+/* return NULL if eof or syntax error occurs;
+ * otherwise return a pointer to a new entry.
+ */
+entry *
+load_entry(file, error_func, pw, envp)
+ FILE *file;
+ void (*error_func)(char *);
+ struct passwd *pw;
+ char **envp;
+{
+ /* this function reads one crontab entry -- the next -- from a file.
+ * it skips any leading blank lines, ignores comments, and returns
+ * EOF if for any reason the entry can't be read and parsed.
+ *
+ * the entry is also parsed here.
+ *
+ * syntax:
+ * user crontab:
+ * minutes hours doms months dows cmd\n
+ * system crontab (/etc/crontab):
+ * minutes hours doms months dows USERNAME cmd\n
+ */
+
+ ecode_e ecode = e_none;
+ entry *e;
+ int ch;
+ char cmd[MAX_COMMAND];
+ char envstr[MAX_ENVSTR];
+ char **prev_env;
+
+ Debug(DPARS, ("load_entry()...about to eat comments\n"))
+
+ skip_comments(file);
+
+ ch = get_char(file);
+ if (ch == EOF)
+ return NULL;
+
+ /* ch is now the first useful character of a useful line.
+ * it may be an @special or it may be the first character
+ * of a list of minutes.
+ */
+
+ e = (entry *) calloc(sizeof(entry), sizeof(char));
+
+ if (e == NULL) {
+ warn("load_entry: calloc failed");
+ return NULL;
+ }
+
+ if (ch == '@') {
+ /* all of these should be flagged and load-limited; i.e.,
+ * instead of @hourly meaning "0 * * * *" it should mean
+ * "close to the front of every hour but not 'til the
+ * system load is low". Problems are: how do you know
+ * what "low" means? (save me from /etc/cron.conf!) and:
+ * how to guarantee low variance (how low is low?), which
+ * means how to we run roughly every hour -- seems like
+ * we need to keep a history or let the first hour set
+ * the schedule, which means we aren't load-limited
+ * anymore. too much for my overloaded brain. (vix, jan90)
+ * HINT
+ */
+ Debug(DPARS, ("load_entry()...about to test shortcuts\n"))
+ ch = get_string(cmd, MAX_COMMAND, file, " \t\n");
+ if (!strcmp("reboot", cmd)) {
+ Debug(DPARS, ("load_entry()...reboot shortcut\n"))
+ e->flags |= WHEN_REBOOT;
+ } else if (!strcmp("yearly", cmd) || !strcmp("annually", cmd)){
+ Debug(DPARS, ("load_entry()...yearly shortcut\n"))
+ bit_set(e->minute, 0);
+ bit_set(e->hour, 0);
+ bit_set(e->dom, 0);
+ bit_set(e->month, 0);
+ bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
+ e->flags |= DOW_STAR;
+ } else if (!strcmp("monthly", cmd)) {
+ Debug(DPARS, ("load_entry()...monthly shortcut\n"))
+ bit_set(e->minute, 0);
+ bit_set(e->hour, 0);
+ bit_set(e->dom, 0);
+ bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
+ bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
+ e->flags |= DOW_STAR;
+ } else if (!strcmp("weekly", cmd)) {
+ Debug(DPARS, ("load_entry()...weekly shortcut\n"))
+ bit_set(e->minute, 0);
+ bit_set(e->hour, 0);
+ bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
+ e->flags |= DOM_STAR;
+ bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
+ bit_set(e->dow, 0);
+ } else if (!strcmp("daily", cmd) || !strcmp("midnight", cmd)) {
+ Debug(DPARS, ("load_entry()...daily shortcut\n"))
+ bit_set(e->minute, 0);
+ bit_set(e->hour, 0);
+ bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
+ bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
+ bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
+ } else if (!strcmp("hourly", cmd)) {
+ Debug(DPARS, ("load_entry()...hourly shortcut\n"))
+ bit_set(e->minute, 0);
+ bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1));
+ bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
+ bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
+ bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
+ } else {
+ ecode = e_timespec;
+ goto eof;
+ }
+ /* Advance past whitespace between shortcut and
+ * username/command.
+ */
+ Skip_Blanks(ch, file);
+ if (ch == EOF) {
+ ecode = e_cmd;
+ goto eof;
+ }
+ } else {
+ Debug(DPARS, ("load_entry()...about to parse numerics\n"))
+
+ ch = get_list(e->minute, FIRST_MINUTE, LAST_MINUTE,
+ PPC_NULL, ch, file);
+ if (ch == EOF) {
+ ecode = e_minute;
+ goto eof;
+ }
+
+ /* hours
+ */
+
+ ch = get_list(e->hour, FIRST_HOUR, LAST_HOUR,
+ PPC_NULL, ch, file);
+ if (ch == EOF) {
+ ecode = e_hour;
+ goto eof;
+ }
+
+ /* DOM (days of month)
+ */
+
+ if (ch == '*')
+ e->flags |= DOM_STAR;
+ ch = get_list(e->dom, FIRST_DOM, LAST_DOM,
+ PPC_NULL, ch, file);
+ if (ch == EOF) {
+ ecode = e_dom;
+ goto eof;
+ }
+
+ /* month
+ */
+
+ ch = get_list(e->month, FIRST_MONTH, LAST_MONTH,
+ MonthNames, ch, file);
+ if (ch == EOF) {
+ ecode = e_month;
+ goto eof;
+ }
+
+ /* DOW (days of week)
+ */
+
+ if (ch == '*')
+ e->flags |= DOW_STAR;
+ ch = get_list(e->dow, FIRST_DOW, LAST_DOW,
+ DowNames, ch, file);
+ if (ch == EOF) {
+ ecode = e_dow;
+ goto eof;
+ }
+ }
+
+ /* make sundays equivilent */
+ if (bit_test(e->dow, 0) || bit_test(e->dow, 7)) {
+ bit_set(e->dow, 0);
+ bit_set(e->dow, 7);
+ }
+
+ /* ch is the first character of a command, or a username */
+ unget_char(ch, file);
+
+ if (!pw) {
+ char *username = cmd; /* temp buffer */
+ char *s;
+ struct group *grp;
+#ifdef LOGIN_CAP
+ login_cap_t *lc;
+#endif
+
+ Debug(DPARS, ("load_entry()...about to parse username\n"))
+ ch = get_string(username, MAX_COMMAND, file, " \t");
+
+ Debug(DPARS, ("load_entry()...got %s\n",username))
+ if (ch == EOF) {
+ ecode = e_cmd;
+ goto eof;
+ }
+
+#ifdef LOGIN_CAP
+ if ((s = strrchr(username, '/')) != NULL) {
+ *s = '\0';
+ e->class = strdup(s + 1);
+ if (e->class == NULL)
+ warn("strdup(\"%s\")", s + 1);
+ } else {
+ e->class = strdup(RESOURCE_RC);
+ if (e->class == NULL)
+ warn("strdup(\"%s\")", RESOURCE_RC);
+ }
+ if (e->class == NULL) {
+ ecode = e_mem;
+ goto eof;
+ }
+ if ((lc = login_getclass(e->class)) == NULL) {
+ ecode = e_class;
+ goto eof;
+ }
+ login_close(lc);
+#endif
+ grp = NULL;
+ if ((s = strrchr(username, ':')) != NULL) {
+ *s = '\0';
+ if ((grp = getgrnam(s + 1)) == NULL) {
+ ecode = e_group;
+ goto eof;
+ }
+ }
+
+ pw = getpwnam(username);
+ if (pw == NULL) {
+ ecode = e_username;
+ goto eof;
+ }
+ if (grp != NULL)
+ pw->pw_gid = grp->gr_gid;
+ Debug(DPARS, ("load_entry()...uid %d, gid %d\n",pw->pw_uid,pw->pw_gid))
+#ifdef LOGIN_CAP
+ Debug(DPARS, ("load_entry()...class %s\n",e->class))
+#endif
+ }
+
+#ifndef PAM /* PAM takes care of account expiration by itself */
+ if (pw->pw_expire && time(NULL) >= pw->pw_expire) {
+ ecode = e_username;
+ goto eof;
+ }
+#endif /* !PAM */
+
+ e->uid = pw->pw_uid;
+ e->gid = pw->pw_gid;
+
+ /* copy and fix up environment. some variables are just defaults and
+ * others are overrides.
+ */
+ e->envp = env_copy(envp);
+ if (e->envp == NULL) {
+ warn("env_copy");
+ ecode = e_mem;
+ goto eof;
+ }
+ if (!env_get("SHELL", e->envp)) {
+ prev_env = e->envp;
+ sprintf(envstr, "SHELL=%s", _PATH_BSHELL);
+ e->envp = env_set(e->envp, envstr);
+ if (e->envp == NULL) {
+ warn("env_set(%s)", envstr);
+ env_free(prev_env);
+ ecode = e_mem;
+ goto eof;
+ }
+ }
+ if (!env_get("HOME", e->envp)) {
+ prev_env = e->envp;
+ sprintf(envstr, "HOME=%s", pw->pw_dir);
+ e->envp = env_set(e->envp, envstr);
+ if (e->envp == NULL) {
+ warn("env_set(%s)", envstr);
+ env_free(prev_env);
+ ecode = e_mem;
+ goto eof;
+ }
+ }
+ if (!env_get("PATH", e->envp)) {
+ prev_env = e->envp;
+ sprintf(envstr, "PATH=%s", _PATH_DEFPATH);
+ e->envp = env_set(e->envp, envstr);
+ if (e->envp == NULL) {
+ warn("env_set(%s)", envstr);
+ env_free(prev_env);
+ ecode = e_mem;
+ goto eof;
+ }
+ }
+ prev_env = e->envp;
+ sprintf(envstr, "%s=%s", "LOGNAME", pw->pw_name);
+ e->envp = env_set(e->envp, envstr);
+ if (e->envp == NULL) {
+ warn("env_set(%s)", envstr);
+ env_free(prev_env);
+ ecode = e_mem;
+ goto eof;
+ }
+#if defined(BSD)
+ prev_env = e->envp;
+ sprintf(envstr, "%s=%s", "USER", pw->pw_name);
+ e->envp = env_set(e->envp, envstr);
+ if (e->envp == NULL) {
+ warn("env_set(%s)", envstr);
+ env_free(prev_env);
+ ecode = e_mem;
+ goto eof;
+ }
+#endif
+
+ Debug(DPARS, ("load_entry()...about to parse command\n"))
+
+ /* Everything up to the next \n or EOF is part of the command...
+ * too bad we don't know in advance how long it will be, since we
+ * need to malloc a string for it... so, we limit it to MAX_COMMAND.
+ * XXX - should use realloc().
+ */
+ ch = get_string(cmd, MAX_COMMAND, file, "\n");
+
+ /* a file without a \n before the EOF is rude, so we'll complain...
+ */
+ if (ch == EOF) {
+ ecode = e_cmd;
+ goto eof;
+ }
+
+ /* got the command in the 'cmd' string; save it in *e.
+ */
+ e->cmd = strdup(cmd);
+ if (e->cmd == NULL) {
+ warn("strdup(\"%s\")", cmd);
+ ecode = e_mem;
+ goto eof;
+ }
+ Debug(DPARS, ("load_entry()...returning successfully\n"))
+
+ /* success, fini, return pointer to the entry we just created...
+ */
+ return e;
+
+ eof:
+ free_entry(e);
+ if (ecode != e_none && error_func)
+ (*error_func)(ecodes[(int)ecode]);
+ while (ch != EOF && ch != '\n')
+ ch = get_char(file);
+ return NULL;
+}
+
+
+static char
+get_list(bits, low, high, names, ch, file)
+ bitstr_t *bits; /* one bit per flag, default=FALSE */
+ int low, high; /* bounds, impl. offset for bitstr */
+ char *names[]; /* NULL or *[] of names for these elements */
+ int ch; /* current character being processed */
+ FILE *file; /* file being read */
+{
+ register int done;
+
+ /* we know that we point to a non-blank character here;
+ * must do a Skip_Blanks before we exit, so that the
+ * next call (or the code that picks up the cmd) can
+ * assume the same thing.
+ */
+
+ Debug(DPARS|DEXT, ("get_list()...entered\n"))
+
+ /* list = range {"," range}
+ */
+
+ /* clear the bit string, since the default is 'off'.
+ */
+ bit_nclear(bits, 0, (high-low+1));
+
+ /* process all ranges
+ */
+ done = FALSE;
+ while (!done) {
+ ch = get_range(bits, low, high, names, ch, file);
+ if (ch == ',')
+ ch = get_char(file);
+ else
+ done = TRUE;
+ }
+
+ /* exiting. skip to some blanks, then skip over the blanks.
+ */
+ Skip_Nonblanks(ch, file)
+ Skip_Blanks(ch, file)
+
+ Debug(DPARS|DEXT, ("get_list()...exiting w/ %02x\n", ch))
+
+ return ch;
+}
+
+
+static char
+get_range(bits, low, high, names, ch, file)
+ bitstr_t *bits; /* one bit per flag, default=FALSE */
+ int low, high; /* bounds, impl. offset for bitstr */
+ char *names[]; /* NULL or names of elements */
+ int ch; /* current character being processed */
+ FILE *file; /* file being read */
+{
+ /* range = number | number "-" number [ "/" number ]
+ */
+
+ register int i;
+ auto int num1, num2, num3;
+
+ Debug(DPARS|DEXT, ("get_range()...entering, exit won't show\n"))
+
+ if (ch == '*') {
+ /* '*' means "first-last" but can still be modified by /step
+ */
+ num1 = low;
+ num2 = high;
+ ch = get_char(file);
+ if (ch == EOF)
+ return EOF;
+ } else {
+ if (EOF == (ch = get_number(&num1, low, names, ch, file)))
+ return EOF;
+
+ if (ch == '/')
+ num2 = high;
+ else if (ch != '-') {
+ /* not a range, it's a single number.
+ */
+ if (EOF == set_element(bits, low, high, num1))
+ return EOF;
+ return ch;
+ } else {
+ /* eat the dash
+ */
+ ch = get_char(file);
+ if (ch == EOF)
+ return EOF;
+
+ /* get the number following the dash
+ */
+ ch = get_number(&num2, low, names, ch, file);
+ if (ch == EOF)
+ return EOF;
+ }
+ }
+
+ /* check for step size
+ */
+ if (ch == '/') {
+ /* eat the slash
+ */
+ ch = get_char(file);
+ if (ch == EOF)
+ return EOF;
+
+ /* get the step size -- note: we don't pass the
+ * names here, because the number is not an
+ * element id, it's a step size. 'low' is
+ * sent as a 0 since there is no offset either.
+ */
+ ch = get_number(&num3, 0, PPC_NULL, ch, file);
+ if (ch == EOF || num3 == 0)
+ return EOF;
+ } else {
+ /* no step. default==1.
+ */
+ num3 = 1;
+ }
+
+ /* range. set all elements from num1 to num2, stepping
+ * by num3. (the step is a downward-compatible extension
+ * proposed conceptually by bob@acornrc, syntactically
+ * designed then implmented by paul vixie).
+ */
+ for (i = num1; i <= num2; i += num3)
+ if (EOF == set_element(bits, low, high, i))
+ return EOF;
+
+ return ch;
+}
+
+
+static char
+get_number(numptr, low, names, ch, file)
+ int *numptr; /* where does the result go? */
+ int low; /* offset applied to result if symbolic enum used */
+ char *names[]; /* symbolic names, if any, for enums */
+ int ch; /* current character */
+ FILE *file; /* source */
+{
+ char temp[MAX_TEMPSTR], *pc;
+ int len, i, all_digits;
+
+ /* collect alphanumerics into our fixed-size temp array
+ */
+ pc = temp;
+ len = 0;
+ all_digits = TRUE;
+ while (isalnum(ch)) {
+ if (++len >= MAX_TEMPSTR)
+ return EOF;
+
+ *pc++ = ch;
+
+ if (!isdigit(ch))
+ all_digits = FALSE;
+
+ ch = get_char(file);
+ }
+ *pc = '\0';
+ if (len == 0)
+ return (EOF);
+
+ /* try to find the name in the name list
+ */
+ if (names) {
+ for (i = 0; names[i] != NULL; i++) {
+ Debug(DPARS|DEXT,
+ ("get_num, compare(%s,%s)\n", names[i], temp))
+ if (!strcasecmp(names[i], temp)) {
+ *numptr = i+low;
+ return ch;
+ }
+ }
+ }
+
+ /* no name list specified, or there is one and our string isn't
+ * in it. either way: if it's all digits, use its magnitude.
+ * otherwise, it's an error.
+ */
+ if (all_digits) {
+ *numptr = atoi(temp);
+ return ch;
+ }
+
+ return EOF;
+}
+
+
+static int
+set_element(bits, low, high, number)
+ bitstr_t *bits; /* one bit per flag, default=FALSE */
+ int low;
+ int high;
+ int number;
+{
+ Debug(DPARS|DEXT, ("set_element(?,%d,%d,%d)\n", low, high, number))
+
+ if (number < low || number > high)
+ return EOF;
+
+ bit_set(bits, (number-low));
+ return OK;
+}
diff --git a/usr.sbin/cron/lib/env.c b/usr.sbin/cron/lib/env.c
new file mode 100644
index 0000000..fd35817
--- /dev/null
+++ b/usr.sbin/cron/lib/env.c
@@ -0,0 +1,269 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if !defined(lint) && !defined(LINT)
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+
+
+#include "cron.h"
+
+
+char **
+env_init()
+{
+ register char **p = (char **) malloc(sizeof(char *));
+
+ if (p)
+ p[0] = NULL;
+ return (p);
+}
+
+
+void
+env_free(envp)
+ char **envp;
+{
+ char **p;
+
+ if ((p = envp))
+ for (; *p; p++)
+ free(*p);
+ free(envp);
+}
+
+
+char **
+env_copy(envp)
+ register char **envp;
+{
+ register int count, i;
+ register char **p;
+
+ for (count = 0; envp[count] != NULL; count++)
+ ;
+ p = (char **) malloc((count+1) * sizeof(char *)); /* 1 for the NULL */
+ if (p == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ for (i = 0; i < count; i++)
+ if ((p[i] = strdup(envp[i])) == NULL) {
+ while (--i >= 0)
+ (void) free(p[i]);
+ free(p);
+ errno = ENOMEM;
+ return NULL;
+ }
+ p[count] = NULL;
+ return (p);
+}
+
+
+char **
+env_set(envp, envstr)
+ char **envp;
+ char *envstr;
+{
+ register int count, found;
+ register char **p;
+ char *q;
+
+ /*
+ * count the number of elements, including the null pointer;
+ * also set 'found' to -1 or index of entry if already in here.
+ */
+ found = -1;
+ for (count = 0; envp[count] != NULL; count++) {
+ if (!strcmp_until(envp[count], envstr, '='))
+ found = count;
+ }
+ count++; /* for the NULL */
+
+ if (found != -1) {
+ /*
+ * it exists already, so just free the existing setting,
+ * save our new one there, and return the existing array.
+ */
+ q = envp[found];
+ if ((envp[found] = strdup(envstr)) == NULL) {
+ envp[found] = q;
+ /* XXX env_free(envp); */
+ errno = ENOMEM;
+ return NULL;
+ }
+ free(q);
+ return (envp);
+ }
+
+ /*
+ * it doesn't exist yet, so resize the array, move null pointer over
+ * one, save our string over the old null pointer, and return resized
+ * array.
+ */
+ p = (char **) realloc((void *) envp,
+ (unsigned) ((count+1) * sizeof(char *)));
+ if (p == NULL) {
+ /* XXX env_free(envp); */
+ errno = ENOMEM;
+ return NULL;
+ }
+ p[count] = p[count-1];
+ if ((p[count-1] = strdup(envstr)) == NULL) {
+ env_free(p);
+ errno = ENOMEM;
+ return NULL;
+ }
+ return (p);
+}
+
+
+/* return ERR = end of file
+ * FALSE = not an env setting (file was repositioned)
+ * TRUE = was an env setting
+ */
+int
+load_env(envstr, f)
+ char *envstr;
+ FILE *f;
+{
+ long filepos;
+ int fileline;
+ char name[MAX_ENVSTR], val[MAX_ENVSTR];
+ char quotechar, *c, *str;
+ int state;
+
+ /* The following states are traversed in order: */
+#define NAMEI 0 /* First char of NAME, may be quote */
+#define NAME 1 /* Subsequent chars of NAME */
+#define EQ1 2 /* After end of name, looking for '=' sign */
+#define EQ2 3 /* After '=', skipping whitespace */
+#define VALUEI 4 /* First char of VALUE, may be quote */
+#define VALUE 5 /* Subsequent chars of VALUE */
+#define FINI 6 /* All done, skipping trailing whitespace */
+#define ERROR 7 /* Error */
+
+ filepos = ftell(f);
+ fileline = LineNumber;
+ skip_comments(f);
+ if (EOF == get_string(envstr, MAX_ENVSTR, f, "\n"))
+ return (ERR);
+
+ Debug(DPARS, ("load_env, read <%s>\n", envstr));
+
+ bzero (name, sizeof name);
+ bzero (val, sizeof val);
+ str = name;
+ state = NAMEI;
+ quotechar = '\0';
+ c = envstr;
+ while (state != ERROR && *c) {
+ switch (state) {
+ case NAMEI:
+ case VALUEI:
+ if (*c == '\'' || *c == '"')
+ quotechar = *c++;
+ ++state;
+ /* FALLTHROUGH */
+ case NAME:
+ case VALUE:
+ if (quotechar) {
+ if (*c == quotechar) {
+ state++;
+ c++;
+ break;
+ }
+ if (state == NAME && *c == '=') {
+ state = ERROR;
+ break;
+ }
+ } else {
+ if (state == NAME) {
+ if (isspace (*c)) {
+ c++;
+ state++;
+ break;
+ }
+ if (*c == '=') {
+ state++;
+ break;
+ }
+ }
+ }
+ *str++ = *c++;
+ break;
+
+ case EQ1:
+ if (*c == '=') {
+ state++;
+ str = val;
+ quotechar = '\0';
+ } else {
+ if (!isspace (*c))
+ state = ERROR;
+ }
+ c++;
+ break;
+ case EQ2:
+ case FINI:
+ if (isspace (*c))
+ c++;
+ else
+ state++;
+ break;
+ }
+ }
+ if (state != FINI && !(state == VALUE && !quotechar)) {
+ Debug(DPARS, ("load_env, parse error, state = %d\n", state))
+ fseek(f, filepos, 0);
+ Set_LineNum(fileline);
+ return (FALSE);
+ }
+ if (state == VALUE) {
+ /* End of unquoted value: trim trailing whitespace */
+ c = val + strlen (val);
+ while (c > val && isspace (*(c - 1)))
+ *(--c) = '\0';
+ }
+
+ /* 2 fields from parser; looks like an env setting */
+
+ if (strlen(name) + 1 + strlen(val) >= MAX_ENVSTR-1)
+ return (FALSE);
+ (void) sprintf(envstr, "%s=%s", name, val);
+ Debug(DPARS, ("load_env, <%s> <%s> -> <%s>\n", name, val, envstr))
+ return (TRUE);
+}
+
+
+char *
+env_get(name, envp)
+ register char *name;
+ register char **envp;
+{
+ register int len = strlen(name);
+ register char *p, *q;
+
+ while ((p = *envp++)) {
+ if (!(q = strchr(p, '=')))
+ continue;
+ if ((q - p) == len && !strncmp(p, name, len))
+ return (q+1);
+ }
+ return (NULL);
+}
diff --git a/usr.sbin/cron/lib/misc.c b/usr.sbin/cron/lib/misc.c
new file mode 100644
index 0000000..5a08ad0
--- /dev/null
+++ b/usr.sbin/cron/lib/misc.c
@@ -0,0 +1,598 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if !defined(lint) && !defined(LINT)
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif
+
+/* vix 26jan87 [RCS has the rest of the log]
+ * vix 30dec86 [written]
+ */
+
+
+#include "cron.h"
+#if SYS_TIME_H
+# include <sys/time.h>
+#else
+# include <time.h>
+#endif
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <err.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#if defined(SYSLOG)
+# include <syslog.h>
+#endif
+
+
+#if defined(LOG_DAEMON) && !defined(LOG_CRON)
+#define LOG_CRON LOG_DAEMON
+#endif
+
+
+static int LogFD = ERR;
+
+
+int
+strcmp_until(left, right, until)
+ char *left;
+ char *right;
+ int until;
+{
+ register int diff;
+
+ while (*left && *left != until && *left == *right) {
+ left++;
+ right++;
+ }
+
+ if ((*left=='\0' || *left == until) &&
+ (*right=='\0' || *right == until)) {
+ diff = 0;
+ } else {
+ diff = *left - *right;
+ }
+
+ return diff;
+}
+
+
+/* strdtb(s) - delete trailing blanks in string 's' and return new length
+ */
+int
+strdtb(s)
+ char *s;
+{
+ char *x = s;
+
+ /* scan forward to the null
+ */
+ while (*x)
+ x++;
+
+ /* scan backward to either the first character before the string,
+ * or the last non-blank in the string, whichever comes first.
+ */
+ do {x--;}
+ while (x >= s && isspace(*x));
+
+ /* one character beyond where we stopped above is where the null
+ * goes.
+ */
+ *++x = '\0';
+
+ /* the difference between the position of the null character and
+ * the position of the first character of the string is the length.
+ */
+ return x - s;
+}
+
+
+int
+set_debug_flags(flags)
+ char *flags;
+{
+ /* debug flags are of the form flag[,flag ...]
+ *
+ * if an error occurs, print a message to stdout and return FALSE.
+ * otherwise return TRUE after setting ERROR_FLAGS.
+ */
+
+#if !DEBUGGING
+
+ printf("this program was compiled without debugging enabled\n");
+ return FALSE;
+
+#else /* DEBUGGING */
+
+ char *pc = flags;
+
+ DebugFlags = 0;
+
+ while (*pc) {
+ char **test;
+ int mask;
+
+ /* try to find debug flag name in our list.
+ */
+ for ( test = DebugFlagNames, mask = 1;
+ *test && strcmp_until(*test, pc, ',');
+ test++, mask <<= 1
+ )
+ ;
+
+ if (!*test) {
+ fprintf(stderr,
+ "unrecognized debug flag <%s> <%s>\n",
+ flags, pc);
+ return FALSE;
+ }
+
+ DebugFlags |= mask;
+
+ /* skip to the next flag
+ */
+ while (*pc && *pc != ',')
+ pc++;
+ if (*pc == ',')
+ pc++;
+ }
+
+ if (DebugFlags) {
+ int flag;
+
+ fprintf(stderr, "debug flags enabled:");
+
+ for (flag = 0; DebugFlagNames[flag]; flag++)
+ if (DebugFlags & (1 << flag))
+ fprintf(stderr, " %s", DebugFlagNames[flag]);
+ fprintf(stderr, "\n");
+ }
+
+ return TRUE;
+
+#endif /* DEBUGGING */
+}
+
+
+void
+set_cron_uid()
+{
+#if defined(BSD) || defined(POSIX)
+ if (seteuid(ROOT_UID) < OK)
+ err(ERROR_EXIT, "seteuid");
+#else
+ if (setuid(ROOT_UID) < OK)
+ err(ERROR_EXIT, "setuid");
+#endif
+}
+
+
+void
+set_cron_cwd()
+{
+ struct stat sb;
+
+ /* first check for CRONDIR ("/var/cron" or some such)
+ */
+ if (stat(CRONDIR, &sb) < OK && errno == ENOENT) {
+ warn("%s", CRONDIR);
+ if (OK == mkdir(CRONDIR, 0700)) {
+ warnx("%s: created", CRONDIR);
+ stat(CRONDIR, &sb);
+ } else {
+ err(ERROR_EXIT, "%s: mkdir", CRONDIR);
+ }
+ }
+ if (!(sb.st_mode & S_IFDIR))
+ err(ERROR_EXIT, "'%s' is not a directory, bailing out", CRONDIR);
+ if (chdir(CRONDIR) < OK)
+ err(ERROR_EXIT, "cannot chdir(%s), bailing out", CRONDIR);
+
+ /* CRONDIR okay (now==CWD), now look at SPOOL_DIR ("tabs" or some such)
+ */
+ if (stat(SPOOL_DIR, &sb) < OK && errno == ENOENT) {
+ warn("%s", SPOOL_DIR);
+ if (OK == mkdir(SPOOL_DIR, 0700)) {
+ warnx("%s: created", SPOOL_DIR);
+ stat(SPOOL_DIR, &sb);
+ } else {
+ err(ERROR_EXIT, "%s: mkdir", SPOOL_DIR);
+ }
+ }
+ if (!(sb.st_mode & S_IFDIR))
+ err(ERROR_EXIT, "'%s' is not a directory, bailing out", SPOOL_DIR);
+}
+
+
+/* get_char(file) : like getc() but increment LineNumber on newlines
+ */
+int
+get_char(file)
+ FILE *file;
+{
+ int ch;
+
+ ch = getc(file);
+ if (ch == '\n')
+ Set_LineNum(LineNumber + 1)
+ return ch;
+}
+
+
+/* unget_char(ch, file) : like ungetc but do LineNumber processing
+ */
+void
+unget_char(ch, file)
+ int ch;
+ FILE *file;
+{
+ ungetc(ch, file);
+ if (ch == '\n')
+ Set_LineNum(LineNumber - 1)
+}
+
+
+/* get_string(str, max, file, termstr) : like fgets() but
+ * (1) has terminator string which should include \n
+ * (2) will always leave room for the null
+ * (3) uses get_char() so LineNumber will be accurate
+ * (4) returns EOF or terminating character, whichever
+ */
+int
+get_string(string, size, file, terms)
+ char *string;
+ int size;
+ FILE *file;
+ char *terms;
+{
+ int ch;
+
+ while (EOF != (ch = get_char(file)) && !strchr(terms, ch)) {
+ if (size > 1) {
+ *string++ = (char) ch;
+ size--;
+ }
+ }
+
+ if (size > 0)
+ *string = '\0';
+
+ return ch;
+}
+
+
+/* skip_comments(file) : read past comment (if any)
+ */
+void
+skip_comments(file)
+ FILE *file;
+{
+ int ch;
+
+ while (EOF != (ch = get_char(file))) {
+ /* ch is now the first character of a line.
+ */
+
+ while (ch == ' ' || ch == '\t')
+ ch = get_char(file);
+
+ if (ch == EOF)
+ break;
+
+ /* ch is now the first non-blank character of a line.
+ */
+
+ if (ch != '\n' && ch != '#')
+ break;
+
+ /* ch must be a newline or comment as first non-blank
+ * character on a line.
+ */
+
+ while (ch != '\n' && ch != EOF)
+ ch = get_char(file);
+
+ /* ch is now the newline of a line which we're going to
+ * ignore.
+ */
+ }
+ if (ch != EOF)
+ unget_char(ch, file);
+}
+
+
+/* int in_file(char *string, FILE *file)
+ * return TRUE if one of the lines in file matches string exactly,
+ * FALSE otherwise.
+ */
+static int
+in_file(char *string, FILE *file)
+{
+ char line[MAX_TEMPSTR];
+
+ rewind(file);
+ while (fgets(line, MAX_TEMPSTR, file)) {
+ if (line[0] != '\0')
+ if (line[strlen(line)-1] == '\n')
+ line[strlen(line)-1] = '\0';
+ if (0 == strcmp(line, string))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/* int allowed(char *username)
+ * returns TRUE if (ALLOW_FILE exists and user is listed)
+ * or (DENY_FILE exists and user is NOT listed)
+ * or (neither file exists but user=="root" so it's okay)
+ */
+int
+allowed(username)
+ char *username;
+{
+ FILE *allow, *deny;
+ int isallowed;
+
+ isallowed = FALSE;
+
+ deny = NULL;
+#if defined(ALLOW_FILE) && defined(DENY_FILE)
+ if ((allow = fopen(ALLOW_FILE, "r")) == NULL && errno != ENOENT)
+ goto out;
+ if ((deny = fopen(DENY_FILE, "r")) == NULL && errno != ENOENT)
+ goto out;
+ Debug(DMISC, ("allow/deny enabled, %d/%d\n", !!allow, !!deny))
+#else
+ allow = NULL;
+#endif
+
+ if (allow)
+ isallowed = in_file(username, allow);
+ else if (deny)
+ isallowed = !in_file(username, deny);
+ else {
+#if defined(ALLOW_ONLY_ROOT)
+ isallowed = (strcmp(username, ROOT_USER) == 0);
+#else
+ isallowed = TRUE;
+#endif
+ }
+out: if (allow)
+ fclose(allow);
+ if (deny)
+ fclose(deny);
+ return (isallowed);
+}
+
+
+void
+log_it(username, xpid, event, detail)
+ char *username;
+ int xpid;
+ char *event;
+ char *detail;
+{
+ PID_T pid = xpid;
+#if defined(LOG_FILE)
+ char *msg;
+ TIME_T now = time((TIME_T) 0);
+ register struct tm *t = localtime(&now);
+#endif /*LOG_FILE*/
+
+#if defined(SYSLOG)
+ static int syslog_open = 0;
+#endif
+
+#if defined(LOG_FILE)
+ /* we assume that MAX_TEMPSTR will hold the date, time, &punctuation.
+ */
+ msg = malloc(strlen(username)
+ + strlen(event)
+ + strlen(detail)
+ + MAX_TEMPSTR);
+
+ if (msg == NULL)
+ warnx("failed to allocate memory for log message");
+ else {
+ if (LogFD < OK) {
+ LogFD = open(LOG_FILE, O_WRONLY|O_APPEND|O_CREAT, 0600);
+ if (LogFD < OK) {
+ warn("can't open log file %s", LOG_FILE);
+ } else {
+ (void) fcntl(LogFD, F_SETFD, 1);
+ }
+ }
+
+ /* we have to sprintf() it because fprintf() doesn't always
+ * write everything out in one chunk and this has to be
+ * atomically appended to the log file.
+ */
+ sprintf(msg, "%s (%02d/%02d-%02d:%02d:%02d-%d) %s (%s)\n",
+ username,
+ t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min,
+ t->tm_sec, pid, event, detail);
+
+ /* we have to run strlen() because sprintf() returns (char*)
+ * on old BSD.
+ */
+ if (LogFD < OK || write(LogFD, msg, strlen(msg)) < OK) {
+ if (LogFD >= OK)
+ warn("%s", LOG_FILE);
+ warnx("can't write to log file");
+ write(STDERR, msg, strlen(msg));
+ }
+
+ free(msg);
+ }
+#endif /*LOG_FILE*/
+
+#if defined(SYSLOG)
+ if (!syslog_open) {
+ /* we don't use LOG_PID since the pid passed to us by
+ * our client may not be our own. therefore we want to
+ * print the pid ourselves.
+ */
+# ifdef LOG_DAEMON
+ openlog(ProgramName, LOG_PID, LOG_CRON);
+# else
+ openlog(ProgramName, LOG_PID);
+# endif
+ syslog_open = TRUE; /* assume openlog success */
+ }
+
+ syslog(LOG_INFO, "(%s) %s (%s)\n", username, event, detail);
+
+#endif /*SYSLOG*/
+
+#if DEBUGGING
+ if (DebugFlags) {
+ fprintf(stderr, "log_it: (%s %d) %s (%s)\n",
+ username, pid, event, detail);
+ }
+#endif
+}
+
+
+void
+log_close() {
+ if (LogFD != ERR) {
+ close(LogFD);
+ LogFD = ERR;
+ }
+}
+
+
+/* two warnings:
+ * (1) this routine is fairly slow
+ * (2) it returns a pointer to static storage
+ */
+char *
+first_word(s, t)
+ register char *s; /* string we want the first word of */
+ register char *t; /* terminators, implicitly including \0 */
+{
+ static char retbuf[2][MAX_TEMPSTR + 1]; /* sure wish C had GC */
+ static int retsel = 0;
+ register char *rb, *rp;
+
+ /* select a return buffer */
+ retsel = 1-retsel;
+ rb = &retbuf[retsel][0];
+ rp = rb;
+
+ /* skip any leading terminators */
+ while (*s && (NULL != strchr(t, *s))) {
+ s++;
+ }
+
+ /* copy until next terminator or full buffer */
+ while (*s && (NULL == strchr(t, *s)) && (rp < &rb[MAX_TEMPSTR])) {
+ *rp++ = *s++;
+ }
+
+ /* finish the return-string and return it */
+ *rp = '\0';
+ return rb;
+}
+
+
+/* warning:
+ * heavily ascii-dependent.
+ */
+static void
+mkprint(register char *dst, register unsigned char *src, register int len)
+{
+ while (len-- > 0)
+ {
+ register unsigned char ch = *src++;
+
+ if (ch < ' ') { /* control character */
+ *dst++ = '^';
+ *dst++ = ch + '@';
+ } else if (ch < 0177) { /* printable */
+ *dst++ = ch;
+ } else if (ch == 0177) { /* delete/rubout */
+ *dst++ = '^';
+ *dst++ = '?';
+ } else { /* parity character */
+ sprintf(dst, "\\%03o", ch);
+ dst += 4;
+ }
+ }
+ *dst = '\0';
+}
+
+
+/* warning:
+ * returns a pointer to malloc'd storage, you must call free yourself.
+ */
+char *
+mkprints(src, len)
+ register unsigned char *src;
+ register unsigned int len;
+{
+ register char *dst = malloc(len*4 + 1);
+
+ if (dst != NULL)
+ mkprint(dst, src, len);
+
+ return dst;
+}
+
+
+#ifdef MAIL_DATE
+/* Sat, 27 Feb 93 11:44:51 CST
+ * 123456789012345678901234567
+ */
+char *
+arpadate(clock)
+ time_t *clock;
+{
+ time_t t = clock ?*clock :time(0L);
+ struct tm *tm = localtime(&t);
+ static char ret[32]; /* zone name might be >3 chars */
+
+ if (tm->tm_year >= 100)
+ tm->tm_year += 1900;
+
+ (void) snprintf(ret, sizeof(ret), "%s, %2d %s %d %02d:%02d:%02d %s",
+ DowNames[tm->tm_wday],
+ tm->tm_mday,
+ MonthNames[tm->tm_mon],
+ tm->tm_year,
+ tm->tm_hour,
+ tm->tm_min,
+ tm->tm_sec,
+ TZONE(*tm));
+ return ret;
+}
+#endif /*MAIL_DATE*/
+
+
+#ifdef HAVE_SAVED_UIDS
+static int save_euid;
+int swap_uids() { save_euid = geteuid(); return seteuid(getuid()); }
+int swap_uids_back() { return seteuid(save_euid); }
+#else /*HAVE_SAVED_UIDS*/
+int swap_uids() { return setreuid(geteuid(), getuid()); }
+int swap_uids_back() { return swap_uids(); }
+#endif /*HAVE_SAVED_UIDS*/
diff --git a/usr.sbin/crunch/COPYRIGHT b/usr.sbin/crunch/COPYRIGHT
new file mode 100644
index 0000000..c7b4d2f
--- /dev/null
+++ b/usr.sbin/crunch/COPYRIGHT
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 1994 University of Maryland
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of U.M. not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. U.M. makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
+ * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: James da Silva, Systems Design and Analysis Group
+ * Computer Science Department
+ * University of Maryland at College Park
+ */
diff --git a/usr.sbin/crunch/Makefile b/usr.sbin/crunch/Makefile
new file mode 100644
index 0000000..6bf600f
--- /dev/null
+++ b/usr.sbin/crunch/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+SUBDIR= crunchgen crunchide
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/crunch/Makefile.inc b/usr.sbin/crunch/Makefile.inc
new file mode 100644
index 0000000..f8ffe67
--- /dev/null
+++ b/usr.sbin/crunch/Makefile.inc
@@ -0,0 +1,4 @@
+# $FreeBSD$
+
+# modify to taste
+BINDIR?= /usr/bin
diff --git a/usr.sbin/crunch/README b/usr.sbin/crunch/README
new file mode 100644
index 0000000..27c2d02
--- /dev/null
+++ b/usr.sbin/crunch/README
@@ -0,0 +1,88 @@
+
+CRUNCH 0.2 README 6/14/94
+
+Crunch is available via anonymous ftp to ftp.cs.umd.edu in
+ pub/bsd/crunch-0.2.tar.gz
+
+
+WHAT'S NEW IN 0.2
+
+* The prototype awk script has been replaced by a more capable and
+ hopefully more robust C program.
+* No fragile template makefiles or dependencies on the details of the
+ bsd build environment.
+* You can build crunched binaries even with no sources on-line, you
+ just need the .o files. Crunchgen still will try to figure out as
+ much as possible on its own, but you can override its guessing by
+ specifying the list of .o files explicitly.
+* Crunch itself has been bmake'd and some man pages written, so it
+ should be ready to install.
+
+
+INTRODUCTION
+
+Crunch is a little package that helps create "crunched" binaries for use
+on boot, install, and fixit floppies. A crunched binary in this case is
+one where many programs have been linked together into one a.out file.
+The different programs are run depending on the value of argv[0], so
+hard links to the crunched binary suffice to simulate a perfectly normal
+system.
+
+As an example, I have created an 980K crunched "fixit" binary containing
+the following programs in their entirety:
+
+ cat chmod cp date dd df echo ed expr hostname kill ln ls mkdir
+ mt mv pwd rcp rm rmdir sh sleep stty sync test [ badsect chown
+ clri disklabel dump rdump dmesg fdisk fsck halt ifconfig init
+ mknod mount newfs ping reboot restore rrestore swapon umount
+ ftp rsh sed telnet rlogin vi cpio gzip gunzip gzcat
+
+Note carefully: vi, cpio, gzip, ed, sed, dump/restore, some networking
+utilities, and the disk management utilities, all in a binary small
+enough to fit on a 1.2 MB root filesystem floppy (albeit with the kernel
+on its own boot floppy). A more reasonable subset can be made to fit
+easily with a kernel for a decent one-disk fixit filesystem.
+
+The linking together of different programs by hand is an old
+space-saving technique. Crunch automates the process by building the
+necessary stub files and makefile for you (via the crunchgen program),
+and by doctoring the symbol tables of the component .o files to allow
+them to link without "symbol multiply defined" conflicts (via the
+crunchide program).
+
+
+BUILDING CRUNCH
+
+Just type make, then make install.
+
+Crunch was written and tested under NetBSD/i386, but should work under
+other PC BSD systems that use GNU ld.
+
+The crunchgen(1) and crunchide(1) man pages have more details on using
+crunch, and the examples subdirectory contains some working .conf files
+and a sample Makefile.
+
+CREDITS
+
+Thanks to the NetBSD team for a consistently high quality effort in
+bringing together a solid, state of the art development environment.
+
+Thanks to the FreeBSD guys; Rod Grimes, Nate Williams and Jordan
+Hubbard; and to Bruce Evans, for immediate and detailed feedback on
+crunch 0.1, and for pressing me to make the prototype more useable.
+
+Crunch was written for the Maruti Hard Real-Time Operating System
+project at the University of Maryland, to help make for better install
+and recovery procedures for our NetBSD-based development environment. It
+is copyright (c) 1994 by the University of Maryland under a UCB-style
+freely- redistributable notice. See the file COPYRIGHT for details.
+
+Please let me know of any problems or of enhancements you make to this
+package. I'm particularly interested in the details of what you found
+was good to put on your fixit or install disks. Thanks!
+
+Share and Enjoy,
+Jaime
+............................................................................
+: Stand on my shoulders, : jds@cs.umd.edu : James da Silva
+: not on my toes. : uunet!mimsy!jds : http://www.cs.umd.edu/users/jds
diff --git a/usr.sbin/crunch/crunchgen/Makefile b/usr.sbin/crunch/crunchgen/Makefile
new file mode 100644
index 0000000..8d0a78b
--- /dev/null
+++ b/usr.sbin/crunch/crunchgen/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+PROG= crunchgen
+SRCS= crunchgen.c crunched_skel.c
+CLEANFILES+= crunched_skel.c
+
+crunched_skel.c: crunched_main.c
+ sh -e ${.CURDIR}/mkskel.sh ${.CURDIR}/crunched_main.c >crunched_skel.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/crunch/crunchgen/crunched_main.c b/usr.sbin/crunch/crunchgen/crunched_main.c
new file mode 100644
index 0000000..df40d9d
--- /dev/null
+++ b/usr.sbin/crunch/crunchgen/crunched_main.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 1994 University of Maryland
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of U.M. not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. U.M. makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
+ * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: James da Silva, Systems Design and Analysis Group
+ * Computer Science Department
+ * University of Maryland at College Park
+ *
+ * $FreeBSD$
+ */
+/*
+ * crunched_main.c - main program for crunched binaries, it branches to a
+ * particular subprogram based on the value of argv[0]. Also included
+ * is a little program invoked when the crunched binary is called via
+ * its EXECNAME. This one prints out the list of compiled-in binaries,
+ * or calls one of them based on argv[1]. This allows the testing of
+ * the crunched binary without creating all the links.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct stub {
+ char *name;
+ int (*f)();
+};
+
+extern char *__progname;
+extern struct stub entry_points[];
+
+int main(int argc, char **argv, char **envp)
+{
+ char *slash, *basename;
+ struct stub *ep;
+
+ if(argv[0] == NULL || *argv[0] == '\0')
+ crunched_usage();
+
+ slash = strrchr(argv[0], '/');
+ basename = slash? slash+1 : argv[0];
+
+ for(ep=entry_points; ep->name != NULL; ep++)
+ if(!strcmp(basename, ep->name)) break;
+
+ if(ep->name)
+ return ep->f(argc, argv, envp);
+ else {
+ fprintf(stderr, "%s: %s not compiled in\n", EXECNAME, basename);
+ crunched_usage();
+ }
+}
+
+
+int crunched_here(char *path)
+{
+ char *slash, *basename;
+ struct stub *ep;
+
+ slash = strrchr(path, '/');
+ basename = slash? slash+1 : path;
+
+ for(ep=entry_points; ep->name != NULL; ep++)
+ if(!strcmp(basename, ep->name))
+ return 1;
+ return 0;
+}
+
+
+int crunched_main(int argc, char **argv, char **envp)
+{
+ char *slash;
+ struct stub *ep;
+ int columns, len;
+
+ if(argc <= 1)
+ crunched_usage();
+
+ slash = strrchr(argv[1], '/');
+ __progname = slash? slash+1 : argv[1];
+
+ return main(--argc, ++argv, envp);
+}
+
+
+int crunched_usage()
+{
+ int columns, len;
+ struct stub *ep;
+
+ fprintf(stderr, "usage: %s <prog> <args> ..., where <prog> is one of:\n",
+ EXECNAME);
+ columns = 0;
+ for(ep=entry_points; ep->name != NULL; ep++) {
+ len = strlen(ep->name) + 1;
+ if(columns+len < 80)
+ columns += len;
+ else {
+ fprintf(stderr, "\n");
+ columns = len;
+ }
+ fprintf(stderr, " %s", ep->name);
+ }
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+/* end of crunched_main.c */
+
diff --git a/usr.sbin/crunch/crunchgen/crunchgen.1 b/usr.sbin/crunch/crunchgen/crunchgen.1
new file mode 100644
index 0000000..b7313b9
--- /dev/null
+++ b/usr.sbin/crunch/crunchgen/crunchgen.1
@@ -0,0 +1,477 @@
+.\"
+.\" Copyright (c) 1994 University of Maryland
+.\" All Rights Reserved.
+.\"
+.\" Permission to use, copy, modify, distribute, and sell this software and its
+.\" documentation for any purpose is hereby granted without fee, provided that
+.\" the above copyright notice appear in all copies and that both that
+.\" copyright notice and this permission notice appear in supporting
+.\" documentation, and that the name of U.M. not be used in advertising or
+.\" publicity pertaining to distribution of the software without specific,
+.\" written prior permission. U.M. makes no representations about the
+.\" suitability of this software for any purpose. It is provided "as is"
+.\" without express or implied warranty.
+.\"
+.\" U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
+.\" BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+.\" IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Author: James da Silva, Systems Design and Analysis Group
+.\" Computer Science Department
+.\" University of Maryland at College Park
+.\" $FreeBSD$
+.\"
+.Dd December 23, 2005
+.Dt CRUNCHGEN 1
+.Os
+.Sh NAME
+.Nm crunchgen
+.Nd generates build environment for a crunched binary
+.Sh SYNOPSIS
+.Bk -words
+.Nm
+.Op Fl foql
+.Op Fl h Ar makefile-header-name
+.Op Fl m Ar makefile-name
+.Op Fl p Ar obj-prefix
+.Op Fl c Ar c-file-name
+.Op Fl e Ar exec-file-name
+.Op Ar conf-file
+.Ek
+.Sh DESCRIPTION
+A crunched binary is a program made up of many other programs linked
+together into a single executable.
+The crunched binary
+.Fn main
+function determines which component program to run by the contents of
+.Va argv[0] .
+The main reason to crunch programs together is for fitting
+as many programs as possible onto an installation or system recovery
+floppy.
+.Pp
+The
+.Nm
+utility reads in the specifications in
+.Ar conf-file
+for a crunched binary, and generates a
+.Pa Makefile
+and accompanying
+top-level C source file that when built creates the crunched executable
+file from the component programs.
+For each component program,
+.Nm
+can optionally attempt to determine the object (.o) files that make up
+the program from its source directory
+.Pa Makefile .
+This information is cached between runs.
+The
+.Nm
+utility uses the companion program
+.Xr crunchide 1
+to eliminate link-time conflicts between the component programs by
+hiding all unnecessary symbols.
+.Pp
+The
+.Nm
+utility places specific requirements on package
+.Pa Makefile Ns s
+which make it unsuitable for use with
+.No non- Ns Bx
+sources.
+In particular, the
+.Pa Makefile
+must contain the target
+.Ic depend ,
+and it must define all object files in the variable
+.Va OBJS .
+In some cases, you can use a fake
+.Pa Makefile :
+before looking for
+.Pa Makefile
+in the source directory
+.Pa foo ,
+.Nm
+looks for the file
+.Pa Makefile.foo
+in the current directory.
+.Pp
+After
+.Nm
+is run, the crunched binary can be built by running
+.Dq Li make -f <conf-name>.mk .
+The component programs' object files must already be built.
+An
+.Ic objs
+target, included in the output makefile, will
+run
+.Xr make 1
+in each component program's source dir to build the object
+files for the user.
+This is not done automatically since in release
+engineering circumstances it is generally not desirable to be
+modifying objects in other directories.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl c Ar c-file-name
+Set output C file name to
+.Ar c-file-name .
+The default name is
+.Pa <conf-name>.c .
+.It Fl e Ar exec-file-name
+Set crunched binary executable file name to
+.Ar exec-file-name .
+The default name is
+.Pa <conf-name> .
+.It Fl f
+Flush cache.
+Forces the recalculation of cached parameters.
+.It Fl l
+List names.
+Lists the names this binary will respond to.
+.It Fl h Ar makefile-header-name
+Set the name of a file to be included at the beginning of the
+.Pa Makefile Ns s
+generated by
+.Nm .
+This is useful to define some make variables such as
+.Va RELEASE_CRUNCH
+or similar, which might affect the behavior of
+.Xr make 1
+and are annoying to pass through environment variables.
+.It Fl m Ar makefile-name
+Set output
+.Pa Makefile
+name to
+.Ar makefile-name .
+The default name is
+.Pa <conf-name>.mk .
+.It Fl o
+Add
+.Dq Li make obj
+rules to each program make target.
+.It Fl p Ar obj-prefix
+Set the pathname to be prepended to the
+.Ic srcdir
+when computing the
+.Ic objdir .
+If this option is not present, then the prefix used
+is the content of the
+.Ev MAKEOBJDIRPREFIX
+environment variable, or
+.Pa /usr/obj .
+.It Fl q
+Quiet operation.
+Status messages are suppressed.
+.El
+.Sh CRUNCHGEN CONFIGURATION FILE COMMANDS
+The
+.Nm
+utility reads specifications from the
+.Ar conf-file
+that describe the components of the crunched binary.
+In its simplest
+use, the component program names are merely listed along with the
+top-level source directories in which their sources can be found.
+The
+.Nm
+utility then calculates (via the source makefiles) and caches the
+list of object files and their locations.
+For more specialized
+situations, the user can specify by hand all the parameters that
+.Nm
+needs.
+.Pp
+The
+.Ar conf-file
+commands are as follows:
+.Bl -tag -width indent
+.It Ic srcdirs Ar dirname ...
+A list of source trees in which the source directories of the
+component programs can be found.
+These dirs are searched using the
+.Bx
+.Dq Pa <source-dir>/<progname>/
+convention.
+Multiple
+.Ic srcdirs
+lines can be specified.
+The directories are searched in the order they are given.
+.It Ic progs Ar progname ...
+A list of programs that make up the crunched binary.
+Multiple
+.Ic progs
+lines can be specified.
+.It Ic libs Ar libspec ...
+A list of library specifications to be included in the crunched binary link.
+Multiple
+.Ic libs
+lines can be specified.
+.It Ic libs_so Ar libspec ...
+A list of library specifications to be dynamically linked in the
+crunched binary.
+These libraries will need to be made available via the run-time link-editor
+.Xr rtld 1
+when the component program that requires them is executed from
+the crunched binary.
+Multiple
+.Ic libs_so
+lines can be specified.
+The
+.Ic libs_so
+directive overrides a library specified gratuitously on a
+.Ic libs
+line.
+.It Ic buildopts Ar buildopts ...
+A list of build options to be added to every make target.
+.It Ic ln Ar progname linkname
+Causes the crunched binary to invoke
+.Ar progname
+whenever
+.Ar linkname
+appears in
+.Va argv[0] .
+This allows programs that change their behavior when
+run under different names to operate correctly.
+.El
+.Pp
+To handle specialized situations, such as when the source is not
+available or not built via a conventional
+.Pa Makefile ,
+the following
+.Ic special
+commands can be used to set
+.Nm
+parameters for a component program.
+.Bl -tag -width indent
+.It Ic special Ar progname Ic srcdir Ar pathname
+Set the source directory for
+.Ar progname .
+This is normally calculated by searching the specified
+.Ic srcdirs
+for a directory named
+.Ar progname .
+.It Ic special Ar progname Ic objdir Ar pathname
+Set the
+.Pa obj
+directory for
+.Ar progname .
+The
+.Pa obj
+directory is normally calculated by looking for a directory
+whose name is that of the source directory prepended by
+one of the following components, in order of priority:
+the
+.Fl p
+argument passed to the command line; or,
+the value of the
+.Ev MAKEOBJDIRPREFIX
+environment variable, or
+.Pa /usr/obj .
+If the directory is not found, the
+.Ic srcdir
+itself becomes the
+.Ic objdir .
+.It Ic special Ar progname Ic buildopts Ar buildopts
+Define a set of build options that should be added to
+.Xr make 1
+targets in addition to those specified using
+.Ic buildopts
+when processing
+.Ar progname .
+.It Ic special Ar progname Ic objs Ar object-file-name ...
+Set the list of object files for program
+.Ar progname .
+This is normally calculated by constructing a temporary makefile that includes
+.Dq Ic srcdir Ns / Ns Pa Makefile
+and outputs the value of
+.Va $(OBJS) .
+.It Ic special Ar progname Ic objpaths Ar full-pathname-to-object-file ...
+Sets the pathnames of the object files for program
+.Ar progname .
+This is normally calculated by prepending the
+.Ic objdir
+pathname to each file in the
+.Ic objs
+list.
+.It Ic special Ar progname Ic objvar Ar variable_name
+Sets the name of the
+.Xr make 1
+variable which holds the list of
+object files for program
+.Ar progname .
+This is normally
+.Va OBJS
+but some
+.Pa Makefile Ns s
+might like to use other conventions or
+prepend the program's name to the variable, e.g.\&
+.Va SSHD_OBJS .
+.It Ic special Ar progname Ic lib Ar library-name ...
+Specifies libraries to be linked with object files to produce
+.Ar progname Ns Pa .lo .
+This can be useful with libraries which redefine routines in
+the standard libraries, or poorly written libraries which
+reference symbols in the object files.
+.It Ic special Ar progname Ic keep Ar symbol-name ...
+Add specified list of symbols to the keep list for program
+.Ar progname .
+An underscore
+.Pq Ql _
+is prepended to each symbol and it becomes the argument to a
+.Fl k
+option for the
+.Xr crunchide 1
+phase.
+This option is to be used as a last resort as its use can cause a
+symbol conflict, however in certain instances it may be the only way to
+have a symbol resolve.
+.It Ic special Ar progname Ic ident Ar identifier
+Set the
+.Pa Makefile Ns / Ns Tn C
+identifier for
+.Ar progname .
+This is normally generated from a
+.Ar progname ,
+mapping
+.Ql -
+to
+.Ql _
+and ignoring all other non-identifier characters.
+This leads to programs named
+.Qq Li foo.bar
+and
+.Qq Li foobar
+to map to the same identifier.
+.El
+.Pp
+Only the
+.Ic objpaths
+parameter is actually needed by
+.Nm ,
+but it is calculated from
+.Ic objdir
+and
+.Ic objs ,
+which are in turn calculated from
+.Ic srcdir ,
+so is sometimes convenient to specify the earlier parameters and let
+.Nm
+calculate forward from there if it can.
+.Pp
+The makefile produced by
+.Nm
+contains an optional
+.Ic objs
+target that will build the object files for each component program by
+running
+.Xr make 1
+inside that program's source directory.
+For this to work the
+.Ic srcdir
+and
+.Ic objs
+parameters must also be valid.
+If they are not valid for a particular program, that
+program is skipped in the
+.Ic objs
+target.
+.Sh EXAMPLES
+Here is an example
+.Nm
+input conf file, named
+.Dq Pa kcopy.conf :
+.Pp
+.Bd -literal -offset indent
+srcdirs /usr/src/bin /usr/src/sbin
+
+progs test cp echo sh fsck halt init mount umount myinstall
+progs anotherprog
+ln test [ # test can be invoked via [
+ln sh -sh # init invokes the shell with "-sh" in argv[0]
+
+special myprog objpaths /homes/leroy/src/myinstall.o # no sources
+
+special anotherprog -DNO_FOO WITHOUT_BAR=YES
+
+libs -lutil -lcrypt
+.Ed
+.Pp
+This conf file specifies a small crunched binary consisting of some
+basic system utilities plus a homegrown install program
+.Dq Pa myinstall ,
+for which no source directory is specified, but its object file is
+specified directly with the
+.Ic special
+line.
+.Pp
+Additionally when
+.Dq Pa anotherprog
+is built the arguments
+.Pp
+.Dl -DNO_FOO WITHOUT_BAR=YES
+.Pp
+are added to all build targets.
+.Pp
+The crunched binary
+.Dq Pa kcopy
+can be built as follows:
+.Pp
+.Bd -literal -offset indent
+% crunchgen -m Makefile kcopy.conf # gen Makefile and kcopy.c
+% make objs # build the component programs' .o files
+% make # build the crunched binary kcopy
+% kcopy sh # test that this invokes a sh shell
+$ # it works!
+.Ed
+.Pp
+At this point the binary
+.Dq Pa kcopy
+can be copied onto an install floppy
+and hard-linked to the names of the component programs.
+.Pp
+Note that if the
+.Ic libs_so
+command had been used, copies of the libraries so named
+would also need to be copied to the install floppy.
+.Sh SEE ALSO
+.Xr crunchide 1 ,
+.Xr make 1 ,
+.Xr rtld 1
+.Sh CAVEATS
+While
+.Nm
+takes care to eliminate link conflicts between the component programs
+of a crunched binary, conflicts are still possible between the
+libraries that are linked in.
+Some shuffling in the order of
+libraries may be required, and in some rare cases two libraries may
+have an unresolvable conflict and thus cannot be crunched together.
+.Pp
+Some versions of the
+.Bx
+build environment do not by default build the
+intermediate object file for single-source file programs.
+The
+.Dq Li make objs
+must then be used to get those object files built, or
+some other arrangements made.
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility was written by
+.An James da Silva Aq jds@cs.umd.edu .
+.Pp
+Copyright (c) 1994 University of Maryland.
+All Rights Reserved.
+.Pp
+The
+.Ic libs_so
+keyword was added in 2005 by
+.An Adrian Steinmann Aq ast@marabu.ch
+and
+.An Ceri Davies Aq ceri@FreeBSD.org .
diff --git a/usr.sbin/crunch/crunchgen/crunchgen.c b/usr.sbin/crunch/crunchgen/crunchgen.c
new file mode 100644
index 0000000..752acc6
--- /dev/null
+++ b/usr.sbin/crunch/crunchgen/crunchgen.c
@@ -0,0 +1,1196 @@
+/*
+ * Copyright (c) 1994 University of Maryland
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of U.M. not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. U.M. makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
+ * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: James da Silva, Systems Design and Analysis Group
+ * Computer Science Department
+ * University of Maryland at College Park
+ *
+ * $FreeBSD$
+ */
+/*
+ * ========================================================================
+ * crunchgen.c
+ *
+ * Generates a Makefile and main C file for a crunched executable,
+ * from specs given in a .conf file.
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define CRUNCH_VERSION "0.2"
+
+#define MAXLINELEN 16384
+#define MAXFIELDS 2048
+
+
+/* internal representation of conf file: */
+
+/* simple lists of strings suffice for most parms */
+
+typedef struct strlst {
+ struct strlst *next;
+ char *str;
+} strlst_t;
+
+/* progs have structure, each field can be set with "special" or calculated */
+
+typedef struct prog {
+ struct prog *next; /* link field */
+ char *name; /* program name */
+ char *ident; /* C identifier for the program name */
+ char *srcdir;
+ char *realsrcdir;
+ char *objdir;
+ char *objvar; /* Makefile variable to replace OBJS */
+ strlst_t *objs, *objpaths;
+ strlst_t *buildopts;
+ strlst_t *keeplist;
+ strlst_t *links;
+ strlst_t *libs;
+ strlst_t *libs_so;
+ int goterror;
+} prog_t;
+
+
+/* global state */
+
+strlst_t *buildopts = NULL;
+strlst_t *srcdirs = NULL;
+strlst_t *libs = NULL;
+strlst_t *libs_so = NULL;
+prog_t *progs = NULL;
+
+char confname[MAXPATHLEN], infilename[MAXPATHLEN];
+char outmkname[MAXPATHLEN], outcfname[MAXPATHLEN], execfname[MAXPATHLEN];
+char tempfname[MAXPATHLEN], cachename[MAXPATHLEN], curfilename[MAXPATHLEN];
+char outhdrname[MAXPATHLEN] ; /* user-supplied header for *.mk */
+char *objprefix; /* where are the objects ? */
+int linenum = -1;
+int goterror = 0;
+
+int verbose, readcache; /* options */
+int reading_cache;
+int makeobj = 0; /* add 'make obj' rules to the makefile */
+
+int list_mode;
+
+/* general library routines */
+
+void status(char *str);
+void out_of_memory(void);
+void add_string(strlst_t **listp, char *str);
+int is_dir(char *pathname);
+int is_nonempty_file(char *pathname);
+int subtract_strlst(strlst_t **lista, strlst_t **listb);
+int in_list(strlst_t **listp, char *str);
+
+/* helper routines for main() */
+
+void usage(void);
+void parse_conf_file(void);
+void gen_outputs(void);
+
+
+int main(int argc, char **argv)
+{
+ char *p;
+ int optc;
+
+ verbose = 1;
+ readcache = 1;
+ *outmkname = *outcfname = *execfname = '\0';
+
+ p = getenv("MAKEOBJDIRPREFIX");
+ if (p == NULL || *p == '\0')
+ objprefix = "/usr/obj"; /* default */
+ else
+ if ((objprefix = strdup(p)) == NULL)
+ out_of_memory();
+
+ while((optc = getopt(argc, argv, "lh:m:c:e:p:foq")) != -1) {
+ switch(optc) {
+ case 'f':
+ readcache = 0;
+ break;
+ case 'o':
+ makeobj = 1;
+ break;
+ case 'q':
+ verbose = 0;
+ break;
+
+ case 'm':
+ strlcpy(outmkname, optarg, sizeof(outmkname));
+ break;
+ case 'p':
+ if ((objprefix = strdup(optarg)) == NULL)
+ out_of_memory();
+ break;
+
+ case 'h':
+ strlcpy(outhdrname, optarg, sizeof(outhdrname));
+ break;
+ case 'c':
+ strlcpy(outcfname, optarg, sizeof(outcfname));
+ break;
+ case 'e':
+ strlcpy(execfname, optarg, sizeof(execfname));
+ break;
+
+ case 'l':
+ list_mode++;
+ verbose = 0;
+ break;
+
+ case '?':
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ usage();
+
+ /*
+ * generate filenames
+ */
+
+ strlcpy(infilename, argv[0], sizeof(infilename));
+
+ /* confname = `basename infilename .conf` */
+
+ if ((p=strrchr(infilename, '/')) != NULL)
+ strlcpy(confname, p + 1, sizeof(confname));
+ else
+ strlcpy(confname, infilename, sizeof(confname));
+
+ if ((p=strrchr(confname, '.')) != NULL && !strcmp(p, ".conf"))
+ *p = '\0';
+
+ if (!*outmkname)
+ snprintf(outmkname, sizeof(outmkname), "%s.mk", confname);
+ if (!*outcfname)
+ snprintf(outcfname, sizeof(outcfname), "%s.c", confname);
+ if (!*execfname)
+ snprintf(execfname, sizeof(execfname), "%s", confname);
+
+ snprintf(cachename, sizeof(cachename), "%s.cache", confname);
+ snprintf(tempfname, sizeof(tempfname), "%s/crunchgen_%sXXXXXX",
+ getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP, confname);
+
+ parse_conf_file();
+ if (list_mode)
+ exit(goterror);
+
+ gen_outputs();
+
+ exit(goterror);
+}
+
+
+void usage(void)
+{
+ fprintf(stderr, "%s%s\n\t%s%s\n", "usage: crunchgen [-foq] ",
+ "[-h <makefile-header-name>] [-m <makefile>]",
+ "[-p <obj-prefix>] [-c <c-file-name>] [-e <exec-file>] ",
+ "<conffile>");
+ exit(1);
+}
+
+
+/*
+ * ========================================================================
+ * parse_conf_file subsystem
+ *
+ */
+
+/* helper routines for parse_conf_file */
+
+void parse_one_file(char *filename);
+void parse_line(char *line, int *fc, char **fv, int nf);
+void add_srcdirs(int argc, char **argv);
+void add_progs(int argc, char **argv);
+void add_link(int argc, char **argv);
+void add_libs(int argc, char **argv);
+void add_libs_so(int argc, char **argv);
+void add_buildopts(int argc, char **argv);
+void add_special(int argc, char **argv);
+
+prog_t *find_prog(char *str);
+void add_prog(char *progname);
+
+
+void parse_conf_file(void)
+{
+ if (!is_nonempty_file(infilename))
+ errx(1, "fatal: input file \"%s\" not found", infilename);
+
+ parse_one_file(infilename);
+ if (readcache && is_nonempty_file(cachename)) {
+ reading_cache = 1;
+ parse_one_file(cachename);
+ }
+}
+
+
+void parse_one_file(char *filename)
+{
+ char *fieldv[MAXFIELDS];
+ int fieldc;
+ void (*f)(int c, char **v);
+ FILE *cf;
+ char line[MAXLINELEN];
+
+ snprintf(line, sizeof(line), "reading %s", filename);
+ status(line);
+ strlcpy(curfilename, filename, sizeof(curfilename));
+
+ if ((cf = fopen(curfilename, "r")) == NULL) {
+ warn("%s", curfilename);
+ goterror = 1;
+ return;
+ }
+
+ linenum = 0;
+ while (fgets(line, MAXLINELEN, cf) != NULL) {
+ linenum++;
+ parse_line(line, &fieldc, fieldv, MAXFIELDS);
+
+ if (fieldc < 1)
+ continue;
+
+ if (!strcmp(fieldv[0], "srcdirs"))
+ f = add_srcdirs;
+ else if(!strcmp(fieldv[0], "progs"))
+ f = add_progs;
+ else if(!strcmp(fieldv[0], "ln"))
+ f = add_link;
+ else if(!strcmp(fieldv[0], "libs"))
+ f = add_libs;
+ else if(!strcmp(fieldv[0], "libs_so"))
+ f = add_libs_so;
+ else if(!strcmp(fieldv[0], "buildopts"))
+ f = add_buildopts;
+ else if(!strcmp(fieldv[0], "special"))
+ f = add_special;
+ else {
+ warnx("%s:%d: skipping unknown command `%s'",
+ curfilename, linenum, fieldv[0]);
+ goterror = 1;
+ continue;
+ }
+
+ if (fieldc < 2) {
+ warnx("%s:%d: %s %s",
+ curfilename, linenum, fieldv[0],
+ "command needs at least 1 argument, skipping");
+ goterror = 1;
+ continue;
+ }
+
+ f(fieldc, fieldv);
+ }
+
+ if (ferror(cf)) {
+ warn("%s", curfilename);
+ goterror = 1;
+ }
+ fclose(cf);
+}
+
+
+void parse_line(char *line, int *fc, char **fv, int nf)
+{
+ char *p;
+
+ p = line;
+ *fc = 0;
+
+ while (1) {
+ while (isspace(*p))
+ p++;
+
+ if (*p == '\0' || *p == '#')
+ break;
+
+ if (*fc < nf)
+ fv[(*fc)++] = p;
+
+ while (*p && !isspace(*p) && *p != '#')
+ p++;
+
+ if (*p == '\0' || *p == '#')
+ break;
+
+ *p++ = '\0';
+ }
+
+ if (*p)
+ *p = '\0'; /* needed for '#' case */
+}
+
+
+void add_srcdirs(int argc, char **argv)
+{
+ int i;
+
+ for (i = 1; i < argc; i++) {
+ if (is_dir(argv[i]))
+ add_string(&srcdirs, argv[i]);
+ else {
+ warnx("%s:%d: `%s' is not a directory, skipping it",
+ curfilename, linenum, argv[i]);
+ goterror = 1;
+ }
+ }
+}
+
+
+void add_progs(int argc, char **argv)
+{
+ int i;
+
+ for (i = 1; i < argc; i++)
+ add_prog(argv[i]);
+}
+
+
+void add_prog(char *progname)
+{
+ prog_t *p1, *p2;
+
+ /* add to end, but be smart about dups */
+
+ for (p1 = NULL, p2 = progs; p2 != NULL; p1 = p2, p2 = p2->next)
+ if (!strcmp(p2->name, progname))
+ return;
+
+ p2 = malloc(sizeof(prog_t));
+ if(p2) {
+ memset(p2, 0, sizeof(prog_t));
+ p2->name = strdup(progname);
+ }
+ if (!p2 || !p2->name)
+ out_of_memory();
+
+ p2->next = NULL;
+ if (p1 == NULL)
+ progs = p2;
+ else
+ p1->next = p2;
+
+ p2->ident = NULL;
+ p2->srcdir = NULL;
+ p2->realsrcdir = NULL;
+ p2->objdir = NULL;
+ p2->links = NULL;
+ p2->libs = NULL;
+ p2->libs_so = NULL;
+ p2->objs = NULL;
+ p2->keeplist = NULL;
+ p2->buildopts = NULL;
+ p2->goterror = 0;
+
+ if (list_mode)
+ printf("%s\n",progname);
+}
+
+
+void add_link(int argc, char **argv)
+{
+ int i;
+ prog_t *p = find_prog(argv[1]);
+
+ if (p == NULL) {
+ warnx("%s:%d: no prog %s previously declared, skipping link",
+ curfilename, linenum, argv[1]);
+ goterror = 1;
+ return;
+ }
+
+ for (i = 2; i < argc; i++) {
+ if (list_mode)
+ printf("%s\n",argv[i]);
+
+ add_string(&p->links, argv[i]);
+ }
+}
+
+
+void add_libs(int argc, char **argv)
+{
+ int i;
+
+ for(i = 1; i < argc; i++) {
+ add_string(&libs, argv[i]);
+ if ( in_list(&libs_so, argv[i]) )
+ warnx("%s:%d: "
+ "library `%s' specified as dynamic earlier",
+ curfilename, linenum, argv[i]);
+ }
+}
+
+
+void add_libs_so(int argc, char **argv)
+{
+ int i;
+
+ for(i = 1; i < argc; i++) {
+ add_string(&libs_so, argv[i]);
+ if ( in_list(&libs, argv[i]) )
+ warnx("%s:%d: "
+ "library `%s' specified as static earlier",
+ curfilename, linenum, argv[i]);
+ }
+}
+
+
+void add_buildopts(int argc, char **argv)
+{
+ int i;
+
+ for (i = 1; i < argc; i++)
+ add_string(&buildopts, argv[i]);
+}
+
+
+void add_special(int argc, char **argv)
+{
+ int i;
+ prog_t *p = find_prog(argv[1]);
+
+ if (p == NULL) {
+ if (reading_cache)
+ return;
+
+ warnx("%s:%d: no prog %s previously declared, skipping special",
+ curfilename, linenum, argv[1]);
+ goterror = 1;
+ return;
+ }
+
+ if (!strcmp(argv[2], "ident")) {
+ if (argc != 4)
+ goto argcount;
+ if ((p->ident = strdup(argv[3])) == NULL)
+ out_of_memory();
+ } else if (!strcmp(argv[2], "srcdir")) {
+ if (argc != 4)
+ goto argcount;
+ if ((p->srcdir = strdup(argv[3])) == NULL)
+ out_of_memory();
+ } else if (!strcmp(argv[2], "objdir")) {
+ if(argc != 4)
+ goto argcount;
+ if((p->objdir = strdup(argv[3])) == NULL)
+ out_of_memory();
+ } else if (!strcmp(argv[2], "objs")) {
+ p->objs = NULL;
+ for (i = 3; i < argc; i++)
+ add_string(&p->objs, argv[i]);
+ } else if (!strcmp(argv[2], "objpaths")) {
+ p->objpaths = NULL;
+ for (i = 3; i < argc; i++)
+ add_string(&p->objpaths, argv[i]);
+ } else if (!strcmp(argv[2], "keep")) {
+ p->keeplist = NULL;
+ for(i = 3; i < argc; i++)
+ add_string(&p->keeplist, argv[i]);
+ } else if (!strcmp(argv[2], "objvar")) {
+ if(argc != 4)
+ goto argcount;
+ if ((p->objvar = strdup(argv[3])) == NULL)
+ out_of_memory();
+ } else if (!strcmp(argv[2], "buildopts")) {
+ p->buildopts = NULL;
+ for (i = 3; i < argc; i++)
+ add_string(&p->buildopts, argv[i]);
+ } else if (!strcmp(argv[2], "lib")) {
+ for (i = 3; i < argc; i++)
+ add_string(&p->libs, argv[i]);
+ } else {
+ warnx("%s:%d: bad parameter name `%s', skipping line",
+ curfilename, linenum, argv[2]);
+ goterror = 1;
+ }
+ return;
+
+ argcount:
+ warnx("%s:%d: too %s arguments, expected \"special %s %s <string>\"",
+ curfilename, linenum, argc < 4? "few" : "many", argv[1], argv[2]);
+ goterror = 1;
+}
+
+
+prog_t *find_prog(char *str)
+{
+ prog_t *p;
+
+ for (p = progs; p != NULL; p = p->next)
+ if (!strcmp(p->name, str))
+ return p;
+
+ return NULL;
+}
+
+
+/*
+ * ========================================================================
+ * gen_outputs subsystem
+ *
+ */
+
+/* helper subroutines */
+
+void remove_error_progs(void);
+void fillin_program(prog_t *p);
+void gen_specials_cache(void);
+void gen_output_makefile(void);
+void gen_output_cfile(void);
+
+void fillin_program_objs(prog_t *p, char *path);
+void top_makefile_rules(FILE *outmk);
+void prog_makefile_rules(FILE *outmk, prog_t *p);
+void output_strlst(FILE *outf, strlst_t *lst);
+char *genident(char *str);
+char *dir_search(char *progname);
+
+
+void gen_outputs(void)
+{
+ prog_t *p;
+
+ for (p = progs; p != NULL; p = p->next)
+ fillin_program(p);
+
+ remove_error_progs();
+ gen_specials_cache();
+ gen_output_cfile();
+ gen_output_makefile();
+ status("");
+ fprintf(stderr,
+ "Run \"make -f %s\" to build crunched binary.\n", outmkname);
+}
+
+/*
+ * run the makefile for the program to find which objects are necessary
+ */
+void fillin_program(prog_t *p)
+{
+ char path[MAXPATHLEN];
+ char line[MAXLINELEN];
+ FILE *f;
+
+ snprintf(line, MAXLINELEN, "filling in parms for %s", p->name);
+ status(line);
+
+ if (!p->ident)
+ p->ident = genident(p->name);
+
+ /* look for the source directory if one wasn't specified by a special */
+ if (!p->srcdir) {
+ p->srcdir = dir_search(p->name);
+ }
+
+ /* Determine the actual srcdir (maybe symlinked). */
+ if (p->srcdir) {
+ snprintf(line, MAXLINELEN, "cd %s && echo -n `/bin/pwd`",
+ p->srcdir);
+ f = popen(line,"r");
+ if (!f)
+ errx(1, "Can't execute: %s\n", line);
+
+ path[0] = '\0';
+ fgets(path, sizeof path, f);
+ if (pclose(f))
+ errx(1, "Can't execute: %s\n", line);
+
+ if (!*path)
+ errx(1, "Can't perform pwd on: %s\n", p->srcdir);
+
+ p->realsrcdir = strdup(path);
+ }
+
+ /* Unless the option to make object files was specified the
+ * the objects will be built in the source directory unless
+ * an object directory already exists.
+ */
+ if (!makeobj && !p->objdir && p->srcdir) {
+ snprintf(line, sizeof line, "%s/%s", objprefix, p->realsrcdir);
+ if (is_dir(line)) {
+ if ((p->objdir = strdup(line)) == NULL)
+ out_of_memory();
+ } else
+ p->objdir = p->realsrcdir;
+ }
+
+ /*
+ * XXX look for a Makefile.{name} in local directory first.
+ * This lets us override the original Makefile.
+ */
+ snprintf(path, sizeof(path), "Makefile.%s", p->name);
+ if (is_nonempty_file(path)) {
+ snprintf(line, MAXLINELEN, "Using %s for %s", path, p->name);
+ status(line);
+ } else
+ if (p->srcdir)
+ snprintf(path, sizeof(path), "%s/Makefile", p->srcdir);
+ if (!p->objs && p->srcdir && is_nonempty_file(path))
+ fillin_program_objs(p, path);
+
+ if (!p->srcdir && !p->objdir && verbose)
+ warnx("%s: %s: %s",
+ "warning: could not find source directory",
+ infilename, p->name);
+ if (!p->objs && verbose)
+ warnx("%s: %s: warning: could not find any .o files",
+ infilename, p->name);
+
+ if ((!p->srcdir || !p->objdir) && !p->objs)
+ p->goterror = 1;
+}
+
+void fillin_program_objs(prog_t *p, char *path)
+{
+ char *obj, *cp;
+ int fd, rc;
+ FILE *f;
+ char *objvar="OBJS";
+ strlst_t *s;
+ char line[MAXLINELEN];
+
+ /* discover the objs from the srcdir Makefile */
+
+ if ((fd = mkstemp(tempfname)) == -1) {
+ perror(tempfname);
+ exit(1);
+ }
+ if ((f = fdopen(fd, "w")) == NULL) {
+ warn("%s", tempfname);
+ goterror = 1;
+ return;
+ }
+ if (p->objvar)
+ objvar = p->objvar;
+
+ /*
+ * XXX include outhdrname (e.g. to contain Make variables)
+ */
+ if (outhdrname[0] != '\0')
+ fprintf(f, ".include \"%s\"\n", outhdrname);
+ fprintf(f, ".include \"%s\"\n", path);
+ fprintf(f, ".POSIX:\n");
+ if (buildopts) {
+ fprintf(f, "BUILDOPTS+=");
+ output_strlst(f, buildopts);
+ }
+ fprintf(f, ".if defined(PROG)\n");
+ fprintf(f, "%s?=${PROG}.o\n", objvar);
+ fprintf(f, ".endif\n");
+ fprintf(f, "loop:\n\t@echo 'OBJS= '${%s}\n", objvar);
+
+ fprintf(f, "crunchgen_objs:\n"
+ "\t@cd %s && make -f %s $(BUILDOPTS) $(%s_OPTS)",
+ p->srcdir, tempfname, p->ident);
+ for (s = p->buildopts; s != NULL; s = s->next)
+ fprintf(f, " %s", s->str);
+ fprintf(f, " loop\n");
+
+ fclose(f);
+
+ snprintf(line, MAXLINELEN, "cd %s && make -f %s -B crunchgen_objs",
+ p->srcdir, tempfname);
+ if ((f = popen(line, "r")) == NULL) {
+ warn("submake pipe");
+ goterror = 1;
+ return;
+ }
+
+ while(fgets(line, MAXLINELEN, f)) {
+ if (strncmp(line, "OBJS= ", 6)) {
+ warnx("make error: %s", line);
+ goterror = 1;
+ continue;
+ }
+
+ cp = line + 6;
+ while (isspace(*cp))
+ cp++;
+
+ while(*cp) {
+ obj = cp;
+ while (*cp && !isspace(*cp))
+ cp++;
+ if (*cp)
+ *cp++ = '\0';
+ add_string(&p->objs, obj);
+ while (isspace(*cp))
+ cp++;
+ }
+ }
+
+ if ((rc=pclose(f)) != 0) {
+ warnx("make error: make returned %d", rc);
+ goterror = 1;
+ }
+
+ unlink(tempfname);
+}
+
+void remove_error_progs(void)
+{
+ prog_t *p1, *p2;
+
+ p1 = NULL; p2 = progs;
+ while (p2 != NULL) {
+ if (!p2->goterror)
+ p1 = p2, p2 = p2->next;
+ else {
+ /* delete it from linked list */
+ warnx("%s: %s: ignoring program because of errors",
+ infilename, p2->name);
+ if (p1)
+ p1->next = p2->next;
+ else
+ progs = p2->next;
+ p2 = p2->next;
+ }
+ }
+}
+
+void gen_specials_cache(void)
+{
+ FILE *cachef;
+ prog_t *p;
+ char line[MAXLINELEN];
+
+ snprintf(line, MAXLINELEN, "generating %s", cachename);
+ status(line);
+
+ if ((cachef = fopen(cachename, "w")) == NULL) {
+ warn("%s", cachename);
+ goterror = 1;
+ return;
+ }
+
+ fprintf(cachef, "# %s - parm cache generated from %s by crunchgen "
+ " %s\n\n",
+ cachename, infilename, CRUNCH_VERSION);
+
+ for (p = progs; p != NULL; p = p->next) {
+ fprintf(cachef, "\n");
+ if (p->srcdir)
+ fprintf(cachef, "special %s srcdir %s\n",
+ p->name, p->srcdir);
+ if (p->objdir)
+ fprintf(cachef, "special %s objdir %s\n",
+ p->name, p->objdir);
+ if (p->objs) {
+ fprintf(cachef, "special %s objs", p->name);
+ output_strlst(cachef, p->objs);
+ }
+ if (p->objpaths) {
+ fprintf(cachef, "special %s objpaths", p->name);
+ output_strlst(cachef, p->objpaths);
+ }
+ }
+ fclose(cachef);
+}
+
+
+void gen_output_makefile(void)
+{
+ prog_t *p;
+ FILE *outmk;
+ char line[MAXLINELEN];
+
+ snprintf(line, MAXLINELEN, "generating %s", outmkname);
+ status(line);
+
+ if ((outmk = fopen(outmkname, "w")) == NULL) {
+ warn("%s", outmkname);
+ goterror = 1;
+ return;
+ }
+
+ fprintf(outmk, "# %s - generated from %s by crunchgen %s\n\n",
+ outmkname, infilename, CRUNCH_VERSION);
+
+ if (outhdrname[0] != '\0')
+ fprintf(outmk, ".include \"%s\"\n", outhdrname);
+
+ top_makefile_rules(outmk);
+ for (p = progs; p != NULL; p = p->next)
+ prog_makefile_rules(outmk, p);
+
+ fprintf(outmk, "\n# ========\n");
+ fclose(outmk);
+}
+
+
+void gen_output_cfile(void)
+{
+ extern char *crunched_skel[];
+ char **cp;
+ FILE *outcf;
+ prog_t *p;
+ strlst_t *s;
+ char line[MAXLINELEN];
+
+ snprintf(line, MAXLINELEN, "generating %s", outcfname);
+ status(line);
+
+ if((outcf = fopen(outcfname, "w")) == NULL) {
+ warn("%s", outcfname);
+ goterror = 1;
+ return;
+ }
+
+ fprintf(outcf,
+ "/* %s - generated from %s by crunchgen %s */\n",
+ outcfname, infilename, CRUNCH_VERSION);
+
+ fprintf(outcf, "#define EXECNAME \"%s\"\n", execfname);
+ for (cp = crunched_skel; *cp != NULL; cp++)
+ fprintf(outcf, "%s\n", *cp);
+
+ for (p = progs; p != NULL; p = p->next)
+ fprintf(outcf, "extern int _crunched_%s_stub();\n", p->ident);
+
+ fprintf(outcf, "\nstruct stub entry_points[] = {\n");
+ for (p = progs; p != NULL; p = p->next) {
+ fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n",
+ p->name, p->ident);
+ for (s = p->links; s != NULL; s = s->next)
+ fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n",
+ s->str, p->ident);
+ }
+
+ fprintf(outcf, "\t{ EXECNAME, crunched_main },\n");
+ fprintf(outcf, "\t{ NULL, NULL }\n};\n");
+ fclose(outcf);
+}
+
+
+char *genident(char *str)
+{
+ char *n, *s, *d;
+
+ /*
+ * generates a Makefile/C identifier from a program name,
+ * mapping '-' to '_' and ignoring all other non-identifier
+ * characters. This leads to programs named "foo.bar" and
+ * "foobar" to map to the same identifier.
+ */
+
+ if ((n = strdup(str)) == NULL)
+ return NULL;
+ for (d = s = n; *s != '\0'; s++) {
+ if (*s == '-')
+ *d++ = '_';
+ else if (*s == '_' || isalnum(*s))
+ *d++ = *s;
+ }
+ *d = '\0';
+ return n;
+}
+
+
+char *dir_search(char *progname)
+{
+ char path[MAXPATHLEN];
+ strlst_t *dir;
+ char *srcdir;
+
+ for (dir = srcdirs; dir != NULL; dir = dir->next) {
+ snprintf(path, MAXPATHLEN, "%s/%s", dir->str, progname);
+ if (!is_dir(path))
+ continue;
+
+ if ((srcdir = strdup(path)) == NULL)
+ out_of_memory();
+
+ return srcdir;
+ }
+ return NULL;
+}
+
+
+void top_makefile_rules(FILE *outmk)
+{
+ prog_t *p;
+
+ if ( subtract_strlst(&libs, &libs_so) )
+ fprintf(outmk, "# NOTE: Some LIBS declarations below overridden by LIBS_SO\n");
+
+ fprintf(outmk, "LIBS+=");
+ output_strlst(outmk, libs);
+
+ fprintf(outmk, "LIBS_SO+=");
+ output_strlst(outmk, libs_so);
+
+ if (makeobj) {
+ fprintf(outmk, "MAKEOBJDIRPREFIX?=%s\n", objprefix);
+ fprintf(outmk, "MAKEENV=env MAKEOBJDIRPREFIX=$(MAKEOBJDIRPREFIX)\n");
+ fprintf(outmk, "CRUNCHMAKE=$(MAKEENV) $(MAKE)\n");
+ } else {
+ fprintf(outmk, "CRUNCHMAKE=$(MAKE)\n");
+ }
+
+ if (buildopts) {
+ fprintf(outmk, "BUILDOPTS+=");
+ output_strlst(outmk, buildopts);
+ }
+
+ fprintf(outmk, "CRUNCHED_OBJS=");
+ for (p = progs; p != NULL; p = p->next)
+ fprintf(outmk, " %s.lo", p->name);
+ fprintf(outmk, "\n");
+
+ fprintf(outmk, "SUBMAKE_TARGETS=");
+ for (p = progs; p != NULL; p = p->next)
+ fprintf(outmk, " %s_make", p->ident);
+ fprintf(outmk, "\nSUBCLEAN_TARGETS=");
+ for (p = progs; p != NULL; p = p->next)
+ fprintf(outmk, " %s_clean", p->ident);
+ fprintf(outmk, "\n\n");
+
+ fprintf(outmk, "all: objs exe\nobjs: $(SUBMAKE_TARGETS)\n");
+ fprintf(outmk, "exe: %s\n", execfname);
+ fprintf(outmk, "%s: %s.o $(CRUNCHED_OBJS) $(SUBMAKE_TARGETS)\n", execfname, execfname);
+ fprintf(outmk, ".if defined(LIBS_SO) && !empty(LIBS_SO)\n");
+ fprintf(outmk, "\t$(CC) -o %s %s.o $(CRUNCHED_OBJS) \\\n",
+ execfname, execfname);
+ fprintf(outmk, "\t\t-Xlinker -Bstatic $(LIBS) \\\n");
+ fprintf(outmk, "\t\t-Xlinker -Bdynamic $(LIBS_SO)\n");
+ fprintf(outmk, ".else\n");
+ fprintf(outmk, "\t$(CC) -static -o %s %s.o $(CRUNCHED_OBJS) $(LIBS)\n",
+ execfname, execfname);
+ fprintf(outmk, ".endif\n");
+ fprintf(outmk, "\tstrip %s\n", execfname);
+ fprintf(outmk, "realclean: clean subclean\n");
+ fprintf(outmk, "clean:\n\trm -f %s *.lo *.o *_stub.c\n", execfname);
+ fprintf(outmk, "subclean: $(SUBCLEAN_TARGETS)\n");
+}
+
+
+void prog_makefile_rules(FILE *outmk, prog_t *p)
+{
+ strlst_t *lst;
+
+ fprintf(outmk, "\n# -------- %s\n\n", p->name);
+
+ fprintf(outmk, "%s_OBJDIR=", p->ident);
+ if (p->objdir)
+ fprintf(outmk, "%s", p->objdir);
+ else
+ fprintf(outmk, "$(MAKEOBJDIRPREFIX)/$(%s_REALSRCDIR)\n",
+ p->ident);
+ fprintf(outmk, "\n");
+
+ fprintf(outmk, "%s_OBJPATHS=", p->ident);
+ if (p->objpaths)
+ output_strlst(outmk, p->objpaths);
+ else {
+ for (lst = p->objs; lst != NULL; lst = lst->next) {
+ fprintf(outmk, " $(%s_OBJDIR)/%s", p->ident, lst->str);
+ }
+ fprintf(outmk, "\n");
+ }
+
+ if (p->srcdir && p->objs) {
+ fprintf(outmk, "%s_SRCDIR=%s\n", p->ident, p->srcdir);
+ fprintf(outmk, "%s_REALSRCDIR=%s\n", p->ident, p->realsrcdir);
+
+ fprintf(outmk, "%s_OBJS=", p->ident);
+ output_strlst(outmk, p->objs);
+ if (p->buildopts != NULL) {
+ fprintf(outmk, "%s_OPTS+=", p->ident);
+ output_strlst(outmk, p->buildopts);
+ }
+#if 0
+ fprintf(outmk, "$(%s_OBJPATHS): %s_make\n\n", p->ident, p->ident);
+#endif
+ fprintf(outmk, "%s_make:\n", p->ident);
+ fprintf(outmk, "\t(cd $(%s_SRCDIR) && ", p->ident);
+ if (makeobj)
+ fprintf(outmk, "$(CRUNCHMAKE) obj && ");
+ fprintf(outmk, "\\\n");
+ fprintf(outmk, "\t\t$(CRUNCHMAKE) $(BUILDOPTS) $(%s_OPTS) depend &&",
+ p->ident);
+ fprintf(outmk, "\\\n");
+ fprintf(outmk, "\t\t$(CRUNCHMAKE) $(BUILDOPTS) $(%s_OPTS) "
+ "$(%s_OBJS))",
+ p->ident, p->ident);
+ fprintf(outmk, "\n");
+ fprintf(outmk, "%s_clean:\n", p->ident);
+ fprintf(outmk, "\t(cd $(%s_SRCDIR) && $(CRUNCHMAKE) $(BUILDOPTS) clean cleandepend)\n\n",
+ p->ident);
+ } else {
+ fprintf(outmk, "%s_make:\n", p->ident);
+ fprintf(outmk, "\t@echo \"** cannot make objs for %s\"\n\n",
+ p->name);
+ }
+
+ if (p->libs) {
+ fprintf(outmk, "%s_LIBS=", p->ident);
+ output_strlst(outmk, p->libs);
+ }
+
+ fprintf(outmk, "%s_stub.c:\n", p->name);
+ fprintf(outmk, "\techo \""
+ "int _crunched_%s_stub(int argc, char **argv, char **envp)"
+ "{return main(argc,argv,envp);}\" >%s_stub.c\n",
+ p->ident, p->name);
+ fprintf(outmk, "%s.lo: %s_stub.o $(%s_OBJPATHS)",
+ p->name, p->name, p->ident);
+ if (p->libs)
+ fprintf(outmk, " $(%s_LIBS)", p->ident);
+
+ fprintf(outmk, "\n");
+ fprintf(outmk, "\tld -dc -r -o %s.lo %s_stub.o $(%s_OBJPATHS)",
+ p->name, p->name, p->ident);
+ if (p->libs)
+ fprintf(outmk, " $(%s_LIBS)", p->ident);
+ fprintf(outmk, "\n");
+ fprintf(outmk, "\tcrunchide -k _crunched_%s_stub ", p->ident);
+ for (lst = p->keeplist; lst != NULL; lst = lst->next)
+ fprintf(outmk, "-k _%s ", lst->str);
+ fprintf(outmk, "%s.lo\n", p->name);
+}
+
+void output_strlst(FILE *outf, strlst_t *lst)
+{
+ for (; lst != NULL; lst = lst->next)
+ if ( strlen(lst->str) )
+ fprintf(outf, " %s", lst->str);
+ fprintf(outf, "\n");
+}
+
+
+/*
+ * ========================================================================
+ * general library routines
+ *
+ */
+
+void status(char *str)
+{
+ static int lastlen = 0;
+ int len, spaces;
+
+ if (!verbose)
+ return;
+
+ len = strlen(str);
+ spaces = lastlen - len;
+ if (spaces < 1)
+ spaces = 1;
+
+ fprintf(stderr, " [%s]%*.*s\r", str, spaces, spaces, " ");
+ fflush(stderr);
+ lastlen = len;
+}
+
+
+void out_of_memory(void)
+{
+ err(1, "%s: %d: out of memory, stopping", infilename, linenum);
+}
+
+
+void add_string(strlst_t **listp, char *str)
+{
+ strlst_t *p1, *p2;
+
+ /* add to end, but be smart about dups */
+
+ for (p1 = NULL, p2 = *listp; p2 != NULL; p1 = p2, p2 = p2->next)
+ if (!strcmp(p2->str, str))
+ return;
+
+ p2 = malloc(sizeof(strlst_t));
+ if (p2) {
+ p2->next = NULL;
+ p2->str = strdup(str);
+ }
+ if (!p2 || !p2->str)
+ out_of_memory();
+
+ if (p1 == NULL)
+ *listp = p2;
+ else
+ p1->next = p2;
+}
+
+int subtract_strlst(strlst_t **lista, strlst_t **listb)
+{
+ int subtract_count = 0;
+ strlst_t *p1;
+ for (p1 = *listb; p1 != NULL; p1 = p1->next)
+ if ( in_list(lista, p1->str) ) {
+ warnx("Will compile library `%s' dynamically", p1->str);
+ strcat(p1->str, "");
+ subtract_count++;
+ }
+ return subtract_count;
+}
+
+int in_list(strlst_t **listp, char *str)
+{
+ strlst_t *p1;
+ for (p1 = *listp; p1 != NULL; p1 = p1->next)
+ if (!strcmp(p1->str, str))
+ return 1;
+ return 0;
+}
+
+int is_dir(char *pathname)
+{
+ struct stat buf;
+
+ if (stat(pathname, &buf) == -1)
+ return 0;
+
+ return S_ISDIR(buf.st_mode);
+}
+
+int is_nonempty_file(char *pathname)
+{
+ struct stat buf;
+
+ if (stat(pathname, &buf) == -1)
+ return 0;
+
+ return S_ISREG(buf.st_mode) && buf.st_size > 0;
+}
diff --git a/usr.sbin/crunch/crunchgen/mkskel.sh b/usr.sbin/crunch/crunchgen/mkskel.sh
new file mode 100644
index 0000000..fd53d78
--- /dev/null
+++ b/usr.sbin/crunch/crunchgen/mkskel.sh
@@ -0,0 +1,15 @@
+#! /bin/sh
+# idea and sed lines taken straight from flex
+
+cat <<!EOF
+/* File created via mkskel.sh */
+
+char *crunched_skel[] = {
+!EOF
+
+sed 's/\\/&&/g' $* | sed 's/"/\\"/g' | sed 's/.*/ "&",/'
+
+cat <<!EOF
+ 0
+};
+!EOF
diff --git a/usr.sbin/crunch/crunchide/Makefile b/usr.sbin/crunch/crunchide/Makefile
new file mode 100644
index 0000000..0f85254
--- /dev/null
+++ b/usr.sbin/crunch/crunchide/Makefile
@@ -0,0 +1,23 @@
+# $FreeBSD$
+
+PROG= crunchide
+SRCS= crunchide.c
+
+TARGET_ARCH?= ${MACHINE_ARCH}
+
+.if ${TARGET_ARCH} == i386 && ${MACHINE_ARCH} == i386
+CFLAGS+=-DNLIST_AOUT
+SRCS+= exec_aout.c
+.endif
+
+.if ${TARGET_ARCH} == ia64 || \
+ ${TARGET_ARCH} == sparc64 || ${TARGET_ARCH} == amd64
+CFLAGS+=-DNLIST_ELF64
+SRCS+= exec_elf64.c
+exec_elf64.o: exec_elf32.c
+.else
+CFLAGS+=-DNLIST_ELF32
+SRCS+= exec_elf32.c
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/crunch/crunchide/crunchide.1 b/usr.sbin/crunch/crunchide/crunchide.1
new file mode 100644
index 0000000..ef9947d
--- /dev/null
+++ b/usr.sbin/crunch/crunchide/crunchide.1
@@ -0,0 +1,92 @@
+.\"
+.\" Copyright (c) 1994 University of Maryland
+.\" All Rights Reserved.
+.\"
+.\" Permission to use, copy, modify, distribute, and sell this software and its
+.\" documentation for any purpose is hereby granted without fee, provided that
+.\" the above copyright notice appear in all copies and that both that
+.\" copyright notice and this permission notice appear in supporting
+.\" documentation, and that the name of U.M. not be used in advertising or
+.\" publicity pertaining to distribution of the software without specific,
+.\" written prior permission. U.M. makes no representations about the
+.\" suitability of this software for any purpose. It is provided "as is"
+.\" without express or implied warranty.
+.\"
+.\" U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
+.\" BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+.\" IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Author: James da Silva, Systems Design and Analysis Group
+.\" Computer Science Department
+.\" University of Maryland at College Park
+.\" $FreeBSD$
+.\"
+.Dd June 14, 1994
+.Dt CRUNCHIDE 1
+.Os
+.Sh NAME
+.Nm crunchide
+.Nd hides symbol names from ld, for crunching programs together
+.Sh SYNOPSIS
+.Nm
+.Op Fl f Ar keep-list-file
+.Op Fl k Ar keep-symbol
+.Op Ar object-file ...
+.Sh DESCRIPTION
+The
+.Nm
+utility hides the global symbols of
+.Ar object-file
+such that they are ignored by subsequent runs of the linker,
+.Xr ld 1 .
+Some symbols may be left visible via the
+.Fl k Ar keep-symbol
+and
+.Fl f Ar keep-list-file
+options.
+The
+.Ar keep-list-file
+must contain a list of symbols to keep visible, one symbol per line.
+The names given by
+.Ar keep-symbol
+or in
+.Ar keep-list-file
+should be C names.
+For example,
+to keep the C function
+.Dq foo
+visible, the option
+.Dq -k foo
+should be used.
+.Pp
+The
+.Nm
+utility is designed as a companion program for
+.Xr crunchgen 1 ,
+which automates the process of creating crunched binaries from
+multiple component programs.
+.Sh SEE ALSO
+.Xr crunchgen 1 ,
+.Xr ld 1
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm crunch
+utility was written by
+.An James da Silva Aq jds@cs.umd.edu .
+.Pp
+Copyright (c) 1994 University of Maryland.
+All Rights Reserved.
+.Pp
+.An Chris Demetriou Aq cgd@netbsd.org
+reorganized
+.Nm
+so that it supported multiple object formats, and added
+ELF object support and ECOFF object recognition.
+.Pp
+Copyright (c) 1997
+.An Christopher G. Demetriou .
+All Rights Reserved.
diff --git a/usr.sbin/crunch/crunchide/crunchide.c b/usr.sbin/crunch/crunchide/crunchide.c
new file mode 100644
index 0000000..32c42db
--- /dev/null
+++ b/usr.sbin/crunch/crunchide/crunchide.c
@@ -0,0 +1,268 @@
+/* $NetBSD: crunchide.c,v 1.8 1997/11/01 06:51:45 lukem Exp $ */
+/* $FreeBSD$ */
+/*
+ * Copyright (c) 1997 Christopher G. Demetriou. All rights reserved.
+ * Copyright (c) 1994 University of Maryland
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of U.M. not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. U.M. makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
+ * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: James da Silva, Systems Design and Analysis Group
+ * Computer Science Department
+ * University of Maryland at College Park
+ */
+/*
+ * crunchide.c - tiptoes through an a.out symbol table, hiding all defined
+ * global symbols. Allows the user to supply a "keep list" of symbols
+ * that are not to be hidden. This program relies on the use of the
+ * linker's -dc flag to actually put global bss data into the file's
+ * bss segment (rather than leaving it as undefined "common" data).
+ *
+ * The point of all this is to allow multiple programs to be linked
+ * together without getting multiple-defined errors.
+ *
+ * For example, consider a program "foo.c". It can be linked with a
+ * small stub routine, called "foostub.c", eg:
+ * int foo_main(int argc, char **argv){ return main(argc, argv); }
+ * like so:
+ * cc -c foo.c foostub.c
+ * ld -dc -r foo.o foostub.o -o foo.combined.o
+ * crunchide -k _foo_main foo.combined.o
+ * at this point, foo.combined.o can be linked with another program
+ * and invoked with "foo_main(argc, argv)". foo's main() and any
+ * other globals are hidden and will not conflict with other symbols.
+ *
+ * TODO:
+ * - resolve the theoretical hanging reloc problem (see check_reloc()
+ * below). I have yet to see this problem actually occur in any real
+ * program. In what cases will gcc/gas generate code that needs a
+ * relative reloc from a global symbol, other than PIC? The
+ * solution is to not hide the symbol from the linker in this case,
+ * but to generate some random name for it so that it doesn't link
+ * with anything but holds the place for the reloc.
+ * - arrange that all the BSS segments start at the same address, so
+ * that the final crunched binary BSS size is the max of all the
+ * component programs' BSS sizes, rather than their sum.
+ */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: crunchide.c,v 1.8 1997/11/01 06:51:45 lukem Exp $");
+#endif
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <a.out.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+
+#include "extern.h"
+
+char *pname = "crunchide";
+
+void usage(void);
+
+void add_to_keep_list(char *symbol);
+void add_file_to_keep_list(char *filename);
+
+int hide_syms(const char *filename);
+
+int verbose;
+
+int main(int, char *[]);
+
+int main(argc, argv)
+int argc;
+char **argv;
+{
+ int ch, errors;
+
+ if(argc > 0) pname = argv[0];
+
+ while ((ch = getopt(argc, argv, "k:f:v")) != -1)
+ switch(ch) {
+ case 'k':
+ add_to_keep_list(optarg);
+ break;
+ case 'f':
+ add_file_to_keep_list(optarg);
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if(argc == 0) usage();
+
+ errors = 0;
+ while(argc) {
+ if (hide_syms(*argv))
+ errors = 1;
+ argc--, argv++;
+ }
+
+ return errors;
+}
+
+void usage(void)
+{
+ fprintf(stderr,
+ "usage: %s [-k <symbol-name>] [-f <keep-list-file>] <files> ...\n",
+ pname);
+ exit(1);
+}
+
+/* ---------------------------- */
+
+struct keep {
+ struct keep *next;
+ char *sym;
+} *keep_list;
+
+void add_to_keep_list(char *symbol)
+{
+ struct keep *newp, *prevp, *curp;
+ int cmp;
+
+ cmp = 0;
+
+ for(curp = keep_list, prevp = NULL; curp; prevp = curp, curp = curp->next)
+ if((cmp = strcmp(symbol, curp->sym)) <= 0) break;
+
+ if(curp && cmp == 0)
+ return; /* already in table */
+
+ newp = (struct keep *) malloc(sizeof(struct keep));
+ if(newp) newp->sym = strdup(symbol);
+ if(newp == NULL || newp->sym == NULL) {
+ fprintf(stderr, "%s: out of memory for keep list\n", pname);
+ exit(1);
+ }
+
+ newp->next = curp;
+ if(prevp) prevp->next = newp;
+ else keep_list = newp;
+}
+
+int in_keep_list(const char *symbol)
+{
+ struct keep *curp;
+ int cmp;
+
+ cmp = 0;
+
+ for(curp = keep_list; curp; curp = curp->next)
+ if((cmp = strcmp(symbol, curp->sym)) <= 0) break;
+
+ return curp && cmp == 0;
+}
+
+void add_file_to_keep_list(char *filename)
+{
+ FILE *keepf;
+ char symbol[1024];
+ int len;
+
+ if((keepf = fopen(filename, "r")) == NULL) {
+ perror(filename);
+ usage();
+ }
+
+ while(fgets(symbol, sizeof(symbol), keepf)) {
+ len = strlen(symbol);
+ if(len && symbol[len-1] == '\n')
+ symbol[len-1] = '\0';
+
+ add_to_keep_list(symbol);
+ }
+ fclose(keepf);
+}
+
+/* ---------------------------- */
+
+struct {
+ const char *name;
+ int (*check)(int, const char *); /* 1 if match, zero if not */
+ int (*hide)(int, const char *); /* non-zero if error */
+} exec_formats[] = {
+#ifdef NLIST_AOUT
+ { "a.out", check_aout, hide_aout, },
+#endif
+#ifdef NLIST_ECOFF
+ { "ECOFF", check_elf64, hide_elf64, },
+#endif
+#ifdef NLIST_ELF32
+ { "ELF32", check_elf32, hide_elf32, },
+#endif
+#ifdef NLIST_ELF64
+ { "ELF64", check_elf64, hide_elf64, },
+#endif
+};
+
+int hide_syms(const char *filename)
+{
+ int fd, i, n, rv;
+
+ fd = open(filename, O_RDWR, 0);
+ if (fd == -1) {
+ perror(filename);
+ return 1;
+ }
+
+ rv = 0;
+
+ n = sizeof exec_formats / sizeof exec_formats[0];
+ for (i = 0; i < n; i++) {
+ if (lseek(fd, 0, SEEK_SET) != 0) {
+ perror(filename);
+ goto err;
+ }
+ if ((*exec_formats[i].check)(fd, filename) != 0)
+ break;
+ }
+ if (i == n) {
+ fprintf(stderr, "%s: unknown executable format\n", filename);
+ goto err;
+ }
+
+ if (verbose)
+ fprintf(stderr, "%s is an %s binary\n", filename,
+ exec_formats[i].name);
+
+ if (lseek(fd, 0, SEEK_SET) != 0) {
+ perror(filename);
+ goto err;
+ }
+ rv = (*exec_formats[i].hide)(fd, filename);
+
+out:
+ close (fd);
+ return (rv);
+
+err:
+ rv = 1;
+ goto out;
+}
diff --git a/usr.sbin/crunch/crunchide/exec_aout.c b/usr.sbin/crunch/crunchide/exec_aout.c
new file mode 100644
index 0000000..a608c23
--- /dev/null
+++ b/usr.sbin/crunch/crunchide/exec_aout.c
@@ -0,0 +1,198 @@
+/* $NetBSD: exec_aout.c,v 1.6 1997/08/02 21:30:17 perry Exp $ */
+/*
+ * Copyright (c) 1997 Christopher G. Demetriou. All rights reserved.
+ * Copyright (c) 1994 University of Maryland
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of U.M. not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. U.M. makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
+ * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: James da Silva, Systems Design and Analysis Group
+ * Computer Science Department
+ * University of Maryland at College Park
+ */
+#include <sys/cdefs.h>
+#ifndef lint
+__RCSID("$NetBSD: exec_aout.c,v 1.6 1997/08/02 21:30:17 perry Exp $");
+__FBSDID("$FreeBSD$");
+#endif
+
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <a.out.h>
+#include <sys/types.h>
+#include <sys/endian.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <netinet/in.h>
+
+#include "extern.h"
+
+#if defined(NLIST_AOUT)
+
+int nsyms, ntextrel, ndatarel;
+struct exec *hdrp;
+char *aoutdata, *strbase;
+struct relocation_info *textrel, *datarel;
+struct nlist *symbase;
+
+
+#define SYMSTR(sp) (&strbase[(sp)->n_un.n_strx])
+
+/* is the symbol a global symbol defined in the current file? */
+#define IS_GLOBAL_DEFINED(sp) \
+ (((sp)->n_type & N_EXT) && ((sp)->n_type & N_TYPE) != N_UNDF)
+
+/* is the relocation entry dependent on a symbol? */
+#define IS_SYMBOL_RELOC(rp) \
+ ((rp)->r_extern||(rp)->r_baserel||(rp)->r_jmptable)
+
+static void check_reloc(const char *filename, struct relocation_info *relp);
+
+int check_aout(int inf, const char *filename)
+{
+ struct stat infstat;
+ struct exec eh;
+
+ /*
+ * check the header to make sure it's an a.out-format file.
+ */
+
+ if(fstat(inf, &infstat) == -1)
+ return 0;
+ if(infstat.st_size < sizeof eh)
+ return 0;
+ if(read(inf, &eh, sizeof eh) != sizeof eh)
+ return 0;
+
+ if(N_BADMAG(eh))
+ return 0;
+
+ return 1;
+}
+
+int hide_aout(int inf, const char *filename)
+{
+ struct stat infstat;
+ struct relocation_info *relp;
+ struct nlist *symp;
+ int rc;
+
+ /*
+ * do some error checking.
+ */
+
+ if(fstat(inf, &infstat) == -1) {
+ perror(filename);
+ return 1;
+ }
+
+ /*
+ * Read the entire file into memory. XXX - Really, we only need to
+ * read the header and from TRELOFF to the end of the file.
+ */
+
+ if((aoutdata = (char *) malloc(infstat.st_size)) == NULL) {
+ fprintf(stderr, "%s: too big to read into memory\n", filename);
+ return 1;
+ }
+
+ if((rc = read(inf, aoutdata, infstat.st_size)) < infstat.st_size) {
+ fprintf(stderr, "%s: read error: %s\n", filename,
+ rc == -1? strerror(errno) : "short read");
+ return 1;
+ }
+
+ /*
+ * Calculate offsets and sizes from the header.
+ */
+
+ hdrp = (struct exec *) aoutdata;
+
+#ifdef __FreeBSD__
+ textrel = (struct relocation_info *) (aoutdata + N_RELOFF(*hdrp));
+ datarel = (struct relocation_info *) (aoutdata + N_RELOFF(*hdrp) +
+ hdrp->a_trsize);
+#else
+ textrel = (struct relocation_info *) (aoutdata + N_TRELOFF(*hdrp));
+ datarel = (struct relocation_info *) (aoutdata + N_DRELOFF(*hdrp));
+#endif
+ symbase = (struct nlist *) (aoutdata + N_SYMOFF(*hdrp));
+ strbase = (char *) (aoutdata + N_STROFF(*hdrp));
+
+ ntextrel = hdrp->a_trsize / sizeof(struct relocation_info);
+ ndatarel = hdrp->a_drsize / sizeof(struct relocation_info);
+ nsyms = hdrp->a_syms / sizeof(struct nlist);
+
+ /*
+ * Zap the type field of all globally-defined symbols. The linker will
+ * subsequently ignore these entries. Don't zap any symbols in the
+ * keep list.
+ */
+
+ for(symp = symbase; symp < symbase + nsyms; symp++) {
+ if(!IS_GLOBAL_DEFINED(symp)) /* keep undefined syms */
+ continue;
+
+ /* keep (C) symbols which are on the keep list */
+ if(SYMSTR(symp)[0] == '_' && in_keep_list(SYMSTR(symp) + 1))
+ continue;
+
+ symp->n_type = 0;
+ }
+
+ /*
+ * Check whether the relocation entries reference any symbols that we
+ * just zapped. I don't know whether ld can handle this case, but I
+ * haven't encountered it yet. These checks are here so that the program
+ * doesn't fail silently should such symbols be encountered.
+ */
+
+ for(relp = textrel; relp < textrel + ntextrel; relp++)
+ check_reloc(filename, relp);
+ for(relp = datarel; relp < datarel + ndatarel; relp++)
+ check_reloc(filename, relp);
+
+ /*
+ * Write the .o file back out to disk. XXX - Really, we only need to
+ * write the symbol table entries back out.
+ */
+ lseek(inf, 0, SEEK_SET);
+ if((rc = write(inf, aoutdata, infstat.st_size)) < infstat.st_size) {
+ fprintf(stderr, "%s: write error: %s\n", filename,
+ rc == -1? strerror(errno) : "short write");
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void check_reloc(const char *filename, struct relocation_info *relp)
+{
+ /* bail out if we zapped a symbol that is needed */
+ if(IS_SYMBOL_RELOC(relp) && symbase[relp->r_symbolnum].n_type == 0) {
+ fprintf(stderr,
+ "%s: oops, have hanging relocation for %s: bailing out!\n",
+ filename, SYMSTR(&symbase[relp->r_symbolnum]));
+ exit(1);
+ }
+}
+
+#endif /* defined(NLIST_AOUT) */
diff --git a/usr.sbin/crunch/crunchide/exec_elf32.c b/usr.sbin/crunch/crunchide/exec_elf32.c
new file mode 100644
index 0000000..1081ad8
--- /dev/null
+++ b/usr.sbin/crunch/crunchide/exec_elf32.c
@@ -0,0 +1,433 @@
+/*
+ * Copyright (c) 1997 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
+ * for the NetBSD Project.
+ * 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>
+#ifndef lint
+#if 0
+__RCSID("$NetBSD: exec_elf32.c,v 1.4 1997/08/12 06:07:24 mikel Exp $");
+#endif
+#endif
+__FBSDID("$FreeBSD$");
+
+#ifndef ELFSIZE
+#define ELFSIZE 32
+#endif
+
+#include <sys/types.h>
+#include <sys/endian.h>
+#include <sys/stat.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+#if (defined(NLIST_ELF32) && (ELFSIZE == 32)) || \
+ (defined(NLIST_ELF64) && (ELFSIZE == 64))
+
+#define __ELF_WORD_SIZE ELFSIZE
+#if (ELFSIZE == 32)
+#include <sys/elf32.h>
+#define xewtoh(x) ((data == ELFDATA2MSB) ? be32toh(x) : le32toh(x))
+#define htoxew(x) ((data == ELFDATA2MSB) ? htobe32(x) : htole32(x))
+#elif (ELFSIZE == 64)
+#include <sys/elf64.h>
+#define xewtoh(x) ((data == ELFDATA2MSB) ? be64toh(x) : le64toh(x))
+#define htoxew(x) ((data == ELFDATA2MSB) ? htobe64(x) : htole64(x))
+#endif
+#include <sys/elf_generic.h>
+
+#define CONCAT(x,y) __CONCAT(x,y)
+#define ELFNAME(x) CONCAT(elf,CONCAT(ELFSIZE,CONCAT(_,x)))
+#define ELFNAME2(x,y) CONCAT(x,CONCAT(_elf,CONCAT(ELFSIZE,CONCAT(_,y))))
+#define ELFNAMEEND(x) CONCAT(x,CONCAT(_elf,ELFSIZE))
+#define ELFDEFNNAME(x) CONCAT(ELF,CONCAT(ELFSIZE,CONCAT(_,x)))
+
+#define xe16toh(x) ((data == ELFDATA2MSB) ? be16toh(x) : le16toh(x))
+#define xe32toh(x) ((data == ELFDATA2MSB) ? be32toh(x) : le32toh(x))
+#define htoxe32(x) ((data == ELFDATA2MSB) ? htobe32(x) : htole32(x))
+
+struct listelem {
+ struct listelem *next;
+ void *mem;
+ off_t file;
+ size_t size;
+};
+
+static ssize_t
+xreadatoff(int fd, void *buf, off_t off, size_t size, const char *fn)
+{
+ ssize_t rv;
+
+ if (lseek(fd, off, SEEK_SET) != off) {
+ perror(fn);
+ return -1;
+ }
+ if ((rv = read(fd, buf, size)) != size) {
+ fprintf(stderr, "%s: read error: %s\n", fn,
+ rv == -1 ? strerror(errno) : "short read");
+ return -1;
+ }
+ return size;
+}
+
+static ssize_t
+xwriteatoff(int fd, void *buf, off_t off, size_t size, const char *fn)
+{
+ ssize_t rv;
+
+ if (lseek(fd, off, SEEK_SET) != off) {
+ perror(fn);
+ return -1;
+ }
+ if ((rv = write(fd, buf, size)) != size) {
+ fprintf(stderr, "%s: write error: %s\n", fn,
+ rv == -1 ? strerror(errno) : "short write");
+ return -1;
+ }
+ return size;
+}
+
+static void *
+xmalloc(size_t size, const char *fn, const char *use)
+{
+ void *rv;
+
+ rv = malloc(size);
+ if (rv == NULL)
+ fprintf(stderr, "%s: out of memory (allocating for %s)\n",
+ fn, use);
+ return (rv);
+}
+
+int
+ELFNAMEEND(check)(int fd, const char *fn)
+{
+ Elf_Ehdr eh;
+ struct stat sb;
+ unsigned char data;
+
+ /*
+ * Check the header to maek sure it's an ELF file (of the
+ * appropriate size).
+ */
+ if (fstat(fd, &sb) == -1)
+ return 0;
+ if (sb.st_size < sizeof eh)
+ return 0;
+ if (read(fd, &eh, sizeof eh) != sizeof eh)
+ return 0;
+
+ if (IS_ELF(eh) == 0)
+ return 0;
+
+ data = eh.e_ident[EI_DATA];
+
+ switch (xe16toh(eh.e_machine)) {
+ case EM_386: break;
+ case EM_ALPHA: break;
+#ifndef EM_ARM
+#define EM_ARM 40
+#endif
+ case EM_ARM: break;
+#ifndef EM_MIPS
+#define EM_MIPS 8
+#endif
+#ifndef EM_MIPS_RS4_BE /* same as EM_MIPS_RS3_LE */
+#define EM_MIPS_RS4_BE 10
+#endif
+ case EM_MIPS: break;
+ case /* EM_MIPS_RS3_LE */ EM_MIPS_RS4_BE: break;
+#ifndef EM_IA_64
+#define EM_IA_64 50
+#endif
+ case EM_IA_64: break;
+#ifndef EM_PPC
+#define EM_PPC 20
+#endif
+ case EM_PPC: break;
+#ifndef EM_SPARCV9
+#define EM_SPARCV9 43
+#endif
+ case EM_SPARCV9: break;
+#ifndef EM_X86_64
+#define EM_X86_64 62
+#endif
+ case EM_X86_64: break;
+/* ELFDEFNNAME(MACHDEP_ID_CASES) */
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+int
+ELFNAMEEND(hide)(int fd, const char *fn)
+{
+ Elf_Ehdr ehdr;
+ Elf_Shdr *shdrp = NULL, *symtabshdr, *strtabshdr;
+ Elf_Sym *symtabp = NULL;
+ char *strtabp = NULL;
+ Elf_Size *symfwmap = NULL, *symrvmap = NULL, nsyms, nlocalsyms, ewi;
+ struct listelem *relalist = NULL, *rellist = NULL, *tmpl;
+ ssize_t shdrsize;
+ int rv, i, weird;
+ unsigned char data;
+
+ rv = 0;
+ if (xreadatoff(fd, &ehdr, 0, sizeof ehdr, fn) != sizeof ehdr)
+ goto bad;
+
+ data = ehdr.e_ident[EI_DATA];
+
+ shdrsize = xe16toh(ehdr.e_shnum) * xe16toh(ehdr.e_shentsize);
+ if ((shdrp = xmalloc(shdrsize, fn, "section header table")) == NULL)
+ goto bad;
+ if (xreadatoff(fd, shdrp, xewtoh(ehdr.e_shoff), shdrsize, fn) !=
+ shdrsize)
+ goto bad;
+
+ symtabshdr = strtabshdr = NULL;
+ weird = 0;
+ for (i = 0; i < xe16toh(ehdr.e_shnum); i++) {
+ switch (xe32toh(shdrp[i].sh_type)) {
+ case SHT_SYMTAB:
+ if (symtabshdr != NULL)
+ weird = 1;
+ symtabshdr = &shdrp[i];
+ strtabshdr = &shdrp[xe32toh(shdrp[i].sh_link)];
+ break;
+ case SHT_RELA:
+ tmpl = xmalloc(sizeof *tmpl, fn, "rela list element");
+ if (tmpl == NULL)
+ goto bad;
+ tmpl->mem = NULL;
+ tmpl->file = xewtoh(shdrp[i].sh_offset);
+ tmpl->size = xewtoh(shdrp[i].sh_size);
+ tmpl->next = relalist;
+ relalist = tmpl;
+ break;
+ case SHT_REL:
+ tmpl = xmalloc(sizeof *tmpl, fn, "rel list element");
+ if (tmpl == NULL)
+ goto bad;
+ tmpl->mem = NULL;
+ tmpl->file = xewtoh(shdrp[i].sh_offset);
+ tmpl->size = xewtoh(shdrp[i].sh_size);
+ tmpl->next = rellist;
+ rellist = tmpl;
+ break;
+ }
+ }
+ if (symtabshdr == NULL)
+ goto out;
+ if (strtabshdr == NULL)
+ weird = 1;
+ if (weird) {
+ fprintf(stderr, "%s: weird executable (unsupported)\n", fn);
+ goto bad;
+ }
+
+ /*
+ * load up everything we need
+ */
+
+ /* symbol table */
+ if ((symtabp = xmalloc(xewtoh(symtabshdr->sh_size), fn, "symbol table"))
+ == NULL)
+ goto bad;
+ if (xreadatoff(fd, symtabp, xewtoh(symtabshdr->sh_offset),
+ xewtoh(symtabshdr->sh_size), fn) != xewtoh(symtabshdr->sh_size))
+ goto bad;
+
+ /* string table */
+ if ((strtabp = xmalloc(xewtoh(strtabshdr->sh_size), fn, "string table"))
+ == NULL)
+ goto bad;
+ if (xreadatoff(fd, strtabp, xewtoh(strtabshdr->sh_offset),
+ xewtoh(strtabshdr->sh_size), fn) != xewtoh(strtabshdr->sh_size))
+ goto bad;
+
+ /* any rela tables */
+ for (tmpl = relalist; tmpl != NULL; tmpl = tmpl->next) {
+ if ((tmpl->mem = xmalloc(tmpl->size, fn, "rela table"))
+ == NULL)
+ goto bad;
+ if (xreadatoff(fd, tmpl->mem, tmpl->file,
+ tmpl->size, fn) != tmpl->size)
+ goto bad;
+ }
+
+ /* any rel tables */
+ for (tmpl = rellist; tmpl != NULL; tmpl = tmpl->next) {
+ if ((tmpl->mem = xmalloc(tmpl->size, fn, "rel table"))
+ == NULL)
+ goto bad;
+ if (xreadatoff(fd, tmpl->mem, tmpl->file,
+ tmpl->size, fn) != tmpl->size)
+ goto bad;
+ }
+
+ /* Prepare data structures for symbol movement. */
+ nsyms = xewtoh(symtabshdr->sh_size) / xewtoh(symtabshdr->sh_entsize);
+ nlocalsyms = xe32toh(symtabshdr->sh_info);
+ if ((symfwmap = xmalloc(nsyms * sizeof (Elf_Size), fn,
+ "symbol forward mapping table")) == NULL)
+ goto bad;
+ if ((symrvmap = xmalloc(nsyms * sizeof (Elf_Size), fn,
+ "symbol reverse mapping table")) == NULL)
+ goto bad;
+
+ /* init location -> symbol # table */
+ for (ewi = 0; ewi < nsyms; ewi++)
+ symrvmap[ewi] = ewi;
+
+ /* move symbols, making them local */
+ for (ewi = nlocalsyms; ewi < nsyms; ewi++) {
+ Elf_Sym *sp, symswap;
+ Elf_Size mapswap;
+
+ sp = &symtabp[ewi];
+
+ /* if it's on our keep list, don't move it */
+ if (in_keep_list(strtabp + xe32toh(sp->st_name)))
+ continue;
+
+ /* if it's an undefined symbol, keep it */
+ if (xe16toh(sp->st_shndx) == SHN_UNDEF)
+ continue;
+
+ /* adjust the symbol so that it's local */
+ sp->st_info =
+ ELF_ST_INFO(STB_LOCAL, sp->st_info);
+/* (STB_LOCAL << 4) | ELF_SYM_TYPE(sp->st_info); *//* XXX */
+
+ /*
+ * move the symbol to its new location
+ */
+
+ /* note that symbols in those locations have been swapped */
+ mapswap = symrvmap[ewi];
+ symrvmap[ewi] = symrvmap[nlocalsyms];
+ symrvmap[nlocalsyms] = mapswap;
+
+ /* and swap the symbols */
+ symswap = *sp;
+ *sp = symtabp[nlocalsyms];
+ symtabp[nlocalsyms] = symswap;
+
+ nlocalsyms++; /* note new local sym */
+ }
+ symtabshdr->sh_info = htoxe32(nlocalsyms);
+
+ /* set up symbol # -> location mapping table */
+ for (ewi = 0; ewi < nsyms; ewi++)
+ symfwmap[symrvmap[ewi]] = ewi;
+
+ /* any rela tables */
+ for (tmpl = relalist; tmpl != NULL; tmpl = tmpl->next) {
+ Elf_Rela *relap = tmpl->mem;
+
+ for (ewi = 0; ewi < tmpl->size / sizeof(*relap); ewi++) {
+ relap[ewi].r_info = htoxew(ELF_R_INFO(
+ symfwmap[ELF_R_SYM(xewtoh(relap[ewi].r_info))],
+ ELF_R_TYPE(xewtoh(relap[ewi].r_info))
+ ));
+ }
+ }
+
+ /* any rel tables */
+ for (tmpl = rellist; tmpl != NULL; tmpl = tmpl->next) {
+ Elf_Rel *relp = tmpl->mem;
+
+ for (ewi = 0; ewi < tmpl->size / sizeof *relp; ewi++) {
+ relp[ewi].r_info = htoxew(ELF_R_INFO(
+ symfwmap[ELF_R_SYM(xewtoh(relp[ewi].r_info))],
+ ELF_R_TYPE(xewtoh(relp[ewi].r_info))
+ ));
+ }
+ }
+
+ /*
+ * write new tables to the file
+ */
+ if (xwriteatoff(fd, shdrp, xewtoh(ehdr.e_shoff), shdrsize, fn) !=
+ shdrsize)
+ goto bad;
+ if (xwriteatoff(fd, symtabp, xewtoh(symtabshdr->sh_offset),
+ xewtoh(symtabshdr->sh_size), fn) != xewtoh(symtabshdr->sh_size))
+ goto bad;
+ for (tmpl = relalist; tmpl != NULL; tmpl = tmpl->next) {
+ if (xwriteatoff(fd, tmpl->mem, tmpl->file,
+ tmpl->size, fn) != tmpl->size)
+ goto bad;
+ }
+ for (tmpl = rellist; tmpl != NULL; tmpl = tmpl->next) {
+ if (xwriteatoff(fd, tmpl->mem, tmpl->file,
+ tmpl->size, fn) != tmpl->size)
+ goto bad;
+ }
+
+out:
+ if (shdrp != NULL)
+ free(shdrp);
+ if (symtabp != NULL)
+ free(symtabp);
+ if (strtabp != NULL)
+ free(strtabp);
+ if (symfwmap != NULL)
+ free(symfwmap);
+ if (symrvmap != NULL)
+ free(symrvmap);
+ while ((tmpl = relalist) != NULL) {
+ relalist = tmpl->next;
+ if (tmpl->mem != NULL)
+ free(tmpl->mem);
+ free(tmpl);
+ }
+ while ((tmpl = rellist) != NULL) {
+ rellist = tmpl->next;
+ if (tmpl->mem != NULL)
+ free(tmpl->mem);
+ free(tmpl);
+ }
+ return (rv);
+
+bad:
+ rv = 1;
+ goto out;
+}
+
+#endif /* include this size of ELF */
diff --git a/usr.sbin/crunch/crunchide/exec_elf64.c b/usr.sbin/crunch/crunchide/exec_elf64.c
new file mode 100644
index 0000000..962cc07
--- /dev/null
+++ b/usr.sbin/crunch/crunchide/exec_elf64.c
@@ -0,0 +1,40 @@
+/* $NetBSD: exec_elf64.c,v 1.2 1997/08/02 21:30:19 perry Exp $ */
+
+/*
+ * Copyright (c) 1997 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
+ * for the NetBSD Project.
+ * 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>
+#ifndef lint
+__IDSTRING(elf64rcsid, "$NetBSD: exec_elf64.c,v 1.2 1997/08/02 21:30:19 perry Exp $");
+#endif
+
+#define ELFSIZE 64
+
+#include "exec_elf32.c"
diff --git a/usr.sbin/crunch/crunchide/extern.h b/usr.sbin/crunch/crunchide/extern.h
new file mode 100644
index 0000000..4200bc3
--- /dev/null
+++ b/usr.sbin/crunch/crunchide/extern.h
@@ -0,0 +1,51 @@
+/* $NetBSD: extern.h,v 1.5 1998/05/06 13:16:57 mycroft Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 1997 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
+ * for the NetBSD Project.
+ * 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.
+ */
+
+#ifdef NLIST_AOUT
+int check_aout(int, const char *);
+int hide_aout(int, const char *);
+#endif
+#ifdef NLIST_ECOFF
+int check_ecoff(int, const char *);
+int hide_ecoff(int, const char *);
+#endif
+#ifdef NLIST_ELF32
+int check_elf32(int, const char *);
+int hide_elf32(int, const char *);
+#endif
+#ifdef NLIST_ELF64
+int check_elf64(int, const char *);
+int hide_elf64(int, const char *);
+#endif
+
+int in_keep_list(const char *symbol);
diff --git a/usr.sbin/crunch/examples/Makefile b/usr.sbin/crunch/examples/Makefile
new file mode 100644
index 0000000..6dd49ac
--- /dev/null
+++ b/usr.sbin/crunch/examples/Makefile
@@ -0,0 +1,33 @@
+# $FreeBSD$
+
+CRUNCHED= fixit
+
+# below is boiler-plate to make $(CRUNCHED) from $(CRUNCHED).conf
+# I'd use PROG instead of CRUNCHED, but the system makefiles REALLY want
+# to build things in the normal way if you use PROG.
+
+CONF= $(CRUNCHED).conf
+
+OUTMK= $(CRUNCHED).mk
+OUTPUTS= $(OUTMK) $(CRUNCHED).c $(CRUNCHED).cache
+
+NO_MAN=
+CLEANFILES+= $(CRUNCHED) *.o *.lo *.c *.mk *.cache
+CLEANDIRFILES+= $(OUTPUTS)
+
+all: $(CRUNCHED)
+exe: $(CRUNCHED)
+
+$(OUTPUTS): $(CONF)
+ crunchgen ${.CURDIR}/$(CONF)
+
+$(CRUNCHED): $(OUTPUTS) submake
+
+submake:
+ make -f $(OUTMK)
+objs:
+ make -f $(OUTMK) objs
+cleandir:
+ rm -f $(CLEANDIRFILES)
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/crunch/examples/filesystem.conf b/usr.sbin/crunch/examples/filesystem.conf
new file mode 100644
index 0000000..1fb9385
--- /dev/null
+++ b/usr.sbin/crunch/examples/filesystem.conf
@@ -0,0 +1,31 @@
+# $FreeBSD$
+
+srcdirs /usr/src/bin /usr/src/sbin /usr/src/gnu/usr.bin /usr/src/usr.sbin
+srcdirs /usr/src/sbin/i386
+
+# /bin
+progs sh expr ls mkdir rm sync test
+ln test [
+
+# These are needed because of UN*X's idiotic way of indicating that something
+# is a login shell.
+ln sh -
+ln sh -sh
+
+# /sbin
+progs disklabel fdisk init mount newfs reboot umount
+ln reboot halt
+ln reboot fastboot
+ln reboot fasthalt
+
+
+# /usr/bin
+progs cpio gzip
+ln gzip gunzip
+ln gzip gzcat
+ln gzip zcat
+
+# /usr/sbin
+progs bad144
+
+libs -ll -ledit -ltermcap -lutil -lscrypt
diff --git a/usr.sbin/crunch/examples/fixit.conf b/usr.sbin/crunch/examples/fixit.conf
new file mode 100644
index 0000000..80a2346
--- /dev/null
+++ b/usr.sbin/crunch/examples/fixit.conf
@@ -0,0 +1,45 @@
+# fixit.conf - put in anything we think we might want on a fixit floppy
+
+# first, we list the source dirs that our programs reside in. These are
+# searched in order listed to find the dir containing each program.
+
+srcdirs /usr/src/bin /usr/src/sbin /usr/src/usr.bin /usr/src/usr.sbin
+srcdirs /usr/src/gnu/usr.bin /usr/src/usr.bin/vi
+srcdirs /usr/src/sbin/i386
+
+# second, we list all the programs we want to include in our crunched binary.
+# The order doesn't matter. Any program that needs hard links to it gets an
+# `ln' directive.
+
+# /bin stuff
+
+progs cat chmod cp date dd df echo ed expr hostname kill ln ls mkdir
+progs mt mv pwd rcp rm rmdir sh sleep stty sync test
+
+ln test [
+ln sh -sh # init invokes the shell this way
+
+# /sbin stuff
+
+progs badsect chown clri disklabel dump dmesg fdisk fsck ifconfig init
+progs mknod mount newfs ping reboot restore swapon umount
+ln dump rdump
+ln restore rrestore
+
+# /usr/bin stuff
+
+progs ftp rsh sed telnet rlogin common find
+ln common vi
+ln common view
+ln common ex
+
+# gnu stuff
+
+progs cpio gzip
+ln gzip gunzip
+ln gzip gzcat
+
+# finally, we specify the libraries to link in with our binary
+
+libs -lcrypt -ltelnet -lutil -ll
+libs -lcurses -ltermcap -ledit -lkvm
diff --git a/usr.sbin/crunch/examples/kcopy.conf b/usr.sbin/crunch/examples/kcopy.conf
new file mode 100644
index 0000000..3284e86
--- /dev/null
+++ b/usr.sbin/crunch/examples/kcopy.conf
@@ -0,0 +1,21 @@
+# $FreeBSD$
+
+srcdirs /usr/src/bin /usr/src/sbin
+
+# Programs from bin/
+progs sh cp echo test
+ln test [
+
+# These are needed because of UN*X's idiotic way of indicating that something
+# is a login shell.
+ln sh -
+ln sh -sh
+
+#
+# Programs from sbin/
+progs mount mount_cd9660 fsck init reboot umount
+ln reboot halt
+ln reboot fastboot
+ln reboot fasthalt
+
+libs -ll -ledit -ltermcap -lcompat -lutil -lscrypt
diff --git a/usr.sbin/crunch/examples/really-big.conf b/usr.sbin/crunch/examples/really-big.conf
new file mode 100644
index 0000000..45a10de
--- /dev/null
+++ b/usr.sbin/crunch/examples/really-big.conf
@@ -0,0 +1,158 @@
+# really-big.conf - just about everything, just for testing.
+# This ends up having some good examples of the use of specials for
+# those hard-to-reach programs. I stopped when I got tired, but we
+# could probably get even more stuff (like libexec stuff) in here.
+#
+# This produces a 4608000 byte binary. Pretty sick and twisted, eh?
+#
+# $FreeBSD$
+#
+
+# =========================================================================
+
+srcdirs /usr/src/bin
+
+progs cat chmod cp csh date dd df domainname echo ed expr hostname kill
+progs ln ls mkdir mt mv ps pwd rcp rm rmail rmdir sh sleep stty sync test
+
+ln test [
+ln sh -sh
+
+
+# =========================================================================
+
+srcdirs /usr/src/sbin
+
+progs badsect bim clri disklabel dmesg dump dumpfs fdisk fsck halt
+progs ifconfig init mknod modload modunload mount mount_isofs
+progs mount_lofs mount_msdosfs mount_portalfs mountd
+progs newfs nfsd nfsiod ping quotacheck reboot restore route routed savecore
+progs shutdown slattach swapon ttyflags tunefs umount
+# shell scripts: fastboot
+
+ln dump rdump
+ln restore rrestore
+
+
+# =========================================================================
+
+srcdirs /usr/src/usr.bin
+
+progs apropos ar asa at basename biff cal calendar cap_mkdb checknr chpass
+progs cksum cmp col colcrt colrm column comm compress crontab ctags cut
+progs dirname du env error expand false file find finger fmt fold fpr from
+progs fsplit fstat ftp getconf getopt gprof head hexdump id indent ipcrm
+progs ipcs join kdump ktrace last lastcomm leave lex lock logger locate
+progs login logname look m4 machine mail make man mesg mkfifo
+progs mkstr modstat more msgs netstat newsyslog nfsstat nice nm nohup
+progs pagesize passwd paste patch pr printenv printf quota ranlib
+progs renice rev rlogin rpcgen rpcinfo rsh rup ruptime rusers rwall rwho
+progs script sed showmount size soelim split strings strip su tail talk
+progs tcopy tee telnet tftp time tip tn3270 touch tput tr true tset tsort
+progs tty ul uname unexpand unifdef uniq units unvis users uudecode uuencode
+progs vacation vgrind vi vis vmstat w wall wc what whatis whereis who
+progs whois window write xargs xinstall xstr yacc yes ypcat ypmatch ypwhich
+
+# shell scripts: lorder mkdep shar which
+# problems: rdist uses libcompat.a(regex.o), which conflicts with
+# libedit(readline.o) over regerror().
+
+# special requirements
+
+special locate srcdir /usr/src/usr.bin/locate/locate
+special tn3270 srcdir /usr/src/usr.bin/tn3270/tn3270
+
+
+# =========================================================================
+
+srcdirs /usr/src/usr.sbin
+
+progs ac accton amd arp bad144 catman chown chroot config config.new cron
+progs dev_mkdb diskpart edquota flcopy gettable grfinfo hilinfo htable inetd
+progs iostat iteconfig kvm_mkdb mtree named portmap pppd
+progs pstat pwd_mkdb quot quotaon rarpd rbootd repquota rmt rpc.bootparamd
+progs rwhod sa sliplogin slstats spray sysctl syslogd tcpdump
+progs traceroute trpt trsp update vipw vnconfig ypbind yppoll ypset
+
+special amd srcdir /usr/src/usr.sbin/amd/amd
+special amd objs vers.amd.o afs_ops.o am_ops.o clock.o util.o xutil.o efs_ops.o mapc.o info_file.o info_hes.o info_ndbm.o info_passwd.o info_nis.o info_union.o map.o srvr_afs.o srvr_nfs.o mntfs.o misc_rpc.o mount_fs.o mtab.o mtab_bsd.o nfs_ops.o nfs_prot_svc.o nfs_start.o nfs_subr.o opts.o pfs_ops.o rpc_fwd.o sched.o sfs_ops.o amq_svc.o amq_subr.o umount_fs.o host_ops.o nfsx_ops.o ufs_ops.o ifs_ops.o amd.o get_args.o restart.o wire.o
+
+
+srcdirs /usr/src/usr.sbin/lpr # lpr subsystem
+progs lpr lpc lpq lprm pac lptest
+special lpr srcdir /usr/src/usr.sbin/lpr/lpr
+
+srcdirs /usr/src/usr.sbin/sendmail # sendmail subsystem
+progs mailstats makemap praliases sendmail
+special sendmail srcdir /usr/src/usr.sbin/sendmail/src
+ln sendmail newaliases
+ln sendmail mailq
+
+srcdirs /usr/src/usr.sbin/timed # timed & timedc
+progs timed timedc
+special timed srcdir /usr/src/usr.sbin/timed/timed
+
+srcdirs /usr/src/usr.sbin/xntpd # NTP subsystem
+# xntpd uses a gross hack to pass some information in the global
+# variable `progname' between the actual program (ntpdate in this
+# case), and the NTP library. Add `progname' to the keep list.
+progs ntpdate
+special ntpdate srcdir /usr/src/usr.sbin/xntpd/ntpdate
+special ntpdate keep progname
+libs -L/usr/src/usr.sbin/xntpd/lib -lntp
+
+srcdirs /usr/src/usr.sbin/yp # yp subsystem
+progs ypbind ypwhich ypcat ypmatch ypset yppoll
+
+
+# =========================================================================
+
+srcdirs /usr/src/gnu/usr.bin
+
+progs bc cpio diff diff3 gas gawk grep gzip sdiff sort tar
+# shell scripts: send-pr
+
+srcdirs /usr/src/gnu/usr.bin/ld # ldd and ldconfig
+progs ld ldd ldconfig
+
+# rcs stuff loses because there are cross dependencies between librcs.a and
+# the individual programs. The solution would be to specify the objpaths
+# directly for each one, and include the full path to librcs.a each the
+# objpaths.
+
+# srcdirs /usr/src/gnu/usr.bin/rcs # rcs subsystem
+# progs ci co ident merge rcs rcsclean rcsdiff rcsmerge rlog
+# # shell script: rcsfreeze
+# special rcs srcdir /usr/src/gnu/usr.bin/rcs/rcs
+# libs /usr/src/gnu/usr.bin/rcs/lib/obj/librcs.a
+
+# gdb loses too
+# progs gdb
+# special gdb srcdir /usr/src/gnu/usr.bin/gdb/gdb
+# libs /usr/src/gnu/usr.bin/gdb/bfd/obj/libbfd.a
+# libs /usr/src/gnu/usr.bin/gdb/readline/obj/libreadline.a
+# libs /usr/src/gnu/usr.bin/gdb/libiberty/obj/libiberty.a
+
+# groff has the same problem as rcs
+# srcdirs /usr/src/gnu/usr.bin/groff # groff subsystem
+# progs groff troff tbl pic eqn grops grotty grodvi refer lookbib
+# progs indxbib lkbib tfmtodit addftinfo pfbtops psbb
+# shell script: nroff
+# special groff srcdir /usr/src/gnu/usr.bin/groff/groff
+# libs /usr/src/gnu/usr.bin/groff/libgroff/obj/libgroff.a
+# libs /usr/src/gnu/usr.bin/groff/libbib/obj/libbib.a
+# libs /usr/src/gnu/usr.bin/groff/libdriver/obj/libdriver.a
+
+srcdirs /usr/src/gnu/usr.bin/gcc2 # gcc & friends
+progs cc cpp cc1
+
+# cc1 has the same problem as rcs and groff, but since there's only one program
+# I'll go ahead and solve it as an example.
+
+special cc1 objpaths /usr/src/gnu/usr.bin/gcc2/cc1/obj/c-parse.o /usr/src/gnu/usr.bin/gcc2/cc1/obj/c-lang.o /usr/src/gnu/usr.bin/gcc2/cc1/obj/c-lex.o /usr/src/gnu/usr.bin/gcc2/cc1/obj/c-pragma.o /usr/src/gnu/usr.bin/gcc2/cc1/obj/c-decl.o /usr/src/gnu/usr.bin/gcc2/cc1/obj/c-typeck.o /usr/src/gnu/usr.bin/gcc2/cc1/obj/c-convert.o /usr/src/gnu/usr.bin/gcc2/cc1/obj/c-aux-info.o /usr/src/gnu/usr.bin/gcc2/cc1/obj/c-iterate.o /usr/src/gnu/usr.bin/gcc2/common/obj/libcc1.a
+
+ln gzip gunzip
+ln gzip gzcat
+
+libs -ledit -lgnumalloc -lc -lcrypt -ltermcap -lcurses -ltelnet -lutil -lkvm
+libs -ll -ly -lm -lresolv -lrpcsvc -lcompat
diff --git a/usr.sbin/ctm/Makefile b/usr.sbin/ctm/Makefile
new file mode 100644
index 0000000..630aab0
--- /dev/null
+++ b/usr.sbin/ctm/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+SUBDIR= ctm ctm_rmail ctm_smail ctm_dequeue
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/ctm/Makefile.inc b/usr.sbin/ctm/Makefile.inc
new file mode 100644
index 0000000..c6c2c5a
--- /dev/null
+++ b/usr.sbin/ctm/Makefile.inc
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+.if exists(${.CURDIR}/../../Makefile.inc)
+.include "${.CURDIR}/../../Makefile.inc"
+.endif
diff --git a/usr.sbin/ctm/README b/usr.sbin/ctm/README
new file mode 100644
index 0000000..94c779a
--- /dev/null
+++ b/usr.sbin/ctm/README
@@ -0,0 +1,85 @@
+# ----------------------------------------------------------------------------
+# "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$
+#
+
+What will I not find in this file ?
+-----------------------------------
+Instructions on how to obtain FreeBSD via CTM.
+Contact <CTM@FreeBSD.org> for that.
+
+What is CTM ?
+-------------
+CTM was originally "CVS Through eMail", but has since changed scope to be
+much more general.
+CTM is now meant to be the definitive way to make and apply a delta between
+two versions of a directory tree.
+There are two parts to this, making the delta and applying it. These are two
+entirely different things. CTM concentrates the computation-burden on the
+generation of the deltas, as a delta very often is applied more times than
+it is made. Second CTM tries to make the minimal size delta.
+
+Why not use diff/patch ?
+------------------------
+Good question. Primarily because diff and patch doesn't do their job very
+well. They don't deal with binary files (in this case files with '\0' or
+'\0377' characters in them or files that doesn't end in '\n') which isn't
+a big surprise: they were made to deal with text-files only. As a second
+gripe, with patch you send the entire file to delete it. Not particular
+efficient.
+
+So what does CTM do exactly ?
+-----------------------------
+CTM will produce a file, (a delta) containing the instructions and data needed
+to take another copy of the tree from the old to the new status. CTM means to
+do this in the exact sense, and therefore the delta contains MD5 checksums to
+verify that the tree it is applied to is indeed in the state CTM expects.
+
+This means that if you have modified the tree locally, CTM might not be able
+to upgrade your copy.
+
+How do I make a CTM-delta ?
+---------------------------
+
+Read the source, and be prepared to have 2 copies of the tree; One is
+the reference ("From") tree, and the other is the delta ("To") tree.
+The mkCTM script will create the CTM diff of the differences between
+the reference tree and the delta tree. A lot of scratch space is
+required, and your machine will work hard.
+
+How do I apply a CTM-delta ?
+----------------------------
+You pass it to the 'ctm' command. You can pass a CTM-delta on stdin, or
+you can give the filename as an argument. If you do the latter, you make
+life a lot easier for your self, since the program can accept gzip'ed files
+and since it will not have to make a temporary copy of your file. You can
+specify multiple deltas at one time, they will be processed one at a time.
+
+The ctm command runs in a number of passes. It will process the entire
+input file in each pass, before commencing with the next pass.
+
+Pass 1 will validate that the input file is OK. The syntax, the data and
+the global MD5 checksum will be checked. If any of these fail, ctm will
+never be able to do anything with the file, so it will simply reject it.
+
+Pass 2 will validate that the directory tree is in the state expected by
+the CTM-delta. This is done by looking for files and directories which
+should/should not exists and by checking the MD5 checksums of files.
+
+Pass 3 will actually apply the delta.
+
+Should I delete the delta when I have applied it ?
+--------------------------------------------------
+No. You might want to selectively reconstruct a file latter on.
+
+Why is CTM not being maintained?
+--------------------------------
+Because CVSUP has improved on the concept quite a bit, and is now
+the method of choice.
+
+Poul-Henning
diff --git a/usr.sbin/ctm/ctm/Makefile b/usr.sbin/ctm/ctm/Makefile
new file mode 100644
index 0000000..c0cf79a
--- /dev/null
+++ b/usr.sbin/ctm/ctm/Makefile
@@ -0,0 +1,23 @@
+# ----------------------------------------------------------------------------
+# "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$
+
+PROG= ctm
+MAN= ctm.1 ctm.5
+SRCS= ctm.c ctm_input.c ctm_pass1.c ctm_pass2.c ctm_pass3.c \
+ ctm_passb.c ctm_syntax.c ctm_ed.c
+
+NOTYET= ctm_ed.c
+
+LDADD= -lmd
+DPADD= ${LIBMD}
+
+.if exists(${.CURDIR}/../../Makefile.inc)
+.include "${.CURDIR}/../../Makefile.inc"
+.endif
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ctm/ctm/ctm.1 b/usr.sbin/ctm/ctm/ctm.1
new file mode 100644
index 0000000..732b0b9
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm.1
@@ -0,0 +1,325 @@
+.\" ----------------------------------------------------------------------------
+.\" "THE BEER-WARE LICENSE" (Revision 42):
+.\" <joerg@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. Joerg Wunsch
+.\" ----------------------------------------------------------------------------
+.\"
+.\" This manual page is partially obtained from Poul-Hennings CTM README
+.\" file.
+.\"
+.\" CTM and ctm(1) by <phk@FreeBSD.org>
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 25, 1995
+.Os
+.Dt CTM 1
+.Sh NAME
+.Nm ctm
+.Nd source code mirror program
+.Sh SYNOPSIS
+.Nm
+.Op Fl cFklquv
+.Op Fl b Ar basedir
+.Op Fl B Ar backup-file
+.Op Fl e Ar include-regex
+.Op Fl t Ar tar-command
+.Op Fl T Ar tmpdir
+.Op Fl V Ar level
+.Op Fl x Ar exclude-regex
+.Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility was originally
+.Dq Cvs Through eMail ,
+but now instead it seems more fitting to call it
+.Dq Current Through eMail .
+.Pp
+The
+.Nm
+utility is now meant to be the definitive way to make and apply a delta between
+two versions of a directory tree.
+.Pp
+There are two parts to this, making the delta and applying it.
+These are two
+entirely different things.
+.Ss Usage
+To apply a CTM delta, you pass it to the
+.Nm
+command.
+You can pass a CTM delta on stdin, or you can give the
+filename as an argument.
+If you do the latter, you make life a lot
+easier for your self, since the program can accept gzip'ed files and
+since it will not have to make a temporary copy of your file.
+You can
+specify multiple deltas at one time, they will be processed one at a
+time.
+Deltas that are already applied will be ignored.
+.Pp
+The
+.Nm
+command runs in a number of passes.
+It will process the entire
+input file in each pass, before commencing with the next pass.
+.Pp
+Before working on a file
+.Ar name
+.Nm
+first checks for the existence of the file
+.Ar name.ctm .
+If this file exists,
+.Nm
+works on it instead.
+.Pp
+Pass 1 will verify that the input file is OK.
+The syntax, the data
+and the global MD5 checksum will be checked.
+If any of these fail,
+.Nm
+will simply reject the input file.
+.Pp
+Pass 2 will validate that the directory tree is in the state expected by
+the CTM delta.
+This is done by looking for files and directories which
+should/should not exist and by checking the MD5 checksums of files.
+.Pp
+If a
+.Ar backup-file
+had been specified using the
+.Fl B
+option, all files that would be modified by this
+.Nm
+invocation are backed up
+to this file using the archiver command specified by the
+.Fl t
+option.
+The default archiver command is
+.Nm "tar -rf %s -T -" .
+.Pp
+Pass 3 will actually apply the delta.
+.Pp
+The list of files that would be modified by
+.Nm
+is subject to filtering regular expressions specified
+using the
+.Fl e
+and
+.Fl x
+options.
+The
+.Fl e
+and
+.Fl x
+options are applied in order of appearance on the command line.
+The last
+filter that matched a given file name determines whether the file would be
+operated on or left alone by
+.Nm .
+.Pp
+The
+.Nm
+utility
+will extract the file hierarchy below its working directory.
+Absolute
+filenames or filenames containing references through
+.Sq Pa .\&
+and
+.Sq Pa ..\&
+are explicitly prohibited as a security measure.
+.Ss Options
+.Bl -tag -width indent
+.It Fl b Ar basedir
+Prepend the path
+.Ar basedir
+to every filename.
+.It Fl B Ar backup-file
+Backup all files that would be modified by this CTM run to
+.Ar backup-file .
+If any filters are specified using the
+.Fl e
+and
+.Fl x
+options, then the final set of files backed up are those that would be
+modified by CTM after the filters are applied.
+.It Fl c
+Check it out, do not do anything.
+.It Fl e Ar regular_expression
+Match each name in the CTM file against
+.Ar regular_expression ,
+and if it matches process the file, otherwise leave it alone.
+There may be
+any number of these options.
+Use of this option disables the
+.Pa .ctm_status
+sequence number checks.
+For example, the expression
+.Ic ^usr.sbin/ctm
+for example, will select the
+.Pa usr.sbin/ctm
+source directory and all pathnames under it.
+.Pp
+Pathnames can be disabled from being considered by CTM using the
+.Fl x
+option.
+.It Fl F
+Force.
+.It Fl k
+Keep files and directories and do not remove them even if the CTM file
+specifies they are to be removed.
+If the
+.Fl B
+option is specified, these files and directories will not be backed up.
+.It Fl l
+List files that would be modified by this invocation of CTM and the
+actions that would be performed on them.
+Use of the
+.Fl l
+option disables the
+.Pa .ctm_status
+checks and integrity checks on the source tree being operated on.
+The
+.Fl l
+option can be combined with the
+.Fl e
+and
+.Fl x
+options to determine which files would be modified by the given set of
+command line options.
+.It Fl q
+Tell us less.
+.It Fl t Ar tar-command
+Use
+.Ar tar-command
+instead of the default archiver
+.Nm tar .
+This option takes effect only if a backup file had been specified using the
+.Fl B
+option.
+A %s in the tar command will be replaced by the name of the backup
+file.
+.It Fl T Ar tmpdir
+Put temporary files under
+.Ar tmpdir .
+.It Fl u
+Set modification time of created and modified files to the CTM delta
+creation time.
+.It Fl v
+Tell us more.
+.It Fl V Ar level
+Tell us more.
+.Ar Level
+is the level of verbosity.
+.It Fl x Ar regular_expression
+Match each name in the CTM file against
+.Ar regular_expression
+and if it matches, leave the file alone.
+There may be any number of these
+options.
+Use of this option disables the
+.Pa .ctm_status
+sequence number checks.
+.Pp
+Pathnames can be selected for CTM's consideration using the
+.Fl e
+option.
+.El
+.Sh SECURITY
+On its own, CTM is an insecure protocol
+- there is no authentication performed that the
+changes applied to the source code were sent by a
+trusted party, and so care should be taken if the
+CTM deltas are obtained via an unauthenticated
+medium such as regular email.
+It is a relatively simple matter for an attacker
+to forge a CTM delta to replace or precede the
+legitimate one and insert malicious code into your
+source tree.
+If the legitimate delta is somehow prevented from
+arriving, this will go unnoticed until a later
+delta attempts to touch the same file, at which
+point the MD5 checksum will fail.
+.Pp
+To remedy this insecurity, CTM pieces generated by
+FreeBSD.org are cryptographically signed in a
+format compatible with the GNU Privacy Guard
+utility, available in /usr/ports/security/gpg, and
+the Pretty Good Privacy v5 utility,
+/usr/ports/security/pgp5.
+The relevant public key can be obtained by
+fingering ctm@FreeBSD.org.
+.Pp
+CTM deltas which are thus signed cannot be
+undetectably altered by an attacker.
+Therefore it is recommended that you make use of
+GPG or PGP5 to verify the signatures if you
+receive your CTM deltas via email.
+.Sh ENVIRONMENT
+.Ev TMPDIR ,
+if set to a pathname, will cause ctm to use that pathname
+as the location of temporary file.
+See
+.Xr tempnam 3 ,
+for more details on this.
+The same effect may be achieved with the
+.Fl T
+flag.
+.Sh FILES
+.Pa .ctm_status
+contains the sequence number of the last CTM delta applied.
+Changing
+or removing this file will greatly confuse
+.Nm .
+.Pp
+Using the
+.Fl e
+and
+.Fl x
+options can update a partial subset of the source tree and causes sources
+to be in an inconsistent state.
+It is assumed that you know what you are
+doing when you use these options.
+.Sh EXAMPLES
+.Bd -literal
+cd ~cvs
+/usr/sbin/ctm ~ctm/cvs-*
+.Ed
+.Pp
+To extract and patch all sources under `lib'
+.Bd -literal
+cd ~/lib-srcs
+/usr/sbin/ctm -e '^lib' ~ctm/src-cur*
+.Ed
+.Sh DIAGNOSTICS
+Numerous messages, hopefully self-explanatory.
+The
+.Dq noise level
+can be adjusted with the
+.Fl q ,
+.Fl v
+and
+.Fl V
+options.
+.Sh SEE ALSO
+.Xr ctm_rmail 1 ,
+.Xr ctm 5
+.Sh HISTORY
+Initial trials were run during the work on
+.Fx 1.1.5 ,
+and many bugs and
+methods were hashed out.
+.Pp
+The
+.Nm
+command appeared in
+.Fx 2.1 .
+.Sh AUTHORS
+The CTM system has been designed and implemented by
+.An Poul-Henning Kamp
+.Aq phk@FreeBSD.org .
+.Pp
+.An Joerg Wunsch
+.Aq joerg@FreeBSD.org
+wrote this man-page.
diff --git a/usr.sbin/ctm/ctm/ctm.5 b/usr.sbin/ctm/ctm/ctm.5
new file mode 100644
index 0000000..678837e
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm.5
@@ -0,0 +1,183 @@
+.\" ----------------------------------------------------------------------------
+.\" "THE BEER-WARE LICENSE" (Revision 42):
+.\" <joerg@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. Joerg Wunsch
+.\" ----------------------------------------------------------------------------
+.\"
+.\" This manual page is partially obtained from Poul-Hennings CTM README
+.\" file.
+.\"
+.\" CTM and ctm(1) by <phk@FreeBSD.org>
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 25, 1995
+.Os
+.Dt CTM 5
+.Sh NAME
+.Nm ctm
+.Nd source code mirror system
+.Sh DESCRIPTION
+The
+.Nm
+transfers data in a specific file format, called a CTM delta.
+.Pp
+CTM deltas consist of control lines and data chunks.
+Each control
+line starts with the letters
+.Dq CTM ,
+followed by a CTM statement and control data, and ends with a '\en'
+character.
+.Pp
+Data chunks always belong to the preceding control line, and the
+last field on that control line is the number of bytes in the data
+chunk.
+A trailing newline '\en' character follows each data chunk, this
+newline is not part of the chunk and is not included in the count.
+.Pp
+The CTM statements are as follows.
+.Bl -tag -width indent
+.It _BEGIN Ar version name number timestamp prefix
+This is the overall begin of a CTM delta file.
+The
+.Ar version
+field must match the program version
+(currently 2.0).
+.Ar Name
+is the name and
+.Ar number
+the sequence number of the CTM service, it is matched against the file
+.Pa .ctm_status
+to see if the delta has already been applied.
+.Ar Timestamp
+contains the year, month, day, hour, minute, and second of the
+time of delta creation for reference
+(followed by the letter
+.Sq Z
+meaning this is a UTC timestamp).
+The
+.Ar prefix
+field is currently not implemented.
+.It _END Ar md5
+This statement ends the CTM delta, the global
+.Ar md5
+checksum is matched against the MD5 checksum of the entire delta, up to
+and including the space (0x20) character following ``_END''.
+.It \&FM Ar name uid gid mode md5 count
+Make the file
+.Ar name ,
+the original file had the uid
+.Ar uid
+(numerical, decimal),
+the gid
+.Ar gid
+(numerical, decimal),
+mode
+.Ar mode
+(numerical, octal),
+and the MD5 checksum
+.Ar md5 .
+.Pp
+The following
+.Ar count
+bytes data are the contents of the new file.
+.It \&FS Ar name uid gid mode md5before md5after count
+Substitute the contents of file
+.Ar name ,
+the original file had the new uid
+.Ar uid
+(numerical, decimal),
+the new gid
+.Ar gid
+(numerical, decimal),
+new mode
+.Ar mode
+(numerical, octal),
+the old MD5 checksum
+.Ar md5before ,
+and the new MD5 checksum
+.Ar md5after .
+.Pp
+The following
+.Ar count
+bytes data are the contents of the new file.
+.Pp
+File substitution is used if the commands to edit a file would exceed
+the total file length, so substituting it is more efficient.
+.It \&FN Ar name uid gid mode md5before md5after count
+Edit the file
+.Ar name .
+The arguments are as above, but the data sections contains an
+.Xr diff 1
+-n script which should be applied to the file in question.
+.It \&FR Ar name md5
+Remove the file
+.Ar name ,
+which must match the MD5 checksum
+.Ar md5 .
+.It \&AS Ar name uid gid mode
+The original file
+.Ar name
+changed its owner to
+.Ar uid ,
+its group to
+.Ar gid ,
+and/or its mode to
+.Ar mode .
+.It \&DM Ar name uid gid mode
+The directory
+.Ar name
+is to be created, it had originally the owner
+.Ar uid ,
+group
+.Ar gid ,
+and mode
+.Ar mode .
+.It \&DR Ar name
+The directory
+.Ar name
+is to be removed.
+.El
+.Sh EXAMPLES
+In the following example, long lines have been folded to make them
+printable
+(marked by backslashes).
+.Bd -literal
+CTM_BEGIN 2.0 cvs-cur 485 19950324214652Z .
+CTMFR src/sys/gnu/i386/isa/scd.c,v 5225f13aa3c7e458f9dd0d4bb637b18d
+CTMFR src/sys/gnu/i386/isa/scdreg.h,v e5af42b8a06f2c8030b93a7d71afb223
+CTMDM src/sys/gnu/i386/isa/Attic 0 552 775
+CTMFS .ctm_status 545 552 664 d9ccd2a84a9dbb8db56ba85663adebf0 \\
+e2a10c6f66428981782a0a18a789ee2e 12
+cvs-cur 485
+
+CTMFN CVSROOT/commitlogs/gnu 545 552 664 \\
+5d7bc3549140d860bd9641b5782c002d 7fb04ed84b48160c9b8eea84b4c0b6e3 394
+a6936 21
+ache 95/03/24 09:59:50
+
+ Modified: gnu/lib/libdialog kernel.c prgbox.c
+ Log:
+[...]
+CTM_END 74ddd298d76215ae45a077a4b6a74e9c
+.Ed
+.Sh SEE ALSO
+.Xr ctm 1 ,
+.Xr ctm_rmail 1 ,
+.Xr ed 1
+.Sh HISTORY
+Initial trials ran during the
+.Fx 1.1.5 ,
+and many bugs and
+methods were hashed out.
+The CTM system has been made publicly available in
+.Fx 2.1 .
+.Sh AUTHORS
+The CTM system has been designed and implemented by
+.An Poul-Henning Kamp
+.Aq phk@FreeBSD.org .
+.Pp
+.An Joerg Wunsch
+.Aq joerg@FreeBSD.org
+wrote this man-page.
diff --git a/usr.sbin/ctm/ctm/ctm.c b/usr.sbin/ctm/ctm/ctm.c
new file mode 100644
index 0000000..0ea4895
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm.c
@@ -0,0 +1,331 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "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$
+ *
+ * This is the client program of 'CTM'. It will apply a CTM-patch to a
+ * collection of files.
+ *
+ * Options we'd like to see:
+ *
+ * -a Attempt best effort.
+ * -d <int> Debug TBD.
+ * -m <mail-addr> Email me instead.
+ * -r <name> Reconstruct file.
+ * -R <file> Read list of files to reconstruct.
+ *
+ * Options we have:
+ * -b <dir> Base-dir
+ * -B <file> Backup to tar-file.
+ * -t Tar command (default as in TARCMD).
+ * -c Check it out, don't do anything.
+ * -F Force
+ * -q Tell us less.
+ * -T <tmpdir>. Temporary files.
+ * -u Set all file modification times to the timestamp
+ * -v Tell us more.
+ * -V <level> Tell us more level = number of -v
+ * -k Keep files and directories that would have been removed.
+ * -l List actions.
+ *
+ * Options we don't actually use:
+ * -p Less paranoid.
+ * -P Paranoid.
+ */
+
+#define EXTERN /* */
+#include <paths.h>
+#include "ctm.h"
+
+#define CTM_STATUS ".ctm_status"
+
+extern int Proc(char *, unsigned applied);
+
+int
+main(int argc, char **argv)
+{
+ int stat=0, err=0;
+ int c;
+ unsigned applied = 0;
+ FILE *statfile;
+ struct CTM_Filter *nfilter = NULL; /* new filter */
+ u_char * basedir;
+
+ basedir = NULL;
+ Verbose = 1;
+ Paranoid = 1;
+ SetTime = 0;
+ KeepIt = 0;
+ ListIt = 0;
+ BackupFile = NULL;
+ TarCmd = TARCMD;
+ LastFilter = FilterList = NULL;
+ TmpDir = getenv("TMPDIR");
+ if (TmpDir == NULL)
+ TmpDir = strdup(_PATH_TMP);
+ setbuf(stderr,0);
+ setbuf(stdout,0);
+
+ while((c=getopt(argc,argv,"ab:B:cd:e:Fklm:pPqr:R:t:T:uV:vx:")) != -1) {
+ switch (c) {
+ case 'b': basedir = optarg; break; /* Base Directory */
+ case 'B': BackupFile = optarg; break;
+ case 'c': CheckIt++; break; /* Only check it */
+ case 'F': Force = 1; break;
+ case 'k': KeepIt++; break; /* Don't do removes */
+ case 'l': ListIt++; break; /* Only list actions and files */
+ case 'p': Paranoid--; break; /* Less Paranoid */
+ case 'P': Paranoid++; break; /* More Paranoid */
+ case 'q': Verbose--; break; /* Quiet */
+ case 't': TarCmd = optarg; break; /* archiver command */
+ case 'T': TmpDir = optarg; break; /* set temporary directory */
+ case 'u': SetTime++; break; /* Set timestamp on files */
+ case 'v': Verbose++; break; /* Verbose */
+ case 'V': sscanf(optarg,"%d", &c); /* Verbose */
+ Verbose += c;
+ break;
+ case 'e': /* filter expressions */
+ case 'x':
+ if (NULL == (nfilter = Malloc(sizeof(struct CTM_Filter)))) {
+ warnx("out of memory for expressions: \"%s\"", optarg);
+ stat++;
+ break;
+ }
+
+ (void) memset(nfilter, 0, sizeof(struct CTM_Filter));
+
+ if (0 != (err =
+ regcomp(&nfilter->CompiledRegex, optarg, REG_NOSUB))) {
+
+ char errmsg[128];
+
+ regerror(err, &nfilter->CompiledRegex, errmsg,
+ sizeof(errmsg));
+ warnx("regular expression: \"%s\"", errmsg);
+ stat++;
+ break;
+ }
+
+ /* note whether the filter enables or disables on match */
+ nfilter->Action =
+ (('e' == c) ? CTM_FILTER_ENABLE : CTM_FILTER_DISABLE);
+
+ /* link in the expression into the list */
+ nfilter->Next = NULL;
+ if (NULL == FilterList) {
+ LastFilter = FilterList = nfilter; /* init head and tail */
+ } else { /* place at tail */
+ LastFilter->Next = nfilter;
+ LastFilter = nfilter;
+ }
+ break;
+ case ':':
+ warnx("option '%c' requires an argument",optopt);
+ stat++;
+ break;
+ case '?':
+ warnx("option '%c' not supported",optopt);
+ stat++;
+ break;
+ default:
+ warnx("option '%c' not yet implemented",optopt);
+ break;
+ }
+ }
+
+ if(stat) {
+ warnx("%d errors during option processing",stat);
+ return Exit_Pilot;
+ }
+ stat = Exit_Done;
+ argc -= optind;
+ argv += optind;
+
+ if (basedir == NULL) {
+ Buffer = (u_char *)Malloc(BUFSIZ + strlen(SUBSUFF) +1);
+ CatPtr = Buffer;
+ *Buffer = '\0';
+ } else {
+ Buffer = (u_char *)Malloc(strlen(basedir)+ BUFSIZ + strlen(SUBSUFF) +1);
+ strcpy(Buffer, basedir);
+ CatPtr = Buffer + strlen(basedir);
+ if (CatPtr[-1] != '/') {
+ strcat(Buffer, "/");
+ CatPtr++;
+ }
+ }
+ strcat(Buffer, CTM_STATUS);
+
+ if(ListIt)
+ applied = 0;
+ else
+ if((statfile = fopen(Buffer, "r")) == NULL) {
+ if (Verbose > 0)
+ warnx("warning: %s not found", Buffer);
+ } else {
+ fscanf(statfile, "%*s %u", &applied);
+ fclose(statfile);
+ }
+
+ if(!argc)
+ stat |= Proc("-", applied);
+
+ while(argc-- && stat == Exit_Done) {
+ stat |= Proc(*argv++, applied);
+ stat &= ~(Exit_Version | Exit_NoMatch);
+ }
+
+ if(stat == Exit_Done)
+ stat = Exit_OK;
+
+ if(Verbose > 0)
+ warnx("exit(%d)",stat);
+
+ if (FilterList)
+ for (nfilter = FilterList; nfilter; ) {
+ struct CTM_Filter *tmp = nfilter->Next;
+ Free(nfilter);
+ nfilter = tmp;
+ }
+ return stat;
+}
+
+int
+Proc(char *filename, unsigned applied)
+{
+ FILE *f;
+ int i;
+ char *p = strrchr(filename,'.');
+
+ if(!strcmp(filename,"-")) {
+ p = 0;
+ f = stdin;
+ } else if(p && (!strcmp(p,".gz") || !strcmp(p,".Z"))) {
+ p = alloca(20 + strlen(filename));
+ strcpy(p,"gunzip < ");
+ strcat(p,filename);
+ f = popen(p,"r");
+ if(!f) { warn("%s", p); return Exit_Garbage; }
+ } else {
+ p = 0;
+ f = fopen(filename,"r");
+ }
+ if(!f) {
+ warn("%s", filename);
+ return Exit_Garbage;
+ }
+
+ if(Verbose > 1)
+ fprintf(stderr,"Working on <%s>\n",filename);
+
+ Delete(FileName);
+ FileName = String(filename);
+
+ /* If we cannot seek, we're doomed, so copy to a tmp-file in that case */
+ if(!p && -1 == fseek(f,0,SEEK_END)) {
+ char *fn;
+ FILE *f2;
+ int fd;
+
+ if (asprintf(&fn, "%s/CTMclient.XXXXXXXXXX", TmpDir) == -1) {
+ fprintf(stderr, "Cannot allocate memory\n");
+ fclose(f);
+ return Exit_Broke;
+ }
+ if ((fd = mkstemp(fn)) == -1 || (f2 = fdopen(fd, "w+")) == NULL) {
+ warn("%s", fn);
+ free(fn);
+ if (fd != -1)
+ close(fd);
+ fclose(f);
+ return Exit_Broke;
+ }
+ unlink(fn);
+ if (Verbose > 0)
+ fprintf(stderr,"Writing tmp-file \"%s\"\n",fn);
+ free(fn);
+ while(EOF != (i=getc(f)))
+ if(EOF == putc(i,f2)) {
+ fclose(f2);
+ return Exit_Broke;
+ }
+ fclose(f);
+ f = f2;
+ }
+
+ if(!p)
+ rewind(f);
+
+ if((i=Pass1(f, applied)))
+ goto exit_and_close;
+
+ if(ListIt) {
+ i = Exit_Done;
+ goto exit_and_close;
+ }
+
+ if(!p) {
+ rewind(f);
+ } else {
+ pclose(f);
+ f = popen(p,"r");
+ if(!f) { warn("%s", p); return Exit_Broke; }
+ }
+
+ i=Pass2(f);
+
+ if(!p) {
+ rewind(f);
+ } else {
+ pclose(f);
+ f = popen(p,"r");
+ if(!f) { warn("%s", p); return Exit_Broke; }
+ }
+
+ if(i) {
+ if((!Force) || (i & ~Exit_Forcible))
+ goto exit_and_close;
+ }
+
+ if(CheckIt) {
+ if (Verbose > 0)
+ fprintf(stderr,"All checks out ok.\n");
+ i = Exit_Done;
+ goto exit_and_close;
+ }
+
+ /* backup files if requested */
+ if(BackupFile) {
+
+ i = PassB(f);
+
+ if(!p) {
+ rewind(f);
+ } else {
+ pclose(f);
+ f = popen(p,"r");
+ if(!f) { warn("%s", p); return Exit_Broke; }
+ }
+ }
+
+ i=Pass3(f);
+
+exit_and_close:
+ if(!p)
+ fclose(f);
+ else
+ pclose(f);
+
+ if(i)
+ return i;
+
+ if (Verbose > 0)
+ fprintf(stderr,"All done ok\n");
+
+ return Exit_Done;
+}
diff --git a/usr.sbin/ctm/ctm/ctm.h b/usr.sbin/ctm/ctm/ctm.h
new file mode 100644
index 0000000..b733539
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm.h
@@ -0,0 +1,163 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "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 <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <md5.h>
+#include <regex.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+
+#define VERSION "2.0"
+
+#define SUBSUFF ".ctm"
+#define TMPSUFF ".ctmtmp"
+#define TARCMD "tar -rf %s -T -"
+
+/* The fields... */
+#define CTM_F_MASK 0xff
+#define CTM_F_Name 0x01
+#define CTM_F_Uid 0x02
+#define CTM_F_Gid 0x03
+#define CTM_F_Mode 0x04
+#define CTM_F_MD5 0x05
+#define CTM_F_Count 0x06
+#define CTM_F_Bytes 0x07
+
+/* The qualifiers... */
+#define CTM_Q_MASK 0xff00
+#define CTM_Q_Name_File 0x0100
+#define CTM_Q_Name_Dir 0x0200
+#define CTM_Q_Name_New 0x0400
+#define CTM_Q_Name_Subst 0x0800
+#define CTM_Q_MD5_After 0x0100
+#define CTM_Q_MD5_Before 0x0200
+#define CTM_Q_MD5_Chunk 0x0400
+#define CTM_Q_MD5_Force 0x0800
+
+struct CTM_Syntax {
+ char *Key; /* CTM key for operation */
+ int *List; /* List of operations */
+ };
+
+extern struct CTM_Syntax Syntax[];
+
+struct CTM_Filter {
+ struct CTM_Filter *Next; /* next filter in the list */
+ int Action; /* enable or disable */
+ regex_t CompiledRegex; /* compiled regex */
+};
+
+#define CTM_FILTER_DISABLE 0
+#define CTM_FILTER_ENABLE 1
+
+#define Malloc malloc
+#define Free free
+#define Delete(foo) if (!foo) ; else {Free(foo); foo = 0; }
+#define String(foo) strdup(foo)
+
+#ifndef EXTERN
+# define EXTERN extern
+#endif
+EXTERN u_char *Version;
+EXTERN u_char *Name;
+EXTERN u_char *Nbr;
+EXTERN u_char *TimeStamp;
+EXTERN u_char *Prefix;
+EXTERN u_char *FileName;
+EXTERN u_char *TmpDir;
+EXTERN u_char *CatPtr;
+EXTERN u_char *Buffer;
+EXTERN u_char *BackupFile;
+EXTERN u_char *TarCmd;
+
+/*
+ * Paranoid -- Just in case they should be after us...
+ * 0 not at all.
+ * 1 normal.
+ * 2 somewhat.
+ * 3 you bet!.
+ *
+ * Verbose -- What to tell mom...
+ * 0 Nothing which wouldn't surprise.
+ * 1 Normal.
+ * 2 Show progress '.'.
+ * 3 Show progress names, and actions.
+ * 4 even more...
+ * and so on
+ *
+ * ExitCode -- our Epitaph
+ * 0 Perfect, all input digested, no problems
+ * 1 Bad input, no point in retrying.
+ * 2 Pilot error, commandline problem &c
+ * 4 Out of resources.
+ * 8 Destination-tree not correct.
+ * 16 Destination-tree not correct, can force.
+ * 32 Internal problems.
+ *
+ */
+
+EXTERN int Paranoid;
+EXTERN int Verbose;
+EXTERN int Exit;
+EXTERN int Force;
+EXTERN int CheckIt;
+EXTERN int KeepIt;
+EXTERN int ListIt;
+EXTERN int SetTime;
+EXTERN struct timeval Times[2];
+EXTERN struct CTM_Filter *FilterList;
+EXTERN struct CTM_Filter *LastFilter;
+
+#define Exit_OK 0
+#define Exit_Garbage 1
+#define Exit_Pilot 2
+#define Exit_Broke 4
+#define Exit_NotOK 8
+#define Exit_Forcible 16
+#define Exit_Mess 32
+#define Exit_Done 64
+#define Exit_Version 128
+#define Exit_NoMatch 256
+
+void Fatal_(int ln, char *fn, char *kind);
+#define Fatal(foo) Fatal_(__LINE__,__FILE__,foo)
+#define Assert() Fatal_(__LINE__,__FILE__,"Assert failed.")
+#define WRONG {Assert(); return Exit_Mess;}
+
+u_char * Ffield(FILE *fd, MD5_CTX *ctx,u_char term);
+u_char * Fname(FILE *fd, MD5_CTX *ctx,u_char term,int qual, int verbose);
+
+int Fbytecnt(FILE *fd, MD5_CTX *ctx, u_char term);
+
+u_char * Fdata(FILE *fd, int u_chars, MD5_CTX *ctx);
+
+#define GETFIELD(p,q) if(!((p)=Ffield(fd,&ctx,(q)))) return BADREAD
+#define GETFIELDCOPY(p,q) if(!((p)=Ffield(fd,&ctx,(q)))) return BADREAD; else p=String(p)
+#define GETBYTECNT(p,q) if(0 >((p)= Fbytecnt(fd,&ctx,(q)))) return BADREAD
+#define GETDATA(p,q) if(!((p) = Fdata(fd,(q),&ctx))) return BADREAD
+#define GETNAMECOPY(p,q,r,v) if(!((p)=Fname(fd,&ctx,(q),(r),(v)))) return BADREAD; else p=String(p)
+
+int Pass1(FILE *fd, unsigned applied);
+int Pass2(FILE *fd);
+int PassB(FILE *fd);
+int Pass3(FILE *fd);
+
+int ctm_edit(u_char *script, int length, char *filein, char *fileout);
diff --git a/usr.sbin/ctm/ctm/ctm_ed.c b/usr.sbin/ctm/ctm/ctm_ed.c
new file mode 100644
index 0000000..e3ec464
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm_ed.c
@@ -0,0 +1,114 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "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 "ctm.h"
+
+int
+ctm_edit(u_char *script, int length, char *filein, char *fileout)
+{
+ u_char *ep, cmd;
+ int ln, ln2, iln, ret=0, c;
+ FILE *fi=0,*fo=0;
+
+ fi = fopen(filein,"r");
+ if(!fi) {
+ warn("%s", filein);
+ return 8;
+ }
+
+ fo = fopen(fileout,"w");
+ if(!fo) {
+ warn("%s", fileout);
+ fclose(fi);
+ return 4;
+ }
+ iln = 1;
+ for(ep=script;ep < script+length;) {
+ cmd = *ep++;
+ if(cmd != 'a' && cmd != 'd') { ret = 1; goto bye; }
+ ln = 0;
+ while(isdigit(*ep)) {
+ ln *= 10;
+ ln += (*ep++ - '0');
+ }
+ if(*ep++ != ' ') { ret = 1; goto bye; }
+ ln2 = 0;
+ while(isdigit(*ep)) {
+ ln2 *= 10;
+ ln2 += (*ep++ - '0');
+ }
+ if(*ep++ != '\n') { ret = 1; goto bye; }
+
+
+ if(cmd == 'd') {
+ while(iln < ln) {
+ c = getc(fi);
+ if(c == EOF) { ret = 1; goto bye; }
+ putc(c,fo);
+ if(c == '\n')
+ iln++;
+ }
+ while(ln2) {
+ c = getc(fi);
+ if(c == EOF) { ret = 1; goto bye; }
+ if(c != '\n')
+ continue;
+ ln2--;
+ iln++;
+ }
+ continue;
+ }
+ if(cmd == 'a') {
+ while(iln <= ln) {
+ c = getc(fi);
+ if(c == EOF) { ret = 1; goto bye; }
+ putc(c,fo);
+ if(c == '\n')
+ iln++;
+ }
+ while(ln2) {
+ c = *ep++;
+ putc(c,fo);
+ if(c != '\n')
+ continue;
+ ln2--;
+ }
+ continue;
+ }
+ ret = 1;
+ goto bye;
+ }
+ while(1) {
+ c = getc(fi);
+ if(c == EOF) break;
+ putc(c,fo);
+ }
+ ret = 0;
+bye:
+ if(fi) {
+ if(fclose(fi) != 0) {
+ warn("%s", filein);
+ ret = 1;
+ }
+ }
+ if(fo) {
+ if(fflush(fo) != 0) {
+ warn("%s", fileout);
+ ret = 1;
+ }
+ if(fclose(fo) != 0) {
+ warn("%s", fileout);
+ ret = 1;
+ }
+ }
+ return ret;
+}
diff --git a/usr.sbin/ctm/ctm/ctm_input.c b/usr.sbin/ctm/ctm/ctm_input.c
new file mode 100644
index 0000000..32166d3
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm_input.c
@@ -0,0 +1,134 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "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 "ctm.h"
+
+/*---------------------------------------------------------------------------*/
+void
+Fatal_(int ln, char *fn, char *kind)
+{
+ if(Verbose > 2)
+ fprintf(stderr,"Fatal error. (%s:%d)\n",fn,ln);
+ fprintf(stderr,"%s Fatal error: %s\n",FileName, kind);
+}
+#define Fatal(foo) Fatal_(__LINE__,__FILE__,foo)
+#define Assert() Fatal_(__LINE__,__FILE__,"Assert failed.")
+
+/*---------------------------------------------------------------------------*/
+/* get next field, check that the terminating whitespace is what we expect */
+u_char *
+Ffield(FILE *fd, MD5_CTX *ctx,u_char term)
+{
+ static u_char buf[BUFSIZ];
+ int i,l;
+
+ for(l=0;;) {
+ if((i=getc(fd)) == EOF) {
+ Fatal("Truncated patch.");
+ return 0;
+ }
+ buf[l++] = i;
+ if(isspace(i))
+ break;
+ if(l >= sizeof buf) {
+ Fatal("Corrupt patch.");
+ printf("Token is too long.\n");
+ return 0;
+ }
+ }
+ buf[l] = '\0';
+ MD5Update(ctx,buf,l);
+ if(buf[l-1] != term) {
+ Fatal("Corrupt patch.");
+ fprintf(stderr,"Expected \"%s\" but didn't find it {%02x}.\n",
+ term == '\n' ? "\\n" : " ",buf[l-1]);
+ if(Verbose > 4)
+ fprintf(stderr,"{%s}\n",buf);
+ return 0;
+ }
+ buf[--l] = '\0';
+ if(Verbose > 4)
+ fprintf(stderr,"<%s>\n",buf);
+ return buf;
+}
+
+int
+Fbytecnt(FILE *fd, MD5_CTX *ctx, u_char term)
+{
+ u_char *p,*q;
+ int u_chars=0;
+
+ p = Ffield(fd,ctx,term);
+ if(!p) return -1;
+ for(q=p;*q;q++) {
+ if(!isdigit(*q)) {
+ Fatal("Bytecount contains non-digit.");
+ return -1;
+ }
+ u_chars *= 10;
+ u_chars += (*q - '0');
+ }
+ return u_chars;
+}
+
+u_char *
+Fdata(FILE *fd, int u_chars, MD5_CTX *ctx)
+{
+ u_char *p = Malloc(u_chars+1);
+
+ if(u_chars+1 != fread(p,1,u_chars+1,fd)) {
+ Fatal("Truncated patch.");
+ return 0;
+ }
+ MD5Update(ctx,p,u_chars+1);
+ if(p[u_chars] != '\n') {
+ if(Verbose > 3)
+ printf("FileData wasn't followed by a newline.\n");
+ Fatal("Corrupt patch.");
+ return 0;
+ }
+ p[u_chars] = '\0';
+ return p;
+}
+
+/*---------------------------------------------------------------------------*/
+/* get the filename in the next field, prepend BaseDir and give back the result
+ strings. The sustitute filename is return (the one with the suffix SUBSUFF)
+ if it exists and the qualifier contains CTM_Q_Name_Subst
+ NOTA: Buffer is already initialize with BaseDir, CatPtr is the insertion
+ point on this buffer + the length test in Ffield() is enough for Fname() */
+
+u_char *
+Fname(FILE *fd, MD5_CTX *ctx,u_char term,int qual, int verbose)
+{
+ u_char * p;
+ struct stat st;
+
+ if ((p = Ffield(fd,ctx,term)) == NULL) return(NULL);
+
+ strcpy(CatPtr, p);
+
+ if (!(qual & CTM_Q_Name_Subst)) return(Buffer);
+
+ p = Buffer + strlen(Buffer);
+
+ strcat(Buffer, SUBSUFF);
+
+ if ( -1 == stat(Buffer, &st) ) {
+ *p = '\0';
+ } else {
+ if(verbose > 2)
+ fprintf(stderr,"Using %s as substitute file\n", Buffer);
+ }
+
+ return (Buffer);
+}
diff --git a/usr.sbin/ctm/ctm/ctm_pass1.c b/usr.sbin/ctm/ctm/ctm_pass1.c
new file mode 100644
index 0000000..026a320
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm_pass1.c
@@ -0,0 +1,250 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "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 "ctm.h"
+#define BADREAD 1
+
+/*---------------------------------------------------------------------------*/
+/* Pass1 -- Validate the incoming CTM-file.
+ */
+
+int
+Pass1(FILE *fd, unsigned applied)
+{
+ u_char *p,*q;
+ MD5_CTX ctx;
+ int i,j,sep,cnt;
+ u_char *md5=0,*name=0,*trash=0;
+ struct CTM_Syntax *sp;
+ int slashwarn=0, match=0, total_matches=0;
+ unsigned current;
+ char md5_1[33];
+
+ if(Verbose>3)
+ printf("Pass1 -- Checking integrity of incoming CTM-patch\n");
+ MD5Init (&ctx);
+
+ GETFIELD(p,' '); /* CTM_BEGIN */
+ if(strcmp(p,"CTM_BEGIN")) {
+ Fatal("Probably not a CTM-patch at all.");
+ if(Verbose>3)
+ fprintf(stderr,"Expected \"CTM_BEGIN\" got \"%s\".\n",p);
+ return 1;
+ }
+
+ GETFIELDCOPY(Version,' '); /* <Version> */
+ if(strcmp(Version,VERSION)) {
+ Fatal("CTM-patch is wrong version.");
+ if(Verbose>3)
+ fprintf(stderr,"Expected \"%s\" got \"%s\".\n",VERSION,p);
+ return 1;
+ }
+
+ GETFIELDCOPY(Name,' '); /* <Name> */
+ GETFIELDCOPY(Nbr,' '); /* <Nbr> */
+ GETFIELDCOPY(TimeStamp,' '); /* <TimeStamp> */
+ GETFIELDCOPY(Prefix,'\n'); /* <Prefix> */
+
+ sscanf(Nbr, "%u", &current);
+ if (FilterList || ListIt)
+ current = 0; /* ignore if -l or if filters are present */
+ if(current && current <= applied) {
+ if(Verbose > 0)
+ fprintf(stderr,"Delta number %u is already applied; ignoring.\n",
+ current);
+ return Exit_Version;
+ }
+
+ for(;;) {
+ Delete(md5);
+ Delete(name);
+ Delete(trash);
+ cnt = -1;
+ /* if a filter list is defined we assume that all pathnames require
+ an action opposite to that requested by the first filter in the
+ list.
+ If no filter is defined, all pathnames are assumed to match. */
+ match = (FilterList ? !(FilterList->Action) : CTM_FILTER_ENABLE);
+
+ GETFIELD(p,' '); /* CTM_something */
+
+ if (p[0] != 'C' || p[1] != 'T' || p[2] != 'M') {
+ Fatal("Expected CTM keyword.");
+ fprintf(stderr,"Got [%s]\n",p);
+ return 1;
+ }
+
+ if(!strcmp(p+3,"_END"))
+ break;
+
+ for(sp=Syntax;sp->Key;sp++)
+ if(!strcmp(p+3,sp->Key))
+ goto found;
+ Fatal("Expected CTM keyword.");
+ fprintf(stderr,"Got [%s]\n",p);
+ return 1;
+ found:
+ if(Verbose > 5)
+ fprintf(stderr,"%s ",sp->Key);
+ for(i=0;(j = sp->List[i]);i++) {
+ if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes)
+ sep = ' ';
+ else
+ sep = '\n';
+
+ if(Verbose > 5)
+ fprintf(stderr," %x(%d)",sp->List[i],sep);
+
+ switch (j & CTM_F_MASK) {
+ case CTM_F_Name: /* XXX check for garbage and .. */
+ GETFIELDCOPY(name,sep);
+ j = strlen(name);
+ if(name[j-1] == '/' && !slashwarn) {
+ fprintf(stderr,"Warning: contains trailing slash\n");
+ slashwarn++;
+ }
+ if (name[0] == '/') {
+ Fatal("Absolute paths are illegal.");
+ return Exit_Mess;
+ }
+ q = name;
+ for (;;) {
+ if (q[0] == '.' && q[1] == '.')
+ if (q[2] == '/' || q[2] == '\0') {
+ Fatal("Paths containing '..' are illegal.");
+ return Exit_Mess;
+ }
+ if ((q = strchr(q, '/')) == NULL)
+ break;
+ q++;
+ }
+
+ /* if we have been asked to `keep' files then skip
+ removes; i.e. we don't match these entries at
+ all. */
+ if (KeepIt &&
+ (!strcmp(sp->Key,"DR") || !strcmp(sp->Key,"FR"))) {
+ match = CTM_FILTER_DISABLE;
+ break;
+ }
+
+ /* If filter expression have been defined, match the
+ path name against the expression list. */
+
+ if (FilterList) {
+ struct CTM_Filter *filter;
+
+ for (filter = FilterList; filter;
+ filter = filter->Next) {
+ if (0 == regexec(&filter->CompiledRegex, name,
+ 0, 0, 0))
+ /* if the name matches, adopt the
+ action */
+ match = filter->Action;
+ }
+ }
+
+ /* Add up the total number of matches */
+ total_matches += match;
+ break;
+ case CTM_F_Uid:
+ GETFIELD(p,sep);
+ while(*p) {
+ if(!isdigit(*p)) {
+ Fatal("Non-digit in uid.");
+ return 32;
+ }
+ p++;
+ }
+ break;
+ case CTM_F_Gid:
+ GETFIELD(p,sep);
+ while(*p) {
+ if(!isdigit(*p)) {
+ Fatal("Non-digit in gid.");
+ return 32;
+ }
+ p++;
+ }
+ break;
+ case CTM_F_Mode:
+ GETFIELD(p,sep);
+ while(*p) {
+ if(!isdigit(*p)) {
+ Fatal("Non-digit in mode.");
+ return 32;
+ }
+ p++;
+ }
+ break;
+ case CTM_F_MD5:
+ if(j & CTM_Q_MD5_Chunk) {
+ GETFIELDCOPY(md5,sep); /* XXX check for garbage */
+ } else if(j & CTM_Q_MD5_Before) {
+ GETFIELD(p,sep); /* XXX check for garbage */
+ } else if(j & CTM_Q_MD5_After) {
+ GETFIELD(p,sep); /* XXX check for garbage */
+ } else {
+ fprintf(stderr,"List = 0x%x\n",j);
+ Fatal("Unqualified MD5.");
+ return 32;
+ }
+ break;
+ case CTM_F_Count:
+ GETBYTECNT(cnt,sep);
+ break;
+ case CTM_F_Bytes:
+ if(cnt < 0) WRONG
+ GETDATA(trash,cnt);
+ p = MD5Data(trash,cnt,md5_1);
+ if(md5 && strcmp(md5,p)) {
+ Fatal("Internal MD5 failed.");
+ return Exit_Garbage;
+ default:
+ fprintf(stderr,"List = 0x%x\n",j);
+ Fatal("List had garbage.");
+ return Exit_Garbage;
+ }
+ }
+ }
+ if(Verbose > 5)
+ putc('\n',stderr);
+ if(ListIt && match)
+ printf("> %s %s\n", sp->Key, name);
+ }
+
+ Delete(md5);
+ Delete(name);
+ Delete(trash);
+
+ q = MD5End (&ctx,md5_1);
+ if(Verbose > 2)
+ printf("Expecting Global MD5 <%s>\n",q);
+ GETFIELD(p,'\n'); /* <MD5> */
+ if(Verbose > 2)
+ printf("Reference Global MD5 <%s>\n",p);
+ if(strcmp(q,p)) {
+ Fatal("MD5 sum doesn't match.");
+ fprintf(stderr,"\tI have:<%s>\n",q);
+ fprintf(stderr,"\tShould have been:<%s>\n",p);
+ return Exit_Garbage;
+ }
+ if (-1 != getc(fd)) {
+ if(!Force) {
+ Fatal("Trailing junk in CTM-file. Can Force with -F.");
+ return 16;
+ }
+ }
+ if ((Verbose > 1) && (0 == total_matches))
+ printf("No matches in \"%s\"\n", FileName);
+ return (total_matches ? Exit_OK : Exit_NoMatch);
+}
diff --git a/usr.sbin/ctm/ctm/ctm_pass2.c b/usr.sbin/ctm/ctm/ctm_pass2.c
new file mode 100644
index 0000000..a0b857a
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm_pass2.c
@@ -0,0 +1,301 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "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 "ctm.h"
+#define BADREAD 32
+
+/*---------------------------------------------------------------------------*/
+/* Pass2 -- Validate the incoming CTM-file.
+ */
+
+int
+Pass2(FILE *fd)
+{
+ u_char *p,*q,*md5=0;
+ MD5_CTX ctx;
+ int i,j,sep,cnt,fdesc;
+ u_char *trash=0,*name=0;
+ struct CTM_Syntax *sp;
+ struct stat st;
+ int ret = 0;
+ int match = 0;
+ char md5_1[33];
+ struct CTM_Filter *filter;
+ FILE *ed = NULL;
+ static char *template = NULL;
+
+ if(Verbose>3)
+ printf("Pass2 -- Checking if CTM-patch will apply\n");
+ MD5Init (&ctx);
+
+ GETFIELD(p,' '); if(strcmp("CTM_BEGIN",p)) WRONG
+ GETFIELD(p,' '); if(strcmp(Version,p)) WRONG
+ GETFIELD(p,' '); if(strcmp(Name,p)) WRONG
+ /* XXX Lookup name in /etc/ctm,conf, read stuff */
+ GETFIELD(p,' '); if(strcmp(Nbr,p)) WRONG
+ /* XXX Verify that this is the next patch to apply */
+ GETFIELD(p,' '); if(strcmp(TimeStamp,p)) WRONG
+ GETFIELD(p,'\n'); if(strcmp(Prefix,p)) WRONG
+ /* XXX drop or use ? */
+
+ for(;;) {
+ Delete(trash);
+ Delete(name);
+ Delete(md5);
+ cnt = -1;
+
+ /* if a filter list was specified, check file name against
+ the filters specified
+ if no filter was given operate on all files. */
+ match = (FilterList ?
+ !(FilterList->Action) : CTM_FILTER_ENABLE);
+
+ GETFIELD(p,' ');
+
+ if (p[0] != 'C' || p[1] != 'T' || p[2] != 'M') WRONG
+
+ if(!strcmp(p+3,"_END"))
+ break;
+
+ for(sp=Syntax;sp->Key;sp++)
+ if(!strcmp(p+3,sp->Key))
+ goto found;
+ WRONG
+ found:
+ for(i=0;(j = sp->List[i]);i++) {
+ if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes)
+ sep = ' ';
+ else
+ sep = '\n';
+
+ switch (j & CTM_F_MASK) {
+ case CTM_F_Name:
+ GETNAMECOPY(name,sep,j,0);
+ /* If `keep' was specified, we won't remove any files,
+ so don't check if the file exists */
+ if (KeepIt &&
+ (!strcmp(sp->Key,"FR") || !strcmp(sp->Key,"DR"))) {
+ match = CTM_FILTER_DISABLE;
+ break;
+ }
+
+ for (filter = FilterList; filter; filter = filter->Next) if (0 == regexec(&filter->CompiledRegex, name,
+ 0, 0, 0)) {
+ match = filter->Action;
+ }
+
+ if (CTM_FILTER_DISABLE == match)
+ break; /* should ignore this file */
+
+ /* XXX Check DR DM rec's for parent-dir */
+ if(j & CTM_Q_Name_New) {
+ /* XXX Check DR FR rec's for item */
+ if(-1 != stat(name,&st)) {
+ fprintf(stderr," %s: %s exists.\n",
+ sp->Key,name);
+ ret |= Exit_Forcible;
+ }
+ break;
+ }
+ if(-1 == stat(name,&st)) {
+ fprintf(stderr," %s: %s doesn't exist.\n",
+ sp->Key,name);
+ if (sp->Key[1] == 'R')
+ ret |= Exit_Forcible;
+ else
+ ret |= Exit_NotOK;
+ break;
+ }
+ if (SetTime && getuid() && (getuid() != st.st_uid)) {
+ fprintf(stderr,
+ " %s: %s not mine, cannot set time.\n",
+ sp->Key,name);
+ ret |= Exit_NotOK;
+ }
+ if (j & CTM_Q_Name_Dir) {
+ if((st.st_mode & S_IFMT) != S_IFDIR) {
+ fprintf(stderr,
+ " %s: %s exist, but isn't dir.\n",
+ sp->Key,name);
+ ret |= Exit_NotOK;
+ }
+ break;
+ }
+ if (j & CTM_Q_Name_File) {
+ if((st.st_mode & S_IFMT) != S_IFREG) {
+ fprintf(stderr,
+ " %s: %s exist, but isn't file.\n",
+ sp->Key,name);
+ ret |= Exit_NotOK;
+ }
+ break;
+ }
+ break;
+ case CTM_F_Uid:
+ case CTM_F_Gid:
+ case CTM_F_Mode:
+ GETFIELD(p,sep);
+ break;
+ case CTM_F_MD5:
+ if(!name) WRONG
+ if(j & CTM_Q_MD5_Before) {
+ char *tmp;
+ GETFIELD(p,sep);
+ if(match && (st.st_mode & S_IFMT) == S_IFREG &&
+ (tmp = MD5File(name,md5_1)) != NULL &&
+ strcmp(tmp,p)) {
+ fprintf(stderr," %s: %s md5 mismatch.\n",
+ sp->Key,name);
+ GETFIELDCOPY(md5,sep);
+ if(md5 != NULL && strcmp(tmp,md5) == 0) {
+ fprintf(stderr," %s: %s already applied.\n",
+ sp->Key,name);
+ match = CTM_FILTER_DISABLE;
+ } else if(j & CTM_Q_MD5_Force) {
+ if(Force)
+ fprintf(stderr," Can and will force.\n");
+ else
+ fprintf(stderr," Could have forced.\n");
+ ret |= Exit_Forcible;
+ } else {
+ ret |= Exit_NotOK;
+ }
+ }
+ break;
+ } else if(j & CTM_Q_MD5_After) {
+ if(md5 == NULL) {
+ GETFIELDCOPY(md5,sep);
+ }
+ break;
+ }
+ /* Unqualified MD5 */
+ WRONG
+ break;
+ case CTM_F_Count:
+ GETBYTECNT(cnt,sep);
+ break;
+ case CTM_F_Bytes:
+ if(cnt < 0) WRONG
+ GETDATA(trash,cnt);
+ if (!match)
+ break;
+ if (!template) {
+ if (asprintf(&template, "%s/CTMclientXXXXXX",
+ TmpDir) == -1) {
+ fprintf(stderr, " %s: malloc failed.\n",
+ sp->Key);
+ ret |= Exit_Mess;
+ return ret;
+ }
+ }
+ if(!strcmp(sp->Key,"FN")) {
+ if ((p = strdup(template)) == NULL) {
+ fprintf(stderr, " %s: malloc failed.\n",
+ sp->Key);
+ ret |= Exit_Mess;
+ return ret;
+ }
+ if ((fdesc = mkstemp(p)) == -1) {
+ fprintf(stderr, " %s: mkstemp failed.\n",
+ sp->Key);
+ ret |= Exit_Mess;
+ Free(p);
+ return ret;
+ }
+ if (close(fdesc) == -1) {
+ fprintf(stderr, " %s: close failed.\n",
+ sp->Key);
+ ret |= Exit_Mess;
+ unlink(p);
+ Free(p);
+ return ret;
+ }
+ j = ctm_edit(trash,cnt,name,p);
+ if(j) {
+ fprintf(stderr," %s: %s edit returned %d.\n",
+ sp->Key,name,j);
+ ret |= j;
+ unlink(p);
+ Free(p);
+ return ret;
+ } else if(strcmp(md5,MD5File(p,md5_1))) {
+ fprintf(stderr," %s: %s edit fails.\n",
+ sp->Key,name);
+ ret |= Exit_Mess;
+ unlink(p);
+ Free(p);
+ return ret;
+ }
+ unlink(p);
+ Free(p);
+ } else if (!strcmp(sp->Key,"FE")) {
+ if ((p = strdup(template)) == NULL) {
+ fprintf(stderr, " %s: malloc failed.\n",
+ sp->Key);
+ ret |= Exit_Mess;
+ return ret;
+ }
+ if ((fdesc = mkstemp(p)) == -1) {
+ fprintf(stderr, " %s: mkstemp failed.\n",
+ sp->Key);
+ ret |= Exit_Mess;
+ Free(p);
+ return ret;
+ }
+ if (close(fdesc) == -1) {
+ fprintf(stderr, " %s: close failed.\n",
+ sp->Key);
+ ret |= Exit_Mess;
+ unlink(p);
+ Free(p);
+ return ret;
+ }
+ ed = popen("ed","w");
+ if (!ed) {
+ WRONG
+ }
+ fprintf(ed,"e %s\n", name);
+ if (cnt != fwrite(trash,1,cnt,ed)) {
+ warn("%s", name);
+ pclose(ed);
+ WRONG
+ }
+ fprintf(ed,"w %s\n",p);
+ if (pclose(ed)) {
+ warn("%s", p);
+ WRONG
+ }
+ if(strcmp(md5,MD5File(p,md5_1))) {
+ fprintf(stderr,"%s %s MD5 didn't come out right\n",
+ sp->Key, name);
+ WRONG
+ }
+ unlink(p);
+ Free(p);
+ }
+
+ break;
+ default: WRONG
+ }
+ }
+ }
+
+ Delete(trash);
+ Delete(name);
+ Delete(md5);
+
+ q = MD5End (&ctx,md5_1);
+ GETFIELD(p,'\n'); /* <MD5> */
+ if(strcmp(q,p)) WRONG
+ if (-1 != getc(fd)) WRONG
+ return ret;
+}
diff --git a/usr.sbin/ctm/ctm/ctm_pass3.c b/usr.sbin/ctm/ctm/ctm_pass3.c
new file mode 100644
index 0000000..34c6d87c1d
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm_pass3.c
@@ -0,0 +1,295 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "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 "ctm.h"
+#define BADREAD 32
+
+/*---------------------------------------------------------------------------*/
+/* Pass3 -- Validate the incoming CTM-file.
+ */
+
+int
+settime(const char *name, const struct timeval *times)
+{
+ if (SetTime)
+ if (utimes(name,times)) {
+ warn("utimes(): %s", name);
+ return -1;
+ }
+ return 0;
+}
+
+int
+Pass3(FILE *fd)
+{
+ u_char *p,*q,buf[BUFSIZ];
+ MD5_CTX ctx;
+ int i,j,sep,cnt;
+ u_char *md5=0,*md5before=0,*trash=0,*name=0,*uid=0,*gid=0,*mode=0;
+ struct CTM_Syntax *sp;
+ FILE *ed=0;
+ struct stat st;
+ char md5_1[33];
+ int match=0;
+ struct timeval times[2];
+ struct CTM_Filter *filter = NULL;
+ if(Verbose>3)
+ printf("Pass3 -- Applying the CTM-patch\n");
+ MD5Init (&ctx);
+
+ GETFIELD(p,' '); if(strcmp("CTM_BEGIN",p)) WRONG
+ GETFIELD(p,' '); if(strcmp(Version,p)) WRONG
+ GETFIELD(p,' '); if(strcmp(Name,p)) WRONG
+ GETFIELD(p,' '); if(strcmp(Nbr,p)) WRONG
+ GETFIELD(p,' '); if(strcmp(TimeStamp,p)) WRONG
+ GETFIELD(p,'\n'); if(strcmp(Prefix,p)) WRONG
+
+ /*
+ * This would be cleaner if mktime() worked in UTC rather than
+ * local time.
+ */
+ if (SetTime) {
+ struct tm tm;
+ char *tz;
+ char buf[5];
+ int i;
+
+#define SUBSTR(off,len) strncpy(buf, &TimeStamp[off], len), buf[len] = '\0'
+#define WRONGDATE { fprintf(stderr, " %s failed date validation\n",\
+ TimeStamp); WRONG}
+
+ if (strlen(TimeStamp) != 15 || TimeStamp[14] != 'Z') WRONGDATE
+ for (i = 0; i < 14; i++)
+ if (!isdigit(TimeStamp[i])) WRONGDATE
+
+ tz = getenv("TZ");
+ if (setenv("TZ", "UTC", 1) < 0) WRONG
+ tzset();
+
+ tm.tm_isdst = tm.tm_gmtoff = 0;
+
+ SUBSTR(0, 4);
+ tm.tm_year = atoi(buf) - 1900;
+ SUBSTR(4, 2);
+ tm.tm_mon = atoi(buf) - 1;
+ if (tm.tm_mon < 0 || tm.tm_mon > 11) WRONGDATE
+ SUBSTR(6, 2);
+ tm.tm_mday = atoi(buf);
+ if (tm.tm_mday < 1 || tm.tm_mday > 31) WRONG;
+ SUBSTR(8, 2);
+ tm.tm_hour = atoi(buf);
+ if (tm.tm_hour > 24) WRONGDATE
+ SUBSTR(10, 2);
+ tm.tm_min = atoi(buf);
+ if (tm.tm_min > 59) WRONGDATE
+ SUBSTR(12, 2);
+ tm.tm_sec = atoi(buf);
+ if (tm.tm_min > 62) WRONGDATE /* allow leap seconds */
+
+ times[0].tv_sec = times[1].tv_sec = mktime(&tm);
+ if (times[0].tv_sec == -1) WRONGDATE
+ times[0].tv_usec = times[1].tv_usec = 0;
+
+ if (tz) {
+ if (setenv("TZ", tz, 1) < 0) WRONGDATE
+ } else {
+ unsetenv("TZ");
+ }
+ }
+
+ for(;;) {
+ Delete(md5);
+ Delete(uid);
+ Delete(gid);
+ Delete(mode);
+ Delete(md5before);
+ Delete(trash);
+ Delete(name);
+ cnt = -1;
+
+ GETFIELD(p,' ');
+
+ if (p[0] != 'C' || p[1] != 'T' || p[2] != 'M') WRONG
+
+ if(!strcmp(p+3,"_END"))
+ break;
+
+ for(sp=Syntax;sp->Key;sp++)
+ if(!strcmp(p+3,sp->Key))
+ goto found;
+ WRONG
+ found:
+ for(i=0;(j = sp->List[i]);i++) {
+ if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes)
+ sep = ' ';
+ else
+ sep = '\n';
+
+ switch (j & CTM_F_MASK) {
+ case CTM_F_Name: GETNAMECOPY(name,sep,j, Verbose); break;
+ case CTM_F_Uid: GETFIELDCOPY(uid,sep); break;
+ case CTM_F_Gid: GETFIELDCOPY(gid,sep); break;
+ case CTM_F_Mode: GETFIELDCOPY(mode,sep); break;
+ case CTM_F_MD5:
+ if(j & CTM_Q_MD5_Before)
+ GETFIELDCOPY(md5before,sep);
+ else
+ GETFIELDCOPY(md5,sep);
+ break;
+ case CTM_F_Count: GETBYTECNT(cnt,sep); break;
+ case CTM_F_Bytes: GETDATA(trash,cnt); break;
+ default: WRONG
+ }
+ }
+ /* XXX This should go away. Disallow trailing '/' */
+ j = strlen(name)-1;
+ if(name[j] == '/') name[j] = '\0';
+
+ /*
+ * If a filter list is specified, run thru the filter list and
+ * match `name' against filters. If the name matches, set the
+ * required action to that specified in the filter.
+ * The default action if no filterlist is given is to match
+ * everything.
+ */
+
+ match = (FilterList ? !(FilterList->Action) : CTM_FILTER_ENABLE);
+ for (filter = FilterList; filter; filter = filter->Next) {
+ if (0 == regexec(&filter->CompiledRegex, name,
+ 0, 0, 0)) {
+ match = filter->Action;
+ }
+ }
+
+ if (CTM_FILTER_DISABLE == match) /* skip file if disabled */
+ continue;
+
+ if (Verbose > 0)
+ fprintf(stderr,"> %s %s\n",sp->Key,name);
+ if(!strcmp(sp->Key,"FM") || !strcmp(sp->Key, "FS")) {
+ i = open(name,O_WRONLY|O_CREAT|O_TRUNC,0666);
+ if(i < 0) {
+ warn("%s", name);
+ WRONG
+ }
+ if(cnt != write(i,trash,cnt)) {
+ warn("%s", name);
+ WRONG
+ }
+ close(i);
+ if(strcmp(md5,MD5File(name,md5_1))) {
+ fprintf(stderr," %s %s MD5 didn't come out right\n",
+ sp->Key,name);
+ WRONG
+ }
+ if (settime(name,times)) WRONG
+ continue;
+ }
+ if(!strcmp(sp->Key,"FE")) {
+ ed = popen("ed","w");
+ if(!ed) {
+ WRONG
+ }
+ fprintf(ed,"e %s\n",name);
+ if(cnt != fwrite(trash,1,cnt,ed)) {
+ warn("%s", name);
+ pclose(ed);
+ WRONG
+ }
+ fprintf(ed,"w %s\n",name);
+ if(pclose(ed)) {
+ warn("ed");
+ WRONG
+ }
+ if(strcmp(md5,MD5File(name,md5_1))) {
+ fprintf(stderr," %s %s MD5 didn't come out right\n",
+ sp->Key,name);
+ WRONG
+ }
+ if (settime(name,times)) WRONG
+ continue;
+ }
+ if(!strcmp(sp->Key,"FN")) {
+ strcpy(buf,name);
+ strcat(buf,TMPSUFF);
+ i = ctm_edit(trash,cnt,name,buf);
+ if(i) {
+ fprintf(stderr," %s %s Edit failed with code %d.\n",
+ sp->Key,name,i);
+ WRONG
+ }
+ if(strcmp(md5,MD5File(buf,md5_1))) {
+ fprintf(stderr," %s %s Edit failed MD5 check.\n",
+ sp->Key,name);
+ WRONG
+ }
+ if (rename(buf,name) == -1)
+ WRONG
+ if (settime(name,times)) WRONG
+ continue;
+ }
+ if(!strcmp(sp->Key,"DM")) {
+ if(0 > mkdir(name,0777)) {
+ sprintf(buf,"mkdir -p %s",name);
+ system(buf);
+ }
+ if(0 > stat(name,&st) || ((st.st_mode & S_IFMT) != S_IFDIR)) {
+ fprintf(stderr,"<%s> mkdir failed\n",name);
+ WRONG
+ }
+ if (settime(name,times)) WRONG
+ continue;
+ }
+ if(!strcmp(sp->Key,"FR")) {
+ if (KeepIt) {
+ if (Verbose > 1)
+ printf("<%s> not removed\n", name);
+ }
+ else if (0 != unlink(name)) {
+ fprintf(stderr,"<%s> unlink failed\n",name);
+ if (!Force)
+ WRONG
+ }
+ continue;
+ }
+ if(!strcmp(sp->Key,"DR")) {
+ /*
+ * We cannot use rmdir() because we do not get the directories
+ * in '-depth' order (cvs-cur.0018.gz for examples)
+ */
+ if (KeepIt) {
+ if (Verbose > 1) {
+ printf("<%s> not removed\n", name);
+ }
+ } else {
+ sprintf(buf,"rm -rf %s",name);
+ system(buf);
+ }
+ continue;
+ }
+ WRONG
+ }
+
+ Delete(md5);
+ Delete(uid);
+ Delete(gid);
+ Delete(mode);
+ Delete(md5before);
+ Delete(trash);
+ Delete(name);
+
+ q = MD5End (&ctx,md5_1);
+ GETFIELD(p,'\n');
+ if(strcmp(q,p)) WRONG
+ if (-1 != getc(fd)) WRONG
+ return 0;
+}
diff --git a/usr.sbin/ctm/ctm/ctm_passb.c b/usr.sbin/ctm/ctm/ctm_passb.c
new file mode 100644
index 0000000..ee3a69c5f
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm_passb.c
@@ -0,0 +1,142 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <koshy@india.hp.com> 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. Joseph Koshy
+ * ----------------------------------------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include "ctm.h"
+#define BADREAD 32
+
+/*---------------------------------------------------------------------------*/
+/* PassB -- Backup modified files.
+ */
+
+int
+PassB(FILE *fd)
+{
+ u_char *p,*q;
+ MD5_CTX ctx;
+ int i,j,sep,cnt;
+ u_char *md5=0,*md5before=0,*trash=0,*name=0,*uid=0,*gid=0,*mode=0;
+ struct CTM_Syntax *sp;
+ FILE *b = 0; /* backup command */
+ u_char buf[BUFSIZ];
+ char md5_1[33];
+ int ret = 0;
+ int match = 0;
+ struct CTM_Filter *filter = NULL;
+
+ if(Verbose>3)
+ printf("PassB -- Backing up files which would be changed.\n");
+
+ MD5Init (&ctx);
+ snprintf(buf, sizeof(buf), fmtcheck(TarCmd, TARCMD), BackupFile);
+ b=popen(buf, "w");
+ if(!b) { warn("%s", buf); return Exit_Garbage; }
+
+ GETFIELD(p,' '); if(strcmp("CTM_BEGIN",p)) WRONG
+ GETFIELD(p,' '); if(strcmp(Version,p)) WRONG
+ GETFIELD(p,' '); if(strcmp(Name,p)) WRONG
+ GETFIELD(p,' '); if(strcmp(Nbr,p)) WRONG
+ GETFIELD(p,' '); if(strcmp(TimeStamp,p)) WRONG
+ GETFIELD(p,'\n'); if(strcmp(Prefix,p)) WRONG
+
+ for(;;) {
+ Delete(md5);
+ Delete(uid);
+ Delete(gid);
+ Delete(mode);
+ Delete(md5before);
+ Delete(trash);
+ Delete(name);
+ cnt = -1;
+
+ GETFIELD(p,' ');
+
+ if (p[0] != 'C' || p[1] != 'T' || p[2] != 'M') WRONG
+
+ if(!strcmp(p+3,"_END"))
+ break;
+
+ for(sp=Syntax;sp->Key;sp++)
+ if(!strcmp(p+3,sp->Key))
+ goto found;
+ WRONG
+ found:
+ for(i=0;(j = sp->List[i]);i++) {
+ if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes)
+ sep = ' ';
+ else
+ sep = '\n';
+
+ switch (j & CTM_F_MASK) {
+ case CTM_F_Name: GETNAMECOPY(name,sep,j, Verbose); break;
+ case CTM_F_Uid: GETFIELDCOPY(uid,sep); break;
+ case CTM_F_Gid: GETFIELDCOPY(gid,sep); break;
+ case CTM_F_Mode: GETFIELDCOPY(mode,sep); break;
+ case CTM_F_MD5:
+ if(j & CTM_Q_MD5_Before)
+ GETFIELDCOPY(md5before,sep);
+ else
+ GETFIELDCOPY(md5,sep);
+ break;
+ case CTM_F_Count: GETBYTECNT(cnt,sep); break;
+ case CTM_F_Bytes: GETDATA(trash,cnt); break;
+ default: WRONG
+ }
+ }
+ /* XXX This should go away. Disallow trailing '/' */
+ j = strlen(name)-1;
+ if(name[j] == '/') name[j] = '\0';
+
+ if (KeepIt &&
+ (!strcmp(sp->Key,"DR") || !strcmp(sp->Key,"FR")))
+ continue;
+
+ /* match the name against the elements of the filter list. The
+ action associated with the last matched filter determines whether
+ this file should be ignored or backed up. */
+ match = (FilterList ? !(FilterList->Action) : CTM_FILTER_ENABLE);
+ for (filter = FilterList; filter; filter = filter->Next) {
+ if (0 == regexec(&filter->CompiledRegex, name, 0, 0, 0))
+ match = filter->Action;
+ }
+
+ if (CTM_FILTER_DISABLE == match)
+ continue;
+
+ if (!strcmp(sp->Key,"FS") || !strcmp(sp->Key,"FN") ||
+ !strcmp(sp->Key,"AS") || !strcmp(sp->Key,"DR") ||
+ !strcmp(sp->Key,"FR")) {
+ /* send name to the archiver for a backup */
+ cnt = strlen(name);
+ if (cnt != fwrite(name,1,cnt,b) || EOF == fputc('\n',b)) {
+ warn("%s", name);
+ pclose(b);
+ WRONG;
+ }
+ }
+ }
+
+ ret = pclose(b);
+
+ Delete(md5);
+ Delete(uid);
+ Delete(gid);
+ Delete(mode);
+ Delete(md5before);
+ Delete(trash);
+ Delete(name);
+
+ q = MD5End (&ctx,md5_1);
+ GETFIELD(p,'\n'); /* <MD5> */
+ if(strcmp(q,p)) WRONG
+ if (-1 != getc(fd)) WRONG
+ return ret;
+}
diff --git a/usr.sbin/ctm/ctm/ctm_syntax.c b/usr.sbin/ctm/ctm/ctm_syntax.c
new file mode 100644
index 0000000..6638943
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm_syntax.c
@@ -0,0 +1,67 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "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 "ctm.h"
+
+/* The fields... */
+#define Name CTM_F_Name
+#define Uid CTM_F_Uid
+#define Gid CTM_F_Gid
+#define Mode CTM_F_Mode
+#define MD5 CTM_F_MD5
+#define Count CTM_F_Count
+#define Bytes CTM_F_Bytes
+
+/* The qualifiers... */
+#define File CTM_Q_Name_File
+#define Dir CTM_Q_Name_Dir
+#define New CTM_Q_Name_New
+#define Subst CTM_Q_Name_Subst
+#define After CTM_Q_MD5_After
+#define Before CTM_Q_MD5_Before
+#define Chunk CTM_Q_MD5_Chunk
+#define Force CTM_Q_MD5_Force
+
+static int ctmFM[] = /* File Make */
+ { Name|File|New|Subst, Uid, Gid, Mode,
+ MD5|After|Chunk, Count, Bytes,0 };
+
+static int ctmFS[] = /* File Substitute */
+ { Name|File|Subst, Uid, Gid, Mode,
+ MD5|Before|Force, MD5|After|Chunk, Count, Bytes,0 };
+
+static int ctmFE[] = /* File Edit */
+ { Name|File|Subst, Uid, Gid, Mode,
+ MD5|Before, MD5|After, Count, Bytes,0 };
+
+static int ctmFR[] = /* File Remove */
+ { Name|File|Subst, MD5|Before, 0 };
+
+static int ctmAS[] = /* Attribute Substitute */
+ { Name|Subst, Uid, Gid, Mode, 0 };
+
+static int ctmDM[] = /* Directory Make */
+ { Name|Dir|New , Uid, Gid, Mode, 0 };
+
+static int ctmDR[] = /* Directory Remove */
+ { Name|Dir, 0 };
+
+struct CTM_Syntax Syntax[] = {
+ { "FM", ctmFM },
+ { "FS", ctmFS },
+ { "FE", ctmFE },
+ { "FN", ctmFE },
+ { "FR", ctmFR },
+ { "AS", ctmAS },
+ { "DM", ctmDM },
+ { "DR", ctmDR },
+ { 0, 0} };
diff --git a/usr.sbin/ctm/ctm_dequeue/Makefile b/usr.sbin/ctm/ctm_dequeue/Makefile
new file mode 100644
index 0000000..07a234f
--- /dev/null
+++ b/usr.sbin/ctm/ctm_dequeue/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../ctm_rmail
+
+PROG= ctm_dequeue
+NO_MAN=
+SRCS= ctm_dequeue.c error.c
+
+CFLAGS+= -I${.CURDIR}/../ctm_rmail
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ctm/ctm_dequeue/ctm_dequeue.c b/usr.sbin/ctm/ctm_dequeue/ctm_dequeue.c
new file mode 100644
index 0000000..783fd3f
--- /dev/null
+++ b/usr.sbin/ctm/ctm_dequeue/ctm_dequeue.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 1996, Gary J. Palmer
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED 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$
+ */
+
+/*
+ * ctm_dequeue: Dequeue queued delta pieces and mail them.
+ *
+ * The pieces have already been packaged up as mail messages by ctm_smail,
+ * and will be simply passed to sendmail in alphabetical order.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fts.h>
+#include <limits.h>
+#include <errno.h>
+#include <paths.h>
+#include "error.h"
+#include "options.h"
+
+#define DEFAULT_NUM 1 /* Default number of pieces mailed per run. */
+
+int fts_sort(const FTSENT * const *, const FTSENT * const *);
+int run_sendmail(int ifd);
+
+int
+main(int argc, char **argv)
+{
+ char *log_file = NULL;
+ char *queue_dir = NULL;
+ char *list[2];
+ int num_to_send = DEFAULT_NUM, chunk;
+ int fd;
+ FTS *fts;
+ FTSENT *ftsent;
+ int piece, npieces;
+ char filename[PATH_MAX];
+
+ err_prog_name(argv[0]);
+
+ OPTIONS("[-l log] [-n num] queuedir")
+ NUMBER('n', num_to_send)
+ STRING('l', log_file)
+ ENDOPTS
+
+ if (argc != 2)
+ usage();
+
+ if (log_file)
+ err_set_log(log_file);
+
+ queue_dir = argv[1];
+ list[0] = queue_dir;
+ list[1] = NULL;
+
+ fts = fts_open(list, FTS_PHYSICAL|FTS_COMFOLLOW, fts_sort);
+ if (fts == NULL)
+ {
+ err("fts failed on `%s'", queue_dir);
+ exit(1);
+ }
+
+ ftsent = fts_read(fts);
+ if (ftsent == NULL || ftsent->fts_info != FTS_D)
+ {
+ err("not a directory: %s", queue_dir);
+ exit(1);
+ }
+
+ ftsent = fts_children(fts, 0);
+ if (ftsent == NULL && errno)
+ {
+ err("*ftschildren failed");
+ exit(1);
+ }
+
+ for (chunk = 1; ftsent != NULL; ftsent = ftsent->fts_link)
+ {
+ /*
+ * Skip non-files and ctm_smail tmp files (ones starting with `.')
+ */
+ if (ftsent->fts_info != FTS_F || ftsent->fts_name[0] == '.')
+ continue;
+
+ sprintf(filename, "%s/%s", queue_dir, ftsent->fts_name);
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ {
+ err("*open: %s", filename);
+ exit(1);
+ }
+
+ if (run_sendmail(fd))
+ exit(1);
+
+ close(fd);
+
+ if (unlink(filename) < 0)
+ {
+ err("*unlink: %s", filename);
+ exit(1);
+ }
+
+ /*
+ * Deduce the delta, piece number, and number of pieces from
+ * the name of the file in the queue. Ideally, we should be
+ * able to get the mail alias name too.
+ *
+ * NOTE: This depends intimately on the queue name used in ctm_smail.
+ */
+ npieces = atoi(&ftsent->fts_name[ftsent->fts_namelen-3]);
+ piece = atoi(&ftsent->fts_name[ftsent->fts_namelen-7]);
+ err("%.*s %d/%d sent", ftsent->fts_namelen-8, ftsent->fts_name,
+ piece, npieces);
+
+ if (chunk++ == num_to_send)
+ break;
+ }
+
+ fts_close(fts);
+
+ return(0);
+}
+
+int
+fts_sort(const FTSENT * const * a, const FTSENT * const * b)
+{
+ if ((*a)->fts_info != FTS_F)
+ return(0);
+ if ((*a)->fts_info != FTS_F)
+ return(0);
+
+ return (strcmp((*a)->fts_name, (*b)->fts_name));
+}
+
+/*
+ * Run sendmail with the given file descriptor as standard input.
+ * Sendmail will decode the destination from the message contents.
+ * Returns 0 on success, 1 on failure.
+ */
+int
+run_sendmail(int ifd)
+{
+ pid_t child, pid;
+ int status;
+
+ switch (child = fork())
+ {
+ case -1:
+ err("*fork");
+ return(1);
+
+ case 0: /* Child */
+ dup2(ifd, 0);
+ execl(_PATH_SENDMAIL, _PATH_SENDMAIL, "-odq", "-t", (char *)NULL);
+ err("*exec: %s", _PATH_SENDMAIL);
+ _exit(1);
+
+ default: /* Parent */
+ while ((pid = wait(&status)) != child)
+ {
+ if (pid == -1 && errno != EINTR)
+ {
+ err("*wait");
+ return(1);
+ }
+ }
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+ {
+ err("sendmail failed");
+ return(1);
+ }
+ }
+
+ return(0);
+}
diff --git a/usr.sbin/ctm/ctm_rmail/Makefile b/usr.sbin/ctm/ctm_rmail/Makefile
new file mode 100644
index 0000000..e2052b9
--- /dev/null
+++ b/usr.sbin/ctm/ctm_rmail/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= ctm_rmail
+MLINKS= ctm_rmail.1 ctm_smail.1 \
+ ctm_rmail.1 ctm_dequeue.1
+SRCS= ctm_rmail.c error.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ctm/ctm_rmail/ctm_rmail.1 b/usr.sbin/ctm/ctm_rmail/ctm_rmail.1
new file mode 100644
index 0000000..5476e49
--- /dev/null
+++ b/usr.sbin/ctm/ctm_rmail/ctm_rmail.1
@@ -0,0 +1,510 @@
+.\" NOTICE: This is free documentation. I hope you get some use from these
+.\" words. In return you should think about all the nice people who sweat
+.\" blood to document their free software. Maybe you should write some
+.\" documentation and give it away. Maybe with a free program attached!
+.\"
+.\" Author: Stephen McKay
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 24, 1995
+.Dt CTM_MAIL 1
+.Os
+.Sh NAME
+.Nm ctm_smail ,
+.Nm ctm_dequeue ,
+.Nm ctm_rmail
+.Nd send and receive
+.Xr ctm 1
+deltas via mail
+.Sh SYNOPSIS
+.Nm ctm_smail
+.Op Fl l Ar log
+.Op Fl m Ar maxmsgsize
+.Op Fl c Ar maxctmsize
+.Op Fl q Ar queue-dir
+.Ar ctm-delta
+.Ar mail-alias
+.Nm ctm_dequeue
+.Op Fl l Ar log
+.Op Fl n Ar numchunks
+.Ar queue-dir
+.Nm ctm_rmail
+.Op Fl Dfuv
+.Op Fl l Ar log
+.Op Fl p Ar piecedir
+.Op Fl d Ar deltadir
+.Op Fl b Ar basedir
+.Op Ar
+.Sh DESCRIPTION
+In conjunction with the
+.Xr ctm 1
+command,
+.Nm ctm_smail ,
+.Nm ctm_dequeue
+and
+.Nm ctm_rmail
+are used to distribute changes to a source tree via email.
+The
+.Nm ctm_smail
+utility is given a compressed
+.Xr ctm
+delta, and a mailing list to send it to.
+It splits the delta into manageable
+pieces, encodes them as mail messages and sends them to the mailing list
+(optionally queued to spread the mail load).
+Each recipient uses
+.Nm ctm_rmail
+(either manually or automatically) to decode and reassemble the delta, and
+optionally call
+.Xr ctm
+to apply it to the source tree.
+At the moment,
+several source trees are distributed, and by several sites.
+These include
+the
+.Fx Ns -current
+source and CVS trees, distributed by
+.Li freefall.FreeBSD.org .
+.Pp
+Command line arguments for
+.Nm ctm_smail :
+.Bl -tag -width indent
+.It Fl l Ar log
+Instead of appearing on
+.Em stderr ,
+error diagnostics and informational messages (other than command line errors)
+are time stamped and written to the file
+.Em log .
+.It Fl m Ar maxmsgsize
+Limit the maximum size mail message that
+.Nm ctm_smail
+is allowed to send.
+It is approximate since mail headers and other niceties
+are not counted in this limit.
+If not specified, it will default to 64000
+bytes, leaving room for 1535 bytes of headers before the rumoured 64k mail
+limit.
+.It Fl c Ar maxctmsize
+Limit the maximum size delta that will be sent.
+Deltas bigger that this
+limit will cause an apology mail message to be sent to the mailing list.
+This is to prevent massive changes overwhelming users' mail boxes.
+Note that
+this is the size before encoding.
+Encoding causes a 4/3 size increase before
+mail headers are added.
+If not specified, there is no limit.
+.It Fl q Ar queue-dir
+Instead of mailing the delta pieces now, store them in the given directory
+to be mailed later using
+.Nm ctm_dequeue .
+This feature allows the mailing of large deltas to be spread out over
+hours or even days to limit the impact on recipients with limited network
+bandwidth or small mail spool areas.
+.El
+.Pp
+.Ar ctm-delta
+is the delta to be sent, and
+.Ar mail-alias
+is the mailing list to send the delta to.
+The mail messages are sent using
+.Xr sendmail 8 .
+.Pp
+Command line arguments for
+.Nm ctm_dequeue :
+.Bl -tag -width indent
+.It Fl l Ar log
+Instead of appearing on
+.Em stderr ,
+error diagnostics and informational messages (other than command line errors)
+are time stamped and written to the file
+.Em log .
+.It Fl n Ar numchunks
+Limit the number of mail messages that
+.Nm ctm_dequeue
+will send per run.
+By default,
+.Nm ctm_dequeue
+will send one mail message per run.
+.El
+.Pp
+.Ar queuedir
+is the directory containing the mail messages stored by
+.Nm ctm_smail .
+Up to
+.Ar numchunks
+mail messages will be sent in each run.
+The recipient mailing list is already
+encoded in the queued files.
+.Pp
+It is safe to run
+.Nm ctm_dequeue
+while
+.Nm ctm_smail
+is adding entries to the queue, or even to run
+.Nm ctm_smail
+multiple times concurrently, but a separate queue directory should be used
+for each tree being distributed.
+This is because entries are served in
+alphabetical order, and one tree will be unfairly serviced before any others,
+based on the delta names, not delta creation times.
+.Pp
+Command line arguments for
+.Nm ctm_rmail :
+.Bl -tag -width indent
+.It Fl l Ar log
+Instead of appearing on
+.Em stderr ,
+error diagnostics and informational messages (other than command line errors)
+are time stamped and written to the file
+.Em log .
+.It Fl p Ar piecedir
+Collect pieces of deltas in this directory.
+Each piece corresponds to a
+single mail message.
+Pieces are removed when complete deltas are built.
+If this flag is not given, no input files will be read, but completed
+deltas may still be applied with
+.Xr ctm
+if the
+.Fl b
+flag is given.
+.It Fl d Ar deltadir
+Collect completed deltas in this directory.
+Deltas are built from one or
+more pieces when all pieces are present.
+.It Fl b Ar basedir
+Apply any completed deltas to this source tree.
+If this flag is not given,
+deltas will be stored, but not applied.
+The user may then apply the deltas
+manually, or by using
+.Nm ctm_rmail
+without the
+.Fl p
+flag.
+Deltas will not be applied if they do not match the
+.Li .ctm_status
+file in
+.Ar basedir
+(or if
+.Li .ctm_status
+does not exist).
+.It Fl D
+Delete deltas after successful application by
+.Xr ctm .
+It is probably a good idea to avoid this flag (and keep all the deltas) as
+.Xr ctm
+has the ability to recover small groups of files from a full set of deltas.
+.It Fl f
+Fork and execute in the background while applying deltas with
+.Xr ctm .
+This is useful when automatically invoking
+.Nm ctm_rmail
+from
+.Xr sendmail
+because
+.Xr ctm
+can take a very long time to complete, causing other people's mail to
+be delayed, and can in theory cause spurious
+mail retransmission due to the remote
+.Xr sendmail
+timing out, or even termination of
+.Nm ctm_rmail
+by mail filters such as
+.Xr "MH's"
+.Xr slocal .
+Do not worry about zillions of background
+.Xr ctm
+processes loading your machine, since locking is used to prevent more than one
+.Xr ctm
+invocation at a time.
+.It Fl u
+Pass the
+.Fl u
+flag to the
+.Xr ctm
+command when applying the complete deltas, causing it to set the modification
+time of created and modified files to the CTM delta creation time.
+.It Fl v
+Pass the
+.Fl v
+flag to the
+.Xr ctm
+command when applying the complete deltas, causing a more informative
+output.
+All
+.Xr ctm
+output appears in the
+.Nm ctm_rmail
+log file.
+.El
+.Pp
+The file arguments (or
+.Em stdin ,
+if there are none) are scanned for delta pieces.
+Multiple delta pieces
+can be read from a single file, so an entire maildrop can be scanned
+and processed with a single command.
+.Pp
+It is safe to invoke
+.Nm ctm_rmail
+multiple times concurrently (with different input files),
+as might happen when
+.Xr sendmail
+is delivering mail asynchronously.
+This is because locking is used to
+keep things orderly.
+.Sh FILE FORMAT
+Following are the important parts of an actual (very small) delta piece:
+.Bd -literal
+From: owner-src-cur
+To: src-cur
+Subject: ctm-mail src-cur.0003.gz 1/4
+
+CTM_MAIL BEGIN src-cur.0003.gz 1 4
+H4sIAAAAAAACA3VU72/bNhD9bP0VByQoEiyRSZEUSQP9kKTeYCR2gDTdsGFAwB/HRogtG5K8NCj6
+v4+UZSdtUQh6Rz0eee/xaF/dzx8up3/MFlDkBNrGnbttAwyo1pxoRgoiBNX/QJ5d3c9/X8DcPGGo
+lggkPiXngE4W1gUjKPJCYyk5MZRbIqmNW/ASglIFcdwIzTUxaAqhnCPcBqloKEkJVNDMF0Azk+Bo
+dDzzk0Ods/+A5gXv9YyJHjMCtJwQNeESNma7hOmXDRxn
+CTM_MAIL END 61065
+.Ed
+.Pp
+The subject of the message always begins with
+.Dq ctm-mail
+followed by the name of the delta, which piece this is, and how many total
+pieces there are.
+The data are bracketed by
+.Dq CTM_MAIL BEGIN
+and
+.Dq CTM_MAIL END
+lines, duplicating the information in the subject line, plus a simple checksum.
+.Pp
+If the delta exceeds
+.Ar maxctmsize ,
+then a message like this will be received instead:
+.Bd -literal
+From: owner-src-cur
+To: src-cur
+Subject: ctm-notice src-cur.0999.gz
+
+src-cur.0999.gz is 792843 bytes. The limit is 300000 bytes.
+
+You can retrieve this delta via ftp.
+.Ed
+.Pp
+You are then on your own!
+.Sh ENVIRONMENT
+If deltas are to be applied then
+.Xr ctm 1
+and
+.Xr gunzip 1
+must be in your
+.Ev PATH .
+.Sh FILES
+.Bl -tag -width indent
+.It Pa QUEUEDIR/*
+Pieces of deltas encoded as mail messages waiting to be sent to the
+mailing list.
+.It Pa PIECEDIR/*
+Pieces of deltas waiting for the rest to arrive.
+.It Pa DELTADIR/*
+Completed deltas.
+.It Pa BASEDIR/.ctm_status
+File containing the name and number of the next delta to be applied to this
+source tree.
+.El
+.Sh EXIT STATUS
+The
+.Nm ctm_smail ,
+.Nm ctm_dequeue
+and
+.Nm ctm_rmail
+utilities return exit status 0 for success, and 1 for various failures.
+The
+.Nm ctm_rmail
+utility is expected to be called from a mail transfer program, and thus signals
+failure only when the input mail message should be bounced (preferably into
+your regular maildrop, not back to the sender).
+In short, failure to
+apply a completed delta with
+.Xr ctm
+is not considered an error important enough to bounce the mail, and
+.Nm ctm_rmail
+returns an exit status of 0.
+.Sh EXAMPLES
+To send delta 32 of
+.Em src-cur
+to a group of wonderful code hackers known to
+.Xr sendmail
+as
+.Em src-guys ,
+limiting the mail size to roughly 60000 bytes, you could use:
+.Bd -literal -offset indent
+ctm_smail -m 60000 /wherever/it/is/src-cur.0032.gz src-guys
+.Ed
+.Pp
+To decode every
+.Nm ctm-mail
+message in your mailbox, assemble them into complete deltas, then apply
+any deltas built or lying around, you could use:
+.Bd -literal -offset indent
+ctm_rmail -p ~/pieces -d ~/deltas -b /usr/ctm-src-cur $MAIL
+.Ed
+.Pp
+(Note that no messages are deleted by
+.Nm ctm_rmail .
+Any mail reader could be used for that purpose.)
+.Pp
+To create a mail alias called
+.Em receiver-dude
+that will automatically decode and assemble deltas, but not apply them,
+you could put the following lines in your
+.Pa /etc/mail/aliases
+file (assuming the
+.Pa /ctm/tmp
+and
+.Pa /ctm/deltas
+directories and
+.Pa /ctm/log
+file are writable by user
+.Em daemon
+or group
+.Em wheel ) :
+.Bd -literal -offset indent
+receiver-dude: "|ctm_rmail -p /ctm/tmp -d /ctm/deltas -l /ctm/log"
+owner-receiver-dude: real_dude@wherever.you.like
+.Ed
+.Pp
+The second line will catch failures and drop them into your regular mailbox,
+or wherever else you like.
+.Pp
+To apply all the deltas collected, and delete those applied, you could use:
+.Bd -literal -offset indent
+ctm_rmail -D -d /ctm/deltas -b /ctm/src-cur -l /ctm/apply.log
+.Ed
+.Pp
+For maximum flexibility, consider this excerpt from a
+.Xr procmail
+script:
+.Bd -literal -offset indent
+PATH=$HOME/bin:$PATH
+
+:0 w
+* ^Subject: ctm-mail cvs-cur
+| ctm_incoming
+.Ed
+.Pp
+together with the
+shell script
+.Pa ~/bin/ctm_incoming :
+.Bd -literal -offset indent
+#! /bin/sh
+PATH="$HOME/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin"
+export PATH
+
+cd $HOME/ctm && ctm_rmail -f -p pieces -d deltas -l log -b /ctm
+.Ed
+.Pp
+which will deposit all
+.Xr ctm
+deltas in
+.Pa ~/ctm/deltas ,
+apply them to the tree in
+.Pa /ctm ,
+and drop any failures into your regular mail box.
+Note the
+.Ev PATH
+manipulation in
+.Pa ctm_incoming
+which allows
+.Nm ctm_rmail
+to execute
+.Xr ctm 1
+on the
+.Pq non- Ns Fx
+machine that this example was taken from.
+.Sh SECURITY
+On its own, CTM is an insecure protocol
+- there is no authentication performed that the
+changes applied to the source code were sent by a
+trusted party, and so care should be taken if the
+CTM deltas are obtained via an unauthenticated
+medium such as regular email.
+It is a relatively simple matter for an attacker
+to forge a CTM delta to replace or precede the
+legitimate one and insert malicious code into your
+source tree.
+If the legitimate delta is somehow prevented from
+arriving, this will go unnoticed until a later
+delta attempts to touch the same file, at which
+point the MD5 checksum will fail.
+.Pp
+To remedy this insecurity, CTM delta pieces generated by
+FreeBSD.org are cryptographically signed in a
+format compatible with the GNU Privacy Guard
+utility, available in /usr/ports/security/gpg, and
+the Pretty Good Privacy v5 utility,
+/usr/ports/security/pgp5.
+The relevant public key can be obtained by
+fingering ctm@FreeBSD.org.
+.Pp
+CTM deltas which are thus signed cannot be
+undetectably altered by an attacker.
+Therefore it is recommended that you make use of
+GPG or PGP5 to verify the signatures if you
+receive your CTM deltas via email.
+.Sh DIAGNOSTICS
+In normal operation,
+.Nm ctm_smail
+will report messages like:
+.Bd -literal -offset indent
+ctm_smail: src-cur.0250.gz 1/2 sent to src-guys
+.Ed
+.Pp
+or, if queueing,
+.Bd -literal -offset indent
+ctm_smail: src-cur.0250.gz 1/2 queued for src-guys
+.Ed
+.Pp
+The
+.Nm ctm_dequeue
+utility will report messages like:
+.Bd -literal -offset indent
+ctm_dequeue: src-cur.0250.gz 1/2 sent
+.Ed
+.Pp
+The
+.Nm ctm_rmail
+utility will report messages like:
+.Bd -literal -offset indent
+ctm_rmail: src-cur.0250.gz 1/2 stored
+ctm_rmail: src-cur.0250.gz 2/2 stored
+ctm_rmail: src-cur.0250.gz complete
+.Ed
+.Pp
+If any of the input files do not contain a valid delta piece,
+.Nm ctm_rmail
+will report:
+.Bd -literal -offset indent
+ctm_rmail: message contains no delta
+.Ed
+.Pp
+and return an exit status of 1.
+You can use this to redirect wayward messages
+back into your real mailbox if your mail filter goes wonky.
+.Pp
+These messages go to
+.Em stderr
+or to the log file.
+Messages from
+.Xr ctm 1
+turn up here too.
+Error messages should be self explanatory.
+.Sh SEE ALSO
+.Xr ctm 1 ,
+.Xr ctm 5
+.\" .Sh HISTORY
+.Sh AUTHORS
+.An Stephen McKay Aq mckay@FreeBSD.org
diff --git a/usr.sbin/ctm/ctm_rmail/ctm_rmail.c b/usr.sbin/ctm/ctm_rmail/ctm_rmail.c
new file mode 100644
index 0000000..a46a58aa
--- /dev/null
+++ b/usr.sbin/ctm/ctm_rmail/ctm_rmail.c
@@ -0,0 +1,672 @@
+/*
+ * Accept one (or more) ASCII encoded chunks that together make a compressed
+ * CTM delta. Decode them and reconstruct the deltas. Any completed
+ * deltas may be passed to ctm for unpacking.
+ *
+ * Author: Stephen McKay
+ *
+ * NOTICE: This is free software. I hope you get some use from this program.
+ * In return you should think about all the nice people who give away software.
+ * Maybe you should write some free software too.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <limits.h>
+#include "error.h"
+#include "options.h"
+
+#define CTM_STATUS ".ctm_status"
+
+char *piece_dir = NULL; /* Where to store pieces of deltas. */
+char *delta_dir = NULL; /* Where to store completed deltas. */
+char *base_dir = NULL; /* The tree to apply deltas to. */
+int delete_after = 0; /* Delete deltas after ctm applies them. */
+int apply_verbose = 0; /* Run with '-v' */
+int set_time = 0; /* Set the time of the files that is changed. */
+int mask = 0; /* The current umask */
+
+void apply_complete(void);
+int read_piece(char *input_file);
+int combine_if_complete(char *delta, int pce, int npieces);
+int combine(char *delta, int npieces, char *dname, char *pname, char *tname);
+int decode_line(char *line, char *out_buf);
+int lock_file(char *name);
+
+/*
+ * If given a '-p' flag, read encoded delta pieces from stdin or file
+ * arguments, decode them and assemble any completed deltas. If given
+ * a '-b' flag, pass any completed deltas to 'ctm' for application to
+ * the source tree. The '-d' flag is mandatory, but either of '-p' or
+ * '-b' can be omitted. If given the '-l' flag, notes and errors will
+ * be timestamped and written to the given file.
+ *
+ * Exit status is 0 for success or 1 for indigestible input. That is,
+ * 0 means the encode input pieces were decoded and stored, and 1 means
+ * some input was discarded. If a delta fails to apply, this won't be
+ * reflected in the exit status. In this case, the delta is left in
+ * 'deltadir'.
+ */
+int
+main(int argc, char **argv)
+ {
+ char *log_file = NULL;
+ int status = 0;
+ int fork_ctm = 0;
+
+ mask = umask(0);
+ umask(mask);
+
+ err_prog_name(argv[0]);
+
+ OPTIONS("[-Dfuv] [-p piecedir] [-d deltadir] [-b basedir] [-l log] [file ...]")
+ FLAG('D', delete_after)
+ FLAG('f', fork_ctm)
+ FLAG('u', set_time)
+ FLAG('v', apply_verbose)
+ STRING('p', piece_dir)
+ STRING('d', delta_dir)
+ STRING('b', base_dir)
+ STRING('l', log_file)
+ ENDOPTS
+
+ if (delta_dir == NULL)
+ usage();
+
+ if (piece_dir == NULL && (base_dir == NULL || argc > 1))
+ usage();
+
+ if (log_file != NULL)
+ err_set_log(log_file);
+
+ /*
+ * Digest each file in turn, or just stdin if no files were given.
+ */
+ if (argc <= 1)
+ {
+ if (piece_dir != NULL)
+ status = read_piece(NULL);
+ }
+ else
+ {
+ while (*++argv != NULL)
+ status |= read_piece(*argv);
+ }
+
+ /*
+ * Maybe it's time to look for and apply completed deltas with ctm.
+ *
+ * Shall we report back to sendmail immediately, and let a child do
+ * the work? Sendmail will be waiting for us to complete, delaying
+ * other mail, and possibly some intermediate process (like MH slocal)
+ * will terminate us if we take too long!
+ *
+ * If fork() fails, it's unlikely we'll be able to run ctm, so give up.
+ * Also, the child exit status is unimportant.
+ */
+ if (base_dir != NULL)
+ if (!fork_ctm || fork() == 0)
+ apply_complete();
+
+ return status;
+ }
+
+
+/*
+ * Construct the file name of a piece of a delta.
+ */
+#define mk_piece_name(fn,d,p,n) \
+ sprintf((fn), "%s/%s+%03d-%03d", piece_dir, (d), (p), (n))
+
+/*
+ * Construct the file name of an assembled delta.
+ */
+#define mk_delta_name(fn,d) \
+ sprintf((fn), "%s/%s", delta_dir, (d))
+
+/*
+ * If the next required delta is now present, let ctm lunch on it and any
+ * contiguous deltas.
+ */
+void
+apply_complete()
+ {
+ int i, dn;
+ int lfd;
+ FILE *fp, *ctm;
+ struct stat sb;
+ char class[20];
+ char delta[30];
+ char junk[2];
+ char fname[PATH_MAX];
+ char here[PATH_MAX];
+ char buf[PATH_MAX*2];
+
+ /*
+ * Grab a lock on the ctm mutex file so that we can be sure we are
+ * working alone, not fighting another ctm_rmail!
+ */
+ strcpy(fname, delta_dir);
+ strcat(fname, "/.mutex_apply");
+ if ((lfd = lock_file(fname)) < 0)
+ return;
+
+ /*
+ * Find out which delta ctm needs next.
+ */
+ sprintf(fname, "%s/%s", base_dir, CTM_STATUS);
+ if ((fp = fopen(fname, "r")) == NULL)
+ {
+ close(lfd);
+ return;
+ }
+
+ i = fscanf(fp, "%19s %d %c", class, &dn, junk);
+ fclose(fp);
+ if (i != 2)
+ {
+ close(lfd);
+ return;
+ }
+
+ /*
+ * We might need to convert the delta filename to an absolute pathname.
+ */
+ here[0] = '\0';
+ if (delta_dir[0] != '/')
+ {
+ getcwd(here, sizeof(here)-1);
+ i = strlen(here) - 1;
+ if (i >= 0 && here[i] != '/')
+ {
+ here[++i] = '/';
+ here[++i] = '\0';
+ }
+ }
+
+ /*
+ * Keep applying deltas until we run out or something bad happens.
+ */
+ for (;;)
+ {
+ sprintf(delta, "%s.%04d.gz", class, ++dn);
+ mk_delta_name(fname, delta);
+
+ if (stat(fname, &sb) < 0)
+ break;
+
+ sprintf(buf, "(cd %s && ctm %s%s%s%s) 2>&1", base_dir,
+ set_time ? "-u " : "",
+ apply_verbose ? "-v " : "", here, fname);
+ if ((ctm = popen(buf, "r")) == NULL)
+ {
+ err("ctm failed to apply %s", delta);
+ break;
+ }
+
+ while (fgets(buf, sizeof(buf), ctm) != NULL)
+ {
+ i = strlen(buf) - 1;
+ if (i >= 0 && buf[i] == '\n')
+ buf[i] = '\0';
+ err("ctm: %s", buf);
+ }
+
+ if (pclose(ctm) != 0)
+ {
+ err("ctm failed to apply %s", delta);
+ break;
+ }
+
+ if (delete_after)
+ unlink(fname);
+
+ err("%s applied%s", delta, delete_after ? " and deleted" : "");
+ }
+
+ /*
+ * Closing the lock file clears the lock.
+ */
+ close(lfd);
+ }
+
+
+/*
+ * This cheap plastic checksum effectively rotates our checksum-so-far
+ * left one, then adds the character. We only want 16 bits of it, and
+ * don't care what happens to the rest. It ain't much, but it's small.
+ */
+#define add_ck(sum,x) \
+ ((sum) += ((x)&0xff) + (sum) + (((sum)&0x8000) ? 1 : 0))
+
+
+/*
+ * Decode the data between BEGIN and END, and stash it in the staging area.
+ * Multiple pieces can be present in a single file, bracketed by BEGIN/END.
+ * If we have all pieces of a delta, combine them. Returns 0 on success,
+ * and 1 for any sort of failure.
+ */
+int
+read_piece(char *input_file)
+ {
+ int status = 0;
+ FILE *ifp, *ofp = 0;
+ int decoding = 0;
+ int got_one = 0;
+ int line_no = 0;
+ int i, n;
+ int pce, npieces;
+ unsigned claimed_cksum;
+ unsigned short cksum = 0;
+ char out_buf[200];
+ char line[200];
+ char delta[30];
+ char pname[PATH_MAX];
+ char tname[PATH_MAX];
+ char junk[2];
+
+ ifp = stdin;
+ if (input_file != NULL && (ifp = fopen(input_file, "r")) == NULL)
+ {
+ err("cannot open '%s' for reading", input_file);
+ return 1;
+ }
+
+ while (fgets(line, sizeof(line), ifp) != NULL)
+ {
+ line_no++;
+
+ /*
+ * Remove all trailing white space.
+ */
+ i = strlen(line) - 1;
+ while (i > 0 && isspace(line[i]))
+ line[i--] = '\0';
+
+ /*
+ * Look for the beginning of an encoded piece.
+ */
+ if (!decoding)
+ {
+ char *s;
+ int fd = -1;
+
+ if (sscanf(line, "CTM_MAIL BEGIN %29s %d %d %c",
+ delta, &pce, &npieces, junk) != 3)
+ continue;
+
+ while ((s = strchr(delta, '/')) != NULL)
+ *s = '_';
+
+ got_one++;
+ strcpy(tname, piece_dir);
+ strcat(tname, "/p.XXXXXXXXXX");
+ if ((fd = mkstemp(tname)) == -1 ||
+ (ofp = fdopen(fd, "w")) == NULL)
+ {
+ if (fd != -1) {
+ err("cannot open '%s' for writing", tname);
+ close(fd);
+ }
+ else
+ err("*mkstemp: '%s'", tname);
+ status++;
+ continue;
+ }
+
+ cksum = 0xffff;
+ decoding++;
+ continue;
+ }
+
+ /*
+ * We are decoding. Stop if we see the end flag.
+ */
+ if (sscanf(line, "CTM_MAIL END %d %c", &claimed_cksum, junk) == 1)
+ {
+ int e;
+
+ decoding = 0;
+
+ fflush(ofp);
+ e = ferror(ofp);
+ fclose(ofp);
+
+ if (e)
+ err("error writing %s", tname);
+
+ if (cksum != claimed_cksum)
+ err("checksum: read %d, calculated %d", claimed_cksum, cksum);
+
+ if (e || cksum != claimed_cksum)
+ {
+ err("%s %d/%d discarded", delta, pce, npieces);
+ unlink(tname);
+ status++;
+ continue;
+ }
+
+ mk_piece_name(pname, delta, pce, npieces);
+ if (rename(tname, pname) < 0)
+ {
+ err("*rename: '%s' to '%s'", tname, pname);
+ err("%s %d/%d lost!", delta, pce, npieces);
+ unlink(tname);
+ status++;
+ continue;
+ }
+
+ err("%s %d/%d stored", delta, pce, npieces);
+
+ if (!combine_if_complete(delta, pce, npieces))
+ status++;
+ continue;
+ }
+
+ /*
+ * Must be a line of encoded data. Decode it, sum it, and save it.
+ */
+ n = decode_line(line, out_buf);
+ if (n <= 0)
+ {
+ err("line %d: illegal character: '%c'", line_no, line[-n]);
+ err("%s %d/%d discarded", delta, pce, npieces);
+
+ fclose(ofp);
+ unlink(tname);
+
+ status++;
+ decoding = 0;
+ continue;
+ }
+
+ for (i = 0; i < n; i++)
+ add_ck(cksum, out_buf[i]);
+
+ fwrite(out_buf, sizeof(char), n, ofp);
+ }
+
+ if (decoding)
+ {
+ err("truncated file");
+ err("%s %d/%d discarded", delta, pce, npieces);
+
+ fclose(ofp);
+ unlink(tname);
+
+ status++;
+ }
+
+ if (ferror(ifp))
+ {
+ err("error reading %s", input_file == NULL ? "stdin" : input_file);
+ status++;
+ }
+
+ if (input_file != NULL)
+ fclose(ifp);
+
+ if (!got_one)
+ {
+ err("message contains no delta");
+ status++;
+ }
+
+ return (status != 0);
+ }
+
+
+/*
+ * Put the pieces together to form a delta, if they are all present.
+ * Returns 1 on success (even if we didn't do anything), and 0 on failure.
+ */
+int
+combine_if_complete(char *delta, int pce, int npieces)
+ {
+ int i, e;
+ int lfd;
+ struct stat sb;
+ char pname[PATH_MAX];
+ char dname[PATH_MAX];
+ char tname[PATH_MAX];
+
+ /*
+ * We can probably just rename() it into place if it is a small delta.
+ */
+ if (npieces == 1)
+ {
+ mk_delta_name(dname, delta);
+ mk_piece_name(pname, delta, 1, 1);
+ if (rename(pname, dname) == 0)
+ {
+ chmod(dname, 0666 & ~mask);
+ err("%s complete", delta);
+ return 1;
+ }
+ }
+
+ /*
+ * Grab a lock on the reassembly mutex file so that we can be sure we are
+ * working alone, not fighting another ctm_rmail!
+ */
+ strcpy(tname, delta_dir);
+ strcat(tname, "/.mutex_build");
+ if ((lfd = lock_file(tname)) < 0)
+ return 0;
+
+ /*
+ * Are all of the pieces present? Of course the current one is,
+ * unless all pieces are missing because another ctm_rmail has
+ * processed them already.
+ */
+ for (i = 1; i <= npieces; i++)
+ {
+ if (i == pce)
+ continue;
+ mk_piece_name(pname, delta, i, npieces);
+ if (stat(pname, &sb) < 0)
+ {
+ close(lfd);
+ return 1;
+ }
+ }
+
+ /*
+ * Stick them together. Let combine() use our file name buffers, since
+ * we're such good buddies. :-)
+ */
+ e = combine(delta, npieces, dname, pname, tname);
+ close(lfd);
+ return e;
+ }
+
+
+/*
+ * Put the pieces together to form a delta.
+ * Returns 1 on success, and 0 on failure.
+ * Note: dname, pname, and tname are room for some file names that just
+ * happened to by lying around in the calling routine. Waste not, want not!
+ */
+int
+combine(char *delta, int npieces, char *dname, char *pname, char *tname)
+ {
+ FILE *dfp, *pfp;
+ int i, n, e;
+ char buf[BUFSIZ];
+ int fd = -1;
+
+ strcpy(tname, delta_dir);
+ strcat(tname, "/d.XXXXXXXXXX");
+ if ((fd = mkstemp(tname)) == -1 ||
+ (dfp = fdopen(fd, "w")) == NULL)
+ {
+ if (fd != -1) {
+ close(fd);
+ err("cannot open '%s' for writing", tname);
+ }
+ else
+ err("*mkstemp: '%s'", tname);
+ return 0;
+ }
+
+ /*
+ * Reconstruct the delta by reading each piece in order.
+ */
+ for (i = 1; i <= npieces; i++)
+ {
+ mk_piece_name(pname, delta, i, npieces);
+ if ((pfp = fopen(pname, "r")) == NULL)
+ {
+ err("cannot open '%s' for reading", pname);
+ fclose(dfp);
+ unlink(tname);
+ return 0;
+ }
+ while ((n = fread(buf, sizeof(char), sizeof(buf), pfp)) != 0)
+ fwrite(buf, sizeof(char), n, dfp);
+ e = ferror(pfp);
+ fclose(pfp);
+ if (e)
+ {
+ err("error reading '%s'", pname);
+ fclose(dfp);
+ unlink(tname);
+ return 0;
+ }
+ }
+ fflush(dfp);
+ e = ferror(dfp);
+ fclose(dfp);
+ if (e)
+ {
+ err("error writing '%s'", tname);
+ unlink(tname);
+ return 0;
+ }
+
+ mk_delta_name(dname, delta);
+ if (rename(tname, dname) < 0)
+ {
+ err("*rename: '%s' to '%s'", tname, dname);
+ unlink(tname);
+ return 0;
+ }
+ chmod(dname, 0666 & ~mask);
+
+ /*
+ * Throw the pieces away.
+ */
+ for (i = 1; i <= npieces; i++)
+ {
+ mk_piece_name(pname, delta, i, npieces);
+ if (unlink(pname) < 0)
+ err("*unlink: '%s'", pname);
+ }
+
+ err("%s complete", delta);
+ return 1;
+ }
+
+
+/*
+ * MIME BASE64 decode table.
+ */
+static unsigned char from_b64[0x80] =
+ {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
+ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
+ 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+ 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
+ 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
+ };
+
+
+/*
+ * Decode a line of ASCII into binary. Returns the number of bytes in
+ * the output buffer, or < 0 on indigestable input. Error output is
+ * the negative of the index of the inedible character.
+ */
+int
+decode_line(char *line, char *out_buf)
+ {
+ unsigned char *ip = (unsigned char *)line;
+ unsigned char *op = (unsigned char *)out_buf;
+ unsigned long bits;
+ unsigned x;
+
+ for (;;)
+ {
+ if (*ip >= 0x80 || (x = from_b64[*ip]) >= 0x40)
+ break;
+ bits = x << 18;
+ ip++;
+ if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
+ {
+ bits |= x << 12;
+ *op++ = bits >> 16;
+ ip++;
+ if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
+ {
+ bits |= x << 6;
+ *op++ = bits >> 8;
+ ip++;
+ if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
+ {
+ bits |= x;
+ *op++ = bits;
+ ip++;
+ }
+ }
+ }
+ }
+
+ if (*ip == '\0' || *ip == '\n')
+ return op - (unsigned char *)out_buf;
+ else
+ return -(ip - (unsigned char *)line);
+ }
+
+
+/*
+ * Create and lock the given file.
+ *
+ * Clearing the lock is as simple as closing the file descriptor we return.
+ */
+int
+lock_file(char *name)
+ {
+ int lfd;
+
+ if ((lfd = open(name, O_WRONLY|O_CREAT, 0600)) < 0)
+ {
+ err("*open: '%s'", name);
+ return -1;
+ }
+ if (flock(lfd, LOCK_EX) < 0)
+ {
+ close(lfd);
+ err("*flock: '%s'", name);
+ return -1;
+ }
+ return lfd;
+ }
diff --git a/usr.sbin/ctm/ctm_rmail/error.c b/usr.sbin/ctm/ctm_rmail/error.c
new file mode 100644
index 0000000..56d3dc6
--- /dev/null
+++ b/usr.sbin/ctm/ctm_rmail/error.c
@@ -0,0 +1,102 @@
+/*
+ * Routines for logging error messages or other informative messages.
+ *
+ * Log messages can easily contain the program name, a time stamp, system
+ * error messages, and arbitrary printf-style strings, and can be directed
+ * to stderr or a log file.
+ *
+ * Author: Stephen McKay
+ *
+ * NOTICE: This is free software. I hope you get some use from this program.
+ * In return you should think about all the nice people who give away software.
+ * Maybe you should write some free software too.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+#include <errno.h>
+#include "error.h"
+
+static FILE *error_fp = NULL;
+static char *prog = NULL;
+
+
+/*
+ * Log errors to the given file.
+ */
+void
+err_set_log(char *log_file)
+ {
+ FILE *fp;
+
+ if ((fp = fopen(log_file, "a")) == NULL)
+ err("cannot log to '%s'", log_file);
+ else
+ error_fp = fp;
+ }
+
+
+/*
+ * Set the error prefix if not logging to a file.
+ */
+void
+err_prog_name(char *name)
+ {
+ if ((prog = strrchr(name, '/')) == NULL)
+ prog = name;
+ else
+ prog++;
+ }
+
+
+/*
+ * Log an error.
+ *
+ * A leading '*' in the message format means we want the system errno
+ * decoded and appended.
+ */
+void
+err(const char *fmt, ...)
+ {
+ va_list ap;
+ time_t now;
+ struct tm *tm;
+ FILE *fp;
+ int x = errno;
+ int want_errno;
+
+ if ((fp = error_fp) == NULL)
+ {
+ fp = stderr;
+ if (prog != NULL)
+ fprintf(fp, "%s: ", prog);
+ }
+ else
+ {
+ time(&now);
+ tm = localtime(&now);
+ fprintf(fp, "%04d-%02d-%02d %02d:%02d ", tm->tm_year+1900,
+ tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min);
+ }
+
+ want_errno = 0;
+ if (*fmt == '*')
+ want_errno++, fmt++;
+
+ va_start(ap, fmt);
+ vfprintf(fp, fmt, ap);
+ va_end(ap);
+
+ if (want_errno)
+ fprintf(fp, ": %s", strerror(x));
+
+ fprintf(fp, "\n");
+ fflush(fp);
+ }
diff --git a/usr.sbin/ctm/ctm_rmail/error.h b/usr.sbin/ctm/ctm_rmail/error.h
new file mode 100644
index 0000000..c631b67
--- /dev/null
+++ b/usr.sbin/ctm/ctm_rmail/error.h
@@ -0,0 +1,5 @@
+/* $FreeBSD$ */
+
+extern void err_set_log(char *log_file);
+extern void err_prog_name(char *name);
+extern void err(const char *fmt, ...) __printflike(1, 2);
diff --git a/usr.sbin/ctm/ctm_rmail/options.h b/usr.sbin/ctm/ctm_rmail/options.h
new file mode 100644
index 0000000..86c1247
--- /dev/null
+++ b/usr.sbin/ctm/ctm_rmail/options.h
@@ -0,0 +1,139 @@
+/*
+ * Macros for processing command arguments.
+ *
+ * Conforms closely to the command option requirements of intro(1) in System V
+ * and intro(C) in Xenix.
+ *
+ * A command consists of: cmdname [ options ] [ cmdarguments ]
+ *
+ * Options consist of a leading dash '-' and a flag letter. An argument may
+ * follow optionally preceded by white space.
+ * Options without arguments may be grouped behind a single dash.
+ * A dash on its own is interpreted as the end of the options and is retained
+ * as a command argument.
+ * A double dash '--' is interpreted as the end of the options and is discarded.
+ *
+ * For example:
+ * zap -xz -f flame -q34 -- -x
+ *
+ * where zap.c contains the following in main():
+ *
+ * OPTIONS("[-xz] [-q queue-id] [-f dump-file] user")
+ * FLAG('x', xecute)
+ * FLAG('z', zot)
+ * STRING('f', file)
+ * fp = fopen(file, "w");
+ * NUMBER('q', queue)
+ * ENDOPTS
+ *
+ * Results in:
+ * xecute = 1
+ * zot = 1
+ * file = "flame"
+ * fp = fopen("flame", "w")
+ * queue = 34
+ * argc = 2
+ * argv[0] = "zap"
+ * argv[1] = "-x"
+ *
+ * Should the user enter unknown flags or leave out required arguments,
+ * the message:
+ *
+ * Usage: zap [-xz] [-q queue-id] [-f dump-file] user
+ *
+ * will be printed. This message can be printed by calling pusage(), or
+ * usage(). usage() will also cause program termination with exit code 1.
+ *
+ * Author: Stephen McKay, February 1991
+ *
+ * Based on recollection of the original options.h produced at the University
+ * of Queensland by Ross Patterson (and possibly others).
+ *
+ * $FreeBSD$
+ */
+
+static char *O_usage;
+static char *O_name;
+extern long atol();
+
+void
+pusage()
+ {
+ /*
+ * Avoid gratuitously loading stdio.
+ */
+ write(STDERR_FILENO, "usage: ", 7);
+ write(STDERR_FILENO, O_name, strlen(O_name));
+ write(STDERR_FILENO, " ", 1);
+ write(STDERR_FILENO, O_usage, strlen(O_usage));
+ write(STDERR_FILENO, "\n", 1);
+ }
+
+#define usage() (pusage(), exit(1))
+
+#define OPTIONS(usage_msg) \
+ { \
+ char O_cont; \
+ O_usage = (usage_msg); \
+ O_name = argv[0]; \
+ while (*++argv && **argv == '-') \
+ { \
+ if ((*argv)[1] == '\0') \
+ break; \
+ argc--; \
+ if ((*argv)[1] == '-' && (*argv)[2] == '\0') \
+ { \
+ argv++; \
+ break; \
+ } \
+ O_cont = 1; \
+ while (O_cont) \
+ switch (*++*argv) \
+ { \
+ default: \
+ case '-': \
+ usage(); \
+ case '\0': \
+ O_cont = 0;
+
+#define FLAG(x,flag) \
+ break; \
+ case (x): \
+ (flag) = 1;
+
+#define CHAR(x,ch) \
+ break; \
+ case (x): \
+ O_cont = 0; \
+ if (*++*argv == '\0' && (--argc, *++argv == 0)) \
+ usage(); \
+ (ch) = **argv;
+
+#define NUMBER(x,n) \
+ break; \
+ case (x): \
+ O_cont = 0; \
+ if (*++*argv == '\0' && (--argc, *++argv == 0)) \
+ usage(); \
+ (n) = atol(*argv);
+
+#define STRING(x,str) \
+ break; \
+ case (x): \
+ O_cont = 0; \
+ if (*++*argv == '\0' && (--argc, *++argv == 0)) \
+ usage(); \
+ (str) = *argv;
+
+#define SUFFIX(x,str) \
+ break; \
+ case (x): \
+ (str) = ++*argv; \
+ O_cont = 0;
+
+#define ENDOPTS \
+ break; \
+ } \
+ } \
+ *--argv = O_name; \
+ }
diff --git a/usr.sbin/ctm/ctm_smail/Makefile b/usr.sbin/ctm/ctm_smail/Makefile
new file mode 100644
index 0000000..980e9bf
--- /dev/null
+++ b/usr.sbin/ctm/ctm_smail/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../ctm_rmail
+
+PROG= ctm_smail
+NO_MAN=
+SRCS= ctm_smail.c error.c
+
+CFLAGS+= -I${.CURDIR}/../ctm_rmail
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ctm/ctm_smail/ctm_smail.c b/usr.sbin/ctm/ctm_smail/ctm_smail.c
new file mode 100644
index 0000000..a0dc03c
--- /dev/null
+++ b/usr.sbin/ctm/ctm_smail/ctm_smail.c
@@ -0,0 +1,497 @@
+/*
+ * Send a compressed CTM delta to a recipient mailing list by encoding it
+ * in safe ASCII characters, in mailer-friendly chunks, and passing them
+ * to sendmail. Optionally, the chunks can be queued to be sent later by
+ * ctm_dequeue in controlled bursts. The encoding is almost the same as
+ * MIME BASE64, and is protected by a simple checksum.
+ *
+ * Author: Stephen McKay
+ *
+ * NOTICE: This is free software. I hope you get some use from this program.
+ * In return you should think about all the nice people who give away software.
+ * Maybe you should write some free software too.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <paths.h>
+#include <limits.h>
+#include "error.h"
+#include "options.h"
+
+#define DEF_MAX_MSG 64000 /* Default maximum mail msg minus headers. */
+
+#define LINE_LENGTH 76 /* Chars per encoded line. Divisible by 4. */
+
+int chop_and_send_or_queue(FILE *dfp, char *delta, off_t ctm_size,
+ long max_msg_size, char *mail_alias, char *queue_dir);
+int chop_and_send(FILE *dfp, char *delta, long msg_size, int npieces,
+ char *mail_alias);
+int chop_and_queue(FILE *dfp, char *delta, long msg_size, int npieces,
+ char *mail_alias, char *queue_dir);
+void clean_up_queue(char *queue_dir);
+int encode_body(FILE *sm_fp, FILE *delta_fp, long msg_size, unsigned *sum);
+void write_header(FILE *sfp, char *mail_alias, char *delta, int pce,
+ int npieces);
+void write_trailer(FILE *sfp, unsigned sum);
+int apologise(char *delta, off_t ctm_size, long max_ctm_size,
+ char *mail_alias, char *queue_dir);
+FILE *open_sendmail(void);
+int close_sendmail(FILE *fp);
+
+int
+main(int argc, char **argv)
+ {
+ int status = 0;
+ char *delta_file;
+ char *mail_alias;
+ long max_msg_size = DEF_MAX_MSG;
+ long max_ctm_size = 0;
+ char *log_file = NULL;
+ char *queue_dir = NULL;
+ char *delta;
+ FILE *dfp;
+ struct stat sb;
+
+ err_prog_name(argv[0]);
+
+ OPTIONS("[-l log] [-m maxmsgsize] [-c maxctmsize] [-q queuedir] ctm-delta mail-alias")
+ NUMBER('m', max_msg_size)
+ NUMBER('c', max_ctm_size)
+ STRING('l', log_file)
+ STRING('q', queue_dir)
+ ENDOPTS
+
+ if (argc != 3)
+ usage();
+
+ if (log_file != NULL)
+ err_set_log(log_file);
+
+ delta_file = argv[1];
+ mail_alias = argv[2];
+
+ if ((delta = strrchr(delta_file, '/')) == NULL)
+ delta = delta_file;
+ else
+ delta++;
+
+ if ((dfp = fopen(delta_file, "r")) == NULL || fstat(fileno(dfp), &sb) < 0)
+ {
+ err("*%s", delta_file);
+ exit(1);
+ }
+
+ if (max_ctm_size != 0 && sb.st_size > max_ctm_size)
+ status = apologise(delta, sb.st_size, max_ctm_size, mail_alias,
+ queue_dir);
+ else
+ status = chop_and_send_or_queue(dfp, delta, sb.st_size, max_msg_size,
+ mail_alias, queue_dir);
+
+ fclose(dfp);
+
+ return status;
+ }
+
+
+/*
+ * Carve our CTM delta into pieces, encode them, and send or queue them.
+ * Returns 0 on success, and 1 on failure.
+ */
+int
+chop_and_send_or_queue(FILE *dfp, char *delta, off_t ctm_size,
+ long max_msg_size, char *mail_alias, char *queue_dir)
+ {
+ int npieces;
+ long msg_size;
+ long exp_size;
+ int status;
+
+#undef howmany
+#define howmany(x,y) (((x)+((y)-1)) / (y))
+
+ /*
+ * Work out how many pieces we need, bearing in mind that each piece
+ * grows by 4/3 when encoded. We count the newlines too, but ignore
+ * all mail headers and piece headers. They are a "small" (almost
+ * constant) per message overhead that we make the user worry about. :-)
+ */
+ exp_size = ctm_size * 4 / 3;
+ exp_size += howmany(exp_size, LINE_LENGTH);
+ npieces = howmany(exp_size, max_msg_size);
+ msg_size = howmany(ctm_size, npieces);
+
+#undef howmany
+
+ if (queue_dir == NULL)
+ status = chop_and_send(dfp, delta, msg_size, npieces, mail_alias);
+ else
+ {
+ status = chop_and_queue(dfp, delta, msg_size, npieces, mail_alias,
+ queue_dir);
+ if (status)
+ clean_up_queue(queue_dir);
+ }
+
+ return status;
+ }
+
+
+/*
+ * Carve our CTM delta into pieces, encode them, and send them.
+ * Returns 0 on success, and 1 on failure.
+ */
+int
+chop_and_send(FILE *dfp, char *delta, long msg_size, int npieces,
+ char *mail_alias)
+ {
+ int pce;
+ FILE *sfp;
+ unsigned sum;
+
+ /*
+ * Send each chunk directly to sendmail as it is generated.
+ * No temporary files necessary. If things turn ugly, we just
+ * have to live with the fact the we have sent only part of
+ * the delta.
+ */
+ for (pce = 1; pce <= npieces; pce++)
+ {
+ int read_error;
+
+ if ((sfp = open_sendmail()) == NULL)
+ return 1;
+
+ write_header(sfp, mail_alias, delta, pce, npieces);
+ read_error = encode_body(sfp, dfp, msg_size, &sum);
+ if (!read_error)
+ write_trailer(sfp, sum);
+
+ if (!close_sendmail(sfp) || read_error)
+ return 1;
+
+ err("%s %d/%d sent to %s", delta, pce, npieces, mail_alias);
+ }
+
+ return 0;
+ }
+
+
+/*
+ * Construct the tmp queue file name of a delta piece.
+ */
+#define mk_tmp_name(fn,qd,p) \
+ sprintf((fn), "%s/.%08ld.%03d", (qd), (long)getpid(), (p))
+
+/*
+ * Construct the final queue file name of a delta piece.
+ */
+#define mk_queue_name(fn,qd,d,p,n) \
+ sprintf((fn), "%s/%s+%03d-%03d", (qd), (d), (p), (n))
+
+/*
+ * Carve our CTM delta into pieces, encode them, and queue them.
+ * Returns 0 on success, and 1 on failure.
+ */
+int
+chop_and_queue(FILE *dfp, char *delta, long msg_size, int npieces,
+ char *mail_alias, char *queue_dir)
+ {
+ int pce;
+ FILE *qfp;
+ unsigned sum;
+ char tname[PATH_MAX];
+ char qname[PATH_MAX];
+
+ /*
+ * Store each piece in the queue directory, but under temporary names,
+ * so that they can be deleted without unpleasant consequences if
+ * anything goes wrong. We could easily fill up a disk, for example.
+ */
+ for (pce = 1; pce <= npieces; pce++)
+ {
+ int write_error;
+
+ mk_tmp_name(tname, queue_dir, pce);
+ if ((qfp = fopen(tname, "w")) == NULL)
+ {
+ err("cannot open '%s' for writing", tname);
+ return 1;
+ }
+
+ write_header(qfp, mail_alias, delta, pce, npieces);
+ if (encode_body(qfp, dfp, msg_size, &sum))
+ return 1;
+ write_trailer(qfp, sum);
+
+ fflush(qfp);
+ write_error = ferror(qfp);
+ fclose(qfp);
+ if (write_error)
+ {
+ err("error writing '%s'", tname);
+ return 1;
+ }
+
+ /*
+ * Give the warm success message now, instead of all in a rush
+ * during the rename phase.
+ */
+ err("%s %d/%d queued for %s", delta, pce, npieces, mail_alias);
+ }
+
+ /*
+ * Rename the pieces into place. If an error occurs now, we are
+ * stuffed, but there is no neat way to back out. rename() should
+ * only fail now under extreme circumstances.
+ */
+ for (pce = 1; pce <= npieces; pce++)
+ {
+ mk_tmp_name(tname, queue_dir, pce);
+ mk_queue_name(qname, queue_dir, delta, pce, npieces);
+ if (rename(tname, qname) < 0)
+ {
+ err("*rename: '%s' to '%s'", tname, qname);
+ unlink(tname);
+ }
+ }
+
+ return 0;
+ }
+
+
+/*
+ * There may be temporary files cluttering up the queue directory.
+ */
+void
+clean_up_queue(char *queue_dir)
+ {
+ int pce;
+ char tname[PATH_MAX];
+
+ err("discarding queued delta pieces");
+ for (pce = 1; ; pce++)
+ {
+ mk_tmp_name(tname, queue_dir, pce);
+ if (unlink(tname) < 0)
+ break;
+ }
+ }
+
+
+/*
+ * MIME BASE64 encode table.
+ */
+static char to_b64[0x40] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/*
+ * This cheap plastic checksum effectively rotates our checksum-so-far
+ * left one, then adds the character. We only want 16 bits of it, and
+ * don't care what happens to the rest. It ain't much, but it's small.
+ */
+#define add_ck(sum,x) \
+ ((sum) += ((x)&0xff) + (sum) + (((sum)&0x8000) ? 1 : 0))
+
+/*
+ * Encode the body. Use an encoding almost the same as MIME BASE64.
+ *
+ * Characters are read from delta_fp and encoded characters are written
+ * to sm_fp. At most 'msg_size' characters should be read from delta_fp.
+ *
+ * The body consists of lines of up to LINE_LENGTH characters. Each group
+ * of 4 characters encodes 3 input characters. Each output character encodes
+ * 6 bits. Thus 64 different characters are needed in this representation.
+ */
+int
+encode_body(FILE *sm_fp, FILE *delta_fp, long msg_size, unsigned *sum)
+ {
+ unsigned short cksum = 0xffff;
+ unsigned char *ip;
+ char *op;
+ int want, n, i;
+ unsigned char inbuf[LINE_LENGTH*3/4];
+ char outbuf[LINE_LENGTH+1];
+
+ /*
+ * Round up to the nearest line boundary, for the tiniest of gains,
+ * and lots of neatness. :-)
+ */
+ msg_size += (LINE_LENGTH*3/4) - 1;
+ msg_size -= msg_size % (LINE_LENGTH*3/4);
+
+ while (msg_size > 0)
+ {
+ want = (msg_size < sizeof(inbuf)) ? msg_size : sizeof(inbuf);
+ if ((n = fread(inbuf, sizeof(char), want, delta_fp)) == 0)
+ break;
+ msg_size -= n;
+
+ for (i = 0; i < n; i++)
+ add_ck(cksum, inbuf[i]);
+
+ /*
+ * Produce a line of encoded data. Every line length will be a
+ * multiple of 4, except for, perhaps, the last line.
+ */
+ ip = inbuf;
+ op = outbuf;
+ while (n >= 3)
+ {
+ *op++ = to_b64[ip[0] >> 2];
+ *op++ = to_b64[(ip[0] << 4 & 0x3f) | ip[1] >> 4];
+ *op++ = to_b64[(ip[1] << 2 & 0x3f) | ip[2] >> 6];
+ *op++ = to_b64[ip[2] & 0x3f];
+ ip += 3;
+ n -= 3;
+ }
+ if (n > 0)
+ {
+ *op++ = to_b64[ip[0] >> 2];
+ *op++ = to_b64[(ip[0] << 4 & 0x3f) | ip[1] >> 4];
+ if (n >= 2)
+ *op++ = to_b64[ip[1] << 2 & 0x3f];
+ }
+ *op++ = '\n';
+ fwrite(outbuf, sizeof(char), op - outbuf, sm_fp);
+ }
+
+ if (ferror(delta_fp))
+ {
+ err("error reading input file.");
+ return 1;
+ }
+
+ *sum = cksum;
+
+ return 0;
+ }
+
+
+/*
+ * Write the mail header and data header.
+ */
+void
+write_header(FILE *sfp, char *mail_alias, char *delta, int pce, int npieces)
+ {
+ fprintf(sfp, "From: owner-%s\n", mail_alias);
+ fprintf(sfp, "To: %s\n", mail_alias);
+ fprintf(sfp, "Subject: ctm-mail %s %d/%d\n\n", delta, pce, npieces);
+
+ fprintf(sfp, "CTM_MAIL BEGIN %s %d %d\n", delta, pce, npieces);
+ }
+
+
+/*
+ * Write the data trailer.
+ */
+void
+write_trailer(FILE *sfp, unsigned sum)
+ {
+ fprintf(sfp, "CTM_MAIL END %ld\n", (long)sum);
+ }
+
+
+/*
+ * We're terribly sorry, but the delta is too big to send.
+ * Returns 0 on success, 1 on failure.
+ */
+int
+apologise(char *delta, off_t ctm_size, long max_ctm_size, char *mail_alias,
+ char *queue_dir)
+ {
+ FILE *sfp;
+ char qname[PATH_MAX];
+
+ if (queue_dir == NULL)
+ {
+ sfp = open_sendmail();
+ if (sfp == NULL)
+ return 1;
+ }
+ else
+ {
+ mk_queue_name(qname, queue_dir, delta, 1, 1);
+ sfp = fopen(qname, "w");
+ if (sfp == NULL)
+ {
+ err("cannot open '%s' for writing", qname);
+ return 1;
+ }
+ }
+
+
+ fprintf(sfp, "From: owner-%s\n", mail_alias);
+ fprintf(sfp, "To: %s\n", mail_alias);
+ fprintf(sfp, "Subject: ctm-notice %s\n\n", delta);
+
+ fprintf(sfp, "%s is %ld bytes. The limit is %ld bytes.\n\n", delta,
+ (long)ctm_size, max_ctm_size);
+ fprintf(sfp, "You can retrieve this delta via ftp.\n");
+
+ if (queue_dir == NULL)
+ {
+ if (!close_sendmail(sfp))
+ return 1;
+ }
+ else
+ {
+ if (fclose(sfp)!=0)
+ {
+ err("error writing '%s'", qname);
+ unlink(qname);
+ return 1;
+ }
+ }
+
+ return 0;
+ }
+
+
+/*
+ * Start a pipe to sendmail. Sendmail will decode the destination
+ * from the message contents.
+ */
+FILE *
+open_sendmail()
+ {
+ FILE *fp;
+ char buf[100];
+
+ sprintf(buf, "%s -odq -t", _PATH_SENDMAIL);
+ if ((fp = popen(buf, "w")) == NULL)
+ err("cannot start sendmail");
+ return fp;
+ }
+
+
+/*
+ * Close a pipe to sendmail. Sendmail will then do its bit.
+ * Return 1 on success, 0 on failure.
+ */
+int
+close_sendmail(FILE *fp)
+ {
+ int status;
+
+ fflush(fp);
+ if (ferror(fp))
+ {
+ err("error writing to sendmail");
+ return 0;
+ }
+
+ if ((status = pclose(fp)) != 0)
+ err("sendmail failed with status %d", status);
+
+ return (status == 0);
+ }
diff --git a/usr.sbin/ctm/mkCTM/Makefile b/usr.sbin/ctm/mkCTM/Makefile
new file mode 100644
index 0000000..cefd704
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/Makefile
@@ -0,0 +1,26 @@
+# $FreeBSD$
+
+PROG= mkctm
+NO_MAN=
+
+DPADD= ${LIBMD}
+LDADD= -lmd
+
+test: mkctm
+ rm -f tst.out*
+ time ./mkctm -v -v /3c/210src /a/r1/usr/src \
+ 2>a | md5 -p > /a/tst.out
+ ls -l /a/tst.out
+ gzip -9 -v /a/tst.out
+ ls -l /a/tst.out.gz
+ # cd /usr/src/release && ctm -c -v -v ${.CURDIR}/tst.out
+
+test1: mkctm
+ rm -f tst.out*
+ time ./mkctm -v -v /3c/210src /home/ncvs/src \
+ 2> b | md5 -p > /a/tst2.out
+ ls -l /a/tst2.out
+ gzip -9 -v /a/tst2.out
+ ls -l /a/tst2.out.gz
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ctm/mkCTM/ctm_conf.cvs-cur b/usr.sbin/ctm/mkCTM/ctm_conf.cvs-cur
new file mode 100644
index 0000000..fbb5bf2
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/ctm_conf.cvs-cur
@@ -0,0 +1,9 @@
+#!/usr/local/bin/tclsh
+
+set CTMname cvs-cur
+set CTMref /home/ncvs
+set CTMignore {^src/secure|^src/eBones|^src/kerberosIV|^CVSROOT/val-tags$|CVSROOT/\\.#}
+set CTMbogus {\\.core$|/#cvs|/\\.#}
+set CTMmail ctm-cvs-cur-fast@freebsd.org
+set CTMqueuemail ctm-cvs-cur@freebsd.org
+set CTMqueue /home/ctm/queue/ctm-cvs-cur
diff --git a/usr.sbin/ctm/mkCTM/ctm_conf.gnats b/usr.sbin/ctm/mkCTM/ctm_conf.gnats
new file mode 100644
index 0000000..c2223f0
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/ctm_conf.gnats
@@ -0,0 +1,8 @@
+#!/usr/local/bin/tclsh
+
+#set CTMfirst 1
+set CTMname gnats
+set CTMref /home/gnats
+set CTMdest $CTMSW/../CTM-pub/$CTMname
+set CTMignore {\\.lock$}
+set CTMmail ctm-gnats@freebsd.org
diff --git a/usr.sbin/ctm/mkCTM/ctm_conf.ports-cur b/usr.sbin/ctm/mkCTM/ctm_conf.ports-cur
new file mode 100644
index 0000000..b9d3d13
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/ctm_conf.ports-cur
@@ -0,0 +1,7 @@
+#!/usr/local/bin/tclsh
+
+set CTMname ports-cur
+set CTMref /usr/ports
+set CTMignore {/CVS$|/CVS/|^distfiles}
+set CTMbogus {\\.core$|/#cvs|/\\.#}
+set CTMmail ctm-ports-cur@freebsd.org
diff --git a/usr.sbin/ctm/mkCTM/ctm_conf.smp-cur b/usr.sbin/ctm/mkCTM/ctm_conf.smp-cur
new file mode 100644
index 0000000..5c3e1d1
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/ctm_conf.smp-cur
@@ -0,0 +1,7 @@
+#!/usr/local/bin/tclsh
+
+set CTMname smp-cur
+set CTMref /home/smp
+set CTMignore {^CVSROOT/history.*$|^CVSROOT/val-tags$|^CVSROOT/\\.#}
+set CTMbogus {\\.core$|/#cvs|/\\.#}
+set CTMmail smp-cvs-cur@freebsd.org
diff --git a/usr.sbin/ctm/mkCTM/ctm_conf.src-cur b/usr.sbin/ctm/mkCTM/ctm_conf.src-cur
new file mode 100644
index 0000000..8589d04
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/ctm_conf.src-cur
@@ -0,0 +1,9 @@
+#!/usr/local/bin/tclsh
+
+set CTMname src-cur
+set CTMref /c/src
+set CTMignore {/CVS$|/CVS/|^/secure|^/eBones}
+set CTMbogus {\\.core$|/#cvs|/\\.#}
+set CTMmail ctm-src-cur-fast@freebsd.org
+set CTMqueue /home/ctm/queue/ctm-src-cur
+set CTMqueuemail ctm-src-cur@freebsd.org
diff --git a/usr.sbin/ctm/mkCTM/ctm_conf.src-special b/usr.sbin/ctm/mkCTM/ctm_conf.src-special
new file mode 100644
index 0000000..2a8ca70
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/ctm_conf.src-special
@@ -0,0 +1,9 @@
+#!/usr/local/bin/tclsh
+
+set CTMname src-cur
+set CTMref $CTMSW/../$CTMname
+set CTMcopy /c/phk/20R/usr/src
+set CTMdont {\.core$|/CVS$|/CVS/|^/secure|^/eBones|/#cvs|/\.#}
+set CTMtest 1
+set CTMspecial 1
+set CTMsuff R20
diff --git a/usr.sbin/ctm/mkCTM/dequeue b/usr.sbin/ctm/mkCTM/dequeue
new file mode 100755
index 0000000..697ec0b
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/dequeue
@@ -0,0 +1,6 @@
+#! /bin/sh
+# $FreeBSD$
+
+L=/home/ctm/log.dequeue
+/usr/sbin/ctm_dequeue -n 1 -l $L /home/ctm/queue/ctm-cvs-cur
+/usr/sbin/ctm_dequeue -n 1 -l $L /home/ctm/queue/ctm-src-cur
diff --git a/usr.sbin/ctm/mkCTM/mkCTM b/usr.sbin/ctm/mkCTM/mkCTM
new file mode 100644
index 0000000..02b1544
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/mkCTM
@@ -0,0 +1,188 @@
+#!/usr/local/bin/tclsh7.4
+#
+# $FreeBSD$
+
+#############################################################################
+### Do we already have this delta ?
+#############################################################################
+
+proc find_delta {nbr} {
+ global CTMname CTMdest
+ if {[file exists [format "%s/$CTMname.%04d" $CTMdest $nbr]]} { return 1 }
+ if {[file exists [format "%s/$CTMname.%04d.gz" $CTMdest $nbr]]} { return 1 }
+ return 0
+}
+
+#############################################################################
+### The top level code...
+#############################################################################
+
+set CTMSW /home/ctm/SW
+
+cd $CTMSW
+
+# Defaults...
+set CTMapply 1
+set CTMignore {^///}
+set CTMbogus {\.core$}
+set CTMmail {}
+set CTMqueue {}
+set CTMqueuemail {}
+set CTMmaxctm 10000000
+set CTMmaxmsg 100000
+set CTMsuff {}
+set CTMdate [exec date -u +%Y%m%d%H%M%SZ]
+set CTMtmp {}
+set CTMcopy {}
+set CTMdest {}
+set CTMprefix .
+set CTMtest 0
+set CTMspecial 0
+set CTMscan .
+set CTMfirst 0
+set max_damage 100
+
+set damage 0
+set changes 0
+
+source $argv
+exec sh -c "date -u '+%Y%m%d%H%M%S $argv'" >> ${CTMSW}/log
+
+if {$CTMtmp == ""} {
+ set CTMtmp $CTMSW/../tmp/${CTMname}_${CTMsuff}
+}
+if {$CTMcopy == ""} {
+ set CTMcopy $CTMSW/../$CTMname
+}
+if {$CTMdest == ""} {
+ set CTMdest $CTMSW/../CTM-pub/$CTMname
+}
+
+# Make sure we only run one at a time...
+
+set CTMlock Lck.${CTMname}.${CTMdate}.[pid]
+exec rm -f ${CTMlock}
+exec echo starting > ${CTMlock}
+if {[catch "exec ln $CTMlock LCK.$CTMname" a]} {
+ puts "Not going, lock exists..."
+ exec rm -f $CTMlock
+ exit 1
+}
+exec rm -f $CTMlock
+set CTMlock LCK.$CTMname
+
+set CTMscratch ${CTMtmp}.tmp
+
+while 1 {
+ if { ! $CTMspecial} {
+ if {$CTMfirst} {
+ set CTMnbr 0
+ } else {
+ set CTMnbr [lindex [exec cat $CTMcopy/.ctm_status] 1]
+ }
+
+ if {$CTMnbr > 0 && ![find_delta $CTMnbr]} {
+ puts "$CTMname delta $CTMnbr doesn't exist..."
+ exec rm -f $CTMlock
+ exit 1
+ }
+
+ incr CTMnbr
+
+ if {[find_delta $CTMnbr]} {
+ puts "$CTMname delta $CTMnbr does already exist..."
+ exec rm -f $CTMlock
+ exit 1
+ }
+
+ set fo [open $CTMref/.ctm_status w]
+ puts $fo "$CTMname $CTMnbr"
+ close $fo
+ incr changes -1
+
+ } else {
+ set CTMnbr [lindex [exec cat $CTMref/.ctm_status] 1]
+ }
+
+ puts "Doing CTMname $CTMname CTMnbr $CTMnbr$CTMsuff CTMdate $CTMdate"
+ flush stdout
+ exec sh -c "rm -f ${CTMtmp}.* ${CTMtmp}:*" >&@ stdout
+
+ set nm [format "%s.%04d%s" $CTMname $CTMnbr $CTMsuff]
+
+ set x1 $CTMcopy
+ if {$x1 == ""} {
+ exec mkdir ${CTMtmp}.dir
+ set x1 ${CTMtmp}.dir
+ }
+ set r1 [catch "exec ${CTMSW}/mkctm -I ${CTMignore} -B ${CTMbogus} -l ${CTMtmp}.log -D $max_damage $CTMname $CTMnbr $CTMdate . $x1 $CTMref | md5 -p | gzip -9 > ${CTMtmp}:${nm}.gz 2>@ stderr" r2]
+
+ if {$r1} {
+ if {[lindex $errorCode 2] == 4} {
+ puts "No changes, stopping."
+ exec rm -f $CTMlock
+ exit 0
+ }
+ puts "problems, stopping now."
+ puts "errorCode $errorCode"
+ puts "$r2"
+ exec rm -f $CTMlock
+ exit 1
+ }
+
+ puts "mkctm done"
+
+ if {$CTMtest} {
+ puts "testing, stopping now."
+ exec rm -f $CTMlock
+ exit 0
+ }
+ if {$CTMapply} {
+ puts "Applying delta"
+ flush stdout
+ exec echo now applying > $CTMlock
+ exec sh -e -c "cd $CTMcopy ; $CTMSW/ctm -v -v -v ${CTMtmp}:${nm}.gz" >& ${CTMtmp}.apply
+ exec echo did apply > $CTMlock
+ }
+ puts "Moving delta"
+ flush stdout
+ exec mv ${CTMtmp}:${nm}.gz $CTMdest/.CTMtmp_${nm}.gz >&@ stdout
+ exec mv $CTMdest/.CTMtmp_${nm}.gz $CTMdest/${nm}.gz >&@ stdout
+ exec echo moved > $CTMlock
+
+ exec sh -c "rm -rf ${CTMtmp}.*" >&@ stdout
+
+ if {$CTMmail != ""} {
+ puts "Mailing delta"
+ flush stdout
+ exec $CTMSW/ctm_smail -m $CTMmaxmsg -c $CTMmaxctm $CTMdest/${nm}.gz $CTMmail >&@ stdout
+ if {$CTMqueue != "" && $CTMqueuemail != ""} {
+ puts "Queueing delta"
+ flush stdout
+ exec $CTMSW/ctm_smail -m $CTMmaxmsg -c $CTMmaxctm -q $CTMqueue $CTMdest/${nm}.gz $CTMqueuemail >&@ stdout
+ puts "Sending initial two deltas"
+ flush stdout
+ exec $CTMSW/ctm_dequeue -n 2 $CTMqueue >&@ stdout
+ }
+ }
+ exec echo mailed > $CTMlock
+
+ # If we did an absolute delta: stop.
+ if {$CTMsuff != ""} break
+
+ # Make an absolute delta (!) every 100 deltas
+ if {$CTMnbr == 0 || ($CTMnbr % 100)} break
+
+ # Make an absolute delta too...
+ set CTMref $CTMcopy
+ set CTMsuff A
+ set CTMcopy ""
+ set CTMmail ""
+ set CTMqueue ""
+ set CTMqueuemail ""
+ set CTMapply 0
+ set CTMspecial 1
+ exec rm -f $CTMlock
+}
+puts "done."
+exec rm -f $CTMlock
diff --git a/usr.sbin/ctm/mkCTM/mkctm.c b/usr.sbin/ctm/mkCTM/mkctm.c
new file mode 100644
index 0000000..d2c73e2
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/mkctm.c
@@ -0,0 +1,597 @@
+/* $FreeBSD$ */
+
+/* Still missing:
+ *
+ * mkctm
+ * -B regex Bogus
+ * -I regex Ignore
+ * -D int Damage
+ * -q decrease verbosity
+ * -v increase verbosity
+ * -l file logfile
+ * name cvs-cur
+ * prefix src/secure
+ * dir1 "Soll"
+ * dir2 "Ist"
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <regex.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <md5.h>
+#include <err.h>
+#include <paths.h>
+#include <signal.h>
+
+#define DEFAULT_IGNORE "/CVS$|/\\.#|00_TRANS\\.TBL$"
+#define DEFAULT_BOGUS "\\.core$|\\.orig$|\\.rej$|\\.o$"
+regex_t reg_ignore, reg_bogus;
+int flag_ignore, flag_bogus;
+
+int verbose;
+int damage, damage_limit;
+int change;
+
+FILE *logf;
+
+u_long s1_ignored, s2_ignored;
+u_long s1_bogus, s2_bogus;
+u_long s1_wrong, s2_wrong;
+u_long s_new_dirs, s_new_files, s_new_bytes;
+u_long s_del_dirs, s_del_files, s_del_bytes;
+u_long s_files_chg, s_bytes_add, s_bytes_del;
+u_long s_same_dirs, s_same_files, s_same_bytes;
+u_long s_edit_files, s_edit_bytes, s_edit_saves;
+u_long s_sub_files, s_sub_bytes;
+
+void
+Usage(void)
+{
+ fprintf(stderr,
+ "usage: mkctm [-options] name number timestamp prefix dir1 dir2\n");
+ fprintf(stderr, "options:\n");
+ fprintf(stderr, "\t\t-B bogus_regexp\n");
+ fprintf(stderr, "\t\t-D damage_limit\n");
+ fprintf(stderr, "\t\t-I ignore_regexp\n");
+ fprintf(stderr, "\t\t-q\n");
+ fprintf(stderr, "\t\t-v\n");
+}
+
+void
+print_stat(FILE *fd, char *pre)
+{
+ fprintf(fd, "%sNames:\n", pre);
+ fprintf(fd, "%s ignore: %5lu ref %5lu target\n",
+ pre, s1_ignored, s2_ignored);
+ fprintf(fd, "%s bogus: %5lu ref %5lu target\n",
+ pre, s1_bogus, s2_bogus);
+ fprintf(fd, "%s wrong: %5lu ref %5lu target\n",
+ pre, s1_wrong, s2_wrong);
+ fprintf(fd, "%sDelta:\n", pre);
+ fprintf(fd, "%s new: %5lu dirs %5lu files %9lu plus\n",
+ pre, s_new_dirs, s_new_files, s_new_bytes);
+ fprintf(fd, "%s del: %5lu dirs %5lu files %9lu minus\n",
+ pre, s_del_dirs, s_del_files, s_del_bytes);
+ fprintf(fd, "%s chg: %5lu files %9lu plus %9lu minus\n",
+ pre, s_files_chg, s_bytes_add, s_bytes_del);
+ fprintf(fd, "%s same: %5lu dirs %5lu files %9lu bytes\n",
+ pre, s_same_dirs, s_same_files, s_same_bytes);
+ fprintf(fd, "%sMethod:\n", pre);
+ fprintf(fd, "%s edit: %5lu files %9lu bytes %9lu saved\n",
+ pre, s_edit_files, s_edit_bytes, s_edit_saves);
+ fprintf(fd, "%s sub: %5lu files %9lu bytes\n",
+ pre, s_sub_files, s_sub_bytes);
+
+}
+
+void
+stat_info(int foo)
+{
+ signal(SIGINFO, stat_info);
+ print_stat(stderr, "INFO: ");
+}
+
+void DoDir(const char *dir1, const char *dir2, const char *name);
+
+static struct stat st;
+static __inline struct stat *
+StatFile(char *name)
+{
+ if (lstat(name, &st) < 0)
+ err(1, "couldn't stat %s", name);
+ return &st;
+}
+
+int
+dirselect(struct dirent *de)
+{
+ if (!strcmp(de->d_name, ".")) return 0;
+ if (!strcmp(de->d_name, "..")) return 0;
+ return 1;
+}
+
+void
+name_stat(const char *pfx, const char *dir, const char *name, struct dirent *de)
+{
+ char *buf = alloca(strlen(dir) + strlen(name) +
+ strlen(de->d_name) + 3);
+ struct stat *st;
+
+ strcpy(buf, dir);
+ strcat(buf, "/"); strcat(buf, name);
+ strcat(buf, "/"); strcat(buf, de->d_name);
+ st = StatFile(buf);
+ printf("%s %s%s %u %u %o",
+ pfx, name, de->d_name,
+ st->st_uid, st->st_gid, st->st_mode & ~S_IFMT);
+ fprintf(logf, "%s %s%s\n", pfx, name, de->d_name);
+ if (verbose > 1) {
+ fprintf(stderr, "%s %s%s\n", pfx, name, de->d_name);
+ }
+}
+
+void
+Equ(const char *dir1, const char *dir2, const char *name, struct dirent *de)
+{
+ if (de->d_type == DT_DIR) {
+ char *p = alloca(strlen(name)+strlen(de->d_name)+2);
+
+ strcpy(p, name); strcat(p, de->d_name); strcat(p, "/");
+ DoDir(dir1, dir2, p);
+ s_same_dirs++;
+ } else {
+ char *buf1 = alloca(strlen(dir1) + strlen(name) +
+ strlen(de->d_name) + 3);
+ char *buf2 = alloca(strlen(dir2) + strlen(name) +
+ strlen(de->d_name) + 3);
+ char *m1, md5_1[33], *m2, md5_2[33];
+ u_char *p1, *p2;
+ int fd1, fd2;
+ struct stat s1, s2;
+
+ strcpy(buf1, dir1);
+ strcat(buf1, "/"); strcat(buf1, name);
+ strcat(buf1, "/"); strcat(buf1, de->d_name);
+ fd1 = open(buf1, O_RDONLY);
+ if(fd1 < 0) { err(3, "%s", buf1); }
+ fstat(fd1, &s1);
+ strcpy(buf2, dir2);
+ strcat(buf2, "/"); strcat(buf2, name);
+ strcat(buf2, "/"); strcat(buf2, de->d_name);
+ fd2 = open(buf2, O_RDONLY);
+ if(fd2 < 0) { err(3, "%s", buf2); }
+ fstat(fd2, &s2);
+#if 0
+ /* XXX if we could just trust the size to change... */
+ if (s1.st_size == s2.st_size) {
+ s_same_files++;
+ s_same_bytes += s1.st_size;
+ close(fd1);
+ close(fd2);
+ goto finish;
+ }
+#endif
+ p1=mmap(0, s1.st_size, PROT_READ, MAP_PRIVATE, fd1, 0);
+ if (p1 == (u_char *)MAP_FAILED) { err(3, "%s", buf1); }
+ close(fd1);
+
+ p2=mmap(0, s2.st_size, PROT_READ, MAP_PRIVATE, fd2, 0);
+ if (p2 == (u_char *)MAP_FAILED) { err(3, "%s", buf2); }
+ close(fd2);
+
+ /* If identical, we're done. */
+ if((s1.st_size == s2.st_size) && !memcmp(p1, p2, s1.st_size)) {
+ s_same_files++;
+ s_same_bytes += s1.st_size;
+ goto finish;
+ }
+
+ s_files_chg++;
+ change++;
+ if (s1.st_size > s2.st_size)
+ s_bytes_del += (s1.st_size - s2.st_size);
+ else
+ s_bytes_add += (s2.st_size - s1.st_size);
+
+ m1 = MD5Data(p1, s1.st_size, md5_1);
+ m2 = MD5Data(p2, s2.st_size, md5_2);
+
+ /* Just a curiosity... */
+ if(!strcmp(m1, m2)) {
+ if (s1.st_size != s2.st_size)
+ fprintf(stderr,
+ "Notice: MD5 same for files of diffent size:\n\t%s\n\t%s\n",
+ buf1, buf2);
+ goto finish;
+ }
+
+ {
+ u_long l = s2.st_size + 2;
+ u_char *cmd = alloca(strlen(buf1)+strlen(buf2)+100);
+ u_char *ob = malloc(l), *p;
+ int j;
+ FILE *F;
+
+ if (s1.st_size && p1[s1.st_size-1] != '\n') {
+ if (verbose > 0)
+ fprintf(stderr,
+ "last char != \\n in %s\n",
+ buf1);
+ goto subst;
+ }
+
+ if (s2.st_size && p2[s2.st_size-1] != '\n') {
+ if (verbose > 0)
+ fprintf(stderr,
+ "last char != \\n in %s\n",
+ buf2);
+ goto subst;
+ }
+
+ for (p=p1; p<p1+s1.st_size; p++)
+ if (!*p) {
+ if (verbose > 0)
+ fprintf(stderr,
+ "NULL char in %s\n",
+ buf1);
+ goto subst;
+ }
+
+ for (p=p2; p<p2+s2.st_size; p++)
+ if (!*p) {
+ if (verbose > 0)
+ fprintf(stderr,
+ "NULL char in %s\n",
+ buf2);
+ goto subst;
+ }
+
+ strcpy(cmd, "diff -n ");
+ strcat(cmd, buf1);
+ strcat(cmd, " ");
+ strcat(cmd, buf2);
+ F = popen(cmd, "r");
+ for (j = 1, l = 0; l < s2.st_size; ) {
+ j = fread(ob+l, 1, s2.st_size - l, F);
+ if (j < 1)
+ break;
+ l += j;
+ continue;
+ }
+ if (j) {
+ l = 0;
+ while (EOF != fgetc(F))
+ continue;
+ }
+ pclose(F);
+
+ if (l && l < s2.st_size) {
+ name_stat("CTMFN", dir2, name, de);
+ printf(" %s %s %d\n", m1, m2, (unsigned)l);
+ fwrite(ob, 1, l, stdout);
+ putchar('\n');
+ s_edit_files++;
+ s_edit_bytes += l;
+ s_edit_saves += (s2.st_size - l);
+ } else {
+ subst:
+ name_stat("CTMFS", dir2, name, de);
+ printf(" %s %s %u\n", m1, m2, (unsigned)s2.st_size);
+ fwrite(p2, 1, s2.st_size, stdout);
+ putchar('\n');
+ s_sub_files++;
+ s_sub_bytes += s2.st_size;
+ }
+ free(ob);
+ }
+ finish:
+ munmap(p1, s1.st_size);
+ munmap(p2, s2.st_size);
+ }
+}
+
+void
+Add(const char *dir1, const char *dir2, const char *name, struct dirent *de)
+{
+ change++;
+ if (de->d_type == DT_DIR) {
+ char *p = alloca(strlen(name)+strlen(de->d_name)+2);
+ strcpy(p, name); strcat(p, de->d_name); strcat(p, "/");
+ name_stat("CTMDM", dir2, name, de);
+ putchar('\n');
+ s_new_dirs++;
+ DoDir(dir1, dir2, p);
+ } else if (de->d_type == DT_REG) {
+ char *buf2 = alloca(strlen(dir2) + strlen(name) +
+ strlen(de->d_name) + 3);
+ char *m2, md5_2[33];
+ u_char *p1;
+ struct stat st;
+ int fd1;
+
+ strcpy(buf2, dir2);
+ strcat(buf2, "/"); strcat(buf2, name);
+ strcat(buf2, "/"); strcat(buf2, de->d_name);
+ fd1 = open(buf2, O_RDONLY);
+ if (fd1 < 0) { err(3, "%s", buf2); }
+ fstat(fd1, &st);
+ p1=mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd1, 0);
+ if (p1 == (u_char *)MAP_FAILED) { err(3, "%s", buf2); }
+ close(fd1);
+ m2 = MD5Data(p1, st.st_size, md5_2);
+ name_stat("CTMFM", dir2, name, de);
+ printf(" %s %u\n", m2, (unsigned)st.st_size);
+ fwrite(p1, 1, st.st_size, stdout);
+ putchar('\n');
+ munmap(p1, st.st_size);
+ s_new_files++;
+ s_new_bytes += st.st_size;
+ }
+}
+
+void
+Del (const char *dir1, const char *dir2, const char *name, struct dirent *de)
+{
+ damage++;
+ change++;
+ if (de->d_type == DT_DIR) {
+ char *p = alloca(strlen(name)+strlen(de->d_name)+2);
+ strcpy(p, name); strcat(p, de->d_name); strcat(p, "/");
+ DoDir(dir1, dir2, p);
+ printf("CTMDR %s%s\n", name, de->d_name);
+ fprintf(logf, "CTMDR %s%s\n", name, de->d_name);
+ if (verbose > 1) {
+ fprintf(stderr, "CTMDR %s%s\n", name, de->d_name);
+ }
+ s_del_dirs++;
+ } else if (de->d_type == DT_REG) {
+ char *buf1 = alloca(strlen(dir1) + strlen(name) +
+ strlen(de->d_name) + 3);
+ char *m1, md5_1[33];
+ strcpy(buf1, dir1);
+ strcat(buf1, "/"); strcat(buf1, name);
+ strcat(buf1, "/"); strcat(buf1, de->d_name);
+ m1 = MD5File(buf1, md5_1);
+ printf("CTMFR %s%s %s\n", name, de->d_name, m1);
+ fprintf(logf, "CTMFR %s%s %s\n", name, de->d_name, m1);
+ if (verbose > 1) {
+ fprintf(stderr, "CTMFR %s%s\n", name, de->d_name);
+ }
+ s_del_files++;
+ s_del_bytes += StatFile(buf1)->st_size;
+ }
+}
+
+void
+GetNext(int *i, int *n, struct dirent **nl, const char *dir, const char *name, u_long *ignored, u_long *bogus, u_long *wrong)
+{
+ char buf[BUFSIZ];
+ char buf1[BUFSIZ];
+
+ for (;;) {
+ for (;;) {
+ (*i)++;
+ if (*i >= *n)
+ return;
+ strcpy(buf1, name);
+ if (buf1[strlen(buf1)-1] != '/')
+ strcat(buf1, "/");
+ strcat(buf1, nl[*i]->d_name);
+ if (flag_ignore &&
+ !regexec(&reg_ignore, buf1, 0, 0, 0)) {
+ (*ignored)++;
+ fprintf(logf, "Ignore %s\n", buf1);
+ if (verbose > 2) {
+ fprintf(stderr, "Ignore %s\n", buf1);
+ }
+ } else if (flag_bogus &&
+ !regexec(&reg_bogus, buf1, 0, 0, 0)) {
+ (*bogus)++;
+ fprintf(logf, "Bogus %s\n", buf1);
+ fprintf(stderr, "Bogus %s\n", buf1);
+ damage++;
+ } else {
+ *buf = 0;
+ if (*dir != '/')
+ strcat(buf, "/");
+ strcat(buf, dir);
+ if (buf[strlen(buf)-1] != '/')
+ strcat(buf, "/");
+ strcat(buf, buf1);
+ break;
+ }
+ free(nl[*i]); nl[*i] = 0;
+ }
+ /* If the filesystem didn't tell us, find type */
+ if (nl[*i]->d_type == DT_UNKNOWN)
+ nl[*i]->d_type = IFTODT(StatFile(buf)->st_mode);
+ if (nl[*i]->d_type == DT_REG || nl[*i]->d_type == DT_DIR)
+ break;
+ (*wrong)++;
+ if (verbose > 0)
+ fprintf(stderr, "Wrong %s\n", buf);
+ free(nl[*i]); nl[*i] = 0;
+ }
+}
+
+void
+DoDir(const char *dir1, const char *dir2, const char *name)
+{
+ int i1, i2, n1, n2, i;
+ struct dirent **nl1, **nl2;
+ char *buf1 = alloca(strlen(dir1) + strlen(name) + 4);
+ char *buf2 = alloca(strlen(dir2) + strlen(name) + 4);
+
+ strcpy(buf1, dir1); strcat(buf1, "/"); strcat(buf1, name);
+ strcpy(buf2, dir2); strcat(buf2, "/"); strcat(buf2, name);
+ n1 = scandir(buf1, &nl1, dirselect, alphasort);
+ n2 = scandir(buf2, &nl2, dirselect, alphasort);
+ i1 = i2 = -1;
+ GetNext(&i1, &n1, nl1, dir1, name, &s1_ignored, &s1_bogus, &s1_wrong);
+ GetNext(&i2, &n2, nl2, dir2, name, &s2_ignored, &s2_bogus, &s2_wrong);
+ for (;i1 < n1 || i2 < n2;) {
+
+ if (damage_limit && damage > damage_limit)
+ break;
+
+ /* Get next item from list 1 */
+ if (i1 < n1 && !nl1[i1])
+ GetNext(&i1, &n1, nl1, dir1, name,
+ &s1_ignored, &s1_bogus, &s1_wrong);
+
+ /* Get next item from list 2 */
+ if (i2 < n2 && !nl2[i2])
+ GetNext(&i2, &n2, nl2, dir2, name,
+ &s2_ignored, &s2_bogus, &s2_wrong);
+
+ if (i1 >= n1 && i2 >= n2) {
+ /* Done */
+ break;
+ } else if (i1 >= n1 && i2 < n2) {
+ /* end of list 1, add anything left on list 2 */
+ Add(dir1, dir2, name, nl2[i2]);
+ free(nl2[i2]); nl2[i2] = 0;
+ } else if (i1 < n1 && i2 >= n2) {
+ /* end of list 2, delete anything left on list 1 */
+ Del(dir1, dir2, name, nl1[i1]);
+ free(nl1[i1]); nl1[i1] = 0;
+ } else if (!(i = strcmp(nl1[i1]->d_name, nl2[i2]->d_name))) {
+ /* Identical names */
+ if (nl1[i1]->d_type == nl2[i2]->d_type) {
+ /* same type */
+ Equ(dir1, dir2, name, nl1[i1]);
+ } else {
+ /* different types */
+ Del(dir1, dir2, name, nl1[i1]);
+ Add(dir1, dir2, name, nl2[i2]);
+ }
+ free(nl1[i1]); nl1[i1] = 0;
+ free(nl2[i2]); nl2[i2] = 0;
+ } else if (i < 0) {
+ /* Something extra in list 1, delete it */
+ Del(dir1, dir2, name, nl1[i1]);
+ free(nl1[i1]); nl1[i1] = 0;
+ } else {
+ /* Something extra in list 2, add it */
+ Add(dir1, dir2, name, nl2[i2]);
+ free(nl2[i2]); nl2[i2] = 0;
+ }
+ }
+ if (n1 >= 0)
+ free(nl1);
+ if (n2 >= 0)
+ free(nl2);
+}
+
+int
+main(int argc, char **argv)
+{
+ int i;
+
+ setbuf(stderr, NULL);
+
+#if 0
+ if (regcomp(&reg_bogus, DEFAULT_BOGUS, REG_EXTENDED | REG_NEWLINE))
+ /* XXX use regerror to explain it */
+ errx(1, "default regular expression argument to -B is botched");
+ flag_bogus = 1;
+
+ if (regcomp(&reg_ignore, DEFAULT_IGNORE, REG_EXTENDED | REG_NEWLINE))
+ /* XXX use regerror to explain it */
+ errx(1, "default regular expression argument to -I is botched");
+ flag_ignore = 1;
+#endif
+
+ while ((i = getopt(argc, argv, "D:I:B:l:qv")) != -1)
+ switch (i) {
+ case 'D':
+ damage_limit = strtol(optarg, 0, 0);
+ if (damage_limit < 0)
+ errx(1, "damage limit must be positive");
+ break;
+ case 'I':
+ if (flag_ignore)
+ regfree(&reg_ignore);
+ flag_ignore = 0;
+ if (!*optarg)
+ break;
+ if (regcomp(&reg_ignore, optarg,
+ REG_EXTENDED | REG_NEWLINE))
+ /* XXX use regerror to explain it */
+ errx(1, "regular expression argument to -I is botched");
+ flag_ignore = 1;
+ break;
+ case 'B':
+ if (flag_bogus)
+ regfree(&reg_bogus);
+ flag_bogus = 0;
+ if (!*optarg)
+ break;
+ if (regcomp(&reg_bogus, optarg,
+ REG_EXTENDED | REG_NEWLINE))
+ /* XXX use regerror to explain it */
+ errx(1, "regular expression argument to -B is botched");
+ flag_bogus = 1;
+ break;
+ case 'l':
+ logf = fopen(optarg, "w");
+ if (!logf)
+ err(1, "%s", optarg);
+ setlinebuf(logf);
+ break;
+ case 'q':
+ verbose--;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case '?':
+ default:
+ Usage();
+ return (1);
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!logf)
+ logf = fopen(_PATH_DEVNULL, "w");
+
+ setbuf(stdout, 0);
+
+ if (argc != 6) {
+ Usage();
+ return (1);
+ }
+
+ signal(SIGINFO, stat_info);
+
+ fprintf(stderr, "CTM_BEGIN 2.0 %s %s %s %s\n",
+ argv[0], argv[1], argv[2], argv[3]);
+ fprintf(logf, "CTM_BEGIN 2.0 %s %s %s %s\n",
+ argv[0], argv[1], argv[2], argv[3]);
+ printf("CTM_BEGIN 2.0 %s %s %s %s\n",
+ argv[0], argv[1], argv[2], argv[3]);
+ DoDir(argv[4], argv[5], "");
+ if (damage_limit && damage > damage_limit) {
+ print_stat(stderr, "DAMAGE: ");
+ errx(1, "damage of %d would exceed %d files",
+ damage, damage_limit);
+ } else if (change < 2) {
+ errx(4, "no changes");
+ } else {
+ printf("CTM_END ");
+ fprintf(logf, "CTM_END\n");
+ print_stat(stderr, "END: ");
+ }
+ exit(0);
+}
diff --git a/usr.sbin/cxgbtool/Makefile b/usr.sbin/cxgbtool/Makefile
new file mode 100644
index 0000000..d97c513
--- /dev/null
+++ b/usr.sbin/cxgbtool/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+PROG= cxgbtool
+SRCS= cxgbtool.c
+NO_MAN=
+CFLAGS+= -I${.CURDIR}/../../sys/dev/cxgb -I.
+CFLAGS+= -DCONFIG_T3_REGS -DCHELSIO_INTERNAL
+
+install:
+
+.include <bsd.prog.mk> \ No newline at end of file
diff --git a/usr.sbin/cxgbtool/cxgbtool.c b/usr.sbin/cxgbtool/cxgbtool.c
new file mode 100644
index 0000000..962177f
--- /dev/null
+++ b/usr.sbin/cxgbtool/cxgbtool.c
@@ -0,0 +1,1478 @@
+/**************************************************************************
+
+Copyright (c) 2007-2009, Chelsio 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.
+
+ 3. Neither the name of the Chelsio Corporation 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (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 <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <err.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_types.h>
+#include <sys/endian.h>
+
+#define NMTUS 16
+#define TCB_SIZE 128
+#define TCB_WORDS (TCB_SIZE / 4)
+#define PROTO_SRAM_LINES 128
+#define PROTO_SRAM_LINE_BITS 132
+#define PROTO_SRAM_LINE_NIBBLES (132 / 4)
+#define PROTO_SRAM_SIZE (PROTO_SRAM_LINE_NIBBLES * PROTO_SRAM_LINES / 2)
+#define PROTO_SRAM_EEPROM_ADDR 4096
+
+#include <cxgb_ioctl.h>
+#include <common/cxgb_regs.h>
+#include "version.h"
+
+struct reg_info {
+ const char *name;
+ uint16_t addr;
+ uint16_t len;
+};
+
+
+#include "reg_defs.c"
+#if defined(CONFIG_T3_REGS)
+# include "reg_defs_t3.c"
+# include "reg_defs_t3b.c"
+# include "reg_defs_t3c.c"
+#endif
+
+static const char *progname;
+
+static void __attribute__((noreturn)) usage(FILE *fp)
+{
+ fprintf(fp, "Usage: %s <interface> [operation]\n", progname);
+ fprintf(fp,
+ "\tclearstats clear MAC statistics\n"
+ "\tcontext <type> <id> show an SGE context\n"
+ "\tdesc <qset> <queue> <idx> [<cnt>] dump SGE descriptors\n"
+ "\tioqs dump uP IOQs\n"
+ "\tla dump uP logic analyzer info\n"
+ "\tloadboot <boot image> download boot image\n"
+ "\tloadfw <FW image> download firmware\n"
+ "\tmdio <phy_addr> <mmd_addr>\n"
+ "\t <reg_addr> [<val>] read/write MDIO register\n"
+ "\tmemdump cm|tx|rx <addr> <len> dump a mem range\n"
+ "\tmeminfo show memory info\n"
+ "\tmtus [<mtu0>...<mtuN>] read/write MTU table\n"
+ "\tpktsched port <idx> <min> <max> set TX port scheduler params\n"
+ "\tpktsched tunnelq <idx> <max>\n"
+ "\t <binding> set TX tunnelq scheduler params\n"
+ "\tpktsched tx <idx>\n"
+ "\t [<param> <val>] ... set Tx HW scheduler\n"
+ "\tpm [<TX page spec> <RX page spec>] read/write PM config\n"
+ "\tproto read proto SRAM\n"
+ "\tqset read qset parameters\n"
+ "\tqsets read # of qsets\n"
+ "\treg <address>[=<val>] read/write register\n"
+ "\tregdump [<module>] dump registers\n"
+ "\ttcamdump <address> <count> show TCAM contents\n"
+ "\ttcb <index> read TCB\n"
+ "\ttrace tx|rx|all on|off [not]\n"
+ "\t [<param> <val>[:<mask>]] ... write trace parameters\n"
+ );
+ exit(fp == stderr ? 1 : 0);
+}
+
+static int
+doit(const char *iff_name, unsigned long cmd, void *data)
+{
+ static int fd = 0;
+
+ if (fd == 0) {
+ char buf[64];
+ snprintf(buf, 64, "/dev/%s", iff_name);
+
+ if ((fd = open(buf, O_RDWR)) < 0)
+ return -1;
+ }
+
+ return ioctl(fd, cmd, data) < 0 ? -1 : 0;
+}
+
+static int get_int_arg(const char *s, uint32_t *valp)
+{
+ char *p;
+
+ *valp = strtoul(s, &p, 0);
+ if (*p) {
+ warnx("bad parameter \"%s\"", s);
+ return -1;
+ }
+ return 0;
+}
+
+static uint32_t
+read_reg(const char *iff_name, uint32_t addr)
+{
+ struct ch_reg reg;
+
+ reg.addr = addr;
+
+ if (doit(iff_name, CHELSIO_GETREG, &reg) < 0)
+ err(1, "register read");
+ return reg.val;
+}
+
+static void
+write_reg(const char *iff_name, uint32_t addr, uint32_t val)
+{
+ struct ch_reg ch_reg;
+
+ ch_reg.addr = addr;
+ ch_reg.val = val;
+
+ if (doit(iff_name, CHELSIO_SETREG, &ch_reg) < 0)
+ err(1, "register write");
+}
+
+static int register_io(int argc, char *argv[], int start_arg,
+ const char *iff_name)
+{
+ char *p;
+ uint32_t addr, val = 0, write = 0;
+
+ if (argc != start_arg + 1) return -1;
+
+ addr = strtoul(argv[start_arg], &p, 0);
+ if (p == argv[start_arg]) return -1;
+ if (*p == '=' && p[1]) {
+ val = strtoul(p + 1, &p, 0);
+ write = 1;
+ }
+ if (*p) {
+ warnx("bad parameter \"%s\"", argv[start_arg]);
+ return -1;
+ }
+
+ if (write)
+ write_reg(iff_name, addr, val);
+ else {
+ val = read_reg(iff_name, addr);
+ printf("%#x [%u]\n", val, val);
+ }
+ return 0;
+}
+
+static int mdio_io(int argc, char *argv[], int start_arg, const char *iff_name)
+{
+ struct ifreq ifr;
+ struct ch_mii_data p;
+ unsigned int cmd, phy_addr, reg, mmd, val;
+
+ if (argc == start_arg + 3)
+ cmd = CHELSIO_GET_MIIREG;
+ else if (argc == start_arg + 4)
+ cmd = CHELSIO_SET_MIIREG;
+ else
+ return -1;
+
+ if (get_int_arg(argv[start_arg], &phy_addr) ||
+ get_int_arg(argv[start_arg + 1], &mmd) ||
+ get_int_arg(argv[start_arg + 2], &reg) ||
+ (cmd == CHELSIO_SET_MIIREG && get_int_arg(argv[start_arg + 3], &val)))
+ return -1;
+
+ p.phy_id = phy_addr | (mmd << 8);
+ p.reg_num = reg;
+ p.val_in = val;
+
+ if (doit(iff_name, cmd, &p) < 0)
+ err(1, "MDIO %s", cmd == CHELSIO_GET_MIIREG ? "read" : "write");
+ if (cmd == CHELSIO_GET_MIIREG)
+ printf("%#x [%u]\n", p.val_out, p.val_out);
+ return 0;
+}
+
+static inline uint32_t xtract(uint32_t val, int shift, int len)
+{
+ return (val >> shift) & ((1 << len) - 1);
+}
+
+static int dump_block_regs(const struct reg_info *reg_array, uint32_t *regs)
+{
+ uint32_t reg_val = 0; // silence compiler warning
+
+ for ( ; reg_array->name; ++reg_array)
+ if (!reg_array->len) {
+ reg_val = regs[reg_array->addr / 4];
+ printf("[%#5x] %-40s %#-10x [%u]\n", reg_array->addr,
+ reg_array->name, reg_val, reg_val);
+ } else {
+ uint32_t v = xtract(reg_val, reg_array->addr,
+ reg_array->len);
+
+ printf(" %-40s %#-10x [%u]\n", reg_array->name,
+ v, v);
+ }
+ return 1;
+}
+
+static int dump_regs_t2(int argc, char *argv[], int start_arg, uint32_t *regs)
+{
+ int match = 0;
+ char *block_name = NULL;
+
+ if (argc == start_arg + 1)
+ block_name = argv[start_arg];
+ else if (argc != start_arg)
+ return -1;
+
+ if (!block_name || !strcmp(block_name, "sge"))
+ match += dump_block_regs(sge_regs, regs);
+ if (!block_name || !strcmp(block_name, "mc3"))
+ match += dump_block_regs(mc3_regs, regs);
+ if (!block_name || !strcmp(block_name, "mc4"))
+ match += dump_block_regs(mc4_regs, regs);
+ if (!block_name || !strcmp(block_name, "tpi"))
+ match += dump_block_regs(tpi_regs, regs);
+ if (!block_name || !strcmp(block_name, "tp"))
+ match += dump_block_regs(tp_regs, regs);
+ if (!block_name || !strcmp(block_name, "rat"))
+ match += dump_block_regs(rat_regs, regs);
+ if (!block_name || !strcmp(block_name, "cspi"))
+ match += dump_block_regs(cspi_regs, regs);
+ if (!block_name || !strcmp(block_name, "espi"))
+ match += dump_block_regs(espi_regs, regs);
+ if (!block_name || !strcmp(block_name, "ulp"))
+ match += dump_block_regs(ulp_regs, regs);
+ if (!block_name || !strcmp(block_name, "pl"))
+ match += dump_block_regs(pl_regs, regs);
+ if (!block_name || !strcmp(block_name, "mc5"))
+ match += dump_block_regs(mc5_regs, regs);
+ if (!match)
+ errx(1, "unknown block \"%s\"", block_name);
+ return 0;
+}
+
+#if defined(CONFIG_T3_REGS)
+static int dump_regs_t3(int argc, char *argv[], int start_arg, uint32_t *regs,
+ int is_pcie)
+{
+ int match = 0;
+ char *block_name = NULL;
+
+ if (argc == start_arg + 1)
+ block_name = argv[start_arg];
+ else if (argc != start_arg)
+ return -1;
+
+ if (!block_name || !strcmp(block_name, "sge"))
+ match += dump_block_regs(sge3_regs, regs);
+ if (!block_name || !strcmp(block_name, "pci"))
+ match += dump_block_regs(is_pcie ? pcie0_regs : pcix1_regs,
+ regs);
+ if (!block_name || !strcmp(block_name, "t3dbg"))
+ match += dump_block_regs(t3dbg_regs, regs);
+ if (!block_name || !strcmp(block_name, "pmrx"))
+ match += dump_block_regs(mc7_pmrx_regs, regs);
+ if (!block_name || !strcmp(block_name, "pmtx"))
+ match += dump_block_regs(mc7_pmtx_regs, regs);
+ if (!block_name || !strcmp(block_name, "cm"))
+ match += dump_block_regs(mc7_cm_regs, regs);
+ if (!block_name || !strcmp(block_name, "cim"))
+ match += dump_block_regs(cim_regs, regs);
+ if (!block_name || !strcmp(block_name, "tp"))
+ match += dump_block_regs(tp1_regs, regs);
+ if (!block_name || !strcmp(block_name, "ulp_rx"))
+ match += dump_block_regs(ulp2_rx_regs, regs);
+ if (!block_name || !strcmp(block_name, "ulp_tx"))
+ match += dump_block_regs(ulp2_tx_regs, regs);
+ if (!block_name || !strcmp(block_name, "pmrx"))
+ match += dump_block_regs(pm1_rx_regs, regs);
+ if (!block_name || !strcmp(block_name, "pmtx"))
+ match += dump_block_regs(pm1_tx_regs, regs);
+ if (!block_name || !strcmp(block_name, "mps"))
+ match += dump_block_regs(mps0_regs, regs);
+ if (!block_name || !strcmp(block_name, "cplsw"))
+ match += dump_block_regs(cpl_switch_regs, regs);
+ if (!block_name || !strcmp(block_name, "smb"))
+ match += dump_block_regs(smb0_regs, regs);
+ if (!block_name || !strcmp(block_name, "i2c"))
+ match += dump_block_regs(i2cm0_regs, regs);
+ if (!block_name || !strcmp(block_name, "mi1"))
+ match += dump_block_regs(mi1_regs, regs);
+ if (!block_name || !strcmp(block_name, "sf"))
+ match += dump_block_regs(sf1_regs, regs);
+ if (!block_name || !strcmp(block_name, "pl"))
+ match += dump_block_regs(pl3_regs, regs);
+ if (!block_name || !strcmp(block_name, "mc5"))
+ match += dump_block_regs(mc5a_regs, regs);
+ if (!block_name || !strcmp(block_name, "xgmac0"))
+ match += dump_block_regs(xgmac0_0_regs, regs);
+ if (!block_name || !strcmp(block_name, "xgmac1"))
+ match += dump_block_regs(xgmac0_1_regs, regs);
+ if (!match)
+ errx(1, "unknown block \"%s\"", block_name);
+ return 0;
+}
+
+static int dump_regs_t3b(int argc, char *argv[], int start_arg, uint32_t *regs,
+ int is_pcie)
+{
+ int match = 0;
+ char *block_name = NULL;
+
+ if (argc == start_arg + 1)
+ block_name = argv[start_arg];
+ else if (argc != start_arg)
+ return -1;
+
+ if (!block_name || !strcmp(block_name, "sge"))
+ match += dump_block_regs(t3b_sge3_regs, regs);
+ if (!block_name || !strcmp(block_name, "pci"))
+ match += dump_block_regs(is_pcie ? t3b_pcie0_regs :
+ t3b_pcix1_regs, regs);
+ if (!block_name || !strcmp(block_name, "t3dbg"))
+ match += dump_block_regs(t3b_t3dbg_regs, regs);
+ if (!block_name || !strcmp(block_name, "pmrx"))
+ match += dump_block_regs(t3b_mc7_pmrx_regs, regs);
+ if (!block_name || !strcmp(block_name, "pmtx"))
+ match += dump_block_regs(t3b_mc7_pmtx_regs, regs);
+ if (!block_name || !strcmp(block_name, "cm"))
+ match += dump_block_regs(t3b_mc7_cm_regs, regs);
+ if (!block_name || !strcmp(block_name, "cim"))
+ match += dump_block_regs(t3b_cim_regs, regs);
+ if (!block_name || !strcmp(block_name, "tp"))
+ match += dump_block_regs(t3b_tp1_regs, regs);
+ if (!block_name || !strcmp(block_name, "ulp_rx"))
+ match += dump_block_regs(t3b_ulp2_rx_regs, regs);
+ if (!block_name || !strcmp(block_name, "ulp_tx"))
+ match += dump_block_regs(t3b_ulp2_tx_regs, regs);
+ if (!block_name || !strcmp(block_name, "pmrx"))
+ match += dump_block_regs(t3b_pm1_rx_regs, regs);
+ if (!block_name || !strcmp(block_name, "pmtx"))
+ match += dump_block_regs(t3b_pm1_tx_regs, regs);
+ if (!block_name || !strcmp(block_name, "mps"))
+ match += dump_block_regs(t3b_mps0_regs, regs);
+ if (!block_name || !strcmp(block_name, "cplsw"))
+ match += dump_block_regs(t3b_cpl_switch_regs, regs);
+ if (!block_name || !strcmp(block_name, "smb"))
+ match += dump_block_regs(t3b_smb0_regs, regs);
+ if (!block_name || !strcmp(block_name, "i2c"))
+ match += dump_block_regs(t3b_i2cm0_regs, regs);
+ if (!block_name || !strcmp(block_name, "mi1"))
+ match += dump_block_regs(t3b_mi1_regs, regs);
+ if (!block_name || !strcmp(block_name, "sf"))
+ match += dump_block_regs(t3b_sf1_regs, regs);
+ if (!block_name || !strcmp(block_name, "pl"))
+ match += dump_block_regs(t3b_pl3_regs, regs);
+ if (!block_name || !strcmp(block_name, "mc5"))
+ match += dump_block_regs(t3b_mc5a_regs, regs);
+ if (!block_name || !strcmp(block_name, "xgmac0"))
+ match += dump_block_regs(t3b_xgmac0_0_regs, regs);
+ if (!block_name || !strcmp(block_name, "xgmac1"))
+ match += dump_block_regs(t3b_xgmac0_1_regs, regs);
+ if (!match)
+ errx(1, "unknown block \"%s\"", block_name);
+ return 0;
+}
+
+static int dump_regs_t3c(int argc, char *argv[], int start_arg, uint32_t *regs,
+ int is_pcie)
+{
+ int match = 0;
+ char *block_name = NULL;
+
+ if (argc == start_arg + 1)
+ block_name = argv[start_arg];
+ else if (argc != start_arg)
+ return -1;
+
+ if (!block_name || !strcmp(block_name, "sge"))
+ match += dump_block_regs(t3c_sge3_regs, regs);
+ if (!block_name || !strcmp(block_name, "pci"))
+ match += dump_block_regs(is_pcie ? t3c_pcie0_regs :
+ t3c_pcix1_regs, regs);
+ if (!block_name || !strcmp(block_name, "t3dbg"))
+ match += dump_block_regs(t3c_t3dbg_regs, regs);
+ if (!block_name || !strcmp(block_name, "pmrx"))
+ match += dump_block_regs(t3c_mc7_pmrx_regs, regs);
+ if (!block_name || !strcmp(block_name, "pmtx"))
+ match += dump_block_regs(t3c_mc7_pmtx_regs, regs);
+ if (!block_name || !strcmp(block_name, "cm"))
+ match += dump_block_regs(t3c_mc7_cm_regs, regs);
+ if (!block_name || !strcmp(block_name, "cim"))
+ match += dump_block_regs(t3c_cim_regs, regs);
+ if (!block_name || !strcmp(block_name, "tp"))
+ match += dump_block_regs(t3c_tp1_regs, regs);
+ if (!block_name || !strcmp(block_name, "ulp_rx"))
+ match += dump_block_regs(t3c_ulp2_rx_regs, regs);
+ if (!block_name || !strcmp(block_name, "ulp_tx"))
+ match += dump_block_regs(t3c_ulp2_tx_regs, regs);
+ if (!block_name || !strcmp(block_name, "pmrx"))
+ match += dump_block_regs(t3c_pm1_rx_regs, regs);
+ if (!block_name || !strcmp(block_name, "pmtx"))
+ match += dump_block_regs(t3c_pm1_tx_regs, regs);
+ if (!block_name || !strcmp(block_name, "mps"))
+ match += dump_block_regs(t3c_mps0_regs, regs);
+ if (!block_name || !strcmp(block_name, "cplsw"))
+ match += dump_block_regs(t3c_cpl_switch_regs, regs);
+ if (!block_name || !strcmp(block_name, "smb"))
+ match += dump_block_regs(t3c_smb0_regs, regs);
+ if (!block_name || !strcmp(block_name, "i2c"))
+ match += dump_block_regs(t3c_i2cm0_regs, regs);
+ if (!block_name || !strcmp(block_name, "mi1"))
+ match += dump_block_regs(t3c_mi1_regs, regs);
+ if (!block_name || !strcmp(block_name, "sf"))
+ match += dump_block_regs(t3c_sf1_regs, regs);
+ if (!block_name || !strcmp(block_name, "pl"))
+ match += dump_block_regs(t3c_pl3_regs, regs);
+ if (!block_name || !strcmp(block_name, "mc5"))
+ match += dump_block_regs(t3c_mc5a_regs, regs);
+ if (!block_name || !strcmp(block_name, "xgmac0"))
+ match += dump_block_regs(t3c_xgmac0_0_regs, regs);
+ if (!block_name || !strcmp(block_name, "xgmac1"))
+ match += dump_block_regs(t3c_xgmac0_1_regs, regs);
+ if (!match)
+ errx(1, "unknown block \"%s\"", block_name);
+ return 0;
+}
+#endif
+
+static int
+dump_regs(int argc, char *argv[], int start_arg, const char *iff_name)
+{
+ int i, vers, revision, is_pcie;
+ struct ch_ifconf_regs regs;
+
+ regs.len = REGDUMP_SIZE;
+
+ /* XXX: This is never freed. Looks like we don't care. */
+ if ((regs.data = malloc(regs.len)) == NULL)
+ err(1, "can't malloc");
+
+ if (doit(iff_name, CHELSIO_IFCONF_GETREGS, &regs))
+ err(1, "can't read registers");
+
+ vers = regs.version & 0x3ff;
+ revision = (regs.version >> 10) & 0x3f;
+ is_pcie = (regs.version & 0x80000000) != 0;
+
+ if (vers <= 2)
+ return dump_regs_t2(argc, argv, start_arg, (uint32_t *)regs.data);
+#if defined(CONFIG_T3_REGS)
+ if (vers == 3) {
+ if (revision == 0)
+ return dump_regs_t3(argc, argv, start_arg,
+ (uint32_t *)regs.data, is_pcie);
+ if (revision == 2 || revision == 3)
+ return dump_regs_t3b(argc, argv, start_arg,
+ (uint32_t *)regs.data, is_pcie);
+ if (revision == 4)
+ return dump_regs_t3c(argc, argv, start_arg,
+ (uint32_t *)regs.data, is_pcie);
+ }
+#endif
+ errx(1, "unknown card type %d.%d", vers, revision);
+ return 0;
+}
+
+static int t3_meminfo(const uint32_t *regs)
+{
+ enum {
+ SG_EGR_CNTX_BADDR = 0x58,
+ SG_CQ_CONTEXT_BADDR = 0x6c,
+ CIM_SDRAM_BASE_ADDR = 0x28c,
+ CIM_SDRAM_ADDR_SIZE = 0x290,
+ TP_CMM_MM_BASE = 0x314,
+ TP_CMM_TIMER_BASE = 0x318,
+ TP_CMM_MM_RX_FLST_BASE = 0x460,
+ TP_CMM_MM_TX_FLST_BASE = 0x464,
+ TP_CMM_MM_PS_FLST_BASE = 0x468,
+ ULPRX_ISCSI_LLIMIT = 0x50c,
+ ULPRX_ISCSI_ULIMIT = 0x510,
+ ULPRX_TDDP_LLIMIT = 0x51c,
+ ULPRX_TDDP_ULIMIT = 0x520,
+ ULPRX_STAG_LLIMIT = 0x52c,
+ ULPRX_STAG_ULIMIT = 0x530,
+ ULPRX_RQ_LLIMIT = 0x534,
+ ULPRX_RQ_ULIMIT = 0x538,
+ ULPRX_PBL_LLIMIT = 0x53c,
+ ULPRX_PBL_ULIMIT = 0x540,
+ };
+
+ unsigned int egr_cntxt = regs[SG_EGR_CNTX_BADDR / 4],
+ cq_cntxt = regs[SG_CQ_CONTEXT_BADDR / 4],
+ timers = regs[TP_CMM_TIMER_BASE / 4] & 0xfffffff,
+ pstructs = regs[TP_CMM_MM_BASE / 4],
+ pstruct_fl = regs[TP_CMM_MM_PS_FLST_BASE / 4],
+ rx_fl = regs[TP_CMM_MM_RX_FLST_BASE / 4],
+ tx_fl = regs[TP_CMM_MM_TX_FLST_BASE / 4],
+ cim_base = regs[CIM_SDRAM_BASE_ADDR / 4],
+ cim_size = regs[CIM_SDRAM_ADDR_SIZE / 4];
+ unsigned int iscsi_ll = regs[ULPRX_ISCSI_LLIMIT / 4],
+ iscsi_ul = regs[ULPRX_ISCSI_ULIMIT / 4],
+ tddp_ll = regs[ULPRX_TDDP_LLIMIT / 4],
+ tddp_ul = regs[ULPRX_TDDP_ULIMIT / 4],
+ stag_ll = regs[ULPRX_STAG_LLIMIT / 4],
+ stag_ul = regs[ULPRX_STAG_ULIMIT / 4],
+ rq_ll = regs[ULPRX_RQ_LLIMIT / 4],
+ rq_ul = regs[ULPRX_RQ_ULIMIT / 4],
+ pbl_ll = regs[ULPRX_PBL_LLIMIT / 4],
+ pbl_ul = regs[ULPRX_PBL_ULIMIT / 4];
+
+ printf("CM memory map:\n");
+ printf(" TCB region: 0x%08x - 0x%08x [%u]\n", 0, egr_cntxt - 1,
+ egr_cntxt);
+ printf(" Egress contexts: 0x%08x - 0x%08x [%u]\n", egr_cntxt,
+ cq_cntxt - 1, cq_cntxt - egr_cntxt);
+ printf(" CQ contexts: 0x%08x - 0x%08x [%u]\n", cq_cntxt,
+ timers - 1, timers - cq_cntxt);
+ printf(" Timers: 0x%08x - 0x%08x [%u]\n", timers,
+ pstructs - 1, pstructs - timers);
+ printf(" Pstructs: 0x%08x - 0x%08x [%u]\n", pstructs,
+ pstruct_fl - 1, pstruct_fl - pstructs);
+ printf(" Pstruct FL: 0x%08x - 0x%08x [%u]\n", pstruct_fl,
+ rx_fl - 1, rx_fl - pstruct_fl);
+ printf(" Rx FL: 0x%08x - 0x%08x [%u]\n", rx_fl, tx_fl - 1,
+ tx_fl - rx_fl);
+ printf(" Tx FL: 0x%08x - 0x%08x [%u]\n", tx_fl, cim_base - 1,
+ cim_base - tx_fl);
+ printf(" uP RAM: 0x%08x - 0x%08x [%u]\n", cim_base,
+ cim_base + cim_size - 1, cim_size);
+
+ printf("\nPMRX memory map:\n");
+ printf(" iSCSI region: 0x%08x - 0x%08x [%u]\n", iscsi_ll, iscsi_ul,
+ iscsi_ul - iscsi_ll + 1);
+ printf(" TCP DDP region: 0x%08x - 0x%08x [%u]\n", tddp_ll, tddp_ul,
+ tddp_ul - tddp_ll + 1);
+ printf(" TPT region: 0x%08x - 0x%08x [%u]\n", stag_ll, stag_ul,
+ stag_ul - stag_ll + 1);
+ printf(" RQ region: 0x%08x - 0x%08x [%u]\n", rq_ll, rq_ul,
+ rq_ul - rq_ll + 1);
+ printf(" PBL region: 0x%08x - 0x%08x [%u]\n", pbl_ll, pbl_ul,
+ pbl_ul - pbl_ll + 1);
+ return 0;
+}
+
+static int meminfo(int argc, char *argv[], int start_arg, const char *iff_name)
+{
+ int vers;
+ struct ch_ifconf_regs regs;
+
+ regs.len = REGDUMP_SIZE;
+ if ((regs.data = malloc(regs.len)) == NULL)
+ err(1, "can't malloc");
+
+ if (doit(iff_name, CHELSIO_IFCONF_GETREGS, &regs))
+ err(1, "can't read registers");
+
+ vers = regs.version & 0x3ff;
+ if (vers == 3)
+ return t3_meminfo((uint32_t *)regs.data);
+
+ errx(1, "unknown card type %d", vers);
+ return 0;
+}
+
+static int mtu_tab_op(int argc, char *argv[], int start_arg,
+ const char *iff_name)
+{
+ struct ch_mtus m;
+ int i;
+
+ if (argc == start_arg) {
+ if (doit(iff_name, CHELSIO_GETMTUTAB, &m) < 0)
+ err(1, "get MTU table");
+ for (i = 0; i < m.nmtus; ++i)
+ printf("%u ", m.mtus[i]);
+ printf("\n");
+ } else if (argc <= start_arg + NMTUS) {
+ m.nmtus = argc - start_arg;
+
+ for (i = 0; i < m.nmtus; ++i) {
+ char *p;
+ unsigned long mt = strtoul(argv[start_arg + i], &p, 0);
+
+ if (*p || mt > 9600) {
+ warnx("bad parameter \"%s\"",
+ argv[start_arg + i]);
+ return -1;
+ }
+ if (i && mt < m.mtus[i - 1])
+ errx(1, "MTUs must be in ascending order");
+ m.mtus[i] = mt;
+ }
+ if (doit(iff_name, CHELSIO_SETMTUTAB, &m) < 0)
+ err(1, "set MTU table");
+ } else
+ return -1;
+
+ return 0;
+}
+
+#ifdef CHELSIO_INTERNAL
+static void show_egress_cntxt(uint32_t data[])
+{
+ printf("credits: %u\n", data[0] & 0x7fff);
+ printf("GTS: %u\n", (data[0] >> 15) & 1);
+ printf("index: %u\n", data[0] >> 16);
+ printf("queue size: %u\n", data[1] & 0xffff);
+ printf("base address: 0x%llx\n",
+ ((data[1] >> 16) | ((uint64_t)data[2] << 16) |
+ (((uint64_t)data[3] & 0xf) << 48)) << 12);
+ printf("rsp queue #: %u\n", (data[3] >> 4) & 7);
+ printf("cmd queue #: %u\n", (data[3] >> 7) & 1);
+ printf("TUN: %u\n", (data[3] >> 8) & 1);
+ printf("TOE: %u\n", (data[3] >> 9) & 1);
+ printf("generation: %u\n", (data[3] >> 10) & 1);
+ printf("uP token: %u\n", (data[3] >> 11) & 0xfffff);
+ printf("valid: %u\n", (data[3] >> 31) & 1);
+}
+
+static void show_fl_cntxt(uint32_t data[])
+{
+ printf("base address: 0x%llx\n",
+ ((uint64_t)data[0] | ((uint64_t)data[1] & 0xfffff) << 32) << 12);
+ printf("index: %u\n", (data[1] >> 20) | ((data[2] & 0xf) << 12));
+ printf("queue size: %u\n", (data[2] >> 4) & 0xffff);
+ printf("generation: %u\n", (data[2] >> 20) & 1);
+ printf("entry size: %u\n",
+ (data[2] >> 21) | (data[3] & 0x1fffff) << 11);
+ printf("congest thr: %u\n", (data[3] >> 21) & 0x3ff);
+ printf("GTS: %u\n", (data[3] >> 31) & 1);
+}
+
+static void show_response_cntxt(uint32_t data[])
+{
+ printf("index: %u\n", data[0] & 0xffff);
+ printf("size: %u\n", data[0] >> 16);
+ printf("base address: 0x%llx\n",
+ ((uint64_t)data[1] | ((uint64_t)data[2] & 0xfffff) << 32) << 12);
+ printf("MSI-X/RspQ: %u\n", (data[2] >> 20) & 0x3f);
+ printf("intr enable: %u\n", (data[2] >> 26) & 1);
+ printf("intr armed: %u\n", (data[2] >> 27) & 1);
+ printf("generation: %u\n", (data[2] >> 28) & 1);
+ printf("CQ mode: %u\n", (data[2] >> 31) & 1);
+ printf("FL threshold: %u\n", data[3]);
+}
+
+static void show_cq_cntxt(uint32_t data[])
+{
+ printf("index: %u\n", data[0] & 0xffff);
+ printf("size: %u\n", data[0] >> 16);
+ printf("base address: 0x%llx\n",
+ ((uint64_t)data[1] | ((uint64_t)data[2] & 0xfffff) << 32) << 12);
+ printf("rsp queue #: %u\n", (data[2] >> 20) & 0x3f);
+ printf("AN: %u\n", (data[2] >> 26) & 1);
+ printf("armed: %u\n", (data[2] >> 27) & 1);
+ printf("ANS: %u\n", (data[2] >> 28) & 1);
+ printf("generation: %u\n", (data[2] >> 29) & 1);
+ printf("overflow mode: %u\n", (data[2] >> 31) & 1);
+ printf("credits: %u\n", data[3] & 0xffff);
+ printf("credit threshold: %u\n", data[3] >> 16);
+}
+
+static int get_sge_context(int argc, char *argv[], int start_arg,
+ const char *iff_name)
+{
+ struct ch_cntxt ctx;
+
+ if (argc != start_arg + 2) return -1;
+
+ if (!strcmp(argv[start_arg], "egress"))
+ ctx.cntxt_type = CNTXT_TYPE_EGRESS;
+ else if (!strcmp(argv[start_arg], "fl"))
+ ctx.cntxt_type = CNTXT_TYPE_FL;
+ else if (!strcmp(argv[start_arg], "response"))
+ ctx.cntxt_type = CNTXT_TYPE_RSP;
+ else if (!strcmp(argv[start_arg], "cq"))
+ ctx.cntxt_type = CNTXT_TYPE_CQ;
+ else {
+ warnx("unknown context type \"%s\"; known types are egress, "
+ "fl, cq, and response", argv[start_arg]);
+ return -1;
+ }
+
+ if (get_int_arg(argv[start_arg + 1], &ctx.cntxt_id))
+ return -1;
+
+ if (doit(iff_name, CHELSIO_GET_SGE_CONTEXT, &ctx) < 0)
+ err(1, "get SGE context");
+
+ if (!strcmp(argv[start_arg], "egress"))
+ show_egress_cntxt(ctx.data);
+ else if (!strcmp(argv[start_arg], "fl"))
+ show_fl_cntxt(ctx.data);
+ else if (!strcmp(argv[start_arg], "response"))
+ show_response_cntxt(ctx.data);
+ else if (!strcmp(argv[start_arg], "cq"))
+ show_cq_cntxt(ctx.data);
+ return 0;
+}
+
+#define ntohll(x) be64toh((x))
+
+static int get_sge_desc(int argc, char *argv[], int start_arg,
+ const char *iff_name)
+{
+ uint64_t *p, wr_hdr;
+ unsigned int n = 1, qset, qnum;
+ struct ch_desc desc;
+
+ if (argc != start_arg + 3 && argc != start_arg + 4)
+ return -1;
+
+ if (get_int_arg(argv[start_arg], &qset) ||
+ get_int_arg(argv[start_arg + 1], &qnum) ||
+ get_int_arg(argv[start_arg + 2], &desc.idx))
+ return -1;
+
+ if (argc == start_arg + 4 && get_int_arg(argv[start_arg + 3], &n))
+ return -1;
+
+ if (qnum > 5)
+ errx(1, "invalid queue number %d, range is 0..5", qnum);
+
+ desc.queue_num = qset * 6 + qnum;
+
+ for (; n--; desc.idx++) {
+ if (doit(iff_name, CHELSIO_GET_SGE_DESC, &desc) < 0)
+ err(1, "get SGE descriptor");
+
+ p = (uint64_t *)desc.data;
+ wr_hdr = ntohll(*p);
+ printf("Descriptor %u: cmd %u, TID %u, %s%s%s%s%u flits\n",
+ desc.idx, (unsigned int)(wr_hdr >> 56),
+ ((unsigned int)wr_hdr >> 8) & 0xfffff,
+ ((wr_hdr >> 55) & 1) ? "SOP, " : "",
+ ((wr_hdr >> 54) & 1) ? "EOP, " : "",
+ ((wr_hdr >> 53) & 1) ? "COMPL, " : "",
+ ((wr_hdr >> 52) & 1) ? "SGL, " : "",
+ (unsigned int)wr_hdr & 0xff);
+
+ for (; desc.size; p++, desc.size -= sizeof(uint64_t))
+ printf("%016" PRIx64 "%c", ntohll(*p),
+ desc.size % 32 == 8 ? '\n' : ' ');
+ }
+ return 0;
+}
+#endif
+
+static int get_tcb2(int argc, char *argv[], int start_arg, const char *iff_name)
+{
+ uint64_t *d;
+ unsigned int i;
+ unsigned int tcb_idx;
+ struct ch_mem_range mr;
+
+ if (argc != start_arg + 1)
+ return -1;
+
+ if (get_int_arg(argv[start_arg], &tcb_idx))
+ return -1;
+
+ mr.buf = calloc(1, TCB_SIZE);
+ if (!mr.buf)
+ err(1, "get TCB");
+
+ mr.mem_id = MEM_CM;
+ mr.addr = tcb_idx * TCB_SIZE;
+ mr.len = TCB_SIZE;
+
+ if (doit(iff_name, CHELSIO_GET_MEM, &mr) < 0)
+ err(1, "get TCB");
+
+ for (d = (uint64_t *)mr.buf, i = 0; i < TCB_SIZE / 32; i++) {
+ printf("%2u:", i);
+ printf(" %08x %08x %08x %08x", (uint32_t)d[1],
+ (uint32_t)(d[1] >> 32), (uint32_t)d[0],
+ (uint32_t)(d[0] >> 32));
+ d += 2;
+ printf(" %08x %08x %08x %08x\n", (uint32_t)d[1],
+ (uint32_t)(d[1] >> 32), (uint32_t)d[0],
+ (uint32_t)(d[0] >> 32));
+ d += 2;
+ }
+ free(mr.buf);
+ return 0;
+}
+
+static int get_pm_page_spec(const char *s, unsigned int *page_size,
+ unsigned int *num_pages)
+{
+ char *p;
+ unsigned long val;
+
+ val = strtoul(s, &p, 0);
+ if (p == s) return -1;
+ if (*p == 'x' && p[1]) {
+ *num_pages = val;
+ *page_size = strtoul(p + 1, &p, 0);
+ } else {
+ *num_pages = -1;
+ *page_size = val;
+ }
+ *page_size <<= 10; // KB -> bytes
+ return *p;
+}
+
+static int conf_pm(int argc, char *argv[], int start_arg, const char *iff_name)
+{
+ struct ch_pm pm;
+
+ if (argc == start_arg) {
+ if (doit(iff_name, CHELSIO_GET_PM, &pm) < 0)
+ err(1, "read pm config");
+ printf("%ux%uKB TX pages, %ux%uKB RX pages, %uKB total memory\n",
+ pm.tx_num_pg, pm.tx_pg_sz >> 10, pm.rx_num_pg,
+ pm.rx_pg_sz >> 10, pm.pm_total >> 10);
+ return 0;
+ }
+
+ if (argc != start_arg + 2) return -1;
+
+ if (get_pm_page_spec(argv[start_arg], &pm.tx_pg_sz, &pm.tx_num_pg)) {
+ warnx("bad parameter \"%s\"", argv[start_arg]);
+ return -1;
+ }
+ if (get_pm_page_spec(argv[start_arg + 1], &pm.rx_pg_sz,
+ &pm.rx_num_pg)) {
+ warnx("bad parameter \"%s\"", argv[start_arg + 1]);
+ return -1;
+ }
+ if (doit(iff_name, CHELSIO_SET_PM, &pm) < 0)
+ err(1, "pm config");
+ return 0;
+}
+
+#ifdef CHELSIO_INTERNAL
+static int dump_tcam(int argc, char *argv[], int start_arg,
+ const char *iff_name)
+{
+ unsigned int nwords;
+ struct ch_tcam_word op;
+
+ if (argc != start_arg + 2) return -1;
+
+ if (get_int_arg(argv[start_arg], &op.addr) ||
+ get_int_arg(argv[start_arg + 1], &nwords))
+ return -1;
+
+ while (nwords--) {
+ if (doit(iff_name, CHELSIO_READ_TCAM_WORD, &op) < 0)
+ err(1, "tcam dump");
+
+ printf("0x%08x: 0x%02x 0x%08x 0x%08x\n", op.addr,
+ op.buf[0] & 0xff, op.buf[1], op.buf[2]);
+ op.addr++;
+ }
+ return 0;
+}
+
+static void hexdump_8b(unsigned int start, uint64_t *data, unsigned int len)
+{
+ int i;
+
+ while (len) {
+ printf("0x%08x:", start);
+ for (i = 0; i < 4 && len; ++i, --len)
+ printf(" %016llx", (unsigned long long)*data++);
+ printf("\n");
+ start += 32;
+ }
+}
+
+static int dump_mc7(int argc, char *argv[], int start_arg,
+ const char *iff_name)
+{
+ struct ch_mem_range mem;
+ unsigned int mem_id, addr, len;
+
+ if (argc != start_arg + 3) return -1;
+
+ if (!strcmp(argv[start_arg], "cm"))
+ mem_id = MEM_CM;
+ else if (!strcmp(argv[start_arg], "rx"))
+ mem_id = MEM_PMRX;
+ else if (!strcmp(argv[start_arg], "tx"))
+ mem_id = MEM_PMTX;
+ else
+ errx(1, "unknown memory \"%s\"; must be one of \"cm\", \"tx\","
+ " or \"rx\"", argv[start_arg]);
+
+ if (get_int_arg(argv[start_arg + 1], &addr) ||
+ get_int_arg(argv[start_arg + 2], &len))
+ return -1;
+
+ mem.buf = malloc(len);
+ if (!mem.buf)
+ err(1, "memory dump");
+
+ mem.mem_id = mem_id;
+ mem.addr = addr;
+ mem.len = len;
+
+ if (doit(iff_name, CHELSIO_GET_MEM, &mem) < 0)
+ err(1, "memory dump");
+
+ hexdump_8b(mem.addr, (uint64_t *)mem.buf, mem.len >> 3);
+ free(mem.buf);
+ return 0;
+}
+#endif
+
+/* Max FW size is 32K including version, +4 bytes for the checksum. */
+#define MAX_FW_IMAGE_SIZE (64 * 1024)
+
+static int load_fw(int argc, char *argv[], int start_arg, const char *iff_name)
+{
+ int fd, len;
+ struct ch_mem_range op;
+ const char *fname = argv[start_arg];
+
+ if (argc != start_arg + 1) return -1;
+
+ fd = open(fname, O_RDONLY);
+ if (fd < 0)
+ err(1, "load firmware");
+
+ bzero(&op, sizeof(op));
+ op.buf = malloc(MAX_FW_IMAGE_SIZE + 1);
+ if (!op.buf)
+ err(1, "load firmware");
+
+ op.len = read(fd, op.buf, MAX_FW_IMAGE_SIZE + 1);
+ if (op.len < 0)
+ err(1, "load firmware");
+ if (op.len > MAX_FW_IMAGE_SIZE)
+ errx(1, "FW image too large");
+
+ if (doit(iff_name, CHELSIO_LOAD_FW, &op) < 0)
+ err(1, "load firmware");
+ return 0;
+}
+
+/* Max BOOT size is 255*512 bytes including the BIOS boot ROM basic header */
+#define MAX_BOOT_IMAGE_SIZE (0xff * 512)
+
+static int load_boot(int argc, char *argv[],
+ int start_arg, const char *iff_name)
+{
+ int fd, len;
+ struct ch_mem_range op;
+ const char *fname = argv[start_arg];
+
+ if (argc != start_arg + 1) return -1;
+
+ fd = open(fname, O_RDONLY);
+ if (fd < 0)
+ err(1, "load boot image");
+
+ op.buf = malloc(MAX_BOOT_IMAGE_SIZE + 1);
+ if (!op.buf)
+ err(1, "load boot image");
+
+ len = read(fd, op.buf, MAX_BOOT_IMAGE_SIZE + 1);
+ if (len < 0)
+ err(1, "load boot image");
+ if (len > MAX_BOOT_IMAGE_SIZE)
+ errx(1, "boot image too large");
+
+ op.len = len;
+
+ if (doit(iff_name, CHELSIO_LOAD_BOOT, &op) < 0)
+ err(1, "load boot image");
+
+ return 0;
+}
+
+static int dump_proto_sram(const char *iff_name)
+{
+ int i, j;
+ uint8_t buf[PROTO_SRAM_SIZE];
+ struct ch_eeprom ee;
+ uint8_t *p = buf;
+
+ bzero(buf, sizeof(buf));
+ ee.offset = PROTO_SRAM_EEPROM_ADDR;
+ ee.data = p;
+ ee.len = sizeof(buf);
+ if (doit(iff_name, CHELSIO_GET_EEPROM, &ee))
+ err(1, "show protocol sram");
+
+ for (i = 0; i < PROTO_SRAM_LINES; i++) {
+ for (j = PROTO_SRAM_LINE_NIBBLES - 1; j >= 0; j--) {
+ int nibble_idx = i * PROTO_SRAM_LINE_NIBBLES + j;
+ uint8_t nibble = p[nibble_idx / 2];
+
+ if (nibble_idx & 1)
+ nibble >>= 4;
+ else
+ nibble &= 0xf;
+ printf("%x", nibble);
+ }
+ putchar('\n');
+ }
+ return 0;
+}
+
+static int proto_sram_op(int argc, char *argv[], int start_arg,
+ const char *iff_name)
+{
+ if (argc == start_arg)
+ return dump_proto_sram(iff_name);
+ return -1;
+}
+
+static int dump_qset_params(const char *iff_name)
+{
+ struct ch_qset_params qp;
+
+ qp.qset_idx = 0;
+
+ while (doit(iff_name, CHELSIO_GET_QSET_PARAMS, &qp) == 0) {
+ if (!qp.qset_idx)
+ printf("Qset TxQ0 TxQ1 TxQ2 RspQ RxQ0 RxQ1"
+ " Cong Lat IRQ\n");
+ printf("%4u %6u %6u %6u %6u %6u %6u %5u %4u %5d\n",
+ qp.qnum,
+ qp.txq_size[0], qp.txq_size[1], qp.txq_size[2],
+ qp.rspq_size, qp.fl_size[0], qp.fl_size[1],
+ qp.cong_thres, qp.intr_lat, qp.vector);
+ qp.qset_idx++;
+ }
+ if (!qp.qset_idx || (errno && errno != EINVAL))
+ err(1, "get qset parameters");
+ return 0;
+}
+
+static int qset_config(int argc, char *argv[], int start_arg,
+ const char *iff_name)
+{
+ struct ch_qset_params qp;
+
+ if (argc == start_arg)
+ return dump_qset_params(iff_name);
+
+ return -1;
+}
+
+static int qset_num_config(int argc, char *argv[], int start_arg,
+ const char *iff_name)
+{
+ struct ch_reg reg;
+
+ if (argc == start_arg) {
+ if (doit(iff_name, CHELSIO_GET_QSET_NUM, &reg) < 0)
+ err(1, "get qsets");
+ printf("%u\n", reg.val);
+ return 0;
+ }
+
+ return -1;
+}
+
+/*
+ * Parse a string containing an IP address with an optional network prefix.
+ */
+static int parse_ipaddr(const char *s, uint32_t *addr, uint32_t *mask)
+{
+ char *p, *slash;
+ struct in_addr ia;
+
+ *mask = 0xffffffffU;
+ slash = strchr(s, '/');
+ if (slash)
+ *slash = 0;
+ if (!inet_aton(s, &ia)) {
+ if (slash)
+ *slash = '/';
+ *addr = 0;
+ return -1;
+ }
+ *addr = ntohl(ia.s_addr);
+ if (slash) {
+ unsigned int prefix = strtoul(slash + 1, &p, 10);
+
+ *slash = '/';
+ if (p == slash + 1 || *p || prefix > 32)
+ return -1;
+ *mask <<= (32 - prefix);
+ }
+ return 0;
+}
+
+/*
+ * Parse a string containing a value and an optional colon separated mask.
+ */
+static int parse_val_mask_param(const char *s, uint32_t *val, uint32_t *mask)
+{
+ char *p;
+
+ *mask = 0xffffffffU;
+ *val = strtoul(s, &p, 0);
+ if (p == s)
+ return -1;
+ if (*p == ':' && p[1])
+ *mask = strtoul(p + 1, &p, 0);
+ return *p ? -1 : 0;
+}
+
+static int parse_trace_param(const char *s, uint32_t *val, uint32_t *mask)
+{
+ return strchr(s, '.') ? parse_ipaddr(s, val, mask) :
+ parse_val_mask_param(s, val, mask);
+}
+
+static int trace_config(int argc, char *argv[], int start_arg,
+ const char *iff_name)
+{
+ uint32_t val, mask;
+ struct ch_trace trace;
+
+ if (argc == start_arg)
+ return -1;
+
+ memset(&trace, 0, sizeof(trace));
+ if (!strcmp(argv[start_arg], "tx"))
+ trace.config_tx = 1;
+ else if (!strcmp(argv[start_arg], "rx"))
+ trace.config_rx = 1;
+ else if (!strcmp(argv[start_arg], "all"))
+ trace.config_tx = trace.config_rx = 1;
+ else
+ errx(1, "bad trace filter \"%s\"; must be one of \"rx\", "
+ "\"tx\" or \"all\"", argv[start_arg]);
+
+ if (argc == ++start_arg)
+ return -1;
+ if (!strcmp(argv[start_arg], "on")) {
+ trace.trace_tx = trace.config_tx;
+ trace.trace_rx = trace.config_rx;
+ } else if (strcmp(argv[start_arg], "off"))
+ errx(1, "bad argument \"%s\"; must be \"on\" or \"off\"",
+ argv[start_arg]);
+
+ start_arg++;
+ if (start_arg < argc && !strcmp(argv[start_arg], "not")) {
+ trace.invert_match = 1;
+ start_arg++;
+ }
+
+ while (start_arg + 2 <= argc) {
+ int ret = parse_trace_param(argv[start_arg + 1], &val, &mask);
+
+ if (!strcmp(argv[start_arg], "interface")) {
+ trace.intf = val;
+ trace.intf_mask = mask;
+ } else if (!strcmp(argv[start_arg], "sip")) {
+ trace.sip = val;
+ trace.sip_mask = mask;
+ } else if (!strcmp(argv[start_arg], "dip")) {
+ trace.dip = val;
+ trace.dip_mask = mask;
+ } else if (!strcmp(argv[start_arg], "sport")) {
+ trace.sport = val;
+ trace.sport_mask = mask;
+ } else if (!strcmp(argv[start_arg], "dport")) {
+ trace.dport = val;
+ trace.dport_mask = mask;
+ } else if (!strcmp(argv[start_arg], "vlan")) {
+ trace.vlan = val;
+ trace.vlan_mask = mask;
+ } else if (!strcmp(argv[start_arg], "proto")) {
+ trace.proto = val;
+ trace.proto_mask = mask;
+ } else
+ errx(1, "unknown trace parameter \"%s\"\n"
+ "known parameters are \"interface\", \"sip\", "
+ "\"dip\", \"sport\", \"dport\", \"vlan\", "
+ "\"proto\"", argv[start_arg]);
+ if (ret < 0)
+ errx(1, "bad parameter \"%s\"", argv[start_arg + 1]);
+ start_arg += 2;
+ }
+ if (start_arg != argc)
+ errx(1, "unknown parameter \"%s\"", argv[start_arg]);
+
+ if (doit(iff_name, CHELSIO_SET_TRACE_FILTER, &trace) < 0)
+ err(1, "trace");
+ return 0;
+}
+
+static int get_sched_param(int argc, char *argv[], int pos, unsigned int *valp)
+{
+ if (pos + 1 >= argc)
+ errx(1, "missing value for %s", argv[pos]);
+ if (get_int_arg(argv[pos + 1], valp))
+ exit(1);
+ return 0;
+}
+
+static int tx_sched(int argc, char *argv[], int start_arg, const char *iff_name)
+{
+ struct ch_hw_sched op;
+ unsigned int idx, val;
+
+ if (argc < 5 || get_int_arg(argv[start_arg++], &idx))
+ return -1;
+
+ op.sched = idx;
+ op.mode = op.channel = -1;
+ op.kbps = op.class_ipg = op.flow_ipg = -1;
+
+ while (argc > start_arg) {
+ if (!strcmp(argv[start_arg], "mode")) {
+ if (start_arg + 1 >= argc)
+ errx(1, "missing value for mode");
+ if (!strcmp(argv[start_arg + 1], "class"))
+ op.mode = 0;
+ else if (!strcmp(argv[start_arg + 1], "flow"))
+ op.mode = 1;
+ else
+ errx(1, "bad mode \"%s\"", argv[start_arg + 1]);
+ } else if (!strcmp(argv[start_arg], "channel") &&
+ !get_sched_param(argc, argv, start_arg, &val))
+ op.channel = val;
+ else if (!strcmp(argv[start_arg], "rate") &&
+ !get_sched_param(argc, argv, start_arg, &val))
+ op.kbps = val;
+ else if (!strcmp(argv[start_arg], "ipg") &&
+ !get_sched_param(argc, argv, start_arg, &val))
+ op.class_ipg = val;
+ else if (!strcmp(argv[start_arg], "flowipg") &&
+ !get_sched_param(argc, argv, start_arg, &val))
+ op.flow_ipg = val;
+ else
+ errx(1, "unknown scheduler parameter \"%s\"",
+ argv[start_arg]);
+ start_arg += 2;
+ }
+
+ if (doit(iff_name, CHELSIO_SET_HW_SCHED, &op) < 0)
+ err(1, "pktsched");
+
+ return 0;
+}
+
+static int pktsched(int argc, char *argv[], int start_arg, const char *iff_name)
+{
+ struct ch_pktsched_params op;
+ unsigned int idx, min = -1, max, binding = -1;
+
+ if (argc < 4)
+ errx(1, "no scheduler specified");
+
+ if (!strcmp(argv[start_arg], "port")) {
+ if (argc != start_arg + 4)
+ return -1;
+ if (get_int_arg(argv[start_arg + 1], &idx) ||
+ get_int_arg(argv[start_arg + 2], &min) ||
+ get_int_arg(argv[start_arg + 3], &max))
+ return -1;
+ op.sched = 0;
+ } else if (!strcmp(argv[start_arg], "tunnelq")) {
+ if (argc != start_arg + 4)
+ return -1;
+ if (get_int_arg(argv[start_arg + 1], &idx) ||
+ get_int_arg(argv[start_arg + 2], &max) ||
+ get_int_arg(argv[start_arg + 3], &binding))
+ return -1;
+ op.sched = 1;
+ } else if (!strcmp(argv[start_arg], "tx"))
+ return tx_sched(argc, argv, start_arg + 1, iff_name);
+ else
+ errx(1, "unknown scheduler \"%s\"; must be one of \"port\", "
+ "\"tunnelq\" or \"tx\"", argv[start_arg]);
+
+ op.idx = idx;
+ op.min = min;
+ op.max = max;
+ op.binding = binding;
+ if (doit(iff_name, CHELSIO_SET_PKTSCHED, &op) < 0)
+ err(1, "pktsched");
+
+ return 0;
+}
+
+static int clear_stats(int argc, char *argv[], int start_arg,
+ const char *iff_name)
+{
+ if (doit(iff_name, CHELSIO_CLEAR_STATS, NULL) < 0)
+ err(1, "clearstats");
+
+ return 0;
+}
+
+static int get_up_la(int argc, char *argv[], int start_arg, const char *iff_name)
+{
+ struct ch_up_la la;
+ int i, idx, max_idx, entries;
+
+ la.stopped = 0;
+ la.idx = -1;
+ la.bufsize = LA_BUFSIZE;
+ la.data = malloc(la.bufsize);
+ if (!la.data)
+ err(1, "uP_LA malloc");
+
+ if (doit(iff_name, CHELSIO_GET_UP_LA, &la) < 0)
+ err(1, "uP_LA");
+
+ if (la.stopped)
+ printf("LA is not running\n");
+
+ entries = la.bufsize / 4;
+ idx = (int)la.idx;
+ max_idx = (entries / 4) - 1;
+ for (i = 0; i < max_idx; i++) {
+ printf("%04x %08x %08x\n",
+ la.data[idx], la.data[idx+2], la.data[idx+1]);
+ idx = (idx + 4) & (entries - 1);
+ }
+
+ return 0;
+}
+
+static int get_up_ioqs(int argc, char *argv[], int start_arg, const char *iff_name)
+{
+ struct ch_up_ioqs ioqs;
+ int i, entries;
+
+ bzero(&ioqs, sizeof(ioqs));
+ ioqs.bufsize = IOQS_BUFSIZE;
+ ioqs.data = malloc(IOQS_BUFSIZE);
+ if (!ioqs.data)
+ err(1, "uP_IOQs malloc");
+
+ if (doit(iff_name, CHELSIO_GET_UP_IOQS, &ioqs) < 0)
+ err(1, "uP_IOQs");
+
+ printf("ioq_rx_enable : 0x%08x\n", ioqs.ioq_rx_enable);
+ printf("ioq_tx_enable : 0x%08x\n", ioqs.ioq_tx_enable);
+ printf("ioq_rx_status : 0x%08x\n", ioqs.ioq_rx_status);
+ printf("ioq_tx_status : 0x%08x\n", ioqs.ioq_tx_status);
+
+ entries = ioqs.bufsize / sizeof(struct t3_ioq_entry);
+ for (i = 0; i < entries; i++) {
+ printf("\nioq[%d].cp : 0x%08x\n", i,
+ ioqs.data[i].ioq_cp);
+ printf("ioq[%d].pp : 0x%08x\n", i,
+ ioqs.data[i].ioq_pp);
+ printf("ioq[%d].alen : 0x%08x\n", i,
+ ioqs.data[i].ioq_alen);
+ printf("ioq[%d].stats : 0x%08x\n", i,
+ ioqs.data[i].ioq_stats);
+ printf(" sop %u\n", ioqs.data[i].ioq_stats >> 16);
+ printf(" eop %u\n", ioqs.data[i].ioq_stats & 0xFFFF);
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int r = -1;
+ const char *iff_name;
+
+ progname = argv[0];
+
+ if (argc == 2) {
+ if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
+ usage(stdout);
+ if (!strcmp(argv[1], "-v") || !strcmp(argv[1], "--version")) {
+ printf("%s version %s\n", PROGNAME, VERSION);
+ printf("%s\n", COPYRIGHT);
+ exit(0);
+ }
+ }
+
+ if (argc < 3) usage(stderr);
+
+ iff_name = argv[1];
+ if (!strcmp(argv[2], "reg"))
+ r = register_io(argc, argv, 3, iff_name);
+ else if (!strcmp(argv[2], "mdio"))
+ r = mdio_io(argc, argv, 3, iff_name);
+ else if (!strcmp(argv[2], "mtus"))
+ r = mtu_tab_op(argc, argv, 3, iff_name);
+ else if (!strcmp(argv[2], "pm"))
+ r = conf_pm(argc, argv, 3, iff_name);
+ else if (!strcmp(argv[2], "regdump"))
+ r = dump_regs(argc, argv, 3, iff_name);
+ else if (!strcmp(argv[2], "tcamdump"))
+ r = dump_tcam(argc, argv, 3, iff_name);
+ else if (!strcmp(argv[2], "memdump"))
+ r = dump_mc7(argc, argv, 3, iff_name);
+ else if (!strcmp(argv[2], "meminfo"))
+ r = meminfo(argc, argv, 3, iff_name);
+ else if (!strcmp(argv[2], "context"))
+ r = get_sge_context(argc, argv, 3, iff_name);
+ else if (!strcmp(argv[2], "desc"))
+ r = get_sge_desc(argc, argv, 3, iff_name);
+ else if (!strcmp(argv[2], "loadfw"))
+ r = load_fw(argc, argv, 3, iff_name);
+ else if (!strcmp(argv[2], "loadboot"))
+ r = load_boot(argc, argv, 3, iff_name);
+ else if (!strcmp(argv[2], "proto"))
+ r = proto_sram_op(argc, argv, 3, iff_name);
+ else if (!strcmp(argv[2], "qset"))
+ r = qset_config(argc, argv, 3, iff_name);
+ else if (!strcmp(argv[2], "qsets"))
+ r = qset_num_config(argc, argv, 3, iff_name);
+ else if (!strcmp(argv[2], "trace"))
+ r = trace_config(argc, argv, 3, iff_name);
+ else if (!strcmp(argv[2], "pktsched"))
+ r = pktsched(argc, argv, 3, iff_name);
+ else if (!strcmp(argv[2], "tcb"))
+ r = get_tcb2(argc, argv, 3, iff_name);
+ else if (!strcmp(argv[2], "clearstats"))
+ r = clear_stats(argc, argv, 3, iff_name);
+ else if (!strcmp(argv[2], "la"))
+ r = get_up_la(argc, argv, 3, iff_name);
+ else if (!strcmp(argv[2], "ioqs"))
+ r = get_up_ioqs(argc, argv, 3, iff_name);
+
+ if (r == -1)
+ usage(stderr);
+
+ return 0;
+}
diff --git a/usr.sbin/cxgbtool/reg_defs.c b/usr.sbin/cxgbtool/reg_defs.c
new file mode 100644
index 0000000..734061f
--- /dev/null
+++ b/usr.sbin/cxgbtool/reg_defs.c
@@ -0,0 +1,837 @@
+/*
+ * $FreeBSD$
+ */
+
+/* This file is automatically generated --- do not edit */
+
+struct reg_info sge_regs[] = {
+ { "SG_CONTROL", 0x0, 0 },
+ { "CmdQ0_Enable", 0, 1 },
+ { "CmdQ1_Enable", 1, 1 },
+ { "FL0_Enable", 2, 1 },
+ { "FL1_Enable", 3, 1 },
+ { "CPL_Enable", 4, 1 },
+ { "Response_Queue_Enable", 5, 1 },
+ { "CmdQ_Priority", 6, 2 },
+ { "Disable_CmdQ0_GTS", 8, 1 },
+ { "Disable_CmdQ1_GTS", 9, 1 },
+ { "Disable_FL0_GTS", 10, 1 },
+ { "Disable_FL1_GTS", 11, 1 },
+ { "Enable_Big_Endian", 12, 1 },
+ { "FL_Selection_Criteria", 13, 1 },
+ { "iSCSI_Coalesce", 14, 1 },
+ { "RX_Pkt_Offset", 15, 3 },
+ { "VLAN_Xtract", 18, 1 },
+ { "SG_DOORBELL", 0x4, 0 },
+ { "CmdQ0_Enable", 0, 1 },
+ { "CmdQ1_Enable", 1, 1 },
+ { "FL0_Enable", 2, 1 },
+ { "FL1_Enable", 3, 1 },
+ { "SG_CMD0BASELWR", 0x8, 0 },
+ { "SG_CMD0BASEUPR", 0xc, 0 },
+ { "SG_CMD1BASELWR", 0x10, 0 },
+ { "SG_CMD1BASEUPR", 0x14, 0 },
+ { "SG_FL0BASELWR", 0x18, 0 },
+ { "SG_FL0BASEUPR", 0x1c, 0 },
+ { "SG_FL1BASELWR", 0x20, 0 },
+ { "SG_FL1BASEUPR", 0x24, 0 },
+ { "SG_CMD0SIZE", 0x28, 0 },
+ { "CmdQ0_Size", 0, 17 },
+ { "SG_FL0SIZE", 0x2c, 0 },
+ { "FL0_Size", 0, 17 },
+ { "SG_RSPSIZE", 0x30, 0 },
+ { "RespQ_Size", 0, 17 },
+ { "SG_RSPBASELWR", 0x34, 0 },
+ { "SG_RSPBASEUPR", 0x38, 0 },
+ { "SG_FLTHRESHOLD", 0x3c, 0 },
+ { "FL_Threshold", 0, 16 },
+ { "SG_RSPQUEUECREDIT", 0x40, 0 },
+ { "RespQ_Credit", 0, 17 },
+ { "SG_DEBUGTXDATAL", 0x44, 0 },
+ { "SG_SLEEPING", 0x48, 0 },
+ { "Sleeping", 0, 16 },
+ { "SG_INTRTIMER", 0x4c, 0 },
+ { "Interrupt_Timer_Count", 0, 24 },
+ { "SG_CMD0PTR", 0x50, 0 },
+ { "CmdQ0_Pointer", 0, 16 },
+ { "Current_Generation_Bit", 16, 1 },
+ { "SG_CMD1PTR", 0x54, 0 },
+ { "CmdQ1_Pointer", 0, 16 },
+ { "Current_Generation_Bit", 16, 1 },
+ { "SG_FL0PTR", 0x58, 0 },
+ { "FL0_Pointer", 0, 16 },
+ { "Current_Generation_Bit", 16, 1 },
+ { "SG_FL1PTR", 0x5c, 0 },
+ { "FL1_Pointer", 0, 16 },
+ { "Current_Generation_Bit", 16, 1 },
+ { "SG_DEBUGTXDATAH", 0x60, 0 },
+ { "SG_DEBUGRXDATAL", 0x64, 0 },
+ { "SG_DEBUGRXDATAH", 0x68, 0 },
+ { "SG_VERSION", 0x6c, 0 },
+ { "Day", 0, 5 },
+ { "Month", 5, 4 },
+ { "SG_DEBUGRXSOP", 0x70, 0 },
+ { "SG_DEBUGTXSOP", 0x74, 0 },
+ { "SG_LA_RDPTR0", 0x78, 0 },
+ { "Logic_Analyzer0_Read_Pointer", 0, 9 },
+ { "SG_LA_RDDATA0", 0x7c, 0 },
+ { "SG_LA_WRPTR0", 0x80, 0 },
+ { "SG_DEBUGRXEOP", 0x84, 0 },
+ { "SG_DEBUGTXEOP", 0x88, 0 },
+ { "SG_DEBUGRXSIZE", 0x8c, 0 },
+ { "SG_DEBUGTXSIZE", 0x90, 0 },
+ { "SG_NUMBER_LA", 0x94, 0 },
+ { "SG_LA_RDPTR1", 0x98, 0 },
+ { "Logic_Analyzer1_Read_Pointer", 0, 9 },
+ { "SG_LA_RDDATA1", 0x9c, 0 },
+ { "SG_LA_WRPTR1", 0xa0, 0 },
+ { "SG_LA_RDPTR2", 0xa4, 0 },
+ { "Logic_Analyzer2_Read_Pointer", 0, 9 },
+ { "SG_LA_RDDATA2", 0xa8, 0 },
+ { "SG_LA_WRPTR2", 0xac, 0 },
+ { "SG_CMD1SIZE", 0xb0, 0 },
+ { "CmdQ1_Size", 0, 17 },
+ { "SG_FL1SIZE", 0xb4, 0 },
+ { "FL1_Size", 0, 17 },
+ { "SG_INT_ENABLE", 0xb8, 0 },
+ { "RespQ_Exhausted", 0, 1 },
+ { "RespQ_Overflow", 1, 1 },
+ { "FL_Exhausted", 2, 1 },
+ { "Packet_Too_Big", 3, 1 },
+ { "Packet_Mismatch", 4, 1 },
+ { "SG_INT_CAUSE", 0xbc, 0 },
+ { "RespQ_Exhausted", 0, 1 },
+ { "RespQ_Overflow", 1, 1 },
+ { "FL_Exhausted", 2, 1 },
+ { "Packet_Too_Big", 3, 1 },
+ { "Packet_Mismatch", 4, 1 },
+ { "SG_RESPACCUTIMER", 0xc0, 0 },
+ { NULL }
+};
+
+struct reg_info mc3_regs[] = {
+ { "MC3_CFG", 0x100, 0 },
+ { "Clk_Enable", 0, 1 },
+ { "Ready", 1, 1 },
+ { "Read_to_Write_Delay", 2, 3 },
+ { "Write_to_Read_Delay", 5, 3 },
+ { "MC3_Bank_Cycle", 8, 4 },
+ { "Refresh_Cycle", 12, 4 },
+ { "Precharge_Cycle", 16, 2 },
+ { "Active_to_Read_Write_Delay", 18, 1 },
+ { "Active_to_Precharge_Delay", 19, 3 },
+ { "Write_Recovery_Delay", 22, 2 },
+ { "Density", 24, 2 },
+ { "Organization", 26, 1 },
+ { "Banks", 27, 1 },
+ { "Unregistered", 28, 1 },
+ { "MC3_Width", 29, 2 },
+ { "MC3_Slow", 31, 1 },
+ { "MC3_MODE", 0x104, 0 },
+ { "MC3_Mode", 0, 14 },
+ { "Busy", 31, 1 },
+ { "MC3_EXT_MODE", 0x108, 0 },
+ { "MC3_Extended_Mode", 0, 14 },
+ { "Busy", 31, 1 },
+ { "MC3_PRECHARG", 0x10c, 0 },
+ { "Busy", 31, 1 },
+ { "MC3_REFRESH", 0x110, 0 },
+ { "Refresh_Enable", 0, 1 },
+ { "Refresh_Divisor", 1, 14 },
+ { "Busy", 31, 1 },
+ { "MC3_STROBE", 0x114, 0 },
+ { "Master_DLL_Reset", 0, 1 },
+ { "Master_DLL_Tap_Count", 1, 8 },
+ { "Master_DLL_Locked", 9, 1 },
+ { "Master_DLL_Max_Tap_Count", 10, 1 },
+ { "Master_DLL_Tap_Count_Offset", 11, 6 },
+ { "Slave_DLL_Reset", 11, 1 },
+ { "Slave_DLL_Delta", 12, 4 },
+ { "Slave_Delay_Line_Manual_Tap_Count", 17, 6 },
+ { "Slave_Delay_Line_Manual_Tap_Count_Enable", 23, 1 },
+ { "Slave_Delay_Line_Tap_Count", 24, 6 },
+ { "MC3_ECC_CNTL", 0x118, 0 },
+ { "ECC_Generation_Enable", 0, 1 },
+ { "ECC_Check_Enable", 1, 1 },
+ { "Correctable_Error_Count", 2, 8 },
+ { "Uncorrectable_Error_Count", 10, 8 },
+ { "MC3_CE_ADDR", 0x11c, 0 },
+ { "MC3_CE_Addr", 4, 28 },
+ { "MC3_CE_DATA0", 0x120, 0 },
+ { "MC3_CE_DATA1", 0x124, 0 },
+ { "MC3_CE_DATA2", 0x128, 0 },
+ { "MC3_CE_DATA3", 0x12c, 0 },
+ { "MC3_CE_DATA4", 0x130, 0 },
+ { "MC3_UE_ADDR", 0x134, 0 },
+ { "MC3_UE_Addr", 4, 28 },
+ { "MC3_UE_DATA0", 0x138, 0 },
+ { "MC3_UE_DATA1", 0x13c, 0 },
+ { "MC3_UE_DATA2", 0x140, 0 },
+ { "MC3_UE_DATA3", 0x144, 0 },
+ { "MC3_UE_DATA4", 0x148, 0 },
+ { "MC3_BD_ADDR", 0x14c, 0 },
+ { "MC3_BD_DATA0", 0x150, 0 },
+ { "MC3_BD_DATA1", 0x154, 0 },
+ { "MC3_BD_DATA2", 0x158, 0 },
+ { "MC3_BD_DATA3", 0x15c, 0 },
+ { "MC3_BD_DATA4", 0x160, 0 },
+ { "MC3_BD_OP", 0x164, 0 },
+ { "Back_Door_Operation", 0, 1 },
+ { "Busy", 31, 1 },
+ { "MC3_BIST_ADDR_BEG", 0x168, 0 },
+ { "MC3_BIST_ADDR_END", 0x16c, 0 },
+ { "MC3_BIST_DATA", 0x170, 0 },
+ { "MC3_BIST_OP", 0x174, 0 },
+ { "Op", 0, 1 },
+ { "Data_Pattern", 1, 2 },
+ { "Continuous", 3, 1 },
+ { "Busy", 31, 1 },
+ { "MC3_INT_ENABLE", 0x178, 0 },
+ { "MC3_Corr_Err", 0, 1 },
+ { "MC3_Uncorr_Err", 1, 1 },
+ { "MC3_Parity_Err", 2, 8 },
+ { "MC3_Addr_Err", 10, 1 },
+ { "MC3_INT_CAUSE", 0x17c, 0 },
+ { "MC3_Corr_Err", 0, 1 },
+ { "MC3_Uncorr_Err", 1, 1 },
+ { "MC3_Parity_Err", 2, 8 },
+ { "MC3_Addr_Err", 10, 1 },
+ { NULL }
+};
+
+struct reg_info mc4_regs[] = {
+ { "MC4_CFG", 0x180, 0 },
+ { "Power_Up", 0, 1 },
+ { "Ready", 1, 1 },
+ { "Read_to_Write_Delay", 2, 3 },
+ { "Write_to_Read_Delay", 5, 3 },
+ { "MC4_Bank_Cycle", 8, 3 },
+ { "MC4_Narrow", 24, 1 },
+ { "MC4_Slow", 25, 1 },
+ { "MC4A_Width", 24, 2 },
+ { "MC4A_Slow", 26, 1 },
+ { "MC4_MODE", 0x184, 0 },
+ { "MC4_Mode", 0, 15 },
+ { "Busy", 31, 1 },
+ { "MC4_EXT_MODE", 0x188, 0 },
+ { "MC4_Extended_Mode", 0, 15 },
+ { "Busy", 31, 1 },
+ { "MC4_REFRESH", 0x190, 0 },
+ { "Refresh_Enable", 0, 1 },
+ { "Refresh_Divisor", 1, 14 },
+ { "Busy", 31, 1 },
+ { "MC4_STROBE", 0x194, 0 },
+ { "Master_DLL_Reset", 0, 1 },
+ { "Master_DLL_Tap_Count", 1, 8 },
+ { "Master_DLL_Locked", 9, 1 },
+ { "Master_DLL_Max_Tap_Count", 10, 1 },
+ { "Master_DLL_Tap_Count_Offset", 11, 6 },
+ { "Slave_DLL_Reset", 11, 1 },
+ { "Slave_DLL_Delta", 12, 4 },
+ { "Slave_Delay_Line_Manual_Tap_Count", 17, 6 },
+ { "Slave_Delay_Line_Manual_Tap_Count_Enable", 23, 1 },
+ { "Slave_Delay_Line_Tap_Count", 24, 6 },
+ { "MC4_ECC_CNTL", 0x198, 0 },
+ { "ECC_Generation_Enable", 0, 1 },
+ { "ECC_Check_Enable", 1, 1 },
+ { "Correctable_Error_Count", 2, 8 },
+ { "Uncorrectable_Error_Count", 10, 8 },
+ { "MC4_CE_ADDR", 0x19c, 0 },
+ { "MC4_CE_Addr", 4, 24 },
+ { "MC4_CE_DATA0", 0x1a0, 0 },
+ { "MC4_CE_DATA1", 0x1a4, 0 },
+ { "MC4_CE_DATA2", 0x1a8, 0 },
+ { "MC4_CE_DATA3", 0x1ac, 0 },
+ { "MC4_CE_DATA4", 0x1b0, 0 },
+ { "MC4_UE_ADDR", 0x1b4, 0 },
+ { "MC4_UE_Addr", 4, 24 },
+ { "MC4_UE_DATA0", 0x1b8, 0 },
+ { "MC4_UE_DATA1", 0x1bc, 0 },
+ { "MC4_UE_DATA2", 0x1c0, 0 },
+ { "MC4_UE_DATA3", 0x1c4, 0 },
+ { "MC4_UE_DATA4", 0x1c8, 0 },
+ { "MC4_BD_ADDR", 0x1cc, 0 },
+ { "MC4_Back_Door_Addr", 0, 28 },
+ { "MC4_BD_DATA0", 0x1d0, 0 },
+ { "MC4_BD_DATA1", 0x1d4, 0 },
+ { "MC4_BD_DATA2", 0x1d8, 0 },
+ { "MC4_BD_DATA3", 0x1dc, 0 },
+ { "MC4_BD_DATA4", 0x1e0, 0 },
+ { "MC4_BD_OP", 0x1e4, 0 },
+ { "Operation", 0, 1 },
+ { "Busy", 31, 1 },
+ { "MC4_BIST_ADDR_BEG", 0x1e8, 0 },
+ { "MC4_BIST_ADDR_END", 0x1ec, 0 },
+ { "MC4_BIST_DATA", 0x1f0, 0 },
+ { "MC4_BIST_OP", 0x1f4, 0 },
+ { "Op", 0, 1 },
+ { "Data_Pattern", 1, 2 },
+ { "Continuous", 3, 1 },
+ { "Busy", 31, 1 },
+ { "MC4_INT_ENABLE", 0x1f8, 0 },
+ { "MC4_Corr_Err", 0, 1 },
+ { "MC4_Uncorr_Err", 1, 1 },
+ { "MC4_Addr_Err", 2, 1 },
+ { "MC4_INT_CAUSE", 0x1fc, 0 },
+ { "MC4_Corr_Err", 0, 1 },
+ { "MC4_Uncorr_Err", 1, 1 },
+ { "MC4_Addr_Err", 2, 1 },
+ { NULL }
+};
+
+struct reg_info tpi_regs[] = {
+ { "TPI_ADDR", 0x280, 0 },
+ { "TPI_ADDRESS", 0, 24 },
+ { "TPI_WR_DATA", 0x284, 0 },
+ { "TPI_RD_DATA", 0x288, 0 },
+ { "TPI_CSR", 0x28c, 0 },
+ { "TPIWR", 0, 1 },
+ { "TPIRDY", 1, 1 },
+ { "INT_DIR", 31, 1 },
+ { "TPI_PAR", 0x29c, 0 },
+ { "TPIPAR", 0, 7 },
+ { NULL }
+};
+
+struct reg_info tp_regs[] = {
+ { "TP_IN_CONFIG", 0x300, 0 },
+ { "TP_IN_CSPI_Tunnel", 0, 1 },
+ { "TP_IN_CSPI_Ethernet", 1, 1 },
+ { "TP_IN_CSPI_CPL", 3, 1 },
+ { "TP_IN_CSPI_POS", 4, 1 },
+ { "TP_IN_CSPI_Check_IP_Csum", 5, 1 },
+ { "TP_IN_CSPI_Check_TCP_Csum", 6, 1 },
+ { "TP_IN_ESPI_Tunnel", 7, 1 },
+ { "TP_IN_ESPI_Ethernet", 8, 1 },
+ { "TP_IN_ESPI_CPL", 10, 1 },
+ { "TP_IN_ESPI_POS", 11, 1 },
+ { "TP_IN_ESPI_Check_IP_Csum", 12, 1 },
+ { "TP_IN_ESPI_Check_TCP_Csum", 13, 1 },
+ { "Offload_Disable", 14, 1 },
+ { "TP_OUT_CONFIG", 0x304, 0 },
+ { "TP_OUT_C_ETH", 0, 1 },
+ { "TP_OUT_CSPI_CPL", 2, 1 },
+ { "TP_OUT_CSPI_POS", 3, 1 },
+ { "TP_OUT_CSPI_Generate_IP_Csum", 4, 1 },
+ { "TP_OUT_CSPI_Generate_TCP_Csum", 5, 1 },
+ { "TP_OUT_ESPI_Ethernet", 6, 1 },
+ { "TP_OUT_ESPI_TAG_Ethernet", 7, 1 },
+ { "TP_OUT_ESPI_CPL", 8, 1 },
+ { "TP_OUT_ESPI_POS", 9, 1 },
+ { "TP_OUT_ESPI_Generate_IP_Csum", 10, 1 },
+ { "TP_OUT_ESPI_Generate_TCP_Csum", 11, 1 },
+ { "TP_GLOBAL_CONFIG", 0x308, 0 },
+ { "IP_TTL", 0, 8 },
+ { "TCAM_Server_Region_Usage", 8, 2 },
+ { "QOS_Mapping", 10, 1 },
+ { "TCP_Csum", 11, 1 },
+ { "UDP_Csum", 12, 1 },
+ { "IP_Csum", 13, 1 },
+ { "IP_ID_Split", 14, 1 },
+ { "Path_MTU", 15, 1 },
+ { "5Tuple_Lookup", 17, 2 },
+ { "IP_Fragment_Drop", 19, 1 },
+ { "Ping_Drop", 20, 1 },
+ { "Protect_Mode", 21, 1 },
+ { "SYN_Cookie_Algorithm", 22, 1 },
+ { "Attack_Filter", 23, 1 },
+ { "Interface_Type", 24, 1 },
+ { "Disable_RX_Flow_Control", 25, 1 },
+ { "SYN_Cookie_Parameter", 26, 6 },
+ { "TP_GLOBAL_RX_CREDITS", 0x30c, 0 },
+ { "TP_CM_SIZE", 0x310, 0 },
+ { "TP_CM_MM_BASE", 0x314, 0 },
+ { "CM_MemMgr_Base", 0, 28 },
+ { "TP_CM_TIMER_BASE", 0x318, 0 },
+ { "CM_Timer_Base", 0, 28 },
+ { "TP_PM_SIZE", 0x31c, 0 },
+ { "TP_PM_TX_BASE", 0x320, 0 },
+ { "TP_PM_DEFRAG_BASE", 0x324, 0 },
+ { "TP_PM_RX_BASE", 0x328, 0 },
+ { "TP_PM_RX_PG_SIZE", 0x32c, 0 },
+ { "TP_PM_RX_MAX_PGS", 0x330, 0 },
+ { "TP_PM_TX_PG_SIZE", 0x334, 0 },
+ { "TP_PM_TX_MAX_PGS", 0x338, 0 },
+ { "TP_TCP_OPTIONS", 0x340, 0 },
+ { "Timestamp", 0, 2 },
+ { "Window_Scale", 2, 2 },
+ { "SACK", 4, 2 },
+ { "ECN", 6, 2 },
+ { "SACK_Algorithm", 8, 2 },
+ { "MSS", 10, 1 },
+ { "Default_Peer_MSS", 16, 16 },
+ { "TP_DACK_CONFIG", 0x344, 0 },
+ { "DACK_Mode", 0, 1 },
+ { "DACK_Auto_Mgmt", 1, 1 },
+ { "DACK_Auto_Careful", 2, 1 },
+ { "DACK_MSS_Selector", 3, 2 },
+ { "DACK_Byte_Threshold", 5, 20 },
+ { "TP_PC_CONFIG", 0x348, 0 },
+ { "TP_Access_Latency", 0, 4 },
+ { "Held_FIN_Disable", 4, 1 },
+ { "DDP_FC_Enable", 5, 1 },
+ { "RDMA_Err_Enable", 6, 1 },
+ { "Fast_PDU_Delivery", 7, 1 },
+ { "Clear_FIN", 8, 1 },
+ { "TP_PC_Rev", 30, 2 },
+ { "TP_BACKOFF0", 0x350, 0 },
+ { "Element0", 0, 8 },
+ { "Element1", 8, 8 },
+ { "Element2", 16, 8 },
+ { "Element3", 24, 8 },
+ { "TP_BACKOFF1", 0x354, 0 },
+ { "Element0", 0, 8 },
+ { "Element1", 8, 8 },
+ { "Element2", 16, 8 },
+ { "Element3", 24, 8 },
+ { "TP_BACKOFF2", 0x358, 0 },
+ { "Element0", 0, 8 },
+ { "Element1", 8, 8 },
+ { "Element2", 16, 8 },
+ { "Element3", 24, 8 },
+ { "TP_BACKOFF3", 0x35c, 0 },
+ { "Element0", 0, 8 },
+ { "Element1", 8, 8 },
+ { "Element2", 16, 8 },
+ { "Element3", 24, 8 },
+ { "TP_PARA_REG0", 0x360, 0 },
+ { "Var_Mult", 0, 4 },
+ { "Var_Gain", 4, 4 },
+ { "SRTT_Gain", 8, 4 },
+ { "RTTVar_Init", 12, 4 },
+ { "Dup_Thresh", 20, 4 },
+ { "Init_Cong_Win", 24, 3 },
+ { "TP_PARA_REG1", 0x364, 0 },
+ { "Initial_Slow_Start_Threshold", 0, 16 },
+ { "Receive_Buffer_Size", 16, 16 },
+ { "TP_PARA_REG2", 0x368, 0 },
+ { "RX_Coalesce_Size", 0, 16 },
+ { "MAX_RX_Size", 16, 16 },
+ { "TP_PARA_REG3", 0x36c, 0 },
+ { "RX_Coalescing_PSH_Deliver", 0, 1 },
+ { "RX_Coalescing_Enable", 1, 1 },
+ { "Tahoe_Enable", 2, 1 },
+ { "MAX_Reorder_Fragments", 12, 3 },
+ { "TP_TIMER_RESOLUTION", 0x390, 0 },
+ { "Delayed_ACK_Timer_Resolution", 0, 6 },
+ { "Generic_Timer_Resolution", 16, 6 },
+ { "TP_2MSL", 0x394, 0 },
+ { "2MSL", 0, 30 },
+ { "TP_RXT_MIN", 0x398, 0 },
+ { "Retransmit_Timer_MIN", 0, 16 },
+ { "TP_RXT_MAX", 0x39c, 0 },
+ { "Retransmit_Timer_MAX", 0, 30 },
+ { "TP_PERS_MIN", 0x3a0, 0 },
+ { "Persist_Timer_MIN", 0, 16 },
+ { "TP_PERS_MAX", 0x3a4, 0 },
+ { "Persist_Timer_MAX", 0, 30 },
+ { "TP_KEEP_IDLE", 0x3ac, 0 },
+ { "Keep_Alive_Idle_Time", 0, 30 },
+ { "TP_KEEP_INTVL", 0x3b0, 0 },
+ { "Keep_Alive_Interval_Time", 0, 30 },
+ { "TP_INIT_SRTT", 0x3b4, 0 },
+ { "Initial_SRTT", 0, 16 },
+ { "TP_DACK_TIME", 0x3b8, 0 },
+ { "Delayed_ACK_Time", 0, 11 },
+ { "TP_FINWAIT2_TIME", 0x3bc, 0 },
+ { "FINWAIT2_TIME", 0, 30 },
+ { "TP_FAST_FINWAIT2_TIME", 0x3c0, 0 },
+ { "Fast_FINWAIT2_Time", 0, 30 },
+ { "TP_SHIFT_CNT", 0x3c4, 0 },
+ { "KeepAlive_MAX", 0, 8 },
+ { "WindowProbe_MAX", 8, 8 },
+ { "Retransmission_MAX", 16, 8 },
+ { "SYN_MAX", 24, 8 },
+ { "TP_QOS_REG0", 0x3e0, 0 },
+ { "L3_Value", 0, 6 },
+ { "TP_QOS_REG1", 0x3e4, 0 },
+ { "L3_Value", 0, 6 },
+ { "TP_QOS_REG2", 0x3e8, 0 },
+ { "L3_Value", 0, 6 },
+ { "TP_QOS_REG3", 0x3ec, 0 },
+ { "L3_Value", 0, 6 },
+ { "TP_QOS_REG4", 0x3f0, 0 },
+ { "L3_Value", 0, 6 },
+ { "TP_QOS_REG5", 0x3f4, 0 },
+ { "L3_Value", 0, 6 },
+ { "TP_QOS_REG6", 0x3f8, 0 },
+ { "L3_Value", 0, 6 },
+ { "TP_QOS_REG7", 0x3fc, 0 },
+ { "L3_Value", 0, 6 },
+ { "TP_MTU_REG0", 0x404, 0 },
+ { "TP_MTU_REG1", 0x408, 0 },
+ { "TP_MTU_REG2", 0x40c, 0 },
+ { "TP_MTU_REG3", 0x410, 0 },
+ { "TP_MTU_REG4", 0x414, 0 },
+ { "TP_MTU_REG5", 0x418, 0 },
+ { "TP_MTU_REG6", 0x41c, 0 },
+ { "TP_MTU_REG7", 0x420, 0 },
+ { "TP_RESET", 0x44c, 0 },
+ { "TP_Reset", 0, 1 },
+ { "CM_MemMgr_Init", 1, 1 },
+ { "TP_MIB_INDEX", 0x450, 0 },
+ { "TP_MIB_DATA", 0x454, 0 },
+ { "TP_SYNC_TIME_HI", 0x458, 0 },
+ { "TP_SYNC_TIME_LO", 0x45c, 0 },
+ { "TP_CM_MM_RX_FLST_BASE", 0x460, 0 },
+ { "CM_MemMgr_RX_Free_List_Base", 0, 28 },
+ { "TP_CM_MM_TX_FLST_BASE", 0x464, 0 },
+ { "CM_MemMgr_TX_Free_List_Base", 0, 28 },
+ { "TP_CM_MM_P_FLST_BASE", 0x468, 0 },
+ { "CM_MemMgr_PStruct_Free_List_Base", 0, 28 },
+ { "TP_CM_MM_MAX_P", 0x46c, 0 },
+ { "CM_MemMgr_MAX_PStruct", 0, 28 },
+ { "TP_INT_ENABLE", 0x470, 0 },
+ { "TX_Free_List_Empty", 0, 1 },
+ { "RX_Free_List_Empty", 1, 1 },
+ { "TP_INT_CAUSE", 0x474, 0 },
+ { "TX_Free_List_Empty", 0, 1 },
+ { "RX_Free_List_Empty", 1, 1 },
+ { "TP_FLM_FREE_PSTRUCT_CNT", 0x480, 0 },
+ { "TP_FLM_FREE_RX_PG_CNT", 0x484, 0 },
+ { "TP_FLM_FREE_TX_PG_CNT", 0x488, 0 },
+ { "TP_HEAP_PUSH_CNT", 0x48c, 0 },
+ { "TP_HEAP_POP_CNT", 0x490, 0 },
+ { "TP_DACK_PUSH_CNT", 0x494, 0 },
+ { "TP_DACK_POP_CNT", 0x498, 0 },
+ { "TP_MOD_PUSH_CNT", 0x49c, 0 },
+ { "TP_MOD_POP_CNT", 0x4a0, 0 },
+ { "TP_TIMER_SEPARATOR", 0x4a4, 0 },
+ { "Disable_Past_Timer_Insertion", 0, 1 },
+ { "Modulation_Timer_Separator", 1, 15 },
+ { "Global_Timer_Separator", 16, 16 },
+ { "TP_DEBUG_SEL", 0x4a8, 0 },
+ { "TP_CM_FC_MODE", 0x4b0, 0 },
+ { "TP_PC_CONGESTION_CNTL", 0x4b4, 0 },
+ { "TP_TX_DROP_CONFIG", 0x4b8, 0 },
+ { "ENABLE_TX_DROP", 31, 1 },
+ { "ENABLE_TX_ERROR", 30, 1 },
+ { "DROP_TICKS_CNT", 4, 26 },
+ { "NUM_PKTS_DROPPED", 0, 4 },
+ { "TP_TX_DROP_COUNT", 0x4bc, 0 },
+ { NULL }
+};
+
+struct reg_info rat_regs[] = {
+ { "RAT_ROUTE_CONTROL", 0x580, 0 },
+ { "Use_Route_Table", 0, 1 },
+ { "Enable_CSPI", 1, 1 },
+ { "Enable_PCIX", 2, 1 },
+ { "RAT_ROUTE_TABLE_INDEX", 0x584, 0 },
+ { "Route_Table_Index", 0, 4 },
+ { "RAT_ROUTE_TABLE_DATA", 0x588, 0 },
+ { "RAT_NO_ROUTE", 0x58c, 0 },
+ { "CPL_Opcode", 0, 8 },
+ { "RAT_INTR_ENABLE", 0x590, 0 },
+ { "ZeroRouteError", 0, 1 },
+ { "CspiFramingError", 1, 1 },
+ { "SgeFramingError", 2, 1 },
+ { "TpFramingError", 3, 1 },
+ { "RAT_INTR_CAUSE", 0x594, 0 },
+ { "ZeroRouteError", 0, 1 },
+ { "CspiFramingError", 1, 1 },
+ { "SgeFramingError", 2, 1 },
+ { "TpFramingError", 3, 1 },
+ { NULL }
+};
+
+struct reg_info cspi_regs[] = {
+ { "CSPI_RX_AE_WM", 0x810, 0 },
+ { "CSPI_RX_AF_WM", 0x814, 0 },
+ { "CSPI_CALENDAR_LEN", 0x818, 0 },
+ { "CalendarLength", 0, 16 },
+ { "CSPI_FIFO_STATUS_ENABLE", 0x820, 0 },
+ { "FifoStatusEnable", 0, 1 },
+ { "CSPI_MAXBURST1_MAXBURST2", 0x828, 0 },
+ { "MaxBurst1", 0, 16 },
+ { "MaxBurst2", 16, 16 },
+ { "CSPI_TRAIN", 0x82c, 0 },
+ { "CSPI_TRAIN_ALPHA", 0, 16 },
+ { "CSPI_TRAIN_DATA_MAXT", 16, 16 },
+ { "CSPI_INTR_STATUS", 0x848, 0 },
+ { "DIP4Err", 0, 1 },
+ { "RXDrop", 1, 1 },
+ { "TXDrop", 2, 1 },
+ { "RXOverflow", 3, 1 },
+ { "RAMParityErr", 4, 1 },
+ { "CSPI_INTR_ENABLE", 0x84c, 0 },
+ { "DIP4Err", 0, 1 },
+ { "RXDrop", 1, 1 },
+ { "TXDrop", 2, 1 },
+ { "RXOverflow", 3, 1 },
+ { "RAMParityErr", 4, 1 },
+ { NULL }
+};
+
+struct reg_info espi_regs[] = {
+ { "ESPI_SCH_TOKEN0", 0x880, 0 },
+ { "SchToken0", 0, 16 },
+ { "ESPI_SCH_TOKEN1", 0x884, 0 },
+ { "SchToken1", 0, 16 },
+ { "ESPI_SCH_TOKEN2", 0x888, 0 },
+ { "SchToken2", 0, 16 },
+ { "ESPI_SCH_TOKEN3", 0x88c, 0 },
+ { "SchToken3", 0, 16 },
+ { "ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK", 0x890, 0 },
+ { "AlmostEmpty", 0, 16 },
+ { "ESPI_RX_FIFO_ALMOST_FULL_WATERMARK", 0x894, 0 },
+ { "AlmostFull", 0, 16 },
+ { "ESPI_CALENDAR_LENGTH", 0x898, 0 },
+ { "CalendarLength", 0, 16 },
+ { "PORT_CONFIG", 0x89c, 0 },
+ { "RX_NPorts", 0, 8 },
+ { "TX_NPorts", 8, 8 },
+ { "ESPI_FIFO_STATUS_ENABLE", 0x8a0, 0 },
+ { "RXStatusEnable", 0, 1 },
+ { "TXDropEnable", 1, 1 },
+ { "RXEndianMode", 2, 1 },
+ { "TXEndianMode", 3, 1 },
+ { "Intel1010Mode", 4, 1 },
+ { "ESPI_MAXBURST1_MAXBURST2", 0x8a8, 0 },
+ { "MaxBurst1", 0, 16 },
+ { "MaxBurst2", 16, 16 },
+ { "ESPI_TRAIN", 0x8ac, 0 },
+ { "MaxTrainAlpha", 0, 16 },
+ { "MaxTrainData", 16, 16 },
+ { "RAM_STATUS", 0x8b0, 0 },
+ { "RXFIFOParityError", 0, 10 },
+ { "TXFIFOParityError", 10, 10 },
+ { "RXFIFOOverflow", 20, 10 },
+ { "TX_DROP_COUNT0", 0x8b4, 0 },
+ { "TXPort0DropCnt", 0, 16 },
+ { "TxPort1DropCnt", 16, 16 },
+ { "TX_DROP_COUNT1", 0x8b8, 0 },
+ { "TXPort2DropCnt", 0, 16 },
+ { "TxPort3DropCnt", 16, 16 },
+ { "RX_DROP_COUNT0", 0x8bc, 0 },
+ { "RXPort0DropCnt", 0, 16 },
+ { "RxPort1DropCnt", 16, 16 },
+ { "RX_DROP_COUNT1", 0x8c0, 0 },
+ { "RXPort2DropCnt", 0, 16 },
+ { "RxPort3DropCnt", 16, 16 },
+ { "DIP4_ERROR_COUNT", 0x8c4, 0 },
+ { "Dip4ErrorCnt", 0, 12 },
+ { "Dip4ErrorCntShadow", 12, 12 },
+ { "TriCN_RX_Train_Err", 24, 1 },
+ { "TriCN_RX_Training", 25, 1 },
+ { "TriCN_RX_Train_OK", 26, 1 },
+ { "ESPI_INTR_STATUS", 0x8c8, 0 },
+ { "DIP4Err", 0, 1 },
+ { "RXDrop", 1, 1 },
+ { "TXDrop", 2, 1 },
+ { "RXOverflow", 3, 1 },
+ { "RAMParityErr", 4, 1 },
+ { "DIP2ParityErr", 5, 1 },
+ { "ESPI_INTR_ENABLE", 0x8cc, 0 },
+ { "DIP4Err", 0, 1 },
+ { "RXDrop", 1, 1 },
+ { "TXDrop", 2, 1 },
+ { "RXOverflow", 3, 1 },
+ { "RAMParityErr", 4, 1 },
+ { "DIP2ParityErr", 5, 1 },
+ { "RX_DROP_THRESHOLD", 0x8d0, 0 },
+ { "ESPI_RX_RESET", 0x8ec, 0 },
+ { "ESPI_RX_LNK_RST", 0, 1 },
+ { "ESPI_RX_CORE_RST", 1, 1 },
+ { "RX_CLK_STATUS", 2, 1 },
+ { "ESPI_MISC_CONTROL", 0x8f0, 0 },
+ { "Out_of_Sync_Count", 0, 4 },
+ { "DIP2_Count_Mode_Enable", 4, 1 },
+ { "DIP2_Parity_Err_Thres", 5, 4 },
+ { "DIP4_Thres", 9, 12 },
+ { "DIP4_Thres_Enable", 21, 1 },
+ { "Force_Disable_Status", 22, 1 },
+ { "Dynamic_Deskew", 23, 1 },
+ { "Monitored_Port_Num", 25, 2 },
+ { "Monitored_Direction", 27, 1 },
+ { "Monitored_Interface", 28, 1 },
+ { "ESPI_DIP2_ERR_COUNT", 0x8f4, 0 },
+ { "DIP2_Err_Cnt", 0, 4 },
+ { "ESPI_CMD_ADDR", 0x8f8, 0 },
+ { "Write_Data", 0, 8 },
+ { "Register_Offset", 8, 4 },
+ { "Channel_Addr", 12, 4 },
+ { "Module_Addr", 16, 2 },
+ { "Bundle_Addr", 20, 2 },
+ { "SPI4_Command", 24, 8 },
+ { "ESPI_GOSTAT", 0x8fc, 0 },
+ { "Read_Data", 0, 8 },
+ { "ESPI_Cmd_Busy", 8, 1 },
+ { "Error_Ack", 9, 1 },
+ { "Unmapped_Err", 10, 1 },
+ { "Transaction_Timer", 16, 8 },
+ { NULL }
+};
+
+struct reg_info ulp_regs[] = {
+ { "ULP_ULIMIT", 0x980, 0 },
+ { "ULP_TAGMASK", 0x984, 0 },
+ { "ULP_HREG_INDEX", 0x988, 0 },
+ { "ULP_HREG_DATA", 0x98c, 0 },
+ { "ULP_INT_ENABLE", 0x990, 0 },
+ { "ULP_INT_CAUSE", 0x994, 0 },
+ { "Hreg_Par_Err", 0, 1 },
+ { "Egrs_Data_Par_Err", 1, 1 },
+ { "Ingrs_Data_Par_Err", 2, 1 },
+ { "Pm_Intr", 3, 1 },
+ { "Pm_E2C_Sync_Err", 4, 1 },
+ { "Pm_C2E_Sync_Err", 5, 1 },
+ { "Pm_E2C_Empty_Err", 6, 1 },
+ { "Pm_C2E_Empty_Err", 7, 1 },
+ { "Pm_Par_Err", 8, 16 },
+ { "Pm_E2C_Wrt_Full", 24, 1 },
+ { "Pm_C2E_Wrt_Full", 25, 1 },
+ { "ULP_PIO_CTRL", 0x998, 0 },
+ { NULL }
+};
+
+struct reg_info pl_regs[] = {
+ { "PL_ENABLE", 0xa00, 0 },
+ { "PL_Intr_SGE_Err", 0, 1 },
+ { "PL_Intr_SGE_Data", 1, 1 },
+ { "PL_Intr_MC3", 2, 1 },
+ { "PL_Intr_MC4", 3, 1 },
+ { "PL_Intr_MC5", 4, 1 },
+ { "PL_Intr_RAT", 5, 1 },
+ { "PL_Intr_TP", 6, 1 },
+ { "PL_Intr_ULP", 7, 1 },
+ { "PL_Intr_ESPI", 8, 1 },
+ { "PL_Intr_CSPI", 9, 1 },
+ { "PL_Intr_PCIX", 10, 1 },
+ { "PL_Intr_EXT", 11, 1 },
+ { "PL_CAUSE", 0xa04, 0 },
+ { "PL_Intr_SGE_Err", 0, 1 },
+ { "PL_Intr_SGE_Data", 1, 1 },
+ { "PL_Intr_MC3", 2, 1 },
+ { "PL_Intr_MC4", 3, 1 },
+ { "PL_Intr_MC5", 4, 1 },
+ { "PL_Intr_RAT", 5, 1 },
+ { "PL_Intr_TP", 6, 1 },
+ { "PL_Intr_ULP", 7, 1 },
+ { "PL_Intr_ESPI", 8, 1 },
+ { "PL_Intr_CSPI", 9, 1 },
+ { "PL_Intr_PCIX", 10, 1 },
+ { "PL_Intr_EXT", 11, 1 },
+ { NULL }
+};
+
+struct reg_info mc5_regs[] = {
+ { "MC5_CONFIG", 0xc04, 0 },
+ { "Mode", 0, 1 },
+ { "TCAM_Reset", 1, 1 },
+ { "TCAM_Ready", 2, 1 },
+ { "DBGI_Enable", 4, 1 },
+ { "M_Bus_Enable", 5, 1 },
+ { "Parity_Enable", 6, 1 },
+ { "SYN_Issue_Mode", 7, 2 },
+ { "Build", 16, 1 },
+ { "Compression_Enable", 17, 1 },
+ { "Num_LIP", 18, 6 },
+ { "TCAM_Part_Cnt", 24, 2 },
+ { "TCAM_Part_Type", 26, 2 },
+ { "TCAM_Part_Size", 28, 2 },
+ { "TCAM_Part_Type_HI", 30, 1 },
+ { "MC5_SIZE", 0xc08, 0 },
+ { "Size", 0, 22 },
+ { "MC5_ROUTING_TABLE_INDEX", 0xc0c, 0 },
+ { "Start_of_Routing_Table", 0, 22 },
+ { "MC5_SERVER_INDEX", 0xc14, 0 },
+ { "Start_of_Server_Index", 0, 22 },
+ { "MC5_LIP_RAM_ADDR", 0xc18, 0 },
+ { "Local_IP_RAM_Addr", 0, 6 },
+ { "RAM_Write_Enable", 8, 1 },
+ { "MC5_LIP_RAM_DATA", 0xc1c, 0 },
+ { "MC5_RSP_LATENCY", 0xc20, 0 },
+ { "Search_Response_Latency", 0, 5 },
+ { "Learn_Response_Latency", 8, 5 },
+ { "MC5_PARITY_LATENCY", 0xc24, 0 },
+ { "SRCHLAT", 0, 5 },
+ { "PARLAT", 8, 5 },
+ { "MC5_WR_LRN_VERIFY", 0xc28, 0 },
+ { "POVEREN", 0, 1 },
+ { "LRNVEREN", 1, 1 },
+ { "VWVEREN", 2, 1 },
+ { "MC5_PART_ID_INDEX", 0xc2c, 0 },
+ { "IDINDEX", 0, 4 },
+ { "MC5_RESET_MAX", 0xc30, 0 },
+ { "RSTMAX", 0, 9 },
+ { "MC5_INT_ENABLE", 0xc40, 0 },
+ { "MC5_Int_Hit_Out_Active_Region_Err", 0, 1 },
+ { "MC5_Int_Hit_In_Active_Region_Err", 1, 1 },
+ { "MC5_Int_Hit_In_RT_Region_Err", 2, 1 },
+ { "MC5_Int_Miss_Err", 3, 1 },
+ { "MC5_Int_LIP0_Err", 4, 1 },
+ { "MC5_Int_LIP_Miss_Err", 5, 1 },
+ { "MC5_Int_Parity_Err", 6, 1 },
+ { "MC5_Int_Active_Region_Full", 7, 1 },
+ { "MC5_Int_NFA_Srch_Err", 8, 1 },
+ { "MC5_Int_SYN_Cookie", 9, 1 },
+ { "MC5_Int_SYN_Cookie_Bad", 10, 1 },
+ { "MC5_Int_SYN_Cookie_Off", 11, 1 },
+ { "MC5_Int_Unknown_Cmd", 15, 1 },
+ { "MC5_Int_RequestQ_Parity_Err", 16, 1 },
+ { "MC5_Int_DispatchQ_Parity_Err", 17, 1 },
+ { "MC5_Int_Del_Act_Empty", 18, 1 },
+ { "MC5_INT_CAUSE", 0xc44, 0 },
+ { "MC5_Int_Hit_Out_Active_Region_Err", 0, 1 },
+ { "MC5_Int_Hit_In_Active_Region_Err", 1, 1 },
+ { "MC5_Int_Hit_In_RT_Region_Err", 2, 1 },
+ { "MC5_Int_Miss_Err", 3, 1 },
+ { "MC5_Int_LIP0_Err", 4, 1 },
+ { "MC5_Int_LIP_Miss_Err", 5, 1 },
+ { "MC5_Int_Parity_Err", 6, 1 },
+ { "MC5_Int_Active_Region_Full", 7, 1 },
+ { "MC5_Int_NFA_Srch_Err", 8, 1 },
+ { "MC5_Int_SYN_Cookie", 9, 1 },
+ { "MC5_Int_SYN_Cookie_Bad", 10, 1 },
+ { "MC5_Int_SYN_Cookie_Off", 11, 1 },
+ { "MC5_Int_Unknown_Cmd", 15, 1 },
+ { "MC5_Int_RequestQ_Parity_Err", 16, 1 },
+ { "MC5_Int_DispatchQ_Parity_Err", 17, 1 },
+ { "MC5_Int_Del_Act_Empty", 18, 1 },
+ { "MC5_INT_TID", 0xc48, 0 },
+ { "MC5_INT_PTID", 0xc4c, 0 },
+ { "MC5_DBGI_CONFIG", 0xc74, 0 },
+ { "MC5_DBGI_REQ_CMD", 0xc78, 0 },
+ { "CmdMode", 0, 3 },
+ { "SADRSEL", 4, 1 },
+ { "Write_Burst_Size", 22, 10 },
+ { "MC5_DBGI_REQ_ADDR0", 0xc7c, 0 },
+ { "MC5_DBGI_REQ_ADDR1", 0xc80, 0 },
+ { "MC5_DBGI_REQ_ADDR2", 0xc84, 0 },
+ { "MC5_DBGI_REQ_DATA0", 0xc88, 0 },
+ { "MC5_DBGI_REQ_DATA1", 0xc8c, 0 },
+ { "MC5_DBGI_REQ_DATA2", 0xc90, 0 },
+ { "MC5_DBGI_REQ_DATA3", 0xc94, 0 },
+ { "MC5_DBGI_REQ_DATA4", 0xc98, 0 },
+ { "MC5_DBGI_REQ_MASK0", 0xc9c, 0 },
+ { "MC5_DBGI_REQ_MASK1", 0xca0, 0 },
+ { "MC5_DBGI_REQ_MASK2", 0xca4, 0 },
+ { "MC5_DBGI_REQ_MASK3", 0xca8, 0 },
+ { "MC5_DBGI_REQ_MASK4", 0xcac, 0 },
+ { "MC5_DBGI_RSP_STATUS", 0xcb0, 0 },
+ { "DBGI_Rsp_Valid", 0, 1 },
+ { "DBGI_Rsp_Hit", 1, 1 },
+ { "DBGI_Rsp_Err", 2, 1 },
+ { "DBGI_Rsp_Err_Reason", 8, 3 },
+ { "MC5_DBGI_RSP_DATA0", 0xcb4, 0 },
+ { "MC5_DBGI_RSP_DATA1", 0xcb8, 0 },
+ { "MC5_DBGI_RSP_DATA2", 0xcbc, 0 },
+ { "MC5_DBGI_RSP_DATA3", 0xcc0, 0 },
+ { "MC5_DBGI_RSP_DATA4", 0xcc4, 0 },
+ { "MC5_DBGI_RSP_LAST_CMD", 0xcc8, 0 },
+ { "MC5_POPEN_DATA_WR_CMD", 0xccc, 0 },
+ { "MC5_POPEN_MASK_WR_CMD", 0xcd0, 0 },
+ { "MC5_AOPEN_SRCH_CMD", 0xcd4, 0 },
+ { "MC5_AOPEN_LRN_CMD", 0xcd8, 0 },
+ { "MC5_SYN_SRCH_CMD", 0xcdc, 0 },
+ { "MC5_SYN_LRN_CMD", 0xce0, 0 },
+ { "MC5_ACK_SRCH_CMD", 0xce4, 0 },
+ { "MC5_ACK_LRN_CMD", 0xce8, 0 },
+ { "MC5_ILOOKUP_CMD", 0xcec, 0 },
+ { "MC5_ELOOKUP_CMD", 0xcf0, 0 },
+ { "MC5_DATA_WRITE_CMD", 0xcf4, 0 },
+ { "MC5_DATA_READ_CMD", 0xcf8, 0 },
+ { "MC5_MASK_WRITE_CMD", 0xcfc, 0 },
+ { NULL }
+};
diff --git a/usr.sbin/cxgbtool/reg_defs_t3.c b/usr.sbin/cxgbtool/reg_defs_t3.c
new file mode 100644
index 0000000..ffa4aef
--- /dev/null
+++ b/usr.sbin/cxgbtool/reg_defs_t3.c
@@ -0,0 +1,2676 @@
+/*
+ * $FreeBSD$
+ */
+
+
+/* This file is automatically generated --- do not edit */
+
+struct reg_info sge3_regs[] = {
+ { "SG_CONTROL", 0x0, 0 },
+ { "EgrEnUpBp", 21, 1 },
+ { "DropPkt", 20, 1 },
+ { "EgrGenCtrl", 19, 1 },
+ { "UserSpaceSize", 14, 5 },
+ { "HostPageSize", 11, 3 },
+ { "PCIRelax", 10, 1 },
+ { "FLMode", 9, 1 },
+ { "PktShift", 6, 3 },
+ { "OneIntMultQ", 5, 1 },
+ { "FLPickAvail", 4, 1 },
+ { "BigEndianEgress", 3, 1 },
+ { "BigEndianIngress", 2, 1 },
+ { "IscsiCoalescing", 1, 1 },
+ { "GlobalEnable", 0, 1 },
+ { "SG_KDOORBELL", 0x4, 0 },
+ { "SelEgrCntx", 31, 1 },
+ { "EgrCntx", 0, 16 },
+ { "SG_GTS", 0x8, 0 },
+ { "RspQ", 29, 3 },
+ { "NewTimer", 16, 13 },
+ { "NewIndex", 0, 16 },
+ { "SG_CONTEXT_CMD", 0xc, 0 },
+ { "Opcode", 28, 4 },
+ { "Busy", 27, 1 },
+ { "CQ_credit", 20, 7 },
+ { "CQ", 19, 1 },
+ { "RspQ", 18, 1 },
+ { "Egress", 17, 1 },
+ { "FreeList", 16, 1 },
+ { "Context", 0, 16 },
+ { "SG_CONTEXT_DATA0", 0x10, 0 },
+ { "SG_CONTEXT_DATA1", 0x14, 0 },
+ { "SG_CONTEXT_DATA2", 0x18, 0 },
+ { "SG_CONTEXT_DATA3", 0x1c, 0 },
+ { "SG_CONTEXT_MASK0", 0x20, 0 },
+ { "SG_CONTEXT_MASK1", 0x24, 0 },
+ { "SG_CONTEXT_MASK2", 0x28, 0 },
+ { "SG_CONTEXT_MASK3", 0x2c, 0 },
+ { "SG_RSPQ_CREDIT_RETURN", 0x30, 0 },
+ { "RspQ", 29, 3 },
+ { "Data", 0, 16 },
+ { "SG_HI_DRB_HI_THRSH", 0x38, 0 },
+ { "HiDrbHiThrsh", 0, 10 },
+ { "SG_HI_DRB_LO_THRSH", 0x3c, 0 },
+ { "HiDrbLoThrsh", 0, 10 },
+ { "SG_LO_DRB_HI_THRSH", 0x40, 0 },
+ { "LoDrbHiThrsh", 0, 10 },
+ { "SG_LO_DRB_LO_THRSH", 0x44, 0 },
+ { "LoDrbLoThrsh", 0, 10 },
+ { "SG_ONE_INT_MULT_Q_COALESCING_TIMER", 0x48, 0 },
+ { "SG_RSPQ_FL_STATUS", 0x4c, 0 },
+ { "RspQ0Starved", 0, 1 },
+ { "RspQ1Starved", 1, 1 },
+ { "RspQ2Starved", 2, 1 },
+ { "RspQ3Starved", 3, 1 },
+ { "RspQ4Starved", 4, 1 },
+ { "RspQ5Starved", 5, 1 },
+ { "RspQ6Starved", 6, 1 },
+ { "RspQ7Starved", 7, 1 },
+ { "RspQ0Disabled", 8, 1 },
+ { "RspQ1Disabled", 9, 1 },
+ { "RspQ2Disabled", 10, 1 },
+ { "RspQ3Disabled", 11, 1 },
+ { "RspQ4Disabled", 12, 1 },
+ { "RspQ5Disabled", 13, 1 },
+ { "RspQ6Disabled", 14, 1 },
+ { "RspQ7Disabled", 15, 1 },
+ { "FL0Empty", 16, 1 },
+ { "FL1Empty", 17, 1 },
+ { "FL2Empty", 18, 1 },
+ { "FL3Empty", 19, 1 },
+ { "FL4Empty", 20, 1 },
+ { "FL5Empty", 21, 1 },
+ { "FL6Empty", 22, 1 },
+ { "FL7Empty", 23, 1 },
+ { "FL8Empty", 24, 1 },
+ { "FL9Empty", 25, 1 },
+ { "FL10Empty", 26, 1 },
+ { "FL11Empty", 27, 1 },
+ { "FL12Empty", 28, 1 },
+ { "FL13Empty", 29, 1 },
+ { "FL14Empty", 30, 1 },
+ { "FL15Empty", 31, 1 },
+ { "SG_EGR_PRI_CNT", 0x50, 0 },
+ { "EgrPriCnt", 0, 5 },
+ { "SG_EGR_RCQ_DRB_THRSH", 0x54, 0 },
+ { "HiRcqDrbThrsh", 16, 11 },
+ { "LoRcqDrbThrsh", 0, 11 },
+ { "SG_EGR_CNTX_BADDR", 0x58, 0 },
+ { "EgrCntxBAddr", 5, 27 },
+ { "SG_INT_CAUSE", 0x5c, 0 },
+ { "HiCtlDrbDropErr", 13, 1 },
+ { "LoCtlDrbDropErr", 12, 1 },
+ { "HiPioDrbDropErr", 11, 1 },
+ { "LoPioDrbDropErr", 10, 1 },
+ { "HiCrdtUndFlowErr", 9, 1 },
+ { "LoCrdtUndFlowErr", 8, 1 },
+ { "HiPriorityDBFull", 7, 1 },
+ { "HiPriorityDBEmpty", 6, 1 },
+ { "LoPriorityDBFull", 5, 1 },
+ { "LoPriorityDBEmpty", 4, 1 },
+ { "RspQDisabled", 3, 1 },
+ { "RspQCreditOverfow", 2, 1 },
+ { "FlEmpty", 1, 1 },
+ { "RspQStarve", 0, 1 },
+ { "SG_INT_ENABLE", 0x60, 0 },
+ { "HiCtlDrbDropErr", 13, 1 },
+ { "LoCtlDrbDropErr", 12, 1 },
+ { "HiPioDrbDropErr", 11, 1 },
+ { "LoPioDrbDropErr", 10, 1 },
+ { "HiCrdtUndFlowErr", 9, 1 },
+ { "LoCrdtUndFlowErr", 8, 1 },
+ { "HiPriorityDBFull", 7, 1 },
+ { "HiPriorityDBEmpty", 6, 1 },
+ { "LoPriorityDBFull", 5, 1 },
+ { "LoPriorityDBEmpty", 4, 1 },
+ { "RspQDisabled", 3, 1 },
+ { "RspQCreditOverfow", 2, 1 },
+ { "FlEmpty", 1, 1 },
+ { "RspQStarve", 0, 1 },
+ { "SG_CMDQ_CREDIT_TH", 0x64, 0 },
+ { "Timeout", 8, 24 },
+ { "Threshold", 0, 8 },
+ { "SG_TIMER_TICK", 0x68, 0 },
+ { "SG_CQ_CONTEXT_BADDR", 0x6c, 0 },
+ { "baseAddr", 5, 27 },
+ { "SG_OCO_BASE", 0x70, 0 },
+ { "Base1", 16, 16 },
+ { "Base0", 0, 16 },
+ { "SG_DRB_PRI_THRESH", 0x74, 0 },
+ { "DrbPriThrsh", 0, 16 },
+ { "SG_DEBUG_INDEX", 0x78, 0 },
+ { "SG_DEBUG_DATA", 0x7c, 0 },
+ { NULL }
+};
+
+struct reg_info pcix1_regs[] = {
+ { "PCIX_INT_ENABLE", 0x80, 0 },
+ { "MSIXParErr", 22, 3 },
+ { "CFParErr", 18, 4 },
+ { "RFParErr", 14, 4 },
+ { "WFParErr", 12, 2 },
+ { "PIOParErr", 11, 1 },
+ { "DetUncECCErr", 10, 1 },
+ { "DetCorECCErr", 9, 1 },
+ { "RcvSplCmpErr", 8, 1 },
+ { "UnxSplCmp", 7, 1 },
+ { "SplCmpDis", 6, 1 },
+ { "DetParErr", 5, 1 },
+ { "SigSysErr", 4, 1 },
+ { "RcvMstAbt", 3, 1 },
+ { "RcvTarAbt", 2, 1 },
+ { "SigTarAbt", 1, 1 },
+ { "MstDetParErr", 0, 1 },
+ { "PCIX_INT_CAUSE", 0x84, 0 },
+ { "MSIXParErr", 22, 3 },
+ { "CFParErr", 18, 4 },
+ { "RFParErr", 14, 4 },
+ { "WFParErr", 12, 2 },
+ { "PIOParErr", 11, 1 },
+ { "DetUncECCErr", 10, 1 },
+ { "DetCorECCErr", 9, 1 },
+ { "RcvSplCmpErr", 8, 1 },
+ { "UnxSplCmp", 7, 1 },
+ { "SplCmpDis", 6, 1 },
+ { "DetParErr", 5, 1 },
+ { "SigSysErr", 4, 1 },
+ { "RcvMstAbt", 3, 1 },
+ { "RcvTarAbt", 2, 1 },
+ { "SigTarAbt", 1, 1 },
+ { "MstDetParErr", 0, 1 },
+ { "PCIX_CFG", 0x88, 0 },
+ { "CLIDecEn", 18, 1 },
+ { "LatTmrDis", 17, 1 },
+ { "LowPwrEn", 16, 1 },
+ { "AsyncIntVec", 11, 5 },
+ { "MaxSplTrnC", 8, 3 },
+ { "MaxSplTrnR", 5, 3 },
+ { "MaxWrByteCnt", 3, 2 },
+ { "WrReqAtomicEn", 2, 1 },
+ { "CRstWrmMode", 1, 1 },
+ { "PIOAck64En", 0, 1 },
+ { "PCIX_MODE", 0x8c, 0 },
+ { "PClkRange", 6, 2 },
+ { "PCIXInitPat", 2, 4 },
+ { "66MHz", 1, 1 },
+ { "64Bit", 0, 1 },
+ { "PCIX_CAL", 0x90, 0 },
+ { "Busy", 31, 1 },
+ { "PerCalDiv", 22, 8 },
+ { "PerCalEn", 21, 1 },
+ { "SglCalEn", 20, 1 },
+ { "ZInUpdMode", 19, 1 },
+ { "ZInSel", 18, 1 },
+ { "ZPDMan", 15, 3 },
+ { "ZPUMan", 12, 3 },
+ { "ZPDOut", 9, 3 },
+ { "ZPUOut", 6, 3 },
+ { "ZPDIn", 3, 3 },
+ { "ZPUIn", 0, 3 },
+ { "PCIX_WOL", 0x94, 0 },
+ { "WakeUp1", 3, 1 },
+ { "WakeUp0", 2, 1 },
+ { "SleepMode1", 1, 1 },
+ { "SleepMode0", 0, 1 },
+ { NULL }
+};
+
+struct reg_info pcie0_regs[] = {
+ { "PCIE_INT_ENABLE", 0x80, 0 },
+ { "BISTErr", 15, 8 },
+ { "MSIXParErr", 12, 3 },
+ { "CFParErr", 11, 1 },
+ { "RFParErr", 10, 1 },
+ { "WFParErr", 9, 1 },
+ { "PIOParErr", 8, 1 },
+ { "UnxSplCplErrC", 7, 1 },
+ { "UnxSplCplErrR", 6, 1 },
+ { "VPDAddrChng", 5, 1 },
+ { "BusMstrEn", 4, 1 },
+ { "PMStChng", 3, 1 },
+ { "PEXMsg", 2, 1 },
+ { "ZeroLenRd", 1, 1 },
+ { "PEXErr", 0, 1 },
+ { "PCIE_INT_CAUSE", 0x84, 0 },
+ { "BISTErr", 15, 8 },
+ { "MSIXParErr", 12, 3 },
+ { "CFParErr", 11, 1 },
+ { "RFParErr", 10, 1 },
+ { "WFParErr", 9, 1 },
+ { "PIOParErr", 8, 1 },
+ { "UnxSplCplErrC", 7, 1 },
+ { "UnxSplCplErrR", 6, 1 },
+ { "VPDAddrChng", 5, 1 },
+ { "BusMstrEn", 4, 1 },
+ { "PMStChng", 3, 1 },
+ { "PEXMsg", 2, 1 },
+ { "ZeroLenRd", 1, 1 },
+ { "PEXErr", 0, 1 },
+ { "PCIE_CFG", 0x88, 0 },
+ { "EnableLinkDwnDRst", 21, 1 },
+ { "EnableLinkDownRst", 20, 1 },
+ { "EnableHotRst", 19, 1 },
+ { "IniWaitForGnt", 18, 1 },
+ { "IniBEDis", 17, 1 },
+ { "CLIDecEn", 16, 1 },
+ { "AsyncIntVec", 11, 5 },
+ { "MaxSplTrnC", 7, 4 },
+ { "MaxSplTrnR", 1, 6 },
+ { "CRstWrmMode", 0, 1 },
+ { "PCIE_MODE", 0x8c, 0 },
+ { "LnkCntlState", 2, 8 },
+ { "VC0Up", 1, 1 },
+ { "LnkInitial", 0, 1 },
+ { "PCIE_CAL", 0x90, 0 },
+ { "CalBusy", 31, 1 },
+ { "CalFault", 30, 1 },
+ { "ZInSel", 11, 1 },
+ { "ZMan", 8, 3 },
+ { "ZOut", 3, 5 },
+ { "ZIn", 0, 3 },
+ { "PCIE_WOL", 0x94, 0 },
+ { "WakeUp1", 3, 1 },
+ { "WakeUp0", 2, 1 },
+ { "SleepMode1", 1, 1 },
+ { "SleepMode0", 0, 1 },
+ { "PCIE_PEX_CTRL0", 0x98, 0 },
+ { "NumFstTrnSeq", 22, 8 },
+ { "ReplayLmt", 2, 20 },
+ { "TxPndChkEn", 1, 1 },
+ { "CplPndChkEn", 0, 1 },
+ { "PCIE_PEX_CTRL1", 0x9c, 0 },
+ { "DLLPTimeoutLmt", 11, 20 },
+ { "AckLat", 0, 11 },
+ { "PCIE_PEX_CTRL2", 0xa0, 0 },
+ { "PMExitL1Req", 29, 1 },
+ { "PMTxIdle", 28, 1 },
+ { "PCIModeLoop", 27, 1 },
+ { "L1ASPMTxRxL0sTime", 15, 12 },
+ { "L0sIdleTime", 4, 11 },
+ { "EnterL23", 3, 1 },
+ { "EnterL1ASPMEn", 2, 1 },
+ { "EnterL1En", 1, 1 },
+ { "EnterL0sEn", 0, 1 },
+ { "PCIE_PEX_ERR", 0xa4, 0 },
+ { "FlowCtlOFlowErr", 17, 1 },
+ { "ReplayTimeout", 16, 1 },
+ { "ReplayRollover", 15, 1 },
+ { "BadDLLP", 14, 1 },
+ { "DLLPErr", 13, 1 },
+ { "FlowCtlProtErr", 12, 1 },
+ { "CplTimeout", 11, 1 },
+ { "PHYRcvErr", 10, 1 },
+ { "DisTLP", 9, 1 },
+ { "BadECRC", 8, 1 },
+ { "BadTLP", 7, 1 },
+ { "MalTLP", 6, 1 },
+ { "UnxCpl", 5, 1 },
+ { "UnsReq", 4, 1 },
+ { "PsnReq", 3, 1 },
+ { "UnsCpl", 2, 1 },
+ { "CplAbt", 1, 1 },
+ { "PsnCpl", 0, 1 },
+ { "PCIE_PIPE_CTRL", 0xa8, 0 },
+ { "RecDetUsec", 19, 3 },
+ { "PLLLckCyc", 6, 13 },
+ { "ElecIdleDetCyc", 3, 3 },
+ { "UseCDRLOS", 2, 1 },
+ { "PClkReqInP1", 1, 1 },
+ { "PClkOffInP1", 0, 1 },
+ { "PCIE_SERDES_CTRL", 0xac, 0 },
+ { "ManMode", 31, 1 },
+ { "ManLpbkEn", 29, 2 },
+ { "ManTxRecDetEn", 28, 1 },
+ { "ManTxBeacon", 27, 1 },
+ { "ManTxEI", 26, 1 },
+ { "ManRxPolarity", 25, 1 },
+ { "ManTxRst", 24, 1 },
+ { "ManRxRst", 23, 1 },
+ { "ManTxEn", 22, 1 },
+ { "ManRxEn", 21, 1 },
+ { "ManEn", 20, 1 },
+ { "CMURange", 17, 3 },
+ { "BGEnb", 16, 1 },
+ { "EnSkpDrop", 15, 1 },
+ { "EnComma", 14, 1 },
+ { "En8B10B", 13, 1 },
+ { "EnElBuf", 12, 1 },
+ { "Gain", 7, 5 },
+ { "BandGap", 3, 4 },
+ { "RxComAdj", 2, 1 },
+ { "PreEmph", 0, 2 },
+ { "PCIE_SERDES_STATUS0", 0xb0, 0 },
+ { "RxErrLane7", 21, 3 },
+ { "RxErrLane6", 18, 3 },
+ { "RxErrLane5", 15, 3 },
+ { "RxErrLane4", 12, 3 },
+ { "RxErrLane3", 9, 3 },
+ { "RxErrLane2", 6, 3 },
+ { "RxErrLane1", 3, 3 },
+ { "RxErrLane0", 0, 3 },
+ { "PCIE_SERDES_STATUS1", 0xb4, 0 },
+ { "CMULock", 31, 1 },
+ { "RxKLockLane7", 23, 1 },
+ { "RxKLockLane6", 22, 1 },
+ { "RxKLockLane5", 21, 1 },
+ { "RxKLockLane4", 20, 1 },
+ { "RxKLockLane3", 19, 1 },
+ { "RxKLockLane2", 18, 1 },
+ { "RxKLockLane1", 17, 1 },
+ { "RxKLockLane0", 16, 1 },
+ { "RxUFlowLane7", 15, 1 },
+ { "RxUFlowLane6", 14, 1 },
+ { "RxUFlowLane5", 13, 1 },
+ { "RxUFlowLane4", 12, 1 },
+ { "RxUFlowLane3", 11, 1 },
+ { "RxUFlowLane2", 10, 1 },
+ { "RxUFlowLane1", 9, 1 },
+ { "RxUFlowLane0", 8, 1 },
+ { "RxOFlowLane7", 7, 1 },
+ { "RxOFlowLane6", 6, 1 },
+ { "RxOFlowLane5", 5, 1 },
+ { "RxOFlowLane4", 4, 1 },
+ { "RxOFlowLane3", 3, 1 },
+ { "RxOFlowLane2", 2, 1 },
+ { "RxOFlowLane1", 1, 1 },
+ { "RxOFlowLane0", 0, 1 },
+ { "PCIE_SERDES_STATUS2", 0xb8, 0 },
+ { "TxRecDetLane7", 31, 1 },
+ { "TxRecDetLane6", 30, 1 },
+ { "TxRecDetLane5", 29, 1 },
+ { "TxRecDetLane4", 28, 1 },
+ { "TxRecDetLane3", 27, 1 },
+ { "TxRecDetLane2", 26, 1 },
+ { "TxRecDetLane1", 25, 1 },
+ { "TxRecDetLane0", 24, 1 },
+ { "RxEIDLane7", 23, 1 },
+ { "RxEIDLane6", 22, 1 },
+ { "RxEIDLane5", 21, 1 },
+ { "RxEIDLane4", 20, 1 },
+ { "RxEIDLane3", 19, 1 },
+ { "RxEIDLane2", 18, 1 },
+ { "RxEIDLane1", 17, 1 },
+ { "RxEIDLane0", 16, 1 },
+ { "RxRemSkipLane7", 15, 1 },
+ { "RxRemSkipLane6", 14, 1 },
+ { "RxRemSkipLane5", 13, 1 },
+ { "RxRemSkipLane4", 12, 1 },
+ { "RxRemSkipLane3", 11, 1 },
+ { "RxRemSkipLane2", 10, 1 },
+ { "RxRemSkipLane1", 9, 1 },
+ { "RxRemSkipLane0", 8, 1 },
+ { "RxAddSkipLane7", 7, 1 },
+ { "RxAddSkipLane6", 6, 1 },
+ { "RxAddSkipLane5", 5, 1 },
+ { "RxAddSkipLane4", 4, 1 },
+ { "RxAddSkipLane3", 3, 1 },
+ { "RxAddSkipLane2", 2, 1 },
+ { "RxAddSkipLane1", 1, 1 },
+ { "RxAddSkipLane0", 0, 1 },
+ { "PCIE_SERDES_BIST", 0xbc, 0 },
+ { "BISTDone", 24, 8 },
+ { "BISTCycleThresh", 3, 16 },
+ { "BISTMode", 0, 3 },
+ { NULL }
+};
+
+struct reg_info t3dbg_regs[] = {
+ { "T3DBG_DBG0_CFG", 0xc0, 0 },
+ { "RegSelect", 9, 8 },
+ { "ModuleSelect", 4, 5 },
+ { "ClkSelect", 0, 4 },
+ { "T3DBG_DBG0_EN", 0xc4, 0 },
+ { "SDRByte0", 8, 1 },
+ { "DDREn", 4, 1 },
+ { "PortEn", 0, 1 },
+ { "T3DBG_DBG1_CFG", 0xc8, 0 },
+ { "RegSelect", 9, 8 },
+ { "ModuleSelect", 4, 5 },
+ { "ClkSelect", 0, 4 },
+ { "T3DBG_DBG1_EN", 0xcc, 0 },
+ { "SDRByte0", 8, 1 },
+ { "DDREn", 4, 1 },
+ { "PortEn", 0, 1 },
+ { "T3DBG_GPIO_EN", 0xd0, 0 },
+ { "GPIO11_OEn", 27, 1 },
+ { "GPIO10_OEn", 26, 1 },
+ { "GPIO9_OEn", 25, 1 },
+ { "GPIO8_OEn", 24, 1 },
+ { "GPIO7_OEn", 23, 1 },
+ { "GPIO6_OEn", 22, 1 },
+ { "GPIO5_OEn", 21, 1 },
+ { "GPIO4_OEn", 20, 1 },
+ { "GPIO3_OEn", 19, 1 },
+ { "GPIO2_OEn", 18, 1 },
+ { "GPIO1_OEn", 17, 1 },
+ { "GPIO0_OEn", 16, 1 },
+ { "GPIO11_Out_Val", 11, 1 },
+ { "GPIO10_Out_Val", 10, 1 },
+ { "GPIO9_Out_Val", 9, 1 },
+ { "GPIO8_Out_Val", 8, 1 },
+ { "GPIO7_Out_Val", 7, 1 },
+ { "GPIO6_Out_Val", 6, 1 },
+ { "GPIO5_Out_Val", 5, 1 },
+ { "GPIO4_Out_Val", 4, 1 },
+ { "GPIO3_Out_Val", 3, 1 },
+ { "GPIO2_Out_Val", 2, 1 },
+ { "GPIO1_Out_Val", 1, 1 },
+ { "GPIO0_Out_Val", 0, 1 },
+ { "T3DBG_GPIO_IN", 0xd4, 0 },
+ { "GPIO11_IN", 11, 1 },
+ { "GPIO10_IN", 10, 1 },
+ { "GPIO9_IN", 9, 1 },
+ { "GPIO8_IN", 8, 1 },
+ { "GPIO7_IN", 7, 1 },
+ { "GPIO6_IN", 6, 1 },
+ { "GPIO5_IN", 5, 1 },
+ { "GPIO4_IN", 4, 1 },
+ { "GPIO3_IN", 3, 1 },
+ { "GPIO2_IN", 2, 1 },
+ { "GPIO1_IN", 1, 1 },
+ { "GPIO0_IN", 0, 1 },
+ { "T3DBG_INT_ENABLE", 0xd8, 0 },
+ { "C_LOCK", 21, 1 },
+ { "M_LOCK", 20, 1 },
+ { "U_LOCK", 19, 1 },
+ { "R_LOCK", 18, 1 },
+ { "PX_LOCK", 17, 1 },
+ { "PE_LOCK", 16, 1 },
+ { "GPIO11", 11, 1 },
+ { "GPIO10", 10, 1 },
+ { "GPIO9", 9, 1 },
+ { "GPIO8", 8, 1 },
+ { "GPIO7", 7, 1 },
+ { "GPIO6", 6, 1 },
+ { "GPIO5", 5, 1 },
+ { "GPIO4", 4, 1 },
+ { "GPIO3", 3, 1 },
+ { "GPIO2", 2, 1 },
+ { "GPIO1", 1, 1 },
+ { "GPIO0", 0, 1 },
+ { "T3DBG_INT_CAUSE", 0xdc, 0 },
+ { "C_LOCK", 21, 1 },
+ { "M_LOCK", 20, 1 },
+ { "U_LOCK", 19, 1 },
+ { "R_LOCK", 18, 1 },
+ { "PX_LOCK", 17, 1 },
+ { "PE_LOCK", 16, 1 },
+ { "GPIO11", 11, 1 },
+ { "GPIO10", 10, 1 },
+ { "GPIO9", 9, 1 },
+ { "GPIO8", 8, 1 },
+ { "GPIO7", 7, 1 },
+ { "GPIO6", 6, 1 },
+ { "GPIO5", 5, 1 },
+ { "GPIO4", 4, 1 },
+ { "GPIO3", 3, 1 },
+ { "GPIO2", 2, 1 },
+ { "GPIO1", 1, 1 },
+ { "GPIO0", 0, 1 },
+ { "T3DBG_DBG0_RST_VALUE", 0xe0, 0 },
+ { "DebugData", 0, 1 },
+ { "T3DBG_PLL_OCLK_PAD_EN", 0xe4, 0 },
+ { "PCIE_OCLK_En", 20, 1 },
+ { "PCIX_OCLK_En", 16, 1 },
+ { "U_OCLK_En", 12, 1 },
+ { "R_OCLK_En", 8, 1 },
+ { "M_OCLK_En", 4, 1 },
+ { "C_OCLK_En", 0, 1 },
+ { "T3DBG_PLL_LOCK", 0xe8, 0 },
+ { "PCIE_LOCK", 20, 1 },
+ { "PCIX_LOCK", 16, 1 },
+ { "U_LOCK", 12, 1 },
+ { "R_LOCK", 8, 1 },
+ { "M_LOCK", 4, 1 },
+ { "C_LOCK", 0, 1 },
+ { "T3DBG_SERDES_RBC_CFG", 0xec, 0 },
+ { "X_RBC_Lane_Sel", 16, 1 },
+ { "X_RBC_Dbg_En", 12, 1 },
+ { "X_Serdes_Sel", 8, 1 },
+ { "PE_RBC_Lane_Sel", 4, 1 },
+ { "PE_RBC_Dbg_En", 0, 1 },
+ { "T3DBG_GPIO_ACT_LOW", 0xf0, 0 },
+ { "C_LOCK_ACT_LOW", 21, 1 },
+ { "M_LOCK_ACT_LOW", 20, 1 },
+ { "U_LOCK_ACT_LOW", 19, 1 },
+ { "R_LOCK_ACT_LOW", 18, 1 },
+ { "PX_LOCK_ACT_LOW", 17, 1 },
+ { "PE_LOCK_ACT_LOW", 16, 1 },
+ { "GPIO11_ACT_LOW", 11, 1 },
+ { "GPIO10_ACT_LOW", 10, 1 },
+ { "GPIO9_ACT_LOW", 9, 1 },
+ { "GPIO8_ACT_LOW", 8, 1 },
+ { "GPIO7_ACT_LOW", 7, 1 },
+ { "GPIO6_ACT_LOW", 6, 1 },
+ { "GPIO5_ACT_LOW", 5, 1 },
+ { "GPIO4_ACT_LOW", 4, 1 },
+ { "GPIO3_ACT_LOW", 3, 1 },
+ { "GPIO2_ACT_LOW", 2, 1 },
+ { "GPIO1_ACT_LOW", 1, 1 },
+ { "GPIO0_ACT_LOW", 0, 1 },
+ { "T3DBG_PMON_CFG", 0xf4, 0 },
+ { "PMON_DONE", 29, 1 },
+ { "PMON_FAIL", 28, 1 },
+ { "PMON_FDEL_AUTO", 22, 1 },
+ { "PMON_CDEL_AUTO", 16, 1 },
+ { "PMON_FDEL_MANUAL", 10, 1 },
+ { "PMON_CDEL_MANUAL", 4, 1 },
+ { "PMON_MANUAL", 1, 1 },
+ { "PMON_AUTO", 0, 1 },
+ { NULL }
+};
+
+struct reg_info mc7_pmrx_regs[] = {
+ { "MC7_CFG", 0x100, 0 },
+ { "ImpSetUpdate", 14, 1 },
+ { "IFEn", 13, 1 },
+ { "TERM300", 12, 1 },
+ { "TERM150", 11, 1 },
+ { "Slow", 10, 1 },
+ { "Width", 8, 2 },
+ { "ODTEn", 7, 1 },
+ { "Bks", 6, 1 },
+ { "Org", 5, 1 },
+ { "Den", 2, 3 },
+ { "Rdy", 1, 1 },
+ { "ClkEn", 0, 1 },
+ { "MC7_MODE", 0x104, 0 },
+ { "Busy", 31, 1 },
+ { "Mode", 0, 16 },
+ { "MC7_EXT_MODE1", 0x108, 0 },
+ { "Busy", 31, 1 },
+ { "OCDAdjustMode", 20, 1 },
+ { "OCDCode", 16, 4 },
+ { "ExtMode1", 0, 16 },
+ { "MC7_EXT_MODE2", 0x10c, 0 },
+ { "Busy", 31, 1 },
+ { "ExtMode2", 0, 16 },
+ { "MC7_EXT_MODE3", 0x110, 0 },
+ { "Busy", 31, 1 },
+ { "ExtMode3", 0, 16 },
+ { "MC7_PRE", 0x114, 0 },
+ { "Busy", 31, 1 },
+ { "MC7_REF", 0x118, 0 },
+ { "Busy", 31, 1 },
+ { "PreRefDiv", 1, 14 },
+ { "PerRefEn", 0, 1 },
+ { "MC7_DLL", 0x11c, 0 },
+ { "DLLLock", 31, 1 },
+ { "DLLDelta", 24, 7 },
+ { "ManDelta", 3, 7 },
+ { "DLLDeltaSel", 2, 1 },
+ { "DLLEnb", 1, 1 },
+ { "DLLRst", 0, 1 },
+ { "MC7_PARM", 0x120, 0 },
+ { "ActToPreDly", 26, 4 },
+ { "ActToRdWrDly", 23, 3 },
+ { "PreCyc", 20, 3 },
+ { "RefCyc", 13, 7 },
+ { "BkCyc", 8, 5 },
+ { "WrToRdDly", 4, 4 },
+ { "RdToWrDly", 0, 4 },
+ { "MC7_HWM_WRR", 0x124, 0 },
+ { "MEM_HWM", 26, 6 },
+ { "ULP_HWM", 22, 4 },
+ { "TOT_RLD_WT", 14, 8 },
+ { "MEM_RLD_WT", 7, 7 },
+ { "ULP_RLD_WT", 0, 7 },
+ { "MC7_CAL", 0x128, 0 },
+ { "BUSY", 31, 1 },
+ { "CAL_FAULT", 30, 1 },
+ { "PER_CAL_DIV", 22, 8 },
+ { "PER_CAL_EN", 21, 1 },
+ { "SGL_CAL_EN", 20, 1 },
+ { "IMP_UPD_MODE", 19, 1 },
+ { "IMP_SEL", 18, 1 },
+ { "IMP_MAN_PD", 15, 3 },
+ { "IMP_MAN_PU", 12, 3 },
+ { "IMP_CAL_PD", 9, 3 },
+ { "IMP_CAL_PU", 6, 3 },
+ { "IMP_SET_PD", 3, 3 },
+ { "IMP_SET_PU", 0, 3 },
+ { "MC7_ECC", 0x130, 0 },
+ { "UECnt", 10, 8 },
+ { "CECnt", 2, 8 },
+ { "ECCChkEn", 1, 1 },
+ { "ECCGenEn", 0, 1 },
+ { "MC7_CE_ADDR", 0x134, 0 },
+ { "MC7_CE_DATA0", 0x138, 0 },
+ { "MC7_CE_DATA1", 0x13c, 0 },
+ { "MC7_CE_DATA2", 0x140, 0 },
+ { "Data", 0, 8 },
+ { "MC7_UE_ADDR", 0x144, 0 },
+ { "MC7_UE_DATA0", 0x148, 0 },
+ { "MC7_UE_DATA1", 0x14c, 0 },
+ { "MC7_UE_DATA2", 0x150, 0 },
+ { "Data", 0, 8 },
+ { "MC7_BD_ADDR", 0x154, 0 },
+ { "Addr", 3, 29 },
+ { "MC7_BD_DATA0", 0x158, 0 },
+ { "MC7_BD_DATA1", 0x15c, 0 },
+ { "MC7_BD_DATA2", 0x160, 0 },
+ { "Data", 0, 8 },
+ { "MC7_BD_OP", 0x164, 0 },
+ { "Busy", 31, 1 },
+ { "Op", 0, 1 },
+ { "MC7_BIST_ADDR_BEG", 0x168, 0 },
+ { "AddrBeg", 5, 27 },
+ { "MC7_BIST_ADDR_END", 0x16c, 0 },
+ { "AddrEnd", 5, 27 },
+ { "MC7_BIST_DATA", 0x170, 0 },
+ { "MC7_BIST_OP", 0x174, 0 },
+ { "Busy", 31, 1 },
+ { "Gap", 4, 5 },
+ { "Cont", 3, 1 },
+ { "DataPat", 1, 2 },
+ { "Op", 0, 1 },
+ { "MC7_INT_ENABLE", 0x178, 0 },
+ { "AE", 17, 1 },
+ { "PE", 2, 15 },
+ { "UE", 1, 1 },
+ { "CE", 0, 1 },
+ { "MC7_INT_CAUSE", 0x17c, 0 },
+ { "AE", 17, 1 },
+ { "PE", 2, 15 },
+ { "UE", 1, 1 },
+ { "CE", 0, 1 },
+ { NULL }
+};
+
+struct reg_info mc7_pmtx_regs[] = {
+ { "MC7_CFG", 0x180, 0 },
+ { "ImpSetUpdate", 14, 1 },
+ { "IFEn", 13, 1 },
+ { "TERM300", 12, 1 },
+ { "TERM150", 11, 1 },
+ { "Slow", 10, 1 },
+ { "Width", 8, 2 },
+ { "ODTEn", 7, 1 },
+ { "Bks", 6, 1 },
+ { "Org", 5, 1 },
+ { "Den", 2, 3 },
+ { "Rdy", 1, 1 },
+ { "ClkEn", 0, 1 },
+ { "MC7_MODE", 0x184, 0 },
+ { "Busy", 31, 1 },
+ { "Mode", 0, 16 },
+ { "MC7_EXT_MODE1", 0x188, 0 },
+ { "Busy", 31, 1 },
+ { "OCDAdjustMode", 20, 1 },
+ { "OCDCode", 16, 4 },
+ { "ExtMode1", 0, 16 },
+ { "MC7_EXT_MODE2", 0x18c, 0 },
+ { "Busy", 31, 1 },
+ { "ExtMode2", 0, 16 },
+ { "MC7_EXT_MODE3", 0x190, 0 },
+ { "Busy", 31, 1 },
+ { "ExtMode3", 0, 16 },
+ { "MC7_PRE", 0x194, 0 },
+ { "Busy", 31, 1 },
+ { "MC7_REF", 0x198, 0 },
+ { "Busy", 31, 1 },
+ { "PreRefDiv", 1, 14 },
+ { "PerRefEn", 0, 1 },
+ { "MC7_DLL", 0x19c, 0 },
+ { "DLLLock", 31, 1 },
+ { "DLLDelta", 24, 7 },
+ { "ManDelta", 3, 7 },
+ { "DLLDeltaSel", 2, 1 },
+ { "DLLEnb", 1, 1 },
+ { "DLLRst", 0, 1 },
+ { "MC7_PARM", 0x1a0, 0 },
+ { "ActToPreDly", 26, 4 },
+ { "ActToRdWrDly", 23, 3 },
+ { "PreCyc", 20, 3 },
+ { "RefCyc", 13, 7 },
+ { "BkCyc", 8, 5 },
+ { "WrToRdDly", 4, 4 },
+ { "RdToWrDly", 0, 4 },
+ { "MC7_HWM_WRR", 0x1a4, 0 },
+ { "MEM_HWM", 26, 6 },
+ { "ULP_HWM", 22, 4 },
+ { "TOT_RLD_WT", 14, 8 },
+ { "MEM_RLD_WT", 7, 7 },
+ { "ULP_RLD_WT", 0, 7 },
+ { "MC7_CAL", 0x1a8, 0 },
+ { "BUSY", 31, 1 },
+ { "CAL_FAULT", 30, 1 },
+ { "PER_CAL_DIV", 22, 8 },
+ { "PER_CAL_EN", 21, 1 },
+ { "SGL_CAL_EN", 20, 1 },
+ { "IMP_UPD_MODE", 19, 1 },
+ { "IMP_SEL", 18, 1 },
+ { "IMP_MAN_PD", 15, 3 },
+ { "IMP_MAN_PU", 12, 3 },
+ { "IMP_CAL_PD", 9, 3 },
+ { "IMP_CAL_PU", 6, 3 },
+ { "IMP_SET_PD", 3, 3 },
+ { "IMP_SET_PU", 0, 3 },
+ { "MC7_ECC", 0x1b0, 0 },
+ { "UECnt", 10, 8 },
+ { "CECnt", 2, 8 },
+ { "ECCChkEn", 1, 1 },
+ { "ECCGenEn", 0, 1 },
+ { "MC7_CE_ADDR", 0x1b4, 0 },
+ { "MC7_CE_DATA0", 0x1b8, 0 },
+ { "MC7_CE_DATA1", 0x1bc, 0 },
+ { "MC7_CE_DATA2", 0x1c0, 0 },
+ { "Data", 0, 8 },
+ { "MC7_UE_ADDR", 0x1c4, 0 },
+ { "MC7_UE_DATA0", 0x1c8, 0 },
+ { "MC7_UE_DATA1", 0x1cc, 0 },
+ { "MC7_UE_DATA2", 0x1d0, 0 },
+ { "Data", 0, 8 },
+ { "MC7_BD_ADDR", 0x1d4, 0 },
+ { "Addr", 3, 29 },
+ { "MC7_BD_DATA0", 0x1d8, 0 },
+ { "MC7_BD_DATA1", 0x1dc, 0 },
+ { "MC7_BD_DATA2", 0x1e0, 0 },
+ { "Data", 0, 8 },
+ { "MC7_BD_OP", 0x1e4, 0 },
+ { "Busy", 31, 1 },
+ { "Op", 0, 1 },
+ { "MC7_BIST_ADDR_BEG", 0x1e8, 0 },
+ { "AddrBeg", 5, 27 },
+ { "MC7_BIST_ADDR_END", 0x1ec, 0 },
+ { "AddrEnd", 5, 27 },
+ { "MC7_BIST_DATA", 0x1f0, 0 },
+ { "MC7_BIST_OP", 0x1f4, 0 },
+ { "Busy", 31, 1 },
+ { "Gap", 4, 5 },
+ { "Cont", 3, 1 },
+ { "DataPat", 1, 2 },
+ { "Op", 0, 1 },
+ { "MC7_INT_ENABLE", 0x1f8, 0 },
+ { "AE", 17, 1 },
+ { "PE", 2, 15 },
+ { "UE", 1, 1 },
+ { "CE", 0, 1 },
+ { "MC7_INT_CAUSE", 0x1fc, 0 },
+ { "AE", 17, 1 },
+ { "PE", 2, 15 },
+ { "UE", 1, 1 },
+ { "CE", 0, 1 },
+ { NULL }
+};
+
+struct reg_info mc7_cm_regs[] = {
+ { "MC7_CFG", 0x200, 0 },
+ { "ImpSetUpdate", 14, 1 },
+ { "IFEn", 13, 1 },
+ { "TERM300", 12, 1 },
+ { "TERM150", 11, 1 },
+ { "Slow", 10, 1 },
+ { "Width", 8, 2 },
+ { "ODTEn", 7, 1 },
+ { "Bks", 6, 1 },
+ { "Org", 5, 1 },
+ { "Den", 2, 3 },
+ { "Rdy", 1, 1 },
+ { "ClkEn", 0, 1 },
+ { "MC7_MODE", 0x204, 0 },
+ { "Busy", 31, 1 },
+ { "Mode", 0, 16 },
+ { "MC7_EXT_MODE1", 0x208, 0 },
+ { "Busy", 31, 1 },
+ { "OCDAdjustMode", 20, 1 },
+ { "OCDCode", 16, 4 },
+ { "ExtMode1", 0, 16 },
+ { "MC7_EXT_MODE2", 0x20c, 0 },
+ { "Busy", 31, 1 },
+ { "ExtMode2", 0, 16 },
+ { "MC7_EXT_MODE3", 0x210, 0 },
+ { "Busy", 31, 1 },
+ { "ExtMode3", 0, 16 },
+ { "MC7_PRE", 0x214, 0 },
+ { "Busy", 31, 1 },
+ { "MC7_REF", 0x218, 0 },
+ { "Busy", 31, 1 },
+ { "PreRefDiv", 1, 14 },
+ { "PerRefEn", 0, 1 },
+ { "MC7_DLL", 0x21c, 0 },
+ { "DLLLock", 31, 1 },
+ { "DLLDelta", 24, 7 },
+ { "ManDelta", 3, 7 },
+ { "DLLDeltaSel", 2, 1 },
+ { "DLLEnb", 1, 1 },
+ { "DLLRst", 0, 1 },
+ { "MC7_PARM", 0x220, 0 },
+ { "ActToPreDly", 26, 4 },
+ { "ActToRdWrDly", 23, 3 },
+ { "PreCyc", 20, 3 },
+ { "RefCyc", 13, 7 },
+ { "BkCyc", 8, 5 },
+ { "WrToRdDly", 4, 4 },
+ { "RdToWrDly", 0, 4 },
+ { "MC7_HWM_WRR", 0x224, 0 },
+ { "MEM_HWM", 26, 6 },
+ { "ULP_HWM", 22, 4 },
+ { "TOT_RLD_WT", 14, 8 },
+ { "MEM_RLD_WT", 7, 7 },
+ { "ULP_RLD_WT", 0, 7 },
+ { "MC7_CAL", 0x228, 0 },
+ { "BUSY", 31, 1 },
+ { "CAL_FAULT", 30, 1 },
+ { "PER_CAL_DIV", 22, 8 },
+ { "PER_CAL_EN", 21, 1 },
+ { "SGL_CAL_EN", 20, 1 },
+ { "IMP_UPD_MODE", 19, 1 },
+ { "IMP_SEL", 18, 1 },
+ { "IMP_MAN_PD", 15, 3 },
+ { "IMP_MAN_PU", 12, 3 },
+ { "IMP_CAL_PD", 9, 3 },
+ { "IMP_CAL_PU", 6, 3 },
+ { "IMP_SET_PD", 3, 3 },
+ { "IMP_SET_PU", 0, 3 },
+ { "MC7_ECC", 0x230, 0 },
+ { "UECnt", 10, 8 },
+ { "CECnt", 2, 8 },
+ { "ECCChkEn", 1, 1 },
+ { "ECCGenEn", 0, 1 },
+ { "MC7_CE_ADDR", 0x234, 0 },
+ { "MC7_CE_DATA0", 0x238, 0 },
+ { "MC7_CE_DATA1", 0x23c, 0 },
+ { "MC7_CE_DATA2", 0x240, 0 },
+ { "Data", 0, 8 },
+ { "MC7_UE_ADDR", 0x244, 0 },
+ { "MC7_UE_DATA0", 0x248, 0 },
+ { "MC7_UE_DATA1", 0x24c, 0 },
+ { "MC7_UE_DATA2", 0x250, 0 },
+ { "Data", 0, 8 },
+ { "MC7_BD_ADDR", 0x254, 0 },
+ { "Addr", 3, 29 },
+ { "MC7_BD_DATA0", 0x258, 0 },
+ { "MC7_BD_DATA1", 0x25c, 0 },
+ { "MC7_BD_DATA2", 0x260, 0 },
+ { "Data", 0, 8 },
+ { "MC7_BD_OP", 0x264, 0 },
+ { "Busy", 31, 1 },
+ { "Op", 0, 1 },
+ { "MC7_BIST_ADDR_BEG", 0x268, 0 },
+ { "AddrBeg", 5, 27 },
+ { "MC7_BIST_ADDR_END", 0x26c, 0 },
+ { "AddrEnd", 5, 27 },
+ { "MC7_BIST_DATA", 0x270, 0 },
+ { "MC7_BIST_OP", 0x274, 0 },
+ { "Busy", 31, 1 },
+ { "Gap", 4, 5 },
+ { "Cont", 3, 1 },
+ { "DataPat", 1, 2 },
+ { "Op", 0, 1 },
+ { "MC7_INT_ENABLE", 0x278, 0 },
+ { "AE", 17, 1 },
+ { "PE", 2, 15 },
+ { "UE", 1, 1 },
+ { "CE", 0, 1 },
+ { "MC7_INT_CAUSE", 0x27c, 0 },
+ { "AE", 17, 1 },
+ { "PE", 2, 15 },
+ { "UE", 1, 1 },
+ { "CE", 0, 1 },
+ { NULL }
+};
+
+struct reg_info cim_regs[] = {
+ { "CIM_BOOT_CFG", 0x280, 0 },
+ { "BootAddr", 2, 30 },
+ { "BootSdram", 1, 1 },
+ { "uPCRst", 0, 1 },
+ { "CIM_FLASH_BASE_ADDR", 0x284, 0 },
+ { "FlashBaseAddr", 2, 22 },
+ { "CIM_FLASH_ADDR_SIZE", 0x288, 0 },
+ { "FlashAddrSize", 2, 22 },
+ { "CIM_SDRAM_BASE_ADDR", 0x28c, 0 },
+ { "SdramBaseAddr", 2, 30 },
+ { "CIM_SDRAM_ADDR_SIZE", 0x290, 0 },
+ { "SdramAddrSize", 2, 30 },
+ { "CIM_UP_SPARE_INT", 0x294, 0 },
+ { "uPSpareInt", 0, 3 },
+ { "CIM_HOST_INT_ENABLE", 0x298, 0 },
+ { "Timer1IntEn", 15, 1 },
+ { "Timer0IntEn", 14, 1 },
+ { "PrefDropIntEn", 13, 1 },
+ { "BlkWrPlIntEn", 12, 1 },
+ { "BlkRdPlIntEn", 11, 1 },
+ { "BlkWrCtlIntEn", 10, 1 },
+ { "BlkRdCtlIntEn", 9, 1 },
+ { "BlkWrFlashIntEn", 8, 1 },
+ { "BlkRdFlashIntEn", 7, 1 },
+ { "SglWrFlashIntEn", 6, 1 },
+ { "WrBlkFlashIntEn", 5, 1 },
+ { "BlkWrBootIntEn", 4, 1 },
+ { "BlkRdBootIntEn", 3, 1 },
+ { "FlashRangeIntEn", 2, 1 },
+ { "SdramRangeIntEn", 1, 1 },
+ { "RsvdSpaceIntEn", 0, 1 },
+ { "CIM_HOST_INT_CAUSE", 0x29c, 0 },
+ { "Timer1Int", 15, 1 },
+ { "Timer0Int", 14, 1 },
+ { "PrefDropInt", 13, 1 },
+ { "BlkWrPlInt", 12, 1 },
+ { "BlkRdPlInt", 11, 1 },
+ { "BlkWrCtlInt", 10, 1 },
+ { "BlkRdCtlInt", 9, 1 },
+ { "BlkWrFlashInt", 8, 1 },
+ { "BlkRdFlashInt", 7, 1 },
+ { "SglWrFlashInt", 6, 1 },
+ { "WrBlkFlashInt", 5, 1 },
+ { "BlkWrBootInt", 4, 1 },
+ { "BlkRdBootInt", 3, 1 },
+ { "FlashRangeInt", 2, 1 },
+ { "SdramRangeInt", 1, 1 },
+ { "RsvdSpaceInt", 0, 1 },
+ { "CIM_UP_INT_ENABLE", 0x2a0, 0 },
+ { "MstPlIntEn", 16, 1 },
+ { "Timer1IntEn", 15, 1 },
+ { "Timer0IntEn", 14, 1 },
+ { "PrefDropIntEn", 13, 1 },
+ { "BlkWrPlIntEn", 12, 1 },
+ { "BlkRdPlIntEn", 11, 1 },
+ { "BlkWrCtlIntEn", 10, 1 },
+ { "BlkRdCtlIntEn", 9, 1 },
+ { "BlkWrFlashIntEn", 8, 1 },
+ { "BlkRdFlashIntEn", 7, 1 },
+ { "SglWrFlashIntEn", 6, 1 },
+ { "WrBlkFlashIntEn", 5, 1 },
+ { "BlkWrBootIntEn", 4, 1 },
+ { "BlkRdBootIntEn", 3, 1 },
+ { "FlashRangeIntEn", 2, 1 },
+ { "SdramRangeIntEn", 1, 1 },
+ { "RsvdSpaceIntEn", 0, 1 },
+ { "CIM_UP_INT_CAUSE", 0x2a4, 0 },
+ { "MstPlInt", 16, 1 },
+ { "Timer1Int", 15, 1 },
+ { "Timer0Int", 14, 1 },
+ { "PrefDropInt", 13, 1 },
+ { "BlkWrPlInt", 12, 1 },
+ { "BlkRdPlInt", 11, 1 },
+ { "BlkWrCtlInt", 10, 1 },
+ { "BlkRdCtlInt", 9, 1 },
+ { "BlkWrFlashInt", 8, 1 },
+ { "BlkRdFlashInt", 7, 1 },
+ { "SglWrFlashInt", 6, 1 },
+ { "WrBlkFlashInt", 5, 1 },
+ { "BlkWrBootInt", 4, 1 },
+ { "BlkRdBootInt", 3, 1 },
+ { "FlashRangeInt", 2, 1 },
+ { "SdramRangeInt", 1, 1 },
+ { "RsvdSpaceInt", 0, 1 },
+ { "CIM_IBQ_FULLA_THRSH", 0x2a8, 0 },
+ { "Ibq0FullThrsh", 0, 9 },
+ { "Ibq1FullThrsh", 16, 9 },
+ { "CIM_IBQ_FULLB_THRSH", 0x2ac, 0 },
+ { "Ibq2FullThrsh", 0, 9 },
+ { "Ibq3FullThrsh", 16, 9 },
+ { "CIM_HOST_ACC_CTRL", 0x2b0, 0 },
+ { "HostBusy", 17, 1 },
+ { "HostWrite", 16, 1 },
+ { "HostAddr", 0, 16 },
+ { "CIM_HOST_ACC_DATA", 0x2b4, 0 },
+ { "CIM_IBQ_DBG_CFG", 0x2c0, 0 },
+ { "IbqDbgAddr", 16, 9 },
+ { "IbqDbgQID", 3, 2 },
+ { "IbqDbgWr", 2, 1 },
+ { "IbqDbgBusy", 1, 1 },
+ { "IbqDbgEn", 0, 1 },
+ { "CIM_OBQ_DBG_CFG", 0x2c4, 0 },
+ { "ObqDbgAddr", 16, 9 },
+ { "ObqDbgQID", 3, 2 },
+ { "ObqDbgWr", 2, 1 },
+ { "ObqDbgBusy", 1, 1 },
+ { "ObqDbgEn", 0, 1 },
+ { "CIM_IBQ_DBG_DATA", 0x2c8, 0 },
+ { "CIM_OBQ_DBG_DATA", 0x2cc, 0 },
+ { "CIM_CDEBUGDATA", 0x2d0, 0 },
+ { "CDebugDataH", 16, 16 },
+ { "CDebugDataL", 0, 16 },
+ { NULL }
+};
+
+struct reg_info tp1_regs[] = {
+ { "TP_IN_CONFIG", 0x300, 0 },
+ { "RXFbArbPrio", 25, 1 },
+ { "TXFbArbPrio", 24, 1 },
+ { "DBMaxOpCnt", 16, 8 },
+ { "NICMode", 14, 1 },
+ { "EChecksumCheckTCP", 13, 1 },
+ { "EChecksumCheckIP", 12, 1 },
+ { "ECPL", 10, 1 },
+ { "EEthernet", 8, 1 },
+ { "ETunnel", 7, 1 },
+ { "CChecksumCheckTCP", 6, 1 },
+ { "CChecksumCheckIP", 5, 1 },
+ { "CCPL", 3, 1 },
+ { "CEthernet", 1, 1 },
+ { "CTunnel", 0, 1 },
+ { "TP_OUT_CONFIG", 0x304, 0 },
+ { "VLANExtractionEnable", 12, 1 },
+ { "EChecksumGenerateTCP", 11, 1 },
+ { "EChecksumGenerateIP", 10, 1 },
+ { "ECPL", 8, 1 },
+ { "EEthernet", 6, 1 },
+ { "CChecksumGenerateTCP", 5, 1 },
+ { "CChecksumGenerateIP", 4, 1 },
+ { "CCPL", 2, 1 },
+ { "CEthernet", 0, 1 },
+ { "TP_GLOBAL_CONFIG", 0x308, 0 },
+ { "RXFlowControlDisable", 25, 1 },
+ { "TXPacingEnable", 24, 1 },
+ { "AttackFilterEnable", 23, 1 },
+ { "SYNCookieNoOptions", 22, 1 },
+ { "ProtectedMode", 21, 1 },
+ { "PingDrop", 20, 1 },
+ { "FragmentDrop", 19, 1 },
+ { "FiveTupleLookup", 17, 2 },
+ { "PathMTU", 15, 1 },
+ { "IPIdentSplit", 14, 1 },
+ { "IPChecksumOffload", 13, 1 },
+ { "UDPChecksumOffload", 12, 1 },
+ { "TCPChecksumOffload", 11, 1 },
+ { "QOSMapping", 10, 1 },
+ { "TCAMServerUse", 8, 2 },
+ { "IPTTL", 0, 8 },
+ { "TP_GLOBAL_RX_CREDIT", 0x30c, 0 },
+ { "TP_CMM_SIZE", 0x310, 0 },
+ { "CMMemMgrSize", 0, 28 },
+ { "TP_CMM_MM_BASE", 0x314, 0 },
+ { "CMMemMgrBase", 0, 28 },
+ { "TP_CMM_TIMER_BASE", 0x318, 0 },
+ { "CMTimerBase", 0, 28 },
+ { "TP_PMM_SIZE", 0x31c, 0 },
+ { "PMSize", 0, 28 },
+ { "TP_PMM_TX_BASE", 0x320, 0 },
+ { "TP_PMM_DEFRAG_BASE", 0x324, 0 },
+ { "TP_PMM_RX_BASE", 0x328, 0 },
+ { "TP_PMM_RX_PAGE_SIZE", 0x32c, 0 },
+ { "TP_PMM_RX_MAX_PAGE", 0x330, 0 },
+ { "PMRxMaxPage", 0, 21 },
+ { "TP_PMM_TX_PAGE_SIZE", 0x334, 0 },
+ { "TP_PMM_TX_MAX_PAGE", 0x338, 0 },
+ { "PMTxMaxPage", 0, 21 },
+ { "TP_TCP_OPTIONS", 0x340, 0 },
+ { "MTUDefault", 16, 16 },
+ { "MTUEnable", 10, 1 },
+ { "SACKTx", 9, 1 },
+ { "SACKRx", 8, 1 },
+ { "SACKMode", 4, 2 },
+ { "WindowScaleMode", 2, 2 },
+ { "TimestampsMode", 0, 2 },
+ { "TP_DACK_CONFIG", 0x344, 0 },
+ { "AutoState3", 30, 2 },
+ { "AutoState2", 28, 2 },
+ { "AutoState1", 26, 2 },
+ { "ByteThreshold", 5, 20 },
+ { "MSSThreshold", 3, 2 },
+ { "AutoCareful", 2, 1 },
+ { "AutoEnable", 1, 1 },
+ { "Mode", 0, 1 },
+ { "TP_PC_CONFIG", 0x348, 0 },
+ { "TxTosQueueMapMode", 26, 1 },
+ { "RddpCongEn", 25, 1 },
+ { "EnableOnFlyPDU", 24, 1 },
+ { "EnableEPCMDAFull", 23, 1 },
+ { "ModulateUnionMode", 22, 1 },
+ { "TxDataAckRateEnable", 21, 1 },
+ { "TxDeferEnable", 20, 1 },
+ { "RxCongestionMode", 19, 1 },
+ { "HearbeatOnceDACK", 18, 1 },
+ { "HearbeatOnceHeap", 17, 1 },
+ { "HearbeatDACK", 16, 1 },
+ { "TxCongestionMode", 15, 1 },
+ { "AcceptLatestRcvAdv", 14, 1 },
+ { "DisableSYNData", 13, 1 },
+ { "DisableWindowPSH", 12, 1 },
+ { "DisableFINOldData", 11, 1 },
+ { "EnableFLMError", 10, 1 },
+ { "DisableFINOldDataFix", 9, 1 },
+ { "FilterPeerFIN", 8, 1 },
+ { "EnableFeedbackSend", 7, 1 },
+ { "EnableRDMAError", 6, 1 },
+ { "EnableDDPFlowControl", 5, 1 },
+ { "DisableHeldData", 4, 1 },
+ { "TableLatencyDelta", 0, 4 },
+ { "TP_TCP_BACKOFF_REG0", 0x350, 0 },
+ { "TimerBackoffIndex3", 24, 8 },
+ { "TimerBackoffIndex2", 16, 8 },
+ { "TimerBackoffIndex1", 8, 8 },
+ { "TimerBackoffIndex0", 0, 8 },
+ { "TP_TCP_BACKOFF_REG1", 0x354, 0 },
+ { "TimerBackoffIndex7", 24, 8 },
+ { "TimerBackoffIndex6", 16, 8 },
+ { "TimerBackoffIndex5", 8, 8 },
+ { "TimerBackoffIndex4", 0, 8 },
+ { "TP_TCP_BACKOFF_REG2", 0x358, 0 },
+ { "TimerBackoffIndex11", 24, 8 },
+ { "TimerBackoffIndex10", 16, 8 },
+ { "TimerBackoffIndex9", 8, 8 },
+ { "TimerBackoffIndex8", 0, 8 },
+ { "TP_TCP_BACKOFF_REG3", 0x35c, 0 },
+ { "TimerBackoffIndex15", 24, 8 },
+ { "TimerBackoffIndex14", 16, 8 },
+ { "TimerBackoffIndex13", 8, 8 },
+ { "TimerBackoffIndex12", 0, 8 },
+ { "TP_PARA_REG0", 0x360, 0 },
+ { "InitCwnd", 24, 3 },
+ { "DupAckThresh", 20, 4 },
+ { "TP_PARA_REG1", 0x364, 0 },
+ { "InitRwnd", 16, 16 },
+ { "InitialSSThresh", 0, 16 },
+ { "TP_PARA_REG2", 0x368, 0 },
+ { "MaxRxData", 16, 16 },
+ { "RxCoalesceSize", 0, 16 },
+ { "TP_PARA_REG3", 0x36c, 0 },
+ { "TunnelCngDrop1", 21, 1 },
+ { "TunnelCngDrop0", 20, 1 },
+ { "TxDataAckIdx", 16, 4 },
+ { "RxFragEnable", 12, 3 },
+ { "TxPaceFixedStrict", 11, 1 },
+ { "TxPaceAutoStrict", 10, 1 },
+ { "TxPaceFixed", 9, 1 },
+ { "TxPaceAuto", 8, 1 },
+ { "RxUrgMode", 5, 1 },
+ { "TxUrgMode", 4, 1 },
+ { "CngCtrlMode", 2, 2 },
+ { "RxCoalesceEnable", 1, 1 },
+ { "RxCoalescePshEn", 0, 1 },
+ { "TP_PARA_REG4", 0x370, 0 },
+ { "HighSpeedCfg", 24, 8 },
+ { "NewRenoCfg", 16, 8 },
+ { "TahoeCfg", 8, 8 },
+ { "RenoCfg", 0, 8 },
+ { "TP_PARA_REG5", 0x374, 0 },
+ { "IndicateSize", 16, 16 },
+ { "SchdEnable", 8, 1 },
+ { "OnFlyDDPEnable", 2, 1 },
+ { "DackTimerSpin", 1, 1 },
+ { "PushTimerEnable", 0, 1 },
+ { "TP_PARA_REG6", 0x378, 0 },
+ { "TxPDUSizeAdj", 16, 8 },
+ { "EnableEPDU", 14, 1 },
+ { "EnableESnd", 13, 1 },
+ { "EnableCSnd", 12, 1 },
+ { "EnableDeferACK", 9, 1 },
+ { "EnablePDUC", 8, 1 },
+ { "EnablePDUI", 7, 1 },
+ { "EnablePDUE", 6, 1 },
+ { "EnableDefer", 5, 1 },
+ { "EnableClearRxmtOos", 4, 1 },
+ { "DisablePDUCng", 3, 1 },
+ { "DisablePDUTimeout", 2, 1 },
+ { "DisablePDURxmt", 1, 1 },
+ { "DisablePDUxmt", 0, 1 },
+ { "TP_PARA_REG7", 0x37c, 0 },
+ { "PMMaxXferLen1", 16, 16 },
+ { "PMMaxXferLen0", 0, 16 },
+ { "TP_TIMER_RESOLUTION", 0x390, 0 },
+ { "TimerResolution", 16, 8 },
+ { "TimestampResolution", 8, 8 },
+ { "DelayedACKResolution", 0, 8 },
+ { "TP_MSL", 0x394, 0 },
+ { "MSL", 0, 30 },
+ { "TP_RXT_MIN", 0x398, 0 },
+ { "RxtMin", 0, 30 },
+ { "TP_RXT_MAX", 0x39c, 0 },
+ { "RxtMax", 0, 30 },
+ { "TP_PERS_MIN", 0x3a0, 0 },
+ { "PersMin", 0, 30 },
+ { "TP_PERS_MAX", 0x3a4, 0 },
+ { "PersMax", 0, 30 },
+ { "TP_KEEP_IDLE", 0x3a8, 0 },
+ { "KeepaliveIdle", 0, 30 },
+ { "TP_KEEP_INTVL", 0x3ac, 0 },
+ { "KeepaliveIntvl", 0, 30 },
+ { "TP_INIT_SRTT", 0x3b0, 0 },
+ { "InitSrtt", 0, 16 },
+ { "TP_DACK_TIMER", 0x3b4, 0 },
+ { "DackTime", 0, 12 },
+ { "TP_FINWAIT2_TIMER", 0x3b8, 0 },
+ { "Finwait2Time", 0, 30 },
+ { "TP_FAST_FINWAIT2_TIMER", 0x3bc, 0 },
+ { "FastFinwait2Time", 0, 30 },
+ { "TP_SHIFT_CNT", 0x3c0, 0 },
+ { "SynShiftMax", 24, 8 },
+ { "RxtShiftMaxR1", 20, 4 },
+ { "RxtShiftMaxR2", 16, 4 },
+ { "PerShiftBackoffMax", 12, 4 },
+ { "PerShiftMax", 8, 4 },
+ { "KeepaliveMax", 0, 8 },
+ { "TP_TIME_HI", 0x3c8, 0 },
+ { "TP_TIME_LO", 0x3cc, 0 },
+ { "TP_ULP_TABLE", 0x3d4, 0 },
+ { "ULPType7Field", 28, 4 },
+ { "ULPType6Field", 24, 4 },
+ { "ULPType5Field", 20, 4 },
+ { "ULPType4Field", 16, 4 },
+ { "ULPType3Field", 12, 4 },
+ { "ULPType2Field", 8, 4 },
+ { "ULPType1Field", 4, 4 },
+ { "ULPType0Field", 0, 4 },
+ { "TP_PACE_TABLE", 0x3d8, 0 },
+ { "TP_CCTRL_TABLE", 0x3dc, 0 },
+ { "TP_TOS_TABLE", 0x3e0, 0 },
+ { "TP_MTU_TABLE", 0x3e4, 0 },
+ { "TP_RSS_MAP_TABLE", 0x3e8, 0 },
+ { "TP_RSS_LKP_TABLE", 0x3ec, 0 },
+ { "TP_RSS_CONFIG", 0x3f0, 0 },
+ { "TNL4tupEn", 29, 1 },
+ { "TNL2tupEn", 28, 1 },
+ { "TNLprtEn", 26, 1 },
+ { "TNLMapEn", 25, 1 },
+ { "TNLLkpEn", 24, 1 },
+ { "OFD4tupEn", 21, 1 },
+ { "OFD2tupEn", 20, 1 },
+ { "OFDMapEn", 17, 1 },
+ { "OFDLkpEn", 16, 1 },
+ { "SYN4tupEn", 13, 1 },
+ { "SYN2tupEn", 12, 1 },
+ { "SYNMapEn", 9, 1 },
+ { "SYNLkpEn", 8, 1 },
+ { "RRCPLMapEn", 7, 1 },
+ { "RRCPLCPUSIZE", 4, 3 },
+ { "RQFeedbackEnable", 3, 1 },
+ { "HashToeplitz", 2, 1 },
+ { "HashSave", 1, 1 },
+ { "Disable", 0, 1 },
+ { "TP_RSS_CONFIG_TNL", 0x3f4, 0 },
+ { "MaskSize", 28, 3 },
+ { "DefaultCPUBase", 22, 6 },
+ { "DefaultCPU", 16, 6 },
+ { "DefaultQueue", 0, 16 },
+ { "TP_RSS_CONFIG_OFD", 0x3f8, 0 },
+ { "MaskSize", 28, 3 },
+ { "DefaultCPUBase", 22, 6 },
+ { "DefaultCPU", 16, 6 },
+ { "DefaultQueue", 0, 16 },
+ { "TP_RSS_CONFIG_SYN", 0x3fc, 0 },
+ { "MaskSize", 28, 3 },
+ { "DefaultCPUBase", 22, 6 },
+ { "DefaultCPU", 16, 6 },
+ { "DefaultQueue", 0, 16 },
+ { "TP_RSS_SECRET_KEY0", 0x400, 0 },
+ { "TP_RSS_SECRET_KEY1", 0x404, 0 },
+ { "TP_RSS_SECRET_KEY2", 0x408, 0 },
+ { "TP_RSS_SECRET_KEY3", 0x40c, 0 },
+ { "TP_TM_PIO_ADDR", 0x418, 0 },
+ { "TP_TM_PIO_DATA", 0x41c, 0 },
+ { "TP_TX_MOD_QUE_TABLE", 0x420, 0 },
+ { "TP_TX_RESOURCE_LIMIT", 0x424, 0 },
+ { "TX_RESOURCE_LIMIT_CH1_PC", 24, 8 },
+ { "TX_RESOURCE_LIMIT_CH1_NON_PC", 16, 8 },
+ { "TX_RESOURCE_LIMIT_CH0_PC", 8, 8 },
+ { "TX_RESOURCE_LIMIT_CH0_NON_PC", 0, 8 },
+ { "TP_TX_MOD_QUEUE_REQ_MAP", 0x428, 0 },
+ { "RX_MOD_WEIGHT", 24, 8 },
+ { "TX_MOD_WEIGHT", 16, 8 },
+ { "TX_MOD_TIMER_MODE", 8, 8 },
+ { "TX_MOD_QUEUE_REQ_MAP", 0, 8 },
+ { "TP_TX_MOD_QUEUE_WEIGHT1", 0x42c, 0 },
+ { "TP_TX_MOD_QUEUE_WEIGHT7", 24, 8 },
+ { "TP_TX_MOD_QUEUE_WEIGHT6", 16, 8 },
+ { "TP_TX_MOD_QUEUE_WEIGHT5", 8, 8 },
+ { "TP_TX_MOD_QUEUE_WEIGHT4", 0, 8 },
+ { "TP_TX_MOD_QUEUE_WEIGHT0", 0x430, 0 },
+ { "TP_TX_MOD_QUEUE_WEIGHT3", 24, 8 },
+ { "TP_TX_MOD_QUEUE_WEIGHT2", 16, 8 },
+ { "TP_TX_MOD_QUEUE_WEIGHT1", 8, 8 },
+ { "TP_TX_MOD_QUEUE_WEIGHT0", 0, 8 },
+ { "TP_MOD_CHANNEL_WEIGHT", 0x434, 0 },
+ { "RX_MOD_CHANNEL_WEIGHT1", 24, 8 },
+ { "RX_MOD_CHANNEL_WEIGHT0", 16, 8 },
+ { "TX_MOD_CHANNEL_WEIGHT1", 8, 8 },
+ { "TX_MOD_CHANNEL_WEIGHT0", 0, 8 },
+ { "TP_MOD_RATE_LIMIT", 0x438, 0 },
+ { "RX_MOD_RATE_LIMIT_INC", 24, 8 },
+ { "RX_MOD_RATE_LIMIT_TICK", 16, 8 },
+ { "TX_MOD_RATE_LIMIT_INC", 8, 8 },
+ { "TX_MOD_RATE_LIMIT_TICK", 0, 8 },
+ { "TP_PIO_ADDR", 0x440, 0 },
+ { "TP_PIO_DATA", 0x444, 0 },
+ { "TP_RESET", 0x44c, 0 },
+ { "FlstInitEnable", 1, 1 },
+ { "TPReset", 0, 1 },
+ { "TP_MIB_INDEX", 0x450, 0 },
+ { "TP_MIB_RDATA", 0x454, 0 },
+ { "TP_SYNC_TIME_HI", 0x458, 0 },
+ { "TP_SYNC_TIME_LO", 0x45c, 0 },
+ { "TP_CMM_MM_RX_FLST_BASE", 0x460, 0 },
+ { "CMRxFlstBase", 0, 28 },
+ { "TP_CMM_MM_TX_FLST_BASE", 0x464, 0 },
+ { "CMTxFlstBase", 0, 28 },
+ { "TP_CMM_MM_PS_FLST_BASE", 0x468, 0 },
+ { "CMPsFlstBase", 0, 28 },
+ { "TP_CMM_MM_MAX_PSTRUCT", 0x46c, 0 },
+ { "CMMaxPstruct", 0, 21 },
+ { "TP_INT_ENABLE", 0x470, 0 },
+ { "TP_INT_CAUSE", 0x474, 0 },
+ { "TP_FLM_FREE_PS_CNT", 0x480, 0 },
+ { "FreePstructCount", 0, 21 },
+ { "TP_FLM_FREE_RX_CNT", 0x484, 0 },
+ { "FreeRxPageCount", 0, 21 },
+ { "TP_FLM_FREE_TX_CNT", 0x488, 0 },
+ { "FreeTxPageCount", 0, 21 },
+ { "TP_TM_HEAP_PUSH_CNT", 0x48c, 0 },
+ { "TP_TM_HEAP_POP_CNT", 0x490, 0 },
+ { "TP_TM_DACK_PUSH_CNT", 0x494, 0 },
+ { "TP_TM_DACK_POP_CNT", 0x498, 0 },
+ { "TP_TM_MOD_PUSH_CNT", 0x49c, 0 },
+ { "TP_MOD_POP_CNT", 0x4a0, 0 },
+ { "TP_TIMER_SEPARATOR", 0x4a4, 0 },
+ { "TP_DEBUG_SEL", 0x4a8, 0 },
+ { "TP_DEBUG_FLAGS", 0x4ac, 0 },
+ { "RXDebugFlags", 16, 16 },
+ { "TXDebugFlags", 0, 16 },
+ { "TP_CM_FLOW_CNTL_MODE", 0x4b0, 0 },
+ { "CMFlowCacheDisable", 0, 1 },
+ { "TP_PC_CONGESTION_CNTL", 0x4b4, 0 },
+ { "EDropTunnel", 19, 1 },
+ { "CDropTunnel", 18, 1 },
+ { "EThreshold", 12, 6 },
+ { "CThreshold", 6, 6 },
+ { "TxThreshold", 0, 6 },
+ { "TP_TX_DROP_COUNT", 0x4bc, 0 },
+ { "TP_CLEAR_DEBUG", 0x4c0, 0 },
+ { "ClrDebug", 0, 1 },
+ { "TP_DEBUG_VEC", 0x4c4, 0 },
+ { "TP_DEBUG_VEC2", 0x4c8, 0 },
+ { "TP_DEBUG_REG_SEL", 0x4cc, 0 },
+ { "TP_DEBUG", 0x4d0, 0 },
+ { "TP_DBG_LA_CONFIG", 0x4d4, 0 },
+ { "TP_DBG_LA_DATAH", 0x4d8, 0 },
+ { "TP_DBG_LA_DATAL", 0x4dc, 0 },
+ { "TP_EMBED_OP_FIELD0", 0x4e8, 0 },
+ { "TP_EMBED_OP_FIELD1", 0x4ec, 0 },
+ { "TP_EMBED_OP_FIELD2", 0x4f0, 0 },
+ { "TP_EMBED_OP_FIELD3", 0x4f4, 0 },
+ { "TP_EMBED_OP_FIELD4", 0x4f8, 0 },
+ { "TP_EMBED_OP_FIELD5", 0x4fc, 0 },
+ { NULL }
+};
+
+struct reg_info ulp2_rx_regs[] = {
+ { "ULPRX_CTL", 0x500, 0 },
+ { "PCMD1Threshold", 24, 8 },
+ { "PCMD0Threshold", 16, 8 },
+ { "round_robin", 4, 1 },
+ { "RDMA_permissive_mode", 3, 1 },
+ { "PagePodME", 2, 1 },
+ { "IscsiTagTcb", 1, 1 },
+ { "TddpTagTcb", 0, 1 },
+ { "ULPRX_INT_ENABLE", 0x504, 0 },
+ { "ParErr", 0, 1 },
+ { "ULPRX_INT_CAUSE", 0x508, 0 },
+ { "ParErr", 0, 1 },
+ { "ULPRX_ISCSI_LLIMIT", 0x50c, 0 },
+ { "IscsiLlimit", 6, 26 },
+ { "ULPRX_ISCSI_ULIMIT", 0x510, 0 },
+ { "IscsiUlimit", 6, 26 },
+ { "ULPRX_ISCSI_TAGMASK", 0x514, 0 },
+ { "IscsiTagMask", 6, 26 },
+ { "ULPRX_ISCSI_PSZ", 0x518, 0 },
+ { "Hpz3", 24, 4 },
+ { "Hpz2", 16, 4 },
+ { "Hpz1", 8, 4 },
+ { "Hpz0", 0, 4 },
+ { "ULPRX_TDDP_LLIMIT", 0x51c, 0 },
+ { "TddpLlimit", 6, 26 },
+ { "ULPRX_TDDP_ULIMIT", 0x520, 0 },
+ { "TddpUlimit", 6, 26 },
+ { "ULPRX_TDDP_TAGMASK", 0x524, 0 },
+ { "TddpTagMask", 6, 26 },
+ { "ULPRX_TDDP_PSZ", 0x528, 0 },
+ { "Hpz3", 24, 4 },
+ { "Hpz2", 16, 4 },
+ { "Hpz1", 8, 4 },
+ { "Hpz0", 0, 4 },
+ { "ULPRX_STAG_LLIMIT", 0x52c, 0 },
+ { "ULPRX_STAG_ULIMIT", 0x530, 0 },
+ { "ULPRX_RQ_LLIMIT", 0x534, 0 },
+ { "ULPRX_RQ_ULIMIT", 0x538, 0 },
+ { "ULPRX_PBL_LLIMIT", 0x53c, 0 },
+ { "ULPRX_PBL_ULIMIT", 0x540, 0 },
+ { NULL }
+};
+
+struct reg_info ulp2_tx_regs[] = {
+ { "ULPTX_CONFIG", 0x580, 0 },
+ { "CFG_RR_ARB", 0, 1 },
+ { "ULPTX_INT_ENABLE", 0x584, 0 },
+ { "Pbl_bound_err_ch1", 1, 1 },
+ { "Pbl_bound_err_ch0", 0, 1 },
+ { "ULPTX_INT_CAUSE", 0x588, 0 },
+ { "Pbl_bound_err_ch1", 1, 1 },
+ { "Pbl_bound_err_ch0", 0, 1 },
+ { "ULPTX_TPT_LLIMIT", 0x58c, 0 },
+ { "ULPTX_TPT_ULIMIT", 0x590, 0 },
+ { "ULPTX_PBL_LLIMIT", 0x594, 0 },
+ { "ULPTX_PBL_ULIMIT", 0x598, 0 },
+ { "ULPTX_CPL_ERR_OFFSET", 0x59c, 0 },
+ { "ULPTX_CPL_ERR_MASK", 0x5a0, 0 },
+ { "ULPTX_CPL_ERR_VALUE", 0x5a4, 0 },
+ { "ULPTX_CPL_PACK_SIZE", 0x5a8, 0 },
+ { "value", 24, 8 },
+ { "Ch1Size2", 24, 8 },
+ { "Ch1Size1", 16, 8 },
+ { "Ch0Size2", 8, 8 },
+ { "Ch0Size1", 0, 8 },
+ { "ULPTX_DMA_WEIGHT", 0x5ac, 0 },
+ { "D1_WEIGHT", 16, 16 },
+ { "D0_WEIGHT", 0, 16 },
+ { NULL }
+};
+
+struct reg_info pm1_rx_regs[] = {
+ { "PM1_RX_CFG", 0x5c0, 0 },
+ { "PM1_RX_MODE", 0x5c4, 0 },
+ { "stat_channel", 1, 1 },
+ { "priority_ch", 0, 1 },
+ { "PM1_RX_STAT_CONFIG", 0x5c8, 0 },
+ { "PM1_RX_STAT_COUNT", 0x5cc, 0 },
+ { "PM1_RX_STAT_MSB", 0x5d0, 0 },
+ { "PM1_RX_STAT_LSB", 0x5d4, 0 },
+ { "PM1_RX_INT_ENABLE", 0x5d8, 0 },
+ { "zero_e_cmd_error", 18, 1 },
+ { "iespi0_fifo2x_Rx_framing_error", 17, 1 },
+ { "iespi1_fifo2x_Rx_framing_error", 16, 1 },
+ { "iespi0_Rx_framing_error", 15, 1 },
+ { "iespi1_Rx_framing_error", 14, 1 },
+ { "iespi0_Tx_framing_error", 13, 1 },
+ { "iespi1_Tx_framing_error", 12, 1 },
+ { "ocspi0_Rx_framing_error", 11, 1 },
+ { "ocspi1_Rx_framing_error", 10, 1 },
+ { "ocspi0_Tx_framing_error", 9, 1 },
+ { "ocspi1_Tx_framing_error", 8, 1 },
+ { "ocspi0_ofifo2x_Tx_framing_error", 7, 1 },
+ { "ocspi1_ofifo2x_Tx_framing_error", 6, 1 },
+ { "iespi_par_error", 3, 3 },
+ { "ocspi_par_error", 0, 3 },
+ { "PM1_RX_INT_CAUSE", 0x5dc, 0 },
+ { "zero_e_cmd_error", 18, 1 },
+ { "iespi0_fifo2x_Rx_framing_error", 17, 1 },
+ { "iespi1_fifo2x_Rx_framing_error", 16, 1 },
+ { "iespi0_Rx_framing_error", 15, 1 },
+ { "iespi1_Rx_framing_error", 14, 1 },
+ { "iespi0_Tx_framing_error", 13, 1 },
+ { "iespi1_Tx_framing_error", 12, 1 },
+ { "ocspi0_Rx_framing_error", 11, 1 },
+ { "ocspi1_Rx_framing_error", 10, 1 },
+ { "ocspi0_Tx_framing_error", 9, 1 },
+ { "ocspi1_Tx_framing_error", 8, 1 },
+ { "ocspi0_ofifo2x_Tx_framing_error", 7, 1 },
+ { "ocspi1_ofifo2x_Tx_framing_error", 6, 1 },
+ { "iespi_par_error", 3, 3 },
+ { "ocspi_par_error", 0, 3 },
+ { NULL }
+};
+
+struct reg_info pm1_tx_regs[] = {
+ { "PM1_TX_CFG", 0x5e0, 0 },
+ { "PM1_TX_MODE", 0x5e4, 0 },
+ { "stat_channel", 1, 1 },
+ { "priority_ch", 0, 1 },
+ { "PM1_TX_STAT_CONFIG", 0x5e8, 0 },
+ { "PM1_TX_STAT_COUNT", 0x5ec, 0 },
+ { "PM1_TX_STAT_MSB", 0x5f0, 0 },
+ { "PM1_TX_STAT_LSB", 0x5f4, 0 },
+ { "PM1_TX_INT_ENABLE", 0x5f8, 0 },
+ { "zero_c_cmd_error", 18, 1 },
+ { "icspi0_fifo2x_Rx_framing_error", 17, 1 },
+ { "icspi1_fifo2x_Rx_framing_error", 16, 1 },
+ { "icspi0_Rx_framing_error", 15, 1 },
+ { "icspi1_Rx_framing_error", 14, 1 },
+ { "icspi0_Tx_framing_error", 13, 1 },
+ { "icspi1_Tx_framing_error", 12, 1 },
+ { "oespi0_Rx_framing_error", 11, 1 },
+ { "oespi1_Rx_framing_error", 10, 1 },
+ { "oespi0_Tx_framing_error", 9, 1 },
+ { "oespi1_Tx_framing_error", 8, 1 },
+ { "oespi0_ofifo2x_Tx_framing_error", 7, 1 },
+ { "oespi1_ofifo2x_Tx_framing_error", 6, 1 },
+ { "icspi_par_error", 3, 3 },
+ { "oespi_par_error", 0, 3 },
+ { "PM1_TX_INT_CAUSE", 0x5fc, 0 },
+ { "zero_c_cmd_error", 18, 1 },
+ { "icspi0_fifo2x_Rx_framing_error", 17, 1 },
+ { "icspi1_fifo2x_Rx_framing_error", 16, 1 },
+ { "icspi0_Rx_framing_error", 15, 1 },
+ { "icspi1_Rx_framing_error", 14, 1 },
+ { "icspi0_Tx_framing_error", 13, 1 },
+ { "icspi1_Tx_framing_error", 12, 1 },
+ { "oespi0_Rx_framing_error", 11, 1 },
+ { "oespi1_Rx_framing_error", 10, 1 },
+ { "oespi0_Tx_framing_error", 9, 1 },
+ { "oespi1_Tx_framing_error", 8, 1 },
+ { "oespi0_ofifo2x_Tx_framing_error", 7, 1 },
+ { "oespi1_ofifo2x_Tx_framing_error", 6, 1 },
+ { "icspi_par_error", 3, 3 },
+ { "oespi_par_error", 0, 3 },
+ { NULL }
+};
+
+struct reg_info mps0_regs[] = {
+ { "MPS_CFG", 0x600, 0 },
+ { "SGETPQid", 8, 3 },
+ { "TPRxPortSize", 7, 1 },
+ { "TPTxPort1Size", 6, 1 },
+ { "TPTxPort0Size", 5, 1 },
+ { "TPRxPortEn", 4, 1 },
+ { "TPTxPort1En", 3, 1 },
+ { "TPTxPort0En", 2, 1 },
+ { "Port1Active", 1, 1 },
+ { "Port0Active", 0, 1 },
+ { "MPS_DRR_CFG1", 0x604, 0 },
+ { "RldWtTPD1", 11, 11 },
+ { "RldWtTPD0", 0, 11 },
+ { "MPS_DRR_CFG2", 0x608, 0 },
+ { "RldWtTotal", 0, 12 },
+ { "MPS_MCA_STATUS", 0x60c, 0 },
+ { "MCAPktCnt", 12, 20 },
+ { "MCADepth", 0, 12 },
+ { "MPS_TX0_TP_CNT", 0x610, 0 },
+ { "TX0TPDisCnt", 24, 8 },
+ { "TX0TPCnt", 0, 24 },
+ { "MPS_TX1_TP_CNT", 0x614, 0 },
+ { "TX1TPDisCnt", 24, 8 },
+ { "TX1TPCnt", 0, 24 },
+ { "MPS_RX_TP_CNT", 0x618, 0 },
+ { "RXTPDisCnt", 24, 8 },
+ { "RXTPCnt", 0, 24 },
+ { "MPS_INT_ENABLE", 0x61c, 0 },
+ { "MCAParErrEnb", 6, 3 },
+ { "RXTpParErrEnb", 4, 2 },
+ { "TX1TpParErrEnb", 2, 2 },
+ { "TX0TpParErrEnb", 0, 2 },
+ { "MPS_INT_CAUSE", 0x620, 0 },
+ { "MCAParErr", 6, 3 },
+ { "RXTpParErr", 4, 2 },
+ { "TX1TpParErr", 2, 2 },
+ { "TX0TpParErr", 0, 2 },
+ { NULL }
+};
+
+struct reg_info cpl_switch_regs[] = {
+ { "CPL_SWITCH_CNTRL", 0x640, 0 },
+ { "cpl_pkt_tid", 8, 24 },
+ { "cpu_no_3F_CIM_enable", 3, 1 },
+ { "switch_table_enable", 2, 1 },
+ { "sge_enable", 1, 1 },
+ { "cim_enable", 0, 1 },
+ { "CPL_SWITCH_TBL_IDX", 0x644, 0 },
+ { "switch_tbl_idx", 0, 4 },
+ { "CPL_SWITCH_TBL_DATA", 0x648, 0 },
+ { "CPL_SWITCH_ZERO_ERROR", 0x64c, 0 },
+ { "zero_cmd", 0, 8 },
+ { "CPL_INTR_ENABLE", 0x650, 0 },
+ { "cim_ovfl_error", 4, 1 },
+ { "tp_framing_error", 3, 1 },
+ { "sge_framing_error", 2, 1 },
+ { "cim_framing_error", 1, 1 },
+ { "zero_switch_error", 0, 1 },
+ { "CPL_INTR_CAUSE", 0x654, 0 },
+ { "cim_ovfl_error", 4, 1 },
+ { "tp_framing_error", 3, 1 },
+ { "sge_framing_error", 2, 1 },
+ { "cim_framing_error", 1, 1 },
+ { "zero_switch_error", 0, 1 },
+ { "CPL_MAP_TBL_IDX", 0x658, 0 },
+ { "cpl_map_tbl_idx", 0, 8 },
+ { "CPL_MAP_TBL_DATA", 0x65c, 0 },
+ { "cpl_map_tbl_data", 0, 8 },
+ { NULL }
+};
+
+struct reg_info smb0_regs[] = {
+ { "SMB_GLOBAL_TIME_CFG", 0x660, 0 },
+ { "LADbgWrPtr", 24, 8 },
+ { "LADbgRdPtr", 16, 8 },
+ { "LADbgEn", 13, 1 },
+ { "MacroCntCfg", 8, 5 },
+ { "MicroCntCfg", 0, 8 },
+ { "SMB_MST_TIMEOUT_CFG", 0x664, 0 },
+ { "DebugSelH", 28, 4 },
+ { "DebugSelL", 24, 4 },
+ { "MstTimeOutCfg", 0, 24 },
+ { "SMB_MST_CTL_CFG", 0x668, 0 },
+ { "MstFifoDbg", 31, 1 },
+ { "MstFifoDbgClr", 30, 1 },
+ { "MstRxByteCfg", 12, 6 },
+ { "MstTxByteCfg", 6, 6 },
+ { "MstReset", 1, 1 },
+ { "MstCtlEn", 0, 1 },
+ { "SMB_MST_CTL_STS", 0x66c, 0 },
+ { "MstRxByteCnt", 12, 6 },
+ { "MstTxByteCnt", 6, 6 },
+ { "MstBusySts", 0, 1 },
+ { "SMB_MST_TX_FIFO_RDWR", 0x670, 0 },
+ { "SMB_MST_RX_FIFO_RDWR", 0x674, 0 },
+ { "SMB_SLV_TIMEOUT_CFG", 0x678, 0 },
+ { "SlvTimeOutCfg", 0, 24 },
+ { "SMB_SLV_CTL_CFG", 0x67c, 0 },
+ { "SlvFifoDbg", 31, 1 },
+ { "SlvFifoDbgClr", 30, 1 },
+ { "SlvAddrCfg", 4, 7 },
+ { "SlvAlrtSet", 2, 1 },
+ { "SlvReset", 1, 1 },
+ { "SlvCtlEn", 0, 1 },
+ { "SMB_SLV_CTL_STS", 0x680, 0 },
+ { "SlvFifoTxCnt", 12, 6 },
+ { "SlvFifoCnt", 6, 6 },
+ { "SlvAlrtSts", 2, 1 },
+ { "SlvBusySts", 0, 1 },
+ { "SMB_SLV_FIFO_RDWR", 0x684, 0 },
+ { "SMB_SLV_CMD_FIFO_RDWR", 0x688, 0 },
+ { "SMB_INT_ENABLE", 0x68c, 0 },
+ { "SlvTimeOutIntEn", 7, 1 },
+ { "SlvErrIntEn", 6, 1 },
+ { "SlvDoneIntEn", 5, 1 },
+ { "SlvRxRdyIntEn", 4, 1 },
+ { "MstTimeOutIntEn", 3, 1 },
+ { "MstNAckIntEn", 2, 1 },
+ { "MstLostArbIntEn", 1, 1 },
+ { "MstDoneIntEn", 0, 1 },
+ { "SMB_INT_CAUSE", 0x690, 0 },
+ { "SlvTimeOutInt", 7, 1 },
+ { "SlvErrInt", 6, 1 },
+ { "SlvDoneInt", 5, 1 },
+ { "SlvRxRdyInt", 4, 1 },
+ { "MstTimeOutInt", 3, 1 },
+ { "MstNAckInt", 2, 1 },
+ { "MstLostArbInt", 1, 1 },
+ { "MstDoneInt", 0, 1 },
+ { "SMB_DEBUG_DATA", 0x694, 0 },
+ { "DebugDataH", 16, 16 },
+ { "DebugDataL", 0, 16 },
+ { "SMB_DEBUG_LA", 0x69c, 0 },
+ { "DebugLAReqAddr", 0, 10 },
+ { NULL }
+};
+
+struct reg_info i2cm0_regs[] = {
+ { "I2C_CFG", 0x6a0, 0 },
+ { "ClkDiv", 0, 12 },
+ { "I2C_DATA", 0x6a4, 0 },
+ { "Data", 0, 8 },
+ { "I2C_OP", 0x6a8, 0 },
+ { "Busy", 31, 1 },
+ { "Ack", 30, 1 },
+ { "Cont", 1, 1 },
+ { "Op", 0, 1 },
+ { NULL }
+};
+
+struct reg_info mi1_regs[] = {
+ { "MI1_CFG", 0x6b0, 0 },
+ { "ClkDiv", 5, 8 },
+ { "St", 3, 2 },
+ { "PreEn", 2, 1 },
+ { "MDIInv", 1, 1 },
+ { "MDIEn", 0, 1 },
+ { "MI1_ADDR", 0x6b4, 0 },
+ { "PhyAddr", 5, 5 },
+ { "RegAddr", 0, 5 },
+ { "MI1_DATA", 0x6b8, 0 },
+ { "Data", 0, 16 },
+ { "MI1_OP", 0x6bc, 0 },
+ { "Busy", 31, 1 },
+ { "Inc", 2, 1 },
+ { "Op", 0, 2 },
+ { NULL }
+};
+
+struct reg_info jm1_regs[] = {
+ { "JM_CFG", 0x6c0, 0 },
+ { "ClkDiv", 2, 8 },
+ { "TRst", 1, 1 },
+ { "En", 0, 1 },
+ { "JM_MODE", 0x6c4, 0 },
+ { "JM_DATA", 0x6c8, 0 },
+ { "JM_OP", 0x6cc, 0 },
+ { "Busy", 31, 1 },
+ { "Cnt", 0, 5 },
+ { NULL }
+};
+
+struct reg_info sf1_regs[] = {
+ { "SF_DATA", 0x6d8, 0 },
+ { "SF_OP", 0x6dc, 0 },
+ { "Busy", 31, 1 },
+ { "Cont", 3, 1 },
+ { "ByteCnt", 1, 2 },
+ { "Op", 0, 1 },
+ { NULL }
+};
+
+struct reg_info pl3_regs[] = {
+ { "PL_INT_ENABLE0", 0x6e0, 0 },
+ { "EXT", 24, 1 },
+ { "T3DBG", 23, 1 },
+ { "XGMAC0_1", 20, 1 },
+ { "XGMAC0_0", 19, 1 },
+ { "MC5A", 18, 1 },
+ { "SF1", 17, 1 },
+ { "SMB0", 15, 1 },
+ { "I2CM0", 14, 1 },
+ { "MI1", 13, 1 },
+ { "CPL_SWITCH", 12, 1 },
+ { "MPS0", 11, 1 },
+ { "PM1_TX", 10, 1 },
+ { "PM1_RX", 9, 1 },
+ { "ULP2_TX", 8, 1 },
+ { "ULP2_RX", 7, 1 },
+ { "TP1", 6, 1 },
+ { "CIM", 5, 1 },
+ { "MC7_CM", 4, 1 },
+ { "MC7_PMTX", 3, 1 },
+ { "MC7_PMRX", 2, 1 },
+ { "PCIM0", 1, 1 },
+ { "SGE3", 0, 1 },
+ { "PL_INT_CAUSE0", 0x6e4, 0 },
+ { "EXT", 24, 1 },
+ { "T3DBG", 23, 1 },
+ { "XGMAC0_1", 20, 1 },
+ { "XGMAC0_0", 19, 1 },
+ { "MC5A", 18, 1 },
+ { "SF1", 17, 1 },
+ { "SMB0", 15, 1 },
+ { "I2CM0", 14, 1 },
+ { "MI1", 13, 1 },
+ { "CPL_SWITCH", 12, 1 },
+ { "MPS0", 11, 1 },
+ { "PM1_TX", 10, 1 },
+ { "PM1_RX", 9, 1 },
+ { "ULP2_TX", 8, 1 },
+ { "ULP2_RX", 7, 1 },
+ { "TP1", 6, 1 },
+ { "CIM", 5, 1 },
+ { "MC7_CM", 4, 1 },
+ { "MC7_PMTX", 3, 1 },
+ { "MC7_PMRX", 2, 1 },
+ { "PCIM0", 1, 1 },
+ { "SGE3", 0, 1 },
+ { "PL_INT_ENABLE1", 0x6e8, 0 },
+ { "EXT", 24, 1 },
+ { "T3DBG", 23, 1 },
+ { "XGMAC0_1", 20, 1 },
+ { "XGMAC0_0", 19, 1 },
+ { "MC5A", 18, 1 },
+ { "SF1", 17, 1 },
+ { "SMB0", 15, 1 },
+ { "I2CM0", 14, 1 },
+ { "MI1", 13, 1 },
+ { "CPL_SWITCH", 12, 1 },
+ { "MPS0", 11, 1 },
+ { "PM1_TX", 10, 1 },
+ { "PM1_RX", 9, 1 },
+ { "ULP2_TX", 8, 1 },
+ { "ULP2_RX", 7, 1 },
+ { "TP1", 6, 1 },
+ { "CIM", 5, 1 },
+ { "MC7_CM", 4, 1 },
+ { "MC7_PMTX", 3, 1 },
+ { "MC7_PMRX", 2, 1 },
+ { "PCIM0", 1, 1 },
+ { "SGE3", 0, 1 },
+ { "PL_INT_CAUSE1", 0x6ec, 0 },
+ { "EXT", 24, 1 },
+ { "T3DBG", 23, 1 },
+ { "XGMAC0_1", 20, 1 },
+ { "XGMAC0_0", 19, 1 },
+ { "MC5A", 18, 1 },
+ { "SF1", 17, 1 },
+ { "SMB0", 15, 1 },
+ { "I2CM0", 14, 1 },
+ { "MI1", 13, 1 },
+ { "CPL_SWITCH", 12, 1 },
+ { "MPS0", 11, 1 },
+ { "PM1_TX", 10, 1 },
+ { "PM1_RX", 9, 1 },
+ { "ULP2_TX", 8, 1 },
+ { "ULP2_RX", 7, 1 },
+ { "TP1", 6, 1 },
+ { "CIM", 5, 1 },
+ { "MC7_CM", 4, 1 },
+ { "MC7_PMTX", 3, 1 },
+ { "MC7_PMRX", 2, 1 },
+ { "PCIM0", 1, 1 },
+ { "SGE3", 0, 1 },
+ { "PL_RST", 0x6f0, 0 },
+ { "CRstWrm", 1, 1 },
+ { "CRstWrmMode", 0, 1 },
+ { "PL_REV", 0x6f4, 0 },
+ { "Rev", 0, 4 },
+ { "PL_CLI", 0x6f8, 0 },
+ { NULL }
+};
+
+struct reg_info mc5a_regs[] = {
+ { "MC5_BUF_CONFIG", 0x700, 0 },
+ { "term300_240", 31, 1 },
+ { "term150", 30, 1 },
+ { "term60", 29, 1 },
+ { "gddriii", 28, 1 },
+ { "gddrii", 27, 1 },
+ { "gddri", 26, 1 },
+ { "read", 25, 1 },
+ { "cal_imp_upd", 23, 1 },
+ { "cal_busy", 22, 1 },
+ { "cal_error", 21, 1 },
+ { "sgl_cal_en", 20, 1 },
+ { "imp_upd_mode", 19, 1 },
+ { "imp_sel", 18, 1 },
+ { "man_pu", 15, 3 },
+ { "man_pd", 12, 3 },
+ { "cal_pu", 9, 3 },
+ { "cal_pd", 6, 3 },
+ { "set_pu", 3, 3 },
+ { "set_pd", 0, 3 },
+ { "MC5_DB_CONFIG", 0x704, 0 },
+ { "TMCfgWrLock", 31, 1 },
+ { "TMTypeHi", 30, 1 },
+ { "TMPartSize", 28, 2 },
+ { "TMType", 26, 2 },
+ { "TMPartCount", 24, 2 },
+ { "nLIP", 18, 6 },
+ { "COMPEN", 17, 1 },
+ { "BUILD", 16, 1 },
+ { "TM_IO_PDOWN", 9, 1 },
+ { "SYNMode", 7, 2 },
+ { "PRTYEN", 6, 1 },
+ { "MBUSEN", 5, 1 },
+ { "DBGIEN", 4, 1 },
+ { "TMRDY", 2, 1 },
+ { "TMRST", 1, 1 },
+ { "TMMode", 0, 1 },
+ { "MC5_DB_ROUTING_TABLE_INDEX", 0x70c, 0 },
+ { "RTINDX", 0, 22 },
+ { "MC5_DB_SERVER_INDEX", 0x714, 0 },
+ { "SRINDX", 0, 22 },
+ { "MC5_DB_LIP_RAM_ADDR", 0x718, 0 },
+ { "RAMWR", 8, 1 },
+ { "RAMADDR", 0, 6 },
+ { "MC5_DB_LIP_RAM_DATA", 0x71c, 0 },
+ { "MC5_DB_RSP_LATENCY", 0x720, 0 },
+ { "RDLAT", 16, 5 },
+ { "LRNLAT", 8, 5 },
+ { "SRCHLAT", 0, 5 },
+ { "MC5_DB_PARITY_LATENCY", 0x724, 0 },
+ { "PARLAT", 0, 4 },
+ { "MC5_DB_WR_LRN_VERIFY", 0x728, 0 },
+ { "VWVEREN", 2, 1 },
+ { "LRNVEREN", 1, 1 },
+ { "POVEREN", 0, 1 },
+ { "MC5_DB_PART_ID_INDEX", 0x72c, 0 },
+ { "IDINDEX", 0, 4 },
+ { "MC5_DB_RESET_MAX", 0x730, 0 },
+ { "RSTMAX", 0, 4 },
+ { "MC5_DB_ACT_CNT", 0x734, 0 },
+ { "ACTCNT", 0, 20 },
+ { "MC5_DB_INT_ENABLE", 0x740, 0 },
+ { "MsgSel", 28, 4 },
+ { "DelActEmpty", 18, 1 },
+ { "DispQParErr", 17, 1 },
+ { "ReqQParErr", 16, 1 },
+ { "UnknownCmd", 15, 1 },
+ { "SYNCookieOff", 11, 1 },
+ { "SYNCookieBad", 10, 1 },
+ { "SYNCookie", 9, 1 },
+ { "NFASrchFail", 8, 1 },
+ { "ActRgnFull", 7, 1 },
+ { "ParityErr", 6, 1 },
+ { "LIPMiss", 5, 1 },
+ { "LIP0", 4, 1 },
+ { "Miss", 3, 1 },
+ { "RoutingHit", 2, 1 },
+ { "ActiveHit", 1, 1 },
+ { "ActiveOutHit", 0, 1 },
+ { "MC5_DB_INT_CAUSE", 0x744, 0 },
+ { "DelActEmpty", 18, 1 },
+ { "DispQParErr", 17, 1 },
+ { "ReqQParErr", 16, 1 },
+ { "UnknownCmd", 15, 1 },
+ { "SYNCookieOff", 11, 1 },
+ { "SYNCookieBad", 10, 1 },
+ { "SYNCookie", 9, 1 },
+ { "NFASrchFail", 8, 1 },
+ { "ActRgnFull", 7, 1 },
+ { "ParityErr", 6, 1 },
+ { "LIPMiss", 5, 1 },
+ { "LIP0", 4, 1 },
+ { "Miss", 3, 1 },
+ { "RoutingHit", 2, 1 },
+ { "ActiveHit", 1, 1 },
+ { "ActiveOutHit", 0, 1 },
+ { "MC5_DB_INT_TID", 0x748, 0 },
+ { "INTTID", 0, 20 },
+ { "MC5_DB_INT_PTID", 0x74c, 0 },
+ { "INTPTID", 0, 20 },
+ { "MC5_DB_DBGI_CONFIG", 0x774, 0 },
+ { "WRReqSize", 22, 10 },
+ { "SADRSel", 4, 1 },
+ { "CMDMode", 0, 3 },
+ { "MC5_DB_DBGI_REQ_CMD", 0x778, 0 },
+ { "MBusCmd", 0, 4 },
+ { "IDTCmdHi", 11, 3 },
+ { "IDTCmdLo", 0, 4 },
+ { "IDTCmd", 0, 20 },
+ { "LCMDB", 16, 11 },
+ { "LCMDA", 0, 11 },
+ { "MC5_DB_DBGI_REQ_ADDR0", 0x77c, 0 },
+ { "MC5_DB_DBGI_REQ_ADDR1", 0x780, 0 },
+ { "MC5_DB_DBGI_REQ_ADDR2", 0x784, 0 },
+ { "DBGIReqAdrHi", 0, 8 },
+ { "MC5_DB_DBGI_REQ_DATA0", 0x788, 0 },
+ { "MC5_DB_DBGI_REQ_DATA1", 0x78c, 0 },
+ { "MC5_DB_DBGI_REQ_DATA2", 0x790, 0 },
+ { "MC5_DB_DBGI_REQ_DATA3", 0x794, 0 },
+ { "MC5_DB_DBGI_REQ_DATA4", 0x798, 0 },
+ { "DBGIReqData4", 0, 16 },
+ { "MC5_DB_DBGI_REQ_MASK0", 0x79c, 0 },
+ { "MC5_DB_DBGI_REQ_MASK1", 0x7a0, 0 },
+ { "MC5_DB_DBGI_REQ_MASK2", 0x7a4, 0 },
+ { "MC5_DB_DBGI_REQ_MASK3", 0x7a8, 0 },
+ { "MC5_DB_DBGI_REQ_MASK4", 0x7ac, 0 },
+ { "DBGIReqMsk4", 0, 16 },
+ { "MC5_DB_DBGI_RSP_STATUS", 0x7b0, 0 },
+ { "DBGIRspMsg", 8, 4 },
+ { "DBGIRspMsgVld", 2, 1 },
+ { "DBGIRspHit", 1, 1 },
+ { "DBGIRspValid", 0, 1 },
+ { "MC5_DB_DBGI_RSP_DATA0", 0x7b4, 0 },
+ { "MC5_DB_DBGI_RSP_DATA1", 0x7b8, 0 },
+ { "MC5_DB_DBGI_RSP_DATA2", 0x7bc, 0 },
+ { "MC5_DB_DBGI_RSP_DATA3", 0x7c0, 0 },
+ { "MC5_DB_DBGI_RSP_DATA4", 0x7c4, 0 },
+ { "DBGIRspData3", 0, 16 },
+ { "MC5_DB_DBGI_RSP_LAST_CMD", 0x7c8, 0 },
+ { "LastCmdB", 16, 11 },
+ { "LastCmdA", 0, 11 },
+ { "MC5_DB_POPEN_DATA_WR_CMD", 0x7cc, 0 },
+ { "PO_DWR", 0, 20 },
+ { "MC5_DB_POPEN_MASK_WR_CMD", 0x7d0, 0 },
+ { "PO_MWR", 0, 20 },
+ { "MC5_DB_AOPEN_SRCH_CMD", 0x7d4, 0 },
+ { "AO_SRCH", 0, 20 },
+ { "MC5_DB_AOPEN_LRN_CMD", 0x7d8, 0 },
+ { "AO_LRN", 0, 20 },
+ { "MC5_DB_SYN_SRCH_CMD", 0x7dc, 0 },
+ { "SYN_SRCH", 0, 20 },
+ { "MC5_DB_SYN_LRN_CMD", 0x7e0, 0 },
+ { "SYN_LRN", 0, 20 },
+ { "MC5_DB_ACK_SRCH_CMD", 0x7e4, 0 },
+ { "ACK_SRCH", 0, 20 },
+ { "MC5_DB_ACK_LRN_CMD", 0x7e8, 0 },
+ { "ACK_LRN", 0, 20 },
+ { "MC5_DB_ILOOKUP_CMD", 0x7ec, 0 },
+ { "I_SRCH", 0, 20 },
+ { "MC5_DB_ELOOKUP_CMD", 0x7f0, 0 },
+ { "E_SRCH", 0, 20 },
+ { "MC5_DB_DATA_WRITE_CMD", 0x7f4, 0 },
+ { "Write", 0, 20 },
+ { "MC5_DB_DATA_READ_CMD", 0x7f8, 0 },
+ { "ReadCmd", 0, 20 },
+ { "MC5_DB_MASK_WRITE_CMD", 0x7fc, 0 },
+ { "MaskWr", 0, 16 },
+ { NULL }
+};
+
+struct reg_info xgmac0_0_regs[] = {
+ { "XGM_TX_CTRL", 0x800, 0 },
+ { "SendPause", 2, 1 },
+ { "SendZeroPause", 1, 1 },
+ { "TxEn", 0, 1 },
+ { "XGM_TX_CFG", 0x804, 0 },
+ { "CfgClkSpeed", 2, 3 },
+ { "StretchMode", 1, 1 },
+ { "TxPauseEn", 0, 1 },
+ { "XGM_TX_PAUSE_QUANTA", 0x808, 0 },
+ { "TxPauseQuanta", 0, 16 },
+ { "XGM_RX_CTRL", 0x80c, 0 },
+ { "RxEn", 0, 1 },
+ { "XGM_RX_CFG", 0x810, 0 },
+ { "Con802_3Preamble", 12, 1 },
+ { "EnNon802_3Preamble", 11, 1 },
+ { "CopyPreamble", 10, 1 },
+ { "DisPauseFrames", 9, 1 },
+ { "En1536BFrames", 8, 1 },
+ { "EnJumbo", 7, 1 },
+ { "RmFCS", 6, 1 },
+ { "DisNonVlan", 5, 1 },
+ { "EnExtMatch", 4, 1 },
+ { "EnHashUcast", 3, 1 },
+ { "EnHashMcast", 2, 1 },
+ { "DisBCast", 1, 1 },
+ { "CopyAllFrames", 0, 1 },
+ { "XGM_RX_HASH_LOW", 0x814, 0 },
+ { "XGM_RX_HASH_HIGH", 0x818, 0 },
+ { "XGM_RX_EXACT_MATCH_LOW_1", 0x81c, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_1", 0x820, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_2", 0x824, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_2", 0x828, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_3", 0x82c, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_3", 0x830, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_4", 0x834, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_4", 0x838, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_5", 0x83c, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_5", 0x840, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_6", 0x844, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_6", 0x848, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_7", 0x84c, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_7", 0x850, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_8", 0x854, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_8", 0x858, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_TYPE_MATCH_1", 0x85c, 0 },
+ { "EnTypeMatch", 31, 1 },
+ { "type", 0, 16 },
+ { "XGM_RX_TYPE_MATCH_2", 0x860, 0 },
+ { "EnTypeMatch", 31, 1 },
+ { "type", 0, 16 },
+ { "XGM_RX_TYPE_MATCH_3", 0x864, 0 },
+ { "EnTypeMatch", 31, 1 },
+ { "type", 0, 16 },
+ { "XGM_RX_TYPE_MATCH_4", 0x868, 0 },
+ { "EnTypeMatch", 31, 1 },
+ { "type", 0, 16 },
+ { "XGM_INT_STATUS", 0x86c, 0 },
+ { "XGMIIExtInt", 10, 1 },
+ { "LinkFaultChange", 9, 1 },
+ { "PhyFrameComplete", 8, 1 },
+ { "PauseFrameTxmt", 7, 1 },
+ { "PauseCntrTimeOut", 6, 1 },
+ { "Non0PauseRcvd", 5, 1 },
+ { "StatOFlow", 4, 1 },
+ { "TxErrFIFO", 3, 1 },
+ { "TxUFlow", 2, 1 },
+ { "FrameTxmt", 1, 1 },
+ { "FrameRcvd", 0, 1 },
+ { "XGM_XGM_INT_MASK", 0x870, 0 },
+ { "XGMIIExtInt", 10, 1 },
+ { "LinkFaultChange", 9, 1 },
+ { "PhyFrameComplete", 8, 1 },
+ { "PauseFrameTxmt", 7, 1 },
+ { "PauseCntrTimeOut", 6, 1 },
+ { "Non0PauseRcvd", 5, 1 },
+ { "StatOFlow", 4, 1 },
+ { "TxErrFIFO", 3, 1 },
+ { "TxUFlow", 2, 1 },
+ { "FrameTxmt", 1, 1 },
+ { "FrameRcvd", 0, 1 },
+ { "XGM_XGM_INT_ENABLE", 0x874, 0 },
+ { "XGMIIExtInt", 10, 1 },
+ { "LinkFaultChange", 9, 1 },
+ { "PhyFrameComplete", 8, 1 },
+ { "PauseFrameTxmt", 7, 1 },
+ { "PauseCntrTimeOut", 6, 1 },
+ { "Non0PauseRcvd", 5, 1 },
+ { "StatOFlow", 4, 1 },
+ { "TxErrFIFO", 3, 1 },
+ { "TxUFlow", 2, 1 },
+ { "FrameTxmt", 1, 1 },
+ { "FrameRcvd", 0, 1 },
+ { "XGM_XGM_INT_DISABLE", 0x878, 0 },
+ { "XGMIIExtInt", 10, 1 },
+ { "LinkFaultChange", 9, 1 },
+ { "PhyFrameComplete", 8, 1 },
+ { "PauseFrameTxmt", 7, 1 },
+ { "PauseCntrTimeOut", 6, 1 },
+ { "Non0PauseRcvd", 5, 1 },
+ { "StatOFlow", 4, 1 },
+ { "TxErrFIFO", 3, 1 },
+ { "TxUFlow", 2, 1 },
+ { "FrameTxmt", 1, 1 },
+ { "FrameRcvd", 0, 1 },
+ { "XGM_TX_PAUSE_TIMER", 0x87c, 0 },
+ { "CurPauseTimer", 0, 16 },
+ { "XGM_STAT_CTRL", 0x880, 0 },
+ { "ReadSnpShot", 4, 1 },
+ { "TakeSnpShot", 3, 1 },
+ { "ClrStats", 2, 1 },
+ { "IncrStats", 1, 1 },
+ { "EnTestModeWr", 0, 1 },
+ { "XGM_RXFIFO_CFG", 0x884, 0 },
+ { "RxFIFOPauseHWM", 17, 12 },
+ { "RxFIFOPauseLWM", 5, 12 },
+ { "ForcedPause", 4, 1 },
+ { "ExternLoopback", 3, 1 },
+ { "RxByteSwap", 2, 1 },
+ { "RxStrFrwrd", 1, 1 },
+ { "DisErrFrames", 0, 1 },
+ { "XGM_TXFIFO_CFG", 0x888, 0 },
+ { "TxIPG", 13, 8 },
+ { "TxFIFOThresh", 4, 9 },
+ { "InternLoopback", 3, 1 },
+ { "TxByteSwap", 2, 1 },
+ { "DisCRC", 1, 1 },
+ { "DisPreAmble", 0, 1 },
+ { "XGM_SLOW_TIMER", 0x88c, 0 },
+ { "PauseSlowTimerEn", 31, 1 },
+ { "PauseSlowTimer", 0, 20 },
+ { "XGM_SERDES_CTRL", 0x890, 0 },
+ { "SERDESEn", 25, 1 },
+ { "SERDESReset_", 24, 1 },
+ { "CMURange", 21, 3 },
+ { "BGEnb", 20, 1 },
+ { "EnSkpDrop", 19, 1 },
+ { "EnComma", 18, 1 },
+ { "En8B10B", 17, 1 },
+ { "EnElBuf", 16, 1 },
+ { "Gain", 11, 5 },
+ { "BandGap", 7, 4 },
+ { "LpbkEn", 5, 2 },
+ { "RxEn", 4, 1 },
+ { "TxEn", 3, 1 },
+ { "RxComAdj", 2, 1 },
+ { "PreEmph", 0, 2 },
+ { "XGM_XAUI_PCS_TEST", 0x894, 0 },
+ { "TestPattern", 1, 2 },
+ { "EnTest", 0, 1 },
+ { "XGM_RGMII_CTRL", 0x898, 0 },
+ { "PhAlignFIFOThresh", 1, 2 },
+ { "TxClk90Shift", 0, 1 },
+ { "XGM_RGMII_IMP", 0x89c, 0 },
+ { "ImpSetUpdate", 6, 1 },
+ { "RGMIIImpPD", 3, 3 },
+ { "RGMIIImpPU", 0, 3 },
+ { "XGM_XAUI_IMP", 0x8a0, 0 },
+ { "CalBusy", 31, 1 },
+ { "CalFault", 29, 1 },
+ { "CalImp", 24, 5 },
+ { "XAUIImp", 0, 3 },
+ { "XGM_SERDES_BIST", 0x8a4, 0 },
+ { "BISTDone", 28, 4 },
+ { "BISTCycleThresh", 3, 17 },
+ { "BISTMode", 0, 3 },
+ { "XGM_RX_MAX_PKT_SIZE", 0x8a8, 0 },
+ { "RxMaxPktSize", 0, 14 },
+ { "XGM_RESET_CTRL", 0x8ac, 0 },
+ { "XG2G_Reset_", 3, 1 },
+ { "RGMII_Reset_", 2, 1 },
+ { "PCS_Reset_", 1, 1 },
+ { "MAC_Reset_", 0, 1 },
+ { "XGM_XAUI1G_CTRL", 0x8b0, 0 },
+ { "XAUI1GLinkId", 0, 2 },
+ { "XGM_SERDES_LANE_CTRL", 0x8b4, 0 },
+ { "LaneReversal", 8, 1 },
+ { "TxPolarity", 4, 4 },
+ { "RxPolarity", 0, 4 },
+ { "XGM_PORT_CFG", 0x8b8, 0 },
+ { "SafeSpeedChange", 4, 1 },
+ { "ClkDivReset_", 3, 1 },
+ { "PortSpeed", 1, 2 },
+ { "EnRGMII", 0, 1 },
+ { "XGM_EPIO_DATA0", 0x8c0, 0 },
+ { "XGM_EPIO_DATA1", 0x8c4, 0 },
+ { "XGM_EPIO_DATA2", 0x8c8, 0 },
+ { "XGM_EPIO_DATA3", 0x8cc, 0 },
+ { "XGM_EPIO_OP", 0x8d0, 0 },
+ { "PIO_Ready", 31, 1 },
+ { "PIO_WrRd", 24, 1 },
+ { "PIO_Address", 0, 8 },
+ { "XGM_INT_ENABLE", 0x8d4, 0 },
+ { "SERDESCMULock_loss", 24, 1 },
+ { "RGMIIRxFIFOOverflow", 23, 1 },
+ { "RGMIIRxFIFOUnderflow", 22, 1 },
+ { "RxPktSizeError", 21, 1 },
+ { "WOLPatDetected", 20, 1 },
+ { "TXFIFO_prty_err", 17, 3 },
+ { "RXFIFO_prty_err", 14, 3 },
+ { "TXFIFO_underrun", 13, 1 },
+ { "RXFIFO_overflow", 12, 1 },
+ { "SERDESBIST_err", 8, 4 },
+ { "SERDES_los", 4, 4 },
+ { "XAUIPCSCTCErr", 3, 1 },
+ { "XAUIPCSAlignChange", 2, 1 },
+ { "RGMIILinkStsChange", 1, 1 },
+ { "xgm_int", 0, 1 },
+ { "XGM_INT_CAUSE", 0x8d8, 0 },
+ { "SERDESCMULock_loss", 24, 1 },
+ { "RGMIIRxFIFOOverflow", 23, 1 },
+ { "RGMIIRxFIFOUnderflow", 22, 1 },
+ { "RxPktSizeError", 21, 1 },
+ { "WOLPatDetected", 20, 1 },
+ { "TXFIFO_prty_err", 17, 3 },
+ { "RXFIFO_prty_err", 14, 3 },
+ { "TXFIFO_underrun", 13, 1 },
+ { "RXFIFO_overflow", 12, 1 },
+ { "SERDESBIST_err", 8, 4 },
+ { "SERDES_los", 4, 4 },
+ { "XAUIPCSCTCErr", 3, 1 },
+ { "XAUIPCSAlignChange", 2, 1 },
+ { "RGMIILinkStsChange", 1, 1 },
+ { "xgm_int", 0, 1 },
+ { "XGM_STAT_TX_BYTE_LOW", 0x900, 0 },
+ { "XGM_STAT_TX_BYTE_HIGH", 0x904, 0 },
+ { "TxBytes_high", 0, 13 },
+ { "XGM_STAT_TX_FRAME_LOW", 0x908, 0 },
+ { "XGM_STAT_TX_FRAME_HIGH", 0x90c, 0 },
+ { "TxFrames_high", 0, 4 },
+ { "XGM_STAT_TX_BCAST", 0x910, 0 },
+ { "XGM_STAT_TX_MCAST", 0x914, 0 },
+ { "XGM_STAT_TX_PAUSE", 0x918, 0 },
+ { "XGM_STAT_TX_64B_FRAMES", 0x91c, 0 },
+ { "XGM_STAT_TX_65_127B_FRAMES", 0x920, 0 },
+ { "XGM_STAT_TX_128_255B_FRAMES", 0x924, 0 },
+ { "XGM_STAT_TX_256_511B_FRAMES", 0x928, 0 },
+ { "XGM_STAT_TX_512_1023B_FRAMES", 0x92c, 0 },
+ { "XGM_STAT_TX_1024_1518B_FRAMES", 0x930, 0 },
+ { "XGM_STAT_TX_1519_MAXB_FRAMES", 0x934, 0 },
+ { "XGM_STAT_TX_ERR_FRAMES", 0x938, 0 },
+ { "XGM_STAT_RX_BYTES_LOW", 0x93c, 0 },
+ { "XGM_STAT_RX_BYTES_HIGH", 0x940, 0 },
+ { "RxBytes_high", 0, 13 },
+ { "XGM_STAT_RX_FRAMES_LOW", 0x944, 0 },
+ { "XGM_STAT_RX_FRAMES_HIGH", 0x948, 0 },
+ { "RxFrames_high", 0, 4 },
+ { "XGM_STAT_RX_BCAST_FRAMES", 0x94c, 0 },
+ { "XGM_STAT_RX_MCAST_FRAMES", 0x950, 0 },
+ { "XGM_STAT_RX_PAUSE_FRAMES", 0x954, 0 },
+ { "RxPauseFrames", 0, 16 },
+ { "XGM_STAT_RX_64B_FRAMES", 0x958, 0 },
+ { "XGM_STAT_RX_65_127B_FRAMES", 0x95c, 0 },
+ { "XGM_STAT_RX_128_255B_FRAMES", 0x960, 0 },
+ { "XGM_STAT_RX_256_511B_FRAMES", 0x964, 0 },
+ { "XGM_STAT_RX_512_1023B_FRAMES", 0x968, 0 },
+ { "XGM_STAT_RX_1024_1518B_FRAMES", 0x96c, 0 },
+ { "XGM_STAT_RX_1519_MAXB_FRAMES", 0x970, 0 },
+ { "XGM_STAT_RX_SHORT_FRAMES", 0x974, 0 },
+ { "RxShortFrames", 0, 16 },
+ { "XGM_STAT_RX_OVERSIZE_FRAMES", 0x978, 0 },
+ { "RxOversizeFrames", 0, 16 },
+ { "XGM_STAT_RX_JABBER_FRAMES", 0x97c, 0 },
+ { "RxJabberFrames", 0, 16 },
+ { "XGM_STAT_RX_CRC_ERR_FRAMES", 0x980, 0 },
+ { "RxCRCErrFrames", 0, 16 },
+ { "XGM_STAT_RX_LENGTH_ERR_FRAMES", 0x984, 0 },
+ { "RxLengthErrFrames", 0, 16 },
+ { "XGM_STAT_RX_SYM_CODE_ERR_FRAMES", 0x988, 0 },
+ { "RxSymCodeErrFrames", 0, 16 },
+ { "XGM_SERDES_STATUS0", 0x98c, 0 },
+ { "RxErrLane3", 9, 3 },
+ { "RxErrLane2", 6, 3 },
+ { "RxErrLane1", 3, 3 },
+ { "RxErrLane0", 0, 3 },
+ { "XGM_SERDES_STATUS1", 0x990, 0 },
+ { "CMULock", 31, 1 },
+ { "RxKLockLane3", 11, 1 },
+ { "RxKLockLane2", 10, 1 },
+ { "RxKLockLane1", 9, 1 },
+ { "RxKLockLane0", 8, 1 },
+ { "RxUFlowLane3", 7, 1 },
+ { "RxUFlowLane2", 6, 1 },
+ { "RxUFlowLane1", 5, 1 },
+ { "RxUFlowLane0", 4, 1 },
+ { "RxOFlowLane3", 3, 1 },
+ { "RxOFlowLane2", 2, 1 },
+ { "RxOFlowLane1", 1, 1 },
+ { "RxOFlowLane0", 0, 1 },
+ { "XGM_SERDES_STATUS2", 0x994, 0 },
+ { "RxEIDLane3", 11, 1 },
+ { "RxEIDLane2", 10, 1 },
+ { "RxEIDLane1", 9, 1 },
+ { "RxEIDLane0", 8, 1 },
+ { "RxRemSkipLane3", 7, 1 },
+ { "RxRemSkipLane2", 6, 1 },
+ { "RxRemSkipLane1", 5, 1 },
+ { "RxRemSkipLane0", 4, 1 },
+ { "RxAddSkipLane3", 3, 1 },
+ { "RxAddSkipLane2", 2, 1 },
+ { "RxAddSkipLane1", 1, 1 },
+ { "RxAddSkipLane0", 0, 1 },
+ { "XGM_XAUI_PCS_ERR", 0x998, 0 },
+ { "PCS_SyncStatus", 5, 4 },
+ { "PCS_CTCFIFOErr", 1, 4 },
+ { "PCS_NotAligned", 0, 1 },
+ { "XGM_RGMII_STATUS", 0x99c, 0 },
+ { "GMIIDuplex", 3, 1 },
+ { "GMIISpeed", 1, 2 },
+ { "GMIILinkStatus", 0, 1 },
+ { "XGM_WOL_STATUS", 0x9a0, 0 },
+ { "PatDetected", 31, 1 },
+ { "MatchedFilter", 0, 3 },
+ { "XGM_RX_MAX_PKT_SIZE_ERR_CNT", 0x9a4, 0 },
+ { "XGM_TX_SPI4_SOP_EOP_CNT", 0x9a8, 0 },
+ { "TxSPI4SopCnt", 16, 16 },
+ { "TxSPI4EopCnt", 0, 16 },
+ { "XGM_RX_SPI4_SOP_EOP_CNT", 0x9ac, 0 },
+ { "RxSPI4SopCnt", 16, 16 },
+ { "RxSPI4EopCnt", 0, 16 },
+ { NULL }
+};
+
+struct reg_info xgmac0_1_regs[] = {
+ { "XGM_TX_CTRL", 0xa00, 0 },
+ { "SendPause", 2, 1 },
+ { "SendZeroPause", 1, 1 },
+ { "TxEn", 0, 1 },
+ { "XGM_TX_CFG", 0xa04, 0 },
+ { "CfgClkSpeed", 2, 3 },
+ { "StretchMode", 1, 1 },
+ { "TxPauseEn", 0, 1 },
+ { "XGM_TX_PAUSE_QUANTA", 0xa08, 0 },
+ { "TxPauseQuanta", 0, 16 },
+ { "XGM_RX_CTRL", 0xa0c, 0 },
+ { "RxEn", 0, 1 },
+ { "XGM_RX_CFG", 0xa10, 0 },
+ { "Con802_3Preamble", 12, 1 },
+ { "EnNon802_3Preamble", 11, 1 },
+ { "CopyPreamble", 10, 1 },
+ { "DisPauseFrames", 9, 1 },
+ { "En1536BFrames", 8, 1 },
+ { "EnJumbo", 7, 1 },
+ { "RmFCS", 6, 1 },
+ { "DisNonVlan", 5, 1 },
+ { "EnExtMatch", 4, 1 },
+ { "EnHashUcast", 3, 1 },
+ { "EnHashMcast", 2, 1 },
+ { "DisBCast", 1, 1 },
+ { "CopyAllFrames", 0, 1 },
+ { "XGM_RX_HASH_LOW", 0xa14, 0 },
+ { "XGM_RX_HASH_HIGH", 0xa18, 0 },
+ { "XGM_RX_EXACT_MATCH_LOW_1", 0xa1c, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_1", 0xa20, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_2", 0xa24, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_2", 0xa28, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_3", 0xa2c, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_3", 0xa30, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_4", 0xa34, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_4", 0xa38, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_5", 0xa3c, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_5", 0xa40, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_6", 0xa44, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_6", 0xa48, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_7", 0xa4c, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_7", 0xa50, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_8", 0xa54, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_8", 0xa58, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_TYPE_MATCH_1", 0xa5c, 0 },
+ { "EnTypeMatch", 31, 1 },
+ { "type", 0, 16 },
+ { "XGM_RX_TYPE_MATCH_2", 0xa60, 0 },
+ { "EnTypeMatch", 31, 1 },
+ { "type", 0, 16 },
+ { "XGM_RX_TYPE_MATCH_3", 0xa64, 0 },
+ { "EnTypeMatch", 31, 1 },
+ { "type", 0, 16 },
+ { "XGM_RX_TYPE_MATCH_4", 0xa68, 0 },
+ { "EnTypeMatch", 31, 1 },
+ { "type", 0, 16 },
+ { "XGM_INT_STATUS", 0xa6c, 0 },
+ { "XGMIIExtInt", 10, 1 },
+ { "LinkFaultChange", 9, 1 },
+ { "PhyFrameComplete", 8, 1 },
+ { "PauseFrameTxmt", 7, 1 },
+ { "PauseCntrTimeOut", 6, 1 },
+ { "Non0PauseRcvd", 5, 1 },
+ { "StatOFlow", 4, 1 },
+ { "TxErrFIFO", 3, 1 },
+ { "TxUFlow", 2, 1 },
+ { "FrameTxmt", 1, 1 },
+ { "FrameRcvd", 0, 1 },
+ { "XGM_XGM_INT_MASK", 0xa70, 0 },
+ { "XGMIIExtInt", 10, 1 },
+ { "LinkFaultChange", 9, 1 },
+ { "PhyFrameComplete", 8, 1 },
+ { "PauseFrameTxmt", 7, 1 },
+ { "PauseCntrTimeOut", 6, 1 },
+ { "Non0PauseRcvd", 5, 1 },
+ { "StatOFlow", 4, 1 },
+ { "TxErrFIFO", 3, 1 },
+ { "TxUFlow", 2, 1 },
+ { "FrameTxmt", 1, 1 },
+ { "FrameRcvd", 0, 1 },
+ { "XGM_XGM_INT_ENABLE", 0xa74, 0 },
+ { "XGMIIExtInt", 10, 1 },
+ { "LinkFaultChange", 9, 1 },
+ { "PhyFrameComplete", 8, 1 },
+ { "PauseFrameTxmt", 7, 1 },
+ { "PauseCntrTimeOut", 6, 1 },
+ { "Non0PauseRcvd", 5, 1 },
+ { "StatOFlow", 4, 1 },
+ { "TxErrFIFO", 3, 1 },
+ { "TxUFlow", 2, 1 },
+ { "FrameTxmt", 1, 1 },
+ { "FrameRcvd", 0, 1 },
+ { "XGM_XGM_INT_DISABLE", 0xa78, 0 },
+ { "XGMIIExtInt", 10, 1 },
+ { "LinkFaultChange", 9, 1 },
+ { "PhyFrameComplete", 8, 1 },
+ { "PauseFrameTxmt", 7, 1 },
+ { "PauseCntrTimeOut", 6, 1 },
+ { "Non0PauseRcvd", 5, 1 },
+ { "StatOFlow", 4, 1 },
+ { "TxErrFIFO", 3, 1 },
+ { "TxUFlow", 2, 1 },
+ { "FrameTxmt", 1, 1 },
+ { "FrameRcvd", 0, 1 },
+ { "XGM_TX_PAUSE_TIMER", 0xa7c, 0 },
+ { "CurPauseTimer", 0, 16 },
+ { "XGM_STAT_CTRL", 0xa80, 0 },
+ { "ReadSnpShot", 4, 1 },
+ { "TakeSnpShot", 3, 1 },
+ { "ClrStats", 2, 1 },
+ { "IncrStats", 1, 1 },
+ { "EnTestModeWr", 0, 1 },
+ { "XGM_RXFIFO_CFG", 0xa84, 0 },
+ { "RxFIFOPauseHWM", 17, 12 },
+ { "RxFIFOPauseLWM", 5, 12 },
+ { "ForcedPause", 4, 1 },
+ { "ExternLoopback", 3, 1 },
+ { "RxByteSwap", 2, 1 },
+ { "RxStrFrwrd", 1, 1 },
+ { "DisErrFrames", 0, 1 },
+ { "XGM_TXFIFO_CFG", 0xa88, 0 },
+ { "TxIPG", 13, 8 },
+ { "TxFIFOThresh", 4, 9 },
+ { "InternLoopback", 3, 1 },
+ { "TxByteSwap", 2, 1 },
+ { "DisCRC", 1, 1 },
+ { "DisPreAmble", 0, 1 },
+ { "XGM_SLOW_TIMER", 0xa8c, 0 },
+ { "PauseSlowTimerEn", 31, 1 },
+ { "PauseSlowTimer", 0, 20 },
+ { "XGM_SERDES_CTRL", 0xa90, 0 },
+ { "SERDESEn", 25, 1 },
+ { "SERDESReset_", 24, 1 },
+ { "CMURange", 21, 3 },
+ { "BGEnb", 20, 1 },
+ { "EnSkpDrop", 19, 1 },
+ { "EnComma", 18, 1 },
+ { "En8B10B", 17, 1 },
+ { "EnElBuf", 16, 1 },
+ { "Gain", 11, 5 },
+ { "BandGap", 7, 4 },
+ { "LpbkEn", 5, 2 },
+ { "RxEn", 4, 1 },
+ { "TxEn", 3, 1 },
+ { "RxComAdj", 2, 1 },
+ { "PreEmph", 0, 2 },
+ { "XGM_XAUI_PCS_TEST", 0xa94, 0 },
+ { "TestPattern", 1, 2 },
+ { "EnTest", 0, 1 },
+ { "XGM_RGMII_CTRL", 0xa98, 0 },
+ { "PhAlignFIFOThresh", 1, 2 },
+ { "TxClk90Shift", 0, 1 },
+ { "XGM_RGMII_IMP", 0xa9c, 0 },
+ { "ImpSetUpdate", 6, 1 },
+ { "RGMIIImpPD", 3, 3 },
+ { "RGMIIImpPU", 0, 3 },
+ { "XGM_XAUI_IMP", 0xaa0, 0 },
+ { "CalBusy", 31, 1 },
+ { "CalFault", 29, 1 },
+ { "CalImp", 24, 5 },
+ { "XAUIImp", 0, 3 },
+ { "XGM_SERDES_BIST", 0xaa4, 0 },
+ { "BISTDone", 28, 4 },
+ { "BISTCycleThresh", 3, 17 },
+ { "BISTMode", 0, 3 },
+ { "XGM_RX_MAX_PKT_SIZE", 0xaa8, 0 },
+ { "RxMaxPktSize", 0, 14 },
+ { "XGM_RESET_CTRL", 0xaac, 0 },
+ { "XG2G_Reset_", 3, 1 },
+ { "RGMII_Reset_", 2, 1 },
+ { "PCS_Reset_", 1, 1 },
+ { "MAC_Reset_", 0, 1 },
+ { "XGM_XAUI1G_CTRL", 0xab0, 0 },
+ { "XAUI1GLinkId", 0, 2 },
+ { "XGM_SERDES_LANE_CTRL", 0xab4, 0 },
+ { "LaneReversal", 8, 1 },
+ { "TxPolarity", 4, 4 },
+ { "RxPolarity", 0, 4 },
+ { "XGM_PORT_CFG", 0xab8, 0 },
+ { "SafeSpeedChange", 4, 1 },
+ { "ClkDivReset_", 3, 1 },
+ { "PortSpeed", 1, 2 },
+ { "EnRGMII", 0, 1 },
+ { "XGM_EPIO_DATA0", 0xac0, 0 },
+ { "XGM_EPIO_DATA1", 0xac4, 0 },
+ { "XGM_EPIO_DATA2", 0xac8, 0 },
+ { "XGM_EPIO_DATA3", 0xacc, 0 },
+ { "XGM_EPIO_OP", 0xad0, 0 },
+ { "PIO_Ready", 31, 1 },
+ { "PIO_WrRd", 24, 1 },
+ { "PIO_Address", 0, 8 },
+ { "XGM_INT_ENABLE", 0xad4, 0 },
+ { "SERDESCMULock_loss", 24, 1 },
+ { "RGMIIRxFIFOOverflow", 23, 1 },
+ { "RGMIIRxFIFOUnderflow", 22, 1 },
+ { "RxPktSizeError", 21, 1 },
+ { "WOLPatDetected", 20, 1 },
+ { "TXFIFO_prty_err", 17, 3 },
+ { "RXFIFO_prty_err", 14, 3 },
+ { "TXFIFO_underrun", 13, 1 },
+ { "RXFIFO_overflow", 12, 1 },
+ { "SERDESBIST_err", 8, 4 },
+ { "SERDES_los", 4, 4 },
+ { "XAUIPCSCTCErr", 3, 1 },
+ { "XAUIPCSAlignChange", 2, 1 },
+ { "RGMIILinkStsChange", 1, 1 },
+ { "xgm_int", 0, 1 },
+ { "XGM_INT_CAUSE", 0xad8, 0 },
+ { "SERDESCMULock_loss", 24, 1 },
+ { "RGMIIRxFIFOOverflow", 23, 1 },
+ { "RGMIIRxFIFOUnderflow", 22, 1 },
+ { "RxPktSizeError", 21, 1 },
+ { "WOLPatDetected", 20, 1 },
+ { "TXFIFO_prty_err", 17, 3 },
+ { "RXFIFO_prty_err", 14, 3 },
+ { "TXFIFO_underrun", 13, 1 },
+ { "RXFIFO_overflow", 12, 1 },
+ { "SERDESBIST_err", 8, 4 },
+ { "SERDES_los", 4, 4 },
+ { "XAUIPCSCTCErr", 3, 1 },
+ { "XAUIPCSAlignChange", 2, 1 },
+ { "RGMIILinkStsChange", 1, 1 },
+ { "xgm_int", 0, 1 },
+ { "XGM_STAT_TX_BYTE_LOW", 0xb00, 0 },
+ { "XGM_STAT_TX_BYTE_HIGH", 0xb04, 0 },
+ { "TxBytes_high", 0, 13 },
+ { "XGM_STAT_TX_FRAME_LOW", 0xb08, 0 },
+ { "XGM_STAT_TX_FRAME_HIGH", 0xb0c, 0 },
+ { "TxFrames_high", 0, 4 },
+ { "XGM_STAT_TX_BCAST", 0xb10, 0 },
+ { "XGM_STAT_TX_MCAST", 0xb14, 0 },
+ { "XGM_STAT_TX_PAUSE", 0xb18, 0 },
+ { "XGM_STAT_TX_64B_FRAMES", 0xb1c, 0 },
+ { "XGM_STAT_TX_65_127B_FRAMES", 0xb20, 0 },
+ { "XGM_STAT_TX_128_255B_FRAMES", 0xb24, 0 },
+ { "XGM_STAT_TX_256_511B_FRAMES", 0xb28, 0 },
+ { "XGM_STAT_TX_512_1023B_FRAMES", 0xb2c, 0 },
+ { "XGM_STAT_TX_1024_1518B_FRAMES", 0xb30, 0 },
+ { "XGM_STAT_TX_1519_MAXB_FRAMES", 0xb34, 0 },
+ { "XGM_STAT_TX_ERR_FRAMES", 0xb38, 0 },
+ { "XGM_STAT_RX_BYTES_LOW", 0xb3c, 0 },
+ { "XGM_STAT_RX_BYTES_HIGH", 0xb40, 0 },
+ { "RxBytes_high", 0, 13 },
+ { "XGM_STAT_RX_FRAMES_LOW", 0xb44, 0 },
+ { "XGM_STAT_RX_FRAMES_HIGH", 0xb48, 0 },
+ { "RxFrames_high", 0, 4 },
+ { "XGM_STAT_RX_BCAST_FRAMES", 0xb4c, 0 },
+ { "XGM_STAT_RX_MCAST_FRAMES", 0xb50, 0 },
+ { "XGM_STAT_RX_PAUSE_FRAMES", 0xb54, 0 },
+ { "RxPauseFrames", 0, 16 },
+ { "XGM_STAT_RX_64B_FRAMES", 0xb58, 0 },
+ { "XGM_STAT_RX_65_127B_FRAMES", 0xb5c, 0 },
+ { "XGM_STAT_RX_128_255B_FRAMES", 0xb60, 0 },
+ { "XGM_STAT_RX_256_511B_FRAMES", 0xb64, 0 },
+ { "XGM_STAT_RX_512_1023B_FRAMES", 0xb68, 0 },
+ { "XGM_STAT_RX_1024_1518B_FRAMES", 0xb6c, 0 },
+ { "XGM_STAT_RX_1519_MAXB_FRAMES", 0xb70, 0 },
+ { "XGM_STAT_RX_SHORT_FRAMES", 0xb74, 0 },
+ { "RxShortFrames", 0, 16 },
+ { "XGM_STAT_RX_OVERSIZE_FRAMES", 0xb78, 0 },
+ { "RxOversizeFrames", 0, 16 },
+ { "XGM_STAT_RX_JABBER_FRAMES", 0xb7c, 0 },
+ { "RxJabberFrames", 0, 16 },
+ { "XGM_STAT_RX_CRC_ERR_FRAMES", 0xb80, 0 },
+ { "RxCRCErrFrames", 0, 16 },
+ { "XGM_STAT_RX_LENGTH_ERR_FRAMES", 0xb84, 0 },
+ { "RxLengthErrFrames", 0, 16 },
+ { "XGM_STAT_RX_SYM_CODE_ERR_FRAMES", 0xb88, 0 },
+ { "RxSymCodeErrFrames", 0, 16 },
+ { "XGM_SERDES_STATUS0", 0xb8c, 0 },
+ { "RxErrLane3", 9, 3 },
+ { "RxErrLane2", 6, 3 },
+ { "RxErrLane1", 3, 3 },
+ { "RxErrLane0", 0, 3 },
+ { "XGM_SERDES_STATUS1", 0xb90, 0 },
+ { "CMULock", 31, 1 },
+ { "RxKLockLane3", 11, 1 },
+ { "RxKLockLane2", 10, 1 },
+ { "RxKLockLane1", 9, 1 },
+ { "RxKLockLane0", 8, 1 },
+ { "RxUFlowLane3", 7, 1 },
+ { "RxUFlowLane2", 6, 1 },
+ { "RxUFlowLane1", 5, 1 },
+ { "RxUFlowLane0", 4, 1 },
+ { "RxOFlowLane3", 3, 1 },
+ { "RxOFlowLane2", 2, 1 },
+ { "RxOFlowLane1", 1, 1 },
+ { "RxOFlowLane0", 0, 1 },
+ { "XGM_SERDES_STATUS2", 0xb94, 0 },
+ { "RxEIDLane3", 11, 1 },
+ { "RxEIDLane2", 10, 1 },
+ { "RxEIDLane1", 9, 1 },
+ { "RxEIDLane0", 8, 1 },
+ { "RxRemSkipLane3", 7, 1 },
+ { "RxRemSkipLane2", 6, 1 },
+ { "RxRemSkipLane1", 5, 1 },
+ { "RxRemSkipLane0", 4, 1 },
+ { "RxAddSkipLane3", 3, 1 },
+ { "RxAddSkipLane2", 2, 1 },
+ { "RxAddSkipLane1", 1, 1 },
+ { "RxAddSkipLane0", 0, 1 },
+ { "XGM_XAUI_PCS_ERR", 0xb98, 0 },
+ { "PCS_SyncStatus", 5, 4 },
+ { "PCS_CTCFIFOErr", 1, 4 },
+ { "PCS_NotAligned", 0, 1 },
+ { "XGM_RGMII_STATUS", 0xb9c, 0 },
+ { "GMIIDuplex", 3, 1 },
+ { "GMIISpeed", 1, 2 },
+ { "GMIILinkStatus", 0, 1 },
+ { "XGM_WOL_STATUS", 0xba0, 0 },
+ { "PatDetected", 31, 1 },
+ { "MatchedFilter", 0, 3 },
+ { "XGM_RX_MAX_PKT_SIZE_ERR_CNT", 0xba4, 0 },
+ { "XGM_TX_SPI4_SOP_EOP_CNT", 0xba8, 0 },
+ { "TxSPI4SopCnt", 16, 16 },
+ { "TxSPI4EopCnt", 0, 16 },
+ { "XGM_RX_SPI4_SOP_EOP_CNT", 0xbac, 0 },
+ { "RxSPI4SopCnt", 16, 16 },
+ { "RxSPI4EopCnt", 0, 16 },
+ { NULL }
+};
diff --git a/usr.sbin/cxgbtool/reg_defs_t3b.c b/usr.sbin/cxgbtool/reg_defs_t3b.c
new file mode 100644
index 0000000..539742c
--- /dev/null
+++ b/usr.sbin/cxgbtool/reg_defs_t3b.c
@@ -0,0 +1,2832 @@
+/*
+ * $FreeBSD$
+ */
+
+/* This file is automatically generated --- do not edit */
+
+struct reg_info t3b_sge3_regs[] = {
+ { "SG_CONTROL", 0x0, 0 },
+ { "UrgTnl", 26, 1 },
+ { "NewNotify", 25, 1 },
+ { "AvoidCqOvfl", 24, 1 },
+ { "OptOneIntMultQ", 23, 1 },
+ { "CQCrdtCtrl", 22, 1 },
+ { "EgrEnUpBp", 21, 1 },
+ { "DropPkt", 20, 1 },
+ { "EgrGenCtrl", 19, 1 },
+ { "UserSpaceSize", 14, 5 },
+ { "HostPageSize", 11, 3 },
+ { "PCIRelax", 10, 1 },
+ { "FLMode", 9, 1 },
+ { "PktShift", 6, 3 },
+ { "OneIntMultQ", 5, 1 },
+ { "FLPickAvail", 4, 1 },
+ { "BigEndianEgress", 3, 1 },
+ { "BigEndianIngress", 2, 1 },
+ { "IscsiCoalescing", 1, 1 },
+ { "GlobalEnable", 0, 1 },
+ { "SG_KDOORBELL", 0x4, 0 },
+ { "SelEgrCntx", 31, 1 },
+ { "EgrCntx", 0, 16 },
+ { "SG_GTS", 0x8, 0 },
+ { "RspQ", 29, 3 },
+ { "NewTimer", 16, 13 },
+ { "NewIndex", 0, 16 },
+ { "SG_CONTEXT_CMD", 0xc, 0 },
+ { "Opcode", 28, 4 },
+ { "Busy", 27, 1 },
+ { "CQ_credit", 20, 7 },
+ { "CQ", 19, 1 },
+ { "RspQ", 18, 1 },
+ { "Egress", 17, 1 },
+ { "FreeList", 16, 1 },
+ { "Context", 0, 16 },
+ { "SG_CONTEXT_DATA0", 0x10, 0 },
+ { "SG_CONTEXT_DATA1", 0x14, 0 },
+ { "SG_CONTEXT_DATA2", 0x18, 0 },
+ { "SG_CONTEXT_DATA3", 0x1c, 0 },
+ { "SG_CONTEXT_MASK0", 0x20, 0 },
+ { "SG_CONTEXT_MASK1", 0x24, 0 },
+ { "SG_CONTEXT_MASK2", 0x28, 0 },
+ { "SG_CONTEXT_MASK3", 0x2c, 0 },
+ { "SG_RSPQ_CREDIT_RETURN", 0x30, 0 },
+ { "RspQ", 29, 3 },
+ { "Data", 0, 16 },
+ { "SG_DATA_INTR", 0x34, 0 },
+ { "ErrIntr", 31, 1 },
+ { "DataIntr", 0, 8 },
+ { "SG_HI_DRB_HI_THRSH", 0x38, 0 },
+ { "HiDrbHiThrsh", 0, 10 },
+ { "SG_HI_DRB_LO_THRSH", 0x3c, 0 },
+ { "HiDrbLoThrsh", 0, 10 },
+ { "SG_LO_DRB_HI_THRSH", 0x40, 0 },
+ { "LoDrbHiThrsh", 0, 10 },
+ { "SG_LO_DRB_LO_THRSH", 0x44, 0 },
+ { "LoDrbLoThrsh", 0, 10 },
+ { "SG_ONE_INT_MULT_Q_COALESCING_TIMER", 0x48, 0 },
+ { "SG_RSPQ_FL_STATUS", 0x4c, 0 },
+ { "RspQ0Starved", 0, 1 },
+ { "RspQ1Starved", 1, 1 },
+ { "RspQ2Starved", 2, 1 },
+ { "RspQ3Starved", 3, 1 },
+ { "RspQ4Starved", 4, 1 },
+ { "RspQ5Starved", 5, 1 },
+ { "RspQ6Starved", 6, 1 },
+ { "RspQ7Starved", 7, 1 },
+ { "RspQ0Disabled", 8, 1 },
+ { "RspQ1Disabled", 9, 1 },
+ { "RspQ2Disabled", 10, 1 },
+ { "RspQ3Disabled", 11, 1 },
+ { "RspQ4Disabled", 12, 1 },
+ { "RspQ5Disabled", 13, 1 },
+ { "RspQ6Disabled", 14, 1 },
+ { "RspQ7Disabled", 15, 1 },
+ { "FL0Empty", 16, 1 },
+ { "FL1Empty", 17, 1 },
+ { "FL2Empty", 18, 1 },
+ { "FL3Empty", 19, 1 },
+ { "FL4Empty", 20, 1 },
+ { "FL5Empty", 21, 1 },
+ { "FL6Empty", 22, 1 },
+ { "FL7Empty", 23, 1 },
+ { "FL8Empty", 24, 1 },
+ { "FL9Empty", 25, 1 },
+ { "FL10Empty", 26, 1 },
+ { "FL11Empty", 27, 1 },
+ { "FL12Empty", 28, 1 },
+ { "FL13Empty", 29, 1 },
+ { "FL14Empty", 30, 1 },
+ { "FL15Empty", 31, 1 },
+ { "SG_EGR_PRI_CNT", 0x50, 0 },
+ { "EgrErrOpCode", 24, 8 },
+ { "EgrHiOpCode", 16, 8 },
+ { "EgrLoOpCode", 8, 8 },
+ { "EgrPriCnt", 0, 5 },
+ { "SG_EGR_RCQ_DRB_THRSH", 0x54, 0 },
+ { "HiRcqDrbThrsh", 16, 11 },
+ { "LoRcqDrbThrsh", 0, 11 },
+ { "SG_EGR_CNTX_BADDR", 0x58, 0 },
+ { "EgrCntxBAddr", 5, 27 },
+ { "SG_INT_CAUSE", 0x5c, 0 },
+ { "HiCtlDrbDropErr", 13, 1 },
+ { "LoCtlDrbDropErr", 12, 1 },
+ { "HiPioDrbDropErr", 11, 1 },
+ { "LoPioDrbDropErr", 10, 1 },
+ { "HiCrdtUndFlowErr", 9, 1 },
+ { "LoCrdtUndFlowErr", 8, 1 },
+ { "HiPriorityDBFull", 7, 1 },
+ { "HiPriorityDBEmpty", 6, 1 },
+ { "LoPriorityDBFull", 5, 1 },
+ { "LoPriorityDBEmpty", 4, 1 },
+ { "RspQDisabled", 3, 1 },
+ { "RspQCreditOverfow", 2, 1 },
+ { "FlEmpty", 1, 1 },
+ { "RspQStarve", 0, 1 },
+ { "SG_INT_ENABLE", 0x60, 0 },
+ { "HiCtlDrbDropErr", 13, 1 },
+ { "LoCtlDrbDropErr", 12, 1 },
+ { "HiPioDrbDropErr", 11, 1 },
+ { "LoPioDrbDropErr", 10, 1 },
+ { "HiCrdtUndFlowErr", 9, 1 },
+ { "LoCrdtUndFlowErr", 8, 1 },
+ { "HiPriorityDBFull", 7, 1 },
+ { "HiPriorityDBEmpty", 6, 1 },
+ { "LoPriorityDBFull", 5, 1 },
+ { "LoPriorityDBEmpty", 4, 1 },
+ { "RspQDisabled", 3, 1 },
+ { "RspQCreditOverfow", 2, 1 },
+ { "FlEmpty", 1, 1 },
+ { "RspQStarve", 0, 1 },
+ { "SG_CMDQ_CREDIT_TH", 0x64, 0 },
+ { "Timeout", 8, 24 },
+ { "Threshold", 0, 8 },
+ { "SG_TIMER_TICK", 0x68, 0 },
+ { "SG_CQ_CONTEXT_BADDR", 0x6c, 0 },
+ { "baseAddr", 5, 27 },
+ { "SG_OCO_BASE", 0x70, 0 },
+ { "Base1", 16, 16 },
+ { "Base0", 0, 16 },
+ { "SG_DRB_PRI_THRESH", 0x74, 0 },
+ { "DrbPriThrsh", 0, 16 },
+ { "SG_DEBUG_INDEX", 0x78, 0 },
+ { "SG_DEBUG_DATA", 0x7c, 0 },
+ { NULL }
+};
+
+struct reg_info t3b_pcix1_regs[] = {
+ { "PCIX_INT_ENABLE", 0x80, 0 },
+ { "MSIXParErr", 22, 3 },
+ { "CFParErr", 18, 4 },
+ { "RFParErr", 14, 4 },
+ { "WFParErr", 12, 2 },
+ { "PIOParErr", 11, 1 },
+ { "DetUncECCErr", 10, 1 },
+ { "DetCorECCErr", 9, 1 },
+ { "RcvSplCmpErr", 8, 1 },
+ { "UnxSplCmp", 7, 1 },
+ { "SplCmpDis", 6, 1 },
+ { "DetParErr", 5, 1 },
+ { "SigSysErr", 4, 1 },
+ { "RcvMstAbt", 3, 1 },
+ { "RcvTarAbt", 2, 1 },
+ { "SigTarAbt", 1, 1 },
+ { "MstDetParErr", 0, 1 },
+ { "PCIX_INT_CAUSE", 0x84, 0 },
+ { "MSIXParErr", 22, 3 },
+ { "CFParErr", 18, 4 },
+ { "RFParErr", 14, 4 },
+ { "WFParErr", 12, 2 },
+ { "PIOParErr", 11, 1 },
+ { "DetUncECCErr", 10, 1 },
+ { "DetCorECCErr", 9, 1 },
+ { "RcvSplCmpErr", 8, 1 },
+ { "UnxSplCmp", 7, 1 },
+ { "SplCmpDis", 6, 1 },
+ { "DetParErr", 5, 1 },
+ { "SigSysErr", 4, 1 },
+ { "RcvMstAbt", 3, 1 },
+ { "RcvTarAbt", 2, 1 },
+ { "SigTarAbt", 1, 1 },
+ { "MstDetParErr", 0, 1 },
+ { "PCIX_CFG", 0x88, 0 },
+ { "CLIDecEn", 18, 1 },
+ { "LatTmrDis", 17, 1 },
+ { "LowPwrEn", 16, 1 },
+ { "AsyncIntVec", 11, 5 },
+ { "MaxSplTrnC", 8, 3 },
+ { "MaxSplTrnR", 5, 3 },
+ { "MaxWrByteCnt", 3, 2 },
+ { "WrReqAtomicEn", 2, 1 },
+ { "CRstWrmMode", 1, 1 },
+ { "PIOAck64En", 0, 1 },
+ { "PCIX_MODE", 0x8c, 0 },
+ { "PClkRange", 6, 2 },
+ { "PCIXInitPat", 2, 4 },
+ { "66MHz", 1, 1 },
+ { "64Bit", 0, 1 },
+ { "PCIX_CAL", 0x90, 0 },
+ { "Busy", 31, 1 },
+ { "PerCalDiv", 22, 8 },
+ { "PerCalEn", 21, 1 },
+ { "SglCalEn", 20, 1 },
+ { "ZInUpdMode", 19, 1 },
+ { "ZInSel", 18, 1 },
+ { "ZPDMan", 15, 3 },
+ { "ZPUMan", 12, 3 },
+ { "ZPDOut", 9, 3 },
+ { "ZPUOut", 6, 3 },
+ { "ZPDIn", 3, 3 },
+ { "ZPUIn", 0, 3 },
+ { "PCIX_WOL", 0x94, 0 },
+ { "WakeUp1", 3, 1 },
+ { "WakeUp0", 2, 1 },
+ { "SleepMode1", 1, 1 },
+ { "SleepMode0", 0, 1 },
+ { NULL }
+};
+
+struct reg_info t3b_pcie0_regs[] = {
+ { "PCIE_INT_ENABLE", 0x80, 0 },
+ { "BISTErr", 15, 8 },
+ { "MSIXParErr", 12, 3 },
+ { "CFParErr", 11, 1 },
+ { "RFParErr", 10, 1 },
+ { "WFParErr", 9, 1 },
+ { "PIOParErr", 8, 1 },
+ { "UnxSplCplErrC", 7, 1 },
+ { "UnxSplCplErrR", 6, 1 },
+ { "VPDAddrChng", 5, 1 },
+ { "BusMstrEn", 4, 1 },
+ { "PMStChng", 3, 1 },
+ { "PEXMsg", 2, 1 },
+ { "ZeroLenRd", 1, 1 },
+ { "PEXErr", 0, 1 },
+ { "PCIE_INT_CAUSE", 0x84, 0 },
+ { "BISTErr", 15, 8 },
+ { "MSIXParErr", 12, 3 },
+ { "CFParErr", 11, 1 },
+ { "RFParErr", 10, 1 },
+ { "WFParErr", 9, 1 },
+ { "PIOParErr", 8, 1 },
+ { "UnxSplCplErrC", 7, 1 },
+ { "UnxSplCplErrR", 6, 1 },
+ { "VPDAddrChng", 5, 1 },
+ { "BusMstrEn", 4, 1 },
+ { "PMStChng", 3, 1 },
+ { "PEXMsg", 2, 1 },
+ { "ZeroLenRd", 1, 1 },
+ { "PEXErr", 0, 1 },
+ { "PCIE_CFG", 0x88, 0 },
+ { "PriorityINTA", 23, 1 },
+ { "IniFullPkt", 22, 1 },
+ { "EnableLinkDwnDRst", 21, 1 },
+ { "EnableLinkDownRst", 20, 1 },
+ { "EnableHotRst", 19, 1 },
+ { "IniWaitForGnt", 18, 1 },
+ { "IniBEDis", 17, 1 },
+ { "CLIDecEn", 16, 1 },
+ { "AsyncIntVec", 11, 5 },
+ { "MaxSplTrnC", 7, 4 },
+ { "MaxSplTrnR", 1, 6 },
+ { "CRstWrmMode", 0, 1 },
+ { "PCIE_MODE", 0x8c, 0 },
+ { "NumFstTrnSeqRx", 10, 8 },
+ { "LnkCntlState", 2, 8 },
+ { "VC0Up", 1, 1 },
+ { "LnkInitial", 0, 1 },
+ { "PCIE_WOL", 0x94, 0 },
+ { "WakeUp1", 3, 1 },
+ { "WakeUp0", 2, 1 },
+ { "SleepMode1", 1, 1 },
+ { "SleepMode0", 0, 1 },
+ { "PCIE_PEX_CTRL0", 0x98, 0 },
+ { "CplTimeoutRetry", 31, 1 },
+ { "StrictTSMN", 30, 1 },
+ { "NumFstTrnSeq", 22, 8 },
+ { "ReplayLmt", 2, 20 },
+ { "TxPndChkEn", 1, 1 },
+ { "CplPndChkEn", 0, 1 },
+ { "PCIE_PEX_CTRL1", 0x9c, 0 },
+ { "RxPhyErrEn", 31, 1 },
+ { "DLLPTimeoutLmt", 13, 18 },
+ { "AckLat", 0, 13 },
+ { "PCIE_PEX_CTRL2", 0xa0, 0 },
+ { "LnkCntlDetDir", 30, 1 },
+ { "EnterL1rEn", 29, 1 },
+ { "PMExitL1Req", 28, 1 },
+ { "PMTxIdle", 27, 1 },
+ { "PCIModeLoop", 26, 1 },
+ { "L1ASPMTxRxL0sTime", 14, 12 },
+ { "L0sIdleTime", 3, 11 },
+ { "EnterL1ASPMEn", 2, 1 },
+ { "EnterL1En", 1, 1 },
+ { "EnterL0sEn", 0, 1 },
+ { "PCIE_PEX_ERR", 0xa4, 0 },
+ { "CplTimeoutID", 18, 7 },
+ { "FlowCtlOFlowErr", 17, 1 },
+ { "ReplayTimeout", 16, 1 },
+ { "ReplayRollover", 15, 1 },
+ { "BadDLLP", 14, 1 },
+ { "DLLPErr", 13, 1 },
+ { "FlowCtlProtErr", 12, 1 },
+ { "CplTimeout", 11, 1 },
+ { "PHYRcvErr", 10, 1 },
+ { "DisTLP", 9, 1 },
+ { "BadECRC", 8, 1 },
+ { "BadTLP", 7, 1 },
+ { "MalTLP", 6, 1 },
+ { "UnxCpl", 5, 1 },
+ { "UnsReq", 4, 1 },
+ { "PsnReq", 3, 1 },
+ { "UnsCpl", 2, 1 },
+ { "CplAbt", 1, 1 },
+ { "PsnCpl", 0, 1 },
+ { "PCIE_SERDES_CTRL", 0xa8, 0 },
+ { "PMASel", 3, 1 },
+ { "Lane", 0, 3 },
+ { "PCIE_SERDES_QUAD_CTRL0", 0xac, 0 },
+ { "TestSig", 10, 19 },
+ { "Offset", 2, 8 },
+ { "OffsetEn", 1, 1 },
+ { "IDDQb", 0, 1 },
+ { "PCIE_SERDES_QUAD_CTRL1", 0xb0, 0 },
+ { "FastInit", 28, 1 },
+ { "CTCDisable", 27, 1 },
+ { "ManResetPLL", 26, 1 },
+ { "ManL2Pwrdn", 25, 1 },
+ { "ManQuadEn", 24, 1 },
+ { "RxEqCtl", 22, 2 },
+ { "HiVMode", 21, 1 },
+ { "RefSel", 19, 2 },
+ { "RxTermAdj", 17, 2 },
+ { "TxTermAdj", 15, 2 },
+ { "Deq", 11, 4 },
+ { "Dtx", 7, 4 },
+ { "LoDrv", 6, 1 },
+ { "HiDrv", 5, 1 },
+ { "IntParReset", 4, 1 },
+ { "IntParLPBK", 3, 1 },
+ { "IntSerLPBKwDrv", 2, 1 },
+ { "PW", 1, 1 },
+ { "PClkDetect", 0, 1 },
+ { "PCIE_SERDES_LANE_CTRL", 0xb4, 0 },
+ { "ExtBISTChkErrClr", 22, 1 },
+ { "ExtBISTChkEn", 21, 1 },
+ { "ExtBISTGenEn", 20, 1 },
+ { "ExtBISTPat", 17, 3 },
+ { "ExtParReset", 16, 1 },
+ { "ExtParLPBK", 15, 1 },
+ { "ManRxTermEn", 14, 1 },
+ { "ManBeaconTxEn", 13, 1 },
+ { "ManRxDetectEn", 12, 1 },
+ { "ManTxIdleEn", 11, 1 },
+ { "ManRxIdleEn", 10, 1 },
+ { "ManL1Pwrdn", 9, 1 },
+ { "ManReset", 8, 1 },
+ { "ManFmOffset", 3, 5 },
+ { "ManFmOffsetEn", 2, 1 },
+ { "ManLaneEn", 1, 1 },
+ { "IntSerLPBK", 0, 1 },
+ { "PCIE_SERDES_LANE_STAT", 0xb8, 0 },
+ { "ExtBISTChkErrCnt", 8, 24 },
+ { "ExtBISTChkFmd", 7, 1 },
+ { "BeaconDetectChg", 6, 1 },
+ { "RxDetectChg", 5, 1 },
+ { "TxIdleDetectChg", 4, 1 },
+ { "BeaconDetect", 2, 1 },
+ { "RxDetect", 1, 1 },
+ { "TxIdleDetect", 0, 1 },
+ { NULL }
+};
+
+struct reg_info t3b_t3dbg_regs[] = {
+ { "T3DBG_DBG0_CFG", 0xc0, 0 },
+ { "RegSelect", 9, 8 },
+ { "ModuleSelect", 4, 5 },
+ { "ClkSelect", 0, 4 },
+ { "T3DBG_DBG0_EN", 0xc4, 0 },
+ { "SDRByte0", 8, 1 },
+ { "DDREn", 4, 1 },
+ { "PortEn", 0, 1 },
+ { "T3DBG_DBG1_CFG", 0xc8, 0 },
+ { "RegSelect", 9, 8 },
+ { "ModuleSelect", 4, 5 },
+ { "ClkSelect", 0, 4 },
+ { "T3DBG_DBG1_EN", 0xcc, 0 },
+ { "SDRByte0", 8, 1 },
+ { "DDREn", 4, 1 },
+ { "PortEn", 0, 1 },
+ { "T3DBG_GPIO_EN", 0xd0, 0 },
+ { "GPIO11_OEn", 27, 1 },
+ { "GPIO10_OEn", 26, 1 },
+ { "GPIO9_OEn", 25, 1 },
+ { "GPIO8_OEn", 24, 1 },
+ { "GPIO7_OEn", 23, 1 },
+ { "GPIO6_OEn", 22, 1 },
+ { "GPIO5_OEn", 21, 1 },
+ { "GPIO4_OEn", 20, 1 },
+ { "GPIO3_OEn", 19, 1 },
+ { "GPIO2_OEn", 18, 1 },
+ { "GPIO1_OEn", 17, 1 },
+ { "GPIO0_OEn", 16, 1 },
+ { "GPIO11_Out_Val", 11, 1 },
+ { "GPIO10_Out_Val", 10, 1 },
+ { "GPIO9_Out_Val", 9, 1 },
+ { "GPIO8_Out_Val", 8, 1 },
+ { "GPIO7_Out_Val", 7, 1 },
+ { "GPIO6_Out_Val", 6, 1 },
+ { "GPIO5_Out_Val", 5, 1 },
+ { "GPIO4_Out_Val", 4, 1 },
+ { "GPIO3_Out_Val", 3, 1 },
+ { "GPIO2_Out_Val", 2, 1 },
+ { "GPIO1_Out_Val", 1, 1 },
+ { "GPIO0_Out_Val", 0, 1 },
+ { "T3DBG_GPIO_IN", 0xd4, 0 },
+ { "GPIO11_CHG_DET", 27, 1 },
+ { "GPIO10_CHG_DET", 26, 1 },
+ { "GPIO9_CHG_DET", 25, 1 },
+ { "GPIO8_CHG_DET", 24, 1 },
+ { "GPIO7_CHG_DET", 23, 1 },
+ { "GPIO6_CHG_DET", 22, 1 },
+ { "GPIO5_CHG_DET", 21, 1 },
+ { "GPIO4_CHG_DET", 20, 1 },
+ { "GPIO3_CHG_DET", 19, 1 },
+ { "GPIO2_CHG_DET", 18, 1 },
+ { "GPIO1_CHG_DET", 17, 1 },
+ { "GPIO0_CHG_DET", 16, 1 },
+ { "GPIO11_IN", 11, 1 },
+ { "GPIO10_IN", 10, 1 },
+ { "GPIO9_IN", 9, 1 },
+ { "GPIO8_IN", 8, 1 },
+ { "GPIO7_IN", 7, 1 },
+ { "GPIO6_IN", 6, 1 },
+ { "GPIO5_IN", 5, 1 },
+ { "GPIO4_IN", 4, 1 },
+ { "GPIO3_IN", 3, 1 },
+ { "GPIO2_IN", 2, 1 },
+ { "GPIO1_IN", 1, 1 },
+ { "GPIO0_IN", 0, 1 },
+ { "T3DBG_INT_ENABLE", 0xd8, 0 },
+ { "C_LOCK", 21, 1 },
+ { "M_LOCK", 20, 1 },
+ { "U_LOCK", 19, 1 },
+ { "R_LOCK", 18, 1 },
+ { "PX_LOCK", 17, 1 },
+ { "GPIO11", 11, 1 },
+ { "GPIO10", 10, 1 },
+ { "GPIO9", 9, 1 },
+ { "GPIO8", 8, 1 },
+ { "GPIO7", 7, 1 },
+ { "GPIO6", 6, 1 },
+ { "GPIO5", 5, 1 },
+ { "GPIO4", 4, 1 },
+ { "GPIO3", 3, 1 },
+ { "GPIO2", 2, 1 },
+ { "GPIO1", 1, 1 },
+ { "GPIO0", 0, 1 },
+ { "T3DBG_INT_CAUSE", 0xdc, 0 },
+ { "C_LOCK", 21, 1 },
+ { "M_LOCK", 20, 1 },
+ { "U_LOCK", 19, 1 },
+ { "R_LOCK", 18, 1 },
+ { "PX_LOCK", 17, 1 },
+ { "GPIO11", 11, 1 },
+ { "GPIO10", 10, 1 },
+ { "GPIO9", 9, 1 },
+ { "GPIO8", 8, 1 },
+ { "GPIO7", 7, 1 },
+ { "GPIO6", 6, 1 },
+ { "GPIO5", 5, 1 },
+ { "GPIO4", 4, 1 },
+ { "GPIO3", 3, 1 },
+ { "GPIO2", 2, 1 },
+ { "GPIO1", 1, 1 },
+ { "GPIO0", 0, 1 },
+ { "T3DBG_DBG0_RST_VALUE", 0xe0, 0 },
+ { "DebugData", 0, 8 },
+ { "T3DBG_PLL_OCLK_PAD_EN", 0xe4, 0 },
+ { "PCIE_OCLK_En", 20, 1 },
+ { "PClkTree_DBG_En", 17, 1 },
+ { "PCIX_OCLK_En", 16, 1 },
+ { "U_OCLK_En", 12, 1 },
+ { "R_OCLK_En", 8, 1 },
+ { "M_OCLK_En", 4, 1 },
+ { "C_OCLK_En", 0, 1 },
+ { "T3DBG_PLL_LOCK", 0xe8, 0 },
+ { "PCIX_LOCK", 16, 1 },
+ { "U_LOCK", 12, 1 },
+ { "R_LOCK", 8, 1 },
+ { "M_LOCK", 4, 1 },
+ { "C_LOCK", 0, 1 },
+ { "T3DBG_SERDES_RBC_CFG", 0xec, 0 },
+ { "X_RBC_Lane_Sel", 16, 2 },
+ { "X_RBC_Dbg_En", 12, 1 },
+ { "X_Serdes_Sel", 8, 1 },
+ { "PE_RBC_Lane_Sel", 4, 3 },
+ { "PE_RBC_Dbg_En", 0, 1 },
+ { "T3DBG_GPIO_ACT_LOW", 0xf0, 0 },
+ { "C_LOCK_ACT_LOW", 21, 1 },
+ { "M_LOCK_ACT_LOW", 20, 1 },
+ { "U_LOCK_ACT_LOW", 19, 1 },
+ { "R_LOCK_ACT_LOW", 18, 1 },
+ { "PX_LOCK_ACT_LOW", 17, 1 },
+ { "GPIO11_ACT_LOW", 11, 1 },
+ { "GPIO10_ACT_LOW", 10, 1 },
+ { "GPIO9_ACT_LOW", 9, 1 },
+ { "GPIO8_ACT_LOW", 8, 1 },
+ { "GPIO7_ACT_LOW", 7, 1 },
+ { "GPIO6_ACT_LOW", 6, 1 },
+ { "GPIO5_ACT_LOW", 5, 1 },
+ { "GPIO4_ACT_LOW", 4, 1 },
+ { "GPIO3_ACT_LOW", 3, 1 },
+ { "GPIO2_ACT_LOW", 2, 1 },
+ { "GPIO1_ACT_LOW", 1, 1 },
+ { "GPIO0_ACT_LOW", 0, 1 },
+ { "T3DBG_PMON_CFG", 0xf4, 0 },
+ { "PMON_DONE", 29, 1 },
+ { "PMON_FAIL", 28, 1 },
+ { "PMON_FDEL_AUTO", 22, 6 },
+ { "PMON_CDEL_AUTO", 16, 6 },
+ { "PMON_FDEL_MANUAL", 10, 6 },
+ { "PMON_CDEL_MANUAL", 4, 6 },
+ { "PMON_MANUAL", 1, 1 },
+ { "PMON_AUTO", 0, 1 },
+ { "T3DBG_SERDES_REFCLK_CFG", 0xf8, 0 },
+ { "PE_REFCLK_DBG_EN", 12, 1 },
+ { "X_REFCLK_DBG_EN", 8, 1 },
+ { "PE_REFCLK_TERMADJ", 5, 2 },
+ { "PE_REFCLK_PD", 4, 1 },
+ { "X_REFCLK_TERMADJ", 1, 2 },
+ { "X_REFCLK_PD", 0, 1 },
+ { "T3DBG_PCIE_PMA_BSPIN_CFG", 0xfc, 0 },
+ { "BSModeQuad1", 31, 1 },
+ { "BSInSelLane7", 29, 2 },
+ { "BSEnLane7", 28, 1 },
+ { "BSInSelLane6", 25, 2 },
+ { "BSEnLane6", 24, 1 },
+ { "BSInSelLane5", 21, 2 },
+ { "BSEnLane5", 20, 1 },
+ { "BSInSelLane4", 17, 2 },
+ { "BSEnLane4", 16, 1 },
+ { "BSModeQuad0", 15, 1 },
+ { "BSInSelLane3", 13, 2 },
+ { "BSEnLane3", 12, 1 },
+ { "BSInSelLane2", 9, 2 },
+ { "BSEnLane2", 8, 1 },
+ { "BSInSelLane1", 5, 2 },
+ { "BSEnLane1", 4, 1 },
+ { "BSInSelLane0", 1, 2 },
+ { "BSEnLane0", 0, 1 },
+ { NULL }
+};
+
+struct reg_info t3b_mc7_pmrx_regs[] = {
+ { "MC7_CFG", 0x100, 0 },
+ { "ImpSetUpdate", 14, 1 },
+ { "IFEn", 13, 1 },
+ { "TERM300", 12, 1 },
+ { "TERM150", 11, 1 },
+ { "Slow", 10, 1 },
+ { "Width", 8, 2 },
+ { "ODTEn", 7, 1 },
+ { "Bks", 6, 1 },
+ { "Org", 5, 1 },
+ { "Den", 2, 3 },
+ { "Rdy", 1, 1 },
+ { "ClkEn", 0, 1 },
+ { "MC7_MODE", 0x104, 0 },
+ { "Busy", 31, 1 },
+ { "Mode", 0, 16 },
+ { "MC7_EXT_MODE1", 0x108, 0 },
+ { "Busy", 31, 1 },
+ { "OCDAdjustMode", 20, 1 },
+ { "OCDCode", 16, 4 },
+ { "ExtMode1", 0, 16 },
+ { "MC7_EXT_MODE2", 0x10c, 0 },
+ { "Busy", 31, 1 },
+ { "ExtMode2", 0, 16 },
+ { "MC7_EXT_MODE3", 0x110, 0 },
+ { "Busy", 31, 1 },
+ { "ExtMode3", 0, 16 },
+ { "MC7_PRE", 0x114, 0 },
+ { "Busy", 31, 1 },
+ { "MC7_REF", 0x118, 0 },
+ { "Busy", 31, 1 },
+ { "PreRefDiv", 1, 14 },
+ { "PerRefEn", 0, 1 },
+ { "MC7_DLL", 0x11c, 0 },
+ { "DLLLock", 31, 1 },
+ { "DLLDelta", 24, 7 },
+ { "ManDelta", 3, 7 },
+ { "DLLDeltaSel", 2, 1 },
+ { "DLLEnb", 1, 1 },
+ { "DLLRst", 0, 1 },
+ { "MC7_PARM", 0x120, 0 },
+ { "ActToPreDly", 26, 4 },
+ { "ActToRdWrDly", 23, 3 },
+ { "PreCyc", 20, 3 },
+ { "RefCyc", 13, 7 },
+ { "BkCyc", 8, 5 },
+ { "WrToRdDly", 4, 4 },
+ { "RdToWrDly", 0, 4 },
+ { "MC7_HWM_WRR", 0x124, 0 },
+ { "MEM_HWM", 26, 6 },
+ { "ULP_HWM", 22, 4 },
+ { "TOT_RLD_WT", 14, 8 },
+ { "MEM_RLD_WT", 7, 7 },
+ { "ULP_RLD_WT", 0, 7 },
+ { "MC7_CAL", 0x128, 0 },
+ { "BUSY", 31, 1 },
+ { "CAL_FAULT", 30, 1 },
+ { "PER_CAL_DIV", 22, 8 },
+ { "PER_CAL_EN", 21, 1 },
+ { "SGL_CAL_EN", 20, 1 },
+ { "IMP_UPD_MODE", 19, 1 },
+ { "IMP_SEL", 18, 1 },
+ { "IMP_MAN_PD", 15, 3 },
+ { "IMP_MAN_PU", 12, 3 },
+ { "IMP_CAL_PD", 9, 3 },
+ { "IMP_CAL_PU", 6, 3 },
+ { "IMP_SET_PD", 3, 3 },
+ { "IMP_SET_PU", 0, 3 },
+ { "MC7_ERR_ADDR", 0x12c, 0 },
+ { "ErrAddress", 3, 29 },
+ { "ErrAgent", 1, 2 },
+ { "ErrOp", 0, 1 },
+ { "MC7_ECC", 0x130, 0 },
+ { "UECnt", 10, 8 },
+ { "CECnt", 2, 8 },
+ { "ECCChkEn", 1, 1 },
+ { "ECCGenEn", 0, 1 },
+ { "MC7_CE_ADDR", 0x134, 0 },
+ { "MC7_CE_DATA0", 0x138, 0 },
+ { "MC7_CE_DATA1", 0x13c, 0 },
+ { "MC7_CE_DATA2", 0x140, 0 },
+ { "Data", 0, 8 },
+ { "MC7_UE_ADDR", 0x144, 0 },
+ { "MC7_UE_DATA0", 0x148, 0 },
+ { "MC7_UE_DATA1", 0x14c, 0 },
+ { "MC7_UE_DATA2", 0x150, 0 },
+ { "Data", 0, 8 },
+ { "MC7_BD_ADDR", 0x154, 0 },
+ { "Addr", 3, 29 },
+ { "MC7_BD_DATA0", 0x158, 0 },
+ { "MC7_BD_DATA1", 0x15c, 0 },
+ { "MC7_BD_DATA2", 0x160, 0 },
+ { "Data", 0, 8 },
+ { "MC7_BD_OP", 0x164, 0 },
+ { "Busy", 31, 1 },
+ { "Op", 0, 1 },
+ { "MC7_BIST_ADDR_BEG", 0x168, 0 },
+ { "AddrBeg", 5, 27 },
+ { "MC7_BIST_ADDR_END", 0x16c, 0 },
+ { "AddrEnd", 5, 27 },
+ { "MC7_BIST_DATA", 0x170, 0 },
+ { "MC7_BIST_OP", 0x174, 0 },
+ { "Busy", 31, 1 },
+ { "Gap", 4, 5 },
+ { "Cont", 3, 1 },
+ { "DataPat", 1, 2 },
+ { "Op", 0, 1 },
+ { "MC7_INT_ENABLE", 0x178, 0 },
+ { "AE", 17, 1 },
+ { "PE", 2, 15 },
+ { "UE", 1, 1 },
+ { "CE", 0, 1 },
+ { "MC7_INT_CAUSE", 0x17c, 0 },
+ { "AE", 17, 1 },
+ { "PE", 2, 15 },
+ { "UE", 1, 1 },
+ { "CE", 0, 1 },
+ { NULL }
+};
+
+struct reg_info t3b_mc7_pmtx_regs[] = {
+ { "MC7_CFG", 0x180, 0 },
+ { "ImpSetUpdate", 14, 1 },
+ { "IFEn", 13, 1 },
+ { "TERM300", 12, 1 },
+ { "TERM150", 11, 1 },
+ { "Slow", 10, 1 },
+ { "Width", 8, 2 },
+ { "ODTEn", 7, 1 },
+ { "Bks", 6, 1 },
+ { "Org", 5, 1 },
+ { "Den", 2, 3 },
+ { "Rdy", 1, 1 },
+ { "ClkEn", 0, 1 },
+ { "MC7_MODE", 0x184, 0 },
+ { "Busy", 31, 1 },
+ { "Mode", 0, 16 },
+ { "MC7_EXT_MODE1", 0x188, 0 },
+ { "Busy", 31, 1 },
+ { "OCDAdjustMode", 20, 1 },
+ { "OCDCode", 16, 4 },
+ { "ExtMode1", 0, 16 },
+ { "MC7_EXT_MODE2", 0x18c, 0 },
+ { "Busy", 31, 1 },
+ { "ExtMode2", 0, 16 },
+ { "MC7_EXT_MODE3", 0x190, 0 },
+ { "Busy", 31, 1 },
+ { "ExtMode3", 0, 16 },
+ { "MC7_PRE", 0x194, 0 },
+ { "Busy", 31, 1 },
+ { "MC7_REF", 0x198, 0 },
+ { "Busy", 31, 1 },
+ { "PreRefDiv", 1, 14 },
+ { "PerRefEn", 0, 1 },
+ { "MC7_DLL", 0x19c, 0 },
+ { "DLLLock", 31, 1 },
+ { "DLLDelta", 24, 7 },
+ { "ManDelta", 3, 7 },
+ { "DLLDeltaSel", 2, 1 },
+ { "DLLEnb", 1, 1 },
+ { "DLLRst", 0, 1 },
+ { "MC7_PARM", 0x1a0, 0 },
+ { "ActToPreDly", 26, 4 },
+ { "ActToRdWrDly", 23, 3 },
+ { "PreCyc", 20, 3 },
+ { "RefCyc", 13, 7 },
+ { "BkCyc", 8, 5 },
+ { "WrToRdDly", 4, 4 },
+ { "RdToWrDly", 0, 4 },
+ { "MC7_HWM_WRR", 0x1a4, 0 },
+ { "MEM_HWM", 26, 6 },
+ { "ULP_HWM", 22, 4 },
+ { "TOT_RLD_WT", 14, 8 },
+ { "MEM_RLD_WT", 7, 7 },
+ { "ULP_RLD_WT", 0, 7 },
+ { "MC7_CAL", 0x1a8, 0 },
+ { "BUSY", 31, 1 },
+ { "CAL_FAULT", 30, 1 },
+ { "PER_CAL_DIV", 22, 8 },
+ { "PER_CAL_EN", 21, 1 },
+ { "SGL_CAL_EN", 20, 1 },
+ { "IMP_UPD_MODE", 19, 1 },
+ { "IMP_SEL", 18, 1 },
+ { "IMP_MAN_PD", 15, 3 },
+ { "IMP_MAN_PU", 12, 3 },
+ { "IMP_CAL_PD", 9, 3 },
+ { "IMP_CAL_PU", 6, 3 },
+ { "IMP_SET_PD", 3, 3 },
+ { "IMP_SET_PU", 0, 3 },
+ { "MC7_ERR_ADDR", 0x1ac, 0 },
+ { "ErrAddress", 3, 29 },
+ { "ErrAgent", 1, 2 },
+ { "ErrOp", 0, 1 },
+ { "MC7_ECC", 0x1b0, 0 },
+ { "UECnt", 10, 8 },
+ { "CECnt", 2, 8 },
+ { "ECCChkEn", 1, 1 },
+ { "ECCGenEn", 0, 1 },
+ { "MC7_CE_ADDR", 0x1b4, 0 },
+ { "MC7_CE_DATA0", 0x1b8, 0 },
+ { "MC7_CE_DATA1", 0x1bc, 0 },
+ { "MC7_CE_DATA2", 0x1c0, 0 },
+ { "Data", 0, 8 },
+ { "MC7_UE_ADDR", 0x1c4, 0 },
+ { "MC7_UE_DATA0", 0x1c8, 0 },
+ { "MC7_UE_DATA1", 0x1cc, 0 },
+ { "MC7_UE_DATA2", 0x1d0, 0 },
+ { "Data", 0, 8 },
+ { "MC7_BD_ADDR", 0x1d4, 0 },
+ { "Addr", 3, 29 },
+ { "MC7_BD_DATA0", 0x1d8, 0 },
+ { "MC7_BD_DATA1", 0x1dc, 0 },
+ { "MC7_BD_DATA2", 0x1e0, 0 },
+ { "Data", 0, 8 },
+ { "MC7_BD_OP", 0x1e4, 0 },
+ { "Busy", 31, 1 },
+ { "Op", 0, 1 },
+ { "MC7_BIST_ADDR_BEG", 0x1e8, 0 },
+ { "AddrBeg", 5, 27 },
+ { "MC7_BIST_ADDR_END", 0x1ec, 0 },
+ { "AddrEnd", 5, 27 },
+ { "MC7_BIST_DATA", 0x1f0, 0 },
+ { "MC7_BIST_OP", 0x1f4, 0 },
+ { "Busy", 31, 1 },
+ { "Gap", 4, 5 },
+ { "Cont", 3, 1 },
+ { "DataPat", 1, 2 },
+ { "Op", 0, 1 },
+ { "MC7_INT_ENABLE", 0x1f8, 0 },
+ { "AE", 17, 1 },
+ { "PE", 2, 15 },
+ { "UE", 1, 1 },
+ { "CE", 0, 1 },
+ { "MC7_INT_CAUSE", 0x1fc, 0 },
+ { "AE", 17, 1 },
+ { "PE", 2, 15 },
+ { "UE", 1, 1 },
+ { "CE", 0, 1 },
+ { NULL }
+};
+
+struct reg_info t3b_mc7_cm_regs[] = {
+ { "MC7_CFG", 0x200, 0 },
+ { "ImpSetUpdate", 14, 1 },
+ { "IFEn", 13, 1 },
+ { "TERM300", 12, 1 },
+ { "TERM150", 11, 1 },
+ { "Slow", 10, 1 },
+ { "Width", 8, 2 },
+ { "ODTEn", 7, 1 },
+ { "Bks", 6, 1 },
+ { "Org", 5, 1 },
+ { "Den", 2, 3 },
+ { "Rdy", 1, 1 },
+ { "ClkEn", 0, 1 },
+ { "MC7_MODE", 0x204, 0 },
+ { "Busy", 31, 1 },
+ { "Mode", 0, 16 },
+ { "MC7_EXT_MODE1", 0x208, 0 },
+ { "Busy", 31, 1 },
+ { "OCDAdjustMode", 20, 1 },
+ { "OCDCode", 16, 4 },
+ { "ExtMode1", 0, 16 },
+ { "MC7_EXT_MODE2", 0x20c, 0 },
+ { "Busy", 31, 1 },
+ { "ExtMode2", 0, 16 },
+ { "MC7_EXT_MODE3", 0x210, 0 },
+ { "Busy", 31, 1 },
+ { "ExtMode3", 0, 16 },
+ { "MC7_PRE", 0x214, 0 },
+ { "Busy", 31, 1 },
+ { "MC7_REF", 0x218, 0 },
+ { "Busy", 31, 1 },
+ { "PreRefDiv", 1, 14 },
+ { "PerRefEn", 0, 1 },
+ { "MC7_DLL", 0x21c, 0 },
+ { "DLLLock", 31, 1 },
+ { "DLLDelta", 24, 7 },
+ { "ManDelta", 3, 7 },
+ { "DLLDeltaSel", 2, 1 },
+ { "DLLEnb", 1, 1 },
+ { "DLLRst", 0, 1 },
+ { "MC7_PARM", 0x220, 0 },
+ { "ActToPreDly", 26, 4 },
+ { "ActToRdWrDly", 23, 3 },
+ { "PreCyc", 20, 3 },
+ { "RefCyc", 13, 7 },
+ { "BkCyc", 8, 5 },
+ { "WrToRdDly", 4, 4 },
+ { "RdToWrDly", 0, 4 },
+ { "MC7_HWM_WRR", 0x224, 0 },
+ { "MEM_HWM", 26, 6 },
+ { "ULP_HWM", 22, 4 },
+ { "TOT_RLD_WT", 14, 8 },
+ { "MEM_RLD_WT", 7, 7 },
+ { "ULP_RLD_WT", 0, 7 },
+ { "MC7_CAL", 0x228, 0 },
+ { "BUSY", 31, 1 },
+ { "CAL_FAULT", 30, 1 },
+ { "PER_CAL_DIV", 22, 8 },
+ { "PER_CAL_EN", 21, 1 },
+ { "SGL_CAL_EN", 20, 1 },
+ { "IMP_UPD_MODE", 19, 1 },
+ { "IMP_SEL", 18, 1 },
+ { "IMP_MAN_PD", 15, 3 },
+ { "IMP_MAN_PU", 12, 3 },
+ { "IMP_CAL_PD", 9, 3 },
+ { "IMP_CAL_PU", 6, 3 },
+ { "IMP_SET_PD", 3, 3 },
+ { "IMP_SET_PU", 0, 3 },
+ { "MC7_ERR_ADDR", 0x22c, 0 },
+ { "ErrAddress", 3, 29 },
+ { "ErrAgent", 1, 2 },
+ { "ErrOp", 0, 1 },
+ { "MC7_ECC", 0x230, 0 },
+ { "UECnt", 10, 8 },
+ { "CECnt", 2, 8 },
+ { "ECCChkEn", 1, 1 },
+ { "ECCGenEn", 0, 1 },
+ { "MC7_CE_ADDR", 0x234, 0 },
+ { "MC7_CE_DATA0", 0x238, 0 },
+ { "MC7_CE_DATA1", 0x23c, 0 },
+ { "MC7_CE_DATA2", 0x240, 0 },
+ { "Data", 0, 8 },
+ { "MC7_UE_ADDR", 0x244, 0 },
+ { "MC7_UE_DATA0", 0x248, 0 },
+ { "MC7_UE_DATA1", 0x24c, 0 },
+ { "MC7_UE_DATA2", 0x250, 0 },
+ { "Data", 0, 8 },
+ { "MC7_BD_ADDR", 0x254, 0 },
+ { "Addr", 3, 29 },
+ { "MC7_BD_DATA0", 0x258, 0 },
+ { "MC7_BD_DATA1", 0x25c, 0 },
+ { "MC7_BD_DATA2", 0x260, 0 },
+ { "Data", 0, 8 },
+ { "MC7_BD_OP", 0x264, 0 },
+ { "Busy", 31, 1 },
+ { "Op", 0, 1 },
+ { "MC7_BIST_ADDR_BEG", 0x268, 0 },
+ { "AddrBeg", 5, 27 },
+ { "MC7_BIST_ADDR_END", 0x26c, 0 },
+ { "AddrEnd", 5, 27 },
+ { "MC7_BIST_DATA", 0x270, 0 },
+ { "MC7_BIST_OP", 0x274, 0 },
+ { "Busy", 31, 1 },
+ { "Gap", 4, 5 },
+ { "Cont", 3, 1 },
+ { "DataPat", 1, 2 },
+ { "Op", 0, 1 },
+ { "MC7_INT_ENABLE", 0x278, 0 },
+ { "AE", 17, 1 },
+ { "PE", 2, 15 },
+ { "UE", 1, 1 },
+ { "CE", 0, 1 },
+ { "MC7_INT_CAUSE", 0x27c, 0 },
+ { "AE", 17, 1 },
+ { "PE", 2, 15 },
+ { "UE", 1, 1 },
+ { "CE", 0, 1 },
+ { NULL }
+};
+
+struct reg_info t3b_cim_regs[] = {
+ { "CIM_BOOT_CFG", 0x280, 0 },
+ { "BootAddr", 2, 30 },
+ { "BootSdram", 1, 1 },
+ { "uPCRst", 0, 1 },
+ { "CIM_FLASH_BASE_ADDR", 0x284, 0 },
+ { "FlashBaseAddr", 2, 22 },
+ { "CIM_FLASH_ADDR_SIZE", 0x288, 0 },
+ { "FlashAddrSize", 2, 22 },
+ { "CIM_SDRAM_BASE_ADDR", 0x28c, 0 },
+ { "SdramBaseAddr", 2, 30 },
+ { "CIM_SDRAM_ADDR_SIZE", 0x290, 0 },
+ { "SdramAddrSize", 2, 30 },
+ { "CIM_UP_SPARE_INT", 0x294, 0 },
+ { "uPSpareInt", 0, 3 },
+ { "CIM_HOST_INT_ENABLE", 0x298, 0 },
+ { "Timer1IntEn", 15, 1 },
+ { "Timer0IntEn", 14, 1 },
+ { "PrefDropIntEn", 13, 1 },
+ { "BlkWrPlIntEn", 12, 1 },
+ { "BlkRdPlIntEn", 11, 1 },
+ { "BlkWrCtlIntEn", 10, 1 },
+ { "BlkRdCtlIntEn", 9, 1 },
+ { "BlkWrFlashIntEn", 8, 1 },
+ { "BlkRdFlashIntEn", 7, 1 },
+ { "SglWrFlashIntEn", 6, 1 },
+ { "WrBlkFlashIntEn", 5, 1 },
+ { "BlkWrBootIntEn", 4, 1 },
+ { "BlkRdBootIntEn", 3, 1 },
+ { "FlashRangeIntEn", 2, 1 },
+ { "SdramRangeIntEn", 1, 1 },
+ { "RsvdSpaceIntEn", 0, 1 },
+ { "CIM_HOST_INT_CAUSE", 0x29c, 0 },
+ { "Timer1Int", 15, 1 },
+ { "Timer0Int", 14, 1 },
+ { "PrefDropInt", 13, 1 },
+ { "BlkWrPlInt", 12, 1 },
+ { "BlkRdPlInt", 11, 1 },
+ { "BlkWrCtlInt", 10, 1 },
+ { "BlkRdCtlInt", 9, 1 },
+ { "BlkWrFlashInt", 8, 1 },
+ { "BlkRdFlashInt", 7, 1 },
+ { "SglWrFlashInt", 6, 1 },
+ { "WrBlkFlashInt", 5, 1 },
+ { "BlkWrBootInt", 4, 1 },
+ { "BlkRdBootInt", 3, 1 },
+ { "FlashRangeInt", 2, 1 },
+ { "SdramRangeInt", 1, 1 },
+ { "RsvdSpaceInt", 0, 1 },
+ { "CIM_UP_INT_ENABLE", 0x2a0, 0 },
+ { "MstPlIntEn", 16, 1 },
+ { "Timer1IntEn", 15, 1 },
+ { "Timer0IntEn", 14, 1 },
+ { "PrefDropIntEn", 13, 1 },
+ { "BlkWrPlIntEn", 12, 1 },
+ { "BlkRdPlIntEn", 11, 1 },
+ { "BlkWrCtlIntEn", 10, 1 },
+ { "BlkRdCtlIntEn", 9, 1 },
+ { "BlkWrFlashIntEn", 8, 1 },
+ { "BlkRdFlashIntEn", 7, 1 },
+ { "SglWrFlashIntEn", 6, 1 },
+ { "WrBlkFlashIntEn", 5, 1 },
+ { "BlkWrBootIntEn", 4, 1 },
+ { "BlkRdBootIntEn", 3, 1 },
+ { "FlashRangeIntEn", 2, 1 },
+ { "SdramRangeIntEn", 1, 1 },
+ { "RsvdSpaceIntEn", 0, 1 },
+ { "CIM_UP_INT_CAUSE", 0x2a4, 0 },
+ { "MstPlInt", 16, 1 },
+ { "Timer1Int", 15, 1 },
+ { "Timer0Int", 14, 1 },
+ { "PrefDropInt", 13, 1 },
+ { "BlkWrPlInt", 12, 1 },
+ { "BlkRdPlInt", 11, 1 },
+ { "BlkWrCtlInt", 10, 1 },
+ { "BlkRdCtlInt", 9, 1 },
+ { "BlkWrFlashInt", 8, 1 },
+ { "BlkRdFlashInt", 7, 1 },
+ { "SglWrFlashInt", 6, 1 },
+ { "WrBlkFlashInt", 5, 1 },
+ { "BlkWrBootInt", 4, 1 },
+ { "BlkRdBootInt", 3, 1 },
+ { "FlashRangeInt", 2, 1 },
+ { "SdramRangeInt", 1, 1 },
+ { "RsvdSpaceInt", 0, 1 },
+ { "CIM_IBQ_FULLA_THRSH", 0x2a8, 0 },
+ { "Ibq0FullThrsh", 0, 9 },
+ { "Ibq1FullThrsh", 16, 9 },
+ { "CIM_IBQ_FULLB_THRSH", 0x2ac, 0 },
+ { "Ibq2FullThrsh", 0, 9 },
+ { "Ibq3FullThrsh", 16, 9 },
+ { "CIM_HOST_ACC_CTRL", 0x2b0, 0 },
+ { "HostBusy", 17, 1 },
+ { "HostWrite", 16, 1 },
+ { "HostAddr", 0, 16 },
+ { "CIM_HOST_ACC_DATA", 0x2b4, 0 },
+ { "CIM_IBQ_DBG_CFG", 0x2c0, 0 },
+ { "IbqDbgAddr", 16, 9 },
+ { "IbqDbgQID", 3, 2 },
+ { "IbqDbgWr", 2, 1 },
+ { "IbqDbgBusy", 1, 1 },
+ { "IbqDbgEn", 0, 1 },
+ { "CIM_OBQ_DBG_CFG", 0x2c4, 0 },
+ { "ObqDbgAddr", 16, 9 },
+ { "ObqDbgQID", 3, 2 },
+ { "ObqDbgWr", 2, 1 },
+ { "ObqDbgBusy", 1, 1 },
+ { "ObqDbgEn", 0, 1 },
+ { "CIM_IBQ_DBG_DATA", 0x2c8, 0 },
+ { "CIM_OBQ_DBG_DATA", 0x2cc, 0 },
+ { "CIM_CDEBUGDATA", 0x2d0, 0 },
+ { "CDebugDataH", 16, 16 },
+ { "CDebugDataL", 0, 16 },
+ { "CIM_DEBUGCFG", 0x2e0, 0 },
+ { "POLADbgRdPtr", 23, 9 },
+ { "PILADbgRdPtr", 14, 9 },
+ { "LADbgEn", 12, 1 },
+ { "DebugSelH", 5, 5 },
+ { "DebugSelL", 0, 5 },
+ { "CIM_DEBUGSTS", 0x2e4, 0 },
+ { "POLADbgWrPtr", 16, 9 },
+ { "PILADbgWrPtr", 0, 9 },
+ { "CIM_PO_LA_DEBUGDATA", 0x2e8, 0 },
+ { "CIM_PI_LA_DEBUGDATA", 0x2ec, 0 },
+ { NULL }
+};
+
+struct reg_info t3b_tp1_regs[] = {
+ { "TP_IN_CONFIG", 0x300, 0 },
+ { "RXFbArbPrio", 25, 1 },
+ { "TXFbArbPrio", 24, 1 },
+ { "DBMaxOpCnt", 16, 8 },
+ { "IPv6Enable", 15, 1 },
+ { "NICMode", 14, 1 },
+ { "EChecksumCheckTCP", 13, 1 },
+ { "EChecksumCheckIP", 12, 1 },
+ { "ECPL", 10, 1 },
+ { "EEthernet", 8, 1 },
+ { "ETunnel", 7, 1 },
+ { "CChecksumCheckTCP", 6, 1 },
+ { "CChecksumCheckIP", 5, 1 },
+ { "CCPL", 3, 1 },
+ { "CEthernet", 1, 1 },
+ { "CTunnel", 0, 1 },
+ { "TP_OUT_CONFIG", 0x304, 0 },
+ { "IPIDSplitMode", 16, 1 },
+ { "VLANExtractionEnable2ndPort", 13, 1 },
+ { "VLANExtractionEnable", 12, 1 },
+ { "EChecksumGenerateTCP", 11, 1 },
+ { "EChecksumGenerateIP", 10, 1 },
+ { "ECPL", 8, 1 },
+ { "EEthernet", 6, 1 },
+ { "CChecksumGenerateTCP", 5, 1 },
+ { "CChecksumGenerateIP", 4, 1 },
+ { "CCPL", 2, 1 },
+ { "CEthernet", 0, 1 },
+ { "TP_GLOBAL_CONFIG", 0x308, 0 },
+ { "SYNCookieParams", 26, 6 },
+ { "RXFlowControlDisable", 25, 1 },
+ { "TXPacingEnable", 24, 1 },
+ { "AttackFilterEnable", 23, 1 },
+ { "SYNCookieNoOptions", 22, 1 },
+ { "ProtectedMode", 21, 1 },
+ { "PingDrop", 20, 1 },
+ { "FragmentDrop", 19, 1 },
+ { "FiveTupleLookup", 17, 2 },
+ { "PathMTU", 15, 1 },
+ { "IPIdentSplit", 14, 1 },
+ { "IPChecksumOffload", 13, 1 },
+ { "UDPChecksumOffload", 12, 1 },
+ { "TCPChecksumOffload", 11, 1 },
+ { "QOSMapping", 10, 1 },
+ { "TCAMServerUse", 8, 2 },
+ { "IPTTL", 0, 8 },
+ { "TP_GLOBAL_RX_CREDIT", 0x30c, 0 },
+ { "TP_CMM_SIZE", 0x310, 0 },
+ { "CMMemMgrSize", 0, 28 },
+ { "TP_CMM_MM_BASE", 0x314, 0 },
+ { "CMMemMgrBase", 0, 28 },
+ { "TP_CMM_TIMER_BASE", 0x318, 0 },
+ { "CMTimerMaxNum", 28, 2 },
+ { "CMTimerBase", 0, 28 },
+ { "TP_PMM_SIZE", 0x31c, 0 },
+ { "PMSize", 0, 28 },
+ { "TP_PMM_TX_BASE", 0x320, 0 },
+ { "TP_PMM_DEFRAG_BASE", 0x324, 0 },
+ { "TP_PMM_RX_BASE", 0x328, 0 },
+ { "TP_PMM_RX_PAGE_SIZE", 0x32c, 0 },
+ { "TP_PMM_RX_MAX_PAGE", 0x330, 0 },
+ { "PMRxMaxPage", 0, 21 },
+ { "TP_PMM_TX_PAGE_SIZE", 0x334, 0 },
+ { "TP_PMM_TX_MAX_PAGE", 0x338, 0 },
+ { "PMTxMaxPage", 0, 21 },
+ { "TP_TCP_OPTIONS", 0x340, 0 },
+ { "MTUDefault", 16, 16 },
+ { "MTUEnable", 10, 1 },
+ { "SACKTx", 9, 1 },
+ { "SACKRx", 8, 1 },
+ { "SACKMode", 4, 2 },
+ { "WindowScaleMode", 2, 2 },
+ { "TimestampsMode", 0, 2 },
+ { "TP_DACK_CONFIG", 0x344, 0 },
+ { "AutoState3", 30, 2 },
+ { "AutoState2", 28, 2 },
+ { "AutoState1", 26, 2 },
+ { "ByteThreshold", 5, 20 },
+ { "MSSThreshold", 3, 2 },
+ { "AutoCareful", 2, 1 },
+ { "AutoEnable", 1, 1 },
+ { "Mode", 0, 1 },
+ { "TP_PC_CONFIG", 0x348, 0 },
+ { "CMCacheDisable", 31, 1 },
+ { "EnableOcspiFull", 30, 1 },
+ { "EnableFLMErrorDDP", 29, 1 },
+ { "LockTid", 28, 1 },
+ { "FixRcvWnd", 27, 1 },
+ { "TxTosQueueMapMode", 26, 1 },
+ { "RddpCongEn", 25, 1 },
+ { "EnableOnFlyPDU", 24, 1 },
+ { "EnableEPCMDAFull", 23, 1 },
+ { "ModulateUnionMode", 22, 1 },
+ { "TxDataAckRateEnable", 21, 1 },
+ { "TxDeferEnable", 20, 1 },
+ { "RxCongestionMode", 19, 1 },
+ { "HearbeatOnceDACK", 18, 1 },
+ { "HearbeatOnceHeap", 17, 1 },
+ { "HearbeatDACK", 16, 1 },
+ { "TxCongestionMode", 15, 1 },
+ { "AcceptLatestRcvAdv", 14, 1 },
+ { "DisableSYNData", 13, 1 },
+ { "DisableWindowPSH", 12, 1 },
+ { "DisableFINOldData", 11, 1 },
+ { "EnableFLMError", 10, 1 },
+ { "DisableNextMtu", 9, 1 },
+ { "FilterPeerFIN", 8, 1 },
+ { "EnableFeedbackSend", 7, 1 },
+ { "EnableRDMAError", 6, 1 },
+ { "EnableDDPFlowControl", 5, 1 },
+ { "DisableHeldFIN", 4, 1 },
+ { "TableLatencyDelta", 0, 4 },
+ { "TP_PC_CONFIG2", 0x34c, 0 },
+ { "EnableDropRQEmptyPkt", 10, 1 },
+ { "EnableTxPortfromDA2", 9, 1 },
+ { "EnableRxPktTmstpRss", 8, 1 },
+ { "EnableSndUnaInRxData", 7, 1 },
+ { "EnableRxPortFromAddr", 6, 1 },
+ { "EnableTxPortfromDA", 5, 1 },
+ { "CHdrAFull", 4, 1 },
+ { "EnableNonOfdScbBit", 3, 1 },
+ { "EnableNonOfdTidRss", 2, 1 },
+ { "EnableNonOfdTcbRss", 1, 1 },
+ { "EnableOldRxForward", 0, 1 },
+ { "TP_TCP_BACKOFF_REG0", 0x350, 0 },
+ { "TimerBackoffIndex3", 24, 8 },
+ { "TimerBackoffIndex2", 16, 8 },
+ { "TimerBackoffIndex1", 8, 8 },
+ { "TimerBackoffIndex0", 0, 8 },
+ { "TP_TCP_BACKOFF_REG1", 0x354, 0 },
+ { "TimerBackoffIndex7", 24, 8 },
+ { "TimerBackoffIndex6", 16, 8 },
+ { "TimerBackoffIndex5", 8, 8 },
+ { "TimerBackoffIndex4", 0, 8 },
+ { "TP_TCP_BACKOFF_REG2", 0x358, 0 },
+ { "TimerBackoffIndex11", 24, 8 },
+ { "TimerBackoffIndex10", 16, 8 },
+ { "TimerBackoffIndex9", 8, 8 },
+ { "TimerBackoffIndex8", 0, 8 },
+ { "TP_TCP_BACKOFF_REG3", 0x35c, 0 },
+ { "TimerBackoffIndex15", 24, 8 },
+ { "TimerBackoffIndex14", 16, 8 },
+ { "TimerBackoffIndex13", 8, 8 },
+ { "TimerBackoffIndex12", 0, 8 },
+ { "TP_PARA_REG0", 0x360, 0 },
+ { "InitCwnd", 24, 3 },
+ { "DupAckThresh", 20, 4 },
+ { "TP_PARA_REG1", 0x364, 0 },
+ { "InitRwnd", 16, 16 },
+ { "InitialSSThresh", 0, 16 },
+ { "TP_PARA_REG2", 0x368, 0 },
+ { "MaxRxData", 16, 16 },
+ { "RxCoalesceSize", 0, 16 },
+ { "TP_PARA_REG3", 0x36c, 0 },
+ { "TunnelCngDrop1", 21, 1 },
+ { "TunnelCngDrop0", 20, 1 },
+ { "TxDataAckIdx", 16, 4 },
+ { "RxFragEnable", 12, 3 },
+ { "TxPaceFixedStrict", 11, 1 },
+ { "TxPaceAutoStrict", 10, 1 },
+ { "TxPaceFixed", 9, 1 },
+ { "TxPaceAuto", 8, 1 },
+ { "RxUrgTunnel", 6, 1 },
+ { "RxUrgMode", 5, 1 },
+ { "TxUrgMode", 4, 1 },
+ { "CngCtrlMode", 2, 2 },
+ { "RxCoalesceEnable", 1, 1 },
+ { "RxCoalescePshEn", 0, 1 },
+ { "TP_PARA_REG4", 0x370, 0 },
+ { "HighSpeedCfg", 24, 8 },
+ { "NewRenoCfg", 16, 8 },
+ { "TahoeCfg", 8, 8 },
+ { "RenoCfg", 0, 8 },
+ { "TP_PARA_REG5", 0x374, 0 },
+ { "IndicateSize", 16, 16 },
+ { "SchdEnable", 8, 1 },
+ { "OnFlyDDPEnable", 2, 1 },
+ { "DackTimerSpin", 1, 1 },
+ { "PushTimerEnable", 0, 1 },
+ { "TP_PARA_REG6", 0x378, 0 },
+ { "TxPDUSizeAdj", 16, 8 },
+ { "EnableDeferACK", 12, 1 },
+ { "EnableESnd", 11, 1 },
+ { "EnableCSnd", 10, 1 },
+ { "EnablePDUE", 9, 1 },
+ { "EnablePDUC", 8, 1 },
+ { "EnableBUFI", 7, 1 },
+ { "EnableBUFE", 6, 1 },
+ { "EnableDefer", 5, 1 },
+ { "EnableClearRxmtOos", 4, 1 },
+ { "DisablePDUCng", 3, 1 },
+ { "DisablePDUTimeout", 2, 1 },
+ { "DisablePDURxmt", 1, 1 },
+ { "DisablePDUxmt", 0, 1 },
+ { "TP_PARA_REG7", 0x37c, 0 },
+ { "PMMaxXferLen1", 16, 16 },
+ { "PMMaxXferLen0", 0, 16 },
+ { "TP_TIMER_RESOLUTION", 0x390, 0 },
+ { "TimerResolution", 16, 8 },
+ { "TimestampResolution", 8, 8 },
+ { "DelayedACKResolution", 0, 8 },
+ { "TP_MSL", 0x394, 0 },
+ { "MSL", 0, 30 },
+ { "TP_RXT_MIN", 0x398, 0 },
+ { "RxtMin", 0, 30 },
+ { "TP_RXT_MAX", 0x39c, 0 },
+ { "RxtMax", 0, 30 },
+ { "TP_PERS_MIN", 0x3a0, 0 },
+ { "PersMin", 0, 30 },
+ { "TP_PERS_MAX", 0x3a4, 0 },
+ { "PersMax", 0, 30 },
+ { "TP_KEEP_IDLE", 0x3a8, 0 },
+ { "KeepaliveIdle", 0, 30 },
+ { "TP_KEEP_INTVL", 0x3ac, 0 },
+ { "KeepaliveIntvl", 0, 30 },
+ { "TP_INIT_SRTT", 0x3b0, 0 },
+ { "InitSrtt", 0, 16 },
+ { "TP_DACK_TIMER", 0x3b4, 0 },
+ { "DackTime", 0, 12 },
+ { "TP_FINWAIT2_TIMER", 0x3b8, 0 },
+ { "Finwait2Time", 0, 30 },
+ { "TP_FAST_FINWAIT2_TIMER", 0x3bc, 0 },
+ { "FastFinwait2Time", 0, 30 },
+ { "TP_SHIFT_CNT", 0x3c0, 0 },
+ { "SynShiftMax", 24, 8 },
+ { "RxtShiftMaxR1", 20, 4 },
+ { "RxtShiftMaxR2", 16, 4 },
+ { "PerShiftBackoffMax", 12, 4 },
+ { "PerShiftMax", 8, 4 },
+ { "KeepaliveMax", 0, 8 },
+ { "TP_TIME_HI", 0x3c8, 0 },
+ { "TP_TIME_LO", 0x3cc, 0 },
+ { "TP_MTU_PORT_TABLE", 0x3d0, 0 },
+ { "Port1MTUValue", 16, 16 },
+ { "Port0MTUValue", 0, 16 },
+ { "TP_ULP_TABLE", 0x3d4, 0 },
+ { "ULPType7Field", 28, 4 },
+ { "ULPType6Field", 24, 4 },
+ { "ULPType5Field", 20, 4 },
+ { "ULPType4Field", 16, 4 },
+ { "ULPType3Field", 12, 4 },
+ { "ULPType2Field", 8, 4 },
+ { "ULPType1Field", 4, 4 },
+ { "ULPType0Field", 0, 4 },
+ { "TP_PACE_TABLE", 0x3d8, 0 },
+ { "TP_CCTRL_TABLE", 0x3dc, 0 },
+ { "TP_TOS_TABLE", 0x3e0, 0 },
+ { "TP_MTU_TABLE", 0x3e4, 0 },
+ { "TP_RSS_MAP_TABLE", 0x3e8, 0 },
+ { "TP_RSS_LKP_TABLE", 0x3ec, 0 },
+ { "TP_RSS_CONFIG", 0x3f0, 0 },
+ { "TNL4tupEn", 29, 1 },
+ { "TNL2tupEn", 28, 1 },
+ { "TNLprtEn", 26, 1 },
+ { "TNLMapEn", 25, 1 },
+ { "TNLLkpEn", 24, 1 },
+ { "OFD4tupEn", 21, 1 },
+ { "OFD2tupEn", 20, 1 },
+ { "OFDMapEn", 17, 1 },
+ { "OFDLkpEn", 16, 1 },
+ { "SYN4tupEn", 13, 1 },
+ { "SYN2tupEn", 12, 1 },
+ { "SYNMapEn", 9, 1 },
+ { "SYNLkpEn", 8, 1 },
+ { "RRCPLMapEn", 7, 1 },
+ { "RRCPLCPUSIZE", 4, 3 },
+ { "RQFeedbackEnable", 3, 1 },
+ { "HashToeplitz", 2, 1 },
+ { "HashSave", 1, 1 },
+ { "Disable", 0, 1 },
+ { "TP_RSS_CONFIG_TNL", 0x3f4, 0 },
+ { "MaskSize", 28, 3 },
+ { "DefaultCPUBase", 22, 6 },
+ { "DefaultCPU", 16, 6 },
+ { "DefaultQueue", 0, 16 },
+ { "TP_RSS_CONFIG_OFD", 0x3f8, 0 },
+ { "MaskSize", 28, 3 },
+ { "DefaultCPUBase", 22, 6 },
+ { "DefaultCPU", 16, 6 },
+ { "DefaultQueue", 0, 16 },
+ { "TP_RSS_CONFIG_SYN", 0x3fc, 0 },
+ { "MaskSize", 28, 3 },
+ { "DefaultCPUBase", 22, 6 },
+ { "DefaultCPU", 16, 6 },
+ { "DefaultQueue", 0, 16 },
+ { "TP_RSS_SECRET_KEY0", 0x400, 0 },
+ { "TP_RSS_SECRET_KEY1", 0x404, 0 },
+ { "TP_RSS_SECRET_KEY2", 0x408, 0 },
+ { "TP_RSS_SECRET_KEY3", 0x40c, 0 },
+ { "TP_TM_PIO_ADDR", 0x418, 0 },
+ { "TP_TM_PIO_DATA", 0x41c, 0 },
+ { "TP_TX_MOD_QUE_TABLE", 0x420, 0 },
+ { "TP_TX_RESOURCE_LIMIT", 0x424, 0 },
+ { "TX_RESOURCE_LIMIT_CH1_PC", 24, 8 },
+ { "TX_RESOURCE_LIMIT_CH1_NON_PC", 16, 8 },
+ { "TX_RESOURCE_LIMIT_CH0_PC", 8, 8 },
+ { "TX_RESOURCE_LIMIT_CH0_NON_PC", 0, 8 },
+ { "TP_TX_MOD_QUEUE_REQ_MAP", 0x428, 0 },
+ { "RX_MOD_WEIGHT", 24, 8 },
+ { "TX_MOD_WEIGHT", 16, 8 },
+ { "TX_MOD_TIMER_MODE", 8, 8 },
+ { "TX_MOD_QUEUE_REQ_MAP", 0, 8 },
+ { "TP_TX_MOD_QUEUE_WEIGHT1", 0x42c, 0 },
+ { "TP_TX_MOD_QUEUE_WEIGHT7", 24, 8 },
+ { "TP_TX_MOD_QUEUE_WEIGHT6", 16, 8 },
+ { "TP_TX_MOD_QUEUE_WEIGHT5", 8, 8 },
+ { "TP_TX_MOD_QUEUE_WEIGHT4", 0, 8 },
+ { "TP_TX_MOD_QUEUE_WEIGHT0", 0x430, 0 },
+ { "TP_TX_MOD_QUEUE_WEIGHT3", 24, 8 },
+ { "TP_TX_MOD_QUEUE_WEIGHT2", 16, 8 },
+ { "TP_TX_MOD_QUEUE_WEIGHT1", 8, 8 },
+ { "TP_TX_MOD_QUEUE_WEIGHT0", 0, 8 },
+ { "TP_MOD_CHANNEL_WEIGHT", 0x434, 0 },
+ { "RX_MOD_CHANNEL_WEIGHT1", 24, 8 },
+ { "RX_MOD_CHANNEL_WEIGHT0", 16, 8 },
+ { "TX_MOD_CHANNEL_WEIGHT1", 8, 8 },
+ { "TX_MOD_CHANNEL_WEIGHT0", 0, 8 },
+ { "TP_MOD_RATE_LIMIT", 0x438, 0 },
+ { "RX_MOD_RATE_LIMIT_INC", 24, 8 },
+ { "RX_MOD_RATE_LIMIT_TICK", 16, 8 },
+ { "TX_MOD_RATE_LIMIT_INC", 8, 8 },
+ { "TX_MOD_RATE_LIMIT_TICK", 0, 8 },
+ { "TP_PIO_ADDR", 0x440, 0 },
+ { "TP_PIO_DATA", 0x444, 0 },
+ { "TP_RESET", 0x44c, 0 },
+ { "FlstInitEnable", 1, 1 },
+ { "TPReset", 0, 1 },
+ { "TP_MIB_INDEX", 0x450, 0 },
+ { "TP_MIB_RDATA", 0x454, 0 },
+ { "TP_SYNC_TIME_HI", 0x458, 0 },
+ { "TP_SYNC_TIME_LO", 0x45c, 0 },
+ { "TP_CMM_MM_RX_FLST_BASE", 0x460, 0 },
+ { "CMRxFlstBase", 0, 28 },
+ { "TP_CMM_MM_TX_FLST_BASE", 0x464, 0 },
+ { "CMTxFlstBase", 0, 28 },
+ { "TP_CMM_MM_PS_FLST_BASE", 0x468, 0 },
+ { "CMPsFlstBase", 0, 28 },
+ { "TP_CMM_MM_MAX_PSTRUCT", 0x46c, 0 },
+ { "CMMaxPstruct", 0, 21 },
+ { "TP_INT_ENABLE", 0x470, 0 },
+ { "TP_INT_CAUSE", 0x474, 0 },
+ { "TP_FLM_FREE_PS_CNT", 0x480, 0 },
+ { "FreePstructCount", 0, 21 },
+ { "TP_FLM_FREE_RX_CNT", 0x484, 0 },
+ { "FreeRxPageCount", 0, 21 },
+ { "TP_FLM_FREE_TX_CNT", 0x488, 0 },
+ { "FreeTxPageCount", 0, 21 },
+ { "TP_TM_HEAP_PUSH_CNT", 0x48c, 0 },
+ { "TP_TM_HEAP_POP_CNT", 0x490, 0 },
+ { "TP_TM_DACK_PUSH_CNT", 0x494, 0 },
+ { "TP_TM_DACK_POP_CNT", 0x498, 0 },
+ { "TP_TM_MOD_PUSH_CNT", 0x49c, 0 },
+ { "TP_MOD_POP_CNT", 0x4a0, 0 },
+ { "TP_TIMER_SEPARATOR", 0x4a4, 0 },
+ { "TP_DEBUG_SEL", 0x4a8, 0 },
+ { "TP_DEBUG_FLAGS", 0x4ac, 0 },
+ { "RxTimerDackFirst", 26, 1 },
+ { "RxTimerDack", 25, 1 },
+ { "RxTimerHeartbeat", 24, 1 },
+ { "RxPawsDrop", 23, 1 },
+ { "RxUrgDataDrop", 22, 1 },
+ { "RxFutureData", 21, 1 },
+ { "RxRcvRxmData", 20, 1 },
+ { "RxRcvOooDataFin", 19, 1 },
+ { "RxRcvOooData", 18, 1 },
+ { "RxRcvWndZero", 17, 1 },
+ { "RxRcvWndLtMss", 16, 1 },
+ { "TxDupAckInc", 11, 1 },
+ { "TxRxmUrg", 10, 1 },
+ { "TxRxmFin", 9, 1 },
+ { "TxRxmSyn", 8, 1 },
+ { "TxRxmNewReno", 7, 1 },
+ { "TxRxmFast", 6, 1 },
+ { "TxRxmTimer", 5, 1 },
+ { "TxRxmTimerKeepalive", 4, 1 },
+ { "TxRxmTimerPersist", 3, 1 },
+ { "TxRcvAdvShrunk", 2, 1 },
+ { "TxRcvAdvZero", 1, 1 },
+ { "TxRcvAdvLtMss", 0, 1 },
+ { "TP_PROXY_FLOW_CNTL", 0x4b0, 0 },
+ { "TP_PC_CONGESTION_CNTL", 0x4b4, 0 },
+ { "EDropTunnel", 19, 1 },
+ { "CDropTunnel", 18, 1 },
+ { "EThreshold", 12, 6 },
+ { "CThreshold", 6, 6 },
+ { "TxThreshold", 0, 6 },
+ { "TP_TX_DROP_COUNT", 0x4bc, 0 },
+ { "TP_CLEAR_DEBUG", 0x4c0, 0 },
+ { "ClrDebug", 0, 1 },
+ { "TP_DEBUG_VEC", 0x4c4, 0 },
+ { "TP_DEBUG_VEC2", 0x4c8, 0 },
+ { "TP_DEBUG_REG_SEL", 0x4cc, 0 },
+ { "TP_DEBUG", 0x4d0, 0 },
+ { "TP_DBG_LA_CONFIG", 0x4d4, 0 },
+ { "TP_DBG_LA_DATAH", 0x4d8, 0 },
+ { "TP_DBG_LA_DATAL", 0x4dc, 0 },
+ { "TP_EMBED_OP_FIELD0", 0x4e8, 0 },
+ { "TP_EMBED_OP_FIELD1", 0x4ec, 0 },
+ { "TP_EMBED_OP_FIELD2", 0x4f0, 0 },
+ { "TP_EMBED_OP_FIELD3", 0x4f4, 0 },
+ { "TP_EMBED_OP_FIELD4", 0x4f8, 0 },
+ { "TP_EMBED_OP_FIELD5", 0x4fc, 0 },
+ { NULL }
+};
+
+struct reg_info t3b_ulp2_rx_regs[] = {
+ { "ULPRX_CTL", 0x500, 0 },
+ { "PCMD1Threshold", 24, 8 },
+ { "PCMD0Threshold", 16, 8 },
+ { "round_robin", 4, 1 },
+ { "RDMA_permissive_mode", 3, 1 },
+ { "PagePodME", 2, 1 },
+ { "IscsiTagTcb", 1, 1 },
+ { "TddpTagTcb", 0, 1 },
+ { "ULPRX_INT_ENABLE", 0x504, 0 },
+ { "ParErr", 0, 1 },
+ { "ULPRX_INT_CAUSE", 0x508, 0 },
+ { "ParErr", 0, 1 },
+ { "ULPRX_ISCSI_LLIMIT", 0x50c, 0 },
+ { "IscsiLlimit", 6, 26 },
+ { "ULPRX_ISCSI_ULIMIT", 0x510, 0 },
+ { "IscsiUlimit", 6, 26 },
+ { "ULPRX_ISCSI_TAGMASK", 0x514, 0 },
+ { "IscsiTagMask", 6, 26 },
+ { "ULPRX_ISCSI_PSZ", 0x518, 0 },
+ { "Hpz3", 24, 4 },
+ { "Hpz2", 16, 4 },
+ { "Hpz1", 8, 4 },
+ { "Hpz0", 0, 4 },
+ { "ULPRX_TDDP_LLIMIT", 0x51c, 0 },
+ { "TddpLlimit", 6, 26 },
+ { "ULPRX_TDDP_ULIMIT", 0x520, 0 },
+ { "TddpUlimit", 6, 26 },
+ { "ULPRX_TDDP_TAGMASK", 0x524, 0 },
+ { "TddpTagMask", 6, 26 },
+ { "ULPRX_TDDP_PSZ", 0x528, 0 },
+ { "Hpz3", 24, 4 },
+ { "Hpz2", 16, 4 },
+ { "Hpz1", 8, 4 },
+ { "Hpz0", 0, 4 },
+ { "ULPRX_STAG_LLIMIT", 0x52c, 0 },
+ { "ULPRX_STAG_ULIMIT", 0x530, 0 },
+ { "ULPRX_RQ_LLIMIT", 0x534, 0 },
+ { "ULPRX_RQ_ULIMIT", 0x538, 0 },
+ { "ULPRX_PBL_LLIMIT", 0x53c, 0 },
+ { "ULPRX_PBL_ULIMIT", 0x540, 0 },
+ { NULL }
+};
+
+struct reg_info t3b_ulp2_tx_regs[] = {
+ { "ULPTX_CONFIG", 0x580, 0 },
+ { "CFG_RR_ARB", 0, 1 },
+ { "ULPTX_INT_ENABLE", 0x584, 0 },
+ { "Pbl_bound_err_ch1", 1, 1 },
+ { "Pbl_bound_err_ch0", 0, 1 },
+ { "ULPTX_INT_CAUSE", 0x588, 0 },
+ { "Pbl_bound_err_ch1", 1, 1 },
+ { "Pbl_bound_err_ch0", 0, 1 },
+ { "ULPTX_TPT_LLIMIT", 0x58c, 0 },
+ { "ULPTX_TPT_ULIMIT", 0x590, 0 },
+ { "ULPTX_PBL_LLIMIT", 0x594, 0 },
+ { "ULPTX_PBL_ULIMIT", 0x598, 0 },
+ { "ULPTX_CPL_ERR_OFFSET", 0x59c, 0 },
+ { "ULPTX_CPL_ERR_MASK", 0x5a0, 0 },
+ { "ULPTX_CPL_ERR_VALUE", 0x5a4, 0 },
+ { "ULPTX_CPL_PACK_SIZE", 0x5a8, 0 },
+ { "value", 24, 8 },
+ { "Ch1Size2", 24, 8 },
+ { "Ch1Size1", 16, 8 },
+ { "Ch0Size2", 8, 8 },
+ { "Ch0Size1", 0, 8 },
+ { "ULPTX_DMA_WEIGHT", 0x5ac, 0 },
+ { "D1_WEIGHT", 16, 16 },
+ { "D0_WEIGHT", 0, 16 },
+ { NULL }
+};
+
+struct reg_info t3b_pm1_rx_regs[] = {
+ { "PM1_RX_CFG", 0x5c0, 0 },
+ { "PM1_RX_MODE", 0x5c4, 0 },
+ { "stat_channel", 1, 1 },
+ { "priority_ch", 0, 1 },
+ { "PM1_RX_STAT_CONFIG", 0x5c8, 0 },
+ { "PM1_RX_STAT_COUNT", 0x5cc, 0 },
+ { "PM1_RX_STAT_MSB", 0x5d0, 0 },
+ { "PM1_RX_STAT_LSB", 0x5d4, 0 },
+ { "PM1_RX_INT_ENABLE", 0x5d8, 0 },
+ { "zero_e_cmd_error", 18, 1 },
+ { "iespi0_fifo2x_Rx_framing_error", 17, 1 },
+ { "iespi1_fifo2x_Rx_framing_error", 16, 1 },
+ { "iespi0_Rx_framing_error", 15, 1 },
+ { "iespi1_Rx_framing_error", 14, 1 },
+ { "iespi0_Tx_framing_error", 13, 1 },
+ { "iespi1_Tx_framing_error", 12, 1 },
+ { "ocspi0_Rx_framing_error", 11, 1 },
+ { "ocspi1_Rx_framing_error", 10, 1 },
+ { "ocspi0_Tx_framing_error", 9, 1 },
+ { "ocspi1_Tx_framing_error", 8, 1 },
+ { "ocspi0_ofifo2x_Tx_framing_error", 7, 1 },
+ { "ocspi1_ofifo2x_Tx_framing_error", 6, 1 },
+ { "iespi_par_error", 3, 3 },
+ { "ocspi_par_error", 0, 3 },
+ { "PM1_RX_INT_CAUSE", 0x5dc, 0 },
+ { "zero_e_cmd_error", 18, 1 },
+ { "iespi0_fifo2x_Rx_framing_error", 17, 1 },
+ { "iespi1_fifo2x_Rx_framing_error", 16, 1 },
+ { "iespi0_Rx_framing_error", 15, 1 },
+ { "iespi1_Rx_framing_error", 14, 1 },
+ { "iespi0_Tx_framing_error", 13, 1 },
+ { "iespi1_Tx_framing_error", 12, 1 },
+ { "ocspi0_Rx_framing_error", 11, 1 },
+ { "ocspi1_Rx_framing_error", 10, 1 },
+ { "ocspi0_Tx_framing_error", 9, 1 },
+ { "ocspi1_Tx_framing_error", 8, 1 },
+ { "ocspi0_ofifo2x_Tx_framing_error", 7, 1 },
+ { "ocspi1_ofifo2x_Tx_framing_error", 6, 1 },
+ { "iespi_par_error", 3, 3 },
+ { "ocspi_par_error", 0, 3 },
+ { NULL }
+};
+
+struct reg_info t3b_pm1_tx_regs[] = {
+ { "PM1_TX_CFG", 0x5e0, 0 },
+ { "PM1_TX_MODE", 0x5e4, 0 },
+ { "stat_channel", 1, 1 },
+ { "priority_ch", 0, 1 },
+ { "PM1_TX_STAT_CONFIG", 0x5e8, 0 },
+ { "PM1_TX_STAT_COUNT", 0x5ec, 0 },
+ { "PM1_TX_STAT_MSB", 0x5f0, 0 },
+ { "PM1_TX_STAT_LSB", 0x5f4, 0 },
+ { "PM1_TX_INT_ENABLE", 0x5f8, 0 },
+ { "zero_c_cmd_error", 18, 1 },
+ { "icspi0_fifo2x_Rx_framing_error", 17, 1 },
+ { "icspi1_fifo2x_Rx_framing_error", 16, 1 },
+ { "icspi0_Rx_framing_error", 15, 1 },
+ { "icspi1_Rx_framing_error", 14, 1 },
+ { "icspi0_Tx_framing_error", 13, 1 },
+ { "icspi1_Tx_framing_error", 12, 1 },
+ { "oespi0_Rx_framing_error", 11, 1 },
+ { "oespi1_Rx_framing_error", 10, 1 },
+ { "oespi0_Tx_framing_error", 9, 1 },
+ { "oespi1_Tx_framing_error", 8, 1 },
+ { "oespi0_ofifo2x_Tx_framing_error", 7, 1 },
+ { "oespi1_ofifo2x_Tx_framing_error", 6, 1 },
+ { "icspi_par_error", 3, 3 },
+ { "oespi_par_error", 0, 3 },
+ { "PM1_TX_INT_CAUSE", 0x5fc, 0 },
+ { "zero_c_cmd_error", 18, 1 },
+ { "icspi0_fifo2x_Rx_framing_error", 17, 1 },
+ { "icspi1_fifo2x_Rx_framing_error", 16, 1 },
+ { "icspi0_Rx_framing_error", 15, 1 },
+ { "icspi1_Rx_framing_error", 14, 1 },
+ { "icspi0_Tx_framing_error", 13, 1 },
+ { "icspi1_Tx_framing_error", 12, 1 },
+ { "oespi0_Rx_framing_error", 11, 1 },
+ { "oespi1_Rx_framing_error", 10, 1 },
+ { "oespi0_Tx_framing_error", 9, 1 },
+ { "oespi1_Tx_framing_error", 8, 1 },
+ { "oespi0_ofifo2x_Tx_framing_error", 7, 1 },
+ { "oespi1_ofifo2x_Tx_framing_error", 6, 1 },
+ { "icspi_par_error", 3, 3 },
+ { "oespi_par_error", 0, 3 },
+ { NULL }
+};
+
+struct reg_info t3b_mps0_regs[] = {
+ { "MPS_CFG", 0x600, 0 },
+ { "EnForcePkt", 11, 1 },
+ { "SGETPQid", 8, 3 },
+ { "TPRxPortSize", 7, 1 },
+ { "TPTxPort1Size", 6, 1 },
+ { "TPTxPort0Size", 5, 1 },
+ { "TPRxPortEn", 4, 1 },
+ { "TPTxPort1En", 3, 1 },
+ { "TPTxPort0En", 2, 1 },
+ { "Port1Active", 1, 1 },
+ { "Port0Active", 0, 1 },
+ { "MPS_DRR_CFG1", 0x604, 0 },
+ { "RldWtTPD1", 11, 11 },
+ { "RldWtTPD0", 0, 11 },
+ { "MPS_DRR_CFG2", 0x608, 0 },
+ { "RldWtTotal", 0, 12 },
+ { "MPS_MCA_STATUS", 0x60c, 0 },
+ { "MCAPktCnt", 12, 20 },
+ { "MCADepth", 0, 12 },
+ { "MPS_TX0_TP_CNT", 0x610, 0 },
+ { "TX0TPDisCnt", 24, 8 },
+ { "TX0TPCnt", 0, 24 },
+ { "MPS_TX1_TP_CNT", 0x614, 0 },
+ { "TX1TPDisCnt", 24, 8 },
+ { "TX1TPCnt", 0, 24 },
+ { "MPS_RX_TP_CNT", 0x618, 0 },
+ { "RXTPDisCnt", 24, 8 },
+ { "RXTPCnt", 0, 24 },
+ { "MPS_INT_ENABLE", 0x61c, 0 },
+ { "MCAParErrEnb", 6, 3 },
+ { "RXTpParErrEnb", 4, 2 },
+ { "TX1TpParErrEnb", 2, 2 },
+ { "TX0TpParErrEnb", 0, 2 },
+ { "MPS_INT_CAUSE", 0x620, 0 },
+ { "MCAParErr", 6, 3 },
+ { "RXTpParErr", 4, 2 },
+ { "TX1TpParErr", 2, 2 },
+ { "TX0TpParErr", 0, 2 },
+ { NULL }
+};
+
+struct reg_info t3b_cpl_switch_regs[] = {
+ { "CPL_SWITCH_CNTRL", 0x640, 0 },
+ { "cpl_pkt_tid", 8, 24 },
+ { "cpu_no_3F_CIM_enable", 3, 1 },
+ { "switch_table_enable", 2, 1 },
+ { "sge_enable", 1, 1 },
+ { "cim_enable", 0, 1 },
+ { "CPL_SWITCH_TBL_IDX", 0x644, 0 },
+ { "switch_tbl_idx", 0, 4 },
+ { "CPL_SWITCH_TBL_DATA", 0x648, 0 },
+ { "CPL_SWITCH_ZERO_ERROR", 0x64c, 0 },
+ { "zero_cmd", 0, 8 },
+ { "CPL_INTR_ENABLE", 0x650, 0 },
+ { "cim_ovfl_error", 4, 1 },
+ { "tp_framing_error", 3, 1 },
+ { "sge_framing_error", 2, 1 },
+ { "cim_framing_error", 1, 1 },
+ { "zero_switch_error", 0, 1 },
+ { "CPL_INTR_CAUSE", 0x654, 0 },
+ { "cim_ovfl_error", 4, 1 },
+ { "tp_framing_error", 3, 1 },
+ { "sge_framing_error", 2, 1 },
+ { "cim_framing_error", 1, 1 },
+ { "zero_switch_error", 0, 1 },
+ { "CPL_MAP_TBL_IDX", 0x658, 0 },
+ { "cpl_map_tbl_idx", 0, 8 },
+ { "CPL_MAP_TBL_DATA", 0x65c, 0 },
+ { "cpl_map_tbl_data", 0, 8 },
+ { NULL }
+};
+
+struct reg_info t3b_smb0_regs[] = {
+ { "SMB_GLOBAL_TIME_CFG", 0x660, 0 },
+ { "LADbgWrPtr", 24, 8 },
+ { "LADbgRdPtr", 16, 8 },
+ { "LADbgEn", 13, 1 },
+ { "MacroCntCfg", 8, 5 },
+ { "MicroCntCfg", 0, 8 },
+ { "SMB_MST_TIMEOUT_CFG", 0x664, 0 },
+ { "DebugSelH", 28, 4 },
+ { "DebugSelL", 24, 4 },
+ { "MstTimeOutCfg", 0, 24 },
+ { "SMB_MST_CTL_CFG", 0x668, 0 },
+ { "MstFifoDbg", 31, 1 },
+ { "MstFifoDbgClr", 30, 1 },
+ { "MstRxByteCfg", 12, 6 },
+ { "MstTxByteCfg", 6, 6 },
+ { "MstReset", 1, 1 },
+ { "MstCtlEn", 0, 1 },
+ { "SMB_MST_CTL_STS", 0x66c, 0 },
+ { "MstRxByteCnt", 12, 6 },
+ { "MstTxByteCnt", 6, 6 },
+ { "MstBusySts", 0, 1 },
+ { "SMB_MST_TX_FIFO_RDWR", 0x670, 0 },
+ { "SMB_MST_RX_FIFO_RDWR", 0x674, 0 },
+ { "SMB_SLV_TIMEOUT_CFG", 0x678, 0 },
+ { "SlvTimeOutCfg", 0, 24 },
+ { "SMB_SLV_CTL_CFG", 0x67c, 0 },
+ { "SlvFifoDbg", 31, 1 },
+ { "SlvFifoDbgClr", 30, 1 },
+ { "SlvAddrCfg", 4, 7 },
+ { "SlvAlrtSet", 2, 1 },
+ { "SlvReset", 1, 1 },
+ { "SlvCtlEn", 0, 1 },
+ { "SMB_SLV_CTL_STS", 0x680, 0 },
+ { "SlvFifoTxCnt", 12, 6 },
+ { "SlvFifoCnt", 6, 6 },
+ { "SlvAlrtSts", 2, 1 },
+ { "SlvBusySts", 0, 1 },
+ { "SMB_SLV_FIFO_RDWR", 0x684, 0 },
+ { "SMB_SLV_CMD_FIFO_RDWR", 0x688, 0 },
+ { "SMB_INT_ENABLE", 0x68c, 0 },
+ { "SlvTimeOutIntEn", 7, 1 },
+ { "SlvErrIntEn", 6, 1 },
+ { "SlvDoneIntEn", 5, 1 },
+ { "SlvRxRdyIntEn", 4, 1 },
+ { "MstTimeOutIntEn", 3, 1 },
+ { "MstNAckIntEn", 2, 1 },
+ { "MstLostArbIntEn", 1, 1 },
+ { "MstDoneIntEn", 0, 1 },
+ { "SMB_INT_CAUSE", 0x690, 0 },
+ { "SlvTimeOutInt", 7, 1 },
+ { "SlvErrInt", 6, 1 },
+ { "SlvDoneInt", 5, 1 },
+ { "SlvRxRdyInt", 4, 1 },
+ { "MstTimeOutInt", 3, 1 },
+ { "MstNAckInt", 2, 1 },
+ { "MstLostArbInt", 1, 1 },
+ { "MstDoneInt", 0, 1 },
+ { "SMB_DEBUG_DATA", 0x694, 0 },
+ { "DebugDataH", 16, 16 },
+ { "DebugDataL", 0, 16 },
+ { "SMB_DEBUG_LA", 0x69c, 0 },
+ { "DebugLAReqAddr", 0, 10 },
+ { NULL }
+};
+
+struct reg_info t3b_i2cm0_regs[] = {
+ { "I2C_CFG", 0x6a0, 0 },
+ { "ClkDiv", 0, 12 },
+ { "I2C_DATA", 0x6a4, 0 },
+ { "Data", 0, 8 },
+ { "I2C_OP", 0x6a8, 0 },
+ { "Busy", 31, 1 },
+ { "Ack", 30, 1 },
+ { "Cont", 1, 1 },
+ { "Op", 0, 1 },
+ { NULL }
+};
+
+struct reg_info t3b_mi1_regs[] = {
+ { "MI1_CFG", 0x6b0, 0 },
+ { "ClkDiv", 5, 8 },
+ { "St", 3, 2 },
+ { "PreEn", 2, 1 },
+ { "MDIInv", 1, 1 },
+ { "MDIEn", 0, 1 },
+ { "MI1_ADDR", 0x6b4, 0 },
+ { "PhyAddr", 5, 5 },
+ { "RegAddr", 0, 5 },
+ { "MI1_DATA", 0x6b8, 0 },
+ { "Data", 0, 16 },
+ { "MI1_OP", 0x6bc, 0 },
+ { "Busy", 31, 1 },
+ { "Inc", 2, 1 },
+ { "Op", 0, 2 },
+ { NULL }
+};
+
+struct reg_info t3b_jm1_regs[] = {
+ { "JM_CFG", 0x6c0, 0 },
+ { "ClkDiv", 2, 8 },
+ { "TRst", 1, 1 },
+ { "En", 0, 1 },
+ { "JM_MODE", 0x6c4, 0 },
+ { "JM_DATA", 0x6c8, 0 },
+ { "JM_OP", 0x6cc, 0 },
+ { "Busy", 31, 1 },
+ { "Cnt", 0, 5 },
+ { NULL }
+};
+
+struct reg_info t3b_sf1_regs[] = {
+ { "SF_DATA", 0x6d8, 0 },
+ { "SF_OP", 0x6dc, 0 },
+ { "Busy", 31, 1 },
+ { "Cont", 3, 1 },
+ { "ByteCnt", 1, 2 },
+ { "Op", 0, 1 },
+ { NULL }
+};
+
+struct reg_info t3b_pl3_regs[] = {
+ { "PL_INT_ENABLE0", 0x6e0, 0 },
+ { "SW", 25, 1 },
+ { "EXT", 24, 1 },
+ { "T3DBG", 23, 1 },
+ { "XGMAC0_1", 20, 1 },
+ { "XGMAC0_0", 19, 1 },
+ { "MC5A", 18, 1 },
+ { "SF1", 17, 1 },
+ { "SMB0", 15, 1 },
+ { "I2CM0", 14, 1 },
+ { "MI1", 13, 1 },
+ { "CPL_SWITCH", 12, 1 },
+ { "MPS0", 11, 1 },
+ { "PM1_TX", 10, 1 },
+ { "PM1_RX", 9, 1 },
+ { "ULP2_TX", 8, 1 },
+ { "ULP2_RX", 7, 1 },
+ { "TP1", 6, 1 },
+ { "CIM", 5, 1 },
+ { "MC7_CM", 4, 1 },
+ { "MC7_PMTX", 3, 1 },
+ { "MC7_PMRX", 2, 1 },
+ { "PCIM0", 1, 1 },
+ { "SGE3", 0, 1 },
+ { "PL_INT_CAUSE0", 0x6e4, 0 },
+ { "SW", 25, 1 },
+ { "EXT", 24, 1 },
+ { "T3DBG", 23, 1 },
+ { "XGMAC0_1", 20, 1 },
+ { "XGMAC0_0", 19, 1 },
+ { "MC5A", 18, 1 },
+ { "SF1", 17, 1 },
+ { "SMB0", 15, 1 },
+ { "I2CM0", 14, 1 },
+ { "MI1", 13, 1 },
+ { "CPL_SWITCH", 12, 1 },
+ { "MPS0", 11, 1 },
+ { "PM1_TX", 10, 1 },
+ { "PM1_RX", 9, 1 },
+ { "ULP2_TX", 8, 1 },
+ { "ULP2_RX", 7, 1 },
+ { "TP1", 6, 1 },
+ { "CIM", 5, 1 },
+ { "MC7_CM", 4, 1 },
+ { "MC7_PMTX", 3, 1 },
+ { "MC7_PMRX", 2, 1 },
+ { "PCIM0", 1, 1 },
+ { "SGE3", 0, 1 },
+ { "PL_INT_ENABLE1", 0x6e8, 0 },
+ { "SW", 25, 1 },
+ { "EXT", 24, 1 },
+ { "T3DBG", 23, 1 },
+ { "XGMAC0_1", 20, 1 },
+ { "XGMAC0_0", 19, 1 },
+ { "MC5A", 18, 1 },
+ { "SF1", 17, 1 },
+ { "SMB0", 15, 1 },
+ { "I2CM0", 14, 1 },
+ { "MI1", 13, 1 },
+ { "CPL_SWITCH", 12, 1 },
+ { "MPS0", 11, 1 },
+ { "PM1_TX", 10, 1 },
+ { "PM1_RX", 9, 1 },
+ { "ULP2_TX", 8, 1 },
+ { "ULP2_RX", 7, 1 },
+ { "TP1", 6, 1 },
+ { "CIM", 5, 1 },
+ { "MC7_CM", 4, 1 },
+ { "MC7_PMTX", 3, 1 },
+ { "MC7_PMRX", 2, 1 },
+ { "PCIM0", 1, 1 },
+ { "SGE3", 0, 1 },
+ { "PL_INT_CAUSE1", 0x6ec, 0 },
+ { "SW", 25, 1 },
+ { "EXT", 24, 1 },
+ { "T3DBG", 23, 1 },
+ { "XGMAC0_1", 20, 1 },
+ { "XGMAC0_0", 19, 1 },
+ { "MC5A", 18, 1 },
+ { "SF1", 17, 1 },
+ { "SMB0", 15, 1 },
+ { "I2CM0", 14, 1 },
+ { "MI1", 13, 1 },
+ { "CPL_SWITCH", 12, 1 },
+ { "MPS0", 11, 1 },
+ { "PM1_TX", 10, 1 },
+ { "PM1_RX", 9, 1 },
+ { "ULP2_TX", 8, 1 },
+ { "ULP2_RX", 7, 1 },
+ { "TP1", 6, 1 },
+ { "CIM", 5, 1 },
+ { "MC7_CM", 4, 1 },
+ { "MC7_PMTX", 3, 1 },
+ { "MC7_PMRX", 2, 1 },
+ { "PCIM0", 1, 1 },
+ { "SGE3", 0, 1 },
+ { "PL_RST", 0x6f0, 0 },
+ { "SWInt1", 3, 1 },
+ { "SWInt0", 2, 1 },
+ { "CRstWrm", 1, 1 },
+ { "CRstWrmMode", 0, 1 },
+ { "PL_REV", 0x6f4, 0 },
+ { "Rev", 0, 4 },
+ { "PL_CLI", 0x6f8, 0 },
+ { "PL_LCK", 0x6fc, 0 },
+ { "Lck", 0, 2 },
+ { NULL }
+};
+
+struct reg_info t3b_mc5a_regs[] = {
+ { "MC5_BUF_CONFIG", 0x700, 0 },
+ { "term300_240", 31, 1 },
+ { "term150", 30, 1 },
+ { "term60", 29, 1 },
+ { "gddriii", 28, 1 },
+ { "gddrii", 27, 1 },
+ { "gddri", 26, 1 },
+ { "read", 25, 1 },
+ { "imp_set_update", 24, 1 },
+ { "cal_update", 23, 1 },
+ { "cal_busy", 22, 1 },
+ { "cal_error", 21, 1 },
+ { "sgl_cal_en", 20, 1 },
+ { "imp_upd_mode", 19, 1 },
+ { "imp_sel", 18, 1 },
+ { "man_pu", 15, 3 },
+ { "man_pd", 12, 3 },
+ { "cal_pu", 9, 3 },
+ { "cal_pd", 6, 3 },
+ { "set_pu", 3, 3 },
+ { "set_pd", 0, 3 },
+ { "MC5_DB_CONFIG", 0x704, 0 },
+ { "TMCfgWrLock", 31, 1 },
+ { "TMTypeHi", 30, 1 },
+ { "TMPartSize", 28, 2 },
+ { "TMType", 26, 2 },
+ { "TMPartCount", 24, 2 },
+ { "nLIP", 18, 6 },
+ { "COMPEN", 17, 1 },
+ { "BUILD", 16, 1 },
+ { "FilterEn", 11, 1 },
+ { "CLIPUpdate", 10, 1 },
+ { "TM_IO_PDOWN", 9, 1 },
+ { "SYNMode", 7, 2 },
+ { "PRTYEN", 6, 1 },
+ { "MBUSEN", 5, 1 },
+ { "DBGIEN", 4, 1 },
+ { "TcmCfgOvr", 3, 1 },
+ { "TMRDY", 2, 1 },
+ { "TMRST", 1, 1 },
+ { "TMMode", 0, 1 },
+ { "MC5_MISC", 0x708, 0 },
+ { "LIP_Cmp_Unavailable", 0, 4 },
+ { "MC5_DB_ROUTING_TABLE_INDEX", 0x70c, 0 },
+ { "RTINDX", 0, 22 },
+ { "MC5_DB_FILTER_TABLE", 0x710, 0 },
+ { "SRINDX", 0, 22 },
+ { "MC5_DB_SERVER_INDEX", 0x714, 0 },
+ { "SRINDX", 0, 22 },
+ { "MC5_DB_LIP_RAM_ADDR", 0x718, 0 },
+ { "RAMWR", 8, 1 },
+ { "RAMADDR", 0, 6 },
+ { "MC5_DB_LIP_RAM_DATA", 0x71c, 0 },
+ { "MC5_DB_RSP_LATENCY", 0x720, 0 },
+ { "RDLAT", 16, 5 },
+ { "LRNLAT", 8, 5 },
+ { "SRCHLAT", 0, 5 },
+ { "MC5_DB_PARITY_LATENCY", 0x724, 0 },
+ { "PARLAT", 0, 4 },
+ { "MC5_DB_WR_LRN_VERIFY", 0x728, 0 },
+ { "VWVEREN", 2, 1 },
+ { "LRNVEREN", 1, 1 },
+ { "POVEREN", 0, 1 },
+ { "MC5_DB_PART_ID_INDEX", 0x72c, 0 },
+ { "IDINDEX", 0, 4 },
+ { "MC5_DB_RESET_MAX", 0x730, 0 },
+ { "RSTMAX", 0, 4 },
+ { "MC5_DB_ACT_CNT", 0x734, 0 },
+ { "ACTCNT", 0, 20 },
+ { "MC5_DB_CLIP_MAP", 0x738, 0 },
+ { "CLIPMapOp", 31, 1 },
+ { "CLIPMapVal", 16, 6 },
+ { "CLIPMapAddr", 0, 6 },
+ { "MC5_DB_INT_ENABLE", 0x740, 0 },
+ { "MsgSel", 28, 4 },
+ { "DelActEmpty", 18, 1 },
+ { "DispQParErr", 17, 1 },
+ { "ReqQParErr", 16, 1 },
+ { "UnknownCmd", 15, 1 },
+ { "SYNCookieOff", 11, 1 },
+ { "SYNCookieBad", 10, 1 },
+ { "SYNCookie", 9, 1 },
+ { "NFASrchFail", 8, 1 },
+ { "ActRgnFull", 7, 1 },
+ { "ParityErr", 6, 1 },
+ { "LIPMiss", 5, 1 },
+ { "LIP0", 4, 1 },
+ { "Miss", 3, 1 },
+ { "RoutingHit", 2, 1 },
+ { "ActiveHit", 1, 1 },
+ { "ActiveOutHit", 0, 1 },
+ { "MC5_DB_INT_CAUSE", 0x744, 0 },
+ { "DelActEmpty", 18, 1 },
+ { "DispQParErr", 17, 1 },
+ { "ReqQParErr", 16, 1 },
+ { "UnknownCmd", 15, 1 },
+ { "SYNCookieOff", 11, 1 },
+ { "SYNCookieBad", 10, 1 },
+ { "SYNCookie", 9, 1 },
+ { "NFASrchFail", 8, 1 },
+ { "ActRgnFull", 7, 1 },
+ { "ParityErr", 6, 1 },
+ { "LIPMiss", 5, 1 },
+ { "LIP0", 4, 1 },
+ { "Miss", 3, 1 },
+ { "RoutingHit", 2, 1 },
+ { "ActiveHit", 1, 1 },
+ { "ActiveOutHit", 0, 1 },
+ { "MC5_DB_INT_TID", 0x748, 0 },
+ { "INTTID", 0, 20 },
+ { "MC5_DB_INT_PTID", 0x74c, 0 },
+ { "INTPTID", 0, 20 },
+ { "MC5_DB_DBGI_CONFIG", 0x774, 0 },
+ { "WRReqSize", 22, 10 },
+ { "SADRSel", 4, 1 },
+ { "CMDMode", 0, 3 },
+ { "MC5_DB_DBGI_REQ_CMD", 0x778, 0 },
+ { "MBusCmd", 0, 4 },
+ { "IDTCmdHi", 11, 3 },
+ { "IDTCmdLo", 0, 4 },
+ { "IDTCmd", 0, 20 },
+ { "LCMDB", 16, 11 },
+ { "LCMDA", 0, 11 },
+ { "MC5_DB_DBGI_REQ_ADDR0", 0x77c, 0 },
+ { "MC5_DB_DBGI_REQ_ADDR1", 0x780, 0 },
+ { "MC5_DB_DBGI_REQ_ADDR2", 0x784, 0 },
+ { "DBGIReqAdrHi", 0, 8 },
+ { "MC5_DB_DBGI_REQ_DATA0", 0x788, 0 },
+ { "MC5_DB_DBGI_REQ_DATA1", 0x78c, 0 },
+ { "MC5_DB_DBGI_REQ_DATA2", 0x790, 0 },
+ { "MC5_DB_DBGI_REQ_DATA3", 0x794, 0 },
+ { "MC5_DB_DBGI_REQ_DATA4", 0x798, 0 },
+ { "DBGIReqData4", 0, 16 },
+ { "MC5_DB_DBGI_REQ_MASK0", 0x79c, 0 },
+ { "MC5_DB_DBGI_REQ_MASK1", 0x7a0, 0 },
+ { "MC5_DB_DBGI_REQ_MASK2", 0x7a4, 0 },
+ { "MC5_DB_DBGI_REQ_MASK3", 0x7a8, 0 },
+ { "MC5_DB_DBGI_REQ_MASK4", 0x7ac, 0 },
+ { "DBGIReqMsk4", 0, 16 },
+ { "MC5_DB_DBGI_RSP_STATUS", 0x7b0, 0 },
+ { "DBGIRspMsg", 8, 4 },
+ { "DBGIRspMsgVld", 2, 1 },
+ { "DBGIRspHit", 1, 1 },
+ { "DBGIRspValid", 0, 1 },
+ { "MC5_DB_DBGI_RSP_DATA0", 0x7b4, 0 },
+ { "MC5_DB_DBGI_RSP_DATA1", 0x7b8, 0 },
+ { "MC5_DB_DBGI_RSP_DATA2", 0x7bc, 0 },
+ { "MC5_DB_DBGI_RSP_DATA3", 0x7c0, 0 },
+ { "MC5_DB_DBGI_RSP_DATA4", 0x7c4, 0 },
+ { "DBGIRspData3", 0, 16 },
+ { "MC5_DB_DBGI_RSP_LAST_CMD", 0x7c8, 0 },
+ { "LastCmdB", 16, 11 },
+ { "LastCmdA", 0, 11 },
+ { "MC5_DB_POPEN_DATA_WR_CMD", 0x7cc, 0 },
+ { "PO_DWR", 0, 20 },
+ { "MC5_DB_POPEN_MASK_WR_CMD", 0x7d0, 0 },
+ { "PO_MWR", 0, 20 },
+ { "MC5_DB_AOPEN_SRCH_CMD", 0x7d4, 0 },
+ { "AO_SRCH", 0, 20 },
+ { "MC5_DB_AOPEN_LRN_CMD", 0x7d8, 0 },
+ { "AO_LRN", 0, 20 },
+ { "MC5_DB_SYN_SRCH_CMD", 0x7dc, 0 },
+ { "SYN_SRCH", 0, 20 },
+ { "MC5_DB_SYN_LRN_CMD", 0x7e0, 0 },
+ { "SYN_LRN", 0, 20 },
+ { "MC5_DB_ACK_SRCH_CMD", 0x7e4, 0 },
+ { "ACK_SRCH", 0, 20 },
+ { "MC5_DB_ACK_LRN_CMD", 0x7e8, 0 },
+ { "ACK_LRN", 0, 20 },
+ { "MC5_DB_ILOOKUP_CMD", 0x7ec, 0 },
+ { "I_SRCH", 0, 20 },
+ { "MC5_DB_ELOOKUP_CMD", 0x7f0, 0 },
+ { "E_SRCH", 0, 20 },
+ { "MC5_DB_DATA_WRITE_CMD", 0x7f4, 0 },
+ { "Write", 0, 20 },
+ { "MC5_DB_DATA_READ_CMD", 0x7f8, 0 },
+ { "ReadCmd", 0, 20 },
+ { "MC5_DB_MASK_WRITE_CMD", 0x7fc, 0 },
+ { "MaskWr", 0, 16 },
+ { NULL }
+};
+
+struct reg_info t3b_xgmac0_0_regs[] = {
+ { "XGM_TX_CTRL", 0x800, 0 },
+ { "SendPause", 2, 1 },
+ { "SendZeroPause", 1, 1 },
+ { "TxEn", 0, 1 },
+ { "XGM_TX_CFG", 0x804, 0 },
+ { "CfgClkSpeed", 2, 3 },
+ { "StretchMode", 1, 1 },
+ { "TxPauseEn", 0, 1 },
+ { "XGM_TX_PAUSE_QUANTA", 0x808, 0 },
+ { "TxPauseQuanta", 0, 16 },
+ { "XGM_RX_CTRL", 0x80c, 0 },
+ { "RxEn", 0, 1 },
+ { "XGM_RX_CFG", 0x810, 0 },
+ { "Con802_3Preamble", 12, 1 },
+ { "EnNon802_3Preamble", 11, 1 },
+ { "CopyPreamble", 10, 1 },
+ { "DisPauseFrames", 9, 1 },
+ { "En1536BFrames", 8, 1 },
+ { "EnJumbo", 7, 1 },
+ { "RmFCS", 6, 1 },
+ { "DisNonVlan", 5, 1 },
+ { "EnExtMatch", 4, 1 },
+ { "EnHashUcast", 3, 1 },
+ { "EnHashMcast", 2, 1 },
+ { "DisBCast", 1, 1 },
+ { "CopyAllFrames", 0, 1 },
+ { "XGM_RX_HASH_LOW", 0x814, 0 },
+ { "XGM_RX_HASH_HIGH", 0x818, 0 },
+ { "XGM_RX_EXACT_MATCH_LOW_1", 0x81c, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_1", 0x820, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_2", 0x824, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_2", 0x828, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_3", 0x82c, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_3", 0x830, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_4", 0x834, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_4", 0x838, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_5", 0x83c, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_5", 0x840, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_6", 0x844, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_6", 0x848, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_7", 0x84c, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_7", 0x850, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_8", 0x854, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_8", 0x858, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_TYPE_MATCH_1", 0x85c, 0 },
+ { "EnTypeMatch", 31, 1 },
+ { "type", 0, 16 },
+ { "XGM_RX_TYPE_MATCH_2", 0x860, 0 },
+ { "EnTypeMatch", 31, 1 },
+ { "type", 0, 16 },
+ { "XGM_RX_TYPE_MATCH_3", 0x864, 0 },
+ { "EnTypeMatch", 31, 1 },
+ { "type", 0, 16 },
+ { "XGM_RX_TYPE_MATCH_4", 0x868, 0 },
+ { "EnTypeMatch", 31, 1 },
+ { "type", 0, 16 },
+ { "XGM_INT_STATUS", 0x86c, 0 },
+ { "XGMIIExtInt", 10, 1 },
+ { "LinkFaultChange", 9, 1 },
+ { "PhyFrameComplete", 8, 1 },
+ { "PauseFrameTxmt", 7, 1 },
+ { "PauseCntrTimeOut", 6, 1 },
+ { "Non0PauseRcvd", 5, 1 },
+ { "StatOFlow", 4, 1 },
+ { "TxErrFIFO", 3, 1 },
+ { "TxUFlow", 2, 1 },
+ { "FrameTxmt", 1, 1 },
+ { "FrameRcvd", 0, 1 },
+ { "XGM_XGM_INT_MASK", 0x870, 0 },
+ { "XGMIIExtInt", 10, 1 },
+ { "LinkFaultChange", 9, 1 },
+ { "PhyFrameComplete", 8, 1 },
+ { "PauseFrameTxmt", 7, 1 },
+ { "PauseCntrTimeOut", 6, 1 },
+ { "Non0PauseRcvd", 5, 1 },
+ { "StatOFlow", 4, 1 },
+ { "TxErrFIFO", 3, 1 },
+ { "TxUFlow", 2, 1 },
+ { "FrameTxmt", 1, 1 },
+ { "FrameRcvd", 0, 1 },
+ { "XGM_XGM_INT_ENABLE", 0x874, 0 },
+ { "XGMIIExtInt", 10, 1 },
+ { "LinkFaultChange", 9, 1 },
+ { "PhyFrameComplete", 8, 1 },
+ { "PauseFrameTxmt", 7, 1 },
+ { "PauseCntrTimeOut", 6, 1 },
+ { "Non0PauseRcvd", 5, 1 },
+ { "StatOFlow", 4, 1 },
+ { "TxErrFIFO", 3, 1 },
+ { "TxUFlow", 2, 1 },
+ { "FrameTxmt", 1, 1 },
+ { "FrameRcvd", 0, 1 },
+ { "XGM_XGM_INT_DISABLE", 0x878, 0 },
+ { "XGMIIExtInt", 10, 1 },
+ { "LinkFaultChange", 9, 1 },
+ { "PhyFrameComplete", 8, 1 },
+ { "PauseFrameTxmt", 7, 1 },
+ { "PauseCntrTimeOut", 6, 1 },
+ { "Non0PauseRcvd", 5, 1 },
+ { "StatOFlow", 4, 1 },
+ { "TxErrFIFO", 3, 1 },
+ { "TxUFlow", 2, 1 },
+ { "FrameTxmt", 1, 1 },
+ { "FrameRcvd", 0, 1 },
+ { "XGM_TX_PAUSE_TIMER", 0x87c, 0 },
+ { "CurPauseTimer", 0, 16 },
+ { "XGM_STAT_CTRL", 0x880, 0 },
+ { "ReadSnpShot", 4, 1 },
+ { "TakeSnpShot", 3, 1 },
+ { "ClrStats", 2, 1 },
+ { "IncrStats", 1, 1 },
+ { "EnTestModeWr", 0, 1 },
+ { "XGM_RXFIFO_CFG", 0x884, 0 },
+ { "RxFIFOPauseHWM", 17, 12 },
+ { "RxFIFOPauseLWM", 5, 12 },
+ { "ForcedPause", 4, 1 },
+ { "ExternLoopback", 3, 1 },
+ { "RxByteSwap", 2, 1 },
+ { "RxStrFrwrd", 1, 1 },
+ { "DisErrFrames", 0, 1 },
+ { "XGM_TXFIFO_CFG", 0x888, 0 },
+ { "EnDropPkt", 21, 1 },
+ { "TxIPG", 13, 8 },
+ { "TxFIFOThresh", 4, 9 },
+ { "InternLoopback", 3, 1 },
+ { "TxByteSwap", 2, 1 },
+ { "DisCRC", 1, 1 },
+ { "DisPreAmble", 0, 1 },
+ { "XGM_SLOW_TIMER", 0x88c, 0 },
+ { "PauseSlowTimerEn", 31, 1 },
+ { "PauseSlowTimer", 0, 20 },
+ { "XGM_PAUSE_TIMER", 0x890, 0 },
+ { "PauseTimer", 0, 20 },
+ { "XGM_XAUI_PCS_TEST", 0x894, 0 },
+ { "TestPattern", 1, 2 },
+ { "EnTest", 0, 1 },
+ { "XGM_RGMII_CTRL", 0x898, 0 },
+ { "PhAlignFIFOThresh", 1, 2 },
+ { "TxClk90Shift", 0, 1 },
+ { "XGM_RGMII_IMP", 0x89c, 0 },
+ { "CalReset", 8, 1 },
+ { "CalUpdate", 7, 1 },
+ { "ImpSetUpdate", 6, 1 },
+ { "RGMIIImpPD", 3, 3 },
+ { "RGMIIImpPU", 0, 3 },
+ { "XGM_RX_MAX_PKT_SIZE", 0x8a8, 0 },
+ { "RxMaxPktSize", 0, 14 },
+ { "XGM_RESET_CTRL", 0x8ac, 0 },
+ { "XG2G_Reset_", 3, 1 },
+ { "RGMII_Reset_", 2, 1 },
+ { "PCS_Reset_", 1, 1 },
+ { "MAC_Reset_", 0, 1 },
+ { "XGM_XAUI1G_CTRL", 0x8b0, 0 },
+ { "XAUI1GLinkId", 0, 2 },
+ { "XGM_SERDES_LANE_CTRL", 0x8b4, 0 },
+ { "LaneReversal", 8, 1 },
+ { "TxPolarity", 4, 4 },
+ { "RxPolarity", 0, 4 },
+ { "XGM_PORT_CFG", 0x8b8, 0 },
+ { "SafeSpeedChange", 4, 1 },
+ { "ClkDivReset_", 3, 1 },
+ { "PortSpeed", 1, 2 },
+ { "EnRGMII", 0, 1 },
+ { "XGM_EPIO_DATA0", 0x8c0, 0 },
+ { "XGM_EPIO_DATA1", 0x8c4, 0 },
+ { "XGM_EPIO_DATA2", 0x8c8, 0 },
+ { "XGM_EPIO_DATA3", 0x8cc, 0 },
+ { "XGM_EPIO_OP", 0x8d0, 0 },
+ { "PIO_Ready", 31, 1 },
+ { "PIO_WrRd", 24, 1 },
+ { "PIO_Address", 0, 8 },
+ { "XGM_INT_ENABLE", 0x8d4, 0 },
+ { "RGMIIRxFIFOOverflow", 23, 1 },
+ { "RGMIIRxFIFOUnderflow", 22, 1 },
+ { "RxPktSizeError", 21, 1 },
+ { "WOLPatDetected", 20, 1 },
+ { "TXFIFO_prty_err", 17, 3 },
+ { "RXFIFO_prty_err", 14, 3 },
+ { "TXFIFO_underrun", 13, 1 },
+ { "RXFIFO_overflow", 12, 1 },
+ { "SERDESBISTErr", 8, 4 },
+ { "SERDESLowSigChange", 4, 4 },
+ { "XAUIPCSCTCErr", 3, 1 },
+ { "XAUIPCSAlignChange", 2, 1 },
+ { "RGMIILinkStsChange", 1, 1 },
+ { "xgm_int", 0, 1 },
+ { "XGM_INT_CAUSE", 0x8d8, 0 },
+ { "RGMIIRxFIFOOverflow", 23, 1 },
+ { "RGMIIRxFIFOUnderflow", 22, 1 },
+ { "RxPktSizeError", 21, 1 },
+ { "WOLPatDetected", 20, 1 },
+ { "TXFIFO_prty_err", 17, 3 },
+ { "RXFIFO_prty_err", 14, 3 },
+ { "TXFIFO_underrun", 13, 1 },
+ { "RXFIFO_overflow", 12, 1 },
+ { "SERDESBISTErr", 8, 4 },
+ { "SERDESLowSigChange", 4, 4 },
+ { "XAUIPCSCTCErr", 3, 1 },
+ { "XAUIPCSAlignChange", 2, 1 },
+ { "RGMIILinkStsChange", 1, 1 },
+ { "xgm_int", 0, 1 },
+ { "XGM_XAUI_ACT_CTRL", 0x8dc, 0 },
+ { "TxEn", 1, 1 },
+ { "RxEn", 0, 1 },
+ { "XGM_SERDES_CTRL0", 0x8e0, 0 },
+ { "IntSerLPBK3", 27, 1 },
+ { "IntSerLPBK2", 26, 1 },
+ { "IntSerLPBK1", 25, 1 },
+ { "IntSerLPBK0", 24, 1 },
+ { "Reset3", 23, 1 },
+ { "Reset2", 22, 1 },
+ { "Reset1", 21, 1 },
+ { "Reset0", 20, 1 },
+ { "Pwrdn3", 19, 1 },
+ { "Pwrdn2", 18, 1 },
+ { "Pwrdn1", 17, 1 },
+ { "Pwrdn0", 16, 1 },
+ { "ResetPLL23", 15, 1 },
+ { "ResetPLL01", 14, 1 },
+ { "PW23", 12, 2 },
+ { "PW01", 10, 2 },
+ { "Deq", 6, 4 },
+ { "Dtx", 2, 4 },
+ { "LoDrv", 1, 1 },
+ { "HiDrv", 0, 1 },
+ { "XGM_SERDES_CTRL1", 0x8e4, 0 },
+ { "FmOffset3", 19, 5 },
+ { "FmOffsetEn3", 18, 1 },
+ { "FmOffset2", 13, 5 },
+ { "FmOffsetEn2", 12, 1 },
+ { "FmOffset1", 7, 5 },
+ { "FmOffsetEn1", 6, 1 },
+ { "FmOffset0", 1, 5 },
+ { "FmOffsetEn0", 0, 1 },
+ { "XGM_SERDES_CTRL2", 0x8e8, 0 },
+ { "DnIn3", 11, 1 },
+ { "UpIn3", 10, 1 },
+ { "RxSlave3", 9, 1 },
+ { "DnIn2", 8, 1 },
+ { "UpIn2", 7, 1 },
+ { "RxSlave2", 6, 1 },
+ { "DnIn1", 5, 1 },
+ { "UpIn1", 4, 1 },
+ { "RxSlave1", 3, 1 },
+ { "DnIn0", 2, 1 },
+ { "UpIn0", 1, 1 },
+ { "RxSlave0", 0, 1 },
+ { "XGM_SERDES_CTRL3", 0x8ec, 0 },
+ { "ExtBISTChkErrClr3", 31, 1 },
+ { "ExtBISTChkEn3", 30, 1 },
+ { "ExtBISTGenEn3", 29, 1 },
+ { "ExtBISTPat3", 26, 3 },
+ { "ExtParReset3", 25, 1 },
+ { "ExtParLPBK3", 24, 1 },
+ { "ExtBISTChkErrClr2", 23, 1 },
+ { "ExtBISTChkEn2", 22, 1 },
+ { "ExtBISTGenEn2", 21, 1 },
+ { "ExtBISTPat2", 18, 3 },
+ { "ExtParReset2", 17, 1 },
+ { "ExtParLPBK2", 16, 1 },
+ { "ExtBISTChkErrClr1", 15, 1 },
+ { "ExtBISTChkEn1", 14, 1 },
+ { "ExtBISTGenEn1", 13, 1 },
+ { "ExtBISTPat1", 10, 3 },
+ { "ExtParReset1", 9, 1 },
+ { "ExtParLPBK1", 8, 1 },
+ { "ExtBISTChkErrClr0", 7, 1 },
+ { "ExtBISTChkEn0", 6, 1 },
+ { "ExtBISTGenEn0", 5, 1 },
+ { "ExtBISTPat0", 2, 3 },
+ { "ExtParReset0", 1, 1 },
+ { "ExtParLPBK0", 0, 1 },
+ { "XGM_SERDES_STAT0", 0x8f0, 0 },
+ { "ExtBISTChkErrCnt0", 4, 24 },
+ { "ExtBISTChkFmd0", 3, 1 },
+ { "LowSig0", 0, 1 },
+ { "XGM_SERDES_STAT1", 0x8f4, 0 },
+ { "ExtBISTChkErrCnt1", 4, 24 },
+ { "ExtBISTChkFmd1", 3, 1 },
+ { "LowSig1", 0, 1 },
+ { "XGM_SERDES_STAT2", 0x8f8, 0 },
+ { "ExtBISTChkErrCnt2", 4, 24 },
+ { "ExtBISTChkFmd2", 3, 1 },
+ { "LowSig2", 0, 1 },
+ { "XGM_SERDES_STAT3", 0x8fc, 0 },
+ { "ExtBISTChkErrCnt3", 4, 24 },
+ { "ExtBISTChkFmd3", 3, 1 },
+ { "LowSig3", 0, 1 },
+ { "XGM_STAT_TX_BYTE_LOW", 0x900, 0 },
+ { "XGM_STAT_TX_BYTE_HIGH", 0x904, 0 },
+ { "TxBytes_high", 0, 13 },
+ { "XGM_STAT_TX_FRAME_LOW", 0x908, 0 },
+ { "XGM_STAT_TX_FRAME_HIGH", 0x90c, 0 },
+ { "TxFrames_high", 0, 4 },
+ { "XGM_STAT_TX_BCAST", 0x910, 0 },
+ { "XGM_STAT_TX_MCAST", 0x914, 0 },
+ { "XGM_STAT_TX_PAUSE", 0x918, 0 },
+ { "XGM_STAT_TX_64B_FRAMES", 0x91c, 0 },
+ { "XGM_STAT_TX_65_127B_FRAMES", 0x920, 0 },
+ { "XGM_STAT_TX_128_255B_FRAMES", 0x924, 0 },
+ { "XGM_STAT_TX_256_511B_FRAMES", 0x928, 0 },
+ { "XGM_STAT_TX_512_1023B_FRAMES", 0x92c, 0 },
+ { "XGM_STAT_TX_1024_1518B_FRAMES", 0x930, 0 },
+ { "XGM_STAT_TX_1519_MAXB_FRAMES", 0x934, 0 },
+ { "XGM_STAT_TX_ERR_FRAMES", 0x938, 0 },
+ { "XGM_STAT_RX_BYTES_LOW", 0x93c, 0 },
+ { "XGM_STAT_RX_BYTES_HIGH", 0x940, 0 },
+ { "RxBytes_high", 0, 13 },
+ { "XGM_STAT_RX_FRAMES_LOW", 0x944, 0 },
+ { "XGM_STAT_RX_FRAMES_HIGH", 0x948, 0 },
+ { "RxFrames_high", 0, 4 },
+ { "XGM_STAT_RX_BCAST_FRAMES", 0x94c, 0 },
+ { "XGM_STAT_RX_MCAST_FRAMES", 0x950, 0 },
+ { "XGM_STAT_RX_PAUSE_FRAMES", 0x954, 0 },
+ { "RxPauseFrames", 0, 16 },
+ { "XGM_STAT_RX_64B_FRAMES", 0x958, 0 },
+ { "XGM_STAT_RX_65_127B_FRAMES", 0x95c, 0 },
+ { "XGM_STAT_RX_128_255B_FRAMES", 0x960, 0 },
+ { "XGM_STAT_RX_256_511B_FRAMES", 0x964, 0 },
+ { "XGM_STAT_RX_512_1023B_FRAMES", 0x968, 0 },
+ { "XGM_STAT_RX_1024_1518B_FRAMES", 0x96c, 0 },
+ { "XGM_STAT_RX_1519_MAXB_FRAMES", 0x970, 0 },
+ { "XGM_STAT_RX_SHORT_FRAMES", 0x974, 0 },
+ { "RxShortFrames", 0, 16 },
+ { "XGM_STAT_RX_OVERSIZE_FRAMES", 0x978, 0 },
+ { "RxOversizeFrames", 0, 16 },
+ { "XGM_STAT_RX_JABBER_FRAMES", 0x97c, 0 },
+ { "RxJabberFrames", 0, 16 },
+ { "XGM_STAT_RX_CRC_ERR_FRAMES", 0x980, 0 },
+ { "RxCRCErrFrames", 0, 16 },
+ { "XGM_STAT_RX_LENGTH_ERR_FRAMES", 0x984, 0 },
+ { "RxLengthErrFrames", 0, 16 },
+ { "XGM_STAT_RX_SYM_CODE_ERR_FRAMES", 0x988, 0 },
+ { "RxSymCodeErrFrames", 0, 16 },
+ { "XGM_XAUI_PCS_ERR", 0x998, 0 },
+ { "PCS_SyncStatus", 5, 4 },
+ { "PCS_CTCFIFOErr", 1, 4 },
+ { "PCS_NotAligned", 0, 1 },
+ { "XGM_RGMII_STATUS", 0x99c, 0 },
+ { "GMIIDuplex", 3, 1 },
+ { "GMIISpeed", 1, 2 },
+ { "GMIILinkStatus", 0, 1 },
+ { "XGM_WOL_STATUS", 0x9a0, 0 },
+ { "PatDetected", 31, 1 },
+ { "MatchedFilter", 0, 3 },
+ { "XGM_RX_MAX_PKT_SIZE_ERR_CNT", 0x9a4, 0 },
+ { "XGM_TX_SPI4_SOP_EOP_CNT", 0x9a8, 0 },
+ { "TxSPI4SopCnt", 16, 16 },
+ { "TxSPI4EopCnt", 0, 16 },
+ { "XGM_RX_SPI4_SOP_EOP_CNT", 0x9ac, 0 },
+ { "RxSPI4SopCnt", 16, 16 },
+ { "RxSPI4EopCnt", 0, 16 },
+ { NULL }
+};
+
+struct reg_info t3b_xgmac0_1_regs[] = {
+ { "XGM_TX_CTRL", 0xa00, 0 },
+ { "SendPause", 2, 1 },
+ { "SendZeroPause", 1, 1 },
+ { "TxEn", 0, 1 },
+ { "XGM_TX_CFG", 0xa04, 0 },
+ { "CfgClkSpeed", 2, 3 },
+ { "StretchMode", 1, 1 },
+ { "TxPauseEn", 0, 1 },
+ { "XGM_TX_PAUSE_QUANTA", 0xa08, 0 },
+ { "TxPauseQuanta", 0, 16 },
+ { "XGM_RX_CTRL", 0xa0c, 0 },
+ { "RxEn", 0, 1 },
+ { "XGM_RX_CFG", 0xa10, 0 },
+ { "Con802_3Preamble", 12, 1 },
+ { "EnNon802_3Preamble", 11, 1 },
+ { "CopyPreamble", 10, 1 },
+ { "DisPauseFrames", 9, 1 },
+ { "En1536BFrames", 8, 1 },
+ { "EnJumbo", 7, 1 },
+ { "RmFCS", 6, 1 },
+ { "DisNonVlan", 5, 1 },
+ { "EnExtMatch", 4, 1 },
+ { "EnHashUcast", 3, 1 },
+ { "EnHashMcast", 2, 1 },
+ { "DisBCast", 1, 1 },
+ { "CopyAllFrames", 0, 1 },
+ { "XGM_RX_HASH_LOW", 0xa14, 0 },
+ { "XGM_RX_HASH_HIGH", 0xa18, 0 },
+ { "XGM_RX_EXACT_MATCH_LOW_1", 0xa1c, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_1", 0xa20, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_2", 0xa24, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_2", 0xa28, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_3", 0xa2c, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_3", 0xa30, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_4", 0xa34, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_4", 0xa38, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_5", 0xa3c, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_5", 0xa40, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_6", 0xa44, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_6", 0xa48, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_7", 0xa4c, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_7", 0xa50, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_8", 0xa54, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_8", 0xa58, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_TYPE_MATCH_1", 0xa5c, 0 },
+ { "EnTypeMatch", 31, 1 },
+ { "type", 0, 16 },
+ { "XGM_RX_TYPE_MATCH_2", 0xa60, 0 },
+ { "EnTypeMatch", 31, 1 },
+ { "type", 0, 16 },
+ { "XGM_RX_TYPE_MATCH_3", 0xa64, 0 },
+ { "EnTypeMatch", 31, 1 },
+ { "type", 0, 16 },
+ { "XGM_RX_TYPE_MATCH_4", 0xa68, 0 },
+ { "EnTypeMatch", 31, 1 },
+ { "type", 0, 16 },
+ { "XGM_INT_STATUS", 0xa6c, 0 },
+ { "XGMIIExtInt", 10, 1 },
+ { "LinkFaultChange", 9, 1 },
+ { "PhyFrameComplete", 8, 1 },
+ { "PauseFrameTxmt", 7, 1 },
+ { "PauseCntrTimeOut", 6, 1 },
+ { "Non0PauseRcvd", 5, 1 },
+ { "StatOFlow", 4, 1 },
+ { "TxErrFIFO", 3, 1 },
+ { "TxUFlow", 2, 1 },
+ { "FrameTxmt", 1, 1 },
+ { "FrameRcvd", 0, 1 },
+ { "XGM_XGM_INT_MASK", 0xa70, 0 },
+ { "XGMIIExtInt", 10, 1 },
+ { "LinkFaultChange", 9, 1 },
+ { "PhyFrameComplete", 8, 1 },
+ { "PauseFrameTxmt", 7, 1 },
+ { "PauseCntrTimeOut", 6, 1 },
+ { "Non0PauseRcvd", 5, 1 },
+ { "StatOFlow", 4, 1 },
+ { "TxErrFIFO", 3, 1 },
+ { "TxUFlow", 2, 1 },
+ { "FrameTxmt", 1, 1 },
+ { "FrameRcvd", 0, 1 },
+ { "XGM_XGM_INT_ENABLE", 0xa74, 0 },
+ { "XGMIIExtInt", 10, 1 },
+ { "LinkFaultChange", 9, 1 },
+ { "PhyFrameComplete", 8, 1 },
+ { "PauseFrameTxmt", 7, 1 },
+ { "PauseCntrTimeOut", 6, 1 },
+ { "Non0PauseRcvd", 5, 1 },
+ { "StatOFlow", 4, 1 },
+ { "TxErrFIFO", 3, 1 },
+ { "TxUFlow", 2, 1 },
+ { "FrameTxmt", 1, 1 },
+ { "FrameRcvd", 0, 1 },
+ { "XGM_XGM_INT_DISABLE", 0xa78, 0 },
+ { "XGMIIExtInt", 10, 1 },
+ { "LinkFaultChange", 9, 1 },
+ { "PhyFrameComplete", 8, 1 },
+ { "PauseFrameTxmt", 7, 1 },
+ { "PauseCntrTimeOut", 6, 1 },
+ { "Non0PauseRcvd", 5, 1 },
+ { "StatOFlow", 4, 1 },
+ { "TxErrFIFO", 3, 1 },
+ { "TxUFlow", 2, 1 },
+ { "FrameTxmt", 1, 1 },
+ { "FrameRcvd", 0, 1 },
+ { "XGM_TX_PAUSE_TIMER", 0xa7c, 0 },
+ { "CurPauseTimer", 0, 16 },
+ { "XGM_STAT_CTRL", 0xa80, 0 },
+ { "ReadSnpShot", 4, 1 },
+ { "TakeSnpShot", 3, 1 },
+ { "ClrStats", 2, 1 },
+ { "IncrStats", 1, 1 },
+ { "EnTestModeWr", 0, 1 },
+ { "XGM_RXFIFO_CFG", 0xa84, 0 },
+ { "RxFIFOPauseHWM", 17, 12 },
+ { "RxFIFOPauseLWM", 5, 12 },
+ { "ForcedPause", 4, 1 },
+ { "ExternLoopback", 3, 1 },
+ { "RxByteSwap", 2, 1 },
+ { "RxStrFrwrd", 1, 1 },
+ { "DisErrFrames", 0, 1 },
+ { "XGM_TXFIFO_CFG", 0xa88, 0 },
+ { "EnDropPkt", 21, 1 },
+ { "TxIPG", 13, 8 },
+ { "TxFIFOThresh", 4, 9 },
+ { "InternLoopback", 3, 1 },
+ { "TxByteSwap", 2, 1 },
+ { "DisCRC", 1, 1 },
+ { "DisPreAmble", 0, 1 },
+ { "XGM_SLOW_TIMER", 0xa8c, 0 },
+ { "PauseSlowTimerEn", 31, 1 },
+ { "PauseSlowTimer", 0, 20 },
+ { "XGM_PAUSE_TIMER", 0xa90, 0 },
+ { "PauseTimer", 0, 20 },
+ { "XGM_XAUI_PCS_TEST", 0xa94, 0 },
+ { "TestPattern", 1, 2 },
+ { "EnTest", 0, 1 },
+ { "XGM_RGMII_CTRL", 0xa98, 0 },
+ { "PhAlignFIFOThresh", 1, 2 },
+ { "TxClk90Shift", 0, 1 },
+ { "XGM_RGMII_IMP", 0xa9c, 0 },
+ { "CalReset", 8, 1 },
+ { "CalUpdate", 7, 1 },
+ { "ImpSetUpdate", 6, 1 },
+ { "RGMIIImpPD", 3, 3 },
+ { "RGMIIImpPU", 0, 3 },
+ { "XGM_RX_MAX_PKT_SIZE", 0xaa8, 0 },
+ { "RxMaxPktSize", 0, 14 },
+ { "XGM_RESET_CTRL", 0xaac, 0 },
+ { "XG2G_Reset_", 3, 1 },
+ { "RGMII_Reset_", 2, 1 },
+ { "PCS_Reset_", 1, 1 },
+ { "MAC_Reset_", 0, 1 },
+ { "XGM_XAUI1G_CTRL", 0xab0, 0 },
+ { "XAUI1GLinkId", 0, 2 },
+ { "XGM_SERDES_LANE_CTRL", 0xab4, 0 },
+ { "LaneReversal", 8, 1 },
+ { "TxPolarity", 4, 4 },
+ { "RxPolarity", 0, 4 },
+ { "XGM_PORT_CFG", 0xab8, 0 },
+ { "SafeSpeedChange", 4, 1 },
+ { "ClkDivReset_", 3, 1 },
+ { "PortSpeed", 1, 2 },
+ { "EnRGMII", 0, 1 },
+ { "XGM_EPIO_DATA0", 0xac0, 0 },
+ { "XGM_EPIO_DATA1", 0xac4, 0 },
+ { "XGM_EPIO_DATA2", 0xac8, 0 },
+ { "XGM_EPIO_DATA3", 0xacc, 0 },
+ { "XGM_EPIO_OP", 0xad0, 0 },
+ { "PIO_Ready", 31, 1 },
+ { "PIO_WrRd", 24, 1 },
+ { "PIO_Address", 0, 8 },
+ { "XGM_INT_ENABLE", 0xad4, 0 },
+ { "RGMIIRxFIFOOverflow", 23, 1 },
+ { "RGMIIRxFIFOUnderflow", 22, 1 },
+ { "RxPktSizeError", 21, 1 },
+ { "WOLPatDetected", 20, 1 },
+ { "TXFIFO_prty_err", 17, 3 },
+ { "RXFIFO_prty_err", 14, 3 },
+ { "TXFIFO_underrun", 13, 1 },
+ { "RXFIFO_overflow", 12, 1 },
+ { "SERDESBISTErr", 8, 4 },
+ { "SERDESLowSigChange", 4, 4 },
+ { "XAUIPCSCTCErr", 3, 1 },
+ { "XAUIPCSAlignChange", 2, 1 },
+ { "RGMIILinkStsChange", 1, 1 },
+ { "xgm_int", 0, 1 },
+ { "XGM_INT_CAUSE", 0xad8, 0 },
+ { "RGMIIRxFIFOOverflow", 23, 1 },
+ { "RGMIIRxFIFOUnderflow", 22, 1 },
+ { "RxPktSizeError", 21, 1 },
+ { "WOLPatDetected", 20, 1 },
+ { "TXFIFO_prty_err", 17, 3 },
+ { "RXFIFO_prty_err", 14, 3 },
+ { "TXFIFO_underrun", 13, 1 },
+ { "RXFIFO_overflow", 12, 1 },
+ { "SERDESBISTErr", 8, 4 },
+ { "SERDESLowSigChange", 4, 4 },
+ { "XAUIPCSCTCErr", 3, 1 },
+ { "XAUIPCSAlignChange", 2, 1 },
+ { "RGMIILinkStsChange", 1, 1 },
+ { "xgm_int", 0, 1 },
+ { "XGM_XAUI_ACT_CTRL", 0xadc, 0 },
+ { "TxEn", 1, 1 },
+ { "RxEn", 0, 1 },
+ { "XGM_SERDES_CTRL0", 0xae0, 0 },
+ { "IntSerLPBK3", 27, 1 },
+ { "IntSerLPBK2", 26, 1 },
+ { "IntSerLPBK1", 25, 1 },
+ { "IntSerLPBK0", 24, 1 },
+ { "Reset3", 23, 1 },
+ { "Reset2", 22, 1 },
+ { "Reset1", 21, 1 },
+ { "Reset0", 20, 1 },
+ { "Pwrdn3", 19, 1 },
+ { "Pwrdn2", 18, 1 },
+ { "Pwrdn1", 17, 1 },
+ { "Pwrdn0", 16, 1 },
+ { "ResetPLL23", 15, 1 },
+ { "ResetPLL01", 14, 1 },
+ { "PW23", 12, 2 },
+ { "PW01", 10, 2 },
+ { "Deq", 6, 4 },
+ { "Dtx", 2, 4 },
+ { "LoDrv", 1, 1 },
+ { "HiDrv", 0, 1 },
+ { "XGM_SERDES_CTRL1", 0xae4, 0 },
+ { "FmOffset3", 19, 5 },
+ { "FmOffsetEn3", 18, 1 },
+ { "FmOffset2", 13, 5 },
+ { "FmOffsetEn2", 12, 1 },
+ { "FmOffset1", 7, 5 },
+ { "FmOffsetEn1", 6, 1 },
+ { "FmOffset0", 1, 5 },
+ { "FmOffsetEn0", 0, 1 },
+ { "XGM_SERDES_CTRL2", 0xae8, 0 },
+ { "DnIn3", 11, 1 },
+ { "UpIn3", 10, 1 },
+ { "RxSlave3", 9, 1 },
+ { "DnIn2", 8, 1 },
+ { "UpIn2", 7, 1 },
+ { "RxSlave2", 6, 1 },
+ { "DnIn1", 5, 1 },
+ { "UpIn1", 4, 1 },
+ { "RxSlave1", 3, 1 },
+ { "DnIn0", 2, 1 },
+ { "UpIn0", 1, 1 },
+ { "RxSlave0", 0, 1 },
+ { "XGM_SERDES_CTRL3", 0xaec, 0 },
+ { "ExtBISTChkErrClr3", 31, 1 },
+ { "ExtBISTChkEn3", 30, 1 },
+ { "ExtBISTGenEn3", 29, 1 },
+ { "ExtBISTPat3", 26, 3 },
+ { "ExtParReset3", 25, 1 },
+ { "ExtParLPBK3", 24, 1 },
+ { "ExtBISTChkErrClr2", 23, 1 },
+ { "ExtBISTChkEn2", 22, 1 },
+ { "ExtBISTGenEn2", 21, 1 },
+ { "ExtBISTPat2", 18, 3 },
+ { "ExtParReset2", 17, 1 },
+ { "ExtParLPBK2", 16, 1 },
+ { "ExtBISTChkErrClr1", 15, 1 },
+ { "ExtBISTChkEn1", 14, 1 },
+ { "ExtBISTGenEn1", 13, 1 },
+ { "ExtBISTPat1", 10, 3 },
+ { "ExtParReset1", 9, 1 },
+ { "ExtParLPBK1", 8, 1 },
+ { "ExtBISTChkErrClr0", 7, 1 },
+ { "ExtBISTChkEn0", 6, 1 },
+ { "ExtBISTGenEn0", 5, 1 },
+ { "ExtBISTPat0", 2, 3 },
+ { "ExtParReset0", 1, 1 },
+ { "ExtParLPBK0", 0, 1 },
+ { "XGM_SERDES_STAT0", 0xaf0, 0 },
+ { "ExtBISTChkErrCnt0", 4, 24 },
+ { "ExtBISTChkFmd0", 3, 1 },
+ { "LowSig0", 0, 1 },
+ { "XGM_SERDES_STAT1", 0xaf4, 0 },
+ { "ExtBISTChkErrCnt1", 4, 24 },
+ { "ExtBISTChkFmd1", 3, 1 },
+ { "LowSig1", 0, 1 },
+ { "XGM_SERDES_STAT2", 0xaf8, 0 },
+ { "ExtBISTChkErrCnt2", 4, 24 },
+ { "ExtBISTChkFmd2", 3, 1 },
+ { "LowSig2", 0, 1 },
+ { "XGM_SERDES_STAT3", 0xafc, 0 },
+ { "ExtBISTChkErrCnt3", 4, 24 },
+ { "ExtBISTChkFmd3", 3, 1 },
+ { "LowSig3", 0, 1 },
+ { "XGM_STAT_TX_BYTE_LOW", 0xb00, 0 },
+ { "XGM_STAT_TX_BYTE_HIGH", 0xb04, 0 },
+ { "TxBytes_high", 0, 13 },
+ { "XGM_STAT_TX_FRAME_LOW", 0xb08, 0 },
+ { "XGM_STAT_TX_FRAME_HIGH", 0xb0c, 0 },
+ { "TxFrames_high", 0, 4 },
+ { "XGM_STAT_TX_BCAST", 0xb10, 0 },
+ { "XGM_STAT_TX_MCAST", 0xb14, 0 },
+ { "XGM_STAT_TX_PAUSE", 0xb18, 0 },
+ { "XGM_STAT_TX_64B_FRAMES", 0xb1c, 0 },
+ { "XGM_STAT_TX_65_127B_FRAMES", 0xb20, 0 },
+ { "XGM_STAT_TX_128_255B_FRAMES", 0xb24, 0 },
+ { "XGM_STAT_TX_256_511B_FRAMES", 0xb28, 0 },
+ { "XGM_STAT_TX_512_1023B_FRAMES", 0xb2c, 0 },
+ { "XGM_STAT_TX_1024_1518B_FRAMES", 0xb30, 0 },
+ { "XGM_STAT_TX_1519_MAXB_FRAMES", 0xb34, 0 },
+ { "XGM_STAT_TX_ERR_FRAMES", 0xb38, 0 },
+ { "XGM_STAT_RX_BYTES_LOW", 0xb3c, 0 },
+ { "XGM_STAT_RX_BYTES_HIGH", 0xb40, 0 },
+ { "RxBytes_high", 0, 13 },
+ { "XGM_STAT_RX_FRAMES_LOW", 0xb44, 0 },
+ { "XGM_STAT_RX_FRAMES_HIGH", 0xb48, 0 },
+ { "RxFrames_high", 0, 4 },
+ { "XGM_STAT_RX_BCAST_FRAMES", 0xb4c, 0 },
+ { "XGM_STAT_RX_MCAST_FRAMES", 0xb50, 0 },
+ { "XGM_STAT_RX_PAUSE_FRAMES", 0xb54, 0 },
+ { "RxPauseFrames", 0, 16 },
+ { "XGM_STAT_RX_64B_FRAMES", 0xb58, 0 },
+ { "XGM_STAT_RX_65_127B_FRAMES", 0xb5c, 0 },
+ { "XGM_STAT_RX_128_255B_FRAMES", 0xb60, 0 },
+ { "XGM_STAT_RX_256_511B_FRAMES", 0xb64, 0 },
+ { "XGM_STAT_RX_512_1023B_FRAMES", 0xb68, 0 },
+ { "XGM_STAT_RX_1024_1518B_FRAMES", 0xb6c, 0 },
+ { "XGM_STAT_RX_1519_MAXB_FRAMES", 0xb70, 0 },
+ { "XGM_STAT_RX_SHORT_FRAMES", 0xb74, 0 },
+ { "RxShortFrames", 0, 16 },
+ { "XGM_STAT_RX_OVERSIZE_FRAMES", 0xb78, 0 },
+ { "RxOversizeFrames", 0, 16 },
+ { "XGM_STAT_RX_JABBER_FRAMES", 0xb7c, 0 },
+ { "RxJabberFrames", 0, 16 },
+ { "XGM_STAT_RX_CRC_ERR_FRAMES", 0xb80, 0 },
+ { "RxCRCErrFrames", 0, 16 },
+ { "XGM_STAT_RX_LENGTH_ERR_FRAMES", 0xb84, 0 },
+ { "RxLengthErrFrames", 0, 16 },
+ { "XGM_STAT_RX_SYM_CODE_ERR_FRAMES", 0xb88, 0 },
+ { "RxSymCodeErrFrames", 0, 16 },
+ { "XGM_XAUI_PCS_ERR", 0xb98, 0 },
+ { "PCS_SyncStatus", 5, 4 },
+ { "PCS_CTCFIFOErr", 1, 4 },
+ { "PCS_NotAligned", 0, 1 },
+ { "XGM_RGMII_STATUS", 0xb9c, 0 },
+ { "GMIIDuplex", 3, 1 },
+ { "GMIISpeed", 1, 2 },
+ { "GMIILinkStatus", 0, 1 },
+ { "XGM_WOL_STATUS", 0xba0, 0 },
+ { "PatDetected", 31, 1 },
+ { "MatchedFilter", 0, 3 },
+ { "XGM_RX_MAX_PKT_SIZE_ERR_CNT", 0xba4, 0 },
+ { "XGM_TX_SPI4_SOP_EOP_CNT", 0xba8, 0 },
+ { "TxSPI4SopCnt", 16, 16 },
+ { "TxSPI4EopCnt", 0, 16 },
+ { "XGM_RX_SPI4_SOP_EOP_CNT", 0xbac, 0 },
+ { "RxSPI4SopCnt", 16, 16 },
+ { "RxSPI4EopCnt", 0, 16 },
+ { NULL }
+};
diff --git a/usr.sbin/cxgbtool/reg_defs_t3c.c b/usr.sbin/cxgbtool/reg_defs_t3c.c
new file mode 100644
index 0000000..6127fa4
--- /dev/null
+++ b/usr.sbin/cxgbtool/reg_defs_t3c.c
@@ -0,0 +1,3119 @@
+/*
+ * $FreeBSD$
+ */
+
+/* This file is automatically generated --- do not edit */
+
+struct reg_info t3c_sge3_regs[] = {
+ { "SG_CONTROL", 0x0, 0 },
+ { "CongMode", 29, 1 },
+ { "TnlFLMode", 28, 1 },
+ { "FatlPerrEn", 27, 1 },
+ { "UrgTnl", 26, 1 },
+ { "NewNotify", 25, 1 },
+ { "AvoidCqOvfl", 24, 1 },
+ { "OptOneIntMultQ", 23, 1 },
+ { "CQCrdtCtrl", 22, 1 },
+ { "EgrEnUpBp", 21, 1 },
+ { "DropPkt", 20, 1 },
+ { "EgrGenCtrl", 19, 1 },
+ { "UserSpaceSize", 14, 5 },
+ { "HostPageSize", 11, 3 },
+ { "PCIRelax", 10, 1 },
+ { "FLMode", 9, 1 },
+ { "PktShift", 6, 3 },
+ { "OneIntMultQ", 5, 1 },
+ { "FLPickAvail", 4, 1 },
+ { "BigEndianEgress", 3, 1 },
+ { "BigEndianIngress", 2, 1 },
+ { "IscsiCoalescing", 1, 1 },
+ { "GlobalEnable", 0, 1 },
+ { "SG_KDOORBELL", 0x4, 0 },
+ { "SelEgrCntx", 31, 1 },
+ { "EgrCntx", 0, 16 },
+ { "SG_GTS", 0x8, 0 },
+ { "RspQ", 29, 3 },
+ { "NewTimer", 16, 13 },
+ { "NewIndex", 0, 16 },
+ { "SG_CONTEXT_CMD", 0xc, 0 },
+ { "Opcode", 28, 4 },
+ { "Busy", 27, 1 },
+ { "CQ_credit", 20, 7 },
+ { "CQ", 19, 1 },
+ { "RspQ", 18, 1 },
+ { "Egress", 17, 1 },
+ { "FreeList", 16, 1 },
+ { "Context", 0, 16 },
+ { "SG_CONTEXT_DATA0", 0x10, 0 },
+ { "SG_CONTEXT_DATA1", 0x14, 0 },
+ { "SG_CONTEXT_DATA2", 0x18, 0 },
+ { "SG_CONTEXT_DATA3", 0x1c, 0 },
+ { "SG_CONTEXT_MASK0", 0x20, 0 },
+ { "SG_CONTEXT_MASK1", 0x24, 0 },
+ { "SG_CONTEXT_MASK2", 0x28, 0 },
+ { "SG_CONTEXT_MASK3", 0x2c, 0 },
+ { "SG_RSPQ_CREDIT_RETURN", 0x30, 0 },
+ { "RspQ", 29, 3 },
+ { "Data", 0, 16 },
+ { "SG_DATA_INTR", 0x34, 0 },
+ { "ErrIntr", 31, 1 },
+ { "DataIntr", 0, 8 },
+ { "SG_HI_DRB_HI_THRSH", 0x38, 0 },
+ { "HiDrbHiThrsh", 0, 10 },
+ { "SG_HI_DRB_LO_THRSH", 0x3c, 0 },
+ { "HiDrbLoThrsh", 0, 10 },
+ { "SG_LO_DRB_HI_THRSH", 0x40, 0 },
+ { "LoDrbHiThrsh", 0, 10 },
+ { "SG_LO_DRB_LO_THRSH", 0x44, 0 },
+ { "LoDrbLoThrsh", 0, 10 },
+ { "SG_ONE_INT_MULT_Q_COALESCING_TIMER", 0x48, 0 },
+ { "SG_RSPQ_FL_STATUS", 0x4c, 0 },
+ { "RspQ0Starved", 0, 1 },
+ { "RspQ1Starved", 1, 1 },
+ { "RspQ2Starved", 2, 1 },
+ { "RspQ3Starved", 3, 1 },
+ { "RspQ4Starved", 4, 1 },
+ { "RspQ5Starved", 5, 1 },
+ { "RspQ6Starved", 6, 1 },
+ { "RspQ7Starved", 7, 1 },
+ { "RspQ0Disabled", 8, 1 },
+ { "RspQ1Disabled", 9, 1 },
+ { "RspQ2Disabled", 10, 1 },
+ { "RspQ3Disabled", 11, 1 },
+ { "RspQ4Disabled", 12, 1 },
+ { "RspQ5Disabled", 13, 1 },
+ { "RspQ6Disabled", 14, 1 },
+ { "RspQ7Disabled", 15, 1 },
+ { "FL0Empty", 16, 1 },
+ { "FL1Empty", 17, 1 },
+ { "FL2Empty", 18, 1 },
+ { "FL3Empty", 19, 1 },
+ { "FL4Empty", 20, 1 },
+ { "FL5Empty", 21, 1 },
+ { "FL6Empty", 22, 1 },
+ { "FL7Empty", 23, 1 },
+ { "FL8Empty", 24, 1 },
+ { "FL9Empty", 25, 1 },
+ { "FL10Empty", 26, 1 },
+ { "FL11Empty", 27, 1 },
+ { "FL12Empty", 28, 1 },
+ { "FL13Empty", 29, 1 },
+ { "FL14Empty", 30, 1 },
+ { "FL15Empty", 31, 1 },
+ { "SG_EGR_PRI_CNT", 0x50, 0 },
+ { "EgrErrOpCode", 24, 8 },
+ { "EgrHiOpCode", 16, 8 },
+ { "EgrLoOpCode", 8, 8 },
+ { "EgrPriCnt", 0, 5 },
+ { "SG_EGR_RCQ_DRB_THRSH", 0x54, 0 },
+ { "HiRcqDrbThrsh", 16, 11 },
+ { "LoRcqDrbThrsh", 0, 11 },
+ { "SG_EGR_CNTX_BADDR", 0x58, 0 },
+ { "EgrCntxBAddr", 5, 27 },
+ { "SG_INT_CAUSE", 0x5c, 0 },
+ { "HiRcqParityError", 31, 1 },
+ { "LoRcqParityError", 30, 1 },
+ { "HiDrbParityError", 29, 1 },
+ { "LoDrbParityError", 28, 1 },
+ { "FlParityError", 22, 6 },
+ { "ItParityError", 20, 2 },
+ { "IrParityError", 19, 1 },
+ { "RcParityError", 18, 1 },
+ { "OcParityError", 17, 1 },
+ { "CpParityError", 16, 1 },
+ { "R_Req_FramingError", 15, 1 },
+ { "UC_Req_FramingError", 14, 1 },
+ { "HiCtlDrbDropErr", 13, 1 },
+ { "LoCtlDrbDropErr", 12, 1 },
+ { "HiPioDrbDropErr", 11, 1 },
+ { "LoPioDrbDropErr", 10, 1 },
+ { "HiCrdtUndFlowErr", 9, 1 },
+ { "LoCrdtUndFlowErr", 8, 1 },
+ { "HiPriorityDBFull", 7, 1 },
+ { "HiPriorityDBEmpty", 6, 1 },
+ { "LoPriorityDBFull", 5, 1 },
+ { "LoPriorityDBEmpty", 4, 1 },
+ { "RspQDisabled", 3, 1 },
+ { "RspQCreditOverfow", 2, 1 },
+ { "FlEmpty", 1, 1 },
+ { "RspQStarve", 0, 1 },
+ { "SG_INT_ENABLE", 0x60, 0 },
+ { "HiRcqParityError", 31, 1 },
+ { "LoRcqParityError", 30, 1 },
+ { "HiDrbParityError", 29, 1 },
+ { "LoDrbParityError", 28, 1 },
+ { "FlParityError", 22, 6 },
+ { "ItParityError", 20, 2 },
+ { "IrParityError", 19, 1 },
+ { "RcParityError", 18, 1 },
+ { "OcParityError", 17, 1 },
+ { "CpParityError", 16, 1 },
+ { "R_Req_FramingError", 15, 1 },
+ { "UC_Req_FramingError", 14, 1 },
+ { "HiCtlDrbDropErr", 13, 1 },
+ { "LoCtlDrbDropErr", 12, 1 },
+ { "HiPioDrbDropErr", 11, 1 },
+ { "LoPioDrbDropErr", 10, 1 },
+ { "HiCrdtUndFlowErr", 9, 1 },
+ { "LoCrdtUndFlowErr", 8, 1 },
+ { "HiPriorityDBFull", 7, 1 },
+ { "HiPriorityDBEmpty", 6, 1 },
+ { "LoPriorityDBFull", 5, 1 },
+ { "LoPriorityDBEmpty", 4, 1 },
+ { "RspQDisabled", 3, 1 },
+ { "RspQCreditOverfow", 2, 1 },
+ { "FlEmpty", 1, 1 },
+ { "RspQStarve", 0, 1 },
+ { "SG_CMDQ_CREDIT_TH", 0x64, 0 },
+ { "Timeout", 8, 24 },
+ { "Threshold", 0, 8 },
+ { "SG_TIMER_TICK", 0x68, 0 },
+ { "SG_CQ_CONTEXT_BADDR", 0x6c, 0 },
+ { "baseAddr", 5, 27 },
+ { "SG_OCO_BASE", 0x70, 0 },
+ { "Base1", 16, 16 },
+ { "Base0", 0, 16 },
+ { "SG_DRB_PRI_THRESH", 0x74, 0 },
+ { "DrbPriThrsh", 0, 16 },
+ { "SG_DEBUG_INDEX", 0x78, 0 },
+ { "SG_DEBUG_DATA", 0x7c, 0 },
+ { NULL }
+};
+
+struct reg_info t3c_pcix1_regs[] = {
+ { "PCIX_INT_ENABLE", 0x80, 0 },
+ { "MSIXParErr", 22, 3 },
+ { "CFParErr", 18, 4 },
+ { "RFParErr", 14, 4 },
+ { "WFParErr", 12, 2 },
+ { "PIOParErr", 11, 1 },
+ { "DetUncECCErr", 10, 1 },
+ { "DetCorECCErr", 9, 1 },
+ { "RcvSplCmpErr", 8, 1 },
+ { "UnxSplCmp", 7, 1 },
+ { "SplCmpDis", 6, 1 },
+ { "DetParErr", 5, 1 },
+ { "SigSysErr", 4, 1 },
+ { "RcvMstAbt", 3, 1 },
+ { "RcvTarAbt", 2, 1 },
+ { "SigTarAbt", 1, 1 },
+ { "MstDetParErr", 0, 1 },
+ { "PCIX_INT_CAUSE", 0x84, 0 },
+ { "MSIXParErr", 22, 3 },
+ { "CFParErr", 18, 4 },
+ { "RFParErr", 14, 4 },
+ { "WFParErr", 12, 2 },
+ { "PIOParErr", 11, 1 },
+ { "DetUncECCErr", 10, 1 },
+ { "DetCorECCErr", 9, 1 },
+ { "RcvSplCmpErr", 8, 1 },
+ { "UnxSplCmp", 7, 1 },
+ { "SplCmpDis", 6, 1 },
+ { "DetParErr", 5, 1 },
+ { "SigSysErr", 4, 1 },
+ { "RcvMstAbt", 3, 1 },
+ { "RcvTarAbt", 2, 1 },
+ { "SigTarAbt", 1, 1 },
+ { "MstDetParErr", 0, 1 },
+ { "PCIX_CFG", 0x88, 0 },
+ { "DMAStopEn", 19, 1 },
+ { "CLIDecEn", 18, 1 },
+ { "LatTmrDis", 17, 1 },
+ { "LowPwrEn", 16, 1 },
+ { "AsyncIntVec", 11, 5 },
+ { "MaxSplTrnC", 8, 3 },
+ { "MaxSplTrnR", 5, 3 },
+ { "MaxWrByteCnt", 3, 2 },
+ { "WrReqAtomicEn", 2, 1 },
+ { "CRstWrmMode", 1, 1 },
+ { "PIOAck64En", 0, 1 },
+ { "PCIX_MODE", 0x8c, 0 },
+ { "PClkRange", 6, 2 },
+ { "PCIXInitPat", 2, 4 },
+ { "66MHz", 1, 1 },
+ { "64Bit", 0, 1 },
+ { "PCIX_CAL", 0x90, 0 },
+ { "Busy", 31, 1 },
+ { "PerCalDiv", 22, 8 },
+ { "PerCalEn", 21, 1 },
+ { "SglCalEn", 20, 1 },
+ { "ZInUpdMode", 19, 1 },
+ { "ZInSel", 18, 1 },
+ { "ZPDMan", 15, 3 },
+ { "ZPUMan", 12, 3 },
+ { "ZPDOut", 9, 3 },
+ { "ZPUOut", 6, 3 },
+ { "ZPDIn", 3, 3 },
+ { "ZPUIn", 0, 3 },
+ { "PCIX_WOL", 0x94, 0 },
+ { "WakeUp1", 3, 1 },
+ { "WakeUp0", 2, 1 },
+ { "SleepMode1", 1, 1 },
+ { "SleepMode0", 0, 1 },
+ { "PCIX_STAT0", 0x98, 0 },
+ { "PIOReqFifoLevel", 26, 6 },
+ { "RFIniSt", 24, 2 },
+ { "RFRespRdSt", 22, 2 },
+ { "TarCSt", 19, 3 },
+ { "TarXSt", 16, 3 },
+ { "WFReqWrSt", 13, 3 },
+ { "WFRespFifoEmpty", 12, 1 },
+ { "WFReqFifoEmpty", 11, 1 },
+ { "RFRespFifoEmpty", 10, 1 },
+ { "RFReqFifoEmpty", 9, 1 },
+ { "PIORespFifoLevel", 7, 2 },
+ { "CFRespFifoEmpty", 6, 1 },
+ { "CFReqFifoEmpty", 5, 1 },
+ { "VPDRespFifoEmpty", 4, 1 },
+ { "VPDReqFifoEmpty", 3, 1 },
+ { "PIO_RspPnd", 2, 1 },
+ { "DlyTrnPnd", 1, 1 },
+ { "SplTrnPnd", 0, 1 },
+ { "PCIX_STAT1", 0x9c, 0 },
+ { "WFIniSt", 26, 4 },
+ { "ArbSt", 23, 3 },
+ { "PMISt", 21, 2 },
+ { "CalSt", 19, 2 },
+ { "CFReqRdSt", 17, 2 },
+ { "CFIniSt", 15, 2 },
+ { "CFRespRdSt", 13, 2 },
+ { "IniCSt", 10, 3 },
+ { "IniXSt", 7, 3 },
+ { "IntSt", 4, 3 },
+ { "PIOSt", 2, 2 },
+ { "RFReqRdSt", 0, 2 },
+ { NULL }
+};
+
+struct reg_info t3c_pcie0_regs[] = {
+ { "PCIE_INT_ENABLE", 0x80, 0 },
+ { "BISTErr", 19, 8 },
+ { "TxParErr", 18, 1 },
+ { "RxParErr", 17, 1 },
+ { "RetryLUTParErr", 16, 1 },
+ { "RetryBUFParErr", 15, 1 },
+ { "MSIXParErr", 12, 3 },
+ { "CFParErr", 11, 1 },
+ { "RFParErr", 10, 1 },
+ { "WFParErr", 9, 1 },
+ { "PIOParErr", 8, 1 },
+ { "UnxSplCplErrC", 7, 1 },
+ { "UnxSplCplErrR", 6, 1 },
+ { "VPDAddrChng", 5, 1 },
+ { "BusMstrEn", 4, 1 },
+ { "PMStChng", 3, 1 },
+ { "PEXMsg", 2, 1 },
+ { "ZeroLenRd", 1, 1 },
+ { "PEXErr", 0, 1 },
+ { "PCIE_INT_CAUSE", 0x84, 0 },
+ { "BISTErr", 19, 8 },
+ { "TxParErr", 18, 1 },
+ { "RxParErr", 17, 1 },
+ { "RetryLUTParErr", 16, 1 },
+ { "RetryBUFParErr", 15, 1 },
+ { "MSIXParErr", 12, 3 },
+ { "CFParErr", 11, 1 },
+ { "RFParErr", 10, 1 },
+ { "WFParErr", 9, 1 },
+ { "PIOParErr", 8, 1 },
+ { "UnxSplCplErrC", 7, 1 },
+ { "UnxSplCplErrR", 6, 1 },
+ { "VPDAddrChng", 5, 1 },
+ { "BusMstrEn", 4, 1 },
+ { "PMStChng", 3, 1 },
+ { "PEXMsg", 2, 1 },
+ { "ZeroLenRd", 1, 1 },
+ { "PEXErr", 0, 1 },
+ { "PCIE_CFG", 0x88, 0 },
+ { "DMAStopEn", 24, 1 },
+ { "PriorityINTA", 23, 1 },
+ { "IniFullPkt", 22, 1 },
+ { "EnableLinkDwnDRst", 21, 1 },
+ { "EnableLinkDownRst", 20, 1 },
+ { "EnableHotRst", 19, 1 },
+ { "IniWaitForGnt", 18, 1 },
+ { "IniBEDis", 17, 1 },
+ { "CLIDecEn", 16, 1 },
+ { "AsyncIntVec", 11, 5 },
+ { "MaxSplTrnC", 7, 4 },
+ { "MaxSplTrnR", 1, 6 },
+ { "CRstWrmMode", 0, 1 },
+ { "PCIE_MODE", 0x8c, 0 },
+ { "TAR_State", 29, 3 },
+ { "RF_StateIni", 26, 3 },
+ { "CF_StateIni", 23, 3 },
+ { "PIO_StatePL", 20, 3 },
+ { "PIO_StateISC", 18, 2 },
+ { "NumFstTrnSeqRx", 10, 8 },
+ { "LnkCntlState", 2, 8 },
+ { "VC0Up", 1, 1 },
+ { "LnkInitial", 0, 1 },
+ { "PCIE_STAT", 0x90, 0 },
+ { "INI_State", 28, 4 },
+ { "WF_StateIni", 24, 4 },
+ { "PLM_ReqFIFOCnt", 22, 2 },
+ { "ER_ReqFIFOEmpty", 21, 1 },
+ { "WF_RspFIFOEmpty", 20, 1 },
+ { "WF_ReqFIFOEmpty", 19, 1 },
+ { "RF_RspFIFOEmpty", 18, 1 },
+ { "RF_ReqFIFOEmpty", 17, 1 },
+ { "RF_ActEmpty", 16, 1 },
+ { "PIO_RspFIFOCnt", 11, 5 },
+ { "PIO_ReqFIFOCnt", 5, 6 },
+ { "CF_RspFIFOEmpty", 4, 1 },
+ { "CF_ReqFIFOEmpty", 3, 1 },
+ { "CF_ActEmpty", 2, 1 },
+ { "VPD_RspFIFOEmpty", 1, 1 },
+ { "VPD_ReqFIFOEmpty", 0, 1 },
+ { "PCIE_WOL", 0x94, 0 },
+ { "CF_RspState", 12, 2 },
+ { "RF_RspState", 10, 2 },
+ { "PME_State", 7, 3 },
+ { "INT_State", 4, 3 },
+ { "WakeUp1", 3, 1 },
+ { "WakeUp0", 2, 1 },
+ { "SleepMode1", 1, 1 },
+ { "SleepMode0", 0, 1 },
+ { "PCIE_PEX_CTRL0", 0x98, 0 },
+ { "CplTimeoutRetry", 31, 1 },
+ { "StrictTSMN", 30, 1 },
+ { "NumFstTrnSeq", 22, 8 },
+ { "ReplayLmt", 2, 20 },
+ { "TxPndChkEn", 1, 1 },
+ { "CplPndChkEn", 0, 1 },
+ { "PCIE_PEX_CTRL1", 0x9c, 0 },
+ { "RxPhyErrEn", 31, 1 },
+ { "DLLPTimeoutLmt", 13, 18 },
+ { "AckLat", 0, 13 },
+ { "PCIE_PEX_CTRL2", 0xa0, 0 },
+ { "LnkCntlDetDir", 30, 1 },
+ { "EnterL1rEn", 29, 1 },
+ { "PMExitL1Req", 28, 1 },
+ { "PMTxIdle", 27, 1 },
+ { "PCIModeLoop", 26, 1 },
+ { "L1ASPMTxRxL0sTime", 14, 12 },
+ { "L0sIdleTime", 3, 11 },
+ { "EnterL1ASPMEn", 2, 1 },
+ { "EnterL1En", 1, 1 },
+ { "EnterL0sEn", 0, 1 },
+ { "PCIE_PEX_ERR", 0xa4, 0 },
+ { "CplTimeoutID", 18, 7 },
+ { "FlowCtlOFlowErr", 17, 1 },
+ { "ReplayTimeout", 16, 1 },
+ { "ReplayRollover", 15, 1 },
+ { "BadDLLP", 14, 1 },
+ { "DLLPErr", 13, 1 },
+ { "FlowCtlProtErr", 12, 1 },
+ { "CplTimeout", 11, 1 },
+ { "PHYRcvErr", 10, 1 },
+ { "DisTLP", 9, 1 },
+ { "BadECRC", 8, 1 },
+ { "BadTLP", 7, 1 },
+ { "MalTLP", 6, 1 },
+ { "UnxCpl", 5, 1 },
+ { "UnsReq", 4, 1 },
+ { "PsnReq", 3, 1 },
+ { "UnsCpl", 2, 1 },
+ { "CplAbt", 1, 1 },
+ { "PsnCpl", 0, 1 },
+ { "PCIE_SERDES_CTRL", 0xa8, 0 },
+ { "PMASel", 3, 1 },
+ { "Lane", 0, 3 },
+ { "PCIE_SERDES_QUAD_CTRL0", 0xac, 0 },
+ { "TestSig", 10, 19 },
+ { "Offset", 2, 8 },
+ { "OffsetEn", 1, 1 },
+ { "IDDQb", 0, 1 },
+ { "PCIE_SERDES_QUAD_CTRL1", 0xb0, 0 },
+ { "FastInit", 28, 1 },
+ { "CTCDisable", 27, 1 },
+ { "ManResetPLL", 26, 1 },
+ { "ManL2Pwrdn", 25, 1 },
+ { "ManQuadEn", 24, 1 },
+ { "RxEqCtl", 22, 2 },
+ { "HiVMode", 21, 1 },
+ { "RefSel", 19, 2 },
+ { "RxTermAdj", 17, 2 },
+ { "TxTermAdj", 15, 2 },
+ { "Deq", 11, 4 },
+ { "Dtx", 7, 4 },
+ { "LoDrv", 6, 1 },
+ { "HiDrv", 5, 1 },
+ { "IntParReset", 4, 1 },
+ { "IntParLPBK", 3, 1 },
+ { "IntSerLPBKwDrv", 2, 1 },
+ { "PW", 1, 1 },
+ { "PClkDetect", 0, 1 },
+ { "PCIE_SERDES_LANE_CTRL", 0xb4, 0 },
+ { "ExtBISTChkErrClr", 22, 1 },
+ { "ExtBISTChkEn", 21, 1 },
+ { "ExtBISTGenEn", 20, 1 },
+ { "ExtBISTPat", 17, 3 },
+ { "ExtParReset", 16, 1 },
+ { "ExtParLPBK", 15, 1 },
+ { "ManRxTermEn", 14, 1 },
+ { "ManBeaconTxEn", 13, 1 },
+ { "ManRxDetectEn", 12, 1 },
+ { "ManTxIdleEn", 11, 1 },
+ { "ManRxIdleEn", 10, 1 },
+ { "ManL1Pwrdn", 9, 1 },
+ { "ManReset", 8, 1 },
+ { "ManFmOffset", 3, 5 },
+ { "ManFmOffsetEn", 2, 1 },
+ { "ManLaneEn", 1, 1 },
+ { "IntSerLPBK", 0, 1 },
+ { "PCIE_SERDES_LANE_STAT", 0xb8, 0 },
+ { "ExtBISTChkErrCnt", 8, 24 },
+ { "ExtBISTChkFmd", 7, 1 },
+ { "BeaconDetectChg", 6, 1 },
+ { "RxDetectChg", 5, 1 },
+ { "TxIdleDetectChg", 4, 1 },
+ { "BeaconDetect", 2, 1 },
+ { "RxDetect", 1, 1 },
+ { "TxIdleDetect", 0, 1 },
+ { "PCIE_PEX_WMARK", 0xbc, 0 },
+ { "P_WMark", 18, 11 },
+ { "NP_WMark", 11, 7 },
+ { "CPL_WMark", 0, 11 },
+ { NULL }
+};
+
+struct reg_info t3c_t3dbg_regs[] = {
+ { "T3DBG_DBG0_CFG", 0xc0, 0 },
+ { "RegSelect", 9, 8 },
+ { "ModuleSelect", 4, 5 },
+ { "ClkSelect", 0, 4 },
+ { "T3DBG_DBG0_EN", 0xc4, 0 },
+ { "SDRByte0", 8, 1 },
+ { "DDREn", 4, 1 },
+ { "PortEn", 0, 1 },
+ { "T3DBG_DBG1_CFG", 0xc8, 0 },
+ { "RegSelect", 9, 8 },
+ { "ModuleSelect", 4, 5 },
+ { "ClkSelect", 0, 4 },
+ { "T3DBG_DBG1_EN", 0xcc, 0 },
+ { "SDRByte0", 8, 1 },
+ { "DDREn", 4, 1 },
+ { "PortEn", 0, 1 },
+ { "T3DBG_GPIO_EN", 0xd0, 0 },
+ { "GPIO11_OEn", 27, 1 },
+ { "GPIO10_OEn", 26, 1 },
+ { "GPIO9_OEn", 25, 1 },
+ { "GPIO8_OEn", 24, 1 },
+ { "GPIO7_OEn", 23, 1 },
+ { "GPIO6_OEn", 22, 1 },
+ { "GPIO5_OEn", 21, 1 },
+ { "GPIO4_OEn", 20, 1 },
+ { "GPIO3_OEn", 19, 1 },
+ { "GPIO2_OEn", 18, 1 },
+ { "GPIO1_OEn", 17, 1 },
+ { "GPIO0_OEn", 16, 1 },
+ { "GPIO11_Out_Val", 11, 1 },
+ { "GPIO10_Out_Val", 10, 1 },
+ { "GPIO9_Out_Val", 9, 1 },
+ { "GPIO8_Out_Val", 8, 1 },
+ { "GPIO7_Out_Val", 7, 1 },
+ { "GPIO6_Out_Val", 6, 1 },
+ { "GPIO5_Out_Val", 5, 1 },
+ { "GPIO4_Out_Val", 4, 1 },
+ { "GPIO3_Out_Val", 3, 1 },
+ { "GPIO2_Out_Val", 2, 1 },
+ { "GPIO1_Out_Val", 1, 1 },
+ { "GPIO0_Out_Val", 0, 1 },
+ { "T3DBG_GPIO_IN", 0xd4, 0 },
+ { "GPIO11_CHG_DET", 27, 1 },
+ { "GPIO10_CHG_DET", 26, 1 },
+ { "GPIO9_CHG_DET", 25, 1 },
+ { "GPIO8_CHG_DET", 24, 1 },
+ { "GPIO7_CHG_DET", 23, 1 },
+ { "GPIO6_CHG_DET", 22, 1 },
+ { "GPIO5_CHG_DET", 21, 1 },
+ { "GPIO4_CHG_DET", 20, 1 },
+ { "GPIO3_CHG_DET", 19, 1 },
+ { "GPIO2_CHG_DET", 18, 1 },
+ { "GPIO1_CHG_DET", 17, 1 },
+ { "GPIO0_CHG_DET", 16, 1 },
+ { "GPIO11_IN", 11, 1 },
+ { "GPIO10_IN", 10, 1 },
+ { "GPIO9_IN", 9, 1 },
+ { "GPIO8_IN", 8, 1 },
+ { "GPIO7_IN", 7, 1 },
+ { "GPIO6_IN", 6, 1 },
+ { "GPIO5_IN", 5, 1 },
+ { "GPIO4_IN", 4, 1 },
+ { "GPIO3_IN", 3, 1 },
+ { "GPIO2_IN", 2, 1 },
+ { "GPIO1_IN", 1, 1 },
+ { "GPIO0_IN", 0, 1 },
+ { "T3DBG_INT_ENABLE", 0xd8, 0 },
+ { "C_LOCK", 21, 1 },
+ { "M_LOCK", 20, 1 },
+ { "U_LOCK", 19, 1 },
+ { "R_LOCK", 18, 1 },
+ { "PX_LOCK", 17, 1 },
+ { "GPIO11", 11, 1 },
+ { "GPIO10", 10, 1 },
+ { "GPIO9", 9, 1 },
+ { "GPIO8", 8, 1 },
+ { "GPIO7", 7, 1 },
+ { "GPIO6", 6, 1 },
+ { "GPIO5", 5, 1 },
+ { "GPIO4", 4, 1 },
+ { "GPIO3", 3, 1 },
+ { "GPIO2", 2, 1 },
+ { "GPIO1", 1, 1 },
+ { "GPIO0", 0, 1 },
+ { "T3DBG_INT_CAUSE", 0xdc, 0 },
+ { "C_LOCK", 21, 1 },
+ { "M_LOCK", 20, 1 },
+ { "U_LOCK", 19, 1 },
+ { "R_LOCK", 18, 1 },
+ { "PX_LOCK", 17, 1 },
+ { "GPIO11", 11, 1 },
+ { "GPIO10", 10, 1 },
+ { "GPIO9", 9, 1 },
+ { "GPIO8", 8, 1 },
+ { "GPIO7", 7, 1 },
+ { "GPIO6", 6, 1 },
+ { "GPIO5", 5, 1 },
+ { "GPIO4", 4, 1 },
+ { "GPIO3", 3, 1 },
+ { "GPIO2", 2, 1 },
+ { "GPIO1", 1, 1 },
+ { "GPIO0", 0, 1 },
+ { "T3DBG_DBG0_RST_VALUE", 0xe0, 0 },
+ { "DebugData", 0, 8 },
+ { "T3DBG_PLL_OCLK_PAD_EN", 0xe4, 0 },
+ { "PCIE_OCLK_En", 20, 1 },
+ { "PClkTree_DBG_En", 17, 1 },
+ { "PCIX_OCLK_En", 16, 1 },
+ { "U_OCLK_En", 12, 1 },
+ { "R_OCLK_En", 8, 1 },
+ { "M_OCLK_En", 4, 1 },
+ { "C_OCLK_En", 0, 1 },
+ { "T3DBG_PLL_LOCK", 0xe8, 0 },
+ { "PCIX_LOCK", 16, 1 },
+ { "U_LOCK", 12, 1 },
+ { "R_LOCK", 8, 1 },
+ { "M_LOCK", 4, 1 },
+ { "C_LOCK", 0, 1 },
+ { "T3DBG_SERDES_RBC_CFG", 0xec, 0 },
+ { "X_RBC_Lane_Sel", 16, 2 },
+ { "X_RBC_Dbg_En", 12, 1 },
+ { "X_Serdes_Sel", 8, 1 },
+ { "PE_RBC_Lane_Sel", 4, 3 },
+ { "PE_RBC_Dbg_En", 0, 1 },
+ { "T3DBG_GPIO_ACT_LOW", 0xf0, 0 },
+ { "C_LOCK_ACT_LOW", 21, 1 },
+ { "M_LOCK_ACT_LOW", 20, 1 },
+ { "U_LOCK_ACT_LOW", 19, 1 },
+ { "R_LOCK_ACT_LOW", 18, 1 },
+ { "PX_LOCK_ACT_LOW", 17, 1 },
+ { "GPIO11_ACT_LOW", 11, 1 },
+ { "GPIO10_ACT_LOW", 10, 1 },
+ { "GPIO9_ACT_LOW", 9, 1 },
+ { "GPIO8_ACT_LOW", 8, 1 },
+ { "GPIO7_ACT_LOW", 7, 1 },
+ { "GPIO6_ACT_LOW", 6, 1 },
+ { "GPIO5_ACT_LOW", 5, 1 },
+ { "GPIO4_ACT_LOW", 4, 1 },
+ { "GPIO3_ACT_LOW", 3, 1 },
+ { "GPIO2_ACT_LOW", 2, 1 },
+ { "GPIO1_ACT_LOW", 1, 1 },
+ { "GPIO0_ACT_LOW", 0, 1 },
+ { "T3DBG_PMON_CFG", 0xf4, 0 },
+ { "PMON_DONE", 29, 1 },
+ { "PMON_FAIL", 28, 1 },
+ { "PMON_FDEL_AUTO", 22, 6 },
+ { "PMON_CDEL_AUTO", 16, 6 },
+ { "PMON_FDEL_MANUAL", 10, 6 },
+ { "PMON_CDEL_MANUAL", 4, 6 },
+ { "PMON_MANUAL", 1, 1 },
+ { "PMON_AUTO", 0, 1 },
+ { "T3DBG_SERDES_REFCLK_CFG", 0xf8, 0 },
+ { "PE_REFCLK_DBG_EN", 12, 1 },
+ { "X_REFCLK_DBG_EN", 8, 1 },
+ { "PE_REFCLK_TERMADJ", 5, 2 },
+ { "PE_REFCLK_PD", 4, 1 },
+ { "X_REFCLK_TERMADJ", 1, 2 },
+ { "X_REFCLK_PD", 0, 1 },
+ { "T3DBG_PCIE_PMA_BSPIN_CFG", 0xfc, 0 },
+ { "BSModeQuad1", 31, 1 },
+ { "BSInSelLane7", 29, 2 },
+ { "BSEnLane7", 28, 1 },
+ { "BSInSelLane6", 25, 2 },
+ { "BSEnLane6", 24, 1 },
+ { "BSInSelLane5", 21, 2 },
+ { "BSEnLane5", 20, 1 },
+ { "BSInSelLane4", 17, 2 },
+ { "BSEnLane4", 16, 1 },
+ { "BSModeQuad0", 15, 1 },
+ { "BSInSelLane3", 13, 2 },
+ { "BSEnLane3", 12, 1 },
+ { "BSInSelLane2", 9, 2 },
+ { "BSEnLane2", 8, 1 },
+ { "BSInSelLane1", 5, 2 },
+ { "BSEnLane1", 4, 1 },
+ { "BSInSelLane0", 1, 2 },
+ { "BSEnLane0", 0, 1 },
+ { NULL }
+};
+
+struct reg_info t3c_mc7_pmrx_regs[] = {
+ { "MC7_CFG", 0x100, 0 },
+ { "ImpSetUpdate", 14, 1 },
+ { "IFEn", 13, 1 },
+ { "TERM300", 12, 1 },
+ { "TERM150", 11, 1 },
+ { "Slow", 10, 1 },
+ { "Width", 8, 2 },
+ { "ODTEn", 7, 1 },
+ { "Bks", 6, 1 },
+ { "Org", 5, 1 },
+ { "Den", 2, 3 },
+ { "Rdy", 1, 1 },
+ { "ClkEn", 0, 1 },
+ { "MC7_MODE", 0x104, 0 },
+ { "Busy", 31, 1 },
+ { "Mode", 0, 16 },
+ { "MC7_EXT_MODE1", 0x108, 0 },
+ { "Busy", 31, 1 },
+ { "OCDAdjustMode", 20, 1 },
+ { "OCDCode", 16, 4 },
+ { "ExtMode1", 0, 16 },
+ { "MC7_EXT_MODE2", 0x10c, 0 },
+ { "Busy", 31, 1 },
+ { "ExtMode2", 0, 16 },
+ { "MC7_EXT_MODE3", 0x110, 0 },
+ { "Busy", 31, 1 },
+ { "ExtMode3", 0, 16 },
+ { "MC7_PRE", 0x114, 0 },
+ { "Busy", 31, 1 },
+ { "MC7_REF", 0x118, 0 },
+ { "Busy", 31, 1 },
+ { "PreRefDiv", 1, 14 },
+ { "PerRefEn", 0, 1 },
+ { "MC7_DLL", 0x11c, 0 },
+ { "DLLLock", 31, 1 },
+ { "DLLDelta", 24, 7 },
+ { "ManDelta", 3, 7 },
+ { "DLLDeltaSel", 2, 1 },
+ { "DLLEnb", 1, 1 },
+ { "DLLRst", 0, 1 },
+ { "MC7_PARM", 0x120, 0 },
+ { "ActToPreDly", 26, 4 },
+ { "ActToRdWrDly", 23, 3 },
+ { "PreCyc", 20, 3 },
+ { "RefCyc", 13, 7 },
+ { "BkCyc", 8, 5 },
+ { "WrToRdDly", 4, 4 },
+ { "RdToWrDly", 0, 4 },
+ { "MC7_HWM_WRR", 0x124, 0 },
+ { "MEM_HWM", 26, 6 },
+ { "ULP_HWM", 22, 4 },
+ { "TOT_RLD_WT", 14, 8 },
+ { "MEM_RLD_WT", 7, 7 },
+ { "ULP_RLD_WT", 0, 7 },
+ { "MC7_CAL", 0x128, 0 },
+ { "BUSY", 31, 1 },
+ { "CAL_FAULT", 30, 1 },
+ { "PER_CAL_DIV", 22, 8 },
+ { "PER_CAL_EN", 21, 1 },
+ { "SGL_CAL_EN", 20, 1 },
+ { "IMP_UPD_MODE", 19, 1 },
+ { "IMP_SEL", 18, 1 },
+ { "IMP_MAN_PD", 15, 3 },
+ { "IMP_MAN_PU", 12, 3 },
+ { "IMP_CAL_PD", 9, 3 },
+ { "IMP_CAL_PU", 6, 3 },
+ { "IMP_SET_PD", 3, 3 },
+ { "IMP_SET_PU", 0, 3 },
+ { "MC7_ERR_ADDR", 0x12c, 0 },
+ { "ErrAddress", 3, 29 },
+ { "ErrAgent", 1, 2 },
+ { "ErrOp", 0, 1 },
+ { "MC7_ECC", 0x130, 0 },
+ { "UECnt", 10, 8 },
+ { "CECnt", 2, 8 },
+ { "ECCChkEn", 1, 1 },
+ { "ECCGenEn", 0, 1 },
+ { "MC7_CE_ADDR", 0x134, 0 },
+ { "MC7_CE_DATA0", 0x138, 0 },
+ { "MC7_CE_DATA1", 0x13c, 0 },
+ { "MC7_CE_DATA2", 0x140, 0 },
+ { "Data", 0, 8 },
+ { "MC7_UE_ADDR", 0x144, 0 },
+ { "MC7_UE_DATA0", 0x148, 0 },
+ { "MC7_UE_DATA1", 0x14c, 0 },
+ { "MC7_UE_DATA2", 0x150, 0 },
+ { "Data", 0, 8 },
+ { "MC7_BD_ADDR", 0x154, 0 },
+ { "Addr", 3, 29 },
+ { "MC7_BD_DATA0", 0x158, 0 },
+ { "MC7_BD_DATA1", 0x15c, 0 },
+ { "MC7_BD_DATA2", 0x160, 0 },
+ { "Data", 0, 8 },
+ { "MC7_BD_OP", 0x164, 0 },
+ { "Busy", 31, 1 },
+ { "Op", 0, 1 },
+ { "MC7_BIST_ADDR_BEG", 0x168, 0 },
+ { "AddrBeg", 5, 27 },
+ { "MC7_BIST_ADDR_END", 0x16c, 0 },
+ { "AddrEnd", 5, 27 },
+ { "MC7_BIST_DATA", 0x170, 0 },
+ { "MC7_BIST_OP", 0x174, 0 },
+ { "Busy", 31, 1 },
+ { "Gap", 4, 5 },
+ { "Cont", 3, 1 },
+ { "DataPat", 1, 2 },
+ { "Op", 0, 1 },
+ { "MC7_INT_ENABLE", 0x178, 0 },
+ { "AE", 17, 1 },
+ { "PE", 2, 15 },
+ { "UE", 1, 1 },
+ { "CE", 0, 1 },
+ { "MC7_INT_CAUSE", 0x17c, 0 },
+ { "AE", 17, 1 },
+ { "PE", 2, 15 },
+ { "UE", 1, 1 },
+ { "CE", 0, 1 },
+ { NULL }
+};
+
+struct reg_info t3c_mc7_pmtx_regs[] = {
+ { "MC7_CFG", 0x180, 0 },
+ { "ImpSetUpdate", 14, 1 },
+ { "IFEn", 13, 1 },
+ { "TERM300", 12, 1 },
+ { "TERM150", 11, 1 },
+ { "Slow", 10, 1 },
+ { "Width", 8, 2 },
+ { "ODTEn", 7, 1 },
+ { "Bks", 6, 1 },
+ { "Org", 5, 1 },
+ { "Den", 2, 3 },
+ { "Rdy", 1, 1 },
+ { "ClkEn", 0, 1 },
+ { "MC7_MODE", 0x184, 0 },
+ { "Busy", 31, 1 },
+ { "Mode", 0, 16 },
+ { "MC7_EXT_MODE1", 0x188, 0 },
+ { "Busy", 31, 1 },
+ { "OCDAdjustMode", 20, 1 },
+ { "OCDCode", 16, 4 },
+ { "ExtMode1", 0, 16 },
+ { "MC7_EXT_MODE2", 0x18c, 0 },
+ { "Busy", 31, 1 },
+ { "ExtMode2", 0, 16 },
+ { "MC7_EXT_MODE3", 0x190, 0 },
+ { "Busy", 31, 1 },
+ { "ExtMode3", 0, 16 },
+ { "MC7_PRE", 0x194, 0 },
+ { "Busy", 31, 1 },
+ { "MC7_REF", 0x198, 0 },
+ { "Busy", 31, 1 },
+ { "PreRefDiv", 1, 14 },
+ { "PerRefEn", 0, 1 },
+ { "MC7_DLL", 0x19c, 0 },
+ { "DLLLock", 31, 1 },
+ { "DLLDelta", 24, 7 },
+ { "ManDelta", 3, 7 },
+ { "DLLDeltaSel", 2, 1 },
+ { "DLLEnb", 1, 1 },
+ { "DLLRst", 0, 1 },
+ { "MC7_PARM", 0x1a0, 0 },
+ { "ActToPreDly", 26, 4 },
+ { "ActToRdWrDly", 23, 3 },
+ { "PreCyc", 20, 3 },
+ { "RefCyc", 13, 7 },
+ { "BkCyc", 8, 5 },
+ { "WrToRdDly", 4, 4 },
+ { "RdToWrDly", 0, 4 },
+ { "MC7_HWM_WRR", 0x1a4, 0 },
+ { "MEM_HWM", 26, 6 },
+ { "ULP_HWM", 22, 4 },
+ { "TOT_RLD_WT", 14, 8 },
+ { "MEM_RLD_WT", 7, 7 },
+ { "ULP_RLD_WT", 0, 7 },
+ { "MC7_CAL", 0x1a8, 0 },
+ { "BUSY", 31, 1 },
+ { "CAL_FAULT", 30, 1 },
+ { "PER_CAL_DIV", 22, 8 },
+ { "PER_CAL_EN", 21, 1 },
+ { "SGL_CAL_EN", 20, 1 },
+ { "IMP_UPD_MODE", 19, 1 },
+ { "IMP_SEL", 18, 1 },
+ { "IMP_MAN_PD", 15, 3 },
+ { "IMP_MAN_PU", 12, 3 },
+ { "IMP_CAL_PD", 9, 3 },
+ { "IMP_CAL_PU", 6, 3 },
+ { "IMP_SET_PD", 3, 3 },
+ { "IMP_SET_PU", 0, 3 },
+ { "MC7_ERR_ADDR", 0x1ac, 0 },
+ { "ErrAddress", 3, 29 },
+ { "ErrAgent", 1, 2 },
+ { "ErrOp", 0, 1 },
+ { "MC7_ECC", 0x1b0, 0 },
+ { "UECnt", 10, 8 },
+ { "CECnt", 2, 8 },
+ { "ECCChkEn", 1, 1 },
+ { "ECCGenEn", 0, 1 },
+ { "MC7_CE_ADDR", 0x1b4, 0 },
+ { "MC7_CE_DATA0", 0x1b8, 0 },
+ { "MC7_CE_DATA1", 0x1bc, 0 },
+ { "MC7_CE_DATA2", 0x1c0, 0 },
+ { "Data", 0, 8 },
+ { "MC7_UE_ADDR", 0x1c4, 0 },
+ { "MC7_UE_DATA0", 0x1c8, 0 },
+ { "MC7_UE_DATA1", 0x1cc, 0 },
+ { "MC7_UE_DATA2", 0x1d0, 0 },
+ { "Data", 0, 8 },
+ { "MC7_BD_ADDR", 0x1d4, 0 },
+ { "Addr", 3, 29 },
+ { "MC7_BD_DATA0", 0x1d8, 0 },
+ { "MC7_BD_DATA1", 0x1dc, 0 },
+ { "MC7_BD_DATA2", 0x1e0, 0 },
+ { "Data", 0, 8 },
+ { "MC7_BD_OP", 0x1e4, 0 },
+ { "Busy", 31, 1 },
+ { "Op", 0, 1 },
+ { "MC7_BIST_ADDR_BEG", 0x1e8, 0 },
+ { "AddrBeg", 5, 27 },
+ { "MC7_BIST_ADDR_END", 0x1ec, 0 },
+ { "AddrEnd", 5, 27 },
+ { "MC7_BIST_DATA", 0x1f0, 0 },
+ { "MC7_BIST_OP", 0x1f4, 0 },
+ { "Busy", 31, 1 },
+ { "Gap", 4, 5 },
+ { "Cont", 3, 1 },
+ { "DataPat", 1, 2 },
+ { "Op", 0, 1 },
+ { "MC7_INT_ENABLE", 0x1f8, 0 },
+ { "AE", 17, 1 },
+ { "PE", 2, 15 },
+ { "UE", 1, 1 },
+ { "CE", 0, 1 },
+ { "MC7_INT_CAUSE", 0x1fc, 0 },
+ { "AE", 17, 1 },
+ { "PE", 2, 15 },
+ { "UE", 1, 1 },
+ { "CE", 0, 1 },
+ { NULL }
+};
+
+struct reg_info t3c_mc7_cm_regs[] = {
+ { "MC7_CFG", 0x200, 0 },
+ { "ImpSetUpdate", 14, 1 },
+ { "IFEn", 13, 1 },
+ { "TERM300", 12, 1 },
+ { "TERM150", 11, 1 },
+ { "Slow", 10, 1 },
+ { "Width", 8, 2 },
+ { "ODTEn", 7, 1 },
+ { "Bks", 6, 1 },
+ { "Org", 5, 1 },
+ { "Den", 2, 3 },
+ { "Rdy", 1, 1 },
+ { "ClkEn", 0, 1 },
+ { "MC7_MODE", 0x204, 0 },
+ { "Busy", 31, 1 },
+ { "Mode", 0, 16 },
+ { "MC7_EXT_MODE1", 0x208, 0 },
+ { "Busy", 31, 1 },
+ { "OCDAdjustMode", 20, 1 },
+ { "OCDCode", 16, 4 },
+ { "ExtMode1", 0, 16 },
+ { "MC7_EXT_MODE2", 0x20c, 0 },
+ { "Busy", 31, 1 },
+ { "ExtMode2", 0, 16 },
+ { "MC7_EXT_MODE3", 0x210, 0 },
+ { "Busy", 31, 1 },
+ { "ExtMode3", 0, 16 },
+ { "MC7_PRE", 0x214, 0 },
+ { "Busy", 31, 1 },
+ { "MC7_REF", 0x218, 0 },
+ { "Busy", 31, 1 },
+ { "PreRefDiv", 1, 14 },
+ { "PerRefEn", 0, 1 },
+ { "MC7_DLL", 0x21c, 0 },
+ { "DLLLock", 31, 1 },
+ { "DLLDelta", 24, 7 },
+ { "ManDelta", 3, 7 },
+ { "DLLDeltaSel", 2, 1 },
+ { "DLLEnb", 1, 1 },
+ { "DLLRst", 0, 1 },
+ { "MC7_PARM", 0x220, 0 },
+ { "ActToPreDly", 26, 4 },
+ { "ActToRdWrDly", 23, 3 },
+ { "PreCyc", 20, 3 },
+ { "RefCyc", 13, 7 },
+ { "BkCyc", 8, 5 },
+ { "WrToRdDly", 4, 4 },
+ { "RdToWrDly", 0, 4 },
+ { "MC7_HWM_WRR", 0x224, 0 },
+ { "MEM_HWM", 26, 6 },
+ { "ULP_HWM", 22, 4 },
+ { "TOT_RLD_WT", 14, 8 },
+ { "MEM_RLD_WT", 7, 7 },
+ { "ULP_RLD_WT", 0, 7 },
+ { "MC7_CAL", 0x228, 0 },
+ { "BUSY", 31, 1 },
+ { "CAL_FAULT", 30, 1 },
+ { "PER_CAL_DIV", 22, 8 },
+ { "PER_CAL_EN", 21, 1 },
+ { "SGL_CAL_EN", 20, 1 },
+ { "IMP_UPD_MODE", 19, 1 },
+ { "IMP_SEL", 18, 1 },
+ { "IMP_MAN_PD", 15, 3 },
+ { "IMP_MAN_PU", 12, 3 },
+ { "IMP_CAL_PD", 9, 3 },
+ { "IMP_CAL_PU", 6, 3 },
+ { "IMP_SET_PD", 3, 3 },
+ { "IMP_SET_PU", 0, 3 },
+ { "MC7_ERR_ADDR", 0x22c, 0 },
+ { "ErrAddress", 3, 29 },
+ { "ErrAgent", 1, 2 },
+ { "ErrOp", 0, 1 },
+ { "MC7_ECC", 0x230, 0 },
+ { "UECnt", 10, 8 },
+ { "CECnt", 2, 8 },
+ { "ECCChkEn", 1, 1 },
+ { "ECCGenEn", 0, 1 },
+ { "MC7_CE_ADDR", 0x234, 0 },
+ { "MC7_CE_DATA0", 0x238, 0 },
+ { "MC7_CE_DATA1", 0x23c, 0 },
+ { "MC7_CE_DATA2", 0x240, 0 },
+ { "Data", 0, 8 },
+ { "MC7_UE_ADDR", 0x244, 0 },
+ { "MC7_UE_DATA0", 0x248, 0 },
+ { "MC7_UE_DATA1", 0x24c, 0 },
+ { "MC7_UE_DATA2", 0x250, 0 },
+ { "Data", 0, 8 },
+ { "MC7_BD_ADDR", 0x254, 0 },
+ { "Addr", 3, 29 },
+ { "MC7_BD_DATA0", 0x258, 0 },
+ { "MC7_BD_DATA1", 0x25c, 0 },
+ { "MC7_BD_DATA2", 0x260, 0 },
+ { "Data", 0, 8 },
+ { "MC7_BD_OP", 0x264, 0 },
+ { "Busy", 31, 1 },
+ { "Op", 0, 1 },
+ { "MC7_BIST_ADDR_BEG", 0x268, 0 },
+ { "AddrBeg", 5, 27 },
+ { "MC7_BIST_ADDR_END", 0x26c, 0 },
+ { "AddrEnd", 5, 27 },
+ { "MC7_BIST_DATA", 0x270, 0 },
+ { "MC7_BIST_OP", 0x274, 0 },
+ { "Busy", 31, 1 },
+ { "Gap", 4, 5 },
+ { "Cont", 3, 1 },
+ { "DataPat", 1, 2 },
+ { "Op", 0, 1 },
+ { "MC7_INT_ENABLE", 0x278, 0 },
+ { "AE", 17, 1 },
+ { "PE", 2, 15 },
+ { "UE", 1, 1 },
+ { "CE", 0, 1 },
+ { "MC7_INT_CAUSE", 0x27c, 0 },
+ { "AE", 17, 1 },
+ { "PE", 2, 15 },
+ { "UE", 1, 1 },
+ { "CE", 0, 1 },
+ { NULL }
+};
+
+struct reg_info t3c_cim_regs[] = {
+ { "CIM_BOOT_CFG", 0x280, 0 },
+ { "BootAddr", 2, 30 },
+ { "BootSdram", 1, 1 },
+ { "uPCRst", 0, 1 },
+ { "CIM_FLASH_BASE_ADDR", 0x284, 0 },
+ { "FlashBaseAddr", 2, 22 },
+ { "CIM_FLASH_ADDR_SIZE", 0x288, 0 },
+ { "FlashAddrSize", 2, 22 },
+ { "CIM_SDRAM_BASE_ADDR", 0x28c, 0 },
+ { "SdramBaseAddr", 2, 30 },
+ { "CIM_SDRAM_ADDR_SIZE", 0x290, 0 },
+ { "SdramAddrSize", 2, 30 },
+ { "CIM_UP_SPARE_INT", 0x294, 0 },
+ { "uPSpareInt", 0, 3 },
+ { "CIM_HOST_INT_ENABLE", 0x298, 0 },
+ { "DTagParErr", 28, 1 },
+ { "ITagParErr", 27, 1 },
+ { "IBQTPParErr", 26, 1 },
+ { "IBQULPParErr", 25, 1 },
+ { "IBQSGEHIParErr", 24, 1 },
+ { "IBQSGELOParErr", 23, 1 },
+ { "OBQULPLOParErr", 22, 1 },
+ { "OBQULPHIParErr", 21, 1 },
+ { "OBQSGEParErr", 20, 1 },
+ { "DCacheParErr", 19, 1 },
+ { "ICacheParErr", 18, 1 },
+ { "DRamParErr", 17, 1 },
+ { "Timer1IntEn", 15, 1 },
+ { "Timer0IntEn", 14, 1 },
+ { "PrefDropIntEn", 13, 1 },
+ { "BlkWrPlIntEn", 12, 1 },
+ { "BlkRdPlIntEn", 11, 1 },
+ { "BlkWrCtlIntEn", 10, 1 },
+ { "BlkRdCtlIntEn", 9, 1 },
+ { "BlkWrFlashIntEn", 8, 1 },
+ { "BlkRdFlashIntEn", 7, 1 },
+ { "SglWrFlashIntEn", 6, 1 },
+ { "WrBlkFlashIntEn", 5, 1 },
+ { "BlkWrBootIntEn", 4, 1 },
+ { "BlkRdBootIntEn", 3, 1 },
+ { "FlashRangeIntEn", 2, 1 },
+ { "SdramRangeIntEn", 1, 1 },
+ { "RsvdSpaceIntEn", 0, 1 },
+ { "CIM_HOST_INT_CAUSE", 0x29c, 0 },
+ { "DTagParErr", 28, 1 },
+ { "ITagParErr", 27, 1 },
+ { "IBQTPParErr", 26, 1 },
+ { "IBQULPParErr", 25, 1 },
+ { "IBQSGEHIParErr", 24, 1 },
+ { "IBQSGELOParErr", 23, 1 },
+ { "OBQULPLOParErr", 22, 1 },
+ { "OBQULPHIParErr", 21, 1 },
+ { "OBQSGEParErr", 20, 1 },
+ { "DCacheParErr", 19, 1 },
+ { "ICacheParErr", 18, 1 },
+ { "DRamParErr", 17, 1 },
+ { "Timer1Int", 15, 1 },
+ { "Timer0Int", 14, 1 },
+ { "PrefDropInt", 13, 1 },
+ { "BlkWrPlInt", 12, 1 },
+ { "BlkRdPlInt", 11, 1 },
+ { "BlkWrCtlInt", 10, 1 },
+ { "BlkRdCtlInt", 9, 1 },
+ { "BlkWrFlashInt", 8, 1 },
+ { "BlkRdFlashInt", 7, 1 },
+ { "SglWrFlashInt", 6, 1 },
+ { "WrBlkFlashInt", 5, 1 },
+ { "BlkWrBootInt", 4, 1 },
+ { "BlkRdBootInt", 3, 1 },
+ { "FlashRangeInt", 2, 1 },
+ { "SdramRangeInt", 1, 1 },
+ { "RsvdSpaceInt", 0, 1 },
+ { "CIM_UP_INT_ENABLE", 0x2a0, 0 },
+ { "DTagParErr", 28, 1 },
+ { "ITagParErr", 27, 1 },
+ { "IBQTPParErr", 26, 1 },
+ { "IBQULPParErr", 25, 1 },
+ { "IBQSGEHIParErr", 24, 1 },
+ { "IBQSGELOParErr", 23, 1 },
+ { "OBQULPLOParErr", 22, 1 },
+ { "OBQULPHIParErr", 21, 1 },
+ { "OBQSGEParErr", 20, 1 },
+ { "DCacheParErr", 19, 1 },
+ { "ICacheParErr", 18, 1 },
+ { "DRamParErr", 17, 1 },
+ { "MstPlIntEn", 16, 1 },
+ { "Timer1IntEn", 15, 1 },
+ { "Timer0IntEn", 14, 1 },
+ { "PrefDropIntEn", 13, 1 },
+ { "BlkWrPlIntEn", 12, 1 },
+ { "BlkRdPlIntEn", 11, 1 },
+ { "BlkWrCtlIntEn", 10, 1 },
+ { "BlkRdCtlIntEn", 9, 1 },
+ { "BlkWrFlashIntEn", 8, 1 },
+ { "BlkRdFlashIntEn", 7, 1 },
+ { "SglWrFlashIntEn", 6, 1 },
+ { "WrBlkFlashIntEn", 5, 1 },
+ { "BlkWrBootIntEn", 4, 1 },
+ { "BlkRdBootIntEn", 3, 1 },
+ { "FlashRangeIntEn", 2, 1 },
+ { "SdramRangeIntEn", 1, 1 },
+ { "RsvdSpaceIntEn", 0, 1 },
+ { "CIM_UP_INT_CAUSE", 0x2a4, 0 },
+ { "DTagParErr", 28, 1 },
+ { "ITagParErr", 27, 1 },
+ { "IBQTPParErr", 26, 1 },
+ { "IBQULPParErr", 25, 1 },
+ { "IBQSGEHIParErr", 24, 1 },
+ { "IBQSGELOParErr", 23, 1 },
+ { "OBQULPLOParErr", 22, 1 },
+ { "OBQULPHIParErr", 21, 1 },
+ { "OBQSGEParErr", 20, 1 },
+ { "DCacheParErr", 19, 1 },
+ { "ICacheParErr", 18, 1 },
+ { "DRamParErr", 17, 1 },
+ { "MstPlInt", 16, 1 },
+ { "Timer1Int", 15, 1 },
+ { "Timer0Int", 14, 1 },
+ { "PrefDropInt", 13, 1 },
+ { "BlkWrPlInt", 12, 1 },
+ { "BlkRdPlInt", 11, 1 },
+ { "BlkWrCtlInt", 10, 1 },
+ { "BlkRdCtlInt", 9, 1 },
+ { "BlkWrFlashInt", 8, 1 },
+ { "BlkRdFlashInt", 7, 1 },
+ { "SglWrFlashInt", 6, 1 },
+ { "WrBlkFlashInt", 5, 1 },
+ { "BlkWrBootInt", 4, 1 },
+ { "BlkRdBootInt", 3, 1 },
+ { "FlashRangeInt", 2, 1 },
+ { "SdramRangeInt", 1, 1 },
+ { "RsvdSpaceInt", 0, 1 },
+ { "CIM_IBQ_FULLA_THRSH", 0x2a8, 0 },
+ { "Ibq0FullThrsh", 0, 9 },
+ { "Ibq1FullThrsh", 16, 9 },
+ { "CIM_IBQ_FULLB_THRSH", 0x2ac, 0 },
+ { "Ibq2FullThrsh", 0, 9 },
+ { "Ibq3FullThrsh", 16, 9 },
+ { "CIM_HOST_ACC_CTRL", 0x2b0, 0 },
+ { "HostBusy", 17, 1 },
+ { "HostWrite", 16, 1 },
+ { "HostAddr", 0, 16 },
+ { "CIM_HOST_ACC_DATA", 0x2b4, 0 },
+ { "CIM_IBQ_DBG_CFG", 0x2c0, 0 },
+ { "IbqDbgAddr", 16, 9 },
+ { "IbqDbgQID", 3, 2 },
+ { "IbqDbgWr", 2, 1 },
+ { "IbqDbgBusy", 1, 1 },
+ { "IbqDbgEn", 0, 1 },
+ { "CIM_OBQ_DBG_CFG", 0x2c4, 0 },
+ { "ObqDbgAddr", 16, 9 },
+ { "ObqDbgQID", 3, 2 },
+ { "ObqDbgWr", 2, 1 },
+ { "ObqDbgBusy", 1, 1 },
+ { "ObqDbgEn", 0, 1 },
+ { "CIM_IBQ_DBG_DATA", 0x2c8, 0 },
+ { "CIM_OBQ_DBG_DATA", 0x2cc, 0 },
+ { "CIM_CDEBUGDATA", 0x2d0, 0 },
+ { "CDebugDataH", 16, 16 },
+ { "CDebugDataL", 0, 16 },
+ { "CIM_DEBUGCFG", 0x2e0, 0 },
+ { "POLADbgRdPtr", 23, 9 },
+ { "PILADbgRdPtr", 14, 9 },
+ { "LADbgEn", 12, 1 },
+ { "DebugSelH", 5, 5 },
+ { "DebugSelL", 0, 5 },
+ { "CIM_DEBUGSTS", 0x2e4, 0 },
+ { "POLADbgWrPtr", 16, 9 },
+ { "PILADbgWrPtr", 0, 9 },
+ { "CIM_PO_LA_DEBUGDATA", 0x2e8, 0 },
+ { "CIM_PI_LA_DEBUGDATA", 0x2ec, 0 },
+ { NULL }
+};
+
+struct reg_info t3c_tp1_regs[] = {
+ { "TP_IN_CONFIG", 0x300, 0 },
+ { "RXFbArbPrio", 25, 1 },
+ { "TXFbArbPrio", 24, 1 },
+ { "DBMaxOpCnt", 16, 8 },
+ { "IPv6Enable", 15, 1 },
+ { "NICMode", 14, 1 },
+ { "EChecksumCheckTCP", 13, 1 },
+ { "EChecksumCheckIP", 12, 1 },
+ { "ECPL", 10, 1 },
+ { "EEthernet", 8, 1 },
+ { "ETunnel", 7, 1 },
+ { "CChecksumCheckTCP", 6, 1 },
+ { "CChecksumCheckIP", 5, 1 },
+ { "CCPL", 3, 1 },
+ { "CEthernet", 1, 1 },
+ { "CTunnel", 0, 1 },
+ { "TP_OUT_CONFIG", 0x304, 0 },
+ { "IPIDSplitMode", 16, 1 },
+ { "VLANExtractionEnable2ndPort", 13, 1 },
+ { "VLANExtractionEnable", 12, 1 },
+ { "EChecksumGenerateTCP", 11, 1 },
+ { "EChecksumGenerateIP", 10, 1 },
+ { "ECPL", 8, 1 },
+ { "EEthernet", 6, 1 },
+ { "CChecksumGenerateTCP", 5, 1 },
+ { "CChecksumGenerateIP", 4, 1 },
+ { "CCPL", 2, 1 },
+ { "CEthernet", 0, 1 },
+ { "TP_GLOBAL_CONFIG", 0x308, 0 },
+ { "SYNCookieParams", 26, 6 },
+ { "RXFlowControlDisable", 25, 1 },
+ { "TXPacingEnable", 24, 1 },
+ { "AttackFilterEnable", 23, 1 },
+ { "SYNCookieNoOptions", 22, 1 },
+ { "ProtectedMode", 21, 1 },
+ { "PingDrop", 20, 1 },
+ { "FragmentDrop", 19, 1 },
+ { "FiveTupleLookup", 17, 2 },
+ { "PathMTU", 15, 1 },
+ { "IPIdentSplit", 14, 1 },
+ { "IPChecksumOffload", 13, 1 },
+ { "UDPChecksumOffload", 12, 1 },
+ { "TCPChecksumOffload", 11, 1 },
+ { "QOSMapping", 10, 1 },
+ { "TCAMServerUse", 8, 2 },
+ { "IPTTL", 0, 8 },
+ { "TP_GLOBAL_RX_CREDIT", 0x30c, 0 },
+ { "TP_CMM_SIZE", 0x310, 0 },
+ { "CMMemMgrSize", 0, 28 },
+ { "TP_CMM_MM_BASE", 0x314, 0 },
+ { "CMMemMgrBase", 0, 28 },
+ { "TP_CMM_TIMER_BASE", 0x318, 0 },
+ { "CMTimerMaxNum", 28, 2 },
+ { "CMTimerBase", 0, 28 },
+ { "TP_PMM_SIZE", 0x31c, 0 },
+ { "PMSize", 0, 28 },
+ { "TP_PMM_TX_BASE", 0x320, 0 },
+ { "TP_PMM_DEFRAG_BASE", 0x324, 0 },
+ { "TP_PMM_RX_BASE", 0x328, 0 },
+ { "TP_PMM_RX_PAGE_SIZE", 0x32c, 0 },
+ { "TP_PMM_RX_MAX_PAGE", 0x330, 0 },
+ { "PMRxMaxPage", 0, 21 },
+ { "TP_PMM_TX_PAGE_SIZE", 0x334, 0 },
+ { "TP_PMM_TX_MAX_PAGE", 0x338, 0 },
+ { "PMTxMaxPage", 0, 21 },
+ { "TP_TCP_OPTIONS", 0x340, 0 },
+ { "MTUDefault", 16, 16 },
+ { "MTUEnable", 10, 1 },
+ { "SACKTx", 9, 1 },
+ { "SACKRx", 8, 1 },
+ { "SACKMode", 4, 2 },
+ { "WindowScaleMode", 2, 2 },
+ { "TimestampsMode", 0, 2 },
+ { "TP_DACK_CONFIG", 0x344, 0 },
+ { "AutoState3", 30, 2 },
+ { "AutoState2", 28, 2 },
+ { "AutoState1", 26, 2 },
+ { "ByteThreshold", 5, 20 },
+ { "MSSThreshold", 3, 2 },
+ { "AutoCareful", 2, 1 },
+ { "AutoEnable", 1, 1 },
+ { "Mode", 0, 1 },
+ { "TP_PC_CONFIG", 0x348, 0 },
+ { "CMCacheDisable", 31, 1 },
+ { "EnableOcspiFull", 30, 1 },
+ { "EnableFLMErrorDDP", 29, 1 },
+ { "LockTid", 28, 1 },
+ { "FixRcvWnd", 27, 1 },
+ { "TxTosQueueMapMode", 26, 1 },
+ { "RddpCongEn", 25, 1 },
+ { "EnableOnFlyPDU", 24, 1 },
+ { "EnableEPCMDAFull", 23, 1 },
+ { "ModulateUnionMode", 22, 1 },
+ { "TxDataAckRateEnable", 21, 1 },
+ { "TxDeferEnable", 20, 1 },
+ { "RxCongestionMode", 19, 1 },
+ { "HearbeatOnceDACK", 18, 1 },
+ { "HearbeatOnceHeap", 17, 1 },
+ { "HearbeatDACK", 16, 1 },
+ { "TxCongestionMode", 15, 1 },
+ { "AcceptLatestRcvAdv", 14, 1 },
+ { "DisableSYNData", 13, 1 },
+ { "DisableWindowPSH", 12, 1 },
+ { "DisableFINOldData", 11, 1 },
+ { "EnableFLMError", 10, 1 },
+ { "DisableNextMtu", 9, 1 },
+ { "FilterPeerFIN", 8, 1 },
+ { "EnableFeedbackSend", 7, 1 },
+ { "EnableRDMAError", 6, 1 },
+ { "EnableDDPFlowControl", 5, 1 },
+ { "DisableHeldFIN", 4, 1 },
+ { "TableLatencyDelta", 0, 4 },
+ { "TP_PC_CONFIG2", 0x34c, 0 },
+ { "DisbleDaParbit0", 15, 1 },
+ { "EnableArpMiss", 13, 1 },
+ { "EnableNonOfdTnlSyn", 12, 1 },
+ { "EnableIPv6RSS", 11, 1 },
+ { "EnableDropRQEmptyPkt", 10, 1 },
+ { "EnableTxPortfromDA2", 9, 1 },
+ { "EnableRxPktTmstpRss", 8, 1 },
+ { "EnableSndUnaInRxData", 7, 1 },
+ { "EnableRxPortFromAddr", 6, 1 },
+ { "EnableTxPortfromDA", 5, 1 },
+ { "EnableCHdrAFull", 4, 1 },
+ { "EnableNonOfdScbBit", 3, 1 },
+ { "EnableNonOfdTidRss", 2, 1 },
+ { "EnableNonOfdTcbRss", 1, 1 },
+ { "EnableOldRxForward", 0, 1 },
+ { "TP_TCP_BACKOFF_REG0", 0x350, 0 },
+ { "TimerBackoffIndex3", 24, 8 },
+ { "TimerBackoffIndex2", 16, 8 },
+ { "TimerBackoffIndex1", 8, 8 },
+ { "TimerBackoffIndex0", 0, 8 },
+ { "TP_TCP_BACKOFF_REG1", 0x354, 0 },
+ { "TimerBackoffIndex7", 24, 8 },
+ { "TimerBackoffIndex6", 16, 8 },
+ { "TimerBackoffIndex5", 8, 8 },
+ { "TimerBackoffIndex4", 0, 8 },
+ { "TP_TCP_BACKOFF_REG2", 0x358, 0 },
+ { "TimerBackoffIndex11", 24, 8 },
+ { "TimerBackoffIndex10", 16, 8 },
+ { "TimerBackoffIndex9", 8, 8 },
+ { "TimerBackoffIndex8", 0, 8 },
+ { "TP_TCP_BACKOFF_REG3", 0x35c, 0 },
+ { "TimerBackoffIndex15", 24, 8 },
+ { "TimerBackoffIndex14", 16, 8 },
+ { "TimerBackoffIndex13", 8, 8 },
+ { "TimerBackoffIndex12", 0, 8 },
+ { "TP_PARA_REG0", 0x360, 0 },
+ { "InitCwnd", 24, 3 },
+ { "DupAckThresh", 20, 4 },
+ { "TP_PARA_REG1", 0x364, 0 },
+ { "InitRwnd", 16, 16 },
+ { "InitialSSThresh", 0, 16 },
+ { "TP_PARA_REG2", 0x368, 0 },
+ { "MaxRxData", 16, 16 },
+ { "RxCoalesceSize", 0, 16 },
+ { "TP_PARA_REG3", 0x36c, 0 },
+ { "TunnelCngDrop1", 21, 1 },
+ { "TunnelCngDrop0", 20, 1 },
+ { "TxDataAckIdx", 16, 4 },
+ { "RxFragEnable", 12, 3 },
+ { "TxPaceFixedStrict", 11, 1 },
+ { "TxPaceAutoStrict", 10, 1 },
+ { "TxPaceFixed", 9, 1 },
+ { "TxPaceAuto", 8, 1 },
+ { "RxUrgTunnel", 6, 1 },
+ { "RxUrgMode", 5, 1 },
+ { "TxUrgMode", 4, 1 },
+ { "CngCtrlMode", 2, 2 },
+ { "RxCoalesceEnable", 1, 1 },
+ { "RxCoalescePshEn", 0, 1 },
+ { "TP_PARA_REG4", 0x370, 0 },
+ { "HighSpeedCfg", 24, 8 },
+ { "NewRenoCfg", 16, 8 },
+ { "TahoeCfg", 8, 8 },
+ { "RenoCfg", 0, 8 },
+ { "TP_PARA_REG5", 0x374, 0 },
+ { "IndicateSize", 16, 16 },
+ { "SchdEnable", 8, 1 },
+ { "RxDdpOffInit", 3, 1 },
+ { "OnFlyDDPEnable", 2, 1 },
+ { "DackTimerSpin", 1, 1 },
+ { "PushTimerEnable", 0, 1 },
+ { "TP_PARA_REG6", 0x378, 0 },
+ { "TxPDUSizeAdj", 16, 8 },
+ { "EnableDeferACK", 12, 1 },
+ { "EnableESnd", 11, 1 },
+ { "EnableCSnd", 10, 1 },
+ { "EnablePDUE", 9, 1 },
+ { "EnablePDUC", 8, 1 },
+ { "EnableBUFI", 7, 1 },
+ { "EnableBUFE", 6, 1 },
+ { "EnableDefer", 5, 1 },
+ { "EnableClearRxmtOos", 4, 1 },
+ { "DisablePDUCng", 3, 1 },
+ { "DisablePDUTimeout", 2, 1 },
+ { "DisablePDURxmt", 1, 1 },
+ { "DisablePDUxmt", 0, 1 },
+ { "TP_PARA_REG7", 0x37c, 0 },
+ { "PMMaxXferLen1", 16, 16 },
+ { "PMMaxXferLen0", 0, 16 },
+ { "TP_TIMER_RESOLUTION", 0x390, 0 },
+ { "TimerResolution", 16, 8 },
+ { "TimestampResolution", 8, 8 },
+ { "DelayedACKResolution", 0, 8 },
+ { "TP_MSL", 0x394, 0 },
+ { "MSL", 0, 30 },
+ { "TP_RXT_MIN", 0x398, 0 },
+ { "RxtMin", 0, 30 },
+ { "TP_RXT_MAX", 0x39c, 0 },
+ { "RxtMax", 0, 30 },
+ { "TP_PERS_MIN", 0x3a0, 0 },
+ { "PersMin", 0, 30 },
+ { "TP_PERS_MAX", 0x3a4, 0 },
+ { "PersMax", 0, 30 },
+ { "TP_KEEP_IDLE", 0x3a8, 0 },
+ { "KeepaliveIdle", 0, 30 },
+ { "TP_KEEP_INTVL", 0x3ac, 0 },
+ { "KeepaliveIntvl", 0, 30 },
+ { "TP_INIT_SRTT", 0x3b0, 0 },
+ { "InitSrtt", 0, 16 },
+ { "TP_DACK_TIMER", 0x3b4, 0 },
+ { "DackTime", 0, 12 },
+ { "TP_FINWAIT2_TIMER", 0x3b8, 0 },
+ { "Finwait2Time", 0, 30 },
+ { "TP_FAST_FINWAIT2_TIMER", 0x3bc, 0 },
+ { "FastFinwait2Time", 0, 30 },
+ { "TP_SHIFT_CNT", 0x3c0, 0 },
+ { "SynShiftMax", 24, 8 },
+ { "RxtShiftMaxR1", 20, 4 },
+ { "RxtShiftMaxR2", 16, 4 },
+ { "PerShiftBackoffMax", 12, 4 },
+ { "PerShiftMax", 8, 4 },
+ { "KeepaliveMax", 0, 8 },
+ { "TP_TIME_HI", 0x3c8, 0 },
+ { "TP_TIME_LO", 0x3cc, 0 },
+ { "TP_MTU_PORT_TABLE", 0x3d0, 0 },
+ { "Port1MTUValue", 16, 16 },
+ { "Port0MTUValue", 0, 16 },
+ { "TP_ULP_TABLE", 0x3d4, 0 },
+ { "ULPType7Field", 28, 4 },
+ { "ULPType6Field", 24, 4 },
+ { "ULPType5Field", 20, 4 },
+ { "ULPType4Field", 16, 4 },
+ { "ULPType3Field", 12, 4 },
+ { "ULPType2Field", 8, 4 },
+ { "ULPType1Field", 4, 4 },
+ { "ULPType0Field", 0, 4 },
+ { "TP_PACE_TABLE", 0x3d8, 0 },
+ { "TP_CCTRL_TABLE", 0x3dc, 0 },
+ { "TP_TOS_TABLE", 0x3e0, 0 },
+ { "TP_MTU_TABLE", 0x3e4, 0 },
+ { "TP_RSS_MAP_TABLE", 0x3e8, 0 },
+ { "TP_RSS_LKP_TABLE", 0x3ec, 0 },
+ { "TP_RSS_CONFIG", 0x3f0, 0 },
+ { "TNL4tupEn", 29, 1 },
+ { "TNL2tupEn", 28, 1 },
+ { "TNLprtEn", 26, 1 },
+ { "TNLMapEn", 25, 1 },
+ { "TNLLkpEn", 24, 1 },
+ { "OFD4tupEn", 21, 1 },
+ { "OFD2tupEn", 20, 1 },
+ { "OFDMapEn", 17, 1 },
+ { "OFDLkpEn", 16, 1 },
+ { "SYN4tupEn", 13, 1 },
+ { "SYN2tupEn", 12, 1 },
+ { "SYNMapEn", 9, 1 },
+ { "SYNLkpEn", 8, 1 },
+ { "RRCPLMapEn", 7, 1 },
+ { "RRCPLCPUSIZE", 4, 3 },
+ { "RQFeedbackEnable", 3, 1 },
+ { "HashToeplitz", 2, 1 },
+ { "HashSave", 1, 1 },
+ { "Disable", 0, 1 },
+ { "TP_RSS_CONFIG_TNL", 0x3f4, 0 },
+ { "MaskSize", 28, 3 },
+ { "DefaultCPUBase", 22, 6 },
+ { "DefaultCPU", 16, 6 },
+ { "DefaultQueue", 0, 16 },
+ { "TP_RSS_CONFIG_OFD", 0x3f8, 0 },
+ { "MaskSize", 28, 3 },
+ { "DefaultCPUBase", 22, 6 },
+ { "DefaultCPU", 16, 6 },
+ { "DefaultQueue", 0, 16 },
+ { "TP_RSS_CONFIG_SYN", 0x3fc, 0 },
+ { "MaskSize", 28, 3 },
+ { "DefaultCPUBase", 22, 6 },
+ { "DefaultCPU", 16, 6 },
+ { "DefaultQueue", 0, 16 },
+ { "TP_RSS_SECRET_KEY0", 0x400, 0 },
+ { "TP_RSS_SECRET_KEY1", 0x404, 0 },
+ { "TP_RSS_SECRET_KEY2", 0x408, 0 },
+ { "TP_RSS_SECRET_KEY3", 0x40c, 0 },
+ { "TP_TM_PIO_ADDR", 0x418, 0 },
+ { "TP_TM_PIO_DATA", 0x41c, 0 },
+ { "TP_TX_MOD_QUE_TABLE", 0x420, 0 },
+ { "TP_TX_RESOURCE_LIMIT", 0x424, 0 },
+ { "TX_RESOURCE_LIMIT_CH1_PC", 24, 8 },
+ { "TX_RESOURCE_LIMIT_CH1_NON_PC", 16, 8 },
+ { "TX_RESOURCE_LIMIT_CH0_PC", 8, 8 },
+ { "TX_RESOURCE_LIMIT_CH0_NON_PC", 0, 8 },
+ { "TP_TX_MOD_QUEUE_REQ_MAP", 0x428, 0 },
+ { "RX_MOD_WEIGHT", 24, 8 },
+ { "TX_MOD_WEIGHT", 16, 8 },
+ { "TX_MOD_TIMER_MODE", 8, 8 },
+ { "TX_MOD_QUEUE_REQ_MAP", 0, 8 },
+ { "TP_TX_MOD_QUEUE_WEIGHT1", 0x42c, 0 },
+ { "TP_TX_MOD_QUEUE_WEIGHT7", 24, 8 },
+ { "TP_TX_MOD_QUEUE_WEIGHT6", 16, 8 },
+ { "TP_TX_MOD_QUEUE_WEIGHT5", 8, 8 },
+ { "TP_TX_MOD_QUEUE_WEIGHT4", 0, 8 },
+ { "TP_TX_MOD_QUEUE_WEIGHT0", 0x430, 0 },
+ { "TP_TX_MOD_QUEUE_WEIGHT3", 24, 8 },
+ { "TP_TX_MOD_QUEUE_WEIGHT2", 16, 8 },
+ { "TP_TX_MOD_QUEUE_WEIGHT1", 8, 8 },
+ { "TP_TX_MOD_QUEUE_WEIGHT0", 0, 8 },
+ { "TP_MOD_CHANNEL_WEIGHT", 0x434, 0 },
+ { "RX_MOD_CHANNEL_WEIGHT1", 24, 8 },
+ { "RX_MOD_CHANNEL_WEIGHT0", 16, 8 },
+ { "TX_MOD_CHANNEL_WEIGHT1", 8, 8 },
+ { "TX_MOD_CHANNEL_WEIGHT0", 0, 8 },
+ { "TP_MOD_RATE_LIMIT", 0x438, 0 },
+ { "RX_MOD_RATE_LIMIT_INC", 24, 8 },
+ { "RX_MOD_RATE_LIMIT_TICK", 16, 8 },
+ { "TX_MOD_RATE_LIMIT_INC", 8, 8 },
+ { "TX_MOD_RATE_LIMIT_TICK", 0, 8 },
+ { "TP_PIO_ADDR", 0x440, 0 },
+ { "TP_PIO_DATA", 0x444, 0 },
+ { "TP_RESET", 0x44c, 0 },
+ { "FlstInitEnable", 1, 1 },
+ { "TPReset", 0, 1 },
+ { "TP_MIB_INDEX", 0x450, 0 },
+ { "TP_MIB_RDATA", 0x454, 0 },
+ { "TP_SYNC_TIME_HI", 0x458, 0 },
+ { "TP_SYNC_TIME_LO", 0x45c, 0 },
+ { "TP_CMM_MM_RX_FLST_BASE", 0x460, 0 },
+ { "CMRxFlstBase", 0, 28 },
+ { "TP_CMM_MM_TX_FLST_BASE", 0x464, 0 },
+ { "CMTxFlstBase", 0, 28 },
+ { "TP_CMM_MM_PS_FLST_BASE", 0x468, 0 },
+ { "CMPsFlstBase", 0, 28 },
+ { "TP_CMM_MM_MAX_PSTRUCT", 0x46c, 0 },
+ { "CMMaxPstruct", 0, 21 },
+ { "TP_INT_ENABLE", 0x470, 0 },
+ { "FlmTxFlstEmpty", 30, 1 },
+ { "FlmRxFlstEmpty", 29, 1 },
+ { "FlmPerrSet", 28, 1 },
+ { "ProtocolSramPerr", 27, 1 },
+ { "ArpLutPerr", 26, 1 },
+ { "CmRcfOpPerr", 25, 1 },
+ { "CmCachePerr", 24, 1 },
+ { "CmRcfDataPerr", 23, 1 },
+ { "DbL2tLutPerr", 22, 1 },
+ { "DbTxTidPerr", 21, 1 },
+ { "DbExtPerr", 20, 1 },
+ { "DbOpPerr", 19, 1 },
+ { "TmCachePerr", 18, 1 },
+ { "ETpOutCplFifoPerr", 17, 1 },
+ { "ETpOutTcpFifoPerr", 16, 1 },
+ { "ETpOutIpFifoPerr", 15, 1 },
+ { "ETpOutEthFifoPerr", 14, 1 },
+ { "ETpInCplFifoPerr", 13, 1 },
+ { "ETpInTcpOptFifoPerr", 12, 1 },
+ { "ETpInTcpFifoPerr", 11, 1 },
+ { "ETpInIpFifoPerr", 10, 1 },
+ { "ETpInEthFifoPerr", 9, 1 },
+ { "CTpOutCplFifoPerr", 8, 1 },
+ { "CTpOutTcpFifoPerr", 7, 1 },
+ { "CTpOutIpFifoPerr", 6, 1 },
+ { "CTpOutEthFifoPerr", 5, 1 },
+ { "CTpInCplFifoPerr", 4, 1 },
+ { "CTpInTcpOpFifoPerr", 3, 1 },
+ { "CTpInTcpFifoPerr", 2, 1 },
+ { "CTpInIpFifoPerr", 1, 1 },
+ { "CTpInEthFifoPerr", 0, 1 },
+ { "TP_INT_CAUSE", 0x474, 0 },
+ { "FlmTxFlstEmpty", 30, 1 },
+ { "FlmRxFlstEmpty", 29, 1 },
+ { "FlmPerrSet", 28, 1 },
+ { "ProtocolSramPerr", 27, 1 },
+ { "ArpLutPerr", 26, 1 },
+ { "CmRcfOpPerr", 25, 1 },
+ { "CmCachePerr", 24, 1 },
+ { "CmRcfDataPerr", 23, 1 },
+ { "DbL2tLutPerr", 22, 1 },
+ { "DbTxTidPerr", 21, 1 },
+ { "DbExtPerr", 20, 1 },
+ { "DbOpPerr", 19, 1 },
+ { "TmCachePerr", 18, 1 },
+ { "ETpOutCplFifoPerr", 17, 1 },
+ { "ETpOutTcpFifoPerr", 16, 1 },
+ { "ETpOutIpFifoPerr", 15, 1 },
+ { "ETpOutEthFifoPerr", 14, 1 },
+ { "ETpInCplFifoPerr", 13, 1 },
+ { "ETpInTcpOptFifoPerr", 12, 1 },
+ { "ETpInTcpFifoPerr", 11, 1 },
+ { "ETpInIpFifoPerr", 10, 1 },
+ { "ETpInEthFifoPerr", 9, 1 },
+ { "CTpOutCplFifoPerr", 8, 1 },
+ { "CTpOutTcpFifoPerr", 7, 1 },
+ { "CTpOutIpFifoPerr", 6, 1 },
+ { "CTpOutEthFifoPerr", 5, 1 },
+ { "CTpInCplFifoPerr", 4, 1 },
+ { "CTpInTcpOpFifoPerr", 3, 1 },
+ { "CTpInTcpFifoPerr", 2, 1 },
+ { "CTpInIpFifoPerr", 1, 1 },
+ { "CTpInEthFifoPerr", 0, 1 },
+ { "TP_FLM_FREE_PS_CNT", 0x480, 0 },
+ { "FreePstructCount", 0, 21 },
+ { "TP_FLM_FREE_RX_CNT", 0x484, 0 },
+ { "FreeRxPageCount", 0, 21 },
+ { "TP_FLM_FREE_TX_CNT", 0x488, 0 },
+ { "FreeTxPageCount", 0, 21 },
+ { "TP_TM_HEAP_PUSH_CNT", 0x48c, 0 },
+ { "TP_TM_HEAP_POP_CNT", 0x490, 0 },
+ { "TP_TM_DACK_PUSH_CNT", 0x494, 0 },
+ { "TP_TM_DACK_POP_CNT", 0x498, 0 },
+ { "TP_TM_MOD_PUSH_CNT", 0x49c, 0 },
+ { "TP_MOD_POP_CNT", 0x4a0, 0 },
+ { "TP_TIMER_SEPARATOR", 0x4a4, 0 },
+ { "TP_DEBUG_SEL", 0x4a8, 0 },
+ { "TP_DEBUG_FLAGS", 0x4ac, 0 },
+ { "RxTimerDackFirst", 26, 1 },
+ { "RxTimerDack", 25, 1 },
+ { "RxTimerHeartbeat", 24, 1 },
+ { "RxPawsDrop", 23, 1 },
+ { "RxUrgDataDrop", 22, 1 },
+ { "RxFutureData", 21, 1 },
+ { "RxRcvRxmData", 20, 1 },
+ { "RxRcvOooDataFin", 19, 1 },
+ { "RxRcvOooData", 18, 1 },
+ { "RxRcvWndZero", 17, 1 },
+ { "RxRcvWndLtMss", 16, 1 },
+ { "TxDupAckInc", 11, 1 },
+ { "TxRxmUrg", 10, 1 },
+ { "TxRxmFin", 9, 1 },
+ { "TxRxmSyn", 8, 1 },
+ { "TxRxmNewReno", 7, 1 },
+ { "TxRxmFast", 6, 1 },
+ { "TxRxmTimer", 5, 1 },
+ { "TxRxmTimerKeepalive", 4, 1 },
+ { "TxRxmTimerPersist", 3, 1 },
+ { "TxRcvAdvShrunk", 2, 1 },
+ { "TxRcvAdvZero", 1, 1 },
+ { "TxRcvAdvLtMss", 0, 1 },
+ { "TP_PROXY_FLOW_CNTL", 0x4b0, 0 },
+ { "TP_PC_CONGESTION_CNTL", 0x4b4, 0 },
+ { "EDropTunnel", 19, 1 },
+ { "CDropTunnel", 18, 1 },
+ { "EThreshold", 12, 6 },
+ { "CThreshold", 6, 6 },
+ { "TxThreshold", 0, 6 },
+ { "TP_TX_DROP_COUNT", 0x4bc, 0 },
+ { "TP_CLEAR_DEBUG", 0x4c0, 0 },
+ { "ClrDebug", 0, 1 },
+ { "TP_DEBUG_VEC", 0x4c4, 0 },
+ { "TP_DEBUG_VEC2", 0x4c8, 0 },
+ { "TP_DEBUG_REG_SEL", 0x4cc, 0 },
+ { "TP_DEBUG", 0x4d0, 0 },
+ { "TP_DBG_LA_CONFIG", 0x4d4, 0 },
+ { "TP_DBG_LA_DATAH", 0x4d8, 0 },
+ { "TP_DBG_LA_DATAL", 0x4dc, 0 },
+ { "TP_EMBED_OP_FIELD0", 0x4e8, 0 },
+ { "TP_EMBED_OP_FIELD1", 0x4ec, 0 },
+ { "TP_EMBED_OP_FIELD2", 0x4f0, 0 },
+ { "TP_EMBED_OP_FIELD3", 0x4f4, 0 },
+ { "TP_EMBED_OP_FIELD4", 0x4f8, 0 },
+ { "TP_EMBED_OP_FIELD5", 0x4fc, 0 },
+ { NULL }
+};
+
+struct reg_info t3c_ulp2_rx_regs[] = {
+ { "ULPRX_CTL", 0x500, 0 },
+ { "PCMD1Threshold", 24, 8 },
+ { "PCMD0Threshold", 16, 8 },
+ { "round_robin", 4, 1 },
+ { "RDMA_permissive_mode", 3, 1 },
+ { "PagePodME", 2, 1 },
+ { "IscsiTagTcb", 1, 1 },
+ { "TddpTagTcb", 0, 1 },
+ { "ULPRX_INT_ENABLE", 0x504, 0 },
+ { "DataSelFrameErr0", 7, 1 },
+ { "DataSelFrameErr1", 6, 1 },
+ { "PcmdMuxPerr", 5, 1 },
+ { "ArbFPerr", 4, 1 },
+ { "ArbPF0Perr", 3, 1 },
+ { "ArbPF1Perr", 2, 1 },
+ { "ParErrPcmd", 1, 1 },
+ { "ParErrData", 0, 1 },
+ { "ULPRX_INT_CAUSE", 0x508, 0 },
+ { "DataSelFrameErr0", 7, 1 },
+ { "DataSelFrameErr1", 6, 1 },
+ { "PcmdMuxPerr", 5, 1 },
+ { "ArbFPerr", 4, 1 },
+ { "ArbPF0Perr", 3, 1 },
+ { "ArbPF1Perr", 2, 1 },
+ { "ParErrPcmd", 1, 1 },
+ { "ParErrData", 0, 1 },
+ { "ULPRX_ISCSI_LLIMIT", 0x50c, 0 },
+ { "IscsiLlimit", 6, 26 },
+ { "ULPRX_ISCSI_ULIMIT", 0x510, 0 },
+ { "IscsiUlimit", 6, 26 },
+ { "ULPRX_ISCSI_TAGMASK", 0x514, 0 },
+ { "IscsiTagMask", 6, 26 },
+ { "ULPRX_ISCSI_PSZ", 0x518, 0 },
+ { "Hpz3", 24, 4 },
+ { "Hpz2", 16, 4 },
+ { "Hpz1", 8, 4 },
+ { "Hpz0", 0, 4 },
+ { "ULPRX_TDDP_LLIMIT", 0x51c, 0 },
+ { "TddpLlimit", 6, 26 },
+ { "ULPRX_TDDP_ULIMIT", 0x520, 0 },
+ { "TddpUlimit", 6, 26 },
+ { "ULPRX_TDDP_TAGMASK", 0x524, 0 },
+ { "TddpTagMask", 6, 26 },
+ { "ULPRX_TDDP_PSZ", 0x528, 0 },
+ { "Hpz3", 24, 4 },
+ { "Hpz2", 16, 4 },
+ { "Hpz1", 8, 4 },
+ { "Hpz0", 0, 4 },
+ { "ULPRX_STAG_LLIMIT", 0x52c, 0 },
+ { "ULPRX_STAG_ULIMIT", 0x530, 0 },
+ { "ULPRX_RQ_LLIMIT", 0x534, 0 },
+ { "ULPRX_RQ_ULIMIT", 0x538, 0 },
+ { "ULPRX_PBL_LLIMIT", 0x53c, 0 },
+ { "ULPRX_PBL_ULIMIT", 0x540, 0 },
+ { NULL }
+};
+
+struct reg_info t3c_ulp2_tx_regs[] = {
+ { "ULPTX_CONFIG", 0x580, 0 },
+ { "CFG_CQE_SOP_MASK", 1, 1 },
+ { "CFG_RR_ARB", 0, 1 },
+ { "ULPTX_INT_ENABLE", 0x584, 0 },
+ { "cmd_fifo_perr_set1", 7, 1 },
+ { "cmd_fifo_perr_set0", 6, 1 },
+ { "lso_hdr_sram_perr_set1", 5, 1 },
+ { "lso_hdr_sram_perr_set0", 4, 1 },
+ { "imm_data_perr_set_ch1", 3, 1 },
+ { "imm_data_perr_set_ch0", 2, 1 },
+ { "Pbl_bound_err_ch1", 1, 1 },
+ { "Pbl_bound_err_ch0", 0, 1 },
+ { "ULPTX_INT_CAUSE", 0x588, 0 },
+ { "cmd_fifo_perr_set1", 7, 1 },
+ { "cmd_fifo_perr_set0", 6, 1 },
+ { "lso_hdr_sram_perr_set1", 5, 1 },
+ { "lso_hdr_sram_perr_set0", 4, 1 },
+ { "imm_data_perr_set_ch1", 3, 1 },
+ { "imm_data_perr_set_ch0", 2, 1 },
+ { "Pbl_bound_err_ch1", 1, 1 },
+ { "Pbl_bound_err_ch0", 0, 1 },
+ { "ULPTX_TPT_LLIMIT", 0x58c, 0 },
+ { "ULPTX_TPT_ULIMIT", 0x590, 0 },
+ { "ULPTX_PBL_LLIMIT", 0x594, 0 },
+ { "ULPTX_PBL_ULIMIT", 0x598, 0 },
+ { "ULPTX_CPL_ERR_OFFSET", 0x59c, 0 },
+ { "ULPTX_CPL_ERR_MASK", 0x5a0, 0 },
+ { "ULPTX_CPL_ERR_VALUE", 0x5a4, 0 },
+ { "ULPTX_CPL_PACK_SIZE", 0x5a8, 0 },
+ { "value", 24, 8 },
+ { "Ch1Size2", 24, 8 },
+ { "Ch1Size1", 16, 8 },
+ { "Ch0Size2", 8, 8 },
+ { "Ch0Size1", 0, 8 },
+ { "ULPTX_DMA_WEIGHT", 0x5ac, 0 },
+ { "D1_WEIGHT", 16, 16 },
+ { "D0_WEIGHT", 0, 16 },
+ { NULL }
+};
+
+struct reg_info t3c_pm1_rx_regs[] = {
+ { "PM1_RX_CFG", 0x5c0, 0 },
+ { "PM1_RX_MODE", 0x5c4, 0 },
+ { "stat_channel", 1, 1 },
+ { "priority_ch", 0, 1 },
+ { "PM1_RX_STAT_CONFIG", 0x5c8, 0 },
+ { "PM1_RX_STAT_COUNT", 0x5cc, 0 },
+ { "PM1_RX_STAT_MSB", 0x5d0, 0 },
+ { "PM1_RX_STAT_LSB", 0x5d4, 0 },
+ { "PM1_RX_INT_ENABLE", 0x5d8, 0 },
+ { "zero_e_cmd_error", 18, 1 },
+ { "iespi0_fifo2x_Rx_framing_error", 17, 1 },
+ { "iespi1_fifo2x_Rx_framing_error", 16, 1 },
+ { "iespi0_Rx_framing_error", 15, 1 },
+ { "iespi1_Rx_framing_error", 14, 1 },
+ { "iespi0_Tx_framing_error", 13, 1 },
+ { "iespi1_Tx_framing_error", 12, 1 },
+ { "ocspi0_Rx_framing_error", 11, 1 },
+ { "ocspi1_Rx_framing_error", 10, 1 },
+ { "ocspi0_Tx_framing_error", 9, 1 },
+ { "ocspi1_Tx_framing_error", 8, 1 },
+ { "ocspi0_ofifo2x_Tx_framing_error", 7, 1 },
+ { "ocspi1_ofifo2x_Tx_framing_error", 6, 1 },
+ { "iespi_par_error", 3, 3 },
+ { "ocspi_par_error", 0, 3 },
+ { "PM1_RX_INT_CAUSE", 0x5dc, 0 },
+ { "zero_e_cmd_error", 18, 1 },
+ { "iespi0_fifo2x_Rx_framing_error", 17, 1 },
+ { "iespi1_fifo2x_Rx_framing_error", 16, 1 },
+ { "iespi0_Rx_framing_error", 15, 1 },
+ { "iespi1_Rx_framing_error", 14, 1 },
+ { "iespi0_Tx_framing_error", 13, 1 },
+ { "iespi1_Tx_framing_error", 12, 1 },
+ { "ocspi0_Rx_framing_error", 11, 1 },
+ { "ocspi1_Rx_framing_error", 10, 1 },
+ { "ocspi0_Tx_framing_error", 9, 1 },
+ { "ocspi1_Tx_framing_error", 8, 1 },
+ { "ocspi0_ofifo2x_Tx_framing_error", 7, 1 },
+ { "ocspi1_ofifo2x_Tx_framing_error", 6, 1 },
+ { "iespi_par_error", 3, 3 },
+ { "ocspi_par_error", 0, 3 },
+ { NULL }
+};
+
+struct reg_info t3c_pm1_tx_regs[] = {
+ { "PM1_TX_CFG", 0x5e0, 0 },
+ { "PM1_TX_MODE", 0x5e4, 0 },
+ { "stat_channel", 1, 1 },
+ { "priority_ch", 0, 1 },
+ { "PM1_TX_STAT_CONFIG", 0x5e8, 0 },
+ { "PM1_TX_STAT_COUNT", 0x5ec, 0 },
+ { "PM1_TX_STAT_MSB", 0x5f0, 0 },
+ { "PM1_TX_STAT_LSB", 0x5f4, 0 },
+ { "PM1_TX_INT_ENABLE", 0x5f8, 0 },
+ { "zero_c_cmd_error", 18, 1 },
+ { "icspi0_fifo2x_Rx_framing_error", 17, 1 },
+ { "icspi1_fifo2x_Rx_framing_error", 16, 1 },
+ { "icspi0_Rx_framing_error", 15, 1 },
+ { "icspi1_Rx_framing_error", 14, 1 },
+ { "icspi0_Tx_framing_error", 13, 1 },
+ { "icspi1_Tx_framing_error", 12, 1 },
+ { "oespi0_Rx_framing_error", 11, 1 },
+ { "oespi1_Rx_framing_error", 10, 1 },
+ { "oespi0_Tx_framing_error", 9, 1 },
+ { "oespi1_Tx_framing_error", 8, 1 },
+ { "oespi0_ofifo2x_Tx_framing_error", 7, 1 },
+ { "oespi1_ofifo2x_Tx_framing_error", 6, 1 },
+ { "icspi_par_error", 3, 3 },
+ { "oespi_par_error", 0, 3 },
+ { "PM1_TX_INT_CAUSE", 0x5fc, 0 },
+ { "zero_c_cmd_error", 18, 1 },
+ { "icspi0_fifo2x_Rx_framing_error", 17, 1 },
+ { "icspi1_fifo2x_Rx_framing_error", 16, 1 },
+ { "icspi0_Rx_framing_error", 15, 1 },
+ { "icspi1_Rx_framing_error", 14, 1 },
+ { "icspi0_Tx_framing_error", 13, 1 },
+ { "icspi1_Tx_framing_error", 12, 1 },
+ { "oespi0_Rx_framing_error", 11, 1 },
+ { "oespi1_Rx_framing_error", 10, 1 },
+ { "oespi0_Tx_framing_error", 9, 1 },
+ { "oespi1_Tx_framing_error", 8, 1 },
+ { "oespi0_ofifo2x_Tx_framing_error", 7, 1 },
+ { "oespi1_ofifo2x_Tx_framing_error", 6, 1 },
+ { "icspi_par_error", 3, 3 },
+ { "oespi_par_error", 0, 3 },
+ { NULL }
+};
+
+struct reg_info t3c_mps0_regs[] = {
+ { "MPS_CFG", 0x600, 0 },
+ { "EnForcePkt", 11, 1 },
+ { "SGETPQid", 8, 3 },
+ { "TPRxPortSize", 7, 1 },
+ { "TPTxPort1Size", 6, 1 },
+ { "TPTxPort0Size", 5, 1 },
+ { "TPRxPortEn", 4, 1 },
+ { "TPTxPort1En", 3, 1 },
+ { "TPTxPort0En", 2, 1 },
+ { "Port1Active", 1, 1 },
+ { "Port0Active", 0, 1 },
+ { "MPS_DRR_CFG1", 0x604, 0 },
+ { "RldWtTPD1", 11, 11 },
+ { "RldWtTPD0", 0, 11 },
+ { "MPS_DRR_CFG2", 0x608, 0 },
+ { "RldWtTotal", 0, 12 },
+ { "MPS_MCA_STATUS", 0x60c, 0 },
+ { "MCAPktCnt", 12, 20 },
+ { "MCADepth", 0, 12 },
+ { "MPS_TX0_TP_CNT", 0x610, 0 },
+ { "TX0TPDisCnt", 24, 8 },
+ { "TX0TPCnt", 0, 24 },
+ { "MPS_TX1_TP_CNT", 0x614, 0 },
+ { "TX1TPDisCnt", 24, 8 },
+ { "TX1TPCnt", 0, 24 },
+ { "MPS_RX_TP_CNT", 0x618, 0 },
+ { "RXTPDisCnt", 24, 8 },
+ { "RXTPCnt", 0, 24 },
+ { "MPS_INT_ENABLE", 0x61c, 0 },
+ { "MCAParErrEnb", 6, 3 },
+ { "RXTpParErrEnb", 4, 2 },
+ { "TX1TpParErrEnb", 2, 2 },
+ { "TX0TpParErrEnb", 0, 2 },
+ { "MPS_INT_CAUSE", 0x620, 0 },
+ { "MCAParErr", 6, 3 },
+ { "RXTpParErr", 4, 2 },
+ { "TX1TpParErr", 2, 2 },
+ { "TX0TpParErr", 0, 2 },
+ { NULL }
+};
+
+struct reg_info t3c_cpl_switch_regs[] = {
+ { "CPL_SWITCH_CNTRL", 0x640, 0 },
+ { "cpl_pkt_tid", 8, 24 },
+ { "cim_to_up_full_size", 4, 1 },
+ { "cpu_no_3F_CIM_enable", 3, 1 },
+ { "switch_table_enable", 2, 1 },
+ { "sge_enable", 1, 1 },
+ { "cim_enable", 0, 1 },
+ { "CPL_SWITCH_TBL_IDX", 0x644, 0 },
+ { "switch_tbl_idx", 0, 4 },
+ { "CPL_SWITCH_TBL_DATA", 0x648, 0 },
+ { "CPL_SWITCH_ZERO_ERROR", 0x64c, 0 },
+ { "zero_cmd", 0, 8 },
+ { "CPL_INTR_ENABLE", 0x650, 0 },
+ { "cim_op_map_perr", 5, 1 },
+ { "cim_ovfl_error", 4, 1 },
+ { "tp_framing_error", 3, 1 },
+ { "sge_framing_error", 2, 1 },
+ { "cim_framing_error", 1, 1 },
+ { "zero_switch_error", 0, 1 },
+ { "CPL_INTR_CAUSE", 0x654, 0 },
+ { "cim_op_map_perr", 5, 1 },
+ { "cim_ovfl_error", 4, 1 },
+ { "tp_framing_error", 3, 1 },
+ { "sge_framing_error", 2, 1 },
+ { "cim_framing_error", 1, 1 },
+ { "zero_switch_error", 0, 1 },
+ { "CPL_MAP_TBL_IDX", 0x658, 0 },
+ { "cpl_map_tbl_idx", 0, 8 },
+ { "CPL_MAP_TBL_DATA", 0x65c, 0 },
+ { "cpl_map_tbl_data", 0, 8 },
+ { NULL }
+};
+
+struct reg_info t3c_smb0_regs[] = {
+ { "SMB_GLOBAL_TIME_CFG", 0x660, 0 },
+ { "LADbgWrPtr", 24, 8 },
+ { "LADbgRdPtr", 16, 8 },
+ { "LADbgEn", 13, 1 },
+ { "MacroCntCfg", 8, 5 },
+ { "MicroCntCfg", 0, 8 },
+ { "SMB_MST_TIMEOUT_CFG", 0x664, 0 },
+ { "DebugSelH", 28, 4 },
+ { "DebugSelL", 24, 4 },
+ { "MstTimeOutCfg", 0, 24 },
+ { "SMB_MST_CTL_CFG", 0x668, 0 },
+ { "MstFifoDbg", 31, 1 },
+ { "MstFifoDbgClr", 30, 1 },
+ { "MstRxByteCfg", 12, 6 },
+ { "MstTxByteCfg", 6, 6 },
+ { "MstReset", 1, 1 },
+ { "MstCtlEn", 0, 1 },
+ { "SMB_MST_CTL_STS", 0x66c, 0 },
+ { "MstRxByteCnt", 12, 6 },
+ { "MstTxByteCnt", 6, 6 },
+ { "MstBusySts", 0, 1 },
+ { "SMB_MST_TX_FIFO_RDWR", 0x670, 0 },
+ { "SMB_MST_RX_FIFO_RDWR", 0x674, 0 },
+ { "SMB_SLV_TIMEOUT_CFG", 0x678, 0 },
+ { "SlvTimeOutCfg", 0, 24 },
+ { "SMB_SLV_CTL_CFG", 0x67c, 0 },
+ { "SlvFifoDbg", 31, 1 },
+ { "SlvFifoDbgClr", 30, 1 },
+ { "SlvAddrCfg", 4, 7 },
+ { "SlvAlrtSet", 2, 1 },
+ { "SlvReset", 1, 1 },
+ { "SlvCtlEn", 0, 1 },
+ { "SMB_SLV_CTL_STS", 0x680, 0 },
+ { "SlvFifoTxCnt", 12, 6 },
+ { "SlvFifoCnt", 6, 6 },
+ { "SlvAlrtSts", 2, 1 },
+ { "SlvBusySts", 0, 1 },
+ { "SMB_SLV_FIFO_RDWR", 0x684, 0 },
+ { "SMB_SLV_CMD_FIFO_RDWR", 0x688, 0 },
+ { "SMB_INT_ENABLE", 0x68c, 0 },
+ { "SlvTimeOutIntEn", 7, 1 },
+ { "SlvErrIntEn", 6, 1 },
+ { "SlvDoneIntEn", 5, 1 },
+ { "SlvRxRdyIntEn", 4, 1 },
+ { "MstTimeOutIntEn", 3, 1 },
+ { "MstNAckIntEn", 2, 1 },
+ { "MstLostArbIntEn", 1, 1 },
+ { "MstDoneIntEn", 0, 1 },
+ { "SMB_INT_CAUSE", 0x690, 0 },
+ { "SlvTimeOutInt", 7, 1 },
+ { "SlvErrInt", 6, 1 },
+ { "SlvDoneInt", 5, 1 },
+ { "SlvRxRdyInt", 4, 1 },
+ { "MstTimeOutInt", 3, 1 },
+ { "MstNAckInt", 2, 1 },
+ { "MstLostArbInt", 1, 1 },
+ { "MstDoneInt", 0, 1 },
+ { "SMB_DEBUG_DATA", 0x694, 0 },
+ { "DebugDataH", 16, 16 },
+ { "DebugDataL", 0, 16 },
+ { "SMB_DEBUG_LA", 0x69c, 0 },
+ { "DebugLAReqAddr", 0, 10 },
+ { NULL }
+};
+
+struct reg_info t3c_i2cm0_regs[] = {
+ { "I2C_CFG", 0x6a0, 0 },
+ { "ClkDiv", 0, 12 },
+ { "I2C_DATA", 0x6a4, 0 },
+ { "Data", 0, 8 },
+ { "I2C_OP", 0x6a8, 0 },
+ { "Busy", 31, 1 },
+ { "Ack", 30, 1 },
+ { "Cont", 1, 1 },
+ { "Op", 0, 1 },
+ { NULL }
+};
+
+struct reg_info t3c_mi1_regs[] = {
+ { "MI1_CFG", 0x6b0, 0 },
+ { "ClkDiv", 5, 8 },
+ { "St", 3, 2 },
+ { "PreEn", 2, 1 },
+ { "MDIInv", 1, 1 },
+ { "MDIEn", 0, 1 },
+ { "MI1_ADDR", 0x6b4, 0 },
+ { "PhyAddr", 5, 5 },
+ { "RegAddr", 0, 5 },
+ { "MI1_DATA", 0x6b8, 0 },
+ { "Data", 0, 16 },
+ { "MI1_OP", 0x6bc, 0 },
+ { "Busy", 31, 1 },
+ { "Inc", 2, 1 },
+ { "Op", 0, 2 },
+ { NULL }
+};
+
+struct reg_info t3c_jm1_regs[] = {
+ { "JM_CFG", 0x6c0, 0 },
+ { "ClkDiv", 2, 8 },
+ { "TRst", 1, 1 },
+ { "En", 0, 1 },
+ { "JM_MODE", 0x6c4, 0 },
+ { "JM_DATA", 0x6c8, 0 },
+ { "JM_OP", 0x6cc, 0 },
+ { "Busy", 31, 1 },
+ { "Cnt", 0, 5 },
+ { NULL }
+};
+
+struct reg_info t3c_sf1_regs[] = {
+ { "SF_DATA", 0x6d8, 0 },
+ { "SF_OP", 0x6dc, 0 },
+ { "Busy", 31, 1 },
+ { "Cont", 3, 1 },
+ { "ByteCnt", 1, 2 },
+ { "Op", 0, 1 },
+ { NULL }
+};
+
+struct reg_info t3c_pl3_regs[] = {
+ { "PL_INT_ENABLE0", 0x6e0, 0 },
+ { "SW", 25, 1 },
+ { "EXT", 24, 1 },
+ { "T3DBG", 23, 1 },
+ { "XGMAC0_1", 20, 1 },
+ { "XGMAC0_0", 19, 1 },
+ { "MC5A", 18, 1 },
+ { "SF1", 17, 1 },
+ { "SMB0", 15, 1 },
+ { "I2CM0", 14, 1 },
+ { "MI1", 13, 1 },
+ { "CPL_SWITCH", 12, 1 },
+ { "MPS0", 11, 1 },
+ { "PM1_TX", 10, 1 },
+ { "PM1_RX", 9, 1 },
+ { "ULP2_TX", 8, 1 },
+ { "ULP2_RX", 7, 1 },
+ { "TP1", 6, 1 },
+ { "CIM", 5, 1 },
+ { "MC7_CM", 4, 1 },
+ { "MC7_PMTX", 3, 1 },
+ { "MC7_PMRX", 2, 1 },
+ { "PCIM0", 1, 1 },
+ { "SGE3", 0, 1 },
+ { "PL_INT_CAUSE0", 0x6e4, 0 },
+ { "SW", 25, 1 },
+ { "EXT", 24, 1 },
+ { "T3DBG", 23, 1 },
+ { "XGMAC0_1", 20, 1 },
+ { "XGMAC0_0", 19, 1 },
+ { "MC5A", 18, 1 },
+ { "SF1", 17, 1 },
+ { "SMB0", 15, 1 },
+ { "I2CM0", 14, 1 },
+ { "MI1", 13, 1 },
+ { "CPL_SWITCH", 12, 1 },
+ { "MPS0", 11, 1 },
+ { "PM1_TX", 10, 1 },
+ { "PM1_RX", 9, 1 },
+ { "ULP2_TX", 8, 1 },
+ { "ULP2_RX", 7, 1 },
+ { "TP1", 6, 1 },
+ { "CIM", 5, 1 },
+ { "MC7_CM", 4, 1 },
+ { "MC7_PMTX", 3, 1 },
+ { "MC7_PMRX", 2, 1 },
+ { "PCIM0", 1, 1 },
+ { "SGE3", 0, 1 },
+ { "PL_INT_ENABLE1", 0x6e8, 0 },
+ { "SW", 25, 1 },
+ { "EXT", 24, 1 },
+ { "T3DBG", 23, 1 },
+ { "XGMAC0_1", 20, 1 },
+ { "XGMAC0_0", 19, 1 },
+ { "MC5A", 18, 1 },
+ { "SF1", 17, 1 },
+ { "SMB0", 15, 1 },
+ { "I2CM0", 14, 1 },
+ { "MI1", 13, 1 },
+ { "CPL_SWITCH", 12, 1 },
+ { "MPS0", 11, 1 },
+ { "PM1_TX", 10, 1 },
+ { "PM1_RX", 9, 1 },
+ { "ULP2_TX", 8, 1 },
+ { "ULP2_RX", 7, 1 },
+ { "TP1", 6, 1 },
+ { "CIM", 5, 1 },
+ { "MC7_CM", 4, 1 },
+ { "MC7_PMTX", 3, 1 },
+ { "MC7_PMRX", 2, 1 },
+ { "PCIM0", 1, 1 },
+ { "SGE3", 0, 1 },
+ { "PL_INT_CAUSE1", 0x6ec, 0 },
+ { "SW", 25, 1 },
+ { "EXT", 24, 1 },
+ { "T3DBG", 23, 1 },
+ { "XGMAC0_1", 20, 1 },
+ { "XGMAC0_0", 19, 1 },
+ { "MC5A", 18, 1 },
+ { "SF1", 17, 1 },
+ { "SMB0", 15, 1 },
+ { "I2CM0", 14, 1 },
+ { "MI1", 13, 1 },
+ { "CPL_SWITCH", 12, 1 },
+ { "MPS0", 11, 1 },
+ { "PM1_TX", 10, 1 },
+ { "PM1_RX", 9, 1 },
+ { "ULP2_TX", 8, 1 },
+ { "ULP2_RX", 7, 1 },
+ { "TP1", 6, 1 },
+ { "CIM", 5, 1 },
+ { "MC7_CM", 4, 1 },
+ { "MC7_PMTX", 3, 1 },
+ { "MC7_PMRX", 2, 1 },
+ { "PCIM0", 1, 1 },
+ { "SGE3", 0, 1 },
+ { "PL_RST", 0x6f0, 0 },
+ { "FatalPerrEn", 4, 1 },
+ { "SWInt1", 3, 1 },
+ { "SWInt0", 2, 1 },
+ { "CRstWrm", 1, 1 },
+ { "CRstWrmMode", 0, 1 },
+ { "PL_REV", 0x6f4, 0 },
+ { "Rev", 0, 4 },
+ { "PL_CLI", 0x6f8, 0 },
+ { "PL_LCK", 0x6fc, 0 },
+ { "Lck", 0, 2 },
+ { NULL }
+};
+
+struct reg_info t3c_mc5a_regs[] = {
+ { "MC5_BUF_CONFIG", 0x700, 0 },
+ { "term300_240", 31, 1 },
+ { "term150", 30, 1 },
+ { "term60", 29, 1 },
+ { "gddriii", 28, 1 },
+ { "gddrii", 27, 1 },
+ { "gddri", 26, 1 },
+ { "read", 25, 1 },
+ { "imp_set_update", 24, 1 },
+ { "cal_update", 23, 1 },
+ { "cal_busy", 22, 1 },
+ { "cal_error", 21, 1 },
+ { "sgl_cal_en", 20, 1 },
+ { "imp_upd_mode", 19, 1 },
+ { "imp_sel", 18, 1 },
+ { "man_pu", 15, 3 },
+ { "man_pd", 12, 3 },
+ { "cal_pu", 9, 3 },
+ { "cal_pd", 6, 3 },
+ { "set_pu", 3, 3 },
+ { "set_pd", 0, 3 },
+ { "MC5_DB_CONFIG", 0x704, 0 },
+ { "TMCfgWrLock", 31, 1 },
+ { "TMTypeHi", 30, 1 },
+ { "TMPartSize", 28, 2 },
+ { "TMType", 26, 2 },
+ { "TMPartCount", 24, 2 },
+ { "nLIP", 18, 6 },
+ { "COMPEN", 17, 1 },
+ { "BUILD", 16, 1 },
+ { "FilterEn", 11, 1 },
+ { "CLIPUpdate", 10, 1 },
+ { "TM_IO_PDOWN", 9, 1 },
+ { "SYNMode", 7, 2 },
+ { "PRTYEN", 6, 1 },
+ { "MBUSEN", 5, 1 },
+ { "DBGIEN", 4, 1 },
+ { "TcmCfgOvr", 3, 1 },
+ { "TMRDY", 2, 1 },
+ { "TMRST", 1, 1 },
+ { "TMMode", 0, 1 },
+ { "MC5_MISC", 0x708, 0 },
+ { "LIP_Cmp_Unavailable", 0, 4 },
+ { "MC5_DB_ROUTING_TABLE_INDEX", 0x70c, 0 },
+ { "RTINDX", 0, 22 },
+ { "MC5_DB_FILTER_TABLE", 0x710, 0 },
+ { "SRINDX", 0, 22 },
+ { "MC5_DB_SERVER_INDEX", 0x714, 0 },
+ { "SRINDX", 0, 22 },
+ { "MC5_DB_LIP_RAM_ADDR", 0x718, 0 },
+ { "RAMWR", 8, 1 },
+ { "RAMADDR", 0, 6 },
+ { "MC5_DB_LIP_RAM_DATA", 0x71c, 0 },
+ { "MC5_DB_RSP_LATENCY", 0x720, 0 },
+ { "RDLAT", 16, 5 },
+ { "LRNLAT", 8, 5 },
+ { "SRCHLAT", 0, 5 },
+ { "MC5_DB_PARITY_LATENCY", 0x724, 0 },
+ { "PARLAT", 0, 4 },
+ { "MC5_DB_WR_LRN_VERIFY", 0x728, 0 },
+ { "VWVEREN", 2, 1 },
+ { "LRNVEREN", 1, 1 },
+ { "POVEREN", 0, 1 },
+ { "MC5_DB_PART_ID_INDEX", 0x72c, 0 },
+ { "IDINDEX", 0, 4 },
+ { "MC5_DB_RESET_MAX", 0x730, 0 },
+ { "RSTMAX", 0, 4 },
+ { "MC5_DB_ACT_CNT", 0x734, 0 },
+ { "ACTCNT", 0, 20 },
+ { "MC5_DB_CLIP_MAP", 0x738, 0 },
+ { "CLIPMapOp", 31, 1 },
+ { "CLIPMapVal", 16, 6 },
+ { "CLIPMapAddr", 0, 6 },
+ { "MC5_DB_SIZE", 0x73c, 0 },
+ { "MC5_DB_INT_ENABLE", 0x740, 0 },
+ { "MsgSel", 28, 4 },
+ { "DelActEmpty", 18, 1 },
+ { "DispQParErr", 17, 1 },
+ { "ReqQParErr", 16, 1 },
+ { "UnknownCmd", 15, 1 },
+ { "SYNCookieOff", 11, 1 },
+ { "SYNCookieBad", 10, 1 },
+ { "SYNCookie", 9, 1 },
+ { "NFASrchFail", 8, 1 },
+ { "ActRgnFull", 7, 1 },
+ { "ParityErr", 6, 1 },
+ { "LIPMiss", 5, 1 },
+ { "LIP0", 4, 1 },
+ { "Miss", 3, 1 },
+ { "RoutingHit", 2, 1 },
+ { "ActiveHit", 1, 1 },
+ { "ActiveOutHit", 0, 1 },
+ { "MC5_DB_INT_CAUSE", 0x744, 0 },
+ { "DelActEmpty", 18, 1 },
+ { "DispQParErr", 17, 1 },
+ { "ReqQParErr", 16, 1 },
+ { "UnknownCmd", 15, 1 },
+ { "SYNCookieOff", 11, 1 },
+ { "SYNCookieBad", 10, 1 },
+ { "SYNCookie", 9, 1 },
+ { "NFASrchFail", 8, 1 },
+ { "ActRgnFull", 7, 1 },
+ { "ParityErr", 6, 1 },
+ { "LIPMiss", 5, 1 },
+ { "LIP0", 4, 1 },
+ { "Miss", 3, 1 },
+ { "RoutingHit", 2, 1 },
+ { "ActiveHit", 1, 1 },
+ { "ActiveOutHit", 0, 1 },
+ { "MC5_DB_INT_TID", 0x748, 0 },
+ { "INTTID", 0, 20 },
+ { "MC5_DB_INT_PTID", 0x74c, 0 },
+ { "INTPTID", 0, 20 },
+ { "MC5_DB_DBGI_CONFIG", 0x774, 0 },
+ { "WRReqSize", 22, 10 },
+ { "SADRSel", 4, 1 },
+ { "CMDMode", 0, 3 },
+ { "MC5_DB_DBGI_REQ_CMD", 0x778, 0 },
+ { "MBusCmd", 0, 4 },
+ { "IDTCmdHi", 11, 3 },
+ { "IDTCmdLo", 0, 4 },
+ { "IDTCmd", 0, 20 },
+ { "LCMDB", 16, 11 },
+ { "LCMDA", 0, 11 },
+ { "MC5_DB_DBGI_REQ_ADDR0", 0x77c, 0 },
+ { "MC5_DB_DBGI_REQ_ADDR1", 0x780, 0 },
+ { "MC5_DB_DBGI_REQ_ADDR2", 0x784, 0 },
+ { "DBGIReqAdrHi", 0, 8 },
+ { "MC5_DB_DBGI_REQ_DATA0", 0x788, 0 },
+ { "MC5_DB_DBGI_REQ_DATA1", 0x78c, 0 },
+ { "MC5_DB_DBGI_REQ_DATA2", 0x790, 0 },
+ { "MC5_DB_DBGI_REQ_DATA3", 0x794, 0 },
+ { "MC5_DB_DBGI_REQ_DATA4", 0x798, 0 },
+ { "DBGIReqData4", 0, 16 },
+ { "MC5_DB_DBGI_REQ_MASK0", 0x79c, 0 },
+ { "MC5_DB_DBGI_REQ_MASK1", 0x7a0, 0 },
+ { "MC5_DB_DBGI_REQ_MASK2", 0x7a4, 0 },
+ { "MC5_DB_DBGI_REQ_MASK3", 0x7a8, 0 },
+ { "MC5_DB_DBGI_REQ_MASK4", 0x7ac, 0 },
+ { "DBGIReqMsk4", 0, 16 },
+ { "MC5_DB_DBGI_RSP_STATUS", 0x7b0, 0 },
+ { "DBGIRspMsg", 8, 4 },
+ { "DBGIRspMsgVld", 2, 1 },
+ { "DBGIRspHit", 1, 1 },
+ { "DBGIRspValid", 0, 1 },
+ { "MC5_DB_DBGI_RSP_DATA0", 0x7b4, 0 },
+ { "MC5_DB_DBGI_RSP_DATA1", 0x7b8, 0 },
+ { "MC5_DB_DBGI_RSP_DATA2", 0x7bc, 0 },
+ { "MC5_DB_DBGI_RSP_DATA3", 0x7c0, 0 },
+ { "MC5_DB_DBGI_RSP_DATA4", 0x7c4, 0 },
+ { "DBGIRspData3", 0, 16 },
+ { "MC5_DB_DBGI_RSP_LAST_CMD", 0x7c8, 0 },
+ { "LastCmdB", 16, 11 },
+ { "LastCmdA", 0, 11 },
+ { "MC5_DB_POPEN_DATA_WR_CMD", 0x7cc, 0 },
+ { "PO_DWR", 0, 20 },
+ { "MC5_DB_POPEN_MASK_WR_CMD", 0x7d0, 0 },
+ { "PO_MWR", 0, 20 },
+ { "MC5_DB_AOPEN_SRCH_CMD", 0x7d4, 0 },
+ { "AO_SRCH", 0, 20 },
+ { "MC5_DB_AOPEN_LRN_CMD", 0x7d8, 0 },
+ { "AO_LRN", 0, 20 },
+ { "MC5_DB_SYN_SRCH_CMD", 0x7dc, 0 },
+ { "SYN_SRCH", 0, 20 },
+ { "MC5_DB_SYN_LRN_CMD", 0x7e0, 0 },
+ { "SYN_LRN", 0, 20 },
+ { "MC5_DB_ACK_SRCH_CMD", 0x7e4, 0 },
+ { "ACK_SRCH", 0, 20 },
+ { "MC5_DB_ACK_LRN_CMD", 0x7e8, 0 },
+ { "ACK_LRN", 0, 20 },
+ { "MC5_DB_ILOOKUP_CMD", 0x7ec, 0 },
+ { "I_SRCH", 0, 20 },
+ { "MC5_DB_ELOOKUP_CMD", 0x7f0, 0 },
+ { "E_SRCH", 0, 20 },
+ { "MC5_DB_DATA_WRITE_CMD", 0x7f4, 0 },
+ { "Write", 0, 20 },
+ { "MC5_DB_DATA_READ_CMD", 0x7f8, 0 },
+ { "ReadCmd", 0, 20 },
+ { "MC5_DB_MASK_WRITE_CMD", 0x7fc, 0 },
+ { "MaskWr", 0, 16 },
+ { NULL }
+};
+
+struct reg_info t3c_xgmac0_0_regs[] = {
+ { "XGM_TX_CTRL", 0x800, 0 },
+ { "SendPause", 2, 1 },
+ { "SendZeroPause", 1, 1 },
+ { "TxEn", 0, 1 },
+ { "XGM_TX_CFG", 0x804, 0 },
+ { "CfgClkSpeed", 2, 3 },
+ { "StretchMode", 1, 1 },
+ { "TxPauseEn", 0, 1 },
+ { "XGM_TX_PAUSE_QUANTA", 0x808, 0 },
+ { "TxPauseQuanta", 0, 16 },
+ { "XGM_RX_CTRL", 0x80c, 0 },
+ { "RxEn", 0, 1 },
+ { "XGM_RX_CFG", 0x810, 0 },
+ { "Con802_3Preamble", 12, 1 },
+ { "EnNon802_3Preamble", 11, 1 },
+ { "CopyPreamble", 10, 1 },
+ { "DisPauseFrames", 9, 1 },
+ { "En1536BFrames", 8, 1 },
+ { "EnJumbo", 7, 1 },
+ { "RmFCS", 6, 1 },
+ { "DisNonVlan", 5, 1 },
+ { "EnExtMatch", 4, 1 },
+ { "EnHashUcast", 3, 1 },
+ { "EnHashMcast", 2, 1 },
+ { "DisBCast", 1, 1 },
+ { "CopyAllFrames", 0, 1 },
+ { "XGM_RX_HASH_LOW", 0x814, 0 },
+ { "XGM_RX_HASH_HIGH", 0x818, 0 },
+ { "XGM_RX_EXACT_MATCH_LOW_1", 0x81c, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_1", 0x820, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_2", 0x824, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_2", 0x828, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_3", 0x82c, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_3", 0x830, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_4", 0x834, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_4", 0x838, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_5", 0x83c, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_5", 0x840, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_6", 0x844, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_6", 0x848, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_7", 0x84c, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_7", 0x850, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_8", 0x854, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_8", 0x858, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_TYPE_MATCH_1", 0x85c, 0 },
+ { "EnTypeMatch", 31, 1 },
+ { "type", 0, 16 },
+ { "XGM_RX_TYPE_MATCH_2", 0x860, 0 },
+ { "EnTypeMatch", 31, 1 },
+ { "type", 0, 16 },
+ { "XGM_RX_TYPE_MATCH_3", 0x864, 0 },
+ { "EnTypeMatch", 31, 1 },
+ { "type", 0, 16 },
+ { "XGM_RX_TYPE_MATCH_4", 0x868, 0 },
+ { "EnTypeMatch", 31, 1 },
+ { "type", 0, 16 },
+ { "XGM_INT_STATUS", 0x86c, 0 },
+ { "XGMIIExtInt", 10, 1 },
+ { "LinkFaultChange", 9, 1 },
+ { "PhyFrameComplete", 8, 1 },
+ { "PauseFrameTxmt", 7, 1 },
+ { "PauseCntrTimeOut", 6, 1 },
+ { "Non0PauseRcvd", 5, 1 },
+ { "StatOFlow", 4, 1 },
+ { "TxErrFIFO", 3, 1 },
+ { "TxUFlow", 2, 1 },
+ { "FrameTxmt", 1, 1 },
+ { "FrameRcvd", 0, 1 },
+ { "XGM_XGM_INT_MASK", 0x870, 0 },
+ { "XGMIIExtInt", 10, 1 },
+ { "LinkFaultChange", 9, 1 },
+ { "PhyFrameComplete", 8, 1 },
+ { "PauseFrameTxmt", 7, 1 },
+ { "PauseCntrTimeOut", 6, 1 },
+ { "Non0PauseRcvd", 5, 1 },
+ { "StatOFlow", 4, 1 },
+ { "TxErrFIFO", 3, 1 },
+ { "TxUFlow", 2, 1 },
+ { "FrameTxmt", 1, 1 },
+ { "FrameRcvd", 0, 1 },
+ { "XGM_XGM_INT_ENABLE", 0x874, 0 },
+ { "XGMIIExtInt", 10, 1 },
+ { "LinkFaultChange", 9, 1 },
+ { "PhyFrameComplete", 8, 1 },
+ { "PauseFrameTxmt", 7, 1 },
+ { "PauseCntrTimeOut", 6, 1 },
+ { "Non0PauseRcvd", 5, 1 },
+ { "StatOFlow", 4, 1 },
+ { "TxErrFIFO", 3, 1 },
+ { "TxUFlow", 2, 1 },
+ { "FrameTxmt", 1, 1 },
+ { "FrameRcvd", 0, 1 },
+ { "XGM_XGM_INT_DISABLE", 0x878, 0 },
+ { "XGMIIExtInt", 10, 1 },
+ { "LinkFaultChange", 9, 1 },
+ { "PhyFrameComplete", 8, 1 },
+ { "PauseFrameTxmt", 7, 1 },
+ { "PauseCntrTimeOut", 6, 1 },
+ { "Non0PauseRcvd", 5, 1 },
+ { "StatOFlow", 4, 1 },
+ { "TxErrFIFO", 3, 1 },
+ { "TxUFlow", 2, 1 },
+ { "FrameTxmt", 1, 1 },
+ { "FrameRcvd", 0, 1 },
+ { "XGM_TX_PAUSE_TIMER", 0x87c, 0 },
+ { "CurPauseTimer", 0, 16 },
+ { "XGM_STAT_CTRL", 0x880, 0 },
+ { "ReadSnpShot", 4, 1 },
+ { "TakeSnpShot", 3, 1 },
+ { "ClrStats", 2, 1 },
+ { "IncrStats", 1, 1 },
+ { "EnTestModeWr", 0, 1 },
+ { "XGM_RXFIFO_CFG", 0x884, 0 },
+ { "RxFIFO_empty", 31, 1 },
+ { "RxFIFO_full", 30, 1 },
+ { "RxFIFOPauseHWM", 17, 12 },
+ { "RxFIFOPauseLWM", 5, 12 },
+ { "ForcedPause", 4, 1 },
+ { "ExternLoopback", 3, 1 },
+ { "RxByteSwap", 2, 1 },
+ { "RxStrFrwrd", 1, 1 },
+ { "DisErrFrames", 0, 1 },
+ { "XGM_TXFIFO_CFG", 0x888, 0 },
+ { "TxFIFO_empty", 31, 1 },
+ { "TxFIFO_full", 30, 1 },
+ { "UnderunFix", 22, 1 },
+ { "EnDropPkt", 21, 1 },
+ { "TxIPG", 13, 8 },
+ { "TxFIFOThresh", 4, 9 },
+ { "InternLoopback", 3, 1 },
+ { "TxByteSwap", 2, 1 },
+ { "DisCRC", 1, 1 },
+ { "DisPreAmble", 0, 1 },
+ { "XGM_SLOW_TIMER", 0x88c, 0 },
+ { "PauseSlowTimerEn", 31, 1 },
+ { "PauseSlowTimer", 0, 20 },
+ { "XGM_PAUSE_TIMER", 0x890, 0 },
+ { "PauseTimer", 0, 20 },
+ { "XGM_XAUI_PCS_TEST", 0x894, 0 },
+ { "TestPattern", 1, 2 },
+ { "EnTest", 0, 1 },
+ { "XGM_RGMII_CTRL", 0x898, 0 },
+ { "PhAlignFIFOThresh", 1, 2 },
+ { "TxClk90Shift", 0, 1 },
+ { "XGM_RGMII_IMP", 0x89c, 0 },
+ { "CalReset", 8, 1 },
+ { "CalUpdate", 7, 1 },
+ { "ImpSetUpdate", 6, 1 },
+ { "RGMIIImpPD", 3, 3 },
+ { "RGMIIImpPU", 0, 3 },
+ { "XGM_RX_MAX_PKT_SIZE", 0x8a8, 0 },
+ { "RxMaxFramerSize", 17, 14 },
+ { "RxEnErrorGather", 16, 1 },
+ { "RxEnSingleFlit", 15, 1 },
+ { "RxEnFramer", 14, 1 },
+ { "RxMaxPktSize", 0, 14 },
+ { "XGM_RESET_CTRL", 0x8ac, 0 },
+ { "XGMAC_STOP_EN", 4, 1 },
+ { "XG2G_Reset_", 3, 1 },
+ { "RGMII_Reset_", 2, 1 },
+ { "PCS_Reset_", 1, 1 },
+ { "MAC_Reset_", 0, 1 },
+ { "XGM_XAUI1G_CTRL", 0x8b0, 0 },
+ { "XAUI1GLinkId", 0, 2 },
+ { "XGM_SERDES_LANE_CTRL", 0x8b4, 0 },
+ { "LaneReversal", 8, 1 },
+ { "TxPolarity", 4, 4 },
+ { "RxPolarity", 0, 4 },
+ { "XGM_PORT_CFG", 0x8b8, 0 },
+ { "SafeSpeedChange", 4, 1 },
+ { "ClkDivReset_", 3, 1 },
+ { "PortSpeed", 1, 2 },
+ { "EnRGMII", 0, 1 },
+ { "XGM_EPIO_DATA0", 0x8c0, 0 },
+ { "XGM_EPIO_DATA1", 0x8c4, 0 },
+ { "XGM_EPIO_DATA2", 0x8c8, 0 },
+ { "XGM_EPIO_DATA3", 0x8cc, 0 },
+ { "XGM_EPIO_OP", 0x8d0, 0 },
+ { "PIO_Ready", 31, 1 },
+ { "PIO_WrRd", 24, 1 },
+ { "PIO_Address", 0, 8 },
+ { "XGM_INT_ENABLE", 0x8d4, 0 },
+ { "XAUIPCSDECErr", 24, 1 },
+ { "RGMIIRxFIFOOverflow", 23, 1 },
+ { "RGMIIRxFIFOUnderflow", 22, 1 },
+ { "RxPktSizeError", 21, 1 },
+ { "WOLPatDetected", 20, 1 },
+ { "TXFIFO_prty_err", 17, 3 },
+ { "RXFIFO_prty_err", 14, 3 },
+ { "TXFIFO_underrun", 13, 1 },
+ { "RXFIFO_overflow", 12, 1 },
+ { "SERDESBISTErr", 8, 4 },
+ { "SERDESLowSigChange", 4, 4 },
+ { "XAUIPCSCTCErr", 3, 1 },
+ { "XAUIPCSAlignChange", 2, 1 },
+ { "RGMIILinkStsChange", 1, 1 },
+ { "xgm_int", 0, 1 },
+ { "XGM_INT_CAUSE", 0x8d8, 0 },
+ { "XAUIPCSDECErr", 24, 1 },
+ { "RGMIIRxFIFOOverflow", 23, 1 },
+ { "RGMIIRxFIFOUnderflow", 22, 1 },
+ { "RxPktSizeError", 21, 1 },
+ { "WOLPatDetected", 20, 1 },
+ { "TXFIFO_prty_err", 17, 3 },
+ { "RXFIFO_prty_err", 14, 3 },
+ { "TXFIFO_underrun", 13, 1 },
+ { "RXFIFO_overflow", 12, 1 },
+ { "SERDESBISTErr", 8, 4 },
+ { "SERDESLowSigChange", 4, 4 },
+ { "XAUIPCSCTCErr", 3, 1 },
+ { "XAUIPCSAlignChange", 2, 1 },
+ { "RGMIILinkStsChange", 1, 1 },
+ { "xgm_int", 0, 1 },
+ { "XGM_XAUI_ACT_CTRL", 0x8dc, 0 },
+ { "TxEn", 1, 1 },
+ { "RxEn", 0, 1 },
+ { "XGM_SERDES_CTRL0", 0x8e0, 0 },
+ { "IntSerLPBK3", 27, 1 },
+ { "IntSerLPBK2", 26, 1 },
+ { "IntSerLPBK1", 25, 1 },
+ { "IntSerLPBK0", 24, 1 },
+ { "Reset3", 23, 1 },
+ { "Reset2", 22, 1 },
+ { "Reset1", 21, 1 },
+ { "Reset0", 20, 1 },
+ { "Pwrdn3", 19, 1 },
+ { "Pwrdn2", 18, 1 },
+ { "Pwrdn1", 17, 1 },
+ { "Pwrdn0", 16, 1 },
+ { "ResetPLL23", 15, 1 },
+ { "ResetPLL01", 14, 1 },
+ { "PW23", 12, 2 },
+ { "PW01", 10, 2 },
+ { "Deq", 6, 4 },
+ { "Dtx", 2, 4 },
+ { "LoDrv", 1, 1 },
+ { "HiDrv", 0, 1 },
+ { "XGM_SERDES_CTRL1", 0x8e4, 0 },
+ { "FmOffset3", 19, 5 },
+ { "FmOffsetEn3", 18, 1 },
+ { "FmOffset2", 13, 5 },
+ { "FmOffsetEn2", 12, 1 },
+ { "FmOffset1", 7, 5 },
+ { "FmOffsetEn1", 6, 1 },
+ { "FmOffset0", 1, 5 },
+ { "FmOffsetEn0", 0, 1 },
+ { "XGM_SERDES_CTRL2", 0x8e8, 0 },
+ { "DnIn3", 11, 1 },
+ { "UpIn3", 10, 1 },
+ { "RxSlave3", 9, 1 },
+ { "DnIn2", 8, 1 },
+ { "UpIn2", 7, 1 },
+ { "RxSlave2", 6, 1 },
+ { "DnIn1", 5, 1 },
+ { "UpIn1", 4, 1 },
+ { "RxSlave1", 3, 1 },
+ { "DnIn0", 2, 1 },
+ { "UpIn0", 1, 1 },
+ { "RxSlave0", 0, 1 },
+ { "XGM_SERDES_CTRL3", 0x8ec, 0 },
+ { "ExtBISTChkErrClr3", 31, 1 },
+ { "ExtBISTChkEn3", 30, 1 },
+ { "ExtBISTGenEn3", 29, 1 },
+ { "ExtBISTPat3", 26, 3 },
+ { "ExtParReset3", 25, 1 },
+ { "ExtParLPBK3", 24, 1 },
+ { "ExtBISTChkErrClr2", 23, 1 },
+ { "ExtBISTChkEn2", 22, 1 },
+ { "ExtBISTGenEn2", 21, 1 },
+ { "ExtBISTPat2", 18, 3 },
+ { "ExtParReset2", 17, 1 },
+ { "ExtParLPBK2", 16, 1 },
+ { "ExtBISTChkErrClr1", 15, 1 },
+ { "ExtBISTChkEn1", 14, 1 },
+ { "ExtBISTGenEn1", 13, 1 },
+ { "ExtBISTPat1", 10, 3 },
+ { "ExtParReset1", 9, 1 },
+ { "ExtParLPBK1", 8, 1 },
+ { "ExtBISTChkErrClr0", 7, 1 },
+ { "ExtBISTChkEn0", 6, 1 },
+ { "ExtBISTGenEn0", 5, 1 },
+ { "ExtBISTPat0", 2, 3 },
+ { "ExtParReset0", 1, 1 },
+ { "ExtParLPBK0", 0, 1 },
+ { "XGM_SERDES_STAT0", 0x8f0, 0 },
+ { "ExtBISTChkErrCnt0", 4, 24 },
+ { "ExtBISTChkFmd0", 3, 1 },
+ { "LowSigForceEn0", 2, 1 },
+ { "LowSigForceValue0", 1, 1 },
+ { "LowSig0", 0, 1 },
+ { "XGM_SERDES_STAT1", 0x8f4, 0 },
+ { "ExtBISTChkErrCnt1", 4, 24 },
+ { "ExtBISTChkFmd1", 3, 1 },
+ { "LowSigForceEn1", 2, 1 },
+ { "LowSigForceValue1", 1, 1 },
+ { "LowSig1", 0, 1 },
+ { "XGM_SERDES_STAT2", 0x8f8, 0 },
+ { "ExtBISTChkErrCnt2", 4, 24 },
+ { "ExtBISTChkFmd2", 3, 1 },
+ { "LowSigForceEn2", 2, 1 },
+ { "LowSigForceValue2", 1, 1 },
+ { "LowSig2", 0, 1 },
+ { "XGM_SERDES_STAT3", 0x8fc, 0 },
+ { "ExtBISTChkErrCnt3", 4, 24 },
+ { "ExtBISTChkFmd3", 3, 1 },
+ { "LowSigForceEn3", 2, 1 },
+ { "LowSigForceValue3", 1, 1 },
+ { "LowSig3", 0, 1 },
+ { "XGM_STAT_TX_BYTE_LOW", 0x900, 0 },
+ { "XGM_STAT_TX_BYTE_HIGH", 0x904, 0 },
+ { "TxBytes_high", 0, 13 },
+ { "XGM_STAT_TX_FRAME_LOW", 0x908, 0 },
+ { "XGM_STAT_TX_FRAME_HIGH", 0x90c, 0 },
+ { "TxFrames_high", 0, 4 },
+ { "XGM_STAT_TX_BCAST", 0x910, 0 },
+ { "XGM_STAT_TX_MCAST", 0x914, 0 },
+ { "XGM_STAT_TX_PAUSE", 0x918, 0 },
+ { "XGM_STAT_TX_64B_FRAMES", 0x91c, 0 },
+ { "XGM_STAT_TX_65_127B_FRAMES", 0x920, 0 },
+ { "XGM_STAT_TX_128_255B_FRAMES", 0x924, 0 },
+ { "XGM_STAT_TX_256_511B_FRAMES", 0x928, 0 },
+ { "XGM_STAT_TX_512_1023B_FRAMES", 0x92c, 0 },
+ { "XGM_STAT_TX_1024_1518B_FRAMES", 0x930, 0 },
+ { "XGM_STAT_TX_1519_MAXB_FRAMES", 0x934, 0 },
+ { "XGM_STAT_TX_ERR_FRAMES", 0x938, 0 },
+ { "XGM_STAT_RX_BYTES_LOW", 0x93c, 0 },
+ { "XGM_STAT_RX_BYTES_HIGH", 0x940, 0 },
+ { "RxBytes_high", 0, 13 },
+ { "XGM_STAT_RX_FRAMES_LOW", 0x944, 0 },
+ { "XGM_STAT_RX_FRAMES_HIGH", 0x948, 0 },
+ { "RxFrames_high", 0, 4 },
+ { "XGM_STAT_RX_BCAST_FRAMES", 0x94c, 0 },
+ { "XGM_STAT_RX_MCAST_FRAMES", 0x950, 0 },
+ { "XGM_STAT_RX_PAUSE_FRAMES", 0x954, 0 },
+ { "RxPauseFrames", 0, 16 },
+ { "XGM_STAT_RX_64B_FRAMES", 0x958, 0 },
+ { "XGM_STAT_RX_65_127B_FRAMES", 0x95c, 0 },
+ { "XGM_STAT_RX_128_255B_FRAMES", 0x960, 0 },
+ { "XGM_STAT_RX_256_511B_FRAMES", 0x964, 0 },
+ { "XGM_STAT_RX_512_1023B_FRAMES", 0x968, 0 },
+ { "XGM_STAT_RX_1024_1518B_FRAMES", 0x96c, 0 },
+ { "XGM_STAT_RX_1519_MAXB_FRAMES", 0x970, 0 },
+ { "XGM_STAT_RX_SHORT_FRAMES", 0x974, 0 },
+ { "RxShortFrames", 0, 16 },
+ { "XGM_STAT_RX_OVERSIZE_FRAMES", 0x978, 0 },
+ { "RxOversizeFrames", 0, 16 },
+ { "XGM_STAT_RX_JABBER_FRAMES", 0x97c, 0 },
+ { "RxJabberFrames", 0, 16 },
+ { "XGM_STAT_RX_CRC_ERR_FRAMES", 0x980, 0 },
+ { "RxCRCErrFrames", 0, 16 },
+ { "XGM_STAT_RX_LENGTH_ERR_FRAMES", 0x984, 0 },
+ { "RxLengthErrFrames", 0, 16 },
+ { "XGM_STAT_RX_SYM_CODE_ERR_FRAMES", 0x988, 0 },
+ { "RxSymCodeErrFrames", 0, 16 },
+ { "XGM_XAUI_PCS_ERR", 0x998, 0 },
+ { "PCS_SyncStatus", 5, 4 },
+ { "PCS_CTCFIFOErr", 1, 4 },
+ { "PCS_NotAligned", 0, 1 },
+ { "XGM_RGMII_STATUS", 0x99c, 0 },
+ { "GMIIDuplex", 3, 1 },
+ { "GMIISpeed", 1, 2 },
+ { "GMIILinkStatus", 0, 1 },
+ { "XGM_WOL_STATUS", 0x9a0, 0 },
+ { "PatDetected", 31, 1 },
+ { "MatchedFilter", 0, 3 },
+ { "XGM_RX_MAX_PKT_SIZE_ERR_CNT", 0x9a4, 0 },
+ { "XGM_TX_SPI4_SOP_EOP_CNT", 0x9a8, 0 },
+ { "TxSPI4SopCnt", 16, 16 },
+ { "TxSPI4EopCnt", 0, 16 },
+ { "XGM_RX_SPI4_SOP_EOP_CNT", 0x9ac, 0 },
+ { "RxSPI4SopCnt", 16, 16 },
+ { "RxSPI4EopCnt", 0, 16 },
+ { NULL }
+};
+
+struct reg_info t3c_xgmac0_1_regs[] = {
+ { "XGM_TX_CTRL", 0xa00, 0 },
+ { "SendPause", 2, 1 },
+ { "SendZeroPause", 1, 1 },
+ { "TxEn", 0, 1 },
+ { "XGM_TX_CFG", 0xa04, 0 },
+ { "CfgClkSpeed", 2, 3 },
+ { "StretchMode", 1, 1 },
+ { "TxPauseEn", 0, 1 },
+ { "XGM_TX_PAUSE_QUANTA", 0xa08, 0 },
+ { "TxPauseQuanta", 0, 16 },
+ { "XGM_RX_CTRL", 0xa0c, 0 },
+ { "RxEn", 0, 1 },
+ { "XGM_RX_CFG", 0xa10, 0 },
+ { "Con802_3Preamble", 12, 1 },
+ { "EnNon802_3Preamble", 11, 1 },
+ { "CopyPreamble", 10, 1 },
+ { "DisPauseFrames", 9, 1 },
+ { "En1536BFrames", 8, 1 },
+ { "EnJumbo", 7, 1 },
+ { "RmFCS", 6, 1 },
+ { "DisNonVlan", 5, 1 },
+ { "EnExtMatch", 4, 1 },
+ { "EnHashUcast", 3, 1 },
+ { "EnHashMcast", 2, 1 },
+ { "DisBCast", 1, 1 },
+ { "CopyAllFrames", 0, 1 },
+ { "XGM_RX_HASH_LOW", 0xa14, 0 },
+ { "XGM_RX_HASH_HIGH", 0xa18, 0 },
+ { "XGM_RX_EXACT_MATCH_LOW_1", 0xa1c, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_1", 0xa20, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_2", 0xa24, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_2", 0xa28, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_3", 0xa2c, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_3", 0xa30, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_4", 0xa34, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_4", 0xa38, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_5", 0xa3c, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_5", 0xa40, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_6", 0xa44, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_6", 0xa48, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_7", 0xa4c, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_7", 0xa50, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_EXACT_MATCH_LOW_8", 0xa54, 0 },
+ { "XGM_RX_EXACT_MATCH_HIGH_8", 0xa58, 0 },
+ { "address_high", 0, 16 },
+ { "XGM_RX_TYPE_MATCH_1", 0xa5c, 0 },
+ { "EnTypeMatch", 31, 1 },
+ { "type", 0, 16 },
+ { "XGM_RX_TYPE_MATCH_2", 0xa60, 0 },
+ { "EnTypeMatch", 31, 1 },
+ { "type", 0, 16 },
+ { "XGM_RX_TYPE_MATCH_3", 0xa64, 0 },
+ { "EnTypeMatch", 31, 1 },
+ { "type", 0, 16 },
+ { "XGM_RX_TYPE_MATCH_4", 0xa68, 0 },
+ { "EnTypeMatch", 31, 1 },
+ { "type", 0, 16 },
+ { "XGM_INT_STATUS", 0xa6c, 0 },
+ { "XGMIIExtInt", 10, 1 },
+ { "LinkFaultChange", 9, 1 },
+ { "PhyFrameComplete", 8, 1 },
+ { "PauseFrameTxmt", 7, 1 },
+ { "PauseCntrTimeOut", 6, 1 },
+ { "Non0PauseRcvd", 5, 1 },
+ { "StatOFlow", 4, 1 },
+ { "TxErrFIFO", 3, 1 },
+ { "TxUFlow", 2, 1 },
+ { "FrameTxmt", 1, 1 },
+ { "FrameRcvd", 0, 1 },
+ { "XGM_XGM_INT_MASK", 0xa70, 0 },
+ { "XGMIIExtInt", 10, 1 },
+ { "LinkFaultChange", 9, 1 },
+ { "PhyFrameComplete", 8, 1 },
+ { "PauseFrameTxmt", 7, 1 },
+ { "PauseCntrTimeOut", 6, 1 },
+ { "Non0PauseRcvd", 5, 1 },
+ { "StatOFlow", 4, 1 },
+ { "TxErrFIFO", 3, 1 },
+ { "TxUFlow", 2, 1 },
+ { "FrameTxmt", 1, 1 },
+ { "FrameRcvd", 0, 1 },
+ { "XGM_XGM_INT_ENABLE", 0xa74, 0 },
+ { "XGMIIExtInt", 10, 1 },
+ { "LinkFaultChange", 9, 1 },
+ { "PhyFrameComplete", 8, 1 },
+ { "PauseFrameTxmt", 7, 1 },
+ { "PauseCntrTimeOut", 6, 1 },
+ { "Non0PauseRcvd", 5, 1 },
+ { "StatOFlow", 4, 1 },
+ { "TxErrFIFO", 3, 1 },
+ { "TxUFlow", 2, 1 },
+ { "FrameTxmt", 1, 1 },
+ { "FrameRcvd", 0, 1 },
+ { "XGM_XGM_INT_DISABLE", 0xa78, 0 },
+ { "XGMIIExtInt", 10, 1 },
+ { "LinkFaultChange", 9, 1 },
+ { "PhyFrameComplete", 8, 1 },
+ { "PauseFrameTxmt", 7, 1 },
+ { "PauseCntrTimeOut", 6, 1 },
+ { "Non0PauseRcvd", 5, 1 },
+ { "StatOFlow", 4, 1 },
+ { "TxErrFIFO", 3, 1 },
+ { "TxUFlow", 2, 1 },
+ { "FrameTxmt", 1, 1 },
+ { "FrameRcvd", 0, 1 },
+ { "XGM_TX_PAUSE_TIMER", 0xa7c, 0 },
+ { "CurPauseTimer", 0, 16 },
+ { "XGM_STAT_CTRL", 0xa80, 0 },
+ { "ReadSnpShot", 4, 1 },
+ { "TakeSnpShot", 3, 1 },
+ { "ClrStats", 2, 1 },
+ { "IncrStats", 1, 1 },
+ { "EnTestModeWr", 0, 1 },
+ { "XGM_RXFIFO_CFG", 0xa84, 0 },
+ { "RxFIFO_empty", 31, 1 },
+ { "RxFIFO_full", 30, 1 },
+ { "RxFIFOPauseHWM", 17, 12 },
+ { "RxFIFOPauseLWM", 5, 12 },
+ { "ForcedPause", 4, 1 },
+ { "ExternLoopback", 3, 1 },
+ { "RxByteSwap", 2, 1 },
+ { "RxStrFrwrd", 1, 1 },
+ { "DisErrFrames", 0, 1 },
+ { "XGM_TXFIFO_CFG", 0xa88, 0 },
+ { "TxFIFO_empty", 31, 1 },
+ { "TxFIFO_full", 30, 1 },
+ { "UnderunFix", 22, 1 },
+ { "EnDropPkt", 21, 1 },
+ { "TxIPG", 13, 8 },
+ { "TxFIFOThresh", 4, 9 },
+ { "InternLoopback", 3, 1 },
+ { "TxByteSwap", 2, 1 },
+ { "DisCRC", 1, 1 },
+ { "DisPreAmble", 0, 1 },
+ { "XGM_SLOW_TIMER", 0xa8c, 0 },
+ { "PauseSlowTimerEn", 31, 1 },
+ { "PauseSlowTimer", 0, 20 },
+ { "XGM_PAUSE_TIMER", 0xa90, 0 },
+ { "PauseTimer", 0, 20 },
+ { "XGM_XAUI_PCS_TEST", 0xa94, 0 },
+ { "TestPattern", 1, 2 },
+ { "EnTest", 0, 1 },
+ { "XGM_RGMII_CTRL", 0xa98, 0 },
+ { "PhAlignFIFOThresh", 1, 2 },
+ { "TxClk90Shift", 0, 1 },
+ { "XGM_RGMII_IMP", 0xa9c, 0 },
+ { "CalReset", 8, 1 },
+ { "CalUpdate", 7, 1 },
+ { "ImpSetUpdate", 6, 1 },
+ { "RGMIIImpPD", 3, 3 },
+ { "RGMIIImpPU", 0, 3 },
+ { "XGM_RX_MAX_PKT_SIZE", 0xaa8, 0 },
+ { "RxMaxFramerSize", 17, 14 },
+ { "RxEnErrorGather", 16, 1 },
+ { "RxEnSingleFlit", 15, 1 },
+ { "RxEnFramer", 14, 1 },
+ { "RxMaxPktSize", 0, 14 },
+ { "XGM_RESET_CTRL", 0xaac, 0 },
+ { "XGMAC_STOP_EN", 4, 1 },
+ { "XG2G_Reset_", 3, 1 },
+ { "RGMII_Reset_", 2, 1 },
+ { "PCS_Reset_", 1, 1 },
+ { "MAC_Reset_", 0, 1 },
+ { "XGM_XAUI1G_CTRL", 0xab0, 0 },
+ { "XAUI1GLinkId", 0, 2 },
+ { "XGM_SERDES_LANE_CTRL", 0xab4, 0 },
+ { "LaneReversal", 8, 1 },
+ { "TxPolarity", 4, 4 },
+ { "RxPolarity", 0, 4 },
+ { "XGM_PORT_CFG", 0xab8, 0 },
+ { "SafeSpeedChange", 4, 1 },
+ { "ClkDivReset_", 3, 1 },
+ { "PortSpeed", 1, 2 },
+ { "EnRGMII", 0, 1 },
+ { "XGM_EPIO_DATA0", 0xac0, 0 },
+ { "XGM_EPIO_DATA1", 0xac4, 0 },
+ { "XGM_EPIO_DATA2", 0xac8, 0 },
+ { "XGM_EPIO_DATA3", 0xacc, 0 },
+ { "XGM_EPIO_OP", 0xad0, 0 },
+ { "PIO_Ready", 31, 1 },
+ { "PIO_WrRd", 24, 1 },
+ { "PIO_Address", 0, 8 },
+ { "XGM_INT_ENABLE", 0xad4, 0 },
+ { "XAUIPCSDECErr", 24, 1 },
+ { "RGMIIRxFIFOOverflow", 23, 1 },
+ { "RGMIIRxFIFOUnderflow", 22, 1 },
+ { "RxPktSizeError", 21, 1 },
+ { "WOLPatDetected", 20, 1 },
+ { "TXFIFO_prty_err", 17, 3 },
+ { "RXFIFO_prty_err", 14, 3 },
+ { "TXFIFO_underrun", 13, 1 },
+ { "RXFIFO_overflow", 12, 1 },
+ { "SERDESBISTErr", 8, 4 },
+ { "SERDESLowSigChange", 4, 4 },
+ { "XAUIPCSCTCErr", 3, 1 },
+ { "XAUIPCSAlignChange", 2, 1 },
+ { "RGMIILinkStsChange", 1, 1 },
+ { "xgm_int", 0, 1 },
+ { "XGM_INT_CAUSE", 0xad8, 0 },
+ { "XAUIPCSDECErr", 24, 1 },
+ { "RGMIIRxFIFOOverflow", 23, 1 },
+ { "RGMIIRxFIFOUnderflow", 22, 1 },
+ { "RxPktSizeError", 21, 1 },
+ { "WOLPatDetected", 20, 1 },
+ { "TXFIFO_prty_err", 17, 3 },
+ { "RXFIFO_prty_err", 14, 3 },
+ { "TXFIFO_underrun", 13, 1 },
+ { "RXFIFO_overflow", 12, 1 },
+ { "SERDESBISTErr", 8, 4 },
+ { "SERDESLowSigChange", 4, 4 },
+ { "XAUIPCSCTCErr", 3, 1 },
+ { "XAUIPCSAlignChange", 2, 1 },
+ { "RGMIILinkStsChange", 1, 1 },
+ { "xgm_int", 0, 1 },
+ { "XGM_XAUI_ACT_CTRL", 0xadc, 0 },
+ { "TxEn", 1, 1 },
+ { "RxEn", 0, 1 },
+ { "XGM_SERDES_CTRL0", 0xae0, 0 },
+ { "IntSerLPBK3", 27, 1 },
+ { "IntSerLPBK2", 26, 1 },
+ { "IntSerLPBK1", 25, 1 },
+ { "IntSerLPBK0", 24, 1 },
+ { "Reset3", 23, 1 },
+ { "Reset2", 22, 1 },
+ { "Reset1", 21, 1 },
+ { "Reset0", 20, 1 },
+ { "Pwrdn3", 19, 1 },
+ { "Pwrdn2", 18, 1 },
+ { "Pwrdn1", 17, 1 },
+ { "Pwrdn0", 16, 1 },
+ { "ResetPLL23", 15, 1 },
+ { "ResetPLL01", 14, 1 },
+ { "PW23", 12, 2 },
+ { "PW01", 10, 2 },
+ { "Deq", 6, 4 },
+ { "Dtx", 2, 4 },
+ { "LoDrv", 1, 1 },
+ { "HiDrv", 0, 1 },
+ { "XGM_SERDES_CTRL1", 0xae4, 0 },
+ { "FmOffset3", 19, 5 },
+ { "FmOffsetEn3", 18, 1 },
+ { "FmOffset2", 13, 5 },
+ { "FmOffsetEn2", 12, 1 },
+ { "FmOffset1", 7, 5 },
+ { "FmOffsetEn1", 6, 1 },
+ { "FmOffset0", 1, 5 },
+ { "FmOffsetEn0", 0, 1 },
+ { "XGM_SERDES_CTRL2", 0xae8, 0 },
+ { "DnIn3", 11, 1 },
+ { "UpIn3", 10, 1 },
+ { "RxSlave3", 9, 1 },
+ { "DnIn2", 8, 1 },
+ { "UpIn2", 7, 1 },
+ { "RxSlave2", 6, 1 },
+ { "DnIn1", 5, 1 },
+ { "UpIn1", 4, 1 },
+ { "RxSlave1", 3, 1 },
+ { "DnIn0", 2, 1 },
+ { "UpIn0", 1, 1 },
+ { "RxSlave0", 0, 1 },
+ { "XGM_SERDES_CTRL3", 0xaec, 0 },
+ { "ExtBISTChkErrClr3", 31, 1 },
+ { "ExtBISTChkEn3", 30, 1 },
+ { "ExtBISTGenEn3", 29, 1 },
+ { "ExtBISTPat3", 26, 3 },
+ { "ExtParReset3", 25, 1 },
+ { "ExtParLPBK3", 24, 1 },
+ { "ExtBISTChkErrClr2", 23, 1 },
+ { "ExtBISTChkEn2", 22, 1 },
+ { "ExtBISTGenEn2", 21, 1 },
+ { "ExtBISTPat2", 18, 3 },
+ { "ExtParReset2", 17, 1 },
+ { "ExtParLPBK2", 16, 1 },
+ { "ExtBISTChkErrClr1", 15, 1 },
+ { "ExtBISTChkEn1", 14, 1 },
+ { "ExtBISTGenEn1", 13, 1 },
+ { "ExtBISTPat1", 10, 3 },
+ { "ExtParReset1", 9, 1 },
+ { "ExtParLPBK1", 8, 1 },
+ { "ExtBISTChkErrClr0", 7, 1 },
+ { "ExtBISTChkEn0", 6, 1 },
+ { "ExtBISTGenEn0", 5, 1 },
+ { "ExtBISTPat0", 2, 3 },
+ { "ExtParReset0", 1, 1 },
+ { "ExtParLPBK0", 0, 1 },
+ { "XGM_SERDES_STAT0", 0xaf0, 0 },
+ { "ExtBISTChkErrCnt0", 4, 24 },
+ { "ExtBISTChkFmd0", 3, 1 },
+ { "LowSigForceEn0", 2, 1 },
+ { "LowSigForceValue0", 1, 1 },
+ { "LowSig0", 0, 1 },
+ { "XGM_SERDES_STAT1", 0xaf4, 0 },
+ { "ExtBISTChkErrCnt1", 4, 24 },
+ { "ExtBISTChkFmd1", 3, 1 },
+ { "LowSigForceEn1", 2, 1 },
+ { "LowSigForceValue1", 1, 1 },
+ { "LowSig1", 0, 1 },
+ { "XGM_SERDES_STAT2", 0xaf8, 0 },
+ { "ExtBISTChkErrCnt2", 4, 24 },
+ { "ExtBISTChkFmd2", 3, 1 },
+ { "LowSigForceEn2", 2, 1 },
+ { "LowSigForceValue2", 1, 1 },
+ { "LowSig2", 0, 1 },
+ { "XGM_SERDES_STAT3", 0xafc, 0 },
+ { "ExtBISTChkErrCnt3", 4, 24 },
+ { "ExtBISTChkFmd3", 3, 1 },
+ { "LowSigForceEn3", 2, 1 },
+ { "LowSigForceValue3", 1, 1 },
+ { "LowSig3", 0, 1 },
+ { "XGM_STAT_TX_BYTE_LOW", 0xb00, 0 },
+ { "XGM_STAT_TX_BYTE_HIGH", 0xb04, 0 },
+ { "TxBytes_high", 0, 13 },
+ { "XGM_STAT_TX_FRAME_LOW", 0xb08, 0 },
+ { "XGM_STAT_TX_FRAME_HIGH", 0xb0c, 0 },
+ { "TxFrames_high", 0, 4 },
+ { "XGM_STAT_TX_BCAST", 0xb10, 0 },
+ { "XGM_STAT_TX_MCAST", 0xb14, 0 },
+ { "XGM_STAT_TX_PAUSE", 0xb18, 0 },
+ { "XGM_STAT_TX_64B_FRAMES", 0xb1c, 0 },
+ { "XGM_STAT_TX_65_127B_FRAMES", 0xb20, 0 },
+ { "XGM_STAT_TX_128_255B_FRAMES", 0xb24, 0 },
+ { "XGM_STAT_TX_256_511B_FRAMES", 0xb28, 0 },
+ { "XGM_STAT_TX_512_1023B_FRAMES", 0xb2c, 0 },
+ { "XGM_STAT_TX_1024_1518B_FRAMES", 0xb30, 0 },
+ { "XGM_STAT_TX_1519_MAXB_FRAMES", 0xb34, 0 },
+ { "XGM_STAT_TX_ERR_FRAMES", 0xb38, 0 },
+ { "XGM_STAT_RX_BYTES_LOW", 0xb3c, 0 },
+ { "XGM_STAT_RX_BYTES_HIGH", 0xb40, 0 },
+ { "RxBytes_high", 0, 13 },
+ { "XGM_STAT_RX_FRAMES_LOW", 0xb44, 0 },
+ { "XGM_STAT_RX_FRAMES_HIGH", 0xb48, 0 },
+ { "RxFrames_high", 0, 4 },
+ { "XGM_STAT_RX_BCAST_FRAMES", 0xb4c, 0 },
+ { "XGM_STAT_RX_MCAST_FRAMES", 0xb50, 0 },
+ { "XGM_STAT_RX_PAUSE_FRAMES", 0xb54, 0 },
+ { "RxPauseFrames", 0, 16 },
+ { "XGM_STAT_RX_64B_FRAMES", 0xb58, 0 },
+ { "XGM_STAT_RX_65_127B_FRAMES", 0xb5c, 0 },
+ { "XGM_STAT_RX_128_255B_FRAMES", 0xb60, 0 },
+ { "XGM_STAT_RX_256_511B_FRAMES", 0xb64, 0 },
+ { "XGM_STAT_RX_512_1023B_FRAMES", 0xb68, 0 },
+ { "XGM_STAT_RX_1024_1518B_FRAMES", 0xb6c, 0 },
+ { "XGM_STAT_RX_1519_MAXB_FRAMES", 0xb70, 0 },
+ { "XGM_STAT_RX_SHORT_FRAMES", 0xb74, 0 },
+ { "RxShortFrames", 0, 16 },
+ { "XGM_STAT_RX_OVERSIZE_FRAMES", 0xb78, 0 },
+ { "RxOversizeFrames", 0, 16 },
+ { "XGM_STAT_RX_JABBER_FRAMES", 0xb7c, 0 },
+ { "RxJabberFrames", 0, 16 },
+ { "XGM_STAT_RX_CRC_ERR_FRAMES", 0xb80, 0 },
+ { "RxCRCErrFrames", 0, 16 },
+ { "XGM_STAT_RX_LENGTH_ERR_FRAMES", 0xb84, 0 },
+ { "RxLengthErrFrames", 0, 16 },
+ { "XGM_STAT_RX_SYM_CODE_ERR_FRAMES", 0xb88, 0 },
+ { "RxSymCodeErrFrames", 0, 16 },
+ { "XGM_XAUI_PCS_ERR", 0xb98, 0 },
+ { "PCS_SyncStatus", 5, 4 },
+ { "PCS_CTCFIFOErr", 1, 4 },
+ { "PCS_NotAligned", 0, 1 },
+ { "XGM_RGMII_STATUS", 0xb9c, 0 },
+ { "GMIIDuplex", 3, 1 },
+ { "GMIISpeed", 1, 2 },
+ { "GMIILinkStatus", 0, 1 },
+ { "XGM_WOL_STATUS", 0xba0, 0 },
+ { "PatDetected", 31, 1 },
+ { "MatchedFilter", 0, 3 },
+ { "XGM_RX_MAX_PKT_SIZE_ERR_CNT", 0xba4, 0 },
+ { "XGM_TX_SPI4_SOP_EOP_CNT", 0xba8, 0 },
+ { "TxSPI4SopCnt", 16, 16 },
+ { "TxSPI4EopCnt", 0, 16 },
+ { "XGM_RX_SPI4_SOP_EOP_CNT", 0xbac, 0 },
+ { "RxSPI4SopCnt", 16, 16 },
+ { "RxSPI4EopCnt", 0, 16 },
+ { NULL }
+};
+
diff --git a/usr.sbin/cxgbtool/version.h b/usr.sbin/cxgbtool/version.h
new file mode 100644
index 0000000..403a8be
--- /dev/null
+++ b/usr.sbin/cxgbtool/version.h
@@ -0,0 +1,32 @@
+/*****************************************************************************
+ * *
+ * File: *
+ * version.h *
+ * *
+ * Description: *
+ * cxgbtool userspace utility version defines. *
+ * *
+ * http://www.chelsio.com *
+ * *
+ * Copyright (c) 2003 - 2008 Chelsio Communications, Inc. *
+ * All rights reserved. *
+ * *
+ * Maintainers: maintainers@chelsio.com *
+ * *
+ * History: *
+ * *
+ ****************************************************************************/
+/* $Date: 2007/02/05 18:46:24 $ $RCSfile: version.h,v $ $Revision: 1.9 $ */
+
+/*
+ * $FreeBSD$
+ */
+
+#ifndef __CXGBTOOL_VERSION_H
+#define __CXGBTOOL_VERSION_H
+
+#define PROGNAME "cxgbtool"
+#define VERSION "1.16f"
+#define COPYRIGHT "Copyright (c) 2004-2009 Chelsio Communications"
+
+#endif //__CXGBTOOL_VERSION_H
diff --git a/usr.sbin/daemon/Makefile b/usr.sbin/daemon/Makefile
new file mode 100644
index 0000000..3ca3e91
--- /dev/null
+++ b/usr.sbin/daemon/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+PROG= daemon
+MAN= daemon.8
+
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/daemon/daemon.8 b/usr.sbin/daemon/daemon.8
new file mode 100644
index 0000000..c284427
--- /dev/null
+++ b/usr.sbin/daemon/daemon.8
@@ -0,0 +1,95 @@
+.\" Copyright (c) 1999 Berkeley Software Design, 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.
+.\" 3. Berkeley Software Design Inc's name may not be used to endorse or
+.\" promote products derived from this software without specific prior
+.\" written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN 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 BERKELEY SOFTWARE DESIGN 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 19, 2007
+.Dt DAEMON 8
+.Os
+.Sh NAME
+.Nm daemon
+.Nd run detached from the controlling terminal
+.Sh SYNOPSIS
+.Nm
+.Op Fl cf
+.Op Fl p Ar pidfile
+.Op Fl u Ar user
+.Ar command arguments ...
+.Sh DESCRIPTION
+The
+.Nm
+utility detaches itself from the controlling terminal and
+executes the program specified by its arguments.
+Privileges may be lowered to the specified user.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl c
+Change the current working directory to the root
+.Pq Dq Pa / .
+.It Fl f
+Redirect standard input, standard output and standard error to
+.Pa /dev/null .
+.It Fl p Ar file
+Write the ID of the created process into the
+.Ar file
+using the
+.Xr pidfile 3
+functionality.
+Note, that the file will be created shortly before the process is
+actually executed, and will remain after the process exits (although
+it will be removed if the execution fails).
+.It Fl u Ar user
+Run the program with the rights of user specified, requires privilege.
+.El
+.Sh EXIT STATUS
+The
+.Nm
+utility exits 1 if an error is returned by the
+.Xr daemon 3
+library routine, 2 if the
+.Ar pidfile
+is requested, but cannot be opened, 3 if process is already running (pidfile
+exists and is locked),
+otherwise 0.
+.Sh DIAGNOSTICS
+If the command cannot be executed, an error message is displayed on
+standard error unless the
+.Fl f
+flag is specified.
+.Sh SEE ALSO
+.Xr setregid 2 ,
+.Xr setreuid 2 ,
+.Xr daemon 3 ,
+.Xr exec 3 ,
+.Xr pidfile 3 ,
+.Xr termios 4 ,
+.Xr tty 4
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 4.7 .
diff --git a/usr.sbin/daemon/daemon.c b/usr.sbin/daemon/daemon.c
new file mode 100644
index 0000000..540ebf3
--- /dev/null
+++ b/usr.sbin/daemon/daemon.c
@@ -0,0 +1,141 @@
+/*-
+ * Copyright (c) 1999 Berkeley Software Design, 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.
+ * 3. Berkeley Software Design Inc's name may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN 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 BERKELEY SOFTWARE DESIGN 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.
+ *
+ * From BSDI: daemon.c,v 1.2 1996/08/15 01:11:09 jch Exp
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <err.h>
+#include <errno.h>
+#include <pwd.h>
+#include <libutil.h>
+#include <login_cap.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static void restrict_process(const char *);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ struct pidfh *pfh = NULL;
+ int ch, nochdir, noclose, errcode;
+ const char *pidfile, *user;
+ pid_t otherpid;
+
+ nochdir = noclose = 1;
+ pidfile = user = NULL;
+ while ((ch = getopt(argc, argv, "-cfp:u:")) != -1) {
+ switch (ch) {
+ case 'c':
+ nochdir = 0;
+ break;
+ case 'f':
+ noclose = 0;
+ break;
+ case 'p':
+ pidfile = optarg;
+ break;
+ case 'u':
+ user = optarg;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0)
+ usage();
+
+ if (user != NULL)
+ restrict_process(user);
+
+ /*
+ * Try to open the pidfile before calling daemon(3),
+ * to be able to report the error intelligently
+ */
+ if (pidfile) {
+ pfh = pidfile_open(pidfile, 0600, &otherpid);
+ if (pfh == NULL) {
+ if (errno == EEXIST) {
+ errx(3, "process already running, pid: %d",
+ otherpid);
+ }
+ err(2, "pidfile ``%s''", pidfile);
+ }
+ }
+
+ if (daemon(nochdir, noclose) == -1)
+ err(1, NULL);
+
+ /* Now that we are the child, write out the pid */
+ if (pidfile)
+ pidfile_write(pfh);
+
+ execvp(argv[0], argv);
+
+ /*
+ * execvp() failed -- unlink pidfile if any, and
+ * report the error
+ */
+ errcode = errno; /* Preserve errcode -- unlink may reset it */
+ if (pidfile)
+ pidfile_remove(pfh);
+
+ /* The child is now running, so the exit status doesn't matter. */
+ errc(1, errcode, "%s", argv[0]);
+}
+
+static void
+restrict_process(const char *user)
+{
+ struct passwd *pw = NULL;
+
+ pw = getpwnam(user);
+ if (pw == NULL)
+ errx(1, "unknown user: %s", user);
+
+ if (setusercontext(NULL, pw, pw->pw_uid, LOGIN_SETALL) != 0)
+ errx(1, "failed to set user environment");
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr,
+ "usage: daemon [-cf] [-p pidfile] [-u user] command "
+ "arguments ...\n");
+ exit(1);
+}
diff --git a/usr.sbin/dconschat/Makefile b/usr.sbin/dconschat/Makefile
new file mode 100644
index 0000000..47efe52
--- /dev/null
+++ b/usr.sbin/dconschat/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+PROG= dconschat
+MAN= dconschat.8
+
+CFLAGS+= -I${.CURDIR}/../../sys
+
+DPADD= ${LIBKVM}
+LDADD= -lkvm
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/dconschat/dconschat.8 b/usr.sbin/dconschat/dconschat.8
new file mode 100644
index 0000000..b34d968
--- /dev/null
+++ b/usr.sbin/dconschat/dconschat.8
@@ -0,0 +1,330 @@
+.\" Copyright (c) 2003 Hidetoshi Shimokawa
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 February 11, 2003
+.Dt DCONSCHAT 8
+.Os
+.Sh NAME
+.Nm dconschat
+.Nd user interface to
+.Xr dcons 4
+.Sh SYNOPSIS
+.Nm
+.Op Fl brvwRT1
+.Op Fl e Ar escape-char
+.Op Fl h Ar hz
+.Op Fl C Ar console_port
+.Op Fl G Ar gdb_port
+.Op Fl M Ar core
+.Op Fl N Ar system
+.Nm
+.Op Fl brvwR1
+.Op Fl h Ar hz
+.Op Fl C Ar console_port
+.Op Fl G Ar gdb_port
+.Op Fl a Ar address
+.Op Fl u Ar bus_num
+.Fl t Ar target_eui64
+.Sh DESCRIPTION
+The
+.Nm
+utility is designed to provide a way for users to access
+.Xr dcons 4
+(dumb console device) on a local or remote system.
+The
+.Nm
+utility interacts with
+.Xr dcons 4
+using
+.Xr kvm 3
+or
+.Xr firewire 4 ,
+and interacts with the user over TTY or TCP/IP.
+To access remote
+.Xr dcons 4
+using
+.Xr firewire 4 ,
+you have to specify target EUI64 address using the
+.Fl t
+option. Physical DMA should be enabled on the target machine for access
+via FireWire.
+.Pp
+The
+.Nm
+utility and the
+.Xr dcons 4
+driver communicate using 2 ports, one for the console port and another
+for remote
+.Xr gdb 1
+port.
+Users are supposed to access
+.Nm
+using TTY,
+.Xr telnet 1
+and
+.Xr gdb 1 .
+You can specify listen ports for console and
+.Xr gdb 1
+port using the
+.Fl C
+and
+.Fl G
+options respectively.
+The port number 0 has special meaning that
+current TTY (stdin/stdout) is used instead of TCP/IP.
+A negative port number will disable the port.
+By analogy with
+.Xr pty 4
+device, the
+.Xr dcons 4
+acts as a slave device and
+.Nm
+acts as a master device with
+.Xr telnetd 8 .
+.Pp
+Typed characters are normally transmitted directly to
+.Xr dcons 4 .
+A escape character (the default is
+.Ql ~
+) appearing as the first character of a line is an escape signal; the
+following are recognized:
+.Bl -tag -width ident
+.It Ic ~.
+Drop the connection and exit.
+.It Ic ~^G
+Invoke kgdb on the terminal on which dconschat is running.
+.It Ic ~^R
+Reset the target over FireWire if a reset address is registered in Configuration ROM.
+.It Ic ~^Z
+Suspend the dconschat process.
+.El
+.Pp
+The following options are supported.
+.Pp
+.Bl -tag -width indent
+.It Fl b
+Translate Ctrl-C to ALT_BREAK (CR +
+.Ql ~
++ Ctrl-B) on
+.Xr gdb 1
+port.
+.It Fl r
+Replay old buffer on connection.
+.It Fl v
+Verbose debug output.
+Multiple
+.Fl v
+options increase verbosity.
+.It Fl w
+Listen on a wildcard address rather than localhost.
+.It Fl R
+Read-only.
+Do not write anything to the
+.Xr dcons 4
+buffer.
+.It Fl T
+Enable ad-hoc workaround for the TELNET protocol to
+remove unnecessary byte sequences.
+It should be set when you access
+.Nm
+using
+.Xr telnet 1 .
+.It Fl 1
+One-shot.
+Read available buffer, then exit.
+This implies the
+.Fl r
+option.
+.It Fl e Ar escape-char
+Specify escape character.
+The default is '~'.
+.It Fl h Ar hz
+Specify polling rate.
+The default value is 100.
+.It Fl C Ar console_port
+Specify the console port.
+The default value is 0 (stdin/stdout).
+.It Fl G Ar gdb_port
+Specify
+.Xr gdb 1
+port.
+The default value is \-1 (disabled).
+.It Fl M Ar core
+Specify core file.
+.It Fl N Ar system
+Specify system file such as
+.Pa /boot/kernel/kernel .
+.It Fl t Ar target_eui64
+Specify the 64-bit extended unique identifier of the target,
+and use FireWire to access remote
+.Xr dcons 4 .
+.It Fl a Ar address
+Specify the physical I/O address of the
+.Xr dcons 4
+buffer.
+See
+.Xr dcons 4
+for details.
+If this option is not specified,
+.Nm
+tries to get the address from the Configuration ROM on the target.
+You are supposed to enable
+.Xr dcons_crom 4
+on the target to omit this option.
+.It Fl u Ar bus_num
+Specify FireWire bus number.
+The default is 0.
+.El
+.Sh FILES
+.Bl -tag -width indent -compact
+.It Pa /dev/fwmem0.0
+.It Pa /dev/mem
+.It Pa /dev/kmem
+.El
+.Sh EXAMPLES
+To use
+.Nm
+with FireWire for remote
+.Xr dcons 4 ,
+you have to specify the EUI64 of the target.
+You can obtain EUI64 by running
+.Xr fwcontrol 8
+without options.
+The first EUI64 is of the host running
+.Xr fwcontrol 8
+and others on the bus follow.
+.Bd -literal -offset indent
+# fwcontrol
+2 devices (info_len=2)
+node EUI64 status
+ 1 77-66-55-44-33-22-11-00 0
+ 0 00-11-22-33-44-55-66-77 1
+.Ed
+.Pp
+The EUI64 does not change unless you change the hardware
+as the ethernet address.
+.Pp
+Now we can run
+.Nm .
+.Bd -literal -offset indent
+# dconschat -br -G 12345 -t 00-11-22-33-44-55-66-77
+.Ed
+.Pp
+You will get console output of the target and login prompt if a
+.Xr getty 8
+is running on
+.Xr dcons 4 .
+You can break to DDB with ALT_BREAK (CR +
+.Ql ~
++ Ctrl-B)
+if
+.Dv DDB
+and
+.Dv ALT_BREAK_TO_DEBUGGER
+are enabled in the target kernel.
+To quit the session, type CR +
+.Ql ~
++
+.Ql \&.
+in the console port.
+.Pp
+Using
+.Xr gdb 1
+port is almost the same as remote
+.Xr gdb 1
+over serial line except
+using TCP/IP instead of
+.Pa /dev/cu* .
+See
+.Sx "On-line Kernel Debugging Using Remote GDB"
+section of
+.%T "The FreeBSD Developers Handbook"
+and
+.Xr gdb 4
+for details.
+.Bd -literal -offset indent
+% gdb -k kernel.debug
+(kgdb) target remote :12345
+.Ed
+.Pp
+Once
+.Xr gdb 1
+is attached and you specified the
+.Fl b
+option to
+.Nm ,
+typing Ctrl-C in
+.Xr gdb 1
+causes a break to debugger.
+.Pp
+The following command gets the console log from the crash dump:
+.Bd -literal -offset indent
+# dconschat -1 -M vmcore.0 -N kernel.0
+.Ed
+.Pp
+If you want access to the console using
+.Xr telnet 1 ,
+try the following:
+.Bd -literal -offset indent
+# dconschat -rTC 5555 &
+# telnet localhost 5555
+.Ed
+.Pp
+You may want to keep logging console output of several machines.
+.Nm conserver-com
+in the Ports collection may help you.
+Insert the following lines in
+.Pa conserver.cf :
+.Bd -literal -offset indent
+console local {
+ master localhost;
+ type exec;
+ exec /usr/sbin/dconschat -rh 25;
+}
+console remote {
+ master localhost;
+ type exec;
+ exec /usr/sbin/dconschat -rh 25 -t 00-11-22-33-44-55-66-77;
+}
+.Ed
+.Sh SEE ALSO
+.Xr gdb 1 ,
+.Xr telnet 1 ,
+.Xr kvm 3 ,
+.Xr dcons 4 ,
+.Xr dcons_crom 4 ,
+.Xr ddb 4 ,
+.Xr firewire 4 ,
+.Xr fwohci 4 ,
+.Xr gdb 4 ,
+.Xr eui64 5 ,
+.Xr fwcontrol 8
+.Sh AUTHORS
+.An Hidetoshi Shimokawa Aq simokawa@FreeBSD.org
+.Sh BUGS
+This utility is
+.Ud .
diff --git a/usr.sbin/dconschat/dconschat.c b/usr.sbin/dconschat/dconschat.c
new file mode 100644
index 0000000..4334cf8
--- /dev/null
+++ b/usr.sbin/dconschat/dconschat.c
@@ -0,0 +1,1152 @@
+/*
+ * Copyright (C) 2003
+ * Hidetoshi Shimokawa. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Hidetoshi Shimokawa.
+ *
+ * 4. Neither the name of the author 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.
+ *
+ * $Id: dconschat.c,v 1.76 2003/10/23 06:21:13 simokawa Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <dev/dcons/dcons.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <err.h>
+#include <string.h>
+#include <sys/eui64.h>
+#include <sys/event.h>
+#include <sys/time.h>
+#include <arpa/telnet.h>
+
+#include <sys/ioccom.h>
+#include <dev/firewire/firewire.h>
+#include <dev/firewire/iec13213.h>
+
+#include <kvm.h>
+#include <nlist.h>
+
+#include <sys/errno.h>
+
+#define DCONS_POLL_HZ 100
+#define DCONS_POLL_OFFLINE 2 /* sec */
+
+#define RETRY 3
+
+#ifdef CSRVAL_VENDOR_PRIVATE
+#define USE_CROM 1
+#else
+#define USE_CROM 0
+#endif
+
+int verbose = 0;
+int tc_set = 0;
+int poll_hz = DCONS_POLL_HZ;
+static u_char abreak[3] = {13 /* CR */, 126 /* ~ */, 2 /* ^B */};
+
+#define IS_CONSOLE(p) ((p)->port == DCONS_CON)
+#define IS_GDB(p) ((p)->port == DCONS_GDB)
+
+static struct dcons_state {
+ int fd;
+ kvm_t *kd;
+ int kq;
+ off_t paddr;
+ off_t reset;
+#define F_READY (1 << 1)
+#define F_RD_ONLY (1 << 2)
+#define F_ALT_BREAK (1 << 3)
+#define F_TELNET (1 << 4)
+#define F_USE_CROM (1 << 5)
+#define F_ONE_SHOT (1 << 6)
+#define F_REPLAY (1 << 7)
+ int flags;
+ enum {
+ TYPE_KVM,
+ TYPE_FW
+ } type;
+ int escape_state;
+ struct dcons_port {
+ int port;
+ int sport;
+ struct dcons_ch o;
+ struct dcons_ch i;
+ u_int32_t optr;
+ u_int32_t iptr;
+ int s;
+ int infd;
+ int outfd;
+ struct addrinfo *res;
+ int skip_read;
+ } port[DCONS_NPORT];
+ struct timespec to;
+ struct timespec zero;
+ struct termios tsave;
+ struct termios traw;
+ char escape;
+} sc;
+
+static int dconschat_write_dcons(struct dcons_state *, int, char *, int);
+
+static int
+dread(struct dcons_state *dc, void *buf, size_t n, off_t offset)
+{
+ switch (dc->type) {
+ case TYPE_FW:
+ return (pread(dc->fd, buf, n, offset));
+ case TYPE_KVM:
+ return (kvm_read(dc->kd, offset, buf, n));
+ }
+ return (-1);
+}
+
+static int
+dwrite(struct dcons_state *dc, void *buf, size_t n, off_t offset)
+{
+ if ((dc->flags & F_RD_ONLY) != 0)
+ return (n);
+
+ switch (dc->type) {
+ case TYPE_FW:
+ return (pwrite(dc->fd, buf, n, offset));
+ case TYPE_KVM:
+ return (kvm_write(dc->kd, offset, buf, n));
+ }
+ return (-1);
+}
+
+static void
+dconschat_reset_target(struct dcons_state *dc, struct dcons_port *p)
+{
+ char buf[PAGE_SIZE];
+ if (dc->reset == 0)
+ return;
+
+ snprintf(buf, PAGE_SIZE, "\r\n[dconschat reset target(addr=0x%zx)...]\r\n", dc->reset);
+ write(p->outfd, buf, strlen(buf));
+ bzero(&buf[0], PAGE_SIZE);
+ dwrite(dc, (void *)buf, PAGE_SIZE, dc->reset);
+}
+
+
+static void
+dconschat_suspend(struct dcons_state *dc, struct dcons_port *p)
+{
+ if (p->sport != 0)
+ return;
+
+ if (tc_set)
+ tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->tsave);
+
+ printf("\n[dconschat suspend]\n");
+ kill(getpid(), SIGTSTP);
+
+ if (tc_set)
+ tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->traw);
+}
+
+static void
+dconschat_sigchld(int s)
+{
+ struct kevent kev;
+ struct dcons_port *p;
+ char buf[256];
+
+ p = &sc.port[DCONS_CON];
+
+ snprintf(buf, 256, "\r\n[child exit]\r\n");
+ write(p->outfd, buf, strlen(buf));
+
+ if (tc_set)
+ tcsetattr(STDIN_FILENO, TCSADRAIN, &sc.traw);
+
+ EV_SET(&kev, p->infd, EVFILT_READ, EV_ADD, NOTE_LOWAT, 1, (void *)p);
+ kevent(sc.kq, &kev, 1, NULL, 0, &sc.zero);
+}
+
+static void
+dconschat_fork_gdb(struct dcons_state *dc, struct dcons_port *p)
+{
+ pid_t pid;
+ char buf[256], com[256];
+ struct kevent kev;
+
+ pid = fork();
+ if (pid < 0) {
+ snprintf(buf, 256, "\r\n[%s: fork failed]\r\n", __FUNCTION__);
+ write(p->outfd, buf, strlen(buf));
+ }
+
+
+ if (pid == 0) {
+ /* child */
+ if (tc_set)
+ tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->tsave);
+
+ snprintf(com, sizeof(buf), "kgdb -r :%d kernel",
+ dc->port[DCONS_GDB].sport);
+ snprintf(buf, 256, "\n[fork %s]\n", com);
+ write(p->outfd, buf, strlen(buf));
+
+ execl("/bin/sh", "/bin/sh", "-c", com, 0);
+
+ snprintf(buf, 256, "\n[fork failed]\n");
+ write(p->outfd, buf, strlen(buf));
+
+ if (tc_set)
+ tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->traw);
+
+ exit(0);
+ } else {
+ signal(SIGCHLD, dconschat_sigchld);
+ EV_SET(&kev, p->infd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
+ kevent(sc.kq, &kev, 1, NULL, 0, &sc.zero);
+ }
+}
+
+
+static void
+dconschat_cleanup(int sig)
+{
+ struct dcons_state *dc;
+ int status;
+
+ dc = &sc;
+ if (tc_set != 0)
+ tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->tsave);
+
+ if (sig > 0)
+ printf("\n[dconschat exiting with signal %d ...]\n", sig);
+ else
+ printf("\n[dconschat exiting...]\n");
+ wait(&status);
+ exit(0);
+}
+
+#if USE_CROM
+static int
+dconschat_get_crom(struct dcons_state *dc)
+{
+ off_t addr;
+ int i, state = 0;
+ u_int32_t buf, hi = 0, lo = 0, reset_hi = 0, reset_lo = 0;
+ struct csrreg *reg;
+
+ reg = (struct csrreg *)&buf;
+ addr = 0xffff;
+ addr = (addr << 32) | 0xf0000400;
+ for (i = 20; i < 0x400; i += 4) {
+ if (dread(dc, &buf, 4, addr + i) < 0) {
+ if (verbose)
+ warn("crom read faild");
+ goto out;
+ }
+ buf = ntohl(buf);
+ if (verbose)
+ printf("%d %02x %06x\n", state, reg->key, reg->val);
+ switch (state) {
+ case 0:
+ if (reg->key == CSRKEY_SPEC &&
+ reg->val == CSRVAL_VENDOR_PRIVATE)
+ state = 1;
+ break;
+ case 1:
+ if (reg->key == CSRKEY_VER &&
+ reg->val == DCONS_CSR_VAL_VER)
+ state = 2;
+ break;
+ case 2:
+ switch (reg->key) {
+ case DCONS_CSR_KEY_HI:
+ hi = reg->val;
+ break;
+ case DCONS_CSR_KEY_LO:
+ lo = reg->val;
+ break;
+ case DCONS_CSR_KEY_RESET_HI:
+ reset_hi = reg->val;
+ break;
+ case DCONS_CSR_KEY_RESET_LO:
+ reset_lo = reg->val;
+ goto out;
+ break;
+ case 0x81:
+ break;
+ default:
+ state = 0;
+ }
+ break;
+ }
+ }
+out:
+ if (verbose)
+ printf("addr: %06x %06x\n", hi, lo);
+ dc->paddr = ((off_t)hi << 24) | lo;
+ dc->reset = ((off_t)reset_hi << 24) | reset_lo;
+ if (dc->paddr == 0)
+ return (-1);
+ return (0);
+}
+#endif
+
+static void
+dconschat_ready(struct dcons_state *dc, int ready, char *reason)
+{
+ static char oldreason[64] = "";
+ int old;
+
+ old = (dc->flags & F_READY) ? 1 : 0;
+
+ if (ready) {
+ dc->flags |= F_READY;
+ if (ready != old)
+ printf("[dcons connected]\r\n");
+ oldreason[0] = 0;
+ } else {
+ dc->flags &= ~F_READY;
+ if (strncmp(oldreason, reason, sizeof(oldreason)) != 0) {
+ printf("[dcons disconnected (%s)]\r\n", reason);
+ strlcpy(oldreason, reason, sizeof(oldreason));
+ }
+ }
+}
+
+static int
+dconschat_fetch_header(struct dcons_state *dc)
+{
+ char ebuf[64];
+ struct dcons_buf dbuf;
+ int j;
+
+#if USE_CROM
+ if (dc->paddr == 0 && (dc->flags & F_USE_CROM) != 0) {
+ if (dconschat_get_crom(dc)) {
+ dconschat_ready(dc, 0, "get crom failed");
+ return (-1);
+ }
+ }
+#endif
+
+ if (dread(dc, &dbuf, DCONS_HEADER_SIZE, dc->paddr) < 0) {
+ dconschat_ready(dc, 0, "read header failed");
+ return (-1);
+ }
+ if (dbuf.magic != htonl(DCONS_MAGIC)) {
+ if ((dc->flags & F_USE_CROM) !=0)
+ dc->paddr = 0;
+ snprintf(ebuf, sizeof(ebuf), "wrong magic 0x%08x", dbuf.magic);
+ dconschat_ready(dc, 0, ebuf);
+ return (-1);
+ }
+ if (ntohl(dbuf.version) != DCONS_VERSION) {
+ snprintf(ebuf, sizeof(ebuf),
+#if __FreeBSD_version < 500000
+ "wrong version %ld,%d",
+#else
+ "wrong version %d,%d",
+#endif
+ ntohl(dbuf.version), DCONS_VERSION);
+ /* XXX exit? */
+ dconschat_ready(dc, 0, ebuf);
+ return (-1);
+ }
+
+ for (j = 0; j < DCONS_NPORT; j++) {
+ struct dcons_ch *o, *i;
+ off_t newbuf;
+ int new = 0;
+
+ o = &dc->port[j].o;
+ newbuf = dc->paddr + ntohl(dbuf.ooffset[j]);
+ o->size = ntohl(dbuf.osize[j]);
+
+ if (newbuf != o->buf) {
+ /* buffer address has changes */
+ new = 1;
+ o->gen = ntohl(dbuf.optr[j]) >> DCONS_GEN_SHIFT;
+ o->pos = ntohl(dbuf.optr[j]) & DCONS_POS_MASK;
+ o->buf = newbuf;
+ }
+
+ i = &dc->port[j].i;
+ i->size = ntohl(dbuf.isize[j]);
+ i->gen = ntohl(dbuf.iptr[j]) >> DCONS_GEN_SHIFT;
+ i->pos = ntohl(dbuf.iptr[j]) & DCONS_POS_MASK;
+ i->buf = dc->paddr + ntohl(dbuf.ioffset[j]);
+
+ if (verbose) {
+ printf("port %d size offset gen pos\n", j);
+#if __FreeBSD_version < 500000
+ printf("output: %5d %6ld %5d %5d\n"
+ "input : %5d %6ld %5d %5d\n",
+#else
+ printf("output: %5d %6d %5d %5d\n"
+ "input : %5d %6d %5d %5d\n",
+#endif
+ o->size, ntohl(dbuf.ooffset[j]), o->gen, o->pos,
+ i->size, ntohl(dbuf.ioffset[j]), i->gen, i->pos);
+ }
+
+ if (IS_CONSOLE(&dc->port[j]) && new &&
+ (dc->flags & F_REPLAY) !=0) {
+ if (o->gen > 0)
+ o->gen --;
+ else
+ o->pos = 0;
+ }
+ }
+ dconschat_ready(dc, 1, NULL);
+ return(0);
+}
+
+static int
+dconschat_get_ptr (struct dcons_state *dc) {
+ int dlen, i;
+ u_int32_t ptr[DCONS_NPORT*2+1];
+ static int retry = RETRY;
+ char ebuf[64];
+
+again:
+ dlen = dread(dc, &ptr, sizeof(ptr),
+ dc->paddr + __offsetof(struct dcons_buf, magic));
+
+ if (dlen < 0) {
+ if (errno == ETIMEDOUT)
+ if (retry -- > 0)
+ goto again;
+ dconschat_ready(dc, 0, "get ptr failed");
+ return(-1);
+ }
+ if (ptr[0] != htonl(DCONS_MAGIC)) {
+ if ((dc->flags & F_USE_CROM) !=0)
+ dc->paddr = 0;
+ snprintf(ebuf, sizeof(ebuf), "wrong magic 0x%08x", ptr[0]);
+ dconschat_ready(dc, 0, ebuf);
+ return(-1);
+ }
+ retry = RETRY;
+ for (i = 0; i < DCONS_NPORT; i ++) {
+ dc->port[i].optr = ntohl(ptr[i + 1]);
+ dc->port[i].iptr = ntohl(ptr[DCONS_NPORT + i + 1]);
+ }
+ return(0);
+}
+
+#define MAX_XFER 2048
+static int
+dconschat_read_dcons(struct dcons_state *dc, int port, char *buf, int len)
+{
+ struct dcons_ch *ch;
+ u_int32_t ptr, pos, gen, next_gen;
+ int rlen, dlen, lost;
+ int retry = RETRY;
+
+ ch = &dc->port[port].o;
+ ptr = dc->port[port].optr;
+ gen = ptr >> DCONS_GEN_SHIFT;
+ pos = ptr & DCONS_POS_MASK;
+ if (gen == ch->gen && pos == ch->pos)
+ return (-1);
+
+ next_gen = DCONS_NEXT_GEN(ch->gen);
+ /* XXX sanity check */
+ if (gen == ch->gen) {
+ if (pos > ch->pos)
+ goto ok;
+ lost = ch->size * DCONS_GEN_MASK - ch->pos;
+ ch->pos = 0;
+ } else if (gen == next_gen) {
+ if (pos <= ch->pos)
+ goto ok;
+ lost = pos - ch->pos;
+ ch->pos = pos;
+ } else {
+ lost = gen - ch->gen;
+ if (lost < 0)
+ lost += DCONS_GEN_MASK;
+ if (verbose)
+ printf("[genskip %d]", lost);
+ lost = lost * ch->size - ch->pos;
+ ch->pos = 0;
+ ch->gen = gen;
+ }
+ /* generation skipped !! */
+ /* XXX discard */
+ if (verbose)
+ printf("[lost %d]", lost);
+ok:
+ if (gen == ch->gen)
+ rlen = pos - ch->pos;
+ else
+ rlen = ch->size - ch->pos;
+
+ if (rlen > MAX_XFER)
+ rlen = MAX_XFER;
+ if (rlen > len)
+ rlen = len;
+
+#if 1
+ if (verbose == 1)
+ printf("[%d]", rlen); fflush(stdout);
+#endif
+
+again:
+ dlen = dread(dc, buf, rlen, ch->buf + ch->pos);
+ if (dlen < 0) {
+ if (errno == ETIMEDOUT)
+ if (retry -- > 0)
+ goto again;
+ dconschat_ready(dc, 0, "read buffer failed");
+ return(-1);
+ }
+ if (dlen != rlen)
+ warnx("dlen(%d) != rlen(%d)\n", dlen, rlen);
+ ch->pos += dlen;
+ if (ch->pos >= ch->size) {
+ ch->gen = next_gen;
+ ch->pos = 0;
+ if (verbose)
+ printf("read_dcons: gen=%d", ch->gen);
+ }
+ return (dlen);
+}
+
+static int
+dconschat_write_dcons(struct dcons_state *dc, int port, char *buf, int blen)
+{
+ struct dcons_ch *ch;
+ u_int32_t ptr;
+ int len, wlen;
+ int retry = RETRY;
+
+ ch = &dc->port[port].i;
+ ptr = dc->port[port].iptr;
+
+ /* the others may advance the pointer sync with it */
+ ch->gen = ptr >> DCONS_GEN_SHIFT;
+ ch->pos = ptr & DCONS_POS_MASK;
+
+ while(blen > 0) {
+ wlen = MIN(blen, ch->size - ch->pos);
+ wlen = MIN(wlen, MAX_XFER);
+ len = dwrite(dc, buf, wlen, ch->buf + ch->pos);
+ if (len < 0) {
+ if (errno == ETIMEDOUT)
+ if (retry -- > 0)
+ continue; /* try again */
+ dconschat_ready(dc, 0, "write buffer failed");
+ return(-1);
+ }
+ ch->pos += len;
+ buf += len;
+ blen -= len;
+ if (ch->pos >= ch->size) {
+ ch->gen = DCONS_NEXT_GEN(ch->gen);
+ ch->pos = 0;
+ if (verbose)
+ printf("write_dcons: gen=%d", ch->gen);
+
+ }
+ }
+
+ ptr = DCONS_MAKE_PTR(ch);
+ dc->port[port].iptr = ptr;
+
+ if (verbose > 2)
+ printf("(iptr: 0x%x)", ptr);
+again:
+ len = dwrite(dc, &ptr, sizeof(u_int32_t),
+ dc->paddr + __offsetof(struct dcons_buf, iptr[port]));
+ if (len < 0) {
+ if (errno == ETIMEDOUT)
+ if (retry -- > 0)
+ goto again;
+ dconschat_ready(dc, 0, "write ptr failed");
+ return(-1);
+ }
+ return(0);
+}
+
+
+static int
+dconschat_write_socket(int fd, char *buf, int len)
+{
+ write(fd, buf, len);
+ if (verbose > 1) {
+ buf[len] = 0;
+ printf("<- %s\n", buf);
+ }
+ return (0);
+}
+
+static void
+dconschat_init_socket(struct dcons_state *dc, int port, char *host, int sport)
+{
+ struct addrinfo hints, *res;
+ int on = 1, error;
+ char service[10];
+ struct kevent kev;
+ struct dcons_port *p;
+
+ p = &dc->port[port];
+ p->port = port;
+ p->sport = sport;
+ p->infd = p->outfd = -1;
+
+ if (sport < 0)
+ return;
+
+ if (sport == 0) {
+
+ /* Use stdin and stdout */
+ p->infd = STDIN_FILENO;
+ p->outfd = STDOUT_FILENO;
+ p->s = -1;
+ if (tc_set == 0 &&
+ tcgetattr(STDIN_FILENO, &dc->tsave) == 0) {
+ dc->traw = dc->tsave;
+ cfmakeraw(&dc->traw);
+ tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->traw);
+ tc_set = 1;
+ }
+ EV_SET(&kev, p->infd, EVFILT_READ, EV_ADD, NOTE_LOWAT, 1,
+ (void *)p);
+ kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
+ return;
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_PASSIVE;
+#if 1 /* gdb can talk v4 only */
+ hints.ai_family = PF_INET;
+#else
+ hints.ai_family = PF_UNSPEC;
+#endif
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = 0;
+
+ if (verbose)
+ printf("%s:%d for port %d\n",
+ host == NULL ? "*" : host, sport, port);
+ snprintf(service, sizeof(service), "%d", sport);
+ error = getaddrinfo(host, service, &hints, &res);
+ if (error)
+ errx(1, "tcp/%s: %s\n", service, gai_strerror(error));
+ p->res = res;
+ p->s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (p->s < 0)
+ err(1, "socket");
+ setsockopt(p->s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+ if (bind(p->s, p->res->ai_addr, p->res->ai_addrlen) < 0) {
+ err(1, "bind");
+ }
+ if (listen(p->s, 1) < 0)
+ err(1, "listen");
+ EV_SET(&kev, p->s, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, (void *)p);
+ error = kevent(dc->kq, &kev, 1, NULL, 0, &dc->to);
+ if (error < 0)
+ err(1, "kevent");
+ return;
+}
+
+static int
+dconschat_accept_socket(struct dcons_state *dc, struct dcons_port *p)
+{
+ socklen_t addrlen;
+ int ns, flags;
+ struct kevent kev;
+
+ /* accept connection */
+ addrlen = p->res->ai_addrlen;
+ ns = accept(p->s, p->res->ai_addr, &addrlen);
+ if (ns < 0)
+ err(1, "accept");
+ if (verbose)
+ printf("port%d accepted\n", p->port);
+
+ flags = fcntl(ns, F_GETFL, 0);
+ flags |= O_NDELAY;
+ fcntl(ns, F_SETFL, flags);
+#if 1
+ if (IS_CONSOLE(p) && (dc->flags & F_TELNET) != 0) {
+ char sga[] = {IAC, WILL, TELOPT_SGA};
+ char linemode[] = {IAC, DONT, TELOPT_LINEMODE};
+ char echo[] = {IAC, WILL, TELOPT_ECHO};
+ char bin[] = {IAC, DO, TELOPT_BINARY};
+
+ write(ns, sga, sizeof(sga));
+ write(ns, linemode, sizeof(linemode));
+ write(ns, echo, sizeof(echo));
+ write(ns, bin, sizeof(bin));
+ p->skip_read = 0;
+ }
+#endif
+ /* discard backlog on GDB port */
+ if (IS_GDB(p)) {
+ char buf[2048];
+ int len;
+
+ while ((len = dconschat_read_dcons(dc, DCONS_GDB, &buf[0],
+ 2048)) > 0)
+ if (verbose)
+ printf("discard %d chars on GDB port\n", len);
+ }
+
+ p->infd = p->outfd = ns;
+ EV_SET(&kev, ns, EVFILT_READ, EV_ADD, NOTE_LOWAT, 1, (void *)p);
+ kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
+ return(0);
+}
+
+static int
+dconschat_read_filter(struct dcons_state *dc, struct dcons_port *p,
+ u_char *sp, int slen, u_char *dp, int *dlen)
+{
+ int skip;
+ char *buf;
+
+ while (slen > 0) {
+ skip = 0;
+ if (IS_CONSOLE(p)) {
+ if ((dc->flags & F_TELNET) != 0) {
+ /* XXX Telnet workarounds */
+ if (p->skip_read -- > 0) {
+ sp ++;
+ slen --;
+ continue;
+ }
+ if (*sp == IAC) {
+ if (verbose)
+ printf("(IAC)");
+ p->skip_read = 2;
+ sp ++;
+ slen --;
+ continue;
+ }
+ if (*sp == 0) {
+ if (verbose)
+ printf("(0 stripped)");
+ sp ++;
+ slen --;
+ continue;
+ }
+ }
+ switch (dc->escape_state) {
+ case STATE1:
+ if (*sp == dc->escape) {
+ skip = 1;
+ dc->escape_state = STATE2;
+ } else
+ dc->escape_state = STATE0;
+ break;
+ case STATE2:
+ dc->escape_state = STATE0;
+ skip = 1;
+ if (*sp == '.')
+ dconschat_cleanup(0);
+ else if (*sp == CTRL('B')) {
+ bcopy(abreak, dp, 3);
+ dp += 3;
+ *dlen += 3;
+ }
+ else if (*sp == CTRL('G'))
+ dconschat_fork_gdb(dc, p);
+ else if ((*sp == CTRL('R'))
+ && (dc->reset != 0)) {
+ dc->escape_state = STATE3;
+ buf = "\r\n[Are you sure to reset target? (y/N)]";
+ write(p->outfd, buf, strlen(buf));
+ } else if (*sp == CTRL('Z'))
+ dconschat_suspend(dc, p);
+ else {
+ skip = 0;
+ *dp++ = dc->escape;
+ (*dlen) ++;
+ }
+ break;
+ case STATE3:
+ dc->escape_state = STATE0;
+ skip = 1;
+ if (*sp == 'y')
+ dconschat_reset_target(dc, p);
+ else {
+ write(p->outfd, sp, 1);
+ write(p->outfd, "\r\n", 2);
+ }
+ break;
+ }
+ if (*sp == KEY_CR)
+ dc->escape_state = STATE1;
+ } else if (IS_GDB(p)) {
+ /* GDB: ^C -> CR+~+^B */
+ if (*sp == CTRL('C') && (dc->flags & F_ALT_BREAK) != 0) {
+ bcopy(abreak, dp, 3);
+ dp += 3;
+ sp ++;
+ *dlen += 3;
+ /* discard rest of the packet */
+ slen = 0;
+ break;
+ }
+ }
+ if (!skip) {
+ *dp++ = *sp;
+ (*dlen) ++;
+ }
+ sp ++;
+ slen --;
+ }
+ return (*dlen);
+
+}
+
+static int
+dconschat_read_socket(struct dcons_state *dc, struct dcons_port *p)
+{
+ struct kevent kev;
+ int len, wlen;
+ char rbuf[MAX_XFER], wbuf[MAX_XFER+2];
+
+ if ((len = read(p->infd, rbuf, sizeof(rbuf))) > 0) {
+ wlen = 0;
+ dconschat_read_filter(dc, p, rbuf, len, wbuf, &wlen);
+ /* XXX discard if not ready*/
+ if (wlen > 0 && (dc->flags & F_READY) != 0) {
+ dconschat_write_dcons(dc, p->port, wbuf, wlen);
+ if (verbose > 1) {
+ wbuf[wlen] = 0;
+ printf("-> %s\n", wbuf);
+ } else if (verbose == 1) {
+ printf("(%d)", wlen);
+ fflush(stdout);
+ }
+ }
+ } else {
+ if (verbose) {
+ if (len == 0)
+ warnx("port%d: closed", p->port);
+ else
+ warn("port%d: read", p->port);
+ }
+ EV_SET(&kev, p->infd, EVFILT_READ,
+ EV_DELETE, 0, 0, NULL);
+ kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
+ close(p->infd);
+ close(p->outfd);
+ /* XXX exit for pipe case XXX */
+ EV_SET(&kev, p->s, EVFILT_READ,
+ EV_ADD | EV_ONESHOT, 0, 0, (void *) p);
+ kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
+ p->infd = p->outfd = -1;
+ }
+ return(0);
+}
+#define NEVENT 5
+static int
+dconschat_proc_socket(struct dcons_state *dc)
+{
+ struct kevent elist[NEVENT], *e;
+ int i, n;
+ struct dcons_port *p;
+
+ n = kevent(dc->kq, NULL, 0, elist, NEVENT, &dc->to);
+ for (i = 0; i < n; i ++) {
+ e = &elist[i];
+ p = (struct dcons_port *)e->udata;
+ if (e->ident == p->s) {
+ dconschat_accept_socket(dc, p);
+ } else {
+ dconschat_read_socket(dc, p);
+ }
+ }
+ return(0);
+}
+
+static int
+dconschat_proc_dcons(struct dcons_state *dc)
+{
+ int port, len, err;
+ char buf[MAX_XFER];
+ struct dcons_port *p;
+
+ err = dconschat_get_ptr(dc);
+ if (err) {
+ /* XXX we should stop write operation too. */
+ return err;
+ }
+ for (port = 0; port < DCONS_NPORT; port ++) {
+ p = &dc->port[port];
+ if (p->infd < 0)
+ continue;
+ while ((len = dconschat_read_dcons(dc, port, buf,
+ sizeof(buf))) > 0) {
+ dconschat_write_socket(p->outfd, buf, len);
+ if ((err = dconschat_get_ptr(dc)))
+ return (err);
+ }
+ if ((dc->flags & F_ONE_SHOT) != 0 && len <= 0)
+ dconschat_cleanup(0);
+ }
+ return 0;
+}
+
+static int
+dconschat_start_session(struct dcons_state *dc)
+{
+ int counter = 0;
+ int retry = 0;
+ int retry_unit_init = MAX(1, poll_hz / 10);
+ int retry_unit_offline = poll_hz * DCONS_POLL_OFFLINE;
+ int retry_unit = retry_unit_init;
+ int retry_max = retry_unit_offline / retry_unit;
+
+ while (1) {
+ if (((dc->flags & F_READY) == 0) && ++counter > retry_unit) {
+ counter = 0;
+ retry ++;
+ if (retry > retry_max)
+ retry_unit = retry_unit_offline;
+ if (verbose) {
+ printf("%d/%d ", retry, retry_max);
+ fflush(stdout);
+ }
+ dconschat_fetch_header(dc);
+ }
+ if ((dc->flags & F_READY) != 0) {
+ counter = 0;
+ retry = 0;
+ retry_unit = retry_unit_init;
+ dconschat_proc_dcons(dc);
+ }
+ dconschat_proc_socket(dc);
+ }
+ return (0);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: dconschat [-brvwRT1] [-h hz] [-C port] [-G port]\n"
+ "\t\t\t[-M core] [-N system]\n"
+ "\t\t\t[-u unit] [-a address] [-t target_eui64]\n"
+ "\t-b translate ctrl-C to CR+~+ctrl-B on gdb port\n"
+ "\t-v verbose\n"
+ "\t-w listen on wildcard address rather than localhost\n"
+ "\t-r replay old buffer on connection\n"
+ "\t-R read-only\n"
+ "\t-T enable Telnet protocol workaround on console port\n"
+ "\t-1 one shot: read buffer and exit\n"
+ "\t-h polling rate\n"
+ "\t-C port number for console port\n"
+ "\t-G port number for gdb port\n"
+ "\t(for KVM)\n"
+ "\t-M core file\n"
+ "\t-N system file\n"
+ "\t(for FireWire)\n"
+ "\t-u specify unit number of the bus\n"
+ "\t-t EUI64 of target host (must be specified)\n"
+ "\t-a physical address of dcons buffer on target host\n"
+ );
+ exit(0);
+}
+int
+main(int argc, char **argv)
+{
+ struct dcons_state *dc;
+ struct fw_eui64 eui;
+ struct eui64 target;
+ char devname[256], *core = NULL, *system = NULL;
+ int i, ch, error;
+ int unit=0, wildcard=0;
+ int port[DCONS_NPORT];
+
+ bzero(&sc, sizeof(sc));
+ dc = &sc;
+ dc->flags |= USE_CROM ? F_USE_CROM : 0;
+
+ /* default ports */
+ port[0] = 0; /* stdin/out for console */
+ port[1] = -1; /* disable gdb port */
+
+ /* default escape char */
+ dc->escape = KEY_TILDE;
+
+ while ((ch = getopt(argc, argv, "a:be:h:rt:u:vwC:G:M:N:RT1")) != -1) {
+ switch(ch) {
+ case 'a':
+ dc->paddr = strtoull(optarg, NULL, 0);
+ dc->flags &= ~F_USE_CROM;
+ break;
+ case 'b':
+ dc->flags |= F_ALT_BREAK;
+ break;
+ case 'e':
+ dc->escape = optarg[0];
+ break;
+ case 'h':
+ poll_hz = strtoul(optarg, NULL, 0);
+ if (poll_hz == 0)
+ poll_hz = DCONS_POLL_HZ;
+ break;
+ case 'r':
+ dc->flags |= F_REPLAY;
+ break;
+ case 't':
+ if (eui64_hostton(optarg, &target) != 0 &&
+ eui64_aton(optarg, &target) != 0)
+ errx(1, "invalid target: %s", optarg);
+ eui.hi = ntohl(*(u_int32_t*)&(target.octet[0]));
+ eui.lo = ntohl(*(u_int32_t*)&(target.octet[4]));
+ dc->type = TYPE_FW;
+ break;
+ case 'u':
+ unit = strtol(optarg, NULL, 0);
+ break;
+ case 'v':
+ verbose ++;
+ break;
+ case 'w':
+ wildcard = 1;
+ break;
+ case 'C':
+ port[0] = strtol(optarg, NULL, 0);
+ break;
+ case 'G':
+ port[1] = strtol(optarg, NULL, 0);
+ break;
+ case 'M':
+ core = optarg;
+ break;
+ case 'N':
+ system = optarg;
+ break;
+ case 'R':
+ dc->flags |= F_RD_ONLY;
+ break;
+ case 'T':
+ dc->flags |= F_TELNET;
+ break;
+ case '1':
+ dc->flags |= F_ONE_SHOT | F_REPLAY;
+ break;
+ default:
+ usage();
+ }
+ }
+ if (dc->paddr == 0 && (dc->flags & F_USE_CROM) == 0) {
+ warnx("no address specified");
+ usage();
+ }
+
+ if (port[0] < 0 && port[1] < 0) {
+ warnx("no port specified");
+ usage();
+ }
+
+ /* set signal handler */
+ signal(SIGHUP, dconschat_cleanup);
+ signal(SIGINT, dconschat_cleanup);
+ signal(SIGPIPE, dconschat_cleanup);
+ signal(SIGTERM, dconschat_cleanup);
+
+ /* init firewire */
+ switch (dc->type) {
+ case TYPE_FW:
+#define MAXDEV 10
+ for (i = 0; i < MAXDEV; i ++) {
+ snprintf(devname, sizeof(devname),
+ "/dev/fwmem%d.%d", unit, i);
+ dc->fd = open(devname, O_RDWR);
+ if (dc->fd >= 0)
+ goto found;
+ }
+ err(1, "open");
+found:
+ error = ioctl(dc->fd, FW_SDEUI64, &eui);
+ if (error)
+ err(1, "ioctl");
+ break;
+ case TYPE_KVM:
+ {
+ struct nlist nl[] = {{"dcons_buf"}, {""}};
+ void *dcons_buf;
+
+ dc->kd = kvm_open(system, core, NULL,
+ (dc->flags & F_RD_ONLY) ? O_RDONLY : O_RDWR, "dconschat");
+ if (dc->kd == NULL)
+ errx(1, "kvm_open");
+
+ if (kvm_nlist(dc->kd, nl) < 0)
+ errx(1, "kvm_nlist: %s", kvm_geterr(dc->kd));
+
+ if (kvm_read(dc->kd, nl[0].n_value, &dcons_buf,
+ sizeof(void *)) < 0)
+ errx(1, "kvm_read: %s", kvm_geterr(dc->kd));
+ dc->paddr = (uintptr_t)dcons_buf;
+ if (verbose)
+ printf("dcons_buf: 0x%x\n", (uint)dc->paddr);
+ break;
+ }
+ }
+ dconschat_fetch_header(dc);
+
+ /* init sockets */
+ dc->kq = kqueue();
+ if (poll_hz == 1) {
+ dc->to.tv_sec = 1;
+ dc->to.tv_nsec = 0;
+ } else {
+ dc->to.tv_sec = 0;
+ dc->to.tv_nsec = 1000 * 1000 * 1000 / poll_hz;
+ }
+ dc->zero.tv_sec = 0;
+ dc->zero.tv_nsec = 0;
+ for (i = 0; i < DCONS_NPORT; i++)
+ dconschat_init_socket(dc, i,
+ wildcard ? NULL : "localhost", port[i]);
+
+ dconschat_start_session(dc);
+
+ for (i = 0; i < DCONS_NPORT; i++) {
+ freeaddrinfo(dc->port[i].res);
+ }
+ return (0);
+}
diff --git a/usr.sbin/devinfo/Makefile b/usr.sbin/devinfo/Makefile
new file mode 100644
index 0000000..52e4d6d
--- /dev/null
+++ b/usr.sbin/devinfo/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+PROG= devinfo
+MAN= devinfo.8
+
+WARNS?= 2
+
+DPADD= ${LIBDEVINFO}
+LDADD= -ldevinfo
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/devinfo/devinfo.8 b/usr.sbin/devinfo/devinfo.8
new file mode 100644
index 0000000..6c893dc
--- /dev/null
+++ b/usr.sbin/devinfo/devinfo.8
@@ -0,0 +1,76 @@
+.\" -*- nroff -*-
+.\"
+.\" Copyright (c) 2002 Hiten Pandya
+.\" Copyright (c) 2002 Robert N. M. Watson
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED 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 November 28, 2005
+.Os
+.Dt DEVINFO 8
+.Sh NAME
+.Nm devinfo
+.Nd print information about system device configuration
+.Sh SYNOPSIS
+.Nm
+.Op Fl rv
+.Nm
+.Fl u
+.Sh DESCRIPTION
+The
+.Nm
+utility, without any arguments, shows the hierarchy of devices available
+in the system, starting from the
+.Dq nexus
+device.
+.Pp
+The following options are accepted.
+.Bl -tag -width indent
+.It Fl r
+Causes hardware resource information (such as IRQ, I/O ports, I/O memory
+addresses) to be also listed, under each device that has reserved those resources.
+.It Fl u
+Displays the same information as with
+.Fl r
+but sorts by resource type rather than by device, allowing to review the
+set of system resources by usage and available resources.
+I.e., it lists all
+the IRQ consumers together.
+.It Fl v
+Display all devices in the driver tree, not just those that are attached or
+busy.
+Without this flag, only those devices that have attached are reported.
+.El
+.Sh SEE ALSO
+.Xr systat 1 ,
+.Xr devinfo 3 ,
+.Xr iostat 8 ,
+.Xr pciconf 8 ,
+.Xr pnpinfo 8 ,
+.Xr vmstat 8 ,
+.Xr devclass 9 ,
+.Xr device 9
+.Sh AUTHORS
+.An Mike Smith Aq msmith@FreeBSD.org
diff --git a/usr.sbin/devinfo/devinfo.c b/usr.sbin/devinfo/devinfo.c
new file mode 100644
index 0000000..c32e57b
--- /dev/null
+++ b/usr.sbin/devinfo/devinfo.c
@@ -0,0 +1,233 @@
+/*-
+ * Copyright (c) 2000, 2001 Michael Smith
+ * Copyright (c) 2000 BSDi
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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.
+ */
+
+/*
+ * Print information about system device configuration.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "devinfo.h"
+
+int rflag;
+int vflag;
+
+static void print_resource(struct devinfo_res *);
+static int print_device_matching_resource(struct devinfo_res *, void *);
+static int print_device_rman_resources(struct devinfo_rman *, void *);
+static int print_device(struct devinfo_dev *, void *);
+static int print_rman_resource(struct devinfo_res *, void *);
+static int print_rman(struct devinfo_rman *, void *);
+
+struct indent_arg
+{
+ int indent;
+ void *arg;
+};
+
+/*
+ * Print a resource.
+ */
+void
+print_resource(struct devinfo_res *res)
+{
+ struct devinfo_rman *rman;
+ int hexmode;
+
+ rman = devinfo_handle_to_rman(res->dr_rman);
+ hexmode = (rman->dm_size > 1000) || (rman->dm_size == 0);
+ printf(hexmode ? "0x%lx" : "%lu", res->dr_start);
+ if (res->dr_size > 1)
+ printf(hexmode ? "-0x%lx" : "-%lu",
+ res->dr_start + res->dr_size - 1);
+}
+
+/*
+ * Print resource information if this resource matches the
+ * given device.
+ *
+ * If the given indent is 0, return an indicator that a matching
+ * resource exists.
+ */
+int
+print_device_matching_resource(struct devinfo_res *res, void *arg)
+{
+ struct indent_arg *ia = (struct indent_arg *)arg;
+ struct devinfo_dev *dev = (struct devinfo_dev *)ia->arg;
+ int i;
+
+ if (devinfo_handle_to_device(res->dr_device) == dev) {
+ /* in 'detect' mode, found a match */
+ if (ia->indent == 0)
+ return(1);
+ for (i = 0; i < ia->indent; i++)
+ printf(" ");
+ print_resource(res);
+ printf("\n");
+ }
+ return(0);
+}
+
+/*
+ * Print resource information for this device and resource manager.
+ */
+int
+print_device_rman_resources(struct devinfo_rman *rman, void *arg)
+{
+ struct indent_arg *ia = (struct indent_arg *)arg;
+ int indent, i;
+
+ indent = ia->indent;
+
+ /* check whether there are any resources matching this device */
+ ia->indent = 0;
+ if (devinfo_foreach_rman_resource(rman,
+ print_device_matching_resource, ia) != 0) {
+
+ /* there are, print header */
+ for (i = 0; i < indent; i++)
+ printf(" ");
+ printf("%s:\n", rman->dm_desc);
+
+ /* print resources */
+ ia->indent = indent + 4;
+ devinfo_foreach_rman_resource(rman,
+ print_device_matching_resource, ia);
+ }
+ ia->indent = indent;
+ return(0);
+}
+
+/*
+ * Print information about a device.
+ */
+int
+print_device(struct devinfo_dev *dev, void *arg)
+{
+ struct indent_arg ia;
+ int i, indent;
+
+ if (vflag || (dev->dd_name[0] != 0 && dev->dd_state >= DIS_ATTACHED)) {
+ indent = (int)(intptr_t)arg;
+ for (i = 0; i < indent; i++)
+ printf(" ");
+ printf("%s", dev->dd_name[0] ? dev->dd_name : "unknown");
+ if (vflag && *dev->dd_pnpinfo)
+ printf(" pnpinfo %s", dev->dd_pnpinfo);
+ if (vflag && *dev->dd_location)
+ printf(" at %s", dev->dd_location);
+ printf("\n");
+ if (rflag) {
+ ia.indent = indent + 4;
+ ia.arg = dev;
+ devinfo_foreach_rman(print_device_rman_resources,
+ (void *)&ia);
+ }
+ }
+
+ return(devinfo_foreach_device_child(dev, print_device,
+ (void *)((char *)arg + 2)));
+}
+
+/*
+ * Print information about a resource under a resource manager.
+ */
+int
+print_rman_resource(struct devinfo_res *res, void *arg __unused)
+{
+ struct devinfo_dev *dev;
+
+ printf(" ");
+ print_resource(res);
+ dev = devinfo_handle_to_device(res->dr_device);
+ if ((dev != NULL) && (dev->dd_name[0] != 0)) {
+ printf(" (%s)", dev->dd_name);
+ } else {
+ printf(" ----");
+ }
+ printf("\n");
+ return(0);
+}
+
+/*
+ * Print information about a resource manager.
+ */
+int
+print_rman(struct devinfo_rman *rman, void *arg __unused)
+{
+ printf("%s:\n", rman->dm_desc);
+ devinfo_foreach_rman_resource(rman, print_rman_resource, 0);
+ return(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct devinfo_dev *root;
+ int c, uflag;
+
+ uflag = 0;
+ while ((c = getopt(argc, argv, "ruv")) != -1) {
+ switch(c) {
+ case 'r':
+ rflag++;
+ break;
+ case 'u':
+ uflag++;
+ break;
+ case 'v':
+ vflag++;
+ break;
+ default:
+ fprintf(stderr, "%s\n%s\n",
+ "usage: devinfo [-rv]",
+ " devinfo -u");
+ exit(1);
+ }
+ }
+
+ if (devinfo_init())
+ err(1, "devinfo_init");
+
+ if ((root = devinfo_handle_to_device(DEVINFO_ROOT_DEVICE)) == NULL)
+ errx(1, "can't find root device");
+
+ /* print resource usage? */
+ if (uflag) {
+ devinfo_foreach_rman(print_rman, NULL);
+ } else {
+ /* print device hierarchy */
+ devinfo_foreach_device_child(root, print_device, (void *)0);
+ }
+ return(0);
+}
diff --git a/usr.sbin/digictl/Makefile b/usr.sbin/digictl/Makefile
new file mode 100644
index 0000000..029dfee
--- /dev/null
+++ b/usr.sbin/digictl/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= digictl
+MAN= digictl.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/digictl/digictl.8 b/usr.sbin/digictl/digictl.8
new file mode 100644
index 0000000..2ac98ea
--- /dev/null
+++ b/usr.sbin/digictl/digictl.8
@@ -0,0 +1,118 @@
+.\" $FreeBSD$
+.Dd June 20, 2001
+.Dt DIGICTL 8
+.Os
+.Sh NAME
+.Nm digictl
+.Nd control
+.Tn Digiboard
+devices
+.Sh SYNOPSIS
+.Nm
+.Fl a
+.Cm disable | enable | query
+.Ar device ...
+.Nm
+.Op Fl d Ar debug
+.Op Fl ir
+.Ar ctrl-device ...
+.Sh DESCRIPTION
+The
+.Nm
+utility provides control of the
+.Tn Digiboard
+installed with the given
+.Ar ctrl-device
+name and provides control of individual digiboard
+.Ar devices .
+A digiboard
+.Ar ctrl-device
+is usually of the form
+.Sm off
+.Pa /dev/digi Ar N Pa .ctl
+.Sm on
+where
+.Ar N
+is the card number and starts at
+.Dq 0
+for the first attached card.
+A digiboard
+.Ar device
+is usually of the form
+.Sm off
+.Pa /dev/cua Oo Pa il Oc Pa D Ar N Pa \&. Ar P
+.Sm on
+or
+.Sm off
+.Pa /dev/tty Oo Pa il Oc Pa D Ar N Pa \&. Ar P
+.Sm on
+where
+.Ar N
+is the card number and
+.Ar P
+is the port number.
+.Pp
+The following flags are recognized:
+.Bl -tag
+.It Fl a Cm disable | enable | query
+Disable, enable or query the
+.Em ALTPIN
+settings for the given port(s).
+.Pp
+When ALTPIN is enabled, the CD and DSR lines are logically reversed.
+This is useful when wiring serial ports to an 8 way RJ45 cable (full
+10 way RJ45 cables are quite rare).
+.Pp
+A single ALTPIN setting applies to both of the callout and callin devices.
+.It Fl d Ar debug
+If the driver has been compiled with
+.Dv DEBUG
+defined, the following bits from the
+.Ar debug
+variable are used to enable diagnostics in the digiboard driver:
+.Bl -tag -width ".No 1024 ( Em MODEM )"
+.It 1 ( Em INIT )
+Diagnostics during card attach, detach and initialization.
+.It 2 ( Em OPEN )
+Diagnostics when opening a port.
+.It 4 ( Em CLOSE )
+Diagnostics when closing a port.
+.It 8 ( Em SET )
+Diagnostics when setting tty device flags.
+.It 16 ( Em INT )
+Diagnostics when processing card events.
+.It 32 ( Em READ )
+Reports return values from port reads.
+.It 64 ( Em WRITE )
+Reports return values from port writes.
+.It 128 ( Em RX )
+Reports receive queue flow control.
+.It 256 ( Em TX )
+Reports transmit queue flow control.
+.It 512 ( Em IRQ )
+Diagnostics during interrupts (enable these with care).
+.It 1024 ( Em MODEM )
+Diagnostics when setting modem status flags.
+.It 2048 ( Em RI )
+Reports when a RING is received.
+.El
+.It Fl i
+Display the card identification string and type ID.
+.It Fl r
+Reinitialize the card.
+For boards with external port modules, it is possible to add or remove
+modules and dynamically reprobe the number of ports using this switch.
+All ports on the card must be closed in order to reinitialize the card.
+.Pp
+It is preferable to reinitialize the card rather than reload the entire
+digi module as reinitialization only affects the specified board rather
+than affecting all attached boards.
+.El
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 5.0 .
+.Sh BUGS
+It should be possible to reinitialize a board without closing all of the
+existing ports.
diff --git a/usr.sbin/digictl/digictl.c b/usr.sbin/digictl/digictl.c
new file mode 100644
index 0000000..0aa5f68
--- /dev/null
+++ b/usr.sbin/digictl/digictl.c
@@ -0,0 +1,171 @@
+/*-
+ * Copyright (c) 2001 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <sys/digiio.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+enum aflag { NO_AFLAG, ALTPIN_DISABLE, ALTPIN_ENABLE, ALTPIN_QUERY };
+
+static int
+usage(const char *prog)
+{
+ fprintf(stderr, "usage: %s -a disable|enable|query device\n", prog);
+ fprintf(stderr, " %s [-d debug] [-ir] ctrl-device...\n", prog);
+ return (EX_USAGE);
+}
+
+int
+main(int argc, char **argv)
+{
+ char namedata[256], *name = namedata;
+ const char *prog;
+ enum digi_model model;
+ int altpin, ch, debug, fd, i, res;
+ int dflag, iflag, rflag;
+ enum aflag aflag;
+
+ if ((prog = strrchr(argv[0], '/')) == NULL)
+ prog = argv[0];
+ else
+ prog++;
+
+ aflag = NO_AFLAG;
+ dflag = iflag = rflag = 0;
+ while ((ch = getopt(argc, argv, "a:d:ir")) != -1)
+ switch (ch) {
+ case 'a':
+ if (strcasecmp(optarg, "disable") == 0)
+ aflag = ALTPIN_DISABLE;
+ else if (strcasecmp(optarg, "enable") == 0)
+ aflag = ALTPIN_ENABLE;
+ else if (strcasecmp(optarg, "query") == 0)
+ aflag = ALTPIN_QUERY;
+ else
+ return (usage(prog));
+ break;
+
+ case 'd':
+ dflag = 1;
+ debug = atoi(optarg);
+ break;
+
+ case 'i':
+ iflag = 1;
+ break;
+
+ case 'r':
+ rflag = 1;
+ break;
+
+ default:
+ return (usage(prog));
+ }
+
+ if ((dflag || iflag || rflag) && aflag != NO_AFLAG)
+ return (usage(prog));
+
+ if (argc <= optind)
+ return (usage(prog));
+
+ altpin = (aflag == ALTPIN_ENABLE);
+ res = 0;
+
+ for (i = optind; i < argc; i++) {
+ if ((fd = open(argv[i], O_RDONLY)) == -1) {
+ fprintf(stderr, "%s: %s: open: %s\n", prog, argv[i],
+ strerror(errno));
+ res++;
+ continue;
+ }
+
+ switch (aflag) {
+ case NO_AFLAG:
+ break;
+
+ case ALTPIN_ENABLE:
+ case ALTPIN_DISABLE:
+ if (ioctl(fd, DIGIIO_SETALTPIN, &altpin) != 0) {
+ fprintf(stderr, "%s: %s: set altpin: %s\n",
+ prog, argv[i], strerror(errno));
+ res++;
+ }
+ break;
+
+ case ALTPIN_QUERY:
+ if (ioctl(fd, DIGIIO_GETALTPIN, &altpin) != 0) {
+ fprintf(stderr, "%s: %s: get altpin: %s\n",
+ prog, argv[i], strerror(errno));
+ res++;
+ } else {
+ if (argc > optind + 1)
+ printf("%s: ", argv[i]);
+ puts(altpin ? "enabled" : "disabled");
+ }
+ break;
+ }
+
+ if (dflag && ioctl(fd, DIGIIO_DEBUG, &debug) != 0) {
+ fprintf(stderr, "%s: %s: debug: %s\n",
+ prog, argv[i], strerror(errno));
+ res++;
+ }
+
+ if (iflag) {
+ if (ioctl(fd, DIGIIO_MODEL, &model) != 0) {
+ fprintf(stderr, "%s: %s: model: %s\n",
+ prog, argv[i], strerror(errno));
+ res++;
+ } else if (ioctl(fd, DIGIIO_IDENT, &name) != 0) {
+ fprintf(stderr, "%s: %s: ident: %s\n",
+ prog, argv[i], strerror(errno));
+ res++;
+ } else {
+ if (argc > optind + 1)
+ printf("%s: ", argv[i]);
+ printf("%s (type %d)\n", name, (int)model);
+ }
+ }
+
+ if (rflag && ioctl(fd, DIGIIO_REINIT) != 0) {
+ fprintf(stderr, "%s: %s: reinit: %s\n",
+ prog, argv[i], strerror(errno));
+ res++;
+ }
+
+ close(fd);
+ }
+
+ return (res);
+}
diff --git a/usr.sbin/diskinfo/Makefile b/usr.sbin/diskinfo/Makefile
new file mode 100644
index 0000000..5ffa27f6
--- /dev/null
+++ b/usr.sbin/diskinfo/Makefile
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+PROG= diskinfo
+MAN= diskinfo.8
+
+WARNS?= 5
+
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+
+.include <bsd.prog.mk>
+
+test: ${PROG}
+ ./${PROG} /dev/ad4 md50
+ ./${PROG} -v /dev/ad4 md50
+ ./${PROG} -t /dev/ad4
diff --git a/usr.sbin/diskinfo/diskinfo.8 b/usr.sbin/diskinfo/diskinfo.8
new file mode 100644
index 0000000..dfc7040
--- /dev/null
+++ b/usr.sbin/diskinfo/diskinfo.8
@@ -0,0 +1,73 @@
+.\"
+.\" Copyright (c) 2003 Poul-Henning Kamp
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 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 November 9, 2004
+.Dt DISKINFO 8
+.Os
+.Sh NAME
+.Nm diskinfo
+.Nd get information about disk device
+.Sh SYNOPSIS
+.Nm
+.Op Fl ctv
+.Ar disk ...
+.Sh DESCRIPTION
+The
+.Nm
+utility prints out information about a disk device,
+and optionally runs a naive performance test on the device.
+.Pp
+If given no arguments, the output will be a single line per specified device
+with the following fields: device name, sectorsize, media size in bytes,
+media size in sectors, firmware cylinders, firmware heads, and firmware sectors.
+The last three fields are only present if the information is available.
+.Pp
+If given the
+.Fl v
+option, the fields will be printed one per line with a descriptive comment.
+.Pp
+The
+.Fl c
+option triggers a simple measurement of the I/O read command overhead.
+.Pp
+The
+.Fl t
+option triggers a simple and rather naive benchmark of the disks seek
+and transfer performance.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Fx 5.1 .
+.Sh AUTHORS
+.An Poul-Henning Kamp
+.Sh BUGS
+There are in order of increasing severity: lies,
+damn lies, statistics, and computer benchmarks.
diff --git a/usr.sbin/diskinfo/diskinfo.c b/usr.sbin/diskinfo/diskinfo.c
new file mode 100644
index 0000000..48b019b
--- /dev/null
+++ b/usr.sbin/diskinfo/diskinfo.c
@@ -0,0 +1,357 @@
+/*-
+ * Copyright (c) 2003 Poul-Henning Kamp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 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 <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libutil.h>
+#include <paths.h>
+#include <err.h>
+#include <sys/disk.h>
+#include <sys/time.h>
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: diskinfo [-ctv] disk ...\n");
+ exit (1);
+}
+
+static int opt_c, opt_t, opt_v;
+
+static void speeddisk(int fd, off_t mediasize, u_int sectorsize);
+static void commandtime(int fd, off_t mediasize, u_int sectorsize);
+
+int
+main(int argc, char **argv)
+{
+ int i, ch, fd, error;
+ char buf[BUFSIZ], ident[DISK_IDENT_SIZE];
+ off_t mediasize;
+ u_int sectorsize, fwsectors, fwheads;
+
+ while ((ch = getopt(argc, argv, "ctv")) != -1) {
+ switch (ch) {
+ case 'c':
+ opt_c = 1;
+ opt_v = 1;
+ break;
+ case 't':
+ opt_t = 1;
+ opt_v = 1;
+ break;
+ case 'v':
+ opt_v = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1)
+ usage();
+
+ for (i = 0; i < argc; i++) {
+ fd = open(argv[i], O_RDONLY);
+ if (fd < 0 && errno == ENOENT && *argv[i] != '/') {
+ sprintf(buf, "%s%s", _PATH_DEV, argv[i]);
+ fd = open(buf, O_RDONLY);
+ }
+ if (fd < 0)
+ err(1, argv[i]);
+ error = ioctl(fd, DIOCGMEDIASIZE, &mediasize);
+ if (error)
+ err(1, "%s: ioctl(DIOCGMEDIASIZE) failed, probably not a disk.", argv[i]);
+ error = ioctl(fd, DIOCGSECTORSIZE, &sectorsize);
+ if (error)
+ err(1, "%s: DIOCGSECTORSIZE failed, probably not a disk.", argv[i]);
+ error = ioctl(fd, DIOCGFWSECTORS, &fwsectors);
+ if (error)
+ fwsectors = 0;
+ error = ioctl(fd, DIOCGFWHEADS, &fwheads);
+ if (error)
+ fwheads = 0;
+ error = ioctl(fd, DIOCGIDENT, ident);
+ if (error)
+ ident[0] = '\0';
+ if (!opt_v) {
+ printf("%s", argv[i]);
+ printf("\t%u", sectorsize);
+ printf("\t%jd", (intmax_t)mediasize);
+ printf("\t%jd", (intmax_t)mediasize/sectorsize);
+ if (fwsectors != 0 && fwheads != 0) {
+ printf("\t%jd", (intmax_t)mediasize /
+ (fwsectors * fwheads * sectorsize));
+ printf("\t%u", fwheads);
+ printf("\t%u", fwsectors);
+ }
+ } else {
+ humanize_number(buf, 5, (int64_t)mediasize, "",
+ HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
+ printf("%s\n", argv[i]);
+ printf("\t%-12u\t# sectorsize\n", sectorsize);
+ printf("\t%-12jd\t# mediasize in bytes (%s)\n",
+ (intmax_t)mediasize, buf);
+ printf("\t%-12jd\t# mediasize in sectors\n",
+ (intmax_t)mediasize/sectorsize);
+ if (fwsectors != 0 && fwheads != 0) {
+ printf("\t%-12jd\t# Cylinders according to firmware.\n", (intmax_t)mediasize /
+ (fwsectors * fwheads * sectorsize));
+ printf("\t%-12u\t# Heads according to firmware.\n", fwheads);
+ printf("\t%-12u\t# Sectors according to firmware.\n", fwsectors);
+ }
+ if (ident[0] != '\0')
+ printf("\t%-12s\t# Disk ident.\n", ident);
+ }
+ printf("\n");
+ if (opt_c)
+ commandtime(fd, mediasize, sectorsize);
+ if (opt_t)
+ speeddisk(fd, mediasize, sectorsize);
+ close(fd);
+ }
+ exit (0);
+}
+
+
+static char sector[65536];
+static char mega[1024 * 1024];
+
+static void
+rdsect(int fd, u_int blockno, u_int sectorsize)
+{
+ int error;
+
+ lseek(fd, (off_t)blockno * sectorsize, SEEK_SET);
+ error = read(fd, sector, sectorsize);
+ if (error != (int)sectorsize)
+ err(1, "read error or disk too small for test.");
+}
+
+static void
+rdmega(int fd)
+{
+ int error;
+
+ error = read(fd, mega, sizeof(mega));
+ if (error != sizeof(mega))
+ err(1, "read error or disk too small for test.");
+}
+
+static struct timeval tv1, tv2;
+
+static void
+T0(void)
+{
+
+ fflush(stdout);
+ sync();
+ sleep(1);
+ sync();
+ sync();
+ gettimeofday(&tv1, NULL);
+}
+
+static void
+TN(int count)
+{
+ double dt;
+
+ gettimeofday(&tv2, NULL);
+ dt = (tv2.tv_usec - tv1.tv_usec) / 1e6;
+ dt += (tv2.tv_sec - tv1.tv_sec);
+ printf("%5d iter in %10.6f sec = %8.3f msec\n",
+ count, dt, dt * 1000.0 / count);
+}
+
+static void
+TR(double count)
+{
+ double dt;
+
+ gettimeofday(&tv2, NULL);
+ dt = (tv2.tv_usec - tv1.tv_usec) / 1e6;
+ dt += (tv2.tv_sec - tv1.tv_sec);
+ printf("%8.0f kbytes in %10.6f sec = %8.0f kbytes/sec\n",
+ count, dt, count / dt);
+}
+
+static void
+speeddisk(int fd, off_t mediasize, u_int sectorsize)
+{
+ int i;
+ uint b0, b1, sectorcount;
+
+ sectorcount = mediasize / sectorsize;
+
+ printf("Seek times:\n");
+ printf("\tFull stroke:\t");
+ b0 = 0;
+ b1 = sectorcount - 1 - 16384;
+ T0();
+ for (i = 0; i < 125; i++) {
+ rdsect(fd, b0, sectorsize);
+ b0 += 16384;
+ rdsect(fd, b1, sectorsize);
+ b1 -= 16384;
+ }
+ TN(250);
+
+ printf("\tHalf stroke:\t");
+ b0 = sectorcount / 4;
+ b1 = b0 + sectorcount / 2;
+ T0();
+ for (i = 0; i < 125; i++) {
+ rdsect(fd, b0, sectorsize);
+ b0 += 16384;
+ rdsect(fd, b1, sectorsize);
+ b1 += 16384;
+ }
+ TN(250);
+ printf("\tQuarter stroke:\t");
+ b0 = sectorcount / 4;
+ b1 = b0 + sectorcount / 4;
+ T0();
+ for (i = 0; i < 250; i++) {
+ rdsect(fd, b0, sectorsize);
+ b0 += 16384;
+ rdsect(fd, b1, sectorsize);
+ b1 += 16384;
+ }
+ TN(500);
+
+ printf("\tShort forward:\t");
+ b0 = sectorcount / 2;
+ T0();
+ for (i = 0; i < 400; i++) {
+ rdsect(fd, b0, sectorsize);
+ b0 += 16384;
+ }
+ TN(400);
+
+ printf("\tShort backward:\t");
+ b0 = sectorcount / 2;
+ T0();
+ for (i = 0; i < 400; i++) {
+ rdsect(fd, b0, sectorsize);
+ b0 -= 16384;
+ }
+ TN(400);
+
+ printf("\tSeq outer:\t");
+ b0 = 0;
+ T0();
+ for (i = 0; i < 2048; i++) {
+ rdsect(fd, b0, sectorsize);
+ b0++;
+ }
+ TN(2048);
+
+ printf("\tSeq inner:\t");
+ b0 = sectorcount - 2048 - 1;
+ T0();
+ for (i = 0; i < 2048; i++) {
+ rdsect(fd, b0, sectorsize);
+ b0++;
+ }
+ TN(2048);
+
+ printf("Transfer rates:\n");
+ printf("\toutside: ");
+ rdsect(fd, 0, sectorsize);
+ T0();
+ for (i = 0; i < 100; i++) {
+ rdmega(fd);
+ }
+ TR(100 * 1024);
+
+ printf("\tmiddle: ");
+ b0 = sectorcount / 2;
+ rdsect(fd, b0, sectorsize);
+ T0();
+ for (i = 0; i < 100; i++) {
+ rdmega(fd);
+ }
+ TR(100 * 1024);
+
+ printf("\tinside: ");
+ b0 = sectorcount - 100 * (1024*1024 / sectorsize) - 1;;
+ rdsect(fd, b0, sectorsize);
+ T0();
+ for (i = 0; i < 100; i++) {
+ rdmega(fd);
+ }
+ TR(100 * 1024);
+
+ printf("\n");
+ return;
+}
+
+static void
+commandtime(int fd, off_t mediasize, u_int sectorsize)
+{
+ double dtmega, dtsector;
+ int i;
+
+ printf("I/O command overhead:\n");
+ i = mediasize;
+ rdsect(fd, 0, sectorsize);
+ T0();
+ for (i = 0; i < 10; i++)
+ rdmega(fd);
+ gettimeofday(&tv2, NULL);
+ dtmega = (tv2.tv_usec - tv1.tv_usec) / 1e6;
+ dtmega += (tv2.tv_sec - tv1.tv_sec);
+
+ printf("\ttime to read 10MB block %10.6f sec\t= %8.3f msec/sector\n",
+ dtmega, dtmega*100/2048);
+
+ rdsect(fd, 0, sectorsize);
+ T0();
+ for (i = 0; i < 20480; i++)
+ rdsect(fd, 0, sectorsize);
+ gettimeofday(&tv2, NULL);
+ dtsector = (tv2.tv_usec - tv1.tv_usec) / 1e6;
+ dtsector += (tv2.tv_sec - tv1.tv_sec);
+
+ printf("\ttime to read 20480 sectors %10.6f sec\t= %8.3f msec/sector\n",
+ dtsector, dtsector*100/2048);
+ printf("\tcalculated command overhead\t\t\t= %8.3f msec/sector\n",
+ (dtsector - dtmega)*100/2048);
+
+ printf("\n");
+ return;
+}
diff --git a/usr.sbin/dnssec-keygen/Makefile b/usr.sbin/dnssec-keygen/Makefile
new file mode 100644
index 0000000..3ac9005
--- /dev/null
+++ b/usr.sbin/dnssec-keygen/Makefile
@@ -0,0 +1,22 @@
+# $FreeBSD$
+
+BIND_DIR= ${.CURDIR}/../../contrib/bind9
+LIB_BIND_REL= ../../lib/bind
+LIB_BIND_DIR= ${.CURDIR}/${LIB_BIND_REL}
+SRCDIR= ${BIND_DIR}/bin/dnssec
+
+.include "${LIB_BIND_DIR}/config.mk"
+
+PROG= dnssec-keygen
+
+.PATH: ${SRCDIR}
+SRCS+= dnssec-keygen.c dnssectool.c
+
+CFLAGS+= -I${SRCDIR}/unix/include -I${SRCDIR}/include
+
+DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD}
+LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD}
+
+MAN= dnssec-keygen.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/dnssec-signzone/Makefile b/usr.sbin/dnssec-signzone/Makefile
new file mode 100644
index 0000000..7c4ca65
--- /dev/null
+++ b/usr.sbin/dnssec-signzone/Makefile
@@ -0,0 +1,22 @@
+# $FreeBSD$
+
+BIND_DIR= ${.CURDIR}/../../contrib/bind9
+LIB_BIND_REL= ../../lib/bind
+LIB_BIND_DIR= ${.CURDIR}/${LIB_BIND_REL}
+SRCDIR= ${BIND_DIR}/bin/dnssec
+
+.include "${LIB_BIND_DIR}/config.mk"
+
+PROG= dnssec-signzone
+
+.PATH: ${SRCDIR}
+SRCS+= dnssec-signzone.c dnssectool.c
+
+CFLAGS+= -I${SRCDIR}/unix/include -I${SRCDIR}/include
+
+DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD}
+LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD}
+
+MAN= dnssec-signzone.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/dumpcis/Makefile b/usr.sbin/dumpcis/Makefile
new file mode 100644
index 0000000..283de3a
--- /dev/null
+++ b/usr.sbin/dumpcis/Makefile
@@ -0,0 +1,10 @@
+# pccardc Makefile
+#
+# $FreeBSD$
+
+PROG= dumpcis
+MAN= dumpcis.8
+SRCS= main.c readcis.c printcis.c
+WARNS?= 5
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/dumpcis/cardinfo.h b/usr.sbin/dumpcis/cardinfo.h
new file mode 100644
index 0000000..9489067
--- /dev/null
+++ b/usr.sbin/dumpcis/cardinfo.h
@@ -0,0 +1,205 @@
+/*
+ * Include file for PCMCIA user process interface
+ *
+ *-------------------------------------------------------------------------
+ */
+/*-
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$ */
+
+#ifndef _PCCARD_CARDINFO_H_
+#define _PCCARD_CARDINFO_H_
+
+#ifndef _KERNEL
+#include <sys/types.h>
+#endif
+#include <sys/ioccom.h>
+
+#define PIOCGSTATE _IOR('P', 1, struct slotstate) /* Get slot state */
+#define PIOCGMEM _IOWR('P', 2, struct mem_desc) /* Get memory map */
+#define PIOCSMEM _IOW('P', 3, struct mem_desc) /* Set memory map */
+#define PIOCGIO _IOWR('P', 4, struct io_desc) /* Get I/O map */
+#define PIOCSIO _IOW('P', 5, struct io_desc) /* Set I/O map */
+#define PIOCSDRV _IOWR('P', 6, struct dev_desc) /* Set driver */
+#define PIOCRWFLAG _IOW('P', 7, int) /* Set flags for drv use */
+#define PIOCRWMEM _IOWR('P', 8, unsigned long) /* Set mem for drv use */
+#define PIOCSPOW _IOW('P', 9, struct power) /* Set power structure */
+#define PIOCSVIR _IOW('P', 10, int) /* Virtual insert/remove */
+#define PIOCSBEEP _IOW('P', 11, int) /* Select Beep */
+#define PIOCSRESOURCE _IOWR('P', 12, struct pccard_resource) /* get resource info */
+/*
+ * Debug codes.
+ */
+#define PIOCGREG _IOWR('P',100, struct pcic_reg) /* get reg */
+#define PIOCSREG _IOW('P', 101, struct pcic_reg) /* Set reg */
+
+/*
+ * Slot states for PIOCGSTATE
+ *
+ * Here's a state diagram of all the possible states:
+ *
+ * power x 1
+ * -------------------
+ * / \
+ * / v
+ * resume +----------+ power x 0 +----------+
+ * ------->| inactive |<--------------| filled |
+ * / +----------+ +----------+
+ * / / \ ^ |
+ * nil <--------- \ insert or | | suspend or
+ * suspend \ power x 1 | | eject
+ * \ | v
+ * \ +----------+
+ * ----------->| empty |
+ * eject +----------+
+ *
+ * Note, the above diagram is for the state. On suspend, the laststate
+ * gets set to suspend to tell pccardd what happened. Also the nil state
+ * means that when the no state change has happened. Note: if you eject
+ * while suspended in the inactive state, you will return to the
+ * empty state if you do not insert a new card and to the inactive state
+ * if you do insert a new card.
+ *
+ * Some might argue that inactive should be sticky forever and
+ * eject/insert shouldn't take it out of that state. They might be
+ * right. On the other hand, some would argue that eject resets all
+ * state. They might be right. They both can't be right. The above
+ * represents a reasonable compromise between the two.
+ *
+ * Some bridges allow one to query to see if the card was changed while
+ * we were suspended. Others do not. We make no use of this functionality
+ * at this time.
+ */
+enum cardstate { noslot, empty, suspend, filled, inactive };
+
+/*
+ * Descriptor structure for memory map.
+ */
+struct mem_desc {
+ int window; /* Memory map window number (0-4) */
+ int flags; /* Flags - see below */
+ caddr_t start; /* System memory start */
+ int size; /* Size of memory area */
+ unsigned long card; /* Card memory address */
+};
+
+#define MDF_16BITS 0x01 /* Memory is 16 bits wide */
+#define MDF_ZEROWS 0x02 /* Set no wait states for memory */
+#define MDF_WS0 0x04 /* Wait state flags */
+#define MDF_WS1 0x08
+#define MDF_ATTR 0x10 /* Memory is attribute memory */
+#define MDF_WP 0x20 /* Write protect memory */
+#define MDF_ACTIVE 0x40 /* Context active (read-only) */
+
+/*
+ * Descriptor structure for I/O map
+ */
+struct io_desc {
+ int window; /* I/O map number (0-1) */
+ int flags; /* Flags - see below */
+ int start; /* I/O port start */
+ int size; /* Number of port addresses */
+};
+
+#define IODF_WS 0x01 /* Set wait states for 16 bit I/O access */
+#define IODF_16BIT 0x02 /* I/O access are 16 bit */
+#define IODF_CS16 0x04 /* Allow card selection of 16 bit access */
+#define IODF_ZEROWS 0x08 /* No wait states for 8 bit I/O */
+#define IODF_ACTIVE 0x10 /* Context active (read-only) */
+
+/*
+ * Device descriptor for allocation of driver.
+ */
+#define DEV_MISC_LEN 36
+#define DEV_MAX_CIS_LEN 40
+struct dev_desc {
+ char name[16]; /* Driver name */
+ int unit; /* Driver unit number */
+ unsigned long mem; /* Memory address of driver */
+ int memsize; /* Memory size (if used) */
+ int iobase; /* base of I/O ports */
+ int iosize; /* Length of I/O ports */
+ int irqmask; /* Interrupt number(s) to allocate */
+ int flags; /* Device flags */
+ uint8_t misc[DEV_MISC_LEN]; /* For any random info */
+ uint8_t manufstr[DEV_MAX_CIS_LEN];
+ uint8_t versstr[DEV_MAX_CIS_LEN];
+ uint8_t cis3str[DEV_MAX_CIS_LEN];
+ uint8_t cis4str[DEV_MAX_CIS_LEN];
+ uint32_t manufacturer; /* Manufacturer ID */
+ uint32_t product; /* Product ID */
+ uint32_t prodext; /* Product ID (extended) */
+};
+#define DEV_DESC_HAS_SIZE 1
+
+struct pcic_reg {
+ unsigned char reg;
+ unsigned char value;
+};
+
+/*
+ * Slot information. Used to read current status of slot.
+ */
+struct slotstate {
+ enum cardstate state; /* Current state of slot */
+ enum cardstate laststate; /* Previous state of slot */
+ int maxmem; /* Max allowed memory windows */
+ int maxio; /* Max allowed I/O windows */
+ int irqs; /* Bitmap of IRQs allowed */
+ int flags; /* Capability flags */
+};
+
+/*
+ * The power values are in volts * 10, e.g. 5V is 50, 3.3V is 33.
+ */
+struct power {
+ int vcc;
+ int vpp;
+};
+
+/*
+ * The PC-Card resource IOC_GET_RESOURCE_RANGE
+ */
+struct pccard_resource {
+ int type;
+ u_long size;
+ u_long min;
+ u_long max;
+ u_long resource_addr;
+};
+
+
+/*
+ * Other system limits
+ */
+#define MAXSLOT 16
+#define NUM_MEM_WINDOWS 10
+#define NUM_IO_WINDOWS 6
+#define CARD_DEVICE "/dev/card%d" /* String for snprintf */
+#define PCCARD_MEMSIZE (4*1024)
+
+#endif /* !_PCCARD_CARDINFO_H_ */
diff --git a/usr.sbin/dumpcis/cis.h b/usr.sbin/dumpcis/cis.h
new file mode 100644
index 0000000..6cc935b
--- /dev/null
+++ b/usr.sbin/dumpcis/cis.h
@@ -0,0 +1,279 @@
+/*
+ * PCMCIA card structures and defines.
+ * These defines relate to the user level
+ * structures and card information, not
+ * driver/process communication.
+ *-------------------------------------------------------------------------
+ */
+/*-
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ *
+ */
+
+/*
+ * Card Information Structure tuples definitions
+ * The structure of a tuple is basically:
+ *
+ * Tuple_code
+ * Tuple_data_length
+ * Tuple_data ...
+ *
+ * Tuples are contiguous in attribute memory, and
+ * are terminated with a 0xFF for the tuple code or
+ * the tuple length.
+ */
+#ifndef _PCCARD_CIS_H
+#define _PCCARD_CIS_H
+
+#define CIS_NULL 0 /* Empty tuple */
+#define CIS_MEM_COMMON 0x01 /* Device descriptor, common memory */
+#define CIS_LONGLINK_CB 0x02 /* Long link to next chain for CardBus */
+#define CIS_INDIRECT 0x03 /* Indirect access */
+#define CIS_CONF_MAP_CB 0x04 /* Card Configuration map for CardBus */
+#define CIS_CONFIG_CB 0x05 /* Card Configuration entry for CardBus */
+#define CIS_LONGLINK_MFC 0x06 /* Long link to next chain for Multi function card */
+#define CIS_BAR 0x07 /* Base address register for CardBus */
+#define CIS_CHECKSUM 0x10 /* Checksum */
+#define CIS_LONGLINK_A 0x11 /* Link to Attribute memory */
+#define CIS_LONGLINK_C 0x12 /* Link to Common memory */
+#define CIS_LINKTARGET 0x13 /* Linked tuple must start with this. */
+#define CIS_NOLINK 0x14 /* Assume no common memory link tuple. */
+#define CIS_INFO_V1 0x15 /* Card info data, version 1 */
+#define CIS_ALTSTR 0x16 /* Alternate language string tuple. */
+#define CIS_MEM_ATTR 0x17 /* Device descriptor, Attribute memory */
+#define CIS_JEDEC_C 0x18 /* JEDEC descr for common memory */
+#define CIS_JEDEC_A 0x19 /* JEDEC descr for Attribute memory */
+#define CIS_CONF_MAP 0x1A /* Card Configuration map */
+#define CIS_CONFIG 0x1B /* Card Configuration entry */
+#define CIS_DEVICE_OC 0x1C /* Other conditions info - common memory */
+#define CIS_DEVICE_OA 0x1D /* Other conditions info - attribute memory */
+#define CIS_DEVICEGEO 0x1E /* Geometry info for common memory */
+#define CIS_DEVICEGEO_A 0x1F /* Geometry info for attribute memory */
+#define CIS_MANUF_ID 0x20 /* Card manufacturer's ID */
+#define CIS_FUNC_ID 0x21 /* Function of card */
+#define CIS_FUNC_EXT 0x22 /* Functional extension */
+/*
+ * Data recording format tuples.
+ */
+#define CIS_SW_INTERLV 0x23 /* Software interleave */
+#define CIS_VERS_2 0x40 /* Card info data, version 2 */
+#define CIS_FORMAT 0x41 /* Memory card format */
+#define CIS_GEOMETRY 0x42 /* Disk sector layout */
+#define CIS_BYTEORDER 0x43 /* Byte order of memory data */
+#define CIS_DATE 0x44 /* Format data/time */
+#define CIS_BATTERY 0x45 /* Battery replacement date */
+#define CIS_ORG 0x46 /* Organization of data on card */
+#define CIS_END 0xFF /* Termination code */
+
+/*
+ * Internal tuple definitions.
+ *
+ * Device descriptor for memory (CIS_MEM_ATTR, CIS_MEM_COMMON)
+ *
+ * Byte 1:
+ * 0xF0 - Device type
+ * 0x08 - Write protect switch
+ * 0x07 - Speed index (7 = extended speed)
+ * Byte 2: Extended speed (bit 7 = another follows)
+ * Byte 3: (ignored if 0xFF)
+ * 0xF8 - Addressable units (0's numbered)
+ * 0x07 - Unit size
+ * The three byte sequence is repeated until byte 1 == 0xFF
+ */
+
+/*
+ * CIS_INFO_V1 - Version one card information.
+ *
+ * Byte 1: Major version number (should be 4)
+ * Byte 2: Minor version number (should be 1)
+ * Byte 3-x: Null terminated Manufacturer name
+ * Byte x-x: Null terminated product name
+ * Byte x-x: Null terminated additional info 1
+ * Byte x-x: Null terminated additional info 2
+ * Byte x: final byte must be 0xFF
+ */
+#define CIS_MAJOR_VERSION 4
+#define CIS_MINOR_VERSION 1
+
+/*
+ * CIS_CONF_MAP - Provides an address map for the card
+ * configuration register(s), and a max value
+ * identifying the last configuration tuple.
+ *
+ * Byte 1:
+ * 0x3C - Register mask size (0's numbered)
+ * 0x03 - Register address size (0's numbered)
+ * Byte 2:
+ * 0x3F - ID of last configuration.
+ * Byte 3-n: Card register address (size is determined by
+ * the value in byte 1).
+ * Byte x-x: Card register masks (size determined by the
+ * value in byte 1)
+ */
+
+/*
+ * CIS_CONFIG - Card configuration entry. Multiple tuples may
+ * exist of this type, each one describing a different
+ * memory/I-O map that can be used to address this card.
+ * The first one usually has extra config data about the
+ * card features. The final configuration tuple number
+ * is stored in the CIS_CONF_MAP tuple so that the complete
+ * list can be scanned.
+ *
+ * Byte 1:
+ * 0x3F - Configuration ID number.
+ * 0x40 - Indicates this is the default configuration
+ * 0x80 - Interface byte exists
+ * Byte 2: (exists only if bit 0x80 set in byte 1)
+ * 0x0F - Interface type value
+ * 0x10 - Battery voltage detect
+ * 0x20 - Write protect active
+ * 0x40 - RdyBsy active bit
+ * 0x80 - Wait signal required
+ * Byte 3: (features byte)
+ * 0x03 - Power sub-tuple(s) exists
+ * 0x04 - Timing sub-tuple exists
+ * 0x08 - I/O space sub-tuple exists
+ * 0x10 - IRQ sub-tuple exists
+ * 0x60 - Memory space sub-tuple(s) exists
+ * 0x80 - Miscellaneous sub-tuple exists
+ */
+#define CIS_FEAT_POWER(x) ((x) & 0x3)
+#define CIS_FEAT_TIMING 0x4
+#define CIS_FEAT_I_O 0x8
+#define CIS_FEAT_IRQ 0x10
+#define CIS_FEAT_MEMORY(x) (((x) >> 5) & 0x3)
+#define CIS_FEAT_MISC 0x80
+/*
+ * Depending on whether the "features" byte has the corresponding
+ * bit set, a number of sub-tuples follow. Some features have
+ * more than one sub-tuple, depending on the count within the
+ * features byte (e.g power feature bits allows up to 3 sub-tuples).
+ *
+ * Power structure sub-tuple:
+ * Byte 1: parameter exists - Each bit (starting from 0x01) indicates
+ * that a parameter block exists - up to 8 parameter blocks
+ * are therefore allowed).
+ * Byte 2:
+ * 0x7F - Parameter data
+ * 0x80 - More bytes follow (0 = last byte)
+ *
+ * Timing sub-tuple
+ * Byte 1:
+ * 0x03 - Wait scale
+ * 0x1C - Ready scale
+ * 0xE0 - Reserved scale
+ * Byte 2: extended wait scale if wait scale != 3
+ * Byte 3: extended ready scale if ready scale != 7
+ * Byte 4: extended reserved scale if reserved scale != 7
+ */
+#define CIS_WAIT_SCALE(x) ((x) & 0x3)
+#define CIS_READY_SCALE(x) (((x)>>2) & 0x7)
+#define CIS_RESERVED_SCALE(x) (((x)>>5) & 0x7)
+/*
+ * I/O mapping sub-tuple:
+ * Byte 1:
+ * 0x1F - I/O address lines
+ * 0x20 - 8 bit I/O
+ * 0x40 - 16 bit I/O
+ * 0x80 - I/O range??
+ * Byte 2:
+ * 0x0F - 0's numbered count of I/O block subtuples following.
+ * 0x30 - Size of I/O address value within subtuple. Values
+ * can be 1 (8 bits), 2 (16 bits) or 3 (32 bits).
+ * 0xC0 - Size of I/O port block size value within subtuple.
+ * I/O block sub-tuples, count from previous block:
+ * Byte 1-n: I/O start address
+ * Byte x-x: Size of I/O port block.
+ */
+#define CIS_IO_ADDR(x) ((x) & 0x1F)
+#define CIS_IO_8BIT 0x20
+#define CIS_IO_16BIT 0x40
+#define CIS_IO_RANGE 0x80
+#define CIS_IO_BLKS(x) ((x) & 0xF)
+#define CIS_IO_ADSZ(x) (((x)>>4) & 3)
+#define CIS_IO_BLKSZ(x) (((x)>>6) & 3)
+/*
+ * IRQ sub-tuple.
+ * Byte 1:
+ * 0x0F - Irq number or mask bits
+ * 0x10 - IRQ mask values exist
+ * 0x20 - Level triggered interrupts
+ * 0x40 - Pulse triggered requests
+ * 0x80 - Interrupt sharing.
+ * Byte 2-3: Interrupt req mask (if 0x10 of byte 1 set).
+ */
+#define CIS_IRQ_IRQN(x) ((x) & 0xF)
+#define CIS_IRQ_MASK 0x10
+#define CIS_IRQ_LEVEL 0x20
+#define CIS_IRQ_PULSE 0x40
+#define CIS_IRQ_SHARING 0x80
+/*
+ * Memory block subtuple. Depending on the features bits, the
+ * following subtuples are used:
+ * mem features == 1
+ * Byte 1-2: upper 16 bits of 24 bit memory length.
+ * mem features == 2
+ * Byte 1-2: upper 16 bits of 24 bit memory length.
+ * Byte 3-4: upper 16 bits of 24 bit memory address.
+ * mem_features == 3
+ * Byte 1:
+ * 0x07 - 0's numbered count of memory sub-tuples
+ * 0x18 - Memory length size (1's numbered)
+ * 0x60 - Memory address size (1's numbered)
+ * 0x80 - Host address value exists
+ * Memory sub-tuples follow:
+ * Byte 1-n: Memory length value (<< 8)
+ * Byte n-n: Memory card address value (<< 8)
+ * Byte n-n: Memory host address value (<< 8)
+ */
+#define CIS_FEAT_MEM_NONE 0 /* No memory config */
+#define CIS_FEAT_MEM_LEN 1 /* Just length */
+#define CIS_FEAT_MEM_ADDR 2 /* Card address & length */
+#define CIS_FEAT_MEM_WIN 3 /* Multiple windows */
+
+#define CIS_MEM_WINS(x) (((x) & 0x7)+1)
+#define CIS_MEM_LENSZ(x) (((x) >> 3) & 0x3)
+#define CIS_MEM_ADDRSZ(x) (((x) >> 5) & 0x3)
+#define CIS_MEM_HOST 0x80
+/*
+ * Misc sub-tuple.
+ * Byte 1:
+ * Byte 2:
+ * 0x0c - DMA Request Signal
+ * 00 - not support DMA
+ * 01 - use SPKR# line
+ * 10 - use IOIS16# line
+ * 11 - use INPACK# line
+ * 0x10 - DMA Width
+ * 0 - 8 bit DMA
+ * 1 - 16 bit DMA
+ */
+#define CIS_MISC_DMA_WIDTH(x) (((x) & 0x10) >> 4)
+#define CIS_MISC_DMA_REQ(x) (((x) >> 2) & 0x3)
+
+#endif /* _PCCARD_CIS_H */
diff --git a/usr.sbin/dumpcis/dumpcis.8 b/usr.sbin/dumpcis/dumpcis.8
new file mode 100644
index 0000000..518eadd
--- /dev/null
+++ b/usr.sbin/dumpcis/dumpcis.8
@@ -0,0 +1,49 @@
+.\"
+.\" Copyright (c) 2006 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.
+.\" 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 October 18, 2006
+.Dt DUMPCIS 8
+.Os
+.Sh NAME
+.Nm dumpcis
+.Nd PC Card and Cardbus (PCMCIA) CIS display tool
+.Sh SYNOPSIS
+.Nm
+.Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility translates a raw CIS stream into human readable form.
+.Sh SEE ALSO
+.Xr cardbus 4 ,
+.Xr cbb 4 ,
+.Xr pccard 4
+.Sh AUTHORS
+.An -nosplit
+The original version was written by
+.An Warner Losh Aq imp@FreeBSD.org .
diff --git a/usr.sbin/dumpcis/main.c b/usr.sbin/dumpcis/main.c
new file mode 100644
index 0000000..2e66506
--- /dev/null
+++ b/usr.sbin/dumpcis/main.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2006 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 ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED 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 <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "readcis.h"
+
+static void
+scanfile(char *name)
+{
+ int fd;
+ struct tuple_list *tl;
+
+ fd = open(name, O_RDONLY);
+ if (fd < 0)
+ return;
+ tl = readcis(fd);
+ if (tl) {
+ printf("Configuration data for file %s\n",
+ name);
+ dumpcis(tl);
+ freecis(tl);
+ }
+ close(fd);
+}
+
+int
+main(int argc, char **argv)
+{
+ for (argc--, argv++; argc; argc--, argv++)
+ scanfile(*argv);
+ return 0;
+}
diff --git a/usr.sbin/dumpcis/printcis.c b/usr.sbin/dumpcis/printcis.c
new file mode 100644
index 0000000..74221e8
--- /dev/null
+++ b/usr.sbin/dumpcis/printcis.c
@@ -0,0 +1,1105 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 */
+
+/*
+ * Code cleanup, bug-fix and extension
+ * by Tatsumi Hosokawa <hosokawa@mt.cs.keio.ac.jp>
+ */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include "cis.h"
+#include "readcis.h"
+
+static void dump_config_map(struct tuple *tp);
+static void dump_cis_config(struct tuple *tp);
+static void dump_other_cond(u_char *p, int len);
+static void dump_device_desc(u_char *p, int len, const char *type);
+static void dump_info_v1(u_char *p, int len);
+static void dump_longlink_mfc(u_char *p, int len);
+static void dump_bar(u_char *p, int len);
+static void dump_device_geo(u_char *p, int len);
+static void dump_func_id(u_char *p);
+static void dump_serial_ext(u_char *p, int len);
+static void dump_disk_ext(u_char *p, int len);
+static void dump_network_ext(u_char *p, int len);
+static void dump_info_v2(u_char *p, int len);
+static void dump_org(u_char *p, int len);
+
+void
+dumpcis(struct tuple_list *tlist)
+{
+ struct tuple *tp;
+ struct tuple_list *tl;
+ int count = 0, sz, ad, i;
+ u_char *p;
+ int func = 0;
+
+ for (tl = tlist; tl; tl = tl->next)
+ for (tp = tl->tuples; tp; tp = tp->next) {
+ printf("Tuple #%d, code = 0x%x (%s), length = %d\n",
+ ++count, tp->code, tuple_name(tp->code), tp->length);
+ p = tp->data;
+ sz = tp->length;
+ ad = 0;
+ while (sz > 0) {
+ printf(" %03x: ", ad);
+ for (i = 0; i < ((sz < 16) ? sz : 16); i++)
+ printf(" %02x", p[i]);
+ printf("\n");
+ sz -= 16;
+ p += 16;
+ ad += 16;
+ }
+ switch (tp->code) {
+ default:
+ break;
+ case CIS_MEM_COMMON: /* 0x01 */
+ dump_device_desc(tp->data, tp->length, "Common");
+ break;
+ case CIS_CONF_MAP_CB: /* 0x04 */
+ dump_config_map(tp);
+ break;
+ case CIS_CONFIG_CB: /* 0x05 */
+ dump_cis_config(tp);
+ break;
+ case CIS_LONGLINK_MFC: /* 0x06 */
+ dump_longlink_mfc(tp->data, tp->length);
+ break;
+ case CIS_BAR: /* 0x07 */
+ dump_bar(tp->data, tp->length);
+ break;
+ case CIS_CHECKSUM: /* 0x10 */
+ printf("\tChecksum from offset %d, length %d, value is 0x%x\n",
+ tpl16(tp->data),
+ tpl16(tp->data + 2),
+ tp->data[4]);
+ break;
+ case CIS_LONGLINK_A: /* 0x11 */
+ printf("\tLong link to attribute memory, address 0x%x\n",
+ tpl32(tp->data));
+ break;
+ case CIS_LONGLINK_C: /* 0x12 */
+ printf("\tLong link to common memory, address 0x%x\n",
+ tpl32(tp->data));
+ break;
+ case CIS_INFO_V1: /* 0x15 */
+ dump_info_v1(tp->data, tp->length);
+ break;
+ case CIS_ALTSTR: /* 0x16 */
+ break;
+ case CIS_MEM_ATTR: /* 0x17 */
+ dump_device_desc(tp->data, tp->length, "Attribute");
+ break;
+ case CIS_JEDEC_C: /* 0x18 */
+ case CIS_JEDEC_A: /* 0x19 */
+ break;
+ case CIS_CONF_MAP: /* 0x1A */
+ dump_config_map(tp);
+ break;
+ case CIS_CONFIG: /* 0x1B */
+ dump_cis_config(tp);
+ break;
+ case CIS_DEVICE_OC: /* 0x1C */
+ case CIS_DEVICE_OA: /* 0x1D */
+ dump_other_cond(tp->data, tp->length);
+ break;
+ case CIS_DEVICEGEO: /* 0x1E */
+ case CIS_DEVICEGEO_A: /* 0x1F */
+ dump_device_geo(tp->data, tp->length);
+ break;
+ case CIS_MANUF_ID: /* 0x20 */
+ printf("\tPCMCIA ID = 0x%x, OEM ID = 0x%x\n",
+ tpl16(tp->data),
+ tpl16(tp->data + 2));
+ break;
+ case CIS_FUNC_ID: /* 0x21 */
+ func = tp->data[0];
+ dump_func_id(tp->data);
+ break;
+ case CIS_FUNC_EXT: /* 0x22 */
+ switch (func) {
+ case 2:
+ dump_serial_ext(tp->data, tp->length);
+ break;
+ case 4:
+ dump_disk_ext(tp->data, tp->length);
+ break;
+ case 6:
+ dump_network_ext(tp->data, tp->length);
+ break;
+ }
+ break;
+ case CIS_VERS_2: /* 0x40 */
+ dump_info_v2(tp->data, tp->length);
+ break;
+ case CIS_ORG: /* 0x46 */
+ dump_org(tp->data, tp->length);
+ break;
+ }
+ }
+}
+
+/*
+ * CIS_CONF_MAP : Dump configuration map tuple.
+ * CIS_CONF_MAP_CB: Dump configuration map for CardBus
+ */
+static void
+dump_config_map(struct tuple *tp)
+{
+ u_char *p = tp->data, x;
+ int rlen, mlen = 0;
+ int i;
+
+ rlen = (p[0] & 3) + 1;
+ if (tp->code == CIS_CONF_MAP)
+ mlen = ((p[0] >> 2) & 3) + 1;
+ if (tp->length < rlen + mlen + 2) {
+ printf("\tWrong length for configuration map tuple\n");
+ return;
+ }
+ printf("\tReg len = %d, config register addr = 0x%x, last config = 0x%x\n",
+ rlen, parse_num(rlen | 0x10, p + 2, &p, 0), p[1]);
+ if (mlen) {
+ printf("\tRegisters: ");
+ for (i = 0; i < mlen; i++, p++) {
+ for (x = 0x1; x; x <<= 1)
+ printf("%c", x & *p ? 'X' : '-');
+ putchar(' ');
+ }
+ }
+ i = tp->length - (rlen + mlen + 2);
+ if (i) {
+ if (!mlen)
+ putchar('\t');
+ printf("%d bytes in subtuples", i);
+ }
+ if (mlen || i)
+ putchar('\n');
+}
+
+/*
+ * Dump power descriptor.
+ * call from dump_cis_config()
+ */
+static int
+print_pwr_desc(u_char *p)
+{
+ int len = 1, i;
+ u_char mask;
+ const char **expp;
+ static const char *pname[] =
+ {"Nominal operating supply voltage",
+ "Minimum operating supply voltage",
+ "Maximum operating supply voltage",
+ "Continuous supply current",
+ "Max current average over 1 second",
+ "Max current average over 10 ms",
+ "Power down supply current",
+ "Reserved"
+ };
+ static const char *vexp[] =
+ {"10uV", "100uV", "1mV", "10mV", "100mV", "1V", "10V", "100V"};
+ static const char *cexp[] =
+ {"10nA", "1uA", "10uA", "100uA", "1mA", "10mA", "100mA", "1A"};
+ static const char *mant[] =
+ {"1", "1.2", "1.3", "1.5", "2", "2.5", "3", "3.5", "4", "4.5",
+ "5", "5.5", "6", "7", "8", "9"};
+
+ mask = *p++;
+ expp = vexp;
+ for (i = 0; i < 8; i++)
+ if (mask & (1 << i)) {
+ len++;
+ if (i >= 3)
+ expp = cexp;
+ printf("\t\t%s: ", pname[i]);
+ printf("%s x %s",
+ mant[(*p >> 3) & 0xF],
+ expp[*p & 7]);
+ while (*p & 0x80) {
+ len++;
+ p++;
+ printf(", ext = 0x%x", *p);
+ }
+ printf("\n");
+ p++;
+ }
+ return (len);
+}
+
+/*
+ * print_ext_speed - Print extended speed.
+ * call from dump_cis_config(), dump_device_desc()
+ */
+static void
+print_ext_speed(u_char x, int scale)
+{
+ static const char *mant[] =
+ {"Reserved", "1.0", "1.2", "1.3", "1.5", "2.0", "2.5", "3.0",
+ "3.5", "4.0", "4.5", "5.0", "5.5", "6.0", "7.0", "8.0"};
+ static const char *exp[] =
+ {"1 ns", "10 ns", "100 ns", "1 us", "10 us", "100 us",
+ "1 ms", "10 ms"};
+ static const char *scale_name[] =
+ {"None", "10", "100", "1,000", "10,000", "100,000",
+ "1,000,000", "10,000,000"};
+
+ printf("Speed = %s x %s", mant[(x >> 3) & 0xF], exp[x & 7]);
+ if (scale)
+ printf(", scaled by %s", scale_name[scale & 7]);
+}
+
+/*
+ * Print variable length value.
+ * call from print_io_map(), print_mem_map()
+ */
+static int
+print_num(int sz, const char *fmt, u_char *p, int ofs)
+{
+ switch (sz) {
+ case 0:
+ case 0x10:
+ return 0;
+ case 1:
+ case 0x11:
+ printf(fmt, *p + ofs);
+ return 1;
+ case 2:
+ case 0x12:
+ printf(fmt, tpl16(p) + ofs);
+ return 2;
+ case 0x13:
+ printf(fmt, tpl24(p) + ofs);
+ return 3;
+ case 3:
+ case 0x14:
+ printf(fmt, tpl32(p) + ofs);
+ return 4;
+ }
+ errx(1, "print_num(0x%x): Illegal arguments", sz);
+/*NOTREACHED*/
+}
+
+/*
+ * Print I/O mapping sub-tuple.
+ * call from dump_cis_config()
+ */
+static u_char *
+print_io_map(u_char *p, u_char *q)
+{
+ int i, j;
+ u_char c;
+
+ if (q <= p)
+ goto err;
+ if (CIS_IO_ADDR(*p)) /* I/O address line */
+ printf("\tCard decodes %d address lines",
+ CIS_IO_ADDR(*p));
+ else
+ printf("\tCard provides address decode");
+
+ /* 8/16 bit I/O */
+ switch (*p & (CIS_IO_8BIT | CIS_IO_16BIT)) {
+ case CIS_IO_8BIT:
+ printf(", 8 Bit I/O only");
+ break;
+ case CIS_IO_16BIT:
+ printf(", limited 8/16 Bit I/O");
+ break;
+ case (CIS_IO_8BIT | CIS_IO_16BIT):
+ printf(", full 8/16 Bit I/O");
+ break;
+ }
+ putchar('\n');
+
+ /* I/O block sub-tuple exist */
+ if (*p++ & CIS_IO_RANGE) {
+ if (q <= p)
+ goto err;
+ c = *p++;
+ /* calculate byte length */
+ j = CIS_IO_ADSZ(c) + CIS_IO_BLKSZ(c);
+ if (CIS_IO_ADSZ(c) == 3)
+ j++;
+ if (CIS_IO_BLKSZ(c) == 3)
+ j++;
+ /* number of I/O block sub-tuples */
+ for (i = 0; i <= CIS_IO_BLKS(c); i++) {
+ if (q - p < j)
+ goto err;
+ printf("\t\tI/O address # %d: ", i + 1);
+ /* start block address */
+ p += print_num(CIS_IO_ADSZ(c),
+ "block start = 0x%x", p, 0);
+ /* block size */
+ p += print_num(CIS_IO_BLKSZ(c),
+ " block length = 0x%x", p, 1);
+ putchar('\n');
+ }
+ }
+ return p;
+
+ err: /* warning */
+ printf("\tWrong length for I/O mapping sub-tuple\n");
+ return p;
+}
+
+/*
+ * Print IRQ sub-tuple.
+ * call from dump_cis_config()
+ */
+static u_char *
+print_irq_map(u_char *p, u_char *q)
+{
+ int i, j;
+ u_char c;
+
+ if (q <= p)
+ goto err;
+ printf("\t\tIRQ modes:");
+ c = ' ';
+ if (*p & CIS_IRQ_LEVEL) { /* Level triggered interrupts */
+ printf(" Level");
+ c = ',';
+ }
+ if (*p & CIS_IRQ_PULSE) { /* Pulse triggered requests */
+ printf("%c Pulse", c);
+ c = ',';
+ }
+ if (*p & CIS_IRQ_SHARING) /* Interrupt sharing */
+ printf("%c Shared", c);
+ putchar('\n');
+
+ /* IRQ mask values exist */
+ if (*p & CIS_IRQ_MASK) {
+ if (q - p < 3)
+ goto err;
+ i = tpl16(p + 1); /* IRQ mask */
+ printf("\t\tIRQs: ");
+ if (*p & 1)
+ printf(" NMI");
+ if (*p & 0x2)
+ printf(" IOCK");
+ if (*p & 0x4)
+ printf(" BERR");
+ if (*p & 0x8)
+ printf(" VEND");
+ for (j = 0; j < 16; j++)
+ if (i & (1 << j))
+ printf(" %d", j);
+ putchar('\n');
+ p += 3;
+ } else {
+ printf("\t\tIRQ level = %d\n", CIS_IRQ_IRQN(*p));
+ p++;
+ }
+ return p;
+
+ err: /* warning */
+ printf("\tWrong length for IRQ sub-tuple\n");
+ return p;
+}
+
+/*
+ * Print memory map sub-tuple.
+ * call from dump_cis_config()
+ */
+static u_char *
+print_mem_map(u_char feat, u_char *p, u_char *q)
+{
+ int i, j;
+ u_char c;
+
+ switch (CIS_FEAT_MEMORY(feat)) {
+
+ case CIS_FEAT_MEM_NONE: /* No memory block */
+ break;
+ case CIS_FEAT_MEM_LEN: /* Specify memory length */
+ if (q - p < 2)
+ goto err;
+ printf("\tMemory space length = 0x%x\n", tpl16(p));
+ p += 2;
+ break;
+ case CIS_FEAT_MEM_ADDR: /* Memory address and length */
+ if (q - p < 4)
+ goto err;
+ printf("\tMemory space address = 0x%x, length = 0x%x\n",
+ tpl16(p + 2), tpl16(p));
+ p += 4;
+ break;
+ case CIS_FEAT_MEM_WIN: /* Memory descriptors. */
+ if (q <= p)
+ goto err;
+ c = *p++;
+ /* calculate byte length */
+ j = CIS_MEM_LENSZ(c) + CIS_MEM_ADDRSZ(c);
+ if (c & CIS_MEM_HOST)
+ j += CIS_MEM_ADDRSZ(c);
+ /* number of memory block */
+ for (i = 0; i < CIS_MEM_WINS(c); i++) {
+ if (q - p < j)
+ goto err;
+ printf("\tMemory descriptor %d\n\t\t", i + 1);
+ /* memory length */
+ p += print_num(CIS_MEM_LENSZ(c) | 0x10,
+ " blk length = 0x%x00", p, 0);
+ /* card address */
+ p += print_num(CIS_MEM_ADDRSZ(c) | 0x10,
+ " card addr = 0x%x00", p, 0);
+ if (c & CIS_MEM_HOST) /* Host address value exist */
+ p += print_num(CIS_MEM_ADDRSZ(c) | 0x10,
+ " host addr = 0x%x00", p, 0);
+ putchar('\n');
+ }
+ break;
+ }
+ return p;
+
+ err: /* warning */
+ printf("\tWrong length for memory mapping sub-tuple\n");
+ return p;
+}
+
+/*
+ * CIS_CONFIG : Dump a config entry.
+ * CIS_CONFIG_CB: Dump a configuration entry for CardBus
+ */
+static void
+dump_cis_config(struct tuple *tp)
+{
+ u_char *p, *q, feat;
+ int i, j;
+ char c;
+
+ p = tp->data;
+ q = p + tp->length;
+ printf("\tConfig index = 0x%x%s\n", *p & 0x3F,
+ *p & 0x40 ? "(default)" : "");
+
+ /* Interface byte exists */
+ if (tp->code == CIS_CONFIG && (*p & 0x80)) {
+ p++;
+ printf("\tInterface byte = 0x%x ", *p);
+ switch (*p & 0xF) { /* Interface type */
+ default:
+ printf("(reserved)");
+ break;
+ case 0:
+ printf("(memory)");
+ break;
+ case 1:
+ printf("(I/O)");
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ printf("(custom)");
+ break;
+ }
+ c = ' ';
+ if (*p & 0x10) { /* Battery voltage detect */
+ printf(" BVD1/2 active");
+ c = ',';
+ }
+ if (*p & 0x20) { /* Write protect active */
+ printf("%c card WP active", c); /* Write protect */
+ c = ',';
+ }
+ if (*p & 0x40) { /* RdyBsy active bit */
+ printf("%c +RDY/-BSY active", c);
+ c = ',';
+ }
+ if (*p & 0x80) /* Wait signal required */
+ printf("%c wait signal supported", c);
+ printf("\n");
+ }
+
+ /* features byte */
+ p++;
+ feat = *p++;
+
+ /* Power structure sub-tuple */
+ switch (CIS_FEAT_POWER(feat)) { /* Power sub-tuple(s) exists */
+ case 0:
+ break;
+ case 1:
+ printf("\tVcc pwr:\n");
+ p += print_pwr_desc(p);
+ break;
+ case 2:
+ printf("\tVcc pwr:\n");
+ p += print_pwr_desc(p);
+ printf("\tVpp pwr:\n");
+ p += print_pwr_desc(p);
+ break;
+ case 3:
+ printf("\tVcc pwr:\n");
+ p += print_pwr_desc(p);
+ printf("\tVpp1 pwr:\n");
+ p += print_pwr_desc(p);
+ printf("\tVpp2 pwr:\n");
+ p += print_pwr_desc(p);
+ break;
+ }
+
+ /* Timing sub-tuple */
+ if (tp->code == CIS_CONFIG &&
+ (feat & CIS_FEAT_TIMING)) { /* Timing sub-tuple exists */
+ i = *p++;
+ j = CIS_WAIT_SCALE(i);
+ if (j != 3) {
+ printf("\tWait scale ");
+ print_ext_speed(*p++, j);
+ printf("\n");
+ }
+ j = CIS_READY_SCALE(i);
+ if (j != 7) {
+ printf("\tRDY/BSY scale ");
+ print_ext_speed(*p++, j);
+ printf("\n");
+ }
+ j = CIS_RESERVED_SCALE(i);
+ if (j != 7) {
+ printf("\tExternal scale ");
+ print_ext_speed(*p++, j);
+ printf("\n");
+ }
+ }
+
+ /* I/O mapping sub-tuple */
+ if (feat & CIS_FEAT_I_O) { /* I/O space sub-tuple exists */
+ if (tp->code == CIS_CONFIG)
+ p = print_io_map(p, q);
+ else { /* CIS_CONFIG_CB */
+ printf("\tI/O base:");
+ for (i = 0; i < 8; i++)
+ if (*p & (1 << i))
+ printf(" %d", i);
+ putchar('\n');
+ p++;
+ }
+ }
+
+ /* IRQ descriptor sub-tuple */
+ if (feat & CIS_FEAT_IRQ) /* IRQ sub-tuple exists */
+ p = print_irq_map(p, q);
+
+ /* Memory map sub-tuple */
+ if (CIS_FEAT_MEMORY(feat)) { /* Memory space sub-tuple(s) exists */
+ if (tp->code == CIS_CONFIG)
+ p = print_mem_map(feat, p, q);
+ else { /* CIS_CONFIG_CB */
+ printf("\tMemory base:");
+ for (i = 0; i < 8; i++)
+ if (*p & (1 << i))
+ printf(" %d", i);
+ putchar('\n');
+ p++;
+ }
+ }
+
+ /* Misc sub-tuple */
+ if (feat & CIS_FEAT_MISC) { /* Miscellaneous sub-tuple exists */
+ if (tp->code == CIS_CONFIG) {
+ printf("\tMax twin cards = %d\n", *p & 7);
+ printf("\tMisc attr:%s%s%s",
+ (*p & 8) ? " (Audio-BVD2)" : "",
+ (*p & 0x10) ? " (Read-only)" : "",
+ (*p & 0x20) ? " (Power down supported)" : "");
+ if (*p++ & 0x80) {
+ printf(" (Ext byte = 0x%x)", *p);
+ p++;
+ }
+ putchar('\n');
+ }
+ else { /* CIS_CONFIG_CB */
+ printf("\tMisc attr:");
+ printf("%s%s%s%s%s%s%s",
+ (*p & 1) ? " (Master)" : "",
+ (*p & 2) ? " (Invalidate)" : "",
+ (*p & 4) ? " (VGA palette)" : "",
+ (*p & 8) ? " (Parity)" : "",
+ (*p & 0x10) ? " (Wait)" : "",
+ (*p & 0x20) ? " (Serr)" : "",
+ (*p & 0x40) ? " (Fast back)" : "");
+ if (*p++ & 0x80) {
+ printf("%s%s",
+ (*p & 1) ? " (Binary audio)" : "",
+ (*p & 2) ? " (pwm audio)" : "");
+ p++;
+ }
+ putchar('\n');
+ }
+ }
+}
+
+/*
+ * CIS_DEVICE_OC, CIS_DEVICE_OA:
+ * Dump other conditions for common/attribute memory
+ */
+static void
+dump_other_cond(u_char *p, int len)
+{
+ if (p[0] && len > 0) {
+ printf("\t");
+ if (p[0] & 1)
+ printf("(MWAIT)");
+ if (p[0] & 2)
+ printf(" (3V card)");
+ if (p[0] & 0x80)
+ printf(" (Extension bytes follow)");
+ printf("\n");
+ }
+}
+
+/*
+ * CIS_MEM_COMMON, CIS_MEM_ATTR:
+ * Common / Attribute memory descripter
+ */
+static void
+dump_device_desc(u_char *p, int len, const char *type)
+{
+ static const char *un_name[] =
+ {"512b", "2Kb", "8Kb", "32Kb", "128Kb", "512Kb", "2Mb", "reserved"};
+ static const char *speed[] =
+ {"No speed", "250nS", "200nS", "150nS",
+ "100nS", "Reserved", "Reserved"};
+ static const char *dev[] =
+ {"No device", "Mask ROM", "OTPROM", "UV EPROM",
+ "EEPROM", "FLASH EEPROM", "SRAM", "DRAM",
+ "Reserved", "Reserved", "Reserved", "Reserved",
+ "Reserved", "Function specific", "Extended",
+ "Reserved"};
+ int count = 0;
+
+ while (*p != 0xFF && len > 0) {
+ u_char x;
+
+ x = *p++;
+ len -= 2;
+ if (count++ == 0)
+ printf("\t%s memory device information:\n", type);
+ printf("\t\tDevice number %d, type %s, WPS = %s\n",
+ count, dev[x >> 4], (x & 0x8) ? "ON" : "OFF");
+ if ((x & 7) == 7) {
+ len--;
+ if (*p) {
+ printf("\t\t");
+ print_ext_speed(*p, 0);
+ while (*p & 0x80) {
+ p++;
+ len--;
+ }
+ }
+ p++;
+ } else
+ printf("\t\tSpeed = %s", speed[x & 7]);
+ printf(", Memory block size = %s, %d units\n",
+ un_name[*p & 7], (*p >> 3) + 1);
+ p++;
+ }
+}
+
+/*
+ * CIS_INFO_V1: Print version-1 info
+ */
+static void
+dump_info_v1(u_char *p, int len)
+{
+ if (len < 2) {
+ printf("\tWrong length for version-1 info tuple\n");
+ return;
+ }
+ printf("\tVersion = %d.%d", p[0], p[1]);
+ p += 2;
+ len -= 2;
+ if (len > 1 && *p != 0xff) {
+ printf(", Manuf = [%s]", p);
+ while (*p++ && --len > 0);
+ }
+ if (len > 1 && *p != 0xff) {
+ printf(", card vers = [%s]", p);
+ while (*p++ && --len > 0);
+ } else {
+ printf("\n\tWrong length for version-1 info tuple\n");
+ return;
+ }
+ putchar('\n');
+ if (len > 1 && *p != 0xff) {
+ printf("\tAddit. info = [%.*s]", len, p);
+ while (*p++ && --len > 0);
+ if (len > 1 && *p != 0xff)
+ printf(",[%.*s]", len, p);
+ putchar('\n');
+ }
+}
+
+/*
+ * CIS_FUNC_ID: Functional ID
+ */
+static void
+dump_func_id(u_char *p)
+{
+ static const char *id[] = {
+ "Multifunction card",
+ "Memory card",
+ "Serial port/modem",
+ "Parallel port",
+ "Fixed disk card",
+ "Video adapter",
+ "Network/LAN adapter",
+ "AIMS",
+ "SCSI card",
+ "Security"
+ };
+
+ printf("\t%s%s%s\n",
+ (*p <= 9) ? id[*p] : "Unknown function",
+ (p[1] & 1) ? " - POST initialize" : "",
+ (p[1] & 2) ? " - Card has ROM" : "");
+}
+
+/*
+ * CIS_FUNC_EXT: Dump functional extension tuple.
+ * (Serial port/modem)
+ */
+static void
+dump_serial_ext(u_char *p, int len)
+{
+ static const char *type[] = {
+ "", "Modem", "Data", "Fax", "Voice", "Data modem",
+ "Fax/modem", "Voice", " (Data)", " (Fax)", " (Voice)"
+ };
+
+ if (len < 1)
+ return;
+ switch (p[0]) {
+ case 0: /* Serial */
+ case 8: /* Data */
+ case 9: /* Fax */
+ case 10: /* Voice */
+ printf("\tSerial interface extension:%s\n", type[*p]);
+ if (len < 4)
+ goto err;
+ switch (p[1] & 0x1F) {
+ default:
+ printf("\t\tUnknown device");
+ break;
+ case 0:
+ printf("\t\t8250 UART");
+ break;
+ case 1:
+ printf("\t\t16450 UART");
+ break;
+ case 2:
+ printf("\t\t16550 UART");
+ break;
+ }
+ printf(", Parity - %s%s%s%s\n",
+ (p[2] & 1) ? "Space," : "",
+ (p[2] & 2) ? "Mark," : "",
+ (p[2] & 4) ? "Odd," : "",
+ (p[2] & 8) ? "Even" : "");
+ printf("\t\tData bit - %s%s%s%s Stop bit - %s%s%s\n",
+ (p[3] & 1) ? "5bit," : "",
+ (p[3] & 2) ? "6bit," : "",
+ (p[3] & 4) ? "7bit," : "",
+ (p[3] & 8) ? "8bit," : "",
+ (p[3] & 0x10) ? "1bit," : "",
+ (p[3] & 0x20) ? "1.5bit," : "",
+ (p[3] & 0x40) ? "2bit" : "");
+ break;
+ case 1: /* Serial */
+ case 5: /* Data */
+ case 6: /* Fax */
+ case 7: /* Voice */
+ printf("\t%s interface capabilities:\n", type[*p]);
+ if (len < 9)
+ goto err;
+ break;
+ case 2: /* Data */
+ printf("\tData modem services available:\n");
+ break;
+ case 0x13: /* Fax1 */
+ case 0x23: /* Fax2 */
+ case 0x33: /* Fax3 */
+ printf("\tFax%d/modem services available:\n", *p >> 4);
+ break;
+ case 0x84: /* Voice */
+ printf("\tVoice services available:\n");
+ break;
+ err: /* warning */
+ printf("\tWrong length for serial extension tuple\n");
+ return;
+ }
+}
+
+/*
+ * CIS_FUNC_EXT: Dump functional extension tuple.
+ * (Fixed disk card)
+ */
+static void
+dump_disk_ext(u_char *p, int len)
+{
+ if (len < 1)
+ return;
+ switch (p[0]) {
+ case 1: /* IDE interface */
+ if (len < 2)
+ goto err;
+ printf("\tDisk interface: %s\n",
+ (p[1] & 1) ? "IDE" : "Undefined");
+ break;
+ case 2: /* Master */
+ case 3: /* Slave */
+ if (len < 3)
+ goto err;
+ printf("\tDisk features: %s, %s%s\n",
+ (p[1] & 0x04) ? "Silicon" : "Rotating",
+ (p[1] & 0x08) ? "Unique, " : "",
+ (p[1] & 0x10) ? "Dual" : "Single");
+ if (p[2] & 0x7f)
+ printf("\t\t%s%s%s%s%s%s%s\n",
+ (p[2] & 0x01) ? "Sleep, " : "",
+ (p[2] & 0x02) ? "Standby, " : "",
+ (p[2] & 0x04) ? "Idle, " : "",
+ (p[2] & 0x08) ? "Low power, " : "",
+ (p[2] & 0x10) ? "Reg inhibit, " : "",
+ (p[2] & 0x20) ? "Index, " : "",
+ (p[2] & 0x40) ? "Iois16" : "");
+ break;
+ err: /* warning */
+ printf("\tWrong length for fixed disk extension tuple\n");
+ return;
+ }
+}
+
+static void
+print_speed(u_int i)
+{
+ if (i < 1000)
+ printf("%u bits/sec", i);
+ else if (i < 1000000)
+ printf("%u kb/sec", i / 1000);
+ else
+ printf("%u Mb/sec", i / 1000000);
+}
+
+/*
+ * CIS_FUNC_EXT: Dump functional extension tuple.
+ * (Network/LAN adapter)
+ */
+static void
+dump_network_ext(u_char *p, int len)
+{
+ static const char *tech[] = {
+ "Undefined", "ARCnet", "Ethernet", "Token ring",
+ "Localtalk", "FDDI/CDDI", "ATM", "Wireless"
+ };
+ static const char *media[] = {
+ "Undefined", "UTP", "STP", "Thin coax",
+ "THICK coax", "Fiber", "900 MHz", "2.4 GHz",
+ "5.4 GHz", "Diffuse Infrared", "Point to point Infrared"
+ };
+ u_int i = 0;
+
+ if (len < 1)
+ return;
+ switch (p[0]) {
+ case 1: /* Network technology */
+ if (len < 2)
+ goto err;
+ printf("\tNetwork technology: %s\n", tech[p[1] & 7]);
+ break;
+ case 2: /* Network speed */
+ if (len < 5)
+ goto err;
+ printf("\tNetwork speed: ");
+ print_speed(tpl32(p + 1));
+ putchar('\n');
+ break;
+ case 3: /* Network media */
+ if (len < 2)
+ goto err;
+ if (p[1] <= 10)
+ i = p[1];
+ printf("\tNetwork media: %s\n", media[i]);
+ break;
+ case 4: /* Node ID */
+ if (len <= 2 || len < p[1] + 2)
+ goto err;
+ printf("\tNetwork node ID:");
+ for (i = 0; i < p[1]; i++)
+ printf(" %02x", p[i + 2]);
+ putchar('\n');
+ break;
+ case 5: /* Connecter type */
+ if (len < 2)
+ goto err;
+ printf("\tNetwork connector: %s connector standard\n",
+ (p[1] == 0) ? "open" : "closed");
+ break;
+ err: /* warning */
+ printf("\tWrong length for network extension tuple\n");
+ return;
+ }
+}
+
+/*
+ * CIS_LONGLINK_MFC: Long link to next chain for Multi function card
+ */
+static void
+dump_longlink_mfc(u_char *p, int len)
+{
+ u_int i, n = *p++;
+
+ --len;
+ for (i = 0; i < n; i++) {
+ if (len < 5) {
+ printf("\tWrong length for long link MFC tuple\n");
+ return;
+ }
+ printf("\tFunction %d: %s memory, address 0x%x\n",
+ i, (*p ? "common" : "attribute"), tpl32(p + 1));
+ p += 5;
+ len -= 5;
+ }
+}
+
+/*
+ * CIS_DEVICEGEO, CIS_DEVICEGEO_A:
+ * Geometry info for common/attribute memory
+ */
+static void
+dump_device_geo(u_char *p, int len)
+{
+ while (len >= 6) {
+ printf("\twidth = %d, erase = 0x%x, read = 0x%x, write = 0x%x\n"
+ "\t\tpartition = 0x%x, interleave = 0x%x\n",
+ p[0], 1 << (p[1] - 1),
+ 1 << (p[2] - 1), 1 << (p[3] - 1),
+ 1 << (p[4] - 1), 1 << (p[5] - 1));
+ len -= 6;
+ }
+}
+
+/*
+ * CIS_INFO_V2: Print version-2 info
+ */
+static void
+dump_info_v2(u_char *p, int len)
+{
+ if (len < 9) {
+ printf("\tWrong length for version-2 info tuple\n");
+ return;
+ }
+ printf("\tVersion = 0x%x, compliance = 0x%x, dindex = 0x%x\n",
+ p[0], p[1], tpl16(p + 2));
+ printf("\tVspec8 = 0x%x, vspec9 = 0x%x, nhdr = %d\n",
+ p[6], p[7], p[8]);
+ p += 9;
+ len -= 9;
+ if (len <= 1 || *p == 0xff)
+ return;
+ printf("\tVendor = [%.*s]", len, p);
+ while (*p++ && --len > 0);
+ if (len > 1 && *p != 0xff)
+ printf(", info = [%.*s]", len, p);
+ putchar('\n');
+}
+
+/*
+ * CIS_ORG: Organization
+ */
+static void
+dump_org(u_char *p, int len)
+{
+ if (len < 1) {
+ printf("\tWrong length for organization tuple\n");
+ return;
+ }
+ switch (*p) {
+ case 0:
+ printf("\tFilesystem");
+ break;
+ case 1:
+ printf("\tApp specific");
+ break;
+ case 2:
+ printf("\tCode");
+ break;
+ default:
+ if (*p < 0x80)
+ printf("\tReserved");
+ else
+ printf("\tVendor specific");
+ break;
+ }
+ printf(" [%.*s]\n", len - 1, p + 1);
+}
+
+static void
+print_size(u_int i)
+{
+ if (i < 1024)
+ printf("%ubits", i);
+ else if (i < 1024*1024)
+ printf("%ukb", i / 1024);
+ else
+ printf("%uMb", i / (1024*1024));
+}
+
+/*
+ * CIS_BAR: Base address register for CardBus
+ */
+static void
+dump_bar(u_char *p, int len)
+{
+ if (len < 6) {
+ printf("\tWrong length for BAR tuple\n");
+ return;
+ }
+ printf("\tBAR %d: size = ", *p & 7);
+ print_size(tpl32(p + 2));
+ printf(", %s%s%s%s\n",
+ (*p & 0x10) ? "I/O" : "Memory",
+ (*p & 0x20) ? ", Prefetch" : "",
+ (*p & 0x40) ? ", Cacheable" : "",
+ (*p & 0x80) ? ", <1Mb" : "");
+}
diff --git a/usr.sbin/dumpcis/readcis.c b/usr.sbin/dumpcis/readcis.c
new file mode 100644
index 0000000..25bd396
--- /dev/null
+++ b/usr.sbin/dumpcis/readcis.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 */
+
+/*
+ * Code cleanup, bug-fix and extension
+ * by Tatsumi Hosokawa <hosokawa@mt.cs.keio.ac.jp>
+ */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "cardinfo.h"
+#include "cis.h"
+#include "readcis.h"
+
+static int ck_linktarget(int, off_t, int);
+static struct tuple_list *read_one_tuplelist(int, int, off_t);
+static struct tuple_list *read_tuples(int);
+static struct tuple *find_tuple_in_list(struct tuple_list *, unsigned char);
+static struct tuple_info *get_tuple_info(unsigned char);
+
+static struct tuple_info tuple_info[] = {
+ {"Null tuple", CIS_NULL, 0},
+ {"Common memory descriptor", CIS_MEM_COMMON, 255},
+ {"Long link to next chain for CardBus", CIS_LONGLINK_CB, 255},
+ {"Indirect access", CIS_INDIRECT, 255},
+ {"Configuration map for CardBus", CIS_CONF_MAP_CB, 255},
+ {"Configuration entry for CardBus", CIS_CONFIG_CB, 255},
+ {"Long link to next chain for MFC", CIS_LONGLINK_MFC, 255},
+ {"Base address register for CardBus", CIS_BAR, 6},
+ {"Checksum", CIS_CHECKSUM, 5},
+ {"Long link to attribute memory", CIS_LONGLINK_A, 4},
+ {"Long link to common memory", CIS_LONGLINK_C, 4},
+ {"Link target", CIS_LINKTARGET, 3},
+ {"No link", CIS_NOLINK, 0},
+ {"Version 1 info", CIS_INFO_V1, 255},
+ {"Alternate language string", CIS_ALTSTR, 255},
+ {"Attribute memory descriptor", CIS_MEM_ATTR, 255},
+ {"JEDEC descr for common memory", CIS_JEDEC_C, 255},
+ {"JEDEC descr for attribute memory", CIS_JEDEC_A, 255},
+ {"Configuration map", CIS_CONF_MAP, 255},
+ {"Configuration entry", CIS_CONFIG, 255},
+ {"Other conditions for common memory", CIS_DEVICE_OC, 255},
+ {"Other conditions for attribute memory", CIS_DEVICE_OA, 255},
+ {"Geometry info for common memory", CIS_DEVICEGEO, 255},
+ {"Geometry info for attribute memory", CIS_DEVICEGEO_A, 255},
+ {"Manufacturer ID", CIS_MANUF_ID, 4},
+ {"Functional ID", CIS_FUNC_ID, 2},
+ {"Functional EXT", CIS_FUNC_EXT, 255},
+ {"Software interleave", CIS_SW_INTERLV, 2},
+ {"Version 2 Info", CIS_VERS_2, 255},
+ {"Data format", CIS_FORMAT, 255},
+ {"Geometry", CIS_GEOMETRY, 4},
+ {"Byte order", CIS_BYTEORDER, 2},
+ {"Card init date", CIS_DATE, 4},
+ {"Battery replacement", CIS_BATTERY, 4},
+ {"Organization", CIS_ORG, 255},
+ {"Terminator", CIS_END, 0},
+ {0, 0, 0}
+};
+
+static void *
+xmalloc(int sz)
+{
+ void *p;
+
+ sz = (sz + 7) & ~7;
+ p = malloc(sz);
+ if (p)
+ bzero(p, sz);
+ else
+ errx(1, "malloc");
+ return (p);
+}
+
+/*
+ * After reading the tuples, decode the relevant ones.
+ */
+struct tuple_list *
+readcis(int fd)
+{
+
+ return (read_tuples(fd));
+}
+
+/*
+ * free_cis - delete cis entry.
+ */
+void
+freecis(struct tuple_list *tlist)
+{
+ struct tuple_list *tl;
+ struct tuple *tp;
+
+ while ((tl = tlist) != 0) {
+ tlist = tl->next;
+ while ((tp = tl->tuples) != 0) {
+ tl->tuples = tp->next;
+ free(tp->data);
+ free(tp);
+ }
+ free(tl);
+ }
+}
+
+/*
+ * Parse variable length value.
+ */
+u_int
+parse_num(int sz, u_char *p, u_char **q, int ofs)
+{
+ u_int num = 0;
+
+ switch (sz) {
+ case 0:
+ case 0x10:
+ break;
+ case 1:
+ case 0x11:
+ num = (*p++) + ofs;
+ break;
+ case 2:
+ case 0x12:
+ num = tpl16(p) + ofs;
+ p += 2;
+ break;
+ case 0x13:
+ num = tpl24(p) + ofs;
+ p += 3;
+ break;
+ case 3:
+ case 0x14:
+ num = tpl32(p) + ofs;
+ p += 4;
+ break;
+ }
+ if (q)
+ *q = p;
+ return num;
+}
+
+/*
+ * Read the tuples from the card.
+ * The processing of tuples is as follows:
+ * - Read tuples at attribute memory, offset 0.
+ * - If a CIS_END is the first tuple, look for
+ * a tuple list at common memory offset 0; this list
+ * must start with a LINKTARGET.
+ * - If a long link tuple was encountered, execute the long
+ * link.
+ * - If a no-link tuple was seen, terminate processing.
+ * - If no no-link tuple exists, and no long link tuple
+ * exists while processing the primary tuple list,
+ * then look for a LINKTARGET tuple in common memory.
+ * - If a long link tuple is found in any list, then process
+ * it. Only one link is allowed per list.
+ */
+static struct tuple_list *tlist;
+
+static struct tuple_list *
+read_tuples(int fd)
+{
+ struct tuple_list *tl = 0, *last_tl;
+ struct tuple *tp;
+ int flag;
+ off_t offs;
+
+ tlist = 0;
+ last_tl = tlist = read_one_tuplelist(fd, MDF_ATTR, (off_t) 0);
+
+ /* Now start processing the links (if any). */
+ do {
+ flag = MDF_ATTR;
+ tp = find_tuple_in_list(last_tl, CIS_LONGLINK_A);
+ if (tp == 0) {
+ flag = 0;
+ tp = find_tuple_in_list(last_tl, CIS_LONGLINK_C);
+ }
+ if (tp && tp->length == 4) {
+ offs = tpl32(tp->data);
+#ifdef DEBUG
+ printf("Checking long link at %zd (%s memory)\n",
+ offs, flag ? "Attribute" : "Common");
+#endif
+ /* If a link was found, read the tuple list from it. */
+ if (ck_linktarget(fd, offs, flag)) {
+ tl = read_one_tuplelist(fd, flag, offs);
+ last_tl->next = tl;
+ last_tl = tl;
+ }
+ } else
+ tl = 0;
+ } while (tl);
+
+ /*
+ * If the primary list had no NOLINK tuple, and no LINKTARGET,
+ * then try to read a tuple list at common memory (offset 0).
+ */
+ if (find_tuple_in_list(tlist, CIS_NOLINK) == 0 &&
+ find_tuple_in_list(tlist, CIS_LINKTARGET) == 0 &&
+ ck_linktarget(fd, (off_t) 0, 0)) {
+ offs = 0;
+#ifdef DEBUG
+ printf("Reading long link at %zd (%s memory)\n",
+ offs, flag ? "Attribute" : "Common");
+#endif
+ tlist->next = read_one_tuplelist(fd, 0, offs);
+ }
+ return (tlist);
+}
+
+/*
+ * Read one tuple list from the card.
+ */
+static struct tuple_list *
+read_one_tuplelist(int fd, int flags, off_t offs)
+{
+ struct tuple *tp, *last_tp = 0;
+ struct tuple_list *tl;
+ struct tuple_info *tinfo;
+ int total = 0;
+ unsigned char code, length;
+
+ /* Check to see if this memory has already been scanned. */
+ for (tl = tlist; tl; tl = tl->next)
+ if (tl->offs == offs && tl->flags == (flags & MDF_ATTR))
+ return (0);
+ tl = xmalloc(sizeof(*tl));
+ tl->offs = offs;
+ tl->flags = flags & MDF_ATTR;
+ ioctl(fd, PIOCRWFLAG, &flags);
+ lseek(fd, offs, SEEK_SET);
+ do {
+ if (read(fd, &code, 1) != 1) {
+ warn("CIS code read");
+ break;
+ }
+ total++;
+ if (code == CIS_NULL)
+ continue;
+ tp = xmalloc(sizeof(*tp));
+ tp->code = code;
+ if (code == CIS_END)
+ length = 0;
+ else {
+ if (read(fd, &length, 1) != 1) {
+ warn("CIS len read");
+ break;
+ }
+ total++;
+ }
+ tp->length = length;
+#ifdef DEBUG
+ printf("Tuple code = 0x%x, len = %d\n", code, length);
+#endif
+ if (length == 0xFF) {
+ length = tp->length = 0;
+ code = CIS_END;
+ }
+ if (length != 0) {
+ total += length;
+ tp->data = xmalloc(length);
+ if (read(fd, tp->data, length) != length) {
+ warn("CIS read");
+ break;
+ }
+ }
+
+ /*
+ * Check the tuple, and ignore it if it isn't in the table
+ * or the length is illegal.
+ */
+ tinfo = get_tuple_info(code);
+ if (tinfo != NULL && (tinfo->length != 255 && tinfo->length > length)) {
+ printf("code %s (%d) ignored\n", tuple_name(code), code);
+ tp->code = CIS_NULL;
+ }
+ if (tl->tuples == NULL)
+ tl->tuples = tp;
+ else
+ last_tp->next = tp;
+ last_tp = tp;
+ } while (code != CIS_END && total < 1024);
+ return (tl);
+}
+
+/*
+ * return true if the offset points to a LINKTARGET tuple.
+ */
+static int
+ck_linktarget(int fd, off_t offs, int flag)
+{
+ char blk[5];
+
+ ioctl(fd, PIOCRWFLAG, &flag);
+ lseek(fd, offs, SEEK_SET);
+ if (read(fd, blk, 5) != 5)
+ return (0);
+ if (blk[0] == CIS_LINKTARGET &&
+ blk[1] == 0x3 &&
+ blk[2] == 'C' &&
+ blk[3] == 'I' &&
+ blk[4] == 'S')
+ return (1);
+ return (0);
+}
+
+/*
+ * find_tuple_in_list - find a tuple within a
+ * single tuple list.
+ */
+static struct tuple *
+find_tuple_in_list(struct tuple_list *tl, unsigned char code)
+{
+ struct tuple *tp;
+
+ for (tp = tl->tuples; tp; tp = tp->next)
+ if (tp->code == code)
+ break;
+ return (tp);
+}
+
+/*
+ * return table entry for code.
+ */
+static struct tuple_info *
+get_tuple_info(unsigned char code)
+{
+ struct tuple_info *tp;
+
+ for (tp = tuple_info; tp->name; tp++)
+ if (tp->code == code)
+ return (tp);
+ return (0);
+}
+
+const char *
+tuple_name(unsigned char code)
+{
+ struct tuple_info *tp;
+
+ tp = get_tuple_info(code);
+ if (tp)
+ return (tp->name);
+ return ("Unknown");
+}
diff --git a/usr.sbin/dumpcis/readcis.h b/usr.sbin/dumpcis/readcis.h
new file mode 100644
index 0000000..8fc2e2d
--- /dev/null
+++ b/usr.sbin/dumpcis/readcis.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+struct tuple {
+ struct tuple *next;
+ unsigned char code;
+ int length;
+ unsigned char *data;
+};
+
+struct tuple_list {
+ struct tuple_list *next;
+ struct tuple *tuples;
+ off_t offs;
+ int flags;
+};
+
+struct tuple_info {
+ const char *name;
+ unsigned char code;
+ unsigned char length; /* 255 means variable length */
+};
+
+#define tpl32(tp) ((*((tp) + 3) << 24) | \
+ (*((tp) + 2) << 16) | \
+ (*((tp) + 1) << 8) | *(tp))
+#define tpl24(tp) ((*((tp) + 2) << 16) | \
+ (*((tp) + 1) << 8) | *(tp))
+#define tpl16(tp) ((*((tp) + 1) << 8) | *(tp))
+
+void dumpcis(struct tuple_list *);
+void freecis(struct tuple_list *);
+struct tuple_list *readcis(int);
+
+const char *tuple_name(unsigned char);
+u_int parse_num(int, u_char *, u_char **, int);
diff --git a/usr.sbin/editmap/Makefile b/usr.sbin/editmap/Makefile
new file mode 100644
index 0000000..93e3e07
--- /dev/null
+++ b/usr.sbin/editmap/Makefile
@@ -0,0 +1,37 @@
+# $FreeBSD$
+
+SENDMAIL_DIR=${.CURDIR}/../../contrib/sendmail
+.PATH: ${SENDMAIL_DIR}/editmap
+
+PROG= editmap
+SRCS= editmap.c
+MAN= editmap.8
+
+CFLAGS+= -I${SENDMAIL_DIR}/src -I${SENDMAIL_DIR}/include -I.
+CFLAGS+= -DNEWDB -DNOT_SENDMAIL
+
+LIBSMDIR= ${.OBJDIR}/../../lib/libsm
+LIBSM= ${LIBSMDIR}/libsm.a
+
+LIBSMDBDIR= ${.OBJDIR}/../../lib/libsmdb
+LIBSMDB= ${LIBSMDBDIR}/libsmdb.a
+
+LIBSMUTILDIR= ${.OBJDIR}/../../lib/libsmutil
+LIBSMUTIL= ${LIBSMUTILDIR}/libsmutil.a
+
+DPADD= ${LIBSMDB} ${LIBSMUTIL} ${LIBSM}
+LDADD= ${LIBSMDB} ${LIBSMUTIL} ${LIBSM}
+
+SRCS+= sm_os.h
+CLEANFILES+=sm_os.h
+
+# User customizations to the sendmail build environment
+CFLAGS+=${SENDMAIL_CFLAGS}
+DPADD+=${SENDMAIL_DPADD}
+LDADD+=${SENDMAIL_LDADD}
+LDFLAGS+=${SENDMAIL_LDFLAGS}
+
+sm_os.h:
+ ln -sf ${SENDMAIL_DIR}/include/sm/os/sm_os_freebsd.h sm_os.h
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/edquota/Makefile b/usr.sbin/edquota/Makefile
new file mode 100644
index 0000000..e5a3b12
--- /dev/null
+++ b/usr.sbin/edquota/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= edquota
+MAN= edquota.8
+
+WARNS?= 4
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/edquota/edquota.8 b/usr.sbin/edquota/edquota.8
new file mode 100644
index 0000000..7cf72dd
--- /dev/null
+++ b/usr.sbin/edquota/edquota.8
@@ -0,0 +1,246 @@
+.\" Copyright (c) 1983, 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.
+.\"
+.\" @(#)edquota.8 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\"
+.Dd June 6, 1993
+.Dt EDQUOTA 8
+.Os
+.Sh NAME
+.Nm edquota
+.Nd edit user quotas
+.Sh SYNOPSIS
+.Nm
+.Op Fl u
+.Op Fl f Ar fspath
+.Op Fl p Ar proto-username
+.Ar username ...
+.Nm
+.Op Fl u
+.Fl e
+.Sm off
+.Ar fspath Op : Ar bslim Op : Ar bhlim Op : Ar islim Op : Ar ihlim
+.Sm on
+.Op Fl e Ar ...
+.Ar username ...
+.Nm
+.Fl g
+.Op Fl f Ar fspath
+.Op Fl p Ar proto-groupname
+.Ar groupname ...
+.Nm
+.Fl g
+.Fl e
+.Sm off
+.Ar fspath Op : Ar bslim Op : Ar bhlim Op : Ar islim Op : Ar ihlim
+.Sm on
+.Op Fl e Ar ...
+.Ar groupname ...
+.Nm
+.Fl t
+.Op Fl u
+.Op Fl f Ar fspath
+.Nm
+.Fl t
+.Fl g
+.Op Fl f Ar fspath
+.Sh DESCRIPTION
+The
+.Nm
+utility is a quota editor.
+By default, or if the
+.Fl u
+flag is specified,
+one or more users may be specified on the command line.
+For each user a temporary file is created
+with an
+.Tn ASCII
+representation of the current
+disk quotas for that user.
+The list of file systems with user quotas is determined from
+.Pa /etc/fstab .
+An editor is invoked on the
+.Tn ASCII
+file.
+The editor invoked is
+.Xr vi 1
+unless the environment variable
+.Ev EDITOR
+specifies otherwise.
+.Pp
+The quotas may then be modified, new quotas added, etc.
+Setting a quota to zero indicates that no quota should be imposed.
+Setting a hard limit to one indicates that no allocations should
+be permitted.
+Setting a soft limit to one with a hard limit of zero
+indicates that allocations should be permitted only on
+a temporary basis (see
+.Fl t
+below).
+The current usage information in the file is for informational purposes;
+only the hard and soft limits can be changed.
+.Pp
+On leaving the editor,
+.Nm
+reads the temporary file and modifies the binary
+quota files to reflect the changes made.
+.Pp
+If the
+.Fl p
+option is specified,
+.Nm
+will duplicate the quotas of the prototypical user
+specified for each user specified.
+This is the normal mechanism used to
+initialize quotas for groups of users.
+If the user given to assign quotas to is a numerical uid
+range (e.g.\& 1000-2000), then
+.Nm
+will duplicate the quotas of the prototypical user
+for each uid in the range specified.
+This allows
+for easy setup of default quotas for a group of users.
+The uids in question do not have to be currently assigned in
+.Pa /etc/passwd .
+.Pp
+If one or more
+.Fl e
+.Sm off
+.Ar fspath Op : Ar bslim Op : Ar bhlim Op : Ar islim Op : Ar ihlim
+.Sm on
+options are specified,
+.Nm
+will non-interactively set quotas defined by
+.Ar bslim , bhlim , islim ,
+and
+.Ar ihlim
+on each particular file system referenced by
+.Ar fspath .
+Here
+.Ar bslim
+is the soft limit on the number of blocks,
+.Ar bhlim
+is the hard limit on the number of blocks,
+.Ar islim
+is the soft limit on the number of files, and
+.Ar ihlim
+is the hard limit on the number of files.
+If any of the
+.Ar bslim , bhlim , islim ,
+and
+.Ar ihlim
+values is omitted, it is assumed to be zero, therefore
+indicating that no particular quota should be imposed.
+.Pp
+If invoked with the
+.Fl f
+option,
+.Nm
+will read and modify quotas on the file system specified by
+.Ar fspath
+only.
+The
+.Ar fspath
+argument may be either a special device
+or a file system mount point.
+The primary purpose of this option is to set the scope for the
+.Fl p
+option, which would overwrite quota records on every
+file system with quotas otherwise.
+.Pp
+If the
+.Fl g
+flag is specified,
+.Nm
+is invoked to edit the quotas of
+one or more groups specified on the command line.
+The
+.Fl p
+flag can be specified in conjunction with
+the
+.Fl g
+flag to specify a prototypical group
+to be duplicated among the listed set of groups.
+Similarly,
+.Fl e
+flag can be specified in conjunction with
+the
+.Fl g
+flag to non-interactively set-up quotas on the listed set
+of groups.
+.Pp
+Users are permitted to exceed their soft limits
+for a grace period that may be specified per file system.
+Once the grace period has expired,
+the soft limit is enforced as a hard limit.
+The default grace period for a file system is specified in
+.In ufs/ufs/quota.h .
+The
+.Fl t
+flag can be used to change the grace period.
+By default, or when invoked with the
+.Fl u
+flag,
+the grace period is set for all the file systems with user
+quotas specified in
+.Pa /etc/fstab .
+When invoked with the
+.Fl g
+flag the grace period is
+set for all the file systems with group quotas specified in
+.Pa /etc/fstab .
+The grace period may be specified in days, hours, minutes, or seconds.
+Setting a grace period to zero indicates that the default
+grace period should be imposed.
+Setting a grace period to one second indicates that no
+grace period should be granted.
+Quotas must be turned off for the file system and
+then turned back on for the new grace period to take effect.
+.Pp
+Only the super-user may edit quotas.
+.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
+to find file system names and locations
+.El
+.Sh DIAGNOSTICS
+Various messages about inaccessible files; self-explanatory.
+.Sh SEE ALSO
+.Xr quota 1 ,
+.Xr quotactl 2 ,
+.Xr fstab 5 ,
+.Xr quotacheck 8 ,
+.Xr quotaon 8 ,
+.Xr repquota 8
diff --git a/usr.sbin/edquota/edquota.c b/usr.sbin/edquota/edquota.c
new file mode 100644
index 0000000..f38fa77
--- /dev/null
+++ b/usr.sbin/edquota/edquota.c
@@ -0,0 +1,890 @@
+/*
+ * 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[] = "@(#)edquota.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Disk quota editor.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+#include <ufs/ufs/quota.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fstab.h>
+#include <grp.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "pathnames.h"
+
+const char *qfname = QUOTAFILENAME;
+const char *qfextension[] = INITQFNAMES;
+const char *quotagroup = QUOTAGROUP;
+char tmpfil[] = _PATH_TMP;
+
+struct quotause {
+ struct quotause *next;
+ long flags;
+ struct dqblk dqblk;
+ char fsname[MAXPATHLEN + 1];
+ char qfname[1]; /* actually longer */
+};
+#define FOUND 0x01
+
+int alldigits(const char *s);
+int cvtatos(time_t, char *, time_t *);
+char *cvtstoa(time_t);
+int editit(char *);
+void freeprivs(struct quotause *);
+int getentry(const char *, int);
+struct quotause *getprivs(long, int, char *);
+int hasquota(struct fstab *, int, char **);
+void putprivs(long, int, struct quotause *);
+int readprivs(struct quotause *, char *);
+int readtimes(struct quotause *, char *);
+static void usage(void);
+int writetimes(struct quotause *, int, int);
+int writeprivs(struct quotause *, int, char *, int);
+
+int
+main(int argc, char *argv[])
+{
+ struct quotause *qup, *protoprivs, *curprivs;
+ long id, protoid;
+ long long lim;
+ int i, quotatype, range, tmpfd;
+ uid_t startuid, enduid;
+ u_int32_t *limp;
+ char *protoname, *cp, *oldoptarg;
+ int eflag = 0, tflag = 0, pflag = 0, ch;
+ char *fspath = NULL;
+ char buf[MAXLOGNAME];
+
+ if (argc < 2)
+ usage();
+ if (getuid())
+ errx(1, "permission denied");
+ quotatype = USRQUOTA;
+ protoprivs = NULL;
+ curprivs = NULL;
+ protoname = NULL;
+ while ((ch = getopt(argc, argv, "ugtf:p:e:")) != -1) {
+ switch(ch) {
+ case 'f':
+ fspath = optarg;
+ break;
+ case 'p':
+ protoname = optarg;
+ pflag++;
+ break;
+ case 'g':
+ quotatype = GRPQUOTA;
+ break;
+ case 'u':
+ quotatype = USRQUOTA;
+ break;
+ case 't':
+ tflag++;
+ break;
+ case 'e':
+ if ((qup = malloc(sizeof(*qup))) == NULL)
+ errx(2, "out of memory");
+ bzero(qup, sizeof(*qup));
+ i = 0;
+ oldoptarg = optarg;
+ for (cp = optarg; (cp = strsep(&optarg, ":")) != NULL;
+ i++) {
+ if (cp != oldoptarg)
+ *(cp - 1) = ':';
+ limp = NULL;
+ switch (i) {
+ case 0:
+ strlcpy(qup->fsname, cp,
+ sizeof(qup->fsname));
+ break;
+ case 1:
+ limp = &qup->dqblk.dqb_bsoftlimit;
+ break;
+ case 2:
+ limp = &qup->dqblk.dqb_bhardlimit;
+ break;
+ case 3:
+ limp = &qup->dqblk.dqb_isoftlimit;
+ break;
+ case 4:
+ limp = &qup->dqblk.dqb_ihardlimit;
+ break;
+ default:
+ warnx("incorrect quota specification: "
+ "%s", oldoptarg);
+ usage();
+ break; /* XXX: report an error */
+ }
+ if (limp != NULL) {
+ lim = strtoll(cp, NULL, 10);
+ if (lim < 0 || lim > UINT_MAX)
+ errx(1, "invalid limit value: "
+ "%lld", lim);
+ *limp = (u_int32_t)lim;
+ }
+ }
+ qup->dqblk.dqb_bsoftlimit =
+ btodb((off_t)qup->dqblk.dqb_bsoftlimit * 1024);
+ qup->dqblk.dqb_bhardlimit =
+ btodb((off_t)qup->dqblk.dqb_bhardlimit * 1024);
+ if (protoprivs == NULL) {
+ protoprivs = curprivs = qup;
+ } else {
+ curprivs->next = qup;
+ curprivs = qup;
+ }
+ eflag++;
+ pflag++;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (pflag) {
+ if (protoprivs == NULL) {
+ if ((protoid = getentry(protoname, quotatype)) == -1)
+ exit(1);
+ protoprivs = getprivs(protoid, quotatype, fspath);
+ for (qup = protoprivs; qup; qup = qup->next) {
+ qup->dqblk.dqb_btime = 0;
+ qup->dqblk.dqb_itime = 0;
+ }
+ }
+ for (; argc-- > 0; argv++) {
+ if (strspn(*argv, "0123456789-") == strlen(*argv) &&
+ (cp = strchr(*argv, '-')) != NULL) {
+ *cp++ = '\0';
+ startuid = atoi(*argv);
+ enduid = atoi(cp);
+ if (enduid < startuid)
+ errx(1,
+ "ending uid (%d) must be >= starting uid (%d) when using uid ranges",
+ enduid, startuid);
+ range = 1;
+ } else {
+ startuid = enduid = 0;
+ range = 0;
+ }
+ for ( ; startuid <= enduid; startuid++) {
+ if (range)
+ snprintf(buf, sizeof(buf), "%d",
+ startuid);
+ else
+ snprintf(buf, sizeof(buf), "%s",
+ *argv);
+ if ((id = getentry(buf, quotatype)) < 0)
+ continue;
+ if (eflag) {
+ for (qup = protoprivs; qup;
+ qup = qup->next) {
+ curprivs = getprivs(id,
+ quotatype, qup->fsname);
+ if (curprivs == NULL)
+ continue;
+ strcpy(qup->qfname,
+ curprivs->qfname);
+ strcpy(qup->fsname,
+ curprivs->fsname);
+ }
+ }
+ putprivs(id, quotatype, protoprivs);
+ }
+ }
+ exit(0);
+ }
+ tmpfd = mkstemp(tmpfil);
+ fchown(tmpfd, getuid(), getgid());
+ if (tflag) {
+ protoprivs = getprivs(0, quotatype, fspath);
+ if (writetimes(protoprivs, tmpfd, quotatype) == 0)
+ exit(1);
+ if (editit(tmpfil) && readtimes(protoprivs, tmpfil))
+ putprivs(0L, quotatype, protoprivs);
+ freeprivs(protoprivs);
+ close(tmpfd);
+ unlink(tmpfil);
+ exit(0);
+ }
+ for ( ; argc > 0; argc--, argv++) {
+ if ((id = getentry(*argv, quotatype)) == -1)
+ continue;
+ curprivs = getprivs(id, quotatype, fspath);
+ if (writeprivs(curprivs, tmpfd, *argv, quotatype) == 0)
+ continue;
+ if (editit(tmpfil) && readprivs(curprivs, tmpfil))
+ putprivs(id, quotatype, curprivs);
+ freeprivs(curprivs);
+ }
+ close(tmpfd);
+ unlink(tmpfil);
+ exit(0);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
+ "usage: edquota [-u] [-f fspath] [-p username] username ...",
+ " edquota [-u] -e fspath[:bslim[:bhlim[:islim[:ihlim]]]] [-e ...]",
+ " username ...",
+ " edquota -g [-f fspath] [-p groupname] groupname ...",
+ " edquota -g -e fspath[:bslim[:bhlim[:islim[:ihlim]]]] [-e ...]",
+ " groupname ...",
+ " edquota [-u] -t [-f fspath]",
+ " edquota -g -t [-f fspath]");
+ exit(1);
+}
+
+/*
+ * This routine converts a name for a particular quota type to
+ * an identifier. This routine must agree with the kernel routine
+ * getinoquota as to the interpretation of quota types.
+ */
+int
+getentry(const char *name, int quotatype)
+{
+ struct passwd *pw;
+ struct group *gr;
+
+ if (alldigits(name))
+ return (atoi(name));
+ switch(quotatype) {
+ case USRQUOTA:
+ if ((pw = getpwnam(name)))
+ return (pw->pw_uid);
+ warnx("%s: no such user", name);
+ break;
+ case GRPQUOTA:
+ if ((gr = getgrnam(name)))
+ return (gr->gr_gid);
+ warnx("%s: no such group", name);
+ break;
+ default:
+ warnx("%d: unknown quota type", quotatype);
+ break;
+ }
+ sleep(1);
+ return (-1);
+}
+
+/*
+ * Collect the requested quota information.
+ */
+struct quotause *
+getprivs(long id, int quotatype, char *fspath)
+{
+ struct fstab *fs;
+ struct quotause *qup, *quptail;
+ struct quotause *quphead;
+ int qcmd, qupsize, fd;
+ char *qfpathname;
+ static int warned = 0;
+
+ setfsent();
+ quphead = quptail = NULL;
+ qcmd = QCMD(Q_GETQUOTA, quotatype);
+ while ((fs = getfsent())) {
+ if (fspath && *fspath && strcmp(fspath, fs->fs_spec) &&
+ strcmp(fspath, fs->fs_file))
+ continue;
+ if (strcmp(fs->fs_vfstype, "ufs"))
+ continue;
+ if (!hasquota(fs, quotatype, &qfpathname))
+ continue;
+ qupsize = sizeof(*qup) + strlen(qfpathname);
+ if ((qup = (struct quotause *)malloc(qupsize)) == NULL)
+ errx(2, "out of memory");
+ if (quotactl(fs->fs_file, qcmd, id, &qup->dqblk) != 0) {
+ if (errno == EOPNOTSUPP && !warned) {
+ warned++;
+ warnx("warning: quotas are not compiled into this kernel");
+ sleep(3);
+ }
+ if ((fd = open(qfpathname, O_RDONLY)) < 0) {
+ fd = open(qfpathname, O_RDWR|O_CREAT, 0640);
+ if (fd < 0 && errno != ENOENT) {
+ warn("%s", qfpathname);
+ free(qup);
+ continue;
+ }
+ warnx("creating quota file %s", qfpathname);
+ sleep(3);
+ (void) fchown(fd, getuid(),
+ getentry(quotagroup, GRPQUOTA));
+ (void) fchmod(fd, 0640);
+ }
+ if (lseek(fd, (off_t)id * sizeof(struct dqblk),
+ L_SET) < 0) {
+ warn("seek error on %s", qfpathname);
+ close(fd);
+ free(qup);
+ continue;
+ }
+ switch (read(fd, &qup->dqblk, sizeof(struct dqblk))) {
+ case 0: /* EOF */
+ /*
+ * Convert implicit 0 quota (EOF)
+ * into an explicit one (zero'ed dqblk)
+ */
+ bzero((caddr_t)&qup->dqblk,
+ sizeof(struct dqblk));
+ break;
+
+ case sizeof(struct dqblk): /* OK */
+ break;
+
+ default: /* ERROR */
+ warn("read error in %s", qfpathname);
+ close(fd);
+ free(qup);
+ continue;
+ }
+ close(fd);
+ }
+ strcpy(qup->qfname, qfpathname);
+ strcpy(qup->fsname, fs->fs_file);
+ if (quphead == NULL)
+ quphead = qup;
+ else
+ quptail->next = qup;
+ quptail = qup;
+ qup->next = 0;
+ }
+ endfsent();
+ return (quphead);
+}
+
+/*
+ * Store the requested quota information.
+ */
+void
+putprivs(long id, int quotatype, struct quotause *quplist)
+{
+ struct quotause *qup;
+ int qcmd, fd;
+ struct dqblk dqbuf;
+
+ qcmd = QCMD(Q_SETQUOTA, quotatype);
+ for (qup = quplist; qup; qup = qup->next) {
+ if (quotactl(qup->fsname, qcmd, id, &qup->dqblk) == 0)
+ continue;
+ if ((fd = open(qup->qfname, O_RDWR)) < 0) {
+ warn("%s", qup->qfname);
+ continue;
+ }
+ if (lseek(fd, (off_t)id * sizeof(struct dqblk), L_SET) < 0) {
+ warn("seek error on %s", qup->qfname);
+ close(fd);
+ continue;
+ }
+ switch (read(fd, &dqbuf, sizeof(struct dqblk))) {
+ case 0: /* EOF */
+ /*
+ * Convert implicit 0 quota (EOF)
+ * into an explicit one (zero'ed dqblk)
+ */
+ bzero(&dqbuf, sizeof(struct dqblk));
+ break;
+
+ case sizeof(struct dqblk): /* OK */
+ break;
+
+ default: /* ERROR */
+ warn("read error in %s", qup->qfname);
+ close(fd);
+ continue;
+ }
+ /*
+ * Reset time limit if have a soft limit and were
+ * previously under it, but are now over it
+ * or if there previously was no soft limit, but
+ * now have one and are over it.
+ */
+ if (dqbuf.dqb_bsoftlimit && id != 0 &&
+ dqbuf.dqb_curblocks < dqbuf.dqb_bsoftlimit &&
+ dqbuf.dqb_curblocks >= qup->dqblk.dqb_bsoftlimit)
+ qup->dqblk.dqb_btime = 0;
+ if (dqbuf.dqb_bsoftlimit == 0 && id != 0 &&
+ dqbuf.dqb_curblocks >= qup->dqblk.dqb_bsoftlimit)
+ qup->dqblk.dqb_btime = 0;
+ if (dqbuf.dqb_isoftlimit && id != 0 &&
+ dqbuf.dqb_curinodes < dqbuf.dqb_isoftlimit &&
+ dqbuf.dqb_curinodes >= qup->dqblk.dqb_isoftlimit)
+ qup->dqblk.dqb_itime = 0;
+ if (dqbuf.dqb_isoftlimit == 0 && id !=0 &&
+ dqbuf.dqb_curinodes >= qup->dqblk.dqb_isoftlimit)
+ qup->dqblk.dqb_itime = 0;
+ qup->dqblk.dqb_curinodes = dqbuf.dqb_curinodes;
+ qup->dqblk.dqb_curblocks = dqbuf.dqb_curblocks;
+ if (lseek(fd, (off_t)id * sizeof(struct dqblk), L_SET) < 0) {
+ warn("seek error on %s", qup->qfname);
+ close(fd);
+ continue;
+ }
+ if (write(fd, &qup->dqblk, sizeof (struct dqblk)) !=
+ sizeof (struct dqblk)) {
+ warn("%s", qup->qfname);
+ }
+ close(fd);
+ }
+}
+
+/*
+ * Take a list of priviledges and get it edited.
+ */
+int
+editit(char *tmpf)
+{
+ long omask;
+ int pid, status;
+
+ omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
+ top:
+ if ((pid = fork()) < 0) {
+
+ if (errno == EPROCLIM) {
+ warnx("you have too many processes");
+ return(0);
+ }
+ if (errno == EAGAIN) {
+ sleep(1);
+ goto top;
+ }
+ warn("fork");
+ return (0);
+ }
+ if (pid == 0) {
+ const char *ed;
+
+ sigsetmask(omask);
+ setgid(getgid());
+ setuid(getuid());
+ if ((ed = getenv("EDITOR")) == (char *)0)
+ ed = _PATH_VI;
+ execlp(ed, ed, tmpf, (char *)0);
+ err(1, "%s", ed);
+ }
+ waitpid(pid, &status, 0);
+ sigsetmask(omask);
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+ return (0);
+ return (1);
+}
+
+/*
+ * Convert a quotause list to an ASCII file.
+ */
+int
+writeprivs(struct quotause *quplist, int outfd, char *name, int quotatype)
+{
+ struct quotause *qup;
+ FILE *fd;
+
+ ftruncate(outfd, 0);
+ lseek(outfd, 0, L_SET);
+ if ((fd = fdopen(dup(outfd), "w")) == NULL)
+ err(1, "%s", tmpfil);
+ fprintf(fd, "Quotas for %s %s:\n", qfextension[quotatype], name);
+ for (qup = quplist; qup; qup = qup->next) {
+ fprintf(fd, "%s: %s %lu, limits (soft = %lu, hard = %lu)\n",
+ qup->fsname, "kbytes in use:",
+ (unsigned long)(dbtob(qup->dqblk.dqb_curblocks) / 1024),
+ (unsigned long)(dbtob(qup->dqblk.dqb_bsoftlimit) / 1024),
+ (unsigned long)(dbtob(qup->dqblk.dqb_bhardlimit) / 1024));
+ fprintf(fd, "%s %lu, limits (soft = %lu, hard = %lu)\n",
+ "\tinodes in use:",
+ (unsigned long)qup->dqblk.dqb_curinodes,
+ (unsigned long)qup->dqblk.dqb_isoftlimit,
+ (unsigned long)qup->dqblk.dqb_ihardlimit);
+ }
+ fclose(fd);
+ return (1);
+}
+
+/*
+ * Merge changes to an ASCII file into a quotause list.
+ */
+int
+readprivs(struct quotause *quplist, char *inname)
+{
+ struct quotause *qup;
+ FILE *fd;
+ unsigned long bhardlimit, bsoftlimit, curblocks;
+ unsigned long ihardlimit, isoftlimit, curinodes;
+ int cnt;
+ char *cp;
+ struct dqblk dqblk;
+ char *fsp, line1[BUFSIZ], line2[BUFSIZ];
+
+ fd = fopen(inname, "r");
+ if (fd == NULL) {
+ warnx("can't re-read temp file!!");
+ return (0);
+ }
+ /*
+ * Discard title line, then read pairs of lines to process.
+ */
+ (void) fgets(line1, sizeof (line1), fd);
+ while (fgets(line1, sizeof (line1), fd) != NULL &&
+ fgets(line2, sizeof (line2), fd) != NULL) {
+ if ((fsp = strtok(line1, " \t:")) == NULL) {
+ warnx("%s: bad format", line1);
+ return (0);
+ }
+ if ((cp = strtok((char *)0, "\n")) == NULL) {
+ warnx("%s: %s: bad format", fsp, &fsp[strlen(fsp) + 1]);
+ return (0);
+ }
+ cnt = sscanf(cp,
+ " kbytes in use: %lu, limits (soft = %lu, hard = %lu)",
+ &curblocks, &bsoftlimit, &bhardlimit);
+ if (cnt != 3) {
+ warnx("%s:%s: bad format", fsp, cp);
+ return (0);
+ }
+ dqblk.dqb_curblocks = btodb((off_t)curblocks * 1024);
+ dqblk.dqb_bsoftlimit = btodb((off_t)bsoftlimit * 1024);
+ dqblk.dqb_bhardlimit = btodb((off_t)bhardlimit * 1024);
+ if ((cp = strtok(line2, "\n")) == NULL) {
+ warnx("%s: %s: bad format", fsp, line2);
+ return (0);
+ }
+ cnt = sscanf(cp,
+ "\tinodes in use: %lu, limits (soft = %lu, hard = %lu)",
+ &curinodes, &isoftlimit, &ihardlimit);
+ if (cnt != 3) {
+ warnx("%s: %s: bad format", fsp, line2);
+ return (0);
+ }
+ dqblk.dqb_curinodes = curinodes;
+ dqblk.dqb_isoftlimit = isoftlimit;
+ dqblk.dqb_ihardlimit = ihardlimit;
+ for (qup = quplist; qup; qup = qup->next) {
+ if (strcmp(fsp, qup->fsname))
+ continue;
+ /*
+ * Cause time limit to be reset when the quota
+ * is next used if previously had no soft limit
+ * or were under it, but now have a soft limit
+ * and are over it.
+ */
+ if (dqblk.dqb_bsoftlimit &&
+ qup->dqblk.dqb_curblocks >= dqblk.dqb_bsoftlimit &&
+ (qup->dqblk.dqb_bsoftlimit == 0 ||
+ qup->dqblk.dqb_curblocks <
+ qup->dqblk.dqb_bsoftlimit))
+ qup->dqblk.dqb_btime = 0;
+ if (dqblk.dqb_isoftlimit &&
+ qup->dqblk.dqb_curinodes >= dqblk.dqb_isoftlimit &&
+ (qup->dqblk.dqb_isoftlimit == 0 ||
+ qup->dqblk.dqb_curinodes <
+ qup->dqblk.dqb_isoftlimit))
+ qup->dqblk.dqb_itime = 0;
+ qup->dqblk.dqb_bsoftlimit = dqblk.dqb_bsoftlimit;
+ qup->dqblk.dqb_bhardlimit = dqblk.dqb_bhardlimit;
+ qup->dqblk.dqb_isoftlimit = dqblk.dqb_isoftlimit;
+ qup->dqblk.dqb_ihardlimit = dqblk.dqb_ihardlimit;
+ qup->flags |= FOUND;
+ if (dqblk.dqb_curblocks == qup->dqblk.dqb_curblocks &&
+ dqblk.dqb_curinodes == qup->dqblk.dqb_curinodes)
+ break;
+ warnx("%s: cannot change current allocation", fsp);
+ break;
+ }
+ }
+ fclose(fd);
+ /*
+ * Disable quotas for any filesystems that have not been found.
+ */
+ for (qup = quplist; qup; qup = qup->next) {
+ if (qup->flags & FOUND) {
+ qup->flags &= ~FOUND;
+ continue;
+ }
+ qup->dqblk.dqb_bsoftlimit = 0;
+ qup->dqblk.dqb_bhardlimit = 0;
+ qup->dqblk.dqb_isoftlimit = 0;
+ qup->dqblk.dqb_ihardlimit = 0;
+ }
+ return (1);
+}
+
+/*
+ * Convert a quotause list to an ASCII file of grace times.
+ */
+int
+writetimes(struct quotause *quplist, int outfd, int quotatype)
+{
+ struct quotause *qup;
+ FILE *fd;
+
+ ftruncate(outfd, 0);
+ lseek(outfd, 0, L_SET);
+ if ((fd = fdopen(dup(outfd), "w")) == NULL)
+ err(1, "%s", tmpfil);
+ fprintf(fd, "Time units may be: days, hours, minutes, or seconds\n");
+ fprintf(fd, "Grace period before enforcing soft limits for %ss:\n",
+ qfextension[quotatype]);
+ for (qup = quplist; qup; qup = qup->next) {
+ fprintf(fd, "%s: block grace period: %s, ",
+ qup->fsname, cvtstoa(qup->dqblk.dqb_btime));
+ fprintf(fd, "file grace period: %s\n",
+ cvtstoa(qup->dqblk.dqb_itime));
+ }
+ fclose(fd);
+ return (1);
+}
+
+/*
+ * Merge changes of grace times in an ASCII file into a quotause list.
+ */
+int
+readtimes(struct quotause *quplist, char *inname)
+{
+ struct quotause *qup;
+ FILE *fd;
+ int cnt;
+ char *cp;
+ time_t itime, btime, iseconds, bseconds;
+ long l_itime, l_btime;
+ char *fsp, bunits[10], iunits[10], line1[BUFSIZ];
+
+ fd = fopen(inname, "r");
+ if (fd == NULL) {
+ warnx("can't re-read temp file!!");
+ return (0);
+ }
+ /*
+ * Discard two title lines, then read lines to process.
+ */
+ (void) fgets(line1, sizeof (line1), fd);
+ (void) fgets(line1, sizeof (line1), fd);
+ while (fgets(line1, sizeof (line1), fd) != NULL) {
+ if ((fsp = strtok(line1, " \t:")) == NULL) {
+ warnx("%s: bad format", line1);
+ return (0);
+ }
+ if ((cp = strtok((char *)0, "\n")) == NULL) {
+ warnx("%s: %s: bad format", fsp, &fsp[strlen(fsp) + 1]);
+ return (0);
+ }
+ cnt = sscanf(cp,
+ " block grace period: %ld %s file grace period: %ld %s",
+ &l_btime, bunits, &l_itime, iunits);
+ if (cnt != 4) {
+ warnx("%s:%s: bad format", fsp, cp);
+ return (0);
+ }
+ btime = l_btime;
+ itime = l_itime;
+ if (cvtatos(btime, bunits, &bseconds) == 0)
+ return (0);
+ if (cvtatos(itime, iunits, &iseconds) == 0)
+ return (0);
+ for (qup = quplist; qup; qup = qup->next) {
+ if (strcmp(fsp, qup->fsname))
+ continue;
+ qup->dqblk.dqb_btime = bseconds;
+ qup->dqblk.dqb_itime = iseconds;
+ qup->flags |= FOUND;
+ break;
+ }
+ }
+ fclose(fd);
+ /*
+ * reset default grace periods for any filesystems
+ * that have not been found.
+ */
+ for (qup = quplist; qup; qup = qup->next) {
+ if (qup->flags & FOUND) {
+ qup->flags &= ~FOUND;
+ continue;
+ }
+ qup->dqblk.dqb_btime = 0;
+ qup->dqblk.dqb_itime = 0;
+ }
+ return (1);
+}
+
+/*
+ * Convert seconds to ASCII times.
+ */
+char *
+cvtstoa(time_t secs)
+{
+ static char buf[20];
+
+ if (secs % (24 * 60 * 60) == 0) {
+ secs /= 24 * 60 * 60;
+ sprintf(buf, "%ld day%s", (long)secs, secs == 1 ? "" : "s");
+ } else if (secs % (60 * 60) == 0) {
+ secs /= 60 * 60;
+ sprintf(buf, "%ld hour%s", (long)secs, secs == 1 ? "" : "s");
+ } else if (secs % 60 == 0) {
+ secs /= 60;
+ sprintf(buf, "%ld minute%s", (long)secs, secs == 1 ? "" : "s");
+ } else
+ sprintf(buf, "%ld second%s", (long)secs, secs == 1 ? "" : "s");
+ return (buf);
+}
+
+/*
+ * Convert ASCII input times to seconds.
+ */
+int
+cvtatos(time_t period, char *units, time_t *seconds)
+{
+
+ if (bcmp(units, "second", 6) == 0)
+ *seconds = period;
+ else if (bcmp(units, "minute", 6) == 0)
+ *seconds = period * 60;
+ else if (bcmp(units, "hour", 4) == 0)
+ *seconds = period * 60 * 60;
+ else if (bcmp(units, "day", 3) == 0)
+ *seconds = period * 24 * 60 * 60;
+ else {
+ printf("%s: bad units, specify %s\n", units,
+ "days, hours, minutes, or seconds");
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * Free a list of quotause structures.
+ */
+void
+freeprivs(struct quotause *quplist)
+{
+ struct quotause *qup, *nextqup;
+
+ for (qup = quplist; qup; qup = nextqup) {
+ nextqup = qup->next;
+ free(qup);
+ }
+}
+
+/*
+ * Check whether a string is completely composed of digits.
+ */
+int
+alldigits(const char *s)
+{
+ int c;
+
+ c = *s++;
+ do {
+ if (!isdigit(c))
+ return (0);
+ } while ((c = *s++));
+ return (1);
+}
+
+/*
+ * Check to see if a particular quota is to be enabled.
+ */
+int
+hasquota(struct fstab *fs, int type, char **qfnamep)
+{
+ char *opt;
+ char *cp;
+ struct statfs sfb;
+ 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, '=')))
+ *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;
+ }
+ if (statfs(fs->fs_file, &sfb) != 0) {
+ warn("cannot statfs mount point %s", fs->fs_file);
+ return (0);
+ }
+ if (strcmp(fs->fs_file, sfb.f_mntonname)) {
+ warnx("%s not mounted for %s quotas", fs->fs_file,
+ type == USRQUOTA ? "user" : "group");
+ sleep(3);
+ return (0);
+ }
+ return (1);
+}
diff --git a/usr.sbin/edquota/pathnames.h b/usr.sbin/edquota/pathnames.h
new file mode 100644
index 0000000..53bfbfe
--- /dev/null
+++ b/usr.sbin/edquota/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/6/93
+ * $FreeBSD$
+ */
+
+#include <paths.h>
+
+#undef _PATH_TMP
+#define _PATH_TMP "/tmp/EdP.aXXXXX"
diff --git a/usr.sbin/eeprom/Makefile b/usr.sbin/eeprom/Makefile
new file mode 100644
index 0000000..fa5f411
--- /dev/null
+++ b/usr.sbin/eeprom/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../ofwdump
+
+PROG= eeprom
+MAN= eeprom.8
+MANSUBDIR= /sparc64
+SRCS= eeprom.c ofw_options.c ofw_util.c
+WARNS?= 6
+CFLAGS+= -I${.CURDIR}/../ofwdump
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/eeprom/eeprom.8 b/usr.sbin/eeprom/eeprom.8
new file mode 100644
index 0000000..81cb2d3
--- /dev/null
+++ b/usr.sbin/eeprom/eeprom.8
@@ -0,0 +1,707 @@
+.\" Copyright (c) 1996 The NetBSD Foundation, Inc.
+.\" Copyright (c) 2004 Marius Strobl
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Jason R. Thorpe.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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.
+.\"
+.\" from: NetBSD: eeprom.8,v 1.11 2003/03/31 01:31:39 perry Exp
+.\" $FreeBSD$
+.\"
+.Dd September 1, 2006
+.Dt EEPROM 8 sparc64
+.Os
+.Sh NAME
+.Nm eeprom
+.Nd "display or modify contents of the EEPROM or NVRAM"
+.Sh SYNOPSIS
+.Nm
+.Fl a
+.Nm
+.Op Fl
+.Ar name Ns Op = Ns Ar value
+.Ar ...
+.Sh DESCRIPTION
+The
+.Nm
+utility provides an interface for displaying and changing the system's
+configuration variables contained in EEPROM or NVRAM.
+In the first synopsis form, all available configuration variables and their
+current values are printed.
+In the second form, only the variable selected by
+.Ar name
+and its value is printed or changed if
+.Ar name
+is followed by
+.Ql =
+and a
+.Ar value .
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl
+Commands for displaying or changing variables are taken from stdin, allowing
+one
+.Ar name
+or one
+.Ar name
+and
+.Ar value
+pair per line.
+The output is printed on stdout.
+.It Fl a
+Print all available configuration variables and their current values.
+.El
+.Sh VARIABLES AND VALUES
+Below are variables and values that one is likely to find on a system equipped
+with OpenBoot 3.x and Open Firmware respectively.
+.Pp
+Note: the attempt to set a variable to an illegal value results in the
+Open Firmware setting it to some legal value instead.
+The
+.Nm
+utility will detect this, try to recover the previous value of the variable
+and issue a warning telling that the requested value could not be set.
+.Bl -tag -width ".Va last-hardware-update"
+.It Va auto-boot?
+If
+.Dq Li true ,
+the system will try to boot automatically from the devices listed in
+.Va boot-device
+and
+.Va diag-device
+respectively, using the command specified in
+.Va boot-command
+at power-up.
+Default:
+.Dq Li true .
+.It Va auto-boot-retry?
+If set to
+.Dq Li true
+and
+.Va auto-boot?
+is also set to
+.Dq Li true ,
+the system will try to boot from the specified boot devices forever.
+Default:
+.Dq Li false .
+.It Va ansi-terminal?
+If
+.Dq Li false ,
+.Tn ANSI
+escape sequences are not interpreted by the terminal emulator.
+Default:
+.Dq Li true .
+.It Va boot-command
+Command executed when
+.Va auto-boot?
+is set to
+.Dq Li true .
+Default:
+.Dq Li boot .
+.It Va boot-device
+Default device to boot from if
+.Va diag-switch?
+is set to
+.Dq Li false .
+Takes one or more device aliases or device paths.
+The boot devices are sequentially tried to boot from, beginning with the first
+one specified.
+Default:
+.Dq Li "net disk" .
+.It Va cpci-probe-list
+Digits in the format
+.Dq Li 0,1,2
+specifying in which order to probe the devices on the CompactPCI bus at
+power-up.
+Default: system-dependent.
+.It Va boot-file
+Default arguments for boot when
+.Va diag-switch?
+is set to
+.Dq Li false .
+When empty, the secondary boot loader will choose the file to boot.
+Default: empty string.
+.It Va diag-device
+Like
+.Va boot-device .
+Used when
+.Va diag-switch?
+is set to
+.Dq Li true .
+Default:
+.Dq Li net .
+.It Va diag-file
+Like
+.Va boot-file .
+Used when
+.Va diag-switch?
+is set to
+.Dq Li true .
+Default: empty string.
+.It Va diag-level
+Level of diagnostics to run when
+.Va diag-switch?
+is set to
+.Dq Li true .
+Possible values are
+.Dq Li max ,
+.Dq Li menus ,
+.Dq Li min
+and
+.Dq Li off
+(depending on the system model).
+When set to
+.Dq Li off ,
+the Power-On Self Test (POST) is not run.
+The other values are interpreted by the POST.
+Default:
+.Dq Li min
+or
+.Dq Li max
+(system-dependent).
+.It Va diag-switch?
+If
+.Dq Li true ,
+the system will boot and run in diagnostic mode.
+Default:
+.Dq Li false
+or
+.Dq Li true
+(system-dependent).
+.It Va env-monitor
+Enables or disables the Advanced System Monitoring (ASM).
+Possible values are
+.Dq Li enabled
+and
+.Dq Li disabled .
+Default:
+.Dq Li enabled .
+.It Va fcode-debug?
+Used for debugging FCode programs.
+If set to
+.Dq Li true ,
+names of additional FCodes are registered in the Forth dictionary.
+Default:
+.Dq Li false .
+.It Va hardware-revision
+A string describing the system hardware version.
+Default: system-dependent.
+.It Va input-device
+One of the strings
+.Dq Li keyboard ,
+.Dq Li ttya ,
+or
+.Dq Li ttyb ,
+specifying the default console input device.
+Default:
+.Dq Li keyboard
+or
+.Dq Li ttya
+(system-dependent).
+.It Va keyboard-click?
+If set to
+.Dq Li true ,
+the keys click annoyingly.
+Default:
+.Dq Li false .
+.It Va keymap
+Keymap for a custom keyboard.
+Default: empty string.
+.It Va last-hardware-update
+Similar to
+.Va hardware-revision ,
+describing when the hardware was last updated.
+Default: system-dependent.
+.It Va last-poweroff-cause
+Cause of the last power-off.
+Used internally by the OpenBoot PROM.
+Default:
+.Dq Li 0 .
+.It Va load-base
+Default address where client programs are loaded to.
+It is unlikely that this value should ever be changed.
+Default:
+.Dq Li 16384 .
+.It Va local-mac-address?
+If set to
+.Dq Li false ,
+all Ethernet devices with FCode will use the system default MAC address.
+If set to
+.Dq Li true ,
+Ethernet devices with FCode that contains a unique MAC address will use it
+rather than the system's default MAC address.
+Default:
+.Dq Li false .
+.Pp
+Ethernet devices with FCode include those supported by
+.Xr dc 4 ,
+.Xr gem 4
+and
+.Xr hme 4 .
+Please see the respective manual page for further information.
+.It Va mfg-mode
+Manufacture test mode interpreted by the POST.
+Possible values are
+.Dq Li chamber
+and
+.Dq Li off .
+Default:
+.Dq Li off .
+.It Va mfg-switch?
+If set to
+.Dq Li true ,
+manufacturing tests are repeated until stopped by pressing STOP-A.
+Default:
+.Dq Li off .
+.It Va net-timeout
+If set to
+.Dq Li 0 ,
+the system will try to boot forever when the boot device used is a network
+device.
+Any non-zero value is interpreted as minutes to try a network boot.
+Default:
+.Dq Li 0 .
+.It Va nvramrc
+Contents of the NVRAMRC.
+Default: empty string.
+.Pp
+While
+.Va nvramrc
+can be set using
+.Nm ,
+it is preferred to use
+.Ic nvedit
+in the boot monitor instead.
+.It Va oem-banner
+A string displayed at power-up, rather than the default banner.
+Used when
+.Va oem-banner?
+is set to
+.Dq Li true .
+Default: system-dependent.
+.It Va oem-banner?
+If set to
+.Dq Li true ,
+the string stored in
+.Va oem-banner
+is displayed at power-up rather than the default banner.
+Default: system-dependent.
+.It Va oem-logo
+A logo displayed at power-up when
+.Va oem-logo?
+is set to
+.Dq Li true ,
+rather than the default logo.
+The logo has to be 512 bytes in size, containing a 64x64-bit monochrome image
+in Sun Raster format without the leading 32-byte header.
+Default: system-dependent.
+.Pp
+To set the logo with
+.Nm ,
+give the pathname of the file containing the image as the
+.Ar value .
+Using an empty
+.Ar value
+will remove the image.
+.It Va oem-logo?
+If set to
+.Dq Li true ,
+the logo stored in
+.Va oem-logo
+is displayed at power-up rather than the default logo.
+.It Va output-device
+One of the strings
+.Dq Li screen ,
+.Dq Li ttya ,
+or
+.Dq Li ttyb ,
+specifying the default console output device.
+Default:
+.Dq Li screen
+or
+.Dq Li ttya
+(system-dependent).
+.It Va pcia-probe-list
+Digits in the format
+.Dq Li 1,2,3
+specifying in which order to probe the devices on the PCI bus A.
+Default: system-dependent.
+.It Va pcib-probe-list
+Like
+.Va pcia-probe-list ,
+but for PCI bus B.
+Default: system-dependent.
+.It Va #power-cycles
+Number of power-cycles.
+Automatically incremented on each power-cycle.
+Default: system-dependent.
+.It Va sbus-probe-list
+Digits in the format
+.Dq Li 0123
+specifying in which order to probe the SBus slots at power-up.
+Default: system-dependent.
+.It Va screen-#columns
+An integer specifying the screen width in characters per line.
+Default:
+.Dq Li 80 .
+.It Va screen-#rows
+An integer specifying the screen height in lines.
+Default:
+.Dq Li 34 .
+.It Va scsi-initiator-id
+The SCSI ID of SCSI controllers in the range of [0-7] or [0-f] (depending
+on the controller).
+A SCSI controller may or may not adhere to this setting, depending on its
+FCode and device driver.
+Default:
+.Dq Li 7 .
+.It Va security-#badlogins
+Number of incorrect password attempts when
+.Va security-mode
+is set to
+.Dq Li command
+or
+.Dq Li full .
+Default:
+.Dq Li 0 .
+.It Va security-mode
+Boot monitor security level.
+One of the three possible values
+.Dq Li full ,
+.Dq Li command ,
+or
+.Dq Li none .
+When set to
+.Dq Li full ,
+all boot monitor commands except for
+.Ic go
+require the password.
+When set to
+.Dq Li command ,
+all boot monitor commands except for
+.Ic boot
+and
+.Ic go
+require the password.
+When set to
+.Dq Li none ,
+no password is required.
+Default:
+.Dq Li none .
+.Pp
+When
+.Nm
+is used to set
+.Va security-mode
+to
+.Dq Li full
+or
+.Dq Li command ,
+you will be prompted for the password.
+When
+.Va security-mode
+is set to
+.Dq Li none ,
+.Nm
+will clear the password.
+.It Va security-password
+The password used when
+.Va security-mode
+is set to
+.Dq Li full
+or
+.Dq Li command .
+The maximum length for this password is 8 characters.
+All characters exceeding this length will be ignored.
+The value displayed for
+.Va security-password
+is always an empty string, even when a password is set.
+Default: empty string.
+.Pp
+When
+.Va security-mode
+is set to
+.Dq Li full
+or
+.Dq Li command ,
+.Nm
+can be used to enter a new password using any
+.Ar value
+for
+.Va security-password
+on the command line.
+You will be prompted by
+.Nm
+to type in the new password in this case.
+Trying to set
+.Va security-password
+when
+.Va security-mode
+is set to
+.Dq Li none
+using
+.Nm
+has no effect.
+.It Va selftest-#megs
+An integer specifying the number of megabytes of memory to test upon
+power-up when
+.Va diag-switch?
+is set to
+.Dq Li false .
+Default:
+.Dq Li 1 .
+.It Va shutdown-temperature
+Temperature at which the ASM issues an over-temperature shutdown.
+Default: system-dependent.
+.It Va silent-mode
+If set to
+.Dq Li true ,
+memory test messages will not be displayed at power-up.
+Default:
+.Dq Li false .
+.It Va sunmon-compat?
+If set to
+.Dq Li true ,
+the old bootROM interface will be used while in the boot monitor,
+rather than the OpenBoot PROM interface.
+Default:
+.Dq Li false .
+.It Va system-board-date
+Manufacturing date of the system board.
+Default: system-dependent.
+.It Va system-board-serial#
+Serial number of the system board.
+Default: system-dependent.
+.It Va tpe-link-test?
+Enable link test on 10baseT and 100baseTX Ethernet devices.
+Default:
+.Dq Li true .
+.It Va ttya-mode
+A string of five comma separated fields in the format
+.Dq Li 9600,8,n,1,- .
+The first field is the baud rate.
+The second field is the number of data bits.
+The third field is the parity; acceptable values for parity are
+.Ql n
+(none),
+.Ql e
+(even),
+.Ql o
+(odd),
+.Ql m
+(mark), and
+.Ql s
+(space).
+The fourth field is the number of stop bits.
+The fifth field is the
+.Dq handshake
+field; acceptable values are
+.Ql -
+(none),
+.Ql h
+(RTS/CTS), and
+.Ql s
+(Xon/Xoff).
+Default:
+.Dq Li 9600,8,n,1,- .
+.It Va ttya-ignore-cd
+If set to
+.Dq Li true ,
+the system will ignore carrier detect.
+Default:
+.Dq Li true .
+.It Va ttya-rts-dtr-off
+If set to
+.Dq Li true ,
+the system will ignore RTS/DTR.
+Default:
+.Dq Li false .
+.It Va ttyb-mode
+Like
+.Va ttya-mode ,
+but for ttyb.
+Default:
+.Dq Li 9600,8,n,1,- .
+.It Va ttyb-ignore-cd
+Like
+.Va ttya-ignore-cd ,
+but for ttyb.
+Default:
+.Dq Li true .
+.It Va ttyb-rts-dtr-off
+Like
+.Va ttya-rts-dtr-off ,
+but for ttyb.
+Default:
+.Dq Li false .
+.It Va use-boot-table?
+Use boot table defined by the OEM.
+Default: system-dependent.
+.It Va use-nvramrc?
+If set to
+.Dq Li true ,
+the script stored in
+.Va nvramrc
+will be executed during start-up.
+Default:
+.Dq Li false .
+.It Va warning-temperature
+Temperature at which the ASM issues an over-temperature warning.
+Default: system-dependent.
+.It Va watchdog-enable?
+Enables or disables the system watchdog timer.
+Default:
+.Dq Li false .
+.It Va watchdog-reboot?
+If set to
+.Dq Li true ,
+the system will reboot upon terminal count of the system watchdog timer.
+If set to
+.Dq Li false ,
+the system will fall into the boot monitor.
+Default:
+.Dq Li false .
+.It Va watchdog-timeout
+Expiry limit for the system watchdog timer.
+Range and unit depend on the system model.
+Default: system-dependent.
+.El
+.Sh EXAMPLES
+Print all available configuration variables and their current values:
+.Pp
+.Dl "eeprom -a"
+.Pp
+Print the current value of the
+.Va local-mac-address?
+variable:
+.Pp
+.Dl "eeprom local-mac-address\e?"
+.Pp
+Set the value of the
+.Va local-mac-address?
+variable to
+.Dq Li true :
+.Pp
+.Dl "eeprom local-mac-address\e?=true"
+.Pp
+Note that the
+.Ql \e
+in the above examples is used to keep the shell from interpreting the
+.Ql \&? .
+.Pp
+Write an image to the
+.Va oem-logo
+variable:
+.Pp
+.Dl "eeprom oem-logo=/path/to/image.raw"
+.Pp
+Remove the image from the
+.Va oem-logo
+variable again:
+.Pp
+.Dl "eeprom oem-logo="
+.Pp
+Set the value of the
+.Va security-mode
+variable to
+.Dq Li full ,
+and set the password:
+.Bd -literal -offset indent
+eeprom security-mode=full
+New password:
+Retype new password:
+.Ed
+.Pp
+Remember that the maximum length for the password is 8 characters.
+All characters exceeding this length will be ignored.
+.Pp
+Set a new password when the
+.Va security-mode
+variable is set to
+.Dq Li command
+or
+.Dq Li full :
+.Bd -literal -offset indent
+eeprom security-password=
+New password:
+Retype new password:
+.Ed
+.Sh SEE ALSO
+.Xr dc 4 ,
+.Xr gem 4 ,
+.Xr hme 4 ,
+.Xr ofwdump 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Bx 4.4 .
+It was adopted from there by
+.Fx 2.0 .
+The
+.Nm
+utility was removed from
+.Fx
+again after
+.Fx 2.1.7
+because the utility was unused at that time.
+The present implementation of the
+.Nm
+utility first appeared in
+.Fx 5.3 .
+It is inspired by the
+.Nx
+.Xr eeprom 8
+and SunOS/Solaris
+.Xr eeprom 1M
+utilities.
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility uses base code from the
+.Nx
+version written by
+.An "Jason R. Thorpe" .
+The handlers for the Open Firmware
+.Pa /options
+node were written by
+.An "Marius Strobl" Aq marius@FreeBSD.org .
+The code for accessing the Open Firmware device tree is shared with the
+.Xr ofwdump 8
+utility written by
+.An "Thomas Moestl" Aq tmm@FreeBSD.org .
+.Sh BUGS
+Currently,
+.Nm
+only supports systems equipped with Open Firmware and is only tested on Sun
+Microsystems sun4u machines.
diff --git a/usr.sbin/eeprom/eeprom.c b/usr.sbin/eeprom/eeprom.c
new file mode 100644
index 0000000..ecd9267
--- /dev/null
+++ b/usr.sbin/eeprom/eeprom.c
@@ -0,0 +1,153 @@
+/*-
+ * Copyright (c) 1996 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Jason R. Thorpe.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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.
+ *
+ * from: NetBSD: main.c,v 1.15 2001/02/19 23:22:42 cgd Exp
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "ofw_options.h"
+
+static int action(char *);
+static void dump_config(void);
+static void usage(void);
+
+static void
+usage(void)
+{
+
+ fprintf(stderr,
+ "usage: eeprom -a\n"
+ " eeprom [-] name[=value] ...\n");
+ exit(EX_USAGE);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int do_stdin, opt;
+ int aflag, rv;
+ char *cp;
+ char line[BUFSIZ];
+
+ aflag = do_stdin = 0;
+ rv = EX_OK;
+ while ((opt = getopt(argc, argv, "-a")) != -1) {
+ switch (opt) {
+ case '-':
+ if (aflag)
+ usage();
+ do_stdin = 1;
+ break;
+ case 'a':
+ if (do_stdin)
+ usage();
+ aflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (aflag) {
+ if (argc != 0)
+ usage();
+ dump_config();
+ } else {
+ if (do_stdin) {
+ while (fgets(line, BUFSIZ, stdin) != NULL &&
+ rv == EX_OK) {
+ if (line[0] == '\n')
+ continue;
+ if ((cp = strrchr(line, '\n')) != NULL)
+ *cp = '\0';
+ rv = action(line);
+ }
+ if (ferror(stdin))
+ err(EX_NOINPUT, "stdin");
+ } else {
+ if (argc == 0)
+ usage();
+ while (argc && rv == EX_OK) {
+ rv = action(*argv);
+ ++argv;
+ --argc;
+ }
+ }
+ }
+ return (rv);
+}
+
+static int
+action(char *line)
+{
+ int rv;
+ char *keyword, *arg;
+
+ keyword = strdup(line);
+ if (keyword == NULL)
+ err(EX_OSERR, "malloc() failed");
+ if ((arg = strrchr(keyword, '=')) != NULL)
+ *arg++ = '\0';
+ switch (rv = ofwo_action(keyword, arg)) {
+ case EX_UNAVAILABLE:
+ warnx("nothing available for '%s'.", keyword);
+ break;
+ case EX_DATAERR:
+ warnx("invalid value '%s' for '%s'.", arg, keyword);
+ break;
+ }
+ free(keyword);
+ return(rv);
+}
+
+static void
+dump_config()
+{
+
+ ofwo_dump();
+}
diff --git a/usr.sbin/eeprom/ofw_options.c b/usr.sbin/eeprom/ofw_options.c
new file mode 100644
index 0000000..eb7437f
--- /dev/null
+++ b/usr.sbin/eeprom/ofw_options.c
@@ -0,0 +1,310 @@
+/*-
+ * Copyright (c) 2004 Marius Strobl
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (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$");
+
+/*
+ * Handlers for Open Firmware /options node.
+ */
+
+#include <sys/types.h>
+
+#include <dev/ofw/openfirm.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <readpassphrase.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "ofw_options.h"
+#include "ofw_util.h"
+
+#define OFWO_LOGO 512
+#define OFWO_MAXPROP 31
+#define OFWO_MAXPWD 8
+
+struct ofwo_extabent {
+ const char *ex_prop;
+ int (*ex_handler)(const struct ofwo_extabent *, int,
+ const void *, int, const char *);
+};
+
+static int ofwo_oemlogo(const struct ofwo_extabent *, int, const void *,
+ int, const char *);
+static int ofwo_secmode(const struct ofwo_extabent *, int, const void *,
+ int, const char *);
+static int ofwo_secpwd(const struct ofwo_extabent *, int, const void *,
+ int, const char *);
+
+static const struct ofwo_extabent const ofwo_extab[] = {
+ { "oem-logo", ofwo_oemlogo },
+ { "security-mode", ofwo_secmode },
+ { "security-password", ofwo_secpwd },
+ { NULL, NULL }
+};
+
+static int ofwo_setpass(int);
+static int ofwo_setstr(int, const void *, int, const char *, const char *);
+
+static __inline void
+ofwo_printprop(const char *prop, const char* buf, int buflen)
+{
+
+ printf("%s: %.*s\n", prop, buflen, buf);
+}
+
+static int
+ofwo_oemlogo(const struct ofwo_extabent *exent, int fd, const void *buf,
+ int buflen, const char *val)
+{
+ int lfd;
+ char logo[OFWO_LOGO + 1];
+
+ if (val) {
+ if (val[0] == '\0')
+ ofw_setprop(fd, ofw_optnode(fd), exent->ex_prop, "", 1);
+ else {
+ if ((lfd = open(val, O_RDONLY)) == -1) {
+ warn("could not open '%s'", val);
+ return (EX_USAGE);
+ }
+ if (read(lfd, logo, OFWO_LOGO) != OFWO_LOGO ||
+ lseek(lfd, 0, SEEK_END) != OFWO_LOGO) {
+ close(lfd);
+ warnx("logo '%s' has wrong size.", val);
+ return (EX_USAGE);
+ }
+ close(lfd);
+ logo[OFWO_LOGO] = '\0';
+ if (ofw_setprop(fd, ofw_optnode(fd), exent->ex_prop,
+ logo, OFWO_LOGO + 1) != OFWO_LOGO)
+ errx(EX_IOERR, "writing logo failed.");
+ }
+ } else
+ if (buflen != 0)
+ printf("%s: <logo data>\n", exent->ex_prop);
+ else
+ ofwo_printprop(exent->ex_prop, (const char *)buf,
+ buflen);
+ return (EX_OK);
+}
+
+static int
+ofwo_secmode(const struct ofwo_extabent *exent, int fd, const void *buf,
+ int buflen, const char *val)
+{
+ int res;
+
+ if (val) {
+ if (strcmp(val, "full") == 0 || strcmp(val, "command") == 0) {
+ if ((res = ofwo_setpass(fd)) != EX_OK)
+ return (res);
+ if ((res = ofwo_setstr(fd, buf, buflen, exent->ex_prop,
+ val)) != EX_OK)
+ ofw_setprop(fd, ofw_optnode(fd),
+ "security-password", "", 1);
+ return (res);
+ }
+ if (strcmp(val, "none") == 0) {
+ ofw_setprop(fd, ofw_optnode(fd), "security-password",
+ "", 1);
+ return (ofwo_setstr(fd, buf, buflen, exent->ex_prop,
+ val));
+ }
+ return (EX_DATAERR);
+ } else
+ ofwo_printprop(exent->ex_prop, (const char *)buf, buflen);
+ return (EX_OK);
+}
+
+static int
+ofwo_secpwd(const struct ofwo_extabent *exent, int fd, const void *buf,
+ int buflen, const char *val)
+{
+ void *pbuf;
+ int len, pblen, rv;
+
+ pblen = 0;
+ rv = EX_OK;
+ pbuf = NULL;
+ if (val) {
+ len = ofw_getprop_alloc(fd, ofw_optnode(fd), "security-mode",
+ &pbuf, &pblen, 1);
+ if (len <= 0 || strncmp("none", (char *)pbuf, len) == 0) {
+ rv = EX_CONFIG;
+ warnx("no security mode set.");
+ } else if (strncmp("command", (char *)pbuf, len) == 0 ||
+ strncmp("full", (char *)pbuf, len) == 0) {
+ rv = ofwo_setpass(fd);
+ } else {
+ rv = EX_CONFIG;
+ warnx("invalid security mode.");
+ }
+ } else
+ ofwo_printprop(exent->ex_prop, (const char *)buf, buflen);
+ if (pbuf != NULL)
+ free(pbuf);
+ return (rv);
+}
+
+static int
+ofwo_setpass(int fd)
+{
+ char pwd1[OFWO_MAXPWD + 1], pwd2[OFWO_MAXPWD + 1];
+
+ if (readpassphrase("New password:", pwd1, sizeof(pwd1),
+ RPP_ECHO_OFF | RPP_REQUIRE_TTY) == NULL ||
+ readpassphrase("Retype new password:", pwd2, sizeof(pwd2),
+ RPP_ECHO_OFF | RPP_REQUIRE_TTY) == NULL)
+ errx(EX_USAGE, "failed to get password.");
+ if (strlen(pwd1) == 0) {
+ printf("Password unchanged.\n");
+ return (EX_OK);
+ }
+ if (strcmp(pwd1, pwd2) != 0) {
+ printf("Mismatch - password unchanged.\n");
+ return (EX_USAGE);
+ }
+ ofw_setprop(fd, ofw_optnode(fd), "security-password", pwd1,
+ strlen(pwd1) + 1);
+ return (EX_OK);
+}
+
+static int
+ofwo_setstr(int fd, const void *buf, int buflen, const char *prop,
+ const char *val)
+{
+ void *pbuf;
+ int len, pblen, rv;
+ phandle_t optnode;
+ char *oval;
+
+ pblen = 0;
+ rv = EX_OK;
+ pbuf = NULL;
+ optnode = ofw_optnode(fd);
+ ofw_setprop(fd, optnode, prop, val, strlen(val) + 1);
+ len = ofw_getprop_alloc(fd, optnode, prop, &pbuf, &pblen, 1);
+ if (len < 0 || strncmp(val, (char *)pbuf, len) != 0) {
+ /*
+ * The value is too long for this property and the OFW has
+ * truncated it to fit or the value is illegal and a legal
+ * one has been written instead (e.g. attempted to write
+ * "foobar" to a "true"/"false"-property) - try to recover
+ * the old value.
+ */
+ rv = EX_DATAERR;
+ if ((oval = malloc(buflen + 1)) == NULL)
+ err(EX_OSERR, "malloc() failed.");
+ strncpy(oval, buf, buflen);
+ oval[buflen] = '\0';
+ len = ofw_setprop(fd, optnode, prop, oval, buflen + 1);
+ if (len != buflen)
+ errx(EX_IOERR, "recovery of old value failed.");
+ free(oval);
+ goto out;
+ }
+ printf("%s: %.*s%s->%s%.*s\n", prop, buflen, (const char *)buf,
+ buflen > 0 ? " " : "", len > 0 ? " " : "", len, (char *)pbuf);
+out:
+ if (pbuf != NULL)
+ free(pbuf);
+ return (rv);
+}
+
+void
+ofwo_dump(void)
+{
+ void *pbuf;
+ int fd, len, nlen, pblen;
+ phandle_t optnode;
+ char prop[OFWO_MAXPROP + 1];
+ const struct ofwo_extabent *ex;
+
+ pblen = 0;
+ pbuf = NULL;
+ fd = ofw_open(O_RDONLY);
+ optnode = ofw_optnode(fd);
+ for (nlen = ofw_firstprop(fd, optnode, prop, sizeof(prop)); nlen != 0;
+ nlen = ofw_nextprop(fd, optnode, prop, prop, sizeof(prop))) {
+ len = ofw_getprop_alloc(fd, optnode, prop, &pbuf, &pblen, 1);
+ if (len < 0)
+ continue;
+ if (strcmp(prop, "name") == 0)
+ continue;
+ for (ex = ofwo_extab; ex->ex_prop != NULL; ++ex)
+ if (strcmp(ex->ex_prop, prop) == 0)
+ break;
+ if (ex->ex_prop != NULL)
+ (*ex->ex_handler)(ex, fd, pbuf, len, NULL);
+ else
+ ofwo_printprop(prop, (char *)pbuf, len);
+ }
+ if (pbuf != NULL)
+ free(pbuf);
+ ofw_close(fd);
+}
+
+int
+ofwo_action(const char *prop, const char *val)
+{
+ void *pbuf;
+ int fd, len, pblen, rv;
+ const struct ofwo_extabent *ex;
+
+ pblen = 0;
+ rv = EX_OK;
+ pbuf = NULL;
+ if (strcmp(prop, "name") == 0)
+ return (EX_UNAVAILABLE);
+ if (val)
+ fd = ofw_open(O_RDWR);
+ else
+ fd = ofw_open(O_RDONLY);
+ len = ofw_getprop_alloc(fd, ofw_optnode(fd), prop, &pbuf, &pblen, 1);
+ if (len < 0) {
+ rv = EX_UNAVAILABLE;
+ goto out;
+ }
+ for (ex = ofwo_extab; ex->ex_prop != NULL; ++ex)
+ if (strcmp(ex->ex_prop, prop) == 0)
+ break;
+ if (ex->ex_prop != NULL)
+ rv = (*ex->ex_handler)(ex, fd, pbuf, len, val);
+ else if (val)
+ rv = ofwo_setstr(fd, pbuf, len, prop, val);
+ else
+ ofwo_printprop(prop, (char *)pbuf, len);
+out:
+ if (pbuf != NULL)
+ free(pbuf);
+ ofw_close(fd);
+ return (rv);
+}
diff --git a/usr.sbin/eeprom/ofw_options.h b/usr.sbin/eeprom/ofw_options.h
new file mode 100644
index 0000000..5bc6656
--- /dev/null
+++ b/usr.sbin/eeprom/ofw_options.h
@@ -0,0 +1,34 @@
+/*-
+ * Copyright (c) 2004 Marius Strobl
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (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 OFW_OPTIONS_H
+#define OFW_OPTIONS_H
+
+void ofwo_dump(void);
+int ofwo_action(const char *prop, const char *val);
+
+#endif /* OFW_OPTIONS_H */
diff --git a/usr.sbin/extattr/Makefile b/usr.sbin/extattr/Makefile
new file mode 100644
index 0000000..ae34975
--- /dev/null
+++ b/usr.sbin/extattr/Makefile
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+PROG= rmextattr
+MAN= rmextattr.8
+
+LINKS+= ${BINDIR}/rmextattr ${BINDIR}/getextattr
+LINKS+= ${BINDIR}/rmextattr ${BINDIR}/setextattr
+LINKS+= ${BINDIR}/rmextattr ${BINDIR}/lsextattr
+
+MLINKS+= rmextattr.8 setextattr.8
+MLINKS+= rmextattr.8 getextattr.8
+MLINKS+= rmextattr.8 lsextattr.8
+
+WARNS?= 5
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/extattr/rmextattr.8 b/usr.sbin/extattr/rmextattr.8
new file mode 100644
index 0000000..c51fa6d
--- /dev/null
+++ b/usr.sbin/extattr/rmextattr.8
@@ -0,0 +1,135 @@
+.\"-
+.\" Copyright (c) 2000, 2001 Robert N. M. Watson
+.\" Copyright (c) 2002 Networks Associates Technology, Inc.
+.\" All rights reserved.
+.\"
+.\" This software was developed for the FreeBSD Project by Poul-Henning
+.\" Kamp 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 30, 2000
+.Dt RMEXTATTR 8
+.Os
+.Sh NAME
+.Nm getextattr ,
+.Nm lsextattr ,
+.Nm rmextattr ,
+.Nm setextattr
+.Nd manipulate extended attributes
+.Sh SYNOPSIS
+.Nm getextattr
+.Op Fl fhqsx
+.Ar attrnamespace
+.Ar attrname
+.Ar filename ...
+.Nm lsextattr
+.Op Fl fhq
+.Ar attrnamespace
+.Ar filename ...
+.Nm rmextattr
+.Op Fl fhq
+.Ar attrnamespace
+.Ar attrname
+.Ar filename ...
+.Nm setextattr
+.Op Fl fhnq
+.Ar attrnamespace
+.Ar attrname
+.Ar attrvalue
+.Ar filename ...
+.Sh DESCRIPTION
+These
+utilities
+are user tools to manipulate the named extended attributes on files and
+directories.
+The
+.Ar attrnamespace
+argument should be the namespace of the attribute to retrieve: legal
+values are
+.Cm user
+and
+.Cm system .
+The
+.Ar attrname
+argument should be the name of the attribute,
+.Ar filename
+the name of the target file or directory,
+.Ar attrvalue
+a string to store in the attribute.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl f
+(Force.)
+Ignore errors on individual filenames and continue with
+the remaining arguments.
+.It Fl h
+(No follow.)
+If the file is a symbolic link, perform the operation on the
+link itself rather than the file that the link points to.
+.It Fl n
+.Dv ( NUL Ns
+-terminate.)
+.Dv NUL Ns
+-terminate the extent content written out.
+.It Fl q
+(Quiet.)
+Do not print out the pathname and suppress error messages.
+.It Fl s
+(Stringify.)
+Escape nonprinting characters and put quotes around the output.
+.It Fl x
+(Hex.)
+Print the output in hexadecimal.
+.El
+.Sh EXAMPLES
+.Bd -literal
+setextattr system md5 `md5 -q /boot/kernel/kernel` /boot/kernel/kernel
+getextattr system md5 /boot/kernel/kernel
+lsextattr system /boot/kernel/kernel
+rmextattr system md5 /boot/kernel/kernel
+.Ed
+.Sh SEE ALSO
+.Xr extattr 2 ,
+.Xr extattr 3 ,
+.Xr extattrctl 8 ,
+.Xr extattr 9
+.Sh HISTORY
+Extended attribute support was developed as part of the
+.Tn TrustedBSD
+Project,
+and introduced in
+.Fx 5.0 .
+It was developed to support security extensions requiring additional labels
+to be associated with each file or directory.
+.Sh AUTHORS
+.An Robert N M Watson
+.An Poul-Henning Kamp
+.Sh BUGS
+The
+.Nm setextattr
+utility can only be used to set attributes to strings.
diff --git a/usr.sbin/extattr/rmextattr.c b/usr.sbin/extattr/rmextattr.c
new file mode 100644
index 0000000..2b2689b
--- /dev/null
+++ b/usr.sbin/extattr/rmextattr.c
@@ -0,0 +1,286 @@
+/*-
+ * Copyright (c) 2002, 2003 Networks Associates Technology, Inc.
+ * Copyright (c) 2002 Poul-Henning Kamp.
+ * Copyright (c) 1999, 2000, 2001, 2002 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by Poul-Henning
+ * Kamp 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/types.h>
+#include <sys/uio.h>
+#include <sys/extattr.h>
+
+#include <libgen.h>
+#include <libutil.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <vis.h>
+#include <err.h>
+#include <errno.h>
+
+static enum { EADUNNO, EAGET, EASET, EARM, EALS } what = EADUNNO;
+
+static void __dead2
+usage(void)
+{
+
+ switch (what) {
+ case EAGET:
+ fprintf(stderr, "usage: getextattr [-fhqsx] attrnamespace");
+ fprintf(stderr, " attrname filename ...\n");
+ exit(-1);
+ case EASET:
+ fprintf(stderr, "usage: setextattr [-fhnq] attrnamespace");
+ fprintf(stderr, " attrname attrvalue filename ...\n");
+ exit(-1);
+ case EARM:
+ fprintf(stderr, "usage: rmextattr [-fhq] attrnamespace");
+ fprintf(stderr, " attrname filename ...\n");
+ exit(-1);
+ case EALS:
+ fprintf(stderr, "usage: lsextattr [-fhq] attrnamespace");
+ fprintf(stderr, " filename ...\n");
+ exit(-1);
+ case EADUNNO:
+ default:
+ fprintf(stderr, "usage: (getextattr|lsextattr|rmextattr");
+ fprintf(stderr, "|setextattr)\n");
+ exit (-1);
+ }
+}
+
+static void
+mkbuf(char **buf, int *oldlen, int newlen)
+{
+
+ if (*oldlen >= newlen)
+ return;
+ if (*buf != NULL)
+ free(*buf);
+ *buf = malloc(newlen);
+ if (*buf == NULL)
+ err(1, "malloc");
+ *oldlen = newlen;
+ return;
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *buf, *visbuf, *p;
+
+ const char *options, *attrname;
+ int buflen, visbuflen, ch, error, i, arg_counter, attrnamespace,
+ minargc;
+
+ int flag_force = 0;
+ int flag_nofollow = 0;
+ int flag_null = 0;
+ int flag_quiet = 0;
+ int flag_string = 0;
+ int flag_hex = 0;
+
+ visbuflen = buflen = 0;
+ visbuf = buf = NULL;
+
+ p = basename(argv[0]);
+ if (p == NULL)
+ p = argv[0];
+ if (!strcmp(p, "getextattr")) {
+ what = EAGET;
+ options = "fhqsx";
+ minargc = 3;
+ } else if (!strcmp(p, "setextattr")) {
+ what = EASET;
+ options = "fhnq";
+ minargc = 4;
+ } else if (!strcmp(p, "rmextattr")) {
+ what = EARM;
+ options = "fhq";
+ minargc = 3;
+ } else if (!strcmp(p, "lsextattr")) {
+ what = EALS;
+ options = "fhq";
+ minargc = 2;
+ } else {
+ usage();
+ }
+
+ while ((ch = getopt(argc, argv, options)) != -1) {
+ switch (ch) {
+ case 'f':
+ flag_force = 1;
+ break;
+ case 'h':
+ flag_nofollow = 1;
+ break;
+ case 'n':
+ flag_null = 1;
+ break;
+ case 'q':
+ flag_quiet = 1;
+ break;
+ case 's':
+ flag_string = 1;
+ break;
+ case 'x':
+ flag_hex = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < minargc)
+ usage();
+
+ error = extattr_string_to_namespace(argv[0], &attrnamespace);
+ if (error)
+ err(-1, "%s", argv[0]);
+ argc--; argv++;
+
+ if (what != EALS) {
+ attrname = argv[0];
+ argc--; argv++;
+ } else
+ attrname = NULL;
+
+ if (what == EASET) {
+ mkbuf(&buf, &buflen, strlen(argv[0]) + 1);
+ strcpy(buf, argv[0]);
+ argc--; argv++;
+ }
+
+ for (arg_counter = 0; arg_counter < argc; arg_counter++) {
+ switch (what) {
+ case EARM:
+ if (flag_nofollow)
+ error = extattr_delete_link(argv[arg_counter],
+ attrnamespace, attrname);
+ else
+ error = extattr_delete_file(argv[arg_counter],
+ attrnamespace, attrname);
+ if (error >= 0)
+ continue;
+ break;
+ case EASET:
+ if (flag_nofollow)
+ error = extattr_set_link(argv[arg_counter],
+ attrnamespace, attrname, buf,
+ strlen(buf) + flag_null);
+ else
+ error = extattr_set_file(argv[arg_counter],
+ attrnamespace, attrname, buf,
+ strlen(buf) + flag_null);
+ if (error >= 0)
+ continue;
+ break;
+ case EALS:
+ if (flag_nofollow)
+ error = extattr_list_link(argv[arg_counter],
+ attrnamespace, NULL, 0);
+ else
+ error = extattr_list_file(argv[arg_counter],
+ attrnamespace, NULL, 0);
+ if (error < 0)
+ break;
+ mkbuf(&buf, &buflen, error);
+ if (flag_nofollow)
+ error = extattr_list_link(argv[arg_counter],
+ attrnamespace, buf, buflen);
+ else
+ error = extattr_list_file(argv[arg_counter],
+ attrnamespace, buf, buflen);
+ if (error < 0)
+ break;
+ if (!flag_quiet)
+ printf("%s\t", argv[arg_counter]);
+ for (i = 0; i < error; i += buf[i] + 1)
+ printf("%s%*.*s", i ? "\t" : "",
+ buf[i], buf[i], buf + i + 1);
+ printf("\n");
+ continue;
+ case EAGET:
+ if (flag_nofollow)
+ error = extattr_get_link(argv[arg_counter],
+ attrnamespace, attrname, NULL, 0);
+ else
+ error = extattr_get_file(argv[arg_counter],
+ attrnamespace, attrname, NULL, 0);
+ if (error < 0)
+ break;
+ mkbuf(&buf, &buflen, error);
+ if (flag_nofollow)
+ error = extattr_get_link(argv[arg_counter],
+ attrnamespace, attrname, buf, buflen);
+ else
+ error = extattr_get_file(argv[arg_counter],
+ attrnamespace, attrname, buf, buflen);
+ if (error < 0)
+ break;
+ if (!flag_quiet)
+ printf("%s\t", argv[arg_counter]);
+ if (flag_string) {
+ mkbuf(&visbuf, &visbuflen, error * 4 + 1);
+ strvisx(visbuf, buf, error,
+ VIS_SAFE | VIS_WHITE);
+ printf("\"%s\"\n", visbuf);
+ continue;
+ } else if (flag_hex) {
+ for (i = 0; i < error; i++)
+ printf("%s%02x", i ? " " : "",
+ buf[i]);
+ printf("\n");
+ continue;
+ } else {
+ fwrite(buf, buflen, 1, stdout);
+ printf("\n");
+ continue;
+ }
+ default:
+ break;
+ }
+ if (!flag_quiet)
+ warn("%s: failed", argv[arg_counter]);
+ if (flag_force)
+ continue;
+ return(1);
+ }
+ return (0);
+}
diff --git a/usr.sbin/extattrctl/Makefile b/usr.sbin/extattrctl/Makefile
new file mode 100644
index 0000000..5eb1945
--- /dev/null
+++ b/usr.sbin/extattrctl/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= extattrctl
+MAN= extattrctl.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/extattrctl/extattrctl.8 b/usr.sbin/extattrctl/extattrctl.8
new file mode 100644
index 0000000..c5850ab4
--- /dev/null
+++ b/usr.sbin/extattrctl/extattrctl.8
@@ -0,0 +1,181 @@
+.\"-
+.\" Copyright (c) 2000-2001 Robert N. M. Watson
+.\" All rights reserved.
+.\"
+.\" This software was developed by Robert Watson for the TrustedBSD 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.
+.\"
+.\" 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$
+.\"
+.\" Developed by the TrustedBSD Project.
+.\" Support for file system extended attribute.
+.\"
+.Dd March 30, 2000
+.Dt EXTATTRCTL 8
+.Os
+.Sh NAME
+.Nm extattrctl
+.Nd manage UFS1 extended attributes
+.Sh SYNOPSIS
+.Nm
+.Cm start
+.Ar path
+.Nm
+.Cm stop
+.Ar path
+.Nm
+.Cm initattr
+.Op Fl f
+.Op Fl p Ar path
+.Ar attrsize
+.Ar attrfile
+.Nm
+.Cm showattr
+.Ar attrfile
+.Nm
+.Cm enable
+.Ar path
+.Ar attrnamespace
+.Ar attrname
+.Ar attrfile
+.Nm
+.Cm disable
+.Ar path
+.Ar attrnamespace
+.Ar attrname
+.Sh DESCRIPTION
+The
+.Nm
+utility
+is the management utility for extended attributes over the UFS1 file system.
+It allows the starting and stopping of extended attributes on a file system,
+as well as initialization of attribute backing files, and enabling and
+disabling of specific extended attributes on a file system.
+.Pp
+The first argument on the command line indicates the operation to be
+performed.
+Operation must be one of the following:
+.Bl -tag -width indent
+.It Cm start Ar path
+Start extended attribute support on the file system named using
+.Ar path .
+The file system must be an UFS1 file system, and the UFS_EXTATTR kernel
+option must have been enabled.
+.It Cm stop Ar path
+Stop extended attribute support on the file system named using
+.Ar path .
+Extended attribute support must previously have been started.
+.It Xo
+.Cm initattr
+.Op Fl f
+.Op Fl p Ar path
+.Ar attrsize attrfile
+.Xc
+Create and initialize a file to use as an attribute backing file.
+You must specify a maximum per-inode size for the attribute in bytes in
+.Ar attrsize ,
+as well as the file where the attribute will be stored, using
+.Ar attrfile .
+.Pp
+The
+.Fl f
+argument may be used to indicate that it is alright to overwrite an
+existing attribute backing file; otherwise, if the target file exists,
+an error will be returned.
+.Pp
+The
+.Fl p Ar path
+argument may be used to preallocate space for all attributes rather than
+relying on sparse files to conserve space.
+This has the advantage of guaranteeing that space will be available
+for attributes when they are written, preventing low disk space conditions
+from denying attribute service.
+.Pp
+This file should not exist before running
+.Cm initattr .
+.It Cm showattr Ar attrfile
+Show the attribute header values in the attribute file named by
+.Ar attrfile .
+.It Cm enable Ar path attrnamespace attrname attrfile
+Enable an attribute named
+.Ar attrname
+in the namespace
+.Ar attrnamespace
+on the file system identified using
+.Ar path ,
+and backed by initialized attribute file
+.Ar attrfile .
+Available namespaces are "user" and "system".
+The backing file must have been initialized using
+.Cm initattr
+before its first use.
+Attributes must have been started on the file system prior to the
+enabling of any attributes.
+.It Cm disable Ar path attrnamespace attrname
+Disable the attributed named
+.Ar attrname
+in namespace
+.Ar attrnamespace
+on the file system identified by
+.Ar path .
+Available namespaces are "user" and "system".
+The file system must have attributes started on it, and the attribute
+most have been enabled using
+.Cm enable .
+.El
+.Sh EXAMPLES
+.Dl extattrctl start /
+.Pp
+Start extended attributes on the root file system.
+.Pp
+.Dl extattrctl initattr 17 /.attribute/system/md5
+.Pp
+Create an attribute backing file in /.attribute/system/md5, and set the maximum
+size of each attribute to 17 bytes, with a sparse file used for storing
+the attributes.
+.Pp
+.Dl extattrctl enable / system md5 /.attribute/system/md5
+.Pp
+Enable an attribute named md5 on the root file system, backed from the file
+/.attribute/system/md5.
+.Pp
+.Dl extattrctl disable / md5
+.Pp
+Disable the attribute named md5 on the root file system.
+.Pp
+.Dl extattrctl stop /
+.Pp
+Stop extended attributes on the root file system.
+.Sh SEE ALSO
+.Xr ffs 7 ,
+.Xr getextattr 8 ,
+.Xr setextattr 8 ,
+.Xr extattr 9
+.Sh HISTORY
+Extended attribute support was developed as part of the TrustedBSD Project,
+and introduced in
+.Fx 5.0 .
+It was developed to support security extensions requiring additional labels
+to be associated with each file or directory.
+.Sh AUTHORS
+Robert N M Watson
diff --git a/usr.sbin/extattrctl/extattrctl.c b/usr.sbin/extattrctl/extattrctl.c
new file mode 100644
index 0000000..103fcd3
--- /dev/null
+++ b/usr.sbin/extattrctl/extattrctl.c
@@ -0,0 +1,259 @@
+/*-
+ * Copyright (c) 1999-2002 Robert N. M. Watson
+ * All rights reserved.
+ *
+ * This software was developed by Robert Watson for the TrustedBSD 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.
+ *
+ * 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$
+ */
+/*
+ * Developed by the TrustedBSD Project.
+ * Support for file system extended attribute.
+ */
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <sys/extattr.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <ufs/ufs/extattr.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <libutil.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int initattr(int argc, char *argv[]);
+int showattr(int argc, char *argv[]);
+long num_inodes_by_path(char *path);
+void usage(void);
+
+void
+usage()
+{
+
+ fprintf(stderr,
+ "usage:\n"
+ " extattrctl start path\n"
+ " extattrctl stop path\n"
+ " extattrctl initattr [-f] [-p path] attrsize attrfile\n"
+ " extattrctl showattr attrfile\n"
+ " extattrctl enable path attrnamespace attrname attrfile\n"
+ " extattrctl disable path attrnamespace attrname\n");
+ exit(-1);
+}
+
+long
+num_inodes_by_path(char *path)
+{
+ struct statfs buf;
+ int error;
+
+ error = statfs(path, &buf);
+ if (error) {
+ perror("statfs");
+ return (-1);
+ }
+
+ return (buf.f_files);
+}
+
+static const char zero_buf[8192];
+
+int
+initattr(int argc, char *argv[])
+{
+ struct ufs_extattr_fileheader uef;
+ char *fs_path = NULL;
+ int ch, i, error, flags;
+ ssize_t wlen;
+ size_t easize;
+
+ flags = O_CREAT | O_WRONLY | O_TRUNC | O_EXCL;
+ optind = 0;
+ while ((ch = getopt(argc, argv, "fp:r:w:")) != -1)
+ switch (ch) {
+ case 'f':
+ flags &= ~O_EXCL;
+ break;
+ case 'p':
+ fs_path = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ usage();
+
+ error = 0;
+ if ((i = open(argv[1], flags, 0600)) == -1) {
+ /* unable to open file */
+ perror(argv[1]);
+ return (-1);
+ }
+ uef.uef_magic = UFS_EXTATTR_MAGIC;
+ uef.uef_version = UFS_EXTATTR_VERSION;
+ uef.uef_size = atoi(argv[0]);
+ if (write(i, &uef, sizeof(uef)) == -1)
+ error = -1;
+ else if (fs_path != NULL) {
+ easize = (sizeof uef + uef.uef_size) *
+ num_inodes_by_path(fs_path);
+ while (easize > 0) {
+ if (easize > sizeof zero_buf)
+ wlen = write(i, zero_buf, sizeof zero_buf);
+ else
+ wlen = write(i, zero_buf, easize);
+ if (wlen == -1) {
+ error = -1;
+ break;
+ }
+ easize -= wlen;
+ }
+ }
+ if (error == -1) {
+ perror(argv[1]);
+ unlink(argv[1]);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+showattr(int argc, char *argv[])
+{
+ struct ufs_extattr_fileheader uef;
+ int i, fd;
+
+ if (argc != 1)
+ usage();
+
+ fd = open(argv[0], O_RDONLY);
+ if (fd == -1) {
+ perror(argv[0]);
+ return (-1);
+ }
+
+ i = read(fd, &uef, sizeof(uef));
+ if (i == -1) {
+ perror(argv[0]);
+ return (-1);
+ }
+ if (i != sizeof(uef)) {
+ fprintf(stderr, "%s: invalid file header\n", argv[0]);
+ return (-1);
+ }
+
+ if (uef.uef_magic != UFS_EXTATTR_MAGIC) {
+ fprintf(stderr, "%s: bad magic\n", argv[0]);
+ return (-1);
+ }
+
+ printf("%s: version %d, size %d\n", argv[0], uef.uef_version,
+ uef.uef_size);
+
+ return (0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int error = 0, attrnamespace;
+
+ if (argc < 2)
+ usage();
+
+ if (!strcmp(argv[1], "start")) {
+ if (argc != 3)
+ usage();
+ error = extattrctl(argv[2], UFS_EXTATTR_CMD_START, NULL, 0,
+ NULL);
+ if (error) {
+ perror("extattrctl start");
+ return (-1);
+ }
+ } else if (!strcmp(argv[1], "stop")) {
+ if (argc != 3)
+ usage();
+ error = extattrctl(argv[2], UFS_EXTATTR_CMD_STOP, NULL, 0,
+ NULL);
+ if (error) {
+ perror("extattrctl stop");
+ return (-1);
+ }
+ } else if (!strcmp(argv[1], "enable")) {
+ if (argc != 6)
+ usage();
+ error = extattr_string_to_namespace(argv[3], &attrnamespace);
+ if (error) {
+ perror("extattrctl enable");
+ return (-1);
+ }
+ error = extattrctl(argv[2], UFS_EXTATTR_CMD_ENABLE, argv[5],
+ attrnamespace, argv[4]);
+ if (error) {
+ perror("extattrctl enable");
+ return (-1);
+ }
+ } else if (!strcmp(argv[1], "disable")) {
+ if (argc != 5)
+ usage();
+ error = extattr_string_to_namespace(argv[3], &attrnamespace);
+ if (error) {
+ perror("extattrctl disable");
+ return (-1);
+ }
+ error = extattrctl(argv[2], UFS_EXTATTR_CMD_DISABLE, NULL,
+ attrnamespace, argv[4]);
+ if (error) {
+ perror("extattrctl disable");
+ return (-1);
+ }
+ } else if (!strcmp(argv[1], "initattr")) {
+ argc -= 2;
+ argv += 2;
+ error = initattr(argc, argv);
+ if (error)
+ return (-1);
+ } else if (!strcmp(argv[1], "showattr")) {
+ argc -= 2;
+ argv += 2;
+ error = showattr(argc, argv);
+ if (error)
+ return (-1);
+ } else
+ usage();
+
+ return (0);
+}
diff --git a/usr.sbin/faithd/Makefile b/usr.sbin/faithd/Makefile
new file mode 100644
index 0000000..e1898ed
--- /dev/null
+++ b/usr.sbin/faithd/Makefile
@@ -0,0 +1,23 @@
+# 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$
+
+PROG= faithd
+MAN= faithd.8
+SRCS= faithd.c tcp.c ftp.c prefix.c
+
+CFLAGS+= -DHAVE_POLL_H
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/faithd/README b/usr.sbin/faithd/README
new file mode 100644
index 0000000..6628bf6
--- /dev/null
+++ b/usr.sbin/faithd/README
@@ -0,0 +1,148 @@
+Configuring FAITH IPv6-to-IPv4 TCP relay
+
+Kazu Yamamoto and Jun-ichiro itojun Hagino
+$KAME: README,v 1.10 2003/01/06 21:40:33 sumikawa Exp $
+$FreeBSD$
+
+
+Introduction
+============
+
+FAITH is an IPv6-to-IPv4 TCP relay. It performs tcp relay just as some of
+firewall-oriented gateway does, but between IPv6 and IPv4 with address
+translation.
+TCP connections has to be made from IPv6 node to IPv4 node. FAITH will
+not relay connections for the opposite direction.
+To perform relays, FAITH daemon needs to be executed on a router between
+your local IPv6 site and outside IPv4 network. The daemon needs to be
+invoked per each TCP services (TCP port number).
+
+ IPv4 node "dest" = 123.4.5.6
+ |
+ [[[[ outside IPv4 ocean ]]]]
+ |
+ node that runs FAITH-daemon (usually a router)
+ |
+ ==+=====+===+==== IPv6, or IPv4/v6 network in your site ^
+ | | | connection
+ clients IPv6 node "src" |
+
+You will have to allocate an IPv6 address prefix to map IPv4 addresses into.
+The following description uses 3ffe:0501:ffff:0000:: as example.
+Please use a prefix which belongs to your site.
+FAITH will make it possible to make an IPv6 TCP connection From IPv6 node
+"src", toward IPv4 node "dest", by specifying FAITH-mapped address
+3ffe:0501:ffff:0000::123.4.5.6
+(which is, 3ffe:0501:ffff:0000:0000:0000:7b04:0506).
+The address mapping can be performed by hand:-), by special nameserver on
+the network, or by special resolver on the source node.
+
+
+Setup
+=====
+
+The following example assumes:
+- You have assigned 3ffe:0501:ffff:0000:: as FAITH adderss prefix.
+- You are willing to provide IPv6-to IPv4 TCP relay for telnet.
+
+<<On the translating router on which faithd runs>>
+
+(1) If you have IPv6 TCP server for the "telnet" service, i.e. telnetd via
+ inet6d, disable that daemon. Comment out the line from "inet6d.conf"
+ and send the HUP signal to "inet6d".
+
+(2) Execute sysctl as root to enable FAITH support in the kernel.
+
+ # sysctl net.inet6.ip6.keepfaith=1
+
+(3) Route packets toward FAITH prefix into "faith0" interface.
+
+ # ifconfig faith0 up
+ # route add -inet6 3ffe:0501:ffff:0000:: -prefixlen 64 ::1
+ # route change -inet6 3ffe:0501:ffff:0000:: -prefixlen 64 -ifp faith0
+
+(4) Execute "faithd" by root as follows:
+
+ # faithd telnet /usr/libexec/telnetd telnetd
+
+ 1st argument is a service name you are willing to provide TCP relay.
+ (it can be specified either by number "23" or by string "telnet")
+ 2nd argument is a path name for local IPv6 TCP server. If there is a
+ connection toward the router itself, this program will be invoked.
+ 3rd and the following arguments are arguments for the local IPv6 TCP
+ server. (3rd argument is typically the program name without its path.)
+
+ More examples:
+
+ # faithd ftpd /usr/libexec/ftpd ftpd -l
+ # faithd sshd
+
+If inetd(8) on your platform have special support for faithd, it is possible
+to setup faithd services via inetd(8). Consult manpage for details.
+
+
+<<Routing>>
+
+(4) Make sure that packets whose destinations match the prefix can
+reach from the IPv6 host to the translating router.
+
+<<On the IPv6 host>>
+
+There are two ways to translate IPv4 address to IPv6 address:
+ (a) Faked by DNS
+ (b) Faked by /etc/hosts.
+
+(5.a) Install "newbie" and set up FAITH mode. See kit/ports/newbie.
+
+(5.b) Add an entry into /etc/hosts so that you can resolve hostname into
+faked IPv6 addrss. For example, add the following line for www.netbsd.org:
+
+ 3ffe:0501:ffff:0000::140.160.140.252 www.netbsd.org
+
+<<On the translating router on which faithd runs.>>
+
+(6) To see if "faithd" works, watch "/var/log/daemon". Note: please
+setup "/etc/syslog.conf" so that LOG_DAEMON messages are to be stored
+in "/var/log/daemon".
+
+ <e.g.>
+ daemon.* /var/log/daemon
+
+
+Access control
+==============
+
+Since faithd implements TCP relaying service, it is critical to implement
+proper access control to cope with malicious use. Bad guy may try to
+use your relay router to circumvent access controls, or may try to
+abuse your network (like sending SPAMs from IPv4 address that belong to you).
+Install IPv6 packet filter directives that would reject traffic from
+unwanted source. If you are using inetd-based setup, you may be able to
+use access control mechanisms in inetd.
+
+
+Advanced configuration
+======================
+
+If you would like to restrict IPv4 destination for translation, you may
+want to do the following:
+
+ # route add -inet6 3ffe:0501:ffff:0000::123.0.0.0 -prefixlen 104 ::1
+ # route change -inet6 3ffe:0501:ffff:0000::123.0.0.0 -prefixlen 104 \
+ -ifp faith0
+
+By this way, you can restrict IPv4 destination to 123.0.0.0/8.
+You may also want to reject packets toward 3ffe:0501:ffff:0000::/64 which
+is not in 3ffe:0501:ffff:0000::123.0.0.0/104. This will be left as excerside
+for the reader.
+
+By doing this, you will be able to provide your IPv4 web server to outside
+IPv6 customers, without risks of unwanted open relays.
+
+ [[[[ IPv6 network outside ]]]] |
+ | | connection
+ node that runs FAITH-daemon (usually a router) v
+ |
+ ========+======== IPv4/v6 network in your site
+ | (123.0.0.0/8)
+ IPv4 web server
diff --git a/usr.sbin/faithd/faithd.8 b/usr.sbin/faithd/faithd.8
new file mode 100644
index 0000000..5d16989
--- /dev/null
+++ b/usr.sbin/faithd/faithd.8
@@ -0,0 +1,407 @@
+.\" $KAME: faithd.8,v 1.37 2002/05/09 14:21:23 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 FAITHD 8
+.Os
+.Sh NAME
+.Nm faithd
+.Nd FAITH IPv6/v4 translator daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl dp
+.Op Fl f Ar configfile
+.Ar service
+.Op Ar serverpath Op Ar serverargs
+.Sh DESCRIPTION
+The
+.Nm
+utility provides IPv6-to-IPv4 TCP relay.
+It must be used on an IPv4/v6 dual stack router.
+.Pp
+When
+.Nm
+receives
+.Tn TCPv6
+traffic,
+.Nm
+will relay the
+.Tn TCPv6
+traffic to
+.Tn TCPv4 .
+Destination for relayed
+.Tn TCPv4
+connection will be determined by the last 4 octets of the original
+.Tn IPv6
+destination.
+For example, if
+.Li 3ffe:0501:4819:ffff::
+is reserved for
+.Nm ,
+and the
+.Tn TCPv6
+destination address is
+.Li 3ffe:0501:4819:ffff::0a01:0101 ,
+the traffic will be relayed to IPv4 destination
+.Li 10.1.1.1 .
+.Pp
+To use
+.Nm
+translation service,
+an IPv6 address prefix must be reserved for mapping IPv4 addresses into.
+Kernel must be properly configured to route all the TCP connection
+toward the reserved IPv6 address prefix into the
+.Xr faith 4
+pseudo interface, by using
+.Xr route 8
+command.
+Also,
+.Xr sysctl 8
+should be used to configure
+.Dv net.inet6.ip6.keepfaith
+to
+.Dv 1 .
+.Pp
+The router must be configured to capture all the TCP traffic
+toward reserved
+.Tn IPv6
+address prefix, by using
+.Xr route 8
+and
+.Xr sysctl 8
+commands.
+.Pp
+The
+.Nm
+utility needs a special name-to-address translation logic, so that
+hostnames gets resolved into special
+.Tn IPv6
+address prefix.
+For small-scale installation, use
+.Xr hosts 5 .
+For large-scale installation, it is useful to have
+a DNS server with special address translation support.
+An implementation called
+.Nm totd
+is available
+at
+.Pa http://www.vermicelli.pasta.cs.uit.no/ipv6/software.html .
+Make sure you do not propagate translated DNS records to normal DNS cloud,
+it is highly harmful.
+.Ss Daemon mode
+When
+.Nm
+is invoked as a standalone program,
+.Nm
+will daemonize itself.
+The
+.Nm
+utility will listen to
+.Tn TCPv6
+port
+.Ar service .
+If
+.Tn TCPv6
+traffic to port
+.Ar service
+is found, it relays the connection.
+.Pp
+Since
+.Nm
+listens to TCP port
+.Ar service ,
+it is not possible to run local TCP daemons for port
+.Ar service
+on the router, using
+.Xr inetd 8
+or other standard mechanisms.
+By specifying
+.Ar serverpath
+to
+.Nm ,
+you can run local daemons on the router.
+The
+.Nm
+utility will invoke local daemon at
+.Ar serverpath
+if the destination address is local interface address,
+and will perform translation to IPv4 TCP in other cases.
+You can also specify
+.Ar serverargs
+for the arguments for the local daemon.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d
+Debugging information will be generated using
+.Xr syslog 3 .
+.It Fl f Ar configfile
+Specify a configuration file for access control.
+See below.
+.It Fl p
+Use privileged TCP port number as source port,
+for IPv4 TCP connection toward final destination.
+For relaying
+.Xr ftp 1 ,
+this flag is not necessary as special program code is supplied.
+.El
+.Pp
+The
+.Nm
+utility will relay both normal and out-of-band TCP data.
+It is capable of emulating TCP half close as well.
+The
+.Nm
+utility includes special support for protocols used by
+.Xr ftp 1 .
+When translating FTP protocol,
+.Nm
+translates network level addresses in
+.Li PORT/LPRT/EPRT
+and
+.Li PASV/LPSV/EPSV
+commands.
+.Pp
+Inactive sessions will be disconnected in 30 minutes,
+to avoid stale sessions from chewing up resources.
+This may be inappropriate for some of the services
+(should this be configurable?).
+.Ss inetd mode
+When
+.Nm
+is invoked via
+.Xr inetd 8 ,
+.Nm
+will handle connection passed from standard input.
+If the connection endpoint is in the reserved IPv6 address prefix,
+.Nm
+will relay the connection.
+Otherwise,
+.Nm
+will invoke service-specific daemon like
+.Xr telnetd 8 ,
+by using the command argument passed from
+.Xr inetd 8 .
+.Pp
+The
+.Nm
+utility determines operation mode by the local TCP port number,
+and enables special protocol handling whenever necessary/possible.
+For example, if
+.Nm
+is invoked via
+.Xr inetd 8
+on FTP port, it will operate as a FTP relay.
+.Pp
+The operation mode requires special support for
+.Nm
+in
+.Xr inetd 8 .
+.Ss Access control
+To prevent malicious accesses,
+.Nm
+implements a simple address-based access control.
+With
+.Pa /etc/faithd.conf
+(or
+.Ar configfile
+specified by
+.Fl f ) ,
+.Nm
+will avoid relaying unwanted traffic.
+The
+.Pa faithd.conf
+contains directives with the following format:
+.Bl -bullet
+.It
+.Ar src Ns / Ns Ar slen Cm deny Ar dst Ns / Ns Ar dlen
+.Pp
+If the source address of a query matches
+.Ar src Ns / Ns Ar slen ,
+and the translated destination address matches
+.Ar dst Ns / Ns Ar dlen ,
+deny the connection.
+.It
+.Ar src Ns / Ns Ar slen Cm permit Ar dst Ns / Ns Ar dlen
+.Pp
+If the source address of a query matches
+.Ar src Ns / Ns Ar slen ,
+and the translated destination address matches
+.Ar dst Ns / Ns Ar dlen ,
+permit the connection.
+.El
+.Pp
+The directives are evaluated in sequence,
+and the first matching entry will be effective.
+If there is no match
+(if we reach the end of the ruleset)
+the traffic will be denied.
+.Pp
+With inetd mode,
+traffic may be filtered by using access control functionality in
+.Xr inetd 8 .
+.Sh EXIT STATUS
+The
+.Nm
+utility exits with
+.Dv EXIT_SUCCESS
+.Pq 0
+on success, and
+.Dv EXIT_FAILURE
+.Pq 1
+on error.
+.Sh EXAMPLES
+Before invoking
+.Nm ,
+.Xr faith 4
+interface has to be configured properly.
+.Bd -literal -offset
+# sysctl net.inet6.ip6.accept_rtadv=0
+# sysctl net.inet6.ip6.forwarding=1
+# sysctl net.inet6.ip6.keepfaith=1
+# ifconfig faith0 up
+# route add -inet6 3ffe:501:4819:ffff:: -prefixlen 96 ::1
+# route change -inet6 3ffe:501:4819:ffff:: -prefixlen 96 -ifp faith0
+.Ed
+.Ss Daemon mode samples
+To translate
+.Li telnet
+service, and provide no local telnet service, invoke
+.Nm
+as follows:
+.Bd -literal -offset
+# faithd telnet
+.Ed
+.Pp
+If you would like to provide local telnet service via
+.Xr telnetd 8
+on
+.Pa /usr/libexec/telnetd ,
+use the following command line:
+.Bd -literal -offset
+# faithd telnet /usr/libexec/telnetd telnetd
+.Ed
+.Pp
+If you would like to pass extra arguments to the local daemon:
+.Bd -literal -offset
+# faithd ftp /usr/libexec/ftpd ftpd -l
+.Ed
+.Pp
+Here are some other examples.
+You may need
+.Fl p
+if the service checks the source port range.
+.Bd -literal -offset
+# faithd ssh
+# faithd telnet /usr/libexec/telnetd telnetd
+.Ed
+.Ss inetd mode samples
+Add the following lines into
+.Xr inetd.conf 5 .
+Syntax may vary depending upon your operating system.
+.Bd -literal -offset
+telnet stream tcp6/faith nowait root faithd telnetd
+ftp stream tcp6/faith nowait root faithd ftpd -l
+ssh stream tcp6/faith nowait root faithd /usr/sbin/sshd -i
+.Ed
+.Pp
+.Xr inetd 8
+will open listening sockets with enabling kernel TCP relay support.
+Whenever connection comes in,
+.Nm
+will be invoked by
+.Xr inetd 8 .
+If it the connection endpoint is in the reserved IPv6 address prefix.
+The
+.Nm
+utility will relay the connection.
+Otherwise,
+.Nm
+will invoke service-specific daemon like
+.Xr telnetd 8 .
+.Ss Access control samples
+The following illustrates a simple
+.Pa faithd.conf
+setting.
+.Bd -literal -offset
+# permit anyone from 3ffe:501:ffff::/48 to use the translator,
+# to connect to the following IPv4 destinations:
+# - any location except 10.0.0.0/8 and 127.0.0.0/8.
+# Permit no other connections.
+#
+3ffe:501:ffff::/48 deny 10.0.0.0/8
+3ffe:501:ffff::/48 deny 127.0.0.0/8
+3ffe:501:ffff::/48 permit 0.0.0.0/0
+.Ed
+.Sh SEE ALSO
+.Xr faith 4 ,
+.Xr route 8 ,
+.Xr sysctl 8
+.Rs
+.%A Jun-ichiro itojun Hagino
+.%A Kazu Yamamoto
+.%T "An IPv6-to-IPv4 transport relay translator"
+.%B RFC3142
+.%O ftp://ftp.isi.edu/in-notes/rfc3142.txt
+.%D June 2001
+.Re
+.\"
+.Sh HISTORY
+The
+.Nm
+utility first appeared in WIDE Hydrangea IPv6 protocol stack kit.
+.\"
+.Pp
+IPv6 and IPsec support based on the KAME Project (http://www.kame.net/) stack
+was initially integrated into
+.Fx 4.0
+.Sh SECURITY CONSIDERATIONS
+It is very insecure to use IP-address based authentication, for connections relayed by
+.Nm ,
+and any other TCP relaying services.
+.Pp
+Administrators are advised to limit accesses to
+.Nm
+using
+.Pa faithd.conf ,
+or by using IPv6 packet filters.
+It is to protect
+.Nm
+service from malicious parties and avoid theft of service/bandwidth.
+IPv6 destination address can be limited by
+carefully configuring routing entries that points to
+.Xr faith 4 ,
+using
+.Xr route 8 .
+IPv6 source address needs to be filtered by using packet filters.
+Documents listed in
+.Sx SEE ALSO
+have more discussions on this topic.
diff --git a/usr.sbin/faithd/faithd.c b/usr.sbin/faithd/faithd.c
new file mode 100644
index 0000000..f192fbd
--- /dev/null
+++ b/usr.sbin/faithd/faithd.c
@@ -0,0 +1,908 @@
+/* $KAME: faithd.c,v 1.67 2003/10/16 05:26:21 itojun Exp $ */
+
+/*
+ * Copyright (C) 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.
+ */
+
+/*
+ * User level translator from IPv6 to IPv4.
+ *
+ * Usage: faithd [<port> <progpath> <arg1(progname)> <arg2> ...]
+ * e.g. faithd telnet /usr/libexec/telnetd telnetd
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <libutil.h>
+
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <termios.h>
+
+#include <net/if_types.h>
+#ifdef IFT_FAITH
+# define USE_ROUTE
+# include <net/if.h>
+# include <net/route.h>
+# include <net/if_dl.h>
+#endif
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <ifaddrs.h>
+
+#include "faithd.h"
+#include "prefix.h"
+
+char *serverpath = NULL;
+char *serverarg[MAXARGV + 1];
+static char *faithdname = NULL;
+char logname[BUFSIZ];
+char procname[BUFSIZ];
+
+struct myaddrs {
+ struct myaddrs *next;
+ struct sockaddr *addr;
+};
+struct myaddrs *myaddrs = NULL;
+
+static const char *service;
+#ifdef USE_ROUTE
+static int sockfd = 0;
+#endif
+int dflag = 0;
+static int pflag = 0;
+static int inetd = 0;
+static char *configfile = NULL;
+
+int main(int, char **);
+static int inetd_main(int, char **);
+static int daemon_main(int, char **);
+static void play_service(int);
+static void play_child(int, struct sockaddr *);
+static int faith_prefix(struct sockaddr *);
+static int map6to4(struct sockaddr_in6 *, struct sockaddr_in *);
+static void sig_child(int);
+static void sig_terminate(int);
+static void start_daemon(void);
+static void exit_stderr(const char *, ...)
+ __attribute__((__format__(__printf__, 1, 2)));
+static void grab_myaddrs(void);
+static void free_myaddrs(void);
+static void update_myaddrs(void);
+static void usage(void);
+
+int
+main(int argc, char **argv)
+{
+
+ /*
+ * Initializing stuff
+ */
+
+ faithdname = strrchr(argv[0], '/');
+ if (faithdname)
+ faithdname++;
+ else
+ faithdname = argv[0];
+
+ if (strcmp(faithdname, "faithd") != 0) {
+ inetd = 1;
+ return inetd_main(argc, argv);
+ } else
+ return daemon_main(argc, argv);
+}
+
+static int
+inetd_main(int argc, char **argv)
+{
+ char path[MAXPATHLEN];
+ struct sockaddr_storage me;
+ struct sockaddr_storage from;
+ socklen_t melen, fromlen;
+ int i;
+ int error;
+ const int on = 1;
+ char sbuf[NI_MAXSERV], snum[NI_MAXSERV];
+
+ if (config_load(configfile) < 0 && configfile) {
+ exit_failure("could not load config file");
+ /*NOTREACHED*/
+ }
+
+ if (strrchr(argv[0], '/') == NULL)
+ snprintf(path, sizeof(path), "%s/%s", DEFAULT_DIR, argv[0]);
+ else
+ snprintf(path, sizeof(path), "%s", argv[0]);
+
+#ifdef USE_ROUTE
+ grab_myaddrs();
+
+ sockfd = socket(PF_ROUTE, SOCK_RAW, PF_UNSPEC);
+ if (sockfd < 0) {
+ exit_failure("socket(PF_ROUTE): %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+#endif
+
+ melen = sizeof(me);
+ if (getsockname(STDIN_FILENO, (struct sockaddr *)&me, &melen) < 0) {
+ exit_failure("getsockname: %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+ fromlen = sizeof(from);
+ if (getpeername(STDIN_FILENO, (struct sockaddr *)&from, &fromlen) < 0) {
+ exit_failure("getpeername: %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+ if (getnameinfo((struct sockaddr *)&me, melen, NULL, 0,
+ sbuf, sizeof(sbuf), NI_NUMERICHOST) == 0)
+ service = sbuf;
+ else
+ service = DEFAULT_PORT_NAME;
+ if (getnameinfo((struct sockaddr *)&me, melen, NULL, 0,
+ snum, sizeof(snum), NI_NUMERICHOST) != 0)
+ snprintf(snum, sizeof(snum), "?");
+
+ snprintf(logname, sizeof(logname), "faithd %s", snum);
+ snprintf(procname, sizeof(procname), "accepting port %s", snum);
+ openlog(logname, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
+
+ if (argc >= MAXARGV) {
+ exit_failure("too many arguments");
+ /*NOTREACHED*/
+ }
+ serverarg[0] = serverpath = path;
+ for (i = 1; i < argc; i++)
+ serverarg[i] = argv[i];
+ serverarg[i] = NULL;
+
+ error = setsockopt(STDIN_FILENO, SOL_SOCKET, SO_OOBINLINE, &on,
+ sizeof(on));
+ if (error < 0) {
+ exit_failure("setsockopt(SO_OOBINLINE): %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+
+ play_child(STDIN_FILENO, (struct sockaddr *)&from);
+ exit_failure("should not reach here");
+ return 0; /*dummy!*/
+}
+
+static int
+daemon_main(int argc, char **argv)
+{
+ struct addrinfo hints, *res;
+ int s_wld, error, i, serverargc, on = 1;
+ int family = AF_INET6;
+ int c;
+
+ while ((c = getopt(argc, argv, "df:p")) != -1) {
+ switch (c) {
+ case 'd':
+ dflag++;
+ break;
+ case 'f':
+ configfile = optarg;
+ break;
+ case 'p':
+ pflag++;
+ break;
+ default:
+ usage();
+ /*NOTREACHED*/
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (config_load(configfile) < 0 && configfile) {
+ exit_failure("could not load config file");
+ /*NOTREACHED*/
+ }
+
+
+#ifdef USE_ROUTE
+ grab_myaddrs();
+#endif
+
+ switch (argc) {
+ case 0:
+ usage();
+ /*NOTREACHED*/
+ default:
+ serverargc = argc - NUMARG;
+ if (serverargc >= MAXARGV)
+ exit_stderr("too many arguments");
+
+ serverpath = strdup(argv[NUMPRG]);
+ if (!serverpath)
+ exit_stderr("not enough core");
+ for (i = 0; i < serverargc; i++) {
+ serverarg[i] = strdup(argv[i + NUMARG]);
+ if (!serverarg[i])
+ exit_stderr("not enough core");
+ }
+ serverarg[i] = NULL;
+ /* fall throuth */
+ case 1: /* no local service */
+ service = argv[NUMPRT];
+ break;
+ }
+
+ start_daemon();
+
+ /*
+ * Opening wild card socket for this service.
+ */
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP; /* SCTP? */
+ error = getaddrinfo(NULL, service, &hints, &res);
+ if (error)
+ exit_failure("getaddrinfo: %s", gai_strerror(error));
+
+ s_wld = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (s_wld == -1)
+ exit_failure("socket: %s", strerror(errno));
+
+#ifdef IPV6_FAITH
+ if (res->ai_family == AF_INET6) {
+ error = setsockopt(s_wld, IPPROTO_IPV6, IPV6_FAITH, &on, sizeof(on));
+ if (error == -1)
+ exit_failure("setsockopt(IPV6_FAITH): %s",
+ strerror(errno));
+ }
+#endif
+
+ error = setsockopt(s_wld, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+ if (error == -1)
+ exit_failure("setsockopt(SO_REUSEADDR): %s", strerror(errno));
+
+ error = setsockopt(s_wld, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on));
+ if (error == -1)
+ exit_failure("setsockopt(SO_OOBINLINE): %s", strerror(errno));
+
+#ifdef IPV6_V6ONLY
+ error = setsockopt(s_wld, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on));
+ if (error == -1)
+ exit_failure("setsockopt(IPV6_V6ONLY): %s", strerror(errno));
+#endif
+
+ error = bind(s_wld, (struct sockaddr *)res->ai_addr, res->ai_addrlen);
+ if (error == -1)
+ exit_failure("bind: %s", strerror(errno));
+
+ error = listen(s_wld, 5);
+ if (error == -1)
+ exit_failure("listen: %s", strerror(errno));
+
+#ifdef USE_ROUTE
+ sockfd = socket(PF_ROUTE, SOCK_RAW, PF_UNSPEC);
+ if (sockfd < 0) {
+ exit_failure("socket(PF_ROUTE): %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+#endif
+
+ /*
+ * Everything is OK.
+ */
+
+ snprintf(logname, sizeof(logname), "faithd %s", service);
+ snprintf(procname, sizeof(procname), "accepting port %s", service);
+ openlog(logname, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
+ syslog(LOG_INFO, "Staring faith daemon for %s port", service);
+
+ play_service(s_wld);
+ /* NOTREACHED */
+ exit(1); /*pacify gcc*/
+}
+
+static void
+play_service(int s_wld)
+{
+ struct sockaddr_storage srcaddr;
+ socklen_t len;
+ int s_src;
+ pid_t child_pid;
+#ifdef HAVE_POLL_H
+ struct pollfd pfd[2];
+#else
+ fd_set rfds;
+ int maxfd;
+#endif
+ int error;
+
+ /*
+ * Wait, accept, fork, faith....
+ */
+again:
+ setproctitle("%s", procname);
+
+#ifdef HAVE_POLL_H
+ pfd[0].fd = s_wld;
+ pfd[0].events = POLLIN;
+ pfd[1].fd = -1;
+ pfd[1].revents = 0;
+#else
+ FD_ZERO(&rfds);
+ if (s_wld >= FD_SETSIZE)
+ exit_failure("descriptor too big");
+ FD_SET(s_wld, &rfds);
+ maxfd = s_wld;
+#endif
+#ifdef USE_ROUTE
+ if (sockfd) {
+#ifdef HAVE_POLL_H
+ pfd[1].fd = sockfd;
+ pfd[1].events = POLLIN;
+#else
+ if (sockfd >= FD_SETSIZE)
+ exit_failure("descriptor too big");
+ FD_SET(sockfd, &rfds);
+ maxfd = (maxfd < sockfd) ? sockfd : maxfd;
+#endif
+ }
+#endif
+
+#ifdef HAVE_POLL_H
+ error = poll(pfd, sizeof(pfd)/sizeof(pfd[0]), INFTIM);
+#else
+ error = select(maxfd + 1, &rfds, NULL, NULL, NULL);
+#endif
+ if (error < 0) {
+ if (errno == EINTR)
+ goto again;
+ exit_failure("select: %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+
+#ifdef USE_ROUTE
+#ifdef HAVE_POLL_H
+ if (pfd[1].revents & POLLIN)
+#else
+ if (FD_ISSET(sockfd, &rfds))
+#endif
+ {
+ update_myaddrs();
+ }
+#endif
+#ifdef HAVE_POLL_H
+ if (pfd[0].revents & POLLIN)
+#else
+ if (FD_ISSET(s_wld, &rfds))
+#endif
+ {
+ len = sizeof(srcaddr);
+ s_src = accept(s_wld, (struct sockaddr *)&srcaddr, &len);
+ if (s_src < 0) {
+ if (errno == ECONNABORTED)
+ goto again;
+ exit_failure("socket: %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+ if (srcaddr.ss_family == AF_INET6 &&
+ IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)&srcaddr)->sin6_addr)) {
+ close(s_src);
+ syslog(LOG_ERR, "connection from IPv4 mapped address?");
+ goto again;
+ }
+
+ child_pid = fork();
+
+ if (child_pid == 0) {
+ /* child process */
+ close(s_wld);
+ closelog();
+ openlog(logname, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
+ play_child(s_src, (struct sockaddr *)&srcaddr);
+ exit_failure("should never reach here");
+ /*NOTREACHED*/
+ } else {
+ /* parent process */
+ close(s_src);
+ if (child_pid == -1)
+ syslog(LOG_ERR, "can't fork");
+ }
+ }
+ goto again;
+}
+
+static void
+play_child(int s_src, struct sockaddr *srcaddr)
+{
+ struct sockaddr_storage dstaddr6;
+ struct sockaddr_storage dstaddr4;
+ char src[NI_MAXHOST];
+ char dst6[NI_MAXHOST];
+ char dst4[NI_MAXHOST];
+ socklen_t len = sizeof(dstaddr6);
+ int s_dst, error, hport, nresvport, on = 1;
+ struct timeval tv;
+ struct sockaddr *sa4;
+ const struct config *conf;
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 0;
+
+ getnameinfo(srcaddr, srcaddr->sa_len,
+ src, sizeof(src), NULL, 0, NI_NUMERICHOST);
+ syslog(LOG_INFO, "accepted a client from %s", src);
+
+ error = getsockname(s_src, (struct sockaddr *)&dstaddr6, &len);
+ if (error == -1) {
+ exit_failure("getsockname: %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+
+ getnameinfo((struct sockaddr *)&dstaddr6, len,
+ dst6, sizeof(dst6), NULL, 0, NI_NUMERICHOST);
+ syslog(LOG_INFO, "the client is connecting to %s", dst6);
+
+ if (!faith_prefix((struct sockaddr *)&dstaddr6)) {
+ if (serverpath) {
+ /*
+ * Local service
+ */
+ syslog(LOG_INFO, "executing local %s", serverpath);
+ if (!inetd) {
+ dup2(s_src, 0);
+ close(s_src);
+ dup2(0, 1);
+ dup2(0, 2);
+ }
+ execv(serverpath, serverarg);
+ syslog(LOG_ERR, "execv %s: %s", serverpath,
+ strerror(errno));
+ _exit(EXIT_FAILURE);
+ } else {
+ close(s_src);
+ exit_success("no local service for %s", service);
+ }
+ }
+
+ /*
+ * Act as a translator
+ */
+
+ switch (((struct sockaddr *)&dstaddr6)->sa_family) {
+ case AF_INET6:
+ if (!map6to4((struct sockaddr_in6 *)&dstaddr6,
+ (struct sockaddr_in *)&dstaddr4)) {
+ close(s_src);
+ exit_failure("map6to4 failed");
+ /*NOTREACHED*/
+ }
+ syslog(LOG_INFO, "translating from v6 to v4");
+ break;
+ default:
+ close(s_src);
+ exit_failure("family not supported");
+ /*NOTREACHED*/
+ }
+
+ sa4 = (struct sockaddr *)&dstaddr4;
+ getnameinfo(sa4, sa4->sa_len,
+ dst4, sizeof(dst4), NULL, 0, NI_NUMERICHOST);
+
+ conf = config_match(srcaddr, sa4);
+ if (!conf || !conf->permit) {
+ close(s_src);
+ if (conf) {
+ exit_failure("translation to %s not permitted for %s",
+ dst4, prefix_string(&conf->match));
+ /*NOTREACHED*/
+ } else {
+ exit_failure("translation to %s not permitted", dst4);
+ /*NOTREACHED*/
+ }
+ }
+
+ syslog(LOG_INFO, "the translator is connecting to %s", dst4);
+
+ setproctitle("port %s, %s -> %s", service, src, dst4);
+
+ if (sa4->sa_family == AF_INET6)
+ hport = ntohs(((struct sockaddr_in6 *)&dstaddr4)->sin6_port);
+ else /* AF_INET */
+ hport = ntohs(((struct sockaddr_in *)&dstaddr4)->sin_port);
+
+ if (pflag)
+ s_dst = rresvport_af(&nresvport, sa4->sa_family);
+ else
+ s_dst = socket(sa4->sa_family, SOCK_STREAM, 0);
+ if (s_dst < 0) {
+ exit_failure("socket: %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+
+ if (conf->src.a.ss_family) {
+ if (bind(s_dst, (const struct sockaddr *)&conf->src.a,
+ conf->src.a.ss_len) < 0) {
+ exit_failure("bind: %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+ }
+
+ error = setsockopt(s_dst, SOL_SOCKET, SO_OOBINLINE, &on, sizeof(on));
+ if (error < 0) {
+ exit_failure("setsockopt(SO_OOBINLINE): %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+
+ error = setsockopt(s_src, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
+ if (error < 0) {
+ exit_failure("setsockopt(SO_SNDTIMEO): %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+ error = setsockopt(s_dst, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
+ if (error < 0) {
+ exit_failure("setsockopt(SO_SNDTIMEO): %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+
+ error = connect(s_dst, sa4, sa4->sa_len);
+ if (error < 0) {
+ exit_failure("connect: %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+
+ switch (hport) {
+ case FTP_PORT:
+ ftp_relay(s_src, s_dst);
+ break;
+ default:
+ tcp_relay(s_src, s_dst, service);
+ break;
+ }
+
+ /* NOTREACHED */
+}
+
+/* 0: non faith, 1: faith */
+static int
+faith_prefix(struct sockaddr *dst)
+{
+#ifndef USE_ROUTE
+ int mib[4], size;
+ struct in6_addr faith_prefix;
+ struct sockaddr_in6 *dst6 = (struct sockaddr_in *)dst;
+
+ if (dst->sa_family != AF_INET6)
+ return 0;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_INET6;
+ mib[2] = IPPROTO_IPV6;
+ mib[3] = IPV6CTL_FAITH_PREFIX;
+ size = sizeof(struct in6_addr);
+ if (sysctl(mib, 4, &faith_prefix, &size, NULL, 0) < 0) {
+ exit_failure("sysctl: %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+
+ if (memcmp(dst, &faith_prefix,
+ sizeof(struct in6_addr) - sizeof(struct in_addr) == 0) {
+ return 1;
+ }
+ return 0;
+#else
+ struct myaddrs *p;
+ struct sockaddr_in6 *sin6;
+ struct sockaddr_in *sin4;
+ struct sockaddr_in6 *dst6;
+ struct sockaddr_in *dst4;
+ struct sockaddr_in dstmap;
+
+ dst6 = (struct sockaddr_in6 *)dst;
+ if (dst->sa_family == AF_INET6
+ && IN6_IS_ADDR_V4MAPPED(&dst6->sin6_addr)) {
+ /* ugly... */
+ memset(&dstmap, 0, sizeof(dstmap));
+ dstmap.sin_family = AF_INET;
+ dstmap.sin_len = sizeof(dstmap);
+ memcpy(&dstmap.sin_addr, &dst6->sin6_addr.s6_addr[12],
+ sizeof(dstmap.sin_addr));
+ dst = (struct sockaddr *)&dstmap;
+ }
+
+ dst6 = (struct sockaddr_in6 *)dst;
+ dst4 = (struct sockaddr_in *)dst;
+
+ for (p = myaddrs; p; p = p->next) {
+ sin6 = (struct sockaddr_in6 *)p->addr;
+ sin4 = (struct sockaddr_in *)p->addr;
+
+ if (p->addr->sa_len != dst->sa_len
+ || p->addr->sa_family != dst->sa_family)
+ continue;
+
+ switch (dst->sa_family) {
+ case AF_INET6:
+ if (sin6->sin6_scope_id == dst6->sin6_scope_id
+ && IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr, &dst6->sin6_addr))
+ return 0;
+ break;
+ case AF_INET:
+ if (sin4->sin_addr.s_addr == dst4->sin_addr.s_addr)
+ return 0;
+ break;
+ }
+ }
+ return 1;
+#endif
+}
+
+/* 0: non faith, 1: faith */
+static int
+map6to4(struct sockaddr_in6 *dst6, struct sockaddr_in *dst4)
+{
+ memset(dst4, 0, sizeof(*dst4));
+ dst4->sin_len = sizeof(*dst4);
+ dst4->sin_family = AF_INET;
+ dst4->sin_port = dst6->sin6_port;
+ memcpy(&dst4->sin_addr, &dst6->sin6_addr.s6_addr[12],
+ sizeof(dst4->sin_addr));
+
+ if (dst4->sin_addr.s_addr == INADDR_ANY
+ || dst4->sin_addr.s_addr == INADDR_BROADCAST
+ || IN_MULTICAST(ntohl(dst4->sin_addr.s_addr)))
+ return 0;
+
+ return 1;
+}
+
+
+static void
+sig_child(int sig __unused)
+{
+ int status;
+ pid_t pid;
+
+ while ((pid = wait3(&status, WNOHANG, (struct rusage *)0)) > 0)
+ if (WEXITSTATUS(status))
+ syslog(LOG_WARNING, "child %ld exit status 0x%x",
+ (long)pid, status);
+}
+
+void
+sig_terminate(int sig __unused)
+{
+ syslog(LOG_INFO, "Terminating faith daemon");
+ exit(EXIT_SUCCESS);
+}
+
+static void
+start_daemon(void)
+{
+#ifdef SA_NOCLDWAIT
+ struct sigaction sa;
+#endif
+
+ if (daemon(0, 0) == -1)
+ exit_stderr("daemon: %s", strerror(errno));
+
+#ifdef SA_NOCLDWAIT
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = sig_child;
+ sa.sa_flags = SA_NOCLDWAIT;
+ sigemptyset(&sa.sa_mask);
+ sigaction(SIGCHLD, &sa, (struct sigaction *)0);
+#else
+ if (signal(SIGCHLD, sig_child) == SIG_ERR) {
+ exit_failure("signal CHLD: %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+#endif
+
+ if (signal(SIGTERM, sig_terminate) == SIG_ERR) {
+ exit_failure("signal TERM: %s", strerror(errno));
+ /*NOTREACHED*/
+ }
+}
+
+static void
+exit_stderr(const char *fmt, ...)
+{
+ va_list ap;
+ char buf[BUFSIZ];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ fprintf(stderr, "%s\n", buf);
+ exit(EXIT_FAILURE);
+}
+
+void
+exit_failure(const char *fmt, ...)
+{
+ va_list ap;
+ char buf[BUFSIZ];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ syslog(LOG_ERR, "%s", buf);
+ exit(EXIT_FAILURE);
+}
+
+void
+exit_success(const char *fmt, ...)
+{
+ va_list ap;
+ char buf[BUFSIZ];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ syslog(LOG_INFO, "%s", buf);
+ exit(EXIT_SUCCESS);
+}
+
+#ifdef USE_ROUTE
+static void
+grab_myaddrs()
+{
+ struct ifaddrs *ifap, *ifa;
+ struct myaddrs *p;
+ struct sockaddr_in6 *sin6;
+
+ if (getifaddrs(&ifap) != 0) {
+ exit_failure("getifaddrs");
+ /*NOTREACHED*/
+ }
+
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ switch (ifa->ifa_addr->sa_family) {
+ case AF_INET:
+ case AF_INET6:
+ break;
+ default:
+ continue;
+ }
+
+ p = (struct myaddrs *)malloc(sizeof(struct myaddrs) +
+ ifa->ifa_addr->sa_len);
+ if (!p) {
+ exit_failure("not enough core");
+ /*NOTREACHED*/
+ }
+ memcpy(p + 1, ifa->ifa_addr, ifa->ifa_addr->sa_len);
+ p->next = myaddrs;
+ p->addr = (struct sockaddr *)(p + 1);
+#ifdef __KAME__
+ if (ifa->ifa_addr->sa_family == AF_INET6) {
+ sin6 = (struct sockaddr_in6 *)p->addr;
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)
+ || IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr)) {
+ 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
+ myaddrs = p;
+ if (dflag) {
+ char hbuf[NI_MAXHOST];
+ getnameinfo(p->addr, p->addr->sa_len,
+ hbuf, sizeof(hbuf), NULL, 0,
+ NI_NUMERICHOST);
+ syslog(LOG_INFO, "my interface: %s %s", hbuf,
+ ifa->ifa_name);
+ }
+ }
+
+ freeifaddrs(ifap);
+}
+
+static void
+free_myaddrs()
+{
+ struct myaddrs *p, *q;
+
+ p = myaddrs;
+ while (p) {
+ q = p->next;
+ free(p);
+ p = q;
+ }
+ myaddrs = NULL;
+}
+
+static void
+update_myaddrs()
+{
+ char msg[BUFSIZ];
+ int len;
+ struct rt_msghdr *rtm;
+
+ len = read(sockfd, msg, sizeof(msg));
+ if (len < 0) {
+ syslog(LOG_ERR, "read(PF_ROUTE) failed");
+ return;
+ }
+ rtm = (struct rt_msghdr *)msg;
+ if (len < 4 || len < rtm->rtm_msglen) {
+ syslog(LOG_ERR, "read(PF_ROUTE) short read");
+ return;
+ }
+ if (rtm->rtm_version != RTM_VERSION) {
+ syslog(LOG_ERR, "routing socket version mismatch");
+ close(sockfd);
+ sockfd = 0;
+ return;
+ }
+ switch (rtm->rtm_type) {
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ case RTM_IFINFO:
+ break;
+ default:
+ return;
+ }
+ /* XXX more filters here? */
+
+ syslog(LOG_INFO, "update interface address list");
+ free_myaddrs();
+ grab_myaddrs();
+}
+#endif /*USE_ROUTE*/
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: %s [-dp] [-f conf] service [serverpath [serverargs]]\n",
+ faithdname);
+ exit(0);
+}
diff --git a/usr.sbin/faithd/faithd.h b/usr.sbin/faithd/faithd.h
new file mode 100644
index 0000000..c578d46
--- /dev/null
+++ b/usr.sbin/faithd/faithd.h
@@ -0,0 +1,70 @@
+/* $KAME: faithd.h,v 1.9 2002/05/09 09:41:24 itojun Exp $ */
+
+/*
+ * Copyright (C) 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$
+ */
+
+extern char logname[];
+extern int dflag;
+
+extern void tcp_relay(int, int, const char *);
+extern void ftp_relay(int, int);
+extern int ftp_active(int, int, int *, int *);
+extern int ftp_passive(int, int, int *, int *);
+extern void exit_success(const char *, ...)
+ __attribute__((__format__(__printf__, 1, 2)));
+extern void exit_failure(const char *, ...)
+ __attribute__((__format__(__printf__, 1, 2)));
+
+#define DEFAULT_PORT_NAME "telnet"
+#define DEFAULT_DIR "/usr/libexec"
+#define DEFAULT_NAME "telnetd"
+#define DEFAULT_PATH (DEFAULT_DIR "/" DEFAULT_NAME)
+
+#define FTP_PORT 21
+#define RLOGIN_PORT 513
+#define RSH_PORT 514
+
+#define RETURN_SUCCESS 0
+#define RETURN_FAILURE 1
+
+#define YES 1
+#define NO 0
+
+#define MSS 2048
+#define MAXARGV 20
+
+#define NUMPRT 0
+#define NUMPRG 1
+#define NUMARG 2
+
+#define UC(b) (((int)b)&0xff)
+
+#define FAITH_TIMEOUT (30 * 60) /*second*/
diff --git a/usr.sbin/faithd/ftp.c b/usr.sbin/faithd/ftp.c
new file mode 100644
index 0000000..c54371a
--- /dev/null
+++ b/usr.sbin/faithd/ftp.c
@@ -0,0 +1,1085 @@
+/* $KAME: ftp.c,v 1.24 2005/03/16 05:05:48 itojun Exp $ */
+
+/*
+ * Copyright (C) 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$
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#endif
+#include <errno.h>
+#include <ctype.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "faithd.h"
+
+static char rbuf[MSS];
+static char sbuf[MSS];
+static int passivemode = 0;
+static int wport4 = -1; /* listen() to active */
+static int wport6 = -1; /* listen() to passive */
+static int port4 = -1; /* active: inbound passive: outbound */
+static int port6 = -1; /* active: outbound passive: inbound */
+static struct sockaddr_storage data4; /* server data address */
+static struct sockaddr_storage data6; /* client data address */
+static int epsvall = 0;
+
+enum state { NONE, LPRT, EPRT, LPSV, EPSV };
+
+static int ftp_activeconn(void);
+static int ftp_passiveconn(void);
+static int ftp_copy(int, int);
+static int ftp_copyresult(int, int, enum state);
+static int ftp_copycommand(int, int, enum state *);
+
+void
+ftp_relay(int ctl6, int ctl4)
+{
+#ifdef HAVE_POLL_H
+ struct pollfd pfd[6];
+#else
+ fd_set readfds;
+#endif
+ int error;
+ enum state state = NONE;
+ struct timeval tv;
+
+ syslog(LOG_INFO, "starting ftp control connection");
+
+ for (;;) {
+#ifdef HAVE_POLL_H
+ pfd[0].fd = ctl4;
+ pfd[0].events = POLLIN;
+ pfd[1].fd = ctl6;
+ pfd[1].events = POLLIN;
+ if (0 <= port4) {
+ pfd[2].fd = port4;
+ pfd[2].events = POLLIN;
+ } else
+ pfd[2].fd = -1;
+ if (0 <= port6) {
+ pfd[3].fd = port6;
+ pfd[3].events = POLLIN;
+ } else
+ pfd[3].fd = -1;
+#if 0
+ if (0 <= wport4) {
+ pfd[4].fd = wport4;
+ pfd[4].events = POLLIN;
+ } else
+ pfd[4].fd = -1;
+ if (0 <= wport6) {
+ pfd[5].fd = wport4;
+ pfd[5].events = POLLIN;
+ } else
+ pfd[5].fd = -1;
+#else
+ pfd[4].fd = pfd[5].fd = -1;
+ pfd[4].events = pfd[5].events = 0;
+#endif
+#else
+ int maxfd = 0;
+
+ FD_ZERO(&readfds);
+ if (ctl4 >= FD_SETSIZE)
+ exit_failure("descriptor too big");
+ FD_SET(ctl4, &readfds);
+ maxfd = ctl4;
+ if (ctl6 >= FD_SETSIZE)
+ exit_failure("descriptor too big");
+ FD_SET(ctl6, &readfds);
+ maxfd = (ctl6 > maxfd) ? ctl6 : maxfd;
+ if (0 <= port4) {
+ if (port4 >= FD_SETSIZE)
+ exit_failure("descriptor too big");
+ FD_SET(port4, &readfds);
+ maxfd = (port4 > maxfd) ? port4 : maxfd;
+ }
+ if (0 <= port6) {
+ if (port6 >= FD_SETSIZE)
+ exit_failure("descriptor too big");
+ FD_SET(port6, &readfds);
+ maxfd = (port6 > maxfd) ? port6 : maxfd;
+ }
+#if 0
+ if (0 <= wport4) {
+ if (wport4 >= FD_SETSIZE)
+ exit_failure("descriptor too big");
+ FD_SET(wport4, &readfds);
+ maxfd = (wport4 > maxfd) ? wport4 : maxfd;
+ }
+ if (0 <= wport6) {
+ if (wport6 >= FD_SETSIZE)
+ exit_failure("descriptor too big");
+ FD_SET(wport6, &readfds);
+ maxfd = (wport6 > maxfd) ? wport6 : maxfd;
+ }
+#endif
+#endif
+ tv.tv_sec = FAITH_TIMEOUT;
+ tv.tv_usec = 0;
+
+#ifdef HAVE_POLL_H
+ error = poll(pfd, sizeof(pfd)/sizeof(pfd[0]), tv.tv_sec * 1000);
+#else
+ error = select(maxfd + 1, &readfds, NULL, NULL, &tv);
+#endif
+ if (error == -1) {
+#ifdef HAVE_POLL_H
+ exit_failure("poll: %s", strerror(errno));
+#else
+ exit_failure("select: %s", strerror(errno));
+#endif
+ }
+ else if (error == 0)
+ exit_failure("connection timeout");
+
+ /*
+ * The order of the following checks does (slightly) matter.
+ * It is important to visit all checks (do not use "continue"),
+ * otherwise some of the pipe may become full and we cannot
+ * relay correctly.
+ */
+#ifdef HAVE_POLL_H
+ if (pfd[1].revents & POLLIN)
+#else
+ if (FD_ISSET(ctl6, &readfds))
+#endif
+ {
+ /*
+ * copy control connection from the client.
+ * command translation is necessary.
+ */
+ error = ftp_copycommand(ctl6, ctl4, &state);
+
+ if (error < 0)
+ goto bad;
+ else if (error == 0) {
+ close(ctl4);
+ close(ctl6);
+ exit_success("terminating ftp control connection");
+ /*NOTREACHED*/
+ }
+ }
+#ifdef HAVE_POLL_H
+ if (pfd[0].revents & POLLIN)
+#else
+ if (FD_ISSET(ctl4, &readfds))
+#endif
+ {
+ /*
+ * copy control connection from the server
+ * translation of result code is necessary.
+ */
+ error = ftp_copyresult(ctl4, ctl6, state);
+
+ if (error < 0)
+ goto bad;
+ else if (error == 0) {
+ close(ctl4);
+ close(ctl6);
+ exit_success("terminating ftp control connection");
+ /*NOTREACHED*/
+ }
+ }
+#ifdef HAVE_POLL_H
+ if (0 <= port4 && 0 <= port6 && (pfd[2].revents & POLLIN))
+#else
+ if (0 <= port4 && 0 <= port6 && FD_ISSET(port4, &readfds))
+#endif
+ {
+ /*
+ * copy data connection.
+ * no special treatment necessary.
+ */
+#ifdef HAVE_POLL_H
+ if (pfd[2].revents & POLLIN)
+#else
+ if (FD_ISSET(port4, &readfds))
+#endif
+ error = ftp_copy(port4, port6);
+ switch (error) {
+ case -1:
+ goto bad;
+ case 0:
+ close(port4);
+ close(port6);
+ port4 = port6 = -1;
+ syslog(LOG_INFO, "terminating data connection");
+ break;
+ default:
+ break;
+ }
+ }
+#ifdef HAVE_POLL_H
+ if (0 <= port4 && 0 <= port6 && (pfd[3].revents & POLLIN))
+#else
+ if (0 <= port4 && 0 <= port6 && FD_ISSET(port6, &readfds))
+#endif
+ {
+ /*
+ * copy data connection.
+ * no special treatment necessary.
+ */
+#ifdef HAVE_POLL_H
+ if (pfd[3].revents & POLLIN)
+#else
+ if (FD_ISSET(port6, &readfds))
+#endif
+ error = ftp_copy(port6, port4);
+ switch (error) {
+ case -1:
+ goto bad;
+ case 0:
+ close(port4);
+ close(port6);
+ port4 = port6 = -1;
+ syslog(LOG_INFO, "terminating data connection");
+ break;
+ default:
+ break;
+ }
+ }
+#if 0
+#ifdef HAVE_POLL_H
+ if (wport4 && (pfd[4].revents & POLLIN))
+#else
+ if (wport4 && FD_ISSET(wport4, &readfds))
+#endif
+ {
+ /*
+ * establish active data connection from the server.
+ */
+ ftp_activeconn();
+ }
+#ifdef HAVE_POLL_H
+ if (wport4 && (pfd[5].revents & POLLIN))
+#else
+ if (wport6 && FD_ISSET(wport6, &readfds))
+#endif
+ {
+ /*
+ * establish passive data connection from the client.
+ */
+ ftp_passiveconn();
+ }
+#endif
+ }
+
+ bad:
+ exit_failure("%s", strerror(errno));
+}
+
+static int
+ftp_activeconn()
+{
+ socklen_t n;
+ int error;
+#ifdef HAVE_POLL_H
+ struct pollfd pfd[1];
+#else
+ fd_set set;
+#endif
+ struct timeval timeout;
+ struct sockaddr *sa;
+
+ /* get active connection from server */
+#ifdef HAVE_POLL_H
+ pfd[0].fd = wport4;
+ pfd[0].events = POLLIN;
+#else
+ FD_ZERO(&set);
+ if (wport4 >= FD_SETSIZE)
+ exit_failure("descriptor too big");
+ FD_SET(wport4, &set);
+#endif
+ timeout.tv_sec = 120;
+ timeout.tv_usec = 0;
+ n = sizeof(data4);
+#ifdef HAVE_POLL_H
+ if (poll(pfd, sizeof(pfd)/sizeof(pfd[0]), timeout.tv_sec * 1000) == 0 ||
+ (port4 = accept(wport4, (struct sockaddr *)&data4, &n)) < 0)
+#else
+ if (select(wport4 + 1, &set, NULL, NULL, &timeout) == 0 ||
+ (port4 = accept(wport4, (struct sockaddr *)&data4, &n)) < 0)
+#endif
+ {
+ close(wport4);
+ wport4 = -1;
+ syslog(LOG_INFO, "active mode data connection failed");
+ return -1;
+ }
+
+ /* ask active connection to client */
+ sa = (struct sockaddr *)&data6;
+ port6 = socket(sa->sa_family, SOCK_STREAM, 0);
+ if (port6 == -1) {
+ close(port4);
+ close(wport4);
+ port4 = wport4 = -1;
+ syslog(LOG_INFO, "active mode data connection failed");
+ return -1;
+ }
+ error = connect(port6, sa, sa->sa_len);
+ if (error < 0) {
+ close(port6);
+ close(port4);
+ close(wport4);
+ port6 = port4 = wport4 = -1;
+ syslog(LOG_INFO, "active mode data connection failed");
+ return -1;
+ }
+
+ syslog(LOG_INFO, "active mode data connection established");
+ return 0;
+}
+
+static int
+ftp_passiveconn()
+{
+ socklen_t len;
+ int error;
+#ifdef HAVE_POLL_H
+ struct pollfd pfd[1];
+#else
+ fd_set set;
+#endif
+ struct timeval timeout;
+ struct sockaddr *sa;
+
+ /* get passive connection from client */
+#ifdef HAVE_POLL_H
+ pfd[0].fd = wport6;
+ pfd[0].events = POLLIN;
+#else
+ FD_ZERO(&set);
+ if (wport6 >= FD_SETSIZE)
+ exit_failure("descriptor too big");
+ FD_SET(wport6, &set);
+#endif
+ timeout.tv_sec = 120;
+ timeout.tv_usec = 0;
+ len = sizeof(data6);
+#ifdef HAVE_POLL_H
+ if (poll(pfd, sizeof(pfd)/sizeof(pfd[0]), timeout.tv_sec * 1000) == 0 ||
+ (port6 = accept(wport6, (struct sockaddr *)&data6, &len)) < 0)
+#else
+ if (select(wport6 + 1, &set, NULL, NULL, &timeout) == 0 ||
+ (port6 = accept(wport6, (struct sockaddr *)&data6, &len)) < 0)
+#endif
+ {
+ close(wport6);
+ wport6 = -1;
+ syslog(LOG_INFO, "passive mode data connection failed");
+ return -1;
+ }
+
+ /* ask passive connection to server */
+ sa = (struct sockaddr *)&data4;
+ port4 = socket(sa->sa_family, SOCK_STREAM, 0);
+ if (port4 == -1) {
+ close(wport6);
+ close(port6);
+ wport6 = port6 = -1;
+ syslog(LOG_INFO, "passive mode data connection failed");
+ return -1;
+ }
+ error = connect(port4, sa, sa->sa_len);
+ if (error < 0) {
+ close(wport6);
+ close(port4);
+ close(port6);
+ wport6 = port4 = port6 = -1;
+ syslog(LOG_INFO, "passive mode data connection failed");
+ return -1;
+ }
+
+ syslog(LOG_INFO, "passive mode data connection established");
+ return 0;
+}
+
+static int
+ftp_copy(int src, int dst)
+{
+ int error, atmark, n;
+
+ /* OOB data handling */
+ error = ioctl(src, SIOCATMARK, &atmark);
+ if (error != -1 && atmark == 1) {
+ n = read(src, rbuf, 1);
+ if (n == -1)
+ goto bad;
+ send(dst, rbuf, n, MSG_OOB);
+#if 0
+ n = read(src, rbuf, sizeof(rbuf));
+ if (n == -1)
+ goto bad;
+ write(dst, rbuf, n);
+ return n;
+#endif
+ }
+
+ n = read(src, rbuf, sizeof(rbuf));
+ switch (n) {
+ case -1:
+ case 0:
+ return n;
+ default:
+ write(dst, rbuf, n);
+ return n;
+ }
+
+ bad:
+ exit_failure("%s", strerror(errno));
+ /*NOTREACHED*/
+ return 0; /* to make gcc happy */
+}
+
+static int
+ftp_copyresult(int src, int dst, enum state state)
+{
+ int error, atmark, n;
+ socklen_t len;
+ char *param;
+ int code;
+ char *a, *p;
+ int i;
+
+ /* OOB data handling */
+ error = ioctl(src, SIOCATMARK, &atmark);
+ if (error != -1 && atmark == 1) {
+ n = read(src, rbuf, 1);
+ if (n == -1)
+ goto bad;
+ send(dst, rbuf, n, MSG_OOB);
+#if 0
+ n = read(src, rbuf, sizeof(rbuf));
+ if (n == -1)
+ goto bad;
+ write(dst, rbuf, n);
+ return n;
+#endif
+ }
+
+ n = read(src, rbuf, sizeof(rbuf));
+ if (n <= 0)
+ return n;
+ rbuf[n] = '\0';
+
+ /*
+ * parse argument
+ */
+ p = rbuf;
+ for (i = 0; i < 3; i++) {
+ if (!isdigit(*p)) {
+ /* invalid reply */
+ write(dst, rbuf, n);
+ return n;
+ }
+ p++;
+ }
+ if (!isspace(*p)) {
+ /* invalid reply */
+ write(dst, rbuf, n);
+ return n;
+ }
+ code = atoi(rbuf);
+ param = p;
+ /* param points to first non-command token, if any */
+ while (*param && isspace(*param))
+ param++;
+ if (!*param)
+ param = NULL;
+
+ switch (state) {
+ case NONE:
+ if (!passivemode && rbuf[0] == '1') {
+ if (ftp_activeconn() < 0) {
+ n = snprintf(rbuf, sizeof(rbuf),
+ "425 Cannot open data connetion\r\n");
+ if (n < 0 || n >= sizeof(rbuf))
+ n = 0;
+ }
+ }
+ if (n)
+ write(dst, rbuf, n);
+ return n;
+ case LPRT:
+ case EPRT:
+ /* expecting "200 PORT command successful." */
+ if (code == 200) {
+ p = strstr(rbuf, "PORT");
+ if (p) {
+ p[0] = (state == LPRT) ? 'L' : 'E';
+ p[1] = 'P';
+ }
+ } else {
+ close(wport4);
+ wport4 = -1;
+ }
+ write(dst, rbuf, n);
+ return n;
+ case LPSV:
+ case EPSV:
+ /*
+ * expecting "227 Entering Passive Mode (x,x,x,x,x,x,x)"
+ * (in some cases result comes without paren)
+ */
+ if (code != 227) {
+passivefail0:
+ close(wport6);
+ wport6 = -1;
+ write(dst, rbuf, n);
+ return n;
+ }
+
+ {
+ unsigned int ho[4], po[2];
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ u_short port;
+
+ /*
+ * PASV result -> LPSV/EPSV result
+ */
+ p = param;
+ while (*p && *p != '(' && !isdigit(*p)) /*)*/
+ p++;
+ if (!*p)
+ goto passivefail0; /*XXX*/
+ if (*p == '(') /*)*/
+ p++;
+ n = sscanf(p, "%u,%u,%u,%u,%u,%u",
+ &ho[0], &ho[1], &ho[2], &ho[3], &po[0], &po[1]);
+ if (n != 6)
+ goto passivefail0; /*XXX*/
+
+ /* keep PORT parameter */
+ memset(&data4, 0, sizeof(data4));
+ sin = (struct sockaddr_in *)&data4;
+ sin->sin_len = sizeof(*sin);
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = 0;
+ for (n = 0; n < 4; n++) {
+ sin->sin_addr.s_addr |=
+ htonl((ho[n] & 0xff) << ((3 - n) * 8));
+ }
+ sin->sin_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
+
+ /* get ready for passive data connection */
+ memset(&data6, 0, sizeof(data6));
+ sin6 = (struct sockaddr_in6 *)&data6;
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_family = AF_INET6;
+ wport6 = socket(sin6->sin6_family, SOCK_STREAM, 0);
+ if (wport6 == -1) {
+passivefail:
+ n = snprintf(sbuf, sizeof(sbuf),
+ "500 could not translate from PASV\r\n");
+ if (n < 0 || n >= sizeof(sbuf))
+ n = 0;
+ if (n)
+ write(src, sbuf, n);
+ return n;
+ }
+#ifdef IPV6_FAITH
+ {
+ int on = 1;
+ error = setsockopt(wport6, IPPROTO_IPV6, IPV6_FAITH,
+ &on, sizeof(on));
+ if (error == -1)
+ exit_failure("setsockopt(IPV6_FAITH): %s", strerror(errno));
+ }
+#endif
+ error = bind(wport6, (struct sockaddr *)sin6, sin6->sin6_len);
+ if (error == -1) {
+ close(wport6);
+ wport6 = -1;
+ goto passivefail;
+ }
+ error = listen(wport6, 1);
+ if (error == -1) {
+ close(wport6);
+ wport6 = -1;
+ goto passivefail;
+ }
+
+ /* transmit LPSV or EPSV */
+ /*
+ * addr from dst, port from wport6
+ */
+ len = sizeof(data6);
+ error = getsockname(wport6, (struct sockaddr *)&data6, &len);
+ if (error == -1) {
+ close(wport6);
+ wport6 = -1;
+ goto passivefail;
+ }
+ sin6 = (struct sockaddr_in6 *)&data6;
+ port = sin6->sin6_port;
+
+ len = sizeof(data6);
+ error = getsockname(dst, (struct sockaddr *)&data6, &len);
+ if (error == -1) {
+ close(wport6);
+ wport6 = -1;
+ goto passivefail;
+ }
+ sin6 = (struct sockaddr_in6 *)&data6;
+ sin6->sin6_port = port;
+
+ if (state == LPSV) {
+ a = (char *)&sin6->sin6_addr;
+ p = (char *)&sin6->sin6_port;
+ n = snprintf(sbuf, sizeof(sbuf),
+"228 Entering Long Passive Mode (%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)\r\n",
+ 6, 16, UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
+ UC(a[4]), UC(a[5]), UC(a[6]), UC(a[7]),
+ UC(a[8]), UC(a[9]), UC(a[10]), UC(a[11]),
+ UC(a[12]), UC(a[13]), UC(a[14]), UC(a[15]),
+ 2, UC(p[0]), UC(p[1]));
+ if (n < 0 || n >= sizeof(sbuf))
+ n = 0;
+ if (n)
+ write(dst, sbuf, n);
+ passivemode = 1;
+ return n;
+ } else {
+ n = snprintf(sbuf, sizeof(sbuf),
+"229 Entering Extended Passive Mode (|||%d|)\r\n",
+ ntohs(sin6->sin6_port));
+ if (n < 0 || n >= sizeof(sbuf))
+ n = 0;
+ if (n)
+ write(dst, sbuf, n);
+ passivemode = 1;
+ return n;
+ }
+ }
+ }
+
+ bad:
+ exit_failure("%s", strerror(errno));
+ /*NOTREACHED*/
+ return 0; /* to make gcc happy */
+}
+
+static int
+ftp_copycommand(int src, int dst, enum state *state)
+{
+ int error, atmark, n;
+ socklen_t len;
+ unsigned int af, hal, ho[16], pal, po[2];
+ char *a, *p, *q;
+ char cmd[5], *param;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ enum state nstate;
+ char ch;
+ int i;
+
+ /* OOB data handling */
+ error = ioctl(src, SIOCATMARK, &atmark);
+ if (error != -1 && atmark == 1) {
+ n = read(src, rbuf, 1);
+ if (n == -1)
+ goto bad;
+ send(dst, rbuf, n, MSG_OOB);
+#if 0
+ n = read(src, rbuf, sizeof(rbuf));
+ if (n == -1)
+ goto bad;
+ write(dst, rbuf, n);
+ return n;
+#endif
+ }
+
+ n = read(src, rbuf, sizeof(rbuf));
+ if (n <= 0)
+ return n;
+ rbuf[n] = '\0';
+
+ if (n < 4) {
+ write(dst, rbuf, n);
+ return n;
+ }
+
+ /*
+ * parse argument
+ */
+ p = rbuf;
+ q = cmd;
+ for (i = 0; i < 4; i++) {
+ if (!isalpha(*p)) {
+ /* invalid command */
+ write(dst, rbuf, n);
+ return n;
+ }
+ *q++ = islower(*p) ? toupper(*p) : *p;
+ p++;
+ }
+ if (!isspace(*p)) {
+ /* invalid command */
+ write(dst, rbuf, n);
+ return n;
+ }
+ *q = '\0';
+ param = p;
+ /* param points to first non-command token, if any */
+ while (*param && isspace(*param))
+ param++;
+ if (!*param)
+ param = NULL;
+
+ *state = NONE;
+
+ if (strcmp(cmd, "LPRT") == 0 && param) {
+ /*
+ * LPRT -> PORT
+ */
+ nstate = LPRT;
+
+ close(wport4);
+ close(wport6);
+ close(port4);
+ close(port6);
+ wport4 = wport6 = port4 = port6 = -1;
+
+ if (epsvall) {
+ n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
+ cmd);
+ if (n < 0 || n >= sizeof(sbuf))
+ n = 0;
+ if (n)
+ write(src, sbuf, n);
+ return n;
+ }
+
+ n = sscanf(param,
+"%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u,%u",
+ &af, &hal, &ho[0], &ho[1], &ho[2], &ho[3],
+ &ho[4], &ho[5], &ho[6], &ho[7],
+ &ho[8], &ho[9], &ho[10], &ho[11],
+ &ho[12], &ho[13], &ho[14], &ho[15],
+ &pal, &po[0], &po[1]);
+ if (n != 21 || af != 6 || hal != 16|| pal != 2) {
+ n = snprintf(sbuf, sizeof(sbuf),
+ "501 illegal parameter to LPRT\r\n");
+ if (n < 0 || n >= sizeof(sbuf))
+ n = 0;
+ if (n)
+ write(src, sbuf, n);
+ return n;
+ }
+
+ /* keep LPRT parameter */
+ memset(&data6, 0, sizeof(data6));
+ sin6 = (struct sockaddr_in6 *)&data6;
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_family = AF_INET6;
+ for (n = 0; n < 16; n++)
+ sin6->sin6_addr.s6_addr[n] = ho[n];
+ sin6->sin6_port = htons(((po[0] & 0xff) << 8) | (po[1] & 0xff));
+
+sendport:
+ /* get ready for active data connection */
+ len = sizeof(data4);
+ error = getsockname(dst, (struct sockaddr *)&data4, &len);
+ if (error == -1) {
+lprtfail:
+ n = snprintf(sbuf, sizeof(sbuf),
+ "500 could not translate to PORT\r\n");
+ if (n < 0 || n >= sizeof(sbuf))
+ n = 0;
+ if (n)
+ write(src, sbuf, n);
+ return n;
+ }
+ if (((struct sockaddr *)&data4)->sa_family != AF_INET)
+ goto lprtfail;
+ sin = (struct sockaddr_in *)&data4;
+ sin->sin_port = 0;
+ wport4 = socket(sin->sin_family, SOCK_STREAM, 0);
+ if (wport4 == -1)
+ goto lprtfail;
+ error = bind(wport4, (struct sockaddr *)sin, sin->sin_len);
+ if (error == -1) {
+ close(wport4);
+ wport4 = -1;
+ goto lprtfail;
+ }
+ error = listen(wport4, 1);
+ if (error == -1) {
+ close(wport4);
+ wport4 = -1;
+ goto lprtfail;
+ }
+
+ /* transmit PORT */
+ len = sizeof(data4);
+ error = getsockname(wport4, (struct sockaddr *)&data4, &len);
+ if (error == -1) {
+ close(wport4);
+ wport4 = -1;
+ goto lprtfail;
+ }
+ if (((struct sockaddr *)&data4)->sa_family != AF_INET) {
+ close(wport4);
+ wport4 = -1;
+ goto lprtfail;
+ }
+ sin = (struct sockaddr_in *)&data4;
+ a = (char *)&sin->sin_addr;
+ p = (char *)&sin->sin_port;
+ n = snprintf(sbuf, sizeof(sbuf), "PORT %d,%d,%d,%d,%d,%d\r\n",
+ UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
+ UC(p[0]), UC(p[1]));
+ if (n < 0 || n >= sizeof(sbuf))
+ n = 0;
+ if (n)
+ write(dst, sbuf, n);
+ *state = nstate;
+ passivemode = 0;
+ return n;
+ } else if (strcmp(cmd, "EPRT") == 0 && param) {
+ /*
+ * EPRT -> PORT
+ */
+ char *afp, *hostp, *portp;
+ struct addrinfo hints, *res;
+
+ nstate = EPRT;
+
+ close(wport4);
+ close(wport6);
+ close(port4);
+ close(port6);
+ wport4 = wport6 = port4 = port6 = -1;
+
+ if (epsvall) {
+ n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
+ cmd);
+ if (n < 0 || n >= sizeof(sbuf))
+ n = 0;
+ if (n)
+ write(src, sbuf, n);
+ return n;
+ }
+
+ p = param;
+ ch = *p++; /* boundary character */
+ afp = p;
+ while (*p && *p != ch)
+ p++;
+ if (!*p) {
+eprtparamfail:
+ n = snprintf(sbuf, sizeof(sbuf),
+ "501 illegal parameter to EPRT\r\n");
+ if (n < 0 || n >= sizeof(sbuf))
+ n = 0;
+ if (n)
+ write(src, sbuf, n);
+ return n;
+ }
+ *p++ = '\0';
+ hostp = p;
+ while (*p && *p != ch)
+ p++;
+ if (!*p)
+ goto eprtparamfail;
+ *p++ = '\0';
+ portp = p;
+ while (*p && *p != ch)
+ p++;
+ if (!*p)
+ goto eprtparamfail;
+ *p++ = '\0';
+
+ n = sscanf(afp, "%d", &af);
+ if (n != 1 || af != 2) {
+ n = snprintf(sbuf, sizeof(sbuf),
+ "501 unsupported address family to EPRT\r\n");
+ if (n < 0 || n >= sizeof(sbuf))
+ n = 0;
+ if (n)
+ write(src, sbuf, n);
+ return n;
+ }
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ error = getaddrinfo(hostp, portp, &hints, &res);
+ if (error) {
+ n = snprintf(sbuf, sizeof(sbuf),
+ "501 EPRT: %s\r\n", gai_strerror(error));
+ if (n < 0 || n >= sizeof(sbuf))
+ n = 0;
+ if (n)
+ write(src, sbuf, n);
+ return n;
+ }
+ if (res->ai_next) {
+ n = snprintf(sbuf, sizeof(sbuf),
+ "501 EPRT: %s resolved to multiple addresses\r\n", hostp);
+ if (n < 0 || n >= sizeof(sbuf))
+ n = 0;
+ if (n)
+ write(src, sbuf, n);
+ freeaddrinfo(res);
+ return n;
+ }
+
+ memcpy(&data6, res->ai_addr, res->ai_addrlen);
+
+ freeaddrinfo(res);
+ goto sendport;
+ } else if (strcmp(cmd, "LPSV") == 0 && !param) {
+ /*
+ * LPSV -> PASV
+ */
+ nstate = LPSV;
+
+ close(wport4);
+ close(wport6);
+ close(port4);
+ close(port6);
+ wport4 = wport6 = port4 = port6 = -1;
+
+ if (epsvall) {
+ n = snprintf(sbuf, sizeof(sbuf), "501 %s disallowed in EPSV ALL\r\n",
+ cmd);
+ if (n < 0 || n >= sizeof(sbuf))
+ n = 0;
+ if (n)
+ write(src, sbuf, n);
+ return n;
+ }
+
+ /* transmit PASV */
+ n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n");
+ if (n < 0 || n >= sizeof(sbuf))
+ n = 0;
+ if (n)
+ write(dst, sbuf, n);
+ *state = LPSV;
+ passivemode = 0; /* to be set to 1 later */
+ return n;
+ } else if (strcmp(cmd, "EPSV") == 0 && !param) {
+ /*
+ * EPSV -> PASV
+ */
+ close(wport4);
+ close(wport6);
+ close(port4);
+ close(port6);
+ wport4 = wport6 = port4 = port6 = -1;
+
+ n = snprintf(sbuf, sizeof(sbuf), "PASV\r\n");
+ if (n < 0 || n >= sizeof(sbuf))
+ n = 0;
+ if (n)
+ write(dst, sbuf, n);
+ *state = EPSV;
+ passivemode = 0; /* to be set to 1 later */
+ return n;
+ } else if (strcmp(cmd, "EPSV") == 0 && param
+ && strncasecmp(param, "ALL", 3) == 0 && isspace(param[3])) {
+ /*
+ * EPSV ALL
+ */
+ epsvall = 1;
+ n = snprintf(sbuf, sizeof(sbuf), "200 EPSV ALL command successful.\r\n");
+ if (n < 0 || n >= sizeof(sbuf))
+ n = 0;
+ if (n)
+ write(src, sbuf, n);
+ return n;
+ } else if (strcmp(cmd, "PORT") == 0 || strcmp(cmd, "PASV") == 0) {
+ /*
+ * reject PORT/PASV
+ */
+ n = snprintf(sbuf, sizeof(sbuf), "502 %s not implemented.\r\n", cmd);
+ if (n < 0 || n >= sizeof(sbuf))
+ n = 0;
+ if (n)
+ write(src, sbuf, n);
+ return n;
+ } else if (passivemode
+ && (strcmp(cmd, "STOR") == 0
+ || strcmp(cmd, "STOU") == 0
+ || strcmp(cmd, "RETR") == 0
+ || strcmp(cmd, "LIST") == 0
+ || strcmp(cmd, "NLST") == 0
+ || strcmp(cmd, "APPE") == 0)) {
+ /*
+ * commands with data transfer. need to care about passive
+ * mode data connection.
+ */
+
+ if (ftp_passiveconn() < 0) {
+ n = snprintf(sbuf, sizeof(sbuf), "425 Cannot open data connetion\r\n");
+ if (n < 0 || n >= sizeof(sbuf))
+ n = 0;
+ if (n)
+ write(src, sbuf, n);
+ } else {
+ /* simply relay the command */
+ write(dst, rbuf, n);
+ }
+
+ *state = NONE;
+ return n;
+ } else {
+ /* simply relay it */
+ *state = NONE;
+ write(dst, rbuf, n);
+ return n;
+ }
+
+ bad:
+ exit_failure("%s", strerror(errno));
+ /*NOTREACHED*/
+ return 0; /* to make gcc happy */
+}
diff --git a/usr.sbin/faithd/prefix.c b/usr.sbin/faithd/prefix.c
new file mode 100644
index 0000000..6a3f66b
--- /dev/null
+++ b/usr.sbin/faithd/prefix.c
@@ -0,0 +1,349 @@
+/* $KAME: prefix.c,v 1.13 2003/09/02 22:50:17 itojun Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (C) 2000 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/socket.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <string.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <limits.h>
+
+#ifndef offsetof
+#define offsetof(type, member) ((size_t)(u_long)(&((type *)0)->member))
+#endif
+
+#include "faithd.h"
+#include "prefix.h"
+
+static int prefix_set(const char *, struct prefix *, int);
+static struct config *config_load1(const char *);
+#if 0
+static void config_show1(const struct config *);
+static void config_show(void);
+#endif
+
+struct config *config_list = NULL;
+const int niflags = NI_NUMERICHOST;
+
+static int
+prefix_set(const char *s, struct prefix *prefix, int slash)
+{
+ char *p = NULL, *q, *r;
+ struct addrinfo hints, *res = NULL;
+ int max;
+ char *a;
+
+ p = strdup(s);
+ if (!p)
+ goto fail;
+ q = strchr(p, '/');
+ if (q) {
+ if (!slash)
+ goto fail;
+ *q++ = '\0';
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+ hints.ai_flags = AI_NUMERICHOST;
+ if (getaddrinfo(p, "0", &hints, &res))
+ goto fail;
+ if (res->ai_next || res->ai_addrlen > sizeof(prefix->a))
+ goto fail;
+ memcpy(&prefix->a, res->ai_addr, res->ai_addrlen);
+
+ switch (prefix->a.ss_family) {
+ case AF_INET:
+ max = 32;
+ a = (char *)&((struct sockaddr_in *)&prefix->a)->sin_addr;
+ break;
+ case AF_INET6:
+ max = 128;
+ a = (char *)&((struct sockaddr_in6 *)&prefix->a)->sin6_addr;
+ break;
+ default:
+ a = NULL;
+ max = -1;
+ break;
+ }
+
+ if (q) {
+ r = NULL;
+ prefix->l = (int)strtoul(q, &r, 10);
+ if (!*q || *r)
+ goto fail;
+ if (prefix->l < 0 || prefix->l > max)
+ goto fail;
+ } else
+ prefix->l = max;
+
+ if (p)
+ free(p);
+ if (res)
+ freeaddrinfo(res);
+ return 0;
+
+fail:
+ if (p)
+ free(p);
+ if (res)
+ freeaddrinfo(res);
+ return -1;
+}
+
+const char *
+prefix_string(const struct prefix *prefix)
+{
+ static char buf[NI_MAXHOST + 20];
+ char hbuf[NI_MAXHOST];
+
+ if (getnameinfo((const struct sockaddr *)&prefix->a, prefix->a.ss_len,
+ hbuf, sizeof(hbuf), NULL, 0, niflags))
+ return NULL;
+ snprintf(buf, sizeof(buf), "%s/%d", hbuf, prefix->l);
+ return buf;
+}
+
+int
+prefix_match(const struct prefix *prefix, const struct sockaddr *sa)
+{
+ struct sockaddr_storage a, b;
+ char *pa, *pb;
+ int off, l;
+
+ if (prefix->a.ss_family != sa->sa_family ||
+ prefix->a.ss_len != sa->sa_len)
+ return 0;
+
+ if (prefix->a.ss_len > sizeof(a) || sa->sa_len > sizeof(b))
+ return 0;
+
+ switch (prefix->a.ss_family) {
+ case AF_INET:
+ off = offsetof(struct sockaddr_in, sin_addr);
+ break;
+ case AF_INET6:
+ off = offsetof(struct sockaddr_in6, sin6_addr);
+ break;
+ default:
+ if (memcmp(&prefix->a, sa, prefix->a.ss_len) != 0)
+ return 0;
+ else
+ return 1;
+ }
+
+ memcpy(&a, &prefix->a, prefix->a.ss_len);
+ memcpy(&b, sa, sa->sa_len);
+ l = prefix->l / 8 + (prefix->l % 8 ? 1 : 0);
+
+ /* overrun check */
+ if (off + l > a.ss_len)
+ return 0;
+
+ pa = ((char *)&a) + off;
+ pb = ((char *)&b) + off;
+ if (prefix->l % 8) {
+ pa[prefix->l / 8] &= 0xff00 >> (prefix->l % 8);
+ pb[prefix->l / 8] &= 0xff00 >> (prefix->l % 8);
+ }
+ if (memcmp(pa, pb, l) != 0)
+ return 0;
+ else
+ return 1;
+}
+
+/*
+ * prefix/prefixlen permit/deny prefix/prefixlen [srcaddr]
+ * 3ffe::/16 permit 10.0.0.0/8 10.1.1.1
+ */
+static struct config *
+config_load1(const char *line)
+{
+ struct config *conf;
+ char buf[BUFSIZ];
+ char *p;
+ char *token[4];
+ int i;
+
+ if (strlen(line) + 1 > sizeof(buf))
+ return NULL;
+ strlcpy(buf, line, sizeof(buf));
+
+ p = strchr(buf, '\n');
+ if (!p)
+ return NULL;
+ *p = '\0';
+ p = strchr(buf, '#');
+ if (p)
+ *p = '\0';
+ if (strlen(buf) == 0)
+ return NULL;
+
+ p = buf;
+ memset(token, 0, sizeof(token));
+ for (i = 0; i < sizeof(token) / sizeof(token[0]); i++) {
+ token[i] = strtok(p, "\t ");
+ p = NULL;
+ if (token[i] == NULL)
+ break;
+ }
+ /* extra tokens? */
+ if (strtok(p, "\t ") != NULL)
+ return NULL;
+ /* insufficient tokens */
+ switch (i) {
+ case 3:
+ case 4:
+ break;
+ default:
+ return NULL;
+ }
+
+ conf = (struct config *)malloc(sizeof(*conf));
+ if (conf == NULL)
+ return NULL;
+ memset(conf, 0, sizeof(*conf));
+
+ if (strcasecmp(token[1], "permit") == 0)
+ conf->permit = 1;
+ else if (strcasecmp(token[1], "deny") == 0)
+ conf->permit = 0;
+ else {
+ /* invalid keyword is considered as "deny" */
+ conf->permit = 0;
+ }
+
+ if (prefix_set(token[0], &conf->match, 1) < 0)
+ goto fail;
+ if (prefix_set(token[2], &conf->dest, 1) < 0)
+ goto fail;
+ if (token[3]) {
+ if (prefix_set(token[3], &conf->src, 0) < 0)
+ goto fail;
+ }
+
+ return conf;
+
+fail:
+ free(conf);
+ return NULL;
+}
+
+int
+config_load(const char *configfile)
+{
+ FILE *fp;
+ char buf[BUFSIZ];
+ struct config *conf, *p;
+ struct config sentinel;
+
+ config_list = NULL;
+
+ if (!configfile)
+ configfile = _PATH_PREFIX_CONF;
+ fp = fopen(configfile, "r");
+ if (fp == NULL)
+ return -1;
+
+ p = &sentinel;
+ sentinel.next = NULL;
+ while (fgets(buf, sizeof(buf), fp) != NULL) {
+ conf = config_load1(buf);
+ if (conf) {
+ p->next = conf;
+ p = p->next;
+ }
+ }
+ config_list = sentinel.next;
+
+ fclose(fp);
+ return 0;
+}
+
+#if 0
+static void
+config_show1(const struct config *conf)
+{
+ const char *p;
+
+ p = prefix_string(&conf->match);
+ printf("%s", p ? p : "?");
+
+ if (conf->permit)
+ printf(" permit");
+ else
+ printf(" deny");
+
+ p = prefix_string(&conf->dest);
+ printf(" %s", p ? p : "?");
+
+ printf("\n");
+}
+
+static void
+config_show()
+{
+ struct config *conf;
+
+ for (conf = config_list; conf; conf = conf->next)
+ config_show1(conf);
+}
+#endif
+
+const struct config *
+config_match(struct sockaddr *sa1, struct sockaddr *sa2)
+{
+ static struct config conf;
+ const struct config *p;
+
+ if (sa1->sa_len > sizeof(conf.match.a) ||
+ sa2->sa_len > sizeof(conf.dest.a))
+ return NULL;
+
+ memset(&conf, 0, sizeof(conf));
+ if (!config_list) {
+ conf.permit = 1;
+ memcpy(&conf.match.a, sa1, sa1->sa_len);
+ memcpy(&conf.dest.a, sa2, sa2->sa_len);
+ return &conf;
+ }
+
+ for (p = config_list; p; p = p->next)
+ if (prefix_match(&p->match, sa1) && prefix_match(&p->dest, sa2))
+ return p;
+
+ return NULL;
+}
diff --git a/usr.sbin/faithd/prefix.h b/usr.sbin/faithd/prefix.h
new file mode 100644
index 0000000..4d6b3d5
--- /dev/null
+++ b/usr.sbin/faithd/prefix.h
@@ -0,0 +1,52 @@
+/* $KAME: prefix.h,v 1.4 2001/09/05 03:04:21 itojun Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (C) 2000 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.
+ */
+
+struct prefix {
+ struct sockaddr_storage a;
+ int l;
+};
+
+struct config {
+ struct config *next;
+
+ int permit;
+ struct prefix match;
+ struct prefix dest;
+ struct prefix src; /* src to use for outgoing connection */
+};
+
+#define _PATH_PREFIX_CONF "/etc/faithd.conf"
+
+extern const char *prefix_string(const struct prefix *);
+extern int prefix_match(const struct prefix *, const struct sockaddr *);
+extern int config_load(const char *);
+extern const struct config *config_match(struct sockaddr *, struct sockaddr *);
diff --git a/usr.sbin/faithd/tcp.c b/usr.sbin/faithd/tcp.c
new file mode 100644
index 0000000..2197694
--- /dev/null
+++ b/usr.sbin/faithd/tcp.c
@@ -0,0 +1,324 @@
+/* $KAME: tcp.c,v 1.13 2003/09/02 22:49:21 itojun Exp $ */
+
+/*
+ * Copyright (C) 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "faithd.h"
+
+static char tcpbuf[16*1024];
+ /* bigger than MSS and may be lesser than window size */
+static int tblen, tboff, oob_exists;
+static fd_set readfds, writefds, exceptfds;
+static char atmark_buf[2];
+static pid_t cpid = (pid_t)0;
+static pid_t ppid = (pid_t)0;
+volatile time_t child_lastactive = (time_t)0;
+static time_t parent_lastactive = (time_t)0;
+
+static void sig_ctimeout(int);
+static void sig_child(int);
+static void notify_inactive(void);
+static void notify_active(void);
+static void send_data(int, int, const char *, int);
+static void relay(int, int, const char *, int);
+
+/*
+ * Inactivity timer:
+ * - child side (ppid != 0) will send SIGUSR1 to parent every (FAITH_TIMEOUT/4)
+ * second if traffic is active. if traffic is inactive, don't send SIGUSR1.
+ * - parent side (ppid == 0) will check the last SIGUSR1 it have seen.
+ */
+static void
+sig_ctimeout(int sig __unused)
+{
+ /* parent side: record notification from the child */
+ if (dflag)
+ syslog(LOG_DEBUG, "activity timer from child");
+ child_lastactive = time(NULL);
+}
+
+/* parent will terminate if child dies. */
+static void
+sig_child(int sig __unused)
+{
+ int status;
+ pid_t pid;
+
+ pid = wait3(&status, WNOHANG, (struct rusage *)0);
+ if (pid > 0 && WEXITSTATUS(status))
+ syslog(LOG_WARNING, "child %ld exit status 0x%x",
+ (long)pid, status);
+ exit_success("terminate connection due to child termination");
+}
+
+static void
+notify_inactive()
+{
+ time_t t;
+
+ /* only on parent side... */
+ if (ppid)
+ return;
+
+ /* parent side should check for timeout. */
+ t = time(NULL);
+ if (dflag) {
+ syslog(LOG_DEBUG, "parent side %sactive, child side %sactive",
+ (FAITH_TIMEOUT < t - parent_lastactive) ? "in" : "",
+ (FAITH_TIMEOUT < t - child_lastactive) ? "in" : "");
+ }
+
+ if (FAITH_TIMEOUT < t - child_lastactive
+ && FAITH_TIMEOUT < t - parent_lastactive) {
+ /* both side timeouted */
+ signal(SIGCHLD, SIG_DFL);
+ kill(cpid, SIGTERM);
+ wait(NULL);
+ exit_failure("connection timeout");
+ /* NOTREACHED */
+ }
+}
+
+static void
+notify_active()
+{
+ if (ppid) {
+ /* child side: notify parent of active traffic */
+ time_t t;
+ t = time(NULL);
+ if (FAITH_TIMEOUT / 4 < t - child_lastactive) {
+ if (kill(ppid, SIGUSR1) < 0) {
+ exit_failure("terminate connection due to parent termination");
+ /* NOTREACHED */
+ }
+ child_lastactive = t;
+ }
+ } else {
+ /* parent side */
+ parent_lastactive = time(NULL);
+ }
+}
+
+static void
+send_data(int s_rcv, int s_snd, const char *service __unused, int direction)
+{
+ int cc;
+
+ if (oob_exists) {
+ cc = send(s_snd, atmark_buf, 1, MSG_OOB);
+ if (cc == -1)
+ goto retry_or_err;
+ oob_exists = 0;
+ if (s_rcv >= FD_SETSIZE)
+ exit_failure("descriptor too big");
+ FD_SET(s_rcv, &exceptfds);
+ }
+
+ for (; tboff < tblen; tboff += cc) {
+ cc = write(s_snd, tcpbuf + tboff, tblen - tboff);
+ if (cc < 0)
+ goto retry_or_err;
+ }
+#ifdef DEBUG
+ if (tblen) {
+ if (tblen >= sizeof(tcpbuf))
+ tblen = sizeof(tcpbuf) - 1;
+ tcpbuf[tblen] = '\0';
+ syslog(LOG_DEBUG, "from %s (%dbytes): %s",
+ direction == 1 ? "client" : "server", tblen, tcpbuf);
+ }
+#endif /* DEBUG */
+ tblen = 0; tboff = 0;
+ if (s_snd >= FD_SETSIZE)
+ exit_failure("descriptor too big");
+ FD_CLR(s_snd, &writefds);
+ if (s_rcv >= FD_SETSIZE)
+ exit_failure("descriptor too big");
+ FD_SET(s_rcv, &readfds);
+ return;
+ retry_or_err:
+ if (errno != EAGAIN)
+ exit_failure("writing relay data failed: %s", strerror(errno));
+ if (s_snd >= FD_SETSIZE)
+ exit_failure("descriptor too big");
+ FD_SET(s_snd, &writefds);
+}
+
+static void
+relay(int s_rcv, int s_snd, const char *service, int direction)
+{
+ int atmark, error, maxfd;
+ struct timeval tv;
+ fd_set oreadfds, owritefds, oexceptfds;
+
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ FD_ZERO(&exceptfds);
+ fcntl(s_snd, F_SETFD, O_NONBLOCK);
+ oreadfds = readfds; owritefds = writefds; oexceptfds = exceptfds;
+ if (s_rcv >= FD_SETSIZE)
+ exit_failure("descriptor too big");
+ FD_SET(s_rcv, &readfds);
+ FD_SET(s_rcv, &exceptfds);
+ oob_exists = 0;
+ maxfd = (s_rcv > s_snd) ? s_rcv : s_snd;
+
+ for (;;) {
+ tv.tv_sec = FAITH_TIMEOUT / 4;
+ tv.tv_usec = 0;
+ oreadfds = readfds;
+ owritefds = writefds;
+ oexceptfds = exceptfds;
+ error = select(maxfd + 1, &readfds, &writefds, &exceptfds, &tv);
+ if (error == -1) {
+ if (errno == EINTR)
+ continue;
+ exit_failure("select: %s", strerror(errno));
+ } else if (error == 0) {
+ readfds = oreadfds;
+ writefds = owritefds;
+ exceptfds = oexceptfds;
+ notify_inactive();
+ continue;
+ }
+
+ /* activity notification */
+ notify_active();
+
+ if (FD_ISSET(s_rcv, &exceptfds)) {
+ error = ioctl(s_rcv, SIOCATMARK, &atmark);
+ if (error != -1 && atmark == 1) {
+ int cc;
+ oob_read_retry:
+ cc = read(s_rcv, atmark_buf, 1);
+ if (cc == 1) {
+ if (s_rcv >= FD_SETSIZE)
+ exit_failure("descriptor too big");
+ FD_CLR(s_rcv, &exceptfds);
+ if (s_snd >= FD_SETSIZE)
+ exit_failure("descriptor too big");
+ FD_SET(s_snd, &writefds);
+ oob_exists = 1;
+ } else if (cc == -1) {
+ if (errno == EINTR)
+ goto oob_read_retry;
+ exit_failure("reading oob data failed"
+ ": %s",
+ strerror(errno));
+ }
+ }
+ }
+ if (FD_ISSET(s_rcv, &readfds)) {
+ relaydata_read_retry:
+ tblen = read(s_rcv, tcpbuf, sizeof(tcpbuf));
+ tboff = 0;
+
+ switch (tblen) {
+ case -1:
+ if (errno == EINTR)
+ goto relaydata_read_retry;
+ exit_failure("reading relay data failed: %s",
+ strerror(errno));
+ /* NOTREACHED */
+ case 0:
+ /* to close opposite-direction relay process */
+ shutdown(s_snd, 0);
+
+ close(s_rcv);
+ close(s_snd);
+ exit_success("terminating %s relay", service);
+ /* NOTREACHED */
+ default:
+ if (s_rcv >= FD_SETSIZE)
+ exit_failure("descriptor too big");
+ FD_CLR(s_rcv, &readfds);
+ if (s_snd >= FD_SETSIZE)
+ exit_failure("descriptor too big");
+ FD_SET(s_snd, &writefds);
+ break;
+ }
+ }
+ if (FD_ISSET(s_snd, &writefds))
+ send_data(s_rcv, s_snd, service, direction);
+ }
+}
+
+void
+tcp_relay(int s_src, int s_dst, const char *service)
+{
+ syslog(LOG_INFO, "starting %s relay", service);
+
+ child_lastactive = parent_lastactive = time(NULL);
+
+ cpid = fork();
+ switch (cpid) {
+ case -1:
+ exit_failure("tcp_relay: can't fork grand child: %s",
+ strerror(errno));
+ /* NOTREACHED */
+ case 0:
+ /* child process: relay going traffic */
+ ppid = getppid();
+ /* this is child so reopen log */
+ closelog();
+ openlog(logname, LOG_PID | LOG_NOWAIT, LOG_DAEMON);
+ relay(s_src, s_dst, service, 1);
+ /* NOTREACHED */
+ default:
+ /* parent process: relay coming traffic */
+ ppid = (pid_t)0;
+ signal(SIGUSR1, sig_ctimeout);
+ signal(SIGCHLD, sig_child);
+ relay(s_dst, s_src, service, 0);
+ /* NOTREACHED */
+ }
+}
diff --git a/usr.sbin/faithd/test/faithd.rb b/usr.sbin/faithd/test/faithd.rb
new file mode 100644
index 0000000..682f540
--- /dev/null
+++ b/usr.sbin/faithd/test/faithd.rb
@@ -0,0 +1,312 @@
+# faithd, ruby version. requires v6-enabled ruby.
+#
+# highly experimental (not working right at all) and very limited
+# functionality.
+#
+# $Id: faithd.rb,v 1.1.2.4 1999/05/10 17:06:30 itojun Exp $
+# $FreeBSD$
+
+require "socket"
+require "thread"
+
+# XXX should be derived from system headers
+IPPROTO_IPV6 = 41
+IPV6_FAITH = 29
+DEBUG = true
+DEBUG_LOOPBACK = true
+
+# TODO: OOB data handling
+def tcpcopy(s1, s2, m)
+ STDERR.print "tcpcopy #{s1} #{s2}\n" if DEBUG
+ buf = ""
+ while TRUE
+ begin
+ buf = s1.sysread(100)
+ s2.syswrite(buf)
+ rescue EOFError
+ break
+ rescue IOError
+ break
+ end
+ end
+ STDERR.print "tcpcopy #{s1} #{s2} finished\n" if DEBUG
+ s1.shutdown(0)
+ s2.shutdown(1)
+end
+
+def relay_ftp_passiveconn(s6, s4, dport6, dport4)
+ Thread.start do
+ d6 = TCPserver.open("::", dport6).accept
+ d4 = TCPsocket.open(s4.getpeer[3], dport4)
+ t = []
+ t[0] = Thread.start do
+ tcpcopy(d6, d4)
+ end
+ t[1] = Thread.start do
+ tcpcopy(d4, d6)
+ end
+ for i in t
+ i.join
+ end
+ d4.close
+ d6.close
+ end
+end
+
+def ftp_parse_2428(line)
+ if (line[0] != line[line.length - 1])
+ return nil
+ end
+ t = line.split(line[0 .. 0]) # as string
+ if (t.size != 4 || t[1] !~ /^[12]$/ || t[3] !~ /^\d+$/)
+ return nil
+ end
+ return t[1 .. 3]
+end
+
+def relay_ftp_command(s6, s4, state)
+ STDERR.print "relay_ftp_command start\n" if DEBUG
+ while TRUE
+ begin
+ STDERR.print "s6.gets\n" if DEBUG
+ line = s6.gets
+ STDERR.print "line is #{line}\n" if DEBUG
+ if line == nil
+ return nil
+ end
+
+ # translate then copy
+ STDERR.print "line is #{line}\n" if DEBUG
+ if (line =~ /^EPSV\r\n/i)
+ STDERR.print "EPSV -> PASV\n" if DEBUG
+ line = "PASV\n"
+ state = "EPSV"
+ elsif (line =~ /^EPRT\s+(.+)\r\n/i)
+ t = ftp_parse_2428($1)
+ if t == nil
+ s6.puts "501 illegal parameter to EPRT\r\n"
+ next
+ end
+
+ # some tricks should be here
+ s6.puts "501 illegal parameter to EPRT\r\n"
+ next
+ end
+ STDERR.print "fail: send #{line} as is\n" if DEBUG
+ s4.puts(line)
+ break
+ rescue EOFError
+ return nil
+ rescue IOError
+ return nil
+ end
+ end
+ STDERR.print "relay_ftp_command finish\n" if DEBUG
+ return state
+end
+
+def relay_ftp_status(s4, s6, state)
+ STDERR.print "relay_ftp_status start\n" if DEBUG
+ while TRUE
+ begin
+ line = s4.gets
+ if line == nil
+ return nil
+ end
+
+ # translate then copy
+ s6.puts(line)
+
+ next if line =~ /^\d\d\d-/
+ next if line !~ /^\d/
+
+ # special post-processing
+ case line
+ when /^221 / # result to QUIT
+ s4.shutdown(0)
+ s6.shutdown(1)
+ end
+
+ break if (line =~ /^\d\d\d /)
+ rescue EOFError
+ return nil
+ rescue IOError
+ return nil
+ end
+ end
+ STDERR.print "relay_ftp_status finish\n" if DEBUG
+ return state
+end
+
+def relay_ftp(sock, name)
+ STDERR.print "relay_ftp(#{sock}, #{name})\n" if DEBUG
+ while TRUE
+ STDERR.print "relay_ftp(#{sock}, #{name}) accepting\n" if DEBUG
+ s = sock.accept
+ STDERR.print "relay_ftp(#{sock}, #{name}) accepted #{s}\n" if DEBUG
+ Thread.start do
+ threads = []
+ STDERR.print "accepted #{s} -> #{Thread.current}\n" if DEBUG
+ s6 = s
+ dest6 = s.addr[3]
+ if !DEBUG_LOOPBACK
+ t = s.getsockname.unpack("x8 x12 C4")
+ dest4 = "#{t[0]}.#{t[1]}.#{t[2]}.#{t[3]}"
+ port4 = s.addr[1]
+ else
+ dest4 = "127.0.0.1"
+ port4 = "ftp"
+ end
+ if DEBUG
+ STDERR.print "IPv6 dest: #{dest6} IPv4 dest: #{dest4}\n" if DEBUG
+ end
+ STDERR.print "connect to #{dest4} #{port4}\n" if DEBUG
+ s4 = TCPsocket.open(dest4, port4)
+ STDERR.print "connected to #{dest4} #{port4}, #{s4.addr[1]}\n" if DEBUG
+ state = 0
+ while TRUE
+ # translate status line
+ state = relay_ftp_status(s4, s6, state)
+ break if state == nil
+ # translate command line
+ state = relay_ftp_command(s6, s4, state)
+ break if state == nil
+ end
+ STDERR.print "relay_ftp(#{sock}, #{name}) closing s4\n" if DEBUG
+ s4.close
+ STDERR.print "relay_ftp(#{sock}, #{name}) closing s6\n" if DEBUG
+ s6.close
+ STDERR.print "relay_ftp(#{sock}, #{name}) done\n" if DEBUG
+ end
+ end
+ STDERR.print "relay_ftp(#{sock}, #{name}) finished\n" if DEBUG
+end
+
+def relay_tcp(sock, name)
+ STDERR.print "relay_tcp(#{sock}, #{name})\n" if DEBUG
+ while TRUE
+ STDERR.print "relay_tcp(#{sock}, #{name}) accepting\n" if DEBUG
+ s = sock.accept
+ STDERR.print "relay_tcp(#{sock}, #{name}) accepted #{s}\n" if DEBUG
+ Thread.start do
+ threads = []
+ STDERR.print "accepted #{s} -> #{Thread.current}\n" if DEBUG
+ s6 = s
+ dest6 = s.addr[3]
+ if !DEBUG_LOOPBACK
+ t = s.getsockname.unpack("x8 x12 C4")
+ dest4 = "#{t[0]}.#{t[1]}.#{t[2]}.#{t[3]}"
+ port4 = s.addr[1]
+ else
+ dest4 = "127.0.0.1"
+ port4 = "telnet"
+ end
+ if DEBUG
+ STDERR.print "IPv6 dest: #{dest6} IPv4 dest: #{dest4}\n" if DEBUG
+ end
+ STDERR.print "connect to #{dest4} #{port4}\n" if DEBUG
+ s4 = TCPsocket.open(dest4, port4)
+ STDERR.print "connected to #{dest4} #{port4}, #{s4.addr[1]}\n" if DEBUG
+ [0, 1].each do |i|
+ threads[i] = Thread.start do
+ if (i == 0)
+ tcpcopy(s6, s4)
+ else
+ tcpcopy(s4, s6)
+ end
+ end
+ end
+ STDERR.print "relay_tcp(#{sock}, #{name}) wait\n" if DEBUG
+ for i in threads
+ STDERR.print "relay_tcp(#{sock}, #{name}) wait #{i}\n" if DEBUG
+ i.join
+ STDERR.print "relay_tcp(#{sock}, #{name}) wait #{i} done\n" if DEBUG
+ end
+ STDERR.print "relay_tcp(#{sock}, #{name}) closing s4\n" if DEBUG
+ s4.close
+ STDERR.print "relay_tcp(#{sock}, #{name}) closing s6\n" if DEBUG
+ s6.close
+ STDERR.print "relay_tcp(#{sock}, #{name}) done\n" if DEBUG
+ end
+ end
+ STDERR.print "relay_tcp(#{sock}, #{name}) finished\n" if DEBUG
+end
+
+def usage()
+ STDERR.print "usage: #{$0} [-f] port...\n"
+end
+
+#------------------------------------------------------------
+
+$mode = "tcp"
+
+while ARGV[0] =~ /^-/ do
+ case ARGV[0]
+ when /^-f/
+ $mode = "ftp"
+ else
+ usage()
+ exit 0
+ end
+ ARGV.shift
+end
+
+if ARGV.length == 0
+ usage()
+ exit 1
+end
+
+ftpport = Socket.getservbyname("ftp")
+
+res = []
+for port in ARGV
+ t = Socket.getaddrinfo(nil, port, Socket::PF_INET6, Socket::SOCK_STREAM,
+ nil, Socket::AI_PASSIVE)
+ if (t.size <= 0)
+ STDERR.print "FATAL: getaddrinfo failed (port=#{port})\n"
+ exit 1
+ end
+ res += t
+end
+
+sockpool = []
+names = []
+listenthreads = []
+
+res.each do |i|
+ s = TCPserver.new(i[3], i[1])
+ n = Socket.getnameinfo(s.getsockname, Socket::NI_NUMERICHOST|Socket::NI_NUMERICSERV).join(" port ")
+ if i[6] == IPPROTO_IPV6
+ s.setsockopt(i[6], IPV6_FAITH, 1)
+ end
+ s.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, 1)
+ sockpool.push s
+ names.push n
+end
+
+if DEBUG
+ (0 .. sockpool.size - 1).each do |i|
+ STDERR.print "listen[#{i}]: #{sockpool[i]} #{names[i]}\n" if DEBUG
+ end
+end
+
+(0 .. sockpool.size - 1).each do |i|
+ listenthreads[i] = Thread.start do
+ if DEBUG
+ STDERR.print "listen[#{i}]: thread #{Thread.current}\n" if DEBUG
+ end
+ STDERR.print "listen[#{i}]: thread #{Thread.current}\n" if DEBUG
+ case $mode
+ when "tcp"
+ relay_tcp(sockpool[i], names[i])
+ when "ftp"
+ relay_ftp(sockpool[i], names[i])
+ end
+ end
+end
+
+for i in listenthreads
+ i.join
+end
+
+exit 0
diff --git a/usr.sbin/fdcontrol/Makefile b/usr.sbin/fdcontrol/Makefile
new file mode 100644
index 0000000..e2b54aa
--- /dev/null
+++ b/usr.sbin/fdcontrol/Makefile
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../fdread
+
+PROG= fdcontrol
+SRCS= fdcontrol.c fdutil.c
+WARNS?= 6
+CFLAGS+= -I${.CURDIR}/../fdread
+MAN= fdcontrol.8
+
+.if ${MACHINE} == "pc98"
+CFLAGS+= -DPC98
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/fdcontrol/fdcontrol.8 b/usr.sbin/fdcontrol/fdcontrol.8
new file mode 100644
index 0000000..cbc6883
--- /dev/null
+++ b/usr.sbin/fdcontrol/fdcontrol.8
@@ -0,0 +1,335 @@
+'\" t
+.\" Copyright (C) 1994, 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 25, 2001
+.Os
+.Dt FDCONTROL 8
+.Sh NAME
+.Nm fdcontrol
+.Nd display and modify floppy disk parameters
+.Sh SYNOPSIS
+.Nm
+.Op Fl F
+.Op Fl d Ar dbg
+.Op Fl f Ar fmt
+.Op Fl s Ar fmtstr
+.Op Fl v
+.Ar device
+.Sh DESCRIPTION
+The
+.Nm
+utility allows the modification of the run-time behavior of the
+.Xr fdc 4
+driver for the device specified by
+.Ar device .
+.Pp
+Commands are implemented to query the current device density settings
+as well as the underlying device hardware as registered with the
+driver, to manipulate debugging levels, and to adjust the device
+density settings.
+All the operations that manipulate the kernel
+settings are restricted to the superuser (by the device driver), while
+all inquiry requests only require read access to
+.Ar device .
+.Pp
+The
+.Ar device
+argument should always be given as a full path name, e.g.\&
+.Pa /dev/fd0 .
+.Ss Inquiry Commands
+Running the
+.Nm
+utility without any of the optional flags will report the drive type
+that is registered with the device driver.
+In the shortest form, a single string describing the drive type will
+be returned.
+Possible values are:
+.Dq Li 360K ,
+.Dq Li 1.2M ,
+.Dq Li 720K ,
+.Dq Li 1.44M ,
+.Dq Li 2.88M ,
+or
+.Dq Li unknown .
+This information is primarily intended to be easily parsable by
+scripts.
+.Pp
+In order to add some descriptive text that makes the output better
+human readable, the flag
+.Fl v
+can be added.
+.Pp
+Specifying flag
+.Fl F
+will report the device's density settings in a form that is suitable
+as input to the
+.Fl s Ar fmtstr
+option (see below).
+Again, together with
+.Fl v ,
+some more text will be returned, including the total capacity of the
+density settings in kilobytes.
+.Ss Debug Control
+If the
+.Xr fdc 4
+driver was configured with the
+.Dv FDC_DEBUG
+option, by default, device debugging information is still disabled
+since it could produce huge amounts of kernel messages.
+It needs to
+be turned on using
+.Nm
+together with
+.Dq Fl d Li 1 ,
+usually immediately before starting an operation on the respective
+device the debug information is wanted for, and later turned off again
+using
+.Dq Fl d Li 0 .
+Note that debugging levels are a driver's global option that will
+affect any drives and controllers using the
+.Xr fdc 4
+driver, regardless which
+.Ar device
+was specified on the
+.Nm
+command line.
+.Ss Density Control
+The
+.Xr fdc 4
+control utilities support two different options how to specify device
+density settings.
+The first form uses
+.Fl f Ar fmt
+to specify the format of the medium in kilobytes.
+Depending on the
+underlying drive type, the value is compared against a table of known
+commonly used device density settings for that drive, and if a match
+is found, those settings will be used.
+Currently, the following
+values for the respective drive types are acceptable:
+.Bl -item
+.It
+2.88M and 1.44M drives:
+.Bd -ragged -offset indent -compact
+.TS
+lB lB lB lB lB lB lB
+r l l l l l l.
+KB sectrac secsize ncyls speed heads flags
+1721 21 2 (512) 82 500 2 MFM
+1476 18 2 (512) 82 500 2 MFM
+1440 18 2 (512) 80 500 2 MFM
+1200 15 2 (512) 80 500 2 MFM
+820 10 2 (512) 82 250 2 MFM
+800 10 2 (512) 80 250 2 MFM
+720 9 2 (512) 80 250 2 MFM
+.TE
+.Ed
+.It
+1.2M drives:
+.Bd -ragged -offset indent -compact
+.TS
+lB lB lB lB lB lB lB
+r l l l l l l.
+KB sectrac secsize ncyls speed heads flags
+1200 15 2 (512) 80 500 2 MFM
+1232 8 3 (1024) 77 500 2 MFM
+1476 18 2 (512) 82 500 2 MFM
+1440 18 2 (512) 80 500 2 MFM
+1200 15 2 (512) 80 500 2 MFM
+820 10 2 (512) 82 300 2 MFM
+800 10 2 (512) 80 300 2 MFM
+720 9 2 (512) 80 300 2 MFM
+360 9 2 (512) 40 300 2 MFM,2STEP
+640 8 2 (512) 80 300 2 MFM
+.TE
+.Ed
+.It
+720K drives:
+.Bd -ragged -offset indent -compact
+.TS
+lB lB lB lB lB lB lB
+r l l l l l l.
+KB sectrac secsize ncyls speed heads flags
+720 9 2 (512) 80 250 2 MFM
+.TE
+.Ed
+.It
+360K drives:
+.Bd -ragged -offset indent -compact
+.TS
+lB lB lB lB lB lB lB
+r l l l l l l.
+KB sectrac secsize ncyls speed heads flags
+360 9 2 (512) 40 250 2 MFM
+.TE
+.Ed
+.El
+.Pp
+The second form to specify a device density uses
+.Fl s Ar fmtstr
+to explicitly specify each parameter in detail.
+The argument
+.Ar fmtstr
+is a comma-separated list of values of the form:
+.Pp
+.Sm off
+.Ar sectrac , secsize , datalen , gap , ncyls , speed ,
+.Ar heads , f_gap , f_inter , offs2 , flags
+.Sm on
+.Pp
+The meaning of the parameters is:
+.Bl -tag -width ".Ar secsize"
+.It Ar sectrac
+The number of sectors per track.
+.It Ar secsize
+The sector size code, 0 = 128 bytes (or less), 1 = 256 bytes, 2 = 512
+bytes, 3 = 1024 bytes.
+.It Ar datalen
+The actual sector size if the size code is 0, or the (ignored) value
+0xFF for larger size codes.
+.It Ar gap
+The length of the gap 3 parameter for read/write operations.
+.It Ar ncyls
+The number of cylinders.
+.It Ar speed
+The transfer speed in kilobytes per second.
+Can be 250, 300, 500, or
+1000, but each drive type only supports a subset of these values.
+.It Ar heads
+The number of heads.
+.It Ar f_gap
+The length of the gap 3 when formatting media.
+.It Ar f_inter
+The sector interleave to be applied when formatting.
+0 means no
+interleave, 1 means 1:1 etc.
+.It Ar offs2
+The offset of the sector numbers on side 2 (i.e., head number 1).
+Normally, sector numbering on both sides starts with 1.
+.It Ar flags
+A list from one of the following flag values:
+.Pp
+.Bl -tag -width ".Cm +perpend" -compact
+.It Cm +mfm
+Use MFM encoding.
+.It Cm -mfm
+Use FM (single-density) encoding.
+.It Cm +2step
+Use 2 steps per each cylinder (for accessing 40-cylinder media in
+80-cylinder drives).
+.It Cm -2step
+Do not use 2 steps per cylinder, i.e., access each physical cylinder
+of the drive.
+.It Cm +perpend
+Use perpendicular recording (for 2.88 MB media, currently not
+supported).
+.It Cm -perpend
+Use longitudinal recording.
+.El
+.El
+.Pp
+For any missing parameter, the current value will be used, so only
+actual changes need to be specified.
+Thus to turn off a flag bit
+(like
+.Cm +mfm
+which is the default for all drive types), the form with a leading
+minus sign must explicitly be used.
+.Sh EXAMPLES
+A simple inquiry about the drive type:
+.Bd -literal -offset indent
+$ fdcontrol /dev/fd0
+1.44M
+.Ed
+.Pp
+Same as above, but with verbose output.
+Note that the result is about
+the
+.Em "drive type" ,
+as opposed to a
+.Em "device density" ,
+so it is independent from the actual subdevice being used for
+.Ar device .
+.Bd -literal -offset indent
+$ fdcontrol -v /dev/fd0
+/dev/fd0: 1.44M drive (3.5" high-density)
+.Ed
+.Pp
+Inquiry about the density settings:
+.Bd -literal -offset indent
+$ fdcontrol -F /dev/fd0
+18,512,0xff,0x1b,80,500,2,0x6c,1,0,+mfm
+.Ed
+.Pp
+The verbose flag makes this human readable:
+.Bd -literal -offset indent
+/dev/fd0: 1440 KB media type
+ Format: 18,512,0xff,0x1b,80,500,2,0x6c,1,0,+mfm
+ Sector size: 512
+ Sectors/track: 18
+ Heads/cylinder: 2
+ Cylinders/disk: 80
+ Transfer rate: 500 kbps
+ Sector gap: 27
+ Format gap: 108
+ Interleave: 1
+ Side offset: 0
+ Flags <MFM>
+.Ed
+.Pp
+As indicated, trailing commas in the parameter list may be omitted.
+.Pp
+In order to access archaic 160 KB single-density (FM encoded) 5.25
+media in a modern 1.2M drive, something like the following definition
+would be needed.
+(Note that not all controller hardware is actually
+capable of handling FM encoding at all.)
+.Bd -literal
+# fdcontrol -s 16,128,0x80,0x2,40,300,,0x10,,,-mfm,+2step /dev/fd1.1
+.Ed
+.Pp
+It is still possible to hook up 8" drives to most modern floppy
+controllers, given the right cable magic.
+(On PC hardware, tell the BIOS that it is a 5.25" drive.)
+The classical 128/26/2/77 format can be read with this entry
+.Bd -literal -offset indent
+fdcontrol -s 26,128,0x80,0x2,77,500,2,0x10,,,-mfm /dev/fd0
+.Ed
+.Sh SEE ALSO
+.Xr fdc 4
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 2.0 ,
+and was vastly overhauled in
+.Fx 5.0 .
+.Sh AUTHORS
+The program and this man page was contributed by
+.An J\(:org Wunsch ,
+Dresden.
diff --git a/usr.sbin/fdcontrol/fdcontrol.c b/usr.sbin/fdcontrol/fdcontrol.c
new file mode 100644
index 0000000..f1a9233
--- /dev/null
+++ b/usr.sbin/fdcontrol/fdcontrol.c
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 1994, 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/fdcio.h>
+#include <sys/file.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "fdutil.h"
+
+
+static int format, verbose, show = 1, showfmt;
+static char *fmtstring;
+
+static void showdev(enum fd_drivetype, const char *);
+static void usage(void);
+
+static void
+usage(void)
+{
+ errx(EX_USAGE,
+ "usage: fdcontrol [-F] [-d dbg] [-f fmt] [-s fmtstr] [-v] device");
+}
+
+void
+showdev(enum fd_drivetype type, const char *fname)
+{
+ const char *name, *descr;
+
+ getname(type, &name, &descr);
+ if (verbose)
+ printf("%s: %s drive (%s)\n", fname, name, descr);
+ else
+ printf("%s\n", name);
+}
+
+int
+main(int argc, char **argv)
+{
+ enum fd_drivetype type;
+ struct fd_type ft, newft, *fdtp;
+ const char *name, *descr;
+ int fd, i, mode, autofmt;
+
+ autofmt = 0;
+ while((i = getopt(argc, argv, "aFf:s:v")) != -1)
+ switch(i) {
+
+ case 'a':
+ autofmt = 1;
+ case 'F':
+ showfmt = 1;
+ show = 0;
+ break;
+
+ case 'f':
+ if (!strcmp(optarg, "auto")) {
+ format = -1;
+ } else if (getnum(optarg, &format)) {
+ fprintf(stderr,
+ "Bad argument %s to -f option; must be numeric\n",
+ optarg);
+ usage();
+ }
+ show = 0;
+ break;
+
+ case 's':
+ fmtstring = optarg;
+ show = 0;
+ break;
+
+ case 'v':
+ verbose++;
+ break;
+
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if(argc != 1)
+ usage();
+
+ if (show || showfmt)
+ mode = O_RDONLY | O_NONBLOCK;
+ else
+ mode = O_RDWR;
+
+ if((fd = open(argv[0], mode)) < 0)
+ err(EX_UNAVAILABLE, "open(%s)", argv[0]);
+
+ if (ioctl(fd, FD_GDTYPE, &type) == -1)
+ err(EX_OSERR, "ioctl(FD_GDTYPE)");
+ if (ioctl(fd, FD_GTYPE, &ft) == -1)
+ err(EX_OSERR, "ioctl(FD_GTYPE)");
+
+ if (show) {
+ showdev(type, argv[0]);
+ return (0);
+ }
+
+ if (autofmt) {
+ memset(&newft, 0, sizeof newft);
+ ft = newft;
+ }
+
+ if (format) {
+ getname(type, &name, &descr);
+ fdtp = get_fmt(format, type);
+ if (fdtp == 0)
+ errx(EX_USAGE,
+ "unknown format %d KB for drive type %s",
+ format, name);
+ ft = *fdtp;
+ }
+
+ if (fmtstring) {
+ parse_fmt(fmtstring, type, ft, &newft);
+ ft = newft;
+ }
+
+ if (showfmt) {
+ if (verbose) {
+ const char *s;
+
+ printf("%s: %d KB media type\n", argv[0],
+ (128 << ft.secsize) * ft.size / 1024);
+ printf("\tFormat:\t\t");
+ print_fmt(ft);
+ if (ft.datalen != 0xff &&
+ ft.datalen != (128 << ft.secsize))
+ printf("\tData length:\t%d\n", ft.datalen);
+ printf("\tSector size:\t%d\n", 128 << ft.secsize);
+ printf("\tSectors/track:\t%d\n", ft.sectrac);
+ printf("\tHeads/cylinder:\t%d\n", ft.heads);
+ printf("\tCylinders/disk:\t%d\n", ft.tracks);
+ switch (ft.trans) {
+ case 0: printf("\tTransfer rate:\t500 kbps\n"); break;
+ case 1: printf("\tTransfer rate:\t300 kbps\n"); break;
+ case 2: printf("\tTransfer rate:\t250 kbps\n"); break;
+ case 3: printf("\tTransfer rate:\t1 Mbps\n"); break;
+ }
+ printf("\tSector gap:\t%d\n", ft.gap);
+ printf("\tFormat gap:\t%d\n", ft.f_gap);
+ printf("\tInterleave:\t%d\n", ft.f_inter);
+ printf("\tSide offset:\t%d\n", ft.offset_side2);
+ printf("\tFlags\t\t<");
+ s = "";
+ if (ft.flags & FL_MFM) {
+ printf("%sMFM", s);
+ s = ",";
+ }
+ if (ft.flags & FL_2STEP) {
+ printf("%s2STEP", s);
+ s = ",";
+ }
+ if (ft.flags & FL_PERPND) {
+ printf("%sPERPENDICULAR", s);
+ s = ",";
+ }
+ if (ft.flags & FL_AUTO) {
+ printf("%sAUTO", s);
+ s = ",";
+ }
+ printf(">\n");
+ } else {
+ print_fmt(ft);
+ }
+ return (0);
+ }
+
+ if (format || fmtstring) {
+ if (ioctl(fd, FD_STYPE, &ft) == -1)
+ err(EX_OSERR, "ioctl(FD_STYPE)");
+ return (0);
+ }
+
+ return 0;
+}
diff --git a/usr.sbin/fdformat/Makefile b/usr.sbin/fdformat/Makefile
new file mode 100644
index 0000000..a172811
--- /dev/null
+++ b/usr.sbin/fdformat/Makefile
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../fdread
+
+PROG= fdformat
+SRCS= fdformat.c fdutil.c
+
+WARNS?= 6
+CFLAGS+= -I${.CURDIR}/../fdread
+
+.if ${MACHINE} == "pc98"
+CFLAGS+= -DPC98
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/fdformat/fdformat.1 b/usr.sbin/fdformat/fdformat.1
new file mode 100644
index 0000000..25891c0
--- /dev/null
+++ b/usr.sbin/fdformat/fdformat.1
@@ -0,0 +1,180 @@
+.\" Copyright (C) 1993, 1994, 1995, 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 25, 2001
+.Os
+.Dt FDFORMAT 1
+.Sh NAME
+.Nm fdformat
+.Nd format floppy disks
+.Sh SYNOPSIS
+.Nm
+.Op Fl F Ar fill
+.Op Fl f Ar fmt
+.Op Fl s Ar fmtstr
+.Op Fl nqvy
+.Ar device
+.Sh DESCRIPTION
+The
+.Nm
+utility formats a floppy disk at
+.Ar device ,
+where
+.Ar device
+may either be given as a full path
+name of a device node for a floppy disk drive
+(e.g.\&
+.Pa /dev/fd0 ) ,
+or using an abbreviated name that will be looked up
+under
+.Pa /dev
+(e.g.\&
+.Dq Li fd0 ) .
+.Pp
+The options are as follows:
+.Bl -tag -width ".Fl s Ar fmtstr"
+.It Fl F Ar fill
+Use
+.Ar fill
+as the fill byte for newly formatted sectors.
+The
+.Ar fill
+argument
+must be a number in the range 0 through 255 using common C
+language notation.
+The default value is
+.Dq Li 0xf6 .
+.It Fl f Ar fmt
+Specify the density settings for a
+.Ar fmt
+kilobyte format, as described in
+.Xr fdcontrol 8 .
+.It Fl s Ar fmtstr
+Specify the density settings using explicit parameters, as
+described in
+.Xr fdcontrol 8 .
+.It Fl n
+Do not verify floppy after formatting.
+.It Fl q
+Suppress any normal output from the command, and do not ask the
+user for a confirmation whether to format the floppy disk at
+.Ar device .
+.It Fl v
+Do not format, verify only.
+.It Fl y
+Do not ask for confirmation whether to format the floppy disk but
+still report formatting status.
+.El
+.Pp
+For non-autoselecting subdevices, neither
+.Fl f Ar fmt
+nor
+.Fl s Ar fmtstr
+may be specified, since the preconfigured media density settings
+from the kernel driver will always be used.
+However, if
+.Ar device
+is a device with automatic media density selection (see
+.Xr fdc 4 ) ,
+both methods can be used to override the density settings for the
+newly formatted medium (without permanently changing the density
+settings of
+.Ar device ) .
+.Pp
+If the
+.Fl q
+flag has not been specified, the user is asked for a confirmation
+of the intended formatting process.
+In order to continue, an answer
+of
+.Ql y
+must be given.
+.Pp
+Note that
+.Nm
+does only perform low-level formatting.
+In order to create
+a file system on the medium, see the commands
+.Xr newfs 8
+for a
+.Tn UFS
+file system, or
+.Xr newfs_msdos 8
+for an
+.Tn MS-DOS
+(FAT)
+file system.
+.Sh EXIT STATUS
+An exit status of 0 is returned upon successful operation.
+Exit status
+1 is returned on any errors during floppy formatting, and an exit status
+of 2 reflects invalid arguments given to the program (along with an
+appropriate information written to diagnostic output).
+.Sh DIAGNOSTICS
+Unless
+.Fl q
+has been specified, a single letter is printed to standard output
+to inform the user about the progress of work.
+First, an
+.Ql F
+is printed when the track is being formatted, then a
+.Ql V
+while it is being verified, and if an error has been detected, it
+will finally change to
+.Ql E .
+Detailed status information (cylinder, head and sector number, and the
+exact cause of the error) will be printed for up to 10 errors after the
+entire formatting process has completed.
+.Sh SEE ALSO
+.Xr fdc 4 ,
+.Xr fdcontrol 8 ,
+.Xr newfs 8 ,
+.Xr newfs_msdos 8
+.Sh HISTORY
+The
+.Nm
+utility
+has been developed for
+.Bx 386 0.1
+and upgraded to the new
+.Xr fdc 4
+floppy disk driver.
+It later became part of the
+.Fx 1.1
+system.
+Starting with
+.Fx 5.0 ,
+it uses the unified density specifications as described in
+.Xr fdcontrol 8 .
+.Sh AUTHORS
+.An -nosplit
+The program has been contributed by
+.An J\(:org Wunsch ,
+Dresden, with changes by
+.An Serge Vakulenko
+and
+.An Andrey A. Chernov ,
+Moscow.
diff --git a/usr.sbin/fdformat/fdformat.c b/usr.sbin/fdformat/fdformat.c
new file mode 100644
index 0000000..8e29e40
--- /dev/null
+++ b/usr.sbin/fdformat/fdformat.c
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 1992-1994,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$
+ */
+
+#include <sys/types.h>
+#include <sys/fdcio.h>
+#include <sys/stat.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 <strings.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "fdutil.h"
+
+static void
+format_track(int fd, int cyl, int secs, int head, int rate,
+ int gaplen, int secsize, int fill, int interleave,
+ int offset)
+{
+ struct fd_formb f;
+ int i, j, il[FD_MAX_NSEC + 1];
+
+ memset(il, 0, sizeof il);
+ for(j = 0, i = 1 + offset; i <= secs + offset; i++) {
+ while(il[(j % secs) + 1])
+ j++;
+ il[(j % secs) + 1] = i;
+ j += interleave;
+ }
+
+ f.format_version = FD_FORMAT_VERSION;
+ f.head = head;
+ f.cyl = cyl;
+ f.transfer_rate = rate;
+
+ f.fd_formb_secshift = secsize;
+ f.fd_formb_nsecs = secs;
+ f.fd_formb_gaplen = gaplen;
+ f.fd_formb_fillbyte = fill;
+ for(i = 0; i < secs; i++) {
+ f.fd_formb_cylno(i) = cyl;
+ f.fd_formb_headno(i) = head;
+ f.fd_formb_secno(i) = il[i+1];
+ f.fd_formb_secsize(i) = secsize;
+ }
+ if(ioctl(fd, FD_FORM, (caddr_t)&f) < 0)
+ err(EX_OSERR, "ioctl(FD_FORM)");
+}
+
+static int
+verify_track(int fd, int track, int tracksize)
+{
+ static char *buf;
+ static int bufsz;
+ int fdopts = -1, ofdopts, rv = 0;
+
+ if (ioctl(fd, FD_GOPTS, &fdopts) < 0)
+ warn("warning: ioctl(FD_GOPTS)");
+ else {
+ ofdopts = fdopts;
+ fdopts |= FDOPT_NORETRY;
+ (void)ioctl(fd, FD_SOPTS, &fdopts);
+ }
+
+ if (bufsz < tracksize)
+ buf = realloc(buf, bufsz = tracksize);
+ if (buf == 0)
+ errx(EX_UNAVAILABLE, "out of memory");
+ if (lseek (fd, (long) track * tracksize, 0) < 0)
+ rv = -1;
+ /* try twice reading it, without using the normal retrier */
+ else if (read (fd, buf, tracksize) != tracksize
+ && read (fd, buf, tracksize) != tracksize)
+ rv = -1;
+ if (fdopts != -1)
+ (void)ioctl(fd, FD_SOPTS, &ofdopts);
+ return (rv);
+}
+
+static void
+usage (void)
+{
+ errx(EX_USAGE,
+ "usage: fdformat [-F fill] [-f fmt] [-s fmtstr] [-nqvy] device");
+}
+
+static int
+yes (void)
+{
+ char reply[256], *p;
+
+ reply[sizeof(reply) - 1] = 0;
+ for (;;) {
+ fflush(stdout);
+ if (!fgets (reply, sizeof(reply) - 1, stdin))
+ return (0);
+ for (p=reply; *p==' ' || *p=='\t'; ++p)
+ continue;
+ if (*p=='y' || *p=='Y')
+ return (1);
+ if (*p=='n' || *p=='N' || *p=='\n' || *p=='\r')
+ return (0);
+ printf("Answer `yes' or `no': ");
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ enum fd_drivetype type;
+ struct fd_type fdt, newft, *fdtp;
+ struct stat sb;
+#define MAXPRINTERRS 10
+ struct fdc_status fdcs[MAXPRINTERRS];
+ int format, fill, quiet, verify, verify_only, confirm;
+ int fd, c, i, track, error, tracks_per_dot, bytes_per_track, errs;
+ int fdopts, flags;
+ char *fmtstring, *device;
+ const char *name, *descr;
+
+ format = quiet = verify_only = confirm = 0;
+ verify = 1;
+ fill = 0xf6;
+ fmtstring = 0;
+
+ while((c = getopt(argc, argv, "F:f:nqs:vy")) != -1)
+ switch(c) {
+ case 'F': /* fill byte */
+ if (getnum(optarg, &fill)) {
+ fprintf(stderr,
+ "Bad argument %s to -F option; must be numeric\n",
+ optarg);
+ usage();
+ }
+ break;
+
+ case 'f': /* format in kilobytes */
+ if (getnum(optarg, &format)) {
+ fprintf(stderr,
+ "Bad argument %s to -f option; must be numeric\n",
+ optarg);
+ usage();
+ }
+ break;
+
+ case 'n': /* don't verify */
+ verify = 0;
+ break;
+
+ case 'q': /* quiet */
+ quiet = 1;
+ break;
+
+ case 's': /* format string with detailed options */
+ fmtstring = optarg;
+ break;
+
+ case 'v': /* verify only */
+ verify = 1;
+ verify_only = 1;
+ break;
+
+ case 'y': /* confirm */
+ confirm = 1;
+ break;
+
+ default:
+ usage();
+ }
+
+ if(optind != argc - 1)
+ usage();
+
+ if (stat(argv[optind], &sb) == -1 && errno == ENOENT) {
+ /* try prepending _PATH_DEV */
+ device = malloc(strlen(argv[optind]) + sizeof(_PATH_DEV) + 1);
+ if (device == 0)
+ errx(EX_UNAVAILABLE, "out of memory");
+ strcpy(device, _PATH_DEV);
+ strcat(device, argv[optind]);
+ if (stat(device, &sb) == -1) {
+ free(device);
+ device = argv[optind]; /* let it fail below */
+ }
+ } else {
+ device = argv[optind];
+ }
+
+ if ((fd = open(device, O_RDWR | O_NONBLOCK)) < 0)
+ err(EX_OSERR, "open(%s)", device);
+
+ /*
+ * Device initialization.
+ *
+ * First, get the device type descriptor. This tells us about
+ * the media geometry data we need to format a medium. It also
+ * lets us know quickly whether the device name actually points
+ * to a floppy disk drive.
+ *
+ * Then, obtain any drive options. We're mainly interested to
+ * see whether we're currently working on a device with media
+ * density autoselection (FDOPT_AUTOSEL). Then, we add the
+ * device option to tell the kernel not to log media errors,
+ * since we can handle them ourselves. If the device does
+ * media density autoselection, we then need to set the device
+ * type appropriately, since by opening with O_NONBLOCK we
+ * told the driver to bypass media autoselection (otherwise we
+ * wouldn't stand a chance to format an unformatted or damaged
+ * medium). We do not attempt to set the media type on any
+ * other devices since this is a privileged operation. For the
+ * same reason, specifying -f and -s options is only possible
+ * for autoselecting devices.
+ *
+ * Finally, we are ready to turn off O_NONBLOCK, and start to
+ * actually format something.
+ */
+ if(ioctl(fd, FD_GTYPE, &fdt) < 0)
+ errx(EX_OSERR, "not a floppy disk: %s", device);
+ if (ioctl(fd, FD_GDTYPE, &type) == -1)
+ err(EX_OSERR, "ioctl(FD_GDTYPE)");
+ if (ioctl(fd, FD_GOPTS, &fdopts) == -1)
+ err(EX_OSERR, "ioctl(FD_GOPTS)");
+ fdopts |= FDOPT_NOERRLOG;
+ if (ioctl(fd, FD_SOPTS, &fdopts) == -1)
+ err(EX_OSERR, "ioctl(FD_SOPTS, FDOPT_NOERRLOG)");
+ if (format) {
+ getname(type, &name, &descr);
+ fdtp = get_fmt(format, type);
+ if (fdtp == 0)
+ errx(EX_USAGE,
+ "unknown format %d KB for drive type %s",
+ format, name);
+ fdt = *fdtp;
+ }
+ if (fmtstring) {
+ parse_fmt(fmtstring, type, fdt, &newft);
+ fdt = newft;
+ }
+ if (ioctl(fd, FD_STYPE, &fdt) < 0)
+ err(EX_OSERR, "ioctl(FD_STYPE)");
+ if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
+ err(EX_OSERR, "fcntl(F_GETFL)");
+ flags &= ~O_NONBLOCK;
+ if (fcntl(fd, F_SETFL, flags) == -1)
+ err(EX_OSERR, "fcntl(F_SETFL)");
+
+ bytes_per_track = fdt.sectrac * (128 << fdt.secsize);
+
+ /* XXX 20/40 = 0.5 */
+ tracks_per_dot = (fdt.tracks * fdt.heads + 20) / 40;
+
+ if (verify_only) {
+ if(!quiet)
+ printf("Verify %dK floppy `%s'.\n",
+ fdt.tracks * fdt.heads * bytes_per_track / 1024,
+ device);
+ }
+ else if(!quiet && !confirm) {
+ printf("Format %dK floppy `%s'? (y/n): ",
+ fdt.tracks * fdt.heads * bytes_per_track / 1024,
+ device);
+ if(!yes()) {
+ printf("Not confirmed.\n");
+ return (EX_UNAVAILABLE);
+ }
+ }
+
+ /*
+ * Formatting.
+ */
+ if(!quiet) {
+ printf("Processing ");
+ for (i = 0; i < (fdt.tracks * fdt.heads) / tracks_per_dot; i++)
+ putchar('-');
+ printf("\rProcessing ");
+ fflush(stdout);
+ }
+
+ error = errs = 0;
+
+ for (track = 0; track < fdt.tracks * fdt.heads; track++) {
+ if (!verify_only) {
+ format_track(fd, track / fdt.heads, fdt.sectrac,
+ track % fdt.heads, fdt.trans, fdt.f_gap,
+ fdt.secsize, fill, fdt.f_inter,
+ track % fdt.heads? fdt.offset_side2: 0);
+ if(!quiet && !((track + 1) % tracks_per_dot)) {
+ putchar('F');
+ fflush(stdout);
+ }
+ }
+ if (verify) {
+ if (verify_track(fd, track, bytes_per_track) < 0) {
+ error = 1;
+ if (errs < MAXPRINTERRS && errno == EIO) {
+ if (ioctl(fd, FD_GSTAT, fdcs + errs) ==
+ -1)
+ errx(EX_IOERR,
+ "floppy IO error, but no FDC status");
+ errs++;
+ }
+ }
+ if(!quiet && !((track + 1) % tracks_per_dot)) {
+ if (!verify_only)
+ putchar('\b');
+ if (error) {
+ putchar('E');
+ error = 0;
+ }
+ else
+ putchar('V');
+ fflush(stdout);
+ }
+ }
+ }
+ if(!quiet)
+ printf(" done.\n");
+
+ if (!quiet && errs) {
+ fflush(stdout);
+ fprintf(stderr, "Errors encountered:\nCyl Head Sect Error\n");
+ for (i = 0; i < errs && i < MAXPRINTERRS; i++) {
+ fprintf(stderr, " %2d %2d %2d ",
+ fdcs[i].status[3], fdcs[i].status[4],
+ fdcs[i].status[5]);
+ printstatus(fdcs + i, 1);
+ putc('\n', stderr);
+ }
+ if (errs >= MAXPRINTERRS)
+ fprintf(stderr, "(Further errors not printed.)\n");
+ }
+
+ return errs != 0;
+}
diff --git a/usr.sbin/fdread/Makefile b/usr.sbin/fdread/Makefile
new file mode 100644
index 0000000..2868a3c
--- /dev/null
+++ b/usr.sbin/fdread/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+PROG= fdread
+SRCS= fdread.c fdutil.c
+
+WARNS?= 6
+
+.if ${MACHINE} == "pc98"
+CFLAGS+= -DPC98
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/fdread/fdread.1 b/usr.sbin/fdread/fdread.1
new file mode 100644
index 0000000..20a79fc
--- /dev/null
+++ b/usr.sbin/fdread/fdread.1
@@ -0,0 +1,233 @@
+.\"
+.\" Copyright (c) 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.
+.\"
+.\" $FreeBSD$
+.\"
+.\"
+.Dd May 14, 2001
+.Os
+.Dt FDREAD 1
+.Sh NAME
+.Nm fdread
+.Nd read floppy disks
+.Sh SYNOPSIS
+.Nm
+.Op Fl qr
+.Op Fl d Ar device
+.Op Fl f Ar fillbyte
+.Op Fl o Ar file
+.Nm
+.Op Fl d Ar device
+.Fl I Ar numsects
+.Op Fl t Ar trackno
+.Sh DESCRIPTION
+The
+.Nm
+utility reads floppy disks.
+Effective read blocking based on the track
+size is performed, and floppy-specific error recovery of otherwise
+bad blocks can be enabled.
+.Pp
+The
+.Nm
+utility
+will always read an entire floppy medium, and write its contents to
+the respective output file.
+Unlike other tools like
+.Xr dd 1 ,
+.Nm
+automatically uses a read block size that is more efficient than
+reading single blocks (usually one track of data at a time), but
+falls back to reading single floppy sectors in case of an input/output
+error occurred, in order to obtain as much valid data as possible.
+While
+.Nm
+is working, kernel error reporting for floppy errors is turned off, so
+the console and/or syslog are not flooded with kernel error messages.
+.Pp
+The
+.Nm
+utility accepts the following options:
+.Bl -tag -width indent
+.It Fl q
+Turn on quiet mode.
+By default, the medium parameters of the device
+are being written to standard error output, progress will be indicated
+by the approximate number of kilobytes read so far, and errors will be
+printed out in detail, including the information about the location of
+recovered data in the output.
+In quiet mode, none of these messages
+will be generated.
+.It Fl r
+Enable error recovery.
+By default,
+.Nm
+stops after the first unrecovered read error, much like
+.Xr dd 1
+does.
+In recovery mode, however, one of two recovery actions will be
+taken:
+.Bl -bullet
+.It
+If the error was a CRC error in the data field, the
+kernel is told to ignore the error, and data are transferred to the
+output file anyway.
+.Bf -emphasis
+Note that this will cause the erroneous data
+to be included in the output file!
+.Ef
+Still, this is the best recovery action that can be taken at all.
+.It
+All other errors are really fatal (usually, the FDC did not find the
+sector ID fields), thus a dummy block with fill
+bytes will be included in the output file.
+.El
+.Pp
+Unless operating in quiet mode, the action taken and the location of
+the error in the output file will be displayed.
+.It Fl d Ar device
+Specify the input floppy device, defaulting to
+.Pa /dev/fd0 .
+The parameter
+.Ar device
+must be a valid floppy disk device.
+.It Fl f Ar fillbyte
+Value of the fill byte used for dummy blocks in the output file in
+recovery mode.
+Defaults to
+.Ql 0xf0 .
+(Mnemonic:
+.Dq foo . )
+The value can be specified using the usual C language notation of
+the number base.
+.It Fl o Ar file
+Specify the output file to be
+.Ar file .
+By default, the data will be written to standard output.
+.It Fl I Ar numsects
+Read
+.Ar numsects
+sector ID fields, and write out their contents to standard output.
+Each sector ID field contains recorded values for the cylinder number
+.Pq Ql C ,
+the head number
+.Pq Ql H ,
+the record number (sector number starting with 1)
+.Pq Ql R ,
+and the
+.Em sector shift value
+(0 = 128 bytes, 1 = 256 bytes, 2 = 512 bytes, 3 = 1024 bytes)
+.Pq Ql N .
+The
+.Fl I
+option is mutually exclusive with all other options except
+.Fl d Ar device
+and
+.Fl t Ar trackno .
+.It Fl t Ar trackno
+Specify the track number (cylinder number * number of heads + head
+number) to read the sector ID fields from; only allowed together with
+the
+.Fl I Ar numsects
+option.
+.El
+.Sh FILES
+.Bl -tag -width /dev/fd0
+.It Pa /dev/fd0
+Default device to read from.
+.El
+.Sh EXIT STATUS
+The
+.Nm
+utility sets the exit value according to
+.Xr sysexits 3 .
+In recovery mode, the exit value will be set to
+.Dv EX_IOERR
+if any error occurred during processing (even in quiet mode).
+.Sh DIAGNOSTICS
+Unless running in quiet mode, upon encountering an error, the status
+of the floppy disc controller (FDC) will be printed out, both in
+hexadecimal form, followed by a textual description that translates
+those values into a human-readable form for the most common error
+cases that can happen in a PC environment.
+.Pp
+The FDC error status includes the three FDC status registers
+.Ql ST0 ,
+.Ql ST1 ,
+and
+.Ql ST2 ,
+as well as the location of the error (physical cylinder, head, and sector
+number, plus the
+.Dq sector shift value ,
+respectively).
+See the manual for the NE765 or compatible for details
+about the status register contents.
+.Pp
+The FDC's status is then examined to determine whether the error is
+deemed to be recoverable.
+If error recovery was requested, the
+location of the bad block in the output file is indicated by its
+(hexadecimal) bounds.
+Also, a summary line indicating the total number
+of transfer errors will be printed before exiting.
+.Sh SEE ALSO
+.Xr dd 1 ,
+.Xr fdwrite 1 ,
+.Xr sysexits 3 ,
+.Xr fdc 4 ,
+.Xr fdcontrol 8
+.Sh HISTORY
+The
+.Nm
+utility was written mainly to provide a means of recovering at least some of
+the data on bad media, and to obviate the need to invoke
+.Xr dd 1
+with too many hard to memorize options that might be useful to handle
+a floppy.
+.Pp
+The command appeared in
+.Fx 5.0 .
+.Sh AUTHORS
+Program and man page by
+.An J\(:org Wunsch .
+.Sh BUGS
+Concurrent traffic on the second floppy drive located at the same FDC
+will make error recovery attempts pointless, since the FDC status
+obtained after a read error occurred cannot be guaranteed to actually
+belong to the erroneous transfer.
+Thus using option
+.Fl r
+is only reliable if
+.Ar device
+is the only active drive on that controller.
+.Pp
+No attempt beyond the floppy error retry mechanism of
+.Xr fdc 4
+is made in order to see whether bad sectors could still be read
+without errors by trying multiple times.
+.Pp
+Bits that are (no longer) available on the floppy medium cannot be
+guessed by
+.Nm .
diff --git a/usr.sbin/fdread/fdread.c b/usr.sbin/fdread/fdread.c
new file mode 100644
index 0000000..2539b3a
--- /dev/null
+++ b/usr.sbin/fdread/fdread.c
@@ -0,0 +1,337 @@
+/*
+ * Copyright (c) 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/fdcio.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include <dev/ic/nec765.h>
+
+#include "fdutil.h"
+
+int quiet, recover;
+unsigned char fillbyte = 0xf0; /* "foo" */
+
+int doread(int fd, FILE *of, const char *_devname);
+int doreadid(int fd, unsigned int numids, unsigned int trackno);
+void usage(void);
+
+void
+usage(void)
+{
+
+ errx(EX_USAGE,
+ "usage: fdread [-qr] [-d device] [-f fillbyte]\n"
+ " fdread [-d device] -I numids [-t trackno]");
+}
+
+
+int
+main(int argc, char **argv)
+{
+ int c, errs = 0;
+ unsigned int numids = 0, trackno = 0;
+ const char *fname = 0, *_devname = "/dev/fd0";
+ char *cp;
+ FILE *of = stdout;
+ int fd;
+ unsigned long ul;
+
+ while ((c = getopt(argc, argv, "d:f:I:o:qrt:")) != -1)
+ switch (c) {
+ case 'd':
+ _devname = optarg;
+ break;
+
+ case 'f':
+ ul = strtoul(optarg, &cp, 0);
+ if (*cp != '\0') {
+ fprintf(stderr,
+ "Bad argument %s to -f option; must be numeric\n",
+ optarg);
+ usage();
+ }
+ if (ul > 0xff)
+ warnx(
+ "Warning: fillbyte %#lx too large, truncating\n",
+ ul);
+ fillbyte = ul & 0xff;
+ break;
+
+ case 'I':
+ ul = strtoul(optarg, &cp, 0);
+ if (*cp != '\0') {
+ fprintf(stderr,
+ "Bad argument %s to -I option; must be numeric\n",
+ optarg);
+ usage();
+ }
+ numids = ul;
+ break;
+
+ case 'o':
+ fname = optarg;
+ break;
+
+ case 'q':
+ quiet++;
+ break;
+
+ case 'r':
+ recover++;
+ break;
+
+ case 't':
+ ul = strtoul(optarg, &cp, 0);
+ if (*cp != '\0') {
+ fprintf(stderr,
+ "Bad argument %s to -t option; must be numeric\n",
+ optarg);
+ usage();
+ }
+ trackno = ul;
+ break;
+
+ default:
+ errs++;
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 0 || errs)
+ usage();
+ /* check for mutually exclusive options */
+ if (numids) {
+ if (fname || quiet || recover)
+ usage();
+ } else {
+ if (trackno)
+ usage();
+ }
+
+ if (fname) {
+ if ((of = fopen(fname, "w")) == NULL)
+ err(EX_OSERR, "cannot create output file %s", fname);
+ }
+
+ if ((fd = open(_devname, O_RDWR)) == -1)
+ err(EX_OSERR, "cannot open device %s", _devname);
+
+ return (numids? doreadid(fd, numids, trackno): doread(fd, of, _devname));
+}
+
+int
+doread(int fd, FILE *of, const char *_devname)
+{
+ char *trackbuf;
+ int rv, fdopts, recoverable, nerrs = 0;
+ unsigned int nbytes, tracksize, mediasize, secsize, n;
+ struct fdc_status fdcs;
+ struct fd_type fdt;
+
+ if (ioctl(fd, FD_GTYPE, &fdt) == -1)
+ err(EX_OSERR, "ioctl(FD_GTYPE) failed -- not a floppy?");
+ fdopts = FDOPT_NOERRLOG;
+ if (ioctl(fd, FD_SOPTS, &fdopts) == -1)
+ err(EX_OSERR, "ioctl(FD_SOPTS, FDOPT_NOERRLOG)");
+
+ secsize = 128 << fdt.secsize;
+ tracksize = fdt.sectrac * secsize;
+ mediasize = tracksize * fdt.tracks * fdt.heads;
+ if ((trackbuf = malloc(tracksize)) == 0)
+ errx(EX_TEMPFAIL, "out of memory");
+
+ if (!quiet)
+ fprintf(stderr, "Reading %d * %d * %d * %d medium at %s\n",
+ fdt.tracks, fdt.heads, fdt.sectrac, secsize, _devname);
+
+ for (nbytes = 0; nbytes < mediasize;) {
+ if (lseek(fd, nbytes, SEEK_SET) != nbytes)
+ err(EX_OSERR, "cannot lseek()");
+ rv = read(fd, trackbuf, tracksize);
+ if (rv == 0) {
+ /* EOF? */
+ warnx("premature EOF after %u bytes", nbytes);
+ return (EX_OK);
+ }
+ if ((unsigned)rv == tracksize) {
+ nbytes += rv;
+ if (!quiet)
+ fprintf(stderr, "%5d KB\r", nbytes / 1024);
+ fwrite(trackbuf, sizeof(unsigned char), rv, of);
+ fflush(of);
+ continue;
+ }
+ if (rv == -1) {
+ /* fall back reading one sector at a time */
+ for (n = 0; n < tracksize; n += secsize) {
+ if (lseek(fd, nbytes, SEEK_SET) != nbytes)
+ err(EX_OSERR, "cannot lseek()");
+ rv = read(fd, trackbuf, secsize);
+ if ((unsigned) rv == secsize) {
+ nbytes += rv;
+ if (!quiet)
+ fprintf(stderr, "%5d KB\r",
+ nbytes / 1024);
+ fwrite(trackbuf, sizeof(unsigned char),
+ rv, of);
+ fflush(of);
+ continue;
+ }
+ if (rv == -1) {
+ if (errno != EIO) {
+ if (!quiet)
+ putc('\n', stderr);
+ perror("non-IO error");
+ return (EX_OSERR);
+ }
+ if (ioctl(fd, FD_GSTAT, &fdcs) == -1)
+ errx(EX_IOERR,
+ "floppy IO error, but no FDC status");
+ nerrs++;
+ recoverable = fdcs.status[2] &
+ NE7_ST2_DD;
+ if (!quiet) {
+ printstatus(&fdcs, 0);
+ fputs(" (", stderr);
+ if (!recoverable)
+ fputs("not ", stderr);
+ fputs("recoverable)", stderr);
+ }
+ if (!recover) {
+ if (!quiet)
+ putc('\n', stderr);
+ return (EX_IOERR);
+ }
+ memset(trackbuf, fillbyte, secsize);
+ if (recoverable) {
+ fdopts |= FDOPT_NOERROR;
+ if (ioctl(fd, FD_SOPTS,
+ &fdopts) == -1)
+ err(EX_OSERR,
+ "ioctl(fd, FD_SOPTS, FDOPT_NOERROR)");
+ rv = read(fd, trackbuf,
+ secsize);
+ if ((unsigned)rv != secsize)
+ err(EX_IOERR,
+ "read() with FDOPT_NOERROR still fails");
+ fdopts &= ~FDOPT_NOERROR;
+ (void)ioctl(fd, FD_SOPTS,
+ &fdopts);
+ }
+ if (!quiet) {
+ if (recoverable)
+ fprintf(stderr,
+ ": recovered");
+ else
+ fprintf(stderr,
+ ": dummy");
+ fprintf(stderr,
+ " data @ %#x ... %#x\n",
+ nbytes,
+ nbytes + secsize - 1);
+ }
+ nbytes += secsize;
+ fwrite(trackbuf, sizeof(unsigned char),
+ secsize, of);
+ fflush(of);
+ continue;
+ }
+ errx(EX_OSERR, "unexpected read() result: %d",
+ rv);
+ }
+ }
+ if ((unsigned)rv < tracksize) {
+ /* should not happen */
+ nbytes += rv;
+ if (!quiet)
+ fprintf(stderr, "\nshort after %5d KB\r",
+ nbytes / 1024);
+ fwrite(trackbuf, sizeof(unsigned char), rv, of);
+ fflush(of);
+ continue;
+ }
+ }
+ if (!quiet) {
+ putc('\n', stderr);
+ if (nerrs)
+ fprintf(stderr, "%d error%s\n",
+ nerrs, nerrs > 1? "s": "");
+ }
+
+ return (nerrs? EX_IOERR: EX_OK);
+}
+
+int
+doreadid(int fd, unsigned int numids, unsigned int trackno)
+{
+ int rv = 0, fdopts;
+ unsigned int i;
+ struct fdc_readid info;
+ struct fdc_status fdcs;
+ struct fd_type fdt;
+
+ if (ioctl(fd, FD_GTYPE, &fdt) == -1)
+ err(EX_OSERR, "ioctl(FD_GTYPE) failed -- not a floppy?");
+
+ fdopts = FDOPT_NOERRLOG;
+ if (ioctl(fd, FD_SOPTS, &fdopts) == -1)
+ err(EX_OSERR, "ioctl(FD_SOPTS, FDOPT_NOERRLOG)");
+
+ for (i = 0; i < numids; i++) {
+ info.cyl = trackno / fdt.heads;
+ info.head = fdt.heads > 1? trackno % fdt.heads: 0;
+ if (ioctl(fd, FD_READID, &info) == 0) {
+ printf("C = %d, H = %d, R = %d, N = %d\n",
+ info.cyl, info.head, info.sec, info.secshift);
+ } else {
+ if (errno != EIO) {
+ perror("non-IO error");
+ return (EX_OSERR);
+ }
+ if (ioctl(fd, FD_GSTAT, &fdcs) == -1)
+ errx(EX_IOERR,
+ "floppy IO error, but no FDC status");
+ printstatus(&fdcs, 0);
+ putc('\n', stderr);
+ rv = EX_IOERR;
+ }
+ }
+
+ return (rv);
+}
diff --git a/usr.sbin/fdread/fdutil.c b/usr.sbin/fdread/fdutil.c
new file mode 100644
index 0000000..c1f4d8f
--- /dev/null
+++ b/usr.sbin/fdread/fdutil.c
@@ -0,0 +1,523 @@
+/*
+ * Copyright (c) 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <dev/ic/nec765.h>
+
+#include <sys/fdcio.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#include "fdutil.h"
+
+/*
+ * Decode the FDC status pointed to by `fdcsp', and print a textual
+ * translation to stderr. If `terse' is false, the numerical FDC
+ * register status is printed, too.
+ */
+void
+printstatus(struct fdc_status *fdcsp, int terse)
+{
+ char msgbuf[100];
+
+ if (!terse)
+ fprintf(stderr,
+ "\nFDC status ST0=%#x ST1=%#x ST2=%#x C=%u H=%u R=%u N=%u:\n",
+ fdcsp->status[0] & 0xff,
+ fdcsp->status[1] & 0xff,
+ fdcsp->status[2] & 0xff,
+ fdcsp->status[3] & 0xff,
+ fdcsp->status[4] & 0xff,
+ fdcsp->status[5] & 0xff,
+ fdcsp->status[6] & 0xff);
+
+ if ((fdcsp->status[0] & NE7_ST0_IC_RC) == 0) {
+ sprintf(msgbuf, "timeout");
+ } else if ((fdcsp->status[0] & NE7_ST0_IC_RC) != NE7_ST0_IC_AT) {
+ sprintf(msgbuf, "unexcpted interrupt code %#x",
+ fdcsp->status[0] & NE7_ST0_IC_RC);
+ } else {
+ strcpy(msgbuf, "unexpected error code in ST1/ST2");
+
+ if (fdcsp->status[1] & NE7_ST1_EN)
+ strcpy(msgbuf, "end of cylinder (wrong format)");
+ else if (fdcsp->status[1] & NE7_ST1_DE) {
+ if (fdcsp->status[2] & NE7_ST2_DD)
+ strcpy(msgbuf, "CRC error in data field");
+ else
+ strcpy(msgbuf, "CRC error in ID field");
+ } else if (fdcsp->status[1] & NE7_ST1_MA) {
+ if (fdcsp->status[2] & NE7_ST2_MD)
+ strcpy(msgbuf, "no address mark in data field");
+ else
+ strcpy(msgbuf, "no address mark in ID field");
+ } else if (fdcsp->status[2] & NE7_ST2_WC)
+ strcpy(msgbuf, "wrong cylinder (format mismatch)");
+ else if (fdcsp->status[1] & NE7_ST1_ND)
+ strcpy(msgbuf, "no data (sector not found)");
+ }
+ fputs(msgbuf, stderr);
+}
+
+static struct fd_type fd_types_auto[1] =
+ { { 0,0,0,0,0,0,0,0,0,0,0,FL_AUTO } };
+
+
+static struct fd_type fd_types_288m[] = {
+#if 0
+ { FDF_3_2880 },
+#endif
+ { FDF_3_1722 },
+ { FDF_3_1476 },
+ { FDF_3_1440 },
+ { FDF_3_1200 },
+ { FDF_3_820 },
+ { FDF_3_800 },
+ { FDF_3_720 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0 }
+};
+
+static struct fd_type fd_types_144m[] = {
+#ifdef PC98
+#if 0
+ { FDF_3_1722 },
+ { FDF_3_1476 },
+#endif
+ { FDF_3_1440 },
+ { FDF_3_1200 },
+#if 0
+ { FDF_3_820 },
+ { FDF_3_800 },
+#endif
+ { FDF_3_720 },
+ { FDF_3_360 },
+ { FDF_3_640 },
+ { FDF_3_1230 },
+#if 0
+ { FDF_3_1280 },
+ { FDF_3_1480 },
+ { FDF_3_1640 },
+#endif
+ { 0,0,0,0,0,0,0,0,0,0,0,0 }
+#else
+ { FDF_3_1722 },
+ { FDF_3_1476 },
+ { FDF_3_1440 },
+ { FDF_3_1200 },
+ { FDF_3_820 },
+ { FDF_3_800 },
+ { FDF_3_720 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0 }
+#endif
+};
+
+static struct fd_type fd_types_12m[] = {
+#ifdef PC98
+ { FDF_5_1200 },
+#if 0
+ { FDF_5_820 },
+ { FDF_5_800 },
+#endif
+ { FDF_5_720 },
+ { FDF_5_360 },
+ { FDF_5_640 },
+ { FDF_5_1230 },
+#if 0
+ { FDF_5_1280 },
+#endif
+ { 0,0,0,0,0,0,0,0,0,0,0,0 }
+#else
+ { FDF_5_1200 },
+ { FDF_5_1230 },
+ { FDF_5_1480 },
+ { FDF_5_1440 },
+ { FDF_5_820 },
+ { FDF_5_800 },
+ { FDF_5_720 },
+ { FDF_5_360 | FL_2STEP },
+ { FDF_5_640 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0 }
+#endif
+};
+
+static struct fd_type fd_types_720k[] =
+{
+ { FDF_3_720 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0 }
+};
+
+static struct fd_type fd_types_360k[] =
+{
+ { FDF_5_360 },
+ { 0,0,0,0,0,0,0,0,0,0,0,0 }
+};
+
+
+/*
+ * Parse a format string, and fill in the parameter pointed to by `out'.
+ *
+ * sectrac,secsize,datalen,gap,ncyls,speed,heads,f_gap,f_inter,offs2,flags[...]
+ *
+ * sectrac = sectors per track
+ * secsize = sector size in bytes
+ * datalen = length of sector if secsize == 128
+ * gap = gap length when reading
+ * ncyls = number of cylinders
+ * speed = transfer speed 250/300/500/1000 KB/s
+ * heads = number of heads
+ * f_gap = gap length when formatting
+ * f_inter = sector interleave when formatting
+ * offs2 = offset of sectors on side 2
+ * flags = +/-mfm | +/-2step | +/-perpend
+ * mfm - use MFM recording
+ * 2step - use 2 steps between cylinders
+ * perpend - user perpendicular (vertical) recording
+ *
+ * Any omitted value will be passed on from parameter `in'.
+ */
+void
+parse_fmt(const char *s, enum fd_drivetype type,
+ struct fd_type in, struct fd_type *out)
+{
+ int i, j;
+ const char *cp;
+ char *s1;
+
+ *out = in;
+
+ for (i = 0;; i++) {
+ if (s == 0)
+ break;
+
+ if ((cp = strchr(s, ',')) == 0) {
+ s1 = strdup(s);
+ if (s1 == NULL)
+ abort();
+ s = 0;
+ } else {
+ s1 = malloc(cp - s + 1);
+ if (s1 == NULL)
+ abort();
+ memcpy(s1, s, cp - s);
+ s1[cp - s] = 0;
+
+ s = cp + 1;
+ }
+ if (strlen(s1) == 0) {
+ free(s1);
+ continue;
+ }
+
+ switch (i) {
+ case 0: /* sectrac */
+ if (getnum(s1, &out->sectrac))
+ errx(EX_USAGE,
+ "bad numeric value for sectrac: %s", s1);
+ break;
+
+ case 1: /* secsize */
+ if (getnum(s1, &j))
+ errx(EX_USAGE,
+ "bad numeric value for secsize: %s", s1);
+ if (j == 128) out->secsize = 0;
+ else if (j == 256) out->secsize = 1;
+ else if (j == 512) out->secsize = 2;
+ else if (j == 1024) out->secsize = 3;
+ else
+ errx(EX_USAGE, "bad sector size %d", j);
+ break;
+
+ case 2: /* datalen */
+ if (getnum(s1, &j))
+ errx(EX_USAGE,
+ "bad numeric value for datalen: %s", s1);
+ if (j >= 256)
+ errx(EX_USAGE, "bad datalen %d", j);
+ out->datalen = j;
+ break;
+
+ case 3: /* gap */
+ if (getnum(s1, &out->gap))
+ errx(EX_USAGE,
+ "bad numeric value for gap: %s", s1);
+ break;
+
+ case 4: /* ncyls */
+ if (getnum(s1, &j))
+ errx(EX_USAGE,
+ "bad numeric value for ncyls: %s", s1);
+ if (j > 85)
+ errx(EX_USAGE, "bad # of cylinders %d", j);
+ out->tracks = j;
+ break;
+
+ case 5: /* speed */
+ if (getnum(s1, &j))
+ errx(EX_USAGE,
+ "bad numeric value for speed: %s", s1);
+ switch (type) {
+ default:
+ abort(); /* paranoia */
+
+ case FDT_360K:
+ case FDT_720K:
+ if (j == 250)
+ out->trans = FDC_250KBPS;
+ else
+ errx(EX_USAGE, "bad speed %d", j);
+ break;
+
+ case FDT_12M:
+ if (j == 300)
+ out->trans = FDC_300KBPS;
+ else if (j == 250)
+ out->trans = FDC_250KBPS;
+ else if (j == 500)
+ out->trans = FDC_500KBPS;
+ else
+ errx(EX_USAGE, "bad speed %d", j);
+ break;
+
+ case FDT_288M:
+ if (j == 1000)
+ out->trans = FDC_1MBPS;
+ /* FALLTHROUGH */
+ case FDT_144M:
+ if (j == 250)
+ out->trans = FDC_250KBPS;
+ else if (j == 500)
+ out->trans = FDC_500KBPS;
+ else
+ errx(EX_USAGE, "bad speed %d", j);
+ break;
+ }
+ break;
+
+ case 6: /* heads */
+ if (getnum(s1, &j))
+ errx(EX_USAGE,
+ "bad numeric value for heads: %s", s1);
+ if (j == 1 || j == 2)
+ out->heads = j;
+ else
+ errx(EX_USAGE, "bad # of heads %d", j);
+ break;
+
+ case 7: /* f_gap */
+ if (getnum(s1, &out->f_gap))
+ errx(EX_USAGE,
+ "bad numeric value for f_gap: %s", s1);
+ break;
+
+ case 8: /* f_inter */
+ if (getnum(s1, &out->f_inter))
+ errx(EX_USAGE,
+ "bad numeric value for f_inter: %s", s1);
+ break;
+
+ case 9: /* offs2 */
+ if (getnum(s1, &out->offset_side2))
+ errx(EX_USAGE,
+ "bad numeric value for offs2: %s", s1);
+ break;
+
+ default:
+ if (strcmp(s1, "+mfm") == 0)
+ out->flags |= FL_MFM;
+ else if (strcmp(s1, "-mfm") == 0)
+ out->flags &= ~FL_MFM;
+ else if (strcmp(s1, "+auto") == 0)
+ out->flags |= FL_AUTO;
+ else if (strcmp(s1, "-auto") == 0)
+ out->flags &= ~FL_AUTO;
+ else if (strcmp(s1, "+2step") == 0)
+ out->flags |= FL_2STEP;
+ else if (strcmp(s1, "-2step") == 0)
+ out->flags &= ~FL_2STEP;
+ else if (strcmp(s1, "+perpnd") == 0)
+ out->flags |= FL_PERPND;
+ else if (strcmp(s1, "-perpnd") == 0)
+ out->flags &= ~FL_PERPND;
+ else
+ errx(EX_USAGE, "bad flag: %s", s1);
+ break;
+ }
+ free(s1);
+ }
+
+ out->size = out->tracks * out->heads * out->sectrac;
+}
+
+/*
+ * Print a textual translation of the drive (density) type described
+ * by `in' to stdout. The string uses the same form that is parseable
+ * by parse_fmt().
+ */
+void
+print_fmt(struct fd_type in)
+{
+ int secsize, speed;
+
+ secsize = 128 << in.secsize;
+ switch (in.trans) {
+ case FDC_250KBPS: speed = 250; break;
+ case FDC_300KBPS: speed = 300; break;
+ case FDC_500KBPS: speed = 500; break;
+ case FDC_1MBPS: speed = 1000; break;
+ default: speed = 1; break;
+ }
+
+ printf("%d,%d,%#x,%#x,%d,%d,%d,%#x,%d,%d",
+ in.sectrac, secsize, in.datalen, in.gap, in.tracks,
+ speed, in.heads, in.f_gap, in.f_inter, in.offset_side2);
+ if (in.flags & FL_MFM)
+ printf(",+mfm");
+ if (in.flags & FL_2STEP)
+ printf(",+2step");
+ if (in.flags & FL_PERPND)
+ printf(",+perpnd");
+ if (in.flags & FL_AUTO)
+ printf(",+auto");
+ putc('\n', stdout);
+}
+
+/*
+ * Based on `size' (in kilobytes), walk through the table of known
+ * densities for drive type `type' and see if we can find one. If
+ * found, return it (as a pointer to static storage), otherwise return
+ * NULL.
+ */
+struct fd_type *
+get_fmt(int size, enum fd_drivetype type)
+{
+ int i, n;
+ struct fd_type *fdtp;
+
+ switch (type) {
+ default:
+ return (0);
+
+ case FDT_360K:
+ fdtp = fd_types_360k;
+ n = sizeof fd_types_360k / sizeof(struct fd_type);
+ break;
+
+ case FDT_720K:
+ fdtp = fd_types_720k;
+ n = sizeof fd_types_720k / sizeof(struct fd_type);
+ break;
+
+ case FDT_12M:
+ fdtp = fd_types_12m;
+ n = sizeof fd_types_12m / sizeof(struct fd_type);
+ break;
+
+ case FDT_144M:
+ fdtp = fd_types_144m;
+ n = sizeof fd_types_144m / sizeof(struct fd_type);
+ break;
+
+ case FDT_288M:
+ fdtp = fd_types_288m;
+ n = sizeof fd_types_288m / sizeof(struct fd_type);
+ break;
+ }
+
+ if (size == -1)
+ return fd_types_auto;
+
+ for (i = 0; i < n; i++, fdtp++) {
+ fdtp->size = fdtp->sectrac * fdtp->heads * fdtp->tracks;
+ if (((128 << fdtp->secsize) * fdtp->size / 1024) == size)
+ return (fdtp);
+ }
+ return (0);
+}
+
+/*
+ * Parse a number from `s'. If the string cannot be converted into a
+ * number completely, return -1, otherwise 0. The result is returned
+ * in `*res'.
+ */
+int
+getnum(const char *s, int *res)
+{
+ unsigned long ul;
+ char *cp;
+
+ ul = strtoul(s, &cp, 0);
+ if (*cp != '\0')
+ return (-1);
+
+ *res = (int)ul;
+ return (0);
+}
+
+/*
+ * Return a short name and a verbose description for the drive
+ * described by `t'.
+ */
+void
+getname(enum fd_drivetype t, const char **name, const char **descr)
+{
+
+ switch (t) {
+ default:
+ *name = "unknown";
+ *descr = "unknown drive type";
+ break;
+
+ case FDT_360K:
+ *name = "360K";
+ *descr = "5.25\" double-density";
+ break;
+
+ case FDT_12M:
+ *name = "1.2M";
+ *descr = "5.25\" high-density";
+ break;
+
+ case FDT_720K:
+ *name = "720K";
+ *descr = "3.5\" double-density";
+ break;
+
+ case FDT_144M:
+ *name = "1.44M";
+ *descr = "3.5\" high-density";
+ break;
+
+ case FDT_288M:
+ *name = "2.88M";
+ *descr = "3.5\" extra-density";
+ break;
+ }
+}
diff --git a/usr.sbin/fdread/fdutil.h b/usr.sbin/fdread/fdutil.h
new file mode 100644
index 0000000..a093228
--- /dev/null
+++ b/usr.sbin/fdread/fdutil.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 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.
+ *
+ * $FreeBSD$
+ */
+
+
+void printstatus(struct fdc_status *fdcsp, int terse);
+void parse_fmt(const char *, enum fd_drivetype,
+ struct fd_type, struct fd_type *);
+struct fd_type *get_fmt(int, enum fd_drivetype);
+void print_fmt(struct fd_type);
+int getnum(const char *, int *);
+void getname(enum fd_drivetype, const char **, const char **);
+
diff --git a/usr.sbin/fdwrite/Makefile b/usr.sbin/fdwrite/Makefile
new file mode 100644
index 0000000..ba6f047
--- /dev/null
+++ b/usr.sbin/fdwrite/Makefile
@@ -0,0 +1,13 @@
+# ----------------------------------------------------------------------------
+# "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$
+
+PROG= fdwrite
+WARNS?= 6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/fdwrite/fdwrite.1 b/usr.sbin/fdwrite/fdwrite.1
new file mode 100644
index 0000000..28df514
--- /dev/null
+++ b/usr.sbin/fdwrite/fdwrite.1
@@ -0,0 +1,129 @@
+.\"
+.\" ----------------------------------------------------------------------------
+.\" "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 September 16, 1993
+.Os
+.Dt FDWRITE 1
+.Sh NAME
+.Nm fdwrite
+.Nd format and write floppy disks
+.Sh SYNOPSIS
+.Nm
+.Op Fl v
+.Op Fl y
+.Op Fl f Ar inputfile
+.Op Fl d Ar device
+.Sh DESCRIPTION
+The
+.Nm
+utility formats and writes one and more floppy disks.
+Any floppy disk device capable of formatting can be used.
+.Pp
+The
+.Nm
+utility will ask the user
+(on
+.Pa /dev/tty )
+to insert a new floppy and press return.
+The device will then be opened, and queried for its parameters,
+then each track will be formatted, written with data from the
+.Ar inputfile ,
+read back and compared.
+When the floppy disk is filled, the process is repeated, with the next disk.
+This continues until the program is interrupted or EOF is encountered on the
+.Ar inputfile .
+.Pp
+The options are as follows:
+.Bl -tag -width 10n -offset indent
+.It Fl v
+Toggle verbosity on stdout.
+Default is ``on''.
+After
+.Ar device
+is opened first time the format will be printed.
+During operation progress will be reported with the number of tracks
+remaining on the current floppy disk, and the letters I, Z, F, W,
+R and C, which indicates completion of Input, Zero-fill, Format
+Write, Read and Compare of current track respectively.
+.It Fl y
+Do not ask for presence of a floppy disk in the drive.
+This non-interactive flag
+is useful for shell scripts.
+.It Fl f Ar inputfile
+Input file to read.
+If none is given, stdin is assumed.
+.It Fl d Ar device
+The name of the floppy device to write to.
+Default is
+.Pa /dev/fd0 .
+.El
+.Pp
+The
+.Nm
+utility actually closes the
+.Ar device
+while it waits for the user to press return,
+it is thus quite possible to use the drive for other purposes at this
+time and later resume writing with the next floppy.
+.Pp
+The parameters returned from
+.Ar device
+are used for formatting.
+If custom formatting is needed, please use
+.Xr fdformat 1
+instead.
+.Sh EXAMPLES
+The
+.Nm
+utility
+was planned as a tool to make life easier when writing a set of floppies,
+one such use could be to write a tar-archive:
+.Pp
+.Dl "tar cf - . | gzip -9 | fdwrite -d /dev/fd0.1720 -v
+.Pp
+The main difference from using
+.Xr tar 1 Ns 's
+multivolume facility is of course the formatting of the floppies, which
+here is done on the fly,
+thus reducing the amount of work for the floppy-jockey.
+.Sh SEE ALSO
+.Xr fdformat 1
+.Sh HISTORY
+The
+.Nm
+utility was written while waiting for ``make world'' to complete.
+Some of the code was taken from
+.Xr fdformat 1 .
+.Sh AUTHORS
+The program has been contributed by
+.An Poul-Henning Kamp Aq phk@FreeBSD.org .
+.Sh BUGS
+Diagnostics are less than complete at present.
+.Pp
+If a floppy is sick, and the
+.Ar inputfile
+is seekable, it should ask the user to frisbee the disk, insert
+another, and rewind to the right spot and continue.
+.Pp
+This concept could be extended to cover non-seekable input also
+by employing a temporary file.
+.Pp
+An option (defaulting to zero) should allow the user to ask for
+retries in case of failure.
+.Pp
+At present a suitable tool for reading back a multivolume set
+of floppies is missing.
+Programs like
+.Xr tar 1
+for instance, will do the job, if the data has not been compressed.
+One can always trust
+.Xr dd 1
+to help out in this situation of course.
diff --git a/usr.sbin/fdwrite/fdwrite.c b/usr.sbin/fdwrite/fdwrite.c
new file mode 100644
index 0000000..3c22091
--- /dev/null
+++ b/usr.sbin/fdwrite/fdwrite.c
@@ -0,0 +1,199 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "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 <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/fdcio.h>
+
+static int
+format_track(int fd, int cyl, int secs, int head, int rate,
+ int gaplen, int secsize, int fill, int interleave)
+{
+ struct fd_formb f;
+ register int i,j;
+ int il[100];
+
+ memset(il,0,sizeof il);
+ for(j = 0, i = 1; i <= secs; i++) {
+ while(il[(j%secs)+1]) j++;
+ il[(j%secs)+1] = i;
+ j += interleave;
+ }
+
+ f.format_version = FD_FORMAT_VERSION;
+ f.head = head;
+ f.cyl = cyl;
+ f.transfer_rate = rate;
+
+ f.fd_formb_secshift = secsize;
+ f.fd_formb_nsecs = secs;
+ f.fd_formb_gaplen = gaplen;
+ f.fd_formb_fillbyte = fill;
+ for(i = 0; i < secs; i++) {
+ f.fd_formb_cylno(i) = cyl;
+ f.fd_formb_headno(i) = head;
+ f.fd_formb_secno(i) = il[i+1];
+ f.fd_formb_secsize(i) = secsize;
+ }
+ return ioctl(fd, FD_FORM, (caddr_t)&f);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: fdwrite [-v] [-y] [-f inputfile] [-d device]\n");
+ exit(2);
+}
+
+int
+main(int argc, char **argv)
+{
+ int inputfd = -1, c, fdn = 0, i,j,fd;
+ int bpt, verbose=1, nbytes=0, track;
+ int interactive = 1, fdopts;
+ const char *device= "/dev/fd0";
+ char *trackbuf = 0,*vrfybuf = 0;
+ struct fd_type fdt;
+ FILE *tty;
+
+ setbuf(stdout,0);
+ while((c = getopt(argc, argv, "d:f:vy")) != -1)
+ switch(c) {
+ case 'd': /* Which drive */
+ device = optarg;
+ break;
+
+ case 'f': /* input file */
+ if (inputfd >= 0)
+ close(inputfd);
+ inputfd = open(optarg,O_RDONLY);
+ if (inputfd < 0)
+ err(1, "%s", optarg);
+ break;
+
+ case 'v': /* Toggle verbosity */
+ verbose = !verbose;
+ break;
+
+ case 'y': /* Don't confirm? */
+ interactive = 0;
+ break;
+
+ case '?': default:
+ usage();
+ }
+
+ if (inputfd < 0)
+ inputfd = 0;
+
+ if (!isatty(1))
+ interactive = 0;
+
+ if(optind < argc)
+ usage();
+
+ tty = fopen(_PATH_TTY,"r+");
+ if(!tty)
+ err(1, _PATH_TTY);
+ setbuf(tty,0);
+
+ for(j=1;j > 0;) {
+ fdn++;
+ if (interactive) {
+ fprintf(tty,
+ "Please insert floppy #%d in drive %s and press return >",
+ fdn,device);
+ while(1) {
+ i = getc(tty);
+ if(i == '\n') break;
+ }
+ }
+
+ if((fd = open(device, O_RDWR)) < 0)
+ err(1, "%s", device);
+
+ if(ioctl(fd, FD_GTYPE, &fdt) < 0)
+ errx(1, "not a floppy disk: %s", device);
+ fdopts = FDOPT_NOERRLOG;
+ if (ioctl(fd, FD_SOPTS, &fdopts) == -1)
+ err(1, "ioctl(FD_SOPTS, FDOPT_NOERRLOG)");
+
+ bpt = fdt.sectrac * (1<<fdt.secsize) * 128;
+ if(!trackbuf) {
+ trackbuf = malloc(bpt);
+ if(!trackbuf) errx(1, "malloc");
+ }
+ if(!vrfybuf) {
+ vrfybuf = malloc(bpt);
+ if(!vrfybuf) errx(1, "malloc");
+ }
+
+ if(fdn == 1) {
+ if(verbose) {
+ printf("Format: %d cylinders, %d heads, %d sectors, %d bytes = %dkb\n",
+ fdt.tracks,fdt.heads,fdt.sectrac,(1<<fdt.secsize) * 128,
+ fdt.tracks*bpt*fdt.heads/1024);
+
+ }
+ memset(trackbuf,0,bpt);
+ for(j=0;inputfd >= 0 && j<bpt;j+=i) {
+ if(!(i = read(inputfd,trackbuf+j,bpt-j))) {
+ close(inputfd);
+ inputfd = -1;
+ break;
+ }
+ nbytes += i;
+ }
+ }
+ for (track = 0; track < fdt.tracks * fdt.heads; track++) {
+ if(verbose) printf("\r%3d ",fdt.tracks * fdt.heads-track);
+ if(verbose) putc((j ? 'I':'Z'),stdout);
+ format_track(fd, track / fdt.heads, fdt.sectrac, track % fdt.heads,
+ fdt.trans, fdt.f_gap, fdt.secsize, 0xe6,
+ fdt.f_inter);
+ if(verbose) putc('F',stdout);
+
+ if (lseek (fd, (long) track*bpt, 0) < 0) err(1, "lseek");
+ if (write (fd, trackbuf, bpt) != bpt) err(1, "write");
+ if(verbose) putc('W',stdout);
+
+ if (lseek (fd, (long) track*bpt, 0) < 0) err(1, "lseek");
+ if (read (fd, vrfybuf, bpt) != bpt) err(1, "read");
+ if(verbose) putc('R',stdout);
+
+ if (memcmp(trackbuf,vrfybuf,bpt)) err(1, "compare");
+ if(verbose) putc('C',stdout);
+
+ memset(trackbuf,0,bpt);
+ for(j=0;inputfd >= 0 && j<bpt;j+=i) {
+ if(!(i = read(inputfd,trackbuf+j,bpt-j))) {
+ close(inputfd);
+ inputfd = -1;
+ break;
+ }
+ nbytes += i;
+ }
+ }
+ close(fd);
+ putc('\r',stdout);
+ }
+ if(verbose)
+ printf("%d bytes on %d flopp%s\n",nbytes,fdn,fdn==1?"y":"ies");
+ exit(0);
+}
diff --git a/usr.sbin/fifolog/Makefile b/usr.sbin/fifolog/Makefile
new file mode 100644
index 0000000..59ac9fe
--- /dev/null
+++ b/usr.sbin/fifolog/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+SUBDIR= lib fifolog_create fifolog_writer fifolog_reader
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/fifolog/Makefile.inc b/usr.sbin/fifolog/Makefile.inc
new file mode 100644
index 0000000..7fb2a9e
--- /dev/null
+++ b/usr.sbin/fifolog/Makefile.inc
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+LIBFIFOLOG= ${.OBJDIR}/../lib/libfifolog.a
+
+WARNS?= 6
+
+#LINT= flint
+#LINTFLAGS= ${.CURDIR}/../flint.lnt -I/usr/include
+
+.include "../Makefile.inc"
diff --git a/usr.sbin/fifolog/fifolog_create/Makefile b/usr.sbin/fifolog/fifolog_create/Makefile
new file mode 100644
index 0000000..8b59b25
--- /dev/null
+++ b/usr.sbin/fifolog/fifolog_create/Makefile
@@ -0,0 +1,22 @@
+# $FreeBSD$
+
+PROG= fifolog_create
+
+CFLAGS+= -I${.CURDIR}/../lib
+
+DPADD= ${LIBFIFOLOG} ${LIBUTIL}
+LDADD= ${LIBFIFOLOG} -lutil
+
+MAN= fifolog.1
+MLINKS= fifolog.1 fifolog_create.1 \
+ fifolog.1 fifolog_reader.1 \
+ fifolog.1 fifolog_writer.1
+
+regress:
+ rm -f /tmp/fifolog.?
+ ./${PROG} /tmp/fifolog.0
+ ./${PROG} -s 10m /tmp/fifolog.1
+ ./${PROG} -l 1k /tmp/fifolog.2
+ ./${PROG} -r 1k /tmp/fifolog.3
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/fifolog/fifolog_create/fifolog.1 b/usr.sbin/fifolog/fifolog_create/fifolog.1
new file mode 100644
index 0000000..e754577
--- /dev/null
+++ b/usr.sbin/fifolog/fifolog_create/fifolog.1
@@ -0,0 +1,218 @@
+.\" Copyright (c) 2008 Poul-Henning Kamp
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd February 9, 2008
+.Os
+.Dt FIFOLOG 1
+.Sh NAME
+.Nm fifolog_create , fifolog_write , fifolog_read
+.Nd "initialize, write, seek and extract data from a fifolog"
+.Sh SYNOPSIS
+.Nm fifolog_create
+.Op Fl l Ar record-size
+.Op Fl r Ar record-count
+.Op Fl s Ar size
+.Ar file
+.Nm fifolog_reader
+.Op Fl t
+.Op Fl b Ar tstart
+.Op Fl B Ar Tstart
+.Op Fl e Ar tend
+.Op Fl E Ar Tend
+.Op Fl o Ar ofile
+.Op Fl R Ar regexp
+.Op Fl T Ar timefmt
+.Ar file
+.Nm fifolog_writer
+.Op Fl w Ar write-rate
+.Op Fl s Ar sync-rate
+.Op Fl z Ar compression
+.Ar file
+.Sh DESCRIPTION
+Fifologs provide a compact round-robin circular storage for
+recording text and binary information to permanent storage in a bounded
+and predictable fashion, time and space wise.
+.Pp
+A fifolog can be stored either directly on a disk partition or in a
+regular file.
+.Pp
+The input data stream is encoded, compressed and marked up with
+timestamps before it is written to storage, such that it is possible
+to seek out a particular time interval in the stored data, without
+having to decompress the entire logfile.
+.Pp
+The
+.Nm fifolog_create
+utility
+is used to initialize the first sector of a disk device
+or file system file to make it a fifolog and should be called only
+once.
+.Pp
+Running
+.Nm fifolog_create
+on an existing fifolog will reset it so that
+.Nm fifolog_reader
+and
+.Nm fifolog_writer
+will not see the previous contents.
+(The previous contents are not physically erased, and with a bit
+of hand-work all but the first record can be easily recovered.)
+.Pp
+If the
+.Ar file
+does not already exist,
+.Nm
+will attempt to create and
+.Xr ftruncate 2
+it to the specified size, defaulting to 86400 records of 512 bytes
+if the
+.Fl r , l
+or
+.Fl s
+options do not specify otherwise.
+.Pp
+The
+.Nm fifolog_writer
+utility
+will read standard input and write it to the end of the fifolog
+according to the parameters given.
+.Pp
+Writes happen whenever the output buffer is filled with compressed
+data or when either of two timers expire, forcing a partially filled
+buffer to be written.
+.Pp
+The first and faster timer,
+.Fl w Ar write-rate ,
+forces available data to be written
+but does not flush and reset the compression dictionary.
+This timer is intended to minimize the amount of logdata lost in RAM
+in case of a crash and by default it fires 10 seconds after
+the previous write.
+.Pp
+The second and slower timer,
+.Fl s Ar sync-rate ,
+forces a full flush and reset of the compression
+engine and causes the next record written to be a synchronization
+point with an uncompressed timestamp, making it possible to start
+reading the logfile from that record.
+By default this timer fires a minute after the previous sync.
+.Pp
+The
+.Fl z Ar compression
+option controls the
+.Xr zlib 3
+compression level; legal values are zero to nine which is the default.
+.Pp
+The
+.Nm fifolog_reader
+utility
+will retrieve records from the fifolog according to the specified
+parameters and write them either to standard output or the file specified
+with
+.Fl o .
+.Pp
+It is possible to specify a start and end time to limit the amount
+of data
+.Nm fifolog_reader
+will report.
+The lower-case variants
+.Fl b
+and
+.Fl e
+take a
+.Vt time_t
+value, whereas the upper-case variants
+.Fl B
+and
+.Fl E
+take human-readable specifications such as
+.Dq Li "1 hour ago" .
+.Pp
+The
+.Fl t
+option forces timestamps to be formatted as
+.Dq Li "YYYYMMDDhhmmss"
+instead of as
+.Vt time_t ,
+and
+.Fl T
+allows the specification of an
+.Xr strftime 3
+formatting string.
+.Pp
+Finally, records can be filtered such that only records matching the
+.Pq Dv REG_BASIC
+regular expression specified with
+.Fl R
+are output.
+.Sh IMPLEMENTATION NOTES
+The data stored in the fifolog consists of three layers, an outer
+layer that allows searches to synchronization points based on timestamps
+without having to decompress and decode the actual contents, a
+compression layer implemented with
+.Xr zlib 3 ,
+and an inner serialization and timestamping layer.
+.Pp
+The exact encoding is described in the
+.Pa fifolog.h
+file.
+.Pp
+Fifolog is particularly well suited for use on Flash based media, where
+it results in much lower write-wear, than a file system with regular
+log files rotated with
+.Xr newsyslog 8
+etc.
+.Sh EXAMPLES
+Create a fifolog with 1024*1024 records of 512 bytes:
+.Pp
+.Dl "fifolog_create -r 10m /tmp/fifolog"
+.Pp
+Write a single record to this file:
+.Pp
+.Dl "date | fifolog_writer /tmp/fifolog"
+.Pp
+Read it back with human readable timestamps:
+.Pp
+.Dl "fifolog_reader -t /tmp/fifolog"
+.Pp
+One particular useful use of
+.Nm fifolog_writer
+is with
+.Xr syslogd 8
+using a line such as this in
+.Xr /etc/syslog.conf 5 :
+.Pp
+.Dl "*.* |fifolog_writer /var/log/syslog_fifolog"
+.Sh HISTORY
+The fifolog tools have been liberated from an open source
+.Tn SCADA
+applications called
+.Dq measured ,
+which monitors and controls remote radio navigation
+transmitters for the Danish Air Traffic Control system.
+.Sh AUTHORS
+The fifolog tools were written by
+.An Poul-Henning Kamp .
diff --git a/usr.sbin/fifolog/fifolog_create/fifolog_create.c b/usr.sbin/fifolog/fifolog_create/fifolog_create.c
new file mode 100644
index 0000000..5e91db1
--- /dev/null
+++ b/usr.sbin/fifolog/fifolog_create/fifolog_create.c
@@ -0,0 +1,115 @@
+/*-
+ * Copyright (c) 2005-2008 Poul-Henning Kamp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <err.h>
+#include <libutil.h>
+
+#include "libfifolog.h"
+
+#define DEF_RECSIZE 512
+#define DEF_RECCNT (24 * 60 * 60)
+
+static void
+usage(void)
+{
+ fprintf(stderr, "Usage: fifolog_create [-l record-size] "
+ "[-r record-count] [-s size] file\n");
+ exit(EX_USAGE);
+}
+
+int
+main(int argc, char * const *argv)
+{
+ int ch;
+ int64_t size;
+ int64_t recsize;
+ int64_t reccnt;
+ const char *s;
+
+ recsize = 0;
+ size = 0;
+ reccnt = 0;
+ while((ch = getopt(argc, argv, "l:r:s:")) != -1) {
+ switch (ch) {
+ case 'l':
+ if (expand_number(optarg, &recsize))
+ err(1, "Couldn't parse -l argument");
+ break;
+ case 'r':
+ if (expand_number(optarg, &reccnt))
+ err(1, "Couldn't parse -r argument");
+ break;
+ case 's':
+ if (expand_number(optarg, &size))
+ err(1, "Couldn't parse -s argument");
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 1)
+ usage();
+
+ if (size != 0 && reccnt != 0 && recsize != 0) { /* N N N */
+ if (size != reccnt * recsize)
+ errx(1, "Inconsistent -l, -r and -s values");
+ } else if (size != 0 && reccnt != 0 && recsize == 0) { /* N N Z */
+ if (size % reccnt)
+ errx(1,
+ "Inconsistent -r and -s values (gives remainder)");
+ recsize = size / reccnt;
+ } else if (size != 0 && reccnt == 0 && recsize != 0) { /* N Z N */
+ if (size % recsize)
+ errx(1, "-s arg not divisible by -l arg");
+ } else if (size != 0 && reccnt == 0 && recsize == 0) { /* N Z Z */
+ recsize = DEF_RECSIZE;
+ if (size % recsize)
+ errx(1, "-s arg not divisible by %jd", recsize);
+ } else if (size == 0 && reccnt != 0 && recsize != 0) { /* Z N N */
+ size = reccnt * recsize;
+ } else if (size == 0 && reccnt != 0 && recsize == 0) { /* Z N Z */
+ recsize = DEF_RECSIZE;
+ size = reccnt * recsize;
+ } else if (size == 0 && reccnt == 0 && recsize != 0) { /* Z Z N */
+ size = DEF_RECCNT * recsize;
+ } else if (size == 0 && reccnt == 0 && recsize == 0) { /* Z Z Z */
+ recsize = DEF_RECSIZE;
+ size = DEF_RECCNT * recsize;
+ }
+
+ s = fifolog_create(argv[0], size, recsize);
+ if (s == NULL)
+ return (0);
+ err(1, "%s", s);
+}
diff --git a/usr.sbin/fifolog/fifolog_reader/Makefile b/usr.sbin/fifolog/fifolog_reader/Makefile
new file mode 100644
index 0000000..7dcc8c2
--- /dev/null
+++ b/usr.sbin/fifolog/fifolog_reader/Makefile
@@ -0,0 +1,20 @@
+# $FreeBSD$
+
+PROG= fifolog_reader
+
+CFLAGS+= -I${.CURDIR}/../lib
+
+NO_MAN= # see ../fifolog_create/fifolog.1
+
+DPADD= ${LIBFIFOLOG} ${LIBUTIL} ${LIBZ}
+LDADD= ${LIBFIFOLOG} -lutil -lz
+
+regress:
+ ./${PROG} /tmp/fifolog.0
+ ./${PROG} -t /tmp/fifolog.0
+ ./${PROG} /tmp/fifolog.1
+ ./${PROG} -B "00:00" /tmp/fifolog.1
+ ./${PROG} -T "%y%m%d-%H%M%S" /tmp/fifolog.1
+ ./${PROG} -T "" /tmp/fifolog.1
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/fifolog/fifolog_reader/fifolog_reader.c b/usr.sbin/fifolog/fifolog_reader/fifolog_reader.c
new file mode 100644
index 0000000..7178082
--- /dev/null
+++ b/usr.sbin/fifolog/fifolog_reader/fifolog_reader.c
@@ -0,0 +1,171 @@
+/*-
+ * Copyright (c) 2005-2008 Poul-Henning Kamp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <unistd.h>
+#include <err.h>
+#include <time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <regex.h>
+
+#include "libfifolog.h"
+
+static time_t opt_B;
+static time_t opt_E;
+static const char *opt_T;
+static const char *opt_o;
+static const char *opt_R;
+static regex_t R;
+
+static FILE *fo;
+
+static void
+Render(void *priv __unused, time_t now, unsigned flag __unused, const unsigned char *p, unsigned l __unused)
+{
+ static struct tm utc;
+ char buf[128];
+ int i;
+
+ if (now < opt_B || now > opt_E)
+ return;
+
+ if (opt_R != NULL && regexec(&R, (const char *)p, 0, NULL, 0))
+ return;
+
+ if (opt_T != NULL && *opt_T == '\0') {
+ fprintf(fo, "%s\n", p);
+ } else if (opt_T != NULL) {
+ (void)gmtime_r(&now, &utc);
+ i = strftime(buf, sizeof buf, opt_T, &utc);
+ assert(i > 0);
+ fprintf(fo, "%s %s\n", buf, p);
+ } else {
+ fprintf(fo, "%12ld %s\n", (long)now, p);
+ }
+}
+
+/*--------------------------------------------------------------------*/
+
+static void
+Usage(void)
+{
+ fprintf(stderr,
+ "Usage: fiforead [options] fifofile\n"
+ "\t-b <start time integer>\n"
+ "\t-B <start time>\n"
+ "\t-e <end time integer>\n"
+ "\t-E <end time>\n"
+ "\t-o <output file>\n"
+ "\t-R <regexp> # match regexp\n"
+ "\t-t # format timestamps as %%Y%%m%%d%%H%%M%%S\n"
+ "\t-T <timestamp format>\n"
+ );
+ exit (EX_USAGE);
+}
+
+int
+main(int argc, char * const *argv)
+{
+ int ch, i;
+ off_t o;
+ struct fifolog_reader *fl;
+
+ time(&opt_E);
+ opt_o = "-";
+ while ((ch = getopt(argc, argv, "b:B:e:E:o:R:tT:")) != -1) {
+ switch (ch) {
+ case 'b':
+ opt_B = strtoul(optarg, NULL, 0);
+ break;
+ case 'B':
+ opt_B = get_date(optarg);
+ if (opt_B == -1)
+ errx(1, "Didn't understand \"%s\"", optarg);
+ break;
+ case 'e':
+ opt_E = strtoul(optarg, NULL, 0);
+ break;
+ case 'E':
+ opt_E = get_date(optarg);
+ if (opt_E == -1)
+ errx(1, "Didn't understand \"%s\"", optarg);
+ break;
+ case 'o':
+ opt_o = optarg;
+ break;
+ case 'R':
+ opt_R = optarg;
+ break;
+ case 't':
+ opt_T = "%Y%m%d%H%M%S";
+ break;
+ case 'T':
+ opt_T = optarg;
+ break;
+ default:
+ Usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (opt_R != NULL) {
+ i = regcomp(&R, opt_R, REG_NOSUB);
+ if (i != 0) {
+ char buf[BUFSIZ];
+ (void)regerror(i, &R, buf, sizeof buf);
+ fprintf(stderr, "-R argument: %s\n", buf);
+ exit (1);
+ }
+ }
+
+ if (argv[0] == NULL)
+ Usage();
+
+ fprintf(stderr, "From\t%jd %s", (intmax_t)opt_B, ctime(&opt_B));
+ fprintf(stderr, "To\t%jd %s", (intmax_t)opt_E, ctime(&opt_E));
+ if (opt_B >= opt_E)
+ errx(1, "Begin time not before End time");
+
+ fl = fifolog_reader_open(argv[0]);
+
+ if (!strcmp(opt_o, "-"))
+ fo = stdout;
+ else {
+ fo = fopen(opt_o, "w");
+ if (fo == NULL)
+ err(1, "Cannot open: %s", argv[1]);
+ }
+
+ o = fifolog_reader_seek(fl, opt_B);
+ fifolog_reader_process(fl, o, Render, NULL, opt_E);
+ return (0);
+}
diff --git a/usr.sbin/fifolog/fifolog_writer/Makefile b/usr.sbin/fifolog/fifolog_writer/Makefile
new file mode 100644
index 0000000..b6ceb63
--- /dev/null
+++ b/usr.sbin/fifolog/fifolog_writer/Makefile
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+PROG= fifolog_writer
+
+CFLAGS+= -I${.CURDIR}/../lib
+
+NO_MAN= # see ../fifolog_create/fifolog.1
+
+DPADD= ${LIBFIFOLOG} ${LIBUTIL} ${LIBZ}
+LDADD= ${LIBFIFOLOG} -lutil -lz
+
+regress:
+ date | ./${PROG} -z 0 /tmp/fifolog.0
+ lptest 65 | ./${PROG} -z 9 /tmp/fifolog.1
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/fifolog/fifolog_writer/fifolog_writer.c b/usr.sbin/fifolog/fifolog_writer/fifolog_writer.c
new file mode 100644
index 0000000..fd10711
--- /dev/null
+++ b/usr.sbin/fifolog/fifolog_writer/fifolog_writer.c
@@ -0,0 +1,114 @@
+/*-
+ * Copyright (c) 2005-2008 Poul-Henning Kamp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <err.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <assert.h>
+#include <poll.h>
+#include <string.h>
+#include <zlib.h>
+
+#include "libfifolog.h"
+
+static void
+usage(void)
+{
+ fprintf(stderr, "fifolog_writer [-w write-rate] [-s sync-rate] "
+ "[-z compression] file\n");
+ exit(EX_USAGE);
+}
+
+int
+main(int argc, char * const *argv)
+{
+ struct fifolog_writer *f;
+ const char *es;
+ struct pollfd pfd[1];
+ char buf[BUFSIZ], *p;
+ int i, c;
+ unsigned w_opt = 10;
+ unsigned s_opt = 60;
+ unsigned z_opt = Z_BEST_COMPRESSION;
+
+ while ((c = getopt(argc, argv, "w:s:z:")) != -1) {
+ switch(c) {
+ case 'w':
+ w_opt = strtoul(optarg, NULL, 0);
+ break;
+ case 's':
+ s_opt = strtoul(optarg, NULL, 0);
+ break;
+ case 'z':
+ z_opt = strtoul(optarg, NULL, 0);
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 1)
+ usage();
+
+ if (z_opt > 9)
+ usage();
+
+ if (w_opt > s_opt)
+ usage();
+
+ f = fifolog_write_new();
+ assert(f != NULL);
+
+ es = fifolog_write_open(f, argv[0], w_opt, s_opt, z_opt);
+ if (es)
+ err(1, "Error: %s", es);
+
+ while (1) {
+ pfd[0].fd = 0;
+ pfd[0].events = POLLIN;
+ i = poll(pfd, 1, 1000);
+ if (i == 1) {
+ if (fgets(buf, sizeof buf, stdin) == NULL)
+ break;
+ p = strchr(buf, '\0');
+ assert(p != NULL);
+ while (p > buf && isspace(p[-1]))
+ p--;
+ *p = '\0';
+ if (*buf != '\0')
+ fifolog_write_bytes_poll(f, 0, 0, buf, 0);
+ } else if (i == 0)
+ (void)fifolog_write_poll(f, 0);
+ }
+ (void)fifolog_write_flush(f);
+ return (0);
+}
diff --git a/usr.sbin/fifolog/flint.lnt b/usr.sbin/fifolog/flint.lnt
new file mode 100644
index 0000000..16ee7ad
--- /dev/null
+++ b/usr.sbin/fifolog/flint.lnt
@@ -0,0 +1,49 @@
+// $FreeBSD$
+// FlexeLint file for fifolog tools
+//
+
+-passes=3
+-ffc
+
+// GCC
+-cgnu
++d__FreeBSD__=7
++d__GNUC__=4
++d__GNUC_MINOR__=2
++d__FreeBSD_cc_version=700003
++d__attribute__()=
+-d__builtin_va_list=void* // used by stdarg.h
+// -d__builtin_stdarg_start()=_to_semi // ditto
+// -d__builtin_va_start(a,b)=((void)(b),(a)=0) // ditto
+// -d__builtin_va_end()=_to_semi // ditto
++rw(__inline) // enable the (non-standard) __inline keyword
++rw(__inline__) // enable the (non-standard) __inline__ keyword
+
++d"__unused=/*lint -e{715} -e{818} */"
+
+-e537 // Repeated include file
+-elib(652) // #define of symbol '...' declared previously
+-function(exit,__assert)
+-function(exit,err)
+-function(exit,errx)
+-e716 // while(1) ...
+-e717 // do ... while(0)
+
+// Ignore return values
+-esym(534, memset)
+-esym(534, memcpy)
+-esym(534, strcpy)
+-esym(534, printf)
+-esym(534, time)
+-esym(534, fprintf)
+-esym(534, vfprintf)
+
++libh(fifolog.h)
++libh(miniobj.h)
++libh(libfifolog.h)
+
+-e713 // loss of precision sign/unsigned
+-e732 // loss of sign
+-e734 // loss of precision assignment
+-e737 // loss of sign in promotion int->unsigned
+-e573 // sign/unsign mix in divide
diff --git a/usr.sbin/fifolog/lib/Makefile b/usr.sbin/fifolog/lib/Makefile
new file mode 100644
index 0000000..c10126f
--- /dev/null
+++ b/usr.sbin/fifolog/lib/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+LIB= fifolog
+INTERNALLIB= # API not published or supported.
+
+SRCS= fifolog_int.c fifolog_create.c fifolog_write_poll.c fifolog_reader.c
+SRCS+= getdate.y
+
+CFLAGS+= -I${.CURDIR}
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/fifolog/lib/fifolog.h b/usr.sbin/fifolog/lib/fifolog.h
new file mode 100644
index 0000000..d5c8297
--- /dev/null
+++ b/usr.sbin/fifolog/lib/fifolog.h
@@ -0,0 +1,138 @@
+/*-
+ * Copyright (c) 2005-2008 Poul-Henning Kamp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __LOCAL_FIFOLOG_H_
+#define __LOCAL_FIFOLOG_H_
+
+/*
+ * Definitions for fifolog "protocol": the on-media layout.
+ *
+ * The fifolog on-media record has three layers:
+ * The outher timestamping and synchronization layer.
+ * The zlib implemented data compression
+ * The inner sequencing and identification layer.
+ *
+ * All three layers are synchronized at a subset of the outher layer
+ * record boundaries, from where reading can be initiated.
+ *
+ *
+ * The outher layer:
+ * -----------------
+ * The first record in a fifolog contains a magic string and version
+ * information along with a 32be encoded recordsize for all records
+ * in the fifolog, including the first.
+ * The recordsize is explicit to avoid ambiguities when a media is
+ * moved from one machine to another.
+ *
+ * Each record in the fifolog has the following contents:
+ * offset type contents
+ * --------------------------------------------------------------
+ * 0 32be sequence_number
+ * The sequence number is randomly chosen for the
+ * fifolog and increments once for each record written.
+ * It's precense allow quick identification of the next
+ * record to be written using a binary search for the
+ * first place where a discontinuity in the sequence
+ * numbers occur.
+ * 4 8 flags (FIFOLOG_FLG_*)
+ *
+ * If (flags & (FIFOLOG_FLG_SYNC)) the record is a synchronization point
+ * at which the inner layers are aligned so that reading can be started
+ * at this point.
+ * To enable seeks into the file based on timestamps, a third field is
+ * present in these records as well:
+ * 5 32be time_t containing POSIX's understanding of UTC.
+ *
+ * These fields are immediately followed by the inner layer payload as
+ * described below, which has variable length.
+ *
+ * If the inner layer payload is shorter than the available space in
+ * the record, it is padded with zero bytes, and the number of unused
+ * bytes, including the encoded length thereof is recorded at the end
+ * of the record as follows:
+ *
+ * If (flags & FIFOLOG_FLG_1BYTE)
+ * n-1 8 number of unused bytes
+ * else if (flags & FIFOLOG_FLG_4BYTE)
+ * n-4 32be number of unused bytes
+ *
+ *
+ * The gzip layer
+ * --------------
+ * Is just output from zlib, nothing special here. FIFOLOG_FLG_SYNC
+ * corresponds to Z_FINISH flags to zlib.
+ * In most cases, the timer will expire before zlib has filled an entire
+ * record in which case Z_SYNC_FLUSH will be used to force as much as
+ * possible into the buffer before it is written. This is not marked
+ * in outher layer (apart from a natural correlation with padding) since
+ * zlibs data stream handles this without help.
+ *
+ *
+ * The inner layer:
+ * ----------------
+ * The inner layer contains data indentification and to the second
+ * timestamping (the timestamp in the outherlayer only marks the
+ * first possible timestamp for content in the SYNC record).
+ *
+ * offset type contents
+ * --------------------------------------------------------------
+ * 0 32be ident
+ *
+ * The bottom 30 bits of the identification word are application defined,
+ * presently unused in the stand-alone fifolog tools, but used in the
+ * original "measured" application that originated the fifolog format.
+ * Should for instance syslogd(8) grow native support for fifolog format,
+ * it could store the message priority here.
+ *
+ * If (ident & FIFOLOG_TIMESTAMP) the record is prefixed by:
+ * 4 32be time_t containing POSIX's understanding of UTC.
+ *
+ * Then follows the content, either as a NUL terminated string or as
+ * a lenght encoded binary sequence:
+ *
+ * If (ident & FIFOLOG_LENGTH) the record is prefixed by:
+ * {0|4} 8 length of binary data
+ *
+ */
+
+/* Magic identification string */
+#define FIFOLOG_FMT_MAGIC "Measured FIFOLOG Ver 1.01\n"
+
+/* Offset of the 32be encoded recordsize in the first sector */
+#define FIFOLOG_OFF_BS 0x20
+
+#define FIFOLOG_FLG_1BYTE 0x01
+#define FIFOLOG_FLG_4BYTE 0x02
+#define FIFOLOG_FLG_RESTART 0x40
+#define FIFOLOG_FLG_SYNC 0x80
+
+#define FIFOLOG_TIMESTAMP 0x80000000
+#define FIFOLOG_LENGTH 0x40000000
+#define FIFOLOG_IDENT 0x3fffffff
+
+#endif /* __LOCAL_FIFOLOG_H_ */
diff --git a/usr.sbin/fifolog/lib/fifolog_create.c b/usr.sbin/fifolog/lib/fifolog_create.c
new file mode 100644
index 0000000..68f8cef
--- /dev/null
+++ b/usr.sbin/fifolog/lib/fifolog_create.c
@@ -0,0 +1,122 @@
+/*-
+ * Copyright (c) 2005-2008 Poul-Henning Kamp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/endian.h>
+#include <sys/stat.h>
+#include <sys/disk.h>
+
+#include "fifolog.h"
+#include "libfifolog.h"
+
+const char *
+fifolog_create(const char *fn, off_t size, unsigned recsize)
+{
+ int i, fd;
+ unsigned u;
+ off_t ms;
+ struct stat st;
+ char *buf;
+ int created;
+
+ fd = open(fn, O_WRONLY | O_TRUNC | O_EXCL | O_CREAT, 0644);
+ if (fd < 0) {
+ created = 0;
+ fd = open(fn, O_WRONLY);
+ if (fd < 0)
+ return ("Could not open");
+ } else
+ created = 1;
+
+ /* Default sectorsize is 512 */
+ if (recsize == 0)
+ recsize = 512;
+
+ /* See what we got... */
+ i = fstat(fd, &st);
+ assert(i == 0);
+ if (!S_ISBLK(st.st_mode) &&
+ !S_ISCHR(st.st_mode) &&
+ !S_ISREG(st.st_mode)) {
+ assert(!close (fd));
+ return ("Wrong file type");
+ }
+
+ if(!created && S_ISREG(st.st_mode)) {
+ assert(!close (fd));
+ return ("Wrong file type");
+ }
+
+ /* For raw disk with larger sectors: use 1 sector */
+ i = ioctl(fd, DIOCGSECTORSIZE, &u);
+ if (i == 0 && (u > recsize || (recsize % u) != 0))
+ recsize = u;
+
+ /* If no configured size, or too large for disk, use device size */
+ i = ioctl(fd, DIOCGMEDIASIZE, &ms);
+ if (i == 0 && (size == 0 || size > ms))
+ size = ms;
+
+ if (size == 0 && S_ISREG(st.st_mode))
+ size = st.st_size;
+
+ if (size == 0)
+ size = recsize * (off_t)(24*60*60);
+
+ if (S_ISREG(st.st_mode) && ftruncate(fd, size) < 0)
+ return ("Could not ftrunc");
+
+ buf = calloc(recsize, 1);
+ if (buf == NULL)
+ return ("Could not malloc");
+
+ strcpy(buf, FIFOLOG_FMT_MAGIC); /*lint !e64 */
+ be32enc(buf + FIFOLOG_OFF_BS, recsize);
+ if ((int)recsize != pwrite(fd, buf, recsize, 0)) {
+ i = errno;
+ free(buf);
+ errno = i;
+ return ("Could not write first sector");
+ }
+ memset(buf, 0, recsize);
+ if ((int)recsize != pwrite(fd, buf, recsize, recsize)) {
+ i = errno;
+ free(buf);
+ errno = i;
+ return ("Could not write second sector");
+ }
+ free(buf);
+ assert(0 == close(fd));
+ return (NULL);
+}
diff --git a/usr.sbin/fifolog/lib/fifolog_int.c b/usr.sbin/fifolog/lib/fifolog_int.c
new file mode 100644
index 0000000..f87fd82
--- /dev/null
+++ b/usr.sbin/fifolog/lib/fifolog_int.c
@@ -0,0 +1,275 @@
+/*-
+ * Copyright (c) 2005-2008 Poul-Henning Kamp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <zlib.h>
+
+#include <sys/disk.h>
+#include <sys/endian.h>
+#include <sys/stat.h>
+
+#include "miniobj.h"
+#include "fifolog.h"
+#include "libfifolog_int.h"
+
+/*
+ * Memory handling for zlib
+ */
+
+static voidpf
+fifo_zalloc(voidpf opaque __unused, uInt items, uInt size)
+{
+
+ return calloc(items,size);
+}
+
+static void
+fifo_zfree(voidpf opaque __unused, voidpf address)
+{
+
+ free(address);
+}
+
+/*
+ * Open a fifolog file or partition for reading or writing.
+ *
+ * Return value is NULL for success or a error description string to
+ * be augmented by errno if non-zero.
+ *
+ * The second function is just an error-handling wrapper around the
+ * first which, does the actual work.
+ */
+
+static const char *
+fifolog_int_open_i(struct fifolog_file *f, const char *fname, int mode)
+{
+ struct stat st;
+ unsigned u;
+ int i;
+
+ f->fd = open(fname, mode ? O_RDWR : O_RDONLY);
+ if (f->fd < 0)
+ return ("Cannot open");
+
+ /* Determine initial record size guesstimate */
+ i = ioctl(f->fd, DIOCGSECTORSIZE, &f->recsize);
+ if (i != 0 && errno != ENOTTY)
+ return ("ioctl(DIOCGSECTORSIZE) failed");
+
+ if (i != 0) {
+ i = fstat(f->fd, &st);
+ if (!S_ISREG(st.st_mode))
+ return ("Neither disk nor regular file");
+ f->recsize = 512;
+ f->logsize = st.st_size;
+ } else if (f->recsize < 64) {
+ return ("Disk device sectorsize smaller than 64");
+ } else {
+ i = ioctl(f->fd, DIOCGMEDIASIZE, &f->logsize);
+ if (i < 0 && errno != ENOTTY)
+ return ("ioctl(DIOCGMEDIASIZE) failed");
+ }
+
+ /* Allocate a record buffer */
+ f->recbuf = malloc(f->recsize);
+ if (f->recbuf == NULL)
+ return ("Cannot malloc");
+
+ /* Read and validate the label sector */
+ i = pread(f->fd, f->recbuf, f->recsize, 0);
+ if (i < 0 || i < (int)f->recsize)
+ return ("Read error, first sector");
+
+ errno = 0;
+ if (memcmp(f->recbuf, FIFOLOG_FMT_MAGIC, strlen(FIFOLOG_FMT_MAGIC) + 1))
+ return ("Wrong or missing magic string");
+
+ u = be32dec(f->recbuf + FIFOLOG_OFF_BS);
+ if (u < 64)
+ return ("Wrong record size in header (<64)");
+
+ if ((off_t)u >= f->logsize)
+ return ("Record size in header bigger than fifolog");
+
+ f->recsize = u;
+
+ /* Reallocate the buffer to correct size if necessary */
+ if (u != f->recsize) {
+ free(f->recbuf);
+ f->recbuf = NULL;
+ f->recsize = u;
+ f->recbuf = malloc(f->recsize);
+ if (f->recbuf == NULL)
+ return ("Cannot malloc");
+ }
+
+ /* Calculate number of records in fifolog */
+ f->logsize /= u;
+ if (f->logsize < 10)
+ return ("less than 10 records in fifolog");
+
+ f->logsize--; /* the label record */
+
+ /* Initialize zlib handling */
+
+ f->zs = calloc(sizeof *f->zs, 1);
+ if (f->zs == NULL)
+ return ("cannot malloc");
+ f->zs->zalloc = fifo_zalloc;
+ f->zs->zfree = fifo_zfree;
+
+ return (NULL);
+}
+
+const char *
+fifolog_int_open(struct fifolog_file **ff, const char *fname, int mode)
+{
+ struct fifolog_file fs, *f;
+ const char *retval;
+ int e;
+
+ f = &fs;
+ memset(f, 0, sizeof *f);
+ f->fd = -1;
+ retval = fifolog_int_open_i(f, fname, mode);
+ e = errno;
+ if (retval == NULL) {
+ *ff = malloc(sizeof *f);
+ if (*ff != NULL) {
+ memcpy(*ff, f, sizeof *f);
+ (*ff)->magic = FIFOLOG_FILE_MAGIC;
+ return (retval);
+ }
+ }
+ fifolog_int_close(&f);
+ errno = e;
+ return (retval);
+}
+
+void
+fifolog_int_close(struct fifolog_file **ff)
+{
+ struct fifolog_file *f;
+
+ f = *ff;
+ *ff = NULL;
+ if (f == NULL)
+ return;
+
+ if (f->fd >= 0)
+ (void)close(f->fd);
+ if (f->zs != NULL)
+ free(f->zs);
+ if (f->recbuf != NULL)
+ free(f->recbuf);
+}
+
+static void
+fifolog_int_file_assert(const struct fifolog_file *ff)
+{
+
+ CHECK_OBJ_NOTNULL(ff, FIFOLOG_FILE_MAGIC);
+ assert(ff->fd >= 0);
+ assert(ff->recbuf != NULL);
+}
+
+
+/*
+ * Read a record.
+ *
+ * Return zero on success
+ */
+
+int
+fifolog_int_read(const struct fifolog_file *ff, off_t recno)
+{
+ int i;
+
+ fifolog_int_file_assert(ff);
+ if (recno >= ff->logsize)
+ return (-1);
+ recno++; /* label sector */
+ i = pread(ff->fd, ff->recbuf, ff->recsize, recno * ff->recsize);
+ if (i < 0)
+ return (-2);
+ if (i != (int)ff->recsize)
+ return (-3);
+ return (0);
+}
+
+/*
+ * Find the last written record in the fifolog.
+ *
+ * Return is error string or NULL on success
+ */
+
+const char *
+fifolog_int_findend(const struct fifolog_file *ff, off_t *last)
+{
+ off_t o, s;
+ int e;
+ unsigned seq0, seq;
+
+ fifolog_int_file_assert(ff);
+
+ o = 0;
+ e = fifolog_int_read(ff, o);
+ if (e)
+ return("Read error, first record");
+
+ seq0 = be32dec(ff->recbuf);
+
+ /* If the first records sequence is zero, the fifolog is empty */
+ if (seq0 == 0) {
+ *last = o;
+ return (NULL);
+ }
+
+ /* Do a binary search for a discontinuity in the sequence numbers */
+ s = ff->logsize / 2;
+ do {
+ e = fifolog_int_read(ff, o + s);
+ if (e)
+ return ("Read error while searching");
+ seq = be32dec(ff->recbuf);
+ if (seq == seq0 + s) {
+ o += s;
+ seq0 = seq;
+ }
+ s /= 2;
+ assert(o < ff->logsize);
+ } while (s > 0);
+
+ *last = o;
+ return (NULL);
+}
diff --git a/usr.sbin/fifolog/lib/fifolog_reader.c b/usr.sbin/fifolog/lib/fifolog_reader.c
new file mode 100644
index 0000000..77e586f
--- /dev/null
+++ b/usr.sbin/fifolog/lib/fifolog_reader.c
@@ -0,0 +1,318 @@
+/*-
+ * Copyright (c) 2005-2008 Poul-Henning Kamp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <assert.h>
+#include <err.h>
+#include <time.h>
+#include <string.h>
+#include <stdlib.h>
+#include <zlib.h>
+#include <sys/endian.h>
+
+#include "fifolog.h"
+#include "libfifolog.h"
+#include "libfifolog_int.h"
+#include "miniobj.h"
+
+/*--------------------------------------------------------------------*/
+
+struct fifolog_reader {
+ unsigned magic;
+#define FIFOLOG_READER_MAGIC 0x1036d139
+ struct fifolog_file *ff;
+ unsigned olen;
+ unsigned char *obuf;
+ time_t now;
+};
+
+struct fifolog_reader *
+fifolog_reader_open(const char *fname)
+{
+ const char *retval;
+ struct fifolog_reader *fr;
+ int i;
+
+ fr = calloc(sizeof *fr, 1);
+ if (fr == NULL)
+ err(1, "Cannot malloc");
+
+ retval = fifolog_int_open(&fr->ff, fname, 0);
+ if (retval != NULL)
+ err(1, "%s", retval);
+
+ fr->olen = fr->ff->recsize * 16;
+ fr->obuf = calloc(fr->olen, 1);
+ if (fr->obuf == NULL)
+ err(1, "Cannot malloc");
+
+ i = inflateInit(fr->ff->zs);
+ assert(i == Z_OK);
+
+ fr->magic = FIFOLOG_READER_MAGIC;
+ return (fr);
+}
+
+/*
+ * Find the next SYNC block
+ *
+ * Return:
+ * 0 - empty fifolog
+ * 1 - found sync block
+ * 2 - would have wrapped around
+ * 3 - End of written log.
+ */
+
+static int
+fifolog_reader_findsync(const struct fifolog_file *ff, off_t *o)
+{
+ int e;
+ unsigned seq, seqs;
+
+ assert(*o < ff->logsize);
+ e = fifolog_int_read(ff, *o);
+ if (e)
+ err(1, "Read error (%d) while looking for SYNC", e);
+ seq = be32dec(ff->recbuf);
+ if (*o == 0 && seq == 0)
+ return (0);
+
+ if (ff->recbuf[4] & FIFOLOG_FLG_SYNC)
+ return (1); /* That was easy... */
+ while(1) {
+ assert(*o < ff->logsize);
+ (*o)++;
+ seq++;
+ if (*o == ff->logsize)
+ return (2); /* wraparound */
+ e = fifolog_int_read(ff, *o);
+ if (e)
+ err(1, "Read error (%d) while looking for SYNC", e);
+ seqs = be32dec(ff->recbuf);
+ if (seqs != seq)
+ return (3); /* End of log */
+ if (ff->recbuf[4] & FIFOLOG_FLG_SYNC)
+ return (1); /* Bingo! */
+ }
+}
+
+/*
+ * Seek out a given timestamp
+ */
+
+off_t
+fifolog_reader_seek(const struct fifolog_reader *fr, time_t t0)
+{
+ off_t o, s, st;
+ time_t t, tt;
+ unsigned seq, seqs;
+ const char *retval;
+ int e;
+
+ CHECK_OBJ_NOTNULL(fr, FIFOLOG_READER_MAGIC);
+
+ /*
+ * First, find the first SYNC block
+ */
+ o = 0;
+ e = fifolog_reader_findsync(fr->ff, &o);
+ if (e == 0)
+ return (0); /* empty fifolog */
+ assert(e == 1);
+
+ assert(fr->ff->recbuf[4] & FIFOLOG_FLG_SYNC);
+ seq = be32dec(fr->ff->recbuf);
+ t = be32dec(fr->ff->recbuf + 5);
+
+ if (t > t0) {
+ /* Check if there is a second older part we can use */
+ retval = fifolog_int_findend(fr->ff, &s);
+ if (retval != NULL)
+ err(1, "%s", retval);
+ s++;
+ e = fifolog_reader_findsync(fr->ff, &s);
+ if (e == 0)
+ return (0); /* empty fifolog */
+ if (e == 1) {
+ o = s;
+ seq = be32dec(fr->ff->recbuf);
+ t = be32dec(fr->ff->recbuf + 5);
+ }
+ }
+
+ /* Now do a binary search to find the sync block right before t0 */
+ s = st = (fr->ff->logsize - o) / 2;
+ while (s > 1) {
+ /* We know we shouldn't wrap */
+ if (o + st > fr->ff->logsize + 1) {
+ s = st = s / 2;
+ continue;
+ }
+ e = fifolog_int_read(fr->ff, o + st);
+ if (e) {
+ s = st = s / 2;
+ continue;
+ }
+ /* If not in same part, sequence won't match */
+ seqs = be32dec(fr->ff->recbuf);
+ if (seqs != seq + st) {
+ s = st = s / 2;
+ continue;
+ }
+ /* If not sync block, try next */
+ if (!(fr->ff->recbuf[4] & FIFOLOG_FLG_SYNC)) {
+ st++;
+ continue;
+ }
+ /* Check timestamp */
+ tt = be32dec(fr->ff->recbuf + 5);
+ if (tt >= t0) {
+ s = st = s / 2;
+ continue;
+ }
+ o += st;
+ seq = seqs;
+ }
+ fprintf(stderr, "Read from %jx\n", o * fr->ff->recsize);
+ return (o);
+}
+
+static unsigned char *
+fifolog_reader_chop(struct fifolog_reader *fr, fifolog_reader_render_t *func, void *priv)
+{
+ u_char *p, *q;
+ uint32_t v, w, u;
+
+ p = fr->obuf;
+ q = fr->obuf + (fr->olen - fr->ff->zs->avail_out);
+
+ while (1) {
+ /* Make sure we have a complete header */
+ if (p + 5 >= q)
+ return (p);
+ w = 4;
+ u = be32dec(p);
+ if (u & FIFOLOG_TIMESTAMP) {
+ fr->now = be32dec(p + 4);
+ w += 4;
+ }
+ if (u & FIFOLOG_LENGTH) {
+ v = p[w];
+ w++;
+ } else {
+ for (v = 0; p + v + w < q && p[v + w] != '\0'; v++)
+ continue;
+ if (p + v + w >= q)
+ return (p);
+ v++;
+ }
+ func(priv, fr->now, u, p + w, v);
+ p += w + v;
+ }
+}
+
+/*
+ * Process fifolog until end of written log or provided timestamp
+ */
+
+void
+fifolog_reader_process(struct fifolog_reader *fr, off_t from, fifolog_reader_render_t *func, void *priv, time_t end)
+{
+ uint32_t seq, lseq;
+ off_t o = from;
+ int i, e;
+ time_t t;
+ u_char *p, *q;
+ z_stream *zs;
+
+ CHECK_OBJ_NOTNULL(fr, FIFOLOG_READER_MAGIC);
+ zs = fr->ff->zs;
+ lseq = 0;
+ while (1) {
+ e = fifolog_int_read(fr->ff, o);
+ if (e)
+ err(1, "Read error (%d)", e);
+ if (++o >= fr->ff->logsize)
+ o = 0;
+ seq = be32dec(fr->ff->recbuf);
+ if (lseq != 0 && seq != lseq + 1)
+ break;
+ lseq = seq;
+ zs->avail_in = fr->ff->recsize - 5;
+ zs->next_in = fr->ff->recbuf + 5;
+ if (fr->ff->recbuf[4] & FIFOLOG_FLG_1BYTE)
+ zs->avail_in -= fr->ff->recbuf[fr->ff->recsize - 1];
+ if (fr->ff->recbuf[4] & FIFOLOG_FLG_4BYTE)
+ zs->avail_in -=
+ be32dec(fr->ff->recbuf + fr->ff->recsize - 4);
+ if (fr->ff->recbuf[4] & FIFOLOG_FLG_SYNC) {
+ i = inflateReset(zs);
+ assert(i == Z_OK);
+ zs->next_out = fr->obuf;
+ zs->avail_out = fr->olen;
+ t = be32dec(fr->ff->recbuf + 5);
+ if (t > end)
+ break;
+ zs->next_in += 4;
+ zs->avail_in -= 4;
+ }
+
+ while(zs->avail_in > 0) {
+ i = inflate(zs, 0);
+ if (i == Z_BUF_ERROR) {
+#if 1
+ fprintf(stderr,
+ "Z_BUF_ERROR [%d,%d] [%d,%d,%d]\n",
+ (int)(zs->next_in - fr->ff->recbuf),
+ zs->avail_in,
+ (int)(zs->next_out - fr->obuf),
+ zs->avail_out, fr->olen);
+ exit (250);
+#else
+
+ i = Z_OK;
+#endif
+ }
+ if (i == Z_STREAM_END) {
+ i = inflateReset(zs);
+ }
+ if (i != Z_OK)
+ fprintf(stderr, "inflate = %d\n", i);
+ assert(i == Z_OK);
+ if (zs->avail_out != fr->olen) {
+ q = fr->obuf + (fr->olen - zs->avail_out);
+ p = fifolog_reader_chop(fr, func, priv);
+ if (p < q)
+ (void)memmove(fr->obuf, p, q - p);
+ zs->avail_out = fr->olen - (q - p);
+ zs->next_out = fr->obuf + (q - p);
+ }
+ }
+ }
+}
diff --git a/usr.sbin/fifolog/lib/fifolog_write.h b/usr.sbin/fifolog/lib/fifolog_write.h
new file mode 100644
index 0000000..06b3233
--- /dev/null
+++ b/usr.sbin/fifolog/lib/fifolog_write.h
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 2005-2008 Poul-Henning Kamp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define FIFOLOG_PT_BYTES_PRE 0
+#define FIFOLOG_PT_BYTES_POST 1
+#define FIFOLOG_PT_WRITES 2
+#define FIFOLOG_PT_FLUSH 3
+#define FIFOLOG_PT_SYNC 4
+#define FIFOLOG_PT_RUNTIME 5
+#define FIFOLOG_NPOINT 6
+
+struct fifolog_writer {
+ unsigned magic;
+#define FIFOLOG_WRITER_MAGIC 0xf1f0706
+
+ struct fifolog_file *ff;
+
+ unsigned writerate;
+ unsigned syncrate;
+ unsigned compression;
+
+ unsigned writes_since_sync;
+
+ int cleanup;
+
+ intmax_t cnt[FIFOLOG_NPOINT];
+
+ uint32_t seq;
+ off_t recno;
+ int flag;
+ time_t last;
+
+ u_int ibufsize;
+ u_char *ibuf;
+ u_char *iptr;
+
+ time_t starttime;
+ time_t lastwrite;
+ time_t lastsync;
+};
diff --git a/usr.sbin/fifolog/lib/fifolog_write_poll.c b/usr.sbin/fifolog/lib/fifolog_write_poll.c
new file mode 100644
index 0000000..4fc5204
--- /dev/null
+++ b/usr.sbin/fifolog/lib/fifolog_write_poll.c
@@ -0,0 +1,417 @@
+/*-
+ * Copyright (c) 2005-2008 Poul-Henning Kamp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <sys/endian.h>
+
+#include <zlib.h>
+
+#include "fifolog.h"
+#include "libfifolog.h"
+#include "libfifolog_int.h"
+#include "fifolog_write.h"
+#include "miniobj.h"
+
+#define ALLOC(ptr, size) do { \
+ (*(ptr)) = calloc(size, 1); \
+ assert(*(ptr) != NULL); \
+} while (0)
+
+
+const char *fifolog_write_statnames[] = {
+[FIFOLOG_PT_BYTES_PRE] = "Bytes before compression",
+[FIFOLOG_PT_BYTES_POST] = "Bytes after compression",
+[FIFOLOG_PT_WRITES] = "Writes",
+[FIFOLOG_PT_FLUSH] = "Flushes",
+[FIFOLOG_PT_SYNC] = "Syncs",
+[FIFOLOG_PT_RUNTIME] = "Runtime"
+};
+
+/*
+ * Check that everything is all right
+ */
+static void
+fifolog_write_assert(const struct fifolog_writer *f)
+{
+
+ CHECK_OBJ_NOTNULL(f, FIFOLOG_WRITER_MAGIC);
+ assert(f->iptr == f->ff->zs->next_in + f->ff->zs->avail_in);
+ assert(f->ff->zs->next_out + f->ff->zs->avail_out == \
+ f->ff->recbuf + f->ff->recsize);
+}
+
+struct fifolog_writer *
+fifolog_write_new(void)
+{
+ struct fifolog_writer *f;
+
+ ALLOC(&f, sizeof *f);
+ f->magic = FIFOLOG_WRITER_MAGIC;
+ return (f);
+}
+
+void
+fifolog_write_destroy(struct fifolog_writer *f)
+{
+ CHECK_OBJ_NOTNULL(f, FIFOLOG_WRITER_MAGIC);
+ free(f);
+}
+
+void
+fifolog_write_close(struct fifolog_writer *f)
+{
+
+ CHECK_OBJ_NOTNULL(f, FIFOLOG_WRITER_MAGIC);
+ fifolog_int_close(&f->ff);
+ free(f->ff);
+ if (f->ibuf != NULL)
+ free(f->ibuf);
+ free(f);
+}
+
+static void
+fifo_prepobuf(struct fifolog_writer *f, time_t now, int flag)
+{
+
+ memset(f->ff->recbuf, 0, f->ff->recsize);
+ f->ff->zs->next_out = f->ff->recbuf + 5;
+ f->ff->zs->avail_out = f->ff->recsize - 5;
+ if (f->recno == 0 && f->seq == 0) {
+ srandomdev();
+ do {
+ f->seq = random();
+ } while (f->seq == 0);
+ }
+ be32enc(f->ff->recbuf, f->seq++);
+ f->ff->recbuf[4] = f->flag;
+ f->flag = 0;
+ if (flag) {
+ f->ff->recbuf[4] |= FIFOLOG_FLG_SYNC;
+ be32enc(f->ff->recbuf + 5, (u_int)now);
+ f->ff->zs->next_out += 4;
+ f->ff->zs->avail_out -= 4;
+ }
+ fifolog_write_assert(f);
+}
+
+const char *
+fifolog_write_open(struct fifolog_writer *f, const char *fn, unsigned writerate, unsigned syncrate, int compression)
+{
+ const char *es;
+ int i;
+ time_t now;
+ off_t o;
+
+ CHECK_OBJ_NOTNULL(f, FIFOLOG_WRITER_MAGIC);
+
+ /* Check for legal compression value */
+ if (compression < Z_DEFAULT_COMPRESSION ||
+ compression > Z_BEST_COMPRESSION)
+ return ("Illegal compression value");
+
+ f->writerate = writerate;
+ f->syncrate = syncrate;
+ f->compression = compression;
+
+ /* Reset statistics */
+ memset(f->cnt, 0, sizeof f->cnt);
+
+ es = fifolog_int_open(&f->ff, fn, 1);
+ if (es != NULL)
+ return (es);
+ es = fifolog_int_findend(f->ff, &o);
+ if (es != NULL)
+ return (es);
+ i = fifolog_int_read(f->ff, o);
+ if (i)
+ return ("Read error, looking for seq");
+ f->seq = be32dec(f->ff->recbuf);
+ if (f->seq == 0) {
+ /* Empty fifolog */
+ f->seq = random();
+ } else {
+ f->recno = o + 1;
+ f->seq++;
+ }
+
+ f->ibufsize = 32768;
+ ALLOC(&f->ibuf, f->ibufsize);
+ f->iptr = f->ibuf;
+ f->ff->zs->next_in = f->iptr;
+ i = deflateInit(f->ff->zs, (int)f->compression);
+ assert(i == Z_OK);
+
+ f->flag |= FIFOLOG_FLG_RESTART;
+
+ time(&now);
+ fifo_prepobuf(f, now, 1);
+ f->starttime = now;
+
+ fifolog_write_assert(f);
+ return (NULL);
+}
+
+static void
+fifo_writerec(struct fifolog_writer *f)
+{
+ int i;
+ time_t t;
+
+ fifolog_write_assert(f);
+ f->writes_since_sync++;
+
+ assert(f->recno < f->ff->logsize);
+ f->cnt[FIFOLOG_PT_BYTES_POST] += f->ff->recsize - f->ff->zs->avail_out;
+ if (f->ff->zs->avail_out == 0) {
+ /* nothing */
+ } else if (f->ff->zs->avail_out <= 255) {
+ f->ff->recbuf[f->ff->recsize - 1] =
+ (u_char)f->ff->zs->avail_out;
+ f->ff->recbuf[4] |= FIFOLOG_FLG_1BYTE;
+ } else {
+ be32enc(f->ff->recbuf + f->ff->recsize - 4,
+ f->ff->zs->avail_out);
+ f->ff->recbuf[4] |= FIFOLOG_FLG_4BYTE;
+ }
+ i = pwrite(f->ff->fd, f->ff->recbuf, f->ff->recsize,
+ (f->recno + 1) * f->ff->recsize);
+ assert (i == (int)f->ff->recsize);
+ if (++f->recno == f->ff->logsize)
+ f->recno = 0;
+ f->cnt[FIFOLOG_PT_WRITES]++;
+ time(&t);
+ f->cnt[FIFOLOG_PT_RUNTIME] = t - f->starttime; /*lint !e776 */
+ fifolog_write_assert(f);
+}
+
+int
+fifolog_write_poll(struct fifolog_writer *f, time_t now)
+{
+ int i, fl, bo, bf;
+
+ if (now == 0)
+ time(&now);
+
+ fifolog_write_assert(f);
+ if (f->cleanup || now >= (int)(f->lastsync + f->syncrate)) {
+ /*
+ * We always check the sync timer, otherwise a flood of data
+ * would not get any sync records at all
+ */
+ f->cleanup = 0;
+ fl = Z_FINISH;
+ f->lastsync = now;
+ f->lastwrite = now;
+ f->cnt[FIFOLOG_PT_SYNC]++;
+ } else if (f->ff->zs->avail_in == 0 &&
+ now >= (int)(f->lastwrite + f->writerate)) {
+ /*
+ * We only check for writerate timeouts when the input
+ * buffer is empty. It would be silly to force a write if
+ * pending input could cause it to happen on its own.
+ */
+ fl = Z_SYNC_FLUSH;
+ f->lastwrite = now;
+ f->cnt[FIFOLOG_PT_FLUSH]++;
+ } else if (f->ff->zs->avail_in == 0)
+ return (0); /* nothing to do */
+ else
+ fl = Z_NO_FLUSH;
+
+ for (;;) {
+ assert(f->ff->zs->avail_out > 0);
+
+ bf = f->ff->zs->avail_out;
+
+ i = deflate(f->ff->zs, fl);
+ assert (i == Z_OK || i == Z_BUF_ERROR || i == Z_STREAM_END);
+
+ bo = f->ff->zs->avail_out;
+
+ /* If we have output space and not in a hurry.. */
+ if (bo > 0 && fl == Z_NO_FLUSH)
+ break;
+
+ /* Write output buffer, if anything in it */
+ if (bo != bf)
+ fifo_writerec(f);
+
+ /* If the buffer were full, we need to check again */
+ if (bo == 0) {
+ fifo_prepobuf(f, now, 0);
+ continue;
+ }
+
+ if (fl == Z_FINISH) {
+ /* Make next record a SYNC record */
+ fifo_prepobuf(f, now, 1);
+ /* And reset the zlib engine */
+ i = deflateReset(f->ff->zs);
+ assert(i == Z_OK);
+ f->writes_since_sync = 0;
+ } else {
+ fifo_prepobuf(f, now, 0);
+ }
+ break;
+ }
+
+ if (f->ff->zs->avail_in == 0) {
+ /* Reset input buffer when empty */
+ f->iptr = f->ibuf;
+ f->ff->zs->next_in = f->iptr;
+ }
+
+ fifolog_write_assert(f);
+ return (1);
+}
+
+static void
+fifolog_acct(struct fifolog_writer *f, unsigned bytes)
+{
+
+ f->ff->zs->avail_in += bytes;
+ f->iptr += bytes;
+ f->cnt[FIFOLOG_PT_BYTES_PRE] += bytes;
+}
+
+/*
+ * Attempt to write an entry.
+ * Return zero if there is no space, one otherwise
+ */
+
+int
+fifolog_write_bytes(struct fifolog_writer *f, uint32_t id, time_t now, const void *ptr, unsigned len)
+{
+ u_int l;
+ const unsigned char *p;
+
+ fifolog_write_assert(f);
+ assert(!(id & (FIFOLOG_TIMESTAMP|FIFOLOG_LENGTH)));
+ assert(ptr != NULL);
+
+ p = ptr;
+ if (len == 0) {
+ len = strlen(ptr) + 1;
+ l = 4 + len; /* id */
+ } else {
+ assert(len <= 255);
+ id |= FIFOLOG_LENGTH;
+ l = 5 + len; /* id + len */
+ }
+
+ l += 4; /* A timestamp may be necessary */
+
+ /* Now do timestamp, if needed */
+ if (now == 0)
+ time(&now);
+
+ assert(l < f->ibufsize);
+
+ /* Return if there is not enough space */
+ if (f->iptr + l > f->ibuf + f->ibufsize)
+ return (0);
+
+ if (now != f->last) {
+ id |= FIFOLOG_TIMESTAMP;
+ f->last = now;
+ }
+
+ /* Emit instance+flag and length */
+ be32enc(f->iptr, id);
+ fifolog_acct(f, 4);
+
+ if (id & FIFOLOG_TIMESTAMP) {
+ be32enc(f->iptr, (uint32_t)f->last);
+ fifolog_acct(f, 4);
+ }
+ if (id & FIFOLOG_LENGTH) {
+ f->iptr[0] = (u_char)len;
+ fifolog_acct(f, 1);
+ }
+
+ assert (len > 0);
+ memcpy(f->iptr, p, len);
+ fifolog_acct(f, len);
+ fifolog_write_assert(f);
+ return (1);
+}
+
+/*
+ * Write an entry, polling until success.
+ * Long binary entries are broken into 255 byte chunks.
+ */
+
+void
+fifolog_write_bytes_poll(struct fifolog_writer *f, uint32_t id, time_t now, const void *ptr, unsigned len)
+{
+ u_int l;
+ const unsigned char *p;
+
+ fifolog_write_assert(f);
+
+ assert(!(id & (FIFOLOG_TIMESTAMP|FIFOLOG_LENGTH)));
+ assert(ptr != NULL);
+
+ if (len == 0) {
+ while (!fifolog_write_bytes(f, id, now, ptr, len)) {
+ (void)fifolog_write_poll(f, now);
+ (void)usleep(10000);
+ }
+ } else {
+ p = ptr;
+ for (p = ptr; len > 0; len -= l, p += l) {
+ l = len;
+ if (l > 255)
+ l = 255;
+ while (!fifolog_write_bytes(f, id, now, p, l)) {
+ (void)fifolog_write_poll(f, now);
+ (void)usleep(10000);
+ }
+ }
+ }
+ fifolog_write_assert(f);
+}
+
+int
+fifolog_write_flush(struct fifolog_writer *f)
+{
+ int i;
+
+ fifolog_write_assert(f);
+
+ f->cleanup = 1;
+ for (i = 0; fifolog_write_poll(f, 0); i = 1)
+ continue;
+ fifolog_write_assert(f);
+ return (i);
+}
diff --git a/usr.sbin/fifolog/lib/getdate.y b/usr.sbin/fifolog/lib/getdate.y
new file mode 100644
index 0000000..93b5b6b
--- /dev/null
+++ b/usr.sbin/fifolog/lib/getdate.y
@@ -0,0 +1,889 @@
+%{
+/*
+** Originally written by Steven M. Bellovin <smb@research.att.com> while
+** at the University of North Carolina at Chapel Hill. Later tweaked by
+** a couple of people on Usenet. Completely overhauled by Rich $alz
+** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990;
+**
+** This grammar has 10 shift/reduce conflicts.
+**
+** This code is in the public domain and has no copyright.
+**
+** Picked up from CVS and slightly cleaned up by to WARNS=5 level by
+** Poul-Henning Kamp <phk@FreeBSD.org>
+**
+** $FreeBSD$
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include "libfifolog.h"
+
+#define yyparse getdate_yyparse
+#define yylex getdate_yylex
+#define yyerror getdate_yyerror
+
+static int yyparse(void);
+static int yylex(void);
+static int yyerror(const char *);
+
+#define EPOCH 1970
+#define HOUR(x) ((time_t)(x) * 60)
+#define SECSPERDAY (24L * 60L * 60L)
+
+
+/*
+** An entry in the lexical lookup table.
+*/
+typedef struct _TABLE {
+ const char *name;
+ int type;
+ time_t value;
+} TABLE;
+
+
+/*
+** Daylight-savings mode: on, off, or not yet known.
+*/
+typedef enum _DSTMODE {
+ DSToff, DSTon, DSTmaybe
+} DSTMODE;
+
+/*
+** Meridian: am, pm, or 24-hour style.
+*/
+typedef enum _MERIDIAN {
+ MERam, MERpm, MER24
+} MERIDIAN;
+
+
+/*
+** Global variables. We could get rid of most of these by using a good
+** union as the yacc stack. (This routine was originally written before
+** yacc had the %union construct.) Maybe someday; right now we only use
+** the %union very rarely.
+*/
+static char *yyInput;
+static DSTMODE yyDSTmode;
+static time_t yyDayOrdinal;
+static time_t yyDayNumber;
+static int yyHaveDate;
+static int yyHaveDay;
+static int yyHaveRel;
+static int yyHaveTime;
+static int yyHaveZone;
+static time_t yyTimezone;
+static time_t yyDay;
+static time_t yyHour;
+static time_t yyMinutes;
+static time_t yyMonth;
+static time_t yySeconds;
+static time_t yyYear;
+static MERIDIAN yyMeridian;
+static time_t yyRelMonth;
+static time_t yyRelSeconds;
+
+%}
+
+%union {
+ time_t Number;
+ enum _MERIDIAN Meridian;
+}
+
+%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT
+%token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST
+
+%type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT
+%type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE
+%type <Meridian> tMERIDIAN o_merid
+
+%%
+
+spec : /* NULL */
+ | spec item
+ ;
+
+item : time {
+ yyHaveTime++;
+ }
+ | zone {
+ yyHaveZone++;
+ }
+ | date {
+ yyHaveDate++;
+ }
+ | day {
+ yyHaveDay++;
+ }
+ | rel {
+ yyHaveRel++;
+ }
+ | cvsstamp {
+ yyHaveTime++;
+ yyHaveDate++;
+ yyHaveZone++;
+ }
+ | number
+ ;
+
+cvsstamp: tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER '.' tUNUMBER {
+ yyYear = $1;
+ if (yyYear < 100) yyYear += 1900;
+ yyMonth = $3;
+ yyDay = $5;
+ yyHour = $7;
+ yyMinutes = $9;
+ yySeconds = $11;
+ yyDSTmode = DSToff;
+ yyTimezone = 0;
+ }
+ ;
+
+time : tUNUMBER tMERIDIAN {
+ yyHour = $1;
+ yyMinutes = 0;
+ yySeconds = 0;
+ yyMeridian = $2;
+ }
+ | tUNUMBER ':' tUNUMBER o_merid {
+ yyHour = $1;
+ yyMinutes = $3;
+ yySeconds = 0;
+ yyMeridian = $4;
+ }
+ | tUNUMBER ':' tUNUMBER tSNUMBER {
+ yyHour = $1;
+ yyMinutes = $3;
+ yyMeridian = MER24;
+ yyDSTmode = DSToff;
+ yyTimezone = - ($4 % 100 + ($4 / 100) * 60);
+ }
+ | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid {
+ yyHour = $1;
+ yyMinutes = $3;
+ yySeconds = $5;
+ yyMeridian = $6;
+ }
+ | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER {
+ yyHour = $1;
+ yyMinutes = $3;
+ yySeconds = $5;
+ yyMeridian = MER24;
+ yyDSTmode = DSToff;
+ yyTimezone = - ($6 % 100 + ($6 / 100) * 60);
+ }
+ ;
+
+zone : tZONE {
+ yyTimezone = $1;
+ yyDSTmode = DSToff;
+ }
+ | tDAYZONE {
+ yyTimezone = $1;
+ yyDSTmode = DSTon;
+ }
+ |
+ tZONE tDST {
+ yyTimezone = $1;
+ yyDSTmode = DSTon;
+ }
+ ;
+
+day : tDAY {
+ yyDayOrdinal = 1;
+ yyDayNumber = $1;
+ }
+ | tDAY ',' {
+ yyDayOrdinal = 1;
+ yyDayNumber = $1;
+ }
+ | tUNUMBER tDAY {
+ yyDayOrdinal = $1;
+ yyDayNumber = $2;
+ }
+ ;
+
+date : tUNUMBER '/' tUNUMBER {
+ yyMonth = $1;
+ yyDay = $3;
+ }
+ | tUNUMBER '/' tUNUMBER '/' tUNUMBER {
+ if ($1 >= 100) {
+ yyYear = $1;
+ yyMonth = $3;
+ yyDay = $5;
+ } else {
+ yyMonth = $1;
+ yyDay = $3;
+ yyYear = $5;
+ }
+ }
+ | tUNUMBER tSNUMBER tSNUMBER {
+ /* ISO 8601 format. yyyy-mm-dd. */
+ yyYear = $1;
+ yyMonth = -$2;
+ yyDay = -$3;
+ }
+ | tUNUMBER tMONTH tSNUMBER {
+ /* e.g. 17-JUN-1992. */
+ yyDay = $1;
+ yyMonth = $2;
+ yyYear = -$3;
+ }
+ | tMONTH tUNUMBER {
+ yyMonth = $1;
+ yyDay = $2;
+ }
+ | tMONTH tUNUMBER ',' tUNUMBER {
+ yyMonth = $1;
+ yyDay = $2;
+ yyYear = $4;
+ }
+ | tUNUMBER tMONTH {
+ yyMonth = $2;
+ yyDay = $1;
+ }
+ | tUNUMBER tMONTH tUNUMBER {
+ yyMonth = $2;
+ yyDay = $1;
+ yyYear = $3;
+ }
+ ;
+
+rel : relunit tAGO {
+ yyRelSeconds = -yyRelSeconds;
+ yyRelMonth = -yyRelMonth;
+ }
+ | relunit
+ ;
+
+relunit : tUNUMBER tMINUTE_UNIT {
+ yyRelSeconds += $1 * $2 * 60L;
+ }
+ | tSNUMBER tMINUTE_UNIT {
+ yyRelSeconds += $1 * $2 * 60L;
+ }
+ | tMINUTE_UNIT {
+ yyRelSeconds += $1 * 60L;
+ }
+ | tSNUMBER tSEC_UNIT {
+ yyRelSeconds += $1;
+ }
+ | tUNUMBER tSEC_UNIT {
+ yyRelSeconds += $1;
+ }
+ | tSEC_UNIT {
+ yyRelSeconds++;
+ }
+ | tSNUMBER tMONTH_UNIT {
+ yyRelMonth += $1 * $2;
+ }
+ | tUNUMBER tMONTH_UNIT {
+ yyRelMonth += $1 * $2;
+ }
+ | tMONTH_UNIT {
+ yyRelMonth += $1;
+ }
+ ;
+
+number : tUNUMBER {
+ if (yyHaveTime && yyHaveDate && !yyHaveRel)
+ yyYear = $1;
+ else {
+ if($1>10000) {
+ yyHaveDate++;
+ yyDay= ($1)%100;
+ yyMonth= ($1/100)%100;
+ yyYear = $1/10000;
+ }
+ else {
+ yyHaveTime++;
+ if ($1 < 100) {
+ yyHour = $1;
+ yyMinutes = 0;
+ }
+ else {
+ yyHour = $1 / 100;
+ yyMinutes = $1 % 100;
+ }
+ yySeconds = 0;
+ yyMeridian = MER24;
+ }
+ }
+ }
+ ;
+
+o_merid : /* NULL */ {
+ $$ = MER24;
+ }
+ | tMERIDIAN {
+ $$ = $1;
+ }
+ ;
+
+%%
+
+/* Month and day table. */
+static TABLE const MonthDayTable[] = {
+ { "january", tMONTH, 1 },
+ { "february", tMONTH, 2 },
+ { "march", tMONTH, 3 },
+ { "april", tMONTH, 4 },
+ { "may", tMONTH, 5 },
+ { "june", tMONTH, 6 },
+ { "july", tMONTH, 7 },
+ { "august", tMONTH, 8 },
+ { "september", tMONTH, 9 },
+ { "sept", tMONTH, 9 },
+ { "october", tMONTH, 10 },
+ { "november", tMONTH, 11 },
+ { "december", tMONTH, 12 },
+ { "sunday", tDAY, 0 },
+ { "monday", tDAY, 1 },
+ { "tuesday", tDAY, 2 },
+ { "tues", tDAY, 2 },
+ { "wednesday", tDAY, 3 },
+ { "wednes", tDAY, 3 },
+ { "thursday", tDAY, 4 },
+ { "thur", tDAY, 4 },
+ { "thurs", tDAY, 4 },
+ { "friday", tDAY, 5 },
+ { "saturday", tDAY, 6 },
+ { NULL, 0, 0 }
+};
+
+/* Time units table. */
+static TABLE const UnitsTable[] = {
+ { "year", tMONTH_UNIT, 12 },
+ { "month", tMONTH_UNIT, 1 },
+ { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 },
+ { "week", tMINUTE_UNIT, 7 * 24 * 60 },
+ { "day", tMINUTE_UNIT, 1 * 24 * 60 },
+ { "hour", tMINUTE_UNIT, 60 },
+ { "minute", tMINUTE_UNIT, 1 },
+ { "min", tMINUTE_UNIT, 1 },
+ { "second", tSEC_UNIT, 1 },
+ { "sec", tSEC_UNIT, 1 },
+ { NULL, 0, 0 }
+};
+
+/* Assorted relative-time words. */
+static TABLE const OtherTable[] = {
+ { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 },
+ { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 },
+ { "today", tMINUTE_UNIT, 0 },
+ { "now", tMINUTE_UNIT, 0 },
+ { "last", tUNUMBER, -1 },
+ { "this", tMINUTE_UNIT, 0 },
+ { "next", tUNUMBER, 2 },
+ { "first", tUNUMBER, 1 },
+/* { "second", tUNUMBER, 2 }, */
+ { "third", tUNUMBER, 3 },
+ { "fourth", tUNUMBER, 4 },
+ { "fifth", tUNUMBER, 5 },
+ { "sixth", tUNUMBER, 6 },
+ { "seventh", tUNUMBER, 7 },
+ { "eighth", tUNUMBER, 8 },
+ { "ninth", tUNUMBER, 9 },
+ { "tenth", tUNUMBER, 10 },
+ { "eleventh", tUNUMBER, 11 },
+ { "twelfth", tUNUMBER, 12 },
+ { "ago", tAGO, 1 },
+ { NULL, 0, 0 }
+};
+
+/* The timezone table. */
+/* Some of these are commented out because a time_t can't store a float. */
+static TABLE const TimezoneTable[] = {
+ { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */
+ { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */
+ { "utc", tZONE, HOUR( 0) },
+ { "wet", tZONE, HOUR( 0) }, /* Western European */
+ { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */
+ { "wat", tZONE, HOUR( 1) }, /* West Africa */
+ { "at", tZONE, HOUR( 2) }, /* Azores */
+#if 0
+ /* For completeness. BST is also British Summer, and GST is
+ * also Guam Standard. */
+ { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */
+ { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */
+#endif
+#if 0
+ { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */
+ { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */
+ { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */
+#endif
+ { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */
+ { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */
+ { "est", tZONE, HOUR( 5) }, /* Eastern Standard */
+ { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */
+ { "cst", tZONE, HOUR( 6) }, /* Central Standard */
+ { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */
+ { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */
+ { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */
+ { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */
+ { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */
+ { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */
+ { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */
+ { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */
+ { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */
+ { "cat", tZONE, HOUR(10) }, /* Central Alaska */
+ { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */
+ { "nt", tZONE, HOUR(11) }, /* Nome */
+ { "idlw", tZONE, HOUR(12) }, /* International Date Line West */
+ { "cet", tZONE, -HOUR(1) }, /* Central European */
+ { "met", tZONE, -HOUR(1) }, /* Middle European */
+ { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */
+ { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */
+ { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */
+ { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */
+ { "fwt", tZONE, -HOUR(1) }, /* French Winter */
+ { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */
+ { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */
+ { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */
+#if 0
+ { "it", tZONE, -HOUR(3.5) },/* Iran */
+#endif
+ { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */
+ { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */
+#if 0
+ { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */
+#endif
+ { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */
+#if 0
+ /* For completeness. NST is also Newfoundland Stanard, and SST is
+ * also Swedish Summer. */
+ { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */
+ { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */
+#endif /* 0 */
+ { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */
+ { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */
+#if 0
+ { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */
+#endif
+ { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */
+ { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */
+#if 0
+ { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */
+ { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */
+#endif
+ { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */
+ { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */
+ { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */
+ { "nzt", tZONE, -HOUR(12) }, /* New Zealand */
+ { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */
+ { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */
+ { "idle", tZONE, -HOUR(12) }, /* International Date Line East */
+ { NULL, 0, 0 }
+};
+
+/* Military timezone table. */
+static TABLE const MilitaryTable[] = {
+ { "a", tZONE, HOUR( 1) },
+ { "b", tZONE, HOUR( 2) },
+ { "c", tZONE, HOUR( 3) },
+ { "d", tZONE, HOUR( 4) },
+ { "e", tZONE, HOUR( 5) },
+ { "f", tZONE, HOUR( 6) },
+ { "g", tZONE, HOUR( 7) },
+ { "h", tZONE, HOUR( 8) },
+ { "i", tZONE, HOUR( 9) },
+ { "k", tZONE, HOUR( 10) },
+ { "l", tZONE, HOUR( 11) },
+ { "m", tZONE, HOUR( 12) },
+ { "n", tZONE, HOUR(- 1) },
+ { "o", tZONE, HOUR(- 2) },
+ { "p", tZONE, HOUR(- 3) },
+ { "q", tZONE, HOUR(- 4) },
+ { "r", tZONE, HOUR(- 5) },
+ { "s", tZONE, HOUR(- 6) },
+ { "t", tZONE, HOUR(- 7) },
+ { "u", tZONE, HOUR(- 8) },
+ { "v", tZONE, HOUR(- 9) },
+ { "w", tZONE, HOUR(-10) },
+ { "x", tZONE, HOUR(-11) },
+ { "y", tZONE, HOUR(-12) },
+ { "z", tZONE, HOUR( 0) },
+ { NULL, 0, 0 }
+};
+
+
+
+
+/* ARGSUSED */
+static int
+yyerror(const char *s __unused)
+{
+ return 0;
+}
+
+
+static time_t
+ToSeconds(time_t Hours, time_t Minutes, time_t Seconds, MERIDIAN Meridian)
+{
+ if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59)
+ return -1;
+ switch (Meridian) {
+ case MER24:
+ if (Hours < 0 || Hours > 23)
+ return -1;
+ return (Hours * 60L + Minutes) * 60L + Seconds;
+ case MERam:
+ if (Hours < 1 || Hours > 12)
+ return -1;
+ if (Hours == 12)
+ Hours = 0;
+ return (Hours * 60L + Minutes) * 60L + Seconds;
+ case MERpm:
+ if (Hours < 1 || Hours > 12)
+ return -1;
+ if (Hours == 12)
+ Hours = 0;
+ return ((Hours + 12) * 60L + Minutes) * 60L + Seconds;
+ default:
+ abort ();
+ }
+ /* NOTREACHED */
+}
+
+
+/* Year is either
+ * A negative number, which means to use its absolute value (why?)
+ * A number from 0 to 99, which means a year from 1900 to 1999, or
+ * The actual year (>=100). */
+static time_t
+Convert(time_t Month, time_t Day, time_t Year,
+ time_t Hours, time_t Minutes, time_t Seconds,
+ MERIDIAN Meridian, DSTMODE DSTmode)
+{
+ static int DaysInMonth[12] = {
+ 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ };
+ time_t tod;
+ time_t Julian;
+ int i;
+ struct tm *ltm;
+
+ if (Year < 0)
+ Year = -Year;
+ if (Year < 69)
+ Year += 2000;
+ else if (Year < 100)
+ Year += 1900;
+ DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0)
+ ? 29 : 28;
+ /* Checking for 2038 bogusly assumes that time_t is 32 bits. But
+ I'm too lazy to try to check for time_t overflow in another way. */
+ if (Year < EPOCH || Year > 2038
+ || Month < 1 || Month > 12
+ /* Lint fluff: "conversion from long may lose accuracy" */
+ || Day < 1 || Day > DaysInMonth[(int)--Month])
+ /* FIXME:
+ * It would be nice to set a global error string here.
+ * "February 30 is not a valid date" is much more informative than
+ * "Can't parse date/time: 100 months" when the user input was
+ * "100 months" and addition resolved that to February 30, for
+ * example. See rcs2-7 in src/sanity.sh for more. */
+ return -1;
+
+ for (Julian = Day - 1, i = 0; i < Month; i++)
+ Julian += DaysInMonth[i];
+ for (i = EPOCH; i < Year; i++)
+ Julian += 365 + (i % 4 == 0);
+ Julian *= SECSPERDAY;
+ Julian += yyTimezone * 60L;
+ if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0)
+ return -1;
+ Julian += tod;
+ ltm = localtime(&Julian);
+fprintf(stderr, "DST %d TZ %s %d\n", DSTmode, ltm->tm_zone, ltm->tm_isdst);
+ if (DSTmode == DSTon
+ || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst))
+ Julian -= 60 * 60;
+ return Julian;
+}
+
+
+static time_t
+DSTcorrect(time_t Start, time_t Future)
+{
+ time_t StartDay;
+ time_t FutureDay;
+
+ StartDay = (localtime(&Start)->tm_hour + 1) % 24;
+ FutureDay = (localtime(&Future)->tm_hour + 1) % 24;
+ return (Future - Start) + (StartDay - FutureDay) * 60L * 60L;
+}
+
+
+static time_t
+RelativeDate(time_t Start, time_t DayOrdinal, time_t DayNumber)
+{
+ struct tm *tm;
+ time_t now;
+
+ now = Start;
+ tm = localtime(&now);
+ now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7);
+ now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1);
+ return DSTcorrect(Start, now);
+}
+
+
+static time_t
+RelativeMonth(time_t Start, time_t RelMonth)
+{
+ struct tm *tm;
+ time_t Month;
+ time_t Year;
+
+ if (RelMonth == 0)
+ return 0;
+ tm = localtime(&Start);
+ Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth;
+ Year = Month / 12;
+ Month = Month % 12 + 1;
+ return DSTcorrect(Start,
+ Convert(Month, (time_t)tm->tm_mday, Year,
+ (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec,
+ MER24, DSTmaybe));
+}
+
+
+static int
+LookupWord(char *buff)
+{
+ char *p;
+ char *q;
+ const TABLE *tp;
+ int i;
+ int abbrev;
+
+ /* Make it lowercase. */
+ for (p = buff; *p; p++)
+ if (isupper(*p))
+ *p = tolower(*p);
+
+ if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) {
+ yylval.Meridian = MERam;
+ return tMERIDIAN;
+ }
+ if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) {
+ yylval.Meridian = MERpm;
+ return tMERIDIAN;
+ }
+
+ /* See if we have an abbreviation for a month. */
+ if (strlen(buff) == 3)
+ abbrev = 1;
+ else if (strlen(buff) == 4 && buff[3] == '.') {
+ abbrev = 1;
+ buff[3] = '\0';
+ }
+ else
+ abbrev = 0;
+
+ for (tp = MonthDayTable; tp->name; tp++) {
+ if (abbrev) {
+ if (strncmp(buff, tp->name, 3) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ }
+ else if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ }
+
+ for (tp = TimezoneTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ if (strcmp(buff, "dst") == 0)
+ return tDST;
+
+ for (tp = UnitsTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ /* Strip off any plural and try the units table again. */
+ i = strlen(buff) - 1;
+ if (buff[i] == 's') {
+ buff[i] = '\0';
+ for (tp = UnitsTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ buff[i] = 's'; /* Put back for "this" in OtherTable. */
+ }
+
+ for (tp = OtherTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ /* Military timezones. */
+ if (buff[1] == '\0' && isalpha(*buff)) {
+ for (tp = MilitaryTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+ }
+
+ /* Drop out any periods and try the timezone table again. */
+ for (i = 0, p = q = buff; *q; q++)
+ if (*q != '.')
+ *p++ = *q;
+ else
+ i++;
+ *p = '\0';
+ if (i)
+ for (tp = TimezoneTable; tp->name; tp++)
+ if (strcmp(buff, tp->name) == 0) {
+ yylval.Number = tp->value;
+ return tp->type;
+ }
+
+ return tID;
+}
+
+
+static int
+yylex()
+{
+ char c;
+ char *p;
+ char buff[20];
+ int Count;
+ int sign;
+
+ for ( ; ; ) {
+ while (isspace(*yyInput))
+ yyInput++;
+
+ if (isdigit(c = *yyInput) || c == '-' || c == '+') {
+ if (c == '-' || c == '+') {
+ sign = c == '-' ? -1 : 1;
+ if (!isdigit(*++yyInput))
+ /* skip the '-' sign */
+ continue;
+ }
+ else
+ sign = 0;
+ for (yylval.Number = 0; isdigit(c = *yyInput++); )
+ yylval.Number = 10 * yylval.Number + c - '0';
+ yyInput--;
+ if (sign < 0)
+ yylval.Number = -yylval.Number;
+ return sign ? tSNUMBER : tUNUMBER;
+ }
+ if (isalpha(c)) {
+ for (p = buff; isalpha(c = *yyInput++) || c == '.'; )
+ if (p < &buff[sizeof buff - 1])
+ *p++ = c;
+ *p = '\0';
+ yyInput--;
+ return LookupWord(buff);
+ }
+ if (c != '(')
+ return *yyInput++;
+ Count = 0;
+ do {
+ c = *yyInput++;
+ if (c == '\0')
+ return c;
+ if (c == '(')
+ Count++;
+ else if (c == ')')
+ Count--;
+ } while (Count > 0);
+ }
+}
+
+#define TM_YEAR_ORIGIN 1900
+
+time_t
+get_date(char *p)
+{
+ struct tm *tm, gmt;
+ time_t Start;
+ time_t tod;
+ time_t nowtime;
+ struct tm *gmt_ptr;
+
+ yyInput = p;
+
+ (void)time (&nowtime);
+
+ gmt_ptr = gmtime (&nowtime);
+ if (gmt_ptr != NULL)
+ {
+ /* Make a copy, in case localtime modifies *tm (I think
+ that comment now applies to *gmt_ptr, but I am too
+ lazy to dig into how gmtime and locatime allocate the
+ structures they return pointers to). */
+ gmt = *gmt_ptr;
+ }
+
+ if (! (tm = localtime (&nowtime)))
+ return -1;
+
+ tm = localtime(&nowtime);
+ yyYear = tm->tm_year + 1900;
+ yyMonth = tm->tm_mon + 1;
+ yyDay = tm->tm_mday;
+ yyTimezone = tm->tm_gmtoff;
+ yyDSTmode = DSTmaybe;
+ yyHour = 0;
+ yyMinutes = 0;
+ yySeconds = 0;
+ yyMeridian = MER24;
+ yyRelSeconds = 0;
+ yyRelMonth = 0;
+ yyHaveDate = 0;
+ yyHaveDay = 0;
+ yyHaveRel = 0;
+ yyHaveTime = 0;
+ yyHaveZone = 0;
+
+ if (yyparse()
+ || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1)
+ return -1;
+
+ if (yyHaveDate || yyHaveTime || yyHaveDay) {
+ Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds,
+ yyMeridian, yyDSTmode);
+ if (Start < 0)
+ return -1;
+ }
+ else {
+ Start = nowtime;
+ if (!yyHaveRel)
+ Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec;
+ }
+
+ Start += yyRelSeconds;
+ Start += RelativeMonth(Start, yyRelMonth);
+
+ if (yyHaveDay && !yyHaveDate) {
+ tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber);
+ Start += tod;
+ }
+
+ /* Have to do *something* with a legitimate -1 so it's distinguishable
+ * from the error return value. (Alternately could set errno on error.) */
+ return Start == -1 ? 0 : Start;
+}
diff --git a/usr.sbin/fifolog/lib/libfifolog.h b/usr.sbin/fifolog/lib/libfifolog.h
new file mode 100644
index 0000000..430e72b
--- /dev/null
+++ b/usr.sbin/fifolog/lib/libfifolog.h
@@ -0,0 +1,62 @@
+/*-
+ * Copyright (c) 2005-2008 Poul-Henning Kamp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/stdint.h>
+
+/* CREATORS */
+const char *fifolog_create(const char *fn, off_t size, unsigned recsize);
+
+
+/* WRITERS */
+
+struct fifolog_writer;
+struct fifolog_writer *fifolog_write_new(void);
+const char *fifolog_write_open(struct fifolog_writer *f, const char *fn, unsigned writerate, unsigned syncrate, int compression);
+int fifolog_write_bytes(struct fifolog_writer *f, uint32_t id, time_t now, const void *ptr, unsigned len);
+void fifolog_write_bytes_poll(struct fifolog_writer *f, uint32_t id, time_t now, const void *ptr, unsigned len);
+int fifolog_write_poll(struct fifolog_writer *f, time_t now);
+void fifolog_write_close(struct fifolog_writer *f);
+void fifolog_write_destroy(struct fifolog_writer *f);
+int fifolog_write_flush(struct fifolog_writer *f);
+extern const char *fifolog_write_statnames[];
+
+/* READERS */
+
+typedef void fifolog_reader_render_t(void *priv, time_t when, unsigned flag, const unsigned char *p, unsigned l);
+struct fifolog_reader;
+struct fifolog_reader *fifolog_reader_open(const char *fname);
+off_t fifolog_reader_seek(const struct fifolog_reader *fl, time_t t0);
+void fifolog_reader_process(struct fifolog_reader *fl, off_t from, fifolog_reader_render_t *func, void *priv, time_t end);
+
+/* UTILS */
+time_t get_date(char *p);
+
+#if (__FreeBSD__ < 7)
+int expand_number(char *_buf, int64_t *_num);
+#endif
+
diff --git a/usr.sbin/fifolog/lib/libfifolog_int.h b/usr.sbin/fifolog/lib/libfifolog_int.h
new file mode 100644
index 0000000..54ab897
--- /dev/null
+++ b/usr.sbin/fifolog/lib/libfifolog_int.h
@@ -0,0 +1,45 @@
+/*-
+ * Copyright (c) 2005-2008
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+struct fifolog_file {
+ unsigned magic;
+#define FIFOLOG_FILE_MAGIC 0x307ea50d
+
+ unsigned recsize;
+ off_t logsize;
+ int fd;
+
+ z_stream *zs;
+
+ unsigned char *recbuf;
+};
+
+const char *fifolog_int_open(struct fifolog_file **ff, const char *fname, int mode);
+void fifolog_int_close(struct fifolog_file **ff);
+int fifolog_int_read(const struct fifolog_file *ff, off_t recno);
+const char *fifolog_int_findend(const struct fifolog_file *ff, off_t *last);
diff --git a/usr.sbin/fifolog/lib/miniobj.h b/usr.sbin/fifolog/lib/miniobj.h
new file mode 100644
index 0000000..a805316
--- /dev/null
+++ b/usr.sbin/fifolog/lib/miniobj.h
@@ -0,0 +1,66 @@
+/*-
+ * Copyright (c) 2005-2008 Poul-Henning Kamp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define ALLOC_OBJ(to, type_magic) \
+ do { \
+ (to) = calloc(sizeof *(to), 1); \
+ assert((to) != NULL); \
+ (to)->magic = (type_magic); \
+ } while (0)
+
+#define FREE_OBJ(to) \
+ do { \
+ (to)->magic = (0); \
+ free(to); \
+ } while (0)
+
+#define CHECK_OBJ(ptr, type_magic) \
+ do { \
+ assert((ptr)->magic == type_magic); \
+ } while (0)
+
+#define CHECK_OBJ_NOTNULL(ptr, type_magic) \
+ do { \
+ assert((ptr) != NULL); \
+ assert((ptr)->magic == type_magic); \
+ } while (0)
+
+#define CAST_OBJ(to, from, type_magic) \
+ do { \
+ (to) = (from); \
+ if ((to) != NULL) \
+ CHECK_OBJ((to), (type_magic)); \
+ } while (0)
+
+#define CAST_OBJ_NOTNULL(to, from, type_magic) \
+ do { \
+ (to) = (from); \
+ assert((to) != NULL); \
+ CHECK_OBJ((to), (type_magic)); \
+ } while (0)
+
diff --git a/usr.sbin/flowctl/Makefile b/usr.sbin/flowctl/Makefile
new file mode 100644
index 0000000..d47993b
--- /dev/null
+++ b/usr.sbin/flowctl/Makefile
@@ -0,0 +1,12 @@
+#
+# $FreeBSD$
+#
+
+PROG= flowctl
+MAN= flowctl.8
+
+WARNS?= 2
+DPADD= ${LIBNETGRAPH}
+LDADD= -lnetgraph
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/flowctl/flowctl.8 b/usr.sbin/flowctl/flowctl.8
new file mode 100644
index 0000000..e72486b
--- /dev/null
+++ b/usr.sbin/flowctl/flowctl.8
@@ -0,0 +1,84 @@
+.\" Copyright (c) 2004-2005 Gleb Smirnoff <glebius@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 March 23, 2005
+.Os
+.Dt FLOWCTL 8
+.Sh NAME
+.Nm flowctl
+.Nd
+.Xr ng_netflow 4
+control utility
+.Sh SYNOPSIS
+.Nm
+.Op Fl d Ar level
+.Ar node command
+.Sh DESCRIPTION
+The
+.Nm
+utility is intended to control the
+.Xr ng_netflow 4
+nodes.
+It has a single option:
+.Bl -tag -width ".Fl d Ar level"
+.It Fl d Ar level
+Set the
+.Xr netgraph 3
+debugging level to
+.Ar level .
+.El
+.Sh COMMANDS
+Currently,
+.Nm
+supports only one command.
+.Pp
+.Bl -tag -width ".Cm show"
+.It Cm show
+This command is the analog of the
+.Dq "show ip cache flow"
+command of a Cisco router.
+It dumps the contents of the flow cache in Cisco-like format.
+It has optional parameter
+.Cm verbose ,
+which is analog of the
+.Dq "show ip cache verbose flow"
+command.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr netgraph 3 ,
+.Xr ng_netflow 4
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility was written by
+.An Gleb Smirnoff Aq glebius@FreeBSD.org ,
+based on
+.Nm ipacctctl
+written by
+.An Roman V. Palagin Aq romanp@unshadow.net .
diff --git a/usr.sbin/flowctl/flowctl.c b/usr.sbin/flowctl/flowctl.c
new file mode 100644
index 0000000..856fe58
--- /dev/null
+++ b/usr.sbin/flowctl/flowctl.c
@@ -0,0 +1,281 @@
+/*-
+ * Copyright (c) 2004-2005 Gleb Smirnoff <glebius@FreeBSD.org>
+ * Copyright (c) 2001-2003 Roman V. Palagin <romanp@unshadow.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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.
+ *
+ * $SourceForge: flowctl.c,v 1.15 2004/08/31 20:24:58 glebius Exp $
+ */
+
+#ifndef lint
+static const char rcs_id[] =
+ "@(#) $FreeBSD$";
+#endif
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <netgraph.h>
+#include <netgraph/netflow/ng_netflow.h>
+
+#define CISCO_SH_FLOW_HEADER "SrcIf SrcIPaddress DstIf DstIPaddress Pr SrcP DstP Pkts\n"
+#define CISCO_SH_FLOW "%-13s %-15s %-13s %-15s %2u %4.4x %4.4x %6lu\n"
+
+#define CISCO_SH_VERB_FLOW_HEADER "SrcIf SrcIPaddress DstIf DstIPaddress Pr TOS Flgs Pkts\n" \
+"Port Msk AS Port Msk AS NextHop B/Pk Active\n"
+
+#define CISCO_SH_VERB_FLOW "%-14s %-15s %-14s %-15s %2u %3x %4x %6lu\n" \
+ "%4.4x /%-2u %-5u %4.4x /%-2u %-5u %-15s %9u %8u\n\n"
+
+static int flow_cache_print(struct ngnf_flows *recs);
+static int flow_cache_print_verbose(struct ngnf_flows *recs);
+static int ctl_show(int, char **);
+static void help(void);
+static void execute_command(int, char **);
+
+struct ip_ctl_cmd {
+ char *cmd_name;
+ int (*cmd_func)(int argc, char **argv);
+};
+
+struct ip_ctl_cmd cmds[] = {
+ {"show", ctl_show},
+ {NULL, NULL},
+};
+
+int cs;
+char ng_nodename[NG_PATHLEN + 1];
+
+int
+main(int argc, char **argv)
+{
+ int c;
+ char sname[NG_NODESIZ];
+ int rcvbuf = SORCVBUF_SIZE;
+ char *ng_name;
+
+ /* parse options */
+ while ((c = getopt(argc, argv, "d:")) != -1) {
+ switch (c) {
+ case 'd': /* set libnetgraph debug level. */
+ NgSetDebug(atoi(optarg));
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ ng_name = argv[0];
+ if (ng_name == NULL)
+ help();
+ argc--;
+ argv++;
+
+ snprintf(ng_nodename, sizeof(ng_nodename), "%s:", ng_name);
+
+ /* create control socket. */
+ snprintf(sname, sizeof(sname), "flowctl%i", getpid());
+
+ if (NgMkSockNode(sname, &cs, NULL) == -1)
+ err(1, "NgMkSockNode");
+
+ /* set receive buffer size */
+ if (setsockopt(cs, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(int)) == -1)
+ err(1, "setsockopt(SOL_SOCKET, SO_RCVBUF)");
+
+ /* parse and execute command */
+ execute_command(argc, argv);
+
+ close(cs);
+
+ exit(0);
+}
+
+static void
+execute_command(int argc, char **argv)
+{
+ int cindex = -1;
+ int i;
+
+ if (!argc)
+ help();
+ for (i = 0; cmds[i].cmd_name != NULL; i++)
+ if (!strncmp(argv[0], cmds[i].cmd_name, strlen(argv[0]))) {
+ if (cindex != -1)
+ errx(1, "ambiguous command: %s", argv[0]);
+ cindex = i;
+ }
+ if (cindex == -1)
+ errx(1, "bad command: %s", argv[0]);
+ argc--;
+ argv++;
+ (*cmds[cindex].cmd_func)(argc, argv);
+}
+
+static int
+ctl_show(int argc, char **argv)
+{
+ struct ng_mesg *ng_mesg;
+ struct ngnf_flows *data;
+ char path[NG_PATHLEN + 1];
+ int token, nread, last = 0;
+ int verbose = 0;
+
+ if (argc > 0 && !strncmp(argv[0], "verbose", strlen(argv[0])))
+ verbose = 1;
+
+ ng_mesg = alloca(SORCVBUF_SIZE);
+
+ if (verbose)
+ printf(CISCO_SH_VERB_FLOW_HEADER);
+ else
+ printf(CISCO_SH_FLOW_HEADER);
+
+ for (;;) {
+ /* request set of accounting records */
+ token = NgSendMsg(cs, ng_nodename, NGM_NETFLOW_COOKIE,
+ NGM_NETFLOW_SHOW, (void *)&last, sizeof(last));
+ if (token == -1)
+ err(1, "NgSendMsg(NGM_NETFLOW_SHOW)");
+
+ /* read reply */
+ nread = NgRecvMsg(cs, ng_mesg, SORCVBUF_SIZE, path);
+ if (nread == -1)
+ err(1, "NgRecvMsg() failed");
+
+ if (ng_mesg->header.token != token)
+ err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): token mismatch");
+
+ data = (struct ngnf_flows*)ng_mesg->data;
+ if ((ng_mesg->header.arglen < (sizeof(*data))) ||
+ (ng_mesg->header.arglen < (sizeof(*data) +
+ (data->nentries * sizeof(struct flow_entry_data)))))
+ err(1, "NgRecvMsg(NGM_NETFLOW_SHOW): arglen too small");
+
+ if (verbose)
+ (void )flow_cache_print_verbose(data);
+ else
+ (void )flow_cache_print(data);
+
+ if (data->last != 0)
+ last = data->last;
+ else
+ break;
+ }
+
+ return (0);
+}
+
+static int
+flow_cache_print(struct ngnf_flows *recs)
+{
+ struct flow_entry_data *fle;
+ char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN];
+ char src_if[IFNAMSIZ], dst_if[IFNAMSIZ];
+ int i;
+
+ /* quick check */
+ if (recs->nentries == 0)
+ return (0);
+
+ fle = recs->entries;
+ for (i = 0; i < recs->nentries; i++, fle++) {
+ inet_ntop(AF_INET, &fle->r.r_src, src, sizeof(src));
+ inet_ntop(AF_INET, &fle->r.r_dst, dst, sizeof(dst));
+ printf(CISCO_SH_FLOW,
+ if_indextoname(fle->fle_i_ifx, src_if),
+ src,
+ if_indextoname(fle->fle_o_ifx, dst_if),
+ dst,
+ fle->r.r_ip_p,
+ ntohs(fle->r.r_sport),
+ ntohs(fle->r.r_dport),
+ fle->packets);
+
+ }
+
+ return (i);
+}
+
+static int
+flow_cache_print_verbose(struct ngnf_flows *recs)
+{
+ struct flow_entry_data *fle;
+ char src[INET_ADDRSTRLEN], dst[INET_ADDRSTRLEN], next[INET_ADDRSTRLEN];
+ char src_if[IFNAMSIZ], dst_if[IFNAMSIZ];
+ int i;
+
+ /* quick check */
+ if (recs->nentries == 0)
+ return (0);
+
+ fle = recs->entries;
+ for (i = 0; i < recs->nentries; i++, fle++) {
+ inet_ntop(AF_INET, &fle->r.r_src, src, sizeof(src));
+ inet_ntop(AF_INET, &fle->r.r_dst, dst, sizeof(dst));
+ inet_ntop(AF_INET, &fle->next_hop, next, sizeof(next));
+ printf(CISCO_SH_VERB_FLOW,
+ if_indextoname(fle->fle_i_ifx, src_if),
+ src,
+ if_indextoname(fle->fle_o_ifx, dst_if),
+ dst,
+ fle->r.r_ip_p,
+ fle->r.r_tos,
+ fle->tcp_flags,
+ fle->packets,
+ ntohs(fle->r.r_sport),
+ fle->src_mask,
+ 0,
+ ntohs(fle->r.r_dport),
+ fle->dst_mask,
+ 0,
+ next,
+ (u_int)(fle->bytes / fle->packets),
+ 0);
+
+ }
+
+ return (i);
+}
+
+static void
+help(void)
+{
+ extern char *__progname;
+
+ fprintf(stderr, "usage: %s [-d level] nodename command\n", __progname);
+ exit (0);
+}
diff --git a/usr.sbin/freebsd-update/Makefile b/usr.sbin/freebsd-update/Makefile
new file mode 100644
index 0000000..c5d4b00
--- /dev/null
+++ b/usr.sbin/freebsd-update/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+SCRIPTS=freebsd-update.sh
+MAN= freebsd-update.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/freebsd-update/freebsd-update.8 b/usr.sbin/freebsd-update/freebsd-update.8
new file mode 100644
index 0000000..589886e
--- /dev/null
+++ b/usr.sbin/freebsd-update/freebsd-update.8
@@ -0,0 +1,173 @@
+.\"-
+.\" Copyright 2006, 2007 Colin Percival
+.\" All rights reserved
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted providing that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+.\" DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+.\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd November 11, 2007
+.Dt FREEBSD-UPDATE 8
+.Os FreeBSD
+.Sh NAME
+.Nm freebsd-update
+.Nd fetch and install binary updates to FreeBSD
+.Sh SYNOPSIS
+.Nm
+.Op Fl b Ar basedir
+.Op Fl d Ar workdir
+.Op Fl f Ar conffile
+.Op Fl k Ar KEY
+.Op Fl r Ar newrelease
+.Op Fl s Ar server
+.Op Fl t Ar address
+.Cm command ...
+.Sh DESCRIPTION
+The
+.Nm
+tool is used to fetch, install, and rollback binary
+updates to the FreeBSD base system.
+Note that updates are only available if they are being built for the
+FreeBSD release and architecture being used; in particular, the
+.Fx
+Security Team only builds updates for releases shipped in binary form
+by the
+.Fx
+Release Engineering Team, e.g.,
+.Fx
+6.1-RELEASE and
+.Fx
+6.2-RC1, but not
+.Fx
+6.2-STABLE or
+.Fx
+7.0-CURRENT.
+.Sh OPTIONS
+The following options are supported
+.Bl -tag -width "-f conffile"
+.It Fl b Ar basedir
+Operate on a system mounted at
+.Ar basedir .
+(default:
+.Pa / ,
+or as given in the configuration file.)
+.It Fl d Ar workdir
+Store working files in
+.Ar workdir .
+(default:
+.Pa /var/db/freebsd-update/ ,
+or as given in the configuration file.)
+.It Fl f Ar conffile
+Read configuration options from
+.Ar conffile .
+(default:
+.Pa /etc/freebsd-update.conf )
+.It Fl k Ar KEY
+Trust an RSA key with SHA256 of
+.Ar KEY .
+(default: read value from configuration file.)
+.It Fl r Ar newrelease
+Specify the new release to which
+.Nm
+should upgrade (upgrade command only).
+.It Fl s Ar server
+Fetch files from the specified server or server pool.
+(default: read value from configuration file.)
+.It Fl t Ar address
+Mail output of
+.Cm cron
+command, if any, to
+.Ar address .
+(default: root, or as given in the configuration file.)
+.El
+.Sh COMMANDS
+The
+.Cm command
+can be any one of the following:
+.Pp
+.Bl -tag -width "-f conffile"
+.It Cm fetch
+Based on the currently installed world and the configuration
+options set, fetch all available binary updates.
+.It Cm cron
+Sleep a random amount of time between 1 and 3600 seconds,
+then download updates as if the
+.Cm fetch
+command was used.
+If updates are downloaded, an email will be sent
+(to root or a different address if specified via the
+.Fl t
+option or in the configuration file).
+As the name suggests, this command is designed for running
+from
+.Xr cron 8 ;
+the random delay serves to minimize the probability that
+a large number of machines will simultaneously attempt to
+fetch updates.
+.It Cm upgrade
+Fetch files necessary for upgrading to a new release.
+Before using this command, make sure that you read the
+announcement and release notes for the new release in
+case there are any special steps needed for upgrading.
+.It Cm install
+Install the most recently fetched updates or upgrade.
+.It Cm rollback
+Uninstall the most recently installed updates.
+.It Cm IDS
+Compare the system against a "known good" index of the
+installed release.
+.El
+.Sh TIPS
+.Bl -bullet
+.It
+If your clock is set to local time, adding the line
+.Pp
+.Dl 0 3 * * * root /usr/sbin/freebsd-update cron
+.Pp
+to /etc/crontab will check for updates every night.
+If your clock is set to UTC, please pick a random time
+other than 3AM, to avoid overly imposing an uneven load
+on the server(s) hosting the updates.
+.It
+In spite of its name,
+.Cm
+IDS should not be relied upon as an "Intrusion Detection
+System", since it if the system has been tampered with
+it cannot be trusted to operate correctly.
+If you intend to use this command for intrusion-detection
+purposes, make sure you boot from a secure disk (e.g., a CD).
+.El
+.Sh FILES
+.Bl -tag -width "/etc/freebsd-update.conf"
+.It /etc/freebsd-update.conf
+Default location of the
+.Nm
+configuration file.
+.It /var/db/freebsd-update/
+Default location where
+.Nm
+stores temporary files and downloaded updates.
+.El
+.Sh SEE ALSO
+.Xr freebsd-update.conf 5
+.Sh AUTHORS
+.An Colin Percival Aq cperciva@FreeBSD.org
diff --git a/usr.sbin/freebsd-update/freebsd-update.sh b/usr.sbin/freebsd-update/freebsd-update.sh
new file mode 100644
index 0000000..331ef10
--- /dev/null
+++ b/usr.sbin/freebsd-update/freebsd-update.sh
@@ -0,0 +1,3030 @@
+#!/bin/sh
+
+#-
+# Copyright 2004-2007 Colin Percival
+# All rights reserved
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted providing that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+# $FreeBSD$
+
+#### Usage function -- called from command-line handling code.
+
+# Usage instructions. Options not listed:
+# --debug -- don't filter output from utilities
+# --no-stats -- don't show progress statistics while fetching files
+usage () {
+ cat <<EOF
+usage: `basename $0` [options] command ... [path]
+
+Options:
+ -b basedir -- Operate on a system mounted at basedir
+ (default: /)
+ -d workdir -- Store working files in workdir
+ (default: /var/db/freebsd-update/)
+ -f conffile -- Read configuration options from conffile
+ (default: /etc/freebsd-update.conf)
+ -k KEY -- Trust an RSA key with SHA256 hash of KEY
+ -r release -- Target for upgrade (e.g., 6.2-RELEASE)
+ -s server -- Server from which to fetch updates
+ (default: update.FreeBSD.org)
+ -t address -- Mail output of cron command, if any, to address
+ (default: root)
+Commands:
+ fetch -- Fetch updates from server
+ cron -- Sleep rand(3600) seconds, fetch updates, and send an
+ email if updates were found
+ upgrade -- Fetch upgrades to FreeBSD version specified via -r option
+ install -- Install downloaded updates or upgrades
+ rollback -- Uninstall most recently installed updates
+ IDS -- Compare the system against an index of "known good" files.
+EOF
+ exit 0
+}
+
+#### Configuration processing functions
+
+#-
+# Configuration options are set in the following order of priority:
+# 1. Command line options
+# 2. Configuration file options
+# 3. Default options
+# In addition, certain options (e.g., IgnorePaths) can be specified multiple
+# times and (as long as these are all in the same place, e.g., inside the
+# configuration file) they will accumulate. Finally, because the path to the
+# configuration file can be specified at the command line, the entire command
+# line must be processed before we start reading the configuration file.
+#
+# Sound like a mess? It is. Here's how we handle this:
+# 1. Initialize CONFFILE and all the options to "".
+# 2. Process the command line. Throw an error if a non-accumulating option
+# is specified twice.
+# 3. If CONFFILE is "", set CONFFILE to /etc/freebsd-update.conf .
+# 4. For all the configuration options X, set X_saved to X.
+# 5. Initialize all the options to "".
+# 6. Read CONFFILE line by line, parsing options.
+# 7. For each configuration option X, set X to X_saved iff X_saved is not "".
+# 8. Repeat steps 4-7, except setting options to their default values at (6).
+
+CONFIGOPTIONS="KEYPRINT WORKDIR SERVERNAME MAILTO ALLOWADD ALLOWDELETE
+ KEEPMODIFIEDMETADATA COMPONENTS IGNOREPATHS UPDATEIFUNMODIFIED
+ BASEDIR VERBOSELEVEL TARGETRELEASE STRICTCOMPONENTS MERGECHANGES
+ IDSIGNOREPATHS"
+
+# Set all the configuration options to "".
+nullconfig () {
+ for X in ${CONFIGOPTIONS}; do
+ eval ${X}=""
+ done
+}
+
+# For each configuration option X, set X_saved to X.
+saveconfig () {
+ for X in ${CONFIGOPTIONS}; do
+ eval ${X}_saved=\$${X}
+ done
+}
+
+# For each configuration option X, set X to X_saved if X_saved is not "".
+mergeconfig () {
+ for X in ${CONFIGOPTIONS}; do
+ eval _=\$${X}_saved
+ if ! [ -z "${_}" ]; then
+ eval ${X}=\$${X}_saved
+ fi
+ done
+}
+
+# Set the trusted keyprint.
+config_KeyPrint () {
+ if [ -z ${KEYPRINT} ]; then
+ KEYPRINT=$1
+ else
+ return 1
+ fi
+}
+
+# Set the working directory.
+config_WorkDir () {
+ if [ -z ${WORKDIR} ]; then
+ WORKDIR=$1
+ else
+ return 1
+ fi
+}
+
+# Set the name of the server (pool) from which to fetch updates
+config_ServerName () {
+ if [ -z ${SERVERNAME} ]; then
+ SERVERNAME=$1
+ else
+ return 1
+ fi
+}
+
+# Set the address to which 'cron' output will be mailed.
+config_MailTo () {
+ if [ -z ${MAILTO} ]; then
+ MAILTO=$1
+ else
+ return 1
+ fi
+}
+
+# Set whether FreeBSD Update is allowed to add files (or directories, or
+# symlinks) which did not previously exist.
+config_AllowAdd () {
+ if [ -z ${ALLOWADD} ]; then
+ case $1 in
+ [Yy][Ee][Ss])
+ ALLOWADD=yes
+ ;;
+ [Nn][Oo])
+ ALLOWADD=no
+ ;;
+ *)
+ return 1
+ ;;
+ esac
+ else
+ return 1
+ fi
+}
+
+# Set whether FreeBSD Update is allowed to remove files/directories/symlinks.
+config_AllowDelete () {
+ if [ -z ${ALLOWDELETE} ]; then
+ case $1 in
+ [Yy][Ee][Ss])
+ ALLOWDELETE=yes
+ ;;
+ [Nn][Oo])
+ ALLOWDELETE=no
+ ;;
+ *)
+ return 1
+ ;;
+ esac
+ else
+ return 1
+ fi
+}
+
+# Set whether FreeBSD Update should keep existing inode ownership,
+# permissions, and flags, in the event that they have been modified locally
+# after the release.
+config_KeepModifiedMetadata () {
+ if [ -z ${KEEPMODIFIEDMETADATA} ]; then
+ case $1 in
+ [Yy][Ee][Ss])
+ KEEPMODIFIEDMETADATA=yes
+ ;;
+ [Nn][Oo])
+ KEEPMODIFIEDMETADATA=no
+ ;;
+ *)
+ return 1
+ ;;
+ esac
+ else
+ return 1
+ fi
+}
+
+# Add to the list of components which should be kept updated.
+config_Components () {
+ for C in $@; do
+ COMPONENTS="${COMPONENTS} ${C}"
+ done
+}
+
+# Add to the list of paths under which updates will be ignored.
+config_IgnorePaths () {
+ for C in $@; do
+ IGNOREPATHS="${IGNOREPATHS} ${C}"
+ done
+}
+
+# Add to the list of paths which IDS should ignore.
+config_IDSIgnorePaths () {
+ for C in $@; do
+ IDSIGNOREPATHS="${IDSIGNOREPATHS} ${C}"
+ done
+}
+
+# Add to the list of paths within which updates will be performed only if the
+# file on disk has not been modified locally.
+config_UpdateIfUnmodified () {
+ for C in $@; do
+ UPDATEIFUNMODIFIED="${UPDATEIFUNMODIFIED} ${C}"
+ done
+}
+
+# Add to the list of paths within which updates to text files will be merged
+# instead of overwritten.
+config_MergeChanges () {
+ for C in $@; do
+ MERGECHANGES="${MERGECHANGES} ${C}"
+ done
+}
+
+# Work on a FreeBSD installation mounted under $1
+config_BaseDir () {
+ if [ -z ${BASEDIR} ]; then
+ BASEDIR=$1
+ else
+ return 1
+ fi
+}
+
+# When fetching upgrades, should we assume the user wants exactly the
+# components listed in COMPONENTS, rather than trying to guess based on
+# what's currently installed?
+config_StrictComponents () {
+ if [ -z ${STRICTCOMPONENTS} ]; then
+ case $1 in
+ [Yy][Ee][Ss])
+ STRICTCOMPONENTS=yes
+ ;;
+ [Nn][Oo])
+ STRICTCOMPONENTS=no
+ ;;
+ *)
+ return 1
+ ;;
+ esac
+ else
+ return 1
+ fi
+}
+
+# Upgrade to FreeBSD $1
+config_TargetRelease () {
+ if [ -z ${TARGETRELEASE} ]; then
+ TARGETRELEASE=$1
+ else
+ return 1
+ fi
+}
+
+# Define what happens to output of utilities
+config_VerboseLevel () {
+ if [ -z ${VERBOSELEVEL} ]; then
+ case $1 in
+ [Dd][Ee][Bb][Uu][Gg])
+ VERBOSELEVEL=debug
+ ;;
+ [Nn][Oo][Ss][Tt][Aa][Tt][Ss])
+ VERBOSELEVEL=nostats
+ ;;
+ [Ss][Tt][Aa][Tt][Ss])
+ VERBOSELEVEL=stats
+ ;;
+ *)
+ return 1
+ ;;
+ esac
+ else
+ return 1
+ fi
+}
+
+# Handle one line of configuration
+configline () {
+ if [ $# -eq 0 ]; then
+ return
+ fi
+
+ OPT=$1
+ shift
+ config_${OPT} $@
+}
+
+#### Parameter handling functions.
+
+# Initialize parameters to null, just in case they're
+# set in the environment.
+init_params () {
+ # Configration settings
+ nullconfig
+
+ # No configuration file set yet
+ CONFFILE=""
+
+ # No commands specified yet
+ COMMANDS=""
+}
+
+# Parse the command line
+parse_cmdline () {
+ while [ $# -gt 0 ]; do
+ case "$1" in
+ # Location of configuration file
+ -f)
+ if [ $# -eq 1 ]; then usage; fi
+ if [ ! -z "${CONFFILE}" ]; then usage; fi
+ shift; CONFFILE="$1"
+ ;;
+
+ # Configuration file equivalents
+ -b)
+ if [ $# -eq 1 ]; then usage; fi; shift
+ config_BaseDir $1 || usage
+ ;;
+ -d)
+ if [ $# -eq 1 ]; then usage; fi; shift
+ config_WorkDir $1 || usage
+ ;;
+ -k)
+ if [ $# -eq 1 ]; then usage; fi; shift
+ config_KeyPrint $1 || usage
+ ;;
+ -s)
+ if [ $# -eq 1 ]; then usage; fi; shift
+ config_ServerName $1 || usage
+ ;;
+ -r)
+ if [ $# -eq 1 ]; then usage; fi; shift
+ config_TargetRelease $1 || usage
+ ;;
+ -t)
+ if [ $# -eq 1 ]; then usage; fi; shift
+ config_MailTo $1 || usage
+ ;;
+ -v)
+ if [ $# -eq 1 ]; then usage; fi; shift
+ config_VerboseLevel $1 || usage
+ ;;
+
+ # Aliases for "-v debug" and "-v nostats"
+ --debug)
+ config_VerboseLevel debug || usage
+ ;;
+ --no-stats)
+ config_VerboseLevel nostats || usage
+ ;;
+
+ # Commands
+ cron | fetch | upgrade | install | rollback | IDS)
+ COMMANDS="${COMMANDS} $1"
+ ;;
+
+ # Anything else is an error
+ *)
+ usage
+ ;;
+ esac
+ shift
+ done
+
+ # Make sure we have at least one command
+ if [ -z "${COMMANDS}" ]; then
+ usage
+ fi
+}
+
+# Parse the configuration file
+parse_conffile () {
+ # If a configuration file was specified on the command line, check
+ # that it exists and is readable.
+ if [ ! -z "${CONFFILE}" ] && [ ! -r "${CONFFILE}" ]; then
+ echo -n "File does not exist "
+ echo -n "or is not readable: "
+ echo ${CONFFILE}
+ exit 1
+ fi
+
+ # If a configuration file was not specified on the command line,
+ # use the default configuration file path. If that default does
+ # not exist, give up looking for any configuration.
+ if [ -z "${CONFFILE}" ]; then
+ CONFFILE="/etc/freebsd-update.conf"
+ if [ ! -r "${CONFFILE}" ]; then
+ return
+ fi
+ fi
+
+ # Save the configuration options specified on the command line, and
+ # clear all the options in preparation for reading the config file.
+ saveconfig
+ nullconfig
+
+ # Read the configuration file. Anything after the first '#' is
+ # ignored, and any blank lines are ignored.
+ L=0
+ while read LINE; do
+ L=$(($L + 1))
+ LINEX=`echo "${LINE}" | cut -f 1 -d '#'`
+ if ! configline ${LINEX}; then
+ echo "Error processing configuration file, line $L:"
+ echo "==> ${LINE}"
+ exit 1
+ fi
+ done < ${CONFFILE}
+
+ # Merge the settings read from the configuration file with those
+ # provided at the command line.
+ mergeconfig
+}
+
+# Provide some default parameters
+default_params () {
+ # Save any parameters already configured, and clear the slate
+ saveconfig
+ nullconfig
+
+ # Default configurations
+ config_WorkDir /var/db/freebsd-update
+ config_MailTo root
+ config_AllowAdd yes
+ config_AllowDelete yes
+ config_KeepModifiedMetadata yes
+ config_BaseDir /
+ config_VerboseLevel stats
+ config_StrictComponents no
+
+ # Merge these defaults into the earlier-configured settings
+ mergeconfig
+}
+
+# Set utility output filtering options, based on ${VERBOSELEVEL}
+fetch_setup_verboselevel () {
+ case ${VERBOSELEVEL} in
+ debug)
+ QUIETREDIR="/dev/stderr"
+ QUIETFLAG=" "
+ STATSREDIR="/dev/stderr"
+ DDSTATS=".."
+ XARGST="-t"
+ NDEBUG=" "
+ ;;
+ nostats)
+ QUIETREDIR=""
+ QUIETFLAG=""
+ STATSREDIR="/dev/null"
+ DDSTATS=".."
+ XARGST=""
+ NDEBUG=""
+ ;;
+ stats)
+ QUIETREDIR="/dev/null"
+ QUIETFLAG="-q"
+ STATSREDIR="/dev/stdout"
+ DDSTATS=""
+ XARGST=""
+ NDEBUG="-n"
+ ;;
+ esac
+}
+
+# Perform sanity checks and set some final parameters
+# in preparation for fetching files. Figure out which
+# set of updates should be downloaded: If the user is
+# running *-p[0-9]+, strip off the last part; if the
+# user is running -SECURITY, call it -RELEASE. Chdir
+# into the working directory.
+fetch_check_params () {
+ export HTTP_USER_AGENT="freebsd-update (${COMMAND}, `uname -r`)"
+
+ _SERVERNAME_z=\
+"SERVERNAME must be given via command line or configuration file."
+ _KEYPRINT_z="Key must be given via -k option or configuration file."
+ _KEYPRINT_bad="Invalid key fingerprint: "
+ _WORKDIR_bad="Directory does not exist or is not writable: "
+
+ if [ -z "${SERVERNAME}" ]; then
+ echo -n "`basename $0`: "
+ echo "${_SERVERNAME_z}"
+ exit 1
+ fi
+ if [ -z "${KEYPRINT}" ]; then
+ echo -n "`basename $0`: "
+ echo "${_KEYPRINT_z}"
+ exit 1
+ fi
+ if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then
+ echo -n "`basename $0`: "
+ echo -n "${_KEYPRINT_bad}"
+ echo ${KEYPRINT}
+ exit 1
+ fi
+ if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
+ echo -n "`basename $0`: "
+ echo -n "${_WORKDIR_bad}"
+ echo ${WORKDIR}
+ exit 1
+ fi
+ cd ${WORKDIR} || exit 1
+
+ # Generate release number. The s/SECURITY/RELEASE/ bit exists
+ # to provide an upgrade path for FreeBSD Update 1.x users, since
+ # the kernels provided by FreeBSD Update 1.x are always labelled
+ # as X.Y-SECURITY.
+ RELNUM=`uname -r |
+ sed -E 's,-p[0-9]+,,' |
+ sed -E 's,-SECURITY,-RELEASE,'`
+ ARCH=`uname -m`
+ FETCHDIR=${RELNUM}/${ARCH}
+ PATCHDIR=${RELNUM}/${ARCH}/bp
+
+ # Figure out what directory contains the running kernel
+ BOOTFILE=`sysctl -n kern.bootfile`
+ KERNELDIR=${BOOTFILE%/kernel}
+ if ! [ -d ${KERNELDIR} ]; then
+ echo "Cannot identify running kernel"
+ exit 1
+ fi
+
+ # Figure out what kernel configuration is running. We start with
+ # the output of `uname -i`, and then make the following adjustments:
+ # 1. Replace "SMP-GENERIC" with "SMP". Why the SMP kernel config
+ # file says "ident SMP-GENERIC", I don't know...
+ # 2. If the kernel claims to be GENERIC _and_ ${ARCH} is "amd64"
+ # _and_ `sysctl kern.version` contains a line which ends "/SMP", then
+ # we're running an SMP kernel. This mis-identification is a bug
+ # which was fixed in 6.2-STABLE.
+ KERNCONF=`uname -i`
+ if [ ${KERNCONF} = "SMP-GENERIC" ]; then
+ KERNCONF=SMP
+ fi
+ if [ ${KERNCONF} = "GENERIC" ] && [ ${ARCH} = "amd64" ]; then
+ if sysctl kern.version | grep -qE '/SMP$'; then
+ KERNCONF=SMP
+ fi
+ fi
+
+ # Define some paths
+ BSPATCH=/usr/bin/bspatch
+ SHA256=/sbin/sha256
+ PHTTPGET=/usr/libexec/phttpget
+
+ # Set up variables relating to VERBOSELEVEL
+ fetch_setup_verboselevel
+
+ # Construct a unique name from ${BASEDIR}
+ BDHASH=`echo ${BASEDIR} | sha256 -q`
+}
+
+# Perform sanity checks etc. before fetching upgrades.
+upgrade_check_params () {
+ fetch_check_params
+
+ # Unless set otherwise, we're upgrading to the same kernel config.
+ NKERNCONF=${KERNCONF}
+
+ # We need TARGETRELEASE set
+ _TARGETRELEASE_z="Release target must be specified via -r option."
+ if [ -z "${TARGETRELEASE}" ]; then
+ echo -n "`basename $0`: "
+ echo "${_TARGETRELEASE_z}"
+ exit 1
+ fi
+
+ # The target release should be != the current release.
+ if [ "${TARGETRELEASE}" = "${RELNUM}" ]; then
+ echo -n "`basename $0`: "
+ echo "Cannot upgrade from ${RELNUM} to itself"
+ exit 1
+ fi
+
+ # Turning off AllowAdd or AllowDelete is a bad idea for upgrades.
+ if [ "${ALLOWADD}" = "no" ]; then
+ echo -n "`basename $0`: "
+ echo -n "WARNING: \"AllowAdd no\" is a bad idea "
+ echo "when upgrading between releases."
+ echo
+ fi
+ if [ "${ALLOWDELETE}" = "no" ]; then
+ echo -n "`basename $0`: "
+ echo -n "WARNING: \"AllowDelete no\" is a bad idea "
+ echo "when upgrading between releases."
+ echo
+ fi
+
+ # Set EDITOR to /usr/bin/vi if it isn't already set
+ : ${EDITOR:='/usr/bin/vi'}
+}
+
+# Perform sanity checks and set some final parameters in
+# preparation for installing updates.
+install_check_params () {
+ # Check that we are root. All sorts of things won't work otherwise.
+ if [ `id -u` != 0 ]; then
+ echo "You must be root to run this."
+ exit 1
+ fi
+
+ # Check that securelevel <= 0. Otherwise we can't update schg files.
+ if [ `sysctl -n kern.securelevel` -gt 0 ]; then
+ echo "Updates cannot be installed when the system securelevel"
+ echo "is greater than zero."
+ exit 1
+ fi
+
+ # Check that we have a working directory
+ _WORKDIR_bad="Directory does not exist or is not writable: "
+ if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
+ echo -n "`basename $0`: "
+ echo -n "${_WORKDIR_bad}"
+ echo ${WORKDIR}
+ exit 1
+ fi
+ cd ${WORKDIR} || exit 1
+
+ # Construct a unique name from ${BASEDIR}
+ BDHASH=`echo ${BASEDIR} | sha256 -q`
+
+ # Check that we have updates ready to install
+ if ! [ -L ${BDHASH}-install ]; then
+ echo "No updates are available to install."
+ echo "Run '$0 fetch' first."
+ exit 1
+ fi
+ if ! [ -f ${BDHASH}-install/INDEX-OLD ] ||
+ ! [ -f ${BDHASH}-install/INDEX-NEW ]; then
+ echo "Update manifest is corrupt -- this should never happen."
+ echo "Re-run '$0 fetch'."
+ exit 1
+ fi
+}
+
+# Perform sanity checks and set some final parameters in
+# preparation for UNinstalling updates.
+rollback_check_params () {
+ # Check that we are root. All sorts of things won't work otherwise.
+ if [ `id -u` != 0 ]; then
+ echo "You must be root to run this."
+ exit 1
+ fi
+
+ # Check that we have a working directory
+ _WORKDIR_bad="Directory does not exist or is not writable: "
+ if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
+ echo -n "`basename $0`: "
+ echo -n "${_WORKDIR_bad}"
+ echo ${WORKDIR}
+ exit 1
+ fi
+ cd ${WORKDIR} || exit 1
+
+ # Construct a unique name from ${BASEDIR}
+ BDHASH=`echo ${BASEDIR} | sha256 -q`
+
+ # Check that we have updates ready to rollback
+ if ! [ -L ${BDHASH}-rollback ]; then
+ echo "No rollback directory found."
+ exit 1
+ fi
+ if ! [ -f ${BDHASH}-rollback/INDEX-OLD ] ||
+ ! [ -f ${BDHASH}-rollback/INDEX-NEW ]; then
+ echo "Update manifest is corrupt -- this should never happen."
+ exit 1
+ fi
+}
+
+# Perform sanity checks and set some final parameters
+# in preparation for comparing the system against the
+# published index. Figure out which index we should
+# compare against: If the user is running *-p[0-9]+,
+# strip off the last part; if the user is running
+# -SECURITY, call it -RELEASE. Chdir into the working
+# directory.
+IDS_check_params () {
+ export HTTP_USER_AGENT="freebsd-update (${COMMAND}, `uname -r`)"
+
+ _SERVERNAME_z=\
+"SERVERNAME must be given via command line or configuration file."
+ _KEYPRINT_z="Key must be given via -k option or configuration file."
+ _KEYPRINT_bad="Invalid key fingerprint: "
+ _WORKDIR_bad="Directory does not exist or is not writable: "
+
+ if [ -z "${SERVERNAME}" ]; then
+ echo -n "`basename $0`: "
+ echo "${_SERVERNAME_z}"
+ exit 1
+ fi
+ if [ -z "${KEYPRINT}" ]; then
+ echo -n "`basename $0`: "
+ echo "${_KEYPRINT_z}"
+ exit 1
+ fi
+ if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then
+ echo -n "`basename $0`: "
+ echo -n "${_KEYPRINT_bad}"
+ echo ${KEYPRINT}
+ exit 1
+ fi
+ if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
+ echo -n "`basename $0`: "
+ echo -n "${_WORKDIR_bad}"
+ echo ${WORKDIR}
+ exit 1
+ fi
+ cd ${WORKDIR} || exit 1
+
+ # Generate release number. The s/SECURITY/RELEASE/ bit exists
+ # to provide an upgrade path for FreeBSD Update 1.x users, since
+ # the kernels provided by FreeBSD Update 1.x are always labelled
+ # as X.Y-SECURITY.
+ RELNUM=`uname -r |
+ sed -E 's,-p[0-9]+,,' |
+ sed -E 's,-SECURITY,-RELEASE,'`
+ ARCH=`uname -m`
+ FETCHDIR=${RELNUM}/${ARCH}
+ PATCHDIR=${RELNUM}/${ARCH}/bp
+
+ # Figure out what directory contains the running kernel
+ BOOTFILE=`sysctl -n kern.bootfile`
+ KERNELDIR=${BOOTFILE%/kernel}
+ if ! [ -d ${KERNELDIR} ]; then
+ echo "Cannot identify running kernel"
+ exit 1
+ fi
+
+ # Figure out what kernel configuration is running. We start with
+ # the output of `uname -i`, and then make the following adjustments:
+ # 1. Replace "SMP-GENERIC" with "SMP". Why the SMP kernel config
+ # file says "ident SMP-GENERIC", I don't know...
+ # 2. If the kernel claims to be GENERIC _and_ ${ARCH} is "amd64"
+ # _and_ `sysctl kern.version` contains a line which ends "/SMP", then
+ # we're running an SMP kernel. This mis-identification is a bug
+ # which was fixed in 6.2-STABLE.
+ KERNCONF=`uname -i`
+ if [ ${KERNCONF} = "SMP-GENERIC" ]; then
+ KERNCONF=SMP
+ fi
+ if [ ${KERNCONF} = "GENERIC" ] && [ ${ARCH} = "amd64" ]; then
+ if sysctl kern.version | grep -qE '/SMP$'; then
+ KERNCONF=SMP
+ fi
+ fi
+
+ # Define some paths
+ SHA256=/sbin/sha256
+ PHTTPGET=/usr/libexec/phttpget
+
+ # Set up variables relating to VERBOSELEVEL
+ fetch_setup_verboselevel
+}
+
+#### Core functionality -- the actual work gets done here
+
+# Use an SRV query to pick a server. If the SRV query doesn't provide
+# a useful answer, use the server name specified by the user.
+# Put another way... look up _http._tcp.${SERVERNAME} and pick a server
+# from that; or if no servers are returned, use ${SERVERNAME}.
+# This allows a user to specify "portsnap.freebsd.org" (in which case
+# portsnap will select one of the mirrors) or "portsnap5.tld.freebsd.org"
+# (in which case portsnap will use that particular server, since there
+# won't be an SRV entry for that name).
+#
+# We ignore the Port field, since we are always going to use port 80.
+
+# Fetch the mirror list, but do not pick a mirror yet. Returns 1 if
+# no mirrors are available for any reason.
+fetch_pick_server_init () {
+ : > serverlist_tried
+
+# Check that host(1) exists (i.e., that the system wasn't built with the
+# WITHOUT_BIND set) and don't try to find a mirror if it doesn't exist.
+ if ! which -s host; then
+ : > serverlist_full
+ return 1
+ fi
+
+ echo -n "Looking up ${SERVERNAME} mirrors... "
+
+# Issue the SRV query and pull out the Priority, Weight, and Target fields.
+# BIND 9 prints "$name has SRV record ..." while BIND 8 prints
+# "$name server selection ..."; we allow either format.
+ MLIST="_http._tcp.${SERVERNAME}"
+ host -t srv "${MLIST}" |
+ sed -nE "s/${MLIST} (has SRV record|server selection) //p" |
+ cut -f 1,2,4 -d ' ' |
+ sed -e 's/\.$//' |
+ sort > serverlist_full
+
+# If no records, give up -- we'll just use the server name we were given.
+ if [ `wc -l < serverlist_full` -eq 0 ]; then
+ echo "none found."
+ return 1
+ fi
+
+# Report how many mirrors we found.
+ echo `wc -l < serverlist_full` "mirrors found."
+
+# Generate a random seed for use in picking mirrors. If HTTP_PROXY
+# is set, this will be used to generate the seed; otherwise, the seed
+# will be random.
+ if [ -n "${HTTP_PROXY}${http_proxy}" ]; then
+ RANDVALUE=`sha256 -qs "${HTTP_PROXY}${http_proxy}" |
+ tr -d 'a-f' |
+ cut -c 1-9`
+ else
+ RANDVALUE=`jot -r 1 0 999999999`
+ fi
+}
+
+# Pick a mirror. Returns 1 if we have run out of mirrors to try.
+fetch_pick_server () {
+# Generate a list of not-yet-tried mirrors
+ sort serverlist_tried |
+ comm -23 serverlist_full - > serverlist
+
+# Have we run out of mirrors?
+ if [ `wc -l < serverlist` -eq 0 ]; then
+ echo "No mirrors remaining, giving up."
+ return 1
+ fi
+
+# Find the highest priority level (lowest numeric value).
+ SRV_PRIORITY=`cut -f 1 -d ' ' serverlist | sort -n | head -1`
+
+# Add up the weights of the response lines at that priority level.
+ SRV_WSUM=0;
+ while read X; do
+ case "$X" in
+ ${SRV_PRIORITY}\ *)
+ SRV_W=`echo $X | cut -f 2 -d ' '`
+ SRV_WSUM=$(($SRV_WSUM + $SRV_W))
+ ;;
+ esac
+ done < serverlist
+
+# If all the weights are 0, pretend that they are all 1 instead.
+ if [ ${SRV_WSUM} -eq 0 ]; then
+ SRV_WSUM=`grep -E "^${SRV_PRIORITY} " serverlist | wc -l`
+ SRV_W_ADD=1
+ else
+ SRV_W_ADD=0
+ fi
+
+# Pick a value between 0 and the sum of the weights - 1
+ SRV_RND=`expr ${RANDVALUE} % ${SRV_WSUM}`
+
+# Read through the list of mirrors and set SERVERNAME. Write the line
+# corresponding to the mirror we selected into serverlist_tried so that
+# we won't try it again.
+ while read X; do
+ case "$X" in
+ ${SRV_PRIORITY}\ *)
+ SRV_W=`echo $X | cut -f 2 -d ' '`
+ SRV_W=$(($SRV_W + $SRV_W_ADD))
+ if [ $SRV_RND -lt $SRV_W ]; then
+ SERVERNAME=`echo $X | cut -f 3 -d ' '`
+ echo "$X" >> serverlist_tried
+ break
+ else
+ SRV_RND=$(($SRV_RND - $SRV_W))
+ fi
+ ;;
+ esac
+ done < serverlist
+}
+
+# Take a list of ${oldhash}|${newhash} and output a list of needed patches,
+# i.e., those for which we have ${oldhash} and don't have ${newhash}.
+fetch_make_patchlist () {
+ grep -vE "^([0-9a-f]{64})\|\1$" |
+ tr '|' ' ' |
+ while read X Y; do
+ if [ -f "files/${Y}.gz" ] ||
+ [ ! -f "files/${X}.gz" ]; then
+ continue
+ fi
+ echo "${X}|${Y}"
+ done | uniq
+}
+
+# Print user-friendly progress statistics
+fetch_progress () {
+ LNC=0
+ while read x; do
+ LNC=$(($LNC + 1))
+ if [ $(($LNC % 10)) = 0 ]; then
+ echo -n $LNC
+ elif [ $(($LNC % 2)) = 0 ]; then
+ echo -n .
+ fi
+ done
+ echo -n " "
+}
+
+# Function for asking the user if everything is ok
+continuep () {
+ while read -p "Does this look reasonable (y/n)? " CONTINUE; do
+ case "${CONTINUE}" in
+ y*)
+ return 0
+ ;;
+ n*)
+ return 1
+ ;;
+ esac
+ done
+}
+
+# Initialize the working directory
+workdir_init () {
+ mkdir -p files
+ touch tINDEX.present
+}
+
+# Check that we have a public key with an appropriate hash, or
+# fetch the key if it doesn't exist. Returns 1 if the key has
+# not yet been fetched.
+fetch_key () {
+ if [ -r pub.ssl ] && [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
+ return 0
+ fi
+
+ echo -n "Fetching public key from ${SERVERNAME}... "
+ rm -f pub.ssl
+ fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/pub.ssl \
+ 2>${QUIETREDIR} || true
+ if ! [ -r pub.ssl ]; then
+ echo "failed."
+ return 1
+ fi
+ if ! [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
+ echo "key has incorrect hash."
+ rm -f pub.ssl
+ return 1
+ fi
+ echo "done."
+}
+
+# Fetch metadata signature, aka "tag".
+fetch_tag () {
+ echo -n "Fetching metadata signature "
+ echo ${NDEBUG} "for ${RELNUM} from ${SERVERNAME}... "
+ rm -f latest.ssl
+ fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/latest.ssl \
+ 2>${QUIETREDIR} || true
+ if ! [ -r latest.ssl ]; then
+ echo "failed."
+ return 1
+ fi
+
+ openssl rsautl -pubin -inkey pub.ssl -verify \
+ < latest.ssl > tag.new 2>${QUIETREDIR} || true
+ rm latest.ssl
+
+ if ! [ `wc -l < tag.new` = 1 ] ||
+ ! grep -qE \
+ "^freebsd-update\|${ARCH}\|${RELNUM}\|[0-9]+\|[0-9a-f]{64}\|[0-9]{10}" \
+ tag.new; then
+ echo "invalid signature."
+ return 1
+ fi
+
+ echo "done."
+
+ RELPATCHNUM=`cut -f 4 -d '|' < tag.new`
+ TINDEXHASH=`cut -f 5 -d '|' < tag.new`
+ EOLTIME=`cut -f 6 -d '|' < tag.new`
+}
+
+# Sanity-check the patch number in a tag, to make sure that we're not
+# going to "update" backwards and to prevent replay attacks.
+fetch_tagsanity () {
+ # Check that we're not going to move from -pX to -pY with Y < X.
+ RELPX=`uname -r | sed -E 's,.*-,,'`
+ if echo ${RELPX} | grep -qE '^p[0-9]+$'; then
+ RELPX=`echo ${RELPX} | cut -c 2-`
+ else
+ RELPX=0
+ fi
+ if [ "${RELPATCHNUM}" -lt "${RELPX}" ]; then
+ echo
+ echo -n "Files on mirror (${RELNUM}-p${RELPATCHNUM})"
+ echo " appear older than what"
+ echo "we are currently running (`uname -r`)!"
+ echo "Cowardly refusing to proceed any further."
+ return 1
+ fi
+
+ # If "tag" exists and corresponds to ${RELNUM}, make sure that
+ # it contains a patch number <= RELPATCHNUM, in order to protect
+ # against rollback (replay) attacks.
+ if [ -f tag ] &&
+ grep -qE \
+ "^freebsd-update\|${ARCH}\|${RELNUM}\|[0-9]+\|[0-9a-f]{64}\|[0-9]{10}" \
+ tag; then
+ LASTRELPATCHNUM=`cut -f 4 -d '|' < tag`
+
+ if [ "${RELPATCHNUM}" -lt "${LASTRELPATCHNUM}" ]; then
+ echo
+ echo -n "Files on mirror (${RELNUM}-p${RELPATCHNUM})"
+ echo " are older than the"
+ echo -n "most recently seen updates"
+ echo " (${RELNUM}-p${LASTRELPATCHNUM})."
+ echo "Cowardly refusing to proceed any further."
+ return 1
+ fi
+ fi
+}
+
+# Fetch metadata index file
+fetch_metadata_index () {
+ echo ${NDEBUG} "Fetching metadata index... "
+ rm -f ${TINDEXHASH}
+ fetch ${QUIETFLAG} http://${SERVERNAME}/${FETCHDIR}/t/${TINDEXHASH}
+ 2>${QUIETREDIR}
+ if ! [ -f ${TINDEXHASH} ]; then
+ echo "failed."
+ return 1
+ fi
+ if [ `${SHA256} -q ${TINDEXHASH}` != ${TINDEXHASH} ]; then
+ echo "update metadata index corrupt."
+ return 1
+ fi
+ echo "done."
+}
+
+# Print an error message about signed metadata being bogus.
+fetch_metadata_bogus () {
+ echo
+ echo "The update metadata$1 is correctly signed, but"
+ echo "failed an integrity check."
+ echo "Cowardly refusing to proceed any further."
+ return 1
+}
+
+# Construct tINDEX.new by merging the lines named in $1 from ${TINDEXHASH}
+# with the lines not named in $@ from tINDEX.present (if that file exists).
+fetch_metadata_index_merge () {
+ for METAFILE in $@; do
+ if [ `grep -E "^${METAFILE}\|" ${TINDEXHASH} | wc -l` \
+ -ne 1 ]; then
+ fetch_metadata_bogus " index"
+ return 1
+ fi
+
+ grep -E "${METAFILE}\|" ${TINDEXHASH}
+ done |
+ sort > tINDEX.wanted
+
+ if [ -f tINDEX.present ]; then
+ join -t '|' -v 2 tINDEX.wanted tINDEX.present |
+ sort -m - tINDEX.wanted > tINDEX.new
+ rm tINDEX.wanted
+ else
+ mv tINDEX.wanted tINDEX.new
+ fi
+}
+
+# Sanity check all the lines of tINDEX.new. Even if more metadata lines
+# are added by future versions of the server, this won't cause problems,
+# since the only lines which appear in tINDEX.new are the ones which we
+# specifically grepped out of ${TINDEXHASH}.
+fetch_metadata_index_sanity () {
+ if grep -qvE '^[0-9A-Z.-]+\|[0-9a-f]{64}$' tINDEX.new; then
+ fetch_metadata_bogus " index"
+ return 1
+ fi
+}
+
+# Sanity check the metadata file $1.
+fetch_metadata_sanity () {
+ # Some aliases to save space later: ${P} is a character which can
+ # appear in a path; ${M} is the four numeric metadata fields; and
+ # ${H} is a sha256 hash.
+ P="[-+./:=_[[:alnum:]]"
+ M="[0-9]+\|[0-9]+\|[0-9]+\|[0-9]+"
+ H="[0-9a-f]{64}"
+
+ # Check that the first four fields make sense.
+ if gunzip -c < files/$1.gz |
+ grep -qvE "^[a-z]+\|[0-9a-z]+\|${P}+\|[fdL-]\|"; then
+ fetch_metadata_bogus ""
+ return 1
+ fi
+
+ # Remove the first three fields.
+ gunzip -c < files/$1.gz |
+ cut -f 4- -d '|' > sanitycheck.tmp
+
+ # Sanity check entries with type 'f'
+ if grep -E '^f' sanitycheck.tmp |
+ grep -qvE "^f\|${M}\|${H}\|${P}*\$"; then
+ fetch_metadata_bogus ""
+ return 1
+ fi
+
+ # Sanity check entries with type 'd'
+ if grep -E '^d' sanitycheck.tmp |
+ grep -qvE "^d\|${M}\|\|\$"; then
+ fetch_metadata_bogus ""
+ return 1
+ fi
+
+ # Sanity check entries with type 'L'
+ if grep -E '^L' sanitycheck.tmp |
+ grep -qvE "^L\|${M}\|${P}*\|\$"; then
+ fetch_metadata_bogus ""
+ return 1
+ fi
+
+ # Sanity check entries with type '-'
+ if grep -E '^-' sanitycheck.tmp |
+ grep -qvE "^-\|\|\|\|\|\|"; then
+ fetch_metadata_bogus ""
+ return 1
+ fi
+
+ # Clean up
+ rm sanitycheck.tmp
+}
+
+# Fetch the metadata index and metadata files listed in $@,
+# taking advantage of metadata patches where possible.
+fetch_metadata () {
+ fetch_metadata_index || return 1
+ fetch_metadata_index_merge $@ || return 1
+ fetch_metadata_index_sanity || return 1
+
+ # Generate a list of wanted metadata patches
+ join -t '|' -o 1.2,2.2 tINDEX.present tINDEX.new |
+ fetch_make_patchlist > patchlist
+
+ if [ -s patchlist ]; then
+ # Attempt to fetch metadata patches
+ echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
+ echo ${NDEBUG} "metadata patches.${DDSTATS}"
+ tr '|' '-' < patchlist |
+ lam -s "${FETCHDIR}/tp/" - -s ".gz" |
+ xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \
+ 2>${STATSREDIR} | fetch_progress
+ echo "done."
+
+ # Attempt to apply metadata patches
+ echo -n "Applying metadata patches... "
+ tr '|' ' ' < patchlist |
+ while read X Y; do
+ if [ ! -f "${X}-${Y}.gz" ]; then continue; fi
+ gunzip -c < ${X}-${Y}.gz > diff
+ gunzip -c < files/${X}.gz > diff-OLD
+
+ # Figure out which lines are being added and removed
+ grep -E '^-' diff |
+ cut -c 2- |
+ while read PREFIX; do
+ look "${PREFIX}" diff-OLD
+ done |
+ sort > diff-rm
+ grep -E '^\+' diff |
+ cut -c 2- > diff-add
+
+ # Generate the new file
+ comm -23 diff-OLD diff-rm |
+ sort - diff-add > diff-NEW
+
+ if [ `${SHA256} -q diff-NEW` = ${Y} ]; then
+ mv diff-NEW files/${Y}
+ gzip -n files/${Y}
+ else
+ mv diff-NEW ${Y}.bad
+ fi
+ rm -f ${X}-${Y}.gz diff
+ rm -f diff-OLD diff-NEW diff-add diff-rm
+ done 2>${QUIETREDIR}
+ echo "done."
+ fi
+
+ # Update metadata without patches
+ cut -f 2 -d '|' < tINDEX.new |
+ while read Y; do
+ if [ ! -f "files/${Y}.gz" ]; then
+ echo ${Y};
+ fi
+ done |
+ sort -u > filelist
+
+ if [ -s filelist ]; then
+ echo -n "Fetching `wc -l < filelist | tr -d ' '` "
+ echo ${NDEBUG} "metadata files... "
+ lam -s "${FETCHDIR}/m/" - -s ".gz" < filelist |
+ xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \
+ 2>${QUIETREDIR}
+
+ while read Y; do
+ if ! [ -f ${Y}.gz ]; then
+ echo "failed."
+ return 1
+ fi
+ if [ `gunzip -c < ${Y}.gz |
+ ${SHA256} -q` = ${Y} ]; then
+ mv ${Y}.gz files/${Y}.gz
+ else
+ echo "metadata is corrupt."
+ return 1
+ fi
+ done < filelist
+ echo "done."
+ fi
+
+# Sanity-check the metadata files.
+ cut -f 2 -d '|' tINDEX.new > filelist
+ while read X; do
+ fetch_metadata_sanity ${X} || return 1
+ done < filelist
+
+# Remove files which are no longer needed
+ cut -f 2 -d '|' tINDEX.present |
+ sort > oldfiles
+ cut -f 2 -d '|' tINDEX.new |
+ sort |
+ comm -13 - oldfiles |
+ lam -s "files/" - -s ".gz" |
+ xargs rm -f
+ rm patchlist filelist oldfiles
+ rm ${TINDEXHASH}
+
+# We're done!
+ mv tINDEX.new tINDEX.present
+ mv tag.new tag
+
+ return 0
+}
+
+# Extract a subset of a downloaded metadata file containing only the parts
+# which are listed in COMPONENTS.
+fetch_filter_metadata_components () {
+ METAHASH=`look "$1|" tINDEX.present | cut -f 2 -d '|'`
+ gunzip -c < files/${METAHASH}.gz > $1.all
+
+ # Fish out the lines belonging to components we care about.
+ for C in ${COMPONENTS}; do
+ look "`echo ${C} | tr '/' '|'`|" $1.all
+ done > $1
+
+ # Remove temporary file.
+ rm $1.all
+}
+
+# Generate a filtered version of the metadata file $1 from the downloaded
+# file, by fishing out the lines corresponding to components we're trying
+# to keep updated, and then removing lines corresponding to paths we want
+# to ignore.
+fetch_filter_metadata () {
+ # Fish out the lines belonging to components we care about.
+ fetch_filter_metadata_components $1
+
+ # Canonicalize directory names by removing any trailing / in
+ # order to avoid listing directories multiple times if they
+ # belong to multiple components. Turning "/" into "" doesn't
+ # matter, since we add a leading "/" when we use paths later.
+ cut -f 3- -d '|' $1 |
+ sed -e 's,/|d|,|d|,' |
+ sort -u > $1.tmp
+
+ # Figure out which lines to ignore and remove them.
+ for X in ${IGNOREPATHS}; do
+ grep -E "^${X}" $1.tmp
+ done |
+ sort -u |
+ comm -13 - $1.tmp > $1
+
+ # Remove temporary files.
+ rm $1.tmp
+}
+
+# Filter the metadata file $1 by adding lines with "/boot/$2"
+# replaced by ${KERNELDIR} (which is `sysctl -n kern.bootfile` minus the
+# trailing "/kernel"); and if "/boot/$2" does not exist, remove
+# the original lines which start with that.
+# Put another way: Deal with the fact that the FOO kernel is sometimes
+# installed in /boot/FOO/ and is sometimes installed elsewhere.
+fetch_filter_kernel_names () {
+ grep ^/boot/$2 $1 |
+ sed -e "s,/boot/$2,${KERNELDIR},g" |
+ sort - $1 > $1.tmp
+ mv $1.tmp $1
+
+ if ! [ -d /boot/$2 ]; then
+ grep -v ^/boot/$2 $1 > $1.tmp
+ mv $1.tmp $1
+ fi
+}
+
+# For all paths appearing in $1 or $3, inspect the system
+# and generate $2 describing what is currently installed.
+fetch_inspect_system () {
+ # No errors yet...
+ rm -f .err
+
+ # Tell the user why his disk is suddenly making lots of noise
+ echo -n "Inspecting system... "
+
+ # Generate list of files to inspect
+ cat $1 $3 |
+ cut -f 1 -d '|' |
+ sort -u > filelist
+
+ # Examine each file and output lines of the form
+ # /path/to/file|type|device-inum|user|group|perm|flags|value
+ # sorted by device and inode number.
+ while read F; do
+ # If the symlink/file/directory does not exist, record this.
+ if ! [ -e ${BASEDIR}/${F} ]; then
+ echo "${F}|-||||||"
+ continue
+ fi
+ if ! [ -r ${BASEDIR}/${F} ]; then
+ echo "Cannot read file: ${BASEDIR}/${F}" \
+ >/dev/stderr
+ touch .err
+ return 1
+ fi
+
+ # Otherwise, output an index line.
+ if [ -L ${BASEDIR}/${F} ]; then
+ echo -n "${F}|L|"
+ stat -n -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F};
+ readlink ${BASEDIR}/${F};
+ elif [ -f ${BASEDIR}/${F} ]; then
+ echo -n "${F}|f|"
+ stat -n -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F};
+ sha256 -q ${BASEDIR}/${F};
+ elif [ -d ${BASEDIR}/${F} ]; then
+ echo -n "${F}|d|"
+ stat -f '%d-%i|%u|%g|%Mp%Lp|%Of|' ${BASEDIR}/${F};
+ else
+ echo "Unknown file type: ${BASEDIR}/${F}" \
+ >/dev/stderr
+ touch .err
+ return 1
+ fi
+ done < filelist |
+ sort -k 3,3 -t '|' > $2.tmp
+ rm filelist
+
+ # Check if an error occured during system inspection
+ if [ -f .err ]; then
+ return 1
+ fi
+
+ # Convert to the form
+ # /path/to/file|type|user|group|perm|flags|value|hlink
+ # by resolving identical device and inode numbers into hard links.
+ cut -f 1,3 -d '|' $2.tmp |
+ sort -k 1,1 -t '|' |
+ sort -s -u -k 2,2 -t '|' |
+ join -1 2 -2 3 -t '|' - $2.tmp |
+ awk -F \| -v OFS=\| \
+ '{
+ if (($2 == $3) || ($4 == "-"))
+ print $3,$4,$5,$6,$7,$8,$9,""
+ else
+ print $3,$4,$5,$6,$7,$8,$9,$2
+ }' |
+ sort > $2
+ rm $2.tmp
+
+ # We're finished looking around
+ echo "done."
+}
+
+# For any paths matching ${MERGECHANGES}, compare $1 and $2 and find any
+# files which differ; generate $3 containing these paths and the old hashes.
+fetch_filter_mergechanges () {
+ # Pull out the paths and hashes of the files matching ${MERGECHANGES}.
+ for F in $1 $2; do
+ for X in ${MERGECHANGES}; do
+ grep -E "^${X}" ${F}
+ done |
+ cut -f 1,2,7 -d '|' |
+ sort > ${F}-values
+ done
+
+ # Any line in $2-values which doesn't appear in $1-values and is a
+ # file means that we should list the path in $3.
+ comm -13 $1-values $2-values |
+ fgrep '|f|' |
+ cut -f 1 -d '|' > $2-paths
+
+ # For each path, pull out one (and only one!) entry from $1-values.
+ # Note that we cannot distinguish which "old" version the user made
+ # changes to; but hopefully any changes which occur due to security
+ # updates will exist in both the "new" version and the version which
+ # the user has installed, so the merging will still work.
+ while read X; do
+ look "${X}|" $1-values |
+ head -1
+ done < $2-paths > $3
+
+ # Clean up
+ rm $1-values $2-values $2-paths
+}
+
+# For any paths matching ${UPDATEIFUNMODIFIED}, remove lines from $[123]
+# which correspond to lines in $2 with hashes not matching $1 or $3, unless
+# the paths are listed in $4. For entries in $2 marked "not present"
+# (aka. type -), remove lines from $[123] unless there is a corresponding
+# entry in $1.
+fetch_filter_unmodified_notpresent () {
+ # Figure out which lines of $1 and $3 correspond to bits which
+ # should only be updated if they haven't changed, and fish out
+ # the (path, type, value) tuples.
+ # NOTE: We don't consider a file to be "modified" if it matches
+ # the hash from $3.
+ for X in ${UPDATEIFUNMODIFIED}; do
+ grep -E "^${X}" $1
+ grep -E "^${X}" $3
+ done |
+ cut -f 1,2,7 -d '|' |
+ sort > $1-values
+
+ # Do the same for $2.
+ for X in ${UPDATEIFUNMODIFIED}; do
+ grep -E "^${X}" $2
+ done |
+ cut -f 1,2,7 -d '|' |
+ sort > $2-values
+
+ # Any entry in $2-values which is not in $1-values corresponds to
+ # a path which we need to remove from $1, $2, and $3, unless it
+ # that path appears in $4.
+ comm -13 $1-values $2-values |
+ sort -t '|' -k 1,1 > mlines.tmp
+ cut -f 1 -d '|' $4 |
+ sort |
+ join -v 2 -t '|' - mlines.tmp |
+ sort > mlines
+ rm $1-values $2-values mlines.tmp
+
+ # Any lines in $2 which are not in $1 AND are "not present" lines
+ # also belong in mlines.
+ comm -13 $1 $2 |
+ cut -f 1,2,7 -d '|' |
+ fgrep '|-|' >> mlines
+
+ # Remove lines from $1, $2, and $3
+ for X in $1 $2 $3; do
+ sort -t '|' -k 1,1 ${X} > ${X}.tmp
+ cut -f 1 -d '|' < mlines |
+ sort |
+ join -v 2 -t '|' - ${X}.tmp |
+ sort > ${X}
+ rm ${X}.tmp
+ done
+
+ # Store a list of the modified files, for future reference
+ fgrep -v '|-|' mlines |
+ cut -f 1 -d '|' > modifiedfiles
+ rm mlines
+}
+
+# For each entry in $1 of type -, remove any corresponding
+# entry from $2 if ${ALLOWADD} != "yes". Remove all entries
+# of type - from $1.
+fetch_filter_allowadd () {
+ cut -f 1,2 -d '|' < $1 |
+ fgrep '|-' |
+ cut -f 1 -d '|' > filesnotpresent
+
+ if [ ${ALLOWADD} != "yes" ]; then
+ sort < $2 |
+ join -v 1 -t '|' - filesnotpresent |
+ sort > $2.tmp
+ mv $2.tmp $2
+ fi
+
+ sort < $1 |
+ join -v 1 -t '|' - filesnotpresent |
+ sort > $1.tmp
+ mv $1.tmp $1
+ rm filesnotpresent
+}
+
+# If ${ALLOWDELETE} != "yes", then remove any entries from $1
+# which don't correspond to entries in $2.
+fetch_filter_allowdelete () {
+ # Produce a lists ${PATH}|${TYPE}
+ for X in $1 $2; do
+ cut -f 1-2 -d '|' < ${X} |
+ sort -u > ${X}.nodes
+ done
+
+ # Figure out which lines need to be removed from $1.
+ if [ ${ALLOWDELETE} != "yes" ]; then
+ comm -23 $1.nodes $2.nodes > $1.badnodes
+ else
+ : > $1.badnodes
+ fi
+
+ # Remove the relevant lines from $1
+ while read X; do
+ look "${X}|" $1
+ done < $1.badnodes |
+ comm -13 - $1 > $1.tmp
+ mv $1.tmp $1
+
+ rm $1.badnodes $1.nodes $2.nodes
+}
+
+# If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in $2
+# with metadata not matching any entry in $1, replace the corresponding
+# line of $3 with one having the same metadata as the entry in $2.
+fetch_filter_modified_metadata () {
+ # Fish out the metadata from $1 and $2
+ for X in $1 $2; do
+ cut -f 1-6 -d '|' < ${X} > ${X}.metadata
+ done
+
+ # Find the metadata we need to keep
+ if [ ${KEEPMODIFIEDMETADATA} = "yes" ]; then
+ comm -13 $1.metadata $2.metadata > keepmeta
+ else
+ : > keepmeta
+ fi
+
+ # Extract the lines which we need to remove from $3, and
+ # construct the lines which we need to add to $3.
+ : > $3.remove
+ : > $3.add
+ while read LINE; do
+ NODE=`echo "${LINE}" | cut -f 1-2 -d '|'`
+ look "${NODE}|" $3 >> $3.remove
+ look "${NODE}|" $3 |
+ cut -f 7- -d '|' |
+ lam -s "${LINE}|" - >> $3.add
+ done < keepmeta
+
+ # Remove the specified lines and add the new lines.
+ sort $3.remove |
+ comm -13 - $3 |
+ sort -u - $3.add > $3.tmp
+ mv $3.tmp $3
+
+ rm keepmeta $1.metadata $2.metadata $3.add $3.remove
+}
+
+# Remove lines from $1 and $2 which are identical;
+# no need to update a file if it isn't changing.
+fetch_filter_uptodate () {
+ comm -23 $1 $2 > $1.tmp
+ comm -13 $1 $2 > $2.tmp
+
+ mv $1.tmp $1
+ mv $2.tmp $2
+}
+
+# Fetch any "clean" old versions of files we need for merging changes.
+fetch_files_premerge () {
+ # We only need to do anything if $1 is non-empty.
+ if [ -s $1 ]; then
+ # Tell the user what we're doing
+ echo -n "Fetching files from ${OLDRELNUM} for merging... "
+
+ # List of files wanted
+ fgrep '|f|' < $1 |
+ cut -f 3 -d '|' |
+ sort -u > files.wanted
+
+ # Only fetch the files we don't already have
+ while read Y; do
+ if [ ! -f "files/${Y}.gz" ]; then
+ echo ${Y};
+ fi
+ done < files.wanted > filelist
+
+ # Actually fetch them
+ lam -s "${OLDFETCHDIR}/f/" - -s ".gz" < filelist |
+ xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \
+ 2>${QUIETREDIR}
+
+ # Make sure we got them all, and move them into /files/
+ while read Y; do
+ if ! [ -f ${Y}.gz ]; then
+ echo "failed."
+ return 1
+ fi
+ if [ `gunzip -c < ${Y}.gz |
+ ${SHA256} -q` = ${Y} ]; then
+ mv ${Y}.gz files/${Y}.gz
+ else
+ echo "${Y} has incorrect hash."
+ return 1
+ fi
+ done < filelist
+ echo "done."
+
+ # Clean up
+ rm filelist files.wanted
+ fi
+}
+
+# Prepare to fetch files: Generate a list of the files we need,
+# copy the unmodified files we have into /files/, and generate
+# a list of patches to download.
+fetch_files_prepare () {
+ # Tell the user why his disk is suddenly making lots of noise
+ echo -n "Preparing to download files... "
+
+ # Reduce indices to ${PATH}|${HASH} pairs
+ for X in $1 $2 $3; do
+ cut -f 1,2,7 -d '|' < ${X} |
+ fgrep '|f|' |
+ cut -f 1,3 -d '|' |
+ sort > ${X}.hashes
+ done
+
+ # List of files wanted
+ cut -f 2 -d '|' < $3.hashes |
+ sort -u |
+ while read HASH; do
+ if ! [ -f files/${HASH}.gz ]; then
+ echo ${HASH}
+ fi
+ done > files.wanted
+
+ # Generate a list of unmodified files
+ comm -12 $1.hashes $2.hashes |
+ sort -k 1,1 -t '|' > unmodified.files
+
+ # Copy all files into /files/. We only need the unmodified files
+ # for use in patching; but we'll want all of them if the user asks
+ # to rollback the updates later.
+ while read LINE; do
+ F=`echo "${LINE}" | cut -f 1 -d '|'`
+ HASH=`echo "${LINE}" | cut -f 2 -d '|'`
+
+ # Skip files we already have.
+ if [ -f files/${HASH}.gz ]; then
+ continue
+ fi
+
+ # Make sure the file hasn't changed.
+ cp "${BASEDIR}/${F}" tmpfile
+ if [ `sha256 -q tmpfile` != ${HASH} ]; then
+ echo
+ echo "File changed while FreeBSD Update running: ${F}"
+ return 1
+ fi
+
+ # Place the file into storage.
+ gzip -c < tmpfile > files/${HASH}.gz
+ rm tmpfile
+ done < $2.hashes
+
+ # Produce a list of patches to download
+ sort -k 1,1 -t '|' $3.hashes |
+ join -t '|' -o 2.2,1.2 - unmodified.files |
+ fetch_make_patchlist > patchlist
+
+ # Garbage collect
+ rm unmodified.files $1.hashes $2.hashes $3.hashes
+
+ # We don't need the list of possible old files any more.
+ rm $1
+
+ # We're finished making noise
+ echo "done."
+}
+
+# Fetch files.
+fetch_files () {
+ # Attempt to fetch patches
+ if [ -s patchlist ]; then
+ echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
+ echo ${NDEBUG} "patches.${DDSTATS}"
+ tr '|' '-' < patchlist |
+ lam -s "${PATCHDIR}/" - |
+ xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \
+ 2>${STATSREDIR} | fetch_progress
+ echo "done."
+
+ # Attempt to apply patches
+ echo -n "Applying patches... "
+ tr '|' ' ' < patchlist |
+ while read X Y; do
+ if [ ! -f "${X}-${Y}" ]; then continue; fi
+ gunzip -c < files/${X}.gz > OLD
+
+ bspatch OLD NEW ${X}-${Y}
+
+ if [ `${SHA256} -q NEW` = ${Y} ]; then
+ mv NEW files/${Y}
+ gzip -n files/${Y}
+ fi
+ rm -f diff OLD NEW ${X}-${Y}
+ done 2>${QUIETREDIR}
+ echo "done."
+ fi
+
+ # Download files which couldn't be generate via patching
+ while read Y; do
+ if [ ! -f "files/${Y}.gz" ]; then
+ echo ${Y};
+ fi
+ done < files.wanted > filelist
+
+ if [ -s filelist ]; then
+ echo -n "Fetching `wc -l < filelist | tr -d ' '` "
+ echo ${NDEBUG} "files... "
+ lam -s "${FETCHDIR}/f/" - -s ".gz" < filelist |
+ xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \
+ 2>${QUIETREDIR}
+
+ while read Y; do
+ if ! [ -f ${Y}.gz ]; then
+ echo "failed."
+ return 1
+ fi
+ if [ `gunzip -c < ${Y}.gz |
+ ${SHA256} -q` = ${Y} ]; then
+ mv ${Y}.gz files/${Y}.gz
+ else
+ echo "${Y} has incorrect hash."
+ return 1
+ fi
+ done < filelist
+ echo "done."
+ fi
+
+ # Clean up
+ rm files.wanted filelist patchlist
+}
+
+# Create and populate install manifest directory; and report what updates
+# are available.
+fetch_create_manifest () {
+ # If we have an existing install manifest, nuke it.
+ if [ -L "${BDHASH}-install" ]; then
+ rm -r ${BDHASH}-install/
+ rm ${BDHASH}-install
+ fi
+
+ # Report to the user if any updates were avoided due to local changes
+ if [ -s modifiedfiles ]; then
+ echo
+ echo -n "The following files are affected by updates, "
+ echo "but no changes have"
+ echo -n "been downloaded because the files have been "
+ echo "modified locally:"
+ cat modifiedfiles
+ fi | more
+ rm modifiedfiles
+
+ # If no files will be updated, tell the user and exit
+ if ! [ -s INDEX-PRESENT ] &&
+ ! [ -s INDEX-NEW ]; then
+ rm INDEX-PRESENT INDEX-NEW
+ echo
+ echo -n "No updates needed to update system to "
+ echo "${RELNUM}-p${RELPATCHNUM}."
+ return
+ fi
+
+ # Divide files into (a) removed files, (b) added files, and
+ # (c) updated files.
+ cut -f 1 -d '|' < INDEX-PRESENT |
+ sort > INDEX-PRESENT.flist
+ cut -f 1 -d '|' < INDEX-NEW |
+ sort > INDEX-NEW.flist
+ comm -23 INDEX-PRESENT.flist INDEX-NEW.flist > files.removed
+ comm -13 INDEX-PRESENT.flist INDEX-NEW.flist > files.added
+ comm -12 INDEX-PRESENT.flist INDEX-NEW.flist > files.updated
+ rm INDEX-PRESENT.flist INDEX-NEW.flist
+
+ # Report removed files, if any
+ if [ -s files.removed ]; then
+ echo
+ echo -n "The following files will be removed "
+ echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:"
+ cat files.removed
+ fi | more
+ rm files.removed
+
+ # Report added files, if any
+ if [ -s files.added ]; then
+ echo
+ echo -n "The following files will be added "
+ echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:"
+ cat files.added
+ fi | more
+ rm files.added
+
+ # Report updated files, if any
+ if [ -s files.updated ]; then
+ echo
+ echo -n "The following files will be updated "
+ echo "as part of updating to ${RELNUM}-p${RELPATCHNUM}:"
+
+ cat files.updated
+ fi | more
+ rm files.updated
+
+ # Create a directory for the install manifest.
+ MDIR=`mktemp -d install.XXXXXX` || return 1
+
+ # Populate it
+ mv INDEX-PRESENT ${MDIR}/INDEX-OLD
+ mv INDEX-NEW ${MDIR}/INDEX-NEW
+
+ # Link it into place
+ ln -s ${MDIR} ${BDHASH}-install
+}
+
+# Warn about any upcoming EoL
+fetch_warn_eol () {
+ # What's the current time?
+ NOWTIME=`date "+%s"`
+
+ # When did we last warn about the EoL date?
+ if [ -f lasteolwarn ]; then
+ LASTWARN=`cat lasteolwarn`
+ else
+ LASTWARN=`expr ${NOWTIME} - 63072000`
+ fi
+
+ # If the EoL time is past, warn.
+ if [ ${EOLTIME} -lt ${NOWTIME} ]; then
+ echo
+ cat <<-EOF
+ WARNING: `uname -sr` HAS PASSED ITS END-OF-LIFE DATE.
+ Any security issues discovered after `date -r ${EOLTIME}`
+ will not have been corrected.
+ EOF
+ return 1
+ fi
+
+ # Figure out how long it has been since we last warned about the
+ # upcoming EoL, and how much longer we have left.
+ SINCEWARN=`expr ${NOWTIME} - ${LASTWARN}`
+ TIMELEFT=`expr ${EOLTIME} - ${NOWTIME}`
+
+ # Don't warn if the EoL is more than 3 months away
+ if [ ${TIMELEFT} -gt 7884000 ]; then
+ return 0
+ fi
+
+ # Don't warn if the time remaining is more than 3 times the time
+ # since the last warning.
+ if [ ${TIMELEFT} -gt `expr ${SINCEWARN} \* 3` ]; then
+ return 0
+ fi
+
+ # Figure out what time units to use.
+ if [ ${TIMELEFT} -lt 604800 ]; then
+ UNIT="day"
+ SIZE=86400
+ elif [ ${TIMELEFT} -lt 2678400 ]; then
+ UNIT="week"
+ SIZE=604800
+ else
+ UNIT="month"
+ SIZE=2678400
+ fi
+
+ # Compute the right number of units
+ NUM=`expr ${TIMELEFT} / ${SIZE}`
+ if [ ${NUM} != 1 ]; then
+ UNIT="${UNIT}s"
+ fi
+
+ # Print the warning
+ echo
+ cat <<-EOF
+ WARNING: `uname -sr` is approaching its End-of-Life date.
+ It is strongly recommended that you upgrade to a newer
+ release within the next ${NUM} ${UNIT}.
+ EOF
+
+ # Update the stored time of last warning
+ echo ${NOWTIME} > lasteolwarn
+}
+
+# Do the actual work involved in "fetch" / "cron".
+fetch_run () {
+ workdir_init || return 1
+
+ # Prepare the mirror list.
+ fetch_pick_server_init && fetch_pick_server
+
+ # Try to fetch the public key until we run out of servers.
+ while ! fetch_key; do
+ fetch_pick_server || return 1
+ done
+
+ # Try to fetch the metadata index signature ("tag") until we run
+ # out of available servers; and sanity check the downloaded tag.
+ while ! fetch_tag; do
+ fetch_pick_server || return 1
+ done
+ fetch_tagsanity || return 1
+
+ # Fetch the latest INDEX-NEW and INDEX-OLD files.
+ fetch_metadata INDEX-NEW INDEX-OLD || return 1
+
+ # Generate filtered INDEX-NEW and INDEX-OLD files containing only
+ # the lines which (a) belong to components we care about, and (b)
+ # don't correspond to paths we're explicitly ignoring.
+ fetch_filter_metadata INDEX-NEW || return 1
+ fetch_filter_metadata INDEX-OLD || return 1
+
+ # Translate /boot/${KERNCONF} into ${KERNELDIR}
+ fetch_filter_kernel_names INDEX-NEW ${KERNCONF}
+ fetch_filter_kernel_names INDEX-OLD ${KERNCONF}
+
+ # For all paths appearing in INDEX-OLD or INDEX-NEW, inspect the
+ # system and generate an INDEX-PRESENT file.
+ fetch_inspect_system INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
+
+ # Based on ${UPDATEIFUNMODIFIED}, remove lines from INDEX-* which
+ # correspond to lines in INDEX-PRESENT with hashes not appearing
+ # in INDEX-OLD or INDEX-NEW. Also remove lines where the entry in
+ # INDEX-PRESENT has type - and there isn't a corresponding entry in
+ # INDEX-OLD with type -.
+ fetch_filter_unmodified_notpresent \
+ INDEX-OLD INDEX-PRESENT INDEX-NEW /dev/null
+
+ # For each entry in INDEX-PRESENT of type -, remove any corresponding
+ # entry from INDEX-NEW if ${ALLOWADD} != "yes". Remove all entries
+ # of type - from INDEX-PRESENT.
+ fetch_filter_allowadd INDEX-PRESENT INDEX-NEW
+
+ # If ${ALLOWDELETE} != "yes", then remove any entries from
+ # INDEX-PRESENT which don't correspond to entries in INDEX-NEW.
+ fetch_filter_allowdelete INDEX-PRESENT INDEX-NEW
+
+ # If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in
+ # INDEX-PRESENT with metadata not matching any entry in INDEX-OLD,
+ # replace the corresponding line of INDEX-NEW with one having the
+ # same metadata as the entry in INDEX-PRESENT.
+ fetch_filter_modified_metadata INDEX-OLD INDEX-PRESENT INDEX-NEW
+
+ # Remove lines from INDEX-PRESENT and INDEX-NEW which are identical;
+ # no need to update a file if it isn't changing.
+ fetch_filter_uptodate INDEX-PRESENT INDEX-NEW
+
+ # Prepare to fetch files: Generate a list of the files we need,
+ # copy the unmodified files we have into /files/, and generate
+ # a list of patches to download.
+ fetch_files_prepare INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
+
+ # Fetch files.
+ fetch_files || return 1
+
+ # Create and populate install manifest directory; and report what
+ # updates are available.
+ fetch_create_manifest || return 1
+
+ # Warn about any upcoming EoL
+ fetch_warn_eol || return 1
+}
+
+# If StrictComponents is not "yes", generate a new components list
+# with only the components which appear to be installed.
+upgrade_guess_components () {
+ if [ "${STRICTCOMPONENTS}" = "no" ]; then
+ # Generate filtered INDEX-ALL with only the components listed
+ # in COMPONENTS.
+ fetch_filter_metadata_components $1 || return 1
+
+ # Tell the user why his disk is suddenly making lots of noise
+ echo -n "Inspecting system... "
+
+ # Look at the files on disk, and assume that a component is
+ # supposed to be present if it is more than half-present.
+ cut -f 1-3 -d '|' < INDEX-ALL |
+ tr '|' ' ' |
+ while read C S F; do
+ if [ -e ${BASEDIR}/${F} ]; then
+ echo "+ ${C}|${S}"
+ fi
+ echo "= ${C}|${S}"
+ done |
+ sort |
+ uniq -c |
+ sed -E 's,^ +,,' > compfreq
+ grep ' = ' compfreq |
+ cut -f 1,3 -d ' ' |
+ sort -k 2,2 -t ' ' > compfreq.total
+ grep ' + ' compfreq |
+ cut -f 1,3 -d ' ' |
+ sort -k 2,2 -t ' ' > compfreq.present
+ join -t ' ' -1 2 -2 2 compfreq.present compfreq.total |
+ while read S P T; do
+ if [ ${P} -gt `expr ${T} / 2` ]; then
+ echo ${S}
+ fi
+ done > comp.present
+ cut -f 2 -d ' ' < compfreq.total > comp.total
+ rm INDEX-ALL compfreq compfreq.total compfreq.present
+
+ # We're done making noise.
+ echo "done."
+
+ # Sometimes the kernel isn't installed where INDEX-ALL
+ # thinks that it should be: In particular, it is often in
+ # /boot/kernel instead of /boot/GENERIC or /boot/SMP. To
+ # deal with this, if "kernel|X" is listed in comp.total
+ # (i.e., is a component which would be upgraded if it is
+ # found to be present) we will add it to comp.present.
+ # If "kernel|<anything>" is in comp.total but "kernel|X" is
+ # not, we print a warning -- the user is running a kernel
+ # which isn't part of the release.
+ KCOMP=`echo ${KERNCONF} | tr 'A-Z' 'a-z'`
+ grep -E "^kernel\|${KCOMP}\$" comp.total >> comp.present
+
+ if grep -qE "^kernel\|" comp.total &&
+ ! grep -qE "^kernel\|${KCOMP}\$" comp.total; then
+ cat <<-EOF
+
+WARNING: This system is running a "${KCOMP}" kernel, which is not a
+kernel configuration distributed as part of FreeBSD ${RELNUM}.
+This kernel will not be updated: you MUST update the kernel manually
+before running "$0 install".
+ EOF
+ fi
+
+ # Re-sort the list of installed components and generate
+ # the list of non-installed components.
+ sort -u < comp.present > comp.present.tmp
+ mv comp.present.tmp comp.present
+ comm -13 comp.present comp.total > comp.absent
+
+ # Ask the user to confirm that what we have is correct. To
+ # reduce user confusion, translate "X|Y" back to "X/Y" (as
+ # subcomponents must be listed in the configuration file).
+ echo
+ echo -n "The following components of FreeBSD "
+ echo "seem to be installed:"
+ tr '|' '/' < comp.present |
+ fmt -72
+ echo
+ echo -n "The following components of FreeBSD "
+ echo "do not seem to be installed:"
+ tr '|' '/' < comp.absent |
+ fmt -72
+ echo
+ continuep || return 1
+ echo
+
+ # Suck the generated list of components into ${COMPONENTS}.
+ # Note that comp.present.tmp is used due to issues with
+ # pipelines and setting variables.
+ COMPONENTS=""
+ tr '|' '/' < comp.present > comp.present.tmp
+ while read C; do
+ COMPONENTS="${COMPONENTS} ${C}"
+ done < comp.present.tmp
+
+ # Delete temporary files
+ rm comp.present comp.present.tmp comp.absent comp.total
+ fi
+}
+
+# If StrictComponents is not "yes", COMPONENTS contains an entry
+# corresponding to the currently running kernel, and said kernel
+# does not exist in the new release, add "kernel/generic" to the
+# list of components.
+upgrade_guess_new_kernel () {
+ if [ "${STRICTCOMPONENTS}" = "no" ]; then
+ # Grab the unfiltered metadata file.
+ METAHASH=`look "$1|" tINDEX.present | cut -f 2 -d '|'`
+ gunzip -c < files/${METAHASH}.gz > $1.all
+
+ # If "kernel/${KCOMP}" is in ${COMPONENTS} and that component
+ # isn't in $1.all, we need to add kernel/generic.
+ for C in ${COMPONENTS}; do
+ if [ ${C} = "kernel/${KCOMP}" ] &&
+ ! grep -qE "^kernel\|${KCOMP}\|" $1.all; then
+ COMPONENTS="${COMPONENTS} kernel/generic"
+ NKERNCONF="GENERIC"
+ cat <<-EOF
+
+WARNING: This system is running a "${KCOMP}" kernel, which is not a
+kernel configuration distributed as part of FreeBSD ${RELNUM}.
+As part of upgrading to FreeBSD ${RELNUM}, this kernel will be
+replaced with a "generic" kernel.
+ EOF
+ continuep || return 1
+ fi
+ done
+
+ # Don't need this any more...
+ rm $1.all
+ fi
+}
+
+# Convert INDEX-OLD (last release) and INDEX-ALL (new release) into
+# INDEX-OLD and INDEX-NEW files (in the sense of normal upgrades).
+upgrade_oldall_to_oldnew () {
+ # For each ${F}|... which appears in INDEX-ALL but does not appear
+ # in INDEX-OLD, add ${F}|-|||||| to INDEX-OLD.
+ cut -f 1 -d '|' < $1 |
+ sort -u > $1.paths
+ cut -f 1 -d '|' < $2 |
+ sort -u |
+ comm -13 $1.paths - |
+ lam - -s "|-||||||" |
+ sort - $1 > $1.tmp
+ mv $1.tmp $1
+
+ # Remove lines from INDEX-OLD which also appear in INDEX-ALL
+ comm -23 $1 $2 > $1.tmp
+ mv $1.tmp $1
+
+ # Remove lines from INDEX-ALL which have a file name not appearing
+ # anywhere in INDEX-OLD (since these must be files which haven't
+ # changed -- if they were new, there would be an entry of type "-").
+ cut -f 1 -d '|' < $1 |
+ sort -u > $1.paths
+ sort -k 1,1 -t '|' < $2 |
+ join -t '|' - $1.paths |
+ sort > $2.tmp
+ rm $1.paths
+ mv $2.tmp $2
+
+ # Rename INDEX-ALL to INDEX-NEW.
+ mv $2 $3
+}
+
+# From the list of "old" files in $1, merge changes in $2 with those in $3,
+# and update $3 to reflect the hashes of merged files.
+upgrade_merge () {
+ # We only need to do anything if $1 is non-empty.
+ if [ -s $1 ]; then
+ cut -f 1 -d '|' $1 |
+ sort > $1-paths
+
+ # Create staging area for merging files
+ rm -rf merge/
+ while read F; do
+ D=`dirname ${F}`
+ mkdir -p merge/old/${D}
+ mkdir -p merge/${OLDRELNUM}/${D}
+ mkdir -p merge/${RELNUM}/${D}
+ mkdir -p merge/new/${D}
+ done < $1-paths
+
+ # Copy in files
+ while read F; do
+ # Currently installed file
+ V=`look "${F}|" $2 | cut -f 7 -d '|'`
+ gunzip < files/${V}.gz > merge/old/${F}
+
+ # Old release
+ if look "${F}|" $1 | fgrep -q "|f|"; then
+ V=`look "${F}|" $1 | cut -f 3 -d '|'`
+ gunzip < files/${V}.gz \
+ > merge/${OLDRELNUM}/${F}
+ fi
+
+ # New release
+ if look "${F}|" $3 | cut -f 1,2,7 -d '|' |
+ fgrep -q "|f|"; then
+ V=`look "${F}|" $3 | cut -f 7 -d '|'`
+ gunzip < files/${V}.gz \
+ > merge/${RELNUM}/${F}
+ fi
+ done < $1-paths
+
+ # Attempt to automatically merge changes
+ echo -n "Attempting to automatically merge "
+ echo -n "changes in files..."
+ : > failed.merges
+ while read F; do
+ # If the file doesn't exist in the new release,
+ # the result of "merging changes" is having the file
+ # not exist.
+ if ! [ -f merge/${RELNUM}/${F} ]; then
+ continue
+ fi
+
+ # If the file didn't exist in the old release, we're
+ # going to throw away the existing file and hope that
+ # the version from the new release is what we want.
+ if ! [ -f merge/${OLDRELNUM}/${F} ]; then
+ cp merge/${RELNUM}/${F} merge/new/${F}
+ continue
+ fi
+
+ # Some files need special treatment.
+ case ${F} in
+ /etc/spwd.db | /etc/pwd.db | /etc/login.conf.db)
+ # Don't merge these -- we're rebuild them
+ # after updates are installed.
+ cp merge/old/${F} merge/new/${F}
+ ;;
+ *)
+ if ! merge -p -L "current version" \
+ -L "${OLDRELNUM}" -L "${RELNUM}" \
+ merge/old/${F} \
+ merge/${OLDRELNUM}/${F} \
+ merge/${RELNUM}/${F} \
+ > merge/new/${F} 2>/dev/null; then
+ echo ${F} >> failed.merges
+ fi
+ ;;
+ esac
+ done < $1-paths
+ echo " done."
+
+ # Ask the user to handle any files which didn't merge.
+ while read F; do
+ cat <<-EOF
+
+The following file could not be merged automatically: ${F}
+Press Enter to edit this file in ${EDITOR} and resolve the conflicts
+manually...
+ EOF
+ read dummy </dev/tty
+ ${EDITOR} `pwd`/merge/new/${F} < /dev/tty
+ done < failed.merges
+ rm failed.merges
+
+ # Ask the user to confirm that he likes how the result
+ # of merging files.
+ while read F; do
+ # Skip files which haven't changed.
+ if [ -f merge/new/${F} ] &&
+ cmp -s merge/old/${F} merge/new/${F}; then
+ continue
+ fi
+
+ # Warn about files which are ceasing to exist.
+ if ! [ -f merge/new/${F} ]; then
+ cat <<-EOF
+
+The following file will be removed, as it no longer exists in
+FreeBSD ${RELNUM}: ${F}
+ EOF
+ continuep < /dev/tty || return 1
+ continue
+ fi
+
+ # Print changes for the user's approval.
+ cat <<-EOF
+
+The following changes, which occurred between FreeBSD ${OLDRELNUM} and
+FreeBSD ${RELNUM} have been merged into ${F}:
+EOF
+ diff -U 5 -L "current version" -L "new version" \
+ merge/old/${F} merge/new/${F} || true
+ continuep < /dev/tty || return 1
+ done < $1-paths
+
+ # Store merged files.
+ while read F; do
+ if [ -f merge/new/${F} ]; then
+ V=`${SHA256} -q merge/new/${F}`
+
+ gzip -c < merge/new/${F} > files/${V}.gz
+ echo "${F}|${V}"
+ fi
+ done < $1-paths > newhashes
+
+ # Pull lines out from $3 which need to be updated to
+ # reflect merged files.
+ while read F; do
+ look "${F}|" $3
+ done < $1-paths > $3-oldlines
+
+ # Update lines to reflect merged files
+ join -t '|' -o 1.1,1.2,1.3,1.4,1.5,1.6,2.2,1.8 \
+ $3-oldlines newhashes > $3-newlines
+
+ # Remove old lines from $3 and add new lines.
+ sort $3-oldlines |
+ comm -13 - $3 |
+ sort - $3-newlines > $3.tmp
+ mv $3.tmp $3
+
+ # Clean up
+ rm $1-paths newhashes $3-oldlines $3-newlines
+ rm -rf merge/
+ fi
+
+ # We're done with merging files.
+ rm $1
+}
+
+# Do the work involved in fetching upgrades to a new release
+upgrade_run () {
+ workdir_init || return 1
+
+ # Prepare the mirror list.
+ fetch_pick_server_init && fetch_pick_server
+
+ # Try to fetch the public key until we run out of servers.
+ while ! fetch_key; do
+ fetch_pick_server || return 1
+ done
+
+ # Try to fetch the metadata index signature ("tag") until we run
+ # out of available servers; and sanity check the downloaded tag.
+ while ! fetch_tag; do
+ fetch_pick_server || return 1
+ done
+ fetch_tagsanity || return 1
+
+ # Fetch the INDEX-OLD and INDEX-ALL.
+ fetch_metadata INDEX-OLD INDEX-ALL || return 1
+
+ # If StrictComponents is not "yes", generate a new components list
+ # with only the components which appear to be installed.
+ upgrade_guess_components INDEX-ALL || return 1
+
+ # Generate filtered INDEX-OLD and INDEX-ALL files containing only
+ # the components we want and without anything marked as "Ignore".
+ fetch_filter_metadata INDEX-OLD || return 1
+ fetch_filter_metadata INDEX-ALL || return 1
+
+ # Merge the INDEX-OLD and INDEX-ALL files into INDEX-OLD.
+ sort INDEX-OLD INDEX-ALL > INDEX-OLD.tmp
+ mv INDEX-OLD.tmp INDEX-OLD
+ rm INDEX-ALL
+
+ # Adjust variables for fetching files from the new release.
+ OLDRELNUM=${RELNUM}
+ RELNUM=${TARGETRELEASE}
+ OLDFETCHDIR=${FETCHDIR}
+ FETCHDIR=${RELNUM}/${ARCH}
+
+ # Try to fetch the NEW metadata index signature ("tag") until we run
+ # out of available servers; and sanity check the downloaded tag.
+ while ! fetch_tag; do
+ fetch_pick_server || return 1
+ done
+
+ # Fetch the new INDEX-ALL.
+ fetch_metadata INDEX-ALL || return 1
+
+ # If StrictComponents is not "yes", COMPONENTS contains an entry
+ # corresponding to the currently running kernel, and said kernel
+ # does not exist in the new release, add "kernel/generic" to the
+ # list of components.
+ upgrade_guess_new_kernel INDEX-ALL || return 1
+
+ # Filter INDEX-ALL to contain only the components we want and without
+ # anything marked as "Ignore".
+ fetch_filter_metadata INDEX-ALL || return 1
+
+ # Convert INDEX-OLD (last release) and INDEX-ALL (new release) into
+ # INDEX-OLD and INDEX-NEW files (in the sense of normal upgrades).
+ upgrade_oldall_to_oldnew INDEX-OLD INDEX-ALL INDEX-NEW
+
+ # Translate /boot/${KERNCONF} or /boot/${NKERNCONF} into ${KERNELDIR}
+ fetch_filter_kernel_names INDEX-NEW ${NKERNCONF}
+ fetch_filter_kernel_names INDEX-OLD ${KERNCONF}
+
+ # For all paths appearing in INDEX-OLD or INDEX-NEW, inspect the
+ # system and generate an INDEX-PRESENT file.
+ fetch_inspect_system INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
+
+ # Based on ${MERGECHANGES}, generate a file tomerge-old with the
+ # paths and hashes of old versions of files to merge.
+ fetch_filter_mergechanges INDEX-OLD INDEX-PRESENT tomerge-old
+
+ # Based on ${UPDATEIFUNMODIFIED}, remove lines from INDEX-* which
+ # correspond to lines in INDEX-PRESENT with hashes not appearing
+ # in INDEX-OLD or INDEX-NEW. Also remove lines where the entry in
+ # INDEX-PRESENT has type - and there isn't a corresponding entry in
+ # INDEX-OLD with type -.
+ fetch_filter_unmodified_notpresent \
+ INDEX-OLD INDEX-PRESENT INDEX-NEW tomerge-old
+
+ # For each entry in INDEX-PRESENT of type -, remove any corresponding
+ # entry from INDEX-NEW if ${ALLOWADD} != "yes". Remove all entries
+ # of type - from INDEX-PRESENT.
+ fetch_filter_allowadd INDEX-PRESENT INDEX-NEW
+
+ # If ${ALLOWDELETE} != "yes", then remove any entries from
+ # INDEX-PRESENT which don't correspond to entries in INDEX-NEW.
+ fetch_filter_allowdelete INDEX-PRESENT INDEX-NEW
+
+ # If ${KEEPMODIFIEDMETADATA} == "yes", then for each entry in
+ # INDEX-PRESENT with metadata not matching any entry in INDEX-OLD,
+ # replace the corresponding line of INDEX-NEW with one having the
+ # same metadata as the entry in INDEX-PRESENT.
+ fetch_filter_modified_metadata INDEX-OLD INDEX-PRESENT INDEX-NEW
+
+ # Remove lines from INDEX-PRESENT and INDEX-NEW which are identical;
+ # no need to update a file if it isn't changing.
+ fetch_filter_uptodate INDEX-PRESENT INDEX-NEW
+
+ # Fetch "clean" files from the old release for merging changes.
+ fetch_files_premerge tomerge-old
+
+ # Prepare to fetch files: Generate a list of the files we need,
+ # copy the unmodified files we have into /files/, and generate
+ # a list of patches to download.
+ fetch_files_prepare INDEX-OLD INDEX-PRESENT INDEX-NEW || return 1
+
+ # Fetch patches from to-${RELNUM}/${ARCH}/bp/
+ PATCHDIR=to-${RELNUM}/${ARCH}/bp
+ fetch_files || return 1
+
+ # Merge configuration file changes.
+ upgrade_merge tomerge-old INDEX-PRESENT INDEX-NEW || return 1
+
+ # Create and populate install manifest directory; and report what
+ # updates are available.
+ fetch_create_manifest || return 1
+
+ # Leave a note behind to tell the "install" command that the kernel
+ # needs to be installed before the world.
+ touch ${BDHASH}-install/kernelfirst
+}
+
+# Make sure that all the file hashes mentioned in $@ have corresponding
+# gzipped files stored in /files/.
+install_verify () {
+ # Generate a list of hashes
+ cat $@ |
+ cut -f 2,7 -d '|' |
+ grep -E '^f' |
+ cut -f 2 -d '|' |
+ sort -u > filelist
+
+ # Make sure all the hashes exist
+ while read HASH; do
+ if ! [ -f files/${HASH}.gz ]; then
+ echo -n "Update files missing -- "
+ echo "this should never happen."
+ echo "Re-run '$0 fetch'."
+ return 1
+ fi
+ done < filelist
+
+ # Clean up
+ rm filelist
+}
+
+# Remove the system immutable flag from files
+install_unschg () {
+ # Generate file list
+ cat $@ |
+ cut -f 1 -d '|' > filelist
+
+ # Remove flags
+ while read F; do
+ if ! [ -e ${BASEDIR}/${F} ]; then
+ continue
+ fi
+
+ chflags noschg ${BASEDIR}/${F} || return 1
+ done < filelist
+
+ # Clean up
+ rm filelist
+}
+
+# Install new files
+install_from_index () {
+ # First pass: Do everything apart from setting file flags. We
+ # can't set flags yet, because schg inhibits hard linking.
+ sort -k 1,1 -t '|' $1 |
+ tr '|' ' ' |
+ while read FPATH TYPE OWNER GROUP PERM FLAGS HASH LINK; do
+ case ${TYPE} in
+ d)
+ # Create a directory
+ install -d -o ${OWNER} -g ${GROUP} \
+ -m ${PERM} ${BASEDIR}/${FPATH}
+ ;;
+ f)
+ if [ -z "${LINK}" ]; then
+ # Create a file, without setting flags.
+ gunzip < files/${HASH}.gz > ${HASH}
+ install -S -o ${OWNER} -g ${GROUP} \
+ -m ${PERM} ${HASH} ${BASEDIR}/${FPATH}
+ rm ${HASH}
+ else
+ # Create a hard link.
+ ln -f ${BASEDIR}/${LINK} ${BASEDIR}/${FPATH}
+ fi
+ ;;
+ L)
+ # Create a symlink
+ ln -sfh ${HASH} ${BASEDIR}/${FPATH}
+ ;;
+ esac
+ done
+
+ # Perform a second pass, adding file flags.
+ tr '|' ' ' < $1 |
+ while read FPATH TYPE OWNER GROUP PERM FLAGS HASH LINK; do
+ if [ ${TYPE} = "f" ] &&
+ ! [ ${FLAGS} = "0" ]; then
+ chflags ${FLAGS} ${BASEDIR}/${FPATH}
+ fi
+ done
+}
+
+# Remove files which we want to delete
+install_delete () {
+ # Generate list of new files
+ cut -f 1 -d '|' < $2 |
+ sort > newfiles
+
+ # Generate subindex of old files we want to nuke
+ sort -k 1,1 -t '|' $1 |
+ join -t '|' -v 1 - newfiles |
+ sort -r -k 1,1 -t '|' |
+ cut -f 1,2 -d '|' |
+ tr '|' ' ' > killfiles
+
+ # Remove the offending bits
+ while read FPATH TYPE; do
+ case ${TYPE} in
+ d)
+ rmdir ${BASEDIR}/${FPATH}
+ ;;
+ f)
+ rm ${BASEDIR}/${FPATH}
+ ;;
+ L)
+ rm ${BASEDIR}/${FPATH}
+ ;;
+ esac
+ done < killfiles
+
+ # Clean up
+ rm newfiles killfiles
+}
+
+# Install new files, delete old files, and update linker.hints
+install_files () {
+ # If we haven't already dealt with the kernel, deal with it.
+ if ! [ -f $1/kerneldone ]; then
+ grep -E '^/boot/' $1/INDEX-OLD > INDEX-OLD
+ grep -E '^/boot/' $1/INDEX-NEW > INDEX-NEW
+
+ # Install new files
+ install_from_index INDEX-NEW || return 1
+
+ # Remove files which need to be deleted
+ install_delete INDEX-OLD INDEX-NEW || return 1
+
+ # Update linker.hints if necessary
+ if [ -s INDEX-OLD -o -s INDEX-NEW ]; then
+ kldxref -R /boot/ 2>/dev/null
+ fi
+
+ # We've finished updating the kernel.
+ touch $1/kerneldone
+
+ # Do we need to ask for a reboot now?
+ if [ -f $1/kernelfirst ] &&
+ [ -s INDEX-OLD -o -s INDEX-NEW ]; then
+ cat <<-EOF
+
+Kernel updates have been installed. Please reboot and run
+"$0 install" again to finish installing updates.
+ EOF
+ exit 0
+ fi
+ fi
+
+ # If we haven't already dealt with the world, deal with it.
+ if ! [ -f $1/worlddone ]; then
+ # Install new shared libraries next
+ grep -vE '^/boot/' $1/INDEX-NEW |
+ grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW
+ install_from_index INDEX-NEW || return 1
+
+ # Deal with everything else
+ grep -vE '^/boot/' $1/INDEX-OLD |
+ grep -vE '/lib/.*\.so\.[0-9]+\|' > INDEX-OLD
+ grep -vE '^/boot/' $1/INDEX-NEW |
+ grep -vE '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW
+ install_from_index INDEX-NEW || return 1
+ install_delete INDEX-OLD INDEX-NEW || return 1
+
+ # Rebuild /etc/spwd.db and /etc/pwd.db if necessary.
+ if [ /etc/master.passwd -nt /etc/spwd.db ] ||
+ [ /etc/master.passwd -nt /etc/pwd.db ]; then
+ pwd_mkdb /etc/master.passwd
+ fi
+
+ # Rebuild /etc/login.conf.db if necessary.
+ if [ /etc/login.conf -nt /etc/login.conf.db ]; then
+ cap_mkdb /etc/login.conf
+ fi
+
+ # We've finished installing the world and deleting old files
+ # which are not shared libraries.
+ touch $1/worlddone
+
+ # Do we need to ask the user to portupgrade now?
+ grep -vE '^/boot/' $1/INDEX-NEW |
+ grep -E '/lib/.*\.so\.[0-9]+\|' |
+ cut -f 1 -d '|' |
+ sort > newfiles
+ if grep -vE '^/boot/' $1/INDEX-OLD |
+ grep -E '/lib/.*\.so\.[0-9]+\|' |
+ cut -f 1 -d '|' |
+ sort |
+ join -v 1 - newfiles |
+ grep -q .; then
+ cat <<-EOF
+
+Completing this upgrade requires removing old shared object files.
+Please rebuild all installed 3rd party software (e.g., programs
+installed from the ports tree) and then run "$0 install"
+again to finish installing updates.
+ EOF
+ rm newfiles
+ exit 0
+ fi
+ rm newfiles
+ fi
+
+ # Remove old shared libraries
+ grep -vE '^/boot/' $1/INDEX-NEW |
+ grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW
+ grep -vE '^/boot/' $1/INDEX-OLD |
+ grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-OLD
+ install_delete INDEX-OLD INDEX-NEW || return 1
+
+ # Remove temporary files
+ rm INDEX-OLD INDEX-NEW
+}
+
+# Rearrange bits to allow the installed updates to be rolled back
+install_setup_rollback () {
+ # Remove the "reboot after installing kernel", "kernel updated", and
+ # "finished installing the world" flags if present -- they are
+ # irrelevant when rolling back updates.
+ if [ -f ${BDHASH}-install/kernelfirst ]; then
+ rm ${BDHASH}-install/kernelfirst
+ rm ${BDHASH}-install/kerneldone
+ fi
+ if [ -f ${BDHASH}-install/worlddone ]; then
+ rm ${BDHASH}-install/worlddone
+ fi
+
+ if [ -L ${BDHASH}-rollback ]; then
+ mv ${BDHASH}-rollback ${BDHASH}-install/rollback
+ fi
+
+ mv ${BDHASH}-install ${BDHASH}-rollback
+}
+
+# Actually install updates
+install_run () {
+ echo -n "Installing updates..."
+
+ # Make sure we have all the files we should have
+ install_verify ${BDHASH}-install/INDEX-OLD \
+ ${BDHASH}-install/INDEX-NEW || return 1
+
+ # Remove system immutable flag from files
+ install_unschg ${BDHASH}-install/INDEX-OLD \
+ ${BDHASH}-install/INDEX-NEW || return 1
+
+ # Install new files, delete old files, and update linker.hints
+ install_files ${BDHASH}-install || return 1
+
+ # Rearrange bits to allow the installed updates to be rolled back
+ install_setup_rollback
+
+ echo " done."
+}
+
+# Rearrange bits to allow the previous set of updates to be rolled back next.
+rollback_setup_rollback () {
+ if [ -L ${BDHASH}-rollback/rollback ]; then
+ mv ${BDHASH}-rollback/rollback rollback-tmp
+ rm -r ${BDHASH}-rollback/
+ rm ${BDHASH}-rollback
+ mv rollback-tmp ${BDHASH}-rollback
+ else
+ rm -r ${BDHASH}-rollback/
+ rm ${BDHASH}-rollback
+ fi
+}
+
+# Install old files, delete new files, and update linker.hints
+rollback_files () {
+ # Install old shared library files which don't have the same path as
+ # a new shared library file.
+ grep -vE '^/boot/' $1/INDEX-NEW |
+ grep -E '/lib/.*\.so\.[0-9]+\|' |
+ cut -f 1 -d '|' |
+ sort > INDEX-NEW.libs.flist
+ grep -vE '^/boot/' $1/INDEX-OLD |
+ grep -E '/lib/.*\.so\.[0-9]+\|' |
+ sort -k 1,1 -t '|' - |
+ join -t '|' -v 1 - INDEX-NEW.libs.flist > INDEX-OLD
+ install_from_index INDEX-OLD || return 1
+
+ # Deal with files which are neither kernel nor shared library
+ grep -vE '^/boot/' $1/INDEX-OLD |
+ grep -vE '/lib/.*\.so\.[0-9]+\|' > INDEX-OLD
+ grep -vE '^/boot/' $1/INDEX-NEW |
+ grep -vE '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW
+ install_from_index INDEX-OLD || return 1
+ install_delete INDEX-NEW INDEX-OLD || return 1
+
+ # Install any old shared library files which we didn't install above.
+ grep -vE '^/boot/' $1/INDEX-OLD |
+ grep -E '/lib/.*\.so\.[0-9]+\|' |
+ sort -k 1,1 -t '|' - |
+ join -t '|' - INDEX-NEW.libs.flist > INDEX-OLD
+ install_from_index INDEX-OLD || return 1
+
+ # Delete unneeded shared library files
+ grep -vE '^/boot/' $1/INDEX-OLD |
+ grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-OLD
+ grep -vE '^/boot/' $1/INDEX-NEW |
+ grep -E '/lib/.*\.so\.[0-9]+\|' > INDEX-NEW
+ install_delete INDEX-NEW INDEX-OLD || return 1
+
+ # Deal with kernel files
+ grep -E '^/boot/' $1/INDEX-OLD > INDEX-OLD
+ grep -E '^/boot/' $1/INDEX-NEW > INDEX-NEW
+ install_from_index INDEX-OLD || return 1
+ install_delete INDEX-NEW INDEX-OLD || return 1
+ if [ -s INDEX-OLD -o -s INDEX-NEW ]; then
+ kldxref -R /boot/ 2>/dev/null
+ fi
+
+ # Remove temporary files
+ rm INDEX-OLD INDEX-NEW INDEX-NEW.libs.flist
+}
+
+# Actually rollback updates
+rollback_run () {
+ echo -n "Uninstalling updates..."
+
+ # If there are updates waiting to be installed, remove them; we
+ # want the user to re-run 'fetch' after rolling back updates.
+ if [ -L ${BDHASH}-install ]; then
+ rm -r ${BDHASH}-install/
+ rm ${BDHASH}-install
+ fi
+
+ # Make sure we have all the files we should have
+ install_verify ${BDHASH}-rollback/INDEX-NEW \
+ ${BDHASH}-rollback/INDEX-OLD || return 1
+
+ # Remove system immutable flag from files
+ install_unschg ${BDHASH}-rollback/INDEX-NEW \
+ ${BDHASH}-rollback/INDEX-OLD || return 1
+
+ # Install old files, delete new files, and update linker.hints
+ rollback_files ${BDHASH}-rollback || return 1
+
+ # Remove the rollback directory and the symlink pointing to it; and
+ # rearrange bits to allow the previous set of updates to be rolled
+ # back next.
+ rollback_setup_rollback
+
+ echo " done."
+}
+
+# Compare INDEX-ALL and INDEX-PRESENT and print warnings about differences.
+IDS_compare () {
+ # Get all the lines which mismatch in something other than file
+ # flags. We ignore file flags because sysinstall doesn't seem to
+ # set them when it installs FreeBSD; warning about these adds a
+ # very large amount of noise.
+ cut -f 1-5,7-8 -d '|' $1 > $1.noflags
+ sort -k 1,1 -t '|' $1.noflags > $1.sorted
+ cut -f 1-5,7-8 -d '|' $2 |
+ comm -13 $1.noflags - |
+ fgrep -v '|-|||||' |
+ sort -k 1,1 -t '|' |
+ join -t '|' $1.sorted - > INDEX-NOTMATCHING
+
+ # Ignore files which match IDSIGNOREPATHS.
+ for X in ${IDSIGNOREPATHS}; do
+ grep -E "^${X}" INDEX-NOTMATCHING
+ done |
+ sort -u |
+ comm -13 - INDEX-NOTMATCHING > INDEX-NOTMATCHING.tmp
+ mv INDEX-NOTMATCHING.tmp INDEX-NOTMATCHING
+
+ # Go through the lines and print warnings.
+ while read LINE; do
+ FPATH=`echo "${LINE}" | cut -f 1 -d '|'`
+ TYPE=`echo "${LINE}" | cut -f 2 -d '|'`
+ OWNER=`echo "${LINE}" | cut -f 3 -d '|'`
+ GROUP=`echo "${LINE}" | cut -f 4 -d '|'`
+ PERM=`echo "${LINE}" | cut -f 5 -d '|'`
+ HASH=`echo "${LINE}" | cut -f 6 -d '|'`
+ LINK=`echo "${LINE}" | cut -f 7 -d '|'`
+ P_TYPE=`echo "${LINE}" | cut -f 8 -d '|'`
+ P_OWNER=`echo "${LINE}" | cut -f 9 -d '|'`
+ P_GROUP=`echo "${LINE}" | cut -f 10 -d '|'`
+ P_PERM=`echo "${LINE}" | cut -f 11 -d '|'`
+ P_HASH=`echo "${LINE}" | cut -f 12 -d '|'`
+ P_LINK=`echo "${LINE}" | cut -f 13 -d '|'`
+
+ # Warn about different object types.
+ if ! [ "${TYPE}" = "${P_TYPE}" ]; then
+ echo -n "${FPATH} is a "
+ case "${P_TYPE}" in
+ f) echo -n "regular file, "
+ ;;
+ d) echo -n "directory, "
+ ;;
+ L) echo -n "symlink, "
+ ;;
+ esac
+ echo -n "but should be a "
+ case "${TYPE}" in
+ f) echo -n "regular file."
+ ;;
+ d) echo -n "directory."
+ ;;
+ L) echo -n "symlink."
+ ;;
+ esac
+ echo
+
+ # Skip other tests, since they don't make sense if
+ # we're comparing different object types.
+ continue
+ fi
+
+ # Warn about different owners.
+ if ! [ "${OWNER}" = "${P_OWNER}" ]; then
+ echo -n "${FPATH} is owned by user id ${P_OWNER}, "
+ echo "but should be owned by user id ${OWNER}."
+ fi
+
+ # Warn about different groups.
+ if ! [ "${GROUP}" = "${P_GROUP}" ]; then
+ echo -n "${FPATH} is owned by group id ${P_GROUP}, "
+ echo "but should be owned by group id ${GROUP}."
+ fi
+
+ # Warn about different permissions. We do not warn about
+ # different permissions on symlinks, since some archivers
+ # don't extract symlink permissions correctly and they are
+ # ignored anyway.
+ if ! [ "${PERM}" = "${P_PERM}" ] &&
+ ! [ "${TYPE}" = "L" ]; then
+ echo -n "${FPATH} has ${P_PERM} permissions, "
+ echo "but should have ${PERM} permissions."
+ fi
+
+ # Warn about different file hashes / symlink destinations.
+ if ! [ "${HASH}" = "${P_HASH}" ]; then
+ if [ "${TYPE}" = "L" ]; then
+ echo -n "${FPATH} is a symlink to ${P_HASH}, "
+ echo "but should be a symlink to ${HASH}."
+ fi
+ if [ "${TYPE}" = "f" ]; then
+ echo -n "${FPATH} has SHA256 hash ${P_HASH}, "
+ echo "but should have SHA256 hash ${HASH}."
+ fi
+ fi
+
+ # We don't warn about different hard links, since some
+ # some archivers break hard links, and as long as the
+ # underlying data is correct they really don't matter.
+ done < INDEX-NOTMATCHING
+
+ # Clean up
+ rm $1 $1.noflags $1.sorted $2 INDEX-NOTMATCHING
+}
+
+# Do the work involved in comparing the system to a "known good" index
+IDS_run () {
+ workdir_init || return 1
+
+ # Prepare the mirror list.
+ fetch_pick_server_init && fetch_pick_server
+
+ # Try to fetch the public key until we run out of servers.
+ while ! fetch_key; do
+ fetch_pick_server || return 1
+ done
+
+ # Try to fetch the metadata index signature ("tag") until we run
+ # out of available servers; and sanity check the downloaded tag.
+ while ! fetch_tag; do
+ fetch_pick_server || return 1
+ done
+ fetch_tagsanity || return 1
+
+ # Fetch INDEX-OLD and INDEX-ALL.
+ fetch_metadata INDEX-OLD INDEX-ALL || return 1
+
+ # Generate filtered INDEX-OLD and INDEX-ALL files containing only
+ # the components we want and without anything marked as "Ignore".
+ fetch_filter_metadata INDEX-OLD || return 1
+ fetch_filter_metadata INDEX-ALL || return 1
+
+ # Merge the INDEX-OLD and INDEX-ALL files into INDEX-ALL.
+ sort INDEX-OLD INDEX-ALL > INDEX-ALL.tmp
+ mv INDEX-ALL.tmp INDEX-ALL
+ rm INDEX-OLD
+
+ # Translate /boot/${KERNCONF} to ${KERNELDIR}
+ fetch_filter_kernel_names INDEX-ALL ${KERNCONF}
+
+ # Inspect the system and generate an INDEX-PRESENT file.
+ fetch_inspect_system INDEX-ALL INDEX-PRESENT /dev/null || return 1
+
+ # Compare INDEX-ALL and INDEX-PRESENT and print warnings about any
+ # differences.
+ IDS_compare INDEX-ALL INDEX-PRESENT
+}
+
+#### Main functions -- call parameter-handling and core functions
+
+# Using the command line, configuration file, and defaults,
+# set all the parameters which are needed later.
+get_params () {
+ init_params
+ parse_cmdline $@
+ parse_conffile
+ default_params
+}
+
+# Fetch command. Make sure that we're being called
+# interactively, then run fetch_check_params and fetch_run
+cmd_fetch () {
+ if [ ! -t 0 ]; then
+ echo -n "`basename $0` fetch should not "
+ echo "be run non-interactively."
+ echo "Run `basename $0` cron instead."
+ exit 1
+ fi
+ fetch_check_params
+ fetch_run || exit 1
+}
+
+# Cron command. Make sure the parameters are sensible; wait
+# rand(3600) seconds; then fetch updates. While fetching updates,
+# send output to a temporary file; only print that file if the
+# fetching failed.
+cmd_cron () {
+ fetch_check_params
+ sleep `jot -r 1 0 3600`
+
+ TMPFILE=`mktemp /tmp/freebsd-update.XXXXXX` || exit 1
+ if ! fetch_run >> ${TMPFILE} ||
+ ! grep -q "No updates needed" ${TMPFILE} ||
+ [ ${VERBOSELEVEL} = "debug" ]; then
+ mail -s "`hostname` security updates" ${MAILTO} < ${TMPFILE}
+ fi
+
+ rm ${TMPFILE}
+}
+
+# Fetch files for upgrading to a new release.
+cmd_upgrade () {
+ upgrade_check_params
+ upgrade_run || exit 1
+}
+
+# Install downloaded updates.
+cmd_install () {
+ install_check_params
+ install_run || exit 1
+}
+
+# Rollback most recently installed updates.
+cmd_rollback () {
+ rollback_check_params
+ rollback_run || exit 1
+}
+
+# Compare system against a "known good" index.
+cmd_IDS () {
+ IDS_check_params
+ IDS_run || exit 1
+}
+
+#### Entry point
+
+# Make sure we find utilities from the base system
+export PATH=/sbin:/bin:/usr/sbin:/usr/bin:${PATH}
+
+# Set LC_ALL in order to avoid problems with character ranges like [A-Z].
+export LC_ALL=C
+
+get_params $@
+for COMMAND in ${COMMANDS}; do
+ cmd_${COMMAND}
+done
diff --git a/usr.sbin/ftp-proxy/Makefile b/usr.sbin/ftp-proxy/Makefile
new file mode 100644
index 0000000..67660c8
--- /dev/null
+++ b/usr.sbin/ftp-proxy/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+SUBDIR= libevent ftp-proxy
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/ftp-proxy/Makefile.inc b/usr.sbin/ftp-proxy/Makefile.inc
new file mode 100644
index 0000000..5abb7c0
--- /dev/null
+++ b/usr.sbin/ftp-proxy/Makefile.inc
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+LIBEVENT= ${.OBJDIR}/../libevent/libevent.a
+
+.include "../Makefile.inc" \ No newline at end of file
diff --git a/usr.sbin/ftp-proxy/ftp-proxy/Makefile b/usr.sbin/ftp-proxy/ftp-proxy/Makefile
new file mode 100644
index 0000000..773c25d
--- /dev/null
+++ b/usr.sbin/ftp-proxy/ftp-proxy/Makefile
@@ -0,0 +1,18 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/pf/ftp-proxy
+
+PROG= ftp-proxy
+MAN= ftp-proxy.8
+
+SRCS= ftp-proxy.c filter.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/pf/libevent
+CFLAGS+= -I${.CURDIR}/../../../sys/contrib/pf
+
+DPADD= ${LIBEVENT}
+LDADD= ${LIBEVENT}
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ftp-proxy/libevent/Makefile b/usr.sbin/ftp-proxy/libevent/Makefile
new file mode 100644
index 0000000..acc546f
--- /dev/null
+++ b/usr.sbin/ftp-proxy/libevent/Makefile
@@ -0,0 +1,25 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/pf/libevent
+
+
+LIB= event
+INTERNALLIB=yes
+SRCS= buffer.c evbuffer.c event.c kqueue.c log.c poll.c select.c signal.c
+HDRS= event.h
+
+CFLAGS+= -I${.CURDIR} \
+ -DHAVE_CLOCK_GETTIME \
+ -DHAVE_FCNTL_H \
+ -DHAVE_POLL \
+ -DHAVE_SELECT \
+ -DHAVE_SETFD \
+ -DHAVE_STDARG_H \
+ -DHAVE_SYS_IOCTL_H \
+ -DHAVE_SYS_TIME_H \
+ -DHAVE_UNISTD_H \
+ -DHAVE_VASPRINTF \
+ -DHAVE_WORKING_KQUEUE \
+ -DVERSION='"1.3b"'
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/fwcontrol/Makefile b/usr.sbin/fwcontrol/Makefile
new file mode 100644
index 0000000..9694d5e
--- /dev/null
+++ b/usr.sbin/fwcontrol/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+PROG= fwcontrol
+SRCS= fwcontrol.c fwcrom.c fwdv.c fwmpegts.c
+MAN= fwcontrol.8
+WARNS= 3
+
+.PATH: ${.CURDIR}/../../sys/dev/firewire
+
+SDIR= ${.CURDIR}/../../sys
+CFLAGS+=-I${.CURDIR} -I${SDIR}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/fwcontrol/fwcontrol.8 b/usr.sbin/fwcontrol/fwcontrol.8
new file mode 100644
index 0000000..b936195
--- /dev/null
+++ b/usr.sbin/fwcontrol/fwcontrol.8
@@ -0,0 +1,220 @@
+.\" Copyright (c) 2002 Hidetoshi Shimokawa
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 September 12, 2008
+.Dt FWCONTROL 8
+.Os
+.Sh NAME
+.Nm fwcontrol
+.Nd FireWire control utility
+.Sh SYNOPSIS
+.Nm
+.Op Fl u Ar bus_num
+.Op Fl prt
+.Op Fl c Ar node
+.Op Fl d Ar node
+.Op Fl o Ar node
+.Op Fl s Ar node
+.Op Fl l Ar file
+.Op Fl f Ar node
+.Op Fl g Ar gap_count
+.Op Fl b Ar pri_req
+.Op Fl M Ar mode
+.Op Fl R Ar filename
+.Op Fl S Ar filename
+.Op Fl m Ar EUI64 | hostname
+.Sh DESCRIPTION
+The
+.Nm
+utility is designed to provide a way for users to access and control the
+.Fx
+FireWire subsystem.
+Without options,
+.Nm
+will output a list of devices that are/were connected to the bus.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl u Ar bus_num
+Specify the FireWire bus number to be operated on.
+The default is bus 0.
+.It Fl r
+Initiate bus reset.
+.It Fl t
+Show the topology map.
+.It Fl p
+Dump PHY registers.
+.It Fl c Ar node
+Show the configuration ROM on the node.
+.It Fl d Ar node
+Hex dump of the configuration ROM.
+.It Fl o Ar node
+Send a link-on PHY packet to the node.
+.It Fl s Ar node
+Write to the
+.Dv RESET_START
+register on the node.
+.It Fl l Ar file
+Load hex dump file of the configuration ROM and parse it.
+.It Fl f Ar node
+Force specified
+.Ar node
+to be the root node on the next bus reset by sending a PHY config packet.
+Valid values are 0 - 63.
+.It Fl g Ar gap_count
+Broadcast new
+.Ar gap_count
+by sending a PHY_config packet.
+By default this value is 63 on all nodes.
+Valid values are 0 - 63.
+.It Fl i Ar pri_req
+Set the
+.Dv PRIORITY_BUDGET
+register on all supported nodes.
+.It Fl M Ar mode
+Explicitly specify either
+.Ar dv
+or
+.Ar mpeg
+mode for the incoming stream.
+Only meaningful in case of and must precede the
+.Fl R
+option.
+If not specified, the program will try to guess.
+In case of
+.Dq format 0x20
+error, try to force the
+.Dq mpeg
+mode.
+.It Fl R Ar filename
+Receive DV or MPEG TS stream and dump it to a file.
+Use ^C to stop the receiving.
+Some DV cameras seem not to send the stream if a bus manager exists.
+If it is impossible to get the stream, try the following commands:
+.Bd -literal -offset indent
+sysctl hw.firewire.try_bmr=0
+fwcontrol -r
+.Ed
+.Pp
+The resulting file contains raw DV data excluding isochronous header
+and CIP header.
+It can be handled by
+.Nm libdv
+in the
+.Fx
+Ports Collection.
+Resulting MPEG TS stream can be played and sent over a
+network using the VideoLAN
+.Nm vlc
+tool in the
+.Fx
+Ports Collection.
+The stream can be piped directly to
+.Nm vlc,
+see
+.Sx EXAMPLES .
+.It Fl S Ar filename
+Send a DV file as isochronous stream.
+.It Fl m Ar EUI64 | hostname
+Set default fwmem target.
+Hostname will be converted to EUI64 using
+.Xr eui64 5 .
+.El
+.Sh FILES
+.Bl -tag
+.It Pa /dev/fw0.0
+.El
+.Sh EXAMPLES
+Each DV frame has a fixed size and it is easy to edit the frame order.
+.Pp
+.Dl "fwcontrol -R original.dv"
+.Pp
+Receive a DV stream with DV camera attached.
+.Pp
+.Dl "dd if=original.dv of=first.dv bs=120000 count=30"
+.Pp
+Get first 30 frames(NTSC).
+.Pp
+.Dl "dd if=original.dv of=second.dv bs=120000 skip=30 count=30"
+.Pp
+Get second 30 frames(NTSC).
+.Pp
+.Dl "cat second.dv first.dv | fwcontrol -S /dev/stdin"
+.Pp
+Swap first and second 30 frames and send them to DV recorder.
+.Pp
+For PAL, replace
+.Dq Li bs=120000
+with
+.Dq Li bs=144000 .
+.Pp
+.Dl "fwcontrol -R file.m2t
+.Pp
+Receive an MPEG TS stream from a camera producing MPEG transport stream.
+This has been tested with SONY HDR-FX1E camera that produces HD MPEG-2
+stream at 25 Mbps bandwidth.
+.Pp
+To send the stream from the camera over the network using TCP (which
+supprisingly works better with vlc), you can use
+.Dl "fwcontrol -R - | nc 192.168.10.11 9000
+with
+.Nm netcat
+from ports and to receive the stream, use
+.Dl nc -l -p 9000 | vlc -
+.Pp
+To netcast via UDP, you need to use
+.Nm buffer
+program from ports, since vlc is not fast enough to read UDP packets from
+buffers and thus it experiences dropouts when run directly.
+The sending side can use
+.Dl "fwcontrol -R - | nc 192.168.10.11 9000
+and to receive the stream, use
+.Dl nc -l -u -p 9000 | buffer -s 10k -b 1000 -m 20m -p 5 | vlc -
+.Pp
+For more information on how to work with
+.Nm vlc
+see its docs.
+.Sh SEE ALSO
+.Xr mplayer 1 ,
+.Xr vlc 1 ,
+.Xr firewire 4 ,
+.Xr fwe 4 ,
+.Xr fwip 4 ,
+.Xr fwohci 4 ,
+.Xr sbp 4
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 5.0 .
+.Sh AUTHORS
+.An Hidetoshi Shimokawa Aq simokawa@FreeBSD.org
+.An Petr Holub Aq hopet@ics.muni.cz
+- MPEG TS mode.
+.Sh BUGS
+This utility is still under development and provided for debugging purposes.
+Especially MPEG TS reception support is very rudimental and supports only
+high-bandwidth MPEG-2 streams (fn field in CIP header equals 3).
diff --git a/usr.sbin/fwcontrol/fwcontrol.c b/usr.sbin/fwcontrol/fwcontrol.c
new file mode 100644
index 0000000..509d25d
--- /dev/null
+++ b/usr.sbin/fwcontrol/fwcontrol.c
@@ -0,0 +1,1089 @@
+/*
+ * Copyright (C) 2002
+ * Hidetoshi Shimokawa. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Hidetoshi Shimokawa.
+ *
+ * 4. Neither the name of the author 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 defined(__FreeBSD__)
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+#endif
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#if defined(__FreeBSD__)
+#include <sys/eui64.h>
+#include <dev/firewire/firewire.h>
+#include <dev/firewire/iec13213.h>
+#include <dev/firewire/fwphyreg.h>
+#include <dev/firewire/iec68113.h>
+#elif defined(__NetBSD__)
+#include "eui64.h"
+#include <dev/ieee1394/firewire.h>
+#include <dev/ieee1394/iec13213.h>
+#include <dev/ieee1394/fwphyreg.h>
+#include <dev/ieee1394/iec68113.h>
+#else
+#warning "You need to add support for your OS"
+#endif
+
+
+#include <netinet/in.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include "fwmethods.h"
+
+static void sysctl_set_int(const char *, int);
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "%s [-u bus_num] [-prt] [-c node] [-d node] [-o node] [-s node]\n"
+ "\t [-l file] [-g gap_count] [-f force_root ] [-b pri_req]\n"
+ "\t [-M mode] [-R filename] [-S filename] [-m EUI64 | hostname]\n"
+ "\t-u: specify bus number\n"
+ "\t-p: Display current PHY register settings\n"
+ "\t-r: bus reset\n"
+ "\t-t: read topology map\n"
+ "\t-c: read configuration ROM\n"
+ "\t-d: hex dump of configuration ROM\n"
+ "\t-o: send link-on packet to the node\n"
+ "\t-s: write RESET_START register on the node\n"
+ "\t-l: load and parse hex dump file of configuration ROM\n"
+ "\t-g: set gap count\n"
+ "\t-f: force root node\n"
+ "\t-b: set PRIORITY_BUDGET register on all supported nodes\n"
+ "\t-M: specify dv or mpeg\n"
+ "\t-R: Receive DV or MPEG TS stream\n"
+ "\t-S: Send DV stream\n"
+ "\t-m: set fwmem target\n"
+ , getprogname() );
+ fprintf(stderr, "\n");
+}
+
+static void
+fweui2eui64(const struct fw_eui64 *fweui, struct eui64 *eui)
+{
+ *(u_int32_t*)&(eui->octet[0]) = htonl(fweui->hi);
+ *(u_int32_t*)&(eui->octet[4]) = htonl(fweui->lo);
+}
+
+static void
+get_dev(int fd, struct fw_devlstreq *data)
+{
+ if (data == NULL)
+ err(EX_SOFTWARE, "%s: data malloc", __func__);
+ if( ioctl(fd, FW_GDEVLST, data) < 0) {
+ err(EX_IOERR, "%s: ioctl", __func__);
+ }
+}
+
+static int
+str2node(int fd, const char *nodestr)
+{
+ struct eui64 eui, tmpeui;
+ struct fw_devlstreq *data;
+ char *endptr;
+ int i, node;
+
+ if (nodestr == '\0')
+ return (-1);
+
+ /*
+ * Deal with classic node specifications.
+ */
+ node = strtol(nodestr, &endptr, 0);
+ if (*endptr == '\0')
+ goto gotnode;
+
+ /*
+ * Try to get an eui and match it against available nodes.
+ */
+ if (eui64_hostton(nodestr, &eui) != 0 && eui64_aton(nodestr, &eui) != 0)
+ return (-1);
+
+ data = (struct fw_devlstreq *)malloc(sizeof(struct fw_devlstreq));
+ if (data == NULL)
+ err(EX_SOFTWARE, "%s: data malloc", __func__);
+ get_dev(fd,data);
+
+ for (i = 0; i < data->info_len; i++) {
+ fweui2eui64(&data->dev[i].eui, &tmpeui);
+ if (memcmp(&eui, &tmpeui, sizeof(struct eui64)) == 0) {
+ node = data->dev[i].dst;
+ if (data != NULL)
+ free(data);
+ goto gotnode;
+ }
+ }
+ if (i >= data->info_len) {
+ if (data != NULL)
+ free(data);
+ return (-1);
+ }
+
+gotnode:
+ if (node < 0 || node > 63)
+ return (-1);
+ else
+ return (node);
+}
+
+static void
+list_dev(int fd)
+{
+ struct fw_devlstreq *data;
+ struct fw_devinfo *devinfo;
+ struct eui64 eui;
+ char addr[EUI64_SIZ], hostname[40];
+ int i;
+
+ data = (struct fw_devlstreq *)malloc(sizeof(struct fw_devlstreq));
+ if (data == NULL)
+ err(EX_SOFTWARE, "%s:data malloc", __func__);
+ get_dev(fd, data);
+ printf("%d devices (info_len=%d)\n", data->n, data->info_len);
+ printf("node EUI64 status hostname\n");
+ for (i = 0; i < data->info_len; i++) {
+ devinfo = &data->dev[i];
+ fweui2eui64(&devinfo->eui, &eui);
+ eui64_ntoa(&eui, addr, sizeof(addr));
+ if (eui64_ntohost(hostname, sizeof(hostname), &eui))
+ hostname[0] = 0;
+ printf("%4d %s %6d %s\n",
+ (devinfo->status || i == 0) ? devinfo->dst : -1,
+ addr,
+ devinfo->status,
+ hostname
+ );
+ }
+ free((void *)data);
+}
+
+static u_int32_t
+read_write_quad(int fd, struct fw_eui64 eui, u_int32_t addr_lo, int readmode, u_int32_t data)
+{
+ struct fw_asyreq *asyreq;
+ u_int32_t *qld, res;
+
+ asyreq = (struct fw_asyreq *)malloc(sizeof(struct fw_asyreq_t) + 16);
+ if (asyreq == NULL)
+ err(EX_SOFTWARE, "%s:asyreq malloc", __func__);
+ asyreq->req.len = 16;
+#if 0
+ asyreq->req.type = FWASREQNODE;
+ asyreq->pkt.mode.rreqq.dst = FWLOCALBUS | node;
+#else
+ asyreq->req.type = FWASREQEUI;
+ asyreq->req.dst.eui = eui;
+#endif
+ asyreq->pkt.mode.rreqq.tlrt = 0;
+ if (readmode)
+ asyreq->pkt.mode.rreqq.tcode = FWTCODE_RREQQ;
+ else
+ asyreq->pkt.mode.rreqq.tcode = FWTCODE_WREQQ;
+
+ asyreq->pkt.mode.rreqq.dest_hi = 0xffff;
+ asyreq->pkt.mode.rreqq.dest_lo = addr_lo;
+
+ qld = (u_int32_t *)&asyreq->pkt;
+ if (!readmode)
+ asyreq->pkt.mode.wreqq.data = htonl(data);
+
+ if (ioctl(fd, FW_ASYREQ, asyreq) < 0) {
+ err(EX_IOERR, "%s: ioctl", __func__);
+ }
+ res = qld[3];
+ free(asyreq);
+ if (readmode)
+ return ntohl(res);
+ else
+ return 0;
+}
+
+/*
+ * Send a PHY Config Packet
+ * ieee 1394a-2005 4.3.4.3
+ *
+ * Message ID Root ID R T Gap Count
+ * 00(2 bits) (6 bits) 1 1 (6 bits)
+ *
+ * if "R" is set, then Root ID will be the next
+ * root node upon the next bus reset.
+ * if "T" is set, then Gap Count will be the
+ * value that all nodes use for their Gap Count
+ * if "R" and "T" are not set, then this message
+ * is either ignored or interpreted as an extended
+ * PHY config Packet as per 1394a-2005 4.3.4.4
+ */
+static void
+send_phy_config(int fd, int root_node, int gap_count)
+{
+ struct fw_asyreq *asyreq;
+
+ asyreq = (struct fw_asyreq *)malloc(sizeof(struct fw_asyreq_t) + 12);
+ if (asyreq == NULL)
+ err(EX_SOFTWARE, "%s:asyreq malloc", __func__);
+ asyreq->req.len = 12;
+ asyreq->req.type = FWASREQNODE;
+ asyreq->pkt.mode.ld[0] = 0;
+ asyreq->pkt.mode.ld[1] = 0;
+ asyreq->pkt.mode.common.tcode = FWTCODE_PHY;
+ if (root_node >= 0)
+ asyreq->pkt.mode.ld[1] |= ((root_node << 24) | (1 << 23));
+ if (gap_count >= 0)
+ asyreq->pkt.mode.ld[1] |= ((1 << 22) | (gap_count << 16));
+ asyreq->pkt.mode.ld[2] = ~asyreq->pkt.mode.ld[1];
+
+ printf("send phy_config root_node=%d gap_count=%d\n",
+ root_node, gap_count);
+
+ if (ioctl(fd, FW_ASYREQ, asyreq) < 0)
+ err(EX_IOERR, "%s: ioctl", __func__);
+ free(asyreq);
+}
+
+static void
+link_on(int fd, int node)
+{
+ struct fw_asyreq *asyreq;
+
+ asyreq = (struct fw_asyreq *)malloc(sizeof(struct fw_asyreq_t) + 12);
+ if (asyreq == NULL)
+ err(EX_SOFTWARE, "%s:asyreq malloc", __func__);
+ asyreq->req.len = 12;
+ asyreq->req.type = FWASREQNODE;
+ asyreq->pkt.mode.common.tcode = FWTCODE_PHY;
+ asyreq->pkt.mode.ld[1] |= (1 << 30) | ((node & 0x3f) << 24);
+ asyreq->pkt.mode.ld[2] = ~asyreq->pkt.mode.ld[1];
+
+ if (ioctl(fd, FW_ASYREQ, asyreq) < 0)
+ err(EX_IOERR, "%s: ioctl", __func__);
+ free(asyreq);
+}
+
+static void
+reset_start(int fd, int node)
+{
+ struct fw_asyreq *asyreq;
+
+ asyreq = (struct fw_asyreq *)malloc(sizeof(struct fw_asyreq_t) + 16);
+ if (asyreq == NULL)
+ err(EX_SOFTWARE, "%s:asyreq malloc", __func__);
+ asyreq->req.len = 16;
+ asyreq->req.type = FWASREQNODE;
+ asyreq->pkt.mode.wreqq.dst = FWLOCALBUS | (node & 0x3f);
+ asyreq->pkt.mode.wreqq.tlrt = 0;
+ asyreq->pkt.mode.wreqq.tcode = FWTCODE_WREQQ;
+
+ asyreq->pkt.mode.wreqq.dest_hi = 0xffff;
+ asyreq->pkt.mode.wreqq.dest_lo = 0xf0000000 | RESET_START;
+
+ asyreq->pkt.mode.wreqq.data = htonl(0x1);
+
+ if (ioctl(fd, FW_ASYREQ, asyreq) < 0)
+ err(EX_IOERR, "%s: ioctl", __func__);
+ free(asyreq);
+}
+
+static void
+set_pri_req(int fd, u_int32_t pri_req)
+{
+ struct fw_devlstreq *data;
+ struct fw_devinfo *devinfo;
+ struct eui64 eui;
+ char addr[EUI64_SIZ];
+ u_int32_t max, reg, old;
+ int i;
+
+ data = (struct fw_devlstreq *)malloc(sizeof(struct fw_devlstreq));
+ if (data == NULL)
+ err(EX_SOFTWARE, "%s:data malloc", __func__);
+ get_dev(fd, data);
+#define BUGET_REG 0xf0000218
+ for (i = 0; i < data->info_len; i++) {
+ devinfo = &data->dev[i];
+ if (!devinfo->status)
+ continue;
+ reg = read_write_quad(fd, devinfo->eui, BUGET_REG, 1, 0);
+ fweui2eui64(&devinfo->eui, &eui);
+ eui64_ntoa(&eui, addr, sizeof(addr));
+ printf("%d %s, %08x",
+ devinfo->dst, addr, reg);
+ if (reg > 0) {
+ old = (reg & 0x3f);
+ max = (reg & 0x3f00) >> 8;
+ if (pri_req > max)
+ pri_req = max;
+ printf(" 0x%x -> 0x%x\n", old, pri_req);
+ read_write_quad(fd, devinfo->eui, BUGET_REG, 0, pri_req);
+ } else {
+ printf("\n");
+ }
+ }
+ free((void *)data);
+}
+
+static void
+parse_bus_info_block(u_int32_t *p)
+{
+ char addr[EUI64_SIZ];
+ struct bus_info *bi;
+ struct eui64 eui;
+
+ bi = (struct bus_info *)p;
+ fweui2eui64(&bi->eui64, &eui);
+ eui64_ntoa(&eui, addr, sizeof(addr));
+ printf("bus_name: 0x%04x\n"
+ "irmc:%d cmc:%d isc:%d bmc:%d pmc:%d\n"
+ "cyc_clk_acc:%d max_rec:%d max_rom:%d\n"
+ "generation:%d link_spd:%d\n"
+ "EUI64: %s\n",
+ bi->bus_name,
+ bi->irmc, bi->cmc, bi->isc, bi->bmc, bi->pmc,
+ bi->cyc_clk_acc, bi->max_rec, bi->max_rom,
+ bi->generation, bi->link_spd,
+ addr);
+}
+
+static int
+get_crom(int fd, int node, void *crom_buf, int len)
+{
+ struct fw_crom_buf buf;
+ int i, error;
+ struct fw_devlstreq *data;
+
+ data = (struct fw_devlstreq *)malloc(sizeof(struct fw_devlstreq));
+ if (data == NULL)
+ err(EX_SOFTWARE, "%s:data malloc", __func__);
+ get_dev(fd, data);
+
+ for (i = 0; i < data->info_len; i++) {
+ if (data->dev[i].dst == node && data->dev[i].eui.lo != 0)
+ break;
+ }
+ if (i == data->info_len)
+ errx(1, "no such node %d.", node);
+ else
+ buf.eui = data->dev[i].eui;
+ free((void *)data);
+
+ buf.len = len;
+ buf.ptr = crom_buf;
+ bzero(crom_buf, len);
+ if ((error = ioctl(fd, FW_GCROM, &buf)) < 0) {
+ err(EX_IOERR, "%s: ioctl", __func__);
+ }
+
+ return error;
+}
+
+static void
+show_crom(u_int32_t *crom_buf)
+{
+ int i;
+ struct crom_context cc;
+ char *desc, info[256];
+ static const char *key_types = "ICLD";
+ struct csrreg *reg;
+ struct csrdirectory *dir;
+ struct csrhdr *hdr;
+ u_int16_t crc;
+
+ printf("first quad: 0x%08x ", *crom_buf);
+ if (crom_buf[0] == 0) {
+ printf("(Invalid Configuration ROM)\n");
+ return;
+ }
+ hdr = (struct csrhdr *)crom_buf;
+ if (hdr->info_len == 1) {
+ /* minimum ROM */
+ reg = (struct csrreg *)hdr;
+ printf("verndor ID: 0x%06x\n", reg->val);
+ return;
+ }
+ printf("info_len=%d crc_len=%d crc=0x%04x",
+ hdr->info_len, hdr->crc_len, hdr->crc);
+ crc = crom_crc(crom_buf+1, hdr->crc_len);
+ if (crc == hdr->crc)
+ printf("(OK)\n");
+ else
+ printf("(NG)\n");
+ parse_bus_info_block(crom_buf+1);
+
+ crom_init_context(&cc, crom_buf);
+ dir = cc.stack[0].dir;
+ if (!dir) {
+ printf("no root directory - giving up\n");
+ return;
+ }
+ printf("root_directory: len=0x%04x(%d) crc=0x%04x",
+ dir->crc_len, dir->crc_len, dir->crc);
+ crc = crom_crc((u_int32_t *)&dir->entry[0], dir->crc_len);
+ if (crc == dir->crc)
+ printf("(OK)\n");
+ else
+ printf("(NG)\n");
+ if (dir->crc_len < 1)
+ return;
+ while (cc.depth >= 0) {
+ desc = crom_desc(&cc, info, sizeof(info));
+ reg = crom_get(&cc);
+ for (i = 0; i < cc.depth; i++)
+ printf("\t");
+ printf("%02x(%c:%02x) %06x %s: %s\n",
+ reg->key,
+ key_types[(reg->key & CSRTYPE_MASK)>>6],
+ reg->key & CSRKEY_MASK, reg->val,
+ desc, info);
+ crom_next(&cc);
+ }
+}
+
+#define DUMP_FORMAT "%08x %08x %08x %08x %08x %08x %08x %08x\n"
+
+static void
+dump_crom(u_int32_t *p)
+{
+ int len=1024, i;
+
+ for (i = 0; i < len/(4*8); i ++) {
+ printf(DUMP_FORMAT,
+ p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7]);
+ p += 8;
+ }
+}
+
+static void
+load_crom(char *filename, u_int32_t *p)
+{
+ FILE *file;
+ int len=1024, i;
+
+ if ((file = fopen(filename, "r")) == NULL)
+ err(1, "load_crom %s", filename);
+ for (i = 0; i < len/(4*8); i ++) {
+ fscanf(file, DUMP_FORMAT,
+ p, p+1, p+2, p+3, p+4, p+5, p+6, p+7);
+ p += 8;
+ }
+}
+
+static void
+show_topology_map(int fd)
+{
+ struct fw_topology_map *tmap;
+ union fw_self_id sid;
+ int i;
+ static const char *port_status[] = {" ", "-", "P", "C"};
+ static const char *pwr_class[] = {" 0W", "15W", "30W", "45W",
+ "-1W", "-2W", "-5W", "-9W"};
+ static const char *speed[] = {"S100", "S200", "S400", "S800"};
+ tmap = malloc(sizeof(struct fw_topology_map));
+ if (tmap == NULL)
+ err(EX_SOFTWARE, "%s:tmap malloc", __func__);
+ if (ioctl(fd, FW_GTPMAP, tmap) < 0) {
+ err(EX_IOERR, "%s: ioctl", __func__);
+ }
+ printf("crc_len: %d generation:%d node_count:%d sid_count:%d\n",
+ tmap->crc_len, tmap->generation,
+ tmap->node_count, tmap->self_id_count);
+ printf("id link gap_cnt speed delay cIRM power port0 port1 port2"
+ " ini more\n");
+ for (i = 0; i < tmap->crc_len - 2; i++) {
+ sid = tmap->self_id[i];
+ if (sid.p0.sequel) {
+ printf("%02d sequel packet\n", sid.p0.phy_id);
+ continue;
+ }
+ printf("%02d %2d %2d %4s %d %3s"
+ " %s %s %s %d %d\n",
+ sid.p0.phy_id,
+ sid.p0.link_active,
+ sid.p0.gap_count,
+ speed[sid.p0.phy_speed],
+ sid.p0.contender,
+ pwr_class[sid.p0.power_class],
+ port_status[sid.p0.port0],
+ port_status[sid.p0.port1],
+ port_status[sid.p0.port2],
+ sid.p0.initiated_reset,
+ sid.p0.more_packets
+ );
+ }
+ free(tmap);
+}
+
+static void
+read_phy_registers(int fd, u_int8_t *buf, int offset, int len)
+{
+ struct fw_reg_req_t reg;
+ int i;
+
+ for (i = 0; i < len; i++) {
+ reg.addr = offset + i;
+ if (ioctl(fd, FWOHCI_RDPHYREG, &reg) < 0)
+ err(EX_IOERR, "%s: ioctl", __func__);
+ buf[i] = (u_int8_t) reg.data;
+ printf("0x%02x ", reg.data);
+ }
+ printf("\n");
+}
+
+static void
+read_phy_page(int fd, u_int8_t *buf, int page, int port)
+{
+ struct fw_reg_req_t reg;
+
+ reg.addr = 0x7;
+ reg.data = ((page & 7) << 5) | (port & 0xf);
+ if (ioctl(fd, FWOHCI_WRPHYREG, &reg) < 0)
+ err(EX_IOERR, "%s: ioctl", __func__);
+ read_phy_registers(fd, buf, 8, 8);
+}
+
+static void
+dump_phy_registers(int fd)
+{
+ struct phyreg_base b;
+ struct phyreg_page0 p;
+ struct phyreg_page1 v;
+ int i;
+
+ printf("=== base register ===\n");
+ read_phy_registers(fd, (u_int8_t *)&b, 0, 8);
+ printf(
+ "Physical_ID:%d R:%d CPS:%d\n"
+ "RHB:%d IBR:%d Gap_Count:%d\n"
+ "Extended:%d Num_Ports:%d\n"
+ "PHY_Speed:%d Delay:%d\n"
+ "LCtrl:%d C:%d Jitter:%d Pwr_Class:%d\n"
+ "WDIE:%d ISBR:%d CTOI:%d CPSI:%d STOI:%d PEI:%d EAA:%d EMC:%d\n"
+ "Max_Legacy_SPD:%d BLINK:%d Bridge:%d\n"
+ "Page_Select:%d Port_Select%d\n",
+ b.phy_id, b.r, b.cps,
+ b.rhb, b.ibr, b.gap_count,
+ b.extended, b.num_ports,
+ b.phy_speed, b.delay,
+ b.lctrl, b.c, b.jitter, b.pwr_class,
+ b.wdie, b.isbr, b.ctoi, b.cpsi, b.stoi, b.pei, b.eaa, b.emc,
+ b.legacy_spd, b.blink, b.bridge,
+ b.page_select, b.port_select
+ );
+
+ for (i = 0; i < b.num_ports; i ++) {
+ printf("\n=== page 0 port %d ===\n", i);
+ read_phy_page(fd, (u_int8_t *)&p, 0, i);
+ printf(
+ "Astat:%d BStat:%d Ch:%d Con:%d RXOK:%d Dis:%d\n"
+ "Negotiated_speed:%d PIE:%d Fault:%d Stanby_fault:%d Disscrm:%d B_Only:%d\n"
+ "DC_connected:%d Max_port_speed:%d LPP:%d Cable_speed:%d\n"
+ "Connection_unreliable:%d Beta_mode:%d\n"
+ "Port_error:0x%x\n"
+ "Loop_disable:%d In_standby:%d Hard_disable:%d\n",
+ p.astat, p.bstat, p.ch, p.con, p.rxok, p.dis,
+ p.negotiated_speed, p.pie, p.fault, p.stanby_fault, p.disscrm, p.b_only,
+ p.dc_connected, p.max_port_speed, p.lpp, p.cable_speed,
+ p.connection_unreliable, p.beta_mode,
+ p.port_error,
+ p.loop_disable, p.in_standby, p.hard_disable
+ );
+ }
+ printf("\n=== page 1 ===\n");
+ read_phy_page(fd, (u_int8_t *)&v, 1, 0);
+ printf(
+ "Compliance:%d\n"
+ "Vendor_ID:0x%06x\n"
+ "Product_ID:0x%06x\n",
+ v.compliance,
+ (v.vendor_id[0] << 16) | (v.vendor_id[1] << 8) | v.vendor_id[2],
+ (v.product_id[0] << 16) | (v.product_id[1] << 8) | v.product_id[2]
+ );
+}
+
+static int
+open_dev(int *fd, char *devname)
+{
+ if (*fd < 0) {
+ *fd = open(devname, O_RDWR);
+ if (*fd < 0)
+ return(-1);
+
+ }
+ return(0);
+}
+
+static void
+sysctl_set_int(const char *name, int val)
+{
+ if (sysctlbyname(name, NULL, NULL, &val, sizeof(int)) < 0)
+ err(1, "sysctl %s failed.", name);
+}
+
+static fwmethod *
+detect_recv_fn(int fd, char ich)
+{
+ char *buf;
+ struct fw_isochreq isoreq;
+ struct fw_isobufreq bufreq;
+ int len;
+ u_int32_t *ptr;
+ struct ciphdr *ciph;
+ fwmethod *retfn;
+#define RECV_NUM_PACKET 16
+#define RECV_PACKET_SZ 1024
+
+ bufreq.rx.nchunk = 8;
+ bufreq.rx.npacket = RECV_NUM_PACKET;
+ bufreq.rx.psize = RECV_PACKET_SZ;
+ bufreq.tx.nchunk = 0;
+ bufreq.tx.npacket = 0;
+ bufreq.tx.psize = 0;
+
+ if (ioctl(fd, FW_SSTBUF, &bufreq) < 0)
+ err(EX_IOERR, "%s: ioctl FW_SSTBUF", __func__);
+
+ isoreq.ch = ich & 0x3f;
+ isoreq.tag = (ich >> 6) & 3;
+
+ if (ioctl(fd, FW_SRSTREAM, &isoreq) < 0)
+ err(EX_IOERR, "%s: ioctl FW_SRSTREAM", __func__);
+
+ buf = (char *)malloc(RECV_NUM_PACKET * RECV_PACKET_SZ);
+ if (buf == NULL)
+ err(EX_SOFTWARE, "%s:buf malloc", __func__);
+ /*
+ * fwdev.c seems to return EIO on error and
+ * the return value of the last uiomove
+ * on success. For now, checking that the
+ * return is not less than zero should be
+ * sufficient. fwdev.c::fw_read() should
+ * return the total length read, not the value
+ * of the last uiomove().
+ */
+ len = read(fd, buf, RECV_NUM_PACKET * RECV_PACKET_SZ);
+ if (len < 0)
+ err(EX_IOERR, "%s: error reading from device", __func__);
+ ptr = (u_int32_t *) buf;
+ ciph = (struct ciphdr *)(ptr + 1);
+
+ switch(ciph->fmt) {
+ case CIP_FMT_DVCR:
+ fprintf(stderr, "Detected DV format on input.\n");
+ retfn = dvrecv;
+ break;
+ case CIP_FMT_MPEG:
+ fprintf(stderr, "Detected MPEG TS format on input.\n");
+ retfn = mpegtsrecv;
+ break;
+ default:
+ errx(1, "Unsupported format for receiving: fmt=0x%x", ciph->fmt);
+ }
+ free(buf);
+ return retfn;
+}
+
+int
+main(int argc, char **argv)
+{
+#define MAX_BOARDS 10
+ u_int32_t crom_buf[1024/4];
+ u_int32_t crom_buf_hex[1024/4];
+ char devbase[64];
+ const char *device_string = "/dev/fw";
+ int fd = -1, ch, len=1024;
+ int32_t current_board = 0;
+ /*
+ * If !command_set, then -u will display the nodes for the board.
+ * This emulates the previous behavior when -u is passed by itself
+ */
+ bool command_set = false;
+ bool open_needed = false;
+ long tmp;
+ struct fw_eui64 eui;
+ struct eui64 target;
+ fwmethod *recvfn = NULL;
+/*
+ * Holders for which functions
+ * to iterate through
+ */
+ bool display_board_only = false;
+ bool display_crom = false;
+ bool send_bus_reset = false;
+ bool display_crom_hex = false;
+ bool load_crom_from_file = false;
+ bool set_fwmem_target = false;
+ bool dump_topology = false;
+ bool dump_phy_reg = false;
+
+ int32_t priority_budget = -1;
+ int32_t set_root_node = -1;
+ int32_t set_gap_count = -1;
+ int32_t send_link_on = -1;
+ int32_t send_reset_start = -1;
+
+ char *crom_string = NULL;
+ char *crom_string_hex = NULL;
+ char *recv_data = NULL;
+ char *send_data = NULL;
+
+ if (argc < 2) {
+ for (current_board = 0; current_board < MAX_BOARDS; current_board++) {
+ snprintf(devbase, sizeof(devbase), "%s%d.0", device_string, current_board);
+ if (open_dev(&fd, devbase) < 0) {
+ if (current_board == 0) {
+ usage();
+ err(EX_IOERR, "%s: Error opening firewire controller #%d %s",
+ __func__, current_board, devbase);
+ }
+ return(EIO);
+ }
+ list_dev(fd);
+ close(fd);
+ fd = -1;
+ }
+ }
+ /*
+ * Parse all command line options, then execute requested operations.
+ */
+ while ((ch = getopt(argc, argv, "M:f:g:m:o:s:b:prtc:d:l:u:R:S:")) != -1) {
+ switch(ch) {
+ case 'b':
+ priority_budget = strtol(optarg, NULL, 0);
+ if (priority_budget < 0 || priority_budget > INT32_MAX)
+ errx(EX_USAGE, "%s: priority_budget out of range: %s", __func__, optarg);
+ command_set = true;
+ open_needed = true;
+ display_board_only = false;
+ break;
+ case 'c':
+ crom_string = malloc(strlen(optarg)+1);
+ if (crom_string == NULL)
+ err(EX_SOFTWARE, "%s:crom_string malloc", __func__);
+ if ( (strtol(crom_string, NULL, 0) < 0) || strtol(crom_string, NULL, 0) > MAX_BOARDS)
+ errx(EX_USAGE, "%s:Invalid value for node", __func__);
+ strcpy(crom_string, optarg);
+ display_crom = 1;
+ open_needed = true;
+ command_set = true;
+ display_board_only = false;
+ break;
+ case 'd':
+ crom_string_hex = malloc(strlen(optarg)+1);
+ if (crom_string_hex == NULL)
+ err(EX_SOFTWARE, "%s:crom_string_hex malloc", __func__);
+ strcpy(crom_string_hex, optarg);
+ display_crom_hex = 1;
+ open_needed = true;
+ command_set = true;
+ display_board_only = false;
+ break;
+ case 'f':
+#define MAX_PHY_CONFIG 0x3f
+ set_root_node = strtol(optarg, NULL, 0);
+ if ( (set_root_node < 0) || (set_root_node > MAX_PHY_CONFIG) )
+ errx(EX_USAGE, "%s:set_root_node out of range", __func__);
+ open_needed = true;
+ command_set = true;
+ display_board_only = false;
+ break;
+ case 'g':
+ set_gap_count = strtol(optarg, NULL, 0);
+ if ( (set_gap_count < 0) || (set_gap_count > MAX_PHY_CONFIG) )
+ errx(EX_USAGE, "%s:set_gap_count out of range", __func__);
+ open_needed = true;
+ command_set = true;
+ display_board_only = false;
+ break;
+ case 'l':
+ load_crom_from_file = 1;
+ load_crom(optarg, crom_buf);
+ command_set = true;
+ display_board_only = false;
+ break;
+ case 'm':
+ set_fwmem_target = 1;
+ open_needed = 0;
+ command_set = true;
+ display_board_only = false;
+ if (eui64_hostton(optarg, &target) != 0 &&
+ eui64_aton(optarg, &target) != 0)
+ errx(EX_USAGE, "%s: invalid target: %s", __func__, optarg);
+ break;
+ case 'o':
+ send_link_on = str2node(fd, optarg);
+ if ( (send_link_on < 0) || (send_link_on > MAX_PHY_CONFIG) )
+ errx(EX_USAGE, "%s: node out of range: %s\n",__func__, optarg);
+ open_needed = true;
+ command_set = true;
+ display_board_only = false;
+ break;
+ case 'p':
+ dump_phy_reg = 1;
+ open_needed = true;
+ command_set = true;
+ display_board_only = false;
+ break;
+ case 'r':
+ send_bus_reset = 1;
+ open_needed = true;
+ command_set = true;
+ display_board_only = false;
+ break;
+ case 's':
+ send_reset_start = str2node(fd, optarg);
+ if ( (send_reset_start < 0) || (send_reset_start > MAX_PHY_CONFIG) )
+ errx(EX_USAGE, "%s: node out of range: %s\n", __func__, optarg);
+ open_needed = true;
+ command_set = true;
+ display_board_only = false;
+ break;
+ case 't':
+ dump_topology = 1;
+ open_needed = true;
+ command_set = true;
+ display_board_only = false;
+ break;
+ case 'u':
+ if(!command_set)
+ display_board_only = true;
+ current_board = strtol(optarg, NULL, 0);
+ open_needed = true;
+ break;
+ case 'M':
+ switch (optarg[0]) {
+ case 'm':
+ recvfn = mpegtsrecv;
+ break;
+ case 'd':
+ recvfn = dvrecv;
+ break;
+ default:
+ errx(EX_USAGE, "unrecognized method: %s",
+ optarg);
+ }
+ command_set = true;
+ display_board_only = false;
+ break;
+ case 'R':
+ recv_data = malloc(strlen(optarg)+1);
+ if (recv_data == NULL)
+ err(EX_SOFTWARE, "%s:recv_data malloc", __func__);
+ strcpy(recv_data, optarg);
+ open_needed = false;
+ command_set = true;
+ display_board_only = false;
+ break;
+ case 'S':
+ send_data = malloc(strlen(optarg)+1);
+ if (send_data == NULL)
+ err(EX_SOFTWARE, "%s:send_data malloc", __func__);
+ strcpy(send_data, optarg);
+ open_needed = true;
+ command_set = true;
+ display_board_only = false;
+ break;
+ case '?':
+ default:
+ usage();
+ warnc(EINVAL, "%s: Unknown command line arguments", __func__);
+ return 0;
+ }
+ } /* end while */
+
+ /*
+ * Catch the error case when the user
+ * executes the command with non ''-''
+ * delimited arguments.
+ * Generate the usage() display and exit.
+ */
+ if (!command_set && !display_board_only) {
+ usage();
+ warnc(EINVAL, "%s: Unknown command line arguments", __func__);
+ return 0;
+ }
+
+ /*
+ * If -u <bus_number> is passed, execute
+ * command for that card only.
+ *
+ * If -u <bus_number> is not passed, execute
+ * command for card 0 only.
+ *
+ */
+ if(open_needed){
+ snprintf(devbase, sizeof(devbase), "%s%d.0", device_string, current_board);
+ if (open_dev(&fd, devbase) < 0) {
+ err(EX_IOERR, "%s: Error opening firewire controller #%d %s", __func__, current_board, devbase);
+ }
+ }
+ /*
+ * display the nodes on this board "-u"
+ * only
+ */
+ if (display_board_only)
+ list_dev(fd);
+
+ /*
+ * dump_phy_reg "-p"
+ */
+ if (dump_phy_reg)
+ dump_phy_registers(fd);
+
+ /*
+ * send a BUS_RESET Event "-r"
+ */
+ if (send_bus_reset) {
+ if(ioctl(fd, FW_IBUSRST, &tmp) < 0)
+ err(EX_IOERR, "%s: Ioctl of bus reset failed for %s", __func__, devbase);
+ }
+ /*
+ * Print out the CROM for this node "-c"
+ */
+ if (display_crom) {
+ tmp = str2node(fd, crom_string);
+ get_crom(fd, tmp, crom_buf, len);
+ show_crom(crom_buf);
+ free(crom_string);
+ }
+ /*
+ * Hex Dump the CROM for this node "-d"
+ */
+ if (display_crom_hex) {
+ tmp = str2node(fd, crom_string_hex);
+ get_crom(fd, tmp, crom_buf_hex, len);
+ dump_crom(crom_buf_hex);
+ free(crom_string_hex);
+ }
+ /*
+ * Set Priority Budget to value for this node "-b"
+ */
+ if (priority_budget >= 0)
+ set_pri_req(fd, priority_budget);
+
+ /*
+ * Explicitly set the root node of this bus to value "-f"
+ */
+ if (set_root_node >= 0)
+ send_phy_config(fd, set_root_node, -1);
+
+ /*
+ * Set the gap count for this card/bus "-g"
+ */
+ if (set_gap_count >= 0)
+ send_phy_config(fd, -1, set_gap_count);
+
+ /*
+ * Load a CROM from a file "-l"
+ */
+ if (load_crom_from_file)
+ show_crom(crom_buf);
+ /*
+ * Set the fwmem target for a node to argument "-m"
+ */
+ if (set_fwmem_target) {
+ eui.hi = ntohl(*(u_int32_t*)&(target.octet[0]));
+ eui.lo = ntohl(*(u_int32_t*)&(target.octet[4]));
+#if defined(__FreeBSD__)
+ sysctl_set_int("hw.firewire.fwmem.eui64_hi", eui.hi);
+ sysctl_set_int("hw.firewire.fwmem.eui64_lo", eui.lo);
+#elif defined(__NetBSD__)
+ sysctl_set_int("hw.fwmem.eui64_hi", eui.hi);
+ sysctl_set_int("hw.fwmem.eui64_lo", eui.lo);
+#else
+#warning "You need to add support for your OS"
+#endif
+
+ }
+
+ /*
+ * Send a link on to this board/bus "-o"
+ */
+ if (send_link_on >= 0)
+ link_on(fd, send_link_on);
+
+ /*
+ * Send a reset start to this board/bus "-s"
+ */
+ if (send_reset_start >= 0)
+ reset_start(fd, send_reset_start);
+
+ /*
+ * Dump the node topology for this board/bus "-t"
+ */
+ if (dump_topology)
+ show_topology_map(fd);
+
+ /*
+ * Recieve data file from node "-R"
+ */
+#define TAG (1<<6)
+#define CHANNEL 63
+ if (recv_data != NULL){
+ if (recvfn == NULL) { /* guess... */
+ recvfn = detect_recv_fn(fd, TAG | CHANNEL);
+ close(fd);
+ fd = -1;
+ }
+ snprintf(devbase, sizeof(devbase), "%s%d.0", device_string, current_board);
+ if (open_dev(&fd, devbase) < 0)
+ err(EX_IOERR, "%s: Error opening firewire controller #%d %s in recv_data\n", __func__, current_board, devbase);
+ (*recvfn)(fd, recv_data, TAG | CHANNEL, -1);
+ free(recv_data);
+ }
+
+ /*
+ * Send data file to node "-S"
+ */
+ if (send_data != NULL){
+ dvsend(fd, send_data, TAG | CHANNEL, -1);
+ free(send_data);
+ }
+
+ if (fd > 0) {
+ close(fd);
+ fd = -1;
+ }
+ return 0;
+}
diff --git a/usr.sbin/fwcontrol/fwdv.c b/usr.sbin/fwcontrol/fwdv.c
new file mode 100644
index 0000000..23237e5
--- /dev/null
+++ b/usr.sbin/fwcontrol/fwdv.c
@@ -0,0 +1,418 @@
+/*
+ * Copyright (C) 2003
+ * Hidetoshi Shimokawa. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Hidetoshi Shimokawa.
+ *
+ * 4. Neither the name of the author 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/param.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#if __FreeBSD_version >= 500000
+#include <arpa/inet.h>
+#endif
+
+#include <err.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#include <dev/firewire/firewire.h>
+#include <dev/firewire/iec68113.h>
+
+#include "fwmethods.h"
+
+#define DEBUG 0
+#define FIX_FRAME 1
+
+struct frac {
+ int n,d;
+};
+
+struct frac frame_cycle[2] = {
+ {8000*100, 2997}, /* NTSC 8000 cycle / 29.97 Hz */
+ {320, 1}, /* PAL 8000 cycle / 25 Hz */
+};
+int npackets[] = {
+ 250 /* NTSC */,
+ 300 /* PAL */
+};
+struct frac pad_rate[2] = {
+ {203, 2997}, /* = (8000 - 29.97 * 250)/(29.97 * 250) */
+ {1, 15}, /* = (8000 - 25 * 300)/(25 * 300) */
+};
+char *system_name[] = {"NTSC", "PAL"};
+int frame_rate[] = {30, 25};
+
+#define PSIZE 512
+#define DSIZE 480
+#define NCHUNK 64
+
+#define NPACKET_R 256
+#define NPACKET_T 255
+#define TNBUF 100 /* XXX too large value causes block noise */
+#define NEMPTY 10 /* depends on TNBUF */
+#define RBUFSIZE (PSIZE * NPACKET_R)
+#define MAXBLOCKS (300)
+#define CYCLE_FRAC 0xc00
+
+void
+dvrecv(int d, const char *filename, char ich, int count)
+{
+ struct fw_isochreq isoreq;
+ struct fw_isobufreq bufreq;
+ struct dvdbc *dv;
+ struct ciphdr *ciph;
+ struct fw_pkt *pkt;
+ char *pad, *buf;
+ u_int32_t *ptr;
+ int len, tlen, npad, fd, k, m, vec, system = -1, nb;
+ int nblocks[] = {250 /* NTSC */, 300 /* PAL */};
+ struct iovec wbuf[NPACKET_R];
+
+ if(strcmp(filename, "-") == 0) {
+ fd = STDOUT_FILENO;
+ } else {
+ fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0660);
+ if (fd == -1)
+ err(EX_NOINPUT, filename);
+ }
+ buf = malloc(RBUFSIZE);
+ pad = malloc(DSIZE*MAXBLOCKS);
+ memset(pad, 0xff, DSIZE*MAXBLOCKS);
+ bzero(wbuf, sizeof(wbuf));
+
+ bufreq.rx.nchunk = NCHUNK;
+ bufreq.rx.npacket = NPACKET_R;
+ bufreq.rx.psize = PSIZE;
+ bufreq.tx.nchunk = 0;
+ bufreq.tx.npacket = 0;
+ bufreq.tx.psize = 0;
+ if (ioctl(d, FW_SSTBUF, &bufreq) < 0)
+ err(1, "ioctl FW_SSTBUF");
+
+ isoreq.ch = ich & 0x3f;
+ isoreq.tag = (ich >> 6) & 3;
+
+ if (ioctl(d, FW_SRSTREAM, &isoreq) < 0)
+ err(1, "ioctl");
+
+ k = m = 0;
+ while (count <= 0 || k <= count) {
+#if 0
+ tlen = 0;
+ while ((len = read(d, buf + tlen, PSIZE
+ /* RBUFSIZE - tlen */)) > 0) {
+ if (len < 0) {
+ if (errno == EAGAIN) {
+ fprintf(stderr, "(EAGAIN)\n");
+ fflush(stderr);
+ if (len <= 0)
+ continue;
+ } else
+ err(1, "read failed");
+ }
+ tlen += len;
+ if ((RBUFSIZE - tlen) < PSIZE)
+ break;
+ };
+#else
+ tlen = len = read(d, buf, RBUFSIZE);
+ if (len < 0) {
+ if (errno == EAGAIN) {
+ fprintf(stderr, "(EAGAIN) - push 'Play'?\n");
+ fflush(stderr);
+ if (len <= 0)
+ continue;
+ } else
+ err(1, "read failed");
+ }
+#endif
+ vec = 0;
+ ptr = (u_int32_t *) buf;
+again:
+ pkt = (struct fw_pkt *) ptr;
+#if DEBUG
+ fprintf(stderr, "%08x %08x %08x %08x\n",
+ htonl(ptr[0]), htonl(ptr[1]),
+ htonl(ptr[2]), htonl(ptr[3]));
+#endif
+ ciph = (struct ciphdr *)(ptr + 1); /* skip iso header */
+ if (ciph->fmt != CIP_FMT_DVCR)
+ errx(1, "unknown format 0x%x", ciph->fmt);
+ ptr = (u_int32_t *) (ciph + 1); /* skip cip header */
+#if DEBUG
+ if (ciph->fdf.dv.cyc != 0xffff && k == 0) {
+ fprintf(stderr, "0x%04x\n", ntohs(ciph->fdf.dv.cyc));
+ }
+#endif
+ if (pkt->mode.stream.len <= sizeof(struct ciphdr))
+ /* no payload */
+ goto next;
+ for (dv = (struct dvdbc *)ptr;
+ (char *)dv < (char *)(ptr + ciph->len);
+ dv+=6) {
+
+#if DEBUG
+ fprintf(stderr, "(%d,%d) ", dv->sct, dv->dseq);
+#endif
+ if (dv->sct == DV_SCT_HEADER && dv->dseq == 0) {
+ if (system < 0) {
+ system = ciph->fdf.dv.fs;
+ fprintf(stderr, "%s\n", system_name[system]);
+ }
+
+ /* Fix DSF bit */
+ if (system == 1 &&
+ (dv->payload[0] & DV_DSF_12) == 0)
+ dv->payload[0] |= DV_DSF_12;
+ nb = nblocks[system];
+ fprintf(stderr, "%d:%02d:%02d %d\r",
+ k / (3600 * frame_rate[system]),
+ (k / (60 * frame_rate[system])) % 60,
+ (k / frame_rate[system]) % 60,
+ k % frame_rate[system]);
+
+#if FIX_FRAME
+ if (m > 0 && m != nb) {
+ /* padding bad frame */
+ npad = ((nb - m) % nb);
+ if (npad < 0)
+ npad += nb;
+ fprintf(stderr, "\n%d blocks padded\n",
+ npad);
+ npad *= DSIZE;
+ wbuf[vec].iov_base = pad;
+ wbuf[vec++].iov_len = npad;
+ if (vec >= NPACKET_R) {
+ writev(fd, wbuf, vec);
+ vec = 0;
+ }
+ }
+#endif
+ k++;
+ fflush(stderr);
+ m = 0;
+ }
+ if (k == 0 || (count > 0 && k > count))
+ continue;
+ m++;
+ wbuf[vec].iov_base = (char *) dv;
+ wbuf[vec++].iov_len = DSIZE;
+ if (vec >= NPACKET_R) {
+ writev(fd, wbuf, vec);
+ vec = 0;
+ }
+ }
+ ptr = (u_int32_t *)dv;
+next:
+ if ((char *)ptr < buf + tlen)
+ goto again;
+ if (vec > 0)
+ writev(fd, wbuf, vec);
+ }
+ if (fd != STDOUT_FILENO)
+ close(fd);
+ fprintf(stderr, "\n");
+}
+
+
+void
+dvsend(int d, const char *filename, char ich, int count)
+{
+ struct fw_isochreq isoreq;
+ struct fw_isobufreq bufreq;
+ struct dvdbc *dv;
+ struct fw_pkt *pkt;
+ int len, tlen, header, fd, frames, packets, vec, offset, nhdr, i;
+ int system=-1, pad_acc, cycle_acc, cycle, f_cycle, f_frac;
+ struct iovec wbuf[TNBUF*2 + NEMPTY];
+ char *pbuf;
+ u_int32_t iso_data, iso_empty, hdr[TNBUF + NEMPTY][3];
+ struct ciphdr *ciph;
+ struct timeval start, end;
+ double rtime;
+
+ fd = open(filename, O_RDONLY);
+ if (fd == -1)
+ err(EX_NOINPUT, filename);
+
+ pbuf = malloc(DSIZE * TNBUF);
+ bzero(wbuf, sizeof(wbuf));
+
+ bufreq.rx.nchunk = 0;
+ bufreq.rx.npacket = 0;
+ bufreq.rx.psize = 0;
+ bufreq.tx.nchunk = NCHUNK;
+ bufreq.tx.npacket = NPACKET_T;
+ bufreq.tx.psize = PSIZE;
+ if (ioctl(d, FW_SSTBUF, &bufreq) < 0)
+ err(1, "ioctl FW_SSTBUF");
+
+ isoreq.ch = ich & 0x3f;
+ isoreq.tag = (ich >> 6) & 3;
+
+ if (ioctl(d, FW_STSTREAM, &isoreq) < 0)
+ err(1, "ioctl FW_STSTREAM");
+
+ iso_data = 0;
+ pkt = (struct fw_pkt *) &iso_data;
+ pkt->mode.stream.len = DSIZE + sizeof(struct ciphdr);
+ pkt->mode.stream.sy = 0;
+ pkt->mode.stream.tcode = FWTCODE_STREAM;
+ pkt->mode.stream.chtag = ich;
+ iso_empty = iso_data;
+ pkt = (struct fw_pkt *) &iso_empty;
+ pkt->mode.stream.len = sizeof(struct ciphdr);
+
+ bzero(hdr[0], sizeof(hdr[0]));
+ hdr[0][0] = iso_data;
+ ciph = (struct ciphdr *)&hdr[0][1];
+ ciph->src = 0; /* XXX */
+ ciph->len = 120;
+ ciph->dbc = 0;
+ ciph->eoh1 = 1;
+ ciph->fdf.dv.cyc = 0xffff;
+
+ for (i = 1; i < TNBUF; i++)
+ bcopy(hdr[0], hdr[i], sizeof(hdr[0]));
+
+ gettimeofday(&start, NULL);
+#if DEBUG
+ fprintf(stderr, "%08x %08x %08x\n",
+ htonl(hdr[0]), htonl(hdr[1]), htonl(hdr[2]));
+#endif
+ frames = 0;
+ packets = 0;
+ pad_acc = 0;
+ while (1) {
+ tlen = 0;
+ while (tlen < DSIZE * TNBUF) {
+ len = read(fd, pbuf + tlen, DSIZE * TNBUF - tlen);
+ if (len <= 0) {
+ if (tlen > 0)
+ break;
+ if (len < 0)
+ warn("read");
+ else
+ fprintf(stderr, "\nend of file\n");
+ goto send_end;
+ }
+ tlen += len;
+ }
+ vec = 0;
+ offset = 0;
+ nhdr = 0;
+next:
+ dv = (struct dvdbc *)(pbuf + offset * DSIZE);
+#if 0
+ header = (dv->sct == 0 && dv->dseq == 0);
+#else
+ header = (packets == 0 || packets % npackets[system] == 0);
+#endif
+
+ ciph = (struct ciphdr *)&hdr[nhdr][1];
+ if (header) {
+ if (system < 0) {
+ system = ((dv->payload[0] & DV_DSF_12) != 0);
+ printf("%s\n", system_name[system]);
+ cycle = 1;
+ cycle_acc = frame_cycle[system].d * cycle;
+ }
+ fprintf(stderr, "%d", frames % 10);
+ frames ++;
+ if (count > 0 && frames > count)
+ break;
+ if (frames % frame_rate[system] == 0)
+ fprintf(stderr, "\n");
+ fflush(stderr);
+ f_cycle = (cycle_acc / frame_cycle[system].d) & 0xf;
+ f_frac = (cycle_acc % frame_cycle[system].d
+ * CYCLE_FRAC) / frame_cycle[system].d;
+#if 0
+ ciph->fdf.dv.cyc = htons(f_cycle << 12 | f_frac);
+#else
+ ciph->fdf.dv.cyc = htons(cycle << 12 | f_frac);
+#endif
+ cycle_acc += frame_cycle[system].n;
+ cycle_acc %= frame_cycle[system].d * 0x10;
+
+ } else {
+ ciph->fdf.dv.cyc = 0xffff;
+ }
+ ciph->dbc = packets++ % 256;
+ pad_acc += pad_rate[system].n;
+ if (pad_acc >= pad_rate[system].d) {
+ pad_acc -= pad_rate[system].d;
+ bcopy(hdr[nhdr], hdr[nhdr+1], sizeof(hdr[0]));
+ hdr[nhdr][0] = iso_empty;
+ wbuf[vec].iov_base = (char *)hdr[nhdr];
+ wbuf[vec++].iov_len = sizeof(hdr[0]);
+ nhdr ++;
+ cycle ++;
+ }
+ hdr[nhdr][0] = iso_data;
+ wbuf[vec].iov_base = (char *)hdr[nhdr];
+ wbuf[vec++].iov_len = sizeof(hdr[0]);
+ wbuf[vec].iov_base = (char *)dv;
+ wbuf[vec++].iov_len = DSIZE;
+ nhdr ++;
+ cycle ++;
+ offset ++;
+ if (offset * DSIZE < tlen)
+ goto next;
+
+again:
+ len = writev(d, wbuf, vec);
+ if (len < 0) {
+ if (errno == EAGAIN) {
+ fprintf(stderr, "(EAGAIN) - push 'Play'?\n");
+ goto again;
+ }
+ err(1, "write failed");
+ }
+ }
+ close(fd);
+ fprintf(stderr, "\n");
+send_end:
+ gettimeofday(&end, NULL);
+ rtime = end.tv_sec - start.tv_sec
+ + (end.tv_usec - start.tv_usec) * 1e-6;
+ fprintf(stderr, "%d frames, %.2f secs, %.2f frames/sec\n",
+ frames, rtime, frames/rtime);
+}
diff --git a/usr.sbin/fwcontrol/fwmethods.h b/usr.sbin/fwcontrol/fwmethods.h
new file mode 100644
index 0000000..9ac82e6
--- /dev/null
+++ b/usr.sbin/fwcontrol/fwmethods.h
@@ -0,0 +1,10 @@
+/*-
+ * This file is in the public domain.
+ *
+ * $FreeBSD$
+ */
+
+typedef void (fwmethod)(int dev_fd, const char *filename, char ich, int count);
+extern fwmethod dvrecv;
+extern fwmethod dvsend;
+extern fwmethod mpegtsrecv;
diff --git a/usr.sbin/fwcontrol/fwmpegts.c b/usr.sbin/fwcontrol/fwmpegts.c
new file mode 100644
index 0000000..e92b799
--- /dev/null
+++ b/usr.sbin/fwcontrol/fwmpegts.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2005
+ * Petr Holub, Hidetoshi Shimokawa. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Hidetoshi Shimokawa.
+ *
+ * 4. Neither the name of the author 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/param.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+
+#if __FreeBSD_version >= 500000
+#include <arpa/inet.h>
+#endif
+
+#include <err.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+
+#if defined(__FreeBSD__)
+#include <dev/firewire/firewire.h>
+#include <dev/firewire/iec68113.h>
+#elif defined(__NetBSD__)
+#include <dev/ieee1394/firewire.h>
+#include <dev/ieee1394/iec68113.h>
+#else
+#warning "You need to add support for your OS"
+#endif
+
+
+#include "fwmethods.h"
+
+#define DEBUG 0
+
+/*****************************************************************************
+
+MPEG-2 Transport Stream (MPEG TS) packet format according to IEC 61883:
+
+31 15 0
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ --------
+| len |tag| channel | tcode | sy |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 1394
+| header_CRC |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ --------
+|0|0| sid | dbs |fn | qpc |S|RSV| dbc |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ CIP
+|1|0| fmt | fdf | fdf/syt |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ --------
+| reserved | cycle_count | cycle_offset |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+| | N x
+. . MPEG
+. MPEG TS payload 188 bytes .
+. .
+| |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ --------
+| data_CRC |
++-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+N.b. that CRCs are removed by firewire layer!
+
+The following fiels are fixed for IEEE-1394:
+tag = 01b
+tcode = 1010b
+The length is payload length, i.e. includes CIP header and data size.
+
+The following fields are constant for MPEG TS:
+sph = 1 (denoted as S in CIP header above)
+dbs = 6
+fmt = (1<<5)
+fdf = reserved
+In the supported streams we also require
+qpc = 0
+fn = 3
+and thus the payload is divided in 8 blocks as follows:
+
+ +-----+-----+-----+-----+-----+-----+-----+-----+
+ | db0 | db1 | db2 | db3 | db4 | db5 | db6 | db7 |
+ +-----+-----+-----+-----+-----+-----+-----+-----+
+
+We have several cases of payload distribution based on stream
+bandwidth (R):
+1) R < 1.5 Mbps: any of db0..db7 may be payload,
+2) 1.5 < R < 3 Mbps: db0/db1 or db2/db3 or db4/db5 or db6/db7 is payload,
+3) 3 < R < 6 Mbps: db0/db1/db2/db3 or db4/db5/db6/db7 is payload,
+4) R > 6 Mbps: all db0..db7 contain the payload.
+Curently, only case (4) is supported in fwmpegts.c
+
+Each packet may contain N MPEG TS data blocks with timestamp header,
+which are (4+188)B long. Experimentally, the N ranges from 0 through 3.
+
+*****************************************************************************/
+
+
+typedef uint8_t mpeg_ts_pld[188];
+
+struct mpeg_pldt {
+#if BYTE_ORDER == BIG_ENDIAN
+ uint32_t :7,
+ c_count:13,
+ c_offset:12;
+#else /* BYTE_ORDER != BIG_ENDIAN */
+ uint32_t c_offset:12,
+ c_count:13,
+ :7;
+#endif /* BYTE_ORDER == BIG_ENDIAN */
+ mpeg_ts_pld payload;
+};
+
+
+#define NCHUNK 8
+#define PSIZE 596
+#define NPACKET_R 4096
+#define RBUFSIZE (PSIZE * NPACKET_R)
+
+void
+mpegtsrecv(int d, const char *filename, char ich, int count)
+{
+ struct ciphdr *ciph;
+ struct fw_isochreq isoreq;
+ struct fw_isobufreq bufreq;
+ struct fw_pkt *pkt;
+ struct mpeg_pldt *pld;
+ uint32_t *ptr;
+ int fd, k, len, m, pkt_size, startwr, tlen;
+ char *buf;
+
+ startwr = 0;
+
+ if (strcmp(filename, "-") == 0)
+ fd = STDOUT_FILENO;
+ else {
+ fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0660);
+ if (fd == -1)
+ err(EX_NOINPUT, filename);
+ }
+ buf = malloc(RBUFSIZE);
+
+ bufreq.rx.nchunk = NCHUNK;
+ bufreq.rx.npacket = NPACKET_R;
+ bufreq.rx.psize = PSIZE;
+ bufreq.tx.nchunk = 0;
+ bufreq.tx.npacket = 0;
+ bufreq.tx.psize = 0;
+ if (ioctl(d, FW_SSTBUF, &bufreq) < 0)
+ err(1, "ioctl");
+
+ isoreq.ch = ich & 0x3f;
+ isoreq.tag = (ich >> 6) & 3;
+
+ if (ioctl(d, FW_SRSTREAM, &isoreq) < 0)
+ err(1, "ioctl");
+
+ k = m = 0;
+ while (count <= 0 || k <= count) {
+ len = tlen = read(d, buf, RBUFSIZE);
+#if DEBUG
+ fprintf(stderr, "Read %d bytes.\n", len);
+#endif /* DEBUG */
+ if (len < 0) {
+ if (errno == EAGAIN) {
+ fprintf(stderr, "(EAGAIN) - push 'Play'?\n");
+ continue;
+ }
+ err(1, "read failed");
+ }
+ ptr = (uint32_t *) buf;
+
+ do {
+ pkt = (struct fw_pkt *) ptr;
+#if DEBUG
+ fprintf(stderr, "\nReading new packet.\n");
+ fprintf(stderr, "%08x %08x %08x %08x\n",
+ htonl(ptr[0]), htonl(ptr[1]),
+ htonl(ptr[2]), htonl(ptr[3]));
+#endif /* DEBUG */
+ /* there is no CRC in the 1394 header */
+ ciph = (struct ciphdr *)(ptr + 1); /* skip iso header */
+ if (ciph->fmt != CIP_FMT_MPEG)
+ errx(1, "unknown format 0x%x", ciph->fmt);
+ if (ciph->fn != 3) {
+ errx(1,
+ "unsupported MPEG TS stream, fn=%d (only fn=3 is supported)",
+ ciph->fn);
+ }
+ ptr = (uint32_t *) (ciph + 1); /* skip cip header */
+
+ if (pkt->mode.stream.len <= sizeof(struct ciphdr)) {
+ /* no payload */
+ /* tlen needs to be decremented before end of the loop */
+ goto next;
+ }
+#if DEBUG
+ else {
+ fprintf(stderr,
+ "Packet net payload length (IEEE1394 header): %d\n",
+ pkt->mode.stream.len - sizeof(struct ciphdr));
+ fprintf(stderr, "Data block size (CIP header): %d [q], %d [B]\n",
+ ciph->len, ciph->len * 4);
+ fprintf(stderr,
+ "Data fraction number (CIP header): %d => DBC increments with %d\n",
+ ciph->fn, (1<<ciph->fn) );
+ fprintf(stderr, "QCP (CIP header): %d\n", ciph->qpc );
+ fprintf(stderr, "DBC counter (CIP header): %d\n", ciph->dbc );
+ fprintf(stderr, "MPEG payload type size: %d\n",
+ sizeof(struct mpeg_pldt));
+ }
+#endif /* DEBUG */
+
+ /* This is a condition that needs to be satisfied to start
+ writing the data */
+ if (ciph->dbc % (1<<ciph->fn) == 0)
+ startwr = 1;
+ /* Read out all the MPEG TS data blocks from current packet */
+ for (pld = (struct mpeg_pldt *)ptr;
+ (intptr_t)pld < (intptr_t)((char *)ptr +
+ pkt->mode.stream.len - sizeof(struct ciphdr));
+ pld++) {
+ if (startwr == 1)
+ write(fd, pld->payload,
+ sizeof(pld->payload));
+ }
+
+next:
+ /* CRCs are removed from both header and trailer
+ so that only 4 bytes of 1394 header remains */
+ pkt_size = pkt->mode.stream.len + 4;
+ ptr = (uint32_t *)((intptr_t)pkt + pkt_size);
+ tlen -= pkt_size;
+ } while (tlen > 0);
+#if DEBUG
+ fprintf(stderr, "\nReading a data from firewire.\n");
+#endif /* DEBUG */
+
+ }
+ if (fd != STDOUT_FILENO)
+ close(fd);
+ fprintf(stderr, "\n");
+}
diff --git a/usr.sbin/getfmac/Makefile b/usr.sbin/getfmac/Makefile
new file mode 100644
index 0000000..ad49657
--- /dev/null
+++ b/usr.sbin/getfmac/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= getfmac
+MAN= getfmac.8
+
+WARNS?= 6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/getfmac/getfmac.8 b/usr.sbin/getfmac/getfmac.8
new file mode 100644
index 0000000..f8ebb31
--- /dev/null
+++ b/usr.sbin/getfmac/getfmac.8
@@ -0,0 +1,57 @@
+.\" Copyright (c) 2002 Networks Associates Technology, Inc.
+.\" All rights reserved.
+.\"
+.\" This software was developed for the FreeBSD Project by Chris
+.\" Costello at Safeport Network Services 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 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 June 27, 2002
+.Dt GETFMAC 8
+.Os
+.Sh NAME
+.Nm getfmac
+.Nd print MAC label for a file system object
+.Sh SYNOPSIS
+.Nm
+.Op Fl h
+.Op Fl l Ar list,of,labels
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility prints the text representation of the MAC label associated with the
+specified files.
+.Sh SEE ALSO
+.Xr mac 3 ,
+.Xr mac_get_file 3 ,
+.Xr mac 4 ,
+.Xr setfmac 8 ,
+.Xr mac 9
diff --git a/usr.sbin/getfmac/getfmac.c b/usr.sbin/getfmac/getfmac.c
new file mode 100644
index 0000000..7c03d59
--- /dev/null
+++ b/usr.sbin/getfmac/getfmac.c
@@ -0,0 +1,116 @@
+/*-
+ * Copyright (c) 2002 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 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/types.h>
+#include <sys/mac.h>
+
+#include <err.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#define MAXELEMENTS 32
+
+static void
+usage(void)
+{
+
+ fprintf(stderr,
+ "getfmac [-h] [-l list,of,labels] [file1] [file2 ...]\n");
+ exit (EX_USAGE);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *labellist, *string;
+ mac_t label;
+ int ch, hflag;
+ int error, i;
+
+ labellist = NULL;
+ hflag = 0;
+ while ((ch = getopt(argc, argv, "hl:")) != -1) {
+ switch (ch) {
+ case 'h':
+ hflag = 1;
+ break;
+ case 'l':
+ if (labellist != NULL)
+ usage();
+ labellist = argv[optind - 1];
+ break;
+ default:
+ usage();
+ }
+
+ }
+
+ for (i = optind; i < argc; i++) {
+ if (labellist != NULL)
+ error = mac_prepare(&label, labellist);
+ else
+ error = mac_prepare_file_label(&label);
+
+ if (error != 0) {
+ perror("mac_prepare");
+ return (-1);
+ }
+
+ if (hflag)
+ error = mac_get_link(argv[i], label);
+ else
+ error = mac_get_file(argv[i], label);
+ if (error) {
+ perror(argv[i]);
+ mac_free(label);
+ continue;
+ }
+
+ error = mac_to_text(label, &string);
+ if (error != 0)
+ perror("mac_to_text");
+ else {
+ printf("%s: %s\n", argv[i], string);
+ free(string);
+ }
+ mac_free(label);
+ }
+
+ exit(EX_OK);
+}
diff --git a/usr.sbin/getpmac/Makefile b/usr.sbin/getpmac/Makefile
new file mode 100644
index 0000000..87e6d28
--- /dev/null
+++ b/usr.sbin/getpmac/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= getpmac
+MAN= getpmac.8
+
+WARNS?= 6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/getpmac/getpmac.8 b/usr.sbin/getpmac/getpmac.8
new file mode 100644
index 0000000..307c962
--- /dev/null
+++ b/usr.sbin/getpmac/getpmac.8
@@ -0,0 +1,59 @@
+.\" Copyright (c) 2003 Networks Associates Technology, Inc.
+.\" All rights reserved.
+.\"
+.\" This software was developed for the FreeBSD Project by Chris Costello
+.\" at Safeport Network Services and Network Associates 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 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 14, 2003
+.Dt GETPMAC 8
+.Os
+.Sh NAME
+.Nm getpmac
+.Nd print process-related MAC labels
+.Sh SYNOPSIS
+.Nm
+.Op Fl l Ar list,of,labels
+.Op Fl p Ar pid
+.Sh DESCRIPTION
+The
+.Nm
+utility prints the text representation of the MAC label associated with the
+specified process.
+If no process is specified, it prints the label associated with its own
+process.
+.Sh SEE ALSO
+.Xr mac 3 ,
+.Xr mac_get_pid 3 ,
+.Xr mac_get_proc 3 ,
+.Xr mac 4 ,
+.Xr maclabel 7 ,
+.Xr getfmac 8 ,
+.Xr setfmac 8 ,
+.Xr setpmac 8 ,
+.Xr mac 9
diff --git a/usr.sbin/getpmac/getpmac.c b/usr.sbin/getpmac/getpmac.c
new file mode 100644
index 0000000..6e81558
--- /dev/null
+++ b/usr.sbin/getpmac/getpmac.c
@@ -0,0 +1,127 @@
+/*-
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by 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/types.h>
+#include <sys/mac.h>
+
+#include <err.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#define MAXELEMENTS 32
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "getpmac [-l list,of,labels] [-p pid]\n");
+ exit (EX_USAGE);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *labellist, *string;
+ mac_t label;
+ pid_t pid;
+ int ch, error, pid_set;
+
+ pid_set = 0;
+ pid = 0;
+ labellist = NULL;
+ while ((ch = getopt(argc, argv, "l:p:")) != -1) {
+ switch (ch) {
+ case 'l':
+ if (labellist != NULL)
+ usage();
+ labellist = argv[optind - 1];
+ break;
+ case 'p':
+ if (pid_set)
+ usage();
+ pid = atoi(argv[optind - 1]);
+ pid_set = 1;
+ break;
+ default:
+ usage();
+ }
+
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 0)
+ usage();
+
+ if (labellist != NULL)
+ error = mac_prepare(&label, labellist);
+ else
+ error = mac_prepare_process_label(&label);
+ if (error != 0) {
+ perror("mac_prepare");
+ return (-1);
+ }
+
+ if (pid_set) {
+ error = mac_get_pid(pid, label);
+ if (error)
+ perror("mac_get_pid");
+ }
+ else {
+ error = mac_get_proc(label);
+ if (error)
+ perror("mac_get_proc");
+ }
+ if (error) {
+ mac_free(label);
+ exit (-1);
+ }
+ error = mac_to_text(label, &string);
+ if (error != 0) {
+ perror("mac_to_text");
+ exit(EX_DATAERR);
+ }
+
+ printf("%s\n", string);
+
+ mac_free(label);
+ free(string);
+ exit(EX_OK);
+}
diff --git a/usr.sbin/gssd/Makefile b/usr.sbin/gssd/Makefile
new file mode 100644
index 0000000..45d8de4
--- /dev/null
+++ b/usr.sbin/gssd/Makefile
@@ -0,0 +1,29 @@
+# $FreeBSD$
+
+PROG= gssd
+MAN= gssd.8
+SRCS= gssd.c gssd.h gssd_svc.c gssd_xdr.c gssd_prot.c
+
+CFLAGS+= -I.
+WARNS?= 1
+
+DPADD= ${LIBGSSAPI}
+LDADD= -lgssapi
+
+CLEANFILES= gssd_svc.c gssd.h
+
+RPCSRC= ${.CURDIR}/../../sys/kgssapi/gssd.x
+RPCGEN= rpcgen -L -C -M
+
+gssd_svc.c: ${RPCSRC} gssd.h
+ ${RPCGEN} -m -o ${.TARGET} ${RPCSRC}
+
+gssd_xdr.c: ${RPCSRC} gssd.h
+ ${RPCGEN} -c -o ${.TARGET} ${RPCSRC}
+
+gssd.h: ${RPCSRC}
+ ${RPCGEN} -h -o ${.TARGET} ${RPCSRC}
+
+.PATH: ${.CURDIR}/../../sys/kgssapi
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/gssd/gssd.8 b/usr.sbin/gssd/gssd.8
new file mode 100644
index 0000000..c4704a1
--- /dev/null
+++ b/usr.sbin/gssd/gssd.8
@@ -0,0 +1,68 @@
+.\" Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+.\" Authors: Doug Rabson <dfr@rabson.org>
+.\" Developed with Red Inc: Alfred Perlstein <alfred@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$
+.\"
+.Dd November 5, 2008
+.Dt GSSD 8
+.Os
+.Sh NAME
+.Nm gssd
+.Nd "Generic Security Services Daemon"
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Sh DESCRIPTION
+The
+.Nm
+program provides support for the kernel GSS-API implementation.
+.Pp
+The options are as follows:
+.Bl -tag
+.It Fl d
+Run in debug mode.
+In this mode,
+.Nm
+will not fork when it starts.
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /etc/krb5.keytab" -compact
+.It Pa /etc/krb5.keytab
+Contains Kerberos service principals which may be used as credentials
+by kernel GSS-API services.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr gssapi 3
+.Sh HISTORY
+The
+.Nm
+manual page first appeared in
+.Fx 8.0 .
+.Sh AUTHORS
+This
+manual page was written by
+.An Doug Rabson Aq dfr@FreeBSD.org .
diff --git a/usr.sbin/gssd/gssd.c b/usr.sbin/gssd/gssd.c
new file mode 100644
index 0000000..ba2805b
--- /dev/null
+++ b/usr.sbin/gssd/gssd.c
@@ -0,0 +1,610 @@
+/*-
+ * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
+ * Authors: Doug Rabson <dfr@rabson.org>
+ * Developed with Red Inc: Alfred Perlstein <alfred@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 <sys/param.h>
+#include <sys/stat.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+#include <sys/queue.h>
+#include <ctype.h>
+#include <err.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <gssapi/gssapi.h>
+#include <rpc/rpc.h>
+#include <rpc/rpc_com.h>
+
+#include "gssd.h"
+
+#ifndef _PATH_GSS_MECH
+#define _PATH_GSS_MECH "/etc/gss/mech"
+#endif
+#ifndef _PATH_GSSDSOCK
+#define _PATH_GSSDSOCK "/var/run/gssd.sock"
+#endif
+
+struct gss_resource {
+ LIST_ENTRY(gss_resource) gr_link;
+ uint64_t gr_id; /* indentifier exported to kernel */
+ void* gr_res; /* GSS-API resource pointer */
+};
+LIST_HEAD(gss_resource_list, gss_resource) gss_resources;
+int gss_resource_count;
+uint32_t gss_next_id;
+uint32_t gss_start_time;
+int debug_level;
+
+static void gssd_load_mech(void);
+
+extern void gssd_1(struct svc_req *rqstp, SVCXPRT *transp);
+extern int gssd_syscall(char *path);
+
+int
+main(int argc, char **argv)
+{
+ /*
+ * We provide an RPC service on a local-domain socket. The
+ * kernel's GSS-API code will pass what it can't handle
+ * directly to us.
+ */
+ struct sockaddr_un sun;
+ int fd, oldmask, ch, debug;
+ SVCXPRT *xprt;
+
+ debug = 0;
+ while ((ch = getopt(argc, argv, "d")) != -1) {
+ switch (ch) {
+ case 'd':
+ debug_level++;
+ break;
+ default:
+ fprintf(stderr, "usage: %s [-d]\n", argv[0]);
+ exit(1);
+ break;
+ }
+ }
+
+ gssd_load_mech();
+
+ if (!debug_level)
+ daemon(0, 0);
+
+ memset(&sun, 0, sizeof sun);
+ sun.sun_family = AF_LOCAL;
+ unlink(_PATH_GSSDSOCK);
+ strcpy(sun.sun_path, _PATH_GSSDSOCK);
+ sun.sun_len = SUN_LEN(&sun);
+ fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+ if (!fd) {
+ err(1, "Can't create local gssd socket");
+ }
+ oldmask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
+ if (bind(fd, (struct sockaddr *) &sun, sun.sun_len) < 0) {
+ err(1, "Can't bind local gssd socket");
+ }
+ umask(oldmask);
+ if (listen(fd, SOMAXCONN) < 0) {
+ err(1, "Can't listen on local gssd socket");
+ }
+ xprt = svc_vc_create(fd, RPC_MAXDATASIZE, RPC_MAXDATASIZE);
+ if (!xprt) {
+ err(1, "Can't create transport for local gssd socket");
+ }
+ if (!svc_reg(xprt, GSSD, GSSDVERS, gssd_1, NULL)) {
+ err(1, "Can't register service for local gssd socket");
+ }
+
+ LIST_INIT(&gss_resources);
+ gss_next_id = 1;
+ gss_start_time = time(0);
+
+ gssd_syscall(_PATH_GSSDSOCK);
+ svc_run();
+
+ return (0);
+}
+
+static void
+gssd_load_mech(void)
+{
+ FILE *fp;
+ char buf[256];
+ char *p;
+ char *name, *oid, *lib, *kobj;
+
+ fp = fopen(_PATH_GSS_MECH, "r");
+ if (!fp)
+ return;
+
+ while (fgets(buf, sizeof(buf), fp)) {
+ if (*buf == '#')
+ continue;
+ p = buf;
+ name = strsep(&p, "\t\n ");
+ if (p) while (isspace(*p)) p++;
+ oid = strsep(&p, "\t\n ");
+ if (p) while (isspace(*p)) p++;
+ lib = strsep(&p, "\t\n ");
+ if (p) while (isspace(*p)) p++;
+ kobj = strsep(&p, "\t\n ");
+ if (!name || !oid || !lib || !kobj)
+ continue;
+
+ if (strcmp(kobj, "-")) {
+ /*
+ * Attempt to load the kernel module if its
+ * not already present.
+ */
+ if (modfind(kobj) < 0) {
+ if (kldload(kobj) < 0) {
+ fprintf(stderr,
+ "%s: can't find or load kernel module %s for %s\n",
+ getprogname(), kobj, name);
+ }
+ }
+ }
+ }
+ fclose(fp);
+}
+
+static void *
+gssd_find_resource(uint64_t id)
+{
+ struct gss_resource *gr;
+
+ if (!id)
+ return (NULL);
+
+ LIST_FOREACH(gr, &gss_resources, gr_link)
+ if (gr->gr_id == id)
+ return (gr->gr_res);
+
+ return (NULL);
+}
+
+static uint64_t
+gssd_make_resource(void *res)
+{
+ struct gss_resource *gr;
+
+ if (!res)
+ return (0);
+
+ gr = malloc(sizeof(struct gss_resource));
+ if (!gr)
+ return (0);
+ gr->gr_id = (gss_next_id++) + ((uint64_t) gss_start_time << 32);
+ gr->gr_res = res;
+ LIST_INSERT_HEAD(&gss_resources, gr, gr_link);
+ gss_resource_count++;
+ if (debug_level > 1)
+ printf("%d resources allocated\n", gss_resource_count);
+
+ return (gr->gr_id);
+}
+
+static void
+gssd_delete_resource(uint64_t id)
+{
+ struct gss_resource *gr;
+
+ LIST_FOREACH(gr, &gss_resources, gr_link) {
+ if (gr->gr_id == id) {
+ LIST_REMOVE(gr, gr_link);
+ free(gr);
+ gss_resource_count--;
+ if (debug_level > 1)
+ printf("%d resources allocated\n",
+ gss_resource_count);
+ return;
+ }
+ }
+}
+
+bool_t
+gssd_null_1_svc(void *argp, void *result, struct svc_req *rqstp)
+{
+
+ return (TRUE);
+}
+
+bool_t
+gssd_init_sec_context_1_svc(init_sec_context_args *argp, init_sec_context_res *result, struct svc_req *rqstp)
+{
+ gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
+ gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
+ gss_name_t name = GSS_C_NO_NAME;
+ char ccname[strlen("FILE:/tmp/krb5cc_") + 6 + 1];
+
+ snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d",
+ (int) argp->uid);
+ setenv("KRB5CCNAME", ccname, TRUE);
+
+ memset(result, 0, sizeof(*result));
+ if (argp->cred) {
+ cred = gssd_find_resource(argp->cred);
+ if (!cred) {
+ result->major_status = GSS_S_CREDENTIALS_EXPIRED;
+ return (TRUE);
+ }
+ }
+ if (argp->ctx) {
+ ctx = gssd_find_resource(argp->ctx);
+ if (!ctx) {
+ result->major_status = GSS_S_CONTEXT_EXPIRED;
+ return (TRUE);
+ }
+ }
+ if (argp->name) {
+ name = gssd_find_resource(argp->name);
+ if (!name) {
+ result->major_status = GSS_S_BAD_NAME;
+ return (TRUE);
+ }
+ }
+
+ memset(result, 0, sizeof(*result));
+ result->major_status = gss_init_sec_context(&result->minor_status,
+ cred, &ctx, name, argp->mech_type,
+ argp->req_flags, argp->time_req, argp->input_chan_bindings,
+ &argp->input_token, &result->actual_mech_type,
+ &result->output_token, &result->ret_flags, &result->time_rec);
+
+ if (result->major_status == GSS_S_COMPLETE
+ || result->major_status == GSS_S_CONTINUE_NEEDED) {
+ if (argp->ctx)
+ result->ctx = argp->ctx;
+ else
+ result->ctx = gssd_make_resource(ctx);
+ }
+
+ return (TRUE);
+}
+
+bool_t
+gssd_accept_sec_context_1_svc(accept_sec_context_args *argp, accept_sec_context_res *result, struct svc_req *rqstp)
+{
+ gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
+ gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
+ gss_name_t src_name;
+ gss_cred_id_t delegated_cred_handle;
+
+ memset(result, 0, sizeof(*result));
+ if (argp->ctx) {
+ ctx = gssd_find_resource(argp->ctx);
+ if (!ctx) {
+ result->major_status = GSS_S_CONTEXT_EXPIRED;
+ return (TRUE);
+ }
+ }
+ if (argp->cred) {
+ cred = gssd_find_resource(argp->cred);
+ if (!cred) {
+ result->major_status = GSS_S_CREDENTIALS_EXPIRED;
+ return (TRUE);
+ }
+ }
+
+ memset(result, 0, sizeof(*result));
+ result->major_status = gss_accept_sec_context(&result->minor_status,
+ &ctx, cred, &argp->input_token, argp->input_chan_bindings,
+ &src_name, &result->mech_type, &result->output_token,
+ &result->ret_flags, &result->time_rec,
+ &delegated_cred_handle);
+
+ if (result->major_status == GSS_S_COMPLETE
+ || result->major_status == GSS_S_CONTINUE_NEEDED) {
+ if (argp->ctx)
+ result->ctx = argp->ctx;
+ else
+ result->ctx = gssd_make_resource(ctx);
+ result->src_name = gssd_make_resource(src_name);
+ result->delegated_cred_handle =
+ gssd_make_resource(delegated_cred_handle);
+ }
+
+ return (TRUE);
+}
+
+bool_t
+gssd_delete_sec_context_1_svc(delete_sec_context_args *argp, delete_sec_context_res *result, struct svc_req *rqstp)
+{
+ gss_ctx_id_t ctx = gssd_find_resource(argp->ctx);
+
+ if (ctx) {
+ result->major_status = gss_delete_sec_context(
+ &result->minor_status, &ctx, &result->output_token);
+ gssd_delete_resource(argp->ctx);
+ } else {
+ result->major_status = GSS_S_COMPLETE;
+ result->minor_status = 0;
+ }
+
+ return (TRUE);
+}
+
+bool_t
+gssd_export_sec_context_1_svc(export_sec_context_args *argp, export_sec_context_res *result, struct svc_req *rqstp)
+{
+ gss_ctx_id_t ctx = gssd_find_resource(argp->ctx);
+
+ if (ctx) {
+ result->major_status = gss_export_sec_context(
+ &result->minor_status, &ctx,
+ &result->interprocess_token);
+ result->format = KGSS_HEIMDAL_1_1;
+ gssd_delete_resource(argp->ctx);
+ } else {
+ result->major_status = GSS_S_FAILURE;
+ result->minor_status = 0;
+ result->interprocess_token.length = 0;
+ result->interprocess_token.value = NULL;
+ }
+
+ return (TRUE);
+}
+
+bool_t
+gssd_import_name_1_svc(import_name_args *argp, import_name_res *result, struct svc_req *rqstp)
+{
+ gss_name_t name;
+
+ result->major_status = gss_import_name(&result->minor_status,
+ &argp->input_name_buffer, argp->input_name_type, &name);
+
+ if (result->major_status == GSS_S_COMPLETE)
+ result->output_name = gssd_make_resource(name);
+ else
+ result->output_name = 0;
+
+ return (TRUE);
+}
+
+bool_t
+gssd_canonicalize_name_1_svc(canonicalize_name_args *argp, canonicalize_name_res *result, struct svc_req *rqstp)
+{
+ gss_name_t name = gssd_find_resource(argp->input_name);
+ gss_name_t output_name;
+
+ memset(result, 0, sizeof(*result));
+ if (!name) {
+ result->major_status = GSS_S_BAD_NAME;
+ return (TRUE);
+ }
+
+ result->major_status = gss_canonicalize_name(&result->minor_status,
+ name, argp->mech_type, &output_name);
+
+ if (result->major_status == GSS_S_COMPLETE)
+ result->output_name = gssd_make_resource(output_name);
+ else
+ result->output_name = 0;
+
+ return (TRUE);
+}
+
+bool_t
+gssd_export_name_1_svc(export_name_args *argp, export_name_res *result, struct svc_req *rqstp)
+{
+ gss_name_t name = gssd_find_resource(argp->input_name);
+
+ memset(result, 0, sizeof(*result));
+ if (!name) {
+ result->major_status = GSS_S_BAD_NAME;
+ return (TRUE);
+ }
+
+ result->major_status = gss_export_name(&result->minor_status,
+ name, &result->exported_name);
+
+ return (TRUE);
+}
+
+bool_t
+gssd_release_name_1_svc(release_name_args *argp, release_name_res *result, struct svc_req *rqstp)
+{
+ gss_name_t name = gssd_find_resource(argp->input_name);
+
+ if (name) {
+ result->major_status = gss_release_name(&result->minor_status,
+ &name);
+ gssd_delete_resource(argp->input_name);
+ } else {
+ result->major_status = GSS_S_COMPLETE;
+ result->minor_status = 0;
+ }
+
+ return (TRUE);
+}
+
+bool_t
+gssd_pname_to_uid_1_svc(pname_to_uid_args *argp, pname_to_uid_res *result, struct svc_req *rqstp)
+{
+ gss_name_t name = gssd_find_resource(argp->pname);
+ uid_t uid;
+ char buf[128];
+ struct passwd pwd, *pw;
+
+ memset(result, 0, sizeof(*result));
+ if (name) {
+ result->major_status =
+ gss_pname_to_uid(&result->minor_status,
+ name, argp->mech, &uid);
+ if (result->major_status == GSS_S_COMPLETE) {
+ result->uid = uid;
+ getpwuid_r(uid, &pwd, buf, sizeof(buf), &pw);
+ if (pw) {
+ int len = NGRPS;
+ int groups[NGRPS];
+ result->gid = pw->pw_gid;
+ getgrouplist(pw->pw_name, pw->pw_gid,
+ groups, &len);
+ result->gidlist.gidlist_len = len;
+ result->gidlist.gidlist_val =
+ mem_alloc(len * sizeof(int));
+ memcpy(result->gidlist.gidlist_val, groups,
+ len * sizeof(int));
+ } else {
+ result->gid = 65534;
+ result->gidlist.gidlist_len = 0;
+ result->gidlist.gidlist_val = NULL;
+ }
+ }
+ } else {
+ result->major_status = GSS_S_BAD_NAME;
+ result->minor_status = 0;
+ }
+
+ return (TRUE);
+}
+
+bool_t
+gssd_acquire_cred_1_svc(acquire_cred_args *argp, acquire_cred_res *result, struct svc_req *rqstp)
+{
+ gss_name_t desired_name = GSS_C_NO_NAME;
+ gss_cred_id_t cred;
+ char ccname[strlen("FILE:/tmp/krb5cc_") + 6 + 1];
+
+ snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d",
+ (int) argp->uid);
+ setenv("KRB5CCNAME", ccname, TRUE);
+
+ memset(result, 0, sizeof(*result));
+ if (argp->desired_name) {
+ desired_name = gssd_find_resource(argp->desired_name);
+ if (!desired_name) {
+ result->major_status = GSS_S_BAD_NAME;
+ return (TRUE);
+ }
+ }
+
+ result->major_status = gss_acquire_cred(&result->minor_status,
+ desired_name, argp->time_req, argp->desired_mechs,
+ argp->cred_usage, &cred, &result->actual_mechs, &result->time_rec);
+
+ if (result->major_status == GSS_S_COMPLETE)
+ result->output_cred = gssd_make_resource(cred);
+ else
+ result->output_cred = 0;
+
+ return (TRUE);
+}
+
+bool_t
+gssd_set_cred_option_1_svc(set_cred_option_args *argp, set_cred_option_res *result, struct svc_req *rqstp)
+{
+ gss_cred_id_t cred = gssd_find_resource(argp->cred);
+
+ memset(result, 0, sizeof(*result));
+ if (!cred) {
+ result->major_status = GSS_S_CREDENTIALS_EXPIRED;
+ return (TRUE);
+ }
+
+ result->major_status = gss_set_cred_option(&result->minor_status,
+ &cred, argp->option_name, &argp->option_value);
+
+ return (TRUE);
+}
+
+bool_t
+gssd_release_cred_1_svc(release_cred_args *argp, release_cred_res *result, struct svc_req *rqstp)
+{
+ gss_cred_id_t cred = gssd_find_resource(argp->cred);
+
+ if (cred) {
+ result->major_status = gss_release_cred(&result->minor_status,
+ &cred);
+ gssd_delete_resource(argp->cred);
+ } else {
+ result->major_status = GSS_S_COMPLETE;
+ result->minor_status = 0;
+ }
+
+ return (TRUE);
+}
+
+bool_t
+gssd_display_status_1_svc(display_status_args *argp, display_status_res *result, struct svc_req *rqstp)
+{
+
+ result->message_context = argp->message_context;
+ result->major_status = gss_display_status(&result->minor_status,
+ argp->status_value, argp->status_type, argp->mech_type,
+ &result->message_context, &result->status_string);
+
+ return (TRUE);
+}
+
+int
+gssd_1_freeresult(SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result)
+{
+ /*
+ * We don't use XDR to free the results - anything which was
+ * allocated came from GSS-API. We use xdr_result to figure
+ * out what to do.
+ */
+ OM_uint32 junk;
+
+ if (xdr_result == (xdrproc_t) xdr_init_sec_context_res) {
+ init_sec_context_res *p = (init_sec_context_res *) result;
+ gss_release_buffer(&junk, &p->output_token);
+ } else if (xdr_result == (xdrproc_t) xdr_accept_sec_context_res) {
+ accept_sec_context_res *p = (accept_sec_context_res *) result;
+ gss_release_buffer(&junk, &p->output_token);
+ } else if (xdr_result == (xdrproc_t) xdr_delete_sec_context_res) {
+ delete_sec_context_res *p = (delete_sec_context_res *) result;
+ gss_release_buffer(&junk, &p->output_token);
+ } else if (xdr_result == (xdrproc_t) xdr_export_sec_context_res) {
+ export_sec_context_res *p = (export_sec_context_res *) result;
+ if (p->interprocess_token.length)
+ memset(p->interprocess_token.value, 0,
+ p->interprocess_token.length);
+ gss_release_buffer(&junk, &p->interprocess_token);
+ } else if (xdr_result == (xdrproc_t) xdr_export_name_res) {
+ export_name_res *p = (export_name_res *) result;
+ gss_release_buffer(&junk, &p->exported_name);
+ } else if (xdr_result == (xdrproc_t) xdr_acquire_cred_res) {
+ acquire_cred_res *p = (acquire_cred_res *) result;
+ gss_release_oid_set(&junk, &p->actual_mechs);
+ } else if (xdr_result == (xdrproc_t) xdr_pname_to_uid_res) {
+ pname_to_uid_res *p = (pname_to_uid_res *) result;
+ if (p->gidlist.gidlist_val)
+ free(p->gidlist.gidlist_val);
+ } else if (xdr_result == (xdrproc_t) xdr_display_status_res) {
+ display_status_res *p = (display_status_res *) result;
+ gss_release_buffer(&junk, &p->status_string);
+ }
+
+ return (TRUE);
+}
diff --git a/usr.sbin/gstat/Makefile b/usr.sbin/gstat/Makefile
new file mode 100644
index 0000000..a02cf46
--- /dev/null
+++ b/usr.sbin/gstat/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= gstat
+MAN= gstat.8
+WARNS?= 5
+DPADD= ${LIBDEVSTAT} ${LIBKVM} ${LIBGEOM} ${LIBBSDXML} ${LIBSBUF} ${LIBEDIT} ${LIBCURSES}
+LDADD= -ldevstat -lkvm -lgeom -lbsdxml -lsbuf -ledit -lcurses
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/gstat/gstat.8 b/usr.sbin/gstat/gstat.8
new file mode 100644
index 0000000..9ac6789
--- /dev/null
+++ b/usr.sbin/gstat/gstat.8
@@ -0,0 +1,96 @@
+.\" Copyright (c) 2003 Giorgos Keramidas
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 12, 2009
+.Dt GSTAT 8
+.Os
+.Sh NAME
+.Nm gstat
+.Nd print statistics about GEOM disks
+.Sh SYNOPSIS
+.Nm
+.Op Fl abcd
+.Op Fl f Ar filter
+.Op Fl I Ar interval
+.Sh DESCRIPTION
+The
+.Nm
+utility can be used to monitor I/O transactions of
+.Xr geom 4
+devices.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl a
+Only display providers that are at least 0.1% busy.
+.It Fl b
+Batch mode.
+Collect numbers, print and exit.
+Default if stdout is not a tty.
+.It Fl c
+Enable display of
+.Xr geom 4
+consumers too.
+The default is to show statistics only for
+.Xr geom 4
+producers.
+.It Fl d
+Enable display of statistics for delete
+.Pq Dv BIO_DELETE
+operations.
+.It Fl f Ar filter
+A regular expression that can be used to only show statistics for some
+devices.
+Only devices with the names matching
+.Ar filter
+will be displayed.
+The format of the regular expression is described in
+.Xr re_format 7 .
+.It Fl I Ar interval
+Refresh the
+.Nm
+display every
+.Ar interval
+microseconds.
+Adding a suffix of
+.Cm s , ms ,
+or
+.Cm us
+(the default) indicates that the update interval is specified in
+seconds, milliseconds, or microseconds, respectively.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr systat 1 ,
+.Xr geom 4 ,
+.Xr iostat 8 ,
+.Xr vmstat 8
+.Sh HISTORY
+A
+.Nm
+utility appeared in
+.Fx 5.0 .
diff --git a/usr.sbin/gstat/gstat.c b/usr.sbin/gstat/gstat.c
new file mode 100644
index 0000000..39054eb
--- /dev/null
+++ b/usr.sbin/gstat/gstat.c
@@ -0,0 +1,431 @@
+/*-
+ * Copyright (c) 2003 Poul-Henning Kamp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 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/devicestat.h>
+#include <sys/mman.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+
+#include <curses.h>
+#include <devstat.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <histedit.h>
+#include <libgeom.h>
+#include <paths.h>
+#include <regex.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+static int flag_a, flag_b, flag_c, flag_d;
+static int flag_I = 1000000;
+
+#define PRINTMSG(...) do { \
+ if (flag_b && !loop) \
+ printf(__VA_ARGS__); \
+ else if (!flag_b) \
+ printw(__VA_ARGS__); \
+ } while(0)
+
+static void usage(void);
+
+static const char*
+el_prompt(void)
+{
+
+ return ("Filter: ");
+}
+
+int
+main(int argc, char **argv)
+{
+ int error, i, quit;
+ int curx, cury, maxx, maxy, line_len, loop, max_flen;
+ struct devstat *gsp, *gsq;
+ void *sp, *sq;
+ double dt;
+ struct timespec tp, tq;
+ struct gmesh gmp;
+ struct gprovider *pp;
+ struct gconsumer *cp;
+ struct gident *gid;
+ regex_t f_re, tmp_f_re;
+ short cf, cb;
+ char *p;
+ char f_s[100], pf_s[100], tmp_f_s[100];
+ const char *line;
+ long double ld[11];
+ uint64_t u64;
+ EditLine *el;
+ History *hist;
+ HistEvent hist_ev;
+
+ hist = NULL;
+ el = NULL;
+ maxx = -1;
+ curx = -1;
+ loop = 1;
+ /* Turn on batch mode if output is not tty. */
+ if (!isatty(fileno(stdout)))
+ flag_b = 1;
+
+ f_s[0] = '\0';
+ while ((i = getopt(argc, argv, "adcf:I:b")) != -1) {
+ switch (i) {
+ case 'a':
+ flag_a = 1;
+ break;
+ case 'b':
+ flag_b = 1;
+ break;
+ case 'c':
+ flag_c = 1;
+ break;
+ case 'd':
+ flag_d = 1;
+ break;
+ case 'f':
+ if (strlen(optarg) > sizeof(f_s) - 1)
+ errx(EX_USAGE, "Filter string too long");
+ if (regcomp(&f_re, optarg, REG_EXTENDED) != 0)
+ errx(EX_USAGE,
+ "Invalid filter - see re_format(7)");
+ strncpy(f_s, optarg, sizeof(f_s));
+ break;
+ case 'I':
+ p = NULL;
+ i = strtoul(optarg, &p, 0);
+ if (p == optarg || errno == EINVAL ||
+ errno == ERANGE) {
+ errx(1, "Invalid argument to -I");
+ } else if (!strcmp(p, "s"))
+ i *= 1000000;
+ else if (!strcmp(p, "ms"))
+ i *= 1000;
+ else if (!strcmp(p, "us"))
+ i *= 1;
+ flag_I = i;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 0)
+ usage();
+
+ i = geom_gettree(&gmp);
+ if (i != 0)
+ err(1, "geom_gettree = %d", i);
+ error = geom_stats_open();
+ if (error)
+ err(1, "geom_stats_open()");
+ sq = NULL;
+ sq = geom_stats_snapshot_get();
+ if (sq == NULL)
+ err(1, "geom_stats_snapshot()");
+ if (!flag_b) {
+ /* Setup curses */
+ initscr();
+ start_color();
+ use_default_colors();
+ pair_content(0, &cf, &cb);
+ init_pair(1, COLOR_GREEN, cb);
+ init_pair(2, COLOR_MAGENTA, cb);
+ init_pair(3, COLOR_RED, cb);
+ cbreak();
+ noecho();
+ nonl();
+ nodelay(stdscr, 1);
+ intrflush(stdscr, FALSE);
+ keypad(stdscr, TRUE);
+ /* Setup libedit */
+ hist = history_init();
+ if (hist == NULL)
+ err(EX_SOFTWARE, "history_init()");
+ history(hist, &hist_ev, H_SETSIZE, 100);
+ el = el_init("gstat", stdin, stdout, stderr);
+ if (el == NULL)
+ err(EX_SOFTWARE, "el_init");
+ el_set(el, EL_EDITOR, "emacs");
+ el_set(el, EL_SIGNAL, 1);
+ el_set(el, EL_HIST, history, hist);
+ el_set(el, EL_PROMPT, el_prompt);
+ if (f_s[0] != '\0')
+ history(hist, &hist_ev, H_ENTER, f_s);
+ }
+ geom_stats_snapshot_timestamp(sq, &tq);
+ for (quit = 0; !quit;) {
+ sp = geom_stats_snapshot_get();
+ if (sp == NULL)
+ err(1, "geom_stats_snapshot()");
+ geom_stats_snapshot_timestamp(sp, &tp);
+ dt = tp.tv_sec - tq.tv_sec;
+ dt += (tp.tv_nsec - tq.tv_nsec) * 1e-9;
+ tq = tp;
+
+ geom_stats_snapshot_reset(sp);
+ geom_stats_snapshot_reset(sq);
+ move(0,0);
+ PRINTMSG("dT: %5.3fs w: %.3fs", dt, (float)flag_I / 1000000);
+ if (f_s[0] != '\0') {
+ PRINTMSG(" filter: ");
+ if (!flag_b) {
+ getyx(stdscr, cury, curx);
+ getmaxyx(stdscr, maxy, maxx);
+ }
+ strncpy(pf_s, f_s, sizeof(pf_s));
+ max_flen = maxx - curx - 1;
+ if ((int)strlen(f_s) > max_flen && max_flen >= 0) {
+ if (max_flen > 3)
+ pf_s[max_flen - 3] = '.';
+ if (max_flen > 2)
+ pf_s[max_flen - 2] = '.';
+ if (max_flen > 1)
+ pf_s[max_flen - 1] = '.';
+ pf_s[max_flen] = '\0';
+ }
+ PRINTMSG("%s", pf_s);
+ }
+ PRINTMSG("\n");
+ PRINTMSG(" L(q) ops/s ");
+ PRINTMSG(" r/s kBps ms/r ");
+ PRINTMSG(" w/s kBps ms/w ");
+ if (flag_d)
+ PRINTMSG(" d/s kBps ms/d ");
+ PRINTMSG("%%busy Name\n");
+ for (;;) {
+ gsp = geom_stats_snapshot_next(sp);
+ gsq = geom_stats_snapshot_next(sq);
+ if (gsp == NULL || gsq == NULL)
+ break;
+ if (gsp->id == NULL)
+ continue;
+ gid = geom_lookupid(&gmp, gsp->id);
+ if (gid == NULL) {
+ geom_deletetree(&gmp);
+ i = geom_gettree(&gmp);
+ if (i != 0)
+ err(1, "geom_gettree = %d", i);
+ gid = geom_lookupid(&gmp, gsp->id);
+ }
+ if (gid == NULL)
+ continue;
+ if (gid->lg_what == ISCONSUMER && !flag_c)
+ continue;
+ /* Do not print past end of window */
+ if (!flag_b) {
+ getyx(stdscr, cury, curx);
+ if (curx > 0)
+ continue;
+ }
+ if ((gid->lg_what == ISPROVIDER
+ || gid->lg_what == ISCONSUMER) && f_s[0] != '\0') {
+ pp = gid->lg_ptr;
+ if ((regexec(&f_re, pp->lg_name, 0, NULL, 0)
+ != 0))
+ continue;
+ }
+ if (gsp->sequence0 != gsp->sequence1) {
+ PRINTMSG("*\n");
+ continue;
+ }
+ devstat_compute_statistics(gsp, gsq, dt,
+ DSM_QUEUE_LENGTH, &u64,
+ DSM_TRANSFERS_PER_SECOND, &ld[0],
+
+ DSM_TRANSFERS_PER_SECOND_READ, &ld[1],
+ DSM_MB_PER_SECOND_READ, &ld[2],
+ DSM_MS_PER_TRANSACTION_READ, &ld[3],
+
+ DSM_TRANSFERS_PER_SECOND_WRITE, &ld[4],
+ DSM_MB_PER_SECOND_WRITE, &ld[5],
+ DSM_MS_PER_TRANSACTION_WRITE, &ld[6],
+
+ DSM_BUSY_PCT, &ld[7],
+ DSM_TRANSFERS_PER_SECOND_FREE, &ld[8],
+ DSM_MB_PER_SECOND_FREE, &ld[9],
+ DSM_MS_PER_TRANSACTION_FREE, &ld[10],
+ DSM_NONE);
+
+ if (flag_a && ld[7] < 0.1) {
+ *gsq = *gsp;
+ continue;
+ }
+
+ PRINTMSG(" %4ju", (uintmax_t)u64);
+ PRINTMSG(" %6.0f", (double)ld[0]);
+ PRINTMSG(" %6.0f", (double)ld[1]);
+ PRINTMSG(" %6.0f", (double)ld[2] * 1024);
+ if (ld[3] > 1e3)
+ PRINTMSG(" %6.0f", (double)ld[3]);
+ else
+ PRINTMSG(" %6.1f", (double)ld[3]);
+ PRINTMSG(" %6.0f", (double)ld[4]);
+ PRINTMSG(" %6.0f", (double)ld[5] * 1024);
+ if (ld[6] > 1e3)
+ PRINTMSG(" %6.0f", (double)ld[6]);
+ else
+ PRINTMSG(" %6.1f", (double)ld[6]);
+
+ if (flag_d) {
+ PRINTMSG(" %6.0f", (double)ld[8]);
+ PRINTMSG(" %6.0f", (double)ld[9] * 1024);
+ if (ld[10] > 1e3)
+ PRINTMSG(" %6.0f", (double)ld[10]);
+ else
+ PRINTMSG(" %6.1f", (double)ld[10]);
+ }
+
+ if (ld[7] > 80)
+ i = 3;
+ else if (ld[7] > 50)
+ i = 2;
+ else
+ i = 1;
+ if (!flag_b)
+ attron(COLOR_PAIR(i));
+ PRINTMSG(" %6.1lf", (double)ld[7]);
+ if (!flag_b) {
+ attroff(COLOR_PAIR(i));
+ PRINTMSG("|");
+ } else
+ PRINTMSG(" ");
+ if (gid == NULL) {
+ PRINTMSG(" ??");
+ } else if (gid->lg_what == ISPROVIDER) {
+ pp = gid->lg_ptr;
+ PRINTMSG(" %s", pp->lg_name);
+ } else if (gid->lg_what == ISCONSUMER) {
+ cp = gid->lg_ptr;
+ PRINTMSG(" %s/%s/%s",
+ cp->lg_geom->lg_class->lg_name,
+ cp->lg_geom->lg_name,
+ cp->lg_provider->lg_name);
+ }
+ if (!flag_b)
+ clrtoeol();
+ PRINTMSG("\n");
+ *gsq = *gsp;
+ }
+ geom_stats_snapshot_free(sp);
+ if (flag_b) {
+ /* We loop extra to make sure we get the information. */
+ if (!loop)
+ break;
+ loop = 0;
+ usleep(flag_I);
+ continue;
+ }
+ getyx(stdscr, cury, curx);
+ getmaxyx(stdscr, maxy, maxx);
+ clrtobot();
+ if (maxy - 1 <= cury)
+ move(maxy - 1, 0);
+ refresh();
+ usleep(flag_I);
+ while((i = getch()) != ERR) {
+ switch (i) {
+ case '>':
+ flag_I *= 2;
+ break;
+ case '<':
+ flag_I /= 2;
+ if (flag_I < 1000)
+ flag_I = 1000;
+ break;
+ case 'c':
+ flag_c = !flag_c;
+ break;
+ case 'f':
+ move(0,0);
+ clrtoeol();
+ refresh();
+ line = el_gets(el, &line_len);
+ if (line == NULL)
+ err(1, "el_gets");
+ if (line_len > 1)
+ history(hist, &hist_ev, H_ENTER, line);
+ strncpy(tmp_f_s, line, sizeof(f_s));
+ if ((p = strchr(tmp_f_s, '\n')) != NULL)
+ *p = '\0';
+ /*
+ * We have to clear since we messed up
+ * curses idea of the screen by using
+ * libedit.
+ */
+ clear();
+ refresh();
+ if (regcomp(&tmp_f_re, tmp_f_s, REG_EXTENDED)
+ != 0) {
+ move(0, 0);
+ printw("Invalid filter");
+ refresh();
+ sleep(1);
+ } else {
+ strncpy(f_s, tmp_f_s, sizeof(f_s));
+ f_re = tmp_f_re;
+ }
+ break;
+ case 'F':
+ f_s[0] = '\0';
+ break;
+ case 'q':
+ quit = 1;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (!flag_b) {
+ endwin();
+ el_end(el);
+ }
+ exit(EX_OK);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: gstat [-abcd] [-f filter] [-I interval]\n");
+ exit(EX_USAGE);
+ /* NOTREACHED */
+}
diff --git a/usr.sbin/i2c/Makefile b/usr.sbin/i2c/Makefile
new file mode 100644
index 0000000..9f377e6
--- /dev/null
+++ b/usr.sbin/i2c/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= i2c
+MAN= i2c.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/i2c/i2c.8 b/usr.sbin/i2c/i2c.8
new file mode 100644
index 0000000..0067be7
--- /dev/null
+++ b/usr.sbin/i2c/i2c.8
@@ -0,0 +1,168 @@
+.\"
+.\" Copyright (C) 2008-2009 Semihalf, Michal Hajduk and Bartlomiej Sieka
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 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 Jan 23, 2009
+.Dt I2C 8
+.Os
+.Sh NAME
+.Nm i2c
+.Nd test I2C bus and slave devices
+.Sh SYNOPSIS
+.Nm
+.Cm -a Ar address
+.Op Fl f Ar device
+.Op Fl d Ar r|w
+.Op Fl w Ar 0|8|16
+.Op Fl o Ar offset
+.Op Fl c Ar count
+.Op Fl m Ar ss|rs|no
+.Op Fl b
+.Op Fl v
+.Nm
+.Cm -s
+.Op Fl f Ar device
+.Op Fl n Ar skip_addr
+.Op Fl v
+.Nm
+.Cm -r
+.Op Fl f Ar device
+.Op Fl v
+.Sh DESCRIPTION
+The
+.Nm
+utility can be used to perform raw data transfers (read or write) with devices
+on the I2C bus. It can also scan the bus for available devices and reset the
+I2C controller.
+.Pp
+The options are as follows:
+.Bl -tag -width ".Fl d Ar direction"
+.It Fl a Ar address
+7-bit address on the I2C device to operate on (hex).
+.It Fl b
+binary mode - when performing a read operation, the data read from the device
+is output in binary format on stdout; when doing a write, the binary data to
+be written to the device is read from stdin.
+.It Fl c Ar count
+number of bytes to transfer (dec).
+.It Fl d Ar r|w
+transfer direction: r - read, w - write.
+.It Fl f Ar device
+I2C bus to use (default is /dev/iic0).
+.It Fl m Ar ss|rs|no
+addressing mode, i.e., I2C bus operations performed after the offset for the
+transfer has been written to the device and before the actual read/write
+operation. rs - repeated start; ss - stop start; no - none.
+.It Fl n Ar skip_addr
+skip address - address(es) to be skipped during bus scan.
+The are two ways to specify addresses to ignore: by range 'a..b' or
+using selected addresses 'a:b:c'. This option is available only when "-s" is
+used.
+.It Fl o Ar offset
+offset within the device for data transfer (hex).
+.It Fl r
+reset the controller.
+.It Fl s
+scan the bus for devices.
+.It Fl v
+be verbose
+.It Fl w Ar 0|8|16
+device addressing width (in bits).
+.El
+.Sh WARNINGS
+Great care must be taken when manipulating slave I2C devices with the
+.Nm
+utility. Often times important configuration data for the system is kept in
+non-volatile but write enabled memories located on the I2C bus, for example
+Ethernet hardware addresses, RAM module parameters (SPD), processor reset
+configuration word etc.
+.Pp
+It is very easy to render the whole system unusable when such configuration
+data is deleted or altered, so use the
+.Dq -d w
+(write) command only if you know exactly what you are doing.
+.Pp
+Also avoid ungraceful interrupting of an ongoing transaction on the I2C bus,
+as it can lead to potentially dangerous effects. Consider the following
+scenario: when the host CPU is reset (for whatever reason) in the middle of a
+started I2C transaction, the I2C slave device could be left in write mode
+waiting for data or offset to arrive. When the CPU reinitializes itself and
+talks to this I2C slave device again, the commands and other control info it
+sends are treated by the slave device as data or offset it was waiting for,
+and there's great potential for corruption if such a write is performed.
+.Sh EXAMPLES
+.Pp
+.Bl -bullet
+.It
+Scan the default bus (/dev/iic0) for devices:
+.Pp
+i2c -s
+.It
+Scan the default bus (/dev/iic0) for devices and skip addresses 0x56 and
+0x45.
+.Pp
+i2c -s -n 0x56:0x45
+.It
+Scan the default bus (/dev/iic0) for devices and skip address range
+0x34 to 0x56.
+.Pp
+i2c -s -n 0x34..0x56
+.It
+Read 8 bytes of data from device at address 0x56 (e.g., an EEPROM):
+.Pp
+i2c -a 0x56 -d r -c 8
+.It
+Write 16 bytes of data from file data.bin to device 0x56 at offset 0x10:
+.Pp
+i2c -a 0x56 -d w -c 16 -o 0x10 -b < data.bin
+.It
+Copy 4 bytes between two EEPROMs (0x56 on /dev/iic1 to 0x57 on /dev/iic0):
+.Pp
+i2c -a 0x56 -f /dev/iic1 -d r -c 0x4 -b | i2c -a 0x57 -f /dev/iic0 -d w -c 4 -b
+.It
+Reset the controller:
+.Pp
+i2c -f /dev/iic1 -r
+.El
+.Sh SEE ALSO
+.Xr iic 4 ,
+.Xr iicbus 4
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 8.0 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility and this manual page were written by
+.An Bartlomiej Sieka
+.Aq tur@semihalf.com
+and
+.An Michal Hajduk
+.Aq mih@semihalf.com .
diff --git a/usr.sbin/i2c/i2c.c b/usr.sbin/i2c/i2c.c
new file mode 100644
index 0000000..c0b3663
--- /dev/null
+++ b/usr.sbin/i2c/i2c.c
@@ -0,0 +1,633 @@
+/*-
+ * Copyright (C) 2008-2009 Semihalf, Michal Hajduk and Bartlomiej Sieka
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 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 <errno.h>
+#include <sysexits.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include <dev/iicbus/iic.h>
+
+#define I2C_DEV "/dev/iic0"
+#define I2C_MODE_NOTSET 0
+#define I2C_MODE_NONE 1
+#define I2C_MODE_STOP_START 2
+#define I2C_MODE_REPEATED_START 3
+
+struct options {
+ int width;
+ int count;
+ int verbose;
+ int addr_set;
+ int binary;
+ int scan;
+ int skip;
+ int reset;
+ int mode;
+ char dir;
+ uint32_t addr;
+ uint32_t off;
+};
+
+struct skip_range {
+ int start;
+ int end;
+};
+
+__dead2 static void
+usage(void)
+{
+
+ fprintf(stderr, "usage: %s -a addr [-f device] [-d [r|w]] [-o offset] "
+ "[-w [0|8|16]] [-c count] [-m [ss|rs|no]] [-b] [-v]\n",
+ getprogname());
+ fprintf(stderr, " %s -s [-f device] [-n skip_addr] -v\n",
+ getprogname());
+ fprintf(stderr, " %s -r [-f device] -v\n", getprogname());
+ exit(EX_USAGE);
+}
+
+static struct skip_range
+skip_get_range(char *skip_addr)
+{
+ struct skip_range addr_range;
+ char *token;
+
+ addr_range.start = 0;
+ addr_range.end = 0;
+
+ token = strsep(&skip_addr, "..");
+ if (token) {
+ addr_range.start = strtoul(token, 0, 16);
+ token = strsep(&skip_addr, "..");
+ if ((token != NULL) && !atoi(token)) {
+ token = strsep(&skip_addr, "..");
+ if (token)
+ addr_range.end = strtoul(token, 0, 16);
+ }
+ }
+
+ return (addr_range);
+}
+
+/* Parse the string to get hex 7 bits addresses */
+static int
+skip_get_tokens(char *skip_addr, int *sk_addr, int max_index)
+{
+ char *token;
+ int i;
+
+ for (i = 0; i < max_index; i++) {
+ token = strsep(&skip_addr, ":");
+ if (token == NULL)
+ break;
+ sk_addr[i] = strtoul(token, 0, 16);
+ }
+ return (i);
+}
+
+static int
+scan_bus(struct iiccmd cmd, char *dev, int skip, char *skip_addr)
+{
+ struct skip_range addr_range = { 0, 0 };
+ int *tokens, fd, error, i, index, j;
+ int len = 0, do_skip = 0, no_range = 1;
+
+ fd = open(dev, O_RDWR);
+ if (fd == -1) {
+ fprintf(stderr, "Error opening I2C controller (%s) for "
+ "scanning: %s\n", dev, strerror(errno));
+ return (EX_NOINPUT);
+ }
+
+ if (skip) {
+ len = strlen(skip_addr);
+ if (strstr(skip_addr, "..") != NULL) {
+ addr_range = skip_get_range(skip_addr);
+ no_range = 0;
+ } else {
+ tokens = (int *)malloc((len / 2 + 1) * sizeof(int));
+ if (tokens == NULL) {
+ fprintf(stderr, "Error allocating tokens "
+ "buffer\n");
+ goto out;
+ }
+ index = skip_get_tokens(skip_addr, tokens,
+ len / 2 + 1);
+ }
+
+ if (!no_range && (addr_range.start > addr_range.end)) {
+ fprintf(stderr, "Skip address out of range\n");
+ goto out;
+ }
+ }
+
+ printf("Scanning I2C devices on %s: ", dev);
+ for (i = 1; i < 127; i++) {
+
+ if (skip && ( addr_range.start < addr_range.end)) {
+ if (i >= addr_range.start && i <= addr_range.end)
+ continue;
+
+ } else if (skip && no_range)
+ for (j = 0; j < index; j++) {
+ if (tokens[j] == i) {
+ do_skip = 1;
+ break;
+ }
+ }
+
+ if (do_skip) {
+ do_skip = 0;
+ continue;
+ }
+
+ cmd.slave = i << 1;
+ cmd.last = 1;
+ cmd.count = 0;
+ error = ioctl(fd, I2CRSTCARD, &cmd);
+ if (error)
+ goto out;
+
+ cmd.slave = i << 1;
+ cmd.last = 1;
+ error = ioctl(fd, I2CSTART, &cmd);
+ if (!error)
+ printf("%x ", i);
+ cmd.slave = i << 1;
+ cmd.last = 1;
+ error = ioctl(fd, I2CSTOP, &cmd);
+ }
+ printf("\n");
+
+ error = ioctl(fd, I2CRSTCARD, &cmd);
+out:
+ close(fd);
+ if (skip && no_range)
+ free(tokens);
+
+ if (error) {
+ fprintf(stderr, "Error scanning I2C controller (%s): %s\n",
+ dev, strerror(errno));
+ return (EX_NOINPUT);
+ } else
+ return (EX_OK);
+}
+
+static int
+reset_bus(struct iiccmd cmd, char *dev)
+{
+ int fd, error;
+
+ fd = open(dev, O_RDWR);
+ if (fd == -1) {
+ fprintf(stderr, "Error opening I2C controller (%s) for "
+ "resetting: %s\n", dev, strerror(errno));
+ return (EX_NOINPUT);
+ }
+
+ printf("Resetting I2C controller on %s: ", dev);
+ error = ioctl(fd, I2CRSTCARD, &cmd);
+ close (fd);
+
+ if (error) {
+ printf("error: %s\n", strerror(errno));
+ return (EX_IOERR);
+ } else {
+ printf("OK\n");
+ return (EX_OK);
+ }
+}
+
+static char *
+prepare_buf(int size, uint32_t off)
+{
+ char *buf;
+
+ buf = malloc(size);
+ if (buf == NULL)
+ return (buf);
+
+ if (size == 1)
+ buf[0] = off & 0xff;
+ else if (size == 2) {
+ buf[0] = (off >> 8) & 0xff;
+ buf[1] = off & 0xff;
+ }
+
+ return (buf);
+}
+
+static int
+i2c_write(char *dev, struct options i2c_opt, char *i2c_buf)
+{
+ struct iiccmd cmd;
+ int ch, i, error, fd, bufsize;
+ char *err_msg, *buf;
+
+ /*
+ * Read data to be written to the chip from stdin
+ */
+ if (i2c_opt.verbose && !i2c_opt.binary)
+ fprintf(stderr, "Enter %u bytes of data: ", i2c_opt.count);
+
+ for (i = 0; i < i2c_opt.count; i++) {
+ ch = getchar();
+ if (ch == EOF) {
+ free(i2c_buf);
+ err(1, "not enough data, exiting\n");
+ }
+ i2c_buf[i] = ch;
+ }
+
+ fd = open(dev, O_RDWR);
+ if (fd == -1) {
+ free(i2c_buf);
+ err(1, "open failed");
+ }
+
+ /*
+ * Write offset where the data will go
+ */
+ cmd.slave = i2c_opt.addr;
+ error = ioctl(fd, I2CSTART, &cmd);
+ if (error == -1) {
+ err_msg = "ioctl: error sending start condition";
+ goto err1;
+ }
+
+ if (i2c_opt.width) {
+ bufsize = i2c_opt.width / 8;
+ buf = prepare_buf(bufsize, i2c_opt.off);
+ if (buf == NULL) {
+ err_msg = "error: offset malloc";
+ goto err1;
+ }
+
+ cmd.count = bufsize;
+ cmd.buf = buf;
+ error = ioctl(fd, I2CWRITE, &cmd);
+ free(buf);
+ if (error == -1) {
+ err_msg = "ioctl: error when write offset";
+ goto err1;
+ }
+ }
+
+ /* Mode - stop start */
+ if (i2c_opt.mode == I2C_MODE_STOP_START) {
+ cmd.slave = i2c_opt.addr;
+ error = ioctl(fd, I2CSTOP, &cmd);
+ if (error == -1) {
+ err_msg = "ioctl: error sending stop condition";
+ goto err2;
+ }
+ cmd.slave = i2c_opt.addr;
+ error = ioctl(fd, I2CSTART, &cmd);
+ if (error == -1) {
+ err_msg = "ioctl: error sending start condition";
+ goto err1;
+ }
+ }
+ /* Mode - repeated start */
+ if (i2c_opt.mode == I2C_MODE_REPEATED_START) {
+ cmd.slave = i2c_opt.addr;
+ error = ioctl(fd, I2CRPTSTART, &cmd);
+ if (error == -1) {
+ err_msg = "ioctl: error sending repeated start "
+ "condition";
+ goto err1;
+ }
+ }
+
+ /*
+ * Write the data
+ */
+ cmd.count = i2c_opt.count;
+ cmd.buf = i2c_buf;
+ cmd.last = 0;
+ error = ioctl(fd, I2CWRITE, &cmd);
+ if (error == -1) {
+ err_msg = "ioctl: error when write";
+ goto err1;
+ }
+ cmd.slave = i2c_opt.addr;
+ error = ioctl(fd, I2CSTOP, &cmd);
+ if (error == -1) {
+ err_msg = "ioctl: error sending stop condition";
+ goto err2;
+ }
+
+ close(fd);
+ return (0);
+
+err1:
+ cmd.slave = i2c_opt.addr;
+ error = ioctl(fd, I2CSTOP, &cmd);
+ if (error == -1)
+ fprintf(stderr, "error sending stop condtion\n");
+err2:
+ if (err_msg)
+ fprintf(stderr, err_msg);
+
+ close(fd);
+ return (1);
+}
+
+static int
+i2c_read(char *dev, struct options i2c_opt, char *i2c_buf)
+{
+ struct iiccmd cmd;
+ int i, fd, error, bufsize;
+ char *err_msg, data = 0, *buf;
+
+ fd = open(dev, O_RDWR);
+ if (fd == -1)
+ err(1, "open failed");
+
+ bzero(&cmd, sizeof(cmd));
+
+ if (i2c_opt.width) {
+ cmd.slave = i2c_opt.addr;
+ cmd.count = 1;
+ cmd.last = 0;
+ cmd.buf = &data;
+ error = ioctl(fd, I2CSTART, &cmd);
+ if (error == -1) {
+ err_msg = "ioctl: error sending start condition";
+ goto err1;
+ }
+ bufsize = i2c_opt.width / 8;
+ buf = prepare_buf(bufsize, i2c_opt.off);
+ if (buf == NULL) {
+ err_msg = "error: offset malloc";
+ goto err1;
+ }
+
+ cmd.count = bufsize;
+ cmd.buf = buf;
+ cmd.last = 0;
+ error = ioctl(fd, I2CWRITE, &cmd);
+ free(buf);
+ if (error == -1) {
+ err_msg = "ioctl: error when write offset";
+ goto err1;
+ }
+
+ if (i2c_opt.mode == I2C_MODE_STOP_START) {
+ cmd.slave = i2c_opt.addr;
+ error = ioctl(fd, I2CSTOP, &cmd);
+ if (error == -1)
+ goto err2;
+ }
+ }
+ cmd.slave = i2c_opt.addr;
+ cmd.count = 1;
+ cmd.last = 0;
+ cmd.buf = &data;
+ if (i2c_opt.mode == I2C_MODE_STOP_START) {
+ error = ioctl(fd, I2CSTART, &cmd);
+ if (error == -1) {
+ err_msg = "ioctl: error sending start condition";
+ goto err1;
+ }
+ } else if (i2c_opt.mode == I2C_MODE_REPEATED_START) {
+ error = ioctl(fd, I2CRPTSTART, &cmd);
+ if (error == -1) {
+ err_msg = "ioctl: error sending repeated start "
+ "condition";
+ goto err1;
+ }
+ }
+ error = ioctl(fd, I2CSTOP, &cmd);
+ if (error == -1)
+ goto err2;
+
+ for (i = 0; i < i2c_opt.count; i++) {
+ error = read(fd, &i2c_buf[i], 1);
+ if (error == -1) {
+ err_msg = "ioctl: error while reading";
+ goto err1;
+ }
+ }
+
+ close(fd);
+ return (0);
+
+err1:
+ cmd.slave = i2c_opt.addr;
+ error = ioctl(fd, I2CSTOP, &cmd);
+ if (error == -1)
+ fprintf(stderr, "error sending stop condtion\n");
+err2:
+ if (err_msg)
+ fprintf(stderr, err_msg);
+
+ close(fd);
+ return (1);
+}
+
+int
+main(int argc, char** argv)
+{
+ struct iiccmd cmd;
+ struct options i2c_opt;
+ char *dev, *skip_addr, *err_msg, *i2c_buf;
+ int error, chunk_size, i, j, ch;
+
+ errno = 0;
+ error = 0;
+
+ /* Line-break the output every chunk_size bytes */
+ chunk_size = 16;
+
+ dev = I2C_DEV;
+ err_msg = NULL;
+
+ /* Default values */
+ i2c_opt.addr_set = 0;
+ i2c_opt.off = 0;
+ i2c_opt.verbose = 0;
+ i2c_opt.dir = 'r'; /* direction = read */
+ i2c_opt.width = 8;
+ i2c_opt.count = 1;
+ i2c_opt.binary = 0; /* ASCII text output */
+ i2c_opt.scan = 0; /* no bus scan */
+ i2c_opt.skip = 0; /* scan all addresses */
+ i2c_opt.reset = 0; /* no bus reset */
+ i2c_opt.mode = I2C_MODE_NOTSET;
+
+ while ((ch = getopt(argc, argv, "a:f:d:o:w:c:m:n:sbvrh")) != -1) {
+ switch(ch) {
+ case 'a':
+ i2c_opt.addr = (strtoul(optarg, 0, 16) << 1);
+ if (i2c_opt.addr == 0 && errno == EINVAL)
+ i2c_opt.addr_set = 0;
+ else
+ i2c_opt.addr_set = 1;
+ break;
+ case 'f':
+ dev = optarg;
+ break;
+ case 'd':
+ i2c_opt.dir = optarg[0];
+ break;
+ case 'o':
+ i2c_opt.off = strtoul(optarg, 0, 16);
+ if (i2c_opt.off == 0 && errno == EINVAL)
+ error = 1;
+ break;
+ case 'w':
+ i2c_opt.width = atoi(optarg);
+ break;
+ case 'c':
+ i2c_opt.count = atoi(optarg);
+ break;
+ case 'm':
+ if (!strcmp(optarg, "no"))
+ i2c_opt.mode = I2C_MODE_NONE;
+ else if (!strcmp(optarg, "ss"))
+ i2c_opt.mode = I2C_MODE_STOP_START;
+ else if (!strcmp(optarg, "rs"))
+ i2c_opt.mode = I2C_MODE_REPEATED_START;
+ else
+ usage();
+ break;
+ case 'n':
+ i2c_opt.skip = 1;
+ skip_addr = optarg;
+ break;
+ case 's':
+ i2c_opt.scan = 1;
+ break;
+ case 'b':
+ i2c_opt.binary = 1;
+ break;
+ case 'v':
+ i2c_opt.verbose = 1;
+ break;
+ case 'r':
+ i2c_opt.reset = 1;
+ break;
+ case 'h':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* Set default mode if option -m is not specified */
+ if (i2c_opt.mode == I2C_MODE_NOTSET) {
+ if (i2c_opt.dir == 'r')
+ i2c_opt.mode = I2C_MODE_STOP_START;
+ else if (i2c_opt.dir == 'w')
+ i2c_opt.mode = I2C_MODE_NONE;
+ }
+
+ /* Basic sanity check of command line arguments */
+ if (i2c_opt.scan) {
+ if (i2c_opt.addr_set)
+ usage();
+ } else if (i2c_opt.reset) {
+ if (i2c_opt.addr_set)
+ usage();
+ } else if (error) {
+ usage();
+ } else if ((i2c_opt.dir == 'r' || i2c_opt.dir == 'w')) {
+ if ((i2c_opt.addr_set == 0) ||
+ !(i2c_opt.width == 0 || i2c_opt.width == 8 ||
+ i2c_opt.width == 16))
+ usage();
+ }
+
+ if (i2c_opt.verbose)
+ fprintf(stderr, "dev: %s, addr: 0x%x, r/w: %c, "
+ "offset: 0x%02x, width: %u, count: %u\n", dev,
+ i2c_opt.addr >> 1, i2c_opt.dir, i2c_opt.off,
+ i2c_opt.width, i2c_opt.count);
+
+ if (i2c_opt.scan)
+ exit(scan_bus(cmd, dev, i2c_opt.skip, skip_addr));
+
+ if (i2c_opt.reset)
+ exit(reset_bus(cmd, dev));
+
+ i2c_buf = malloc(i2c_opt.count);
+ if (i2c_buf == NULL)
+ err(1, "data malloc");
+
+ if (i2c_opt.dir == 'w') {
+ error = i2c_write(dev, i2c_opt, i2c_buf);
+ if (error) {
+ free(i2c_buf);
+ return (1);
+ }
+ }
+ if (i2c_opt.dir == 'r') {
+ error = i2c_read(dev, i2c_opt, i2c_buf);
+ if (error) {
+ free(i2c_buf);
+ return (1);
+ }
+ }
+
+ if (i2c_opt.verbose)
+ fprintf(stderr, "\nData %s (hex):\n", i2c_opt.dir == 'r' ?
+ "read" : "written");
+
+ i = 0;
+ j = 0;
+ while (i < i2c_opt.count) {
+ if (i2c_opt.verbose || (i2c_opt.dir == 'r' &&
+ !i2c_opt.binary))
+ fprintf (stderr, "%02hhx ", i2c_buf[i++]);
+
+ if (i2c_opt.dir == 'r' && i2c_opt.binary) {
+ fprintf(stdout, "%c", i2c_buf[j++]);
+ if(!i2c_opt.verbose)
+ i++;
+ }
+ if (!i2c_opt.verbose && (i2c_opt.dir == 'w'))
+ break;
+ if ((i % chunk_size) == 0)
+ fprintf(stderr, "\n");
+ }
+ if ((i % chunk_size) != 0)
+ fprintf(stderr, "\n");
+
+ free(i2c_buf);
+ return (0);
+}
diff --git a/usr.sbin/ifmcstat/Makefile b/usr.sbin/ifmcstat/Makefile
new file mode 100644
index 0000000..fab7ea6
--- /dev/null
+++ b/usr.sbin/ifmcstat/Makefile
@@ -0,0 +1,24 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+PROG= ifmcstat
+SRCS= ifmcstat.c printb.c
+
+MAN= ifmcstat.8
+BINMODE= 555
+
+WARNS?= 2
+
+.if ${MK_INET6_SUPPORT} != "no"
+CFLAGS+=-DINET6
+.endif
+
+.if ${MK_KVM_SUPPORT} != "no"
+CFLAGS+=-DWITH_KVM
+DPADD= ${LIBKVM}
+LDADD= -lkvm
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ifmcstat/ifmcstat.8 b/usr.sbin/ifmcstat/ifmcstat.8
new file mode 100644
index 0000000..5805183
--- /dev/null
+++ b/usr.sbin/ifmcstat/ifmcstat.8
@@ -0,0 +1,133 @@
+.\" $KAME: ifmcstat.8,v 1.6 2002/10/31 04:23:43 suz Exp $
+.\"
+.\" Copyright (c) 2007-2009 Bruce Simpson.
+.\" 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 February 28, 2009
+.Dt IFMCSTAT 8
+.Os
+.Sh NAME
+.Nm ifmcstat
+.Nd dump multicast group management statistics per interface
+.Sh SYNOPSIS
+.Nm
+.Op Fl i Ar interface
+.Op Fl f Ar address-family
+.Op Fl v
+.Op Fl K
+.Op Fl M Ar core
+.Op Fl N Ar system
+.\"
+.Sh DESCRIPTION
+The
+.Nm
+command dumps multicast group information from the kernel.
+.Pp
+The following options are supported:
+.Bl -tag -width Fl
+.It Fl i Ar interface
+specifies the interface to be displayed.
+.Pp
+.It Fl f Ar address-family
+specifies the address family to be displayed;
+.Ar inet ,
+.Ar inet6
+and
+.Ar link
+are supported.
+.It Fl v
+specifies that link-layer memberships should be printed;
+they are suppressed by default.
+It may not be specified for
+.Fl f Ar link .
+Source lists for each group will also be printed.
+.Pp
+If specified twice, and
+.Xr kvm 3
+is in use, the IGMP timers for each interface
+and the IGMP source list counters for each group
+will also be printed.
+.El
+.Pp
+The following options are only available if
+.Nm
+has been built with support for
+.Xr kvm 3 :
+.Bl -tag -width Fl
+.It Fl K
+forces the use of
+.Xr kvm 3
+to be disabled.
+.It Fl M Ar core
+extracts values associated with the name list from the specified core,
+instead of the default
+.Pa /dev/kmem .
+.It Fl N Ar system
+extracts the name list from the specified kernel instead of the
+default, which is the kernel image the system has booted from.
+.El
+.Sh IMPLEMENTATION NOTES
+When run with the
+.Fl v
+option,
+.Nm
+may print multicast MAC addresses twice if they are
+referenced by a layer 3 protocol.
+.Pp
+When run with
+.Xr kvm 3
+support,
+the names of all interfaces configured in the system will be
+printed in the first column of output, even if no multicast
+group memberships are present on those interfaces.
+The output may also be slightly different, as the kernel
+data structures are being traversed with minimal post-processing
+of the output.
+.Pp
+When built without
+.Xr kvm 3
+support, the information displayed by
+.Nm
+is more limited.
+This support is recommended for debugging purposes.
+It requires super-user privilege if used to inspect a running kernel.
+.Pp
+The
+.Xr kvm 3
+back-end will be used by default if
+.Nm
+is run with super-user privileges, unless the
+.Fl K
+option is specified.
+.Sh SEE ALSO
+.Xr getifaddrs 3 ,
+.Xr getifmaddrs 3 ,
+.Xr kvm 3 ,
+.Xr netstat 8
diff --git a/usr.sbin/ifmcstat/ifmcstat.c b/usr.sbin/ifmcstat/ifmcstat.c
new file mode 100644
index 0000000..34068ea
--- /dev/null
+++ b/usr.sbin/ifmcstat/ifmcstat.c
@@ -0,0 +1,1061 @@
+/* $KAME: ifmcstat.c,v 1.48 2006/11/15 05:13:59 itojun Exp $ */
+
+/*
+ * Copyright (c) 2007-2009 Bruce Simpson.
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/tree.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/igmp.h>
+#define KERNEL
+# include <netinet/if_ether.h>
+#undef KERNEL
+#define _KERNEL
+#define SYSCTL_DECL(x)
+# include <netinet/igmp_var.h>
+#undef SYSCTL_DECL
+#undef _KERNEL
+
+#ifdef INET6
+#include <netinet/icmp6.h>
+#define _KERNEL
+# include <netinet6/mld6_var.h>
+#undef _KERNEL
+#endif /* INET6 */
+
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <limits.h>
+#include <ifaddrs.h>
+#include <nlist.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+/* XXX: This file currently assumes INET and KVM support in the base system. */
+#ifndef INET
+#define INET
+#endif
+
+extern void printb(const char *, unsigned int, const char *);
+
+union sockunion {
+ struct sockaddr_storage ss;
+ struct sockaddr sa;
+ struct sockaddr_dl sdl;
+#ifdef INET
+ struct sockaddr_in sin;
+#endif
+#ifdef INET6
+ struct sockaddr_in6 sin6;
+#endif
+};
+typedef union sockunion sockunion_t;
+
+uint32_t ifindex = 0;
+int af = AF_UNSPEC;
+#ifdef WITH_KVM
+int Kflag = 0;
+#endif
+int vflag = 0;
+
+#define sa_equal(a1, a2) \
+ (bcmp((a1), (a2), ((a1))->sa_len) == 0)
+
+#define sa_dl_equal(a1, a2) \
+ ((((struct sockaddr_dl *)(a1))->sdl_len == \
+ ((struct sockaddr_dl *)(a2))->sdl_len) && \
+ (bcmp(LLADDR((struct sockaddr_dl *)(a1)), \
+ LLADDR((struct sockaddr_dl *)(a2)), \
+ ((struct sockaddr_dl *)(a1))->sdl_alen) == 0))
+
+/*
+ * Most of the code in this utility is to support the use of KVM for
+ * post-mortem debugging of the multicast code.
+ */
+#ifdef WITH_KVM
+
+#ifdef INET
+static void if_addrlist(struct ifaddr *);
+static struct in_multi *
+ in_multientry(struct in_multi *);
+#endif /* INET */
+
+#ifdef INET6
+static void if6_addrlist(struct ifaddr *);
+static struct in6_multi *
+ in6_multientry(struct in6_multi *);
+#endif /* INET6 */
+
+static void kread(u_long, void *, int);
+static void ll_addrlist(struct ifaddr *);
+
+static int ifmcstat_kvm(const char *kernel, const char *core);
+
+#define KREAD(addr, buf, type) \
+ kread((u_long)addr, (void *)buf, sizeof(type))
+
+kvm_t *kvmd;
+struct nlist nl[] = {
+ { "_ifnet", 0, 0, 0, 0, },
+ { "", 0, 0, 0, 0, },
+};
+#define N_IFNET 0
+
+#endif /* WITH_KVM */
+
+static int ifmcstat_getifmaddrs(void);
+#ifdef INET
+static void in_ifinfo(struct igmp_ifinfo *);
+static const char * inm_mode(u_int mode);
+#endif
+#ifdef INET6
+static const char * inet6_n2a(struct in6_addr *);
+#endif
+int main(int, char **);
+
+static void
+usage()
+{
+
+ fprintf(stderr,
+ "usage: ifmcstat [-i interface] [-f address family]"
+ " [-v]"
+#ifdef WITH_KVM
+ " [-K] [-M core] [-N system]"
+#endif
+ "\n");
+ exit(EX_USAGE);
+}
+
+static const char *options = "i:f:vM:N:"
+#ifdef WITH_KVM
+ "K"
+#endif
+ ;
+
+int
+main(int argc, char **argv)
+{
+ int c, error;
+#ifdef WITH_KVM
+ const char *kernel = NULL;
+ const char *core = NULL;
+#endif
+
+ while ((c = getopt(argc, argv, options)) != -1) {
+ switch (c) {
+ case 'i':
+ if ((ifindex = if_nametoindex(optarg)) == 0) {
+ fprintf(stderr, "%s: unknown interface\n",
+ optarg);
+ exit(EX_NOHOST);
+ }
+ break;
+
+ case 'f':
+#ifdef INET
+ if (strcmp(optarg, "inet") == 0) {
+ af = AF_INET;
+ break;
+ }
+#endif
+#ifdef INET6
+ if (strcmp(optarg, "inet6") == 0) {
+ af = AF_INET6;
+ break;
+ }
+#endif
+ if (strcmp(optarg, "link") == 0) {
+ af = AF_LINK;
+ break;
+ }
+ fprintf(stderr, "%s: unknown address family\n", optarg);
+ exit(EX_USAGE);
+ /*NOTREACHED*/
+ break;
+
+#ifdef WITH_KVM
+ case 'K':
+ ++Kflag;
+ break;
+#endif
+
+ case 'v':
+ ++vflag;
+ break;
+
+#ifdef WITH_KVM
+ case 'M':
+ core = strdup(optarg);
+ break;
+
+ case 'N':
+ kernel = strdup(optarg);
+ break;
+#endif
+
+ default:
+ usage();
+ break;
+ /*NOTREACHED*/
+ }
+ }
+
+ if (af == AF_LINK && vflag)
+ usage();
+
+#ifdef WITH_KVM
+ if (!Kflag)
+ error = ifmcstat_kvm(kernel, core);
+ /*
+ * If KVM failed, and user did not explicitly specify a core file,
+ * or force KVM backend to be disabled, try the sysctl backend.
+ */
+ if (Kflag || (error != 0 && (core == NULL && kernel == NULL)))
+#endif
+ error = ifmcstat_getifmaddrs();
+ if (error != 0)
+ exit(EX_OSERR);
+
+ exit(EX_OK);
+ /*NOTREACHED*/
+}
+
+#ifdef INET
+
+static void
+in_ifinfo(struct igmp_ifinfo *igi)
+{
+
+ printf("\t");
+ switch (igi->igi_version) {
+ case IGMP_VERSION_1:
+ case IGMP_VERSION_2:
+ case IGMP_VERSION_3:
+ printf("igmpv%d", igi->igi_version);
+ break;
+ default:
+ printf("igmpv?(%d)", igi->igi_version);
+ break;
+ }
+ printb(" flags", igi->igi_flags, "\020\1SILENT\2LOOPBACK");
+ if (igi->igi_version == IGMP_VERSION_3) {
+ printf(" rv %u qi %u qri %u uri %u",
+ igi->igi_rv, igi->igi_qi, igi->igi_qri, igi->igi_uri);
+ }
+ if (vflag >= 2) {
+ printf(" v1timer %u v2timer %u v3timer %u",
+ igi->igi_v1_timer, igi->igi_v2_timer, igi->igi_v3_timer);
+ }
+ printf("\n");
+}
+
+static const char *inm_modes[] = {
+ "undefined",
+ "include",
+ "exclude",
+};
+
+static const char *
+inm_mode(u_int mode)
+{
+
+ if (mode >= MCAST_UNDEFINED && mode <= MCAST_EXCLUDE)
+ return (inm_modes[mode]);
+ return (NULL);
+}
+
+#endif /* INET */
+
+#ifdef WITH_KVM
+
+static int
+ifmcstat_kvm(const char *kernel, const char *core)
+{
+ char buf[_POSIX2_LINE_MAX], ifname[IFNAMSIZ];
+ struct ifnet *ifp, *nifp, ifnet;
+
+ if ((kvmd = kvm_openfiles(kernel, core, NULL, O_RDONLY, buf)) ==
+ NULL) {
+ perror("kvm_openfiles");
+ return (-1);
+ }
+ if (kvm_nlist(kvmd, nl) < 0) {
+ perror("kvm_nlist");
+ return (-1);
+ }
+ if (nl[N_IFNET].n_value == 0) {
+ printf("symbol %s not found\n", nl[N_IFNET].n_name);
+ return (-1);
+ }
+ KREAD(nl[N_IFNET].n_value, &ifp, struct ifnet *);
+ while (ifp) {
+ KREAD(ifp, &ifnet, struct ifnet);
+ nifp = ifnet.if_link.tqe_next;
+ if (ifindex && ifindex != ifnet.if_index)
+ goto next;
+
+ printf("%s:\n", if_indextoname(ifnet.if_index, ifname));
+#ifdef INET
+ if_addrlist(TAILQ_FIRST(&ifnet.if_addrhead));
+#endif
+#ifdef INET6
+ if6_addrlist(TAILQ_FIRST(&ifnet.if_addrhead));
+#endif
+ if (vflag)
+ ll_addrlist(TAILQ_FIRST(&ifnet.if_addrhead));
+ next:
+ ifp = nifp;
+ }
+
+ return (0);
+}
+
+static void
+kread(u_long addr, void *buf, int len)
+{
+
+ if (kvm_read(kvmd, addr, buf, len) != len) {
+ perror("kvm_read");
+ exit(EX_OSERR);
+ }
+}
+
+static void
+ll_addrlist(struct ifaddr *ifap)
+{
+ char addrbuf[NI_MAXHOST];
+ struct ifaddr ifa;
+ struct sockaddr sa;
+ struct sockaddr_dl sdl;
+ struct ifaddr *ifap0;
+ int error;
+
+ if (af && af != AF_LINK)
+ return;
+
+ ifap0 = ifap;
+ while (ifap) {
+ KREAD(ifap, &ifa, struct ifaddr);
+ if (ifa.ifa_addr == NULL)
+ goto nextifap;
+ KREAD(ifa.ifa_addr, &sa, struct sockaddr);
+ if (sa.sa_family != PF_LINK)
+ goto nextifap;
+ KREAD(ifa.ifa_addr, &sdl, struct sockaddr_dl);
+ if (sdl.sdl_alen == 0)
+ goto nextifap;
+ addrbuf[0] = '\0';
+ error = getnameinfo((struct sockaddr *)&sdl, sdl.sdl_len,
+ addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
+ printf("\tlink %s\n", addrbuf);
+ nextifap:
+ ifap = ifa.ifa_link.tqe_next;
+ }
+ if (ifap0) {
+ struct ifnet ifnet;
+ struct ifmultiaddr ifm, *ifmp = 0;
+
+ KREAD(ifap0, &ifa, struct ifaddr);
+ KREAD(ifa.ifa_ifp, &ifnet, struct ifnet);
+ if (TAILQ_FIRST(&ifnet.if_multiaddrs))
+ ifmp = TAILQ_FIRST(&ifnet.if_multiaddrs);
+ while (ifmp) {
+ KREAD(ifmp, &ifm, struct ifmultiaddr);
+ if (ifm.ifma_addr == NULL)
+ goto nextmulti;
+ KREAD(ifm.ifma_addr, &sa, struct sockaddr);
+ if (sa.sa_family != AF_LINK)
+ goto nextmulti;
+ KREAD(ifm.ifma_addr, &sdl, struct sockaddr_dl);
+ addrbuf[0] = '\0';
+ error = getnameinfo((struct sockaddr *)&sdl,
+ sdl.sdl_len, addrbuf, sizeof(addrbuf),
+ NULL, 0, NI_NUMERICHOST);
+ printf("\t\tgroup %s refcnt %d\n",
+ addrbuf, ifm.ifma_refcount);
+ nextmulti:
+ ifmp = TAILQ_NEXT(&ifm, ifma_link);
+ }
+ }
+}
+
+#ifdef INET6
+
+static void
+if6_addrlist(struct ifaddr *ifap)
+{
+ struct ifaddr ifa;
+ struct sockaddr sa;
+ struct in6_ifaddr if6a;
+ struct ifaddr *ifap0;
+
+ if (af && af != AF_INET6)
+ return;
+ ifap0 = ifap;
+ while (ifap) {
+ KREAD(ifap, &ifa, struct ifaddr);
+ if (ifa.ifa_addr == NULL)
+ goto nextifap;
+ KREAD(ifa.ifa_addr, &sa, struct sockaddr);
+ if (sa.sa_family != PF_INET6)
+ goto nextifap;
+ KREAD(ifap, &if6a, struct in6_ifaddr);
+ printf("\tinet6 %s\n", inet6_n2a(&if6a.ia_addr.sin6_addr));
+ nextifap:
+ ifap = ifa.ifa_link.tqe_next;
+ }
+ if (ifap0) {
+ struct ifnet ifnet;
+ struct ifmultiaddr ifm, *ifmp = 0;
+ struct sockaddr_dl sdl;
+
+ KREAD(ifap0, &ifa, struct ifaddr);
+ KREAD(ifa.ifa_ifp, &ifnet, struct ifnet);
+ if (TAILQ_FIRST(&ifnet.if_multiaddrs))
+ ifmp = TAILQ_FIRST(&ifnet.if_multiaddrs);
+ while (ifmp) {
+ KREAD(ifmp, &ifm, struct ifmultiaddr);
+ if (ifm.ifma_addr == NULL)
+ goto nextmulti;
+ KREAD(ifm.ifma_addr, &sa, struct sockaddr);
+ if (sa.sa_family != AF_INET6)
+ goto nextmulti;
+ (void)in6_multientry((struct in6_multi *)
+ ifm.ifma_protospec);
+ if (ifm.ifma_lladdr == 0)
+ goto nextmulti;
+ KREAD(ifm.ifma_lladdr, &sdl, struct sockaddr_dl);
+ printf("\t\t\tmcast-macaddr %s refcnt %d\n",
+ ether_ntoa((struct ether_addr *)LLADDR(&sdl)),
+ ifm.ifma_refcount);
+ nextmulti:
+ ifmp = TAILQ_NEXT(&ifm, ifma_link);
+ }
+ }
+}
+
+static struct in6_multi *
+in6_multientry(struct in6_multi *mc)
+{
+ struct in6_multi multi;
+
+ KREAD(mc, &multi, struct in6_multi);
+ printf("\t\tgroup %s", inet6_n2a(&multi.in6m_addr));
+ printf(" refcnt %u\n", multi.in6m_refcount);
+
+ return (multi.in6m_entry.le_next);
+}
+
+#endif /* INET6 */
+
+#ifdef INET
+
+static void
+if_addrlist(struct ifaddr *ifap)
+{
+ struct ifaddr ifa;
+ struct ifnet ifnet;
+ struct sockaddr sa;
+ struct in_ifaddr ia;
+ struct ifaddr *ifap0;
+
+ if (af && af != AF_INET)
+ return;
+ ifap0 = ifap;
+ while (ifap) {
+ KREAD(ifap, &ifa, struct ifaddr);
+ if (ifa.ifa_addr == NULL)
+ goto nextifap;
+ KREAD(ifa.ifa_addr, &sa, struct sockaddr);
+ if (sa.sa_family != PF_INET)
+ goto nextifap;
+ KREAD(ifap, &ia, struct in_ifaddr);
+ printf("\tinet %s\n", inet_ntoa(ia.ia_addr.sin_addr));
+ /*
+ * Print per-link IGMP information, if available.
+ */
+ if (ifa.ifa_ifp != NULL) {
+ struct in_ifinfo ii;
+ struct igmp_ifinfo igi;
+
+ KREAD(ifa.ifa_ifp, &ifnet, struct ifnet);
+ KREAD(ifnet.if_afdata[AF_INET], &ii, struct in_ifinfo);
+ if (ii.ii_igmp != NULL) {
+ KREAD(ii.ii_igmp, &igi, struct igmp_ifinfo);
+ in_ifinfo(&igi);
+ }
+ }
+ nextifap:
+ ifap = ifa.ifa_link.tqe_next;
+ }
+ if (ifap0) {
+ struct ifmultiaddr ifm, *ifmp = 0;
+ struct sockaddr_dl sdl;
+
+ KREAD(ifap0, &ifa, struct ifaddr);
+ KREAD(ifa.ifa_ifp, &ifnet, struct ifnet);
+ if (TAILQ_FIRST(&ifnet.if_multiaddrs))
+ ifmp = TAILQ_FIRST(&ifnet.if_multiaddrs);
+ while (ifmp) {
+ KREAD(ifmp, &ifm, struct ifmultiaddr);
+ if (ifm.ifma_addr == NULL)
+ goto nextmulti;
+ KREAD(ifm.ifma_addr, &sa, struct sockaddr);
+ if (sa.sa_family != AF_INET)
+ goto nextmulti;
+ (void)in_multientry((struct in_multi *)
+ ifm.ifma_protospec);
+ if (ifm.ifma_lladdr == 0)
+ goto nextmulti;
+ KREAD(ifm.ifma_lladdr, &sdl, struct sockaddr_dl);
+ printf("\t\t\tmcast-macaddr %s refcnt %d\n",
+ ether_ntoa((struct ether_addr *)LLADDR(&sdl)),
+ ifm.ifma_refcount);
+ nextmulti:
+ ifmp = TAILQ_NEXT(&ifm, ifma_link);
+ }
+ }
+}
+
+static const char *inm_states[] = {
+ "not-member",
+ "silent",
+ "idle",
+ "lazy",
+ "sleeping",
+ "awakening",
+ "query-pending",
+ "sg-query-pending",
+ "leaving"
+};
+
+static const char *
+inm_state(u_int state)
+{
+
+ if (state >= IGMP_NOT_MEMBER && state <= IGMP_LEAVING_MEMBER)
+ return (inm_states[state]);
+ return (NULL);
+}
+
+#if 0
+static struct ip_msource *
+ims_min_kvm(struct in_multi *pinm)
+{
+ struct ip_msource ims0;
+ struct ip_msource *tmp, *parent;
+
+ parent = NULL;
+ tmp = RB_ROOT(&pinm->inm_srcs);
+ while (tmp) {
+ parent = tmp;
+ KREAD(tmp, &ims0, struct ip_msource);
+ tmp = RB_LEFT(&ims0, ims_link);
+ }
+ return (parent); /* kva */
+}
+
+/* XXX This routine is buggy. See RB_NEXT in sys/tree.h. */
+static struct ip_msource *
+ims_next_kvm(struct ip_msource *ims)
+{
+ struct ip_msource ims0, ims1;
+ struct ip_msource *tmp;
+
+ KREAD(ims, &ims0, struct ip_msource);
+ if (RB_RIGHT(&ims0, ims_link)) {
+ ims = RB_RIGHT(&ims0, ims_link);
+ KREAD(ims, &ims1, struct ip_msource);
+ while ((tmp = RB_LEFT(&ims1, ims_link))) {
+ KREAD(tmp, &ims0, struct ip_msource);
+ ims = RB_LEFT(&ims0, ims_link);
+ }
+ } else {
+ tmp = RB_PARENT(&ims0, ims_link);
+ if (tmp) {
+ KREAD(tmp, &ims1, struct ip_msource);
+ if (ims == RB_LEFT(&ims1, ims_link))
+ ims = tmp;
+ } else {
+ while ((tmp = RB_PARENT(&ims0, ims_link))) {
+ KREAD(tmp, &ims1, struct ip_msource);
+ if (ims == RB_RIGHT(&ims1, ims_link)) {
+ ims = tmp;
+ KREAD(ims, &ims0, struct ip_msource);
+ } else
+ break;
+ }
+ ims = RB_PARENT(&ims0, ims_link);
+ }
+ }
+ return (ims); /* kva */
+}
+
+static void
+inm_print_sources_kvm(struct in_multi *pinm)
+{
+ struct ip_msource ims0;
+ struct ip_msource *ims;
+ struct in_addr src;
+ int cnt;
+ uint8_t fmode;
+
+ cnt = 0;
+ fmode = pinm->inm_st[1].iss_fmode;
+ if (fmode == MCAST_UNDEFINED)
+ return;
+ for (ims = ims_min_kvm(pinm); ims != NULL; ims = ims_next_kvm(ims)) {
+ if (cnt == 0)
+ printf(" srcs ");
+ KREAD(ims, &ims0, struct ip_msource);
+ /* Only print sources in-mode at t1. */
+ if (fmode != ims_get_mode(pinm, ims, 1))
+ continue;
+ src.s_addr = htonl(ims0.ims_haddr);
+ printf("%s%s", (cnt++ == 0 ? "" : ","), inet_ntoa(src));
+ }
+}
+#endif
+
+static struct in_multi *
+in_multientry(struct in_multi *pinm)
+{
+ struct in_multi inm;
+ const char *state, *mode;
+
+ KREAD(pinm, &inm, struct in_multi);
+ printf("\t\tgroup %s", inet_ntoa(inm.inm_addr));
+ printf(" refcnt %u", inm.inm_refcount);
+
+ state = inm_state(inm.inm_state);
+ if (state)
+ printf(" state %s", state);
+ else
+ printf(" state (%d)", inm.inm_state);
+
+ mode = inm_mode(inm.inm_st[1].iss_fmode);
+ if (mode)
+ printf(" mode %s", mode);
+ else
+ printf(" mode (%d)", inm.inm_st[1].iss_fmode);
+
+ if (vflag >= 2) {
+ printf(" asm %u ex %u in %u rec %u",
+ (u_int)inm.inm_st[1].iss_asm,
+ (u_int)inm.inm_st[1].iss_ex,
+ (u_int)inm.inm_st[1].iss_in,
+ (u_int)inm.inm_st[1].iss_rec);
+ }
+
+#if 0
+ /* Buggy. */
+ if (vflag)
+ inm_print_sources_kvm(&inm);
+#endif
+
+ printf("\n");
+ return (NULL);
+}
+
+#endif /* INET */
+
+#endif /* WITH_KVM */
+
+#ifdef INET6
+static const char *
+inet6_n2a(struct in6_addr *p)
+{
+ static char buf[NI_MAXHOST];
+ struct sockaddr_in6 sin6;
+ u_int32_t scopeid;
+ const int niflags = NI_NUMERICHOST;
+
+ memset(&sin6, 0, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_len = sizeof(struct sockaddr_in6);
+ sin6.sin6_addr = *p;
+ if (IN6_IS_ADDR_LINKLOCAL(p) || IN6_IS_ADDR_MC_LINKLOCAL(p) ||
+ IN6_IS_ADDR_MC_NODELOCAL(p)) {
+ scopeid = ntohs(*(u_int16_t *)&sin6.sin6_addr.s6_addr[2]);
+ if (scopeid) {
+ sin6.sin6_scope_id = scopeid;
+ sin6.sin6_addr.s6_addr[2] = 0;
+ sin6.sin6_addr.s6_addr[3] = 0;
+ }
+ }
+ if (getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len,
+ buf, sizeof(buf), NULL, 0, niflags) == 0) {
+ return (buf);
+ } else {
+ return ("(invalid)");
+ }
+}
+#endif /* INET6 */
+
+#ifdef INET
+/*
+ * Retrieve per-group source filter mode and lists via sysctl.
+ */
+static void
+inm_print_sources_sysctl(uint32_t ifindex, struct in_addr gina)
+{
+#define MAX_SYSCTL_TRY 5
+ int mib[7];
+ int ntry = 0;
+ size_t mibsize;
+ size_t len;
+ size_t needed;
+ size_t cnt;
+ int i;
+ char *buf;
+ struct in_addr *pina;
+ uint32_t *p;
+ uint32_t fmode;
+ const char *modestr;
+
+ mibsize = sizeof(mib) / sizeof(mib[0]);
+ if (sysctlnametomib("net.inet.ip.mcast.filters", mib, &mibsize) == -1) {
+ perror("sysctlnametomib");
+ return;
+ }
+
+ needed = 0;
+ mib[5] = ifindex;
+ mib[6] = gina.s_addr; /* 32 bits wide */
+ mibsize = sizeof(mib) / sizeof(mib[0]);
+ do {
+ if (sysctl(mib, mibsize, NULL, &needed, NULL, 0) == -1) {
+ perror("sysctl net.inet.ip.mcast.filters");
+ return;
+ }
+ if ((buf = malloc(needed)) == NULL) {
+ perror("malloc");
+ return;
+ }
+ if (sysctl(mib, mibsize, buf, &needed, NULL, 0) == -1) {
+ if (errno != ENOMEM || ++ntry >= MAX_SYSCTL_TRY) {
+ perror("sysctl");
+ goto out_free;
+ }
+ free(buf);
+ buf = NULL;
+ }
+ } while (buf == NULL);
+
+ len = needed;
+ if (len < sizeof(uint32_t)) {
+ perror("sysctl");
+ goto out_free;
+ }
+
+ p = (uint32_t *)buf;
+ fmode = *p++;
+ len -= sizeof(uint32_t);
+
+ modestr = inm_mode(fmode);
+ if (modestr)
+ printf(" mode %s", modestr);
+ else
+ printf(" mode (%u)", fmode);
+
+ if (vflag == 0)
+ goto out_free;
+
+ cnt = len / sizeof(struct in_addr);
+ pina = (struct in_addr *)p;
+
+ for (i = 0; i < cnt; i++) {
+ if (i == 0)
+ printf(" srcs ");
+ fprintf(stdout, "%s%s", (i == 0 ? "" : ","),
+ inet_ntoa(*pina++));
+ len -= sizeof(struct in_addr);
+ }
+ if (len > 0) {
+ fprintf(stderr, "warning: %u trailing bytes from %s\n",
+ (unsigned int)len, "net.inet.ip.mcast.filters");
+ }
+
+out_free:
+ free(buf);
+#undef MAX_SYSCTL_TRY
+}
+
+#endif /* INET */
+
+static int
+ifmcstat_getifmaddrs(void)
+{
+ char thisifname[IFNAMSIZ];
+ char addrbuf[NI_MAXHOST];
+ struct ifaddrs *ifap, *ifa;
+ struct ifmaddrs *ifmap, *ifma;
+ sockunion_t lastifasa;
+ sockunion_t *psa, *pgsa, *pllsa, *pifasa;
+ char *pcolon;
+ char *pafname;
+ uint32_t lastifindex, thisifindex;
+ int error;
+
+ error = 0;
+ ifap = NULL;
+ ifmap = NULL;
+ lastifindex = 0;
+ thisifindex = 0;
+ lastifasa.ss.ss_family = AF_UNSPEC;
+
+ if (getifaddrs(&ifap) != 0) {
+ warn("getifmaddrs");
+ return (-1);
+ }
+
+ if (getifmaddrs(&ifmap) != 0) {
+ warn("getifmaddrs");
+ error = -1;
+ goto out;
+ }
+
+ for (ifma = ifmap; ifma; ifma = ifma->ifma_next) {
+ error = 0;
+ if (ifma->ifma_name == NULL || ifma->ifma_addr == NULL)
+ continue;
+
+ psa = (sockunion_t *)ifma->ifma_name;
+ if (psa->sa.sa_family != AF_LINK) {
+ fprintf(stderr,
+ "WARNING: Kernel returned invalid data.\n");
+ error = -1;
+ break;
+ }
+
+ /* Filter on interface name. */
+ thisifindex = psa->sdl.sdl_index;
+ if (ifindex != 0 && thisifindex != ifindex)
+ continue;
+
+ /* Filter on address family. */
+ pgsa = (sockunion_t *)ifma->ifma_addr;
+ if (af != 0 && pgsa->sa.sa_family != af)
+ continue;
+
+ strlcpy(thisifname, link_ntoa(&psa->sdl), IFNAMSIZ);
+ pcolon = strchr(thisifname, ':');
+ if (pcolon)
+ *pcolon = '\0';
+
+ /* Only print the banner for the first ifmaddrs entry. */
+ if (lastifindex == 0 || lastifindex != thisifindex) {
+ lastifindex = thisifindex;
+ fprintf(stdout, "%s:\n", thisifname);
+ }
+
+ /*
+ * Currently, multicast joins only take place on the
+ * primary IPv4 address, and only on the link-local IPv6
+ * address, as per IGMPv2/3 and MLDv1/2 semantics.
+ * Therefore, we only look up the primary address on
+ * the first pass.
+ */
+ pifasa = NULL;
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ if ((strcmp(ifa->ifa_name, thisifname) != 0) ||
+ (ifa->ifa_addr == NULL) ||
+ (ifa->ifa_addr->sa_family != pgsa->sa.sa_family))
+ continue;
+ /*
+ * For AF_INET6 only the link-local address should
+ * be returned. If built without IPv6 support,
+ * skip this address entirely.
+ */
+ pifasa = (sockunion_t *)ifa->ifa_addr;
+ if (pifasa->sa.sa_family == AF_INET6
+#ifdef INET6
+ && !IN6_IS_ADDR_LINKLOCAL(&pifasa->sin6.sin6_addr)
+#endif
+ ) {
+ pifasa = NULL;
+ continue;
+ }
+ break;
+ }
+ if (pifasa == NULL)
+ continue; /* primary address not found */
+
+ if (!vflag && pifasa->sa.sa_family == AF_LINK)
+ continue;
+
+ /* Parse and print primary address, if not already printed. */
+ if (lastifasa.ss.ss_family == AF_UNSPEC ||
+ ((lastifasa.ss.ss_family == AF_LINK &&
+ !sa_dl_equal(&lastifasa.sa, &pifasa->sa)) ||
+ !sa_equal(&lastifasa.sa, &pifasa->sa))) {
+
+ switch (pifasa->sa.sa_family) {
+ case AF_INET:
+ pafname = "inet";
+ break;
+ case AF_INET6:
+ pafname = "inet6";
+ break;
+ case AF_LINK:
+ pafname = "link";
+ break;
+ default:
+ pafname = "unknown";
+ break;
+ }
+
+ switch (pifasa->sa.sa_family) {
+ case AF_INET6:
+#ifdef INET6
+ {
+ const char *p =
+ inet6_n2a(&pifasa->sin6.sin6_addr);
+ strlcpy(addrbuf, p, sizeof(addrbuf));
+ break;
+ }
+#else
+ /* FALLTHROUGH */
+#endif
+ case AF_INET:
+ case AF_LINK:
+ error = getnameinfo(&pifasa->sa,
+ pifasa->sa.sa_len,
+ addrbuf, sizeof(addrbuf), NULL, 0,
+ NI_NUMERICHOST);
+ if (error)
+ perror("getnameinfo");
+ break;
+ default:
+ addrbuf[0] = '\0';
+ break;
+ }
+
+ fprintf(stdout, "\t%s %s\n", pafname, addrbuf);
+#ifdef INET
+ /*
+ * Print per-link IGMP information, if available.
+ */
+ if (pifasa->sa.sa_family == AF_INET) {
+ struct igmp_ifinfo igi;
+ size_t mibsize, len;
+ int mib[5];
+
+ mibsize = sizeof(mib) / sizeof(mib[0]);
+ if (sysctlnametomib("net.inet.igmp.ifinfo",
+ mib, &mibsize) == -1) {
+ perror("sysctlnametomib");
+ goto next_ifnet;
+ }
+ mib[mibsize] = thisifindex;
+ len = sizeof(struct igmp_ifinfo);
+ if (sysctl(mib, mibsize + 1, &igi, &len, NULL,
+ 0) == -1) {
+ perror("sysctl net.inet.igmp.ifinfo");
+ goto next_ifnet;
+ }
+ in_ifinfo(&igi);
+ }
+next_ifnet:
+#endif
+ lastifasa = *pifasa;
+ }
+
+ /* Print this group address. */
+#ifdef INET6
+ if (pgsa->sa.sa_family == AF_INET6) {
+ const char *p = inet6_n2a(&pgsa->sin6.sin6_addr);
+ strlcpy(addrbuf, p, sizeof(addrbuf));
+ } else
+#endif
+ {
+ error = getnameinfo(&pgsa->sa, pgsa->sa.sa_len,
+ addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
+ if (error)
+ perror("getnameinfo");
+ }
+
+ fprintf(stdout, "\t\tgroup %s", addrbuf);
+#ifdef INET
+ if (pgsa->sa.sa_family == AF_INET) {
+ inm_print_sources_sysctl(thisifindex,
+ pgsa->sin.sin_addr);
+ }
+#endif
+ fprintf(stdout, "\n");
+
+ /* Link-layer mapping, if present. */
+ pllsa = (sockunion_t *)ifma->ifma_lladdr;
+ if (pllsa != NULL) {
+ error = getnameinfo(&pllsa->sa, pllsa->sa.sa_len,
+ addrbuf, sizeof(addrbuf), NULL, 0, NI_NUMERICHOST);
+ fprintf(stdout, "\t\t\tmcast-macaddr %s\n", addrbuf);
+ }
+ }
+out:
+ if (ifmap != NULL)
+ freeifmaddrs(ifmap);
+ if (ifap != NULL)
+ freeifaddrs(ifap);
+
+ return (error);
+}
diff --git a/usr.sbin/ifmcstat/printb.c b/usr.sbin/ifmcstat/printb.c
new file mode 100644
index 0000000..a60d4f0
--- /dev/null
+++ b/usr.sbin/ifmcstat/printb.c
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdio.h>
+
+/*
+ * Print a value a la the %b format of the kernel's printf
+ */
+void
+printb(const char *s, unsigned int 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('>');
+ }
+}
diff --git a/usr.sbin/inetd/Makefile b/usr.sbin/inetd/Makefile
new file mode 100644
index 0000000..35f2c22
--- /dev/null
+++ b/usr.sbin/inetd/Makefile
@@ -0,0 +1,29 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+PROG= inetd
+MAN= inetd.8
+MLINKS= inetd.8 inetd.conf.5
+SRCS= inetd.c builtins.c
+
+WARNS?= 2
+CFLAGS+= -DLOGIN_CAP
+#CFLAGS+= -DSANITY_CHECK
+
+.if ${MK_INET6_SUPPORT} != "no"
+CFLAGS+= -DINET6
+.endif
+
+DPADD= ${LIBUTIL} ${LIBWRAP}
+LDADD= -lutil -lwrap
+
+# XXX for src/release/picobsd
+.if !defined(RELEASE_CRUNCH)
+CFLAGS+= -DIPSEC
+DPADD+= ${LIBIPSEC}
+LDADD+= -lipsec
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/inetd/builtins.c b/usr.sbin/inetd/builtins.c
new file mode 100644
index 0000000..ad7a82e
--- /dev/null
+++ b/usr.sbin/inetd/builtins.c
@@ -0,0 +1,820 @@
+/*-
+ * Copyright (c) 1983, 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.
+ *
+ * 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/filio.h>
+#include <sys/ioccom.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/ucred.h>
+#include <sys/uio.h>
+#include <sys/utsname.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "inetd.h"
+
+void chargen_dg(int, struct servtab *);
+void chargen_stream(int, struct servtab *);
+void daytime_dg(int, struct servtab *);
+void daytime_stream(int, struct servtab *);
+void discard_dg(int, struct servtab *);
+void discard_stream(int, struct servtab *);
+void echo_dg(int, struct servtab *);
+void echo_stream(int, struct servtab *);
+static int getline(int, char *, int);
+void iderror(int, int, int, const char *);
+void ident_stream(int, struct servtab *);
+void initring(void);
+uint32_t machtime(void);
+void machtime_dg(int, struct servtab *);
+void machtime_stream(int, struct servtab *);
+
+char ring[128];
+char *endring;
+
+
+struct biltin biltins[] = {
+ /* Echo received data */
+ { "echo", SOCK_STREAM, 1, -1, echo_stream },
+ { "echo", SOCK_DGRAM, 0, 1, echo_dg },
+
+ /* Internet /dev/null */
+ { "discard", SOCK_STREAM, 1, -1, discard_stream },
+ { "discard", SOCK_DGRAM, 0, 1, discard_dg },
+
+ /* Return 32 bit time since 1900 */
+ { "time", SOCK_STREAM, 0, -1, machtime_stream },
+ { "time", SOCK_DGRAM, 0, 1, machtime_dg },
+
+ /* Return human-readable time */
+ { "daytime", SOCK_STREAM, 0, -1, daytime_stream },
+ { "daytime", SOCK_DGRAM, 0, 1, daytime_dg },
+
+ /* Familiar character generator */
+ { "chargen", SOCK_STREAM, 1, -1, chargen_stream },
+ { "chargen", SOCK_DGRAM, 0, 1, chargen_dg },
+
+ { "tcpmux", SOCK_STREAM, 1, -1, (bi_fn_t *)tcpmux },
+
+ { "auth", SOCK_STREAM, 1, -1, ident_stream },
+
+ { NULL, 0, 0, 0, NULL }
+};
+
+/*
+ * RFC864 Character Generator Protocol. Generates character data without
+ * any regard for input.
+ */
+
+void
+initring(void)
+{
+ int i;
+
+ endring = ring;
+
+ for (i = 0; i <= 128; ++i)
+ if (isprint(i))
+ *endring++ = i;
+}
+
+/* Character generator
+ * The RFC says that we should send back a random number of
+ * characters chosen from the range 0 to 512. We send LINESIZ+2.
+ */
+/* ARGSUSED */
+void
+chargen_dg(int s, struct servtab *sep)
+{
+ struct sockaddr_storage ss;
+ static char *rs;
+ int len;
+ socklen_t size;
+ char text[LINESIZ+2];
+
+ if (endring == 0) {
+ initring();
+ rs = ring;
+ }
+
+ size = sizeof(ss);
+ if (recvfrom(s, text, sizeof(text), 0,
+ (struct sockaddr *)&ss, &size) < 0)
+ return;
+
+ if (check_loop((struct sockaddr *)&ss, sep))
+ return;
+
+ if ((len = endring - rs) >= LINESIZ)
+ memmove(text, rs, LINESIZ);
+ else {
+ memmove(text, rs, len);
+ memmove(text + len, ring, LINESIZ - len);
+ }
+ if (++rs == endring)
+ rs = ring;
+ text[LINESIZ] = '\r';
+ text[LINESIZ + 1] = '\n';
+ (void) sendto(s, text, sizeof(text), 0, (struct sockaddr *)&ss, size);
+}
+
+/* Character generator */
+/* ARGSUSED */
+void
+chargen_stream(int s, struct servtab *sep)
+{
+ int len;
+ char *rs, text[LINESIZ+2];
+
+ inetd_setproctitle(sep->se_service, s);
+
+ if (!endring) {
+ initring();
+ rs = ring;
+ }
+
+ text[LINESIZ] = '\r';
+ text[LINESIZ + 1] = '\n';
+ for (rs = ring;;) {
+ if ((len = endring - rs) >= LINESIZ)
+ memmove(text, rs, LINESIZ);
+ else {
+ memmove(text, rs, len);
+ memmove(text + len, ring, LINESIZ - len);
+ }
+ if (++rs == endring)
+ rs = ring;
+ if (write(s, text, sizeof(text)) != sizeof(text))
+ break;
+ }
+ exit(0);
+}
+
+/*
+ * RFC867 Daytime Protocol. Sends the current date and time as an ascii
+ * character string without any regard for input.
+ */
+
+/* Return human-readable time of day */
+/* ARGSUSED */
+void
+daytime_dg(int s, struct servtab *sep)
+{
+ char buffer[256];
+ time_t now;
+ struct sockaddr_storage ss;
+ socklen_t size;
+
+ now = time((time_t *) 0);
+
+ size = sizeof(ss);
+ if (recvfrom(s, buffer, sizeof(buffer), 0,
+ (struct sockaddr *)&ss, &size) < 0)
+ return;
+
+ if (check_loop((struct sockaddr *)&ss, sep))
+ return;
+
+ (void) sprintf(buffer, "%.24s\r\n", ctime(&now));
+ (void) sendto(s, buffer, strlen(buffer), 0,
+ (struct sockaddr *)&ss, size);
+}
+
+/* Return human-readable time of day */
+/* ARGSUSED */
+void
+daytime_stream(int s, struct servtab *sep __unused)
+{
+ char buffer[256];
+ time_t now;
+
+ now = time((time_t *) 0);
+
+ (void) sprintf(buffer, "%.24s\r\n", ctime(&now));
+ (void) send(s, buffer, strlen(buffer), MSG_EOF);
+}
+
+/*
+ * RFC863 Discard Protocol. Any data received is thrown away and no response
+ * is sent.
+ */
+
+/* Discard service -- ignore data */
+/* ARGSUSED */
+void
+discard_dg(int s, struct servtab *sep __unused)
+{
+ char buffer[BUFSIZE];
+
+ (void) read(s, buffer, sizeof(buffer));
+}
+
+/* Discard service -- ignore data */
+/* ARGSUSED */
+void
+discard_stream(int s, struct servtab *sep)
+{
+ int ret;
+ char buffer[BUFSIZE];
+
+ inetd_setproctitle(sep->se_service, s);
+ while (1) {
+ while ((ret = read(s, buffer, sizeof(buffer))) > 0)
+ ;
+ if (ret == 0 || errno != EINTR)
+ break;
+ }
+ exit(0);
+}
+
+/*
+ * RFC862 Echo Protocol. Any data received is sent back to the sender as
+ * received.
+ */
+
+/* Echo service -- echo data back */
+/* ARGSUSED */
+void
+echo_dg(int s, struct servtab *sep)
+{
+ char buffer[65536]; /* Should be sizeof(max datagram). */
+ int i;
+ socklen_t size;
+ struct sockaddr_storage ss;
+
+ size = sizeof(ss);
+ if ((i = recvfrom(s, buffer, sizeof(buffer), 0,
+ (struct sockaddr *)&ss, &size)) < 0)
+ return;
+
+ if (check_loop((struct sockaddr *)&ss, sep))
+ return;
+
+ (void) sendto(s, buffer, i, 0, (struct sockaddr *)&ss, size);
+}
+
+/* Echo service -- echo data back */
+/* ARGSUSED */
+void
+echo_stream(int s, struct servtab *sep)
+{
+ char buffer[BUFSIZE];
+ int i;
+
+ inetd_setproctitle(sep->se_service, s);
+ while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
+ write(s, buffer, i) > 0)
+ ;
+ exit(0);
+}
+
+/*
+ * RFC1413 Identification Protocol. Given a TCP port number pair, return a
+ * character string which identifies the owner of that connection on the
+ * server's system. Extended to allow for ~/.fakeid support and ~/.noident
+ * support.
+ */
+
+/* RFC 1413 says the following are the only errors you can return. */
+#define ID_INVALID "INVALID-PORT" /* Port number improperly specified. */
+#define ID_NOUSER "NO-USER" /* Port not in use/not identifable. */
+#define ID_HIDDEN "HIDDEN-USER" /* Hiden at user's request. */
+#define ID_UNKNOWN "UNKNOWN-ERROR" /* Everything else. */
+
+/* Generic ident_stream error-sending func */
+/* ARGSUSED */
+void
+iderror(int lport, int fport, int s, const char *er)
+{
+ char *p;
+
+ asprintf(&p, "%d , %d : ERROR : %s\r\n", lport, fport, er);
+ if (p == NULL) {
+ syslog(LOG_ERR, "asprintf: %m");
+ exit(EX_OSERR);
+ }
+ send(s, p, strlen(p), MSG_EOF);
+ free(p);
+
+ exit(0);
+}
+
+/* Ident service (AKA "auth") */
+/* ARGSUSED */
+void
+ident_stream(int s, struct servtab *sep)
+{
+ struct utsname un;
+ struct stat sb;
+ struct sockaddr_in sin4[2];
+#ifdef INET6
+ struct sockaddr_in6 sin6[2];
+#endif
+ struct sockaddr_storage ss[2];
+ struct xucred uc;
+ struct timeval tv = {
+ 10,
+ 0
+ }, to;
+ struct passwd *pw = NULL;
+ fd_set fdset;
+ char buf[BUFSIZE], *p, **av, *osname = NULL, e;
+ char idbuf[MAXLOGNAME] = ""; /* Big enough to hold uid in decimal. */
+ socklen_t socklen;
+ ssize_t ssize;
+ size_t size, bufsiz;
+ int c, fflag = 0, nflag = 0, rflag = 0, argc = 0;
+ int gflag = 0, iflag = 0, Fflag = 0, getcredfail = 0, onreadlen;
+ u_short lport, fport;
+
+ inetd_setproctitle(sep->se_service, s);
+ /*
+ * Reset getopt() since we are a fork() but not an exec() from
+ * a parent which used getopt() already.
+ */
+ optind = 1;
+ optreset = 1;
+ /*
+ * Take the internal argument vector and count it out to make an
+ * argument count for getopt. This can be used for any internal
+ * service to read arguments and use getopt() easily.
+ */
+ for (av = sep->se_argv; *av; av++)
+ argc++;
+ if (argc) {
+ int sec, usec;
+ size_t i;
+ u_int32_t rnd32;
+
+ while ((c = getopt(argc, sep->se_argv, "d:fFgino:rt:")) != -1)
+ switch (c) {
+ case 'd':
+ if (!gflag)
+ strlcpy(idbuf, optarg, sizeof(idbuf));
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 'F':
+ fflag = 1;
+ Fflag=1;
+ break;
+ case 'g':
+ gflag = 1;
+ rnd32 = 0; /* Shush, compiler. */
+ /*
+ * The number of bits in "rnd32" divided
+ * by the number of bits needed per iteration
+ * gives a more optimal way to reload the
+ * random number only when necessary.
+ *
+ * 32 bits from arc4random corresponds to
+ * about 6 base-36 digits, so we reseed evey 6.
+ */
+ for (i = 0; i < sizeof(idbuf) - 1; i++) {
+ static const char *const base36 =
+ "0123456789"
+ "abcdefghijklmnopqrstuvwxyz";
+ if (i % 6 == 0)
+ rnd32 = arc4random();
+ idbuf[i] = base36[rnd32 % 36];
+ rnd32 /= 36;
+ }
+ idbuf[i] = '\0';
+ break;
+ case 'i':
+ iflag = 1;
+ break;
+ case 'n':
+ nflag = 1;
+ break;
+ case 'o':
+ osname = optarg;
+ break;
+ case 'r':
+ rflag = 1;
+ break;
+ case 't':
+ switch (sscanf(optarg, "%d.%d", &sec, &usec)) {
+ case 2:
+ tv.tv_usec = usec;
+ /* FALLTHROUGH */
+ case 1:
+ tv.tv_sec = sec;
+ break;
+ default:
+ if (debug)
+ warnx("bad -t argument");
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ if (osname == NULL) {
+ if (uname(&un) == -1)
+ iderror(0, 0, s, ID_UNKNOWN);
+ osname = un.sysname;
+ }
+
+ /*
+ * We're going to prepare for and execute reception of a
+ * packet of data from the user. The data is in the format
+ * "local_port , foreign_port\r\n" (with local being the
+ * server's port and foreign being the client's.)
+ */
+ gettimeofday(&to, NULL);
+ to.tv_sec += tv.tv_sec;
+ to.tv_usec += tv.tv_usec;
+ if (to.tv_usec >= 1000000) {
+ to.tv_usec -= 1000000;
+ to.tv_sec++;
+ }
+
+ size = 0;
+ bufsiz = sizeof(buf) - 1;
+ FD_ZERO(&fdset);
+ while (bufsiz > 0) {
+ gettimeofday(&tv, NULL);
+ tv.tv_sec = to.tv_sec - tv.tv_sec;
+ tv.tv_usec = to.tv_usec - tv.tv_usec;
+ if (tv.tv_usec < 0) {
+ tv.tv_usec += 1000000;
+ tv.tv_sec--;
+ }
+ if (tv.tv_sec < 0)
+ break;
+ FD_SET(s, &fdset);
+ if (select(s + 1, &fdset, NULL, NULL, &tv) == -1)
+ iderror(0, 0, s, ID_UNKNOWN);
+ if (ioctl(s, FIONREAD, &onreadlen) == -1)
+ iderror(0, 0, s, ID_UNKNOWN);
+ if ((size_t)onreadlen > bufsiz)
+ onreadlen = bufsiz;
+ ssize = read(s, &buf[size], (size_t)onreadlen);
+ if (ssize == -1)
+ iderror(0, 0, s, ID_UNKNOWN);
+ else if (ssize == 0)
+ break;
+ bufsiz -= ssize;
+ size += ssize;
+ if (memchr(&buf[size - ssize], '\n', ssize) != NULL)
+ break;
+ }
+ buf[size] = '\0';
+ /* Read two characters, and check for a delimiting character */
+ if (sscanf(buf, "%hu , %hu%c", &lport, &fport, &e) != 3 || isdigit(e))
+ iderror(0, 0, s, ID_INVALID);
+
+ /* Send garbage? */
+ if (gflag)
+ goto printit;
+
+ /*
+ * If not "real" (-r), send a HIDDEN-USER error for everything.
+ * If -d is used to set a fallback username, this is used to
+ * override it, and the fallback is returned instead.
+ */
+ if (!rflag) {
+ if (*idbuf == '\0')
+ iderror(lport, fport, s, ID_HIDDEN);
+ goto printit;
+ }
+
+ /*
+ * We take the input and construct an array of two sockaddr_ins
+ * which contain the local address information and foreign
+ * address information, respectively, used to look up the
+ * credentials for the socket (which are returned by the
+ * sysctl "net.inet.tcp.getcred" when we call it.)
+ */
+ socklen = sizeof(ss[0]);
+ if (getsockname(s, (struct sockaddr *)&ss[0], &socklen) == -1)
+ iderror(lport, fport, s, ID_UNKNOWN);
+ socklen = sizeof(ss[1]);
+ if (getpeername(s, (struct sockaddr *)&ss[1], &socklen) == -1)
+ iderror(lport, fport, s, ID_UNKNOWN);
+ if (ss[0].ss_family != ss[1].ss_family)
+ iderror(lport, fport, s, ID_UNKNOWN);
+ size = sizeof(uc);
+ switch (ss[0].ss_family) {
+ case AF_INET:
+ sin4[0] = *(struct sockaddr_in *)&ss[0];
+ sin4[0].sin_port = htons(lport);
+ sin4[1] = *(struct sockaddr_in *)&ss[1];
+ sin4[1].sin_port = htons(fport);
+ if (sysctlbyname("net.inet.tcp.getcred", &uc, &size, sin4,
+ sizeof(sin4)) == -1)
+ getcredfail = errno;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ sin6[0] = *(struct sockaddr_in6 *)&ss[0];
+ sin6[0].sin6_port = htons(lport);
+ sin6[1] = *(struct sockaddr_in6 *)&ss[1];
+ sin6[1].sin6_port = htons(fport);
+ if (sysctlbyname("net.inet6.tcp6.getcred", &uc, &size, sin6,
+ sizeof(sin6)) == -1)
+ getcredfail = errno;
+ break;
+#endif
+ default: /* should not reach here */
+ getcredfail = EAFNOSUPPORT;
+ break;
+ }
+ if (getcredfail != 0 || uc.cr_version != XUCRED_VERSION) {
+ if (*idbuf == '\0')
+ iderror(lport, fport, s,
+ getcredfail == ENOENT ? ID_NOUSER : ID_UNKNOWN);
+ goto printit;
+ }
+
+ /* Look up the pw to get the username and home directory*/
+ errno = 0;
+ pw = getpwuid(uc.cr_uid);
+ if (pw == NULL)
+ iderror(lport, fport, s, errno == 0 ? ID_NOUSER : ID_UNKNOWN);
+
+ if (iflag)
+ snprintf(idbuf, sizeof(idbuf), "%u", (unsigned)pw->pw_uid);
+ else
+ strlcpy(idbuf, pw->pw_name, sizeof(idbuf));
+
+ /*
+ * If enabled, we check for a file named ".noident" in the user's
+ * home directory. If found, we return HIDDEN-USER.
+ */
+ if (nflag) {
+ if (asprintf(&p, "%s/.noident", pw->pw_dir) == -1)
+ iderror(lport, fport, s, ID_UNKNOWN);
+ if (lstat(p, &sb) == 0) {
+ free(p);
+ iderror(lport, fport, s, ID_HIDDEN);
+ }
+ free(p);
+ }
+
+ /*
+ * Here, if enabled, we read a user's ".fakeid" file in their
+ * home directory. It consists of a line containing the name
+ * they want.
+ */
+ if (fflag) {
+ int fakeid_fd;
+
+ /*
+ * Here we set ourself to effectively be the user, so we don't
+ * open any files we have no permission to open, especially
+ * symbolic links to sensitive root-owned files or devices.
+ */
+ if (initgroups(pw->pw_name, pw->pw_gid) == -1)
+ iderror(lport, fport, s, ID_UNKNOWN);
+ if (seteuid(pw->pw_uid) == -1)
+ iderror(lport, fport, s, ID_UNKNOWN);
+ /*
+ * We can't stat() here since that would be a race
+ * condition.
+ * Therefore, we open the file we have permissions to open
+ * and if it's not a regular file, we close it and end up
+ * returning the user's real username.
+ */
+ if (asprintf(&p, "%s/.fakeid", pw->pw_dir) == -1)
+ iderror(lport, fport, s, ID_UNKNOWN);
+ fakeid_fd = open(p, O_RDONLY | O_NONBLOCK);
+ free(p);
+ if (fakeid_fd == -1 || fstat(fakeid_fd, &sb) == -1 ||
+ !S_ISREG(sb.st_mode))
+ goto fakeid_fail;
+
+ if ((ssize = read(fakeid_fd, buf, sizeof(buf) - 1)) < 0)
+ goto fakeid_fail;
+ buf[ssize] = '\0';
+
+ /*
+ * Usually, the file will have the desired identity
+ * in the form "identity\n". Allow for leading white
+ * space and trailing white space/end of line.
+ */
+ p = buf;
+ p += strspn(p, " \t");
+ p[strcspn(p, " \t\r\n")] = '\0';
+ if (strlen(p) > MAXLOGNAME - 1) /* Too long (including nul)? */
+ p[MAXLOGNAME - 1] = '\0';
+
+ /*
+ * If the name is a zero-length string or matches it
+ * the id or name of another user (unless permitted by -F)
+ * then it is invalid.
+ */
+ if (*p == '\0')
+ goto fakeid_fail;
+ if (!Fflag) {
+ if (iflag) {
+ if (p[strspn(p, "0123456789")] == '\0' &&
+ getpwuid(atoi(p)) != NULL)
+ goto fakeid_fail;
+ } else {
+ if (getpwnam(p) != NULL)
+ goto fakeid_fail;
+ }
+ }
+
+ strlcpy(idbuf, p, sizeof(idbuf));
+
+fakeid_fail:
+ if (fakeid_fd != -1)
+ close(fakeid_fd);
+ }
+
+printit:
+ /* Finally, we make and send the reply. */
+ if (asprintf(&p, "%d , %d : USERID : %s : %s\r\n", lport, fport, osname,
+ idbuf) == -1) {
+ syslog(LOG_ERR, "asprintf: %m");
+ exit(EX_OSERR);
+ }
+ send(s, p, strlen(p), MSG_EOF);
+ free(p);
+
+ exit(0);
+}
+
+/*
+ * RFC738/868 Time Server.
+ * Return a machine readable date and time, in the form of the
+ * number of seconds since midnight, Jan 1, 1900. Since gettimeofday
+ * returns the number of seconds since midnight, Jan 1, 1970,
+ * we must add 2208988800 seconds to this figure to make up for
+ * some seventy years Bell Labs was asleep.
+ */
+
+uint32_t
+machtime(void)
+{
+ struct timeval tv;
+
+ if (gettimeofday(&tv, (struct timezone *)NULL) < 0) {
+ if (debug)
+ warnx("unable to get time of day");
+ return (0L);
+ }
+#define OFFSET ((uint32_t)25567 * 24*60*60)
+ return (htonl((uint32_t)(tv.tv_sec + OFFSET)));
+#undef OFFSET
+}
+
+/* ARGSUSED */
+void
+machtime_dg(int s, struct servtab *sep)
+{
+ uint32_t result;
+ struct sockaddr_storage ss;
+ socklen_t size;
+
+ size = sizeof(ss);
+ if (recvfrom(s, (char *)&result, sizeof(result), 0,
+ (struct sockaddr *)&ss, &size) < 0)
+ return;
+
+ if (check_loop((struct sockaddr *)&ss, sep))
+ return;
+
+ result = machtime();
+ (void) sendto(s, (char *) &result, sizeof(result), 0,
+ (struct sockaddr *)&ss, size);
+}
+
+/* ARGSUSED */
+void
+machtime_stream(int s, struct servtab *sep __unused)
+{
+ uint32_t result;
+
+ result = machtime();
+ (void) send(s, (char *) &result, sizeof(result), MSG_EOF);
+}
+
+/*
+ * RFC1078 TCP Port Service Multiplexer (TCPMUX). Service connections to
+ * services based on the service name sent.
+ *
+ * Based on TCPMUX.C by Mark K. Lottor November 1988
+ * sri-nic::ps:<mkl>tcpmux.c
+ */
+
+#define MAX_SERV_LEN (256+2) /* 2 bytes for \r\n */
+#define strwrite(fd, buf) (void) write(fd, buf, sizeof(buf)-1)
+
+static int /* # of characters upto \r,\n or \0 */
+getline(int fd, char *buf, int len)
+{
+ int count = 0, n;
+ struct sigaction sa;
+
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sa.sa_handler = SIG_DFL;
+ sigaction(SIGALRM, &sa, (struct sigaction *)0);
+ do {
+ alarm(10);
+ n = read(fd, buf, len-count);
+ alarm(0);
+ if (n == 0)
+ return (count);
+ if (n < 0)
+ return (-1);
+ while (--n >= 0) {
+ if (*buf == '\r' || *buf == '\n' || *buf == '\0')
+ return (count);
+ count++;
+ buf++;
+ }
+ } while (count < len);
+ return (count);
+}
+
+struct servtab *
+tcpmux(int s)
+{
+ struct servtab *sep;
+ char service[MAX_SERV_LEN+1];
+ int len;
+
+ /* Get requested service name */
+ if ((len = getline(s, service, MAX_SERV_LEN)) < 0) {
+ strwrite(s, "-Error reading service name\r\n");
+ return (NULL);
+ }
+ service[len] = '\0';
+
+ if (debug)
+ warnx("tcpmux: someone wants %s", service);
+
+ /*
+ * Help is a required command, and lists available services,
+ * one per line.
+ */
+ if (!strcasecmp(service, "help")) {
+ for (sep = servtab; sep; sep = sep->se_next) {
+ if (!ISMUX(sep))
+ continue;
+ (void)write(s,sep->se_service,strlen(sep->se_service));
+ strwrite(s, "\r\n");
+ }
+ return (NULL);
+ }
+
+ /* Try matching a service in inetd.conf with the request */
+ for (sep = servtab; sep; sep = sep->se_next) {
+ if (!ISMUX(sep))
+ continue;
+ if (!strcasecmp(service, sep->se_service)) {
+ if (ISMUXPLUS(sep)) {
+ strwrite(s, "+Go\r\n");
+ }
+ return (sep);
+ }
+ }
+ strwrite(s, "-Service not available\r\n");
+ return (NULL);
+}
diff --git a/usr.sbin/inetd/inetd.8 b/usr.sbin/inetd/inetd.8
new file mode 100644
index 0000000..80cfbd2
--- /dev/null
+++ b/usr.sbin/inetd/inetd.8
@@ -0,0 +1,955 @@
+.\" Copyright (c) 1985, 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.
+.\"
+.\" from: @(#)inetd.8 8.3 (Berkeley) 4/13/94
+.\" $FreeBSD$
+.\"
+.Dd January 12, 2008
+.Dt INETD 8
+.Os
+.Sh NAME
+.Nm inetd
+.Nd internet
+.Dq super-server
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl l
+.Op Fl w
+.Op Fl W
+.Op Fl c Ar maximum
+.Op Fl C Ar rate
+.Op Fl a Ar address | hostname
+.Op Fl p Ar filename
+.Op Fl R Ar rate
+.Op Fl s Ar maximum
+.Op Ar configuration file
+.Sh DESCRIPTION
+The
+.Nm
+utility should be run at boot time by
+.Pa /etc/rc
+(see
+.Xr rc 8 ) .
+It then listens for connections on certain
+internet sockets.
+When a connection is found on one
+of its sockets, it decides what service the socket
+corresponds to, and invokes a program to service the request.
+The server program is invoked with the service socket
+as its standard input, output and error descriptors.
+After the program is
+finished,
+.Nm
+continues to listen on the socket (except in some cases which
+will be described below).
+Essentially,
+.Nm
+allows running one daemon to invoke several others,
+reducing load on the system.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d
+Turn on debugging.
+.It Fl l
+Turn on logging of successful connections.
+.It Fl w
+Turn on TCP Wrapping for external services.
+See the
+.Sx "IMPLEMENTATION NOTES"
+section for more information on TCP Wrappers support.
+.It Fl W
+Turn on TCP Wrapping for internal services which are built in to
+.Nm .
+.It Fl c Ar maximum
+Specify the default maximum number of
+simultaneous invocations of each service;
+the default is unlimited.
+May be overridden on a per-service basis with the "max-child"
+parameter.
+.It Fl C Ar rate
+Specify the default maximum number of times a service can be invoked
+from a single IP address in one minute; the default is unlimited.
+May be overridden on a per-service basis with the
+"max-connections-per-ip-per-minute" parameter.
+.It Fl R Ar rate
+Specify the maximum number of times a service can be invoked
+in one minute; the default is 256.
+A rate of 0 allows an unlimited number of invocations.
+.It Fl s Ar maximum
+Specify the default maximum number of
+simultaneous invocations of each service from a single IP address;
+the default is unlimited.
+May be overridden on a per-service basis with the "max-child-per-ip"
+parameter.
+.It Fl a
+Specify one specific IP address to bind to.
+Alternatively, a hostname can be specified,
+in which case the IPv4 or IPv6 address
+which corresponds to that hostname is used.
+Usually a hostname is specified when
+.Nm
+is run inside a
+.Xr jail 8 ,
+in which case the hostname corresponds to that of the
+.Xr jail 8
+environment.
+.Pp
+When the hostname specification is used
+and both IPv4 and IPv6 bindings are desired,
+one entry with the appropriate
+.Em protocol
+type for each binding
+is required for each service in
+.Pa /etc/inetd.conf .
+For example,
+a TCP-based service would need two entries,
+one using
+.Dq tcp4
+for the
+.Em protocol
+and the other using
+.Dq tcp6 .
+See the explanation of the
+.Pa /etc/inetd.conf
+.Em protocol
+field below.
+.It Fl p
+Specify an alternate file in which to store the process ID.
+.El
+.Pp
+Upon execution,
+.Nm
+reads its configuration information from a configuration
+file which, by default, is
+.Pa /etc/inetd.conf .
+There must be an entry for each field of the configuration
+file, with entries for each field separated by a tab or
+a space.
+Comments are denoted by a
+.Dq #
+at the beginning
+of a line.
+There must be an entry for each field.
+The
+fields of the configuration file are as follows:
+.Pp
+.Bd -unfilled -offset indent -compact
+service-name
+socket-type
+protocol
+{wait|nowait}[/max-child[/max-connections-per-ip-per-minute[/max-child-per-ip]]]
+user[:group][/login-class]
+server-program
+server-program-arguments
+.Ed
+.Pp
+To specify an
+.Tn "ONC RPC" Ns -based
+service, the entry would contain these fields:
+.Pp
+.Bd -unfilled -offset indent -compact
+service-name/version
+socket-type
+rpc/protocol
+{wait|nowait}[/max-child[/max-connections-per-ip-per-minute[/max-child-per-ip]]]
+user[:group][/login-class]
+server-program
+server-program-arguments
+.Ed
+.Pp
+There are two types of services that
+.Nm
+can start: standard and TCPMUX.
+A standard service has a well-known port assigned to it;
+it may be a service that implements an official Internet standard or is a
+.Bx Ns -specific
+service.
+As described in
+.Tn RFC 1078 ,
+TCPMUX services are nonstandard services that do not have a
+well-known port assigned to them.
+They are invoked from
+.Nm
+when a program connects to the
+.Dq tcpmux
+well-known port and specifies
+the service name.
+This feature is useful for adding locally-developed servers.
+TCPMUX requests are only accepted when the multiplexor service itself
+is enabled, above and beyond and specific TCPMUX-based servers; see the
+discussion of internal services below.
+.Pp
+The
+.Em service-name
+entry is the name of a valid service in
+the file
+.Pa /etc/services ,
+or the specification of a
+.Ux
+domain socket (see below).
+For
+.Dq internal
+services (discussed below), the service
+name
+should
+be the official name of the service (that is, the first entry in
+.Pa /etc/services ) .
+When used to specify an
+.Tn "ONC RPC" Ns -based
+service, this field is a valid RPC service name listed in
+the file
+.Pa /etc/rpc .
+The part on the right of the
+.Dq /
+is the RPC version number.
+This
+can simply be a single numeric argument or a range of versions.
+A range is bounded by the low version to the high version -
+.Dq rusers/1-3 .
+For TCPMUX services, the value of the
+.Em service-name
+field consists of the string
+.Dq tcpmux
+followed by a slash and the
+locally-chosen service name.
+The service names listed in
+.Pa /etc/services
+and the name
+.Dq help
+are reserved.
+Try to choose unique names for your TCPMUX services by prefixing them with
+your organization's name and suffixing them with a version number.
+.Pp
+The
+.Em socket-type
+should be one of
+.Dq stream ,
+.Dq dgram ,
+.Dq raw ,
+.Dq rdm ,
+or
+.Dq seqpacket ,
+depending on whether the socket is a stream, datagram, raw,
+reliably delivered message, or sequenced packet socket.
+TCPMUX services must use
+.Dq stream .
+.Pp
+The
+.Em protocol
+must be a valid protocol or
+.Dq unix .
+Examples are
+.Dq tcp
+or
+.Dq udp ,
+both of which imply IPv4 for backward compatibility.
+The names
+.Dq tcp4
+and
+.Dq udp4
+specify IPv4 only.
+The names
+.Dq tcp6
+and
+.Dq udp6
+specify IPv6 only.
+The names
+.Dq tcp46
+and
+.Dq udp46
+specify that the entry accepts both IPv4 and IPv6 connections
+via a wildcard
+.Dv AF_INET6
+socket.
+Rpc based services
+are specified with the
+.Dq rpc/tcp
+or
+.Dq rpc/udp
+service type.
+One can use specify IPv4 and/or IPv6 with the 4, 6 or 46 suffix, for example
+.Dq rpc/tcp6
+or
+.Dq rpc/udp46 .
+TCPMUX services must use
+.Dq tcp ,
+.Dq tcp4 ,
+.Dq tcp6
+or
+.Dq tcp46 .
+.Pp
+The
+.Em wait/nowait
+entry specifies whether the server that is invoked by
+.Nm
+will take over
+the socket associated with the service access point, and thus whether
+.Nm
+should wait for the server to exit before listening for new service
+requests.
+Datagram servers must use
+.Dq wait ,
+as they are always invoked with the original datagram socket bound
+to the specified service address.
+These servers must read at least one datagram from the socket
+before exiting.
+If a datagram server connects
+to its peer, freeing the socket so
+.Nm
+can receive further messages on the socket, it is said to be
+a
+.Dq multi-threaded
+server;
+it should read one datagram from the socket and create a new socket
+connected to the peer.
+It should fork, and the parent should then exit
+to allow
+.Nm
+to check for new service requests to spawn new servers.
+Datagram servers which process all incoming datagrams
+on a socket and eventually time out are said to be
+.Dq single-threaded .
+The
+.Xr comsat 8
+and
+.Xr talkd 8
+utilities are examples of the latter type of
+datagram server.
+The
+.Xr tftpd 8
+utility is an example of a multi-threaded datagram server.
+.Pp
+Servers using stream sockets generally are multi-threaded and
+use the
+.Dq nowait
+entry.
+Connection requests for these services are accepted by
+.Nm ,
+and the server is given only the newly-accepted socket connected
+to a client of the service.
+Most stream-based services operate in this manner.
+Stream-based servers that use
+.Dq wait
+are started with the listening service socket, and must accept
+at least one connection request before exiting.
+Such a server would normally accept and process incoming connection
+requests until a timeout.
+TCPMUX services must use
+.Dq nowait .
+.Pp
+The maximum number of outstanding child processes (or
+.Dq threads )
+for a
+.Dq nowait
+service may be explicitly specified by appending a
+.Dq /
+followed by the number to the
+.Dq nowait
+keyword.
+Normally
+(or if a value of zero is specified) there is no maximum.
+Otherwise,
+once the maximum is reached, further connection attempts will be
+queued up until an existing child process exits.
+This also works
+in the case of
+.Dq wait
+mode, although a value other than one (the
+default) might not make sense in some cases.
+You can also specify the maximum number of connections per minute
+for a given IP address by appending
+a
+.Dq /
+followed by the number to the maximum number of
+outstanding child processes.
+Once the maximum is reached, further
+connections from this IP address will be dropped until the end of the
+minute.
+In addition, you can specify the maximum number of simultaneous
+invocations of each service from a single IP address by appending a
+.Dq /
+followed by the number to the maximum number of outstanding child
+processes.
+Once the maximum is reached, further connections from this
+IP address will be dropped.
+.Pp
+The
+.Em user
+entry should contain the user name of the user as whom the server
+should run.
+This allows for servers to be given less permission
+than root.
+The optional
+.Em group
+part separated by
+.Dq \&:
+allows a group name other
+than the default group for this user to be specified.
+The optional
+.Em login-class
+part separated by
+.Dq /
+allows specification of a login class other
+than the default
+.Dq daemon
+login class.
+.Pp
+The
+.Em server-program
+entry should contain the pathname of the program which is to be
+executed by
+.Nm
+when a request is found on its socket.
+If
+.Nm
+provides this service internally, this entry should
+be
+.Dq internal .
+.Pp
+The
+.Em server-program-arguments
+entry lists the arguments to be passed to the
+.Em server-program ,
+starting with argv[0], which usually is the name of
+the program.
+If the service is provided internally, the
+.Em service-name
+of the service (and any arguments to it) or the word
+.Dq internal
+should take the place of this entry.
+.Pp
+Currently, the only internal service to take arguments is
+.Dq auth .
+Without options, the service will always return
+.Dq ERROR\ : HIDDEN-USER .
+The available arguments to this service that alter its behavior are:
+.Bl -tag -width indent
+.It Fl d Ar fallback
+Provide a
+.Ar fallback
+username.
+If the real
+.Dq auth
+service is enabled
+(with the
+.Fl r
+option discussed below),
+return this username instead of an error
+when lookups fail
+for either socket credentials or the username.
+If the real
+.Dq auth
+service is disabled,
+return this username for every request.
+This is primarily useful when running this service on a NAT machine.
+.It Fl g
+Instead of returning
+the user's name to the ident requester,
+report a
+username made up of random alphanumeric characters,
+e.g.\&
+.Dq c0c993 .
+The
+.Fl g
+flag overrides not only the user names,
+but also any fallback name,
+.Pa .fakeid
+or
+.Pa .noident
+files.
+.It Fl t Xo
+.Ar sec Ns Op . Ns Ar usec
+.Xc
+Specify a timeout for the service.
+The default timeout is 10.0 seconds.
+.It Fl r
+Offer a real
+.Dq auth
+service, as per RFC 1413.
+All the remaining flags apply only in this case.
+.It Fl i
+Return numeric user IDs instead of usernames.
+.It Fl f
+If the file
+.Pa .fakeid
+exists in the home directory of the identified user, report the username
+found in that file instead of the real username.
+If the username found in
+.Pa .fakeid
+is that of an existing user,
+then the real username is reported.
+If the
+.Fl i
+flag is also given then the username in
+.Pa .fakeid
+is checked against existing user IDs instead.
+.It Fl F
+same as
+.Fl f
+but without the restriction that the username in
+.Pa .fakeid
+must not match an existing user.
+.It Fl n
+If the file
+.Pa .noident
+exists in the home directory of the identified user, return
+.Dq ERROR\ : HIDDEN-USER .
+This overrides any
+.Pa fakeid
+file which might exist.
+.It Fl o Ar osname
+Use
+.Ar osname
+instead of the name of the system as reported by
+.Xr uname 3 .
+.El
+.Pp
+The
+.Nm
+utility also provides several other
+.Dq trivial
+services internally by use of
+routines within itself.
+These services are
+.Dq echo ,
+.Dq discard ,
+.Dq chargen
+(character generator),
+.Dq daytime
+(human readable time), and
+.Dq time
+(machine readable time, in the form of the number of seconds since
+midnight, January 1, 1900).
+All of these services are available in
+both TCP and UDP versions; the UDP versions will refuse service if the
+request specifies a reply port corresponding to any internal service.
+(This is done as a defense against looping attacks; the remote IP address
+is logged.)
+For details of these services, consult the
+appropriate
+.Tn RFC
+document.
+.Pp
+The TCPMUX-demultiplexing service is also implemented as an internal service.
+For any TCPMUX-based service to function, the following line must be included
+in
+.Pa inetd.conf :
+.Bd -literal -offset indent
+tcpmux stream tcp nowait root internal
+.Ed
+.Pp
+When given the
+.Fl l
+option
+.Nm
+will log an entry to syslog each time a connection is accepted, noting the
+service selected and the IP-number of the remote requester if available.
+Unless otherwise specified in the configuration file,
+and in the absence of the
+.Fl W
+and
+.Fl w
+options,
+.Nm
+will log to the
+.Dq daemon
+facility.
+.Pp
+The
+.Nm
+utility rereads its configuration file when it receives a hangup signal,
+.Dv SIGHUP .
+Services may be added, deleted or modified when the configuration file
+is reread.
+Except when started in debugging mode,
+or configured otherwise with the
+.Fl p
+option,
+.Nm
+records its process ID in the file
+.Pa /var/run/inetd.pid
+to assist in reconfiguration.
+.Sh IMPLEMENTATION NOTES
+.Ss TCP Wrappers
+When given the
+.Fl w
+option,
+.Nm
+will wrap all services specified as
+.Dq stream nowait
+or
+.Dq dgram
+except for
+.Dq internal
+services.
+If the
+.Fl W
+option is given, such
+.Dq internal
+services will be wrapped.
+If both options are given, wrapping for both
+internal and external services will be enabled.
+Either wrapping option
+will cause failed connections to be logged to the
+.Dq auth
+syslog facility.
+Adding the
+.Fl l
+flag to the wrapping options will include successful connections in the
+logging to the
+.Dq auth
+facility.
+.Pp
+Note that
+.Nm
+only wraps requests for a
+.Dq wait
+service while no servers are available to service requests.
+Once a
+connection to such a service has been allowed,
+.Nm
+has no control
+over subsequent connections to the service until no more servers
+are left listening for connection requests.
+.Pp
+When wrapping is enabled, the
+.Pa tcpd
+daemon is not required, as that functionality is builtin.
+For more information on TCP Wrappers, see the relevant documentation
+.Pq Xr hosts_access 5 .
+When reading that document, keep in mind that
+.Dq internal
+services have no associated daemon name.
+Therefore, the service name
+as specified in
+.Pa inetd.conf
+should be used as the daemon name for
+.Dq internal
+services.
+.Ss TCPMUX
+.Tn RFC 1078
+describes the TCPMUX protocol:
+``A TCP client connects to a foreign host on TCP port 1.
+It sends the
+service name followed by a carriage-return line-feed <CRLF>.
+The
+service name is never case sensitive.
+The server replies with a
+single character indicating positive (+) or negative (\-)
+acknowledgment, immediately followed by an optional message of
+explanation, terminated with a <CRLF>.
+If the reply was positive,
+the selected protocol begins; otherwise the connection is closed.''
+The program is passed the TCP connection as file descriptors 0 and 1.
+.Pp
+If the TCPMUX service name begins with a
+.Dq + ,
+.Nm
+returns the positive reply for the program.
+This allows you to invoke programs that use stdin/stdout
+without putting any special server code in them.
+.Pp
+The special service name
+.Dq help
+causes
+.Nm
+to list the TCPMUX services which are enabled in
+.Pa inetd.conf .
+.Ss IPsec
+The implementation includes a tiny hack
+to support IPsec policy settings for each socket.
+A special form of comment line, starting with
+.Dq Li #@ ,
+is interpreted as a policy specifier.
+Everything after the
+.Dq Li #@
+will be used as an IPsec policy string,
+as described in
+.Xr ipsec_set_policy 3 .
+Each
+policy specifier is applied to all the following lines in
+.Pa inetd.conf
+until the next policy specifier.
+An empty policy specifier resets the IPsec policy.
+.Pp
+If an invalid IPsec policy specifier appears in
+.Pa inetd.conf ,
+.Nm
+will provide an error message via the
+.Xr syslog 3
+interface and abort execution.
+.Ss Ux Domain Sockets
+In addition to running services on IP sockets,
+.Nm
+can also manage
+.Ux
+domain sockets.
+To do this you specify a
+.Em protocol
+of
+.Dq unix
+and specify the
+.Ux
+domain socket as the
+.Em service-name .
+The
+.Em service-type
+may be
+.Dq stream
+or
+.Dq dgram .
+The specification of the socket must be
+an absolute path name,
+optionally prefixed by an owner and mode
+of the form
+.Em :user:group:mode: .
+The specification:
+.Pp
+.Dl ":news:daemon:220:/var/run/sock"
+.Pp
+creates a socket owned
+by user
+.Dq news
+in group
+.Dq daemon
+with permissions allowing only that user and group to connect.
+The default owner is the user that
+.Nm
+is running as.
+The default mode only allows the socket's owner to connect.
+.Pp
+.Sy WARNING :
+while creating a
+.Ux
+domain socket,
+.Nm
+must change the ownership and permissions on the socket.
+This can only be done securely if
+the directory in which the socket is created
+is writable only by root.
+Do
+.Em NOT
+use
+.Nm
+to create sockets in world writable directories
+such as
+.Pa /tmp ;
+use
+.Pa /var/run
+or a similar directory instead.
+.Pp
+Internal services may be run on
+.Ux
+domain sockets, in the usual way.
+In this case
+the name of the internal service
+is determined using
+the last component of the socket's pathname.
+For example, specifying a socket named
+.Pa /var/run/chargen
+would invoke the
+.Dq chargen
+service when a connection is received on that socket.
+.Sh "FILES"
+.Bl -tag -width /var/run/inetd.pid -compact
+.It Pa /etc/inetd.conf
+configuration file
+.It Pa /etc/netconfig
+network configuration data base
+.It Pa /etc/rpc
+translation of service names to RPC program numbers
+.It Pa /etc/services
+translation of service names to port numbers
+.It Pa /var/run/inetd.pid
+the pid of the currently running
+.Nm
+.El
+.Sh "EXAMPLES"
+Here are several example service entries for the various types of services:
+.Bd -literal
+ftp stream tcp nowait root /usr/libexec/ftpd ftpd -l
+ntalk dgram udp wait root /usr/libexec/ntalkd ntalkd
+telnet stream tcp6 nowait root /usr/libexec/telnetd telnetd
+shell stream tcp46 nowait root /usr/libexec/rshd rshd
+tcpmux/+date stream tcp nowait guest /bin/date date
+tcpmux/phonebook stream tcp nowait guest /usr/local/bin/phonebook phonebook
+rstatd/1-3 dgram rpc/udp wait root /usr/libexec/rpc.rstatd rpc.rstatd
+/var/run/echo stream unix nowait root internal
+#@ ipsec ah/require
+chargen stream tcp nowait root internal
+#@
+.Ed
+.Sh "ERROR MESSAGES"
+The
+.Nm
+server
+logs error messages using
+.Xr syslog 3 .
+Important error messages and their explanations are:
+.Pp
+.Bl -ohang -compact
+.It Xo
+.Ar service Ns / Ns Ar protocol
+.No "server failing (looping), service terminated."
+.Xc
+The number of requests for the specified service in the past minute
+exceeded the limit.
+The limit exists to prevent a broken program
+or a malicious user from swamping the system.
+This message may occur for several reasons:
+.Bl -enum -offset indent
+.It
+There are many hosts requesting the service within a short time period.
+.It
+A broken client program is requesting the service too frequently.
+.It
+A malicious user is running a program to invoke the service in
+a denial-of-service attack.
+.It
+The invoked service program has an error that causes clients
+to retry quickly.
+.El
+.Pp
+Use the
+.Fl R Ar rate
+option,
+as described above, to change the rate limit.
+Once the limit is reached, the service will be
+reenabled automatically in 10 minutes.
+.Pp
+.It Xo
+.Ar service Ns / Ns Ar protocol :
+.No \&No such user
+.Ar user ,
+.No service ignored
+.Xc
+.It Xo
+.Ar service Ns / Ns Ar protocol :
+.No getpwnam :
+.Ar user :
+.No \&No such user
+.Xc
+No entry for
+.Ar user
+exists in the
+.Xr passwd 5
+database.
+The first message
+occurs when
+.Nm
+(re)reads the configuration file.
+The second message occurs when the
+service is invoked.
+.Pp
+.It Xo
+.Ar service :
+.No can't set uid
+.Ar uid
+.Xc
+.It Xo
+.Ar service :
+.No can't set gid
+.Ar gid
+.Xc
+The user or group ID for the entry's
+.Ar user
+field is invalid.
+.Pp
+.It "setsockopt(SO_PRIVSTATE): Operation not supported"
+The
+.Nm
+utility attempted to renounce the privileged state associated with a
+socket but was unable to.
+.Pp
+.It Xo unknown
+.Ar rpc/udp
+or
+.Ar rpc/tcp
+.Xc
+No entry was found for either
+.Ar udp
+or
+.Ar tcp
+in the
+.Xr netconfig 5
+database.
+.Pp
+.It Xo unknown
+.Ar rpc/udp6
+or
+.Ar rpc/tcp6
+.Xc
+No entry was found for either
+.Ar udp6
+or
+.Ar tcp6
+in the
+.Xr netconfig 5
+database.
+.El
+.Sh SEE ALSO
+.Xr ipsec_set_policy 3 ,
+.Xr hosts_access 5 ,
+.Xr hosts_options 5 ,
+.Xr login.conf 5 ,
+.Xr netconfig 5 ,
+.Xr passwd 5 ,
+.Xr rpc 5 ,
+.Xr services 5 ,
+.Xr comsat 8 ,
+.Xr fingerd 8 ,
+.Xr ftpd 8 ,
+.Xr rlogind 8 ,
+.Xr rpcbind 8 ,
+.Xr rshd 8 ,
+.Xr talkd 8 ,
+.Xr telnetd 8 ,
+.Xr tftpd 8
+.Rs
+.%A Michael C. St. Johns
+.%T Identification Protocol
+.%O RFC1413
+.Re
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.3 .
+TCPMUX is based on code and documentation by Mark Lottor.
+Support for
+.Tn "ONC RPC"
+based services is modeled after that
+provided by
+.Tn SunOS
+4.1.
+The IPsec hack was contributed by the KAME project in 1999.
+The
+.Fx
+TCP Wrappers support first appeared in
+.Fx 3.2 .
diff --git a/usr.sbin/inetd/inetd.c b/usr.sbin/inetd/inetd.c
new file mode 100644
index 0000000..6bcbac6
--- /dev/null
+++ b/usr.sbin/inetd/inetd.c
@@ -0,0 +1,2581 @@
+/*
+ * Copyright (c) 1983, 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) 1983, 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[] = "@(#)from: inetd.c 8.4 (Berkeley) 4/13/94";
+#endif
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Inetd - Internet super-server
+ *
+ * This program invokes all internet services as needed. Connection-oriented
+ * services are invoked each time a connection is made, by creating a process.
+ * This process is passed the connection as file descriptor 0 and is expected
+ * to do a getpeername to find out the source host and port.
+ *
+ * Datagram oriented services are invoked when a datagram
+ * arrives; a process is created and passed a pending message
+ * on file descriptor 0. Datagram servers may either connect
+ * to their peer, freeing up the original socket for inetd
+ * to receive further messages on, or ``take over the socket'',
+ * processing all arriving datagrams and, eventually, timing
+ * out. The first type of server is said to be ``multi-threaded'';
+ * the second type of server ``single-threaded''.
+ *
+ * Inetd uses a configuration file which is read at startup
+ * and, possibly, at some later time in response to a hangup signal.
+ * The configuration file is ``free format'' with fields given in the
+ * order shown below. Continuation lines for an entry must begin with
+ * a space or tab. All fields must be present in each entry.
+ *
+ * service name must be in /etc/services
+ * or name a tcpmux service
+ * or specify a unix domain socket
+ * socket type stream/dgram/raw/rdm/seqpacket
+ * protocol tcp[4][6][/faith], udp[4][6], unix
+ * wait/nowait single-threaded/multi-threaded
+ * user[:group][/login-class] user/group/login-class to run daemon as
+ * server program full path name
+ * server program arguments maximum of MAXARGS (20)
+ *
+ * TCP services without official port numbers are handled with the
+ * RFC1078-based tcpmux internal service. Tcpmux listens on port 1 for
+ * requests. When a connection is made from a foreign host, the service
+ * requested is passed to tcpmux, which looks it up in the servtab list
+ * and returns the proper entry for the service. Tcpmux returns a
+ * negative reply if the service doesn't exist, otherwise the invoked
+ * server is expected to return the positive reply if the service type in
+ * inetd.conf file has the prefix "tcpmux/". If the service type has the
+ * prefix "tcpmux/+", tcpmux will return the positive reply for the
+ * process; this is for compatibility with older server code, and also
+ * allows you to invoke programs that use stdin/stdout without putting any
+ * special server code in them. Services that use tcpmux are "nowait"
+ * because they do not have a well-known port and hence cannot listen
+ * for new requests.
+ *
+ * For RPC services
+ * service name/version must be in /etc/rpc
+ * socket type stream/dgram/raw/rdm/seqpacket
+ * protocol rpc/tcp[4][6], rpc/udp[4][6]
+ * wait/nowait single-threaded/multi-threaded
+ * user[:group][/login-class] user/group/login-class to run daemon as
+ * server program full path name
+ * server program arguments maximum of MAXARGS
+ *
+ * Comment lines are indicated by a `#' in column 1.
+ *
+ * #ifdef IPSEC
+ * Comment lines that start with "#@" denote IPsec policy string, as described
+ * in ipsec_set_policy(3). This will affect all the following items in
+ * inetd.conf(8). To reset the policy, just use "#@" line. By default,
+ * there's no IPsec policy.
+ * #endif
+ */
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <err.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <libutil.h>
+#include <limits.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <syslog.h>
+#include <tcpd.h>
+#include <unistd.h>
+
+#include "inetd.h"
+#include "pathnames.h"
+
+#ifdef IPSEC
+#include <netipsec/ipsec.h>
+#ifndef IPSEC_POLICY_IPSEC /* no ipsec support on old ipsec */
+#undef IPSEC
+#endif
+#endif
+
+#ifndef LIBWRAP_ALLOW_FACILITY
+# define LIBWRAP_ALLOW_FACILITY LOG_AUTH
+#endif
+#ifndef LIBWRAP_ALLOW_SEVERITY
+# define LIBWRAP_ALLOW_SEVERITY LOG_INFO
+#endif
+#ifndef LIBWRAP_DENY_FACILITY
+# define LIBWRAP_DENY_FACILITY LOG_AUTH
+#endif
+#ifndef LIBWRAP_DENY_SEVERITY
+# define LIBWRAP_DENY_SEVERITY LOG_WARNING
+#endif
+
+#define ISWRAP(sep) \
+ ( ((wrap_ex && !(sep)->se_bi) || (wrap_bi && (sep)->se_bi)) \
+ && (sep->se_family == AF_INET || sep->se_family == AF_INET6) \
+ && ( ((sep)->se_accept && (sep)->se_socktype == SOCK_STREAM) \
+ || (sep)->se_socktype == SOCK_DGRAM))
+
+#ifdef LOGIN_CAP
+#include <login_cap.h>
+
+/* see init.c */
+#define RESOURCE_RC "daemon"
+
+#endif
+
+#ifndef MAXCHILD
+#define MAXCHILD -1 /* maximum number of this service
+ < 0 = no limit */
+#endif
+
+#ifndef MAXCPM
+#define MAXCPM -1 /* rate limit invocations from a
+ single remote address,
+ < 0 = no limit */
+#endif
+
+#ifndef MAXPERIP
+#define MAXPERIP -1 /* maximum number of this service
+ from a single remote address,
+ < 0 = no limit */
+#endif
+
+#ifndef TOOMANY
+#define TOOMANY 256 /* don't start more than TOOMANY */
+#endif
+#define CNT_INTVL 60 /* servers in CNT_INTVL sec. */
+#define RETRYTIME (60*10) /* retry after bind or server fail */
+#define MAX_MAXCHLD 32767 /* max allowable max children */
+
+#define SIGBLOCK (sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM))
+
+void close_sep(struct servtab *);
+void flag_signal(int);
+void flag_config(int);
+void config(void);
+int cpmip(const struct servtab *, int);
+void endconfig(void);
+struct servtab *enter(struct servtab *);
+void freeconfig(struct servtab *);
+struct servtab *getconfigent(void);
+int matchservent(const char *, const char *, const char *);
+char *nextline(FILE *);
+void addchild(struct servtab *, int);
+void flag_reapchild(int);
+void reapchild(void);
+void enable(struct servtab *);
+void disable(struct servtab *);
+void flag_retry(int);
+void retry(void);
+int setconfig(void);
+void setup(struct servtab *);
+#ifdef IPSEC
+void ipsecsetup(struct servtab *);
+#endif
+void unregisterrpc(register struct servtab *sep);
+static struct conninfo *search_conn(struct servtab *sep, int ctrl);
+static int room_conn(struct servtab *sep, struct conninfo *conn);
+static void addchild_conn(struct conninfo *conn, pid_t pid);
+static void reapchild_conn(pid_t pid);
+static void free_conn(struct conninfo *conn);
+static void resize_conn(struct servtab *sep, int maxperip);
+static void free_connlist(struct servtab *sep);
+static void free_proc(struct procinfo *);
+static struct procinfo *search_proc(pid_t pid, int add);
+static int hashval(char *p, int len);
+
+int allow_severity;
+int deny_severity;
+int wrap_ex = 0;
+int wrap_bi = 0;
+int debug = 0;
+int dolog = 0;
+int maxsock; /* highest-numbered descriptor */
+fd_set allsock;
+int options;
+int timingout;
+int toomany = TOOMANY;
+int maxchild = MAXCHILD;
+int maxcpm = MAXCPM;
+int maxperip = MAXPERIP;
+struct servent *sp;
+struct rpcent *rpc;
+char *hostname = NULL;
+struct sockaddr_in *bind_sa4;
+int v4bind_ok = 0;
+#ifdef INET6
+struct sockaddr_in6 *bind_sa6;
+int v6bind_ok = 0;
+#endif
+int signalpipe[2];
+#ifdef SANITY_CHECK
+int nsock;
+#endif
+uid_t euid;
+gid_t egid;
+mode_t mask;
+
+struct servtab *servtab;
+
+extern struct biltin biltins[];
+
+const char *CONFIG = _PATH_INETDCONF;
+const char *pid_file = _PATH_INETDPID;
+struct pidfh *pfh = NULL;
+
+struct netconfig *udpconf, *tcpconf, *udp6conf, *tcp6conf;
+
+static LIST_HEAD(, procinfo) proctable[PERIPSIZE];
+
+int
+getvalue(const char *arg, int *value, const char *whine)
+{
+ int tmp;
+ char *p;
+
+ tmp = strtol(arg, &p, 0);
+ if (tmp < 0 || *p) {
+ syslog(LOG_ERR, whine, arg);
+ return 1; /* failure */
+ }
+ *value = tmp;
+ return 0; /* success */
+}
+
+static sa_family_t
+whichaf(struct request_info *req)
+{
+ struct sockaddr *sa;
+
+ sa = (struct sockaddr *)req->client->sin;
+ if (sa == NULL)
+ return AF_UNSPEC;
+ if (sa->sa_family == AF_INET6 &&
+ IN6_IS_ADDR_V4MAPPED(&((struct sockaddr_in6 *)sa)->sin6_addr))
+ return AF_INET;
+ return sa->sa_family;
+}
+
+int
+main(int argc, char **argv)
+{
+ struct servtab *sep;
+ struct passwd *pwd;
+ struct group *grp;
+ struct sigaction sa, saalrm, sachld, sahup, sapipe;
+ int ch, dofork;
+ pid_t pid;
+ char buf[50];
+#ifdef LOGIN_CAP
+ login_cap_t *lc = NULL;
+#endif
+ struct request_info req;
+ int denied;
+ char *service = NULL;
+ union {
+ struct sockaddr peer_un;
+ struct sockaddr_in peer_un4;
+ struct sockaddr_in6 peer_un6;
+ struct sockaddr_storage peer_max;
+ } p_un;
+#define peer p_un.peer_un
+#define peer4 p_un.peer_un4
+#define peer6 p_un.peer_un6
+#define peermax p_un.peer_max
+ int i;
+ struct addrinfo hints, *res;
+ const char *servname;
+ int error;
+ struct conninfo *conn;
+
+ openlog("inetd", LOG_PID | LOG_NOWAIT | LOG_PERROR, LOG_DAEMON);
+
+ while ((ch = getopt(argc, argv, "dlwWR:a:c:C:p:s:")) != -1)
+ switch(ch) {
+ case 'd':
+ debug = 1;
+ options |= SO_DEBUG;
+ break;
+ case 'l':
+ dolog = 1;
+ break;
+ case 'R':
+ getvalue(optarg, &toomany,
+ "-R %s: bad value for service invocation rate");
+ break;
+ case 'c':
+ getvalue(optarg, &maxchild,
+ "-c %s: bad value for maximum children");
+ break;
+ case 'C':
+ getvalue(optarg, &maxcpm,
+ "-C %s: bad value for maximum children/minute");
+ break;
+ case 'a':
+ hostname = optarg;
+ break;
+ case 'p':
+ pid_file = optarg;
+ break;
+ case 's':
+ getvalue(optarg, &maxperip,
+ "-s %s: bad value for maximum children per source address");
+ break;
+ case 'w':
+ wrap_ex++;
+ break;
+ case 'W':
+ wrap_bi++;
+ break;
+ case '?':
+ default:
+ syslog(LOG_ERR,
+ "usage: inetd [-dlwW] [-a address] [-R rate]"
+ " [-c maximum] [-C rate]"
+ " [-p pidfile] [conf-file]");
+ exit(EX_USAGE);
+ }
+ /*
+ * Initialize Bind Addrs.
+ * When hostname is NULL, wild card bind addrs are obtained from
+ * getaddrinfo(). But getaddrinfo() requires at least one of
+ * hostname or servname is non NULL.
+ * So when hostname is NULL, set dummy value to servname.
+ * Since getaddrinfo() doesn't accept numeric servname, and
+ * we doesn't use ai_socktype of struct addrinfo returned
+ * from getaddrinfo(), we set dummy value to ai_socktype.
+ */
+ servname = (hostname == NULL) ? "0" /* dummy */ : NULL;
+
+ bzero(&hints, sizeof(struct addrinfo));
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM; /* dummy */
+ error = getaddrinfo(hostname, servname, &hints, &res);
+ if (error != 0) {
+ syslog(LOG_ERR, "-a %s: %s", hostname, gai_strerror(error));
+ if (error == EAI_SYSTEM)
+ syslog(LOG_ERR, "%s", strerror(errno));
+ exit(EX_USAGE);
+ }
+ do {
+ if (res->ai_addr == NULL) {
+ syslog(LOG_ERR, "-a %s: getaddrinfo failed", hostname);
+ exit(EX_USAGE);
+ }
+ switch (res->ai_addr->sa_family) {
+ case AF_INET:
+ if (v4bind_ok)
+ continue;
+ bind_sa4 = (struct sockaddr_in *)res->ai_addr;
+ /* init port num in case servname is dummy */
+ bind_sa4->sin_port = 0;
+ v4bind_ok = 1;
+ continue;
+#ifdef INET6
+ case AF_INET6:
+ if (v6bind_ok)
+ continue;
+ bind_sa6 = (struct sockaddr_in6 *)res->ai_addr;
+ /* init port num in case servname is dummy */
+ bind_sa6->sin6_port = 0;
+ v6bind_ok = 1;
+ continue;
+#endif
+ }
+ if (v4bind_ok
+#ifdef INET6
+ && v6bind_ok
+#endif
+ )
+ break;
+ } while ((res = res->ai_next) != NULL);
+ if (!v4bind_ok
+#ifdef INET6
+ && !v6bind_ok
+#endif
+ ) {
+ syslog(LOG_ERR, "-a %s: unknown address family", hostname);
+ exit(EX_USAGE);
+ }
+
+ euid = geteuid();
+ egid = getegid();
+ umask(mask = umask(0777));
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 0)
+ CONFIG = argv[0];
+ if (access(CONFIG, R_OK) < 0)
+ syslog(LOG_ERR, "Accessing %s: %m, continuing anyway.", CONFIG);
+ if (debug == 0) {
+ pid_t otherpid;
+
+ pfh = pidfile_open(pid_file, 0600, &otherpid);
+ if (pfh == NULL) {
+ if (errno == EEXIST) {
+ syslog(LOG_ERR, "%s already running, pid: %d",
+ getprogname(), otherpid);
+ exit(EX_OSERR);
+ }
+ syslog(LOG_WARNING, "pidfile_open() failed: %m");
+ }
+
+ if (daemon(0, 0) < 0) {
+ syslog(LOG_WARNING, "daemon(0,0) failed: %m");
+ }
+ /* From now on we don't want syslog messages going to stderr. */
+ closelog();
+ openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
+ /*
+ * In case somebody has started inetd manually, we need to
+ * clear the logname, so that old servers run as root do not
+ * get the user's logname..
+ */
+ if (setlogin("") < 0) {
+ syslog(LOG_WARNING, "cannot clear logname: %m");
+ /* no big deal if it fails.. */
+ }
+ if (pfh != NULL && pidfile_write(pfh) == -1) {
+ syslog(LOG_WARNING, "pidfile_write(): %m");
+ }
+ }
+
+ for (i = 0; i < PERIPSIZE; ++i)
+ LIST_INIT(&proctable[i]);
+
+ if (v4bind_ok) {
+ udpconf = getnetconfigent("udp");
+ tcpconf = getnetconfigent("tcp");
+ if (udpconf == NULL || tcpconf == NULL) {
+ syslog(LOG_ERR, "unknown rpc/udp or rpc/tcp");
+ exit(EX_USAGE);
+ }
+ }
+#ifdef INET6
+ if (v6bind_ok) {
+ udp6conf = getnetconfigent("udp6");
+ tcp6conf = getnetconfigent("tcp6");
+ if (udp6conf == NULL || tcp6conf == NULL) {
+ syslog(LOG_ERR, "unknown rpc/udp6 or rpc/tcp6");
+ exit(EX_USAGE);
+ }
+ }
+#endif
+
+ sa.sa_flags = 0;
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, SIGALRM);
+ sigaddset(&sa.sa_mask, SIGCHLD);
+ sigaddset(&sa.sa_mask, SIGHUP);
+ sa.sa_handler = flag_retry;
+ sigaction(SIGALRM, &sa, &saalrm);
+ config();
+ sa.sa_handler = flag_config;
+ sigaction(SIGHUP, &sa, &sahup);
+ sa.sa_handler = flag_reapchild;
+ sigaction(SIGCHLD, &sa, &sachld);
+ sa.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &sa, &sapipe);
+
+ {
+ /* space for daemons to overwrite environment for ps */
+#define DUMMYSIZE 100
+ char dummy[DUMMYSIZE];
+
+ (void)memset(dummy, 'x', DUMMYSIZE - 1);
+ dummy[DUMMYSIZE - 1] = '\0';
+ (void)setenv("inetd_dummy", dummy, 1);
+ }
+
+ if (pipe(signalpipe) != 0) {
+ syslog(LOG_ERR, "pipe: %m");
+ exit(EX_OSERR);
+ }
+ if (fcntl(signalpipe[0], F_SETFD, FD_CLOEXEC) < 0 ||
+ fcntl(signalpipe[1], F_SETFD, FD_CLOEXEC) < 0) {
+ syslog(LOG_ERR, "signalpipe: fcntl (F_SETFD, FD_CLOEXEC): %m");
+ exit(EX_OSERR);
+ }
+ FD_SET(signalpipe[0], &allsock);
+#ifdef SANITY_CHECK
+ nsock++;
+#endif
+ if (signalpipe[0] > maxsock)
+ maxsock = signalpipe[0];
+ if (signalpipe[1] > maxsock)
+ maxsock = signalpipe[1];
+
+ for (;;) {
+ int n, ctrl;
+ fd_set readable;
+
+#ifdef SANITY_CHECK
+ if (nsock == 0) {
+ syslog(LOG_ERR, "%s: nsock=0", __func__);
+ exit(EX_SOFTWARE);
+ }
+#endif
+ readable = allsock;
+ if ((n = select(maxsock + 1, &readable, (fd_set *)0,
+ (fd_set *)0, (struct timeval *)0)) <= 0) {
+ if (n < 0 && errno != EINTR) {
+ syslog(LOG_WARNING, "select: %m");
+ sleep(1);
+ }
+ continue;
+ }
+ /* handle any queued signal flags */
+ if (FD_ISSET(signalpipe[0], &readable)) {
+ int nsig;
+ if (ioctl(signalpipe[0], FIONREAD, &nsig) != 0) {
+ syslog(LOG_ERR, "ioctl: %m");
+ exit(EX_OSERR);
+ }
+ while (--nsig >= 0) {
+ char c;
+ if (read(signalpipe[0], &c, 1) != 1) {
+ syslog(LOG_ERR, "read: %m");
+ exit(EX_OSERR);
+ }
+ if (debug)
+ warnx("handling signal flag %c", c);
+ switch(c) {
+ case 'A': /* sigalrm */
+ retry();
+ break;
+ case 'C': /* sigchld */
+ reapchild();
+ break;
+ case 'H': /* sighup */
+ config();
+ break;
+ }
+ }
+ }
+ for (sep = servtab; n && sep; sep = sep->se_next)
+ if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) {
+ n--;
+ if (debug)
+ warnx("someone wants %s", sep->se_service);
+ dofork = !sep->se_bi || sep->se_bi->bi_fork || ISWRAP(sep);
+ conn = NULL;
+ if (sep->se_accept && sep->se_socktype == SOCK_STREAM) {
+ i = 1;
+ if (ioctl(sep->se_fd, FIONBIO, &i) < 0)
+ syslog(LOG_ERR, "ioctl (FIONBIO, 1): %m");
+ ctrl = accept(sep->se_fd, (struct sockaddr *)0,
+ (socklen_t *)0);
+ if (debug)
+ warnx("accept, ctrl %d", ctrl);
+ if (ctrl < 0) {
+ if (errno != EINTR)
+ syslog(LOG_WARNING,
+ "accept (for %s): %m",
+ sep->se_service);
+ if (sep->se_accept &&
+ sep->se_socktype == SOCK_STREAM)
+ close(ctrl);
+ continue;
+ }
+ i = 0;
+ if (ioctl(sep->se_fd, FIONBIO, &i) < 0)
+ syslog(LOG_ERR, "ioctl1(FIONBIO, 0): %m");
+ if (ioctl(ctrl, FIONBIO, &i) < 0)
+ syslog(LOG_ERR, "ioctl2(FIONBIO, 0): %m");
+ if (cpmip(sep, ctrl) < 0) {
+ close(ctrl);
+ continue;
+ }
+ if (dofork &&
+ (conn = search_conn(sep, ctrl)) != NULL &&
+ !room_conn(sep, conn)) {
+ close(ctrl);
+ continue;
+ }
+ } else
+ ctrl = sep->se_fd;
+ if (dolog && !ISWRAP(sep)) {
+ char pname[INET6_ADDRSTRLEN] = "unknown";
+ socklen_t sl;
+ sl = sizeof peermax;
+ if (getpeername(ctrl, (struct sockaddr *)
+ &peermax, &sl)) {
+ sl = sizeof peermax;
+ if (recvfrom(ctrl, buf, sizeof(buf),
+ MSG_PEEK,
+ (struct sockaddr *)&peermax,
+ &sl) >= 0) {
+ getnameinfo((struct sockaddr *)&peermax,
+ peer.sa_len,
+ pname, sizeof(pname),
+ NULL, 0, NI_NUMERICHOST);
+ }
+ } else {
+ getnameinfo((struct sockaddr *)&peermax,
+ peer.sa_len,
+ pname, sizeof(pname),
+ NULL, 0, NI_NUMERICHOST);
+ }
+ syslog(LOG_INFO,"%s from %s", sep->se_service, pname);
+ }
+ (void) sigblock(SIGBLOCK);
+ pid = 0;
+ /*
+ * Fork for all external services, builtins which need to
+ * fork and anything we're wrapping (as wrapping might
+ * block or use hosts_options(5) twist).
+ */
+ if (dofork) {
+ if (sep->se_count++ == 0)
+ (void)gettimeofday(&sep->se_time, (struct timezone *)NULL);
+ else if (toomany > 0 && sep->se_count >= toomany) {
+ struct timeval now;
+
+ (void)gettimeofday(&now, (struct timezone *)NULL);
+ if (now.tv_sec - sep->se_time.tv_sec >
+ CNT_INTVL) {
+ sep->se_time = now;
+ sep->se_count = 1;
+ } else {
+ syslog(LOG_ERR,
+ "%s/%s server failing (looping), service terminated",
+ sep->se_service, sep->se_proto);
+ if (sep->se_accept &&
+ sep->se_socktype == SOCK_STREAM)
+ close(ctrl);
+ close_sep(sep);
+ free_conn(conn);
+ sigsetmask(0L);
+ if (!timingout) {
+ timingout = 1;
+ alarm(RETRYTIME);
+ }
+ continue;
+ }
+ }
+ pid = fork();
+ }
+ if (pid < 0) {
+ syslog(LOG_ERR, "fork: %m");
+ if (sep->se_accept &&
+ sep->se_socktype == SOCK_STREAM)
+ close(ctrl);
+ free_conn(conn);
+ sigsetmask(0L);
+ sleep(1);
+ continue;
+ }
+ if (pid) {
+ addchild_conn(conn, pid);
+ addchild(sep, pid);
+ }
+ sigsetmask(0L);
+ if (pid == 0) {
+ pidfile_close(pfh);
+ if (dofork) {
+ sigaction(SIGALRM, &saalrm, (struct sigaction *)0);
+ sigaction(SIGCHLD, &sachld, (struct sigaction *)0);
+ sigaction(SIGHUP, &sahup, (struct sigaction *)0);
+ /* SIGPIPE reset before exec */
+ }
+ /*
+ * Call tcpmux to find the real service to exec.
+ */
+ if (sep->se_bi &&
+ sep->se_bi->bi_fn == (bi_fn_t *) tcpmux) {
+ sep = tcpmux(ctrl);
+ if (sep == NULL) {
+ close(ctrl);
+ _exit(0);
+ }
+ }
+ if (ISWRAP(sep)) {
+ inetd_setproctitle("wrapping", ctrl);
+ service = sep->se_server_name ?
+ sep->se_server_name : sep->se_service;
+ request_init(&req, RQ_DAEMON, service, RQ_FILE, ctrl, 0);
+ fromhost(&req);
+ deny_severity = LIBWRAP_DENY_FACILITY|LIBWRAP_DENY_SEVERITY;
+ allow_severity = LIBWRAP_ALLOW_FACILITY|LIBWRAP_ALLOW_SEVERITY;
+ denied = !hosts_access(&req);
+ if (denied) {
+ syslog(deny_severity,
+ "refused connection from %.500s, service %s (%s%s)",
+ eval_client(&req), service, sep->se_proto,
+ (whichaf(&req) == AF_INET6) ? "6" : "");
+ if (sep->se_socktype != SOCK_STREAM)
+ recv(ctrl, buf, sizeof (buf), 0);
+ if (dofork) {
+ sleep(1);
+ _exit(0);
+ }
+ }
+ if (dolog) {
+ syslog(allow_severity,
+ "connection from %.500s, service %s (%s%s)",
+ eval_client(&req), service, sep->se_proto,
+ (whichaf(&req) == AF_INET6) ? "6" : "");
+ }
+ }
+ if (sep->se_bi) {
+ (*sep->se_bi->bi_fn)(ctrl, sep);
+ } else {
+ if (debug)
+ warnx("%d execl %s",
+ getpid(), sep->se_server);
+ /* Clear close-on-exec. */
+ if (fcntl(ctrl, F_SETFD, 0) < 0) {
+ syslog(LOG_ERR,
+ "%s/%s: fcntl (F_SETFD, 0): %m",
+ sep->se_service, sep->se_proto);
+ _exit(EX_OSERR);
+ }
+ if (ctrl != 0) {
+ dup2(ctrl, 0);
+ close(ctrl);
+ }
+ dup2(0, 1);
+ dup2(0, 2);
+ if ((pwd = getpwnam(sep->se_user)) == NULL) {
+ syslog(LOG_ERR,
+ "%s/%s: %s: no such user",
+ sep->se_service, sep->se_proto,
+ sep->se_user);
+ if (sep->se_socktype != SOCK_STREAM)
+ recv(0, buf, sizeof (buf), 0);
+ _exit(EX_NOUSER);
+ }
+ grp = NULL;
+ if ( sep->se_group != NULL
+ && (grp = getgrnam(sep->se_group)) == NULL
+ ) {
+ syslog(LOG_ERR,
+ "%s/%s: %s: no such group",
+ sep->se_service, sep->se_proto,
+ sep->se_group);
+ if (sep->se_socktype != SOCK_STREAM)
+ recv(0, buf, sizeof (buf), 0);
+ _exit(EX_NOUSER);
+ }
+ if (grp != NULL)
+ pwd->pw_gid = grp->gr_gid;
+#ifdef LOGIN_CAP
+ if ((lc = login_getclass(sep->se_class)) == NULL) {
+ /* error syslogged by getclass */
+ syslog(LOG_ERR,
+ "%s/%s: %s: login class error",
+ sep->se_service, sep->se_proto,
+ sep->se_class);
+ if (sep->se_socktype != SOCK_STREAM)
+ recv(0, buf, sizeof (buf), 0);
+ _exit(EX_NOUSER);
+ }
+#endif
+ if (setsid() < 0) {
+ syslog(LOG_ERR,
+ "%s: can't setsid(): %m",
+ sep->se_service);
+ /* _exit(EX_OSERR); not fatal yet */
+ }
+#ifdef LOGIN_CAP
+ if (setusercontext(lc, pwd, pwd->pw_uid,
+ LOGIN_SETALL & ~LOGIN_SETMAC)
+ != 0) {
+ syslog(LOG_ERR,
+ "%s: can't setusercontext(..%s..): %m",
+ sep->se_service, sep->se_user);
+ _exit(EX_OSERR);
+ }
+ login_close(lc);
+#else
+ if (pwd->pw_uid) {
+ if (setlogin(sep->se_user) < 0) {
+ syslog(LOG_ERR,
+ "%s: can't setlogin(%s): %m",
+ sep->se_service, sep->se_user);
+ /* _exit(EX_OSERR); not yet */
+ }
+ if (setgid(pwd->pw_gid) < 0) {
+ syslog(LOG_ERR,
+ "%s: can't set gid %d: %m",
+ sep->se_service, pwd->pw_gid);
+ _exit(EX_OSERR);
+ }
+ (void) initgroups(pwd->pw_name,
+ pwd->pw_gid);
+ if (setuid(pwd->pw_uid) < 0) {
+ syslog(LOG_ERR,
+ "%s: can't set uid %d: %m",
+ sep->se_service, pwd->pw_uid);
+ _exit(EX_OSERR);
+ }
+ }
+#endif
+ sigaction(SIGPIPE, &sapipe,
+ (struct sigaction *)0);
+ execv(sep->se_server, sep->se_argv);
+ syslog(LOG_ERR,
+ "cannot execute %s: %m", sep->se_server);
+ if (sep->se_socktype != SOCK_STREAM)
+ recv(0, buf, sizeof (buf), 0);
+ }
+ if (dofork)
+ _exit(0);
+ }
+ if (sep->se_accept && sep->se_socktype == SOCK_STREAM)
+ close(ctrl);
+ }
+ }
+}
+
+/*
+ * Add a signal flag to the signal flag queue for later handling
+ */
+
+void
+flag_signal(int c)
+{
+ char ch = c;
+
+ if (write(signalpipe[1], &ch, 1) != 1) {
+ syslog(LOG_ERR, "write: %m");
+ _exit(EX_OSERR);
+ }
+}
+
+/*
+ * Record a new child pid for this service. If we've reached the
+ * limit on children, then stop accepting incoming requests.
+ */
+
+void
+addchild(struct servtab *sep, pid_t pid)
+{
+ if (sep->se_maxchild <= 0)
+ return;
+#ifdef SANITY_CHECK
+ if (sep->se_numchild >= sep->se_maxchild) {
+ syslog(LOG_ERR, "%s: %d >= %d",
+ __func__, sep->se_numchild, sep->se_maxchild);
+ exit(EX_SOFTWARE);
+ }
+#endif
+ sep->se_pids[sep->se_numchild++] = pid;
+ if (sep->se_numchild == sep->se_maxchild)
+ disable(sep);
+}
+
+/*
+ * Some child process has exited. See if it's on somebody's list.
+ */
+
+void
+flag_reapchild(int signo __unused)
+{
+ flag_signal('C');
+}
+
+void
+reapchild(void)
+{
+ int k, status;
+ pid_t pid;
+ struct servtab *sep;
+
+ for (;;) {
+ pid = wait3(&status, WNOHANG, (struct rusage *)0);
+ if (pid <= 0)
+ break;
+ if (debug)
+ warnx("%d reaped, %s %u", pid,
+ WIFEXITED(status) ? "status" : "signal",
+ WIFEXITED(status) ? WEXITSTATUS(status)
+ : WTERMSIG(status));
+ for (sep = servtab; sep; sep = sep->se_next) {
+ for (k = 0; k < sep->se_numchild; k++)
+ if (sep->se_pids[k] == pid)
+ break;
+ if (k == sep->se_numchild)
+ continue;
+ if (sep->se_numchild == sep->se_maxchild)
+ enable(sep);
+ sep->se_pids[k] = sep->se_pids[--sep->se_numchild];
+ if (WIFSIGNALED(status) || WEXITSTATUS(status))
+ syslog(LOG_WARNING,
+ "%s[%d]: exited, %s %u",
+ sep->se_server, pid,
+ WIFEXITED(status) ? "status" : "signal",
+ WIFEXITED(status) ? WEXITSTATUS(status)
+ : WTERMSIG(status));
+ break;
+ }
+ reapchild_conn(pid);
+ }
+}
+
+void
+flag_config(int signo __unused)
+{
+ flag_signal('H');
+}
+
+void
+config(void)
+{
+ struct servtab *sep, *new, **sepp;
+ long omask;
+ int new_nomapped;
+#ifdef LOGIN_CAP
+ login_cap_t *lc = NULL;
+#endif
+
+ if (!setconfig()) {
+ syslog(LOG_ERR, "%s: %m", CONFIG);
+ return;
+ }
+ for (sep = servtab; sep; sep = sep->se_next)
+ sep->se_checked = 0;
+ while ((new = getconfigent())) {
+ if (getpwnam(new->se_user) == NULL) {
+ syslog(LOG_ERR,
+ "%s/%s: no such user '%s', service ignored",
+ new->se_service, new->se_proto, new->se_user);
+ continue;
+ }
+ if (new->se_group && getgrnam(new->se_group) == NULL) {
+ syslog(LOG_ERR,
+ "%s/%s: no such group '%s', service ignored",
+ new->se_service, new->se_proto, new->se_group);
+ continue;
+ }
+#ifdef LOGIN_CAP
+ if ((lc = login_getclass(new->se_class)) == NULL) {
+ /* error syslogged by getclass */
+ syslog(LOG_ERR,
+ "%s/%s: %s: login class error, service ignored",
+ new->se_service, new->se_proto, new->se_class);
+ continue;
+ }
+ login_close(lc);
+#endif
+ new_nomapped = new->se_nomapped;
+ for (sep = servtab; sep; sep = sep->se_next)
+ if (strcmp(sep->se_service, new->se_service) == 0 &&
+ strcmp(sep->se_proto, new->se_proto) == 0 &&
+ sep->se_rpc == new->se_rpc &&
+ sep->se_socktype == new->se_socktype &&
+ sep->se_family == new->se_family)
+ break;
+ if (sep != 0) {
+ int i;
+
+#define SWAP(t,a, b) { t c = a; a = b; b = c; }
+ omask = sigblock(SIGBLOCK);
+ if (sep->se_nomapped != new->se_nomapped) {
+ /* for rpc keep old nommaped till unregister */
+ if (!sep->se_rpc)
+ sep->se_nomapped = new->se_nomapped;
+ sep->se_reset = 1;
+ }
+ /* copy over outstanding child pids */
+ if (sep->se_maxchild > 0 && new->se_maxchild > 0) {
+ new->se_numchild = sep->se_numchild;
+ if (new->se_numchild > new->se_maxchild)
+ new->se_numchild = new->se_maxchild;
+ memcpy(new->se_pids, sep->se_pids,
+ new->se_numchild * sizeof(*new->se_pids));
+ }
+ SWAP(pid_t *, sep->se_pids, new->se_pids);
+ sep->se_maxchild = new->se_maxchild;
+ sep->se_numchild = new->se_numchild;
+ sep->se_maxcpm = new->se_maxcpm;
+ resize_conn(sep, new->se_maxperip);
+ sep->se_maxperip = new->se_maxperip;
+ sep->se_bi = new->se_bi;
+ /* might need to turn on or off service now */
+ if (sep->se_fd >= 0) {
+ if (sep->se_maxchild > 0
+ && sep->se_numchild == sep->se_maxchild) {
+ if (FD_ISSET(sep->se_fd, &allsock))
+ disable(sep);
+ } else {
+ if (!FD_ISSET(sep->se_fd, &allsock))
+ enable(sep);
+ }
+ }
+ sep->se_accept = new->se_accept;
+ SWAP(char *, sep->se_user, new->se_user);
+ SWAP(char *, sep->se_group, new->se_group);
+#ifdef LOGIN_CAP
+ SWAP(char *, sep->se_class, new->se_class);
+#endif
+ SWAP(char *, sep->se_server, new->se_server);
+ SWAP(char *, sep->se_server_name, new->se_server_name);
+ for (i = 0; i < MAXARGV; i++)
+ SWAP(char *, sep->se_argv[i], new->se_argv[i]);
+#ifdef IPSEC
+ SWAP(char *, sep->se_policy, new->se_policy);
+ ipsecsetup(sep);
+#endif
+ sigsetmask(omask);
+ freeconfig(new);
+ if (debug)
+ print_service("REDO", sep);
+ } else {
+ sep = enter(new);
+ if (debug)
+ print_service("ADD ", sep);
+ }
+ sep->se_checked = 1;
+ if (ISMUX(sep)) {
+ sep->se_fd = -1;
+ continue;
+ }
+ switch (sep->se_family) {
+ case AF_INET:
+ if (!v4bind_ok) {
+ sep->se_fd = -1;
+ continue;
+ }
+ break;
+#ifdef INET6
+ case AF_INET6:
+ if (!v6bind_ok) {
+ sep->se_fd = -1;
+ continue;
+ }
+ break;
+#endif
+ }
+ if (!sep->se_rpc) {
+ if (sep->se_family != AF_UNIX) {
+ sp = getservbyname(sep->se_service, sep->se_proto);
+ if (sp == 0) {
+ syslog(LOG_ERR, "%s/%s: unknown service",
+ sep->se_service, sep->se_proto);
+ sep->se_checked = 0;
+ continue;
+ }
+ }
+ switch (sep->se_family) {
+ case AF_INET:
+ if (sp->s_port != sep->se_ctrladdr4.sin_port) {
+ sep->se_ctrladdr4.sin_port =
+ sp->s_port;
+ sep->se_reset = 1;
+ }
+ break;
+#ifdef INET6
+ case AF_INET6:
+ if (sp->s_port !=
+ sep->se_ctrladdr6.sin6_port) {
+ sep->se_ctrladdr6.sin6_port =
+ sp->s_port;
+ sep->se_reset = 1;
+ }
+ break;
+#endif
+ }
+ if (sep->se_reset != 0 && sep->se_fd >= 0)
+ close_sep(sep);
+ } else {
+ rpc = getrpcbyname(sep->se_service);
+ if (rpc == 0) {
+ syslog(LOG_ERR, "%s/%s unknown RPC service",
+ sep->se_service, sep->se_proto);
+ if (sep->se_fd != -1)
+ (void) close(sep->se_fd);
+ sep->se_fd = -1;
+ continue;
+ }
+ if (sep->se_reset != 0 ||
+ rpc->r_number != sep->se_rpc_prog) {
+ if (sep->se_rpc_prog)
+ unregisterrpc(sep);
+ sep->se_rpc_prog = rpc->r_number;
+ if (sep->se_fd != -1)
+ (void) close(sep->se_fd);
+ sep->se_fd = -1;
+ }
+ sep->se_nomapped = new_nomapped;
+ }
+ sep->se_reset = 0;
+ if (sep->se_fd == -1)
+ setup(sep);
+ }
+ endconfig();
+ /*
+ * Purge anything not looked at above.
+ */
+ omask = sigblock(SIGBLOCK);
+ sepp = &servtab;
+ while ((sep = *sepp)) {
+ if (sep->se_checked) {
+ sepp = &sep->se_next;
+ continue;
+ }
+ *sepp = sep->se_next;
+ if (sep->se_fd >= 0)
+ close_sep(sep);
+ if (debug)
+ print_service("FREE", sep);
+ if (sep->se_rpc && sep->se_rpc_prog > 0)
+ unregisterrpc(sep);
+ freeconfig(sep);
+ free(sep);
+ }
+ (void) sigsetmask(omask);
+}
+
+void
+unregisterrpc(struct servtab *sep)
+{
+ u_int i;
+ struct servtab *sepp;
+ long omask;
+ struct netconfig *netid4, *netid6;
+
+ omask = sigblock(SIGBLOCK);
+ netid4 = sep->se_socktype == SOCK_DGRAM ? udpconf : tcpconf;
+ netid6 = sep->se_socktype == SOCK_DGRAM ? udp6conf : tcp6conf;
+ if (sep->se_family == AF_INET)
+ netid6 = NULL;
+ else if (sep->se_nomapped)
+ netid4 = NULL;
+ /*
+ * Conflict if same prog and protocol - In that case one should look
+ * to versions, but it is not interesting: having separate servers for
+ * different versions does not work well.
+ * Therefore one do not unregister if there is a conflict.
+ * There is also transport conflict if destroying INET when INET46
+ * exists, or destroying INET46 when INET exists
+ */
+ for (sepp = servtab; sepp; sepp = sepp->se_next) {
+ if (sepp == sep)
+ continue;
+ if (sepp->se_checked == 0 ||
+ !sepp->se_rpc ||
+ strcmp(sep->se_proto, sepp->se_proto) != 0 ||
+ sep->se_rpc_prog != sepp->se_rpc_prog)
+ continue;
+ if (sepp->se_family == AF_INET)
+ netid4 = NULL;
+ if (sepp->se_family == AF_INET6) {
+ netid6 = NULL;
+ if (!sep->se_nomapped)
+ netid4 = NULL;
+ }
+ if (netid4 == NULL && netid6 == NULL)
+ return;
+ }
+ if (debug)
+ print_service("UNREG", sep);
+ for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) {
+ if (netid4)
+ rpcb_unset(sep->se_rpc_prog, i, netid4);
+ if (netid6)
+ rpcb_unset(sep->se_rpc_prog, i, netid6);
+ }
+ if (sep->se_fd != -1)
+ (void) close(sep->se_fd);
+ sep->se_fd = -1;
+ (void) sigsetmask(omask);
+}
+
+void
+flag_retry(int signo __unused)
+{
+ flag_signal('A');
+}
+
+void
+retry(void)
+{
+ struct servtab *sep;
+
+ timingout = 0;
+ for (sep = servtab; sep; sep = sep->se_next)
+ if (sep->se_fd == -1 && !ISMUX(sep))
+ setup(sep);
+}
+
+void
+setup(struct servtab *sep)
+{
+ int on = 1;
+
+ if ((sep->se_fd = socket(sep->se_family, sep->se_socktype, 0)) < 0) {
+ if (debug)
+ warn("socket failed on %s/%s",
+ sep->se_service, sep->se_proto);
+ syslog(LOG_ERR, "%s/%s: socket: %m",
+ sep->se_service, sep->se_proto);
+ return;
+ }
+ /* Set all listening sockets to close-on-exec. */
+ if (fcntl(sep->se_fd, F_SETFD, FD_CLOEXEC) < 0) {
+ syslog(LOG_ERR, "%s/%s: fcntl (F_SETFD, FD_CLOEXEC): %m",
+ sep->se_service, sep->se_proto);
+ close(sep->se_fd);
+ return;
+ }
+#define turnon(fd, opt) \
+setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
+ if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
+ turnon(sep->se_fd, SO_DEBUG) < 0)
+ syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
+ if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
+ syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
+#ifdef SO_PRIVSTATE
+ if (turnon(sep->se_fd, SO_PRIVSTATE) < 0)
+ syslog(LOG_ERR, "setsockopt (SO_PRIVSTATE): %m");
+#endif
+ /* tftpd opens a new connection then needs more infos */
+ if ((sep->se_family == AF_INET6) &&
+ (strcmp(sep->se_proto, "udp") == 0) &&
+ (sep->se_accept == 0) &&
+ (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_RECVPKTINFO,
+ (char *)&on, sizeof (on)) < 0))
+ syslog(LOG_ERR, "setsockopt (IPV6_RECVPKTINFO): %m");
+ if (sep->se_family == AF_INET6) {
+ int flag = sep->se_nomapped ? 1 : 0;
+ if (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_V6ONLY,
+ (char *)&flag, sizeof (flag)) < 0)
+ syslog(LOG_ERR, "setsockopt (IPV6_V6ONLY): %m");
+ }
+#undef turnon
+#ifdef IPV6_FAITH
+ if (sep->se_type == FAITH_TYPE) {
+ if (setsockopt(sep->se_fd, IPPROTO_IPV6, IPV6_FAITH, &on,
+ sizeof(on)) < 0) {
+ syslog(LOG_ERR, "setsockopt (IPV6_FAITH): %m");
+ }
+ }
+#endif
+#ifdef IPSEC
+ ipsecsetup(sep);
+#endif
+ if (sep->se_family == AF_UNIX) {
+ (void) unlink(sep->se_ctrladdr_un.sun_path);
+ umask(0777); /* Make socket with conservative permissions */
+ }
+ if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr,
+ sep->se_ctrladdr_size) < 0) {
+ if (debug)
+ warn("bind failed on %s/%s",
+ sep->se_service, sep->se_proto);
+ syslog(LOG_ERR, "%s/%s: bind: %m",
+ sep->se_service, sep->se_proto);
+ (void) close(sep->se_fd);
+ sep->se_fd = -1;
+ if (!timingout) {
+ timingout = 1;
+ alarm(RETRYTIME);
+ }
+ if (sep->se_family == AF_UNIX)
+ umask(mask);
+ return;
+ }
+ if (sep->se_family == AF_UNIX) {
+ /* Ick - fch{own,mod} don't work on Unix domain sockets */
+ if (chown(sep->se_service, sep->se_sockuid, sep->se_sockgid) < 0)
+ syslog(LOG_ERR, "chown socket: %m");
+ if (chmod(sep->se_service, sep->se_sockmode) < 0)
+ syslog(LOG_ERR, "chmod socket: %m");
+ umask(mask);
+ }
+ if (sep->se_rpc) {
+ u_int i;
+ socklen_t len = sep->se_ctrladdr_size;
+ struct netconfig *netid, *netid2 = NULL;
+ struct sockaddr_in sock;
+ struct netbuf nbuf, nbuf2;
+
+ if (getsockname(sep->se_fd,
+ (struct sockaddr*)&sep->se_ctrladdr, &len) < 0){
+ syslog(LOG_ERR, "%s/%s: getsockname: %m",
+ sep->se_service, sep->se_proto);
+ (void) close(sep->se_fd);
+ sep->se_fd = -1;
+ return;
+ }
+ nbuf.buf = &sep->se_ctrladdr;
+ nbuf.len = sep->se_ctrladdr.sa_len;
+ if (sep->se_family == AF_INET)
+ netid = sep->se_socktype==SOCK_DGRAM? udpconf:tcpconf;
+ else {
+ netid = sep->se_socktype==SOCK_DGRAM? udp6conf:tcp6conf;
+ if (!sep->se_nomapped) { /* INET and INET6 */
+ netid2 = netid==udp6conf? udpconf:tcpconf;
+ memset(&sock, 0, sizeof sock); /* ADDR_ANY */
+ nbuf2.buf = &sock;
+ nbuf2.len = sock.sin_len = sizeof sock;
+ sock.sin_family = AF_INET;
+ sock.sin_port = sep->se_ctrladdr6.sin6_port;
+ }
+ }
+ if (debug)
+ print_service("REG ", sep);
+ for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) {
+ rpcb_unset(sep->se_rpc_prog, i, netid);
+ rpcb_set(sep->se_rpc_prog, i, netid, &nbuf);
+ if (netid2) {
+ rpcb_unset(sep->se_rpc_prog, i, netid2);
+ rpcb_set(sep->se_rpc_prog, i, netid2, &nbuf2);
+ }
+ }
+ }
+ if (sep->se_socktype == SOCK_STREAM)
+ listen(sep->se_fd, 64);
+ enable(sep);
+ if (debug) {
+ warnx("registered %s on %d",
+ sep->se_server, sep->se_fd);
+ }
+}
+
+#ifdef IPSEC
+void
+ipsecsetup(sep)
+ struct servtab *sep;
+{
+ char *buf;
+ char *policy_in = NULL;
+ char *policy_out = NULL;
+ int level;
+ int opt;
+
+ switch (sep->se_family) {
+ case AF_INET:
+ level = IPPROTO_IP;
+ opt = IP_IPSEC_POLICY;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ level = IPPROTO_IPV6;
+ opt = IPV6_IPSEC_POLICY;
+ break;
+#endif
+ default:
+ return;
+ }
+
+ if (!sep->se_policy || sep->se_policy[0] == '\0') {
+ static char def_in[] = "in entrust", def_out[] = "out entrust";
+ policy_in = def_in;
+ policy_out = def_out;
+ } else {
+ if (!strncmp("in", sep->se_policy, 2))
+ policy_in = sep->se_policy;
+ else if (!strncmp("out", sep->se_policy, 3))
+ policy_out = sep->se_policy;
+ else {
+ syslog(LOG_ERR, "invalid security policy \"%s\"",
+ sep->se_policy);
+ return;
+ }
+ }
+
+ if (policy_in != NULL) {
+ buf = ipsec_set_policy(policy_in, strlen(policy_in));
+ if (buf != NULL) {
+ if (setsockopt(sep->se_fd, level, opt,
+ buf, ipsec_get_policylen(buf)) < 0 &&
+ debug != 0)
+ warnx("%s/%s: ipsec initialization failed; %s",
+ sep->se_service, sep->se_proto,
+ policy_in);
+ free(buf);
+ } else
+ syslog(LOG_ERR, "invalid security policy \"%s\"",
+ policy_in);
+ }
+ if (policy_out != NULL) {
+ buf = ipsec_set_policy(policy_out, strlen(policy_out));
+ if (buf != NULL) {
+ if (setsockopt(sep->se_fd, level, opt,
+ buf, ipsec_get_policylen(buf)) < 0 &&
+ debug != 0)
+ warnx("%s/%s: ipsec initialization failed; %s",
+ sep->se_service, sep->se_proto,
+ policy_out);
+ free(buf);
+ } else
+ syslog(LOG_ERR, "invalid security policy \"%s\"",
+ policy_out);
+ }
+}
+#endif
+
+/*
+ * Finish with a service and its socket.
+ */
+void
+close_sep(struct servtab *sep)
+{
+ if (sep->se_fd >= 0) {
+ if (FD_ISSET(sep->se_fd, &allsock))
+ disable(sep);
+ (void) close(sep->se_fd);
+ sep->se_fd = -1;
+ }
+ sep->se_count = 0;
+ sep->se_numchild = 0; /* forget about any existing children */
+}
+
+int
+matchservent(const char *name1, const char *name2, const char *proto)
+{
+ char **alias, *p;
+ struct servent *se;
+
+ if (strcmp(proto, "unix") == 0) {
+ if ((p = strrchr(name1, '/')) != NULL)
+ name1 = p + 1;
+ if ((p = strrchr(name2, '/')) != NULL)
+ name2 = p + 1;
+ }
+ if (strcmp(name1, name2) == 0)
+ return(1);
+ if ((se = getservbyname(name1, proto)) != NULL) {
+ if (strcmp(name2, se->s_name) == 0)
+ return(1);
+ for (alias = se->s_aliases; *alias; alias++)
+ if (strcmp(name2, *alias) == 0)
+ return(1);
+ }
+ return(0);
+}
+
+struct servtab *
+enter(struct servtab *cp)
+{
+ struct servtab *sep;
+ long omask;
+
+ sep = (struct servtab *)malloc(sizeof (*sep));
+ if (sep == (struct servtab *)0) {
+ syslog(LOG_ERR, "malloc: %m");
+ exit(EX_OSERR);
+ }
+ *sep = *cp;
+ sep->se_fd = -1;
+ omask = sigblock(SIGBLOCK);
+ sep->se_next = servtab;
+ servtab = sep;
+ sigsetmask(omask);
+ return (sep);
+}
+
+void
+enable(struct servtab *sep)
+{
+ if (debug)
+ warnx(
+ "enabling %s, fd %d", sep->se_service, sep->se_fd);
+#ifdef SANITY_CHECK
+ if (sep->se_fd < 0) {
+ syslog(LOG_ERR,
+ "%s: %s: bad fd", __func__, sep->se_service);
+ exit(EX_SOFTWARE);
+ }
+ if (ISMUX(sep)) {
+ syslog(LOG_ERR,
+ "%s: %s: is mux", __func__, sep->se_service);
+ exit(EX_SOFTWARE);
+ }
+ if (FD_ISSET(sep->se_fd, &allsock)) {
+ syslog(LOG_ERR,
+ "%s: %s: not off", __func__, sep->se_service);
+ exit(EX_SOFTWARE);
+ }
+ nsock++;
+#endif
+ FD_SET(sep->se_fd, &allsock);
+ if (sep->se_fd > maxsock)
+ maxsock = sep->se_fd;
+}
+
+void
+disable(struct servtab *sep)
+{
+ if (debug)
+ warnx(
+ "disabling %s, fd %d", sep->se_service, sep->se_fd);
+#ifdef SANITY_CHECK
+ if (sep->se_fd < 0) {
+ syslog(LOG_ERR,
+ "%s: %s: bad fd", __func__, sep->se_service);
+ exit(EX_SOFTWARE);
+ }
+ if (ISMUX(sep)) {
+ syslog(LOG_ERR,
+ "%s: %s: is mux", __func__, sep->se_service);
+ exit(EX_SOFTWARE);
+ }
+ if (!FD_ISSET(sep->se_fd, &allsock)) {
+ syslog(LOG_ERR,
+ "%s: %s: not on", __func__, sep->se_service);
+ exit(EX_SOFTWARE);
+ }
+ if (nsock == 0) {
+ syslog(LOG_ERR, "%s: nsock=0", __func__);
+ exit(EX_SOFTWARE);
+ }
+ nsock--;
+#endif
+ FD_CLR(sep->se_fd, &allsock);
+ if (sep->se_fd == maxsock)
+ maxsock--;
+}
+
+FILE *fconfig = NULL;
+struct servtab serv;
+char line[LINE_MAX];
+
+int
+setconfig(void)
+{
+
+ if (fconfig != NULL) {
+ fseek(fconfig, 0L, SEEK_SET);
+ return (1);
+ }
+ fconfig = fopen(CONFIG, "r");
+ return (fconfig != NULL);
+}
+
+void
+endconfig(void)
+{
+ if (fconfig) {
+ (void) fclose(fconfig);
+ fconfig = NULL;
+ }
+}
+
+struct servtab *
+getconfigent(void)
+{
+ struct servtab *sep = &serv;
+ int argc;
+ char *cp, *arg, *s;
+ char *versp;
+ static char TCPMUX_TOKEN[] = "tcpmux/";
+#define MUX_LEN (sizeof(TCPMUX_TOKEN)-1)
+#ifdef IPSEC
+ char *policy;
+#endif
+ int v4bind;
+#ifdef INET6
+ int v6bind;
+#endif
+ int i;
+
+#ifdef IPSEC
+ policy = NULL;
+#endif
+more:
+ v4bind = 0;
+#ifdef INET6
+ v6bind = 0;
+#endif
+ while ((cp = nextline(fconfig)) != NULL) {
+#ifdef IPSEC
+ /* lines starting with #@ is not a comment, but the policy */
+ if (cp[0] == '#' && cp[1] == '@') {
+ char *p;
+ for (p = cp + 2; p && *p && isspace(*p); p++)
+ ;
+ if (*p == '\0') {
+ if (policy)
+ free(policy);
+ policy = NULL;
+ } else if (ipsec_get_policylen(p) >= 0) {
+ if (policy)
+ free(policy);
+ policy = newstr(p);
+ } else {
+ syslog(LOG_ERR,
+ "%s: invalid ipsec policy \"%s\"",
+ CONFIG, p);
+ exit(EX_CONFIG);
+ }
+ }
+#endif
+ if (*cp == '#' || *cp == '\0')
+ continue;
+ break;
+ }
+ if (cp == NULL)
+ return ((struct servtab *)0);
+ /*
+ * clear the static buffer, since some fields (se_ctrladdr,
+ * for example) don't get initialized here.
+ */
+ memset(sep, 0, sizeof *sep);
+ arg = skip(&cp);
+ if (cp == NULL) {
+ /* got an empty line containing just blanks/tabs. */
+ goto more;
+ }
+ if (arg[0] == ':') { /* :user:group:perm: */
+ char *user, *group, *perm;
+ struct passwd *pw;
+ struct group *gr;
+ user = arg+1;
+ if ((group = strchr(user, ':')) == NULL) {
+ syslog(LOG_ERR, "no group after user '%s'", user);
+ goto more;
+ }
+ *group++ = '\0';
+ if ((perm = strchr(group, ':')) == NULL) {
+ syslog(LOG_ERR, "no mode after group '%s'", group);
+ goto more;
+ }
+ *perm++ = '\0';
+ if ((pw = getpwnam(user)) == NULL) {
+ syslog(LOG_ERR, "no such user '%s'", user);
+ goto more;
+ }
+ sep->se_sockuid = pw->pw_uid;
+ if ((gr = getgrnam(group)) == NULL) {
+ syslog(LOG_ERR, "no such user '%s'", group);
+ goto more;
+ }
+ sep->se_sockgid = gr->gr_gid;
+ sep->se_sockmode = strtol(perm, &arg, 8);
+ if (*arg != ':') {
+ syslog(LOG_ERR, "bad mode '%s'", perm);
+ goto more;
+ }
+ *arg++ = '\0';
+ } else {
+ sep->se_sockuid = euid;
+ sep->se_sockgid = egid;
+ sep->se_sockmode = 0200;
+ }
+ if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) {
+ char *c = arg + MUX_LEN;
+ if (*c == '+') {
+ sep->se_type = MUXPLUS_TYPE;
+ c++;
+ } else
+ sep->se_type = MUX_TYPE;
+ sep->se_service = newstr(c);
+ } else {
+ sep->se_service = newstr(arg);
+ sep->se_type = NORM_TYPE;
+ }
+ arg = sskip(&cp);
+ if (strcmp(arg, "stream") == 0)
+ sep->se_socktype = SOCK_STREAM;
+ else if (strcmp(arg, "dgram") == 0)
+ sep->se_socktype = SOCK_DGRAM;
+ else if (strcmp(arg, "rdm") == 0)
+ sep->se_socktype = SOCK_RDM;
+ else if (strcmp(arg, "seqpacket") == 0)
+ sep->se_socktype = SOCK_SEQPACKET;
+ else if (strcmp(arg, "raw") == 0)
+ sep->se_socktype = SOCK_RAW;
+ else
+ sep->se_socktype = -1;
+
+ arg = sskip(&cp);
+ if (strncmp(arg, "tcp", 3) == 0) {
+ sep->se_proto = newstr(strsep(&arg, "/"));
+ if (arg != NULL) {
+ if (strcmp(arg, "faith") == 0)
+ sep->se_type = FAITH_TYPE;
+ }
+ } else {
+ if (sep->se_type == NORM_TYPE &&
+ strncmp(arg, "faith/", 6) == 0) {
+ arg += 6;
+ sep->se_type = FAITH_TYPE;
+ }
+ sep->se_proto = newstr(arg);
+ }
+ if (strncmp(sep->se_proto, "rpc/", 4) == 0) {
+ memmove(sep->se_proto, sep->se_proto + 4,
+ strlen(sep->se_proto) + 1 - 4);
+ sep->se_rpc = 1;
+ sep->se_rpc_prog = sep->se_rpc_lowvers =
+ sep->se_rpc_lowvers = 0;
+ memcpy(&sep->se_ctrladdr4, bind_sa4,
+ sizeof(sep->se_ctrladdr4));
+ if ((versp = rindex(sep->se_service, '/'))) {
+ *versp++ = '\0';
+ switch (sscanf(versp, "%u-%u",
+ &sep->se_rpc_lowvers,
+ &sep->se_rpc_highvers)) {
+ case 2:
+ break;
+ case 1:
+ sep->se_rpc_highvers =
+ sep->se_rpc_lowvers;
+ break;
+ default:
+ syslog(LOG_ERR,
+ "bad RPC version specifier; %s",
+ sep->se_service);
+ freeconfig(sep);
+ goto more;
+ }
+ }
+ else {
+ sep->se_rpc_lowvers =
+ sep->se_rpc_highvers = 1;
+ }
+ }
+ sep->se_nomapped = 0;
+ if (strcmp(sep->se_proto, "unix") == 0) {
+ sep->se_family = AF_UNIX;
+ } else {
+ while (isdigit(sep->se_proto[strlen(sep->se_proto) - 1])) {
+#ifdef INET6
+ if (sep->se_proto[strlen(sep->se_proto) - 1] == '6') {
+ sep->se_proto[strlen(sep->se_proto) - 1] = '\0';
+ v6bind = 1;
+ continue;
+ }
+#endif
+ if (sep->se_proto[strlen(sep->se_proto) - 1] == '4') {
+ sep->se_proto[strlen(sep->se_proto) - 1] = '\0';
+ v4bind = 1;
+ continue;
+ }
+ /* illegal version num */
+ syslog(LOG_ERR, "bad IP version for %s", sep->se_proto);
+ freeconfig(sep);
+ goto more;
+ }
+#ifdef INET6
+ if (v6bind && !v6bind_ok) {
+ syslog(LOG_INFO, "IPv6 bind is ignored for %s",
+ sep->se_service);
+ if (v4bind && v4bind_ok)
+ v6bind = 0;
+ else {
+ freeconfig(sep);
+ goto more;
+ }
+ }
+ if (v6bind) {
+ sep->se_family = AF_INET6;
+ if (!v4bind || !v4bind_ok)
+ sep->se_nomapped = 1;
+ } else
+#endif
+ { /* default to v4 bind if not v6 bind */
+ if (!v4bind_ok) {
+ syslog(LOG_NOTICE, "IPv4 bind is ignored for %s",
+ sep->se_service);
+ freeconfig(sep);
+ goto more;
+ }
+ sep->se_family = AF_INET;
+ }
+ }
+ /* init ctladdr */
+ switch(sep->se_family) {
+ case AF_INET:
+ memcpy(&sep->se_ctrladdr4, bind_sa4,
+ sizeof(sep->se_ctrladdr4));
+ sep->se_ctrladdr_size = sizeof(sep->se_ctrladdr4);
+ break;
+#ifdef INET6
+ case AF_INET6:
+ memcpy(&sep->se_ctrladdr6, bind_sa6,
+ sizeof(sep->se_ctrladdr6));
+ sep->se_ctrladdr_size = sizeof(sep->se_ctrladdr6);
+ break;
+#endif
+ case AF_UNIX:
+ if (strlen(sep->se_service) >= sizeof(sep->se_ctrladdr_un.sun_path)) {
+ syslog(LOG_ERR,
+ "domain socket pathname too long for service %s",
+ sep->se_service);
+ goto more;
+ }
+ memset(&sep->se_ctrladdr, 0, sizeof(sep->se_ctrladdr));
+ sep->se_ctrladdr_un.sun_family = sep->se_family;
+ sep->se_ctrladdr_un.sun_len = strlen(sep->se_service);
+ strcpy(sep->se_ctrladdr_un.sun_path, sep->se_service);
+ sep->se_ctrladdr_size = SUN_LEN(&sep->se_ctrladdr_un);
+ }
+ arg = sskip(&cp);
+ if (!strncmp(arg, "wait", 4))
+ sep->se_accept = 0;
+ else if (!strncmp(arg, "nowait", 6))
+ sep->se_accept = 1;
+ else {
+ syslog(LOG_ERR,
+ "%s: bad wait/nowait for service %s",
+ CONFIG, sep->se_service);
+ goto more;
+ }
+ sep->se_maxchild = -1;
+ sep->se_maxcpm = -1;
+ sep->se_maxperip = -1;
+ if ((s = strchr(arg, '/')) != NULL) {
+ char *eptr;
+ u_long val;
+
+ val = strtoul(s + 1, &eptr, 10);
+ if (eptr == s + 1 || val > MAX_MAXCHLD) {
+ syslog(LOG_ERR,
+ "%s: bad max-child for service %s",
+ CONFIG, sep->se_service);
+ goto more;
+ }
+ if (debug)
+ if (!sep->se_accept && val != 1)
+ warnx("maxchild=%lu for wait service %s"
+ " not recommended", val, sep->se_service);
+ sep->se_maxchild = val;
+ if (*eptr == '/')
+ sep->se_maxcpm = strtol(eptr + 1, &eptr, 10);
+ if (*eptr == '/')
+ sep->se_maxperip = strtol(eptr + 1, &eptr, 10);
+ /*
+ * explicitly do not check for \0 for future expansion /
+ * backwards compatibility
+ */
+ }
+ if (ISMUX(sep)) {
+ /*
+ * Silently enforce "nowait" mode for TCPMUX services
+ * since they don't have an assigned port to listen on.
+ */
+ sep->se_accept = 1;
+ if (strcmp(sep->se_proto, "tcp")) {
+ syslog(LOG_ERR,
+ "%s: bad protocol for tcpmux service %s",
+ CONFIG, sep->se_service);
+ goto more;
+ }
+ if (sep->se_socktype != SOCK_STREAM) {
+ syslog(LOG_ERR,
+ "%s: bad socket type for tcpmux service %s",
+ CONFIG, sep->se_service);
+ goto more;
+ }
+ }
+ sep->se_user = newstr(sskip(&cp));
+#ifdef LOGIN_CAP
+ if ((s = strrchr(sep->se_user, '/')) != NULL) {
+ *s = '\0';
+ sep->se_class = newstr(s + 1);
+ } else
+ sep->se_class = newstr(RESOURCE_RC);
+#endif
+ if ((s = strrchr(sep->se_user, ':')) != NULL) {
+ *s = '\0';
+ sep->se_group = newstr(s + 1);
+ } else
+ sep->se_group = NULL;
+ sep->se_server = newstr(sskip(&cp));
+ if ((sep->se_server_name = rindex(sep->se_server, '/')))
+ sep->se_server_name++;
+ if (strcmp(sep->se_server, "internal") == 0) {
+ struct biltin *bi;
+
+ for (bi = biltins; bi->bi_service; bi++)
+ if (bi->bi_socktype == sep->se_socktype &&
+ matchservent(bi->bi_service, sep->se_service,
+ sep->se_proto))
+ break;
+ if (bi->bi_service == 0) {
+ syslog(LOG_ERR, "internal service %s unknown",
+ sep->se_service);
+ goto more;
+ }
+ sep->se_accept = 1; /* force accept mode for built-ins */
+ sep->se_bi = bi;
+ } else
+ sep->se_bi = NULL;
+ if (sep->se_maxperip < 0)
+ sep->se_maxperip = maxperip;
+ if (sep->se_maxcpm < 0)
+ sep->se_maxcpm = maxcpm;
+ if (sep->se_maxchild < 0) { /* apply default max-children */
+ if (sep->se_bi && sep->se_bi->bi_maxchild >= 0)
+ sep->se_maxchild = sep->se_bi->bi_maxchild;
+ else if (sep->se_accept)
+ sep->se_maxchild = maxchild > 0 ? maxchild : 0;
+ else
+ sep->se_maxchild = 1;
+ }
+ if (sep->se_maxchild > 0) {
+ sep->se_pids = malloc(sep->se_maxchild * sizeof(*sep->se_pids));
+ if (sep->se_pids == NULL) {
+ syslog(LOG_ERR, "malloc: %m");
+ exit(EX_OSERR);
+ }
+ }
+ argc = 0;
+ for (arg = skip(&cp); cp; arg = skip(&cp))
+ if (argc < MAXARGV) {
+ sep->se_argv[argc++] = newstr(arg);
+ } else {
+ syslog(LOG_ERR,
+ "%s: too many arguments for service %s",
+ CONFIG, sep->se_service);
+ goto more;
+ }
+ while (argc <= MAXARGV)
+ sep->se_argv[argc++] = NULL;
+ for (i = 0; i < PERIPSIZE; ++i)
+ LIST_INIT(&sep->se_conn[i]);
+#ifdef IPSEC
+ sep->se_policy = policy ? newstr(policy) : NULL;
+#endif
+ return (sep);
+}
+
+void
+freeconfig(struct servtab *cp)
+{
+ int i;
+
+ if (cp->se_service)
+ free(cp->se_service);
+ if (cp->se_proto)
+ free(cp->se_proto);
+ if (cp->se_user)
+ free(cp->se_user);
+ if (cp->se_group)
+ free(cp->se_group);
+#ifdef LOGIN_CAP
+ if (cp->se_class)
+ free(cp->se_class);
+#endif
+ if (cp->se_server)
+ free(cp->se_server);
+ if (cp->se_pids)
+ free(cp->se_pids);
+ for (i = 0; i < MAXARGV; i++)
+ if (cp->se_argv[i])
+ free(cp->se_argv[i]);
+ free_connlist(cp);
+#ifdef IPSEC
+ if (cp->se_policy)
+ free(cp->se_policy);
+#endif
+}
+
+
+/*
+ * Safe skip - if skip returns null, log a syntax error in the
+ * configuration file and exit.
+ */
+char *
+sskip(char **cpp)
+{
+ char *cp;
+
+ cp = skip(cpp);
+ if (cp == NULL) {
+ syslog(LOG_ERR, "%s: syntax error", CONFIG);
+ exit(EX_DATAERR);
+ }
+ return (cp);
+}
+
+char *
+skip(char **cpp)
+{
+ char *cp = *cpp;
+ char *start;
+ char quote = '\0';
+
+again:
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+ if (*cp == '\0') {
+ int c;
+
+ c = getc(fconfig);
+ (void) ungetc(c, fconfig);
+ if (c == ' ' || c == '\t')
+ if ((cp = nextline(fconfig)))
+ goto again;
+ *cpp = (char *)0;
+ return ((char *)0);
+ }
+ if (*cp == '"' || *cp == '\'')
+ quote = *cp++;
+ start = cp;
+ if (quote)
+ while (*cp && *cp != quote)
+ cp++;
+ else
+ while (*cp && *cp != ' ' && *cp != '\t')
+ cp++;
+ if (*cp != '\0')
+ *cp++ = '\0';
+ *cpp = cp;
+ return (start);
+}
+
+char *
+nextline(FILE *fd)
+{
+ char *cp;
+
+ if (fgets(line, sizeof (line), fd) == NULL)
+ return ((char *)0);
+ cp = strchr(line, '\n');
+ if (cp)
+ *cp = '\0';
+ return (line);
+}
+
+char *
+newstr(const char *cp)
+{
+ char *cr;
+
+ if ((cr = strdup(cp != NULL ? cp : "")))
+ return (cr);
+ syslog(LOG_ERR, "strdup: %m");
+ exit(EX_OSERR);
+}
+
+void
+inetd_setproctitle(const char *a, int s)
+{
+ socklen_t size;
+ struct sockaddr_storage ss;
+ char buf[80], pbuf[INET6_ADDRSTRLEN];
+
+ size = sizeof(ss);
+ if (getpeername(s, (struct sockaddr *)&ss, &size) == 0) {
+ getnameinfo((struct sockaddr *)&ss, size, pbuf, sizeof(pbuf),
+ NULL, 0, NI_NUMERICHOST);
+ (void) sprintf(buf, "%s [%s]", a, pbuf);
+ } else
+ (void) sprintf(buf, "%s", a);
+ setproctitle("%s", buf);
+}
+
+int
+check_loop(const struct sockaddr *sa, const struct servtab *sep)
+{
+ struct servtab *se2;
+ char pname[INET6_ADDRSTRLEN];
+
+ for (se2 = servtab; se2; se2 = se2->se_next) {
+ if (!se2->se_bi || se2->se_socktype != SOCK_DGRAM)
+ continue;
+
+ switch (se2->se_family) {
+ case AF_INET:
+ if (((const struct sockaddr_in *)sa)->sin_port ==
+ se2->se_ctrladdr4.sin_port)
+ goto isloop;
+ continue;
+#ifdef INET6
+ case AF_INET6:
+ if (((const struct sockaddr_in *)sa)->sin_port ==
+ se2->se_ctrladdr4.sin_port)
+ goto isloop;
+ continue;
+#endif
+ default:
+ continue;
+ }
+ isloop:
+ getnameinfo(sa, sa->sa_len, pname, sizeof(pname), NULL, 0,
+ NI_NUMERICHOST);
+ syslog(LOG_WARNING, "%s/%s:%s/%s loop request REFUSED from %s",
+ sep->se_service, sep->se_proto,
+ se2->se_service, se2->se_proto,
+ pname);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * print_service:
+ * Dump relevant information to stderr
+ */
+void
+print_service(const char *action, const struct servtab *sep)
+{
+ fprintf(stderr,
+ "%s: %s proto=%s accept=%d max=%d user=%s group=%s"
+#ifdef LOGIN_CAP
+ "class=%s"
+#endif
+ " builtin=%p server=%s"
+#ifdef IPSEC
+ " policy=\"%s\""
+#endif
+ "\n",
+ action, sep->se_service, sep->se_proto,
+ sep->se_accept, sep->se_maxchild, sep->se_user, sep->se_group,
+#ifdef LOGIN_CAP
+ sep->se_class,
+#endif
+ (void *) sep->se_bi, sep->se_server
+#ifdef IPSEC
+ , (sep->se_policy ? sep->se_policy : "")
+#endif
+ );
+}
+
+#define CPMHSIZE 256
+#define CPMHMASK (CPMHSIZE-1)
+#define CHTGRAN 10
+#define CHTSIZE 6
+
+typedef struct CTime {
+ unsigned long ct_Ticks;
+ int ct_Count;
+} CTime;
+
+typedef struct CHash {
+ union {
+ struct in_addr c4_Addr;
+ struct in6_addr c6_Addr;
+ } cu_Addr;
+#define ch_Addr4 cu_Addr.c4_Addr
+#define ch_Addr6 cu_Addr.c6_Addr
+ int ch_Family;
+ time_t ch_LTime;
+ char *ch_Service;
+ CTime ch_Times[CHTSIZE];
+} CHash;
+
+CHash CHashAry[CPMHSIZE];
+
+int
+cpmip(const struct servtab *sep, int ctrl)
+{
+ struct sockaddr_storage rss;
+ socklen_t rssLen = sizeof(rss);
+ int r = 0;
+
+ /*
+ * If getpeername() fails, just let it through (if logging is
+ * enabled the condition is caught elsewhere)
+ */
+
+ if (sep->se_maxcpm > 0 &&
+ (sep->se_family == AF_INET || sep->se_family == AF_INET6) &&
+ getpeername(ctrl, (struct sockaddr *)&rss, &rssLen) == 0 ) {
+ time_t t = time(NULL);
+ int hv = 0xABC3D20F;
+ int i;
+ int cnt = 0;
+ CHash *chBest = NULL;
+ unsigned int ticks = t / CHTGRAN;
+ struct sockaddr_in *sin4;
+#ifdef INET6
+ struct sockaddr_in6 *sin6;
+#endif
+
+ sin4 = (struct sockaddr_in *)&rss;
+#ifdef INET6
+ sin6 = (struct sockaddr_in6 *)&rss;
+#endif
+ {
+ char *p;
+ int addrlen;
+
+ switch (rss.ss_family) {
+ case AF_INET:
+ p = (char *)&sin4->sin_addr;
+ addrlen = sizeof(struct in_addr);
+ break;
+#ifdef INET6
+ case AF_INET6:
+ p = (char *)&sin6->sin6_addr;
+ addrlen = sizeof(struct in6_addr);
+ break;
+#endif
+ default:
+ /* should not happen */
+ return -1;
+ }
+
+ for (i = 0; i < addrlen; ++i, ++p) {
+ hv = (hv << 5) ^ (hv >> 23) ^ *p;
+ }
+ hv = (hv ^ (hv >> 16));
+ }
+ for (i = 0; i < 5; ++i) {
+ CHash *ch = &CHashAry[(hv + i) & CPMHMASK];
+
+ if (rss.ss_family == AF_INET &&
+ ch->ch_Family == AF_INET &&
+ sin4->sin_addr.s_addr == ch->ch_Addr4.s_addr &&
+ ch->ch_Service && strcmp(sep->se_service,
+ ch->ch_Service) == 0) {
+ chBest = ch;
+ break;
+ }
+#ifdef INET6
+ if (rss.ss_family == AF_INET6 &&
+ ch->ch_Family == AF_INET6 &&
+ IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
+ &ch->ch_Addr6) != 0 &&
+ ch->ch_Service && strcmp(sep->se_service,
+ ch->ch_Service) == 0) {
+ chBest = ch;
+ break;
+ }
+#endif
+ if (chBest == NULL || ch->ch_LTime == 0 ||
+ ch->ch_LTime < chBest->ch_LTime) {
+ chBest = ch;
+ }
+ }
+ if ((rss.ss_family == AF_INET &&
+ (chBest->ch_Family != AF_INET ||
+ sin4->sin_addr.s_addr != chBest->ch_Addr4.s_addr)) ||
+ chBest->ch_Service == NULL ||
+ strcmp(sep->se_service, chBest->ch_Service) != 0) {
+ chBest->ch_Family = sin4->sin_family;
+ chBest->ch_Addr4 = sin4->sin_addr;
+ if (chBest->ch_Service)
+ free(chBest->ch_Service);
+ chBest->ch_Service = strdup(sep->se_service);
+ bzero(chBest->ch_Times, sizeof(chBest->ch_Times));
+ }
+#ifdef INET6
+ if ((rss.ss_family == AF_INET6 &&
+ (chBest->ch_Family != AF_INET6 ||
+ IN6_ARE_ADDR_EQUAL(&sin6->sin6_addr,
+ &chBest->ch_Addr6) == 0)) ||
+ chBest->ch_Service == NULL ||
+ strcmp(sep->se_service, chBest->ch_Service) != 0) {
+ chBest->ch_Family = sin6->sin6_family;
+ chBest->ch_Addr6 = sin6->sin6_addr;
+ if (chBest->ch_Service)
+ free(chBest->ch_Service);
+ chBest->ch_Service = strdup(sep->se_service);
+ bzero(chBest->ch_Times, sizeof(chBest->ch_Times));
+ }
+#endif
+ chBest->ch_LTime = t;
+ {
+ CTime *ct = &chBest->ch_Times[ticks % CHTSIZE];
+ if (ct->ct_Ticks != ticks) {
+ ct->ct_Ticks = ticks;
+ ct->ct_Count = 0;
+ }
+ ++ct->ct_Count;
+ }
+ for (i = 0; i < CHTSIZE; ++i) {
+ CTime *ct = &chBest->ch_Times[i];
+ if (ct->ct_Ticks <= ticks &&
+ ct->ct_Ticks >= ticks - CHTSIZE) {
+ cnt += ct->ct_Count;
+ }
+ }
+ if ((cnt * 60) / (CHTSIZE * CHTGRAN) > sep->se_maxcpm) {
+ char pname[INET6_ADDRSTRLEN];
+
+ getnameinfo((struct sockaddr *)&rss,
+ ((struct sockaddr *)&rss)->sa_len,
+ pname, sizeof(pname), NULL, 0,
+ NI_NUMERICHOST);
+ r = -1;
+ syslog(LOG_ERR,
+ "%s from %s exceeded counts/min (limit %d/min)",
+ sep->se_service, pname,
+ sep->se_maxcpm);
+ }
+ }
+ return(r);
+}
+
+static struct conninfo *
+search_conn(struct servtab *sep, int ctrl)
+{
+ struct sockaddr_storage ss;
+ socklen_t sslen = sizeof(ss);
+ struct conninfo *conn;
+ int hv;
+ char pname[NI_MAXHOST], pname2[NI_MAXHOST];
+
+ if (sep->se_maxperip <= 0)
+ return NULL;
+
+ /*
+ * If getpeername() fails, just let it through (if logging is
+ * enabled the condition is caught elsewhere)
+ */
+ if (getpeername(ctrl, (struct sockaddr *)&ss, &sslen) != 0)
+ return NULL;
+
+ switch (ss.ss_family) {
+ case AF_INET:
+ hv = hashval((char *)&((struct sockaddr_in *)&ss)->sin_addr,
+ sizeof(struct in_addr));
+ break;
+#ifdef INET6
+ case AF_INET6:
+ hv = hashval((char *)&((struct sockaddr_in6 *)&ss)->sin6_addr,
+ sizeof(struct in6_addr));
+ break;
+#endif
+ default:
+ /*
+ * Since we only support AF_INET and AF_INET6, just
+ * let other than AF_INET and AF_INET6 through.
+ */
+ return NULL;
+ }
+
+ if (getnameinfo((struct sockaddr *)&ss, sslen, pname, sizeof(pname),
+ NULL, 0, NI_NUMERICHOST) != 0)
+ return NULL;
+
+ LIST_FOREACH(conn, &sep->se_conn[hv], co_link) {
+ if (getnameinfo((struct sockaddr *)&conn->co_addr,
+ conn->co_addr.ss_len, pname2, sizeof(pname2), NULL, 0,
+ NI_NUMERICHOST) == 0 &&
+ strcmp(pname, pname2) == 0)
+ break;
+ }
+
+ if (conn == NULL) {
+ if ((conn = malloc(sizeof(struct conninfo))) == NULL) {
+ syslog(LOG_ERR, "malloc: %m");
+ exit(EX_OSERR);
+ }
+ conn->co_proc = malloc(sep->se_maxperip * sizeof(*conn->co_proc));
+ if (conn->co_proc == NULL) {
+ syslog(LOG_ERR, "malloc: %m");
+ exit(EX_OSERR);
+ }
+ memcpy(&conn->co_addr, (struct sockaddr *)&ss, sslen);
+ conn->co_numchild = 0;
+ LIST_INSERT_HEAD(&sep->se_conn[hv], conn, co_link);
+ }
+
+ /*
+ * Since a child process is not invoked yet, we cannot
+ * determine a pid of a child. So, co_proc and co_numchild
+ * should be filled leter.
+ */
+
+ return conn;
+}
+
+static int
+room_conn(struct servtab *sep, struct conninfo *conn)
+{
+ char pname[NI_MAXHOST];
+
+ if (conn->co_numchild >= sep->se_maxperip) {
+ getnameinfo((struct sockaddr *)&conn->co_addr,
+ conn->co_addr.ss_len, pname, sizeof(pname), NULL, 0,
+ NI_NUMERICHOST);
+ syslog(LOG_ERR, "%s from %s exceeded counts (limit %d)",
+ sep->se_service, pname, sep->se_maxperip);
+ return 0;
+ }
+ return 1;
+}
+
+static void
+addchild_conn(struct conninfo *conn, pid_t pid)
+{
+ struct procinfo *proc;
+
+ if (conn == NULL)
+ return;
+
+ if ((proc = search_proc(pid, 1)) != NULL) {
+ if (proc->pr_conn != NULL) {
+ syslog(LOG_ERR,
+ "addchild_conn: child already on process list");
+ exit(EX_OSERR);
+ }
+ proc->pr_conn = conn;
+ }
+
+ conn->co_proc[conn->co_numchild++] = proc;
+}
+
+static void
+reapchild_conn(pid_t pid)
+{
+ struct procinfo *proc;
+ struct conninfo *conn;
+ int i;
+
+ if ((proc = search_proc(pid, 0)) == NULL)
+ return;
+ if ((conn = proc->pr_conn) == NULL)
+ return;
+ for (i = 0; i < conn->co_numchild; ++i)
+ if (conn->co_proc[i] == proc) {
+ conn->co_proc[i] = conn->co_proc[--conn->co_numchild];
+ break;
+ }
+ free_proc(proc);
+ free_conn(conn);
+}
+
+static void
+resize_conn(struct servtab *sep, int maxpip)
+{
+ struct conninfo *conn;
+ int i, j;
+
+ if (sep->se_maxperip <= 0)
+ return;
+ if (maxpip <= 0) {
+ free_connlist(sep);
+ return;
+ }
+ for (i = 0; i < PERIPSIZE; ++i) {
+ LIST_FOREACH(conn, &sep->se_conn[i], co_link) {
+ for (j = maxpip; j < conn->co_numchild; ++j)
+ free_proc(conn->co_proc[j]);
+ conn->co_proc = realloc(conn->co_proc,
+ maxpip * sizeof(*conn->co_proc));
+ if (conn->co_proc == NULL) {
+ syslog(LOG_ERR, "realloc: %m");
+ exit(EX_OSERR);
+ }
+ if (conn->co_numchild > maxpip)
+ conn->co_numchild = maxpip;
+ }
+ }
+}
+
+static void
+free_connlist(struct servtab *sep)
+{
+ struct conninfo *conn;
+ int i, j;
+
+ for (i = 0; i < PERIPSIZE; ++i) {
+ while ((conn = LIST_FIRST(&sep->se_conn[i])) != NULL) {
+ for (j = 0; j < conn->co_numchild; ++j)
+ free_proc(conn->co_proc[j]);
+ conn->co_numchild = 0;
+ free_conn(conn);
+ }
+ }
+}
+
+static void
+free_conn(struct conninfo *conn)
+{
+ if (conn == NULL)
+ return;
+ if (conn->co_numchild <= 0) {
+ LIST_REMOVE(conn, co_link);
+ free(conn->co_proc);
+ free(conn);
+ }
+}
+
+static struct procinfo *
+search_proc(pid_t pid, int add)
+{
+ struct procinfo *proc;
+ int hv;
+
+ hv = hashval((char *)&pid, sizeof(pid));
+ LIST_FOREACH(proc, &proctable[hv], pr_link) {
+ if (proc->pr_pid == pid)
+ break;
+ }
+ if (proc == NULL && add) {
+ if ((proc = malloc(sizeof(struct procinfo))) == NULL) {
+ syslog(LOG_ERR, "malloc: %m");
+ exit(EX_OSERR);
+ }
+ proc->pr_pid = pid;
+ proc->pr_conn = NULL;
+ LIST_INSERT_HEAD(&proctable[hv], proc, pr_link);
+ }
+ return proc;
+}
+
+static void
+free_proc(struct procinfo *proc)
+{
+ if (proc == NULL)
+ return;
+ LIST_REMOVE(proc, pr_link);
+ free(proc);
+}
+
+static int
+hashval(char *p, int len)
+{
+ int i, hv = 0xABC3D20F;
+
+ for (i = 0; i < len; ++i, ++p)
+ hv = (hv << 5) ^ (hv >> 23) ^ *p;
+ hv = (hv ^ (hv >> 16)) & (PERIPSIZE - 1);
+ return hv;
+}
diff --git a/usr.sbin/inetd/inetd.h b/usr.sbin/inetd/inetd.h
new file mode 100644
index 0000000..e0a83bd
--- /dev/null
+++ b/usr.sbin/inetd/inetd.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 1983, 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/queue.h>
+
+#include <netinet/in.h>
+
+#include <stdio.h>
+
+#define BUFSIZE 8192
+#define LINESIZ 72
+
+#define NORM_TYPE 0
+#define MUX_TYPE 1
+#define MUXPLUS_TYPE 2
+#define FAITH_TYPE 4
+#define ISMUX(sep) (((sep)->se_type == MUX_TYPE) || \
+ ((sep)->se_type == MUXPLUS_TYPE))
+#define ISMUXPLUS(sep) ((sep)->se_type == MUXPLUS_TYPE)
+
+struct procinfo {
+ LIST_ENTRY(procinfo) pr_link;
+ pid_t pr_pid; /* child pid */
+ struct conninfo *pr_conn;
+};
+
+struct conninfo {
+ LIST_ENTRY(conninfo) co_link;
+ struct sockaddr_storage co_addr; /* source address */
+ int co_numchild; /* current number of children */
+ struct procinfo **co_proc; /* array of child proc entry */
+};
+
+#define PERIPSIZE 256
+
+struct servtab {
+ char *se_service; /* name of service */
+ int se_socktype; /* type of socket to use */
+ int se_family; /* address family */
+ char *se_proto; /* protocol used */
+ int se_maxchild; /* max number of children */
+ int se_maxcpm; /* max connects per IP per minute */
+ int se_numchild; /* current number of children */
+ pid_t *se_pids; /* array of child pids */
+ char *se_user; /* user name to run as */
+ char *se_group; /* group name to run as */
+#ifdef LOGIN_CAP
+ char *se_class; /* login class name to run with */
+#endif
+ struct biltin *se_bi; /* if built-in, description */
+ char *se_server; /* server program */
+ char *se_server_name; /* server program without path */
+#define MAXARGV 20
+ char *se_argv[MAXARGV+1]; /* program arguments */
+#ifdef IPSEC
+ char *se_policy; /* IPsec policy string */
+#endif
+ int se_fd; /* open descriptor */
+ union { /* bound address */
+ struct sockaddr se_un_ctrladdr;
+ struct sockaddr_in se_un_ctrladdr4;
+ struct sockaddr_in6 se_un_ctrladdr6;
+ struct sockaddr_un se_un_ctrladdr_un;
+ } se_un;
+#define se_ctrladdr se_un.se_un_ctrladdr
+#define se_ctrladdr4 se_un.se_un_ctrladdr4
+#define se_ctrladdr6 se_un.se_un_ctrladdr6
+#define se_ctrladdr_un se_un.se_un_ctrladdr_un
+ socklen_t se_ctrladdr_size;
+ uid_t se_sockuid; /* Owner for unix domain socket */
+ gid_t se_sockgid; /* Group for unix domain socket */
+ mode_t se_sockmode; /* Mode for unix domain socket */
+ u_char se_type; /* type: normal, mux, or mux+ */
+ u_char se_checked; /* looked at during merge */
+ u_char se_accept; /* i.e., wait/nowait mode */
+ u_char se_rpc; /* ==1 if RPC service */
+ int se_rpc_prog; /* RPC program number */
+ u_int se_rpc_lowvers; /* RPC low version */
+ u_int se_rpc_highvers; /* RPC high version */
+ int se_count; /* number started since se_time */
+ struct timeval se_time; /* start of se_count */
+ struct servtab *se_next;
+ struct se_flags {
+ u_int se_nomapped : 1;
+ u_int se_reset : 1;
+ } se_flags;
+ int se_maxperip; /* max number of children per src */
+ LIST_HEAD(, conninfo) se_conn[PERIPSIZE];
+};
+
+#define se_nomapped se_flags.se_nomapped
+#define se_reset se_flags.se_reset
+
+int check_loop(const struct sockaddr *, const struct servtab *sep);
+int getvalue(const char *, int *, const char *);
+char *newstr(const char *);
+void inetd_setproctitle(const char *, int);
+void print_service(const char *, const struct servtab *);
+char *sskip(char **);
+char *skip(char **);
+struct servtab *tcpmux(int);
+
+extern int debug;
+extern struct servtab *servtab;
+
+typedef void (bi_fn_t)(int, struct servtab *);
+
+struct biltin {
+ const char *bi_service; /* internally provided service name */
+ int bi_socktype; /* type of socket supported */
+ short bi_fork; /* 1 if should fork before call */
+ int bi_maxchild; /* max number of children, -1=default */
+ bi_fn_t *bi_fn; /* function which performs it */
+};
diff --git a/usr.sbin/inetd/pathnames.h b/usr.sbin/inetd/pathnames.h
new file mode 100644
index 0000000..d4fd81d
--- /dev/null
+++ b/usr.sbin/inetd/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/6/93
+ * $FreeBSD$
+ */
+
+#include <paths.h>
+
+#define _PATH_INETDCONF "/etc/inetd.conf"
+#define _PATH_INETDPID _PATH_VARRUN "inetd.pid"
diff --git a/usr.sbin/iostat/Makefile b/usr.sbin/iostat/Makefile
new file mode 100644
index 0000000..558b09b
--- /dev/null
+++ b/usr.sbin/iostat/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= iostat
+MAN= iostat.8
+
+DPADD= ${LIBDEVSTAT} ${LIBKVM} ${LIBM}
+LDADD= -ldevstat -lkvm -lm
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/iostat/iostat.8 b/usr.sbin/iostat/iostat.8
new file mode 100644
index 0000000..e7ceb62
--- /dev/null
+++ b/usr.sbin/iostat/iostat.8
@@ -0,0 +1,466 @@
+.\"
+.\" Copyright (c) 1997 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$
+.\"
+.\" Copyright (c) 1985, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.\" @(#)iostat.8 8.1 (Berkeley) 6/6/93
+.\"
+.Dd April 17, 2006
+.Dt IOSTAT 8
+.Os
+.Sh NAME
+.Nm iostat
+.Nd report
+.Tn I/O
+statistics
+.Sh SYNOPSIS
+.Nm
+.Op Fl CdhIKoTxz?\&
+.Op Fl c Ar count
+.Op Fl M Ar core
+.Op Fl n Ar devs
+.Op Fl N Ar system
+.Oo
+.Fl t
+.Sm off
+.Ar type , if , pass
+.Sm on
+.Oc
+.Op Fl w Ar wait
+.Op Ar drives
+.Sh DESCRIPTION
+The
+.Nm
+utility displays kernel
+.Tn I/O
+statistics on terminal, device and cpu operations.
+The first statistics that are printed are averaged over the system uptime.
+To get information about the current activity, a suitable wait time should
+be specified, so that the subsequent sets of printed statistics will be
+averaged over that time.
+.Pp
+The options are as follows:
+.Bl -tag -width flag
+.It Fl c
+Repeat the display
+.Ar count
+times.
+If no
+.Ar wait
+interval is specified, the default is 1 second.
+.It Fl C
+Display CPU statistics.
+This is on by default, unless
+.Fl d
+is specified.
+.It Fl d
+Display only device statistics.
+If this flag is turned on, only device statistics will be displayed, unless
+.Fl C
+or
+.Fl T
+is also specified to enable the display of CPU or TTY statistics.
+.It Fl h
+Put
+.Nm
+in
+.Sq top
+mode.
+In this mode,
+.Nm
+will show devices in order from highest to lowest bytes
+per measurement cycle.
+.It Fl I
+Display total statistics for a given time period, rather than average
+statistics for each second during that time period.
+.It Fl K
+In the blocks transferred display (-o), display block count in kilobytes rather
+then the device native block size.
+.It Fl M
+Extract values associated with the name list from the specified core
+instead of the default
+.Dq Pa /dev/kmem .
+.It Fl n
+Display up to
+.Ar devs
+number of devices.
+The
+.Nm
+utility will display fewer devices if there are not
+.Ar devs
+devices present.
+.It Fl N
+Extract the name list from the specified system instead of the default
+.Dq Pa /boot/kernel/kernel .
+.It Fl o
+Display old-style
+.Nm
+device statistics.
+Sectors per second, transfers per second, and milliseconds per seek are
+displayed.
+If
+.Fl I
+is specified, total blocks/sectors, total transfers, and
+milliseconds per seek are displayed.
+.It Fl t
+Specify which types of devices to display.
+There are three different categories of devices:
+.Pp
+.Bl -tag -width indent -compact
+.It device type:
+.Bl -tag -width 9n -compact
+.It da
+Direct Access devices
+.It sa
+Sequential Access devices
+.It printer
+Printers
+.It proc
+Processor devices
+.It worm
+Write Once Read Multiple devices
+.It cd
+CD devices
+.It scanner
+Scanner devices
+.It optical
+Optical Memory devices
+.It changer
+Medium Changer devices
+.It comm
+Communication devices
+.It array
+Storage Array devices
+.It enclosure
+Enclosure Services devices
+.It floppy
+Floppy devices
+.El
+.Pp
+.It interface:
+.Bl -tag -width 9n -compact
+.It IDE
+Integrated Drive Electronics devices
+.It SCSI
+Small Computer System Interface devices
+.It other
+Any other device interface
+.El
+.Pp
+.It passthrough:
+.Bl -tag -width 9n -compact
+.It pass
+Passthrough devices
+.El
+.El
+.Pp
+The user must specify at least one device type, and may specify at most
+one device type from each category.
+Multiple device types in a single device type statement must be separated by
+commas.
+.Pp
+Any number of
+.Fl t
+arguments may be specified on the command line.
+All
+.Fl t
+arguments are ORed together to form a matching expression against which
+all devices in the system are compared.
+Any device that fully matches any
+.Fl t
+argument will be included in the
+.Nm
+output, up to the number of devices that can be displayed in
+80 columns, or the maximum number of devices specified by the user.
+.It Fl T
+Display TTY statistics.
+This is on by default, unless
+.Fl d
+is specified.
+.It Fl w
+Pause
+.Ar wait
+seconds between each display.
+If no repeat
+.Ar count
+is specified, the default is infinity.
+.It Fl x
+Show extended disk statistics.
+Each disk is displayed on a line of its own with all available statistics.
+.It Fl z
+If
+.Fl x
+is specified, omit lines for devices with no activity.
+.It Fl ?\&
+Display a usage statement and exit.
+.El
+.Pp
+The
+.Nm
+utility displays its information in the following format:
+.Bl -tag -width flag
+.It tty
+.Bl -tag -width indent -compact
+.It tin
+characters read from terminals
+.It tout
+characters written to terminals
+.El
+.It devices
+Device operations.
+The header of the field is the device name and unit number.
+The
+.Nm
+utility
+will display as many devices as will fit in a standard 80 column screen, or
+the maximum number of devices in the system, whichever is smaller.
+If
+.Fl n
+is specified on the command line,
+.Nm
+will display the smaller of the
+requested number of devices, and the maximum number of devices in the system.
+To force
+.Nm
+to display specific drives, their names may be supplied on the command
+line.
+The
+.Nm
+utility
+will not display more devices than will fit in an 80 column screen, unless
+the
+.Fl n
+argument is given on the command line to specify a maximum number of
+devices to display.
+If fewer devices are specified on the command line than will fit in an 80
+column screen,
+.Nm
+will show only the specified devices.
+.Pp
+The standard
+.Nm
+device display shows the following statistics:
+.Pp
+.Bl -tag -width indent -compact
+.It KB/t
+kilobytes per transfer
+.It tps
+transfers per second
+.It MB/s
+megabytes per second
+.El
+.Pp
+The standard
+.Nm
+device display, with the
+.Fl I
+flag specified, shows the following statistics:
+.Pp
+.Bl -tag -width indent -compact
+.It KB/t
+kilobytes per transfer
+.It xfrs
+total number of transfers
+.It MB
+total number of megabytes transferred
+.El
+.Pp
+The extended
+.Nm
+device display, with the
+.Fl x
+flag specified, shows the following statistics:
+.Pp
+.Bl -tag -width indent -compact
+.It r/s
+read operations per second
+.It w/s
+write operations per second
+.It kr/s
+kilobytes read per second
+.It kw/s
+kilobytes write per second
+.It wait
+transactions queue length
+.It svc_t
+average duration of transactions, in milliseconds
+.It %b
+% of time the device had one or more outstanding transactions
+.El
+.Pp
+The old-style
+.Nm
+display (using
+.Fl o )
+shows the following statistics:
+.Pp
+.Bl -tag -width indent -compact
+.It sps
+sectors transferred per second
+.It tps
+transfers per second
+.It msps
+average milliseconds per transaction
+.El
+.Pp
+The old-style
+.Nm
+display, with the
+.Fl I
+flag specified, shows the following statistics:
+.Pp
+.Bl -tag -width indent -compact
+.It blk
+total blocks/sectors transferred
+.It xfr
+total transfers
+.It msps
+average milliseconds per transaction
+.El
+.It cpu
+.Bl -tag -width indent -compact
+.It \&us
+% of cpu time in user mode
+.It \&ni
+% of cpu time in user mode running niced processes
+.It \&sy
+% of cpu time in system mode
+.It \&in
+% of cpu time in interrupt mode
+.It \&id
+% of cpu time in idle mode
+.El
+.El
+.Sh FILES
+.Bl -tag -width /boot/kernel/kernel -compact
+.It Pa /boot/kernel/kernel
+Default kernel namelist.
+.It Pa /dev/kmem
+Default memory file.
+.El
+.Sh EXAMPLES
+.Dl iostat -w 1 da0 da1 cd0
+.Pp
+Display statistics for the first two Direct Access devices and the first
+CDROM device every second ad infinitum.
+.Pp
+.Dl iostat -c 2
+.Pp
+Display the statistics for the first four devices in the system twice, with
+a one second display interval.
+.Pp
+.Dl iostat -t da -t cd -w 1
+.Pp
+Display statistics for all CDROM and Direct Access devices every second
+ad infinitum.
+.Pp
+.Dl iostat -t da,scsi,pass -t cd,scsi,pass
+.Pp
+Display statistics once for all SCSI passthrough devices that provide access
+to either Direct Access or CDROM devices.
+.Pp
+.Dl iostat -h -n 8 -w 1
+.Pp
+Display up to 8 devices with the most I/O every second ad infinitum.
+.Pp
+.Dl iostat -dh -t da -w 1
+.Pp
+Omit the TTY and CPU displays, show devices in order of performance and
+show only Direct Access devices every second ad infinitum.
+.Pp
+.Dl iostat -Iw 3
+.Pp
+Display total statistics every three seconds ad infinitum.
+.Pp
+.Dl iostat -odICTw 2 -c 9
+.Pp
+Display total statistics using the old-style output format 9 times, with
+a two second interval between each measurement/display.
+The
+.Fl d
+flag generally disables the TTY and CPU displays, but since the
+.Fl T
+and
+.Fl C
+flags are given, the TTY and CPU displays will be displayed.
+.Sh SEE ALSO
+.Xr fstat 1 ,
+.Xr netstat 1 ,
+.Xr nfsstat 1 ,
+.Xr ps 1 ,
+.Xr systat 1 ,
+.Xr devstat 3 ,
+.Xr gstat 8 ,
+.Xr pstat 8 ,
+.Xr vmstat 8
+.Pp
+The sections starting with ``Interpreting system activity'' in
+.%T "Installing and Operating 4.3BSD" .
+.Sh HISTORY
+This version of
+.Nm
+first appeared in
+.Fx 3.0 .
+.Sh AUTHORS
+.An Kenneth Merry Aq ken@FreeBSD.org
+.Sh BUGS
+The use of
+.Nm
+as a debugging tool for crash dumps is probably limited because there is
+currently no way to get statistics that only cover the time immediately before
+the crash.
diff --git a/usr.sbin/iostat/iostat.c b/usr.sbin/iostat/iostat.c
new file mode 100644
index 0000000..40d78ab
--- /dev/null
+++ b/usr.sbin/iostat/iostat.c
@@ -0,0 +1,956 @@
+/*
+ * Copyright (c) 1997, 1998, 2000, 2001 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$
+ */
+/*
+ * Parts of this program are derived from the original FreeBSD iostat
+ * program:
+ */
+/*-
+ * Copyright (c) 1986, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.
+ */
+/*
+ * Ideas for the new iostat statistics output modes taken from the NetBSD
+ * version of iostat:
+ */
+/*
+ * Copyright (c) 1996 John M. Vinopal
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 John M. Vinopal.
+ * 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/param.h>
+#include <sys/errno.h>
+#include <sys/resource.h>
+#include <sys/sysctl.h>
+
+#include <err.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <nlist.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+#include <limits.h>
+#include <devstat.h>
+#include <math.h>
+
+struct nlist namelist[] = {
+#define X_TK_NIN 0
+ { "_tk_nin" },
+#define X_TK_NOUT 1
+ { "_tk_nout" },
+#define X_BOOTTIME 2
+ { "_boottime" },
+#define X_END 2
+ { NULL },
+};
+
+#define IOSTAT_DEFAULT_ROWS 20 /* Traditional default `wrows' */
+
+struct statinfo cur, last;
+int num_devices;
+struct device_selection *dev_select;
+int maxshowdevs;
+volatile sig_atomic_t headercount;
+volatile sig_atomic_t wresized; /* Tty resized, when non-zero. */
+unsigned short wrows; /* Current number of tty rows. */
+int dflag = 0, Iflag = 0, Cflag = 0, Tflag = 0, oflag = 0, Kflag = 0;
+int xflag = 0, zflag = 0;
+
+/* local function declarations */
+static void usage(void);
+static void needhdr(int signo);
+static void needresize(int signo);
+static void doresize(void);
+static void phdr(void);
+static void devstats(int perf_select, long double etime, int havelast);
+static void cpustats(void);
+static int readvar(kvm_t *kd, const char *name, int nlid, void *ptr,
+ size_t len);
+
+static void
+usage(void)
+{
+ /*
+ * We also support the following 'traditional' syntax:
+ * iostat [drives] [wait [count]]
+ * This isn't mentioned in the man page, or the usage statement,
+ * but it is supported.
+ */
+ fprintf(stderr, "usage: iostat [-CdhIKoTxz?] [-c count] [-M core]"
+ " [-n devs] [-N system]\n"
+ "\t [-t type,if,pass] [-w wait] [drives]\n");
+}
+
+int
+main(int argc, char **argv)
+{
+ int c, i;
+ int tflag = 0, hflag = 0, cflag = 0, wflag = 0, nflag = 0;
+ int count = 0, waittime = 0;
+ char *memf = NULL, *nlistf = NULL;
+ struct devstat_match *matches;
+ int num_matches = 0;
+ char errbuf[_POSIX2_LINE_MAX];
+ kvm_t *kd = NULL;
+ long generation;
+ int num_devices_specified;
+ int num_selected, num_selections;
+ long select_generation;
+ char **specified_devices;
+ devstat_select_mode select_mode;
+ int havelast = 0;
+
+ matches = NULL;
+ maxshowdevs = 3;
+
+ while ((c = getopt(argc, argv, "c:CdhIKM:n:N:ot:Tw:xz?")) != -1) {
+ switch(c) {
+ case 'c':
+ cflag++;
+ count = atoi(optarg);
+ if (count < 1)
+ errx(1, "count %d is < 1", count);
+ break;
+ case 'C':
+ Cflag++;
+ break;
+ case 'd':
+ dflag++;
+ break;
+ case 'h':
+ hflag++;
+ break;
+ case 'I':
+ Iflag++;
+ break;
+ case 'K':
+ Kflag++;
+ break;
+ case 'M':
+ memf = optarg;
+ break;
+ case 'n':
+ nflag++;
+ maxshowdevs = atoi(optarg);
+ if (maxshowdevs < 0)
+ errx(1, "number of devices %d is < 0",
+ maxshowdevs);
+ break;
+ case 'N':
+ nlistf = optarg;
+ break;
+ case 'o':
+ oflag++;
+ break;
+ case 't':
+ tflag++;
+ if (devstat_buildmatch(optarg, &matches,
+ &num_matches) != 0)
+ errx(1, "%s", devstat_errbuf);
+ break;
+ case 'T':
+ Tflag++;
+ break;
+ case 'w':
+ wflag++;
+ waittime = atoi(optarg);
+ if (waittime < 1)
+ errx(1, "wait time is < 1");
+ break;
+ case 'x':
+ xflag++;
+ break;
+ case 'z':
+ zflag++;
+ break;
+ default:
+ usage();
+ exit(1);
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (nlistf != NULL || memf != NULL) {
+ kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
+
+ if (kd == NULL)
+ errx(1, "kvm_openfiles: %s", errbuf);
+
+ if (kvm_nlist(kd, namelist) == -1)
+ errx(1, "kvm_nlist: %s", kvm_geterr(kd));
+ }
+
+ /*
+ * Make sure that the userland devstat version matches the kernel
+ * devstat version. If not, exit and print a message informing
+ * the user of his mistake.
+ */
+ if (devstat_checkversion(kd) < 0)
+ errx(1, "%s", devstat_errbuf);
+
+ /*
+ * Make sure Tflag and/or Cflag are set if dflag == 0. If dflag is
+ * greater than 0, they may be 0 or non-zero.
+ */
+ if (dflag == 0 && xflag == 0) {
+ Cflag = 1;
+ Tflag = 1;
+ }
+
+ /* find out how many devices we have */
+ if ((num_devices = devstat_getnumdevs(kd)) < 0)
+ err(1, "can't get number of devices");
+
+ /*
+ * Figure out how many devices we should display.
+ */
+ if (nflag == 0) {
+ if (xflag > 0)
+ maxshowdevs = num_devices;
+ else if (oflag > 0) {
+ if ((dflag > 0) && (Cflag == 0) && (Tflag == 0))
+ maxshowdevs = 5;
+ else if ((dflag > 0) && (Tflag > 0) && (Cflag == 0))
+ maxshowdevs = 5;
+ else
+ maxshowdevs = 4;
+ } else {
+ if ((dflag > 0) && (Cflag == 0))
+ maxshowdevs = 4;
+ else
+ maxshowdevs = 3;
+ }
+ }
+
+ cur.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
+ if (cur.dinfo == NULL)
+ err(1, "malloc failed");
+
+ last.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
+ if (last.dinfo == NULL)
+ err(1, "malloc failed");
+
+ bzero(cur.dinfo, sizeof(struct devinfo));
+ bzero(last.dinfo, sizeof(struct devinfo));
+
+ /*
+ * Grab all the devices. We don't look to see if the list has
+ * changed here, since it almost certainly has. We only look for
+ * errors.
+ */
+ if (devstat_getdevs(kd, &cur) == -1)
+ errx(1, "%s", devstat_errbuf);
+
+ num_devices = cur.dinfo->numdevs;
+ generation = cur.dinfo->generation;
+
+ /*
+ * If the user specified any devices on the command line, see if
+ * they are in the list of devices we have now.
+ */
+ specified_devices = (char **)malloc(sizeof(char *));
+ if (specified_devices == NULL)
+ err(1, "malloc failed");
+
+ for (num_devices_specified = 0; *argv; ++argv) {
+ if (isdigit(**argv))
+ break;
+ num_devices_specified++;
+ specified_devices = (char **)realloc(specified_devices,
+ sizeof(char *) *
+ num_devices_specified);
+ if (specified_devices == NULL)
+ err(1, "realloc failed");
+
+ specified_devices[num_devices_specified - 1] = *argv;
+
+ }
+ if (nflag == 0 && maxshowdevs < num_devices_specified)
+ maxshowdevs = num_devices_specified;
+
+ dev_select = NULL;
+
+ if ((num_devices_specified == 0) && (num_matches == 0))
+ select_mode = DS_SELECT_ADD;
+ else
+ select_mode = DS_SELECT_ONLY;
+
+ /*
+ * At this point, selectdevs will almost surely indicate that the
+ * device list has changed, so we don't look for return values of 0
+ * or 1. If we get back -1, though, there is an error.
+ */
+ if (devstat_selectdevs(&dev_select, &num_selected,
+ &num_selections, &select_generation, generation,
+ cur.dinfo->devices, num_devices, matches,
+ num_matches, specified_devices,
+ num_devices_specified, select_mode, maxshowdevs,
+ hflag) == -1)
+ errx(1, "%s", devstat_errbuf);
+
+ /*
+ * Look for the traditional wait time and count arguments.
+ */
+ if (*argv) {
+ waittime = atoi(*argv);
+
+ /* Let the user know he goofed, but keep going anyway */
+ if (wflag != 0)
+ warnx("discarding previous wait interval, using"
+ " %d instead", waittime);
+ wflag++;
+
+ if (*++argv) {
+ count = atoi(*argv);
+ if (cflag != 0)
+ warnx("discarding previous count, using %d"
+ " instead", count);
+ cflag++;
+ } else
+ count = -1;
+ }
+
+ /*
+ * If the user specified a count, but not an interval, we default
+ * to an interval of 1 second.
+ */
+ if ((wflag == 0) && (cflag > 0))
+ waittime = 1;
+
+ /*
+ * If the user specified a wait time, but not a count, we want to
+ * go on ad infinitum. This can be redundant if the user uses the
+ * traditional method of specifying the wait, since in that case we
+ * already set count = -1 above. Oh well.
+ */
+ if ((wflag > 0) && (cflag == 0))
+ count = -1;
+
+ bzero(cur.cp_time, sizeof(cur.cp_time));
+ cur.tk_nout = 0;
+ cur.tk_nin = 0;
+
+ /*
+ * Set the snap time to the system boot time (ie: zero), so the
+ * stats are calculated since system boot.
+ */
+ cur.snap_time = 0;
+
+ /*
+ * If the user stops the program (control-Z) and then resumes it,
+ * print out the header again.
+ */
+ (void)signal(SIGCONT, needhdr);
+
+ /*
+ * If our standard output is a tty, then install a SIGWINCH handler
+ * and set wresized so that our first iteration through the main
+ * iostat loop will peek at the terminal's current rows to find out
+ * how many lines can fit in a screenful of output.
+ */
+ if (isatty(fileno(stdout)) != 0) {
+ wresized = 1;
+ (void)signal(SIGWINCH, needresize);
+ } else {
+ wresized = 0;
+ wrows = IOSTAT_DEFAULT_ROWS;
+ }
+
+ for (headercount = 1;;) {
+ struct devinfo *tmp_dinfo;
+ long tmp;
+ long double etime;
+
+ if (Tflag > 0) {
+ if ((readvar(kd, "kern.tty_nin", X_TK_NIN, &cur.tk_nin,
+ sizeof(cur.tk_nin)) != 0)
+ || (readvar(kd, "kern.tty_nout", X_TK_NOUT,
+ &cur.tk_nout, sizeof(cur.tk_nout))!= 0)) {
+ Tflag = 0;
+ warnx("disabling TTY statistics");
+ }
+ }
+
+ if (Cflag > 0) {
+ if (kd == NULL) {
+ if (readvar(kd, "kern.cp_time", 0,
+ &cur.cp_time, sizeof(cur.cp_time)) != 0)
+ Cflag = 0;
+ } else {
+ if (kvm_getcptime(kd, cur.cp_time) < 0) {
+ warnx("kvm_getcptime: %s",
+ kvm_geterr(kd));
+ Cflag = 0;
+ }
+ }
+ if (Cflag == 0)
+ warnx("disabling CPU time statistics");
+ }
+
+ if (!--headercount) {
+ phdr();
+ if (wresized != 0)
+ doresize();
+ headercount = wrows;
+ }
+
+ tmp_dinfo = last.dinfo;
+ last.dinfo = cur.dinfo;
+ cur.dinfo = tmp_dinfo;
+
+ last.snap_time = cur.snap_time;
+
+ /*
+ * Here what we want to do is refresh our device stats.
+ * devstat_getdevs() returns 1 when the device list has changed.
+ * If the device list has changed, we want to go through
+ * the selection process again, in case a device that we
+ * were previously displaying has gone away.
+ */
+ switch (devstat_getdevs(kd, &cur)) {
+ case -1:
+ errx(1, "%s", devstat_errbuf);
+ break;
+ case 1: {
+ int retval;
+
+ num_devices = cur.dinfo->numdevs;
+ generation = cur.dinfo->generation;
+ retval = devstat_selectdevs(&dev_select, &num_selected,
+ &num_selections,
+ &select_generation,
+ generation,
+ cur.dinfo->devices,
+ num_devices, matches,
+ num_matches,
+ specified_devices,
+ num_devices_specified,
+ select_mode, maxshowdevs,
+ hflag);
+ switch(retval) {
+ case -1:
+ errx(1, "%s", devstat_errbuf);
+ break;
+ case 1:
+ phdr();
+ if (wresized != 0)
+ doresize();
+ headercount = wrows;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ /*
+ * We only want to re-select devices if we're in 'top'
+ * mode. This is the only mode where the devices selected
+ * could actually change.
+ */
+ if (hflag > 0) {
+ int retval;
+ retval = devstat_selectdevs(&dev_select, &num_selected,
+ &num_selections,
+ &select_generation,
+ generation,
+ cur.dinfo->devices,
+ num_devices, matches,
+ num_matches,
+ specified_devices,
+ num_devices_specified,
+ select_mode, maxshowdevs,
+ hflag);
+ switch(retval) {
+ case -1:
+ errx(1,"%s", devstat_errbuf);
+ break;
+ case 1:
+ phdr();
+ if (wresized != 0)
+ doresize();
+ headercount = wrows;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (Tflag > 0) {
+ tmp = cur.tk_nin;
+ cur.tk_nin -= last.tk_nin;
+ last.tk_nin = tmp;
+ tmp = cur.tk_nout;
+ cur.tk_nout -= last.tk_nout;
+ last.tk_nout = tmp;
+ }
+
+ etime = cur.snap_time - last.snap_time;
+
+ if (etime == 0.0)
+ etime = 1.0;
+
+ for (i = 0; i < CPUSTATES; i++) {
+ tmp = cur.cp_time[i];
+ cur.cp_time[i] -= last.cp_time[i];
+ last.cp_time[i] = tmp;
+ }
+
+ if (xflag == 0 && Tflag > 0)
+ printf("%4.0Lf%5.0Lf", cur.tk_nin / etime,
+ cur.tk_nout / etime);
+
+ devstats(hflag, etime, havelast);
+
+ if (xflag == 0) {
+ if (Cflag > 0)
+ cpustats();
+
+ printf("\n");
+ }
+ fflush(stdout);
+
+ if (count >= 0 && --count <= 0)
+ break;
+
+ sleep(waittime);
+ havelast = 1;
+ }
+
+ exit(0);
+}
+
+/*
+ * Force a header to be prepended to the next output.
+ */
+void
+needhdr(int signo)
+{
+
+ headercount = 1;
+}
+
+/*
+ * When the terminal is resized, force an update of the maximum number of rows
+ * printed between each header repetition. Then force a new header to be
+ * prepended to the next output.
+ */
+void
+needresize(int signo)
+{
+
+ wresized = 1;
+ headercount = 1;
+}
+
+/*
+ * Update the global `wrows' count of terminal rows.
+ */
+void
+doresize(void)
+{
+ int status;
+ struct winsize w;
+
+ for (;;) {
+ status = ioctl(fileno(stdout), TIOCGWINSZ, &w);
+ if (status == -1 && errno == EINTR)
+ continue;
+ else if (status == -1)
+ err(1, "ioctl");
+ if (w.ws_row > 3)
+ wrows = w.ws_row - 3;
+ else
+ wrows = IOSTAT_DEFAULT_ROWS;
+ break;
+ }
+
+ /*
+ * Inhibit doresize() calls until we are rescheduled by SIGWINCH.
+ */
+ wresized = 0;
+}
+
+static void
+phdr(void)
+{
+ int i, printed;
+ char devbuf[256];
+
+ /*
+ * If xflag is set, we need a per-loop header, not a page header, so
+ * just return. We'll print the header in devstats().
+ */
+ if (xflag > 0)
+ return;
+
+ if (Tflag > 0)
+ (void)printf(" tty");
+ for (i = 0, printed=0;(i < num_devices) && (printed < maxshowdevs);i++){
+ int di;
+ if ((dev_select[i].selected != 0)
+ && (dev_select[i].selected <= maxshowdevs)) {
+ di = dev_select[i].position;
+ snprintf(devbuf, sizeof(devbuf), "%s%d",
+ cur.dinfo->devices[di].device_name,
+ cur.dinfo->devices[di].unit_number);
+ if (oflag > 0)
+ (void)printf("%13.6s ", devbuf);
+ else
+ printf("%16.6s ", devbuf);
+ printed++;
+ }
+ }
+ if (Cflag > 0)
+ (void)printf(" cpu\n");
+ else
+ (void)printf("\n");
+
+ if (Tflag > 0)
+ (void)printf(" tin tout");
+
+ for (i=0, printed = 0;(i < num_devices) && (printed < maxshowdevs);i++){
+ if ((dev_select[i].selected != 0)
+ && (dev_select[i].selected <= maxshowdevs)) {
+ if (oflag > 0) {
+ if (Iflag == 0)
+ (void)printf(" sps tps msps ");
+ else
+ (void)printf(" blk xfr msps ");
+ } else {
+ if (Iflag == 0)
+ printf(" KB/t tps MB/s ");
+ else
+ printf(" KB/t xfrs MB ");
+ }
+ printed++;
+ }
+ }
+ if (Cflag > 0)
+ (void)printf(" us ni sy in id\n");
+ else
+ printf("\n");
+
+}
+
+static void
+devstats(int perf_select, long double etime, int havelast)
+{
+ int dn;
+ long double transfers_per_second, transfers_per_second_read, transfers_per_second_write;
+ long double kb_per_transfer, mb_per_second, mb_per_second_read, mb_per_second_write;
+ u_int64_t total_bytes, total_transfers, total_blocks;
+ u_int64_t total_bytes_read, total_transfers_read;
+ u_int64_t total_bytes_write, total_transfers_write;
+ long double busy_pct;
+ u_int64_t queue_len;
+ long double total_mb;
+ long double blocks_per_second, ms_per_transaction;
+ int firstline = 1;
+ char *devname;
+
+ if (xflag > 0) {
+ printf(" extended device statistics ");
+ if (Tflag > 0)
+ printf(" tty ");
+ if (Cflag > 0)
+ printf(" cpu ");
+ printf("\n");
+ if (Iflag == 0)
+ printf(
+ "device r/s w/s kr/s kw/s wait svc_t %%b "
+ );
+ else
+ printf(
+ "device r/i w/i kr/i kw/i wait svc_t %%b "
+ );
+ if (Tflag > 0)
+ printf("tin tout ");
+ if (Cflag > 0)
+ printf("us ni sy in id ");
+ printf("\n");
+ }
+
+ for (dn = 0; dn < num_devices; dn++) {
+ int di;
+
+ if (((perf_select == 0) && (dev_select[dn].selected == 0))
+ || (dev_select[dn].selected > maxshowdevs))
+ continue;
+
+ di = dev_select[dn].position;
+
+ if (devstat_compute_statistics(&cur.dinfo->devices[di],
+ havelast ? &last.dinfo->devices[di] : NULL, etime,
+ DSM_TOTAL_BYTES, &total_bytes,
+ DSM_TOTAL_BYTES_READ, &total_bytes_read,
+ DSM_TOTAL_BYTES_WRITE, &total_bytes_write,
+ DSM_TOTAL_TRANSFERS, &total_transfers,
+ DSM_TOTAL_TRANSFERS_READ, &total_transfers_read,
+ DSM_TOTAL_TRANSFERS_WRITE, &total_transfers_write,
+ DSM_TOTAL_BLOCKS, &total_blocks,
+ DSM_KB_PER_TRANSFER, &kb_per_transfer,
+ DSM_TRANSFERS_PER_SECOND, &transfers_per_second,
+ DSM_TRANSFERS_PER_SECOND_READ, &transfers_per_second_read,
+ DSM_TRANSFERS_PER_SECOND_WRITE, &transfers_per_second_write,
+ DSM_MB_PER_SECOND, &mb_per_second,
+ DSM_MB_PER_SECOND_READ, &mb_per_second_read,
+ DSM_MB_PER_SECOND_WRITE, &mb_per_second_write,
+ DSM_BLOCKS_PER_SECOND, &blocks_per_second,
+ DSM_MS_PER_TRANSACTION, &ms_per_transaction,
+ DSM_BUSY_PCT, &busy_pct,
+ DSM_QUEUE_LENGTH, &queue_len,
+ DSM_NONE) != 0)
+ errx(1, "%s", devstat_errbuf);
+
+ if (perf_select != 0) {
+ dev_select[dn].bytes = total_bytes;
+ if ((dev_select[dn].selected == 0)
+ || (dev_select[dn].selected > maxshowdevs))
+ continue;
+ }
+
+ if (Kflag > 0 || xflag > 0) {
+ int block_size = cur.dinfo->devices[di].block_size;
+ total_blocks = total_blocks * (block_size ?
+ block_size : 512) / 1024;
+ }
+
+ if (xflag > 0) {
+ if (asprintf(&devname, "%s%d",
+ cur.dinfo->devices[di].device_name,
+ cur.dinfo->devices[di].unit_number) == -1)
+ err(1, "asprintf");
+ /*
+ * If zflag is set, skip any devices with zero I/O.
+ */
+ if (zflag == 0 || transfers_per_second_read > 0.05 ||
+ transfers_per_second_write > 0.05 ||
+ mb_per_second_read > ((long double).0005)/1024 ||
+ mb_per_second_write > ((long double).0005)/1024 ||
+ busy_pct > 0.5) {
+ if (Iflag == 0)
+ printf("%-8.8s %5.1Lf %5.1Lf %7.1Lf %7.1Lf %4qu %5.1Lf %3.0Lf ",
+ devname, transfers_per_second_read,
+ transfers_per_second_write,
+ mb_per_second_read * 1024,
+ mb_per_second_write * 1024,
+ queue_len,
+ ms_per_transaction, busy_pct);
+ else
+ printf("%-8.8s %5.1Lf %5.1Lf %7.1Lf %7.1Lf %4qu %5.1Lf %3.0Lf ",
+ devname,
+ (long double)total_transfers_read,
+ (long double)total_transfers_write,
+ (long double)
+ total_bytes_read / 1024,
+ (long double)
+ total_bytes_write / 1024,
+ queue_len,
+ ms_per_transaction, busy_pct);
+ if (firstline) {
+ /*
+ * If this is the first device
+ * we're printing, also print
+ * CPU or TTY stats if requested.
+ */
+ firstline = 0;
+ if (Tflag > 0)
+ printf("%4.0Lf%5.0Lf",
+ cur.tk_nin / etime,
+ cur.tk_nout / etime);
+ if (Cflag > 0)
+ cpustats();
+ }
+ printf("\n");
+ }
+ free(devname);
+ } else if (oflag > 0) {
+ int msdig = (ms_per_transaction < 100.0) ? 1 : 0;
+
+ if (Iflag == 0)
+ printf("%4.0Lf%4.0Lf%5.*Lf ",
+ blocks_per_second,
+ transfers_per_second,
+ msdig,
+ ms_per_transaction);
+ else
+ printf("%4.1qu%4.1qu%5.*Lf ",
+ total_blocks,
+ total_transfers,
+ msdig,
+ ms_per_transaction);
+ } else {
+ if (Iflag == 0)
+ printf(" %5.2Lf %3.0Lf %5.2Lf ",
+ kb_per_transfer,
+ transfers_per_second,
+ mb_per_second);
+ else {
+ total_mb = total_bytes;
+ total_mb /= 1024 * 1024;
+
+ printf(" %5.2Lf %3.1qu %5.2Lf ",
+ kb_per_transfer,
+ total_transfers,
+ total_mb);
+ }
+ }
+ }
+ if (xflag > 0 && zflag > 0 && firstline == 1 &&
+ (Tflag > 0 || Cflag > 0)) {
+ /*
+ * If zflag is set and we did not print any device
+ * lines I/O because they were all zero,
+ * print TTY/CPU stats.
+ */
+ printf("%52s","");
+ if (Tflag > 0)
+ printf("%4.0Lf%5.0Lf", cur.tk_nin / etime,
+ cur.tk_nout / etime);
+ if (Cflag > 0)
+ cpustats();
+ printf("\n");
+ }
+}
+
+static void
+cpustats(void)
+{
+ int state;
+ double time;
+
+ time = 0.0;
+
+ for (state = 0; state < CPUSTATES; ++state)
+ time += cur.cp_time[state];
+ for (state = 0; state < CPUSTATES; ++state)
+ printf(" %2.0f",
+ rint(100. * cur.cp_time[state] / (time ? time : 1)));
+}
+
+static int
+readvar(kvm_t *kd, const char *name, int nlid, void *ptr, size_t len)
+{
+ if (kd != NULL) {
+ ssize_t nbytes;
+
+ nbytes = kvm_read(kd, namelist[nlid].n_value, ptr, len);
+
+ if (nbytes < 0) {
+ warnx("kvm_read(%s): %s", namelist[nlid].n_name,
+ kvm_geterr(kd));
+ return (1);
+ }
+ if (nbytes != len) {
+ warnx("kvm_read(%s): expected %zu bytes, got %zd bytes",
+ namelist[nlid].n_name, len, nbytes);
+ return (1);
+ }
+ } else {
+ size_t nlen = len;
+
+ if (sysctlbyname(name, ptr, &nlen, NULL, 0) == -1) {
+ warn("sysctl(%s...) failed", name);
+ return (1);
+ }
+ if (nlen != len) {
+ warnx("sysctl(%s...): expected %lu, got %lu", name,
+ (unsigned long)len, (unsigned long)nlen);
+ return (1);
+ }
+ }
+ return (0);
+}
diff --git a/usr.sbin/ip6addrctl/Makefile b/usr.sbin/ip6addrctl/Makefile
new file mode 100644
index 0000000..cd2510e
--- /dev/null
+++ b/usr.sbin/ip6addrctl/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+PROG= ip6addrctl
+MAN= ip6addrctl.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ip6addrctl/ip6addrctl.8 b/usr.sbin/ip6addrctl/ip6addrctl.8
new file mode 100644
index 0000000..59154d4
--- /dev/null
+++ b/usr.sbin/ip6addrctl/ip6addrctl.8
@@ -0,0 +1,126 @@
+.\" $KAME: ip6addrctl.8,v 1.3 2003/03/22 05:56:41 jinmei Exp $
+.\"
+.\" Copyright (C) 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd September 25, 2001
+.Dt IP6ADDRCTL 8
+.Os
+.\"
+.Sh NAME
+.Nm ip6addrctl
+.Nd configure address selection policy for IPv6 and IPv4
+.\"
+.Sh SYNOPSIS
+.Nm
+.Op Cm show
+.Nm
+.Cm add
+.Ar prefix precedence label
+.Nm
+.Cm delete
+.Ar prefix
+.Nm
+.Cm flush
+.Nm
+.Cm install
+.Ar configfile
+.\"
+.Sh DESCRIPTION
+The
+.Nm
+utility manages the policy table of source and destination address
+selection for outgoing IPv4 and IPv6 packets.
+When
+.Nm
+is invoked without an argument or with a single argument
+.Cm show ,
+it prints the content of the policy table currently installed in the
+kernel.
+.Pp
+To modify the table, the following operations are available:
+.Bl -tag -width indent
+.It Cm add Ar prefix precedence label
+Add a policy entry.
+The
+.Ar prefix
+argument
+is an IPv6 prefix, which is a key for the entry.
+An IPv4 prefix should be specified with an IPv6 prefix using an
+IPv4-mapped IPv6 address.
+The
+.Ar precedence
+and
+.Ar label
+arguments
+are decimal numbers, which specify the precedence and label values
+for the entry, respectively.
+This operation should be performed without an existing entry for the
+prefix.
+.It Cm delete Ar prefix
+Delete a policy entry specified by
+.Ar prefix ,
+which should be an IPv6 prefix.
+A corresponding entry for the prefix should have already been
+installed.
+.It Cm flush
+Delete all existing policy entries in the kernel.
+.It Cm install Ar configfile
+Install policy entries from a configuration file named
+.Ar configfile .
+The configuration file should contain a set of policy entries.
+Each entry is specified in a single line which contains an IPv6 prefix,
+a decimal precedence value, and a decimal label value, separated with
+white space or tab characters.
+In the configuration file, lines beginning with the pound-sign
+.Pq Ql #
+are
+comments and are ignored.
+.El
+.\"
+.Sh EXIT STATUS
+.Ex -std
+.\"
+.Sh SEE ALSO
+.Rs
+.%A "Richard Draves"
+.%T "Default Address Selection for IPv6"
+.%N RFC 3484
+.Re
+.\"
+.Sh HISTORY
+The
+.Nm
+utility first appeared in the KAME IPv6 protocol stack kit.
+The original command name was
+.Nm addrselect ,
+but it was then renamed to the current one so that the name would
+describe its function well.
+.\" .Sh BUGS
+.\" (to be written)
diff --git a/usr.sbin/ip6addrctl/ip6addrctl.c b/usr.sbin/ip6addrctl/ip6addrctl.c
new file mode 100644
index 0000000..532a096
--- /dev/null
+++ b/usr.sbin/ip6addrctl/ip6addrctl.c
@@ -0,0 +1,467 @@
+/* $KAME: ip6addrctl.c,v 1.3 2003/12/16 08:14:28 suz Exp $ */
+
+/*
+ * Copyright (C) 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+
+#include <netinet/in.h>
+#include <netinet6/in6_var.h>
+
+#include <stdlib.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <limits.h>
+#include <string.h>
+#include <err.h>
+
+static char *configfile;
+
+struct policyqueue {
+ TAILQ_ENTRY(policyqueue) pc_entry;
+ struct in6_addrpolicy pc_policy;
+};
+TAILQ_HEAD(policyhead, policyqueue);
+struct policyhead policyhead;
+
+static void usage(void);
+static void get_policy(void);
+static void dump_policy(void);
+static int mask2plen(struct sockaddr_in6 *);
+static int parse_prefix(const char *, struct in6_addrpolicy *);
+static void make_policy_fromfile(char *);
+static void plen2mask(struct sockaddr_in6 *, int);
+static void set_policy(void);
+static void add_policy(char *, char *, char *);
+static void delete_policy(char *);
+static void flush_policy();
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ TAILQ_INIT(&policyhead);
+
+ if (argc == 1 || strcasecmp(argv[1], "show") == 0) {
+ get_policy();
+ dump_policy();
+ } else if (strcasecmp(argv[1], "add") == 0) {
+ if (argc < 5)
+ usage();
+ add_policy(argv[2], argv[3], argv[4]);
+ } else if (strcasecmp(argv[1], "delete") == 0) {
+ if (argc < 3)
+ usage();
+ delete_policy(argv[2]);
+ } else if (strcasecmp(argv[1], "flush") == 0) {
+ get_policy();
+ flush_policy();
+ } else if (strcasecmp(argv[1], "install") == 0) {
+ if (argc < 3)
+ usage();
+ configfile = argv[2];
+ make_policy_fromfile(configfile);
+ set_policy();
+ } else
+ usage();
+
+ exit(0);
+}
+
+static void
+get_policy()
+{
+ int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_ADDRCTLPOLICY };
+ size_t l;
+ char *buf;
+ struct in6_addrpolicy *pol, *ep;
+
+ if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) {
+ err(1, "sysctl(IPV6CTL_ADDRCTLPOLICY)");
+ /* NOTREACHED */
+ }
+ if (l == 0) {
+ printf("no source-address-selection policy is installed\n");
+ return;
+ }
+ if ((buf = malloc(l)) == NULL) {
+ errx(1, "malloc failed");
+ /* NOTREACHED */
+ }
+ if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) {
+ err(1, "sysctl(IPV6CTL_ADDRCTLPOLICY)");
+ /* NOTREACHED */
+ }
+
+ ep = (struct in6_addrpolicy *)(buf + l);
+ for (pol = (struct in6_addrpolicy *)buf; pol + 1 <= ep; pol++) {
+ struct policyqueue *new;
+
+ if ((new = malloc(sizeof(*new))) == NULL)
+ errx(1, "malloc failed\n");
+ new->pc_policy = *pol;
+ TAILQ_INSERT_TAIL(&policyhead, new, pc_entry);
+ }
+
+ free(buf);
+}
+
+static void
+dump_policy()
+{
+ size_t addrlen;
+ char addrbuf[NI_MAXHOST];
+ struct in6_addrpolicy *pol;
+ struct policyqueue *ent;
+ int plen, first = 1;
+
+ for (ent = TAILQ_FIRST(&policyhead); ent;
+ ent = TAILQ_NEXT(ent, pc_entry)) {
+ pol = &ent->pc_policy;
+ if (first) {
+ printf("%-30s %5s %5s %8s\n",
+ "Prefix", "Prec", "Label", "Use");
+ first = 0;
+ }
+
+ if ((getnameinfo((struct sockaddr *)&pol->addr,
+ sizeof(pol->addr), addrbuf, sizeof(addrbuf),
+ NULL, 0, NI_NUMERICHOST))) {
+ warnx("getnameinfo for prefix address failed");
+ continue;
+ }
+ if ((plen = mask2plen(&pol->addrmask)) < 0) {
+ warnx("invalid address mask");
+ continue;
+ }
+ addrlen = strlen(addrbuf);
+ if (addrlen + sizeof("/128") < sizeof(addrbuf)) {
+ snprintf(&addrbuf[addrlen],
+ sizeof(addrbuf) - addrlen - 1,
+ "/%d", plen);
+ printf("%-30s", addrbuf);
+ } else /* XXX */
+ printf("%s/%d", addrbuf, plen);
+ printf(" %5d %5d %8llu\n", pol->preced, pol->label,
+ (unsigned long long)pol->use);
+ }
+}
+
+#define SKIP_WHITE(p, emptyok) \
+ do { \
+ while((*(p) == ' ' || *(p) == '\t')) \
+ (p)++; \
+ if ((*(p) == '\0' || (*(p) == '\n')) && !(emptyok)) \
+ goto bad; \
+ } while (0);
+#define SKIP_WORD(p) \
+ do { \
+ while(*(p) != ' ' && *(p) != '\t') \
+ (p)++; \
+ if (*(p) == '\0' || *(p) == '\n') \
+ goto bad; \
+ } while (0);
+
+static void
+make_policy_fromfile(conf)
+ char *conf;
+{
+ char line[_POSIX2_LINE_MAX], *cp;
+ char *addrstr;
+ FILE *fp;
+ int count = 0;
+ struct in6_addrpolicy pol0;
+ struct policyqueue *new;
+
+ if ((fp = fopen(conf, "r")) == NULL)
+ err(1, "fopen: %s", conf);
+
+ while(fgets(line, sizeof(line), fp)) {
+ count++;
+ cp = line;
+
+ memset(&pol0, 0, sizeof(pol0));
+
+ /* get prefix */
+ SKIP_WHITE(cp, 1);
+ if (*cp == '\n') /* empty line */
+ continue;
+ if (*cp == '#')
+ continue;
+ addrstr = cp;
+ if (parse_prefix((const char *)addrstr, &pol0))
+ goto bad;
+
+ /* get precedence value */
+ SKIP_WORD(cp);
+ SKIP_WHITE(cp, 0);
+ pol0.preced = atoi(cp);
+
+ /* get label */
+ SKIP_WORD(cp);
+ SKIP_WHITE(cp, 0);
+ pol0.label = atoi(cp);
+
+ /* parse succeeded. make a control buffer entry. */
+ if ((new = malloc(sizeof(*new))) == NULL)
+ errx(1, "malloc failed\n");
+ memset(new, 0, sizeof(*new));
+ new->pc_policy = pol0;
+ TAILQ_INSERT_TAIL(&policyhead, new, pc_entry);
+ }
+
+ fclose(fp);
+ return;
+
+ bad:
+ errx(1, "parse failed at line %d", count);
+ /* NOTREACHED */
+}
+
+static int
+parse_prefix(prefix0, pol)
+ const char *prefix0;
+ struct in6_addrpolicy *pol;
+{
+ int e = 0, plen;
+ char *prefix, *plenstr;
+ struct addrinfo hints, *res;
+
+ if ((prefix = strdup(prefix0)) == NULL)
+ errx(1, "strdup failed");
+
+ if ((plenstr = strchr(prefix, '/')) == NULL) {
+ e = -1;
+ goto end;
+ }
+ *plenstr = '\0';
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_NUMERICHOST;
+ hints.ai_family = AF_INET6;
+
+ if ((e = getaddrinfo(prefix, NULL, &hints, &res)) != 0) {
+ warnx("getaddrinfo failed for %s: %s", prefix,
+ gai_strerror(e));
+ goto end;
+ }
+ memcpy(&pol->addr, res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
+ plen = atoi(plenstr + 1);
+ if (plen < 0 || plen > 128) {
+ warnx("invalid prefix length: %d", plen);
+ e = -1;
+ goto end;
+ }
+ plen2mask(&pol->addrmask, plen);
+
+ end:
+ free(prefix);
+ return(e);
+}
+
+static void
+plen2mask(mask, plen)
+ struct sockaddr_in6 *mask;
+ int plen;
+{
+ u_char *cp = (char *)&mask->sin6_addr;
+
+ memset(mask, 0, sizeof(*mask));
+ mask->sin6_family = AF_INET6; /* just in case */
+ mask->sin6_len = sizeof(*mask);
+
+ for(; plen >= 8; plen -= 8)
+ *cp++ = 0xff;
+ if (plen > 0)
+ *cp = (0xff << (8 - plen));
+}
+
+static void
+set_policy()
+{
+ struct policyqueue *ent;
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0)
+ err(1, "socket(UDP)");
+
+ for (ent = TAILQ_FIRST(&policyhead); ent;
+ ent = TAILQ_NEXT(ent, pc_entry)) {
+ if (ioctl(s, SIOCAADDRCTL_POLICY, &ent->pc_policy))
+ warn("ioctl(SIOCAADDRCTL_POLICY)");
+ }
+
+ close(s);
+}
+
+static int
+mask2plen(mask)
+ struct sockaddr_in6 *mask;
+{
+ int masklen, final = 0;
+ u_char *p, *lim;
+
+ masklen = 0;
+ lim = (u_char *)(mask + 1);
+ for (p = (u_char *)(&mask->sin6_addr); p < lim; p++) {
+ if (final && *p) {
+ goto bad;
+ }
+
+ switch (*p & 0xff) {
+ case 0xff:
+ masklen += 8;
+ break;
+ case 0xfe:
+ masklen += 7;
+ final++;
+ break;
+ case 0xfc:
+ masklen += 6;
+ final++;
+ break;
+ case 0xf8:
+ masklen += 5;
+ final++;
+ break;
+ case 0xf0:
+ masklen += 4;
+ final++;
+ break;
+ case 0xe0:
+ masklen += 3;
+ final++;
+ break;
+ case 0xc0:
+ masklen += 2;
+ final++;
+ break;
+ case 0x80:
+ masklen += 1;
+ final++;
+ break;
+ case 0x00:
+ final++;
+ break;
+ default:
+ goto bad;
+ break;
+ }
+ }
+ return(masklen);
+
+ bad:
+ return(-1);
+}
+
+static void
+add_policy(prefix, prec, label)
+ char *prefix, *prec, *label;
+{
+ struct in6_addrpolicy p;
+ int s;
+
+ memset(&p, 0, sizeof(p));
+
+ if (parse_prefix((const char *)prefix, &p))
+ errx(1, "bad prefix: %s", prefix);
+ p.preced = atoi(prec);
+ p.label = atoi(label);
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0)
+ err(1, "socket(UDP)");
+ if (ioctl(s, SIOCAADDRCTL_POLICY, &p))
+ err(1, "ioctl(SIOCAADDRCTL_POLICY)");
+
+ close(s);
+}
+
+static void
+delete_policy(prefix)
+ char *prefix;
+{
+ struct in6_addrpolicy p;
+ int s;
+
+ memset(&p, 0, sizeof(p));
+
+ if (parse_prefix((const char *)prefix, &p))
+ errx(1, "bad prefix: %s", prefix);
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0)
+ err(1, "socket(UDP)");
+ if (ioctl(s, SIOCDADDRCTL_POLICY, &p))
+ err(1, "ioctl(SIOCDADDRCTL_POLICY)");
+
+ close(s);
+}
+
+static void
+flush_policy()
+{
+ struct policyqueue *ent;
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP)) < 0)
+ err(1, "socket(UDP)");
+
+ for (ent = TAILQ_FIRST(&policyhead); ent;
+ ent = TAILQ_NEXT(ent, pc_entry)) {
+ if (ioctl(s, SIOCDADDRCTL_POLICY, &ent->pc_policy))
+ warn("ioctl(SIOCDADDRCTL_POLICY)");
+ }
+
+ close(s);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: ip6addrctl [show]\n");
+ fprintf(stderr, " ip6addrctl add "
+ "<prefix> <precedence> <label>\n");
+ fprintf(stderr, " ip6addrctl delete <prefix>\n");
+ fprintf(stderr, " ip6addrctl flush\n");
+ fprintf(stderr, " ip6addrctl install <configfile>\n");
+
+ exit(1);
+}
diff --git a/usr.sbin/ip6addrctl/ip6addrctl.conf.sample b/usr.sbin/ip6addrctl/ip6addrctl.conf.sample
new file mode 100644
index 0000000..a2b3759
--- /dev/null
+++ b/usr.sbin/ip6addrctl/ip6addrctl.conf.sample
@@ -0,0 +1,12 @@
+# default policy table based on RFC 3484.
+# usage: ip6addrctl install path_to_this_file
+#
+# $FreeBSD$
+#
+#Format:
+#Prefix Precedence Label
+::1/128 50 0
+::/0 40 1
+2002::/16 30 2
+::/96 20 3
+::ffff:0:0/96 10 4
diff --git a/usr.sbin/ipfwpcap/Makefile b/usr.sbin/ipfwpcap/Makefile
new file mode 100644
index 0000000..d16f888
--- /dev/null
+++ b/usr.sbin/ipfwpcap/Makefile
@@ -0,0 +1,19 @@
+#
+# From: Id: Makefile,v 1.2 2004/01/15 16:20:56 pkern Exp
+#
+# $FreeBSD$
+#
+
+PROG= ipfwpcap
+
+LDADD= -lpcap
+DPADD= ${LIBPCAP}
+
+MAN= ipfwpcap.8
+
+.include <bsd.prog.mk>
+
+test: $(CMD)
+ -rm /var/run/ipfwpcap.2000.pid
+ ./ipfwpcap -d 2000 - | tcpdump -r - -n -s 2000 -X
+
diff --git a/usr.sbin/ipfwpcap/ipfwpcap.8 b/usr.sbin/ipfwpcap/ipfwpcap.8
new file mode 100644
index 0000000..53787fc
--- /dev/null
+++ b/usr.sbin/ipfwpcap/ipfwpcap.8
@@ -0,0 +1,132 @@
+.\" Copyright (c) 2006 Niclas Zeising
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 May 22, 2006
+.Dt IPFWPCAP 8
+.Os
+.Sh NAME
+.Nm ipfwpcap
+.Nd "copy diverted packets to a file in tcpdump format"
+.Sh SYNOPSIS
+.Nm
+.Op Fl dr
+.Op Fl b Ar maxbytes
+.Op Fl p Ar maxpkts
+.Op Fl P Ar pidfile
+.Ar portnum
+.Ar dumpfile
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to copy diverted packets to a file in
+.Xr tcpdump 1
+format.
+The interesting packets are diverted by
+.Xr ipfw 8
+to a port on which
+.Nm
+listens.
+The packets are then dropped unless
+.Fl r
+is used.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl d
+Turns on extra debugging messages.
+.It Fl r
+Writes packets back to the
+.Xr divert 4
+socket.
+.It Fl rr
+Indicates that it is okay to quit if
+.Ar maxbytes
+or
+.Ar maxpkts
+are reached.
+Diverted packets will silently disappear if nothing is listening on the
+.Xr divert 4
+socket.
+.It Fl b Ar maxbytes
+Stop dumping after
+.Ar maxbytes
+bytes.
+.It Fl p Ar maxpkts
+Stop dumping after
+.Ar maxpkt
+packets.
+.It Fl P Ar pidfile
+File to store PID number in.
+Default is
+.Pa /var/run/ipwfpcap.portnr.pid .
+.El
+.Pp
+The
+.Ar portnum
+argument specifies which
+.Xr divert 4
+socket port to listen on.
+The
+.Ar dumpfile
+argument is the path to the file to write captured packets to.
+Specify
+.Sq Fl
+to write to stdout.
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+.Dl "ipfwpcap -r 8091 divt.log &"
+.Pp
+Starts
+.Nm
+as a background job listening to port 8091 and reflecting the packets
+back to the socket.
+.Pp
+.Dl "ipfw add 2864 divert 8091 ip from 192.0.2.101"
+.Pp
+Example
+.Xr ipfw 8
+rule to divert all packets from 192.0.2.101 to port 8091.
+See
+.Xr ipfw 8
+for details.
+.Sh SEE ALSO
+.Xr tcpdump 1 ,
+.Xr pcap 3 ,
+.Xr divert 4 ,
+.Xr ipfw 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 7.0 .
+.Sh AUTHORS
+.An -nosplit
+.Nm
+was written by
+.An P. Kern Aq pkern@cns.utoronto.ca .
+This manual page was written by
+.An Niclas Zeising Aq niclas.zeising@gmail.com .
diff --git a/usr.sbin/ipfwpcap/ipfwpcap.c b/usr.sbin/ipfwpcap/ipfwpcap.c
new file mode 100644
index 0000000..36b764d
--- /dev/null
+++ b/usr.sbin/ipfwpcap/ipfwpcap.c
@@ -0,0 +1,306 @@
+/*
+ * copy diverted (or tee'd) packets to a file in 'tcpdump' format
+ * (ie. this uses the '-lpcap' routines).
+ *
+ * example usage:
+ * # ipfwpcap -r 8091 divt.log &
+ * # ipfw add 2864 divert 8091 ip from 128.432.53.82 to any
+ * # ipfw add 2864 divert 8091 ip from any to 128.432.53.82
+ *
+ * the resulting dump file can be read with ...
+ * # tcpdump -nX -r divt.log
+ */
+/*
+ * Written by P Kern { pkern [AT] cns.utoronto.ca }
+ *
+ * Copyright (c) 2004 University of Toronto. All rights reserved.
+ * Anyone may use or copy this software except that this copyright
+ * notice remain intact and that credit is given where it is due.
+ * The University of Toronto and the author make no warranty and
+ * accept no liability for this software.
+ *
+ * From: Header: /local/src/local.lib/SRC/ipfwpcap/RCS/ipfwpcap.c,v 1.4 2004/01/15 16:19:07 pkern Exp
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <paths.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/param.h> /* for MAXPATHLEN */
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <netinet/in_systm.h> /* for IP_MAXPACKET */
+#include <netinet/ip.h> /* for IP_MAXPACKET */
+
+/* XXX normally defined in config.h */
+#define HAVE_STRLCPY 1
+#define HAVE_SNPRINTF 1
+#define HAVE_VSNPRINTF 1
+#include <pcap-int.h> /* see pcap(3) and /usr/src/contrib/libpcap/. */
+
+#ifdef IP_MAXPACKET
+#define BUFMAX IP_MAXPACKET
+#else
+#define BUFMAX 65535
+#endif
+
+#ifndef MAXPATHLEN
+#define MAXPATHLEN 1024
+#endif
+
+static int debug = 0;
+static int reflect = 0; /* 1 == write packet back to socket. */
+
+static ssize_t totbytes = 0, maxbytes = 0;
+static ssize_t totpkts = 0, maxpkts = 0;
+
+char *prog = NULL;
+char pidfile[MAXPATHLEN] = { '\0' };
+
+/*
+ * tidy up.
+ */
+void
+quit(sig)
+int sig;
+{
+ (void) unlink(pidfile);
+ exit(sig);
+}
+
+/*
+ * do the "paper work"
+ * - save my own pid in /var/run/$0.{port#}.pid
+ */
+okay(pn)
+int pn;
+{
+ FILE *fp;
+ int fd, numlen, n;
+ char *p, numbuf[80];
+
+ numlen = sizeof(numbuf);
+ bzero(numbuf, numlen);
+ snprintf(numbuf, numlen-1, "%ld\n", getpid());
+ numlen = strlen(numbuf);
+
+ if (pidfile[0] == '\0') {
+ p = (char *)rindex(prog, '/');
+ p = (p == NULL) ? prog : p+1 ;
+
+ snprintf(pidfile, sizeof(pidfile)-1,
+ "%s%s.%d.pid", _PATH_VARRUN, p, pn);
+ }
+
+ fd = open(pidfile, O_WRONLY|O_CREAT|O_EXCL, 0644);
+ if (fd < 0) { perror(pidfile); exit(21); }
+
+ siginterrupt(SIGTERM, 1);
+ siginterrupt(SIGHUP, 1);
+ signal (SIGTERM, quit);
+ signal (SIGHUP, quit);
+ signal (SIGINT, quit);
+
+ n = write(fd, numbuf, numlen);
+ if (n < 0) { perror(pidfile); quit(23); }
+ (void) close(fd);
+}
+
+usage()
+{
+ fprintf(stderr, "\
+\n\
+usage:\n\
+ %s [-dr] [-b maxbytes] [-p maxpkts] [-P pidfile] portnum dumpfile\n\
+\n\
+where:\n\
+ '-d' = enable debugging messages.\n\
+ '-r' = reflect. write packets back to the divert socket.\n\
+ (ie. simulate the original intent of \"ipfw tee\").\n\
+ '-rr' = indicate that it is okay to quit if packet-count or\n\
+ byte-count limits are reached (see the NOTE below\n\
+ about what this implies).\n\
+ '-b bytcnt' = stop dumping after {bytcnt} bytes.\n\
+ '-p pktcnt' = stop dumping after {pktcnt} packets.\n\
+ '-P pidfile' = alternate file to store the PID\n\
+ (default: /var/run/%s.{portnum}.pid).\n\
+\n\
+ portnum = divert(4) socket port number.\n\
+ dumpfile = file to write captured packets (tcpdump format).\n\
+ (specify '-' to write packets to stdout).\n\
+\n\
+", prog, prog);
+
+ fprintf(stderr, "\
+The '-r' option should not be necessary, but because \"ipfw tee\" is broken\n\
+(see BUGS in ipfw(8) for details) this feature can be used along with\n\
+an \"ipfw divert\" rule to simulate the original intent of \"ipfw tee\".\n\
+\n\
+NOTE: With an \"ipfw divert\" rule, diverted packets will silently\n\
+ disappear if there is nothing listening to the divert socket.\n\
+\n\
+");
+ exit(-1);
+}
+
+main(ac, av)
+int ac;
+char *av[];
+{
+ int r, sd, portnum, l;
+ struct sockaddr_in sin;
+ int errflg = 0;
+
+ int nfd;
+ fd_set rds;
+
+ ssize_t nr;
+
+ char *dumpf, buf[BUFMAX];
+
+ pcap_t *p;
+ pcap_dumper_t *dp;
+ struct pcap_pkthdr phd;
+
+ prog = av[0];
+
+ while ((r = getopt(ac, av, "drb:p:P:")) != -1) {
+ switch (r) {
+ case 'd':
+ debug++;
+ break;
+ case 'r':
+ reflect++;
+ break;
+ case 'b':
+ maxbytes = (ssize_t) atol(optarg);
+ break;
+ case 'p':
+ maxpkts = (ssize_t) atoi(optarg);
+ break;
+ case 'P':
+ strcpy(pidfile, optarg);
+ break;
+ case '?':
+ default:
+ errflg++;
+ break;
+ }
+ }
+
+ if ((ac - optind) != 2 || errflg)
+ usage();
+
+ portnum = atoi(av[optind++]);
+ dumpf = av[optind];
+
+if (debug) fprintf(stderr, "bind to %d.\ndump to '%s'.\n", portnum, dumpf);
+
+ if ((r = socket(PF_INET, SOCK_RAW, IPPROTO_DIVERT)) == -1) {
+ perror("socket(DIVERT)");
+ exit(2);
+ }
+ sd = r;
+
+ sin.sin_port = htons(portnum);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = INADDR_ANY;
+
+ if (bind(sd, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
+ perror("bind(divert)");
+ exit(3);
+ }
+
+ p = pcap_open_dead(DLT_RAW, BUFMAX);
+ dp = pcap_dump_open(p, dumpf);
+ if (dp == NULL) {
+ pcap_perror(p, dumpf);
+ exit(4);
+ }
+
+ okay(portnum);
+
+ nfd = sd + 1;
+ for (;;) {
+ FD_ZERO(&rds);
+ FD_SET(sd, &rds);
+
+ r = select(nfd, &rds, NULL, NULL, NULL);
+ if (r == -1) {
+ if (errno == EINTR) continue;
+ perror("select");
+ quit(11);
+ }
+
+ if (!FD_ISSET(sd, &rds))
+ /* hmm. no work. */
+ continue;
+
+ /*
+ * use recvfrom(3 and sendto(3) as in natd(8).
+ * see /usr/src/sbin/natd/natd.c
+ * see ipfw(8) about using 'divert' and 'tee'.
+ */
+
+ /*
+ * read packet.
+ */
+ l = sizeof(sin);
+ nr = recvfrom(sd, buf, sizeof(buf), 0, (struct sockaddr *)&sin, &l);
+if (debug) fprintf(stderr, "recvfrom(%d) = %d (%d)\n", sd, nr, l);
+ if (nr < 0 && errno != EINTR) {
+ perror("recvfrom(sd)");
+ quit(12);
+ }
+ if (nr <= 0) continue;
+
+ if (reflect) {
+ /*
+ * write packet back so it can continue
+ * being processed by any further IPFW rules.
+ */
+ l = sizeof(sin);
+ r = sendto(sd, buf, nr, 0, (struct sockaddr *)&sin, l);
+if (debug) fprintf(stderr, " sendto(%d) = %d\n", sd, r);
+ if (r < 0) { perror("sendto(sd)"); quit(13); }
+ }
+
+ /*
+ * check maximums, if any.
+ * but don't quit if must continue reflecting packets.
+ */
+ if (maxpkts) {
+ totpkts++;
+ if (totpkts > maxpkts) {
+ if (reflect == 1) continue;
+ quit(0);
+ }
+ }
+ if (maxbytes) {
+ totbytes += nr;
+ if (totbytes > maxbytes) {
+ if (reflect == 1) continue;
+ quit(0);
+ }
+ }
+
+ /*
+ * save packet in tcpdump(1) format. see pcap(3).
+ * divert packets are fully assembled. see ipfw(8).
+ */
+ (void) gettimeofday(&(phd.ts), NULL);
+ phd.caplen = phd.len = nr;
+ pcap_dump((u_char *)dp, &phd, buf);
+ if (ferror((FILE *)dp)) { perror(dumpf); quit(14); }
+ (void) fflush((FILE *)dp);
+ }
+
+ quit(0);
+}
diff --git a/usr.sbin/jail/Makefile b/usr.sbin/jail/Makefile
new file mode 100644
index 0000000..e92ced0
--- /dev/null
+++ b/usr.sbin/jail/Makefile
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+PROG= jail
+MAN= jail.8
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+
+WARNS?= 6
+
+.if ${MK_INET6_SUPPORT} != "no"
+CFLAGS+= -DINET6
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/jail/jail.8 b/usr.sbin/jail/jail.8
new file mode 100644
index 0000000..3a0767e
--- /dev/null
+++ b/usr.sbin/jail/jail.8
@@ -0,0 +1,701 @@
+.\"
+.\" Copyright (c) 2000, 2003 Robert N. M. Watson
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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.
+.\"
+.\"
+.\" ----------------------------------------------------------------------------
+.\" "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 January 24, 2009
+.Dt JAIL 8
+.Os
+.Sh NAME
+.Nm jail
+.Nd "imprison process and its descendants"
+.Sh SYNOPSIS
+.Nm
+.Op Fl hi
+.Op Fl n Ar jailname
+.Op Fl J Ar jid_file
+.Op Fl s Ar securelevel
+.Op Fl l u Ar username | Fl U Ar username
+.Ar path hostname [ip[,..]] command ...
+.Sh DESCRIPTION
+The
+.Nm
+utility imprisons a process and all future descendants.
+.Pp
+The options are as follows:
+.Bl -tag -width ".Fl u Ar username"
+.It Fl h
+Resolve
+.Va hostname
+and add all IP addresses returned by the resolver
+to the list of
+.Va ip-addresses
+for this prison.
+This may affect default address selection for outgoing IPv4 connections
+of prisons.
+The address first returned by the resolver for each address family
+will be used as primary address.
+See
+.Va ip-addresses
+further down for details.
+.It Fl i
+Output the jail identifier of the newly created jail.
+.It Fl n Ar jailname
+Assign and administrative name to the jail that can be used for management
+or auditing purposes.
+The system will
+.Sy not enforce
+the name to be unique.
+.It Fl J Ar jid_file
+Write a
+.Ar jid_file
+file, containing jail identifier, path, hostname, IP and
+command used to start the jail.
+.It Fl l
+Run program in the clean environment.
+The environment is discarded except for
+.Ev HOME , SHELL , TERM
+and
+.Ev USER .
+.Ev HOME
+and
+.Ev SHELL
+are set to the target login's default values.
+.Ev USER
+is set to the target login.
+.Ev TERM
+is imported from the current environment.
+The environment variables from the login class capability database for the
+target login are also set.
+.It Fl s Ar securelevel
+Sets the
+.Va kern.securelevel
+sysctl variable to the specified value inside the newly created jail.
+.It Fl u Ar username
+The user name from host environment as whom the
+.Ar command
+should run.
+.It Fl U Ar username
+The user name from jailed environment as whom the
+.Ar command
+should run.
+.It Ar path
+Directory which is to be the root of the prison.
+.It Ar hostname
+Hostname of the prison.
+.It Ar ip-addresses
+None, one or more IPv4 and IPv6 addresses assigned to the prison.
+The first address of each address family that was assigned to the jail will
+be used as the source address in case source address selection on unbound
+sockets cannot find a better match.
+It is only possible to start multiple jails with the same IP address,
+if none of the jails has more than this single overlapping IP address
+assigned to itself for the address family in question.
+.It Ar command
+Pathname of the program which is to be executed.
+.El
+.Pp
+Jails are typically set up using one of two philosophies: either to
+constrain a specific application (possibly running with privilege), or
+to create a
+.Dq "virtual system image"
+running a variety of daemons and services.
+In both cases, a fairly complete file system install of
+.Fx
+is
+required, so as to provide the necessary command line tools, daemons,
+libraries, application configuration files, etc.
+However, for a virtual server configuration, a fair amount of
+additional work is required so as to configure the
+.Dq boot
+process.
+This manual page documents the configuration steps necessary to support
+either of these steps, although the configuration steps may be
+refined based on local requirements.
+.Pp
+Please see the
+.Xr jail 2
+man page for further details.
+.Sh EXAMPLES
+.Ss "Setting up a Jail Directory Tree"
+To set up a jail directory tree containing an entire
+.Fx
+distribution, the following
+.Xr sh 1
+command script can be used:
+.Bd -literal
+D=/here/is/the/jail
+cd /usr/src
+mkdir -p $D
+make world DESTDIR=$D
+make distribution DESTDIR=$D
+mount -t devfs devfs $D/dev
+.Ed
+.Pp
+NOTE: It is important that only appropriate device nodes in devfs be
+exposed to a jail; access to disk devices in the jail may permit processes
+in the jail to bypass the jail sandboxing by modifying files outside of
+the jail.
+See
+.Xr devfs 8
+for information on how to use devfs rules to limit access to entries
+in the per-jail devfs.
+A simple devfs ruleset for jails is available as ruleset #4 in
+.Pa /etc/defaults/devfs.rules .
+.Pp
+In many cases this example would put far more in the jail than needed.
+In the other extreme case a jail might contain only one file:
+the executable to be run in the jail.
+.Pp
+We recommend experimentation and caution that it is a lot easier to
+start with a
+.Dq fat
+jail and remove things until it stops working,
+than it is to start with a
+.Dq thin
+jail and add things until it works.
+.Ss "Setting Up a Jail"
+Do what was described in
+.Sx "Setting Up a Jail Directory Tree"
+to build the jail directory tree.
+For the sake of this example, we will
+assume you built it in
+.Pa /data/jail/192.0.2.100 ,
+named for the jailed IP address.
+Substitute below as needed with your
+own directory, IP address, and hostname.
+.Ss "Setting up the Host Environment"
+First, you will want to set up your real system's environment to be
+.Dq jail-friendly .
+For consistency, we will refer to the parent box as the
+.Dq "host environment" ,
+and to the jailed virtual machine as the
+.Dq "jail environment" .
+Since jail is implemented using IP aliases, one of the first things to do
+is to disable IP services on the host system that listen on all local
+IP addresses for a service.
+If a network service is present in the host environment that binds all
+available IP addresses rather than specific IP addresses, it may service
+requests sent to jail IP addresses if the jail did not bind the port.
+This means changing
+.Xr inetd 8
+to only listen on the
+appropriate IP address, and so forth.
+Add the following to
+.Pa /etc/rc.conf
+in the host environment:
+.Bd -literal -offset indent
+sendmail_enable="NO"
+inetd_flags="-wW -a 192.0.2.23"
+rpcbind_enable="NO"
+.Ed
+.Pp
+.Li 192.0.2.23
+is the native IP address for the host system, in this example.
+Daemons that run out of
+.Xr inetd 8
+can be easily set to use only the specified host IP address.
+Other daemons
+will need to be manually configured\(emfor some this is possible through
+the
+.Xr rc.conf 5
+flags entries; for others it is necessary to modify per-application
+configuration files, or to recompile the applications.
+The following frequently deployed services must have their individual
+configuration files modified to limit the application to listening
+to a specific IP address:
+.Pp
+To configure
+.Xr sshd 8 ,
+it is necessary to modify
+.Pa /etc/ssh/sshd_config .
+.Pp
+To configure
+.Xr sendmail 8 ,
+it is necessary to modify
+.Pa /etc/mail/sendmail.cf .
+.Pp
+For
+.Xr named 8 ,
+it is necessary to modify
+.Pa /etc/namedb/named.conf .
+.Pp
+In addition, a number of services must be recompiled in order to run
+them in the host environment.
+This includes most applications providing services using
+.Xr rpc 3 ,
+such as
+.Xr rpcbind 8 ,
+.Xr nfsd 8 ,
+and
+.Xr mountd 8 .
+In general, applications for which it is not possible to specify which
+IP address to bind should not be run in the host environment unless they
+should also service requests sent to jail IP addresses.
+Attempting to serve
+NFS from the host environment may also cause confusion, and cannot be
+easily reconfigured to use only specific IPs, as some NFS services are
+hosted directly from the kernel.
+Any third-party network software running
+in the host environment should also be checked and configured so that it
+does not bind all IP addresses, which would result in those services' also
+appearing to be offered by the jail environments.
+.Pp
+Once
+these daemons have been disabled or fixed in the host environment, it is
+best to reboot so that all daemons are in a known state, to reduce the
+potential for confusion later (such as finding that when you send mail
+to a jail, and its sendmail is down, the mail is delivered to the host,
+etc.).
+.Ss "Configuring the Jail"
+Start any jail for the first time without configuring the network
+interface so that you can clean it up a little and set up accounts.
+As
+with any machine (virtual or not) you will need to set a root password, time
+zone, etc.
+Some of these steps apply only if you intend to run a full virtual server
+inside the jail; others apply both for constraining a particular application
+or for running a virtual server.
+.Pp
+Start a shell in the jail:
+.Pp
+.Dl "jail /data/jail/192.0.2.100 testhostname 192.0.2.100 /bin/sh"
+.Pp
+Assuming no errors, you will end up with a shell prompt within the jail.
+You can now run
+.Pa /usr/sbin/sysinstall
+and do the post-install configuration to set various configuration options,
+or perform these actions manually by editing
+.Pa /etc/rc.conf ,
+etc.
+.Pp
+.Bl -bullet -offset indent -compact
+.It
+Create an empty
+.Pa /etc/fstab
+to quell startup warnings about missing fstab (virtual server only)
+.It
+Disable the port mapper
+.Pa ( /etc/rc.conf :
+.Li rpcbind_enable="NO" )
+(virtual server only)
+.It
+Configure
+.Pa /etc/resolv.conf
+so that name resolution within the jail will work correctly
+.It
+Run
+.Xr newaliases 1
+to quell
+.Xr sendmail 8
+warnings.
+.It
+Disable interface configuration to quell startup warnings about
+.Xr ifconfig 8
+.Pq Li network_interfaces=""
+(virtual server only)
+.It
+Set a root password, probably different from the real host system
+.It
+Set the timezone
+.It
+Add accounts for users in the jail environment
+.It
+Install any packages the environment requires
+.El
+.Pp
+You may also want to perform any package-specific configuration (web servers,
+SSH servers, etc), patch up
+.Pa /etc/syslog.conf
+so it logs as you would like, etc.
+If you are not using a virtual server, you may wish to modify
+.Xr syslogd 8
+in the host environment to listen on the syslog socket in the jail
+environment; in this example, the syslog socket would be stored in
+.Pa /data/jail/192.0.2.100/var/run/log .
+.Pp
+Exit from the shell, and the jail will be shut down.
+.Ss "Starting the Jail"
+You are now ready to restart the jail and bring up the environment with
+all of its daemons and other programs.
+If you are running a single application in the jail, substitute the
+command used to start the application for
+.Pa /etc/rc
+in the examples below.
+To start a virtual server environment,
+.Pa /etc/rc
+is run to launch various daemons and services.
+To do this, first bring up the
+virtual host interface, and then start the jail's
+.Pa /etc/rc
+script from within the jail.
+.Pp
+NOTE: If you plan to allow untrusted users to have root access inside the
+jail, you may wish to consider setting the
+.Va security.jail.set_hostname_allowed
+sysctl variable to 0.
+Please see the management discussion later in this document as to why this
+may be a good idea.
+If you do decide to set this variable,
+it must be set before starting any jails, and once each boot.
+.Bd -literal -offset indent
+ifconfig ed0 inet alias 192.0.2.100/32
+mount -t procfs proc /data/jail/192.0.2.100/proc
+jail /data/jail/192.0.2.100 testhostname 192.0.2.100 \\
+ /bin/sh /etc/rc
+.Ed
+.Pp
+A few warnings will be produced, because most
+.Xr sysctl 8
+configuration variables cannot be set from within the jail, as they are
+global across all jails and the host environment.
+However, it should all
+work properly.
+You should be able to see
+.Xr inetd 8 ,
+.Xr syslogd 8 ,
+and other processes running within the jail using
+.Xr ps 1 ,
+with the
+.Ql J
+flag appearing beside jailed processes.
+To see an active list of jails, use the
+.Xr jls 8
+utility.
+You should also be able to
+.Xr telnet 1
+to the hostname or IP address of the jailed environment, and log
+in using the accounts you created previously.
+.Pp
+It is possible to have jails started at boot time.
+Please refer to the
+.Dq jail_*
+variables in
+.Xr rc.conf 5
+for more information.
+The
+.Xr rc 8
+jail script provides a flexible system to start/stop jails:
+.Bd -literal
+/etc/rc.d/jail start
+/etc/rc.d/jail stop
+/etc/rc.d/jail start myjail
+/etc/rc.d/jail stop myjail
+.Ed
+.Ss "Managing the Jail"
+Normal machine shutdown commands, such as
+.Xr halt 8 ,
+.Xr reboot 8 ,
+and
+.Xr shutdown 8 ,
+cannot be used successfully within the jail.
+To kill all processes in a
+jail, you may log into the jail and, as root, use one of the following
+commands, depending on what you want to accomplish:
+.Pp
+.Bd -literal -offset indent
+kill -TERM -1
+kill -KILL -1
+.Ed
+.Pp
+This will send the
+.Dv SIGTERM
+or
+.Dv SIGKILL
+signals to all processes in the jail from within the jail.
+Depending on
+the intended use of the jail, you may also want to run
+.Pa /etc/rc.shutdown
+from within the jail.
+To kill processes from outside the jail, use the
+.Xr jexec 8
+utility in conjunction with the one of the
+.Xr kill 1
+commands above.
+.Pp
+The
+.Pa /proc/ Ns Ar pid Ns Pa /status
+file contains, as its last field, the hostname of the jail in which the
+process runs, or
+.Dq Li -
+to indicate that the process is not running within a jail.
+The
+.Xr ps 1
+command also shows a
+.Ql J
+flag for processes in a jail.
+However, the hostname for a jail may be, by
+default, modified from within the jail, so the
+.Pa /proc
+status entry is unreliable by default.
+To disable the setting of the hostname
+from within a jail, set the
+.Va security.jail.set_hostname_allowed
+sysctl variable in the host environment to 0, which will affect all jails.
+You can have this sysctl set on each boot using
+.Xr sysctl.conf 5 .
+Just add the following line to
+.Pa /etc/sysctl.conf :
+.Pp
+.Dl security.jail.set_hostname_allowed=0
+.Pp
+You can also list/kill processes based on their jail ID.
+To show processes and their jail ID, use the following command:
+.Pp
+.Dl "ps ax -o pid,jid,args"
+.Pp
+To show and then kill processes in jail number 3 use the following commands:
+.Bd -literal -offset indent
+pgrep -lfj 3
+pkill -j 3
+.Ed
+or:
+.Pp
+.Dl "killall -j 3"
+.Ss "Jails and File Systems"
+It is not possible to
+.Xr mount 8
+or
+.Xr umount 8
+any file system inside a jail unless the file system is marked
+jail-friendly.
+See
+.Va security.jail.mount_allowed
+in the
+.Va "Sysctl MIB Entries"
+section.
+.Pp
+Multiple jails sharing the same file system can influence each other.
+For example a user in one jail can fill the file system also
+leaving no space for processes in the other jail.
+Trying to use
+.Xr quota 1
+to prevent this will not work either as the file system quotas
+are not aware of jails but only look at the user and group IDs.
+This means the same user ID in two jails share the same file
+system quota.
+One would need to use one file system per jail to make this working.
+.Ss "Sysctl MIB Entries"
+Certain aspects of the jail containments environment may be modified from
+the host environment using
+.Xr sysctl 8
+MIB variables.
+Currently, these variables affect all jails on the system, although in
+the future this functionality may be finer grained.
+.Bl -tag -width XXX
+.It Va security.jail.allow_raw_sockets
+This MIB entry determines whether or not prison root is allowed to
+create raw sockets.
+Setting this MIB to 1 allows utilities like
+.Xr ping 8
+and
+.Xr traceroute 8
+to operate inside the prison.
+If this MIB
+is set, the source IP addresses are enforced to comply
+with the IP address bound to the jail, regardless of whether or not
+the
+.Dv IP_HDRINCL
+flag has been set on the socket.
+Since raw sockets can be used to configure
+and interact with various network subsystems, extra caution should be used
+where privileged access to jails is given out to untrusted parties.
+As such,
+by default this option is disabled.
+.It Va security.jail.enforce_statfs
+This MIB entry determines which information processes in a jail are
+able to get about mount-points.
+It affects the behaviour of the following syscalls:
+.Xr statfs 2 ,
+.Xr fstatfs 2 ,
+.Xr getfsstat 2
+and
+.Xr fhstatfs 2
+(as well as similar compatibility syscalls).
+When set to 0, all mount-points are available without any restrictions.
+When set to 1, only mount-points below the jail's chroot directory are
+visible.
+In addition to that, the path to the jail's chroot directory is removed
+from the front of their pathnames.
+When set to 2 (default), above syscalls can operate only on a mount-point
+where the jail's chroot directory is located.
+.It Va security.jail.set_hostname_allowed
+This MIB entry determines whether or not processes within a jail are
+allowed to change their hostname via
+.Xr hostname 1
+or
+.Xr sethostname 3 .
+In the current jail implementation, the ability to set the hostname from
+within the jail can impact management tools relying on the accuracy of jail
+information in
+.Pa /proc .
+As such, this should be disabled in environments where privileged access to
+jails is given out to untrusted parties.
+.It Va security.jail.socket_unixiproute_only
+The jail functionality binds an IPv4 address to each jail, and limits
+access to other network addresses in the IPv4 space that may be available
+in the host environment.
+However, jail is not currently able to limit access to other network
+protocol stacks that have not had jail functionality added to them.
+As such, by default, processes within jails may only access protocols
+in the following domains:
+.Dv PF_LOCAL , PF_INET ,
+and
+.Dv PF_ROUTE ,
+permitting them access to
+.Ux
+domain sockets,
+IPv4 addresses, and routing sockets.
+To enable access to other domains, this MIB variable may be set to
+0.
+.It Va security.jail.sysvipc_allowed
+This MIB entry determines whether or not processes within a jail have access
+to System V IPC primitives.
+In the current jail implementation, System V primitives share a single
+namespace across the host and jail environments, meaning that processes
+within a jail would be able to communicate with (and potentially interfere
+with) processes outside of the jail, and in other jails.
+As such, this functionality is disabled by default, but can be enabled
+by setting this MIB entry to 1.
+.It Va security.jail.chflags_allowed
+This MIB entry determines how a privileged user inside a jail will be
+treated by
+.Xr chflags 2 .
+If zero, such users are treated as unprivileged, and are unable to set
+or clear system file flags; if non-zero, such users are treated as
+privileged, and may manipulate system file flags subject to the usual
+constraints on
+.Va kern.securelevel .
+.It Va security.jail.mount_allowed
+This MIB entry determines if a privileged user inside a jail will be
+able to mount and unmount file system types marked as jail-friendly.
+The
+.Xr lsvfs 1
+command can be used to find file system types available for mount from within
+a jail.
+This functionality is disabled by default, but can be enabled by setting this
+MIB entry to 1.
+.It Va security.jail.jail_max_af_ips
+This MIB entry determines how may address per address family a prison
+may have. The default is 255.
+.El
+.Pp
+The read-only sysctl variable
+.Va security.jail.jailed
+can be used to determine if a process is running inside a jail (value
+is one) or not (value is zero).
+.Pp
+The
+.Va security.jail.list
+MIB entry is read-only and it returns an array of
+.Vt "struct xprison"
+defined in
+.In sys/jail.h .
+It is recommended to use the
+.Xr jls 8
+utility to see current active list of jails.
+.Pp
+There are currently two MIB related variables that have per-jail settings.
+Changes to these variables by a jailed process do not effect the host
+environment, only the jail environment.
+The variables are
+.Va kern.securelevel
+and
+.Va kern.hostname .
+.Sh SEE ALSO
+.Xr killall 1 ,
+.Xr lsvfs 1 ,
+.Xr newaliases 1 ,
+.Xr pgrep 1 ,
+.Xr pkill 1 ,
+.Xr ps 1 ,
+.Xr quota 1 ,
+.Xr chroot 2 ,
+.Xr jail 2 ,
+.Xr jail_attach 2 ,
+.Xr procfs 5 ,
+.Xr rc.conf 5 ,
+.Xr sysctl.conf 5 ,
+.Xr devfs 8 ,
+.Xr halt 8 ,
+.Xr inetd 8 ,
+.Xr jexec 8 ,
+.Xr jls 8 ,
+.Xr mount 8 ,
+.Xr named 8 ,
+.Xr reboot 8 ,
+.Xr rpcbind 8 ,
+.Xr sendmail 8 ,
+.Xr shutdown 8 ,
+.Xr sysctl 8 ,
+.Xr syslogd 8 ,
+.Xr umount 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 4.0 .
+.Sh AUTHORS
+.An -nosplit
+The jail feature was written by
+.An Poul-Henning Kamp
+for R&D Associates
+.Pa http://www.rndassociates.com/
+who contributed it to
+.Fx .
+.Pp
+.An Robert Watson
+wrote the extended documentation, found a few bugs, added
+a few new features, and cleaned up the userland jail environment.
+.Pp
+.An Bjoern A. Zeeb
+added multi-IP jail support for IPv4 and IPv6 based on a patch
+originally done by
+.An Pawel Jakub Dawidek
+for IPv4.
+.Sh BUGS
+Jail currently lacks the ability to allow access to
+specific jail information via
+.Xr ps 1
+as opposed to
+.Xr procfs 5 .
+Similarly, it might be a good idea to add an
+address alias flag such that daemons listening on all IPs
+.Pq Dv INADDR_ANY
+will not bind on that address, which would facilitate building a safe
+host environment such that host daemons do not impose on services offered
+from within jails.
+Currently, the simplest answer is to minimize services
+offered on the host, possibly limiting it to services offered from
+.Xr inetd 8
+which is easily configurable.
diff --git a/usr.sbin/jail/jail.c b/usr.sbin/jail/jail.c
new file mode 100644
index 0000000..be337b7
--- /dev/null
+++ b/usr.sbin/jail/jail.c
@@ -0,0 +1,412 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "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
+ * ----------------------------------------------------------------------------
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/jail.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <err.h>
+#include <errno.h>
+#include <grp.h>
+#include <login_cap.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <string.h>
+#include <unistd.h>
+
+static void usage(void);
+static int add_addresses(struct addrinfo *);
+static struct in_addr *copy_addr4(void);
+#ifdef INET6
+static struct in6_addr *copy_addr6(void);
+#endif
+
+extern char **environ;
+
+struct addr4entry {
+ STAILQ_ENTRY(addr4entry) addr4entries;
+ struct in_addr ip4;
+ int count;
+};
+struct addr6entry {
+ STAILQ_ENTRY(addr6entry) addr6entries;
+#ifdef INET6
+ struct in6_addr ip6;
+#endif
+ int count;
+};
+STAILQ_HEAD(addr4head, addr4entry) addr4 = STAILQ_HEAD_INITIALIZER(addr4);
+STAILQ_HEAD(addr6head, addr6entry) addr6 = STAILQ_HEAD_INITIALIZER(addr6);
+
+#define GET_USER_INFO do { \
+ pwd = getpwnam(username); \
+ if (pwd == NULL) { \
+ if (errno) \
+ err(1, "getpwnam: %s", username); \
+ else \
+ errx(1, "%s: no such user", username); \
+ } \
+ lcap = login_getpwclass(pwd); \
+ if (lcap == NULL) \
+ err(1, "getpwclass: %s", username); \
+ ngroups = NGROUPS; \
+ if (getgrouplist(username, pwd->pw_gid, groups, &ngroups) != 0) \
+ err(1, "getgrouplist: %s", username); \
+} while (0)
+
+int
+main(int argc, char **argv)
+{
+ login_cap_t *lcap = NULL;
+ struct jail j;
+ struct passwd *pwd = NULL;
+ gid_t groups[NGROUPS];
+ int ch, error, i, ngroups, securelevel;
+ int hflag, iflag, Jflag, lflag, uflag, Uflag;
+ char path[PATH_MAX], *jailname, *ep, *username, *JidFile, *ip;
+ static char *cleanenv;
+ const char *shell, *p = NULL;
+ long ltmp;
+ FILE *fp;
+ struct addrinfo hints, *res0;
+
+ hflag = iflag = Jflag = lflag = uflag = Uflag = 0;
+ securelevel = -1;
+ jailname = username = JidFile = cleanenv = NULL;
+ fp = NULL;
+
+ while ((ch = getopt(argc, argv, "hiln:s:u:U:J:")) != -1) {
+ switch (ch) {
+ case 'h':
+ hflag = 1;
+ break;
+ case 'i':
+ iflag = 1;
+ break;
+ case 'J':
+ JidFile = optarg;
+ Jflag = 1;
+ break;
+ case 'n':
+ jailname = optarg;
+ break;
+ case 's':
+ ltmp = strtol(optarg, &ep, 0);
+ if (*ep || ep == optarg || ltmp > INT_MAX || !ltmp)
+ errx(1, "invalid securelevel: `%s'", optarg);
+ securelevel = ltmp;
+ break;
+ case 'u':
+ username = optarg;
+ uflag = 1;
+ break;
+ case 'U':
+ username = optarg;
+ Uflag = 1;
+ break;
+ case 'l':
+ lflag = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc < 4)
+ usage();
+ if (uflag && Uflag)
+ usage();
+ if (lflag && username == NULL)
+ usage();
+ if (uflag)
+ GET_USER_INFO;
+ if (realpath(argv[0], path) == NULL)
+ err(1, "realpath: %s", argv[0]);
+ if (chdir(path) != 0)
+ err(1, "chdir: %s", path);
+ /* Initialize struct jail. */
+ memset(&j, 0, sizeof(j));
+ j.version = JAIL_API_VERSION;
+ j.path = path;
+ j.hostname = argv[1];
+ if (jailname != NULL)
+ j.jailname = jailname;
+
+ /* Handle IP addresses. If requested resolve hostname too. */
+ bzero(&hints, sizeof(struct addrinfo));
+ hints.ai_protocol = IPPROTO_TCP;
+ hints.ai_socktype = SOCK_STREAM;
+ if (JAIL_API_VERSION < 2)
+ hints.ai_family = PF_INET;
+ else
+ hints.ai_family = PF_UNSPEC;
+ /* Handle hostname. */
+ if (hflag != 0) {
+ error = getaddrinfo(j.hostname, NULL, &hints, &res0);
+ if (error != 0)
+ errx(1, "failed to handle hostname: %s",
+ gai_strerror(error));
+ error = add_addresses(res0);
+ freeaddrinfo(res0);
+ if (error != 0)
+ errx(1, "failed to add addresses.");
+ }
+ /* Handle IP addresses. */
+ hints.ai_flags = AI_NUMERICHOST;
+ ip = strtok(argv[2], ",");
+ while (ip != NULL) {
+ error = getaddrinfo(ip, NULL, &hints, &res0);
+ if (error != 0)
+ errx(1, "failed to handle ip: %s", gai_strerror(error));
+ error = add_addresses(res0);
+ freeaddrinfo(res0);
+ if (error != 0)
+ errx(1, "failed to add addresses.");
+ ip = strtok(NULL, ",");
+ }
+ /* Count IP addresses and add them to struct jail. */
+ if (!STAILQ_EMPTY(&addr4)) {
+ j.ip4s = STAILQ_FIRST(&addr4)->count;
+ j.ip4 = copy_addr4();
+ if (j.ip4s > 0 && j.ip4 == NULL)
+ errx(1, "copy_addr4()");
+ }
+#ifdef INET6
+ if (!STAILQ_EMPTY(&addr6)) {
+ j.ip6s = STAILQ_FIRST(&addr6)->count;
+ j.ip6 = copy_addr6();
+ if (j.ip6s > 0 && j.ip6 == NULL)
+ errx(1, "copy_addr6()");
+ }
+#endif
+
+ if (Jflag) {
+ fp = fopen(JidFile, "w");
+ if (fp == NULL)
+ errx(1, "Could not create JidFile: %s", JidFile);
+ }
+ i = jail(&j);
+ if (i == -1)
+ err(1, "syscall failed with");
+ if (iflag) {
+ printf("%d\n", i);
+ fflush(stdout);
+ }
+ if (Jflag) {
+ if (fp != NULL) {
+ fprintf(fp, "%d\t%s\t%s\t%s\t%s\n",
+ i, j.path, j.hostname, argv[2], argv[3]);
+ (void)fclose(fp);
+ } else {
+ errx(1, "Could not write JidFile: %s", JidFile);
+ }
+ }
+ if (securelevel > 0) {
+ if (sysctlbyname("kern.securelevel", NULL, 0, &securelevel,
+ sizeof(securelevel)))
+ err(1, "Can not set securelevel to %d", securelevel);
+ }
+ if (username != NULL) {
+ if (Uflag)
+ GET_USER_INFO;
+ if (lflag) {
+ p = getenv("TERM");
+ environ = &cleanenv;
+ }
+ if (setgroups(ngroups, groups) != 0)
+ err(1, "setgroups");
+ if (setgid(pwd->pw_gid) != 0)
+ err(1, "setgid");
+ if (setusercontext(lcap, pwd, pwd->pw_uid,
+ LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN) != 0)
+ err(1, "setusercontext");
+ login_close(lcap);
+ }
+ if (lflag) {
+ if (*pwd->pw_shell)
+ shell = pwd->pw_shell;
+ else
+ shell = _PATH_BSHELL;
+ if (chdir(pwd->pw_dir) < 0)
+ errx(1, "no home directory");
+ setenv("HOME", pwd->pw_dir, 1);
+ setenv("SHELL", shell, 1);
+ setenv("USER", pwd->pw_name, 1);
+ if (p)
+ setenv("TERM", p, 1);
+ }
+ if (execv(argv[3], argv + 3) != 0)
+ err(1, "execv: %s", argv[3]);
+ exit(0);
+}
+
+static void
+usage(void)
+{
+
+ (void)fprintf(stderr, "%s%s%s\n",
+ "usage: jail [-hi] [-n jailname] [-J jid_file] ",
+ "[-s securelevel] [-l -u username | -U username] ",
+ "path hostname [ip[,..]] command ...");
+ exit(1);
+}
+
+static int
+add_addresses(struct addrinfo *res0)
+{
+ int error;
+ struct addrinfo *res;
+ struct addr4entry *a4p;
+ struct sockaddr_in *sai;
+#ifdef INET6
+ struct addr6entry *a6p;
+ struct sockaddr_in6 *sai6;
+#endif
+ int count;
+
+ error = 0;
+ for (res = res0; res && error == 0; res = res->ai_next) {
+ switch (res->ai_family) {
+ case AF_INET:
+ sai = (struct sockaddr_in *)(void *)res->ai_addr;
+ STAILQ_FOREACH(a4p, &addr4, addr4entries) {
+ if (bcmp(&sai->sin_addr, &a4p->ip4,
+ sizeof(struct in_addr)) == 0) {
+ err(1, "Ignoring duplicate IPv4 address.");
+ break;
+ }
+ }
+ a4p = (struct addr4entry *) malloc(
+ sizeof(struct addr4entry));
+ if (a4p == NULL) {
+ error = 1;
+ break;
+ }
+ bzero(a4p, sizeof(struct addr4entry));
+ bcopy(&sai->sin_addr, &a4p->ip4,
+ sizeof(struct in_addr));
+ if (!STAILQ_EMPTY(&addr4))
+ count = STAILQ_FIRST(&addr4)->count;
+ else
+ count = 0;
+ STAILQ_INSERT_TAIL(&addr4, a4p, addr4entries);
+ STAILQ_FIRST(&addr4)->count = count + 1;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ sai6 = (struct sockaddr_in6 *)(void *)res->ai_addr;
+ STAILQ_FOREACH(a6p, &addr6, addr6entries) {
+ if (bcmp(&sai6->sin6_addr, &a6p->ip6,
+ sizeof(struct in6_addr)) == 0) {
+ err(1, "Ignoring duplicate IPv6 address.");
+ break;
+ }
+ }
+ a6p = (struct addr6entry *) malloc(
+ sizeof(struct addr6entry));
+ if (a6p == NULL) {
+ error = 1;
+ break;
+ }
+ bzero(a6p, sizeof(struct addr6entry));
+ bcopy(&sai6->sin6_addr, &a6p->ip6,
+ sizeof(struct in6_addr));
+ if (!STAILQ_EMPTY(&addr6))
+ count = STAILQ_FIRST(&addr6)->count;
+ else
+ count = 0;
+ STAILQ_INSERT_TAIL(&addr6, a6p, addr6entries);
+ STAILQ_FIRST(&addr6)->count = count + 1;
+ break;
+#endif
+ default:
+ err(1, "Address family %d not supported. Ignoring.\n",
+ res->ai_family);
+ break;
+ }
+ }
+
+ return (error);
+}
+
+static struct in_addr *
+copy_addr4(void)
+{
+ size_t len;
+ struct in_addr *ip4s, *p, ia;
+ struct addr4entry *a4p;
+
+ if (STAILQ_EMPTY(&addr4))
+ return NULL;
+
+ len = STAILQ_FIRST(&addr4)->count * sizeof(struct in_addr);
+
+ ip4s = p = (struct in_addr *)malloc(len);
+ if (ip4s == NULL)
+ return (NULL);
+
+ bzero(p, len);
+
+ while (!STAILQ_EMPTY(&addr4)) {
+ a4p = STAILQ_FIRST(&addr4);
+ STAILQ_REMOVE_HEAD(&addr4, addr4entries);
+ ia.s_addr = a4p->ip4.s_addr;
+ bcopy(&ia, p, sizeof(struct in_addr));
+ p++;
+ free(a4p);
+ }
+
+ return (ip4s);
+}
+
+#ifdef INET6
+static struct in6_addr *
+copy_addr6(void)
+{
+ size_t len;
+ struct in6_addr *ip6s, *p;
+ struct addr6entry *a6p;
+
+ if (STAILQ_EMPTY(&addr6))
+ return NULL;
+
+ len = STAILQ_FIRST(&addr6)->count * sizeof(struct in6_addr);
+
+ ip6s = p = (struct in6_addr *)malloc(len);
+ if (ip6s == NULL)
+ return (NULL);
+
+ bzero(p, len);
+
+ while (!STAILQ_EMPTY(&addr6)) {
+ a6p = STAILQ_FIRST(&addr6);
+ STAILQ_REMOVE_HEAD(&addr6, addr6entries);
+ bcopy(&a6p->ip6, p, sizeof(struct in6_addr));
+ p++;
+ free(a6p);
+ }
+
+ return (ip6s);
+}
+#endif
+
diff --git a/usr.sbin/jexec/Makefile b/usr.sbin/jexec/Makefile
new file mode 100644
index 0000000..049ccd4
--- /dev/null
+++ b/usr.sbin/jexec/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+PROG= jexec
+MAN= jexec.8
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+WARNS?= 6
+
+CFLAGS+= -DSUPPORT_OLD_XPRISON
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/jexec/jexec.8 b/usr.sbin/jexec/jexec.8
new file mode 100644
index 0000000..bdda23d
--- /dev/null
+++ b/usr.sbin/jexec/jexec.8
@@ -0,0 +1,94 @@
+.\"
+.\" Copyright (c) 2003 Mike Barcroft <mike@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 November 29, 2008
+.Dt JEXEC 8
+.Os
+.Sh NAME
+.Nm jexec
+.Nd "execute a command inside an existing jail"
+.Sh SYNOPSIS
+.Nm
+.Op Fl u Ar username | Fl U Ar username
+.Op Fl n Ar jailname
+.Ar jid command ...
+.Sh DESCRIPTION
+The
+.Nm
+utility executes
+.Ar command
+inside the jail identified by either
+.Ar jailname
+or
+.Ar jid
+or both.
+.Pp
+If the jail cannot be identified uniquely by the given parameters,
+an error message is printed.
+.Nm
+will also check the state of the jail (once supported) to be
+.Dv ALIVE
+and ignore jails in other states.
+The mandatory argument
+.Ar jid
+is the unique jail identifier as given by
+.Xr jls 8 .
+In case you only want to match on other criteria, give an empty string.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl n Ar jailname
+The name of the jail, if given upon creation of the jail.
+This is not the hostname of the jail.
+.It Fl u Ar username
+The user name from host environment as whom the
+.Ar command
+should run.
+.It Fl U Ar username
+The user name from jailed environment as whom the
+.Ar command
+should run.
+.El
+.Sh SEE ALSO
+.Xr jail_attach 2 ,
+.Xr jail 8 ,
+.Xr jls 8
+.Sh HISTORY
+The
+.Nm
+utility was added in
+.Fx 5.1 .
+.Sh BUGS
+If the jail is not identified by
+.Ar jid
+there is a possible race in between the lookup of the jail
+and executing the command inside the jail.
+Giving a
+.Ar jid
+has a similar race as another process can stop the jail and
+start another one after the user looked up the
+.Ar jid .
diff --git a/usr.sbin/jexec/jexec.c b/usr.sbin/jexec/jexec.c
new file mode 100644
index 0000000..9d788dd
--- /dev/null
+++ b/usr.sbin/jexec/jexec.c
@@ -0,0 +1,296 @@
+/*-
+ * Copyright (c) 2003 Mike Barcroft <mike@FreeBSD.org>
+ * Copyright (c) 2008 Bjoern A. Zeeb <bz@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 <sys/param.h>
+#include <sys/jail.h>
+#include <sys/sysctl.h>
+
+#include <netinet/in.h>
+
+#include <err.h>
+#include <errno.h>
+#include <login_cap.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pwd.h>
+#include <unistd.h>
+
+static void usage(void);
+
+#ifdef SUPPORT_OLD_XPRISON
+static
+char *lookup_xprison_v1(void *p, char *end, int *id)
+{
+ struct xprison_v1 *xp;
+
+ if (id == NULL)
+ errx(1, "Internal error. Invalid ID pointer.");
+
+ if ((char *)p + sizeof(struct xprison_v1) > end)
+ errx(1, "Invalid length for jail");
+
+ xp = (struct xprison_v1 *)p;
+
+ *id = xp->pr_id;
+ return ((char *)(xp + 1));
+}
+#endif
+
+static
+char *lookup_xprison_v3(void *p, char *end, int *id, char *jailname)
+{
+ struct xprison *xp;
+ char *q;
+ int ok;
+
+ if (id == NULL)
+ errx(1, "Internal error. Invalid ID pointer.");
+
+ if ((char *)p + sizeof(struct xprison) > end)
+ errx(1, "Invalid length for jail");
+
+ xp = (struct xprison *)p;
+ ok = 1;
+
+ /* Jail state and name. */
+ if (xp->pr_state < 0 || xp->pr_state >=
+ (int)((sizeof(prison_states) / sizeof(struct prison_state))))
+ errx(1, "Invalid jail state.");
+ else if (xp->pr_state != PRISON_STATE_ALIVE)
+ ok = 0;
+ if (jailname != NULL) {
+ if (xp->pr_name[0] == '\0')
+ ok = 0;
+ else if (strcmp(jailname, xp->pr_name) != 0)
+ ok = 0;
+ }
+
+ q = (char *)(xp + 1);
+ /* IPv4 addresses. */
+ q += (xp->pr_ip4s * sizeof(struct in_addr));
+ if ((char *)q > end)
+ errx(1, "Invalid length for jail");
+ /* IPv6 addresses. */
+ q += (xp->pr_ip6s * sizeof(struct in6_addr));
+ if ((char *)q > end)
+ errx(1, "Invalid length for jail");
+
+ if (ok)
+ *id = xp->pr_id;
+ return (q);
+}
+
+static int
+lookup_jail(int jid, char *jailname)
+{
+ size_t i, j, len;
+ void *p, *q;
+ int version, id, xid, count;
+
+ if (sysctlbyname("security.jail.list", NULL, &len, NULL, 0) == -1)
+ err(1, "sysctlbyname(): security.jail.list");
+
+ j = len;
+ for (i = 0; i < 4; i++) {
+ if (len <= 0)
+ exit(0);
+ p = q = malloc(len);
+ if (p == NULL)
+ err(1, "malloc()");
+
+ if (sysctlbyname("security.jail.list", q, &len, NULL, 0) == -1) {
+ if (errno == ENOMEM) {
+ free(p);
+ p = NULL;
+ len += j;
+ continue;
+ }
+ err(1, "sysctlbyname(): security.jail.list");
+ }
+ break;
+ }
+ if (p == NULL)
+ err(1, "sysctlbyname(): security.jail.list");
+ if (len < sizeof(int))
+ errx(1, "This is no prison. Kernel and userland out of sync?");
+ version = *(int *)p;
+ if (version > XPRISON_VERSION)
+ errx(1, "Sci-Fi prison. Kernel/userland out of sync?");
+
+ count = 0;
+ xid = -1;
+ for (; q != NULL && (char *)q + sizeof(int) < (char *)p + len;) {
+ version = *(int *)q;
+ if (version > XPRISON_VERSION)
+ errx(1, "Sci-Fi prison. Kernel/userland out of sync?");
+ id = -1;
+ switch (version) {
+#ifdef SUPPORT_OLD_XPRISON
+ case 1:
+ if (jailname != NULL)
+ errx(1, "Version 1 prisons did not "
+ "support jail names.");
+ q = lookup_xprison_v1(q, (char *)p + len, &id);
+ break;
+ case 2:
+ errx(1, "Version 2 was used by multi-IPv4 jail "
+ "implementations that never made it into the "
+ "official kernel.");
+ /* NOTREACHED */
+ break;
+#endif
+ case 3:
+ q = lookup_xprison_v3(q, (char *)p + len, &id, jailname);
+ break;
+ default:
+ errx(1, "Prison unknown. Kernel/userland out of sync?");
+ /* NOTREACHED */
+ break;
+ }
+ /* Possible match. */
+ if (id > 0) {
+ /* Do we have a jail ID to match as well? */
+ if (jid > 0) {
+ if (jid == id) {
+ xid = id;
+ count++;
+ }
+ } else {
+ xid = id;
+ count++;
+ }
+ }
+ }
+
+ free(p);
+
+ if (count != 1)
+ errx(1, "Could not uniquely identify the jail.");
+
+ return (xid);
+}
+
+#define GET_USER_INFO do { \
+ pwd = getpwnam(username); \
+ if (pwd == NULL) { \
+ if (errno) \
+ err(1, "getpwnam: %s", username); \
+ else \
+ errx(1, "%s: no such user", username); \
+ } \
+ lcap = login_getpwclass(pwd); \
+ if (lcap == NULL) \
+ err(1, "getpwclass: %s", username); \
+ ngroups = NGROUPS; \
+ if (getgrouplist(username, pwd->pw_gid, groups, &ngroups) != 0) \
+ err(1, "getgrouplist: %s", username); \
+} while (0)
+
+int
+main(int argc, char *argv[])
+{
+ int jid;
+ login_cap_t *lcap = NULL;
+ struct passwd *pwd = NULL;
+ gid_t groups[NGROUPS];
+ int ch, ngroups, uflag, Uflag;
+ char *jailname, *username;
+
+ ch = uflag = Uflag = 0;
+ jailname = username = NULL;
+ jid = -1;
+
+ while ((ch = getopt(argc, argv, "i:n:u:U:")) != -1) {
+ switch (ch) {
+ case 'n':
+ jailname = optarg;
+ break;
+ case 'u':
+ username = optarg;
+ uflag = 1;
+ break;
+ case 'U':
+ username = optarg;
+ Uflag = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc < 2)
+ usage();
+ if (strlen(argv[0]) > 0) {
+ jid = (int)strtol(argv[0], NULL, 10);
+ if (errno)
+ err(1, "Unable to parse jail ID.");
+ }
+ if (jid <= 0 && jailname == NULL) {
+ fprintf(stderr, "Neither jail ID nor jail name given.\n");
+ usage();
+ }
+ if (uflag && Uflag)
+ usage();
+ if (uflag)
+ GET_USER_INFO;
+ jid = lookup_jail(jid, jailname);
+ if (jid <= 0)
+ errx(1, "Cannot identify jail.");
+ if (jail_attach(jid) == -1)
+ err(1, "jail_attach(): %d", jid);
+ if (chdir("/") == -1)
+ err(1, "chdir(): /");
+ if (username != NULL) {
+ if (Uflag)
+ GET_USER_INFO;
+ if (setgroups(ngroups, groups) != 0)
+ err(1, "setgroups");
+ if (setgid(pwd->pw_gid) != 0)
+ err(1, "setgid");
+ if (setusercontext(lcap, pwd, pwd->pw_uid,
+ LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN) != 0)
+ err(1, "setusercontext");
+ login_close(lcap);
+ }
+ if (execvp(argv[1], argv + 1) == -1)
+ err(1, "execvp(): %s", argv[1]);
+ exit(0);
+}
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "%s%s\n",
+ "usage: jexec [-u username | -U username]",
+ " [-n jailname] jid command ...");
+ exit(1);
+}
diff --git a/usr.sbin/jls/Makefile b/usr.sbin/jls/Makefile
new file mode 100644
index 0000000..01294bd
--- /dev/null
+++ b/usr.sbin/jls/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= jls
+MAN= jls.8
+WARNS?= 6
+
+CFLAGS+= -DSUPPORT_OLD_XPRISON
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/jls/jls.8 b/usr.sbin/jls/jls.8
new file mode 100644
index 0000000..aff9848
--- /dev/null
+++ b/usr.sbin/jls/jls.8
@@ -0,0 +1,74 @@
+.\"
+.\" Copyright (c) 2003 Mike Barcroft <mike@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 November 29, 2008
+.Dt JLS 8
+.Os
+.Sh NAME
+.Nm jls
+.Nd "list jails"
+.Sh SYNOPSIS
+.Nm
+.Op Fl av
+.Sh DESCRIPTION
+The
+.Nm
+utility lists all jails.
+By default only active jails are listed.
+.Pp
+The options are as follows:
+.Bl -tag -width ".Fl a"
+.It Fl a
+Show jails in all states, not only active ones.
+.It Fl v
+Show more verbose information.
+This also lists cpusets, jail state, multi-IP, etc. instead of the
+classic single-IP jail output.
+.El
+.Pp
+Each jail is represented by rows which, depending on
+.Fl v ,
+contain the following columns:
+.Bl -item -offset indent -compact
+.It
+jail identifier (JID), hostname and path
+.It
+jail state and name
+.It
+jail cpuset
+.It
+followed by one IP adddress per line.
+.El
+.Sh SEE ALSO
+.Xr jail 2 ,
+.Xr jail 8 ,
+.Xr jexec 8
+.Sh HISTORY
+The
+.Nm
+utility was added in
+.Fx 5.1 .
diff --git a/usr.sbin/jls/jls.c b/usr.sbin/jls/jls.c
new file mode 100644
index 0000000..95f17d4
--- /dev/null
+++ b/usr.sbin/jls/jls.c
@@ -0,0 +1,257 @@
+/*-
+ * Copyright (c) 2003 Mike Barcroft <mike@FreeBSD.org>
+ * Copyright (c) 2008 Bjoern A. Zeeb <bz@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 <sys/param.h>
+#include <sys/types.h>
+#include <sys/jail.h>
+#include <sys/sysctl.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define FLAG_A 0x00001
+#define FLAG_V 0x00002
+
+#ifdef SUPPORT_OLD_XPRISON
+static
+char *print_xprison_v1(void *p, char *end, unsigned flags)
+{
+ struct xprison_v1 *xp;
+ struct in_addr in;
+
+ if ((char *)p + sizeof(struct xprison_v1) > end)
+ errx(1, "Invalid length for jail");
+
+ xp = (struct xprison_v1 *)p;
+ if (flags & FLAG_V) {
+ printf("%6d %-29.29s %.74s\n",
+ xp->pr_id, xp->pr_host, xp->pr_path);
+ /* We are not printing an empty line here for state and name. */
+ /* We are not printing an empty line here for cpusetid. */
+ /* IPv4 address. */
+ in.s_addr = htonl(xp->pr_ip);
+ printf("%6s %-15.15s\n", "", inet_ntoa(in));
+ } else {
+ printf("%6d %-15.15s %-29.29s %.74s\n",
+ xp->pr_id, inet_ntoa(in), xp->pr_host, xp->pr_path);
+ }
+
+ return ((char *)(xp + 1));
+}
+#endif
+
+static
+char *print_xprison_v3(void *p, char *end, unsigned flags)
+{
+ struct xprison *xp;
+ struct in_addr *iap, in;
+ struct in6_addr *ia6p;
+ char buf[INET6_ADDRSTRLEN];
+ const char *state;
+ char *q;
+ uint32_t i;
+
+ if ((char *)p + sizeof(struct xprison) > end)
+ errx(1, "Invalid length for jail");
+ xp = (struct xprison *)p;
+
+ if (xp->pr_state < 0 || xp->pr_state >= (int)
+ ((sizeof(prison_states) / sizeof(struct prison_state))))
+ state = "(bogus)";
+ else
+ state = prison_states[xp->pr_state].state_name;
+
+ /* See if we should print non-ACTIVE jails. No? */
+ if ((flags & FLAG_A) == 0 && strcmp(state, "ALIVE")) {
+ q = (char *)(xp + 1);
+ q += (xp->pr_ip4s * sizeof(struct in_addr));
+ if (q > end)
+ errx(1, "Invalid length for jail");
+ q += (xp->pr_ip6s * sizeof(struct in6_addr));
+ if (q > end)
+ errx(1, "Invalid length for jail");
+ return (q);
+ }
+
+ if (flags & FLAG_V)
+ printf("%6d %-29.29s %.74s\n",
+ xp->pr_id, xp->pr_host, xp->pr_path);
+
+ /* Jail state and name. */
+ if (flags & FLAG_V)
+ printf("%6s %-29.29s %.74s\n",
+ "", (xp->pr_name[0] != '\0') ? xp->pr_name : "", state);
+
+ /* cpusetid. */
+ if (flags & FLAG_V)
+ printf("%6s %-6d\n",
+ "", xp->pr_cpusetid);
+
+ q = (char *)(xp + 1);
+ /* IPv4 addresses. */
+ iap = (struct in_addr *)(void *)q;
+ q += (xp->pr_ip4s * sizeof(struct in_addr));
+ if (q > end)
+ errx(1, "Invalid length for jail");
+ in.s_addr = 0;
+ for (i = 0; i < xp->pr_ip4s; i++) {
+ if (i == 0 || flags & FLAG_V)
+ in.s_addr = iap[i].s_addr;
+ if (flags & FLAG_V)
+ printf("%6s %-15.15s\n", "", inet_ntoa(in));
+ }
+ /* IPv6 addresses. */
+ ia6p = (struct in6_addr *)(void *)q;
+ q += (xp->pr_ip6s * sizeof(struct in6_addr));
+ if (q > end)
+ errx(1, "Invalid length for jail");
+ for (i = 0; i < xp->pr_ip6s; i++) {
+ if (flags & FLAG_V) {
+ inet_ntop(AF_INET6, &ia6p[i], buf, sizeof(buf));
+ printf("%6s %s\n", "", buf);
+ }
+ }
+
+ /* If requested print the old style single line version. */
+ if (!(flags & FLAG_V))
+ printf("%6d %-15.15s %-29.29s %.74s\n",
+ xp->pr_id, (in.s_addr) ? inet_ntoa(in) : "",
+ xp->pr_host, xp->pr_path);
+
+ return (q);
+}
+
+static void
+usage(void)
+{
+
+ (void)fprintf(stderr, "usage: jls [-av]\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int ch, version;
+ unsigned flags;
+ size_t i, j, len;
+ void *p, *q;
+
+ flags = 0;
+ while ((ch = getopt(argc, argv, "av")) != -1) {
+ switch (ch) {
+ case 'a':
+ flags |= FLAG_A;
+ break;
+ case 'v':
+ flags |= FLAG_V;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (sysctlbyname("security.jail.list", NULL, &len, NULL, 0) == -1)
+ err(1, "sysctlbyname(): security.jail.list");
+
+ j = len;
+ for (i = 0; i < 4; i++) {
+ if (len <= 0)
+ exit(0);
+ p = q = malloc(len);
+ if (p == NULL)
+ err(1, "malloc()");
+
+ if (sysctlbyname("security.jail.list", q, &len, NULL, 0) == -1) {
+ if (errno == ENOMEM) {
+ free(p);
+ p = NULL;
+ len += j;
+ continue;
+ }
+ err(1, "sysctlbyname(): security.jail.list");
+ }
+ break;
+ }
+ if (p == NULL)
+ err(1, "sysctlbyname(): security.jail.list");
+ if (len < sizeof(int))
+ errx(1, "This is no prison. Kernel and userland out of sync?");
+ version = *(int *)p;
+ if (version > XPRISON_VERSION)
+ errx(1, "Sci-Fi prison. Kernel/userland out of sync?");
+
+ if (flags & FLAG_V) {
+ printf(" JID Hostname Path\n");
+ printf(" Name State\n");
+ printf(" CPUSetID\n");
+ printf(" IP Address(es)\n");
+ } else {
+ printf(" JID IP Address Hostname"
+ " Path\n");
+ }
+ for (; q != NULL && (char *)q + sizeof(int) < (char *)p + len;) {
+ version = *(int *)q;
+ if (version > XPRISON_VERSION)
+ errx(1, "Sci-Fi prison. Kernel/userland out of sync?");
+ switch (version) {
+#ifdef SUPPORT_OLD_XPRISON
+ case 1:
+ q = print_xprison_v1(q, (char *)p + len, flags);
+ break;
+ case 2:
+ errx(1, "Version 2 was used by multi-IPv4 jail "
+ "implementations that never made it into the "
+ "official kernel.");
+ /* NOTREACHED */
+ break;
+#endif
+ case 3:
+ q = print_xprison_v3(q, (char *)p + len, flags);
+ break;
+ default:
+ errx(1, "Prison unknown. Kernel/userland out of sync?");
+ /* NOTREACHED */
+ break;
+ }
+ }
+
+ free(p);
+ exit(0);
+}
diff --git a/usr.sbin/kbdcontrol/Makefile b/usr.sbin/kbdcontrol/Makefile
new file mode 100644
index 0000000..ca1b6e6
--- /dev/null
+++ b/usr.sbin/kbdcontrol/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+PROG= kbdcontrol
+MAN= kbdcontrol.1 kbdmap.5
+MLINKS= kbdmap.5 keymap.5
+SRCS= kbdcontrol.c lex.l
+
+WARNS?= 4
+CFLAGS+= -I${.CURDIR}
+
+DPADD= ${LIBL}
+LDADD= -ll
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/kbdcontrol/kbdcontrol.1 b/usr.sbin/kbdcontrol/kbdcontrol.1
new file mode 100644
index 0000000..68cee84
--- /dev/null
+++ b/usr.sbin/kbdcontrol/kbdcontrol.1
@@ -0,0 +1,276 @@
+.\"
+.\" kbdcontrol - a utility for manipulating the syscons keyboard driver section
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" @(#)kbdcontrol.1
+.\" $FreeBSD$
+.\"
+.Dd January 29, 2008
+.Dt KBDCONTROL 1
+.Os
+.Sh NAME
+.Nm kbdcontrol
+.Nd keyboard control and configuration utility
+.Sh SYNOPSIS
+.Nm
+.Op Fl dFKix
+.Op Fl A Ar name
+.Op Fl a Ar name
+.Oo
+.Fl b
+.Ar duration . Ns Ar pitch | Ar belltype
+.Oc
+.Oo
+.Fl r
+.Ar delay . Ns Ar repeat | Ar speed
+.Oc
+.Op Fl l Ar keymap_file
+.Op Fl f Ar # Ar string
+.Op Fl k Ar keyboard_device
+.Op Fl L Ar keymap_file
+.Sh DESCRIPTION
+The
+.Nm
+command is used to set various keyboard related options for the
+.Xr syscons 4
+console driver and the keyboard drivers,
+such as key map, keyboard repeat and delay rates, bell
+characteristics etc.
+.Pp
+Keyboard options may be automatically configured at system boot time by
+setting variables in
+.Pa /etc/rc.conf .
+See
+.Sx Boot Time Configuration
+below.
+.Pp
+The following command line options are supported:
+.Bl -tag -width indent
+.It Fl A Ar name
+Detach the keyboard, specified by the keyboard device name, from the keyboard
+multiplexer.
+When using this option, the standard input of the
+.Nm
+process should be redirected from the keyboard multiplexer keyboard device
+(if the keyboard multiplexer is not the active keyboard) or
+.Pa /dev/console
+(if the keyboard multiplexer is the active keyboard and
+you are not working on the system console).
+.It Fl a Ar name
+Attach the keyboard, specified by the keyboard device name, to the keyboard
+multiplexer.
+When using this option, the standard input of the
+.Nm
+process should be redirected from the keyboard multiplexer keyboard device
+(if the keyboard multiplexer is not the active keyboard) or
+.Pa /dev/console
+(if the keyboard multiplexer is the active keyboard and
+you are not working on the system console).
+.It Fl b Xo
+.Ar duration . Ns Ar pitch | Ar belltype
+.Xc
+Set the bell duration in milliseconds and pitch in hertz.
+If a
+.Ar belltype
+argument is specified, it may be one of
+.Cm normal
+which sets sound parameters back to normal values,
+.Cm off
+which disables the bell entirely, or
+.Cm visual
+which sets the bell to visual mode, i.e., flashes the screen instead.
+If
+.Ar belltype
+is preceded by the word
+.Cm quiet. ,
+the bell will not be rung when the ringing process is in the background vty.
+The
+.Cm visual
+bell, when chosen, applies to all vtys; other bell types
+can be set individually for each vty.
+.It Fl r Xo
+.Ar delay . Ns Ar repeat | Ar speed
+.Xc
+Set keyboard
+.Ar delay
+(250, 500, 750, 1000)
+and
+.Ar repeat
+(34, 38, 42, 46, 50, 55, 59, 63, 68, 76, 84, 92, 100, 110, 118, 126,
+136, 152, 168, 184, 200, 220, 236, 252, 272, 304, 336, 368, 400, 440,
+472, 504)
+rates, or if a
+.Ar speed
+argument is specified, it may be one of
+.Cm slow
+(1000.504),
+.Cm fast
+(250.34)
+or
+.Cm normal
+(500.126).
+.It Fl l Ar keymap_file
+Install keyboard map file from
+.Ar keymap_file .
+You may load the keyboard map file from a menu-driven command,
+.Xr kbdmap 1 .
+The format of keyboard map files is documented in the
+.Xr kbdmap 5
+manual page.
+.It Fl d
+Dump the current keyboard map onto stdout.
+The output may be redirected to a file and can be loaded
+back to the kernel later by the
+.Fl l
+option above.
+.It Fl f Ar # Ar string
+Set function key number
+.Ar #
+to send
+.Ar string .
+Refer to the man page for the keyboard driver
+(e.g.\&
+.Xr atkbd 4 )
+for available function keys and their numbers.
+.It Fl F
+Set function keys back to the standard definitions.
+.It Fl x
+Use hexadecimal numbers in keyboard map dump.
+.It Fl i
+Print brief information about the keyboard.
+.It Fl K
+Disconnect the keyboard from the console.
+You need to use the
+.Fl k
+option below to associate a keyboard with the console again.
+.It Fl k Ar keyboard_device
+Use the specified device as the console keyboard.
+When using this option, the standard input of the
+.Nm
+process should be redirected from
+.Pa /dev/console
+if you are not working on the system console
+(see the
+.Sx EXAMPLES
+section).
+.It Fl L Ar keymap_file
+Load keyboard map file from
+.Ar keymap_file
+and write the
+.Ft "struct keymap"
+compiled from it to stdout.
+This option is primarily intended for programmers and is probably
+of little use under normal circumstances.
+.El
+.Sh ENVIRONMENT
+The environment variable
+.Ev KEYMAP_PATH
+can hold an alternative path to the keyboard map files.
+.Sh KEYBOARD CONFIGURATION
+.Ss Boot Time Configuration
+You may set variables in
+.Pa /etc/rc.conf
+or
+.Pa /etc/rc.conf.local
+in order to configure the keyboard at boot time.
+The following is the list of relevant variables.
+.Pp
+.Bl -tag -width foo_bar_var -compact
+.It Ar keymap
+Specifies a keyboard map file for the
+.Fl l
+option.
+.It Ar keyrate
+Sets the keyboard repeat rate for the
+.Fl r
+option.
+.It Ar keychange
+Lists function key strings for the
+.Fl f
+option.
+.El
+.Pp
+See
+.Xr rc.conf 5
+for details.
+.Ss Driver Configuration
+The keyboard device driver may let you change default configuration
+options, such as the default keyboard map, so that you do not need to set up
+the options at boot time.
+See keyboard driver manuals
+(e.g.\&
+.Xr atkbd 4 ,
+.Xr ukbd 4 )
+for details.
+.Sh FILES
+.Bl -tag -width /usr/share/syscons/keymaps/foo_bar -compact
+.It Pa /usr/share/syscons/keymaps/*
+keyboard map files
+.El
+.Sh EXAMPLES
+The following command will load the keyboard map file
+.Pa /usr/share/syscons/keymaps/ru.koi8-r.kbd .
+.Pp
+.Dl kbdcontrol -l /usr/share/syscons/keymaps/ru.koi8-r.kbd
+.Pp
+So long as the keyboard map file resides in
+.Pa /usr/share/syscons/keymaps ,
+you may abbreviate the file name as
+.Pa ru.koi8-r .
+.Pp
+.Dl kbdcontrol -l ru.koi8-r
+.Pp
+The following command will make the function key 10 emit "telnet myhost".
+.Pp
+.Dl kbdcontrol -f 10 \&"telnet myhost\&"
+.Pp
+In order to get the visual effect for bell, but prevent the screen
+from flushing if the bell is to ring in the background screen,
+run the following command.
+.Pp
+.Dl kbdcontrol -b quiet.visual
+.Pp
+To change the default console keyboard to the another keyboard,
+for example the first USB keyboard (see
+.Xr ukbd 4 ) ,
+use the following commands.
+.Pp
+.Dl kbdcontrol -k /dev/ukbd0 < /dev/console
+.Pp
+To switch back to the default keyboard, use this command.
+.Pp
+.Dl kbdcontrol -k /dev/kbd0
+.Pp
+To allow using both the second USB keyboard and the first AT keyboard
+at the same time on console via the
+.Xr kbdmux 4
+driver, use the following sequence of commands.
+.Bd -literal -offset indent
+kbdcontrol -K < /dev/console
+kbdcontrol -a atkbd0 < /dev/kbdmux0
+kbdcontrol -a ukbd1 < /dev/kbdmux0
+kbdcontrol -k /dev/kbdmux0 < /dev/console
+.Ed
+.Sh SEE ALSO
+.Xr kbdmap 1 ,
+.Xr vidcontrol 1 ,
+.Xr atkbd 4 ,
+.Xr kbdmux 4 ,
+.Xr keyboard 4 ,
+.Xr screen 4 ,
+.Xr syscons 4 ,
+.Xr ukbd 4 ,
+.Xr kbdmap 5 ,
+.Xr rc.conf 5
+.Sh AUTHORS
+.An S\(/oren Schmidt Aq sos@FreeBSD.org
+.Sh BUGS
+Report when found.
diff --git a/usr.sbin/kbdcontrol/kbdcontrol.c b/usr.sbin/kbdcontrol/kbdcontrol.c
new file mode 100644
index 0000000..c800233
--- /dev/null
+++ b/usr.sbin/kbdcontrol/kbdcontrol.c
@@ -0,0 +1,1210 @@
+/*-
+ * Copyright (c) 1994-1995 Søren Schmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/kbio.h>
+#include <sys/consio.h>
+#include "path.h"
+#include "lex.h"
+
+/*
+ * HALT, PDWN, and PASTE aren't defined in 4.x, but we need them to bridge
+ * to 5.0-current so define them here as a stop gap transition measure.
+ */
+#ifndef HALT
+#define HALT 0xa1 /* halt machine */
+#endif
+#ifndef PDWN
+#define PDWN 0xa2 /* halt machine and power down */
+#endif
+#ifndef PASTE
+#define PASTE 0xa3 /* paste from cut-paste buffer */
+#endif
+
+char ctrl_names[32][4] = {
+ "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
+ "bs ", "ht ", "nl ", "vt ", "ff ", "cr ", "so ", "si ",
+ "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
+ "can", "em ", "sub", "esc", "fs ", "gs ", "rs ", "us "
+ };
+
+char acc_names[15][5] = {
+ "dgra", "dacu", "dcir", "dtil", "dmac", "dbre", "ddot",
+ "duml", "dsla", "drin", "dced", "dapo", "ddac", "dogo",
+ "dcar",
+ };
+
+char acc_names_u[15][5] = {
+ "DGRA", "DACU", "DCIR", "DTIL", "DMAC", "DBRE", "DDOT",
+ "DUML", "DSLA", "DRIN", "DCED", "DAPO", "DDAC", "DOGO",
+ "DCAR",
+ };
+
+char fkey_table[96][MAXFK] = {
+/* 01-04 */ "\033[M", "\033[N", "\033[O", "\033[P",
+/* 05-08 */ "\033[Q", "\033[R", "\033[S", "\033[T",
+/* 09-12 */ "\033[U", "\033[V", "\033[W", "\033[X",
+/* 13-16 */ "\033[Y", "\033[Z", "\033[a", "\033[b",
+/* 17-20 */ "\033[c", "\033[d", "\033[e", "\033[f",
+/* 21-24 */ "\033[g", "\033[h", "\033[i", "\033[j",
+/* 25-28 */ "\033[k", "\033[l", "\033[m", "\033[n",
+/* 29-32 */ "\033[o", "\033[p", "\033[q", "\033[r",
+/* 33-36 */ "\033[s", "\033[t", "\033[u", "\033[v",
+/* 37-40 */ "\033[w", "\033[x", "\033[y", "\033[z",
+/* 41-44 */ "\033[@", "\033[[", "\033[\\","\033[]",
+/* 45-48 */ "\033[^", "\033[_", "\033[`", "\033[{",
+/* 49-52 */ "\033[H", "\033[A", "\033[I", "-" ,
+/* 53-56 */ "\033[D", "\033[E", "\033[C", "+" ,
+/* 57-60 */ "\033[F", "\033[B", "\033[G", "\033[L",
+/* 61-64 */ "\177", "\033[J", "\033[~", "\033[}",
+/* 65-68 */ "" , "" , "" , "" ,
+/* 69-72 */ "" , "" , "" , "" ,
+/* 73-76 */ "" , "" , "" , "" ,
+/* 77-80 */ "" , "" , "" , "" ,
+/* 81-84 */ "" , "" , "" , "" ,
+/* 85-88 */ "" , "" , "" , "" ,
+/* 89-92 */ "" , "" , "" , "" ,
+/* 93-96 */ "" , "" , "" , "" ,
+ };
+
+const int delays[] = {250, 500, 750, 1000};
+const int repeats[] = { 34, 38, 42, 46, 50, 55, 59, 63,
+ 68, 76, 84, 92, 100, 110, 118, 126,
+ 136, 152, 168, 184, 200, 220, 236, 252,
+ 272, 304, 336, 368, 400, 440, 472, 504};
+const int ndelays = (sizeof(delays) / sizeof(int));
+const int nrepeats = (sizeof(repeats) / sizeof(int));
+int hex = 0;
+int number;
+char letter;
+int token;
+
+void dump_accent_definition(char *name, accentmap_t *accentmap);
+void dump_entry(int value);
+void dump_key_definition(char *name, keymap_t *keymap);
+int get_accent_definition_line(accentmap_t *);
+int get_entry(void);
+int get_key_definition_line(keymap_t *);
+void load_keymap(char *opt, int dumponly);
+void load_default_functionkeys(void);
+char * nextarg(int ac, char **av, int *indp, int oc);
+char * mkfullname(const char *s1, const char *s2, const char *s3);
+void print_accent_definition_line(FILE *fp, int accent,
+ struct acc_t *key);
+void print_entry(FILE *fp, int value);
+void print_key_definition_line(FILE *fp, int scancode,
+ struct keyent_t *key);
+void print_keymap(void);
+void release_keyboard(void);
+void mux_keyboard(u_int op, char *kbd);
+void set_bell_values(char *opt);
+void set_functionkey(char *keynumstr, char *string);
+void set_keyboard(char *device);
+void set_keyrates(char *opt);
+void show_kbd_info(void);
+void usage(void) __dead2;
+
+char *
+nextarg(int ac, char **av, int *indp, int oc)
+{
+ if (*indp < ac)
+ return(av[(*indp)++]);
+ warnx("option requires two arguments -- %c", oc);
+ usage();
+}
+
+
+char *
+mkfullname(const char *s1, const char *s2, const char *s3)
+{
+ static char *buf = NULL;
+ static int bufl = 0;
+ int f;
+
+ f = strlen(s1) + strlen(s2) + strlen(s3) + 1;
+ if (f > bufl) {
+ if (buf)
+ buf = (char *)realloc(buf, f);
+ else
+ buf = (char *)malloc(f);
+ }
+ if (!buf) {
+ bufl = 0;
+ return(NULL);
+ }
+
+ bufl = f;
+ strcpy(buf, s1);
+ strcat(buf, s2);
+ strcat(buf, s3);
+ return(buf);
+}
+
+
+int
+get_entry(void)
+{
+ switch ((token = yylex())) {
+ case TNOP:
+ return NOP | 0x100;
+ case TLSH:
+ return LSH | 0x100;
+ case TRSH:
+ return RSH | 0x100;
+ case TCLK:
+ return CLK | 0x100;
+ case TNLK:
+ return NLK | 0x100;
+ case TSLK:
+ return SLK | 0x100;
+ case TBTAB:
+ return BTAB | 0x100;
+ case TLALT:
+ return LALT | 0x100;
+ case TLCTR:
+ return LCTR | 0x100;
+ case TNEXT:
+ return NEXT | 0x100;
+ case TPREV:
+ return PREV | 0x100;
+ case TRCTR:
+ return RCTR | 0x100;
+ case TRALT:
+ return RALT | 0x100;
+ case TALK:
+ return ALK | 0x100;
+ case TASH:
+ return ASH | 0x100;
+ case TMETA:
+ return META | 0x100;
+ case TRBT:
+ return RBT | 0x100;
+ case TDBG:
+ return DBG | 0x100;
+ case TSUSP:
+ return SUSP | 0x100;
+ case TSPSC:
+ return SPSC | 0x100;
+ case TPANIC:
+ return PNC | 0x100;
+ case TLSHA:
+ return LSHA | 0x100;
+ case TRSHA:
+ return RSHA | 0x100;
+ case TLCTRA:
+ return LCTRA | 0x100;
+ case TRCTRA:
+ return RCTRA | 0x100;
+ case TLALTA:
+ return LALTA | 0x100;
+ case TRALTA:
+ return RALTA | 0x100;
+ case THALT:
+ return HALT | 0x100;
+ case TPDWN:
+ return PDWN | 0x100;
+ case TPASTE:
+ return PASTE | 0x100;
+ case TACC:
+ if (ACC(number) > L_ACC)
+ return -1;
+ return ACC(number) | 0x100;
+ case TFUNC:
+ if (F(number) > L_FN)
+ return -1;
+ return F(number) | 0x100;
+ case TSCRN:
+ if (S(number) > L_SCR)
+ return -1;
+ return S(number) | 0x100;
+ case TLET:
+ return (unsigned char)letter;
+ case TNUM:
+ if (number < 0 || number > 255)
+ return -1;
+ return number;
+ default:
+ return -1;
+ }
+}
+
+static int
+get_definition_line(FILE *fd, keymap_t *keymap, accentmap_t *accentmap)
+{
+ int c;
+
+ yyin = fd;
+
+ if (token < 0)
+ token = yylex();
+ switch (token) {
+ case TNUM:
+ c = get_key_definition_line(keymap);
+ if (c < 0)
+ errx(1, "invalid key definition");
+ if (c > keymap->n_keys)
+ keymap->n_keys = c;
+ break;
+ case TACC:
+ c = get_accent_definition_line(accentmap);
+ if (c < 0)
+ errx(1, "invalid accent key definition");
+ if (c > accentmap->n_accs)
+ accentmap->n_accs = c;
+ break;
+ case 0:
+ /* EOF */
+ return -1;
+ default:
+ errx(1, "illegal definition line");
+ }
+ return c;
+}
+
+int
+get_key_definition_line(keymap_t *map)
+{
+ int i, def, scancode;
+
+ /* check scancode number */
+ if (number < 0 || number >= NUM_KEYS)
+ return -1;
+ scancode = number;
+
+ /* get key definitions */
+ map->key[scancode].spcl = 0;
+ for (i=0; i<NUM_STATES; i++) {
+ if ((def = get_entry()) == -1)
+ return -1;
+ if (def & 0x100)
+ map->key[scancode].spcl |= (0x80 >> i);
+ map->key[scancode].map[i] = def & 0xFF;
+ }
+ /* get lock state key def */
+ if ((token = yylex()) != TFLAG)
+ return -1;
+ map->key[scancode].flgs = number;
+ token = yylex();
+ return (scancode + 1);
+}
+
+int
+get_accent_definition_line(accentmap_t *map)
+{
+ int accent;
+ int c1, c2;
+ int i;
+
+ if (ACC(number) < F_ACC || ACC(number) > L_ACC)
+ /* number out of range */
+ return -1;
+ accent = number;
+ if (map->acc[accent].accchar != 0) {
+ /* this entry has already been defined before! */
+ errx(1, "duplicated accent key definition");
+ }
+
+ switch ((token = yylex())) {
+ case TLET:
+ map->acc[accent].accchar = letter;
+ break;
+ case TNUM:
+ map->acc[accent].accchar = number;
+ break;
+ default:
+ return -1;
+ }
+
+ for (i = 0; (token = yylex()) == '(';) {
+ switch ((token = yylex())) {
+ case TLET:
+ c1 = letter;
+ break;
+ case TNUM:
+ c1 = number;
+ break;
+ default:
+ return -1;
+ }
+ switch ((token = yylex())) {
+ case TLET:
+ c2 = letter;
+ break;
+ case TNUM:
+ c2 = number;
+ break;
+ default:
+ return -1;
+ }
+ if ((token = yylex()) != ')')
+ return -1;
+ if (i >= NUM_ACCENTCHARS) {
+ warnx("too many accented characters, ignored");
+ continue;
+ }
+ map->acc[accent].map[i][0] = c1;
+ map->acc[accent].map[i][1] = c2;
+ ++i;
+ }
+ return (accent + 1);
+}
+
+void
+print_entry(FILE *fp, int value)
+{
+ int val = value & 0xFF;
+
+ switch (value) {
+ case NOP | 0x100:
+ fprintf(fp, " nop ");
+ break;
+ case LSH | 0x100:
+ fprintf(fp, " lshift");
+ break;
+ case RSH | 0x100:
+ fprintf(fp, " rshift");
+ break;
+ case CLK | 0x100:
+ fprintf(fp, " clock ");
+ break;
+ case NLK | 0x100:
+ fprintf(fp, " nlock ");
+ break;
+ case SLK | 0x100:
+ fprintf(fp, " slock ");
+ break;
+ case BTAB | 0x100:
+ fprintf(fp, " btab ");
+ break;
+ case LALT | 0x100:
+ fprintf(fp, " lalt ");
+ break;
+ case LCTR | 0x100:
+ fprintf(fp, " lctrl ");
+ break;
+ case NEXT | 0x100:
+ fprintf(fp, " nscr ");
+ break;
+ case PREV | 0x100:
+ fprintf(fp, " pscr ");
+ break;
+ case RCTR | 0x100:
+ fprintf(fp, " rctrl ");
+ break;
+ case RALT | 0x100:
+ fprintf(fp, " ralt ");
+ break;
+ case ALK | 0x100:
+ fprintf(fp, " alock ");
+ break;
+ case ASH | 0x100:
+ fprintf(fp, " ashift");
+ break;
+ case META | 0x100:
+ fprintf(fp, " meta ");
+ break;
+ case RBT | 0x100:
+ fprintf(fp, " boot ");
+ break;
+ case DBG | 0x100:
+ fprintf(fp, " debug ");
+ break;
+ case SUSP | 0x100:
+ fprintf(fp, " susp ");
+ break;
+ case SPSC | 0x100:
+ fprintf(fp, " saver ");
+ break;
+ case PNC | 0x100:
+ fprintf(fp, " panic ");
+ break;
+ case LSHA | 0x100:
+ fprintf(fp, " lshifta");
+ break;
+ case RSHA | 0x100:
+ fprintf(fp, " rshifta");
+ break;
+ case LCTRA | 0x100:
+ fprintf(fp, " lctrla");
+ break;
+ case RCTRA | 0x100:
+ fprintf(fp, " rctrla");
+ break;
+ case LALTA | 0x100:
+ fprintf(fp, " lalta ");
+ break;
+ case RALTA | 0x100:
+ fprintf(fp, " ralta ");
+ break;
+ case HALT | 0x100:
+ fprintf(fp, " halt ");
+ break;
+ case PDWN | 0x100:
+ fprintf(fp, " pdwn ");
+ break;
+ case PASTE | 0x100:
+ fprintf(fp, " paste ");
+ break;
+ default:
+ if (value & 0x100) {
+ if (val >= F_FN && val <= L_FN)
+ fprintf(fp, " fkey%02d", val - F_FN + 1);
+ else if (val >= F_SCR && val <= L_SCR)
+ fprintf(fp, " scr%02d ", val - F_SCR + 1);
+ else if (val >= F_ACC && val <= L_ACC)
+ fprintf(fp, " %-6s", acc_names[val - F_ACC]);
+ else if (hex)
+ fprintf(fp, " 0x%02x ", val);
+ else
+ fprintf(fp, " %3d ", val);
+ }
+ else {
+ if (val < ' ')
+ fprintf(fp, " %s ", ctrl_names[val]);
+ else if (val == 127)
+ fprintf(fp, " del ");
+ else if (isascii(val) && isprint(val))
+ fprintf(fp, " '%c' ", val);
+ else if (hex)
+ fprintf(fp, " 0x%02x ", val);
+ else
+ fprintf(fp, " %3d ", val);
+ }
+ }
+}
+
+void
+print_key_definition_line(FILE *fp, int scancode, struct keyent_t *key)
+{
+ int i;
+
+ /* print scancode number */
+ if (hex)
+ fprintf(fp, " 0x%02x ", scancode);
+ else
+ fprintf(fp, " %03d ", scancode);
+
+ /* print key definitions */
+ for (i=0; i<NUM_STATES; i++) {
+ if (key->spcl & (0x80 >> i))
+ print_entry(fp, key->map[i] | 0x100);
+ else
+ print_entry(fp, key->map[i]);
+ }
+
+ /* print lock state key def */
+ switch (key->flgs) {
+ case 0:
+ fprintf(fp, " O\n");
+ break;
+ case 1:
+ fprintf(fp, " C\n");
+ break;
+ case 2:
+ fprintf(fp, " N\n");
+ break;
+ case 3:
+ fprintf(fp, " B\n");
+ break;
+ }
+}
+
+void
+print_accent_definition_line(FILE *fp, int accent, struct acc_t *key)
+{
+ int c;
+ int i;
+
+ if (key->accchar == 0)
+ return;
+
+ /* print accent number */
+ fprintf(fp, " %-6s", acc_names[accent]);
+ if (isascii(key->accchar) && isprint(key->accchar))
+ fprintf(fp, "'%c' ", key->accchar);
+ else if (hex)
+ fprintf(fp, "0x%02x ", key->accchar);
+ else
+ fprintf(fp, "%03d ", key->accchar);
+
+ for (i = 0; i < NUM_ACCENTCHARS; ++i) {
+ c = key->map[i][0];
+ if (c == 0)
+ break;
+ if ((i > 0) && ((i % 4) == 0))
+ fprintf(fp, "\n ");
+ if (isascii(c) && isprint(c))
+ fprintf(fp, "( '%c' ", c);
+ else if (hex)
+ fprintf(fp, "(0x%02x ", c);
+ else
+ fprintf(fp, "( %03d ", c);
+ c = key->map[i][1];
+ if (isascii(c) && isprint(c))
+ fprintf(fp, "'%c' ) ", c);
+ else if (hex)
+ fprintf(fp, "0x%02x) ", c);
+ else
+ fprintf(fp, "%03d ) ", c);
+ }
+ fprintf(fp, "\n");
+}
+
+void
+dump_entry(int value)
+{
+ if (value & 0x100) {
+ value &= 0x00ff;
+ switch (value) {
+ case NOP:
+ printf(" NOP, ");
+ break;
+ case LSH:
+ printf(" LSH, ");
+ break;
+ case RSH:
+ printf(" RSH, ");
+ break;
+ case CLK:
+ printf(" CLK, ");
+ break;
+ case NLK:
+ printf(" NLK, ");
+ break;
+ case SLK:
+ printf(" SLK, ");
+ break;
+ case BTAB:
+ printf(" BTAB, ");
+ break;
+ case LALT:
+ printf(" LALT, ");
+ break;
+ case LCTR:
+ printf(" LCTR, ");
+ break;
+ case NEXT:
+ printf(" NEXT, ");
+ break;
+ case PREV:
+ printf(" PREV, ");
+ break;
+ case RCTR:
+ printf(" RCTR, ");
+ break;
+ case RALT:
+ printf(" RALT, ");
+ break;
+ case ALK:
+ printf(" ALK, ");
+ break;
+ case ASH:
+ printf(" ASH, ");
+ break;
+ case META:
+ printf(" META, ");
+ break;
+ case RBT:
+ printf(" RBT, ");
+ break;
+ case DBG:
+ printf(" DBG, ");
+ break;
+ case SUSP:
+ printf(" SUSP, ");
+ break;
+ case SPSC:
+ printf(" SPSC, ");
+ break;
+ case PNC:
+ printf(" PNC, ");
+ break;
+ case LSHA:
+ printf(" LSHA, ");
+ break;
+ case RSHA:
+ printf(" RSHA, ");
+ break;
+ case LCTRA:
+ printf("LCTRA, ");
+ break;
+ case RCTRA:
+ printf("RCTRA, ");
+ break;
+ case LALTA:
+ printf("LALTA, ");
+ break;
+ case RALTA:
+ printf("RALTA, ");
+ break;
+ case HALT:
+ printf(" HALT, ");
+ break;
+ case PDWN:
+ printf(" PDWN, ");
+ break;
+ case PASTE:
+ printf("PASTE, ");
+ break;
+ default:
+ if (value >= F_FN && value <= L_FN)
+ printf(" F(%2d),", value - F_FN + 1);
+ else if (value >= F_SCR && value <= L_SCR)
+ printf(" S(%2d),", value - F_SCR + 1);
+ else if (value >= F_ACC && value <= L_ACC)
+ printf(" %-4s, ", acc_names_u[value - F_ACC]);
+ else
+ printf(" 0x%02X, ", value);
+ break;
+ }
+ } else if (value == '\'') {
+ printf(" '\\'', ");
+ } else if (value == '\\') {
+ printf(" '\\\\', ");
+ } else if (isascii(value) && isprint(value)) {
+ printf(" '%c', ", value);
+ } else {
+ printf(" 0x%02X, ", value);
+ }
+}
+
+void
+dump_key_definition(char *name, keymap_t *keymap)
+{
+ int i, j;
+
+ printf("static keymap_t keymap_%s = { 0x%02x, {\n",
+ name, (unsigned)keymap->n_keys);
+ printf(
+"/* alt\n"
+" * scan cntrl alt alt cntrl\n"
+" * code base shift cntrl shift alt shift cntrl shift spcl flgs\n"
+" * ---------------------------------------------------------------------------\n"
+" */\n");
+ for (i = 0; i < keymap->n_keys; i++) {
+ printf("/*%02x*/{{", i);
+ for (j = 0; j < NUM_STATES; j++) {
+ if (keymap->key[i].spcl & (0x80 >> j))
+ dump_entry(keymap->key[i].map[j] | 0x100);
+ else
+ dump_entry(keymap->key[i].map[j]);
+ }
+ printf("}, 0x%02X,0x%02X },\n",
+ (unsigned)keymap->key[i].spcl,
+ (unsigned)keymap->key[i].flgs);
+ }
+ printf("} };\n\n");
+}
+
+void
+dump_accent_definition(char *name, accentmap_t *accentmap)
+{
+ int i, j;
+ int c;
+
+ printf("static accentmap_t accentmap_%s = { %d",
+ name, accentmap->n_accs);
+ if (accentmap->n_accs <= 0) {
+ printf(" };\n\n");
+ return;
+ }
+ printf(", {\n");
+ for (i = 0; i < NUM_DEADKEYS; i++) {
+ printf(" /* %s=%d */\n {", acc_names[i], i);
+ c = accentmap->acc[i].accchar;
+ if (c == '\'')
+ printf(" '\\'', {");
+ else if (c == '\\')
+ printf(" '\\\\', {");
+ else if (isascii(c) && isprint(c))
+ printf(" '%c', {", c);
+ else if (c == 0) {
+ printf(" 0x00 }, \n");
+ continue;
+ } else
+ printf(" 0x%02x, {", c);
+ for (j = 0; j < NUM_ACCENTCHARS; j++) {
+ c = accentmap->acc[i].map[j][0];
+ if (c == 0)
+ break;
+ if ((j > 0) && ((j % 4) == 0))
+ printf("\n\t ");
+ if (isascii(c) && isprint(c))
+ printf(" { '%c',", c);
+ else
+ printf(" { 0x%02x,", c);
+ printf("0x%02x },", accentmap->acc[i].map[j][1]);
+ }
+ printf(" }, },\n");
+ }
+ printf("} };\n\n");
+}
+
+void
+load_keymap(char *opt, int dumponly)
+{
+ keymap_t keymap;
+ accentmap_t accentmap;
+ FILE *fd;
+ int i, j;
+ char *name, *cp;
+ char blank[] = "", keymap_path[] = KEYMAP_PATH, dotkbd[] = ".kbd";
+ char *prefix[] = {blank, blank, keymap_path, NULL};
+ char *postfix[] = {blank, dotkbd, NULL};
+
+ cp = getenv("KEYMAP_PATH");
+ if (cp != NULL)
+ asprintf(&(prefix[0]), "%s/", cp);
+
+ fd = NULL;
+ for (i=0; prefix[i] && fd == NULL; i++) {
+ for (j=0; postfix[j] && fd == NULL; j++) {
+ name = mkfullname(prefix[i], opt, postfix[j]);
+ fd = fopen(name, "r");
+ }
+ }
+ if (fd == NULL) {
+ warn("keymap file \"%s\" not found", opt);
+ return;
+ }
+ memset(&keymap, 0, sizeof(keymap));
+ memset(&accentmap, 0, sizeof(accentmap));
+ token = -1;
+ while (1) {
+ if (get_definition_line(fd, &keymap, &accentmap) < 0)
+ break;
+ }
+ if (dumponly) {
+ /* fix up the filename to make it a valid C identifier */
+ for (cp = opt; *cp; cp++)
+ if (!isalpha(*cp) && !isdigit(*cp)) *cp = '_';
+ printf("/*\n"
+ " * Automatically generated from %s.\n"
+ " * DO NOT EDIT!\n"
+ " */\n", name);
+ dump_key_definition(opt, &keymap);
+ dump_accent_definition(opt, &accentmap);
+ return;
+ }
+ if ((keymap.n_keys > 0) && (ioctl(0, PIO_KEYMAP, &keymap) < 0)) {
+ warn("setting keymap");
+ fclose(fd);
+ return;
+ }
+ if ((accentmap.n_accs > 0)
+ && (ioctl(0, PIO_DEADKEYMAP, &accentmap) < 0)) {
+ warn("setting accentmap");
+ fclose(fd);
+ return;
+ }
+}
+
+void
+print_keymap(void)
+{
+ keymap_t keymap;
+ accentmap_t accentmap;
+ int i;
+
+ if (ioctl(0, GIO_KEYMAP, &keymap) < 0)
+ err(1, "getting keymap");
+ if (ioctl(0, GIO_DEADKEYMAP, &accentmap) < 0)
+ memset(&accentmap, 0, sizeof(accentmap));
+ printf(
+"# alt\n"
+"# scan cntrl alt alt cntrl lock\n"
+"# code base shift cntrl shift alt shift cntrl shift state\n"
+"# ------------------------------------------------------------------\n"
+ );
+ for (i=0; i<keymap.n_keys; i++)
+ print_key_definition_line(stdout, i, &keymap.key[i]);
+
+ printf("\n");
+ for (i = 0; i < NUM_DEADKEYS; i++)
+ print_accent_definition_line(stdout, i, &accentmap.acc[i]);
+
+}
+
+void
+load_default_functionkeys(void)
+{
+ fkeyarg_t fkey;
+ int i;
+
+ for (i=0; i<NUM_FKEYS; i++) {
+ fkey.keynum = i;
+ strcpy(fkey.keydef, fkey_table[i]);
+ fkey.flen = strlen(fkey_table[i]);
+ if (ioctl(0, SETFKEY, &fkey) < 0)
+ warn("setting function key");
+ }
+}
+
+void
+set_functionkey(char *keynumstr, char *string)
+{
+ fkeyarg_t fkey;
+
+ if (!strcmp(keynumstr, "load") && !strcmp(string, "default")) {
+ load_default_functionkeys();
+ return;
+ }
+ fkey.keynum = atoi(keynumstr);
+ if (fkey.keynum < 1 || fkey.keynum > NUM_FKEYS) {
+ warnx("function key number must be between 1 and %d",
+ NUM_FKEYS);
+ return;
+ }
+ if ((fkey.flen = strlen(string)) > MAXFK) {
+ warnx("function key string too long (%d > %d)",
+ fkey.flen, MAXFK);
+ return;
+ }
+ strncpy(fkey.keydef, string, MAXFK);
+ fkey.keynum -= 1;
+ if (ioctl(0, SETFKEY, &fkey) < 0)
+ warn("setting function key");
+}
+
+void
+set_bell_values(char *opt)
+{
+ int bell, duration, pitch;
+
+ bell = 0;
+ if (!strncmp(opt, "quiet.", 6)) {
+ bell = CONS_QUIET_BELL;
+ opt += 6;
+ }
+ if (!strcmp(opt, "visual"))
+ bell |= CONS_VISUAL_BELL;
+ else if (!strcmp(opt, "normal"))
+ duration = 5, pitch = 800;
+ else if (!strcmp(opt, "off"))
+ duration = 0, pitch = 0;
+ else {
+ char *v1;
+
+ bell = 0;
+ duration = strtol(opt, &v1, 0);
+ if ((duration < 0) || (*v1 != '.'))
+ goto badopt;
+ opt = ++v1;
+ pitch = strtol(opt, &v1, 0);
+ if ((pitch < 0) || (*opt == '\0') || (*v1 != '\0')) {
+badopt:
+ warnx("argument to -b must be duration.pitch or [quiet.]visual|normal|off");
+ return;
+ }
+ if (pitch != 0)
+ pitch = 1193182 / pitch; /* in Hz */
+ duration /= 10; /* in 10 m sec */
+ }
+
+ ioctl(0, CONS_BELLTYPE, &bell);
+ if (!(bell & CONS_VISUAL_BELL))
+ fprintf(stderr, "[=%d;%dB", pitch, duration);
+}
+
+void
+set_keyrates(char *opt)
+{
+ int arg[2];
+ int repeat;
+ int delay;
+ int r, d;
+
+ if (!strcmp(opt, "slow")) {
+ delay = 1000, repeat = 500;
+ d = 3, r = 31;
+ } else if (!strcmp(opt, "normal")) {
+ delay = 500, repeat = 125;
+ d = 1, r = 15;
+ } else if (!strcmp(opt, "fast")) {
+ delay = repeat = 0;
+ d = r = 0;
+ } else {
+ int n;
+ char *v1;
+
+ delay = strtol(opt, &v1, 0);
+ if ((delay < 0) || (*v1 != '.'))
+ goto badopt;
+ opt = ++v1;
+ repeat = strtol(opt, &v1, 0);
+ if ((repeat < 0) || (*opt == '\0') || (*v1 != '\0')) {
+badopt:
+ warnx("argument to -r must be delay.repeat or slow|normal|fast");
+ return;
+ }
+ for (n = 0; n < ndelays - 1; n++)
+ if (delay <= delays[n])
+ break;
+ d = n;
+ for (n = 0; n < nrepeats - 1; n++)
+ if (repeat <= repeats[n])
+ break;
+ r = n;
+ }
+
+ arg[0] = delay;
+ arg[1] = repeat;
+ if (ioctl(0, KDSETREPEAT, arg)) {
+ if (ioctl(0, KDSETRAD, (d << 5) | r))
+ warn("setting keyboard rate");
+ }
+}
+
+static const char *
+get_kbd_type_name(int type)
+{
+ static struct {
+ int type;
+ const char *name;
+ } name_table[] = {
+ { KB_84, "AT 84" },
+ { KB_101, "AT 101/102" },
+ { KB_OTHER, "generic" },
+ };
+ unsigned int i;
+
+ for (i = 0; i < sizeof(name_table)/sizeof(name_table[0]); ++i) {
+ if (type == name_table[i].type)
+ return name_table[i].name;
+ }
+ return "unknown";
+}
+
+void
+show_kbd_info(void)
+{
+ keyboard_info_t info;
+
+ if (ioctl(0, KDGKBINFO, &info) == -1) {
+ warn("unable to obtain keyboard information");
+ return;
+ }
+ printf("kbd%d:\n", info.kb_index);
+ printf(" %.*s%d, type:%s (%d)\n",
+ (int)sizeof(info.kb_name), info.kb_name, info.kb_unit,
+ get_kbd_type_name(info.kb_type), info.kb_type);
+}
+
+void
+set_keyboard(char *device)
+{
+ keyboard_info_t info;
+ int fd;
+
+ fd = open(device, O_RDONLY);
+ if (fd < 0) {
+ warn("cannot open %s", device);
+ return;
+ }
+ if (ioctl(fd, KDGKBINFO, &info) == -1) {
+ warn("unable to obtain keyboard information");
+ close(fd);
+ return;
+ }
+ /*
+ * The keyboard device driver won't release the keyboard by
+ * the following ioctl, but it automatically will, when the device
+ * is closed. So, we don't check error here.
+ */
+ ioctl(fd, CONS_RELKBD, 0);
+ close(fd);
+#if 1
+ printf("kbd%d\n", info.kb_index);
+ printf(" %.*s%d, type:%s (%d)\n",
+ (int)sizeof(info.kb_name), info.kb_name, info.kb_unit,
+ get_kbd_type_name(info.kb_type), info.kb_type);
+#endif
+
+ if (ioctl(0, CONS_SETKBD, info.kb_index) == -1)
+ warn("unable to set keyboard");
+}
+
+void
+release_keyboard(void)
+{
+ keyboard_info_t info;
+
+ /*
+ * If stdin is not associated with a keyboard, the following ioctl
+ * will fail.
+ */
+ if (ioctl(0, KDGKBINFO, &info) == -1) {
+ warn("unable to obtain keyboard information");
+ return;
+ }
+#if 1
+ printf("kbd%d\n", info.kb_index);
+ printf(" %.*s%d, type:%s (%d)\n",
+ (int)sizeof(info.kb_name), info.kb_name, info.kb_unit,
+ get_kbd_type_name(info.kb_type), info.kb_type);
+#endif
+ if (ioctl(0, CONS_RELKBD, 0) == -1)
+ warn("unable to release the keyboard");
+}
+
+void
+mux_keyboard(u_int op, char *kbd)
+{
+ keyboard_info_t info;
+ char *unit, *ep;
+
+ /*
+ * If stdin is not associated with a keyboard, the following ioctl
+ * will fail.
+ */
+ if (ioctl(0, KDGKBINFO, &info) == -1) {
+ warn("unable to obtain keyboard information");
+ return;
+ }
+#if 1
+ printf("kbd%d\n", info.kb_index);
+ printf(" %.*s%d, type:%s (%d)\n",
+ (int)sizeof(info.kb_name), info.kb_name, info.kb_unit,
+ get_kbd_type_name(info.kb_type), info.kb_type);
+#endif
+ /*
+ * split kbd into name and unit. find the right most part of the
+ * kbd string that consist of only digits.
+ */
+
+ memset(&info, 0, sizeof(info));
+
+ info.kb_unit = -1;
+ ep = kbd - 1;
+
+ do {
+ unit = strpbrk(ep + 1, "0123456789");
+ if (unit != NULL) {
+ info.kb_unit = strtol(unit, &ep, 10);
+ if (*ep != '\0')
+ info.kb_unit = -1;
+ }
+ } while (unit != NULL && info.kb_unit == -1);
+
+ if (info.kb_unit == -1) {
+ warnx("unable to find keyboard driver unit in '%s'", kbd);
+ return;
+ }
+
+ if (unit == kbd) {
+ warnx("unable to find keyboard driver name in '%s'", kbd);
+ return;
+ }
+ if (unit - kbd >= (int) sizeof(info.kb_name)) {
+ warnx("keyboard name '%s' is too long", kbd);
+ return;
+ }
+
+ strncpy(info.kb_name, kbd, unit - kbd);
+
+ /*
+ * If stdin is not associated with a kbdmux(4) keyboard, the following
+ * ioctl will fail.
+ */
+
+ if (ioctl(0, op, &info) == -1)
+ warn("unable to (un)mux the keyboard");
+}
+
+void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n%s\n",
+"usage: kbdcontrol [-dFKix] [-A name] [-a name] [-b duration.pitch | [quiet.]belltype]",
+" [-r delay.repeat | speed] [-l mapfile] [-f # string]",
+" [-k device] [-L mapfile]");
+ exit(1);
+}
+
+
+int
+main(int argc, char **argv)
+{
+ int opt;
+
+ while((opt = getopt(argc, argv, "A:a:b:df:iKk:Fl:L:r:x")) != -1)
+ switch(opt) {
+ case 'A':
+ case 'a':
+ mux_keyboard((opt == 'A')? KBRELKBD : KBADDKBD, optarg);
+ break;
+ case 'b':
+ set_bell_values(optarg);
+ break;
+ case 'd':
+ print_keymap();
+ break;
+ case 'l':
+ load_keymap(optarg, 0);
+ break;
+ case 'L':
+ load_keymap(optarg, 1);
+ break;
+ case 'f':
+ set_functionkey(optarg,
+ nextarg(argc, argv, &optind, 'f'));
+ break;
+ case 'F':
+ load_default_functionkeys();
+ break;
+ case 'i':
+ show_kbd_info();
+ break;
+ case 'K':
+ release_keyboard();
+ break;
+ case 'k':
+ set_keyboard(optarg);
+ break;
+ case 'r':
+ set_keyrates(optarg);
+ break;
+ case 'x':
+ hex = 1;
+ break;
+ default:
+ usage();
+ }
+ if ((optind != argc) || (argc == 1))
+ usage();
+ exit(0);
+}
diff --git a/usr.sbin/kbdcontrol/kbdmap.5 b/usr.sbin/kbdcontrol/kbdmap.5
new file mode 100644
index 0000000..2b94b35
--- /dev/null
+++ b/usr.sbin/kbdcontrol/kbdmap.5
@@ -0,0 +1,326 @@
+.\" Copyright (c) 2000
+.\" David Malone
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 January 29, 2008
+.Dt KBDMAP 5
+.Os
+.Sh NAME
+.Nm kbdmap
+.Nd keyboard map file format for kbdcontrol
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+A
+.Nm
+file describes how the keys on a keyboard should behave.
+These files can be loaded
+using
+.Xr kbdcontrol 1 ,
+or
+.Xr kbdmap 1
+can be used to select one of the default
+.Nm
+files interactively.
+A
+.Nm
+file can be specified in
+.Xr rc.conf 5 ,
+to be loaded
+at boot time.
+The current keymap may also be printed using
+.Xr kbdcontrol 1 .
+.Pp
+Each line in the file
+can describe a key or an accent.
+A
+.Ql #
+character begins a comment,
+which extends to the end of the line.
+.Pp
+The description of a key
+begins with the scancode for that key.
+Then the effect of the key
+under combinations of
+shift,
+control
+and alt
+are listed in the following order:
+no modifier,
+shift,
+control,
+control and shift,
+alt,
+alt and shift,
+alt and control,
+alt and control and shift.
+The action of the key
+under each modifier can be:
+.Bl -tag -width Ar
+.It ' Ns Ar symbol Ns No '
+The symbol the key should produce,
+in single quotes.
+.It Ar decnum
+The
+.Tn ASCII
+value to produce
+as a decimal number
+(see
+.Xr ascii 7 ) .
+For example, 32 for space.
+.It 0x Ns Ar hexnum
+The
+.Tn ASCII
+value to produce
+as a hexadecimal number.
+For example, 0x20 for space.
+.It Ar ctrlname
+One of the standard names
+for the
+.Tn ASCII
+control characters:
+nul,
+soh,
+stx,
+etx,
+eot,
+enq,
+ack,
+bel,
+bs,
+ht,
+nl,
+vt,
+np,
+cr,
+so,
+si,
+dle,
+dc1,
+dc2,
+dc3,
+dc4,
+nak,
+syn,
+etb,
+can,
+em,
+sub,
+esc,
+fs,
+gs,
+rs,
+ns,
+us,
+sp,
+del.
+.It Ar accentname
+By giving one of the accent names,
+the next key pressed will produce
+an accented character
+in accordance with that accent.
+See the description of accents below.
+The accent names are:
+dgra,
+dacu,
+dcir,
+dtil,
+dmac,
+dbre,
+ddot,
+duml,
+ddia,
+dsla,
+drin,
+dced,
+dapo,
+ddac,
+dogo,
+dcar.
+.It fkey Ns Ar N
+Act as the
+.Ar N Ns No th
+function key,
+where
+.Ar N
+is a decimal number in the range from 1 to 96.
+Refer to the
+.Xr atkbd 4
+manual page for a list of predefined function keys.
+You can use the
+.Fl f
+option of the
+.Xr kbdcontrol 1
+utility to assign arbitrary strings to function keys.
+.It lshift
+Act as left shift key.
+.It rshift
+Act as right shift key.
+.It clock
+Act as caps lock key.
+.It nlock
+Act as num lock key.
+.It slock
+Act as scroll lock key.
+.It lalt|alt
+Act as left alt key.
+.It btab
+Act as backwards tab.
+.It lctrl|ctrl
+Act as left control key.
+.It rctrl
+Act as right control key.
+.It ralt
+Act as right alt (altgr) key.
+.It alock
+Act as alt lock key.
+.It ashift
+Act as alt shift key.
+.It meta
+Act as meta key.
+.It lshifta|shifta
+Act as left shift key / alt lock.
+.It rshifta
+Act as right shift key / alt lock.
+.It lctrla|ctrla
+Act as left ctrl key / alt lock.
+.It rctrla
+Act as right ctrl key / alt lock.
+.It lalta|alta
+Act as left alt key / alt lock.
+.It ralta
+Act as right alt key / alt lock.
+.It nscr
+Act as switch to next screen.
+.It pscr
+Act as switch to previous screen.
+.It scr Ns Ar N
+Switch to screen
+.Ar N ,
+where
+.Ar N
+is a decimal number.
+.It boot
+Reboot the machine.
+.It halt
+Halt the machine.
+.It pdwn
+Halt the machine
+and attempt to power it down.
+.It debug
+Call the debugger.
+.It susp
+Use APM to suspend power.
+.It saver
+Activate screen saver
+by toggling between splash/text screen.
+.It panic
+Panic the system.
+The
+.Xr sysctl 8
+variable
+.Va machdep.enable_panic_key
+must be set to 1 to enable this feature.
+.It paste
+Act as mouse buffer paste.
+.El
+.Pp
+Finally,
+to complete the description of a key,
+a flag which describes
+the effect of caps lock and num lock
+on that key is given.
+The flag can be
+.Ql C
+to indicate that caps lock affects the key,
+.Ql N
+to indicate that num lock affects the key,
+.Ql B
+to indicate that both
+caps lock and num lock affects the key,
+or
+.Ql O
+to indicate that neither affects the key.
+.Pp
+An accent key works
+by modifying the behavior
+of the next key pressed.
+The description of an accent begins
+with one of the accent names
+given above.
+This is followed
+by the symbol for the accent,
+given in single quotes or
+as a decimal or hexadecimal
+.Tn ASCII
+value.
+This symbol will be produced
+if the accent key is pressed and
+then the space key is pressed.
+.Pp
+The description of the accent key
+continues with a list showing
+how it modifies various symbols,
+by giving pairs made up of the normal symbol and
+the modified symbol
+enclosed in parentheses.
+Both symbols in a pair can be given
+in either single quotes or
+as decimal or
+hexadecimal
+.Tn ASCII
+values.
+.Pp
+For example,
+consider the following extract from a
+.Nm :
+.Bd -literal -offset indent
+ 041 dgra 172 nop nop '|' '|' nop nop O
+ dgra '`' ( 'a' 224 ) ( 'A' 192 ) ( 'e' 232 ) ( 'E' 200 )
+ ( 'i' 236 ) ( 'I' 204 ) ( 'o' 242 ) ( 'O' 210 )
+ ( 'u' 249 ) ( 'U' 217 )
+.Ed
+This extract
+configures the backtick key on a UK keyboard
+to act as a grave accent key.
+Pressing backtick followed by space
+produces a backtick, and
+pressing a backtick followed by a vowel
+produces the ISO-8859-1 symbol
+for that vowel with a grave accent.
+.Sh FILES
+.Bl -tag -width /usr/share/syscons/keymaps/* -compact
+.It Pa /usr/share/syscons/keymaps/*
+standard keyboard map files
+.El
+.Sh SEE ALSO
+.Xr kbdcontrol 1 ,
+.Xr kbdmap 1 ,
+.Xr keyboard 4 ,
+.Xr syscons 4 ,
+.Xr ascii 7
+.Sh HISTORY
+This manual page first appeared in
+.Fx 4.2 .
diff --git a/usr.sbin/kbdcontrol/lex.h b/usr.sbin/kbdcontrol/lex.h
new file mode 100644
index 0000000..17e10c2
--- /dev/null
+++ b/usr.sbin/kbdcontrol/lex.h
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 1994-1995 Søren Schmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must 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$
+ */
+
+#define TNOP 256
+#define TLSH 257
+#define TRSH 258
+#define TCLK 259
+#define TNLK 260
+#define TSLK 261
+#define TLALT 262
+#define TLCTR 263
+#define TNEXT 264
+#define TRCTR 265
+#define TRALT 266
+#define TALK 267
+#define TASH 268
+#define TMETA 269
+#define TRBT 270
+#define TDBG 271
+#define TFUNC 272
+#define TSCRN 273
+#define TLET 274
+#define TNUM 275
+#define TFLAG 276
+#define TBTAB 277
+#define TSUSP 278
+#define TACC 279
+#define TSPSC 280
+#define TPREV 281
+#define TPANIC 282
+#define TLSHA 283
+#define TRSHA 284
+#define TLCTRA 285
+#define TRCTRA 286
+#define TLALTA 287
+#define TRALTA 288
+#define THALT 289
+#define TPDWN 290
+#define TPASTE 291
+
+extern int number;
+extern char letter;
+extern FILE *yyin;
+
+extern int yylex(void);
diff --git a/usr.sbin/kbdcontrol/lex.l b/usr.sbin/kbdcontrol/lex.l
new file mode 100644
index 0000000..7d9fd53
--- /dev/null
+++ b/usr.sbin/kbdcontrol/lex.l
@@ -0,0 +1,149 @@
+/*-
+ * Copyright (c) 1994-1995 Søren Schmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must 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$
+ */
+
+%{
+
+#include "lex.h"
+#define YY_NO_UNPUT
+
+%}
+
+D [0-9]
+X [0-9a-fA-F]
+A .
+L [OCNB]
+
+%%
+
+nop { return TNOP; }
+lshift { return TLSH; }
+rshift { return TRSH; }
+clock { return TCLK; }
+nlock { return TNLK; }
+slock { return TSLK; }
+lalt|alt { return TLALT; }
+btab { return TBTAB; }
+lctrl|ctrl { return TLCTR; }
+nscr { return TNEXT; }
+pscr { return TPREV; }
+rctrl { return TRCTR; }
+ralt { return TRALT; }
+alock { return TALK; }
+ashift { return TASH; }
+meta { return TMETA; }
+boot { return TRBT; }
+debug { return TDBG; }
+susp { return TSUSP; }
+saver { return TSPSC; }
+panic { return TPANIC; }
+lshifta|shifta { return TLSHA; }
+rshifta { return TRSHA; }
+lctrla|ctrla { return TLCTRA; }
+rctrla { return TRCTRA; }
+lalta|alta { return TLALTA; }
+ralta { return TRALTA; }
+halt { return THALT; }
+pdwn { return TPDWN; }
+paste { return TPASTE; }
+
+NUL|nul { number = 0; return TNUM; }
+SOH|soh { number = 1; return TNUM; }
+STX|stx { number = 2; return TNUM; }
+ETX|etx { number = 3; return TNUM; }
+EOT|eot { number = 4; return TNUM; }
+ENQ|enq { number = 5; return TNUM; }
+ACK|ack { number = 6; return TNUM; }
+BEL|bel { number = 7; return TNUM; }
+BS|bs { number = 8; return TNUM; }
+HT|ht { number = 9; return TNUM; }
+LF|lf|NL|nl { number = 10; return TNUM; }
+VT|vt { number = 11; return TNUM; }
+FF|ff|NP|np { number = 12; return TNUM; }
+CR|cr { number = 13; return TNUM; }
+SO|so { number = 14; return TNUM; }
+SI|si { number = 15; return TNUM; }
+DLE|dle { number = 16; return TNUM; }
+DC1|dc1 { number = 17; return TNUM; }
+DC2|dc2 { number = 18; return TNUM; }
+DC3|dc3 { number = 19; return TNUM; }
+DC4|dc4 { number = 20; return TNUM; }
+NAK|nak { number = 21; return TNUM; }
+SYN|syn { number = 22; return TNUM; }
+ETB|etb { number = 23; return TNUM; }
+CAN|can { number = 24; return TNUM; }
+EM|em { number = 25; return TNUM; }
+SUB|sub { number = 26; return TNUM; }
+ESC|esc { number = 27; return TNUM; }
+FS|fs { number = 28; return TNUM; }
+GS|gs { number = 29; return TNUM; }
+RS|rs { number = 30; return TNUM; }
+NS|ns { number = 31; return TNUM; }
+US|us { number = 31; return TNUM; }
+SP|sp { number = 32; return TNUM; }
+DEL|del { number = 127; return TNUM; }
+
+dgra|DGRA { number = 0; return TACC; }
+dacu|DACU { number = 1; return TACC; }
+dcir|DCIR { number = 2; return TACC; }
+dtil|DTIL { number = 3; return TACC; }
+dmac|DMAC { number = 4; return TACC; }
+dbre|DBRE { number = 5; return TACC; }
+ddot|DDOT { number = 6; return TACC; }
+duml|DUML { number = 7; return TACC; }
+ddia|DDIA { number = 7; return TACC; }
+dsla|DSLA { number = 8; return TACC; }
+drin|DRIN { number = 9; return TACC; }
+dced|DCED { number = 10; return TACC; }
+dapo|DAPO { number = 11; return TACC; }
+ddac|DDAC { number = 12; return TACC; }
+dogo|DOGO { number = 13; return TACC; }
+dcar|DCAR { number = 14; return TACC; }
+
+fkey{D}({D}*) {
+ sscanf(yytext+4, "%d", &number);
+ return TFUNC;
+ }
+scr{D}({D}*) {
+ sscanf(yytext+3, "%d", &number);
+ return TSCRN;
+ }
+'{A}' { letter = *(yytext+1); return TLET; }
+#({A}*) { /* ignore */ }
+0x{X}({X}*) { sscanf(yytext, "%x", &number); return TNUM; }
+{D}({D}*) { sscanf(yytext, "%d", &number); return TNUM; }
+{L} {
+ if (*yytext == 'O') number = 0;
+ if (*yytext == 'C') number = 1;
+ if (*yytext == 'N') number = 2;
+ if (*yytext == 'B') number = 3;
+ return TFLAG;
+ }
+[ \t\n] { /* ignore */ }
+. { return *yytext; }
diff --git a/usr.sbin/kbdcontrol/path.h b/usr.sbin/kbdcontrol/path.h
new file mode 100644
index 0000000..709acbc
--- /dev/null
+++ b/usr.sbin/kbdcontrol/path.h
@@ -0,0 +1,4 @@
+#define KEYMAP_PATH "/usr/share/syscons/keymaps/"
+#define FONT_PATH "/usr/share/syscons/fonts/"
+#define SCRNMAP_PATH "/usr/share/syscons/scrnmaps/"
+
diff --git a/usr.sbin/kbdmap/Languages.phrases b/usr.sbin/kbdmap/Languages.phrases
new file mode 100644
index 0000000..8bf7922
--- /dev/null
+++ b/usr.sbin/kbdmap/Languages.phrases
@@ -0,0 +1,37 @@
+#
+# Please use only iso-latin character set, which available
+# at console *and* X11 (IBM cp850 does not)
+#
+# missing: Norwegian, Russian, Danish, Swedish
+#
+
+1. Chose your keyboard font
+
+2. Codepage
+
+3. English
+
+4. thin
+
+5. Multilingual Latin I
+
+6. Norwegian
+
+7. Russian
+
+8. West European
+
+9. Chose your keyboard language
+
+10. Danish
+
+11. French
+
+12. German
+
+13. Swedish
+
+14. United Kingdom
+
+15. United States of America
+
diff --git a/usr.sbin/kbdmap/Makefile b/usr.sbin/kbdmap/Makefile
new file mode 100644
index 0000000..ec49341
--- /dev/null
+++ b/usr.sbin/kbdmap/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+PROG= kbdmap
+LINKS= ${BINDIR}/kbdmap ${BINDIR}/vidfont
+MLINKS= kbdmap.1 vidfont.1
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/kbdmap/TODO b/usr.sbin/kbdmap/TODO
new file mode 100644
index 0000000..cef30f7
--- /dev/null
+++ b/usr.sbin/kbdmap/TODO
@@ -0,0 +1,6 @@
+$FreeBSD$
+
+o remember some hackers to translate Languages.phrases into
+ Norwegian, Russian, Danish, Swedish
+
+95/04/03 Wolfram
diff --git a/usr.sbin/kbdmap/kbdmap.1 b/usr.sbin/kbdmap/kbdmap.1
new file mode 100644
index 0000000..a359b38
--- /dev/null
+++ b/usr.sbin/kbdmap/kbdmap.1
@@ -0,0 +1,153 @@
+.\" Copyright (c) March 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.Dd July 3, 2002
+.Dt KBDMAP 1
+.Os
+.Sh NAME
+.Nm kbdmap ,
+.Nm vidfont
+.Nd front end for syscons
+.Sh SYNOPSIS
+.Nm
+.Op Fl K
+.Op Fl V
+.Op Fl d | default
+.Op Fl h | help
+.Op Fl l | lang Ar language
+.Op Fl p | print
+.Op Fl r | restore
+.Op Fl s | show
+.Op Fl v | verbose
+.Sh DESCRIPTION
+The
+.Nm
+utility allows easy setting of available keymaps.
+The
+.Nm vidfont
+command allows the setting of fonts.
+Both examine a database for the keymaps and fonts.
+Descriptions are in English by default, but may also be
+in other languages.
+These programs are interactive and expect to run
+in a terminal to get required input (e.g., a keymap selection)
+from the user.
+.Pp
+It is strongly recommended to not choose
+.Tn MSDOS
+codepage keymaps
+or fonts.
+Use the
+.Tn ISO
+standard version if available!
+.Tn X11
+does not
+support
+.Tn MSDOS
+codepage.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl K
+Run as command
+.Nm .
+.It Fl V
+Run as command
+.Nm vidfont .
+.It Fl d , default
+Use default language.
+Ignore
+.Ev LANG
+environment variable.
+.It Fl h , help
+Print options and exit.
+.It Fl l , lang Ar language
+Use
+.Ar language
+for description and menu.
+.It Fl p , print
+Print description of available keymaps or fonts
+to stdout and exit.
+.It Fl r , restore
+Load default font from
+.Pa /etc/rc.conf .
+.It Fl s , show
+Show currently supported languages and exit.
+.It Fl v , verbose
+More warnings.
+.El
+.Sh ENVIRONMENT
+.Bl -tag -width LANG -compact
+.It Ev LANG
+preferred language
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /usr/share/syscons/keymaps/INDEX.keymaps" -compact
+.It Pa /usr/share/syscons/keymaps/INDEX.keymaps
+database for keymaps
+.It Pa /usr/share/syscons/fonts/INDEX.fonts
+database for fonts
+.It Pa /etc/rc.conf
+default font
+.It Pa /usr/X11/lib/X11/locale/locale.alias
+describe common
+.Ev LANG
+values
+.El
+.Sh SEE ALSO
+.Xr dialog 1 ,
+.Xr kbdcontrol 1 ,
+.Xr vidcontrol 1 ,
+.Xr kbdmap 5 ,
+.Xr rc.conf 5
+.Sh HISTORY
+The
+.Nm
+and
+.Nm vidfont
+commands appeared in
+.Fx 2.1 .
+.Sh AUTHORS
+.An -nosplit
+.An Wolfram Schneider
+.Aq wosch@FreeBSD.org
+wrote the original Perl version.
+The current version was rewritten in C by
+.An Jonathan Belson
+.Aq jon@witchspace.com
+for
+.Fx 5.0 .
+.Sh BUGS
+.\" .Nm kbdmap/vidfont
+.\" does not know which font is in use. E.g. if the current font
+.\" is iso-8859-1 and you chose lang 'ru' (for Russian)
+.\" you get funny latin1 characters and not russkij shrift.
+.\"
+The
+.Nm
+and
+.Nm vidfont
+utilities work only on a (virtual) console and not with
+.Tn X11 .
diff --git a/usr.sbin/kbdmap/kbdmap.c b/usr.sbin/kbdmap/kbdmap.c
new file mode 100644
index 0000000..474553d
--- /dev/null
+++ b/usr.sbin/kbdmap/kbdmap.c
@@ -0,0 +1,842 @@
+/*-
+ * Copyright (c) 2002 Jonathan Belson <jon@witchspace.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stringlist.h>
+#include <unistd.h>
+
+#include "kbdmap.h"
+
+
+static const char *lang_default = DEFAULT_LANG;
+static const char *font;
+static const char *lang;
+static const char *program;
+static const char *keymapdir = DEFAULT_KEYMAP_DIR;
+static const char *fontdir = DEFAULT_FONT_DIR;
+static const char *sysconfig = DEFAULT_SYSCONFIG;
+static const char *font_default = DEFAULT_FONT;
+static const char *font_current;
+static const char *dir;
+static const char *menu = "";
+
+static int x11;
+static int show;
+static int verbose;
+static int print;
+
+
+struct keymap {
+ char *desc;
+ char *keym;
+ int mark;
+ SLIST_ENTRY(keymap) entries;
+};
+static SLIST_HEAD(slisthead, keymap) head = SLIST_HEAD_INITIALIZER(head);
+
+
+/*
+ * Get keymap entry for 'key', or NULL of not found
+ */
+static struct keymap *
+get_keymap(const char *key)
+{
+ struct keymap *km;
+
+ SLIST_FOREACH(km, &head, entries)
+ if (!strcmp(km->keym, key))
+ return km;
+
+ return NULL;
+}
+
+/*
+ * Count the number of keymaps we found
+ */
+static int
+get_num_keymaps(void)
+{
+ struct keymap *km;
+ int count = 0;
+
+ SLIST_FOREACH(km, &head, entries)
+ count++;
+
+ return count;
+}
+
+/*
+ * Remove any keymap with given keym
+ */
+static void
+remove_keymap(const char *keym)
+{
+ struct keymap *km;
+
+ SLIST_FOREACH(km, &head, entries) {
+ if (!strcmp(keym, km->keym)) {
+ SLIST_REMOVE(&head, km, keymap, entries);
+ free(km);
+ break;
+ }
+ }
+}
+
+/*
+ * Add to hash with 'key'
+ */
+static void
+add_keymap(const char *desc, int mark, const char *keym)
+{
+ struct keymap *km, *km_new;
+
+ /* Is there already an entry with this key? */
+ SLIST_FOREACH(km, &head, entries) {
+ if (!strcmp(km->keym, keym)) {
+ /* Reuse this entry */
+ free(km->desc);
+ km->desc = strdup(desc);
+ km->mark = mark;
+ return;
+ }
+ }
+
+ km_new = (struct keymap *) malloc (sizeof(struct keymap));
+ km_new->desc = strdup(desc);
+ km_new->keym = strdup(keym);
+ km_new->mark = mark;
+
+ /* Add to keymap list */
+ SLIST_INSERT_HEAD(&head, km_new, entries);
+}
+
+/*
+ * Figure out the default language to use.
+ */
+static const char *
+get_locale(void)
+{
+ const char *locale;
+
+ if ((locale = getenv("LC_ALL")) == NULL &&
+ (locale = getenv("LC_CTYPE")) == NULL &&
+ (locale = getenv("LANG")) == NULL)
+ locale = lang_default;
+
+ /* Check for alias */
+ if (!strcmp(locale, "C"))
+ locale = DEFAULT_LANG;
+
+ return locale;
+}
+
+/*
+ * Extract filename part
+ */
+static const char *
+extract_name(const char *name)
+{
+ char *p;
+
+ p = strrchr(name, '/');
+ if (p != NULL && p[1] != '\0')
+ return p + 1;
+
+ return name;
+}
+
+/*
+ * Return file extension or NULL
+ */
+static char *
+get_extension(const char *name)
+{
+ char *p;
+
+ p = strrchr(name, '.');
+
+ if (p != NULL && p[1] != '\0')
+ return p;
+
+ return NULL;
+}
+
+/*
+ * Read font from /etc/rc.conf else return default.
+ * Freeing the memory is the caller's responsibility.
+ */
+static char *
+get_font(void)
+{
+ char line[256], buf[20];
+ char *fnt = NULL;
+
+ FILE *fp = fopen(sysconfig, "r");
+ if (fp) {
+ while (fgets(line, sizeof(line), fp)) {
+ int a, b, matches;
+
+ if (line[0] == '#')
+ continue;
+
+ matches = sscanf(line,
+ " font%dx%d = \"%20[-.0-9a-zA-Z_]",
+ &a, &b, buf);
+ if (matches==3) {
+ if (strcmp(buf, "NO")) {
+ if (fnt)
+ free(fnt);
+ fnt = (char *) malloc(strlen(buf) + 1);
+ strcpy(fnt, buf);
+ }
+ }
+ }
+ } else
+ fprintf(stderr, "Could not open %s for reading\n", sysconfig);
+
+ return fnt;
+}
+
+/*
+ * Set a font using 'vidcontrol'
+ */
+static void
+vidcontrol(const char *fnt)
+{
+ char *tmp, *p, *q;
+ char ch;
+ int i;
+
+ /* syscons test failed */
+ if (x11)
+ return;
+
+ tmp = strdup(fnt);
+
+ /* Extract font size */
+ p = strrchr(tmp, '-');
+ if (p && p[1] != '\0') {
+ p++;
+ /* Remove any '.fnt' extension */
+ if ((q = strstr(p, ".fnt")))
+ *q = '\0';
+
+ /*
+ * Check font size is valid, with no trailing characters
+ * ('&ch' should not be matched)
+ */
+ if (sscanf(p, "%dx%d%c", &i, &i, &ch) != 2)
+ fprintf(stderr, "Which font size? %s\n", fnt);
+ else {
+ char *cmd;
+ asprintf(&cmd, "vidcontrol -f %s %s", p, fnt);
+ if (verbose)
+ fprintf(stderr, "%s\n", cmd);
+ system(cmd);
+ free(cmd);
+ }
+ } else
+ fprintf(stderr, "Which font size? %s\n", fnt);
+
+ free(tmp);
+}
+
+/*
+ * Execute 'kbdcontrol' with the appropriate arguments
+ */
+static void
+do_kbdcontrol(struct keymap *km)
+{
+ char *kbd_cmd;
+ asprintf(&kbd_cmd, "kbdcontrol -l %s/%s", dir, km->keym);
+
+ if (!x11)
+ system(kbd_cmd);
+
+ fprintf(stderr, "keymap=%s\n", km->keym);
+ free(kbd_cmd);
+}
+
+/*
+ * Call 'vidcontrol' with the appropriate arguments
+ */
+static void
+do_vidfont(struct keymap *km)
+{
+ char *vid_cmd, *tmp, *p, *q;
+
+ asprintf(&vid_cmd, "%s/%s", dir, km->keym);
+ vidcontrol(vid_cmd);
+ free(vid_cmd);
+
+ tmp = strdup(km->keym);
+ p = strrchr(tmp, '-');
+ if (p && p[1]!='\0') {
+ p++;
+ q = get_extension(p);
+ if (q) {
+ *q = '\0';
+ printf("font%s=%s\n", p, km->keym);
+ }
+ }
+ free(tmp);
+}
+
+/*
+ * Display dialog from 'keymaps[]'
+ */
+static void
+show_dialog(struct keymap **km_sorted, int num_keymaps)
+{
+ FILE *fp;
+ char *cmd, *dialog;
+ char tmp_name[] = "/tmp/_kbd_lang.XXXX";
+ const char *ext;
+ int fd, i, size;
+
+ fd = mkstemp(tmp_name);
+ if (fd == -1) {
+ fprintf(stderr, "Could not open temporary file \"%s\"\n",
+ tmp_name);
+ exit(1);
+ }
+ asprintf(&dialog, "/usr/bin/dialog --clear --title \"Keyboard Menu\" "
+ "--menu \"%s\" -1 -1 10", menu);
+
+ ext = extract_name(dir);
+
+ /* start right font, assume that current font is equal
+ * to default font in /etc/rc.conf
+ *
+ * $font is the font which require the language $lang; e.g.
+ * russian *need* a koi8 font
+ * $font_current is the current font from /etc/rc.conf
+ */
+ if (font && strcmp(font, font_current))
+ vidcontrol(font);
+
+ /* Build up the command */
+ size = 0;
+ for (i=0; i<num_keymaps; i++) {
+ /*
+ * Each 'font' is passed as ' "font" ""', so allow the
+ * extra space
+ */
+ size += strlen(km_sorted[i]->desc) + 6;
+ }
+
+ /* Allow the space for '2> tmpfilename' redirection */
+ size += strlen(tmp_name) + 3;
+
+ cmd = (char *) malloc(strlen(dialog) + size + 1);
+ strcpy(cmd, dialog);
+
+ for (i=0; i<num_keymaps; i++) {
+ strcat(cmd, " \"");
+ strcat(cmd, km_sorted[i]->desc);
+ strcat(cmd, "\"");
+ strcat(cmd, " \"\"");
+ }
+
+ strcat(cmd, " 2>");
+ strcat(cmd, tmp_name);
+
+ /* Show the dialog.. */
+ system(cmd);
+
+ fp = fopen(tmp_name, "r");
+ if (fp) {
+ char choice[64];
+ if (fgets(choice, sizeof(choice), fp) != NULL) {
+ /* Find key for desc */
+ for (i=0; i<num_keymaps; i++) {
+ if (!strcmp(choice, km_sorted[i]->desc)) {
+ if (!strcmp(program, "kbdmap"))
+ do_kbdcontrol(km_sorted[i]);
+ else
+ do_vidfont(km_sorted[i]);
+ break;
+ }
+ }
+ } else {
+ if (font != NULL && strcmp(font, font_current))
+ /* Cancelled, restore old font */
+ vidcontrol(font_current);
+ }
+ fclose(fp);
+ } else
+ fprintf(stderr, "Failed to open temporary file");
+
+ /* Tidy up */
+ remove(tmp_name);
+ free(cmd);
+ free(dialog);
+ close(fd);
+}
+
+/*
+ * Search for 'token' in comma delimited array 'buffer'.
+ * Return true for found, false for not found.
+ */
+static int
+find_token(const char *buffer, const char *token)
+{
+ char *buffer_tmp, *buffer_copy, *inputstring;
+ char **ap;
+ int found;
+
+ buffer_copy = strdup(buffer);
+ buffer_tmp = buffer_copy;
+ inputstring = buffer_copy;
+ ap = &buffer_tmp;
+
+ found = 0;
+
+ while ((*ap = strsep(&inputstring, ",")) != NULL) {
+ if (strcmp(buffer_tmp, token) == 0) {
+ found = 1;
+ break;
+ }
+ }
+
+ free(buffer_copy);
+
+ return found;
+}
+
+/*
+ * Compare function for qsort
+ */
+static int
+compare_keymap(const void *a, const void *b)
+{
+
+ /* We've been passed pointers to pointers, so: */
+ const struct keymap *km1 = *((const struct keymap * const *) a);
+ const struct keymap *km2 = *((const struct keymap * const *) b);
+
+ return strcmp(km1->desc, km2->desc);
+}
+
+/*
+ * Compare function for qsort
+ */
+static int
+compare_lang(const void *a, const void *b)
+{
+ const char *l1 = *((const char * const *) a);
+ const char *l2 = *((const char * const *) b);
+
+ return strcmp(l1, l2);
+}
+
+/*
+ * Change '8x8' to '8x08' so qsort will put it before eg. '8x14'
+ */
+static void
+kludge_desc(struct keymap **km_sorted, int num_keymaps)
+{
+ int i;
+
+ for (i=0; i<num_keymaps; i++) {
+ char *p;
+ char *km = km_sorted[i]->desc;
+ if ((p = strstr(km, "8x8")) != NULL) {
+ int len;
+ int j;
+ int offset;
+
+ offset = p - km;
+
+ /* Make enough space for the extra '0' */
+ len = strlen(km);
+ km = realloc(km, len + 2);
+
+ for (j=len; j!=offset+1; j--)
+ km[j + 1] = km[j];
+
+ km[offset+2] = '0';
+
+ km_sorted[i]->desc = km;
+ }
+ }
+}
+
+/*
+ * Reverse 'kludge_desc()' - change '8x08' back to '8x8'
+ */
+static void
+unkludge_desc(struct keymap **km_sorted, int num_keymaps)
+{
+ int i;
+
+ for (i=0; i<num_keymaps; i++) {
+ char *p;
+ char *km = km_sorted[i]->desc;
+ if ((p = strstr(km, "8x08")) != NULL) {
+ p += 2;
+ while (*p++)
+ p[-1] = p[0];
+
+ km = realloc(km, p - km - 1);
+ km_sorted[i]->desc = km;
+ }
+ }
+}
+
+/*
+ * Return 0 if file exists and is readable, else -1
+ */
+static int
+check_file(const char *keym)
+{
+ int status = 0;
+
+ if (access(keym, R_OK) == -1) {
+ char *fn;
+ asprintf(&fn, "%s/%s", dir, keym);
+ if (access(fn, R_OK) == -1) {
+ if (verbose)
+ fprintf(stderr, "%s not found!\n", fn);
+ status = -1;
+ }
+ free(fn);
+ } else {
+ if (verbose)
+ fprintf(stderr, "No read permission for %s!\n", keym);
+ status = -1;
+ }
+
+ return status;
+}
+
+/*
+ * Read options from the relevent configuration file, then
+ * present to user.
+ */
+static void
+menu_read(void)
+{
+ const char *lg;
+ char *p;
+ int mark, num_keymaps, items, i;
+ char buffer[256], filename[PATH_MAX];
+ char keym[64], lng[64], desc[64];
+ char dialect[64], lang_abk[64];
+ struct keymap *km;
+ struct keymap **km_sorted;
+ struct dirent *dp;
+ StringList *lang_list;
+ FILE *fp;
+ DIR *dirp;
+
+ lang_list = sl_init();
+
+ sprintf(filename, "%s/INDEX.%s", dir, extract_name(dir));
+
+ /* en_US.ISO8859-1 -> en_..\.ISO8859-1 */
+ strlcpy(dialect, lang, sizeof(dialect));
+ if (strlen(dialect) >= 6 && dialect[2] == '_') {
+ dialect[3] = '.';
+ dialect[4] = '.';
+ }
+
+
+ /* en_US.ISO8859-1 -> en */
+ strlcpy(lang_abk, lang, sizeof(lang_abk));
+ if (strlen(lang_abk) >= 3 && lang_abk[2] == '_')
+ lang_abk[2] = '\0';
+
+ fprintf(stderr, "lang_default = %s\n", lang_default);
+ fprintf(stderr, "dialect = %s\n", dialect);
+ fprintf(stderr, "lang_abk = %s\n", lang_abk);
+
+ fp = fopen(filename, "r");
+ if (fp) {
+ int matches;
+ while (fgets(buffer, sizeof(buffer), fp)) {
+ p = buffer;
+ if (p[0] == '#')
+ continue;
+
+ while (isspace(*p))
+ p++;
+
+ if (*p == '\0')
+ continue;
+
+ /* Parse input, removing newline */
+ matches = sscanf(p, "%64[^:]:%64[^:]:%64[^:\n]",
+ keym, lng, desc);
+ if (matches == 3) {
+ if (strcmp(keym, "FONT")
+ && strcmp(keym, "MENU")) {
+ /* Check file exists & is readable */
+ if (check_file(keym) == -1)
+ continue;
+ }
+ }
+
+ if (show) {
+ /*
+ * Take note of supported languages, which
+ * might be in a comma-delimited list
+ */
+ char *tmp = strdup(lng);
+ char *delim = tmp;
+
+ for (delim = tmp; ; ) {
+ char ch = *delim++;
+ if (ch == ',' || ch == '\0') {
+ delim[-1] = '\0';
+ if (!sl_find(lang_list, tmp))
+ sl_add(lang_list, tmp);
+ if (ch == '\0')
+ break;
+ tmp = delim;
+ }
+ }
+ }
+ /* Set empty language to default language */
+ if (lng[0] == '\0')
+ lg = lang_default;
+ else
+ lg = lng;
+
+
+ /* 4) Your choice if it exists
+ * 3) Long match eg. en_GB.ISO8859-1 is equal to
+ * en_..\.ISO8859-1
+ * 2) short match 'de'
+ * 1) default langlist 'en'
+ * 0) any language
+ *
+ * Language may be a comma separated list
+ * A higher match overwrites a lower
+ * A later entry overwrites a previous if it exists
+ * twice in the database
+ */
+
+ /* Check for favoured language */
+ km = get_keymap(keym);
+ mark = (km) ? km->mark : 0;
+
+ if (find_token(lg, lang))
+ add_keymap(desc, 4, keym);
+ else if (mark <= 3 && find_token(lg, dialect))
+ add_keymap(desc, 3, keym);
+ else if (mark <= 2 && find_token(lg, lang_abk))
+ add_keymap(desc, 2, keym);
+ else if (mark <= 1 && find_token(lg, lang_default))
+ add_keymap(desc, 1, keym);
+ else if (mark <= 0)
+ add_keymap(desc, 0, keym);
+ }
+ fclose(fp);
+
+ } else
+ printf("Could not open file\n");
+
+ if (show) {
+ qsort(lang_list->sl_str, lang_list->sl_cur, sizeof(char*),
+ compare_lang);
+ printf("Currently supported languages: ");
+ for (i=0; i< (int) lang_list->sl_cur; i++)
+ printf("%s ", lang_list->sl_str[i]);
+ puts("");
+ exit(0);
+ }
+
+ km = get_keymap("MENU");
+ if (km)
+ /* Take note of menu title */
+ menu = strdup(km->desc);
+ km = get_keymap("FONT");
+ if (km)
+ /* Take note of language font */
+ font = strdup(km->desc);
+
+ /* Remove unwanted items from list */
+ remove_keymap("MENU");
+ remove_keymap("FONT");
+
+ /* Look for keymaps not in database */
+ dirp = opendir(dir);
+ if (dirp) {
+ while ((dp = readdir(dirp)) != NULL) {
+ const char *ext = get_extension(dp->d_name);
+ if (ext) {
+ if ((!strcmp(ext, ".fnt") ||
+ !strcmp(ext, ".kbd")) &&
+ !get_keymap(dp->d_name)) {
+ char *q;
+
+ /* Remove any .fnt or .kbd extension */
+ q = strdup(dp->d_name);
+ *(get_extension(q)) = '\0';
+ add_keymap(q, 0, dp->d_name);
+ free(q);
+
+ if (verbose)
+ fprintf(stderr,
+ "'%s' not in database\n",
+ dp->d_name);
+ }
+ }
+ }
+ closedir(dirp);
+ } else
+ fprintf(stderr, "Could not open directory '%s'\n", dir);
+
+ /* Sort items in keymap */
+ num_keymaps = get_num_keymaps();
+
+ km_sorted = (struct keymap **)
+ malloc(num_keymaps*sizeof(struct keymap *));
+
+ /* Make array of pointers to items in hash */
+ items = 0;
+ SLIST_FOREACH(km, &head, entries)
+ km_sorted[items++] = km;
+
+ /* Change '8x8' to '8x08' so sort works as we might expect... */
+ kludge_desc(km_sorted, num_keymaps);
+
+ qsort(km_sorted, num_keymaps, sizeof(struct keymap *), compare_keymap);
+
+ /* ...change back again */
+ unkludge_desc(km_sorted, num_keymaps);
+
+ if (print) {
+ for (i=0; i<num_keymaps; i++)
+ printf("%s\n", km_sorted[i]->desc);
+ exit(0);
+ }
+
+ show_dialog(km_sorted, num_keymaps);
+
+ free(km_sorted);
+}
+
+/*
+ * Display usage information and exit
+ */
+static void
+usage(void)
+{
+
+ fprintf(stderr, "usage: %s\t[-K] [-V] [-d|-default] [-h|-help] "
+ "[-l|-lang language]\n\t\t[-p|-print] [-r|-restore] [-s|-show] "
+ "[-v|-verbose]\n", program);
+ exit(1);
+}
+
+static void
+parse_args(int argc, char **argv)
+{
+ int i;
+
+ for (i=1; i<argc; i++) {
+ if (argv[i][0] != '-')
+ usage();
+ else if (!strcmp(argv[i], "-help") || !strcmp(argv[i], "-h"))
+ usage();
+ else if (!strcmp(argv[i], "-verbose") || !strcmp(argv[i], "-v"))
+ verbose = 1;
+ else if (!strcmp(argv[i], "-lang") || !strcmp(argv[i], "-l"))
+ if (i + 1 == argc)
+ usage();
+ else
+ lang = argv[++i];
+ else if (!strcmp(argv[i], "-default") || !strcmp(argv[i], "-d"))
+ lang = lang_default;
+ else if (!strcmp(argv[i], "-show") || !strcmp(argv[i], "-s"))
+ show = 1;
+ else if (!strcmp(argv[i], "-print") || !strcmp(argv[i], "-p"))
+ print = 1;
+ else if (!strcmp(argv[i], "-restore") ||
+ !strcmp(argv[i], "-r")) {
+ vidcontrol(font_current);
+ exit(0);
+ } else if (!strcmp(argv[i], "-K"))
+ dir = keymapdir;
+ else if (!strcmp(argv[i], "-V"))
+ dir = fontdir;
+ else
+ usage();
+ }
+}
+
+/*
+ * A front-end for the 'vidfont' and 'kbdmap' programs.
+ */
+int
+main(int argc, char **argv)
+{
+
+ x11 = system("kbdcontrol -d >/dev/null");
+
+ if (x11) {
+ fprintf(stderr, "You are not on a virtual console - "
+ "expect certain strange side-effects\n");
+ sleep(2);
+ }
+
+ SLIST_INIT(&head);
+
+ lang = get_locale();
+
+ program = extract_name(argv[0]);
+
+ font_current = get_font();
+ if (font_current == NULL)
+ font_current = font_default;
+
+ if (strcmp(program, "kbdmap"))
+ dir = fontdir;
+ else
+ dir = keymapdir;
+
+ /* Parse command line arguments */
+ parse_args(argc, argv);
+
+ /* Read and display options */
+ menu_read();
+
+ return 0;
+}
diff --git a/usr.sbin/kbdmap/kbdmap.h b/usr.sbin/kbdmap/kbdmap.h
new file mode 100644
index 0000000..e57fa8c
--- /dev/null
+++ b/usr.sbin/kbdmap/kbdmap.h
@@ -0,0 +1,34 @@
+/*-
+ * Copyright (c) 2002 Jonathan Belson <jon@witchspace.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+
+#define DEFAULT_LANG "en"
+#define DEFAULT_KEYMAP_DIR "/usr/share/syscons/keymaps"
+#define DEFAULT_FONT_DIR "/usr/share/syscons/fonts"
+#define DEFAULT_SYSCONFIG "/etc/rc.conf"
+#define DEFAULT_FONT "cp437-8x16.fnt"
diff --git a/usr.sbin/kernbb/Makefile b/usr.sbin/kernbb/Makefile
new file mode 100644
index 0000000..7da462b
--- /dev/null
+++ b/usr.sbin/kernbb/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+PROG= kernbb
+MAN= kernbb.8
+
+DPADD= ${LIBKVM}
+LDADD= -lkvm
+
+CFLAGS+= -I${.CURDIR}/../../contrib/gcc
+
+WARNS?= 3
+.include <bsd.prog.mk>
+
diff --git a/usr.sbin/kernbb/kernbb.8 b/usr.sbin/kernbb/kernbb.8
new file mode 100644
index 0000000..3d6fe02
--- /dev/null
+++ b/usr.sbin/kernbb/kernbb.8
@@ -0,0 +1,82 @@
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 22, 1995
+.Dt KERNBB 8
+.Os
+.Sh NAME
+.Nm kernbb
+.Nd generate a dump of the kernels basic-block profile buffers
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to extract the basic-block profiling buffers of the running
+kernel into the files needed for the
+.Xr gcov 1
+tool.
+.Pp
+At least one source file in the running kernel must have been compiled
+with the
+.Fl Fl test-coverage
+and
+.Fl Fl profile-arcs
+options.
+.Pp
+The output is stored in the filenames compiled into the kernel by
+.Xr gcc 1 .
+If the absolute pathname cannot be written to, the directory part
+of the filename is discarded and the file stored in the current
+directory under its basename.
+.Pp
+The output files are named
+.Pa *.da ,
+and the
+.Xr gcov 1
+program will extract the counts and merge them with the source
+file to show actual execution counts.
+.Sh FILES
+.Bl -tag -width /boot/kernel/kernel -compact
+.It Pa /boot/kernel/kernel
+the default system
+.It Pa /dev/kmem
+the default memory
+.El
+.Sh SEE ALSO
+.Xr cc 1 ,
+.Xr gcov 1
+.Sh AUTHORS
+The
+.Nm
+utility was written by
+.An Poul-Henning Kamp ,
+along with the kernel-support.
+.Sh BUGS
+There are far too much magic and internal knowledge from GCC in this.
diff --git a/usr.sbin/kernbb/kernbb.c b/usr.sbin/kernbb/kernbb.c
new file mode 100644
index 0000000..1ecd5ad
--- /dev/null
+++ b/usr.sbin/kernbb/kernbb.c
@@ -0,0 +1,145 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "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
+ * ----------------------------------------------------------------------------
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <nlist.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/endian.h>
+
+typedef long long gcov_type;
+
+#define PARAMS(foo) foo
+#define ATTRIBUTE_UNUSED __unused
+#include "gcov-io.h"
+
+struct bbf {
+ long checksum;
+ int arc_count;
+ u_long name;
+};
+
+struct bb {
+ u_long zero_one;
+ u_long filename;
+ u_long counts;
+ u_long ncounts;
+ u_long next;
+ u_long sizeof_bb;
+ u_long funcs;
+};
+
+struct nlist namelist[] = {
+ { "bbhead", 0, 0, 0, 0 },
+ { NULL, 0, 0, 0, 0 }
+};
+
+kvm_t *kv;
+
+int
+main(int argc __unused, char **argv __unused)
+{
+ int i, funcs;
+ u_long l1,l2,l4;
+ struct bb bb;
+ struct bbf bbf;
+ char buf[BUFSIZ], *p;
+ gcov_type *q, *qr;
+
+ FILE *f;
+
+ kv = kvm_open(NULL,NULL,NULL,O_RDWR,"dnc");
+ if (!kv)
+ err(1,"kvm_open");
+ i = kvm_nlist(kv,namelist);
+ if (i)
+ err(1,"kvm_nlist");
+
+ l1 = namelist[0].n_value;
+ kvm_read(kv,l1,&l2,sizeof l2);
+ while(l2) {
+ l1 += sizeof l1;
+ kvm_read(kv,l2,&bb,sizeof bb);
+#if 0
+printf("%lx\n%lx\n%lx\n%lx\n%lx\n%lx\n%lx\n",
+ bb.zero_one, bb.filename, bb.counts, bb.ncounts, bb.next,
+ bb.sizeof_bb, bb.funcs);
+#endif
+
+ funcs = 0;
+ for (l4 = bb.funcs; ; l4 += sizeof (bbf)) {
+ kvm_read(kv, l4, &bbf, sizeof(bbf));
+ if (bbf.arc_count == -1)
+ break;
+ funcs++;
+ }
+
+ l2 = bb.next;
+
+ kvm_read(kv, bb.filename, buf, sizeof(buf));
+ p = buf;
+ f = fopen(p, "w");
+ if (f != NULL) {
+ printf("Writing \"%s\"\n", p);
+ } else {
+ p = strrchr(buf, '/');
+ if (p == NULL)
+ p = buf;
+ else
+ p++;
+ printf("Writing \"%s\" (spec \"%s\")\n", p, buf);
+ f = fopen(p, "w");
+ }
+ if (f == NULL)
+ err(1,"%s", p);
+ __write_long(-123, f, 4);
+
+ __write_long(funcs, f, 4);
+
+ __write_long(4 + 8 + 8 + 4 + 8 + 8, f, 4);
+
+ __write_long(bb.ncounts, f, 4);
+ __write_long(0, f, 8);
+ __write_long(0, f, 8);
+
+ __write_long(bb.ncounts, f, 4);
+ __write_long(0, f, 8);
+ __write_long(0, f, 8);
+
+ qr = malloc(bb.ncounts * 8);
+ kvm_read(kv, bb.counts, qr, bb.ncounts * 8);
+ q = qr;
+ for (l4 = bb.funcs; ; l4 += sizeof (bbf)) {
+ kvm_read(kv, l4, &bbf, sizeof(bbf));
+ if (bbf.arc_count == -1)
+ break;
+ kvm_read(kv, bbf.name, buf, sizeof(buf));
+
+ __write_gcov_string(buf, strlen(buf), f, -1);
+
+ __write_long(bbf.checksum, f, 4);
+ __write_long(bbf.arc_count, f, 4);
+ for (i = 0; i < bbf.arc_count; i++) {
+ __write_gcov_type(*q, f, 8);
+ q++;
+ }
+ }
+ fclose(f);
+ free(qr);
+ }
+ return 0;
+}
diff --git a/usr.sbin/keyserv/Makefile b/usr.sbin/keyserv/Makefile
new file mode 100644
index 0000000..37a416f
--- /dev/null
+++ b/usr.sbin/keyserv/Makefile
@@ -0,0 +1,26 @@
+# $FreeBSD$
+
+PROG= keyserv
+MAN= keyserv.8
+SRCS= keyserv.c setkey.c crypt_svc.c crypt_server.c crypt.h
+
+CFLAGS+= -DKEYSERV_RANDOM -DBROKEN_DES -I.
+
+DPADD= ${LIBMP} ${LIBCRYPTO} ${LIBRPCSVC}
+LDADD= -lmp -lcrypto -lrpcsvc
+
+WARNS?= 1
+
+RPCDIR= ${DESTDIR}/usr/include/rpcsvc
+
+CLEANFILES= crypt_svc.c crypt.h
+
+RPCGEN= rpcgen -C
+
+crypt_svc.c: ${RPCDIR}/crypt.x
+ ${RPCGEN} -m -o ${.TARGET} ${RPCDIR}/crypt.x
+
+crypt.h: ${RPCDIR}/crypt.x
+ ${RPCGEN} -h -o ${.TARGET} ${RPCDIR}/crypt.x
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/keyserv/crypt_server.c b/usr.sbin/keyserv/crypt_server.c
new file mode 100644
index 0000000..45f6f6e
--- /dev/null
+++ b/usr.sbin/keyserv/crypt_server.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <rpc/des_crypt.h>
+#include <rpc/des.h>
+#include "crypt.h"
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * The U.S. government stupidly believes that a) it can keep strong
+ * crypto code a secret and b) that doing so somehow protects national
+ * interests. It's wrong on both counts, but until it listens to reason
+ * we have to make certain compromises so it doesn't have an excuse to
+ * throw us in federal prison.
+ *
+ * Consequently, the core OS ships without DES support, and keyserv
+ * defaults to using ARCFOUR with only a 40 bit key, just like nutscrape.
+ * This breaks compatibility with Secure RPC on other systems, but it
+ * allows Secure RPC to work between FreeBSD systems that don't have the
+ * DES package installed without throwing security totally out the window.
+ *
+ * In order to avoid having to supply two versions of keyserv (one with
+ * DES and one without), we use dlopen() and friends to load libdes.so
+ * into our address space at runtime. We check for the presence of
+ * /usr/lib/libdes.so.3.0 at startup and load it if we find it. If we
+ * can't find it, or the __des_crypt symbol doesn't exist, we fall back
+ * to the ARCFOUR encryption code. The user can specify another path using
+ * the -p flag.
+ */
+
+ /* arcfour.h */
+typedef struct arcfour_key
+{
+ unsigned char state[256];
+ unsigned char x;
+ unsigned char y;
+} arcfour_key;
+
+static void prepare_key(unsigned char *key_data_ptr,int key_data_len,
+ arcfour_key *key);
+static void arcfour(unsigned char *buffer_ptr,int buffer_len,arcfour_key * key);
+static void swap_byte(unsigned char *a, unsigned char *b);
+
+static void prepare_key(unsigned char *key_data_ptr, int key_data_len,
+ arcfour_key *key)
+{
+ unsigned char index1;
+ unsigned char index2;
+ unsigned char* state;
+ short counter;
+
+ state = &key->state[0];
+ for(counter = 0; counter < 256; counter++)
+ state[counter] = counter;
+ key->x = 0;
+ key->y = 0;
+ index1 = 0;
+ index2 = 0;
+ for(counter = 0; counter < 256; counter++)
+ {
+ index2 = (key_data_ptr[index1] + state[counter] +
+ index2) % 256;
+ swap_byte(&state[counter], &state[index2]);
+
+ index1 = (index1 + 1) % key_data_len;
+ }
+}
+
+static void arcfour(unsigned char *buffer_ptr, int buffer_len, arcfour_key *key)
+{
+ unsigned char x;
+ unsigned char y;
+ unsigned char* state;
+ unsigned char xorIndex;
+ short counter;
+
+ x = key->x;
+ y = key->y;
+
+ state = &key->state[0];
+ for(counter = 0; counter < buffer_len; counter ++)
+ {
+ x = (x + 1) % 256;
+ y = (state[x] + y) % 256;
+ swap_byte(&state[x], &state[y]);
+
+ xorIndex = (state[x] + state[y]) % 256;
+
+ buffer_ptr[counter] ^= state[xorIndex];
+ }
+ key->x = x;
+ key->y = y;
+}
+
+static void swap_byte(unsigned char *a, unsigned char *b)
+{
+ unsigned char swapByte;
+
+ swapByte = *a;
+ *a = *b;
+ *b = swapByte;
+}
+
+/* Dummy _des_crypt function that uses ARCFOUR with a 40 bit key */
+int _arcfour_crypt(buf, len, desp)
+ char *buf;
+ int len;
+ struct desparams *desp;
+{
+ struct arcfour_key arcfourk;
+
+ /*
+ * U.S. government anti-crypto weasels take
+ * note: although we are supplied with a 64 bit
+ * key, we're only passing 40 bits to the ARCFOUR
+ * encryption code. So there.
+ */
+ prepare_key(desp->des_key, 5, &arcfourk);
+ arcfour(buf, len, &arcfourk);
+
+ return(DESERR_NOHWDEVICE);
+}
+
+int (*_my_crypt)(char *, int, struct desparams *) = NULL;
+
+static void *dlhandle;
+
+#ifndef _PATH_USRLIB
+#define _PATH_USRLIB "/usr/lib"
+#endif
+
+#ifndef LIBCRYPTO
+#define LIBCRYPTO "libcrypto.so.2"
+#endif
+
+void load_des(warn, libpath)
+ int warn;
+ char *libpath;
+{
+ char dlpath[MAXPATHLEN];
+
+ if (libpath == NULL) {
+ snprintf(dlpath, sizeof(dlpath), "%s/%s", _PATH_USRLIB, LIBCRYPTO);
+ } else
+ snprintf(dlpath, sizeof(dlpath), "%s", libpath);
+
+ if (dlpath != NULL && (dlhandle = dlopen(dlpath, 0444)) != NULL)
+ _my_crypt = (int (*)())dlsym(dlhandle, "_des_crypt");
+
+ if (_my_crypt == NULL) {
+ if (dlhandle != NULL)
+ dlclose(dlhandle);
+ _my_crypt = &_arcfour_crypt;
+ if (warn) {
+ printf ("DES support disabled -- using ARCFOUR instead.\n");
+ printf ("Warning: ARCFOUR cipher is not compatible with ");
+ printf ("other Secure RPC implementations.\nInstall ");
+ printf ("the FreeBSD 'des' distribution to enable");
+ printf (" DES encryption.\n");
+ }
+ } else {
+ if (warn) {
+ printf ("DES support enabled\n");
+ printf ("Using %s shared object.\n", dlpath);
+ }
+ }
+
+ return;
+}
+
+desresp *
+des_crypt_1_svc(desargs *argp, struct svc_req *rqstp)
+{
+ static desresp result;
+ struct desparams dparm;
+
+ if (argp->desbuf.desbuf_len > DES_MAXDATA) {
+ result.stat = DESERR_BADPARAM;
+ return(&result);
+ }
+
+
+ bcopy(argp->des_key, dparm.des_key, 8);
+ bcopy(argp->des_ivec, dparm.des_ivec, 8);
+ dparm.des_mode = argp->des_mode;
+ dparm.des_dir = argp->des_dir;
+#ifdef BROKEN_DES
+ dparm.UDES.UDES_buf = argp->desbuf.desbuf_val;
+#endif
+
+ /*
+ * XXX This compensates for a bug in the libdes Secure RPC
+ * compat interface. (Actually, there are a couple.) The
+ * des_ecb_encrypt() routine in libdes only encrypts 8 bytes
+ * (64 bits) at a time. However, the Sun Secure RPC ecb_crypt()
+ * routine is supposed to be able to handle buffers up to 8Kbytes.
+ * The rpc_enc module in libdes ignores this fact and just drops
+ * the length parameter on the floor, encrypting only the
+ * first 64 bits of whatever buffer you feed it. We deal with
+ * this here: if we're using DES encryption, and we're using
+ * ECB mode, then we make a pass over the entire buffer
+ * ourselves. Note: the rpc_enc module incorrectly transposes
+ * the mode flags, so when you ask for CBC mode, you're really
+ * getting ECB mode.
+ */
+#ifdef BROKEN_DES
+ if (_my_crypt != &_arcfour_crypt && argp->des_mode == CBC) {
+#else
+ if (_my_crypt != &_arcfour_crypt && argp->des_mode == ECB) {
+#endif
+ int i;
+ char *dptr;
+
+ for (i = 0; i < argp->desbuf.desbuf_len / 8; i++) {
+ dptr = argp->desbuf.desbuf_val;
+ dptr += (i * 8);
+#ifdef BROKEN_DES
+ dparm.UDES.UDES_buf = dptr;
+#endif
+ result.stat = _my_crypt(dptr, 8, &dparm);
+ }
+ } else {
+ result.stat = _my_crypt(argp->desbuf.desbuf_val,
+ argp->desbuf.desbuf_len,
+ &dparm);
+ }
+
+ if (result.stat == DESERR_NONE || result.stat == DESERR_NOHWDEVICE) {
+ bcopy(dparm.des_ivec, result.des_ivec, 8);
+ result.desbuf.desbuf_len = argp->desbuf.desbuf_len;
+ result.desbuf.desbuf_val = argp->desbuf.desbuf_val;
+ }
+
+ return (&result);
+}
diff --git a/usr.sbin/keyserv/keyserv.8 b/usr.sbin/keyserv/keyserv.8
new file mode 100644
index 0000000..726f91c
--- /dev/null
+++ b/usr.sbin/keyserv/keyserv.8
@@ -0,0 +1,80 @@
+.\" @(#)keyserv.1m 1.21 93/07/14 SMI; from SVr4
+.\"macro stdmacro
+.\" Copyright 1989 AT&T
+.\" @(#)keyserv.8c 1.8 89/03/29 SMI;
+.\" $FreeBSD$
+.\".TH KEYSERV 8C "9 September 1987"
+.Dd September 14, 1992
+.Dt KEYSERV 8
+.Os
+.Sh NAME
+.Nm keyserv
+.Nd server for storing private encryption keys
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl D
+.Op Fl n
+.Op Fl p Ar path
+.Op Fl v
+.Sh DESCRIPTION
+The
+.Nm
+utility is a daemon that is used for storing the
+private encryption keys of each
+user logged into the system.
+These encryption keys are used for accessing
+secure network services such as secure
+.Tn NFS .
+.Pp
+Normally, root's key is read from the file
+.Pa /etc/.rootkey
+when the daemon is started.
+This is useful during power-fail reboots
+when no one is around to type a password.
+.Pp
+If a client with no secret key calls
+.Nm ,
+then the key of user
+.Em nobody
+is used instead as the default key.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d
+Disable the use of default keys for
+.Em nobody .
+.It Fl D
+Run in debugging mode and log all requests to
+.Nm .
+.It Fl n
+Root's secret key is not read from
+.Pa /etc/.rootkey .
+Instead,
+.Nm
+prompts the user for the password to decrypt
+root's key stored in the
+.Pa /etc/publickey
+database and then stores the decrypted key in
+.Pa /etc/.rootkey
+for future use.
+This option is useful if the
+.Pa /etc/.rootkey
+file ever gets out of date or corrupted.
+.It Fl p Ar path
+Specify where to search for
+.Pa libdes.so.3 .
+Default is
+.Pa /usr/lib .
+.It Fl v
+Display status of DES support (enabled/disabled).
+.El
+.Sh FILES
+.Bl -tag -width /usr/lib/libdes.so.3 -compact
+.It Pa /etc/.rootkey
+.It Pa /usr/lib/libdes.so.3
+.El
+.Sh "SEE ALSO"
+.Xr keylogin 1 ,
+.Xr keylogout 1 ,
+.Xr publickey 5
diff --git a/usr.sbin/keyserv/keyserv.c b/usr.sbin/keyserv/keyserv.c
new file mode 100644
index 0000000..04e4972
--- /dev/null
+++ b/usr.sbin/keyserv/keyserv.c
@@ -0,0 +1,821 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)keyserv.c 1.15 94/04/25 SMI";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc.
+ */
+
+/*
+ * Keyserver
+ * Store secret keys per uid. Do public key encryption and decryption
+ * operations. Generate "random" keys.
+ * Do not talk to anything but a local root
+ * process on the local transport only
+ */
+
+#include <err.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <rpc/rpc.h>
+#include <sys/param.h>
+#include <sys/file.h>
+#include <rpc/des_crypt.h>
+#include <rpc/des.h>
+#include <rpc/key_prot.h>
+#include <rpcsvc/crypt.h>
+#include "keyserv.h"
+
+#ifndef NGROUPS
+#define NGROUPS 16
+#endif
+
+#ifndef KEYSERVSOCK
+#define KEYSERVSOCK "/var/run/keyservsock"
+#endif
+
+static void randomize( des_block * );
+static void usage( void );
+static int getrootkey( des_block *, int );
+static int root_auth( SVCXPRT *, struct svc_req * );
+
+#ifdef DEBUG
+static int debugging = 1;
+#else
+static int debugging = 0;
+#endif
+
+static void keyprogram();
+static des_block masterkey;
+char *getenv();
+static char ROOTKEY[] = "/etc/.rootkey";
+
+/*
+ * Hack to allow the keyserver to use AUTH_DES (for authenticated
+ * NIS+ calls, for example). The only functions that get called
+ * are key_encryptsession_pk, key_decryptsession_pk, and key_gendes.
+ *
+ * The approach is to have the keyserver fill in pointers to local
+ * implementations of these functions, and to call those in key_call().
+ */
+
+extern cryptkeyres *(*__key_encryptsession_pk_LOCAL)();
+extern cryptkeyres *(*__key_decryptsession_pk_LOCAL)();
+extern des_block *(*__key_gendes_LOCAL)();
+extern int (*__des_crypt_LOCAL)();
+
+cryptkeyres *key_encrypt_pk_2_svc_prog( uid_t, cryptkeyarg2 * );
+cryptkeyres *key_decrypt_pk_2_svc_prog( uid_t, cryptkeyarg2 * );
+des_block *key_gen_1_svc_prog( void *, struct svc_req * );
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int nflag = 0;
+ int c;
+ int warn = 0;
+ char *path = NULL;
+ void *localhandle;
+ register SVCXPRT *transp;
+ struct netconfig *nconf = NULL;
+
+ __key_encryptsession_pk_LOCAL = &key_encrypt_pk_2_svc_prog;
+ __key_decryptsession_pk_LOCAL = &key_decrypt_pk_2_svc_prog;
+ __key_gendes_LOCAL = &key_gen_1_svc_prog;
+
+ while ((c = getopt(argc, argv, "ndDvp:")) != -1)
+ switch (c) {
+ case 'n':
+ nflag++;
+ break;
+ case 'd':
+ pk_nodefaultkeys();
+ break;
+ case 'D':
+ debugging = 1;
+ break;
+ case 'v':
+ warn = 1;
+ break;
+ case 'p':
+ path = optarg;
+ break;
+ default:
+ usage();
+ }
+
+ load_des(warn, path);
+ __des_crypt_LOCAL = _my_crypt;
+ if (svc_auth_reg(AUTH_DES, _svcauth_des) == -1)
+ errx(1, "failed to register AUTH_DES authenticator");
+
+ if (optind != argc) {
+ usage();
+ }
+
+ /*
+ * Initialize
+ */
+ (void) umask(S_IXUSR|S_IXGRP|S_IXOTH);
+ if (geteuid() != 0)
+ errx(1, "keyserv must be run as root");
+ setmodulus(HEXMODULUS);
+ getrootkey(&masterkey, nflag);
+
+ rpcb_unset(KEY_PROG, KEY_VERS, NULL);
+ rpcb_unset(KEY_PROG, KEY_VERS2, NULL);
+
+ if (svc_create(keyprogram, KEY_PROG, KEY_VERS,
+ "netpath") == 0) {
+ (void) fprintf(stderr,
+ "%s: unable to create service\n", argv[0]);
+ exit(1);
+ }
+
+ if (svc_create(keyprogram, KEY_PROG, KEY_VERS2,
+ "netpath") == 0) {
+ (void) fprintf(stderr,
+ "%s: unable to create service\n", argv[0]);
+ exit(1);
+ }
+
+ localhandle = setnetconfig();
+ while ((nconf = getnetconfig(localhandle)) != NULL) {
+ if (nconf->nc_protofmly != NULL &&
+ strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0)
+ break;
+ }
+
+ if (nconf == NULL)
+ errx(1, "getnetconfig: %s", nc_sperror());
+
+ unlink(KEYSERVSOCK);
+ rpcb_unset(CRYPT_PROG, CRYPT_VERS, nconf);
+ transp = svcunix_create(RPC_ANYSOCK, 0, 0, KEYSERVSOCK);
+ if (transp == NULL)
+ errx(1, "cannot create AF_LOCAL service");
+ if (!svc_reg(transp, KEY_PROG, KEY_VERS, keyprogram, nconf))
+ errx(1, "unable to register (KEY_PROG, KEY_VERS, unix)");
+ if (!svc_reg(transp, KEY_PROG, KEY_VERS2, keyprogram, nconf))
+ errx(1, "unable to register (KEY_PROG, KEY_VERS2, unix)");
+ if (!svc_reg(transp, CRYPT_PROG, CRYPT_VERS, crypt_prog_1, nconf))
+ errx(1, "unable to register (CRYPT_PROG, CRYPT_VERS, unix)");
+
+ endnetconfig(localhandle);
+
+ (void) umask(066); /* paranoia */
+
+ if (!debugging) {
+ daemon(0,0);
+ }
+
+ signal(SIGPIPE, SIG_IGN);
+
+ svc_run();
+ abort();
+ /* NOTREACHED */
+}
+
+/*
+ * In the event that we don't get a root password, we try to
+ * randomize the master key the best we can
+ */
+static void
+randomize(master)
+ des_block *master;
+{
+#ifndef __FreeBSD__
+ int i;
+ int seed;
+ struct timeval tv;
+ int shift;
+
+ seed = 0;
+ for (i = 0; i < 1024; i++) {
+ (void) gettimeofday(&tv, (struct timezone *) NULL);
+ shift = i % 8 * sizeof (int);
+ seed ^= (tv.tv_usec << shift) | (tv.tv_usec >> (32 - shift));
+ }
+#endif
+#ifdef KEYSERV_RANDOM
+#ifdef __FreeBSD__
+ master->key.low = arc4random();
+ master->key.high = arc4random();
+#else
+ srandom(seed);
+ master->key.low = random();
+ master->key.high = random();
+#endif
+#else
+ /* use stupid dangerous bad rand() */
+#ifdef __FreeBSD__
+ sranddev();
+#else
+ srand(seed);
+#endif
+ master->key.low = rand();
+ master->key.high = rand();
+#endif
+}
+
+/*
+ * Try to get root's secret key, by prompting if terminal is a tty, else trying
+ * from standard input.
+ * Returns 1 on success.
+ */
+static int
+getrootkey(master, prompt)
+ des_block *master;
+ int prompt;
+{
+ char *passwd;
+ char name[MAXNETNAMELEN + 1];
+ char secret[HEXKEYBYTES];
+ key_netstarg netstore;
+ int fd;
+
+ if (!prompt) {
+ /*
+ * Read secret key out of ROOTKEY
+ */
+ fd = open(ROOTKEY, O_RDONLY, 0);
+ if (fd < 0) {
+ randomize(master);
+ return (0);
+ }
+ if (read(fd, secret, HEXKEYBYTES) < HEXKEYBYTES) {
+ warnx("the key read from %s was too short", ROOTKEY);
+ (void) close(fd);
+ return (0);
+ }
+ (void) close(fd);
+ if (!getnetname(name)) {
+ warnx(
+ "failed to generate host's netname when establishing root's key");
+ return (0);
+ }
+ memcpy(netstore.st_priv_key, secret, HEXKEYBYTES);
+ memset(netstore.st_pub_key, 0, HEXKEYBYTES);
+ netstore.st_netname = name;
+ if (pk_netput(0, &netstore) != KEY_SUCCESS) {
+ warnx("could not set root's key and netname");
+ return (0);
+ }
+ return (1);
+ }
+ /*
+ * Decrypt yellow pages publickey entry to get secret key
+ */
+ passwd = getpass("root password:");
+ passwd2des(passwd, (char *)master);
+ getnetname(name);
+ if (!getsecretkey(name, secret, passwd)) {
+ warnx("can't find %s's secret key", name);
+ return (0);
+ }
+ if (secret[0] == 0) {
+ warnx("password does not decrypt secret key for %s", name);
+ return (0);
+ }
+ (void) pk_setkey(0, secret);
+ /*
+ * Store it for future use in $ROOTKEY, if possible
+ */
+ fd = open(ROOTKEY, O_WRONLY|O_TRUNC|O_CREAT, 0);
+ if (fd > 0) {
+ char newline = '\n';
+
+ write(fd, secret, strlen(secret));
+ write(fd, &newline, sizeof (newline));
+ close(fd);
+ }
+ return (1);
+}
+
+/*
+ * Procedures to implement RPC service
+ */
+char *
+strstatus(status)
+ keystatus status;
+{
+ switch (status) {
+ case KEY_SUCCESS:
+ return ("KEY_SUCCESS");
+ case KEY_NOSECRET:
+ return ("KEY_NOSECRET");
+ case KEY_UNKNOWN:
+ return ("KEY_UNKNOWN");
+ case KEY_SYSTEMERR:
+ return ("KEY_SYSTEMERR");
+ default:
+ return ("(bad result code)");
+ }
+}
+
+keystatus *
+key_set_1_svc_prog(uid, key)
+ uid_t uid;
+ keybuf key;
+{
+ static keystatus status;
+
+ if (debugging) {
+ (void) fprintf(stderr, "set(%ld, %.*s) = ", uid,
+ (int) sizeof (keybuf), key);
+ }
+ status = pk_setkey(uid, key);
+ if (debugging) {
+ (void) fprintf(stderr, "%s\n", strstatus(status));
+ (void) fflush(stderr);
+ }
+ return (&status);
+}
+
+cryptkeyres *
+key_encrypt_pk_2_svc_prog(uid, arg)
+ uid_t uid;
+ cryptkeyarg2 *arg;
+{
+ static cryptkeyres res;
+
+ if (debugging) {
+ (void) fprintf(stderr, "encrypt(%ld, %s, %08x%08x) = ", uid,
+ arg->remotename, arg->deskey.key.high,
+ arg->deskey.key.low);
+ }
+ res.cryptkeyres_u.deskey = arg->deskey;
+ res.status = pk_encrypt(uid, arg->remotename, &(arg->remotekey),
+ &res.cryptkeyres_u.deskey);
+ if (debugging) {
+ if (res.status == KEY_SUCCESS) {
+ (void) fprintf(stderr, "%08x%08x\n",
+ res.cryptkeyres_u.deskey.key.high,
+ res.cryptkeyres_u.deskey.key.low);
+ } else {
+ (void) fprintf(stderr, "%s\n", strstatus(res.status));
+ }
+ (void) fflush(stderr);
+ }
+ return (&res);
+}
+
+cryptkeyres *
+key_decrypt_pk_2_svc_prog(uid, arg)
+ uid_t uid;
+ cryptkeyarg2 *arg;
+{
+ static cryptkeyres res;
+
+ if (debugging) {
+ (void) fprintf(stderr, "decrypt(%ld, %s, %08x%08x) = ", uid,
+ arg->remotename, arg->deskey.key.high,
+ arg->deskey.key.low);
+ }
+ res.cryptkeyres_u.deskey = arg->deskey;
+ res.status = pk_decrypt(uid, arg->remotename, &(arg->remotekey),
+ &res.cryptkeyres_u.deskey);
+ if (debugging) {
+ if (res.status == KEY_SUCCESS) {
+ (void) fprintf(stderr, "%08x%08x\n",
+ res.cryptkeyres_u.deskey.key.high,
+ res.cryptkeyres_u.deskey.key.low);
+ } else {
+ (void) fprintf(stderr, "%s\n", strstatus(res.status));
+ }
+ (void) fflush(stderr);
+ }
+ return (&res);
+}
+
+keystatus *
+key_net_put_2_svc_prog(uid, arg)
+ uid_t uid;
+ key_netstarg *arg;
+{
+ static keystatus status;
+
+ if (debugging) {
+ (void) fprintf(stderr, "net_put(%s, %.*s, %.*s) = ",
+ arg->st_netname, (int)sizeof (arg->st_pub_key),
+ arg->st_pub_key, (int)sizeof (arg->st_priv_key),
+ arg->st_priv_key);
+ };
+
+ status = pk_netput(uid, arg);
+
+ if (debugging) {
+ (void) fprintf(stderr, "%s\n", strstatus(status));
+ (void) fflush(stderr);
+ }
+
+ return (&status);
+}
+
+key_netstres *
+key_net_get_2_svc_prog(uid, arg)
+ uid_t uid;
+ void *arg;
+{
+ static key_netstres keynetname;
+
+ if (debugging)
+ (void) fprintf(stderr, "net_get(%ld) = ", uid);
+
+ keynetname.status = pk_netget(uid, &keynetname.key_netstres_u.knet);
+ if (debugging) {
+ if (keynetname.status == KEY_SUCCESS) {
+ fprintf(stderr, "<%s, %.*s, %.*s>\n",
+ keynetname.key_netstres_u.knet.st_netname,
+ (int)sizeof (keynetname.key_netstres_u.knet.st_pub_key),
+ keynetname.key_netstres_u.knet.st_pub_key,
+ (int)sizeof (keynetname.key_netstres_u.knet.st_priv_key),
+ keynetname.key_netstres_u.knet.st_priv_key);
+ } else {
+ (void) fprintf(stderr, "NOT FOUND\n");
+ }
+ (void) fflush(stderr);
+ }
+
+ return (&keynetname);
+
+}
+
+cryptkeyres *
+key_get_conv_2_svc_prog(uid, arg)
+ uid_t uid;
+ keybuf arg;
+{
+ static cryptkeyres res;
+
+ if (debugging)
+ (void) fprintf(stderr, "get_conv(%ld, %.*s) = ", uid,
+ (int)sizeof (arg), arg);
+
+
+ res.status = pk_get_conv_key(uid, arg, &res);
+
+ if (debugging) {
+ if (res.status == KEY_SUCCESS) {
+ (void) fprintf(stderr, "%08x%08x\n",
+ res.cryptkeyres_u.deskey.key.high,
+ res.cryptkeyres_u.deskey.key.low);
+ } else {
+ (void) fprintf(stderr, "%s\n", strstatus(res.status));
+ }
+ (void) fflush(stderr);
+ }
+ return (&res);
+}
+
+
+cryptkeyres *
+key_encrypt_1_svc_prog(uid, arg)
+ uid_t uid;
+ cryptkeyarg *arg;
+{
+ static cryptkeyres res;
+
+ if (debugging) {
+ (void) fprintf(stderr, "encrypt(%ld, %s, %08x%08x) = ", uid,
+ arg->remotename, arg->deskey.key.high,
+ arg->deskey.key.low);
+ }
+ res.cryptkeyres_u.deskey = arg->deskey;
+ res.status = pk_encrypt(uid, arg->remotename, NULL,
+ &res.cryptkeyres_u.deskey);
+ if (debugging) {
+ if (res.status == KEY_SUCCESS) {
+ (void) fprintf(stderr, "%08x%08x\n",
+ res.cryptkeyres_u.deskey.key.high,
+ res.cryptkeyres_u.deskey.key.low);
+ } else {
+ (void) fprintf(stderr, "%s\n", strstatus(res.status));
+ }
+ (void) fflush(stderr);
+ }
+ return (&res);
+}
+
+cryptkeyres *
+key_decrypt_1_svc_prog(uid, arg)
+ uid_t uid;
+ cryptkeyarg *arg;
+{
+ static cryptkeyres res;
+
+ if (debugging) {
+ (void) fprintf(stderr, "decrypt(%ld, %s, %08x%08x) = ", uid,
+ arg->remotename, arg->deskey.key.high,
+ arg->deskey.key.low);
+ }
+ res.cryptkeyres_u.deskey = arg->deskey;
+ res.status = pk_decrypt(uid, arg->remotename, NULL,
+ &res.cryptkeyres_u.deskey);
+ if (debugging) {
+ if (res.status == KEY_SUCCESS) {
+ (void) fprintf(stderr, "%08x%08x\n",
+ res.cryptkeyres_u.deskey.key.high,
+ res.cryptkeyres_u.deskey.key.low);
+ } else {
+ (void) fprintf(stderr, "%s\n", strstatus(res.status));
+ }
+ (void) fflush(stderr);
+ }
+ return (&res);
+}
+
+/* ARGSUSED */
+des_block *
+key_gen_1_svc_prog(v, s)
+ void *v;
+ struct svc_req *s;
+{
+ struct timeval time;
+ static des_block keygen;
+ static des_block key;
+
+ (void) gettimeofday(&time, (struct timezone *) NULL);
+ keygen.key.high += (time.tv_sec ^ time.tv_usec);
+ keygen.key.low += (time.tv_sec ^ time.tv_usec);
+ ecb_crypt((char *)&masterkey, (char *)&keygen, sizeof (keygen),
+ DES_ENCRYPT | DES_HW);
+ key = keygen;
+ des_setparity((char *)&key);
+ if (debugging) {
+ (void) fprintf(stderr, "gen() = %08x%08x\n", key.key.high,
+ key.key.low);
+ (void) fflush(stderr);
+ }
+ return (&key);
+}
+
+getcredres *
+key_getcred_1_svc_prog(uid, name)
+ uid_t uid;
+ netnamestr *name;
+{
+ static getcredres res;
+ static u_int gids[NGROUPS];
+ struct unixcred *cred;
+
+ cred = &res.getcredres_u.cred;
+ cred->gids.gids_val = gids;
+ if (!netname2user(*name, (uid_t *) &cred->uid, (gid_t *) &cred->gid,
+ (int *)&cred->gids.gids_len, (gid_t *)gids)) {
+ res.status = KEY_UNKNOWN;
+ } else {
+ res.status = KEY_SUCCESS;
+ }
+ if (debugging) {
+ (void) fprintf(stderr, "getcred(%s) = ", *name);
+ if (res.status == KEY_SUCCESS) {
+ (void) fprintf(stderr, "uid=%d, gid=%d, grouplen=%d\n",
+ cred->uid, cred->gid, cred->gids.gids_len);
+ } else {
+ (void) fprintf(stderr, "%s\n", strstatus(res.status));
+ }
+ (void) fflush(stderr);
+ }
+ return (&res);
+}
+
+/*
+ * RPC boilerplate
+ */
+static void
+keyprogram(rqstp, transp)
+ struct svc_req *rqstp;
+ SVCXPRT *transp;
+{
+ union {
+ keybuf key_set_1_arg;
+ cryptkeyarg key_encrypt_1_arg;
+ cryptkeyarg key_decrypt_1_arg;
+ netnamestr key_getcred_1_arg;
+ cryptkeyarg key_encrypt_2_arg;
+ cryptkeyarg key_decrypt_2_arg;
+ netnamestr key_getcred_2_arg;
+ cryptkeyarg2 key_encrypt_pk_2_arg;
+ cryptkeyarg2 key_decrypt_pk_2_arg;
+ key_netstarg key_net_put_2_arg;
+ netobj key_get_conv_2_arg;
+ } argument;
+ char *result;
+ xdrproc_t xdr_argument, xdr_result;
+ char *(*local) ();
+ uid_t uid = -1;
+ int check_auth;
+
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ svc_sendreply(transp, (xdrproc_t)xdr_void, NULL);
+ return;
+
+ case KEY_SET:
+ xdr_argument = (xdrproc_t)xdr_keybuf;
+ xdr_result = (xdrproc_t)xdr_int;
+ local = (char *(*)()) key_set_1_svc_prog;
+ check_auth = 1;
+ break;
+
+ case KEY_ENCRYPT:
+ xdr_argument = (xdrproc_t)xdr_cryptkeyarg;
+ xdr_result = (xdrproc_t)xdr_cryptkeyres;
+ local = (char *(*)()) key_encrypt_1_svc_prog;
+ check_auth = 1;
+ break;
+
+ case KEY_DECRYPT:
+ xdr_argument = (xdrproc_t)xdr_cryptkeyarg;
+ xdr_result = (xdrproc_t)xdr_cryptkeyres;
+ local = (char *(*)()) key_decrypt_1_svc_prog;
+ check_auth = 1;
+ break;
+
+ case KEY_GEN:
+ xdr_argument = (xdrproc_t)xdr_void;
+ xdr_result = (xdrproc_t)xdr_des_block;
+ local = (char *(*)()) key_gen_1_svc_prog;
+ check_auth = 0;
+ break;
+
+ case KEY_GETCRED:
+ xdr_argument = (xdrproc_t)xdr_netnamestr;
+ xdr_result = (xdrproc_t)xdr_getcredres;
+ local = (char *(*)()) key_getcred_1_svc_prog;
+ check_auth = 0;
+ break;
+
+ case KEY_ENCRYPT_PK:
+ xdr_argument = (xdrproc_t)xdr_cryptkeyarg2;
+ xdr_result = (xdrproc_t)xdr_cryptkeyres;
+ local = (char *(*)()) key_encrypt_pk_2_svc_prog;
+ check_auth = 1;
+ break;
+
+ case KEY_DECRYPT_PK:
+ xdr_argument = (xdrproc_t)xdr_cryptkeyarg2;
+ xdr_result = (xdrproc_t)xdr_cryptkeyres;
+ local = (char *(*)()) key_decrypt_pk_2_svc_prog;
+ check_auth = 1;
+ break;
+
+
+ case KEY_NET_PUT:
+ xdr_argument = (xdrproc_t)xdr_key_netstarg;
+ xdr_result = (xdrproc_t)xdr_keystatus;
+ local = (char *(*)()) key_net_put_2_svc_prog;
+ check_auth = 1;
+ break;
+
+ case KEY_NET_GET:
+ xdr_argument = (xdrproc_t) xdr_void;
+ xdr_result = (xdrproc_t)xdr_key_netstres;
+ local = (char *(*)()) key_net_get_2_svc_prog;
+ check_auth = 1;
+ break;
+
+ case KEY_GET_CONV:
+ xdr_argument = (xdrproc_t) xdr_keybuf;
+ xdr_result = (xdrproc_t)xdr_cryptkeyres;
+ local = (char *(*)()) key_get_conv_2_svc_prog;
+ check_auth = 1;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+ if (check_auth) {
+ if (root_auth(transp, rqstp) == 0) {
+ if (debugging) {
+ (void) fprintf(stderr,
+ "not local privileged process\n");
+ }
+ svcerr_weakauth(transp);
+ return;
+ }
+ if (rqstp->rq_cred.oa_flavor != AUTH_SYS) {
+ if (debugging) {
+ (void) fprintf(stderr,
+ "not unix authentication\n");
+ }
+ svcerr_weakauth(transp);
+ return;
+ }
+ uid = ((struct authsys_parms *)rqstp->rq_clntcred)->aup_uid;
+ }
+
+ memset(&argument, 0, sizeof (argument));
+ if (!svc_getargs(transp, xdr_argument, &argument)) {
+ svcerr_decode(transp);
+ return;
+ }
+ result = (*local) (uid, &argument);
+ if (!svc_sendreply(transp, xdr_result, result)) {
+ if (debugging)
+ (void) fprintf(stderr, "unable to reply\n");
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp, xdr_argument, &argument)) {
+ if (debugging)
+ (void) fprintf(stderr,
+ "unable to free arguments\n");
+ exit(1);
+ }
+ return;
+}
+
+static int
+root_auth(trans, rqstp)
+ SVCXPRT *trans;
+ struct svc_req *rqstp;
+{
+ uid_t uid;
+ struct sockaddr *remote;
+
+ remote = svc_getrpccaller(trans)->buf;
+ if (remote->sa_family != AF_UNIX) {
+ if (debugging)
+ fprintf(stderr, "client didn't use AF_UNIX\n");
+ return (0);
+ }
+
+ if (__rpc_get_local_uid(trans, &uid) < 0) {
+ if (debugging)
+ fprintf(stderr, "__rpc_get_local_uid failed\n");
+ return (0);
+ }
+
+ if (debugging)
+ fprintf(stderr, "local_uid %ld\n", uid);
+ if (uid == 0)
+ return (1);
+ if (rqstp->rq_cred.oa_flavor == AUTH_SYS) {
+ if (((uid_t) ((struct authunix_parms *)
+ rqstp->rq_clntcred)->aup_uid)
+ == uid) {
+ return (1);
+ } else {
+ if (debugging)
+ fprintf(stderr,
+ "local_uid %ld mismatches auth %ld\n", uid,
+((uid_t) ((struct authunix_parms *)rqstp->rq_clntcred)->aup_uid));
+ return (0);
+ }
+ } else {
+ if (debugging)
+ fprintf(stderr, "Not auth sys\n");
+ return (0);
+ }
+}
+
+static void
+usage()
+{
+ (void) fprintf(stderr,
+ "usage: keyserv [-n] [-D] [-d] [-v] [-p path]\n");
+ (void) fprintf(stderr, "-d disables the use of default keys\n");
+ exit(1);
+}
diff --git a/usr.sbin/keyserv/keyserv.h b/usr.sbin/keyserv/keyserv.h
new file mode 100644
index 0000000..07e4ab6
--- /dev/null
+++ b/usr.sbin/keyserv/keyserv.h
@@ -0,0 +1,17 @@
+/*
+ * $FreeBSD$
+ */
+extern void setmodulus(char *modx);
+
+extern keystatus pk_setkey( uid_t, keybuf );
+extern keystatus pk_encrypt( uid_t, char *, netobj *, des_block * );
+extern keystatus pk_decrypt( uid_t, char *, netobj *, des_block * );
+extern keystatus pk_netput( uid_t, key_netstarg * );
+extern keystatus pk_netget( uid_t, key_netstarg * );
+extern keystatus pk_get_conv_key( uid_t, keybuf, cryptkeyres * );
+extern void pk_nodefaultkeys( void );
+
+extern void crypt_prog_1( struct svc_req *, register SVCXPRT * );
+extern void load_des( int, char * );
+
+extern int (*_my_crypt)( char *, int, struct desparams * );
diff --git a/usr.sbin/keyserv/setkey.c b/usr.sbin/keyserv/setkey.c
new file mode 100644
index 0000000..a11d04d
--- /dev/null
+++ b/usr.sbin/keyserv/setkey.c
@@ -0,0 +1,550 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)setkey.c 1.11 94/04/25 SMI";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc.
+ */
+
+/*
+ * Do the real work of the keyserver.
+ * Store secret keys. Compute common keys,
+ * and use them to decrypt and encrypt DES keys.
+ * Cache the common keys, so the expensive computation is avoided.
+ */
+#include <mp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <rpc/rpc.h>
+#include <rpc/key_prot.h>
+#include <rpc/des_crypt.h>
+#include <rpc/des.h>
+#include <sys/errno.h>
+#include "keyserv.h"
+
+static MINT *MODULUS;
+static char *fetchsecretkey( uid_t );
+static void writecache( char *, char *, des_block * );
+static int readcache( char *, char *, des_block * );
+static void extractdeskey( MINT *, des_block * );
+static int storesecretkey( uid_t, keybuf );
+static keystatus pk_crypt( uid_t, char *, netobj *, des_block *, int);
+static int nodefaultkeys = 0;
+
+
+/*
+ * prohibit the nobody key on this machine k (the -d flag)
+ */
+void
+pk_nodefaultkeys()
+{
+ nodefaultkeys = 1;
+}
+
+/*
+ * Set the modulus for all our Diffie-Hellman operations
+ */
+void
+setmodulus(modx)
+ char *modx;
+{
+ MODULUS = mp_xtom(modx);
+}
+
+/*
+ * Set the secretkey key for this uid
+ */
+keystatus
+pk_setkey(uid, skey)
+ uid_t uid;
+ keybuf skey;
+{
+ if (!storesecretkey(uid, skey)) {
+ return (KEY_SYSTEMERR);
+ }
+ return (KEY_SUCCESS);
+}
+
+/*
+ * Encrypt the key using the public key associated with remote_name and the
+ * secret key associated with uid.
+ */
+keystatus
+pk_encrypt(uid, remote_name, remote_key, key)
+ uid_t uid;
+ char *remote_name;
+ netobj *remote_key;
+ des_block *key;
+{
+ return (pk_crypt(uid, remote_name, remote_key, key, DES_ENCRYPT));
+}
+
+/*
+ * Decrypt the key using the public key associated with remote_name and the
+ * secret key associated with uid.
+ */
+keystatus
+pk_decrypt(uid, remote_name, remote_key, key)
+ uid_t uid;
+ char *remote_name;
+ netobj *remote_key;
+ des_block *key;
+{
+ return (pk_crypt(uid, remote_name, remote_key, key, DES_DECRYPT));
+}
+
+static int store_netname( uid_t, key_netstarg * );
+static int fetch_netname( uid_t, key_netstarg * );
+
+keystatus
+pk_netput(uid, netstore)
+ uid_t uid;
+ key_netstarg *netstore;
+{
+ if (!store_netname(uid, netstore)) {
+ return (KEY_SYSTEMERR);
+ }
+ return (KEY_SUCCESS);
+}
+
+keystatus
+pk_netget(uid, netstore)
+ uid_t uid;
+ key_netstarg *netstore;
+{
+ if (!fetch_netname(uid, netstore)) {
+ return (KEY_SYSTEMERR);
+ }
+ return (KEY_SUCCESS);
+}
+
+
+/*
+ * Do the work of pk_encrypt && pk_decrypt
+ */
+static keystatus
+pk_crypt(uid, remote_name, remote_key, key, mode)
+ uid_t uid;
+ char *remote_name;
+ netobj *remote_key;
+ des_block *key;
+ int mode;
+{
+ char *xsecret;
+ char xpublic[1024];
+ char xsecret_hold[1024];
+ des_block deskey;
+ int err;
+ MINT *public;
+ MINT *secret;
+ MINT *common;
+ char zero[8];
+
+ xsecret = fetchsecretkey(uid);
+ if (xsecret == NULL || xsecret[0] == 0) {
+ memset(zero, 0, sizeof (zero));
+ xsecret = xsecret_hold;
+ if (nodefaultkeys)
+ return (KEY_NOSECRET);
+
+ if (!getsecretkey("nobody", xsecret, zero) || xsecret[0] == 0) {
+ return (KEY_NOSECRET);
+ }
+ }
+ if (remote_key) {
+ memcpy(xpublic, remote_key->n_bytes, remote_key->n_len);
+ } else {
+ bzero((char *)&xpublic, sizeof(xpublic));
+ if (!getpublickey(remote_name, xpublic)) {
+ if (nodefaultkeys || !getpublickey("nobody", xpublic))
+ return (KEY_UNKNOWN);
+ }
+ }
+
+ if (!readcache(xpublic, xsecret, &deskey)) {
+ public = mp_xtom(xpublic);
+ secret = mp_xtom(xsecret);
+ /* Sanity Check on public and private keys */
+ if ((public == NULL) || (secret == NULL))
+ return (KEY_SYSTEMERR);
+
+ common = mp_itom(0);
+ mp_pow(public, secret, MODULUS, common);
+ extractdeskey(common, &deskey);
+ writecache(xpublic, xsecret, &deskey);
+ mp_mfree(secret);
+ mp_mfree(public);
+ mp_mfree(common);
+ }
+ err = ecb_crypt((char *)&deskey, (char *)key, sizeof (des_block),
+ DES_HW | mode);
+ if (DES_FAILED(err)) {
+ return (KEY_SYSTEMERR);
+ }
+ return (KEY_SUCCESS);
+}
+
+keystatus
+pk_get_conv_key(uid, xpublic, result)
+ uid_t uid;
+ keybuf xpublic;
+ cryptkeyres *result;
+{
+ char *xsecret;
+ char xsecret_hold[1024];
+ MINT *public;
+ MINT *secret;
+ MINT *common;
+ char zero[8];
+
+
+ xsecret = fetchsecretkey(uid);
+
+ if (xsecret == NULL || xsecret[0] == 0) {
+ memset(zero, 0, sizeof (zero));
+ xsecret = xsecret_hold;
+ if (nodefaultkeys)
+ return (KEY_NOSECRET);
+
+ if (!getsecretkey("nobody", xsecret, zero) ||
+ xsecret[0] == 0)
+ return (KEY_NOSECRET);
+ }
+
+ if (!readcache(xpublic, xsecret, &result->cryptkeyres_u.deskey)) {
+ public = mp_xtom(xpublic);
+ secret = mp_xtom(xsecret);
+ /* Sanity Check on public and private keys */
+ if ((public == NULL) || (secret == NULL))
+ return (KEY_SYSTEMERR);
+
+ common = mp_itom(0);
+ mp_pow(public, secret, MODULUS, common);
+ extractdeskey(common, &result->cryptkeyres_u.deskey);
+ writecache(xpublic, xsecret, &result->cryptkeyres_u.deskey);
+ mp_mfree(secret);
+ mp_mfree(public);
+ mp_mfree(common);
+ }
+
+ return (KEY_SUCCESS);
+}
+
+/*
+ * Choose middle 64 bits of the common key to use as our des key, possibly
+ * overwriting the lower order bits by setting parity.
+ */
+static void
+extractdeskey(ck, deskey)
+ MINT *ck;
+ des_block *deskey;
+{
+ MINT *a;
+ short r;
+ int i;
+ short base = (1 << 8);
+ char *k;
+
+ a = mp_itom(0);
+#ifdef SOLARIS_MP
+ _mp_move(ck, a);
+#else
+ mp_move(ck, a);
+#endif
+ for (i = 0; i < ((KEYSIZE - 64) / 2) / 8; i++) {
+ mp_sdiv(a, base, a, &r);
+ }
+ k = deskey->c;
+ for (i = 0; i < 8; i++) {
+ mp_sdiv(a, base, a, &r);
+ *k++ = r;
+ }
+ mp_mfree(a);
+ des_setparity((char *)deskey);
+}
+
+/*
+ * Key storage management
+ */
+
+#define KEY_ONLY 0
+#define KEY_NAME 1
+struct secretkey_netname_list {
+ uid_t uid;
+ key_netstarg keynetdata;
+ u_char sc_flag;
+ struct secretkey_netname_list *next;
+};
+
+
+
+static struct secretkey_netname_list *g_secretkey_netname;
+
+/*
+ * Store the keys and netname for this uid
+ */
+static int
+store_netname(uid, netstore)
+ uid_t uid;
+ key_netstarg *netstore;
+{
+ struct secretkey_netname_list *new;
+ struct secretkey_netname_list **l;
+
+ for (l = &g_secretkey_netname; *l != NULL && (*l)->uid != uid;
+ l = &(*l)->next) {
+ }
+ if (*l == NULL) {
+ new = (struct secretkey_netname_list *)malloc(sizeof (*new));
+ if (new == NULL) {
+ return (0);
+ }
+ new->uid = uid;
+ new->next = NULL;
+ *l = new;
+ } else {
+ new = *l;
+ if (new->keynetdata.st_netname)
+ (void) free (new->keynetdata.st_netname);
+ }
+ memcpy(new->keynetdata.st_priv_key, netstore->st_priv_key,
+ HEXKEYBYTES);
+ memcpy(new->keynetdata.st_pub_key, netstore->st_pub_key, HEXKEYBYTES);
+
+ if (netstore->st_netname)
+ new->keynetdata.st_netname = strdup(netstore->st_netname);
+ else
+ new->keynetdata.st_netname = (char *)NULL;
+ new->sc_flag = KEY_NAME;
+ return (1);
+
+}
+
+/*
+ * Fetch the keys and netname for this uid
+ */
+
+static int
+fetch_netname(uid, key_netst)
+ uid_t uid;
+ struct key_netstarg *key_netst;
+{
+ struct secretkey_netname_list *l;
+
+ for (l = g_secretkey_netname; l != NULL; l = l->next) {
+ if ((l->uid == uid) && (l->sc_flag == KEY_NAME)){
+
+ memcpy(key_netst->st_priv_key,
+ l->keynetdata.st_priv_key, HEXKEYBYTES);
+
+ memcpy(key_netst->st_pub_key,
+ l->keynetdata.st_pub_key, HEXKEYBYTES);
+
+ if (l->keynetdata.st_netname)
+ key_netst->st_netname =
+ strdup(l->keynetdata.st_netname);
+ else
+ key_netst->st_netname = NULL;
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+static char *
+fetchsecretkey(uid)
+ uid_t uid;
+{
+ struct secretkey_netname_list *l;
+
+ for (l = g_secretkey_netname; l != NULL; l = l->next) {
+ if (l->uid == uid) {
+ return (l->keynetdata.st_priv_key);
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * Store the secretkey for this uid
+ */
+static int
+storesecretkey(uid, key)
+ uid_t uid;
+ keybuf key;
+{
+ struct secretkey_netname_list *new;
+ struct secretkey_netname_list **l;
+
+ for (l = &g_secretkey_netname; *l != NULL && (*l)->uid != uid;
+ l = &(*l)->next) {
+ }
+ if (*l == NULL) {
+ new = (struct secretkey_netname_list *) malloc(sizeof (*new));
+ if (new == NULL) {
+ return (0);
+ }
+ new->uid = uid;
+ new->sc_flag = KEY_ONLY;
+ memset(new->keynetdata.st_pub_key, 0, HEXKEYBYTES);
+ new->keynetdata.st_netname = NULL;
+ new->next = NULL;
+ *l = new;
+ } else {
+ new = *l;
+ }
+
+ memcpy(new->keynetdata.st_priv_key, key,
+ HEXKEYBYTES);
+ return (1);
+}
+
+static int
+hexdigit(val)
+ int val;
+{
+ return ("0123456789abcdef"[val]);
+}
+
+void
+bin2hex(bin, hex, size)
+ unsigned char *bin;
+ unsigned char *hex;
+ int size;
+{
+ int i;
+
+ for (i = 0; i < size; i++) {
+ *hex++ = hexdigit(*bin >> 4);
+ *hex++ = hexdigit(*bin++ & 0xf);
+ }
+}
+
+static int
+hexval(dig)
+ char dig;
+{
+ if ('0' <= dig && dig <= '9') {
+ return (dig - '0');
+ } else if ('a' <= dig && dig <= 'f') {
+ return (dig - 'a' + 10);
+ } else if ('A' <= dig && dig <= 'F') {
+ return (dig - 'A' + 10);
+ } else {
+ return (-1);
+ }
+}
+
+void
+hex2bin(hex, bin, size)
+ unsigned char *hex;
+ unsigned char *bin;
+ int size;
+{
+ int i;
+
+ for (i = 0; i < size; i++) {
+ *bin = hexval(*hex++) << 4;
+ *bin++ |= hexval(*hex++);
+ }
+}
+
+/*
+ * Exponential caching management
+ */
+struct cachekey_list {
+ keybuf secret;
+ keybuf public;
+ des_block deskey;
+ struct cachekey_list *next;
+};
+static struct cachekey_list *g_cachedkeys;
+
+/*
+ * cache result of expensive multiple precision exponential operation
+ */
+static void
+writecache(pub, sec, deskey)
+ char *pub;
+ char *sec;
+ des_block *deskey;
+{
+ struct cachekey_list *new;
+
+ new = (struct cachekey_list *) malloc(sizeof (struct cachekey_list));
+ if (new == NULL) {
+ return;
+ }
+ memcpy(new->public, pub, sizeof (keybuf));
+ memcpy(new->secret, sec, sizeof (keybuf));
+ new->deskey = *deskey;
+ new->next = g_cachedkeys;
+ g_cachedkeys = new;
+}
+
+/*
+ * Try to find the common key in the cache
+ */
+static int
+readcache(pub, sec, deskey)
+ char *pub;
+ char *sec;
+ des_block *deskey;
+{
+ struct cachekey_list *found;
+ register struct cachekey_list **l;
+
+#define cachehit(pub, sec, list) \
+ (memcmp(pub, (list)->public, sizeof (keybuf)) == 0 && \
+ memcmp(sec, (list)->secret, sizeof (keybuf)) == 0)
+
+ for (l = &g_cachedkeys; (*l) != NULL && !cachehit(pub, sec, *l);
+ l = &(*l)->next)
+ ;
+ if ((*l) == NULL) {
+ return (0);
+ }
+ found = *l;
+ (*l) = (*l)->next;
+ found->next = g_cachedkeys;
+ g_cachedkeys = found;
+ *deskey = found->deskey;
+ return (1);
+}
diff --git a/usr.sbin/kgmon/Makefile b/usr.sbin/kgmon/Makefile
new file mode 100644
index 0000000..bf19401
--- /dev/null
+++ b/usr.sbin/kgmon/Makefile
@@ -0,0 +1,17 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= kgmon
+MAN= kgmon.8
+WARNS?= 2
+
+# This program may safely be run setuid-root to allow non-root
+# users to start, stop, and reset profiling buffers.
+#
+#BINOWN=root
+#BINMODE=4555
+
+DPADD= ${LIBKVM}
+LDADD= -lkvm
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/kgmon/kgmon.8 b/usr.sbin/kgmon/kgmon.8
new file mode 100644
index 0000000..b783d6f
--- /dev/null
+++ b/usr.sbin/kgmon/kgmon.8
@@ -0,0 +1,129 @@
+.\" 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.
+.\"
+.\" @(#)kgmon.8 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\"
+.Dd June 6, 1993
+.Dt KGMON 8
+.Os
+.Sh NAME
+.Nm kgmon
+.Nd generate a dump of the operating system's profile buffers
+.Sh SYNOPSIS
+.Nm
+.Op Fl Bbhpr
+.Op Fl M Ar core
+.Op Fl N Ar system
+.Sh DESCRIPTION
+The
+.Nm
+utility is used when profiling the operating system.
+When no arguments are supplied,
+.Nm
+indicates the state of operating system profiling as running,
+off, or not configured.
+(see
+.Xr config 8 )
+If the
+.Fl p
+flag is specified,
+.Nm
+extracts profile data from the operating system and produces a
+.Pa gmon.out
+file suitable for later analysis by
+.Xr gprof 1 .
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl B
+Resume the collection of high resolution profile data.
+.It Fl b
+Resume the collection of low resolution profile data.
+.It Fl h
+Stop the collection of profile data.
+.It Fl p
+Dump the contents of the profile buffers into a
+.Pa gmon.out
+file.
+.It Fl r
+Reset all the profile buffers.
+If the
+.Fl p
+flag is also specified, the
+.Pa gmon.out
+file is generated before the buffers are reset.
+.It Fl M
+Extract values associated with the name list from the specified core
+instead of the default
+.Pa /dev/kmem .
+.It Fl N
+Extract the name list from the specified system instead of the
+default
+.Pa /boot/kernel/kernel .
+.El
+.Pp
+If neither
+.Fl B
+nor
+.Fl b
+nor
+.Fl h
+is specified, the state of profiling collection remains unchanged.
+For example, if the
+.Fl p
+flag is specified and profile data is being collected,
+profiling will be momentarily suspended,
+the operating system profile buffers will be dumped,
+and profiling will be immediately resumed.
+.Pp
+The profile buffers should be reset when the resolution
+of the profile data is changed.
+.Sh FILES
+.Bl -tag -width /boot/kernel/kernel -compact
+.It Pa /boot/kernel/kernel
+the default system
+.It Pa /dev/kmem
+the default memory
+.El
+.Sh DIAGNOSTICS
+Users with only read permission on
+.Pa /dev/kmem
+cannot change the state
+of profiling collection.
+They can get a
+.Pa gmon.out
+file with the warning that the data may be
+inconsistent if profiling is in progress.
+.Sh SEE ALSO
+.Xr gprof 1 ,
+.Xr config 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.2 .
diff --git a/usr.sbin/kgmon/kgmon.c b/usr.sbin/kgmon/kgmon.c
new file mode 100644
index 0000000..bdb2da2
--- /dev/null
+++ b/usr.sbin/kgmon/kgmon.c
@@ -0,0 +1,534 @@
+/*
+ * 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[] = "@(#)kgmon.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/sysctl.h>
+#include <sys/gmon.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <kvm.h>
+#include <limits.h>
+#include <nlist.h>
+#include <paths.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+struct nlist nl[] = {
+#define N_GMONPARAM 0
+ { "__gmonparam" },
+#define N_PROFHZ 1
+ { "_profhz" },
+ { NULL },
+};
+
+struct kvmvars {
+ kvm_t *kd;
+ struct gmonparam gpm;
+};
+
+int Bflag, bflag, hflag, kflag, rflag, pflag;
+int debug = 0;
+int getprof(struct kvmvars *);
+int getprofhz(struct kvmvars *);
+void kern_readonly(int);
+int openfiles(char *, char *, struct kvmvars *);
+void setprof(struct kvmvars *kvp, int state);
+void dumpstate(struct kvmvars *kvp);
+void reset(struct kvmvars *kvp);
+static void usage(void);
+
+int
+main(int argc, char **argv)
+{
+ int ch, mode, disp, accessmode;
+ struct kvmvars kvmvars;
+ char *system, *kmemf;
+
+ seteuid(getuid());
+ kmemf = NULL;
+ system = NULL;
+ while ((ch = getopt(argc, argv, "M:N:Bbhpr")) != -1) {
+ switch((char)ch) {
+
+ case 'M':
+ kmemf = optarg;
+ kflag = 1;
+ break;
+
+ case 'N':
+ system = optarg;
+ break;
+
+ case 'B':
+ Bflag = 1;
+ break;
+
+ case 'b':
+ bflag = 1;
+ break;
+
+ case 'h':
+ hflag = 1;
+ break;
+
+ case 'p':
+ pflag = 1;
+ break;
+
+ case 'r':
+ rflag = 1;
+ break;
+
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+#define BACKWARD_COMPATIBILITY
+#ifdef BACKWARD_COMPATIBILITY
+ if (*argv) {
+ system = *argv;
+ if (*++argv) {
+ kmemf = *argv;
+ ++kflag;
+ }
+ }
+#endif
+ if (system == NULL)
+ system = (char *)getbootfile();
+ accessmode = openfiles(system, kmemf, &kvmvars);
+ mode = getprof(&kvmvars);
+ if (hflag)
+ disp = GMON_PROF_OFF;
+ else if (Bflag)
+ disp = GMON_PROF_HIRES;
+ else if (bflag)
+ disp = GMON_PROF_ON;
+ else
+ disp = mode;
+ if (pflag)
+ dumpstate(&kvmvars);
+ if (rflag)
+ reset(&kvmvars);
+ if (accessmode == O_RDWR)
+ setprof(&kvmvars, disp);
+ (void)fprintf(stdout, "kgmon: kernel profiling is %s.\n",
+ disp == GMON_PROF_OFF ? "off" :
+ disp == GMON_PROF_HIRES ? "running (high resolution)" :
+ disp == GMON_PROF_ON ? "running" :
+ disp == GMON_PROF_BUSY ? "busy" :
+ disp == GMON_PROF_ERROR ? "off (error)" :
+ "in an unknown state");
+ return (0);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: kgmon [-Bbhrp] [-M core] [-N system]\n");
+ exit(1);
+}
+
+/*
+ * Check that profiling is enabled and open any necessary files.
+ */
+int
+openfiles(system, kmemf, kvp)
+ char *system;
+ char *kmemf;
+ struct kvmvars *kvp;
+{
+ size_t size;
+ int mib[3], state, openmode;
+ char errbuf[_POSIX2_LINE_MAX];
+
+ if (!kflag) {
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROF;
+ mib[2] = GPROF_STATE;
+ size = sizeof state;
+ if (sysctl(mib, 3, &state, &size, NULL, 0) < 0)
+ errx(20, "profiling not defined in kernel");
+ if (!(Bflag || bflag || hflag || rflag ||
+ (pflag &&
+ (state == GMON_PROF_HIRES || state == GMON_PROF_ON))))
+ return (O_RDONLY);
+ (void)seteuid(0);
+ if (sysctl(mib, 3, NULL, NULL, &state, size) >= 0)
+ return (O_RDWR);
+ (void)seteuid(getuid());
+ kern_readonly(state);
+ return (O_RDONLY);
+ }
+ openmode = (Bflag || bflag || hflag || pflag || rflag)
+ ? O_RDWR : O_RDONLY;
+ kvp->kd = kvm_openfiles(system, kmemf, NULL, openmode, errbuf);
+ if (kvp->kd == NULL) {
+ if (openmode == O_RDWR) {
+ openmode = O_RDONLY;
+ kvp->kd = kvm_openfiles(system, kmemf, NULL, O_RDONLY,
+ errbuf);
+ }
+ if (kvp->kd == NULL)
+ errx(2, "kvm_openfiles: %s", errbuf);
+ kern_readonly(GMON_PROF_ON);
+ }
+ if (kvm_nlist(kvp->kd, nl) < 0)
+ errx(3, "%s: no namelist", system);
+ if (!nl[N_GMONPARAM].n_value)
+ errx(20, "profiling not defined in kernel");
+ return (openmode);
+}
+
+/*
+ * Suppress options that require a writable kernel.
+ */
+void
+kern_readonly(mode)
+ int mode;
+{
+
+ (void)fprintf(stderr, "kgmon: kernel read-only: ");
+ if (pflag && (mode == GMON_PROF_HIRES || mode == GMON_PROF_ON))
+ (void)fprintf(stderr, "data may be inconsistent\n");
+ if (rflag)
+ (void)fprintf(stderr, "-r supressed\n");
+ if (Bflag)
+ (void)fprintf(stderr, "-B supressed\n");
+ if (bflag)
+ (void)fprintf(stderr, "-b supressed\n");
+ if (hflag)
+ (void)fprintf(stderr, "-h supressed\n");
+ rflag = Bflag = bflag = hflag = 0;
+}
+
+/*
+ * Get the state of kernel profiling.
+ */
+int
+getprof(kvp)
+ struct kvmvars *kvp;
+{
+ size_t size;
+ int mib[3];
+
+ if (kflag) {
+ size = kvm_read(kvp->kd, nl[N_GMONPARAM].n_value, &kvp->gpm,
+ sizeof kvp->gpm);
+ } else {
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROF;
+ mib[2] = GPROF_GMONPARAM;
+ size = sizeof kvp->gpm;
+ if (sysctl(mib, 3, &kvp->gpm, &size, NULL, 0) < 0)
+ size = 0;
+ }
+
+ /*
+ * Accept certain undersized "structs" from old kernels. We need
+ * everything up to hashfraction, and want profrate and
+ * histcounter_type. Assume that the kernel doesn't put garbage
+ * in any padding that is returned instead of profrate and
+ * histcounter_type. This is a bad assumption for dead kernels,
+ * since kvm_read() will normally return garbage for bytes beyond
+ * the end of the actual kernel struct, if any.
+ */
+ if (size < offsetof(struct gmonparam, hashfraction) +
+ sizeof(kvp->gpm.hashfraction) || size > sizeof(kvp->gpm))
+ errx(4, "cannot get gmonparam: %s",
+ kflag ? kvm_geterr(kvp->kd) : strerror(errno));
+ bzero((char *)&kvp->gpm + size, sizeof(kvp->gpm) - size);
+ if (kvp->gpm.profrate == 0)
+ kvp->gpm.profrate = getprofhz(kvp);
+#ifdef __i386__
+ if (kvp->gpm.histcounter_type == 0) {
+ /*
+ * This fixup only works for not-so-old i386 kernels. The
+ * magic 16 is the kernel FUNCTION_ALIGNMENT. 64-bit
+ * counters are signed; smaller counters are unsigned.
+ */
+ kvp->gpm.histcounter_type = 16 /
+ (kvp->gpm.textsize / kvp->gpm.kcountsize) * CHAR_BIT;
+ if (kvp->gpm.histcounter_type == 64)
+ kvp->gpm.histcounter_type = -64;
+ }
+#endif
+
+ return (kvp->gpm.state);
+}
+
+/*
+ * Enable or disable kernel profiling according to the state variable.
+ */
+void
+setprof(kvp, state)
+ struct kvmvars *kvp;
+ int state;
+{
+ struct gmonparam *p = (struct gmonparam *)nl[N_GMONPARAM].n_value;
+ size_t sz;
+ int mib[3], oldstate;
+
+ sz = sizeof(state);
+ if (!kflag) {
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROF;
+ mib[2] = GPROF_STATE;
+ if (sysctl(mib, 3, &oldstate, &sz, NULL, 0) < 0)
+ goto bad;
+ if (oldstate == state)
+ return;
+ (void)seteuid(0);
+ if (sysctl(mib, 3, NULL, NULL, &state, sz) >= 0) {
+ (void)seteuid(getuid());
+ return;
+ }
+ (void)seteuid(getuid());
+ } else if (kvm_write(kvp->kd, (u_long)&p->state, (void *)&state, sz)
+ == sz)
+ return;
+bad:
+ warnx("warning: cannot turn profiling %s",
+ state == GMON_PROF_OFF ? "off" : "on");
+}
+
+/*
+ * Build the gmon.out file.
+ */
+void
+dumpstate(kvp)
+ struct kvmvars *kvp;
+{
+ register FILE *fp;
+ struct rawarc rawarc;
+ struct tostruct *tos;
+ u_long frompc;
+ u_short *froms, *tickbuf;
+ size_t i;
+ int mib[3];
+ struct gmonhdr h;
+ int fromindex, endfrom, toindex;
+
+ setprof(kvp, GMON_PROF_OFF);
+ fp = fopen("gmon.out", "w");
+ if (fp == 0) {
+ warn("gmon.out");
+ return;
+ }
+
+ /*
+ * Build the gmon header and write it to a file.
+ */
+ bzero(&h, sizeof(h));
+ h.lpc = kvp->gpm.lowpc;
+ h.hpc = kvp->gpm.highpc;
+ h.ncnt = kvp->gpm.kcountsize + sizeof(h);
+ h.version = GMONVERSION;
+ h.profrate = kvp->gpm.profrate;
+ h.histcounter_type = kvp->gpm.histcounter_type;
+ fwrite((char *)&h, sizeof(h), 1, fp);
+
+ /*
+ * Write out the tick buffer.
+ */
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROF;
+ if ((tickbuf = (u_short *)malloc(kvp->gpm.kcountsize)) == NULL)
+ errx(5, "cannot allocate kcount space");
+ if (kflag) {
+ i = kvm_read(kvp->kd, (u_long)kvp->gpm.kcount, (void *)tickbuf,
+ kvp->gpm.kcountsize);
+ } else {
+ mib[2] = GPROF_COUNT;
+ i = kvp->gpm.kcountsize;
+ if (sysctl(mib, 3, tickbuf, &i, NULL, 0) < 0)
+ i = 0;
+ }
+ if (i != kvp->gpm.kcountsize)
+ errx(6, "read ticks: read %lu, got %ld: %s",
+ kvp->gpm.kcountsize, (long)i,
+ kflag ? kvm_geterr(kvp->kd) : strerror(errno));
+ if ((fwrite(tickbuf, kvp->gpm.kcountsize, 1, fp)) != 1)
+ err(7, "writing tocks to gmon.out");
+ free(tickbuf);
+
+ /*
+ * Write out the arc info.
+ */
+ if ((froms = (u_short *)malloc(kvp->gpm.fromssize)) == NULL)
+ errx(8, "cannot allocate froms space");
+ if (kflag) {
+ i = kvm_read(kvp->kd, (u_long)kvp->gpm.froms, (void *)froms,
+ kvp->gpm.fromssize);
+ } else {
+ mib[2] = GPROF_FROMS;
+ i = kvp->gpm.fromssize;
+ if (sysctl(mib, 3, froms, &i, NULL, 0) < 0)
+ i = 0;
+ }
+ if (i != kvp->gpm.fromssize)
+ errx(9, "read froms: read %lu, got %ld: %s",
+ kvp->gpm.fromssize, (long)i,
+ kflag ? kvm_geterr(kvp->kd) : strerror(errno));
+ if ((tos = (struct tostruct *)malloc(kvp->gpm.tossize)) == NULL)
+ errx(10, "cannot allocate tos space");
+ if (kflag) {
+ i = kvm_read(kvp->kd, (u_long)kvp->gpm.tos, (void *)tos,
+ kvp->gpm.tossize);
+ } else {
+ mib[2] = GPROF_TOS;
+ i = kvp->gpm.tossize;
+ if (sysctl(mib, 3, tos, &i, NULL, 0) < 0)
+ i = 0;
+ }
+ if (i != kvp->gpm.tossize)
+ errx(11, "read tos: read %lu, got %ld: %s",
+ kvp->gpm.tossize, (long)i,
+ kflag ? kvm_geterr(kvp->kd) : strerror(errno));
+ if (debug)
+ warnx("lowpc 0x%lx, textsize 0x%lx",
+ (unsigned long)kvp->gpm.lowpc, kvp->gpm.textsize);
+ endfrom = kvp->gpm.fromssize / sizeof(*froms);
+ for (fromindex = 0; fromindex < endfrom; ++fromindex) {
+ if (froms[fromindex] == 0)
+ continue;
+ frompc = (u_long)kvp->gpm.lowpc +
+ (fromindex * kvp->gpm.hashfraction * sizeof(*froms));
+ for (toindex = froms[fromindex]; toindex != 0;
+ toindex = tos[toindex].link) {
+ if (debug)
+ warnx("[mcleanup] frompc 0x%lx selfpc 0x%lx "
+ "count %ld", frompc, tos[toindex].selfpc,
+ tos[toindex].count);
+ rawarc.raw_frompc = frompc;
+ rawarc.raw_selfpc = (u_long)tos[toindex].selfpc;
+ rawarc.raw_count = tos[toindex].count;
+ fwrite((char *)&rawarc, sizeof(rawarc), 1, fp);
+ }
+ }
+ fclose(fp);
+}
+
+/*
+ * Get the profiling rate.
+ */
+int
+getprofhz(kvp)
+ struct kvmvars *kvp;
+{
+ size_t size;
+ int mib[2], profrate;
+ struct clockinfo clockrate;
+
+ if (kflag) {
+ profrate = 1;
+ if (kvm_read(kvp->kd, nl[N_PROFHZ].n_value, &profrate,
+ sizeof profrate) != sizeof profrate)
+ warnx("get clockrate: %s", kvm_geterr(kvp->kd));
+ return (profrate);
+ }
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_CLOCKRATE;
+ clockrate.profhz = 1;
+ size = sizeof clockrate;
+ if (sysctl(mib, 2, &clockrate, &size, NULL, 0) < 0)
+ warn("get clockrate");
+ return (clockrate.profhz);
+}
+
+/*
+ * Reset the kernel profiling date structures.
+ */
+void
+reset(kvp)
+ struct kvmvars *kvp;
+{
+ char *zbuf;
+ u_long biggest;
+ int mib[3];
+
+ setprof(kvp, GMON_PROF_OFF);
+
+ biggest = kvp->gpm.kcountsize;
+ if (kvp->gpm.fromssize > biggest)
+ biggest = kvp->gpm.fromssize;
+ if (kvp->gpm.tossize > biggest)
+ biggest = kvp->gpm.tossize;
+ if ((zbuf = (char *)malloc(biggest)) == NULL)
+ errx(12, "cannot allocate zbuf space");
+ bzero(zbuf, biggest);
+ if (kflag) {
+ if (kvm_write(kvp->kd, (u_long)kvp->gpm.kcount, zbuf,
+ kvp->gpm.kcountsize) != kvp->gpm.kcountsize)
+ errx(13, "tickbuf zero: %s", kvm_geterr(kvp->kd));
+ if (kvm_write(kvp->kd, (u_long)kvp->gpm.froms, zbuf,
+ kvp->gpm.fromssize) != kvp->gpm.fromssize)
+ errx(14, "froms zero: %s", kvm_geterr(kvp->kd));
+ if (kvm_write(kvp->kd, (u_long)kvp->gpm.tos, zbuf,
+ kvp->gpm.tossize) != kvp->gpm.tossize)
+ errx(15, "tos zero: %s", kvm_geterr(kvp->kd));
+ return;
+ }
+ (void)seteuid(0);
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROF;
+ mib[2] = GPROF_COUNT;
+ if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.kcountsize) < 0)
+ err(13, "tickbuf zero");
+ mib[2] = GPROF_FROMS;
+ if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.fromssize) < 0)
+ err(14, "froms zero");
+ mib[2] = GPROF_TOS;
+ if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.tossize) < 0)
+ err(15, "tos zero");
+ (void)seteuid(getuid());
+ free(zbuf);
+}
diff --git a/usr.sbin/kgzip/Makefile b/usr.sbin/kgzip/Makefile
new file mode 100644
index 0000000..013c65f
--- /dev/null
+++ b/usr.sbin/kgzip/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= kgzip
+MAN= kgzip.8
+SRCS= kgzip.c aouthdr.c elfhdr.c kgzcmp.c kgzld.c xio.c
+
+WARNS?= 3
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/kgzip/aouthdr.c b/usr.sbin/kgzip/aouthdr.c
new file mode 100644
index 0000000..27541e6
--- /dev/null
+++ b/usr.sbin/kgzip/aouthdr.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2000 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$
+ */
+
+#include <stddef.h>
+#include "aouthdr.h"
+
+#define KGZ_FIX_NSIZE 0 /* Run-time fixup */
+
+const struct kgz_aouthdr0 aouthdr0 = {
+ /* a.out header */
+ {
+ MID_I386 << 020 | OMAGIC, /* a_midmag */
+ 0, /* a_text */
+ sizeof(struct kgz_hdr) + KGZ_FIX_NSIZE, /* a_data */
+ 0, /* a_bss */
+ sizeof(struct nlist) * KGZ__STNUM, /* a_syms */
+ 0, /* a_entry */
+ 0, /* a_trsize */
+ 0 /* a_drsize */
+ }
+};
+
+const struct kgz_aouthdr1 aouthdr1 = {
+ /* Symbol table */
+ {
+ {
+ {
+ (char *)offsetof(struct kgz__strtab,
+ kgz) /* n_un */
+ },
+ N_DATA | N_EXT, /* n_type */
+ AUX_OBJECT, /* n_other */
+ 0, /* n_desc */
+ 0 /* n_value */
+ },
+ {
+ {
+ (char *)offsetof(struct kgz__strtab,
+ kgz_ndata) /* n_un */
+ },
+ N_DATA | N_EXT, /* n_type */
+ AUX_OBJECT, /* n_other */
+ 0, /* n_desc */
+ sizeof(struct kgz_hdr) /* n_value */
+ }
+ },
+ /* String table */
+ {
+ sizeof(struct kgz__strtab), /* length */
+ KGZ__STR_KGZ, /* kgz */
+ KGZ__STR_KGZ_NDATA /* kgz_ndata */
+ }
+};
diff --git a/usr.sbin/kgzip/aouthdr.h b/usr.sbin/kgzip/aouthdr.h
new file mode 100644
index 0000000..63c2dff
--- /dev/null
+++ b/usr.sbin/kgzip/aouthdr.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2000 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$
+ */
+
+#include <a.out.h>
+#include "kgz.h"
+
+/* Relocatable header: part 0 */
+struct kgz_aouthdr0 {
+ struct exec a;
+};
+
+/* Symbol table entries */
+#define KGZ__STNUM 2
+
+/* Symbol table strings */
+#define KGZ__STR_KGZ "_kgz"
+#define KGZ__STR_KGZ_NDATA "_kgz_ndata"
+
+/* String table */
+struct kgz__strtab {
+ unsigned long length;
+ char kgz[sizeof(KGZ__STR_KGZ)];
+ char kgz_ndata[sizeof(KGZ__STR_KGZ_NDATA)];
+};
+
+/* Relocatable header: part 1 */
+struct kgz_aouthdr1 {
+ struct nlist st[KGZ__STNUM];
+ struct kgz__strtab strtab;
+};
+
+extern const struct kgz_aouthdr0 aouthdr0;
+extern const struct kgz_aouthdr1 aouthdr1;
diff --git a/usr.sbin/kgzip/elfhdr.c b/usr.sbin/kgzip/elfhdr.c
new file mode 100644
index 0000000..12b1d5f
--- /dev/null
+++ b/usr.sbin/kgzip/elfhdr.c
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 1999 Global Technology Associates, 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$
+ */
+
+#include <sys/endian.h>
+#include <stddef.h>
+#include "elfhdr.h"
+
+#define KGZ_FIX_NSIZE 0 /* Run-time fixup */
+
+/*
+ * Relocatable header template.
+ */
+const struct kgz_elfhdr elfhdr = {
+ /* ELF header */
+ {
+ {
+ ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3, /* e_ident */
+ ELFCLASS32, ELFDATA2LSB, EV_CURRENT, 0,
+ 'F', 'r', 'e', 'e', 'B', 'S', 'D', 0
+ },
+ htole16(ET_EXEC), /* e_type */
+ htole16(EM_386), /* e_machine */
+ htole32(EV_CURRENT), /* e_version */
+ 0, /* e_entry */
+ 0, /* e_phoff */
+ htole32(offsetof(struct kgz_elfhdr, sh)), /* e_shoff */
+ 0, /* e_flags */
+ htole16(sizeof(Elf32_Ehdr)), /* e_ehsize */
+ 0, /* e_phentsize */
+ 0, /* e_phnum */
+ htole16(sizeof(Elf32_Shdr)), /* e_shentsize */
+ htole16(KGZ_SHNUM), /* e_shnum */
+ htole16(KGZ_SH_SHSTRTAB) /* e_shstrndx */
+ },
+ /* Section header */
+ {
+ {
+ 0, /* sh_name */
+ htole32(SHT_NULL), /* sh_type */
+ 0, /* sh_flags */
+ 0, /* sh_addr */
+ 0, /* sh_offset */
+ 0, /* sh_size */
+ htole32(SHN_UNDEF), /* sh_link */
+ 0, /* sh_info */
+ 0, /* sh_addralign */
+ 0 /* sh_entsize */
+ },
+ {
+ htole32(offsetof(struct kgz_shstrtab, symtab)), /* sh_name */
+ htole32(SHT_SYMTAB), /* sh_type */
+ 0, /* sh_flags */
+ 0, /* sh_addr */
+ htole32(offsetof(struct kgz_elfhdr, st)), /* sh_offset */
+ htole32(sizeof(Elf32_Sym) * KGZ_STNUM), /* sh_size */
+ htole32(KGZ_SH_STRTAB), /* sh_link */
+ htole32(1), /* sh_info */
+ htole32(4), /* sh_addralign */
+ htole32(sizeof(Elf32_Sym)) /* sh_entsize */
+ },
+ {
+ htole32(offsetof(struct kgz_shstrtab, shstrtab)), /* sh_name */
+ htole32(SHT_STRTAB), /* sh_type */
+ 0, /* sh_flags */
+ 0, /* sh_addr */
+ htole32(offsetof(struct kgz_elfhdr, shstrtab)), /* sh_offset */
+ htole32(sizeof(struct kgz_shstrtab)), /* sh_size */
+ htole32(SHN_UNDEF), /* sh_link */
+ 0, /* sh_info */
+ htole32(1), /* sh_addralign */
+ 0 /* sh_entsize */
+ },
+ {
+ htole32(offsetof(struct kgz_shstrtab, strtab)), /* sh_name */
+ htole32(SHT_STRTAB), /* sh_type */
+ 0, /* sh_flags */
+ 0, /* sh_addr */
+ htole32(offsetof(struct kgz_elfhdr, strtab)), /* sh_offset */
+ htole32(sizeof(struct kgz_strtab)), /* sh_size */
+ htole32(SHN_UNDEF), /* sh_link */
+ 0, /* sh_info */
+ htole32(1), /* sh_addralign */
+ 0 /* sh_entsize */
+ },
+ {
+ htole32(offsetof(struct kgz_shstrtab, data)), /* sh_name */
+ htole32(SHT_PROGBITS), /* sh_type */
+ htole32(SHF_ALLOC | SHF_WRITE), /* sh_flags */
+ 0, /* sh_addr */
+ htole32(sizeof(struct kgz_elfhdr)), /* sh_offset */
+ htole32(sizeof(struct kgz_hdr) + KGZ_FIX_NSIZE), /* sh_size */
+ htole32(SHN_UNDEF), /* sh_link */
+ 0, /* sh_info */
+ htole32(4), /* sh_addralign */
+ 0 /* sh_entsize */
+ }
+ },
+ /* Symbol table */
+ {
+ {
+ 0, /* st_name */
+ 0, /* st_value */
+ 0, /* st_size */
+ 0, /* st_info */
+ 0, /* st_other */
+ htole16(SHN_UNDEF) /* st_shndx */
+ },
+ {
+ htole32(offsetof(struct kgz_strtab, kgz)), /* st_name */
+ 0, /* st_value */
+ htole32(sizeof(struct kgz_hdr)), /* st_size */
+ ELF32_ST_INFO(STB_GLOBAL, STT_OBJECT), /* st_info */
+ 0, /* st_other */
+ htole16(KGZ_SH_DATA) /* st_shndx */
+ },
+ {
+ htole32(offsetof(struct kgz_strtab, kgz_ndata)), /* st_name */
+ htole32(sizeof(struct kgz_hdr)), /* st_value */
+ htole32(KGZ_FIX_NSIZE), /* st_size */
+ ELF32_ST_INFO(STB_GLOBAL, STT_OBJECT), /* st_info */
+ 0, /* st_other */
+ htole16(KGZ_SH_DATA) /* st_shndx */
+ }
+ },
+ /* Section header string table */
+ {
+ KGZ_SHSTR_ZERO, /* zero */
+ KGZ_SHSTR_SYMTAB, /* symtab */
+ KGZ_SHSTR_SHSTRTAB, /* shstrtab */
+ KGZ_SHSTR_STRTAB, /* strtab */
+ KGZ_SHSTR_DATA /* data */
+ },
+ /* String table */
+ {
+ KGZ_STR_ZERO, /* zero */
+ KGZ_STR_KGZ, /* kgz */
+ KGZ_STR_KGZ_NDATA /* kgz_ndata */
+ }
+};
diff --git a/usr.sbin/kgzip/elfhdr.h b/usr.sbin/kgzip/elfhdr.h
new file mode 100644
index 0000000..57333b0
--- /dev/null
+++ b/usr.sbin/kgzip/elfhdr.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 1999 Global Technology Associates, 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$
+ */
+
+#include <sys/types.h>
+#define __ELF_WORD_SIZE 32
+#include <sys/elf32.h>
+#include <sys/elf_generic.h>
+#include "kgz.h"
+
+/* Section header indices */
+#define KGZ_SH_SYMTAB 1
+#define KGZ_SH_SHSTRTAB 2
+#define KGZ_SH_STRTAB 3
+#define KGZ_SH_DATA 4
+#define KGZ_SHNUM 5
+
+/* Section header strings */
+#define KGZ_SHSTR_ZERO ""
+#define KGZ_SHSTR_SYMTAB ".symtab"
+#define KGZ_SHSTR_SHSTRTAB ".shstrtab"
+#define KGZ_SHSTR_STRTAB ".strtab"
+#define KGZ_SHSTR_DATA ".data"
+
+/* Section header string table */
+struct kgz_shstrtab {
+ char zero[sizeof(KGZ_SHSTR_ZERO)];
+ char symtab[sizeof(KGZ_SHSTR_SYMTAB)];
+ char shstrtab[sizeof(KGZ_SHSTR_SHSTRTAB)];
+ char strtab[sizeof(KGZ_SHSTR_STRTAB)];
+ char data[sizeof(KGZ_SHSTR_DATA)];
+};
+
+/* Symbol table indices */
+#define KGZ_ST_KGZ 1
+#define KGZ_ST_KGZ_NDATA 2
+#define KGZ_STNUM 3
+
+/* Symbol table strings */
+#define KGZ_STR_ZERO ""
+#define KGZ_STR_KGZ "kgz"
+#define KGZ_STR_KGZ_NDATA "kgz_ndata"
+
+/* String table */
+struct kgz_strtab {
+ char zero[sizeof(KGZ_STR_ZERO)];
+ char kgz[sizeof(KGZ_STR_KGZ)];
+ char kgz_ndata[sizeof(KGZ_STR_KGZ_NDATA)];
+};
+
+/* Relocatable header format */
+struct kgz_elfhdr {
+ Elf32_Ehdr e;
+ Elf32_Shdr sh[KGZ_SHNUM];
+ Elf32_Sym st[KGZ_STNUM];
+ struct kgz_shstrtab shstrtab;
+ struct kgz_strtab strtab;
+};
+
+extern const struct kgz_elfhdr elfhdr;
diff --git a/usr.sbin/kgzip/kgz.h b/usr.sbin/kgzip/kgz.h
new file mode 100644
index 0000000..3cc8e13
--- /dev/null
+++ b/usr.sbin/kgzip/kgz.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 1999 Global Technology Associates, 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$
+ */
+
+#ifndef _KGZ_H_
+#define _KGZ_H_
+
+#include <sys/types.h>
+
+/*
+ * KGZ definitions: kgzip(8) output.
+ */
+
+/* Values for ident[]. */
+#define KGZ_ID0 'K'
+#define KGZ_ID1 'G'
+#define KGZ_ID2 'Z'
+#define KGZ_ID3 '\0'
+
+/* KGZ header. */
+struct kgz_hdr {
+ char ident[4]; /* identification */
+ uint32_t dload; /* decoded image load address */
+ uint32_t dsize; /* decoded image size */
+ uint32_t isize; /* image size in memory */
+ uint32_t entry; /* program entry point */
+ uint32_t nsize; /* encoded image size */
+};
+
+extern struct kgz_hdr kgz; /* header */
+extern uint8_t kgz_ndata[]; /* encoded image */
+
+#endif /* !_KGZ_H_ */
diff --git a/usr.sbin/kgzip/kgzcmp.c b/usr.sbin/kgzip/kgzcmp.c
new file mode 100644
index 0000000..0ea4f0e
--- /dev/null
+++ b/usr.sbin/kgzip/kgzcmp.c
@@ -0,0 +1,237 @@
+/*
+ * Copyright (c) 1999 Global Technology Associates, 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$
+ */
+
+#define _KERNEL
+#include <sys/param.h>
+#undef _KERNEL
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <a.out.h>
+
+#include "aouthdr.h"
+#include "elfhdr.h"
+#include "kgzip.h"
+
+static void mk_data(const struct iodesc *i, const struct iodesc *,
+ struct kgz_hdr *, size_t);
+static int ld_elf(const struct iodesc *, const struct iodesc *,
+ struct kgz_hdr *, const Elf32_Ehdr *);
+static int ld_aout(const struct iodesc *, const struct iodesc *,
+ struct kgz_hdr *, const struct exec *);
+
+/*
+ * Compress executable and output it in relocatable object format.
+ */
+void
+kgzcmp(struct kgz_hdr *kh, const char *f1, const char *f2)
+{
+ struct iodesc idi, ido;
+ struct kgz_hdr khle;
+
+ if ((idi.fd = open(idi.fname = f1, O_RDONLY)) == -1)
+ err(1, "%s", idi.fname);
+ if ((ido.fd = open(ido.fname = f2, O_CREAT | O_TRUNC | O_WRONLY,
+ 0666)) == -1)
+ err(1, "%s", ido.fname);
+ kh->ident[0] = KGZ_ID0;
+ kh->ident[1] = KGZ_ID1;
+ kh->ident[2] = KGZ_ID2;
+ kh->ident[3] = KGZ_ID3;
+ mk_data(&idi, &ido, kh,
+ (format == F_AOUT ? sizeof(struct kgz_aouthdr0) :
+ sizeof(struct kgz_elfhdr)) +
+ sizeof(struct kgz_hdr));
+ kh->dload &= 0xffffff;
+ kh->entry &= 0xffffff;
+ if (format == F_AOUT) {
+ struct kgz_aouthdr0 ahdr0 = aouthdr0;
+ struct kgz_aouthdr1 ahdr1 = aouthdr1;
+ unsigned x = (sizeof(struct kgz_hdr) + kh->nsize) & (16 - 1);
+ if (x) {
+ x = 16 - x;
+ xzero(&ido, x);
+ }
+ xwrite(&ido, &ahdr1, sizeof(ahdr1));
+ ahdr0.a.a_data += kh->nsize + x;
+ xseek(&ido, 0);
+ xwrite(&ido, &ahdr0, sizeof(ahdr0));
+ } else {
+ struct kgz_elfhdr ehdr = elfhdr;
+ ehdr.st[KGZ_ST_KGZ_NDATA].st_size = htole32(kh->nsize);
+ ehdr.sh[KGZ_SH_DATA].sh_size =
+ htole32(le32toh(ehdr.sh[KGZ_SH_DATA].sh_size) + kh->nsize);
+ xseek(&ido, 0);
+ xwrite(&ido, &ehdr, sizeof(ehdr));
+ }
+ khle = *kh;
+ khle.dload = htole32(khle.dload);
+ khle.dsize = htole32(khle.dsize);
+ khle.isize = htole32(khle.isize);
+ khle.entry = htole32(khle.entry);
+ khle.nsize = htole32(khle.nsize);
+ xwrite(&ido, &khle, sizeof(khle));
+ xclose(&ido);
+ xclose(&idi);
+}
+
+/*
+ * Make encoded (compressed) data.
+ */
+static void
+mk_data(const struct iodesc * idi, const struct iodesc * ido,
+ struct kgz_hdr * kh, size_t off)
+{
+ union {
+ struct exec ex;
+ Elf32_Ehdr ee;
+ } hdr;
+ struct stat sb;
+ struct iodesc idp;
+ int fd[2];
+ pid_t pid;
+ size_t n;
+ int fmt, status, e;
+
+ n = xread(idi, &hdr, sizeof(hdr), 0);
+ fmt = 0;
+ if (n >= sizeof(hdr.ee) && IS_ELF(hdr.ee))
+ fmt = F_ELF;
+ else if (n >= sizeof(hdr.ex) && N_GETMAGIC(hdr.ex) == ZMAGIC)
+ fmt = F_AOUT;
+ if (!fmt)
+ errx(1, "%s: Format not supported", idi->fname);
+ xseek(ido, off);
+ if (pipe(fd))
+ err(1, NULL);
+ switch (pid = fork()) {
+ case -1:
+ err(1, NULL);
+ case 0:
+ close(fd[1]);
+ dup2(fd[0], STDIN_FILENO);
+ close(fd[0]);
+ close(idi->fd);
+ dup2(ido->fd, STDOUT_FILENO);
+ close(ido->fd);
+ execlp("gzip", "gzip", "-9n", (char *)NULL);
+ warn(NULL);
+ _exit(1);
+ default:
+ close(fd[0]);
+ idp.fname = "(pipe)";
+ idp.fd = fd[1];
+ e = fmt == F_ELF ? ld_elf(idi, &idp, kh, &hdr.ee) :
+ fmt == F_AOUT ? ld_aout(idi, &idp, kh, &hdr.ex) : -1;
+ close(fd[1]);
+ if ((pid = waitpid(pid, &status, 0)) == -1)
+ err(1, NULL);
+ if (WIFSIGNALED(status) || WEXITSTATUS(status))
+ exit(1);
+ }
+ if (e)
+ errx(1, "%s: Invalid format", idi->fname);
+ if (fstat(ido->fd, &sb))
+ err(1, "%s", ido->fname);
+ kh->nsize = sb.st_size - off;
+}
+
+/*
+ * "Load" an ELF-format executable.
+ */
+static int
+ld_elf(const struct iodesc * idi, const struct iodesc * ido,
+ struct kgz_hdr * kh, const Elf32_Ehdr * e)
+{
+ Elf32_Phdr p;
+ size_t load, addr, n;
+ unsigned x, i;
+
+ load = addr = n = 0;
+ for (x = i = 0; i < e->e_phnum; i++) {
+ if (xread(idi, &p, sizeof(p),
+ e->e_phoff + i * e->e_phentsize) != e->e_phentsize)
+ return -1;
+ if (p.p_type != PT_LOAD)
+ continue;
+ if (!x)
+ load = addr = p.p_vaddr;
+ else {
+ if (p.p_vaddr < addr)
+ return -1;
+ n = p.p_vaddr - addr;
+ if (n) {
+ xzero(ido, n);
+ addr += n;
+ }
+ }
+ if (p.p_memsz < p.p_filesz)
+ return -1;
+ n = p.p_memsz - p.p_filesz;
+ xcopy(idi, ido, p.p_filesz, p.p_offset);
+ addr += p.p_filesz;
+ x++;
+ }
+ if (!x)
+ return -1;
+ kh->dload = load;
+ kh->dsize = addr - load;
+ kh->isize = kh->dsize + n;
+ kh->entry = e->e_entry;
+ return 0;
+}
+
+/*
+ * "Load" an a.out-format executable.
+ */
+static int
+ld_aout(const struct iodesc * idi, const struct iodesc * ido,
+ struct kgz_hdr * kh, const struct exec * a)
+{
+ size_t load, addr;
+
+ load = addr = N_TXTADDR(*a);
+ xcopy(idi, ido, le32toh(a->a_text), N_TXTOFF(*a));
+ addr += le32toh(a->a_text);
+ if (N_DATADDR(*a) != addr)
+ return -1;
+ xcopy(idi, ido, le32toh(a->a_data), N_DATOFF(*a));
+ addr += le32toh(a->a_data);
+ kh->dload = load;
+ kh->dsize = addr - load;
+ kh->isize = kh->dsize + le32toh(a->a_bss);
+ kh->entry = le32toh(a->a_entry);
+ return 0;
+}
diff --git a/usr.sbin/kgzip/kgzip.8 b/usr.sbin/kgzip/kgzip.8
new file mode 100644
index 0000000..7f810d4
--- /dev/null
+++ b/usr.sbin/kgzip/kgzip.8
@@ -0,0 +1,143 @@
+.\" Copyright (c) 1999 Global Technology Associates, 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$
+.\"
+.Dd July 19, 1999
+.Dt KGZIP 8
+.Os
+.Sh NAME
+.Nm kgzip
+.Nd compress a kernel
+.Sh SYNOPSIS
+.Nm
+.Op Fl cv
+.Op Fl f Ar format
+.Op Fl l Ar loader
+.Op Fl o Ar output
+.Ar file
+.Sh DESCRIPTION
+The
+.Nm
+utility compresses a kernel or some other bootable binary.
+Operation
+is in two phases as follows:
+.Bl -enum
+.It
+A load image of the executable file is built which omits all but
+the
+.Sq text
+and
+.Sq data
+segments.
+This image is compressed using
+.Xr gzip 1
+and output as data in relocatable object format.
+.It
+The object file is linked with a special self-hosting loader, producing
+an executable suitable for booting with either the second- or
+third-level bootstraps.
+.El
+.Pp
+Supported object formats are 32-bit ELF and a.out ZMAGIC.
+.Pp
+If the
+.Ar file
+operand has a
+.Sq .o
+suffix, input is assumed to be for the link phase, and the first phase
+is omitted.
+.Pp
+The options are:
+.Bl -tag -width Fl
+.It Fl c
+Omit the link phase.
+.It Fl v
+Display object file information.
+.It Fl f Ar format
+Use
+.Ar format
+as the output format, where
+.Ar format
+is
+.Sq aout
+or
+.Sq elf .
+The default format is ELF.
+.It Fl l Ar loader
+Link
+.Ar loader
+as the loader.
+.It Fl o Ar output
+Name the output file
+.Ar output .
+The default is to use the input name with the suffix
+.Sq .o
+(for relocatables) or
+.Sq .kgz
+(for executables).
+.El
+.Sh NOTES
+Global variables equivalent to the following are defined in the output:
+.Bd -literal
+struct kgz_hdr {
+ char ident[4]; /* identification: "KGZ" */
+ uint32_t dload; /* decoded image load address */
+ uint32_t dsize; /* decoded image size */
+ uint32_t isize; /* image size in memory */
+ uint32_t entry; /* entry point */
+ uint32_t nsize; /* encoded image size */
+} kgz;
+
+uint8_t kgz_ndata[]; /* encoded data */
+.Ed
+.Pp
+The encoded data is simply
+.Xr gzip 1
+output: a header (with no optional fields); compressed data; and 32-bit
+CRC and size values.
+.Sh FILES
+.Bl -tag -width /usr/lib/kgzldr.o -compact
+.It Pa /usr/lib/kgzldr.o
+The default loader
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr gzip 1 ,
+.Xr ld 1 ,
+.Xr a.out 5 ,
+.Xr elf 5 ,
+.Xr boot 8 ,
+.Xr loader 8
+.Sh AUTHORS
+.An Robert Nordier Aq rnordier@FreeBSD.org .
+.Sh BUGS
+As symbols are lost, the usefulness of this utility for compressing
+kernels is limited to situations where
+.Xr loader 8
+cannot be used; otherwise the preferred method of compressing a kernel
+is simply to
+.Xr gzip 1
+it.
diff --git a/usr.sbin/kgzip/kgzip.c b/usr.sbin/kgzip/kgzip.c
new file mode 100644
index 0000000..d6c48f2
--- /dev/null
+++ b/usr.sbin/kgzip/kgzip.c
@@ -0,0 +1,176 @@
+/*
+ * Copyright (c) 1999 Global Technology Associates, 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <err.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "kgzip.h"
+
+#define FN_SRC 0 /* Filename: source */
+#define FN_OBJ 1 /* Filename: relocatable */
+#define FN_KGZ 2 /* Filename: executable */
+#define FN_CNT 3 /* Number of filenames */
+
+#define SFX_OBJ ".o" /* Filename suffix: relocatable */
+#define SFX_KGZ ".kgz" /* Filename suffix: executable */
+#define SFX_MAX 5 /* Size of larger filename suffix */
+
+const char *loader = "/usr/lib/kgzldr.o"; /* Default loader */
+int format; /* Output format */
+
+char *tname; /* Name of temporary file */
+
+static void cleanup(void);
+static void mk_fn(int, const char *, const char *, char *[]);
+static void usage(void);
+
+/*
+ * Compress a kernel.
+ */
+int
+main(int argc, char *argv[])
+{
+ static char *fn[FN_CNT];
+ struct kgz_hdr kh;
+ const char *output;
+ char *tmpdir;
+ int cflag, vflag, c;
+
+ tmpdir = getenv("TMPDIR");
+ if (asprintf(&tname, "%s/kgzXXXXXXXXXX",
+ tmpdir == NULL ? _PATH_TMP : tmpdir) == -1)
+ errx(1, "Out of memory");
+ output = NULL;
+ cflag = vflag = 0;
+ while ((c = getopt(argc, argv, "cvf:l:o:")) != -1)
+ switch (c) {
+ case 'c':
+ cflag = 1;
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ case 'f':
+ if (!strcmp(optarg, "aout"))
+ format = F_AOUT;
+ else if (!strcmp(optarg, "elf"))
+ format = F_ELF;
+ else
+ errx(1, "%s: Unknown format", optarg);
+ break;
+ case 'l':
+ loader = optarg;
+ break;
+ case 'o':
+ output = optarg;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 1)
+ usage();
+ atexit(cleanup);
+ mk_fn(cflag, *argv, output, fn);
+ memset(&kh, 0, sizeof(kh));
+ if (fn[FN_SRC]) {
+ if (!format)
+ format = F_ELF;
+ kgzcmp(&kh, fn[FN_SRC], fn[FN_OBJ]);
+ }
+ if (!cflag)
+ kgzld(&kh, fn[FN_OBJ], fn[FN_KGZ]);
+ if (vflag)
+ printf("dload=%#x dsize=%#x isize=%#x entry=%#x nsize=%#x\n",
+ kh.dload, kh.dsize, kh.isize, kh.entry, kh.nsize);
+ return 0;
+}
+
+/*
+ * Clean up after processing.
+ */
+static void
+cleanup(void)
+{
+ if (tname)
+ unlink(tname);
+}
+
+/*
+ * Make the required filenames.
+ */
+static void
+mk_fn(int cflag, const char *f1, const char *f2, char *fn[])
+{
+ const char *p, *s;
+ size_t n;
+ int i, fd;
+
+ i = 0;
+ s = strrchr(f1, 0);
+ n = sizeof(SFX_OBJ) - 1;
+ if ((size_t)(s - f1) > n && !memcmp(s - n, SFX_OBJ, n)) {
+ s -= n;
+ i++;
+ }
+ fn[i++] = (char *)f1;
+ if (i == FN_OBJ && !cflag) {
+ if ((fd = mkstemp(tname)) == -1)
+ err(1, NULL);
+ close(fd);
+ fn[i++] = tname;
+ }
+ if (!(fn[i] = (char *)f2)) {
+ p = (p = strrchr(f1, '/')) ? p + 1 : f1;
+ n = (size_t)(s - p);
+ if (!(fn[i] = malloc(n + SFX_MAX)))
+ err(1, NULL);
+ memcpy(fn[i], p, n);
+ strcpy(fn[i] + n, i == FN_OBJ ? SFX_OBJ : SFX_KGZ);
+ }
+}
+
+/*
+ * Display usage information.
+ */
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: kgzip [-cv] [-f format] [-l loader] [-o output] file\n");
+ exit(1);
+}
diff --git a/usr.sbin/kgzip/kgzip.h b/usr.sbin/kgzip/kgzip.h
new file mode 100644
index 0000000..f04f2a4
--- /dev/null
+++ b/usr.sbin/kgzip/kgzip.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 1999 Global Technology Associates, 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$
+ */
+
+#include "kgz.h"
+
+#define F_AOUT 1 /* Format: a.out */
+#define F_ELF 2 /* Format: ELF32 */
+
+/* Used by I/O routines */
+struct iodesc {
+ const char *fname; /* File name */
+ int fd; /* File descriptor */
+};
+
+extern const char *loader; /* Default loader */
+extern int format; /* Output format */
+
+void kgzcmp(struct kgz_hdr *, const char *, const char *);
+void kgzld(struct kgz_hdr *, const char *, const char *);
+
+void xclose(const struct iodesc *);
+void xcopy(const struct iodesc *, const struct iodesc *, size_t, off_t);
+void xzero(const struct iodesc *, size_t);
+size_t xread(const struct iodesc *, void *, size_t, off_t);
+void xwrite(const struct iodesc *, const void *, size_t);
+void xseek(const struct iodesc *, off_t);
diff --git a/usr.sbin/kgzip/kgzld.c b/usr.sbin/kgzip/kgzld.c
new file mode 100644
index 0000000..11a20a5
--- /dev/null
+++ b/usr.sbin/kgzip/kgzld.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 1999 Global Technology Associates, 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$
+ */
+
+#include <sys/types.h>
+#include <sys/endian.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "aouthdr.h"
+#include "elfhdr.h"
+#include "kgzip.h"
+
+#define align(x, y) (((x) + (y) - 1) & ~((y) - 1))
+
+/*
+ * Link KGZ file and loader.
+ */
+void
+kgzld(struct kgz_hdr * kh, const char *f1, const char *f2)
+{
+ char addr[16];
+ struct iodesc idi;
+ pid_t pid;
+ size_t n;
+ int status;
+
+ if (strcmp(kh->ident, "KGZ")) {
+ if ((idi.fd = open(idi.fname = f1, O_RDONLY)) == -1)
+ err(1, "%s", idi.fname);
+ if (!format) {
+ union {
+ struct exec ex;
+ Elf32_Ehdr ee;
+ } hdr;
+ n = xread(&idi, &hdr, sizeof(hdr), 0);
+ if (n >= sizeof(hdr.ee) && IS_ELF(hdr.ee))
+ format = F_ELF;
+ else if (n >= sizeof(hdr.ex) &&
+ N_GETMAGIC(hdr.ex) == OMAGIC)
+ format = F_AOUT;
+ if (!format)
+ errx(1, "%s: Format not supported", idi.fname);
+ }
+ n = xread(&idi, kh, sizeof(*kh),
+ format == F_AOUT ? sizeof(struct kgz_aouthdr0)
+ : sizeof(struct kgz_elfhdr));
+ xclose(&idi);
+ if (n != sizeof(*kh) || strcmp(kh->ident, "KGZ"))
+ errx(1, "%s: Invalid format", idi.fname);
+ }
+ sprintf(addr, "%#x", align(kh->dload + kh->dsize, 0x1000));
+ switch (pid = fork()) {
+ case -1:
+ err(1, NULL);
+ case 0:
+ if (format == F_AOUT)
+ execlp("ld", "ld", "-aout", "-Z", "-T", addr, "-o", f2,
+ loader, f1, (char *)NULL);
+ else
+ execlp("ld", "ld", "-Ttext", addr, "-o", f2, loader, f1,
+ (char *)NULL);
+ warn(NULL);
+ _exit(1);
+ default:
+ if ((pid = waitpid(pid, &status, 0)) == -1)
+ err(1, NULL);
+ if (WIFSIGNALED(status) || WEXITSTATUS(status))
+ exit(1);
+ }
+}
diff --git a/usr.sbin/kgzip/xio.c b/usr.sbin/kgzip/xio.c
new file mode 100644
index 0000000..64481ff
--- /dev/null
+++ b/usr.sbin/kgzip/xio.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 1999 Global Technology Associates, 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$
+ */
+
+#include <err.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "kgzip.h"
+
+/*
+ * Close a file.
+ */
+void
+xclose(const struct iodesc *id)
+{
+ if (close(id->fd))
+ err(1, "%s", id->fname);
+}
+
+/*
+ * Copy bytes from one file to another.
+ */
+void
+xcopy(const struct iodesc * idi, const struct iodesc * ido,
+ size_t nbyte, off_t offset)
+{
+ char buf[8192];
+ size_t n;
+
+ while (nbyte) {
+ if ((n = sizeof(buf)) > nbyte)
+ n = nbyte;
+ if (xread(idi, buf, n, offset) != n)
+ errx(1, "%s: Short read", idi->fname);
+ xwrite(ido, buf, n);
+ nbyte -= n;
+ offset = -1;
+ }
+}
+
+/*
+ * Write binary zeroes to a file.
+ */
+void
+xzero(const struct iodesc * id, size_t nbyte)
+{
+ char buf[8192];
+ size_t n;
+
+ memset(buf, 0, sizeof(buf));
+ while (nbyte) {
+ if ((n = sizeof(buf)) > nbyte)
+ n = nbyte;
+ xwrite(id, buf, n);
+ nbyte -= n;
+ }
+}
+
+/*
+ * Read from a file.
+ */
+size_t
+xread(const struct iodesc * id, void *buf, size_t nbyte, off_t offset)
+{
+ ssize_t n;
+
+ if (offset != -1 && lseek(id->fd, offset, SEEK_SET) != offset)
+ err(1, "%s", id->fname);
+ if ((n = read(id->fd, buf, nbyte)) == -1)
+ err(1, "%s", id->fname);
+ return (size_t)n;
+}
+
+/*
+ * Write to a file.
+ */
+void
+xwrite(const struct iodesc * id, const void *buf, size_t nbyte)
+{
+ ssize_t n;
+
+ if ((n = write(id->fd, buf, nbyte)) == -1)
+ err(1, "%s", id->fname);
+ if ((size_t)n != nbyte)
+ errx(1, "%s: Short write", id->fname);
+}
+
+/*
+ * Reposition within a file.
+ */
+void
+xseek(const struct iodesc *id, off_t offset)
+{
+ if (lseek(id->fd, offset, SEEK_SET) != offset)
+ err(1, "%s", id->fname);
+}
diff --git a/usr.sbin/kldxref/Makefile b/usr.sbin/kldxref/Makefile
new file mode 100644
index 0000000..2f15505
--- /dev/null
+++ b/usr.sbin/kldxref/Makefile
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+PROG= kldxref
+MAN= kldxref.8
+SRCS= kldxref.c ef.c ef_obj.c
+
+WARNS?= 2
+CFLAGS+=-fno-strict-aliasing
+
+.if exists(ef_${MACHINE_ARCH}.c)
+SRCS+= ef_${MACHINE_ARCH}.c
+.else
+SRCS+= ef_nop.c
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/kldxref/ef.c b/usr.sbin/kldxref/ef.c
new file mode 100644
index 0000000..276771d
--- /dev/null
+++ b/usr.sbin/kldxref/ef.c
@@ -0,0 +1,648 @@
+/*
+ * Copyright (c) 2000, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Boris Popov.
+ * 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 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/linker.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <machine/elf.h>
+#define FREEBSD_ELF
+#include <link.h>
+
+#include <err.h>
+
+#include "ef.h"
+
+struct ef_file {
+ char* ef_name;
+ struct elf_file *ef_efile;
+ Elf_Phdr * ef_ph;
+ int ef_fd;
+ int ef_type;
+ Elf_Ehdr ef_hdr;
+ void* ef_fpage; /* First block of the file */
+ int ef_fplen; /* length of first block */
+ Elf_Dyn* ef_dyn; /* Symbol table etc. */
+ Elf_Hashelt ef_nbuckets;
+ Elf_Hashelt ef_nchains;
+ Elf_Hashelt* ef_buckets;
+ Elf_Hashelt* ef_chains;
+ Elf_Hashelt* ef_hashtab;
+ Elf_Off ef_stroff;
+ caddr_t ef_strtab;
+ int ef_strsz;
+ Elf_Off ef_symoff;
+ Elf_Sym* ef_symtab;
+ int ef_nsegs;
+ Elf_Phdr * ef_segs[2];
+ int ef_verbose;
+ Elf_Rel * ef_rel; /* relocation table */
+ int ef_relsz; /* number of entries */
+ Elf_Rela * ef_rela; /* relocation table */
+ int ef_relasz; /* number of entries */
+};
+
+static void ef_print_phdr(Elf_Phdr *);
+static u_long ef_get_offset(elf_file_t, Elf_Off);
+static int ef_parse_dynamic(elf_file_t);
+
+static int ef_get_type(elf_file_t ef);
+static int ef_close(elf_file_t ef);
+static int ef_read(elf_file_t ef, Elf_Off offset, size_t len, void* dest);
+static int ef_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr);
+static int ef_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest);
+static int ef_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len,
+ void *dest);
+static int ef_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len,
+ void **ptr);
+static int ef_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len,
+ void **ptr);
+static Elf_Addr ef_symaddr(elf_file_t ef, Elf_Size symidx);
+static int ef_lookup_set(elf_file_t ef, const char *name, long *startp,
+ long *stopp, long *countp);
+static int ef_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym);
+
+static struct elf_file_ops ef_file_ops = {
+ ef_get_type,
+ ef_close,
+ ef_read,
+ ef_read_entry,
+ ef_seg_read,
+ ef_seg_read_rel,
+ ef_seg_read_entry,
+ ef_seg_read_entry_rel,
+ ef_symaddr,
+ ef_lookup_set,
+ ef_lookup_symbol
+};
+
+static void
+ef_print_phdr(Elf_Phdr *phdr)
+{
+
+ if ((phdr->p_flags & PF_W) == 0) {
+ printf("text=0x%lx ", (long)phdr->p_filesz);
+ } else {
+ printf("data=0x%lx", (long)phdr->p_filesz);
+ if (phdr->p_filesz < phdr->p_memsz)
+ printf("+0x%lx", (long)(phdr->p_memsz - phdr->p_filesz));
+ printf(" ");
+ }
+}
+
+static u_long
+ef_get_offset(elf_file_t ef, Elf_Off off)
+{
+ Elf_Phdr *ph;
+ int i;
+
+ for (i = 0; i < ef->ef_nsegs; i++) {
+ ph = ef->ef_segs[i];
+ if (off >= ph->p_vaddr && off < ph->p_vaddr + ph->p_memsz) {
+ return ph->p_offset + (off - ph->p_vaddr);
+ }
+ }
+ return 0;
+}
+
+static int
+ef_get_type(elf_file_t ef)
+{
+
+ return (ef->ef_type);
+}
+
+/*
+ * next three functions copied from link_elf.c
+ */
+static unsigned long
+elf_hash(const char *name)
+{
+ const unsigned char *p = (const unsigned char *) name;
+ unsigned long h = 0;
+ unsigned long g;
+
+ while (*p != '\0') {
+ h = (h << 4) + *p++;
+ if ((g = h & 0xf0000000) != 0)
+ h ^= g >> 24;
+ h &= ~g;
+ }
+ return h;
+}
+
+static int
+ef_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym)
+{
+ unsigned long symnum;
+ Elf_Sym* symp;
+ char *strp;
+ unsigned long hash;
+
+ /* First, search hashed global symbols */
+ hash = elf_hash(name);
+ symnum = ef->ef_buckets[hash % ef->ef_nbuckets];
+
+ while (symnum != STN_UNDEF) {
+ if (symnum >= ef->ef_nchains) {
+ warnx("ef_lookup_symbol: file %s have corrupted symbol table\n",
+ ef->ef_name);
+ return ENOENT;
+ }
+
+ symp = ef->ef_symtab + symnum;
+ if (symp->st_name == 0) {
+ warnx("ef_lookup_symbol: file %s have corrupted symbol table\n",
+ ef->ef_name);
+ return ENOENT;
+ }
+
+ strp = ef->ef_strtab + symp->st_name;
+
+ if (strcmp(name, strp) == 0) {
+ if (symp->st_shndx != SHN_UNDEF ||
+ (symp->st_value != 0 &&
+ ELF_ST_TYPE(symp->st_info) == STT_FUNC)) {
+ *sym = symp;
+ return 0;
+ } else
+ return ENOENT;
+ }
+
+ symnum = ef->ef_chains[symnum];
+ }
+
+ return ENOENT;
+}
+
+static int
+ef_lookup_set(elf_file_t ef, const char *name, long *startp, long *stopp,
+ long *countp)
+{
+ Elf_Sym *sym;
+ char *setsym;
+ int error, len;
+
+ len = strlen(name) + sizeof("__start_set_"); /* sizeof includes \0 */
+ setsym = malloc(len);
+ if (setsym == NULL)
+ return (ENOMEM);
+
+ /* get address of first entry */
+ snprintf(setsym, len, "%s%s", "__start_set_", name);
+ error = ef_lookup_symbol(ef, setsym, &sym);
+ if (error)
+ goto out;
+ *startp = sym->st_value;
+
+ /* get address of last entry */
+ snprintf(setsym, len, "%s%s", "__stop_set_", name);
+ error = ef_lookup_symbol(ef, setsym, &sym);
+ if (error)
+ goto out;
+ *stopp = sym->st_value;
+
+ /* and the number of entries */
+ *countp = (*stopp - *startp) / sizeof(void *);
+
+out:
+ free(setsym);
+ return (error);
+}
+
+static Elf_Addr
+ef_symaddr(elf_file_t ef, Elf_Size symidx)
+{
+ const Elf_Sym *sym;
+
+ if (symidx >= ef->ef_nchains)
+ return (0);
+ sym = ef->ef_symtab + symidx;
+
+ if (ELF_ST_BIND(sym->st_info) == STB_LOCAL &&
+ sym->st_shndx != SHN_UNDEF && sym->st_value != 0)
+ return (sym->st_value);
+ return (0);
+}
+
+static int
+ef_parse_dynamic(elf_file_t ef)
+{
+ Elf_Dyn *dp;
+ Elf_Hashelt hashhdr[2];
+/* int plttype = DT_REL;*/
+ int error;
+ Elf_Off rel_off;
+ Elf_Off rela_off;
+ int rel_sz;
+ int rela_sz;
+ int rel_entry;
+ int rela_entry;
+
+ rel_off = rela_off = 0;
+ rel_sz = rela_sz = 0;
+ rel_entry = rela_entry = 0;
+ for (dp = ef->ef_dyn; dp->d_tag != DT_NULL; dp++) {
+ switch (dp->d_tag) {
+ case DT_HASH:
+ error = ef_read(ef, ef_get_offset(ef, dp->d_un.d_ptr),
+ sizeof(hashhdr), hashhdr);
+ if (error) {
+ warnx("can't read hash header (%lx)",
+ ef_get_offset(ef, dp->d_un.d_ptr));
+ return error;
+ }
+ ef->ef_nbuckets = hashhdr[0];
+ ef->ef_nchains = hashhdr[1];
+ error = ef_read_entry(ef, -1,
+ (hashhdr[0] + hashhdr[1]) * sizeof(Elf_Hashelt),
+ (void**)&ef->ef_hashtab);
+ if (error) {
+ warnx("can't read hash table");
+ return error;
+ }
+ ef->ef_buckets = ef->ef_hashtab;
+ ef->ef_chains = ef->ef_buckets + ef->ef_nbuckets;
+ break;
+ case DT_STRTAB:
+ ef->ef_stroff = dp->d_un.d_ptr;
+ break;
+ case DT_STRSZ:
+ ef->ef_strsz = dp->d_un.d_val;
+ break;
+ case DT_SYMTAB:
+ ef->ef_symoff = dp->d_un.d_ptr;
+ break;
+ case DT_SYMENT:
+ if (dp->d_un.d_val != sizeof(Elf_Sym))
+ return EFTYPE;
+ break;
+ case DT_REL:
+ if (rel_off != 0)
+ warnx("second DT_REL entry ignored");
+ rel_off = dp->d_un.d_ptr;
+ break;
+ case DT_RELSZ:
+ if (rel_sz != 0)
+ warnx("second DT_RELSZ entry ignored");
+ rel_sz = dp->d_un.d_val;
+ break;
+ case DT_RELENT:
+ if (rel_entry != 0)
+ warnx("second DT_RELENT entry ignored");
+ rel_entry = dp->d_un.d_val;
+ break;
+ case DT_RELA:
+ if (rela_off != 0)
+ warnx("second DT_RELA entry ignored");
+ rela_off = dp->d_un.d_ptr;
+ break;
+ case DT_RELASZ:
+ if (rela_sz != 0)
+ warnx("second DT_RELASZ entry ignored");
+ rela_sz = dp->d_un.d_val;
+ break;
+ case DT_RELAENT:
+ if (rela_entry != 0)
+ warnx("second DT_RELAENT entry ignored");
+ rela_entry = dp->d_un.d_val;
+ break;
+ }
+ }
+ if (ef->ef_symoff == 0) {
+ warnx("%s: no .dynsym section found\n", ef->ef_name);
+ return EFTYPE;
+ }
+ if (ef->ef_stroff == 0) {
+ warnx("%s: no .dynstr section found\n", ef->ef_name);
+ return EFTYPE;
+ }
+ if (ef_read_entry(ef, ef_get_offset(ef, ef->ef_symoff),
+ ef->ef_nchains * sizeof(Elf_Sym),
+ (void**)&ef->ef_symtab) != 0) {
+ if (ef->ef_verbose)
+ warnx("%s: can't load .dynsym section (0x%lx)",
+ ef->ef_name, (long)ef->ef_symoff);
+ return EIO;
+ }
+ if (ef_read_entry(ef, ef_get_offset(ef, ef->ef_stroff), ef->ef_strsz,
+ (void**)&ef->ef_strtab) != 0) {
+ warnx("can't load .dynstr section");
+ return EIO;
+ }
+ if (rel_off != 0) {
+ if (rel_entry == 0) {
+ warnx("%s: no DT_RELENT for DT_REL", ef->ef_name);
+ return (EFTYPE);
+ }
+ if (rel_entry != sizeof(Elf_Rel)) {
+ warnx("%s: inconsistent DT_RELENT value",
+ ef->ef_name);
+ return (EFTYPE);
+ }
+ if (rel_sz % rel_entry != 0) {
+ warnx("%s: inconsistent values for DT_RELSZ and "
+ "DT_RELENT", ef->ef_name);
+ return (EFTYPE);
+ }
+ if (ef_read_entry(ef, ef_get_offset(ef, rel_off), rel_sz,
+ (void **)&ef->ef_rel) != 0) {
+ warnx("%s: cannot load DT_REL section", ef->ef_name);
+ return (EIO);
+ }
+ ef->ef_relsz = rel_sz / rel_entry;
+ if (ef->ef_verbose)
+ warnx("%s: %d REL entries", ef->ef_name,
+ ef->ef_relsz);
+ }
+ if (rela_off != 0) {
+ if (rela_entry == 0) {
+ warnx("%s: no DT_RELAENT for DT_RELA", ef->ef_name);
+ return (EFTYPE);
+ }
+ if (rela_entry != sizeof(Elf_Rela)) {
+ warnx("%s: inconsistent DT_RELAENT value",
+ ef->ef_name);
+ return (EFTYPE);
+ }
+ if (rela_sz % rela_entry != 0) {
+ warnx("%s: inconsistent values for DT_RELASZ and "
+ "DT_RELAENT", ef->ef_name);
+ return (EFTYPE);
+ }
+ if (ef_read_entry(ef, ef_get_offset(ef, rela_off), rela_sz,
+ (void **)&ef->ef_rela) != 0) {
+ warnx("%s: cannot load DT_RELA section", ef->ef_name);
+ return (EIO);
+ }
+ ef->ef_relasz = rela_sz / rela_entry;
+ if (ef->ef_verbose)
+ warnx("%s: %d RELA entries", ef->ef_name,
+ ef->ef_relasz);
+ }
+ return 0;
+}
+
+static int
+ef_read(elf_file_t ef, Elf_Off offset, size_t len, void*dest)
+{
+ ssize_t r;
+
+ if (offset != (Elf_Off)-1) {
+ if (lseek(ef->ef_fd, offset, SEEK_SET) == -1)
+ return EIO;
+ }
+
+ r = read(ef->ef_fd, dest, len);
+ if (r != -1 && (size_t)r == len)
+ return 0;
+ else
+ return EIO;
+}
+
+static int
+ef_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void**ptr)
+{
+ int error;
+
+ *ptr = malloc(len);
+ if (*ptr == NULL)
+ return ENOMEM;
+ error = ef_read(ef, offset, len, *ptr);
+ if (error)
+ free(*ptr);
+ return error;
+}
+
+static int
+ef_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void*dest)
+{
+ u_long ofs = ef_get_offset(ef, offset);
+
+ if (ofs == 0) {
+ if (ef->ef_verbose)
+ warnx("ef_seg_read(%s): zero offset (%lx:%ld)",
+ ef->ef_name, (long)offset, ofs);
+ return EFAULT;
+ }
+ return ef_read(ef, ofs, len, dest);
+}
+
+static int
+ef_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, void*dest)
+{
+ u_long ofs = ef_get_offset(ef, offset);
+ const Elf_Rela *a;
+ const Elf_Rel *r;
+ int error;
+
+ if (ofs == 0) {
+ if (ef->ef_verbose)
+ warnx("ef_seg_read(%s): zero offset (%lx:%ld)",
+ ef->ef_name, (long)offset, ofs);
+ return EFAULT;
+ }
+ if ((error = ef_read(ef, ofs, len, dest)) != 0)
+ return (error);
+
+ for (r = ef->ef_rel; r < &ef->ef_rel[ef->ef_relsz]; r++) {
+ error = ef_reloc(ef->ef_efile, r, EF_RELOC_REL, 0, offset, len,
+ dest);
+ if (error != 0)
+ return (error);
+ }
+ for (a = ef->ef_rela; a < &ef->ef_rela[ef->ef_relasz]; a++) {
+ error = ef_reloc(ef->ef_efile, a, EF_RELOC_RELA, 0, offset, len,
+ dest);
+ if (error != 0)
+ return (error);
+ }
+ return (0);
+}
+
+static int
+ef_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void**ptr)
+{
+ int error;
+
+ *ptr = malloc(len);
+ if (*ptr == NULL)
+ return ENOMEM;
+ error = ef_seg_read(ef, offset, len, *ptr);
+ if (error)
+ free(*ptr);
+ return error;
+}
+
+static int
+ef_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len, void**ptr)
+{
+ int error;
+
+ *ptr = malloc(len);
+ if (*ptr == NULL)
+ return ENOMEM;
+ error = ef_seg_read_rel(ef, offset, len, *ptr);
+ if (error)
+ free(*ptr);
+ return error;
+}
+
+int
+ef_open(const char *filename, struct elf_file *efile, int verbose)
+{
+ elf_file_t ef;
+ Elf_Ehdr *hdr;
+ int fd;
+ int error;
+ int phlen, res;
+ int nsegs;
+ Elf_Phdr *phdr, *phdyn, *phphdr, *phlimit;
+
+ if (filename == NULL)
+ return EFTYPE;
+ if ((fd = open(filename, O_RDONLY)) == -1)
+ return errno;
+
+ ef = malloc(sizeof(*ef));
+ if (ef == NULL) {
+ close(fd);
+ return (ENOMEM);
+ }
+
+ efile->ef_ef = ef;
+ efile->ef_ops = &ef_file_ops;
+
+ bzero(ef, sizeof(*ef));
+ ef->ef_verbose = verbose;
+ ef->ef_fd = fd;
+ ef->ef_name = strdup(filename);
+ ef->ef_efile = efile;
+ hdr = (Elf_Ehdr *)&ef->ef_hdr;
+ do {
+ res = read(fd, hdr, sizeof(*hdr));
+ error = EFTYPE;
+ if (res != sizeof(*hdr))
+ break;
+ if (!IS_ELF(*hdr))
+ break;
+ if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS ||
+ hdr->e_ident[EI_DATA] != ELF_TARG_DATA ||
+ hdr->e_ident[EI_VERSION] != EV_CURRENT ||
+ hdr->e_version != EV_CURRENT ||
+ hdr->e_machine != ELF_TARG_MACH ||
+ hdr->e_phentsize != sizeof(Elf_Phdr))
+ break;
+ phlen = hdr->e_phnum * sizeof(Elf_Phdr);
+ if (ef_read_entry(ef, hdr->e_phoff, phlen,
+ (void**)&ef->ef_ph) != 0)
+ break;
+ phdr = ef->ef_ph;
+ phlimit = phdr + hdr->e_phnum;
+ nsegs = 0;
+ phdyn = NULL;
+ phphdr = NULL;
+ while (phdr < phlimit) {
+ if (verbose > 1)
+ ef_print_phdr(phdr);
+ switch (phdr->p_type) {
+ case PT_LOAD:
+ if (nsegs == 2) {
+ warnx("%s: too many sections",
+ filename);
+ break;
+ }
+ ef->ef_segs[nsegs++] = phdr;
+ break;
+ case PT_PHDR:
+ phphdr = phdr;
+ break;
+ case PT_DYNAMIC:
+ phdyn = phdr;
+ break;
+ }
+ phdr++;
+ }
+ if (verbose > 1)
+ printf("\n");
+ ef->ef_nsegs = nsegs;
+ if (phdyn == NULL) {
+ warnx("file isn't dynamically-linked");
+ break;
+ }
+ if (ef_read_entry(ef, phdyn->p_offset,
+ phdyn->p_filesz, (void**)&ef->ef_dyn) != 0) {
+ printf("ef_read_entry failed\n");
+ break;
+ }
+ error = ef_parse_dynamic(ef);
+ if (error)
+ break;
+ if (hdr->e_type == ET_DYN) {
+ ef->ef_type = EFT_KLD;
+/* pad = (u_int)dest & PAGE_MASK;
+ if (pad)
+ dest += PAGE_SIZE - pad;*/
+ error = 0;
+ } else if (hdr->e_type == ET_EXEC) {
+/* dest = hdr->e_entry;
+ if (dest == 0)
+ break;*/
+ ef->ef_type = EFT_KERNEL;
+ error = 0;
+ } else
+ break;
+ } while(0);
+ if (error)
+ ef_close(ef);
+ return error;
+}
+
+static int
+ef_close(elf_file_t ef)
+{
+ close(ef->ef_fd);
+/* if (ef->ef_fpage)
+ free(ef->ef_fpage);*/
+ if (ef->ef_name)
+ free(ef->ef_name);
+ ef->ef_efile->ef_ops = NULL;
+ ef->ef_efile->ef_ef = NULL;
+ free(ef);
+ return 0;
+}
diff --git a/usr.sbin/kldxref/ef.h b/usr.sbin/kldxref/ef.h
new file mode 100644
index 0000000..5bb1985
--- /dev/null
+++ b/usr.sbin/kldxref/ef.h
@@ -0,0 +1,69 @@
+/* $FreeBSD$ */
+
+#ifndef _EF_H_
+#define _EF_H_
+
+#define EFT_KLD 1
+#define EFT_KERNEL 2
+
+#define EF_RELOC_REL 1
+#define EF_RELOC_RELA 2
+
+#define EF_GET_TYPE(ef) \
+ (ef)->ef_ops->get_type((ef)->ef_ef)
+#define EF_CLOSE(ef) \
+ (ef)->ef_ops->close((ef)->ef_ef)
+#define EF_READ(ef, offset, len, dest) \
+ (ef)->ef_ops->read((ef)->ef_ef, offset, len, dest)
+#define EF_READ_ENTRY(ef, offset, len, ptr) \
+ (ef)->ef_ops->read_entry((ef)->ef_ef, offset, len, ptr)
+#define EF_SEG_READ(ef, offset, len, dest) \
+ (ef)->ef_ops->seg_read((ef)->ef_ef, offset, len, dest)
+#define EF_SEG_READ_REL(ef, offset, len, dest) \
+ (ef)->ef_ops->seg_read_rel((ef)->ef_ef, offset, len, dest)
+#define EF_SEG_READ_ENTRY(ef, offset, len, ptr) \
+ (ef)->ef_ops->seg_read_entry((ef)->kf_ef, offset, len, ptr)
+#define EF_SEG_READ_ENTRY_REL(ef, offset, len, ptr) \
+ (ef)->ef_ops->seg_read_entry_rel((ef)->ef_ef, offset, len, ptr)
+#define EF_SYMADDR(ef, symidx) \
+ (ef)->ef_ops->symaddr((ef)->ef_ef, symidx)
+#define EF_LOOKUP_SET(ef, name, startp, stopp, countp) \
+ (ef)->ef_ops->lookup_set((ef)->ef_ef, name, startp, stopp, countp)
+#define EF_LOOKUP_SYMBOL(ef, name, sym) \
+ (ef)->ef_ops->lookup_symbol((ef)->ef_ef, name, sym)
+
+/* XXX, should have a different name. */
+typedef struct ef_file *elf_file_t;
+
+struct elf_file_ops {
+ int (*get_type)(elf_file_t ef);
+ int (*close)(elf_file_t ef);
+ int (*read)(elf_file_t ef, Elf_Off offset, size_t len, void* dest);
+ int (*read_entry)(elf_file_t ef, Elf_Off offset, size_t len,
+ void **ptr);
+ int (*seg_read)(elf_file_t ef, Elf_Off offset, size_t len, void *dest);
+ int (*seg_read_rel)(elf_file_t ef, Elf_Off offset, size_t len,
+ void *dest);
+ int (*seg_read_entry)(elf_file_t ef, Elf_Off offset, size_t len,
+ void**ptr);
+ int (*seg_read_entry_rel)(elf_file_t ef, Elf_Off offset, size_t len,
+ void**ptr);
+ Elf_Addr (*symaddr)(elf_file_t ef, Elf_Size symidx);
+ int (*lookup_set)(elf_file_t ef, const char *name, long *startp,
+ long *stopp, long *countp);
+ int (*lookup_symbol)(elf_file_t ef, const char* name, Elf_Sym** sym);
+};
+
+struct elf_file {
+ elf_file_t ef_ef;
+ struct elf_file_ops *ef_ops;
+};
+
+__BEGIN_DECLS
+int ef_open(const char *filename, struct elf_file *ef, int verbose);
+int ef_obj_open(const char *filename, struct elf_file *ef, int verbose);
+int ef_reloc(struct elf_file *ef, const void *reldata, int reltype,
+ Elf_Off relbase, Elf_Off dataoff, size_t len, void *dest);
+__END_DECLS
+
+#endif /* _EF_H_*/
diff --git a/usr.sbin/kldxref/ef_amd64.c b/usr.sbin/kldxref/ef_amd64.c
new file mode 100644
index 0000000..5a1d192
--- /dev/null
+++ b/usr.sbin/kldxref/ef_amd64.c
@@ -0,0 +1,117 @@
+/*-
+ * Copyright (c) 2003 Jake Burkholder.
+ * Copyright 1996-1998 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED 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 <machine/elf.h>
+
+#include <err.h>
+#include <errno.h>
+#include <string.h>
+
+#include "ef.h"
+
+/*
+ * Apply relocations to the values we got from the file. `relbase' is the
+ * target relocation address of the section, and `dataoff' is the target
+ * relocation address of the data in `dest'.
+ */
+int
+ef_reloc(struct elf_file *ef, const void *reldata, int reltype, Elf_Off relbase,
+ Elf_Off dataoff, size_t len, void *dest)
+{
+ Elf64_Addr *where, val;
+ Elf32_Addr *where32, val32;
+ Elf_Addr addend, addr;
+ Elf_Size rtype, symidx;
+ const Elf_Rel *rel;
+ const Elf_Rela *rela;
+
+ switch (reltype) {
+ case EF_RELOC_REL:
+ rel = (const Elf_Rel *)reldata;
+ where = (Elf_Addr *)(dest + relbase + rel->r_offset - dataoff);
+ addend = 0;
+ rtype = ELF_R_TYPE(rel->r_info);
+ symidx = ELF_R_SYM(rel->r_info);
+ break;
+ case EF_RELOC_RELA:
+ rela = (const Elf_Rela *)reldata;
+ where = (Elf_Addr *)(dest + relbase + rela->r_offset - dataoff);
+ addend = rela->r_addend;
+ rtype = ELF_R_TYPE(rela->r_info);
+ symidx = ELF_R_SYM(rela->r_info);
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ if ((char *)where < (char *)dest || (char *)where >= (char *)dest + len)
+ return (0);
+
+ if (reltype == EF_RELOC_REL) {
+ /* Addend is 32 bit on 32 bit relocs */
+ switch (rtype) {
+ case R_X86_64_PC32:
+ case R_X86_64_32S:
+ addend = *(Elf32_Addr *)where;
+ break;
+ default:
+ addend = *where;
+ break;
+ }
+ }
+
+ switch (rtype) {
+ case R_X86_64_NONE: /* none */
+ break;
+ case R_X86_64_64: /* S + A */
+ addr = EF_SYMADDR(ef, symidx);
+ val = addr + addend;
+ *where = val;
+ break;
+ case R_X86_64_32S: /* S + A sign extend */
+ addr = EF_SYMADDR(ef, symidx);
+ val32 = (Elf32_Addr)(addr + addend);
+ where32 = (Elf32_Addr *)where;
+ *where32 = val32;
+ break;
+ case R_X86_64_GLOB_DAT: /* S */
+ addr = EF_SYMADDR(ef, symidx);
+ *where = addr;
+ break;
+ case R_X86_64_RELATIVE: /* B + A */
+ addr = (Elf_Addr)addend + relbase;
+ val = addr;
+ *where = val;
+ break;
+ default:
+ warnx("unhandled relocation type %d", (int)rtype);
+ }
+ return (0);
+}
diff --git a/usr.sbin/kldxref/ef_i386.c b/usr.sbin/kldxref/ef_i386.c
new file mode 100644
index 0000000..9b10c7d
--- /dev/null
+++ b/usr.sbin/kldxref/ef_i386.c
@@ -0,0 +1,97 @@
+/*-
+ * Copyright (c) 2003 Jake Burkholder.
+ * Copyright 1996-1998 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED 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 <machine/elf.h>
+
+#include <err.h>
+#include <errno.h>
+#include <string.h>
+
+#include "ef.h"
+
+/*
+ * Apply relocations to the values we got from the file. `relbase' is the
+ * target relocation address of the section, and `dataoff' is the target
+ * relocation address of the data in `dest'.
+ */
+int
+ef_reloc(struct elf_file *ef, const void *reldata, int reltype, Elf_Off relbase,
+ Elf_Off dataoff, size_t len, void *_dest)
+{
+ Elf_Addr *where, addr, addend;
+ Elf_Size rtype, symidx;
+ const Elf_Rel *rel;
+ const Elf_Rela *rela;
+ char *dest = _dest;
+
+ switch (reltype) {
+ case EF_RELOC_REL:
+ rel = (const Elf_Rel *)reldata;
+ where = (Elf_Addr *)(dest + relbase + rel->r_offset - dataoff);
+ addend = 0;
+ rtype = ELF_R_TYPE(rel->r_info);
+ symidx = ELF_R_SYM(rel->r_info);
+ break;
+ case EF_RELOC_RELA:
+ rela = (const Elf_Rela *)reldata;
+ where = (Elf_Addr *)(dest + relbase + rela->r_offset - dataoff);
+ addend = rela->r_addend;
+ rtype = ELF_R_TYPE(rela->r_info);
+ symidx = ELF_R_SYM(rela->r_info);
+ break;
+ default:
+ return (EINVAL);
+ }
+
+ if ((char *)where < (char *)dest || (char *)where >= (char *)dest + len)
+ return (0);
+
+ if (reltype == EF_RELOC_REL)
+ addend = *where;
+
+ switch (rtype) {
+ case R_386_RELATIVE: /* A + B */
+ addr = (Elf_Addr)addend + relbase;
+ *where = addr;
+ break;
+ case R_386_32: /* S + A - P */
+ addr = EF_SYMADDR(ef, symidx);
+ addr += addend;
+ *where = addr;
+ break;
+ case R_386_GLOB_DAT: /* S */
+ addr = EF_SYMADDR(ef, symidx);
+ *where = addr;
+ break;
+ default:
+ warnx("unhandled relocation type %d", (int)rtype);
+ }
+ return (0);
+}
diff --git a/usr.sbin/kldxref/ef_nop.c b/usr.sbin/kldxref/ef_nop.c
new file mode 100644
index 0000000..cbbd43c
--- /dev/null
+++ b/usr.sbin/kldxref/ef_nop.c
@@ -0,0 +1,40 @@
+/*-
+ * Copyright (c) 2003 Jake Burkholder.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <machine/elf.h>
+
+#include "ef.h"
+
+int
+ef_reloc(struct elf_file *ef, const void *reldata, int reltype, Elf_Off relbase,
+ Elf_Off dataoff, size_t len, void *dest)
+{
+
+ return (0);
+}
diff --git a/usr.sbin/kldxref/ef_obj.c b/usr.sbin/kldxref/ef_obj.c
new file mode 100644
index 0000000..5a4ae22
--- /dev/null
+++ b/usr.sbin/kldxref/ef_obj.c
@@ -0,0 +1,608 @@
+/*
+ * Copyright (c) 2000, Boris Popov
+ * Copyright (c) 1998-2000 Doug Rabson
+ * Copyright (c) 2004 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 by Boris Popov.
+ * 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 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/linker.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <machine/elf.h>
+#define FREEBSD_ELF
+#include <link.h>
+
+#include <err.h>
+
+#include "ef.h"
+
+typedef struct {
+ void *addr;
+ Elf_Off size;
+ int flags;
+ int sec; /* Original section */
+ char *name;
+} Elf_progent;
+
+typedef struct {
+ Elf_Rel *rel;
+ int nrel;
+ int sec;
+} Elf_relent;
+
+typedef struct {
+ Elf_Rela *rela;
+ int nrela;
+ int sec;
+} Elf_relaent;
+
+struct ef_file {
+ char *ef_name;
+ int ef_fd;
+ Elf_Ehdr ef_hdr;
+ struct elf_file *ef_efile;
+
+ caddr_t address;
+ Elf_Off size;
+ Elf_Shdr *e_shdr;
+
+ Elf_progent *progtab;
+ int nprogtab;
+
+ Elf_relaent *relatab;
+ int nrela;
+
+ Elf_relent *reltab;
+ int nrel;
+
+ Elf_Sym *ddbsymtab; /* The symbol table we are using */
+ long ddbsymcnt; /* Number of symbols */
+ caddr_t ddbstrtab; /* String table */
+ long ddbstrcnt; /* number of bytes in string table */
+
+ caddr_t shstrtab; /* Section name string table */
+ long shstrcnt; /* number of bytes in string table */
+
+ int ef_verbose;
+};
+
+static int ef_obj_get_type(elf_file_t ef);
+static int ef_obj_close(elf_file_t ef);
+static int ef_obj_read(elf_file_t ef, Elf_Off offset, size_t len, void* dest);
+static int ef_obj_read_entry(elf_file_t ef, Elf_Off offset, size_t len,
+ void **ptr);
+static int ef_obj_seg_read(elf_file_t ef, Elf_Off offset, size_t len,
+ void *dest);
+static int ef_obj_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len,
+ void *dest);
+static int ef_obj_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len,
+ void **ptr);
+static int ef_obj_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len,
+ void **ptr);
+static Elf_Addr ef_obj_symaddr(elf_file_t ef, Elf_Size symidx);
+static int ef_obj_lookup_set(elf_file_t ef, const char *name, long *startp,
+ long *stopp, long *countp);
+static int ef_obj_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym);
+
+static struct elf_file_ops ef_obj_file_ops = {
+ ef_obj_get_type,
+ ef_obj_close,
+ ef_obj_read,
+ ef_obj_read_entry,
+ ef_obj_seg_read,
+ ef_obj_seg_read_rel,
+ ef_obj_seg_read_entry,
+ ef_obj_seg_read_entry_rel,
+ ef_obj_symaddr,
+ ef_obj_lookup_set,
+ ef_obj_lookup_symbol
+};
+
+static int
+ef_obj_get_type(elf_file_t __unused ef)
+{
+
+ return (EFT_KLD);
+}
+
+static int
+ef_obj_lookup_symbol(elf_file_t ef, const char* name, Elf_Sym** sym)
+{
+ Elf_Sym *symp;
+ const char *strp;
+ int i;
+
+ for (i = 0, symp = ef->ddbsymtab; i < ef->ddbsymcnt; i++, symp++) {
+ strp = ef->ddbstrtab + symp->st_name;
+ if (symp->st_shndx != SHN_UNDEF && strcmp(name, strp) == 0) {
+ *sym = symp;
+ return 0;
+ }
+ }
+ return ENOENT;
+}
+
+static int
+ef_obj_lookup_set(elf_file_t ef, const char *name, long *startp, long *stopp,
+ long *countp)
+{
+ int i;
+
+ for (i = 0; i < ef->nprogtab; i++) {
+ if ((strncmp(ef->progtab[i].name, "set_", 4) == 0) &&
+ strcmp(ef->progtab[i].name + 4, name) == 0) {
+ *startp = (char *)ef->progtab[i].addr - ef->address;
+ *stopp = (char *)ef->progtab[i].addr +
+ ef->progtab[i].size - ef->address;
+ *countp = (*stopp - *startp) / sizeof(void *);
+ return (0);
+ }
+ }
+ return (ESRCH);
+}
+
+static Elf_Addr
+ef_obj_symaddr(elf_file_t ef, Elf_Size symidx)
+{
+ const Elf_Sym *sym;
+
+ if (symidx >= (size_t) ef->ddbsymcnt)
+ return (0);
+ sym = ef->ddbsymtab + symidx;
+
+ if (sym->st_shndx != SHN_UNDEF)
+ return (sym->st_value - (Elf_Addr)ef->address);
+ return (0);
+}
+
+static int
+ef_obj_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest)
+{
+ ssize_t r;
+
+ if (offset != (Elf_Off)-1) {
+ if (lseek(ef->ef_fd, offset, SEEK_SET) == -1)
+ return EIO;
+ }
+
+ r = read(ef->ef_fd, dest, len);
+ if (r != -1 && (size_t)r == len)
+ return 0;
+ else
+ return EIO;
+}
+
+static int
+ef_obj_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr)
+{
+ int error;
+
+ *ptr = malloc(len);
+ if (*ptr == NULL)
+ return ENOMEM;
+ error = ef_obj_read(ef, offset, len, *ptr);
+ if (error)
+ free(*ptr);
+ return error;
+}
+
+static int
+ef_obj_seg_read(elf_file_t ef, Elf_Off offset, size_t len, void *dest)
+{
+
+ if (offset + len > ef->size) {
+ if (ef->ef_verbose)
+ warnx("ef_seg_read_rel(%s): bad offset/len (%lx:%ld)",
+ ef->ef_name, (long)offset, (long)len);
+ return (EFAULT);
+ }
+ bcopy(ef->address + offset, dest, len);
+ return (0);
+}
+
+static int
+ef_obj_seg_read_rel(elf_file_t ef, Elf_Off offset, size_t len, void *dest)
+{
+ char *memaddr;
+ Elf_Rel *r;
+ Elf_Rela *a;
+ Elf_Off secbase, dataoff;
+ int error, i, sec;
+
+ if (offset + len > ef->size) {
+ if (ef->ef_verbose)
+ warnx("ef_seg_read_rel(%s): bad offset/len (%lx:%ld)",
+ ef->ef_name, (long)offset, (long)len);
+ return (EFAULT);
+ }
+ bcopy(ef->address + offset, dest, len);
+
+ /* Find out which section contains the data. */
+ memaddr = ef->address + offset;
+ sec = -1;
+ secbase = dataoff = 0;
+ for (i = 0; i < ef->nprogtab; i++) {
+ if (ef->progtab[i].addr == NULL)
+ continue;
+ if (memaddr < (char *)ef->progtab[i].addr || memaddr + len >
+ (char *)ef->progtab[i].addr + ef->progtab[i].size)
+ continue;
+ sec = ef->progtab[i].sec;
+ /* We relocate to address 0. */
+ secbase = (char *)ef->progtab[i].addr - ef->address;
+ dataoff = memaddr - ef->address;
+ break;
+ }
+
+ if (sec == -1)
+ return (EFAULT);
+
+ /* Now do the relocations. */
+ for (i = 0; i < ef->nrel; i++) {
+ if (ef->reltab[i].sec != sec)
+ continue;
+ for (r = ef->reltab[i].rel;
+ r < &ef->reltab[i].rel[ef->reltab[i].nrel]; r++) {
+ error = ef_reloc(ef->ef_efile, r, EF_RELOC_REL, secbase,
+ dataoff, len, dest);
+ if (error != 0)
+ return (error);
+ }
+ }
+ for (i = 0; i < ef->nrela; i++) {
+ if (ef->relatab[i].sec != sec)
+ continue;
+ for (a = ef->relatab[i].rela;
+ a < &ef->relatab[i].rela[ef->relatab[i].nrela]; a++) {
+ error = ef_reloc(ef->ef_efile, a, EF_RELOC_RELA,
+ secbase, dataoff, len, dest);
+ if (error != 0)
+ return (error);
+ }
+ }
+ return (0);
+}
+
+static int
+ef_obj_seg_read_entry(elf_file_t ef, Elf_Off offset, size_t len, void **ptr)
+{
+ int error;
+
+ *ptr = malloc(len);
+ if (*ptr == NULL)
+ return ENOMEM;
+ error = ef_obj_seg_read(ef, offset, len, *ptr);
+ if (error)
+ free(*ptr);
+ return error;
+}
+
+static int
+ef_obj_seg_read_entry_rel(elf_file_t ef, Elf_Off offset, size_t len,
+ void **ptr)
+{
+ int error;
+
+ *ptr = malloc(len);
+ if (*ptr == NULL)
+ return ENOMEM;
+ error = ef_obj_seg_read_rel(ef, offset, len, *ptr);
+ if (error)
+ free(*ptr);
+ return error;
+}
+
+int
+ef_obj_open(const char *filename, struct elf_file *efile, int verbose)
+{
+ elf_file_t ef;
+ Elf_Ehdr *hdr;
+ Elf_Shdr *shdr;
+ Elf_Sym *es;
+ char *mapbase;
+ void *vtmp;
+ size_t mapsize, alignmask, max_addralign;
+ int error, fd, pb, ra, res, rl;
+ int i, j, nbytes, nsym, shstrindex, symstrindex, symtabindex;
+
+ if (filename == NULL)
+ return EFTYPE;
+ if ((fd = open(filename, O_RDONLY)) == -1)
+ return errno;
+
+ ef = malloc(sizeof(*ef));
+ if (ef == NULL) {
+ close(fd);
+ return (ENOMEM);
+ }
+
+ efile->ef_ef = ef;
+ efile->ef_ops = &ef_obj_file_ops;
+
+ bzero(ef, sizeof(*ef));
+ ef->ef_verbose = verbose;
+ ef->ef_fd = fd;
+ ef->ef_name = strdup(filename);
+ ef->ef_efile = efile;
+ hdr = (Elf_Ehdr *)&ef->ef_hdr;
+
+ res = read(fd, hdr, sizeof(*hdr));
+ error = EFTYPE;
+ if (res != sizeof(*hdr))
+ goto out;
+ if (!IS_ELF(*hdr))
+ goto out;
+ if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS ||
+ hdr->e_ident[EI_DATA] != ELF_TARG_DATA ||
+ hdr->e_ident[EI_VERSION] != EV_CURRENT ||
+ hdr->e_version != EV_CURRENT || hdr->e_machine != ELF_TARG_MACH ||
+ hdr->e_type != ET_REL)
+ goto out;
+
+ nbytes = hdr->e_shnum * hdr->e_shentsize;
+ if (nbytes == 0 || hdr->e_shoff == 0 ||
+ hdr->e_shentsize != sizeof(Elf_Shdr))
+ goto out;
+
+ if (ef_obj_read_entry(ef, hdr->e_shoff, nbytes, &vtmp) != 0) {
+ printf("ef_read_entry failed\n");
+ goto out;
+ }
+ ef->e_shdr = shdr = vtmp;
+
+ /* Scan the section header for information and table sizing. */
+ nsym = 0;
+ symtabindex = -1;
+ symstrindex = -1;
+ for (i = 0; i < hdr->e_shnum; i++) {
+ switch (shdr[i].sh_type) {
+ case SHT_PROGBITS:
+ case SHT_NOBITS:
+ ef->nprogtab++;
+ break;
+ case SHT_SYMTAB:
+ nsym++;
+ symtabindex = i;
+ symstrindex = shdr[i].sh_link;
+ break;
+ case SHT_REL:
+ ef->nrel++;
+ break;
+ case SHT_RELA:
+ ef->nrela++;
+ break;
+ case SHT_STRTAB:
+ break;
+ }
+ }
+
+ if (ef->nprogtab == 0) {
+ warnx("%s: file has no contents", filename);
+ goto out;
+ }
+ if (nsym != 1) {
+ warnx("%s: file has no valid symbol table", filename);
+ goto out;
+ }
+ if (symstrindex < 0 || symstrindex > hdr->e_shnum ||
+ shdr[symstrindex].sh_type != SHT_STRTAB) {
+ warnx("%s: file has invalid symbol strings", filename);
+ goto out;
+ }
+
+ /* Allocate space for tracking the load chunks */
+ if (ef->nprogtab != 0)
+ ef->progtab = calloc(ef->nprogtab, sizeof(*ef->progtab));
+ if (ef->nrel != 0)
+ ef->reltab = calloc(ef->nrel, sizeof(*ef->reltab));
+ if (ef->nrela != 0)
+ ef->relatab = calloc(ef->nrela, sizeof(*ef->relatab));
+ if ((ef->nprogtab != 0 && ef->progtab == NULL) ||
+ (ef->nrel != 0 && ef->reltab == NULL) ||
+ (ef->nrela != 0 && ef->relatab == NULL)) {
+ printf("malloc failed\n");
+ error = ENOMEM;
+ goto out;
+ }
+
+ ef->ddbsymcnt = shdr[symtabindex].sh_size / sizeof(Elf_Sym);
+ if (ef_obj_read_entry(ef, shdr[symtabindex].sh_offset,
+ shdr[symtabindex].sh_size, (void**)&ef->ddbsymtab) != 0) {
+ printf("ef_read_entry failed\n");
+ goto out;
+ }
+
+ ef->ddbstrcnt = shdr[symstrindex].sh_size;
+ if (ef_obj_read_entry(ef, shdr[symstrindex].sh_offset,
+ shdr[symstrindex].sh_size, (void**)&ef->ddbstrtab) != 0) {
+ printf("ef_read_entry failed\n");
+ goto out;
+ }
+
+ /* Do we have a string table for the section names? */
+ shstrindex = -1;
+ if (hdr->e_shstrndx != 0 &&
+ shdr[hdr->e_shstrndx].sh_type == SHT_STRTAB) {
+ shstrindex = hdr->e_shstrndx;
+ ef->shstrcnt = shdr[shstrindex].sh_size;
+ if (ef_obj_read_entry(ef, shdr[shstrindex].sh_offset,
+ shdr[shstrindex].sh_size, (void**)&ef->shstrtab) != 0) {
+ printf("ef_read_entry failed\n");
+ goto out;
+ }
+ }
+
+ /* Size up code/data(progbits) and bss(nobits). */
+ alignmask = 0;
+ max_addralign = 0;
+ mapsize = 0;
+ for (i = 0; i < hdr->e_shnum; i++) {
+ switch (shdr[i].sh_type) {
+ case SHT_PROGBITS:
+ case SHT_NOBITS:
+ alignmask = shdr[i].sh_addralign - 1;
+ if (shdr[i].sh_addralign > max_addralign)
+ max_addralign = shdr[i].sh_addralign;
+ mapsize += alignmask;
+ mapsize &= ~alignmask;
+ mapsize += shdr[i].sh_size;
+ break;
+ }
+ }
+
+ /* We know how much space we need for the text/data/bss/etc. */
+ ef->size = mapsize;
+ if (posix_memalign((void **)&ef->address, max_addralign, mapsize)) {
+ printf("posix_memalign failed\n");
+ goto out;
+ }
+ mapbase = ef->address;
+
+ /*
+ * Now load code/data(progbits), zero bss(nobits), allocate
+ * space for and load relocs
+ */
+ pb = 0;
+ rl = 0;
+ ra = 0;
+ alignmask = 0;
+ for (i = 0; i < hdr->e_shnum; i++) {
+ switch (shdr[i].sh_type) {
+ case SHT_PROGBITS:
+ case SHT_NOBITS:
+ alignmask = shdr[i].sh_addralign - 1;
+ mapbase += alignmask;
+ mapbase = (char *)((uintptr_t)mapbase & ~alignmask);
+ ef->progtab[pb].addr = (void *)(uintptr_t)mapbase;
+ if (shdr[i].sh_type == SHT_PROGBITS) {
+ ef->progtab[pb].name = "<<PROGBITS>>";
+ if (ef_obj_read(ef, shdr[i].sh_offset,
+ shdr[i].sh_size,
+ ef->progtab[pb].addr) != 0) {
+ printf("failed to read progbits\n");
+ goto out;
+ }
+ } else {
+ ef->progtab[pb].name = "<<NOBITS>>";
+ bzero(ef->progtab[pb].addr, shdr[i].sh_size);
+ }
+ ef->progtab[pb].size = shdr[i].sh_size;
+ ef->progtab[pb].sec = i;
+ if (ef->shstrtab && shdr[i].sh_name != 0)
+ ef->progtab[pb].name =
+ ef->shstrtab + shdr[i].sh_name;
+
+ /* Update all symbol values with the offset. */
+ for (j = 0; j < ef->ddbsymcnt; j++) {
+ es = &ef->ddbsymtab[j];
+ if (es->st_shndx != i)
+ continue;
+ es->st_value += (Elf_Addr)ef->progtab[pb].addr;
+ }
+ mapbase += shdr[i].sh_size;
+ pb++;
+ break;
+ case SHT_REL:
+ ef->reltab[rl].nrel = shdr[i].sh_size / sizeof(Elf_Rel);
+ ef->reltab[rl].sec = shdr[i].sh_info;
+ if (ef_obj_read_entry(ef, shdr[i].sh_offset,
+ shdr[i].sh_size, (void**)&ef->reltab[rl].rel) !=
+ 0) {
+ printf("ef_read_entry failed\n");
+ goto out;
+ }
+ rl++;
+ break;
+ case SHT_RELA:
+ ef->relatab[ra].nrela =
+ shdr[i].sh_size / sizeof(Elf_Rela);
+ ef->relatab[ra].sec = shdr[i].sh_info;
+ if (ef_obj_read_entry(ef, shdr[i].sh_offset,
+ shdr[i].sh_size, (void**)&ef->relatab[ra].rela) !=
+ 0) {
+ printf("ef_read_entry failed\n");
+ goto out;
+ }
+ ra++;
+ break;
+ }
+ }
+ error = 0;
+out:
+ if (error)
+ ef_obj_close(ef);
+ return error;
+}
+
+static int
+ef_obj_close(elf_file_t ef)
+{
+ int i;
+
+ close(ef->ef_fd);
+ if (ef->ef_name)
+ free(ef->ef_name);
+ if (ef->e_shdr != NULL)
+ free(ef->e_shdr);
+ if (ef->size != 0)
+ free(ef->address);
+ if (ef->nprogtab != 0)
+ free(ef->progtab);
+ if (ef->nrel != 0) {
+ for (i = 0; i < ef->nrel; i++)
+ if (ef->reltab[i].rel != NULL)
+ free(ef->reltab[i].rel);
+ free(ef->reltab);
+ }
+ if (ef->nrela != 0) {
+ for (i = 0; i < ef->nrela; i++)
+ if (ef->relatab[i].rela != NULL)
+ free(ef->relatab[i].rela);
+ free(ef->relatab);
+ }
+ if (ef->ddbsymtab != NULL)
+ free(ef->ddbsymtab);
+ if (ef->ddbstrtab != NULL)
+ free(ef->ddbstrtab);
+ if (ef->shstrtab != NULL)
+ free(ef->shstrtab);
+ ef->ef_efile->ef_ops = NULL;
+ ef->ef_efile->ef_ef = NULL;
+ free(ef);
+
+ return 0;
+}
diff --git a/usr.sbin/kldxref/ef_powerpc.c b/usr.sbin/kldxref/ef_powerpc.c
new file mode 100644
index 0000000..a96a727
--- /dev/null
+++ b/usr.sbin/kldxref/ef_powerpc.c
@@ -0,0 +1,74 @@
+/*-
+ * Copyright (c) 2005 Peter Grehan.
+ * Copyright 1996-1998 John D. Polstra.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED 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 <machine/elf.h>
+
+#include <err.h>
+#include <errno.h>
+#include <string.h>
+
+#include "ef.h"
+
+#include <stdio.h>
+
+/*
+ * Apply relocations to the values obtained from the file. `relbase' is the
+ * target relocation address of the section, and `dataoff/len' is the region
+ * that is to be relocated, and has been copied to *dest
+ */
+int
+ef_reloc(struct elf_file *ef, const void *reldata, int reltype, Elf_Off relbase,
+ Elf_Off dataoff, size_t len, void *dest)
+{
+ Elf_Addr *where, addend;
+ Elf_Size rtype, symidx;
+ const Elf_Rela *rela;
+
+ if (reltype != EF_RELOC_RELA)
+ return (EINVAL);
+
+ rela = (const Elf_Rela *)reldata;
+ where = (Elf_Addr *) ((Elf_Off)dest - dataoff + rela->r_offset);
+ addend = rela->r_addend;
+ rtype = ELF_R_TYPE(rela->r_info);
+ symidx = ELF_R_SYM(rela->r_info);
+
+ if ((char *)where < (char *)dest || (char *)where >= (char *)dest + len)
+ return (0);
+
+ switch(rtype) {
+ case R_PPC_RELATIVE: /* word32 B + A */
+ *where = relbase + addend;
+ break;
+ default:
+ warnx("unhandled relocation type %d", rtype);
+ }
+ return (0);
+}
diff --git a/usr.sbin/kldxref/ef_sparc64.c b/usr.sbin/kldxref/ef_sparc64.c
new file mode 100644
index 0000000..7ba2a43
--- /dev/null
+++ b/usr.sbin/kldxref/ef_sparc64.c
@@ -0,0 +1,69 @@
+/*-
+ * Copyright (c) 2003 Jake Burkholder.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <machine/elf.h>
+
+#include <err.h>
+#include <string.h>
+
+#include "ef.h"
+
+/*
+ * Apply relocations to the values we got from the file. `relbase' is the
+ * target relocation address of the section, and `dataoff' is the target
+ * relocation address of the data in `dest'.
+ */
+int
+ef_reloc(struct elf_file *ef, const void *reldata, int reltype, Elf_Off relbase,
+ Elf_Off dataoff, size_t len, void *dest)
+{
+ const Elf_Rela *a;
+ Elf_Size w;
+
+ switch (reltype) {
+ case EF_RELOC_RELA:
+ a = reldata;
+ if (relbase + a->r_offset >= dataoff && relbase + a->r_offset <
+ dataoff + len) {
+ switch (ELF_R_TYPE(a->r_info)) {
+ case R_SPARC_RELATIVE:
+ w = a->r_addend + relbase;
+ memcpy((u_char *)dest + (relbase + a->r_offset -
+ dataoff), &w, sizeof(w));
+ break;
+ default:
+ warnx("unhandled relocation type %u",
+ (unsigned int)ELF_R_TYPE(a->r_info));
+ break;
+ }
+ }
+ break;
+ }
+ return (0);
+}
diff --git a/usr.sbin/kldxref/fileformat b/usr.sbin/kldxref/fileformat
new file mode 100644
index 0000000..81d115c
--- /dev/null
+++ b/usr.sbin/kldxref/fileformat
@@ -0,0 +1,45 @@
+$FreeBSD$
+
+linker.hints file consists from the one or more records,
+and is processed by sys/kern/kern_linker.c::linker_hints_lookup()
+
+First record of file is special and determines its version:
+
+int version;
+
+ All subsequent records have following format:
+
+struct record {
+ int length; /* length of following data */
+ char data[length];
+};
+
+ Each record is aligned on sizeof(int) boundary. First integer of the field
+'data' determines its type:
+
+struct data {
+ int type; /* type of data. currently MDT_* values */
+};
+
+ The rest of record depends on the type.
+
+struct string {
+ uint8_t length; /* length of string */
+ char val[]; /* string itself (no terminating zero) */
+};
+
+struct data_mdt_version {
+ int type = MDT_VERSION;
+ struct string modname;
+ /* padding */
+ int version;
+ struct string kldname;
+ /* padding */
+};
+
+struct data_mdt_module {
+ int type = MDT_MODULE;
+ struct string modname;
+ struct string kldname;
+ /* padding */
+};
diff --git a/usr.sbin/kldxref/kldxref.8 b/usr.sbin/kldxref/kldxref.8
new file mode 100644
index 0000000..a6174b6
--- /dev/null
+++ b/usr.sbin/kldxref/kldxref.8
@@ -0,0 +1,95 @@
+.\"-
+.\" Copyright (c) 2001 Boris Popov
+.\" Copyright (c) 2001 Dag-Erling Coïdan Smørgrav
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 9, 2001
+.Dt KLDXREF 8
+.Os
+.Sh NAME
+.Nm kldxref
+.Nd generate hints for the kernel loader
+.Sh SYNOPSIS
+.Nm
+.Op Fl Rdv
+.Op Fl f Ar hintsfile
+.Ar path ...
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to generate hint files which list modules, their
+version numbers, and the files that contain them.
+These hints are used by the kernel loader to determine where to find a
+particular KLD module.
+.Pp
+A separate hint file is generated for each directory listed on the
+command line that contains modules.
+If no hint records are generated for a particular directory, no hint
+file is created, and the preexisting hint file (if there was one in
+that directory) is removed.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl R
+Recurse into subdirectories.
+.It Fl d
+Do not generate a hint file, but print module metadata on standard
+output.
+.It Fl f Ar hintsfile
+Specify a different name for the hints files than
+.Pa linker.hints .
+.It Fl v
+Operate in verbose mode.
+.El
+.Sh EXAMPLES
+To build hint files for both standard and add-on modules:
+.Pp
+.Dl "kldxref /boot/kernel /boot/modules"
+.Pp
+To build hint files for all installed kernels:
+.Pp
+.Dl "kldxref -R /boot"
+.Sh SEE ALSO
+.Xr kld 4 ,
+.Xr kldconfig 8 ,
+.Xr kldload 8 ,
+.Xr kldstat 8 ,
+.Xr kldunload 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 5.0 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility was implemented by
+.An Boris Popov Aq bp@FreeBSD.org .
+This manual page was written by
+.An Boris Popov Aq bp@FreeBSD.org
+and
+.An Dag-Erling Sm\(/orgrav Aq des@FreeBSD.org .
diff --git a/usr.sbin/kldxref/kldxref.c b/usr.sbin/kldxref/kldxref.c
new file mode 100644
index 0000000..b4de191
--- /dev/null
+++ b/usr.sbin/kldxref/kldxref.c
@@ -0,0 +1,360 @@
+/*
+ * Copyright (c) 2000, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Boris Popov.
+ * 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 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/param.h>
+#include <sys/exec.h>
+#include <sys/queue.h>
+#include <sys/kernel.h>
+#include <sys/reboot.h>
+#include <sys/linker.h>
+#include <sys/stat.h>
+#include <sys/module.h>
+#define FREEBSD_ELF
+#include <link.h>
+#include <err.h>
+#include <fts.h>
+#include <string.h>
+#include <machine/elf.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "ef.h"
+
+#define MAXRECSIZE 1024
+#define check(val) if ((error = (val)) != 0) break
+
+static int dflag; /* do not create a hint file, only write on stdout */
+static int verbose;
+
+static FILE *fxref; /* current hints file */
+
+static const char *xref_file = "linker.hints";
+
+/*
+ * A record is stored in the static buffer recbuf before going to disk.
+ */
+static char recbuf[MAXRECSIZE];
+static int recpos; /* current write position */
+static int reccnt; /* total record written to this file so far */
+
+static void
+intalign(void)
+{
+ recpos = (recpos + sizeof(int) - 1) & ~(sizeof(int) - 1);
+}
+
+static void
+record_start(void)
+{
+ recpos = 0;
+ memset(recbuf, 0, MAXRECSIZE);
+}
+
+static int
+record_end(void)
+{
+ if (recpos == 0)
+ return 0;
+ reccnt++;
+ intalign();
+ fwrite(&recpos, sizeof(recpos), 1, fxref);
+ return fwrite(recbuf, recpos, 1, fxref) != 1 ? errno : 0;
+}
+
+static int
+record_buf(const void *buf, int size)
+{
+ if (MAXRECSIZE - recpos < size)
+ errx(1, "record buffer overflow");
+ memcpy(recbuf + recpos, buf, size);
+ recpos += size;
+ return 0;
+}
+
+/*
+ * An int is stored in host order and aligned
+ */
+static int
+record_int(int val)
+{
+ intalign();
+ return record_buf(&val, sizeof(val));
+}
+
+/*
+ * A string is stored as 1-byte length plus data, no padding
+ */
+static int
+record_string(const char *str)
+{
+ int len, error;
+ u_char val;
+
+ if (dflag)
+ return 0;
+ val = len = strlen(str);
+ if (len > 255)
+ errx(1, "string %s too long", str);
+ error = record_buf(&val, sizeof(val));
+ if (error)
+ return error;
+ return record_buf(str, len);
+}
+
+static int
+parse_entry(struct mod_metadata *md, const char *cval,
+ struct elf_file *ef, const char *kldname)
+{
+ struct mod_depend mdp;
+ struct mod_version mdv;
+ Elf_Off data = (Elf_Off)md->md_data;
+ int error = 0;
+
+ record_start();
+ switch (md->md_type) {
+ case MDT_DEPEND:
+ if (!dflag)
+ break;
+ check(EF_SEG_READ(ef, data, sizeof(mdp), &mdp));
+ printf(" depends on %s.%d (%d,%d)\n", cval,
+ mdp.md_ver_preferred, mdp.md_ver_minimum, mdp.md_ver_maximum);
+ break;
+ case MDT_VERSION:
+ check(EF_SEG_READ(ef, data, sizeof(mdv), &mdv));
+ if (dflag) {
+ printf(" interface %s.%d\n", cval, mdv.mv_version);
+ } else {
+ record_int(MDT_VERSION);
+ record_string(cval);
+ record_int(mdv.mv_version);
+ record_string(kldname);
+ }
+ break;
+ case MDT_MODULE:
+ if (dflag) {
+ printf(" module %s\n", cval);
+ } else {
+ record_int(MDT_MODULE);
+ record_string(cval);
+ record_string(kldname);
+ }
+ break;
+ default:
+ warnx("unknown metadata record %d in file %s", md->md_type, kldname);
+ }
+ if (!error)
+ record_end();
+ return error;
+}
+
+static int
+read_kld(char *filename, char *kldname)
+{
+ struct mod_metadata md;
+ struct elf_file ef;
+ void **p, **orgp;
+ int error, eftype, nmlen;
+ long start, finish, entries;
+ char kldmodname[MAXMODNAME + 1], cval[MAXMODNAME + 1], *cp;
+
+ if (verbose || dflag)
+ printf("%s\n", filename);
+ error = ef_open(filename, &ef, verbose);
+ if (error) {
+ error = ef_obj_open(filename, &ef, verbose);
+ if (error) {
+ if (verbose)
+ warnc(error, "elf_open(%s)", filename);
+ return error;
+ }
+ }
+ eftype = EF_GET_TYPE(&ef);
+ if (eftype != EFT_KLD && eftype != EFT_KERNEL) {
+ EF_CLOSE(&ef);
+ return 0;
+ }
+ if (!dflag) {
+ cp = strrchr(kldname, '.');
+ nmlen = (cp != NULL) ? cp - kldname : (int)strlen(kldname);
+ if (nmlen > MAXMODNAME)
+ nmlen = MAXMODNAME;
+ strlcpy(kldmodname, kldname, nmlen);
+/* fprintf(fxref, "%s:%s:%d\n", kldmodname, kldname, 0);*/
+ }
+ do {
+ check(EF_LOOKUP_SET(&ef, MDT_SETNAME, &start, &finish,
+ &entries));
+ check(EF_SEG_READ_ENTRY_REL(&ef, start, sizeof(*p) * entries,
+ (void *)&p));
+ orgp = p;
+ while(entries--) {
+ check(EF_SEG_READ_REL(&ef, (Elf_Off)*p, sizeof(md),
+ &md));
+ p++;
+ check(EF_SEG_READ(&ef, (Elf_Off)md.md_cval,
+ sizeof(cval), cval));
+ cval[MAXMODNAME] = '\0';
+ parse_entry(&md, cval, &ef, kldname);
+ }
+ if (error)
+ warnc(error, "error while reading %s", filename);
+ free(orgp);
+ } while(0);
+ EF_CLOSE(&ef);
+ return error;
+}
+
+/*
+ * Create a temp file in directory root, make sure we don't
+ * overflow the buffer for the destination name
+ */
+static FILE *
+maketempfile(char *dest, const char *root)
+{
+ char *p;
+ int n, fd;
+
+ p = strrchr(root, '/');
+ n = p != NULL ? p - root + 1 : 0;
+ if (snprintf(dest, MAXPATHLEN, "%.*slhint.XXXXXX", n, root) >=
+ MAXPATHLEN) {
+ errno = ENAMETOOLONG;
+ return NULL;
+ }
+
+ fd = mkstemp(dest);
+ if (fd < 0)
+ return NULL;
+ fchmod(fd, 0644); /* nothing secret in the file */
+ return fdopen(fd, "w+");
+}
+
+static char xrefname[MAXPATHLEN], tempname[MAXPATHLEN];
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "%s\n",
+ "usage: kldxref [-Rdv] [-f hintsfile] path ..."
+ );
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ FTS *ftsp;
+ FTSENT *p;
+ int opt, fts_options, ival;
+ struct stat sb;
+
+ fts_options = FTS_PHYSICAL;
+
+ while ((opt = getopt(argc, argv, "Rdf:v")) != -1) {
+ switch (opt) {
+ case 'd': /* no hint file, only print on stdout */
+ dflag = 1;
+ break;
+ case 'f': /* use this name instead of linker.hints */
+ xref_file = optarg;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'R': /* recurse on directories */
+ fts_options |= FTS_COMFOLLOW;
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ if (argc - optind < 1)
+ usage();
+ argc -= optind;
+ argv += optind;
+
+ if (stat(argv[0], &sb) != 0)
+ err(1, "%s", argv[0]);
+ if ((sb.st_mode & S_IFDIR) == 0) {
+ errno = ENOTDIR;
+ err(1, "%s", argv[0]);
+ }
+
+ ftsp = fts_open(argv, fts_options, 0);
+ if (ftsp == NULL)
+ exit(1);
+
+ for (;;) {
+ p = fts_read(ftsp);
+ if ((p == NULL || p->fts_info == FTS_D) && fxref) {
+ /* close and rename the current hint file */
+ fclose(fxref);
+ fxref = NULL;
+ if (reccnt) {
+ rename(tempname, xrefname);
+ } else {
+ /* didn't find any entry, ignore this file */
+ unlink(tempname);
+ unlink(xrefname);
+ }
+ }
+ if (p == NULL)
+ break;
+ if (p->fts_info == FTS_D && !dflag) {
+ /* visiting a new directory, create a new hint file */
+ snprintf(xrefname, sizeof(xrefname), "%s/%s",
+ ftsp->fts_path, xref_file);
+ fxref = maketempfile(tempname, ftsp->fts_path);
+ if (fxref == NULL)
+ err(1, "can't create %s", tempname);
+ ival = 1;
+ fwrite(&ival, sizeof(ival), 1, fxref);
+ reccnt = 0;
+ }
+ /* skip non-files or .symbols entries */
+ if (p->fts_info != FTS_F)
+ continue;
+ if (p->fts_namelen >= 8 &&
+ strcmp(p->fts_name + p->fts_namelen - 8, ".symbols") == 0)
+ continue;
+ read_kld(p->fts_path, p->fts_name);
+ }
+ fts_close(ftsp);
+ return 0;
+}
diff --git a/usr.sbin/lastlogin/Makefile b/usr.sbin/lastlogin/Makefile
new file mode 100644
index 0000000..017aadf
--- /dev/null
+++ b/usr.sbin/lastlogin/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= lastlogin
+MAN= lastlogin.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lastlogin/lastlogin.8 b/usr.sbin/lastlogin/lastlogin.8
new file mode 100644
index 0000000..03a587d
--- /dev/null
+++ b/usr.sbin/lastlogin/lastlogin.8
@@ -0,0 +1,80 @@
+.\" $FreeBSD$
+.\" $NetBSD: lastlogin.8,v 1.3 1998/02/06 06:20:39 perry Exp $
+.\"
+.\" Copyright (c) 1996 John M. Vinopal
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 John M. Vinopal.
+.\" 4. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+.\" BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+.\" AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+.\" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd January 11, 1996
+.Dt LASTLOGIN 8
+.Os
+.Sh NAME
+.Nm lastlogin
+.Nd indicate last login time of users
+.Sh SYNOPSIS
+.Nm
+.Op Ar user ...
+.Sh DESCRIPTION
+The
+.Nm
+utility will list the last login session of each specified
+.Ar user ,
+or for all users by default.
+Each line of output contains
+the user name, the tty from which the session was conducted, any
+hostname, and the start time for the session.
+.Pp
+If more than one
+.Ar user
+is given, the session information for each user is printed in
+the order given on the command line.
+Otherwise, information
+for all users is printed, sorted by uid.
+.Pp
+The
+.Nm
+utility differs from
+.Xr last 1
+in that it only prints information regarding the very last login session.
+The last login database is never turned over or deleted in standard usage.
+.Sh FILES
+.Bl -tag -width /var/log/lastlog -compact
+.It Pa /var/log/lastlog
+last login database
+.El
+.Sh SEE ALSO
+.Xr last 1 ,
+.Xr lastlog 5 ,
+.Xr ac 8
+.Sh AUTHORS
+.An John M. Vinopal
+wrote this program in January 1996 and contributed it
+to the
+.Nx
+project.
diff --git a/usr.sbin/lastlogin/lastlogin.c b/usr.sbin/lastlogin/lastlogin.c
new file mode 100644
index 0000000..21c82c3
--- /dev/null
+++ b/usr.sbin/lastlogin/lastlogin.c
@@ -0,0 +1,134 @@
+/*
+ * Copyright (c) 1996 John M. Vinopal
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 John M. Vinopal.
+ * 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>
+#ifndef lint
+__RCSID("$FreeBSD$");
+__RCSID("$NetBSD: lastlogin.c,v 1.4 1998/02/03 04:45:35 perry Exp $");
+#endif
+
+#include <sys/types.h>
+#include <err.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <timeconv.h>
+#include <utmp.h>
+#include <unistd.h>
+
+static const char *logfile = _PATH_LASTLOG;
+
+ int main(int, char **);
+static void output(struct passwd *, struct lastlog *);
+static void usage(void);
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch, i;
+ FILE *fp;
+ struct passwd *passwd;
+ struct lastlog last;
+
+ while ((ch = getopt(argc, argv, "")) != -1) {
+ usage();
+ }
+
+ fp = fopen(logfile, "r");
+ if (fp == NULL)
+ err(1, "%s", logfile);
+
+ setpassent(1); /* Keep passwd file pointers open */
+
+ /* Process usernames given on the command line. */
+ if (argc > 1) {
+ long offset;
+ for (i = 1; i < argc; ++i) {
+ if ((passwd = getpwnam(argv[i])) == NULL) {
+ warnx("user '%s' not found", argv[i]);
+ continue;
+ }
+ /* Calculate the offset into the lastlog file. */
+ offset = (long)(passwd->pw_uid * sizeof(last));
+ if (fseek(fp, offset, SEEK_SET)) {
+ warn("fseek error");
+ continue;
+ }
+ if (fread(&last, sizeof(last), 1, fp) != 1) {
+ warnx("fread error on '%s'", passwd->pw_name);
+ clearerr(fp);
+ continue;
+ }
+ output(passwd, &last);
+ }
+ }
+ /* Read all lastlog entries, looking for active ones */
+ else {
+ for (i = 0; fread(&last, sizeof(last), 1, fp) == 1; i++) {
+ if (last.ll_time == 0)
+ continue;
+ if ((passwd = getpwuid((uid_t)i)) != NULL)
+ output(passwd, &last);
+ }
+ if (ferror(fp))
+ warnx("fread error");
+ }
+
+ setpassent(0); /* Close passwd file pointers */
+
+ fclose(fp);
+ exit(0);
+}
+
+/* Duplicate the output of last(1) */
+static void
+output(p, l)
+ struct passwd *p;
+ struct lastlog *l;
+{
+ time_t t = _int_to_time(l->ll_time);
+ printf("%-*.*s %-*.*s %-*.*s %s",
+ UT_NAMESIZE, UT_NAMESIZE, p->pw_name,
+ UT_LINESIZE, UT_LINESIZE, l->ll_line,
+ UT_HOSTSIZE, UT_HOSTSIZE, l->ll_host,
+ (l->ll_time) ? ctime(&t) : "Never logged in\n");
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: lastlogin [user ...]\n");
+ exit(1);
+}
diff --git a/usr.sbin/lmcconfig/Makefile b/usr.sbin/lmcconfig/Makefile
new file mode 100644
index 0000000..b75ec2c
--- /dev/null
+++ b/usr.sbin/lmcconfig/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= lmcconfig
+MAN= lmcconfig.8
+
+DPADD= ${LIBNETGRAPH}
+LDADD= -lnetgraph
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lmcconfig/lmcconfig.8 b/usr.sbin/lmcconfig/lmcconfig.8
new file mode 100644
index 0000000..ad3ef27
--- /dev/null
+++ b/usr.sbin/lmcconfig/lmcconfig.8
@@ -0,0 +1,723 @@
+.\" Copyright (c) 2003 David Boggs. (boggs@boggs.palo-alto.ca.us)
+.\" All rights reserved.
+.\"
+.\" BSD 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.
+.\"
+.\" 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.
+.\"
+.\" GNU General Public License:
+.\"
+.\" This program is free software; you can redistribute it and/or modify it
+.\" under the terms of the GNU General Public License as published by the Free
+.\" Software Foundation; either version 2 of the License, or (at your option)
+.\" any later version.
+.\"
+.\" This program is distributed in the hope that it will be useful, but WITHOUT
+.\" ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+.\" FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+.\" more details.
+.\"
+.\" You should have received a copy of the GNU General Public License along with
+.\" this program; if not, write to the Free Software Foundation, Inc., 59
+.\" Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd October 3, 2005
+.Dt LMCCONFIG 8
+.Os
+.Sh NAME
+.Nm lmcconfig
+.Nd configuration program for
+.Tn SBE
+(formerly
+.Tn LMC )
+wide-area network interface cards
+.Sh SYNOPSIS
+.Nm
+.Ar interface
+.Op Fl abBcCdDeEfhLmMpPrsStTuwxXyYzZ?
+.Nm
+.Ar interface
+.Fl 1
+.Op Fl aABceEfFgiIlLpPstTuUxX
+.Nm
+.Ar interface
+.Fl 3
+.Op Fl aABcefFlLsSv
+.Sh DESCRIPTION
+The
+.Nm
+utility
+is the configuration program for the
+.Xr lmc 4
+wide-area network device driver.
+It sets control values, such as T3 framing format,
+and it displays status, such as that of integrated modems,
+that are beyond the scope of
+.Xr ifconfig 8 .
+.Pp
+The
+.Nm
+utility
+displays the interface status when no parameters are specified;
+see example below.
+For this case only, if no
+.Ar interface
+is specified, it defaults to
+.Dq Li lmc0 .
+.Pp
+Only the super-user may modify the configuration of a network interface.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Ar interface
+This is the name of the interface; the default is
+.Dq Li lmc0 .
+If
+.Xr netgraph 4
+is present and the interface name ends with a colon
+then Netgraph control messages are used,
+otherwise the
+.Xr ifnet 9
+kernel interface and socket
+.Xr ioctl 2
+system calls are used.
+.It Fl 1
+All parameters after this apply to the T1E1 card.
+.It Fl 3
+All parameters after this apply to the T3 card.
+.El
+.Ss Commands for all cards
+The following parameters apply to more then one card type.
+.Bl -tag -width indent
+.It Fl a Ar number
+Set Transmitter clock source to
+.Ar number .
+.Pp
+.Bl -column "1" "External connector" "T1E1, HSSIc" -offset 2m -compact
+.It "1" Ta "TxClk from modem" Ta "T1E1, HSSI" Ta "default"
+.It "2" Ta "Internal source" Ta "T1E1, HSSI"
+.It "3" Ta "RxClk from modem" Ta "T1E1, HSSIc" Ta "loop timed"
+.It "4" Ta "External connector" Ta "T1E1, HSSIc"
+.El
+.Pp
+An HSSI card normally takes its Tx clock from the modem connector
+(it is a DTE) but can use the PCI bus clock (typically 33 MHz)
+for loopback and null modem testing; values 3 and 4 are only
+applicable to a few rare CompactPCI/HSSI cards.
+.Pp
+A T1E1 card uses an on-board synthesized oscillator
+if the value is 1 or 2; it
+.Em loop times
+(uses the clock recovered by the receiver as the transmitter clock)
+if the value is 3; and it uses a clock from a header connector on
+the card if the value is 4.
+.Pp
+TxClk source is not applicable to other card types.
+.It Fl b
+Read BIOS ROM.
+Print the first 256 locations.
+The BIOS ROM is not used and not present on some cards.
+.It Fl B
+Write BIOS ROM.
+Write the first 256 locations with an address pattern.
+.It Fl c
+Use HDLC's 16-bit CRC polynomial: X^16+X^12+X^5+1 (default).
+.It Fl C
+Use HDLC's 32-bit CRC polynomial:
+X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X+1
+.It Fl d
+Clear the driver-level debug flag.
+Non-critical log messages are suppressed.
+.It Fl D
+Set the driver-level debug flag.
+The driver generates more log messages.
+The driver also generates more log messages if the interface-level debug
+flag is set by
+.Xr ifconfig 8 .
+.It Fl e
+Set DTE (Data Terminal Equipment) mode (default).
+An SSI card transmitter uses the Tx clock signal from the modem connector
+and receives the Data Carrier Detect pin (DCD).
+DTE/DCE is not applicable to other card types except
+a few rare CompactPCI/HSSI cards.
+.It Fl E
+Set DCE (Data Communication Equipment) mode.
+An SSI card transmitter uses an on-board synthesized oscillator
+and drives the Data Carrier Detect pin (DCD).
+.It Fl f Ar number
+Set the frequency of the built-in synthesized oscillator to
+.Ar number
+bits/second.
+The nearest frequency that the synthesizer can generate will be used.
+Only SSI cards and a few rare CompactPCI/HSSI cards have synthesizers.
+.It Fl F
+Set SPPP line protocol to Frame-Relay.
+Only works for
+.Fx 5.4
+and later.
+.It Fl h
+Print help (usage message).
+.It Fl i
+Set interface name (e.g.\&
+.Dq Li lmc0 ) .
+.It Fl L Ar number
+Set loopback mode to
+.Ar number .
+.Pp
+.Bl -column "99" "payload" "inward thru drvrs/rcvrsxxx" "HSSI, SSI" -offset 1m -compact
+.It "1" Ta "none" Ta "default"
+.It "2" Ta "payload" Ta "outward thru framer" Ta "T1E1. T3"
+.It "3" Ta "line" Ta "outward thru line if" Ta "T1E1, T3, HSSIc"
+.It "4" Ta "other" Ta "inward thru line if" Ta "T1E1, T3"
+.It "5" Ta "inward" Ta "inward thru framer" Ta "T1E1, T3"
+.It "6" Ta "dual" Ta "inward and outward" Ta "T1E1, T3"
+.It "16" Ta "tulip" Ta "inward thru Tulip chip" Ta "all cards"
+.It "17" Ta "pins" Ta "inward thru drvrs/rcvrs" Ta "SSI"
+.It "18" Ta "LA/LL" Ta "assert LA/LL modem pin" Ta "HSSI, SSI"
+.It "19" Ta "LB/RL" Ta "assert LB/RL modem pin" Ta "HSSI, SSI"
+.El
+.It Fl m
+Read Tulip MII registers.
+Print the 32 16-bit registers in the Media Independent Interface.
+.It Fl M Ar addr Ar data
+Write Tulip MII register.
+Write
+.Ar data
+into register
+.Ar addr .
+.It Fl p
+Read Tulip PCI configuration registers.
+Print the first 16 32-bit registers in the PCI configuration space.
+.It Fl P Ar addr Ar data
+Write Tulip PCI configuration register.
+Write
+.Ar data
+into register
+.Ar addr .
+.It Fl s
+Read Tulip SROM.
+Print the 64 16-bit locations.
+The PCI subsystem vendor and device IDs are kept here.
+.It Fl S Ar number
+Write Tulip SROM.
+Initializes the Tulip SROM to card type
+.Ar number .
+.Pp
+.Bl -column "9" -offset 1m -compact
+.It 3 Ta HSSI
+.It 4 Ta T3
+.It 5 Ta SSI
+.It 6 Ta T1E1
+.It 7 Ta HSSIc
+.It 8 Ta SDSL
+.It 0 Ta auto-set from MII PHYID
+.El
+.Pp
+If
+.Ar number
+is zero, then the card type is computed from the gate array
+microcode version field in the MII PHYID register.
+.Em CAUTION :
+if the SROM is incorrect, the card will be unusable!
+This command is
+.Em so
+dangerous that
+.Nm
+must be edited and recompiled to enable it.
+.It Fl t
+Read Tulip CSRs.
+Print the 16 32-bit control and status registers.
+.It Fl T Ar addr Ar data
+Write Tulip CSR.
+Write
+.Ar data
+into CSR number
+.Ar addr .
+Note that
+.Ar addr
+is a CSR number (0-15) not a byte offset into CSR space.
+.It Fl u
+Reset event counters to zero.
+The driver counts events like packets in and out, errors, discards, etc.
+The time when the counters are reset is remembered.
+.It Fl U
+Reset gate array.
+Not needed during normal operation; just for testing.
+.It Fl v
+Set verbose mode: print more stuff.
+.It Fl V
+Print the card configuration \[em] see the
+.Sx EXAMPLES
+section.
+.It Fl w
+Load gate array from on-board ROM.
+Not needed during normal operation; just for testing.
+.It Fl W Ar filename
+Load gate array microcode from
+.Ar filename .
+.It Fl x
+Select RAWIP mode \[em] bypass line protocol code.
+.It Fl X
+Select line protocol code rather than RAWIP mode.
+.It Fl y
+Disable SPPP keep-alive packets,
+.It Fl Y
+Enable SPPP keep-alive packets.
+.It Fl z
+Set SPPP line protocol to Cisco-HDLC.
+.It Fl Z
+Set SPPP line protocol to PPP.
+.It Fl ?\&
+Print help (usage message).
+.El
+.Ss Commands for T1E1 cards
+The following parameters apply to the T1E1 card type:
+.Bl -tag -width indent
+.It Fl a Sm Cm y | a | b Sm
+Stop sending alarm signal.
+.Pp
+.Bl -column "y" "Yellow Alarm" "unframed all ones; aka AIS" -offset 1m -compact
+.It "y" Ta "Yellow Alarm" Ta "varies with framing"
+.It "a" Ta "Red Alarm" Ta "unframed all ones; aka AIS"
+.It "b" Ta "Blue Alarm" Ta "unframed all ones"
+.El
+.Pp
+Red alarm, also known as AIS (Alarm Indication Signal),
+and Blue alarm are identical in T1.
+.It Fl A Sm Cm y | a | b Sm
+Start sending alarm signal (see table above).
+.It Fl B Ar number
+Send a Bit Oriented Protocol (BOP) message with code
+.Ar number .
+BOP codes are six bits.
+.It Fl c Ar number
+Set cable length to
+.Ar number
+meters (default: 10 meters).
+This is used to set receiver sensitivity
+and transmitter line build-out.
+.It Fl d
+Print the status of the on-board DSU/CSU \[em] see the
+.Sx EXAMPLES
+section.
+.It Fl e Ar number
+Set the framing format to
+.Ar number :
+.Pp
+.Bl -column "99" -offset 1m -compact
+.It 9 Ta T1-SF/AMI
+.It 27 Ta T1-ESF/B8ZS (default)
+.It 0 Ta E1-FAS
+.It 8 Ta E1-FAS+CRC
+.It 16 Ta E1-FAS+CAS
+.It 24 Ta E1-FAS+CRC+CAS
+.It 32 Ta E1-NO-framing
+.El
+.It Fl E Ar number
+Enable 64Kb time slots (TSs) for the T1E1 card.
+The
+.Ar number
+argument
+is a 32-bit hex number (default 0xFFFFFFFF).
+The LSB is TS0 and the MSB is TS31.
+TS0 and TS25-31 are ignored in T1 mode.
+TS0 and TS16 are determined by the framing format in E1 mode.
+.It Fl f
+Read framer registers.
+Print the 512 8-bit registers in the framer chip.
+.It Fl F Ar addr Ar data
+Write framer register.
+Write
+.Ar data
+into register
+.Ar addr .
+.It Fl g Ar number
+Set receiver gain range to
+.Ar number :
+.Pp
+.Bl -column "0x00" "Medium" "auto-set based on cable length (default)" -offset 1m -compact
+.It "0x24" Ta "Short" Ta "0 to 20 dB of equalized gain"
+.It "0x2C" Ta "Medium" Ta "0 to 30 dB of equalized gain"
+.It "0x34" Ta "Long" Ta "0 to 40 dB of equalized gain"
+.It "0x3F" Ta "Extend" Ta "0 to 64 dB of equalized gain (wide open)"
+.It "0xFF" Ta "Auto" Ta "auto-set based on cable length (default)"
+.El
+.Pp
+This sets the level at which
+.Em Loss-Of-Signal
+is declared.
+.It Fl i
+Send a
+.Em CSU loopback deactivate
+inband command (T1-SF only).
+.It Fl I
+Send a
+.Em CSU loopback activate
+inband command (T1-SF only).
+.It Fl l
+Send a
+.Em line loopback deactivate
+BOP message (T1-ESF only).
+.It Fl L
+Send a
+.Em line loopback activate
+BOP message (T1-ESF only).
+.It Fl p
+Send a
+.Em payload loopback deactivate
+BOP message (T1-ESF only).
+.It Fl P
+Send a
+.Em payload loopback activate
+BOP message (T1-ESF only).
+.It Fl s
+Print the status of the on-board DSU/CSU \[em] see the
+.Sx EXAMPLES
+section.
+.It Fl t
+Stop sending test pattern.
+.It Fl T Ar number
+Start sending test pattern
+.Ar number :
+.Pp
+.Bl -column "99" -offset 1m -compact
+.It 0 Ta unframed X^11+X^9+1
+.It 1 Ta unframed X^15+X^14+1
+.It 2 Ta unframed X^20+X^17+1
+.It 3 Ta unframed X^23+X^18+1
+.It 4 Ta unframed X^11+X^9+1 with 7ZS
+.It 5 Ta unframed X^15+X^14+1 with 7ZS
+.It 6 Ta unframed X^20+X^17+1 with 14ZS (QRSS)
+.It 7 Ta unframed X^23+X^18+1 with 14ZS
+.It 8 Ta framed X^11+X^9+1
+.It 9 Ta framed X^15+X^14+1
+.It 10 Ta framed X^20+X^17+1
+.It 11 Ta framed X^23+X^18+1
+.It 12 Ta framed X^11+X^9+1 with 7ZS
+.It 13 Ta framed X^15+X^14+1 with 7ZS
+.It 14 Ta framed X^20+X^17+1 with 14ZS (QRSS)
+.It 15 Ta framed X^23+X^18+1 with 14ZS
+.El
+.It Fl u Ar number
+Set transmit pulse shape to
+.Ar number :
+.Pp
+.Bl -column "99" -offset 1m -compact
+.It 0 Ta T1 DSX 0 to 40 meters
+.It 2 Ta T1 DSX 40 to 80 meters
+.It 4 Ta T1 DSX 80 to 120 meters
+.It 6 Ta T1 DSX 120 to 160 meters
+.It 8 Ta T1 DSX 160 to 200 meters
+.It 10 Ta E1 75-ohm coax pair
+.It 12 Ta E1 120-ohm twisted pairs
+.It 14 Ta T1 CSU 200 to 2000 meters; set LBO
+.It 255 Ta auto-set based on cable length and framing format (default)
+.El
+.It Fl U Ar number
+Set transmit line build-out to
+.Ar number :
+.Pp
+.Bl -column "255" "22.5 dB" "FCC option A" -offset 1m -compact
+.It " 0" Ta "0 dB" Ta "FCC option A"
+.It " 16" Ta "7.5 dB" Ta "FCC option B"
+.It " 32" Ta "15 dB" Ta "FCC option C"
+.It " 48" Ta "22.5 dB" Ta "final span"
+.It "255" Ta "auto-set based on cable length (default)"
+.El
+.Pp
+This is only applicable if the pulse shape is T1-CSU.
+.It Fl v
+Set verbose mode: print more stuff.
+.It Fl x
+Disable transmitter outputs.
+.It Fl X
+Enable transmitter outputs.
+.El
+.Ss Commands for T3 cards
+The following parameters apply to the T3 card type:
+.Bl -tag -width indent
+.It Fl a Sm Cm y | a | b | i Sm
+Stop sending alarm signal.
+.Pp
+.Bl -column "y" "Yellow Alarm" "framed 1010... aka AIS" -offset 1m -compact
+.It "y" Ta "Yellow Alarm" Ta "X-bits set to 0"
+.It "a" Ta "Red Alarm" Ta "framed 1010... aka AIS"
+.It "b" Ta "Blue Alarm" Ta "unframed all-ones"
+.It "i" Ta "Idle signal" Ta "framed 11001100..."
+.El
+.It Fl A Sm Cm y | a | b | i Sm
+Start sending alarm signal (see table above).
+.It Fl B Ar number
+Send a BOP (Bit Oriented Protocol) message with code
+.Ar number .
+BOP codes are six bits.
+.It Fl c Ar number
+Set cable length to
+.Ar number
+meters (default: 10 meters).
+This is used to set receiver sensitivity
+and transmitter line build-out.
+.It Fl d
+Print the status of the on-board T3 DSU \[em] see the
+.Sx EXAMPLES
+section.
+.It Fl e Ar number
+Set the framing format to
+.Ar number :
+.Pp
+.Bl -column "100" -offset 1m -compact
+.It 100 Ta T3-C-bit parity
+.It 101 Ta T3-M13 format
+.El
+.It Fl f
+Read framer registers.
+Print the 22 8-bit registers in the framer chip.
+.It Fl F Ar addr Ar data
+Write framer register.
+Write
+.Ar data
+into register
+.Ar addr .
+.It Fl l
+Send a
+.Em line loopback deactivate
+BOP message.
+.It Fl L
+Send a
+.Em line loopback activate
+BOP message.
+.It Fl s
+Print the status of the on-board T3 DSU \[em] see the
+.Sx EXAMPLES
+section.
+.It Fl S Ar number
+Set payload scrambler polynominal to
+.Ar number :
+.Pp
+.Bl -column "9" -offset 1m -compact
+.It 1 Ta payload scrambler disabled
+.It 2 Ta X^43+1: DigitalLink and Kentrox
+.It 3 Ta X^20+X^17+1 w/28ZS: Larscom
+.El
+Payload scrambler polynomials are not standardized.
+.It Fl v
+Set verbose mode: print more stuff.
+.It Fl V Ar number
+Set transmit frequency offset to
+.Ar number .
+Some T3 cards can offset the transmitter frequency from 44.736 MHz.
+.Ar Number
+is in the range (0..4095); 2048 is zero offset; step size is about 3 Hz.
+A
+.Ar number
+is written to a Digital-Analog Converter (DAC) which connects
+to a Voltage Controlled Crystal Oscillator (VCXO).
+.El
+.Ss Event Counters
+The device driver counts many interesting events such as
+packets in and out, errors and discards.
+The table below lists the event counters and describes what they count.
+.Bl -tag -width ".Va underruns"
+.It Va ibytes
+Bytes received in packets with good ending status.
+.It Va obytes
+Bytes transmitted.
+.It Va ipackets
+Packets received with good ending status.
+.It Va opackets
+Packets transmitted.
+.It Va ierrors
+Packets received with bad ending status.
+.It Va oerrors
+Packets transmitted with bad ending status.
+.It Va idiscards
+Packets received but discarded because
+the input queue was full or the interface was down.
+.It Va odiscards
+Packets presented for transmission but discarded because
+the output queue was full or the interface was down.
+.It Va txdma
+Packets presented for transmission but queued and retried later
+because no DMA descriptors were available.
+This can happen during normal operation and is not an indication of trouble.
+.It Va fifo-overrun
+Packets that started to arrive, but were aborted because
+the card was unable to DMA data to memory fast enough
+to prevent the receiver fifo from overflowing.
+.It Va fifo-underrun
+Packets that started to transmit but were aborted because
+the card was unable to DMA data from the memory fast enough
+to prevent the transmitter fifo from underflowing.
+When this happens, the transmitter threshold is increased,
+so that more bytes are required to be in the fifo
+before the transmitter is started.
+.It Va missed
+Packets that are missed because the receiver is stopped.
+.It Va overruns
+Packets that are missed because the receiver
+had no DMA descriptors available.
+.It Va fdl_pkts
+Packets received on the T1 Facility Data Link.
+.It Va crc-errs
+Cyclic Redundancy Checksum errors detected by the CRC-6 in
+T1 Extended SuperFrames (ESF) or the CRC-4 in E1 frames.
+.It Va lcv-errs
+Line Coding Violation errors:
+Alternate Mark Inversion (AMI) errors for T1-SF,
+Bipolar 8-Zero Substitution (B8ZS) errors for T1-ESF, or
+High Density Bipolar with 3-Zero Substitution (HDB3) errors for E1 or
+Bipolar 3-Zero Substitution (B3ZS) errors for T3.
+.It Va frm-errs
+T1 or T3 bit errors in the frame alignment signal.
+.It Va febe-errs
+Far End Block Errors:
+T1 or T3 bit errors detected by the device at the far end of the link.
+.It Va par-errs
+T3 bit errors detected by the hop-by-hop parity mechanism.
+.It Va cpar-errs
+T3 bit errors detected by the end-to-end parity mechanism.
+.It Va mfrm-errs
+T3 bit errors in the multi-frame alignment signal.
+.El
+.Ss Transmit Speed
+The hardware counts transmit clocks divided by 2048.
+The software computes
+.Dq "Tx speed"
+from this (see
+.Sx EXAMPLES
+below).
+The transmit clock is the bit rate of the circuit divided by two if the
+circuit is idle and divided by four if the circuit is carrying a packet.
+So an idle circuit reports a Tx speed equal to its bit rate,
+and a busy circuit reports a Tx speed equal to half its bit rate.
+.Pp
+This
+.Dq "bit rate"
+does not include circuit-level overhead bits
+(such as T1 or T3 frame bits) but does include HDLC stuff bits.
+An idle T1 circuit with a raw bit rate of 1544000 and a
+bit-rate-minus-overhead of 1536000 will report a
+.Dq "Tx speed"
+of ((1536000 bitand 4095) plus or minus 4096).
+Sometimes it will even get the correct answer of 1536000, and
+if the link is fully loaded it will report about 768000 bits/sec.
+.Pp
+It is not a perfect bit rate meter (the circuit must be idle),
+but it is a useful circuit utilization meter if you know the
+circuit bit rate and do some arithmetic.
+Software recalculates
+Tx speed once a second; the measurement period has some jitter.
+.Sh EXAMPLES
+When
+.Dq Li lmc0
+is a T1E1 card,
+.Dq Li lmcconfig lmc0
+generates the following output:
+.Bd -literal -offset 2m
+Card name: lmc0
+Card type: SBE/LMC T1E1 card
+Link status: Up
+Tx Speed: 1548288
+Line Prot/Pkg: Frame-Relay/SPPP
+SPPP Keep-alives: OFF
+CRC length: 16 bits
+Loopback: None
+Tx Clk src: Internal source
+Format-Frame/Code: T1-ESF/B8ZS
+TimeSlot [31-0]: 0x01FFFFFE
+Cable length: 10 meters
+Tx pulse shape: auto-set to T1-DSX: 0 to 40 meters
+Rx gain max: auto-set to 20.0 dB
+Current time: Thu Sep 29 21:48:51 2005
+Cntrs reset: Thu Sep 29 16:21:05 2005
+RX bytes: 15053836
+RX packets: 23271
+TX bytes: 1732169
+TX packets: 20526
+Rx fdl pkts: 5443
+.Ed
+.Pp
+When
+.Dq Li lmc0
+is a T1E1 card,
+.Dq Li "lmcconfig lmc0 -1 -d"
+generates the following output:
+.Bd -literal -offset 2m
+Format-Frame/Code: T1-ESF/B8ZS
+TimeSlot [31-0]: 0x01FFFFFE
+Tx Clk src: Internal source
+Tx Speed: 1548288
+Tx pulse shape: T1-DSX: 0 to 40 meters
+Tx outputs: Enabled
+Line impedance: 100 ohms
+Max line loss: 20.0 dB
+Cur line loss: 3.1 dB
+Invert data: No
+Line loop: No
+Payload loop: No
+Framer loop: No
+Analog loop: No
+Tx AIS: No
+Rx AIS: No
+Tx BOP RAI: No
+Rx BOP RAI: No
+Rx LOS analog: No
+Rx LOS digital: No
+Rx LOF: No
+Tx QRS: No
+Rx QRS: No
+LCV errors: 0
+CRC errors: 0
+Frame errors: 0
+Sev Err Frms: 0
+Change of Frm align: 0
+Loss of Frame events: 0
+Last Tx BOP msg: 0x00 (Yellow Alarm (far end LOF))
+Last Rx BOP msg: 0x00 (Yellow Alarm (far end LOF))
+SNMP Near-end performance data:
+ LCV=0 LOS=0 FE=0 CRC=0 AIS=0 SEF=0 OOF=0 RAI=0
+ANSI Far-end performance reports:
+ SEQ=1 CRC=0 SE=0 FE=0 LV=0 SL=0 LB=0
+ SEQ=0 CRC=0 SE=0 FE=0 LV=0 SL=0 LB=0
+ SEQ=3 CRC=0 SE=0 FE=0 LV=0 SL=0 LB=0
+ SEQ=2 CRC=0 SE=0 FE=0 LV=0 SL=0 LB=0
+.Ed
+.Sh DIAGNOSTICS
+Messages indicating the specified interface does not exist, or
+the user is not privileged and tried to alter an interface's configuration.
+.Sh SEE ALSO
+.Xr ioctl 2 ,
+.Xr lmc 4 ,
+.Xr netgraph 4 ,
+.Xr ifconfig 8 ,
+.Xr ifnet 9
+.Pp
+.Pa http://www.sbei.com/
+.Sh HISTORY
+This is a total rewrite of the program
+.Nm lmcctl
+by
+.An "Andrew Stanley-Jones" .
+.Sh AUTHORS
+.An "David Boggs" Aq boggs@boggs.palo-alto.ca.us
diff --git a/usr.sbin/lmcconfig/lmcconfig.c b/usr.sbin/lmcconfig/lmcconfig.c
new file mode 100644
index 0000000..1fe63f7
--- /dev/null
+++ b/usr.sbin/lmcconfig/lmcconfig.c
@@ -0,0 +1,2485 @@
+/*
+ * First author: Michael Graff.
+ * Copyright (c) 1997-2000 Lan Media Corp. (www.lanmedia.com).
+ * All rights reserved.
+ *
+ * Second author: Andrew Stanley-Jones.
+ * Copyright (c) 2000-2002 SBE Corp. (www.sbei.com).
+ * All rights reserved.
+ *
+ * Third author: David Boggs.
+ * Copyright (c) 2002-2004 David Boggs. (boggs@boggs.palo-alto.ca.us).
+ * All rights reserved.
+ *
+ * BSD 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.
+ *
+ * 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.
+ *
+ * GNU General Public License:
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc., 59
+ * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Description:
+ *
+ * This program configures the Unix/Linux device driver for SBE Corp's
+ * wanADAPT and wanPMC series of Wide Area Network Interface Cards.
+ * There is a man page for this program; go find it.
+ *
+ * If Netgraph is present (FreeBSD only):
+ * cc -o lmcconfig -l netgraph -D NETGRAPH lmcconfig.c
+ * If Netgraph is NOT present:
+ * cc -o lmcconfig lmcconfig.c
+ * Install the executable program in /usr/local/sbin/lmcconfig.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <unistd.h>
+#if defined(NETGRAPH)
+# include <netgraph.h>
+#endif
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+
+#include <dev/lmc/if_lmc.h>
+
+/* program global variables */
+char * progname; /* name of this program */
+char * ifname; /* interface name */
+int fdcs; /* ifnet File Desc or ng Ctl Socket */
+struct status status; /* card status (read only) */
+struct config config; /* card configuration (read/write) */
+int netgraph = 0; /* non-zero if netgraph present */
+int summary = 0; /* print summary at end */
+int update = 0; /* update driver config */
+int verbose = 0; /* verbose output */
+u_int8_t checksum; /* gate array ucode file checksum */
+
+void usage()
+ {
+ fprintf(stderr, "Usage: %s interface [-abBcCdDeEfhiLmMpPsStTuUvVwWxXyYzZ?]\n", progname);
+ fprintf(stderr, "or\n");
+ fprintf(stderr, "Usage: %s interface -1 [-aABcdeEfFgiIlLpPstTuUvxX]\n", progname);
+ fprintf(stderr, "or\n");
+ fprintf(stderr, "Usage: %s interface -3 [-aABcdefFlLsSvV]\n\n", progname);
+ fprintf(stderr, "\tInterface is the interface name, e.g. '%s'\n", ifname);
+#if defined(NETGRAPH)
+ fprintf(stderr, "\tIf interface name ends with ':' then use netgraph\n");
+#endif
+ fprintf(stderr, "\t-1 following parameters apply to T1E1 cards\n");
+ fprintf(stderr, "\t-3 following parameters apply to T3 cards\n");
+ fprintf(stderr, "\t-a <number> Set Tx clock source, where:\n");
+ fprintf(stderr, "\t 1:modem Tx clk 2:int src 3:modem Rx Clk 4:ext conn\n");
+ fprintf(stderr, "\t-b Read and print bios rom addrs 0-255\n");
+ fprintf(stderr, "\t-B Write bios rom with address pattern\n");
+ fprintf(stderr, "\t-c Set 16-bit CRC (default)\n");
+ fprintf(stderr, "\t-C Set 32-bit CRC\n");
+ fprintf(stderr, "\t-d Clear driver DEBUG flag\n");
+ fprintf(stderr, "\t-D Set driver DEBUG flag (more log msgs)\n");
+ fprintf(stderr, "\t-e Set DTE mode (default)\n");
+ fprintf(stderr, "\t-E Set DCE mode\n");
+ fprintf(stderr, "\t-f <number> Set synth osc freq in bits/sec\n");
+ fprintf(stderr, "\t-F Set SPPP line protocol to Frame-Relay\n");
+ fprintf(stderr, "\t-h Help: this usage message\n");
+ fprintf(stderr, "\t-i Interface name (eg, lmc0)\n");
+ fprintf(stderr, "\t-L <number> Set loopback: 1:none 2:payload 3:line 4:other\n");
+ fprintf(stderr, "\t 5:inward 6:dual 16:Tulip 17:pins 18:LA/LL 19:LB/RL\n");
+ fprintf(stderr, "\t-m Read and print MII regs\n");
+ fprintf(stderr, "\t-M <addr> <data> Write MII reg\n");
+ fprintf(stderr, "\t-p Read and print PCI config regs\n");
+ fprintf(stderr, "\t-P <addr> <data> Write PCI config reg\n");
+ fprintf(stderr, "\t-s Read and print Tulip SROM\n");
+ fprintf(stderr, "\t-S <number> Initialize Tulip SROM\n");
+ fprintf(stderr, "\t-t Read and print Tulip Control/Status regs\n");
+ fprintf(stderr, "\t-T <addr> <data> Write Tulip Control/status reg\n");
+ fprintf(stderr, "\t-u Reset event counters\n");
+ fprintf(stderr, "\t-U Reset gate array\n");
+ fprintf(stderr, "\t-v Set verbose printout mode\n");
+ fprintf(stderr, "\t-V Print card configuration\n");
+ fprintf(stderr, "\t-w Load gate array from ROM\n");
+ fprintf(stderr, "\t-W <filename> Load gate array from file\n");
+ fprintf(stderr, "\t-x select RAWIP mode and bypass line protocols\n");
+ fprintf(stderr, "\t-X Select line protocols: SPPP, P2P or HDLC\n");
+ fprintf(stderr, "\t-y disable SPPP keep-alive packets\n");
+ fprintf(stderr, "\t-Y enable SPPP keep-alive packets\n");
+ fprintf(stderr, "\t-z Set SPPP line protocol to Cisco-HDLC\n");
+ fprintf(stderr, "\t-Z Set SPPP line protocol to PPP\n");
+
+ fprintf(stderr, "The -1 switch precedes T1/E1 commands.\n");
+ fprintf(stderr, "\t-a <y|b|a> Stop sending Yellow|Blue|AIS signal\n");
+ fprintf(stderr, "\t-A <y|b|a> Start sending Yellow|Blue}AIS signal\n");
+ fprintf(stderr, "\t-B <number> Send BOP msg 25 times\n");
+ fprintf(stderr, "\t-c <number> Set cable length in meters\n");
+ fprintf(stderr, "\t-d Print status of T1 DSU/CSU\n");
+ fprintf(stderr, "\t-e <number> Set framing format, where:\n");
+ fprintf(stderr, "\t 27:T1-ESF 9:T1-SF 0:E1-FAS 8:E1-FAS+CRC\n");
+ fprintf(stderr, "\t 16:E1-FAS+CAS 24:E1-FAS+CRC+CAS 32:E1-NO-FRAMING\n");
+ fprintf(stderr, "\t-E <32-bit hex number> 1 activates a channel and 0 deactivates it.\n");
+ fprintf(stderr, "\t Use this to config a link in fractional T1/E1 mode\n");
+ fprintf(stderr, "\t-f Read and print Framer/LIU registers\n");
+ fprintf(stderr, "\t-F <addr> <data> Write Framer/LIU register\n");
+ fprintf(stderr, "\t-g <number> Set receiver gain, where:\n");
+ fprintf(stderr, "\t 0:short range 1:medium range\n");
+ fprintf(stderr, "\t 2:long range 3:extended range\n");
+ fprintf(stderr, "\t 4:auto-set based on cable length\n");
+ fprintf(stderr, "\t-i Send 'CSU Loop Down' inband msg\n");
+ fprintf(stderr, "\t-I Send 'CSU Loop Up' inband msg\n");
+ fprintf(stderr, "\t-l Send 'Line Loop Down' BOP msg\n");
+ fprintf(stderr, "\t-L Send 'Line Loop Up' BOP msg\n");
+ fprintf(stderr, "\t-p Send 'Payload Loop Down' BOP msg\n");
+ fprintf(stderr, "\t-P Send 'Payload Loop Up' BOP msg\n");
+ fprintf(stderr, "\t-s Print status of T1 DSU/CSU\n");
+ fprintf(stderr, "\t-t Stop sending test pattern\n");
+ fprintf(stderr, "\t-T <number> Start sending test pattern, where:\n");
+ fprintf(stderr, "\t 0:unframed 2^11 1:unframed 2^15\n");
+ fprintf(stderr, "\t 2:unframed 2^20 3:unframed 2^23\n");
+ fprintf(stderr, "\t 4:unframed 2^11 w/ZS 5:unframed 2^15 w/ZS\n");
+ fprintf(stderr, "\t 6:unframed QRSS 7:unframed 2^23 w/ZS\n");
+ fprintf(stderr, "\t 8: framed 2^11 9: framed 2^15\n");
+ fprintf(stderr, "\t 10: framed 2^20 11: framed 2^23\n");
+ fprintf(stderr, "\t 12: framed 2^11 w/ZS 13: framed 2^15 w/ZS\n");
+ fprintf(stderr, "\t 14: framed QRSS 15: framed 2^23 w/ZS\n");
+ fprintf(stderr, "\t-u <number> Set transmitter pulse shape, where:\n");
+ fprintf(stderr, "\t 0:T1-DSX 0-40m 1:T1-DSX 40-80m\n");
+ fprintf(stderr, "\t 2:T1-DSX 80-120m 3:T1-DSX 120-160m\n");
+ fprintf(stderr, "\t 4:T1-DSX 160-200m 5:E1-G.703 75ohm coax\n");
+ fprintf(stderr, "\t 6:E1-G.703 120ohm TP 7:T1-CSU Long range\n");
+ fprintf(stderr, "\t 8:auto-set based on cable length (T1 only)\n");
+ fprintf(stderr, "\t-U <number> Set line build out where:\n");
+ fprintf(stderr, "\t 0:0dB 1:7.5dB 2:15dB 3:22.5dB\n");
+ fprintf(stderr, "\t 4:auto-set based on cable length\n");
+ fprintf(stderr, "\t-v Set verbose printout mode\n");
+ fprintf(stderr, "\t-x disable Transmitter outputs\n");
+ fprintf(stderr, "\t-X enable Transmitter outputs\n");
+
+ fprintf(stderr, "The -3 switch precedes T3 commands.\n");
+ fprintf(stderr, "\t-a <y|b|a|i> Stop sending Yellow|Blue|AIS|Idle signal\n");
+ fprintf(stderr, "\t-A <y|b|a|i> Start sending Yellow|Blue|AIS|Idle signal\n");
+ fprintf(stderr, "\t-B <bopcode> Send BOP msg 10 times\n");
+ fprintf(stderr, "\t-c <number> Set cable length in meters\n");
+ fprintf(stderr, "\t-d Print status of T3 DSU/CSU\n");
+ fprintf(stderr, "\t-e <number> Set T3 frame format, where:\n");
+ fprintf(stderr, "\t 100:C-Bit Parity 101:M13\n");
+ fprintf(stderr, "\t-f Read and print Framer registers\n");
+ fprintf(stderr, "\t-F <addr> <data> Write Framer register\n");
+ fprintf(stderr, "\t-l Send 'Line Loop Down' BOP msg\n");
+ fprintf(stderr, "\t-L Send 'Line Loop Up' BOP msg\n");
+ fprintf(stderr, "\t-s Print status of T3 DSU/CSU\n");
+ fprintf(stderr, "\t-S <number> Set DS3 scrambler mode, where:\n");
+ fprintf(stderr, "\t 1:OFF 2:DigitalLink|Kentrox 3:Larse\n");
+ fprintf(stderr, "\t-v Set verbose printout mode\n");
+ fprintf(stderr, "\t-V <number> Write to T3 VCXO freq control DAC\n");
+ }
+
+void call_driver(unsigned long cmd, struct iohdr *iohdr)
+ {
+ int error = 0;
+
+ strncpy(iohdr->ifname, ifname, sizeof(iohdr->ifname));
+ iohdr->cookie = NGM_LMC_COOKIE;
+ iohdr->iohdr = iohdr;
+
+ /* Exchange data with a running device driver. */
+#if defined(NETGRAPH)
+ if (netgraph)
+ {
+ NgSendMsg(fdcs, ifname, NGM_LMC_COOKIE, cmd, iohdr, IOCPARM_LEN(cmd));
+ if (cmd & IOC_OUT)
+ {
+ int replen = sizeof(struct ng_mesg) + IOCPARM_LEN(cmd);
+ char rep[replen]; /* storage for the reply */
+ struct ng_mesg *reply = (struct ng_mesg *)rep;
+ int rl = NgRecvMsg(fdcs, reply, replen, NULL);
+ if (rl == replen)
+ bcopy(&reply->data, iohdr, IOCPARM_LEN(cmd));
+ else
+ {
+ fprintf(stderr, "%s: NgRecvMsg returned %d bytes, expected %d\n",
+ progname, rl, replen);
+ exit(1);
+ }
+ }
+ }
+ else
+#endif
+ {
+ if ((error = ioctl(fdcs, cmd, (caddr_t)iohdr)) < 0)
+ {
+ fprintf(stderr, "%s: ioctl() returned error code %d: %s\n",
+ progname, errno, strerror(errno));
+ if (errno == ENETDOWN)
+ printf("Type: 'ifconfig %s up' then try again.\n", ifname);
+ exit(1);
+ }
+ }
+
+ if (iohdr->cookie != NGM_LMC_COOKIE)
+ {
+ fprintf(stderr, "%s: cookie = 0x%08X, expected 0x%08X\n", progname, iohdr->cookie, NGM_LMC_COOKIE);
+ fprintf(stderr, "%s: This version of %s is incompatible with the device driver\n", progname, progname);
+ exit(1);
+ }
+ }
+
+u_int32_t read_pci_config(u_int8_t addr)
+ {
+ struct ioctl ioctl;
+
+ ioctl.iohdr.direction = DIR_IOWR;
+ ioctl.iohdr.length = sizeof(struct ioctl);
+ ioctl.cmd = IOCTL_RW_PCI;
+ ioctl.address = addr;
+
+ call_driver(LMCIOCREAD, &ioctl.iohdr);
+
+ return ioctl.data;
+ }
+
+void write_pci_config(u_int8_t addr, u_int32_t data)
+ {
+ struct ioctl ioctl;
+
+ ioctl.iohdr.direction = DIR_IOW;
+ ioctl.iohdr.length = sizeof(struct ioctl);
+ ioctl.cmd = IOCTL_RW_PCI;
+ ioctl.address = addr;
+ ioctl.data = data;
+
+ call_driver(LMCIOCWRITE, &ioctl.iohdr);
+ }
+
+u_int32_t read_csr(u_int8_t addr)
+ {
+ struct ioctl ioctl;
+
+ ioctl.iohdr.direction = DIR_IOWR;
+ ioctl.iohdr.length = sizeof(struct ioctl);
+ ioctl.cmd = IOCTL_RW_CSR;
+ ioctl.address = addr;
+
+ call_driver(LMCIOCREAD, &ioctl.iohdr);
+
+ return ioctl.data;
+ }
+
+void write_csr(u_int8_t addr, u_int32_t data)
+ {
+ struct ioctl ioctl;
+
+ ioctl.iohdr.direction = DIR_IOW;
+ ioctl.iohdr.length = sizeof(struct ioctl);
+ ioctl.cmd = IOCTL_RW_CSR;
+ ioctl.address = addr;
+ ioctl.data = data;
+
+ call_driver(LMCIOCWRITE, &ioctl.iohdr);
+ }
+
+u_int16_t read_srom(u_int8_t addr)
+ {
+ struct ioctl ioctl;
+
+ ioctl.iohdr.direction = DIR_IOWR;
+ ioctl.iohdr.length = sizeof(struct ioctl);
+ ioctl.cmd = IOCTL_RW_SROM;
+ ioctl.address = addr;
+
+ call_driver(LMCIOCREAD, &ioctl.iohdr);
+
+ return ioctl.data;
+ }
+
+void write_srom(u_int8_t addr, u_int16_t data)
+ {
+ struct ioctl ioctl;
+
+ ioctl.iohdr.direction = DIR_IOW;
+ ioctl.iohdr.length = sizeof(struct ioctl);
+ ioctl.cmd = IOCTL_RW_SROM;
+ ioctl.address = addr;
+ ioctl.data = data;
+
+ call_driver(LMCIOCWRITE, &ioctl.iohdr);
+ }
+
+u_int8_t read_bios_rom(u_int32_t addr)
+ {
+ struct ioctl ioctl;
+
+ ioctl.iohdr.direction = DIR_IOWR;
+ ioctl.iohdr.length = sizeof(struct ioctl);
+ ioctl.cmd = IOCTL_RW_BIOS;
+ ioctl.address = addr;
+
+ call_driver(LMCIOCREAD, &ioctl.iohdr);
+
+ return ioctl.data;
+ }
+
+void write_bios_rom(u_int32_t addr, u_int8_t data)
+ {
+ struct ioctl ioctl;
+
+ ioctl.iohdr.direction = DIR_IOW;
+ ioctl.iohdr.length = sizeof(struct ioctl);
+ ioctl.cmd = IOCTL_RW_BIOS;
+ ioctl.address = addr;
+ ioctl.data = data;
+
+ call_driver(LMCIOCWRITE, &ioctl.iohdr);
+ }
+
+u_int16_t read_mii(u_int8_t addr)
+ {
+ struct ioctl ioctl;
+
+ ioctl.iohdr.direction = DIR_IOWR;
+ ioctl.iohdr.length = sizeof(struct ioctl);
+ ioctl.cmd = IOCTL_RW_MII;
+ ioctl.address = addr;
+
+ call_driver(LMCIOCREAD, &ioctl.iohdr);
+
+ return ioctl.data;
+ }
+
+void write_mii(u_int8_t addr, u_int16_t data)
+ {
+ struct ioctl ioctl;
+
+ ioctl.iohdr.direction = DIR_IOW;
+ ioctl.iohdr.length = sizeof(struct ioctl);
+ ioctl.cmd = IOCTL_RW_MII;
+ ioctl.address = addr;
+ ioctl.data = data;
+
+ call_driver(LMCIOCWRITE, &ioctl.iohdr);
+ }
+
+unsigned char read_framer(u_int16_t addr)
+ {
+ struct ioctl ioctl;
+
+ ioctl.iohdr.direction = DIR_IOWR;
+ ioctl.iohdr.length = sizeof(struct ioctl);
+ ioctl.cmd = IOCTL_RW_FRAME;
+ ioctl.address = addr;
+
+ call_driver(LMCIOCREAD, &ioctl.iohdr);
+
+ return ioctl.data;
+ }
+
+void write_framer(u_int16_t addr, u_int8_t data)
+ {
+ struct ioctl ioctl;
+
+ ioctl.iohdr.direction = DIR_IOW;
+ ioctl.iohdr.length = sizeof(struct ioctl);
+ ioctl.cmd = IOCTL_RW_FRAME;
+ ioctl.address = addr;
+ ioctl.data = data;
+
+ call_driver(LMCIOCWRITE, &ioctl.iohdr);
+ }
+
+void write_synth(struct synth synth)
+ {
+ struct ioctl ioctl;
+
+ ioctl.iohdr.direction = DIR_IOW;
+ ioctl.iohdr.length = sizeof(struct ioctl);
+ ioctl.cmd = IOCTL_WO_SYNTH;
+ bcopy(&synth, &ioctl.data, sizeof(synth));
+
+ call_driver(LMCIOCWRITE, &ioctl.iohdr);
+ }
+
+void write_dac(u_int16_t data)
+ {
+ struct ioctl ioctl;
+
+ ioctl.iohdr.direction = DIR_IOW;
+ ioctl.iohdr.length = sizeof(struct ioctl);
+ ioctl.cmd = IOCTL_WO_DAC;
+ ioctl.data = data;
+
+ call_driver(LMCIOCWRITE, &ioctl.iohdr);
+ }
+
+void reset_xilinx()
+ {
+ struct ioctl ioctl;
+
+ ioctl.iohdr.direction = DIR_IOWR;
+ ioctl.iohdr.length = sizeof(struct ioctl);
+ ioctl.cmd = IOCTL_XILINX_RESET;
+
+ call_driver(LMCIOCTL, &ioctl.iohdr);
+ }
+
+void load_xilinx_from_rom()
+ {
+ struct ioctl ioctl;
+
+ ioctl.iohdr.direction = DIR_IOWR;
+ ioctl.iohdr.length = sizeof(struct ioctl);
+ ioctl.cmd = IOCTL_XILINX_ROM;
+
+ call_driver(LMCIOCTL, &ioctl.iohdr);
+ }
+
+void load_xilinx_from_file(char *ucode, u_int32_t len)
+ {
+ struct ioctl ioctl;
+
+ ioctl.iohdr.direction = DIR_IOWR;
+ ioctl.iohdr.length = sizeof(struct ioctl);
+ ioctl.cmd = IOCTL_XILINX_FILE;
+ ioctl.data = len;
+ ioctl.ucode = ucode;
+
+ call_driver(LMCIOCTL, &ioctl.iohdr);
+ }
+
+void ioctl_snmp_send(u_int32_t send)
+ {
+ struct ioctl ioctl;
+
+ ioctl.iohdr.direction = DIR_IOWR;
+ ioctl.iohdr.length = sizeof(struct ioctl);
+ ioctl.cmd = IOCTL_SNMP_SEND;
+ ioctl.data = send;
+
+ call_driver(LMCIOCTL, &ioctl.iohdr);
+ }
+
+void ioctl_snmp_loop(u_int32_t loop)
+ {
+ struct ioctl ioctl;
+
+ ioctl.iohdr.direction = DIR_IOWR;
+ ioctl.iohdr.length = sizeof(struct ioctl);
+ ioctl.cmd = IOCTL_SNMP_LOOP;
+ ioctl.data = loop;
+
+ call_driver(LMCIOCTL, &ioctl.iohdr);
+ }
+
+void ioctl_reset_cntrs()
+ {
+ struct ioctl ioctl;
+
+ ioctl.iohdr.direction = DIR_IOWR;
+ ioctl.iohdr.length = sizeof(struct ioctl);
+ ioctl.cmd = IOCTL_RESET_CNTRS;
+
+ call_driver(LMCIOCTL, &ioctl.iohdr);
+ }
+
+void ioctl_read_config()
+ {
+ config.iohdr.direction = DIR_IOWR;
+ config.iohdr.length = sizeof(struct config);
+
+ call_driver(LMCIOCGCFG, &config.iohdr);
+ }
+
+void ioctl_write_config()
+ {
+ config.iohdr.direction = DIR_IOW;
+ config.iohdr.length = sizeof(struct config);
+
+ call_driver(LMCIOCSCFG, &config.iohdr);
+ }
+
+void ioctl_read_status()
+ {
+ status.iohdr.direction = DIR_IOWR;
+ status.iohdr.length = sizeof(struct status);
+
+ call_driver(LMCIOCGSTAT, &status.iohdr);
+ }
+
+void print_card_name()
+ {
+ printf("Card name:\t\t%s\n", ifname);
+ }
+
+void print_card_type()
+ {
+ printf("Card type:\t\t");
+ switch(status.card_type)
+ {
+ case TLP_CSID_HSSI:
+ printf("HSSI (lmc5200)\n");
+ break;
+ case TLP_CSID_T3:
+ printf("T3 (lmc5245)\n");
+ break;
+ case TLP_CSID_SSI:
+ printf("SSI (lmc1000)\n");
+ break;
+ case TLP_CSID_T1E1:
+ printf("T1E1 (lmc1200)\n");
+ break;
+ case TLP_CSID_HSSIc:
+ printf("HSSI (lmc5200C)\n");
+ break;
+ default:
+ printf("unknown card_type: %d\n", status.card_type);
+ break;
+ }
+ }
+
+void print_status()
+ {
+ char *status_string;
+
+ if (status.oper_status == STATUS_UP)
+ status_string = "Up";
+ else if (status.oper_status == STATUS_DOWN)
+ status_string = "Down";
+ else if (status.oper_status == STATUS_TEST)
+ status_string = "Test";
+ else
+ status_string = "Unknown";
+ printf("Link status:\t\t%s\n", status_string);
+ }
+
+void print_tx_speed()
+ {
+ printf("Tx Speed:\t\t%u\n", status.tx_speed);
+ }
+
+void print_debug()
+ {
+ if (config.debug != 0)
+ printf("Debug:\t\t\t%s\n", "On");
+ }
+
+void print_line_prot()
+ {
+ char *on = "On", *off = "Off";
+
+ printf("Line Prot/Pkg:\t\t");
+ switch (status.line_prot)
+ {
+ case 0:
+ printf("NotSet/");
+ break;
+ case PROT_PPP:
+ printf("PPP/");
+ break;
+ case PROT_C_HDLC:
+ printf("Cisco-HDLC/");
+ break;
+ case PROT_FRM_RLY:
+ printf("Frame-Relay/");
+ break;
+ case PROT_IP_HDLC:
+ printf("IP-in-HDLC/");
+ break;
+ case PROT_ETH_HDLC:
+ printf("Ether-in-HDLC/");
+ break;
+ case PROT_X25:
+ printf("X25+LAPB/");
+ break;
+ default:
+ printf("unknown line_prot: %d/", status.line_prot);
+ break;
+ }
+
+ switch (status.line_pkg)
+ {
+ case 0:
+ printf("NotSet\n");
+ break;
+ case PKG_RAWIP:
+ printf("Driver\n");
+ break;
+ case PKG_NG:
+ printf("Netgraph\n");
+ break;
+ case PKG_GEN_HDLC:
+ printf("GenHDLC\n");
+ break;
+ case PKG_SPPP:
+ printf("SPPP\n");
+ break;
+ case PKG_P2P:
+ printf("P2P\n");
+ break;
+ default:
+ printf("unknown line_pkg: %d\n", status.line_pkg);
+ break;
+ }
+
+ if (status.line_pkg == PKG_SPPP)
+ printf("SPPP Keep-alives:\t%s\n",
+ config.keep_alive ? on : off);
+ }
+
+void print_crc_len()
+ {
+ printf("CRC length:\t\t");
+ if (config.crc_len == CFG_CRC_0)
+ printf("no CRC\n");
+ else if (config.crc_len == CFG_CRC_16)
+ printf("16 bits\n");
+ else if (config.crc_len == CFG_CRC_32)
+ printf("32 bits\n");
+ else
+ printf("bad crc_len: %d\n", config.crc_len);
+ }
+
+void print_loop_back()
+ {
+ printf("Loopback:\t\t");
+ switch (config.loop_back)
+ {
+ case CFG_LOOP_NONE:
+ printf("None\n");
+ break;
+ case CFG_LOOP_PAYLOAD:
+ printf("Outward thru framer (payload loop)\n");
+ break;
+ case CFG_LOOP_LINE:
+ printf("Outward thru line interface (line loop)\n");
+ break;
+ case CFG_LOOP_OTHER:
+ printf("Inward thru line interface\n");
+ break;
+ case CFG_LOOP_INWARD:
+ printf("Inward thru framer\n");
+ break;
+ case CFG_LOOP_DUAL:
+ printf("Inward & outward (dual loop)\n");
+ break;
+ case CFG_LOOP_TULIP:
+ printf("Inward thru Tulip chip\n");
+ break;
+ case CFG_LOOP_PINS:
+ printf("Inward thru drvrs/rcvrs\n");
+ break;
+ case CFG_LOOP_LL:
+ printf("LA/LL asserted\n");
+ break;
+ case CFG_LOOP_RL:
+ printf("LB/RL asserted\n");
+ break;
+ default:
+ printf("unknown loop_back: %d\n", config.loop_back);
+ break;
+ }
+ }
+
+void print_tx_clk_src()
+ {
+ printf("Tx Clk src:\t\t");
+ switch (config.tx_clk_src)
+ {
+ case CFG_CLKMUX_ST:
+ printf("Tx Clk from modem\n");
+ break;
+ case CFG_CLKMUX_INT:
+ printf("Internal source\n");
+ break;
+ case CFG_CLKMUX_RT:
+ printf("Rx Clk from modem (loop timed)\n");
+ break;
+ case CFG_CLKMUX_EXT:
+ printf("External connector\n");
+ break;
+ default:
+ printf("unknown tx_clk_src: %d\n", config.tx_clk_src);
+ break;
+ }
+ }
+
+void print_format()
+ {
+ printf("Format-Frame/Code:\t");
+ switch (config.format)
+ {
+ case CFG_FORMAT_T1SF:
+ printf("T1-SF/AMI\n");
+ break;
+ case CFG_FORMAT_T1ESF:
+ printf("T1-ESF/B8ZS\n");
+ break;
+ case CFG_FORMAT_E1FAS:
+ printf("E1-FAS/HDB3\n");
+ break;
+ case CFG_FORMAT_E1FASCRC:
+ printf("E1-FAS+CRC/HDB3\n");
+ break;
+ case CFG_FORMAT_E1FASCAS:
+ printf("E1-FAS+CAS/HDB3\n");
+ break;
+ case CFG_FORMAT_E1FASCRCCAS:
+ printf("E1-FAS+CRC+CAS/HDB3\n");
+ break;
+ case CFG_FORMAT_E1NONE:
+ printf("E1-NOFRAMING/HDB3\n");
+ break;
+ case CFG_FORMAT_T3CPAR:
+ printf("T3-CParity/B3ZS\n");
+ break;
+ case CFG_FORMAT_T3M13:
+ printf("T3-M13/B3ZS\n");
+ break;
+ default:
+ printf("unknown format: %d\n", config.format);
+ break;
+ }
+ }
+
+void print_dte_dce()
+ {
+ printf("DTE or DCE:\t\t");
+ switch(config.dte_dce)
+ {
+ case CFG_DTE:
+ printf("DTE (receiving TxClk)\n");
+ break;
+ case CFG_DCE:
+ printf("DCE (driving TxClk)\n");
+ break;
+ default:
+ printf("unknown dte_dce: %d\n", config.dte_dce);
+ break;
+ }
+ }
+
+void print_synth_freq()
+ {
+ double Fref = 20e6;
+ double Fout, Fvco;
+
+ /* decode the synthesizer params */
+ Fvco = (Fref * (config.synth.n<<(3*config.synth.v)))/config.synth.m;
+ Fout = Fvco / (1<<(config.synth.x+config.synth.r+config.synth.prescale));
+
+ printf("Synth freq:\t\t%.0f\n", Fout);
+ }
+
+void synth_freq(unsigned long target)
+ {
+ unsigned int n, m, v, x, r;
+ double Fout, Fvco, Ftarg;
+ double newdiff, olddiff;
+ double bestF=0.0, bestV=0.0;
+ unsigned prescale = (target < 50000) ? 9:4;
+
+ Ftarg = target<<prescale;
+ for (n=3; n<=127; n++)
+ for (m=3; m<=127; m++)
+ for (v=0; v<=1; v++)
+ for (x=0; x<=3; x++)
+ for (r=0; r<=3; r++)
+ {
+ Fvco = (SYNTH_FREF * (n<<(3*v)))/m;
+ if (Fvco < SYNTH_FMIN || Fvco > SYNTH_FMAX) continue;
+ Fout = Fvco / (1<<(x+r));
+ if (Fout >= Ftarg)
+ newdiff = Fout - Ftarg;
+ else
+ newdiff = Ftarg - Fout;
+ if (bestF >= Ftarg)
+ olddiff = bestF - Ftarg;
+ else
+ olddiff = Ftarg - bestF;
+ if ((newdiff < olddiff) ||
+ ((newdiff == olddiff) && (Fvco < bestV)))
+ {
+ config.synth.n = n;
+ config.synth.m = m;
+ config.synth.v = v;
+ config.synth.x = x;
+ config.synth.r = r;
+ config.synth.prescale = prescale;
+ bestF = Fout;
+ bestV = Fvco;
+ }
+ }
+#if 0
+ printf("Fbest=%.0f, Ftarg=%u, Fout=%.0f\n", bestF>>prescale, target, bestF);
+ printf("N=%u, M=%u, V=%u, X=%u, R=%u\n", config.synth.n,
+ config.synth.m, config.synth.v, config.synth.x, config.synth.r);
+#endif
+ }
+
+void print_cable_len()
+ {
+ printf("Cable length:\t\t%d meters\n", config.cable_len);
+ }
+
+void print_cable_type()
+ {
+ printf("Cable type:\t\t");
+ if (status.cable_type > 7)
+ printf("unknown cable_type: %d\n", status.cable_type);
+ else
+ printf("%s\n", ssi_cables[status.cable_type]);
+ }
+
+void print_time_slots()
+ {
+ printf("TimeSlot [31-0]:\t0x%08X\n", config.time_slots);
+ }
+
+void print_scrambler()
+ {
+ printf("Scrambler:\t\t");
+ if (config.scrambler == CFG_SCRAM_OFF)
+ printf("off\n");
+ else if (config.scrambler == CFG_SCRAM_DL_KEN)
+ printf("DigLink/Kentrox: X^43+1\n");
+ else if (config.scrambler == CFG_SCRAM_LARS)
+ printf("Larse: X^20+X^17+1 w/28ZS\n");
+ else
+ printf("unknown scrambler: %d\n", config.scrambler);
+ }
+
+double vga_dbs(u_int8_t vga)
+ {
+ if (vga < 0x0F) return 0.0;
+ if ((vga >= 0x0F) && (vga <= 0x1B)) return 0.0 + 0.77 * (vga - 0x0F);
+ if ((vga >= 0x1C) && (vga <= 0x33)) return 10.0 + 1.25 * (vga - 0x1C);
+ if ((vga >= 0x34) && (vga <= 0x39)) return 40.0 + 1.67 * (vga - 0x34);
+ if ((vga >= 0x3A) && (vga <= 0x3F)) return 50.0 + 2.80 * (vga - 0x3A);
+ if (vga > 0x3F) return 64.0;
+ return 0.0; /* suppress compiler warning */
+ }
+
+void print_rx_gain()
+ {
+ printf("Rx gain max:\t\t");
+
+ if (config.rx_gain == CFG_GAIN_AUTO)
+ printf("auto-set to %02.1f dB\n",
+ vga_dbs(read_framer(Bt8370_VGA_MAX) & 0x3F));
+ else
+ printf("up to %02.1f dB\n", vga_dbs(config.rx_gain));
+ }
+
+void print_tx_lbo()
+ {
+ u_int8_t saved_lbo = config.tx_lbo;
+
+ printf("LBO = ");
+ if (config.tx_lbo == CFG_LBO_AUTO)
+ {
+ config.tx_lbo = read_framer(Bt8370_TLIU_CR) & 0x30;
+ printf("auto-set to ");
+ }
+
+ switch (config.tx_lbo)
+ {
+ case CFG_LBO_0DB:
+ printf("0 dB\n");
+ break;
+ case CFG_LBO_7DB:
+ printf("7.5 dB\n");
+ break;
+ case CFG_LBO_15DB:
+ printf("15 dB\n");
+ break;
+ case CFG_LBO_22DB:
+ printf("22.5 dB\n");
+ break;
+ default:
+ printf("unknown tx_lbo: %d\n", config.tx_lbo);
+ break;
+ }
+
+ if (saved_lbo == CFG_LBO_AUTO)
+ config.tx_lbo = saved_lbo;
+ }
+
+void print_tx_pulse()
+ {
+ u_int8_t saved_pulse = config.tx_pulse;
+
+ printf("Tx pulse shape:\t\t");
+ if (config.tx_pulse == CFG_PULSE_AUTO)
+ {
+ config.tx_pulse = read_framer(Bt8370_TLIU_CR) & 0x0E;
+ printf("auto-set to ");
+ }
+
+ switch (config.tx_pulse)
+ {
+ case CFG_PULSE_T1DSX0:
+ printf("T1-DSX: 0 to 40 meters\n");
+ break;
+ case CFG_PULSE_T1DSX1:
+ printf("T1-DSX: 40 to 80 meters\n");
+ break;
+ case CFG_PULSE_T1DSX2:
+ printf("T1-DSX: 80 to 120 meters\n");
+ break;
+ case CFG_PULSE_T1DSX3:
+ printf("T1-DSX: 120 to 160 meters\n");
+ break;
+ case CFG_PULSE_T1DSX4:
+ printf("T1-DSX: 160 to 200 meters\n");
+ break;
+ case CFG_PULSE_E1COAX:
+ printf("E1: Twin Coax\n");
+ break;
+ case CFG_PULSE_E1TWIST:
+ printf("E1: Twisted Pairs\n");
+ break;
+ case CFG_PULSE_T1CSU:
+ printf("T1-CSU; ");
+ print_tx_lbo();
+ break;
+ default:
+ printf("unknown tx_pulse: %d\n", config.tx_pulse);
+ break;
+ }
+
+ if (saved_pulse == CFG_PULSE_AUTO)
+ config.tx_pulse = saved_pulse;
+ }
+
+void print_ssi_sigs()
+ {
+ u_int32_t mii16 = status.snmp.ssi.sigs;
+ char *on = "On", *off = "Off";
+
+ printf("Modem signals:\t\tDTR=%s DSR=%s RTS=%s CTS=%s\n",
+ (mii16 & MII16_SSI_DTR) ? on : off,
+ (mii16 & MII16_SSI_DSR) ? on : off,
+ (mii16 & MII16_SSI_RTS) ? on : off,
+ (mii16 & MII16_SSI_CTS) ? on : off);
+ printf("Modem signals:\t\tDCD=%s RI=%s LL=%s RL=%s TM=%s\n",
+ (mii16 & MII16_SSI_DCD) ? on : off,
+ (mii16 & MII16_SSI_RI) ? on : off,
+ (mii16 & MII16_SSI_LL) ? on : off,
+ (mii16 & MII16_SSI_RL) ? on : off,
+ (mii16 & MII16_SSI_TM) ? on : off);
+ }
+
+void print_hssi_sigs()
+ {
+ u_int32_t mii16 = status.snmp.hssi.sigs;
+ char *on = "On", *off = "Off";
+
+ printf("Modem signals:\t\tTA=%s CA=%s\n",
+ (mii16 & MII16_HSSI_TA) ? on : off,
+ (mii16 & MII16_HSSI_CA) ? on : off);
+ printf("Modem signals:\t\tLA=%s LB=%s LC=%s TM=%s\n",
+ (mii16 & MII16_HSSI_LA) ? on : off,
+ (mii16 & MII16_HSSI_LB) ? on : off,
+ (mii16 & MII16_HSSI_LC) ? on : off,
+ (mii16 & MII16_HSSI_TM) ? on : off);
+ }
+
+void print_events()
+ {
+ char *time;
+ struct timeval tv;
+ struct timezone tz;
+
+ gettimeofday(&tv, &tz);
+ time = (char *)ctime((time_t *)&tv);
+ printf("Current time:\t\t%s", time);
+ if (status.cntrs.reset_time.tv_sec < 1000)
+ time = "Never\n";
+ else
+ time = (char *)ctime((time_t *)&status.cntrs.reset_time.tv_sec);
+ printf("Cntrs reset:\t\t%s", time);
+
+ if (status.cntrs.ibytes) printf("Rx bytes:\t\t%qu\n", status.cntrs.ibytes);
+ if (status.cntrs.obytes) printf("Tx bytes:\t\t%qu\n", status.cntrs.obytes);
+ if (status.cntrs.ipackets) printf("Rx packets:\t\t%qu\n", status.cntrs.ipackets);
+ if (status.cntrs.opackets) printf("Tx packets:\t\t%qu\n", status.cntrs.opackets);
+ if (status.cntrs.ierrors) printf("Rx errors:\t\t%u\n", status.cntrs.ierrors);
+ if (status.cntrs.oerrors) printf("Tx errors:\t\t%u\n", status.cntrs.oerrors);
+ if (status.cntrs.idiscards) printf("Rx discards:\t\t%u\n", status.cntrs.idiscards);
+ if (status.cntrs.odiscards) printf("Tx discards:\t\t%u\n", status.cntrs.odiscards);
+ if (status.cntrs.fifo_over) printf("Rx fifo overruns:\t%u\n", status.cntrs.fifo_over);
+ if (status.cntrs.fifo_under) printf("Tx fifo underruns:\t%u\n", status.cntrs.fifo_under);
+ if (status.cntrs.missed) printf("Rx missed:\t\t%u\n", status.cntrs.missed);
+ if (status.cntrs.overruns) printf("Rx overruns:\t\t%u\n", status.cntrs.overruns);
+ if (status.cntrs.fdl_pkts) printf("Rx FDL pkts:\t\t%u\n", status.cntrs.fdl_pkts);
+ if (status.cntrs.crc_errs) printf("Rx CRC:\t\t\t%u\n", status.cntrs.crc_errs);
+ if (status.cntrs.lcv_errs) printf("Rx line code:\t\t%u\n", status.cntrs.lcv_errs);
+ if (status.cntrs.frm_errs) printf("Rx F-bits:\t\t%u\n", status.cntrs.frm_errs);
+ if (status.cntrs.febe_errs) printf("Rx FEBE:\t\t%u\n", status.cntrs.febe_errs);
+ if (status.cntrs.par_errs) printf("Rx P-parity:\t\t%u\n", status.cntrs.par_errs);
+ if (status.cntrs.cpar_errs) printf("Rx C-parity:\t\t%u\n", status.cntrs.cpar_errs);
+ if (status.cntrs.mfrm_errs) printf("Rx M-bits:\t\t%u\n", status.cntrs.mfrm_errs);
+ if (config.debug)
+ { /* These events are hard to explain and may worry users, */
+ if (status.cntrs.rxdma) printf("Rx no buffs:\t\t%u\n", status.cntrs.rxdma);
+ if (status.cntrs.txdma) printf("Tx no descs:\t\t%u\n", status.cntrs.txdma);
+ if (status.cntrs.lck_watch) printf("Lck watch:\t\t%u\n", status.cntrs.lck_watch);
+ if (status.cntrs.lck_ioctl) printf("Lck ioctl:\t\t%u\n", status.cntrs.lck_ioctl);
+ if (status.cntrs.lck_intr) printf("Lck intr:\t\t%u\n", status.cntrs.lck_intr);
+ }
+ }
+
+void print_summary()
+ {
+ switch(status.card_type)
+ {
+ case TLP_CSID_HSSI:
+ {
+ print_card_name();
+ print_card_type();
+ print_debug();
+ print_status();
+ print_tx_speed();
+ print_line_prot();
+ print_crc_len();
+ print_loop_back();
+ print_tx_clk_src();
+ print_hssi_sigs();
+ print_events();
+ break;
+ }
+ case TLP_CSID_T3:
+ {
+ print_card_name();
+ print_card_type();
+ print_debug();
+ print_status();
+ print_tx_speed();
+ print_line_prot();
+ print_crc_len();
+ print_loop_back();
+ print_format();
+ print_cable_len();
+ print_scrambler();
+ print_events();
+ break;
+ }
+ case TLP_CSID_SSI:
+ {
+ print_card_name();
+ print_card_type();
+ print_debug();
+ print_status();
+ print_tx_speed();
+ print_line_prot();
+ print_crc_len();
+ print_loop_back();
+ print_dte_dce();
+ print_synth_freq();
+ print_cable_type();
+ print_ssi_sigs();
+ print_events();
+ break;
+ }
+ case TLP_CSID_T1E1:
+ {
+ print_card_name();
+ print_card_type();
+ print_debug();
+ print_status();
+ print_tx_speed();
+ print_line_prot();
+ print_crc_len();
+ print_loop_back();
+ print_tx_clk_src();
+ print_format();
+ print_time_slots();
+ print_cable_len();
+ print_tx_pulse();
+ print_rx_gain();
+ print_events();
+ break;
+ }
+ case TLP_CSID_HSSIc:
+ {
+ print_card_name();
+ print_card_type();
+ print_debug();
+ print_status();
+ print_line_prot();
+ print_tx_speed();
+ print_crc_len();
+ print_loop_back();
+ print_tx_clk_src();
+ print_dte_dce();
+ print_synth_freq();
+ print_hssi_sigs();
+ print_events();
+ break;
+ }
+ default:
+ {
+ printf("%s: Unknown card type: %d\n", ifname, status.card_type);
+ break;
+ }
+ }
+ }
+
+char *print_t3_bop(int bop_code)
+ {
+ switch(bop_code)
+ {
+ case 0x00:
+ return "far end LOF";
+ case 0x0E:
+ return "far end LOS";
+ case 0x16:
+ return "far end AIS";
+ case 0x1A:
+ return "far end IDL";
+ case 0x07:
+ return "Line Loopback activate";
+ case 0x1C:
+ return "Line Loopback deactivate";
+ case 0x1B:
+ return "Entire DS3 line";
+ default:
+ return "Unknown BOP code";
+ }
+ }
+
+void print_t3_snmp()
+ {
+ printf("SNMP performance data:\n");
+ printf(" LCV=%d", status.snmp.t3.lcv);
+ printf(" LOS=%d", (status.snmp.t3.line & TLINE_LOS) ? 1 : 0);
+ printf(" PCV=%d", status.snmp.t3.pcv);
+ printf(" CCV=%d", status.snmp.t3.ccv);
+ printf(" AIS=%d", (status.snmp.t3.line & TLINE_RX_AIS) ? 1 : 0);
+ printf(" SEF=%d", (status.snmp.t3.line & T1LINE_SEF) ? 1 : 0);
+ printf(" OOF=%d", (status.snmp.t3.line & TLINE_LOF) ? 1 : 0);
+ printf(" FEBE=%d", status.snmp.t3.febe);
+ printf(" RAI=%d", (status.snmp.t3.line & TLINE_RX_RAI) ? 1 : 0);
+ printf("\n");
+ }
+
+void print_t3_dsu()
+ {
+ char *no = "No", *yes = "Yes";
+ u_int16_t mii16 = read_mii(16);
+ u_int8_t ctl1 = read_framer(T3CSR_CTL1);
+ u_int8_t ctl8 = read_framer(T3CSR_CTL8);
+ u_int8_t stat9 = read_framer(T3CSR_STAT9);
+ u_int8_t ctl12 = read_framer(T3CSR_CTL12);
+ u_int8_t stat16 = read_framer(T3CSR_STAT16);
+
+ printf("Framing: \t\t%s\n", ctl1 & CTL1_M13MODE ? "M13" : "CPAR");
+ print_tx_speed();
+ printf("Scrambler: \t\t%s\n", mii16 & MII16_DS3_SCRAM ? yes : no);
+ printf("Scram poly: \t\t%s\n", mii16 & MII16_DS3_POLY ? "X^20" : "X^43");
+ printf("Cable length \t\t%s\n", mii16 & MII16_DS3_ZERO ? "Short" : "Long");
+ printf("Line loop: \t\t%s\n", mii16 & MII16_DS3_LNLBK ? yes : no);
+ printf("Payload loop: \t\t%s\n", ctl12 & CTL12_RTPLOOP ? yes : no);
+ printf("Frame loop: \t\t%s\n", ctl1 & CTL1_3LOOP ? yes : no);
+ printf("Host loop: \t\t%s\n", mii16 & MII16_DS3_TRLBK ? yes : no);
+ printf("Transmit RAI: \t\t%s\n", ctl1 & CTL1_XTX ? no : yes);
+ printf("Receive RAI \t\t%s\n", stat16 & STAT16_XERR ? yes : no);
+ printf("Transmit AIS: \t\t%s\n", ctl1 & CTL1_TXAIS ? yes : no);
+ printf("Receive AIS: \t\t%s\n", stat16 & STAT16_RAIS ? yes : no);
+ printf("Transmit IDLE: \t\t%s\n", ctl1 & CTL1_TXIDL ? yes : no);
+ printf("Receive IDLE: \t\t%s\n", stat16 & STAT16_RIDL ? yes : no);
+ printf("Transmit BLUE: \t\t%s\n", ctl8 & CTL8_TBLU ? yes : no);
+ printf("Receive BLUE: \t\t%s\n", stat9 & STAT9_RBLU ? yes : no);
+ printf("Loss of Signal:\t\t%s\n", stat16 & STAT16_RLOS ? yes : no);
+ printf("Loss of Frame: \t\t%s\n", stat16 & STAT16_ROOF ? yes : no);
+ printf("Sev Err Frms: \t\t%s\n", stat16 & STAT16_SEF ? yes : no);
+ printf("Code errors: \t\t%d\n", read_framer(T3CSR_CVLO) + (read_framer(T3CSR_CVHI)<<8));
+ printf("C-Par errors: \t\t%d\n", read_framer(T3CSR_CERR));
+ printf("P-Par errors: \t\t%d\n", read_framer(T3CSR_PERR));
+ printf("F-Bit errors: \t\t%d\n", read_framer(T3CSR_FERR));
+ printf("M-Bit errors: \t\t%d\n", read_framer(T3CSR_MERR));
+ printf("FarEndBitErrs: \t\t%d\n", read_framer(T3CSR_FEBE));
+ printf("Last Tx FEAC msg:\t0x%02X (%s)\n",
+ read_framer(T3CSR_TX_FEAC) & 0x3F,
+ print_t3_bop(read_framer(T3CSR_TX_FEAC) & 0x3F));
+ printf("Last dbl FEAC msg;\t0x%02X (%s)\n",
+ read_framer(T3CSR_DBL_FEAC) & 0x3F,
+ print_t3_bop(read_framer(T3CSR_DBL_FEAC) & 0x3F));
+ printf("Last Rx FEAC msg:\t0x%02X (%s)\n",
+ read_framer(T3CSR_RX_FEAC) & 0x3F,
+ print_t3_bop(read_framer(T3CSR_RX_FEAC) & 0x3F));
+ print_t3_snmp();
+ }
+
+void t3_cmd(int argc, char **argv)
+ {
+ int ch;
+
+ while ((ch = getopt(argc, argv, "a:A:B:c:de:fF:lLsS:vV:")) != -1)
+ {
+ switch (ch)
+ {
+ case 'a': /* stop alarms */
+ {
+ switch (optarg[0])
+ {
+ case 'a': /* Stop sending AIS Signal */
+ {
+ write_mii(16,
+ read_mii(16) & ~MII16_DS3_FRAME);
+ write_framer(T3CSR_CTL1,
+ read_framer(T3CSR_CTL1) & ~CTL1_TXAIS);
+ if (verbose) printf("Stop sending Alarm Indication Signal (AIS)\n");
+ break;
+ }
+ case 'b': /* Stop sending Blue signal */
+ {
+ write_mii(16,
+ read_mii(16) & ~MII16_DS3_FRAME);
+ write_framer(T3CSR_CTL8,
+ read_framer(T3CSR_CTL8) & ~CTL8_TBLU);
+ if (verbose) printf("Stop sending Blue signal\n");
+ break;
+ }
+ case 'i': /* Stop sending IDLE signal */
+ {
+ write_framer(T3CSR_CTL1,
+ read_framer(T3CSR_CTL1) & ~CTL1_TXIDL);
+ if (verbose) printf("Stop sending IDLE signal\n");
+ break;
+ }
+ case 'y': /* Stop sending Yellow alarm */
+ {
+ write_framer(T3CSR_CTL1,
+ read_framer(T3CSR_CTL1) | CTL1_XTX);
+ if (verbose) printf("Stop sending Yellow alarm\n");
+ break;
+ }
+ default:
+ printf("Unknown alarm: %c\n", optarg[0]);
+ break;
+ }
+ break;
+ }
+ case 'A': /* start alarms */
+ {
+ switch (optarg[0])
+ {
+ case 'a': /* Start sending AIS Signal */
+ {
+ write_mii(16,
+ read_mii(16) | MII16_DS3_FRAME);
+ write_framer(T3CSR_CTL1,
+ read_framer(T3CSR_CTL1) | CTL1_TXAIS);
+ if (verbose) printf("Sending AIS signal (framed 1010..)\n");
+ break;
+ }
+ case 'b': /* Start sending Blue signal */
+ {
+ write_mii(16,
+ read_mii(16) | MII16_DS3_FRAME);
+ write_framer(T3CSR_CTL8,
+ read_framer(T3CSR_CTL8) | CTL8_TBLU);
+ if (verbose) printf("Sending Blue signal (unframed all 1s)\n");
+ break;
+ }
+ case 'i': /* Start sending IDLE signal */
+ {
+ write_framer(T3CSR_CTL1,
+ read_framer(T3CSR_CTL1) | CTL1_TXIDL);
+ if (verbose) printf("Sending IDLE signal (framed 1100..)\n");
+ break;
+ }
+ case 'y': /* Start sending Yellow alarm */
+ {
+ write_framer(T3CSR_CTL1,
+ read_framer(T3CSR_CTL1) & ~CTL1_XTX);
+ if (verbose) printf("Sending Yellow alarm (X-bits=0)\n");
+ break;
+ }
+ default:
+ printf("Unknown alarm: %c\n", optarg[0]);
+ break;
+ }
+ break;
+ }
+ case 'B': /* send BOP msg */
+ {
+ u_int8_t bop = strtoul(optarg, NULL, 0);
+ write_framer(T3CSR_TX_FEAC, 0xC0 + bop);
+ if (verbose) printf("Sent '0x%02X' BOP msg 10 times\n", bop);
+ break;
+ }
+ case 'c': /* set cable length */
+ {
+ config.cable_len = strtoul(optarg, NULL, 0);
+ if (verbose) print_cable_len();
+ update = 1;
+ break;
+ }
+ case 'd': /* DSU status */
+ case 's': /* deprecated */
+ {
+ print_t3_dsu();
+ break;
+ }
+ case 'e': /* set framimg format */
+ {
+ config.format = strtoul(optarg, NULL, 0);
+ if (verbose) print_format();
+ update = 1;
+ break;
+ }
+ case 'f': /* read and print framer regs */
+ {
+ int i;
+ printf("TXC03401 regs:\n");
+ printf(" 0 1 2 3 4 5 6 7");
+ for (i=0; i<21; i++)
+ {
+ if (i%8 == 0) printf("\n%02X: ", i);
+ printf("%02X ", read_framer(i));
+ }
+ printf("\n\n");
+ break;
+ }
+ case 'F': /* write framer reg */
+ {
+ u_int32_t addr = strtoul(optarg, NULL, 0);
+ u_int32_t data = strtoul(argv[optind++], NULL, 0);
+ write_framer(addr, data);
+ if (verbose)
+ {
+ data = read_framer(addr);
+ printf("Write framer register: addr = 0x%02X data = 0x%02X\n", addr, data);
+ }
+ break;
+ }
+ case 'l': /* send DS3 line loopback deactivate BOP cmd */
+ {
+ ioctl_snmp_send(TSEND_RESET);
+ if (verbose) printf("Sent 'DS3 Line Loopback deactivate' BOP cmd\n");
+ break;
+ }
+ case 'L': /* send DS3 line loopback activate BOP cmd */
+ {
+ ioctl_snmp_send(TSEND_LINE);
+ if (verbose) printf("Sent 'DS3 Line Loopback activate' BOP cmd\n");
+ break;
+ }
+ case 'S': /* set scrambler */
+ {
+ config.scrambler = strtoul(optarg, NULL, 0);
+ if (verbose) print_scrambler();
+ update = 1;
+ break;
+ }
+ case 'v': /* set verbose mode */
+ {
+ verbose = 1;
+ break;
+ }
+ case 'V': /* set T3 freq control DAC */
+ {
+ u_int32_t dac = strtoul(optarg, NULL, 0);
+ write_dac(dac);
+ if (verbose) printf("VCXO DAC value is %d\n", dac);
+ break;
+ }
+ default:
+ {
+ printf("Unknown command char: %c\n", ch);
+ exit(1);
+ } /* case */
+ } /* switch */
+ } /* while */
+ } /* proc */
+
+void print_test_pattern(int patt)
+ {
+ printf("Test Pattern:\t\t");
+ switch (patt)
+ {
+ case 0:
+ printf("unframed X^11+X^9+1\n");
+ break;
+ case 1:
+ printf("unframed X^15+X^14+1\n");
+ break;
+ case 2:
+ printf("unframed X^20+X^17+1\n");
+ break;
+ case 3:
+ printf("unframed X^23+X^18+1\n");
+ break;
+ case 4:
+ printf("unframed X^11+X^9+1 w/7ZS\n");
+ break;
+ case 5:
+ printf("unframed X^15+X^14+1 w/7ZS\n");
+ break;
+ case 6:
+ printf("unframed X^20+X^17+1 w/14ZS (QRSS)\n");
+ break;
+ case 7:
+ printf("unframed X^23+X^18+1 w/14ZS\n");
+ break;
+ case 8:
+ printf("framed X^11+X^9+1\n");
+ break;
+ case 9:
+ printf("framed X^15+X^14+1\n");
+ break;
+ case 10:
+ printf("framed X^20+X^17+1\n");
+ break;
+ case 11:
+ printf("framed X^23+X^18+1\n");
+ break;
+ case 12:;
+ printf("framed X^11+X^9+1 w/7ZS\n");
+ break;
+ case 13:
+ printf("framed X^15+X^14+1 w/7ZS\n");
+ break;
+ case 14:
+ printf("framed X^20+X^17+1 w/14ZS (QRSS)\n");
+ break;
+ case 15:
+ printf("framed X^23+X^18+1 w/14ZS\n");
+ break;
+ }
+ }
+
+char *print_t1_bop(int bop_code)
+ {
+ switch(bop_code)
+ {
+ case 0x00:
+ return "Yellow Alarm (far end LOF)";
+ case 0x07:
+ return "Line Loop up";
+ case 0x1C:
+ return "Line Loop down";
+ case 0x0A:
+ return "Payload Loop up";
+ case 0x19:
+ return "Payload Loop down";
+ case 0x09:
+ return "Network Loop up";
+ case 0x12:
+ return "Network Loop down";
+ default:
+ return "Unknown BOP code";
+ }
+ }
+
+void print_far_report(int index)
+ {
+ u_int16_t far = status.snmp.t1.prm[index];
+
+ printf(" SEQ=%d ", (far & T1PRM_SEQ)>>8);
+ if (far & T1PRM_G1) printf("CRC=1");
+ else if (far & T1PRM_G2) printf("CRC=1 to 5");
+ else if (far & T1PRM_G3) printf("CRC=5 to 10");
+ else if (far & T1PRM_G4) printf("CRC=10 to 100");
+ else if (far & T1PRM_G5) printf("CRC=100 to 319");
+ else if (far & T1PRM_G6) printf("CRC>=320");
+ else printf("CRC=0");
+ printf(" SE=%d", (far & T1PRM_SE) ? 1 : 0);
+ printf(" FE=%d", (far & T1PRM_FE) ? 1 : 0);
+ printf(" LV=%d", (far & T1PRM_LV) ? 1 : 0);
+ printf(" SL=%d", (far & T1PRM_SL) ? 1 : 0);
+ printf(" LB=%d", (far & T1PRM_LB) ? 1 : 0);
+ printf("\n");
+ }
+
+void print_t1_snmp()
+ {
+ printf("SNMP Near-end performance data:\n");
+ printf(" LCV=%d", status.snmp.t1.lcv);
+ printf(" LOS=%d", (status.snmp.t1.line & TLINE_LOS) ? 1 : 0);
+ printf(" FE=%d", status.snmp.t1.fe);
+ printf(" CRC=%d", status.snmp.t1.crc);
+ printf(" AIS=%d", (status.snmp.t1.line & TLINE_RX_AIS) ? 1 : 0);
+ printf(" SEF=%d", (status.snmp.t1.line & T1LINE_SEF) ? 1 : 0);
+ printf(" OOF=%d", (status.snmp.t1.line & TLINE_LOF) ? 1 : 0);
+ printf(" RAI=%d",(status.snmp.t1.line & TLINE_RX_RAI) ? 1 : 0);
+ printf("\n");
+ if (config.format == CFG_FORMAT_T1ESF)
+ {
+ printf("ANSI Far-end performance reports:\n");
+ print_far_report(0);
+ print_far_report(1);
+ print_far_report(2);
+ print_far_report(3);
+ }
+ }
+
+void print_t1_dsu()
+ {
+ char *no = "No", *yes = "Yes";
+ u_int16_t mii16 = read_mii(16);
+ u_int8_t isr0 = read_framer(Bt8370_ISR0);
+ u_int8_t loop = read_framer(Bt8370_LOOP);
+ u_int8_t vga_max = read_framer(Bt8370_VGA_MAX) & 0x3F;
+ u_int8_t alm1 = read_framer(Bt8370_ALM1);
+ u_int8_t alm3 = read_framer(Bt8370_ALM3);
+ u_int8_t talm = read_framer(Bt8370_TALM);
+ u_int8_t tpatt = read_framer(Bt8370_TPATT);
+ u_int8_t tpulse = read_framer(Bt8370_TLIU_CR);
+ u_int8_t vga;
+ u_int8_t saved_pulse, saved_lbo;
+
+ /* d/c write required before read */
+ write_framer(Bt8370_VGA, 0);
+ vga = read_framer(Bt8370_VGA) & 0x3F;
+
+ print_format();
+ print_time_slots();
+ print_tx_clk_src();
+ print_tx_speed();
+
+ saved_pulse = config.tx_pulse;
+ config.tx_pulse = tpulse & 0x0E;
+ saved_lbo = config.tx_lbo;
+ config.tx_lbo = tpulse & 0x30;
+ print_tx_pulse();
+ config.tx_pulse = saved_pulse;
+ config.tx_lbo = saved_lbo;
+
+ printf("Tx outputs: \t\t%sabled\n", (mii16 & MII16_T1_XOE) ? "En" : "Dis");
+ printf("Line impedance:\t\t%s ohms\n", (mii16 & MII16_T1_Z) ? "120" : "100");
+ printf("Max line loss: \t\t%4.1f dB\n", vga_dbs(vga_max));
+ printf("Cur line loss: \t\t%4.1f dB\n", vga_dbs(vga));
+ printf("Invert data: \t\t%s\n", (mii16 & MII16_T1_INVERT) ? yes : no);
+ printf("Line loop: \t\t%s\n", (loop & LOOP_LINE) ? yes : no);
+ printf("Payload loop: \t\t%s\n", (loop & LOOP_PAYLOAD) ? yes : no);
+ printf("Framer loop: \t\t%s\n", (loop & LOOP_FRAMER) ? yes : no);
+ printf("Analog loop: \t\t%s\n", (loop & LOOP_ANALOG) ? yes : no);
+ printf("Tx AIS: \t\t%s\n", ((talm & TALM_TAIS) ||
+ ((talm & TALM_AUTO_AIS) && (alm1 & ALM1_RLOS))) ? yes : no);
+ printf("Rx AIS: \t\t%s\n", (alm1 & ALM1_RAIS) ? yes : no);
+ if (((config.format & 1)==0) && (config.format != CFG_FORMAT_E1NONE))
+ {
+ printf("Tx RAI: \t\t%s\n", ((talm & TALM_TYEL) ||
+ ((talm & TALM_AUTO_YEL) && (alm3 & ALM3_FRED))) ? yes : no);
+ printf("Rx RAI: \t\t%s\n", (alm1 & ALM1_RYEL) ? yes : no);
+ }
+ if (config.format == CFG_FORMAT_T1ESF)
+ {
+ printf("Tx BOP RAI: \t\t%s\n", (alm1 & ALM1_RLOF) ? yes : no);
+ printf("Rx BOP RAI: \t\t%s\n", (alm1 & ALM1_RMYEL) ? yes : no);
+ }
+ if ((config.format & 0x11) == 0x10) /* E1CAS */
+ {
+ printf("Rx TS16 AIS: \t\t%s\n", (alm3 & ALM3_RMAIS) ? yes : no);
+ printf("Tx TS16 RAI; \t\t%s\n",
+ ((talm & TALM_AUTO_MYEL) && (alm3 & ALM3_SRED)) ? yes : no);
+ }
+ printf("Rx LOS analog: \t\t%s\n", (alm1 & ALM1_RALOS) ? yes : no);
+ printf("Rx LOS digital:\t\t%s\n", (alm1 & ALM1_RLOS) ? yes : no);
+ printf("Rx LOF: \t\t%s\n", (alm1 & ALM1_RLOF) ? yes : no);
+ printf("Tx QRS: \t\t%s\n", (tpatt & 0x10) ? yes : no);
+ printf("Rx QRS: \t\t%s\n", (isr0 & 0x10) ? yes : no);
+ printf("LCV errors: \t\t%d\n",
+ read_framer(Bt8370_LCV_LO) + (read_framer(Bt8370_LCV_HI)<<8));
+ if (config.format != CFG_FORMAT_E1NONE)
+ {
+ if ((config.format & 1)==0) printf("Far End Block Errors:\t%d\n",
+ read_framer(Bt8370_FEBE_LO) + (read_framer(Bt8370_FEBE_HI)<<8));
+ printf("CRC errors: \t\t%d\n",
+ read_framer(Bt8370_CRC_LO) + (read_framer(Bt8370_CRC_HI)<<8));
+ printf("Frame errors: \t\t%d\n",
+ read_framer(Bt8370_FERR_LO) + (read_framer(Bt8370_FERR_HI)<<8));
+ printf("Sev Err Frms: \t\t%d\n", read_framer(Bt8370_AERR) & 0x03);
+ printf("Change of Frm align:\t%d\n", (read_framer(Bt8370_AERR) & 0x0C)>>2);
+ printf("Loss of Frame events:\t%d\n", (read_framer(Bt8370_AERR) & 0xF0)>>4);
+ }
+ if (config.format == CFG_FORMAT_T1ESF)
+ {
+ printf("Last Tx BOP msg:\t0x%02X (%s)\n",
+ read_framer(Bt8370_TBOP), print_t1_bop(read_framer(Bt8370_TBOP)));
+ printf("Last Rx BOP msg:\t0x%02X (%s)\n",
+ read_framer(Bt8370_RBOP), print_t1_bop(read_framer(Bt8370_RBOP)&0x3F));
+ }
+ print_t1_snmp();
+ }
+
+void t1_cmd(int argc, char **argv)
+ {
+ int ch;
+
+ while ((ch = getopt(argc, argv, "a:A:B:c:de:E:fF:g:iIlLpPstT:u:U:vxX")) != -1)
+ {
+ switch (ch)
+ {
+ case 'a': /* stop alarms */
+ {
+ switch (optarg[0])
+ {
+ case 'y': /* Stop sending Yellow Alarm */
+ {
+ if ((config.format == CFG_FORMAT_T1SF) ||
+ (config.format == CFG_FORMAT_E1NONE))
+ printf("No Yellow alarm for this frame format\n");
+ else if (config.format == CFG_FORMAT_T1ESF)
+ write_framer(Bt8370_BOP, 0xE0); /* rbop 25, tbop off */
+ else
+ {
+ u_int8_t talm = read_framer(Bt8370_TALM);
+ write_framer(Bt8370_TALM, talm & ~TALM_TYEL);
+ }
+ if (verbose) printf("Stop sending Yellow alarm\n");
+ break;
+ }
+ case 'a': /* Stop sending AIS */
+ case 'b': /* Stop sending Blue Alarm */
+ {
+ u_int8_t talm = read_framer(Bt8370_TALM);
+ write_framer(Bt8370_TALM, talm & ~TALM_TAIS);
+ if (verbose) printf("Stop sending AIS/Blue signal\n");
+ break;
+ }
+ default:
+ printf("Unknown alarm: %c\n", optarg[0]);
+ }
+ break;
+ }
+ case 'A': /* start alarms */
+ {
+ switch (optarg[0])
+ {
+ case 'y': /* Start sending Yellow Alarm */
+ {
+ if ((config.format == CFG_FORMAT_T1SF) ||
+ (config.format == CFG_FORMAT_E1NONE))
+ printf("No Yellow alarm for this frame format\n");
+ else if (config.format == CFG_FORMAT_T1ESF)
+ {
+ write_framer(Bt8370_BOP, 0x0F); /* rbop off, tbop cont */
+ write_framer(Bt8370_TBOP, T1BOP_OOF);
+ }
+ else
+ {
+ u_int8_t talm = read_framer(Bt8370_TALM);
+ write_framer(Bt8370_TALM, talm | TALM_TYEL);
+ }
+ if (verbose) printf("Sending Yellow alarm\n");
+ break;
+ }
+ case 'a': /* Start sending AIS */
+ case 'b': /* Start sending Blue Alarm */
+ {
+ u_int8_t talm = read_framer(Bt8370_TALM);
+ write_framer(Bt8370_TALM, talm | TALM_TAIS);
+ if (verbose) printf("Sending AIS/Blue signal\n");
+ break;
+ }
+ default:
+ printf("Unknown alarm: %c\n", optarg[0]);
+ }
+ break;
+ }
+ case 'B': /* send BOP msg */
+ {
+ u_int8_t bop = strtoul(optarg, NULL, 0);
+ if (config.format == CFG_FORMAT_T1ESF)
+ {
+ write_framer(Bt8370_BOP, 0x0B); /* rbop off, tbop 25 */
+ write_framer(Bt8370_TBOP, bop); /* start sending BOP msg */
+ sleep(1); /* sending 25 BOP msgs takes about 100 ms. */
+ write_framer(Bt8370_BOP, 0xE0); /* rbop 25, tbop off */
+ if (verbose) printf("Sent '0x%02X' BOP msg 25 times\n", bop);
+ }
+ else
+ printf("BOP msgs only work in T1-ESF format\n");
+ break;
+ }
+ case 'c': /* set cable length */
+ {
+ config.cable_len = strtoul(optarg, NULL, 0);
+ if (verbose) print_cable_len();
+ update = 1;
+ break;
+ }
+ case 'd': /* DSU status */
+ case 's': /* deprecated */
+ {
+ print_t1_dsu();
+ break;
+ }
+ case 'e': /* set framimg format */
+ {
+ config.format = strtoul(optarg, NULL, 0);
+ if (verbose) print_format();
+ update = 1;
+ break;
+ }
+ case 'E': /* set time slots */
+ {
+ config.time_slots = strtoul(optarg, NULL, 16);
+ if (verbose) print_time_slots();
+ update = 1;
+ break;
+ }
+ case 'f': /* read and print framer regs */
+ {
+ int i;
+ printf("Bt8370 regs:\n");
+ printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F");
+ for (i=0; i<512; i++)
+ {
+ if (i%16 == 0) printf("\n%03X: ", i);
+ printf("%02X ", read_framer(i));
+ }
+ printf("\n\n");
+ break;
+ }
+ case 'F': /* write framer reg */
+ {
+ u_int32_t addr = strtoul(optarg, NULL, 0);
+ u_int32_t data = strtoul(argv[optind++], NULL, 0);
+ write_framer(addr, data);
+ if (verbose)
+ {
+ data = read_framer(addr);
+ printf("Write framer register: addr = 0x%02X data = 0x%02X\n", addr, data);
+ }
+ break;
+ }
+ case 'g': /* set receiver gain */
+ {
+ config.rx_gain = strtoul(optarg, NULL, 0);
+ if (verbose) print_rx_gain();
+ update = 1;
+ break;
+ }
+ case 'i': /* send CSU loopback deactivate inband cmd */
+ {
+ if (config.format == CFG_FORMAT_T1SF)
+ {
+ if (verbose) printf("Sending 'CSU loop down' inband cmd for 10 secs...");
+ ioctl_snmp_send(TSEND_RESET);
+ sleep(10);
+ ioctl_snmp_send(TSEND_NORMAL);
+ if (verbose) printf("done\n");
+ }
+ else
+ printf("Inband loopback cmds only work in T1-SF format");
+ break;
+ }
+ case 'I': /* send CSU loopback activate inband cmd */
+ {
+ if (config.format == CFG_FORMAT_T1SF)
+ {
+ if (verbose) printf("Sending 'CSU loop up' inband cmd for 10 secs...");
+ ioctl_snmp_send(TSEND_LINE);
+ sleep(10);
+ ioctl_snmp_send(TSEND_NORMAL);
+ if (verbose) printf("done\n");
+ }
+ else
+ printf("Inband loopback cmds only work in T1-SF format");
+ break;
+ }
+ case 'l': /* send line loopback deactivate BOP msg */
+ {
+ if (config.format == CFG_FORMAT_T1ESF)
+ {
+ ioctl_snmp_send(TSEND_RESET);
+ if (verbose) printf("Sent 'Line Loop Down' BOP cmd\n");
+ }
+ else
+ printf("BOP msgs only work in T1-ESF format\n");
+ break;
+ }
+ case 'L': /* send line loopback activate BOP msg */
+ {
+ if (config.format == CFG_FORMAT_T1ESF)
+ {
+ ioctl_snmp_send(TSEND_LINE);
+ if (verbose) printf("Sent 'Line Loop Up' BOP cmd\n");
+ }
+ else
+ printf("BOP msgs only work in T1-ESF format\n");
+ break;
+ }
+ case 'p': /* send payload loopback deactivate BOP msg */
+ {
+ if (config.format == CFG_FORMAT_T1ESF)
+ {
+ ioctl_snmp_send(TSEND_RESET);
+ if (verbose) printf("Sent 'Payload Loop Down' BOP cmd\n");
+ }
+ else
+ printf("BOP msgs only work in T1-ESF format\n");
+ break;
+ }
+ case 'P': /* send payload loopback activate BOP msg */
+ {
+ if (config.format == CFG_FORMAT_T1ESF)
+ {
+ ioctl_snmp_send(TSEND_PAYLOAD);
+ if (verbose) printf("Sent 'Payload Loop Up' BOP cmd\n");
+ }
+ else
+ printf("BOP msgs only work in T1-ESF format\n");
+ break;
+ }
+ case 't': /* stop sending test pattern */
+ {
+ ioctl_snmp_send(TSEND_NORMAL);
+ if (verbose) printf("Stop sending test pattern\n");
+ break;
+ }
+ case 'T': /* start sending test pattern */
+ {
+ u_int8_t patt = strtoul(optarg, NULL, 0);
+ write_framer(Bt8370_TPATT, 0x10 + patt);
+ write_framer(Bt8370_RPATT, 0x30 + patt);
+ if (verbose) print_test_pattern(patt);
+ break;
+ }
+ case 'u': /* set transmit pulse shape */
+ {
+ config.tx_pulse = strtoul(optarg, NULL, 0);
+ if (verbose) print_tx_pulse();
+ update = 1;
+ break;
+ }
+ case 'U': /* set tx line build-out */
+ {
+ if (config.tx_pulse == CFG_PULSE_T1CSU)
+ {
+ config.tx_lbo = strtoul(optarg, NULL, 0);
+ if (verbose) print_tx_pulse();
+ update = 1;
+ }
+ else
+ printf("LBO only meaningful if Tx Pulse is T1CSU\n");
+ break;
+ }
+ case 'v': /* set verbose mode */
+ {
+ verbose = 1;
+ break;
+ }
+ case 'x': /* disable transmitter outputs */
+ {
+ write_mii(16, read_mii(16) & ~MII16_T1_XOE);
+ if (verbose) printf("Transmitter outputs disabled\n");
+ break;
+ }
+ case 'X': /* enable transmitter outputs */
+ {
+ write_mii(16, read_mii(16) | MII16_T1_XOE);
+ if (verbose) printf("Transmitter outputs enabled\n");
+ break;
+ }
+ default:
+ {
+ printf("Unknown command char: %c\n", ch);
+ exit(1);
+ } /* case */
+ } /* switch */
+ } /* while */
+ } /* proc */
+
+/* used when reading Motorola S-Record format ROM files */
+unsigned char read_hex(FILE *f)
+ {
+ unsigned char a, b, c;
+ for (a=0, b=0; a<2; a++)
+ {
+ c = fgetc(f);
+ c -= 48;
+ if (c > 9) c -= 7;
+ b = (b<<4) | (c & 0xF);
+ }
+ checksum += b;
+ return b;
+ }
+
+static void load_xilinx(char *name)
+ {
+ FILE *f;
+ char *ucode;
+ int i, length;
+ char c;
+
+ if (verbose) printf("Load firmware from file %s...\n", name);
+ if ((f = fopen(name, "r")) == 0)
+ {
+ perror("Failed to open file");
+ exit(1);
+ }
+
+ ucode = (char *)malloc(8192); bzero(ucode, 8192);
+
+ c = fgetc(f);
+ if (c == 'X')
+ { /* Xilinx raw bits file (foo.rbt) */
+ /* skip seven lines of boiler plate */
+ for (i=0; i<7;) if ((c=fgetc(f))=='\n') i++;
+ /* build a dense bit array */
+ i = length = 0;
+ while ((c=fgetc(f))!=EOF)
+ { /* LSB first */
+ if (c=='1') ucode[length] |= 1<<i++;
+ if (c=='0') i++;
+ if (i==8) { i=0; length++; }
+ }
+ }
+ else if (c == 'S')
+ { /* Motarola S records (foo.exo) */
+ int blklen;
+ length = 0;
+ ungetc(c, f);
+ while ((c = fgetc(f)) != EOF)
+ {
+ if (c != 'S')
+ {
+ printf("I'm confused; I expected an 'S'\n");
+ exit(1);
+ }
+ c = fgetc(f);
+ if (c == '9') break;
+ else if (c == '1')
+ {
+ checksum = 0;
+ blklen = read_hex(f) -3;
+ read_hex(f); /* hi blkaddr */
+ read_hex(f); /* lo blkaddr */
+ for (i=0; i<blklen; i++)
+ ucode[length++] = read_hex(f);
+ read_hex(f); /* process but ignore checksum */
+ if (checksum != 0xFF)
+ {
+ printf("File checksum error\n");
+ exit(1);
+ }
+ c = fgetc(f); /* throw away eol */
+ c = fgetc(f); /* throw away eol */
+ }
+ else
+ {
+ printf("I'm confused; I expected a '1' or a '9'\n");
+ exit(1);
+ }
+ } /* while */
+ } /* Motorola S-Record */
+ else
+ {
+ printf("Unknown file type giving up\n");
+ exit(1);
+ }
+
+ load_xilinx_from_file(ucode, length);
+ }
+
+/* 32-bit CRC calculated right-to-left over 8-bit bytes */
+u_int32_t crc32(char *bufp, int len)
+ {
+ int bit, i;
+ u_int32_t data;
+ u_int32_t crc = 0xFFFFFFFFL;
+ u_int32_t poly = 0xEDB88320L;
+
+ for (i = 0; i < len; i++)
+ for (data = *bufp++, bit = 0; bit < 8; bit++, data >>= 1)
+ crc = (crc >> 1) ^ (((crc ^ data) & 1) ? poly : 0);
+
+ return crc;
+ }
+
+/* 8-bit CRC calculated left-to-right over 16-bit words */
+u_int8_t crc8(u_int16_t *bufp, int len)
+ {
+ int bit, i;
+ u_int16_t data;
+ u_int8_t crc = 0xFF;
+ u_int8_t poly = 0x07;
+
+ for (i = 0; i < len; i++)
+ for (data = *bufp++, bit = 15; bit >= 0; bit--)
+ {
+ if ((i==8) && (bit==7)) break;
+ crc = (crc << 1) ^ ((((crc >> 7) ^ (data >> bit)) & 1) ? poly : 0);
+ }
+ return crc;
+ }
+
+/* HSSI=3, DS3=4, SSI=5, T1E1=6, HSSIc=7, SDSL=8 */
+void init_srom(int board)
+ {
+ int i;
+ u_int16_t srom[64];
+
+ /* zero the entire rom */
+ for (i=0; i<64; i++) srom[i] = 0;
+
+ srom[0] = 0x1376; /* subsys vendor id */
+ srom[1] = board ? board : (read_mii(3)>>4 & 0xF) +1;
+ srom[8] = crc8(srom, 9);
+ /* Tulip hardware checks this checksum */
+ srom[10] = 0x6000; /* ethernet address */
+ srom[11] = 0x0099; /* ethernet address */
+ srom[12] = 0x0000; /* ethernet address */
+ /* srom checksum is low 16 bits of Ethernet CRC-32 */
+ srom[63] = crc32((char *)srom, 126) ^ 0xFFFFFFFFL;
+
+ /* write the SROM */
+#if 1 /* really write it */
+ for (i=0; i<64; i++) write_srom(i, srom[i]);
+#else /* print what would be written */
+ printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F");
+ for (i=0; i<64; i++)
+ {
+ if (i%8 == 0) printf("\n%02X: ", i<<1);
+ printf("%02X %02X ", srom[i] & 0xFF, srom[i]>>8);
+ }
+ printf("\n\n");
+#endif
+ }
+
+int main(int argc, char **argv)
+ {
+ int i, error, ch;
+ char *optstring = "13a:bBcCdDeEf:Fhi:L:mM:pP:sS:tT:uUvVwW:xXyYzZ?";
+
+ progname = (char *)argv[0];
+
+ /* Here is the overall plan:
+ * 1) Read the interface name from the command line.
+ * 2) Open the device; decide if netgraph is being used.
+ * 3) Read the current interface configuration from the driver.
+ * 4) Read the command line args and carry out their actions.
+ * 5) Write the modified interface configuration to the driver.
+ */
+
+ /* 1) Read the interface name from the command line. */
+#if __linux__
+ ifname = (argc==1) ? "hdlc0" : (char *) argv[1];
+#else
+ ifname = (argc==1) ? DEVICE_NAME"0" : (char *) argv[1];
+#endif
+
+ /* 2) Open the device; decide if netgraph is being used, */
+ /* use netgraph if ifname ends with ":" */
+ for (i=0; i<16; i++) if (ifname[i] == 0) break;
+
+ /* Get a socket type file descriptor. */
+#if defined(NETGRAPH)
+ if ((netgraph = (ifname[i-1] == ':')))
+ error = NgMkSockNode(NULL, &fdcs, NULL);
+ else
+#endif
+ error = fdcs = socket(AF_INET, SOCK_DGRAM, 0);
+ if (error < 0)
+ {
+ fprintf(stderr, "%s: %s() failed: %s\n", progname,
+ netgraph? "NgMkSockNode" : "socket", strerror(errno));
+ exit(1);
+ }
+
+ /* 3) Read the current interface configuration from the driver. */
+ ioctl_read_config();
+ ioctl_read_status();
+
+ summary = (argc <= 2); /* print summary at end */
+ update = 0; /* write to card at end */
+
+ /* 4) Read the command line args and carry out their actions. */
+ optind = 2;
+ while (((ch = getopt(argc, argv, optstring)) != -1) && (argc > 2))
+ {
+ switch (ch)
+ {
+ case '1': /* T1 commands */
+ {
+ if (verbose) printf("Doing T1 settings\n");
+ if (status.card_type != TLP_CSID_T1E1)
+ {
+ printf("T1 settings only apply to T1E1 cards\n");
+ exit(1);
+ }
+ t1_cmd(argc, argv);
+ break;
+ }
+ case '3': /* T3 commands */
+ {
+ if (verbose) printf("Doing T3 settings\n");
+ if (status.card_type != TLP_CSID_T3)
+ {
+ printf("T3 settings only apply to T3 cards\n");
+ exit(1);
+ }
+ t3_cmd(argc, argv);
+ break;
+ }
+ case 'a': /* clock source */
+ {
+ if ((status.card_type != TLP_CSID_T1E1) ||
+ (status.card_type != TLP_CSID_HSSI) ||
+ (status.card_type != TLP_CSID_HSSIc))
+ {
+ if (verbose) print_tx_clk_src();
+ config.tx_clk_src = strtoul(optarg, NULL, 0);
+ update = 1;
+ }
+ else
+ printf("txclksrc only applies to T1E1 and HSSI card types\n");
+ break;
+ }
+ case 'b': /* read bios rom */
+ {
+ int i;
+ printf("Bios ROM:\n");
+ printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F");
+ for (i=0; i<256; i++)
+ {
+ if (i%16 == 0) printf("\n%02X: ", i);
+ printf("%02X ", read_bios_rom(i));
+ }
+ printf("\n\n");
+ break;
+ }
+ case 'B': /* write bios rom */
+ {
+ int i;
+ for (i=0; i<256; i++) write_bios_rom(i, 255-i);
+ if (verbose) printf("wrote (0..255) to bios rom addrs (0..255)\n");
+ break;
+ }
+ case 'c': /* set crc_len = 16 */
+ {
+ config.crc_len = CFG_CRC_16;
+ if (verbose) print_crc_len();
+ update = 1;
+ break;
+ }
+ case 'C': /* set crc_len = 32 */
+ {
+ config.crc_len = CFG_CRC_32;
+ if (verbose) print_crc_len();
+ update = 1;
+ break;
+ }
+ case 'd': /* clear DEBUG flag */
+ {
+ config.debug = 0;
+ if (verbose) printf("DEBUG flag cleared\n");
+ update = 1;
+ break;
+ }
+ case 'D': /* set DEBUG flag */
+ {
+ config.debug = 1;
+ if (verbose) printf("DEBUG flag set\n");
+ update = 1;
+ break;
+ }
+ case 'e': /* set DTE (default) */
+ {
+ if ((status.card_type == TLP_CSID_SSI) ||
+ (status.card_type == TLP_CSID_HSSIc))
+ {
+ config.dte_dce = CFG_DTE;
+ if (verbose) print_dte_dce();
+ update = 1;
+ }
+ else
+ printf("DTE cmd only applies to SSI & HSSIc cards\n");
+ break;
+ }
+ case 'E': /* set DCE */
+ {
+ if ((status.card_type == TLP_CSID_SSI) ||
+ (status.card_type == TLP_CSID_HSSIc))
+ {
+ config.dte_dce = CFG_DCE;
+ if (verbose) print_dte_dce();
+ update = 1;
+ }
+ else
+ printf("DCE cmd only applies to SSI & HSSIc cards\n");
+ break;
+ }
+ case 'f': /* set synth osc freq */
+ {
+ if ((status.card_type == TLP_CSID_SSI) ||
+ (status.card_type == TLP_CSID_HSSIc))
+ {
+ synth_freq(strtoul(optarg, NULL, 0));
+ write_synth(config.synth);
+ if (verbose) print_synth_freq();
+ }
+ else
+ printf("synth osc freq only applies to SSI & HSSIc cards\n");
+ break;
+ }
+ case 'F': /* set SPPP line protocol to Frame-Relay */
+ {
+ config.line_prot = PROT_FRM_RLY;
+ config.keep_alive = 1; /* required for LMI operation */
+ if (verbose) printf("SPPP line protocol set to Frame-Relay\n");
+ update = 1;
+ break;
+ }
+ case 'h': /* help */
+ case '?':
+ {
+ usage();
+ exit(0);
+ }
+ case 'i': /* interface name */
+ {
+ /* already scanned this */
+ break;
+ }
+ case 'L': /* set loopback modes */
+ {
+ config.loop_back = strtoul(optarg, NULL, 0);
+ if (verbose) print_loop_back();
+ update = 1;
+ break;
+ }
+ case 'm': /* read and print MII regs */
+ {
+ printf("MII regs:\n");
+ printf(" 0 1 2 3 4 5 6 7");
+ for (i=0; i<32; i++)
+ {
+ u_int16_t mii = read_mii(i);
+ if (i%8 == 0) printf("\n%02X: ", i);
+ printf("%04X ", mii);
+ }
+ printf("\n\n");
+ break;
+ }
+ case 'M': /* write MII reg */
+ {
+ u_int32_t addr = strtoul(optarg, NULL, 0);
+ u_int32_t data = strtoul(argv[optind++], NULL, 0);
+ write_mii(addr, data);
+ if (verbose)
+ {
+ data = read_mii(addr);
+ printf("Write mii register: addr = 0x%02X data = 0x%04X\n", addr, data);
+ }
+ break;
+ }
+ case 'p': /* read and print PCI config regs */
+ {
+ int i;
+ printf("21140A PCI Config regs:\n");
+ printf(" 0 1 2 3");
+ for (i=0; i<16; i++)
+ {
+ if (i%4 == 0) printf("\n%X: ", i);
+ printf("%08X ", read_pci_config(i<<2));
+ }
+ printf("\n\n");
+ break;
+ }
+ case 'P': /* write PCI config reg */
+ {
+ u_int32_t addr = strtoul(optarg, NULL, 0);
+ u_int32_t data = strtoul(argv[optind++], NULL, 0);
+ write_pci_config(addr, data);
+ if (verbose)
+ {
+ data = read_pci_config(addr);
+ printf("Write PCI config reg: addr = 0x%02X data = 0x%08X\n", addr, data);
+ }
+ break;
+ }
+ case 's': /* read and print Tulip SROM */
+ {
+ int i;
+ printf("21140A SROM:\n");
+ printf(" 0 1 2 3 4 5 6 7 8 9 A B C D E F");
+ for (i=0; i<64; i++)
+ {
+ u_int16_t srom = read_srom(i);
+ if (i%8 == 0) printf("\n%02X: ", i<<1);
+ printf("%02X %02X ", srom & 0xFF, srom>>8);
+ }
+ printf("\n\n");
+ break;
+ }
+ case 'S': /* write Tulip SROM loc */
+ {
+#if 0 /* write a single location -- not too useful */
+ u_int32_t addr = strtoul(optarg, NULL, 0);
+ u_int32_t data = strtoul(argv[optind++], NULL, 0);
+ write_mii(addr, data);
+ data = read_mii(addr);
+ printf("Write SROM: addr = 0x%02X data = 0x%04X\n", addr, data);
+#endif
+#if 0 /* write the whole SROM -- very dangerous */
+ init_srom(strtoul(optarg, NULL, 0));
+#endif
+ printf("Caution! Recompile %s to enable this.\n", progname);
+ break;
+ }
+ case 't': /* read and print Tulip CSRs */
+ {
+ int i;
+ printf("21140A CSRs:\n");
+ printf(" 0 1 2 3");
+ for (i=0; i<16; i++)
+ {
+ if (i%4 == 0) printf("\n%X: ", i);
+ printf("%08X ", read_csr(i));
+ }
+ printf("\n\n");
+ break;
+ }
+ case 'T': /* write Tulip CSR */
+ {
+ u_int32_t addr = strtoul(optarg, NULL, 0);
+ u_int32_t data = strtoul(argv[optind++], NULL, 0);
+ write_csr(addr, data);
+ if (verbose)
+ {
+ data = read_csr(addr);
+ printf("Write 21140A CSR: addr = 0x%02X data = 0x%08X\n", addr, data);
+ }
+ break;
+ }
+ case 'u': /* reset event counters */
+ {
+ ioctl_reset_cntrs();
+ if (verbose) printf("Event counters reset\n");
+ break;
+ }
+ case 'U': /* reset gate array */
+ {
+ reset_xilinx();
+ if (verbose) printf("gate array reset\n");
+ break;
+ }
+ case 'v': /* set verbose mode */
+ {
+ verbose = 1;
+ break;
+ }
+ case 'V': /* print card configuration */
+ {
+ summary = 1;
+ break;
+ }
+ case 'w': /* load gate array microcode from ROM */
+ {
+ load_xilinx_from_rom();
+ if (verbose) printf("gate array configured from on-board ROM\n");
+ break;
+ }
+ case 'W': /* load gate array microcode from file */
+ {
+ load_xilinx(optarg);
+ if (verbose) printf("gate array configured from file %s\n", optarg);
+ break;
+ }
+ case 'x': /* select RAWIP protocol */
+ {
+ config.line_pkg = PKG_RAWIP;
+ if (verbose) printf("RAWIP mode selected\n");
+ update = 1;
+ break;
+ }
+ case 'X': /* Select in-kernel line protocol packages */
+ {
+ config.line_pkg = 0;
+ if (verbose) printf("line protocol mode selected\n");
+ update = 1;
+ break;
+ }
+ case 'y': /* disable SPPP keep-alive packets */
+ {
+ if ((config.line_pkg == PKG_SPPP) &&
+ (config.line_prot == PROT_FRM_RLY))
+ printf("keep-alives must be ON for Frame-Relay/SPPP\n");
+ else
+ {
+ config.keep_alive = 0;
+ if (verbose) printf("SPPP keep-alive packets disabled\n");
+ update = 1;
+ }
+ break;
+ }
+ case 'Y': /* enable SPPP keep-alive packets */
+ {
+ config.keep_alive = 1;
+ if (verbose) printf("SPPP keep-alive packets enabled\n");
+ update = 1;
+ break;
+ }
+ case 'z': /* set SPPP line protocol to Cisco HDLC */
+ {
+ config.line_prot = PROT_C_HDLC;
+ config.keep_alive = 1;
+ if (verbose) printf("SPPP line protocol set to Cisco-HDLC\n");
+ update = 1;
+ break;
+ }
+ case 'Z': /* set SPPP line protocol to PPP */
+ {
+ config.line_prot = PROT_PPP;
+ config.keep_alive = 0;
+ if (verbose) printf("SPPP line protocol set to PPP\n");
+ update = 1;
+ break;
+ }
+ default:
+ {
+ printf("Unknown command char: %c\n", ch);
+ exit(1);
+ }
+ } /* switch */
+ } /* while */
+
+ if (summary) print_summary();
+
+ /* 5) Write the modified interface configuration to the driver. */
+ if (update) ioctl_write_config();
+
+ exit(0);
+ }
diff --git a/usr.sbin/lpr/Makefile b/usr.sbin/lpr/Makefile
new file mode 100644
index 0000000..3cd0eb6
--- /dev/null
+++ b/usr.sbin/lpr/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+SUBDIR= common_source chkprintcap lp lpc lpd lpq lpr lprm lptest pac \
+ filters filters.ru SMM.doc
+
+# Questions/ideas for lpr & friends could also be sent to:
+# freebsd-print@bostonradio.org
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/lpr/Makefile.inc b/usr.sbin/lpr/Makefile.inc
new file mode 100644
index 0000000..ab667dd
--- /dev/null
+++ b/usr.sbin/lpr/Makefile.inc
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+.if ${MK_INET6_SUPPORT} != "no"
+CFLAGS+= -DINET6
+.endif
+
+LIBLPR= ${.OBJDIR}/../common_source/liblpr.a
+
+.include "../Makefile.inc"
diff --git a/usr.sbin/lpr/SMM.doc/0.t b/usr.sbin/lpr/SMM.doc/0.t
new file mode 100644
index 0000000..65ecd4e
--- /dev/null
+++ b/usr.sbin/lpr/SMM.doc/0.t
@@ -0,0 +1,68 @@
+.\" 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.
+.\"
+.\" @(#)0.t 8.1 (Berkeley) 6/8/93
+.\"
+.if n .ND
+.TL
+4.3BSD Line Printer Spooler Manual
+.EH 'SMM:7-%''4.3BSD Line Printer Spooler Manual'
+.OH '4.3BSD Line Printer Spooler Manual''SMM:7-%'
+.AU
+Ralph Campbell
+.AI
+Computer Systems Research Group
+Computer Science Division
+Department of Electrical Engineering and Computer Science
+University of California, Berkeley
+Berkeley, CA 94720
+.AB
+.FS
+* UNIX is a trademark of Bell Laboratories.
+.FE
+This document describes the structure and installation procedure
+for the line printer spooling system
+developed for the 4.3BSD version
+of the UNIX* operating system.
+.de D?
+.ie \\n(.$>1 Revised \\$1 \\$2 \\$3
+.el DRAFT of \n(mo/\n(dy/\n(yr
+..
+.sp 2
+.LP
+.D? June 8, 1993
+.AE
+.de IR
+\fI\\$1\fP\\$2
+..
+.de DT
+.TA 8 16 24 32 40 48 56 64 72 80
+..
diff --git a/usr.sbin/lpr/SMM.doc/1.t b/usr.sbin/lpr/SMM.doc/1.t
new file mode 100644
index 0000000..1d34e9e
--- /dev/null
+++ b/usr.sbin/lpr/SMM.doc/1.t
@@ -0,0 +1,77 @@
+.\" 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.
+.\"
+.\" @(#)1.t 8.1 (Berkeley) 6/8/93
+.\"
+.NH 1
+Overview
+.PP
+The line printer system supports:
+.IP \(bu 3
+multiple printers,
+.IP \(bu 3
+multiple spooling queues,
+.IP \(bu 3
+both local and remote
+printers, and
+.IP \(bu 3
+printers attached via serial lines that require
+line initialization such as the baud rate.
+.LP
+Raster output devices
+such as a Varian or Versatec, and laser printers such as an Imagen,
+are also supported by the line printer system.
+.PP
+The line printer system consists mainly of the
+following files and commands:
+.DS
+.TS
+l l.
+/etc/printcap printer configuration and capability data base
+/usr/lib/lpd line printer daemon, does all the real work
+/usr/ucb/lpr program to enter a job in a printer queue
+/usr/ucb/lpq spooling queue examination program
+/usr/ucb/lprm program to delete jobs from a queue
+/etc/lpc program to administer printers and spooling queues
+/dev/printer socket on which lpd listens
+.TE
+.DE
+The file /etc/printcap is a master data base describing line
+printers directly attached to a machine and, also, printers
+accessible across a network. The manual page entry
+.IR printcap (5)
+provides the authoritative definition of
+the format of this data base, as well as
+specifying default values for important items
+such as the directory in which spooling is performed.
+This document introduces some of the
+information that may be placed
+.IR printcap .
diff --git a/usr.sbin/lpr/SMM.doc/2.t b/usr.sbin/lpr/SMM.doc/2.t
new file mode 100644
index 0000000..9da2ae2
--- /dev/null
+++ b/usr.sbin/lpr/SMM.doc/2.t
@@ -0,0 +1,141 @@
+.\" 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.
+.\"
+.\" @(#)2.t 8.1 (Berkeley) 6/8/93
+.\"
+.NH 1
+Commands
+.NH 2
+lpd \- line printer daemon
+.PP
+The program
+.IR lpd (8),
+usually invoked at boot time from the /etc/rc file, acts as
+a master server for coordinating and controlling
+the spooling queues configured in the printcap file.
+When
+.I lpd
+is started it makes a single pass through the
+.I printcap
+database restarting any printers that have jobs.
+In normal operation
+.I lpd
+listens for service requests on multiple sockets,
+one in the UNIX domain (named ``/dev/printer'') for
+local requests, and one in the Internet domain
+(under the ``printer'' service specification)
+for requests for printer access from off machine;
+see \fIsocket\fP\|(2) and \fIservices\fP\|(5)
+for more information on sockets and service
+specifications, respectively.
+.I Lpd
+spawns a copy of itself to process the request; the master daemon
+continues to listen for new requests.
+.PP
+Clients communicate with
+.I lpd
+using a simple transaction oriented protocol.
+Authentication of remote clients is done based
+on the ``privilege port'' scheme employed by
+\fIrshd\fP\|(8C) and \fIrcmd\fP\|(3X).
+The following table shows the requests
+understood by
+.IR lpd .
+In each request the first byte indicates the
+``meaning'' of the request, followed by the name
+of the printer to which it should be applied. Additional
+qualifiers may follow, depending on the request.
+.DS
+.TS
+l l.
+Request Interpretation
+_
+^Aprinter\en check the queue for jobs and print any found
+^Bprinter\en receive and queue a job from another machine
+^Cprinter [users ...] [jobs ...]\en return short list of current queue state
+^Dprinter [users ...] [jobs ...]\en return long list of current queue state
+^Eprinter person [users ...] [jobs ...]\en remove jobs from a queue
+.TE
+.DE
+.PP
+The \fIlpr\fP\|(1) command
+is used by users to enter a print job in a local queue and to notify
+the local
+.I lpd
+that there are new jobs in the spooling area.
+.I Lpd
+either schedules the job to be printed locally, or if
+printing remotely, attempts to forward
+the job to the appropriate machine.
+If the printer cannot be opened or the destination
+machine is unreachable, the job will remain queued until it is
+possible to complete the work.
+.NH 2
+lpq \- show line printer queue
+.PP
+The \fIlpq\fP\|(1)
+program works recursively backwards displaying the queue of the machine with
+the printer and then the queue(s) of the machine(s) that lead to it.
+.I Lpq
+has two forms of output: in the default, short, format it
+gives a single line of output per queued job; in the long
+format it shows the list of files, and their sizes, that
+comprise a job.
+.NH 2
+lprm \- remove jobs from a queue
+.PP
+The \fIlprm\fP\|(1) command deletes jobs from a spooling
+queue. If necessary, \fIlprm\fP will first kill off a
+running daemon that is servicing the queue and restart
+it after the required files are removed. When removing
+jobs destined for a remote printer, \fIlprm\fP acts
+similarly to \fIlpq\fP except it first checks locally
+for jobs to remove and then
+tries to remove files in queues off-machine.
+.NH 2
+lpc \- line printer control program
+.PP
+The
+.IR lpc (8)
+program is used by the system administrator to control the
+operation of the line printer system.
+For each line printer configured in /etc/printcap,
+.I lpc
+may be used to:
+.IP \(bu
+disable or enable a printer,
+.IP \(bu
+disable or enable a printer's spooling queue,
+.IP \(bu
+rearrange the order of jobs in a spooling queue,
+.IP \(bu
+find the status of printers, and their associated
+spooling queues and printer daemons.
diff --git a/usr.sbin/lpr/SMM.doc/3.t b/usr.sbin/lpr/SMM.doc/3.t
new file mode 100644
index 0000000..8c950a9
--- /dev/null
+++ b/usr.sbin/lpr/SMM.doc/3.t
@@ -0,0 +1,73 @@
+.\" 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.
+.\"
+.\" @(#)3.t 8.1 (Berkeley) 6/8/93
+.\"
+.NH 1
+Access control
+.PP
+The printer system maintains protected spooling areas so that
+users cannot circumvent printer accounting or
+remove files other than their own.
+The strategy used to maintain protected
+spooling areas is as follows:
+.IP \(bu 3
+The spooling area is writable only by a \fIdaemon\fP user
+and \fIdaemon\fP group.
+.IP \(bu 3
+The \fIlpr\fP program runs set-user-id to \fIroot\fP and
+set-group-id to group \fIdaemon\fP. The \fIroot\fP access permits
+reading any file required. Accessibility is verified
+with an \fIaccess\fP\|(2) call. The group ID
+is used in setting up proper ownership of files
+in the spooling area for \fIlprm\fP.
+.IP \(bu 3
+Control files in a spooling area are made with \fIdaemon\fP
+ownership and group ownership \fIdaemon\fP. Their mode is 0660.
+This insures control files are not modified by a user
+and that no user can remove files except through \fIlprm\fP.
+.IP \(bu 3
+The spooling programs,
+\fIlpd\fP, \fIlpq\fP, and \fIlprm\fP run set-user-id to \fIroot\fP
+and set-group-id to group \fIdaemon\fP to access spool files and printers.
+.IP \(bu 3
+The printer server, \fIlpd\fP,
+uses the same verification procedures as \fIrshd\fP\|(8C)
+in authenticating remote clients. The host on which a client
+resides must be present in the file /etc/hosts.equiv or /etc/hosts.lpd and
+the request message must come from a reserved port number.
+.PP
+In practice, none of \fIlpd\fP, \fIlpq\fP, or
+\fIlprm\fP would have to run as user \fIroot\fP if remote
+spooling were not supported. In previous incarnations of
+the printer system \fIlpd\fP ran set-user-id to \fIdaemon\fP,
+set-group-id to group \fIspooling\fP, and \fIlpq\fP and \fIlprm\fP ran
+set-group-id to group \fIspooling\fP.
diff --git a/usr.sbin/lpr/SMM.doc/4.t b/usr.sbin/lpr/SMM.doc/4.t
new file mode 100644
index 0000000..8800bc0
--- /dev/null
+++ b/usr.sbin/lpr/SMM.doc/4.t
@@ -0,0 +1,206 @@
+.\" 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.
+.\"
+.\" @(#)4.t 8.1 (Berkeley) 6/8/93
+.\"
+.NH 1
+Setting up
+.PP
+The 4.3BSD release comes with the necessary programs
+installed and with the default line printer queue
+created. If the system must be modified, the
+makefile in the directory /usr/src/usr.lib/lpr
+should be used in recompiling and reinstalling
+the necessary programs.
+.PP
+The real work in setting up is to create the
+.I printcap
+file and any printer filters for printers not supported
+in the distribution system.
+.NH 2
+Creating a printcap file
+.PP
+The
+.I printcap
+database contains one or more entries per printer.
+A printer should have a separate spooling directory;
+otherwise, jobs will be printed on
+different printers depending on which printer daemon starts first.
+This section describes how to create entries for printers that do not
+conform to the default printer description (an LP-11 style interface to a
+standard, band printer).
+.NH 3
+Printers on serial lines
+.PP
+When a printer is connected via a serial communication line
+it must have the proper baud rate and terminal modes set.
+The following example is for a DecWriter III printer connected
+locally via a 1200 baud serial line.
+.DS
+.DT
+lp|LA-180 DecWriter III:\e
+ :lp=/dev/lp:br#1200:fs#06320:\e
+ :tr=\ef:of=/usr/lib/lpf:lf=/usr/adm/lpd-errs:
+.DE
+The
+.B lp
+entry specifies the file name to open for output. Here it could
+be left out since ``/dev/lp'' is the default.
+The
+.B br
+entry sets the baud rate for the tty line and the
+.B fs
+entry sets CRMOD, no parity, and XTABS (see \fItty\fP\|(4)).
+The
+.B tr
+entry indicates that a form-feed should be printed when the queue
+empties so the paper can be torn off without turning the printer off-line and
+pressing form feed.
+The
+.B of
+entry specifies the filter program
+.I lpf
+should be used for printing the files;
+more will be said about filters later.
+The last entry causes errors
+to be written to the file ``/usr/adm/lpd-errs''
+instead of the console. Most errors from \fIlpd\fP are logged using
+\fIsyslogd\fP\|(8) and will not be logged in the specified file. The
+filters should use \fIsyslogd\fP to report errors; only those that
+write to standard error output will end up with errors in the \fBlf\fP file.
+(Occasionally errors sent to standard error output have not appeared
+in the log file; the use of \fIsyslogd\fP is highly recommended.)
+.NH 3
+Remote printers
+.PP
+Printers that reside on remote hosts should have an empty
+.B lp
+entry.
+For example, the following printcap entry would send output to the printer
+named ``lp'' on the machine ``ucbvax''.
+.DS
+.DT
+lp|default line printer:\e
+ :lp=:rm=ucbvax:rp=lp:sd=/usr/spool/vaxlpd:
+.DE
+The
+.B rm
+entry is the name of the remote machine to connect to; this name must
+be a known host name for a machine on the network.
+The
+.B rp
+capability indicates
+the name of the printer on the remote machine is ``lp'';
+here it could be left out since this is the default value.
+The
+.B sd
+entry specifies ``/usr/spool/vaxlpd''
+as the spooling directory instead of the
+default value of ``/usr/spool/lpd''.
+.NH 2
+Output filters
+.PP
+Filters are used to handle device dependencies and to
+do accounting functions. The output filtering of
+.B of
+is used when accounting is
+not being done or when all text data must be passed through a filter.
+It is not intended to do accounting since it is started only once,
+all text files are filtered through it, and no provision is made for passing
+owners' login name, identifying the beginning and ending of jobs, etc.
+The other filters (if specified) are started for each file
+printed and do accounting if there is an
+.B af
+entry.
+If entries for both
+.B of
+and other filters are specified,
+the output filter is used only to print the banner page;
+it is then stopped to allow other filters access to the printer.
+An example of a printer that requires output filters
+is the Benson-Varian.
+.DS
+.DT
+va|varian|Benson-Varian:\e
+ :lp=/dev/va0:sd=/usr/spool/vad:of=/usr/lib/vpf:\e
+ :tf=/usr/lib/rvcat:mx#2000:pl#58:px=2112:py=1700:tr=\ef:
+.DE
+The
+.B tf
+entry specifies ``/usr/lib/rvcat'' as the filter to be
+used in printing \fItroff\fP\|(1) output.
+This filter is needed to set the device into print mode
+for text, and plot mode for printing
+.I troff
+files and raster images (see \fIva\fP\|(4V)).
+Note that the page length is set to 58 lines by the
+.B pl
+entry for 8.5" by 11" fan-fold paper.
+To enable accounting, the varian entry would be
+augmented with an
+.B af
+filter as shown below.
+.DS
+.DT
+va|varian|Benson-Varian:\e
+ :lp=/dev/va0:sd=/usr/spool/vad:of=/usr/lib/vpf:\e
+ :if=/usr/lib/vpf:tf=/usr/lib/rvcat:af=/usr/adm/vaacct:\e
+ :mx#2000:pl#58:px=2112:py=1700:tr=\ef:
+.DE
+.NH 2
+Access Control
+.PP
+Local access to printer queues is controlled with the
+.B rg
+printcap entry.
+.DS
+ :rg=lprgroup:
+.DE
+Users must be in the group
+.I lprgroup
+to submit jobs to the specified printer.
+The default is to allow all users access.
+Note that once the files are in the local queue, they can be printed
+locally or forwarded to another host depending on the configuration.
+.PP
+Remote access is controlled by listing the hosts in either the file
+/etc/hosts.equiv or /etc/hosts.lpd, one host per line. Note that
+.IR rsh (1)
+and
+.IR rlogin (1)
+use /etc/hosts.equiv to determine which hosts are equivalent for allowing logins
+without passwords. The file /etc/hosts.lpd is only used to control
+which hosts have line printer access.
+Remote access can be further restricted to only allow remote users with accounts
+on the local host to print jobs by using the \fBrs\fP printcap entry.
+.DS
+ :rs:
+.DE
diff --git a/usr.sbin/lpr/SMM.doc/5.t b/usr.sbin/lpr/SMM.doc/5.t
new file mode 100644
index 0000000..137a342
--- /dev/null
+++ b/usr.sbin/lpr/SMM.doc/5.t
@@ -0,0 +1,116 @@
+.\" 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.
+.\"
+.\" @(#)5.t 8.1 (Berkeley) 6/8/93
+.\"
+.NH 1
+Output filter specifications
+.PP
+The filters supplied with 4.3BSD
+handle printing and accounting for most common
+line printers, the Benson-Varian, the wide (36") and
+narrow (11") Versatec printer/plotters. For other devices or accounting
+methods, it may be necessary to create a new filter.
+.PP
+Filters are spawned by \fIlpd\fP
+with their standard input the data to be printed, and standard output
+the printer. The standard error is attached to the
+.B lf
+file for logging errors or \fIsyslogd\fP may be used for logging errors.
+A filter must return a 0 exit
+code if there were no errors, 1 if the job should be reprinted,
+and 2 if the job should be thrown away.
+When \fIlprm\fP
+sends a kill signal to the \fIlpd\fP process controlling
+printing, it sends a SIGINT signal
+to all filters and descendents of filters.
+This signal can be trapped by filters that need
+to do cleanup operations such as
+deleting temporary files.
+.PP
+Arguments passed to a filter depend on its type.
+The
+.B of
+filter is called with the following arguments.
+.DS
+\fIfilter\fP \fB\-w\fPwidth \fB\-l\fPlength
+.DE
+The \fIwidth\fP and \fIlength\fP values come from the
+.B pw
+and
+.B pl
+entries in the printcap database.
+The
+.B if
+filter is passed the following parameters.
+.DS
+\fIfilter\fP [\|\fB\-c\fP\|] \fB\-w\fPwidth \fB\-l\fPlength \fB\-i\fPindent \fB\-n\fP login \fB\-h\fP host accounting_file
+.DE
+The
+.B \-c
+flag is optional, and only supplied when control characters
+are to be passed uninterpreted to the printer (when using the
+.B \-l
+option of
+.I lpr
+to print the file).
+The
+.B \-w
+and
+.B \-l
+parameters are the same as for the
+.B of
+filter.
+The
+.B \-n
+and
+.B \-h
+parameters specify the login name and host name of the job owner.
+The last argument is the name of the accounting file from
+.IR printcap .
+.PP
+All other filters are called with the following arguments:
+.DS
+\fIfilter\fP \fB\-x\fPwidth \fB\-y\fPlength \fB\-n\fP login \fB\-h\fP host accounting_file
+.DE
+The
+.B \-x
+and
+.B \-y
+options specify the horizontal and vertical page
+size in pixels (from the
+.B px
+and
+.B py
+entries in the printcap file).
+The rest of the arguments are the same as for the
+.B if
+filter.
diff --git a/usr.sbin/lpr/SMM.doc/6.t b/usr.sbin/lpr/SMM.doc/6.t
new file mode 100644
index 0000000..7087790
--- /dev/null
+++ b/usr.sbin/lpr/SMM.doc/6.t
@@ -0,0 +1,94 @@
+.\" 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.
+.\"
+.\" @(#)6.t 8.1 (Berkeley) 6/8/93
+.\"
+.NH 1
+Line printer Administration
+.PP
+The
+.I lpc
+program provides local control over line printer activity.
+The major commands and their intended use will be described.
+The command format and remaining commands are described in
+.IR lpc (8).
+.LP
+\fBabort\fP and \fBstart\fP
+.IP
+.I Abort
+terminates an active spooling daemon on the local host immediately and
+then disables printing (preventing new daemons from being started by
+.IR lpr ).
+This is normally used to forcibly restart a hung line printer daemon
+(i.e., \fIlpq\fP reports that there is a daemon present but nothing is
+happening). It does not remove any jobs from the queue
+(use the \fIlprm\fP command instead).
+.I Start
+enables printing and requests \fIlpd\fP to start printing jobs.
+.LP
+\fBenable\fP and \fBdisable\fP
+.IP
+\fIEnable\fP and \fIdisable\fP allow spooling in the local queue to be
+turned on/off.
+This will allow/prevent
+.I lpr
+from putting new jobs in the spool queue. It is frequently convenient
+to turn spooling off while testing new line printer filters since the
+.I root
+user can still use
+.I lpr
+to put jobs in the queue but no one else can.
+The other main use is to prevent users from putting jobs in the queue
+when the printer is expected to be unavailable for a long time.
+.LP
+\fBrestart\fP
+.IP
+.I Restart
+allows ordinary users to restart printer daemons when
+.I lpq
+reports that there is no daemon present.
+.LP
+\fBstop\fP
+.IP
+.I Stop
+halts a spooling daemon after the current job completes;
+this also disables printing. This is a clean way to shutdown a
+printer to do maintenance, etc. Note that users can still enter jobs in a
+spool queue while a printer is
+.IR stopped .
+.LP
+\fBtopq\fP
+.IP
+.I Topq
+places jobs at the top of a printer queue. This can be used
+to reorder high priority jobs since
+.I lpr
+only provides first-come-first-serve ordering of jobs.
diff --git a/usr.sbin/lpr/SMM.doc/7.t b/usr.sbin/lpr/SMM.doc/7.t
new file mode 100644
index 0000000..a6f6bea
--- /dev/null
+++ b/usr.sbin/lpr/SMM.doc/7.t
@@ -0,0 +1,226 @@
+.\" 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.
+.\"
+.\" @(#)7.t 8.1 (Berkeley) 6/8/93
+.\"
+.NH 1
+Troubleshooting
+.PP
+There are several messages that may be generated by the
+the line printer system. This section
+categorizes the most common and explains the cause
+for their generation. Where the message implies a failure,
+directions are given to remedy the problem.
+.PP
+In the examples below, the name
+.I printer
+is the name of the printer from the
+.I printcap
+database.
+.NH 2
+LPR
+.SH
+lpr: \fIprinter\fP\|: unknown printer
+.IP
+The
+.I printer
+was not found in the
+.I printcap
+database. Usually this is a typing mistake; however, it may indicate
+a missing or incorrect entry in the /etc/printcap file.
+.SH
+lpr: \fIprinter\fP\|: jobs queued, but cannot start daemon.
+.IP
+The connection to
+.I lpd
+on the local machine failed.
+This usually means the printer server started at
+boot time has died or is hung. Check the local socket
+/dev/printer to be sure it still exists (if it does not exist,
+there is no
+.I lpd
+process running).
+Usually it is enough to get a super-user to type the following to
+restart
+.IR lpd .
+.DS
+% /usr/lib/lpd
+.DE
+You can also check the state of the master printer daemon with the following.
+.DS
+% ps l`cat /usr/spool/lpd.lock`
+.DE
+.IP
+Another possibility is that the
+.I lpr
+program is not set-user-id to \fIroot\fP, set-group-id to group \fIdaemon\fP.
+This can be checked with
+.DS
+% ls \-lg /usr/ucb/lpr
+.DE
+.SH
+lpr: \fIprinter\fP\|: printer queue is disabled
+.IP
+This means the queue was turned off with
+.DS
+% lpc disable \fIprinter\fP
+.DE
+to prevent
+.I lpr
+from putting files in the queue. This is normally
+done by the system manager when a printer is
+going to be down for a long time. The
+printer can be turned back on by a super-user with
+.IR lpc .
+.NH 2
+LPQ
+.SH
+waiting for \fIprinter\fP to become ready (offline ?)
+.IP
+The printer device could not be opened by the daemon.
+This can happen for several reasons,
+the most common is that the printer is turned off-line.
+This message can also be generated if the printer is out
+of paper, the paper is jammed, etc.
+The actual reason is dependent on the meaning
+of error codes returned by system device driver.
+Not all printers supply enough information
+to distinguish when a printer is off-line or having
+trouble (e.g. a printer connected through a serial line).
+Another possible cause of this message is
+some other process, such as an output filter,
+has an exclusive open on the device. Your only recourse
+here is to kill off the offending program(s) and
+restart the printer with
+.IR lpc .
+.SH
+\fIprinter\fP is ready and printing
+.IP
+The
+.I lpq
+program checks to see if a daemon process exists for
+.I printer
+and prints the file \fIstatus\fP located in the spooling directory.
+If the daemon is hung, a super user can use
+.I lpc
+to abort the current daemon and start a new one.
+.SH
+waiting for \fIhost\fP to come up
+.IP
+This implies there is a daemon trying to connect to the remote
+machine named
+.I host
+to send the files in the local queue.
+If the remote machine is up,
+.I lpd
+on the remote machine is probably dead or
+hung and should be restarted as mentioned for
+.IR lpr .
+.SH
+sending to \fIhost\fP
+.IP
+The files should be in the process of being transferred to the remote
+.IR host .
+If not, the local daemon should be aborted and started with
+.IR lpc .
+.SH
+Warning: \fIprinter\fP is down
+.IP
+The printer has been marked as being unavailable with
+.IR lpc .
+.SH
+Warning: no daemon present
+.IP
+The \fIlpd\fP process overseeing
+the spooling queue, as specified in the ``lock'' file
+in that directory, does not exist. This normally occurs
+only when the daemon has unexpectedly died.
+The error log file for the printer and the \fIsyslogd\fP logs
+should be checked for a
+diagnostic from the deceased process.
+To restart an \fIlpd\fP, use
+.DS
+% lpc restart \fIprinter\fP
+.DE
+.SH
+no space on remote; waiting for queue to drain
+.IP
+This implies that there is insufficient disk space on the remote.
+If the file is large enough, there will never be enough space on
+the remote (even after the queue on the remote is empty). The solution here
+is to move the spooling queue or make more free space on the remote.
+.NH 2
+LPRM
+.SH
+lprm: \fIprinter\fP\|: cannot restart printer daemon
+.IP
+This case is the same as when
+.I lpr
+prints that the daemon cannot be started.
+.NH 2
+LPD
+.PP
+The
+.I lpd
+program can log many different messages using \fIsyslogd\fP\|(8).
+Most of these messages are about files that can not
+be opened and usually imply that the
+.I printcap
+file or the protection modes of the files are
+incorrect. Files may also be inaccessible if people
+manually manipulate the line printer system (i.e. they
+bypass the
+.I lpr
+program).
+.PP
+In addition to messages generated by
+.IR lpd ,
+any of the filters that
+.I lpd
+spawns may log messages using \fIsyslogd\fP or to the error log file
+(the file specified in the \fBlf\fP entry in \fIprintcap\fP\|).
+.NH 2
+LPC
+.PP
+.SH
+couldn't start printer
+.IP
+This case is the same as when
+.I lpr
+reports that the daemon cannot be started.
+.SH
+cannot examine spool directory
+.IP
+Error messages beginning with ``cannot ...'' are usually because of
+incorrect ownership or protection mode of the lock file, spooling
+directory or the
+.I lpc
+program.
diff --git a/usr.sbin/lpr/SMM.doc/Makefile b/usr.sbin/lpr/SMM.doc/Makefile
new file mode 100644
index 0000000..d80c8ce
--- /dev/null
+++ b/usr.sbin/lpr/SMM.doc/Makefile
@@ -0,0 +1,12 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/8/93
+# $FreeBSD$
+
+BINDIR= /usr/share/doc
+VOLUME= smm/07.lpd
+SRCS= 0.t 1.t 2.t 3.t 4.t 5.t 6.t 7.t
+MACROS= -ms
+
+USE_TBL=
+SRCDIR= ${.CURDIR}
+
+.include <bsd.doc.mk>
diff --git a/usr.sbin/lpr/SMM.doc/spell.ok b/usr.sbin/lpr/SMM.doc/spell.ok
new file mode 100644
index 0000000..bf31319
--- /dev/null
+++ b/usr.sbin/lpr/SMM.doc/spell.ok
@@ -0,0 +1,70 @@
+Aprinter
+Bprinter
+CRMOD
+Cprinter
+DecWriter
+Dprinter
+Eprinter
+LPC
+LPD
+Lpd
+Manual''SMM:5
+SIGINT
+SMM:5
+Topq
+XTABS
+adm
+af
+br
+daemon
+daemons
+dev
+f:of
+fs
+hosts.equiv
+hosts.lpd
+lf
+lg
+lib
+lp:br
+lp:sd
+lpc
+lpd
+lpd.lock
+lpf
+lpf:lf
+lprgroup
+makefile
+mx
+offline
+pl
+printcap
+pw
+py
+rc
+rcmd
+rg
+rlogin
+rp
+rs
+rsh
+rshd
+rvcat
+rvcat:af
+rvcat:mx
+sd
+src
+syslogd
+tf
+topq
+ucb
+ucbvax
+ucbvax:rp
+usr.lib
+va0:sd
+vaacct
+vad:of
+varian
+vaxlpd
+vpf
+vpf:tf
diff --git a/usr.sbin/lpr/chkprintcap/Makefile b/usr.sbin/lpr/chkprintcap/Makefile
new file mode 100644
index 0000000..ffffffb
--- /dev/null
+++ b/usr.sbin/lpr/chkprintcap/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../common_source
+
+PROG= chkprintcap
+MAN= chkprintcap.8
+SRCS= chkprintcap.c skimprintcap.c
+
+CFLAGS+= -I${.CURDIR}/../common_source
+
+DPADD= ${LIBLPR}
+LDADD= ${LIBLPR}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/chkprintcap/chkprintcap.8 b/usr.sbin/lpr/chkprintcap/chkprintcap.8
new file mode 100644
index 0000000..c2524a1
--- /dev/null
+++ b/usr.sbin/lpr/chkprintcap/chkprintcap.8
@@ -0,0 +1,99 @@
+.\" Copyright 1997 Massachusetts Institute of Technology
+.\"
+.\" Permission to use, copy, modify, and distribute this software and
+.\" its documentation for any purpose and without fee is hereby
+.\" granted, provided that both the above copyright notice and this
+.\" permission notice appear in all copies, that both the above
+.\" copyright notice and this permission notice appear in all
+.\" supporting documentation, and that the name of M.I.T. not be used
+.\" in advertising or publicity pertaining to distribution of the
+.\" software without specific, written prior permission. M.I.T. makes
+.\" no representations about the suitability of this software for any
+.\" purpose. It is provided "as is" without express or implied
+.\" warranty.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
+.\" ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
+.\" INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+.\" SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+.\" LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+.\" USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+.\" ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+.\" OR TORT (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 30, 1997
+.Dt CHKPRINTCAP 8
+.Os
+.Sh NAME
+.Nm chkprintcap
+.Nd check validity of entries in the print spooler database
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl f Ar printcap
+.Sh DESCRIPTION
+The
+.Nm
+utility scans a
+.Xr printcap 5
+database
+(named by the
+.Ar printcap
+argument, or by default
+.Pa /etc/printcap ) ,
+looking for entries which are invalid in one way or another.
+The following checks are currently implemented:
+.Bl -enum -offset indent
+.It
+.Sq Li tc=
+references were properly expanded
+.It
+.Sq Li tc=
+references did not form a loop
+.It
+No two printers share the same spool directory
+.Sq ( Li sd=
+capability).
+.El
+.Pp
+The
+.Nm
+utility exits with a status equal to the number of errors encountered before
+processing stopped.
+(In some cases, processing can stop before the
+entire file is scanned.)
+.Pp
+If the
+.Fl d
+flag is given,
+.Nm
+will attempt to create any missing spool directories, giving them
+.Sq Li u=rwx,go=rx
+(0755) mode, group
+.Sq Li daemon ,
+and the owner specified by the
+.Sq Li du=
+capability in the database (default 1, which corresponds to user
+.Sq Li daemon ) .
+.Sh SEE ALSO
+.Xr lpr 1 ,
+.Xr printcap 5 ,
+.Xr lpd 8
+.Sh AUTHORS
+The
+.Nm
+utility was written by
+.An Garrett A. Wollman Aq wollman@lcs.mit.edu .
+.Sh BUGS
+Not enough sanity-checking is done.
+At a minimum, the ownership and
+mode of the spool directories should also be checked.
+Other
+parameters whose value could cause
+.Xr lpd 8
+to fail should be diagnosed.
diff --git a/usr.sbin/lpr/chkprintcap/chkprintcap.c b/usr.sbin/lpr/chkprintcap/chkprintcap.c
new file mode 100644
index 0000000..91e8299
--- /dev/null
+++ b/usr.sbin/lpr/chkprintcap/chkprintcap.c
@@ -0,0 +1,318 @@
+/*
+ * Copyright 1997 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that both the above copyright notice and this
+ * permission notice appear in all copies, that both the above
+ * copyright notice and this permission notice appear in all
+ * supporting documentation, and that the name of M.I.T. not be used
+ * in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. M.I.T. makes
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
+ * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+static const char copyright[] =
+ "Copyright (C) 1997, Massachusetts Institute of Technology\r\n";
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <errno.h>
+#include <grp.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <sys/param.h> /* needed for lp.h but not used here */
+#include <dirent.h> /* ditto */
+#include "lp.h"
+#include "lp.local.h"
+#include "pathnames.h"
+#include "skimprintcap.h"
+
+static void check_spool_dirs(void);
+static int interpret_error(const struct printer *pp, int error);
+static void make_spool_dir(const struct printer *pp);
+static void note_spool_dir(const struct printer *pp, const struct stat *st);
+static void usage(void) __dead2;
+
+static int problems; /* number of problems encountered */
+
+/*
+ * chkprintcap - check the printcap file for syntactic and semantic errors
+ * Returns the number of problems found.
+ */
+int
+main(int argc, char **argv)
+{
+ struct skiminfo *skres;
+ char *pcap_fname;
+ int c, error, makedirs, more, queuecnt, verbosity;
+ struct printer myprinter, *pp;
+
+ makedirs = 0;
+ queuecnt = 0;
+ verbosity = 0;
+ pcap_fname = NULL;
+ pp = &myprinter;
+
+ while ((c = getopt(argc, argv, "df:v")) != -1) {
+ switch (c) {
+ case 'd':
+ makedirs = 1;
+ break;
+
+ case 'f':
+ pcap_fname = strdup(optarg);
+ setprintcap(pcap_fname);
+ break;
+
+ case 'v':
+ verbosity++;
+ break;
+
+ default:
+ usage();
+ }
+ }
+
+ if (optind != argc)
+ usage();
+
+ if (pcap_fname == NULL)
+ pcap_fname = strdup(_PATH_PRINTCAP);
+
+ /*
+ * Skim through the printcap file looking for simple user-mistakes
+ * which will produce the wrong result for the user, but which may
+ * be pretty hard for the user to notice. Such user-mistakes will
+ * only generate warning messages. The (fatal-) problem count will
+ * only be incremented if there is a system problem trying to read
+ * the printcap file.
+ */
+ skres = skim_printcap(pcap_fname, verbosity);
+ if (skres->fatalerr)
+ return (skres->fatalerr);
+
+ /*
+ * Now use the standard capability-db routines to check the values
+ * in each of the queues defined in the printcap file.
+ */
+ more = firstprinter(pp, &error);
+ if (interpret_error(pp, error) && more)
+ goto next;
+
+ while (more) {
+ struct stat stab;
+
+ queuecnt++;
+ errno = 0;
+ if (stat(pp->spool_dir, &stab) < 0) {
+ if (errno == ENOENT && makedirs) {
+ make_spool_dir(pp);
+ } else {
+ problems++;
+ warn("%s: %s", pp->printer, pp->spool_dir);
+ }
+ } else {
+ note_spool_dir(pp, &stab);
+ }
+
+ /* Make other queue-specific validity checks here... */
+
+next:
+ more = nextprinter(pp, &error);
+ if (interpret_error(pp, error) && more)
+ goto next;
+ }
+
+ check_spool_dirs();
+
+ if (queuecnt != skres->entries) {
+ warnx("WARNING: found %d entries when skimming %s,",
+ skres->entries, pcap_fname);
+ warnx("WARNING: but only found %d queues to process!",
+ queuecnt);
+ }
+ return (problems);
+}
+
+/*
+ * Interpret the error code. Returns 1 if we should skip to the next
+ * record (as this record is unlikely to make sense). If the problem
+ * is very severe, exit. Otherwise, return zero.
+ */
+static int
+interpret_error(const struct printer *pp, int error)
+{
+ switch(error) {
+ case PCAPERR_OSERR:
+ err(++problems, "reading printer database");
+ case PCAPERR_TCLOOP:
+ ++problems;
+ warnx("%s: loop detected in tc= expansion", pp->printer);
+ return 1;
+ case PCAPERR_TCOPEN:
+ warnx("%s: unresolved tc= expansion", pp->printer);
+ return 1;
+ case PCAPERR_SUCCESS:
+ break;
+ default:
+ errx(++problems, "unknown printcap library error %d", error);
+ }
+ return 0;
+}
+
+/*
+ * Keep the list of spool directories. Note that we don't whine
+ * until all spool directories are noted, so that all of the more serious
+ * problems are noted first. We keep the list sorted by st_dev and
+ * st_ino, so that the problem spool directories can be noted in
+ * a single loop.
+ */
+struct dirlist {
+ LIST_ENTRY(dirlist) link;
+ struct stat stab;
+ char *path;
+ char *printer;
+};
+
+static LIST_HEAD(, dirlist) dirlist;
+
+static int
+lessp(const struct dirlist *a, const struct dirlist *b)
+{
+ if (a->stab.st_dev == b->stab.st_dev)
+ return a->stab.st_ino < b->stab.st_ino;
+ return a->stab.st_dev < b->stab.st_dev;
+}
+
+static int
+equal(const struct dirlist *a, const struct dirlist *b)
+{
+ return ((a->stab.st_dev == b->stab.st_dev)
+ && (a->stab.st_ino == b->stab.st_ino));
+}
+
+static void
+note_spool_dir(const struct printer *pp, const struct stat *st)
+{
+ struct dirlist *dp, *dp2, *last;
+
+ dp = malloc(sizeof *dp);
+ if (dp == 0)
+ err(++problems, "malloc(%lu)", (u_long)sizeof *dp);
+
+ dp->stab = *st;
+ dp->printer = strdup(pp->printer);
+ if (dp->printer == 0)
+ err(++problems, "malloc(%lu)", strlen(pp->printer) + 1UL);
+ dp->path = strdup(pp->spool_dir);
+ if (dp->path == 0)
+ err(++problems, "malloc(%lu)", strlen(pp->spool_dir) + 1UL);
+
+ last = 0;
+ LIST_FOREACH(dp2, &dirlist, link) {
+ if(!lessp(dp, dp2))
+ break;
+ last = dp2;
+ }
+
+ if (last) {
+ LIST_INSERT_AFTER(last, dp, link);
+ } else {
+ LIST_INSERT_HEAD(&dirlist, dp, link);
+ }
+}
+
+static void
+check_spool_dirs(void)
+{
+ struct dirlist *dp, *dp2;
+
+ for (dp = LIST_FIRST(&dirlist); dp; dp = dp2) {
+ dp2 = LIST_NEXT(dp, link);
+
+ if (dp2 != 0 && equal(dp, dp2)) {
+ ++problems;
+ if (strcmp(dp->path, dp2->path) == 0) {
+ warnx("%s and %s share the same spool, %s",
+ dp->printer, dp2->printer, dp->path);
+ } else {
+ warnx("%s (%s) and %s (%s) are the same "
+ "directory", dp->path, dp->printer,
+ dp2->path, dp2->printer);
+ }
+ continue;
+ }
+ /* Should probably check owners and modes here. */
+ }
+}
+
+#ifndef SPOOL_DIR_MODE
+#define SPOOL_DIR_MODE (S_IRUSR | S_IWUSR | S_IXUSR \
+ | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH)
+#endif
+
+static void
+make_spool_dir(const struct printer *pp)
+{
+ char *sd = pp->spool_dir;
+ struct group *gr;
+ struct stat stab;
+
+ if (mkdir(sd, S_IRUSR | S_IXUSR) < 0) {
+ problems++;
+ warn("%s: mkdir %s", pp->printer, sd);
+ return;
+ }
+ gr = getgrnam("daemon");
+ if (gr == 0)
+ errx(++problems, "cannot locate daemon group");
+
+ if (chown(sd, pp->daemon_user, gr->gr_gid) < 0) {
+ ++problems;
+ warn("%s: cannot change ownership to %ld:%ld", sd,
+ (long)pp->daemon_user, (long)gr->gr_gid);
+ return;
+ }
+
+ if (chmod(sd, SPOOL_DIR_MODE) < 0) {
+ ++problems;
+ warn("%s: cannot change mode to %lo", sd, (long)SPOOL_DIR_MODE);
+ return;
+ }
+ if (stat(sd, &stab) < 0)
+ err(++problems, "stat: %s", sd);
+
+ note_spool_dir(pp, &stab);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage:\n\tchkprintcap [-dv] [-f printcapfile]\n");
+ exit(1);
+}
diff --git a/usr.sbin/lpr/chkprintcap/skimprintcap.c b/usr.sbin/lpr/chkprintcap/skimprintcap.c
new file mode 100644
index 0000000..da28282
--- /dev/null
+++ b/usr.sbin/lpr/chkprintcap/skimprintcap.c
@@ -0,0 +1,260 @@
+/*
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ * Copyright (c) 2001 - Garance Alistair Drosehn <gad@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.
+ *
+ * The views and conclusions contained in the software and documentation
+ * are those of the authors and should not be interpreted as representing
+ * official policies, either expressed or implied, of the FreeBSD Project.
+ *
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ */
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <grp.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <sys/param.h> /* needed for lp.h but not used here */
+#include <dirent.h> /* ditto */
+#include "lp.h"
+#include "lp.local.h"
+#include "skimprintcap.h"
+
+/*
+ * Save the canonical queue name of the entry that is currently being
+ * scanned, in case a warning message is printed for the current queue.
+ * Only the first 'QENTRY_MAXLEN' characters will be saved, since this
+ * is only for warning messages. The variable includes space for the
+ * string " (entry " and a trailing ")", when the scanner is in the
+ * middle of an entry. When the scanner is not in a specific entry,
+ * the variable will be the a null string.
+ */
+#define QENTRY_MAXLEN 30
+#define QENTRY_PREFIX " (entry "
+static char skim_entryname[sizeof(QENTRY_PREFIX) + QENTRY_MAXLEN + 2];
+
+/*
+ * isgraph is defined to work on an 'int', in the range 0 to 255, plus EOF.
+ * Define a wrapper which can take 'char', either signed or unsigned.
+ */
+#define isgraphch(Anychar) isgraph(((int) Anychar) & 255)
+
+struct skiminfo *
+skim_printcap(const char *pcap_fname, int verbosity)
+{
+ struct skiminfo *skinf;
+ char buff[BUFSIZ];
+ char *ch, *curline, *endfield, *lastchar;
+ FILE *pc_file;
+ int missing_nl;
+ enum {NO_CONTINUE, WILL_CONTINUE, BAD_CONTINUE} is_cont, had_cont;
+ enum {CMNT_LINE, ENTRY_LINE, TAB_LINE, TABERR_LINE} is_type, had_type;
+
+ skinf = malloc(sizeof(struct skiminfo));
+ memset(skinf, 0, sizeof(struct skiminfo));
+
+ pc_file = fopen(pcap_fname, "r");
+ if (pc_file == NULL) {
+ warn("fopen(%s)", pcap_fname);
+ skinf->fatalerr++;
+ return (skinf); /* fatal error */
+ }
+
+ skim_entryname[0] = '0';
+
+ is_cont = NO_CONTINUE;
+ is_type = CMNT_LINE;
+ errno = 0;
+ curline = fgets(buff, sizeof(buff), pc_file);
+ while (curline != NULL) {
+ skinf->lines++;
+
+ /* Check for the expected newline char, and remove it */
+ missing_nl = 0;
+ lastchar = strchr(curline, '\n');
+ if (lastchar != NULL)
+ *lastchar = '\0';
+ else {
+ lastchar = strchr(curline, '\0');
+ missing_nl = 1;
+ }
+ if (curline < lastchar)
+ lastchar--;
+
+ /*
+ * Check for `\' (continuation-character) at end of line.
+ * If there is none, then trim off spaces and check again.
+ * This would be a bad line because it looks like it is
+ * continued, but it will not be treated that way.
+ */
+ had_cont = is_cont;
+ is_cont = NO_CONTINUE;
+ if (*lastchar == '\\') {
+ is_cont = WILL_CONTINUE;
+ lastchar--;
+ } else {
+ while ((curline < lastchar) && !isgraphch(*lastchar))
+ lastchar--;
+ if (*lastchar == '\\')
+ is_cont = BAD_CONTINUE;
+ }
+
+ had_type = is_type;
+ is_type = CMNT_LINE;
+ switch (*curline) {
+ case '\0': /* treat zero-length line as comment */
+ case '#':
+ skinf->comments++;
+ break;
+ case ' ':
+ case '\t':
+ is_type = TAB_LINE;
+ break;
+ default:
+ is_type = ENTRY_LINE;
+ skinf->entries++;
+
+ /* pick up the queue name, to use in warning messages */
+ ch = curline;
+ while ((ch <= lastchar) && (*ch != ':') && (*ch != '|'))
+ ch++;
+ ch--; /* last char of queue name */
+ strcpy(skim_entryname, QENTRY_PREFIX);
+ if ((ch - curline) > QENTRY_MAXLEN) {
+ strncat(skim_entryname, curline, QENTRY_MAXLEN
+ - 1);
+ strcat(skim_entryname, "+");
+ } else {
+ strncat(skim_entryname, curline, (ch - curline
+ + 1));
+ }
+ strlcat(skim_entryname, ")", sizeof(skim_entryname));
+ break;
+ }
+
+ /*
+ * Check to see if the previous line was a bad contination
+ * line. The check is delayed until now so a warning message
+ * is not printed when a "bad continuation" is on a comment
+ * line, and it just "continues" into another comment line.
+ */
+ if (had_cont == BAD_CONTINUE) {
+ if ((had_type != CMNT_LINE) || (is_type != CMNT_LINE) ||
+ (verbosity > 1)) {
+ skinf->warnings++;
+ warnx("Warning: blanks after trailing '\\',"
+ " at line %d%s", skinf->lines - 1,
+ skim_entryname);
+ }
+ }
+
+ /* If we are no longer in an entry, then forget the name */
+ if ((had_cont != WILL_CONTINUE) && (is_type != ENTRY_LINE)) {
+ skim_entryname[0] = '\0';
+ }
+
+ /*
+ * Print out warning for missing newline, done down here
+ * so we are sure to have the right entry-name for it.
+ */
+ if (missing_nl) {
+ skinf->warnings++;
+ warnx("Warning: No newline at end of line %d%s",
+ skinf->lines, skim_entryname);
+ }
+
+ /*
+ * Check for start-of-entry lines which do not include a
+ * ":" character (to indicate the end of the name field).
+ * This can cause standard printcap processing to ignore
+ * ALL of the following lines.
+ * XXXXX - May need to allow for the list-of-names to
+ * continue on to the following line...
+ */
+ if (is_type == ENTRY_LINE) {
+ endfield = strchr(curline, ':');
+ if (endfield == NULL) {
+ skinf->warnings++;
+ warnx("Warning: No ':' to terminate name-field"
+ " at line %d%s", skinf->lines,
+ skim_entryname);
+ }
+ }
+
+ /*
+ * Now check for cases where this line is (or is-not) a
+ * continuation of the previous line, and a person skimming
+ * the file would assume it is not (or is) a continuation.
+ */
+ switch (had_cont) {
+ case NO_CONTINUE:
+ case BAD_CONTINUE:
+ if (is_type == TAB_LINE) {
+ skinf->warnings++;
+ warnx("Warning: values-line after line with"
+ " NO trailing '\\', at line %d%s",
+ skinf->lines, skim_entryname);
+ }
+ break;
+
+ case WILL_CONTINUE:
+ if (is_type == ENTRY_LINE) {
+ skinf->warnings++;
+ warnx("Warning: new entry starts after line"
+ " with trailing '\\', at line %d%s",
+ skinf->lines, skim_entryname);
+ }
+ break;
+ }
+
+ /* get another line from printcap and repeat loop */
+ curline = fgets(buff, sizeof(buff), pc_file);
+ }
+
+ if (errno != 0) {
+ warn("fgets(%s)", pcap_fname);
+ skinf->fatalerr++; /* fatal error */
+ }
+
+ if (skinf->warnings > 0)
+ warnx("%4d warnings from skimming %s", skinf->warnings,
+ pcap_fname);
+
+ if (verbosity)
+ warnx("%4d lines (%d comments), %d entries for %s",
+ skinf->lines, skinf->comments, skinf->entries, pcap_fname);
+
+ fclose(pc_file);
+ return (skinf);
+}
diff --git a/usr.sbin/lpr/chkprintcap/skimprintcap.h b/usr.sbin/lpr/chkprintcap/skimprintcap.h
new file mode 100644
index 0000000..af74b48
--- /dev/null
+++ b/usr.sbin/lpr/chkprintcap/skimprintcap.h
@@ -0,0 +1,44 @@
+/*
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ * Copyright (c) 2001 - Garance Alistair Drosehn <gad@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.
+ *
+ * The views and conclusions contained in the software and documentation
+ * are those of the authors and should not be interpreted as representing
+ * official policies, either expressed or implied, of the FreeBSD Project.
+ *
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ * $FreeBSD$
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ */
+
+struct skiminfo {
+ int comments;
+ int entries;
+ int fatalerr; /* fatal error, msg already printed */
+ int lines;
+ int warnings;
+};
+
+struct skiminfo *skim_printcap(const char *_pcap, int _verbosity);
diff --git a/usr.sbin/lpr/common_source/Makefile b/usr.sbin/lpr/common_source/Makefile
new file mode 100644
index 0000000..14db0b7
--- /dev/null
+++ b/usr.sbin/lpr/common_source/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+#
+# Library of internal routines for the print spooler suite.
+# Originally these were compiled separately into each program,
+# but the library makes it much easier to modularize them.
+#
+LIB= lpr
+INTERNALLIB=
+SRCS= common.c ctlinfo.c displayq.c matchjobs.c net.c \
+ printcap.c request.c rmjob.c startdaemon.c
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/lpr/common_source/common.c b/usr.sbin/lpr/common_source/common.c
new file mode 100644
index 0000000..d28ac1f
--- /dev/null
+++ b/usr.sbin/lpr/common_source/common.c
@@ -0,0 +1,771 @@
+/*
+ * 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.
+ * 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 char sccsid[] = "@(#)common.c 8.5 (Berkeley) 4/28/95";
+#endif /* not lint */
+#endif
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "lp.h"
+#include "lp.local.h"
+#include "pathnames.h"
+
+/*
+ * Routines and data common to all the line printer functions.
+ */
+char line[BUFSIZ];
+const char *progname; /* program name */
+
+extern uid_t uid, euid;
+
+static int compar(const void *_p1, const void *_p2);
+
+/*
+ * isdigit() takes a parameter of 'int', but expect values in the range
+ * of unsigned char. Define a wrapper which takes a value of type 'char',
+ * whether signed or unsigned, and ensure it ends up in the right range.
+ */
+#define isdigitch(Anychar) isdigit((u_char)(Anychar))
+
+/*
+ * Getline reads a line from the control file cfp, removes tabs, converts
+ * new-line to null and leaves it in line.
+ * Returns 0 at EOF or the number of characters read.
+ */
+int
+getline(FILE *cfp)
+{
+ register int linel = 0;
+ register char *lp = line;
+ register int c;
+
+ while ((c = getc(cfp)) != '\n' && (size_t)(linel+1) < sizeof(line)) {
+ if (c == EOF)
+ return(0);
+ if (c == '\t') {
+ do {
+ *lp++ = ' ';
+ linel++;
+ } while ((linel & 07) != 0 && (size_t)(linel+1) <
+ sizeof(line));
+ continue;
+ }
+ *lp++ = c;
+ linel++;
+ }
+ *lp++ = '\0';
+ return(linel);
+}
+
+/*
+ * Scan the current directory and make a list of daemon files sorted by
+ * creation time.
+ * Return the number of entries and a pointer to the list.
+ */
+int
+getq(const struct printer *pp, struct jobqueue *(*namelist[]))
+{
+ register struct dirent *d;
+ register struct jobqueue *q, **queue;
+ size_t arraysz, entrysz, nitems;
+ struct stat stbuf;
+ DIR *dirp;
+ int statres;
+
+ seteuid(euid);
+ if ((dirp = opendir(pp->spool_dir)) == NULL) {
+ seteuid(uid);
+ return (-1);
+ }
+ if (fstat(dirp->dd_fd, &stbuf) < 0)
+ goto errdone;
+ seteuid(uid);
+
+ /*
+ * Estimate the array size by taking the size of the directory file
+ * and dividing it by a multiple of the minimum size entry.
+ */
+ arraysz = (stbuf.st_size / 24);
+ queue = (struct jobqueue **)malloc(arraysz * sizeof(struct jobqueue *));
+ if (queue == NULL)
+ goto errdone;
+
+ nitems = 0;
+ while ((d = readdir(dirp)) != NULL) {
+ if (d->d_name[0] != 'c' || d->d_name[1] != 'f')
+ continue; /* daemon control files only */
+ seteuid(euid);
+ statres = stat(d->d_name, &stbuf);
+ seteuid(uid);
+ if (statres < 0)
+ continue; /* Doesn't exist */
+ entrysz = sizeof(struct jobqueue) - sizeof(q->job_cfname) +
+ strlen(d->d_name) + 1;
+ q = (struct jobqueue *)malloc(entrysz);
+ if (q == NULL)
+ goto errdone;
+ q->job_matched = 0;
+ q->job_processed = 0;
+ q->job_time = stbuf.st_mtime;
+ strcpy(q->job_cfname, d->d_name);
+ /*
+ * Check to make sure the array has space left and
+ * realloc the maximum size.
+ */
+ if (++nitems > arraysz) {
+ arraysz *= 2;
+ queue = (struct jobqueue **)realloc((char *)queue,
+ arraysz * sizeof(struct jobqueue *));
+ if (queue == NULL)
+ goto errdone;
+ }
+ queue[nitems-1] = q;
+ }
+ closedir(dirp);
+ if (nitems)
+ qsort(queue, nitems, sizeof(struct jobqueue *), compar);
+ *namelist = queue;
+ return(nitems);
+
+errdone:
+ closedir(dirp);
+ seteuid(uid);
+ return (-1);
+}
+
+/*
+ * Compare modification times.
+ */
+static int
+compar(const void *p1, const void *p2)
+{
+ const struct jobqueue *qe1, *qe2;
+
+ qe1 = *(const struct jobqueue * const *)p1;
+ qe2 = *(const struct jobqueue * const *)p2;
+
+ if (qe1->job_time < qe2->job_time)
+ return (-1);
+ if (qe1->job_time > qe2->job_time)
+ return (1);
+ /*
+ * At this point, the two files have the same last-modification time.
+ * return a result based on filenames, so that 'cfA001some.host' will
+ * come before 'cfA002some.host'. Since the jobid ('001') will wrap
+ * around when it gets to '999', we also assume that '9xx' jobs are
+ * older than '0xx' jobs.
+ */
+ if ((qe1->job_cfname[3] == '9') && (qe2->job_cfname[3] == '0'))
+ return (-1);
+ if ((qe1->job_cfname[3] == '0') && (qe2->job_cfname[3] == '9'))
+ return (1);
+ return (strcmp(qe1->job_cfname, qe2->job_cfname));
+}
+
+/*
+ * A simple routine to determine the job number for a print job based on
+ * the name of its control file. The algorithm used here may look odd, but
+ * the main issue is that all parts of `lpd', `lpc', `lpq' & `lprm' must be
+ * using the same algorithm, whatever that algorithm may be. If the caller
+ * provides a non-null value for ''hostpp', then this returns a pointer to
+ * the start of the hostname (or IP address?) as found in the filename.
+ *
+ * Algorithm: The standard `cf' file has the job number start in position 4,
+ * but some implementations have that as an extra file-sequence letter, and
+ * start the job number in position 5. The job number is usually three bytes,
+ * but may be as many as five. Confusing matters still more, some Windows
+ * print servers will append an IP address to the job number, instead of
+ * the expected hostname. So, if the job number ends with a '.', then
+ * assume the correct jobnum value is the first three digits.
+ */
+int
+calc_jobnum(const char *cfname, const char **hostpp)
+{
+ int jnum;
+ const char *cp, *numstr, *hoststr;
+
+ numstr = cfname + 3;
+ if (!isdigitch(*numstr))
+ numstr++;
+ jnum = 0;
+ for (cp = numstr; (cp < numstr + 5) && isdigitch(*cp); cp++)
+ jnum = jnum * 10 + (*cp - '0');
+ hoststr = cp;
+
+ /*
+ * If the filename was built with an IP number instead of a hostname,
+ * then recalculate using only the first three digits found.
+ */
+ while(isdigitch(*cp))
+ cp++;
+ if (*cp == '.') {
+ jnum = 0;
+ for (cp = numstr; (cp < numstr + 3) && isdigitch(*cp); cp++)
+ jnum = jnum * 10 + (*cp - '0');
+ hoststr = cp;
+ }
+ if (hostpp != NULL)
+ *hostpp = hoststr;
+ return (jnum);
+}
+
+/* sleep n milliseconds */
+void
+delay(int millisec)
+{
+ struct timeval tdelay;
+
+ if (millisec <= 0 || millisec > 10000)
+ fatal((struct printer *)0, /* fatal() knows how to deal */
+ "unreasonable delay period (%d)", millisec);
+ tdelay.tv_sec = millisec / 1000;
+ tdelay.tv_usec = millisec * 1000 % 1000000;
+ (void) select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &tdelay);
+}
+
+char *
+lock_file_name(const struct printer *pp, char *buf, size_t len)
+{
+ static char staticbuf[MAXPATHLEN];
+
+ if (buf == 0)
+ buf = staticbuf;
+ if (len == 0)
+ len = MAXPATHLEN;
+
+ if (pp->lock_file[0] == '/')
+ strlcpy(buf, pp->lock_file, len);
+ else
+ snprintf(buf, len, "%s/%s", pp->spool_dir, pp->lock_file);
+
+ return buf;
+}
+
+char *
+status_file_name(const struct printer *pp, char *buf, size_t len)
+{
+ static char staticbuf[MAXPATHLEN];
+
+ if (buf == 0)
+ buf = staticbuf;
+ if (len == 0)
+ len = MAXPATHLEN;
+
+ if (pp->status_file[0] == '/')
+ strlcpy(buf, pp->status_file, len);
+ else
+ snprintf(buf, len, "%s/%s", pp->spool_dir, pp->status_file);
+
+ return buf;
+}
+
+/*
+ * Routine to change operational state of a print queue. The operational
+ * state is indicated by the access bits on the lock file for the queue.
+ * At present, this is only called from various routines in lpc/cmds.c.
+ *
+ * XXX - Note that this works by changing access-bits on the
+ * file, and you can only do that if you are the owner of
+ * the file, or root. Thus, this won't really work for
+ * userids in the "LPR_OPER" group, unless lpc is running
+ * setuid to root (or maybe setuid to daemon).
+ * Generally lpc is installed setgid to daemon, but does
+ * not run setuid.
+ */
+int
+set_qstate(int action, const char *lfname)
+{
+ struct stat stbuf;
+ mode_t chgbits, newbits, oldmask;
+ const char *failmsg, *okmsg;
+ static const char *nomsg = "no state msg";
+ int chres, errsav, fd, res, statres;
+
+ /*
+ * Find what the current access-bits are.
+ */
+ memset(&stbuf, 0, sizeof(stbuf));
+ seteuid(euid);
+ statres = stat(lfname, &stbuf);
+ errsav = errno;
+ seteuid(uid);
+ if ((statres < 0) && (errsav != ENOENT)) {
+ printf("\tcannot stat() lock file\n");
+ return (SQS_STATFAIL);
+ /* NOTREACHED */
+ }
+
+ /*
+ * Determine which bit(s) should change for the requested action.
+ */
+ chgbits = stbuf.st_mode;
+ newbits = LOCK_FILE_MODE;
+ okmsg = NULL;
+ failmsg = NULL;
+ if (action & SQS_QCHANGED) {
+ chgbits |= LFM_RESET_QUE;
+ newbits |= LFM_RESET_QUE;
+ /* The okmsg is not actually printed for this case. */
+ okmsg = nomsg;
+ failmsg = "set queue-changed";
+ }
+ if (action & SQS_DISABLEQ) {
+ chgbits |= LFM_QUEUE_DIS;
+ newbits |= LFM_QUEUE_DIS;
+ okmsg = "queuing disabled";
+ failmsg = "disable queuing";
+ }
+ if (action & SQS_STOPP) {
+ chgbits |= LFM_PRINT_DIS;
+ newbits |= LFM_PRINT_DIS;
+ okmsg = "printing disabled";
+ failmsg = "disable printing";
+ if (action & SQS_DISABLEQ) {
+ okmsg = "printer and queuing disabled";
+ failmsg = "disable queuing and printing";
+ }
+ }
+ if (action & SQS_ENABLEQ) {
+ chgbits &= ~LFM_QUEUE_DIS;
+ newbits &= ~LFM_QUEUE_DIS;
+ okmsg = "queuing enabled";
+ failmsg = "enable queuing";
+ }
+ if (action & SQS_STARTP) {
+ chgbits &= ~LFM_PRINT_DIS;
+ newbits &= ~LFM_PRINT_DIS;
+ okmsg = "printing enabled";
+ failmsg = "enable printing";
+ }
+ if (okmsg == NULL) {
+ /* This routine was called with an invalid action. */
+ printf("\t<error in set_qstate!>\n");
+ return (SQS_PARMERR);
+ /* NOTREACHED */
+ }
+
+ res = 0;
+ if (statres >= 0) {
+ /* The file already exists, so change the access. */
+ seteuid(euid);
+ chres = chmod(lfname, chgbits);
+ errsav = errno;
+ seteuid(uid);
+ res = SQS_CHGOK;
+ if (chres < 0)
+ res = SQS_CHGFAIL;
+ } else if (newbits == LOCK_FILE_MODE) {
+ /*
+ * The file does not exist, but the state requested is
+ * the same as the default state when no file exists.
+ * Thus, there is no need to create the file.
+ */
+ res = SQS_SKIPCREOK;
+ } else {
+ /*
+ * The file did not exist, so create it with the
+ * appropriate access bits for the requested action.
+ * Push a new umask around that create, to make sure
+ * all the read/write bits are set as desired.
+ */
+ oldmask = umask(S_IWOTH);
+ seteuid(euid);
+ fd = open(lfname, O_WRONLY|O_CREAT, newbits);
+ errsav = errno;
+ seteuid(uid);
+ umask(oldmask);
+ res = SQS_CREFAIL;
+ if (fd >= 0) {
+ res = SQS_CREOK;
+ close(fd);
+ }
+ }
+
+ switch (res) {
+ case SQS_CHGOK:
+ case SQS_CREOK:
+ case SQS_SKIPCREOK:
+ if (okmsg != nomsg)
+ printf("\t%s\n", okmsg);
+ break;
+ case SQS_CREFAIL:
+ printf("\tcannot create lock file: %s\n",
+ strerror(errsav));
+ break;
+ default:
+ printf("\tcannot %s: %s\n", failmsg, strerror(errsav));
+ break;
+ }
+
+ return (res);
+}
+
+/* routine to get a current timestamp, optionally in a standard-fmt string */
+void
+lpd_gettime(struct timespec *tsp, char *strp, size_t strsize)
+{
+ struct timespec local_ts;
+ struct timeval btime;
+ char tempstr[TIMESTR_SIZE];
+#ifdef STRFTIME_WRONG_z
+ char *destp;
+#endif
+
+ if (tsp == NULL)
+ tsp = &local_ts;
+
+ /* some platforms have a routine called clock_gettime, but the
+ * routine does nothing but return "not implemented". */
+ memset(tsp, 0, sizeof(struct timespec));
+ if (clock_gettime(CLOCK_REALTIME, tsp)) {
+ /* nanosec-aware rtn failed, fall back to microsec-aware rtn */
+ memset(tsp, 0, sizeof(struct timespec));
+ gettimeofday(&btime, NULL);
+ tsp->tv_sec = btime.tv_sec;
+ tsp->tv_nsec = btime.tv_usec * 1000;
+ }
+
+ /* caller may not need a character-ized version */
+ if ((strp == NULL) || (strsize < 1))
+ return;
+
+ strftime(tempstr, TIMESTR_SIZE, LPD_TIMESTAMP_PATTERN,
+ localtime(&tsp->tv_sec));
+
+ /*
+ * This check is for implementations of strftime which treat %z
+ * (timezone as [+-]hhmm ) like %Z (timezone as characters), or
+ * completely ignore %z. This section is not needed on freebsd.
+ * I'm not sure this is completely right, but it should work OK
+ * for EST and EDT...
+ */
+#ifdef STRFTIME_WRONG_z
+ destp = strrchr(tempstr, ':');
+ if (destp != NULL) {
+ destp += 3;
+ if ((*destp != '+') && (*destp != '-')) {
+ char savday[6];
+ int tzmin = timezone / 60;
+ int tzhr = tzmin / 60;
+ if (daylight)
+ tzhr--;
+ strcpy(savday, destp + strlen(destp) - 4);
+ snprintf(destp, (destp - tempstr), "%+03d%02d",
+ (-1*tzhr), tzmin % 60);
+ strcat(destp, savday);
+ }
+ }
+#endif
+
+ if (strsize > TIMESTR_SIZE) {
+ strsize = TIMESTR_SIZE;
+ strp[TIMESTR_SIZE+1] = '\0';
+ }
+ strlcpy(strp, tempstr, strsize);
+}
+
+/* routines for writing transfer-statistic records */
+void
+trstat_init(struct printer *pp, const char *fname, int filenum)
+{
+ register const char *srcp;
+ register char *destp, *endp;
+
+ /*
+ * Figure out the job id of this file. The filename should be
+ * 'cf', 'df', or maybe 'tf', followed by a letter (or sometimes
+ * two), followed by the jobnum, followed by a hostname.
+ * The jobnum is usually 3 digits, but might be as many as 5.
+ * Note that some care has to be taken parsing this, as the
+ * filename could be coming from a remote-host, and thus might
+ * not look anything like what is expected...
+ */
+ memset(pp->jobnum, 0, sizeof(pp->jobnum));
+ pp->jobnum[0] = '0';
+ srcp = strchr(fname, '/');
+ if (srcp == NULL)
+ srcp = fname;
+ destp = &(pp->jobnum[0]);
+ endp = destp + 5;
+ while (*srcp != '\0' && (*srcp < '0' || *srcp > '9'))
+ srcp++;
+ while (*srcp >= '0' && *srcp <= '9' && destp < endp)
+ *(destp++) = *(srcp++);
+
+ /* get the starting time in both numeric and string formats, and
+ * save those away along with the file-number */
+ pp->jobdfnum = filenum;
+ lpd_gettime(&pp->tr_start, pp->tr_timestr, (size_t)TIMESTR_SIZE);
+
+ return;
+}
+
+void
+trstat_write(struct printer *pp, tr_sendrecv sendrecv, size_t bytecnt,
+ const char *userid, const char *otherhost, const char *orighost)
+{
+#define STATLINE_SIZE 1024
+ double trtime;
+ size_t remspace;
+ int statfile;
+ char thishost[MAXHOSTNAMELEN], statline[STATLINE_SIZE];
+ char *eostat;
+ const char *lprhost, *recvdev, *recvhost, *rectype;
+ const char *sendhost, *statfname;
+#define UPD_EOSTAT(xStr) do { \
+ eostat = strchr(xStr, '\0'); \
+ remspace = eostat - xStr; \
+} while(0)
+
+ lpd_gettime(&pp->tr_done, NULL, (size_t)0);
+ trtime = DIFFTIME_TS(pp->tr_done, pp->tr_start);
+
+ gethostname(thishost, sizeof(thishost));
+ lprhost = sendhost = recvhost = recvdev = NULL;
+ switch (sendrecv) {
+ case TR_SENDING:
+ rectype = "send";
+ statfname = pp->stat_send;
+ sendhost = thishost;
+ recvhost = otherhost;
+ break;
+ case TR_RECVING:
+ rectype = "recv";
+ statfname = pp->stat_recv;
+ sendhost = otherhost;
+ recvhost = thishost;
+ break;
+ case TR_PRINTING:
+ /*
+ * This case is for copying to a device (presumably local,
+ * though filters using things like 'net/CAP' can confuse
+ * this assumption...).
+ */
+ rectype = "prnt";
+ statfname = pp->stat_send;
+ sendhost = thishost;
+ recvdev = _PATH_DEFDEVLP;
+ if (pp->lp) recvdev = pp->lp;
+ break;
+ default:
+ /* internal error... should we syslog/printf an error? */
+ return;
+ }
+ if (statfname == NULL)
+ return;
+
+ /*
+ * the original-host and userid are found out by reading thru the
+ * cf (control-file) for the job. Unfortunately, on incoming jobs
+ * the df's (data-files) are sent before the matching cf, so the
+ * orighost & userid are generally not-available for incoming jobs.
+ *
+ * (it would be nice to create a work-around for that..)
+ */
+ if (orighost && (*orighost != '\0'))
+ lprhost = orighost;
+ else
+ lprhost = ".na.";
+ if (*userid == '\0')
+ userid = NULL;
+
+ /*
+ * Format of statline.
+ * Some of the keywords listed here are not implemented here, but
+ * they are listed to reserve the meaning for a given keyword.
+ * Fields are separated by a blank. The fields in statline are:
+ * <tstamp> - time the transfer started
+ * <ptrqueue> - name of the printer queue (the short-name...)
+ * <hname> - hostname the file originally came from (the
+ * 'lpr host'), if known, or "_na_" if not known.
+ * <xxx> - id of job from that host (generally three digits)
+ * <n> - file count (# of file within job)
+ * <rectype> - 4-byte field indicating the type of transfer
+ * statistics record. "send" means it's from the
+ * host sending a datafile, "recv" means it's from
+ * a host as it receives a datafile.
+ * user=<userid> - user who sent the job (if known)
+ * secs=<n> - seconds it took to transfer the file
+ * bytes=<n> - number of bytes transfered (ie, "bytecount")
+ * bps=<n.n>e<n> - Bytes/sec (if the transfer was "big enough"
+ * for this to be useful)
+ * ! top=<str> - type of printer (if the type is defined in
+ * printcap, and if this statline is for sending
+ * a file to that ptr)
+ * ! qls=<n> - queue-length at start of send/print-ing a job
+ * ! qle=<n> - queue-length at end of send/print-ing a job
+ * sip=<addr> - IP address of sending host, only included when
+ * receiving a job.
+ * shost=<hname> - sending host (if that does != the original host)
+ * rhost=<hname> - hostname receiving the file (ie, "destination")
+ * rdev=<dev> - device receiving the file, when the file is being
+ * send to a device instead of a remote host.
+ *
+ * Note: A single print job may be transferred multiple times. The
+ * original 'lpr' occurs on one host, and that original host might
+ * send to some interim host (or print server). That interim host
+ * might turn around and send the job to yet another host (most likely
+ * the real printer). The 'shost=' parameter is only included if the
+ * sending host for this particular transfer is NOT the same as the
+ * host which did the original 'lpr'.
+ *
+ * Many values have 'something=' tags before them, because they are
+ * in some sense "optional", or their order may vary. "Optional" may
+ * mean in the sense that different SITES might choose to have other
+ * fields in the record, or that some fields are only included under
+ * some circumstances. Programs processing these records should not
+ * assume the order or existence of any of these keyword fields.
+ */
+ snprintf(statline, STATLINE_SIZE, "%s %s %s %s %03ld %s",
+ pp->tr_timestr, pp->printer, lprhost, pp->jobnum,
+ pp->jobdfnum, rectype);
+ UPD_EOSTAT(statline);
+
+ if (userid != NULL) {
+ snprintf(eostat, remspace, " user=%s", userid);
+ UPD_EOSTAT(statline);
+ }
+ snprintf(eostat, remspace, " secs=%#.2f bytes=%lu", trtime,
+ (unsigned long)bytecnt);
+ UPD_EOSTAT(statline);
+
+ /*
+ * The bps field duplicates info from bytes and secs, so do
+ * not bother to include it for very small files.
+ */
+ if ((bytecnt > 25000) && (trtime > 1.1)) {
+ snprintf(eostat, remspace, " bps=%#.2e",
+ ((double)bytecnt/trtime));
+ UPD_EOSTAT(statline);
+ }
+
+ if (sendrecv == TR_RECVING) {
+ if (remspace > 5+strlen(from_ip) ) {
+ snprintf(eostat, remspace, " sip=%s", from_ip);
+ UPD_EOSTAT(statline);
+ }
+ }
+ if (0 != strcmp(lprhost, sendhost)) {
+ if (remspace > 7+strlen(sendhost) ) {
+ snprintf(eostat, remspace, " shost=%s", sendhost);
+ UPD_EOSTAT(statline);
+ }
+ }
+ if (recvhost) {
+ if (remspace > 7+strlen(recvhost) ) {
+ snprintf(eostat, remspace, " rhost=%s", recvhost);
+ UPD_EOSTAT(statline);
+ }
+ }
+ if (recvdev) {
+ if (remspace > 6+strlen(recvdev) ) {
+ snprintf(eostat, remspace, " rdev=%s", recvdev);
+ UPD_EOSTAT(statline);
+ }
+ }
+ if (remspace > 1) {
+ strcpy(eostat, "\n");
+ } else {
+ /* probably should back up to just before the final " x=".. */
+ strcpy(statline+STATLINE_SIZE-2, "\n");
+ }
+ statfile = open(statfname, O_WRONLY|O_APPEND, 0664);
+ if (statfile < 0) {
+ /* statfile was given, but we can't open it. should we
+ * syslog/printf this as an error? */
+ return;
+ }
+ write(statfile, statline, strlen(statline));
+ close(statfile);
+
+ return;
+#undef UPD_EOSTAT
+}
+
+#include <stdarg.h>
+
+void
+fatal(const struct printer *pp, const char *msg, ...)
+{
+ va_list ap;
+ va_start(ap, msg);
+ /* this error message is being sent to the 'from_host' */
+ if (from_host != local_host)
+ (void)printf("%s: ", local_host);
+ (void)printf("%s: ", progname);
+ if (pp && pp->printer)
+ (void)printf("%s: ", pp->printer);
+ (void)vprintf(msg, ap);
+ va_end(ap);
+ (void)putchar('\n');
+ exit(1);
+}
+
+/*
+ * Close all file descriptors from START on up.
+ * This is a horrific kluge, since getdtablesize() might return
+ * ``infinity'', in which case we will be spending a long time
+ * closing ``files'' which were never open. Perhaps it would
+ * be better to close the first N fds, for some small value of N.
+ */
+void
+closeallfds(int start)
+{
+ int stop = getdtablesize();
+ for (; start < stop; start++)
+ close(start);
+}
+
diff --git a/usr.sbin/lpr/common_source/ctlinfo.c b/usr.sbin/lpr/common_source/ctlinfo.c
new file mode 100644
index 0000000..20a153f
--- /dev/null
+++ b/usr.sbin/lpr/common_source/ctlinfo.c
@@ -0,0 +1,914 @@
+/*
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ * Copyright (c) 2001 - Garance Alistair Drosehn <gad@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.
+ *
+ * The views and conclusions contained in the software and documentation
+ * are those of the authors and should not be interpreted as representing
+ * official policies, either expressed or implied, of the FreeBSD Project.
+ *
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ */
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+__FBSDID("$FreeBSD$");
+
+/*
+ * ctlinfo - This collection of routines will know everything there is to
+ * know about the information inside a control file ('cf*') which is used
+ * to describe a print job in lpr & friends. The eventual goal is that it
+ * will be the ONLY source file to know what's inside these control-files.
+ */
+
+/*
+ * Some define's useful for debuging.
+ * TRIGGERTEST_FNAME and DEBUGREADCF_FNAME, allow us to do testing on
+ * a per-spool-directory basis.
+ */
+/* #define TRIGGERTEST_FNAME "LpdTestRenameTF" */
+/* #define DEBUGREADCF_FNAME "LpdDebugReadCF" */
+/* #define LEAVE_TMPCF_FILES 1 */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include "ctlinfo.h"
+
+struct cjprivate {
+ struct cjobinfo pub;
+ char *cji_buff; /* buffer for getline */
+ char *cji_eobuff; /* last byte IN the buffer */
+ FILE *cji_fstream;
+ int cji_buffsize; /* # bytes in the buffer */
+ int cji_dumpit;
+};
+
+/*
+ * All the following take a parameter of 'int', but expect values in the
+ * range of unsigned char. Define wrappers which take values of type 'char',
+ * whether signed or unsigned, and ensure they end up in the right range.
+ */
+#define isdigitch(Anychar) isdigit((u_char)(Anychar))
+#define islowerch(Anychar) islower((u_char)(Anychar))
+#define isupperch(Anychar) isupper((u_char)(Anychar))
+#define tolowerch(Anychar) tolower((u_char)(Anychar))
+
+#define OTHER_USERID_CHARS "-_" /* special chars valid in a userid */
+
+#define roundup(x, y) ((((x)+((y)-1))/(y))*(y))
+
+/*
+ * This has to be large enough to fit the maximum length of a single line
+ * in a control-file, including the leading 'command id', a trailing '\n'
+ * and ending '\0'. The max size of an 'U'nlink line, for instance, is
+ * 1 ('U') + PATH_MAX (filename) + 2 ('\n\0'). The maximum 'H'ost line is
+ * 1 ('H') + NI_MAXHOST (remote hostname) + 2 ('\n\0'). Other lines can be
+ * even longer than those. So, pick some nice, large, arbitrary value.
+ */
+#define CTI_LINEMAX PATH_MAX+NI_MAXHOST+5
+
+extern const char *from_host; /* client's machine name */
+extern const char *from_ip; /* client machine's IP address */
+
+__BEGIN_DECLS
+void ctl_dumpcji(FILE *_dbg_stream, const char *_heading,
+ struct cjobinfo *_cjinf);
+static char *ctl_getline(struct cjobinfo *_cjinf);
+static void ctl_rewindcf(struct cjobinfo *_cjinf);
+char *ctl_rmjob(const char *_ptrname, const char *_cfname);
+__END_DECLS
+
+/*
+ * Here are some things which might be needed when compiling this under
+ * platforms other than FreeBSD.
+ */
+#ifndef __FreeBSD__
+# ifndef NAME_MAX
+# define NAME_MAX 255
+# endif
+# ifndef NI_MAXHOST
+# define NI_MAXHOST 1025
+# endif
+# ifndef PATH_MAX
+# define PATH_MAX 1024
+# endif
+__BEGIN_DECLS
+char *strdup(const char *_src);
+size_t strlcpy(char *_dst, const char *_src, size_t _siz);
+__END_DECLS
+#endif
+
+/*
+ * Control-files (cf*) have the following format.
+ *
+ * Each control-file describes a single job. It will list one or more
+ * "datafiles" (df*) which should be copied to some printer. Usually
+ * there is only one datafile per job. For the curious, RFC 1179 is an
+ * informal and out-of-date description of lpr/lpd circa 1990.
+ *
+ * Each line in the file gives an attribute of the job as a whole, or one
+ * of the datafiles in the job, or a "command" indicating something to do
+ * with one of the datafiles. Each line starts with an 'id' that indicates
+ * what that line is there for. The 'id' is historically a single byte,
+ * but may be multiple bytes (obviously it would be best if multi-byte ids
+ * started with some letter not already used as a single-byte id!).
+ * After the 'id', the remainder of the line will be the value of the
+ * indicated attribute, or a name of the datafile to be operated on.
+ *
+ * In the following lists of ids, the ids with a '!' in front of them are
+ * NOT explicitly supported by this version of lpd, or at least "not yet
+ * supported". They are only listed for reference purposes, so people
+ * won't be tempted to reuse the same id for a different purpose.
+ *
+ * The following are attributes of the job which should not appear more
+ * than once in a control file. Only the 'H' and 'P' lines are required
+ * by the RFC, but some implementations of lpr won't even get that right.
+ *
+ * ! A - [used by lprNG]
+ * B - As far as I know, this is never used as a single-byte id.
+ * Therefore, I intend to use it for multi-byte id codes.
+ * C - "class name" to display on banner page (this is sometimes
+ * used to hold options for print filters)
+ * ! D - [in lprNG, "timestamp" of when the job was submitted]
+ * ! E - "environment variables" to set [some versions of linux]
+ * H - "host name" of machine where the original 'lpr' was done
+ * I - "indent", the amount to indent output
+ * J - "job name" to display on banner page
+ * L - "literal" user's name as it should be displayed on the
+ * banner page (it is the existence of an 'L' line which
+ * indicates that a job should have a banner page).
+ * M - "mail", userid to mail to when done printing (with email
+ * going to 'M'@'H', so to speak).
+ * P - "person", the user's login name (e.g. for accounting)
+ * ! Q - [used by lprNG for queue-name]
+ * R - "resolution" in dpi, for some laser printer queues
+ * T - "title" for files sent thru 'pr'
+ * W - "width" to use for printing plain-text files
+ * Z - In BSD, "locale" to use for datafiles sent thru 'pr'.
+ * (this BSD usage should move to a different id...)
+ * [in lprNG - this line holds the "Z options"]
+ * 1 - "R font file" for files sent thru troff
+ * 2 - "I font file" for files sent thru troff
+ * 3 - "B font file" for files sent thru troff
+ * 4 - "S font file" for files sent thru troff
+ *
+ * The following are attributes attached to a datafile, and thus may
+ * appear multiple times in a control file (once per datafile):
+ *
+ * N - "name" of file (for display purposes, used by 'lpq')
+ * S - "stat() info" used for symbolic link ('lpr -s')
+ * security checks.
+ *
+ * The following indicate actions to take on a given datafile. The same
+ * datafile may appear on more than one "print this file" command in the
+ * control file. Note that ALL ids with lowercase letters are expected
+ * to be actions to "print this file":
+ *
+ * c - "file name", cifplot file to print. This action appears
+ * when the user has requested 'lpr -c'.
+ * d - "file name", dvi file to print, user requested 'lpr -d'
+ * f - "file name", a plain-text file to print = "standard"
+ * g - "file name", plot(1G) file to print, ie 'lpr -g'
+ * l - "file name", text file with control chars which should
+ * be printed literally, ie 'lpr -l' (note: some printers
+ * take this id as a request to print a postscript file,
+ * and because of *that* some OS's use 'l' to indicate
+ * that a datafile is a postscript file)
+ * n - "file name", ditroff(1) file to print, ie 'lpr -n'
+ * o - "file name", a postscript file to print. This id is
+ * described in the original RFC, but not much has been
+ * done with it. This 'lpr' does not generate control
+ * lines with 'o'-actions, but lpd's printjob processing
+ * will treat it the same as 'l'.
+ * p - "file name", text file to print with pr(1), ie 'lpr -p'
+ * t - "file name", troff(1) file to print, ie 'lpr -t'
+ * v - "file name", plain raster file to print
+ *
+ * U - "file name" of datafile to unlink (ie, remove file
+ * from spool directory. To be done in a 'Pass 2',
+ * AFTER having processed all datafiles in the job).
+ *
+ */
+
+void
+ctl_freeinf(struct cjobinfo *cjinf)
+{
+#define FREESTR(xStr) \
+ if (xStr != NULL) { \
+ free(xStr); \
+ xStr = NULL;\
+ }
+
+ struct cjprivate *cpriv;
+
+ if (cjinf == NULL)
+ return;
+ cpriv = cjinf->cji_priv;
+ if ((cpriv == NULL) || (cpriv != cpriv->pub.cji_priv)) {
+ syslog(LOG_ERR, "in ctl_freeinf(%p): invalid cjinf (cpriv %p)",
+ (void *)cjinf, (void *)cpriv);
+ return;
+ }
+
+ FREESTR(cpriv->pub.cji_accthost);
+ FREESTR(cpriv->pub.cji_acctuser);
+ FREESTR(cpriv->pub.cji_class);
+ FREESTR(cpriv->pub.cji_curqueue);
+ /* [cpriv->pub.cji_fname is part of cpriv-malloced area] */
+ FREESTR(cpriv->pub.cji_jobname);
+ FREESTR(cpriv->pub.cji_mailto);
+ FREESTR(cpriv->pub.cji_username);
+
+ if (cpriv->cji_fstream != NULL) {
+ fclose(cpriv->cji_fstream);
+ cpriv->cji_fstream = NULL;
+ }
+
+ cjinf->cji_priv = NULL;
+ free(cpriv);
+#undef FREESTR
+}
+
+#ifdef DEBUGREADCF_FNAME
+static FILE *ctl_dbgfile = NULL;
+static struct stat ctl_dbgstat;
+#endif
+static int ctl_dbgline = 0;
+
+struct cjobinfo *
+ctl_readcf(const char *ptrname, const char *cfname)
+{
+ int id;
+ char *lbuff;
+ void *cstart;
+ FILE *cfile;
+ struct cjprivate *cpriv;
+ struct cjobinfo *cjinf;
+ size_t msize, sroom, sroom2;
+
+ cfile = fopen(cfname, "r");
+ if (cfile == NULL) {
+ syslog(LOG_ERR, "%s: ctl_readcf error fopen(%s): %s",
+ ptrname, cfname, strerror(errno));
+ return NULL;
+ }
+
+ sroom = roundup(sizeof(struct cjprivate), 8);
+ sroom2 = sroom + strlen(cfname) + 1;
+ sroom2 = roundup(sroom2, 8);
+ msize = sroom2 + CTI_LINEMAX;
+ msize = roundup(msize, 8);
+ cstart = malloc(msize);
+ if (cstart == NULL)
+ return NULL;
+ memset(cstart, 0, msize);
+ cpriv = (struct cjprivate *)cstart;
+ cpriv->pub.cji_priv = cpriv;
+
+ cpriv->pub.cji_fname = (char *)cstart + sroom;
+ strcpy(cpriv->pub.cji_fname, cfname);
+ cpriv->cji_buff = (char *)cstart + sroom2;
+ cpriv->cji_buffsize = (int)(msize - sroom2);
+ cpriv->cji_eobuff = (char *)cstart + msize - 1;
+
+ cpriv->cji_fstream = cfile;
+ cpriv->pub.cji_curqueue = strdup(ptrname);
+
+ ctl_dbgline = 0;
+#ifdef DEBUGREADCF_FNAME
+ ctl_dbgfile = NULL;
+ id = stat(DEBUGREADCF_FNAME, &ctl_dbgstat);
+ if (id != -1) {
+ /* the file exists in this spool directory, write some simple
+ * debugging info to it */
+ ctl_dbgfile = fopen(DEBUGREADCF_FNAME, "a");
+ if (ctl_dbgfile != NULL) {
+ fprintf(ctl_dbgfile, "%s: s=%p r=%ld e=%p %p->%s\n",
+ ptrname, (void *)cpriv, (long)sroom,
+ cpriv->cji_eobuff, cpriv->pub.cji_fname,
+ cpriv->pub.cji_fname);
+ }
+ }
+#endif
+ /*
+ * Copy job-attribute values from control file to the struct of
+ * "public" information. In some cases, it is invalid for the
+ * value to be a null-string, so that is ignored.
+ */
+ cjinf = &(cpriv->pub);
+ lbuff = ctl_getline(cjinf);
+ while (lbuff != NULL) {
+ id = *lbuff++;
+ switch (id) {
+ case 'C':
+ cpriv->pub.cji_class = strdup(lbuff);
+ break;
+ case 'H':
+ if (*lbuff == '\0')
+ break;
+ cpriv->pub.cji_accthost = strdup(lbuff);
+ break;
+ case 'J':
+ cpriv->pub.cji_jobname = strdup(lbuff);
+ break;
+ case 'L':
+ cpriv->pub.cji_username = strdup(lbuff);
+ break;
+ case 'M':
+ /*
+ * No valid mail-to address would start with a minus.
+ * If this one does, it is probably some trickster who
+ * is trying to trigger options on sendmail. Ignore.
+ */
+ if (*lbuff == '-')
+ break;
+ if (*lbuff == '\0')
+ break;
+ cpriv->pub.cji_mailto = strdup(lbuff);
+ break;
+ case 'P':
+ if (*lbuff == '\0')
+ break;
+ /* The userid must not start with a minus sign */
+ if (*lbuff == '-')
+ *lbuff = '_';
+ cpriv->pub.cji_acctuser = strdup(lbuff);
+ break;
+ default:
+ if (islower(id)) {
+ cpriv->pub.cji_dfcount++;
+ }
+ break;
+ }
+ lbuff = ctl_getline(cjinf);
+ }
+
+ /* the 'H'ost and 'P'erson fields are *always* supposed to be there */
+ if (cpriv->pub.cji_accthost == NULL)
+ cpriv->pub.cji_accthost = strdup(".na.");
+ if (cpriv->pub.cji_acctuser == NULL)
+ cpriv->pub.cji_acctuser = strdup(".na.");
+
+#ifdef DEBUGREADCF_FNAME
+ if (ctl_dbgfile != NULL) {
+ if (cpriv->cji_dumpit)
+ ctl_dumpcji(ctl_dbgfile, "end readcf", &(cpriv->pub));
+ fclose(ctl_dbgfile);
+ ctl_dbgfile = NULL;
+ }
+#endif
+ return &(cpriv->pub);
+}
+
+/*
+ * This routine renames the temporary control file as received from some
+ * other (remote) host. That file will almost always with `tfA*', because
+ * recvjob.c creates the file by changing `c' to `t' in the original name
+ * for the control file. Now if you read the RFC, you would think that all
+ * control filenames start with `cfA*'. However, it seems there are some
+ * implementations which send control filenames which start with `cf'
+ * followed by *any* letter, so this routine can not assume what the third
+ * letter will (or will not) be. Sigh.
+ *
+ * So this will rewrite the temporary file to `rf*' (correcting any lines
+ * which need correcting), rename that `rf*' file to `cf*', and then remove
+ * the original `tf*' temporary file.
+ *
+ * The *main* purpose of this routine is to be paranoid about the contents
+ * of that control file. It is partially meant to protect against people
+ * TRYING to cause trouble (perhaps after breaking into root of some host
+ * that this host will accept print jobs from). The fact that we're willing
+ * to print jobs from some remote host does not mean that we should blindly
+ * do anything that host tells us to do.
+ *
+ * This is also meant to protect us from errors in other implementations of
+ * lpr, particularly since we may want to use some values from the control
+ * file as environment variables when it comes time to print, or as parameters
+ * to commands which will be exec'ed, or values in statistics records.
+ *
+ * This may also do some "conversions" between how different versions of
+ * lpr or lprNG define the contents of various lines in a control file.
+ *
+ * If there is an error, it returns a pointer to a descriptive error message.
+ * Error messages which are RETURNED (as opposed to syslog-ed) do not include
+ * the printer-queue name. Let the caller add that if it is wanted.
+ */
+char *
+ctl_renametf(const char *ptrname, const char *tfname)
+{
+ int chk3rd, has_uc, newfd, nogood, res;
+ FILE *newcf;
+ struct cjobinfo *cjinf;
+ char *lbuff, *slash, *cp;
+ char tfname2[NAME_MAX+1], cfname2[NAME_MAX+1];
+ char errm[CTI_LINEMAX];
+
+#ifdef TRIGGERTEST_FNAME
+ struct stat tstat;
+ res = stat(TRIGGERTEST_FNAME, &tstat);
+ if (res == -1) {
+ /*
+ * if the trigger file does NOT exist in this spool directory,
+ * then do the exact same steps that the pre-ctlinfo code had
+ * been doing. Ie, very little.
+ */
+ strlcpy(cfname2, tfname, sizeof(cfname2));
+ cfname2[0] = 'c';
+ res = link(tfname, cfname2);
+ if (res < 0) {
+ snprintf(errm, sizeof(errm),
+ "ctl_renametf error link(%s,%s): %s", tfname,
+ cfname2, strerror(errno));
+ return strdup(errm);
+ }
+ unlink(tfname);
+ return NULL;
+ }
+#endif
+ cjinf = NULL; /* in case of early jump to error_ret */
+ newcf = NULL; /* in case of early jump to error_ret */
+ *errm = '\0'; /* in case of early jump to error_ret */
+
+ chk3rd = tfname[2];
+ if ((tfname[0] != 't') || (tfname[1] != 'f') || (!isalpha(chk3rd))) {
+ snprintf(errm, sizeof(errm),
+ "ctl_renametf invalid filename: %s", tfname);
+ goto error_ret;
+ }
+
+ cjinf = ctl_readcf(ptrname, tfname);
+ if (cjinf == NULL) {
+ snprintf(errm, sizeof(errm),
+ "ctl_renametf error cti_readcf(%s)", tfname);
+ goto error_ret;
+ }
+
+ /*
+ * This uses open+fdopen instead of fopen because that combination
+ * gives us greater control over file-creation issues.
+ */
+ strlcpy(tfname2, tfname, sizeof(tfname2));
+ tfname2[0] = 'r'; /* rf<letter><job><hostname> */
+ newfd = open(tfname2, O_WRONLY|O_CREAT|O_TRUNC, 0660);
+ if (newfd == -1) {
+ snprintf(errm, sizeof(errm),
+ "ctl_renametf error open(%s): %s", tfname2,
+ strerror(errno));
+ goto error_ret;
+ }
+ newcf = fdopen(newfd, "w");
+ if (newcf == NULL) {
+ close(newfd);
+ snprintf(errm, sizeof(errm),
+ "ctl_renametf error fopen(%s): %s", tfname2,
+ strerror(errno));
+ goto error_ret;
+ }
+
+ /*
+ * Do extra sanity checks on some key job-attribute fields, and
+ * write them out first (thus making sure they are written in the
+ * order we generally expect them to be in).
+ */
+ /*
+ * Some lpr implementations on PC's set a null-string for their
+ * hostname. A MacOS 10 system which has not correctly setup
+ * /etc/hostconfig will claim a hostname of 'localhost'. Anything
+ * with blanks in it would be an invalid value for hostname. For
+ * any of these invalid hostname values, replace the given value
+ * with the name of the host that this job is coming from.
+ */
+ nogood = 0;
+ if (cjinf->cji_accthost == NULL)
+ nogood = 1;
+ else if (strcmp(cjinf->cji_accthost, ".na.") == 0)
+ nogood = 1;
+ else if (strcmp(cjinf->cji_accthost, "localhost") == 0)
+ nogood = 1;
+ else {
+ for (cp = cjinf->cji_accthost; *cp != '\0'; cp++) {
+ if (*cp <= ' ') {
+ nogood = 1;
+ break;
+ }
+ }
+ }
+ if (nogood)
+ fprintf(newcf, "H%s\n", from_host);
+ else
+ fprintf(newcf, "H%s\n", cjinf->cji_accthost);
+
+ /*
+ * Now do some sanity checks on the 'P' (original userid) value. Note
+ * that the 'P'erson line is the second line which is ALWAYS supposed
+ * to be present in a control file.
+ *
+ * There is no particularly good value to use for replacements, but
+ * at least make sure the value is something reasonable to use in
+ * environment variables and statistics records. Again, some PC
+ * implementations send a null-string for a value. Various Mac
+ * implementations will set whatever string the user has set for
+ * their 'Owner Name', which usually includes blanks, etc.
+ */
+ nogood = 0;
+ if (cjinf->cji_acctuser == NULL)
+ nogood = 1;
+ else if (strcmp(cjinf->cji_acctuser, ".na.") == 0)
+ ; /* No further checks needed... */
+ else {
+ has_uc = 0;
+ cp = cjinf->cji_acctuser;
+ if (*cp == '-')
+ *cp++ = '_';
+ for (; *cp != '\0'; cp++) {
+ if (islowerch(*cp) || isdigitch(*cp))
+ continue; /* Standard valid characters */
+ if (strchr(OTHER_USERID_CHARS, *cp) != NULL)
+ continue; /* Some more valid characters */
+ if (isupperch(*cp)) {
+ has_uc = 1; /* These may be valid... */
+ continue;
+ }
+ *cp = '_';
+ }
+ /*
+ * Some Windows hosts send print jobs where the correct userid
+ * has been converted to uppercase, and that can cause trouble
+ * for sites that expect the correct value (for something like
+ * accounting). On the other hand, some sites do use uppercase
+ * in their userids, so we can't blindly convert to lowercase.
+ */
+ if (has_uc && (getpwnam(cjinf->cji_acctuser) == NULL)) {
+ for (cp = cjinf->cji_acctuser; *cp != '\0'; cp++) {
+ if (isupperch(*cp))
+ *cp = tolowerch(*cp);
+ }
+ }
+ }
+ if (nogood)
+ fprintf(newcf, "P%s\n", ".na.");
+ else
+ fprintf(newcf, "P%s\n", cjinf->cji_acctuser);
+
+ /* No need for sanity checks on class, jobname, "literal" user. */
+ if (cjinf->cji_class != NULL)
+ fprintf(newcf, "C%s\n", cjinf->cji_class);
+ if (cjinf->cji_jobname != NULL)
+ fprintf(newcf, "J%s\n", cjinf->cji_jobname);
+ if (cjinf->cji_username != NULL)
+ fprintf(newcf, "L%s\n", cjinf->cji_username);
+
+ /*
+ * This should probably add more sanity checks on mailto value.
+ * Note that if the mailto value is "wrong", then there's no good
+ * way to know what the "correct" value would be, and we should not
+ * semd email to some random address. At least for now, just ignore
+ * any invalid values.
+ */
+ nogood = 0;
+ if (cjinf->cji_mailto == NULL)
+ nogood = 1;
+ else {
+ for (cp = cjinf->cji_mailto; *cp != '\0'; cp++) {
+ if (*cp <= ' ') {
+ nogood = 1;
+ break;
+ }
+ }
+ }
+ if (!nogood)
+ fprintf(newcf, "M%s\n", cjinf->cji_mailto);
+
+ /*
+ * Now go thru the old control file, copying all information which
+ * hasn't already been written into the new file.
+ */
+ ctl_rewindcf(cjinf);
+ lbuff = ctl_getline(cjinf);
+ while (lbuff != NULL) {
+ switch (lbuff[0]) {
+ case 'H':
+ case 'P':
+ case 'C':
+ case 'J':
+ case 'L':
+ case 'M':
+ /* already wrote values for these to the newcf */
+ break;
+ case 'N':
+ /* see comments under 'U'... */
+ if (cjinf->cji_dfcount == 0) {
+ /* in this case, 'N's will be done in 'U' */
+ break;
+ }
+ fprintf(newcf, "%s\n", lbuff);
+ break;
+ case 'U':
+ /*
+ * check for the very common case where the remote
+ * host had to process 'lpr -s -r', but it did not
+ * remove the Unlink line from the control file.
+ * Such Unlink lines will legitimately have a '/' in
+ * them, but it is the original lpr host which would
+ * have done the unlink of such files, and not any
+ * host receiving that job.
+ */
+ slash = strchr(lbuff, '/');
+ if (slash != NULL) {
+ break; /* skip this line */
+ }
+ /*
+ * Okay, another kind of broken lpr implementation
+ * is one which send datafiles, and Unlink's those
+ * datafiles, but never includes any PRINT request
+ * for those files. Experimentation shows that one
+ * copy of those datafiles should be printed with a
+ * format of 'f'. If this is an example of such a
+ * screwed-up control file, fix it here.
+ */
+ if (cjinf->cji_dfcount == 0) {
+ lbuff++;
+ if (strncmp(lbuff, "df", (size_t)2) == 0) {
+ fprintf(newcf, "f%s\n", lbuff);
+ fprintf(newcf, "U%s\n", lbuff);
+ fprintf(newcf, "N%s\n", lbuff);
+ }
+ break;
+ }
+ fprintf(newcf, "%s\n", lbuff);
+ break;
+ default:
+ fprintf(newcf, "%s\n", lbuff);
+ break;
+ }
+ lbuff = ctl_getline(cjinf);
+ }
+
+ ctl_freeinf(cjinf);
+ cjinf = NULL;
+
+ res = fclose(newcf);
+ newcf = NULL;
+ if (res != 0) {
+ snprintf(errm, sizeof(errm),
+ "ctl_renametf error fclose(%s): %s", tfname2,
+ strerror(errno));
+ goto error_ret;
+ }
+
+ strlcpy(cfname2, tfname, sizeof(cfname2));
+ cfname2[0] = 'c'; /* rename new file to 'cfA*' */
+ res = link(tfname2, cfname2);
+ if (res != 0) {
+ snprintf(errm, sizeof(errm),
+ "ctl_renametf error link(%s,%s): %s", tfname2, cfname2,
+ strerror(errno));
+ goto error_ret;
+ }
+
+ /* All the important work is done. Now just remove temp files */
+#ifdef LEAVE_TMPCF_FILES
+ {
+ struct stat tfstat;
+ size_t size1;
+ tfstat.st_size = 1; /* certainly invalid value */
+ res = stat(tfname, &tfstat);
+ size1 = tfstat.st_size;
+ tfstat.st_size = 2; /* certainly invalid value */
+ res = stat(tfname2, &tfstat);
+ /*
+ * If the sizes do not match, or either stat call failed,
+ * then do not remove the temp files, but just move them
+ * out of the way. This is so I can see what this routine
+ * had changed (and the files won't interfere with some
+ * later job coming in from the same host). In this case,
+ * we don't care if we clobber some previous file.
+ */
+ if (size1 != tfstat.st_size) {
+ strlcpy(cfname2, tfname, sizeof(cfname2));
+ strlcat(cfname2, "._T", sizeof(cfname2));
+ rename(tfname, cfname2);
+ strlcpy(cfname2, tfname2, sizeof(cfname2));
+ strlcat(cfname2, "._T", sizeof(cfname2));
+ rename(tfname2, cfname2);
+ return NULL;
+ }
+ }
+#endif
+ unlink(tfname);
+ unlink(tfname2);
+
+ return NULL;
+
+error_ret:
+ if (cjinf != NULL)
+ ctl_freeinf(cjinf);
+ if (newcf != NULL)
+ fclose(newcf);
+
+ if (*errm != '\0')
+ return strdup(errm);
+ return strdup("ctl_renametf internal (missed) error");
+}
+
+void
+ctl_rewindcf(struct cjobinfo *cjinf)
+{
+ struct cjprivate *cpriv;
+
+ if (cjinf == NULL)
+ return;
+ cpriv = cjinf->cji_priv;
+ if ((cpriv == NULL) || (cpriv != cpriv->pub.cji_priv)) {
+ syslog(LOG_ERR, "in ctl_rewindcf(%p): invalid cjinf (cpriv %p)",
+ (void *)cjinf, (void *)cpriv);
+ return;
+ }
+
+ rewind(cpriv->cji_fstream); /* assume no errors... :-) */
+}
+
+char *
+ctl_rmjob(const char *ptrname, const char *cfname)
+{
+ struct cjobinfo *cjinf;
+ char *lbuff;
+ char errm[CTI_LINEMAX];
+
+ cjinf = ctl_readcf(ptrname, cfname);
+ if (cjinf == NULL) {
+ snprintf(errm, sizeof(errm),
+ "ctl_renametf error cti_readcf(%s)", cfname);
+ return strdup(errm);
+ }
+
+ ctl_rewindcf(cjinf);
+ lbuff = ctl_getline(cjinf);
+ while (lbuff != NULL) {
+ /* obviously we need to fill in the following... */
+ switch (lbuff[0]) {
+ case 'S':
+ break;
+ case 'U':
+ break;
+ default:
+ break;
+ }
+ lbuff = ctl_getline(cjinf);
+ }
+
+ ctl_freeinf(cjinf);
+ cjinf = NULL;
+
+ return NULL;
+}
+
+/*
+ * The following routine was originally written to pin down a bug. It is
+ * no longer needed for that problem, but may be useful to keep around for
+ * other debugging.
+ */
+void
+ctl_dumpcji(FILE *dbg_stream, const char *heading, struct cjobinfo *cjinf)
+{
+#define PRINTSTR(xHdr,xStr) \
+ astr = xStr; \
+ ctl_dbgline++; \
+ fprintf(dbg_stream, "%4d] %12s = ", ctl_dbgline, xHdr); \
+ if (astr == NULL) \
+ fprintf(dbg_stream, "NULL\n"); \
+ else \
+ fprintf(dbg_stream, "%p -> %s\n", astr, astr)
+
+ struct cjprivate *cpriv;
+ char *astr;
+
+ if (cjinf == NULL) {
+ fprintf(dbg_stream,
+ "ctl_dumpcji: ptr to cjobinfo for '%s' is NULL\n",
+ heading);
+ return;
+ }
+ cpriv = cjinf->cji_priv;
+
+ fprintf(dbg_stream, "ctl_dumpcji: Dump '%s' of cjobinfo at %p->%p\n",
+ heading, (void *)cjinf, cpriv->cji_buff);
+
+ PRINTSTR("accthost.H", cpriv->pub.cji_accthost);
+ PRINTSTR("acctuser.P", cpriv->pub.cji_acctuser);
+ PRINTSTR("class.C", cpriv->pub.cji_class);
+ PRINTSTR("cf-qname", cpriv->pub.cji_curqueue);
+ PRINTSTR("cf-fname", cpriv->pub.cji_fname);
+ PRINTSTR("jobname.J", cpriv->pub.cji_jobname);
+ PRINTSTR("mailto.M", cpriv->pub.cji_mailto);
+ PRINTSTR("hdruser.L", cpriv->pub.cji_username);
+
+ ctl_dbgline++;
+ fprintf(dbg_stream, "%4d] %12s = ", ctl_dbgline, "*cjprivate");
+ if (cpriv->pub.cji_priv == NULL)
+ fprintf(dbg_stream, "NULL !!\n");
+ else
+ fprintf(dbg_stream, "%p\n", (void *)cpriv->pub.cji_priv);
+
+ fprintf(dbg_stream, "|- - - - --> Dump '%s' complete\n", heading);
+
+ /* flush output for the benefit of anyone doing a 'tail -f' */
+ fflush(dbg_stream);
+
+#undef PRINTSTR
+}
+
+/*
+ * This routine reads in the next line from the control-file, and removes
+ * the trailing newline character.
+ *
+ * Historical note: Earlier versions of this routine did tab-expansion for
+ * ALL lines read in, which did not make any sense for most of the lines
+ * in a control file. For the lines where tab-expansion is useful, it will
+ * now have to be done by the calling routine.
+ */
+static char *
+ctl_getline(struct cjobinfo *cjinf)
+{
+ char *strp, *nl;
+ struct cjprivate *cpriv;
+
+ if (cjinf == NULL)
+ return NULL;
+ cpriv = cjinf->cji_priv;
+ if ((cpriv == NULL) || (cpriv != cpriv->pub.cji_priv)) {
+ syslog(LOG_ERR, "in ctl_getline(%p): invalid cjinf (cpriv %p)",
+ (void *)cjinf, (void *)cpriv);
+ return NULL;
+ }
+
+ errno = 0;
+ strp = fgets(cpriv->cji_buff, cpriv->cji_buffsize, cpriv->cji_fstream);
+ if (strp == NULL) {
+ if (errno != 0)
+ syslog(LOG_ERR, "%s: ctl_getline error fgets(%s): %s",
+ cpriv->pub.cji_curqueue, cpriv->pub.cji_fname,
+ strerror(errno));
+ return NULL;
+ }
+ nl = strchr(strp, '\n');
+ if (nl != NULL)
+ *nl = '\0';
+
+#ifdef DEBUGREADCF_FNAME
+ /* I'd like to find out if the previous work to expand tabs was ever
+ * really used, and if so, on what lines and for what reason.
+ * Yes, all this work probably means I'm obsessed about this 'tab'
+ * issue, but isn't programming a matter of obsession?
+ */
+ {
+ int tabcnt;
+ char *ch;
+
+ tabcnt = 0;
+ ch = strp;
+ for (ch = strp; *ch != '\0'; ch++) {
+ if (*ch == '\t')
+ tabcnt++;
+ }
+
+ if (tabcnt && (ctl_dbgfile != NULL)) {
+ cpriv->cji_dumpit++;
+ fprintf(ctl_dbgfile, "%s: tabs=%d '%s'\n",
+ cpriv->pub.cji_fname, tabcnt, cpriv->cji_buff);
+ }
+ }
+#endif
+ return strp;
+}
diff --git a/usr.sbin/lpr/common_source/ctlinfo.h b/usr.sbin/lpr/common_source/ctlinfo.h
new file mode 100644
index 0000000..95fe572
--- /dev/null
+++ b/usr.sbin/lpr/common_source/ctlinfo.h
@@ -0,0 +1,73 @@
+/*
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ * Copyright (c) 2001 - Garance Alistair Drosehn <gad@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.
+ *
+ * The views and conclusions contained in the software and documentation
+ * are those of the authors and should not be interpreted as representing
+ * official policies, either expressed or implied, of the FreeBSD Project.
+ *
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ * $FreeBSD$
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ */
+
+/*
+ * ctlinfo - This collection of routines will know everything there is to
+ * know about the information inside a control file ('cf*') which is used
+ * to describe a print job in lpr & friends. The eventual goal is that it
+ * will be the ONLY source file to know what's inside these control-files.
+ */
+
+struct cjprivate; /* used internal to ctl* routines */
+
+struct cjobinfo {
+ int cji_dfcount; /* number of data files to print */
+ int cji_uncount; /* number of unlink-file requests */
+ char *cji_accthost; /* the host that this job came from,
+ * for accounting purposes (usually
+ * the host where the original 'lpr'
+ * was done) */
+ char *cji_acctuser; /* userid who should be charged for
+ * this job (usually, the userid which
+ * did the original 'lpr') */
+ char *cji_class; /* class-name */
+ char *cji_curqueue; /* printer-queue that this cf-file is
+ * curently sitting in (mainly used
+ * in syslog error messages) */
+ char *cji_fname; /* filename of the control file */
+ char *cji_jobname; /* job-name (for banner) */
+ char *cji_mailto; /* userid to send email to (or null) */
+ char *cji_username; /* "literal" user-name (for banner) or
+ * NULL if no banner-page is wanted */
+ struct cjprivate *cji_priv;
+};
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+
+__BEGIN_DECLS
+void ctl_freeinf(struct cjobinfo *_cjinf);
+struct cjobinfo *ctl_readcf(const char *_ptrname, const char *_cfname);
+char *ctl_renametf(const char *_ptrname, const char *_tfname);
+__END_DECLS
diff --git a/usr.sbin/lpr/common_source/displayq.c b/usr.sbin/lpr/common_source/displayq.c
new file mode 100644
index 0000000..6c03621
--- /dev/null
+++ b/usr.sbin/lpr/common_source/displayq.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.
+ * 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 char sccsid[] = "@(#)displayq.c 8.4 (Berkeley) 4/28/95";
+#endif /* not lint */
+#endif
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#define psignal foil_gcc_psignal
+#define sys_siglist foil_gcc_siglist
+#include <unistd.h>
+#undef psignal
+#undef sys_siglist
+
+#include "lp.h"
+#include "lp.local.h"
+#include "pathnames.h"
+
+/*
+ * Routines to display the state of the queue.
+ */
+#define JOBCOL 40 /* column for job # in -l format */
+#define OWNCOL 7 /* start of Owner column in normal */
+#define SIZCOL 62 /* start of Size column in normal */
+
+/*
+ * Stuff for handling job specifications
+ */
+extern uid_t uid, euid;
+
+static int col; /* column on screen */
+static char current[MAXNAMLEN+1]; /* current file being printed */
+static char file[MAXNAMLEN+1]; /* print file name */
+static int first; /* first file in ``files'' column? */
+static int garbage; /* # of garbage cf files */
+static int lflag; /* long output option */
+static int rank; /* order to be printed (-1=none, 0=active) */
+static long totsize; /* total print job size in bytes */
+
+static const char *head0 = "Rank Owner Job Files";
+static const char *head1 = "Total Size\n";
+
+static void alarmhandler(int _signo);
+static void warn(const struct printer *_pp);
+
+/*
+ * Display the current state of the queue. Format = 1 if long format.
+ */
+void
+displayq(struct printer *pp, int format)
+{
+ register struct jobqueue *q;
+ register int i, nitems, fd, ret;
+ char *cp, *endp;
+ struct jobqueue **queue;
+ struct stat statb;
+ FILE *fp;
+ void (*savealrm)(int);
+
+ lflag = format;
+ totsize = 0;
+ rank = -1;
+
+ if ((cp = checkremote(pp))) {
+ printf("Warning: %s\n", cp);
+ free(cp);
+ }
+
+ /*
+ * Print out local queue
+ * Find all the control files in the spooling directory
+ */
+ seteuid(euid);
+ if (chdir(pp->spool_dir) < 0)
+ fatal(pp, "cannot chdir to spooling directory: %s",
+ strerror(errno));
+ seteuid(uid);
+ if ((nitems = getq(pp, &queue)) < 0)
+ fatal(pp, "cannot examine spooling area\n");
+ seteuid(euid);
+ ret = stat(pp->lock_file, &statb);
+ seteuid(uid);
+ if (ret >= 0) {
+ if (statb.st_mode & LFM_PRINT_DIS) {
+ if (pp->remote)
+ printf("%s: ", local_host);
+ printf("Warning: %s is down: ", pp->printer);
+ seteuid(euid);
+ fd = open(pp->status_file, O_RDONLY|O_SHLOCK);
+ seteuid(uid);
+ if (fd >= 0) {
+ while ((i = read(fd, line, sizeof(line))) > 0)
+ (void) fwrite(line, 1, i, stdout);
+ (void) close(fd); /* unlocks as well */
+ } else
+ putchar('\n');
+ }
+ if (statb.st_mode & LFM_QUEUE_DIS) {
+ if (pp->remote)
+ printf("%s: ", local_host);
+ printf("Warning: %s queue is turned off\n",
+ pp->printer);
+ }
+ }
+
+ if (nitems) {
+ seteuid(euid);
+ fp = fopen(pp->lock_file, "r");
+ seteuid(uid);
+ if (fp == NULL)
+ warn(pp);
+ else {
+ /* get daemon pid */
+ cp = current;
+ endp = cp + sizeof(current) - 1;
+ while ((i = getc(fp)) != EOF && i != '\n') {
+ if (cp < endp)
+ *cp++ = i;
+ }
+ *cp = '\0';
+ i = atoi(current);
+ if (i <= 0) {
+ ret = -1;
+ } else {
+ seteuid(euid);
+ ret = kill(i, 0);
+ seteuid(uid);
+ }
+ if (ret < 0) {
+ warn(pp);
+ } else {
+ /* read current file name */
+ cp = current;
+ endp = cp + sizeof(current) - 1;
+ while ((i = getc(fp)) != EOF && i != '\n') {
+ if (cp < endp)
+ *cp++ = i;
+ }
+ *cp = '\0';
+ /*
+ * Print the status file.
+ */
+ if (pp->remote)
+ printf("%s: ", local_host);
+ seteuid(euid);
+ fd = open(pp->status_file, O_RDONLY|O_SHLOCK);
+ seteuid(uid);
+ if (fd >= 0) {
+ while ((i = read(fd, line,
+ sizeof(line))) > 0)
+ fwrite(line, 1, i, stdout);
+ close(fd); /* unlocks as well */
+ } else
+ putchar('\n');
+ }
+ (void) fclose(fp);
+ }
+ /*
+ * Now, examine the control files and print out the jobs to
+ * be done for each user.
+ */
+ if (!lflag)
+ header();
+ for (i = 0; i < nitems; i++) {
+ q = queue[i];
+ inform(pp, q->job_cfname);
+ free(q);
+ }
+ free(queue);
+ }
+ if (!pp->remote) {
+ if (nitems == 0)
+ puts("no entries");
+ return;
+ }
+
+ /*
+ * Print foreign queue
+ * Note that a file in transit may show up in either queue.
+ */
+ if (nitems)
+ putchar('\n');
+ (void) snprintf(line, sizeof(line), "%c%s", format ? '\4' : '\3',
+ pp->remote_queue);
+ cp = line;
+ for (i = 0; i < requests && cp-line+10 < sizeof(line) - 1; i++) {
+ cp += strlen(cp);
+ (void) sprintf(cp, " %d", requ[i]);
+ }
+ for (i = 0; i < users && cp - line + 1 + strlen(user[i]) <
+ sizeof(line) - 1; i++) {
+ cp += strlen(cp);
+ *cp++ = ' ';
+ (void) strcpy(cp, user[i]);
+ }
+ strcat(line, "\n");
+ savealrm = signal(SIGALRM, alarmhandler);
+ alarm(pp->conn_timeout);
+ fd = getport(pp, pp->remote_host, 0);
+ alarm(0);
+ (void)signal(SIGALRM, savealrm);
+ if (fd < 0) {
+ if (from_host != local_host)
+ printf("%s: ", local_host);
+ printf("connection to %s is down\n", pp->remote_host);
+ }
+ else {
+ i = strlen(line);
+ if (write(fd, line, i) != i)
+ fatal(pp, "Lost connection");
+ while ((i = read(fd, line, sizeof(line))) > 0)
+ (void) fwrite(line, 1, i, stdout);
+ (void) close(fd);
+ }
+}
+
+/*
+ * Print a warning message if there is no daemon present.
+ */
+static void
+warn(const struct printer *pp)
+{
+ if (pp->remote)
+ printf("%s: ", local_host);
+ puts("Warning: no daemon present");
+ current[0] = '\0';
+}
+
+/*
+ * Print the header for the short listing format
+ */
+void
+header(void)
+{
+ printf("%s", head0);
+ col = strlen(head0)+1;
+ blankfill(SIZCOL);
+ printf("%s", head1);
+}
+
+void
+inform(const struct printer *pp, char *cf)
+{
+ int copycnt, jnum;
+ char savedname[MAXPATHLEN+1];
+ FILE *cfp;
+
+ /*
+ * There's a chance the control file has gone away
+ * in the meantime; if this is the case just keep going
+ */
+ seteuid(euid);
+ if ((cfp = fopen(cf, "r")) == NULL)
+ return;
+ seteuid(uid);
+
+ if (rank < 0)
+ rank = 0;
+ if (pp->remote || garbage || strcmp(cf, current))
+ rank++;
+
+ /*
+ * The cf-file may include commands to print more than one datafile
+ * from the user. For each datafile, the cf-file contains at least
+ * one line which starts with some format-specifier ('a'-'z'), and
+ * a second line ('N'ame) which indicates the original name the user
+ * specified for that file. There can be multiple format-spec lines
+ * for a single Name-line, if the user requested multiple copies of
+ * that file. Standard lpr puts the format-spec line(s) before the
+ * Name-line, while lprNG puts the Name-line before the format-spec
+ * line(s). This section needs to handle the lines in either order.
+ */
+ copycnt = 0;
+ file[0] = '\0';
+ savedname[0] = '\0';
+ jnum = calc_jobnum(cf, NULL);
+ while (getline(cfp)) {
+ switch (line[0]) {
+ case 'P': /* Was this file specified in the user's list? */
+ if (!inlist(line+1, cf)) {
+ fclose(cfp);
+ return;
+ }
+ if (lflag) {
+ printf("\n%s: ", line+1);
+ col = strlen(line+1) + 2;
+ prank(rank);
+ blankfill(JOBCOL);
+ printf(" [job %s]\n", cf+3);
+ } else {
+ col = 0;
+ prank(rank);
+ blankfill(OWNCOL);
+ printf("%-10s %-3d ", line+1, jnum);
+ col += 16;
+ first = 1;
+ }
+ continue;
+ default: /* some format specifer and file name? */
+ if (line[0] < 'a' || line[0] > 'z')
+ break;
+ if (copycnt == 0 || strcmp(file, line+1) != 0) {
+ strlcpy(file, line + 1, sizeof(file));
+ }
+ copycnt++;
+ /*
+ * deliberately 'continue' to another getline(), so
+ * all format-spec lines for this datafile are read
+ * in and counted before calling show()
+ */
+ continue;
+ case 'N':
+ strlcpy(savedname, line + 1, sizeof(savedname));
+ break;
+ }
+ if ((file[0] != '\0') && (savedname[0] != '\0')) {
+ show(savedname, file, copycnt);
+ copycnt = 0;
+ file[0] = '\0';
+ savedname[0] = '\0';
+ }
+ }
+ fclose(cfp);
+ /* check for a file which hasn't been shown yet */
+ if (file[0] != '\0') {
+ if (savedname[0] == '\0') {
+ /* a safeguard in case the N-ame line is missing */
+ strlcpy(savedname, file, sizeof(savedname));
+ }
+ show(savedname, file, copycnt);
+ }
+ if (!lflag) {
+ blankfill(SIZCOL);
+ printf("%ld bytes\n", totsize);
+ totsize = 0;
+ }
+}
+
+int
+inlist(char *uname, char *cfile)
+{
+ int *r, jnum;
+ char **u;
+ const char *cfhost;
+
+ if (users == 0 && requests == 0)
+ return(1);
+ /*
+ * Check to see if it's in the user list
+ */
+ for (u = user; u < &user[users]; u++)
+ if (!strcmp(*u, uname))
+ return(1);
+ /*
+ * Check the request list
+ */
+ jnum = calc_jobnum(cfile, &cfhost);
+ for (r = requ; r < &requ[requests]; r++)
+ if (*r == jnum && !strcmp(cfhost, from_host))
+ return(1);
+ return(0);
+}
+
+void
+show(const char *nfile, const char *datafile, int copies)
+{
+ if (strcmp(nfile, " ") == 0)
+ nfile = "(standard input)";
+ if (lflag)
+ ldump(nfile, datafile, copies);
+ else
+ dump(nfile, datafile, copies);
+}
+
+/*
+ * Fill the line with blanks to the specified column
+ */
+void
+blankfill(int tocol)
+{
+ while (col++ < tocol)
+ putchar(' ');
+}
+
+/*
+ * Give the abbreviated dump of the file names
+ */
+void
+dump(const char *nfile, const char *datafile, int copies)
+{
+ struct stat lbuf;
+ const char etctmpl[] = ", ...";
+ char etc[sizeof(etctmpl)];
+ char *lastsep;
+ short fill, nlen;
+ short rem, remetc;
+
+ /*
+ * Print as many filenames as will fit
+ * (leaving room for the 'total size' field)
+ */
+ fill = first ? 0 : 2; /* fill space for ``, '' */
+ nlen = strlen(nfile);
+ rem = SIZCOL - 1 - col;
+ if (nlen + fill > rem) {
+ if (first) {
+ /* print the right-most part of the name */
+ printf("...%s ", &nfile[3+nlen-rem]);
+ col = SIZCOL;
+ } else if (rem > 0) {
+ /* fit as much of the etc-string as we can */
+ remetc = rem;
+ if (rem > strlen(etctmpl))
+ remetc = strlen(etctmpl);
+ etc[0] = '\0';
+ strncat(etc, etctmpl, remetc);
+ printf("%s", etc);
+ col += remetc;
+ rem -= remetc;
+ /* room for the last segment of this filename? */
+ lastsep = strrchr(nfile, '/');
+ if ((lastsep != NULL) && (rem > strlen(lastsep))) {
+ /* print the right-most part of this name */
+ printf("%s", lastsep);
+ col += strlen(lastsep);
+ } else {
+ /* do not pack any more names in here */
+ blankfill(SIZCOL);
+ }
+ }
+ } else {
+ if (!first)
+ printf(", ");
+ printf("%s", nfile);
+ col += nlen + fill;
+ }
+ first = 0;
+
+ seteuid(euid);
+ if (*datafile && !stat(datafile, &lbuf))
+ totsize += copies * lbuf.st_size;
+ seteuid(uid);
+}
+
+/*
+ * Print the long info about the file
+ */
+void
+ldump(const char *nfile, const char *datafile, int copies)
+{
+ struct stat lbuf;
+
+ putchar('\t');
+ if (copies > 1)
+ printf("%-2d copies of %-19s", copies, nfile);
+ else
+ printf("%-32s", nfile);
+ if (*datafile && !stat(datafile, &lbuf))
+ printf(" %qd bytes", (long long) lbuf.st_size);
+ else
+ printf(" ??? bytes");
+ putchar('\n');
+}
+
+/*
+ * Print the job's rank in the queue,
+ * update col for screen management
+ */
+void
+prank(int n)
+{
+ char rline[100];
+ static const char *r[] = {
+ "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"
+ };
+
+ if (n == 0) {
+ printf("active");
+ col += 6;
+ return;
+ }
+ if ((n/10)%10 == 1)
+ (void)snprintf(rline, sizeof(rline), "%dth", n);
+ else
+ (void)snprintf(rline, sizeof(rline), "%d%s", n, r[n%10]);
+ col += strlen(rline);
+ printf("%s", rline);
+}
+
+void
+alarmhandler(int signo __unused)
+{
+ /* the signal is ignored */
+ /* (the '__unused' is just to avoid a compile-time warning) */
+}
diff --git a/usr.sbin/lpr/common_source/lp.cdefs.h b/usr.sbin/lpr/common_source/lp.cdefs.h
new file mode 100644
index 0000000..19b1331
--- /dev/null
+++ b/usr.sbin/lpr/common_source/lp.cdefs.h
@@ -0,0 +1,107 @@
+/*-
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ * Copyright (c) 2003 - Garance Alistair Drosehn <gad@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.
+ *
+ * The views and conclusions contained in the software and documentation
+ * are those of the authors and should not be interpreted as representing
+ * official policies, either expressed or implied, of the FreeBSD Project.
+ *
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ * $FreeBSD$
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ */
+
+/*
+ * The main goal of this include file is to provide a platform-neutral way
+ * to define some macros that lpr wants from FreeBSD's <sys/cdefs.h>. This
+ * will simply use the standard <sys/cdefs.h> when compiled in FreeBSD, but
+ * other OS's may not have /usr/include/sys/cdefs.h (or even if that file
+ * exists, it may not define all the macros that lpr will use).
+ */
+
+#if !defined(_LP_CDEFS_H_)
+#define _LP_CDEFS_H_
+
+/*
+ * For non-BSD platforms, you can compile lpr with -DHAVE_SYS_CDEFS_H
+ * if <sys/cdefs.h> should be included.
+ */
+#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
+# define HAVE_SYS_CDEFS_H
+#endif
+#if defined(HAVE_SYS_CDEFS_H)
+# include <sys/cdefs.h>
+#endif
+
+/*
+ * __unused is a compiler-specific trick which can be used to avoid
+ * warnings about a variable which is defined but never referenced.
+ * Some lpr files use this, so define a null version if it was not
+ * defined by <sys/cdefs.h>.
+ */
+#if !defined(__unused)
+# define __unused
+#endif
+
+/*
+ * All the lpr source files will want to reference __FBSDID() to
+ * handle rcs id's.
+ */
+#if !defined(__FBSDID)
+# if defined(lint) || defined(STRIP_FBSDID)
+# define __FBSDID(s) struct skip_rcsid_struct
+# elif defined(__IDSTRING) /* NetBSD */
+# define __FBSDID(s) __IDSTRING(rcsid,s)
+# else
+# define __FBSDID(s) static const char rcsid[] __unused = s
+# endif
+#endif /* __FBSDID */
+
+/*
+ * Some lpr include files use __BEGIN_DECLS and __END_DECLS.
+ */
+#if !defined(__BEGIN_DECLS)
+# if defined(__cplusplus)
+# define __BEGIN_DECLS extern "C" {
+# define __END_DECLS }
+# else
+# define __BEGIN_DECLS
+# define __END_DECLS
+# endif
+#endif
+
+/*
+ * __printflike and __printf0like are a compiler-specific tricks to
+ * tell the compiler to check the format-codes in printf-like
+ * routines wrt the args that will be formatted.
+ */
+#if !defined(__printflike)
+# define __printflike(fmtarg, firstvararg)
+#endif
+#if !defined(__printf0like)
+# define __printf0like(fmtarg, firstvararg)
+#endif
+
+#endif /* !_LP_CDEFS_H_ */
diff --git a/usr.sbin/lpr/common_source/lp.h b/usr.sbin/lpr/common_source/lp.h
new file mode 100644
index 0000000..891ed2f
--- /dev/null
+++ b/usr.sbin/lpr/common_source/lp.h
@@ -0,0 +1,307 @@
+/*
+ * 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.
+ *
+ * From: @(#)lp.h 8.2 (Berkeley) 4/28/95
+ * $FreeBSD$
+ */
+
+#include <sys/queue.h>
+#include <time.h>
+#include <netdb.h>
+
+/*
+ * All this information used to be in global static variables shared
+ * mysteriously by various parts of the lpr/lpd suite.
+ * This structure attempts to centralize all these declarations in the
+ * hope that they can later be made more dynamic.
+ */
+enum lpd_filters { LPF_CIFPLOT, LPF_DVI, LPF_GRAPH, LPF_INPUT,
+ LPF_DITROFF, LPF_OUTPUT, LPF_FORTRAN, LPF_TROFF,
+ LPF_RASTER, LPF_COUNT };
+/* NB: there is a table in common.c giving the mapping from capability names */
+
+struct printer {
+ char *printer; /* printer name */
+ int remote; /* true if RM points to a remote host */
+ int rp_matches_local; /* true if rp has same name as us */
+ int tof; /* true if we are at top-of-form */
+ /* ------------------------------------------------------ */
+ char *acct_file; /* AF: accounting file */
+ long baud_rate; /* BR: baud rate if lp is a tty */
+ char *filters[LPF_COUNT]; /* CF, DF, GF, IF, NF, OF, RF, TF, VF */
+ long conn_timeout; /* CT: TCP connection timeout */
+ long daemon_user; /* DU: daemon user id -- XXX belongs ???? */
+ char *form_feed; /* FF: form feed */
+ long header_last; /* HL: print header last */
+ char *log_file; /* LF: log file */
+ char *lock_file; /* LO: lock file */
+ char *lp; /* LP: device name or network address */
+ long max_copies; /* MC: maximum number of copies allowed */
+ long max_blocks; /* MX: maximum number of blocks to copy */
+ long price100; /* PC: price per 100 units of output */
+ long page_length; /* PL: page length */
+ long page_width; /* PW: page width */
+ long page_pwidth; /* PX: page width in pixels */
+ long page_plength; /* PY: page length in pixels */
+ long resend_copies; /* RC: resend copies to remote host */
+ char *restrict_grp; /* RG: restricted group */
+ char *remote_host; /* RM: remote machine name */
+ char *remote_queue; /* RP: remote printer name */
+ long restricted; /* RS: restricted to those with local accts */
+ long rw; /* RW: open LP for reading and writing */
+ long short_banner; /* SB: short banner */
+ long no_copies; /* SC: suppress multiple copies */
+ char *spool_dir; /* SD: spool directory */
+ long no_formfeed; /* SF: suppress FF on each print job */
+ long no_header; /* SH: suppress header page */
+ char *stat_recv; /* SR: statistics file, receiving jobs */
+ char *stat_send; /* SS: statistics file, sending jobs */
+ char *status_file; /* ST: status file name */
+ char *trailer; /* TR: trailer string send when Q empties */
+ char *mode_set; /* MS: mode set, a la stty */
+
+ /* variables used by trstat*() to keep statistics on file transfers */
+#define JOBNUM_SIZE 8
+ char jobnum[JOBNUM_SIZE];
+ long jobdfnum; /* current datafile number within job */
+ struct timespec tr_start, tr_done;
+#define TIMESTR_SIZE 40 /* holds result from LPD_TIMESTAMP_PATTERN */
+ char tr_timestr[TIMESTR_SIZE];
+#define DIFFTIME_TS(endTS,startTS) \
+ ((double)(endTS.tv_sec - startTS.tv_sec) \
+ + (endTS.tv_nsec - startTS.tv_nsec) * 1.0e-9)
+};
+
+/*
+ * Lists of user names and job numbers, for the benefit of the structs
+ * defined below. We use TAILQs so that requests don't get mysteriously
+ * reversed in process.
+ */
+struct req_user {
+ TAILQ_ENTRY(req_user) ru_link; /* macro glue */
+ char ru_uname[1]; /* name of user */
+};
+TAILQ_HEAD(req_user_head, req_user);
+
+struct req_file {
+ TAILQ_ENTRY(req_file) rf_link; /* macro glue */
+ char rf_type; /* type (lowercase cf file letter) of file */
+ char *rf_prettyname; /* user-visible name of file */
+ char rf_fname[1]; /* name of file */
+};
+TAILQ_HEAD(req_file_head, req_file);
+
+struct req_jobid {
+ TAILQ_ENTRY(req_jobid) rj_link; /* macro glue */
+ int rj_job; /* job number */
+};
+TAILQ_HEAD(req_jobid_head, req_jobid);
+
+/*
+ * Encapsulate all the information relevant to a request in the
+ * lpr/lpd protocol.
+ */
+enum req_type { REQ_START, REQ_RECVJOB, REQ_LIST, REQ_DELETE };
+
+struct request {
+ enum req_type type; /* what sort of request is this? */
+ struct printer prtr; /* which printer is it for? */
+ int remote; /* did request arrive over network? */
+ char *logname; /* login name of requesting user */
+ char *authname; /* authenticated identity of requesting user */
+ char *prettyname; /* ``pretty'' name of requesting user */
+ int privileged; /* was the request from a privileged user? */
+ void *authinfo; /* authentication information */
+ int authentic; /* was the request securely authenticated? */
+
+ /* Information for queries and deletes... */
+ int nusers; /* length of following list... */
+ struct req_user_head users; /* list of users to query/delete */
+ int njobids; /* length of following list... */
+ struct req_jobid_head jobids; /* list of jobids to query/delete */
+};
+
+/*
+ * Global definitions for the line printer system.
+ */
+extern char line[BUFSIZ];
+extern const char *progname; /* program name (lpr, lpq, etc) */
+
+ /*
+ * 'local_host' is the name of the machine that lpd (lpr, whatever)
+ * is actually running on.
+ *
+ * 'from_host' will point to the 'host' variable when receiving a job
+ * from a user on the same host, or "somewhere else" when receiving a
+ * job from a remote host. If 'from_host != local_host', then 'from_ip'
+ * is the character representation of the IP address of from_host (note
+ * that string could be an IPv6 address).
+ *
+ * Also note that when 'from_host' is not pointing at 'local_host', the
+ * string it is pointing at may be as long as NI_MAXHOST (which is very
+ * likely to be much longer than MAXHOSTNAMELEN).
+ */
+extern char local_host[MAXHOSTNAMELEN];
+extern const char *from_host; /* client's machine name */
+extern const char *from_ip; /* client machine's IP address */
+
+extern int requ[]; /* job number of spool entries */
+extern int requests; /* # of spool requests */
+extern char *user[]; /* users to process */
+extern int users; /* # of users in user array */
+extern char *person; /* name of person doing lprm */
+extern u_char family; /* address family */
+
+/*
+ * Structure used for building a sorted list of control files.
+ * The job_processed value can be used by callers of getq(), to keep
+ * track of whatever processing they are doing.
+ */
+struct jobqueue {
+ time_t job_time; /* last-mod time of cf-file */
+ int job_matched; /* used by match_jobspec() */
+ int job_processed; /* set to zero by getq() */
+ char job_cfname[MAXNAMLEN+1]; /* control file name */
+};
+
+/* lpr/lpd generates readable timestamps for logfiles, etc. Have all those
+ * timestamps be in the same format wrt strftime(). This is ISO 8601 format,
+ * with the addition of an easy-readable day-of-the-week field. Note that
+ * '%T' = '%H:%M:%S', and that '%z' is not available on all platforms.
+ */
+#define LPD_TIMESTAMP_PATTERN "%Y-%m-%dT%T%z %a"
+
+/*
+ * Codes to indicate which statistic records trstat_write should write.
+ */
+typedef enum { TR_SENDING, TR_RECVING, TR_PRINTING } tr_sendrecv;
+
+/*
+ * Error codes for our mini printcap library.
+ */
+#define PCAPERR_TCLOOP (-3)
+#define PCAPERR_OSERR (-2)
+#define PCAPERR_NOTFOUND (-1)
+#define PCAPERR_SUCCESS 0
+#define PCAPERR_TCOPEN 1
+
+/*
+ * File modes for the various status files maintained by lpd.
+ */
+#define LOCK_FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH)
+#define LFM_PRINT_DIS (S_IXUSR)
+#define LFM_QUEUE_DIS (S_IXGRP)
+#define LFM_RESET_QUE (S_IXOTH)
+
+#define STAT_FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH)
+#define LOG_FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH)
+#define TEMP_FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH)
+
+/*
+ * Bit-flags for set_qstate() actions, followed by the return values.
+ */
+#define SQS_DISABLEQ 0x01 /* Disable the queuing of new jobs */
+#define SQS_STOPP 0x02 /* Stop the printing of jobs */
+#define SQS_ENABLEQ 0x10 /* Enable the queuing of new jobs */
+#define SQS_STARTP 0x20 /* Start the printing of jobs */
+#define SQS_QCHANGED 0x80 /* The queue has changed (new jobs, etc) */
+
+#define SQS_PARMERR -9 /* Invalid parameters from caller */
+#define SQS_CREFAIL -3 /* File did not exist, and create failed */
+#define SQS_CHGFAIL -2 /* File exists, but unable to change state */
+#define SQS_STATFAIL -1 /* Unable to stat() the lock file */
+#define SQS_CHGOK 1 /* File existed, and the state was changed */
+#define SQS_CREOK 2 /* File did not exist, but was created OK */
+#define SQS_SKIPCREOK 3 /* File did not exist, and there was */
+ /* no need to create it */
+
+/*
+ * Command codes used in the protocol.
+ */
+#define CMD_CHECK_QUE '\1'
+#define CMD_TAKE_THIS '\2'
+#define CMD_SHOWQ_SHORT '\3'
+#define CMD_SHOWQ_LONG '\4'
+#define CMD_RMJOB '\5'
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+
+__BEGIN_DECLS
+struct dirent;
+
+void blankfill(int _tocol);
+int calc_jobnum(const char *_cfname, const char **_hostpp);
+char *checkremote(struct printer *_pp);
+int chk(char *_file);
+void closeallfds(int _start);
+void delay(int _millisec);
+void displayq(struct printer *_pp, int _format);
+void dump(const char *_nfile, const char *_datafile, int _copies);
+void fatal(const struct printer *_pp, const char *_msg, ...)
+ __printflike(2, 3);
+int firstprinter(struct printer *_pp, int *_error);
+void free_printer(struct printer *_pp);
+void free_request(struct request *_rp);
+int getline(FILE *_cfp);
+int getport(const struct printer *_pp, const char *_rhost, int _rport);
+int getprintcap(const char *_printer, struct printer *_pp);
+int getq(const struct printer *_pp, struct jobqueue *(*_namelist[]));
+void header(void);
+void inform(const struct printer *_pp, char *_cf);
+void init_printer(struct printer *_pp);
+void init_request(struct request *_rp);
+int inlist(char *_uname, char *_cfile);
+int iscf(struct dirent *_d);
+void ldump(const char *_nfile, const char *_datafile, int _copies);
+void lastprinter(void);
+int lockchk(struct printer *_pp, char *_slockf);
+char *lock_file_name(const struct printer *_pp, char *_buf, size_t _len);
+void lpd_gettime(struct timespec *_tsp, char *_strp, size_t _strsize);
+int nextprinter(struct printer *_pp, int *_error);
+const
+char *pcaperr(int _error);
+void prank(int _n);
+void process(const struct printer *_pp, char *_file);
+void rmjob(const char *_printer);
+void rmremote(const struct printer *_pp);
+void setprintcap(char *_newfile);
+int set_qstate(int _action, const char *_lfname);
+void show(const char *_nfile, const char *_datafile, int _copies);
+int startdaemon(const struct printer *_pp);
+char *status_file_name(const struct printer *_pp, char *_buf,
+ size_t _len);
+void trstat_init(struct printer *_pp, const char *_fname, int _filenum);
+void trstat_write(struct printer *_pp, tr_sendrecv _sendrecv,
+ size_t _bytecnt, const char *_userid, const char *_otherhost,
+ const char *_orighost);
+ssize_t writel(int _strm, ...);
+__END_DECLS
diff --git a/usr.sbin/lpr/common_source/lp.local.h b/usr.sbin/lpr/common_source/lp.local.h
new file mode 100644
index 0000000..6f018b0
--- /dev/null
+++ b/usr.sbin/lpr/common_source/lp.local.h
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ *
+ * @(#)lp.local.h 8.1 (Berkeley) 6/6/93
+ * $FreeBSD$
+ */
+
+/*
+ * Possibly, local parameters to the spooling system
+ */
+
+/*
+ * Defaults for line printer capabilities data base
+ */
+#define DEFLP "lp"
+#define DEFLOCK "lock"
+#define DEFSTAT "status"
+#define DEFMX 0
+#define DEFMAXCOPIES 0
+#define DEFFF "\f"
+#define DEFWIDTH 132
+#define DEFLENGTH 66
+#define DEFUID 1
+#define DEFTIMEOUT 120
+
+/*
+ * When files are created in the spooling area, they are normally
+ * readable only by their owner and the spooling group. If you
+ * want otherwise, change this mode.
+ */
+#define FILMOD 0660
+
+/*
+ * Printer is assumed to support LINELEN (for block chars)
+ * and background character (blank) is a space
+ */
+#define LINELEN 132
+#define BACKGND ' '
+
+#define HEIGHT 9 /* height of characters */
+#define WIDTH 8 /* width of characters */
+#define DROP 3 /* offset to drop characters with descenders */
+
+/*
+ * Define TERMCAP if the terminal capabilites are to be used for lpq.
+ */
+#define TERMCAP
+
+/*
+ * Maximum number of user and job requests for lpq and lprm.
+ */
+#define MAXUSERS 50
+#define MAXREQUESTS 50
diff --git a/usr.sbin/lpr/common_source/matchjobs.c b/usr.sbin/lpr/common_source/matchjobs.c
new file mode 100644
index 0000000..de572f8
--- /dev/null
+++ b/usr.sbin/lpr/common_source/matchjobs.c
@@ -0,0 +1,568 @@
+/*
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ * Copyright (c) 2002 - Garance Alistair Drosehn <gad@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.
+ *
+ * The views and conclusions contained in the software and documentation
+ * are those of the authors and should not be interpreted as representing
+ * official policies, either expressed or implied, of the FreeBSD Project
+ * or FreeBSD, Inc.
+ *
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ */
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+__FBSDID("$FreeBSD$");
+
+/*
+ * movejobs.c - The lpc commands which move jobs around.
+ */
+
+#include <sys/file.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+
+#include <dirent.h> /* for MAXNAMLEN, for job_cfname in lp.h! */
+#include <ctype.h>
+#include <errno.h>
+#include <fnmatch.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "ctlinfo.h"
+#include "lp.h"
+#include "matchjobs.h"
+
+#define DEBUG_PARSEJS 0 /* set to 1 when testing */
+#define DEBUG_SCANJS 0 /* set to 1 when testing */
+
+static int match_jobspec(struct jobqueue *_jq, struct jobspec *_jspec);
+
+/*
+ * isdigit is defined to work on an 'int', in the range 0 to 255, plus EOF.
+ * Define a wrapper which can take 'char', either signed or unsigned.
+ */
+#define isdigitch(Anychar) isdigit(((int) Anychar) & 255)
+
+/*
+ * Format a single jobspec into a string fit for printing.
+ */
+void
+format_jobspec(struct jobspec *jspec, int fmt_wanted)
+{
+ char rangestr[40], buildstr[200];
+ const char fromuser[] = "from user ";
+ const char fromhost[] = "from host ";
+ size_t strsize;
+
+ /*
+ * If the struct already has a fmtstring, then release it
+ * before building a new one.
+ */
+ if (jspec->fmtoutput != NULL) {
+ free(jspec->fmtoutput);
+ jspec->fmtoutput = NULL;
+ }
+
+ jspec->pluralfmt = 1; /* assume a "plural result" */
+ rangestr[0] = '\0';
+ if (jspec->startnum >= 0) {
+ if (jspec->startnum != jspec->endrange)
+ snprintf(rangestr, sizeof(rangestr), "%ld-%ld",
+ jspec->startnum, jspec->endrange);
+ else {
+ jspec->pluralfmt = 0;
+ snprintf(rangestr, sizeof(rangestr), "%ld",
+ jspec->startnum);
+ }
+ }
+
+ strsize = sizeof(buildstr);
+ buildstr[0] = '\0';
+ switch (fmt_wanted) {
+ case FMTJS_TERSE:
+ /* Build everything but the hostname in a temp string. */
+ if (jspec->wanteduser != NULL)
+ strlcat(buildstr, jspec->wanteduser, strsize);
+ if (rangestr[0] != '\0') {
+ if (buildstr[0] != '\0')
+ strlcat(buildstr, ":", strsize);
+ strlcat(buildstr, rangestr, strsize);
+ }
+ if (jspec->wantedhost != NULL)
+ strlcat(buildstr, "@", strsize);
+
+ /* Get space for the final result, including hostname */
+ strsize = strlen(buildstr) + 1;
+ if (jspec->wantedhost != NULL)
+ strsize += strlen(jspec->wantedhost);
+ jspec->fmtoutput = malloc(strsize);
+
+ /* Put together the final result */
+ strlcpy(jspec->fmtoutput, buildstr, strsize);
+ if (jspec->wantedhost != NULL)
+ strlcat(jspec->fmtoutput, jspec->wantedhost, strsize);
+ break;
+
+ case FMTJS_VERBOSE:
+ default:
+ /* Build everything but the hostname in a temp string. */
+ strlcat(buildstr, rangestr, strsize);
+ if (jspec->wanteduser != NULL) {
+ if (rangestr[0] != '\0')
+ strlcat(buildstr, " ", strsize);
+ strlcat(buildstr, fromuser, strsize);
+ strlcat(buildstr, jspec->wanteduser, strsize);
+ }
+ if (jspec->wantedhost != NULL) {
+ if (jspec->wanteduser == NULL) {
+ if (rangestr[0] != '\0')
+ strlcat(buildstr, " ", strsize);
+ strlcat(buildstr, fromhost, strsize);
+ } else
+ strlcat(buildstr, "@", strsize);
+ }
+
+ /* Get space for the final result, including hostname */
+ strsize = strlen(buildstr) + 1;
+ if (jspec->wantedhost != NULL)
+ strsize += strlen(jspec->wantedhost);
+ jspec->fmtoutput = malloc(strsize);
+
+ /* Put together the final result */
+ strlcpy(jspec->fmtoutput, buildstr, strsize);
+ if (jspec->wantedhost != NULL)
+ strlcat(jspec->fmtoutput, jspec->wantedhost, strsize);
+ break;
+ }
+}
+
+/*
+ * Free all the jobspec-related information.
+ */
+void
+free_jobspec(struct jobspec_hdr *js_hdr)
+{
+ struct jobspec *jsinf;
+
+ while (!STAILQ_EMPTY(js_hdr)) {
+ jsinf = STAILQ_FIRST(js_hdr);
+ STAILQ_REMOVE_HEAD(js_hdr, nextjs);
+ if (jsinf->fmtoutput)
+ free(jsinf->fmtoutput);
+ if (jsinf->matcheduser)
+ free(jsinf->matcheduser);
+ free(jsinf);
+ }
+}
+
+/*
+ * This routine takes a string as typed in from the user, and parses it
+ * into a job-specification. A job specification would match one or more
+ * jobs in the queue of some single printer (the specification itself does
+ * not indicate which queue should be searched).
+ *
+ * This recognizes a job-number range by itself (all digits, or a range
+ * indicated by "digits-digits"), or a userid by itself. If a `:' is
+ * found, it is treated as a separator between a job-number range and
+ * a userid, where the job number range is the side which has a digit as
+ * the first character. If an `@' is found, everything to the right of
+ * it is treated as the hostname the job originated from.
+ *
+ * So, the user can specify:
+ * jobrange userid userid:jobrange jobrange:userid
+ * jobrange@hostname jobrange:userid@hostname
+ * userid@hostname userid:jobrange@hostname
+ *
+ * XXX - it would be nice to add "not options" too, such as ^user,
+ * ^jobrange, and @^hostname.
+ *
+ * This routine may modify the original input string if that input is
+ * valid. If the input was *not* valid, then this routine should return
+ * with the input string the same as when the routine was called.
+ */
+int
+parse_jobspec(char *jobstr, struct jobspec_hdr *js_hdr)
+{
+ struct jobspec *jsinfo;
+ char *atsign, *colon, *lhside, *numstr, *period, *rhside;
+ int jobnum;
+
+#if DEBUG_PARSEJS
+ printf("\t [ pjs-input = %s ]\n", jobstr);
+#endif
+
+ if ((jobstr == NULL) || (*jobstr == '\0'))
+ return (0);
+
+ jsinfo = malloc(sizeof(struct jobspec));
+ memset(jsinfo, 0, sizeof(struct jobspec));
+ jsinfo->startnum = jsinfo->endrange = -1;
+
+ /* Find the separator characters, and nullify them. */
+ numstr = NULL;
+ atsign = strchr(jobstr, '@');
+ colon = strchr(jobstr, ':');
+ if (atsign != NULL)
+ *atsign = '\0';
+ if (colon != NULL)
+ *colon = '\0';
+
+ /* The at-sign always indicates a hostname. */
+ if (atsign != NULL) {
+ rhside = atsign + 1;
+ if (*rhside != '\0')
+ jsinfo->wantedhost = rhside;
+ }
+
+ /* Finish splitting the input into three parts. */
+ rhside = NULL;
+ if (colon != NULL) {
+ rhside = colon + 1;
+ if (*rhside == '\0')
+ rhside = NULL;
+ }
+ lhside = NULL;
+ if (*jobstr != '\0')
+ lhside = jobstr;
+
+ /*
+ * If there is a `:' here, then it's either jobrange:userid,
+ * userid:jobrange, or (if @hostname was not given) perhaps it
+ * might be hostname:jobnum. The side which has a digit as the
+ * first character is assumed to be the jobrange. It is an
+ * input error if both sides start with a digit, or if neither
+ * side starts with a digit.
+ */
+ if ((lhside != NULL) && (rhside != NULL)) {
+ if (isdigitch(*lhside)) {
+ if (isdigitch(*rhside))
+ goto bad_input;
+ numstr = lhside;
+ jsinfo->wanteduser = rhside;
+ } else if (isdigitch(*rhside)) {
+ numstr = rhside;
+ /*
+ * The original implementation of 'lpc topq' accepted
+ * hostname:jobnum. If the input did not include a
+ * @hostname, then assume the userid is a hostname if
+ * it includes a '.'.
+ */
+ period = strchr(lhside, '.');
+ if ((atsign == NULL) && (period != NULL))
+ jsinfo->wantedhost = lhside;
+ else
+ jsinfo->wanteduser = lhside;
+ } else {
+ /* Neither side is a job number = user error */
+ goto bad_input;
+ }
+ } else if (lhside != NULL) {
+ if (isdigitch(*lhside))
+ numstr = lhside;
+ else
+ jsinfo->wanteduser = lhside;
+ } else if (rhside != NULL) {
+ if (isdigitch(*rhside))
+ numstr = rhside;
+ else
+ jsinfo->wanteduser = rhside;
+ }
+
+ /*
+ * Break down the numstr. It should be all digits, or a range
+ * specified as "\d+-\d+".
+ */
+ if (numstr != NULL) {
+ errno = 0;
+ jobnum = strtol(numstr, &numstr, 10);
+ if (errno != 0) /* error in conversion */
+ goto bad_input;
+ if (jobnum < 0) /* a bogus value for this purpose */
+ goto bad_input;
+ if (jobnum > 99999) /* too large for job number */
+ goto bad_input;
+ jsinfo->startnum = jsinfo->endrange = jobnum;
+
+ /* Check for a range of numbers */
+ if ((*numstr == '-') && (isdigitch(*(numstr + 1)))) {
+ numstr++;
+ errno = 0;
+ jobnum = strtol(numstr, &numstr, 10);
+ if (errno != 0) /* error in conversion */
+ goto bad_input;
+ if (jobnum < jsinfo->startnum)
+ goto bad_input;
+ if (jobnum > 99999) /* too large for job number */
+ goto bad_input;
+ jsinfo->endrange = jobnum;
+ }
+
+ /*
+ * If there is anything left in the numstr, and if the
+ * original string did not include a userid or a hostname,
+ * then this might be the ancient form of '\d+hostname'
+ * (with no separator between jobnum and hostname). Accept
+ * that for backwards compatibility, but otherwise any
+ * remaining characters mean a user-error. Note that the
+ * ancient form accepted only a single number, but this
+ * will also accept a range of numbers.
+ */
+ if (*numstr != '\0') {
+ if (atsign != NULL)
+ goto bad_input;
+ if (jsinfo->wantedhost != NULL)
+ goto bad_input;
+ if (jsinfo->wanteduser != NULL)
+ goto bad_input;
+ /* Treat as the rest of the string as a hostname */
+ jsinfo->wantedhost = numstr;
+ }
+ }
+
+ if ((jsinfo->startnum < 0) && (jsinfo->wanteduser == NULL) &&
+ (jsinfo->wantedhost == NULL))
+ goto bad_input;
+
+ /*
+ * The input was valid, in the sense that it could be parsed
+ * into the individual parts. Add this jobspec to the list
+ * of jobspecs.
+ */
+ STAILQ_INSERT_TAIL(js_hdr, jsinfo, nextjs);
+
+#if DEBUG_PARSEJS
+ printf("\t [ will check for");
+ if (jsinfo->startnum >= 0) {
+ if (jsinfo->startnum == jsinfo->endrange)
+ printf(" jobnum = %ld", jsinfo->startnum);
+ else
+ printf(" jobrange = %ld to %ld", jsinfo->startnum,
+ jsinfo->endrange);
+ } else {
+ printf(" jobs");
+ }
+ if ((jsinfo->wanteduser != NULL) || (jsinfo->wantedhost != NULL)) {
+ printf(" from");
+ if (jsinfo->wanteduser != NULL)
+ printf(" user = %s", jsinfo->wanteduser);
+ if (jsinfo->wantedhost != NULL)
+ printf(" host = %s", jsinfo->wantedhost);
+ }
+ printf("]\n");
+#endif
+
+ return (1);
+
+bad_input:
+ /*
+ * Restore any `@' and `:', in case the calling routine wants to
+ * write an error message which includes the input string.
+ */
+ if (atsign != NULL)
+ *atsign = '@';
+ if (colon != NULL)
+ *colon = ':';
+ if (jsinfo != NULL)
+ free(jsinfo);
+ return (0);
+}
+
+/*
+ * Check to see if a given job (specified by a jobqueue entry) matches
+ * all of the specifications in a given jobspec.
+ *
+ * Returns 0 if no match, 1 if the job does match.
+ */
+static int
+match_jobspec(struct jobqueue *jq, struct jobspec *jspec)
+{
+ struct cjobinfo *cfinf;
+ const char *cf_hoststr;
+ int jnum, match;
+
+#if DEBUG_SCANJS
+ printf("\t [ match-js checking %s ]\n", jq->job_cfname);
+#endif
+
+ if (jspec == NULL || jq == NULL)
+ return (0);
+
+ /*
+ * Keep track of which jobs have already been matched by this
+ * routine, and thus (probably) already processed.
+ */
+ if (jq->job_matched)
+ return (0);
+
+ jnum = calc_jobnum(jq->job_cfname, &cf_hoststr);
+ cfinf = NULL;
+ match = 0; /* assume the job will not match */
+ jspec->matcheduser = NULL;
+
+ /*
+ * Check the job-number range.
+ */
+ if (jspec->startnum >= 0) {
+ if (jnum < jspec->startnum)
+ goto nomatch;
+ if (jnum > jspec->endrange)
+ goto nomatch;
+ }
+
+ /*
+ * Check the hostname. Strictly speaking this should be done by
+ * reading the control file, but it is less expensive to check
+ * the hostname-part of the control file name. Also, this value
+ * can be easily seen in 'lpq -l', while there is no easy way for
+ * a user/operator to see the hostname in the control file.
+ */
+ if (jspec->wantedhost != NULL) {
+ if (fnmatch(jspec->wantedhost, cf_hoststr, 0) != 0)
+ goto nomatch;
+ }
+
+ /*
+ * Check for a match on the user name. This has to be done
+ * by reading the control file.
+ */
+ if (jspec->wanteduser != NULL) {
+ cfinf = ctl_readcf("fakeq", jq->job_cfname);
+ if (cfinf == NULL)
+ goto nomatch;
+ if (fnmatch(jspec->wanteduser, cfinf->cji_username, 0) != 0)
+ goto nomatch;
+ }
+
+ /* This job matches all of the specified criteria. */
+ match = 1;
+ jq->job_matched = 1; /* avoid matching the job twice */
+ jspec->matchcnt++;
+ if (jspec->wanteduser != NULL) {
+ /*
+ * If the user specified a userid (which may have been a
+ * pattern), then the caller's "doentry()" routine might
+ * want to know the userid of this job that matched.
+ */
+ jspec->matcheduser = strdup(cfinf->cji_username);
+ }
+#if DEBUG_SCANJS
+ printf("\t [ job matched! ]\n");
+#endif
+
+nomatch:
+ if (cfinf != NULL)
+ ctl_freeinf(cfinf);
+ return (match);
+}
+
+/*
+ * Scan a queue for all jobs which match a jobspec. The queue is scanned
+ * from top to bottom.
+ *
+ * The caller can provide a routine which will be executed for each job
+ * that does match. Note that the processing routine might do anything
+ * to the matched job -- including the removal of it.
+ *
+ * This returns the number of jobs which were matched.
+ */
+int
+scanq_jobspec(int qcount, struct jobqueue **squeue, int sopts, struct
+ jobspec_hdr *js_hdr, process_jqe doentry, void *doentryinfo)
+{
+ struct jobqueue **qent;
+ struct jobspec *jspec;
+ int cnt, matched, total;
+
+ if (qcount < 1)
+ return (0);
+ if (js_hdr == NULL)
+ return (-1);
+
+ /* The caller must specify one of the scanning orders */
+ if ((sopts & (SCQ_JSORDER|SCQ_QORDER)) == 0)
+ return (-1);
+
+ total = 0;
+ if (sopts & SCQ_JSORDER) {
+ /*
+ * For each job specification, scan through the queue
+ * looking for every job that matches.
+ */
+ STAILQ_FOREACH(jspec, js_hdr, nextjs) {
+ for (qent = squeue, cnt = 0; cnt < qcount;
+ qent++, cnt++) {
+ matched = match_jobspec(*qent, jspec);
+ if (!matched)
+ continue;
+ total++;
+ if (doentry != NULL)
+ doentry(doentryinfo, *qent, jspec);
+ if (jspec->matcheduser != NULL) {
+ free(jspec->matcheduser);
+ jspec->matcheduser = NULL;
+ }
+ }
+ /*
+ * The entire queue has been scanned for this
+ * jobspec. Call the user's routine again with
+ * a NULL queue-entry, so it can print out any
+ * kind of per-jobspec summary.
+ */
+ if (doentry != NULL)
+ doentry(doentryinfo, NULL, jspec);
+ }
+ } else {
+ /*
+ * For each job in the queue, check all of the job
+ * specifications to see if any one of them matches
+ * that job.
+ */
+ for (qent = squeue, cnt = 0; cnt < qcount;
+ qent++, cnt++) {
+ STAILQ_FOREACH(jspec, js_hdr, nextjs) {
+ matched = match_jobspec(*qent, jspec);
+ if (!matched)
+ continue;
+ total++;
+ if (doentry != NULL)
+ doentry(doentryinfo, *qent, jspec);
+ if (jspec->matcheduser != NULL) {
+ free(jspec->matcheduser);
+ jspec->matcheduser = NULL;
+ }
+ /*
+ * Once there is a match, then there is no
+ * point in checking this same job against
+ * all the other jobspec's.
+ */
+ break;
+ }
+ }
+ }
+
+ return (total);
+}
diff --git a/usr.sbin/lpr/common_source/matchjobs.h b/usr.sbin/lpr/common_source/matchjobs.h
new file mode 100644
index 0000000..6e4b9ff
--- /dev/null
+++ b/usr.sbin/lpr/common_source/matchjobs.h
@@ -0,0 +1,102 @@
+/*
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ * Copyright (c) 2002 - Garance Alistair Drosehn <gad@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.
+ *
+ * The views and conclusions contained in the software and documentation
+ * are those of the authors and should not be interpreted as representing
+ * official policies, either expressed or implied, of the FreeBSD Project
+ * or FreeBSD, Inc.
+ *
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ * $FreeBSD$
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ */
+
+#include <sys/queue.h>
+
+/*
+ * The "matcheduser" field is *only* valid during the call to the
+ * given "doentry()" routine, and is only set if the specification
+ * included a userid.
+ */
+struct jobspec {
+ STAILQ_ENTRY(jobspec) nextjs;
+ char *wantedhost;
+ char *wanteduser;
+ char *matcheduser; /* only valid for "doentry()" */
+ char *fmtoutput; /* set by format_jobspec() */
+ long startnum;
+ long endrange;
+ int pluralfmt; /* boolean set by format_jobspec() */
+ uint matchcnt;
+};
+STAILQ_HEAD(jobspec_hdr, jobspec);
+
+/*
+ * Format options for format_jobspec.
+ */
+#define FMTJS_TERSE 1 /* user:jobrange@host */
+#define FMTJS_VERBOSE 2 /* jobrange from user@host */
+
+/*
+ * Options for scanq_jobspec.
+ *
+ * The caller must choose the order that entries should be scanned:
+ * 1) JSORDER: Matched jobs are processed (by calling the "doentry()"
+ * routine) in the order that the user specified those jobs.
+ * 2) QORDER: Matched jobs are processed in the order that the jobs are
+ * listed the queue. This guarantees that the "doentry()" routine
+ * will be called only once per job.
+ *
+ * There is a "job_matched" variable in struct jobqueue, which is used
+ * to make sure that the "doentry()" will only be called once for any
+ * given job in JSORDER processing. The "doentry()" routine can turn
+ * that off, if it does want to be called multiple times when the job
+ * is matched by multiple specifiers.
+ *
+ * The JSORDER processing will also call the "doentry()" routine once
+ * after each scan of the queue, with the jobqueue set to null. This
+ * provides a way for the caller to print out a summary message for
+ * each jobspec that was given.
+ */
+#define SCQ_JSORDER 0x0001 /* follow the user-specified order */
+#define SCQ_QORDER 0x0002 /* the order of jobs in the queue */
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+
+__BEGIN_DECLS
+struct jobqueue;
+
+typedef int process_jqe(void *_myinfo, struct jobqueue *_jq,
+ struct jobspec *_jspec);
+
+void format_jobspec(struct jobspec *_jspec, int _fmt_wanted);
+void free_jobspec(struct jobspec_hdr *_js_hdr);
+int scanq_jobspec(int _qitems, struct jobqueue **_squeue, int _sopts,
+ struct jobspec_hdr *_js_hdr, process_jqe _doentry,
+ void *_doentryinfo);
+int parse_jobspec(char *_jobstr, struct jobspec_hdr *_js_hdr);
+__END_DECLS
+
diff --git a/usr.sbin/lpr/common_source/net.c b/usr.sbin/lpr/common_source/net.c
new file mode 100644
index 0000000..2a1e0c8
--- /dev/null
+++ b/usr.sbin/lpr/common_source/net.c
@@ -0,0 +1,299 @@
+/*
+ * 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.
+ * 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: @(#)common.c 8.5 (Berkeley) 4/28/95
+ */
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <dirent.h> /* required for lp.h, not used here */
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "lp.h"
+#include "lp.local.h"
+#include "pathnames.h"
+
+/*
+ * 'local_host' is always the hostname of the machine which is running
+ * lpr (lpd, whatever), while 'from_host' either points at 'local_host'
+ * or points at a different buffer when receiving a job from a remote
+ * machine (and that buffer has the hostname of that remote machine).
+ */
+char local_host[MAXHOSTNAMELEN]; /* host running lpd/lpr */
+const char *from_host = local_host; /* client's machine name */
+const char *from_ip = ""; /* client machine's IP address */
+
+#ifdef INET6
+u_char family = PF_UNSPEC;
+#else
+u_char family = PF_INET;
+#endif
+
+extern uid_t uid, euid;
+
+/*
+ * Create a TCP connection to host "rhost" at port "rport".
+ * If rport == 0, then use the printer service port.
+ * Most of this code comes from rcmd.c.
+ */
+int
+getport(const struct printer *pp, const char *rhost, int rport)
+{
+ struct addrinfo hints, *res, *ai;
+ int s, timo = 1, lport = IPPORT_RESERVED - 1;
+ int err, refused = 0;
+
+ /*
+ * Get the host address and port number to connect to.
+ */
+ if (rhost == NULL)
+ fatal(pp, "no remote host to connect to");
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = 0;
+ err = getaddrinfo(rhost, (rport == 0 ? "printer" : NULL),
+ &hints, &res);
+ if (err)
+ fatal(pp, "%s\n", gai_strerror(err));
+ if (rport != 0)
+ ((struct sockaddr_in *) res->ai_addr)->sin_port = htons(rport);
+
+ /*
+ * Try connecting to the server.
+ */
+ ai = res;
+retry:
+ seteuid(euid);
+ s = rresvport_af(&lport, ai->ai_family);
+ seteuid(uid);
+ if (s < 0) {
+ if (errno != EAGAIN) {
+ if (ai->ai_next) {
+ ai = ai->ai_next;
+ goto retry;
+ }
+ if (refused && timo <= 16) {
+ sleep(timo);
+ timo *= 2;
+ refused = 0;
+ ai = res;
+ goto retry;
+ }
+ }
+ freeaddrinfo(res);
+ return(-1);
+ }
+ if (connect(s, ai->ai_addr, ai->ai_addrlen) < 0) {
+ err = errno;
+ (void) close(s);
+ errno = err;
+ /*
+ * This used to decrement lport, but the current semantics
+ * of rresvport do not provide such a function (in fact,
+ * rresvport should guarantee that the chosen port will
+ * never result in an EADDRINUSE).
+ */
+ if (errno == EADDRINUSE) {
+ goto retry;
+ }
+
+ if (errno == ECONNREFUSED)
+ refused++;
+
+ if (ai->ai_next != NULL) {
+ ai = ai->ai_next;
+ goto retry;
+ }
+ if (refused && timo <= 16) {
+ sleep(timo);
+ timo *= 2;
+ refused = 0;
+ ai = res;
+ goto retry;
+ }
+ freeaddrinfo(res);
+ return(-1);
+ }
+ freeaddrinfo(res);
+ return(s);
+}
+
+/*
+ * Figure out whether the local machine is the same
+ * as the remote machine (RM) entry (if it exists).
+ * We do this by counting the intersection of our
+ * address list and theirs. This is better than the
+ * old method (comparing the canonical names), as it
+ * allows load-sharing between multiple print servers.
+ * The return value is an error message which must be
+ * free()d.
+ */
+char *
+checkremote(struct printer *pp)
+{
+ char lclhost[MAXHOSTNAMELEN];
+ struct addrinfo hints, *local_res, *remote_res, *lr, *rr;
+ char *err;
+ int ncommonaddrs, error;
+ char h1[NI_MAXHOST], h2[NI_MAXHOST];
+
+ if (!pp->rp_matches_local) { /* Remote printer doesn't match local */
+ pp->remote = 1;
+ return NULL;
+ }
+
+ pp->remote = 0; /* assume printer is local */
+ if (pp->remote_host == NULL)
+ return NULL;
+
+ /* get the addresses of the local host */
+ gethostname(lclhost, sizeof(lclhost));
+ lclhost[sizeof(lclhost) - 1] = '\0';
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
+ if ((error = getaddrinfo(lclhost, NULL, &hints, &local_res)) != 0) {
+ asprintf(&err, "unable to get official name "
+ "for local machine %s: %s",
+ lclhost, gai_strerror(error));
+ return err;
+ }
+
+ /* get the official name of RM */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
+ if ((error = getaddrinfo(pp->remote_host, NULL,
+ &hints, &remote_res)) != 0) {
+ asprintf(&err, "unable to get address list for "
+ "remote machine %s: %s",
+ pp->remote_host, gai_strerror(error));
+ freeaddrinfo(local_res);
+ return err;
+ }
+
+ ncommonaddrs = 0;
+ for (lr = local_res; lr; lr = lr->ai_next) {
+ h1[0] = '\0';
+ if (getnameinfo(lr->ai_addr, lr->ai_addrlen, h1, sizeof(h1),
+ NULL, 0, NI_NUMERICHOST) != 0)
+ continue;
+ for (rr = remote_res; rr; rr = rr->ai_next) {
+ h2[0] = '\0';
+ if (getnameinfo(rr->ai_addr, rr->ai_addrlen,
+ h2, sizeof(h2), NULL, 0,
+ NI_NUMERICHOST) != 0)
+ continue;
+ if (strcmp(h1, h2) == 0)
+ ncommonaddrs++;
+ }
+ }
+
+ /*
+ * if the two hosts do not share at least one IP address
+ * then the printer must be remote.
+ */
+ if (ncommonaddrs == 0)
+ pp->remote = 1;
+ freeaddrinfo(local_res);
+ freeaddrinfo(remote_res);
+ return NULL;
+}
+
+/*
+ * This isn't really network-related, but it's used here to write
+ * multi-part strings onto sockets without using stdio. Return
+ * values are as for writev(2).
+ */
+ssize_t
+writel(int strm, ...)
+{
+ va_list ap;
+ int i, n;
+ const char *cp;
+#define NIOV 12
+ struct iovec iov[NIOV], *iovp = iov;
+ ssize_t retval;
+
+ /* first count them */
+ va_start(ap, strm);
+ n = 0;
+ do {
+ cp = va_arg(ap, char *);
+ n++;
+ } while (cp);
+ va_end(ap);
+ n--; /* correct for count of trailing null */
+
+ if (n > NIOV) {
+ iovp = malloc(n * sizeof *iovp);
+ if (iovp == 0)
+ return -1;
+ }
+
+ /* now make up iovec and send */
+ va_start(ap, strm);
+ for (i = 0; i < n; i++) {
+ iovp[i].iov_base = va_arg(ap, char *);
+ iovp[i].iov_len = strlen(iovp[i].iov_base);
+ }
+ va_end(ap);
+ retval = writev(strm, iovp, n);
+ if (iovp != iov)
+ free(iovp);
+ return retval;
+}
diff --git a/usr.sbin/lpr/common_source/pathnames.h b/usr.sbin/lpr/common_source/pathnames.h
new file mode 100644
index 0000000..78ccc7b
--- /dev/null
+++ b/usr.sbin/lpr/common_source/pathnames.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include <paths.h>
+
+#define _PATH_DEFDEVLP "/dev/lp"
+#define _PATH_DEFSPOOL "/var/spool/output/lpd"
+#define _PATH_HOSTSEQUIV "/etc/hosts.equiv"
+#define _PATH_HOSTSLPD "/etc/hosts.lpd"
+#define _PATH_MASTERLOCK "/var/spool/output/lpd.lock"
+#define _PATH_PR "/usr/bin/pr"
+#define _PATH_PRINTCAP "/etc/printcap"
+#define _PATH_SOCKETNAME "/var/run/printer"
+#define _PATH_VFONT "/usr/libdata/vfont/"
+#define _PATH_VFONTB "/usr/libdata/vfont/B"
+#define _PATH_VFONTI "/usr/libdata/vfont/I"
+#define _PATH_VFONTR "/usr/libdata/vfont/R"
+#define _PATH_VFONTS "/usr/libdata/vfont/S"
+#define _PATH_CHKPRINTCAP "/usr/sbin/chkprintcap"
diff --git a/usr.sbin/lpr/common_source/printcap.c b/usr.sbin/lpr/common_source/printcap.c
new file mode 100644
index 0000000..0d3644b
--- /dev/null
+++ b/usr.sbin/lpr/common_source/printcap.c
@@ -0,0 +1,451 @@
+/*
+ * 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.
+ * 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 char sccsid[] = "@(#)printcap.c 8.2 (Berkeley) 4/28/95";
+#endif /* not lint */
+#endif
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+__FBSDID("$FreeBSD$");
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/param.h> /* required for lp.h, but not used here */
+#include <sys/dirent.h> /* ditto */
+#include "lp.h"
+#include "lp.local.h"
+#include "pathnames.h"
+
+/*
+ * Routines and data used in processing the printcap file.
+ */
+static char *printcapdb[2] = { _PATH_PRINTCAP, 0 }; /* list for cget* */
+
+static char *capdb_canonical_name(const char *_bp);
+static int capdb_getaltlog(char *_bp, const char *_shrt,
+ const char *_lng);
+static int capdb_getaltnum(char *_bp, const char *_shrt,
+ const char *_lng, long _dflt, long *_result);
+static int capdb_getaltstr(char *_bp, const char *_shrt,
+ const char *lng, const char *_dflt, char **_result);
+static int getprintcap_int(char *_bp, struct printer *_pp);
+
+/*
+ * Change the name of the printcap file. Used by chkprintcap(8),
+ * but could be used by other members of the suite with appropriate
+ * security measures.
+ */
+void
+setprintcap(char *newfile)
+{
+ printcapdb[0] = newfile;
+}
+
+/*
+ * Read the printcap database for printer `printer' into the
+ * struct printer pointed by `pp'. Return values are as for
+ * cgetent(3): -1 means we could not find what we wanted, -2
+ * means a system error occurred (and errno is set), -3 if a
+ * reference (`tc=') loop was detected, and 0 means success.
+ *
+ * Copied from lpr; should add additional capabilities as they
+ * are required by the other programs in the suite so that
+ * printcap-reading is consistent across the entire family.
+ */
+int
+getprintcap(const char *printer, struct printer *pp)
+{
+ int status;
+ char *XXX;
+ char *bp;
+
+ /*
+ * A bug in the declaration of cgetent(3) means that we have
+ * to hide the constness of its third argument.
+ */
+ XXX = (char *)printer;
+ if ((status = cgetent(&bp, printcapdb, XXX)) < 0)
+ return status;
+ status = getprintcap_int(bp, pp);
+ free(bp);
+ return status;
+}
+
+/*
+ * Map the status values returned by cgetfirst/cgetnext into those
+ * used by cgetent, returning truth if there are more records to
+ * examine. This points out what is arguably a bug in the cget*
+ * interface (or at least a nasty wart).
+ */
+static int
+firstnextmap(int *status)
+{
+ switch (*status) {
+ case 0:
+ return 0;
+ case 1:
+ *status = 0;
+ return 1;
+ case 2:
+ *status = 1;
+ return 1;
+ case -1:
+ *status = -2;
+ return 0;
+ case -2:
+ *status = -3;
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+/*
+ * Scan through the database of printers using cgetfirst/cgetnext.
+ * Return false of error or end-of-database; else true.
+ */
+int
+firstprinter(struct printer *pp, int *error)
+{
+ int status;
+ char *bp;
+
+ init_printer(pp);
+ status = cgetfirst(&bp, printcapdb);
+ if (firstnextmap(&status) == 0) {
+ if (error)
+ *error = status;
+ return 0;
+ }
+ if (error)
+ *error = status;
+ status = getprintcap_int(bp, pp);
+ free(bp);
+ if (error && status)
+ *error = status;
+ return 1;
+}
+
+int
+nextprinter(struct printer *pp, int *error)
+{
+ int status;
+ char *bp;
+
+ free_printer(pp);
+ status = cgetnext(&bp, printcapdb);
+ if (firstnextmap(&status) == 0) {
+ if (error)
+ *error = status;
+ return 0;
+ }
+ if (error)
+ *error = status;
+ status = getprintcap_int(bp, pp);
+ free(bp);
+ if (error && status)
+ *error = status;
+ return 1;
+}
+
+void
+lastprinter(void)
+{
+ cgetclose();
+}
+
+/*
+ * This must match the order of declaration of enum filter in lp.h.
+ */
+static const char *filters[] = {
+ "cf", "df", "gf", "if", "nf", "of", "rf", "tf", "vf"
+};
+
+static const char *longfilters[] = {
+ "filt.cifplot", "filt.dvi", "filt.plot", "filt.input", "filt.ditroff",
+ "filt.output", "filt.fortran", "filt.troff", "filt.raster"
+};
+
+/*
+ * Internal routine for both getprintcap() and nextprinter().
+ * Actually parse the printcap entry using cget* functions.
+ * Also attempt to figure out the canonical name of the printer
+ * and store a malloced copy of it in pp->printer.
+ */
+static int
+getprintcap_int(char *bp, struct printer *pp)
+{
+ enum lpd_filters filt;
+ char *rp_name;
+ int error;
+
+ if ((pp->printer = capdb_canonical_name(bp)) == 0)
+ return PCAPERR_OSERR;
+
+#define CHK(x) do {if ((x) == PCAPERR_OSERR) return PCAPERR_OSERR;}while(0)
+ CHK(capdb_getaltstr(bp, "af", "acct.file", 0, &pp->acct_file));
+ CHK(capdb_getaltnum(bp, "br", "tty.rate", 0, &pp->baud_rate));
+ CHK(capdb_getaltnum(bp, "ct", "remote.timeout", DEFTIMEOUT,
+ &pp->conn_timeout));
+ CHK(capdb_getaltnum(bp, "du", "daemon.user", DEFUID,
+ &pp->daemon_user));
+ CHK(capdb_getaltstr(bp, "ff", "job.formfeed", DEFFF, &pp->form_feed));
+ CHK(capdb_getaltstr(bp, "lf", "spool.log", _PATH_CONSOLE,
+ &pp->log_file));
+ CHK(capdb_getaltstr(bp, "lo", "spool.lock", DEFLOCK, &pp->lock_file));
+ CHK(capdb_getaltstr(bp, "lp", "tty.device", _PATH_DEFDEVLP, &pp->lp));
+ CHK(capdb_getaltnum(bp, "mc", "max.copies", DEFMAXCOPIES,
+ &pp->max_copies));
+ CHK(capdb_getaltstr(bp, "ms", "tty.mode", 0, &pp->mode_set));
+ CHK(capdb_getaltnum(bp, "mx", "max.blocks", DEFMX, &pp->max_blocks));
+ CHK(capdb_getaltnum(bp, "pc", "acct.price", 0, &pp->price100));
+ CHK(capdb_getaltnum(bp, "pl", "page.length", DEFLENGTH,
+ &pp->page_length));
+ CHK(capdb_getaltnum(bp, "pw", "page.width", DEFWIDTH,
+ &pp->page_width));
+ CHK(capdb_getaltnum(bp, "px", "page.pwidth", 0, &pp->page_pwidth));
+ CHK(capdb_getaltnum(bp, "py", "page.plength", 0, &pp->page_plength));
+ CHK(capdb_getaltstr(bp, "rg", "daemon.restrictgrp", 0,
+ &pp->restrict_grp));
+ CHK(capdb_getaltstr(bp, "rm", "remote.host", 0, &pp->remote_host));
+ CHK(capdb_getaltstr(bp, "rp", "remote.queue", DEFLP,
+ &pp->remote_queue));
+ CHK(capdb_getaltstr(bp, "sd", "spool.dir", _PATH_DEFSPOOL,
+ &pp->spool_dir));
+ CHK(capdb_getaltstr(bp, "sr", "stat.recv", 0, &pp->stat_recv));
+ CHK(capdb_getaltstr(bp, "ss", "stat.send", 0, &pp->stat_send));
+ CHK(capdb_getaltstr(bp, "st", "spool.status", DEFSTAT,
+ &pp->status_file));
+ CHK(capdb_getaltstr(bp, "tr", "job.trailer", 0, &pp->trailer));
+
+ pp->resend_copies = capdb_getaltlog(bp, "rc", "remote.resend_copies");
+ pp->restricted = capdb_getaltlog(bp, "rs", "daemon.restricted");
+ pp->short_banner = capdb_getaltlog(bp, "sb", "banner.short");
+ pp->no_copies = capdb_getaltlog(bp, "sc", "job.no_copies");
+ pp->no_formfeed = capdb_getaltlog(bp, "sf", "job.no_formfeed");
+ pp->no_header = capdb_getaltlog(bp, "sh", "banner.disable");
+ pp->header_last = capdb_getaltlog(bp, "hl", "banner.last");
+ pp->rw = capdb_getaltlog(bp, "rw", "tty.rw");
+ pp->tof = !capdb_getaltlog(bp, "fo", "job.topofform");
+
+ /*
+ * Decide if the remote printer name matches the local printer name.
+ * If no name is given then we assume they mean them to match.
+ * If a name is given see if the rp_name is one of the names for
+ * this printer.
+ */
+ pp->rp_matches_local = 1;
+ CHK((error = capdb_getaltstr(bp, "rp", "remote.queue", 0, &rp_name)));
+ if (error != PCAPERR_NOTFOUND && rp_name != NULL) {
+ if (cgetmatch(bp,rp_name) != 0)
+ pp->rp_matches_local = 0;
+ free(rp_name);
+ }
+
+ /*
+ * Filters:
+ */
+ for (filt = 0; filt < LPF_COUNT; filt++) {
+ CHK(capdb_getaltstr(bp, filters[filt], longfilters[filt], 0,
+ &pp->filters[filt]));
+ }
+
+ return 0;
+}
+
+/*
+ * Decode the error codes returned by cgetent() using the names we
+ * made up for them from "lp.h".
+ * This would have been much better done with Common Error, >sigh<.
+ * Perhaps this can be fixed in the next incarnation of cget*.
+ */
+const char *
+pcaperr(int error)
+{
+ switch(error) {
+ case PCAPERR_TCOPEN:
+ return "unresolved tc= expansion";
+ case PCAPERR_SUCCESS:
+ return "no error";
+ case PCAPERR_NOTFOUND:
+ return "printer not found";
+ case PCAPERR_OSERR:
+ return strerror(errno);
+ case PCAPERR_TCLOOP:
+ return "loop detected in tc= expansion";
+ default:
+ return "unknown printcap error";
+ }
+}
+
+/*
+ * Initialize a `struct printer' to contain values harmless to
+ * the other routines in liblpr.
+ */
+void
+init_printer(struct printer *pp)
+{
+ static struct printer zero;
+ *pp = zero;
+}
+
+/*
+ * Free the dynamically-allocated strings in a `struct printer'.
+ * Idempotent.
+ */
+void
+free_printer(struct printer *pp)
+{
+ enum lpd_filters filt;
+#define cfree(x) do { if (x) free(x); } while(0)
+ cfree(pp->printer);
+ cfree(pp->acct_file);
+ for (filt = 0; filt < LPF_COUNT; filt++)
+ cfree(pp->filters[filt]);
+ cfree(pp->form_feed);
+ cfree(pp->log_file);
+ cfree(pp->lock_file);
+ cfree(pp->lp);
+ cfree(pp->restrict_grp);
+ cfree(pp->remote_host);
+ cfree(pp->remote_queue);
+ cfree(pp->spool_dir);
+ cfree(pp->stat_recv);
+ cfree(pp->stat_send);
+ cfree(pp->status_file);
+ cfree(pp->trailer);
+ cfree(pp->mode_set);
+
+ init_printer(pp);
+}
+
+
+/*
+ * The following routines are part of what would be a sensible library
+ * interface to capability databases. Maybe someday this will become
+ * the default.
+ */
+
+/*
+ * It provides similar functionality to cgetstr(),
+ * except that it provides for both a long and a short
+ * capability name and allows for a default to be specified.
+ */
+static int
+capdb_getaltstr(char *bp, const char *shrt, const char *lng,
+ const char *dflt, char **result)
+{
+ int status;
+
+ status = cgetstr(bp, (char *)/*XXX*/lng, result);
+ if (status >= 0 || status == PCAPERR_OSERR)
+ return status;
+ status = cgetstr(bp, (char *)/*XXX*/shrt, result);
+ if (status >= 0 || status == PCAPERR_OSERR)
+ return status;
+ if (dflt) {
+ *result = strdup(dflt);
+ if (*result == 0)
+ return PCAPERR_OSERR;
+ return strlen(*result);
+ }
+ return PCAPERR_NOTFOUND;
+}
+
+/*
+ * The same, only for integers.
+ */
+static int
+capdb_getaltnum(char *bp, const char *shrt, const char *lng, long dflt,
+ long *result)
+{
+ int status;
+
+ status = cgetnum(bp, (char *)/*XXX*/lng, result);
+ if (status >= 0)
+ return status;
+ status = cgetnum(bp, (char *)/*XXX*/shrt, result);
+ if (status >= 0)
+ return status;
+ *result = dflt;
+ return 0;
+}
+
+/*
+ * Likewise for logical values. There's no need for a default parameter
+ * because the default is always false.
+ */
+static int
+capdb_getaltlog(char *bp, const char *shrt, const char *lng)
+{
+ if (cgetcap(bp, (char *)/*XXX*/lng, ':'))
+ return 1;
+ if (cgetcap(bp, (char *)/*XXX*/shrt, ':'))
+ return 1;
+ return 0;
+}
+
+/*
+ * Also should be a part of a better cget* library.
+ * Given a capdb entry, attempt to figure out what its canonical name
+ * is, and return a malloced copy of it. The canonical name is
+ * considered to be the first one listed.
+ */
+static char *
+capdb_canonical_name(const char *bp)
+{
+ char *retval;
+ const char *nameend;
+
+ nameend = strpbrk(bp, "|:");
+ if (nameend == 0)
+ nameend = bp + 1;
+ if ((retval = malloc(nameend - bp + 1)) != 0) {
+ retval[0] = '\0';
+ strncat(retval, bp, nameend - bp);
+ }
+ return retval;
+}
+
+
diff --git a/usr.sbin/lpr/common_source/request.c b/usr.sbin/lpr/common_source/request.c
new file mode 100644
index 0000000..b650e5c
--- /dev/null
+++ b/usr.sbin/lpr/common_source/request.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright 1997 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that both the above copyright notice and this
+ * permission notice appear in all copies, that both the above
+ * copyright notice and this permission notice appear in all
+ * supporting documentation, and that the name of M.I.T. not be used
+ * in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. M.I.T. makes
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
+ * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+static const char copyright[] =
+ "Copyright (C) 1997, Massachusetts Institute of Technology\r\n";
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <sys/param.h> /* needed for lp.h but not used here */
+#include <dirent.h> /* ditto */
+#include <stdio.h> /* ditto */
+#include "lp.h"
+#include "lp.local.h"
+
+void
+init_request(struct request *rp)
+{
+ static struct request zero;
+
+ *rp = zero;
+ TAILQ_INIT(&rp->users);
+ TAILQ_INIT(&rp->jobids);
+}
+
+void
+free_request(struct request *rp)
+{
+ struct req_user *ru;
+ struct req_jobid *rj;
+
+ if (rp->logname)
+ free(rp->logname);
+ if (rp->authname)
+ free(rp->authname);
+ if (rp->prettyname)
+ free(rp->prettyname);
+ if (rp->authinfo)
+ free(rp->authinfo);
+ while ((ru = TAILQ_FIRST(&rp->users)) != 0) {
+ TAILQ_REMOVE(&rp->users, ru, ru_link);
+ free(ru);
+ }
+ while ((rj = TAILQ_FIRST(&rp->jobids)) != 0) {
+ TAILQ_REMOVE(&rp->jobids, rj, rj_link);
+ free(rj);
+ }
+ init_request(rp);
+}
diff --git a/usr.sbin/lpr/common_source/rmjob.c b/usr.sbin/lpr/common_source/rmjob.c
new file mode 100644
index 0000000..d6fd614
--- /dev/null
+++ b/usr.sbin/lpr/common_source/rmjob.c
@@ -0,0 +1,397 @@
+/*
+ * 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.
+ */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)rmjob.c 8.2 (Berkeley) 4/28/95";
+#endif /* not lint */
+#endif
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/uio.h>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#define psignal foil_gcc_psignal
+#define sys_siglist foil_gcc_siglist
+#include <unistd.h>
+#undef psignal
+#undef sys_siglist
+
+#include "lp.h"
+#include "lp.local.h"
+#include "pathnames.h"
+
+/*
+ * rmjob - remove the specified jobs from the queue.
+ */
+
+/*
+ * Stuff for handling lprm specifications
+ */
+static char root[] = "root";
+static int all = 0; /* eliminate all files (root only) */
+static int cur_daemon; /* daemon's pid */
+static char current[7+MAXHOSTNAMELEN]; /* active control file name */
+
+extern uid_t uid, euid; /* real and effective user id's */
+
+static void alarmhandler(int _signo);
+static void do_unlink(char *_file);
+static int isowner(char *_owner, char *_file, const char *_cfhost);
+
+void
+rmjob(const char *printer)
+{
+ register int i, nitems;
+ int assasinated = 0;
+ struct dirent **files;
+ char *cp;
+ struct printer myprinter, *pp = &myprinter;
+
+ init_printer(pp);
+ if ((i = getprintcap(printer, pp)) < 0)
+ fatal(pp, "getprintcap: %s", pcaperr(i));
+ if ((cp = checkremote(pp))) {
+ printf("Warning: %s\n", cp);
+ free(cp);
+ }
+
+ /*
+ * If the format was `lprm -' and the user isn't the super-user,
+ * then fake things to look like he said `lprm user'.
+ */
+ if (users < 0) {
+ if (getuid() == 0)
+ all = 1; /* all files in local queue */
+ else {
+ user[0] = person;
+ users = 1;
+ }
+ }
+ if (!strcmp(person, "-all")) {
+ if (from_host == local_host)
+ fatal(pp, "The login name \"-all\" is reserved");
+ all = 1; /* all those from 'from_host' */
+ person = root;
+ }
+
+ seteuid(euid);
+ if (chdir(pp->spool_dir) < 0)
+ fatal(pp, "cannot chdir to spool directory");
+ if ((nitems = scandir(".", &files, iscf, NULL)) < 0)
+ fatal(pp, "cannot access spool directory");
+ seteuid(uid);
+
+ if (nitems) {
+ /*
+ * Check for an active printer daemon (in which case we
+ * kill it if it is reading our file) then remove stuff
+ * (after which we have to restart the daemon).
+ */
+ if (lockchk(pp, pp->lock_file) && chk(current)) {
+ seteuid(euid);
+ assasinated = kill(cur_daemon, SIGINT) == 0;
+ seteuid(uid);
+ if (!assasinated)
+ fatal(pp, "cannot kill printer daemon");
+ }
+ /*
+ * process the files
+ */
+ for (i = 0; i < nitems; i++)
+ process(pp, files[i]->d_name);
+ }
+ rmremote(pp);
+ /*
+ * Restart the printer daemon if it was killed
+ */
+ if (assasinated && !startdaemon(pp))
+ fatal(pp, "cannot restart printer daemon\n");
+ exit(0);
+}
+
+/*
+ * Process a lock file: collect the pid of the active
+ * daemon and the file name of the active spool entry.
+ * Return boolean indicating existence of a lock file.
+ */
+int
+lockchk(struct printer *pp, char *slockf)
+{
+ register FILE *fp;
+ register int i, n;
+
+ seteuid(euid);
+ if ((fp = fopen(slockf, "r")) == NULL) {
+ if (errno == EACCES)
+ fatal(pp, "%s: %s", slockf, strerror(errno));
+ else
+ return(0);
+ }
+ seteuid(uid);
+ if (!getline(fp)) {
+ (void) fclose(fp);
+ return(0); /* no daemon present */
+ }
+ cur_daemon = atoi(line);
+ if (kill(cur_daemon, 0) < 0 && errno != EPERM) {
+ (void) fclose(fp);
+ return(0); /* no daemon present */
+ }
+ for (i = 1; (n = fread(current, sizeof(char), sizeof(current), fp)) <= 0; i++) {
+ if (i > 5) {
+ n = 1;
+ break;
+ }
+ sleep(i);
+ }
+ current[n-1] = '\0';
+ (void) fclose(fp);
+ return(1);
+}
+
+/*
+ * Process a control file.
+ */
+void
+process(const struct printer *pp, char *file)
+{
+ FILE *cfp;
+
+ if (!chk(file))
+ return;
+ seteuid(euid);
+ if ((cfp = fopen(file, "r")) == NULL)
+ fatal(pp, "cannot open %s", file);
+ seteuid(uid);
+ while (getline(cfp)) {
+ switch (line[0]) {
+ case 'U': /* unlink associated files */
+ if (strchr(line+1, '/') || strncmp(line+1, "df", 2))
+ break;
+ do_unlink(line+1);
+ }
+ }
+ (void) fclose(cfp);
+ do_unlink(file);
+}
+
+static void
+do_unlink(char *file)
+{
+ int ret;
+
+ if (from_host != local_host)
+ printf("%s: ", local_host);
+ seteuid(euid);
+ ret = unlink(file);
+ seteuid(uid);
+ printf(ret ? "cannot dequeue %s\n" : "%s dequeued\n", file);
+}
+
+/*
+ * Do the dirty work in checking
+ */
+int
+chk(char *file)
+{
+ int *r, jnum;
+ char **u;
+ const char *cfhost;
+ FILE *cfp;
+
+ /*
+ * Check for valid cf file name (mostly checking current).
+ */
+ if (strlen(file) < 7 || file[0] != 'c' || file[1] != 'f')
+ return(0);
+
+ jnum = calc_jobnum(file, &cfhost);
+ if (all && (from_host == local_host || !strcmp(from_host, cfhost)))
+ return(1);
+
+ /*
+ * get the owner's name from the control file.
+ */
+ seteuid(euid);
+ if ((cfp = fopen(file, "r")) == NULL)
+ return(0);
+ seteuid(uid);
+ while (getline(cfp)) {
+ if (line[0] == 'P')
+ break;
+ }
+ (void) fclose(cfp);
+ if (line[0] != 'P')
+ return(0);
+
+ if (users == 0 && requests == 0)
+ return(!strcmp(file, current) && isowner(line+1, file, cfhost));
+ /*
+ * Check the request list
+ */
+ for (r = requ; r < &requ[requests]; r++)
+ if (*r == jnum && isowner(line+1, file, cfhost))
+ return(1);
+ /*
+ * Check to see if it's in the user list
+ */
+ for (u = user; u < &user[users]; u++)
+ if (!strcmp(*u, line+1) && isowner(line+1, file, cfhost))
+ return(1);
+ return(0);
+}
+
+/*
+ * If root is removing a file on the local machine, allow it.
+ * If root is removing a file from a remote machine, only allow
+ * files sent from the remote machine to be removed.
+ * Normal users can only remove the file from where it was sent.
+ */
+static int
+isowner(char *owner, char *file, const char *cfhost)
+{
+ if (!strcmp(person, root) && (from_host == local_host ||
+ !strcmp(from_host, cfhost)))
+ return (1);
+ if (!strcmp(person, owner) && !strcmp(from_host, cfhost))
+ return (1);
+ if (from_host != local_host)
+ printf("%s: ", local_host);
+ printf("%s: Permission denied\n", file);
+ return(0);
+}
+
+/*
+ * Check to see if we are sending files to a remote machine. If we are,
+ * then try removing files on the remote machine.
+ */
+void
+rmremote(const struct printer *pp)
+{
+ int i, elem, firstreq, niov, rem, totlen;
+ char buf[BUFSIZ];
+ void (*savealrm)(int);
+ struct iovec *iov;
+
+ if (!pp->remote)
+ return; /* not sending to a remote machine */
+
+ /*
+ * Flush stdout so the user can see what has been deleted
+ * while we wait (possibly) for the connection.
+ */
+ fflush(stdout);
+
+ /*
+ * Counting:
+ * 4 == "\5" + remote_queue + " " + person
+ * 2 * users == " " + user[i] for each user
+ * requests == asprintf results for each request
+ * 1 == "\n"
+ * Although laborious, doing it this way makes it possible for
+ * us to process requests of indeterminate length without
+ * applying an arbitrary limit. Arbitrary Limits Are Bad (tm).
+ */
+ if (users > 0)
+ niov = 4 + 2 * users + requests + 1;
+ else
+ niov = 4 + requests + 1;
+ iov = malloc(niov * sizeof *iov);
+ if (iov == 0)
+ fatal(pp, "out of memory in rmremote()");
+ iov[0].iov_base = "\5";
+ iov[1].iov_base = pp->remote_queue;
+ iov[2].iov_base = " ";
+ iov[3].iov_base = all ? "-all" : person;
+ elem = 4;
+ for (i = 0; i < users; i++) {
+ iov[elem].iov_base = " ";
+ iov[elem + 1].iov_base = user[i];
+ elem += 2;
+ }
+ firstreq = elem;
+ for (i = 0; i < requests; i++) {
+ asprintf((char **)&iov[elem].iov_base, " %d", requ[i]);
+ if (iov[elem].iov_base == 0)
+ fatal(pp, "out of memory in rmremote()");
+ elem++;
+ }
+ iov[elem++].iov_base = "\n";
+ for (totlen = i = 0; i < niov; i++)
+ totlen += (iov[i].iov_len = strlen(iov[i].iov_base));
+
+ savealrm = signal(SIGALRM, alarmhandler);
+ alarm(pp->conn_timeout);
+ rem = getport(pp, pp->remote_host, 0);
+ (void)signal(SIGALRM, savealrm);
+ if (rem < 0) {
+ if (from_host != local_host)
+ printf("%s: ", local_host);
+ printf("connection to %s is down\n", pp->remote_host);
+ } else {
+ if (writev(rem, iov, niov) != totlen)
+ fatal(pp, "Lost connection");
+ while ((i = read(rem, buf, sizeof(buf))) > 0)
+ (void) fwrite(buf, 1, i, stdout);
+ (void) close(rem);
+ }
+ for (i = 0; i < requests; i++)
+ free(iov[firstreq + i].iov_base);
+ free(iov);
+}
+
+/*
+ * Return 1 if the filename begins with 'cf'
+ */
+int
+iscf(struct dirent *d)
+{
+ return(d->d_name[0] == 'c' && d->d_name[1] == 'f');
+}
+
+void
+alarmhandler(int signo __unused)
+{
+ /* the signal is ignored */
+ /* (the '__unused' is just to avoid a compile-time warning) */
+}
diff --git a/usr.sbin/lpr/common_source/startdaemon.c b/usr.sbin/lpr/common_source/startdaemon.c
new file mode 100644
index 0000000..08c2e6f
--- /dev/null
+++ b/usr.sbin/lpr/common_source/startdaemon.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 1983, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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 char sccsid[] = "@(#)startdaemon.c 8.2 (Berkeley) 4/17/94";
+#endif /* not lint */
+#endif
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+
+#include <dirent.h>
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include "lp.h"
+#include "pathnames.h"
+
+extern uid_t uid, euid;
+
+/*
+ * Tell the printer daemon that there are new files in the spool directory.
+ */
+
+int
+startdaemon(const struct printer *pp)
+{
+ struct sockaddr_un un;
+ register int s, n;
+ int connectres;
+ char c;
+
+ s = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (s < 0) {
+ warn("socket");
+ return(0);
+ }
+ memset(&un, 0, sizeof(un));
+ un.sun_family = AF_LOCAL;
+ strcpy(un.sun_path, _PATH_SOCKETNAME);
+#ifndef SUN_LEN
+#define SUN_LEN(unp) (strlen((unp)->sun_path) + 2)
+#endif
+ seteuid(euid);
+ connectres = connect(s, (struct sockaddr *)&un, SUN_LEN(&un));
+ seteuid(uid);
+ if (connectres < 0) {
+ warn("Unable to connect to %s", _PATH_SOCKETNAME);
+ warnx("Check to see if the master 'lpd' process is running.");
+ (void) close(s);
+ return(0);
+ }
+
+ /*
+ * Avoid overruns without putting artificial limitations on
+ * the length.
+ */
+ if (writel(s, "\1", pp->printer, "\n", (char *)0) <= 0) {
+ warn("write");
+ (void) close(s);
+ return(0);
+ }
+ if (read(s, &c, 1) == 1) {
+ if (c == '\0') { /* everything is OK */
+ (void) close(s);
+ return(1);
+ }
+ putchar(c);
+ }
+ while ((n = read(s, &c, 1)) > 0)
+ putchar(c);
+ (void) close(s);
+ return(0);
+}
diff --git a/usr.sbin/lpr/filters.ru/Makefile b/usr.sbin/lpr/filters.ru/Makefile
new file mode 100644
index 0000000..ee15541
--- /dev/null
+++ b/usr.sbin/lpr/filters.ru/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+SUBDIR= koi2alt koi2855
+
+FILES= bjc-240.sh.sample
+
+.include "Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/filters.ru/Makefile.inc b/usr.sbin/lpr/filters.ru/Makefile.inc
new file mode 100644
index 0000000..a312e73
--- /dev/null
+++ b/usr.sbin/lpr/filters.ru/Makefile.inc
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+BINDIR= /usr/libexec/lpr/ru
diff --git a/usr.sbin/lpr/filters.ru/bjc-240.sh.sample b/usr.sbin/lpr/filters.ru/bjc-240.sh.sample
new file mode 100644
index 0000000..1bc907a
--- /dev/null
+++ b/usr.sbin/lpr/filters.ru/bjc-240.sh.sample
@@ -0,0 +1,64 @@
+#!/bin/sh
+#
+# Canon BJC-240 Setup script
+#
+# Settings are:
+# Epson LQ emulation, A4, Code Page 866, 66 lines, Roman font, Smoothing,
+# HQ mode, no CR translation, power off in 10min, auto power on
+#
+
+printf "\033[K\00200\037" | tr 0 "\0"
+
+cat << EOF1
+BJLSTART
+ControlMode=BJ
+Font=Roman
+PageLength=12
+CodePage=866
+AutoLF=Off
+TextScaleMode=On
+AutoCR=Off
+CharacterSet=Set2
+AGM=Off
+BJLEND
+EOF1
+
+printf "\033[K\00200\037" | tr 0 "\0"
+
+cat << EOF2
+BJLSTART
+ControlMode=LQ
+Font=Roman
+PageLength=12
+CodePage=866
+AutoLF=Off
+TextScaleMode=On
+CharacterSet=Graphics
+International=USA
+BJLEND
+EOF2
+
+printf "\033[K\00200\037" | tr 0 "\0"
+
+cat << EOF3
+BJLSTART
+@SetControlMode=LQ
+BJLEND
+EOF3
+
+printf "\033[K\00200\037" | tr 0 "\0"
+
+cat << EOF4
+BJLSTART
+ControlMode=Common
+PrintMode=HQ
+Reduction=Off
+Smoothing=On
+PaperSelect=A4
+I/D-Buffer=Input
+AutoPowerOff=10
+AutoPowerOn=Enable
+BJLEND
+EOF4
+
+exec /usr/libexec/lpr/ru/koi2alt $*
diff --git a/usr.sbin/lpr/filters.ru/koi2855/Makefile b/usr.sbin/lpr/filters.ru/koi2855/Makefile
new file mode 100644
index 0000000..361783e
--- /dev/null
+++ b/usr.sbin/lpr/filters.ru/koi2855/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= koi2855
+NO_MAN=
+
+CFLAGS+= -I${.CURDIR}/../../common_source
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/filters.ru/koi2855/koi2855.c b/usr.sbin/lpr/filters.ru/koi2855/koi2855.c
new file mode 100644
index 0000000..8ecc3ef
--- /dev/null
+++ b/usr.sbin/lpr/filters.ru/koi2855/koi2855.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 1999 by Andrey A. Chernov, Moscow, Russia.
+ * (C) 18 Sep 1999, Alex G. Bulushev (bag@demos.su)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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.
+ */
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+__FBSDID("$FreeBSD$");
+
+/*
+ * KOI8-R -> CP855 conversion filter (Russian character sets)
+ */
+
+#include <sys/types.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+int length = 66;
+int lines;
+
+unsigned char koi2855 [] = {
+0xc4, 0xb3, 0xda, 0xbf, 0xc0, 0xd9, 0xc3, 0xb4,
+0xc2, 0xc1, 0xc5, 0xdf, 0xdc, 0xdb, 0xdb, 0xdb,
+0xb0, 0xb1, 0xb2, 0xda, 0xfe, 0x2e, 0xad, 0x3d,
+0xae, 0xaf, 0xff, 0xd9, 0xcf, 0x32, 0x2e, 0x25,
+0xcd, 0xba, 0xc9, 0x84, 0xc9, 0xc9, 0xbb, 0xbb,
+0xbb, 0xc8, 0xc8, 0xc8, 0xbc, 0xbc, 0xbc, 0xcc,
+0xcc, 0xcc, 0xb9, 0x85, 0xb9, 0xb9, 0xcb, 0xcb,
+0xcb, 0xca, 0xca, 0xca, 0xce, 0xce, 0xce, 0x43,
+0x9c, 0xa0, 0xa2, 0xa4, 0xa6, 0xa8, 0xaa, 0xac,
+0xb5, 0xb7, 0xbd, 0xc6, 0xd0, 0xd2, 0xd4, 0xd6,
+0xd8, 0xde, 0xe1, 0xe3, 0xe5, 0xe7, 0xe9, 0xeb,
+0xed, 0xf1, 0xf3, 0xf5, 0xf7, 0xf9, 0xfb, 0x9e,
+0x9d, 0xa1, 0xa3, 0xa5, 0xa7, 0xa9, 0xab, 0xad,
+0xb6, 0xb8, 0xbe, 0xc7, 0xd1, 0xd3, 0xd5, 0xd7,
+0xdd, 0xe0, 0xe2, 0xe4, 0xe6, 0xe8, 0xea, 0xec,
+0xee, 0xf2, 0xf4, 0xf6, 0xf8, 0xfa, 0xfc, 0x9f
+};
+
+int main(int argc, char *argv[])
+{
+ int c, i;
+ char *cp;
+
+ while (--argc) {
+ if (*(cp = *++argv) == '-') {
+ switch (*++cp) {
+ case 'l':
+ if ((i = atoi(++cp)) > 0)
+ length = i;
+ break;
+ }
+ }
+ }
+
+ while ((c = getchar()) != EOF) {
+ if (c == '\031') {
+ if ((c = getchar()) == '\1') {
+ lines = 0;
+ fflush(stdout);
+ kill(getpid(), SIGSTOP);
+ continue;
+ } else {
+ ungetc(c, stdin);
+ c = '\031';
+ }
+ } else if (c & 0x80) {
+ putchar(koi2855[c & 0x7F]);
+ continue;
+ } else if (c == '\n')
+ lines++;
+ else if (c == '\f')
+ lines = length;
+ putchar(c);
+ if (lines >= length) {
+ lines = 0;
+ fflush(stdout);
+ }
+ }
+ return 0;
+}
diff --git a/usr.sbin/lpr/filters.ru/koi2alt/Makefile b/usr.sbin/lpr/filters.ru/koi2alt/Makefile
new file mode 100644
index 0000000..e410d9a
--- /dev/null
+++ b/usr.sbin/lpr/filters.ru/koi2alt/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= koi2alt
+NO_MAN=
+
+CFLAGS+= -I${.CURDIR}/../../common_source
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/filters.ru/koi2alt/koi2alt.c b/usr.sbin/lpr/filters.ru/koi2alt/koi2alt.c
new file mode 100644
index 0000000..a7a5b5d
--- /dev/null
+++ b/usr.sbin/lpr/filters.ru/koi2alt/koi2alt.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 1993-98 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.
+ */
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+__FBSDID("$FreeBSD$");
+
+/*
+ * KOI8-R -> CP866 conversion filter (Russian character sets)
+ */
+
+#include <sys/types.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+int length = 66;
+int lines;
+
+char *koi2alt[] = {
+/* 0 1 2 3 4 5 6 7 */
+/* 8 9 A B C D E F */
+/* 8 */ "\xc4","\xb3","\xda","\xbf","\xc0","\xd9","\xc3","\xb4",
+ "\xc2","\xc1","\xc5","\xdf","\xdc","\xdb","\xdd","\xde",
+/* 9 */ "\xb0","\xb1","\xb2","\xb3","\xfe","\xf9","\xfb","-\b~",
+ "<\b_",">\b_","\xff","\xb3","\xf8","2\b-","\xfa",":\b-",
+/* A */ "\xcd","\xba","\xd5","\xf1","\xd6","\xc9","\xb8","\xb7",
+ "\xbb","\xd4","\xd3","\xc8","\xbe","\xbd","\xbc","\xc6",
+/* B */ "\xc7","\xcc","\xb5","\xf0","\xb6","\xb9","\xd1","\xd2",
+ "\xcb","\xcf","\xd0","\xca","\xd8","\xd7","\xce","c\b_",
+/* C */ "\xee","\xa0","\xa1","\xe6","\xa4","\xa5","\xe4","\xa3",
+ "\xe5","\xa8","\xa9","\xaa","\xab","\xac","\xad","\xae",
+/* D */ "\xaf","\xef","\xe0","\xe1","\xe2","\xe3","\xa6","\xa2",
+ "\xec","\xeb","\xa7","\xe8","\xed","\xe9","\xe7","\xea",
+/* E */ "\x9e","\x80","\x81","\x96","\x84","\x85","\x94","\x83",
+ "\x95","\x88","\x89","\x8a","\x8b","\x8c","\x8d","\x8e",
+/* F */ "\x8f","\x9f","\x90","\x91","\x92","\x93","\x86","\x82",
+ "\x9c","\x9b","\x87","\x98","\x9d","\x99","\x97","\x9a"
+};
+
+int main(int argc, char *argv[])
+{
+ int c, i;
+ char *cp;
+
+ while (--argc) {
+ if (*(cp = *++argv) == '-') {
+ switch (*++cp) {
+ case 'l':
+ if ((i = atoi(++cp)) > 0)
+ length = i;
+ break;
+ }
+ }
+ }
+
+ while ((c = getchar()) != EOF) {
+ if (c == '\031') {
+ if ((c = getchar()) == '\1') {
+ lines = 0;
+ fflush(stdout);
+ kill(getpid(), SIGSTOP);
+ continue;
+ } else {
+ ungetc(c, stdin);
+ c = '\031';
+ }
+ } else if (c & 0x80) {
+ fputs(koi2alt[c & 0x7F], stdout);
+ continue;
+ } else if (c == '\n')
+ lines++;
+ else if (c == '\f')
+ lines = length;
+ putchar(c);
+ if (lines >= length) {
+ lines = 0;
+ fflush(stdout);
+ }
+ }
+ return 0;
+}
diff --git a/usr.sbin/lpr/filters/Makefile b/usr.sbin/lpr/filters/Makefile
new file mode 100644
index 0000000..b55b536
--- /dev/null
+++ b/usr.sbin/lpr/filters/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+BINDIR= /usr/libexec/lpr
+
+PROG= lpf
+NO_MAN=
+
+CFLAGS+= -I${.CURDIR}/../common_source
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/filters/lpf.c b/usr.sbin/lpr/filters/lpf.c
new file mode 100644
index 0000000..41879a2
--- /dev/null
+++ b/usr.sbin/lpr/filters/lpf.c
@@ -0,0 +1,221 @@
+/*
+ * 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.
+ */
+
+#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 */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)lpf.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+#endif
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+__FBSDID("$FreeBSD$");
+
+/*
+ * filter which reads the output of nroff and converts lines
+ * with ^H's to overwritten lines. Thus this works like 'ul'
+ * but is much better: it can handle more than 2 overwrites
+ * and it is written with some style.
+ * modified by kls to use register references instead of arrays
+ * to try to gain a little speed.
+ */
+
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define MAXWIDTH 132
+#define MAXREP 10
+
+char buf[MAXREP][MAXWIDTH];
+int maxcol[MAXREP] = {-1};
+int lineno;
+int width = 132; /* default line length */
+int length = 66; /* page length */
+int indent; /* indentation length */
+int npages = 1;
+int literal; /* print control characters */
+char *name; /* user's login name */
+char *host; /* user's machine name */
+char *acctfile; /* accounting information file */
+
+int
+main(int argc, char *argv[])
+{
+ register FILE *p = stdin, *o = stdout;
+ register int i, col;
+ register char *cp;
+ int done, linedone, maxrep;
+ char *limit;
+ int ch;
+
+ while (--argc) {
+ if (*(cp = *++argv) == '-') {
+ switch (cp[1]) {
+ case 'n':
+ argc--;
+ name = *++argv;
+ break;
+
+ case 'h':
+ argc--;
+ host = *++argv;
+ break;
+
+ case 'w':
+ if ((i = atoi(&cp[2])) > 0 && i <= MAXWIDTH)
+ width = i;
+ break;
+
+ case 'l':
+ length = atoi(&cp[2]);
+ break;
+
+ case 'i':
+ indent = atoi(&cp[2]);
+ break;
+
+ case 'c': /* Print control chars */
+ literal++;
+ break;
+ }
+ } else
+ acctfile = cp;
+ }
+
+ for (cp = buf[0], limit = buf[MAXREP]; cp < limit; *cp++ = ' ');
+ done = 0;
+
+ while (!done) {
+ col = indent;
+ maxrep = -1;
+ linedone = 0;
+ while (!linedone) {
+ switch (ch = getc(p)) {
+ case EOF:
+ linedone = done = 1;
+ ch = '\n';
+ break;
+
+ case '\f':
+ lineno = length;
+ case '\n':
+ if (maxrep < 0)
+ maxrep = 0;
+ linedone = 1;
+ break;
+
+ case '\b':
+ if (--col < indent)
+ col = indent;
+ break;
+
+ case '\r':
+ col = indent;
+ break;
+
+ case '\t':
+ col = ((col - indent) | 07) + indent + 1;
+ break;
+
+ case '\031':
+ /*
+ * lpd needs to use a different filter to
+ * print data so stop what we are doing and
+ * wait for lpd to restart us.
+ */
+ if ((ch = getchar()) == '\1') {
+ fflush(stdout);
+ kill(getpid(), SIGSTOP);
+ break;
+ } else {
+ ungetc(ch, stdin);
+ ch = '\031';
+ }
+
+ default:
+ if (col >= width || (!literal && ch < ' ')) {
+ col++;
+ break;
+ }
+ cp = &buf[0][col];
+ for (i = 0; i < MAXREP; i++) {
+ if (i > maxrep)
+ maxrep = i;
+ if (*cp == ' ') {
+ *cp = ch;
+ if (col > maxcol[i])
+ maxcol[i] = col;
+ break;
+ }
+ cp += MAXWIDTH;
+ }
+ col++;
+ break;
+ }
+ }
+
+ /* print out lines */
+ for (i = 0; i <= maxrep; i++) {
+ for (cp = buf[i], limit = cp+maxcol[i]; cp <= limit;) {
+ putc(*cp, o);
+ *cp++ = ' ';
+ }
+ if (i < maxrep)
+ putc('\r', o);
+ else
+ putc(ch, o);
+ if (++lineno >= length) {
+ fflush(o);
+ npages++;
+ lineno = 0;
+ }
+ maxcol[i] = -1;
+ }
+ }
+ if (lineno) { /* be sure to end on a page boundary */
+ putchar('\f');
+ npages++;
+ }
+ if (name && acctfile && access(acctfile, 02) >= 0 &&
+ freopen(acctfile, "a", stdout) != NULL) {
+ printf("%7.2f\t%s:%s\n", (float)npages, host, name);
+ }
+ exit(0);
+}
diff --git a/usr.sbin/lpr/lp/Makefile b/usr.sbin/lpr/lp/Makefile
new file mode 100644
index 0000000..2fcec43
--- /dev/null
+++ b/usr.sbin/lpr/lp/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+BINDIR= /usr/bin
+
+SCRIPTS=lp.sh
+MAN= lp.1
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/lp/lp.1 b/usr.sbin/lpr/lp/lp.1
new file mode 100644
index 0000000..8ed842d
--- /dev/null
+++ b/usr.sbin/lpr/lp/lp.1
@@ -0,0 +1,120 @@
+.\"
+.\" Copyright (c) 1995 Joerg Wunsch
+.\"
+.\" All rights reserved.
+.\"
+.\" This program is free software.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 Joerg Wunsch
+.\" 4. The name of the developer may not be used to endorse or promote
+.\" products derived from this software without specific prior written
+.\" permission.
+.\"
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 22, 1995
+.Dt LP 1
+.Os
+.Sh NAME
+.Nm lp
+.Nd front-end to the print spooler
+.Sh SYNOPSIS
+.Nm
+.Op Fl cs
+.Op Fl o Ar option
+.Op Fl d Ar printer
+.Op Fl n Ar num
+.Op Ar name ...
+.Sh DESCRIPTION
+The
+.Nm
+utility is a front-end to the print spooler as required by the
+.St -p1003.2
+specification.
+It effectively invokes
+.Xr lpr 1
+with the proper set of arguments.
+.Pp
+It generally prints the named files on the destination printer.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl c
+Make the
+.Nm
+command exit only after further access to any of the input files is no
+longer required.
+The application can then safely delete or modify the
+files without affecting the output operation.
+.It Fl d Ar dest
+Specify a particular printer.
+If no
+.Fl d
+is provided on the command line, the contents of the environment
+variables
+.Ev LPDEST
+or
+.Ev PRINTER
+(with this precedence)
+are taken as the destination printer.
+.It Fl n Ar num
+Specify that
+.Ar num
+copies of each of the named files shall be printed.
+.It Fl o Ar option
+Printer specific options.
+Not supported, provided only as a compatibility
+option for SVR.
+.It Fl s
+Silent operation.
+Not supported,
+provided only as a compatibility option for
+.St -susv2 .
+.El
+.Sh ENVIRONMENT
+As described above, the variables
+.Ev LPDEST
+and
+.Ev PRINTER
+are examined to select the destination printer.
+.Sh SEE ALSO
+.Xr lpr 1
+.Sh STANDARDS
+The
+.Nm
+command is expected to comply with the
+.St -p1003.2
+specification.
+.Sh AUTHORS
+This implementation of the
+.Nm
+command has been written by
+.An J\(:org Wunsch .
+.Sh BUGS
+The
+.St -p1003.2
+specification does not provide any means to print non-text files.
+It
+rather requires the files to be printed to be text files limited to
+reasonable line lengths and printable characters.
diff --git a/usr.sbin/lpr/lp/lp.sh b/usr.sbin/lpr/lp/lp.sh
new file mode 100644
index 0000000..e7c0688
--- /dev/null
+++ b/usr.sbin/lpr/lp/lp.sh
@@ -0,0 +1,75 @@
+#!/bin/sh
+#
+#
+# Copyright (c) 1995 Joerg Wunsch
+#
+# All rights reserved.
+#
+# This program is free software.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must 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 Joerg Wunsch
+# 4. The name of the developer may not be used to endorse or promote
+# products derived from this software without specific prior written
+# permission.
+#
+# 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.
+#
+#
+# Posix 1003.2 compliant print spooler interface.
+#
+# $FreeBSD$
+#
+
+ncopies=""
+symlink="-s"
+
+# Posix says LPDEST gets precedence over PRINTER
+dest=${LPDEST:-${PRINTER:-lp}}
+
+#
+# XXX We include the -o flag as a dummy. Posix 1003.2 does not require
+# it, but the rationale mentions it as a possible future extension.
+# XXX We include the -s flag as a dummy. SUSv2 requires it,
+# although we do not yet emit the affected messages.
+#
+while getopts "cd:n:o:s" option
+do
+ case $option in
+
+ c) # copy files before printing
+ symlink="";;
+ d) # destination
+ dest="${OPTARG}";;
+ n) # number of copies
+ ncopies="-#${OPTARG}";;
+ o) # (printer option)
+ : ;;
+ s) # (silent option)
+ : ;;
+ *) # (error msg printed by getopts)
+ exit 2;;
+ esac
+done
+
+shift $(($OPTIND - 1))
+
+exec /usr/bin/lpr "-P${dest}" ${symlink} ${ncopies} "$@"
diff --git a/usr.sbin/lpr/lpc/Makefile b/usr.sbin/lpr/lpc/Makefile
new file mode 100644
index 0000000..f050857
--- /dev/null
+++ b/usr.sbin/lpr/lpc/Makefile
@@ -0,0 +1,17 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../common_source
+
+PROG= lpc
+MAN= lpc.8
+SRCS= lpc.c cmds.c cmdtab.c movejobs.c
+BINGRP= daemon
+BINMODE= 2555
+
+CFLAGS+= -I${.CURDIR}/../common_source
+
+DPADD= ${LIBLPR} ${LIBEDIT} ${LIBTERMCAP}
+LDADD= ${LIBLPR} -ledit -ltermcap
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/lpc/cmds.c b/usr.sbin/lpr/lpc/cmds.c
new file mode 100644
index 0000000..06ea4b0
--- /dev/null
+++ b/usr.sbin/lpr/lpc/cmds.c
@@ -0,0 +1,1307 @@
+/*
+ * 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.
+ */
+
+#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 */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)cmds.c 8.2 (Berkeley) 4/28/95";
+#endif /* not lint */
+#endif
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+__FBSDID("$FreeBSD$");
+
+/*
+ * lpc -- line printer control program -- commands:
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+
+#include <signal.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include "lp.h"
+#include "lp.local.h"
+#include "lpc.h"
+#include "extern.h"
+#include "pathnames.h"
+
+/*
+ * Return values from kill_qtask().
+ */
+#define KQT_LFERROR -2
+#define KQT_KILLFAIL -1
+#define KQT_NODAEMON 0
+#define KQT_KILLOK 1
+
+static char *args2line(int argc, char **argv);
+static int doarg(char *_job);
+static int doselect(struct dirent *_d);
+static int kill_qtask(const char *lf);
+static int sortq(const void *_a, const void *_b);
+static int touch(struct jobqueue *_jq);
+static void unlinkf(char *_name);
+static void upstat(struct printer *_pp, const char *_msg, int _notify);
+static void wrapup_clean(int _laststatus);
+
+/*
+ * generic framework for commands which operate on all or a specified
+ * set of printers
+ */
+enum qsel_val { /* how a given ptr was selected */
+ QSEL_UNKNOWN = -1, /* ... not selected yet */
+ QSEL_BYNAME = 0, /* ... user specifed it by name */
+ QSEL_ALL = 1 /* ... user wants "all" printers */
+ /* (with more to come) */
+};
+
+static enum qsel_val generic_qselect; /* indicates how ptr was selected */
+static int generic_initerr; /* result of initrtn processing */
+static char *generic_cmdname;
+static char *generic_msg; /* if a -msg was specified */
+static char *generic_nullarg;
+static void (*generic_wrapup)(int _last_status); /* perform rtn wrap-up */
+
+void
+generic(void (*specificrtn)(struct printer *_pp), int cmdopts,
+ void (*initrtn)(int _argc, char *_argv[]), int argc, char *argv[])
+{
+ int cmdstatus, more, targc;
+ struct printer myprinter, *pp;
+ char **margv, **targv;
+
+ if (argc == 1) {
+ /*
+ * Usage needs a special case for 'down': The user must
+ * either include `-msg', or only the first parameter
+ * that they give will be processed as a printer name.
+ */
+ printf("usage: %s {all | printer ...}", argv[0]);
+ if (strcmp(argv[0], "down") == 0) {
+ printf(" -msg [<text> ...]\n");
+ printf(" or: down {all | printer} [<text> ...]");
+ } else if (cmdopts & LPC_MSGOPT)
+ printf(" [-msg <text> ...]");
+ printf("\n");
+ return;
+ }
+
+ /* The first argument is the command name. */
+ generic_cmdname = *argv++;
+ argc--;
+
+ /*
+ * The initialization routine for a command might set a generic
+ * "wrapup" routine, which should be called after processing all
+ * the printers in the command. This might print summary info.
+ *
+ * Note that the initialization routine may also parse (and
+ * nullify) some of the parameters given on the command, leaving
+ * only the parameters which have to do with printer names.
+ */
+ pp = &myprinter;
+ generic_wrapup = NULL;
+ generic_qselect = QSEL_UNKNOWN;
+ cmdstatus = 0;
+ /* this just needs to be a distinct value of type 'char *' */
+ if (generic_nullarg == NULL)
+ generic_nullarg = strdup("");
+
+ /*
+ * Some commands accept a -msg argument, which indicates that
+ * all remaining arguments should be combined into a string.
+ */
+ generic_msg = NULL;
+ if (cmdopts & LPC_MSGOPT) {
+ targc = argc;
+ targv = argv;
+ for (; targc > 0; targc--, targv++) {
+ if (strcmp(*targv, "-msg") == 0) {
+ argc -= targc;
+ generic_msg = args2line(targc - 1, targv + 1);
+ break;
+ }
+ }
+ }
+
+ /* call initialization routine, if there is one for this cmd */
+ if (initrtn != NULL) {
+ generic_initerr = 0;
+ (*initrtn)(argc, argv);
+ if (generic_initerr)
+ return;
+ /*
+ * The initrtn may have null'ed out some of the parameters.
+ * Compact the parameter list to remove those nulls, and
+ * correct the arg-count.
+ */
+ targc = argc;
+ targv = argv;
+ margv = argv;
+ argc = 0;
+ for (; targc > 0; targc--, targv++) {
+ if (*targv != generic_nullarg) {
+ if (targv != margv)
+ *margv = *targv;
+ margv++;
+ argc++;
+ }
+ }
+ }
+
+ if (argc == 1 && strcmp(*argv, "all") == 0) {
+ generic_qselect = QSEL_ALL;
+ more = firstprinter(pp, &cmdstatus);
+ if (cmdstatus)
+ goto looperr;
+ while (more) {
+ (*specificrtn)(pp);
+ do {
+ more = nextprinter(pp, &cmdstatus);
+looperr:
+ switch (cmdstatus) {
+ case PCAPERR_TCOPEN:
+ printf("warning: %s: unresolved "
+ "tc= reference(s) ",
+ pp->printer);
+ case PCAPERR_SUCCESS:
+ break;
+ default:
+ fatal(pp, "%s", pcaperr(cmdstatus));
+ }
+ } while (more && cmdstatus);
+ }
+ goto wrapup;
+ }
+
+ generic_qselect = QSEL_BYNAME; /* specifically-named ptrs */
+ for (; argc > 0; argc--, argv++) {
+ init_printer(pp);
+ cmdstatus = getprintcap(*argv, pp);
+ switch (cmdstatus) {
+ default:
+ fatal(pp, "%s", pcaperr(cmdstatus));
+ case PCAPERR_NOTFOUND:
+ printf("unknown printer %s\n", *argv);
+ continue;
+ case PCAPERR_TCOPEN:
+ printf("warning: %s: unresolved tc= reference(s)\n",
+ *argv);
+ break;
+ case PCAPERR_SUCCESS:
+ break;
+ }
+ (*specificrtn)(pp);
+ }
+
+wrapup:
+ if (generic_wrapup) {
+ (*generic_wrapup)(cmdstatus);
+ }
+ free_printer(pp);
+ if (generic_msg)
+ free(generic_msg);
+}
+
+/*
+ * Convert an argv-array of character strings into a single string.
+ */
+static char *
+args2line(int argc, char **argv)
+{
+ char *cp1, *cend;
+ const char *cp2;
+ char buf[1024];
+
+ if (argc <= 0)
+ return strdup("\n");
+
+ cp1 = buf;
+ cend = buf + sizeof(buf) - 1; /* save room for '\0' */
+ while (--argc >= 0) {
+ cp2 = *argv++;
+ while ((cp1 < cend) && (*cp1++ = *cp2++))
+ ;
+ cp1[-1] = ' ';
+ }
+ cp1[-1] = '\n';
+ *cp1 = '\0';
+ return strdup(buf);
+}
+
+/*
+ * Kill the current daemon, to stop printing of the active job.
+ */
+static int
+kill_qtask(const char *lf)
+{
+ FILE *fp;
+ pid_t pid;
+ int errsav, killres, lockres, res;
+
+ seteuid(euid);
+ fp = fopen(lf, "r");
+ errsav = errno;
+ seteuid(uid);
+ res = KQT_NODAEMON;
+ if (fp == NULL) {
+ /*
+ * If there is no lock file, then there is no daemon to
+ * kill. Any other error return means there is some
+ * kind of problem with the lock file.
+ */
+ if (errsav != ENOENT)
+ res = KQT_LFERROR;
+ goto killdone;
+ }
+
+ /* If the lock file is empty, then there is no daemon to kill */
+ if (getline(fp) == 0)
+ goto killdone;
+
+ /*
+ * If the file can be locked without blocking, then there
+ * no daemon to kill, or we should not try to kill it.
+ *
+ * XXX - not sure I understand the reasoning behind this...
+ */
+ lockres = flock(fileno(fp), LOCK_SH|LOCK_NB);
+ (void) fclose(fp);
+ if (lockres == 0)
+ goto killdone;
+
+ pid = atoi(line);
+ if (pid < 0) {
+ /*
+ * If we got a negative pid, then the contents of the
+ * lock file is not valid.
+ */
+ res = KQT_LFERROR;
+ goto killdone;
+ }
+
+ seteuid(uid);
+ killres = kill(pid, SIGTERM);
+ errsav = errno;
+ seteuid(uid);
+ if (killres == 0) {
+ res = KQT_KILLOK;
+ printf("\tdaemon (pid %d) killed\n", pid);
+ } else if (errno == ESRCH) {
+ res = KQT_NODAEMON;
+ } else {
+ res = KQT_KILLFAIL;
+ printf("\tWarning: daemon (pid %d) not killed:\n", pid);
+ printf("\t %s\n", strerror(errsav));
+ }
+
+killdone:
+ switch (res) {
+ case KQT_LFERROR:
+ printf("\tcannot open lock file: %s\n",
+ strerror(errsav));
+ break;
+ case KQT_NODAEMON:
+ printf("\tno daemon to abort\n");
+ break;
+ case KQT_KILLFAIL:
+ case KQT_KILLOK:
+ /* These two already printed messages to the user. */
+ break;
+ default:
+ printf("\t<internal error in kill_qtask>\n");
+ break;
+ }
+
+ return (res);
+}
+
+/*
+ * Write a message into the status file.
+ */
+static void
+upstat(struct printer *pp, const char *msg, int notifyuser)
+{
+ int fd;
+ char statfile[MAXPATHLEN];
+
+ status_file_name(pp, statfile, sizeof statfile);
+ umask(0);
+ seteuid(euid);
+ fd = open(statfile, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE);
+ seteuid(uid);
+ if (fd < 0) {
+ printf("\tcannot create status file: %s\n", strerror(errno));
+ return;
+ }
+ (void) ftruncate(fd, 0);
+ if (msg == (char *)NULL)
+ (void) write(fd, "\n", 1);
+ else
+ (void) write(fd, msg, strlen(msg));
+ (void) close(fd);
+ if (notifyuser) {
+ if ((msg == (char *)NULL) || (strcmp(msg, "\n") == 0))
+ printf("\tstatus message is now set to nothing.\n");
+ else
+ printf("\tstatus message is now: %s", msg);
+ }
+}
+
+/*
+ * kill an existing daemon and disable printing.
+ */
+void
+abort_q(struct printer *pp)
+{
+ int killres, setres;
+ char lf[MAXPATHLEN];
+
+ lock_file_name(pp, lf, sizeof lf);
+ printf("%s:\n", pp->printer);
+
+ /*
+ * Turn on the owner execute bit of the lock file to disable printing.
+ */
+ setres = set_qstate(SQS_STOPP, lf);
+
+ /*
+ * If set_qstate found that there already was a lock file, then
+ * call a routine which will read that lock file and kill the
+ * lpd-process which is listed in that lock file. If the lock
+ * file did not exist, then either there is no daemon running
+ * for this queue, or there is one running but *it* could not
+ * write a lock file (which means we can not determine the
+ * process id of that lpd-process).
+ */
+ switch (setres) {
+ case SQS_CHGOK:
+ case SQS_CHGFAIL:
+ /* Kill the process */
+ killres = kill_qtask(lf);
+ break;
+ case SQS_CREOK:
+ case SQS_CREFAIL:
+ printf("\tno daemon to abort\n");
+ break;
+ case SQS_STATFAIL:
+ printf("\tassuming no daemon to abort\n");
+ break;
+ default:
+ printf("\t<unexpected result (%d) from set_qstate>\n",
+ setres);
+ break;
+ }
+
+ if (setres >= 0)
+ upstat(pp, "printing disabled\n", 0);
+}
+
+/*
+ * "global" variables for all the routines related to 'clean' and 'tclean'
+ */
+static time_t cln_now; /* current time */
+static double cln_minage; /* minimum age before file is removed */
+static long cln_sizecnt; /* amount of space freed up */
+static int cln_debug; /* print extra debugging msgs */
+static int cln_filecnt; /* number of files destroyed */
+static int cln_foundcore; /* found a core file! */
+static int cln_queuecnt; /* number of queues checked */
+static int cln_testonly; /* remove-files vs just-print-info */
+
+static int
+doselect(struct dirent *d)
+{
+ int c = d->d_name[0];
+
+ if ((c == 'c' || c == 'd' || c == 'r' || c == 't') &&
+ d->d_name[1] == 'f')
+ return 1;
+ if (c == 'c') {
+ if (!strcmp(d->d_name, "core"))
+ cln_foundcore = 1;
+ }
+ if (c == 'e') {
+ if (!strncmp(d->d_name, "errs.", 5))
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Comparison routine that clean_q() uses for scandir.
+ *
+ * The purpose of this sort is to have all `df' files end up immediately
+ * after the matching `cf' file. For files matching `cf', `df', `rf', or
+ * `tf', it sorts by job number and machine, then by `cf', `df', `rf', or
+ * `tf', and then by the sequence letter (which is A-Z, or a-z). This
+ * routine may also see filenames which do not start with `cf', `df', `rf',
+ * or `tf' (such as `errs.*'), and those are simply sorted by the full
+ * filename.
+ *
+ * XXX
+ * This assumes that all control files start with `cfA*', and it turns
+ * out there are a few implementations of lpr which will create `cfB*'
+ * filenames (they will have datafile names which start with `dfB*').
+ */
+static int
+sortq(const void *a, const void *b)
+{
+ const int a_lt_b = -1, a_gt_b = 1, cat_other = 10;
+ const char *fname_a, *fname_b, *jnum_a, *jnum_b;
+ int cat_a, cat_b, ch, res, seq_a, seq_b;
+
+ fname_a = (*(const struct dirent * const *)a)->d_name;
+ fname_b = (*(const struct dirent * const *)b)->d_name;
+
+ /*
+ * First separate filenames into cagatories. Catagories are
+ * legitimate `cf', `df', `rf' & `tf' filenames, and "other" - in
+ * that order. It is critical that the mapping be exactly the
+ * same for 'a' vs 'b', so define a macro for the job.
+ *
+ * [aside: the standard `cf' file has the jobnumber start in
+ * position 4, but some implementations have that as an extra
+ * file-sequence letter, and start the job number in position 5.]
+ */
+#define MAP_TO_CAT(fname_X,cat_X,jnum_X,seq_X) do { \
+ cat_X = cat_other; \
+ ch = *(fname_X + 2); \
+ jnum_X = fname_X + 3; \
+ seq_X = 0; \
+ if ((*(fname_X + 1) == 'f') && (isalpha(ch))) { \
+ seq_X = ch; \
+ if (*fname_X == 'c') \
+ cat_X = 1; \
+ else if (*fname_X == 'd') \
+ cat_X = 2; \
+ else if (*fname_X == 'r') \
+ cat_X = 3; \
+ else if (*fname_X == 't') \
+ cat_X = 4; \
+ if (cat_X != cat_other) { \
+ ch = *jnum_X; \
+ if (!isdigit(ch)) { \
+ if (isalpha(ch)) { \
+ jnum_X++; \
+ ch = *jnum_X; \
+ seq_X = (seq_X << 8) + ch; \
+ } \
+ if (!isdigit(ch)) \
+ cat_X = cat_other; \
+ } \
+ } \
+ } \
+} while (0)
+
+ MAP_TO_CAT(fname_a, cat_a, jnum_a, seq_a);
+ MAP_TO_CAT(fname_b, cat_b, jnum_b, seq_b);
+
+#undef MAP_TO_CAT
+
+ /* First handle all cases which have "other" files */
+ if ((cat_a >= cat_other) || (cat_b >= cat_other)) {
+ /* for two "other" files, just compare the full name */
+ if (cat_a == cat_b)
+ res = strcmp(fname_a, fname_b);
+ else if (cat_a < cat_b)
+ res = a_lt_b;
+ else
+ res = a_gt_b;
+ goto have_res;
+ }
+
+ /*
+ * At this point, we know both files are legitimate `cf', `df', `rf',
+ * or `tf' files. Compare them by job-number and machine name.
+ */
+ res = strcmp(jnum_a, jnum_b);
+ if (res != 0)
+ goto have_res;
+
+ /*
+ * We have two files which belong to the same job. Sort based
+ * on the catagory of file (`c' before `d', etc).
+ */
+ if (cat_a < cat_b) {
+ res = a_lt_b;
+ goto have_res;
+ } else if (cat_a > cat_b) {
+ res = a_gt_b;
+ goto have_res;
+ }
+
+ /*
+ * Two files in the same catagory for a single job. Sort based
+ * on the sequence letter(s). (usually `A' thru `Z', etc).
+ */
+ if (seq_a < seq_b) {
+ res = a_lt_b;
+ goto have_res;
+ } else if (seq_a > seq_b) {
+ res = a_gt_b;
+ goto have_res;
+ }
+
+ /*
+ * Given that the filenames in a directory are unique, this SHOULD
+ * never happen (unless there are logic errors in this routine).
+ * But if it does happen, we must return "is equal" or the caller
+ * might see inconsistent results in the sorting order, and that
+ * can trigger other problems.
+ */
+ printf("\t*** Error in sortq: %s == %s !\n", fname_a, fname_b);
+ printf("\t*** cat %d == %d ; seq = %d %d\n", cat_a, cat_b,
+ seq_a, seq_b);
+ res = 0;
+
+have_res:
+ return res;
+}
+
+/*
+ * Remove all spool files and temporaries from the spooling area.
+ * Or, perhaps:
+ * Remove incomplete jobs from spooling area.
+ */
+
+void
+clean_gi(int argc, char *argv[])
+{
+
+ /* init some fields before 'clean' is called for each queue */
+ cln_queuecnt = 0;
+ cln_now = time(NULL);
+ cln_minage = 3600.0; /* only delete files >1h old */
+ cln_filecnt = 0;
+ cln_sizecnt = 0;
+ cln_debug = 0;
+ cln_testonly = 0;
+ generic_wrapup = &wrapup_clean;
+
+ /* see if there are any options specified before the ptr list */
+ for (; argc > 0; argc--, argv++) {
+ if (**argv != '-')
+ break;
+ if (strcmp(*argv, "-d") == 0) {
+ /* just an example of an option... */
+ cln_debug++;
+ *argv = generic_nullarg; /* "erase" it */
+ } else {
+ printf("Invalid option '%s'\n", *argv);
+ generic_initerr = 1;
+ }
+ }
+
+ return;
+}
+
+void
+tclean_gi(int argc, char *argv[])
+{
+
+ /* only difference between 'clean' and 'tclean' is one value */
+ /* (...and the fact that 'clean' is priv and 'tclean' is not) */
+ clean_gi(argc, argv);
+ cln_testonly = 1;
+
+ return;
+}
+
+void
+clean_q(struct printer *pp)
+{
+ char *cp, *cp1, *lp;
+ struct dirent **queue;
+ size_t linerem;
+ int didhead, i, n, nitems, rmcp;
+
+ cln_queuecnt++;
+
+ didhead = 0;
+ if (generic_qselect == QSEL_BYNAME) {
+ printf("%s:\n", pp->printer);
+ didhead = 1;
+ }
+
+ lp = line;
+ cp = pp->spool_dir;
+ while (lp < &line[sizeof(line) - 1]) {
+ if ((*lp++ = *cp++) == 0)
+ break;
+ }
+ lp[-1] = '/';
+ linerem = sizeof(line) - (lp - line);
+
+ cln_foundcore = 0;
+ seteuid(euid);
+ nitems = scandir(pp->spool_dir, &queue, doselect, sortq);
+ seteuid(uid);
+ if (nitems < 0) {
+ if (!didhead) {
+ printf("%s:\n", pp->printer);
+ didhead = 1;
+ }
+ printf("\tcannot examine spool directory\n");
+ return;
+ }
+ if (cln_foundcore) {
+ if (!didhead) {
+ printf("%s:\n", pp->printer);
+ didhead = 1;
+ }
+ printf("\t** found a core file in %s !\n", pp->spool_dir);
+ }
+ if (nitems == 0)
+ return;
+ if (!didhead)
+ printf("%s:\n", pp->printer);
+ if (cln_debug) {
+ printf("\t** ----- Sorted list of files being checked:\n");
+ i = 0;
+ do {
+ cp = queue[i]->d_name;
+ printf("\t** [%3d] = %s\n", i, cp);
+ } while (++i < nitems);
+ printf("\t** ----- end of sorted list\n");
+ }
+ i = 0;
+ do {
+ cp = queue[i]->d_name;
+ rmcp = 0;
+ if (*cp == 'c') {
+ /*
+ * A control file. Look for matching data-files.
+ */
+ /* XXX
+ * Note the logic here assumes that the hostname
+ * part of cf-filenames match the hostname part
+ * in df-filenames, and that is not necessarily
+ * true (eg: for multi-homed hosts). This needs
+ * some further thought...
+ */
+ n = 0;
+ while (i + 1 < nitems) {
+ cp1 = queue[i + 1]->d_name;
+ if (*cp1 != 'd' || strcmp(cp + 3, cp1 + 3))
+ break;
+ i++;
+ n++;
+ }
+ if (n == 0) {
+ rmcp = 1;
+ }
+ } else if (*cp == 'e') {
+ /*
+ * Must be an errrs or email temp file.
+ */
+ rmcp = 1;
+ } else {
+ /*
+ * Must be a df with no cf (otherwise, it would have
+ * been skipped above) or an rf or tf file (which can
+ * always be removed if it is old enough).
+ */
+ rmcp = 1;
+ }
+ if (rmcp) {
+ if (strlen(cp) >= linerem) {
+ printf("\t** internal error: 'line' overflow!\n");
+ printf("\t** spooldir = %s\n", pp->spool_dir);
+ printf("\t** cp = %s\n", cp);
+ return;
+ }
+ strlcpy(lp, cp, linerem);
+ unlinkf(line);
+ }
+ } while (++i < nitems);
+}
+
+static void
+wrapup_clean(int laststatus __unused)
+{
+
+ printf("Checked %d queues, and ", cln_queuecnt);
+ if (cln_filecnt < 1) {
+ printf("no cruft was found\n");
+ return;
+ }
+ if (cln_testonly) {
+ printf("would have ");
+ }
+ printf("removed %d files (%ld bytes).\n", cln_filecnt, cln_sizecnt);
+}
+
+static void
+unlinkf(char *name)
+{
+ struct stat stbuf;
+ double agemod, agestat;
+ int res;
+ char linkbuf[BUFSIZ];
+
+ /*
+ * We have to use lstat() instead of stat(), in case this is a df*
+ * "file" which is really a symlink due to 'lpr -s' processing. In
+ * that case, we need to check the last-mod time of the symlink, and
+ * not the file that the symlink is pointed at.
+ */
+ seteuid(euid);
+ res = lstat(name, &stbuf);
+ seteuid(uid);
+ if (res < 0) {
+ printf("\terror return from stat(%s):\n", name);
+ printf("\t %s\n", strerror(errno));
+ return;
+ }
+
+ agemod = difftime(cln_now, stbuf.st_mtime);
+ agestat = difftime(cln_now, stbuf.st_ctime);
+ if (cln_debug > 1) {
+ /* this debugging-aid probably is not needed any more... */
+ printf("\t\t modify age=%g secs, stat age=%g secs\n",
+ agemod, agestat);
+ }
+ if ((agemod <= cln_minage) && (agestat <= cln_minage))
+ return;
+
+ /*
+ * if this file is a symlink, then find out the target of the
+ * symlink before unlink-ing the file itself
+ */
+ if (S_ISLNK(stbuf.st_mode)) {
+ seteuid(euid);
+ res = readlink(name, linkbuf, sizeof(linkbuf));
+ seteuid(uid);
+ if (res < 0) {
+ printf("\terror return from readlink(%s):\n", name);
+ printf("\t %s\n", strerror(errno));
+ return;
+ }
+ if (res == sizeof(linkbuf))
+ res--;
+ linkbuf[res] = '\0';
+ }
+
+ cln_filecnt++;
+ cln_sizecnt += stbuf.st_size;
+
+ if (cln_testonly) {
+ printf("\twould remove %s\n", name);
+ if (S_ISLNK(stbuf.st_mode)) {
+ printf("\t (which is a symlink to %s)\n", linkbuf);
+ }
+ } else {
+ seteuid(euid);
+ res = unlink(name);
+ seteuid(uid);
+ if (res < 0)
+ printf("\tcannot remove %s (!)\n", name);
+ else
+ printf("\tremoved %s\n", name);
+ /* XXX
+ * Note that for a df* file, this code should also check to see
+ * if it is a symlink to some other file, and if the original
+ * lpr command included '-r' ("remove file"). Of course, this
+ * code would not be removing the df* file unless there was no
+ * matching cf* file, and without the cf* file it is currently
+ * impossible to determine if '-r' had been specified...
+ *
+ * As a result of this quandry, we may be leaving behind a
+ * user's file that was supposed to have been removed after
+ * being printed. This may effect services such as CAP or
+ * samba, if they were configured to use 'lpr -r', and if
+ * datafiles are not being properly removed.
+ */
+ if (S_ISLNK(stbuf.st_mode)) {
+ printf("\t (which was a symlink to %s)\n", linkbuf);
+ }
+ }
+}
+
+/*
+ * Enable queuing to the printer (allow lpr to add new jobs to the queue).
+ */
+void
+enable_q(struct printer *pp)
+{
+ int setres;
+ char lf[MAXPATHLEN];
+
+ lock_file_name(pp, lf, sizeof lf);
+ printf("%s:\n", pp->printer);
+
+ setres = set_qstate(SQS_ENABLEQ, lf);
+}
+
+/*
+ * Disable queuing.
+ */
+void
+disable_q(struct printer *pp)
+{
+ int setres;
+ char lf[MAXPATHLEN];
+
+ lock_file_name(pp, lf, sizeof lf);
+ printf("%s:\n", pp->printer);
+
+ setres = set_qstate(SQS_DISABLEQ, lf);
+}
+
+/*
+ * Disable queuing and printing and put a message into the status file
+ * (reason for being down). If the user specified `-msg', then use
+ * everything after that as the message for the status file. If the
+ * user did NOT specify `-msg', then the command should take the first
+ * parameter as the printer name, and all remaining parameters as the
+ * message for the status file. (This is to be compatible with the
+ * original definition of 'down', which was implemented long before
+ * `-msg' was around).
+ */
+void
+down_gi(int argc, char *argv[])
+{
+
+ /* If `-msg' was specified, then this routine has nothing to do. */
+ if (generic_msg != NULL)
+ return;
+
+ /*
+ * If the user only gave one parameter, then use a default msg.
+ * (if argc == 1 at this point, then *argv == name of printer).
+ */
+ if (argc == 1) {
+ generic_msg = strdup("printing disabled\n");
+ return;
+ }
+
+ /*
+ * The user specified multiple parameters, and did not specify
+ * `-msg'. Build a message from all the parameters after the
+ * first one (and nullify those parameters so generic-processing
+ * will not process them as printer-queue names).
+ */
+ argc--;
+ argv++;
+ generic_msg = args2line(argc, argv);
+ for (; argc > 0; argc--, argv++)
+ *argv = generic_nullarg; /* "erase" it */
+}
+
+void
+down_q(struct printer *pp)
+{
+ int setres;
+ char lf[MAXPATHLEN];
+
+ lock_file_name(pp, lf, sizeof lf);
+ printf("%s:\n", pp->printer);
+
+ setres = set_qstate(SQS_DISABLEQ+SQS_STOPP, lf);
+ if (setres >= 0)
+ upstat(pp, generic_msg, 1);
+}
+
+/*
+ * Exit lpc
+ */
+void
+quit(int argc __unused, char *argv[] __unused)
+{
+ exit(0);
+}
+
+/*
+ * Kill and restart the daemon.
+ */
+void
+restart_q(struct printer *pp)
+{
+ int killres, setres, startok;
+ char lf[MAXPATHLEN];
+
+ lock_file_name(pp, lf, sizeof lf);
+ printf("%s:\n", pp->printer);
+
+ killres = kill_qtask(lf);
+
+ /*
+ * XXX - if the kill worked, we should probably sleep for
+ * a second or so before trying to restart the queue.
+ */
+
+ /* make sure the queue is set to print jobs */
+ setres = set_qstate(SQS_STARTP, lf);
+
+ seteuid(euid);
+ startok = startdaemon(pp);
+ seteuid(uid);
+ if (!startok)
+ printf("\tcouldn't restart daemon\n");
+ else
+ printf("\tdaemon restarted\n");
+}
+
+/*
+ * Set the status message of each queue listed. Requires a "-msg"
+ * parameter to indicate the end of the queue list and start of msg text.
+ */
+void
+setstatus_gi(int argc __unused, char *argv[] __unused)
+{
+
+ if (generic_msg == NULL) {
+ printf("You must specify '-msg' before the text of the new status message.\n");
+ generic_initerr = 1;
+ }
+}
+
+void
+setstatus_q(struct printer *pp)
+{
+ char lf[MAXPATHLEN];
+
+ lock_file_name(pp, lf, sizeof lf);
+ printf("%s:\n", pp->printer);
+
+ upstat(pp, generic_msg, 1);
+}
+
+/*
+ * Enable printing on the specified printer and startup the daemon.
+ */
+void
+start_q(struct printer *pp)
+{
+ int setres, startok;
+ char lf[MAXPATHLEN];
+
+ lock_file_name(pp, lf, sizeof lf);
+ printf("%s:\n", pp->printer);
+
+ setres = set_qstate(SQS_STARTP, lf);
+
+ seteuid(euid);
+ startok = startdaemon(pp);
+ seteuid(uid);
+ if (!startok)
+ printf("\tcouldn't start daemon\n");
+ else
+ printf("\tdaemon started\n");
+ seteuid(uid);
+}
+
+/*
+ * Print the status of the printer queue.
+ */
+void
+status(struct printer *pp)
+{
+ struct stat stbuf;
+ register int fd, i;
+ register struct dirent *dp;
+ DIR *dirp;
+ char file[MAXPATHLEN];
+
+ printf("%s:\n", pp->printer);
+ lock_file_name(pp, file, sizeof file);
+ if (stat(file, &stbuf) >= 0) {
+ printf("\tqueuing is %s\n",
+ ((stbuf.st_mode & LFM_QUEUE_DIS) ? "disabled"
+ : "enabled"));
+ printf("\tprinting is %s\n",
+ ((stbuf.st_mode & LFM_PRINT_DIS) ? "disabled"
+ : "enabled"));
+ } else {
+ printf("\tqueuing is enabled\n");
+ printf("\tprinting is enabled\n");
+ }
+ if ((dirp = opendir(pp->spool_dir)) == NULL) {
+ printf("\tcannot examine spool directory\n");
+ return;
+ }
+ i = 0;
+ while ((dp = readdir(dirp)) != NULL) {
+ if (*dp->d_name == 'c' && dp->d_name[1] == 'f')
+ i++;
+ }
+ closedir(dirp);
+ if (i == 0)
+ printf("\tno entries in spool area\n");
+ else if (i == 1)
+ printf("\t1 entry in spool area\n");
+ else
+ printf("\t%d entries in spool area\n", i);
+ fd = open(file, O_RDONLY);
+ if (fd < 0 || flock(fd, LOCK_SH|LOCK_NB) == 0) {
+ (void) close(fd); /* unlocks as well */
+ printf("\tprinter idle\n");
+ return;
+ }
+ (void) close(fd);
+ /* print out the contents of the status file, if it exists */
+ status_file_name(pp, file, sizeof file);
+ fd = open(file, O_RDONLY|O_SHLOCK);
+ if (fd >= 0) {
+ (void) fstat(fd, &stbuf);
+ if (stbuf.st_size > 0) {
+ putchar('\t');
+ while ((i = read(fd, line, sizeof(line))) > 0)
+ (void) fwrite(line, 1, i, stdout);
+ }
+ (void) close(fd); /* unlocks as well */
+ }
+}
+
+/*
+ * Stop the specified daemon after completing the current job and disable
+ * printing.
+ */
+void
+stop_q(struct printer *pp)
+{
+ int setres;
+ char lf[MAXPATHLEN];
+
+ lock_file_name(pp, lf, sizeof lf);
+ printf("%s:\n", pp->printer);
+
+ setres = set_qstate(SQS_STOPP, lf);
+
+ if (setres >= 0)
+ upstat(pp, "printing disabled\n", 0);
+}
+
+struct jobqueue **queue;
+int nitems;
+time_t mtime;
+
+/*
+ * Put the specified jobs at the top of printer queue.
+ */
+void
+topq(int argc, char *argv[])
+{
+ register int i;
+ struct stat stbuf;
+ int cmdstatus, changed;
+ struct printer myprinter, *pp = &myprinter;
+
+ if (argc < 3) {
+ printf("usage: topq printer [jobnum ...] [user ...]\n");
+ return;
+ }
+
+ --argc;
+ ++argv;
+ init_printer(pp);
+ cmdstatus = getprintcap(*argv, pp);
+ switch(cmdstatus) {
+ default:
+ fatal(pp, "%s", pcaperr(cmdstatus));
+ case PCAPERR_NOTFOUND:
+ printf("unknown printer %s\n", *argv);
+ return;
+ case PCAPERR_TCOPEN:
+ printf("warning: %s: unresolved tc= reference(s)", *argv);
+ break;
+ case PCAPERR_SUCCESS:
+ break;
+ }
+ printf("%s:\n", pp->printer);
+
+ seteuid(euid);
+ if (chdir(pp->spool_dir) < 0) {
+ printf("\tcannot chdir to %s\n", pp->spool_dir);
+ goto out;
+ }
+ seteuid(uid);
+ nitems = getq(pp, &queue);
+ if (nitems == 0)
+ return;
+ changed = 0;
+ mtime = queue[0]->job_time;
+ for (i = argc; --i; ) {
+ if (doarg(argv[i]) == 0) {
+ printf("\tjob %s is not in the queue\n", argv[i]);
+ continue;
+ } else
+ changed++;
+ }
+ for (i = 0; i < nitems; i++)
+ free(queue[i]);
+ free(queue);
+ if (!changed) {
+ printf("\tqueue order unchanged\n");
+ return;
+ }
+ /*
+ * Turn on the public execute bit of the lock file to
+ * get lpd to rebuild the queue after the current job.
+ */
+ seteuid(euid);
+ if (changed && stat(pp->lock_file, &stbuf) >= 0)
+ (void) chmod(pp->lock_file, stbuf.st_mode | LFM_RESET_QUE);
+
+out:
+ seteuid(uid);
+}
+
+/*
+ * Reposition the job by changing the modification time of
+ * the control file.
+ */
+static int
+touch(struct jobqueue *jq)
+{
+ struct timeval tvp[2];
+ int ret;
+
+ tvp[0].tv_sec = tvp[1].tv_sec = --mtime;
+ tvp[0].tv_usec = tvp[1].tv_usec = 0;
+ seteuid(euid);
+ ret = utimes(jq->job_cfname, tvp);
+ seteuid(uid);
+ return (ret);
+}
+
+/*
+ * Checks if specified job name is in the printer's queue.
+ * Returns: negative (-1) if argument name is not in the queue.
+ */
+static int
+doarg(char *job)
+{
+ register struct jobqueue **qq;
+ register int jobnum, n;
+ register char *cp, *machine;
+ int cnt = 0;
+ FILE *fp;
+
+ /*
+ * Look for a job item consisting of system name, colon, number
+ * (example: ucbarpa:114)
+ */
+ if ((cp = strchr(job, ':')) != NULL) {
+ machine = job;
+ *cp++ = '\0';
+ job = cp;
+ } else
+ machine = NULL;
+
+ /*
+ * Check for job specified by number (example: 112 or 235ucbarpa).
+ */
+ if (isdigit(*job)) {
+ jobnum = 0;
+ do
+ jobnum = jobnum * 10 + (*job++ - '0');
+ while (isdigit(*job));
+ for (qq = queue + nitems; --qq >= queue; ) {
+ n = 0;
+ for (cp = (*qq)->job_cfname+3; isdigit(*cp); )
+ n = n * 10 + (*cp++ - '0');
+ if (jobnum != n)
+ continue;
+ if (*job && strcmp(job, cp) != 0)
+ continue;
+ if (machine != NULL && strcmp(machine, cp) != 0)
+ continue;
+ if (touch(*qq) == 0) {
+ printf("\tmoved %s\n", (*qq)->job_cfname);
+ cnt++;
+ }
+ }
+ return(cnt);
+ }
+ /*
+ * Process item consisting of owner's name (example: henry).
+ */
+ for (qq = queue + nitems; --qq >= queue; ) {
+ seteuid(euid);
+ fp = fopen((*qq)->job_cfname, "r");
+ seteuid(uid);
+ if (fp == NULL)
+ continue;
+ while (getline(fp) > 0)
+ if (line[0] == 'P')
+ break;
+ (void) fclose(fp);
+ if (line[0] != 'P' || strcmp(job, line+1) != 0)
+ continue;
+ if (touch(*qq) == 0) {
+ printf("\tmoved %s\n", (*qq)->job_cfname);
+ cnt++;
+ }
+ }
+ return(cnt);
+}
+
+/*
+ * Enable both queuing & printing, and start printer (undo `down').
+ */
+void
+up_q(struct printer *pp)
+{
+ int setres, startok;
+ char lf[MAXPATHLEN];
+
+ lock_file_name(pp, lf, sizeof lf);
+ printf("%s:\n", pp->printer);
+
+ setres = set_qstate(SQS_ENABLEQ+SQS_STARTP, lf);
+
+ seteuid(euid);
+ startok = startdaemon(pp);
+ seteuid(uid);
+ if (!startok)
+ printf("\tcouldn't start daemon\n");
+ else
+ printf("\tdaemon started\n");
+}
diff --git a/usr.sbin/lpr/lpc/cmdtab.c b/usr.sbin/lpr/lpc/cmdtab.c
new file mode 100644
index 0000000..21dc6e1c
--- /dev/null
+++ b/usr.sbin/lpr/lpc/cmdtab.c
@@ -0,0 +1,94 @@
+/*
+ * 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.
+ */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)cmdtab.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+#endif
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+__FBSDID("$FreeBSD$");
+
+#include "lpc.h"
+#include "extern.h"
+
+/*
+ * lpc -- command tables
+ */
+char aborthelp[] = "terminate a spooling daemon immediately and disable printing";
+char botmqhelp[] = "move job(s) to the bottom of printer queue";
+char cleanhelp[] = "remove cruft files from a queue";
+char enablehelp[] = "turn a spooling queue on";
+char disablehelp[] = "turn a spooling queue off";
+char downhelp[] = "do a 'stop' followed by 'disable' and put a message in status";
+char helphelp[] = "get help on commands";
+char quithelp[] = "exit lpc";
+char restarthelp[] = "kill (if possible) and restart a spooling daemon";
+char setstatushelp[] = "set the status message of a queue, requires\n"
+ "\t\t\"-msg\" before the text of the new message";
+char starthelp[] = "enable printing and start a spooling daemon";
+char statushelp[] = "show status of daemon and queue";
+char stophelp[] = "stop a spooling daemon after current job completes and disable printing";
+char tcleanhelp[] = "test to see what files a clean cmd would remove";
+char topqhelp[] = "move job(s) to the top of printer queue";
+char uphelp[] = "enable everything and restart spooling daemon";
+
+/* Use some abbreviations so entries won't need to wrap */
+#define PR LPC_PRIVCMD
+#define M LPC_MSGOPT
+
+struct cmd cmdtab[] = {
+ { "abort", aborthelp, PR, 0, abort_q },
+ { "bottomq", botmqhelp, PR, bottomq_cmd, 0 },
+ { "clean", cleanhelp, PR, clean_gi, clean_q },
+ { "enable", enablehelp, PR, 0, enable_q },
+ { "exit", quithelp, 0, quit, 0 },
+ { "disable", disablehelp, PR, 0, disable_q },
+ { "down", downhelp, PR|M, down_gi, down_q },
+ { "help", helphelp, 0, help, 0 },
+ { "quit", quithelp, 0, quit, 0 },
+ { "restart", restarthelp, 0, 0, restart_q },
+ { "start", starthelp, PR, 0, start_q },
+ { "status", statushelp, 0, 0, status },
+ { "setstatus", setstatushelp, PR|M, setstatus_gi, setstatus_q },
+ { "stop", stophelp, PR, 0, stop_q },
+ { "tclean", tcleanhelp, 0, tclean_gi, clean_q },
+ { "topq", topqhelp, PR, topq_cmd, 0 },
+ { "up", uphelp, PR, 0, up_q },
+ { "?", helphelp, 0, help, 0 },
+ { "xtopq", topqhelp, PR, topq, 0 },
+ { 0, 0, 0, 0, 0},
+};
+
+int NCMDS = sizeof (cmdtab) / sizeof (cmdtab[0]);
diff --git a/usr.sbin/lpr/lpc/extern.h b/usr.sbin/lpr/lpc/extern.h
new file mode 100644
index 0000000..fe82d46
--- /dev/null
+++ b/usr.sbin/lpr/lpc/extern.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ *
+ * $FreeBSD$
+ */
+
+
+#include <sys/types.h>
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+
+/*
+ * Options for setup_myprinter().
+ */
+#define SUMP_NOHEADER 0x0001 /* Do not print a header line */
+#define SUMP_CHDIR_SD 0x0002 /* chdir into the spool directory */
+
+__BEGIN_DECLS
+void abort_q(struct printer *_pp);
+void bottomq_cmd(int _argc, char *_argv[]);
+void clean_gi(int _argc, char *_argv[]);
+void clean_q(struct printer *_pp);
+void disable_q(struct printer *_pp);
+void down_gi(int _argc, char *_argv[]);
+void down_q(struct printer *_pp);
+void enable_q(struct printer *_pp);
+void generic(void (*_specificrtn)(struct printer *_pp), int _cmdopts,
+ void (*_initcmd)(int _argc, char *_argv[]),
+ int _argc, char *_argv[]);
+void help(int _argc, char *_argv[]);
+void quit(int _argc, char *_argv[]);
+void restart_q(struct printer *_pp);
+void setstatus_gi(int _argc, char *_argv[]);
+void setstatus_q(struct printer *_pp);
+void start_q(struct printer *_pp);
+void status(struct printer *_pp);
+void stop_q(struct printer *_pp);
+void tclean_gi(int _argc, char *_argv[]);
+void topq_cmd(int _argc, char *_argv[]);
+void up_q(struct printer *_pp);
+void topq(int _argc, char *_argv[]); /* X-version */
+
+/* from lpc.c: */
+struct printer *setup_myprinter(char *_pwanted, struct printer *_pp,
+ int _sump_opts);
+__END_DECLS
+
+extern int NCMDS;
+extern struct cmd cmdtab[];
+extern uid_t uid, euid;
diff --git a/usr.sbin/lpr/lpc/lpc.8 b/usr.sbin/lpr/lpc/lpc.8
new file mode 100644
index 0000000..ea79241
--- /dev/null
+++ b/usr.sbin/lpr/lpc/lpc.8
@@ -0,0 +1,313 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.\" @(#)lpc.8 8.5 (Berkeley) 4/28/95
+.\" $FreeBSD$
+.\"
+.Dd July 16, 2002
+.Dt LPC 8
+.Os
+.Sh NAME
+.Nm lpc
+.Nd line printer control program
+.Sh SYNOPSIS
+.Nm
+.Op Ar command Op Ar argument ...
+.Sh DESCRIPTION
+The
+.Nm
+utility is used by the system administrator to control the
+operation of the line printer system.
+For each line printer configured in
+.Pa /etc/printcap ,
+.Nm
+may be used to:
+.Bl -bullet -offset indent
+.It
+disable or enable a printer,
+.It
+disable or enable a printer's spooling queue,
+.It
+rearrange the order of jobs in a spooling queue,
+.It
+find the status of printers, and their associated
+spooling queues and printer daemons,
+.It
+change the status message for printer queues (the status message
+may be seen by users as part of the output of the
+.Xr lpq 1
+utility).
+.El
+.Pp
+Without any arguments,
+.Nm
+will prompt for commands from the standard input.
+If arguments are supplied,
+.Nm
+interprets the first argument as a command and the remaining
+arguments as parameters to the command.
+The standard input
+may be redirected causing
+.Nm
+to read commands from file.
+Commands may be abbreviated;
+the following is the list of recognized commands.
+.Pp
+.Bl -tag -width indent -compact
+.It Ic \&? Op Ar command ...
+.It Ic help Op Ar command ...
+Print a short description of each command specified in the argument list,
+or, if no argument is given, a list of the recognized commands.
+.Pp
+.It Ic abort Brq Cm all | Ar printer
+Terminate an active spooling daemon on the local host immediately and
+then disable printing (preventing new daemons from being started by
+.Xr lpr 1 )
+for the specified printers.
+.Pp
+.It Ic bottomq Ar printer Op Ar jobspec ...
+Take the specified jobs in the order specified and move them to the
+bottom of the printer queue.
+Each
+.Ar jobspec
+can match multiple print jobs.
+The full description of a
+.Ar jobspec
+is given below.
+.Pp
+.It Ic clean Brq Cm all | Ar printer
+Remove any temporary files, data files, and control files that cannot
+be printed (i.e., do not form a complete printer job)
+from the specified printer queue(s) on the local machine.
+This command will also look for
+.Pa core
+files in spool directory
+for each printer queue, and list any that are found.
+It will not remove any
+.Pa core
+files.
+See also the
+.Ic tclean
+command.
+.Pp
+.It Ic disable Brq Cm all | Ar printer
+Turn the specified printer queues off.
+This prevents new
+printer jobs from being entered into the queue by
+.Xr lpr 1 .
+.Pp
+.It Ic down Bro Cm all | Ar printer ... Brc Cm -msg Ar message ...
+.It Ic down Bro Cm all | Ar printer Brc Ar message ...
+Turn the specified printer queue off, disable printing and put
+.Ar message
+in the printer status file.
+When specifying more than one printer queue, the
+.Ic -msg
+argument is required to separate the list of printers from the text
+that will be the new status message.
+The message does not need to be quoted, the
+remaining arguments are treated like
+.Xr echo 1 .
+This is normally used to take a printer down, and let other users
+find out why it is down (the
+.Xr lpq 1
+utility will indicate that the printer is down and will print the
+status message).
+.Pp
+.It Ic enable Brq Cm all | Ar printer
+Enable spooling on the local queue for the listed printers.
+This will allow
+.Xr lpr 1
+to put new jobs in the spool queue.
+.Pp
+.It Ic exit
+.It Ic quit
+Exit from
+.Nm .
+.Pp
+.It Ic restart Brq Cm all | Ar printer
+Attempt to start a new printer daemon.
+This is useful when some abnormal condition causes the daemon to
+die unexpectedly, leaving jobs in the queue.
+.Xr lpq 1
+will report that there is no daemon present when this condition occurs.
+If the user is the super-user,
+try to abort the current daemon first (i.e., kill and restart a stuck daemon).
+.Pp
+.It Ic setstatus Bro Cm all | Ar printer Brc Cm -msg Ar message ...
+Set the status message for the specified printers.
+The
+.Ic -msg
+argument is required to separate the list of printers from the text
+that will be the new status message.
+This is normally used to change the status message when the printer
+queue is no longer active after printing has been disabled, and you
+want to change what users will see in the output of the
+.Xr lpq 1
+utility.
+.Pp
+.It Ic start Brq Cm all | Ar printer
+Enable printing and start a spooling daemon for the listed printers.
+.Pp
+.It Ic status Brq Cm all | Ar printer
+Display the status of daemons and queues on the local machine.
+.Pp
+.It Ic stop Brq Cm all | Ar printer
+Stop a spooling daemon after the current job completes and disable
+printing.
+.Pp
+.It Ic tclean Brq Cm all | Ar printer
+This will do a test-run of the
+.Ic clean
+command.
+All the same checking is done, but the command will only print out
+messages saying what a similar
+.Ic clean
+command would do if the user typed it in.
+It will not remove any files.
+Note that the
+.Ic clean
+command is a privileged command, while the
+.Ic tclean
+command is not restricted.
+.Pp
+.It Ic topq Ar printer Op Ar jobspec ...
+Take the specified jobs in the order specified and move them to the
+top of the printer queue.
+Each
+.Ar jobspec
+can match multiple print jobs.
+The full description of a
+.Ar jobspec
+is given below.
+.Pp
+.It Ic up Brq Cm all | Ar printer
+Enable everything and start a new printer daemon.
+Undoes the effects of
+.Ic down .
+.El
+.Pp
+Commands such as
+.Ic topq
+and
+.Ic bottomq
+can take one or more
+.Ar jobspec
+to specify which jobs the command should operate on.
+A
+.Ar jobspec
+can be:
+.Bl -bullet
+.It
+a single job number, which will match all jobs in the printer's queue
+which have the same job number.
+Eg:
+.Ar 17 ,
+.It
+a range of job numbers, which will match all jobs with a number between
+the starting and ending job numbers, inclusive.
+Eg:
+.Ar 21-32 ,
+.It
+a specific userid, which will match all jobs which were sent by that
+user.
+Eg:
+.Ar jones ,
+.It
+a host name, when prefixed by an `@', which will match all jobs in
+the queue which were sent from the given host.
+Eg:
+.Ar @freebsd.org ,
+.It
+a job range and a userid, separated by a `:', which will match all jobs
+which both match the job range and were sent by the specified user.
+Eg:
+.Ar jones:17
+or
+.Ar 21-32:jones ,
+.It
+a job range and/or a userid, followed by a host name, which will match
+all jobs which match all the specified criteria.
+Eg:
+.Ar jones@freebsd.org
+or
+.Ar 21-32@freebsd.org
+or
+.Ar jones:17@freebsd.org .
+.El
+.Pp
+The values for userid and host name can also include pattern-matching
+characters, similar to the pattern matching done for filenames in
+most command shells.
+Note that if you enter a
+.Ic topq
+or
+.Ic bottomq
+command as parameters on the initial
+.Nm
+command, then the shell will expand any pattern-matching characters
+that it can (based on what files in finds in the current directory)
+before
+.Nm
+processes the command.
+In that case, any parameters which include pattern-matching characters
+should be enclosed in quotes, so that the shell will not try to
+expand them.
+.Sh FILES
+.Bl -tag -width /var/spool/*/lockx -compact
+.It Pa /etc/printcap
+printer description file
+.It Pa /var/spool/*
+spool directories
+.It Pa /var/spool/*/lock
+lock file for queue control
+.El
+.Sh DIAGNOSTICS
+.Bl -diag
+.It "?Ambiguous command"
+abbreviation matches more than one command
+.It "?Invalid command"
+no match was found
+.It "?Privileged command"
+you must be a member of group "operator" or root to execute this command
+.El
+.Sh SEE ALSO
+.Xr lpq 1 ,
+.Xr lpr 1 ,
+.Xr lprm 1 ,
+.Xr printcap 5 ,
+.Xr chkprintcap 8 ,
+.Xr lpd 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.2 .
diff --git a/usr.sbin/lpr/lpc/lpc.c b/usr.sbin/lpr/lpc/lpc.c
new file mode 100644
index 0000000..e279f3a
--- /dev/null
+++ b/usr.sbin/lpr/lpc/lpc.c
@@ -0,0 +1,419 @@
+/*
+ * 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.
+ */
+
+#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 */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)lpc.c 8.3 (Berkeley) 4/28/95";
+#endif /* not lint */
+#endif
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <err.h>
+#include <grp.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <string.h>
+#include <unistd.h>
+#include <histedit.h>
+
+#include "lp.h"
+#include "lpc.h"
+#include "extern.h"
+
+#ifndef LPR_OPER
+#define LPR_OPER "operator" /* group name of lpr operators */
+#endif
+
+/*
+ * lpc -- line printer control program
+ */
+
+#define MAX_CMDLINE 200
+#define MAX_MARGV 20
+static int fromatty;
+
+static char cmdline[MAX_CMDLINE];
+static int margc;
+static char *margv[MAX_MARGV];
+uid_t uid, euid;
+
+int main(int _argc, char *_argv[]);
+static void cmdscanner(void);
+static struct cmd *getcmd(const char *_name);
+static void intr(int _signo);
+static void makeargv(void);
+static int ingroup(const char *_grname);
+
+int
+main(int argc, char *argv[])
+{
+ register struct cmd *c;
+
+ euid = geteuid();
+ uid = getuid();
+ seteuid(uid);
+ progname = argv[0];
+ openlog("lpd", 0, LOG_LPR);
+
+ if (--argc > 0) {
+ c = getcmd(*++argv);
+ if (c == (struct cmd *)-1) {
+ printf("?Ambiguous command\n");
+ exit(1);
+ }
+ if (c == 0) {
+ printf("?Invalid command\n");
+ exit(1);
+ }
+ if ((c->c_opts & LPC_PRIVCMD) && getuid() &&
+ ingroup(LPR_OPER) == 0) {
+ printf("?Privileged command\n");
+ exit(1);
+ }
+ if (c->c_generic != 0)
+ generic(c->c_generic, c->c_opts, c->c_handler,
+ argc, argv);
+ else
+ (*c->c_handler)(argc, argv);
+ exit(0);
+ }
+ fromatty = isatty(fileno(stdin));
+ if (!fromatty)
+ signal(SIGINT, intr);
+ for (;;) {
+ cmdscanner();
+ }
+}
+
+static void
+intr(int signo __unused)
+{
+ /* (the '__unused' is just to avoid a compile-time warning) */
+ exit(0);
+}
+
+static const char *
+lpc_prompt(void)
+{
+ return ("lpc> ");
+}
+
+/*
+ * Command parser.
+ */
+static void
+cmdscanner(void)
+{
+ register struct cmd *c;
+ static EditLine *el;
+ static History *hist;
+ HistEvent he;
+ size_t len;
+ int num;
+ const char *bp;
+
+ num = 0;
+ bp = NULL;
+ el = NULL;
+ hist = NULL;
+ for (;;) {
+ if (fromatty) {
+ if (!el) {
+ el = el_init("lpc", stdin, stdout, stderr);
+ hist = history_init();
+ history(hist, &he, H_SETSIZE, 100);
+ el_set(el, EL_HIST, history, hist);
+ el_set(el, EL_EDITOR, "emacs");
+ el_set(el, EL_PROMPT, lpc_prompt);
+ el_set(el, EL_SIGNAL, 1);
+ el_source(el, NULL);
+ /*
+ * EditLine init may call 'cgetset()' to set a
+ * capability-db meant for termcap (eg: to set
+ * terminal type 'xterm'). Reset that now, or
+ * that same db-information will be used for
+ * printcap (giving us an "xterm" printer, with
+ * all kinds of invalid capabilities...).
+ */
+ cgetset(NULL);
+ }
+ if ((bp = el_gets(el, &num)) == NULL || num == 0)
+ quit(0, NULL);
+
+ len = (num > MAX_CMDLINE - 1) ? MAX_CMDLINE - 1 : num;
+ memcpy(cmdline, bp, len);
+ cmdline[len] = 0;
+ history(hist, &he, H_ENTER, bp);
+
+ } else {
+ if (fgets(cmdline, MAX_CMDLINE, stdin) == 0)
+ quit(0, NULL);
+ if (cmdline[0] == 0 || cmdline[0] == '\n')
+ break;
+ }
+
+ makeargv();
+ if (margc == 0)
+ continue;
+ if (el != NULL && el_parse(el, margc, margv) != -1)
+ continue;
+
+ c = getcmd(margv[0]);
+ if (c == (struct cmd *)-1) {
+ printf("?Ambiguous command\n");
+ continue;
+ }
+ if (c == 0) {
+ printf("?Invalid command\n");
+ continue;
+ }
+ if ((c->c_opts & LPC_PRIVCMD) && getuid() &&
+ ingroup(LPR_OPER) == 0) {
+ printf("?Privileged command\n");
+ continue;
+ }
+
+ /*
+ * Two different commands might have the same generic rtn
+ * (eg: "clean" and "tclean"), and just use different
+ * handler routines for distinct command-setup. The handler
+ * routine might also be set on a generic routine for
+ * initial parameter processing.
+ */
+ if (c->c_generic != 0)
+ generic(c->c_generic, c->c_opts, c->c_handler,
+ margc, margv);
+ else
+ (*c->c_handler)(margc, margv);
+ }
+}
+
+static struct cmd *
+getcmd(const char *name)
+{
+ register const char *p, *q;
+ register struct cmd *c, *found;
+ register int nmatches, longest;
+
+ longest = 0;
+ nmatches = 0;
+ found = 0;
+ for (c = cmdtab; (p = c->c_name); c++) {
+ for (q = name; *q == *p++; q++)
+ if (*q == 0) /* exact match? */
+ return(c);
+ if (!*q) { /* the name was a prefix */
+ if (q - name > longest) {
+ longest = q - name;
+ nmatches = 1;
+ found = c;
+ } else if (q - name == longest)
+ nmatches++;
+ }
+ }
+ if (nmatches > 1)
+ return((struct cmd *)-1);
+ return(found);
+}
+
+/*
+ * Slice a string up into argc/argv.
+ */
+static void
+makeargv(void)
+{
+ register char *cp;
+ register char **argp = margv;
+ register int n = 0;
+
+ margc = 0;
+ for (cp = cmdline; *cp && (size_t)(cp - cmdline) < sizeof(cmdline) &&
+ n < MAX_MARGV - 1; n++) {
+ while (isspace(*cp))
+ cp++;
+ if (*cp == '\0')
+ break;
+ *argp++ = cp;
+ margc += 1;
+ while (*cp != '\0' && !isspace(*cp))
+ cp++;
+ if (*cp == '\0')
+ break;
+ *cp++ = '\0';
+ }
+ *argp++ = 0;
+}
+
+#define HELPINDENT (sizeof ("directory"))
+
+/*
+ * Help command.
+ */
+void
+help(int argc, char *argv[])
+{
+ register struct cmd *c;
+
+ if (argc == 1) {
+ register int i, j, w;
+ int columns, width = 0, lines;
+
+ printf("Commands may be abbreviated. Commands are:\n\n");
+ for (c = cmdtab; c->c_name; c++) {
+ int len = strlen(c->c_name);
+
+ if (len > width)
+ width = len;
+ }
+ width = (width + 8) &~ 7;
+ columns = 80 / width;
+ if (columns == 0)
+ columns = 1;
+ lines = (NCMDS + columns - 1) / columns;
+ for (i = 0; i < lines; i++) {
+ for (j = 0; j < columns; j++) {
+ c = cmdtab + j * lines + i;
+ if (c->c_name)
+ printf("%s", c->c_name);
+ if (c + lines >= &cmdtab[NCMDS]) {
+ printf("\n");
+ break;
+ }
+ w = strlen(c->c_name);
+ while (w < width) {
+ w = (w + 8) &~ 7;
+ putchar('\t');
+ }
+ }
+ }
+ return;
+ }
+ while (--argc > 0) {
+ register char *arg;
+ arg = *++argv;
+ c = getcmd(arg);
+ if (c == (struct cmd *)-1)
+ printf("?Ambiguous help command %s\n", arg);
+ else if (c == (struct cmd *)0)
+ printf("?Invalid help command %s\n", arg);
+ else
+ printf("%-*s\t%s\n", (int) HELPINDENT,
+ c->c_name, c->c_help);
+ }
+}
+
+/*
+ * return non-zero if the user is a member of the given group
+ */
+static int
+ingroup(const char *grname)
+{
+ static struct group *gptr=NULL;
+ static int ngroups = 0;
+ static gid_t groups[NGROUPS];
+ register gid_t gid;
+ register int i;
+
+ if (gptr == NULL) {
+ if ((gptr = getgrnam(grname)) == NULL) {
+ warnx("warning: unknown group '%s'", grname);
+ return(0);
+ }
+ ngroups = getgroups(NGROUPS, groups);
+ if (ngroups < 0)
+ err(1, "getgroups");
+ }
+ gid = gptr->gr_gid;
+ for (i = 0; i < ngroups; i++)
+ if (gid == groups[i])
+ return(1);
+ return(0);
+}
+
+/*
+ * Routine to get the information for a single printer (which will be
+ * called by the routines which implement individual commands).
+ * Note: This is for commands operating on a *single* printer.
+ */
+struct printer *
+setup_myprinter(char *pwanted, struct printer *pp, int sump_opts)
+{
+ int cdres, cmdstatus;
+
+ init_printer(pp);
+ cmdstatus = getprintcap(pwanted, pp);
+ switch (cmdstatus) {
+ default:
+ fatal(pp, "%s", pcaperr(cmdstatus));
+ /* NOTREACHED */
+ case PCAPERR_NOTFOUND:
+ printf("unknown printer %s\n", pwanted);
+ return (NULL);
+ case PCAPERR_TCOPEN:
+ printf("warning: %s: unresolved tc= reference(s)", pwanted);
+ break;
+ case PCAPERR_SUCCESS:
+ break;
+ }
+ if ((sump_opts & SUMP_NOHEADER) == 0)
+ printf("%s:\n", pp->printer);
+
+ if (sump_opts & SUMP_CHDIR_SD) {
+ seteuid(euid);
+ cdres = chdir(pp->spool_dir);
+ seteuid(uid);
+ if (cdres < 0) {
+ printf("\tcannot chdir to %s\n", pp->spool_dir);
+ free_printer(pp);
+ return (NULL);
+ }
+ }
+
+ return (pp);
+}
diff --git a/usr.sbin/lpr/lpc/lpc.h b/usr.sbin/lpr/lpc/lpc.h
new file mode 100644
index 0000000..73e4cc5
--- /dev/null
+++ b/usr.sbin/lpr/lpc/lpc.h
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ *
+ * @(#)lpc.h 8.1 (Berkeley) 6/6/93
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Line Printer Control (lpc) program.
+ */
+struct printer;
+
+#define LPC_PRIVCMD 0x0001 /* a privileged command */
+#define LPC_MSGOPT 0x0002 /* command recognizes -msg option */
+
+struct cmd {
+ const char *c_name; /* command name */
+ const char *c_help; /* help message */
+ const int c_opts; /* flags (eg: privileged command) */
+ /* routine to do all the work for plain cmds, or
+ * initialization work for generic-printer cmds: */
+ void (*c_handler)(int, char *[]);
+ /* routine to do the work for generic-printer cmds: */
+ void (*c_generic)(struct printer *);
+};
diff --git a/usr.sbin/lpr/lpc/movejobs.c b/usr.sbin/lpr/lpc/movejobs.c
new file mode 100644
index 0000000..60c9b38
--- /dev/null
+++ b/usr.sbin/lpr/lpc/movejobs.c
@@ -0,0 +1,270 @@
+/*
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ * Copyright (c) 2002 - Garance Alistair Drosehn <gad@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.
+ *
+ * The views and conclusions contained in the software and documentation
+ * are those of the authors and should not be interpreted as representing
+ * official policies, either expressed or implied, of the FreeBSD Project
+ * or FreeBSD, Inc.
+ *
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ */
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+__FBSDID("$FreeBSD$");
+
+/*
+ * movejobs.c - The lpc commands which move jobs around.
+ */
+
+#include <sys/file.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <dirent.h> /* just for MAXNAMLEN, for job_cfname in lp.h! */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "lp.h"
+#include "lpc.h"
+#include "matchjobs.h"
+#include "extern.h"
+
+/* Values for origcmd in tqbq_common() */
+#define IS_TOPQ 1
+#define IS_BOTQ 2
+
+static int process_jobs(int _argc, char *_argv[], process_jqe
+ _process_rtn, void *myinfo);
+static process_jqe touch_jqe;
+static void tqbq_common(int _argc, char *_argv[], int _origcmd);
+
+/*
+ * isdigit is defined to work on an 'int', in the range 0 to 255, plus EOF.
+ * Define a wrapper which can take 'char', either signed or unsigned.
+ */
+#define isdigitch(Anychar) isdigit(((int) Anychar) & 255)
+
+struct touchjqe_info { /* for topq/bottomq */
+ time_t newtime;
+};
+
+static int nitems;
+static struct jobqueue **queue;
+
+/*
+ * Process all the jobs, as specified by the user.
+ */
+static int
+process_jobs(int argc, char *argv[], process_jqe process_rtn, void *myinfo)
+{
+ struct jobspec_hdr jobs_wanted;
+ int i, matchcnt, pjres;
+
+ STAILQ_INIT(&jobs_wanted);
+ for (i = 0; i < argc; i++) {
+ pjres = parse_jobspec(argv[i], &jobs_wanted);
+ if (pjres == 0) {
+ printf("\tinvalid job specifier: %s\n", argv[i]);
+ continue;
+ }
+ }
+ matchcnt = scanq_jobspec(nitems, queue, SCQ_JSORDER, &jobs_wanted,
+ process_rtn, myinfo);
+
+ free_jobspec(&jobs_wanted);
+ return (matchcnt);
+}
+
+/*
+ * Reposition the job by changing the modification time of the
+ * control file.
+ */
+static int
+touch_jqe(void *myinfo, struct jobqueue *jq, struct jobspec *jspec)
+{
+ struct timeval tvp[2];
+ struct touchjqe_info *touch_info;
+ int ret;
+
+ /*
+ * If the entire queue has been scanned for the current jobspec,
+ * then let the user know if there were no jobs matched by that
+ * specification.
+ */
+ if (jq == NULL) {
+ if (jspec->matchcnt == 0) {
+ format_jobspec(jspec, FMTJS_VERBOSE);
+ if (jspec->pluralfmt)
+ printf("\tjobs %s are not in the queue\n",
+ jspec->fmtoutput);
+ else
+ printf("\tjob %s is not in the queue\n",
+ jspec->fmtoutput);
+ }
+ return (1);
+ }
+
+ /*
+ * Do a little juggling with "matched" vs "processed", so a single
+ * job can be matched by multiple specifications, and yet it will
+ * be moved only once. This is so, eg, 'topq lp 7 7' will not
+ * complain "job 7 is not in queue" for the second specification.
+ */
+ jq->job_matched = 0;
+ if (jq->job_processed) {
+ printf("\tmoved %s earlier\n", jq->job_cfname);
+ return (1);
+ }
+ jq->job_processed = 1;
+
+ touch_info = myinfo;
+ tvp[0].tv_sec = tvp[1].tv_sec = ++touch_info->newtime;
+ tvp[0].tv_usec = tvp[1].tv_usec = 0;
+ seteuid(euid);
+ ret = utimes(jq->job_cfname, tvp);
+ seteuid(uid);
+
+ if (ret == 0) {
+ if (jspec->matcheduser)
+ printf("\tmoved %s (user %s)\n", jq->job_cfname,
+ jspec->matcheduser);
+ else
+ printf("\tmoved %s\n", jq->job_cfname);
+ }
+ return (ret);
+}
+
+/*
+ * Put the specified jobs at the bottom of printer queue.
+ */
+void
+bottomq_cmd(int argc, char *argv[])
+{
+
+ if (argc < 3) {
+ printf("usage: bottomq printer [jobspec ...]\n");
+ return;
+ }
+ --argc; /* First argv was the command name */
+ ++argv;
+
+ tqbq_common(argc, argv, IS_BOTQ);
+}
+
+/*
+ * Put the specified jobs at the top of printer queue.
+ */
+void
+topq_cmd(int argc, char *argv[])
+{
+
+ if (argc < 3) {
+ printf("usage: topq printer [jobspec ...]\n");
+ return;
+ }
+ --argc; /* First argv was the command name */
+ ++argv;
+
+ tqbq_common(argc, argv, IS_TOPQ);
+}
+
+/*
+ * Processing in common between topq and bottomq commands.
+ */
+void
+tqbq_common(int argc, char *argv[], int origcmd)
+{
+ struct printer myprinter, *pp;
+ struct touchjqe_info touch_info;
+ int i, movecnt, setres;
+
+ pp = setup_myprinter(*argv, &myprinter, SUMP_CHDIR_SD);
+ if (pp == NULL)
+ return;
+ --argc; /* Second argv was the printer name */
+ ++argv;
+
+ nitems = getq(pp, &queue);
+ if (nitems == 0) {
+ printf("\tthere are no jobs in the queue\n");
+ free_printer(pp);
+ return;
+ }
+
+ /*
+ * The only real difference between topq and bottomq is the
+ * initial value used for newtime.
+ */
+ switch (origcmd) {
+ case IS_BOTQ:
+ /*
+ * When moving jobs to the bottom of the queue, pick a
+ * starting value which is one second after the last job
+ * in the queue.
+ */
+ touch_info.newtime = queue[nitems - 1]->job_time + 1;
+ break;
+ case IS_TOPQ:
+ /*
+ * When moving jobs to the top of the queue, the greatest
+ * number of jobs which could be moved is all the jobs
+ * that are in the queue. Pick a starting value which
+ * leaves plenty of room for all existing jobs.
+ */
+ touch_info.newtime = queue[0]->job_time - nitems - 5;
+ break;
+ default:
+ printf("\ninternal error in topq/bottomq processing.\n");
+ return;
+ }
+
+ movecnt = process_jobs(argc, argv, touch_jqe, &touch_info);
+
+ /*
+ * If any jobs were moved, then chmod the lock file to notify any
+ * active process for this queue that the queue has changed, so
+ * it will rescan the queue to find out the new job order.
+ */
+ if (movecnt == 0)
+ printf("\tqueue order unchanged\n");
+ else {
+ setres = set_qstate(SQS_QCHANGED, pp->lock_file);
+ if (setres < 0)
+ printf("\t* queue order changed for %s, but the\n"
+ "\t* attempt to set_qstate() failed [%d]!\n",
+ pp->printer, setres);
+ }
+
+ for (i = 0; i < nitems; i++)
+ free(queue[i]);
+ free(queue);
+ free_printer(pp);
+}
+
diff --git a/usr.sbin/lpr/lpd/Makefile b/usr.sbin/lpr/lpd/Makefile
new file mode 100644
index 0000000..9af3655
--- /dev/null
+++ b/usr.sbin/lpr/lpd/Makefile
@@ -0,0 +1,13 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= lpd
+MAN= lpd.8
+SRCS= lpd.c printjob.c recvjob.c lpdchar.c modes.c
+
+CFLAGS+= -I${.CURDIR}/../common_source
+
+DPADD= ${LIBLPR}
+LDADD= ${LIBLPR}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/lpd/extern.h b/usr.sbin/lpr/lpd/extern.h
new file mode 100644
index 0000000..14cb9e1
--- /dev/null
+++ b/usr.sbin/lpr/lpd/extern.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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: @(#)extern.h 8.1 (Berkeley) 6/6/93
+ * $FreeBSD$
+ */
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+
+extern char scnkey[][HEIGHT]; /* in lpdchar.c */
+extern int lflag; /* in lpd.c */
+
+struct printer;
+struct termios;
+
+__BEGIN_DECLS
+void printjob(struct printer *_pp);
+void startprinting(const char *_printer);
+void recvjob(const char *_printer);
+int msearch(char *_str, struct termios *_ip);
+__END_DECLS
diff --git a/usr.sbin/lpr/lpd/lpd.8 b/usr.sbin/lpr/lpd/lpd.8
new file mode 100644
index 0000000..7c7afab
--- /dev/null
+++ b/usr.sbin/lpr/lpd/lpd.8
@@ -0,0 +1,345 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.\" @(#)lpd.8 8.3 (Berkeley) 4/19/94
+.\" $FreeBSD$
+.\"
+.Dd June 6, 2001
+.Dt LPD 8
+.Os
+.Sh NAME
+.Nm lpd
+.Nd line printer spooler daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl cdlpsW46
+.Op Ar port#
+.Sh DESCRIPTION
+The
+.Nm
+utility
+is the line printer daemon (spool area handler) and is normally invoked
+at boot time from the
+.Xr rc 8
+file.
+It makes a single pass through the
+.Xr printcap 5
+file to find out about the existing printers and
+prints any files left after a crash.
+It then uses the system calls
+.Xr listen 2
+and
+.Xr accept 2
+to receive requests to print files in the queue,
+transfer files to the spooling area, display the queue,
+or remove jobs from the queue.
+In each case, it forks a child to handle
+the request so the parent can continue to listen for more requests.
+.Pp
+Available options:
+.Bl -tag -width Ds
+.It Fl c
+By default, if some remote host has a connection error while trying to
+send a print request to
+.Nm
+on a local host,
+.Nm
+will only send error message to that remote host.
+The
+.Fl c
+flag causes
+.Nm
+to also log all of those connection errors via
+.Xr syslog 3 .
+.It Fl d
+Turn on
+.Dv SO_DEBUG
+on the Internet listening socket (see
+.Xr setsockopt 2 ) .
+.It Fl l
+The
+.Fl l
+flag causes
+.Nm
+to log valid requests received from the network.
+This can be useful
+for debugging purposes.
+.It Fl p
+The
+.Fl p
+flag is a synonym for the
+.Fl s
+flag.
+It is being deprecated, and may be removed in a
+future version of
+.Nm .
+.It Fl s
+The
+.Fl s
+(secure) flag causes
+.Nm
+not to open an Internet listening socket.
+This means that
+.Nm
+will not accept any connections from any remote
+hosts, although it will still accept print requests
+from all local users.
+.It Fl W
+By default, the
+.Nm
+daemon will only accept connections which originate
+from a reserved-port (<1024) on the remote host.
+The
+.Fl W
+flag causes
+.Nm
+to accept connections coming from any port.
+This is can be useful when you want to accept print jobs
+from certain implementations of lpr written for Windows.
+.It Fl 4
+Inet only.
+.It Fl 6
+Inet6 only.
+.It Fl 46
+Inet and inet6 (default).
+.It Ar "port#"
+The Internet port number used to rendezvous
+with other processes is normally obtained with
+.Xr getservbyname 3
+but can be changed with the
+.Ar port#
+argument.
+.El
+.Pp
+Access control is provided by two means.
+First, all requests must come from
+one of the machines listed in the file
+.Pa /etc/hosts.equiv
+or
+.Pa /etc/hosts.lpd .
+Second, if the
+.Li rs
+capability is specified in the
+.Xr printcap 5
+entry for the printer being accessed,
+.Em lpr
+requests will only be honored for those users with accounts on the
+machine with the printer.
+.Pp
+The file
+.Em minfree
+in each spool directory contains the number of disk blocks to leave free
+so that the line printer queue will not completely fill the disk.
+The
+.Em minfree
+file can be edited with your favorite text editor.
+.Pp
+The daemon begins processing files
+after it has successfully set the lock for exclusive
+access (described a bit later),
+and scans the spool directory
+for files beginning with
+.Em cf .
+Lines in each
+.Em cf
+file specify files to be printed or non-printing actions to be
+performed.
+Each such line begins with a key character
+to specify what to do with the remainder of the line.
+.Bl -tag -width Ds
+.It J
+Job Name.
+String to be used for the job name on the burst page.
+.It C
+Classification.
+String to be used for the classification line
+on the burst page.
+.It L
+Literal.
+The line contains identification info from
+the password file and causes the banner page to be printed.
+.It T
+Title.
+String to be used as the title for
+.Xr pr 1 .
+.It H
+Host Name.
+Name of the machine where
+.Xr lpr 1
+was invoked.
+.It P
+Person.
+Login name of the person who invoked
+.Xr lpr 1 .
+This is used to verify ownership by
+.Xr lprm 1 .
+.It M
+Send mail to the specified user when the current print job completes.
+.It f
+Formatted File.
+Name of a file to print which is already formatted.
+.It l
+Like ``f'' but passes control characters and does not make page breaks.
+.It p
+Name of a file to print using
+.Xr pr 1
+as a filter.
+.It t
+Troff File.
+The file contains
+.Xr troff 1
+output (cat phototypesetter commands).
+.It n
+Ditroff File.
+The file contains device independent troff
+output.
+.It r
+DVI File.
+The file contains
+.Tn Tex l
+output
+DVI format from Stanford.
+.It g
+Graph File.
+The file contains data produced by
+.Xr plot 3 .
+.It c
+Cifplot File.
+The file contains data produced by
+.Em cifplot .
+.It v
+The file contains a raster image.
+.It r
+The file contains text data with
+FORTRAN carriage control characters.
+.It \&1
+Troff Font R.
+Name of the font file to use instead of the default.
+.It \&2
+Troff Font I.
+Name of the font file to use instead of the default.
+.It \&3
+Troff Font B.
+Name of the font file to use instead of the default.
+.It \&4
+Troff Font S.
+Name of the font file to use instead of the default.
+.It W
+Width.
+Changes the page width (in characters) used by
+.Xr pr 1
+and the text filters.
+.It I
+Indent.
+The number of characters to indent the output by (in ASCII).
+.It U
+Unlink.
+Name of file to remove upon completion of printing.
+.It N
+File name.
+The name of the file which is being printed, or a blank
+for the standard input (when
+.Xr lpr 1
+is invoked in a pipeline).
+.It Z
+Locale.
+String to be used as the locale for
+.Xr pr 1 .
+.El
+.Pp
+If a file cannot be opened, a message will be logged via
+.Xr syslog 3
+using the
+.Em LOG_LPR
+facility.
+The
+.Nm
+utility will try up to 20 times
+to reopen a file it expects to be there, after which it will
+skip the file to be printed.
+.Pp
+The
+.Nm
+utility uses
+.Xr flock 2
+to provide exclusive access to the lock file and to prevent multiple
+daemons from becoming active simultaneously.
+If the daemon should be killed
+or die unexpectedly, the lock file need not be removed.
+The lock file is kept in a readable
+.Tn ASCII
+form
+and contains two lines.
+The first is the process id of the daemon and the second is the control
+file name of the current job being printed.
+The second line is updated to
+reflect the current status of
+.Nm
+for the programs
+.Xr lpq 1
+and
+.Xr lprm 1 .
+.Sh FILES
+.Bl -tag -width "/var/spool/*/minfree" -compact
+.It Pa /etc/printcap
+printer description file
+.It Pa /var/spool/*
+spool directories
+.It Pa /var/spool/*/minfree
+minimum free space to leave
+.It Pa /dev/lp*
+line printer devices
+.It Pa /var/run/printer
+socket for local requests
+.It Pa /etc/hosts.equiv
+lists machine names allowed printer access
+.It Pa /etc/hosts.lpd
+lists machine names allowed printer access,
+but not under same administrative control.
+.El
+.Sh SEE ALSO
+.Xr lpq 1 ,
+.Xr lpr 1 ,
+.Xr lprm 1 ,
+.Xr setsockopt 2 ,
+.Xr syslog 3 ,
+.Xr hosts.lpd 5 ,
+.Xr printcap 5 ,
+.Xr chkprintcap 8 ,
+.Xr lpc 8 ,
+.Xr pac 8
+.Rs
+.%T "4.2 BSD Line Printer Spooler Manual"
+.Re
+.Sh HISTORY
+An
+.Nm
+daemon appeared in Version 6 AT&T UNIX.
diff --git a/usr.sbin/lpr/lpd/lpd.c b/usr.sbin/lpr/lpd/lpd.c
new file mode 100644
index 0000000..087d5d1
--- /dev/null
+++ b/usr.sbin/lpr/lpd/lpd.c
@@ -0,0 +1,946 @@
+/*
+ * Copyright (c) 1983, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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) 1983, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)lpd.c 8.7 (Berkeley) 5/10/95";
+#endif /* not lint */
+#endif
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+__FBSDID("$FreeBSD$");
+
+/*
+ * lpd -- line printer daemon.
+ *
+ * Listen for a connection and perform the requested operation.
+ * Operations are:
+ * \1printer\n
+ * check the queue for jobs and print any found.
+ * \2printer\n
+ * receive a job from another machine and queue it.
+ * \3printer [users ...] [jobs ...]\n
+ * return the current state of the queue (short form).
+ * \4printer [users ...] [jobs ...]\n
+ * return the current state of the queue (long form).
+ * \5printer person [users ...] [jobs ...]\n
+ * remove jobs from the queue.
+ *
+ * Strategy to maintain protected spooling area:
+ * 1. Spooling area is writable only by daemon and spooling group
+ * 2. lpr runs setuid root and setgrp spooling group; it uses
+ * root to access any file it wants (verifying things before
+ * with an access call) and group id to know how it should
+ * set up ownership of files in the spooling area.
+ * 3. Files in spooling area are owned by root, group spooling
+ * group, with mode 660.
+ * 4. lpd, lpq and lprm run setuid daemon and setgrp spooling group to
+ * access files and printer. Users can't get to anything
+ * w/o help of lpq and lprm programs.
+ */
+
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <netdb.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <signal.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <ctype.h>
+#include "lp.h"
+#include "lp.local.h"
+#include "pathnames.h"
+#include "extern.h"
+
+int lflag; /* log requests flag */
+int sflag; /* no incoming port flag */
+int from_remote; /* from remote socket */
+
+int main(int argc, char **_argv);
+static void reapchild(int _signo);
+static void mcleanup(int _signo);
+static void doit(void);
+static void startup(void);
+static void chkhost(struct sockaddr *_f, int _ch_opts);
+static int ckqueue(struct printer *_pp);
+static void fhosterr(int _ch_opts, char *_sysmsg, char *_usermsg);
+static int *socksetup(int _af, int _debuglvl);
+static void usage(void);
+
+/* XXX from libc/net/rcmd.c */
+extern int __ivaliduser_sa(FILE *, struct sockaddr *, socklen_t,
+ const char *, const char *);
+
+uid_t uid, euid;
+
+#define LPD_NOPORTCHK 0001 /* skip reserved-port check */
+#define LPD_LOGCONNERR 0002 /* (sys)log connection errors */
+#define LPD_ADDFROMLINE 0004 /* just used for fhosterr() */
+
+int
+main(int argc, char **argv)
+{
+ int ch_options, errs, f, funix, *finet, i, lfd, socket_debug;
+ fd_set defreadfds;
+ struct sockaddr_un un, fromunix;
+ struct sockaddr_storage frominet;
+ socklen_t fromlen;
+ sigset_t omask, nmask;
+ struct servent *sp, serv;
+ int inet_flag = 0, inet6_flag = 0;
+
+ euid = geteuid(); /* these shouldn't be different */
+ uid = getuid();
+
+ ch_options = 0;
+ socket_debug = 0;
+ gethostname(local_host, sizeof(local_host));
+
+ progname = "lpd";
+
+ if (euid != 0)
+ errx(EX_NOPERM,"must run as root");
+
+ errs = 0;
+ while ((i = getopt(argc, argv, "cdlpswW46")) != -1)
+ switch (i) {
+ case 'c':
+ /* log all kinds of connection-errors to syslog */
+ ch_options |= LPD_LOGCONNERR;
+ break;
+ case 'd':
+ socket_debug++;
+ break;
+ case 'l':
+ lflag++;
+ break;
+ case 'p': /* letter initially used for -s */
+ /*
+ * This will probably be removed with 5.0-release.
+ */
+ /* FALLTHROUGH */
+ case 's': /* secure (no inet) */
+ sflag++;
+ break;
+ case 'w': /* netbsd uses -w for maxwait */
+ /*
+ * This will be removed after the release of 4.4, as
+ * it conflicts with -w in netbsd's lpd. For now it
+ * is just a warning, so we won't suddenly break lpd
+ * for anyone who is currently using the option.
+ */
+ syslog(LOG_WARNING,
+ "NOTE: the -w option has been renamed -W");
+ syslog(LOG_WARNING,
+ "NOTE: please change your lpd config to use -W");
+ /* FALLTHROUGH */
+ case 'W':
+ /* allow connections coming from a non-reserved port */
+ /* (done by some lpr-implementations for MS-Windows) */
+ ch_options |= LPD_NOPORTCHK;
+ break;
+ case '4':
+ family = PF_INET;
+ inet_flag++;
+ break;
+ case '6':
+#ifdef INET6
+ family = PF_INET6;
+ inet6_flag++;
+#else
+ errx(EX_USAGE, "lpd compiled sans INET6 (IPv6 support)");
+#endif
+ break;
+ /*
+ * The following options are not in FreeBSD (yet?), but are
+ * listed here to "reserve" them, because the option-letters
+ * are used by either NetBSD or OpenBSD (as of July 2001).
+ */
+ case 'b': /* set bind-addr */
+ case 'n': /* set max num of children */
+ case 'r': /* allow 'of' for remote ptrs */
+ /* ...[not needed in freebsd] */
+ /* FALLTHROUGH */
+ default:
+ errs++;
+ }
+ if (inet_flag && inet6_flag)
+ family = PF_UNSPEC;
+ argc -= optind;
+ argv += optind;
+ if (errs)
+ usage();
+
+ if (argc == 1) {
+ if ((i = atoi(argv[0])) == 0)
+ usage();
+ if (i < 0 || i > USHRT_MAX)
+ errx(EX_USAGE, "port # %d is invalid", i);
+
+ serv.s_port = htons(i);
+ sp = &serv;
+ argc--;
+ } else {
+ sp = getservbyname("printer", "tcp");
+ if (sp == NULL)
+ errx(EX_OSFILE, "printer/tcp: unknown service");
+ }
+
+ if (argc != 0)
+ usage();
+
+ /*
+ * We run chkprintcap right away to catch any errors and blat them
+ * to stderr while we still have it open, rather than sending them
+ * to syslog and leaving the user wondering why lpd started and
+ * then stopped. There should probably be a command-line flag to
+ * ignore errors from chkprintcap.
+ */
+ {
+ pid_t pid;
+ int status;
+ pid = fork();
+ if (pid < 0) {
+ err(EX_OSERR, "cannot fork");
+ } else if (pid == 0) { /* child */
+ execl(_PATH_CHKPRINTCAP, _PATH_CHKPRINTCAP, (char *)0);
+ err(EX_OSERR, "cannot execute %s", _PATH_CHKPRINTCAP);
+ }
+ if (waitpid(pid, &status, 0) < 0) {
+ err(EX_OSERR, "cannot wait");
+ }
+ if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
+ errx(EX_OSFILE, "%d errors in printcap file, exiting",
+ WEXITSTATUS(status));
+ }
+
+#ifndef DEBUG
+ /*
+ * Set up standard environment by detaching from the parent.
+ */
+ daemon(0, 0);
+#endif
+
+ openlog("lpd", LOG_PID, LOG_LPR);
+ syslog(LOG_INFO, "lpd startup: logging=%d%s%s", lflag,
+ socket_debug ? " dbg" : "", sflag ? " net-secure" : "");
+ (void) umask(0);
+ /*
+ * NB: This depends on O_NONBLOCK semantics doing the right thing;
+ * i.e., applying only to the O_EXLOCK and not to the rest of the
+ * open/creation. As of 1997-12-02, this is the case for commonly-
+ * used filesystems. There are other places in this code which
+ * make the same assumption.
+ */
+ lfd = open(_PATH_MASTERLOCK, O_WRONLY|O_CREAT|O_EXLOCK|O_NONBLOCK,
+ LOCK_FILE_MODE);
+ if (lfd < 0) {
+ if (errno == EWOULDBLOCK) /* active daemon present */
+ exit(0);
+ syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
+ exit(1);
+ }
+ fcntl(lfd, F_SETFL, 0); /* turn off non-blocking mode */
+ ftruncate(lfd, 0);
+ /*
+ * write process id for others to know
+ */
+ sprintf(line, "%u\n", getpid());
+ f = strlen(line);
+ if (write(lfd, line, f) != f) {
+ syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
+ exit(1);
+ }
+ signal(SIGCHLD, reapchild);
+ /*
+ * Restart all the printers.
+ */
+ startup();
+ (void) unlink(_PATH_SOCKETNAME);
+ funix = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (funix < 0) {
+ syslog(LOG_ERR, "socket: %m");
+ exit(1);
+ }
+
+ sigemptyset(&nmask);
+ sigaddset(&nmask, SIGHUP);
+ sigaddset(&nmask, SIGINT);
+ sigaddset(&nmask, SIGQUIT);
+ sigaddset(&nmask, SIGTERM);
+ sigprocmask(SIG_BLOCK, &nmask, &omask);
+
+ (void) umask(07);
+ signal(SIGHUP, mcleanup);
+ signal(SIGINT, mcleanup);
+ signal(SIGQUIT, mcleanup);
+ signal(SIGTERM, mcleanup);
+ memset(&un, 0, sizeof(un));
+ un.sun_family = AF_UNIX;
+ strcpy(un.sun_path, _PATH_SOCKETNAME);
+#ifndef SUN_LEN
+#define SUN_LEN(unp) (strlen((unp)->sun_path) + 2)
+#endif
+ if (bind(funix, (struct sockaddr *)&un, SUN_LEN(&un)) < 0) {
+ syslog(LOG_ERR, "ubind: %m");
+ exit(1);
+ }
+ (void) umask(0);
+ sigprocmask(SIG_SETMASK, &omask, (sigset_t *)0);
+ FD_ZERO(&defreadfds);
+ FD_SET(funix, &defreadfds);
+ listen(funix, 5);
+ if (sflag == 0) {
+ finet = socksetup(family, socket_debug);
+ } else
+ finet = NULL; /* pretend we couldn't open TCP socket. */
+ if (finet) {
+ for (i = 1; i <= *finet; i++) {
+ FD_SET(finet[i], &defreadfds);
+ listen(finet[i], 5);
+ }
+ }
+ /*
+ * Main loop: accept, do a request, continue.
+ */
+ memset(&frominet, 0, sizeof(frominet));
+ memset(&fromunix, 0, sizeof(fromunix));
+ if (lflag)
+ syslog(LOG_INFO, "lpd startup: ready to accept requests");
+ /*
+ * XXX - should be redone for multi-protocol
+ */
+ for (;;) {
+ int domain, nfds, s;
+ fd_set readfds;
+
+ FD_COPY(&defreadfds, &readfds);
+ nfds = select(20, &readfds, 0, 0, 0);
+ if (nfds <= 0) {
+ if (nfds < 0 && errno != EINTR)
+ syslog(LOG_WARNING, "select: %m");
+ continue;
+ }
+ domain = -1; /* avoid compile-time warning */
+ s = -1; /* avoid compile-time warning */
+ if (FD_ISSET(funix, &readfds)) {
+ domain = AF_UNIX, fromlen = sizeof(fromunix);
+ s = accept(funix,
+ (struct sockaddr *)&fromunix, &fromlen);
+ } else {
+ for (i = 1; i <= *finet; i++)
+ if (FD_ISSET(finet[i], &readfds)) {
+ domain = AF_INET;
+ fromlen = sizeof(frominet);
+ s = accept(finet[i],
+ (struct sockaddr *)&frominet,
+ &fromlen);
+ }
+ }
+ if (s < 0) {
+ if (errno != EINTR)
+ syslog(LOG_WARNING, "accept: %m");
+ continue;
+ }
+ if (fork() == 0) {
+ /*
+ * Note that printjob() also plays around with
+ * signal-handling routines, and may need to be
+ * changed when making changes to signal-handling.
+ */
+ signal(SIGCHLD, SIG_DFL);
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGTERM, SIG_IGN);
+ (void) close(funix);
+ if (sflag == 0 && finet) {
+ for (i = 1; i <= *finet; i++)
+ (void)close(finet[i]);
+ }
+ dup2(s, STDOUT_FILENO);
+ (void) close(s);
+ if (domain == AF_INET) {
+ /* for both AF_INET and AF_INET6 */
+ from_remote = 1;
+ chkhost((struct sockaddr *)&frominet,
+ ch_options);
+ } else
+ from_remote = 0;
+ doit();
+ exit(0);
+ }
+ (void) close(s);
+ }
+}
+
+static void
+reapchild(int signo __unused)
+{
+ int status;
+
+ while (wait3(&status, WNOHANG, 0) > 0)
+ ;
+}
+
+static void
+mcleanup(int signo)
+{
+ /*
+ * XXX syslog(3) is not signal-safe.
+ */
+ if (lflag) {
+ if (signo)
+ syslog(LOG_INFO, "exiting on signal %d", signo);
+ else
+ syslog(LOG_INFO, "exiting");
+ }
+ unlink(_PATH_SOCKETNAME);
+ exit(0);
+}
+
+/*
+ * Stuff for handling job specifications
+ */
+char *user[MAXUSERS]; /* users to process */
+int users; /* # of users in user array */
+int requ[MAXREQUESTS]; /* job number of spool entries */
+int requests; /* # of spool requests */
+char *person; /* name of person doing lprm */
+
+ /* buffer to hold the client's machine-name */
+static char frombuf[MAXHOSTNAMELEN];
+char cbuf[BUFSIZ]; /* command line buffer */
+const char *cmdnames[] = {
+ "null",
+ "printjob",
+ "recvjob",
+ "displayq short",
+ "displayq long",
+ "rmjob"
+};
+
+static void
+doit(void)
+{
+ char *cp, *printer;
+ int n;
+ int status;
+ struct printer myprinter, *pp = &myprinter;
+
+ init_printer(&myprinter);
+
+ for (;;) {
+ cp = cbuf;
+ do {
+ if (cp >= &cbuf[sizeof(cbuf) - 1])
+ fatal(0, "Command line too long");
+ if ((n = read(STDOUT_FILENO, cp, 1)) != 1) {
+ if (n < 0)
+ fatal(0, "Lost connection");
+ return;
+ }
+ } while (*cp++ != '\n');
+ *--cp = '\0';
+ cp = cbuf;
+ if (lflag) {
+ if (*cp >= '\1' && *cp <= '\5')
+ syslog(LOG_INFO, "%s requests %s %s",
+ from_host, cmdnames[(u_char)*cp], cp+1);
+ else
+ syslog(LOG_INFO, "bad request (%d) from %s",
+ *cp, from_host);
+ }
+ switch (*cp++) {
+ case CMD_CHECK_QUE: /* check the queue, print any jobs there */
+ startprinting(cp);
+ break;
+ case CMD_TAKE_THIS: /* receive files to be queued */
+ if (!from_remote) {
+ syslog(LOG_INFO, "illegal request (%d)", *cp);
+ exit(1);
+ }
+ recvjob(cp);
+ break;
+ case CMD_SHOWQ_SHORT: /* display the queue (short form) */
+ case CMD_SHOWQ_LONG: /* display the queue (long form) */
+ /* XXX - this all needs to be redone. */
+ printer = cp;
+ while (*cp) {
+ if (*cp != ' ') {
+ cp++;
+ continue;
+ }
+ *cp++ = '\0';
+ while (isspace(*cp))
+ cp++;
+ if (*cp == '\0')
+ break;
+ if (isdigit(*cp)) {
+ if (requests >= MAXREQUESTS)
+ fatal(0, "Too many requests");
+ requ[requests++] = atoi(cp);
+ } else {
+ if (users >= MAXUSERS)
+ fatal(0, "Too many users");
+ user[users++] = cp;
+ }
+ }
+ status = getprintcap(printer, pp);
+ if (status < 0)
+ fatal(pp, "%s", pcaperr(status));
+ displayq(pp, cbuf[0] == CMD_SHOWQ_LONG);
+ exit(0);
+ case CMD_RMJOB: /* remove a job from the queue */
+ if (!from_remote) {
+ syslog(LOG_INFO, "illegal request (%d)", *cp);
+ exit(1);
+ }
+ printer = cp;
+ while (*cp && *cp != ' ')
+ cp++;
+ if (!*cp)
+ break;
+ *cp++ = '\0';
+ person = cp;
+ while (*cp) {
+ if (*cp != ' ') {
+ cp++;
+ continue;
+ }
+ *cp++ = '\0';
+ while (isspace(*cp))
+ cp++;
+ if (*cp == '\0')
+ break;
+ if (isdigit(*cp)) {
+ if (requests >= MAXREQUESTS)
+ fatal(0, "Too many requests");
+ requ[requests++] = atoi(cp);
+ } else {
+ if (users >= MAXUSERS)
+ fatal(0, "Too many users");
+ user[users++] = cp;
+ }
+ }
+ rmjob(printer);
+ break;
+ }
+ fatal(0, "Illegal service request");
+ }
+}
+
+/*
+ * Make a pass through the printcap database and start printing any
+ * files left from the last time the machine went down.
+ */
+static void
+startup(void)
+{
+ int pid, status, more;
+ struct printer myprinter, *pp = &myprinter;
+
+ more = firstprinter(pp, &status);
+ if (status)
+ goto errloop;
+ while (more) {
+ if (ckqueue(pp) <= 0) {
+ goto next;
+ }
+ if (lflag)
+ syslog(LOG_INFO, "lpd startup: work for %s",
+ pp->printer);
+ if ((pid = fork()) < 0) {
+ syslog(LOG_WARNING, "lpd startup: cannot fork for %s",
+ pp->printer);
+ mcleanup(0);
+ }
+ if (pid == 0) {
+ lastprinter();
+ printjob(pp);
+ /* NOTREACHED */
+ }
+ do {
+next:
+ more = nextprinter(pp, &status);
+errloop:
+ if (status)
+ syslog(LOG_WARNING,
+ "lpd startup: printcap entry for %s has errors, skipping",
+ pp->printer ? pp->printer : "<noname?>");
+ } while (more && status);
+ }
+}
+
+/*
+ * Make sure there's some work to do before forking off a child
+ */
+static int
+ckqueue(struct printer *pp)
+{
+ register struct dirent *d;
+ DIR *dirp;
+ char *spooldir;
+
+ spooldir = pp->spool_dir;
+ if ((dirp = opendir(spooldir)) == NULL)
+ return (-1);
+ while ((d = readdir(dirp)) != NULL) {
+ if (d->d_name[0] != 'c' || d->d_name[1] != 'f')
+ continue; /* daemon control files only */
+ closedir(dirp);
+ return (1); /* found something */
+ }
+ closedir(dirp);
+ return (0);
+}
+
+#define DUMMY ":nobody::"
+
+/*
+ * Check to see if the host connecting to this host has access to any
+ * lpd services on this host.
+ */
+static void
+chkhost(struct sockaddr *f, int ch_opts)
+{
+ struct addrinfo hints, *res, *r;
+ register FILE *hostf;
+ char hostbuf[NI_MAXHOST], ip[NI_MAXHOST];
+ char serv[NI_MAXSERV];
+ char *syserr, *usererr;
+ int error, errsav, fpass, good, wantsl;
+
+ wantsl = 0;
+ if (ch_opts & LPD_LOGCONNERR)
+ wantsl = 1; /* also syslog the errors */
+
+ from_host = ".na.";
+
+ /* Need real hostname for temporary filenames */
+ error = getnameinfo(f, f->sa_len, hostbuf, sizeof(hostbuf), NULL, 0,
+ NI_NAMEREQD);
+ if (error) {
+ errsav = error;
+ error = getnameinfo(f, f->sa_len, hostbuf, sizeof(hostbuf),
+ NULL, 0, NI_NUMERICHOST);
+ if (error) {
+ asprintf(&syserr,
+ "can not determine hostname for remote host (%d,%d)",
+ errsav, error);
+ asprintf(&usererr,
+ "Host name for your address is not known");
+ fhosterr(ch_opts, syserr, usererr);
+ /* NOTREACHED */
+ }
+ asprintf(&syserr,
+ "Host name for remote host (%s) not known (%d)",
+ hostbuf, errsav);
+ asprintf(&usererr,
+ "Host name for your address (%s) is not known",
+ hostbuf);
+ fhosterr(ch_opts, syserr, usererr);
+ /* NOTREACHED */
+ }
+
+ strlcpy(frombuf, hostbuf, sizeof(frombuf));
+ from_host = frombuf;
+ ch_opts |= LPD_ADDFROMLINE;
+
+ /* Need address in stringform for comparison (no DNS lookup here) */
+ error = getnameinfo(f, f->sa_len, hostbuf, sizeof(hostbuf), NULL, 0,
+ NI_NUMERICHOST);
+ if (error) {
+ asprintf(&syserr, "Cannot print IP address (error %d)",
+ error);
+ asprintf(&usererr, "Cannot print IP address for your host");
+ fhosterr(ch_opts, syserr, usererr);
+ /* NOTREACHED */
+ }
+ from_ip = strdup(hostbuf);
+
+ /* Reject numeric addresses */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+ hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
+ if (getaddrinfo(from_host, NULL, &hints, &res) == 0) {
+ freeaddrinfo(res);
+ /* This syslog message already includes from_host */
+ ch_opts &= ~LPD_ADDFROMLINE;
+ asprintf(&syserr, "reverse lookup results in non-FQDN %s",
+ from_host);
+ /* same message to both syslog and remote user */
+ fhosterr(ch_opts, syserr, syserr);
+ /* NOTREACHED */
+ }
+
+ /* Check for spoof, ala rlogind */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+ error = getaddrinfo(from_host, NULL, &hints, &res);
+ if (error) {
+ asprintf(&syserr, "dns lookup for address %s failed: %s",
+ from_ip, gai_strerror(error));
+ asprintf(&usererr, "hostname for your address (%s) unknown: %s",
+ from_ip, gai_strerror(error));
+ fhosterr(ch_opts, syserr, usererr);
+ /* NOTREACHED */
+ }
+ good = 0;
+ for (r = res; good == 0 && r; r = r->ai_next) {
+ error = getnameinfo(r->ai_addr, r->ai_addrlen, ip, sizeof(ip),
+ NULL, 0, NI_NUMERICHOST);
+ if (!error && !strcmp(from_ip, ip))
+ good = 1;
+ }
+ if (res)
+ freeaddrinfo(res);
+ if (good == 0) {
+ asprintf(&syserr, "address for remote host (%s) not matched",
+ from_ip);
+ asprintf(&usererr,
+ "address for your hostname (%s) not matched", from_ip);
+ fhosterr(ch_opts, syserr, usererr);
+ /* NOTREACHED */
+ }
+
+ fpass = 1;
+ hostf = fopen(_PATH_HOSTSEQUIV, "r");
+again:
+ if (hostf) {
+ if (__ivaliduser_sa(hostf, f, f->sa_len, DUMMY, DUMMY) == 0) {
+ (void) fclose(hostf);
+ goto foundhost;
+ }
+ (void) fclose(hostf);
+ }
+ if (fpass == 1) {
+ fpass = 2;
+ hostf = fopen(_PATH_HOSTSLPD, "r");
+ goto again;
+ }
+ /* This syslog message already includes from_host */
+ ch_opts &= ~LPD_ADDFROMLINE;
+ asprintf(&syserr, "refused connection from %s, sip=%s", from_host,
+ from_ip);
+ asprintf(&usererr,
+ "Print-services are not available to your host (%s).", from_host);
+ fhosterr(ch_opts, syserr, usererr);
+ /* NOTREACHED */
+
+foundhost:
+ if (ch_opts & LPD_NOPORTCHK)
+ return; /* skip the reserved-port check */
+
+ error = getnameinfo(f, f->sa_len, NULL, 0, serv, sizeof(serv),
+ NI_NUMERICSERV);
+ if (error) {
+ /* same message to both syslog and remote user */
+ asprintf(&syserr, "malformed from-address (%d)", error);
+ fhosterr(ch_opts, syserr, syserr);
+ /* NOTREACHED */
+ }
+
+ if (atoi(serv) >= IPPORT_RESERVED) {
+ /* same message to both syslog and remote user */
+ asprintf(&syserr, "connected from invalid port (%s)", serv);
+ fhosterr(ch_opts, syserr, syserr);
+ /* NOTREACHED */
+ }
+}
+
+/*
+ * Handle fatal errors in chkhost. The first message will optionally be
+ * sent to syslog, the second one is sent to the connecting host.
+ *
+ * The idea is that the syslog message is meant for an administrator of a
+ * print server (the host receiving connections), while the usermsg is meant
+ * for a remote user who may or may not be clueful, and may or may not be
+ * doing something nefarious. Some remote users (eg, MS-Windows...) may not
+ * even see whatever message is sent, which is why there's the option to
+ * start 'lpd' with the connection-errors also sent to syslog.
+ *
+ * Given that hostnames can theoretically be fairly long (well, over 250
+ * bytes), it would probably be helpful to have the 'from_host' field at
+ * the end of any error messages which include that info.
+ *
+ * These are Fatal host-connection errors, so this routine does not return.
+ */
+static void
+fhosterr(int ch_opts, char *sysmsg, char *usermsg)
+{
+
+ /*
+ * If lpd was started up to print connection errors, then write
+ * the syslog message before the user message.
+ * And for many of the syslog messages, it is helpful to first
+ * write the from_host (if it is known) as a separate syslog
+ * message, since the hostname may be so long.
+ */
+ if (ch_opts & LPD_LOGCONNERR) {
+ if (ch_opts & LPD_ADDFROMLINE) {
+ syslog(LOG_WARNING, "for connection from %s:", from_host);
+ }
+ syslog(LOG_WARNING, "%s", sysmsg);
+ }
+
+ /*
+ * Now send the error message to the remote host which is trying
+ * to make the connection.
+ */
+ printf("%s [@%s]: %s\n", progname, local_host, usermsg);
+ fflush(stdout);
+
+ /*
+ * Add a minimal delay before exiting (and disconnecting from the
+ * sending-host). This is just in case that machine responds by
+ * INSTANTLY retrying (and instantly re-failing...). This may also
+ * give the other side more time to read the error message.
+ */
+ sleep(2); /* a paranoid throttling measure */
+ exit(1);
+}
+
+/* setup server socket for specified address family */
+/* if af is PF_UNSPEC more than one socket may be returned */
+/* the returned list is dynamically allocated, so caller needs to free it */
+static int *
+socksetup(int af, int debuglvl)
+{
+ struct addrinfo hints, *res, *r;
+ int error, maxs, *s, *socks;
+ const int on = 1;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = af;
+ hints.ai_socktype = SOCK_STREAM;
+ error = getaddrinfo(NULL, "printer", &hints, &res);
+ if (error) {
+ syslog(LOG_ERR, "%s", gai_strerror(error));
+ mcleanup(0);
+ }
+
+ /* Count max number of sockets we may open */
+ for (maxs = 0, r = res; r; r = r->ai_next, maxs++)
+ ;
+ socks = malloc((maxs + 1) * sizeof(int));
+ if (!socks) {
+ syslog(LOG_ERR, "couldn't allocate memory for sockets");
+ mcleanup(0);
+ }
+
+ *socks = 0; /* num of sockets counter at start of array */
+ s = socks + 1;
+ for (r = res; r; r = r->ai_next) {
+ *s = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
+ if (*s < 0) {
+ syslog(LOG_DEBUG, "socket(): %m");
+ continue;
+ }
+ if (setsockopt(*s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))
+ < 0) {
+ syslog(LOG_ERR, "setsockopt(SO_REUSEADDR): %m");
+ close(*s);
+ continue;
+ }
+ if (debuglvl)
+ if (setsockopt(*s, SOL_SOCKET, SO_DEBUG, &debuglvl,
+ sizeof(debuglvl)) < 0) {
+ syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
+ close(*s);
+ continue;
+ }
+ if (r->ai_family == AF_INET6) {
+ if (setsockopt(*s, IPPROTO_IPV6, IPV6_V6ONLY,
+ &on, sizeof(on)) < 0) {
+ syslog(LOG_ERR,
+ "setsockopt (IPV6_V6ONLY): %m");
+ close(*s);
+ continue;
+ }
+ }
+ if (bind(*s, r->ai_addr, r->ai_addrlen) < 0) {
+ syslog(LOG_DEBUG, "bind(): %m");
+ close(*s);
+ continue;
+ }
+ (*socks)++;
+ s++;
+ }
+
+ if (res)
+ freeaddrinfo(res);
+
+ if (*socks == 0) {
+ syslog(LOG_ERR, "Couldn't bind to any socket");
+ free(socks);
+ mcleanup(0);
+ }
+ return(socks);
+}
+
+static void
+usage(void)
+{
+#ifdef INET6
+ fprintf(stderr, "usage: lpd [-cdlsW46] [port#]\n");
+#else
+ fprintf(stderr, "usage: lpd [-cdlsW] [port#]\n");
+#endif
+ exit(EX_USAGE);
+}
diff --git a/usr.sbin/lpr/lpd/lpdchar.c b/usr.sbin/lpr/lpd/lpdchar.c
new file mode 100644
index 0000000..5ebaabd
--- /dev/null
+++ b/usr.sbin/lpr/lpd/lpdchar.c
@@ -0,0 +1,1072 @@
+/*
+ * 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.
+ */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)lpdchar.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+#endif
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+__FBSDID("$FreeBSD$");
+
+/*
+ * Character set for line printer daemon
+ */
+#include "lp.local.h"
+#include "extern.h"
+
+#define c_______ 0
+#define c______1 01
+#define c_____1_ 02
+#define c____1__ 04
+#define c____11_ 06
+#define c___1___ 010
+#define c___1__1 011
+#define c___1_1_ 012
+#define c___11__ 014
+#define c__1____ 020
+#define c__1__1_ 022
+#define c__1_1__ 024
+#define c__11___ 030
+#define c__111__ 034
+#define c__111_1 035
+#define c__1111_ 036
+#define c__11111 037
+#define c_1_____ 040
+#define c_1____1 041
+#define c_1___1_ 042
+#define c_1__1__ 044
+#define c_1_1___ 050
+#define c_1_1__1 051
+#define c_1_1_1_ 052
+#define c_11____ 060
+#define c_11_11_ 066
+#define c_111___ 070
+#define c_111__1 071
+#define c_111_1_ 072
+#define c_1111__ 074
+#define c_1111_1 075
+#define c_11111_ 076
+#define c_111111 077
+#define c1______ 0100
+#define c1_____1 0101
+#define c1____1_ 0102
+#define c1____11 0103
+#define c1___1__ 0104
+#define c1___1_1 0105
+#define c1___11_ 0106
+#define c1__1___ 0110
+#define c1__1__1 0111
+#define c1__11_1 0115
+#define c1__1111 0117
+#define c1_1____ 0120
+#define c1_1___1 0121
+#define c1_1_1_1 0125
+#define c1_1_11_ 0126
+#define c1_111__ 0134
+#define c1_1111_ 0136
+#define c11____1 0141
+#define c11___1_ 0142
+#define c11___11 0143
+#define c11_1___ 0150
+#define c11_1__1 0151
+#define c111_11_ 0166
+#define c1111___ 0170
+#define c11111__ 0174
+#define c111111_ 0176
+#define c1111111 0177
+
+char scnkey[][HEIGHT] = /* this is relatively easy to modify */
+ /* just look: */
+{
+ { c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______ }, /* */
+
+ { c__11___,
+ c__11___,
+ c__11___,
+ c__11___,
+ c__11___,
+ c_______,
+ c_______,
+ c__11___,
+ c__11___ }, /* ! */
+
+ { c_1__1__,
+ c_1__1__,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______ }, /* " */
+
+ { c_______,
+ c__1_1__,
+ c__1_1__,
+ c1111111,
+ c__1_1__,
+ c1111111,
+ c__1_1__,
+ c__1_1__,
+ c_______ }, /* # */
+
+ { c___1___,
+ c_11111_,
+ c1__1__1,
+ c1__1___,
+ c_11111_,
+ c___1__1,
+ c1__1__1,
+ c_11111_,
+ c___1___ }, /* $ */
+
+ { c_1_____,
+ c1_1___1,
+ c_1___1_,
+ c____1__,
+ c___1___,
+ c__1____,
+ c_1___1_,
+ c1___1_1,
+ c_____1_ }, /* % */
+
+ { c_11____,
+ c1__1___,
+ c1___1__,
+ c_1_1___,
+ c__1____,
+ c_1_1__1,
+ c1___11_,
+ c1___11_,
+ c_111__1 }, /* & */
+
+ { c___11__,
+ c___11__,
+ c___1___,
+ c__1____,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______ }, /* ' */
+
+ { c____1__,
+ c___1___,
+ c__1____,
+ c__1____,
+ c__1____,
+ c__1____,
+ c__1____,
+ c___1___,
+ c____1__ }, /* ( */
+
+ { c__1____,
+ c___1___,
+ c____1__,
+ c____1__,
+ c____1__,
+ c____1__,
+ c____1__,
+ c___1___,
+ c__1____ }, /* ) */
+
+ { c_______,
+ c___1___,
+ c1__1__1,
+ c_1_1_1_,
+ c__111__,
+ c_1_1_1_,
+ c1__1__1,
+ c___1___,
+ c_______ }, /* * */
+
+ { c_______,
+ c___1___,
+ c___1___,
+ c___1___,
+ c1111111,
+ c___1___,
+ c___1___,
+ c___1___,
+ c_______ }, /* + */
+
+ { c_______,
+ c_______,
+ c_______,
+ c_______,
+ c__11___,
+ c__11___,
+ c__1____,
+ c_1_____,
+ c_______ }, /* , */
+
+ { c_______,
+ c_______,
+ c_______,
+ c_______,
+ c1111111,
+ c_______,
+ c_______,
+ c_______,
+ c_______ }, /* - */
+
+ { c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c__11___,
+ c__11___ }, /* . */
+
+ { c_______,
+ c______1,
+ c_____1_,
+ c____1__,
+ c___1___,
+ c__1____,
+ c_1_____,
+ c1______,
+ c_______ }, /* / */
+
+ { c_11111_,
+ c1_____1,
+ c1____11,
+ c1___1_1,
+ c1__1__1,
+ c1_1___1,
+ c11____1,
+ c1_____1,
+ c_11111_ }, /* 0 */
+
+ { c___1___,
+ c__11___,
+ c_1_1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c_11111_ }, /* 1 */
+
+ { c_11111_,
+ c1_____1,
+ c______1,
+ c_____1_,
+ c__111__,
+ c_1_____,
+ c1______,
+ c1______,
+ c1111111 }, /* 2 */
+
+ { c_11111_,
+ c1_____1,
+ c______1,
+ c______1,
+ c__1111_,
+ c______1,
+ c______1,
+ c1_____1,
+ c_11111_ }, /* 3 */
+
+ { c_____1_,
+ c____11_,
+ c___1_1_,
+ c__1__1_,
+ c_1___1_,
+ c1____1_,
+ c1111111,
+ c_____1_,
+ c_____1_ }, /* 4 */
+
+ { c1111111,
+ c1______,
+ c1______,
+ c11111__,
+ c_____1_,
+ c______1,
+ c______1,
+ c1____1_,
+ c_1111__ }, /* 5 */
+
+ { c__1111_,
+ c_1_____,
+ c1______,
+ c1______,
+ c1_1111_,
+ c11____1,
+ c1_____1,
+ c1_____1,
+ c_11111_ }, /* 6 */
+
+ { c1111111,
+ c1_____1,
+ c_____1_,
+ c____1__,
+ c___1___,
+ c__1____,
+ c__1____,
+ c__1____,
+ c__1____ }, /* 7 */
+
+ { c_11111_,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c_11111_,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c_11111_ }, /* 8 */
+
+ { c_11111_,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c_111111,
+ c______1,
+ c______1,
+ c1_____1,
+ c_1111__ }, /* 9 */
+
+ { c_______,
+ c_______,
+ c_______,
+ c__11___,
+ c__11___,
+ c_______,
+ c_______,
+ c__11___,
+ c__11___ }, /* : */
+
+
+ { c__11___,
+ c__11___,
+ c_______,
+ c_______,
+ c__11___,
+ c__11___,
+ c__1____,
+ c_1_____,
+ c_______ }, /* ; */
+
+ { c____1__,
+ c___1___,
+ c__1____,
+ c_1_____,
+ c1______,
+ c_1_____,
+ c__1____,
+ c___1___,
+ c____1__ }, /* < */
+
+ { c_______,
+ c_______,
+ c_______,
+ c1111111,
+ c_______,
+ c1111111,
+ c_______,
+ c_______,
+ c_______ }, /* = */
+
+ { c__1____,
+ c___1___,
+ c____1__,
+ c_____1_,
+ c______1,
+ c_____1_,
+ c____1__,
+ c___1___,
+ c__1____ }, /* > */
+
+ { c__1111_,
+ c_1____1,
+ c_1____1,
+ c______1,
+ c____11_,
+ c___1___,
+ c___1___,
+ c_______,
+ c___1___ }, /* ? */
+
+ { c__1111_,
+ c_1____1,
+ c1__11_1,
+ c1_1_1_1,
+ c1_1_1_1,
+ c1_1111_,
+ c1______,
+ c_1____1,
+ c__1111_ }, /* @ */
+
+ { c__111__,
+ c_1___1_,
+ c1_____1,
+ c1_____1,
+ c1111111,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c1_____1 }, /* A */
+
+ { c111111_,
+ c_1____1,
+ c_1____1,
+ c_1____1,
+ c_11111_,
+ c_1____1,
+ c_1____1,
+ c_1____1,
+ c111111_ }, /* B */
+
+ { c__1111_,
+ c_1____1,
+ c1______,
+ c1______,
+ c1______,
+ c1______,
+ c1______,
+ c_1____1,
+ c__1111_ }, /* C */
+
+ { c11111__,
+ c_1___1_,
+ c_1____1,
+ c_1____1,
+ c_1____1,
+ c_1____1,
+ c_1____1,
+ c_1___1_,
+ c11111__ }, /* D */
+
+ { c1111111,
+ c1______,
+ c1______,
+ c1______,
+ c111111_,
+ c1______,
+ c1______,
+ c1______,
+ c1111111 }, /* E */
+
+ { c1111111,
+ c1______,
+ c1______,
+ c1______,
+ c111111_,
+ c1______,
+ c1______,
+ c1______,
+ c1______ }, /* F */
+
+ { c__1111_,
+ c_1____1,
+ c1______,
+ c1______,
+ c1______,
+ c1__1111,
+ c1_____1,
+ c_1____1,
+ c__1111_ }, /* G */
+
+ { c1_____1,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c1111111,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c1_____1 }, /* H */
+
+ { c_11111_,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c_11111_ }, /* I */
+
+ { c__11111,
+ c____1__,
+ c____1__,
+ c____1__,
+ c____1__,
+ c____1__,
+ c____1__,
+ c1___1__,
+ c_111___ }, /* J */
+
+ { c1_____1,
+ c1____1_,
+ c1___1__,
+ c1__1___,
+ c1_1____,
+ c11_1___,
+ c1___1__,
+ c1____1_,
+ c1_____1 }, /* K */
+
+ { c1______,
+ c1______,
+ c1______,
+ c1______,
+ c1______,
+ c1______,
+ c1______,
+ c1______,
+ c1111111 }, /* L */
+
+ { c1_____1,
+ c11___11,
+ c1_1_1_1,
+ c1__1__1,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c1_____1 }, /* M */
+
+ { c1_____1,
+ c11____1,
+ c1_1___1,
+ c1__1__1,
+ c1___1_1,
+ c1____11,
+ c1_____1,
+ c1_____1,
+ c1_____1 }, /* N */
+
+ { c__111__,
+ c_1___1_,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c_1___1_,
+ c__111__ }, /* O */
+
+ { c111111_,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c111111_,
+ c1______,
+ c1______,
+ c1______,
+ c1______ }, /* P */
+
+ { c__111__,
+ c_1___1_,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c1__1__1,
+ c1___1_1,
+ c_1___1_,
+ c__111_1 }, /* Q */
+
+ { c111111_,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c111111_,
+ c1__1___,
+ c1___1__,
+ c1____1_,
+ c1_____1 }, /* R */
+
+ { c_11111_,
+ c1_____1,
+ c1______,
+ c1______,
+ c_11111_,
+ c______1,
+ c______1,
+ c1_____1,
+ c_11111_ }, /* S */
+
+ { c1111111,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___ }, /* T */
+
+ { c1_____1,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c_11111_ }, /* U */
+
+ { c1_____1,
+ c1_____1,
+ c1_____1,
+ c_1___1_,
+ c_1___1_,
+ c__1_1__,
+ c__1_1__,
+ c___1___,
+ c___1___ }, /* V */
+
+ { c1_____1,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c1__1__1,
+ c1__1__1,
+ c1_1_1_1,
+ c11___11,
+ c1_____1 }, /* W */
+
+ { c1_____1,
+ c1_____1,
+ c_1___1_,
+ c__1_1__,
+ c___1___,
+ c__1_1__,
+ c_1___1_,
+ c1_____1,
+ c1_____1 }, /* X */
+
+ { c1_____1,
+ c1_____1,
+ c_1___1_,
+ c__1_1__,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___ }, /* Y */
+
+ { c1111111,
+ c______1,
+ c_____1_,
+ c____1__,
+ c___1___,
+ c__1____,
+ c_1_____,
+ c1______,
+ c1111111 }, /* Z */
+
+ { c_1111__,
+ c_1_____,
+ c_1_____,
+ c_1_____,
+ c_1_____,
+ c_1_____,
+ c_1_____,
+ c_1_____,
+ c_1111__ }, /* [ */
+
+ { c_______,
+ c1______,
+ c_1_____,
+ c__1____,
+ c___1___,
+ c____1__,
+ c_____1_,
+ c______1,
+ c_______ }, /* \ */
+
+ { c__1111_,
+ c_____1_,
+ c_____1_,
+ c_____1_,
+ c_____1_,
+ c_____1_,
+ c_____1_,
+ c_____1_,
+ c__1111_ }, /* ] */
+
+ { c___1___,
+ c__1_1__,
+ c_1___1_,
+ c1_____1,
+ c_______,
+ c_______,
+ c_______,
+ c_______ }, /* ^ */
+
+ { c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c1111111,
+ c_______ }, /* _ */
+
+ { c__11___,
+ c__11___,
+ c___1___,
+ c____1__,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______ }, /* ` */
+
+ { c_______,
+ c_______,
+ c_______,
+ c_1111__,
+ c_____1_,
+ c_11111_,
+ c1_____1,
+ c1____11,
+ c_1111_1 }, /* a */
+
+ { c1______,
+ c1______,
+ c1______,
+ c1_111__,
+ c11___1_,
+ c1_____1,
+ c1_____1,
+ c11___1_,
+ c1_111__ }, /* b */
+
+ { c_______,
+ c_______,
+ c_______,
+ c_1111__,
+ c1____1_,
+ c1______,
+ c1______,
+ c1____1_,
+ c_1111__ }, /* c */
+
+ { c_____1_,
+ c_____1_,
+ c_____1_,
+ c_111_1_,
+ c1___11_,
+ c1____1_,
+ c1____1_,
+ c1___11_,
+ c_111_1_ }, /* d */
+
+ { c_______,
+ c_______,
+ c_______,
+ c_1111__,
+ c1____1_,
+ c111111_,
+ c1______,
+ c1____1_,
+ c_1111__ }, /* e */
+
+ { c___11__,
+ c__1__1_,
+ c__1____,
+ c__1____,
+ c11111__,
+ c__1____,
+ c__1____,
+ c__1____,
+ c__1____ }, /* f */
+
+ { c_111_1_,
+ c1___11_,
+ c1____1_,
+ c1____1_,
+ c1___11_,
+ c_111_1_,
+ c_____1_,
+ c1____1_,
+ c_1111__ }, /* g */
+
+ { c1______,
+ c1______,
+ c1______,
+ c1_111__,
+ c11___1_,
+ c1____1_,
+ c1____1_,
+ c1____1_,
+ c1____1_ }, /* h */
+
+ { c_______,
+ c___1___,
+ c_______,
+ c__11___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c__111__ }, /* i */
+
+ { c____11_,
+ c_____1_,
+ c_____1_,
+ c_____1_,
+ c_____1_,
+ c_____1_,
+ c_____1_,
+ c_1___1_,
+ c__111__ }, /* j */
+
+ { c1______,
+ c1______,
+ c1______,
+ c1___1__,
+ c1__1___,
+ c1_1____,
+ c11_1___,
+ c1___1__,
+ c1____1_ }, /* k */
+
+ { c__11___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c__111__ }, /* l */
+
+ { c_______,
+ c_______,
+ c_______,
+ c1_1_11_,
+ c11_1__1,
+ c1__1__1,
+ c1__1__1,
+ c1__1__1,
+ c1__1__1 }, /* m */
+
+ { c_______,
+ c_______,
+ c_______,
+ c1_111__,
+ c11___1_,
+ c1____1_,
+ c1____1_,
+ c1____1_,
+ c1____1_ }, /* n */
+
+ { c_______,
+ c_______,
+ c_______,
+ c_1111__,
+ c1____1_,
+ c1____1_,
+ c1____1_,
+ c1____1_,
+ c_1111__ }, /* o */
+
+ { c1_111__,
+ c11___1_,
+ c1____1_,
+ c1____1_,
+ c11___1_,
+ c1_111__,
+ c1______,
+ c1______,
+ c1______ }, /* p */
+
+ { c_111_1_,
+ c1___11_,
+ c1____1_,
+ c1____1_,
+ c1___11_,
+ c_111_1_,
+ c_____1_,
+ c_____1_,
+ c_____1_ }, /* q */
+
+ { c_______,
+ c_______,
+ c_______,
+ c1_111__,
+ c11___1_,
+ c1______,
+ c1______,
+ c1______,
+ c1______ }, /* r */
+
+ { c_______,
+ c_______,
+ c_______,
+ c_1111__,
+ c1____1_,
+ c_11____,
+ c___11__,
+ c1____1_,
+ c_1111__ }, /* s */
+
+ { c_______,
+ c__1____,
+ c__1____,
+ c11111__,
+ c__1____,
+ c__1____,
+ c__1____,
+ c__1__1_,
+ c___11__ }, /* t */
+
+ { c_______,
+ c_______,
+ c_______,
+ c1____1_,
+ c1____1_,
+ c1____1_,
+ c1____1_,
+ c1___11_,
+ c_111_1_ }, /* u */
+
+ { c_______,
+ c_______,
+ c_______,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c_1___1_,
+ c__1_1__,
+ c___1___ }, /* v */
+
+ { c_______,
+ c_______,
+ c_______,
+ c1_____1,
+ c1__1__1,
+ c1__1__1,
+ c1__1__1,
+ c1__1__1,
+ c_11_11_ }, /* w */
+
+ { c_______,
+ c_______,
+ c_______,
+ c1____1_,
+ c_1__1__,
+ c__11___,
+ c__11___,
+ c_1__1__,
+ c1____1_ }, /* x */
+
+ { c1____1_,
+ c1____1_,
+ c1____1_,
+ c1____1_,
+ c1___11_,
+ c_111_1_,
+ c_____1_,
+ c1____1_,
+ c_1111__ }, /* y */
+
+ { c_______,
+ c_______,
+ c_______,
+ c111111_,
+ c____1__,
+ c___1___,
+ c__1____,
+ c_1_____,
+ c111111_ }, /* z */
+
+ { c___11__,
+ c__1____,
+ c__1____,
+ c__1____,
+ c_1_____,
+ c__1____,
+ c__1____,
+ c__1____,
+ c___11__ }, /* } */
+
+ { c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___ }, /* | */
+
+ { c__11___,
+ c____1__,
+ c____1__,
+ c____1__,
+ c_____1_,
+ c____1__,
+ c____1__,
+ c____1__,
+ c__11___ }, /* } */
+
+ { c_11____,
+ c1__1__1,
+ c____11_,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______ }, /* ~ */
+
+ { c_1__1__,
+ c1__1__1,
+ c__1__1_,
+ c_1__1__,
+ c1__1__1,
+ c__1__1_,
+ c_1__1__,
+ c1__1__1,
+ c__1__1_ } /* rub-out */
+};
diff --git a/usr.sbin/lpr/lpd/modes.c b/usr.sbin/lpr/lpd/modes.c
new file mode 100644
index 0000000..8a05212
--- /dev/null
+++ b/usr.sbin/lpr/lpd/modes.c
@@ -0,0 +1,235 @@
+/*-
+ * Copyright (c) 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.
+ * 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 char sccsid[] = "@(#)modes.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+#endif
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+__FBSDID("$FreeBSD$");
+
+#include <stddef.h>
+#include <string.h>
+#include <termios.h>
+#include "lp.local.h"
+#include "extern.h"
+
+struct modes {
+ const char *name;
+ const long set;
+ const long unset;
+};
+
+/*
+ * The code in optlist() depends on minus options following regular
+ * options, i.e. "foo" must immediately precede "-foo".
+ */
+struct modes cmodes[] = {
+ { "cs5", CS5, CSIZE },
+ { "cs6", CS6, CSIZE },
+ { "cs7", CS7, CSIZE },
+ { "cs8", CS8, CSIZE },
+ { "cstopb", CSTOPB, 0 },
+ { "-cstopb", 0, CSTOPB },
+ { "cread", CREAD, 0 },
+ { "-cread", 0, CREAD },
+ { "parenb", PARENB, 0 },
+ { "-parenb", 0, PARENB },
+ { "parodd", PARODD, 0 },
+ { "-parodd", 0, PARODD },
+ { "parity", PARENB | CS7, PARODD | CSIZE },
+ { "-parity", CS8, PARODD | PARENB | CSIZE },
+ { "evenp", PARENB | CS7, PARODD | CSIZE },
+ { "-evenp", CS8, PARODD | PARENB | CSIZE },
+ { "oddp", PARENB | CS7 | PARODD, CSIZE },
+ { "-oddp", CS8, PARODD | PARENB | CSIZE },
+ { "pass8", CS8, PARODD | PARENB | CSIZE },
+ { "-pass8", PARENB | CS7, PARODD | CSIZE },
+ { "hupcl", HUPCL, 0 },
+ { "-hupcl", 0, HUPCL },
+ { "hup", HUPCL, 0 },
+ { "-hup", 0, HUPCL },
+ { "clocal", CLOCAL, 0 },
+ { "-clocal", 0, CLOCAL },
+ { "crtscts", CRTSCTS, 0 },
+ { "-crtscts", 0, CRTSCTS },
+ { "ctsflow", CCTS_OFLOW, 0 },
+ { "-ctsflow", 0, CCTS_OFLOW },
+ { "dsrflow", CDSR_OFLOW, 0 },
+ { "-dsrflow", 0, CDSR_OFLOW },
+ { "dtrflow", CDTR_IFLOW, 0 },
+ { "-dtrflow", 0, CDTR_IFLOW },
+ { "rtsflow", CRTS_IFLOW, 0 },
+ { "-rtsflow", 0, CRTS_IFLOW },
+ { "mdmbuf", MDMBUF, 0 },
+ { "-mdmbuf", 0, MDMBUF },
+ { NULL, 0, 0},
+};
+
+struct modes imodes[] = {
+ { "ignbrk", IGNBRK, 0 },
+ { "-ignbrk", 0, IGNBRK },
+ { "brkint", BRKINT, 0 },
+ { "-brkint", 0, BRKINT },
+ { "ignpar", IGNPAR, 0 },
+ { "-ignpar", 0, IGNPAR },
+ { "parmrk", PARMRK, 0 },
+ { "-parmrk", 0, PARMRK },
+ { "inpck", INPCK, 0 },
+ { "-inpck", 0, INPCK },
+ { "istrip", ISTRIP, 0 },
+ { "-istrip", 0, ISTRIP },
+ { "inlcr", INLCR, 0 },
+ { "-inlcr", 0, INLCR },
+ { "igncr", IGNCR, 0 },
+ { "-igncr", 0, IGNCR },
+ { "icrnl", ICRNL, 0 },
+ { "-icrnl", 0, ICRNL },
+ { "ixon", IXON, 0 },
+ { "-ixon", 0, IXON },
+ { "flow", IXON, 0 },
+ { "-flow", 0, IXON },
+ { "ixoff", IXOFF, 0 },
+ { "-ixoff", 0, IXOFF },
+ { "tandem", IXOFF, 0 },
+ { "-tandem", 0, IXOFF },
+ { "ixany", IXANY, 0 },
+ { "-ixany", 0, IXANY },
+ { "decctlq", 0, IXANY },
+ { "-decctlq", IXANY, 0 },
+ { "imaxbel", IMAXBEL, 0 },
+ { "-imaxbel", 0, IMAXBEL },
+ { NULL, 0, 0},
+};
+
+struct modes lmodes[] = {
+ { "echo", ECHO, 0 },
+ { "-echo", 0, ECHO },
+ { "echoe", ECHOE, 0 },
+ { "-echoe", 0, ECHOE },
+ { "crterase", ECHOE, 0 },
+ { "-crterase", 0, ECHOE },
+ { "crtbs", ECHOE, 0 }, /* crtbs not supported, close enough */
+ { "-crtbs", 0, ECHOE },
+ { "echok", ECHOK, 0 },
+ { "-echok", 0, ECHOK },
+ { "echoke", ECHOKE, 0 },
+ { "-echoke", 0, ECHOKE },
+ { "crtkill", ECHOKE, 0 },
+ { "-crtkill", 0, ECHOKE },
+ { "altwerase", ALTWERASE, 0 },
+ { "-altwerase", 0, ALTWERASE },
+ { "iexten", IEXTEN, 0 },
+ { "-iexten", 0, IEXTEN },
+ { "echonl", ECHONL, 0 },
+ { "-echonl", 0, ECHONL },
+ { "echoctl", ECHOCTL, 0 },
+ { "-echoctl", 0, ECHOCTL },
+ { "ctlecho", ECHOCTL, 0 },
+ { "-ctlecho", 0, ECHOCTL },
+ { "echoprt", ECHOPRT, 0 },
+ { "-echoprt", 0, ECHOPRT },
+ { "prterase", ECHOPRT, 0 },
+ { "-prterase", 0, ECHOPRT },
+ { "isig", ISIG, 0 },
+ { "-isig", 0, ISIG },
+ { "icanon", ICANON, 0 },
+ { "-icanon", 0, ICANON },
+ { "noflsh", NOFLSH, 0 },
+ { "-noflsh", 0, NOFLSH },
+ { "tostop", TOSTOP, 0 },
+ { "-tostop", 0, TOSTOP },
+ { "flusho", FLUSHO, 0 },
+ { "-flusho", 0, FLUSHO },
+ { "pendin", PENDIN, 0 },
+ { "-pendin", 0, PENDIN },
+ { "crt", ECHOE|ECHOKE|ECHOCTL, ECHOK|ECHOPRT },
+ { "-crt", ECHOK, ECHOE|ECHOKE|ECHOCTL },
+ { "newcrt", ECHOE|ECHOKE|ECHOCTL, ECHOK|ECHOPRT },
+ { "-newcrt", ECHOK, ECHOE|ECHOKE|ECHOCTL },
+ { "nokerninfo", NOKERNINFO, 0 },
+ { "-nokerninfo",0, NOKERNINFO },
+ { "kerninfo", 0, NOKERNINFO },
+ { "-kerninfo", NOKERNINFO, 0 },
+ { NULL, 0, 0},
+};
+
+struct modes omodes[] = {
+ { "opost", OPOST, 0 },
+ { "-opost", 0, OPOST },
+ { "litout", 0, OPOST },
+ { "-litout", OPOST, 0 },
+ { "onlcr", ONLCR, 0 },
+ { "-onlcr", 0, ONLCR },
+ { "tabs", 0, OXTABS }, /* "preserve" tabs */
+ { "-tabs", OXTABS, 0 },
+ { "oxtabs", OXTABS, 0 },
+ { "-oxtabs", 0, OXTABS },
+ { NULL, 0, 0},
+};
+
+#define CHK(name, s) (*name == s[0] && !strcmp(name, s))
+
+int
+msearch(char *str, struct termios *ip)
+{
+ struct modes *mp;
+
+ for (mp = cmodes; mp->name; ++mp)
+ if (CHK(str, mp->name)) {
+ ip->c_cflag &= ~mp->unset;
+ ip->c_cflag |= mp->set;
+ return (1);
+ }
+ for (mp = imodes; mp->name; ++mp)
+ if (CHK(str, mp->name)) {
+ ip->c_iflag &= ~mp->unset;
+ ip->c_iflag |= mp->set;
+ return (1);
+ }
+ for (mp = lmodes; mp->name; ++mp)
+ if (CHK(str, mp->name)) {
+ ip->c_lflag &= ~mp->unset;
+ ip->c_lflag |= mp->set;
+ return (1);
+ }
+ for (mp = omodes; mp->name; ++mp)
+ if (CHK(str, mp->name)) {
+ ip->c_oflag &= ~mp->unset;
+ ip->c_oflag |= mp->set;
+ return (1);
+ }
+ return (0);
+}
diff --git a/usr.sbin/lpr/lpd/printjob.c b/usr.sbin/lpr/lpd/printjob.c
new file mode 100644
index 0000000..58efe6d
--- /dev/null
+++ b/usr.sbin/lpr/lpd/printjob.c
@@ -0,0 +1,2005 @@
+/*
+ * 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.
+ */
+
+#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 */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)printjob.c 8.7 (Berkeley) 5/10/95";
+#endif /* not lint */
+#endif
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+__FBSDID("$FreeBSD$");
+
+/*
+ * printjob -- print jobs in the queue.
+ *
+ * NOTE: the lock file is used to pass information to lpq and lprm.
+ * it does not need to be removed because file locks are dynamic.
+ */
+
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <pwd.h>
+#include <unistd.h>
+#include <signal.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <time.h>
+#include "lp.h"
+#include "lp.local.h"
+#include "pathnames.h"
+#include "extern.h"
+
+#define DORETURN 0 /* dofork should return "can't fork" error */
+#define DOABORT 1 /* dofork should just die if fork() fails */
+
+/*
+ * The buffer size to use when reading/writing spool files.
+ */
+#define SPL_BUFSIZ BUFSIZ
+
+/*
+ * Error tokens
+ */
+#define REPRINT -2
+#define ERROR -1
+#define OK 0
+#define FATALERR 1
+#define NOACCT 2
+#define FILTERERR 3
+#define ACCESS 4
+
+static dev_t fdev; /* device of file pointed to by symlink */
+static ino_t fino; /* inode of file pointed to by symlink */
+static FILE *cfp; /* control file */
+static pid_t of_pid; /* process id of output filter, if any */
+static int child; /* id of any filters */
+static int job_dfcnt; /* count of datafiles in current user job */
+static int lfd; /* lock file descriptor */
+static int ofd; /* output filter file descriptor */
+static int tfd = -1; /* output filter temp file output */
+static int pfd; /* prstatic inter file descriptor */
+static int prchild; /* id of pr process */
+static char title[80]; /* ``pr'' title */
+static char locale[80]; /* ``pr'' locale */
+
+/* these two are set from pp->daemon_user, but only if they are needed */
+static char *daemon_uname; /* set from pwd->pw_name */
+static int daemon_defgid;
+
+static char class[32]; /* classification field */
+static char origin_host[MAXHOSTNAMELEN]; /* user's host machine */
+ /* indentation size in static characters */
+static char indent[10] = "-i0";
+static char jobname[100]; /* job or file name */
+static char length[10] = "-l"; /* page length in lines */
+static char logname[32]; /* user's login name */
+static char pxlength[10] = "-y"; /* page length in pixels */
+static char pxwidth[10] = "-x"; /* page width in pixels */
+/* tempstderr is the filename used to catch stderr from exec-ing filters */
+static char tempstderr[] = "errs.XXXXXXX";
+static char width[10] = "-w"; /* page width in static characters */
+#define TFILENAME "fltXXXXXX"
+static char tfile[] = TFILENAME; /* file name for filter output */
+
+static void abortpr(int _signo);
+static void alarmhandler(int _signo);
+static void banner(struct printer *_pp, char *_name1, char *_name2);
+static int dofork(const struct printer *_pp, int _action);
+static int dropit(int _c);
+static int execfilter(struct printer *_pp, char *_f_cmd, char **_f_av,
+ int _infd, int _outfd);
+static void init(struct printer *_pp);
+static void openpr(const struct printer *_pp);
+static void opennet(const struct printer *_pp);
+static void opentty(const struct printer *_pp);
+static void openrem(const struct printer *pp);
+static int print(struct printer *_pp, int _format, char *_file);
+static int printit(struct printer *_pp, char *_file);
+static void pstatus(const struct printer *_pp, const char *_msg, ...)
+ __printflike(2, 3);
+static char response(const struct printer *_pp);
+static void scan_out(struct printer *_pp, int _scfd, char *_scsp,
+ int _dlm);
+static char *scnline(int _key, char *_p, int _c);
+static int sendfile(struct printer *_pp, int _type, char *_file,
+ char _format, int _copyreq);
+static int sendit(struct printer *_pp, char *_file);
+static void sendmail(struct printer *_pp, char *_userid, int _bombed);
+static void setty(const struct printer *_pp);
+static void wait4data(struct printer *_pp, const char *_dfile);
+
+void
+printjob(struct printer *pp)
+{
+ struct stat stb;
+ register struct jobqueue *q, **qp;
+ struct jobqueue **queue;
+ register int i, nitems;
+ off_t pidoff;
+ pid_t printpid;
+ int errcnt, jobcount, statok, tempfd;
+
+ jobcount = 0;
+ init(pp); /* set up capabilities */
+ (void) write(STDOUT_FILENO, "", 1); /* ack that daemon is started */
+ (void) close(STDERR_FILENO); /* set up log file */
+ if (open(pp->log_file, O_WRONLY|O_APPEND, LOG_FILE_MODE) < 0) {
+ syslog(LOG_ERR, "%s: open(%s): %m", pp->printer,
+ pp->log_file);
+ (void) open(_PATH_DEVNULL, O_WRONLY);
+ }
+ setgid(getegid());
+ printpid = getpid(); /* for use with lprm */
+ setpgrp(0, printpid);
+
+ /*
+ * At initial lpd startup, printjob may be called with various
+ * signal handlers in effect. After that initial startup, any
+ * calls to printjob will have a *different* set of signal-handlers
+ * in effect. Make sure all handlers are the ones we want.
+ */
+ signal(SIGCHLD, SIG_DFL);
+ signal(SIGHUP, abortpr);
+ signal(SIGINT, abortpr);
+ signal(SIGQUIT, abortpr);
+ signal(SIGTERM, abortpr);
+
+ /*
+ * uses short form file names
+ */
+ if (chdir(pp->spool_dir) < 0) {
+ syslog(LOG_ERR, "%s: chdir(%s): %m", pp->printer,
+ pp->spool_dir);
+ exit(1);
+ }
+ statok = stat(pp->lock_file, &stb);
+ if (statok == 0 && (stb.st_mode & LFM_PRINT_DIS))
+ exit(0); /* printing disabled */
+ umask(S_IWOTH);
+ lfd = open(pp->lock_file, O_WRONLY|O_CREAT|O_EXLOCK|O_NONBLOCK,
+ LOCK_FILE_MODE);
+ if (lfd < 0) {
+ if (errno == EWOULDBLOCK) /* active daemon present */
+ exit(0);
+ syslog(LOG_ERR, "%s: open(%s): %m", pp->printer,
+ pp->lock_file);
+ exit(1);
+ }
+ /*
+ * If the initial call to stat() failed, then lock_file will have
+ * been created by open(). Update &stb to match that new file.
+ */
+ if (statok != 0)
+ statok = stat(pp->lock_file, &stb);
+ /* turn off non-blocking mode (was turned on for lock effects only) */
+ if (fcntl(lfd, F_SETFL, 0) < 0) {
+ syslog(LOG_ERR, "%s: fcntl(%s): %m", pp->printer,
+ pp->lock_file);
+ exit(1);
+ }
+ ftruncate(lfd, 0);
+ /*
+ * write process id for others to know
+ */
+ sprintf(line, "%u\n", printpid);
+ pidoff = i = strlen(line);
+ if (write(lfd, line, i) != i) {
+ syslog(LOG_ERR, "%s: write(%s): %m", pp->printer,
+ pp->lock_file);
+ exit(1);
+ }
+ /*
+ * search the spool directory for work and sort by queue order.
+ */
+ if ((nitems = getq(pp, &queue)) < 0) {
+ syslog(LOG_ERR, "%s: can't scan %s", pp->printer,
+ pp->spool_dir);
+ exit(1);
+ }
+ if (nitems == 0) /* no work to do */
+ exit(0);
+ if (stb.st_mode & LFM_RESET_QUE) { /* reset queue flag */
+ if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE) < 0)
+ syslog(LOG_ERR, "%s: fchmod(%s): %m", pp->printer,
+ pp->lock_file);
+ }
+
+ /* create a file which will be used to hold stderr from filters */
+ if ((tempfd = mkstemp(tempstderr)) == -1) {
+ syslog(LOG_ERR, "%s: mkstemp(%s): %m", pp->printer,
+ tempstderr);
+ exit(1);
+ }
+ if ((i = fchmod(tempfd, 0664)) == -1) {
+ syslog(LOG_ERR, "%s: fchmod(%s): %m", pp->printer,
+ tempstderr);
+ exit(1);
+ }
+ /* lpd doesn't need it to be open, it just needs it to exist */
+ close(tempfd);
+
+ openpr(pp); /* open printer or remote */
+again:
+ /*
+ * we found something to do now do it --
+ * write the name of the current control file into the lock file
+ * so the spool queue program can tell what we're working on
+ */
+ for (qp = queue; nitems--; free((char *) q)) {
+ q = *qp++;
+ if (stat(q->job_cfname, &stb) < 0)
+ continue;
+ errcnt = 0;
+ restart:
+ (void) lseek(lfd, pidoff, 0);
+ (void) snprintf(line, sizeof(line), "%s\n", q->job_cfname);
+ i = strlen(line);
+ if (write(lfd, line, i) != i)
+ syslog(LOG_ERR, "%s: write(%s): %m", pp->printer,
+ pp->lock_file);
+ if (!pp->remote)
+ i = printit(pp, q->job_cfname);
+ else
+ i = sendit(pp, q->job_cfname);
+ /*
+ * Check to see if we are supposed to stop printing or
+ * if we are to rebuild the queue.
+ */
+ if (fstat(lfd, &stb) == 0) {
+ /* stop printing before starting next job? */
+ if (stb.st_mode & LFM_PRINT_DIS)
+ goto done;
+ /* rebuild queue (after lpc topq) */
+ if (stb.st_mode & LFM_RESET_QUE) {
+ for (free(q); nitems--; free(q))
+ q = *qp++;
+ if (fchmod(lfd, stb.st_mode & ~LFM_RESET_QUE)
+ < 0)
+ syslog(LOG_WARNING,
+ "%s: fchmod(%s): %m",
+ pp->printer, pp->lock_file);
+ break;
+ }
+ }
+ if (i == OK) /* all files of this job printed */
+ jobcount++;
+ else if (i == REPRINT && ++errcnt < 5) {
+ /* try reprinting the job */
+ syslog(LOG_INFO, "restarting %s", pp->printer);
+ if (of_pid > 0) {
+ kill(of_pid, SIGCONT); /* to be sure */
+ (void) close(ofd);
+ while ((i = wait(NULL)) > 0 && i != of_pid)
+ ;
+ if (i < 0)
+ syslog(LOG_WARNING, "%s: after kill(of=%d), wait() returned: %m",
+ pp->printer, of_pid);
+ of_pid = 0;
+ }
+ (void) close(pfd); /* close printer */
+ if (ftruncate(lfd, pidoff) < 0)
+ syslog(LOG_WARNING, "%s: ftruncate(%s): %m",
+ pp->printer, pp->lock_file);
+ openpr(pp); /* try to reopen printer */
+ goto restart;
+ } else {
+ syslog(LOG_WARNING, "%s: job could not be %s (%s)",
+ pp->printer,
+ pp->remote ? "sent to remote host" : "printed",
+ q->job_cfname);
+ if (i == REPRINT) {
+ /* ensure we don't attempt this job again */
+ (void) unlink(q->job_cfname);
+ q->job_cfname[0] = 'd';
+ (void) unlink(q->job_cfname);
+ if (logname[0])
+ sendmail(pp, logname, FATALERR);
+ }
+ }
+ }
+ free(queue);
+ /*
+ * search the spool directory for more work.
+ */
+ if ((nitems = getq(pp, &queue)) < 0) {
+ syslog(LOG_ERR, "%s: can't scan %s", pp->printer,
+ pp->spool_dir);
+ exit(1);
+ }
+ if (nitems == 0) { /* no more work to do */
+ done:
+ if (jobcount > 0) { /* jobs actually printed */
+ if (!pp->no_formfeed && !pp->tof)
+ (void) write(ofd, pp->form_feed,
+ strlen(pp->form_feed));
+ if (pp->trailer != NULL) /* output trailer */
+ (void) write(ofd, pp->trailer,
+ strlen(pp->trailer));
+ }
+ (void) close(ofd);
+ (void) wait(NULL);
+ (void) unlink(tempstderr);
+ exit(0);
+ }
+ goto again;
+}
+
+char fonts[4][50]; /* fonts for troff */
+
+char ifonts[4][40] = {
+ _PATH_VFONTR,
+ _PATH_VFONTI,
+ _PATH_VFONTB,
+ _PATH_VFONTS,
+};
+
+/*
+ * The remaining part is the reading of the control file (cf)
+ * and performing the various actions.
+ */
+static int
+printit(struct printer *pp, char *file)
+{
+ register int i;
+ char *cp;
+ int bombed, didignorehdr;
+
+ bombed = OK;
+ didignorehdr = 0;
+ /*
+ * open control file; ignore if no longer there.
+ */
+ if ((cfp = fopen(file, "r")) == NULL) {
+ syslog(LOG_INFO, "%s: fopen(%s): %m", pp->printer, file);
+ return (OK);
+ }
+ /*
+ * Reset troff fonts.
+ */
+ for (i = 0; i < 4; i++)
+ strcpy(fonts[i], ifonts[i]);
+ sprintf(&width[2], "%ld", pp->page_width);
+ strcpy(indent+2, "0");
+
+ /* initialize job-specific count of datafiles processed */
+ job_dfcnt = 0;
+
+ /*
+ * read the control file for work to do
+ *
+ * file format -- first character in the line is a command
+ * rest of the line is the argument.
+ * valid commands are:
+ *
+ * S -- "stat info" for symbolic link protection
+ * J -- "job name" on banner page
+ * C -- "class name" on banner page
+ * L -- "literal" user's name to print on banner
+ * T -- "title" for pr
+ * H -- "host name" of machine where lpr was done
+ * P -- "person" user's login name
+ * I -- "indent" amount to indent output
+ * R -- laser dpi "resolution"
+ * f -- "file name" name of text file to print
+ * l -- "file name" text file with control chars
+ * o -- "file name" postscript file, according to
+ * the RFC. Here it is treated like an 'f'.
+ * p -- "file name" text file to print with pr(1)
+ * t -- "file name" troff(1) file to print
+ * n -- "file name" ditroff(1) file to print
+ * d -- "file name" dvi file to print
+ * g -- "file name" plot(1G) file to print
+ * v -- "file name" plain raster file to print
+ * c -- "file name" cifplot file to print
+ * 1 -- "R font file" for troff
+ * 2 -- "I font file" for troff
+ * 3 -- "B font file" for troff
+ * 4 -- "S font file" for troff
+ * N -- "name" of file (used by lpq)
+ * U -- "unlink" name of file to remove
+ * (after we print it. (Pass 2 only)).
+ * M -- "mail" to user when done printing
+ * Z -- "locale" for pr
+ *
+ * getline reads a line and expands tabs to blanks
+ */
+
+ /* pass 1 */
+
+ while (getline(cfp))
+ switch (line[0]) {
+ case 'H':
+ strlcpy(origin_host, line + 1, sizeof(origin_host));
+ if (class[0] == '\0') {
+ strlcpy(class, line+1, sizeof(class));
+ }
+ continue;
+
+ case 'P':
+ strlcpy(logname, line + 1, sizeof(logname));
+ if (pp->restricted) { /* restricted */
+ if (getpwnam(logname) == NULL) {
+ bombed = NOACCT;
+ sendmail(pp, line+1, bombed);
+ goto pass2;
+ }
+ }
+ continue;
+
+ case 'S':
+ cp = line+1;
+ i = 0;
+ while (*cp >= '0' && *cp <= '9')
+ i = i * 10 + (*cp++ - '0');
+ fdev = i;
+ cp++;
+ i = 0;
+ while (*cp >= '0' && *cp <= '9')
+ i = i * 10 + (*cp++ - '0');
+ fino = i;
+ continue;
+
+ case 'J':
+ if (line[1] != '\0') {
+ strlcpy(jobname, line + 1, sizeof(jobname));
+ } else
+ strcpy(jobname, " ");
+ continue;
+
+ case 'C':
+ if (line[1] != '\0')
+ strlcpy(class, line + 1, sizeof(class));
+ else if (class[0] == '\0') {
+ /* XXX - why call gethostname instead of
+ * just strlcpy'ing local_host? */
+ gethostname(class, sizeof(class));
+ class[sizeof(class) - 1] = '\0';
+ }
+ continue;
+
+ case 'T': /* header title for pr */
+ strlcpy(title, line + 1, sizeof(title));
+ continue;
+
+ case 'L': /* identification line */
+ if (!pp->no_header && !pp->header_last)
+ banner(pp, line+1, jobname);
+ continue;
+
+ case '1': /* troff fonts */
+ case '2':
+ case '3':
+ case '4':
+ if (line[1] != '\0') {
+ strlcpy(fonts[line[0]-'1'], line + 1,
+ (size_t)50);
+ }
+ continue;
+
+ case 'W': /* page width */
+ strlcpy(width+2, line + 1, sizeof(width) - 2);
+ continue;
+
+ case 'I': /* indent amount */
+ strlcpy(indent+2, line + 1, sizeof(indent) - 2);
+ continue;
+
+ case 'Z': /* locale for pr */
+ strlcpy(locale, line + 1, sizeof(locale));
+ continue;
+
+ default: /* some file to print */
+ /* only lowercase cmd-codes include a file-to-print */
+ if ((line[0] < 'a') || (line[0] > 'z')) {
+ /* ignore any other lines */
+ if (lflag <= 1)
+ continue;
+ if (!didignorehdr) {
+ syslog(LOG_INFO, "%s: in %s :",
+ pp->printer, file);
+ didignorehdr = 1;
+ }
+ syslog(LOG_INFO, "%s: ignoring line: '%c' %s",
+ pp->printer, line[0], &line[1]);
+ continue;
+ }
+ i = print(pp, line[0], line+1);
+ switch (i) {
+ case ERROR:
+ if (bombed == OK)
+ bombed = FATALERR;
+ break;
+ case REPRINT:
+ (void) fclose(cfp);
+ return (REPRINT);
+ case FILTERERR:
+ case ACCESS:
+ bombed = i;
+ sendmail(pp, logname, bombed);
+ }
+ title[0] = '\0';
+ continue;
+
+ case 'N':
+ case 'U':
+ case 'M':
+ case 'R':
+ continue;
+ }
+
+ /* pass 2 */
+
+pass2:
+ fseek(cfp, 0L, 0);
+ while (getline(cfp))
+ switch (line[0]) {
+ case 'L': /* identification line */
+ if (!pp->no_header && pp->header_last)
+ banner(pp, line+1, jobname);
+ continue;
+
+ case 'M':
+ if (bombed < NOACCT) /* already sent if >= NOACCT */
+ sendmail(pp, line+1, bombed);
+ continue;
+
+ case 'U':
+ if (strchr(line+1, '/'))
+ continue;
+ (void) unlink(line+1);
+ }
+ /*
+ * clean-up in case another control file exists
+ */
+ (void) fclose(cfp);
+ (void) unlink(file);
+ return (bombed == OK ? OK : ERROR);
+}
+
+/*
+ * Print a file.
+ * Set up the chain [ PR [ | {IF, OF} ] ] or {IF, RF, TF, NF, DF, CF, VF}.
+ * Return -1 if a non-recoverable error occured,
+ * 2 if the filter detected some errors (but printed the job anyway),
+ * 1 if we should try to reprint this job and
+ * 0 if all is well.
+ * Note: all filters take stdin as the file, stdout as the printer,
+ * stderr as the log file, and must not ignore SIGINT.
+ */
+static int
+print(struct printer *pp, int format, char *file)
+{
+ register int n, i;
+ register char *prog;
+ int fi, fo;
+ FILE *fp;
+ char *av[15], buf[SPL_BUFSIZ];
+ pid_t wpid;
+ int p[2], retcode, stopped, wstatus, wstatus_set;
+ struct stat stb;
+
+ /* Make sure the entire data file has arrived. */
+ wait4data(pp, file);
+
+ if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0) {
+ syslog(LOG_INFO, "%s: unable to open %s ('%c' line)",
+ pp->printer, file, format);
+ return (ERROR);
+ }
+ /*
+ * Check to see if data file is a symbolic link. If so, it should
+ * still point to the same file or someone is trying to print
+ * something he shouldn't.
+ */
+ if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 &&
+ (stb.st_dev != fdev || stb.st_ino != fino))
+ return (ACCESS);
+
+ job_dfcnt++; /* increment datafile counter for this job */
+ stopped = 0; /* output filter is not stopped */
+
+ /* everything seems OK, start it up */
+ if (!pp->no_formfeed && !pp->tof) { /* start on a fresh page */
+ (void) write(ofd, pp->form_feed, strlen(pp->form_feed));
+ pp->tof = 1;
+ }
+ if (pp->filters[LPF_INPUT] == NULL
+ && (format == 'f' || format == 'l' || format == 'o')) {
+ pp->tof = 0;
+ while ((n = read(fi, buf, SPL_BUFSIZ)) > 0)
+ if (write(ofd, buf, n) != n) {
+ (void) close(fi);
+ return (REPRINT);
+ }
+ (void) close(fi);
+ return (OK);
+ }
+ switch (format) {
+ case 'p': /* print file using 'pr' */
+ if (pp->filters[LPF_INPUT] == NULL) { /* use output filter */
+ prog = _PATH_PR;
+ i = 0;
+ av[i++] = "pr";
+ av[i++] = width;
+ av[i++] = length;
+ av[i++] = "-h";
+ av[i++] = *title ? title : " ";
+ av[i++] = "-L";
+ av[i++] = *locale ? locale : "C";
+ av[i++] = "-F";
+ av[i] = 0;
+ fo = ofd;
+ goto start;
+ }
+ pipe(p);
+ if ((prchild = dofork(pp, DORETURN)) == 0) { /* child */
+ dup2(fi, STDIN_FILENO); /* file is stdin */
+ dup2(p[1], STDOUT_FILENO); /* pipe is stdout */
+ closelog();
+ closeallfds(3);
+ execl(_PATH_PR, "pr", width, length,
+ "-h", *title ? title : " ",
+ "-L", *locale ? locale : "C",
+ "-F", (char *)0);
+ syslog(LOG_ERR, "cannot execl %s", _PATH_PR);
+ exit(2);
+ }
+ (void) close(p[1]); /* close output side */
+ (void) close(fi);
+ if (prchild < 0) {
+ prchild = 0;
+ (void) close(p[0]);
+ return (ERROR);
+ }
+ fi = p[0]; /* use pipe for input */
+ case 'f': /* print plain text file */
+ prog = pp->filters[LPF_INPUT];
+ av[1] = width;
+ av[2] = length;
+ av[3] = indent;
+ n = 4;
+ break;
+ case 'o': /* print postscript file */
+ /*
+ * Treat this as a "plain file with control characters", and
+ * assume the standard LPF_INPUT filter will recognize that
+ * the data is postscript and know what to do with it. These
+ * 'o'-file requests could come from MacOS 10.1 systems.
+ * (later versions of MacOS 10 will explicitly use 'l')
+ * A postscript file can contain binary data, which is why 'l'
+ * is somewhat more appropriate than 'f'.
+ */
+ /* FALLTHROUGH */
+ case 'l': /* like 'f' but pass control characters */
+ prog = pp->filters[LPF_INPUT];
+ av[1] = "-c";
+ av[2] = width;
+ av[3] = length;
+ av[4] = indent;
+ n = 5;
+ break;
+ case 'r': /* print a fortran text file */
+ prog = pp->filters[LPF_FORTRAN];
+ av[1] = width;
+ av[2] = length;
+ n = 3;
+ break;
+ case 't': /* print troff output */
+ case 'n': /* print ditroff output */
+ case 'd': /* print tex output */
+ (void) unlink(".railmag");
+ if ((fo = creat(".railmag", FILMOD)) < 0) {
+ syslog(LOG_ERR, "%s: cannot create .railmag",
+ pp->printer);
+ (void) unlink(".railmag");
+ } else {
+ for (n = 0; n < 4; n++) {
+ if (fonts[n][0] != '/')
+ (void) write(fo, _PATH_VFONT,
+ sizeof(_PATH_VFONT) - 1);
+ (void) write(fo, fonts[n], strlen(fonts[n]));
+ (void) write(fo, "\n", 1);
+ }
+ (void) close(fo);
+ }
+ prog = (format == 't') ? pp->filters[LPF_TROFF]
+ : ((format == 'n') ? pp->filters[LPF_DITROFF]
+ : pp->filters[LPF_DVI]);
+ av[1] = pxwidth;
+ av[2] = pxlength;
+ n = 3;
+ break;
+ case 'c': /* print cifplot output */
+ prog = pp->filters[LPF_CIFPLOT];
+ av[1] = pxwidth;
+ av[2] = pxlength;
+ n = 3;
+ break;
+ case 'g': /* print plot(1G) output */
+ prog = pp->filters[LPF_GRAPH];
+ av[1] = pxwidth;
+ av[2] = pxlength;
+ n = 3;
+ break;
+ case 'v': /* print raster output */
+ prog = pp->filters[LPF_RASTER];
+ av[1] = pxwidth;
+ av[2] = pxlength;
+ n = 3;
+ break;
+ default:
+ (void) close(fi);
+ syslog(LOG_ERR, "%s: illegal format character '%c'",
+ pp->printer, format);
+ return (ERROR);
+ }
+ if (prog == NULL) {
+ (void) close(fi);
+ syslog(LOG_ERR,
+ "%s: no filter found in printcap for format character '%c'",
+ pp->printer, format);
+ return (ERROR);
+ }
+ if ((av[0] = strrchr(prog, '/')) != NULL)
+ av[0]++;
+ else
+ av[0] = prog;
+ av[n++] = "-n";
+ av[n++] = logname;
+ av[n++] = "-h";
+ av[n++] = origin_host;
+ av[n++] = pp->acct_file;
+ av[n] = 0;
+ fo = pfd;
+ if (of_pid > 0) { /* stop output filter */
+ write(ofd, "\031\1", 2);
+ while ((wpid =
+ wait3(&wstatus, WUNTRACED, 0)) > 0 && wpid != of_pid)
+ ;
+ if (wpid < 0)
+ syslog(LOG_WARNING,
+ "%s: after stopping 'of', wait3() returned: %m",
+ pp->printer);
+ else if (!WIFSTOPPED(wstatus)) {
+ (void) close(fi);
+ syslog(LOG_WARNING, "%s: output filter died "
+ "(pid=%d retcode=%d termsig=%d)",
+ pp->printer, of_pid, WEXITSTATUS(wstatus),
+ WTERMSIG(wstatus));
+ return (REPRINT);
+ }
+ stopped++;
+ }
+start:
+ if ((child = dofork(pp, DORETURN)) == 0) { /* child */
+ dup2(fi, STDIN_FILENO);
+ dup2(fo, STDOUT_FILENO);
+ /* setup stderr for the filter (child process)
+ * so it goes to our temporary errors file */
+ n = open(tempstderr, O_WRONLY|O_TRUNC, 0664);
+ if (n >= 0)
+ dup2(n, STDERR_FILENO);
+ closelog();
+ closeallfds(3);
+ execv(prog, av);
+ syslog(LOG_ERR, "%s: cannot execv(%s): %m", pp->printer,
+ prog);
+ exit(2);
+ }
+ (void) close(fi);
+ wstatus_set = 0;
+ if (child < 0)
+ retcode = 100;
+ else {
+ while ((wpid = wait(&wstatus)) > 0 && wpid != child)
+ ;
+ if (wpid < 0) {
+ retcode = 100;
+ syslog(LOG_WARNING,
+ "%s: after execv(%s), wait() returned: %m",
+ pp->printer, prog);
+ } else {
+ wstatus_set = 1;
+ retcode = WEXITSTATUS(wstatus);
+ }
+ }
+ child = 0;
+ prchild = 0;
+ if (stopped) { /* restart output filter */
+ if (kill(of_pid, SIGCONT) < 0) {
+ syslog(LOG_ERR, "cannot restart output filter");
+ exit(1);
+ }
+ }
+ pp->tof = 0;
+
+ /* Copy the filter's output to "lf" logfile */
+ if ((fp = fopen(tempstderr, "r"))) {
+ while (fgets(buf, sizeof(buf), fp))
+ fputs(buf, stderr);
+ fclose(fp);
+ }
+
+ if (wstatus_set && !WIFEXITED(wstatus)) {
+ syslog(LOG_WARNING, "%s: filter '%c' terminated (termsig=%d)",
+ pp->printer, format, WTERMSIG(wstatus));
+ return (ERROR);
+ }
+ switch (retcode) {
+ case 0:
+ pp->tof = 1;
+ return (OK);
+ case 1:
+ return (REPRINT);
+ case 2:
+ return (ERROR);
+ default:
+ syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)",
+ pp->printer, format, retcode);
+ return (FILTERERR);
+ }
+}
+
+/*
+ * Send the daemon control file (cf) and any data files.
+ * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
+ * 0 if all is well.
+ */
+static int
+sendit(struct printer *pp, char *file)
+{
+ int dfcopies, err, i;
+ char *cp, last[sizeof(line)];
+
+ /*
+ * open control file
+ */
+ if ((cfp = fopen(file, "r")) == NULL)
+ return (OK);
+
+ /* initialize job-specific count of datafiles processed */
+ job_dfcnt = 0;
+
+ /*
+ * read the control file for work to do
+ *
+ * file format -- first character in the line is a command
+ * rest of the line is the argument.
+ * commands of interest are:
+ *
+ * a-z -- "file name" name of file to print
+ * U -- "unlink" name of file to remove
+ * (after we print it. (Pass 2 only)).
+ */
+
+ /*
+ * pass 1
+ */
+ err = OK;
+ while (getline(cfp)) {
+ again:
+ if (line[0] == 'S') {
+ cp = line+1;
+ i = 0;
+ while (*cp >= '0' && *cp <= '9')
+ i = i * 10 + (*cp++ - '0');
+ fdev = i;
+ cp++;
+ i = 0;
+ while (*cp >= '0' && *cp <= '9')
+ i = i * 10 + (*cp++ - '0');
+ fino = i;
+ } else if (line[0] == 'H') {
+ strlcpy(origin_host, line + 1, sizeof(origin_host));
+ if (class[0] == '\0') {
+ strlcpy(class, line + 1, sizeof(class));
+ }
+ } else if (line[0] == 'P') {
+ strlcpy(logname, line + 1, sizeof(logname));
+ if (pp->restricted) { /* restricted */
+ if (getpwnam(logname) == NULL) {
+ sendmail(pp, line+1, NOACCT);
+ err = ERROR;
+ break;
+ }
+ }
+ } else if (line[0] == 'I') {
+ strlcpy(indent+2, line + 1, sizeof(indent) - 2);
+ } else if (line[0] >= 'a' && line[0] <= 'z') {
+ dfcopies = 1;
+ strcpy(last, line);
+ while ((i = getline(cfp)) != 0) {
+ if (strcmp(last, line) != 0)
+ break;
+ dfcopies++;
+ }
+ switch (sendfile(pp, '\3', last+1, *last, dfcopies)) {
+ case OK:
+ if (i)
+ goto again;
+ break;
+ case REPRINT:
+ (void) fclose(cfp);
+ return (REPRINT);
+ case ACCESS:
+ sendmail(pp, logname, ACCESS);
+ case ERROR:
+ err = ERROR;
+ }
+ break;
+ }
+ }
+ if (err == OK && sendfile(pp, '\2', file, '\0', 1) > 0) {
+ (void) fclose(cfp);
+ return (REPRINT);
+ }
+ /*
+ * pass 2
+ */
+ fseek(cfp, 0L, 0);
+ while (getline(cfp))
+ if (line[0] == 'U' && !strchr(line+1, '/'))
+ (void) unlink(line+1);
+ /*
+ * clean-up in case another control file exists
+ */
+ (void) fclose(cfp);
+ (void) unlink(file);
+ return (err);
+}
+
+/*
+ * Send a data file to the remote machine and spool it.
+ * Return positive if we should try resending.
+ */
+static int
+sendfile(struct printer *pp, int type, char *file, char format, int copyreq)
+{
+ int i, amt;
+ struct stat stb;
+ char *av[15], *filtcmd;
+ char buf[SPL_BUFSIZ], opt_c[4], opt_h[4], opt_n[4];
+ int copycnt, filtstat, narg, resp, sfd, sfres, sizerr, statrc;
+
+ /* Make sure the entire data file has arrived. */
+ wait4data(pp, file);
+
+ statrc = lstat(file, &stb);
+ if (statrc < 0) {
+ syslog(LOG_ERR, "%s: error from lstat(%s): %m",
+ pp->printer, file);
+ return (ERROR);
+ }
+ sfd = open(file, O_RDONLY);
+ if (sfd < 0) {
+ syslog(LOG_ERR, "%s: error from open(%s,O_RDONLY): %m",
+ pp->printer, file);
+ return (ERROR);
+ }
+ /*
+ * Check to see if data file is a symbolic link. If so, it should
+ * still point to the same file or someone is trying to print something
+ * he shouldn't.
+ */
+ if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(sfd, &stb) == 0 &&
+ (stb.st_dev != fdev || stb.st_ino != fino)) {
+ close(sfd);
+ return (ACCESS);
+ }
+
+ /* Everything seems OK for reading the file, now to send it */
+ filtcmd = NULL;
+ sizerr = 0;
+ tfd = -1;
+ if (type == '\3') {
+ /*
+ * Type == 3 means this is a datafile, not a control file.
+ * Increment the counter of data-files in this job, and
+ * then check for input or output filters (which are only
+ * applied to datafiles, not control files).
+ */
+ job_dfcnt++;
+
+ /*
+ * Note that here we are filtering datafiles, one at a time,
+ * as they are sent to the remote machine. Here, the *only*
+ * difference between an input filter (`if=') and an output
+ * filter (`of=') is the argument list that the filter is
+ * started up with. Here, the output filter is executed
+ * for each individual file as it is sent. This is not the
+ * same as local print queues, where the output filter is
+ * started up once, and then all jobs are passed thru that
+ * single invocation of the output filter.
+ *
+ * Also note that a queue for a remote-machine can have an
+ * input filter or an output filter, but not both.
+ */
+ if (pp->filters[LPF_INPUT]) {
+ filtcmd = pp->filters[LPF_INPUT];
+ av[0] = filtcmd;
+ narg = 0;
+ strcpy(opt_c, "-c");
+ strcpy(opt_h, "-h");
+ strcpy(opt_n, "-n");
+ if (format == 'l')
+ av[++narg] = opt_c;
+ av[++narg] = width;
+ av[++narg] = length;
+ av[++narg] = indent;
+ av[++narg] = opt_n;
+ av[++narg] = logname;
+ av[++narg] = opt_h;
+ av[++narg] = origin_host;
+ av[++narg] = pp->acct_file;
+ av[++narg] = NULL;
+ } else if (pp->filters[LPF_OUTPUT]) {
+ filtcmd = pp->filters[LPF_OUTPUT];
+ av[0] = filtcmd;
+ narg = 0;
+ av[++narg] = width;
+ av[++narg] = length;
+ av[++narg] = NULL;
+ }
+ }
+ if (filtcmd) {
+ /*
+ * If there is an input or output filter, we have to run
+ * the datafile thru that filter and store the result as
+ * a temporary spool file, because the protocol requires
+ * that we send the remote host the file-size before we
+ * start to send any of the data.
+ */
+ strcpy(tfile, TFILENAME);
+ tfd = mkstemp(tfile);
+ if (tfd == -1) {
+ syslog(LOG_ERR, "%s: mkstemp(%s): %m", pp->printer,
+ TFILENAME);
+ sfres = ERROR;
+ goto return_sfres;
+ }
+ filtstat = execfilter(pp, filtcmd, av, sfd, tfd);
+
+ /* process the return-code from the filter */
+ switch (filtstat) {
+ case 0:
+ break;
+ case 1:
+ sfres = REPRINT;
+ goto return_sfres;
+ case 2:
+ sfres = ERROR;
+ goto return_sfres;
+ default:
+ syslog(LOG_WARNING,
+ "%s: filter '%c' exited (retcode=%d)",
+ pp->printer, format, filtstat);
+ sfres = FILTERERR;
+ goto return_sfres;
+ }
+ statrc = fstat(tfd, &stb); /* to find size of tfile */
+ if (statrc < 0) {
+ syslog(LOG_ERR,
+ "%s: error processing 'if', fstat(%s): %m",
+ pp->printer, tfile);
+ sfres = ERROR;
+ goto return_sfres;
+ }
+ close(sfd);
+ sfd = tfd;
+ lseek(sfd, 0, SEEK_SET);
+ }
+
+ copycnt = 0;
+sendagain:
+ copycnt++;
+
+ if (copycnt < 2)
+ (void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file);
+ else
+ (void) sprintf(buf, "%c%qd %s_c%d\n", type, stb.st_size,
+ file, copycnt);
+ amt = strlen(buf);
+ for (i = 0; ; i++) {
+ if (write(pfd, buf, amt) != amt ||
+ (resp = response(pp)) < 0 || resp == '\1') {
+ sfres = REPRINT;
+ goto return_sfres;
+ } else if (resp == '\0')
+ break;
+ if (i == 0)
+ pstatus(pp,
+ "no space on remote; waiting for queue to drain");
+ if (i == 10)
+ syslog(LOG_ALERT, "%s: can't send to %s; queue full",
+ pp->printer, pp->remote_host);
+ sleep(5 * 60);
+ }
+ if (i)
+ pstatus(pp, "sending to %s", pp->remote_host);
+ /*
+ * XXX - we should change trstat_init()/trstat_write() to include
+ * the copycnt in the statistics record it may write.
+ */
+ if (type == '\3')
+ trstat_init(pp, file, job_dfcnt);
+ for (i = 0; i < stb.st_size; i += SPL_BUFSIZ) {
+ amt = SPL_BUFSIZ;
+ if (i + amt > stb.st_size)
+ amt = stb.st_size - i;
+ if (sizerr == 0 && read(sfd, buf, amt) != amt)
+ sizerr = 1;
+ if (write(pfd, buf, amt) != amt) {
+ sfres = REPRINT;
+ goto return_sfres;
+ }
+ }
+
+ if (sizerr) {
+ syslog(LOG_INFO, "%s: %s: changed size", pp->printer, file);
+ /* tell recvjob to ignore this file */
+ (void) write(pfd, "\1", 1);
+ sfres = ERROR;
+ goto return_sfres;
+ }
+ if (write(pfd, "", 1) != 1 || response(pp)) {
+ sfres = REPRINT;
+ goto return_sfres;
+ }
+ if (type == '\3') {
+ trstat_write(pp, TR_SENDING, stb.st_size, logname,
+ pp->remote_host, origin_host);
+ /*
+ * Usually we only need to send one copy of a datafile,
+ * because the control-file will simply print the same
+ * file multiple times. However, some printers ignore
+ * the control file, and simply print each data file as
+ * it arrives. For such "remote hosts", we need to
+ * transfer the same data file multiple times. Such a
+ * a host is indicated by adding 'rc' to the printcap
+ * entry.
+ * XXX - Right now this ONLY works for remote hosts which
+ * do ignore the name of the data file, because
+ * this sends the file multiple times with slight
+ * changes to the filename. To do this right would
+ * require that we also rewrite the control file
+ * to match those filenames.
+ */
+ if (pp->resend_copies && (copycnt < copyreq)) {
+ lseek(sfd, 0, SEEK_SET);
+ goto sendagain;
+ }
+ }
+ sfres = OK;
+
+return_sfres:
+ (void)close(sfd);
+ if (tfd != -1) {
+ /*
+ * If tfd is set, then it is the same value as sfd, and
+ * therefore it is already closed at this point. All
+ * we need to do is remove the temporary file.
+ */
+ tfd = -1;
+ unlink(tfile);
+ }
+ return (sfres);
+}
+
+/*
+ * Some print servers send the control-file first, and then start sending the
+ * matching data file(s). That is not the correct order. If some queue is
+ * already printing an active job, then when that job is finished the queue
+ * may proceed to the control file of any incoming print job. This turns
+ * into a race between the process which is receiving the data file, and the
+ * process which is actively printing the very same file. When the remote
+ * server sends files in the wrong order, it is even possible that a queue
+ * will start to print a data file before the file has been created!
+ *
+ * So before we start to print() or send() a data file, we call this routine
+ * to make sure the data file is not still changing in size. Note that this
+ * problem will only happen for jobs arriving from a remote host, and that
+ * the process which has decided to print this job (and is thus making this
+ * check) is *not* the process which is receiving the job.
+ *
+ * A second benefit of this is that any incoming job is guaranteed to appear
+ * in a queue listing for at least a few seconds after it has arrived. Some
+ * lpr implementations get confused if they send a job and it disappears
+ * from the queue before they can check on it.
+ */
+#define MAXWAIT_ARRIVE 16 /* max to wait for the file to *exist* */
+#define MAXWAIT_4DATA (20*60) /* max to wait for it to stop changing */
+#define MINWAIT_4DATA 4 /* This value must be >= 1 */
+#define DEBUG_MINWAIT 1
+static void
+wait4data(struct printer *pp, const char *dfile)
+{
+ const char *cp;
+ int statres;
+ size_t dlen, hlen;
+ time_t amtslept, checktime;
+ struct stat statdf;
+
+ /* Skip these checks if the print job is from the local host. */
+ dlen = strlen(dfile);
+ hlen = strlen(local_host);
+ if (dlen > hlen) {
+ cp = dfile + dlen - hlen;
+ if (strcmp(cp, local_host) == 0)
+ return;
+ }
+
+ /*
+ * If this data file does not exist, then wait up to MAXWAIT_ARRIVE
+ * seconds for it to arrive.
+ */
+ amtslept = 0;
+ statres = stat(dfile, &statdf);
+ while (statres < 0 && amtslept < MAXWAIT_ARRIVE) {
+ if (amtslept == 0)
+ pstatus(pp, "Waiting for data file from remote host");
+ amtslept += MINWAIT_4DATA - sleep(MINWAIT_4DATA);
+ statres = stat(dfile, &statdf);
+ }
+ if (statres < 0) {
+ /* The file still does not exist, so just give up on it. */
+ syslog(LOG_WARNING, "%s: wait4data() abandoned wait for %s",
+ pp->printer, dfile);
+ return;
+ }
+
+ /*
+ * The file exists, so keep waiting until the data file has not
+ * changed for some reasonable amount of time.
+ */
+ while (statres == 0 && amtslept < MAXWAIT_4DATA) {
+ checktime = time(NULL) - MINWAIT_4DATA;
+ if (statdf.st_mtime <= checktime)
+ break;
+ if (amtslept == 0)
+ pstatus(pp, "Waiting for data file from remote host");
+ amtslept += MINWAIT_4DATA - sleep(MINWAIT_4DATA);
+ statres = stat(dfile, &statdf);
+ }
+
+ if (statres != 0)
+ syslog(LOG_WARNING, "%s: %s disappeared during wait4data()",
+ pp->printer, dfile);
+ else if (amtslept > MAXWAIT_4DATA)
+ syslog(LOG_WARNING,
+ "%s: %s still changing after %lu secs in wait4data()",
+ pp->printer, dfile, (unsigned long)amtslept);
+#if DEBUG_MINWAIT
+ else if (amtslept > MINWAIT_4DATA)
+ syslog(LOG_INFO, "%s: slept %lu secs in wait4data(%s)",
+ pp->printer, (unsigned long)amtslept, dfile);
+#endif
+}
+#undef MAXWAIT_ARRIVE
+#undef MAXWAIT_4DATA
+#undef MINWAIT_4DATA
+
+/*
+ * This routine is called to execute one of the filters as was
+ * specified in a printcap entry. While the child-process will read
+ * all of 'infd', it is up to the caller to close that file descriptor
+ * in the parent process.
+ */
+static int
+execfilter(struct printer *pp, char *f_cmd, char *f_av[], int infd, int outfd)
+{
+ pid_t fpid, wpid;
+ int errfd, retcode, wstatus;
+ FILE *errfp;
+ char buf[BUFSIZ], *slash;
+
+ fpid = dofork(pp, DORETURN);
+ if (fpid != 0) {
+ /*
+ * This is the parent process, which just waits for the child
+ * to complete and then returns the result. Note that it is
+ * the child process which reads the input stream.
+ */
+ if (fpid < 0)
+ retcode = 100;
+ else {
+ while ((wpid = wait(&wstatus)) > 0 &&
+ wpid != fpid)
+ ;
+ if (wpid < 0) {
+ retcode = 100;
+ syslog(LOG_WARNING,
+ "%s: after execv(%s), wait() returned: %m",
+ pp->printer, f_cmd);
+ } else
+ retcode = WEXITSTATUS(wstatus);
+ }
+
+ /*
+ * Copy everything the filter wrote to stderr from our
+ * temporary errors file to the "lf=" logfile.
+ */
+ errfp = fopen(tempstderr, "r");
+ if (errfp) {
+ while (fgets(buf, sizeof(buf), errfp))
+ fputs(buf, stderr);
+ fclose(errfp);
+ }
+
+ return (retcode);
+ }
+
+ /*
+ * This is the child process, which is the one that executes the
+ * given filter.
+ */
+ /*
+ * If the first parameter has any slashes in it, then change it
+ * to point to the first character after the last slash.
+ */
+ slash = strrchr(f_av[0], '/');
+ if (slash != NULL)
+ f_av[0] = slash + 1;
+ /*
+ * XXX - in the future, this should setup an explicit list of
+ * environment variables and use execve()!
+ */
+
+ /*
+ * Setup stdin, stdout, and stderr as we want them when the filter
+ * is running. Stderr is setup so it points to a temporary errors
+ * file, and the parent process will copy that temporary file to
+ * the real logfile after the filter completes.
+ */
+ dup2(infd, STDIN_FILENO);
+ dup2(outfd, STDOUT_FILENO);
+ errfd = open(tempstderr, O_WRONLY|O_TRUNC, 0664);
+ if (errfd >= 0)
+ dup2(errfd, STDERR_FILENO);
+ closelog();
+ closeallfds(3);
+ execv(f_cmd, f_av);
+ syslog(LOG_ERR, "%s: cannot execv(%s): %m", pp->printer, f_cmd);
+ exit(2);
+ /* NOTREACHED */
+}
+
+/*
+ * Check to make sure there have been no errors and that both programs
+ * are in sync with eachother.
+ * Return non-zero if the connection was lost.
+ */
+static char
+response(const struct printer *pp)
+{
+ char resp;
+
+ if (read(pfd, &resp, 1) != 1) {
+ syslog(LOG_INFO, "%s: lost connection", pp->printer);
+ return (-1);
+ }
+ return (resp);
+}
+
+/*
+ * Banner printing stuff
+ */
+static void
+banner(struct printer *pp, char *name1, char *name2)
+{
+ time_t tvec;
+
+ time(&tvec);
+ if (!pp->no_formfeed && !pp->tof)
+ (void) write(ofd, pp->form_feed, strlen(pp->form_feed));
+ if (pp->short_banner) { /* short banner only */
+ if (class[0]) {
+ (void) write(ofd, class, strlen(class));
+ (void) write(ofd, ":", 1);
+ }
+ (void) write(ofd, name1, strlen(name1));
+ (void) write(ofd, " Job: ", 7);
+ (void) write(ofd, name2, strlen(name2));
+ (void) write(ofd, " Date: ", 8);
+ (void) write(ofd, ctime(&tvec), 24);
+ (void) write(ofd, "\n", 1);
+ } else { /* normal banner */
+ (void) write(ofd, "\n\n\n", 3);
+ scan_out(pp, ofd, name1, '\0');
+ (void) write(ofd, "\n\n", 2);
+ scan_out(pp, ofd, name2, '\0');
+ if (class[0]) {
+ (void) write(ofd,"\n\n\n",3);
+ scan_out(pp, ofd, class, '\0');
+ }
+ (void) write(ofd, "\n\n\n\n\t\t\t\t\tJob: ", 15);
+ (void) write(ofd, name2, strlen(name2));
+ (void) write(ofd, "\n\t\t\t\t\tDate: ", 12);
+ (void) write(ofd, ctime(&tvec), 24);
+ (void) write(ofd, "\n", 1);
+ }
+ if (!pp->no_formfeed)
+ (void) write(ofd, pp->form_feed, strlen(pp->form_feed));
+ pp->tof = 1;
+}
+
+static char *
+scnline(int key, char *p, int c)
+{
+ register int scnwidth;
+
+ for (scnwidth = WIDTH; --scnwidth;) {
+ key <<= 1;
+ *p++ = key & 0200 ? c : BACKGND;
+ }
+ return (p);
+}
+
+#define TRC(q) (((q)-' ')&0177)
+
+static void
+scan_out(struct printer *pp, int scfd, char *scsp, int dlm)
+{
+ register char *strp;
+ register int nchrs, j;
+ char outbuf[LINELEN+1], *sp, c, cc;
+ int d, scnhgt;
+
+ for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
+ strp = &outbuf[0];
+ sp = scsp;
+ for (nchrs = 0; ; ) {
+ d = dropit(c = TRC(cc = *sp++));
+ if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
+ for (j = WIDTH; --j;)
+ *strp++ = BACKGND;
+ else
+ strp = scnline(scnkey[(int)c][scnhgt-1-d], strp, cc);
+ if (*sp == dlm || *sp == '\0' ||
+ nchrs++ >= pp->page_width/(WIDTH+1)-1)
+ break;
+ *strp++ = BACKGND;
+ *strp++ = BACKGND;
+ }
+ while (*--strp == BACKGND && strp >= outbuf)
+ ;
+ strp++;
+ *strp++ = '\n';
+ (void) write(scfd, outbuf, strp-outbuf);
+ }
+}
+
+static int
+dropit(int c)
+{
+ switch(c) {
+
+ case TRC('_'):
+ case TRC(';'):
+ case TRC(','):
+ case TRC('g'):
+ case TRC('j'):
+ case TRC('p'):
+ case TRC('q'):
+ case TRC('y'):
+ return (DROP);
+
+ default:
+ return (0);
+ }
+}
+
+/*
+ * sendmail ---
+ * tell people about job completion
+ */
+static void
+sendmail(struct printer *pp, char *userid, int bombed)
+{
+ register int i;
+ int p[2], s;
+ register const char *cp;
+ struct stat stb;
+ FILE *fp;
+
+ pipe(p);
+ if ((s = dofork(pp, DORETURN)) == 0) { /* child */
+ dup2(p[0], STDIN_FILENO);
+ closelog();
+ closeallfds(3);
+ if ((cp = strrchr(_PATH_SENDMAIL, '/')) != NULL)
+ cp++;
+ else
+ cp = _PATH_SENDMAIL;
+ execl(_PATH_SENDMAIL, cp, "-t", (char *)0);
+ _exit(0);
+ } else if (s > 0) { /* parent */
+ dup2(p[1], STDOUT_FILENO);
+ printf("To: %s@%s\n", userid, origin_host);
+ printf("Subject: %s printer job \"%s\"\n", pp->printer,
+ *jobname ? jobname : "<unknown>");
+ printf("Reply-To: root@%s\n\n", local_host);
+ printf("Your printer job ");
+ if (*jobname)
+ printf("(%s) ", jobname);
+
+ switch (bombed) {
+ case OK:
+ cp = "OK";
+ printf("\ncompleted successfully\n");
+ break;
+ default:
+ case FATALERR:
+ cp = "FATALERR";
+ printf("\ncould not be printed\n");
+ break;
+ case NOACCT:
+ cp = "NOACCT";
+ printf("\ncould not be printed without an account on %s\n",
+ local_host);
+ break;
+ case FILTERERR:
+ cp = "FILTERERR";
+ if (stat(tempstderr, &stb) < 0 || stb.st_size == 0
+ || (fp = fopen(tempstderr, "r")) == NULL) {
+ printf("\nhad some errors and may not have printed\n");
+ break;
+ }
+ printf("\nhad the following errors and may not have printed:\n");
+ while ((i = getc(fp)) != EOF)
+ putchar(i);
+ (void) fclose(fp);
+ break;
+ case ACCESS:
+ cp = "ACCESS";
+ printf("\nwas not printed because it was not linked to the original file\n");
+ }
+ fflush(stdout);
+ (void) close(STDOUT_FILENO);
+ } else {
+ syslog(LOG_WARNING, "unable to send mail to %s: %m", userid);
+ return;
+ }
+ (void) close(p[0]);
+ (void) close(p[1]);
+ wait(NULL);
+ syslog(LOG_INFO, "mail sent to user %s about job %s on printer %s (%s)",
+ userid, *jobname ? jobname : "<unknown>", pp->printer, cp);
+}
+
+/*
+ * dofork - fork with retries on failure
+ */
+static int
+dofork(const struct printer *pp, int action)
+{
+ pid_t forkpid;
+ int i, fail;
+ struct passwd *pwd;
+
+ forkpid = -1;
+ if (daemon_uname == NULL) {
+ pwd = getpwuid(pp->daemon_user);
+ if (pwd == NULL) {
+ syslog(LOG_ERR, "%s: Can't lookup default daemon uid (%ld) in password file",
+ pp->printer, pp->daemon_user);
+ goto error_ret;
+ }
+ daemon_uname = strdup(pwd->pw_name);
+ daemon_defgid = pwd->pw_gid;
+ }
+
+ for (i = 0; i < 20; i++) {
+ forkpid = fork();
+ if (forkpid < 0) {
+ sleep((unsigned)(i*i));
+ continue;
+ }
+ /*
+ * Child should run as daemon instead of root
+ */
+ if (forkpid == 0) {
+ errno = 0;
+ fail = initgroups(daemon_uname, daemon_defgid);
+ if (fail) {
+ syslog(LOG_ERR, "%s: initgroups(%s,%u): %m",
+ pp->printer, daemon_uname, daemon_defgid);
+ break;
+ }
+ fail = setgid(daemon_defgid);
+ if (fail) {
+ syslog(LOG_ERR, "%s: setgid(%u): %m",
+ pp->printer, daemon_defgid);
+ break;
+ }
+ fail = setuid(pp->daemon_user);
+ if (fail) {
+ syslog(LOG_ERR, "%s: setuid(%ld): %m",
+ pp->printer, pp->daemon_user);
+ break;
+ }
+ }
+ return (forkpid);
+ }
+
+ /*
+ * An error occurred. If the error is in the child process, then
+ * this routine MUST always exit(). DORETURN only effects how
+ * errors should be handled in the parent process.
+ */
+error_ret:
+ if (forkpid == 0) {
+ syslog(LOG_ERR, "%s: dofork(): aborting child process...",
+ pp->printer);
+ exit(1);
+ }
+ syslog(LOG_ERR, "%s: dofork(): failure in fork", pp->printer);
+
+ sleep(1); /* throttle errors, as a safety measure */
+ switch (action) {
+ case DORETURN:
+ return (-1);
+ default:
+ syslog(LOG_ERR, "bad action (%d) to dofork", action);
+ /* FALLTHROUGH */
+ case DOABORT:
+ exit(1);
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * Kill child processes to abort current job.
+ */
+static void
+abortpr(int signo __unused)
+{
+
+ (void) unlink(tempstderr);
+ kill(0, SIGINT);
+ if (of_pid > 0)
+ kill(of_pid, SIGCONT);
+ while (wait(NULL) > 0)
+ ;
+ if (of_pid > 0 && tfd != -1)
+ unlink(tfile);
+ exit(0);
+}
+
+static void
+init(struct printer *pp)
+{
+ char *s;
+
+ sprintf(&width[2], "%ld", pp->page_width);
+ sprintf(&length[2], "%ld", pp->page_length);
+ sprintf(&pxwidth[2], "%ld", pp->page_pwidth);
+ sprintf(&pxlength[2], "%ld", pp->page_plength);
+ if ((s = checkremote(pp)) != 0) {
+ syslog(LOG_WARNING, "%s", s);
+ free(s);
+ }
+}
+
+void
+startprinting(const char *printer)
+{
+ struct printer myprinter, *pp = &myprinter;
+ int status;
+
+ init_printer(pp);
+ status = getprintcap(printer, pp);
+ switch(status) {
+ case PCAPERR_OSERR:
+ syslog(LOG_ERR, "can't open printer description file: %m");
+ exit(1);
+ case PCAPERR_NOTFOUND:
+ syslog(LOG_ERR, "unknown printer: %s", printer);
+ exit(1);
+ case PCAPERR_TCLOOP:
+ fatal(pp, "potential reference loop detected in printcap file");
+ default:
+ break;
+ }
+ printjob(pp);
+}
+
+/*
+ * Acquire line printer or remote connection.
+ */
+static void
+openpr(const struct printer *pp)
+{
+ int p[2];
+ char *cp;
+
+ if (pp->remote) {
+ openrem(pp);
+ /*
+ * Lpd does support the setting of 'of=' filters for
+ * jobs going to remote machines, but that does not
+ * have the same meaning as 'of=' does when handling
+ * local print queues. For remote machines, all 'of='
+ * filter processing is handled in sendfile(), and that
+ * does not use these global "output filter" variables.
+ */
+ ofd = -1;
+ of_pid = 0;
+ return;
+ } else if (*pp->lp) {
+ if ((cp = strchr(pp->lp, '@')) != NULL)
+ opennet(pp);
+ else
+ opentty(pp);
+ } else {
+ syslog(LOG_ERR, "%s: no line printer device or host name",
+ pp->printer);
+ exit(1);
+ }
+
+ /*
+ * Start up an output filter, if needed.
+ */
+ if (pp->filters[LPF_OUTPUT] && !pp->filters[LPF_INPUT] && !of_pid) {
+ pipe(p);
+ if (pp->remote) {
+ strcpy(tfile, TFILENAME);
+ tfd = mkstemp(tfile);
+ }
+ if ((of_pid = dofork(pp, DOABORT)) == 0) { /* child */
+ dup2(p[0], STDIN_FILENO); /* pipe is std in */
+ /* tfile/printer is stdout */
+ dup2(pp->remote ? tfd : pfd, STDOUT_FILENO);
+ closelog();
+ closeallfds(3);
+ if ((cp = strrchr(pp->filters[LPF_OUTPUT], '/')) == NULL)
+ cp = pp->filters[LPF_OUTPUT];
+ else
+ cp++;
+ execl(pp->filters[LPF_OUTPUT], cp, width, length,
+ (char *)0);
+ syslog(LOG_ERR, "%s: execl(%s): %m", pp->printer,
+ pp->filters[LPF_OUTPUT]);
+ exit(1);
+ }
+ (void) close(p[0]); /* close input side */
+ ofd = p[1]; /* use pipe for output */
+ } else {
+ ofd = pfd;
+ of_pid = 0;
+ }
+}
+
+/*
+ * Printer connected directly to the network
+ * or to a terminal server on the net
+ */
+static void
+opennet(const struct printer *pp)
+{
+ register int i;
+ int resp;
+ u_long port;
+ char *ep;
+ void (*savealrm)(int);
+
+ port = strtoul(pp->lp, &ep, 0);
+ if (*ep != '@' || port > 65535) {
+ syslog(LOG_ERR, "%s: bad port number: %s", pp->printer,
+ pp->lp);
+ exit(1);
+ }
+ ep++;
+
+ for (i = 1; ; i = i < 256 ? i << 1 : i) {
+ resp = -1;
+ savealrm = signal(SIGALRM, alarmhandler);
+ alarm(pp->conn_timeout);
+ pfd = getport(pp, ep, port);
+ alarm(0);
+ (void)signal(SIGALRM, savealrm);
+ if (pfd < 0 && errno == ECONNREFUSED)
+ resp = 1;
+ else if (pfd >= 0) {
+ /*
+ * need to delay a bit for rs232 lines
+ * to stabilize in case printer is
+ * connected via a terminal server
+ */
+ delay(500);
+ break;
+ }
+ if (i == 1) {
+ if (resp < 0)
+ pstatus(pp, "waiting for %s to come up",
+ pp->lp);
+ else
+ pstatus(pp,
+ "waiting for access to printer on %s",
+ pp->lp);
+ }
+ sleep(i);
+ }
+ pstatus(pp, "sending to %s port %lu", ep, port);
+}
+
+/*
+ * Printer is connected to an RS232 port on this host
+ */
+static void
+opentty(const struct printer *pp)
+{
+ register int i;
+
+ for (i = 1; ; i = i < 32 ? i << 1 : i) {
+ pfd = open(pp->lp, pp->rw ? O_RDWR : O_WRONLY);
+ if (pfd >= 0) {
+ delay(500);
+ break;
+ }
+ if (errno == ENOENT) {
+ syslog(LOG_ERR, "%s: %m", pp->lp);
+ exit(1);
+ }
+ if (i == 1)
+ pstatus(pp,
+ "waiting for %s to become ready (offline?)",
+ pp->printer);
+ sleep(i);
+ }
+ if (isatty(pfd))
+ setty(pp);
+ pstatus(pp, "%s is ready and printing", pp->printer);
+}
+
+/*
+ * Printer is on a remote host
+ */
+static void
+openrem(const struct printer *pp)
+{
+ register int i;
+ int resp;
+ void (*savealrm)(int);
+
+ for (i = 1; ; i = i < 256 ? i << 1 : i) {
+ resp = -1;
+ savealrm = signal(SIGALRM, alarmhandler);
+ alarm(pp->conn_timeout);
+ pfd = getport(pp, pp->remote_host, 0);
+ alarm(0);
+ (void)signal(SIGALRM, savealrm);
+ if (pfd >= 0) {
+ if ((writel(pfd, "\2", pp->remote_queue, "\n",
+ (char *)0)
+ == 2 + strlen(pp->remote_queue))
+ && (resp = response(pp)) == 0)
+ break;
+ (void) close(pfd);
+ }
+ if (i == 1) {
+ if (resp < 0)
+ pstatus(pp, "waiting for %s to come up",
+ pp->remote_host);
+ else {
+ pstatus(pp,
+ "waiting for queue to be enabled on %s",
+ pp->remote_host);
+ i = 256;
+ }
+ }
+ sleep(i);
+ }
+ pstatus(pp, "sending to %s", pp->remote_host);
+}
+
+/*
+ * setup tty lines.
+ */
+static void
+setty(const struct printer *pp)
+{
+ struct termios ttybuf;
+
+ if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
+ syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", pp->printer);
+ exit(1);
+ }
+ if (tcgetattr(pfd, &ttybuf) < 0) {
+ syslog(LOG_ERR, "%s: tcgetattr: %m", pp->printer);
+ exit(1);
+ }
+ if (pp->baud_rate > 0)
+ cfsetspeed(&ttybuf, pp->baud_rate);
+ if (pp->mode_set) {
+ char *s = strdup(pp->mode_set), *tmp;
+
+ while ((tmp = strsep(&s, ",")) != NULL) {
+ (void) msearch(tmp, &ttybuf);
+ }
+ }
+ if (pp->mode_set != 0 || pp->baud_rate > 0) {
+ if (tcsetattr(pfd, TCSAFLUSH, &ttybuf) == -1) {
+ syslog(LOG_ERR, "%s: tcsetattr: %m", pp->printer);
+ }
+ }
+}
+
+#include <stdarg.h>
+
+static void
+pstatus(const struct printer *pp, const char *msg, ...)
+{
+ int fd;
+ char *buf;
+ va_list ap;
+ va_start(ap, msg);
+
+ umask(S_IWOTH);
+ fd = open(pp->status_file, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE);
+ if (fd < 0) {
+ syslog(LOG_ERR, "%s: open(%s): %m", pp->printer,
+ pp->status_file);
+ exit(1);
+ }
+ ftruncate(fd, 0);
+ vasprintf(&buf, msg, ap);
+ va_end(ap);
+ writel(fd, buf, "\n", (char *)0);
+ close(fd);
+ free(buf);
+}
+
+void
+alarmhandler(int signo __unused)
+{
+ /* the signal is ignored */
+ /* (the '__unused' is just to avoid a compile-time warning) */
+}
diff --git a/usr.sbin/lpr/lpd/recvjob.c b/usr.sbin/lpr/lpd/recvjob.c
new file mode 100644
index 0000000..a76bb6d
--- /dev/null
+++ b/usr.sbin/lpr/lpd/recvjob.c
@@ -0,0 +1,409 @@
+/*
+ * 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.
+ */
+
+#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 */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)recvjob.c 8.2 (Berkeley) 4/27/95";
+#endif /* not lint */
+#endif
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+__FBSDID("$FreeBSD$");
+
+/*
+ * Receive printer jobs from the network, queue them and
+ * start the printer daemon.
+ */
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "lp.h"
+#include "lp.local.h"
+#include "ctlinfo.h"
+#include "extern.h"
+#include "pathnames.h"
+
+#define ack() (void) write(STDOUT_FILENO, sp, (size_t)1);
+
+/*
+ * The buffer size to use when reading/writing spool files.
+ */
+#define SPL_BUFSIZ BUFSIZ
+
+static char dfname[NAME_MAX]; /* data files */
+static int minfree; /* keep at least minfree blocks available */
+static const char *sp = "";
+static char tfname[NAME_MAX]; /* tmp copy of cf before linking */
+
+static int chksize(int _size);
+static void frecverr(const char *_msg, ...) __printf0like(1, 2);
+static int noresponse(void);
+static void rcleanup(int _signo);
+static int read_number(const char *_fn);
+static int readfile(struct printer *_pp, char *_file, size_t _size);
+static int readjob(struct printer *_pp);
+
+
+void
+recvjob(const char *printer)
+{
+ struct stat stb;
+ int status;
+ struct printer myprinter, *pp = &myprinter;
+
+ /*
+ * Perform lookup for printer name or abbreviation
+ */
+ init_printer(pp);
+ status = getprintcap(printer, pp);
+ switch (status) {
+ case PCAPERR_OSERR:
+ frecverr("cannot open printer description file");
+ break;
+ case PCAPERR_NOTFOUND:
+ frecverr("unknown printer %s", printer);
+ break;
+ case PCAPERR_TCLOOP:
+ fatal(pp, "potential reference loop detected in printcap file");
+ default:
+ break;
+ }
+
+ (void) close(STDERR_FILENO); /* set up log file */
+ if (open(pp->log_file, O_WRONLY|O_APPEND, 0664) < 0) {
+ syslog(LOG_ERR, "%s: %m", pp->log_file);
+ (void) open(_PATH_DEVNULL, O_WRONLY);
+ }
+
+ if (chdir(pp->spool_dir) < 0)
+ frecverr("%s: chdir(%s): %s", pp->printer, pp->spool_dir,
+ strerror(errno));
+ if (stat(pp->lock_file, &stb) == 0) {
+ if (stb.st_mode & 010) {
+ /* queue is disabled */
+ putchar('\1'); /* return error code */
+ exit(1);
+ }
+ } else if (stat(pp->spool_dir, &stb) < 0)
+ frecverr("%s: stat(%s): %s", pp->printer, pp->spool_dir,
+ strerror(errno));
+ minfree = 2 * read_number("minfree"); /* scale KB to 512 blocks */
+ signal(SIGTERM, rcleanup);
+ signal(SIGPIPE, rcleanup);
+
+ if (readjob(pp))
+ printjob(pp);
+}
+
+/*
+ * Read printer jobs sent by lpd and copy them to the spooling directory.
+ * Return the number of jobs successfully transfered.
+ */
+static int
+readjob(struct printer *pp)
+{
+ register int size;
+ int cfcnt, dfcnt;
+ char *cp, *clastp, *errmsg;
+ char givenid[32], givenhost[MAXHOSTNAMELEN];
+
+ ack();
+ cfcnt = 0;
+ dfcnt = 0;
+ for (;;) {
+ /*
+ * Read a command to tell us what to do
+ */
+ cp = line;
+ clastp = line + sizeof(line) - 1;
+ do {
+ size = read(STDOUT_FILENO, cp, (size_t)1);
+ if (size != (ssize_t)1) {
+ if (size < (ssize_t)0) {
+ frecverr("%s: lost connection",
+ pp->printer);
+ /*NOTREACHED*/
+ }
+ return (cfcnt);
+ }
+ } while ((*cp++ != '\n') && (cp <= clastp));
+ if (cp > clastp) {
+ frecverr("%s: readjob overflow", pp->printer);
+ /*NOTREACHED*/
+ }
+ *--cp = '\0';
+ cp = line;
+ switch (*cp++) {
+ case '\1': /* cleanup because data sent was bad */
+ rcleanup(0);
+ continue;
+
+ case '\2': /* read cf file */
+ size = 0;
+ dfcnt = 0;
+ while (*cp >= '0' && *cp <= '9')
+ size = size * 10 + (*cp++ - '0');
+ if (*cp++ != ' ')
+ break;
+ /*
+ * host name has been authenticated, we use our
+ * view of the host name since we may be passed
+ * something different than what gethostbyaddr()
+ * returns
+ */
+ strlcpy(cp + 6, from_host, sizeof(line)
+ + (size_t)(line - cp - 6));
+ if (strchr(cp, '/')) {
+ frecverr("readjob: %s: illegal path name", cp);
+ /*NOTREACHED*/
+ }
+ strlcpy(tfname, cp, sizeof(tfname));
+ tfname[sizeof (tfname) - 1] = '\0';
+ tfname[0] = 't';
+ if (!chksize(size)) {
+ (void) write(STDOUT_FILENO, "\2", (size_t)1);
+ continue;
+ }
+ if (!readfile(pp, tfname, (size_t)size)) {
+ rcleanup(0);
+ continue;
+ }
+ errmsg = ctl_renametf(pp->printer, tfname);
+ tfname[0] = '\0';
+ if (errmsg != NULL) {
+ frecverr("%s: %s", pp->printer, errmsg);
+ /*NOTREACHED*/
+ }
+ cfcnt++;
+ continue;
+
+ case '\3': /* read df file */
+ *givenid = '\0';
+ *givenhost = '\0';
+ size = 0;
+ while (*cp >= '0' && *cp <= '9')
+ size = size * 10 + (*cp++ - '0');
+ if (*cp++ != ' ')
+ break;
+ if (strchr(cp, '/')) {
+ frecverr("readjob: %s: illegal path name", cp);
+ /*NOTREACHED*/
+ }
+ if (!chksize(size)) {
+ (void) write(STDOUT_FILENO, "\2", (size_t)1);
+ continue;
+ }
+ strlcpy(dfname, cp, sizeof(dfname));
+ dfcnt++;
+ trstat_init(pp, dfname, dfcnt);
+ (void) readfile(pp, dfname, (size_t)size);
+ trstat_write(pp, TR_RECVING, (size_t)size, givenid,
+ from_host, givenhost);
+ continue;
+ }
+ frecverr("protocol screwup: %s", line);
+ /*NOTREACHED*/
+ }
+}
+
+/*
+ * Read files send by lpd and copy them to the spooling directory.
+ */
+static int
+readfile(struct printer *pp, char *file, size_t size)
+{
+ register char *cp;
+ char buf[SPL_BUFSIZ];
+ size_t amt, i;
+ int err, fd, j;
+
+ fd = open(file, O_CREAT|O_EXCL|O_WRONLY, FILMOD);
+ if (fd < 0) {
+ frecverr("%s: readfile: error on open(%s): %s",
+ pp->printer, file, strerror(errno));
+ /*NOTREACHED*/
+ }
+ ack();
+ err = 0;
+ for (i = 0; i < size; i += SPL_BUFSIZ) {
+ amt = SPL_BUFSIZ;
+ cp = buf;
+ if (i + amt > size)
+ amt = size - i;
+ do {
+ j = read(STDOUT_FILENO, cp, amt);
+ if (j <= 0) {
+ frecverr("%s: lost connection", pp->printer);
+ /*NOTREACHED*/
+ }
+ amt -= j;
+ cp += j;
+ } while (amt > 0);
+ amt = SPL_BUFSIZ;
+ if (i + amt > size)
+ amt = size - i;
+ if (write(fd, buf, amt) != (ssize_t)amt) {
+ err++;
+ break;
+ }
+ }
+ (void) close(fd);
+ if (err) {
+ frecverr("%s: write error on close(%s)", pp->printer, file);
+ /*NOTREACHED*/
+ }
+ if (noresponse()) { /* file sent had bad data in it */
+ if (strchr(file, '/') == NULL)
+ (void) unlink(file);
+ return (0);
+ }
+ ack();
+ return (1);
+}
+
+static int
+noresponse(void)
+{
+ char resp;
+
+ if (read(STDOUT_FILENO, &resp, (size_t)1) != 1) {
+ frecverr("lost connection in noresponse()");
+ /*NOTREACHED*/
+ }
+ if (resp == '\0')
+ return(0);
+ return(1);
+}
+
+/*
+ * Check to see if there is enough space on the disk for size bytes.
+ * 1 == OK, 0 == Not OK.
+ */
+static int
+chksize(int size)
+{
+ int64_t spacefree;
+ struct statfs sfb;
+
+ if (statfs(".", &sfb) < 0) {
+ syslog(LOG_ERR, "%s: %m", "statfs(\".\")");
+ return (1);
+ }
+ spacefree = sfb.f_bavail * (sfb.f_bsize / 512);
+ size = (size + 511) / 512;
+ if (minfree + size > spacefree)
+ return(0);
+ return(1);
+}
+
+static int
+read_number(const char *fn)
+{
+ char lin[80];
+ register FILE *fp;
+
+ if ((fp = fopen(fn, "r")) == NULL)
+ return (0);
+ if (fgets(lin, sizeof(lin), fp) == NULL) {
+ fclose(fp);
+ return (0);
+ }
+ fclose(fp);
+ return (atoi(lin));
+}
+
+/*
+ * Remove all the files associated with the current job being transfered.
+ */
+static void
+rcleanup(int signo __unused)
+{
+ if (tfname[0] && strchr(tfname, '/') == NULL)
+ (void) unlink(tfname);
+ if (dfname[0] && strchr(dfname, '/') == NULL) {
+ do {
+ do
+ (void) unlink(dfname);
+ while (dfname[2]-- != 'A');
+ dfname[2] = 'z';
+ } while (dfname[0]-- != 'd');
+ }
+ dfname[0] = '\0';
+}
+
+#include <stdarg.h>
+
+static void
+frecverr(const char *msg, ...)
+{
+ va_list ap;
+ va_start(ap, msg);
+ syslog(LOG_ERR, "Error receiving job from %s:", from_host);
+ vsyslog(LOG_ERR, msg, ap);
+ va_end(ap);
+ /*
+ * rcleanup is not called until AFTER logging the error message,
+ * because rcleanup will zap some variables which may have been
+ * supplied as parameters for that msg...
+ */
+ rcleanup(0);
+ /*
+ * Add a minimal delay before returning the final error code to
+ * the sending host. This just in case that machine responds
+ * this error by INSTANTLY retrying (and instantly re-failing...).
+ * It would be stupid of the sending host to do that, but if there
+ * was a broken implementation which did it, the result might be
+ * obscure performance problems and a flood of syslog messages on
+ * the receiving host.
+ */
+ sleep(2); /* a paranoid throttling measure */
+ putchar('\1'); /* return error code */
+ exit(1);
+}
diff --git a/usr.sbin/lpr/lpq/Makefile b/usr.sbin/lpr/lpq/Makefile
new file mode 100644
index 0000000..4df437e
--- /dev/null
+++ b/usr.sbin/lpr/lpq/Makefile
@@ -0,0 +1,16 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+BINDIR= /usr/bin
+
+PROG= lpq
+BINOWN= root
+BINGRP= daemon
+BINMODE= 6555
+
+CFLAGS+= -I${.CURDIR}/../common_source
+
+DPADD= ${LIBLPR}
+LDADD= ${LIBLPR}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/lpq/lpq.1 b/usr.sbin/lpr/lpq/lpq.1
new file mode 100644
index 0000000..3678256
--- /dev/null
+++ b/usr.sbin/lpr/lpq/lpq.1
@@ -0,0 +1,143 @@
+.\" Copyright (c) 1983, 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.
+.\" 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.
+.\"
+.\" @(#)lpq.1 8.2 (Berkeley) 4/28/95
+.\" $FreeBSD$
+.\"
+.Dd April 28, 1995
+.Dt LPQ 1
+.Os
+.Sh NAME
+.Nm lpq
+.Nd spool queue examination program
+.Sh SYNOPSIS
+.Nm
+.Op Fl a
+.Op Fl l
+.Op Fl P Ns Ar printer
+.Op job # ...\&
+.Op user ...\&
+.Sh DESCRIPTION
+The
+.Nm
+utility examines the spooling area used by
+.Xr lpd 8
+for printing files on the line printer, and reports the status of the
+specified jobs or all jobs associated with a user.
+The
+.Nm
+utility invoked
+without any arguments reports on any jobs currently in the queue.
+.Pp
+Options:
+.Pp
+.Bl -tag -width indent
+.It Fl P
+Specify a particular printer, otherwise the default
+line printer is used (or the value of the
+.Ev PRINTER
+variable in the
+environment).
+All other arguments supplied are interpreted as user
+names or job numbers to filter out only those jobs of interest.
+.It Fl l
+Information about each of the files comprising the job entry
+is printed.
+Normally, only as much information as will fit on one line is displayed.
+.It Fl a
+Report on the local queues for all printers,
+rather than just the specified printer.
+.El
+.Pp
+For each job submitted (i.e., invocation of
+.Xr lpr 1 )
+.Nm
+reports the user's name, current rank in the queue, the
+names of files comprising the job, the job identifier (a number which
+may be supplied to
+.Xr lprm 1
+for removing a specific job), and the total size in bytes.
+Job ordering is dependent on
+the algorithm used to scan the spooling directory and is supposed
+to be
+.Tn FIFO
+(First in First Out).
+File names comprising a job may be unavailable
+(when
+.Xr lpr 1
+is used as a sink in a pipeline) in which case the file
+is indicated as ``(standard input)''.
+.Pp
+If
+.Nm
+warns that there is no daemon present (i.e., due to some malfunction),
+the
+.Xr lpc 8
+command can be used to restart the printer daemon.
+.Sh ENVIRONMENT
+If the following environment variable exists, it is used by
+.Nm :
+.Bl -tag -width PRINTER
+.It Ev PRINTER
+Specifies an alternate default printer.
+.El
+.Sh FILES
+.Bl -tag -width "/var/spool/*/lock" -compact
+.It Pa /etc/printcap
+To determine printer characteristics.
+.It Pa /var/spool/*
+The spooling directory, as determined from printcap.
+.It Pa /var/spool/*/cf*
+Control files specifying jobs.
+.It Pa /var/spool/*/lock
+The lock file to obtain the currently active job.
+.El
+.Sh DIAGNOSTICS
+Unable to open various files.
+The lock file being malformed.
+Garbage
+files when there is no daemon active, but files in the spooling directory.
+.Sh SEE ALSO
+.Xr lpr 1 ,
+.Xr lprm 1 ,
+.Xr lpc 8 ,
+.Xr lpd 8
+.Sh HISTORY
+A
+.Nm
+utility appeared in
+.Bx 3 .
+.Sh BUGS
+Due to the dynamic nature of the information in the spooling directory
+.Nm
+may report unreliably.
+Output formatting is sensitive to the line length of the terminal;
+this can results in widely spaced columns.
diff --git a/usr.sbin/lpr/lpq/lpq.c b/usr.sbin/lpr/lpq/lpq.c
new file mode 100644
index 0000000..e1809cf
--- /dev/null
+++ b/usr.sbin/lpr/lpq/lpq.c
@@ -0,0 +1,199 @@
+/*
+ * 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.
+ */
+
+#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 */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)lpq.c 8.3 (Berkeley) 5/10/95";
+#endif /* not lint */
+#endif
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+__FBSDID("$FreeBSD$");
+
+/*
+ * Spool Queue examination program
+ *
+ * lpq [-a] [-l] [-Pprinter] [user...] [job...]
+ *
+ * -a show all non-null queues on the local machine
+ * -l long output
+ * -P used to identify printer as per lpr/lprm
+ */
+
+#include <sys/param.h>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "lp.h"
+#include "lp.local.h"
+#include "pathnames.h"
+
+int requ[MAXREQUESTS]; /* job number of spool entries */
+int requests; /* # of spool requests */
+char *user[MAXUSERS]; /* users to process */
+int users; /* # of users in user array */
+
+uid_t uid, euid;
+
+static int ckqueue(const struct printer *_pp);
+static void usage(void);
+int main(int _argc, char **_argv);
+
+int
+main(int argc, char **argv)
+{
+ int ch, aflag, lflag;
+ const char *printer;
+ struct printer myprinter, *pp = &myprinter;
+
+ printer = NULL;
+ euid = geteuid();
+ uid = getuid();
+ seteuid(uid);
+ progname = *argv;
+ if (gethostname(local_host, sizeof(local_host)))
+ err(1, "gethostname");
+ openlog("lpd", 0, LOG_LPR);
+
+ aflag = lflag = 0;
+ while ((ch = getopt(argc, argv, "alP:")) != -1)
+ switch((char)ch) {
+ case 'a':
+ ++aflag;
+ break;
+ case 'l': /* long output */
+ ++lflag;
+ break;
+ case 'P': /* printer name */
+ printer = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+ if (!aflag && printer == NULL && (printer = getenv("PRINTER")) == NULL)
+ printer = DEFLP;
+
+ for (argc -= optind, argv += optind; argc; --argc, ++argv)
+ if (isdigit(argv[0][0])) {
+ if (requests >= MAXREQUESTS)
+ fatal(0, "too many requests");
+ requ[requests++] = atoi(*argv);
+ }
+ else {
+ if (users >= MAXUSERS)
+ fatal(0, "too many users");
+ user[users++] = *argv;
+ }
+
+ if (aflag) {
+ int more, status;
+
+ more = firstprinter(pp, &status);
+ if (status)
+ goto looperr;
+ while (more) {
+ if (ckqueue(pp) > 0) {
+ printf("%s:\n", pp->printer);
+ displayq(pp, lflag);
+ printf("\n");
+ }
+ do {
+ more = nextprinter(pp, &status);
+looperr:
+ switch (status) {
+ case PCAPERR_TCOPEN:
+ printf("warning: %s: unresolved "
+ "tc= reference(s) ",
+ pp->printer);
+ case PCAPERR_SUCCESS:
+ break;
+ default:
+ fatal(pp, "%s", pcaperr(status));
+ }
+ } while (more && status);
+ }
+ } else {
+ int status;
+
+ init_printer(pp);
+ status = getprintcap(printer, pp);
+ if (status < 0)
+ fatal(pp, "%s", pcaperr(status));
+
+ displayq(pp, lflag);
+ }
+ exit(0);
+}
+
+static int
+ckqueue(const struct printer *pp)
+{
+ register struct dirent *d;
+ DIR *dirp;
+ char *spooldir;
+
+ spooldir = pp->spool_dir;
+ if ((dirp = opendir(spooldir)) == NULL)
+ return (-1);
+ while ((d = readdir(dirp)) != NULL) {
+ if (d->d_name[0] != 'c' || d->d_name[1] != 'f')
+ continue; /* daemon control files only */
+ closedir(dirp);
+ return (1); /* found something */
+ }
+ closedir(dirp);
+ return (0);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: lpq [-a] [-l] [-Pprinter] [user ...] [job ...]\n");
+ exit(1);
+}
diff --git a/usr.sbin/lpr/lpr/Makefile b/usr.sbin/lpr/lpr/Makefile
new file mode 100644
index 0000000..1ff376d
--- /dev/null
+++ b/usr.sbin/lpr/lpr/Makefile
@@ -0,0 +1,19 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../common_source
+
+BINDIR= /usr/bin
+
+PROG= lpr
+MAN= lpr.1 printcap.5
+BINOWN= root
+BINGRP= daemon
+BINMODE= 6555
+
+CFLAGS+= -I${.CURDIR}/../common_source
+
+DPADD= ${LIBLPR}
+LDADD= ${LIBLPR}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/lpr/lpr.1 b/usr.sbin/lpr/lpr/lpr.1
new file mode 100644
index 0000000..00146ac
--- /dev/null
+++ b/usr.sbin/lpr/lpr/lpr.1
@@ -0,0 +1,325 @@
+.\" Copyright (c) 1980, 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.
+.\" 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 @(#)lpr.1 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\" "
+.Dd June 6, 1993
+.Dt LPR 1
+.Os
+.Sh NAME
+.Nm lpr
+.Nd off line print
+.Sh SYNOPSIS
+.Nm
+.Op Fl P Ns Ar printer
+.Op Fl \&# Ns Ar num
+.Op Fl C Ar class
+.Op Fl J Ar job
+.Op Fl L Ar locale
+.Op Fl T Ar title
+.Op Fl U Ar user
+.Op Fl Z Ar daemon-options
+.Op Fl i Ar numcols
+.Op Fl 1234 Ar font
+.Op Fl w Ar num
+.Op Fl cdfghlnmprstv
+.Op Ar name ...
+.Sh DESCRIPTION
+The
+.Nm
+utility uses a spooling daemon to print the named files when facilities
+become available.
+If no names appear, the standard input is assumed.
+.Pp
+The following single letter options are used to notify the line printer
+spooler that the files are not standard text files.
+The spooling daemon will
+use the appropriate filters to print the data accordingly.
+Note that not all spoolers implement filters for all data types,
+and some sites may use these types for other purposes than the ones
+described here.
+.Bl -tag -width indent
+.It Fl d
+The files are assumed to contain data in
+.Tn DVI
+format from the
+.Tn TeX
+typesetting system.
+.It Fl f
+Use a filter which interprets the first character of each line as a
+standard
+.Tn FORTRAN
+carriage control character.
+.It Fl l
+Use a filter which allows control characters to be printed and suppresses
+page breaks.
+.It Fl p
+Use
+.Xr pr 1
+to format the files.
+.El
+.Pp
+The following options are historical and not directly supported by any
+software included in
+.Fx .
+.Bl -tag -width indent
+.It Fl c
+The files are assumed to contain data produced by
+.Xr cifplot 1 .
+.It Fl g
+The files are assumed to contain standard plot data as produced by the
+.Ux
+.Xr plot 3
+routines.
+.It Fl n
+The files are assumed to contain data from
+.Em ditroff
+(device independent troff).
+.It Fl t
+The files are assumed to contain
+.Tn C/A/T
+phototypesetter commands from ancient versions of
+.Ux
+.Xr troff 1 .
+.It Fl v
+The files are assumed to contain a raster image for devices like the
+Benson Varian.
+.El
+.Pp
+These options apply to the handling of
+the print job:
+.Bl -tag -width indent
+.It Fl P
+Force output to a specific printer.
+Normally,
+the default printer is used (site dependent), or the value of the
+environment variable
+.Ev PRINTER
+is used.
+.It Fl h
+Suppress the printing of the burst page.
+.It Fl m
+Send mail upon completion.
+.It Fl r
+Remove the file upon completion of spooling or upon completion of
+printing (with the
+.Fl s
+option).
+.It Fl s
+Use symbolic links.
+Usually files are copied to the spool directory.
+The
+.Fl s
+option will use
+.Xr symlink 2
+to link data files rather than trying to copy them so large files can be
+printed.
+This means the files should
+not be modified or removed until they have been printed.
+.El
+.Pp
+The remaining options apply to copies, the page display, and headers:
+.Bl -tag -width indent
+.It Fl \&# Ns Ar num
+The quantity
+.Ar num
+is the number of copies desired of each file named.
+For example,
+.Bd -literal -offset indent
+lpr \-#3 foo.c bar.c more.c
+.Ed
+would result in 3 copies of the file foo.c, followed by 3 copies
+of the file bar.c, etc.
+On the other hand,
+.Bd -literal -offset indent
+cat foo.c bar.c more.c \&| lpr \-#3
+.Ed
+.Pp
+will give three copies of the concatenation of the files.
+Often
+a site will disable this feature to encourage use of a photocopier
+instead.
+.It Xo
+.Fl Ns Op Cm 1234
+.Ar font
+.Xc
+Specifies a
+.Ar font
+to be mounted on font position
+.Ar i .
+The daemon
+will construct a
+.Li .railmag
+file referencing
+the font pathname.
+.It Fl C Ar class
+Job classification
+to use on the burst page.
+For example,
+.Bd -literal -offset indent
+lpr \-C EECS foo.c
+.Ed
+.Pp
+causes the system name (the name returned by
+.Xr hostname 1 )
+to be replaced on the burst page by
+.Tn EECS ,
+and the file foo.c to be printed.
+.It Fl J Ar job
+Job name to print on the burst page.
+Normally, the first file's name is used.
+.It Fl L Ar locale
+Use
+.Ar locale
+specified as argument instead of one found in environment.
+(Only effective when filtering through
+.Xr pr 1
+is requested using the
+.Fl p
+option.)
+.It Fl T Ar title
+Title name for
+.Xr pr 1 ,
+instead of the file name.
+.It Fl U Ar user
+User name to print on the burst page,
+also for accounting purposes.
+This option is only honored if the real user-id is daemon
+(or that specified in the printcap file instead of daemon),
+and is intended for those instances where print filters wish to requeue jobs.
+.It Fl Z Ar daemon-options
+Some spoolers, such as
+.Tn LPRng ,
+accept additional per-job options using a
+.Ql Z
+control line.
+When
+.Fl Z
+is specified, and
+.Fl p
+.Pq Xr pr 1
+is not requested, the specified
+.Ar daemon-options
+will be passed to the remote
+.Tn LPRng
+spooler.
+.It Fl i Ar numcols
+The output is indented by
+.Pq Ar numcols .
+.It Fl w Ar num
+Uses
+.Ar num
+as the page width for
+.Xr pr 1 .
+.El
+.Sh ENVIRONMENT
+If the following environment variable exists, it is used by
+.Nm :
+.Bl -tag -width PRINTER
+.It Ev PRINTER
+Specifies an alternate default printer.
+.El
+.Sh FILES
+.Bl -tag -width /var/spool/output/*/tf* -compact
+.It Pa /etc/passwd
+Personal identification.
+.It Pa /etc/printcap
+Printer capabilities data base.
+.It Pa /usr/sbin/lpd
+Line printer daemons.
+.It Pa /var/spool/output/*
+Directories used for spooling.
+.It Pa /var/spool/output/*/cf*
+Daemon control files.
+.It Pa /var/spool/output/*/df*
+Data files specified in "cf" files.
+.It Pa /var/spool/output/*/tf*
+Temporary copies of "cf" files.
+.El
+.Sh DIAGNOSTICS
+If you try to spool too large a file, it will be truncated.
+The
+.Nm
+utility will object to printing binary files.
+If a user other than root prints a file and spooling is disabled,
+.Nm
+will print a message saying so and will not put jobs in the queue.
+If a connection to
+.Xr lpd 8
+on the local machine cannot be made,
+.Nm
+will say that the daemon cannot be started.
+Diagnostics may be printed in the daemon's log file
+regarding missing spool files by
+.Xr lpd 8 .
+.Sh SEE ALSO
+.Xr lpq 1 ,
+.Xr lprm 1 ,
+.Xr pr 1 ,
+.Xr symlink 2 ,
+.Xr printcap 5 ,
+.Xr lpc 8 ,
+.Xr lpd 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 3 .
+.Sh BUGS
+Fonts for
+.Xr troff 1
+and
+.Tn TeX
+reside on the host with the printer.
+It is currently not possible to
+use local font libraries.
+.Pp
+The
+.Ql Z
+control file line is used for two different purposes; for
+standard
+.Fx
+.Xr lpd 8 ,
+it specifies a locale to be passed to
+.Xr pr 1 .
+For
+.Tn LPRng
+.Xr lpd 8 ,
+it specifies additional options to be interpreted by the spooler's
+input and output filters.
+When submitting jobs via
+.Nm ,
+.Fl p
+.Fl L Ar locale
+is used in the former context, and
+.Fl Z Ar daemon-options
+is used in the latter.
diff --git a/usr.sbin/lpr/lpr/lpr.c b/usr.sbin/lpr/lpr/lpr.c
new file mode 100644
index 0000000..98e9cea
--- /dev/null
+++ b/usr.sbin/lpr/lpr/lpr.c
@@ -0,0 +1,891 @@
+/*
+ * Copyright (c) 1983, 1989, 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.
+ * 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) 1983, 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)lpr.c 8.4 (Berkeley) 4/28/95";
+#endif /* not lint */
+#endif
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+__FBSDID("$FreeBSD$");
+
+/*
+ * lpr -- off line print
+ *
+ * Allows multiple printers and printers on remote machines by
+ * using information from a printer data base.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <netinet/in.h> /* N_BADMAG uses ntohl() */
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <a.out.h>
+#include <err.h>
+#include <locale.h>
+#include <signal.h>
+#include <syslog.h>
+#include <pwd.h>
+#include <grp.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include "lp.h"
+#include "lp.local.h"
+#include "pathnames.h"
+
+static char *cfname; /* daemon control files, linked from tf's */
+static char *class = local_host; /* class title on header page */
+static char *dfname; /* data files */
+static char *fonts[4]; /* troff font names */
+static char format = 'f'; /* format char for printing files */
+static int hdr = 1; /* print header or not (default is yes) */
+static int iflag; /* indentation wanted */
+static int inchar; /* location to increment char in file names */
+static int indent; /* amount to indent */
+static const char *jobname; /* job name on header page */
+static int mailflg; /* send mail */
+static int nact; /* number of jobs to act on */
+static int ncopies = 1; /* # of copies to make */
+static char *lpr_username; /* person sending the print job(s) */
+static int qflag; /* q job, but don't exec daemon */
+static int rflag; /* remove files upon completion */
+static int sflag; /* symbolic link flag */
+static int tfd; /* control file descriptor */
+static char *tfname; /* tmp copy of cf before linking */
+static char *title; /* pr'ing title */
+static char *locale; /* pr'ing locale */
+static int userid; /* user id */
+static char *Uflag; /* user name specified with -U flag */
+static char *width; /* width for versatec printing */
+static char *Zflag; /* extra filter options for LPRng servers */
+
+static struct stat statb;
+
+static void card(int _c, const char *_p2);
+static int checkwriteperm(const char *_file, const char *_directory);
+static void chkprinter(const char *_ptrname, struct printer *_pp);
+static void cleanup(int _signo);
+static void copy(const struct printer *_pp, int _f, const char _n[]);
+static char *itoa(int _i);
+static const char *linked(const char *_file);
+int main(int _argc, char *_argv[]);
+static char *lmktemp(const struct printer *_pp, const char *_id,
+ int _num, int len);
+static void mktemps(const struct printer *_pp);
+static int nfile(char *_n);
+static int test(const char *_file);
+static void usage(void);
+
+uid_t uid, euid;
+
+int
+main(int argc, char *argv[])
+{
+ struct passwd *pw;
+ struct group *gptr;
+ const char *arg, *cp, *printer;
+ char *p;
+ char buf[BUFSIZ];
+ int c, i, f, errs;
+ int ret, didlink;
+ struct stat stb;
+ struct stat statb1, statb2;
+ struct printer myprinter, *pp = &myprinter;
+
+ printer = NULL;
+ euid = geteuid();
+ uid = getuid();
+ seteuid(uid);
+ if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
+ signal(SIGHUP, cleanup);
+ if (signal(SIGINT, SIG_IGN) != SIG_IGN)
+ signal(SIGINT, cleanup);
+ if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
+ signal(SIGQUIT, cleanup);
+ if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
+ signal(SIGTERM, cleanup);
+
+ progname = argv[0];
+ gethostname(local_host, sizeof(local_host));
+ openlog("lpd", 0, LOG_LPR);
+
+ errs = 0;
+ while ((c = getopt(argc, argv,
+ ":#:1:2:3:4:C:J:L:P:T:U:Z:cdfghi:lnmprstvw:"))
+ != -1)
+ switch (c) {
+ case '#': /* n copies */
+ i = strtol(optarg, &p, 10);
+ if (*p)
+ errx(1, "Bad argument to -#, number expected");
+ if (i > 0)
+ ncopies = i;
+ break;
+
+ case '1': /* troff fonts */
+ case '2':
+ case '3':
+ case '4':
+ fonts[optopt - '1'] = optarg;
+ break;
+
+ case 'C': /* classification spec */
+ hdr++;
+ class = optarg;
+ break;
+
+ case 'J': /* job name */
+ hdr++;
+ jobname = optarg;
+ break;
+
+ case 'P': /* specifiy printer name */
+ printer = optarg;
+ break;
+
+ case 'L': /* pr's locale */
+ locale = optarg;
+ break;
+
+ case 'T': /* pr's title line */
+ title = optarg;
+ break;
+
+ case 'U': /* user name */
+ hdr++;
+ Uflag = optarg;
+ break;
+
+ case 'Z':
+ Zflag = optarg;
+ break;
+
+ case 'c': /* print cifplot output */
+ case 'd': /* print tex output (dvi files) */
+ case 'g': /* print graph(1G) output */
+ case 'l': /* literal output */
+ case 'n': /* print ditroff output */
+ case 't': /* print troff output (cat files) */
+ case 'p': /* print using ``pr'' */
+ case 'v': /* print vplot output */
+ format = optopt;
+ break;
+
+ case 'f': /* print fortran output */
+ format = 'r';
+ break;
+
+ case 'h': /* nulifiy header page */
+ hdr = 0;
+ break;
+
+ case 'i': /* indent output */
+ iflag++;
+ indent = strtol(optarg, &p, 10);
+ if (*p)
+ errx(1, "Bad argument to -i, number expected");
+ break;
+
+ case 'm': /* send mail when done */
+ mailflg++;
+ break;
+
+ case 'q': /* just queue job */
+ qflag++;
+ break;
+
+ case 'r': /* remove file when done */
+ rflag++;
+ break;
+
+ case 's': /* try to link files */
+ sflag++;
+ break;
+
+ case 'w': /* versatec page width */
+ width = optarg;
+ break;
+
+ case ':': /* catch "missing argument" error */
+ if (optopt == 'i') {
+ iflag++; /* -i without args is valid */
+ indent = 8;
+ } else
+ errs++;
+ break;
+
+ default:
+ errs++;
+ }
+ argc -= optind;
+ argv += optind;
+ if (errs)
+ usage();
+ if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
+ printer = DEFLP;
+ chkprinter(printer, pp);
+ if (pp->no_copies && ncopies > 1)
+ errx(1, "multiple copies are not allowed");
+ if (pp->max_copies > 0 && ncopies > pp->max_copies)
+ errx(1, "only %ld copies are allowed", pp->max_copies);
+ /*
+ * Get the identity of the person doing the lpr using the same
+ * algorithm as lprm. Actually, not quite -- lprm will override
+ * the login name with "root" if the user is running as root;
+ * the daemon actually checks for the string "root" in its
+ * permission checking. Sigh.
+ */
+ userid = getuid();
+ if (Uflag) {
+ if (userid != 0 && userid != pp->daemon_user)
+ errx(1, "only privileged users may use the `-U' flag");
+ lpr_username = Uflag; /* -U person doing 'lpr' */
+ } else {
+ lpr_username = getlogin(); /* person doing 'lpr' */
+ if (userid != pp->daemon_user || lpr_username == 0) {
+ if ((pw = getpwuid(userid)) == NULL)
+ errx(1, "Who are you?");
+ lpr_username = pw->pw_name;
+ }
+ }
+
+ /*
+ * Check for restricted group access.
+ */
+ if (pp->restrict_grp != NULL && userid != pp->daemon_user) {
+ if ((gptr = getgrnam(pp->restrict_grp)) == NULL)
+ errx(1, "Restricted group specified incorrectly");
+ if (gptr->gr_gid != getgid()) {
+ while (*gptr->gr_mem != NULL) {
+ if ((strcmp(lpr_username, *gptr->gr_mem)) == 0)
+ break;
+ gptr->gr_mem++;
+ }
+ if (*gptr->gr_mem == NULL)
+ errx(1, "Not a member of the restricted group");
+ }
+ }
+ /*
+ * Check to make sure queuing is enabled if userid is not root.
+ */
+ lock_file_name(pp, buf, sizeof buf);
+ if (userid && stat(buf, &stb) == 0 && (stb.st_mode & LFM_QUEUE_DIS))
+ errx(1, "Printer queue is disabled");
+ /*
+ * Initialize the control file.
+ */
+ mktemps(pp);
+ tfd = nfile(tfname);
+ seteuid(euid);
+ (void) fchown(tfd, pp->daemon_user, -1);
+ /* owned by daemon for protection */
+ seteuid(uid);
+ card('H', local_host);
+ card('P', lpr_username);
+ card('C', class);
+ if (hdr && !pp->no_header) {
+ if (jobname == NULL) {
+ if (argc == 0)
+ jobname = "stdin";
+ else
+ jobname = ((arg = strrchr(argv[0], '/'))
+ ? arg + 1 : argv[0]);
+ }
+ card('J', jobname);
+ card('L', lpr_username);
+ }
+ if (format != 'p' && Zflag != 0)
+ card('Z', Zflag);
+ if (iflag)
+ card('I', itoa(indent));
+ if (mailflg)
+ card('M', lpr_username);
+ if (format == 't' || format == 'n' || format == 'd')
+ for (i = 0; i < 4; i++)
+ if (fonts[i] != NULL)
+ card('1'+i, fonts[i]);
+ if (width != NULL)
+ card('W', width);
+ /*
+ * XXX
+ * Our use of `Z' here is incompatible with LPRng's
+ * use. We assume that the only use of our existing
+ * `Z' card is as shown for `p' format (pr) files.
+ */
+ if (format == 'p') {
+ char *s;
+
+ if (locale)
+ card('Z', locale);
+ else if ((s = setlocale(LC_TIME, "")) != NULL)
+ card('Z', s);
+ }
+
+ /*
+ * Read the files and spool them.
+ */
+ if (argc == 0)
+ copy(pp, 0, " ");
+ else while (argc--) {
+ if (argv[0][0] == '-' && argv[0][1] == '\0') {
+ /* use stdin */
+ copy(pp, 0, " ");
+ argv++;
+ continue;
+ }
+ if ((f = test(arg = *argv++)) < 0)
+ continue; /* file unreasonable */
+
+ if (sflag && (cp = linked(arg)) != NULL) {
+ (void) snprintf(buf, sizeof(buf), "%d %d", statb.st_dev,
+ statb.st_ino);
+ card('S', buf);
+ if (format == 'p')
+ card('T', title ? title : arg);
+ for (i = 0; i < ncopies; i++)
+ card(format, &dfname[inchar-2]);
+ card('U', &dfname[inchar-2]);
+ if (f)
+ card('U', cp);
+ card('N', arg);
+ dfname[inchar]++;
+ nact++;
+ continue;
+ }
+ if (sflag)
+ printf("%s: %s: not linked, copying instead\n",
+ progname, arg);
+
+ if (f) {
+ /*
+ * The user wants the file removed after it is copied
+ * to the spool area, so see if the file can be moved
+ * instead of copy/unlink'ed. This is much faster and
+ * uses less spool space than copying the file. This
+ * can be very significant when running services like
+ * samba, pcnfs, CAP, et al.
+ */
+ seteuid(euid);
+ didlink = 0;
+ /*
+ * There are several things to check to avoid any
+ * security issues. Some of these are redundant
+ * under BSD's, but are necessary when lpr is built
+ * under some other OS's (which I do do...)
+ */
+ if (lstat(arg, &statb1) < 0)
+ goto nohardlink;
+ if (S_ISLNK(statb1.st_mode))
+ goto nohardlink;
+ if (link(arg, dfname) != 0)
+ goto nohardlink;
+ didlink = 1;
+ /*
+ * Make sure the user hasn't tried to trick us via
+ * any race conditions
+ */
+ if (lstat(dfname, &statb2) < 0)
+ goto nohardlink;
+ if (statb1.st_dev != statb2.st_dev)
+ goto nohardlink;
+ if (statb1.st_ino != statb2.st_ino)
+ goto nohardlink;
+ /*
+ * Skip if the file already had multiple hard links,
+ * because changing the owner and access-bits would
+ * change ALL versions of the file
+ */
+ if (statb2.st_nlink > 2)
+ goto nohardlink;
+ /*
+ * If we can access and remove the original file
+ * without special setuid-ness then this method is
+ * safe. Otherwise, abandon the move and fall back
+ * to the (usual) copy method.
+ */
+ seteuid(uid);
+ ret = access(dfname, R_OK);
+ if (ret == 0)
+ ret = unlink(arg);
+ seteuid(euid);
+ if (ret != 0)
+ goto nohardlink;
+ /*
+ * Unlink of user file was successful. Change the
+ * owner and permissions, add entries to the control
+ * file, and skip the file copying step.
+ */
+ chown(dfname, pp->daemon_user, getegid());
+ chmod(dfname, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+ seteuid(uid);
+ if (format == 'p')
+ card('T', title ? title : arg);
+ for (i = 0; i < ncopies; i++)
+ card(format, &dfname[inchar-2]);
+ card('U', &dfname[inchar-2]);
+ card('N', arg);
+ nact++;
+ continue;
+ nohardlink:
+ if (didlink)
+ unlink(dfname);
+ seteuid(uid); /* restore old uid */
+ } /* end: if (f) */
+
+ if ((i = open(arg, O_RDONLY)) < 0) {
+ printf("%s: cannot open %s\n", progname, arg);
+ } else {
+ copy(pp, i, arg);
+ (void) close(i);
+ if (f && unlink(arg) < 0)
+ printf("%s: %s: not removed\n", progname, arg);
+ }
+ }
+
+ if (nact) {
+ (void) close(tfd);
+ tfname[inchar]--;
+ /*
+ * Touch the control file to fix position in the queue.
+ */
+ seteuid(euid);
+ if ((tfd = open(tfname, O_RDWR)) >= 0) {
+ char touch_c;
+
+ if (read(tfd, &touch_c, 1) == 1 &&
+ lseek(tfd, (off_t)0, 0) == 0 &&
+ write(tfd, &touch_c, 1) != 1) {
+ printf("%s: cannot touch %s\n", progname,
+ tfname);
+ tfname[inchar]++;
+ cleanup(0);
+ }
+ (void) close(tfd);
+ }
+ if (link(tfname, cfname) < 0) {
+ printf("%s: cannot rename %s\n", progname, cfname);
+ tfname[inchar]++;
+ cleanup(0);
+ }
+ unlink(tfname);
+ seteuid(uid);
+ if (qflag) /* just q things up */
+ exit(0);
+ if (!startdaemon(pp))
+ printf("jobs queued, but cannot start daemon.\n");
+ exit(0);
+ }
+ cleanup(0);
+ return (1);
+ /* NOTREACHED */
+}
+
+/*
+ * Create the file n and copy from file descriptor f.
+ */
+static void
+copy(const struct printer *pp, int f, const char n[])
+{
+ register int fd, i, nr, nc;
+ char buf[BUFSIZ];
+
+ if (format == 'p')
+ card('T', title ? title : n);
+ for (i = 0; i < ncopies; i++)
+ card(format, &dfname[inchar-2]);
+ card('U', &dfname[inchar-2]);
+ card('N', n);
+ fd = nfile(dfname);
+ nr = nc = 0;
+ while ((i = read(f, buf, BUFSIZ)) > 0) {
+ if (write(fd, buf, i) != i) {
+ printf("%s: %s: temp file write error\n", progname, n);
+ break;
+ }
+ nc += i;
+ if (nc >= BUFSIZ) {
+ nc -= BUFSIZ;
+ nr++;
+ if (pp->max_blocks > 0 && nr > pp->max_blocks) {
+ printf("%s: %s: copy file is too large\n",
+ progname, n);
+ break;
+ }
+ }
+ }
+ (void) close(fd);
+ if (nc==0 && nr==0)
+ printf("%s: %s: empty input file\n", progname,
+ f ? n : "stdin");
+ else
+ nact++;
+}
+
+/*
+ * Try and link the file to dfname. Return a pointer to the full
+ * path name if successful.
+ */
+static const char *
+linked(const char *file)
+{
+ register char *cp;
+ static char buf[MAXPATHLEN];
+ register int ret;
+
+ if (*file != '/') {
+ if (getcwd(buf, sizeof(buf)) == NULL)
+ return(NULL);
+ while (file[0] == '.') {
+ switch (file[1]) {
+ case '/':
+ file += 2;
+ continue;
+ case '.':
+ if (file[2] == '/') {
+ if ((cp = strrchr(buf, '/')) != NULL)
+ *cp = '\0';
+ file += 3;
+ continue;
+ }
+ }
+ break;
+ }
+ strncat(buf, "/", sizeof(buf) - strlen(buf) - 1);
+ strncat(buf, file, sizeof(buf) - strlen(buf) - 1);
+ file = buf;
+ }
+ seteuid(euid);
+ ret = symlink(file, dfname);
+ seteuid(uid);
+ return(ret ? NULL : file);
+}
+
+/*
+ * Put a line into the control file.
+ */
+static void
+card(int c, const char *p2)
+{
+ char buf[BUFSIZ];
+ register char *p1 = buf;
+ size_t len = 2;
+
+ *p1++ = c;
+ while ((c = *p2++) != '\0' && len < sizeof(buf)) {
+ *p1++ = (c == '\n') ? ' ' : c;
+ len++;
+ }
+ *p1++ = '\n';
+ write(tfd, buf, len);
+}
+
+/*
+ * Create a new file in the spool directory.
+ */
+static int
+nfile(char *n)
+{
+ register int f;
+ int oldumask = umask(0); /* should block signals */
+
+ seteuid(euid);
+ f = open(n, O_WRONLY | O_EXCL | O_CREAT, FILMOD);
+ (void) umask(oldumask);
+ if (f < 0) {
+ printf("%s: cannot create %s\n", progname, n);
+ cleanup(0);
+ }
+ if (fchown(f, userid, -1) < 0) {
+ printf("%s: cannot chown %s\n", progname, n);
+ cleanup(0); /* cleanup does exit */
+ }
+ seteuid(uid);
+ if (++n[inchar] > 'z') {
+ if (++n[inchar-2] == 't') {
+ printf("too many files - break up the job\n");
+ cleanup(0);
+ }
+ n[inchar] = 'A';
+ } else if (n[inchar] == '[')
+ n[inchar] = 'a';
+ return(f);
+}
+
+/*
+ * Cleanup after interrupts and errors.
+ */
+static void
+cleanup(int signo __unused)
+{
+ register int i;
+
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGTERM, SIG_IGN);
+ i = inchar;
+ seteuid(euid);
+ if (tfname)
+ do
+ unlink(tfname);
+ while (tfname[i]-- != 'A');
+ if (cfname)
+ do
+ unlink(cfname);
+ while (cfname[i]-- != 'A');
+ if (dfname)
+ do {
+ do
+ unlink(dfname);
+ while (dfname[i]-- != 'A');
+ dfname[i] = 'z';
+ } while (dfname[i-2]-- != 'd');
+ exit(1);
+}
+
+/*
+ * Test to see if this is a printable file.
+ * Return -1 if it is not, 0 if its printable, and 1 if
+ * we should remove it after printing.
+ */
+static int
+test(const char *file)
+{
+ struct exec execb;
+ size_t dlen;
+ int fd;
+ char *cp, *dirpath;
+
+ if (access(file, 4) < 0) {
+ printf("%s: cannot access %s\n", progname, file);
+ return(-1);
+ }
+ if (stat(file, &statb) < 0) {
+ printf("%s: cannot stat %s\n", progname, file);
+ return(-1);
+ }
+ if ((statb.st_mode & S_IFMT) == S_IFDIR) {
+ printf("%s: %s is a directory\n", progname, file);
+ return(-1);
+ }
+ if (statb.st_size == 0) {
+ printf("%s: %s is an empty file\n", progname, file);
+ return(-1);
+ }
+ if ((fd = open(file, O_RDONLY)) < 0) {
+ printf("%s: cannot open %s\n", progname, file);
+ return(-1);
+ }
+ /*
+ * XXX Shall we add a similar test for ELF?
+ */
+ if (read(fd, &execb, sizeof(execb)) == sizeof(execb) &&
+ !N_BADMAG(execb)) {
+ printf("%s: %s is an executable program", progname, file);
+ goto error1;
+ }
+ (void) close(fd);
+ if (rflag) {
+ /*
+ * aside: note that 'cp' is technically a 'const char *'
+ * (because it points into 'file'), even though strrchr
+ * returns a value of type 'char *'.
+ */
+ if ((cp = strrchr(file, '/')) == NULL) {
+ if (checkwriteperm(file,".") == 0)
+ return(1);
+ } else {
+ if (cp == file) {
+ fd = checkwriteperm(file,"/");
+ } else {
+ /* strlcpy will change the '/' to '\0' */
+ dlen = cp - file + 1;
+ dirpath = malloc(dlen);
+ strlcpy(dirpath, file, dlen);
+ fd = checkwriteperm(file, dirpath);
+ free(dirpath);
+ }
+ if (fd == 0)
+ return(1);
+ }
+ printf("%s: %s: is not removable by you\n", progname, file);
+ }
+ return(0);
+
+error1:
+ printf(" and is unprintable\n");
+ (void) close(fd);
+ return(-1);
+}
+
+static int
+checkwriteperm(const char *file, const char *directory)
+{
+ struct stat stats;
+ if (access(directory, W_OK) == 0) {
+ stat(directory, &stats);
+ if (stats.st_mode & S_ISVTX) {
+ stat(file, &stats);
+ if(stats.st_uid == userid) {
+ return(0);
+ }
+ } else return(0);
+ }
+ return(-1);
+}
+
+/*
+ * itoa - integer to string conversion
+ */
+static char *
+itoa(int i)
+{
+ static char b[10] = "########";
+ register char *p;
+
+ p = &b[8];
+ do
+ *p-- = i%10 + '0';
+ while (i /= 10);
+ return(++p);
+}
+
+/*
+ * Perform lookup for printer name or abbreviation --
+ */
+static void
+chkprinter(const char *ptrname, struct printer *pp)
+{
+ int status;
+
+ init_printer(pp);
+ status = getprintcap(ptrname, pp);
+ switch(status) {
+ case PCAPERR_OSERR:
+ case PCAPERR_TCLOOP:
+ errx(1, "%s: %s", ptrname, pcaperr(status));
+ case PCAPERR_NOTFOUND:
+ errx(1, "%s: unknown printer", ptrname);
+ case PCAPERR_TCOPEN:
+ warnx("%s: unresolved tc= reference(s)", ptrname);
+ }
+}
+
+/*
+ * Tell the user what we wanna get.
+ */
+static void
+usage(void)
+{
+ fprintf(stderr, "%s\n",
+"usage: lpr [-Pprinter] [-#num] [-C class] [-J job] [-T title] [-U user]\n"
+ "\t[-Z daemon-options] [-i[numcols]] [-i[numcols]] [-1234 font]\n"
+ "\t[-L locale] [-wnum] [-cdfghlnmprstv] [name ...]");
+ exit(1);
+}
+
+
+/*
+ * Make the temp files.
+ */
+static void
+mktemps(const struct printer *pp)
+{
+ register int len, fd, n;
+ register char *cp;
+ char buf[BUFSIZ];
+
+ (void) snprintf(buf, sizeof(buf), "%s/.seq", pp->spool_dir);
+ seteuid(euid);
+ if ((fd = open(buf, O_RDWR|O_CREAT, 0661)) < 0) {
+ printf("%s: cannot create %s\n", progname, buf);
+ exit(1);
+ }
+ if (flock(fd, LOCK_EX)) {
+ printf("%s: cannot lock %s\n", progname, buf);
+ exit(1);
+ }
+ seteuid(uid);
+ n = 0;
+ if ((len = read(fd, buf, sizeof(buf))) > 0) {
+ for (cp = buf; len--; ) {
+ if (*cp < '0' || *cp > '9')
+ break;
+ n = n * 10 + (*cp++ - '0');
+ }
+ }
+ len = strlen(pp->spool_dir) + strlen(local_host) + 8;
+ tfname = lmktemp(pp, "tf", n, len);
+ cfname = lmktemp(pp, "cf", n, len);
+ dfname = lmktemp(pp, "df", n, len);
+ inchar = strlen(pp->spool_dir) + 3;
+ n = (n + 1) % 1000;
+ (void) lseek(fd, (off_t)0, 0);
+ snprintf(buf, sizeof(buf), "%03d\n", n);
+ (void) write(fd, buf, strlen(buf));
+ (void) close(fd); /* unlocks as well */
+}
+
+/*
+ * Make a temp file name.
+ */
+static char *
+lmktemp(const struct printer *pp, const char *id, int num, int len)
+{
+ register char *s;
+
+ if ((s = malloc(len)) == NULL)
+ errx(1, "out of memory");
+ (void) snprintf(s, len, "%s/%sA%03d%s", pp->spool_dir, id, num,
+ local_host);
+ return(s);
+}
diff --git a/usr.sbin/lpr/lpr/printcap.5 b/usr.sbin/lpr/lpr/printcap.5
new file mode 100644
index 0000000..1ea6fea
--- /dev/null
+++ b/usr.sbin/lpr/lpr/printcap.5
@@ -0,0 +1,437 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.\" @(#)printcap.5 8.2 (Berkeley) 12/11/93
+.\" $FreeBSD$
+.\"
+.Dd October 11, 2000
+.Dt PRINTCAP 5
+.Os
+.Sh NAME
+.Nm printcap
+.Nd printer capability data base
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm Printcap
+function
+is a simplified version of the
+.Xr termcap 5
+data base
+used to describe line printers.
+The spooling system accesses the
+.Nm
+file every time it is used, allowing dynamic
+addition and deletion of printers.
+Each entry in the data base
+is used to describe one printer.
+This data base may not be
+substituted for, as is possible for
+.Xr termcap 5 ,
+because it may allow accounting to be bypassed.
+.Pp
+The default printer is normally
+.Em lp ,
+though the environment variable
+.Ev PRINTER
+may be used to override this.
+Each spooling utility supports an option,
+.Fl P Ar printer ,
+to allow explicit naming of a destination printer.
+.Pp
+Refer to the
+.%T "4.3 BSD Line Printer Spooler Manual"
+for a complete discussion on how to setup the database for a given printer.
+.Sh CAPABILITIES
+Refer to
+.Xr termcap 5
+for a description of the file layout.
+.Bl -column Namexxx Typexx "/var/spool/lpdxxxxx"
+.Sy "Name Type Default Description
+.It "af str" Ta Dv NULL Ta No "name of accounting file"
+.It "br num none if lp is a tty, set the baud rate"
+.Xr ( ioctl 2
+call)
+.It "cf str" Ta Dv NULL Ta No "cifplot data filter"
+.It "ct num 120 TCP connection timeout in seconds"
+.It "df str" Ta Dv NULL Ta No "tex data filter"
+.Tn ( DVI
+format)
+.It "ff str" Ta So Li \ef Sc Ta No "string to send for a form feed"
+.It "fo bool false print a form feed when device is opened"
+.It "gf str" Ta Dv NULL Ta No "graph data filter"
+.Xr ( plot 3
+format
+.It "hl bool false print the burst header page last"
+.It "ic bool false driver supports (non standard) ioctl to indent printout"
+.It "if str" Ta Dv NULL Ta No "name of text filter which does accounting"
+.It "lf str" Ta Pa /dev/console Ta No "error logging file name"
+.It "lo str" Ta Pa lock Ta No "name of lock file"
+.It "lp str" Ta Pa /dev/lp Ta No "device name to open for output, or" Em port Ns @ Ns Em machine No "to open a TCP socket"
+.It "mc num 0 maximum number of copies which can be requested on"
+.Xr lpr 1 ,
+zero = unlimited
+.It "ms str" Ta Dv NULL Ta No "if lp is a tty, a comma-separated,"
+.Xr stty 1 Ns -like
+list describing the tty modes
+.It "mx num 0 maximum file size (in"
+.Dv BUFSIZ
+blocks), zero = unlimited
+.It "nd str" Ta Dv NULL Ta No "next directory for list of queues (unimplemented)"
+.It "nf str" Ta Dv NULL Ta No "ditroff data filter (device independent troff)"
+.It "of str" Ta Dv NULL Ta No "name of output filtering program"
+.It "pc num 200 price per foot or page in hundredths of cents"
+.It "pl num 66 page length (in lines)"
+.It "pw num 132 page width (in characters)"
+.It "px num 0 page width in pixels (horizontal)"
+.It "py num 0 page length in pixels (vertical)"
+.It "rc bool false when sending to a remote host, resend copies (see below)"
+.It "rf str" Ta Dv NULL Ta No "filter for printing"
+.Tn FORTRAN
+style text files
+.It "rg str" Ta Dv NULL Ta No "restricted group. Only members of group allowed access"
+.It "rm str" Ta Dv NULL Ta No "machine name for remote printer"
+.It "rp str" Ta Pa lp Ta No "remote printer name argument"
+.It "rs bool false restrict remote users to those with local accounts"
+.It "rw bool false open the printer device for reading and writing"
+.It "sb bool false short banner (one line only)"
+.It "sc bool false suppress multiple copies"
+.It "sd str" Ta Pa /var/spool/lpd Ta No "spool directory"
+.It "sf bool false suppress form feeds"
+.It "sh bool false suppress printing of burst page header"
+.It "sr str" Ta Dv NULL Ta No "file name to hold statistics of each datafile as it is received"
+.It "ss str" Ta Dv NULL Ta No "file name to hold statistics of each datafile as it is sent"
+.It "st str" Ta Pa status Ta No "status file name"
+.It "tf str" Ta Dv NULL Ta No "troff data filter (cat phototypesetter)"
+.It "tr str" Ta Dv NULL Ta No "trailer string to print when queue empties"
+.It "vf str" Ta Dv NULL Ta No "raster image filter"
+.El
+.Pp
+Each two-letter capability has a human-readable alternate name.
+.Bl -column "Short form" "Long form"
+.Sy "Short form Long form"
+.It "af acct.file"
+.It "br tty.rate"
+.It "cf filt.cifplot"
+.It "ct remote.timeout"
+.It "df filt.dvi"
+.It "du daemon.user"
+.It "ff job.formfeed"
+.It "fo job.topofform"
+.It "gf filt.plot"
+.It "hl banner.last"
+.It "if filt.input"
+.It "lf spool.log"
+.It "lo spool.lock"
+.It "lp tty.device"
+.It "mc max.copies"
+.It "ms tty.mode"
+.It "mx max.blocks"
+.It "nf filt.ditroff"
+.It "of filt.output"
+.It "pc acct.price"
+.It "pl page.length"
+.It "pw page.width"
+.It "px page.pwidth"
+.It "py page.plength"
+.It "rc remote.resend_copies"
+.It "rf filt.fortran"
+.It "rg daemon.restrictgrp"
+.It "rm remote.host"
+.It "rp remote.queue"
+.It "rs daemon.restricted"
+.It "rw tty.rw"
+.It "sb banner.short"
+.It "sc job.no_copies"
+.It "sd spool.dir"
+.It "sf job.no_formfeed"
+.It "sh banner.disable"
+.It "sr stat.recv"
+.It "ss stat.send"
+.It "st spool.status"
+.It "tf filt.troff"
+.It "tr job.trailer"
+.It "vf filt.raster"
+.El
+.Pp
+If the local line printer driver supports indentation, the daemon
+must understand how to invoke it.
+.Sh FILTERS
+The
+.Xr lpd 8
+daemon creates a pipeline of
+.Em filters
+to process files for various printer types.
+The filters selected depend on the flags passed to
+.Xr lpr 1 .
+The pipeline set up is:
+.Bd -literal -offset indent
+p pr | if regular text + pr(1)
+none if regular text
+c cf cifplot
+d df DVI (tex)
+g gf plot(3)
+n nf ditroff
+f rf Fortran
+t tf troff
+v vf raster image
+.Ed
+.Pp
+The
+.Sy if
+filter is invoked with arguments:
+.Bd -ragged -offset indent
+.Cm if
+.Op Fl c
+.Fl w Ns Ar width
+.Fl l Ns Ar length
+.Fl i Ns Ar indent
+.Fl n Ar login
+.Fl h Ar host acct-file
+.Ed
+.Pp
+The
+.Fl c
+flag is passed only if the
+.Fl l
+flag (pass control characters literally)
+is specified to
+.Xr lpr 1 .
+The
+.Ar Width
+function
+and
+.Ar length
+specify the page width and length
+(from
+.Cm pw
+and
+.Cm pl
+respectively) in characters.
+The
+.Fl n
+and
+.Fl h
+parameters specify the login name and host name of the owner
+of the job respectively.
+The
+.Ar Acct-file
+function
+is passed from the
+.Cm af
+.Nm
+entry.
+.Pp
+If no
+.Cm if
+is specified,
+.Cm of
+is used instead,
+with the distinction that
+.Cm of
+is opened only once,
+while
+.Cm if
+is opened for every individual job.
+Thus,
+.Cm if
+is better suited to performing accounting.
+The
+.Cm of
+is only given the
+.Ar width
+and
+.Ar length
+flags.
+.Pp
+All other filters are called as:
+.Bd -ragged -offset indent
+.Nm filter
+.Fl x Ns Ar width
+.Fl y Ns Ar length
+.Fl n Ar login
+.Fl h Ar host acct-file
+.Ed
+.Pp
+where
+.Ar width
+and
+.Ar length
+are represented in pixels,
+specified by the
+.Cm px
+and
+.Cm py
+entries respectively.
+.Pp
+All filters take
+.Em stdin
+as the file,
+.Em stdout
+as the printer,
+may log either to
+.Em stderr
+or using
+.Xr syslog 3 ,
+and must not ignore
+.Dv SIGINT .
+.Sh REMOTE PRINTING
+When printing to a remote printer using
+.Cm rm ,
+it is possible to use either
+.Cm if
+or
+.Cm of .
+If both are specified,
+.Cm of
+is ignored.
+Both filters behave the same except that they are passed
+different arguments as above.
+Specifically, the output filter is
+terminated and restarted for each file transmitted.
+This is necessary
+in order to pass the resulting size to the remote
+.Xr lpd 8 .
+.Pp
+If the
+.Fl p
+flag was passed to
+.Xr lpr 1 ,
+.Xr pr 1
+is not executed locally, but is requested of the remote
+.Xr lpd 8 .
+Any input filtering via
+.Cm if
+will therefore happen before
+.Xr pr 1
+is executed rather than afterwards.
+.Pp
+There are some models of network printers which accept jobs from
+.Xr lpd 8 ,
+but they ignore the control file for a job and simply print
+each data file as it arrives at the printer.
+One side-effect of this behavior is that the printer will ignore any request
+for multiple copies as given with the
+.Fl #
+flag on the
+.Xr lpr 1
+command.
+The
+.Cm rc
+entry will cause
+.Xr lpd 8
+to resend each data file for each copy that the user
+originally requested.
+Note that the
+.Cm rc
+entry should only be specified on hosts which send jobs directly to
+the printer.
+.Pp
+If
+.Cm lp
+is specified as
+.Em port Ns @ Ns Em machine
+(and
+.Cm rm
+is not in use), print data will be sent directly to the given
+.Em port
+on the given
+.Em machine .
+.Sh TRANSFER STATISTICS
+When a print job is transfered to a remote machine (which might be
+another unix box, or may be a network printer), it may be useful
+to keep statistics on each transfer.
+The
+.Cm sr
+and
+.Cm ss
+options indicate filenames that lpd should use to store such
+statistics.
+A statistics line is written for each datafile of a
+job as the file is successfully transferred.
+The format of the
+line is the same for both the sending and receiving side of a
+transfer.
+.Pp
+Statistics on datafiles being received would be used on a print
+server, if you are interested in network performance between a
+variety of machines which are sending jobs to that print server.
+The print server could collect statistics on the speed of each
+print job as it arrived on the server.
+.Pp
+Statistics on datafiles being sent might be used as a minimal
+accounting record, when you want to know who sent which jobs to a
+remote printer, when they were sent, and how large (in bytes) the
+files were.
+This will not give include any idea of how many pages
+were printed, because there is no standard way to get that information
+back from a remote (network) printer in this case.
+.Sh LOGGING
+Error messages generated by the line printer programs themselves
+(that is, the
+.Xr lpd 8
+and related programs)
+are logged by
+.Xr syslog 3
+using the
+.Dv LPR
+facility.
+Messages printed on
+.Em stderr
+of one of the filters
+are sent to the corresponding
+.Cm lf
+file.
+The filters may, of course, use
+.Xr syslogd 8
+themselves.
+.Pp
+Error messages sent to the console have a carriage return and a line
+feed appended to them, rather than just a line feed.
+.Sh SEE ALSO
+.Xr lpq 1 ,
+.Xr lpr 1 ,
+.Xr lprm 1 ,
+.Xr hosts.lpd 5 ,
+.Xr termcap 5 ,
+.Xr chkprintcap 8 ,
+.Xr lpc 8 ,
+.Xr lpd 8 ,
+.Xr pac 8
+.Rs
+.%T "4.3 BSD Line Printer Spooler Manual"
+.Re
+.Sh HISTORY
+The
+.Nm
+file format appeared in
+.Bx 4.2 .
diff --git a/usr.sbin/lpr/lprm/Makefile b/usr.sbin/lpr/lprm/Makefile
new file mode 100644
index 0000000..44bc93a
--- /dev/null
+++ b/usr.sbin/lpr/lprm/Makefile
@@ -0,0 +1,18 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../common_source
+
+BINDIR= /usr/bin
+
+PROG= lprm
+BINOWN= root
+BINGRP= daemon
+BINMODE= 6555
+
+CFLAGS+= -I${.CURDIR}/../common_source
+
+DPADD= ${LIBLPR}
+LDADD= ${LIBLPR}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/lprm/lprm.1 b/usr.sbin/lpr/lprm/lprm.1
new file mode 100644
index 0000000..a194614
--- /dev/null
+++ b/usr.sbin/lpr/lprm/lprm.1
@@ -0,0 +1,152 @@
+.\" Copyright (c) 1983, 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.
+.\" 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.
+.\"
+.\" @(#)lprm.1 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\"
+.Dd June 6, 1993
+.Dt LPRM 1
+.Os
+.Sh NAME
+.Nm lprm
+.Nd remove jobs from the line printer spooling queue
+.Sh SYNOPSIS
+.Nm
+.Op Fl P Ns Ar printer
+.Op Fl
+.Op job # ...\&
+.Op Ar user ...\&
+.Sh DESCRIPTION
+The
+.Nm
+utility will remove a job, or jobs, from a printer's spool queue.
+Since the spooling directory is protected from users, using
+.Nm
+is normally the only method by which a user may remove a job.
+The owner of a job is determined by the user's login name
+and host name on the machine where the
+.Xr lpr 1
+command was invoked.
+.Pp
+Options and arguments:
+.Bl -tag -width indent
+.It Fl P Ns Ar printer
+Specify the queue associated with a specific
+.Ar printer
+(otherwise the default printer is used).
+.It Fl
+If a single
+.Sq Fl
+is given,
+.Nm
+will remove all jobs which a user
+owns.
+If the super-user employs this flag, the spool queue will
+be emptied entirely.
+.It Ar user
+Cause
+.Nm
+to attempt to remove any jobs queued belonging to that user
+(or users).
+This form of invoking
+.Nm
+is useful only to the super-user.
+.It Ar job\ \&#
+A user may dequeue an individual job by specifying its job number.
+This number may be obtained from the
+.Xr lpq 1
+program, e.g.\&
+.Pp
+.Bd -literal -offset indent
+\&% lpq \-l
+
+1st:ken [job #013ucbarpa]
+ (standard input) 100 bytes
+% lprm 13
+.Ed
+.El
+.Pp
+If neither arguments or options are given,
+.Nm
+will delete the currently active job if it is
+owned by the user who invoked
+.Nm .
+.Pp
+The
+.Nm
+utility announces the names of any files it removes and is silent if
+there are no jobs in the queue which match the request list.
+.Pp
+The
+.Nm
+utility will kill off an active daemon, if necessary, before removing
+any spooling files.
+If a daemon is killed, a new one is
+automatically restarted upon completion of file removals.
+.Sh ENVIRONMENT
+If the following environment variable exists, it is utilized by
+.Nm .
+.Bl -tag -width PRINTER
+.It Ev PRINTER
+If the environment variable
+.Ev PRINTER
+exists,
+and a printer has not been specified with the
+.Fl P
+option,
+the default printer is assumed from
+.Ev PRINTER .
+.El
+.Sh FILES
+.Bl -tag -width /var/spool/*/lock/ -compact
+.It Pa /etc/printcap
+Printer characteristics file.
+.It Pa /var/spool/*
+Spooling directories.
+.It Pa /var/spool/*/lock
+Lock file used to obtain the pid of the current
+daemon and the job number of the currently active job.
+.El
+.Sh DIAGNOSTICS
+``Permission denied" if the user tries to remove files other than his
+own.
+.Sh SEE ALSO
+.Xr lpq 1 ,
+.Xr lpr 1 ,
+.Xr lpd 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 3.0 .
+.Sh BUGS
+Since there are race conditions possible in the update of the lock file,
+the currently active job may be incorrectly identified.
diff --git a/usr.sbin/lpr/lprm/lprm.c b/usr.sbin/lpr/lprm/lprm.c
new file mode 100644
index 0000000..47f233c
--- /dev/null
+++ b/usr.sbin/lpr/lprm/lprm.c
@@ -0,0 +1,165 @@
+/*
+ * 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.
+ */
+
+#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 */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)lprm.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+#endif
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+__FBSDID("$FreeBSD$");
+
+/*
+ * lprm - remove the current user's spool entry
+ *
+ * lprm [-] [[job #] [user] ...]
+ *
+ * Using information in the lock file, lprm will kill the
+ * currently active daemon (if necessary), remove the associated files,
+ * and startup a new daemon. Priviledged users may remove anyone's spool
+ * entries, otherwise one can only remove their own.
+ */
+
+#include <sys/param.h>
+
+#include <syslog.h>
+#include <dirent.h>
+#include <pwd.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "lp.h"
+#include "lp.local.h"
+
+/*
+ * Stuff for handling job specifications
+ */
+char *person; /* name of person doing lprm */
+int requ[MAXREQUESTS]; /* job number of spool entries */
+int requests; /* # of spool requests */
+char *user[MAXUSERS]; /* users to process */
+int users; /* # of users in user array */
+uid_t uid, euid; /* real and effective user id's */
+
+static char luser[16]; /* buffer for person */
+
+int main(int argc, char *_argv[]);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ char *arg;
+ const char *printer;
+ struct passwd *p;
+ static char root[] = "root";
+
+ printer = NULL;
+ uid = getuid();
+ euid = geteuid();
+ seteuid(uid); /* be safe */
+ progname = argv[0];
+ gethostname(local_host, sizeof(local_host));
+ openlog("lpd", 0, LOG_LPR);
+
+ /*
+ * Bogus code later checks for string equality between
+ * `person' and "root", so if we are root, better make sure
+ * that code will succeed.
+ */
+ if (getuid() == 0) {
+ person = root;
+ } else if ((person = getlogin()) == NULL) {
+ if ((p = getpwuid(getuid())) == NULL)
+ fatal(0, "Who are you?");
+ if (strlen(p->pw_name) >= sizeof(luser))
+ fatal(0, "Your name is too long");
+ strcpy(luser, p->pw_name);
+ person = luser;
+ }
+ while (--argc) {
+ if ((arg = *++argv)[0] == '-')
+ switch (arg[1]) {
+ case 'P':
+ if (arg[2])
+ printer = &arg[2];
+ else if (argc > 1) {
+ argc--;
+ printer = *++argv;
+ }
+ break;
+ case '\0':
+ if (!users) {
+ users = -1;
+ break;
+ }
+ default:
+ usage();
+ }
+ else {
+ if (users < 0)
+ usage();
+ if (isdigit(arg[0])) {
+ if (requests >= MAXREQUESTS)
+ fatal(0, "Too many requests");
+ requ[requests++] = atoi(arg);
+ } else {
+ if (users >= MAXUSERS)
+ fatal(0, "Too many users");
+ user[users++] = arg;
+ }
+ }
+ }
+ if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
+ printer = DEFLP;
+
+ rmjob(printer);
+ exit(0);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: lprm [-] [-Pprinter] [[job #] [user] ...]\n");
+ exit(2);
+}
diff --git a/usr.sbin/lpr/lptest/Makefile b/usr.sbin/lpr/lptest/Makefile
new file mode 100644
index 0000000..65b1c7c
--- /dev/null
+++ b/usr.sbin/lpr/lptest/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= lptest
+
+CFLAGS+= -I${.CURDIR}/../common_source
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/lptest/lptest.1 b/usr.sbin/lpr/lptest/lptest.1
new file mode 100644
index 0000000..c34934e
--- /dev/null
+++ b/usr.sbin/lpr/lptest/lptest.1
@@ -0,0 +1,77 @@
+.\" Copyright (c) 1985, 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.
+.\" 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.
+.\"
+.\" @(#)lptest.1 8.2 (Berkeley) 12/30/93
+.\" $FreeBSD$
+.\"
+.Dd December 30, 1993
+.Dt LPTEST 1
+.Os
+.Sh NAME
+.Nm lptest
+.Nd generate lineprinter ripple pattern
+.Sh SYNOPSIS
+.Nm
+.Op Ar length
+.Op Ar count
+.Sh DESCRIPTION
+The
+.Nm
+utility writes the traditional "ripple test" pattern on standard output.
+In 96 lines,
+this pattern will print all 96 printable
+.Tn ASCII
+characters
+in each position.
+While originally created to test printers, it is quite
+useful for testing terminals,
+driving terminal ports for debugging purposes,
+or any other task where a quick supply of random data is needed.
+.Pp
+The
+.Ar length
+argument specifies the output line length if the default
+length of 79 is inappropriate.
+.Pp
+The
+.Ar count
+argument specifies the number of output lines to be generated if
+the default count of 200 is inappropriate.
+Note that if
+.Ar count
+is to be specified,
+.Ar length
+must be also be specified.
+.Sh HISTORY
+A
+.Nm
+utility appeared in
+.Bx 4.3 .
diff --git a/usr.sbin/lpr/lptest/lptest.c b/usr.sbin/lpr/lptest/lptest.c
new file mode 100644
index 0000000..3042963
--- /dev/null
+++ b/usr.sbin/lpr/lptest/lptest.c
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+#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 */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)lptest.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+#endif
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+__FBSDID("$FreeBSD$");
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+
+/*
+ * lptest -- line printer test program (and other devices).
+ */
+int
+main(int argc, char **argv)
+{
+ int len, count;
+ register int i, j, fc, nc;
+ char outbuf[BUFSIZ];
+
+ setbuf(stdout, outbuf);
+ if (argc >= 2)
+ len = atoi(argv[1]);
+ else
+ len = 79;
+ if (argc >= 3)
+ count = atoi(argv[2]);
+ else
+ count = 200;
+ fc = ' ';
+ for (i = 0; i < count; i++) {
+ if (++fc == 0177)
+ fc = ' ';
+ nc = fc;
+ for (j = 0; j < len; j++) {
+ if (putchar(nc) == EOF)
+ err(1, "Write error");
+ if (++nc == 0177)
+ nc = ' ';
+ }
+ if (putchar('\n') == EOF)
+ err(1, "Write error");
+ }
+ (void) fflush(stdout);
+ exit(0);
+}
diff --git a/usr.sbin/lpr/pac/Makefile b/usr.sbin/lpr/pac/Makefile
new file mode 100644
index 0000000..bd895a7
--- /dev/null
+++ b/usr.sbin/lpr/pac/Makefile
@@ -0,0 +1,14 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../common_source
+
+PROG= pac
+MAN= pac.8
+
+CFLAGS+= -I${.CURDIR}/../common_source
+
+DPADD= ${LIBLPR}
+LDADD= ${LIBLPR}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/pac/pac.8 b/usr.sbin/lpr/pac/pac.8
new file mode 100644
index 0000000..b6e10a0
--- /dev/null
+++ b/usr.sbin/lpr/pac/pac.8
@@ -0,0 +1,109 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.\" @(#)pac.8 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\"
+.Dd June 6, 1993
+.Dt PAC 8
+.Os
+.Sh NAME
+.Nm pac
+.Nd printer/plotter accounting information
+.Sh SYNOPSIS
+.Nm
+.Op Fl P Ns Ar printer
+.Op Fl c
+.Op Fl m
+.Op Fl p Ns Ar price
+.Op Fl s
+.Op Fl r
+.Op Ar name ...
+.Sh DESCRIPTION
+The
+.Nm
+utility reads the printer/plotter accounting files, accumulating the number
+of pages (the usual case) or feet (for raster devices)
+of paper consumed by each user, and printing out
+how much each user consumed in pages or feet and dollars.
+.Pp
+Options and operands available:
+.Bl -tag -width PPprinter
+.It Fl P Ns Ar printer
+Accounting is done for the named printer.
+Normally, accounting is done for the default printer (site dependent) or
+the value of the environment variable
+.Ev PRINTER
+is used.
+.It Fl c
+Cause the output to be sorted by cost; usually the
+output is sorted alphabetically by name.
+.It Fl m
+Cause the host name to be ignored in the accounting file.
+This
+allows for a user on multiple machines to have all of his printing
+charges grouped together.
+.It Fl p Ns Ar price
+The value
+.Ar price
+is used for the cost in dollars instead of the default value of 0.02
+or the price specified in
+.Pa /etc/printcap .
+.It Fl r
+Reverse the sorting order.
+.It Fl s
+Accounting information is summarized on the
+summary accounting file; this summarization is necessary since on a
+busy system, the accounting file can grow by several lines per day.
+.It Ar names
+Statistics are only printed for user(s)
+.Ar name ;
+usually, statistics are printed for every user who has used any paper.
+.El
+.Sh FILES
+.Bl -tag -width /var/account/?_sum -compact
+.It Pa /var/account/?acct
+raw accounting files
+.It Pa /var/account/?_sum
+summary accounting files
+.It Pa /etc/printcap
+printer capability data base
+.El
+.Sh SEE ALSO
+.Xr printcap 5
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.0 .
+.Sh BUGS
+The relationship between the computed price and reality is
+as yet unknown.
diff --git a/usr.sbin/lpr/pac/pac.c b/usr.sbin/lpr/pac/pac.c
new file mode 100644
index 0000000..34a247f
--- /dev/null
+++ b/usr.sbin/lpr/pac/pac.c
@@ -0,0 +1,454 @@
+/*
+ * 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.
+ */
+
+#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 */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)pac.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+#endif
+
+#include "lp.cdefs.h" /* A cross-platform version of <sys/cdefs.h> */
+__FBSDID("$FreeBSD$");
+
+/*
+ * Do Printer accounting summary.
+ * Currently, usage is
+ * pac [-Pprinter] [-pprice] [-s] [-r] [-c] [-m] [user ...]
+ * to print the usage information for the named people.
+ */
+
+#include <sys/param.h>
+
+#include <dirent.h>
+#include <err.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include "lp.h"
+#include "lp.local.h"
+
+static char *acctfile; /* accounting file (input data) */
+static int allflag = 1; /* Get stats on everybody */
+static int errs;
+static size_t hcount; /* Count of hash entries */
+static int mflag = 0; /* disregard machine names */
+static int pflag = 0; /* 1 if -p on cmd line */
+static float price = 0.02; /* cost per page (or what ever) */
+static int reverse; /* Reverse sort order */
+static int sort; /* Sort by cost */
+static char *sumfile; /* summary file */
+static int summarize; /* Compress accounting file */
+
+uid_t uid, euid;
+
+/*
+ * Grossness follows:
+ * Names to be accumulated are hashed into the following
+ * table.
+ */
+
+#define HSHSIZE 97 /* Number of hash buckets */
+
+struct hent {
+ struct hent *h_link; /* Forward hash link */
+ char *h_name; /* Name of this user */
+ float h_feetpages; /* Feet or pages of paper */
+ int h_count; /* Number of runs */
+};
+
+static struct hent *hashtab[HSHSIZE]; /* Hash table proper */
+
+int main(int argc, char **_argv);
+static void account(FILE *_acctf);
+static int any(int _ch, const char _str[]);
+static int chkprinter(const char *_ptrname);
+static void dumpit(void);
+static int hash(const char _name[]);
+static struct hent *enter(const char _name[]);
+static struct hent *lookup(const char _name[]);
+static int qucmp(const void *_a, const void *_b);
+static void rewrite(void);
+static void usage(void);
+
+int
+main(int argc, char **argv)
+{
+ FILE *acctf;
+ const char *cp, *printer;
+
+ printer = NULL;
+ euid = geteuid(); /* these aren't used in pac(1) */
+ uid = getuid();
+ while (--argc) {
+ cp = *++argv;
+ if (*cp++ == '-') {
+ switch(*cp++) {
+ case 'P':
+ /*
+ * Printer name.
+ */
+ printer = cp;
+ continue;
+
+ case 'p':
+ /*
+ * get the price.
+ */
+ price = atof(cp);
+ pflag = 1;
+ continue;
+
+ case 's':
+ /*
+ * Summarize and compress accounting file.
+ */
+ summarize++;
+ continue;
+
+ case 'c':
+ /*
+ * Sort by cost.
+ */
+ sort++;
+ continue;
+
+ case 'm':
+ /*
+ * disregard machine names for each user
+ */
+ mflag = 1;
+ continue;
+
+ case 'r':
+ /*
+ * Reverse sorting order.
+ */
+ reverse++;
+ continue;
+
+ default:
+ usage();
+ }
+ }
+ (void) enter(--cp);
+ allflag = 0;
+ }
+ if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
+ printer = DEFLP;
+ if (!chkprinter(printer)) {
+ printf("pac: unknown printer %s\n", printer);
+ exit(2);
+ }
+
+ if ((acctf = fopen(acctfile, "r")) == NULL) {
+ perror(acctfile);
+ exit(1);
+ }
+ account(acctf);
+ fclose(acctf);
+ if ((acctf = fopen(sumfile, "r")) != NULL) {
+ account(acctf);
+ fclose(acctf);
+ }
+ if (summarize)
+ rewrite();
+ else
+ dumpit();
+ exit(errs);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: pac [-Pprinter] [-pprice] [-s] [-c] [-r] [-m] [user ...]\n");
+ exit(1);
+}
+
+/*
+ * Read the entire accounting file, accumulating statistics
+ * for the users that we have in the hash table. If allflag
+ * is set, then just gather the facts on everyone.
+ * Note that we must accomodate both the active and summary file
+ * formats here.
+ * Host names are ignored if the -m flag is present.
+ */
+static void
+account(FILE *acctf)
+{
+ char linebuf[BUFSIZ];
+ double t;
+ register char *cp, *cp2;
+ register struct hent *hp;
+ register int ic;
+
+ while (fgets(linebuf, BUFSIZ, acctf) != NULL) {
+ cp = linebuf;
+ while (any(*cp, " \t"))
+ cp++;
+ t = atof(cp);
+ while (any(*cp, ".0123456789"))
+ cp++;
+ while (any(*cp, " \t"))
+ cp++;
+ for (cp2 = cp; !any(*cp2, " \t\n"); cp2++)
+ ;
+ ic = atoi(cp2);
+ *cp2 = '\0';
+ if (mflag && strchr(cp, ':'))
+ cp = strchr(cp, ':') + 1;
+ hp = lookup(cp);
+ if (hp == NULL) {
+ if (!allflag)
+ continue;
+ hp = enter(cp);
+ }
+ hp->h_feetpages += t;
+ if (ic)
+ hp->h_count += ic;
+ else
+ hp->h_count++;
+ }
+}
+
+/*
+ * Sort the hashed entries by name or footage
+ * and print it all out.
+ */
+static void
+dumpit(void)
+{
+ struct hent **base;
+ register struct hent *hp, **ap;
+ register int hno, runs;
+ size_t c;
+ float feet;
+
+ hp = hashtab[0];
+ hno = 1;
+ base = (struct hent **) calloc(sizeof hp, hcount);
+ for (ap = base, c = hcount; c--; ap++) {
+ while (hp == NULL)
+ hp = hashtab[hno++];
+ *ap = hp;
+ hp = hp->h_link;
+ }
+ qsort(base, hcount, sizeof hp, qucmp);
+ printf(" Login pages/feet runs price\n");
+ feet = 0.0;
+ runs = 0;
+ for (ap = base, c = hcount; c--; ap++) {
+ hp = *ap;
+ runs += hp->h_count;
+ feet += hp->h_feetpages;
+ printf("%-24s %7.2f %4d $%6.2f\n", hp->h_name,
+ hp->h_feetpages, hp->h_count, hp->h_feetpages * price);
+ }
+ if (allflag) {
+ printf("\n");
+ printf("%-24s %7.2f %4d $%6.2f\n", "total", feet,
+ runs, feet * price);
+ }
+}
+
+/*
+ * Rewrite the summary file with the summary information we have accumulated.
+ */
+static void
+rewrite(void)
+{
+ register struct hent *hp;
+ register int i;
+ FILE *acctf;
+
+ if ((acctf = fopen(sumfile, "w")) == NULL) {
+ warn("%s", sumfile);
+ errs++;
+ return;
+ }
+ for (i = 0; i < HSHSIZE; i++) {
+ hp = hashtab[i];
+ while (hp != NULL) {
+ fprintf(acctf, "%7.2f\t%s\t%d\n", hp->h_feetpages,
+ hp->h_name, hp->h_count);
+ hp = hp->h_link;
+ }
+ }
+ fflush(acctf);
+ if (ferror(acctf)) {
+ warn("%s", sumfile);
+ errs++;
+ }
+ fclose(acctf);
+ if ((acctf = fopen(acctfile, "w")) == NULL)
+ warn("%s", acctfile);
+ else
+ fclose(acctf);
+}
+
+/*
+ * Hashing routines.
+ */
+
+/*
+ * Enter the name into the hash table and return the pointer allocated.
+ */
+
+static struct hent *
+enter(const char name[])
+{
+ register struct hent *hp;
+ register int h;
+
+ if ((hp = lookup(name)) != NULL)
+ return(hp);
+ h = hash(name);
+ hcount++;
+ hp = (struct hent *) calloc(sizeof *hp, (size_t)1);
+ hp->h_name = (char *) calloc(sizeof(char), strlen(name)+1);
+ strcpy(hp->h_name, name);
+ hp->h_feetpages = 0.0;
+ hp->h_count = 0;
+ hp->h_link = hashtab[h];
+ hashtab[h] = hp;
+ return(hp);
+}
+
+/*
+ * Lookup a name in the hash table and return a pointer
+ * to it.
+ */
+
+static struct hent *
+lookup(const char name[])
+{
+ register int h;
+ register struct hent *hp;
+
+ h = hash(name);
+ for (hp = hashtab[h]; hp != NULL; hp = hp->h_link)
+ if (strcmp(hp->h_name, name) == 0)
+ return(hp);
+ return(NULL);
+}
+
+/*
+ * Hash the passed name and return the index in
+ * the hash table to begin the search.
+ */
+static int
+hash(const char name[])
+{
+ register int h;
+ register const char *cp;
+
+ for (cp = name, h = 0; *cp; h = (h << 2) + *cp++)
+ ;
+ return((h & 0x7fffffff) % HSHSIZE);
+}
+
+/*
+ * Other stuff
+ */
+static int
+any(int ch, const char str[])
+{
+ register int c = ch;
+ register const char *cp = str;
+
+ while (*cp)
+ if (*cp++ == c)
+ return(1);
+ return(0);
+}
+
+/*
+ * The qsort comparison routine.
+ * The comparison is ascii collating order
+ * or by feet of typesetter film, according to sort.
+ */
+static int
+qucmp(const void *a, const void *b)
+{
+ register const struct hent *h1, *h2;
+ register int r;
+
+ h1 = *(const struct hent * const *)a;
+ h2 = *(const struct hent * const *)b;
+ if (sort)
+ r = h1->h_feetpages < h2->h_feetpages ?
+ -1 : h1->h_feetpages > h2->h_feetpages;
+ else
+ r = strcmp(h1->h_name, h2->h_name);
+ return(reverse ? -r : r);
+}
+
+/*
+ * Perform lookup for printer name or abbreviation --
+ */
+static int
+chkprinter(const char *ptrname)
+{
+ int stat;
+ struct printer myprinter, *pp = &myprinter;
+
+ init_printer(&myprinter);
+ stat = getprintcap(ptrname, pp);
+ switch(stat) {
+ case PCAPERR_OSERR:
+ printf("pac: getprintcap: %s\n", pcaperr(stat));
+ exit(3);
+ case PCAPERR_NOTFOUND:
+ return 0;
+ case PCAPERR_TCLOOP:
+ fatal(pp, "%s", pcaperr(stat));
+ }
+ if ((acctfile = pp->acct_file) == NULL)
+ errx(3, "accounting not enabled for printer %s", ptrname);
+ if (!pflag && pp->price100)
+ price = pp->price100/10000.0;
+ sumfile = (char *) calloc(sizeof(char), strlen(acctfile)+5);
+ if (sumfile == NULL)
+ errx(1, "calloc failed");
+ strcpy(sumfile, acctfile);
+ strcat(sumfile, "_sum");
+ return(1);
+}
diff --git a/usr.sbin/lptcontrol/Makefile b/usr.sbin/lptcontrol/Makefile
new file mode 100644
index 0000000..3f7a939
--- /dev/null
+++ b/usr.sbin/lptcontrol/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= lptcontrol
+MAN= lptcontrol.8
+
+WARNS?= 6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lptcontrol/lptcontrol.8 b/usr.sbin/lptcontrol/lptcontrol.8
new file mode 100644
index 0000000..7723078
--- /dev/null
+++ b/usr.sbin/lptcontrol/lptcontrol.8
@@ -0,0 +1,90 @@
+.\"
+.\" lptcontrol - a utility for manipulating the lpt driver
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" $FreeBSD$
+.Dd October 1, 2004
+.Dt LPTCONTROL 8
+.Os
+.Sh NAME
+.Nm lptcontrol
+.Nd a utility for manipulating the lpt printer driver
+.Sh SYNOPSIS
+.Nm
+.Fl e | i | p | s
+.Op Fl d Ar control_device
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to set either the interrupt-driven, extended, standard,
+or polling mode of individual
+.Xr lpt 4
+devices.
+When a printer is switched from
+a mode to another, this change will only take effect
+the next time the device is opened.
+.Pp
+Extended mode is anything the parallel port interface can support.
+For an
+ECP/ISA parallel port, it may be FIFO+DMA or ECP.
+.Pp
+The following command line options are supported:
+.Bl -tag -width indent
+.It Fl e
+Turn on extended mode.
+.It Fl i
+Turn on interrupt-driven mode.
+.It Fl p
+Turn on polled mode.
+.It Fl s
+Turn on standard mode, i.e., turn off extended mode.
+.It Fl d Ar control_device
+Set the mode of the printer control device specified by
+.Ar control_device .
+The default value for
+.Ar control_device
+is
+.Pa /dev/lpt0.ctl .
+.El
+.Pp
+One of
+.Fl e , i , p
+or
+.Fl s
+must be specified.
+.Sh FILES
+.Bl -tag -width /sys/i386/conf/GENERIC -compact
+.It Pa /dev/lpt?
+printer devices
+.It Pa /dev/lpt?.ctl
+printer control devices
+.It Pa /sys/i386/conf/GENERIC
+kernel configuration file
+.It Pa /boot/device.hints
+device hints for the parallel port chipset driver,
+.Xr ppc 4
+.El
+.Sh SEE ALSO
+.Xr lpt 4 ,
+.Xr ppc 4 ,
+.Xr device.hints 5
+.Sh HISTORY
+The
+.Nm
+utility
+first appeared in
+.Fx 1.1.5
+.Sh AUTHORS
+.An Geoffrey M. Rehmet
+.Sh BUGS
+The control device name should never have been an option,
+but should have been an optional argument.
+Because of this, a single argument is treated as a device name.
diff --git a/usr.sbin/lptcontrol/lptcontrol.c b/usr.sbin/lptcontrol/lptcontrol.c
new file mode 100644
index 0000000..62e5b85
--- /dev/null
+++ b/usr.sbin/lptcontrol/lptcontrol.c
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 1994 Geoffrey M. Rehmet
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Geoffrey M. Rehmet
+ * 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 <dev/ppbus/lptio.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#define DEFAULT_DEVICE "/dev/lpt0.ctl"
+#define IRQ_UNSPECIFIED -1
+#define DO_POLL 0
+#define USE_IRQ 1
+#define USE_EXT_MODE 2
+#define USE_STD_MODE 3
+
+static void usage(void)
+{
+ fprintf(stderr,
+ "usage: lptcontrol -e | -i | -p | -s [[-d] controldevice]\n");
+ exit(1);
+}
+
+int main (int argc, char **argv)
+{
+ const char *device;
+ int fd;
+ int irq_status;
+ int opt;
+
+ device = DEFAULT_DEVICE;
+ irq_status = IRQ_UNSPECIFIED;
+ while ((opt = getopt(argc, argv, "d:eips")) != -1)
+ switch (opt) {
+ case 'd':
+ device = optarg;
+ break;
+ case 'e':
+ irq_status = USE_EXT_MODE;
+ break;
+ case 'i':
+ irq_status = USE_IRQ;
+ break;
+ case 'p':
+ irq_status = DO_POLL;
+ break;
+ case 's':
+ irq_status = USE_STD_MODE;
+ break;
+ case '?':
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ argc -= optind;
+ argv += optind;
+ /* POLA: DTRT if -d was forgotten, but device name was specified. */
+ if (argc == 1) {
+ device = argv[0];
+ --argc;
+ }
+
+ if (irq_status == IRQ_UNSPECIFIED || argc != 0)
+ usage();
+
+ if ((fd = open(device, O_WRONLY)) < 0)
+ err(1, "open");
+ if (ioctl(fd, LPT_IRQ, &irq_status) < 0)
+ err(1, "ioctl");
+ close(fd);
+
+ return(0);
+}
diff --git a/usr.sbin/mailstats/Makefile b/usr.sbin/mailstats/Makefile
new file mode 100644
index 0000000..86137b0
--- /dev/null
+++ b/usr.sbin/mailstats/Makefile
@@ -0,0 +1,35 @@
+# @(#)Makefile 8.2 (Berkeley) 9/21/96
+# $FreeBSD$
+
+SENDMAIL_DIR= ${.CURDIR}/../../contrib/sendmail
+.PATH: ${SENDMAIL_DIR}/mailstats
+
+PROG= mailstats
+SRCS= mailstats.c
+MAN= mailstats.8
+
+CFLAGS+= -I${SENDMAIL_DIR}/src -I${SENDMAIL_DIR}/include -I.
+CFLAGS+= -DNOT_SENDMAIL
+
+LIBSMDIR= ${.OBJDIR}/../../lib/libsm
+LIBSM= ${LIBSMDIR}/libsm.a
+
+LIBSMUTILDIR= ${.OBJDIR}/../../lib/libsmutil
+LIBSMUTIL= ${LIBSMUTILDIR}/libsmutil.a
+
+DPADD= ${LIBSMUTIL} ${LIBSM}
+LDADD= ${LIBSMUTIL} ${LIBSM}
+
+SRCS+= sm_os.h
+CLEANFILES+=sm_os.h
+
+# User customizations to the sendmail build environment
+CFLAGS+= ${SENDMAIL_CFLAGS}
+DPADD+= ${SENDMAIL_DPADD}
+LDADD+= ${SENDMAIL_LDADD}
+LDFLAGS+= ${SENDMAIL_LDFLAGS}
+
+sm_os.h:
+ ln -sf ${SENDMAIL_DIR}/include/sm/os/sm_os_freebsd.h sm_os.h
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mailwrapper/Makefile b/usr.sbin/mailwrapper/Makefile
new file mode 100644
index 0000000..252f5e7
--- /dev/null
+++ b/usr.sbin/mailwrapper/Makefile
@@ -0,0 +1,34 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+.if ${MK_MAILWRAPPER} != "no"
+PROG= mailwrapper
+MAN= mailwrapper.8
+
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+WARNS?= 6
+.endif
+
+.if ${MK_MAILWRAPPER} != "no" || ${MK_SENDMAIL} != "no"
+SYMLINKS= ${BINDIR}/mailwrapper /usr/sbin/sendmail \
+ ${BINDIR}/mailwrapper /usr/sbin/hoststat \
+ ${BINDIR}/mailwrapper /usr/sbin/purgestat \
+ ${BINDIR}/mailwrapper /usr/bin/newaliases \
+ ${BINDIR}/mailwrapper /usr/bin/mailq
+
+.if ${MK_MAILWRAPPER} == "no" && ${MK_SENDMAIL} != "no"
+SYMLINKS+= /usr/libexec/sendmail/sendmail ${BINDIR}/mailwrapper
+.endif
+.endif
+
+.if ${MK_MAILWRAPPER} != "no"
+.if !exists(${DESTDIR}/etc/mail/mailer.conf)
+FILES= ${.CURDIR}/../../etc/mail/mailer.conf
+FILESDIR= /etc/mail
+FILESMODE= 644
+.endif
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mailwrapper/mailwrapper.8 b/usr.sbin/mailwrapper/mailwrapper.8
new file mode 100644
index 0000000..587826b
--- /dev/null
+++ b/usr.sbin/mailwrapper/mailwrapper.8
@@ -0,0 +1,164 @@
+.\" $NetBSD: mailwrapper.8,v 1.11 2002/02/08 01:38:50 ross Exp $
+.\" $OpenBSD: mailwrapper.8,v 1.8 2003/06/12 12:59:51 jmc Exp $
+.\" $FreeBSD$
+.\"
+.\" 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.
+.\"
+.\" The following requests are required for all man pages.
+.Dd August 7, 2006
+.Dt MAILWRAPPER 8
+.Os
+.Sh NAME
+.Nm mailwrapper
+.Nd invoke appropriate MTA software based on configuration file
+.Sh SYNOPSIS
+Special.
+See below.
+.Sh DESCRIPTION
+At one time, the only Mail Transfer Agent (MTA) software easily available
+was
+.Xr sendmail 8 .
+As a result of this, most Mail User Agents (MUAs) such as
+.Xr mail 1
+had the path and calling conventions expected by
+.Xr sendmail 8
+compiled in.
+.Pp
+Times have changed, however.
+On a modern
+.Ux
+system, the administrator may wish to use one of several
+available MTAs.
+.Pp
+It would be difficult to modify all MUA software typically available
+on a system, so most of the authors of alternative MTAs have written
+their front end message submission programs so that they use the same
+calling conventions as
+.Xr sendmail 8
+and may be put into place instead of
+.Xr sendmail 8
+in
+.Pa /usr/sbin/sendmail .
+.Pp
+.Xr sendmail 8
+also typically has aliases named
+.Xr mailq 1
+and
+.Xr newaliases 1
+linked to it.
+The program knows to behave differently when its
+.Va argv[0]
+is
+.Dq mailq
+or
+.Dq newaliases
+and behaves appropriately.
+Typically, replacement MTAs provide similar
+functionality, either through a program that also switches behavior
+based on calling name, or through a set of programs that provide
+similar functionality.
+.Pp
+Although having drop-in replacements for
+.Xr sendmail 8
+helps in installing alternative MTAs, it essentially makes the
+configuration of the system depend on hand installing new programs in
+.Pa /usr .
+This leads to configuration problems for many administrators, since
+they may wish to install a new MTA without altering the system
+provided
+.Pa /usr .
+(This may be, for example, to avoid having upgrade problems when a new
+version of the system is installed over the old.)
+They may also have a shared
+.Pa /usr
+among several
+machines, and may wish to avoid placing implicit configuration
+information in a read-only
+.Pa /usr .
+.Pp
+The
+.Nm
+utility is designed to replace
+.Pa /usr/sbin/sendmail
+and to invoke an appropriate MTA instead of
+.Xr sendmail 8
+based on configuration information placed in
+.Pa /etc/mail/mailer.conf .
+This permits the administrator to configure which MTA is to be invoked on
+the system at run time.
+.Pp
+Other configuration files may need to be altered when replacing
+.Xr sendmail 8 .
+For example, if the replacement MTA does not support the
+.Fl A
+option with
+.Xr mailq 1 ,
+.Va daily_status_include_submit_mailq
+should be turned off in
+.Pa /etc/periodic.conf .
+.Sh FILES
+Configuration for
+.Nm
+is kept in
+.Pa /etc/mail/mailer.conf .
+.Pa /usr/sbin/sendmail
+is typically set up as a symbolic link to
+.Nm
+which is not usually invoked on its own.
+.Sh EXIT STATUS
+.Ex -std
+.Sh DIAGNOSTICS
+The
+.Nm
+will print a diagnostic if its configuration file is missing or malformed,
+or does not contain a mapping for the name under which it was invoked.
+.Sh SEE ALSO
+.Xr mail 1 ,
+.Xr mailq 1 ,
+.Xr newaliases 1 ,
+.Xr mailer.conf 5 ,
+.Xr periodic.conf 5 ,
+.Xr sendmail 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Nx 1.4
+and then
+.Fx 4.0 .
+.Sh AUTHORS
+.An Perry E. Metzger Aq perry@piermont.com
+.Sh BUGS
+The entire reason this program exists is a crock.
+Instead, a command
+for how to submit mail should be standardized, and all the "behave
+differently if invoked with a different name" behavior of things like
+.Xr mailq 1
+should go away.
diff --git a/usr.sbin/mailwrapper/mailwrapper.c b/usr.sbin/mailwrapper/mailwrapper.c
new file mode 100644
index 0000000..d696d25
--- /dev/null
+++ b/usr.sbin/mailwrapper/mailwrapper.c
@@ -0,0 +1,162 @@
+/* $OpenBSD: mailwrapper.c,v 1.16 2004/07/06 03:38:14 millert Exp $ */
+/* $NetBSD: mailwrapper.c,v 1.9 2003/03/09 08:10:43 mjl 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <libutil.h>
+#include <sysexits.h>
+#include <syslog.h>
+
+#include "pathnames.h"
+
+struct arglist {
+ size_t argc, maxc;
+ char **argv;
+};
+
+int main(int, char *[], char *[]);
+
+static void initarg(struct arglist *);
+static void addarg(struct arglist *, const char *);
+
+static void
+initarg(struct arglist *al)
+{
+ al->argc = 0;
+ al->maxc = 10;
+ if ((al->argv = malloc(al->maxc * sizeof(char *))) == NULL)
+ err(EX_TEMPFAIL, "malloc");
+}
+
+static void
+addarg(struct arglist *al, const char *arg)
+{
+
+ if (al->argc == al->maxc) {
+ al->maxc <<= 1;
+ al->argv = realloc(al->argv, al->maxc * sizeof(char *));
+ if (al->argv == NULL)
+ err(EX_TEMPFAIL, "realloc");
+ }
+ if (arg == NULL)
+ al->argv[al->argc++] = NULL;
+ else if ((al->argv[al->argc++] = strdup(arg)) == NULL)
+ err(EX_TEMPFAIL, "strdup");
+}
+
+int
+main(int argc, char *argv[], char *envp[])
+{
+ FILE *config;
+ char *line, *cp, *from, *to, *ap;
+ const char *progname;
+ size_t len, lineno = 0;
+ int i;
+ struct arglist al;
+
+ /* change __progname to mailwrapper so we get sensible error messages */
+ progname = getprogname();
+ setprogname("mailwrapper");
+
+ initarg(&al);
+ addarg(&al, argv[0]);
+
+ if ((config = fopen(_PATH_MAILERCONF, "r")) == NULL) {
+ addarg(&al, NULL);
+ openlog(getprogname(), LOG_PID, LOG_MAIL);
+ syslog(LOG_INFO, "cannot open %s, using %s as default MTA",
+ _PATH_MAILERCONF, _PATH_DEFAULTMTA);
+ closelog();
+ execve(_PATH_DEFAULTMTA, al.argv, envp);
+ err(EX_OSERR, "cannot exec %s", _PATH_DEFAULTMTA);
+ /*NOTREACHED*/
+ }
+
+ for (;;) {
+ if ((line = fparseln(config, &len, &lineno, NULL, 0)) == NULL) {
+ if (feof(config))
+ errx(EX_CONFIG, "no mapping in %s", _PATH_MAILERCONF);
+ err(EX_CONFIG, "cannot parse line %lu", (u_long)lineno);
+ }
+
+#define WS " \t\n"
+ cp = line;
+
+ cp += strspn(cp, WS);
+ if (cp[0] == '\0') {
+ /* empty line */
+ free(line);
+ continue;
+ }
+
+ if ((from = strsep(&cp, WS)) == NULL)
+ goto parse_error;
+
+ cp += strspn(cp, WS);
+
+ if ((to = strsep(&cp, WS)) == NULL)
+ goto parse_error;
+
+ if (strcmp(from, progname) == 0) {
+ for (ap = strsep(&cp, WS); ap != NULL;
+ ap = strsep(&cp, WS)) {
+ if (*ap)
+ addarg(&al, ap);
+ }
+ break;
+ }
+
+ free(line);
+ }
+
+ (void)fclose(config);
+
+ for (i = 1; i < argc; i++)
+ addarg(&al, argv[i]);
+
+ addarg(&al, NULL);
+ execve(to, al.argv, envp);
+ err(EX_OSERR, "cannot exec %s", to);
+ /*NOTREACHED*/
+parse_error:
+ errx(EX_CONFIG, "parse error in %s at line %lu",
+ _PATH_MAILERCONF, (u_long)lineno);
+ /*NOTREACHED*/
+}
diff --git a/usr.sbin/mailwrapper/pathnames.h b/usr.sbin/mailwrapper/pathnames.h
new file mode 100644
index 0000000..d8ae498
--- /dev/null
+++ b/usr.sbin/mailwrapper/pathnames.h
@@ -0,0 +1,35 @@
+/* $FreeBSD$ */
+
+/*
+ * 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.
+ */
+
+#define _PATH_MAILERCONF "/etc/mail/mailer.conf"
+#define _PATH_DEFAULTMTA "/usr/libexec/sendmail/sendmail"
diff --git a/usr.sbin/makefs/Makefile b/usr.sbin/makefs/Makefile
new file mode 100644
index 0000000..0356029
--- /dev/null
+++ b/usr.sbin/makefs/Makefile
@@ -0,0 +1,28 @@
+# $FreeBSD$
+
+PROG= makefs
+MAN= makefs.8
+
+WARNS?= 2
+
+CFLAGS+=-I${.CURDIR}
+SRCS= ffs.c getid.c makefs.c walk.c
+
+.PATH: ${.CURDIR}/ffs
+CFLAGS+=-I${.CURDIR}/ffs
+CFLAGS+=-DHAVE_STRUCT_STAT_ST_FLAGS=1
+CFLAGS+=-DHAVE_STRUCT_STAT_ST_GEN=1
+SRCS+= buf.c ffs_alloc.c ffs_balloc.c ffs_bswap.c ffs_subr.c mkfs.c ufs_bmap.c
+
+.PATH: ${.CURDIR}/compat
+CFLAGS+=-I${.CURDIR}/compat
+SRCS+= pwcache.c strsuftoll.c
+
+.PATH: ${.CURDIR}/../mtree
+CFLAGS+=-I${.CURDIR}/../mtree
+SRCS+= misc.c spec.c
+
+.PATH: ${.CURDIR}/../../sys/ufs/ffs
+SRCS+= ffs_tables.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/makefs/compat/pwcache.c b/usr.sbin/makefs/compat/pwcache.c
new file mode 100644
index 0000000..0579d26
--- /dev/null
+++ b/usr.sbin/makefs/compat/pwcache.c
@@ -0,0 +1,623 @@
+/* $NetBSD: pwcache.c,v 1.29 2004/06/20 22:20:14 jmc Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 2002 The NetBSD Foundation, 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.
+ * 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <assert.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef __weak_alias
+__weak_alias(user_from_uid,_user_from_uid)
+__weak_alias(group_from_gid,_group_from_gid)
+__weak_alias(pwcache_userdb,_pwcache_userdb)
+__weak_alias(pwcache_groupdb,_pwcache_groupdb)
+#endif
+
+#include "pwcache.h"
+
+/*
+ * routines that control user, group, uid and gid caches (for the archive
+ * member print routine).
+ * IMPORTANT:
+ * these routines cache BOTH hits and misses, a major performance improvement
+ */
+
+/*
+ * function pointers to various name lookup routines.
+ * these may be changed as necessary.
+ */
+static int (*_pwcache_setgroupent)(int) = setgroupent;
+static void (*_pwcache_endgrent)(void) = endgrent;
+static struct group * (*_pwcache_getgrnam)(const char *) = getgrnam;
+static struct group * (*_pwcache_getgrgid)(gid_t) = getgrgid;
+static int (*_pwcache_setpassent)(int) = setpassent;
+static void (*_pwcache_endpwent)(void) = endpwent;
+static struct passwd * (*_pwcache_getpwnam)(const char *) = getpwnam;
+static struct passwd * (*_pwcache_getpwuid)(uid_t) = getpwuid;
+
+/*
+ * internal state
+ */
+static int pwopn; /* is password file open */
+static int gropn; /* is group file open */
+static UIDC **uidtb; /* uid to name cache */
+static GIDC **gidtb; /* gid to name cache */
+static UIDC **usrtb; /* user name to uid cache */
+static GIDC **grptb; /* group name to gid cache */
+
+static int uidtb_fail; /* uidtb_start() failed ? */
+static int gidtb_fail; /* gidtb_start() failed ? */
+static int usrtb_fail; /* usrtb_start() failed ? */
+static int grptb_fail; /* grptb_start() failed ? */
+
+
+static u_int st_hash(const char *, size_t, int);
+static int uidtb_start(void);
+static int gidtb_start(void);
+static int usrtb_start(void);
+static int grptb_start(void);
+
+
+static u_int
+st_hash(const char *name, size_t len, int tabsz)
+{
+ u_int key = 0;
+
+ while (len--) {
+ key += *name++;
+ key = (key << 8) | (key >> 24);
+ }
+
+ return (key % tabsz);
+}
+
+/*
+ * uidtb_start
+ * creates an an empty uidtb
+ * Return:
+ * 0 if ok, -1 otherwise
+ */
+static int
+uidtb_start(void)
+{
+
+ if (uidtb != NULL)
+ return (0);
+ if (uidtb_fail)
+ return (-1);
+ if ((uidtb = (UIDC **)calloc(UID_SZ, sizeof(UIDC *))) == NULL) {
+ ++uidtb_fail;
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * gidtb_start
+ * creates an an empty gidtb
+ * Return:
+ * 0 if ok, -1 otherwise
+ */
+static int
+gidtb_start(void)
+{
+
+ if (gidtb != NULL)
+ return (0);
+ if (gidtb_fail)
+ return (-1);
+ if ((gidtb = (GIDC **)calloc(GID_SZ, sizeof(GIDC *))) == NULL) {
+ ++gidtb_fail;
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * usrtb_start
+ * creates an an empty usrtb
+ * Return:
+ * 0 if ok, -1 otherwise
+ */
+static int
+usrtb_start(void)
+{
+
+ if (usrtb != NULL)
+ return (0);
+ if (usrtb_fail)
+ return (-1);
+ if ((usrtb = (UIDC **)calloc(UNM_SZ, sizeof(UIDC *))) == NULL) {
+ ++usrtb_fail;
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * grptb_start
+ * creates an an empty grptb
+ * Return:
+ * 0 if ok, -1 otherwise
+ */
+static int
+grptb_start(void)
+{
+
+ if (grptb != NULL)
+ return (0);
+ if (grptb_fail)
+ return (-1);
+ if ((grptb = (GIDC **)calloc(GNM_SZ, sizeof(GIDC *))) == NULL) {
+ ++grptb_fail;
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * user_from_uid()
+ * caches the name (if any) for the uid. If noname clear, we always
+ * return the stored name (if valid or invalid match).
+ * We use a simple hash table.
+ * Return
+ * Pointer to stored name (or a empty string)
+ */
+const char *
+user_from_uid(uid_t uid, int noname)
+{
+ struct passwd *pw;
+ UIDC *ptr, **pptr;
+
+ if ((uidtb == NULL) && (uidtb_start() < 0))
+ return (NULL);
+
+ /*
+ * see if we have this uid cached
+ */
+ pptr = uidtb + (uid % UID_SZ);
+ ptr = *pptr;
+
+ if ((ptr != NULL) && (ptr->valid > 0) && (ptr->uid == uid)) {
+ /*
+ * have an entry for this uid
+ */
+ if (!noname || (ptr->valid == VALID))
+ return (ptr->name);
+ return (NULL);
+ }
+
+ /*
+ * No entry for this uid, we will add it
+ */
+ if (!pwopn) {
+ if (_pwcache_setpassent != NULL)
+ (*_pwcache_setpassent)(1);
+ ++pwopn;
+ }
+
+ if (ptr == NULL)
+ *pptr = ptr = (UIDC *)malloc(sizeof(UIDC));
+
+ if ((pw = (*_pwcache_getpwuid)(uid)) == NULL) {
+ /*
+ * no match for this uid in the local password file
+ * a string that is the uid in numeric format
+ */
+ if (ptr == NULL)
+ return (NULL);
+ ptr->uid = uid;
+ (void)snprintf(ptr->name, UNMLEN, "%lu", (long) uid);
+ ptr->valid = INVALID;
+ if (noname)
+ return (NULL);
+ } else {
+ /*
+ * there is an entry for this uid in the password file
+ */
+ if (ptr == NULL)
+ return (pw->pw_name);
+ ptr->uid = uid;
+ (void)strlcpy(ptr->name, pw->pw_name, UNMLEN);
+ ptr->valid = VALID;
+ }
+ return (ptr->name);
+}
+
+/*
+ * group_from_gid()
+ * caches the name (if any) for the gid. If noname clear, we always
+ * return the stored name (if valid or invalid match).
+ * We use a simple hash table.
+ * Return
+ * Pointer to stored name (or a empty string)
+ */
+const char *
+group_from_gid(gid_t gid, int noname)
+{
+ struct group *gr;
+ GIDC *ptr, **pptr;
+
+ if ((gidtb == NULL) && (gidtb_start() < 0))
+ return (NULL);
+
+ /*
+ * see if we have this gid cached
+ */
+ pptr = gidtb + (gid % GID_SZ);
+ ptr = *pptr;
+
+ if ((ptr != NULL) && (ptr->valid > 0) && (ptr->gid == gid)) {
+ /*
+ * have an entry for this gid
+ */
+ if (!noname || (ptr->valid == VALID))
+ return (ptr->name);
+ return (NULL);
+ }
+
+ /*
+ * No entry for this gid, we will add it
+ */
+ if (!gropn) {
+ if (_pwcache_setgroupent != NULL)
+ (*_pwcache_setgroupent)(1);
+ ++gropn;
+ }
+
+ if (ptr == NULL)
+ *pptr = ptr = (GIDC *)malloc(sizeof(GIDC));
+
+ if ((gr = (*_pwcache_getgrgid)(gid)) == NULL) {
+ /*
+ * no match for this gid in the local group file, put in
+ * a string that is the gid in numberic format
+ */
+ if (ptr == NULL)
+ return (NULL);
+ ptr->gid = gid;
+ (void)snprintf(ptr->name, GNMLEN, "%lu", (long) gid);
+ ptr->valid = INVALID;
+ if (noname)
+ return (NULL);
+ } else {
+ /*
+ * there is an entry for this group in the group file
+ */
+ if (ptr == NULL)
+ return (gr->gr_name);
+ ptr->gid = gid;
+ (void)strlcpy(ptr->name, gr->gr_name, GNMLEN);
+ ptr->valid = VALID;
+ }
+ return (ptr->name);
+}
+
+/*
+ * uid_from_user()
+ * caches the uid for a given user name. We use a simple hash table.
+ * Return
+ * the uid (if any) for a user name, or a -1 if no match can be found
+ */
+int
+uid_from_user(const char *name, uid_t *uid)
+{
+ struct passwd *pw;
+ UIDC *ptr, **pptr;
+ size_t namelen;
+
+ /*
+ * return -1 for mangled names
+ */
+ if (name == NULL || ((namelen = strlen(name)) == 0))
+ return (-1);
+ if ((usrtb == NULL) && (usrtb_start() < 0))
+ return (-1);
+
+ /*
+ * look up in hash table, if found and valid return the uid,
+ * if found and invalid, return a -1
+ */
+ pptr = usrtb + st_hash(name, namelen, UNM_SZ);
+ ptr = *pptr;
+
+ if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
+ if (ptr->valid == INVALID)
+ return (-1);
+ *uid = ptr->uid;
+ return (0);
+ }
+
+ if (!pwopn) {
+ if (_pwcache_setpassent != NULL)
+ (*_pwcache_setpassent)(1);
+ ++pwopn;
+ }
+
+ if (ptr == NULL)
+ *pptr = ptr = (UIDC *)malloc(sizeof(UIDC));
+
+ /*
+ * no match, look it up, if no match store it as an invalid entry,
+ * or store the matching uid
+ */
+ if (ptr == NULL) {
+ if ((pw = (*_pwcache_getpwnam)(name)) == NULL)
+ return (-1);
+ *uid = pw->pw_uid;
+ return (0);
+ }
+ (void)strlcpy(ptr->name, name, UNMLEN);
+ if ((pw = (*_pwcache_getpwnam)(name)) == NULL) {
+ ptr->valid = INVALID;
+ return (-1);
+ }
+ ptr->valid = VALID;
+ *uid = ptr->uid = pw->pw_uid;
+ return (0);
+}
+
+/*
+ * gid_from_group()
+ * caches the gid for a given group name. We use a simple hash table.
+ * Return
+ * the gid (if any) for a group name, or a -1 if no match can be found
+ */
+int
+gid_from_group(const char *name, gid_t *gid)
+{
+ struct group *gr;
+ GIDC *ptr, **pptr;
+ size_t namelen;
+
+ /*
+ * return -1 for mangled names
+ */
+ if (name == NULL || ((namelen = strlen(name)) == 0))
+ return (-1);
+ if ((grptb == NULL) && (grptb_start() < 0))
+ return (-1);
+
+ /*
+ * look up in hash table, if found and valid return the uid,
+ * if found and invalid, return a -1
+ */
+ pptr = grptb + st_hash(name, namelen, GID_SZ);
+ ptr = *pptr;
+
+ if ((ptr != NULL) && (ptr->valid > 0) && !strcmp(name, ptr->name)) {
+ if (ptr->valid == INVALID)
+ return (-1);
+ *gid = ptr->gid;
+ return (0);
+ }
+
+ if (!gropn) {
+ if (_pwcache_setgroupent != NULL)
+ (*_pwcache_setgroupent)(1);
+ ++gropn;
+ }
+
+ if (ptr == NULL)
+ *pptr = ptr = (GIDC *)malloc(sizeof(GIDC));
+
+ /*
+ * no match, look it up, if no match store it as an invalid entry,
+ * or store the matching gid
+ */
+ if (ptr == NULL) {
+ if ((gr = (*_pwcache_getgrnam)(name)) == NULL)
+ return (-1);
+ *gid = gr->gr_gid;
+ return (0);
+ }
+
+ (void)strlcpy(ptr->name, name, GNMLEN);
+ if ((gr = (*_pwcache_getgrnam)(name)) == NULL) {
+ ptr->valid = INVALID;
+ return (-1);
+ }
+ ptr->valid = VALID;
+ *gid = ptr->gid = gr->gr_gid;
+ return (0);
+}
+
+#define FLUSHTB(arr, len, fail) \
+ do { \
+ if (arr != NULL) { \
+ for (i = 0; i < len; i++) \
+ if (arr[i] != NULL) \
+ free(arr[i]); \
+ arr = NULL; \
+ } \
+ fail = 0; \
+ } while (/* CONSTCOND */0);
+
+int
+pwcache_userdb(
+ int (*a_setpassent)(int),
+ void (*a_endpwent)(void),
+ struct passwd * (*a_getpwnam)(const char *),
+ struct passwd * (*a_getpwuid)(uid_t))
+{
+ int i;
+
+ /* a_setpassent and a_endpwent may be NULL */
+ if (a_getpwnam == NULL || a_getpwuid == NULL)
+ return (-1);
+
+ if (_pwcache_endpwent != NULL)
+ (*_pwcache_endpwent)();
+ FLUSHTB(uidtb, UID_SZ, uidtb_fail);
+ FLUSHTB(usrtb, UNM_SZ, usrtb_fail);
+ pwopn = 0;
+ _pwcache_setpassent = a_setpassent;
+ _pwcache_endpwent = a_endpwent;
+ _pwcache_getpwnam = a_getpwnam;
+ _pwcache_getpwuid = a_getpwuid;
+
+ return (0);
+}
+
+int
+pwcache_groupdb(
+ int (*a_setgroupent)(int),
+ void (*a_endgrent)(void),
+ struct group * (*a_getgrnam)(const char *),
+ struct group * (*a_getgrgid)(gid_t))
+{
+ int i;
+
+ /* a_setgroupent and a_endgrent may be NULL */
+ if (a_getgrnam == NULL || a_getgrgid == NULL)
+ return (-1);
+
+ if (_pwcache_endgrent != NULL)
+ (*_pwcache_endgrent)();
+ FLUSHTB(gidtb, GID_SZ, gidtb_fail);
+ FLUSHTB(grptb, GNM_SZ, grptb_fail);
+ gropn = 0;
+ _pwcache_setgroupent = a_setgroupent;
+ _pwcache_endgrent = a_endgrent;
+ _pwcache_getgrnam = a_getgrnam;
+ _pwcache_getgrgid = a_getgrgid;
+
+ return (0);
+}
+
+
+#ifdef TEST_PWCACHE
+
+struct passwd *
+test_getpwnam(const char *name)
+{
+ static struct passwd foo;
+
+ memset(&foo, 0, sizeof(foo));
+ if (strcmp(name, "toor") == 0) {
+ foo.pw_uid = 666;
+ return &foo;
+ }
+ return (getpwnam(name));
+}
+
+int
+main(int argc, char *argv[])
+{
+ uid_t u;
+ int r, i;
+
+ printf("pass 1 (default userdb)\n");
+ for (i = 1; i < argc; i++) {
+ printf("i: %d, pwopn %d usrtb_fail %d usrtb %p\n",
+ i, pwopn, usrtb_fail, usrtb);
+ r = uid_from_user(argv[i], &u);
+ if (r == -1)
+ printf(" uid_from_user %s: failed\n", argv[i]);
+ else
+ printf(" uid_from_user %s: %d\n", argv[i], u);
+ }
+ printf("pass 1 finish: pwopn %d usrtb_fail %d usrtb %p\n",
+ pwopn, usrtb_fail, usrtb);
+
+ puts("");
+ printf("pass 2 (replacement userdb)\n");
+ printf("pwcache_userdb returned %d\n",
+ pwcache_userdb(setpassent, test_getpwnam, getpwuid));
+ printf("pwopn %d usrtb_fail %d usrtb %p\n", pwopn, usrtb_fail, usrtb);
+
+ for (i = 1; i < argc; i++) {
+ printf("i: %d, pwopn %d usrtb_fail %d usrtb %p\n",
+ i, pwopn, usrtb_fail, usrtb);
+ u = -1;
+ r = uid_from_user(argv[i], &u);
+ if (r == -1)
+ printf(" uid_from_user %s: failed\n", argv[i]);
+ else
+ printf(" uid_from_user %s: %d\n", argv[i], u);
+ }
+ printf("pass 2 finish: pwopn %d usrtb_fail %d usrtb %p\n",
+ pwopn, usrtb_fail, usrtb);
+
+ puts("");
+ printf("pass 3 (null pointers)\n");
+ printf("pwcache_userdb returned %d\n",
+ pwcache_userdb(NULL, NULL, NULL));
+
+ return (0);
+}
+#endif /* TEST_PWCACHE */
diff --git a/usr.sbin/makefs/compat/pwcache.h b/usr.sbin/makefs/compat/pwcache.h
new file mode 100644
index 0000000..1708951
--- /dev/null
+++ b/usr.sbin/makefs/compat/pwcache.h
@@ -0,0 +1,73 @@
+/* $NetBSD: pwcache.h,v 1.5 2003/11/10 08:51:51 wiz Exp $ */
+
+/*-
+ * Copyright (c) 1992 Keith Muller.
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)cache.h 8.1 (Berkeley) 5/31/93
+ * $FreeBSD$
+ */
+
+/*
+ * Constants and data structures used to implement group and password file
+ * caches. Traditional passwd/group cache routines perform quite poorly with
+ * archives. The chances of hitting a valid lookup with an archive is quite a
+ * bit worse than with files already resident on the file system. These misses
+ * create a MAJOR performance cost. To address this problem, these routines
+ * cache both hits and misses.
+ *
+ * NOTE: name lengths must be as large as those stored in ANY PROTOCOL and
+ * as stored in the passwd and group files. CACHE SIZES MUST BE PRIME
+ */
+#define UNMLEN 32 /* >= user name found in any protocol */
+#define GNMLEN 32 /* >= group name found in any protocol */
+#define UID_SZ 317 /* size of uid to user_name cache */
+#define UNM_SZ 317 /* size of user_name to uid cache */
+#define GID_SZ 251 /* size of gid to group_name cache */
+#define GNM_SZ 251 /* size of group_name to gid cache */
+#define VALID 1 /* entry and name are valid */
+#define INVALID 2 /* entry valid, name NOT valid */
+
+/*
+ * Node structures used in the user, group, uid, and gid caches.
+ */
+
+typedef struct uidc {
+ int valid; /* is this a valid or a miss entry */
+ char name[UNMLEN]; /* uid name */
+ uid_t uid; /* cached uid */
+} UIDC;
+
+typedef struct gidc {
+ int valid; /* is this a valid or a miss entry */
+ char name[GNMLEN]; /* gid name */
+ gid_t gid; /* cached gid */
+} GIDC;
diff --git a/usr.sbin/makefs/compat/strsuftoll.c b/usr.sbin/makefs/compat/strsuftoll.c
new file mode 100644
index 0000000..f73e3ad
--- /dev/null
+++ b/usr.sbin/makefs/compat/strsuftoll.c
@@ -0,0 +1,229 @@
+/* $NetBSD: strsuftoll.c,v 1.6 2004/03/05 05:58:29 lukem Exp $ */
+/*-
+ * Copyright (c) 2001-2002,2004 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Luke Mewburn.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 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.
+ */
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Keith Muller of the University of California, San Diego and Lance
+ * Visser of Convex Computer Corporation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _LIBC
+# ifdef __weak_alias
+__weak_alias(strsuftoll, _strsuftoll)
+__weak_alias(strsuftollx, _strsuftollx)
+# endif
+#endif /* LIBC */
+
+/*
+ * Convert an expression of the following forms to a (u)int64_t.
+ * 1) A positive decimal number.
+ * 2) A positive decimal number followed by a b (mult by 512).
+ * 3) A positive decimal number followed by a k (mult by 1024).
+ * 4) A positive decimal number followed by a m (mult by 1048576).
+ * 5) A positive decimal number followed by a g (mult by 1073741824).
+ * 6) A positive decimal number followed by a t (mult by 1099511627776).
+ * 7) A positive decimal number followed by a w (mult by sizeof int)
+ * 8) Two or more positive decimal numbers (with/without k,b or w).
+ * separated by x (also * for backwards compatibility), specifying
+ * the product of the indicated values.
+ * Returns the result upon successful conversion, or exits with an
+ * appropriate error.
+ *
+ */
+
+/*
+ * As strsuftoll(), but returns the error message into the provided buffer
+ * rather than exiting with it.
+ */
+/* LONGLONG */
+long long
+strsuftollx(const char *desc, const char *val,
+ long long min, long long max, char *ebuf, size_t ebuflen)
+{
+ long long num, t;
+ char *expr;
+
+ errno = 0;
+ ebuf[0] = '\0';
+
+ while (isspace((unsigned char)*val)) /* Skip leading space */
+ val++;
+
+ num = strtoll(val, &expr, 10);
+ if (errno == ERANGE)
+ goto erange; /* Overflow */
+
+ if (expr == val) /* No digits */
+ goto badnum;
+
+ switch (*expr) {
+ case 'b':
+ t = num;
+ num *= 512; /* 1 block */
+ if (t > num)
+ goto erange;
+ ++expr;
+ break;
+ case 'k':
+ t = num;
+ num *= 1024; /* 1 kilobyte */
+ if (t > num)
+ goto erange;
+ ++expr;
+ break;
+ case 'm':
+ t = num;
+ num *= 1048576; /* 1 megabyte */
+ if (t > num)
+ goto erange;
+ ++expr;
+ break;
+ case 'g':
+ t = num;
+ num *= 1073741824; /* 1 gigabyte */
+ if (t > num)
+ goto erange;
+ ++expr;
+ break;
+ case 't':
+ t = num;
+ num *= 1099511627776LL; /* 1 terabyte */
+ if (t > num)
+ goto erange;
+ ++expr;
+ break;
+ case 'w':
+ t = num;
+ num *= sizeof(int); /* 1 word */
+ if (t > num)
+ goto erange;
+ ++expr;
+ break;
+ }
+
+ switch (*expr) {
+ case '\0':
+ break;
+ case '*': /* Backward compatible */
+ case 'x':
+ t = num;
+ num *= strsuftollx(desc, expr + 1, min, max, ebuf, ebuflen);
+ if (*ebuf != '\0')
+ return (0);
+ if (t > num) {
+ erange:
+ snprintf(ebuf, ebuflen,
+ "%s: %s", desc, strerror(ERANGE));
+ return (0);
+ }
+ break;
+ default:
+ badnum: snprintf(ebuf, ebuflen,
+ "%s `%s': illegal number", desc, val);
+ return (0);
+ }
+ if (num < min) {
+ /* LONGLONG */
+ snprintf(ebuf, ebuflen, "%s %lld is less than %lld.",
+ desc, (long long)num, (long long)min);
+ return (0);
+ }
+ if (num > max) {
+ /* LONGLONG */
+ snprintf(ebuf, ebuflen,
+ "%s %lld is greater than %lld.",
+ desc, (long long)num, (long long)max);
+ return (0);
+ }
+ *ebuf = '\0';
+ return (num);
+}
+
+/* LONGLONG */
+long long
+strsuftoll(const char *desc, const char *val,
+ long long min, long long max)
+{
+ long long result;
+ char errbuf[100];
+
+ result = strsuftollx(desc, val, min, max, errbuf, sizeof(errbuf));
+ if (*errbuf != '\0')
+ errx(1, "%s", errbuf);
+ return (result);
+}
diff --git a/usr.sbin/makefs/ffs.c b/usr.sbin/makefs/ffs.c
new file mode 100644
index 0000000..ece8c65
--- /dev/null
+++ b/usr.sbin/makefs/ffs.c
@@ -0,0 +1,1093 @@
+/* $NetBSD: ffs.c,v 1.30 2004/06/24 22:30:13 lukem Exp $ */
+
+/*
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn 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.
+ */
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ffs_alloc.c 8.19 (Berkeley) 7/13/95
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <sys/mount.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "makefs.h"
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ufs/dir.h>
+#include <ufs/ffs/fs.h>
+
+#include "ffs/ufs_bswap.h"
+#include "ffs/ufs_inode.h"
+#include "ffs/newfs_extern.h"
+#include "ffs/ffs_extern.h"
+
+#undef DIP
+#define DIP(dp, field) \
+ ((fsopts->version == 1) ? \
+ (dp)->ffs1_din.di_##field : (dp)->ffs2_din.di_##field)
+
+/*
+ * Various file system defaults (cribbed from newfs(8)).
+ */
+#define DFL_FRAGSIZE 1024 /* fragment size */
+#define DFL_BLKSIZE 8192 /* block size */
+#define DFL_SECSIZE 512 /* sector size */
+#define DFL_CYLSPERGROUP 65536 /* cylinders per group */
+#define DFL_FRAGSPERINODE 4 /* fragments per inode */
+#define DFL_ROTDELAY 0 /* rotational delay */
+#define DFL_NRPOS 1 /* rotational positions */
+#define DFL_RPM 3600 /* rpm of disk */
+#define DFL_NSECTORS 64 /* # of sectors */
+#define DFL_NTRACKS 16 /* # of tracks */
+
+
+typedef struct {
+ u_char *buf; /* buf for directory */
+ doff_t size; /* full size of buf */
+ doff_t cur; /* offset of current entry */
+} dirbuf_t;
+
+
+static int ffs_create_image(const char *, fsinfo_t *);
+static void ffs_dump_fsinfo(fsinfo_t *);
+static void ffs_dump_dirbuf(dirbuf_t *, const char *, int);
+static void ffs_make_dirbuf(dirbuf_t *, const char *, fsnode *, int);
+static int ffs_populate_dir(const char *, fsnode *, fsinfo_t *);
+static void ffs_size_dir(fsnode *, fsinfo_t *);
+static void ffs_validate(const char *, fsnode *, fsinfo_t *);
+static void ffs_write_file(union dinode *, uint32_t, void *, fsinfo_t *);
+static void ffs_write_inode(union dinode *, uint32_t, const fsinfo_t *);
+static void *ffs_build_dinode1(struct ufs1_dinode *, dirbuf_t *, fsnode *,
+ fsnode *, fsinfo_t *);
+static void *ffs_build_dinode2(struct ufs2_dinode *, dirbuf_t *, fsnode *,
+ fsnode *, fsinfo_t *);
+
+
+
+int sectorsize; /* XXX: for buf.c::getblk() */
+
+ /* publically visible functions */
+
+int
+ffs_parse_opts(const char *option, fsinfo_t *fsopts)
+{
+ option_t ffs_options[] = {
+ { "bsize", &fsopts->bsize, 1, INT_MAX,
+ "block size" },
+ { "fsize", &fsopts->fsize, 1, INT_MAX,
+ "fragment size" },
+ { "density", &fsopts->density, 1, INT_MAX,
+ "bytes per inode" },
+ { "minfree", &fsopts->minfree, 0, 99,
+ "minfree" },
+ { "maxbpf", &fsopts->maxbpg, 1, INT_MAX,
+ "max blocks per file in a cg" },
+ { "avgfilesize", &fsopts->avgfilesize, 1, INT_MAX,
+ "expected average file size" },
+ { "avgfpdir", &fsopts->avgfpdir, 1, INT_MAX,
+ "expected # of files per directory" },
+ { "extent", &fsopts->maxbsize, 1, INT_MAX,
+ "maximum # extent size" },
+ { "maxbpcg", &fsopts->maxblkspercg, 1, INT_MAX,
+ "max # of blocks per group" },
+ { "version", &fsopts->version, 1, 2,
+ "UFS version" },
+ { NULL }
+ };
+
+ char *var, *val;
+ int rv;
+
+ (void)&ffs_options;
+ assert(option != NULL);
+ assert(fsopts != NULL);
+
+ if (debug & DEBUG_FS_PARSE_OPTS)
+ printf("ffs_parse_opts: got `%s'\n", option);
+
+ if ((var = strdup(option)) == NULL)
+ err(1, "Allocating memory for copy of option string");
+ rv = 0;
+
+ if ((val = strchr(var, '=')) == NULL) {
+ warnx("Option `%s' doesn't contain a value", var);
+ goto leave_ffs_parse_opts;
+ }
+ *val++ = '\0';
+
+ if (strcmp(var, "optimization") == 0) {
+ if (strcmp(val, "time") == 0) {
+ fsopts->optimization = FS_OPTTIME;
+ } else if (strcmp(val, "space") == 0) {
+ fsopts->optimization = FS_OPTSPACE;
+ } else {
+ warnx("Invalid optimization `%s'", val);
+ goto leave_ffs_parse_opts;
+ }
+ rv = 1;
+ } else
+ rv = set_option(ffs_options, var, val);
+
+ leave_ffs_parse_opts:
+ if (var)
+ free(var);
+ return (rv);
+}
+
+
+void
+ffs_makefs(const char *image, const char *dir, fsnode *root, fsinfo_t *fsopts)
+{
+ struct fs *superblock;
+ struct timeval start;
+
+ assert(image != NULL);
+ assert(dir != NULL);
+ assert(root != NULL);
+ assert(fsopts != NULL);
+
+ if (debug & DEBUG_FS_MAKEFS)
+ printf("ffs_makefs: image %s directory %s root %p\n",
+ image, dir, root);
+
+ /* validate tree and options */
+ TIMER_START(start);
+ ffs_validate(dir, root, fsopts);
+ TIMER_RESULTS(start, "ffs_validate");
+
+ printf("Calculated size of `%s': %lld bytes, %lld inodes\n",
+ image, (long long)fsopts->size, (long long)fsopts->inodes);
+
+ /* create image */
+ TIMER_START(start);
+ if (ffs_create_image(image, fsopts) == -1)
+ errx(1, "Image file `%s' not created.", image);
+ TIMER_RESULTS(start, "ffs_create_image");
+
+ fsopts->curinode = ROOTINO;
+
+ if (debug & DEBUG_FS_MAKEFS)
+ putchar('\n');
+
+ /* populate image */
+ printf("Populating `%s'\n", image);
+ TIMER_START(start);
+ if (! ffs_populate_dir(dir, root, fsopts))
+ errx(1, "Image file `%s' not populated.", image);
+ TIMER_RESULTS(start, "ffs_populate_dir");
+
+ /* ensure no outstanding buffers remain */
+ if (debug & DEBUG_FS_MAKEFS)
+ bcleanup();
+
+ /* update various superblock parameters */
+ superblock = fsopts->superblock;
+ superblock->fs_fmod = 0;
+ superblock->fs_old_cstotal.cs_ndir = superblock->fs_cstotal.cs_ndir;
+ superblock->fs_old_cstotal.cs_nbfree = superblock->fs_cstotal.cs_nbfree;
+ superblock->fs_old_cstotal.cs_nifree = superblock->fs_cstotal.cs_nifree;
+ superblock->fs_old_cstotal.cs_nffree = superblock->fs_cstotal.cs_nffree;
+
+ /* write out superblock; image is now complete */
+ ffs_write_superblock(fsopts->superblock, fsopts);
+ if (close(fsopts->fd) == -1)
+ err(1, "Closing `%s'", image);
+ fsopts->fd = -1;
+ printf("Image `%s' complete\n", image);
+}
+
+ /* end of public functions */
+
+
+static void
+ffs_validate(const char *dir, fsnode *root, fsinfo_t *fsopts)
+{
+ int32_t ncg = 1;
+#if notyet
+ int32_t spc, nspf, ncyl, fssize;
+#endif
+ off_t size;
+
+ assert(dir != NULL);
+ assert(root != NULL);
+ assert(fsopts != NULL);
+
+ if (debug & DEBUG_FS_VALIDATE) {
+ printf("ffs_validate: before defaults set:\n");
+ ffs_dump_fsinfo(fsopts);
+ }
+
+ /* set FFS defaults */
+ if (fsopts->sectorsize == -1)
+ fsopts->sectorsize = DFL_SECSIZE;
+ if (fsopts->fsize == -1)
+ fsopts->fsize = MAX(DFL_FRAGSIZE, fsopts->sectorsize);
+ if (fsopts->bsize == -1)
+ fsopts->bsize = MIN(DFL_BLKSIZE, 8 * fsopts->fsize);
+ if (fsopts->cpg == -1)
+ fsopts->cpg = DFL_CYLSPERGROUP;
+ else
+ fsopts->cpgflg = 1;
+ /* fsopts->density is set below */
+ if (fsopts->nsectors == -1)
+ fsopts->nsectors = DFL_NSECTORS;
+ if (fsopts->minfree == -1)
+ fsopts->minfree = MINFREE;
+ if (fsopts->optimization == -1)
+ fsopts->optimization = DEFAULTOPT;
+ if (fsopts->maxcontig == -1)
+ fsopts->maxcontig =
+ MAX(1, MIN(MAXPHYS, FFS_MAXBSIZE) / fsopts->bsize);
+ /* XXX ondisk32 */
+ if (fsopts->maxbpg == -1)
+ fsopts->maxbpg = fsopts->bsize / sizeof(int32_t);
+ if (fsopts->avgfilesize == -1)
+ fsopts->avgfilesize = AVFILESIZ;
+ if (fsopts->avgfpdir == -1)
+ fsopts->avgfpdir = AFPDIR;
+
+ /* calculate size of tree */
+ ffs_size_dir(root, fsopts);
+ fsopts->inodes += ROOTINO; /* include first two inodes */
+
+ if (debug & DEBUG_FS_VALIDATE)
+ printf("ffs_validate: size of tree: %lld bytes, %lld inodes\n",
+ (long long)fsopts->size, (long long)fsopts->inodes);
+
+ /* add requested slop */
+ fsopts->size += fsopts->freeblocks;
+ fsopts->inodes += fsopts->freefiles;
+ if (fsopts->freefilepc > 0)
+ fsopts->inodes =
+ fsopts->inodes * (100 + fsopts->freefilepc) / 100;
+ if (fsopts->freeblockpc > 0)
+ fsopts->size =
+ fsopts->size * (100 + fsopts->freeblockpc) / 100;
+
+ /* add space needed for superblocks */
+ /*
+ * The old SBOFF (SBLOCK_UFS1) is used here because makefs is
+ * typically used for small filesystems where space matters.
+ * XXX make this an option.
+ */
+ fsopts->size += (SBLOCK_UFS1 + SBLOCKSIZE) * ncg;
+ /* add space needed to store inodes, x3 for blockmaps, etc */
+ if (fsopts->version == 1)
+ fsopts->size += ncg * DINODE1_SIZE *
+ roundup(fsopts->inodes / ncg, fsopts->bsize / DINODE1_SIZE);
+ else
+ fsopts->size += ncg * DINODE2_SIZE *
+ roundup(fsopts->inodes / ncg, fsopts->bsize / DINODE2_SIZE);
+
+ /* add minfree */
+ if (fsopts->minfree > 0)
+ fsopts->size =
+ fsopts->size * (100 + fsopts->minfree) / 100;
+ /*
+ * XXX any other fs slop to add, such as csum's, bitmaps, etc ??
+ */
+
+ if (fsopts->size < fsopts->minsize) /* ensure meets minimum size */
+ fsopts->size = fsopts->minsize;
+
+ /* round up to the next block */
+ size = roundup(fsopts->size, fsopts->bsize);
+
+ /* now check calculated sizes vs requested sizes */
+ if (fsopts->maxsize > 0 && size > fsopts->maxsize) {
+ if (debug & DEBUG_FS_VALIDATE) {
+ printf("%s: `%s' size of %lld is larger than the "
+ "maxsize of %lld; rounding down to %lld.",
+ __func__, dir, (long long)size,
+ (long long)fsopts->maxsize,
+ (long long) rounddown(fsopts->size, fsopts->bsize));
+ }
+ size = rounddown(fsopts->size, fsopts->bsize);
+ }
+ fsopts->size = size;
+
+ /* calculate density if necessary */
+ if (fsopts->density == -1)
+ fsopts->density = fsopts->size / fsopts->inodes + 1;
+
+ if (debug & DEBUG_FS_VALIDATE) {
+ printf("ffs_validate: after defaults set:\n");
+ ffs_dump_fsinfo(fsopts);
+ printf("ffs_validate: dir %s; %lld bytes, %lld inodes\n",
+ dir, (long long)fsopts->size, (long long)fsopts->inodes);
+ }
+ sectorsize = fsopts->sectorsize; /* XXX - see earlier */
+}
+
+
+static void
+ffs_dump_fsinfo(fsinfo_t *f)
+{
+
+ printf("fsopts at %p\n", f);
+
+ printf("\tsize %lld, inodes %lld, curinode %u\n",
+ (long long)f->size, (long long)f->inodes, f->curinode);
+
+ printf("\tminsize %lld, maxsize %lld\n",
+ (long long)f->minsize, (long long)f->maxsize);
+ printf("\tfree files %lld, freefile %% %d\n",
+ (long long)f->freefiles, f->freefilepc);
+ printf("\tfree blocks %lld, freeblock %% %d\n",
+ (long long)f->freeblocks, f->freeblockpc);
+ printf("\tneedswap %d, sectorsize %d\n", f->needswap, f->sectorsize);
+
+ printf("\tbsize %d, fsize %d, cpg %d, density %d\n",
+ f->bsize, f->fsize, f->cpg, f->density);
+ printf("\tnsectors %d, rpm %d, minfree %d\n",
+ f->nsectors, f->rpm, f->minfree);
+ printf("\tmaxcontig %d, maxbpg %d\n",
+ f->maxcontig, f->maxbpg);
+ printf("\toptimization %s\n",
+ f->optimization == FS_OPTSPACE ? "space" : "time");
+}
+
+
+static int
+ffs_create_image(const char *image, fsinfo_t *fsopts)
+{
+#if HAVE_STRUCT_STATVFS_F_IOSIZE
+ struct statvfs sfs;
+#endif
+ struct fs *fs;
+ char *buf;
+ int i, bufsize;
+ off_t bufrem;
+
+ assert (image != NULL);
+ assert (fsopts != NULL);
+
+ /* create image */
+ if ((fsopts->fd = open(image, O_RDWR | O_CREAT | O_TRUNC, 0777))
+ == -1) {
+ warn("Can't open `%s' for writing", image);
+ return (-1);
+ }
+
+ /* zero image */
+#if HAVE_STRUCT_STATVFS_F_IOSIZE
+ if (fstatvfs(fsopts->fd, &sfs) == -1) {
+#endif
+ bufsize = 8192;
+#if HAVE_STRUCT_STATVFS_F_IOSIZE
+ warn("can't fstatvfs `%s', using default %d byte chunk",
+ image, bufsize);
+ } else
+ bufsize = sfs.f_iosize;
+#endif
+ bufrem = fsopts->size;
+ if (debug & DEBUG_FS_CREATE_IMAGE)
+ printf(
+ "zero-ing image `%s', %lld sectors, using %d byte chunks\n",
+ image, (long long)bufrem, bufsize);
+ if ((buf = calloc(1, bufsize)) == NULL) {
+ warn("Can't create buffer for sector");
+ return (-1);
+ }
+ while (bufrem > 0) {
+ i = write(fsopts->fd, buf, MIN(bufsize, bufrem));
+ if (i == -1) {
+ warn("zeroing image, %lld bytes to go",
+ (long long)bufrem);
+ return (-1);
+ }
+ bufrem -= i;
+ }
+
+ /* make the file system */
+ if (debug & DEBUG_FS_CREATE_IMAGE)
+ printf("calling mkfs(\"%s\", ...)\n", image);
+ fs = ffs_mkfs(image, fsopts);
+ fsopts->superblock = (void *)fs;
+ if (debug & DEBUG_FS_CREATE_IMAGE) {
+ time_t t;
+
+ t = (time_t)((struct fs *)fsopts->superblock)->fs_time;
+ printf("mkfs returned %p; fs_time %s",
+ fsopts->superblock, ctime(&t));
+ printf("fs totals: nbfree %lld, nffree %lld, nifree %lld, ndir %lld\n",
+ (long long)fs->fs_cstotal.cs_nbfree,
+ (long long)fs->fs_cstotal.cs_nffree,
+ (long long)fs->fs_cstotal.cs_nifree,
+ (long long)fs->fs_cstotal.cs_ndir);
+ }
+
+ if (fs->fs_cstotal.cs_nifree + ROOTINO < fsopts->inodes) {
+ warnx(
+ "Image file `%s' has %lld free inodes; %lld are required.",
+ image,
+ (long long)fs->fs_cstotal.cs_nifree + ROOTINO,
+ (long long)fsopts->inodes);
+ return (-1);
+ }
+ return (fsopts->fd);
+}
+
+
+static void
+ffs_size_dir(fsnode *root, fsinfo_t *fsopts)
+{
+ struct direct tmpdir;
+ fsnode * node;
+ int curdirsize, this;
+
+ /* node may be NULL (empty directory) */
+ assert(fsopts != NULL);
+
+ if (debug & DEBUG_FS_SIZE_DIR)
+ printf("ffs_size_dir: entry: bytes %lld inodes %lld\n",
+ (long long)fsopts->size, (long long)fsopts->inodes);
+
+#define ADDDIRENT(e) do { \
+ tmpdir.d_namlen = strlen((e)); \
+ this = DIRSIZ_SWAP(0, &tmpdir, 0); \
+ if (debug & DEBUG_FS_SIZE_DIR_ADD_DIRENT) \
+ printf("ADDDIRENT: was: %s (%d) this %d cur %d\n", \
+ e, tmpdir.d_namlen, this, curdirsize); \
+ if (this + curdirsize > roundup(curdirsize, DIRBLKSIZ)) \
+ curdirsize = roundup(curdirsize, DIRBLKSIZ); \
+ curdirsize += this; \
+ if (debug & DEBUG_FS_SIZE_DIR_ADD_DIRENT) \
+ printf("ADDDIRENT: now: %s (%d) this %d cur %d\n", \
+ e, tmpdir.d_namlen, this, curdirsize); \
+} while (0);
+
+ /*
+ * XXX this needs to take into account extra space consumed
+ * by indirect blocks, etc.
+ */
+#define ADDSIZE(x) do { \
+ fsopts->size += roundup((x), fsopts->fsize); \
+} while (0);
+
+ curdirsize = 0;
+ for (node = root; node != NULL; node = node->next) {
+ ADDDIRENT(node->name);
+ if (FSNODE_EXCLUDE_P(fsopts, node))
+ continue;
+ if (node == root) { /* we're at "." */
+ assert(strcmp(node->name, ".") == 0);
+ ADDDIRENT("..");
+ } else if ((node->inode->flags & FI_SIZED) == 0) {
+ /* don't count duplicate names */
+ node->inode->flags |= FI_SIZED;
+ if (debug & DEBUG_FS_SIZE_DIR_NODE)
+ printf("ffs_size_dir: `%s' size %lld\n",
+ node->name,
+ (long long)node->inode->st.st_size);
+ fsopts->inodes++;
+ if (node->type == S_IFREG)
+ ADDSIZE(node->inode->st.st_size);
+ if (node->type == S_IFLNK) {
+ int slen;
+
+ slen = strlen(node->symlink) + 1;
+ if (slen >= (fsopts->version == 1 ?
+ MAXSYMLINKLEN_UFS1 :
+ MAXSYMLINKLEN_UFS2))
+ ADDSIZE(slen);
+ }
+ }
+ if (node->type == S_IFDIR)
+ ffs_size_dir(node->child, fsopts);
+ }
+ ADDSIZE(curdirsize);
+
+ if (debug & DEBUG_FS_SIZE_DIR)
+ printf("ffs_size_dir: exit: size %lld inodes %lld\n",
+ (long long)fsopts->size, (long long)fsopts->inodes);
+}
+
+static void *
+ffs_build_dinode1(struct ufs1_dinode *dinp, dirbuf_t *dbufp, fsnode *cur,
+ fsnode *root, fsinfo_t *fsopts)
+{
+ int slen;
+ void *membuf;
+
+ memset(dinp, 0, sizeof(*dinp));
+ dinp->di_mode = cur->inode->st.st_mode;
+ dinp->di_nlink = cur->inode->nlink;
+ dinp->di_size = cur->inode->st.st_size;
+ dinp->di_atime = cur->inode->st.st_atime;
+ dinp->di_mtime = cur->inode->st.st_mtime;
+ dinp->di_ctime = cur->inode->st.st_ctime;
+#if HAVE_STRUCT_STAT_ST_MTIMENSEC
+ dinp->di_atimensec = cur->inode->st.st_atimensec;
+ dinp->di_mtimensec = cur->inode->st.st_mtimensec;
+ dinp->di_ctimensec = cur->inode->st.st_ctimensec;
+#endif
+#if HAVE_STRUCT_STAT_ST_FLAGS
+ dinp->di_flags = cur->inode->st.st_flags;
+#endif
+#if HAVE_STRUCT_STAT_ST_GEN
+ dinp->di_gen = cur->inode->st.st_gen;
+#endif
+ dinp->di_uid = cur->inode->st.st_uid;
+ dinp->di_gid = cur->inode->st.st_gid;
+ /* not set: di_db, di_ib, di_blocks, di_spare */
+
+ membuf = NULL;
+ if (cur == root) { /* "."; write dirbuf */
+ membuf = dbufp->buf;
+ dinp->di_size = dbufp->size;
+ } else if (S_ISBLK(cur->type) || S_ISCHR(cur->type)) {
+ dinp->di_size = 0; /* a device */
+ dinp->di_rdev =
+ ufs_rw32(cur->inode->st.st_rdev, fsopts->needswap);
+ } else if (S_ISLNK(cur->type)) { /* symlink */
+ slen = strlen(cur->symlink);
+ if (slen < MAXSYMLINKLEN_UFS1) { /* short link */
+ memcpy(dinp->di_db, cur->symlink, slen);
+ } else
+ membuf = cur->symlink;
+ dinp->di_size = slen;
+ }
+ return membuf;
+}
+
+static void *
+ffs_build_dinode2(struct ufs2_dinode *dinp, dirbuf_t *dbufp, fsnode *cur,
+ fsnode *root, fsinfo_t *fsopts)
+{
+ int slen;
+ void *membuf;
+
+ memset(dinp, 0, sizeof(*dinp));
+ dinp->di_mode = cur->inode->st.st_mode;
+ dinp->di_nlink = cur->inode->nlink;
+ dinp->di_size = cur->inode->st.st_size;
+ dinp->di_atime = cur->inode->st.st_atime;
+ dinp->di_mtime = cur->inode->st.st_mtime;
+ dinp->di_ctime = cur->inode->st.st_ctime;
+#if HAVE_STRUCT_STAT_ST_MTIMENSEC
+ dinp->di_atimensec = cur->inode->st.st_atimensec;
+ dinp->di_mtimensec = cur->inode->st.st_mtimensec;
+ dinp->di_ctimensec = cur->inode->st.st_ctimensec;
+#endif
+#if HAVE_STRUCT_STAT_ST_FLAGS
+ dinp->di_flags = cur->inode->st.st_flags;
+#endif
+#if HAVE_STRUCT_STAT_ST_GEN
+ dinp->di_gen = cur->inode->st.st_gen;
+#endif
+#if HAVE_STRUCT_STAT_BIRTHTIME
+ dinp->di_birthtime = cur->inode->st.st_birthtime;
+ dinp->di_birthnsec = cur->inode->st.st_birthtimensec;
+#endif
+ dinp->di_uid = cur->inode->st.st_uid;
+ dinp->di_gid = cur->inode->st.st_gid;
+ /* not set: di_db, di_ib, di_blocks, di_spare */
+
+ membuf = NULL;
+ if (cur == root) { /* "."; write dirbuf */
+ membuf = dbufp->buf;
+ dinp->di_size = dbufp->size;
+ } else if (S_ISBLK(cur->type) || S_ISCHR(cur->type)) {
+ dinp->di_size = 0; /* a device */
+ dinp->di_rdev =
+ ufs_rw64(cur->inode->st.st_rdev, fsopts->needswap);
+ } else if (S_ISLNK(cur->type)) { /* symlink */
+ slen = strlen(cur->symlink);
+ if (slen < MAXSYMLINKLEN_UFS2) { /* short link */
+ memcpy(dinp->di_db, cur->symlink, slen);
+ } else
+ membuf = cur->symlink;
+ dinp->di_size = slen;
+ }
+ return membuf;
+}
+
+static int
+ffs_populate_dir(const char *dir, fsnode *root, fsinfo_t *fsopts)
+{
+ fsnode *cur;
+ dirbuf_t dirbuf;
+ union dinode din;
+ void *membuf;
+ char path[MAXPATHLEN + 1];
+
+ assert(dir != NULL);
+ assert(root != NULL);
+ assert(fsopts != NULL);
+
+ (void)memset(&dirbuf, 0, sizeof(dirbuf));
+
+ if (debug & DEBUG_FS_POPULATE)
+ printf("ffs_populate_dir: PASS 1 dir %s node %p\n", dir, root);
+
+ /*
+ * pass 1: allocate inode numbers, build directory `file'
+ */
+ for (cur = root; cur != NULL; cur = cur->next) {
+ if (FSNODE_EXCLUDE_P(fsopts, cur))
+ continue;
+ if ((cur->inode->flags & FI_ALLOCATED) == 0) {
+ cur->inode->flags |= FI_ALLOCATED;
+ if (cur == root && cur->parent != NULL)
+ cur->inode->ino = cur->parent->inode->ino;
+ else {
+ cur->inode->ino = fsopts->curinode;
+ fsopts->curinode++;
+ }
+ }
+ ffs_make_dirbuf(&dirbuf, cur->name, cur, fsopts->needswap);
+ if (cur == root) { /* we're at "."; add ".." */
+ ffs_make_dirbuf(&dirbuf, "..",
+ cur->parent == NULL ? cur : cur->parent->first,
+ fsopts->needswap);
+ root->inode->nlink++; /* count my parent's link */
+ } else if (cur->child != NULL)
+ root->inode->nlink++; /* count my child's link */
+
+ /*
+ * XXX possibly write file and long symlinks here,
+ * ensuring that blocks get written before inodes?
+ * otoh, this isn't a real filesystem, so who
+ * cares about ordering? :-)
+ */
+ }
+ if (debug & DEBUG_FS_POPULATE_DIRBUF)
+ ffs_dump_dirbuf(&dirbuf, dir, fsopts->needswap);
+
+ /*
+ * pass 2: write out dirbuf, then non-directories at this level
+ */
+ if (debug & DEBUG_FS_POPULATE)
+ printf("ffs_populate_dir: PASS 2 dir %s\n", dir);
+ for (cur = root; cur != NULL; cur = cur->next) {
+ if (FSNODE_EXCLUDE_P(fsopts, cur))
+ continue;
+ if (cur->inode->flags & FI_WRITTEN)
+ continue; /* skip hard-linked entries */
+ cur->inode->flags |= FI_WRITTEN;
+
+ if (snprintf(path, sizeof(path), "%s/%s", dir, cur->name)
+ >= sizeof(path))
+ errx(1, "Pathname too long.");
+
+ if (cur->child != NULL)
+ continue; /* child creates own inode */
+
+ /* build on-disk inode */
+ if (fsopts->version == 1)
+ membuf = ffs_build_dinode1(&din.ffs1_din, &dirbuf, cur,
+ root, fsopts);
+ else
+ membuf = ffs_build_dinode2(&din.ffs2_din, &dirbuf, cur,
+ root, fsopts);
+
+ if (debug & DEBUG_FS_POPULATE_NODE) {
+ printf("ffs_populate_dir: writing ino %d, %s",
+ cur->inode->ino, inode_type(cur->type));
+ if (cur->inode->nlink > 1)
+ printf(", nlink %d", cur->inode->nlink);
+ putchar('\n');
+ }
+
+ if (membuf != NULL) {
+ ffs_write_file(&din, cur->inode->ino, membuf, fsopts);
+ } else if (S_ISREG(cur->type)) {
+ ffs_write_file(&din, cur->inode->ino, path, fsopts);
+ } else {
+ assert (! S_ISDIR(cur->type));
+ ffs_write_inode(&din, cur->inode->ino, fsopts);
+ }
+ }
+
+ /*
+ * pass 3: write out sub-directories
+ */
+ if (debug & DEBUG_FS_POPULATE)
+ printf("ffs_populate_dir: PASS 3 dir %s\n", dir);
+ for (cur = root; cur != NULL; cur = cur->next) {
+ if (FSNODE_EXCLUDE_P(fsopts, cur))
+ continue;
+ if (cur->child == NULL)
+ continue;
+ if (snprintf(path, sizeof(path), "%s/%s", dir, cur->name)
+ >= sizeof(path))
+ errx(1, "Pathname too long.");
+ if (! ffs_populate_dir(path, cur->child, fsopts))
+ return (0);
+ }
+
+ if (debug & DEBUG_FS_POPULATE)
+ printf("ffs_populate_dir: DONE dir %s\n", dir);
+
+ /* cleanup */
+ if (dirbuf.buf != NULL)
+ free(dirbuf.buf);
+ return (1);
+}
+
+
+static void
+ffs_write_file(union dinode *din, uint32_t ino, void *buf, fsinfo_t *fsopts)
+{
+ int isfile, ffd;
+ char *fbuf, *p;
+ off_t bufleft, chunk, offset;
+ struct inode in;
+ struct buf * bp;
+
+ assert (din != NULL);
+ assert (buf != NULL);
+ assert (fsopts != NULL);
+
+ isfile = S_ISREG(DIP(din, mode));
+ fbuf = NULL;
+ ffd = -1;
+
+ in.i_fs = (struct fs *)fsopts->superblock;
+
+ if (debug & DEBUG_FS_WRITE_FILE) {
+ printf(
+ "ffs_write_file: ino %u, din %p, isfile %d, %s, size %lld",
+ ino, din, isfile, inode_type(DIP(din, mode) & S_IFMT),
+ (long long)DIP(din, size));
+ if (isfile)
+ printf(", file '%s'\n", (char *)buf);
+ else
+ printf(", buffer %p\n", buf);
+ }
+
+ in.i_number = ino;
+ in.i_size = DIP(din, size);
+ if (fsopts->version == 1)
+ memcpy(&in.i_din.ffs1_din, &din->ffs1_din,
+ sizeof(in.i_din.ffs1_din));
+ else
+ memcpy(&in.i_din.ffs2_din, &din->ffs2_din,
+ sizeof(in.i_din.ffs2_din));
+ in.i_fd = fsopts->fd;
+
+ if (DIP(din, size) == 0)
+ goto write_inode_and_leave; /* mmm, cheating */
+
+ if (isfile) {
+ if ((fbuf = malloc(fsopts->bsize)) == NULL)
+ err(1, "Allocating memory for write buffer");
+ if ((ffd = open((char *)buf, O_RDONLY, 0444)) == -1) {
+ warn("Can't open `%s' for reading", (char *)buf);
+ goto leave_ffs_write_file;
+ }
+ } else {
+ p = buf;
+ }
+
+ chunk = 0;
+ for (bufleft = DIP(din, size); bufleft > 0; bufleft -= chunk) {
+ chunk = MIN(bufleft, fsopts->bsize);
+ if (isfile) {
+ if (read(ffd, fbuf, chunk) != chunk)
+ err(1, "Reading `%s', %lld bytes to go",
+ (char *)buf, (long long)bufleft);
+ p = fbuf;
+ }
+ offset = DIP(din, size) - bufleft;
+ if (debug & DEBUG_FS_WRITE_FILE_BLOCK)
+ printf(
+ "ffs_write_file: write %p offset %lld size %lld left %lld\n",
+ p, (long long)offset,
+ (long long)chunk, (long long)bufleft);
+ /*
+ * XXX if holey support is desired, do the check here
+ *
+ * XXX might need to write out last bit in fragroundup
+ * sized chunk. however, ffs_balloc() handles this for us
+ */
+ errno = ffs_balloc(&in, offset, chunk, &bp);
+ bad_ffs_write_file:
+ if (errno != 0)
+ err(1,
+ "Writing inode %d (%s), bytes %lld + %lld",
+ ino,
+ isfile ? (char *)buf :
+ inode_type(DIP(din, mode) & S_IFMT),
+ (long long)offset, (long long)chunk);
+ memcpy(bp->b_data, p, chunk);
+ errno = bwrite(bp);
+ if (errno != 0)
+ goto bad_ffs_write_file;
+ brelse(bp);
+ if (!isfile)
+ p += chunk;
+ }
+
+ write_inode_and_leave:
+ ffs_write_inode(&in.i_din, in.i_number, fsopts);
+
+ leave_ffs_write_file:
+ if (fbuf)
+ free(fbuf);
+ if (ffd != -1)
+ close(ffd);
+}
+
+
+static void
+ffs_dump_dirbuf(dirbuf_t *dbuf, const char *dir, int needswap)
+{
+ doff_t i;
+ struct direct *de;
+ uint16_t reclen;
+
+ assert (dbuf != NULL);
+ assert (dir != NULL);
+ printf("ffs_dump_dirbuf: dir %s size %d cur %d\n",
+ dir, dbuf->size, dbuf->cur);
+
+ for (i = 0; i < dbuf->size; ) {
+ de = (struct direct *)(dbuf->buf + i);
+ reclen = ufs_rw16(de->d_reclen, needswap);
+ printf(
+ " inode %4d %7s offset %4d reclen %3d namlen %3d name %s\n",
+ ufs_rw32(de->d_ino, needswap),
+ inode_type(DTTOIF(de->d_type)), i, reclen,
+ de->d_namlen, de->d_name);
+ i += reclen;
+ assert(reclen > 0);
+ }
+}
+
+static void
+ffs_make_dirbuf(dirbuf_t *dbuf, const char *name, fsnode *node, int needswap)
+{
+ struct direct de, *dp;
+ uint16_t llen, reclen;
+ char *newbuf;
+
+ assert (dbuf != NULL);
+ assert (name != NULL);
+ assert (node != NULL);
+ /* create direct entry */
+ (void)memset(&de, 0, sizeof(de));
+ de.d_ino = ufs_rw32(node->inode->ino, needswap);
+ de.d_type = IFTODT(node->type);
+ de.d_namlen = (uint8_t)strlen(name);
+ strcpy(de.d_name, name);
+ reclen = DIRSIZ_SWAP(0, &de, needswap);
+ de.d_reclen = ufs_rw16(reclen, needswap);
+
+ dp = (struct direct *)(dbuf->buf + dbuf->cur);
+ llen = 0;
+ if (dp != NULL)
+ llen = DIRSIZ_SWAP(0, dp, needswap);
+
+ if (debug & DEBUG_FS_MAKE_DIRBUF)
+ printf(
+ "ffs_make_dirbuf: dbuf siz %d cur %d lastlen %d\n"
+ " ino %d type %d reclen %d namlen %d name %.30s\n",
+ dbuf->size, dbuf->cur, llen,
+ ufs_rw32(de.d_ino, needswap), de.d_type, reclen,
+ de.d_namlen, de.d_name);
+
+ if (reclen + dbuf->cur + llen > roundup(dbuf->size, DIRBLKSIZ)) {
+ if (debug & DEBUG_FS_MAKE_DIRBUF)
+ printf("ffs_make_dirbuf: growing buf to %d\n",
+ dbuf->size + DIRBLKSIZ);
+ if ((newbuf = realloc(dbuf->buf, dbuf->size + DIRBLKSIZ)) == NULL)
+ err(1, "Allocating memory for directory buffer");
+ dbuf->buf = newbuf;
+ dbuf->size += DIRBLKSIZ;
+ memset(dbuf->buf + dbuf->size - DIRBLKSIZ, 0, DIRBLKSIZ);
+ dbuf->cur = dbuf->size - DIRBLKSIZ;
+ } else { /* shrink end of previous */
+ dp->d_reclen = ufs_rw16(llen,needswap);
+ dbuf->cur += llen;
+ }
+ dp = (struct direct *)(dbuf->buf + dbuf->cur);
+ memcpy(dp, &de, reclen);
+ dp->d_reclen = ufs_rw16(dbuf->size - dbuf->cur, needswap);
+}
+
+/*
+ * cribbed from sys/ufs/ffs/ffs_alloc.c
+ */
+static void
+ffs_write_inode(union dinode *dp, uint32_t ino, const fsinfo_t *fsopts)
+{
+ char *buf;
+ struct ufs1_dinode *dp1;
+ struct ufs2_dinode *dp2, *dip;
+ struct cg *cgp;
+ struct fs *fs;
+ int cg, cgino, i;
+ daddr_t d;
+ char sbbuf[FFS_MAXBSIZE];
+ int32_t initediblk;
+
+ assert (dp != NULL);
+ assert (ino > 0);
+ assert (fsopts != NULL);
+
+ fs = (struct fs *)fsopts->superblock;
+ cg = ino_to_cg(fs, ino);
+ cgino = ino % fs->fs_ipg;
+ if (debug & DEBUG_FS_WRITE_INODE)
+ printf("ffs_write_inode: din %p ino %u cg %d cgino %d\n",
+ dp, ino, cg, cgino);
+
+ ffs_rdfs(fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, &sbbuf,
+ fsopts);
+ cgp = (struct cg *)sbbuf;
+ if (!cg_chkmagic_swap(cgp, fsopts->needswap))
+ errx(1, "ffs_write_inode: cg %d: bad magic number", cg);
+
+ assert (isclr(cg_inosused_swap(cgp, fsopts->needswap), cgino));
+
+ buf = malloc(fs->fs_bsize);
+ if (buf == NULL)
+ errx(1, "ffs_write_inode: cg %d: can't alloc inode block", cg);
+
+ dp1 = (struct ufs1_dinode *)buf;
+ dp2 = (struct ufs2_dinode *)buf;
+
+ if (fs->fs_cstotal.cs_nifree == 0)
+ errx(1, "ffs_write_inode: fs out of inodes for ino %u",
+ ino);
+ if (fs->fs_cs(fs, cg).cs_nifree == 0)
+ errx(1,
+ "ffs_write_inode: cg %d out of inodes for ino %u",
+ cg, ino);
+ setbit(cg_inosused_swap(cgp, fsopts->needswap), cgino);
+ ufs_add32(cgp->cg_cs.cs_nifree, -1, fsopts->needswap);
+ fs->fs_cstotal.cs_nifree--;
+ fs->fs_cs(fs, cg).cs_nifree--;
+ if (S_ISDIR(DIP(dp, mode))) {
+ ufs_add32(cgp->cg_cs.cs_ndir, 1, fsopts->needswap);
+ fs->fs_cstotal.cs_ndir++;
+ fs->fs_cs(fs, cg).cs_ndir++;
+ }
+
+ /*
+ * Initialize inode blocks on the fly for UFS2.
+ */
+ initediblk = ufs_rw32(cgp->cg_initediblk, fsopts->needswap);
+ if (fsopts->version == 2 && cgino + INOPB(fs) > initediblk &&
+ initediblk < ufs_rw32(cgp->cg_niblk, fsopts->needswap)) {
+ memset(buf, 0, fs->fs_bsize);
+ dip = (struct ufs2_dinode *)buf;
+ srandom(time(NULL));
+ for (i = 0; i < INOPB(fs); i++) {
+ dip->di_gen = random() / 2 + 1;
+ dip++;
+ }
+ ffs_wtfs(fsbtodb(fs, ino_to_fsba(fs,
+ cg * fs->fs_ipg + initediblk)),
+ fs->fs_bsize, buf, fsopts);
+ initediblk += INOPB(fs);
+ cgp->cg_initediblk = ufs_rw32(initediblk, fsopts->needswap);
+ }
+
+
+ ffs_wtfs(fsbtodb(fs, cgtod(fs, cg)), (int)fs->fs_cgsize, &sbbuf,
+ fsopts);
+
+ /* now write inode */
+ d = fsbtodb(fs, ino_to_fsba(fs, ino));
+ ffs_rdfs(d, fs->fs_bsize, buf, fsopts);
+ if (fsopts->needswap) {
+ if (fsopts->version == 1)
+ ffs_dinode1_swap(&dp->ffs1_din,
+ &dp1[ino_to_fsbo(fs, ino)]);
+ else
+ ffs_dinode2_swap(&dp->ffs2_din,
+ &dp2[ino_to_fsbo(fs, ino)]);
+ } else {
+ if (fsopts->version == 1)
+ dp1[ino_to_fsbo(fs, ino)] = dp->ffs1_din;
+ else
+ dp2[ino_to_fsbo(fs, ino)] = dp->ffs2_din;
+ }
+ ffs_wtfs(d, fs->fs_bsize, buf, fsopts);
+ free(buf);
+}
+
+void
+panic(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vwarnx(fmt, ap);
+ va_end(ap);
+ exit(1);
+}
diff --git a/usr.sbin/makefs/ffs/buf.c b/usr.sbin/makefs/ffs/buf.c
new file mode 100644
index 0000000..08fb627
--- /dev/null
+++ b/usr.sbin/makefs/ffs/buf.c
@@ -0,0 +1,222 @@
+/* $NetBSD: buf.c,v 1.12 2004/06/20 22:20:18 jmc Exp $ */
+
+/*
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "makefs.h"
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include "ffs/buf.h"
+#include "ffs/ufs_inode.h"
+
+extern int sectorsize; /* XXX: from ffs.c & mkfs.c */
+
+TAILQ_HEAD(buftailhead,buf) buftail;
+
+int
+bread(int fd, struct fs *fs, daddr_t blkno, int size, struct buf **bpp)
+{
+ off_t offset;
+ ssize_t rv;
+
+ assert (fs != NULL);
+ assert (bpp != NULL);
+
+ if (debug & DEBUG_BUF_BREAD)
+ printf("bread: fs %p blkno %lld size %d\n",
+ fs, (long long)blkno, size);
+ *bpp = getblk(fd, fs, blkno, size);
+ offset = (*bpp)->b_blkno * sectorsize; /* XXX */
+ if (debug & DEBUG_BUF_BREAD)
+ printf("bread: bp %p blkno %lld offset %lld bcount %ld\n",
+ (*bpp), (long long)(*bpp)->b_blkno, (long long) offset,
+ (*bpp)->b_bcount);
+ if (lseek((*bpp)->b_fd, offset, SEEK_SET) == -1)
+ err(1, "bread: lseek %lld (%lld)",
+ (long long)(*bpp)->b_blkno, (long long)offset);
+ rv = read((*bpp)->b_fd, (*bpp)->b_data, (*bpp)->b_bcount);
+ if (debug & DEBUG_BUF_BREAD)
+ printf("bread: read %ld (%lld) returned %d\n",
+ (*bpp)->b_bcount, (long long)offset, (int)rv);
+ if (rv == -1) /* read error */
+ err(1, "bread: read %ld (%lld) returned %d",
+ (*bpp)->b_bcount, (long long)offset, (int)rv);
+ else if (rv != (*bpp)->b_bcount) /* short read */
+ err(1, "bread: read %ld (%lld) returned %d",
+ (*bpp)->b_bcount, (long long)offset, (int)rv);
+ else
+ return (0);
+}
+
+void
+brelse(struct buf *bp)
+{
+
+ assert (bp != NULL);
+ assert (bp->b_data != NULL);
+
+ if (bp->b_lblkno < 0) {
+ /*
+ * XXX don't remove any buffers with negative logical block
+ * numbers (lblkno), so that we retain the mapping
+ * of negative lblkno -> real blkno that ffs_balloc()
+ * sets up.
+ *
+ * if we instead released these buffers, and implemented
+ * ufs_strategy() (and ufs_bmaparray()) and called those
+ * from bread() and bwrite() to convert the lblkno to
+ * a real blkno, we'd add a lot more code & complexity
+ * and reading off disk, for little gain, because this
+ * simple hack works for our purpose.
+ */
+ bp->b_bcount = 0;
+ return;
+ }
+
+ TAILQ_REMOVE(&buftail, bp, b_tailq);
+ free(bp->b_data);
+ free(bp);
+}
+
+int
+bwrite(struct buf *bp)
+{
+ off_t offset;
+ ssize_t rv;
+
+ assert (bp != NULL);
+ offset = bp->b_blkno * sectorsize; /* XXX */
+ if (debug & DEBUG_BUF_BWRITE)
+ printf("bwrite: bp %p blkno %lld offset %lld bcount %ld\n",
+ bp, (long long)bp->b_blkno, (long long) offset,
+ bp->b_bcount);
+ if (lseek(bp->b_fd, offset, SEEK_SET) == -1)
+ return (errno);
+ rv = write(bp->b_fd, bp->b_data, bp->b_bcount);
+ if (debug & DEBUG_BUF_BWRITE)
+ printf("bwrite: write %ld (offset %lld) returned %lld\n",
+ bp->b_bcount, (long long)offset, (long long)rv);
+ if (rv == bp->b_bcount)
+ return (0);
+ else if (rv == -1) /* write error */
+ return (errno);
+ else /* short write ? */
+ return (EAGAIN);
+}
+
+void
+bcleanup(void)
+{
+ struct buf *bp;
+
+ /*
+ * XXX this really shouldn't be necessary, but i'm curious to
+ * know why there's still some buffers lying around that
+ * aren't brelse()d
+ */
+
+ if (TAILQ_EMPTY(&buftail))
+ return;
+
+ printf("bcleanup: unflushed buffers:\n");
+ TAILQ_FOREACH(bp, &buftail, b_tailq) {
+ printf("\tlblkno %10lld blkno %10lld count %6ld bufsize %6ld\n",
+ (long long)bp->b_lblkno, (long long)bp->b_blkno,
+ bp->b_bcount, bp->b_bufsize);
+ }
+ printf("bcleanup: done\n");
+}
+
+struct buf *
+getblk(int fd, struct fs *fs, daddr_t blkno, int size)
+{
+ static int buftailinitted;
+ struct buf *bp;
+ void *n;
+
+ assert (fs != NULL);
+ if (debug & DEBUG_BUF_GETBLK)
+ printf("getblk: fs %p blkno %lld size %d\n", fs,
+ (long long)blkno, size);
+
+ bp = NULL;
+ if (!buftailinitted) {
+ if (debug & DEBUG_BUF_GETBLK)
+ printf("getblk: initialising tailq\n");
+ TAILQ_INIT(&buftail);
+ buftailinitted = 1;
+ } else {
+ TAILQ_FOREACH(bp, &buftail, b_tailq) {
+ if (bp->b_lblkno != blkno)
+ continue;
+ break;
+ }
+ }
+ if (bp == NULL) {
+ if ((bp = calloc(1, sizeof(struct buf))) == NULL)
+ err(1, "getblk: calloc");
+
+ bp->b_bufsize = 0;
+ bp->b_blkno = bp->b_lblkno = blkno;
+ bp->b_fd = fd;
+ bp->b_fs = fs;
+ bp->b_data = NULL;
+ TAILQ_INSERT_HEAD(&buftail, bp, b_tailq);
+ }
+ bp->b_bcount = size;
+ if (bp->b_data == NULL || bp->b_bcount > bp->b_bufsize) {
+ n = realloc(bp->b_data, size);
+ if (n == NULL)
+ err(1, "getblk: realloc b_data %ld", bp->b_bcount);
+ bp->b_data = n;
+ bp->b_bufsize = size;
+ }
+
+ return (bp);
+}
diff --git a/usr.sbin/makefs/ffs/buf.h b/usr.sbin/makefs/ffs/buf.h
new file mode 100644
index 0000000..02c6713
--- /dev/null
+++ b/usr.sbin/makefs/ffs/buf.h
@@ -0,0 +1,67 @@
+/* $NetBSD: buf.h,v 1.2 2001/11/02 03:12:49 lukem Exp $ */
+
+/*
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _FFS_BUF_H
+#define _FFS_BUF_H
+
+#include <sys/param.h>
+#include <sys/queue.h>
+
+struct buf {
+ void * b_data;
+ long b_bufsize;
+ long b_bcount;
+ daddr_t b_blkno;
+ daddr_t b_lblkno;
+ int b_fd;
+ struct fs * b_fs;
+
+ TAILQ_ENTRY(buf) b_tailq;
+};
+
+void bcleanup(void);
+int bread(int, struct fs *, daddr_t, int, struct buf **);
+void brelse(struct buf *);
+int bwrite(struct buf *);
+struct buf * getblk(int, struct fs *, daddr_t, int);
+
+#define bdwrite(bp) bwrite(bp)
+#define clrbuf(bp) memset((bp)->b_data, 0, (u_int)(bp)->b_bcount)
+
+#endif /* _FFS_BUF_H */
diff --git a/usr.sbin/makefs/ffs/ffs_alloc.c b/usr.sbin/makefs/ffs/ffs_alloc.c
new file mode 100644
index 0000000..0fe65e6
--- /dev/null
+++ b/usr.sbin/makefs/ffs/ffs_alloc.c
@@ -0,0 +1,683 @@
+/* $NetBSD: ffs_alloc.c,v 1.14 2004/06/20 22:20:18 jmc Exp $ */
+/* From: NetBSD: ffs_alloc.c,v 1.50 2001/09/06 02:16:01 lukem Exp */
+
+/*
+ * 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) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ffs_alloc.c 8.19 (Berkeley) 7/13/95
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <errno.h>
+
+#include "makefs.h"
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include "ffs/ufs_bswap.h"
+#include "ffs/buf.h"
+#include "ffs/ufs_inode.h"
+#include "ffs/ffs_extern.h"
+
+static int scanc(u_int, const u_char *, const u_char *, int);
+
+static daddr_t ffs_alloccg(struct inode *, int, daddr_t, int);
+static daddr_t ffs_alloccgblk(struct inode *, struct buf *, daddr_t);
+static daddr_t ffs_hashalloc(struct inode *, int, daddr_t, int,
+ daddr_t (*)(struct inode *, int, daddr_t, int));
+static int32_t ffs_mapsearch(struct fs *, struct cg *, daddr_t, int);
+
+/*
+ * Allocate a block in the file system.
+ *
+ * The size of the requested block is given, which must be some
+ * multiple of fs_fsize and <= fs_bsize.
+ * A preference may be optionally specified. If a preference is given
+ * the following hierarchy is used to allocate a block:
+ * 1) allocate the requested block.
+ * 2) allocate a rotationally optimal block in the same cylinder.
+ * 3) allocate a block in the same cylinder group.
+ * 4) quadradically rehash into other cylinder groups, until an
+ * available block is located.
+ * If no block preference is given the following hierarchy is used
+ * to allocate a block:
+ * 1) allocate a block in the cylinder group that contains the
+ * inode for the file.
+ * 2) quadradically rehash into other cylinder groups, until an
+ * available block is located.
+ */
+int
+ffs_alloc(struct inode *ip, daddr_t lbn, daddr_t bpref, int size,
+ daddr_t *bnp)
+{
+ struct fs *fs = ip->i_fs;
+ daddr_t bno;
+ int cg;
+
+ *bnp = 0;
+ if ((u_int)size > fs->fs_bsize || fragoff(fs, size) != 0) {
+ errx(1, "ffs_alloc: bad size: bsize %d size %d",
+ fs->fs_bsize, size);
+ }
+ if (size == fs->fs_bsize && fs->fs_cstotal.cs_nbfree == 0)
+ goto nospace;
+ if (bpref >= fs->fs_size)
+ bpref = 0;
+ if (bpref == 0)
+ cg = ino_to_cg(fs, ip->i_number);
+ else
+ cg = dtog(fs, bpref);
+ bno = ffs_hashalloc(ip, cg, bpref, size, ffs_alloccg);
+ if (bno > 0) {
+ if (ip->i_fs->fs_magic == FS_UFS1_MAGIC)
+ ip->i_ffs1_blocks += size / DEV_BSIZE;
+ else
+ ip->i_ffs2_blocks += size / DEV_BSIZE;
+ *bnp = bno;
+ return (0);
+ }
+nospace:
+ return (ENOSPC);
+}
+
+/*
+ * Select the desired position for the next block in a file. The file is
+ * logically divided into sections. The first section is composed of the
+ * direct blocks. Each additional section contains fs_maxbpg blocks.
+ *
+ * If no blocks have been allocated in the first section, the policy is to
+ * request a block in the same cylinder group as the inode that describes
+ * the file. If no blocks have been allocated in any other section, the
+ * policy is to place the section in a cylinder group with a greater than
+ * average number of free blocks. An appropriate cylinder group is found
+ * by using a rotor that sweeps the cylinder groups. When a new group of
+ * blocks is needed, the sweep begins in the cylinder group following the
+ * cylinder group from which the previous allocation was made. The sweep
+ * continues until a cylinder group with greater than the average number
+ * of free blocks is found. If the allocation is for the first block in an
+ * indirect block, the information on the previous allocation is unavailable;
+ * here a best guess is made based upon the logical block number being
+ * allocated.
+ *
+ * If a section is already partially allocated, the policy is to
+ * contiguously allocate fs_maxcontig blocks. The end of one of these
+ * contiguous blocks and the beginning of the next is physically separated
+ * so that the disk head will be in transit between them for at least
+ * fs_rotdelay milliseconds. This is to allow time for the processor to
+ * schedule another I/O transfer.
+ */
+/* XXX ondisk32 */
+daddr_t
+ffs_blkpref_ufs1(struct inode *ip, daddr_t lbn, int indx, int32_t *bap)
+{
+ struct fs *fs;
+ int cg;
+ int avgbfree, startcg;
+
+ fs = ip->i_fs;
+ if (indx % fs->fs_maxbpg == 0 || bap[indx - 1] == 0) {
+ if (lbn < NDADDR + NINDIR(fs)) {
+ cg = ino_to_cg(fs, ip->i_number);
+ return (fs->fs_fpg * cg + fs->fs_frag);
+ }
+ /*
+ * Find a cylinder with greater than average number of
+ * unused data blocks.
+ */
+ if (indx == 0 || bap[indx - 1] == 0)
+ startcg =
+ ino_to_cg(fs, ip->i_number) + lbn / fs->fs_maxbpg;
+ else
+ startcg = dtog(fs,
+ ufs_rw32(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + 1);
+ startcg %= fs->fs_ncg;
+ avgbfree = fs->fs_cstotal.cs_nbfree / fs->fs_ncg;
+ for (cg = startcg; cg < fs->fs_ncg; cg++)
+ if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree)
+ return (fs->fs_fpg * cg + fs->fs_frag);
+ for (cg = 0; cg <= startcg; cg++)
+ if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree)
+ return (fs->fs_fpg * cg + fs->fs_frag);
+ return (0);
+ }
+ /*
+ * We just always try to lay things out contiguously.
+ */
+ return ufs_rw32(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + fs->fs_frag;
+}
+
+daddr_t
+ffs_blkpref_ufs2(ip, lbn, indx, bap)
+ struct inode *ip;
+ daddr_t lbn;
+ int indx;
+ int64_t *bap;
+{
+ struct fs *fs;
+ int cg;
+ int avgbfree, startcg;
+
+ fs = ip->i_fs;
+ if (indx % fs->fs_maxbpg == 0 || bap[indx - 1] == 0) {
+ if (lbn < NDADDR + NINDIR(fs)) {
+ cg = ino_to_cg(fs, ip->i_number);
+ return (fs->fs_fpg * cg + fs->fs_frag);
+ }
+ /*
+ * Find a cylinder with greater than average number of
+ * unused data blocks.
+ */
+ if (indx == 0 || bap[indx - 1] == 0)
+ startcg =
+ ino_to_cg(fs, ip->i_number) + lbn / fs->fs_maxbpg;
+ else
+ startcg = dtog(fs,
+ ufs_rw64(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + 1);
+ startcg %= fs->fs_ncg;
+ avgbfree = fs->fs_cstotal.cs_nbfree / fs->fs_ncg;
+ for (cg = startcg; cg < fs->fs_ncg; cg++)
+ if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) {
+ return (fs->fs_fpg * cg + fs->fs_frag);
+ }
+ for (cg = 0; cg < startcg; cg++)
+ if (fs->fs_cs(fs, cg).cs_nbfree >= avgbfree) {
+ return (fs->fs_fpg * cg + fs->fs_frag);
+ }
+ return (0);
+ }
+ /*
+ * We just always try to lay things out contiguously.
+ */
+ return ufs_rw64(bap[indx - 1], UFS_FSNEEDSWAP(fs)) + fs->fs_frag;
+}
+
+/*
+ * Implement the cylinder overflow algorithm.
+ *
+ * The policy implemented by this algorithm is:
+ * 1) allocate the block in its requested cylinder group.
+ * 2) quadradically rehash on the cylinder group number.
+ * 3) brute force search for a free block.
+ *
+ * `size': size for data blocks, mode for inodes
+ */
+/*VARARGS5*/
+static daddr_t
+ffs_hashalloc(struct inode *ip, int cg, daddr_t pref, int size,
+ daddr_t (*allocator)(struct inode *, int, daddr_t, int))
+{
+ struct fs *fs;
+ daddr_t result;
+ int i, icg = cg;
+
+ fs = ip->i_fs;
+ /*
+ * 1: preferred cylinder group
+ */
+ result = (*allocator)(ip, cg, pref, size);
+ if (result)
+ return (result);
+ /*
+ * 2: quadratic rehash
+ */
+ for (i = 1; i < fs->fs_ncg; i *= 2) {
+ cg += i;
+ if (cg >= fs->fs_ncg)
+ cg -= fs->fs_ncg;
+ result = (*allocator)(ip, cg, 0, size);
+ if (result)
+ return (result);
+ }
+ /*
+ * 3: brute force search
+ * Note that we start at i == 2, since 0 was checked initially,
+ * and 1 is always checked in the quadratic rehash.
+ */
+ cg = (icg + 2) % fs->fs_ncg;
+ for (i = 2; i < fs->fs_ncg; i++) {
+ result = (*allocator)(ip, cg, 0, size);
+ if (result)
+ return (result);
+ cg++;
+ if (cg == fs->fs_ncg)
+ cg = 0;
+ }
+ return (0);
+}
+
+/*
+ * Determine whether a block can be allocated.
+ *
+ * Check to see if a block of the appropriate size is available,
+ * and if it is, allocate it.
+ */
+static daddr_t
+ffs_alloccg(struct inode *ip, int cg, daddr_t bpref, int size)
+{
+ struct cg *cgp;
+ struct buf *bp;
+ daddr_t bno, blkno;
+ int error, frags, allocsiz, i;
+ struct fs *fs = ip->i_fs;
+ const int needswap = UFS_FSNEEDSWAP(fs);
+
+ if (fs->fs_cs(fs, cg).cs_nbfree == 0 && size == fs->fs_bsize)
+ return (0);
+ error = bread(ip->i_fd, ip->i_fs, fsbtodb(fs, cgtod(fs, cg)),
+ (int)fs->fs_cgsize, &bp);
+ if (error) {
+ brelse(bp);
+ return (0);
+ }
+ cgp = (struct cg *)bp->b_data;
+ if (!cg_chkmagic_swap(cgp, needswap) ||
+ (cgp->cg_cs.cs_nbfree == 0 && size == fs->fs_bsize)) {
+ brelse(bp);
+ return (0);
+ }
+ if (size == fs->fs_bsize) {
+ bno = ffs_alloccgblk(ip, bp, bpref);
+ bdwrite(bp);
+ return (bno);
+ }
+ /*
+ * check to see if any fragments are already available
+ * allocsiz is the size which will be allocated, hacking
+ * it down to a smaller size if necessary
+ */
+ frags = numfrags(fs, size);
+ for (allocsiz = frags; allocsiz < fs->fs_frag; allocsiz++)
+ if (cgp->cg_frsum[allocsiz] != 0)
+ break;
+ if (allocsiz == fs->fs_frag) {
+ /*
+ * no fragments were available, so a block will be
+ * allocated, and hacked up
+ */
+ if (cgp->cg_cs.cs_nbfree == 0) {
+ brelse(bp);
+ return (0);
+ }
+ bno = ffs_alloccgblk(ip, bp, bpref);
+ bpref = dtogd(fs, bno);
+ for (i = frags; i < fs->fs_frag; i++)
+ setbit(cg_blksfree_swap(cgp, needswap), bpref + i);
+ i = fs->fs_frag - frags;
+ ufs_add32(cgp->cg_cs.cs_nffree, i, needswap);
+ fs->fs_cstotal.cs_nffree += i;
+ fs->fs_cs(fs, cg).cs_nffree += i;
+ fs->fs_fmod = 1;
+ ufs_add32(cgp->cg_frsum[i], 1, needswap);
+ bdwrite(bp);
+ return (bno);
+ }
+ bno = ffs_mapsearch(fs, cgp, bpref, allocsiz);
+ for (i = 0; i < frags; i++)
+ clrbit(cg_blksfree_swap(cgp, needswap), bno + i);
+ ufs_add32(cgp->cg_cs.cs_nffree, -frags, needswap);
+ fs->fs_cstotal.cs_nffree -= frags;
+ fs->fs_cs(fs, cg).cs_nffree -= frags;
+ fs->fs_fmod = 1;
+ ufs_add32(cgp->cg_frsum[allocsiz], -1, needswap);
+ if (frags != allocsiz)
+ ufs_add32(cgp->cg_frsum[allocsiz - frags], 1, needswap);
+ blkno = cg * fs->fs_fpg + bno;
+ bdwrite(bp);
+ return blkno;
+}
+
+/*
+ * Allocate a block in a cylinder group.
+ *
+ * This algorithm implements the following policy:
+ * 1) allocate the requested block.
+ * 2) allocate a rotationally optimal block in the same cylinder.
+ * 3) allocate the next available block on the block rotor for the
+ * specified cylinder group.
+ * Note that this routine only allocates fs_bsize blocks; these
+ * blocks may be fragmented by the routine that allocates them.
+ */
+static daddr_t
+ffs_alloccgblk(struct inode *ip, struct buf *bp, daddr_t bpref)
+{
+ struct cg *cgp;
+ daddr_t blkno;
+ int32_t bno;
+ struct fs *fs = ip->i_fs;
+ const int needswap = UFS_FSNEEDSWAP(fs);
+ u_int8_t *blksfree;
+
+ cgp = (struct cg *)bp->b_data;
+ blksfree = cg_blksfree_swap(cgp, needswap);
+ if (bpref == 0 || dtog(fs, bpref) != ufs_rw32(cgp->cg_cgx, needswap)) {
+ bpref = ufs_rw32(cgp->cg_rotor, needswap);
+ } else {
+ bpref = blknum(fs, bpref);
+ bno = dtogd(fs, bpref);
+ /*
+ * if the requested block is available, use it
+ */
+ if (ffs_isblock(fs, blksfree, fragstoblks(fs, bno)))
+ goto gotit;
+ }
+ /*
+ * Take the next available one in this cylinder group.
+ */
+ bno = ffs_mapsearch(fs, cgp, bpref, (int)fs->fs_frag);
+ if (bno < 0)
+ return (0);
+ cgp->cg_rotor = ufs_rw32(bno, needswap);
+gotit:
+ blkno = fragstoblks(fs, bno);
+ ffs_clrblock(fs, blksfree, (long)blkno);
+ ffs_clusteracct(fs, cgp, blkno, -1);
+ ufs_add32(cgp->cg_cs.cs_nbfree, -1, needswap);
+ fs->fs_cstotal.cs_nbfree--;
+ fs->fs_cs(fs, ufs_rw32(cgp->cg_cgx, needswap)).cs_nbfree--;
+ fs->fs_fmod = 1;
+ blkno = ufs_rw32(cgp->cg_cgx, needswap) * fs->fs_fpg + bno;
+ return (blkno);
+}
+
+/*
+ * Free a block or fragment.
+ *
+ * The specified block or fragment is placed back in the
+ * free map. If a fragment is deallocated, a possible
+ * block reassembly is checked.
+ */
+void
+ffs_blkfree(struct inode *ip, daddr_t bno, long size)
+{
+ struct cg *cgp;
+ struct buf *bp;
+ int32_t fragno, cgbno;
+ int i, error, cg, blk, frags, bbase;
+ struct fs *fs = ip->i_fs;
+ const int needswap = UFS_FSNEEDSWAP(fs);
+
+ if ((u_int)size > fs->fs_bsize || fragoff(fs, size) != 0 ||
+ fragnum(fs, bno) + numfrags(fs, size) > fs->fs_frag) {
+ errx(1, "blkfree: bad size: bno %lld bsize %d size %ld",
+ (long long)bno, fs->fs_bsize, size);
+ }
+ cg = dtog(fs, bno);
+ if (bno >= fs->fs_size) {
+ warnx("bad block %lld, ino %d", (long long)bno, ip->i_number);
+ return;
+ }
+ error = bread(ip->i_fd, ip->i_fs, fsbtodb(fs, cgtod(fs, cg)),
+ (int)fs->fs_cgsize, &bp);
+ if (error) {
+ brelse(bp);
+ return;
+ }
+ cgp = (struct cg *)bp->b_data;
+ if (!cg_chkmagic_swap(cgp, needswap)) {
+ brelse(bp);
+ return;
+ }
+ cgbno = dtogd(fs, bno);
+ if (size == fs->fs_bsize) {
+ fragno = fragstoblks(fs, cgbno);
+ if (!ffs_isfreeblock(fs, cg_blksfree_swap(cgp, needswap), fragno)) {
+ errx(1, "blkfree: freeing free block %lld",
+ (long long)bno);
+ }
+ ffs_setblock(fs, cg_blksfree_swap(cgp, needswap), fragno);
+ ffs_clusteracct(fs, cgp, fragno, 1);
+ ufs_add32(cgp->cg_cs.cs_nbfree, 1, needswap);
+ fs->fs_cstotal.cs_nbfree++;
+ fs->fs_cs(fs, cg).cs_nbfree++;
+ } else {
+ bbase = cgbno - fragnum(fs, cgbno);
+ /*
+ * decrement the counts associated with the old frags
+ */
+ blk = blkmap(fs, cg_blksfree_swap(cgp, needswap), bbase);
+ ffs_fragacct_swap(fs, blk, cgp->cg_frsum, -1, needswap);
+ /*
+ * deallocate the fragment
+ */
+ frags = numfrags(fs, size);
+ for (i = 0; i < frags; i++) {
+ if (isset(cg_blksfree_swap(cgp, needswap), cgbno + i)) {
+ errx(1, "blkfree: freeing free frag: block %lld",
+ (long long)(cgbno + i));
+ }
+ setbit(cg_blksfree_swap(cgp, needswap), cgbno + i);
+ }
+ ufs_add32(cgp->cg_cs.cs_nffree, i, needswap);
+ fs->fs_cstotal.cs_nffree += i;
+ fs->fs_cs(fs, cg).cs_nffree += i;
+ /*
+ * add back in counts associated with the new frags
+ */
+ blk = blkmap(fs, cg_blksfree_swap(cgp, needswap), bbase);
+ ffs_fragacct_swap(fs, blk, cgp->cg_frsum, 1, needswap);
+ /*
+ * if a complete block has been reassembled, account for it
+ */
+ fragno = fragstoblks(fs, bbase);
+ if (ffs_isblock(fs, cg_blksfree_swap(cgp, needswap), fragno)) {
+ ufs_add32(cgp->cg_cs.cs_nffree, -fs->fs_frag, needswap);
+ fs->fs_cstotal.cs_nffree -= fs->fs_frag;
+ fs->fs_cs(fs, cg).cs_nffree -= fs->fs_frag;
+ ffs_clusteracct(fs, cgp, fragno, 1);
+ ufs_add32(cgp->cg_cs.cs_nbfree, 1, needswap);
+ fs->fs_cstotal.cs_nbfree++;
+ fs->fs_cs(fs, cg).cs_nbfree++;
+ }
+ }
+ fs->fs_fmod = 1;
+ bdwrite(bp);
+}
+
+
+static int
+scanc(u_int size, const u_char *cp, const u_char table[], int mask)
+{
+ const u_char *end = &cp[size];
+
+ while (cp < end && (table[*cp] & mask) == 0)
+ cp++;
+ return (end - cp);
+}
+
+/*
+ * Find a block of the specified size in the specified cylinder group.
+ *
+ * It is a panic if a request is made to find a block if none are
+ * available.
+ */
+static int32_t
+ffs_mapsearch(struct fs *fs, struct cg *cgp, daddr_t bpref, int allocsiz)
+{
+ int32_t bno;
+ int start, len, loc, i;
+ int blk, field, subfield, pos;
+ int ostart, olen;
+ const int needswap = UFS_FSNEEDSWAP(fs);
+
+ /*
+ * find the fragment by searching through the free block
+ * map for an appropriate bit pattern
+ */
+ if (bpref)
+ start = dtogd(fs, bpref) / NBBY;
+ else
+ start = ufs_rw32(cgp->cg_frotor, needswap) / NBBY;
+ len = howmany(fs->fs_fpg, NBBY) - start;
+ ostart = start;
+ olen = len;
+ loc = scanc((u_int)len,
+ (const u_char *)&cg_blksfree_swap(cgp, needswap)[start],
+ (const u_char *)fragtbl[fs->fs_frag],
+ (1 << (allocsiz - 1 + (fs->fs_frag % NBBY))));
+ if (loc == 0) {
+ len = start + 1;
+ start = 0;
+ loc = scanc((u_int)len,
+ (const u_char *)&cg_blksfree_swap(cgp, needswap)[0],
+ (const u_char *)fragtbl[fs->fs_frag],
+ (1 << (allocsiz - 1 + (fs->fs_frag % NBBY))));
+ if (loc == 0) {
+ errx(1,
+ "ffs_alloccg: map corrupted: start %d len %d offset %d %ld",
+ ostart, olen,
+ ufs_rw32(cgp->cg_freeoff, needswap),
+ (long)cg_blksfree_swap(cgp, needswap) - (long)cgp);
+ /* NOTREACHED */
+ }
+ }
+ bno = (start + len - loc) * NBBY;
+ cgp->cg_frotor = ufs_rw32(bno, needswap);
+ /*
+ * found the byte in the map
+ * sift through the bits to find the selected frag
+ */
+ for (i = bno + NBBY; bno < i; bno += fs->fs_frag) {
+ blk = blkmap(fs, cg_blksfree_swap(cgp, needswap), bno);
+ blk <<= 1;
+ field = around[allocsiz];
+ subfield = inside[allocsiz];
+ for (pos = 0; pos <= fs->fs_frag - allocsiz; pos++) {
+ if ((blk & field) == subfield)
+ return (bno + pos);
+ field <<= 1;
+ subfield <<= 1;
+ }
+ }
+ errx(1, "ffs_alloccg: block not in map: bno %lld", (long long)bno);
+ return (-1);
+}
+
+/*
+ * Update the cluster map because of an allocation or free.
+ *
+ * Cnt == 1 means free; cnt == -1 means allocating.
+ */
+void
+ffs_clusteracct(struct fs *fs, struct cg *cgp, int32_t blkno, int cnt)
+{
+ int32_t *sump;
+ int32_t *lp;
+ u_char *freemapp, *mapp;
+ int i, start, end, forw, back, map, bit;
+ const int needswap = UFS_FSNEEDSWAP(fs);
+
+ if (fs->fs_contigsumsize <= 0)
+ return;
+ freemapp = cg_clustersfree_swap(cgp, needswap);
+ sump = cg_clustersum_swap(cgp, needswap);
+ /*
+ * Allocate or clear the actual block.
+ */
+ if (cnt > 0)
+ setbit(freemapp, blkno);
+ else
+ clrbit(freemapp, blkno);
+ /*
+ * Find the size of the cluster going forward.
+ */
+ start = blkno + 1;
+ end = start + fs->fs_contigsumsize;
+ if (end >= ufs_rw32(cgp->cg_nclusterblks, needswap))
+ end = ufs_rw32(cgp->cg_nclusterblks, needswap);
+ mapp = &freemapp[start / NBBY];
+ map = *mapp++;
+ bit = 1 << (start % NBBY);
+ for (i = start; i < end; i++) {
+ if ((map & bit) == 0)
+ break;
+ if ((i & (NBBY - 1)) != (NBBY - 1)) {
+ bit <<= 1;
+ } else {
+ map = *mapp++;
+ bit = 1;
+ }
+ }
+ forw = i - start;
+ /*
+ * Find the size of the cluster going backward.
+ */
+ start = blkno - 1;
+ end = start - fs->fs_contigsumsize;
+ if (end < 0)
+ end = -1;
+ mapp = &freemapp[start / NBBY];
+ map = *mapp--;
+ bit = 1 << (start % NBBY);
+ for (i = start; i > end; i--) {
+ if ((map & bit) == 0)
+ break;
+ if ((i & (NBBY - 1)) != 0) {
+ bit >>= 1;
+ } else {
+ map = *mapp--;
+ bit = 1 << (NBBY - 1);
+ }
+ }
+ back = start - i;
+ /*
+ * Account for old cluster and the possibly new forward and
+ * back clusters.
+ */
+ i = back + forw + 1;
+ if (i > fs->fs_contigsumsize)
+ i = fs->fs_contigsumsize;
+ ufs_add32(sump[i], cnt, needswap);
+ if (back > 0)
+ ufs_add32(sump[back], -cnt, needswap);
+ if (forw > 0)
+ ufs_add32(sump[forw], -cnt, needswap);
+
+ /*
+ * Update cluster summary information.
+ */
+ lp = &sump[fs->fs_contigsumsize];
+ for (i = fs->fs_contigsumsize; i > 0; i--)
+ if (ufs_rw32(*lp--, needswap) > 0)
+ break;
+ fs->fs_maxcluster[ufs_rw32(cgp->cg_cgx, needswap)] = i;
+}
diff --git a/usr.sbin/makefs/ffs/ffs_balloc.c b/usr.sbin/makefs/ffs/ffs_balloc.c
new file mode 100644
index 0000000..0a048ad
--- /dev/null
+++ b/usr.sbin/makefs/ffs/ffs_balloc.c
@@ -0,0 +1,578 @@
+/* $NetBSD: ffs_balloc.c,v 1.13 2004/06/20 22:20:18 jmc Exp $ */
+/* From NetBSD: ffs_balloc.c,v 1.25 2001/08/08 08:36:36 lukem Exp */
+
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ffs_balloc.c 8.8 (Berkeley) 6/16/95
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "makefs.h"
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include "ffs/ufs_bswap.h"
+#include "ffs/buf.h"
+#include "ffs/ufs_inode.h"
+#include "ffs/ffs_extern.h"
+
+static int ffs_balloc_ufs1(struct inode *, off_t, int, struct buf **);
+static int ffs_balloc_ufs2(struct inode *, off_t, int, struct buf **);
+
+/*
+ * Balloc defines the structure of file system storage
+ * by allocating the physical blocks on a device given
+ * the inode and the logical block number in a file.
+ *
+ * Assume: flags == B_SYNC | B_CLRBUF
+ */
+
+int
+ffs_balloc(struct inode *ip, off_t offset, int bufsize, struct buf **bpp)
+{
+ if (ip->i_fs->fs_magic == FS_UFS2_MAGIC)
+ return ffs_balloc_ufs2(ip, offset, bufsize, bpp);
+ else
+ return ffs_balloc_ufs1(ip, offset, bufsize, bpp);
+}
+
+static int
+ffs_balloc_ufs1(struct inode *ip, off_t offset, int bufsize, struct buf **bpp)
+{
+ daddr_t lbn, lastlbn;
+ int size;
+ int32_t nb;
+ struct buf *bp, *nbp;
+ struct fs *fs = ip->i_fs;
+ struct indir indirs[NIADDR + 2];
+ daddr_t newb, pref;
+ int32_t *bap;
+ int osize, nsize, num, i, error;
+ int32_t *allocblk, allociblk[NIADDR + 1];
+ int32_t *allocib;
+ const int needswap = UFS_FSNEEDSWAP(fs);
+
+ lbn = lblkno(fs, offset);
+ size = blkoff(fs, offset) + bufsize;
+ if (bpp != NULL) {
+ *bpp = NULL;
+ }
+
+ assert(size <= fs->fs_bsize);
+ if (lbn < 0)
+ return (EFBIG);
+
+ /*
+ * If the next write will extend the file into a new block,
+ * and the file is currently composed of a fragment
+ * this fragment has to be extended to be a full block.
+ */
+
+ lastlbn = lblkno(fs, ip->i_ffs1_size);
+ if (lastlbn < NDADDR && lastlbn < lbn) {
+ nb = lastlbn;
+ osize = blksize(fs, ip, nb);
+ if (osize < fs->fs_bsize && osize > 0) {
+ warnx("need to ffs_realloccg; not supported!");
+ abort();
+ }
+ }
+
+ /*
+ * The first NDADDR blocks are direct blocks
+ */
+
+ if (lbn < NDADDR) {
+ nb = ufs_rw32(ip->i_ffs1_db[lbn], needswap);
+ if (nb != 0 && ip->i_ffs1_size >= lblktosize(fs, lbn + 1)) {
+
+ /*
+ * The block is an already-allocated direct block
+ * and the file already extends past this block,
+ * thus this must be a whole block.
+ * Just read the block (if requested).
+ */
+
+ if (bpp != NULL) {
+ error = bread(ip->i_fd, ip->i_fs, lbn,
+ fs->fs_bsize, bpp);
+ if (error) {
+ brelse(*bpp);
+ return (error);
+ }
+ }
+ return (0);
+ }
+ if (nb != 0) {
+
+ /*
+ * Consider need to reallocate a fragment.
+ */
+
+ osize = fragroundup(fs, blkoff(fs, ip->i_ffs1_size));
+ nsize = fragroundup(fs, size);
+ if (nsize <= osize) {
+
+ /*
+ * The existing block is already
+ * at least as big as we want.
+ * Just read the block (if requested).
+ */
+
+ if (bpp != NULL) {
+ error = bread(ip->i_fd, ip->i_fs, lbn,
+ osize, bpp);
+ if (error) {
+ brelse(*bpp);
+ return (error);
+ }
+ }
+ return 0;
+ } else {
+ warnx("need to ffs_realloccg; not supported!");
+ abort();
+ }
+ } else {
+
+ /*
+ * the block was not previously allocated,
+ * allocate a new block or fragment.
+ */
+
+ if (ip->i_ffs1_size < lblktosize(fs, lbn + 1))
+ nsize = fragroundup(fs, size);
+ else
+ nsize = fs->fs_bsize;
+ error = ffs_alloc(ip, lbn,
+ ffs_blkpref_ufs1(ip, lbn, (int)lbn,
+ &ip->i_ffs1_db[0]),
+ nsize, &newb);
+ if (error)
+ return (error);
+ if (bpp != NULL) {
+ bp = getblk(ip->i_fd, ip->i_fs, lbn, nsize);
+ bp->b_blkno = fsbtodb(fs, newb);
+ clrbuf(bp);
+ *bpp = bp;
+ }
+ }
+ ip->i_ffs1_db[lbn] = ufs_rw32((int32_t)newb, needswap);
+ return (0);
+ }
+
+ /*
+ * Determine the number of levels of indirection.
+ */
+
+ pref = 0;
+ if ((error = ufs_getlbns(ip, lbn, indirs, &num)) != 0)
+ return (error);
+
+ if (num < 1) {
+ warnx("ffs_balloc: ufs_getlbns returned indirect block");
+ abort();
+ }
+
+ /*
+ * Fetch the first indirect block allocating if necessary.
+ */
+
+ --num;
+ nb = ufs_rw32(ip->i_ffs1_ib[indirs[0].in_off], needswap);
+ allocib = NULL;
+ allocblk = allociblk;
+ if (nb == 0) {
+ pref = ffs_blkpref_ufs1(ip, lbn, 0, (int32_t *)0);
+ error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb);
+ if (error)
+ return error;
+ nb = newb;
+ *allocblk++ = nb;
+ bp = getblk(ip->i_fd, ip->i_fs, indirs[1].in_lbn, fs->fs_bsize);
+ bp->b_blkno = fsbtodb(fs, nb);
+ clrbuf(bp);
+ /*
+ * Write synchronously so that indirect blocks
+ * never point at garbage.
+ */
+ if ((error = bwrite(bp)) != 0)
+ return error;
+ allocib = &ip->i_ffs1_ib[indirs[0].in_off];
+ *allocib = ufs_rw32((int32_t)nb, needswap);
+ }
+
+ /*
+ * Fetch through the indirect blocks, allocating as necessary.
+ */
+
+ for (i = 1;;) {
+ error = bread(ip->i_fd, ip->i_fs, indirs[i].in_lbn,
+ fs->fs_bsize, &bp);
+ if (error) {
+ brelse(bp);
+ return error;
+ }
+ bap = (int32_t *)bp->b_data;
+ nb = ufs_rw32(bap[indirs[i].in_off], needswap);
+ if (i == num)
+ break;
+ i++;
+ if (nb != 0) {
+ brelse(bp);
+ continue;
+ }
+ if (pref == 0)
+ pref = ffs_blkpref_ufs1(ip, lbn, 0, (int32_t *)0);
+ error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb);
+ if (error) {
+ brelse(bp);
+ return error;
+ }
+ nb = newb;
+ *allocblk++ = nb;
+ nbp = getblk(ip->i_fd, ip->i_fs, indirs[i].in_lbn,
+ fs->fs_bsize);
+ nbp->b_blkno = fsbtodb(fs, nb);
+ clrbuf(nbp);
+ /*
+ * Write synchronously so that indirect blocks
+ * never point at garbage.
+ */
+
+ if ((error = bwrite(nbp)) != 0) {
+ brelse(bp);
+ return error;
+ }
+ bap[indirs[i - 1].in_off] = ufs_rw32(nb, needswap);
+
+ bwrite(bp);
+ }
+
+ /*
+ * Get the data block, allocating if necessary.
+ */
+
+ if (nb == 0) {
+ pref = ffs_blkpref_ufs1(ip, lbn, indirs[num].in_off, &bap[0]);
+ error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb);
+ if (error) {
+ brelse(bp);
+ return error;
+ }
+ nb = newb;
+ *allocblk++ = nb;
+ if (bpp != NULL) {
+ nbp = getblk(ip->i_fd, ip->i_fs, lbn, fs->fs_bsize);
+ nbp->b_blkno = fsbtodb(fs, nb);
+ clrbuf(nbp);
+ *bpp = nbp;
+ }
+ bap[indirs[num].in_off] = ufs_rw32(nb, needswap);
+
+ /*
+ * If required, write synchronously, otherwise use
+ * delayed write.
+ */
+ bwrite(bp);
+ return (0);
+ }
+ brelse(bp);
+ if (bpp != NULL) {
+ error = bread(ip->i_fd, ip->i_fs, lbn, (int)fs->fs_bsize, &nbp);
+ if (error) {
+ brelse(nbp);
+ return error;
+ }
+ *bpp = nbp;
+ }
+ return (0);
+}
+
+static int
+ffs_balloc_ufs2(struct inode *ip, off_t offset, int bufsize, struct buf **bpp)
+{
+ daddr_t lbn, lastlbn;
+ int size;
+ struct buf *bp, *nbp;
+ struct fs *fs = ip->i_fs;
+ struct indir indirs[NIADDR + 2];
+ daddr_t newb, pref, nb;
+ int64_t *bap;
+ int osize, nsize, num, i, error;
+ int64_t *allocblk, allociblk[NIADDR + 1];
+ int64_t *allocib;
+ const int needswap = UFS_FSNEEDSWAP(fs);
+
+ lbn = lblkno(fs, offset);
+ size = blkoff(fs, offset) + bufsize;
+ if (bpp != NULL) {
+ *bpp = NULL;
+ }
+
+ assert(size <= fs->fs_bsize);
+ if (lbn < 0)
+ return (EFBIG);
+
+ /*
+ * If the next write will extend the file into a new block,
+ * and the file is currently composed of a fragment
+ * this fragment has to be extended to be a full block.
+ */
+
+ lastlbn = lblkno(fs, ip->i_ffs2_size);
+ if (lastlbn < NDADDR && lastlbn < lbn) {
+ nb = lastlbn;
+ osize = blksize(fs, ip, nb);
+ if (osize < fs->fs_bsize && osize > 0) {
+ warnx("need to ffs_realloccg; not supported!");
+ abort();
+ }
+ }
+
+ /*
+ * The first NDADDR blocks are direct blocks
+ */
+
+ if (lbn < NDADDR) {
+ nb = ufs_rw64(ip->i_ffs2_db[lbn], needswap);
+ if (nb != 0 && ip->i_ffs2_size >= lblktosize(fs, lbn + 1)) {
+
+ /*
+ * The block is an already-allocated direct block
+ * and the file already extends past this block,
+ * thus this must be a whole block.
+ * Just read the block (if requested).
+ */
+
+ if (bpp != NULL) {
+ error = bread(ip->i_fd, ip->i_fs, lbn,
+ fs->fs_bsize, bpp);
+ if (error) {
+ brelse(*bpp);
+ return (error);
+ }
+ }
+ return (0);
+ }
+ if (nb != 0) {
+
+ /*
+ * Consider need to reallocate a fragment.
+ */
+
+ osize = fragroundup(fs, blkoff(fs, ip->i_ffs2_size));
+ nsize = fragroundup(fs, size);
+ if (nsize <= osize) {
+
+ /*
+ * The existing block is already
+ * at least as big as we want.
+ * Just read the block (if requested).
+ */
+
+ if (bpp != NULL) {
+ error = bread(ip->i_fd, ip->i_fs, lbn,
+ osize, bpp);
+ if (error) {
+ brelse(*bpp);
+ return (error);
+ }
+ }
+ return 0;
+ } else {
+ warnx("need to ffs_realloccg; not supported!");
+ abort();
+ }
+ } else {
+
+ /*
+ * the block was not previously allocated,
+ * allocate a new block or fragment.
+ */
+
+ if (ip->i_ffs2_size < lblktosize(fs, lbn + 1))
+ nsize = fragroundup(fs, size);
+ else
+ nsize = fs->fs_bsize;
+ error = ffs_alloc(ip, lbn,
+ ffs_blkpref_ufs2(ip, lbn, (int)lbn,
+ &ip->i_ffs2_db[0]),
+ nsize, &newb);
+ if (error)
+ return (error);
+ if (bpp != NULL) {
+ bp = getblk(ip->i_fd, ip->i_fs, lbn, nsize);
+ bp->b_blkno = fsbtodb(fs, newb);
+ clrbuf(bp);
+ *bpp = bp;
+ }
+ }
+ ip->i_ffs2_db[lbn] = ufs_rw64(newb, needswap);
+ return (0);
+ }
+
+ /*
+ * Determine the number of levels of indirection.
+ */
+
+ pref = 0;
+ if ((error = ufs_getlbns(ip, lbn, indirs, &num)) != 0)
+ return (error);
+
+ if (num < 1) {
+ warnx("ffs_balloc: ufs_getlbns returned indirect block");
+ abort();
+ }
+
+ /*
+ * Fetch the first indirect block allocating if necessary.
+ */
+
+ --num;
+ nb = ufs_rw64(ip->i_ffs2_ib[indirs[0].in_off], needswap);
+ allocib = NULL;
+ allocblk = allociblk;
+ if (nb == 0) {
+ pref = ffs_blkpref_ufs2(ip, lbn, 0, (int64_t *)0);
+ error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb);
+ if (error)
+ return error;
+ nb = newb;
+ *allocblk++ = nb;
+ bp = getblk(ip->i_fd, ip->i_fs, indirs[1].in_lbn, fs->fs_bsize);
+ bp->b_blkno = fsbtodb(fs, nb);
+ clrbuf(bp);
+ /*
+ * Write synchronously so that indirect blocks
+ * never point at garbage.
+ */
+ if ((error = bwrite(bp)) != 0)
+ return error;
+ allocib = &ip->i_ffs2_ib[indirs[0].in_off];
+ *allocib = ufs_rw64(nb, needswap);
+ }
+
+ /*
+ * Fetch through the indirect blocks, allocating as necessary.
+ */
+
+ for (i = 1;;) {
+ error = bread(ip->i_fd, ip->i_fs, indirs[i].in_lbn,
+ fs->fs_bsize, &bp);
+ if (error) {
+ brelse(bp);
+ return error;
+ }
+ bap = (int64_t *)bp->b_data;
+ nb = ufs_rw64(bap[indirs[i].in_off], needswap);
+ if (i == num)
+ break;
+ i++;
+ if (nb != 0) {
+ brelse(bp);
+ continue;
+ }
+ if (pref == 0)
+ pref = ffs_blkpref_ufs2(ip, lbn, 0, (int64_t *)0);
+ error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb);
+ if (error) {
+ brelse(bp);
+ return error;
+ }
+ nb = newb;
+ *allocblk++ = nb;
+ nbp = getblk(ip->i_fd, ip->i_fs, indirs[i].in_lbn,
+ fs->fs_bsize);
+ nbp->b_blkno = fsbtodb(fs, nb);
+ clrbuf(nbp);
+ /*
+ * Write synchronously so that indirect blocks
+ * never point at garbage.
+ */
+
+ if ((error = bwrite(nbp)) != 0) {
+ brelse(bp);
+ return error;
+ }
+ bap[indirs[i - 1].in_off] = ufs_rw64(nb, needswap);
+
+ bwrite(bp);
+ }
+
+ /*
+ * Get the data block, allocating if necessary.
+ */
+
+ if (nb == 0) {
+ pref = ffs_blkpref_ufs2(ip, lbn, indirs[num].in_off, &bap[0]);
+ error = ffs_alloc(ip, lbn, pref, (int)fs->fs_bsize, &newb);
+ if (error) {
+ brelse(bp);
+ return error;
+ }
+ nb = newb;
+ *allocblk++ = nb;
+ if (bpp != NULL) {
+ nbp = getblk(ip->i_fd, ip->i_fs, lbn, fs->fs_bsize);
+ nbp->b_blkno = fsbtodb(fs, nb);
+ clrbuf(nbp);
+ *bpp = nbp;
+ }
+ bap[indirs[num].in_off] = ufs_rw64(nb, needswap);
+
+ /*
+ * If required, write synchronously, otherwise use
+ * delayed write.
+ */
+ bwrite(bp);
+ return (0);
+ }
+ brelse(bp);
+ if (bpp != NULL) {
+ error = bread(ip->i_fd, ip->i_fs, lbn, (int)fs->fs_bsize, &nbp);
+ if (error) {
+ brelse(nbp);
+ return error;
+ }
+ *bpp = nbp;
+ }
+ return (0);
+}
diff --git a/usr.sbin/makefs/ffs/ffs_bswap.c b/usr.sbin/makefs/ffs/ffs_bswap.c
new file mode 100644
index 0000000..d560884
--- /dev/null
+++ b/usr.sbin/makefs/ffs/ffs_bswap.c
@@ -0,0 +1,270 @@
+/* $NetBSD: ffs_bswap.c,v 1.28 2004/05/25 14:54:59 hannken Exp $ */
+
+/*
+ * Copyright (c) 1998 Manuel Bouyer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Manuel Bouyer.
+ * 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>
+#if defined(_KERNEL)
+#include <sys/systm.h>
+#endif
+
+#include <ufs/ufs/dinode.h>
+#include "ffs/ufs_bswap.h"
+#include <ufs/ffs/fs.h>
+/* XXX temporary */
+struct ufsmount;
+struct bufobj;
+struct mount;
+struct vnode;
+typedef int vfs_vget_t(struct mount *mp, ino_t ino, int flags,
+ struct vnode **vpp);
+#include <ufs/ffs/ffs_extern.h>
+
+#if !defined(_KERNEL)
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#define panic(x) printf("%s\n", (x)), abort()
+#endif
+
+#define fs_old_postbloff fs_spare5[0]
+#define fs_old_rotbloff fs_spare5[1]
+#define fs_old_postbl_start fs_maxbsize
+#define fs_old_headswitch fs_id[0]
+#define fs_old_trkseek fs_id[1]
+#define fs_old_csmask fs_spare1[0]
+#define fs_old_csshift fs_spare1[1]
+
+#define FS_42POSTBLFMT -1 /* 4.2BSD rotational table format */
+#define FS_DYNAMICPOSTBLFMT 1 /* dynamic rotational table format */
+
+void ffs_csum_swap(struct csum *o, struct csum *n, int size);
+void ffs_csumtotal_swap(struct csum_total *o, struct csum_total *n);
+
+void
+ffs_sb_swap(struct fs *o, struct fs *n)
+{
+ int i;
+ u_int32_t *o32, *n32;
+
+ /*
+ * In order to avoid a lot of lines, as the first N fields (52)
+ * of the superblock up to fs_fmod are u_int32_t, we just loop
+ * here to convert them.
+ */
+ o32 = (u_int32_t *)o;
+ n32 = (u_int32_t *)n;
+ for (i = 0; i < offsetof(struct fs, fs_fmod) / sizeof(u_int32_t); i++)
+ n32[i] = bswap32(o32[i]);
+
+ n->fs_swuid = bswap64(o->fs_swuid);
+ n->fs_cgrotor = bswap32(o->fs_cgrotor); /* Unused */
+ n->fs_old_cpc = bswap32(o->fs_old_cpc);
+
+ /* These fields overlap with a possible location for the
+ * historic FS_DYNAMICPOSTBLFMT postbl table, and with the
+ * first half of the historic FS_42POSTBLFMT postbl table.
+ */
+ n->fs_maxbsize = bswap32(o->fs_maxbsize);
+ n->fs_sblockloc = bswap64(o->fs_sblockloc);
+ ffs_csumtotal_swap(&o->fs_cstotal, &n->fs_cstotal);
+ n->fs_time = bswap64(o->fs_time);
+ n->fs_size = bswap64(o->fs_size);
+ n->fs_dsize = bswap64(o->fs_dsize);
+ n->fs_csaddr = bswap64(o->fs_csaddr);
+ n->fs_pendingblocks = bswap64(o->fs_pendingblocks);
+ n->fs_pendinginodes = bswap32(o->fs_pendinginodes);
+
+ /* These fields overlap with the second half of the
+ * historic FS_42POSTBLFMT postbl table
+ */
+ for (i = 0; i < FSMAXSNAP; i++)
+ n->fs_snapinum[i] = bswap32(o->fs_snapinum[i]);
+ n->fs_avgfilesize = bswap32(o->fs_avgfilesize);
+ n->fs_avgfpdir = bswap32(o->fs_avgfpdir);
+ /* fs_sparecon[28] - ignore for now */
+ n->fs_flags = bswap32(o->fs_flags);
+ n->fs_contigsumsize = bswap32(o->fs_contigsumsize);
+ n->fs_maxsymlinklen = bswap32(o->fs_maxsymlinklen);
+ n->fs_old_inodefmt = bswap32(o->fs_old_inodefmt);
+ n->fs_maxfilesize = bswap64(o->fs_maxfilesize);
+ n->fs_qbmask = bswap64(o->fs_qbmask);
+ n->fs_qfmask = bswap64(o->fs_qfmask);
+ n->fs_state = bswap32(o->fs_state);
+ n->fs_old_postblformat = bswap32(o->fs_old_postblformat);
+ n->fs_old_nrpos = bswap32(o->fs_old_nrpos);
+ n->fs_old_postbloff = bswap32(o->fs_old_postbloff);
+ n->fs_old_rotbloff = bswap32(o->fs_old_rotbloff);
+
+ n->fs_magic = bswap32(o->fs_magic);
+}
+
+void
+ffs_dinode1_swap(struct ufs1_dinode *o, struct ufs1_dinode *n)
+{
+
+ n->di_mode = bswap16(o->di_mode);
+ n->di_nlink = bswap16(o->di_nlink);
+ n->di_u.oldids[0] = bswap16(o->di_u.oldids[0]);
+ n->di_u.oldids[1] = bswap16(o->di_u.oldids[1]);
+ n->di_size = bswap64(o->di_size);
+ n->di_atime = bswap32(o->di_atime);
+ n->di_atimensec = bswap32(o->di_atimensec);
+ n->di_mtime = bswap32(o->di_mtime);
+ n->di_mtimensec = bswap32(o->di_mtimensec);
+ n->di_ctime = bswap32(o->di_ctime);
+ n->di_ctimensec = bswap32(o->di_ctimensec);
+ memcpy(n->di_db, o->di_db, (NDADDR + NIADDR) * sizeof(u_int32_t));
+ n->di_flags = bswap32(o->di_flags);
+ n->di_blocks = bswap32(o->di_blocks);
+ n->di_gen = bswap32(o->di_gen);
+ n->di_uid = bswap32(o->di_uid);
+ n->di_gid = bswap32(o->di_gid);
+}
+
+void
+ffs_dinode2_swap(struct ufs2_dinode *o, struct ufs2_dinode *n)
+{
+ n->di_mode = bswap16(o->di_mode);
+ n->di_nlink = bswap16(o->di_nlink);
+ n->di_uid = bswap32(o->di_uid);
+ n->di_gid = bswap32(o->di_gid);
+ n->di_blksize = bswap32(o->di_blksize);
+ n->di_size = bswap64(o->di_size);
+ n->di_blocks = bswap64(o->di_blocks);
+ n->di_atime = bswap64(o->di_atime);
+ n->di_atimensec = bswap32(o->di_atimensec);
+ n->di_mtime = bswap64(o->di_mtime);
+ n->di_mtimensec = bswap32(o->di_mtimensec);
+ n->di_ctime = bswap64(o->di_ctime);
+ n->di_ctimensec = bswap32(o->di_ctimensec);
+ n->di_birthtime = bswap64(o->di_ctime);
+ n->di_birthnsec = bswap32(o->di_ctimensec);
+ n->di_gen = bswap32(o->di_gen);
+ n->di_kernflags = bswap32(o->di_kernflags);
+ n->di_flags = bswap32(o->di_flags);
+ n->di_extsize = bswap32(o->di_extsize);
+ memcpy(n->di_extb, o->di_extb, (NXADDR + NDADDR + NIADDR) * 8);
+}
+
+void
+ffs_csum_swap(struct csum *o, struct csum *n, int size)
+{
+ int i;
+ u_int32_t *oint, *nint;
+
+ oint = (u_int32_t*)o;
+ nint = (u_int32_t*)n;
+
+ for (i = 0; i < size / sizeof(u_int32_t); i++)
+ nint[i] = bswap32(oint[i]);
+}
+
+void
+ffs_csumtotal_swap(struct csum_total *o, struct csum_total *n)
+{
+ n->cs_ndir = bswap64(o->cs_ndir);
+ n->cs_nbfree = bswap64(o->cs_nbfree);
+ n->cs_nifree = bswap64(o->cs_nifree);
+ n->cs_nffree = bswap64(o->cs_nffree);
+}
+
+/*
+ * Note that ffs_cg_swap may be called with o == n.
+ */
+void
+ffs_cg_swap(struct cg *o, struct cg *n, struct fs *fs)
+{
+ int i;
+ u_int32_t *n32, *o32;
+ u_int16_t *n16, *o16;
+ int32_t btotoff, boff, clustersumoff;
+
+ n->cg_firstfield = bswap32(o->cg_firstfield);
+ n->cg_magic = bswap32(o->cg_magic);
+ n->cg_old_time = bswap32(o->cg_old_time);
+ n->cg_cgx = bswap32(o->cg_cgx);
+ n->cg_old_ncyl = bswap16(o->cg_old_ncyl);
+ n->cg_old_niblk = bswap16(o->cg_old_niblk);
+ n->cg_ndblk = bswap32(o->cg_ndblk);
+ n->cg_cs.cs_ndir = bswap32(o->cg_cs.cs_ndir);
+ n->cg_cs.cs_nbfree = bswap32(o->cg_cs.cs_nbfree);
+ n->cg_cs.cs_nifree = bswap32(o->cg_cs.cs_nifree);
+ n->cg_cs.cs_nffree = bswap32(o->cg_cs.cs_nffree);
+ n->cg_rotor = bswap32(o->cg_rotor);
+ n->cg_frotor = bswap32(o->cg_frotor);
+ n->cg_irotor = bswap32(o->cg_irotor);
+ for (i = 0; i < MAXFRAG; i++)
+ n->cg_frsum[i] = bswap32(o->cg_frsum[i]);
+
+ n->cg_old_btotoff = bswap32(o->cg_old_btotoff);
+ n->cg_old_boff = bswap32(o->cg_old_boff);
+ n->cg_iusedoff = bswap32(o->cg_iusedoff);
+ n->cg_freeoff = bswap32(o->cg_freeoff);
+ n->cg_nextfreeoff = bswap32(o->cg_nextfreeoff);
+ n->cg_clustersumoff = bswap32(o->cg_clustersumoff);
+ n->cg_clusteroff = bswap32(o->cg_clusteroff);
+ n->cg_nclusterblks = bswap32(o->cg_nclusterblks);
+ n->cg_niblk = bswap32(o->cg_niblk);
+ n->cg_initediblk = bswap32(o->cg_initediblk);
+ n->cg_time = bswap64(o->cg_time);
+
+ if (fs->fs_magic == FS_UFS2_MAGIC)
+ return;
+
+ if (n->cg_magic == CG_MAGIC) {
+ btotoff = n->cg_old_btotoff;
+ boff = n->cg_old_boff;
+ clustersumoff = n->cg_clustersumoff;
+ } else {
+ btotoff = bswap32(n->cg_old_btotoff);
+ boff = bswap32(n->cg_old_boff);
+ clustersumoff = bswap32(n->cg_clustersumoff);
+ }
+ n32 = (u_int32_t *)((u_int8_t *)n + btotoff);
+ o32 = (u_int32_t *)((u_int8_t *)o + btotoff);
+ n16 = (u_int16_t *)((u_int8_t *)n + boff);
+ o16 = (u_int16_t *)((u_int8_t *)o + boff);
+
+ for (i = 0; i < fs->fs_old_cpg; i++)
+ n32[i] = bswap32(o32[i]);
+
+ for (i = 0; i < fs->fs_old_cpg * fs->fs_old_nrpos; i++)
+ n16[i] = bswap16(o16[i]);
+
+ n32 = (u_int32_t *)((u_int8_t *)n + clustersumoff);
+ o32 = (u_int32_t *)((u_int8_t *)o + clustersumoff);
+ for (i = 1; i < fs->fs_contigsumsize + 1; i++)
+ n32[i] = bswap32(o32[i]);
+}
diff --git a/usr.sbin/makefs/ffs/ffs_extern.h b/usr.sbin/makefs/ffs/ffs_extern.h
new file mode 100644
index 0000000..d95e69b
--- /dev/null
+++ b/usr.sbin/makefs/ffs/ffs_extern.h
@@ -0,0 +1,77 @@
+/* $NetBSD: ffs_extern.h,v 1.6 2003/08/07 11:25:33 agc Exp $ */
+/* From: NetBSD: ffs_extern.h,v 1.19 2001/08/17 02:18:48 lukem Exp */
+
+/*-
+ * Copyright (c) 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ffs_extern.h 8.6 (Berkeley) 3/30/95
+ * $FreeBSD$
+ */
+
+#include "ffs/buf.h"
+
+/*
+ * Structure used to pass around logical block paths generated by
+ * ufs_getlbns and used by truncate and bmap code.
+ */
+struct indir {
+ daddr_t in_lbn; /* Logical block number. */
+ int in_off; /* Offset in buffer. */
+ int in_exists; /* Flag if the block exists. */
+};
+
+ /* ffs.c */
+void panic(const char *, ...)
+ __attribute__((__noreturn__,__format__(__printf__,1,2)));
+
+ /* ffs_alloc.c */
+int ffs_alloc(struct inode *, daddr_t, daddr_t, int, daddr_t *);
+daddr_t ffs_blkpref_ufs1(struct inode *, daddr_t, int, int32_t *);
+daddr_t ffs_blkpref_ufs2(struct inode *, daddr_t, int, int64_t *);
+void ffs_blkfree(struct inode *, daddr_t, long);
+void ffs_clusteracct(struct fs *, struct cg *, int32_t, int);
+
+ /* ffs_balloc.c */
+int ffs_balloc(struct inode *, off_t, int, struct buf **);
+
+ /* ffs_bswap.c */
+void ffs_sb_swap(struct fs*, struct fs *);
+void ffs_dinode1_swap(struct ufs1_dinode *, struct ufs1_dinode *);
+void ffs_dinode2_swap(struct ufs2_dinode *, struct ufs2_dinode *);
+void ffs_csum_swap(struct csum *, struct csum *, int);
+void ffs_cg_swap(struct cg *, struct cg *, struct fs *);
+
+ /* ffs_subr.c */
+void ffs_fragacct(struct fs *, int, int32_t[], int, int);
+int ffs_isblock(struct fs *, u_char *, int32_t);
+int ffs_isfreeblock(struct fs *, u_char *, int32_t);
+void ffs_clrblock(struct fs *, u_char *, int32_t);
+void ffs_setblock(struct fs *, u_char *, int32_t);
+
+ /* ufs_bmap.c */
+int ufs_getlbns(struct inode *, daddr_t, struct indir *, int *);
diff --git a/usr.sbin/makefs/ffs/ffs_subr.c b/usr.sbin/makefs/ffs/ffs_subr.c
new file mode 100644
index 0000000..5f9b6f2
--- /dev/null
+++ b/usr.sbin/makefs/ffs/ffs_subr.c
@@ -0,0 +1,202 @@
+/* $NetBSD: ffs_subr.c,v 1.32 2003/12/30 12:33:24 pk Exp $ */
+
+/*
+ * Copyright (c) 1982, 1986, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ffs_subr.c 8.5 (Berkeley) 3/21/95
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+/* XXX temporary */
+struct ufsmount;
+struct bufobj;
+struct mount;
+struct vnode;
+typedef int vfs_vget_t(struct mount *mp, ino_t ino, int flags,
+ struct vnode **vpp);
+#include <ufs/ffs/ffs_extern.h>
+#include "ffs/ufs_bswap.h"
+void panic __P((const char *, ...))
+ __attribute__((__noreturn__,__format__(__printf__,1,2)));
+
+/*
+ * Update the frsum fields to reflect addition or deletion
+ * of some frags.
+ */
+void
+ffs_fragacct_swap(struct fs *fs, int fragmap, int32_t fraglist[], int cnt, int needswap)
+{
+ int inblk;
+ int field, subfield;
+ int siz, pos;
+
+ inblk = (int)(fragtbl[fs->fs_frag][fragmap]) << 1;
+ fragmap <<= 1;
+ for (siz = 1; siz < fs->fs_frag; siz++) {
+ if ((inblk & (1 << (siz + (fs->fs_frag & (NBBY - 1))))) == 0)
+ continue;
+ field = around[siz];
+ subfield = inside[siz];
+ for (pos = siz; pos <= fs->fs_frag; pos++) {
+ if ((fragmap & field) == subfield) {
+ fraglist[siz] = ufs_rw32(
+ ufs_rw32(fraglist[siz], needswap) + cnt,
+ needswap);
+ pos += siz;
+ field <<= siz;
+ subfield <<= siz;
+ }
+ field <<= 1;
+ subfield <<= 1;
+ }
+ }
+}
+
+/*
+ * block operations
+ *
+ * check if a block is available
+ * returns true if all the correponding bits in the free map are 1
+ * returns false if any corresponding bit in the free map is 0
+ */
+int
+ffs_isblock(fs, cp, h)
+ struct fs *fs;
+ u_char *cp;
+ int32_t h;
+{
+ u_char mask;
+
+ switch ((int)fs->fs_fragshift) {
+ case 3:
+ return (cp[h] == 0xff);
+ case 2:
+ mask = 0x0f << ((h & 0x1) << 2);
+ return ((cp[h >> 1] & mask) == mask);
+ case 1:
+ mask = 0x03 << ((h & 0x3) << 1);
+ return ((cp[h >> 2] & mask) == mask);
+ case 0:
+ mask = 0x01 << (h & 0x7);
+ return ((cp[h >> 3] & mask) == mask);
+ default:
+ panic("ffs_isblock: unknown fs_fragshift %d",
+ (int)fs->fs_fragshift);
+ }
+}
+
+/*
+ * check if a block is completely allocated
+ * returns true if all the corresponding bits in the free map are 0
+ * returns false if any corresponding bit in the free map is 1
+ */
+int
+ffs_isfreeblock(fs, cp, h)
+ struct fs *fs;
+ u_char *cp;
+ int32_t h;
+{
+
+ switch ((int)fs->fs_fragshift) {
+ case 3:
+ return (cp[h] == 0);
+ case 2:
+ return ((cp[h >> 1] & (0x0f << ((h & 0x1) << 2))) == 0);
+ case 1:
+ return ((cp[h >> 2] & (0x03 << ((h & 0x3) << 1))) == 0);
+ case 0:
+ return ((cp[h >> 3] & (0x01 << (h & 0x7))) == 0);
+ default:
+ panic("ffs_isfreeblock: unknown fs_fragshift %d",
+ (int)fs->fs_fragshift);
+ }
+}
+
+/*
+ * take a block out of the map
+ */
+void
+ffs_clrblock(fs, cp, h)
+ struct fs *fs;
+ u_char *cp;
+ int32_t h;
+{
+
+ switch ((int)fs->fs_fragshift) {
+ case 3:
+ cp[h] = 0;
+ return;
+ case 2:
+ cp[h >> 1] &= ~(0x0f << ((h & 0x1) << 2));
+ return;
+ case 1:
+ cp[h >> 2] &= ~(0x03 << ((h & 0x3) << 1));
+ return;
+ case 0:
+ cp[h >> 3] &= ~(0x01 << (h & 0x7));
+ return;
+ default:
+ panic("ffs_clrblock: unknown fs_fragshift %d",
+ (int)fs->fs_fragshift);
+ }
+}
+
+/*
+ * put a block into the map
+ */
+void
+ffs_setblock(fs, cp, h)
+ struct fs *fs;
+ u_char *cp;
+ int32_t h;
+{
+
+ switch ((int)fs->fs_fragshift) {
+ case 3:
+ cp[h] = 0xff;
+ return;
+ case 2:
+ cp[h >> 1] |= (0x0f << ((h & 0x1) << 2));
+ return;
+ case 1:
+ cp[h >> 2] |= (0x03 << ((h & 0x3) << 1));
+ return;
+ case 0:
+ cp[h >> 3] |= (0x01 << (h & 0x7));
+ return;
+ default:
+ panic("ffs_setblock: unknown fs_fragshift %d",
+ (int)fs->fs_fragshift);
+ }
+}
diff --git a/usr.sbin/makefs/ffs/mkfs.c b/usr.sbin/makefs/ffs/mkfs.c
new file mode 100644
index 0000000..ce9ec59
--- /dev/null
+++ b/usr.sbin/makefs/ffs/mkfs.c
@@ -0,0 +1,832 @@
+/* $NetBSD: mkfs.c,v 1.20 2004/06/24 22:30:13 lukem Exp $ */
+
+/*
+ * 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "makefs.h"
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include "ffs/ufs_bswap.h"
+#include "ffs/ufs_inode.h"
+#include "ffs/ffs_extern.h"
+#include "ffs/newfs_extern.h"
+
+static void initcg(int, time_t, const fsinfo_t *);
+static int ilog2(int);
+
+static int count_digits(int);
+
+/*
+ * make file system for cylinder-group style file systems
+ */
+#define UMASK 0755
+#define POWEROF2(num) (((num) & ((num) - 1)) == 0)
+
+union {
+ struct fs fs;
+ char pad[SBLOCKSIZE];
+} fsun;
+#define sblock fsun.fs
+struct csum *fscs;
+
+union {
+ struct cg cg;
+ char pad[FFS_MAXBSIZE];
+} cgun;
+#define acg cgun.cg
+
+char *iobuf;
+int iobufsize;
+
+char writebuf[FFS_MAXBSIZE];
+
+static int Oflag; /* format as an 4.3BSD file system */
+static int64_t fssize; /* file system size */
+static int sectorsize; /* bytes/sector */
+static int fsize; /* fragment size */
+static int bsize; /* block size */
+static int maxbsize; /* maximum clustering */
+static int maxblkspercg;
+static int minfree; /* free space threshold */
+static int opt; /* optimization preference (space or time) */
+static int density; /* number of bytes per inode */
+static int maxcontig; /* max contiguous blocks to allocate */
+static int maxbpg; /* maximum blocks per file in a cyl group */
+static int sbsize; /* superblock size */
+static int avgfilesize; /* expected average file size */
+static int avgfpdir; /* expected number of files per directory */
+
+struct fs *
+ffs_mkfs(const char *fsys, const fsinfo_t *fsopts)
+{
+ int fragsperinode, optimalfpg, origdensity, minfpg, lastminfpg;
+ int32_t cylno, i, csfrags;
+ long long sizepb;
+ void *space;
+ int size, blks;
+ int nprintcols, printcolwidth;
+
+ Oflag = fsopts->version;
+ fssize = fsopts->size / fsopts->sectorsize;
+ sectorsize = fsopts->sectorsize;
+ fsize = fsopts->fsize;
+ bsize = fsopts->bsize;
+ maxbsize = fsopts->maxbsize;
+ maxblkspercg = fsopts->maxblkspercg;
+ minfree = fsopts->minfree;
+ opt = fsopts->optimization;
+ density = fsopts->density;
+ maxcontig = fsopts->maxcontig;
+ maxbpg = fsopts->maxbpg;
+ avgfilesize = fsopts->avgfilesize;
+ avgfpdir = fsopts->avgfpdir;
+ sbsize = SBLOCKSIZE;
+
+ if (Oflag == 0) {
+ sblock.fs_old_inodefmt = FS_42INODEFMT;
+ sblock.fs_maxsymlinklen = 0;
+ sblock.fs_old_flags = 0;
+ } else {
+ sblock.fs_old_inodefmt = FS_44INODEFMT;
+ sblock.fs_maxsymlinklen = (Oflag == 1 ? MAXSYMLINKLEN_UFS1 :
+ MAXSYMLINKLEN_UFS2);
+ sblock.fs_old_flags = FS_FLAGS_UPDATED;
+ sblock.fs_flags = 0;
+ }
+ /*
+ * 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 %lld\n", (long long)fssize);
+ exit(13);
+ }
+ ffs_wtfs(fssize - 1, sectorsize, (char *)&sblock, fsopts);
+
+ /*
+ * collect and verify the filesystem density info
+ */
+ sblock.fs_avgfilesize = avgfilesize;
+ sblock.fs_avgfpdir = avgfpdir;
+ 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("fragment size %d is too small, minimum is %d\n",
+ sblock.fs_fsize, sectorsize);
+ exit(18);
+ }
+ if (sblock.fs_bsize < MINBSIZE) {
+ printf("block size %d is too small, minimum is %d\n",
+ sblock.fs_bsize, MINBSIZE);
+ exit(19);
+ }
+ if (sblock.fs_bsize > FFS_MAXBSIZE) {
+ printf("block size %d is too large, maximum is %d\n",
+ sblock.fs_bsize, FFS_MAXBSIZE);
+ exit(19);
+ }
+ if (sblock.fs_bsize < sblock.fs_fsize) {
+ printf("block size (%d) cannot be smaller than fragment size (%d)\n",
+ sblock.fs_bsize, sblock.fs_fsize);
+ exit(20);
+ }
+
+ 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;
+ for (sblock.fs_bshift = 0, i = sblock.fs_bsize; i > 1; i >>= 1)
+ sblock.fs_bshift++;
+ for (sblock.fs_fshift = 0, i = sblock.fs_fsize; i > 1; i >>= 1)
+ sblock.fs_fshift++;
+ sblock.fs_frag = numfrags(&sblock, sblock.fs_bsize);
+ for (sblock.fs_fragshift = 0, i = sblock.fs_frag; i > 1; i >>= 1)
+ sblock.fs_fragshift++;
+ if (sblock.fs_frag > MAXFRAG) {
+ printf("fragment size %d is too small, "
+ "minimum with block size %d is %d\n",
+ sblock.fs_fsize, sblock.fs_bsize,
+ sblock.fs_bsize / MAXFRAG);
+ exit(21);
+ }
+ sblock.fs_fsbtodb = ilog2(sblock.fs_fsize / sectorsize);
+ sblock.fs_size = fssize = dbtofsb(&sblock, fssize);
+
+ if (Oflag <= 1) {
+ sblock.fs_magic = FS_UFS1_MAGIC;
+ sblock.fs_sblockloc = SBLOCK_UFS1;
+ sblock.fs_nindir = sblock.fs_bsize / sizeof(int32_t);
+ sblock.fs_inopb = sblock.fs_bsize / sizeof(struct ufs1_dinode);
+ sblock.fs_maxsymlinklen = ((NDADDR + NIADDR) *
+ sizeof (int32_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_magic = FS_UFS2_MAGIC;
+#if 0 /* XXX makefs is used for small filesystems. */
+ sblock.fs_sblockloc = SBLOCK_UFS2;
+#else
+ sblock.fs_sblockloc = SBLOCK_UFS1;
+#endif
+ sblock.fs_nindir = sblock.fs_bsize / sizeof(int64_t);
+ sblock.fs_inopb = sblock.fs_bsize / sizeof(struct ufs2_dinode);
+ sblock.fs_maxsymlinklen = ((NDADDR + NIADDR) *
+ sizeof (int64_t));
+ }
+
+ sblock.fs_sblkno =
+ roundup(howmany(sblock.fs_sblockloc + SBLOCKSIZE, sblock.fs_fsize),
+ sblock.fs_frag);
+ sblock.fs_cblkno = (daddr_t)(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;
+ }
+
+ /*
+ * 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
+ * 1 cylinder group. 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);
+
+ if (maxblkspercg <= 0 || maxblkspercg >= fssize)
+ maxblkspercg = fssize - 1;
+ /*
+ * Start packing more blocks into the cylinder group until
+ * it cannot grow any larger, the number of cylinder groups
+ * drops below 1, 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 < 1)
+ 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 %lld < minimum size of %d\n",
+ (long long)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));
+
+ /*
+ * Setup memory for temporary in-core cylgroup summaries.
+ * Cribbed from ffs_mountfs().
+ */
+ size = sblock.fs_cssize;
+ blks = howmany(size, sblock.fs_fsize);
+ if (sblock.fs_contigsumsize > 0)
+ size += sblock.fs_ncg * sizeof(int32_t);
+ if ((space = (char *)calloc(1, size)) == NULL)
+ err(1, "memory allocation error for cg summaries");
+ sblock.fs_csp = space;
+ space = (char *)space + sblock.fs_cssize;
+ if (sblock.fs_contigsumsize > 0) {
+ int32_t *lp;
+
+ sblock.fs_maxcluster = lp = space;
+ for (i = 0; i < sblock.fs_ncg; i++)
+ *lp++ = sblock.fs_contigsumsize;
+ }
+
+ sblock.fs_sbsize = fragroundup(&sblock, sizeof(struct fs));
+ if (sblock.fs_sbsize > SBLOCKSIZE)
+ sblock.fs_sbsize = SBLOCKSIZE;
+ sblock.fs_minfree = minfree;
+ sblock.fs_maxcontig = maxcontig;
+ sblock.fs_maxbpg = maxbpg;
+ sblock.fs_optim = opt;
+ sblock.fs_cgrotor = 0;
+ sblock.fs_pendingblocks = 0;
+ sblock.fs_pendinginodes = 0;
+ sblock.fs_cstotal.cs_ndir = 0;
+ sblock.fs_cstotal.cs_nbfree = 0;
+ sblock.fs_cstotal.cs_nifree = 0;
+ sblock.fs_cstotal.cs_nffree = 0;
+ sblock.fs_fmod = 0;
+ sblock.fs_ronly = 0;
+ sblock.fs_state = 0;
+ sblock.fs_clean = FS_ISCLEAN;
+ sblock.fs_ronly = 0;
+ sblock.fs_id[0] = start_time.tv_sec;
+ sblock.fs_id[1] = 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 = start_time.tv_sec;
+ if (Oflag <= 1) {
+ sblock.fs_old_time = start_time.tv_sec;
+ 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 (%lld sectors) block size %d, "
+ "fragment size %d\n",
+ fsys, (float)sblock.fs_size * sblock.fs_fsize * B2MBFACTOR,
+ (long long)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);
+#undef B2MBFACTOR
+ /*
+ * Now determine how wide each column will be, and calculate how
+ * many columns will fit in a 76 char line. 76 is the width of the
+ * subwindows in sysinst.
+ */
+ printcolwidth = count_digits(
+ fsbtodb(&sblock, cgsblock(&sblock, sblock.fs_ncg -1)));
+ nprintcols = 76 / (printcolwidth + 2);
+
+ /*
+ * 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);
+ }
+ memset(iobuf, 0, iobufsize);
+ /*
+ * Make a copy of the superblock into the buffer that we will be
+ * writing out in each cylinder group.
+ */
+ memcpy(writebuf, &sblock, sbsize);
+ if (fsopts->needswap)
+ ffs_sb_swap(&sblock, (struct fs*)writebuf);
+ memcpy(iobuf, writebuf, SBLOCKSIZE);
+
+ printf("super-block backups (for fsck -b #) at:");
+ for (cylno = 0; cylno < sblock.fs_ncg; cylno++) {
+ initcg(cylno, start_time.tv_sec, fsopts);
+ if (cylno % nprintcols == 0)
+ printf("\n");
+ printf(" %*lld,", printcolwidth,
+ (long long)fsbtodb(&sblock, cgsblock(&sblock, cylno)));
+ fflush(stdout);
+ }
+ printf("\n");
+
+ /*
+ * Now construct the initial file system,
+ * then write out the super-block.
+ */
+ sblock.fs_time = start_time.tv_sec;
+ 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 (fsopts->needswap)
+ sblock.fs_flags |= FS_SWAPPED;
+ ffs_write_superblock(&sblock, fsopts);
+ return (&sblock);
+}
+
+/*
+ * Write out the superblock and its duplicates,
+ * and the cylinder group summaries
+ */
+void
+ffs_write_superblock(struct fs *fs, const fsinfo_t *fsopts)
+{
+ int cylno, size, blks, i, saveflag;
+ void *space;
+ char *wrbuf;
+
+ saveflag = fs->fs_flags & FS_INTERNAL;
+ fs->fs_flags &= ~FS_INTERNAL;
+
+ memcpy(writebuf, &sblock, sbsize);
+ if (fsopts->needswap)
+ ffs_sb_swap(fs, (struct fs*)writebuf);
+ ffs_wtfs(fs->fs_sblockloc / sectorsize, sbsize, writebuf, fsopts);
+
+ /* Write out the duplicate super blocks */
+ for (cylno = 0; cylno < fs->fs_ncg; cylno++)
+ ffs_wtfs(fsbtodb(fs, cgsblock(fs, cylno)),
+ sbsize, writebuf, fsopts);
+
+ /* Write out the cylinder group summaries */
+ size = fs->fs_cssize;
+ blks = howmany(size, fs->fs_fsize);
+ space = (void *)fs->fs_csp;
+ if ((wrbuf = malloc(size)) == NULL)
+ err(1, "ffs_write_superblock: malloc %d", size);
+ for (i = 0; i < blks; i+= fs->fs_frag) {
+ size = fs->fs_bsize;
+ if (i + fs->fs_frag > blks)
+ size = (blks - i) * fs->fs_fsize;
+ if (fsopts->needswap)
+ ffs_csum_swap((struct csum *)space,
+ (struct csum *)wrbuf, size);
+ else
+ memcpy(wrbuf, space, (u_int)size);
+ ffs_wtfs(fsbtodb(fs, fs->fs_csaddr + i), size, wrbuf, fsopts);
+ space = (char *)space + size;
+ }
+ free(wrbuf);
+ fs->fs_flags |= saveflag;
+}
+
+/*
+ * Initialize a cylinder group.
+ */
+static void
+initcg(int cylno, time_t utime, const fsinfo_t *fsopts)
+{
+ daddr_t cbase, dmax;
+ int32_t i, j, d, dlower, dupper, blkno;
+ struct ufs1_dinode *dp1;
+ struct ufs2_dinode *dp2;
+ int start;
+
+ /*
+ * 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);
+ 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_fragshift;
+ start = &acg.cg_space[0] - (u_char *)(&acg.cg_firstfield);
+ if (Oflag == 2) {
+ acg.cg_iusedoff = start;
+ } else {
+ if (cylno == sblock.fs_ncg - 1)
+ acg.cg_old_ncyl = howmany(acg.cg_ndblk,
+ sblock.fs_fpg / sblock.fs_old_cpg);
+ 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);
+ if (sblock.fs_contigsumsize <= 0) {
+ acg.cg_nextfreeoff = acg.cg_freeoff +
+ howmany(sblock.fs_fpg, CHAR_BIT);
+ } else {
+ acg.cg_clustersumoff = acg.cg_freeoff +
+ howmany(sblock.fs_fpg, CHAR_BIT) - sizeof(int32_t);
+ acg.cg_clustersumoff =
+ roundup(acg.cg_clustersumoff, sizeof(int32_t));
+ acg.cg_clusteroff = acg.cg_clustersumoff +
+ (sblock.fs_contigsumsize + 1) * sizeof(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 < ROOTINO; i++) {
+ setbit(cg_inosused_swap(&acg, 0), i);
+ acg.cg_cs.cs_nifree--;
+ }
+ if (cylno > 0) {
+ /*
+ * In cylno 0, beginning space is reserved
+ * for boot and super blocks.
+ */
+ for (d = 0, blkno = 0; d < dlower;) {
+ ffs_setblock(&sblock, cg_blksfree_swap(&acg, 0), blkno);
+ if (sblock.fs_contigsumsize > 0)
+ setbit(cg_clustersfree_swap(&acg, 0), blkno);
+ acg.cg_cs.cs_nbfree++;
+ d += sblock.fs_frag;
+ blkno++;
+ }
+ }
+ if ((i = (dupper & (sblock.fs_frag - 1))) != 0) {
+ acg.cg_frsum[sblock.fs_frag - i]++;
+ for (d = dupper + sblock.fs_frag - i; dupper < d; dupper++) {
+ setbit(cg_blksfree_swap(&acg, 0), dupper);
+ acg.cg_cs.cs_nffree++;
+ }
+ }
+ for (d = dupper, blkno = dupper >> sblock.fs_fragshift;
+ d + sblock.fs_frag <= acg.cg_ndblk; ) {
+ ffs_setblock(&sblock, cg_blksfree_swap(&acg, 0), blkno);
+ if (sblock.fs_contigsumsize > 0)
+ setbit(cg_clustersfree_swap(&acg, 0), blkno);
+ acg.cg_cs.cs_nbfree++;
+ d += sblock.fs_frag;
+ blkno++;
+ }
+ if (d < acg.cg_ndblk) {
+ acg.cg_frsum[acg.cg_ndblk - d]++;
+ for (; d < acg.cg_ndblk; d++) {
+ setbit(cg_blksfree_swap(&acg, 0), d);
+ acg.cg_cs.cs_nffree++;
+ }
+ }
+ if (sblock.fs_contigsumsize > 0) {
+ int32_t *sump = cg_clustersum_swap(&acg, 0);
+ u_char *mapp = cg_clustersfree_swap(&acg, 0);
+ 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_cs(&sblock, cylno) = 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;
+ memcpy(&iobuf[start], &acg, sblock.fs_cgsize);
+ if (fsopts->needswap)
+ ffs_cg_swap(&acg, (struct cg*)&iobuf[start], &sblock);
+ 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) {
+ /* No need to swap, it'll stay random */
+ dp1->di_gen = random();
+ dp1++;
+ } else {
+ dp2->di_gen = random();
+ dp2++;
+ }
+ }
+ ffs_wtfs(fsbtodb(&sblock, cgsblock(&sblock, cylno)), iobufsize, iobuf,
+ fsopts);
+ /*
+ * 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 = random();
+ dp1++;
+ }
+ ffs_wtfs(fsbtodb(&sblock, cgimin(&sblock, cylno) + i),
+ sblock.fs_bsize, &iobuf[start], fsopts);
+ }
+ }
+}
+
+/*
+ * read a block from the file system
+ */
+void
+ffs_rdfs(daddr_t bno, int size, void *bf, const fsinfo_t *fsopts)
+{
+ int n;
+ off_t offset;
+
+ offset = bno;
+ offset *= fsopts->sectorsize;
+ if (lseek(fsopts->fd, offset, SEEK_SET) < 0)
+ err(1, "ffs_rdfs: seek error for sector %lld: %s\n",
+ (long long)bno, strerror(errno));
+ n = read(fsopts->fd, bf, size);
+ if (n == -1) {
+ abort();
+ err(1, "ffs_rdfs: read error bno %lld size %d", (long long)bno,
+ size);
+ }
+ else if (n != size)
+ errx(1, "ffs_rdfs: read error for sector %lld: %s\n",
+ (long long)bno, strerror(errno));
+}
+
+/*
+ * write a block to the file system
+ */
+void
+ffs_wtfs(daddr_t bno, int size, void *bf, const fsinfo_t *fsopts)
+{
+ int n;
+ off_t offset;
+
+ offset = bno;
+ offset *= fsopts->sectorsize;
+ if (lseek(fsopts->fd, offset, SEEK_SET) < 0)
+ err(1, "wtfs: seek error for sector %lld: %s\n",
+ (long long)bno, strerror(errno));
+ n = write(fsopts->fd, bf, size);
+ if (n == -1)
+ err(1, "wtfs: write error for sector %lld: %s\n",
+ (long long)bno, strerror(errno));
+ else if (n != size)
+ errx(1, "wtfs: write error for sector %lld: %s\n",
+ (long long)bno, strerror(errno));
+}
+
+
+/* Determine how many digits are needed to print a given integer */
+static int
+count_digits(int num)
+{
+ int ndig;
+
+ for(ndig = 1; num > 9; num /=10, ndig++);
+
+ return (ndig);
+}
+
+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);
+}
diff --git a/usr.sbin/makefs/ffs/newfs_extern.h b/usr.sbin/makefs/ffs/newfs_extern.h
new file mode 100644
index 0000000..88559b6
--- /dev/null
+++ b/usr.sbin/makefs/ffs/newfs_extern.h
@@ -0,0 +1,41 @@
+/* $NetBSD: newfs_extern.h,v 1.2 2004/06/24 22:30:13 lukem Exp $ */
+/* From: NetBSD: extern.h,v 1.3 2000/12/01 12:03:27 simonb Exp $ */
+
+/*
+ * Copyright (c) 1997 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$
+ */
+
+/* prototypes */
+struct fs *ffs_mkfs(const char *, const fsinfo_t *);
+void ffs_write_superblock(struct fs *, const fsinfo_t *);
+void ffs_rdfs(daddr_t, int, void *, const fsinfo_t *);
+void ffs_wtfs(daddr_t, int, void *, const fsinfo_t *);
+
+#define FFS_MAXBSIZE 65536
diff --git a/usr.sbin/makefs/ffs/ufs_bmap.c b/usr.sbin/makefs/ffs/ufs_bmap.c
new file mode 100644
index 0000000..85c1d87
--- /dev/null
+++ b/usr.sbin/makefs/ffs/ufs_bmap.c
@@ -0,0 +1,142 @@
+/* $NetBSD: ufs_bmap.c,v 1.14 2004/06/20 22:20:18 jmc Exp $ */
+/* From: NetBSD: ufs_bmap.c,v 1.14 2001/11/08 05:00:51 chs Exp */
+
+/*
+ * Copyright (c) 1989, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ufs_bmap.c 8.8 (Berkeley) 8/11/95
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/time.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <strings.h>
+
+#include "makefs.h"
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include "ffs/ufs_bswap.h"
+#include "ffs/ufs_inode.h"
+#include "ffs/ffs_extern.h"
+
+/*
+ * Create an array of logical block number/offset pairs which represent the
+ * path of indirect blocks required to access a data block. The first "pair"
+ * contains the logical block number of the appropriate single, double or
+ * triple indirect block and the offset into the inode indirect block array.
+ * Note, the logical block number of the inode single/double/triple indirect
+ * block appears twice in the array, once with the offset into the i_ffs_ib and
+ * once with the offset into the page itself.
+ */
+int
+ufs_getlbns(struct inode *ip, daddr_t bn, struct indir *ap, int *nump)
+{
+ daddr_t metalbn, realbn;
+ int64_t blockcnt;
+ int lbc;
+ int i, numlevels, off;
+ u_long lognindir;
+
+ lognindir = ffs(NINDIR(ip->i_fs)) - 1;
+ if (nump)
+ *nump = 0;
+ numlevels = 0;
+ realbn = bn;
+ if ((long)bn < 0)
+ bn = -(long)bn;
+
+ assert (bn >= NDADDR);
+
+ /*
+ * Determine the number of levels of indirection. After this loop
+ * is done, blockcnt indicates the number of data blocks possible
+ * at the given level of indirection, and NIADDR - i is the number
+ * of levels of indirection needed to locate the requested block.
+ */
+
+ bn -= NDADDR;
+ for (lbc = 0, i = NIADDR;; i--, bn -= blockcnt) {
+ if (i == 0)
+ return (EFBIG);
+
+ lbc += lognindir;
+ blockcnt = (int64_t)1 << lbc;
+
+ if (bn < blockcnt)
+ break;
+ }
+
+ /* Calculate the address of the first meta-block. */
+ if (realbn >= 0)
+ metalbn = -(realbn - bn + NIADDR - i);
+ else
+ metalbn = -(-realbn - bn + NIADDR - i);
+
+ /*
+ * At each iteration, off is the offset into the bap array which is
+ * an array of disk addresses at the current level of indirection.
+ * The logical block number and the offset in that block are stored
+ * into the argument array.
+ */
+ ap->in_lbn = metalbn;
+ ap->in_off = off = NIADDR - i;
+ ap->in_exists = 0;
+ ap++;
+ for (++numlevels; i <= NIADDR; i++) {
+ /* If searching for a meta-data block, quit when found. */
+ if (metalbn == realbn)
+ break;
+
+ lbc -= lognindir;
+ blockcnt = (int64_t)1 << lbc;
+ off = (bn >> lbc) & (NINDIR(ip->i_fs) - 1);
+
+ ++numlevels;
+ ap->in_lbn = metalbn;
+ ap->in_off = off;
+ ap->in_exists = 0;
+ ++ap;
+
+ metalbn -= -1 + (off << lbc);
+ }
+ if (nump)
+ *nump = numlevels;
+ return (0);
+}
diff --git a/usr.sbin/makefs/ffs/ufs_bswap.h b/usr.sbin/makefs/ffs/ufs_bswap.h
new file mode 100644
index 0000000..c368255
--- /dev/null
+++ b/usr.sbin/makefs/ffs/ufs_bswap.h
@@ -0,0 +1,88 @@
+/* $NetBSD: ufs_bswap.h,v 1.13 2003/10/05 17:48:50 bouyer Exp $ */
+
+/*
+ * Copyright (c) 1998 Manuel Bouyer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Manuel Bouyer.
+ * 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$
+ */
+
+#ifndef _UFS_UFS_BSWAP_H_
+#define _UFS_UFS_BSWAP_H_
+
+#if defined(_KERNEL_OPT)
+#include "opt_ffs.h"
+#endif
+
+#include <sys/endian.h>
+
+/* Macros to access UFS flags */
+#ifdef FFS_EI
+#define UFS_MPNEEDSWAP(mp) (VFSTOUFS(mp)->um_flags & UFS_NEEDSWAP)
+#define UFS_FSNEEDSWAP(fs) ((fs)->fs_flags & FS_SWAPPED)
+#define UFS_IPNEEDSWAP(ip) UFS_MPNEEDSWAP(ITOV(ip)->v_mount)
+#else
+#define UFS_MPNEEDSWAP(mp) (0)
+#define UFS_FSNEEDSWAP(fs) (0)
+#define UFS_IPNEEDSWAP(ip) (0)
+#endif
+
+#if !defined(_KERNEL) || defined(FFS_EI)
+/* inlines for access to swapped data */
+static __inline u_int16_t ufs_rw16 __P((u_int16_t, int));
+static __inline u_int32_t ufs_rw32 __P((u_int32_t, int));
+static __inline u_int64_t ufs_rw64 __P((u_int64_t, int));
+
+static __inline u_int16_t
+ufs_rw16(u_int16_t a, int ns)
+{
+ return ((ns) ? bswap16(a) : (a));
+}
+static __inline u_int32_t
+ufs_rw32(u_int32_t a, int ns)
+{
+ return ((ns) ? bswap32(a) : (a));
+}
+static __inline u_int64_t
+ufs_rw64(u_int64_t a, int ns)
+{
+ return ((ns) ? bswap64(a) : (a));
+}
+#else
+#define ufs_rw16(a, ns) ((uint16_t)(a))
+#define ufs_rw32(a, ns) ((uint32_t)(a))
+#define ufs_rw64(a, ns) ((uint64_t)(a))
+#endif
+
+#define ufs_add16(a, b, ns) \
+ (a) = ufs_rw16(ufs_rw16((a), (ns)) + (b), (ns))
+#define ufs_add32(a, b, ns) \
+ (a) = ufs_rw32(ufs_rw32((a), (ns)) + (b), (ns))
+#define ufs_add64(a, b, ns) \
+ (a) = ufs_rw64(ufs_rw64((a), (ns)) + (b), (ns))
+
+#endif /* !_UFS_UFS_BSWAP_H_ */
diff --git a/usr.sbin/makefs/ffs/ufs_inode.h b/usr.sbin/makefs/ffs/ufs_inode.h
new file mode 100644
index 0000000..8286f86
--- /dev/null
+++ b/usr.sbin/makefs/ffs/ufs_inode.h
@@ -0,0 +1,97 @@
+/* $NetBSD: ufs_inode.h,v 1.3 2003/08/07 11:25:34 agc Exp $ */
+/* From: NetBSD: inode.h,v 1.27 2001/12/18 10:57:23 fvdl Exp $ */
+
+/*
+ * Copyright (c) 1982, 1989, 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)inode.h 8.9 (Berkeley) 5/14/95
+ * $FreeBSD$
+ */
+
+union dinode {
+ struct ufs1_dinode ffs1_din;
+ struct ufs2_dinode ffs2_din;
+};
+
+struct inode {
+ ino_t i_number; /* The identity of the inode. */
+ struct fs *i_fs; /* File system */
+ union dinode i_din;
+ int i_fd; /* File descriptor */
+ uint64_t i_size;
+};
+
+#define i_ffs1_atime i_din.ffs1_din.di_atime
+#define i_ffs1_atimensec i_din.ffs1_din.di_atimensec
+#define i_ffs1_blocks i_din.ffs1_din.di_blocks
+#define i_ffs1_ctime i_din.ffs1_din.di_ctime
+#define i_ffs1_ctimensec i_din.ffs1_din.di_ctimensec
+#define i_ffs1_db i_din.ffs1_din.di_db
+#define i_ffs1_flags i_din.ffs1_din.di_flags
+#define i_ffs1_gen i_din.ffs1_din.di_gen
+#define i_ffs11_gid i_din.ffs1_din.di_gid
+#define i_ffs1_ib i_din.ffs1_din.di_ib
+#define i_ffs1_mode i_din.ffs1_din.di_mode
+#define i_ffs1_mtime i_din.ffs1_din.di_mtime
+#define i_ffs1_mtimensec i_din.ffs1_din.di_mtimensec
+#define i_ffs1_nlink i_din.ffs1_din.di_nlink
+#define i_ffs1_rdev i_din.ffs1_din.di_rdev
+#define i_ffs1_shortlink i_din.ffs1_din.db
+#define i_ffs1_size i_din.ffs1_din.di_size
+#define i_ffs1_uid i_din.ffs1_din.di_uid
+
+#define i_ffs2_atime i_din.ffs2_din.di_atime
+#define i_ffs2_atimensec i_din.ffs2_din.di_atimensec
+#define i_ffs2_blocks i_din.ffs2_din.di_blocks
+#define i_ffs2_ctime i_din.ffs2_din.di_ctime
+#define i_ffs2_ctimensec i_din.ffs2_din.di_ctimensec
+#define i_ffs2_birthtime i_din.ffs2_din.di_birthtime
+#define i_ffs2_birthnsec i_din.ffs2_din.di_birthnsec
+#define i_ffs2_db i_din.ffs2_din.di_db
+#define i_ffs2_flags i_din.ffs2_din.di_flags
+#define i_ffs2_gen i_din.ffs2_din.di_gen
+#define i_ffs21_gid i_din.ffs2_din.di_gid
+#define i_ffs2_ib i_din.ffs2_din.di_ib
+#define i_ffs2_mode i_din.ffs2_din.di_mode
+#define i_ffs2_mtime i_din.ffs2_din.di_mtime
+#define i_ffs2_mtimensec i_din.ffs2_din.di_mtimensec
+#define i_ffs2_nlink i_din.ffs2_din.di_nlink
+#define i_ffs2_rdev i_din.ffs2_din.di_rdev
+#define i_ffs2_shortlink i_din.ffs2_din.db
+#define i_ffs2_size i_din.ffs2_din.di_size
+#define i_ffs2_uid i_din.ffs2_din.di_uid
+
+#undef DIP
+#define DIP(ip, field) \
+ (((ip)->i_fs->fs_magic == FS_UFS1_MAGIC) ? \
+ (ip)->i_ffs1_##field : (ip)->i_ffs2_##field)
diff --git a/usr.sbin/makefs/getid.c b/usr.sbin/makefs/getid.c
new file mode 100644
index 0000000..ca52fa3
--- /dev/null
+++ b/usr.sbin/makefs/getid.c
@@ -0,0 +1,436 @@
+/* $NetBSD: getid.c,v 1.5 2004/06/20 22:20:18 jmc Exp $ */
+/* from: NetBSD: getpwent.c,v 1.48 2000/10/03 03:22:26 enami Exp */
+/* from: NetBSD: getgrent.c,v 1.41 2002/01/12 23:51:30 lukem Exp */
+
+/*
+ * Copyright (c) 1987, 1988, 1989, 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*-
+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Luke Mewburn of Wasabi 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 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <grp.h>
+#include <limits.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "makefs.h"
+
+#include "mtree.h"
+#include "extern.h"
+
+static struct group * gi_getgrnam(const char *);
+static struct group * gi_getgrgid(gid_t);
+static int gi_setgroupent(int);
+static void gi_endgrent(void);
+static int grstart(void);
+static int grscan(int, gid_t, const char *);
+static int grmatchline(int, gid_t, const char *);
+
+static struct passwd * gi_getpwnam(const char *);
+static struct passwd * gi_getpwuid(uid_t);
+static int gi_setpassent(int);
+static void gi_endpwent(void);
+static int pwstart(void);
+static int pwscan(int, uid_t, const char *);
+static int pwmatchline(int, uid_t, const char *);
+
+#define MAXGRP 200
+#define MAXLINELENGTH 1024
+
+static FILE *_gr_fp;
+static struct group _gr_group;
+static int _gr_stayopen;
+static int _gr_filesdone;
+static FILE *_pw_fp;
+static struct passwd _pw_passwd; /* password structure */
+static int _pw_stayopen; /* keep fd's open */
+static int _pw_filesdone;
+
+static char grfile[MAXPATHLEN];
+static char pwfile[MAXPATHLEN];
+
+static char *members[MAXGRP];
+static char grline[MAXLINELENGTH];
+static char pwline[MAXLINELENGTH];
+
+int
+setup_getid(const char *dir)
+{
+ if (dir == NULL)
+ return (0);
+
+ /* close existing databases */
+ gi_endgrent();
+ gi_endpwent();
+
+ /* build paths to new databases */
+ snprintf(grfile, sizeof(grfile), "%s/group", dir);
+ snprintf(pwfile, sizeof(pwfile), "%s/master.passwd", dir);
+
+ /* try to open new databases */
+ if (!grstart() || !pwstart())
+ return (0);
+
+ /* switch pwcache(3) lookup functions */
+ if (pwcache_groupdb(gi_setgroupent, gi_endgrent,
+ gi_getgrnam, gi_getgrgid) == -1
+ || pwcache_userdb(gi_setpassent, gi_endpwent,
+ gi_getpwnam, gi_getpwuid) == -1)
+ return (0);
+
+ return (1);
+}
+
+
+/*
+ * group lookup functions
+ */
+
+static struct group *
+gi_getgrnam(const char *name)
+{
+ int rval;
+
+ if (!grstart())
+ return NULL;
+ rval = grscan(1, 0, name);
+ if (!_gr_stayopen)
+ endgrent();
+ return (rval) ? &_gr_group : NULL;
+}
+
+static struct group *
+gi_getgrgid(gid_t gid)
+{
+ int rval;
+
+ if (!grstart())
+ return NULL;
+ rval = grscan(1, gid, NULL);
+ if (!_gr_stayopen)
+ endgrent();
+ return (rval) ? &_gr_group : NULL;
+}
+
+static int
+gi_setgroupent(int stayopen)
+{
+
+ if (!grstart())
+ return 0;
+ _gr_stayopen = stayopen;
+ return 1;
+}
+
+static void
+gi_endgrent(void)
+{
+
+ _gr_filesdone = 0;
+ if (_gr_fp) {
+ (void)fclose(_gr_fp);
+ _gr_fp = NULL;
+ }
+}
+
+static int
+grstart(void)
+{
+
+ _gr_filesdone = 0;
+ if (_gr_fp) {
+ rewind(_gr_fp);
+ return 1;
+ }
+ if (grfile[0] == '\0') /* sanity check */
+ return 0;
+ return (_gr_fp = fopen(grfile, "r")) ? 1 : 0;
+}
+
+
+static int
+grscan(int search, gid_t gid, const char *name)
+{
+
+ if (_gr_filesdone)
+ return 0;
+ for (;;) {
+ if (!fgets(grline, sizeof(grline), _gr_fp)) {
+ if (!search)
+ _gr_filesdone = 1;
+ return 0;
+ }
+ /* skip lines that are too big */
+ if (!strchr(grline, '\n')) {
+ int ch;
+
+ while ((ch = getc(_gr_fp)) != '\n' && ch != EOF)
+ ;
+ continue;
+ }
+ if (grmatchline(search, gid, name))
+ return 1;
+ }
+ /* NOTREACHED */
+}
+
+static int
+grmatchline(int search, gid_t gid, const char *name)
+{
+ unsigned long id;
+ char **m;
+ char *cp, *bp, *ep;
+
+ /* name may be NULL if search is nonzero */
+
+ bp = grline;
+ memset(&_gr_group, 0, sizeof(_gr_group));
+ _gr_group.gr_name = strsep(&bp, ":\n");
+ if (search && name && strcmp(_gr_group.gr_name, name))
+ return 0;
+ _gr_group.gr_passwd = strsep(&bp, ":\n");
+ if (!(cp = strsep(&bp, ":\n")))
+ return 0;
+ id = strtoul(cp, &ep, 10);
+ if (id > GID_MAX || *ep != '\0')
+ return 0;
+ _gr_group.gr_gid = (gid_t)id;
+ if (search && name == NULL && _gr_group.gr_gid != gid)
+ return 0;
+ cp = NULL;
+ if (bp == NULL)
+ return 0;
+ for (_gr_group.gr_mem = m = members;; bp++) {
+ if (m == &members[MAXGRP - 1])
+ break;
+ if (*bp == ',') {
+ if (cp) {
+ *bp = '\0';
+ *m++ = cp;
+ cp = NULL;
+ }
+ } else if (*bp == '\0' || *bp == '\n' || *bp == ' ') {
+ if (cp) {
+ *bp = '\0';
+ *m++ = cp;
+ }
+ break;
+ } else if (cp == NULL)
+ cp = bp;
+ }
+ *m = NULL;
+ return 1;
+}
+
+
+/*
+ * user lookup functions
+ */
+
+static struct passwd *
+gi_getpwnam(const char *name)
+{
+ int rval;
+
+ if (!pwstart())
+ return NULL;
+ rval = pwscan(1, 0, name);
+ if (!_pw_stayopen)
+ endpwent();
+ return (rval) ? &_pw_passwd : NULL;
+}
+
+static struct passwd *
+gi_getpwuid(uid_t uid)
+{
+ int rval;
+
+ if (!pwstart())
+ return NULL;
+ rval = pwscan(1, uid, NULL);
+ if (!_pw_stayopen)
+ endpwent();
+ return (rval) ? &_pw_passwd : NULL;
+}
+
+static int
+gi_setpassent(int stayopen)
+{
+
+ if (!pwstart())
+ return 0;
+ _pw_stayopen = stayopen;
+ return 1;
+}
+
+static void
+gi_endpwent(void)
+{
+
+ _pw_filesdone = 0;
+ if (_pw_fp) {
+ (void)fclose(_pw_fp);
+ _pw_fp = NULL;
+ }
+}
+
+static int
+pwstart(void)
+{
+
+ _pw_filesdone = 0;
+ if (_pw_fp) {
+ rewind(_pw_fp);
+ return 1;
+ }
+ if (pwfile[0] == '\0') /* sanity check */
+ return 0;
+ return (_pw_fp = fopen(pwfile, "r")) ? 1 : 0;
+}
+
+
+static int
+pwscan(int search, uid_t uid, const char *name)
+{
+
+ if (_pw_filesdone)
+ return 0;
+ for (;;) {
+ if (!fgets(pwline, sizeof(pwline), _pw_fp)) {
+ if (!search)
+ _pw_filesdone = 1;
+ return 0;
+ }
+ /* skip lines that are too big */
+ if (!strchr(pwline, '\n')) {
+ int ch;
+
+ while ((ch = getc(_pw_fp)) != '\n' && ch != EOF)
+ ;
+ continue;
+ }
+ if (pwmatchline(search, uid, name))
+ return 1;
+ }
+ /* NOTREACHED */
+}
+
+static int
+pwmatchline(int search, uid_t uid, const char *name)
+{
+ unsigned long id;
+ char *cp, *bp, *ep;
+
+ /* name may be NULL if search is nonzero */
+
+ bp = pwline;
+ memset(&_pw_passwd, 0, sizeof(_pw_passwd));
+ _pw_passwd.pw_name = strsep(&bp, ":\n"); /* name */
+ if (search && name && strcmp(_pw_passwd.pw_name, name))
+ return 0;
+
+ _pw_passwd.pw_passwd = strsep(&bp, ":\n"); /* passwd */
+
+ if (!(cp = strsep(&bp, ":\n"))) /* uid */
+ return 0;
+ id = strtoul(cp, &ep, 10);
+ if (id > UID_MAX || *ep != '\0')
+ return 0;
+ _pw_passwd.pw_uid = (uid_t)id;
+ if (search && name == NULL && _pw_passwd.pw_uid != uid)
+ return 0;
+
+ if (!(cp = strsep(&bp, ":\n"))) /* gid */
+ return 0;
+ id = strtoul(cp, &ep, 10);
+ if (id > GID_MAX || *ep != '\0')
+ return 0;
+ _pw_passwd.pw_gid = (gid_t)id;
+
+ if (!(ep = strsep(&bp, ":"))) /* class */
+ return 0;
+ if (!(ep = strsep(&bp, ":"))) /* change */
+ return 0;
+ if (!(ep = strsep(&bp, ":"))) /* expire */
+ return 0;
+
+ if (!(_pw_passwd.pw_gecos = strsep(&bp, ":\n"))) /* gecos */
+ return 0;
+ if (!(_pw_passwd.pw_dir = strsep(&bp, ":\n"))) /* directory */
+ return 0;
+ if (!(_pw_passwd.pw_shell = strsep(&bp, ":\n"))) /* shell */
+ return 0;
+
+ if (strchr(bp, ':') != NULL)
+ return 0;
+
+ return 1;
+}
diff --git a/usr.sbin/makefs/makefs.8 b/usr.sbin/makefs/makefs.8
new file mode 100644
index 0000000..101366a
--- /dev/null
+++ b/usr.sbin/makefs/makefs.8
@@ -0,0 +1,288 @@
+.\" $NetBSD: makefs.8,v 1.13 2004/02/13 17:56:18 wiz Exp $
+.\"
+.\" Copyright (c) 2001-2003 Wasabi Systems, Inc.
+.\" All rights reserved.
+.\"
+.\" Written by Luke Mewburn 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 30, 2003
+.Dt MAKEFS 8
+.Os
+.Sh NAME
+.Nm makefs
+.Nd create a file system image from a directory tree
+.Sh SYNOPSIS
+.Nm
+.Bk -words
+.Op Fl t Ar fs-type
+.Ek
+.Bk -words
+.Op Fl o Ar fs-options
+.Ek
+.Bk -words
+.Op Fl d Ar debug-mask
+.Ek
+.Bk -words
+.Op Fl B Ar byte-order
+.Ek
+.Bk -words
+.Op Fl S Ar sector-size
+.Ek
+.Bk -words
+.Op Fl M Ar minimum-size
+.Ek
+.Bk -words
+.Op Fl m Ar maximum-size
+.Ek
+.Bk -words
+.Op Fl s Ar image-size
+.Ek
+.Bk -words
+.Op Fl b Ar free-blocks
+.Ek
+.Bk -words
+.Op Fl f Ar free-files
+.Ek
+.Bk -words
+.Op Fl F Ar specfile
+.Ek
+.Op Fl x
+.Bk -words
+.Op Fl N Ar userdb-dir
+.Ek
+.Ar image-file
+.Ar directory
+.Sh DESCRIPTION
+The utility
+.Nm
+creates a file system image into
+.Ar image-file
+from the directory tree
+.Ar directory .
+No special devices or privileges are required to perform this task.
+.Pp
+The options are as follows:
+.Bl -tag -width flag
+.It Fl t Ar fs-type
+Create an
+.Ar fs-type
+file system image.
+The following file system types are supported:
+.Bl -tag -width ffs -offset indent
+.It Sy ffs
+BSD fast file system (default).
+.El
+.It Fl o Ar fs-options
+Set file system specific options.
+.Ar fs-options
+is a comma separated list of options.
+Valid file system specific options are detailed below.
+.It Fl d Ar debug-mask
+Enable various levels of debugging, depending upon which bits are set
+in
+.Ar debug-mask .
+XXX: document these
+.It Fl B Ar byte-order
+Set the byte order of the image to
+.Ar byte-order .
+Valid byte orders are
+.Ql 4321 ,
+.Ql big
+or
+.Ql be
+for big endian, and
+.Ql 1234 ,
+.Ql little
+or
+.Ql le
+for little endian.
+Some file systems may have a fixed byte order; in those cases this
+argument will be ignored.
+.It Fl S Ar sector-size
+Set the file system sector size to
+.Ar sector-size .
+Defaults to 512.
+.It Fl M Ar minimum-size
+Set the minimum size of the file system image to
+.Ar minimum-size .
+.It Fl m Ar maximum-size
+Set the maximum size of the file system image to
+.Ar maximum-size .
+An error will be raised if the target file system needs to be larger
+than this to accommodate the provided directory tree.
+.It Fl s Ar image-size
+Set the size of the file system image to
+.Ar image-size .
+.It Fl b Ar free-blocks
+Ensure that a minimum of
+.Ar free-blocks
+free blocks exist in the image.
+An optional
+.Ql %
+suffix may be provided to indicate that
+.Ar free-blocks
+indicates a percentage of the calculated image size
+.It Fl f Ar free-files
+Ensure that a minimum of
+.Ar free-files
+free files (inodes) exist in the image.
+An optional
+.Ql %
+suffix may be provided to indicate that
+.Ar free-blocks
+indicates a percentage of the calculated image size
+.It Fl F Ar specfile
+Use
+.Ar specfile
+as an
+.Xr mtree 8
+.Sq specfile
+specification.
+.Pp
+If a specfile entry exists in the underlying file system, its permissions and
+modification time will be used unless specifically overridden by the specfile.
+An error will be raised if the type of entry in the specfile conflicts
+with that of an existing entry.
+.Pp
+In the opposite case
+(where a specfile entry does not have an entry in the underlying file system)
+the following occurs:
+If the specfile entry is marked
+.Sy optional ,
+the specfile entry is ignored.
+Otherwise, the entry will be created in the image,
+and it is necessary to specify at least the following parameters
+in the specfile:
+.Sy type ,
+.Sy mode ,
+.Sy gname
+or
+.Sy gid ,
+and
+.Sy uname
+or
+.Sy uid ,
+.Sy device
+(in the case of block or character devices), and
+.Sy link
+(in the case of symbolic links).
+If
+.Sy time
+isn't provided, the current time will be used.
+If
+.Sy flags
+isn't provided, the current file flags will be used.
+Missing regular file entries will be created as zero-length files.
+.It Fl x
+Exclude file system nodes not explcitly listed in the specfile.
+.It Fl N Ar dbdir
+Use the user database text file
+.Pa master.passwd
+and group database text file
+.Pa group
+from
+.Ar dbdir ,
+rather than using the results from the system's
+.Xr getpwnam 3
+and
+.Xr getgrnam 3
+(and related) library calls.
+.El
+.Pp
+Where sizes are specified, a decimal number of bytes is expected.
+Two or more numbers may be separated by an
+.Dq x
+to indicate a product.
+Each number may have one of the following optional suffixes:
+.Bl -tag -width 3n -offset indent -compact
+.It b
+Block; multiply by 512
+.It k
+Kilo; multiply by 1024 (1 KB)
+.It m
+Mega; multiply by 1048576 (1 MB)
+.It g
+Giga; multiply by 1073741824 (1 GB)
+.It t
+Tera; multiply by 1099511627776 (1 TB)
+.It w
+Word; multiply by the number of bytes in an integer
+.El
+.\"
+.\"
+.Ss FFS-specific options
+.Sy ffs
+images have ffs-specific optional parameters that may be provided.
+Each of the options consists of a keyword, an equals sign
+.Pq Ql = ,
+and a value.
+The following keywords are supported:
+.Pp
+.Bl -tag -width optimization -offset indent -compact
+.It Sy avgfilesize
+Expected average file size
+.It Sy avgfpdir
+Expected number of files per directory
+.It Sy bsize
+Block size
+.It Sy density
+Bytes per inode
+.It Sy fsize
+Fragment size
+.It Sy maxbpg
+Maximum blocks per file in a cylinder group
+.It Sy minfree
+Minimum % free
+.It Sy optimization
+Optimization preference; one of
+.Ql space
+or
+.Ql time .
+.It Sy extent
+Maximum extent size
+.It Sy maxbpcg
+Maximum total number of blocks in a cylinder group
+.It Sy version
+UFS version. 1 for FFS (default), 2 for UFS2
+.El
+.Sh SEE ALSO
+.Xr mtree 8 ,
+.Xr newfs 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Nx 1.6 .
+.Sh AUTHORS
+.An Luke Mewburn
+.Aq lukem@NetBSD.org .
diff --git a/usr.sbin/makefs/makefs.c b/usr.sbin/makefs/makefs.c
new file mode 100644
index 0000000..c3e4bd2
--- /dev/null
+++ b/usr.sbin/makefs/makefs.c
@@ -0,0 +1,314 @@
+/* $NetBSD: makefs.c,v 1.20 2004/06/20 22:20:18 jmc Exp $ */
+
+/*
+ * Copyright (c) 2001-2003 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "makefs.h"
+#include "mtree.h"
+
+/*
+ * list of supported file systems and dispatch functions
+ */
+typedef struct {
+ const char *type;
+ int (*parse_options)(const char *, fsinfo_t *);
+ void (*make_fs)(const char *, const char *, fsnode *,
+ fsinfo_t *);
+} fstype_t;
+
+static fstype_t fstypes[] = {
+ { "ffs", ffs_parse_opts, ffs_makefs },
+ { NULL },
+};
+
+u_int debug;
+struct timespec start_time;
+
+static fstype_t *get_fstype(const char *);
+static void usage(void);
+int main(int, char *[]);
+
+int
+main(int argc, char *argv[])
+{
+ struct timeval start;
+ fstype_t *fstype;
+ fsinfo_t fsoptions;
+ fsnode *root;
+ int ch, len;
+ char *specfile;
+
+ setprogname(argv[0]);
+
+ debug = 0;
+ if ((fstype = get_fstype(DEFAULT_FSTYPE)) == NULL)
+ errx(1, "Unknown default fs type `%s'.", DEFAULT_FSTYPE);
+
+ /* set default fsoptions */
+ (void)memset(&fsoptions, 0, sizeof(fsoptions));
+ fsoptions.fd = -1;
+ fsoptions.sectorsize = -1;
+ fsoptions.bsize= -1;
+ fsoptions.fsize= -1;
+ fsoptions.cpg= -1;
+ fsoptions.density= -1;
+ fsoptions.minfree= -1;
+ fsoptions.optimization= -1;
+ fsoptions.maxcontig= -1;
+ fsoptions.maxbpg= -1;
+ fsoptions.avgfilesize= -1;
+ fsoptions.avgfpdir= -1;
+ fsoptions.version = 1;
+
+ specfile = NULL;
+ if (gettimeofday(&start, NULL) == -1)
+ err(1, "Unable to get system time");
+
+ start_time.tv_sec = start.tv_sec;
+ start_time.tv_nsec = start.tv_usec * 1000;
+
+ while ((ch = getopt(argc, argv, "B:b:d:f:F:M:m:N:o:s:S:t:x")) != -1) {
+ switch (ch) {
+
+ case 'B':
+ if (strcmp(optarg, "be") == 0 ||
+ strcmp(optarg, "4321") == 0 ||
+ strcmp(optarg, "big") == 0) {
+#if BYTE_ORDER == LITTLE_ENDIAN
+ fsoptions.needswap = 1;
+#endif
+ } else if (strcmp(optarg, "le") == 0 ||
+ strcmp(optarg, "1234") == 0 ||
+ strcmp(optarg, "little") == 0) {
+#if BYTE_ORDER == BIG_ENDIAN
+ fsoptions.needswap = 1;
+#endif
+ } else {
+ warnx("Invalid endian `%s'.", optarg);
+ usage();
+ }
+ break;
+
+ case 'b':
+ len = strlen(optarg) - 1;
+ if (optarg[len] == '%') {
+ optarg[len] = '\0';
+ fsoptions.freeblockpc =
+ strsuftoll("free block percentage",
+ optarg, 0, 99);
+ } else {
+ fsoptions.freeblocks =
+ strsuftoll("free blocks",
+ optarg, 0, LLONG_MAX);
+ }
+ break;
+
+ case 'd':
+ debug =
+ (int)strsuftoll("debug mask", optarg, 0, UINT_MAX);
+ break;
+
+ case 'f':
+ len = strlen(optarg) - 1;
+ if (optarg[len] == '%') {
+ optarg[len] = '\0';
+ fsoptions.freefilepc =
+ strsuftoll("free file percentage",
+ optarg, 0, 99);
+ } else {
+ fsoptions.freefiles =
+ strsuftoll("free files",
+ optarg, 0, LLONG_MAX);
+ }
+ break;
+
+ case 'F':
+ specfile = optarg;
+ break;
+
+ case 'M':
+ fsoptions.minsize =
+ strsuftoll("minimum size", optarg, 1LL, LLONG_MAX);
+ break;
+
+ case 'N':
+ if (! setup_getid(optarg))
+ errx(1,
+ "Unable to use user and group databases in `%s'",
+ optarg);
+ break;
+
+ case 'm':
+ fsoptions.maxsize =
+ strsuftoll("maximum size", optarg, 1LL, LLONG_MAX);
+ break;
+
+ case 'o':
+ {
+ char *p;
+
+ while ((p = strsep(&optarg, ",")) != NULL) {
+ if (*p == '\0')
+ errx(1, "Empty option");
+ if (! fstype->parse_options(p, &fsoptions))
+ usage();
+ }
+ break;
+ }
+
+ case 's':
+ fsoptions.minsize = fsoptions.maxsize =
+ strsuftoll("size", optarg, 1LL, LLONG_MAX);
+ break;
+
+ case 'S':
+ fsoptions.sectorsize =
+ (int)strsuftoll("sector size", optarg,
+ 1LL, INT_MAX);
+ break;
+
+ case 't':
+ if ((fstype = get_fstype(optarg)) == NULL)
+ errx(1, "Unknown fs type `%s'.", optarg);
+ break;
+
+ case 'x':
+ fsoptions.onlyspec = 1;
+ break;
+
+ case '?':
+ default:
+ usage();
+ /* NOTREACHED */
+
+ }
+ }
+ if (debug) {
+ printf("debug mask: 0x%08x\n", debug);
+ printf("start time: %ld.%ld, %s",
+ (long)start_time.tv_sec, (long)start_time.tv_nsec,
+ ctime(&start_time.tv_sec));
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 2)
+ usage();
+
+ /* -x must be accompanied by -F */
+ if (fsoptions.onlyspec != 0 && specfile == NULL)
+ errx(1, "-x requires -F mtree-specfile.");
+
+ /* walk the tree */
+ TIMER_START(start);
+ root = walk_dir(argv[1], NULL);
+ TIMER_RESULTS(start, "walk_dir");
+
+ if (specfile) { /* apply a specfile */
+ TIMER_START(start);
+ apply_specfile(specfile, argv[1], root);
+ TIMER_RESULTS(start, "apply_specfile");
+ }
+
+ if (debug & DEBUG_DUMP_FSNODES) {
+ printf("\nparent: %s\n", argv[1]);
+ dump_fsnodes(".", root);
+ putchar('\n');
+ }
+
+ /* build the file system */
+ TIMER_START(start);
+ fstype->make_fs(argv[0], argv[1], root, &fsoptions);
+ TIMER_RESULTS(start, "make_fs");
+
+ exit(0);
+ /* NOTREACHED */
+}
+
+
+int
+set_option(option_t *options, const char *var, const char *val)
+{
+ int i;
+
+ for (i = 0; options[i].name != NULL; i++) {
+ if (strcmp(options[i].name, var) != 0)
+ continue;
+ *options[i].value = (int)strsuftoll(options[i].desc, val,
+ options[i].minimum, options[i].maximum);
+ return (1);
+ }
+ warnx("Unknown option `%s'", var);
+ return (0);
+}
+
+
+static fstype_t *
+get_fstype(const char *type)
+{
+ int i;
+
+ for (i = 0; fstypes[i].type != NULL; i++)
+ if (strcmp(fstypes[i].type, type) == 0)
+ return (&fstypes[i]);
+ return (NULL);
+}
+
+static void
+usage(void)
+{
+ const char *prog;
+
+ prog = getprogname();
+ fprintf(stderr,
+"usage: %s [-t fs-type] [-o fs-options] [-d debug-mask] [-B endian]\n"
+"\t[-S sector-size] [-M minimum-size] [-m maximum-size] [-s image-size]\n"
+"\t[-b free-blocks] [-f free-files] [-F mtree-specfile] [-x]\n"
+"\t[-N userdb-dir] image-file directory\n",
+ prog);
+ exit(1);
+}
diff --git a/usr.sbin/makefs/makefs.h b/usr.sbin/makefs/makefs.h
new file mode 100644
index 0000000..ce18bf8
--- /dev/null
+++ b/usr.sbin/makefs/makefs.h
@@ -0,0 +1,305 @@
+/* $NetBSD: makefs.h,v 1.14 2004/06/20 22:20:18 jmc Exp $ */
+
+/*
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _MAKEFS_H
+#define _MAKEFS_H
+
+#include <sys/stat.h>
+#include <err.h>
+
+/*
+ * fsnode -
+ * a component of the tree; contains a filename, a pointer to
+ * fsinode, optional symlink name, and tree pointers
+ *
+ * fsinode -
+ * equivalent to an inode, containing target file system inode number,
+ * refcount (nlink), and stat buffer
+ *
+ * A tree of fsnodes looks like this:
+ *
+ * name "." "bin" "netbsd"
+ * type S_IFDIR S_IFDIR S_IFREG
+ * next > > NULL
+ * parent NULL NULL NULL
+ * child NULL v
+ *
+ * name "." "ls"
+ * type S_IFDIR S_IFREG
+ * next > NULL
+ * parent ^ ^ (to "bin")
+ * child NULL NULL
+ *
+ * Notes:
+ * - first always points to first entry, at current level, which
+ * must be "." when the tree has been built; during build it may
+ * not be if "." hasn't yet been found by readdir(2).
+ */
+
+enum fi_flags {
+ FI_SIZED = 1<<0, /* inode sized */
+ FI_ALLOCATED = 1<<1, /* fsinode->ino allocated */
+ FI_WRITTEN = 1<<2, /* inode written */
+};
+
+typedef struct {
+ uint32_t ino; /* inode number used on target fs */
+ uint32_t nlink; /* number of links to this entry */
+ enum fi_flags flags; /* flags used by fs specific code */
+ struct stat st; /* stat entry */
+} fsinode;
+
+typedef struct _fsnode {
+ struct _fsnode *parent; /* parent (NULL if root) */
+ struct _fsnode *child; /* child (if type == S_IFDIR) */
+ struct _fsnode *next; /* next */
+ struct _fsnode *first; /* first node of current level (".") */
+ uint32_t type; /* type of entry */
+ fsinode *inode; /* actual inode data */
+ char *symlink; /* symlink target */
+ char *name; /* file name */
+ int flags; /* misc flags */
+} fsnode;
+
+#define FSNODE_F_HASSPEC 0x01 /* fsnode has a spec entry */
+
+/*
+ * fsinfo_t - contains various settings and parameters pertaining to
+ * the image, including current settings, global options, and fs
+ * specific options
+ */
+typedef struct {
+ /* current settings */
+ off_t size; /* total size */
+ off_t inodes; /* number of inodes */
+ uint32_t curinode; /* current inode */
+
+ /* image settings */
+ int fd; /* file descriptor of image */
+ void *superblock; /* superblock */
+ int onlyspec; /* only add entries in specfile */
+
+
+ /* global options */
+ off_t minsize; /* minimum size image should be */
+ off_t maxsize; /* maximum size image can be */
+ off_t freefiles; /* free file entries to leave */
+ int freefilepc; /* free file % */
+ off_t freeblocks; /* free blocks to leave */
+ int freeblockpc; /* free block % */
+ int needswap; /* non-zero if byte swapping needed */
+ int sectorsize; /* sector size */
+
+ /* ffs specific options */
+ int bsize; /* block size */
+ int fsize; /* fragment size */
+ int cpg; /* cylinders per group */
+ int cpgflg; /* cpg was specified by user */
+ int density; /* bytes per inode */
+ int ntracks; /* number of tracks */
+ int nsectors; /* number of sectors */
+ int rpm; /* rpm */
+ int minfree; /* free space threshold */
+ int optimization; /* optimization (space or time) */
+ int maxcontig; /* max contiguous blocks to allocate */
+ int rotdelay; /* rotational delay between blocks */
+ int maxbpg; /* maximum blocks per file in a cyl group */
+ int nrpos; /* # of distinguished rotational positions */
+ int avgfilesize; /* expected average file size */
+ int avgfpdir; /* expected # of files per directory */
+ int version; /* filesystem version (1 = FFS, 2 = UFS2) */
+ int maxbsize; /* maximum extent size */
+ int maxblkspercg; /* max # of blocks per cylinder group */
+ /* XXX: support `old' file systems ? */
+} fsinfo_t;
+
+
+/*
+ * option_t - contains option name, description, pointer to location to store
+ * result, and range checks for the result. Used to simplify fs specific
+ * option setting
+ */
+typedef struct {
+ const char *name; /* option name */
+ int *value; /* where to stuff the value */
+ int minimum; /* minimum for value */
+ int maximum; /* maximum for value */
+ const char *desc; /* option description */
+} option_t;
+
+
+void apply_specfile(const char *, const char *, fsnode *);
+void dump_fsnodes(const char *, fsnode *);
+const char * inode_type(mode_t);
+int set_option(option_t *, const char *, const char *);
+fsnode * walk_dir(const char *, fsnode *);
+
+int ffs_parse_opts(const char *, fsinfo_t *);
+void ffs_makefs(const char *, const char *, fsnode *, fsinfo_t *);
+
+
+
+extern u_int debug;
+extern struct timespec start_time;
+
+/*
+ * If -x is specified, we want to exclude nodes which do not appear
+ * in the spec file.
+ */
+#define FSNODE_EXCLUDE_P(opts, fsnode) \
+ ((opts)->onlyspec != 0 && ((fsnode)->flags & FSNODE_F_HASSPEC) == 0)
+
+#define DEBUG_TIME 0x00000001
+ /* debug bits 1..3 unused at this time */
+#define DEBUG_WALK_DIR 0x00000010
+#define DEBUG_WALK_DIR_NODE 0x00000020
+#define DEBUG_WALK_DIR_LINKCHECK 0x00000040
+#define DEBUG_DUMP_FSNODES 0x00000080
+#define DEBUG_DUMP_FSNODES_VERBOSE 0x00000100
+#define DEBUG_FS_PARSE_OPTS 0x00000200
+#define DEBUG_FS_MAKEFS 0x00000400
+#define DEBUG_FS_VALIDATE 0x00000800
+#define DEBUG_FS_CREATE_IMAGE 0x00001000
+#define DEBUG_FS_SIZE_DIR 0x00002000
+#define DEBUG_FS_SIZE_DIR_NODE 0x00004000
+#define DEBUG_FS_SIZE_DIR_ADD_DIRENT 0x00008000
+#define DEBUG_FS_POPULATE 0x00010000
+#define DEBUG_FS_POPULATE_DIRBUF 0x00020000
+#define DEBUG_FS_POPULATE_NODE 0x00040000
+#define DEBUG_FS_WRITE_FILE 0x00080000
+#define DEBUG_FS_WRITE_FILE_BLOCK 0x00100000
+#define DEBUG_FS_MAKE_DIRBUF 0x00200000
+#define DEBUG_FS_WRITE_INODE 0x00400000
+#define DEBUG_BUF_BREAD 0x00800000
+#define DEBUG_BUF_BWRITE 0x01000000
+#define DEBUG_BUF_GETBLK 0x02000000
+#define DEBUG_APPLY_SPECFILE 0x04000000
+#define DEBUG_APPLY_SPECENTRY 0x08000000
+
+
+#define TIMER_START(x) \
+ if (debug & DEBUG_TIME) \
+ gettimeofday(&(x), NULL)
+
+#define TIMER_RESULTS(x,d) \
+ if (debug & DEBUG_TIME) { \
+ struct timeval end, td; \
+ gettimeofday(&end, NULL); \
+ timersub(&end, &(x), &td); \
+ printf("%s took %ld.%06ld seconds\n", \
+ (d), (long) td.tv_sec, (long) td.tv_usec); \
+ }
+
+
+#ifndef DEFAULT_FSTYPE
+#define DEFAULT_FSTYPE "ffs"
+#endif
+
+
+/*
+ * ffs specific settings
+ * ---------------------
+ */
+
+#define FFS_EI /* for opposite endian support in ffs headers */
+
+/*
+ * Write-arounds/compat shims for endian-agnostic support.
+ * These belong in the kernel if/when it's possible to mount
+ * filesystems w/ either byte order.
+ */
+
+/*
+ * File system internal flags, also in fs_flags.
+ * (Pick highest number to avoid conflicts with others)
+ */
+#define FS_SWAPPED 0x80000000 /* file system is endian swapped */
+#define FS_INTERNAL 0x80000000 /* mask for internal flags */
+
+#define FS_ISCLEAN 1
+
+#define DINODE1_SIZE (sizeof(struct ufs1_dinode))
+#define DINODE2_SIZE (sizeof(struct ufs2_dinode))
+
+#define MAXSYMLINKLEN_UFS1 ((NDADDR + NIADDR) * sizeof(ufs1_daddr_t))
+#define MAXSYMLINKLEN_UFS2 ((NDADDR + NIADDR) * sizeof(ufs2_daddr_t))
+
+#if (BYTE_ORDER == LITTLE_ENDIAN)
+#define DIRSIZ_SWAP(oldfmt, dp, needswap) \
+ (((oldfmt) && !(needswap)) ? \
+ DIRECTSIZ((dp)->d_type) : DIRECTSIZ((dp)->d_namlen))
+#else
+#define DIRSIZ_SWAP(oldfmt, dp, needswap) \
+ (((oldfmt) && (needswap)) ? \
+ DIRECTSIZ((dp)->d_type) : DIRECTSIZ((dp)->d_namlen))
+#endif
+
+#define cg_chkmagic_swap(cgp, ns) \
+ (ufs_rw32((cgp)->cg_magic, (ns)) == CG_MAGIC)
+#define cg_inosused_swap(cgp, ns) \
+ ((u_int8_t *)((u_int8_t *)(cgp) + ufs_rw32((cgp)->cg_iusedoff, (ns))))
+#define cg_blksfree_swap(cgp, ns) \
+ ((u_int8_t *)((u_int8_t *)(cgp) + ufs_rw32((cgp)->cg_freeoff, (ns))))
+#define cg_clustersfree_swap(cgp, ns) \
+ ((u_int8_t *)((u_int8_t *)(cgp) + ufs_rw32((cgp)->cg_clusteroff, (ns))))
+#define cg_clustersum_swap(cgp, ns) \
+ ((int32_t *)((uintptr_t)(cgp) + ufs_rw32((cgp)->cg_clustersumoff, ns)))
+
+struct fs;
+void ffs_fragacct_swap(struct fs *, int, int32_t [], int, int);
+
+/*
+ * Declarations for compat routines.
+ */
+long long strsuftoll(const char *, const char *, long long, long long);
+long long strsuftollx(const char *, const char *,
+ long long, long long, char *, size_t);
+
+struct passwd;
+int uid_from_user(const char *, uid_t *);
+int pwcache_userdb(int (*)(int), void (*)(void),
+ struct passwd * (*)(const char *), struct passwd * (*)(uid_t));
+struct group;
+int gid_from_group(const char *, gid_t *);
+int pwcache_groupdb(int (*)(int), void (*)(void),
+ struct group * (*)(const char *), struct group * (*)(gid_t));
+
+int setup_getid(const char *dir);
+
+#endif /* _MAKEFS_H */
diff --git a/usr.sbin/makefs/walk.c b/usr.sbin/makefs/walk.c
new file mode 100644
index 0000000..e485787
--- /dev/null
+++ b/usr.sbin/makefs/walk.c
@@ -0,0 +1,568 @@
+/* $NetBSD: walk.c,v 1.17 2004/06/20 22:20:18 jmc Exp $ */
+
+/*
+ * Copyright (c) 2001 Wasabi Systems, Inc.
+ * All rights reserved.
+ *
+ * Written by Luke Mewburn 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.
+ */
+
+/*
+ * The function link_check() was inspired from NetBSD's usr.bin/du/du.c,
+ * which has the following copyright notice:
+ *
+ *
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Chris Newcomb.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "makefs.h"
+
+#include "mtree.h"
+#include "extern.h" /* NB: mtree */
+
+static void apply_specdir(const char *, NODE *, fsnode *);
+static void apply_specentry(const char *, NODE *, fsnode *);
+static fsnode *create_fsnode(const char *, struct stat *);
+static fsinode *link_check(fsinode *);
+
+
+/*
+ * walk_dir --
+ * build a tree of fsnodes from `dir', with a parent fsnode of `parent'
+ * (which may be NULL for the root of the tree).
+ * each "level" is a directory, with the "." entry guaranteed to be
+ * at the start of the list, and without ".." entries.
+ */
+fsnode *
+walk_dir(const char *dir, fsnode *parent)
+{
+ fsnode *first, *cur, *prev;
+ DIR *dirp;
+ struct dirent *dent;
+ char path[MAXPATHLEN + 1];
+ struct stat stbuf;
+
+ assert(dir != NULL);
+
+ if (debug & DEBUG_WALK_DIR)
+ printf("walk_dir: %s %p\n", dir, parent);
+ if ((dirp = opendir(dir)) == NULL)
+ err(1, "Can't opendir `%s'", dir);
+ first = prev = NULL;
+ while ((dent = readdir(dirp)) != NULL) {
+ if (strcmp(dent->d_name, "..") == 0)
+ continue;
+ if (debug & DEBUG_WALK_DIR_NODE)
+ printf("scanning %s/%s\n", dir, dent->d_name);
+ if (snprintf(path, sizeof(path), "%s/%s", dir, dent->d_name)
+ >= sizeof(path))
+ errx(1, "Pathname too long.");
+ if (lstat(path, &stbuf) == -1)
+ err(1, "Can't lstat `%s'", path);
+#ifdef S_ISSOCK
+ if (S_ISSOCK(stbuf.st_mode & S_IFMT)) {
+ if (debug & DEBUG_WALK_DIR_NODE)
+ printf(" skipping socket %s\n", path);
+ continue;
+ }
+#endif
+
+ cur = create_fsnode(dent->d_name, &stbuf);
+ cur->parent = parent;
+ if (strcmp(dent->d_name, ".") == 0) {
+ /* ensure "." is at the start of the list */
+ cur->next = first;
+ first = cur;
+ if (! prev)
+ prev = cur;
+ } else { /* not "." */
+ if (prev)
+ prev->next = cur;
+ prev = cur;
+ if (!first)
+ first = cur;
+ if (S_ISDIR(cur->type)) {
+ cur->child = walk_dir(path, cur);
+ continue;
+ }
+ }
+ if (stbuf.st_nlink > 1) {
+ fsinode *curino;
+
+ curino = link_check(cur->inode);
+ if (curino != NULL) {
+ free(cur->inode);
+ cur->inode = curino;
+ cur->inode->nlink++;
+ }
+ }
+ if (S_ISLNK(cur->type)) {
+ char slink[PATH_MAX+1];
+ int llen;
+
+ llen = readlink(path, slink, sizeof(slink) - 1);
+ if (llen == -1)
+ err(1, "Readlink `%s'", path);
+ slink[llen] = '\0';
+ if ((cur->symlink = strdup(slink)) == NULL)
+ err(1, "Memory allocation error");
+ }
+ }
+ for (cur = first; cur != NULL; cur = cur->next)
+ cur->first = first;
+ if (closedir(dirp) == -1)
+ err(1, "Can't closedir `%s'", dir);
+ return (first);
+}
+
+static fsnode *
+create_fsnode(const char *name, struct stat *stbuf)
+{
+ fsnode *cur;
+
+ if ((cur = calloc(1, sizeof(fsnode))) == NULL ||
+ (cur->name = strdup(name)) == NULL ||
+ (cur->inode = calloc(1, sizeof(fsinode))) == NULL)
+ err(1, "Memory allocation error");
+ cur->type = stbuf->st_mode & S_IFMT;
+ cur->inode->nlink = 1;
+ cur->inode->st = *stbuf;
+ return (cur);
+}
+
+/*
+ * apply_specfile --
+ * read in the mtree(8) specfile, and apply it to the tree
+ * at dir,parent. parameters in parent on equivalent types
+ * will be changed to those found in specfile, and missing
+ * entries will be added.
+ */
+void
+apply_specfile(const char *specfile, const char *dir, fsnode *parent)
+{
+ struct timeval start;
+ FILE *fp;
+ NODE *root;
+
+ assert(specfile != NULL);
+ assert(parent != NULL);
+
+ if (debug & DEBUG_APPLY_SPECFILE)
+ printf("apply_specfile: %s, %s %p\n", specfile, dir, parent);
+
+ /* read in the specfile */
+ if ((fp = fopen(specfile, "r")) == NULL)
+ err(1, "Can't open `%s'", specfile);
+ TIMER_START(start);
+ root = mtree_readspec(fp);
+ TIMER_RESULTS(start, "spec");
+ if (fclose(fp) == EOF)
+ err(1, "Can't close `%s'", specfile);
+
+ /* perform some sanity checks */
+ if (root == NULL)
+ errx(1, "Specfile `%s' did not contain a tree", specfile);
+ assert(strcmp(root->name, ".") == 0);
+ assert(root->type == F_DIR);
+
+ /* merge in the changes */
+ apply_specdir(dir, root, parent);
+}
+
+static u_int
+nodetoino(u_int type)
+{
+
+ switch (type) {
+ case F_BLOCK:
+ return S_IFBLK;
+ case F_CHAR:
+ return S_IFCHR;
+ case F_DIR:
+ return S_IFDIR;
+ case F_FIFO:
+ return S_IFIFO;
+ case F_FILE:
+ return S_IFREG;
+ case F_LINK:
+ return S_IFLNK;
+ case F_SOCK:
+ return S_IFSOCK;
+ default:
+ printf("unknown type %d", type);
+ abort();
+ }
+ /* NOTREACHED */
+}
+
+static void
+apply_specdir(const char *dir, NODE *specnode, fsnode *dirnode)
+{
+ char path[MAXPATHLEN + 1];
+ NODE *curnode;
+ fsnode *curfsnode;
+
+ assert(specnode != NULL);
+ assert(dirnode != NULL);
+
+ if (debug & DEBUG_APPLY_SPECFILE)
+ printf("apply_specdir: %s %p %p\n", dir, specnode, dirnode);
+
+ if (specnode->type != F_DIR)
+ errx(1, "Specfile node `%s/%s' is not a directory",
+ dir, specnode->name);
+ if (dirnode->type != S_IFDIR)
+ errx(1, "Directory node `%s/%s' is not a directory",
+ dir, dirnode->name);
+
+ apply_specentry(dir, specnode, dirnode);
+
+ /* now walk specnode->child matching up with dirnode */
+ for (curnode = specnode->child; curnode != NULL;
+ curnode = curnode->next) {
+ if (debug & DEBUG_APPLY_SPECENTRY)
+ printf("apply_specdir: spec %s\n",
+ curnode->name);
+ for (curfsnode = dirnode->next; curfsnode != NULL;
+ curfsnode = curfsnode->next) {
+#if 0 /* too verbose for now */
+ if (debug & DEBUG_APPLY_SPECENTRY)
+ printf("apply_specdir: dirent %s\n",
+ curfsnode->name);
+#endif
+ if (strcmp(curnode->name, curfsnode->name) == 0)
+ break;
+ }
+ if (snprintf(path, sizeof(path), "%s/%s",
+ dir, curnode->name) >= sizeof(path))
+ errx(1, "Pathname too long.");
+ if (curfsnode == NULL) { /* need new entry */
+ struct stat stbuf;
+
+ /*
+ * don't add optional spec entries
+ * that lack an existing fs entry
+ */
+ if ((curnode->flags & F_OPT) &&
+ lstat(path, &stbuf) == -1)
+ continue;
+
+ /* check that enough info is provided */
+#define NODETEST(t, m) \
+ if (!(t)) \
+ errx(1, "`%s': %s not provided", path, m)
+ NODETEST(curnode->flags & F_TYPE, "type");
+ NODETEST(curnode->flags & F_MODE, "mode");
+ /* XXX: require F_TIME ? */
+ NODETEST(curnode->flags & F_GID ||
+ curnode->flags & F_GNAME, "group");
+ NODETEST(curnode->flags & F_UID ||
+ curnode->flags & F_UNAME, "user");
+#undef NODETEST
+
+ if (debug & DEBUG_APPLY_SPECFILE)
+ printf("apply_specdir: adding %s\n",
+ curnode->name);
+ /* build minimal fsnode */
+ memset(&stbuf, 0, sizeof(stbuf));
+ stbuf.st_mode = nodetoino(curnode->type);
+ stbuf.st_nlink = 1;
+ stbuf.st_mtime = stbuf.st_atime =
+ stbuf.st_ctime = start_time.tv_sec;
+#if HAVE_STRUCT_STAT_ST_MTIMENSEC
+ stbuf.st_mtimensec = stbuf.st_atimensec =
+ stbuf.st_ctimensec = start_time.tv_nsec;
+#endif
+ curfsnode = create_fsnode(curnode->name, &stbuf);
+ curfsnode->parent = dirnode->parent;
+ curfsnode->first = dirnode;
+ curfsnode->next = dirnode->next;
+ dirnode->next = curfsnode;
+ if (curfsnode->type == S_IFDIR) {
+ /* for dirs, make "." entry as well */
+ curfsnode->child = create_fsnode(".", &stbuf);
+ curfsnode->child->parent = curfsnode;
+ curfsnode->child->first = curfsnode->child;
+ }
+ if (curfsnode->type == S_IFLNK) {
+ assert(curnode->slink != NULL);
+ /* for symlinks, copy the target */
+ if ((curfsnode->symlink =
+ strdup(curnode->slink)) == NULL)
+ err(1, "Memory allocation error");
+ }
+ }
+ apply_specentry(dir, curnode, curfsnode);
+ if (curnode->type == F_DIR) {
+ if (curfsnode->type != S_IFDIR)
+ errx(1, "`%s' is not a directory", path);
+ assert (curfsnode->child != NULL);
+ apply_specdir(path, curnode, curfsnode->child);
+ }
+ }
+}
+
+static void
+apply_specentry(const char *dir, NODE *specnode, fsnode *dirnode)
+{
+
+ assert(specnode != NULL);
+ assert(dirnode != NULL);
+
+ if (nodetoino(specnode->type) != dirnode->type)
+ errx(1, "`%s/%s' type mismatch: specfile %s, tree %s",
+ dir, specnode->name, inode_type(nodetoino(specnode->type)),
+ inode_type(dirnode->type));
+
+ if (debug & DEBUG_APPLY_SPECENTRY)
+ printf("apply_specentry: %s/%s\n", dir, dirnode->name);
+
+#define ASEPRINT(t, b, o, n) \
+ if (debug & DEBUG_APPLY_SPECENTRY) \
+ printf("\t\t\tchanging %s from " b " to " b "\n", \
+ t, o, n)
+
+ if (specnode->flags & (F_GID | F_GNAME)) {
+ ASEPRINT("gid", "%d",
+ dirnode->inode->st.st_gid, specnode->st_gid);
+ dirnode->inode->st.st_gid = specnode->st_gid;
+ }
+ if (specnode->flags & F_MODE) {
+ ASEPRINT("mode", "%#o",
+ dirnode->inode->st.st_mode & ALLPERMS, specnode->st_mode);
+ dirnode->inode->st.st_mode &= ~ALLPERMS;
+ dirnode->inode->st.st_mode |= (specnode->st_mode & ALLPERMS);
+ }
+ /* XXX: ignoring F_NLINK for now */
+ if (specnode->flags & F_SIZE) {
+ ASEPRINT("size", "%lld",
+ (long long)dirnode->inode->st.st_size,
+ (long long)specnode->st_size);
+ dirnode->inode->st.st_size = specnode->st_size;
+ }
+ if (specnode->flags & F_SLINK) {
+ assert(dirnode->symlink != NULL);
+ assert(specnode->slink != NULL);
+ ASEPRINT("symlink", "%s", dirnode->symlink, specnode->slink);
+ free(dirnode->symlink);
+ if ((dirnode->symlink = strdup(specnode->slink)) == NULL)
+ err(1, "Memory allocation error");
+ }
+ if (specnode->flags & F_TIME) {
+ ASEPRINT("time", "%ld",
+ (long)dirnode->inode->st.st_mtime,
+ (long)specnode->st_mtimespec.tv_sec);
+ dirnode->inode->st.st_mtime = specnode->st_mtimespec.tv_sec;
+ dirnode->inode->st.st_atime = specnode->st_mtimespec.tv_sec;
+ dirnode->inode->st.st_ctime = start_time.tv_sec;
+#if HAVE_STRUCT_STAT_ST_MTIMENSEC
+ dirnode->inode->st.st_mtimensec = specnode->st_mtimespec.tv_nsec;
+ dirnode->inode->st.st_atimensec = specnode->st_mtimespec.tv_nsec;
+ dirnode->inode->st.st_ctimensec = start_time.tv_nsec;
+#endif
+ }
+ if (specnode->flags & (F_UID | F_UNAME)) {
+ ASEPRINT("uid", "%d",
+ dirnode->inode->st.st_uid, specnode->st_uid);
+ dirnode->inode->st.st_uid = specnode->st_uid;
+ }
+#if HAVE_STRUCT_STAT_ST_FLAGS
+ if (specnode->flags & F_FLAGS) {
+ ASEPRINT("flags", "%#lX",
+ (unsigned long)dirnode->inode->st.st_flags,
+ (unsigned long)specnode->st_flags);
+ dirnode->inode->st.st_flags = specnode->st_flags;
+ }
+#endif
+#undef ASEPRINT
+
+ dirnode->flags |= FSNODE_F_HASSPEC;
+}
+
+
+/*
+ * dump_fsnodes --
+ * dump the fsnodes from `cur', based in the directory `dir'
+ */
+void
+dump_fsnodes(const char *dir, fsnode *root)
+{
+ fsnode *cur;
+ char path[MAXPATHLEN + 1];
+
+ assert (dir != NULL);
+ printf("dump_fsnodes: %s %p\n", dir, root);
+ for (cur = root; cur != NULL; cur = cur->next) {
+ if (snprintf(path, sizeof(path), "%s/%s", dir, cur->name)
+ >= sizeof(path))
+ errx(1, "Pathname too long.");
+
+ if (debug & DEBUG_DUMP_FSNODES_VERBOSE)
+ printf("cur=%8p parent=%8p first=%8p ",
+ cur, cur->parent, cur->first);
+ printf("%7s: %s", inode_type(cur->type), path);
+ if (S_ISLNK(cur->type)) {
+ assert(cur->symlink != NULL);
+ printf(" -> %s", cur->symlink);
+ } else {
+ assert (cur->symlink == NULL);
+ }
+ if (cur->inode->nlink > 1)
+ printf(", nlinks=%d", cur->inode->nlink);
+ putchar('\n');
+
+ if (cur->child) {
+ assert (cur->type == S_IFDIR);
+ dump_fsnodes(path, cur->child);
+ }
+ }
+ printf("dump_fsnodes: finished %s\n", dir);
+}
+
+
+/*
+ * inode_type --
+ * for a given inode type `mode', return a descriptive string.
+ */
+const char *
+inode_type(mode_t mode)
+{
+
+ if (S_ISREG(mode))
+ return ("file");
+ if (S_ISLNK(mode))
+ return ("symlink");
+ if (S_ISDIR(mode))
+ return ("dir");
+ if (S_ISLNK(mode))
+ return ("link");
+ if (S_ISFIFO(mode))
+ return ("fifo");
+ if (S_ISSOCK(mode))
+ return ("socket");
+ /* XXX should not happen but handle them */
+ if (S_ISCHR(mode))
+ return ("char");
+ if (S_ISBLK(mode))
+ return ("block");
+ return ("unknown");
+}
+
+
+/*
+ * link_check --
+ * return pointer to fsnode matching `entry's st_ino & st_dev if it exists,
+ * otherwise add `entry' to table and return NULL
+ */
+static fsinode *
+link_check(fsinode *entry)
+{
+ static struct dupnode {
+ uint32_t dev;
+ uint64_t ino;
+ fsinode *dup;
+ } *dups, *newdups;
+ static int ndups, maxdups;
+
+ int i;
+
+ assert (entry != NULL);
+
+ /* XXX; maybe traverse in reverse for speed? */
+ for (i = 0; i < ndups; i++) {
+ if (dups[i].dev == entry->st.st_dev &&
+ dups[i].ino == entry->st.st_ino) {
+ if (debug & DEBUG_WALK_DIR_LINKCHECK)
+ printf("link_check: found [%d,%d]\n",
+ entry->st.st_dev, entry->st.st_ino);
+ return (dups[i].dup);
+ }
+ }
+
+ if (debug & DEBUG_WALK_DIR_LINKCHECK)
+ printf("link_check: no match for [%d, %d]\n",
+ entry->st.st_dev, entry->st.st_ino);
+ if (ndups == maxdups) {
+ if ((newdups = realloc(dups, sizeof(struct dupnode) * (maxdups + 128)))
+ == NULL)
+ err(1, "Memory allocation error");
+ dups = newdups;
+ maxdups += 128;
+ }
+ dups[ndups].dev = entry->st.st_dev;
+ dups[ndups].ino = entry->st.st_ino;
+ dups[ndups].dup = entry;
+ ndups++;
+
+ return (NULL);
+}
diff --git a/usr.sbin/makemap/Makefile b/usr.sbin/makemap/Makefile
new file mode 100644
index 0000000..5d30c67
--- /dev/null
+++ b/usr.sbin/makemap/Makefile
@@ -0,0 +1,38 @@
+# @(#)Makefile 8.4 (Berkeley) 6/10/97
+# $FreeBSD$
+
+SENDMAIL_DIR=${.CURDIR}/../../contrib/sendmail
+.PATH: ${SENDMAIL_DIR}/makemap
+
+PROG= makemap
+SRCS= makemap.c
+MAN= makemap.8
+
+CFLAGS+= -I${SENDMAIL_DIR}/src -I${SENDMAIL_DIR}/include -I.
+CFLAGS+= -DNEWDB -DNOT_SENDMAIL
+
+LIBSMDIR= ${.OBJDIR}/../../lib/libsm
+LIBSM= ${LIBSMDIR}/libsm.a
+
+LIBSMDBDIR= ${.OBJDIR}/../../lib/libsmdb
+LIBSMDB= ${LIBSMDBDIR}/libsmdb.a
+
+LIBSMUTILDIR= ${.OBJDIR}/../../lib/libsmutil
+LIBSMUTIL= ${LIBSMUTILDIR}/libsmutil.a
+
+DPADD= ${LIBSMDB} ${LIBSMUTIL} ${LIBSM}
+LDADD= ${LIBSMDB} ${LIBSMUTIL} ${LIBSM}
+
+SRCS+= sm_os.h
+CLEANFILES+=sm_os.h
+
+# User customizations to the sendmail build environment
+CFLAGS+=${SENDMAIL_CFLAGS}
+DPADD+=${SENDMAIL_DPADD}
+LDADD+=${SENDMAIL_LDADD}
+LDFLAGS+=${SENDMAIL_LDFLAGS}
+
+sm_os.h:
+ ln -sf ${SENDMAIL_DIR}/include/sm/os/sm_os_freebsd.h sm_os.h
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/manctl/Makefile b/usr.sbin/manctl/Makefile
new file mode 100644
index 0000000..9e0c12e
--- /dev/null
+++ b/usr.sbin/manctl/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+SCRIPTS=manctl.sh
+MAN= manctl.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/manctl/manctl.8 b/usr.sbin/manctl/manctl.8
new file mode 100644
index 0000000..b8e001d
--- /dev/null
+++ b/usr.sbin/manctl/manctl.8
@@ -0,0 +1,58 @@
+.\" Copyright (c) 1996 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.Dd January 1, 1996
+.Dt MANCTL 8
+.Os
+.Sh NAME
+.Nm manctl
+.Nd manipulating manual pages
+.Sh SYNOPSIS
+.Nm
+.Op Fl compress
+.Op Fl uncompress
+.Op Fl purge
+.Op Fl help
+.Ar path ...
+.Sh DESCRIPTION
+The
+.Nm
+utility compress or uncompress manual pages in directory path.
+If possible, .so's are replaced with hard links.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl help
+Print options and exit.
+.It Fl compress
+Compress uncompressed man pages (eliminating .so's).
+.It Fl uncompress
+Uncompress compressed man pages.
+.It Fl purge
+Purge old formatted man pages (not implemented yet).
+.El
+.Sh SEE ALSO
+.Xr catman 1 ,
+.Xr man 1
diff --git a/usr.sbin/manctl/manctl.sh b/usr.sbin/manctl/manctl.sh
new file mode 100644
index 0000000..23e2087
--- /dev/null
+++ b/usr.sbin/manctl/manctl.sh
@@ -0,0 +1,380 @@
+#!/bin/sh
+#
+# Copyright (c) 1994 Geoffrey M. Rehmet, Rhodes University
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must 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 Geoffrey M. Rehmet
+# 4. Neither the name of Geoffrey M. Rehmet nor that of Rhodes University
+# 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 GEOFFREY M. REHMET OR RHODES 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.
+#
+# $FreeBSD$
+#
+# manctl:
+# a utility for manipulating manual pages
+# functions:
+# compress uncompressed man pages (elliminating .so's)
+# this is now two-pass. If possible, .so's
+# are replaced with hard links
+# uncompress compressed man pages
+# purge old formatted man pages (not implemented yet)
+# Things to watch out for:
+# Hard links - careful with g(un)zipping!
+# .so's - throw everything through soelim before gzip!
+# symlinks - ignore these - eg: expn is its own man page:
+# don't want to compress this!
+#
+PATH=/bin:/sbin:/usr/bin:/usr/sbin; export PATH
+
+#
+# purge cat? directories
+#
+do_purge()
+{
+ echo "purge $@" 2>&1
+ echo "not implemented yet\n" 2>&1
+}
+
+
+#
+# Uncompress one page
+#
+uncompress_page()
+{
+ local pname
+ local fname
+ local sect
+ local ext
+
+ # break up file name
+ pname=$1
+ IFS='.' ; set $pname
+ # less than 3 fields - don't know what to do with this
+ if [ $# -lt 3 ] ; then
+ IFS=" " ; echo ignoring $pname 1>&2 ; return 0 ;
+ fi
+ # construct name and section
+ fname=$1 ; shift
+ while [ $# -gt 2 ] ; do
+ fname=$fname.$1
+ shift
+ done
+ sect=$1
+ ext=$2
+
+ IFS=" "
+ case "$ext" in
+ gz|Z) {
+ IFS=" " ; set `file $pname`
+ if [ $2 != "gzip" ] ; then
+ echo moving hard link $pname 1>&2
+ mv $pname $fname.$ext # link
+ else
+ if [ $2 != "symbolic" ] ; then
+ echo gunzipping page $pname 1>&2
+ temp=`mktemp -t manager` || exit 1
+ gunzip -c $pname > $temp
+ chmod u+w $pname
+ cp $temp $pname
+ chmod 444 $pname
+ mv $pname $fname.$sect
+ rm -f $temp
+ else
+ # skip symlinks - this can be
+ # a program like expn, which is
+ # its own man page !
+ echo skipping symlink $pname 1>&2
+ fi
+ fi };;
+ *) {
+ IFS=" "
+ echo skipping file $pname 1>&2
+ } ;;
+ esac
+ # reset IFS - this is important!
+ IFS=" "
+}
+
+
+#
+# Uncompress manpages in paths
+#
+do_uncompress()
+{
+ local i
+ local dir
+ local workdir
+
+ workdir=`pwd`
+ while [ $# != 0 ] ; do
+ if [ -d $1 ] ; then
+ dir=$1
+ cd $dir
+ for i in * ; do
+ case $i in
+ *cat?) ;; # ignore cat directories
+ *) {
+ if [ -d $i ] ; then
+ do_uncompress $i
+ else
+ if [ -e $i ] ; then
+ uncompress_page $i
+ fi
+ fi } ;;
+ esac
+ done
+ cd $workdir
+ else
+ echo "directory $1 not found" 1>&2
+ fi
+ shift
+ done
+}
+
+#
+# Remove .so's from one file
+#
+so_purge_page()
+{
+ local so_entries
+ local lines
+ local fname
+
+ so_entries=`grep "^\.so" $1 | wc -l`
+ if [ $so_entries -eq 0 ] ; then return 0 ; fi
+
+ # we have a page with a .so in it
+ echo $1 contains a .so entry 2>&1
+
+ # now check how many lines in the file
+ lines=`wc -l < $1`
+
+ # if the file is only one line long, we can replace it
+ # with a hard link!
+ if [ $lines -eq 1 ] ; then
+ fname=$1;
+ echo replacing $fname with a hard link
+ set `cat $fname`;
+ rm -f $fname
+ ln ../$2 $fname
+ else
+ echo inlining page $fname 1>&2
+ temp=`mktemp -t manager` || exit 1
+ cat $fname | \
+ (cd .. ; soelim ) > $temp
+ chmod u+w $fname
+ cp $temp $fname
+ chmod 444 $fname
+ rm -f $temp
+ fi
+}
+
+#
+# Remove .so entries from man pages
+# If a page consists of just one line with a .so,
+# replace it with a hard link
+#
+remove_so()
+{
+ local pname
+ local fname
+ local sect
+
+ # break up file name
+ pname=$1
+ IFS='.' ; set $pname
+ if [ $# -lt 2 ] ; then
+ IFS=" " ; echo ignoring $pname 1>&2 ; return 0 ;
+ fi
+ # construct name and section
+ fname=$1 ; shift
+ while [ $# -gt 1 ] ; do
+ fname=$fname.$1
+ shift
+ done
+ sect=$1
+
+ IFS=" "
+ case "$sect" in
+ gz) { echo file $pname already gzipped 1>&2 ; } ;;
+ Z) { echo file $pname already compressed 1>&2 ; } ;;
+ [12345678ln]*){
+ IFS=" " ; set `file $pname`
+ if [ $2 = "gzip" ] ; then
+ echo moving hard link $pname 1>&2
+ mv $pname $pname.gz # link
+ else
+ if [ $2 != "symbolic" ] ; then
+ echo "removing .so's in page $pname" 1>&2
+ so_purge_page $pname
+ else
+ # skip symlink - this can be
+ # a program like expn, which is
+ # its own man page !
+ echo skipping symlink $pname 1>&2
+ fi
+ fi };;
+ *) {
+ IFS=" "
+ echo skipping file $pname 1>&2
+ } ;;
+ esac
+ # reset IFS - this is important!
+ IFS=" "
+}
+
+
+#
+# compress one page
+# We need to watch out for hard links here.
+#
+compress_page()
+{
+ local pname
+ local fname
+ local sect
+
+ # break up file name
+ pname=$1
+ IFS='.' ; set $pname
+ if [ $# -lt 2 ] ; then
+ IFS=" " ; echo ignoring $pname 1>&2 ; return 0 ;
+ fi
+ # construct name and section
+ fname=$1 ; shift
+ while [ $# -gt 1 ] ; do
+ fname=$fname.$1
+ shift
+ done
+ sect=$1
+
+ IFS=" "
+ case "$sect" in
+ gz) { echo file $pname already gzipped 1>&2 ; } ;;
+ Z) { echo file $pname already compressed 1>&2 ; } ;;
+ [12345678ln]*){
+ IFS=" " ; set `file $pname`
+ if [ $2 = "gzip" ] ; then
+ echo moving hard link $pname 1>&2
+ mv $pname $pname.gz # link
+ else
+ if [ $2 != "symbolic" ] ; then
+ echo gzipping page $pname 1>&2
+ temp=`mktemp -t manager` || exit 1
+ cat $pname | \
+ (cd .. ; soelim )| gzip -c -- > $temp
+ chmod u+w $pname
+ cp $temp $pname
+ chmod 444 $pname
+ mv $pname $pname.gz
+ rm -f $temp
+ else
+ # skip symlink - this can be
+ # a program like expn, which is
+ # its own man page !
+ echo skipping symlink $pname 1>&2
+ fi
+ fi };;
+ *) {
+ IFS=" "
+ echo skipping file $pname 1>&2
+ } ;;
+ esac
+ # reset IFS - this is important!
+ IFS=" "
+}
+
+#
+# Compress man pages in paths
+#
+do_compress_so()
+{
+ local i
+ local dir
+ local workdir
+ local what
+
+ what=$1
+ shift
+ workdir=`pwd`
+ while [ $# != 0 ] ; do
+ if [ -d $1 ] ; then
+ dir=$1
+ cd $dir
+ for i in * ; do
+ case $i in
+ *cat?) ;; # ignore cat directories
+ *) {
+ if [ -d $i ] ; then
+ do_compress_so $what $i
+ else
+ if [ -e $i ] ; then
+ $what $i
+ fi
+ fi } ;;
+ esac
+ done
+ cd $workdir
+ else
+ echo "directory $1 not found" 1>&2
+ fi
+ shift
+ done
+}
+
+#
+# Display a usage message
+#
+ctl_usage()
+{
+ echo "usage: $1 -compress <path> ... " 1>&2
+ echo " $1 -uncompress <path> ... " 1>&2
+ echo " $1 -purge <days> <path> ... " 1>&2
+ echo " $1 -purge expire <path> ... " 1>&2
+ exit 1
+}
+
+#
+# remove .so's and do compress
+#
+do_compress()
+{
+ # First remove all so's from the pages to be compressed
+ do_compress_so remove_so "$@"
+ # now do ahead and compress the pages
+ do_compress_so compress_page "$@"
+}
+
+#
+# dispatch options
+#
+if [ $# -lt 2 ] ; then ctl_usage $0 ; fi ;
+
+case "$1" in
+ -compress) shift ; do_compress "$@" ;;
+ -uncompress) shift ; do_uncompress "$@" ;;
+ -purge) shift ; do_purge "$@" ;;
+ *) ctl_usage $0 ;;
+esac
diff --git a/usr.sbin/memcontrol/Makefile b/usr.sbin/memcontrol/Makefile
new file mode 100644
index 0000000..25009db
--- /dev/null
+++ b/usr.sbin/memcontrol/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+PROG= memcontrol
+MAN= memcontrol.8
+WARNS?= 6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/memcontrol/memcontrol.8 b/usr.sbin/memcontrol/memcontrol.8
new file mode 100644
index 0000000..ad0e533
--- /dev/null
+++ b/usr.sbin/memcontrol/memcontrol.8
@@ -0,0 +1,111 @@
+.\" Copyright (c) 1999 Chris Costello
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 15, 2002
+.Dt MEMCONTROL 8
+.Os
+.Sh NAME
+.Nm memcontrol
+.Nd "control system cache behaviour with respect to memory"
+.Sh SYNOPSIS
+.Nm
+.Cm list
+.Op Fl a
+.Nm
+.Cm set
+.Fl b Ar base
+.Fl l Ar length
+.Fl o Ar owner
+.Ar attribute
+.Nm
+.Cm clear
+.Fl o Ar owner
+.Nm
+.Cm clear
+.Fl b Ar base
+.Fl l Ar length
+.Sh DESCRIPTION
+A number of supported system architectures allow the behaviour of the CPU
+cache to be programmed to behave differently depending on the region being
+written.
+.Pp
+The
+.Nm
+utility
+provides an interface to this facility, allowing CPU cache behavior to
+be altered for ranges of system physical memory.
+.Pp
+These ranges are typically power-of-2 aligned and sized, however the specific
+rules governing their layout vary between architectures.
+The
+.Nm
+utility does not attempt to enforce these rules, however the system will
+reject any attempt to set an illegal combination.
+.Bl -tag -width ".Cm clear"
+.It Cm list
+List range slots.
+.Bl -tag -width indent
+.It Fl a
+List all range slots, even those that are inactive.
+.El
+.It Cm set
+Set memory range attributes.
+.Bl -tag -width indent
+.It Fl b Ar base
+Memory range base address.
+.It Fl l Ar length
+Length of memory range in bytes, power of 2.
+.It Fl o Ar owner
+Text identifier for this setting (7 char max).
+.It Ar attribute
+Attributes applied to this range; combinations of
+.Cm force , uncacheable , write-combine , write-through , write-back ,
+and
+.Cm write-protect .
+.El
+.It Cm clear
+Clear memory range attributes.
+Ranges may be cleared by owner or by
+base/length combination.
+.Pp
+To clear based on ownership:
+.Bl -tag -width indent
+.It Fl o Ar owner
+All ranges with this owner will be cleared.
+.El
+.Pp
+To clear based on the base/length combination:
+.Bl -tag -width indent
+.It Fl b Ar base
+Memory range base address.
+.It Fl l Ar length
+Length of memory range in bytes, power of 2.
+.El
+.Pp
+Base and length must exactly match an existing range.
+.El
+.Sh SEE ALSO
+.Xr mem 4
diff --git a/usr.sbin/memcontrol/memcontrol.c b/usr.sbin/memcontrol/memcontrol.c
new file mode 100644
index 0000000..b6cb25f
--- /dev/null
+++ b/usr.sbin/memcontrol/memcontrol.c
@@ -0,0 +1,344 @@
+/*-
+ * Copyright (c) 1999 Michael Smith <msmith@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 <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/memrange.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+struct
+{
+ const char *name;
+ int val;
+ int kind;
+#define MDF_SETTABLE (1<<0)
+} attrnames[] = {
+ {"uncacheable", MDF_UNCACHEABLE, MDF_SETTABLE},
+ {"write-combine", MDF_WRITECOMBINE, MDF_SETTABLE},
+ {"write-through", MDF_WRITETHROUGH, MDF_SETTABLE},
+ {"write-back", MDF_WRITEBACK, MDF_SETTABLE},
+ {"write-protect", MDF_WRITEPROTECT, MDF_SETTABLE},
+ {"force", MDF_FORCE, MDF_SETTABLE},
+ {"unknown", MDF_UNKNOWN, 0},
+ {"fixed-base", MDF_FIXBASE, 0},
+ {"fixed-length", MDF_FIXLEN, 0},
+ {"set-by-firmware", MDF_FIRMWARE, 0},
+ {"active", MDF_ACTIVE, MDF_SETTABLE},
+ {"bogus", MDF_BOGUS, 0},
+ {NULL, 0, 0}
+};
+
+static void listfunc(int memfd, int argc, char *argv[]);
+static void setfunc(int memfd, int argc, char *argv[]);
+static void clearfunc(int memfd, int argc, char *argv[]);
+static void helpfunc(int memfd, int argc, char *argv[]);
+static void help(const char *what);
+
+struct
+{
+ const char *cmd;
+ const char *desc;
+ void (*func)(int memfd, int argc, char *argv[]);
+} functions[] = {
+ {"list",
+ "List current memory range attributes\n"
+ " list [-a]\n"
+ " -a list all range slots, even those that are inactive",
+ listfunc},
+ {"set",
+ "Set memory range attributes\n"
+ " set -b <base> -l <length> -o <owner> <attribute>\n"
+ " <base> memory range base address\n"
+ " <length> length of memory range in bytes, power of 2\n"
+ " <owner> text identifier for this setting (7 char max)\n"
+ " <attribute> attribute(s) to be applied to this range:\n"
+ " uncacheable\n"
+ " write-combine\n"
+ " write-through\n"
+ " write-back\n"
+ " write-protect",
+ setfunc},
+ {"clear",
+ "Clear memory range attributes\n"
+ " clear -o <owner>\n"
+ " <owner> all ranges with this owner will be cleared\n"
+ " clear -b <base> -l <length>\n"
+ " <base> memory range base address\n"
+ " <length> length of memory range in bytes, power of 2\n"
+ " Base and length must exactly match an existing range",
+ clearfunc},
+ {NULL, NULL, helpfunc}
+};
+
+int
+main(int argc, char *argv[])
+{
+ int i, memfd;
+
+ if (argc < 2) {
+ help(NULL);
+ } else {
+ if ((memfd = open(_PATH_MEM, O_RDONLY)) == -1)
+ err(1, "can't open %s", _PATH_MEM);
+
+ for (i = 0; functions[i].cmd != NULL; i++)
+ if (!strcmp(argv[1], functions[i].cmd))
+ break;
+ functions[i].func(memfd, argc - 1, argv + 1);
+ close(memfd);
+ }
+ return(0);
+}
+
+static struct mem_range_desc *
+mrgetall(int memfd, int *nmr)
+{
+ struct mem_range_desc *mrd;
+ struct mem_range_op mro;
+
+ mro.mo_arg[0] = 0;
+ if (ioctl(memfd, MEMRANGE_GET, &mro))
+ err(1, "can't size range descriptor array");
+
+ *nmr = mro.mo_arg[0];
+ mrd = malloc(*nmr * sizeof(struct mem_range_desc));
+ if (mrd == NULL)
+ errx(1, "can't allocate %zd bytes for %d range descriptors",
+ *nmr * sizeof(struct mem_range_desc), *nmr);
+
+ mro.mo_arg[0] = *nmr;
+ mro.mo_desc = mrd;
+ if (ioctl(memfd, MEMRANGE_GET, &mro))
+ err(1, "can't fetch range descriptor array");
+
+ return(mrd);
+}
+
+
+static void
+listfunc(int memfd, int argc, char *argv[])
+{
+ struct mem_range_desc *mrd;
+ int nd, i, j;
+ int ch;
+ int showall = 0;
+ char *owner;
+
+ owner = NULL;
+ while ((ch = getopt(argc, argv, "ao:")) != -1)
+ switch(ch) {
+ case 'a':
+ showall = 1;
+ break;
+ case 'o':
+ owner = strdup(optarg);
+ break;
+ case '?':
+ default:
+ help("list");
+ }
+
+ mrd = mrgetall(memfd, &nd);
+
+ for (i = 0; i < nd; i++) {
+ if (!showall && !(mrd[i].mr_flags & MDF_ACTIVE))
+ continue;
+ if (owner && strcmp(mrd[i].mr_owner, owner))
+ continue;
+ printf("0x%" PRIx64 "/0x%" PRIx64 " %.8s ", mrd[i].mr_base, mrd[i].mr_len,
+ mrd[i].mr_owner[0] ? mrd[i].mr_owner : "-");
+ for (j = 0; attrnames[j].name != NULL; j++)
+ if (mrd[i].mr_flags & attrnames[j].val)
+ printf("%s ", attrnames[j].name);
+ printf("\n");
+ }
+ free(mrd);
+ if (owner)
+ free(owner);
+}
+
+static void
+setfunc(int memfd, int argc, char *argv[])
+{
+ struct mem_range_desc mrd;
+ struct mem_range_op mro;
+ int i;
+ int ch;
+ char *ep;
+
+ mrd.mr_base = 0;
+ mrd.mr_len = 0;
+ mrd.mr_flags = 0;
+ strcpy(mrd.mr_owner, "user");
+ while ((ch = getopt(argc, argv, "b:l:o:")) != -1)
+ switch(ch) {
+ case 'b':
+ mrd.mr_base = strtouq(optarg, &ep, 0);
+ if ((ep == optarg) || (*ep != 0))
+ help("set");
+ break;
+ case 'l':
+ mrd.mr_len = strtouq(optarg, &ep, 0);
+ if ((ep == optarg) || (*ep != 0))
+ help("set");
+ break;
+ case 'o':
+ if ((*optarg == 0) || (strlen(optarg) > 7))
+ help("set");
+ strcpy(mrd.mr_owner, optarg);
+ break;
+
+ case '?':
+ default:
+ help("set");
+ }
+
+ if (mrd.mr_len == 0)
+ help("set");
+
+ argc -= optind;
+ argv += optind;
+
+ while(argc--) {
+ for (i = 0; attrnames[i].name != NULL; i++) {
+ if (!strcmp(attrnames[i].name, argv[0])) {
+ if (!attrnames[i].kind & MDF_SETTABLE)
+ help("flags");
+ mrd.mr_flags |= attrnames[i].val;
+ break;
+ }
+ }
+ if (attrnames[i].name == NULL)
+ help("flags");
+ argv++;
+ }
+
+ mro.mo_desc = &mrd;
+ mro.mo_arg[0] = 0;
+ if (ioctl(memfd, MEMRANGE_SET, &mro))
+ err(1, "can't set range");
+}
+
+static void
+clearfunc(int memfd, int argc, char *argv[])
+{
+ struct mem_range_desc mrd, *mrdp;
+ struct mem_range_op mro;
+ int i, nd;
+ int ch;
+ char *ep, *owner;
+
+ mrd.mr_base = 0;
+ mrd.mr_len = 0;
+ owner = NULL;
+ while ((ch = getopt(argc, argv, "b:l:o:")) != -1)
+ switch(ch) {
+ case 'b':
+ mrd.mr_base = strtouq(optarg, &ep, 0);
+ if ((ep == optarg) || (*ep != 0))
+ help("clear");
+ break;
+ case 'l':
+ mrd.mr_len = strtouq(optarg, &ep, 0);
+ if ((ep == optarg) || (*ep != 0))
+ help("clear");
+ break;
+ case 'o':
+ if ((*optarg == 0) || (strlen(optarg) > 7))
+ help("clear");
+ owner = strdup(optarg);
+ break;
+
+ case '?':
+ default:
+ help("clear");
+ }
+
+ if (owner != NULL) {
+ /* clear-by-owner */
+ if ((mrd.mr_base != 0) || (mrd.mr_len != 0))
+ help("clear");
+
+ mrdp = mrgetall(memfd, &nd);
+ mro.mo_arg[0] = MEMRANGE_SET_REMOVE;
+ for (i = 0; i < nd; i++) {
+ if (!strcmp(owner, mrdp[i].mr_owner) &&
+ (mrdp[i].mr_flags & MDF_ACTIVE) &&
+ !(mrdp[i].mr_flags & MDF_FIXACTIVE)) {
+
+ mro.mo_desc = mrdp + i;
+ if (ioctl(memfd, MEMRANGE_SET, &mro))
+ warn("couldn't clear range owned by '%s'", owner);
+ }
+ }
+ } else if (mrd.mr_len != 0) {
+ /* clear-by-base/len */
+ mro.mo_arg[0] = MEMRANGE_SET_REMOVE;
+ mro.mo_desc = &mrd;
+ if (ioctl(memfd, MEMRANGE_SET, &mro))
+ err(1, "couldn't clear range");
+ } else {
+ help("clear");
+ }
+}
+
+static void
+helpfunc(__unused int memfd, __unused int argc, char *argv[])
+{
+ help(argv[1]);
+}
+
+static void
+help(const char *what)
+{
+ int i;
+
+ if (what != NULL) {
+ /* find a function that matches */
+ for (i = 0; functions[i].cmd != NULL; i++)
+ if (!strcmp(what, functions[i].cmd)) {
+ fprintf(stderr, "%s\n", functions[i].desc);
+ return;
+ }
+ fprintf(stderr, "Unknown command '%s'\n", what);
+ }
+
+ /* print general help */
+ fprintf(stderr, "Valid commands are :\n");
+ for (i = 0; functions[i].cmd != NULL; i++)
+ fprintf(stderr, " %s\n", functions[i].cmd);
+ fprintf(stderr, "Use help <command> for command-specific help\n");
+}
diff --git a/usr.sbin/mergemaster/Makefile b/usr.sbin/mergemaster/Makefile
new file mode 100644
index 0000000..257fbeb
--- /dev/null
+++ b/usr.sbin/mergemaster/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+SCRIPTS=mergemaster.sh
+MAN= mergemaster.8
+
+.include <bsd.prog.mk>
+
diff --git a/usr.sbin/mergemaster/mergemaster.8 b/usr.sbin/mergemaster/mergemaster.8
new file mode 100644
index 0000000..e73812b
--- /dev/null
+++ b/usr.sbin/mergemaster/mergemaster.8
@@ -0,0 +1,457 @@
+.\" Copyright (c) 1998-2009 Douglas Barton
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 18, 2009
+.Dt MERGEMASTER 8
+.Os
+.Sh NAME
+.Nm mergemaster
+.Nd merge configuration files, et al during an upgrade
+.Sh SYNOPSIS
+.Nm
+.Op Fl scrvahipFCPU
+.Op Fl m Ar /path/to/sources
+.Op Fl t Ar /path/to/temp/root
+.Op Fl d
+.Op Fl u Ar N
+.Op Fl w Ar N
+.Op Fl A Ar Target architecture
+.Op Fl D Ar /destdir/path
+.Sh DESCRIPTION
+The
+.Nm
+utility is a Bourne shell script which is designed to aid you
+in updating the various configuration and other files
+associated with
+.Fx .
+It is
+.Sy HIGHLY
+recommended that you back up your
+.Pa /etc
+directory before beginning this process.
+.Pp
+The script uses
+.Pa /usr/src/Makefile
+to build a temporary root environment from
+.Pa /
+down, populating that environment with the various
+files.
+You can specify a different source directory
+with the
+.Op Fl m
+command line option, or specify the destination
+directory with the
+.Op Fl D
+option.
+It then compares each file in that environment
+to its installed counterpart.
+When the script finds a
+change in the new file, or there is no installed
+version of the new file it gives you four options to
+deal with it.
+You can install the new file as is,
+delete the new file, merge the old and new
+files (as appropriate) using
+.Xr sdiff 1
+or leave the file in the temporary root environment to
+merge by hand later.
+.Pp
+By default it creates the temporary root in
+.Pa /var/tmp/temproot
+and compares the
+Version Control System (VCS) Id strings ($FreeBSD$)
+for files that have them, deleting
+the temporary file if the strings match.
+If there is
+no Id string, or if the strings are different it
+compares the files themselves.
+You can
+also specify that the script ignore the Id strings and
+compare every file with the
+.Op Fl s
+option.
+Using the
+.Op Fl F
+option
+.Nm
+will install the new file for you if they differ only by
+VCS strings.
+.Pp
+The merge menu option is designed to let you easily combine your
+customizations from the old version of a file into the new one.
+While you can use the merge function to incorporate changes from
+files that you have not customized,
+it is not recommended.
+.Pp
+The
+.Nm
+utility checks your umask and issues a warning for anything
+other than 022.
+While it is not mandatory to grant
+world read permissions for most configuration files, you
+may run into problems without them.
+If you choose a
+umask other than 022 and experience trouble later this
+could be the cause.
+.Pa /etc/master.passwd
+is treated as a special case.
+If you choose to install
+this file or a merged version of it the file permissions
+are always 600 (rw-------) for security reasons.
+After
+installing an updated version of this file you should
+probably run
+.Xr pwd_mkdb 8
+with the
+.Op Fl p
+option to rebuild your password databases
+and recreate
+.Pa /etc/passwd .
+.Pp
+The script uses the owner and group ids
+that the files are created with by
+.Pa /usr/src/etc/Makefile ,
+and file permissions as specified by the umask.
+Unified diffs are used by default to display any
+differences unless you choose context diffs.
+.Pp
+The
+.Nm
+utility will source scripts that you specify right before
+it starts the comparison, and after it is done running.
+The easiest way to handle this is to place the path
+to the script(s) in the appropriate variables in your
+.Pa .mergemasterrc
+file.
+The script sourced before comparison is named in
+.Ev MM_PRE_COMPARE_SCRIPT ,
+and the one sourced after the script is done is
+.Ev MM_EXIT_SCRIPT .
+This is the recommended way to specify local modifications,
+or files that you want to give special handling to.
+This includes files that you want to be deleted without
+being compared.
+Because the named scripts are sourced from within
+.Nm ,
+all of the script's variables are available for use in
+your custom script.
+You can also use
+.Pa /etc/mergemaster.rc
+which will be read before
+.Pa .mergemasterrc .
+Options specified on the command line are updated last,
+and therefore can override both files.
+.Pp
+The options are as follows:
+.Bl -tag -width Fl
+.It Fl s
+Perform a strict comparison, diffing every pair of files.
+This comparison is performed line by line,
+without regard to VCS Ids.
+.It Fl c
+Use context diffs instead of unified diffs.
+.It Fl r
+Re-run
+.Nm
+on a previously cleaned directory, skipping the creation of
+the temporary root environment.
+This option is compatible
+with all other options.
+.It Fl v
+Be more verbose about the process.
+You should probably use
+this option the first time you run
+.Nm .
+This option also gives you a list of files that exist
+only in the installed version of
+.Pa /etc .
+.It Fl a
+Run automatically.
+This option will leave all the files that
+differ from the installed versions in the temporary directory
+to be dealt with by hand.
+If the
+.Pa temproot
+directory exists, it creates a new one in a previously
+non-existent directory.
+This option unsets the verbose flag,
+but is compatible with all other options.
+Setting
+.Op Fl a
+makes
+.Op Fl w
+superfluous.
+.It Fl h
+Display usage and help information.
+.It Fl i
+Automatically install any files that do not exist in the
+destination directory.
+.It Fl p
+Pre-buildworld mode.
+Compares only files known to be essential to the success of
+{build|install}world,
+including
+.Pa /etc/make.conf .
+.It Fl F
+If the files differ only by VCS Id ($FreeBSD$)
+install the new file.
+.It Fl C
+After a standard
+.Nm
+run,
+compares your rc.conf[.local] options to the defaults.
+.It Fl P
+Preserve files that you replace in
+.Pa /var/tmp/mergemaster/preserved-files-<date> ,
+or another directory you specify in your
+.Nm
+rc file.
+.It Fl U
+Attempt to auto upgrade files that have not been user modified.
+.It Fl m Ar /path/to/sources
+Specify the path to the directory where you want to do the
+.Xr make 1 .
+(In other words, where your sources are, but -s was already
+taken.)
+In previous versions of
+.Nm
+you needed to specify the path all the way to
+.Pa src/etc .
+Starting with r186678 you only need to specify the path to
+.Pa src .
+.Nm
+will convert the path for you if you use the old method.
+.It Fl t Ar /path/to/temp/root
+Create the temporary root environment in
+.Pa /path/to/temp/root
+instead of the default
+.Pa /var/tmp/temproot .
+.It Fl d
+Add the date and time to the name of the temporary
+root directory.
+If
+.Op Fl t
+is specified, this option must
+follow it if you want the date added too.
+.It Fl u Ar N
+Specify a numeric umask.
+The default is 022.
+.It Fl w Ar N
+Supply an alternate screen width to the
+.Xr sdiff 1
+command in numbers of columns.
+The default is 80.
+.It Fl A Ar Target architecture
+Specify an alternative
+.Ev TARGET_ARCH
+architecture name.
+.It Fl D Ar /path
+Specify the destination directory for the installed files.
+.El
+.Sh ENVIRONMENT
+The
+.Nm
+utility uses the
+.Ev PAGER
+environment variable if set.
+Otherwise it uses
+.Xr more 1 .
+If
+.Ev PAGER
+specifies a program outside
+its
+limited
+.Ev PATH
+without specifying the full path,
+.Nm
+prompts you with options on how to proceed.
+The
+.Ev MM_PRE_COMPARE_SCRIPT
+and
+.Ev MM_EXIT_SCRIPT
+variables are used as described above.
+Other variables that are used by the script internally
+can be specified in
+.Pa .mergemasterrc
+as described in more detail below.
+.Sh FILES
+.Bl -tag -width $HOME/.mergemasterrc -compact
+.It Pa /etc/mergemaster.rc
+.It Pa $HOME/.mergemasterrc
+.El
+.Pp
+The
+.Nm
+utility will
+.Ic .\&
+(source) these files if they exist.
+Command line options
+will override rc file options.
+.Pa $HOME/.mergemasterrc
+overrides
+.Pa /etc/mergemaster.rc .
+Here is an example
+with all values commented out:
+.Pp
+.Bd -literal
+# These are options for mergemaster, with their default values listed
+# The following options have command line overrides
+#
+# The target architecture (unset by default)
+#ARCHSTRING='TARGET_ARCH=<foo>'
+#
+# Sourcedir is the directory to do the 'make' in (where the new files are)
+#SOURCEDIR='/usr/src'
+#
+# Directory to install the temporary root environment into
+#TEMPROOT='/var/tmp/temproot'
+#
+# Specify the destination directory for the installed files
+#DESTDIR=
+#
+# Strict comparison skips the VCS Id test and compares every file
+#STRICT=no
+#
+# Type of diff, such as unified, context, etc.
+#DIFF_FLAG='-u'
+#
+# Install the new file if it differs only by VCS Id ($FreeBSD$)
+#FREEBSD_ID=
+#
+# Verbose mode includes more details and additional checks
+#VERBOSE=
+#
+# Automatically install files that do not exist on the system already
+#AUTO_INSTALL=
+#
+# Automatically upgrade files that have not been user modified
+#AUTO_UPGRADE=
+#
+# Compare /etc/rc.conf[.local] to /etc/defaults/rc.conf
+#COMP_CONFS=yes
+#
+# Preserve files that you replace
+#PRESERVE_FILES=yes
+#PRESERVE_FILES_DIR=/var/tmp/mergemaster/preserved-files-`date +%y%m%d-%H%M%S`
+#
+# The umask for mergemaster to compare the default file's modes to
+#NEW_UMASK=022
+#
+# The following options have no command line overrides
+#
+# Files to always avoid comparing
+#IGNORE_FILES='/etc/motd /etc/printcap foo bar'
+#
+# Additional options for diff. This will get unset when using -s.
+#DIFF_OPTIONS='-Bb' # Ignore changes in whitespace
+#
+# Location to store the list of mtree values for AUTO_UPGRADE purposes
+#MTREEDB='/var/db'
+#
+# For those who just cannot stand including the full path to PAGER
+#DONT_CHECK_PAGER=
+#
+# If you set 'yes' above, make sure to include the PATH to your pager
+#PATH=/bin:/usr/bin:/usr/sbin
+#
+# Specify the path to scripts to run before the comparison starts,
+# and/or after the script has finished its work
+#MM_PRE_COMPARE_SCRIPT=
+#MM_EXIT_SCRIPT=
+.Ed
+.Sh EXIT STATUS
+Exit status is 0 on successful completion, or if the user bails out
+manually at some point during execution.
+.Pp
+Exit status is 1 if it fails for one of the following reasons:
+.Pp
+Invalid command line option
+.Pp
+Failure to create the temporary root environment
+.Pp
+Failure to populate the temporary root
+.Pp
+Presence of the 'nodev' option in
+.Pa <DESTDIR>/etc/fstab
+.Pp
+Failure to install a file
+.Sh EXAMPLES
+Typically all you will need to do is type
+.Nm
+at the prompt and the script will do all the work for you.
+.Pp
+To use context diffs and have
+.Nm
+explain more things as it goes along, use:
+.Pp
+.Dl # mergemaster -cv
+.Pp
+To specify that
+.Nm
+put the temporary root environment in
+.Pa /usr/tmp/root ,
+use:
+.Pp
+.Dl # mergemaster -t /usr/tmp/root
+.Pp
+To specify a 110 column screen with a strict
+comparison, use:
+.Pp
+.Dl # mergemaster -sw 110
+.Sh SEE ALSO
+.Xr diff 1 ,
+.Xr make 1 ,
+.Xr more 1 ,
+.Xr sdiff 1 ,
+.Xr pwd_mkdb 8
+.Pp
+.Pa /usr/src/etc/Makefile
+.Rs
+.%O http://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/makeworld.html
+.%T The Cutting Edge (using make world)
+.%A Nik Clayton
+.Re
+.Sh HISTORY
+The
+.Nm
+utility was first publicly available on one of my
+web pages in a much simpler form under the name
+.Pa comproot
+on 13 March 1998.
+The idea for creating the
+temporary root environment comes from Nik Clayton's
+make world tutorial which is referenced above.
+.Sh AUTHORS
+This manual page and the script itself were written by
+.An Douglas Barton Aq DougB@FreeBSD.org .
+.Sh BUGS
+There are no known bugs.
+Please report any problems,
+comments or suggestions to the author.
+Several of the
+improvements to this program have come from user
+suggestions.
+Thank you.
diff --git a/usr.sbin/mergemaster/mergemaster.sh b/usr.sbin/mergemaster/mergemaster.sh
new file mode 100755
index 0000000..ad4dcdf
--- /dev/null
+++ b/usr.sbin/mergemaster/mergemaster.sh
@@ -0,0 +1,1268 @@
+#!/bin/sh
+
+# mergemaster
+
+# Compare files created by /usr/src/etc/Makefile (or the directory
+# the user specifies) with the currently installed copies.
+
+# Copyright 1998-2009 Douglas Barton
+# DougB@FreeBSD.org
+
+# $FreeBSD$
+
+PATH=/bin:/usr/bin:/usr/sbin
+
+display_usage () {
+ VERSION_NUMBER=`grep "[$]FreeBSD:" $0 | cut -d ' ' -f 4`
+ echo "mergemaster version ${VERSION_NUMBER}"
+ echo 'Usage: mergemaster [-scrvahipFCPU]'
+ echo ' [-m /path] [-t /path] [-d] [-u N] [-w N] [-A arch] [-D /path]'
+ echo "Options:"
+ echo " -s Strict comparison (diff every pair of files)"
+ echo " -c Use context diff instead of unified diff"
+ echo " -r Re-run on a previously cleaned directory (skip temproot creation)"
+ echo " -v Be more verbose about the process, include additional checks"
+ echo " -a Leave all files that differ to merge by hand"
+ echo " -h Display more complete help"
+ echo ' -i Automatically install files that do not exist in destination directory'
+ echo ' -p Pre-buildworld mode, only compares crucial files'
+ echo ' -F Install files that differ only by revision control Id ($FreeBSD$)'
+ echo ' -C Compare local rc.conf variables to the defaults'
+ echo ' -P Preserve files that are overwritten'
+ echo " -U Attempt to auto upgrade files that have not been user modified"
+ echo ''
+ echo " -m /path/directory Specify location of source to do the make in"
+ echo " -t /path/directory Specify temp root directory"
+ echo " -d Add date and time to directory name (e.g., /var/tmp/temproot.`date +%m%d.%H.%M`)"
+ echo " -u N Specify a numeric umask"
+ echo " -w N Specify a screen width in columns to sdiff"
+ echo " -A architecture Alternative architecture name to pass to make"
+ echo ' -D /path/directory Specify the destination directory to install files to'
+ echo ''
+}
+
+display_help () {
+ echo "* To specify a directory other than /var/tmp/temproot for the"
+ echo " temporary root environment, use -t /path/to/temp/root"
+ echo "* The -w option takes a number as an argument for the column width"
+ echo " of the screen. The default is 80."
+ echo '* The -a option causes mergemaster to run without prompting.'
+}
+
+# Loop allowing the user to use sdiff to merge files and display the merged
+# file.
+merge_loop () {
+ case "${VERBOSE}" in
+ '') ;;
+ *)
+ echo " *** Type h at the sdiff prompt (%) to get usage help"
+ ;;
+ esac
+ echo ''
+ MERGE_AGAIN=yes
+ while [ "${MERGE_AGAIN}" = "yes" ]; do
+ # Prime file.merged so we don't blat the owner/group id's
+ cp -p "${COMPFILE}" "${COMPFILE}.merged"
+ sdiff -o "${COMPFILE}.merged" --text --suppress-common-lines \
+ --width=${SCREEN_WIDTH:-80} "${DESTDIR}${COMPFILE#.}" "${COMPFILE}"
+ INSTALL_MERGED=V
+ while [ "${INSTALL_MERGED}" = "v" -o "${INSTALL_MERGED}" = "V" ]; do
+ echo ''
+ echo " Use 'i' to install merged file"
+ echo " Use 'r' to re-do the merge"
+ echo " Use 'v' to view the merged file"
+ echo " Default is to leave the temporary file to deal with by hand"
+ echo ''
+ echo -n " *** How should I deal with the merged file? [Leave it for later] "
+ read INSTALL_MERGED
+
+ case "${INSTALL_MERGED}" in
+ [iI])
+ mv "${COMPFILE}.merged" "${COMPFILE}"
+ echo ''
+ if mm_install "${COMPFILE}"; then
+ echo " *** Merged version of ${COMPFILE} installed successfully"
+ else
+ echo " *** Problem installing ${COMPFILE}, it will remain to merge by hand later"
+ fi
+ unset MERGE_AGAIN
+ ;;
+ [rR])
+ rm "${COMPFILE}.merged"
+ ;;
+ [vV])
+ ${PAGER} "${COMPFILE}.merged"
+ ;;
+ '')
+ echo " *** ${COMPFILE} will remain for your consideration"
+ unset MERGE_AGAIN
+ ;;
+ *)
+ echo "invalid choice: ${INSTALL_MERGED}"
+ INSTALL_MERGED=V
+ ;;
+ esac
+ done
+ done
+}
+
+# Loop showing user differences between files, allow merge, skip or install
+# options
+diff_loop () {
+
+ HANDLE_COMPFILE=v
+
+ while [ "${HANDLE_COMPFILE}" = "v" -o "${HANDLE_COMPFILE}" = "V" -o \
+ "${HANDLE_COMPFILE}" = "NOT V" ]; do
+ if [ -f "${DESTDIR}${COMPFILE#.}" -a -f "${COMPFILE}" ]; then
+ if [ -n "${AUTO_UPGRADE}" ]; then
+ if echo "${CHANGED}" | grep -qsv ${DESTDIR}${COMPFILE#.}; then
+ echo ''
+ echo " *** ${COMPFILE} has not been user modified."
+ echo ''
+
+ if mm_install "${COMPFILE}"; then
+ echo " *** ${COMPFILE} upgraded successfully"
+ echo ''
+ # Make the list print one file per line
+ AUTO_UPGRADED_FILES="${AUTO_UPGRADED_FILES} ${DESTDIR}${COMPFILE#.}
+"
+ else
+ echo " *** Problem upgrading ${COMPFILE}, it will remain to merge by hand"
+ fi
+ return
+ fi
+ fi
+ if [ "${HANDLE_COMPFILE}" = "v" -o "${HANDLE_COMPFILE}" = "V" ]; then
+ echo ''
+ echo ' ====================================================================== '
+ echo ''
+ (
+ echo " *** Displaying differences between ${COMPFILE} and installed version:"
+ echo ''
+ diff ${DIFF_FLAG} ${DIFF_OPTIONS} "${DESTDIR}${COMPFILE#.}" "${COMPFILE}"
+ ) | ${PAGER}
+ echo ''
+ fi
+ else
+ echo ''
+ echo " *** There is no installed version of ${COMPFILE}"
+ echo ''
+ case "${AUTO_INSTALL}" in
+ [Yy][Ee][Ss])
+ echo ''
+ if mm_install "${COMPFILE}"; then
+ echo " *** ${COMPFILE} installed successfully"
+ echo ''
+ # Make the list print one file per line
+ AUTO_INSTALLED_FILES="${AUTO_INSTALLED_FILES} ${DESTDIR}${COMPFILE#.}
+"
+ else
+ echo " *** Problem installing ${COMPFILE}, it will remain to merge by hand"
+ fi
+ return
+ ;;
+ *)
+ NO_INSTALLED=yes
+ ;;
+ esac
+ fi
+
+ echo " Use 'd' to delete the temporary ${COMPFILE}"
+ echo " Use 'i' to install the temporary ${COMPFILE}"
+ case "${NO_INSTALLED}" in
+ '')
+ echo " Use 'm' to merge the temporary and installed versions"
+ echo " Use 'v' to view the diff results again"
+ ;;
+ esac
+ echo ''
+ echo " Default is to leave the temporary file to deal with by hand"
+ echo ''
+ echo -n "How should I deal with this? [Leave it for later] "
+ read HANDLE_COMPFILE
+
+ case "${HANDLE_COMPFILE}" in
+ [dD])
+ rm "${COMPFILE}"
+ echo ''
+ echo " *** Deleting ${COMPFILE}"
+ ;;
+ [iI])
+ echo ''
+ if mm_install "${COMPFILE}"; then
+ echo " *** ${COMPFILE} installed successfully"
+ else
+ echo " *** Problem installing ${COMPFILE}, it will remain to merge by hand"
+ fi
+ ;;
+ [mM])
+ case "${NO_INSTALLED}" in
+ '')
+ # interact with user to merge files
+ merge_loop
+ ;;
+ *)
+ echo ''
+ echo " *** There is no installed version of ${COMPFILE}"
+ echo ''
+ HANDLE_COMPFILE="NOT V"
+ ;;
+ esac # End of "No installed version of file but user selected merge" test
+ ;;
+ [vV])
+ continue
+ ;;
+ '')
+ echo ''
+ echo " *** ${COMPFILE} will remain for your consideration"
+ ;;
+ *)
+ # invalid choice, show menu again.
+ echo "invalid choice: ${HANDLE_COMPFILE}"
+ echo ''
+ HANDLE_COMPFILE="NOT V"
+ continue
+ ;;
+ esac # End of "How to handle files that are different"
+ done
+ unset NO_INSTALLED
+ echo ''
+ case "${VERBOSE}" in
+ '') ;;
+ *)
+ sleep 3
+ ;;
+ esac
+}
+
+press_to_continue () {
+ local DISCARD
+ echo -n ' *** Press the [Enter] or [Return] key to continue '
+ read DISCARD
+}
+
+# Set the default path for the temporary root environment
+#
+TEMPROOT='/var/tmp/temproot'
+
+# Read /etc/mergemaster.rc first so the one in $HOME can override
+#
+if [ -r /etc/mergemaster.rc ]; then
+ . /etc/mergemaster.rc
+fi
+
+# Read .mergemasterrc before command line so CLI can override
+#
+if [ -r "$HOME/.mergemasterrc" ]; then
+ . "$HOME/.mergemasterrc"
+fi
+
+# Assign the location of the mtree database
+#
+MTREEDB=${MTREEDB:-/var/db}
+MTREEFILE="${MTREEDB}/mergemaster.mtree"
+
+# Check the command line options
+#
+while getopts ":ascrvhipCPm:t:du:w:D:A:FU" COMMAND_LINE_ARGUMENT ; do
+ case "${COMMAND_LINE_ARGUMENT}" in
+ A)
+ ARCHSTRING='TARGET_ARCH='${OPTARG}
+ ;;
+ F)
+ FREEBSD_ID=yes
+ ;;
+ U)
+ AUTO_UPGRADE=yes
+ ;;
+ s)
+ STRICT=yes
+ unset DIFF_OPTIONS
+ ;;
+ c)
+ DIFF_FLAG='-c'
+ ;;
+ r)
+ RERUN=yes
+ ;;
+ v)
+ case "${AUTO_RUN}" in
+ '') VERBOSE=yes ;;
+ esac
+ ;;
+ a)
+ AUTO_RUN=yes
+ unset VERBOSE
+ ;;
+ h)
+ display_usage
+ display_help
+ exit 0
+ ;;
+ i)
+ AUTO_INSTALL=yes
+ ;;
+ C)
+ COMP_CONFS=yes
+ ;;
+ P)
+ PRESERVE_FILES=yes
+ ;;
+ p)
+ PRE_WORLD=yes
+ unset COMP_CONFS
+ unset AUTO_RUN
+ ;;
+ m)
+ SOURCEDIR=${OPTARG}
+ ;;
+ t)
+ TEMPROOT=${OPTARG}
+ ;;
+ d)
+ TEMPROOT=${TEMPROOT}.`date +%m%d.%H.%M`
+ ;;
+ u)
+ NEW_UMASK=${OPTARG}
+ ;;
+ w)
+ SCREEN_WIDTH=${OPTARG}
+ ;;
+ D)
+ DESTDIR=${OPTARG}
+ ;;
+ *)
+ display_usage
+ exit 1
+ ;;
+ esac
+done
+
+# Don't force the user to set this in the mergemaster rc file
+if [ -n "${PRESERVE_FILES}" -a -z "${PRESERVE_FILES_DIR}" ]; then
+ PRESERVE_FILES_DIR=/var/tmp/mergemaster/preserved-files-`date +%y%m%d-%H%M%S`
+fi
+
+# Check for the mtree database in DESTDIR
+case "${AUTO_UPGRADE}" in
+'') ;; # If the option is not set no need to run the test or warn the user
+*)
+ if [ ! -f "${DESTDIR}${MTREEFILE}" ]; then
+ echo ''
+ echo "*** Unable to find mtree database. Skipping auto-upgrade."
+ echo ''
+ press_to_continue
+ unset AUTO_UPGRADE
+ fi
+ ;;
+esac
+
+if [ -e "${DESTDIR}/etc/fstab" ]; then
+ if grep -q nodev ${DESTDIR}/etc/fstab; then
+ echo ''
+ echo "*** You have the deprecated 'nodev' option in ${DESTDIR}/etc/fstab."
+ echo " This can prevent the filesystem from being mounted on reboot."
+ echo " Please update your fstab before continuing."
+ echo " See fstab(5) for more information."
+ echo ''
+ exit 1
+ fi
+fi
+
+echo ''
+
+# If the user has a pager defined, make sure we can run it
+#
+case "${DONT_CHECK_PAGER}" in
+'')
+check_pager () {
+ while ! type "${PAGER%% *}" >/dev/null; do
+ echo " *** Your PAGER environment variable specifies '${PAGER}', but"
+ echo " due to the limited PATH that I use for security reasons,"
+ echo " I cannot execute it. So, what would you like to do?"
+ echo ''
+ echo " Use 'e' to exit mergemaster and fix your PAGER variable"
+ if [ -x /usr/bin/less -o -x /usr/local/bin/less ]; then
+ echo " Use 'l' to set PAGER to 'less' for this run"
+ fi
+ echo " Use 'm' to use plain old 'more' as your PAGER for this run"
+ echo ''
+ echo " Default is to use plain old 'more' "
+ echo ''
+ echo -n "What should I do? [Use 'more'] "
+ read FIXPAGER
+
+ case "${FIXPAGER}" in
+ [eE])
+ exit 0
+ ;;
+ [lL])
+ if [ -x /usr/bin/less ]; then
+ PAGER=/usr/bin/less
+ elif [ -x /usr/local/bin/less ]; then
+ PAGER=/usr/local/bin/less
+ else
+ echo ''
+ echo " *** Fatal Error:"
+ echo " You asked to use 'less' as your pager, but I can't"
+ echo " find it in /usr/bin or /usr/local/bin"
+ exit 1
+ fi
+ ;;
+ [mM]|'')
+ PAGER=more
+ ;;
+ *)
+ echo ''
+ echo "invalid choice: ${FIXPAGER}"
+ esac
+ echo ''
+ done
+}
+ if [ -n "${PAGER}" ]; then
+ check_pager
+ fi
+ ;;
+esac
+
+# If user has a pager defined, or got assigned one above, use it.
+# If not, use more.
+#
+PAGER=${PAGER:-more}
+
+if [ -n "${VERBOSE}" -a ! "${PAGER}" = "more" ]; then
+ echo " *** You have ${PAGER} defined as your pager so we will use that"
+ echo ''
+ sleep 3
+fi
+
+# Assign the diff flag once so we will not have to keep testing it
+#
+DIFF_FLAG=${DIFF_FLAG:--u}
+
+# Assign the source directory
+#
+SOURCEDIR=${SOURCEDIR:-/usr/src}
+if [ ! -f ${SOURCEDIR}/Makefile.inc1 -a \
+ -f ${SOURCEDIR}/../Makefile.inc1 ]; then
+ echo " *** The source directory you specified (${SOURCEDIR})"
+ echo " will be reset to ${SOURCEDIR}/.."
+ echo ''
+ sleep 3
+ SOURCEDIR=${SOURCEDIR}/..
+fi
+
+# Setup make to use system files from SOURCEDIR
+MM_MAKE="make ${ARCHSTRING} -m ${SOURCEDIR}/share/mk"
+
+# Check DESTDIR against the mergemaster mtree database to see what
+# files the user changed from the reference files.
+#
+CHANGED=
+if [ -n "${AUTO_UPGRADE}" -a -f "${DESTDIR}${MTREEFILE}" ]; then
+ for file in `mtree -eq -f ${DESTDIR}${MTREEFILE} -p ${DESTDIR}/ \
+ 2>/dev/null | awk '($2 == "changed") {print $1}'`; do
+ if [ -f "${DESTDIR}/$file" ]; then
+ CHANGED="${CHANGED} ${DESTDIR}/$file"
+ fi
+ done
+fi
+
+# Check the width of the user's terminal
+#
+if [ -t 0 ]; then
+ w=`tput columns`
+ case "${w}" in
+ 0|'') ;; # No-op, since the input is not valid
+ *)
+ case "${SCREEN_WIDTH}" in
+ '') SCREEN_WIDTH="${w}" ;;
+ "${w}") ;; # No-op, since they are the same
+ *)
+ echo -n "*** You entered ${SCREEN_WIDTH} as your screen width, but stty "
+ echo "thinks it is ${w}."
+ echo ''
+ echo -n "What would you like to use? [${w}] "
+ read SCREEN_WIDTH
+ case "${SCREEN_WIDTH}" in
+ '') SCREEN_WIDTH="${w}" ;;
+ esac
+ ;;
+ esac
+ esac
+fi
+
+# Define what CVS $Id tag to look for to aid portability.
+#
+CVS_ID_TAG=FreeBSD
+
+delete_temproot () {
+ rm -rf "${TEMPROOT}" 2>/dev/null
+ chflags -R 0 "${TEMPROOT}" 2>/dev/null
+ rm -rf "${TEMPROOT}" || exit 1
+}
+
+case "${RERUN}" in
+'')
+ # Set up the loop to test for the existence of the
+ # temp root directory.
+ #
+ TEST_TEMP_ROOT=yes
+ while [ "${TEST_TEMP_ROOT}" = "yes" ]; do
+ if [ -d "${TEMPROOT}" ]; then
+ echo "*** The directory specified for the temporary root environment,"
+ echo " ${TEMPROOT}, exists. This can be a security risk if untrusted"
+ echo " users have access to the system."
+ echo ''
+ case "${AUTO_RUN}" in
+ '')
+ echo " Use 'd' to delete the old ${TEMPROOT} and continue"
+ echo " Use 't' to select a new temporary root directory"
+ echo " Use 'e' to exit mergemaster"
+ echo ''
+ echo " Default is to use ${TEMPROOT} as is"
+ echo ''
+ echo -n "How should I deal with this? [Use the existing ${TEMPROOT}] "
+ read DELORNOT
+
+ case "${DELORNOT}" in
+ [dD])
+ echo ''
+ echo " *** Deleting the old ${TEMPROOT}"
+ echo ''
+ delete_temproot || exit 1
+ unset TEST_TEMP_ROOT
+ ;;
+ [tT])
+ echo " *** Enter new directory name for temporary root environment"
+ read TEMPROOT
+ ;;
+ [eE])
+ exit 0
+ ;;
+ '')
+ echo ''
+ echo " *** Leaving ${TEMPROOT} intact"
+ echo ''
+ unset TEST_TEMP_ROOT
+ ;;
+ *)
+ echo ''
+ echo "invalid choice: ${DELORNOT}"
+ echo ''
+ ;;
+ esac
+ ;;
+ *)
+ # If this is an auto-run, try a hopefully safe alternative then
+ # re-test anyway.
+ TEMPROOT=/var/tmp/temproot.`date +%m%d.%H.%M.%S`
+ ;;
+ esac
+ else
+ unset TEST_TEMP_ROOT
+ fi
+ done
+
+ echo "*** Creating the temporary root environment in ${TEMPROOT}"
+
+ if mkdir -p "${TEMPROOT}"; then
+ echo " *** ${TEMPROOT} ready for use"
+ fi
+
+ if [ ! -d "${TEMPROOT}" ]; then
+ echo ''
+ echo " *** FATAL ERROR: Cannot create ${TEMPROOT}"
+ echo ''
+ exit 1
+ fi
+
+ echo " *** Creating and populating directory structure in ${TEMPROOT}"
+ echo ''
+
+ case "${VERBOSE}" in
+ '') ;;
+ *)
+ press_to_continue
+ ;;
+ esac
+
+ case "${PRE_WORLD}" in
+ '')
+ { cd ${SOURCEDIR} &&
+ case "${DESTDIR}" in
+ '') ;;
+ *)
+ ${MM_MAKE} DESTDIR=${DESTDIR} distrib-dirs
+ ;;
+ esac
+ od=${TEMPROOT}/usr/obj
+ ${MM_MAKE} DESTDIR=${TEMPROOT} distrib-dirs &&
+ MAKEOBJDIRPREFIX=$od ${MM_MAKE} _obj SUBDIR_OVERRIDE=etc &&
+ MAKEOBJDIRPREFIX=$od ${MM_MAKE} everything SUBDIR_OVERRIDE=etc &&
+ MAKEOBJDIRPREFIX=$od ${MM_MAKE} DESTDIR=${TEMPROOT} distribution;} ||
+ { echo '';
+ echo " *** FATAL ERROR: Cannot 'cd' to ${SOURCEDIR} and install files to";
+ echo " the temproot environment";
+ echo '';
+ exit 1;}
+ ;;
+ *)
+ # Only set up files that are crucial to {build|install}world
+ { mkdir -p ${TEMPROOT}/etc &&
+ cp -p ${SOURCEDIR}/etc/master.passwd ${TEMPROOT}/etc &&
+ cp -p ${SOURCEDIR}/etc/group ${TEMPROOT}/etc;} ||
+ { echo '';
+ echo ' *** FATAL ERROR: Cannot copy files to the temproot environment';
+ echo '';
+ exit 1;}
+ ;;
+ esac
+
+ # Doing the inventory and removing files that we don't want to compare only
+ # makes sense if we are not doing a rerun, since we have no way of knowing
+ # what happened to the files during previous incarnations.
+ case "${VERBOSE}" in
+ '') ;;
+ *)
+ echo ''
+ echo ' *** The following files exist only in the installed version of'
+ echo " ${DESTDIR}/etc. In the vast majority of cases these files"
+ echo ' are necessary parts of the system and should not be deleted.'
+ echo ' However because these files are not updated by this process you'
+ echo ' might want to verify their status before rebooting your system.'
+ echo ''
+ press_to_continue
+ diff -qr ${DESTDIR}/etc ${TEMPROOT}/etc | grep "^Only in ${DESTDIR}/etc" | ${PAGER}
+ echo ''
+ press_to_continue
+ ;;
+ esac
+
+ # Avoid comparing the motd if the user specifies it in .mergemasterrc
+ # Compatibility shim to be removed in FreeBSD 9.x
+ case "${IGNORE_MOTD}" in
+ '') ;;
+ *) IGNORE_FILES="${IGNORE_FILES} /etc/motd"
+ echo ''
+ echo "*** You have the IGNORE_MOTD option set in your mergemaster rc file."
+ echo " This option is deprecated in favor of the IGNORE_FILES option."
+ echo " Please update your rc file accordingly."
+ echo ''
+ press_to_continue
+ ;;
+ esac
+
+ # Avoid comparing the following user specified files
+ for file in ${IGNORE_FILES}; do
+ test -e ${TEMPROOT}/${file} && unlink ${TEMPROOT}/${file}
+ done
+ ;; # End of the "RERUN" test
+esac
+
+# We really don't want to have to deal with files like login.conf.db, pwd.db,
+# or spwd.db. Instead, we want to compare the text versions, and run *_mkdb.
+# Prompt the user to do so below, as needed.
+#
+rm -f ${TEMPROOT}/etc/*.db ${TEMPROOT}/etc/passwd
+
+# We only need to compare things like freebsd.cf once
+find ${TEMPROOT}/usr/obj -type f -delete 2>/dev/null
+
+# Delete 0 length files to make the mtree database as small as possible.
+find ${TEMPROOT} -type f -size 0 -delete 2>/dev/null
+
+# Build the mtree database in a temporary location.
+MTREENEW=`mktemp -t mergemaster.mtree`
+case "${PRE_WORLD}" in
+'') mtree -ci -p ${TEMPROOT} -k size,md5digest > ${MTREENEW} 2>/dev/null
+ ;;
+*) # We don't want to mess with the mtree database on a pre-world run.
+ ;;
+esac
+
+# Get ready to start comparing files
+
+# Check umask if not specified on the command line,
+# and we are not doing an autorun
+#
+if [ -z "${NEW_UMASK}" -a -z "${AUTO_RUN}" ]; then
+ USER_UMASK=`umask`
+ case "${USER_UMASK}" in
+ 0022|022) ;;
+ *)
+ echo ''
+ echo " *** Your umask is currently set to ${USER_UMASK}. By default, this script"
+ echo " installs all files with the same user, group and modes that"
+ echo " they are created with by ${SOURCEDIR}/etc/Makefile, compared to"
+ echo " a umask of 022. This umask allows world read permission when"
+ echo " the file's default permissions have it."
+ echo ''
+ echo " No world permissions can sometimes cause problems. A umask of"
+ echo " 022 will restore the default behavior, but is not mandatory."
+ echo " /etc/master.passwd is a special case. Its file permissions"
+ echo " will be 600 (rw-------) if installed."
+ echo ''
+ echo -n "What umask should I use? [${USER_UMASK}] "
+ read NEW_UMASK
+
+ NEW_UMASK="${NEW_UMASK:-$USER_UMASK}"
+ ;;
+ esac
+ echo ''
+fi
+
+CONFIRMED_UMASK=${NEW_UMASK:-0022}
+
+#
+# Warn users who still have old rc files
+#
+for file in atm devfs diskless1 diskless2 network network6 pccard \
+ serial syscons sysctl alpha amd64 i386 ia64 sparc64; do
+ if [ -f "${DESTDIR}/etc/rc.${file}" ]; then
+ OLD_RC_PRESENT=1
+ break
+ fi
+done
+
+case "${OLD_RC_PRESENT}" in
+1)
+ echo ''
+ echo " *** There are elements of the old rc system in ${DESTDIR}/etc/."
+ echo ''
+ echo ' While these scripts will not hurt anything, they are not'
+ echo ' functional on an up to date system, and can be removed.'
+ echo ''
+
+ case "${AUTO_RUN}" in
+ '')
+ echo -n 'Move these files to /var/tmp/mergemaster/old_rc? [yes] '
+ read MOVE_OLD_RC
+
+ case "${MOVE_OLD_RC}" in
+ [nN]*) ;;
+ *)
+ mkdir -p /var/tmp/mergemaster/old_rc
+ for file in atm devfs diskless1 diskless2 network network6 pccard \
+ serial syscons sysctl alpha amd64 i386 ia64 sparc64; do
+ if [ -f "${DESTDIR}/etc/rc.${file}" ]; then
+ mv ${DESTDIR}/etc/rc.${file} /var/tmp/mergemaster/old_rc/
+ fi
+ done
+ echo ' The files have been moved'
+ press_to_continue
+ ;;
+ esac
+ ;;
+ *) ;;
+ esac
+esac
+
+# Use the umask/mode information to install the files
+# Create directories as needed
+#
+install_error () {
+ echo "*** FATAL ERROR: Unable to install ${1} to ${2}"
+ echo ''
+ exit 1
+}
+
+do_install_and_rm () {
+ case "${PRESERVE_FILES}" in
+ [Yy][Ee][Ss])
+ if [ -f "${3}/${2##*/}" ]; then
+ mkdir -p ${PRESERVE_FILES_DIR}/${2%/*}
+ cp ${3}/${2##*/} ${PRESERVE_FILES_DIR}/${2%/*}
+ fi
+ ;;
+ esac
+
+ if [ ! -d "${3}/${2##*/}" ]; then
+ if install -m ${1} ${2} ${3}; then
+ unlink ${2}
+ else
+ install_error ${2} ${3}
+ fi
+ else
+ install_error ${2} ${3}
+ fi
+}
+
+# 4095 = "obase=10;ibase=8;07777" | bc
+find_mode () {
+ local OCTAL
+ OCTAL=$(( ~$(echo "obase=10; ibase=8; ${CONFIRMED_UMASK}" | bc) & 4095 &
+ $(echo "obase=10; ibase=8; $(stat -f "%OMp%OLp" ${1})" | bc) ))
+ printf "%04o\n" ${OCTAL}
+}
+
+mm_install () {
+ local INSTALL_DIR
+ INSTALL_DIR=${1#.}
+ INSTALL_DIR=${INSTALL_DIR%/*}
+
+ case "${INSTALL_DIR}" in
+ '')
+ INSTALL_DIR=/
+ ;;
+ esac
+
+ if [ -n "${DESTDIR}${INSTALL_DIR}" -a ! -d "${DESTDIR}${INSTALL_DIR}" ]; then
+ DIR_MODE=`find_mode "${TEMPROOT}/${INSTALL_DIR}"`
+ install -d -o root -g wheel -m "${DIR_MODE}" "${DESTDIR}${INSTALL_DIR}"
+ fi
+
+ FILE_MODE=`find_mode "${1}"`
+
+ if [ ! -x "${1}" ]; then
+ case "${1#.}" in
+ /etc/mail/aliases)
+ NEED_NEWALIASES=yes
+ ;;
+ /etc/login.conf)
+ NEED_CAP_MKDB=yes
+ ;;
+ /etc/master.passwd)
+ do_install_and_rm 600 "${1}" "${DESTDIR}${INSTALL_DIR}"
+ NEED_PWD_MKDB=yes
+ DONT_INSTALL=yes
+ ;;
+ /.cshrc | /.profile)
+ case "${AUTO_INSTALL}" in
+ '')
+ case "${LINK_EXPLAINED}" in
+ '')
+ echo " *** Historically BSD derived systems have had a"
+ echo " hard link from /.cshrc and /.profile to"
+ echo " their namesakes in /root. Please indicate"
+ echo " your preference below for bringing your"
+ echo " installed files up to date."
+ echo ''
+ LINK_EXPLAINED=yes
+ ;;
+ esac
+
+ echo " Use 'd' to delete the temporary ${COMPFILE}"
+ echo " Use 'l' to delete the existing ${DESTDIR}${COMPFILE#.} and create the link"
+ echo ''
+ echo " Default is to leave the temporary file to deal with by hand"
+ echo ''
+ echo -n " How should I handle ${COMPFILE}? [Leave it to install later] "
+ read HANDLE_LINK
+ ;;
+ *) # Part of AUTO_INSTALL
+ HANDLE_LINK=l
+ ;;
+ esac
+
+ case "${HANDLE_LINK}" in
+ [dD]*)
+ rm "${COMPFILE}"
+ echo ''
+ echo " *** Deleting ${COMPFILE}"
+ ;;
+ [lL]*)
+ echo ''
+ rm -f "${DESTDIR}${COMPFILE#.}"
+ if ln "${DESTDIR}/root/${COMPFILE##*/}" "${DESTDIR}${COMPFILE#.}"; then
+ echo " *** Link from ${DESTDIR}${COMPFILE#.} to ${DESTDIR}/root/${COMPFILE##*/} installed successfully"
+ rm "${COMPFILE}"
+ else
+ echo " *** Error linking ${DESTDIR}${COMPFILE#.} to ${DESTDIR}/root/${COMPFILE##*/}, ${COMPFILE} will remain to install by hand"
+ fi
+ ;;
+ *)
+ echo " *** ${COMPFILE} will remain for your consideration"
+ ;;
+ esac
+ DONT_INSTALL=yes
+ ;;
+ esac
+
+ case "${DONT_INSTALL}" in
+ '')
+ do_install_and_rm "${FILE_MODE}" "${1}" "${DESTDIR}${INSTALL_DIR}"
+ ;;
+ *)
+ unset DONT_INSTALL
+ ;;
+ esac
+ else # File matched -x
+ do_install_and_rm "${FILE_MODE}" "${1}" "${DESTDIR}${INSTALL_DIR}"
+ fi
+ return $?
+}
+
+if [ ! -d "${TEMPROOT}" ]; then
+ echo "*** FATAL ERROR: The temproot directory (${TEMPROOT})"
+ echo ' has disappeared!'
+ echo ''
+ exit 1
+fi
+
+echo ''
+echo "*** Beginning comparison"
+echo ''
+
+# Pre-world does not populate /etc/rc.d.
+# It is very possible that a previous run would have deleted files in
+# ${TEMPROOT}/etc/rc.d, thus creating a lot of false positives.
+if [ -z "${PRE_WORLD}" -a -z "${RERUN}" ]; then
+ echo " *** Checking ${DESTDIR}/etc/rc.d for stale files"
+ echo ''
+ cd "${DESTDIR}/etc/rc.d" &&
+ for file in *; do
+ if [ ! -e "${TEMPROOT}/etc/rc.d/${file}" ]; then
+ STALE_RC_FILES="${STALE_RC_FILES} ${file}"
+ fi
+ done
+ case "${STALE_RC_FILES}" in
+ ''|' *')
+ echo ' *** No stale files found'
+ ;;
+ *)
+ echo " *** The following files exist in ${DESTDIR}/etc/rc.d but not in"
+ echo " ${TEMPROOT}/etc/rc.d/:"
+ echo ''
+ echo "${STALE_RC_FILES}"
+ echo ''
+ echo ' The presence of stale files in this directory can cause the'
+ echo ' dreaded unpredictable results, and therefore it is highly'
+ echo ' recommended that you delete them.'
+ case "${AUTO_RUN}" in
+ '')
+ echo ''
+ echo -n ' *** Delete them now? [n] '
+ read DELETE_STALE_RC_FILES
+ case "${DELETE_STALE_RC_FILES}" in
+ [yY])
+ echo ' *** Deleting ... '
+ rm ${STALE_RC_FILES}
+ echo ' done.'
+ ;;
+ *)
+ echo ' *** Files will not be deleted'
+ ;;
+ esac
+ sleep 2
+ ;;
+ esac
+ ;;
+ esac
+ echo ''
+fi
+
+cd "${TEMPROOT}"
+
+if [ -r "${MM_PRE_COMPARE_SCRIPT}" ]; then
+ . "${MM_PRE_COMPARE_SCRIPT}"
+fi
+
+# Using -size +0 avoids uselessly checking the empty log files created
+# by ${SOURCEDIR}/etc/Makefile and the device entries in ./dev, but does
+# check the scripts in ./dev, as we'd like (assuming no devfs of course).
+#
+for COMPFILE in `find . -type f -size +0`; do
+
+ # First, check to see if the file exists in DESTDIR. If not, the
+ # diff_loop function knows how to handle it.
+ #
+ if [ ! -e "${DESTDIR}${COMPFILE#.}" ]; then
+ case "${AUTO_RUN}" in
+ '')
+ diff_loop
+ ;;
+ *)
+ case "${AUTO_INSTALL}" in
+ '')
+ # If this is an auto run, make it official
+ echo " *** ${COMPFILE} will remain for your consideration"
+ ;;
+ *)
+ diff_loop
+ ;;
+ esac
+ ;;
+ esac # Auto run test
+ continue
+ fi
+
+ case "${STRICT}" in
+ '' | [Nn][Oo])
+ # Compare CVS $Id's first so if the file hasn't been modified
+ # local changes will be ignored.
+ # If the files have the same $Id, delete the one in temproot so the
+ # user will have less to wade through if files are left to merge by hand.
+ #
+ CVSID1=`grep "[$]${CVS_ID_TAG}:" ${DESTDIR}${COMPFILE#.} 2>/dev/null`
+ CVSID2=`grep "[$]${CVS_ID_TAG}:" ${COMPFILE} 2>/dev/null` || CVSID2=none
+
+ case "${CVSID2}" in
+ "${CVSID1}")
+ echo " *** Temp ${COMPFILE} and installed have the same CVS Id, deleting"
+ rm "${COMPFILE}"
+ ;;
+ esac
+ ;;
+ esac
+
+ # If the file is still here either because the $Ids are different, the
+ # file doesn't have an $Id, or we're using STRICT mode; look at the diff.
+ #
+ if [ -f "${COMPFILE}" ]; then
+
+ # Do an absolute diff first to see if the files are actually different.
+ # If they're not different, delete the one in temproot.
+ #
+ if diff -q ${DIFF_OPTIONS} "${DESTDIR}${COMPFILE#.}" "${COMPFILE}" > \
+ /dev/null 2>&1; then
+ echo " *** Temp ${COMPFILE} and installed are the same, deleting"
+ rm "${COMPFILE}"
+ else
+ # Ok, the files are different, so show the user where they differ.
+ # Use user's choice of diff methods; and user's pager if they have one.
+ # Use more if not.
+ # Use unified diffs by default. Context diffs give me a headache. :)
+ #
+ # If the user chose the -F option, test for that before proceeding
+ #
+ if [ -n "$FREEBSD_ID" ]; then
+ if diff -q -I'[$]FreeBSD:.*$' "${DESTDIR}${COMPFILE#.}" "${COMPFILE}" > \
+ /dev/null 2>&1; then
+ if mm_install "${COMPFILE}"; then
+ echo "*** Updated revision control Id for ${DESTDIR}${COMPFILE#.}"
+ else
+ echo "*** Problem installing ${COMPFILE}, it will remain to merge by hand later"
+ fi
+ continue
+ fi
+ fi
+ case "${AUTO_RUN}" in
+ '')
+ # prompt user to install/delete/merge changes
+ diff_loop
+ ;;
+ *)
+ # If this is an auto run, make it official
+ echo " *** ${COMPFILE} will remain for your consideration"
+ ;;
+ esac # Auto run test
+ fi # Yes, the files are different
+ fi # Yes, the file still remains to be checked
+done # This is for the for way up there at the beginning of the comparison
+
+echo ''
+echo "*** Comparison complete"
+
+if [ -f "${MTREENEW}" ]; then
+ echo "*** Saving mtree database for future upgrades"
+ test -e "${DESTDIR}${MTREEFILE}" && unlink ${DESTDIR}${MTREEFILE}
+ mv ${MTREENEW} ${DESTDIR}${MTREEFILE}
+fi
+
+echo ''
+
+TEST_FOR_FILES=`find ${TEMPROOT} -type f -size +0 2>/dev/null`
+if [ -n "${TEST_FOR_FILES}" ]; then
+ echo "*** Files that remain for you to merge by hand:"
+ find "${TEMPROOT}" -type f -size +0
+ echo ''
+fi
+
+case "${AUTO_RUN}" in
+'')
+ echo -n "Do you wish to delete what is left of ${TEMPROOT}? [no] "
+ read DEL_TEMPROOT
+
+ case "${DEL_TEMPROOT}" in
+ [yY]*)
+ if delete_temproot; then
+ echo " *** ${TEMPROOT} has been deleted"
+ else
+ echo " *** Unable to delete ${TEMPROOT}"
+ fi
+ ;;
+ *)
+ echo " *** ${TEMPROOT} will remain"
+ ;;
+ esac
+ ;;
+*) ;;
+esac
+
+case "${AUTO_INSTALLED_FILES}" in
+'') ;;
+*)
+ case "${AUTO_RUN}" in
+ '')
+ (
+ echo ''
+ echo '*** You chose the automatic install option for files that did not'
+ echo ' exist on your system. The following were installed for you:'
+ echo "${AUTO_INSTALLED_FILES}"
+ ) | ${PAGER}
+ ;;
+ *)
+ echo ''
+ echo '*** You chose the automatic install option for files that did not'
+ echo ' exist on your system. The following were installed for you:'
+ echo "${AUTO_INSTALLED_FILES}"
+ ;;
+ esac
+ ;;
+esac
+
+case "${AUTO_UPGRADED_FILES}" in
+'') ;;
+*)
+ case "${AUTO_RUN}" in
+ '')
+ (
+ echo ''
+ echo '*** You chose the automatic upgrade option for files that you did'
+ echo ' not alter on your system. The following were upgraded for you:'
+ echo "${AUTO_UPGRADED_FILES}"
+ ) | ${PAGER}
+ ;;
+ *)
+ echo ''
+ echo '*** You chose the automatic upgrade option for files that you did'
+ echo ' not alter on your system. The following were upgraded for you:'
+ echo "${AUTO_UPGRADED_FILES}"
+ ;;
+ esac
+ ;;
+esac
+
+run_it_now () {
+ case "${AUTO_RUN}" in
+ '')
+ unset YES_OR_NO
+ echo ''
+ echo -n ' Would you like to run it now? y or n [n] '
+ read YES_OR_NO
+
+ case "${YES_OR_NO}" in
+ y)
+ echo " Running ${1}"
+ echo ''
+ eval "${1}"
+ ;;
+ ''|n)
+ echo ''
+ echo " *** Cancelled"
+ echo ''
+ echo " Make sure to run ${1} yourself"
+ ;;
+ *)
+ echo ''
+ echo " *** Sorry, I do not understand your answer (${YES_OR_NO})"
+ echo ''
+ echo " Make sure to run ${1} yourself"
+ esac
+ ;;
+ *) ;;
+ esac
+}
+
+case "${NEED_NEWALIASES}" in
+'') ;;
+*)
+ echo ''
+ if [ -n "${DESTDIR}" ]; then
+ echo "*** You installed a new aliases file into ${DESTDIR}/etc/mail, but"
+ echo " the newaliases command is limited to the directories configured"
+ echo " in sendmail.cf. Make sure to create your aliases database by"
+ echo " hand when your sendmail configuration is done."
+ else
+ echo "*** You installed a new aliases file, so make sure that you run"
+ echo " '/usr/bin/newaliases' to rebuild your aliases database"
+ run_it_now '/usr/bin/newaliases'
+ fi
+ ;;
+esac
+
+case "${NEED_CAP_MKDB}" in
+'') ;;
+*)
+ echo ''
+ echo "*** You installed a login.conf file, so make sure that you run"
+ echo " '/usr/bin/cap_mkdb ${DESTDIR}/etc/login.conf'"
+ echo " to rebuild your login.conf database"
+ run_it_now "/usr/bin/cap_mkdb ${DESTDIR}/etc/login.conf"
+ ;;
+esac
+
+case "${NEED_PWD_MKDB}" in
+'') ;;
+*)
+ echo ''
+ echo "*** You installed a new master.passwd file, so make sure that you run"
+ if [ -n "${DESTDIR}" ]; then
+ echo " '/usr/sbin/pwd_mkdb -d ${DESTDIR}/etc -p ${DESTDIR}/etc/master.passwd'"
+ echo " to rebuild your password files"
+ run_it_now "/usr/sbin/pwd_mkdb -d ${DESTDIR}/etc -p ${DESTDIR}/etc/master.passwd"
+ else
+ echo " '/usr/sbin/pwd_mkdb -p /etc/master.passwd'"
+ echo " to rebuild your password files"
+ run_it_now '/usr/sbin/pwd_mkdb -p /etc/master.passwd'
+ fi
+ ;;
+esac
+
+echo ''
+
+if [ -r "${MM_EXIT_SCRIPT}" ]; then
+ . "${MM_EXIT_SCRIPT}"
+fi
+
+case "${COMP_CONFS}" in
+'') ;;
+*)
+ . ${DESTDIR}/etc/defaults/rc.conf
+
+ (echo ''
+ echo "*** Comparing conf files: ${rc_conf_files}"
+
+ for CONF_FILE in ${rc_conf_files}; do
+ if [ -r "${DESTDIR}${CONF_FILE}" ]; then
+ echo ''
+ echo "*** From ${DESTDIR}${CONF_FILE}"
+ echo "*** From ${DESTDIR}/etc/defaults/rc.conf"
+
+ for RC_CONF_VAR in `grep -i ^[a-z] ${DESTDIR}${CONF_FILE} |
+ cut -d '=' -f 1`; do
+ echo ''
+ grep -w ^${RC_CONF_VAR} ${DESTDIR}${CONF_FILE}
+ grep -w ^${RC_CONF_VAR} ${DESTDIR}/etc/defaults/rc.conf ||
+ echo ' * No default variable with this name'
+ done
+ fi
+ done) | ${PAGER}
+ echo ''
+ ;;
+esac
+
+case "${PRE_WORLD}" in
+'') ;;
+*)
+ MAKE_CONF="${SOURCEDIR}/share/examples/etc/make.conf"
+
+ (echo ''
+ echo '*** Comparing make variables'
+ echo ''
+ echo "*** From ${DESTDIR}/etc/make.conf"
+ echo "*** From ${MAKE_CONF}"
+
+ for MAKE_VAR in `grep -i ^[a-z] ${DESTDIR}/etc/make.conf | cut -d '=' -f 1`; do
+ echo ''
+ grep -w ^${MAKE_VAR} ${DESTDIR}/etc/make.conf
+ grep -w ^#${MAKE_VAR} ${MAKE_CONF} ||
+ echo ' * No example variable with this name'
+ done) | ${PAGER}
+ ;;
+esac
+
+exit 0
+
diff --git a/usr.sbin/mixer/Makefile b/usr.sbin/mixer/Makefile
new file mode 100644
index 0000000..deb3539
--- /dev/null
+++ b/usr.sbin/mixer/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= mixer
+MAN= mixer.8
+
+WARNS?= 6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mixer/mixer.8 b/usr.sbin/mixer/mixer.8
new file mode 100644
index 0000000..2117c7c
--- /dev/null
+++ b/usr.sbin/mixer/mixer.8
@@ -0,0 +1,181 @@
+.\" Copyright (c) 1997
+.\" Mike Pritchard <mpp@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. Neither the name of the author 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 MIKE PRITCHARD AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED 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 14, 2008
+.Dt MIXER 8
+.Os
+.Sh NAME
+.Nm mixer
+.Nd set/display soundcard mixer values
+.Sh SYNOPSIS
+.Nm
+.Op Fl f Ar device
+.Op Fl s | S
+.Oo
+.Ar dev
+.Sm off
+.Oo
+.Op Cm + | -
+.Ar lvol
+.Op : Oo Cm + | - Oc Ar rvol
+.Oc
+.Oc
+.Sm on
+.Ar ...
+.Nm
+.Op Fl f Ar device
+.Op Fl s | S
+.Cm recsrc
+.Ar ...
+.Nm
+.Op Fl f Ar device
+.Op Fl s | S
+.Sm off
+.Bro
+.Cm ^ | + | - | =
+.Brc
+.Cm rec
+.Sm on
+.Ar rdev ...
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to set and display soundcard mixer device levels.
+It may
+also be used to start and stop recording from the soundcard.
+The list
+of mixer devices that may be modified are:
+.Pp
+.Bd -ragged -offset indent
+vol, bass, treble, synth, pcm, speaker, line, mic, cd, mix,
+pcm2, rec, igain, ogain, line1, line2, line3, dig1, dig2, dig3,
+phin, phout, video, radio, and monitor.
+.Ed
+.Pp
+Not all mixer devices are available.
+.Pp
+Without any arguments,
+.Nm
+displays the current settings for all supported devices, followed by information
+about the current recording input devices.
+If the
+.Ar dev
+argument is specified,
+.Nm
+displays only the value for that
+.Ar dev .
+.Pp
+To modify the mixer value
+.Ar dev ,
+the optional left and right channel settings of
+.Ar lvol Ns Op : Ns Ar rvol
+may be specified.
+The
+.Ar lvol
+and
+.Ar rvol
+arguments may be from 0 - 100.
+Omitting
+.Ar dev
+and including only the channel settings will change the main volume level.
+.Pp
+If the left or right channel settings are prefixed with
+.Cm +
+or
+.Cm - ,
+the value following will be used as a relative adjustment, modifying the
+current settings by the amount specified.
+.Pp
+If the
+.Fl s
+flag is used, the current mixer values will be displayed in a format suitable
+for use as the command-line arguments to a future invocation of
+.Nm
+(as above).
+.Pp
+The
+.Fl S
+flag provides the above output without mixing field separators.
+.Pp
+To change the recording device you use one of:
+.Bl -tag -width =rec -offset indent
+.It Cm ^rec
+toggles
+.Ar rdev
+of possible recording devices
+.It Cm +rec
+adds
+.Ar rdev
+to possible recording devices
+.It Cm -rec
+removes
+.Ar rdev
+from possible recording devices
+.It Cm =rec
+sets the recording device to
+.Ar rdev
+.El
+.Pp
+The above commands work on an internal mask.
+After all the options
+have been parsed, it will set then read the mask from the sound card.
+This will let you see EXACTLY what the soundcard is using for the
+recording device(s).
+.Pp
+The option recsrc will display the current recording devices.
+.Pp
+The option
+.Fl f
+.Ar device
+will open
+.Ar device
+as the mixer device.
+.Sh FILES
+.Bl -tag -width /dev/mixer -compact
+.It Pa /dev/mixer
+the default mixer device
+.El
+.Sh SEE ALSO
+.Xr cdcontrol 1
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 2.0.5 .
+.Sh AUTHORS
+.An -nosplit
+Original source by
+.An Craig Metz Aq cmetz@thor.tjhsst.edu
+and
+.An Hannu Savolainen .
+Mostly rewritten by
+.An John-Mark Gurney Aq jmg@FreeBSD.org .
+This
+manual page was written by
+.An Mike Pritchard Aq mpp@FreeBSD.org .
diff --git a/usr.sbin/mixer/mixer.c b/usr.sbin/mixer/mixer.c
new file mode 100644
index 0000000..52997bc
--- /dev/null
+++ b/usr.sbin/mixer/mixer.c
@@ -0,0 +1,331 @@
+/*
+ * This is an example of a mixer program for Linux
+ *
+ * updated 1/1/93 to add stereo, level query, broken
+ * devmask kludge - cmetz@thor.tjhsst.edu
+ *
+ * (C) Craig Metz and Hannu Savolainen 1993.
+ *
+ * You may do anything you wish with this program.
+ *
+ * ditto for my modifications (John-Mark Gurney, 1997)
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/soundcard.h>
+
+const char *names[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
+
+static void usage(int devmask, int recmask);
+static int res_name(const char *name, int mask);
+static void print_recsrc(int recsrc, int recmask, int sflag);
+
+static void
+usage(int devmask, int recmask)
+{
+ int i, n;
+
+ printf("usage: mixer [-f device] [-s | -S] [dev [+|-][voll[:[+|-]volr]] ...\n"
+ " mixer [-f device] [-s | -S] recsrc ...\n"
+ " mixer [-f device] [-s | -S] {^|+|-|=}rec rdev ...\n");
+ if (devmask != 0) {
+ printf(" devices: ");
+ for (i = 0, n = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if ((1 << i) & devmask) {
+ if (n)
+ printf(", ");
+ printf("%s", names[i]);
+ n++;
+ }
+ }
+ if (recmask != 0) {
+ printf("\n rec devices: ");
+ for (i = 0, n = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if ((1 << i) & recmask) {
+ if (n)
+ printf(", ");
+ printf("%s", names[i]);
+ n++;
+ }
+ }
+ printf("\n");
+ exit(1);
+}
+
+static int
+res_name(const char *name, int mask)
+{
+ int foo;
+
+ for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++)
+ if ((1 << foo) & mask && strcmp(names[foo], name) == 0)
+ break;
+
+ return (foo == SOUND_MIXER_NRDEVICES ? -1 : foo);
+}
+
+static void
+print_recsrc(int recsrc, int recmask, int sflag)
+{
+ int i, n;
+
+ if (recmask == 0)
+ return;
+
+ if (!sflag)
+ printf("Recording source: ");
+
+ for (i = 0, n = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if ((1 << i) & recsrc) {
+ if (sflag)
+ printf("%srec ", n ? " +" : "=");
+ else if (n)
+ printf(", ");
+ printf("%s", names[i]);
+ n++;
+ }
+ if (!sflag)
+ printf("\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+ char mixer[PATH_MAX] = "/dev/mixer";
+ char lstr[5], rstr[5];
+ char *name, *eptr;
+ int devmask = 0, recmask = 0, recsrc = 0, orecsrc;
+ int dusage = 0, drecsrc = 0, sflag = 0, Sflag = 0;
+ int l, r, lrel, rrel;
+ int ch, foo, bar, baz, dev, m, n, t;
+
+ if ((name = strdup(basename(argv[0]))) == NULL)
+ err(1, "strdup()");
+ if (strncmp(name, "mixer", 5) == 0 && name[5] != '\0') {
+ n = strtol(name + 5, &eptr, 10) - 1;
+ if (n > 0 && *eptr == '\0')
+ snprintf(mixer, PATH_MAX - 1, "/dev/mixer%d", n);
+ }
+ free(name);
+ name = mixer;
+
+ n = 1;
+ for (;;) {
+ if (n >= argc || *argv[n] != '-')
+ break;
+ if (strlen(argv[n]) != 2) {
+ if (strcmp(argv[n] + 1, "rec") != 0)
+ dusage = 1;
+ break;
+ }
+ ch = *(argv[n] + 1);
+ if (ch == 'f' && n < argc - 1) {
+ name = argv[n + 1];
+ n += 2;
+ } else if (ch == 's') {
+ sflag = 1;
+ n++;
+ } else if (ch == 'S') {
+ Sflag = 1;
+ n++;
+ } else {
+ dusage = 1;
+ break;
+ }
+ }
+ if (sflag && Sflag)
+ dusage = 1;
+
+ argc -= n - 1;
+ argv += n - 1;
+
+ if ((baz = open(name, O_RDWR)) < 0)
+ err(1, "%s", name);
+ if (ioctl(baz, SOUND_MIXER_READ_DEVMASK, &devmask) == -1)
+ err(1, "SOUND_MIXER_READ_DEVMASK");
+ if (ioctl(baz, SOUND_MIXER_READ_RECMASK, &recmask) == -1)
+ err(1, "SOUND_MIXER_READ_RECMASK");
+ if (ioctl(baz, SOUND_MIXER_READ_RECSRC, &recsrc) == -1)
+ err(1, "SOUND_MIXER_READ_RECSRC");
+ orecsrc = recsrc;
+
+ if (argc == 1 && dusage == 0) {
+ for (foo = 0, n = 0; foo < SOUND_MIXER_NRDEVICES; foo++) {
+ if (!((1 << foo) & devmask))
+ continue;
+ if (ioctl(baz, MIXER_READ(foo),&bar) == -1) {
+ warn("MIXER_READ");
+ continue;
+ }
+ if (Sflag || sflag) {
+ printf("%s%s%c%d:%d", n ? " " : "",
+ names[foo], Sflag ? ':' : ' ',
+ bar & 0x7f, (bar >> 8) & 0x7f);
+ n++;
+ } else
+ printf("Mixer %-8s is currently set to "
+ "%3d:%d\n", names[foo], bar & 0x7f,
+ (bar >> 8) & 0x7f);
+ }
+ if (n && recmask)
+ printf(" ");
+ print_recsrc(recsrc, recmask, Sflag || sflag);
+ return (0);
+ }
+
+ argc--;
+ argv++;
+
+ n = 0;
+ while (argc > 0 && dusage == 0) {
+ if (strcmp("recsrc", *argv) == 0) {
+ drecsrc = 1;
+ argc--;
+ argv++;
+ continue;
+ } else if (argc > 1 && strcmp("rec", *argv + 1) == 0) {
+ if (**argv != '+' && **argv != '-' &&
+ **argv != '=' && **argv != '^') {
+ warnx("unknown modifier: %c", **argv);
+ dusage = 1;
+ break;
+ }
+ if ((dev = res_name(argv[1], recmask)) == -1) {
+ warnx("unknown recording device: %s", argv[1]);
+ dusage = 1;
+ break;
+ }
+ switch (**argv) {
+ case '+':
+ recsrc |= (1 << dev);
+ break;
+ case '-':
+ recsrc &= ~(1 << dev);
+ break;
+ case '=':
+ recsrc = (1 << dev);
+ break;
+ case '^':
+ recsrc ^= (1 << dev);
+ break;
+ }
+ drecsrc = 1;
+ argc -= 2;
+ argv += 2;
+ continue;
+ }
+
+ if ((t = sscanf(*argv, "%d:%d", &l, &r)) > 0)
+ dev = 0;
+ else if ((dev = res_name(*argv, devmask)) == -1) {
+ warnx("unknown device: %s", *argv);
+ dusage = 1;
+ break;
+ }
+
+ lrel = rrel = 0;
+ if (argc > 1) {
+ m = sscanf(argv[1], "%7[^:]:%7s", lstr, rstr);
+ if (m > 0) {
+ if (*lstr == '+' || *lstr == '-')
+ lrel = rrel = 1;
+ l = strtol(lstr, NULL, 10);
+ }
+ if (m > 1) {
+ if (*rstr == '+' || *rstr == '-')
+ rrel = 1;
+ r = strtol(rstr, NULL, 10);
+ }
+ }
+
+ switch (argc > 1 ? m : t) {
+ case 0:
+ if (ioctl(baz, MIXER_READ(dev), &bar) == -1) {
+ warn("MIXER_READ");
+ argc--;
+ argv++;
+ continue;
+ }
+ if (Sflag || sflag) {
+ printf("%s%s%c%d:%d", n ? " " : "",
+ names[dev], Sflag ? ':' : ' ',
+ bar & 0x7f, (bar >> 8) & 0x7f);
+ n++;
+ } else
+ printf("Mixer %-8s is currently set to "
+ "%3d:%d\n", names[dev], bar & 0x7f,
+ (bar >> 8) & 0x7f);
+
+ argc--;
+ argv++;
+ break;
+ case 1:
+ r = l;
+ /* FALLTHROUGH */
+ case 2:
+ if (ioctl(baz, MIXER_READ(dev), &bar) == -1) {
+ warn("MIXER_READ");
+ argc--;
+ argv++;
+ continue;
+ }
+
+ if (lrel)
+ l = (bar & 0x7f) + l;
+ if (rrel)
+ r = ((bar >> 8) & 0x7f) + r;
+
+ if (l < 0)
+ l = 0;
+ else if (l > 100)
+ l = 100;
+ if (r < 0)
+ r = 0;
+ else if (r > 100)
+ r = 100;
+
+ if (!Sflag)
+ printf("Setting the mixer %s from %d:%d to "
+ "%d:%d.\n", names[dev], bar & 0x7f,
+ (bar >> 8) & 0x7f, l, r);
+
+ l |= r << 8;
+ if (ioctl(baz, MIXER_WRITE(dev), &l) == -1)
+ warn("WRITE_MIXER");
+
+ argc -= 2;
+ argv += 2;
+ break;
+ }
+ }
+
+ if (dusage) {
+ close(baz);
+ usage(devmask, recmask);
+ /* NOTREACHED */
+ }
+
+ if (orecsrc != recsrc) {
+ if (ioctl(baz, SOUND_MIXER_WRITE_RECSRC, &recsrc) == -1)
+ err(1, "SOUND_MIXER_WRITE_RECSRC");
+ if (ioctl(baz, SOUND_MIXER_READ_RECSRC, &recsrc) == -1)
+ err(1, "SOUND_MIXER_READ_RECSRC");
+ }
+
+ if (drecsrc)
+ print_recsrc(recsrc, recmask, Sflag || sflag);
+
+ close(baz);
+
+ return (0);
+}
diff --git a/usr.sbin/mld6query/Makefile b/usr.sbin/mld6query/Makefile
new file mode 100644
index 0000000..0e9da49
--- /dev/null
+++ b/usr.sbin/mld6query/Makefile
@@ -0,0 +1,23 @@
+# 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$
+
+PROG= mld6query
+MAN= mld6query.8
+SRCS= mld6.c
+
+CFLAGS+= -DIPSEC -DUSE_RFC2292BIS
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mld6query/mld6.c b/usr.sbin/mld6query/mld6.c
new file mode 100644
index 0000000..1f73203
--- /dev/null
+++ b/usr.sbin/mld6query/mld6.c
@@ -0,0 +1,351 @@
+/* $KAME: mld6.c,v 1.15 2003/04/02 11:29:54 suz Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (C) 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.
+ */
+#include <sys/param.h>
+#include <sys/uio.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <ifaddrs.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+
+#include <arpa/inet.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <err.h>
+
+/* portability with older KAME headers */
+#ifndef MLD_LISTENER_QUERY
+#define MLD_LISTENER_QUERY MLD6_LISTENER_QUERY
+#define MLD_LISTENER_REPORT MLD6_LISTENER_REPORT
+#define MLD_LISTENER_DONE MLD6_LISTENER_DONE
+#define MLD_MTRACE_RESP MLD6_MTRACE_RESP
+#define MLD_MTRACE MLD6_MTRACE
+#define mld_hdr mld6_hdr
+#define mld_type mld6_type
+#define mld_code mld6_code
+#define mld_cksum mld6_cksum
+#define mld_maxdelay mld6_maxdelay
+#define mld_reserved mld6_reserved
+#define mld_addr mld6_addr
+#endif
+#ifndef IP6OPT_ROUTER_ALERT
+#define IP6OPT_ROUTER_ALERT IP6OPT_RTALERT
+#endif
+
+struct msghdr m;
+struct sockaddr_in6 dst;
+struct mld_hdr mldh;
+struct in6_addr maddr = IN6ADDR_ANY_INIT, any = IN6ADDR_ANY_INIT;
+struct ipv6_mreq mreq;
+u_short ifindex;
+int s;
+
+#define QUERY_RESPONSE_INTERVAL 10000
+
+void make_msg(int index, struct in6_addr *addr, u_int type);
+void usage(void);
+void dump(int);
+void quit(int);
+
+int
+main(int argc, char *argv[])
+{
+ int i;
+ struct icmp6_filter filt;
+ u_int hlim = 1;
+ fd_set fdset;
+ struct itimerval itimer;
+ u_int type;
+ int ch;
+
+ type = MLD_LISTENER_QUERY;
+ while ((ch = getopt(argc, argv, "dr")) != -1) {
+ switch (ch) {
+ case 'd':
+ type = MLD_LISTENER_DONE;
+ break;
+ case 'r':
+ type = MLD_LISTENER_REPORT;
+ break;
+ default:
+ usage();
+ /*NOTREACHED*/
+ }
+ }
+
+ argv += optind;
+ argc -= optind;
+
+ if (argc != 1 && argc != 2)
+ usage();
+
+ ifindex = (u_short)if_nametoindex(argv[0]);
+ if (ifindex == 0)
+ usage();
+ if (argc == 2 && inet_pton(AF_INET6, argv[1], &maddr) != 1)
+ usage();
+
+ if ((s = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0)
+ err(1, "socket");
+
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &hlim,
+ sizeof(hlim)) == -1)
+ err(1, "setsockopt(IPV6_MULTICAST_HOPS)");
+
+ mreq.ipv6mr_multiaddr = any;
+ mreq.ipv6mr_interface = ifindex;
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq,
+ sizeof(mreq)) == -1)
+ err(1, "setsockopt(IPV6_JOIN_GROUP)");
+
+ ICMP6_FILTER_SETBLOCKALL(&filt);
+ ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_QUERY, &filt);
+ ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_REPORT, &filt);
+ ICMP6_FILTER_SETPASS(ICMP6_MEMBERSHIP_REDUCTION, &filt);
+ if (setsockopt(s, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
+ sizeof(filt)) < 0)
+ err(1, "setsockopt(ICMP6_FILTER)");
+
+ make_msg(ifindex, &maddr, type);
+
+ if (sendmsg(s, &m, 0) < 0)
+ err(1, "sendmsg");
+
+ itimer.it_value.tv_sec = QUERY_RESPONSE_INTERVAL / 1000;
+ itimer.it_interval.tv_sec = 0;
+ itimer.it_interval.tv_usec = 0;
+ itimer.it_value.tv_usec = 0;
+
+ (void)signal(SIGALRM, quit);
+ (void)setitimer(ITIMER_REAL, &itimer, NULL);
+
+ FD_ZERO(&fdset);
+ if (s >= FD_SETSIZE)
+ errx(1, "descriptor too big");
+ for (;;) {
+ FD_SET(s, &fdset);
+ if ((i = select(s + 1, &fdset, NULL, NULL, NULL)) < 0)
+ perror("select");
+ if (i == 0)
+ continue;
+ else
+ dump(s);
+ }
+}
+
+void
+make_msg(int index, struct in6_addr *addr, u_int type)
+{
+ static struct iovec iov[2];
+ static u_char *cmsgbuf;
+ int cmsglen, hbhlen = 0;
+#ifdef USE_RFC2292BIS
+ void *hbhbuf = NULL, *optp = NULL;
+ int currentlen;
+#else
+ u_int8_t raopt[IP6OPT_RTALERT_LEN];
+#endif
+ struct in6_pktinfo *pi;
+ struct cmsghdr *cmsgp;
+ u_short rtalert_code = htons(IP6OPT_RTALERT_MLD);
+ struct ifaddrs *ifa, *ifap;
+ struct in6_addr src;
+
+ dst.sin6_len = sizeof(dst);
+ dst.sin6_family = AF_INET6;
+ if (IN6_IS_ADDR_UNSPECIFIED(addr)) {
+ if (inet_pton(AF_INET6, "ff02::1", &dst.sin6_addr) != 1)
+ errx(1, "inet_pton failed");
+ }
+ else
+ dst.sin6_addr = *addr;
+ m.msg_name = (caddr_t)&dst;
+ m.msg_namelen = dst.sin6_len;
+ iov[0].iov_base = (caddr_t)&mldh;
+ iov[0].iov_len = sizeof(mldh);
+ m.msg_iov = iov;
+ m.msg_iovlen = 1;
+
+ bzero(&mldh, sizeof(mldh));
+ mldh.mld_type = type & 0xff;
+ mldh.mld_maxdelay = htons(QUERY_RESPONSE_INTERVAL);
+ mldh.mld_addr = *addr;
+
+ /* MLD packet should be advertised from linklocal address */
+ getifaddrs(&ifa);
+ for (ifap = ifa; ifap; ifap = ifap->ifa_next) {
+ if (index != if_nametoindex(ifap->ifa_name))
+ continue;
+
+ if (ifap->ifa_addr->sa_family != AF_INET6)
+ continue;
+ if (!IN6_IS_ADDR_LINKLOCAL(&((struct sockaddr_in6 *)
+ ifap->ifa_addr)->sin6_addr))
+ continue;
+ break;
+ }
+ if (ifap == NULL)
+ errx(1, "no linkocal address is available");
+ memcpy(&src, &((struct sockaddr_in6 *)ifap->ifa_addr)->sin6_addr,
+ sizeof(src));
+ freeifaddrs(ifa);
+#ifdef __KAME__
+ /* remove embedded ifindex */
+ src.s6_addr[2] = src.s6_addr[3] = 0;
+#endif
+
+#ifdef USE_RFC2292BIS
+ if ((hbhlen = inet6_opt_init(NULL, 0)) == -1)
+ errx(1, "inet6_opt_init(0) failed");
+ if ((hbhlen = inet6_opt_append(NULL, 0, hbhlen, IP6OPT_ROUTER_ALERT, 2,
+ 2, NULL)) == -1)
+ errx(1, "inet6_opt_append(0) failed");
+ if ((hbhlen = inet6_opt_finish(NULL, 0, hbhlen)) == -1)
+ errx(1, "inet6_opt_finish(0) failed");
+ cmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) + CMSG_SPACE(hbhlen);
+#else
+ hbhlen = sizeof(raopt);
+ cmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+ inet6_option_space(hbhlen);
+#endif
+
+ if ((cmsgbuf = malloc(cmsglen)) == NULL)
+ errx(1, "can't allocate enough memory for cmsg");
+ cmsgp = (struct cmsghdr *)cmsgbuf;
+ m.msg_control = (caddr_t)cmsgbuf;
+ m.msg_controllen = cmsglen;
+ /* specify the outgoing interface */
+ cmsgp->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+ cmsgp->cmsg_level = IPPROTO_IPV6;
+ cmsgp->cmsg_type = IPV6_PKTINFO;
+ pi = (struct in6_pktinfo *)CMSG_DATA(cmsgp);
+ pi->ipi6_ifindex = index;
+ memcpy(&pi->ipi6_addr, &src, sizeof(pi->ipi6_addr));
+ /* specifiy to insert router alert option in a hop-by-hop opt hdr. */
+ cmsgp = CMSG_NXTHDR(&m, cmsgp);
+#ifdef USE_RFC2292BIS
+ cmsgp->cmsg_len = CMSG_LEN(hbhlen);
+ cmsgp->cmsg_level = IPPROTO_IPV6;
+ cmsgp->cmsg_type = IPV6_HOPOPTS;
+ hbhbuf = CMSG_DATA(cmsgp);
+ if ((currentlen = inet6_opt_init(hbhbuf, hbhlen)) == -1)
+ errx(1, "inet6_opt_init(len = %d) failed", hbhlen);
+ if ((currentlen = inet6_opt_append(hbhbuf, hbhlen, currentlen,
+ IP6OPT_ROUTER_ALERT, 2,
+ 2, &optp)) == -1)
+ errx(1, "inet6_opt_append(currentlen = %d, hbhlen = %d) failed",
+ currentlen, hbhlen);
+ (void)inet6_opt_set_val(optp, 0, &rtalert_code, sizeof(rtalert_code));
+ if ((currentlen = inet6_opt_finish(hbhbuf, hbhlen, currentlen)) == -1)
+ errx(1, "inet6_opt_finish(buf) failed");
+#else /* old advanced API */
+ if (inet6_option_init((void *)cmsgp, &cmsgp, IPV6_HOPOPTS))
+ errx(1, "inet6_option_init failed\n");
+ raopt[0] = IP6OPT_ROUTER_ALERT;
+ raopt[1] = IP6OPT_RTALERT_LEN - 2;
+ memcpy(&raopt[2], (caddr_t)&rtalert_code, sizeof(u_short));
+ if (inet6_option_append(cmsgp, raopt, 4, 0))
+ errx(1, "inet6_option_append failed\n");
+#endif
+}
+
+void
+dump(int s)
+{
+ int i;
+ struct mld_hdr *mld;
+ u_char buf[1024];
+ struct sockaddr_in6 from;
+ int from_len = sizeof(from);
+ char ntop_buf[256];
+
+ if ((i = recvfrom(s, buf, sizeof(buf), 0,
+ (struct sockaddr *)&from,
+ &from_len)) < 0)
+ return;
+
+ if (i < sizeof(struct mld_hdr)) {
+ printf("too short!\n");
+ return;
+ }
+
+ mld = (struct mld_hdr *)buf;
+
+ printf("from %s, ", inet_ntop(AF_INET6, &from.sin6_addr,
+ ntop_buf, sizeof(ntop_buf)));
+
+ switch (mld->mld_type) {
+ case ICMP6_MEMBERSHIP_QUERY:
+ printf("type=Multicast Listener Query, ");
+ break;
+ case ICMP6_MEMBERSHIP_REPORT:
+ printf("type=Multicast Listener Report, ");
+ break;
+ case ICMP6_MEMBERSHIP_REDUCTION:
+ printf("type=Multicast Listener Done, ");
+ break;
+ }
+ printf("addr=%s\n", inet_ntop(AF_INET6, &mld->mld_addr,
+ ntop_buf, sizeof(ntop_buf)));
+
+ fflush(stdout);
+}
+
+/* ARGSUSED */
+void
+quit(int signum)
+{
+ mreq.ipv6mr_multiaddr = any;
+ mreq.ipv6mr_interface = ifindex;
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_LEAVE_GROUP, &mreq,
+ sizeof(mreq)) == -1)
+ err(1, "setsockopt(IPV6_LEAVE_GROUP)");
+
+ exit(0);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: mld6query ifname [addr]\n");
+ exit(1);
+}
diff --git a/usr.sbin/mld6query/mld6query.8 b/usr.sbin/mld6query/mld6query.8
new file mode 100644
index 0000000..1516c2e
--- /dev/null
+++ b/usr.sbin/mld6query/mld6query.8
@@ -0,0 +1,91 @@
+.\" $KAME: mld6query.8,v 1.5 2000/12/04 06:28:23 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 MLD6QUERY 8
+.Os
+.\"
+.Sh NAME
+.Nm mld6query
+.Nd send multicast listener query
+.\"
+.Sh SYNOPSIS
+.Nm
+.Op Fl dr
+.Ar intface
+.Op Ar maddr
+.\"
+.Sh DESCRIPTION
+The
+.Nm
+utility sends an IPv6 multicast listener discovery (MLD) query packet toward
+the specified multicast address,
+.Ar maddr ,
+toward interface
+.Ar intface .
+If you omit
+.Ar maddr ,
+linklocal all nodes multicast address(ff02::1) is used.
+.Pp
+After sending a query,
+.Nm
+waits for replies for at most 10 seconds.
+If a reply is returned,
+.Nm
+prints it with its type and then waits for another reply.
+.Pp
+This program is provided only for debugging.
+It is not necessary for normal use.
+.Pp
+With
+.Fl d ,
+.Nm
+will transmit MLD done packet instead of MLD query packet.
+With
+.Fl r ,
+similarly, MLD report packet will be transmitted.
+.Fl dr
+options are for debugging purposes only.
+.\"
+.Sh EXIT STATUS
+The program exits with 0 on success, non-zero on failures.
+.\"
+.\" .Sh SEE ALSO
+.\"
+.Sh HISTORY
+The
+.Nm
+utility first appeared in WIDE/KAME IPv6 protocol stack kit.
+.Sh BUGS
+The
+.Nm
+utility does not take care of multicast addresses which have non link-local
+scope.
diff --git a/usr.sbin/mlxcontrol/Makefile b/usr.sbin/mlxcontrol/Makefile
new file mode 100644
index 0000000..95964a5
--- /dev/null
+++ b/usr.sbin/mlxcontrol/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= mlxcontrol
+MAN= mlxcontrol.8
+SRCS= command.c config.c interface.c util.c
+
+CFLAGS+= -I${.CURDIR}/../../sys
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mlxcontrol/command.c b/usr.sbin/mlxcontrol/command.c
new file mode 100644
index 0000000..a7dc88f
--- /dev/null
+++ b/usr.sbin/mlxcontrol/command.c
@@ -0,0 +1,712 @@
+/*-
+ * Copyright (c) 1999 Michael Smith
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <err.h>
+
+#include <dev/mlx/mlxio.h>
+#include <dev/mlx/mlxreg.h>
+
+#include "mlxcontrol.h"
+
+static int cmd_status(int argc, char *argv[]);
+static int cmd_rescan(int argc, char *argv[]);
+static int cmd_detach(int argc, char *argv[]);
+static int cmd_check(int argc, char *argv[]);
+static int cmd_rebuild(int argc, char *argv[]);
+#ifdef SUPPORT_PAUSE
+static int cmd_pause(int argc, char *argv[]);
+#endif
+static int cmd_help(int argc, char *argv[]);
+
+extern int cmd_config(int argc, char *argv[]);
+
+
+struct
+{
+ char *cmd;
+ int (*func)(int argc, char *argv[]);
+ char *desc;
+ char *text;
+} commands[] = {
+ {"status", cmd_status,
+ "displays device status",
+ " status [-qv] [<drive>...]\n"
+ " Display status for <drive> or all drives if none is listed\n"
+ " -q Suppress output.\n"
+ " -v Display verbose information.\n"
+ " Returns 0 if all drives tested are online, 1 if one or more are\n"
+ " critical, and 2 if one or more are offline."},
+ {"rescan", cmd_rescan,
+ "scan for new system drives",
+ " rescan <controller> [<controller>...]\n"
+ " Rescan <controller> for system drives.\n"
+ " rescan -a\n"
+ " Rescan all controllers for system drives."},
+ {"detach", cmd_detach,
+ "detach system drives",
+ " detach <drive> [<drive>...]\n"
+ " Detaches <drive> from the controller.\n"
+ " detach -a <controller>\n"
+ " Detaches all drives on <controller>."},
+ {"check", cmd_check,
+ "consistency-check a system drive",
+ " check <drive>\n"
+ " Requests a check and rebuild of the parity information on <drive>.\n"
+ " Note that each controller can only check one system drive at a time."},
+ {"rebuild", cmd_rebuild,
+ "initiate a rebuild of a dead physical drive",
+ " rebuild <controller> <physdrive>\n"
+ " All system drives using space on the physical drive <physdrive>\n"
+ " are rebuilt, reconstructing all data on the drive.\n"
+ " Note that each controller can only perform one rebuild at a time."},
+#ifdef SUPPORT_PAUSE
+ {"pause", cmd_pause,
+ "pauses controller channels",
+ " pause [-t <howlong>] [-d <delay>] <controller> [<channel>...]\n"
+ " Pauses SCSI I/O on <channel> and <controller>. If no channel is specified,\n"
+ " all channels are paused.\n"
+ " <howlong> How long (seconds) to pause for (default 30).\n"
+ " <delay> How long (seconds) to wait before pausing (default 30).\n"
+ " pause <controller> -c\n"
+ " Cancels any pending pause operation on <controller>."},
+#endif
+ {"config", cmd_config,
+ "examine and update controller configuration",
+ " config <controller>\n"
+ " Print configuration for <controller>."},
+ {"help", cmd_help,
+ "give help on usage",
+ ""},
+ {NULL, NULL, NULL, NULL}
+};
+
+/********************************************************************************
+ * Command dispatch and global options parsing.
+ */
+
+int
+main(int argc, char *argv[])
+{
+ int ch, i, oargc;
+ char **oargv;
+
+ oargc = argc;
+ oargv = argv;
+ while ((ch = getopt(argc, argv, "")) != -1)
+ switch(ch) {
+ default:
+ return(cmd_help(0, NULL));
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 0)
+ for (i = 0; commands[i].cmd != NULL; i++)
+ if (!strcmp(argv[0], commands[i].cmd))
+ return(commands[i].func(argc, argv));
+
+ return(cmd_help(oargc, oargv));
+}
+
+/********************************************************************************
+ * Helptext output
+ */
+static int
+cmd_help(int argc, char *argv[])
+{
+ int i;
+
+ if (argc > 1)
+ for (i = 0; commands[i].cmd != NULL; i++)
+ if (!strcmp(argv[1], commands[i].cmd)) {
+ fprintf(stderr, "%s\n", commands[i].text);
+ fflush(stderr);
+ return(0);
+ }
+
+ if (argv != NULL)
+ fprintf(stderr, "Unknown command '%s'.\n", argv[1]);
+ fprintf(stderr, "Valid commands are:\n");
+ for (i = 0; commands[i].cmd != NULL; i++)
+ fprintf(stderr, " %-20s %s\n", commands[i].cmd, commands[i].desc);
+ fflush(stderr);
+ return(0);
+}
+
+/********************************************************************************
+ * Status output
+ *
+ * status [-qv] [<device> ...]
+ * Prints status for <device>, or all if none listed.
+ *
+ * -q Suppresses output, command returns 0 if devices are OK, 1 if one or
+ * more devices are critical, 2 if one or more devices are offline.
+ */
+static struct mlx_rebuild_status rs;
+static int rs_ctrlr = -1;
+static int status_result = 0;
+
+/* XXX more verbosity! */
+static void
+status_print(int unit, void *arg)
+{
+ int verbosity = *(int *)arg;
+ int fd, result, ctrlr, sysdrive, statvalid;
+
+ /* Find which controller and what system drive we are */
+ statvalid = 0;
+ if (mlxd_find_ctrlr(unit, &ctrlr, &sysdrive)) {
+ warnx("couldn't get controller/drive for %s", drivepath(unit));
+ } else {
+ /* If we don't have rebuild stats for this controller, get them */
+ if (rs_ctrlr == ctrlr) {
+ statvalid = 1;
+ } else {
+ if ((fd = open(ctrlrpath(ctrlr), 0)) < 0) {
+ warn("can't open %s", ctrlrpath(ctrlr));
+ } else {
+ if (ioctl(fd, MLX_REBUILDSTAT, &rs) < 0) {
+ warn("ioctl MLX_REBUILDSTAT");
+ } else {
+ rs_ctrlr = ctrlr;
+ statvalid = 1;
+ }
+ close(fd);
+ }
+ }
+ }
+
+ /* Get the device */
+ if ((fd = open(drivepath(unit), 0)) < 0) {
+ warn("can't open %s", drivepath(unit));
+ return;
+ }
+
+ /* Get its status */
+ if (ioctl(fd, MLXD_STATUS, &result) < 0) {
+ warn("ioctl MLXD_STATUS");
+ } else {
+ switch(result) {
+ case MLX_SYSD_ONLINE:
+ if (verbosity > 0)
+ printf("%s: online", drivename(unit));
+ break;
+ case MLX_SYSD_CRITICAL:
+ if (verbosity > 0)
+ printf("%s: critical", drivename(unit));
+ if (status_result < 1)
+ status_result = 1;
+ break;
+ case MLX_SYSD_OFFLINE:
+ if (verbosity > 0)
+ printf("%s: offline", drivename(unit));
+ if (status_result < 2)
+ status_result = 2;
+ break;
+ default:
+ if (verbosity > 0) {
+ printf("%s: unknown status 0x%x", drivename(unit), result);
+ }
+ }
+ if (verbosity > 0) {
+ /* rebuild/check in progress on this drive? */
+ if (statvalid && (rs_ctrlr == ctrlr) &&
+ (rs.rs_drive == sysdrive) && (rs.rs_code != MLX_REBUILDSTAT_IDLE)) {
+ switch(rs.rs_code) {
+ case MLX_REBUILDSTAT_REBUILDCHECK:
+ printf(" [consistency check");
+ break;
+ case MLX_REBUILDSTAT_ADDCAPACITY:
+ printf(" [add capacity");
+ break;
+ case MLX_REBUILDSTAT_ADDCAPACITYINIT:
+ printf(" [add capacity init");
+ break;
+ default:
+ printf(" [unknown operation");
+ }
+ printf(": %d/%d, %d%% complete]",
+ rs.rs_remaining, rs.rs_size,
+ ((rs.rs_size - rs.rs_remaining) / (rs.rs_size / 100)));
+ }
+ printf("\n");
+ }
+ }
+ close(fd);
+}
+
+static struct
+{
+ int hwid;
+ char *name;
+} mlx_controller_names[] = {
+ {0x01, "960P/PD"},
+ {0x02, "960PL"},
+ {0x10, "960PG"},
+ {0x11, "960PJ"},
+ {0x12, "960PR"},
+ {0x13, "960PT"},
+ {0x14, "960PTL0"},
+ {0x15, "960PRL"},
+ {0x16, "960PTL1"},
+ {0x20, "1100PVX"},
+ {-1, NULL}
+};
+
+static void
+controller_print(int unit, void *arg)
+{
+ struct mlx_enquiry2 enq;
+ struct mlx_phys_drv pd;
+ int verbosity = *(int *)arg;
+ static char buf[80];
+ char *model;
+ int i, channel, target;
+
+ if (verbosity == 0)
+ return;
+
+ /* fetch and print controller data */
+ if (mlx_enquiry(unit, &enq)) {
+ printf("mlx%d: error submitting ENQUIRY2\n", unit);
+ } else {
+
+ for (i = 0, model = NULL; mlx_controller_names[i].name != NULL; i++) {
+ if ((enq.me_hardware_id & 0xff) == mlx_controller_names[i].hwid) {
+ model = mlx_controller_names[i].name;
+ break;
+ }
+ }
+ if (model == NULL) {
+ sprintf(buf, " model 0x%x", enq.me_hardware_id & 0xff);
+ model = buf;
+ }
+
+ printf("mlx%d: DAC%s, %d channel%s, firmware %d.%02d-%c-%02d, %dMB RAM\n",
+ unit, model,
+ enq.me_actual_channels,
+ enq.me_actual_channels > 1 ? "s" : "",
+ enq.me_firmware_id & 0xff,
+ (enq.me_firmware_id >> 8) & 0xff,
+ (enq.me_firmware_id >> 16),
+ (enq.me_firmware_id >> 24) & 0xff,
+ enq.me_mem_size / (1024 * 1024));
+
+ if (verbosity > 1) {
+ printf(" Hardware ID 0x%08x\n", enq.me_hardware_id);
+ printf(" Firmware ID 0x%08x\n", enq.me_firmware_id);
+ printf(" Configured/Actual channels %d/%d\n", enq.me_configured_channels,
+ enq.me_actual_channels);
+ printf(" Max Targets %d\n", enq.me_max_targets);
+ printf(" Max Tags %d\n", enq.me_max_tags);
+ printf(" Max System Drives %d\n", enq.me_max_sys_drives);
+ printf(" Max Arms %d\n", enq.me_max_arms);
+ printf(" Max Spans %d\n", enq.me_max_spans);
+ printf(" DRAM/cache/flash/NVRAM size %d/%d/%d/%d\n", enq.me_mem_size,
+ enq.me_cache_size, enq.me_flash_size, enq.me_nvram_size);
+ printf(" DRAM type %d\n", enq.me_mem_type);
+ printf(" Clock Speed %dns\n", enq.me_clock_speed);
+ printf(" Hardware Speed %dns\n", enq.me_hardware_speed);
+ printf(" Max Commands %d\n", enq.me_max_commands);
+ printf(" Max SG Entries %d\n", enq.me_max_sg);
+ printf(" Max DP %d\n", enq.me_max_dp);
+ printf(" Max IOD %d\n", enq.me_max_iod);
+ printf(" Max Comb %d\n", enq.me_max_comb);
+ printf(" Latency %ds\n", enq.me_latency);
+ printf(" SCSI Timeout %ds\n", enq.me_scsi_timeout);
+ printf(" Min Free Lines %d\n", enq.me_min_freelines);
+ printf(" Rate Constant %d\n", enq.me_rate_const);
+ printf(" MAXBLK %d\n", enq.me_maxblk);
+ printf(" Blocking Factor %d sectors\n", enq.me_blocking_factor);
+ printf(" Cache Line Size %d blocks\n", enq.me_cacheline);
+ printf(" SCSI Capability %s%dMHz, %d bit\n",
+ enq.me_scsi_cap & (1<<4) ? "differential " : "",
+ (1 << ((enq.me_scsi_cap >> 2) & 3)) * 10,
+ 8 << (enq.me_scsi_cap & 0x3));
+ printf(" Firmware Build Number %d\n", enq.me_firmware_build);
+ printf(" Fault Management Type %d\n", enq.me_fault_mgmt_type);
+#if 0
+ printf(" Features %b\n", enq.me_firmware_features,
+ "\20\4Background Init\3Read Ahead\2MORE\1Cluster\n");
+#endif
+ }
+
+ /* fetch and print physical drive data */
+ for (channel = 0; channel < enq.me_configured_channels; channel++) {
+ for (target = 0; target < enq.me_max_targets; target++) {
+ if ((mlx_get_device_state(unit, channel, target, &pd) == 0) &&
+ (pd.pd_flags1 & MLX_PHYS_DRV_PRESENT)) {
+ mlx_print_phys_drv(&pd, channel, target, " ", verbosity - 1);
+ if (verbosity > 1) {
+ /* XXX print device statistics? */
+ }
+ }
+ }
+ }
+ }
+}
+
+static int
+cmd_status(int argc, char *argv[])
+{
+ int ch, verbosity = 1, i, unit;
+
+ optreset = 1;
+ optind = 1;
+ while ((ch = getopt(argc, argv, "qv")) != -1)
+ switch(ch) {
+ case 'q':
+ verbosity = 0;
+ break;
+ case 'v':
+ verbosity = 2;
+ break;
+ default:
+ return(cmd_help(argc, argv));
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1) {
+ mlx_foreach(controller_print, &verbosity);
+ mlxd_foreach(status_print, &verbosity);
+ } else {
+ for (i = 0; i < argc; i++) {
+ if ((unit = driveunit(argv[i])) == -1) {
+ warnx("'%s' is not a valid drive", argv[i]);
+ } else {
+ status_print(unit, &verbosity);
+ }
+ }
+ }
+ return(status_result);
+}
+
+/********************************************************************************
+ * Recscan for system drives on one or more controllers.
+ *
+ * rescan <controller> [<controller>...]
+ * rescan -a
+ */
+static void
+rescan_ctrlr(int unit, void *junk)
+{
+ int fd;
+
+ /* Get the device */
+ if ((fd = open(ctrlrpath(unit), 0)) < 0) {
+ warn("can't open %s", ctrlrpath(unit));
+ return;
+ }
+
+ if (ioctl(fd, MLX_RESCAN_DRIVES) < 0)
+ warn("can't rescan %s", ctrlrname(unit));
+ close(fd);
+}
+
+static int
+cmd_rescan(int argc, char *argv[])
+{
+ int all = 0, i, ch, unit;
+
+ optreset = 1;
+ optind = 1;
+ while ((ch = getopt(argc, argv, "a")) != -1)
+ switch(ch) {
+ case 'a':
+ all = 1;
+ break;
+ default:
+ return(cmd_help(argc, argv));
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (all) {
+ mlx_foreach(rescan_ctrlr, NULL);
+ } else {
+ for (i = 0; i < argc; i++) {
+ if ((unit = ctrlrunit(argv[i])) == -1) {
+ warnx("'%s' is not a valid controller", argv[i]);
+ } else {
+ rescan_ctrlr(unit, NULL);
+ }
+ }
+ }
+ return(0);
+}
+
+/********************************************************************************
+ * Detach one or more system drives from a controller.
+ *
+ * detach <drive> [<drive>...]
+ * Detach <drive>.
+ *
+ * detach -a <controller> [<controller>...]
+ * Detach all drives on <controller>.
+ *
+ */
+static void
+detach_drive(int unit, void *arg)
+{
+ int fd;
+
+ /* Get the device */
+ if ((fd = open(ctrlrpath(unit), 0)) < 0) {
+ warn("can't open %s", ctrlrpath(unit));
+ return;
+ }
+
+ if (ioctl(fd, MLX_DETACH_DRIVE, &unit) < 0)
+ warn("can't detach %s", drivename(unit));
+ close(fd);
+}
+
+static int
+cmd_detach(int argc, char *argv[])
+{
+ struct mlxd_foreach_action ma;
+ int all = 0, i, ch, unit;
+
+ optreset = 1;
+ optind = 1;
+ while ((ch = getopt(argc, argv, "a")) != -1)
+ switch(ch) {
+ case 'a':
+ all = 1;
+ break;
+ default:
+ return(cmd_help(argc, argv));
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (all) {
+ ma.func = detach_drive;
+ ma.arg = &unit;
+ for (i = 0; i < argc; i++) {
+ if ((unit = ctrlrunit(argv[i])) == -1) {
+ warnx("'%s' is not a valid controller", argv[i]);
+ } else {
+ mlxd_foreach_ctrlr(unit, &ma);
+ }
+ }
+ } else {
+ for (i = 0; i < argc; i++) {
+ if ((unit = driveunit(argv[i])) == -1) {
+ warnx("'%s' is not a valid drive", argv[i]);
+ } else {
+ /* run across all controllers to find this drive */
+ mlx_foreach(detach_drive, &unit);
+ }
+ }
+ }
+ return(0);
+}
+
+/********************************************************************************
+ * Initiate a consistency check on a system drive.
+ *
+ * check [<drive>]
+ * Start a check of <drive>
+ *
+ */
+static int
+cmd_check(int argc, char *argv[])
+{
+ int unit, fd, result;
+
+ if (argc != 2)
+ return(cmd_help(argc, argv));
+
+ if ((unit = driveunit(argv[1])) == -1) {
+ warnx("'%s' is not a valid drive", argv[1]);
+ } else {
+
+ /* Get the device */
+ if ((fd = open(drivepath(unit), 0)) < 0) {
+ warn("can't open %s", drivepath(unit));
+ } else {
+ /* Try to start the check */
+ if ((ioctl(fd, MLXD_CHECKASYNC, &result)) < 0) {
+ switch(result) {
+ case 0x0002:
+ warnx("one or more of the SCSI disks on which the drive '%s' depends is DEAD", argv[1]);
+ break;
+ case 0x0105:
+ warnx("drive %s is invalid, or not a drive which can be checked", argv[1]);
+ break;
+ case 0x0106:
+ warnx("drive rebuild or consistency check is already in progress on this controller");
+ break;
+ default:
+ warn("ioctl MLXD_CHECKASYNC");
+ }
+ }
+ }
+ }
+ return(0);
+}
+
+/********************************************************************************
+ * Initiate a physical drive rebuild
+ *
+ * rebuild <controller> <channel>:<target>
+ * Start a rebuild of <controller>:<channel>:<target>
+ *
+ */
+static int
+cmd_rebuild(int argc, char *argv[])
+{
+ struct mlx_rebuild_request rb;
+ int unit, fd;
+
+ if (argc != 3)
+ return(cmd_help(argc, argv));
+
+ /* parse arguments */
+ if ((unit = ctrlrunit(argv[1])) == -1) {
+ warnx("'%s' is not a valid controller", argv[1]);
+ return(1);
+ }
+ /* try diskXXXX and unknownXXXX as we report the latter for a dead drive ... */
+ if ((sscanf(argv[2], "disk%2d%2d", &rb.rr_channel, &rb.rr_target) != 2) &&
+ (sscanf(argv[2], "unknown%2d%2d", &rb.rr_channel, &rb.rr_target) != 2)) {
+ warnx("'%s' is not a valid physical drive", argv[2]);
+ return(1);
+ }
+ /* get the device */
+ if ((fd = open(ctrlrpath(unit), 0)) < 0) {
+ warn("can't open %s", ctrlrpath(unit));
+ return(1);
+ }
+ /* try to start the rebuild */
+ if ((ioctl(fd, MLX_REBUILDASYNC, &rb)) < 0) {
+ switch(rb.rr_status) {
+ case 0x0002:
+ warnx("the drive at %d:%d is already ONLINE", rb.rr_channel, rb.rr_target);
+ break;
+ case 0x0004:
+ warnx("drive failed during rebuild");
+ break;
+ case 0x0105:
+ warnx("there is no drive at channel %d, target %d", rb.rr_channel, rb.rr_target);
+ break;
+ case 0x0106:
+ warnx("drive rebuild or consistency check is already in progress on this controller");
+ break;
+ default:
+ warn("ioctl MLXD_REBUILDASYNC");
+ }
+ }
+ return(0);
+}
+
+#ifdef SUPPORT_PAUSE
+/********************************************************************************
+ * Pause one or more channels on a controller
+ *
+ * pause [-d <delay>] [-t <time>] <controller> [<channel>...]
+ * Pauses <channel> (or all channels) for <time> seconds after a
+ * delay of <delay> seconds.
+ * pause <controller> -c
+ * Cancels pending pause
+ */
+static int
+cmd_pause(int argc, char *argv[])
+{
+ struct mlx_pause mp;
+ int unit, i, ch, fd, cancel = 0;
+ char *cp;
+ int oargc = argc;
+ char **oargv = argv;
+
+ mp.mp_which = 0;
+ mp.mp_when = 30;
+ mp.mp_howlong = 30;
+ optreset = 1;
+ optind = 1;
+ while ((ch = getopt(argc, argv, "cd:t:")) != -1)
+ switch(ch) {
+ case 'c':
+ cancel = 1;
+ break;
+ case 'd':
+ mp.mp_when = strtol(optarg, &cp, 0);
+ if (*cp != 0)
+ return(cmd_help(argc, argv));
+ break;
+ case 't':
+ mp.mp_howlong = strtol(optarg, &cp, 0);
+ if (*cp != 0)
+ return(cmd_help(argc, argv));
+ break;
+ default:
+ return(cmd_help(argc, argv));
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* get controller unit number that we're working on */
+ if ((argc < 1) || ((unit = ctrlrunit(argv[0])) == -1))
+ return(cmd_help(oargc, oargv));
+
+ /* Get the device */
+ if ((fd = open(ctrlrpath(unit), 0)) < 0) {
+ warn("can't open %s", ctrlrpath(unit));
+ return(1);
+ }
+
+ if (argc == 1) {
+ /* controller-wide pause/cancel */
+ mp.mp_which = cancel ? MLX_PAUSE_CANCEL : MLX_PAUSE_ALL;
+ } else {
+ for (i = 1; i < argc; i++) {
+ ch = strtol(argv[i], &cp, 0);
+ if (*cp != 0) {
+ warnx("bad channel number '%s'", argv[i]);
+ continue;
+ } else {
+ mp.mp_which |= (1 << ch);
+ }
+ }
+ }
+ if ((ioctl(fd, MLX_PAUSE_CHANNEL, &mp)) < 0)
+ warn("couldn't %s %s", cancel ? "cancel pause on" : "pause", ctrlrname(unit));
+ close(fd);
+ return(0);
+}
+#endif /* SUPPORT_PAUSE */
+
diff --git a/usr.sbin/mlxcontrol/config.c b/usr.sbin/mlxcontrol/config.c
new file mode 100644
index 0000000..a525658
--- /dev/null
+++ b/usr.sbin/mlxcontrol/config.c
@@ -0,0 +1,157 @@
+/*-
+ * Copyright (c) 1999 Michael Smith
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <err.h>
+
+#include <dev/mlx/mlxio.h>
+#include <dev/mlx/mlxreg.h>
+
+#include "mlxcontrol.h"
+
+static void print_span(struct mlx_sys_drv_span *span, int arms);
+static void print_sys_drive(struct conf_config *conf, int drvno);
+static void print_phys_drive(struct conf_config *conf, int chn, int targ);
+
+/********************************************************************************
+ * Get the configuration from the selected controller.
+ *
+ * config <controller>
+ * Print the configuration for <controller>
+ *
+ * XXX update to support adding/deleting drives.
+ */
+
+int
+cmd_config(int argc, char *argv[])
+{
+ struct conf_config conf;
+ int unit = 0; /* XXX */
+ int i, j;
+
+ bzero(&conf.cc_cfg, sizeof(conf.cc_cfg));
+ if (mlx_read_configuration(unit, &conf.cc_cfg)) {
+ printf("mlx%d: error submitting READ CONFIGURATION\n", unit);
+ } else {
+
+ printf("# Controller <INSERT DETAILS HERE>\n");
+ printf("#\n# Physical devices connected:\n");
+ for (i = 0; i < 5; i++)
+ for (j = 0; j < 16; j++)
+ print_phys_drive(&conf, i, j);
+ printf("#\n# System Drives defined:\n");
+
+ for (i = 0; i < conf.cc_cfg.cc_num_sys_drives; i++)
+ print_sys_drive(&conf, i);
+ }
+ return(0);
+}
+
+
+/********************************************************************************
+ * Print details for the system drive (drvno) in a format that we will be
+ * able to parse later.
+ *
+ * drive?? <raidlevel> <writemode>
+ * span? 0x????????-0x???????? ????MB on <disk> [...]
+ * ...
+ */
+static void
+print_span(struct mlx_sys_drv_span *span, int arms)
+{
+ int i;
+
+ printf("0x%08x-0x%08x %uMB on", span->sp_start_lba, span->sp_start_lba + span->sp_nblks, span->sp_nblks / 2048);
+ for (i = 0; i < arms; i++)
+ printf(" disk%02d%02d", span->sp_arm[i] >> 4, span->sp_arm[i] & 0x0f);
+ printf("\n");
+}
+
+static void
+print_sys_drive(struct conf_config *conf, int drvno)
+{
+ struct mlx_sys_drv *drv = &conf->cc_cfg.cc_sys_drives[drvno];
+ int i;
+
+ printf("drive%02d ", drvno);
+ switch(drv->sd_raidlevel & 0xf) {
+ case MLX_SYS_DRV_RAID0:
+ printf("RAID0");
+ break;
+ case MLX_SYS_DRV_RAID1:
+ printf("RAID1");
+ break;
+ case MLX_SYS_DRV_RAID3:
+ printf("RAID3");
+ break;
+ case MLX_SYS_DRV_RAID5:
+ printf("RAID5");
+ break;
+ case MLX_SYS_DRV_RAID6:
+ printf("RAID6");
+ break;
+ case MLX_SYS_DRV_JBOD:
+ printf("JBOD");
+ break;
+ default:
+ printf("RAID?");
+ }
+ printf(" write%s\n", drv->sd_raidlevel & MLX_SYS_DRV_WRITEBACK ? "back" : "through");
+
+ for (i = 0; i < drv->sd_valid_spans; i++) {
+ printf(" span%d ", i);
+ print_span(&drv->sd_span[i], drv->sd_valid_arms);
+ }
+}
+
+/********************************************************************************
+ * Print details for the physical drive at chn/targ in a format suitable for
+ * human consumption.
+ *
+ * <type>CCTT (<state>) "<vendor>/<model>"
+ * ????MB <features>
+ *
+ */
+static void
+print_phys_drive(struct conf_config *conf, int chn, int targ)
+{
+ struct mlx_phys_drv *drv = &conf->cc_cfg.cc_phys_drives[chn * 16 + targ];
+
+ /* if the drive isn't present, don't print it */
+ if (!(drv->pd_flags1 & MLX_PHYS_DRV_PRESENT))
+ return;
+
+ mlx_print_phys_drv(drv, chn, targ, "# ", 1);
+}
+
+
diff --git a/usr.sbin/mlxcontrol/interface.c b/usr.sbin/mlxcontrol/interface.c
new file mode 100644
index 0000000..2c7e12b
--- /dev/null
+++ b/usr.sbin/mlxcontrol/interface.c
@@ -0,0 +1,289 @@
+/*-
+ * Copyright (c) 1999 Michael Smith
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <cam/scsi/scsi_all.h>
+
+#include <dev/mlx/mlxio.h>
+#include <dev/mlx/mlxreg.h>
+
+#include "mlxcontrol.h"
+
+/********************************************************************************
+ * Iterate over all mlx devices, call (func) with each ones' path and (arg)
+ */
+void
+mlx_foreach(void (*func)(int unit, void *arg), void *arg)
+{
+ int i, fd;
+
+ /* limit total count for sanity */
+ for (i = 0; i < 64; i++) {
+ /* verify we can open it */
+ if ((fd = open(ctrlrpath(i), 0)) >= 0)
+ close(fd);
+ /* if we can, do */
+ if (fd >= 0) {
+ func(i, arg);
+ }
+ }
+}
+
+/********************************************************************************
+ * Open the controller (unit) and give the fd to (func) along with (arg)
+ */
+void
+mlx_perform(int unit, void (*func)(int fd, void *arg), void *arg)
+{
+ int fd;
+
+ if ((fd = open(ctrlrpath(unit), 0)) >= 0) {
+ func(fd, arg);
+ close(fd);
+ }
+}
+
+/********************************************************************************
+ * Iterate over all mlxd devices, call (func) with each ones' path and (arg)
+ */
+void
+mlxd_foreach_ctrlr(int unit, void *arg)
+{
+ struct mlxd_foreach_action *ma = (struct mlxd_foreach_action *)arg;
+ int i, fd, ctrlfd;
+
+ /* Get the device */
+ if ((ctrlfd = open(ctrlrpath(unit), 0)) < 0)
+ return;
+
+ for (i = -1; ;) {
+ /* Get the unit number of the next child device */
+ if (ioctl(ctrlfd, MLX_NEXT_CHILD, &i) < 0) {
+ close(ctrlfd);
+ return;
+ }
+
+ /* check that we can open this unit */
+ if ((fd = open(drivepath(i), 0)) >= 0)
+ close(fd);
+ /* if we can, do */
+ if (fd >= 0) {
+ ma->func(i, ma->arg);
+ }
+ }
+}
+
+void
+mlxd_foreach(void (*func)(int unit, void *arg), void *arg)
+{
+ struct mlxd_foreach_action ma;
+
+ ma.func = func;
+ ma.arg = arg;
+ mlx_foreach(mlxd_foreach_ctrlr, &ma);
+}
+
+/********************************************************************************
+ * Find the controller that manages the drive (unit), return controller number
+ * and system drive number on that controller.
+ */
+static struct
+{
+ int unit;
+ int ctrlr;
+ int sysdrive;
+} mlxd_find_ctrlr_param;
+
+static void
+mlxd_find_ctrlr_search(int unit, void *arg)
+{
+ int i, fd;
+
+ /* Get the device */
+ if ((fd = open(ctrlrpath(unit), 0)) >= 0) {
+ for (i = -1; ;) {
+ /* Get the unit number of the next child device */
+ if (ioctl(fd, MLX_NEXT_CHILD, &i) < 0)
+ break;
+
+ /* is this child the unit we want? */
+ if (i == mlxd_find_ctrlr_param.unit) {
+ mlxd_find_ctrlr_param.ctrlr = unit;
+ if (ioctl(fd, MLX_GET_SYSDRIVE, &i) == 0)
+ mlxd_find_ctrlr_param.sysdrive = i;
+ }
+ }
+ close(fd);
+ }
+}
+
+int
+mlxd_find_ctrlr(int unit, int *ctrlr, int *sysdrive)
+{
+ mlxd_find_ctrlr_param.unit = unit;
+ mlxd_find_ctrlr_param.ctrlr = -1;
+ mlxd_find_ctrlr_param.sysdrive = -1;
+
+ mlx_foreach(mlxd_find_ctrlr_search, NULL);
+ if ((mlxd_find_ctrlr_param.ctrlr != -1) && (mlxd_find_ctrlr_param.sysdrive != -1)) {
+ *ctrlr = mlxd_find_ctrlr_param.ctrlr;
+ *sysdrive = mlxd_find_ctrlr_param.sysdrive;
+ return(0);
+ }
+ return(1);
+}
+
+
+/********************************************************************************
+ * Send a command to the controller on (fd)
+ */
+
+void
+mlx_command(int fd, void *arg)
+{
+ struct mlx_usercommand *cmd = (struct mlx_usercommand *)arg;
+ int error;
+
+ error = ioctl(fd, MLX_COMMAND, cmd);
+ if (error != 0)
+ cmd->mu_error = error;
+}
+
+/********************************************************************************
+ * Perform an ENQUIRY2 command and return information related to the controller
+ * (unit)
+ */
+int
+mlx_enquiry(int unit, struct mlx_enquiry2 *enq)
+{
+ struct mlx_usercommand cmd;
+
+ /* build the command */
+ cmd.mu_datasize = sizeof(*enq);
+ cmd.mu_buf = enq;
+ cmd.mu_bufptr = 8;
+ cmd.mu_command[0] = MLX_CMD_ENQUIRY2;
+
+ /* hand it off for processing */
+ mlx_perform(unit, mlx_command, (void *)&cmd);
+
+ return(cmd.mu_status != 0);
+}
+
+
+/********************************************************************************
+ * Perform a READ CONFIGURATION command and return information related to the controller
+ * (unit)
+ */
+int
+mlx_read_configuration(int unit, struct mlx_core_cfg *cfg)
+{
+ struct mlx_usercommand cmd;
+
+ /* build the command */
+ cmd.mu_datasize = sizeof(*cfg);
+ cmd.mu_buf = cfg;
+ cmd.mu_bufptr = 8;
+ cmd.mu_command[0] = MLX_CMD_READ_CONFIG;
+
+ /* hand it off for processing */
+ mlx_perform(unit, mlx_command, (void *)&cmd);
+
+ return(cmd.mu_status != 0);
+}
+
+/********************************************************************************
+ * Perform a SCSI INQUIRY command and return pointers to the relevant data.
+ */
+int
+mlx_scsi_inquiry(int unit, int channel, int target, char **vendor, char **device, char **revision)
+{
+ struct mlx_usercommand cmd;
+ static struct {
+ struct mlx_dcdb dcdb;
+ union {
+ struct scsi_inquiry_data inq;
+ u_int8_t pad[SHORT_INQUIRY_LENGTH];
+ } d;
+ } __attribute__ ((packed)) dcdb_cmd;
+ struct scsi_inquiry *inq_cmd = (struct scsi_inquiry *)&dcdb_cmd.dcdb.dcdb_cdb[0];
+
+ /* build the command */
+ cmd.mu_datasize = sizeof(dcdb_cmd);
+ cmd.mu_buf = &dcdb_cmd;
+ cmd.mu_command[0] = MLX_CMD_DIRECT_CDB;
+
+ /* build the DCDB */
+ bzero(&dcdb_cmd, sizeof(dcdb_cmd));
+ dcdb_cmd.dcdb.dcdb_channel = channel;
+ dcdb_cmd.dcdb.dcdb_target = target;
+ dcdb_cmd.dcdb.dcdb_flags = MLX_DCDB_DATA_IN | MLX_DCDB_TIMEOUT_10S;
+ dcdb_cmd.dcdb.dcdb_datasize = SHORT_INQUIRY_LENGTH;
+ dcdb_cmd.dcdb.dcdb_cdb_length = 6;
+ dcdb_cmd.dcdb.dcdb_sense_length = SSD_FULL_SIZE;
+
+ /* build the cdb */
+ inq_cmd->opcode = INQUIRY;
+ inq_cmd->length = SHORT_INQUIRY_LENGTH;
+
+ /* hand it off for processing */
+ mlx_perform(unit, mlx_command, &cmd);
+
+ if (cmd.mu_status == 0) {
+ *vendor = &dcdb_cmd.d.inq.vendor[0];
+ *device = &dcdb_cmd.d.inq.product[0];
+ *revision = &dcdb_cmd.d.inq.revision[0];
+ }
+ return(cmd.mu_status);
+}
+
+/********************************************************************************
+ * Perform a GET DEVICE STATE command and return pointers to the relevant data.
+ */
+int
+mlx_get_device_state(int unit, int channel, int target, struct mlx_phys_drv *drv)
+{
+ struct mlx_usercommand cmd;
+
+ /* build the command */
+ cmd.mu_datasize = sizeof(*drv);
+ cmd.mu_buf = drv;
+ cmd.mu_bufptr = 8;
+ cmd.mu_command[0] = MLX_CMD_DEVICE_STATE;
+ cmd.mu_command[2] = channel;
+ cmd.mu_command[3] = target;
+
+ /* hand it off for processing */
+ mlx_perform(unit, mlx_command, (void *)&cmd);
+
+ return(cmd.mu_status != 0);
+}
diff --git a/usr.sbin/mlxcontrol/mlxcontrol.8 b/usr.sbin/mlxcontrol/mlxcontrol.8
new file mode 100644
index 0000000..542f994
--- /dev/null
+++ b/usr.sbin/mlxcontrol/mlxcontrol.8
@@ -0,0 +1,148 @@
+.\"
+.\" Copyright (c) 2000 Michael Smith
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the 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 ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED 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 10, 2000
+.Dt MLXCONTROL 8
+.Os
+.Sh NAME
+.Nm mlxcontrol
+.Nd Mylex DAC-family RAID management utility
+.Sh SYNOPSIS
+.Nm
+.Aq command
+.Op args
+.Nm
+status
+.Op Fl qv
+.Op Ar drive
+.Nm
+rescan
+.Ar controller
+.Op Ar controller ...
+.Nm
+detach
+.Ar drive
+.Op Ar drive ...
+.Nm
+detach
+.Fl a
+.Nm
+check
+.Ar drive
+.Nm
+config
+.Ar controller
+.Nm
+help
+.Ar command
+.Sh DESCRIPTION
+The
+.Nm
+utility provides status monitoring and management functions
+for devices attached
+to the
+.Xr mlx 4
+driver.
+.Pp
+Controller names are of the form "mlxN"
+where N is the unit number of the controller.
+Drive names are of the form "mlxdN"
+where N is the unit number of the drive.
+Do not specify the path to a device node.
+.Pp
+.Bl -tag -width rebuild
+.It status
+Print the status of controllers and system drives.
+If one or more drives are specified,
+only print information about these drives,
+otherwise print information
+about all controllers and drives in the system.
+With the
+.Fl v
+flag, display much more verbose information.
+With the
+.Fl q
+flag, do not print any output.
+This command returns
+0 if all drives tested are online,
+1 if one or more drives are critical and
+2 if one or more are offline.
+.It rescan
+Rescan one or more controllers for non-attached system drives
+(e.g.\& drives that have been
+detached or created subsequent to driver initialisation).
+If the
+.Fl a
+flag is supplied, rescan all controllers in the system.
+.It detach
+Detach one or more system drives.
+Drives must be unmounted
+and not opened by any other utility for this command to succeed.
+If the
+.Fl a
+flag is supplied, detach all system drives from the nominated controller.
+.It check
+Initiate a consistency check and repair pass on a redundant system drive
+(e.g.\& RAID1 or RAID5).
+The controller will scan the system drive and repair any inconsistencies.
+This command returns immediately;
+use the
+.Ar status
+command to monitor the progress of the check.
+.It rebuild
+Requires two arguments,
+.Ar controller
+and
+.Ar physdrive
+as specified in the
+output of the
+.Ar status
+command.
+All system drives using space on the physical drive
+.Ar physdrive
+are rebuilt, reconstructing all data on the drive.
+Note that each controller can only perform one rebuild at a time.
+This command returns immediately; use the
+.Ar status
+command to monitor the progress of the rebuild.
+.It config
+Print the current configuration from the nominated controller.
+This command will be updated
+to allow addition/deletion of system drives from a configuration
+in a future release.
+.It help
+Print usage information for
+.Ar command .
+.El
+.Sh AUTHORS
+The mlxcontrol utility was written by
+.An Michael Smith
+.Aq msmith@FreeBSD.org .
+.Sh BUGS
+The
+.Ar config
+command does not yet support modifying system drive configuration.
+.Pp
+Error log extraction is not yet supported.
diff --git a/usr.sbin/mlxcontrol/mlxcontrol.h b/usr.sbin/mlxcontrol/mlxcontrol.h
new file mode 100644
index 0000000..ab0f2ab
--- /dev/null
+++ b/usr.sbin/mlxcontrol/mlxcontrol.h
@@ -0,0 +1,89 @@
+/*-
+ * Copyright (c) 1999 Michael Smith
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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/queue.h>
+
+#define debug(fmt, args...) printf("%s: " fmt "\n", __func__ , ##args)
+
+struct mlxd_foreach_action
+{
+ void (*func)(int unit, void *arg);
+ void *arg;
+};
+
+extern void mlx_foreach(void (*func)(int unit, void *arg), void *arg);
+void mlxd_foreach_ctrlr(int unit, void *arg);
+extern void mlxd_foreach(void (*func)(int unit, void *arg), void *arg);
+extern int mlxd_find_ctrlr(int unit, int *ctrlr, int *sysdrive);
+extern void mlx_perform(int unit, void (*func)(int fd, void *arg), void *arg);
+extern void mlx_command(int fd, void *arg);
+extern int mlx_enquiry(int unit, struct mlx_enquiry2 *enq);
+extern int mlx_read_configuration(int unit, struct mlx_core_cfg *cfg);
+extern int mlx_scsi_inquiry(int unit, int bus, int target, char **vendor, char **device, char **revision);
+extern int mlx_get_device_state(int fd, int channel, int target, struct mlx_phys_drv *drv);
+
+extern char *ctrlrpath(int unit);
+extern char *ctrlrname(int unit);
+extern char *drivepath(int unit);
+extern char *drivename(int unit);
+extern int ctrlrunit(char *str);
+extern int driveunit(char *str);
+
+extern void mlx_print_phys_drv(struct mlx_phys_drv *drv, int channel, int target, char *prefix, int verbose);
+
+struct conf_phys_drv
+{
+ TAILQ_ENTRY(conf_phys_drv) pd_link;
+ int pd_bus;
+ int pd_target;
+ struct mlx_phys_drv pd_drv;
+};
+
+struct conf_span
+{
+ TAILQ_ENTRY(conf_span) s_link;
+ struct conf_phys_drv *s_drvs[8];
+ struct mlx_sys_drv_span s_span;
+};
+
+struct conf_sys_drv
+{
+ TAILQ_ENTRY(conf_sys_drv) sd_link;
+ struct conf_span *sd_spans[4];
+ struct mlx_sys_drv sd_drv;
+};
+
+struct conf_config
+{
+ TAILQ_HEAD(,conf_phys_drv) cc_phys_drvs;
+ TAILQ_HEAD(,conf_span) cc_spans;
+ TAILQ_HEAD(,conf_sys_drv) cc_sys_drvs;
+ struct conf_sys_drv *cc_drives[32];
+ struct mlx_core_cfg cc_cfg;
+};
+
diff --git a/usr.sbin/mlxcontrol/util.c b/usr.sbin/mlxcontrol/util.c
new file mode 100644
index 0000000..37cd2b7
--- /dev/null
+++ b/usr.sbin/mlxcontrol/util.c
@@ -0,0 +1,172 @@
+/*-
+ * Copyright (c) 1999 Michael Smith
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <stdio.h>
+#include <paths.h>
+#include <string.h>
+
+#include <dev/mlx/mlxio.h>
+#include <dev/mlx/mlxreg.h>
+
+#include "mlxcontrol.h"
+
+/********************************************************************************
+ * Various name-producing and -parsing functions
+ */
+
+/* return path of controller (unit) */
+char *
+ctrlrpath(int unit)
+{
+ static char buf[32];
+
+ sprintf(buf, "%s%s", _PATH_DEV, ctrlrname(unit));
+ return(buf);
+}
+
+/* return name of controller (unit) */
+char *
+ctrlrname(int unit)
+{
+ static char buf[32];
+
+ sprintf(buf, "mlx%d", unit);
+ return(buf);
+}
+
+/* return path of drive (unit) */
+char *
+drivepath(int unit)
+{
+ static char buf[32];
+
+ sprintf(buf, "%s%s", _PATH_DEV, drivename(unit));
+ return(buf);
+}
+
+/* return name of drive (unit) */
+char *
+drivename(int unit)
+{
+ static char buf[32];
+
+ sprintf(buf, "mlxd%d", unit);
+ return(buf);
+}
+
+/* get controller unit number from name in (str) */
+int
+ctrlrunit(char *str)
+{
+ int unit;
+
+ if (sscanf(str, "mlx%d", &unit) == 1)
+ return(unit);
+ return(-1);
+}
+
+/* get drive unit number from name in (str) */
+int
+driveunit(char *str)
+{
+ int unit;
+
+ if (sscanf(str, "mlxd%d", &unit) == 1)
+ return(unit);
+ return(-1);
+}
+
+/********************************************************************************
+ * Standardised output of various data structures.
+ */
+
+void
+mlx_print_phys_drv(struct mlx_phys_drv *drv, int chn, int targ, char *prefix, int verbose)
+{
+ char *type, *device, *vendor, *revision;
+
+ switch(drv->pd_flags2 & 0x03) {
+ case MLX_PHYS_DRV_DISK:
+ type = "disk";
+ break;
+ case MLX_PHYS_DRV_SEQUENTIAL:
+ type = "tape";
+ break;
+ case MLX_PHYS_DRV_CDROM:
+ type= "cdrom";
+ break;
+ case MLX_PHYS_DRV_OTHER:
+ default:
+ type = "unknown";
+ break;
+ }
+ printf("%s%s%02d%02d ", prefix, type, chn, targ);
+ switch(drv->pd_status) {
+ case MLX_PHYS_DRV_DEAD:
+ printf(" (dead) ");
+ break;
+ case MLX_PHYS_DRV_WRONLY:
+ printf(" (write-only) ");
+ break;
+ case MLX_PHYS_DRV_ONLINE:
+ printf(" (online) ");
+ break;
+ case MLX_PHYS_DRV_STANDBY:
+ printf(" (standby) ");
+ break;
+ default:
+ printf(" (0x%02x) ", drv->pd_status);
+ }
+ printf("\n");
+
+ if (verbose) {
+
+ printf("%s ", prefix);
+ if (!mlx_scsi_inquiry(0, chn, targ, &vendor, &device, &revision)) {
+ printf("'%8.8s' '%16.16s' '%4.4s'", vendor, device, revision);
+ } else {
+ printf("<IDENTIFY FAILED>");
+ }
+
+ printf(" %dMB ", drv->pd_config_size / 2048);
+
+ if (drv->pd_flags2 & MLX_PHYS_DRV_FAST20) {
+ printf(" fast20");
+ } else if (drv->pd_flags2 & MLX_PHYS_DRV_FAST) {
+ printf(" fast");
+ }
+ if (drv->pd_flags2 & MLX_PHYS_DRV_WIDE)
+ printf(" wide");
+ if (drv->pd_flags2 & MLX_PHYS_DRV_SYNC)
+ printf(" sync");
+ if (drv->pd_flags2 & MLX_PHYS_DRV_TAG)
+ printf(" tag-enabled");
+ printf("\n");
+ }
+}
diff --git a/usr.sbin/mount_nwfs/Makefile b/usr.sbin/mount_nwfs/Makefile
new file mode 100644
index 0000000..8fbb9a8
--- /dev/null
+++ b/usr.sbin/mount_nwfs/Makefile
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+PROG= mount_nwfs
+SRCS= mount_nwfs.c getmntopts.c
+MAN= mount_nwfs.8
+
+MOUNT= ${.CURDIR}/../../sbin/mount
+CFLAGS+= -DNWFS -I${MOUNT}
+WARNS?= 0
+
+.PATH: ${MOUNT}
+
+DPADD= ${LIBNCP} ${LIBIPX}
+LDADD= -lncp -lipx
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mount_nwfs/mount_nwfs.8 b/usr.sbin/mount_nwfs/mount_nwfs.8
new file mode 100644
index 0000000..3946f71
--- /dev/null
+++ b/usr.sbin/mount_nwfs/mount_nwfs.8
@@ -0,0 +1,230 @@
+.\" $FreeBSD$
+.Dd October 14, 1999
+.Dt MOUNT_NWFS 8
+.Os
+.Sh NAME
+.Nm mount_nwfs
+.Nd mount NetWare volume from a NetWare file server
+.Sh SYNOPSIS
+.Nm
+.Op Fl Chv
+.Fl S Ar server
+.Fl U Ar user
+.Op Fl connection\ options
+.Fl V Ar volume
+.Op Fl M Ar mode
+.Op Fl c Ar case
+.Op Fl d Ar mode
+.Op Fl f Ar mode
+.Op Fl g Ar gid
+.Op Fl l Ar locale
+.Op Fl n Ar os2
+.Op Fl u Ar uid
+.Op Fl w Ar scheme
+.Ar node
+.Nm
+.Op Fl options
+.Ar /server:user/volume[/path]
+.Ar node
+.Sh DESCRIPTION
+The
+.Nm
+utility allows to mount volume from a NetWare server.
+It may use either
+existing connection or create new: if no usable connection was found
+it will try to establish a new one.
+Connection has count of references to it,
+so when last mount will be dismounted connection will be closed.
+It is
+possible to create connection without any mounts (but use it for them) with
+.Xr ncplogin 1 .
+.Pp
+Note two forms of command line.
+In the first form, server and user specified
+via
+.Fl S
+and
+.Fl U
+options respectively.
+In the second form server and user specified in
+.Ar special
+part of
+.Xr mount 8
+command line arguments (the
+.Fl S ,
+.Fl U
+and
+.Fl V
+options are not used in this case).
+This allows use of
+.Xr fstab 5
+file (see
+.Sx EXAMPLES
+below).
+.Pp
+The options are:
+.Bl -tag -width indent
+.It Fl S Ar server
+Name of NetWare server to connect.
+For native IP you will need also
+.Fl A
+option.
+.It Fl U Ar user
+Name of user used in login sequence.
+.It Fl connection\ options
+See
+.Xr ncplogin 1
+for details.
+.It Fl V Ar volume
+Volume name to mount.
+Volume name can also be specified after all options and
+before
+.Ar mount-point .
+.It Ar node
+Path to mount volume.
+.It Fl c Ar case
+Select a
+.Ar case
+option which affects on name representation.
+.Ar Case
+can be one of the following:
+.Bl -tag -width "ValueXX"
+.It Em Value
+.Em Meaning
+.It l
+All existing file names converted to lower case.
+Newly created file gets a lower case under OS2 name space.
+This is the default when mounting volumes with DOS name space.
+.It L
+Same as 'l' but file system tries to be case insensitive.
+May not work well.
+.It n
+No case conversion is performed.
+.Em Warning !
+Use this option with DOS name space only as a last resort,
+because creating a lower case name in the DOS name space
+can lead to unpredictable results.
+This is the default when mounting volumes with OS2 name space.
+.It u
+All existing file names converted to upper case.
+Newly created file gets an upper case under OS2 name space.
+.It U
+Same as 'u' but file system tries to be case insensitive.
+May not work well.
+.El
+.It Fl f Ar mode , Fl d Ar mode
+Specify permissions that should be assigned to files and directories.
+The values must be specified as octal numbers.
+Default value for the file mode
+is taken from mount point, default value for the dir mode adds execute
+permission where the file mode gives read permission.
+.Pp
+Note that these permissions can differ from the rights granted by NetWare
+server.
+.It Fl n Ar namespace
+Do not use
+.Ar namespace .
+Currently only
+.Ar OS2
+can be here.
+.It Fl v
+Print version number.
+.It Fl u Ar uid , Fl g Ar gid
+User id and group id assigned to files.
+The default is owner and group id from
+directory where volume is mounted.
+.It Fl l Ar locale
+Set the locale for case conversion.
+By default
+.Nm
+tries to use an environment variable
+.Ev LC_* .
+.It Fl w Ar scheme
+Select a
+.Ar scheme
+used to convert file names between NetWare and
+.Fx .
+Supported conversion schemes are:
+.Bl -tag -width ".Cm koi2cp866"
+.It Cm asis
+Characters passed as is without any alteration.
+.It Cm koi2cp866
+koi8-r <-> CP866
+.It Cm se
+Suits for setups used in Sweden.
+.El
+.It Fl M Ar mode
+See
+.Xr ncplogin 1
+for details.
+If this option is omitted, connection permissions
+assumed the same as directory mode
+.Pq Fl d
+option.
+.El
+.Sh FILES
+.Bl -tag -width /var/log/wtmp -compact
+.It Pa ~/.nwfsrc
+keeps static parameters for connections and other information.
+See
+.Pa /usr/share/examples/nwclient/dot.nwfsrc
+for details.
+.El
+.Sh NOTES
+Before any NCP connection can be established kernel must be configured
+for IPX support, IPXrouted and KLD nwfs.ko should be loaded.
+.Sh EXAMPLES
+Next examples illustrates how to connect to NetWare server
+.Em nwserv
+as user
+.Em GUEST
+and mount volumes
+.Em SYS
+and
+.Em VOL1 :
+.Bd -literal -offset indent
+mount_nwfs -S nwserv -U guest -V sys /nw/s1/sys
+mount_nwfs /nwserv:guest/sys /nw/s1/sys
+mount -t nwfs /nwserv:guest/vol1 /nw/s1/vol1
+mount -t nwfs /nwserv:boris/sys/home/boris /home/boris/nw/home
+.Ed
+.Pp
+The last example mounts only subdirectory on a volume and equivalent
+to NetWare 'map root' command.
+.Pp
+It is possible to use
+.Xr fstab 5
+for nwfs mounts:
+.Bd -literal -offset indent
+/nwserv:guest/sys /nw/s1/sys nwfs rw,noauto 0 0
+/nwserv:guest/vol1 /nw/s1/vol2 nwfs rw,noauto 0 0
+.Ed
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 4.0 .
+.Sh CREDITS
+In development of NetWare client for
+.Fx ,
+the following sources were used:
+.Pp
+Documentation from NetWare NDK.
+.Pp
+Ncpfs for Linux - written by
+.An Volker Lendecke Aq lendecke@math.uni\-goettingen.de .
+He granted me permission to publish parts of his code under
+.Bx Ns -style
+license,
+.Pp
+"Interrupt List" from
+.An Ralf Brown ,
+.Pp
+Many files from
+.Pa /sys
+directory.
+.Sh AUTHORS
+.An Boris Popov Aq bp@butya.kz ,
+.Aq rbp@chat.ru
+.Sh BUGS
+to number a few
diff --git a/usr.sbin/mount_nwfs/mount_nwfs.c b/usr.sbin/mount_nwfs/mount_nwfs.c
new file mode 100644
index 0000000..98a5f8e
--- /dev/null
+++ b/usr.sbin/mount_nwfs/mount_nwfs.c
@@ -0,0 +1,363 @@
+/*
+ * Copyright (c) 1999, Boris Popov
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Boris Popov.
+ * 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 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/stat.h>
+#include <sys/errno.h>
+#include <sys/mount.h>
+#include <sys/sysctl.h>
+#include <machine/cpu.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <pwd.h>
+#include <grp.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <err.h>
+#include <sysexits.h>
+#include <time.h>
+
+#include <netncp/ncp_lib.h>
+#include <netncp/ncp_rcfile.h>
+#include <fs/nwfs/nwfs_mount.h>
+#include "mntopts.h"
+
+#define NWFS_VFSNAME "nwfs"
+
+static char mount_point[MAXPATHLEN + 1];
+static void usage(void);
+static int parsercfile(struct ncp_conn_loginfo *li, struct nwfs_args *mdata);
+
+static struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ { NULL }
+};
+
+static int
+parsercfile(struct ncp_conn_loginfo *li, struct nwfs_args *mdata) {
+ return 0;
+}
+
+int
+main(int argc, char *argv[]) {
+ NWCONN_HANDLE connHandle;
+ struct nwfs_args mdata;
+ struct ncp_conn_loginfo li;
+ struct stat st;
+ struct nw_entry_info einfo;
+ struct tm *tm;
+ time_t ltime;
+ int opt, error, mntflags, nlsopt, wall_clock;
+ size_t len;
+ char *p, *p1, tmp[1024];
+ u_char *pv;
+
+ if (argc < 2)
+ usage();
+ if (argc == 2) {
+ if (strcmp(argv[1], "-h") == 0) {
+ usage();
+ } else if (strcmp(argv[1], "-v") == 0) {
+ errx(EX_OK, "version %d.%d.%d", NWFS_VERSION / 100000,
+ (NWFS_VERSION % 10000) / 1000,
+ (NWFS_VERSION % 1000) / 100);
+ }
+ }
+
+ if(ncp_initlib()) exit(1);
+
+ mntflags = error = 0;
+ bzero(&mdata,sizeof(mdata));
+ mdata.uid = mdata.gid = -1;
+ nlsopt = 0;
+
+ if (ncp_li_init(&li, argc, argv)) return 1;
+ /*
+ * A little bit weird, but I should figure out which server/user to use
+ * _before_ reading .rc file
+ */
+ if (argc >= 3 && argv[argc-1][0] != '-' && argv[argc-2][0] != '-' &&
+ argv[argc-2][0] == '/') {
+ p = argv[argc-2];
+ error = 1;
+ do {
+ if (*p++ != '/') break;
+ p1 = tmp;
+ while (*p != ':' && *p != 0) *p1++ = *p++;
+ if (*p++ == 0) break;
+ *p1 = 0;
+ if (ncp_li_setserver(&li, tmp)) break;
+ p1 = tmp;
+ while (*p != '/' && *p != 0) *p1++ = *p++;
+ if (*p++ == 0) break;
+ *p1 = 0;
+ if (ncp_li_setuser(&li, tmp)) break;
+ p1 = tmp;
+ while (*p != '/' && *p != 0) *p1++ = *p++;
+ *p1 = 0;
+ if (strlen(tmp) > NCP_VOLNAME_LEN) {
+ warnx("volume name too long: %s", tmp);
+ break;
+ }
+ ncp_str_upper(strcpy(mdata.mounted_vol,tmp));
+ if (*p == '/')
+ p++;
+ p1 = mdata.root_path + 2;
+ pv = mdata.root_path + 1;
+ for(;*p;) {
+ *pv = 0;
+ while (*p != '/' && *p) {
+ *p1++ = *p++;
+ (*pv)++;
+ }
+ if (*pv) {
+ ncp_nls_mem_u2n(pv + 1, pv + 1, *pv);
+ pv += (*pv) + 1;
+ mdata.root_path[0]++;
+ }
+ if (*p++ == 0) break;
+ p1++;
+ }
+ error = 0;
+ } while(0);
+ if (error)
+ errx(EX_DATAERR,
+ "an error occurred while parsing '%s'",
+ argv[argc - 2]);
+ }
+ if (ncp_li_readrc(&li)) return 1;
+ if (ncp_rc) {
+ parsercfile(&li,&mdata);
+ rc_close(ncp_rc);
+ }
+ while ((opt = getopt(argc, argv, STDPARAM_OPT"V:c:d:f:g:l:n:o:u:w:")) != -1) {
+ switch (opt) {
+ case STDPARAM_ARGS:
+ if (ncp_li_arg(&li, opt, optarg)) {
+ return 1;
+ }
+ break;
+ case 'V':
+ if (strlen(optarg) > NCP_VOLNAME_LEN)
+ errx(EX_DATAERR, "volume too long: %s", optarg);
+ ncp_str_upper(strcpy(mdata.mounted_vol,optarg));
+ break;
+ case 'u': {
+ struct passwd *pwd;
+
+ pwd = isdigit(optarg[0]) ?
+ getpwuid(atoi(optarg)) : getpwnam(optarg);
+ if (pwd == NULL)
+ errx(EX_NOUSER, "unknown user '%s'", optarg);
+ mdata.uid = pwd->pw_uid;
+ break;
+ }
+ case 'g': {
+ struct group *grp;
+
+ grp = isdigit(optarg[0]) ?
+ getgrgid(atoi(optarg)) : getgrnam(optarg);
+ if (grp == NULL)
+ errx(EX_NOUSER, "unknown group '%s'", optarg);
+ mdata.gid = grp->gr_gid;
+ break;
+ }
+ case 'd':
+ errno = 0;
+ mdata.dir_mode = strtol(optarg, &p, 8);
+ if (errno || *p != 0)
+ errx(EX_DATAERR, "invalid value for directory mode");
+ break;
+ case 'f':
+ errno = 0;
+ mdata.file_mode = strtol(optarg, &p, 8);
+ if (errno || *p != 0)
+ errx(EX_DATAERR, "invalid value for file mode");
+ break;
+ case '?':
+ usage();
+ /*NOTREACHED*/
+ case 'n': {
+ char *inp, *nsp;
+
+ nsp = inp = optarg;
+ while ((nsp = strsep(&inp, ",;:")) != NULL) {
+ if (strcasecmp(nsp, "OS2") == 0)
+ mdata.flags |= NWFS_MOUNT_NO_OS2;
+ else if (strcasecmp(nsp, "LONG") == 0)
+ mdata.flags |= NWFS_MOUNT_NO_LONG;
+ else if (strcasecmp(nsp, "NFS") == 0)
+ mdata.flags |= NWFS_MOUNT_NO_NFS;
+ else
+ errx(EX_DATAERR, "unknown namespace '%s'", nsp);
+ }
+ break;
+ };
+ case 'l':
+ if (ncp_nls_setlocale(optarg) != 0) return 1;
+ mdata.flags |= NWFS_MOUNT_HAVE_NLS;
+ break;
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags, 0);
+ break;
+ case 'c':
+ switch (optarg[0]) {
+ case 'l':
+ nlsopt |= NWHP_LOWER;
+ break;
+ case 'u':
+ nlsopt |= NWHP_UPPER;
+ break;
+ case 'n':
+ nlsopt |= NWHP_LOWER | NWHP_UPPER;
+ break;
+ case 'L':
+ nlsopt |= NWHP_LOWER | NWHP_NOSTRICT;
+ break;
+ case 'U':
+ nlsopt |= NWHP_UPPER | NWHP_NOSTRICT;
+ break;
+ default:
+ errx(EX_DATAERR, "invalid suboption '%c' for -c",
+ optarg[0]);
+ }
+ break;
+ case 'w':
+ if (ncp_nls_setrecodebyname(optarg) != 0)
+ return 1;
+ mdata.flags |= NWFS_MOUNT_HAVE_NLS;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if (optind == argc - 2) {
+ optind++;
+ } else if (mdata.mounted_vol[0] == 0)
+ errx(EX_USAGE, "volume name should be specified");
+
+ if (optind != argc - 1)
+ usage();
+ realpath(argv[optind], mount_point);
+
+ if (stat(mount_point, &st) == -1)
+ err(EX_OSERR, "could not find mount point %s", mount_point);
+ if (!S_ISDIR(st.st_mode)) {
+ errno = ENOTDIR;
+ err(EX_OSERR, "can't mount on %s", mount_point);
+ }
+ if (ncp_geteinfo(mount_point, &einfo) == 0)
+ errx(EX_OSERR, "can't mount on %s twice", mount_point);
+
+ if (mdata.uid == -1) {
+ mdata.uid = st.st_uid;
+ }
+ if (mdata.gid == -1) {
+ mdata.gid = st.st_gid;
+ }
+ if (mdata.file_mode == 0 ) {
+ mdata.file_mode = st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO);
+ }
+ if (mdata.dir_mode == 0) {
+ mdata.dir_mode = mdata.file_mode;
+ if ((mdata.dir_mode & S_IRUSR) != 0)
+ mdata.dir_mode |= S_IXUSR;
+ if ((mdata.dir_mode & S_IRGRP) != 0)
+ mdata.dir_mode |= S_IXGRP;
+ if ((mdata.dir_mode & S_IROTH) != 0)
+ mdata.dir_mode |= S_IXOTH;
+ }
+ if (li.access_mode == 0) {
+ li.access_mode = mdata.dir_mode;
+ }
+/* if (mdata.flags & NWFS_MOUNT_HAVE_NLS) {*/
+ mdata.nls = ncp_nls;
+/* }*/
+ mdata.nls.opt = nlsopt;
+
+ len = sizeof(wall_clock);
+ if (sysctlbyname("machdep.wall_cmos_clock", &wall_clock, &len, NULL, 0) == -1)
+ err(EX_OSERR, "get wall_clock");
+ if (wall_clock == 0) {
+ time(&ltime);
+ tm = localtime(&ltime);
+ mdata.tz = -(tm->tm_gmtoff / 60);
+ }
+
+ error = ncp_li_check(&li);
+ if (error)
+ return 1;
+ li.opt |= NCP_OPT_WDOG;
+ /* well, now we can try to login, or use already established connection */
+ error = ncp_li_login(&li, &connHandle);
+ if (error) {
+ ncp_error("cannot login to server %s", error, li.server);
+ exit(1);
+ }
+ error = ncp_conn2ref(connHandle, &mdata.connRef);
+ if (error) {
+ ncp_error("could not convert handle to reference", error);
+ ncp_disconnect(connHandle);
+ exit(1);
+ }
+ strcpy(mdata.mount_point,mount_point);
+ mdata.version = NWFS_VERSION;
+ error = mount(NWFS_VFSNAME, mdata.mount_point, mntflags, (void*)&mdata);
+ if (error) {
+ ncp_error("mount error: %s", error, mdata.mount_point);
+ ncp_disconnect(connHandle);
+ exit(1);
+ }
+ /*
+ * I'm leave along my handle, but kernel should keep own ...
+ */
+ ncp_disconnect(connHandle);
+ /* we are done ?, impossible ... */
+ return 0;
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
+ "usage: mount_nwfs [-Chv] -S server -U user [-connection options]",
+ " -V volume [-M mode] [-c case] [-d mode] [-f mode]",
+ " [-g gid] [-l locale] [-n os2] [-u uid] [-w scheme]",
+ " node",
+ " mount_nwfs [-options] /server:user/volume[/path] node");
+
+ exit (1);
+}
diff --git a/usr.sbin/mount_portalfs/Makefile b/usr.sbin/mount_portalfs/Makefile
new file mode 100644
index 0000000..991a36f
--- /dev/null
+++ b/usr.sbin/mount_portalfs/Makefile
@@ -0,0 +1,15 @@
+# From: @(#)Makefile 8.3 (Berkeley) 3/27/94
+# $FreeBSD$
+
+PROG= mount_portalfs
+SRCS= mount_portalfs.c activate.c conf.c cred.c getmntopts.c pt_conf.c \
+ pt_exec.c pt_file.c pt_pipe.c pt_tcp.c pt_tcplisten.c
+MAN= mount_portalfs.8
+
+MOUNT= ${.CURDIR}/../../sbin/mount
+CFLAGS+=-I${MOUNT}
+WARNS?= 3
+
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mount_portalfs/activate.c b/usr.sbin/mount_portalfs/activate.c
new file mode 100644
index 0000000..7d35d48
--- /dev/null
+++ b/usr.sbin/mount_portalfs/activate.c
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 1992, 1993
+ * 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.
+ *
+ * @(#)activate.c 8.3 (Berkeley) 4/28/95
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/syslog.h>
+#include <sys/uio.h>
+
+#include "portald.h"
+
+/*
+ * Scan the providers list and call the
+ * appropriate function.
+ */
+static int activate_argv(struct portal_cred *pcr, char *key, char **v, int so,
+ int *fdp)
+{
+ provider *pr;
+
+ for (pr = providers; pr->pr_match; pr++)
+ if (strcmp(v[0], pr->pr_match) == 0)
+ return ((*pr->pr_func)(pcr, key, v, so, fdp));
+
+ return (ENOENT);
+}
+
+static int get_request(int so, struct portal_cred *pcr, char *key, int klen)
+{
+ struct iovec iov[2];
+ struct msghdr msg;
+ int n;
+
+ iov[0].iov_base = (caddr_t) pcr;
+ iov[0].iov_len = sizeof(*pcr);
+ iov[1].iov_base = key;
+ iov[1].iov_len = klen;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 2;
+
+ n = recvmsg(so, &msg, 0);
+ if (n < 0)
+ return (errno);
+
+ if (n <= (int)sizeof(*pcr))
+ return (EINVAL);
+
+ n -= sizeof(*pcr);
+ key[n] = '\0';
+
+ return (0);
+}
+
+static void send_reply(int so, int fd, int error)
+{
+ int n;
+ struct iovec iov;
+ struct msghdr msg;
+ union {
+ struct cmsghdr cmsg;
+ char control[CMSG_SPACE(sizeof(int))];
+ } ctl;
+
+ /*
+ * Line up error code. Don't worry about byte ordering
+ * because we must be sending to the local machine.
+ */
+ iov.iov_base = (caddr_t) &error;
+ iov.iov_len = sizeof(error);
+
+ /*
+ * Build a msghdr
+ */
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ /*
+ * If there is a file descriptor to send then
+ * construct a suitable rights control message.
+ */
+ if (fd >= 0) {
+ ctl.cmsg.cmsg_len = CMSG_LEN(sizeof(int));
+ ctl.cmsg.cmsg_level = SOL_SOCKET;
+ ctl.cmsg.cmsg_type = SCM_RIGHTS;
+ *((int *)CMSG_DATA(&ctl.cmsg)) = fd;
+ msg.msg_control = (caddr_t) &ctl;
+ msg.msg_controllen = ctl.cmsg.cmsg_len;
+ }
+
+ /*
+ * Send to kernel...
+ */
+ if ((n = sendmsg(so, &msg, 0)) < 0)
+ syslog(LOG_ERR, "send: %s", strerror(errno));
+#ifdef DEBUG
+ fprintf(stderr, "sent %d bytes\n", n);
+#endif
+ sleep(1); /*XXX*/
+#ifdef notdef
+ if (shutdown(so, SHUT_RDWR) < 0)
+ syslog(LOG_ERR, "shutdown: %s", strerror(errno));
+#endif
+ /*
+ * Throw away the open file descriptor
+ */
+ (void) close(fd);
+}
+
+void activate(qelem *q, int so)
+{
+ struct portal_cred pcred;
+ char key[MAXPATHLEN+1];
+ int error;
+ char **v;
+ int fd = -1;
+
+ /*
+ * Read the key from the socket
+ */
+ error = get_request(so, &pcred, key, sizeof(key));
+ if (error) {
+ syslog(LOG_ERR, "activate: recvmsg: %s", strerror(error));
+ goto drop;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "lookup key %s\n", key);
+#endif
+
+ /*
+ * Find a match in the configuration file
+ */
+ v = conf_match(q, key);
+
+ /*
+ * If a match existed, then find an appropriate portal
+ * otherwise simply return ENOENT.
+ */
+ if (v) {
+ error = activate_argv(&pcred, key, v, so, &fd);
+ if (error)
+ fd = -1;
+ else if (fd < 0)
+ error = -1;
+ } else {
+ error = ENOENT;
+ }
+
+ if (error >= 0)
+ send_reply(so, fd, error);
+
+drop:;
+ close(so);
+}
diff --git a/usr.sbin/mount_portalfs/conf.c b/usr.sbin/mount_portalfs/conf.c
new file mode 100644
index 0000000..f6b34bd
--- /dev/null
+++ b/usr.sbin/mount_portalfs/conf.c
@@ -0,0 +1,318 @@
+/*
+ * Copyright (c) 1992, 1993
+ * 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.
+ *
+ * @(#)conf.c 8.2 (Berkeley) 3/27/94
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <regex.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/syslog.h>
+
+#include "portald.h"
+
+#define ALLOC(ty) (xmalloc(sizeof(ty)))
+
+typedef struct path path;
+struct path {
+ qelem p_q; /* 2-way linked list */
+ int p_lno; /* Line number of this record */
+ char *p_args; /* copy of arg string (malloc) */
+ char *p_key; /* Pathname to match (also p_argv[0]) */
+ regex_t p_rx; /* RE to match against pathname () */
+ int p_rxvalid; /* non-zero if valid regular expression */
+ int p_argc; /* number of elements in arg string */
+ char **p_argv; /* argv[] pointers into arg string (malloc) */
+};
+
+static char *conf_file; /* XXX for regerror */
+static path *curp; /* XXX for regerror */
+
+/*
+ * Add an element to a 2-way list,
+ * just after (pred)
+ */
+static void ins_que(qelem *elem, qelem *pred)
+{
+ qelem *p = pred->q_forw;
+ elem->q_back = pred;
+ elem->q_forw = p;
+ pred->q_forw = elem;
+ p->q_back = elem;
+}
+
+/*
+ * Remove an element from a 2-way list
+ */
+static void rem_que(qelem *elem)
+{
+ qelem *p = elem->q_forw;
+ qelem *p2 = elem->q_back;
+ p2->q_forw = p;
+ p->q_back = p2;
+}
+
+/*
+ * Error checking malloc
+ */
+static void *xmalloc(unsigned siz)
+{
+ void *p = malloc(siz);
+ if (p)
+ return (p);
+ syslog(LOG_ALERT, "malloc: failed to get %d bytes", siz);
+ exit(1);
+}
+
+/*
+ * Insert the path in the list.
+ * If there is already an element with the same key then
+ * the *second* one is ignored (return 0). If the key is
+ * not found then the path is added to the end of the list
+ * and 1 is returned.
+ */
+static int pinsert(path *p0, qelem *q0)
+{
+ qelem *q;
+
+ if (p0->p_argc == 0)
+ return (0);
+
+ for (q = q0->q_forw; q != q0; q = q->q_forw) {
+ path *p = (path *) q;
+ if (strcmp(p->p_key, p0->p_key) == 0)
+ return (0);
+ }
+ ins_que(&p0->p_q, q0->q_back);
+ return (1);
+
+}
+
+static path *palloc(char *cline, int lno)
+{
+ int c;
+ char *s;
+ char *key;
+ path *p;
+ char **ap;
+
+ /*
+ * Implement comment chars
+ */
+ s = strchr(cline, '#');
+ if (s)
+ *s = 0;
+
+ /*
+ * Do a pass through the string to count the number
+ * of arguments
+ */
+ c = 0;
+ key = strdup(cline);
+ for (s = key; s != NULL; ) {
+ char *val;
+ while ((val = strsep(&s, " \t\n")) != NULL && *val == '\0')
+ ;
+ if (val)
+ c++;
+ }
+ c++;
+ free(key);
+
+ if (c <= 1)
+ return (0);
+
+ /*
+ * Now do another pass and generate a new path structure
+ */
+ p = ALLOC(path);
+ p->p_argc = 0;
+ p->p_argv = xmalloc(c * sizeof(char *));
+ p->p_args = strdup(cline);
+ ap = p->p_argv;
+ for (s = p->p_args; s != NULL; ) {
+ char *val;
+ while ((val = strsep(&s, " \t\n")) != NULL && *val == '\0')
+ ;
+ if (val) {
+ *ap++ = val;
+ p->p_argc++;
+ }
+ }
+ *ap = 0;
+
+#ifdef DEBUG
+ for (c = 0; c < p->p_argc; c++)
+ printf("%sv[%d] = %s\n", c?"\t":"", c, p->p_argv[c]);
+#endif
+
+ p->p_key = p->p_argv[0];
+ if (strpbrk(p->p_key, RE_CHARS)) {
+ int val;
+
+ curp = p; /* XXX */
+ val = regcomp(&p->p_rx, p->p_key, REG_EXTENDED | REG_NOSUB);
+ if (val) {
+ char errbuf[_POSIX2_LINE_MAX];
+ regerror(val, &p->p_rx, errbuf, sizeof errbuf);
+ syslog(LOG_ERR, "%s:%d: regcomp %s: %s",
+ conf_file, curp->p_lno, curp->p_key, errbuf);
+ regfree(&p->p_rx);
+ p->p_rxvalid = 0;
+ } else {
+ p->p_rxvalid = 1;
+ }
+ curp = 0; /* XXX */
+ } else {
+ p->p_rxvalid = 0;
+ }
+ p->p_lno = lno;
+
+ return (p);
+}
+
+/*
+ * Free a path structure
+ */
+static void pfree(path *p)
+{
+ free(p->p_args);
+ if (p->p_rxvalid) {
+ regfree(&p->p_rx);
+ }
+ free((char *) p->p_argv);
+ free((char *) p);
+}
+
+/*
+ * Discard all currently held path structures on q0.
+ * and add all the ones on xq.
+ */
+static void preplace(qelem *q0, qelem *xq)
+{
+ /*
+ * While the list is not empty,
+ * take the first element off the list
+ * and free it.
+ */
+ while (q0->q_forw != q0) {
+ qelem *q = q0->q_forw;
+ rem_que(q);
+ pfree((path *) q);
+ }
+ while (xq->q_forw != xq) {
+ qelem *q = xq->q_forw;
+ rem_que(q);
+ ins_que(q, q0);
+ }
+}
+
+/*
+ * Read the lines from the configuration file and
+ * add them to the list of paths.
+ */
+static void readfp(qelem *q0, FILE *fp)
+{
+ char cline[LINE_MAX];
+ int nread = 0;
+ qelem q;
+
+ /*
+ * Make a new empty list.
+ */
+ q.q_forw = q.q_back = &q;
+
+ /*
+ * Read the lines from the configuration file.
+ */
+ while (fgets(cline, sizeof(cline), fp)) {
+ path *p = palloc(cline, nread+1);
+ if (p && !pinsert(p, &q))
+ pfree(p);
+ nread++;
+ }
+
+ /*
+ * If some records were read, then throw
+ * away the old list and replace with the
+ * new one.
+ */
+ if (nread)
+ preplace(q0, &q);
+}
+
+/*
+ * Read the configuration file (conf) and replace
+ * the existing path list with the new version.
+ * If the file is not readable, then no changes take place
+ */
+void conf_read(qelem *q, char *conf)
+{
+ FILE *fp = fopen(conf, "r");
+ if (fp) {
+ conf_file = conf; /* XXX */
+ readfp(q, fp);
+ conf_file = 0; /* XXX */
+ (void) fclose(fp);
+ } else {
+ syslog(LOG_ERR, "open config file \"%s\": %s", conf, strerror(errno));
+ }
+}
+
+
+char **conf_match(qelem *q0, char *key)
+{
+ qelem *q;
+
+ for (q = q0->q_forw; q != q0; q = q->q_forw) {
+ path *p = (path *) q;
+ if (p->p_rxvalid) {
+ if (!regexec(&p->p_rx, key, 0, 0, 0)) {
+ return p->p_argv + 1;
+ }
+ } else {
+ if (strncmp(p->p_key, key, strlen(p->p_key)) == 0)
+ return (p->p_argv+1);
+ }
+ }
+
+ return (0);
+}
diff --git a/usr.sbin/mount_portalfs/cred.c b/usr.sbin/mount_portalfs/cred.c
new file mode 100644
index 0000000..9e5d9f3
--- /dev/null
+++ b/usr.sbin/mount_portalfs/cred.c
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (C) 2005 Diomidis Spinellis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/syslog.h>
+
+#include "portald.h"
+
+/*
+ * Set the process's credentials to those specified in user,
+ * saveing the existing ones in save.
+ * Return 0 on success, -1 (with errno set) on error.
+ */
+int
+set_user_credentials(struct portal_cred *user, struct portal_cred *save)
+{
+ save->pcr_uid = geteuid();
+ if ((save->pcr_ngroups = getgroups(NGROUPS_MAX, save->pcr_groups)) < 0)
+ return (-1);
+ if (setgroups(user->pcr_ngroups, user->pcr_groups) < 0)
+ return (-1);
+ if (seteuid(user->pcr_uid) < 0)
+ return (-1);
+ return (0);
+}
+
+/*
+ * Restore the process's credentials to the ones specified in save.
+ * Log failures using LOG_ERR.
+ * Return 0 on success, -1 (with errno set) on error.
+ */
+int
+restore_credentials(struct portal_cred *save)
+{
+ if (seteuid(save->pcr_uid) < 0) {
+ syslog(LOG_ERR, "seteuid: %m");
+ return (-1);
+ }
+ if (setgroups(save->pcr_ngroups, save->pcr_groups) < 0) {
+ syslog(LOG_ERR, "setgroups: %m");
+ return (-1);
+ }
+ return (0);
+}
diff --git a/usr.sbin/mount_portalfs/mount_portalfs.8 b/usr.sbin/mount_portalfs/mount_portalfs.8
new file mode 100644
index 0000000..ffbcb08
--- /dev/null
+++ b/usr.sbin/mount_portalfs/mount_portalfs.8
@@ -0,0 +1,216 @@
+.\"
+.\" 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
+.\" 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_portal.8 8.3 (Berkeley) 3/27/94
+.\" $FreeBSD$
+.\"
+.Dd March 11, 2005
+.Dt MOUNT_PORTALFS 8
+.Os
+.Sh NAME
+.Nm mount_portalfs
+.Nd mount the portal daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl o Ar options
+.Ar /etc/portal.conf
+.Ar mount_point
+.Sh DESCRIPTION
+The
+.Nm
+utility attaches an instance of the portal daemon
+to the global file system namespace.
+The conventional mount point is
+.Pa /p .
+.\" .PA /dev .
+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
+.Pp
+The portal daemon provides an
+.Em open
+service.
+Objects opened under the portal mount point are
+dynamically created by the portal daemon according
+to rules specified in the named configuration file.
+Using this mechanism allows descriptors such as sockets
+to be made available in the file system namespace.
+.Pp
+The portal daemon works by being passed the full pathname
+of the object being opened.
+The daemon creates an appropriate descriptor according
+to the rules in the configuration file, and then passes the descriptor back
+to the calling process as the result of the open system call.
+.Sh NAMESPACE
+By convention, the portal daemon divides the namespace into sub-namespaces,
+each of which handles objects of a particular type.
+.Pp
+The following sub-namespaces are currently implemented:
+.Pa fs ,
+.Pa pipe ,
+.Pa tcp ,
+and
+.Pa tcplisten .
+.Pp
+The
+.Pa fs
+namespace opens the named file, starting back at the root directory.
+This can be used to provide a controlled escape path from
+a chrooted environment.
+.Pp
+The
+.Pa pipe
+namespace executes the named command, starting back at the root directory.
+The command's arguments can be provided after the command's name,
+by separating them with spaces or tabs.
+Files opened for reading in the
+.Pa pipe
+namespace will receive their input from the command's standard output;
+files opened for writing will send the data of write operations
+to the command's standard input.
+.Pp
+The
+.Pa tcp
+namespace takes a slash separated hostname and a port and
+creates an open TCP/IP connection.
+.Pp
+The
+.Pa tcplisten
+namespace takes a slash separated hostname and port and creates a TCP/IP
+socket bound to the given hostname-port pair.
+The hostname may be
+specified as "ANY" to allow any other host to connect to the socket.
+A
+port number of 0 will dynamically allocate a port, this can be
+discovered by calling
+.Xr getsockname 2
+with the returned file descriptor.
+Privileged ports can only be bound to
+by the super-user.
+.Sh "CONFIGURATION FILE"
+The configuration file contains a list of rules.
+Each rule takes one line and consists of two or more
+whitespace separated fields.
+A hash (``#'') character causes the remainder of a line to
+be ignored.
+Blank lines are ignored.
+.Pp
+The first field is a pathname prefix to match
+against the requested pathname.
+If a match is found, the second field
+tells the daemon what type of object to create.
+Subsequent fields are passed to the creation function.
+.Bd -literal
+# @(#)portal.conf 5.1 (Berkeley) 7/13/92
+tcplisten/ tcplisten tcplisten/
+tcp/ tcp tcp/
+fs/ file fs/
+pipe/ pipe pipe/
+.Ed
+.Sh FILES
+.Bl -tag -width /p/* -compact
+.It Pa /p/*
+.El
+.Sh EXAMPLES
+Display the greeting of the
+.Fx
+.Tn SMTP
+server.
+.Pp
+.Dl "head -1 /p/tcp/mx1.freebsd.org/smtp"
+.Pp
+Implement a (single-threaded) echo server:
+.Bd -literal -offset indent
+while :
+do
+ (exec 3<>/p/tcplisten/ANY/echo && cat -u <&3 >&3)
+done
+.Ed
+.Pp
+Gather data from two sources.
+Verify that two remote files are identical:
+.Bd -literal -offset indent
+diff -q '/p/pipe/usr/bin/fetch -o - \\
+ ftp://ftp1.freebsd.org/pub/FreeBSD/README.TXT' \\
+ '/p/pipe/usr/bin/fetch -o - \\
+ ftp://ftp2.freebsd.org/pub/FreeBSD/README.TXT'
+.Ed
+.Pp
+Scatter data to two sinks.
+Record a remote
+.Tn CD
+.Tn ISO
+image and calculate its checksum:
+.Bd -literal -offset indent
+fetch -o - ftp://ftp5.freebsd.org/.../disc.iso |
+tee '/p/pipe/usr/local/bin/cdrecord -' |
+md5
+.Ed
+.Pp
+Create an
+.Tn XML
+view of the password file:
+.Bd -literal -offset indent
+ln -s '/p/pipe/usr/local/bin/passwd2xml /etc/passwd' \\
+ /etc/passwd.xml"
+.Ed
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr unmount 2 ,
+.Xr fstab 5 ,
+.Xr mount 8
+.Rs
+.%A "W. Richard Stevens"
+.%A "Jan-Simon Pendry"
+.%T "Portals in 4.4BSD"
+.%B "USENIX 1995 Technical Conference Proceedings"
+.%O "Berkeley, CA"
+.%I "Peter Honeyman"
+.Re
+.Sh CAVEATS
+This file system may not be NFS-exported.
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Bx 4.4 .
diff --git a/usr.sbin/mount_portalfs/mount_portalfs.c b/usr.sbin/mount_portalfs/mount_portalfs.c
new file mode 100644
index 0000000..9a8c49c
--- /dev/null
+++ b/usr.sbin/mount_portalfs/mount_portalfs.c
@@ -0,0 +1,280 @@
+/*
+ * 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
+char copyright[] =
+"@(#) Copyright (c) 1992, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#if 0
+static char sccsid[] = "@(#)mount_portal.c 8.6 (Berkeley) 4/26/95";
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <sys/syslog.h>
+#include <sys/mount.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+#include "pathnames.h"
+#include "portald.h"
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ MOPT_END
+};
+
+static void usage(void) __dead2;
+
+static sig_atomic_t readcf; /* Set when SIGHUP received */
+
+static void sighup(int sig __unused)
+{
+ readcf ++;
+}
+
+static void sigchld(int sig __unused)
+{
+ pid_t pid;
+
+ while ((pid = waitpid((pid_t) -1, (int *) 0, WNOHANG)) > 0)
+ ;
+ /* wrtp - waitpid _doesn't_ return 0 when no children! */
+#ifdef notdef
+ if (pid < 0 && errno != ECHILD)
+ syslog(LOG_WARNING, "waitpid: %s", strerror(errno));
+#endif
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct portal_args args;
+ struct sockaddr_un un;
+ char *conf;
+ char mountpt[MAXPATHLEN];
+ int mntflags = 0;
+ char tag[32];
+ mode_t um;
+
+ qelem q;
+ int rc;
+ int so;
+ int error = 0;
+
+ /*
+ * Crack command line args
+ */
+ int ch;
+
+ while ((ch = getopt(argc, argv, "o:")) != -1) {
+ switch (ch) {
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags, 0);
+ break;
+ default:
+ error = 1;
+ break;
+ }
+ }
+
+ if (optind != (argc - 2))
+ error = 1;
+
+ if (error)
+ usage();
+
+ /*
+ * Get config file and mount point
+ */
+ conf = argv[optind];
+ if (conf[0] != '/') {
+ (void)fprintf(stderr,
+ "The configuration file must be specified"
+ "through an absolute file path.\n");
+ exit(EX_USAGE);
+ }
+
+ /* resolve the mountpoint with realpath(3) */
+ (void)checkpath(argv[optind+1], mountpt);
+
+ /*
+ * Construct the listening socket
+ */
+ un.sun_family = AF_UNIX;
+ if (sizeof(_PATH_TMPPORTAL) >= sizeof(un.sun_path)) {
+ errx(EX_SOFTWARE, "portal socket name too long");
+ }
+ strcpy(un.sun_path, _PATH_TMPPORTAL);
+ mktemp(un.sun_path);
+ un.sun_len = strlen(un.sun_path);
+
+ so = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (so < 0) {
+ err(EX_OSERR, "socket");
+ }
+ um = umask(077);
+ (void) unlink(un.sun_path);
+ if (bind(so, (struct sockaddr *) &un, sizeof(un)) < 0)
+ err(1, NULL);
+
+ (void) unlink(un.sun_path);
+ (void) umask(um);
+
+ (void) listen(so, 5);
+
+ args.pa_socket = so;
+ sprintf(tag, "portal:%d", getpid());
+ args.pa_config = tag;
+
+ rc = mount("portalfs", mountpt, mntflags, &args);
+ if (rc < 0)
+ err(1, NULL);
+
+ /*
+ * Everything is ready to go - now is a good time to fork
+ */
+#ifndef DEBUG
+ daemon(0, 0);
+#endif
+
+ /*
+ * Start logging (and change name)
+ */
+ openlog("portald", LOG_CONS|LOG_PID, LOG_DAEMON);
+
+ q.q_forw = q.q_back = &q;
+ readcf = 1;
+
+ signal(SIGCHLD, sigchld);
+ signal(SIGHUP, sighup);
+
+ /*
+ * Just loop waiting for new connections and activating them
+ */
+ for (;;) {
+ struct sockaddr_un un2;
+ int len2 = sizeof(un2);
+ int so2;
+ pid_t pid;
+ fd_set fdset;
+
+ /*
+ * Check whether we need to re-read the configuration file
+ */
+ if (readcf) {
+#ifdef DEBUG
+ printf ("re-reading configuration file\n");
+#endif
+ readcf = 0;
+ conf_read(&q, conf);
+ continue;
+ }
+
+ /*
+ * Accept a new connection
+ * Will get EINTR if a signal has arrived, so just
+ * ignore that error code
+ */
+ FD_ZERO(&fdset);
+ FD_SET(so, &fdset);
+ rc = select(so+1, &fdset, (fd_set *) 0, (fd_set *) 0, (struct timeval *) 0);
+ if (rc < 0) {
+ if (errno == EINTR)
+ continue;
+ syslog(LOG_ERR, "select: %s", strerror(errno));
+ exit(EX_OSERR);
+ }
+ if (rc == 0)
+ break;
+ so2 = accept(so, (struct sockaddr *) &un2, &len2);
+ if (so2 < 0) {
+ /*
+ * The unmount function does a shutdown on the socket
+ * which will generated ECONNABORTED on the accept.
+ */
+ if (errno == ECONNABORTED)
+ break;
+ if (errno != EINTR) {
+ syslog(LOG_ERR, "accept: %s", strerror(errno));
+ exit(EX_OSERR);
+ }
+ continue;
+ }
+
+ /*
+ * Now fork a new child to deal with the connection
+ */
+ eagain:;
+ switch (pid = fork()) {
+ case -1:
+ if (errno == EAGAIN) {
+ sleep(1);
+ goto eagain;
+ }
+ syslog(LOG_ERR, "fork: %s", strerror(errno));
+ break;
+ case 0:
+ (void) close(so);
+ activate(&q, so2);
+ exit(0);
+ default:
+ (void) close(so2);
+ break;
+ }
+ }
+ syslog(LOG_INFO, "%s unmounted", mountpt);
+ exit(0);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: mount_portalfs [-o options] config mount-point\n");
+ exit(EX_USAGE);
+}
diff --git a/usr.sbin/mount_portalfs/pathnames.h b/usr.sbin/mount_portalfs/pathnames.h
new file mode 100644
index 0000000..9373c7d
--- /dev/null
+++ b/usr.sbin/mount_portalfs/pathnames.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 1992, 1993
+ * 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.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/5/93
+ *
+ * $FreeBSD$
+ */
+
+#include <paths.h>
+
+#define _PATH_TMPPORTAL "/tmp/portalXXXXXXXXXX" /* Scratch socket name */
diff --git a/usr.sbin/mount_portalfs/portal.conf b/usr.sbin/mount_portalfs/portal.conf
new file mode 100644
index 0000000..398cfb2
--- /dev/null
+++ b/usr.sbin/mount_portalfs/portal.conf
@@ -0,0 +1,7 @@
+# @(#)portal.conf 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+tcplisten/ tcplisten tcplisten/
+tcp/ tcp tcp/
+fs/ file fs/
+pipe/ pipe pipe/
+foo/ exec ./bar bar baz
diff --git a/usr.sbin/mount_portalfs/portald.h b/usr.sbin/mount_portalfs/portald.h
new file mode 100644
index 0000000..5bd63ed
--- /dev/null
+++ b/usr.sbin/mount_portalfs/portald.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 1992, 1993
+ * 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.
+ *
+ * @(#)portald.h 8.1 (Berkeley) 6/5/93
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/cdefs.h>
+#include <fs/portalfs/portal.h>
+
+/*
+ * Meta-chars in an RE. Paths in the config file containing
+ * any of these characters will be matched using regexec, other
+ * paths will be prefix-matched.
+ */
+#define RE_CHARS ".|()[]*+?\\^$"
+
+typedef struct qelem qelem;
+
+struct qelem {
+ qelem *q_forw;
+ qelem *q_back;
+};
+
+typedef struct provider provider;
+struct provider {
+ const char *pr_match;
+ int (*pr_func)(struct portal_cred *,
+ char *key, char **v, int so, int *fdp);
+};
+extern provider providers[];
+
+/*
+ * Portal providers
+ */
+extern int portal_exec(struct portal_cred *,
+ char *key, char **v, int so, int *fdp);
+extern int portal_file(struct portal_cred *,
+ char *key, char **v, int so, int *fdp);
+extern int portal_pipe(struct portal_cred *,
+ char *key, char **v, int so, int *fdp);
+extern int portal_tcp(struct portal_cred *,
+ char *key, char **v, int so, int *fdp);
+extern int portal_tcplisten(struct portal_cred *,
+ char *key, char **v, int so, int *fdp);
+
+/*
+ * Global functions
+ */
+extern void activate(qelem *q, int so);
+extern char **conf_match(qelem *q, char *key);
+extern void conf_read(qelem *q, char *conf);
+extern int set_user_credentials(struct portal_cred *user,
+ struct portal_cred *save_area);
+extern int restore_credentials(struct portal_cred *save_area);
diff --git a/usr.sbin/mount_portalfs/pt_conf.c b/usr.sbin/mount_portalfs/pt_conf.c
new file mode 100644
index 0000000..7f1e817
--- /dev/null
+++ b/usr.sbin/mount_portalfs/pt_conf.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 1992, 1993
+ * 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.
+ *
+ * @(#)pt_conf.c 8.1 (Berkeley) 6/5/93
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include "portald.h"
+
+provider providers[] = {
+ { "exec", portal_exec },
+ { "file", portal_file },
+ { "pipe", portal_pipe },
+ { "tcp", portal_tcp },
+ { "tcplisten", portal_tcplisten },
+ { 0, 0 }
+};
diff --git a/usr.sbin/mount_portalfs/pt_exec.c b/usr.sbin/mount_portalfs/pt_exec.c
new file mode 100644
index 0000000..86a47e9
--- /dev/null
+++ b/usr.sbin/mount_portalfs/pt_exec.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 1992, 1993
+ * 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.
+ *
+ * @(#)pt_exec.c 8.1 (Berkeley) 6/5/93
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include "portald.h"
+
+int portal_exec(struct portal_cred *pcr __unused, char *key __unused,
+ char **v __unused, int so __unused, int *fdp __unused)
+{
+ return (ENOEXEC);
+}
+
diff --git a/usr.sbin/mount_portalfs/pt_file.c b/usr.sbin/mount_portalfs/pt_file.c
new file mode 100644
index 0000000..270f812
--- /dev/null
+++ b/usr.sbin/mount_portalfs/pt_file.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 1992, 1993
+ * 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.
+ *
+ * @(#)pt_file.c 8.3 (Berkeley) 7/3/94
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <errno.h>
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/syslog.h>
+
+#include "portald.h"
+
+int portal_file(struct portal_cred *pcr,
+ char *key, char **v, int so __unused, int *fdp)
+{
+ int fd;
+ char pbuf[MAXPATHLEN];
+ int error;
+ struct portal_cred save_area;
+
+ pbuf[0] = '/';
+ strcpy(pbuf+1, key + (v[1] ? strlen(v[1]) : 0));
+
+#ifdef DEBUG
+ printf("path = %s, uid = %d, gid = %d\n", pbuf, pcr->pcr_uid, pcr->pcr_groups[0]);
+ printf ("fflag = %x, oflag = %x\n", pcr->pcr_flag, (pcr->pcr_flag)-1);
+#endif
+
+ if (set_user_credentials(pcr, &save_area) < 0)
+ return (errno);
+
+ /* dmb convert kernel flags to oflags, see <fcntl.h> */
+ fd = open(pbuf, (pcr->pcr_flag)-1, 0777);
+ if (fd < 0)
+ error = errno;
+ else
+ error = 0;
+
+ if (restore_credentials(&save_area) < 0) {
+ error = errno;
+ if (fd >= 0) {
+ (void) close(fd);
+ fd = -1;
+ }
+ }
+
+ if (error == 0)
+ *fdp = fd;
+
+#ifdef DEBUG
+ fprintf(stderr, "pt_file returns *fdp = %d, error = %d\n", *fdp, error);
+#endif
+
+ return (error);
+}
diff --git a/usr.sbin/mount_portalfs/pt_pipe.c b/usr.sbin/mount_portalfs/pt_pipe.c
new file mode 100644
index 0000000..fd54b7e
--- /dev/null
+++ b/usr.sbin/mount_portalfs/pt_pipe.c
@@ -0,0 +1,229 @@
+/*-
+ * Copyright (C) 2005 Diomidis Spinellis. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <ctype.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/param.h>
+#include <sys/syslog.h>
+
+#include "portald.h"
+
+/* Usage conventions for the pipe's endpoints. */
+#define READ_END 0
+#define WRITE_END 1
+
+static int errlog(void);
+static int parse_argv(char *args, char **argv);
+
+int portal_pipe(struct portal_cred *pcr, char *key, char **v,
+ int kso __unused, int *fdp)
+{
+ int fd[2]; /* Pipe endpoints. */
+ int caller_end; /* The pipe end we will use. */
+ int process_end; /* The pipe end the spawned process will use. */
+ int redirect_fd; /* The fd to redirect on the spawned process. */
+ char pbuf[MAXPATHLEN];
+ int error = 0;
+ int i;
+ char **argv;
+ int argc;
+ struct portal_cred save_area;
+
+ /* Validate open mode, and assign roles. */
+ if ((pcr->pcr_flag & FWRITE) && (pcr->pcr_flag & FREAD))
+ /* Don't allow both on a single fd. */
+ return (EINVAL);
+ else if (pcr->pcr_flag & FREAD) {
+ /*
+ * The caller reads from the pipe,
+ * the spawned process writes to it.
+ */
+ caller_end = READ_END;
+ process_end = WRITE_END;
+ redirect_fd = STDOUT_FILENO;
+ } else if (pcr->pcr_flag & FWRITE) {
+ /*
+ * The caller writes to the pipe,
+ * the spawned process reads from it.
+ */
+ caller_end = WRITE_END;
+ process_end = READ_END;
+ redirect_fd = STDIN_FILENO;
+ } else
+ return (EINVAL);
+
+ /* Get and check command line. */
+ pbuf[0] = '/';
+ strcpy(pbuf+1, key + (v[1] ? strlen(v[1]) : 0));
+ argc = parse_argv(pbuf, NULL);
+ if (argc == 0)
+ return (ENOENT);
+
+ /* Swap priviledges. */
+ if (set_user_credentials(pcr, &save_area) < 0)
+ return (errno);
+
+ /* Redirect and spawn the specified process. */
+ fd[READ_END] = fd[WRITE_END] = -1;
+ if (pipe(fd) < 0) {
+ error = errno;
+ goto done;
+ }
+ switch (fork()) {
+ case -1: /* Error */
+ error = errno;
+ break;
+ default: /* Parent */
+ (void)close(fd[process_end]);
+ break;
+ case 0: /* Child */
+ argv = (char **)malloc((argc + 1) * sizeof(char *));
+ if (argv == 0) {
+ syslog(LOG_ALERT,
+ "malloc: failed to get space for %d pointers",
+ argc + 1);
+ exit(EXIT_FAILURE);
+ }
+ parse_argv(pbuf, argv);
+
+ if (dup2(fd[process_end], redirect_fd) < 0) {
+ syslog(LOG_ERR, "dup2: %m");
+ exit(EXIT_FAILURE);
+ }
+ (void)close(fd[caller_end]);
+ (void)close(fd[process_end]);
+ if (errlog() < 0) {
+ syslog(LOG_ERR, "errlog: %m");
+ exit(EXIT_FAILURE);
+ }
+ if (execv(argv[0], argv) < 0) {
+ syslog(LOG_ERR, "execv(%s): %m", argv[0]);
+ exit(EXIT_FAILURE);
+ }
+ /* NOTREACHED */
+ }
+
+done:
+ /* Re-establish our priviledges. */
+ if (restore_credentials(&save_area) < 0)
+ error = errno;
+
+ /* Set return fd value. */
+ if (error == 0)
+ *fdp = fd[caller_end];
+ else {
+ for (i = 0; i < 2; i++)
+ if (fd[i] >= 0)
+ (void)close(fd[i]);
+ *fdp = -1;
+ }
+
+ return (error);
+}
+
+/*
+ * Redirect stderr to the system log.
+ * Return 0 if ok.
+ * Return -1 with errno set on error.
+ */
+static int
+errlog(void)
+{
+ int fd[2];
+ char buff[1024];
+ FILE *f;
+ int ret = 0;
+
+ if (pipe(fd) < 0)
+ return (-1);
+ switch (fork()) {
+ case -1: /* Error */
+ return (-1);
+ case 0: /* Child */
+ if ((f = fdopen(fd[READ_END], "r")) == NULL) {
+ syslog(LOG_ERR, "fdopen: %m");
+ exit(EXIT_FAILURE);
+ }
+ (void)close(fd[WRITE_END]);
+ while (fgets(buff, sizeof(buff), f) != NULL)
+ syslog(LOG_ERR, "exec: %s", buff);
+ exit(EXIT_SUCCESS);
+ /* NOTREACHED */
+ default: /* Parent */
+ if (dup2(fd[WRITE_END], STDERR_FILENO) < 0)
+ ret = -1;
+ (void)close(fd[READ_END]);
+ (void)close(fd[WRITE_END]);
+ break;
+ }
+ return (ret);
+}
+
+/*
+ * Parse the args string as a space-separated argument vector.
+ * If argv is not NULL, split the string into its constituent
+ * components, and set argv to point to the beginning of each
+ * string component; NULL-terminating argv.
+ * Return the number of string components.
+ */
+static int
+parse_argv(char *args, char **argv)
+{
+ int count = 0;
+ char *p;
+ enum {WORD, SPACE} state = SPACE;
+
+ for (p = args; *p; p++)
+ switch (state) {
+ case WORD:
+ if (isspace(*p)) {
+ if (argv)
+ *p = '\0';
+ state = SPACE;
+ }
+ break;
+ case SPACE:
+ if (!isspace(*p)) {
+ if (argv)
+ argv[count] = p;
+ count++;
+ state = WORD;
+ }
+ }
+ if (argv)
+ argv[count] = NULL;
+ return (count);
+}
diff --git a/usr.sbin/mount_portalfs/pt_tcp.c b/usr.sbin/mount_portalfs/pt_tcp.c
new file mode 100644
index 0000000..d9a2a1e
--- /dev/null
+++ b/usr.sbin/mount_portalfs/pt_tcp.c
@@ -0,0 +1,157 @@
+/*
+ * 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.
+ *
+ * @(#)pt_tcp.c 8.5 (Berkeley) 4/28/95
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/syslog.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "portald.h"
+
+/*
+ * Key will be tcp/host/port[/"priv"]
+ * Create a TCP socket connected to the
+ * requested host and port.
+ * Some trailing suffix values have special meanings.
+ * An unrecognized suffix is an error.
+ */
+int portal_tcp(struct portal_cred *pcr, char *key, char **v,
+ int kso __unused, int *fdp)
+{
+ char host[MAXHOSTNAMELEN];
+ char port[MAXHOSTNAMELEN];
+ char *p = key + (v[1] ? strlen(v[1]) : 0);
+ char *q;
+ struct hostent *hp;
+ struct servent *sp;
+ struct in_addr **ipp;
+ struct in_addr *ip[2];
+ struct in_addr ina;
+ u_short s_port;
+ int priv = 0;
+ struct sockaddr_in sain;
+
+ q = strchr(p, '/');
+ if (q == 0 || q - p >= (int)sizeof(host))
+ return (EINVAL);
+ *q = '\0';
+ strcpy(host, p);
+ p = q + 1;
+
+ q = strchr(p, '/');
+ if (q)
+ *q = '\0';
+ if (strlen(p) >= sizeof(port))
+ return (EINVAL);
+ strcpy(port, p);
+ if (q) {
+ p = q + 1;
+ if (strcmp(p, "priv") == 0) {
+ if (pcr->pcr_uid == 0)
+ priv = 1;
+ else
+ return (EPERM);
+ } else {
+ return (EINVAL);
+ }
+ }
+
+ hp = gethostbyname(host);
+ if (hp != 0) {
+ ipp = (struct in_addr **) hp->h_addr_list;
+ } else {
+ ina.s_addr = inet_addr(host);
+ if (ina.s_addr == INADDR_NONE)
+ return (EINVAL);
+ ip[0] = &ina;
+ ip[1] = 0;
+ ipp = ip;
+ }
+#ifdef DEBUG
+ printf ("inet address for %s is %s\n", host, inet_ntoa(*ipp[0]));
+#endif
+
+ sp = getservbyname(port, "tcp");
+ if (sp != NULL) {
+ s_port = (u_short)sp->s_port;
+ } else {
+ s_port = strtoul(port, &p, 0);
+ if (s_port == 0 || *p != '\0')
+ return (EINVAL);
+ s_port = htons(s_port);
+ }
+#ifdef DEBUG
+ printf ("port number for %s is %d\n", port, (int)ntohs(s_port));
+#endif
+
+ memset(&sain, 0, sizeof(sain));
+ sain.sin_len = sizeof(sain);
+ sain.sin_family = AF_INET;
+ sain.sin_port = s_port;
+
+ while (ipp[0]) {
+ int so;
+
+ if (priv)
+ so = rresvport((int *) 0);
+ else
+ so = socket(AF_INET, SOCK_STREAM, 0);
+ if (so < 0) {
+ syslog(LOG_ERR, "socket: %m");
+ return (errno);
+ }
+
+ sain.sin_addr = *ipp[0];
+ if (connect(so, (struct sockaddr *) &sain, sizeof(sain)) == 0) {
+ *fdp = so;
+ return (0);
+ }
+ (void) close(so);
+
+ ipp++;
+ }
+
+ return (errno);
+}
diff --git a/usr.sbin/mount_portalfs/pt_tcplisten.c b/usr.sbin/mount_portalfs/pt_tcplisten.c
new file mode 100644
index 0000000..b7febb1
--- /dev/null
+++ b/usr.sbin/mount_portalfs/pt_tcplisten.c
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 1992, 1993
+ * 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.
+ *
+ * Modified by Duncan Barclay.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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.
+ *
+ * @(#)pt_tcp.c 8.3 (Berkeley) 3/27/94
+ *
+ * pt_tcp.c,v 1.1.1.1 1994/05/26 06:34:34 rgrimes Exp
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/syslog.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "portald.h"
+
+/*
+ * Key will be tcplisten/host/port
+ *
+ * Create a TCP socket bound to the requested host and port.
+ * If the host is "ANY" the receving address will be set to INADDR_ANY.
+ * If the port is 0 the caller must find out the returned port number
+ * using a call to getsockname.
+ *
+ * XXX! The owner of the socket will be root rather then the user. This
+ * may cause remote auth (identd) to return unexpected results.
+ *
+ */
+int portal_tcplisten(struct portal_cred *pcr, char *key, char **v,
+ int kso __unused, int *fdp)
+{
+ char host[MAXHOSTNAMELEN];
+ char port[MAXHOSTNAMELEN];
+ char *p = key + (v[1] ? strlen(v[1]) : 0);
+ char *q;
+ struct hostent *hp;
+ struct servent *sp;
+ struct in_addr **ipp = NULL;
+ struct in_addr *ip[2];
+ struct in_addr ina;
+ u_short s_port;
+ int any = 0;
+ struct sockaddr_in sain;
+
+ q = strchr(p, '/');
+ if (q == 0 || q - p >= (int)sizeof(host))
+ return (EINVAL);
+ *q = '\0';
+ snprintf(host, sizeof(host), "%s", p);
+ p = q + 1;
+
+ q = strchr(p, '/');
+ if (q)
+ *q = '\0';
+ if (strlen(p) >= sizeof(port))
+ return (EINVAL);
+ snprintf(port, sizeof(port), "%s", p);
+
+ if (strcmp(host, "ANY") == 0) {
+ any = 1;
+ } else {
+ hp = gethostbyname(host);
+ if (hp != 0) {
+ ipp = (struct in_addr **) hp->h_addr_list;
+ } else {
+ ina.s_addr = inet_addr(host);
+ if (ina.s_addr == INADDR_NONE)
+ return (EINVAL);
+ ip[0] = &ina;
+ ip[1] = 0;
+ ipp = ip;
+ }
+ }
+#ifdef DEBUG
+ if (any)
+ printf("INADDR_ANY to be used for hostname\n");
+ else
+ printf("inet address for %s is %s\n", host, inet_ntoa(*ipp[0]));
+#endif
+
+ sp = getservbyname(port, "tcp");
+ if (sp != NULL) {
+ s_port = (u_short) sp->s_port;
+ } else {
+ s_port = strtoul(port, &p, 0);
+ if (*p != '\0')
+ return (EINVAL);
+ s_port = htons(s_port);
+ }
+ if ((ntohs(s_port) != 0) &&
+ (ntohs(s_port) <= IPPORT_RESERVED) &&
+ (pcr->pcr_uid != 0))
+ return (EPERM);
+#ifdef DEBUG
+ printf("port number for %s is %d\n", port, ntohs(s_port));
+#endif
+
+ memset(&sain, 0, sizeof(sain));
+ sain.sin_len = sizeof(sain);
+ sain.sin_family = AF_INET;
+ sain.sin_port = s_port;
+
+ if (any) {
+ int so;
+ int sock;
+
+ so = socket(AF_INET, SOCK_STREAM, 0);
+ if (so < 0) {
+ syslog(LOG_ERR, "socket: %m");
+ return (errno);
+ }
+
+ sain.sin_addr.s_addr = INADDR_ANY;
+ if (bind(so, (struct sockaddr *) &sain, sizeof(sain)) == 0) {
+ listen(so, 1);
+ if ((sock = accept(so, (struct sockaddr *)0, (int *)0)) == -1) {
+ syslog(LOG_ERR, "accept: %m");
+ (void) close(so);
+ return (errno);
+ }
+ *fdp = sock;
+ (void) close(so);
+ return (0);
+ }
+ syslog(LOG_ERR, "bind: %m");
+ (void) close(so);
+ return (errno);
+ }
+
+ while (ipp[0]) {
+ int so;
+ int sock;
+
+ so = socket(AF_INET, SOCK_STREAM, 0);
+ if (so < 0) {
+ syslog(LOG_ERR, "socket: %m");
+ return (errno);
+ }
+
+ sain.sin_addr = *ipp[0];
+ if (bind(so, (struct sockaddr *) &sain, sizeof(sain)) == 0) {
+ listen(so, 1);
+ if ((sock = accept(so, (struct sockaddr *)0, (int *)0)) == -1) {
+ syslog(LOG_ERR, "accept: %m");
+ (void) close(so);
+ return (errno);
+ }
+ *fdp = sock;
+ (void) close(so);
+ return (0);
+ }
+ (void) close(so);
+
+ ipp++;
+ }
+
+ syslog(LOG_ERR, "bind: %m");
+ return (errno);
+
+}
diff --git a/usr.sbin/mount_smbfs/Makefile b/usr.sbin/mount_smbfs/Makefile
new file mode 100644
index 0000000..ade4fad
--- /dev/null
+++ b/usr.sbin/mount_smbfs/Makefile
@@ -0,0 +1,23 @@
+# $FreeBSD$
+
+PROG= mount_smbfs
+SRCS= mount_smbfs.c getmntopts.c
+WARNS?= 6
+MAN= mount_smbfs.8
+
+MOUNTDIR= ${.CURDIR}/../../sbin/mount
+CONTRIBDIR= ${.CURDIR}/../../contrib/smbfs
+CFLAGS+= -DSMBFS -I${MOUNTDIR} -I${CONTRIBDIR}/include
+
+LDADD= -lsmb -lkiconv
+DPADD= ${LIBSMB} ${LIBKICONV}
+
+# Needs to be dynamically linked for optional dlopen() access to
+# userland libiconv (see the -E option).
+#
+NO_SHARED?= NO
+
+.PATH: ${CONTRIBDIR}/mount_smbfs
+.PATH: ${MOUNTDIR}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mountd/Makefile b/usr.sbin/mountd/Makefile
new file mode 100644
index 0000000..c141d8a
--- /dev/null
+++ b/usr.sbin/mountd/Makefile
@@ -0,0 +1,17 @@
+# From: @(#)Makefile 8.3 (Berkeley) 1/25/94
+# $FreeBSD$
+
+PROG= mountd
+SRCS= mountd.c getmntopts.c
+MAN= exports.5 netgroup.5 mountd.8
+
+MOUNT= ${.CURDIR}/../../sbin/mount
+CFLAGS+= -I${MOUNT}
+WARNS?= 2
+
+.PATH: ${MOUNT}
+
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mountd/exports.5 b/usr.sbin/mountd/exports.5
new file mode 100644
index 0000000..dca4f2d
--- /dev/null
+++ b/usr.sbin/mountd/exports.5
@@ -0,0 +1,449 @@
+.\" 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.
+.\"
+.\" @(#)exports.5 8.3 (Berkeley) 3/29/95
+.\" $FreeBSD$
+.\"
+.Dd June 30, 2008
+.Dt EXPORTS 5
+.Os
+.Sh NAME
+.Nm exports
+.Nd define remote mount points for
+.Tn NFS
+mount requests
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+file specifies remote mount points for the
+.Tn NFS
+mount protocol per the
+.Tn NFS
+server specification; see
+.%T "Network File System Protocol Specification" ,
+RFC1094, Appendix A and
+.%T "NFS: Network File System Version 3 Specification" ,
+Appendix I.
+.Pp
+Each line in the file
+(other than comment lines that begin with a #)
+specifies the mount point(s) and export flags within one local server
+file system for one or more hosts.
+A long line may be split over several lines by ending all but the
+last line with a backslash
+.Pq Ql \e .
+A host may be specified only once for each local file system on the
+server and there may be only one default entry for each server
+file system that applies to all other hosts.
+The latter exports the file system to the
+.Dq world
+and should
+be used only when the file system contains public information.
+.Pp
+In a mount entry,
+the first field(s) specify the directory path(s) within a server file system
+that can be mounted on by the corresponding client(s).
+There are two forms of this specification.
+The first is to list all mount points as absolute
+directory paths separated by whitespace.
+The second is to specify the pathname of the root of the file system
+followed by the
+.Fl alldirs
+flag;
+this form allows the host(s) to mount at any point within the file system,
+including regular files if the
+.Fl r
+option is used on
+.Xr mountd 8 .
+The pathnames must not have any symbolic links in them and should not have
+any
+.Dq Pa \&.
+or
+.Dq Pa ..
+components.
+Mount points for a file system may appear on multiple lines each with
+different sets of hosts and export options.
+.Pp
+The second component of a line specifies how the file system is to be
+exported to the host set.
+The option flags specify whether the file system
+is exported read-only or read-write and how the client UID is mapped to
+user credentials on the server.
+.Pp
+Export options are specified as follows:
+.Pp
+.Sm off
+.Fl maproot Li = Sy user
+.Sm on
+The credential of the specified user is used for remote access by root.
+The credential includes all the groups to which the user is a member
+on the local machine (see
+.Xr id 1 ) .
+The user may be specified by name or number.
+.Pp
+.Sm off
+.Fl maproot Li = Sy user:group1:group2:...
+.Sm on
+The colon separated list is used to specify the precise credential
+to be used for remote access by root.
+The elements of the list may be either names or numbers.
+Note that user: should be used to distinguish a credential containing
+no groups from a complete credential for that user.
+.Pp
+.Sm off
+.Fl mapall Li = Sy user
+.Sm on
+or
+.Sm off
+.Fl mapall Li = Sy user:group1:group2:...
+.Sm on
+specifies a mapping for all client UIDs (including root)
+using the same semantics as
+.Fl maproot .
+.Pp
+The option
+.Fl r
+is a synonym for
+.Fl maproot
+in an effort to be backward compatible with older export file formats.
+.Pp
+In the absence of
+.Fl maproot
+and
+.Fl mapall
+options, remote accesses by root will result in using a credential of -2:-2.
+All other users will be mapped to their remote credential.
+If a
+.Fl maproot
+option is given,
+remote access by root will be mapped to that credential instead of -2:-2.
+If a
+.Fl mapall
+option is given,
+all users (including root) will be mapped to that credential in
+place of their own.
+.Pp
+.Sm off
+.Fl sec Li = Sy flavor1:flavor2...
+.Sm on
+specifies a colon separated list of acceptable security flavors to be
+used for remote access.
+Supported security flavors are sys, krb5, krb5i and krb5p.
+If multiple flavors are listed, they should be ordered with the most
+preferred flavor first.
+If this option is not present,
+the default security flavor list of just sys is used.
+.Pp
+The
+.Fl ro
+option specifies that the file system should be exported read-only
+(default read/write).
+The option
+.Fl o
+is a synonym for
+.Fl ro
+in an effort to be backward compatible with older export file formats.
+.Pp
+.Tn WebNFS
+exports strictly according to the spec (RFC 2054 and RFC 2055) can
+be done with the
+.Fl public
+flag.
+However, this flag in itself allows r/w access to all files in
+the file system, not requiring reserved ports and not remapping UIDs.
+It
+is only provided to conform to the spec, and should normally not be used.
+For a
+.Tn WebNFS
+export,
+use the
+.Fl webnfs
+flag, which implies
+.Fl public ,
+.Sm off
+.Fl mapall No = Sy nobody
+.Sm on
+and
+.Fl ro .
+Note that only one file system can be
+.Tn WebNFS
+exported on a server.
+.Pp
+A
+.Sm off
+.Fl index No = Pa file
+.Sm on
+option can be used to specify a file whose handle will be returned if
+a directory is looked up using the public filehandle
+.Pq Tn WebNFS .
+This is to mimic the behavior of URLs.
+If no
+.Fl index
+option is specified, a directory filehandle will be returned as usual.
+The
+.Fl index
+option only makes sense in combination with the
+.Fl public
+or
+.Fl webnfs
+flags.
+.Pp
+Specifying the
+.Fl quiet
+option will inhibit some of the syslog diagnostics for bad lines in
+.Pa /etc/exports .
+This can be useful to avoid annoying error messages for known possible
+problems (see
+.Sx EXAMPLES
+below).
+.Pp
+The third component of a line specifies the host set to which the line applies.
+The set may be specified in three ways.
+The first way is to list the host name(s) separated by white space.
+(Standard Internet
+.Dq dot
+addresses may be used in place of names.)
+The second way is to specify a
+.Dq netgroup
+as defined in the
+.Pa netgroup
+file (see
+.Xr netgroup 5 ) .
+The third way is to specify an Internet subnetwork using a network and
+network mask that is defined as the set of all hosts with addresses within
+the subnetwork.
+This latter approach requires less overhead within the
+kernel and is recommended for cases where the export line refers to a
+large number of clients within an administrative subnet.
+.Pp
+The first two cases are specified by simply listing the name(s) separated
+by whitespace.
+All names are checked to see if they are
+.Dq netgroup
+names
+first and are assumed to be hostnames otherwise.
+Using the full domain specification for a hostname can normally
+circumvent the problem of a host that has the same name as a netgroup.
+The third case is specified by the flag
+.Sm off
+.Fl network Li = Sy netname Op Li / Ar prefixlength
+.Sm on
+and optionally
+.Sm off
+.Fl mask No = Sy netmask .
+.Sm on
+The netmask may be specified either by attaching a
+.Ar prefixlength
+to the
+.Fl network
+option, or by using a separate
+.Fl mask
+option.
+If the mask is not specified, it will default to the mask for that network
+class (A, B or C; see
+.Xr inet 4 ) .
+See the
+.Sx EXAMPLES
+section below.
+.Pp
+Scoped IPv6 address must carry scope identifier as documented in
+.Xr inet6 4 .
+For example,
+.Dq Li fe80::%re2/10
+is used to specify
+.Li fe80::/10
+on
+.Li re2
+interface.
+.Pp
+The
+.Xr mountd 8
+utility can be made to re-read the
+.Nm
+file by sending it a hangup signal as follows:
+.Bd -literal -offset indent
+/etc/rc.d/mountd reload
+.Ed
+.Pp
+After sending the
+.Dv SIGHUP ,
+check the
+.Xr syslogd 8
+output to see whether
+.Xr mountd 8
+logged any parsing errors in the
+.Nm
+file.
+.Sh FILES
+.Bl -tag -width /etc/exports -compact
+.It Pa /etc/exports
+the default remote mount-point file
+.El
+.Sh EXAMPLES
+.Bd -literal -offset indent
+/usr /usr/local -maproot=0:10 friends
+/usr -maproot=daemon grumpy.cis.uoguelph.ca 131.104.48.16
+/usr -ro -mapall=nobody
+/u -maproot=bin: -network 131.104.48 -mask 255.255.255.0
+/a -network 192.168.0/24
+/a -network 3ffe:1ce1:1:fe80::/64
+/u2 -maproot=root friends
+/u2 -alldirs -network cis-net -mask cis-mask
+/cdrom -alldirs,quiet,ro -network 192.168.33.0 -mask 255.255.255.0
+/private -sec=krb5i
+/secret -sec=krb5p
+.Ed
+.Pp
+Given that
+.Pa /usr , /u , /a
+and
+.Pa /u2
+are
+local file system mount points, the above example specifies the following:
+.Pp
+The file system rooted at
+.Pa /usr
+is exported to hosts
+.Em friends
+where friends is specified in the netgroup file
+with users mapped to their remote credentials and
+root mapped to UID 0 and group 10.
+It is exported read-write and the hosts in
+.Dq friends
+can mount either
+.Pa /usr
+or
+.Pa /usr/local .
+It is exported to
+.Em 131.104.48.16
+and
+.Em grumpy.cis.uoguelph.ca
+with users mapped to their remote credentials and
+root mapped to the user and groups associated with
+.Dq daemon ;
+it is exported to the rest of the world as read-only with
+all users mapped to the user and groups associated with
+.Dq nobody .
+.Pp
+The file system rooted at
+.Pa /u
+is exported to all hosts on the subnetwork
+.Em 131.104.48
+with root mapped to the UID for
+.Dq bin
+and with no group access.
+.Pp
+The file system rooted at
+.Pa /u2
+is exported to the hosts in
+.Dq friends
+with root mapped to UID and groups
+associated with
+.Dq root ;
+it is exported to all hosts on network
+.Dq cis-net
+allowing mounts at any
+directory within /u2.
+.Pp
+The file system rooted at
+.Pa /a
+is exported to the network 192.168.0.0, with a netmask of 255.255.255.0.
+However, the netmask length in the entry for
+.Pa /a
+is not specified through a
+.Fl mask
+option, but through the
+.Li / Ns Ar prefix
+notation.
+.Pp
+The file system rooted at
+.Pa /a
+is also exported to the IPv6 network
+.Li 3ffe:1ce1:1:fe80::
+address, using the upper 64 bits as the prefix.
+Note that, unlike with IPv4 network addresses, the specified network
+address must be complete, and not just contain the upper bits.
+With IPv6 addresses, the
+.Fl mask
+option must not be used.
+.Pp
+The file system rooted at
+.Pa /cdrom
+will be exported read-only to the entire network 192.168.33.0/24, including
+all its subdirectories.
+Since
+.Pa /cdrom
+is the conventional mountpoint for a CD-ROM device, this export will
+fail if no CD-ROM medium is currently mounted there since that line
+would then attempt to export a subdirectory of the root file system
+with the
+.Fl alldirs
+option which is not allowed.
+The
+.Fl quiet
+option will then suppress the error message for this condition that
+would normally be syslogged.
+As soon as an actual CD-ROM is going to be mounted,
+.Xr mount 8
+will notify
+.Xr mountd 8
+about this situation, and the
+.Pa /cdrom
+file system will be exported as intended.
+Note that without using the
+.Fl alldirs
+option, the export would always succeed.
+While there is no CD-ROM medium mounted under
+.Pa /cdrom ,
+it would export the (normally empty) directory
+.Pa /cdrom
+of the root file system instead.
+.Pp
+The file system rooted at
+.Pa /private
+will be exported using Kerberos 5 authentication and will require
+integrity protected messages for all accesses.
+The file system rooted at
+.Pa /secret
+will also be exported using Kerberos 5 authentication and all messages
+used to access it will be encrypted.
+.Sh SEE ALSO
+.Xr netgroup 5 ,
+.Xr mountd 8 ,
+.Xr nfsd 8 ,
+.Xr showmount 8
+.Sh BUGS
+The export options are tied to the local mount points in the kernel and
+must be non-contradictory for any exported subdirectory of the local
+server mount point.
+It is recommended that all exported directories within the same server
+file system be specified on adjacent lines going down the tree.
+You cannot specify a hostname that is also the name of a netgroup.
+Specifying the full domain specification for a hostname can normally
+circumvent the problem.
diff --git a/usr.sbin/mountd/mountd.8 b/usr.sbin/mountd/mountd.8
new file mode 100644
index 0000000..4f1d614
--- /dev/null
+++ b/usr.sbin/mountd/mountd.8
@@ -0,0 +1,182 @@
+.\" 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.
+.\"
+.\" @(#)mountd.8 8.4 (Berkeley) 4/28/95
+.\" $FreeBSD$
+.\"
+.Dd October 20, 2007
+.Dt MOUNTD 8
+.Os
+.Sh NAME
+.Nm mountd
+.Nd service remote
+.Tn NFS
+mount requests
+.Sh SYNOPSIS
+.Nm
+.Op Fl 2dlnr
+.Op Fl h Ar bindip
+.Op Fl p Ar port
+.Op Ar exportsfile ...
+.Sh DESCRIPTION
+The
+.Nm
+utility is the server for
+.Tn NFS
+mount requests from other client machines.
+It listens for service requests at the port indicated in the
+.Tn NFS
+server specification; see
+.%T "Network File System Protocol Specification" ,
+RFC1094, Appendix A and
+.%T "NFS: Network File System Version 3 Protocol Specification" ,
+Appendix I.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl 2
+Allow the administrator to force clients to use only the
+version 2
+.Tn NFS
+protocol to mount file systems from this server.
+.It Fl d
+Output debugging information.
+.Nm
+will not detach from the controlling terminal and will print
+debugging messages to stderr.
+.It Fl h Ar bindip
+Specify specific IP addresses to bind to for TCP and UDP requests.
+This option may be specified multiple times.
+If no
+.Fl h
+option is specified,
+.Nm
+will bind to
+.Dv INADDR_ANY .
+Note that when specifying IP addresses with
+.Fl h ,
+.Nm
+will automatically add
+.Li 127.0.0.1
+and if IPv6 is enabled,
+.Li ::1
+to the list.
+.It Fl l
+Cause all succeeded
+.Nm
+requests to be logged.
+.It Fl n
+Allow non-root mount requests to be served.
+This should only be specified if there are clients such as PC's,
+that require it.
+It will automatically clear the vfs.nfsrv.nfs_privport sysctl flag, which
+controls if the kernel will accept NFS requests from reserved ports only.
+.It Fl p Ar port
+Force
+.Nm
+to bind to the specified port, for both
+.Dv AF_INET
+and
+.Dv AF_INET6
+address families.
+This is typically done to ensure that the port which
+.Nm
+binds to is a known quantity which can be used in firewall rulesets.
+If
+.Nm
+cannot bind to this port, an appropriate error will be recorded in
+the system log, and the daemon will then exit.
+.It Fl r
+Allow mount RPCs requests for regular files to be served.
+Although this seems to violate the mount protocol specification,
+some diskless workstations do mount requests for
+their swapfiles and expect them to be regular files.
+Since a regular file cannot be specified in
+.Pa /etc/exports ,
+the entire file system in which the swapfiles resides
+will have to be exported with the
+.Fl alldirs
+flag.
+.It Ar exportsfile
+Specify an alternate location
+for the exports file.
+More than one exports file can be specified.
+.El
+.Pp
+When
+.Nm
+is started,
+it loads the export host addresses and options into the kernel
+using the
+.Xr mount 2
+system call.
+After changing the exports file,
+a hangup signal should be sent to the
+.Nm
+daemon
+to get it to reload the export information.
+After sending the SIGHUP
+(kill \-s HUP `cat /var/run/mountd.pid`),
+check the syslog output to see if
+.Nm
+logged any parsing
+errors in the exports file.
+.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 FILES
+.Bl -tag -width /var/run/mountd.pid -compact
+.It Pa /etc/exports
+the list of exported file systems
+.It Pa /var/run/mountd.pid
+the pid of the currently running mountd
+.It Pa /var/db/mountdtab
+the current list of remote mounted file systems
+.El
+.Sh SEE ALSO
+.Xr nfsstat 1 ,
+.Xr kldload 2 ,
+.Xr exports 5 ,
+.Xr nfsd 8 ,
+.Xr rpcbind 8 ,
+.Xr showmount 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Bx 4.4 .
diff --git a/usr.sbin/mountd/mountd.c b/usr.sbin/mountd/mountd.c
new file mode 100644
index 0000000..82ab1b8
--- /dev/null
+++ b/usr.sbin/mountd/mountd.c
@@ -0,0 +1,2873 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Herb Hasler and 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.
+ */
+
+#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*/
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)mountd.c 8.15 (Berkeley) 5/1/95";
+#endif /*not lint*/
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/syslog.h>
+#include <sys/sysctl.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+
+#include <rpc/rpc.h>
+#include <rpc/rpc_com.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/pmap_prot.h>
+#include <rpcsvc/mount.h>
+#include <nfs/rpcv2.h>
+#include <nfs/nfsproto.h>
+#include <nfsserver/nfs.h>
+
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <grp.h>
+#include <libutil.h>
+#include <limits.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "pathnames.h"
+#include "mntopts.h"
+
+#ifdef DEBUG
+#include <stdarg.h>
+#endif
+
+/*
+ * Structures for keeping the mount list and export list
+ */
+struct mountlist {
+ struct mountlist *ml_next;
+ char ml_host[RPCMNT_NAMELEN+1];
+ char ml_dirp[RPCMNT_PATHLEN+1];
+};
+
+struct dirlist {
+ struct dirlist *dp_left;
+ struct dirlist *dp_right;
+ int dp_flag;
+ struct hostlist *dp_hosts; /* List of hosts this dir exported to */
+ char dp_dirp[1]; /* Actually malloc'd to size of dir */
+};
+/* dp_flag bits */
+#define DP_DEFSET 0x1
+#define DP_HOSTSET 0x2
+
+struct exportlist {
+ struct exportlist *ex_next;
+ struct dirlist *ex_dirl;
+ struct dirlist *ex_defdir;
+ int ex_flag;
+ fsid_t ex_fs;
+ char *ex_fsdir;
+ char *ex_indexfile;
+ int ex_numsecflavors;
+ int ex_secflavors[MAXSECFLAVORS];
+};
+/* ex_flag bits */
+#define EX_LINKED 0x1
+
+struct netmsk {
+ struct sockaddr_storage nt_net;
+ struct sockaddr_storage nt_mask;
+ char *nt_name;
+};
+
+union grouptypes {
+ struct addrinfo *gt_addrinfo;
+ struct netmsk gt_net;
+};
+
+struct grouplist {
+ int gr_type;
+ union grouptypes gr_ptr;
+ struct grouplist *gr_next;
+};
+/* Group types */
+#define GT_NULL 0x0
+#define GT_HOST 0x1
+#define GT_NET 0x2
+#define GT_DEFAULT 0x3
+#define GT_IGNORE 0x5
+
+struct hostlist {
+ int ht_flag; /* Uses DP_xx bits */
+ struct grouplist *ht_grp;
+ struct hostlist *ht_next;
+};
+
+struct fhreturn {
+ int fhr_flag;
+ int fhr_vers;
+ nfsfh_t fhr_fh;
+ int fhr_numsecflavors;
+ int *fhr_secflavors;
+};
+
+/* Global defs */
+char *add_expdir(struct dirlist **, char *, int);
+void add_dlist(struct dirlist **, struct dirlist *,
+ struct grouplist *, int);
+void add_mlist(char *, char *);
+int check_dirpath(char *);
+int check_options(struct dirlist *);
+int checkmask(struct sockaddr *sa);
+int chk_host(struct dirlist *, struct sockaddr *, int *, int *);
+void create_service(struct netconfig *nconf);
+void del_mlist(char *hostp, char *dirp);
+struct dirlist *dirp_search(struct dirlist *, char *);
+int do_mount(struct exportlist *, struct grouplist *, int,
+ struct xucred *, char *, int, struct statfs *);
+int do_opt(char **, char **, struct exportlist *, struct grouplist *,
+ int *, int *, struct xucred *);
+struct exportlist *ex_search(fsid_t *);
+struct exportlist *get_exp(void);
+void free_dir(struct dirlist *);
+void free_exp(struct exportlist *);
+void free_grp(struct grouplist *);
+void free_host(struct hostlist *);
+void get_exportlist(void);
+int get_host(char *, struct grouplist *, struct grouplist *);
+struct hostlist *get_ht(void);
+int get_line(void);
+void get_mountlist(void);
+int get_net(char *, struct netmsk *, int);
+void getexp_err(struct exportlist *, struct grouplist *);
+struct grouplist *get_grp(void);
+void hang_dirp(struct dirlist *, struct grouplist *,
+ struct exportlist *, int);
+void huphandler(int sig);
+int makemask(struct sockaddr_storage *ssp, int bitlen);
+void mntsrv(struct svc_req *, SVCXPRT *);
+void nextfield(char **, char **);
+void out_of_mem(void);
+void parsecred(char *, struct xucred *);
+int put_exlist(struct dirlist *, XDR *, struct dirlist *, int *, int);
+void *sa_rawaddr(struct sockaddr *sa, int *nbytes);
+int sacmp(struct sockaddr *sa1, struct sockaddr *sa2,
+ struct sockaddr *samask);
+int scan_tree(struct dirlist *, struct sockaddr *);
+static void usage(void);
+int xdr_dir(XDR *, char *);
+int xdr_explist(XDR *, caddr_t);
+int xdr_explist_brief(XDR *, caddr_t);
+int xdr_fhs(XDR *, caddr_t);
+int xdr_mlist(XDR *, caddr_t);
+void terminate(int);
+
+struct exportlist *exphead;
+struct mountlist *mlhead;
+struct grouplist *grphead;
+char *exnames_default[2] = { _PATH_EXPORTS, NULL };
+char **exnames;
+char **hosts = NULL;
+struct xucred def_anon = {
+ XUCRED_VERSION,
+ (uid_t)-2,
+ 1,
+ { (gid_t)-2 },
+ NULL
+};
+int force_v2 = 0;
+int resvport_only = 1;
+int nhosts = 0;
+int dir_only = 1;
+int dolog = 0;
+int got_sighup = 0;
+int xcreated = 0;
+
+char *svcport_str = NULL;
+
+int opt_flags;
+static int have_v6 = 1;
+
+struct pidfh *pfh = NULL;
+/* Bits for opt_flags above */
+#define OP_MAPROOT 0x01
+#define OP_MAPALL 0x02
+/* 0x4 free */
+#define OP_MASK 0x08
+#define OP_NET 0x10
+#define OP_ALLDIRS 0x40
+#define OP_HAVEMASK 0x80 /* A mask was specified or inferred. */
+#define OP_QUIET 0x100
+#define OP_MASKLEN 0x200
+#define OP_SEC 0x400
+
+#ifdef DEBUG
+int debug = 1;
+void SYSLOG(int, const char *, ...) __printflike(2, 3);
+#define syslog SYSLOG
+#else
+int debug = 0;
+#endif
+
+/*
+ * Mountd server for NFS mount protocol as described in:
+ * NFS: Network File System Protocol Specification, RFC1094, Appendix A
+ * The optional arguments are the exports file name
+ * default: _PATH_EXPORTS
+ * and "-n" to allow nonroot mount.
+ */
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ fd_set readfds;
+ struct netconfig *nconf;
+ char *endptr, **hosts_bak;
+ void *nc_handle;
+ pid_t otherpid;
+ in_port_t svcport;
+ int c, k, s;
+ int maxrec = RPC_MAXDATASIZE;
+
+ /* Check that another mountd isn't already running. */
+ pfh = pidfile_open(_PATH_MOUNTDPID, 0600, &otherpid);
+ if (pfh == NULL) {
+ if (errno == EEXIST)
+ errx(1, "mountd already running, pid: %d.", otherpid);
+ warn("cannot open or create pidfile");
+ }
+
+ s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ if (s < 0)
+ have_v6 = 0;
+ else
+ close(s);
+ if (modfind("nfsserver") < 0) {
+ /* Not present in kernel, try loading it */
+ if (kldload("nfsserver") < 0 || modfind("nfsserver") < 0)
+ errx(1, "NFS server is not available or loadable");
+ }
+
+ while ((c = getopt(argc, argv, "2dh:lnp:r")) != -1)
+ switch (c) {
+ case '2':
+ force_v2 = 1;
+ break;
+ case 'n':
+ resvport_only = 0;
+ break;
+ case 'r':
+ dir_only = 0;
+ break;
+ case 'd':
+ debug = debug ? 0 : 1;
+ break;
+ case 'l':
+ dolog = 1;
+ break;
+ case 'p':
+ endptr = NULL;
+ svcport = (in_port_t)strtoul(optarg, &endptr, 10);
+ if (endptr == NULL || *endptr != '\0' ||
+ svcport == 0 || svcport >= IPPORT_MAX)
+ usage();
+ svcport_str = strdup(optarg);
+ break;
+ case 'h':
+ ++nhosts;
+ hosts_bak = hosts;
+ hosts_bak = realloc(hosts, nhosts * sizeof(char *));
+ if (hosts_bak == NULL) {
+ if (hosts != NULL) {
+ for (k = 0; k < nhosts; k++)
+ free(hosts[k]);
+ free(hosts);
+ out_of_mem();
+ }
+ }
+ hosts = hosts_bak;
+ hosts[nhosts - 1] = strdup(optarg);
+ if (hosts[nhosts - 1] == NULL) {
+ for (k = 0; k < (nhosts - 1); k++)
+ free(hosts[k]);
+ free(hosts);
+ out_of_mem();
+ }
+ break;
+ default:
+ usage();
+ };
+ argc -= optind;
+ argv += optind;
+ grphead = (struct grouplist *)NULL;
+ exphead = (struct exportlist *)NULL;
+ mlhead = (struct mountlist *)NULL;
+ if (argc > 0)
+ exnames = argv;
+ else
+ exnames = exnames_default;
+ openlog("mountd", LOG_PID, LOG_DAEMON);
+ if (debug)
+ warnx("getting export list");
+ get_exportlist();
+ if (debug)
+ warnx("getting mount list");
+ get_mountlist();
+ if (debug)
+ warnx("here we go");
+ if (debug == 0) {
+ daemon(0, 0);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ }
+ signal(SIGHUP, huphandler);
+ signal(SIGTERM, terminate);
+ signal(SIGPIPE, SIG_IGN);
+
+ pidfile_write(pfh);
+
+ rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL);
+ rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL);
+ rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
+
+ if (!resvport_only) {
+ if (sysctlbyname("vfs.nfsrv.nfs_privport", NULL, NULL,
+ &resvport_only, sizeof(resvport_only)) != 0 &&
+ errno != ENOENT) {
+ syslog(LOG_ERR, "sysctl: %m");
+ exit(1);
+ }
+ }
+
+ /*
+ * If no hosts were specified, add a wildcard entry to bind to
+ * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the
+ * list.
+ */
+ if (nhosts == 0) {
+ hosts = malloc(sizeof(char**));
+ if (hosts == NULL)
+ out_of_mem();
+ hosts[0] = "*";
+ nhosts = 1;
+ } else {
+ hosts_bak = hosts;
+ if (have_v6) {
+ hosts_bak = realloc(hosts, (nhosts + 2) *
+ sizeof(char *));
+ if (hosts_bak == NULL) {
+ for (k = 0; k < nhosts; k++)
+ free(hosts[k]);
+ free(hosts);
+ out_of_mem();
+ } else
+ hosts = hosts_bak;
+ nhosts += 2;
+ hosts[nhosts - 2] = "::1";
+ } else {
+ hosts_bak = realloc(hosts, (nhosts + 1) * sizeof(char *));
+ if (hosts_bak == NULL) {
+ for (k = 0; k < nhosts; k++)
+ free(hosts[k]);
+ free(hosts);
+ out_of_mem();
+ } else {
+ nhosts += 1;
+ hosts = hosts_bak;
+ }
+ }
+
+ hosts[nhosts - 1] = "127.0.0.1";
+ }
+
+ nc_handle = setnetconfig();
+ while ((nconf = getnetconfig(nc_handle))) {
+ if (nconf->nc_flag & NC_VISIBLE) {
+ if (have_v6 == 0 && strcmp(nconf->nc_protofmly,
+ "inet6") == 0) {
+ /* DO NOTHING */
+ } else
+ create_service(nconf);
+ }
+ }
+ endnetconfig(nc_handle);
+
+ if (xcreated == 0) {
+ syslog(LOG_ERR, "could not create any services");
+ exit(1);
+ }
+
+ /* Expand svc_run() here so that we can call get_exportlist(). */
+ for (;;) {
+ if (got_sighup) {
+ get_exportlist();
+ got_sighup = 0;
+ }
+ readfds = svc_fdset;
+ switch (select(svc_maxfd + 1, &readfds, NULL, NULL, NULL)) {
+ case -1:
+ if (errno == EINTR)
+ continue;
+ syslog(LOG_ERR, "mountd died: select: %m");
+ exit(1);
+ case 0:
+ continue;
+ default:
+ svc_getreqset(&readfds);
+ }
+ }
+}
+
+/*
+ * This routine creates and binds sockets on the appropriate
+ * addresses. It gets called one time for each transport and
+ * registrates the service with rpcbind on that trasport.
+ */
+void
+create_service(struct netconfig *nconf)
+{
+ struct addrinfo hints, *res = NULL;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ struct __rpc_sockinfo si;
+ struct netbuf servaddr;
+ SVCXPRT *transp = NULL;
+ int aicode;
+ int fd;
+ int nhostsbak;
+ int one = 1;
+ int r;
+ int registered = 0;
+ u_int32_t host_addr[4]; /* IPv4 or IPv6 */
+
+ if ((nconf->nc_semantics != NC_TPI_CLTS) &&
+ (nconf->nc_semantics != NC_TPI_COTS) &&
+ (nconf->nc_semantics != NC_TPI_COTS_ORD))
+ return; /* not my type */
+
+ /*
+ * XXX - using RPC library internal functions.
+ */
+ if (!__rpc_nconf2sockinfo(nconf, &si)) {
+ syslog(LOG_ERR, "cannot get information for %s",
+ nconf->nc_netid);
+ return;
+ }
+
+ /* Get mountd's address on this transport */
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = si.si_af;
+ hints.ai_socktype = si.si_socktype;
+ hints.ai_protocol = si.si_proto;
+
+ /*
+ * Bind to specific IPs if asked to
+ */
+ nhostsbak = nhosts;
+ while (nhostsbak > 0) {
+ --nhostsbak;
+ /*
+ * XXX - using RPC library internal functions.
+ */
+ if ((fd = __rpc_nconf2fd(nconf)) < 0) {
+ int non_fatal = 0;
+ if (errno == EPROTONOSUPPORT &&
+ nconf->nc_semantics != NC_TPI_CLTS)
+ non_fatal = 1;
+
+ syslog(non_fatal ? LOG_DEBUG : LOG_ERR,
+ "cannot create socket for %s", nconf->nc_netid);
+ return;
+ }
+
+ switch (hints.ai_family) {
+ case AF_INET:
+ if (inet_pton(AF_INET, hosts[nhostsbak],
+ host_addr) == 1) {
+ hints.ai_flags &= AI_NUMERICHOST;
+ } else {
+ /*
+ * Skip if we have an AF_INET6 address.
+ */
+ if (inet_pton(AF_INET6, hosts[nhostsbak],
+ host_addr) == 1) {
+ close(fd);
+ continue;
+ }
+ }
+ break;
+ case AF_INET6:
+ if (inet_pton(AF_INET6, hosts[nhostsbak],
+ host_addr) == 1) {
+ hints.ai_flags &= AI_NUMERICHOST;
+ } else {
+ /*
+ * Skip if we have an AF_INET address.
+ */
+ if (inet_pton(AF_INET, hosts[nhostsbak],
+ host_addr) == 1) {
+ close(fd);
+ continue;
+ }
+ }
+
+ /*
+ * We're doing host-based access checks here, so don't
+ * allow v4-in-v6 to confuse things. The kernel will
+ * disable it by default on NFS sockets too.
+ */
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &one,
+ sizeof one) < 0) {
+ syslog(LOG_ERR,
+ "can't disable v4-in-v6 on IPv6 socket");
+ exit(1);
+ }
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * If no hosts were specified, just bind to INADDR_ANY
+ */
+ if (strcmp("*", hosts[nhostsbak]) == 0) {
+ if (svcport_str == NULL) {
+ res = malloc(sizeof(struct addrinfo));
+ if (res == NULL)
+ out_of_mem();
+ res->ai_flags = hints.ai_flags;
+ res->ai_family = hints.ai_family;
+ res->ai_protocol = hints.ai_protocol;
+ switch (res->ai_family) {
+ case AF_INET:
+ sin = malloc(sizeof(struct sockaddr_in));
+ if (sin == NULL)
+ out_of_mem();
+ sin->sin_family = AF_INET;
+ sin->sin_port = htons(0);
+ sin->sin_addr.s_addr = htonl(INADDR_ANY);
+ res->ai_addr = (struct sockaddr*) sin;
+ res->ai_addrlen = (socklen_t)
+ sizeof(res->ai_addr);
+ break;
+ case AF_INET6:
+ sin6 = malloc(sizeof(struct sockaddr_in6));
+ if (sin6 == NULL)
+ out_of_mem();
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = htons(0);
+ sin6->sin6_addr = in6addr_any;
+ res->ai_addr = (struct sockaddr*) sin6;
+ res->ai_addrlen = (socklen_t)
+ sizeof(res->ai_addr);
+ break;
+ default:
+ break;
+ }
+ } else {
+ if ((aicode = getaddrinfo(NULL, svcport_str,
+ &hints, &res)) != 0) {
+ syslog(LOG_ERR,
+ "cannot get local address for %s: %s",
+ nconf->nc_netid,
+ gai_strerror(aicode));
+ continue;
+ }
+ }
+ } else {
+ if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str,
+ &hints, &res)) != 0) {
+ syslog(LOG_ERR,
+ "cannot get local address for %s: %s",
+ nconf->nc_netid, gai_strerror(aicode));
+ continue;
+ }
+ }
+
+ r = bindresvport_sa(fd, res->ai_addr);
+ if (r != 0) {
+ syslog(LOG_ERR, "bindresvport_sa: %m");
+ exit(1);
+ }
+
+ if (nconf->nc_semantics != NC_TPI_CLTS)
+ listen(fd, SOMAXCONN);
+
+ if (nconf->nc_semantics == NC_TPI_CLTS )
+ transp = svc_dg_create(fd, 0, 0);
+ else
+ transp = svc_vc_create(fd, RPC_MAXDATASIZE,
+ RPC_MAXDATASIZE);
+
+ if (transp != (SVCXPRT *) NULL) {
+ if (!svc_reg(transp, RPCPROG_MNT, RPCMNT_VER1, mntsrv,
+ NULL))
+ syslog(LOG_ERR,
+ "can't register %s RPCMNT_VER1 service",
+ nconf->nc_netid);
+ if (!force_v2) {
+ if (!svc_reg(transp, RPCPROG_MNT, RPCMNT_VER3,
+ mntsrv, NULL))
+ syslog(LOG_ERR,
+ "can't register %s RPCMNT_VER3 service",
+ nconf->nc_netid);
+ }
+ } else
+ syslog(LOG_WARNING, "can't create %s services",
+ nconf->nc_netid);
+
+ if (registered == 0) {
+ registered = 1;
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = si.si_af;
+ hints.ai_socktype = si.si_socktype;
+ hints.ai_protocol = si.si_proto;
+
+ if (svcport_str == NULL) {
+ svcport_str = malloc(NI_MAXSERV * sizeof(char));
+ if (svcport_str == NULL)
+ out_of_mem();
+
+ if (getnameinfo(res->ai_addr,
+ res->ai_addr->sa_len, NULL, NI_MAXHOST,
+ svcport_str, NI_MAXSERV * sizeof(char),
+ NI_NUMERICHOST | NI_NUMERICSERV))
+ errx(1, "Cannot get port number");
+ }
+
+ if((aicode = getaddrinfo(NULL, svcport_str, &hints,
+ &res)) != 0) {
+ syslog(LOG_ERR, "cannot get local address: %s",
+ gai_strerror(aicode));
+ exit(1);
+ }
+
+ servaddr.buf = malloc(res->ai_addrlen);
+ memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen);
+ servaddr.len = res->ai_addrlen;
+
+ rpcb_set(RPCPROG_MNT, RPCMNT_VER1, nconf, &servaddr);
+ rpcb_set(RPCPROG_MNT, RPCMNT_VER3, nconf, &servaddr);
+
+ xcreated++;
+ freeaddrinfo(res);
+ }
+ } /* end while */
+}
+
+static void
+usage()
+{
+ fprintf(stderr,
+ "usage: mountd [-2] [-d] [-l] [-n] [-p <port>] [-r] "
+ "[-h <bindip>] [export_file ...]\n");
+ exit(1);
+}
+
+/*
+ * The mount rpc service
+ */
+void
+mntsrv(rqstp, transp)
+ struct svc_req *rqstp;
+ SVCXPRT *transp;
+{
+ struct exportlist *ep;
+ struct dirlist *dp;
+ struct fhreturn fhr;
+ struct stat stb;
+ struct statfs fsb;
+ char host[NI_MAXHOST], numerichost[NI_MAXHOST];
+ int lookup_failed = 1;
+ struct sockaddr *saddr;
+ u_short sport;
+ char rpcpath[RPCMNT_PATHLEN + 1], dirpath[MAXPATHLEN];
+ int bad = 0, defset, hostset;
+ sigset_t sighup_mask;
+
+ sigemptyset(&sighup_mask);
+ sigaddset(&sighup_mask, SIGHUP);
+ saddr = svc_getrpccaller(transp)->buf;
+ switch (saddr->sa_family) {
+ case AF_INET6:
+ sport = ntohs(((struct sockaddr_in6 *)saddr)->sin6_port);
+ break;
+ case AF_INET:
+ sport = ntohs(((struct sockaddr_in *)saddr)->sin_port);
+ break;
+ default:
+ syslog(LOG_ERR, "request from unknown address family");
+ return;
+ }
+ lookup_failed = getnameinfo(saddr, saddr->sa_len, host, sizeof host,
+ NULL, 0, 0);
+ getnameinfo(saddr, saddr->sa_len, numerichost,
+ sizeof numerichost, NULL, 0, NI_NUMERICHOST);
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ if (!svc_sendreply(transp, (xdrproc_t)xdr_void, NULL))
+ syslog(LOG_ERR, "can't send reply");
+ return;
+ case RPCMNT_MOUNT:
+ if (sport >= IPPORT_RESERVED && resvport_only) {
+ syslog(LOG_NOTICE,
+ "mount request from %s from unprivileged port",
+ numerichost);
+ svcerr_weakauth(transp);
+ return;
+ }
+ if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
+ syslog(LOG_NOTICE, "undecodable mount request from %s",
+ numerichost);
+ svcerr_decode(transp);
+ return;
+ }
+
+ /*
+ * Get the real pathname and make sure it is a directory
+ * or a regular file if the -r option was specified
+ * and it exists.
+ */
+ if (realpath(rpcpath, dirpath) == NULL ||
+ stat(dirpath, &stb) < 0 ||
+ (!S_ISDIR(stb.st_mode) &&
+ (dir_only || !S_ISREG(stb.st_mode))) ||
+ statfs(dirpath, &fsb) < 0) {
+ chdir("/"); /* Just in case realpath doesn't */
+ syslog(LOG_NOTICE,
+ "mount request from %s for non existent path %s",
+ numerichost, dirpath);
+ if (debug)
+ warnx("stat failed on %s", dirpath);
+ bad = ENOENT; /* We will send error reply later */
+ }
+
+ /* Check in the exports list */
+ sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
+ ep = ex_search(&fsb.f_fsid);
+ hostset = defset = 0;
+ if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset) ||
+ ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
+ chk_host(dp, saddr, &defset, &hostset)) ||
+ (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
+ scan_tree(ep->ex_dirl, saddr) == 0))) {
+ if (bad) {
+ if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
+ (caddr_t)&bad))
+ syslog(LOG_ERR, "can't send reply");
+ sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
+ return;
+ }
+ if (hostset & DP_HOSTSET)
+ fhr.fhr_flag = hostset;
+ else
+ fhr.fhr_flag = defset;
+ fhr.fhr_vers = rqstp->rq_vers;
+ /* Get the file handle */
+ memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t));
+ if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) {
+ bad = errno;
+ syslog(LOG_ERR, "can't get fh for %s", dirpath);
+ if (!svc_sendreply(transp, (xdrproc_t)xdr_long,
+ (caddr_t)&bad))
+ syslog(LOG_ERR, "can't send reply");
+ sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
+ return;
+ }
+ fhr.fhr_numsecflavors = ep->ex_numsecflavors;
+ fhr.fhr_secflavors = ep->ex_secflavors;
+ if (!svc_sendreply(transp, (xdrproc_t)xdr_fhs,
+ (caddr_t)&fhr))
+ syslog(LOG_ERR, "can't send reply");
+ if (!lookup_failed)
+ add_mlist(host, dirpath);
+ else
+ add_mlist(numerichost, dirpath);
+ if (debug)
+ warnx("mount successful");
+ if (dolog)
+ syslog(LOG_NOTICE,
+ "mount request succeeded from %s for %s",
+ numerichost, dirpath);
+ } else {
+ bad = EACCES;
+ syslog(LOG_NOTICE,
+ "mount request denied from %s for %s",
+ numerichost, dirpath);
+ }
+
+ if (bad && !svc_sendreply(transp, (xdrproc_t)xdr_long,
+ (caddr_t)&bad))
+ syslog(LOG_ERR, "can't send reply");
+ sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
+ return;
+ case RPCMNT_DUMP:
+ if (!svc_sendreply(transp, (xdrproc_t)xdr_mlist, (caddr_t)NULL))
+ syslog(LOG_ERR, "can't send reply");
+ else if (dolog)
+ syslog(LOG_NOTICE,
+ "dump request succeeded from %s",
+ numerichost);
+ return;
+ case RPCMNT_UMOUNT:
+ if (sport >= IPPORT_RESERVED && resvport_only) {
+ syslog(LOG_NOTICE,
+ "umount request from %s from unprivileged port",
+ numerichost);
+ svcerr_weakauth(transp);
+ return;
+ }
+ if (!svc_getargs(transp, (xdrproc_t)xdr_dir, rpcpath)) {
+ syslog(LOG_NOTICE, "undecodable umount request from %s",
+ numerichost);
+ svcerr_decode(transp);
+ return;
+ }
+ if (realpath(rpcpath, dirpath) == NULL) {
+ syslog(LOG_NOTICE, "umount request from %s "
+ "for non existent path %s",
+ numerichost, dirpath);
+ }
+ if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
+ syslog(LOG_ERR, "can't send reply");
+ if (!lookup_failed)
+ del_mlist(host, dirpath);
+ del_mlist(numerichost, dirpath);
+ if (dolog)
+ syslog(LOG_NOTICE,
+ "umount request succeeded from %s for %s",
+ numerichost, dirpath);
+ return;
+ case RPCMNT_UMNTALL:
+ if (sport >= IPPORT_RESERVED && resvport_only) {
+ syslog(LOG_NOTICE,
+ "umountall request from %s from unprivileged port",
+ numerichost);
+ svcerr_weakauth(transp);
+ return;
+ }
+ if (!svc_sendreply(transp, (xdrproc_t)xdr_void, (caddr_t)NULL))
+ syslog(LOG_ERR, "can't send reply");
+ if (!lookup_failed)
+ del_mlist(host, NULL);
+ del_mlist(numerichost, NULL);
+ if (dolog)
+ syslog(LOG_NOTICE,
+ "umountall request succeeded from %s",
+ numerichost);
+ return;
+ case RPCMNT_EXPORT:
+ if (!svc_sendreply(transp, (xdrproc_t)xdr_explist, (caddr_t)NULL))
+ if (!svc_sendreply(transp, (xdrproc_t)xdr_explist_brief,
+ (caddr_t)NULL))
+ syslog(LOG_ERR, "can't send reply");
+ if (dolog)
+ syslog(LOG_NOTICE,
+ "export request succeeded from %s",
+ numerichost);
+ return;
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+}
+
+/*
+ * Xdr conversion for a dirpath string
+ */
+int
+xdr_dir(xdrsp, dirp)
+ XDR *xdrsp;
+ char *dirp;
+{
+ return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
+}
+
+/*
+ * Xdr routine to generate file handle reply
+ */
+int
+xdr_fhs(xdrsp, cp)
+ XDR *xdrsp;
+ caddr_t cp;
+{
+ struct fhreturn *fhrp = (struct fhreturn *)cp;
+ u_long ok = 0, len, auth;
+ int i;
+
+ if (!xdr_long(xdrsp, &ok))
+ return (0);
+ switch (fhrp->fhr_vers) {
+ case 1:
+ return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
+ case 3:
+ len = NFSX_V3FH;
+ if (!xdr_long(xdrsp, &len))
+ return (0);
+ if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
+ return (0);
+ if (fhrp->fhr_numsecflavors) {
+ if (!xdr_int(xdrsp, &fhrp->fhr_numsecflavors))
+ return (0);
+ for (i = 0; i < fhrp->fhr_numsecflavors; i++)
+ if (!xdr_int(xdrsp, &fhrp->fhr_secflavors[i]))
+ return (0);
+ return (1);
+ } else {
+ auth = AUTH_SYS;
+ len = 1;
+ if (!xdr_long(xdrsp, &len))
+ return (0);
+ return (xdr_long(xdrsp, &auth));
+ }
+ };
+ return (0);
+}
+
+int
+xdr_mlist(xdrsp, cp)
+ XDR *xdrsp;
+ caddr_t cp;
+{
+ struct mountlist *mlp;
+ int true = 1;
+ int false = 0;
+ char *strp;
+
+ mlp = mlhead;
+ while (mlp) {
+ if (!xdr_bool(xdrsp, &true))
+ return (0);
+ strp = &mlp->ml_host[0];
+ if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
+ return (0);
+ strp = &mlp->ml_dirp[0];
+ if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
+ return (0);
+ mlp = mlp->ml_next;
+ }
+ if (!xdr_bool(xdrsp, &false))
+ return (0);
+ return (1);
+}
+
+/*
+ * Xdr conversion for export list
+ */
+int
+xdr_explist_common(xdrsp, cp, brief)
+ XDR *xdrsp;
+ caddr_t cp;
+ int brief;
+{
+ struct exportlist *ep;
+ int false = 0;
+ int putdef;
+ sigset_t sighup_mask;
+
+ sigemptyset(&sighup_mask);
+ sigaddset(&sighup_mask, SIGHUP);
+ sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
+ ep = exphead;
+ while (ep) {
+ putdef = 0;
+ if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir,
+ &putdef, brief))
+ goto errout;
+ if (ep->ex_defdir && putdef == 0 &&
+ put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL,
+ &putdef, brief))
+ goto errout;
+ ep = ep->ex_next;
+ }
+ sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
+ if (!xdr_bool(xdrsp, &false))
+ return (0);
+ return (1);
+errout:
+ sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
+ return (0);
+}
+
+/*
+ * Called from xdr_explist() to traverse the tree and export the
+ * directory paths.
+ */
+int
+put_exlist(dp, xdrsp, adp, putdefp, brief)
+ struct dirlist *dp;
+ XDR *xdrsp;
+ struct dirlist *adp;
+ int *putdefp;
+ int brief;
+{
+ struct grouplist *grp;
+ struct hostlist *hp;
+ int true = 1;
+ int false = 0;
+ int gotalldir = 0;
+ char *strp;
+
+ if (dp) {
+ if (put_exlist(dp->dp_left, xdrsp, adp, putdefp, brief))
+ return (1);
+ if (!xdr_bool(xdrsp, &true))
+ return (1);
+ strp = dp->dp_dirp;
+ if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
+ return (1);
+ if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
+ gotalldir = 1;
+ *putdefp = 1;
+ }
+ if (brief) {
+ if (!xdr_bool(xdrsp, &true))
+ return (1);
+ strp = "(...)";
+ if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
+ return (1);
+ } else if ((dp->dp_flag & DP_DEFSET) == 0 &&
+ (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
+ hp = dp->dp_hosts;
+ while (hp) {
+ grp = hp->ht_grp;
+ if (grp->gr_type == GT_HOST) {
+ if (!xdr_bool(xdrsp, &true))
+ return (1);
+ strp = grp->gr_ptr.gt_addrinfo->ai_canonname;
+ if (!xdr_string(xdrsp, &strp,
+ RPCMNT_NAMELEN))
+ return (1);
+ } else if (grp->gr_type == GT_NET) {
+ if (!xdr_bool(xdrsp, &true))
+ return (1);
+ strp = grp->gr_ptr.gt_net.nt_name;
+ if (!xdr_string(xdrsp, &strp,
+ RPCMNT_NAMELEN))
+ return (1);
+ }
+ hp = hp->ht_next;
+ if (gotalldir && hp == (struct hostlist *)NULL) {
+ hp = adp->dp_hosts;
+ gotalldir = 0;
+ }
+ }
+ }
+ if (!xdr_bool(xdrsp, &false))
+ return (1);
+ if (put_exlist(dp->dp_right, xdrsp, adp, putdefp, brief))
+ return (1);
+ }
+ return (0);
+}
+
+int
+xdr_explist(xdrsp, cp)
+ XDR *xdrsp;
+ caddr_t cp;
+{
+
+ return xdr_explist_common(xdrsp, cp, 0);
+}
+
+int
+xdr_explist_brief(xdrsp, cp)
+ XDR *xdrsp;
+ caddr_t cp;
+{
+
+ return xdr_explist_common(xdrsp, cp, 1);
+}
+
+char *line;
+int linesize;
+FILE *exp_file;
+
+/*
+ * Get the export list from one, currently open file
+ */
+static void
+get_exportlist_one()
+{
+ struct exportlist *ep, *ep2;
+ struct grouplist *grp, *tgrp;
+ struct exportlist **epp;
+ struct dirlist *dirhead;
+ struct statfs fsb;
+ struct xucred anon;
+ char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
+ int len, has_host, exflags, got_nondir, dirplen, netgrp;
+
+ dirhead = (struct dirlist *)NULL;
+ while (get_line()) {
+ if (debug)
+ warnx("got line %s", line);
+ cp = line;
+ nextfield(&cp, &endcp);
+ if (*cp == '#')
+ goto nextline;
+
+ /*
+ * Set defaults.
+ */
+ has_host = FALSE;
+ anon = def_anon;
+ exflags = MNT_EXPORTED;
+ got_nondir = 0;
+ opt_flags = 0;
+ ep = (struct exportlist *)NULL;
+
+ /*
+ * Create new exports list entry
+ */
+ len = endcp-cp;
+ tgrp = grp = get_grp();
+ while (len > 0) {
+ if (len > RPCMNT_NAMELEN) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ if (*cp == '-') {
+ if (ep == (struct exportlist *)NULL) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ if (debug)
+ warnx("doing opt %s", cp);
+ got_nondir = 1;
+ if (do_opt(&cp, &endcp, ep, grp, &has_host,
+ &exflags, &anon)) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ } else if (*cp == '/') {
+ savedc = *endcp;
+ *endcp = '\0';
+ if (check_dirpath(cp) &&
+ statfs(cp, &fsb) >= 0) {
+ if (got_nondir) {
+ syslog(LOG_ERR, "dirs must be first");
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ if (ep) {
+ if (ep->ex_fs.val[0] != fsb.f_fsid.val[0] ||
+ ep->ex_fs.val[1] != fsb.f_fsid.val[1]) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ } else {
+ /*
+ * See if this directory is already
+ * in the list.
+ */
+ ep = ex_search(&fsb.f_fsid);
+ if (ep == (struct exportlist *)NULL) {
+ ep = get_exp();
+ ep->ex_fs = fsb.f_fsid;
+ ep->ex_fsdir = (char *)
+ malloc(strlen(fsb.f_mntonname) + 1);
+ if (ep->ex_fsdir)
+ strcpy(ep->ex_fsdir,
+ fsb.f_mntonname);
+ else
+ out_of_mem();
+ if (debug)
+ warnx("making new ep fs=0x%x,0x%x",
+ fsb.f_fsid.val[0],
+ fsb.f_fsid.val[1]);
+ } else if (debug)
+ warnx("found ep fs=0x%x,0x%x",
+ fsb.f_fsid.val[0],
+ fsb.f_fsid.val[1]);
+ }
+
+ /*
+ * Add dirpath to export mount point.
+ */
+ dirp = add_expdir(&dirhead, cp, len);
+ dirplen = len;
+ } else {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ *endcp = savedc;
+ } else {
+ savedc = *endcp;
+ *endcp = '\0';
+ got_nondir = 1;
+ if (ep == (struct exportlist *)NULL) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+
+ /*
+ * Get the host or netgroup.
+ */
+ setnetgrent(cp);
+ netgrp = getnetgrent(&hst, &usr, &dom);
+ do {
+ if (has_host) {
+ grp->gr_next = get_grp();
+ grp = grp->gr_next;
+ }
+ if (netgrp) {
+ if (hst == 0) {
+ syslog(LOG_ERR,
+ "null hostname in netgroup %s, skipping", cp);
+ grp->gr_type = GT_IGNORE;
+ } else if (get_host(hst, grp, tgrp)) {
+ syslog(LOG_ERR,
+ "bad host %s in netgroup %s, skipping", hst, cp);
+ grp->gr_type = GT_IGNORE;
+ }
+ } else if (get_host(cp, grp, tgrp)) {
+ syslog(LOG_ERR, "bad host %s, skipping", cp);
+ grp->gr_type = GT_IGNORE;
+ }
+ has_host = TRUE;
+ } while (netgrp && getnetgrent(&hst, &usr, &dom));
+ endnetgrent();
+ *endcp = savedc;
+ }
+ cp = endcp;
+ nextfield(&cp, &endcp);
+ len = endcp - cp;
+ }
+ if (check_options(dirhead)) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ if (!has_host) {
+ grp->gr_type = GT_DEFAULT;
+ if (debug)
+ warnx("adding a default entry");
+
+ /*
+ * Don't allow a network export coincide with a list of
+ * host(s) on the same line.
+ */
+ } else if ((opt_flags & OP_NET) && tgrp->gr_next) {
+ syslog(LOG_ERR, "network/host conflict");
+ getexp_err(ep, tgrp);
+ goto nextline;
+
+ /*
+ * If an export list was specified on this line, make sure
+ * that we have at least one valid entry, otherwise skip it.
+ */
+ } else {
+ grp = tgrp;
+ while (grp && grp->gr_type == GT_IGNORE)
+ grp = grp->gr_next;
+ if (! grp) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ }
+
+ /*
+ * Loop through hosts, pushing the exports into the kernel.
+ * After loop, tgrp points to the start of the list and
+ * grp points to the last entry in the list.
+ */
+ grp = tgrp;
+ do {
+ if (do_mount(ep, grp, exflags, &anon, dirp, dirplen,
+ &fsb)) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ } while (grp->gr_next && (grp = grp->gr_next));
+
+ /*
+ * Success. Update the data structures.
+ */
+ if (has_host) {
+ hang_dirp(dirhead, tgrp, ep, opt_flags);
+ grp->gr_next = grphead;
+ grphead = tgrp;
+ } else {
+ hang_dirp(dirhead, (struct grouplist *)NULL, ep,
+ opt_flags);
+ free_grp(grp);
+ }
+ dirhead = (struct dirlist *)NULL;
+ if ((ep->ex_flag & EX_LINKED) == 0) {
+ ep2 = exphead;
+ epp = &exphead;
+
+ /*
+ * Insert in the list in alphabetical order.
+ */
+ while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {
+ epp = &ep2->ex_next;
+ ep2 = ep2->ex_next;
+ }
+ if (ep2)
+ ep->ex_next = ep2;
+ *epp = ep;
+ ep->ex_flag |= EX_LINKED;
+ }
+nextline:
+ if (dirhead) {
+ free_dir(dirhead);
+ dirhead = (struct dirlist *)NULL;
+ }
+ }
+}
+
+/*
+ * Get the export list from all specified files
+ */
+void
+get_exportlist()
+{
+ struct exportlist *ep, *ep2;
+ struct grouplist *grp, *tgrp;
+ struct export_args export;
+ struct iovec *iov;
+ struct statfs *fsp, *mntbufp;
+ struct xvfsconf vfc;
+ char *dirp;
+ char errmsg[255];
+ int dirplen, num, i;
+ int iovlen;
+ int done;
+
+ bzero(&export, sizeof(export));
+ export.ex_flags = MNT_DELEXPORT;
+ dirp = NULL;
+ dirplen = 0;
+ iov = NULL;
+ iovlen = 0;
+ bzero(errmsg, sizeof(errmsg));
+
+ /*
+ * First, get rid of the old list
+ */
+ ep = exphead;
+ while (ep) {
+ ep2 = ep;
+ ep = ep->ex_next;
+ free_exp(ep2);
+ }
+ exphead = (struct exportlist *)NULL;
+
+ grp = grphead;
+ while (grp) {
+ tgrp = grp;
+ grp = grp->gr_next;
+ free_grp(tgrp);
+ }
+ grphead = (struct grouplist *)NULL;
+
+ /*
+ * And delete exports that are in the kernel for all local
+ * filesystems.
+ * XXX: Should know how to handle all local exportable filesystems.
+ */
+ num = getmntinfo(&mntbufp, MNT_NOWAIT);
+
+ if (num > 0) {
+ build_iovec(&iov, &iovlen, "fstype", NULL, 0);
+ build_iovec(&iov, &iovlen, "fspath", NULL, 0);
+ build_iovec(&iov, &iovlen, "from", NULL, 0);
+ build_iovec(&iov, &iovlen, "update", NULL, 0);
+ build_iovec(&iov, &iovlen, "export", &export, sizeof(export));
+ build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
+ }
+
+ for (i = 0; i < num; i++) {
+ fsp = &mntbufp[i];
+ if (getvfsbyname(fsp->f_fstypename, &vfc) != 0) {
+ syslog(LOG_ERR, "getvfsbyname() failed for %s",
+ fsp->f_fstypename);
+ continue;
+ }
+
+ /*
+ * Do not delete export for network filesystem by
+ * passing "export" arg to nmount().
+ * It only makes sense to do this for local filesystems.
+ */
+ if (vfc.vfc_flags & VFCF_NETWORK)
+ continue;
+
+ iov[1].iov_base = fsp->f_fstypename;
+ iov[1].iov_len = strlen(fsp->f_fstypename) + 1;
+ iov[3].iov_base = fsp->f_mntonname;
+ iov[3].iov_len = strlen(fsp->f_mntonname) + 1;
+ iov[5].iov_base = fsp->f_mntfromname;
+ iov[5].iov_len = strlen(fsp->f_mntfromname) + 1;
+
+ if (nmount(iov, iovlen, fsp->f_flags) < 0 &&
+ errno != ENOENT && errno != ENOTSUP) {
+ syslog(LOG_ERR,
+ "can't delete exports for %s: %m %s",
+ fsp->f_mntonname, errmsg);
+ }
+ }
+
+ if (iov != NULL) {
+ /* Free strings allocated by strdup() in getmntopts.c */
+ free(iov[0].iov_base); /* fstype */
+ free(iov[2].iov_base); /* fspath */
+ free(iov[4].iov_base); /* from */
+ free(iov[6].iov_base); /* update */
+ free(iov[8].iov_base); /* export */
+ free(iov[10].iov_base); /* errmsg */
+
+ /* free iov, allocated by realloc() */
+ free(iov);
+ iovlen = 0;
+ }
+
+ /*
+ * Read in the exports file and build the list, calling
+ * nmount() as we go along to push the export rules into the kernel.
+ */
+ done = 0;
+ for (i = 0; exnames[i] != NULL; i++) {
+ if (debug)
+ warnx("reading exports from %s", exnames[i]);
+ if ((exp_file = fopen(exnames[i], "r")) == NULL) {
+ syslog(LOG_WARNING, "can't open %s", exnames[i]);
+ continue;
+ }
+ get_exportlist_one();
+ fclose(exp_file);
+ done++;
+ }
+ if (done == 0) {
+ syslog(LOG_ERR, "can't open any exports file");
+ exit(2);
+ }
+}
+
+/*
+ * Allocate an export list element
+ */
+struct exportlist *
+get_exp()
+{
+ struct exportlist *ep;
+
+ ep = (struct exportlist *)malloc(sizeof (struct exportlist));
+ if (ep == (struct exportlist *)NULL)
+ out_of_mem();
+ memset(ep, 0, sizeof(struct exportlist));
+ return (ep);
+}
+
+/*
+ * Allocate a group list element
+ */
+struct grouplist *
+get_grp()
+{
+ struct grouplist *gp;
+
+ gp = (struct grouplist *)malloc(sizeof (struct grouplist));
+ if (gp == (struct grouplist *)NULL)
+ out_of_mem();
+ memset(gp, 0, sizeof(struct grouplist));
+ return (gp);
+}
+
+/*
+ * Clean up upon an error in get_exportlist().
+ */
+void
+getexp_err(ep, grp)
+ struct exportlist *ep;
+ struct grouplist *grp;
+{
+ struct grouplist *tgrp;
+
+ if (!(opt_flags & OP_QUIET))
+ syslog(LOG_ERR, "bad exports list line %s", line);
+ if (ep && (ep->ex_flag & EX_LINKED) == 0)
+ free_exp(ep);
+ while (grp) {
+ tgrp = grp;
+ grp = grp->gr_next;
+ free_grp(tgrp);
+ }
+}
+
+/*
+ * Search the export list for a matching fs.
+ */
+struct exportlist *
+ex_search(fsid)
+ fsid_t *fsid;
+{
+ struct exportlist *ep;
+
+ ep = exphead;
+ while (ep) {
+ if (ep->ex_fs.val[0] == fsid->val[0] &&
+ ep->ex_fs.val[1] == fsid->val[1])
+ return (ep);
+ ep = ep->ex_next;
+ }
+ return (ep);
+}
+
+/*
+ * Add a directory path to the list.
+ */
+char *
+add_expdir(dpp, cp, len)
+ struct dirlist **dpp;
+ char *cp;
+ int len;
+{
+ struct dirlist *dp;
+
+ dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len);
+ if (dp == (struct dirlist *)NULL)
+ out_of_mem();
+ dp->dp_left = *dpp;
+ dp->dp_right = (struct dirlist *)NULL;
+ dp->dp_flag = 0;
+ dp->dp_hosts = (struct hostlist *)NULL;
+ strcpy(dp->dp_dirp, cp);
+ *dpp = dp;
+ return (dp->dp_dirp);
+}
+
+/*
+ * Hang the dir list element off the dirpath binary tree as required
+ * and update the entry for host.
+ */
+void
+hang_dirp(dp, grp, ep, flags)
+ struct dirlist *dp;
+ struct grouplist *grp;
+ struct exportlist *ep;
+ int flags;
+{
+ struct hostlist *hp;
+ struct dirlist *dp2;
+
+ if (flags & OP_ALLDIRS) {
+ if (ep->ex_defdir)
+ free((caddr_t)dp);
+ else
+ ep->ex_defdir = dp;
+ if (grp == (struct grouplist *)NULL) {
+ ep->ex_defdir->dp_flag |= DP_DEFSET;
+ } else while (grp) {
+ hp = get_ht();
+ hp->ht_grp = grp;
+ hp->ht_next = ep->ex_defdir->dp_hosts;
+ ep->ex_defdir->dp_hosts = hp;
+ grp = grp->gr_next;
+ }
+ } else {
+
+ /*
+ * Loop through the directories adding them to the tree.
+ */
+ while (dp) {
+ dp2 = dp->dp_left;
+ add_dlist(&ep->ex_dirl, dp, grp, flags);
+ dp = dp2;
+ }
+ }
+}
+
+/*
+ * Traverse the binary tree either updating a node that is already there
+ * for the new directory or adding the new node.
+ */
+void
+add_dlist(dpp, newdp, grp, flags)
+ struct dirlist **dpp;
+ struct dirlist *newdp;
+ struct grouplist *grp;
+ int flags;
+{
+ struct dirlist *dp;
+ struct hostlist *hp;
+ int cmp;
+
+ dp = *dpp;
+ if (dp) {
+ cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
+ if (cmp > 0) {
+ add_dlist(&dp->dp_left, newdp, grp, flags);
+ return;
+ } else if (cmp < 0) {
+ add_dlist(&dp->dp_right, newdp, grp, flags);
+ return;
+ } else
+ free((caddr_t)newdp);
+ } else {
+ dp = newdp;
+ dp->dp_left = (struct dirlist *)NULL;
+ *dpp = dp;
+ }
+ if (grp) {
+
+ /*
+ * Hang all of the host(s) off of the directory point.
+ */
+ do {
+ hp = get_ht();
+ hp->ht_grp = grp;
+ hp->ht_next = dp->dp_hosts;
+ dp->dp_hosts = hp;
+ grp = grp->gr_next;
+ } while (grp);
+ } else {
+ dp->dp_flag |= DP_DEFSET;
+ }
+}
+
+/*
+ * Search for a dirpath on the export point.
+ */
+struct dirlist *
+dirp_search(dp, dirp)
+ struct dirlist *dp;
+ char *dirp;
+{
+ int cmp;
+
+ if (dp) {
+ cmp = strcmp(dp->dp_dirp, dirp);
+ if (cmp > 0)
+ return (dirp_search(dp->dp_left, dirp));
+ else if (cmp < 0)
+ return (dirp_search(dp->dp_right, dirp));
+ else
+ return (dp);
+ }
+ return (dp);
+}
+
+/*
+ * Scan for a host match in a directory tree.
+ */
+int
+chk_host(dp, saddr, defsetp, hostsetp)
+ struct dirlist *dp;
+ struct sockaddr *saddr;
+ int *defsetp;
+ int *hostsetp;
+{
+ struct hostlist *hp;
+ struct grouplist *grp;
+ struct addrinfo *ai;
+
+ if (dp) {
+ if (dp->dp_flag & DP_DEFSET)
+ *defsetp = dp->dp_flag;
+ hp = dp->dp_hosts;
+ while (hp) {
+ grp = hp->ht_grp;
+ switch (grp->gr_type) {
+ case GT_HOST:
+ ai = grp->gr_ptr.gt_addrinfo;
+ for (; ai; ai = ai->ai_next) {
+ if (!sacmp(ai->ai_addr, saddr, NULL)) {
+ *hostsetp =
+ (hp->ht_flag | DP_HOSTSET);
+ return (1);
+ }
+ }
+ break;
+ case GT_NET:
+ if (!sacmp(saddr, (struct sockaddr *)
+ &grp->gr_ptr.gt_net.nt_net,
+ (struct sockaddr *)
+ &grp->gr_ptr.gt_net.nt_mask)) {
+ *hostsetp = (hp->ht_flag | DP_HOSTSET);
+ return (1);
+ }
+ break;
+ }
+ hp = hp->ht_next;
+ }
+ }
+ return (0);
+}
+
+/*
+ * Scan tree for a host that matches the address.
+ */
+int
+scan_tree(dp, saddr)
+ struct dirlist *dp;
+ struct sockaddr *saddr;
+{
+ int defset, hostset;
+
+ if (dp) {
+ if (scan_tree(dp->dp_left, saddr))
+ return (1);
+ if (chk_host(dp, saddr, &defset, &hostset))
+ return (1);
+ if (scan_tree(dp->dp_right, saddr))
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Traverse the dirlist tree and free it up.
+ */
+void
+free_dir(dp)
+ struct dirlist *dp;
+{
+
+ if (dp) {
+ free_dir(dp->dp_left);
+ free_dir(dp->dp_right);
+ free_host(dp->dp_hosts);
+ free((caddr_t)dp);
+ }
+}
+
+/*
+ * Parse a colon separated list of security flavors
+ */
+int
+parsesec(seclist, ep)
+ char *seclist;
+ struct exportlist *ep;
+{
+ char *cp, savedc;
+ int flavor;
+
+ ep->ex_numsecflavors = 0;
+ for (;;) {
+ cp = strchr(seclist, ':');
+ if (cp) {
+ savedc = *cp;
+ *cp = '\0';
+ }
+
+ if (!strcmp(seclist, "sys"))
+ flavor = AUTH_SYS;
+ else if (!strcmp(seclist, "krb5"))
+ flavor = RPCSEC_GSS_KRB5;
+ else if (!strcmp(seclist, "krb5i"))
+ flavor = RPCSEC_GSS_KRB5I;
+ else if (!strcmp(seclist, "krb5p"))
+ flavor = RPCSEC_GSS_KRB5P;
+ else {
+ if (cp)
+ *cp = savedc;
+ syslog(LOG_ERR, "bad sec flavor: %s", seclist);
+ return (1);
+ }
+ if (ep->ex_numsecflavors == MAXSECFLAVORS) {
+ if (cp)
+ *cp = savedc;
+ syslog(LOG_ERR, "too many sec flavors: %s", seclist);
+ return (1);
+ }
+ ep->ex_secflavors[ep->ex_numsecflavors] = flavor;
+ ep->ex_numsecflavors++;
+ if (cp) {
+ *cp = savedc;
+ seclist = cp + 1;
+ } else {
+ break;
+ }
+ }
+ return (0);
+}
+
+/*
+ * Parse the option string and update fields.
+ * Option arguments may either be -<option>=<value> or
+ * -<option> <value>
+ */
+int
+do_opt(cpp, endcpp, ep, grp, has_hostp, exflagsp, cr)
+ char **cpp, **endcpp;
+ struct exportlist *ep;
+ struct grouplist *grp;
+ int *has_hostp;
+ int *exflagsp;
+ struct xucred *cr;
+{
+ char *cpoptarg, *cpoptend;
+ char *cp, *endcp, *cpopt, savedc, savedc2;
+ int allflag, usedarg;
+
+ savedc2 = '\0';
+ cpopt = *cpp;
+ cpopt++;
+ cp = *endcpp;
+ savedc = *cp;
+ *cp = '\0';
+ while (cpopt && *cpopt) {
+ allflag = 1;
+ usedarg = -2;
+ if ((cpoptend = strchr(cpopt, ','))) {
+ *cpoptend++ = '\0';
+ if ((cpoptarg = strchr(cpopt, '=')))
+ *cpoptarg++ = '\0';
+ } else {
+ if ((cpoptarg = strchr(cpopt, '=')))
+ *cpoptarg++ = '\0';
+ else {
+ *cp = savedc;
+ nextfield(&cp, &endcp);
+ **endcpp = '\0';
+ if (endcp > cp && *cp != '-') {
+ cpoptarg = cp;
+ savedc2 = *endcp;
+ *endcp = '\0';
+ usedarg = 0;
+ }
+ }
+ }
+ if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
+ *exflagsp |= MNT_EXRDONLY;
+ } else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
+ !(allflag = strcmp(cpopt, "mapall")) ||
+ !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
+ usedarg++;
+ parsecred(cpoptarg, cr);
+ if (allflag == 0) {
+ *exflagsp |= MNT_EXPORTANON;
+ opt_flags |= OP_MAPALL;
+ } else
+ opt_flags |= OP_MAPROOT;
+ } else if (cpoptarg && (!strcmp(cpopt, "mask") ||
+ !strcmp(cpopt, "m"))) {
+ if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
+ syslog(LOG_ERR, "bad mask: %s", cpoptarg);
+ return (1);
+ }
+ usedarg++;
+ opt_flags |= OP_MASK;
+ } else if (cpoptarg && (!strcmp(cpopt, "network") ||
+ !strcmp(cpopt, "n"))) {
+ if (strchr(cpoptarg, '/') != NULL) {
+ if (debug)
+ fprintf(stderr, "setting OP_MASKLEN\n");
+ opt_flags |= OP_MASKLEN;
+ }
+ if (grp->gr_type != GT_NULL) {
+ syslog(LOG_ERR, "network/host conflict");
+ return (1);
+ } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
+ syslog(LOG_ERR, "bad net: %s", cpoptarg);
+ return (1);
+ }
+ grp->gr_type = GT_NET;
+ *has_hostp = 1;
+ usedarg++;
+ opt_flags |= OP_NET;
+ } else if (!strcmp(cpopt, "alldirs")) {
+ opt_flags |= OP_ALLDIRS;
+ } else if (!strcmp(cpopt, "public")) {
+ *exflagsp |= MNT_EXPUBLIC;
+ } else if (!strcmp(cpopt, "webnfs")) {
+ *exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
+ opt_flags |= OP_MAPALL;
+ } else if (cpoptarg && !strcmp(cpopt, "index")) {
+ ep->ex_indexfile = strdup(cpoptarg);
+ } else if (!strcmp(cpopt, "quiet")) {
+ opt_flags |= OP_QUIET;
+ } else if (!strcmp(cpopt, "sec")) {
+ if (parsesec(cpoptarg, ep))
+ return (1);
+ opt_flags |= OP_SEC;
+ usedarg++;
+ } else {
+ syslog(LOG_ERR, "bad opt %s", cpopt);
+ return (1);
+ }
+ if (usedarg >= 0) {
+ *endcp = savedc2;
+ **endcpp = savedc;
+ if (usedarg > 0) {
+ *cpp = cp;
+ *endcpp = endcp;
+ }
+ return (0);
+ }
+ cpopt = cpoptend;
+ }
+ **endcpp = savedc;
+ return (0);
+}
+
+/*
+ * Translate a character string to the corresponding list of network
+ * addresses for a hostname.
+ */
+int
+get_host(cp, grp, tgrp)
+ char *cp;
+ struct grouplist *grp;
+ struct grouplist *tgrp;
+{
+ struct grouplist *checkgrp;
+ struct addrinfo *ai, *tai, hints;
+ int ecode;
+ char host[NI_MAXHOST];
+
+ if (grp->gr_type != GT_NULL) {
+ syslog(LOG_ERR, "Bad netgroup type for ip host %s", cp);
+ return (1);
+ }
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_CANONNAME;
+ hints.ai_protocol = IPPROTO_UDP;
+ ecode = getaddrinfo(cp, NULL, &hints, &ai);
+ if (ecode != 0) {
+ syslog(LOG_ERR,"can't get address info for host %s", cp);
+ return 1;
+ }
+ grp->gr_ptr.gt_addrinfo = ai;
+ while (ai != NULL) {
+ if (ai->ai_canonname == NULL) {
+ if (getnameinfo(ai->ai_addr, ai->ai_addrlen, host,
+ sizeof host, NULL, 0, NI_NUMERICHOST) != 0)
+ strlcpy(host, "?", sizeof(host));
+ ai->ai_canonname = strdup(host);
+ ai->ai_flags |= AI_CANONNAME;
+ }
+ if (debug)
+ fprintf(stderr, "got host %s\n", ai->ai_canonname);
+ /*
+ * Sanity check: make sure we don't already have an entry
+ * for this host in the grouplist.
+ */
+ for (checkgrp = tgrp; checkgrp != NULL;
+ checkgrp = checkgrp->gr_next) {
+ if (checkgrp->gr_type != GT_HOST)
+ continue;
+ for (tai = checkgrp->gr_ptr.gt_addrinfo; tai != NULL;
+ tai = tai->ai_next) {
+ if (sacmp(tai->ai_addr, ai->ai_addr, NULL) != 0)
+ continue;
+ if (debug)
+ fprintf(stderr,
+ "ignoring duplicate host %s\n",
+ ai->ai_canonname);
+ grp->gr_type = GT_IGNORE;
+ return (0);
+ }
+ }
+ ai = ai->ai_next;
+ }
+ grp->gr_type = GT_HOST;
+ return (0);
+}
+
+/*
+ * Free up an exports list component
+ */
+void
+free_exp(ep)
+ struct exportlist *ep;
+{
+
+ if (ep->ex_defdir) {
+ free_host(ep->ex_defdir->dp_hosts);
+ free((caddr_t)ep->ex_defdir);
+ }
+ if (ep->ex_fsdir)
+ free(ep->ex_fsdir);
+ if (ep->ex_indexfile)
+ free(ep->ex_indexfile);
+ free_dir(ep->ex_dirl);
+ free((caddr_t)ep);
+}
+
+/*
+ * Free hosts.
+ */
+void
+free_host(hp)
+ struct hostlist *hp;
+{
+ struct hostlist *hp2;
+
+ while (hp) {
+ hp2 = hp;
+ hp = hp->ht_next;
+ free((caddr_t)hp2);
+ }
+}
+
+struct hostlist *
+get_ht()
+{
+ struct hostlist *hp;
+
+ hp = (struct hostlist *)malloc(sizeof (struct hostlist));
+ if (hp == (struct hostlist *)NULL)
+ out_of_mem();
+ hp->ht_next = (struct hostlist *)NULL;
+ hp->ht_flag = 0;
+ return (hp);
+}
+
+/*
+ * Out of memory, fatal
+ */
+void
+out_of_mem()
+{
+
+ syslog(LOG_ERR, "out of memory");
+ exit(2);
+}
+
+/*
+ * Do the nmount() syscall with the update flag to push the export info into
+ * the kernel.
+ */
+int
+do_mount(struct exportlist *ep, struct grouplist *grp, int exflags,
+ struct xucred *anoncrp, char *dirp, int dirplen, struct statfs *fsb)
+{
+ struct statfs fsb1;
+ struct addrinfo *ai;
+ struct export_args eap;
+ char errmsg[255];
+ char *cp;
+ int done;
+ char savedc;
+ struct iovec *iov;
+ int i, iovlen;
+ int ret;
+
+ cp = NULL;
+ savedc = '\0';
+ iov = NULL;
+ iovlen = 0;
+ ret = 0;
+
+ bzero(&eap, sizeof(eap));
+ bzero(errmsg, sizeof(errmsg));
+ eap.ex_flags = exflags;
+ eap.ex_anon = *anoncrp;
+ eap.ex_indexfile = ep->ex_indexfile;
+ if (grp->gr_type == GT_HOST)
+ ai = grp->gr_ptr.gt_addrinfo;
+ else
+ ai = NULL;
+ eap.ex_numsecflavors = ep->ex_numsecflavors;
+ for (i = 0; i < eap.ex_numsecflavors; i++)
+ eap.ex_secflavors[i] = ep->ex_secflavors[i];
+ if (eap.ex_numsecflavors == 0) {
+ eap.ex_numsecflavors = 1;
+ eap.ex_secflavors[0] = AUTH_SYS;
+ }
+ done = FALSE;
+
+ build_iovec(&iov, &iovlen, "fstype", NULL, 0);
+ build_iovec(&iov, &iovlen, "fspath", NULL, 0);
+ build_iovec(&iov, &iovlen, "from", NULL, 0);
+ build_iovec(&iov, &iovlen, "update", NULL, 0);
+ build_iovec(&iov, &iovlen, "export", &eap, sizeof(eap));
+ build_iovec(&iov, &iovlen, "errmsg", errmsg, sizeof(errmsg));
+
+ while (!done) {
+ switch (grp->gr_type) {
+ case GT_HOST:
+ if (ai->ai_addr->sa_family == AF_INET6 && have_v6 == 0)
+ goto skip;
+ eap.ex_addr = ai->ai_addr;
+ eap.ex_addrlen = ai->ai_addrlen;
+ eap.ex_masklen = 0;
+ break;
+ case GT_NET:
+ if (grp->gr_ptr.gt_net.nt_net.ss_family == AF_INET6 &&
+ have_v6 == 0)
+ goto skip;
+ eap.ex_addr =
+ (struct sockaddr *)&grp->gr_ptr.gt_net.nt_net;
+ eap.ex_addrlen =
+ ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_net)->sa_len;
+ eap.ex_mask =
+ (struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask;
+ eap.ex_masklen = ((struct sockaddr *)&grp->gr_ptr.gt_net.nt_mask)->sa_len;
+ break;
+ case GT_DEFAULT:
+ eap.ex_addr = NULL;
+ eap.ex_addrlen = 0;
+ eap.ex_mask = NULL;
+ eap.ex_masklen = 0;
+ break;
+ case GT_IGNORE:
+ ret = 0;
+ goto error_exit;
+ break;
+ default:
+ syslog(LOG_ERR, "bad grouptype");
+ if (cp)
+ *cp = savedc;
+ ret = 1;
+ goto error_exit;
+ };
+
+ /*
+ * XXX:
+ * Maybe I should just use the fsb->f_mntonname path instead
+ * of looping back up the dirp to the mount point??
+ * Also, needs to know how to export all types of local
+ * exportable filesystems and not just "ufs".
+ */
+ iov[1].iov_base = fsb->f_fstypename; /* "fstype" */
+ iov[1].iov_len = strlen(fsb->f_fstypename) + 1;
+ iov[3].iov_base = fsb->f_mntonname; /* "fspath" */
+ iov[3].iov_len = strlen(fsb->f_mntonname) + 1;
+ iov[5].iov_base = fsb->f_mntfromname; /* "from" */
+ iov[5].iov_len = strlen(fsb->f_mntfromname) + 1;
+
+ while (nmount(iov, iovlen, fsb->f_flags) < 0) {
+ if (cp)
+ *cp-- = savedc;
+ else
+ cp = dirp + dirplen - 1;
+ if (opt_flags & OP_QUIET) {
+ ret = 1;
+ goto error_exit;
+ }
+ if (errno == EPERM) {
+ if (debug)
+ warnx("can't change attributes for %s",
+ dirp);
+ syslog(LOG_ERR,
+ "can't change attributes for %s", dirp);
+ ret = 1;
+ goto error_exit;
+ }
+ if (opt_flags & OP_ALLDIRS) {
+ if (errno == EINVAL)
+ syslog(LOG_ERR,
+ "-alldirs requested but %s is not a filesystem mountpoint",
+ dirp);
+ else
+ syslog(LOG_ERR,
+ "could not remount %s: %m",
+ dirp);
+ ret = 1;
+ goto error_exit;
+ }
+ /* back up over the last component */
+ while (*cp == '/' && cp > dirp)
+ cp--;
+ while (*(cp - 1) != '/' && cp > dirp)
+ cp--;
+ if (cp == dirp) {
+ if (debug)
+ warnx("mnt unsucc");
+ syslog(LOG_ERR, "can't export %s %s", dirp,
+ errmsg);
+ ret = 1;
+ goto error_exit;
+ }
+ savedc = *cp;
+ *cp = '\0';
+ /* Check that we're still on the same filesystem. */
+ if (statfs(dirp, &fsb1) != 0 || bcmp(&fsb1.f_fsid,
+ &fsb->f_fsid, sizeof(fsb1.f_fsid)) != 0) {
+ *cp = savedc;
+ syslog(LOG_ERR, "can't export %s %s", dirp,
+ errmsg);
+ ret = 1;
+ goto error_exit;
+ }
+ }
+skip:
+ if (ai != NULL)
+ ai = ai->ai_next;
+ if (ai == NULL)
+ done = TRUE;
+ }
+ if (cp)
+ *cp = savedc;
+error_exit:
+ /* free strings allocated by strdup() in getmntopts.c */
+ if (iov != NULL) {
+ free(iov[0].iov_base); /* fstype */
+ free(iov[2].iov_base); /* fspath */
+ free(iov[4].iov_base); /* from */
+ free(iov[6].iov_base); /* update */
+ free(iov[8].iov_base); /* export */
+ free(iov[10].iov_base); /* errmsg */
+
+ /* free iov, allocated by realloc() */
+ free(iov);
+ }
+ return (ret);
+}
+
+/*
+ * Translate a net address.
+ *
+ * If `maskflg' is nonzero, then `cp' is a netmask, not a network address.
+ */
+int
+get_net(cp, net, maskflg)
+ char *cp;
+ struct netmsk *net;
+ int maskflg;
+{
+ struct netent *np = NULL;
+ char *name, *p, *prefp;
+ struct sockaddr_in sin;
+ struct sockaddr *sa = NULL;
+ struct addrinfo hints, *ai = NULL;
+ char netname[NI_MAXHOST];
+ long preflen;
+
+ p = prefp = NULL;
+ if ((opt_flags & OP_MASKLEN) && !maskflg) {
+ p = strchr(cp, '/');
+ *p = '\0';
+ prefp = p + 1;
+ }
+
+ /*
+ * Check for a numeric address first. We wish to avoid
+ * possible DNS lookups in getnetbyname().
+ */
+ if (isxdigit(*cp) || *cp == ':') {
+ memset(&hints, 0, sizeof hints);
+ /* Ensure the mask and the network have the same family. */
+ if (maskflg && (opt_flags & OP_NET))
+ hints.ai_family = net->nt_net.ss_family;
+ else if (!maskflg && (opt_flags & OP_HAVEMASK))
+ hints.ai_family = net->nt_mask.ss_family;
+ else
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_flags = AI_NUMERICHOST;
+ if (getaddrinfo(cp, NULL, &hints, &ai) == 0)
+ sa = ai->ai_addr;
+ if (sa != NULL && ai->ai_family == AF_INET) {
+ /*
+ * The address in `cp' is really a network address, so
+ * use inet_network() to re-interpret this correctly.
+ * e.g. "127.1" means 127.1.0.0, not 127.0.0.1.
+ */
+ bzero(&sin, sizeof sin);
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof sin;
+ sin.sin_addr = inet_makeaddr(inet_network(cp), 0);
+ if (debug)
+ fprintf(stderr, "get_net: v4 addr %s\n",
+ inet_ntoa(sin.sin_addr));
+ sa = (struct sockaddr *)&sin;
+ }
+ }
+ if (sa == NULL && (np = getnetbyname(cp)) != NULL) {
+ bzero(&sin, sizeof sin);
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof sin;
+ sin.sin_addr = inet_makeaddr(np->n_net, 0);
+ sa = (struct sockaddr *)&sin;
+ }
+ if (sa == NULL)
+ goto fail;
+
+ if (maskflg) {
+ /* The specified sockaddr is a mask. */
+ if (checkmask(sa) != 0)
+ goto fail;
+ bcopy(sa, &net->nt_mask, sa->sa_len);
+ opt_flags |= OP_HAVEMASK;
+ } else {
+ /* The specified sockaddr is a network address. */
+ bcopy(sa, &net->nt_net, sa->sa_len);
+
+ /* Get a network name for the export list. */
+ if (np) {
+ name = np->n_name;
+ } else if (getnameinfo(sa, sa->sa_len, netname, sizeof netname,
+ NULL, 0, NI_NUMERICHOST) == 0) {
+ name = netname;
+ } else {
+ goto fail;
+ }
+ if ((net->nt_name = strdup(name)) == NULL)
+ out_of_mem();
+
+ /*
+ * Extract a mask from either a "/<masklen>" suffix, or
+ * from the class of an IPv4 address.
+ */
+ if (opt_flags & OP_MASKLEN) {
+ preflen = strtol(prefp, NULL, 10);
+ if (preflen < 0L || preflen == LONG_MAX)
+ goto fail;
+ bcopy(sa, &net->nt_mask, sa->sa_len);
+ if (makemask(&net->nt_mask, (int)preflen) != 0)
+ goto fail;
+ opt_flags |= OP_HAVEMASK;
+ *p = '/';
+ } else if (sa->sa_family == AF_INET &&
+ (opt_flags & OP_MASK) == 0) {
+ in_addr_t addr;
+
+ addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
+ if (IN_CLASSA(addr))
+ preflen = 8;
+ else if (IN_CLASSB(addr))
+ preflen = 16;
+ else if (IN_CLASSC(addr))
+ preflen = 24;
+ else if (IN_CLASSD(addr))
+ preflen = 28;
+ else
+ preflen = 32; /* XXX */
+
+ bcopy(sa, &net->nt_mask, sa->sa_len);
+ makemask(&net->nt_mask, (int)preflen);
+ opt_flags |= OP_HAVEMASK;
+ }
+ }
+
+ if (ai)
+ freeaddrinfo(ai);
+ return 0;
+
+fail:
+ if (ai)
+ freeaddrinfo(ai);
+ return 1;
+}
+
+/*
+ * Parse out the next white space separated field
+ */
+void
+nextfield(cp, endcp)
+ char **cp;
+ char **endcp;
+{
+ char *p;
+
+ p = *cp;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (*p == '\n' || *p == '\0')
+ *cp = *endcp = p;
+ else {
+ *cp = p++;
+ while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
+ p++;
+ *endcp = p;
+ }
+}
+
+/*
+ * Get an exports file line. Skip over blank lines and handle line
+ * continuations.
+ */
+int
+get_line()
+{
+ char *p, *cp;
+ size_t len;
+ int totlen, cont_line;
+
+ /*
+ * Loop around ignoring blank lines and getting all continuation lines.
+ */
+ p = line;
+ totlen = 0;
+ do {
+ if ((p = fgetln(exp_file, &len)) == NULL)
+ return (0);
+ cp = p + len - 1;
+ cont_line = 0;
+ while (cp >= p &&
+ (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
+ if (*cp == '\\')
+ cont_line = 1;
+ cp--;
+ len--;
+ }
+ if (cont_line) {
+ *++cp = ' ';
+ len++;
+ }
+ if (linesize < len + totlen + 1) {
+ linesize = len + totlen + 1;
+ line = realloc(line, linesize);
+ if (line == NULL)
+ out_of_mem();
+ }
+ memcpy(line + totlen, p, len);
+ totlen += len;
+ line[totlen] = '\0';
+ } while (totlen == 0 || cont_line);
+ return (1);
+}
+
+/*
+ * Parse a description of a credential.
+ */
+void
+parsecred(namelist, cr)
+ char *namelist;
+ struct xucred *cr;
+{
+ char *name;
+ int cnt;
+ char *names;
+ struct passwd *pw;
+ struct group *gr;
+ gid_t groups[NGROUPS + 1];
+ int ngroups;
+
+ cr->cr_version = XUCRED_VERSION;
+ /*
+ * Set up the unprivileged user.
+ */
+ cr->cr_uid = -2;
+ cr->cr_groups[0] = -2;
+ cr->cr_ngroups = 1;
+ /*
+ * Get the user's password table entry.
+ */
+ names = strsep(&namelist, " \t\n");
+ name = strsep(&names, ":");
+ if (isdigit(*name) || *name == '-')
+ pw = getpwuid(atoi(name));
+ else
+ pw = getpwnam(name);
+ /*
+ * Credentials specified as those of a user.
+ */
+ if (names == NULL) {
+ if (pw == NULL) {
+ syslog(LOG_ERR, "unknown user: %s", name);
+ return;
+ }
+ cr->cr_uid = pw->pw_uid;
+ ngroups = NGROUPS + 1;
+ if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups))
+ syslog(LOG_ERR, "too many groups");
+ /*
+ * Compress out duplicate.
+ */
+ cr->cr_ngroups = ngroups - 1;
+ cr->cr_groups[0] = groups[0];
+ for (cnt = 2; cnt < ngroups; cnt++)
+ cr->cr_groups[cnt - 1] = groups[cnt];
+ return;
+ }
+ /*
+ * Explicit credential specified as a colon separated list:
+ * uid:gid:gid:...
+ */
+ if (pw != NULL)
+ cr->cr_uid = pw->pw_uid;
+ else if (isdigit(*name) || *name == '-')
+ cr->cr_uid = atoi(name);
+ else {
+ syslog(LOG_ERR, "unknown user: %s", name);
+ return;
+ }
+ cr->cr_ngroups = 0;
+ while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) {
+ name = strsep(&names, ":");
+ if (isdigit(*name) || *name == '-') {
+ cr->cr_groups[cr->cr_ngroups++] = atoi(name);
+ } else {
+ if ((gr = getgrnam(name)) == NULL) {
+ syslog(LOG_ERR, "unknown group: %s", name);
+ continue;
+ }
+ cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
+ }
+ }
+ if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS)
+ syslog(LOG_ERR, "too many groups");
+}
+
+#define STRSIZ (RPCMNT_NAMELEN+RPCMNT_PATHLEN+50)
+/*
+ * Routines that maintain the remote mounttab
+ */
+void
+get_mountlist()
+{
+ struct mountlist *mlp, **mlpp;
+ char *host, *dirp, *cp;
+ char str[STRSIZ];
+ FILE *mlfile;
+
+ if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
+ if (errno == ENOENT)
+ return;
+ else {
+ syslog(LOG_ERR, "can't open %s", _PATH_RMOUNTLIST);
+ return;
+ }
+ }
+ mlpp = &mlhead;
+ while (fgets(str, STRSIZ, mlfile) != NULL) {
+ cp = str;
+ host = strsep(&cp, " \t\n");
+ dirp = strsep(&cp, " \t\n");
+ if (host == NULL || dirp == NULL)
+ continue;
+ mlp = (struct mountlist *)malloc(sizeof (*mlp));
+ if (mlp == (struct mountlist *)NULL)
+ out_of_mem();
+ strncpy(mlp->ml_host, host, RPCMNT_NAMELEN);
+ mlp->ml_host[RPCMNT_NAMELEN] = '\0';
+ strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
+ mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
+ mlp->ml_next = (struct mountlist *)NULL;
+ *mlpp = mlp;
+ mlpp = &mlp->ml_next;
+ }
+ fclose(mlfile);
+}
+
+void
+del_mlist(char *hostp, char *dirp)
+{
+ struct mountlist *mlp, **mlpp;
+ struct mountlist *mlp2;
+ FILE *mlfile;
+ int fnd = 0;
+
+ mlpp = &mlhead;
+ mlp = mlhead;
+ while (mlp) {
+ if (!strcmp(mlp->ml_host, hostp) &&
+ (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
+ fnd = 1;
+ mlp2 = mlp;
+ *mlpp = mlp = mlp->ml_next;
+ free((caddr_t)mlp2);
+ } else {
+ mlpp = &mlp->ml_next;
+ mlp = mlp->ml_next;
+ }
+ }
+ if (fnd) {
+ if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
+ syslog(LOG_ERR,"can't update %s", _PATH_RMOUNTLIST);
+ return;
+ }
+ mlp = mlhead;
+ while (mlp) {
+ fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
+ mlp = mlp->ml_next;
+ }
+ fclose(mlfile);
+ }
+}
+
+void
+add_mlist(hostp, dirp)
+ char *hostp, *dirp;
+{
+ struct mountlist *mlp, **mlpp;
+ FILE *mlfile;
+
+ mlpp = &mlhead;
+ mlp = mlhead;
+ while (mlp) {
+ if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
+ return;
+ mlpp = &mlp->ml_next;
+ mlp = mlp->ml_next;
+ }
+ mlp = (struct mountlist *)malloc(sizeof (*mlp));
+ if (mlp == (struct mountlist *)NULL)
+ out_of_mem();
+ strncpy(mlp->ml_host, hostp, RPCMNT_NAMELEN);
+ mlp->ml_host[RPCMNT_NAMELEN] = '\0';
+ strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
+ mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
+ mlp->ml_next = (struct mountlist *)NULL;
+ *mlpp = mlp;
+ if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
+ syslog(LOG_ERR, "can't update %s", _PATH_RMOUNTLIST);
+ return;
+ }
+ fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
+ fclose(mlfile);
+}
+
+/*
+ * Free up a group list.
+ */
+void
+free_grp(grp)
+ struct grouplist *grp;
+{
+ if (grp->gr_type == GT_HOST) {
+ if (grp->gr_ptr.gt_addrinfo != NULL)
+ freeaddrinfo(grp->gr_ptr.gt_addrinfo);
+ } else if (grp->gr_type == GT_NET) {
+ if (grp->gr_ptr.gt_net.nt_name)
+ free(grp->gr_ptr.gt_net.nt_name);
+ }
+ free((caddr_t)grp);
+}
+
+#ifdef DEBUG
+void
+SYSLOG(int pri, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+#endif /* DEBUG */
+
+/*
+ * Check options for consistency.
+ */
+int
+check_options(dp)
+ struct dirlist *dp;
+{
+
+ if (dp == (struct dirlist *)NULL)
+ return (1);
+ if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL)) {
+ syslog(LOG_ERR, "-mapall and -maproot mutually exclusive");
+ return (1);
+ }
+ if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
+ syslog(LOG_ERR, "-mask requires -network");
+ return (1);
+ }
+ if ((opt_flags & OP_NET) && (opt_flags & OP_HAVEMASK) == 0) {
+ syslog(LOG_ERR, "-network requires mask specification");
+ return (1);
+ }
+ if ((opt_flags & OP_MASK) && (opt_flags & OP_MASKLEN)) {
+ syslog(LOG_ERR, "-mask and /masklen are mutually exclusive");
+ return (1);
+ }
+ if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
+ syslog(LOG_ERR, "-alldirs has multiple directories");
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Check an absolute directory path for any symbolic links. Return true
+ */
+int
+check_dirpath(dirp)
+ char *dirp;
+{
+ char *cp;
+ int ret = 1;
+ struct stat sb;
+
+ cp = dirp + 1;
+ while (*cp && ret) {
+ if (*cp == '/') {
+ *cp = '\0';
+ if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
+ ret = 0;
+ *cp = '/';
+ }
+ cp++;
+ }
+ if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
+ ret = 0;
+ return (ret);
+}
+
+/*
+ * Make a netmask according to the specified prefix length. The ss_family
+ * and other non-address fields must be initialised before calling this.
+ */
+int
+makemask(struct sockaddr_storage *ssp, int bitlen)
+{
+ u_char *p;
+ int bits, i, len;
+
+ if ((p = sa_rawaddr((struct sockaddr *)ssp, &len)) == NULL)
+ return (-1);
+ if (bitlen > len * CHAR_BIT)
+ return (-1);
+
+ for (i = 0; i < len; i++) {
+ bits = (bitlen > CHAR_BIT) ? CHAR_BIT : bitlen;
+ *p++ = (1 << bits) - 1;
+ bitlen -= bits;
+ }
+ return 0;
+}
+
+/*
+ * Check that the sockaddr is a valid netmask. Returns 0 if the mask
+ * is acceptable (i.e. of the form 1...10....0).
+ */
+int
+checkmask(struct sockaddr *sa)
+{
+ u_char *mask;
+ int i, len;
+
+ if ((mask = sa_rawaddr(sa, &len)) == NULL)
+ return (-1);
+
+ for (i = 0; i < len; i++)
+ if (mask[i] != 0xff)
+ break;
+ if (i < len) {
+ if (~mask[i] & (u_char)(~mask[i] + 1))
+ return (-1);
+ i++;
+ }
+ for (; i < len; i++)
+ if (mask[i] != 0)
+ return (-1);
+ return (0);
+}
+
+/*
+ * Compare two sockaddrs according to a specified mask. Return zero if
+ * `sa1' matches `sa2' when filtered by the netmask in `samask'.
+ * If samask is NULL, perform a full comparision.
+ */
+int
+sacmp(struct sockaddr *sa1, struct sockaddr *sa2, struct sockaddr *samask)
+{
+ unsigned char *p1, *p2, *mask;
+ int len, i;
+
+ if (sa1->sa_family != sa2->sa_family ||
+ (p1 = sa_rawaddr(sa1, &len)) == NULL ||
+ (p2 = sa_rawaddr(sa2, NULL)) == NULL)
+ return (1);
+
+ switch (sa1->sa_family) {
+ case AF_INET6:
+ if (((struct sockaddr_in6 *)sa1)->sin6_scope_id !=
+ ((struct sockaddr_in6 *)sa2)->sin6_scope_id)
+ return (1);
+ break;
+ }
+
+ /* Simple binary comparison if no mask specified. */
+ if (samask == NULL)
+ return (memcmp(p1, p2, len));
+
+ /* Set up the mask, and do a mask-based comparison. */
+ if (sa1->sa_family != samask->sa_family ||
+ (mask = sa_rawaddr(samask, NULL)) == NULL)
+ return (1);
+
+ for (i = 0; i < len; i++)
+ if ((p1[i] & mask[i]) != (p2[i] & mask[i]))
+ return (1);
+ return (0);
+}
+
+/*
+ * Return a pointer to the part of the sockaddr that contains the
+ * raw address, and set *nbytes to its length in bytes. Returns
+ * NULL if the address family is unknown.
+ */
+void *
+sa_rawaddr(struct sockaddr *sa, int *nbytes) {
+ void *p;
+ int len;
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ len = sizeof(((struct sockaddr_in *)sa)->sin_addr);
+ p = &((struct sockaddr_in *)sa)->sin_addr;
+ break;
+ case AF_INET6:
+ len = sizeof(((struct sockaddr_in6 *)sa)->sin6_addr);
+ p = &((struct sockaddr_in6 *)sa)->sin6_addr;
+ break;
+ default:
+ p = NULL;
+ len = 0;
+ }
+
+ if (nbytes != NULL)
+ *nbytes = len;
+ return (p);
+}
+
+void
+huphandler(int sig)
+{
+ got_sighup = 1;
+}
+
+void terminate(sig)
+int sig;
+{
+ pidfile_remove(pfh);
+ rpcb_unset(RPCPROG_MNT, RPCMNT_VER1, NULL);
+ rpcb_unset(RPCPROG_MNT, RPCMNT_VER3, NULL);
+ exit (0);
+}
diff --git a/usr.sbin/mountd/netgroup.5 b/usr.sbin/mountd/netgroup.5
new file mode 100644
index 0000000..c94f880
--- /dev/null
+++ b/usr.sbin/mountd/netgroup.5
@@ -0,0 +1,190 @@
+.\" 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.
+.\"
+.\" @(#)netgroup.5 8.2 (Berkeley) 12/11/93
+.\" $FreeBSD$
+.\"
+.Dd December 11, 1993
+.Dt NETGROUP 5
+.Os
+.Sh NAME
+.Nm netgroup
+.Nd defines network groups
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+file
+specifies ``netgroups'', which are sets of
+.Sy (host, user, domain)
+tuples that are to be given similar network access.
+.Pp
+Each line in the file
+consists of a netgroup name followed by a list of the members of the
+netgroup.
+Each member can be either the name of another netgroup or a specification
+of a tuple as follows:
+.Bd -literal -offset indent
+(host, user, domain)
+.Ed
+.Pp
+where the
+.Sy host ,
+.Sy user ,
+and
+.Sy domain
+are character string names for the corresponding component.
+Any of the comma separated fields may be empty to specify a ``wildcard'' value
+or may consist of the string ``-'' to specify ``no valid value''.
+The members of the list may be separated by whitespace and/or commas;
+the ``\e'' character may be used at the end of a line to specify
+line continuation.
+Lines are limited to 1024 characters.
+The functions specified in
+.Xr getnetgrent 3
+should normally be used to access the
+.Nm
+database.
+.Pp
+Lines that begin with a # are treated as comments.
+.Sh NIS/YP INTERACTION
+On most other platforms,
+.Nm Ns s
+are only used in conjunction with
+.Tn NIS
+and local
+.Pa /etc/netgroup
+files are ignored.
+With
+.Fx ,
+.Nm Ns s
+can be used with either
+.Tn NIS
+or local files, but there are certain
+caveats to consider.
+The existing
+.Nm
+system is extremely inefficient where
+.Fn innetgr 3
+lookups are concerned since
+.Nm
+memberships are computed on the fly.
+By contrast, the
+.Tn NIS
+.Nm
+database consists of three separate maps (netgroup, netgroup.byuser
+and netgroup.byhost) that are keyed to allow
+.Fn innetgr 3
+lookups to be done quickly.
+The
+.Fx
+.Nm
+system can interact with the
+.Tn NIS
+.Nm
+maps in the following ways:
+.Bl -bullet -offset indent
+.It
+If the
+.Pa /etc/netgroup
+file does not exist, or it exists and is empty, or
+it exists and contains only a
+.Sq + ,
+and
+.Tn NIS
+is running,
+.Nm
+lookups will be done exclusively through
+.Tn NIS ,
+with
+.Fn innetgr 3
+taking advantage of the netgroup.byuser and
+netgroup.byhost maps to speed up searches.
+(This
+is more or less compatible with the behavior of SunOS and
+similar platforms.)
+.It
+If the
+.Pa /etc/netgroup
+exists and contains only local
+.Nm
+information (with no
+.Tn NIS
+.Sq +
+token), then only the local
+.Nm
+information will be processed (and
+.Tn NIS
+will be ignored).
+.It
+If
+.Pa /etc/netgroup
+exists and contains both local netgroup data
+.Pa and
+the
+.Tn NIS
+.Sq +
+token, the local data and the
+.Tn NIS
+netgroup
+map will be processed as a single combined
+.Nm
+database.
+While this configuration is the most flexible, it
+is also the least efficient: in particular,
+.Fn innetgr 3
+lookups will be especially slow if the
+database is large.
+.El
+.Sh FILES
+.Bl -tag -width /etc/netgroup -compact
+.It Pa /etc/netgroup
+the netgroup database
+.El
+.Sh COMPATIBILITY
+The file format is compatible with that of various vendors, however it
+appears that not all vendors use an identical format.
+.Sh SEE ALSO
+.Xr getnetgrent 3 ,
+.Xr exports 5
+.Sh BUGS
+The interpretation of access restrictions based on the member tuples of a
+netgroup is left up to the various network applications.
+Also, it is not obvious how the domain specification
+applies to the
+.Bx
+environment.
+.Pp
+The
+.Nm
+database should be stored in the form of a
+hashed
+.Xr db 3
+database just like the
+.Xr passwd 5
+database to speed up reverse lookups.
diff --git a/usr.sbin/mountd/pathnames.h b/usr.sbin/mountd/pathnames.h
new file mode 100644
index 0000000..2b73977
--- /dev/null
+++ b/usr.sbin/mountd/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_EXPORTS "/etc/exports"
+#define _PATH_RMOUNTLIST "/var/db/mountdtab"
+#define _PATH_MOUNTDPID "/var/run/mountd.pid"
diff --git a/usr.sbin/moused/Makefile b/usr.sbin/moused/Makefile
new file mode 100644
index 0000000..623954e
--- /dev/null
+++ b/usr.sbin/moused/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+PROG= moused
+MAN= moused.8
+
+WARNS?=6
+
+DPADD= ${LIBUTIL} ${LIBM}
+LDADD= -lutil -lm
+
+#BINMODE=4555
+#PRECIOUSPROG=
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/moused/moused.8 b/usr.sbin/moused/moused.8
new file mode 100644
index 0000000..333b247
--- /dev/null
+++ b/usr.sbin/moused/moused.8
@@ -0,0 +1,854 @@
+.\" Copyright (c) 1996
+.\" Mike Pritchard <mpp@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 Mike Pritchard.
+.\" 4. Neither the name of the author 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 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 May 15, 2008
+.Dt MOUSED 8
+.Os
+.Sh NAME
+.Nm moused
+.Nd pass mouse data to the console driver
+.Sh SYNOPSIS
+.Nm
+.Op Fl DPRacdfs
+.Op Fl I Ar file
+.Op Fl F Ar rate
+.Op Fl r Ar resolution
+.Op Fl S Ar baudrate
+.Op Fl VH Op Fl U Ar distance Fl L Ar distance
+.Op Fl A Ar exp Ns Op , Ns Ar offset
+.Op Fl a Ar X Ns Op , Ns Ar Y
+.Op Fl C Ar threshold
+.Op Fl m Ar N=M
+.Op Fl w Ar N
+.Op Fl z Ar target
+.Op Fl t Ar mousetype
+.Op Fl l Ar level
+.Op Fl 3 Op Fl E Ar timeout
+.Op Fl T Ar distance Ns Op , Ns Ar time Ns Op , Ns Ar after
+.Fl p Ar port
+.Pp
+.Nm
+.Op Fl Pd
+.Fl p Ar port
+.Fl i Ar info
+.Sh DESCRIPTION
+The
+.Nm
+utility and the console driver work together to support
+mouse operation in the text console and user programs.
+They virtualize the mouse and provide user programs with mouse data
+in the standard format
+(see
+.Xr sysmouse 4 ) .
+.Pp
+The mouse daemon listens to the specified port for mouse data,
+interprets and then passes it via ioctls to the console driver.
+The mouse daemon
+reports translation movement, button press/release
+events and movement of the roller or the wheel if available.
+The roller/wheel movement is reported as
+.Dq Z
+axis movement.
+.Pp
+The console driver will display the mouse pointer on the screen
+and provide cut and paste functions if the mouse pointer is enabled
+in the virtual console via
+.Xr vidcontrol 1 .
+If
+.Xr sysmouse 4
+is opened by the user program, the console driver also passes the mouse
+data to the device so that the user program will see it.
+.Pp
+If the mouse daemon receives the signal
+.Dv SIGHUP ,
+it will reopen the mouse port and reinitialize itself.
+Useful if
+the mouse is attached/detached while the system is suspended.
+.Pp
+If the mouse daemon receives the signal
+.Dv SIGUSR1 ,
+it will stop passing mouse events.
+Sending the signal
+.Dv SIGUSR1
+again will resume passing mouse events.
+Useful if your typing on a laptop is
+interrupted by accidentally touching the mouse pad.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl 3
+Emulate the third (middle) button for 2-button mice.
+It is emulated
+by pressing the left and right physical buttons simultaneously.
+.It Fl C Ar threshold
+Set double click speed as the maximum interval in msec between button clicks.
+Without this option, the default value of 500 msec will be assumed.
+This option will have effect only on the cut and paste operations
+in the text mode console.
+The user program which is reading mouse data
+via
+.Xr sysmouse 4
+will not be affected.
+.It Fl D
+Lower DTR on the serial port.
+This option is valid only if
+.Ar mousesystems
+is selected as the protocol type.
+The DTR line may need to be dropped for a 3-button mouse
+to operate in the
+.Ar mousesystems
+mode.
+.It Fl E Ar timeout
+When the third button emulation is enabled
+(see above),
+the
+.Nm
+utility waits
+.Ar timeout
+msec at most before deciding whether two buttons are being pressed
+simultaneously.
+The default timeout is 100 msec.
+.It Fl F Ar rate
+Set the report rate (reports/sec) of the device if supported.
+.It Fl L Ar distance
+When
+.Dq Virtual Scrolling
+is enabled, the
+.Fl L
+option can be used to set the
+.Ar distance
+(in pixels) that the mouse must move before a scroll event
+is generated. This effectively controls the scrolling speed.
+The default
+.Ar distance
+is 2 pixels.
+.It Fl H
+Enable
+.Dq Horizontal Virtual Scrolling .
+With this option set, holding the middle mouse
+button down will cause motion to be interpreted as
+horizontal scrolling.
+Use the
+.Fl U
+option to set the distance the mouse must move before the scrolling mode is
+activated and the
+.Fl L
+option to set the scrolling speed.
+This option may be used with or without the
+.Fl V
+option.
+.It Fl I Ar file
+Write the process id of the
+.Nm
+utility in the specified file.
+Without this option, the process id will be stored in
+.Pa /var/run/moused.pid .
+.It Fl P
+Do not start the Plug and Play COM device enumeration procedure
+when identifying the serial mouse.
+If this option is given together with the
+.Fl i
+option, the
+.Nm
+utility will not be able to print useful information for the serial mouse.
+.It Fl R
+Lower RTS on the serial port.
+This option is valid only if
+.Ar mousesystems
+is selected as the protocol type by the
+.Fl t
+option below.
+It is often used with the
+.Fl D
+option above.
+Both RTS and DTR lines may need to be dropped for
+a 3-button mouse to operate in the
+.Ar mousesystems
+mode.
+.It Fl S Ar baudrate
+Select the baudrate for the serial port (1200 to 9600).
+Not all serial mice support this option.
+.It Fl T Ar distance Ns Op , Ns Ar time Ns Op , Ns Ar after
+Terminate drift.
+Use this option if mouse pointer slowly wanders when mouse is not moved.
+Movements up to
+.Ar distance
+(for example 4) pixels (X+Y) in
+.Ar time
+msec (default 500) are ignored, except during
+.Ar after
+msec (default 4000) since last real mouse movement.
+.It Fl V
+Enable
+.Dq Virtual Scrolling .
+With this option set, holding the middle mouse
+button down will cause motion to be interpreted as scrolling.
+Use the
+.Fl U
+option to set the distance the mouse must move before the scrolling mode is
+activated and the
+.Fl L
+option to set the scrolling speed.
+.It Fl U Ar distance
+When
+.Dq Virtual Scrolling
+is enabled, the
+.Fl U
+option can be used to set the
+.Ar distance
+(in pixels) that the mouse must move before the scrolling
+mode is activated.
+The default
+.Ar distance
+is 3 pixels.
+.It Fl A Ar exp Ns Op , Ns Ar offset
+Apply exponential (dynamic) acceleration to mouse movements:
+the faster you move the mouse, the more it will be accelerated.
+That means that small mouse movements are not accelerated,
+so they are still very accurate, while a faster movement will
+drive the pointer quickly across the screen.
+.Pp
+The
+.Ar exp
+value specifies the exponent, which is basically
+the amount of acceleration. Useful values are in the
+range 1.1 to 2.0, but it depends on your mouse hardware
+and your personal preference. A value of 1.0 means no
+exponential acceleration. A value of 2.0 means squared
+acceleration (i.e. if you move the mouse twice as fast,
+the pointer will move four times as fast on the screen).
+Values beyond 2.0 are possible but not recommended.
+A good value to start is probably 1.5.
+.Pp
+The optional
+.Ar offset
+value specifies the distance at which the acceleration
+begins. The default is 1.0, which means that the
+acceleration is applied to movements larger than one unit.
+If you specify a larger value, it takes more speed for
+the acceleration to kick in, i.e. the speed range for
+small and accurate movements is wider.
+Usually the default should be sufficient, but if you're
+not satisfied with the behaviour, try a value of 2.0.
+.Pp
+Note that the
+.Fl A
+option interacts badly with the X server's own acceleration,
+which doesn't work very well anyway. Therefore it is
+recommended to switch it off if necessary:
+.Dq xset m 1 .
+.It Fl a Ar X Ns Op , Ns Ar Y
+Accelerate or decelerate the mouse input.
+This is a linear acceleration only.
+Values less than 1.0 slow down movement, values greater than 1.0 speed it
+up.
+Specifying only one value sets the acceleration for both axes.
+.Pp
+You can use the
+.Fl a
+and
+.Fl A
+options at the same time to have the combined effect
+of linear and exponential acceleration.
+.It Fl c
+Some mice report middle button down events
+as if the left and right buttons are being pressed.
+This option handles this.
+.It Fl d
+Enable debugging messages.
+.It Fl f
+Do not become a daemon and instead run as a foreground process.
+Useful for testing and debugging.
+.It Fl i Ar info
+Print specified information and quit.
+Available pieces of
+information are:
+.Pp
+.Bl -tag -compact -width modelxxx
+.It Ar port
+Port (device file) name, i.e.\&
+.Pa /dev/cuad0 ,
+.Pa /dev/mse0
+and
+.Pa /dev/psm0 .
+.It Ar if
+Interface type: serial, bus, inport or ps/2.
+.It Ar type
+Protocol type.
+It is one of the types listed under the
+.Fl t
+option below or
+.Ar sysmouse
+if the driver supports the
+.Ar sysmouse
+data format standard.
+.It Ar model
+Mouse model.
+The
+.Nm
+utility may not always be able to identify the model.
+.It Ar all
+All of the above items.
+Print port, interface, type and model in this order
+in one line.
+.El
+.Pp
+If the
+.Nm
+utility cannot determine the requested information, it prints
+.Dq Li unknown
+or
+.Dq Li generic .
+.It Fl l Ar level
+Specifies at which level
+.Nm
+should operate the mouse driver.
+Refer to
+.Sx Operation Levels
+in
+.Xr psm 4
+for more information on this.
+.It Fl m Ar N=M
+Assign the physical button
+.Ar M
+to the logical button
+.Ar N .
+You may specify as many instances of this option as you like.
+More than one physical button may be assigned to a logical button at the
+same time.
+In this case the logical button will be down,
+if either of the assigned physical buttons is held down.
+Do not put space around
+.Ql = .
+.It Fl p Ar port
+Use
+.Ar port
+to communicate with the mouse.
+.It Fl r Ar resolution
+Set the resolution of the device; in Dots Per Inch, or
+.Ar low ,
+.Ar medium-low ,
+.Ar medium-high
+or
+.Ar high .
+This option may not be supported by all the device.
+.It Fl s
+Select a baudrate of 9600 for the serial line.
+Not all serial mice support this option.
+.It Fl t Ar type
+Specify the protocol type of the mouse attached to the port.
+You may explicitly specify a type listed below, or use
+.Ar auto
+to let the
+.Nm
+utility automatically select an appropriate protocol for the given
+mouse.
+If you entirely omit this option in the command line,
+.Fl t Ar auto
+is assumed.
+Under normal circumstances,
+you need to use this option only if the
+.Nm
+utility is not able to detect the protocol automatically
+(see
+.Sx "Configuring Mouse Daemon" ) .
+.Pp
+Note that if a protocol type is specified with this option, the
+.Fl P
+option above is implied and Plug and Play COM device enumeration
+procedure will be disabled.
+.Pp
+Also note that if your mouse is attached to the PS/2 mouse port, you should
+always choose
+.Ar auto
+or
+.Ar ps/2 ,
+regardless of the brand and model of the mouse.
+Likewise, if your
+mouse is attached to the bus mouse port, choose
+.Ar auto
+or
+.Ar busmouse .
+Serial mouse protocols will not work with these mice.
+.Pp
+For the USB mouse, the protocol must be
+.Ar auto .
+No other protocol will work with the USB mouse.
+.Pp
+Valid types for this option are
+listed below.
+.Pp
+For the serial mouse:
+.Bl -tag -compact -width mousesystemsxxx
+.It Ar microsoft
+Microsoft serial mouse protocol.
+Most 2-button serial mice use this protocol.
+.It Ar intellimouse
+Microsoft IntelliMouse protocol.
+Genius NetMouse,
+.Tn ASCII
+Mie Mouse,
+Logitech MouseMan+ and FirstMouse+ use this protocol too.
+Other mice with a roller/wheel may be compatible with this protocol.
+.It Ar mousesystems
+MouseSystems 5-byte protocol.
+3-button mice may use this protocol.
+.It Ar mmseries
+MM Series mouse protocol.
+.It Ar logitech
+Logitech mouse protocol.
+Note that this is for old Logitech models.
+.Ar mouseman
+or
+.Ar intellimouse
+should be specified for newer models.
+.It Ar mouseman
+Logitech MouseMan and TrackMan protocol.
+Some 3-button mice may be compatible
+with this protocol.
+Note that MouseMan+ and FirstMouse+ use
+.Ar intellimouse
+protocol rather than this one.
+.It Ar glidepoint
+ALPS GlidePoint protocol.
+.It Ar thinkingmouse
+Kensington ThinkingMouse protocol.
+.It Ar mmhitab
+Hitachi tablet protocol.
+.It Ar x10mouseremote
+X10 MouseRemote.
+.It Ar kidspad
+Genius Kidspad and Easypad protocol.
+.It Ar versapad
+Interlink VersaPad protocol.
+.El
+.It Ar gtco_digipad
+GTCO Digipad protocol.
+.Pp
+For the bus and InPort mouse:
+.Bl -tag -compact -width mousesystemsxxx
+.It Ar busmouse
+This is the only protocol type available for
+the bus and InPort mouse and should be specified for any bus mice
+and InPort mice, regardless of the brand.
+.El
+.Pp
+For the PS/2 mouse:
+.Bl -tag -compact -width mousesystemsxxx
+.It Ar ps/2
+This is the only protocol type available for the PS/2 mouse
+and should be specified for any PS/2 mice, regardless of the brand.
+.El
+.Pp
+For the USB mouse,
+.Ar auto
+is the only protocol type available for the USB mouse
+and should be specified for any USB mice, regardless of the brand.
+.It Fl w Ar N
+Make the physical button
+.Ar N
+act as the wheel mode button.
+While this button is pressed, X and Y axis movement is reported to be zero
+and the Y axis movement is mapped to Z axis.
+You may further map the Z axis movement to virtual buttons by the
+.Fl z
+option below.
+.It Fl z Ar target
+Map Z axis (roller/wheel) movement to another axis or to virtual buttons.
+Valid
+.Ar target
+maybe:
+.Bl -tag -compact -width x__
+.It Ar x
+.It Ar y
+X or Y axis movement will be reported when the Z axis movement is detected.
+.It Ar N
+Report down events for the virtual buttons
+.Ar N
+and
+.Ar N+1
+respectively when negative and positive Z axis movement
+is detected.
+There do not need to be physical buttons
+.Ar N
+and
+.Ar N+1 .
+Note that mapping to logical buttons is carried out after mapping
+from the Z axis movement to the virtual buttons is done.
+.It Ar N1 N2
+Report down events for the virtual buttons
+.Ar N1
+and
+.Ar N2
+respectively when negative and positive Z axis movement
+is detected.
+.It Ar N1 N2 N3 N4
+This is useful for the mouse with two wheels of which
+the second wheel is used to generate horizontal scroll action,
+and for the mouse which has a knob or a stick which can detect
+the horizontal force applied by the user.
+.Pp
+The motion of the second wheel will be mapped to the buttons
+.Ar N3 ,
+for the negative direction, and
+.Ar N4 ,
+for the positive direction.
+If the buttons
+.Ar N3
+and
+.Ar N4
+actually exist in this mouse, their actions will not be detected.
+.Pp
+Note that horizontal movement or second roller/wheel movement may not
+always be detected,
+because there appears to be no accepted standard as to how it is encoded.
+.Pp
+Note also that some mice think left is the negative horizontal direction;
+others may think otherwise.
+Moreover, there are some mice whose two wheels are both mounted vertically,
+and the direction of the second vertical wheel does not match the
+first one.
+.El
+.El
+.Ss Configuring Mouse Daemon
+The first thing you need to know is the interface type
+of the mouse you are going to use.
+It can be determined by looking at the connector of the mouse.
+The serial mouse has a D-Sub female 9- or 25-pin connector.
+The bus and InPort mice have either a D-Sub male 9-pin connector
+or a round DIN 9-pin connector.
+The PS/2 mouse is equipped with a small, round DIN 6-pin connector.
+Some mice come with adapters with which the connector can
+be converted to another.
+If you are to use such an adapter,
+remember the connector at the very end of the mouse/adapter pair is
+what matters.
+The USB mouse has a flat rectangular connector.
+.Pp
+The next thing to decide is a port to use for the given interface.
+For the bus, InPort and PS/2 mice, there is little choice:
+the bus and InPort mice always use
+.Pa /dev/mse0 ,
+and the PS/2 mouse is always at
+.Pa /dev/psm0 .
+There may be more than one serial port to which the serial
+mouse can be attached.
+Many people often assign the first, built-in
+serial port
+.Pa /dev/cuad0
+to the mouse.
+You can attach multiple USB mice to your system or to your USB hub.
+They are accessible as
+.Pa /dev/ums0 , /dev/ums1 ,
+and so on.
+.Pp
+You may want to create a symbolic link
+.Pa /dev/mouse
+pointing to the real port to which the mouse is connected, so that you
+can easily distinguish which is your
+.Dq mouse
+port later.
+.Pp
+The next step is to guess the appropriate protocol type for the mouse.
+The
+.Nm
+utility may be able to automatically determine the protocol type.
+Run the
+.Nm
+utility with the
+.Fl i
+option and see what it says.
+If the command can identify
+the protocol type, no further investigation is necessary on your part.
+You may start the daemon without explicitly specifying a protocol type
+(see
+.Sx EXAMPLES ) .
+.Pp
+The command may print
+.Ar sysmouse
+if the mouse driver supports this protocol type.
+.Pp
+Note that the
+.Dv type
+and
+.Dv model
+printed by the
+.Fl i
+option do not necessarily match the product name of the pointing device
+in question, but they may give the name of the device with which it is
+compatible.
+.Pp
+If the
+.Fl i
+option yields nothing, you need to specify a protocol type to the
+.Nm
+utility by the
+.Fl t
+option.
+You have to make a guess and try.
+There is rule of thumb:
+.Pp
+.Bl -enum -compact -width 1.X
+.It
+The bus and InPort mice always use
+.Ar busmouse
+protocol regardless of the brand of the mouse.
+.It
+The
+.Ar ps/2
+protocol should always be specified for the PS/2 mouse
+regardless of the brand of the mouse.
+.It
+You must specify the
+.Ar auto
+protocol for the USB mouse.
+.It
+Most 2-button serial mice support the
+.Ar microsoft
+protocol.
+.It
+3-button serial mice may work with the
+.Ar mousesystems
+protocol.
+If it does not, it may work with the
+.Ar microsoft
+protocol although
+the third (middle) button will not function.
+3-button serial mice may also work with the
+.Ar mouseman
+protocol under which the third button may function as expected.
+.It
+3-button serial mice may have a small switch to choose between
+.Dq MS
+and
+.Dq PC ,
+or
+.Dq 2
+and
+.Dq 3 .
+.Dq MS
+or
+.Dq 2
+usually mean the
+.Ar microsoft
+protocol.
+.Dq PC
+or
+.Dq 3
+will choose the
+.Ar mousesystems
+protocol.
+.It
+If the mouse has a roller or a wheel, it may be compatible with the
+.Ar intellimouse
+protocol.
+.El
+.Pp
+To test if the selected protocol type is correct for the given mouse,
+enable the mouse pointer in the current virtual console,
+.Pp
+.Dl "vidcontrol -m on"
+.Pp
+start the mouse daemon in the foreground mode,
+.Pp
+.Dl "moused -f -p <selected_port> -t <selected_protocol>"
+.Pp
+and see if the mouse pointer travels correctly
+according to the mouse movement.
+Then try cut & paste features by
+clicking the left, right and middle buttons.
+Type ^C to stop
+the command.
+.Ss Multiple Mice
+As many instances of the mouse daemon as the number of mice attached to
+the system may be run simultaneously; one
+instance for each mouse.
+This is useful if the user wants to use the built-in PS/2 pointing device
+of a laptop computer while on the road, but wants to use a serial
+mouse when s/he attaches the system to the docking station in the office.
+Run two mouse daemons and tell the application program
+(such as the
+.Tn "X\ Window System" )
+to use
+.Xr sysmouse 4 ,
+then the application program will always see mouse data from either mouse.
+When the serial mouse is not attached, the corresponding mouse daemon
+will not detect any movement or button state change and the application
+program will only see mouse data coming from the daemon for the
+PS/2 mouse.
+In contrast when both mice are attached and both of them
+are moved at the same time in this configuration,
+the mouse pointer will travel across the screen just as if movement of
+the mice is combined all together.
+.Sh FILES
+.Bl -tag -width /dev/consolectl -compact
+.It Pa /dev/consolectl
+device to control the console
+.It Pa /dev/mse%d
+bus and InPort mouse driver
+.It Pa /dev/psm%d
+PS/2 mouse driver
+.It Pa /dev/sysmouse
+virtualized mouse driver
+.It Pa /dev/ttyv%d
+virtual consoles
+.It Pa /dev/ums%d
+USB mouse driver
+.It Pa /var/run/moused.pid
+process id of the currently running
+.Nm
+utility
+.It Pa /var/run/MouseRemote
+UNIX-domain stream socket for X10 MouseRemote events
+.El
+.Sh EXAMPLES
+.Dl "moused -p /dev/cuad0 -i type"
+.Pp
+Let the
+.Nm
+utility determine the protocol type of the mouse at the serial port
+.Pa /dev/cuad0 .
+If successful, the command will print the type, otherwise it will say
+.Dq Li unknown .
+.Bd -literal -offset indent
+moused -p /dev/cuad0
+vidcontrol -m on
+.Ed
+.Pp
+If the
+.Nm
+utility is able to identify the protocol type of the mouse at the specified
+port automatically, you can start the daemon without the
+.Fl t
+option and enable the mouse pointer in the text console as above.
+.Bd -literal -offset indent
+moused -p /dev/mouse -t microsoft
+vidcontrol -m on
+.Ed
+.Pp
+Start the mouse daemon on the serial port
+.Pa /dev/mouse .
+The protocol type
+.Ar microsoft
+is explicitly specified by the
+.Fl t
+option.
+.Pp
+.Dl "moused -p /dev/mouse -m 1=3 -m 3=1"
+.Pp
+Assign the physical button 3 (right button) to the logical button 1
+(logical left) and the physical button 1 (left) to the logical
+button 3 (logical right).
+This will effectively swap the left and right buttons.
+.Pp
+.Dl "moused -p /dev/mouse -t intellimouse -z 4"
+.Pp
+Report negative Z axis movement (i.e., mouse wheel) as the button 4 pressed
+and positive Z axis movement (i.e., mouse wheel) as the button 5 pressed.
+.Pp
+If you add
+.Pp
+.Dl "ALL ALL = NOPASSWD: /usr/bin/killall -USR1 moused"
+.Pp
+to your
+.Pa /usr/local/etc/sudoers
+file, and bind
+.Pp
+.Dl "killall -USR1 moused"
+.Pp
+to a key in your window manager, you can suspend mouse events on your laptop if
+you keep brushing over the mouse pad while typing.
+.Sh CAVEATS
+Many pad devices behave as if the first (left) button were pressed if
+the user
+.Dq taps
+the surface of the pad.
+In contrast, some ALPS GlidePoint and Interlink VersaPad models
+treat the tapping action
+as fourth button events.
+Use the option
+.Dq Fl m Li 1=4
+for these models
+to obtain the same effect as the other pad devices.
+.Pp
+Cut and paste functions in the virtual console assume that there
+are three buttons on the mouse.
+The logical button 1 (logical left) selects a region of text in the
+console and copies it to the cut buffer.
+The logical button 3 (logical right) extends the selected region.
+The logical button 2 (logical middle) pastes the selected text
+at the text cursor position.
+If the mouse has only two buttons, the middle, `paste' button
+is not available.
+To obtain the paste function, use the
+.Fl 3
+option to emulate the middle button, or use the
+.Fl m
+option to assign the physical right button to the logical middle button:
+.Dq Fl m Li 2=3 .
+.Sh SEE ALSO
+.Xr kill 1 ,
+.Xr vidcontrol 1 ,
+.Xr xset 1 ,
+.Xr keyboard 4 ,
+.Xr mse 4 ,
+.Xr psm 4 ,
+.Xr screen 4 ,
+.Xr sysmouse 4 ,
+.Xr ums 4
+.Sh STANDARDS
+The
+.Nm
+utility partially supports
+.Dq Plug and Play External COM Device Specification
+in order to support PnP serial mice.
+However, due to various degrees of conformance to the specification by
+existing serial mice, it does not strictly follow the version 1.0 of the
+standard.
+Even with this less strict approach,
+it may not always determine an appropriate protocol type
+for the given serial mouse.
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 2.2 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility was written by
+.An Michael Smith Aq msmith@FreeBSD.org .
+This manual page was written by
+.An Mike Pritchard Aq mpp@FreeBSD.org .
+The command and manual page have since been updated by
+.An Kazutaka Yokota Aq yokota@FreeBSD.org .
diff --git a/usr.sbin/moused/moused.c b/usr.sbin/moused/moused.c
new file mode 100644
index 0000000..f647f43
--- /dev/null
+++ b/usr.sbin/moused/moused.c
@@ -0,0 +1,3439 @@
+/**
+ ** Copyright (c) 1995 Michael Smith, All rights reserved.
+ **
+ ** Redistribution and use in source and binary forms, with or without
+ ** modification, are permitted provided that the following conditions
+ ** are met:
+ ** 1. Redistributions of source code must retain the above copyright
+ ** notice, this list of conditions and the following disclaimer as
+ ** the first lines of this file unmodified.
+ ** 2. Redistributions in binary form must 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 Michael Smith.
+ ** 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 Michael Smith ``AS IS'' AND ANY
+ ** EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ ** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Michael Smith BE LIABLE FOR
+ ** ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ ** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ ** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ ** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ ** EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ **
+ **/
+
+/**
+ ** MOUSED.C
+ **
+ ** Mouse daemon : listens to a serial port, the bus mouse interface, or
+ ** the PS/2 mouse port for mouse data stream, interprets data and passes
+ ** ioctls off to the console driver.
+ **
+ ** The mouse interface functions are derived closely from the mouse
+ ** handler in the XFree86 X server. Many thanks to the XFree86 people
+ ** for their great work!
+ **
+ **/
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/consio.h>
+#include <sys/mouse.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/un.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libutil.h>
+#include <limits.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <termios.h>
+#include <unistd.h>
+#include <math.h>
+
+#define MAX_CLICKTHRESHOLD 2000 /* 2 seconds */
+#define MAX_BUTTON2TIMEOUT 2000 /* 2 seconds */
+#define DFLT_CLICKTHRESHOLD 500 /* 0.5 second */
+#define DFLT_BUTTON2TIMEOUT 100 /* 0.1 second */
+#define DFLT_SCROLLTHRESHOLD 3 /* 3 pixels */
+#define DFLT_SCROLLSPEED 2 /* 2 pixels */
+
+/* Abort 3-button emulation delay after this many movement events. */
+#define BUTTON2_MAXMOVE 3
+
+#define TRUE 1
+#define FALSE 0
+
+#define MOUSE_XAXIS (-1)
+#define MOUSE_YAXIS (-2)
+
+/* Logitech PS2++ protocol */
+#define MOUSE_PS2PLUS_CHECKBITS(b) \
+ ((((b[2] & 0x03) << 2) | 0x02) == (b[1] & 0x0f))
+#define MOUSE_PS2PLUS_PACKET_TYPE(b) \
+ (((b[0] & 0x30) >> 2) | ((b[1] & 0x30) >> 4))
+
+#define ChordMiddle 0x0001
+#define Emulate3Button 0x0002
+#define ClearDTR 0x0004
+#define ClearRTS 0x0008
+#define NoPnP 0x0010
+#define VirtualScroll 0x0020
+#define HVirtualScroll 0x0040
+#define ExponentialAcc 0x0080
+
+#define ID_NONE 0
+#define ID_PORT 1
+#define ID_IF 2
+#define ID_TYPE 4
+#define ID_MODEL 8
+#define ID_ALL (ID_PORT | ID_IF | ID_TYPE | ID_MODEL)
+
+/* Operations on timespecs */
+#define tsclr(tvp) ((tvp)->tv_sec = (tvp)->tv_nsec = 0)
+#define tscmp(tvp, uvp, cmp) \
+ (((tvp)->tv_sec == (uvp)->tv_sec) ? \
+ ((tvp)->tv_nsec cmp (uvp)->tv_nsec) : \
+ ((tvp)->tv_sec cmp (uvp)->tv_sec))
+#define tssub(tvp, uvp, vvp) \
+ do { \
+ (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
+ (vvp)->tv_nsec = (tvp)->tv_nsec - (uvp)->tv_nsec; \
+ if ((vvp)->tv_nsec < 0) { \
+ (vvp)->tv_sec--; \
+ (vvp)->tv_nsec += 1000000000; \
+ } \
+ } while (0)
+
+#define debug(...) do { \
+ if (debug && nodaemon) \
+ warnx(__VA_ARGS__); \
+} while (0)
+
+#define logerr(e, ...) do { \
+ log_or_warn(LOG_DAEMON | LOG_ERR, errno, __VA_ARGS__); \
+ exit(e); \
+} while (0)
+
+#define logerrx(e, ...) do { \
+ log_or_warn(LOG_DAEMON | LOG_ERR, 0, __VA_ARGS__); \
+ exit(e); \
+} while (0)
+
+#define logwarn(...) \
+ log_or_warn(LOG_DAEMON | LOG_WARNING, errno, __VA_ARGS__)
+
+#define logwarnx(...) \
+ log_or_warn(LOG_DAEMON | LOG_WARNING, 0, __VA_ARGS__)
+
+/* structures */
+
+/* symbol table entry */
+typedef struct {
+ const char *name;
+ int val;
+ int val2;
+} symtab_t;
+
+/* serial PnP ID string */
+typedef struct {
+ int revision; /* PnP revision, 100 for 1.00 */
+ const char *eisaid; /* EISA ID including mfr ID and product ID */
+ char *serial; /* serial No, optional */
+ const char *class; /* device class, optional */
+ char *compat; /* list of compatible drivers, optional */
+ char *description; /* product description, optional */
+ int neisaid; /* length of the above fields... */
+ int nserial;
+ int nclass;
+ int ncompat;
+ int ndescription;
+} pnpid_t;
+
+/* global variables */
+
+int debug = 0;
+int nodaemon = FALSE;
+int background = FALSE;
+int paused = FALSE;
+int identify = ID_NONE;
+int extioctl = FALSE;
+const char *pidfile = "/var/run/moused.pid";
+struct pidfh *pfh;
+
+#define SCROLL_NOTSCROLLING 0
+#define SCROLL_PREPARE 1
+#define SCROLL_SCROLLING 2
+
+static int scroll_state;
+static int scroll_movement;
+static int hscroll_movement;
+
+/* local variables */
+
+/* interface (the table must be ordered by MOUSE_IF_XXX in mouse.h) */
+static symtab_t rifs[] = {
+ { "serial", MOUSE_IF_SERIAL, 0 },
+ { "bus", MOUSE_IF_BUS, 0 },
+ { "inport", MOUSE_IF_INPORT, 0 },
+ { "ps/2", MOUSE_IF_PS2, 0 },
+ { "sysmouse", MOUSE_IF_SYSMOUSE, 0 },
+ { "usb", MOUSE_IF_USB, 0 },
+ { NULL, MOUSE_IF_UNKNOWN, 0 },
+};
+
+/* types (the table must be ordered by MOUSE_PROTO_XXX in mouse.h) */
+static const char *rnames[] = {
+ "microsoft",
+ "mousesystems",
+ "logitech",
+ "mmseries",
+ "mouseman",
+ "busmouse",
+ "inportmouse",
+ "ps/2",
+ "mmhitab",
+ "glidepoint",
+ "intellimouse",
+ "thinkingmouse",
+ "sysmouse",
+ "x10mouseremote",
+ "kidspad",
+ "versapad",
+ "jogdial",
+#if notyet
+ "mariqua",
+#endif
+ "gtco_digipad",
+ NULL
+};
+
+/* models */
+static symtab_t rmodels[] = {
+ { "NetScroll", MOUSE_MODEL_NETSCROLL, 0 },
+ { "NetMouse/NetScroll Optical", MOUSE_MODEL_NET, 0 },
+ { "GlidePoint", MOUSE_MODEL_GLIDEPOINT, 0 },
+ { "ThinkingMouse", MOUSE_MODEL_THINK, 0 },
+ { "IntelliMouse", MOUSE_MODEL_INTELLI, 0 },
+ { "EasyScroll/SmartScroll", MOUSE_MODEL_EASYSCROLL, 0 },
+ { "MouseMan+", MOUSE_MODEL_MOUSEMANPLUS, 0 },
+ { "Kidspad", MOUSE_MODEL_KIDSPAD, 0 },
+ { "VersaPad", MOUSE_MODEL_VERSAPAD, 0 },
+ { "IntelliMouse Explorer", MOUSE_MODEL_EXPLORER, 0 },
+ { "4D Mouse", MOUSE_MODEL_4D, 0 },
+ { "4D+ Mouse", MOUSE_MODEL_4DPLUS, 0 },
+ { "Synaptics Touchpad", MOUSE_MODEL_SYNAPTICS, 0 },
+ { "generic", MOUSE_MODEL_GENERIC, 0 },
+ { NULL, MOUSE_MODEL_UNKNOWN, 0 },
+};
+
+/* PnP EISA/product IDs */
+static symtab_t pnpprod[] = {
+ /* Kensignton ThinkingMouse */
+ { "KML0001", MOUSE_PROTO_THINK, MOUSE_MODEL_THINK },
+ /* MS IntelliMouse */
+ { "MSH0001", MOUSE_PROTO_INTELLI, MOUSE_MODEL_INTELLI },
+ /* MS IntelliMouse TrackBall */
+ { "MSH0004", MOUSE_PROTO_INTELLI, MOUSE_MODEL_INTELLI },
+ /* Tremon Wheel Mouse MUSD */
+ { "HTK0001", MOUSE_PROTO_INTELLI, MOUSE_MODEL_INTELLI },
+ /* Genius PnP Mouse */
+ { "KYE0001", MOUSE_PROTO_MS, MOUSE_MODEL_GENERIC },
+ /* MouseSystems SmartScroll Mouse (OEM from Genius?) */
+ { "KYE0002", MOUSE_PROTO_MS, MOUSE_MODEL_EASYSCROLL },
+ /* Genius NetMouse */
+ { "KYE0003", MOUSE_PROTO_INTELLI, MOUSE_MODEL_NET },
+ /* Genius Kidspad, Easypad and other tablets */
+ { "KYE0005", MOUSE_PROTO_KIDSPAD, MOUSE_MODEL_KIDSPAD },
+ /* Genius EZScroll */
+ { "KYEEZ00", MOUSE_PROTO_MS, MOUSE_MODEL_EASYSCROLL },
+ /* Logitech Cordless MouseMan Wheel */
+ { "LGI8033", MOUSE_PROTO_INTELLI, MOUSE_MODEL_MOUSEMANPLUS },
+ /* Logitech MouseMan (new 4 button model) */
+ { "LGI800C", MOUSE_PROTO_INTELLI, MOUSE_MODEL_MOUSEMANPLUS },
+ /* Logitech MouseMan+ */
+ { "LGI8050", MOUSE_PROTO_INTELLI, MOUSE_MODEL_MOUSEMANPLUS },
+ /* Logitech FirstMouse+ */
+ { "LGI8051", MOUSE_PROTO_INTELLI, MOUSE_MODEL_MOUSEMANPLUS },
+ /* Logitech serial */
+ { "LGI8001", MOUSE_PROTO_LOGIMOUSEMAN, MOUSE_MODEL_GENERIC },
+ /* A4 Tech 4D/4D+ Mouse */
+ { "A4W0005", MOUSE_PROTO_INTELLI, MOUSE_MODEL_4D },
+ /* 8D Scroll Mouse */
+ { "PEC9802", MOUSE_PROTO_INTELLI, MOUSE_MODEL_INTELLI },
+ /* Mitsumi Wireless Scroll Mouse */
+ { "MTM6401", MOUSE_PROTO_INTELLI, MOUSE_MODEL_INTELLI },
+
+ /* MS bus */
+ { "PNP0F00", MOUSE_PROTO_BUS, MOUSE_MODEL_GENERIC },
+ /* MS serial */
+ { "PNP0F01", MOUSE_PROTO_MS, MOUSE_MODEL_GENERIC },
+ /* MS InPort */
+ { "PNP0F02", MOUSE_PROTO_INPORT, MOUSE_MODEL_GENERIC },
+ /* MS PS/2 */
+ { "PNP0F03", MOUSE_PROTO_PS2, MOUSE_MODEL_GENERIC },
+ /*
+ * EzScroll returns PNP0F04 in the compatible device field; but it
+ * doesn't look compatible... XXX
+ */
+ /* MouseSystems */
+ { "PNP0F04", MOUSE_PROTO_MSC, MOUSE_MODEL_GENERIC },
+ /* MouseSystems */
+ { "PNP0F05", MOUSE_PROTO_MSC, MOUSE_MODEL_GENERIC },
+#if notyet
+ /* Genius Mouse */
+ { "PNP0F06", MOUSE_PROTO_XXX, MOUSE_MODEL_GENERIC },
+ /* Genius Mouse */
+ { "PNP0F07", MOUSE_PROTO_XXX, MOUSE_MODEL_GENERIC },
+#endif
+ /* Logitech serial */
+ { "PNP0F08", MOUSE_PROTO_LOGIMOUSEMAN, MOUSE_MODEL_GENERIC },
+ /* MS BallPoint serial */
+ { "PNP0F09", MOUSE_PROTO_MS, MOUSE_MODEL_GENERIC },
+ /* MS PnP serial */
+ { "PNP0F0A", MOUSE_PROTO_MS, MOUSE_MODEL_GENERIC },
+ /* MS PnP BallPoint serial */
+ { "PNP0F0B", MOUSE_PROTO_MS, MOUSE_MODEL_GENERIC },
+ /* MS serial comatible */
+ { "PNP0F0C", MOUSE_PROTO_MS, MOUSE_MODEL_GENERIC },
+ /* MS InPort comatible */
+ { "PNP0F0D", MOUSE_PROTO_INPORT, MOUSE_MODEL_GENERIC },
+ /* MS PS/2 comatible */
+ { "PNP0F0E", MOUSE_PROTO_PS2, MOUSE_MODEL_GENERIC },
+ /* MS BallPoint comatible */
+ { "PNP0F0F", MOUSE_PROTO_MS, MOUSE_MODEL_GENERIC },
+#if notyet
+ /* TI QuickPort */
+ { "PNP0F10", MOUSE_PROTO_XXX, MOUSE_MODEL_GENERIC },
+#endif
+ /* MS bus comatible */
+ { "PNP0F11", MOUSE_PROTO_BUS, MOUSE_MODEL_GENERIC },
+ /* Logitech PS/2 */
+ { "PNP0F12", MOUSE_PROTO_PS2, MOUSE_MODEL_GENERIC },
+ /* PS/2 */
+ { "PNP0F13", MOUSE_PROTO_PS2, MOUSE_MODEL_GENERIC },
+#if notyet
+ /* MS Kids Mouse */
+ { "PNP0F14", MOUSE_PROTO_XXX, MOUSE_MODEL_GENERIC },
+#endif
+ /* Logitech bus */
+ { "PNP0F15", MOUSE_PROTO_BUS, MOUSE_MODEL_GENERIC },
+#if notyet
+ /* Logitech SWIFT */
+ { "PNP0F16", MOUSE_PROTO_XXX, MOUSE_MODEL_GENERIC },
+#endif
+ /* Logitech serial compat */
+ { "PNP0F17", MOUSE_PROTO_LOGIMOUSEMAN, MOUSE_MODEL_GENERIC },
+ /* Logitech bus compatible */
+ { "PNP0F18", MOUSE_PROTO_BUS, MOUSE_MODEL_GENERIC },
+ /* Logitech PS/2 compatible */
+ { "PNP0F19", MOUSE_PROTO_PS2, MOUSE_MODEL_GENERIC },
+#if notyet
+ /* Logitech SWIFT compatible */
+ { "PNP0F1A", MOUSE_PROTO_XXX, MOUSE_MODEL_GENERIC },
+ /* HP Omnibook */
+ { "PNP0F1B", MOUSE_PROTO_XXX, MOUSE_MODEL_GENERIC },
+ /* Compaq LTE TrackBall PS/2 */
+ { "PNP0F1C", MOUSE_PROTO_XXX, MOUSE_MODEL_GENERIC },
+ /* Compaq LTE TrackBall serial */
+ { "PNP0F1D", MOUSE_PROTO_XXX, MOUSE_MODEL_GENERIC },
+ /* MS Kidts Trackball */
+ { "PNP0F1E", MOUSE_PROTO_XXX, MOUSE_MODEL_GENERIC },
+#endif
+ /* Interlink VersaPad */
+ { "LNK0001", MOUSE_PROTO_VERSAPAD, MOUSE_MODEL_VERSAPAD },
+
+ { NULL, MOUSE_PROTO_UNKNOWN, MOUSE_MODEL_GENERIC },
+};
+
+/* the table must be ordered by MOUSE_PROTO_XXX in mouse.h */
+static unsigned short rodentcflags[] =
+{
+ (CS7 | CREAD | CLOCAL | HUPCL), /* MicroSoft */
+ (CS8 | CSTOPB | CREAD | CLOCAL | HUPCL), /* MouseSystems */
+ (CS8 | CSTOPB | CREAD | CLOCAL | HUPCL), /* Logitech */
+ (CS8 | PARENB | PARODD | CREAD | CLOCAL | HUPCL), /* MMSeries */
+ (CS7 | CREAD | CLOCAL | HUPCL), /* MouseMan */
+ 0, /* Bus */
+ 0, /* InPort */
+ 0, /* PS/2 */
+ (CS8 | CREAD | CLOCAL | HUPCL), /* MM HitTablet */
+ (CS7 | CREAD | CLOCAL | HUPCL), /* GlidePoint */
+ (CS7 | CREAD | CLOCAL | HUPCL), /* IntelliMouse */
+ (CS7 | CREAD | CLOCAL | HUPCL), /* Thinking Mouse */
+ (CS8 | CSTOPB | CREAD | CLOCAL | HUPCL), /* sysmouse */
+ (CS7 | CREAD | CLOCAL | HUPCL), /* X10 MouseRemote */
+ (CS8 | PARENB | PARODD | CREAD | CLOCAL | HUPCL), /* kidspad etc. */
+ (CS8 | CREAD | CLOCAL | HUPCL), /* VersaPad */
+ 0, /* JogDial */
+#if notyet
+ (CS8 | CSTOPB | CREAD | CLOCAL | HUPCL), /* Mariqua */
+#endif
+ (CS8 | CREAD | HUPCL ), /* GTCO Digi-Pad */
+};
+
+static struct rodentparam {
+ int flags;
+ const char *portname; /* /dev/XXX */
+ int rtype; /* MOUSE_PROTO_XXX */
+ int level; /* operation level: 0 or greater */
+ int baudrate;
+ int rate; /* report rate */
+ int resolution; /* MOUSE_RES_XXX or a positive number */
+ int zmap[4]; /* MOUSE_{X|Y}AXIS or a button number */
+ int wmode; /* wheel mode button number */
+ int mfd; /* mouse file descriptor */
+ int cfd; /* /dev/consolectl file descriptor */
+ int mremsfd; /* mouse remote server file descriptor */
+ int mremcfd; /* mouse remote client file descriptor */
+ long clickthreshold; /* double click speed in msec */
+ long button2timeout; /* 3 button emulation timeout */
+ mousehw_t hw; /* mouse device hardware information */
+ mousemode_t mode; /* protocol information */
+ float accelx; /* Acceleration in the X axis */
+ float accely; /* Acceleration in the Y axis */
+ float expoaccel; /* Exponential acceleration */
+ float expoffset; /* Movement offset for exponential accel. */
+ float remainx; /* Remainder on X and Y axis, respectively... */
+ float remainy; /* ... to compensate for rounding errors. */
+ int scrollthreshold; /* Movement distance before virtual scrolling */
+ int scrollspeed; /* Movement distance to rate of scrolling */
+} rodent = {
+ .flags = 0,
+ .portname = NULL,
+ .rtype = MOUSE_PROTO_UNKNOWN,
+ .level = -1,
+ .baudrate = 1200,
+ .rate = 0,
+ .resolution = MOUSE_RES_UNKNOWN,
+ .zmap = { 0, 0, 0, 0 },
+ .wmode = 0,
+ .mfd = -1,
+ .cfd = -1,
+ .mremsfd = -1,
+ .mremcfd = -1,
+ .clickthreshold = DFLT_CLICKTHRESHOLD,
+ .button2timeout = DFLT_BUTTON2TIMEOUT,
+ .accelx = 1.0,
+ .accely = 1.0,
+ .expoaccel = 1.0,
+ .expoffset = 1.0,
+ .remainx = 0.0,
+ .remainy = 0.0,
+ .scrollthreshold = DFLT_SCROLLTHRESHOLD,
+ .scrollspeed = DFLT_SCROLLSPEED,
+};
+
+/* button status */
+struct button_state {
+ int count; /* 0: up, 1: single click, 2: double click,... */
+ struct timespec ts; /* timestamp on the last button event */
+};
+static struct button_state bstate[MOUSE_MAXBUTTON]; /* button state */
+static struct button_state *mstate[MOUSE_MAXBUTTON];/* mapped button st.*/
+static struct button_state zstate[4]; /* Z/W axis state */
+
+/* state machine for 3 button emulation */
+
+#define S0 0 /* start */
+#define S1 1 /* button 1 delayed down */
+#define S2 2 /* button 3 delayed down */
+#define S3 3 /* both buttons down -> button 2 down */
+#define S4 4 /* button 1 delayed up */
+#define S5 5 /* button 1 down */
+#define S6 6 /* button 3 down */
+#define S7 7 /* both buttons down */
+#define S8 8 /* button 3 delayed up */
+#define S9 9 /* button 1 or 3 up after S3 */
+
+#define A(b1, b3) (((b1) ? 2 : 0) | ((b3) ? 1 : 0))
+#define A_TIMEOUT 4
+#define S_DELAYED(st) (states[st].s[A_TIMEOUT] != (st))
+
+static struct {
+ int s[A_TIMEOUT + 1];
+ int buttons;
+ int mask;
+ int timeout;
+} states[10] = {
+ /* S0 */
+ { { S0, S2, S1, S3, S0 }, 0, ~(MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN), FALSE },
+ /* S1 */
+ { { S4, S2, S1, S3, S5 }, 0, ~MOUSE_BUTTON1DOWN, FALSE },
+ /* S2 */
+ { { S8, S2, S1, S3, S6 }, 0, ~MOUSE_BUTTON3DOWN, FALSE },
+ /* S3 */
+ { { S0, S9, S9, S3, S3 }, MOUSE_BUTTON2DOWN, ~0, FALSE },
+ /* S4 */
+ { { S0, S2, S1, S3, S0 }, MOUSE_BUTTON1DOWN, ~0, TRUE },
+ /* S5 */
+ { { S0, S2, S5, S7, S5 }, MOUSE_BUTTON1DOWN, ~0, FALSE },
+ /* S6 */
+ { { S0, S6, S1, S7, S6 }, MOUSE_BUTTON3DOWN, ~0, FALSE },
+ /* S7 */
+ { { S0, S6, S5, S7, S7 }, MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN, ~0, FALSE },
+ /* S8 */
+ { { S0, S2, S1, S3, S0 }, MOUSE_BUTTON3DOWN, ~0, TRUE },
+ /* S9 */
+ { { S0, S9, S9, S3, S9 }, 0, ~(MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN), FALSE },
+};
+static int mouse_button_state;
+static struct timespec mouse_button_state_ts;
+static int mouse_move_delayed;
+
+static jmp_buf env;
+
+struct drift_xy {
+ int x;
+ int y;
+};
+static int drift_distance = 4; /* max steps X+Y */
+static int drift_time = 500; /* in 0.5 sec */
+static struct timespec drift_time_ts;
+static struct timespec drift_2time_ts; /* 2*drift_time */
+static int drift_after = 4000; /* 4 sec */
+static struct timespec drift_after_ts;
+static int drift_terminate = FALSE;
+static struct timespec drift_current_ts;
+static struct timespec drift_tmp;
+static struct timespec drift_last_activity = {0, 0};
+static struct timespec drift_since = {0, 0};
+static struct drift_xy drift_last = {0, 0}; /* steps in last drift_time */
+static struct drift_xy drift_previous = {0, 0}; /* steps in prev. drift_time */
+
+/* function prototypes */
+
+static void linacc(int, int, int*, int*);
+static void expoacc(int, int, int*, int*);
+static void moused(void);
+static void hup(int sig);
+static void cleanup(int sig);
+static void pause_mouse(int sig);
+static void usage(void);
+static void log_or_warn(int log_pri, int errnum, const char *fmt, ...)
+ __printflike(3, 4);
+
+static int r_identify(void);
+static const char *r_if(int type);
+static const char *r_name(int type);
+static const char *r_model(int model);
+static void r_init(void);
+static int r_protocol(u_char b, mousestatus_t *act);
+static int r_statetrans(mousestatus_t *a1, mousestatus_t *a2, int trans);
+static int r_installmap(char *arg);
+static void r_map(mousestatus_t *act1, mousestatus_t *act2);
+static void r_timestamp(mousestatus_t *act);
+static int r_timeout(void);
+static void r_click(mousestatus_t *act);
+static void setmousespeed(int old, int new, unsigned cflag);
+
+static int pnpwakeup1(void);
+static int pnpwakeup2(void);
+static int pnpgets(char *buf);
+static int pnpparse(pnpid_t *id, char *buf, int len);
+static symtab_t *pnpproto(pnpid_t *id);
+
+static symtab_t *gettoken(symtab_t *tab, const char *s, int len);
+static const char *gettokenname(symtab_t *tab, int val);
+
+static void mremote_serversetup(void);
+static void mremote_clientchg(int add);
+
+static int kidspad(u_char rxc, mousestatus_t *act);
+static int gtco_digipad(u_char, mousestatus_t *);
+
+static int usbmodule(void);
+
+int
+main(int argc, char *argv[])
+{
+ int c;
+ int i;
+ int j;
+ static int retry;
+
+ for (i = 0; i < MOUSE_MAXBUTTON; ++i)
+ mstate[i] = &bstate[i];
+
+ while ((c = getopt(argc, argv, "3A:C:DE:F:HI:L:PRS:T:VU:a:cdfhi:l:m:p:r:st:w:z:")) != -1)
+ switch(c) {
+
+ case '3':
+ rodent.flags |= Emulate3Button;
+ break;
+
+ case 'E':
+ rodent.button2timeout = atoi(optarg);
+ if ((rodent.button2timeout < 0) ||
+ (rodent.button2timeout > MAX_BUTTON2TIMEOUT)) {
+ warnx("invalid argument `%s'", optarg);
+ usage();
+ }
+ break;
+
+ case 'a':
+ i = sscanf(optarg, "%f,%f", &rodent.accelx, &rodent.accely);
+ if (i == 0) {
+ warnx("invalid linear acceleration argument '%s'", optarg);
+ usage();
+ }
+
+ if (i == 1)
+ rodent.accely = rodent.accelx;
+
+ break;
+
+ case 'A':
+ rodent.flags |= ExponentialAcc;
+ i = sscanf(optarg, "%f,%f", &rodent.expoaccel, &rodent.expoffset);
+ if (i == 0) {
+ warnx("invalid exponential acceleration argument '%s'", optarg);
+ usage();
+ }
+
+ if (i == 1)
+ rodent.expoffset = 1.0;
+
+ break;
+
+ case 'c':
+ rodent.flags |= ChordMiddle;
+ break;
+
+ case 'd':
+ ++debug;
+ break;
+
+ case 'f':
+ nodaemon = TRUE;
+ break;
+
+ case 'i':
+ if (strcmp(optarg, "all") == 0)
+ identify = ID_ALL;
+ else if (strcmp(optarg, "port") == 0)
+ identify = ID_PORT;
+ else if (strcmp(optarg, "if") == 0)
+ identify = ID_IF;
+ else if (strcmp(optarg, "type") == 0)
+ identify = ID_TYPE;
+ else if (strcmp(optarg, "model") == 0)
+ identify = ID_MODEL;
+ else {
+ warnx("invalid argument `%s'", optarg);
+ usage();
+ }
+ nodaemon = TRUE;
+ break;
+
+ case 'l':
+ rodent.level = atoi(optarg);
+ if ((rodent.level < 0) || (rodent.level > 4)) {
+ warnx("invalid argument `%s'", optarg);
+ usage();
+ }
+ break;
+
+ case 'm':
+ if (!r_installmap(optarg)) {
+ warnx("invalid argument `%s'", optarg);
+ usage();
+ }
+ break;
+
+ case 'p':
+ rodent.portname = optarg;
+ break;
+
+ case 'r':
+ if (strcmp(optarg, "high") == 0)
+ rodent.resolution = MOUSE_RES_HIGH;
+ else if (strcmp(optarg, "medium-high") == 0)
+ rodent.resolution = MOUSE_RES_HIGH;
+ else if (strcmp(optarg, "medium-low") == 0)
+ rodent.resolution = MOUSE_RES_MEDIUMLOW;
+ else if (strcmp(optarg, "low") == 0)
+ rodent.resolution = MOUSE_RES_LOW;
+ else if (strcmp(optarg, "default") == 0)
+ rodent.resolution = MOUSE_RES_DEFAULT;
+ else {
+ rodent.resolution = atoi(optarg);
+ if (rodent.resolution <= 0) {
+ warnx("invalid argument `%s'", optarg);
+ usage();
+ }
+ }
+ break;
+
+ case 's':
+ rodent.baudrate = 9600;
+ break;
+
+ case 'w':
+ i = atoi(optarg);
+ if ((i <= 0) || (i > MOUSE_MAXBUTTON)) {
+ warnx("invalid argument `%s'", optarg);
+ usage();
+ }
+ rodent.wmode = 1 << (i - 1);
+ break;
+
+ case 'z':
+ if (strcmp(optarg, "x") == 0)
+ rodent.zmap[0] = MOUSE_XAXIS;
+ else if (strcmp(optarg, "y") == 0)
+ rodent.zmap[0] = MOUSE_YAXIS;
+ else {
+ i = atoi(optarg);
+ /*
+ * Use button i for negative Z axis movement and
+ * button (i + 1) for positive Z axis movement.
+ */
+ if ((i <= 0) || (i > MOUSE_MAXBUTTON - 1)) {
+ warnx("invalid argument `%s'", optarg);
+ usage();
+ }
+ rodent.zmap[0] = i;
+ rodent.zmap[1] = i + 1;
+ debug("optind: %d, optarg: '%s'", optind, optarg);
+ for (j = 1; j < 4; ++j) {
+ if ((optind >= argc) || !isdigit(*argv[optind]))
+ break;
+ i = atoi(argv[optind]);
+ if ((i <= 0) || (i > MOUSE_MAXBUTTON - 1)) {
+ warnx("invalid argument `%s'", argv[optind]);
+ usage();
+ }
+ rodent.zmap[j] = i;
+ ++optind;
+ }
+ if ((rodent.zmap[2] != 0) && (rodent.zmap[3] == 0))
+ rodent.zmap[3] = rodent.zmap[2] + 1;
+ }
+ break;
+
+ case 'C':
+ rodent.clickthreshold = atoi(optarg);
+ if ((rodent.clickthreshold < 0) ||
+ (rodent.clickthreshold > MAX_CLICKTHRESHOLD)) {
+ warnx("invalid argument `%s'", optarg);
+ usage();
+ }
+ break;
+
+ case 'D':
+ rodent.flags |= ClearDTR;
+ break;
+
+ case 'F':
+ rodent.rate = atoi(optarg);
+ if (rodent.rate <= 0) {
+ warnx("invalid argument `%s'", optarg);
+ usage();
+ }
+ break;
+
+ case 'H':
+ rodent.flags |= HVirtualScroll;
+ break;
+
+ case 'I':
+ pidfile = optarg;
+ break;
+
+ case 'L':
+ rodent.scrollspeed = atoi(optarg);
+ if (rodent.scrollspeed < 0) {
+ warnx("invalid argument `%s'", optarg);
+ usage();
+ }
+ break;
+
+ case 'P':
+ rodent.flags |= NoPnP;
+ break;
+
+ case 'R':
+ rodent.flags |= ClearRTS;
+ break;
+
+ case 'S':
+ rodent.baudrate = atoi(optarg);
+ if (rodent.baudrate <= 0) {
+ warnx("invalid argument `%s'", optarg);
+ usage();
+ }
+ debug("rodent baudrate %d", rodent.baudrate);
+ break;
+
+ case 'T':
+ drift_terminate = TRUE;
+ sscanf(optarg, "%d,%d,%d", &drift_distance, &drift_time,
+ &drift_after);
+ if (drift_distance <= 0 || drift_time <= 0 || drift_after <= 0) {
+ warnx("invalid argument `%s'", optarg);
+ usage();
+ }
+ debug("terminate drift: distance %d, time %d, after %d",
+ drift_distance, drift_time, drift_after);
+ drift_time_ts.tv_sec = drift_time / 1000;
+ drift_time_ts.tv_nsec = (drift_time % 1000) * 1000000;
+ drift_2time_ts.tv_sec = (drift_time *= 2) / 1000;
+ drift_2time_ts.tv_nsec = (drift_time % 1000) * 1000000;
+ drift_after_ts.tv_sec = drift_after / 1000;
+ drift_after_ts.tv_nsec = (drift_after % 1000) * 1000000;
+ break;
+
+ case 't':
+ if (strcmp(optarg, "auto") == 0) {
+ rodent.rtype = MOUSE_PROTO_UNKNOWN;
+ rodent.flags &= ~NoPnP;
+ rodent.level = -1;
+ break;
+ }
+ for (i = 0; rnames[i] != NULL; i++)
+ if (strcmp(optarg, rnames[i]) == 0) {
+ rodent.rtype = i;
+ rodent.flags |= NoPnP;
+ rodent.level = (i == MOUSE_PROTO_SYSMOUSE) ? 1 : 0;
+ break;
+ }
+ if (rnames[i] == NULL) {
+ warnx("no such mouse type `%s'", optarg);
+ usage();
+ }
+ break;
+
+ case 'V':
+ rodent.flags |= VirtualScroll;
+ break;
+ case 'U':
+ rodent.scrollthreshold = atoi(optarg);
+ if (rodent.scrollthreshold < 0) {
+ warnx("invalid argument `%s'", optarg);
+ usage();
+ }
+ break;
+
+ case 'h':
+ case '?':
+ default:
+ usage();
+ }
+
+ /* fix Z axis mapping */
+ for (i = 0; i < 4; ++i) {
+ if (rodent.zmap[i] > 0) {
+ for (j = 0; j < MOUSE_MAXBUTTON; ++j) {
+ if (mstate[j] == &bstate[rodent.zmap[i] - 1])
+ mstate[j] = &zstate[i];
+ }
+ rodent.zmap[i] = 1 << (rodent.zmap[i] - 1);
+ }
+ }
+
+ /* the default port name */
+ switch(rodent.rtype) {
+
+ case MOUSE_PROTO_INPORT:
+ /* INPORT and BUS are the same... */
+ rodent.rtype = MOUSE_PROTO_BUS;
+ /* FALLTHROUGH */
+ case MOUSE_PROTO_BUS:
+ if (!rodent.portname)
+ rodent.portname = "/dev/mse0";
+ break;
+
+ case MOUSE_PROTO_PS2:
+ if (!rodent.portname)
+ rodent.portname = "/dev/psm0";
+ break;
+
+ default:
+ if (rodent.portname)
+ break;
+ warnx("no port name specified");
+ usage();
+ }
+
+ retry = 1;
+ if (strncmp(rodent.portname, "/dev/ums", 8) == 0) {
+ if (usbmodule() != 0)
+ retry = 5;
+ }
+
+ for (;;) {
+ if (setjmp(env) == 0) {
+ signal(SIGHUP, hup);
+ signal(SIGINT , cleanup);
+ signal(SIGQUIT, cleanup);
+ signal(SIGTERM, cleanup);
+ signal(SIGUSR1, pause_mouse);
+ for (i = 0; i < retry; ++i) {
+ if (i > 0)
+ sleep(2);
+ rodent.mfd = open(rodent.portname, O_RDWR | O_NONBLOCK);
+ if (rodent.mfd != -1 || errno != ENOENT)
+ break;
+ }
+ if (rodent.mfd == -1)
+ logerr(1, "unable to open %s", rodent.portname);
+ if (r_identify() == MOUSE_PROTO_UNKNOWN) {
+ logwarnx("cannot determine mouse type on %s", rodent.portname);
+ close(rodent.mfd);
+ rodent.mfd = -1;
+ }
+
+ /* print some information */
+ if (identify != ID_NONE) {
+ if (identify == ID_ALL)
+ printf("%s %s %s %s\n",
+ rodent.portname, r_if(rodent.hw.iftype),
+ r_name(rodent.rtype), r_model(rodent.hw.model));
+ else if (identify & ID_PORT)
+ printf("%s\n", rodent.portname);
+ else if (identify & ID_IF)
+ printf("%s\n", r_if(rodent.hw.iftype));
+ else if (identify & ID_TYPE)
+ printf("%s\n", r_name(rodent.rtype));
+ else if (identify & ID_MODEL)
+ printf("%s\n", r_model(rodent.hw.model));
+ exit(0);
+ } else {
+ debug("port: %s interface: %s type: %s model: %s",
+ rodent.portname, r_if(rodent.hw.iftype),
+ r_name(rodent.rtype), r_model(rodent.hw.model));
+ }
+
+ if (rodent.mfd == -1) {
+ /*
+ * We cannot continue because of error. Exit if the
+ * program has not become a daemon. Otherwise, block
+ * until the the user corrects the problem and issues SIGHUP.
+ */
+ if (!background)
+ exit(1);
+ sigpause(0);
+ }
+
+ r_init(); /* call init function */
+ moused();
+ }
+
+ if (rodent.mfd != -1)
+ close(rodent.mfd);
+ if (rodent.cfd != -1)
+ close(rodent.cfd);
+ rodent.mfd = rodent.cfd = -1;
+ }
+ /* NOT REACHED */
+
+ exit(0);
+}
+
+static int
+usbmodule(void)
+{
+ return (kld_isloaded("uhub/ums") || kld_load("ums") != -1);
+}
+
+/*
+ * Function to calculate linear acceleration.
+ *
+ * If there are any rounding errors, the remainder
+ * is stored in the remainx and remainy variables
+ * and taken into account upon the next movement.
+ */
+
+static void
+linacc(int dx, int dy, int *movex, int *movey)
+{
+ float fdx, fdy;
+
+ if (dx == 0 && dy == 0) {
+ *movex = *movey = 0;
+ return;
+ }
+ fdx = dx * rodent.accelx + rodent.remainx;
+ fdy = dy * rodent.accely + rodent.remainy;
+ *movex = lround(fdx);
+ *movey = lround(fdy);
+ rodent.remainx = fdx - *movex;
+ rodent.remainy = fdy - *movey;
+}
+
+/*
+ * Function to calculate exponential acceleration.
+ * (Also includes linear acceleration if enabled.)
+ *
+ * In order to give a smoother behaviour, we record the four
+ * most recent non-zero movements and use their average value
+ * to calculate the acceleration.
+ */
+
+static void
+expoacc(int dx, int dy, int *movex, int *movey)
+{
+ static float lastlength[3] = {0.0, 0.0, 0.0};
+ float fdx, fdy, length, lbase, accel;
+
+ if (dx == 0 && dy == 0) {
+ *movex = *movey = 0;
+ return;
+ }
+ fdx = dx * rodent.accelx;
+ fdy = dy * rodent.accely;
+ length = sqrtf((fdx * fdx) + (fdy * fdy)); /* Pythagoras */
+ length = (length + lastlength[0] + lastlength[1] + lastlength[2]) / 4;
+ lbase = length / rodent.expoffset;
+ accel = powf(lbase, rodent.expoaccel) / lbase;
+ fdx = fdx * accel + rodent.remainx;
+ fdy = fdy * accel + rodent.remainy;
+ *movex = lroundf(fdx);
+ *movey = lroundf(fdy);
+ rodent.remainx = fdx - *movex;
+ rodent.remainy = fdy - *movey;
+ lastlength[2] = lastlength[1];
+ lastlength[1] = lastlength[0];
+ lastlength[0] = length; /* Insert new average, not original length! */
+}
+
+static void
+moused(void)
+{
+ struct mouse_info mouse;
+ mousestatus_t action0; /* original mouse action */
+ mousestatus_t action; /* interrim buffer */
+ mousestatus_t action2; /* mapped action */
+ struct timeval timeout;
+ fd_set fds;
+ u_char b;
+ pid_t mpid;
+ int flags;
+ int c;
+ int i;
+
+ if ((rodent.cfd = open("/dev/consolectl", O_RDWR, 0)) == -1)
+ logerr(1, "cannot open /dev/consolectl");
+
+ if (!nodaemon && !background) {
+ pfh = pidfile_open(pidfile, 0600, &mpid);
+ if (pfh == NULL) {
+ if (errno == EEXIST)
+ logerrx(1, "moused already running, pid: %d", mpid);
+ logwarn("cannot open pid file");
+ }
+ if (daemon(0, 0)) {
+ int saved_errno = errno;
+ pidfile_remove(pfh);
+ errno = saved_errno;
+ logerr(1, "failed to become a daemon");
+ } else {
+ background = TRUE;
+ pidfile_write(pfh);
+ }
+ }
+
+ /* clear mouse data */
+ bzero(&action0, sizeof(action0));
+ bzero(&action, sizeof(action));
+ bzero(&action2, sizeof(action2));
+ bzero(&mouse, sizeof(mouse));
+ mouse_button_state = S0;
+ clock_gettime(CLOCK_MONOTONIC_FAST, &mouse_button_state_ts);
+ mouse_move_delayed = 0;
+ for (i = 0; i < MOUSE_MAXBUTTON; ++i) {
+ bstate[i].count = 0;
+ bstate[i].ts = mouse_button_state_ts;
+ }
+ for (i = 0; i < (int)(sizeof(zstate) / sizeof(zstate[0])); ++i) {
+ zstate[i].count = 0;
+ zstate[i].ts = mouse_button_state_ts;
+ }
+
+ /* choose which ioctl command to use */
+ mouse.operation = MOUSE_MOTION_EVENT;
+ extioctl = (ioctl(rodent.cfd, CONS_MOUSECTL, &mouse) == 0);
+
+ /* process mouse data */
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 20000; /* 20 msec */
+ for (;;) {
+
+ FD_ZERO(&fds);
+ FD_SET(rodent.mfd, &fds);
+ if (rodent.mremsfd >= 0)
+ FD_SET(rodent.mremsfd, &fds);
+ if (rodent.mremcfd >= 0)
+ FD_SET(rodent.mremcfd, &fds);
+
+ c = select(FD_SETSIZE, &fds, NULL, NULL,
+ (rodent.flags & Emulate3Button) ? &timeout : NULL);
+ if (c < 0) { /* error */
+ logwarn("failed to read from mouse");
+ continue;
+ } else if (c == 0) { /* timeout */
+ /* assert(rodent.flags & Emulate3Button) */
+ action0.button = action0.obutton;
+ action0.dx = action0.dy = action0.dz = 0;
+ action0.flags = flags = 0;
+ if (r_timeout() && r_statetrans(&action0, &action, A_TIMEOUT)) {
+ if (debug > 2)
+ debug("flags:%08x buttons:%08x obuttons:%08x",
+ action.flags, action.button, action.obutton);
+ } else {
+ action0.obutton = action0.button;
+ continue;
+ }
+ } else {
+ /* MouseRemote client connect/disconnect */
+ if ((rodent.mremsfd >= 0) && FD_ISSET(rodent.mremsfd, &fds)) {
+ mremote_clientchg(TRUE);
+ continue;
+ }
+ if ((rodent.mremcfd >= 0) && FD_ISSET(rodent.mremcfd, &fds)) {
+ mremote_clientchg(FALSE);
+ continue;
+ }
+ /* mouse movement */
+ if (read(rodent.mfd, &b, 1) == -1) {
+ if (errno == EWOULDBLOCK)
+ continue;
+ else
+ return;
+ }
+ if ((flags = r_protocol(b, &action0)) == 0)
+ continue;
+
+ if ((rodent.flags & VirtualScroll) || (rodent.flags & HVirtualScroll)) {
+ /* Allow middle button drags to scroll up and down */
+ if (action0.button == MOUSE_BUTTON2DOWN) {
+ if (scroll_state == SCROLL_NOTSCROLLING) {
+ scroll_state = SCROLL_PREPARE;
+ scroll_movement = hscroll_movement = 0;
+ debug("PREPARING TO SCROLL");
+ }
+ debug("[BUTTON2] flags:%08x buttons:%08x obuttons:%08x",
+ action.flags, action.button, action.obutton);
+ } else {
+ debug("[NOTBUTTON2] flags:%08x buttons:%08x obuttons:%08x",
+ action.flags, action.button, action.obutton);
+
+ /* This isn't a middle button down... move along... */
+ if (scroll_state == SCROLL_SCROLLING) {
+ /*
+ * We were scrolling, someone let go of button 2.
+ * Now turn autoscroll off.
+ */
+ scroll_state = SCROLL_NOTSCROLLING;
+ debug("DONE WITH SCROLLING / %d", scroll_state);
+ } else if (scroll_state == SCROLL_PREPARE) {
+ mousestatus_t newaction = action0;
+
+ /* We were preparing to scroll, but we never moved... */
+ r_timestamp(&action0);
+ r_statetrans(&action0, &newaction,
+ A(newaction.button & MOUSE_BUTTON1DOWN,
+ action0.button & MOUSE_BUTTON3DOWN));
+
+ /* Send middle down */
+ newaction.button = MOUSE_BUTTON2DOWN;
+ r_click(&newaction);
+
+ /* Send middle up */
+ r_timestamp(&newaction);
+ newaction.obutton = newaction.button;
+ newaction.button = action0.button;
+ r_click(&newaction);
+ }
+ }
+ }
+
+ r_timestamp(&action0);
+ r_statetrans(&action0, &action,
+ A(action0.button & MOUSE_BUTTON1DOWN,
+ action0.button & MOUSE_BUTTON3DOWN));
+ debug("flags:%08x buttons:%08x obuttons:%08x", action.flags,
+ action.button, action.obutton);
+ }
+ action0.obutton = action0.button;
+ flags &= MOUSE_POSCHANGED;
+ flags |= action.obutton ^ action.button;
+ action.flags = flags;
+
+ if (flags) { /* handler detected action */
+ r_map(&action, &action2);
+ debug("activity : buttons 0x%08x dx %d dy %d dz %d",
+ action2.button, action2.dx, action2.dy, action2.dz);
+
+ if ((rodent.flags & VirtualScroll) || (rodent.flags & HVirtualScroll)) {
+ /*
+ * If *only* the middle button is pressed AND we are moving
+ * the stick/trackpoint/nipple, scroll!
+ */
+ if (scroll_state == SCROLL_PREPARE) {
+ /* Middle button down, waiting for movement threshold */
+ if (action2.dy || action2.dx) {
+ if (rodent.flags & VirtualScroll) {
+ scroll_movement += action2.dy;
+ if (scroll_movement < -rodent.scrollthreshold) {
+ scroll_state = SCROLL_SCROLLING;
+ } else if (scroll_movement > rodent.scrollthreshold) {
+ scroll_state = SCROLL_SCROLLING;
+ }
+ }
+ if (rodent.flags & HVirtualScroll) {
+ hscroll_movement += action2.dx;
+ if (hscroll_movement < -rodent.scrollthreshold) {
+ scroll_state = SCROLL_SCROLLING;
+ } else if (hscroll_movement > rodent.scrollthreshold) {
+ scroll_state = SCROLL_SCROLLING;
+ }
+ }
+ if (scroll_state == SCROLL_SCROLLING) scroll_movement = hscroll_movement = 0;
+ }
+ } else if (scroll_state == SCROLL_SCROLLING) {
+ if (rodent.flags & VirtualScroll) {
+ scroll_movement += action2.dy;
+ debug("SCROLL: %d", scroll_movement);
+ if (scroll_movement < -rodent.scrollspeed) {
+ /* Scroll down */
+ action2.dz = -1;
+ scroll_movement = 0;
+ }
+ else if (scroll_movement > rodent.scrollspeed) {
+ /* Scroll up */
+ action2.dz = 1;
+ scroll_movement = 0;
+ }
+ }
+ if (rodent.flags & HVirtualScroll) {
+ hscroll_movement += action2.dx;
+ debug("HORIZONTAL SCROLL: %d", hscroll_movement);
+
+ if (hscroll_movement < -rodent.scrollspeed) {
+ action2.dz = -2;
+ hscroll_movement = 0;
+ }
+ else if (hscroll_movement > rodent.scrollspeed) {
+ action2.dz = 2;
+ hscroll_movement = 0;
+ }
+ }
+
+ /* Don't move while scrolling */
+ action2.dx = action2.dy = 0;
+ }
+ }
+
+ if (drift_terminate) {
+ if ((flags & MOUSE_POSCHANGED) == 0 || action.dz || action2.dz)
+ drift_last_activity = drift_current_ts;
+ else {
+ /* X or/and Y movement only - possibly drift */
+ tssub(&drift_current_ts, &drift_last_activity, &drift_tmp);
+ if (tscmp(&drift_tmp, &drift_after_ts, >)) {
+ tssub(&drift_current_ts, &drift_since, &drift_tmp);
+ if (tscmp(&drift_tmp, &drift_time_ts, <)) {
+ drift_last.x += action2.dx;
+ drift_last.y += action2.dy;
+ } else {
+ /* discard old accumulated steps (drift) */
+ if (tscmp(&drift_tmp, &drift_2time_ts, >))
+ drift_previous.x = drift_previous.y = 0;
+ else
+ drift_previous = drift_last;
+ drift_last.x = action2.dx;
+ drift_last.y = action2.dy;
+ drift_since = drift_current_ts;
+ }
+ if (abs(drift_last.x) + abs(drift_last.y)
+ > drift_distance) {
+ /* real movement, pass all accumulated steps */
+ action2.dx = drift_previous.x + drift_last.x;
+ action2.dy = drift_previous.y + drift_last.y;
+ /* and reset accumulators */
+ tsclr(&drift_since);
+ drift_last.x = drift_last.y = 0;
+ /* drift_previous will be cleared at next movement*/
+ drift_last_activity = drift_current_ts;
+ } else {
+ continue; /* don't pass current movement to
+ * console driver */
+ }
+ }
+ }
+ }
+
+ if (extioctl) {
+ /* Defer clicks until we aren't VirtualScroll'ing. */
+ if (scroll_state == SCROLL_NOTSCROLLING)
+ r_click(&action2);
+
+ if (action2.flags & MOUSE_POSCHANGED) {
+ mouse.operation = MOUSE_MOTION_EVENT;
+ mouse.u.data.buttons = action2.button;
+ if (rodent.flags & ExponentialAcc) {
+ expoacc(action2.dx, action2.dy,
+ &mouse.u.data.x, &mouse.u.data.y);
+ }
+ else {
+ linacc(action2.dx, action2.dy,
+ &mouse.u.data.x, &mouse.u.data.y);
+ }
+ mouse.u.data.z = action2.dz;
+ if (debug < 2)
+ if (!paused)
+ ioctl(rodent.cfd, CONS_MOUSECTL, &mouse);
+ }
+ } else {
+ mouse.operation = MOUSE_ACTION;
+ mouse.u.data.buttons = action2.button;
+ if (rodent.flags & ExponentialAcc) {
+ expoacc(action2.dx, action2.dy,
+ &mouse.u.data.x, &mouse.u.data.y);
+ }
+ else {
+ linacc(action2.dx, action2.dy,
+ &mouse.u.data.x, &mouse.u.data.y);
+ }
+ mouse.u.data.z = action2.dz;
+ if (debug < 2)
+ if (!paused)
+ ioctl(rodent.cfd, CONS_MOUSECTL, &mouse);
+ }
+
+ /*
+ * If the Z axis movement is mapped to an imaginary physical
+ * button, we need to cook up a corresponding button `up' event
+ * after sending a button `down' event.
+ */
+ if ((rodent.zmap[0] > 0) && (action.dz != 0)) {
+ action.obutton = action.button;
+ action.dx = action.dy = action.dz = 0;
+ r_map(&action, &action2);
+ debug("activity : buttons 0x%08x dx %d dy %d dz %d",
+ action2.button, action2.dx, action2.dy, action2.dz);
+
+ if (extioctl) {
+ r_click(&action2);
+ } else {
+ mouse.operation = MOUSE_ACTION;
+ mouse.u.data.buttons = action2.button;
+ mouse.u.data.x = mouse.u.data.y = mouse.u.data.z = 0;
+ if (debug < 2)
+ if (!paused)
+ ioctl(rodent.cfd, CONS_MOUSECTL, &mouse);
+ }
+ }
+ }
+ }
+ /* NOT REACHED */
+}
+
+static void
+hup(__unused int sig)
+{
+ longjmp(env, 1);
+}
+
+static void
+cleanup(__unused int sig)
+{
+ if (rodent.rtype == MOUSE_PROTO_X10MOUSEREM)
+ unlink(_PATH_MOUSEREMOTE);
+ exit(0);
+}
+
+static void
+pause_mouse(__unused int sig)
+{
+ paused = !paused;
+}
+
+/**
+ ** usage
+ **
+ ** Complain, and free the CPU for more worthy tasks
+ **/
+static void
+usage(void)
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
+ "usage: moused [-DRcdfs] [-I file] [-F rate] [-r resolution] [-S baudrate]",
+ " [-VH [-U threshold]] [-a X[,Y]] [-C threshold] [-m N=M] [-w N]",
+ " [-z N] [-t <mousetype>] [-l level] [-3 [-E timeout]]",
+ " [-T distance[,time[,after]]] -p <port>",
+ " moused [-d] -i <port|if|type|model|all> -p <port>");
+ exit(1);
+}
+
+/*
+ * Output an error message to syslog or stderr as appropriate. If
+ * `errnum' is non-zero, append its string form to the message.
+ */
+static void
+log_or_warn(int log_pri, int errnum, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[256];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ if (errnum) {
+ strlcat(buf, ": ", sizeof(buf));
+ strlcat(buf, strerror(errnum), sizeof(buf));
+ }
+
+ if (background)
+ syslog(log_pri, "%s", buf);
+ else
+ warnx("%s", buf);
+}
+
+/**
+ ** Mouse interface code, courtesy of XFree86 3.1.2.
+ **
+ ** Note: Various bits have been trimmed, and in my shortsighted enthusiasm
+ ** to clean, reformat and rationalise naming, it's quite possible that
+ ** some things in here have been broken.
+ **
+ ** I hope not 8)
+ **
+ ** The following code is derived from a module marked :
+ **/
+
+/* $XConsortium: xf86_Mouse.c,v 1.2 94/10/12 20:33:21 kaleb Exp $ */
+/* $XFree86: xc/programs/Xserver/hw/xfree86/common/xf86_Mouse.c,v 3.2 1995/01/28
+ 17:03:40 dawes Exp $ */
+/*
+ *
+ * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany.
+ * Copyright 1993 by David Dawes <dawes@physics.su.oz.au>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the names of Thomas Roell and David Dawes not be
+ * used in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. Thomas Roell
+ * and David Dawes makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * THOMAS ROELL AND DAVID DAWES DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THOMAS ROELL OR DAVID DAWES BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+/**
+ ** GlidePoint support from XFree86 3.2.
+ ** Derived from the module:
+ **/
+
+/* $XFree86: xc/programs/Xserver/hw/xfree86/common/xf86_Mouse.c,v 3.19 1996/10/16 14:40:51 dawes Exp $ */
+/* $XConsortium: xf86_Mouse.c /main/10 1996/01/30 15:16:12 kaleb $ */
+
+/* the following table must be ordered by MOUSE_PROTO_XXX in mouse.h */
+static unsigned char proto[][7] = {
+ /* hd_mask hd_id dp_mask dp_id bytes b4_mask b4_id */
+ { 0x40, 0x40, 0x40, 0x00, 3, ~0x23, 0x00 }, /* MicroSoft */
+ { 0xf8, 0x80, 0x00, 0x00, 5, 0x00, 0xff }, /* MouseSystems */
+ { 0xe0, 0x80, 0x80, 0x00, 3, 0x00, 0xff }, /* Logitech */
+ { 0xe0, 0x80, 0x80, 0x00, 3, 0x00, 0xff }, /* MMSeries */
+ { 0x40, 0x40, 0x40, 0x00, 3, ~0x33, 0x00 }, /* MouseMan */
+ { 0xf8, 0x80, 0x00, 0x00, 5, 0x00, 0xff }, /* Bus */
+ { 0xf8, 0x80, 0x00, 0x00, 5, 0x00, 0xff }, /* InPort */
+ { 0xc0, 0x00, 0x00, 0x00, 3, 0x00, 0xff }, /* PS/2 mouse */
+ { 0xe0, 0x80, 0x80, 0x00, 3, 0x00, 0xff }, /* MM HitTablet */
+ { 0x40, 0x40, 0x40, 0x00, 3, ~0x33, 0x00 }, /* GlidePoint */
+ { 0x40, 0x40, 0x40, 0x00, 3, ~0x3f, 0x00 }, /* IntelliMouse */
+ { 0x40, 0x40, 0x40, 0x00, 3, ~0x33, 0x00 }, /* ThinkingMouse */
+ { 0xf8, 0x80, 0x00, 0x00, 5, 0x00, 0xff }, /* sysmouse */
+ { 0x40, 0x40, 0x40, 0x00, 3, ~0x23, 0x00 }, /* X10 MouseRem */
+ { 0x80, 0x80, 0x00, 0x00, 5, 0x00, 0xff }, /* KIDSPAD */
+ { 0xc3, 0xc0, 0x00, 0x00, 6, 0x00, 0xff }, /* VersaPad */
+ { 0x00, 0x00, 0x00, 0x00, 1, 0x00, 0xff }, /* JogDial */
+#if notyet
+ { 0xf8, 0x80, 0x00, 0x00, 5, ~0x2f, 0x10 }, /* Mariqua */
+#endif
+};
+static unsigned char cur_proto[7];
+
+static int
+r_identify(void)
+{
+ char pnpbuf[256]; /* PnP identifier string may be up to 256 bytes long */
+ pnpid_t pnpid;
+ symtab_t *t;
+ int level;
+ int len;
+
+ /* set the driver operation level, if applicable */
+ if (rodent.level < 0)
+ rodent.level = 1;
+ ioctl(rodent.mfd, MOUSE_SETLEVEL, &rodent.level);
+ rodent.level = (ioctl(rodent.mfd, MOUSE_GETLEVEL, &level) == 0) ? level : 0;
+
+ /*
+ * Interrogate the driver and get some intelligence on the device...
+ * The following ioctl functions are not always supported by device
+ * drivers. When the driver doesn't support them, we just trust the
+ * user to supply valid information.
+ */
+ rodent.hw.iftype = MOUSE_IF_UNKNOWN;
+ rodent.hw.model = MOUSE_MODEL_GENERIC;
+ ioctl(rodent.mfd, MOUSE_GETHWINFO, &rodent.hw);
+
+ if (rodent.rtype != MOUSE_PROTO_UNKNOWN)
+ bcopy(proto[rodent.rtype], cur_proto, sizeof(cur_proto));
+ rodent.mode.protocol = MOUSE_PROTO_UNKNOWN;
+ rodent.mode.rate = -1;
+ rodent.mode.resolution = MOUSE_RES_UNKNOWN;
+ rodent.mode.accelfactor = 0;
+ rodent.mode.level = 0;
+ if (ioctl(rodent.mfd, MOUSE_GETMODE, &rodent.mode) == 0) {
+ if (rodent.mode.protocol == MOUSE_PROTO_UNKNOWN ||
+ rodent.mode.protocol >= (int)(sizeof(proto) / sizeof(proto[0]))) {
+ logwarnx("unknown mouse protocol (%d)", rodent.mode.protocol);
+ return (MOUSE_PROTO_UNKNOWN);
+ } else {
+ /* INPORT and BUS are the same... */
+ if (rodent.mode.protocol == MOUSE_PROTO_INPORT)
+ rodent.mode.protocol = MOUSE_PROTO_BUS;
+ if (rodent.mode.protocol != rodent.rtype) {
+ /* Hmm, the driver doesn't agree with the user... */
+ if (rodent.rtype != MOUSE_PROTO_UNKNOWN)
+ logwarnx("mouse type mismatch (%s != %s), %s is assumed",
+ r_name(rodent.mode.protocol), r_name(rodent.rtype),
+ r_name(rodent.mode.protocol));
+ rodent.rtype = rodent.mode.protocol;
+ bcopy(proto[rodent.rtype], cur_proto, sizeof(cur_proto));
+ }
+ }
+ cur_proto[4] = rodent.mode.packetsize;
+ cur_proto[0] = rodent.mode.syncmask[0]; /* header byte bit mask */
+ cur_proto[1] = rodent.mode.syncmask[1]; /* header bit pattern */
+ }
+
+ /* maybe this is a PnP mouse... */
+ if (rodent.mode.protocol == MOUSE_PROTO_UNKNOWN) {
+
+ if (rodent.flags & NoPnP)
+ return (rodent.rtype);
+ if (((len = pnpgets(pnpbuf)) <= 0) || !pnpparse(&pnpid, pnpbuf, len))
+ return (rodent.rtype);
+
+ debug("PnP serial mouse: '%*.*s' '%*.*s' '%*.*s'",
+ pnpid.neisaid, pnpid.neisaid, pnpid.eisaid,
+ pnpid.ncompat, pnpid.ncompat, pnpid.compat,
+ pnpid.ndescription, pnpid.ndescription, pnpid.description);
+
+ /* we have a valid PnP serial device ID */
+ rodent.hw.iftype = MOUSE_IF_SERIAL;
+ t = pnpproto(&pnpid);
+ if (t != NULL) {
+ rodent.mode.protocol = t->val;
+ rodent.hw.model = t->val2;
+ } else {
+ rodent.mode.protocol = MOUSE_PROTO_UNKNOWN;
+ }
+ if (rodent.mode.protocol == MOUSE_PROTO_INPORT)
+ rodent.mode.protocol = MOUSE_PROTO_BUS;
+
+ /* make final adjustment */
+ if (rodent.mode.protocol != MOUSE_PROTO_UNKNOWN) {
+ if (rodent.mode.protocol != rodent.rtype) {
+ /* Hmm, the device doesn't agree with the user... */
+ if (rodent.rtype != MOUSE_PROTO_UNKNOWN)
+ logwarnx("mouse type mismatch (%s != %s), %s is assumed",
+ r_name(rodent.mode.protocol), r_name(rodent.rtype),
+ r_name(rodent.mode.protocol));
+ rodent.rtype = rodent.mode.protocol;
+ bcopy(proto[rodent.rtype], cur_proto, sizeof(cur_proto));
+ }
+ }
+ }
+
+ debug("proto params: %02x %02x %02x %02x %d %02x %02x",
+ cur_proto[0], cur_proto[1], cur_proto[2], cur_proto[3],
+ cur_proto[4], cur_proto[5], cur_proto[6]);
+
+ return (rodent.rtype);
+}
+
+static const char *
+r_if(int iftype)
+{
+
+ return (gettokenname(rifs, iftype));
+}
+
+static const char *
+r_name(int type)
+{
+ const char *unknown = "unknown";
+
+ return (type == MOUSE_PROTO_UNKNOWN ||
+ type >= (int)(sizeof(rnames) / sizeof(rnames[0])) ?
+ unknown : rnames[type]);
+}
+
+static const char *
+r_model(int model)
+{
+
+ return (gettokenname(rmodels, model));
+}
+
+static void
+r_init(void)
+{
+ unsigned char buf[16]; /* scrach buffer */
+ fd_set fds;
+ const char *s;
+ char c;
+ int i;
+
+ /**
+ ** This comment is a little out of context here, but it contains
+ ** some useful information...
+ ********************************************************************
+ **
+ ** The following lines take care of the Logitech MouseMan protocols.
+ **
+ ** NOTE: There are different versions of both MouseMan and TrackMan!
+ ** Hence I add another protocol P_LOGIMAN, which the user can
+ ** specify as MouseMan in his XF86Config file. This entry was
+ ** formerly handled as a special case of P_MS. However, people
+ ** who don't have the middle button problem, can still specify
+ ** Microsoft and use P_MS.
+ **
+ ** By default, these mice should use a 3 byte Microsoft protocol
+ ** plus a 4th byte for the middle button. However, the mouse might
+ ** have switched to a different protocol before we use it, so I send
+ ** the proper sequence just in case.
+ **
+ ** NOTE: - all commands to (at least the European) MouseMan have to
+ ** be sent at 1200 Baud.
+ ** - each command starts with a '*'.
+ ** - whenever the MouseMan receives a '*', it will switch back
+ ** to 1200 Baud. Hence I have to select the desired protocol
+ ** first, then select the baud rate.
+ **
+ ** The protocols supported by the (European) MouseMan are:
+ ** - 5 byte packed binary protocol, as with the Mouse Systems
+ ** mouse. Selected by sequence "*U".
+ ** - 2 button 3 byte MicroSoft compatible protocol. Selected
+ ** by sequence "*V".
+ ** - 3 button 3+1 byte MicroSoft compatible protocol (default).
+ ** Selected by sequence "*X".
+ **
+ ** The following baud rates are supported:
+ ** - 1200 Baud (default). Selected by sequence "*n".
+ ** - 9600 Baud. Selected by sequence "*q".
+ **
+ ** Selecting a sample rate is no longer supported with the MouseMan!
+ ** Some additional lines in xf86Config.c take care of ill configured
+ ** baud rates and sample rates. (The user will get an error.)
+ */
+
+ switch (rodent.rtype) {
+
+ case MOUSE_PROTO_LOGI:
+ /*
+ * The baud rate selection command must be sent at the current
+ * baud rate; try all likely settings
+ */
+ setmousespeed(9600, rodent.baudrate, rodentcflags[rodent.rtype]);
+ setmousespeed(4800, rodent.baudrate, rodentcflags[rodent.rtype]);
+ setmousespeed(2400, rodent.baudrate, rodentcflags[rodent.rtype]);
+ setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
+ /* select MM series data format */
+ write(rodent.mfd, "S", 1);
+ setmousespeed(rodent.baudrate, rodent.baudrate,
+ rodentcflags[MOUSE_PROTO_MM]);
+ /* select report rate/frequency */
+ if (rodent.rate <= 0) write(rodent.mfd, "O", 1);
+ else if (rodent.rate <= 15) write(rodent.mfd, "J", 1);
+ else if (rodent.rate <= 27) write(rodent.mfd, "K", 1);
+ else if (rodent.rate <= 42) write(rodent.mfd, "L", 1);
+ else if (rodent.rate <= 60) write(rodent.mfd, "R", 1);
+ else if (rodent.rate <= 85) write(rodent.mfd, "M", 1);
+ else if (rodent.rate <= 125) write(rodent.mfd, "Q", 1);
+ else write(rodent.mfd, "N", 1);
+ break;
+
+ case MOUSE_PROTO_LOGIMOUSEMAN:
+ /* The command must always be sent at 1200 baud */
+ setmousespeed(1200, 1200, rodentcflags[rodent.rtype]);
+ write(rodent.mfd, "*X", 2);
+ setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
+ break;
+
+ case MOUSE_PROTO_HITTAB:
+ setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
+
+ /*
+ * Initialize Hitachi PUMA Plus - Model 1212E to desired settings.
+ * The tablet must be configured to be in MM mode, NO parity,
+ * Binary Format. xf86Info.sampleRate controls the sensativity
+ * of the tablet. We only use this tablet for it's 4-button puck
+ * so we don't run in "Absolute Mode"
+ */
+ write(rodent.mfd, "z8", 2); /* Set Parity = "NONE" */
+ usleep(50000);
+ write(rodent.mfd, "zb", 2); /* Set Format = "Binary" */
+ usleep(50000);
+ write(rodent.mfd, "@", 1); /* Set Report Mode = "Stream" */
+ usleep(50000);
+ write(rodent.mfd, "R", 1); /* Set Output Rate = "45 rps" */
+ usleep(50000);
+ write(rodent.mfd, "I\x20", 2); /* Set Incrememtal Mode "20" */
+ usleep(50000);
+ write(rodent.mfd, "E", 1); /* Set Data Type = "Relative */
+ usleep(50000);
+
+ /* Resolution is in 'lines per inch' on the Hitachi tablet */
+ if (rodent.resolution == MOUSE_RES_LOW) c = 'g';
+ else if (rodent.resolution == MOUSE_RES_MEDIUMLOW) c = 'e';
+ else if (rodent.resolution == MOUSE_RES_MEDIUMHIGH) c = 'h';
+ else if (rodent.resolution == MOUSE_RES_HIGH) c = 'd';
+ else if (rodent.resolution <= 40) c = 'g';
+ else if (rodent.resolution <= 100) c = 'd';
+ else if (rodent.resolution <= 200) c = 'e';
+ else if (rodent.resolution <= 500) c = 'h';
+ else if (rodent.resolution <= 1000) c = 'j';
+ else c = 'd';
+ write(rodent.mfd, &c, 1);
+ usleep(50000);
+
+ write(rodent.mfd, "\021", 1); /* Resume DATA output */
+ break;
+
+ case MOUSE_PROTO_THINK:
+ setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
+ /* the PnP ID string may be sent again, discard it */
+ usleep(200000);
+ i = FREAD;
+ ioctl(rodent.mfd, TIOCFLUSH, &i);
+ /* send the command to initialize the beast */
+ for (s = "E5E5"; *s; ++s) {
+ write(rodent.mfd, s, 1);
+ FD_ZERO(&fds);
+ FD_SET(rodent.mfd, &fds);
+ if (select(FD_SETSIZE, &fds, NULL, NULL, NULL) <= 0)
+ break;
+ read(rodent.mfd, &c, 1);
+ debug("%c", c);
+ if (c != *s)
+ break;
+ }
+ break;
+
+ case MOUSE_PROTO_JOGDIAL:
+ break;
+ case MOUSE_PROTO_MSC:
+ setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
+ if (rodent.flags & ClearDTR) {
+ i = TIOCM_DTR;
+ ioctl(rodent.mfd, TIOCMBIC, &i);
+ }
+ if (rodent.flags & ClearRTS) {
+ i = TIOCM_RTS;
+ ioctl(rodent.mfd, TIOCMBIC, &i);
+ }
+ break;
+
+ case MOUSE_PROTO_SYSMOUSE:
+ if (rodent.hw.iftype == MOUSE_IF_SYSMOUSE)
+ setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
+ /* FALLTHROUGH */
+
+ case MOUSE_PROTO_BUS:
+ case MOUSE_PROTO_INPORT:
+ case MOUSE_PROTO_PS2:
+ if (rodent.rate >= 0)
+ rodent.mode.rate = rodent.rate;
+ if (rodent.resolution != MOUSE_RES_UNKNOWN)
+ rodent.mode.resolution = rodent.resolution;
+ ioctl(rodent.mfd, MOUSE_SETMODE, &rodent.mode);
+ break;
+
+ case MOUSE_PROTO_X10MOUSEREM:
+ mremote_serversetup();
+ setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
+ break;
+
+
+ case MOUSE_PROTO_VERSAPAD:
+ tcsendbreak(rodent.mfd, 0); /* send break for 400 msec */
+ i = FREAD;
+ ioctl(rodent.mfd, TIOCFLUSH, &i);
+ for (i = 0; i < 7; ++i) {
+ FD_ZERO(&fds);
+ FD_SET(rodent.mfd, &fds);
+ if (select(FD_SETSIZE, &fds, NULL, NULL, NULL) <= 0)
+ break;
+ read(rodent.mfd, &c, 1);
+ buf[i] = c;
+ }
+ debug("%s\n", buf);
+ if ((buf[0] != 'V') || (buf[1] != 'P')|| (buf[7] != '\r'))
+ break;
+ setmousespeed(9600, rodent.baudrate, rodentcflags[rodent.rtype]);
+ tcsendbreak(rodent.mfd, 0); /* send break for 400 msec again */
+ for (i = 0; i < 7; ++i) {
+ FD_ZERO(&fds);
+ FD_SET(rodent.mfd, &fds);
+ if (select(FD_SETSIZE, &fds, NULL, NULL, NULL) <= 0)
+ break;
+ read(rodent.mfd, &c, 1);
+ debug("%c", c);
+ if (c != buf[i])
+ break;
+ }
+ i = FREAD;
+ ioctl(rodent.mfd, TIOCFLUSH, &i);
+ break;
+
+ default:
+ setmousespeed(1200, rodent.baudrate, rodentcflags[rodent.rtype]);
+ break;
+ }
+}
+
+static int
+r_protocol(u_char rBuf, mousestatus_t *act)
+{
+ /* MOUSE_MSS_BUTTON?DOWN -> MOUSE_BUTTON?DOWN */
+ static int butmapmss[4] = { /* Microsoft, MouseMan, GlidePoint,
+ IntelliMouse, Thinking Mouse */
+ 0,
+ MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON1DOWN,
+ MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
+ };
+ static int butmapmss2[4] = { /* Microsoft, MouseMan, GlidePoint,
+ Thinking Mouse */
+ 0,
+ MOUSE_BUTTON4DOWN,
+ MOUSE_BUTTON2DOWN,
+ MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN,
+ };
+ /* MOUSE_INTELLI_BUTTON?DOWN -> MOUSE_BUTTON?DOWN */
+ static int butmapintelli[4] = { /* IntelliMouse, NetMouse, Mie Mouse,
+ MouseMan+ */
+ 0,
+ MOUSE_BUTTON2DOWN,
+ MOUSE_BUTTON4DOWN,
+ MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN,
+ };
+ /* MOUSE_MSC_BUTTON?UP -> MOUSE_BUTTON?DOWN */
+ static int butmapmsc[8] = { /* MouseSystems, MMSeries, Logitech,
+ Bus, sysmouse */
+ 0,
+ MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON2DOWN,
+ MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON1DOWN,
+ MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN,
+ MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN
+ };
+ /* MOUSE_PS2_BUTTON?DOWN -> MOUSE_BUTTON?DOWN */
+ static int butmapps2[8] = { /* PS/2 */
+ 0,
+ MOUSE_BUTTON1DOWN,
+ MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON2DOWN,
+ MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN,
+ MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN
+ };
+ /* for Hitachi tablet */
+ static int butmaphit[8] = { /* MM HitTablet */
+ 0,
+ MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON2DOWN,
+ MOUSE_BUTTON1DOWN,
+ MOUSE_BUTTON4DOWN,
+ MOUSE_BUTTON5DOWN,
+ MOUSE_BUTTON6DOWN,
+ MOUSE_BUTTON7DOWN,
+ };
+ /* for serial VersaPad */
+ static int butmapversa[8] = { /* VersaPad */
+ 0,
+ 0,
+ MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON1DOWN,
+ MOUSE_BUTTON1DOWN,
+ MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
+ };
+ /* for PS/2 VersaPad */
+ static int butmapversaps2[8] = { /* VersaPad */
+ 0,
+ MOUSE_BUTTON3DOWN,
+ 0,
+ MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON1DOWN,
+ MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
+ MOUSE_BUTTON1DOWN,
+ MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN,
+ };
+ static int pBufP = 0;
+ static unsigned char pBuf[8];
+ static int prev_x, prev_y;
+ static int on = FALSE;
+ int x, y;
+
+ debug("received char 0x%x",(int)rBuf);
+ if (rodent.rtype == MOUSE_PROTO_KIDSPAD)
+ return (kidspad(rBuf, act));
+ if (rodent.rtype == MOUSE_PROTO_GTCO_DIGIPAD)
+ return (gtco_digipad(rBuf, act));
+
+ /*
+ * Hack for resyncing: We check here for a package that is:
+ * a) illegal (detected by wrong data-package header)
+ * b) invalid (0x80 == -128 and that might be wrong for MouseSystems)
+ * c) bad header-package
+ *
+ * NOTE: b) is a voilation of the MouseSystems-Protocol, since values of
+ * -128 are allowed, but since they are very seldom we can easily
+ * use them as package-header with no button pressed.
+ * NOTE/2: On a PS/2 mouse any byte is valid as a data byte. Furthermore,
+ * 0x80 is not valid as a header byte. For a PS/2 mouse we skip
+ * checking data bytes.
+ * For resyncing a PS/2 mouse we require the two most significant
+ * bits in the header byte to be 0. These are the overflow bits,
+ * and in case of an overflow we actually lose sync. Overflows
+ * are very rare, however, and we quickly gain sync again after
+ * an overflow condition. This is the best we can do. (Actually,
+ * we could use bit 0x08 in the header byte for resyncing, since
+ * that bit is supposed to be always on, but nobody told
+ * Microsoft...)
+ */
+
+ if (pBufP != 0 && rodent.rtype != MOUSE_PROTO_PS2 &&
+ ((rBuf & cur_proto[2]) != cur_proto[3] || rBuf == 0x80))
+ {
+ pBufP = 0; /* skip package */
+ }
+
+ if (pBufP == 0 && (rBuf & cur_proto[0]) != cur_proto[1])
+ return (0);
+
+ /* is there an extra data byte? */
+ if (pBufP >= cur_proto[4] && (rBuf & cur_proto[0]) != cur_proto[1])
+ {
+ /*
+ * Hack for Logitech MouseMan Mouse - Middle button
+ *
+ * Unfortunately this mouse has variable length packets: the standard
+ * Microsoft 3 byte packet plus an optional 4th byte whenever the
+ * middle button status changes.
+ *
+ * We have already processed the standard packet with the movement
+ * and button info. Now post an event message with the old status
+ * of the left and right buttons and the updated middle button.
+ */
+
+ /*
+ * Even worse, different MouseMen and TrackMen differ in the 4th
+ * byte: some will send 0x00/0x20, others 0x01/0x21, or even
+ * 0x02/0x22, so I have to strip off the lower bits.
+ *
+ * [JCH-96/01/21]
+ * HACK for ALPS "fourth button". (It's bit 0x10 of the "fourth byte"
+ * and it is activated by tapping the glidepad with the finger! 8^)
+ * We map it to bit bit3, and the reverse map in xf86Events just has
+ * to be extended so that it is identified as Button 4. The lower
+ * half of the reverse-map may remain unchanged.
+ */
+
+ /*
+ * [KY-97/08/03]
+ * Receive the fourth byte only when preceding three bytes have
+ * been detected (pBufP >= cur_proto[4]). In the previous
+ * versions, the test was pBufP == 0; thus, we may have mistakingly
+ * received a byte even if we didn't see anything preceding
+ * the byte.
+ */
+
+ if ((rBuf & cur_proto[5]) != cur_proto[6]) {
+ pBufP = 0;
+ return (0);
+ }
+
+ switch (rodent.rtype) {
+#if notyet
+ case MOUSE_PROTO_MARIQUA:
+ /*
+ * This mouse has 16! buttons in addition to the standard
+ * three of them. They return 0x10 though 0x1f in the
+ * so-called `ten key' mode and 0x30 though 0x3f in the
+ * `function key' mode. As there are only 31 bits for
+ * button state (including the standard three), we ignore
+ * the bit 0x20 and don't distinguish the two modes.
+ */
+ act->dx = act->dy = act->dz = 0;
+ act->obutton = act->button;
+ rBuf &= 0x1f;
+ act->button = (1 << (rBuf - 13))
+ | (act->obutton & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN));
+ /*
+ * FIXME: this is a button "down" event. There needs to be
+ * a corresponding button "up" event... XXX
+ */
+ break;
+#endif /* notyet */
+ case MOUSE_PROTO_JOGDIAL:
+ break;
+
+ /*
+ * IntelliMouse, NetMouse (including NetMouse Pro) and Mie Mouse
+ * always send the fourth byte, whereas the fourth byte is
+ * optional for GlidePoint and ThinkingMouse. The fourth byte
+ * is also optional for MouseMan+ and FirstMouse+ in their
+ * native mode. It is always sent if they are in the IntelliMouse
+ * compatible mode.
+ */
+ case MOUSE_PROTO_INTELLI: /* IntelliMouse, NetMouse, Mie Mouse,
+ MouseMan+ */
+ act->dx = act->dy = 0;
+ act->dz = (rBuf & 0x08) ? (rBuf & 0x0f) - 16 : (rBuf & 0x0f);
+ if ((act->dz >= 7) || (act->dz <= -7))
+ act->dz = 0;
+ act->obutton = act->button;
+ act->button = butmapintelli[(rBuf & MOUSE_MSS_BUTTONS) >> 4]
+ | (act->obutton & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN));
+ break;
+
+ default:
+ act->dx = act->dy = act->dz = 0;
+ act->obutton = act->button;
+ act->button = butmapmss2[(rBuf & MOUSE_MSS_BUTTONS) >> 4]
+ | (act->obutton & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN));
+ break;
+ }
+
+ act->flags = ((act->dx || act->dy || act->dz) ? MOUSE_POSCHANGED : 0)
+ | (act->obutton ^ act->button);
+ pBufP = 0;
+ return (act->flags);
+ }
+
+ if (pBufP >= cur_proto[4])
+ pBufP = 0;
+ pBuf[pBufP++] = rBuf;
+ if (pBufP != cur_proto[4])
+ return (0);
+
+ /*
+ * assembly full package
+ */
+
+ debug("assembled full packet (len %d) %x,%x,%x,%x,%x,%x,%x,%x",
+ cur_proto[4],
+ pBuf[0], pBuf[1], pBuf[2], pBuf[3],
+ pBuf[4], pBuf[5], pBuf[6], pBuf[7]);
+
+ act->dz = 0;
+ act->obutton = act->button;
+ switch (rodent.rtype)
+ {
+ case MOUSE_PROTO_MS: /* Microsoft */
+ case MOUSE_PROTO_LOGIMOUSEMAN: /* MouseMan/TrackMan */
+ case MOUSE_PROTO_X10MOUSEREM: /* X10 MouseRemote */
+ act->button = act->obutton & MOUSE_BUTTON4DOWN;
+ if (rodent.flags & ChordMiddle)
+ act->button |= ((pBuf[0] & MOUSE_MSS_BUTTONS) == MOUSE_MSS_BUTTONS)
+ ? MOUSE_BUTTON2DOWN
+ : butmapmss[(pBuf[0] & MOUSE_MSS_BUTTONS) >> 4];
+ else
+ act->button |= (act->obutton & MOUSE_BUTTON2DOWN)
+ | butmapmss[(pBuf[0] & MOUSE_MSS_BUTTONS) >> 4];
+
+ /* Send X10 btn events to remote client (ensure -128-+127 range) */
+ if ((rodent.rtype == MOUSE_PROTO_X10MOUSEREM) &&
+ ((pBuf[0] & 0xFC) == 0x44) && (pBuf[2] == 0x3F)) {
+ if (rodent.mremcfd >= 0) {
+ unsigned char key = (signed char)(((pBuf[0] & 0x03) << 6) |
+ (pBuf[1] & 0x3F));
+ write(rodent.mremcfd, &key, 1);
+ }
+ return (0);
+ }
+
+ act->dx = (signed char)(((pBuf[0] & 0x03) << 6) | (pBuf[1] & 0x3F));
+ act->dy = (signed char)(((pBuf[0] & 0x0C) << 4) | (pBuf[2] & 0x3F));
+ break;
+
+ case MOUSE_PROTO_GLIDEPOINT: /* GlidePoint */
+ case MOUSE_PROTO_THINK: /* ThinkingMouse */
+ case MOUSE_PROTO_INTELLI: /* IntelliMouse, NetMouse, Mie Mouse,
+ MouseMan+ */
+ act->button = (act->obutton & (MOUSE_BUTTON2DOWN | MOUSE_BUTTON4DOWN))
+ | butmapmss[(pBuf[0] & MOUSE_MSS_BUTTONS) >> 4];
+ act->dx = (signed char)(((pBuf[0] & 0x03) << 6) | (pBuf[1] & 0x3F));
+ act->dy = (signed char)(((pBuf[0] & 0x0C) << 4) | (pBuf[2] & 0x3F));
+ break;
+
+ case MOUSE_PROTO_MSC: /* MouseSystems Corp */
+#if notyet
+ case MOUSE_PROTO_MARIQUA: /* Mariqua */
+#endif
+ act->button = butmapmsc[(~pBuf[0]) & MOUSE_MSC_BUTTONS];
+ act->dx = (signed char)(pBuf[1]) + (signed char)(pBuf[3]);
+ act->dy = - ((signed char)(pBuf[2]) + (signed char)(pBuf[4]));
+ break;
+
+ case MOUSE_PROTO_JOGDIAL: /* JogDial */
+ if (rBuf == 0x6c)
+ act->dz = -1;
+ if (rBuf == 0x72)
+ act->dz = 1;
+ if (rBuf == 0x64)
+ act->button = MOUSE_BUTTON1DOWN;
+ if (rBuf == 0x75)
+ act->button = 0;
+ break;
+
+ case MOUSE_PROTO_HITTAB: /* MM HitTablet */
+ act->button = butmaphit[pBuf[0] & 0x07];
+ act->dx = (pBuf[0] & MOUSE_MM_XPOSITIVE) ? pBuf[1] : - pBuf[1];
+ act->dy = (pBuf[0] & MOUSE_MM_YPOSITIVE) ? - pBuf[2] : pBuf[2];
+ break;
+
+ case MOUSE_PROTO_MM: /* MM Series */
+ case MOUSE_PROTO_LOGI: /* Logitech Mice */
+ act->button = butmapmsc[pBuf[0] & MOUSE_MSC_BUTTONS];
+ act->dx = (pBuf[0] & MOUSE_MM_XPOSITIVE) ? pBuf[1] : - pBuf[1];
+ act->dy = (pBuf[0] & MOUSE_MM_YPOSITIVE) ? - pBuf[2] : pBuf[2];
+ break;
+
+ case MOUSE_PROTO_VERSAPAD: /* VersaPad */
+ act->button = butmapversa[(pBuf[0] & MOUSE_VERSA_BUTTONS) >> 3];
+ act->button |= (pBuf[0] & MOUSE_VERSA_TAP) ? MOUSE_BUTTON4DOWN : 0;
+ act->dx = act->dy = 0;
+ if (!(pBuf[0] & MOUSE_VERSA_IN_USE)) {
+ on = FALSE;
+ break;
+ }
+ x = (pBuf[2] << 6) | pBuf[1];
+ if (x & 0x800)
+ x -= 0x1000;
+ y = (pBuf[4] << 6) | pBuf[3];
+ if (y & 0x800)
+ y -= 0x1000;
+ if (on) {
+ act->dx = prev_x - x;
+ act->dy = prev_y - y;
+ } else {
+ on = TRUE;
+ }
+ prev_x = x;
+ prev_y = y;
+ break;
+
+ case MOUSE_PROTO_BUS: /* Bus */
+ case MOUSE_PROTO_INPORT: /* InPort */
+ act->button = butmapmsc[(~pBuf[0]) & MOUSE_MSC_BUTTONS];
+ act->dx = (signed char)pBuf[1];
+ act->dy = - (signed char)pBuf[2];
+ break;
+
+ case MOUSE_PROTO_PS2: /* PS/2 */
+ act->button = butmapps2[pBuf[0] & MOUSE_PS2_BUTTONS];
+ act->dx = (pBuf[0] & MOUSE_PS2_XNEG) ? pBuf[1] - 256 : pBuf[1];
+ act->dy = (pBuf[0] & MOUSE_PS2_YNEG) ? -(pBuf[2] - 256) : -pBuf[2];
+ /*
+ * Moused usually operates the psm driver at the operation level 1
+ * which sends mouse data in MOUSE_PROTO_SYSMOUSE protocol.
+ * The following code takes effect only when the user explicitly
+ * requets the level 2 at which wheel movement and additional button
+ * actions are encoded in model-dependent formats. At the level 0
+ * the following code is no-op because the psm driver says the model
+ * is MOUSE_MODEL_GENERIC.
+ */
+ switch (rodent.hw.model) {
+ case MOUSE_MODEL_EXPLORER:
+ /* wheel and additional button data is in the fourth byte */
+ act->dz = (pBuf[3] & MOUSE_EXPLORER_ZNEG)
+ ? (pBuf[3] & 0x0f) - 16 : (pBuf[3] & 0x0f);
+ act->button |= (pBuf[3] & MOUSE_EXPLORER_BUTTON4DOWN)
+ ? MOUSE_BUTTON4DOWN : 0;
+ act->button |= (pBuf[3] & MOUSE_EXPLORER_BUTTON5DOWN)
+ ? MOUSE_BUTTON5DOWN : 0;
+ break;
+ case MOUSE_MODEL_INTELLI:
+ case MOUSE_MODEL_NET:
+ /* wheel data is in the fourth byte */
+ act->dz = (signed char)pBuf[3];
+ if ((act->dz >= 7) || (act->dz <= -7))
+ act->dz = 0;
+ /* some compatible mice may have additional buttons */
+ act->button |= (pBuf[0] & MOUSE_PS2INTELLI_BUTTON4DOWN)
+ ? MOUSE_BUTTON4DOWN : 0;
+ act->button |= (pBuf[0] & MOUSE_PS2INTELLI_BUTTON5DOWN)
+ ? MOUSE_BUTTON5DOWN : 0;
+ break;
+ case MOUSE_MODEL_MOUSEMANPLUS:
+ if (((pBuf[0] & MOUSE_PS2PLUS_SYNCMASK) == MOUSE_PS2PLUS_SYNC)
+ && (abs(act->dx) > 191)
+ && MOUSE_PS2PLUS_CHECKBITS(pBuf)) {
+ /* the extended data packet encodes button and wheel events */
+ switch (MOUSE_PS2PLUS_PACKET_TYPE(pBuf)) {
+ case 1:
+ /* wheel data packet */
+ act->dx = act->dy = 0;
+ if (pBuf[2] & 0x80) {
+ /* horizontal roller count - ignore it XXX*/
+ } else {
+ /* vertical roller count */
+ act->dz = (pBuf[2] & MOUSE_PS2PLUS_ZNEG)
+ ? (pBuf[2] & 0x0f) - 16 : (pBuf[2] & 0x0f);
+ }
+ act->button |= (pBuf[2] & MOUSE_PS2PLUS_BUTTON4DOWN)
+ ? MOUSE_BUTTON4DOWN : 0;
+ act->button |= (pBuf[2] & MOUSE_PS2PLUS_BUTTON5DOWN)
+ ? MOUSE_BUTTON5DOWN : 0;
+ break;
+ case 2:
+ /* this packet type is reserved by Logitech */
+ /*
+ * IBM ScrollPoint Mouse uses this packet type to
+ * encode both vertical and horizontal scroll movement.
+ */
+ act->dx = act->dy = 0;
+ /* horizontal roller count */
+ if (pBuf[2] & 0x0f)
+ act->dz = (pBuf[2] & MOUSE_SPOINT_WNEG) ? -2 : 2;
+ /* vertical roller count */
+ if (pBuf[2] & 0xf0)
+ act->dz = (pBuf[2] & MOUSE_SPOINT_ZNEG) ? -1 : 1;
+#if 0
+ /* vertical roller count */
+ act->dz = (pBuf[2] & MOUSE_SPOINT_ZNEG)
+ ? ((pBuf[2] >> 4) & 0x0f) - 16
+ : ((pBuf[2] >> 4) & 0x0f);
+ /* horizontal roller count */
+ act->dw = (pBuf[2] & MOUSE_SPOINT_WNEG)
+ ? (pBuf[2] & 0x0f) - 16 : (pBuf[2] & 0x0f);
+#endif
+ break;
+ case 0:
+ /* device type packet - shouldn't happen */
+ /* FALLTHROUGH */
+ default:
+ act->dx = act->dy = 0;
+ act->button = act->obutton;
+ debug("unknown PS2++ packet type %d: 0x%02x 0x%02x 0x%02x\n",
+ MOUSE_PS2PLUS_PACKET_TYPE(pBuf),
+ pBuf[0], pBuf[1], pBuf[2]);
+ break;
+ }
+ } else {
+ /* preserve button states */
+ act->button |= act->obutton & MOUSE_EXTBUTTONS;
+ }
+ break;
+ case MOUSE_MODEL_GLIDEPOINT:
+ /* `tapping' action */
+ act->button |= ((pBuf[0] & MOUSE_PS2_TAP)) ? 0 : MOUSE_BUTTON4DOWN;
+ break;
+ case MOUSE_MODEL_NETSCROLL:
+ /* three addtional bytes encode buttons and wheel events */
+ act->button |= (pBuf[3] & MOUSE_PS2_BUTTON3DOWN)
+ ? MOUSE_BUTTON4DOWN : 0;
+ act->button |= (pBuf[3] & MOUSE_PS2_BUTTON1DOWN)
+ ? MOUSE_BUTTON5DOWN : 0;
+ act->dz = (pBuf[3] & MOUSE_PS2_XNEG) ? pBuf[4] - 256 : pBuf[4];
+ break;
+ case MOUSE_MODEL_THINK:
+ /* the fourth button state in the first byte */
+ act->button |= (pBuf[0] & MOUSE_PS2_TAP) ? MOUSE_BUTTON4DOWN : 0;
+ break;
+ case MOUSE_MODEL_VERSAPAD:
+ act->button = butmapversaps2[pBuf[0] & MOUSE_PS2VERSA_BUTTONS];
+ act->button |=
+ (pBuf[0] & MOUSE_PS2VERSA_TAP) ? MOUSE_BUTTON4DOWN : 0;
+ act->dx = act->dy = 0;
+ if (!(pBuf[0] & MOUSE_PS2VERSA_IN_USE)) {
+ on = FALSE;
+ break;
+ }
+ x = ((pBuf[4] << 8) & 0xf00) | pBuf[1];
+ if (x & 0x800)
+ x -= 0x1000;
+ y = ((pBuf[4] << 4) & 0xf00) | pBuf[2];
+ if (y & 0x800)
+ y -= 0x1000;
+ if (on) {
+ act->dx = prev_x - x;
+ act->dy = prev_y - y;
+ } else {
+ on = TRUE;
+ }
+ prev_x = x;
+ prev_y = y;
+ break;
+ case MOUSE_MODEL_4D:
+ act->dx = (pBuf[1] & 0x80) ? pBuf[1] - 256 : pBuf[1];
+ act->dy = (pBuf[2] & 0x80) ? -(pBuf[2] - 256) : -pBuf[2];
+ switch (pBuf[0] & MOUSE_4D_WHEELBITS) {
+ case 0x10:
+ act->dz = 1;
+ break;
+ case 0x30:
+ act->dz = -1;
+ break;
+ case 0x40: /* 2nd wheel rolling right XXX */
+ act->dz = 2;
+ break;
+ case 0xc0: /* 2nd wheel rolling left XXX */
+ act->dz = -2;
+ break;
+ }
+ break;
+ case MOUSE_MODEL_4DPLUS:
+ if ((act->dx < 16 - 256) && (act->dy > 256 - 16)) {
+ act->dx = act->dy = 0;
+ if (pBuf[2] & MOUSE_4DPLUS_BUTTON4DOWN)
+ act->button |= MOUSE_BUTTON4DOWN;
+ act->dz = (pBuf[2] & MOUSE_4DPLUS_ZNEG)
+ ? ((pBuf[2] & 0x07) - 8) : (pBuf[2] & 0x07);
+ } else {
+ /* preserve previous button states */
+ act->button |= act->obutton & MOUSE_EXTBUTTONS;
+ }
+ break;
+ case MOUSE_MODEL_GENERIC:
+ default:
+ break;
+ }
+ break;
+
+ case MOUSE_PROTO_SYSMOUSE: /* sysmouse */
+ act->button = butmapmsc[(~pBuf[0]) & MOUSE_SYS_STDBUTTONS];
+ act->dx = (signed char)(pBuf[1]) + (signed char)(pBuf[3]);
+ act->dy = - ((signed char)(pBuf[2]) + (signed char)(pBuf[4]));
+ if (rodent.level == 1) {
+ act->dz = ((signed char)(pBuf[5] << 1) + (signed char)(pBuf[6] << 1)) >> 1;
+ act->button |= ((~pBuf[7] & MOUSE_SYS_EXTBUTTONS) << 3);
+ }
+ break;
+
+ default:
+ return (0);
+ }
+ /*
+ * We don't reset pBufP here yet, as there may be an additional data
+ * byte in some protocols. See above.
+ */
+
+ /* has something changed? */
+ act->flags = ((act->dx || act->dy || act->dz) ? MOUSE_POSCHANGED : 0)
+ | (act->obutton ^ act->button);
+
+ return (act->flags);
+}
+
+static int
+r_statetrans(mousestatus_t *a1, mousestatus_t *a2, int trans)
+{
+ int changed;
+ int flags;
+
+ a2->dx = a1->dx;
+ a2->dy = a1->dy;
+ a2->dz = a1->dz;
+ a2->obutton = a2->button;
+ a2->button = a1->button;
+ a2->flags = a1->flags;
+ changed = FALSE;
+
+ if (rodent.flags & Emulate3Button) {
+ if (debug > 2)
+ debug("state:%d, trans:%d -> state:%d",
+ mouse_button_state, trans,
+ states[mouse_button_state].s[trans]);
+ /*
+ * Avoid re-ordering button and movement events. While a button
+ * event is deferred, throw away up to BUTTON2_MAXMOVE movement
+ * events to allow for mouse jitter. If more movement events
+ * occur, then complete the deferred button events immediately.
+ */
+ if ((a2->dx != 0 || a2->dy != 0) &&
+ S_DELAYED(states[mouse_button_state].s[trans])) {
+ if (++mouse_move_delayed > BUTTON2_MAXMOVE) {
+ mouse_move_delayed = 0;
+ mouse_button_state =
+ states[mouse_button_state].s[A_TIMEOUT];
+ changed = TRUE;
+ } else
+ a2->dx = a2->dy = 0;
+ } else
+ mouse_move_delayed = 0;
+ if (mouse_button_state != states[mouse_button_state].s[trans])
+ changed = TRUE;
+ if (changed)
+ clock_gettime(CLOCK_MONOTONIC_FAST, &mouse_button_state_ts);
+ mouse_button_state = states[mouse_button_state].s[trans];
+ a2->button &=
+ ~(MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN | MOUSE_BUTTON3DOWN);
+ a2->button &= states[mouse_button_state].mask;
+ a2->button |= states[mouse_button_state].buttons;
+ flags = a2->flags & MOUSE_POSCHANGED;
+ flags |= a2->obutton ^ a2->button;
+ if (flags & MOUSE_BUTTON2DOWN) {
+ a2->flags = flags & MOUSE_BUTTON2DOWN;
+ r_timestamp(a2);
+ }
+ a2->flags = flags;
+ }
+ return (changed);
+}
+
+/* phisical to logical button mapping */
+static int p2l[MOUSE_MAXBUTTON] = {
+ MOUSE_BUTTON1DOWN, MOUSE_BUTTON2DOWN, MOUSE_BUTTON3DOWN, MOUSE_BUTTON4DOWN,
+ MOUSE_BUTTON5DOWN, MOUSE_BUTTON6DOWN, MOUSE_BUTTON7DOWN, MOUSE_BUTTON8DOWN,
+ 0x00000100, 0x00000200, 0x00000400, 0x00000800,
+ 0x00001000, 0x00002000, 0x00004000, 0x00008000,
+ 0x00010000, 0x00020000, 0x00040000, 0x00080000,
+ 0x00100000, 0x00200000, 0x00400000, 0x00800000,
+ 0x01000000, 0x02000000, 0x04000000, 0x08000000,
+ 0x10000000, 0x20000000, 0x40000000,
+};
+
+static char *
+skipspace(char *s)
+{
+ while(isspace(*s))
+ ++s;
+ return (s);
+}
+
+static int
+r_installmap(char *arg)
+{
+ int pbutton;
+ int lbutton;
+ char *s;
+
+ while (*arg) {
+ arg = skipspace(arg);
+ s = arg;
+ while (isdigit(*arg))
+ ++arg;
+ arg = skipspace(arg);
+ if ((arg <= s) || (*arg != '='))
+ return (FALSE);
+ lbutton = atoi(s);
+
+ arg = skipspace(++arg);
+ s = arg;
+ while (isdigit(*arg))
+ ++arg;
+ if ((arg <= s) || (!isspace(*arg) && (*arg != '\0')))
+ return (FALSE);
+ pbutton = atoi(s);
+
+ if ((lbutton <= 0) || (lbutton > MOUSE_MAXBUTTON))
+ return (FALSE);
+ if ((pbutton <= 0) || (pbutton > MOUSE_MAXBUTTON))
+ return (FALSE);
+ p2l[pbutton - 1] = 1 << (lbutton - 1);
+ mstate[lbutton - 1] = &bstate[pbutton - 1];
+ }
+
+ return (TRUE);
+}
+
+static void
+r_map(mousestatus_t *act1, mousestatus_t *act2)
+{
+ register int pb;
+ register int pbuttons;
+ int lbuttons;
+
+ pbuttons = act1->button;
+ lbuttons = 0;
+
+ act2->obutton = act2->button;
+ if (pbuttons & rodent.wmode) {
+ pbuttons &= ~rodent.wmode;
+ act1->dz = act1->dy;
+ act1->dx = 0;
+ act1->dy = 0;
+ }
+ act2->dx = act1->dx;
+ act2->dy = act1->dy;
+ act2->dz = act1->dz;
+
+ switch (rodent.zmap[0]) {
+ case 0: /* do nothing */
+ break;
+ case MOUSE_XAXIS:
+ if (act1->dz != 0) {
+ act2->dx = act1->dz;
+ act2->dz = 0;
+ }
+ break;
+ case MOUSE_YAXIS:
+ if (act1->dz != 0) {
+ act2->dy = act1->dz;
+ act2->dz = 0;
+ }
+ break;
+ default: /* buttons */
+ pbuttons &= ~(rodent.zmap[0] | rodent.zmap[1]
+ | rodent.zmap[2] | rodent.zmap[3]);
+ if ((act1->dz < -1) && rodent.zmap[2]) {
+ pbuttons |= rodent.zmap[2];
+ zstate[2].count = 1;
+ } else if (act1->dz < 0) {
+ pbuttons |= rodent.zmap[0];
+ zstate[0].count = 1;
+ } else if ((act1->dz > 1) && rodent.zmap[3]) {
+ pbuttons |= rodent.zmap[3];
+ zstate[3].count = 1;
+ } else if (act1->dz > 0) {
+ pbuttons |= rodent.zmap[1];
+ zstate[1].count = 1;
+ }
+ act2->dz = 0;
+ break;
+ }
+
+ for (pb = 0; (pb < MOUSE_MAXBUTTON) && (pbuttons != 0); ++pb) {
+ lbuttons |= (pbuttons & 1) ? p2l[pb] : 0;
+ pbuttons >>= 1;
+ }
+ act2->button = lbuttons;
+
+ act2->flags = ((act2->dx || act2->dy || act2->dz) ? MOUSE_POSCHANGED : 0)
+ | (act2->obutton ^ act2->button);
+}
+
+static void
+r_timestamp(mousestatus_t *act)
+{
+ struct timespec ts;
+ struct timespec ts1;
+ struct timespec ts2;
+ struct timespec ts3;
+ int button;
+ int mask;
+ int i;
+
+ mask = act->flags & MOUSE_BUTTONS;
+#if 0
+ if (mask == 0)
+ return;
+#endif
+
+ clock_gettime(CLOCK_MONOTONIC_FAST, &ts1);
+ drift_current_ts = ts1;
+
+ /* double click threshold */
+ ts2.tv_sec = rodent.clickthreshold / 1000;
+ ts2.tv_nsec = (rodent.clickthreshold % 1000) * 1000000;
+ tssub(&ts1, &ts2, &ts);
+ debug("ts: %jd %ld", (intmax_t)ts.tv_sec, ts.tv_nsec);
+
+ /* 3 button emulation timeout */
+ ts2.tv_sec = rodent.button2timeout / 1000;
+ ts2.tv_nsec = (rodent.button2timeout % 1000) * 1000000;
+ tssub(&ts1, &ts2, &ts3);
+
+ button = MOUSE_BUTTON1DOWN;
+ for (i = 0; (i < MOUSE_MAXBUTTON) && (mask != 0); ++i) {
+ if (mask & 1) {
+ if (act->button & button) {
+ /* the button is down */
+ debug(" : %jd %ld",
+ (intmax_t)bstate[i].ts.tv_sec, bstate[i].ts.tv_nsec);
+ if (tscmp(&ts, &bstate[i].ts, >)) {
+ bstate[i].count = 1;
+ } else {
+ ++bstate[i].count;
+ }
+ bstate[i].ts = ts1;
+ } else {
+ /* the button is up */
+ bstate[i].ts = ts1;
+ }
+ } else {
+ if (act->button & button) {
+ /* the button has been down */
+ if (tscmp(&ts3, &bstate[i].ts, >)) {
+ bstate[i].count = 1;
+ bstate[i].ts = ts1;
+ act->flags |= button;
+ debug("button %d timeout", i + 1);
+ }
+ } else {
+ /* the button has been up */
+ }
+ }
+ button <<= 1;
+ mask >>= 1;
+ }
+}
+
+static int
+r_timeout(void)
+{
+ struct timespec ts;
+ struct timespec ts1;
+ struct timespec ts2;
+
+ if (states[mouse_button_state].timeout)
+ return (TRUE);
+ clock_gettime(CLOCK_MONOTONIC_FAST, &ts1);
+ ts2.tv_sec = rodent.button2timeout / 1000;
+ ts2.tv_nsec = (rodent.button2timeout % 1000) * 1000000;
+ tssub(&ts1, &ts2, &ts);
+ return (tscmp(&ts, &mouse_button_state_ts, >));
+}
+
+static void
+r_click(mousestatus_t *act)
+{
+ struct mouse_info mouse;
+ int button;
+ int mask;
+ int i;
+
+ mask = act->flags & MOUSE_BUTTONS;
+ if (mask == 0)
+ return;
+
+ button = MOUSE_BUTTON1DOWN;
+ for (i = 0; (i < MOUSE_MAXBUTTON) && (mask != 0); ++i) {
+ if (mask & 1) {
+ debug("mstate[%d]->count:%d", i, mstate[i]->count);
+ if (act->button & button) {
+ /* the button is down */
+ mouse.u.event.value = mstate[i]->count;
+ } else {
+ /* the button is up */
+ mouse.u.event.value = 0;
+ }
+ mouse.operation = MOUSE_BUTTON_EVENT;
+ mouse.u.event.id = button;
+ if (debug < 2)
+ if (!paused)
+ ioctl(rodent.cfd, CONS_MOUSECTL, &mouse);
+ debug("button %d count %d", i + 1, mouse.u.event.value);
+ }
+ button <<= 1;
+ mask >>= 1;
+ }
+}
+
+/* $XConsortium: posix_tty.c,v 1.3 95/01/05 20:42:55 kaleb Exp $ */
+/* $XFree86: xc/programs/Xserver/hw/xfree86/os-support/shared/posix_tty.c,v 3.4 1995/01/28 17:05:03 dawes Exp $ */
+/*
+ * Copyright 1993 by David Dawes <dawes@physics.su.oz.au>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of David Dawes
+ * not be used in advertising or publicity pertaining to distribution of
+ * the software without specific, written prior permission.
+ * David Dawes makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * DAVID DAWES DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL DAVID DAWES BE LIABLE FOR
+ * ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+
+static void
+setmousespeed(int old, int new, unsigned cflag)
+{
+ struct termios tty;
+ const char *c;
+
+ if (tcgetattr(rodent.mfd, &tty) < 0)
+ {
+ logwarn("unable to get status of mouse fd");
+ return;
+ }
+
+ tty.c_iflag = IGNBRK | IGNPAR;
+ tty.c_oflag = 0;
+ tty.c_lflag = 0;
+ tty.c_cflag = (tcflag_t)cflag;
+ tty.c_cc[VTIME] = 0;
+ tty.c_cc[VMIN] = 1;
+
+ switch (old)
+ {
+ case 9600:
+ cfsetispeed(&tty, B9600);
+ cfsetospeed(&tty, B9600);
+ break;
+ case 4800:
+ cfsetispeed(&tty, B4800);
+ cfsetospeed(&tty, B4800);
+ break;
+ case 2400:
+ cfsetispeed(&tty, B2400);
+ cfsetospeed(&tty, B2400);
+ break;
+ case 1200:
+ default:
+ cfsetispeed(&tty, B1200);
+ cfsetospeed(&tty, B1200);
+ }
+
+ if (tcsetattr(rodent.mfd, TCSADRAIN, &tty) < 0)
+ {
+ logwarn("unable to set status of mouse fd");
+ return;
+ }
+
+ switch (new)
+ {
+ case 9600:
+ c = "*q";
+ cfsetispeed(&tty, B9600);
+ cfsetospeed(&tty, B9600);
+ break;
+ case 4800:
+ c = "*p";
+ cfsetispeed(&tty, B4800);
+ cfsetospeed(&tty, B4800);
+ break;
+ case 2400:
+ c = "*o";
+ cfsetispeed(&tty, B2400);
+ cfsetospeed(&tty, B2400);
+ break;
+ case 1200:
+ default:
+ c = "*n";
+ cfsetispeed(&tty, B1200);
+ cfsetospeed(&tty, B1200);
+ }
+
+ if (rodent.rtype == MOUSE_PROTO_LOGIMOUSEMAN
+ || rodent.rtype == MOUSE_PROTO_LOGI)
+ {
+ if (write(rodent.mfd, c, 2) != 2)
+ {
+ logwarn("unable to write to mouse fd");
+ return;
+ }
+ }
+ usleep(100000);
+
+ if (tcsetattr(rodent.mfd, TCSADRAIN, &tty) < 0)
+ logwarn("unable to set status of mouse fd");
+}
+
+/*
+ * PnP COM device support
+ *
+ * It's a simplistic implementation, but it works :-)
+ * KY, 31/7/97.
+ */
+
+/*
+ * Try to elicit a PnP ID as described in
+ * Microsoft, Hayes: "Plug and Play External COM Device Specification,
+ * rev 1.00", 1995.
+ *
+ * The routine does not fully implement the COM Enumerator as par Section
+ * 2.1 of the document. In particular, we don't have idle state in which
+ * the driver software monitors the com port for dynamic connection or
+ * removal of a device at the port, because `moused' simply quits if no
+ * device is found.
+ *
+ * In addition, as PnP COM device enumeration procedure slightly has
+ * changed since its first publication, devices which follow earlier
+ * revisions of the above spec. may fail to respond if the rev 1.0
+ * procedure is used. XXX
+ */
+static int
+pnpwakeup1(void)
+{
+ struct timeval timeout;
+ fd_set fds;
+ int i;
+
+ /*
+ * This is the procedure described in rev 1.0 of PnP COM device spec.
+ * Unfortunately, some devices which comform to earlier revisions of
+ * the spec gets confused and do not return the ID string...
+ */
+ debug("PnP COM device rev 1.0 probe...");
+
+ /* port initialization (2.1.2) */
+ ioctl(rodent.mfd, TIOCMGET, &i);
+ i |= TIOCM_DTR; /* DTR = 1 */
+ i &= ~TIOCM_RTS; /* RTS = 0 */
+ ioctl(rodent.mfd, TIOCMSET, &i);
+ usleep(240000);
+
+ /*
+ * The PnP COM device spec. dictates that the mouse must set DSR
+ * in response to DTR (by hardware or by software) and that if DSR is
+ * not asserted, the host computer should think that there is no device
+ * at this serial port. But some mice just don't do that...
+ */
+ ioctl(rodent.mfd, TIOCMGET, &i);
+ debug("modem status 0%o", i);
+ if ((i & TIOCM_DSR) == 0)
+ return (FALSE);
+
+ /* port setup, 1st phase (2.1.3) */
+ setmousespeed(1200, 1200, (CS7 | CREAD | CLOCAL | HUPCL));
+ i = TIOCM_DTR | TIOCM_RTS; /* DTR = 0, RTS = 0 */
+ ioctl(rodent.mfd, TIOCMBIC, &i);
+ usleep(240000);
+ i = TIOCM_DTR; /* DTR = 1, RTS = 0 */
+ ioctl(rodent.mfd, TIOCMBIS, &i);
+ usleep(240000);
+
+ /* wait for response, 1st phase (2.1.4) */
+ i = FREAD;
+ ioctl(rodent.mfd, TIOCFLUSH, &i);
+ i = TIOCM_RTS; /* DTR = 1, RTS = 1 */
+ ioctl(rodent.mfd, TIOCMBIS, &i);
+
+ /* try to read something */
+ FD_ZERO(&fds);
+ FD_SET(rodent.mfd, &fds);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 240000;
+ if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) > 0) {
+ debug("pnpwakeup1(): valid response in first phase.");
+ return (TRUE);
+ }
+
+ /* port setup, 2nd phase (2.1.5) */
+ i = TIOCM_DTR | TIOCM_RTS; /* DTR = 0, RTS = 0 */
+ ioctl(rodent.mfd, TIOCMBIC, &i);
+ usleep(240000);
+
+ /* wait for respose, 2nd phase (2.1.6) */
+ i = FREAD;
+ ioctl(rodent.mfd, TIOCFLUSH, &i);
+ i = TIOCM_DTR | TIOCM_RTS; /* DTR = 1, RTS = 1 */
+ ioctl(rodent.mfd, TIOCMBIS, &i);
+
+ /* try to read something */
+ FD_ZERO(&fds);
+ FD_SET(rodent.mfd, &fds);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 240000;
+ if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) > 0) {
+ debug("pnpwakeup1(): valid response in second phase.");
+ return (TRUE);
+ }
+
+ return (FALSE);
+}
+
+static int
+pnpwakeup2(void)
+{
+ struct timeval timeout;
+ fd_set fds;
+ int i;
+
+ /*
+ * This is a simplified procedure; it simply toggles RTS.
+ */
+ debug("alternate probe...");
+
+ ioctl(rodent.mfd, TIOCMGET, &i);
+ i |= TIOCM_DTR; /* DTR = 1 */
+ i &= ~TIOCM_RTS; /* RTS = 0 */
+ ioctl(rodent.mfd, TIOCMSET, &i);
+ usleep(240000);
+
+ setmousespeed(1200, 1200, (CS7 | CREAD | CLOCAL | HUPCL));
+
+ /* wait for respose */
+ i = FREAD;
+ ioctl(rodent.mfd, TIOCFLUSH, &i);
+ i = TIOCM_DTR | TIOCM_RTS; /* DTR = 1, RTS = 1 */
+ ioctl(rodent.mfd, TIOCMBIS, &i);
+
+ /* try to read something */
+ FD_ZERO(&fds);
+ FD_SET(rodent.mfd, &fds);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 240000;
+ if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) > 0) {
+ debug("pnpwakeup2(): valid response.");
+ return (TRUE);
+ }
+
+ return (FALSE);
+}
+
+static int
+pnpgets(char *buf)
+{
+ struct timeval timeout;
+ fd_set fds;
+ int begin;
+ int i;
+ char c;
+
+ if (!pnpwakeup1() && !pnpwakeup2()) {
+ /*
+ * According to PnP spec, we should set DTR = 1 and RTS = 0 while
+ * in idle state. But, `moused' shall set DTR = RTS = 1 and proceed,
+ * assuming there is something at the port even if it didn't
+ * respond to the PnP enumeration procedure.
+ */
+ i = TIOCM_DTR | TIOCM_RTS; /* DTR = 1, RTS = 1 */
+ ioctl(rodent.mfd, TIOCMBIS, &i);
+ return (0);
+ }
+
+ /* collect PnP COM device ID (2.1.7) */
+ begin = -1;
+ i = 0;
+ usleep(240000); /* the mouse must send `Begin ID' within 200msec */
+ while (read(rodent.mfd, &c, 1) == 1) {
+ /* we may see "M", or "M3..." before `Begin ID' */
+ buf[i++] = c;
+ if ((c == 0x08) || (c == 0x28)) { /* Begin ID */
+ debug("begin-id %02x", c);
+ begin = i - 1;
+ break;
+ }
+ debug("%c %02x", c, c);
+ if (i >= 256)
+ break;
+ }
+ if (begin < 0) {
+ /* we haven't seen `Begin ID' in time... */
+ goto connect_idle;
+ }
+
+ ++c; /* make it `End ID' */
+ for (;;) {
+ FD_ZERO(&fds);
+ FD_SET(rodent.mfd, &fds);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 240000;
+ if (select(FD_SETSIZE, &fds, NULL, NULL, &timeout) <= 0)
+ break;
+
+ read(rodent.mfd, &buf[i], 1);
+ if (buf[i++] == c) /* End ID */
+ break;
+ if (i >= 256)
+ break;
+ }
+ if (begin > 0) {
+ i -= begin;
+ bcopy(&buf[begin], &buf[0], i);
+ }
+ /* string may not be human readable... */
+ debug("len:%d, '%-*.*s'", i, i, i, buf);
+
+ if (buf[i - 1] == c)
+ return (i); /* a valid PnP string */
+
+ /*
+ * According to PnP spec, we should set DTR = 1 and RTS = 0 while
+ * in idle state. But, `moused' shall leave the modem control lines
+ * as they are. See above.
+ */
+connect_idle:
+
+ /* we may still have something in the buffer */
+ return ((i > 0) ? i : 0);
+}
+
+static int
+pnpparse(pnpid_t *id, char *buf, int len)
+{
+ char s[3];
+ int offset;
+ int sum = 0;
+ int i, j;
+
+ id->revision = 0;
+ id->eisaid = NULL;
+ id->serial = NULL;
+ id->class = NULL;
+ id->compat = NULL;
+ id->description = NULL;
+ id->neisaid = 0;
+ id->nserial = 0;
+ id->nclass = 0;
+ id->ncompat = 0;
+ id->ndescription = 0;
+
+ if ((buf[0] != 0x28) && (buf[0] != 0x08)) {
+ /* non-PnP mice */
+ switch(buf[0]) {
+ default:
+ return (FALSE);
+ case 'M': /* Microsoft */
+ id->eisaid = "PNP0F01";
+ break;
+ case 'H': /* MouseSystems */
+ id->eisaid = "PNP0F04";
+ break;
+ }
+ id->neisaid = strlen(id->eisaid);
+ id->class = "MOUSE";
+ id->nclass = strlen(id->class);
+ debug("non-PnP mouse '%c'", buf[0]);
+ return (TRUE);
+ }
+
+ /* PnP mice */
+ offset = 0x28 - buf[0];
+
+ /* calculate checksum */
+ for (i = 0; i < len - 3; ++i) {
+ sum += buf[i];
+ buf[i] += offset;
+ }
+ sum += buf[len - 1];
+ for (; i < len; ++i)
+ buf[i] += offset;
+ debug("PnP ID string: '%*.*s'", len, len, buf);
+
+ /* revision */
+ buf[1] -= offset;
+ buf[2] -= offset;
+ id->revision = ((buf[1] & 0x3f) << 6) | (buf[2] & 0x3f);
+ debug("PnP rev %d.%02d", id->revision / 100, id->revision % 100);
+
+ /* EISA vender and product ID */
+ id->eisaid = &buf[3];
+ id->neisaid = 7;
+
+ /* option strings */
+ i = 10;
+ if (buf[i] == '\\') {
+ /* device serial # */
+ for (j = ++i; i < len; ++i) {
+ if (buf[i] == '\\')
+ break;
+ }
+ if (i >= len)
+ i -= 3;
+ if (i - j == 8) {
+ id->serial = &buf[j];
+ id->nserial = 8;
+ }
+ }
+ if (buf[i] == '\\') {
+ /* PnP class */
+ for (j = ++i; i < len; ++i) {
+ if (buf[i] == '\\')
+ break;
+ }
+ if (i >= len)
+ i -= 3;
+ if (i > j + 1) {
+ id->class = &buf[j];
+ id->nclass = i - j;
+ }
+ }
+ if (buf[i] == '\\') {
+ /* compatible driver */
+ for (j = ++i; i < len; ++i) {
+ if (buf[i] == '\\')
+ break;
+ }
+ /*
+ * PnP COM spec prior to v0.96 allowed '*' in this field,
+ * it's not allowed now; just igore it.
+ */
+ if (buf[j] == '*')
+ ++j;
+ if (i >= len)
+ i -= 3;
+ if (i > j + 1) {
+ id->compat = &buf[j];
+ id->ncompat = i - j;
+ }
+ }
+ if (buf[i] == '\\') {
+ /* product description */
+ for (j = ++i; i < len; ++i) {
+ if (buf[i] == ';')
+ break;
+ }
+ if (i >= len)
+ i -= 3;
+ if (i > j + 1) {
+ id->description = &buf[j];
+ id->ndescription = i - j;
+ }
+ }
+
+ /* checksum exists if there are any optional fields */
+ if ((id->nserial > 0) || (id->nclass > 0)
+ || (id->ncompat > 0) || (id->ndescription > 0)) {
+ debug("PnP checksum: 0x%X", sum);
+ sprintf(s, "%02X", sum & 0x0ff);
+ if (strncmp(s, &buf[len - 3], 2) != 0) {
+#if 0
+ /*
+ * I found some mice do not comply with the PnP COM device
+ * spec regarding checksum... XXX
+ */
+ logwarnx("PnP checksum error", 0);
+ return (FALSE);
+#endif
+ }
+ }
+
+ return (TRUE);
+}
+
+static symtab_t *
+pnpproto(pnpid_t *id)
+{
+ symtab_t *t;
+ int i, j;
+
+ if (id->nclass > 0)
+ if (strncmp(id->class, "MOUSE", id->nclass) != 0 &&
+ strncmp(id->class, "TABLET", id->nclass) != 0)
+ /* this is not a mouse! */
+ return (NULL);
+
+ if (id->neisaid > 0) {
+ t = gettoken(pnpprod, id->eisaid, id->neisaid);
+ if (t->val != MOUSE_PROTO_UNKNOWN)
+ return (t);
+ }
+
+ /*
+ * The 'Compatible drivers' field may contain more than one
+ * ID separated by ','.
+ */
+ if (id->ncompat <= 0)
+ return (NULL);
+ for (i = 0; i < id->ncompat; ++i) {
+ for (j = i; id->compat[i] != ','; ++i)
+ if (i >= id->ncompat)
+ break;
+ if (i > j) {
+ t = gettoken(pnpprod, id->compat + j, i - j);
+ if (t->val != MOUSE_PROTO_UNKNOWN)
+ return (t);
+ }
+ }
+
+ return (NULL);
+}
+
+/* name/val mapping */
+
+static symtab_t *
+gettoken(symtab_t *tab, const char *s, int len)
+{
+ int i;
+
+ for (i = 0; tab[i].name != NULL; ++i) {
+ if (strncmp(tab[i].name, s, len) == 0)
+ break;
+ }
+ return (&tab[i]);
+}
+
+static const char *
+gettokenname(symtab_t *tab, int val)
+{
+ static const char unknown[] = "unknown";
+ int i;
+
+ for (i = 0; tab[i].name != NULL; ++i) {
+ if (tab[i].val == val)
+ return (tab[i].name);
+ }
+ return (unknown);
+}
+
+
+/*
+ * code to read from the Genius Kidspad tablet.
+
+The tablet responds to the COM PnP protocol 1.0 with EISA-ID KYE0005,
+and to pre-pnp probes (RTS toggle) with 'T' (tablet ?)
+9600, 8 bit, parity odd.
+
+The tablet puts out 5 bytes. b0 (mask 0xb8, value 0xb8) contains
+the proximity, tip and button info:
+ (byte0 & 0x1) true = tip pressed
+ (byte0 & 0x2) true = button pressed
+ (byte0 & 0x40) false = pen in proximity of tablet.
+
+The next 4 bytes are used for coordinates xl, xh, yl, yh (7 bits valid).
+
+Only absolute coordinates are returned, so we use the following approach:
+we store the last coordinates sent when the pen went out of the tablet,
+
+
+ *
+ */
+
+typedef enum {
+ S_IDLE, S_PROXY, S_FIRST, S_DOWN, S_UP
+} k_status;
+
+static int
+kidspad(u_char rxc, mousestatus_t *act)
+{
+ static int buf[5];
+ static int buflen = 0, b_prev = 0 , x_prev = -1, y_prev = -1;
+ static k_status status = S_IDLE;
+ static struct timespec old, now;
+
+ int x, y;
+
+ if (buflen > 0 && (rxc & 0x80)) {
+ fprintf(stderr, "invalid code %d 0x%x\n", buflen, rxc);
+ buflen = 0;
+ }
+ if (buflen == 0 && (rxc & 0xb8) != 0xb8) {
+ fprintf(stderr, "invalid code 0 0x%x\n", rxc);
+ return (0); /* invalid code, no action */
+ }
+ buf[buflen++] = rxc;
+ if (buflen < 5)
+ return (0);
+
+ buflen = 0; /* for next time... */
+
+ x = buf[1]+128*(buf[2] - 7);
+ if (x < 0) x = 0;
+ y = 28*128 - (buf[3] + 128* (buf[4] - 7));
+ if (y < 0) y = 0;
+
+ x /= 8;
+ y /= 8;
+
+ act->flags = 0;
+ act->obutton = act->button;
+ act->dx = act->dy = act->dz = 0;
+ clock_gettime(CLOCK_MONOTONIC_FAST, &now);
+ if (buf[0] & 0x40) /* pen went out of reach */
+ status = S_IDLE;
+ else if (status == S_IDLE) { /* pen is newly near the tablet */
+ act->flags |= MOUSE_POSCHANGED; /* force update */
+ status = S_PROXY;
+ x_prev = x;
+ y_prev = y;
+ }
+ old = now;
+ act->dx = x - x_prev;
+ act->dy = y - y_prev;
+ if (act->dx || act->dy)
+ act->flags |= MOUSE_POSCHANGED;
+ x_prev = x;
+ y_prev = y;
+ if (b_prev != 0 && b_prev != buf[0]) { /* possibly record button change */
+ act->button = 0;
+ if (buf[0] & 0x01) /* tip pressed */
+ act->button |= MOUSE_BUTTON1DOWN;
+ if (buf[0] & 0x02) /* button pressed */
+ act->button |= MOUSE_BUTTON2DOWN;
+ act->flags |= MOUSE_BUTTONSCHANGED;
+ }
+ b_prev = buf[0];
+ return (act->flags);
+}
+
+static int
+gtco_digipad (u_char rxc, mousestatus_t *act)
+{
+ static u_char buf[5];
+ static int buflen = 0, b_prev = 0 , x_prev = -1, y_prev = -1;
+ static k_status status = S_IDLE;
+ int x, y;
+
+#define GTCO_HEADER 0x80
+#define GTCO_PROXIMITY 0x40
+#define GTCO_START (GTCO_HEADER|GTCO_PROXIMITY)
+#define GTCO_BUTTONMASK 0x3c
+
+
+ if (buflen > 0 && ((rxc & GTCO_HEADER) != GTCO_HEADER)) {
+ fprintf(stderr, "invalid code %d 0x%x\n", buflen, rxc);
+ buflen = 0;
+ }
+ if (buflen == 0 && (rxc & GTCO_START) != GTCO_START) {
+ fprintf(stderr, "invalid code 0 0x%x\n", rxc);
+ return (0); /* invalid code, no action */
+ }
+
+ buf[buflen++] = rxc;
+ if (buflen < 5)
+ return (0);
+
+ buflen = 0; /* for next time... */
+
+ x = ((buf[2] & ~GTCO_START) << 6 | (buf[1] & ~GTCO_START));
+ y = 4768 - ((buf[4] & ~GTCO_START) << 6 | (buf[3] & ~GTCO_START));
+
+ x /= 2.5;
+ y /= 2.5;
+
+ act->flags = 0;
+ act->obutton = act->button;
+ act->dx = act->dy = act->dz = 0;
+
+ if ((buf[0] & 0x40) == 0) /* pen went out of reach */
+ status = S_IDLE;
+ else if (status == S_IDLE) { /* pen is newly near the tablet */
+ act->flags |= MOUSE_POSCHANGED; /* force update */
+ status = S_PROXY;
+ x_prev = x;
+ y_prev = y;
+ }
+
+ act->dx = x - x_prev;
+ act->dy = y - y_prev;
+ if (act->dx || act->dy)
+ act->flags |= MOUSE_POSCHANGED;
+ x_prev = x;
+ y_prev = y;
+
+ /* possibly record button change */
+ if (b_prev != 0 && b_prev != buf[0]) {
+ act->button = 0;
+ if (buf[0] & 0x04) {
+ /* tip pressed/yellow */
+ act->button |= MOUSE_BUTTON1DOWN;
+ }
+ if (buf[0] & 0x08) {
+ /* grey/white */
+ act->button |= MOUSE_BUTTON2DOWN;
+ }
+ if (buf[0] & 0x10) {
+ /* black/green */
+ act->button |= MOUSE_BUTTON3DOWN;
+ }
+ if (buf[0] & 0x20) {
+ /* tip+grey/blue */
+ act->button |= MOUSE_BUTTON4DOWN;
+ }
+ act->flags |= MOUSE_BUTTONSCHANGED;
+ }
+ b_prev = buf[0];
+ return (act->flags);
+}
+
+static void
+mremote_serversetup(void)
+{
+ struct sockaddr_un ad;
+
+ /* Open a UNIX domain stream socket to listen for mouse remote clients */
+ unlink(_PATH_MOUSEREMOTE);
+
+ if ((rodent.mremsfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+ logerrx(1, "unable to create unix domain socket %s",_PATH_MOUSEREMOTE);
+
+ umask(0111);
+
+ bzero(&ad, sizeof(ad));
+ ad.sun_family = AF_UNIX;
+ strcpy(ad.sun_path, _PATH_MOUSEREMOTE);
+#ifndef SUN_LEN
+#define SUN_LEN(unp) (((char *)(unp)->sun_path - (char *)(unp)) + \
+ strlen((unp)->path))
+#endif
+ if (bind(rodent.mremsfd, (struct sockaddr *) &ad, SUN_LEN(&ad)) < 0)
+ logerrx(1, "unable to bind unix domain socket %s", _PATH_MOUSEREMOTE);
+
+ listen(rodent.mremsfd, 1);
+}
+
+static void
+mremote_clientchg(int add)
+{
+ struct sockaddr_un ad;
+ socklen_t ad_len;
+ int fd;
+
+ if (rodent.rtype != MOUSE_PROTO_X10MOUSEREM)
+ return;
+
+ if (add) {
+ /* Accept client connection, if we don't already have one */
+ ad_len = sizeof(ad);
+ fd = accept(rodent.mremsfd, (struct sockaddr *) &ad, &ad_len);
+ if (fd < 0)
+ logwarnx("failed accept on mouse remote socket");
+
+ if (rodent.mremcfd < 0) {
+ rodent.mremcfd = fd;
+ debug("remote client connect...accepted");
+ }
+ else {
+ close(fd);
+ debug("another remote client connect...disconnected");
+ }
+ }
+ else {
+ /* Client disconnected */
+ debug("remote client disconnected");
+ close(rodent.mremcfd);
+ rodent.mremcfd = -1;
+ }
+}
diff --git a/usr.sbin/mptable/Makefile b/usr.sbin/mptable/Makefile
new file mode 100644
index 0000000..a9221f9
--- /dev/null
+++ b/usr.sbin/mptable/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+PROG= mptable
+WARNS?= 6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mptable/mptable.1 b/usr.sbin/mptable/mptable.1
new file mode 100644
index 0000000..445285b
--- /dev/null
+++ b/usr.sbin/mptable/mptable.1
@@ -0,0 +1,69 @@
+.\" Copyright (c) 1996
+.\" Steve Passe <fsmp@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. The name of the developer 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 April 28, 1997
+.Dt MPTABLE 1
+.Os
+.Sh NAME
+.Nm mptable
+.Nd display MP configuration table
+.Sh SYNOPSIS
+.Nm
+.Op Fl dmesg
+.Op Fl verbose
+.Op Fl grope
+.Op Fl help
+.Sh DESCRIPTION
+The
+.Nm
+command finds and analyzes the MP configuration table on
+an Intel(tm) MP spec capable motherboard.
+It is useful for debugging an SMP kernel that will not boot, as well
+as examining the configuration of a system.
+It can be run with a UniProcessor kernel.
+.Pp
+It must be run with gid kmem privileges.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl dmesg
+Include a dmesg dump.
+.It Fl grope
+Look in areas it should not NEED to, use ONLY as a last resort.
+.It Fl help
+Print a usage message and exits.
+.It Fl verbose
+Print extra info.
+.El
+.Sh SEE ALSO
+.Xr smp 4 ,
+.Xr dmesg 8
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Fx 3.0 .
+.Sh AUTHORS
+.An Steve Passe Aq fsmp@FreeBSD.org
diff --git a/usr.sbin/mptable/mptable.c b/usr.sbin/mptable/mptable.c
new file mode 100644
index 0000000..1ca6dfe
--- /dev/null
+++ b/usr.sbin/mptable/mptable.c
@@ -0,0 +1,1110 @@
+/*
+ * Copyright (c) 1996, by Steve Passe
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the 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 developer 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.
+ */
+
+/*
+ * mptable.c
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * this will cause the raw mp table to be dumped to /tmp/mpdump
+ *
+#define RAW_DUMP
+ */
+
+#define MP_SIG 0x5f504d5f /* _MP_ */
+#define EXTENDED_PROCESSING_READY
+#define OEM_PROCESSING_READY_NOT
+
+#include <sys/types.h>
+#include <err.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define SEP_LINE \
+"\n-------------------------------------------------------------------------------\n"
+
+#define SEP_LINE2 \
+"\n===============================================================================\n"
+
+/* EBDA is @ 40:0e in real-mode terms */
+#define EBDA_POINTER 0x040e /* location of EBDA pointer */
+
+/* CMOS 'top of mem' is @ 40:13 in real-mode terms */
+#define TOPOFMEM_POINTER 0x0413 /* BIOS: base memory size */
+
+#define DEFAULT_TOPOFMEM 0xa0000
+
+#define BIOS_BASE 0xf0000
+#define BIOS_BASE2 0xe0000
+#define BIOS_SIZE 0x10000
+#define ONE_KBYTE 1024
+
+#define GROPE_AREA1 0x80000
+#define GROPE_AREA2 0x90000
+#define GROPE_SIZE 0x10000
+
+#define PROCENTRY_FLAG_EN 0x01
+#define PROCENTRY_FLAG_BP 0x02
+#define IOAPICENTRY_FLAG_EN 0x01
+
+#define MAXPNSTR 132
+
+enum busTypes {
+ CBUS = 1,
+ CBUSII = 2,
+ EISA = 3,
+ ISA = 6,
+ PCI = 13,
+ XPRESS = 18,
+ MAX_BUSTYPE = 18,
+ UNKNOWN_BUSTYPE = 0xff
+};
+
+typedef struct BUSTYPENAME {
+ u_char type;
+ char name[ 7 ];
+} busTypeName;
+
+static busTypeName busTypeTable[] =
+{
+ { CBUS, "CBUS" },
+ { CBUSII, "CBUSII" },
+ { EISA, "EISA" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { ISA, "ISA" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { PCI, "PCI" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" }
+};
+
+const char* whereStrings[] = {
+ "Extended BIOS Data Area",
+ "BIOS top of memory",
+ "Default top of memory",
+ "BIOS",
+ "Extended BIOS",
+ "GROPE AREA #1",
+ "GROPE AREA #2"
+};
+
+typedef struct TABLE_ENTRY {
+ u_char type;
+ u_char length;
+ char name[ 32 ];
+} tableEntry;
+
+tableEntry basetableEntryTypes[] =
+{
+ { 0, 20, "Processor" },
+ { 1, 8, "Bus" },
+ { 2, 8, "I/O APIC" },
+ { 3, 8, "I/O INT" },
+ { 4, 8, "Local INT" }
+};
+
+tableEntry extendedtableEntryTypes[] =
+{
+ { 128, 20, "System Address Space" },
+ { 129, 8, "Bus Heirarchy" },
+ { 130, 8, "Compatibility Bus Address" }
+};
+
+/* MP Floating Pointer Structure */
+typedef struct MPFPS {
+ char signature[ 4 ];
+ u_int32_t pap;
+ u_char length;
+ u_char spec_rev;
+ u_char checksum;
+ u_char mpfb1;
+ u_char mpfb2;
+ u_char mpfb3;
+ u_char mpfb4;
+ u_char mpfb5;
+} mpfps_t;
+
+/* MP Configuration Table Header */
+typedef struct MPCTH {
+ char signature[ 4 ];
+ u_short base_table_length;
+ u_char spec_rev;
+ u_char checksum;
+ u_char oem_id[ 8 ];
+ u_char product_id[ 12 ];
+ u_int32_t oem_table_pointer;
+ u_short oem_table_size;
+ u_short entry_count;
+ u_int32_t apic_address;
+ u_short extended_table_length;
+ u_char extended_table_checksum;
+ u_char reserved;
+} mpcth_t;
+
+
+typedef struct PROCENTRY {
+ u_char type;
+ u_char apicID;
+ u_char apicVersion;
+ u_char cpuFlags;
+ u_int32_t cpuSignature;
+ u_int32_t featureFlags;
+ u_int32_t reserved1;
+ u_int32_t reserved2;
+} ProcEntry;
+
+typedef struct BUSENTRY {
+ u_char type;
+ u_char busID;
+ char busType[ 6 ];
+} BusEntry;
+
+typedef struct IOAPICENTRY {
+ u_char type;
+ u_char apicID;
+ u_char apicVersion;
+ u_char apicFlags;
+ u_int32_t apicAddress;
+} IOApicEntry;
+
+typedef struct INTENTRY {
+ u_char type;
+ u_char intType;
+ u_short intFlags;
+ u_char srcBusID;
+ u_char srcBusIRQ;
+ u_char dstApicID;
+ u_char dstApicINT;
+} IntEntry;
+
+
+/*
+ * extended entry type structures
+ */
+
+typedef struct SASENTRY {
+ u_char type;
+ u_char length;
+ u_char busID;
+ u_char addressType;
+ u_int64_t addressBase;
+ u_int64_t addressLength;
+} __attribute__((__packed__)) SasEntry;
+
+
+typedef struct BHDENTRY {
+ u_char type;
+ u_char length;
+ u_char busID;
+ u_char busInfo;
+ u_char busParent;
+ u_char reserved[ 3 ];
+} BhdEntry;
+
+
+typedef struct CBASMENTRY {
+ u_char type;
+ u_char length;
+ u_char busID;
+ u_char addressMod;
+ u_int predefinedRange;
+} CbasmEntry;
+
+
+
+static void apic_probe( u_int32_t* paddr, int* where );
+
+static void MPConfigDefault( int featureByte );
+
+static void MPFloatingPointer( u_int32_t paddr, int where, mpfps_t* mpfps );
+static void MPConfigTableHeader( u_int32_t pap );
+
+static int readType( void );
+static void seekEntry( u_int32_t addr );
+static void readEntry( void* entry, int size );
+
+static void processorEntry( void );
+static void busEntry( void );
+static void ioApicEntry( void );
+static void intEntry( void );
+
+static void sasEntry( void );
+static void bhdEntry( void );
+static void cbasmEntry( void );
+
+static void doDmesg( void );
+static void pnstr( char* s, int c );
+
+/* global data */
+int pfd; /* physical /dev/mem fd */
+
+int busses[ 16 ];
+int apics[ 16 ];
+
+int ncpu;
+int nbus;
+int napic;
+int nintr;
+
+int dmesg;
+int grope;
+int verbose;
+
+static void
+usage( void )
+{
+ fprintf( stderr, "usage: mptable [-dmesg] [-verbose] [-grope] [-help]\n" );
+ exit( 0 );
+}
+
+/*
+ *
+ */
+int
+main( int argc, char *argv[] )
+{
+ u_int32_t paddr;
+ int where;
+ mpfps_t mpfps;
+ int defaultConfig;
+
+ int ch;
+
+ /* announce ourselves */
+ puts( SEP_LINE2 );
+
+ printf( "MPTable\n" );
+
+ while ((ch = getopt(argc, argv, "d:g:h:v:")) != -1) {
+ switch(ch) {
+ case 'd':
+ if ( strcmp( optarg, "mesg") == 0 )
+ dmesg = 1;
+ else
+ dmesg = 0;
+ break;
+ case 'h':
+ if ( strcmp( optarg, "elp") == 0 )
+ usage();
+ break;
+ case 'g':
+ if ( strcmp( optarg, "rope") == 0 )
+ grope = 1;
+ break;
+ case 'v':
+ if ( strcmp( optarg, "erbose") == 0 )
+ verbose = 1;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+ optreset = 1;
+ optind = 0;
+ }
+
+ /* open physical memory for access to MP structures */
+ if ( (pfd = open( _PATH_MEM, O_RDONLY )) < 0 )
+ err( 1, "mem open" );
+
+ /* probe for MP structures */
+ apic_probe( &paddr, &where );
+ if ( where <= 0 ) {
+ fprintf( stderr, "\n MP FPS NOT found,\n" );
+ fprintf( stderr, " suggest trying -grope option!!!\n\n" );
+ return 1;
+ }
+
+ if ( verbose )
+ printf( "\n MP FPS found in %s @ physical addr: 0x%08x\n",
+ whereStrings[ where - 1 ], paddr );
+
+ puts( SEP_LINE );
+
+ /* analyze the MP Floating Pointer Structure */
+ MPFloatingPointer( paddr, where, &mpfps );
+
+ puts( SEP_LINE );
+
+ /* check whether an MP config table exists */
+ if ( (defaultConfig = mpfps.mpfb1) )
+ MPConfigDefault( defaultConfig );
+ else
+ MPConfigTableHeader( mpfps.pap );
+
+ /* do a dmesg output */
+ if ( dmesg )
+ doDmesg();
+
+ puts( SEP_LINE2 );
+
+ return 0;
+}
+
+
+/*
+ * set PHYSICAL address of MP floating pointer structure
+ */
+#define NEXT(X) ((X) += 4)
+static void
+apic_probe( u_int32_t* paddr, int* where )
+{
+ /*
+ * c rewrite of apic_probe() by Jack F. Vogel
+ */
+
+ int x;
+ u_short segment;
+ u_int32_t target;
+ u_int buffer[ BIOS_SIZE / sizeof( int ) ];
+
+ if ( verbose )
+ printf( "\n" );
+
+ /* search Extended Bios Data Area, if present */
+ if ( verbose )
+ printf( " looking for EBDA pointer @ 0x%04x, ", EBDA_POINTER );
+ seekEntry( (u_int32_t)EBDA_POINTER );
+ readEntry( &segment, 2 );
+ if ( segment ) { /* search EBDA */
+ target = (u_int32_t)segment << 4;
+ if ( verbose )
+ printf( "found, searching EBDA @ 0x%08x\n", target );
+ seekEntry( target );
+ readEntry( buffer, ONE_KBYTE );
+
+ for ( x = 0; x < ONE_KBYTE / (int)sizeof ( unsigned int ); NEXT(x) ) {
+ if ( buffer[ x ] == MP_SIG ) {
+ *where = 1;
+ *paddr = (x * sizeof( unsigned int )) + target;
+ return;
+ }
+ }
+ }
+ else {
+ if ( verbose )
+ printf( "NOT found\n" );
+ }
+
+ /* read CMOS for real top of mem */
+ seekEntry( (u_int32_t)TOPOFMEM_POINTER );
+ readEntry( &segment, 2 );
+ --segment; /* less ONE_KBYTE */
+ target = segment * 1024;
+ if ( verbose )
+ printf( " searching CMOS 'top of mem' @ 0x%08x (%dK)\n",
+ target, segment );
+ seekEntry( target );
+ readEntry( buffer, ONE_KBYTE );
+
+ for ( x = 0; x < ONE_KBYTE / (int)sizeof ( unsigned int ); NEXT(x) ) {
+ if ( buffer[ x ] == MP_SIG ) {
+ *where = 2;
+ *paddr = (x * sizeof( unsigned int )) + target;
+ return;
+ }
+ }
+
+ /* we don't necessarily believe CMOS, check base of the last 1K of 640K */
+ if ( target != (DEFAULT_TOPOFMEM - 1024)) {
+ target = (DEFAULT_TOPOFMEM - 1024);
+ if ( verbose )
+ printf( " searching default 'top of mem' @ 0x%08x (%dK)\n",
+ target, (target / 1024) );
+ seekEntry( target );
+ readEntry( buffer, ONE_KBYTE );
+
+ for ( x = 0; x < ONE_KBYTE / (int)sizeof ( unsigned int ); NEXT(x) ) {
+ if ( buffer[ x ] == MP_SIG ) {
+ *where = 3;
+ *paddr = (x * sizeof( unsigned int )) + target;
+ return;
+ }
+ }
+ }
+
+ /* search the BIOS */
+ if ( verbose )
+ printf( " searching BIOS @ 0x%08x\n", BIOS_BASE );
+ seekEntry( BIOS_BASE );
+ readEntry( buffer, BIOS_SIZE );
+
+ for ( x = 0; x < BIOS_SIZE / (int)sizeof( unsigned int ); NEXT(x) ) {
+ if ( buffer[ x ] == MP_SIG ) {
+ *where = 4;
+ *paddr = (x * sizeof( unsigned int )) + BIOS_BASE;
+ return;
+ }
+ }
+
+ /* search the extended BIOS */
+ if ( verbose )
+ printf( " searching extended BIOS @ 0x%08x\n", BIOS_BASE2 );
+ seekEntry( BIOS_BASE2 );
+ readEntry( buffer, BIOS_SIZE );
+
+ for ( x = 0; x < BIOS_SIZE / (int)sizeof( unsigned int ); NEXT(x) ) {
+ if ( buffer[ x ] == MP_SIG ) {
+ *where = 5;
+ *paddr = (x * sizeof( unsigned int )) + BIOS_BASE2;
+ return;
+ }
+ }
+
+ if ( grope ) {
+ /* search additional memory */
+ target = GROPE_AREA1;
+ if ( verbose )
+ printf( " groping memory @ 0x%08x\n", target );
+ seekEntry( target );
+ readEntry( buffer, GROPE_SIZE );
+
+ for ( x = 0; x < GROPE_SIZE / (int)sizeof( unsigned int ); NEXT(x) ) {
+ if ( buffer[ x ] == MP_SIG ) {
+ *where = 6;
+ *paddr = (x * sizeof( unsigned int )) + GROPE_AREA1;
+ return;
+ }
+ }
+
+ target = GROPE_AREA2;
+ if ( verbose )
+ printf( " groping memory @ 0x%08x\n", target );
+ seekEntry( target );
+ readEntry( buffer, GROPE_SIZE );
+
+ for ( x = 0; x < GROPE_SIZE / (int)sizeof( unsigned int ); NEXT(x) ) {
+ if ( buffer[ x ] == MP_SIG ) {
+ *where = 7;
+ *paddr = (x * sizeof( unsigned int )) + GROPE_AREA2;
+ return;
+ }
+ }
+ }
+
+ *where = 0;
+ *paddr = (u_int32_t)0;
+}
+
+
+/*
+ *
+ */
+static void
+MPFloatingPointer( u_int32_t paddr, int where, mpfps_t* mpfps )
+{
+
+ /* read in mpfps structure*/
+ seekEntry( paddr );
+ readEntry( mpfps, sizeof( mpfps_t ) );
+
+ /* show its contents */
+ printf( "MP Floating Pointer Structure:\n\n" );
+
+ printf( " location:\t\t\t" );
+ switch ( where )
+ {
+ case 1:
+ printf( "EBDA\n" );
+ break;
+ case 2:
+ printf( "BIOS base memory\n" );
+ break;
+ case 3:
+ printf( "DEFAULT base memory (639K)\n" );
+ break;
+ case 4:
+ printf( "BIOS\n" );
+ break;
+ case 5:
+ printf( "Extended BIOS\n" );
+ break;
+
+ case 0:
+ printf( "NOT found!\n" );
+ exit( 1 );
+ default:
+ printf( "BOGUS!\n" );
+ exit( 1 );
+ }
+ printf( " physical address:\t\t0x%08x\n", paddr );
+
+ printf( " signature:\t\t\t'" );
+ pnstr( mpfps->signature, 4 );
+ printf( "'\n" );
+
+ printf( " length:\t\t\t%d bytes\n", mpfps->length * 16 );
+ printf( " version:\t\t\t1.%1d\n", mpfps->spec_rev );
+ printf( " checksum:\t\t\t0x%02x\n", mpfps->checksum );
+
+ /* bits 0:6 are RESERVED */
+ if ( mpfps->mpfb2 & 0x7f ) {
+ printf( " warning, MP feature byte 2: 0x%02x\n", mpfps->mpfb2 );
+ }
+
+ /* bit 7 is IMCRP */
+ printf( " mode:\t\t\t\t%s\n", (mpfps->mpfb2 & 0x80) ?
+ "PIC" : "Virtual Wire" );
+
+ /* MP feature bytes 3-5 are expected to be ZERO */
+ if ( mpfps->mpfb3 )
+ printf( " warning, MP feature byte 3 NONZERO!\n" );
+ if ( mpfps->mpfb4 )
+ printf( " warning, MP feature byte 4 NONZERO!\n" );
+ if ( mpfps->mpfb5 )
+ printf( " warning, MP feature byte 5 NONZERO!\n" );
+}
+
+
+/*
+ *
+ */
+static void
+MPConfigDefault( int featureByte )
+{
+ printf( " MP default config type: %d\n\n", featureByte );
+ switch ( featureByte ) {
+ case 1:
+ printf( " bus: ISA, APIC: 82489DX\n" );
+ break;
+ case 2:
+ printf( " bus: EISA, APIC: 82489DX\n" );
+ break;
+ case 3:
+ printf( " bus: EISA, APIC: 82489DX\n" );
+ break;
+ case 4:
+ printf( " bus: MCA, APIC: 82489DX\n" );
+ break;
+ case 5:
+ printf( " bus: ISA+PCI, APIC: Integrated\n" );
+ break;
+ case 6:
+ printf( " bus: EISA+PCI, APIC: Integrated\n" );
+ break;
+ case 7:
+ printf( " bus: MCA+PCI, APIC: Integrated\n" );
+ break;
+ default:
+ printf( " future type\n" );
+ break;
+ }
+
+ switch ( featureByte ) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ nbus = 1;
+ break;
+ case 5:
+ case 6:
+ case 7:
+ nbus = 2;
+ break;
+ default:
+ printf( " future type\n" );
+ break;
+ }
+
+ ncpu = 2;
+ napic = 1;
+ nintr = 16;
+}
+
+
+/*
+ *
+ */
+static void
+MPConfigTableHeader( u_int32_t pap )
+{
+ u_int32_t paddr;
+ mpcth_t cth;
+ int x;
+ int totalSize;
+ int count, c;
+ int type;
+ int oldtype, entrytype;
+
+ if ( pap == 0 ) {
+ printf( "MP Configuration Table Header MISSING!\n" );
+ exit( 1 );
+ }
+
+ /* convert physical address to virtual address */
+ paddr = pap;
+
+ /* read in cth structure */
+ seekEntry( paddr );
+ readEntry( &cth, sizeof( cth ) );
+
+ printf( "MP Config Table Header:\n\n" );
+
+ printf( " physical address:\t\t0x%08x\n", pap );
+
+ printf( " signature:\t\t\t'" );
+ pnstr( cth.signature, 4 );
+ printf( "'\n" );
+
+ printf( " base table length:\t\t%d\n", cth.base_table_length );
+
+ printf( " version:\t\t\t1.%1d\n", cth.spec_rev );
+ printf( " checksum:\t\t\t0x%02x\n", cth.checksum );
+
+ printf( " OEM ID:\t\t\t'" );
+ pnstr( cth.oem_id, 8 );
+ printf( "'\n" );
+
+ printf( " Product ID:\t\t\t'" );
+ pnstr( cth.product_id, 12 );
+ printf( "'\n" );
+
+ printf( " OEM table pointer:\t\t0x%08x\n", cth.oem_table_pointer );
+ printf( " OEM table size:\t\t%d\n", cth.oem_table_size );
+
+ printf( " entry count:\t\t\t%d\n", cth.entry_count );
+
+ printf( " local APIC address:\t\t0x%08x\n", cth.apic_address );
+
+ printf( " extended table length:\t%d\n", cth.extended_table_length );
+ printf( " extended table checksum:\t%d\n", cth.extended_table_checksum );
+
+ totalSize = cth.base_table_length - sizeof( struct MPCTH );
+ count = cth.entry_count;
+
+ puts( SEP_LINE );
+
+ printf( "MP Config Base Table Entries:\n\n" );
+
+ /* initialze tables */
+ for ( x = 0; x < 16; ++x ) {
+ busses[ x ] = apics[ x ] = 0xff;
+ }
+
+ ncpu = 0;
+ nbus = 0;
+ napic = 0;
+ nintr = 0;
+
+ oldtype = -1;
+ for (c = count; c; c--) {
+ entrytype = readType();
+ if (entrytype != oldtype)
+ printf("--\n");
+ if (entrytype < oldtype)
+ printf("MPTABLE OUT OF ORDER!\n");
+ switch (entrytype) {
+ case 0:
+ if (oldtype != 0)
+ printf( "Processors:\tAPIC ID\tVersion\tState"
+ "\t\tFamily\tModel\tStep\tFlags\n" );
+ oldtype = 0;
+ processorEntry();
+ break;
+
+ case 1:
+ if (oldtype != 1)
+ printf( "Bus:\t\tBus ID\tType\n" );
+ oldtype = 1;
+ busEntry();
+ break;
+
+ case 2:
+ if (oldtype != 2)
+ printf( "I/O APICs:\tAPIC ID\tVersion\tState\t\tAddress\n" );
+ oldtype = 2;
+ ioApicEntry();
+ break;
+
+ case 3:
+ if (oldtype != 3)
+ printf( "I/O Ints:\tType\tPolarity Trigger\tBus ID\t IRQ\tAPIC ID\tPIN#\n" );
+ oldtype = 3;
+ intEntry();
+ break;
+
+ case 4:
+ if (oldtype != 4)
+ printf( "Local Ints:\tType\tPolarity Trigger\tBus ID\t IRQ\tAPIC ID\tPIN#\n" );
+ oldtype = 4;
+ intEntry();
+ break;
+
+ default:
+ printf("MPTABLE HOSED! record type = %d\n", entrytype);
+ exit(1);
+ }
+ }
+
+
+#if defined( EXTENDED_PROCESSING_READY )
+ /* process any extended data */
+ if ( (totalSize = cth.extended_table_length) ) {
+ puts( SEP_LINE );
+
+ printf( "MP Config Extended Table Entries:\n\n" );
+
+ while ( totalSize > 0 ) {
+ switch ( type = readType() ) {
+ case 128:
+ sasEntry();
+ break;
+ case 129:
+ bhdEntry();
+ break;
+ case 130:
+ cbasmEntry();
+ break;
+ default:
+ printf( "Extended Table HOSED!\n" );
+ exit( 1 );
+ }
+
+ totalSize -= extendedtableEntryTypes[ type-128 ].length;
+ }
+ }
+#endif /* EXTENDED_PROCESSING_READY */
+
+ /* process any OEM data */
+ if ( cth.oem_table_pointer && (cth.oem_table_size > 0) ) {
+#if defined( OEM_PROCESSING_READY )
+# error your on your own here!
+ /* convert OEM table pointer to virtual address */
+ poemtp = (u_int32_t)cth.oem_table_pointer;
+
+ /* read in oem table structure */
+ if ( (oemdata = (void*)malloc( cth.oem_table_size )) == NULL )
+ err( 1, "oem malloc" );
+
+ seekEntry( poemtp );
+ readEntry( oemdata, cth.oem_table_size );
+
+ /** process it */
+
+ free( oemdata );
+#else
+ printf( "\nyou need to modify the source to handle OEM data!\n\n" );
+#endif /* OEM_PROCESSING_READY */
+ }
+
+ fflush( stdout );
+
+#if defined( RAW_DUMP )
+{
+ int ofd;
+ u_char dumpbuf[ 4096 ];
+
+ ofd = open( "/tmp/mpdump", O_CREAT | O_RDWR );
+ seekEntry( paddr );
+ readEntry( dumpbuf, 1024 );
+ write( ofd, dumpbuf, 1024 );
+ close( ofd );
+}
+#endif /* RAW_DUMP */
+}
+
+
+/*
+ *
+ */
+static int
+readType( void )
+{
+ u_char type;
+
+ if ( read( pfd, &type, sizeof( u_char ) ) != sizeof( u_char ) )
+ err( 1, "type read; pfd: %d", pfd );
+
+ if ( lseek( pfd, -1, SEEK_CUR ) < 0 )
+ err( 1, "type seek" );
+
+ return (int)type;
+}
+
+
+/*
+ *
+ */
+static void
+seekEntry( u_int32_t addr )
+{
+ if ( lseek( pfd, (off_t)addr, SEEK_SET ) < 0 )
+ err( 1, "%s seek", _PATH_MEM );
+}
+
+
+/*
+ *
+ */
+static void
+readEntry( void* entry, int size )
+{
+ if ( read( pfd, entry, size ) != size )
+ err( 1, "readEntry" );
+}
+
+
+static void
+processorEntry( void )
+{
+ ProcEntry entry;
+
+ /* read it into local memory */
+ readEntry( &entry, sizeof( entry ) );
+
+ /* count it */
+ ++ncpu;
+
+ printf( "\t\t%2d", entry.apicID );
+ printf( "\t 0x%2x", entry.apicVersion );
+
+ printf( "\t %s, %s",
+ (entry.cpuFlags & PROCENTRY_FLAG_BP) ? "BSP" : "AP",
+ (entry.cpuFlags & PROCENTRY_FLAG_EN) ? "usable" : "unusable" );
+
+ printf( "\t %d\t %d\t %d",
+ (entry.cpuSignature >> 8) & 0x0f,
+ (entry.cpuSignature >> 4) & 0x0f,
+ entry.cpuSignature & 0x0f );
+
+ printf( "\t 0x%04x\n", entry.featureFlags );
+}
+
+
+/*
+ *
+ */
+static int
+lookupBusType( char* name )
+{
+ int x;
+
+ for ( x = 0; x < MAX_BUSTYPE; ++x )
+ if ( strcmp( busTypeTable[ x ].name, name ) == 0 )
+ return busTypeTable[ x ].type;
+
+ return UNKNOWN_BUSTYPE;
+}
+
+
+static void
+busEntry( void )
+{
+ int x;
+ char name[ 8 ];
+ char c;
+ BusEntry entry;
+
+ /* read it into local memory */
+ readEntry( &entry, sizeof( entry ) );
+
+ /* count it */
+ ++nbus;
+
+ printf( "\t\t%2d", entry.busID );
+ printf( "\t " ); pnstr( entry.busType, 6 ); printf( "\n" );
+
+ for ( x = 0; x < 6; ++x ) {
+ if ( (c = entry.busType[ x ]) == ' ' )
+ break;
+ name[ x ] = c;
+ }
+ name[ x ] = '\0';
+ busses[ entry.busID ] = lookupBusType( name );
+}
+
+
+static void
+ioApicEntry( void )
+{
+ IOApicEntry entry;
+
+ /* read it into local memory */
+ readEntry( &entry, sizeof( entry ) );
+
+ /* count it */
+ ++napic;
+
+ printf( "\t\t%2d", entry.apicID );
+ printf( "\t 0x%02x", entry.apicVersion );
+ printf( "\t %s",
+ (entry.apicFlags & IOAPICENTRY_FLAG_EN) ? "usable" : "unusable" );
+ printf( "\t\t 0x%x\n", entry.apicAddress );
+
+ apics[ entry.apicID ] = entry.apicID;
+}
+
+
+const char* intTypes[] = {
+ "INT", "NMI", "SMI", "ExtINT"
+};
+
+const char* polarityMode[] = {
+ "conforms", "active-hi", "reserved", "active-lo"
+};
+const char* triggerMode[] = {
+ "conforms", "edge", "reserved", "level"
+};
+
+static void
+intEntry( void )
+{
+ IntEntry entry;
+
+ /* read it into local memory */
+ readEntry( &entry, sizeof( entry ) );
+
+ /* count it */
+ if ( (int)entry.type == 3 )
+ ++nintr;
+
+ printf( "\t\t%s", intTypes[ (int)entry.intType ] );
+
+ printf( "\t%9s", polarityMode[ (int)entry.intFlags & 0x03 ] );
+ printf( "%12s", triggerMode[ ((int)entry.intFlags >> 2) & 0x03 ] );
+
+ printf( "\t %5d", (int)entry.srcBusID );
+ if ( busses[ (int)entry.srcBusID ] == PCI )
+ printf( "\t%2d:%c",
+ ((int)entry.srcBusIRQ >> 2) & 0x1f,
+ ((int)entry.srcBusIRQ & 0x03) + 'A' );
+ else
+ printf( "\t %3d", (int)entry.srcBusIRQ );
+ printf( "\t %6d", (int)entry.dstApicID );
+ printf( "\t %3d\n", (int)entry.dstApicINT );
+}
+
+
+static void
+sasEntry( void )
+{
+ SasEntry entry;
+
+ /* read it into local memory */
+ readEntry( &entry, sizeof( entry ) );
+
+ printf( "--\n%s\n", extendedtableEntryTypes[entry.type - 128].name );
+ printf( " bus ID: %d", entry.busID );
+ printf( " address type: " );
+ switch ( entry.addressType ) {
+ case 0:
+ printf( "I/O address\n" );
+ break;
+ case 1:
+ printf( "memory address\n" );
+ break;
+ case 2:
+ printf( "prefetch address\n" );
+ break;
+ default:
+ printf( "UNKNOWN type\n" );
+ break;
+ }
+
+ printf( " address base: 0x%llx\n", (long long)entry.addressBase );
+ printf( " address range: 0x%llx\n", (long long)entry.addressLength );
+}
+
+
+static void
+bhdEntry( void )
+{
+ BhdEntry entry;
+
+ /* read it into local memory */
+ readEntry( &entry, sizeof( entry ) );
+
+ printf( "--\n%s\n", extendedtableEntryTypes[entry.type - 128].name );
+ printf( " bus ID: %d", entry.busID );
+ printf( " bus info: 0x%02x", entry.busInfo );
+ printf( " parent bus ID: %d\n", entry.busParent );
+}
+
+
+static void
+cbasmEntry( void )
+{
+ CbasmEntry entry;
+
+ /* read it into local memory */
+ readEntry( &entry, sizeof( entry ) );
+
+ printf( "--\n%s\n", extendedtableEntryTypes[entry.type - 128].name );
+ printf( " bus ID: %d", entry.busID );
+ printf( " address modifier: %s\n", (entry.addressMod & 0x01) ?
+ "subtract" : "add" );
+ printf( " predefined range: 0x%08x\n", entry.predefinedRange );
+}
+
+
+/*
+ * do a dmesg output
+ */
+static void
+doDmesg( void )
+{
+ puts( SEP_LINE );
+
+ printf( "dmesg output:\n\n" );
+ fflush( stdout );
+ system( "dmesg" );
+}
+
+
+/*
+ *
+ */
+static void
+pnstr( char* s, int c )
+{
+ char string[ MAXPNSTR + 1 ];
+
+ if ( c > MAXPNSTR )
+ c = MAXPNSTR;
+ strncpy( string, s, c );
+ string[ c ] = '\0';
+ printf( "%s", string );
+}
diff --git a/usr.sbin/mtest/Makefile b/usr.sbin/mtest/Makefile
new file mode 100644
index 0000000..95bbe12
--- /dev/null
+++ b/usr.sbin/mtest/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+PROG= mtest
+MAN= mtest.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mtest/mtest.8 b/usr.sbin/mtest/mtest.8
new file mode 100644
index 0000000..86a6f00
--- /dev/null
+++ b/usr.sbin/mtest/mtest.8
@@ -0,0 +1,158 @@
+.\"
+.\" Copyright (c) 2007-2009 Bruce Simpson.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 March 3, 2009
+.Os
+.Dt MTEST 8
+.Sh NAME
+.Nm mtest
+.Nd test multicast membership socket operations and ioctls
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+utility
+is a small program for testing the multicast membership socket operations
+and ioctls.
+It accepts the following commands, interactively:
+.Bl -tag -width "a ifname e.e.e.e e.e.e.e" -compact -offset indent
+.It Ic j Ar g.g.g.g Ar i.i.i.i Op Ar s.s.s.s
+Join the IP group address
+.Ar g.g.g.g
+on the interface with address
+.Ar i.i.i.i .
+.Pp
+If an optional source
+.Ar s.s.s.s
+is specified, a source-specific join will be performed;
+if
+.Nm
+is already a member of the group, the source
+will be added to its filter list.
+.Pp
+.Ar i.i.i.i
+may be specified as 0.0.0.0 to use the default interface,
+although this is legacy behaviour and is not recommended,
+as group memberships are keyed to each individual link.
+.It Ic l Ar g.g.g.g Ar i.i.i.i Op Ar s.s.s.s
+Leave the IP group address
+.Ar g.g.g.g
+on the interface with address
+.Ar i.i.i.i .
+If a source
+.Ar s.s.s.s
+is specified, only that source will be left.
+.It Ic a Ar ifname Ar e.e.e.e.e.e
+Join the Ethernet group address
+.Ar e.e.e.e.e.e
+on interface
+.Ar ifname .
+.It Ic d Ar ifname Ar e.e.e.e.e.e
+Leave the Ethernet group address
+.Ar e.e.e.e.e.e
+on interface
+.Ar ifname .
+.It Ic m Ar ifname Ar 1/0
+Set or reset ALLMULTI mode on interface
+.Ar ifname .
+.It Ic p Ar ifname Ar 1/0
+Set or reset promiscuous mode on interface
+.Ar ifname .
+.\"
+.It Ic i Ar g.g.g.g Ar i.i.i.i Ar n Ar x.x.x.x ...
+Set the socket with group membership of
+.Ar g.g.g.g
+on IPv4 address
+.Ar i.i.i.i
+to include filter mode, and add
+.Ar n
+sources beginning with
+.Ar x.x.x.x
+to the inclusion filter list.
+.\"
+.It Ic e Ar g.g.g.g Ar i.i.i.i Ar n Ar x.x.x.x ...
+Set the socket with group membership of
+.Ar g.g.g.g
+on IPv4 address
+.Ar i.i.i.i
+to exclude filter mode, and add
+.Ar n
+sources beginning with
+.Ar x.x.x.x
+to the exclusion filter list.
+.\"
+.It Ic t Ar g.g.g.g Ar i.i.i.i Ar s.s.s.s
+Set the socket with group membership of
+.Ar g.g.g.g
+on IPv4 address
+.Ar i.i.i.i
+to block traffic from source
+.Ar s.s.s.s .
+.\"
+.It Ic b Ar g.g.g.g Ar i.i.i.i Ar s.s.s.s
+Set the socket with group membership of
+.Ar g.g.g.g
+on IPv4 address
+.Ar i.i.i.i
+to allow traffic from source
+.Ar s.s.s.s .
+.\"
+.It Ic g Ar g.g.g.g Ar i.i.i.i Ar n
+Print
+.Ar n
+source filter entries for group
+.An g.g.g.g
+on IPv4 address
+.An i.i.i.i .
+.\"
+.It Ic f Ar filename
+Read commands from the file
+.Ar filename .
+.It Ic s Ar n
+Sleep for
+.Ar n
+seconds.
+.It Ic ?\&
+List legal commands.
+.It Ic q
+Quit the program.
+.El
+.Sh SEE ALSO
+.Rs
+.%A D. Thaler
+.%A B. Fenner
+.%A B. Quinn
+.%T "Socket Interface Extensions for Multicast Filters"
+.%O RFC 3678
+.Re
+.Sh AUTHORS
+.An -split
+.An "Bruce Simpson"
+.An "Steve Deering"
+.An "Wilbert De Graaf"
diff --git a/usr.sbin/mtest/mtest.c b/usr.sbin/mtest/mtest.c
new file mode 100644
index 0000000..04b860a
--- /dev/null
+++ b/usr.sbin/mtest/mtest.c
@@ -0,0 +1,409 @@
+/*-
+ * Copyright (c) 2007-2009 Bruce Simpson.
+ * Copyright (c) 2000 Wilbert De Graaf.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Diagnostic and test utility for IPv4 multicast sockets.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/ioctl.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/ethernet.h>
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <err.h>
+#include <unistd.h>
+
+static void process_file(char *, int);
+static void process_cmd(char*, int, FILE *fp);
+static void usage(void);
+
+#define MAX_ADDRS 20
+#define STR_SIZE 20
+#define LINE_LENGTH 80
+
+static int
+inaddr_cmp(const void *a, const void *b)
+{
+ return ((int)((const struct in_addr *)a)->s_addr -
+ ((const struct in_addr *)b)->s_addr);
+}
+
+int
+main(int argc, char **argv)
+{
+ char line[LINE_LENGTH];
+ char *p;
+ int i, s;
+
+ s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (s == -1)
+ err(1, "can't open socket");
+
+ if (argc < 2) {
+ if (isatty(STDIN_FILENO)) {
+ printf("multicast membership test program; "
+ "enter ? for list of commands\n");
+ }
+ do {
+ if (fgets(line, sizeof(line), stdin) != NULL) {
+ if (line[0] != 'f')
+ process_cmd(line, s, stdin);
+ else {
+ /* Get the filename */
+ for (i = 1; isblank(line[i]); i++);
+ if ((p = (char*)strchr(line, '\n'))
+ != NULL)
+ *p = '\0';
+ process_file(&line[i], s);
+ }
+ }
+ } while (!feof(stdin));
+ } else {
+ for (i = 1; i < argc; i++) {
+ process_file(argv[i], s);
+ }
+ }
+
+ exit (0);
+}
+
+static void
+process_file(char *fname, int s)
+{
+ char line[80];
+ FILE *fp;
+ char *lineptr;
+
+ fp = fopen(fname, "r");
+ if (fp == NULL) {
+ warn("fopen");
+ return;
+ }
+
+ /* Skip comments and empty lines. */
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ lineptr = line;
+ while (isblank(*lineptr))
+ lineptr++;
+ if (*lineptr != '#' && *lineptr != '\n')
+ process_cmd(lineptr, s, fp);
+ }
+
+ fclose(fp);
+}
+
+static void
+process_cmd(char *cmd, int s, FILE *fp __unused)
+{
+ char str1[STR_SIZE];
+ char str2[STR_SIZE];
+ char str3[STR_SIZE];
+ struct in_addr sources[MAX_ADDRS];
+ struct ifreq ifr;
+ struct ip_mreq imr;
+ struct ip_mreq_source imrs;
+ char *line;
+ uint32_t fmode;
+ int i, n, opt, f, flags;
+
+ line = cmd;
+ while (isblank(*++line))
+ ; /* Skip whitespace. */
+
+ switch (*cmd) {
+ case '?':
+ usage();
+ break;
+
+ case 'q':
+ close(s);
+ exit(0);
+
+ case 's':
+ if ((sscanf(line, "%d", &n) != 1) || (n < 1)) {
+ printf("-1\n");
+ break;
+ }
+ sleep(n);
+ printf("ok\n");
+ break;
+
+ case 'j':
+ case 'l':
+ str3[0] = '\0';
+ sscanf(line, "%s %s %s", str1, str2, str3);
+ if ((imrs.imr_sourceaddr.s_addr = inet_addr(str3)) !=
+ INADDR_NONE) {
+ /*
+ * inclusive mode join with source, possibly
+ * on existing membership.
+ */
+ if (((imrs.imr_multiaddr.s_addr = inet_addr(str1)) ==
+ INADDR_NONE) ||
+ ((imrs.imr_interface.s_addr = inet_addr(str2)) ==
+ INADDR_NONE)) {
+ printf("-1\n");
+ break;
+ }
+ opt = (*cmd == 'j') ? IP_ADD_SOURCE_MEMBERSHIP :
+ IP_DROP_SOURCE_MEMBERSHIP;
+ if (setsockopt( s, IPPROTO_IP, opt, &imrs,
+ sizeof(imrs)) != 0) {
+ warn("setsockopt %s", (*cmd == 'j') ?
+ "IP_ADD_SOURCE_MEMBERSHIP" :
+ "IP_DROP_SOURCE_MEMBERSHIP");
+ } else {
+ printf("ok\n");
+ }
+ } else {
+ /* exclusive mode join w/o source. */
+ if (((imr.imr_multiaddr.s_addr = inet_addr(str1)) ==
+ INADDR_NONE) ||
+ ((imr.imr_interface.s_addr = inet_addr(str2)) ==
+ INADDR_NONE)) {
+ printf("-1\n");
+ break;
+ }
+ opt = (*cmd == 'j') ? IP_ADD_MEMBERSHIP :
+ IP_DROP_MEMBERSHIP;
+ if (setsockopt( s, IPPROTO_IP, opt, &imr,
+ sizeof(imr)) != 0) {
+ warn("setsockopt %s", (*cmd == 'j') ?
+ "IP_ADD_MEMBERSHIP" :
+ "IP_DROP_MEMBERSHIP");
+ } else {
+ printf("ok\n");
+ }
+ }
+ break;
+
+ case 'a':
+ case 'd': {
+ struct sockaddr_dl *dlp;
+ struct ether_addr *ep;
+
+ memset(&ifr, 0, sizeof(struct ifreq));
+ dlp = (struct sockaddr_dl *)&ifr.ifr_addr;
+ dlp->sdl_len = sizeof(struct sockaddr_dl);
+ dlp->sdl_family = AF_LINK;
+ dlp->sdl_index = 0;
+ dlp->sdl_nlen = 0;
+ dlp->sdl_alen = ETHER_ADDR_LEN;
+ dlp->sdl_slen = 0;
+ if (sscanf(line, "%s %s", str1, str2) != 2) {
+ warnc(EINVAL, "sscanf");
+ break;
+ }
+ ep = ether_aton(str2);
+ if (ep == NULL) {
+ warnc(EINVAL, "ether_aton");
+ break;
+ }
+ strlcpy(ifr.ifr_name, str1, IF_NAMESIZE);
+ memcpy(LLADDR(dlp), ep, ETHER_ADDR_LEN);
+ if (ioctl(s, (*cmd == 'a') ? SIOCADDMULTI : SIOCDELMULTI,
+ &ifr) == -1)
+ warn("ioctl SIOCADDMULTI/SIOCDELMULTI");
+ else
+ printf("ok\n");
+ break;
+ }
+
+ case 'm':
+ printf("warning: IFF_ALLMULTI cannot be set from userland "
+ "in FreeBSD; command ignored.\n");
+ break;
+
+ case 'p':
+ if (sscanf(line, "%s %u", ifr.ifr_name, &f) != 2) {
+ printf("-1\n");
+ break;
+ }
+ if (ioctl(s, SIOCGIFFLAGS, &ifr) == -1) {
+ warn("ioctl SIOCGIFFLAGS");
+ break;
+ }
+ flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16);
+ opt = IFF_PPROMISC;
+ if (f == 0) {
+ flags &= ~opt;
+ } else {
+ flags |= opt;
+ }
+ ifr.ifr_flags = flags & 0xffff;
+ ifr.ifr_flagshigh = flags >> 16;
+ if (ioctl(s, SIOCSIFFLAGS, &ifr) == -1)
+ warn("ioctl SIOCGIFFLAGS");
+ else
+ printf( "changed to 0x%08x\n", flags );
+ break;
+
+ /*
+ * Set the socket to include or exclude filter mode, and
+ * add some sources to the filterlist, using the full-state,
+ * or advanced api.
+ */
+ case 'i':
+ case 'e':
+ n = 0;
+ fmode = (*cmd == 'i') ? MCAST_INCLUDE : MCAST_EXCLUDE;
+ if ((sscanf(line, "%s %s %d", str1, str2, &n)) != 3) {
+ printf("-1\n");
+ break;
+ }
+ /* recycle imrs struct for convenience */
+ if (((imrs.imr_multiaddr.s_addr = inet_addr(str1)) ==
+ INADDR_NONE) ||
+ ((imrs.imr_interface.s_addr = inet_addr(str2)) ==
+ INADDR_NONE) || (n < 0 || n > MAX_ADDRS)) {
+ printf("-1\n");
+ break;
+ }
+ for (i = 0; i < n; i++) {
+ fgets(str1, sizeof(str1), fp);
+ if ((sources[i].s_addr = inet_addr(str1)) ==
+ INADDR_NONE) {
+ printf("-1\n");
+ return;
+ }
+ }
+ if (setipv4sourcefilter(s, imrs.imr_interface,
+ imrs.imr_multiaddr, fmode, n, sources) != 0)
+ warn("getipv4sourcefilter");
+ else
+ printf("ok\n");
+ break;
+
+ /*
+ * Allow or block traffic from a source, using the
+ * delta based api.
+ */
+ case 't':
+ case 'b':
+ sscanf(line, "%s %s %s", str1, str2, str3);
+ if (((imrs.imr_multiaddr.s_addr = inet_addr(str1)) ==
+ INADDR_NONE) ||
+ ((imrs.imr_interface.s_addr = inet_addr(str2)) ==
+ INADDR_NONE) ||
+ ((imrs.imr_sourceaddr.s_addr = inet_addr(str3)) ==
+ INADDR_NONE)) {
+ printf("-1\n");
+ break;
+ }
+ /* First determine our current filter mode. */
+ n = 0;
+ if (getipv4sourcefilter(s, imrs.imr_interface,
+ imrs.imr_multiaddr, &fmode, &n, NULL) != 0) {
+ warn("getipv4sourcefilter");
+ break;
+ }
+ if (fmode == MCAST_EXCLUDE) {
+ /* Any source */
+ opt = (*cmd == 't') ? IP_UNBLOCK_SOURCE :
+ IP_BLOCK_SOURCE;
+ } else {
+ /* Controlled source */
+ opt = (*cmd == 't') ? IP_ADD_SOURCE_MEMBERSHIP :
+ IP_DROP_SOURCE_MEMBERSHIP;
+ }
+ if (setsockopt(s, IPPROTO_IP, opt, &imrs, sizeof(imrs)) == -1)
+ warn("ioctl IP_ADD_SOURCE_MEMBERSHIP/IP_DROP_SOURCE_MEMBERSHIP/IP_UNBLOCK_SOURCE/IP_BLOCK_SOURCE");
+ else
+ printf("ok\n");
+ break;
+
+ case 'g':
+ if ((sscanf(line, "%s %s %d", str1, str2, &n)) != 3) {
+ printf("-1\n");
+ break;
+ }
+ /* recycle imrs struct for convenience */
+ if (((imrs.imr_multiaddr.s_addr = inet_addr(str1)) ==
+ INADDR_NONE) ||
+ ((imrs.imr_interface.s_addr = inet_addr(str2)) ==
+ INADDR_NONE) || (n < 0 || n > MAX_ADDRS)) {
+ printf("-1\n");
+ break;
+ }
+ if (getipv4sourcefilter(s, imrs.imr_interface,
+ imrs.imr_multiaddr, &fmode, &n, sources) != 0) {
+ warn("getipv4sourcefilter");
+ break;
+ }
+ printf("%s\n", (fmode == MCAST_INCLUDE) ? "include" :
+ "exclude");
+ printf("%d\n", n);
+ qsort(sources, n, sizeof(struct in_addr), &inaddr_cmp);
+ for (i = 0; i < n; i++)
+ printf("%s\n", inet_ntoa(sources[i]));
+ break;
+
+ case '\n':
+ break;
+ default:
+ printf("invalid command\n");
+ break;
+ }
+}
+
+static void
+usage(void)
+{
+
+ printf("j g.g.g.g i.i.i.i [s.s.s.s] - join IP multicast group\n");
+ printf("l g.g.g.g i.i.i.i [s.s.s.s] - leave IP multicast group\n");
+ printf("a ifname e.e.e.e.e.e - add ether multicast address\n");
+ printf("d ifname e.e.e.e.e.e - delete ether multicast address\n");
+ printf("m ifname 1/0 - set/clear ether allmulti flag\n");
+ printf("p ifname 1/0 - set/clear ether promisc flag\n");
+ printf("i g.g.g.g i.i.i.i n - set n include mode src filter\n");
+ printf("e g.g.g.g i.i.i.i n - set n exclude mode src filter\n");
+ printf("t g.g.g.g i.i.i.i s.s.s.s - allow traffic from src\n");
+ printf("b g.g.g.g i.i.i.i s.s.s.s - block traffic from src\n");
+ printf("g g.g.g.g i.i.i.i n - get and show n src filters\n");
+ printf("f filename - read command(s) from file\n");
+ printf("s seconds - sleep for some time\n");
+ printf("q - quit\n");
+}
diff --git a/usr.sbin/mtree/Makefile b/usr.sbin/mtree/Makefile
new file mode 100644
index 0000000..b66c8bb
--- /dev/null
+++ b/usr.sbin/mtree/Makefile
@@ -0,0 +1,17 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../usr.bin/cksum
+
+PROG= mtree
+MAN= mtree.8 mtree.5
+SRCS= compare.c crc.c create.c excludes.c misc.c mtree.c spec.c verify.c
+SRCS+= specspec.c
+
+WARNS?= 4
+
+CFLAGS+= -DMD5 -DSHA1 -DRMD160 -DSHA256
+DPADD= ${LIBMD}
+LDADD= -lmd
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mtree/compare.c b/usr.sbin/mtree/compare.c
new file mode 100644
index 0000000..44556d6
--- /dev/null
+++ b/usr.sbin/mtree/compare.c
@@ -0,0 +1,388 @@
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)compare.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fts.h>
+#ifdef MD5
+#include <md5.h>
+#endif
+#ifdef RMD160
+#include <ripemd.h>
+#endif
+#ifdef SHA1
+#include <sha.h>
+#endif
+#ifdef SHA256
+#include <sha256.h>
+#endif
+#include <stdint.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include <vis.h>
+
+#include "mtree.h"
+#include "extern.h"
+
+#define INDENTNAMELEN 8
+#define LABEL \
+ if (!label++) { \
+ len = printf("%s changed\n", RP(p)); \
+ tab = "\t"; \
+ }
+
+int
+compare(char *name __unused, NODE *s, FTSENT *p)
+{
+ struct timeval tv[2];
+ uint32_t val;
+ int fd, label;
+ off_t len;
+ char *cp;
+ const char *tab = "";
+ char *fflags;
+
+ label = 0;
+ switch(s->type) {
+ case F_BLOCK:
+ if (!S_ISBLK(p->fts_statp->st_mode))
+ goto typeerr;
+ break;
+ case F_CHAR:
+ if (!S_ISCHR(p->fts_statp->st_mode))
+ goto typeerr;
+ break;
+ case F_DIR:
+ if (!S_ISDIR(p->fts_statp->st_mode))
+ goto typeerr;
+ break;
+ case F_FIFO:
+ if (!S_ISFIFO(p->fts_statp->st_mode))
+ goto typeerr;
+ break;
+ case F_FILE:
+ if (!S_ISREG(p->fts_statp->st_mode))
+ goto typeerr;
+ break;
+ case F_LINK:
+ if (!S_ISLNK(p->fts_statp->st_mode))
+ goto typeerr;
+ break;
+ case F_SOCK:
+ if (!S_ISSOCK(p->fts_statp->st_mode)) {
+typeerr: LABEL;
+ (void)printf("\ttype expected %s found %s\n",
+ ftype(s->type), inotype(p->fts_statp->st_mode));
+ return (label);
+ }
+ break;
+ }
+ /* Set the uid/gid first, then set the mode. */
+ if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) {
+ LABEL;
+ (void)printf("%suser expected %lu found %lu",
+ tab, (u_long)s->st_uid, (u_long)p->fts_statp->st_uid);
+ if (uflag)
+ if (chown(p->fts_accpath, s->st_uid, -1))
+ (void)printf(" not modified: %s\n",
+ strerror(errno));
+ else
+ (void)printf(" modified\n");
+ else
+ (void)printf("\n");
+ tab = "\t";
+ }
+ if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) {
+ LABEL;
+ (void)printf("%sgid expected %lu found %lu",
+ tab, (u_long)s->st_gid, (u_long)p->fts_statp->st_gid);
+ if (uflag)
+ if (chown(p->fts_accpath, -1, s->st_gid))
+ (void)printf(" not modified: %s\n",
+ strerror(errno));
+ else
+ (void)printf(" modified\n");
+ else
+ (void)printf("\n");
+ tab = "\t";
+ }
+ if (s->flags & F_MODE &&
+ !S_ISLNK(p->fts_statp->st_mode) &&
+ s->st_mode != (p->fts_statp->st_mode & MBITS)) {
+ LABEL;
+ (void)printf("%spermissions expected %#o found %#o",
+ tab, s->st_mode, p->fts_statp->st_mode & MBITS);
+ if (uflag)
+ if (chmod(p->fts_accpath, s->st_mode))
+ (void)printf(" not modified: %s\n",
+ strerror(errno));
+ else
+ (void)printf(" modified\n");
+ else
+ (void)printf("\n");
+ tab = "\t";
+ }
+ if (s->flags & F_NLINK && s->type != F_DIR &&
+ s->st_nlink != p->fts_statp->st_nlink) {
+ LABEL;
+ (void)printf("%slink_count expected %u found %u\n",
+ tab, s->st_nlink, p->fts_statp->st_nlink);
+ tab = "\t";
+ }
+ if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size &&
+ !S_ISDIR(p->fts_statp->st_mode)) {
+ LABEL;
+ (void)printf("%ssize expected %jd found %jd\n", tab,
+ (intmax_t)s->st_size, (intmax_t)p->fts_statp->st_size);
+ tab = "\t";
+ }
+ /*
+ * XXX
+ * Catches nano-second differences, but doesn't display them.
+ */
+ if ((s->flags & F_TIME) &&
+ ((s->st_mtimespec.tv_sec != p->fts_statp->st_mtimespec.tv_sec) ||
+ (s->st_mtimespec.tv_nsec != p->fts_statp->st_mtimespec.tv_nsec))) {
+ LABEL;
+ (void)printf("%smodification time expected %.24s ",
+ tab, ctime(&s->st_mtimespec.tv_sec));
+ (void)printf("found %.24s",
+ ctime(&p->fts_statp->st_mtimespec.tv_sec));
+ if (uflag) {
+ tv[0].tv_sec = s->st_mtimespec.tv_sec;
+ tv[0].tv_usec = s->st_mtimespec.tv_nsec / 1000;
+ tv[1] = tv[0];
+ if (utimes(p->fts_accpath, tv))
+ (void)printf(" not modified: %s\n",
+ strerror(errno));
+ else
+ (void)printf(" modified\n");
+ } else
+ (void)printf("\n");
+ tab = "\t";
+ }
+ if (s->flags & F_CKSUM) {
+ if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) {
+ LABEL;
+ (void)printf("%scksum: %s: %s\n",
+ tab, p->fts_accpath, strerror(errno));
+ tab = "\t";
+ } else if (crc(fd, &val, &len)) {
+ (void)close(fd);
+ LABEL;
+ (void)printf("%scksum: %s: %s\n",
+ tab, p->fts_accpath, strerror(errno));
+ tab = "\t";
+ } else {
+ (void)close(fd);
+ if (s->cksum != val) {
+ LABEL;
+ (void)printf("%scksum expected %lu found %lu\n",
+ tab, s->cksum, (unsigned long)val);
+ tab = "\t";
+ }
+ }
+ }
+ if ((s->flags & F_FLAGS) && s->st_flags != p->fts_statp->st_flags) {
+ LABEL;
+ fflags = flags_to_string(s->st_flags);
+ (void)printf("%sflags expected \"%s\"", tab, fflags);
+ free(fflags);
+
+ fflags = flags_to_string(p->fts_statp->st_flags);
+ (void)printf(" found \"%s\"", fflags);
+ free(fflags);
+
+ if (uflag)
+ if (chflags(p->fts_accpath, s->st_flags))
+ (void)printf(" not modified: %s\n",
+ strerror(errno));
+ else
+ (void)printf(" modified\n");
+ else
+ (void)printf("\n");
+ tab = "\t";
+ }
+#ifdef MD5
+ if (s->flags & F_MD5) {
+ char *new_digest, buf[33];
+
+ new_digest = MD5File(p->fts_accpath, buf);
+ if (!new_digest) {
+ LABEL;
+ printf("%sMD5: %s: %s\n", tab, p->fts_accpath,
+ strerror(errno));
+ tab = "\t";
+ } else if (strcmp(new_digest, s->md5digest)) {
+ LABEL;
+ printf("%sMD5 expected %s found %s\n", tab, s->md5digest,
+ new_digest);
+ tab = "\t";
+ }
+ }
+#endif /* MD5 */
+#ifdef SHA1
+ if (s->flags & F_SHA1) {
+ char *new_digest, buf[41];
+
+ new_digest = SHA1_File(p->fts_accpath, buf);
+ if (!new_digest) {
+ LABEL;
+ printf("%sSHA-1: %s: %s\n", tab, p->fts_accpath,
+ strerror(errno));
+ tab = "\t";
+ } else if (strcmp(new_digest, s->sha1digest)) {
+ LABEL;
+ printf("%sSHA-1 expected %s found %s\n",
+ tab, s->sha1digest, new_digest);
+ tab = "\t";
+ }
+ }
+#endif /* SHA1 */
+#ifdef RMD160
+ if (s->flags & F_RMD160) {
+ char *new_digest, buf[41];
+
+ new_digest = RIPEMD160_File(p->fts_accpath, buf);
+ if (!new_digest) {
+ LABEL;
+ printf("%sRIPEMD160: %s: %s\n", tab,
+ p->fts_accpath, strerror(errno));
+ tab = "\t";
+ } else if (strcmp(new_digest, s->rmd160digest)) {
+ LABEL;
+ printf("%sRIPEMD160 expected %s found %s\n",
+ tab, s->rmd160digest, new_digest);
+ tab = "\t";
+ }
+ }
+#endif /* RMD160 */
+#ifdef SHA256
+ if (s->flags & F_SHA256) {
+ char *new_digest, buf[65];
+
+ new_digest = SHA256_File(p->fts_accpath, buf);
+ if (!new_digest) {
+ LABEL;
+ printf("%sSHA-256: %s: %s\n", tab, p->fts_accpath,
+ strerror(errno));
+ tab = "\t";
+ } else if (strcmp(new_digest, s->sha256digest)) {
+ LABEL;
+ printf("%sSHA-256 expected %s found %s\n",
+ tab, s->sha256digest, new_digest);
+ tab = "\t";
+ }
+ }
+#endif /* SHA256 */
+
+ if (s->flags & F_SLINK &&
+ strcmp(cp = rlink(p->fts_accpath), s->slink)) {
+ LABEL;
+ (void)printf("%slink_ref expected %s found %s\n",
+ tab, s->slink, cp);
+ }
+ return (label);
+}
+
+const char *
+inotype(u_int type)
+{
+ switch(type & S_IFMT) {
+ case S_IFBLK:
+ return ("block");
+ case S_IFCHR:
+ return ("char");
+ case S_IFDIR:
+ return ("dir");
+ case S_IFIFO:
+ return ("fifo");
+ case S_IFREG:
+ return ("file");
+ case S_IFLNK:
+ return ("link");
+ case S_IFSOCK:
+ return ("socket");
+ default:
+ return ("unknown");
+ }
+ /* NOTREACHED */
+}
+
+const char *
+ftype(u_int type)
+{
+ switch(type) {
+ case F_BLOCK:
+ return ("block");
+ case F_CHAR:
+ return ("char");
+ case F_DIR:
+ return ("dir");
+ case F_FIFO:
+ return ("fifo");
+ case F_FILE:
+ return ("file");
+ case F_LINK:
+ return ("link");
+ case F_SOCK:
+ return ("socket");
+ default:
+ return ("unknown");
+ }
+ /* NOTREACHED */
+}
+
+char *
+rlink(char *name)
+{
+ static char lbuf[MAXPATHLEN * 4];
+ int len;
+ char tbuf[MAXPATHLEN];
+
+ if ((len = readlink(name, tbuf, sizeof(tbuf) - 1)) == -1)
+ err(1, "line %d: %s", lineno, name);
+ tbuf[len] = '\0';
+ strvis(lbuf, tbuf, VIS_WHITE | VIS_OCTAL);
+ return (lbuf);
+}
diff --git a/usr.sbin/mtree/create.c b/usr.sbin/mtree/create.c
new file mode 100644
index 0000000..eee5037
--- /dev/null
+++ b/usr.sbin/mtree/create.c
@@ -0,0 +1,428 @@
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)create.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <grp.h>
+#ifdef MD5
+#include <md5.h>
+#endif
+#ifdef SHA1
+#include <sha.h>
+#endif
+#ifdef RMD160
+#include <ripemd.h>
+#endif
+#ifdef SHA256
+#include <sha256.h>
+#endif
+#include <pwd.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include <vis.h>
+#include "mtree.h"
+#include "extern.h"
+
+#define INDENTNAMELEN 15
+#define MAXLINELEN 80
+
+static gid_t gid;
+static uid_t uid;
+static mode_t mode;
+static u_long flags = 0xffffffff;
+
+static int dsort(const FTSENT * const *, const FTSENT * const *);
+static void output(int, int *, const char *, ...) __printflike(3, 4);
+static int statd(FTS *, FTSENT *, uid_t *, gid_t *, mode_t *, u_long *);
+static void statf(int, FTSENT *);
+
+void
+cwalk(void)
+{
+ FTS *t;
+ FTSENT *p;
+ time_t cl;
+ char *argv[2], host[MAXHOSTNAMELEN];
+ char dot[] = ".";
+ int indent = 0;
+
+ if (!nflag) {
+ (void)time(&cl);
+ (void)gethostname(host, sizeof(host));
+ (void)printf(
+ "#\t user: %s\n#\tmachine: %s\n",
+ getlogin(), host);
+ (void)printf(
+ "#\t tree: %s\n#\t date: %s",
+ fullpath, ctime(&cl));
+ }
+
+ argv[0] = dot;
+ argv[1] = NULL;
+ if ((t = fts_open(argv, ftsoptions, dsort)) == NULL)
+ err(1, "fts_open()");
+ while ((p = fts_read(t))) {
+ if (iflag)
+ indent = p->fts_level * 4;
+ if (check_excludes(p->fts_name, p->fts_path)) {
+ fts_set(t, p, FTS_SKIP);
+ continue;
+ }
+ switch(p->fts_info) {
+ case FTS_D:
+ if (!dflag)
+ (void)printf("\n");
+ if (!nflag)
+ (void)printf("# %s\n", p->fts_path);
+ statd(t, p, &uid, &gid, &mode, &flags);
+ statf(indent, p);
+ break;
+ case FTS_DP:
+ if (!nflag && (p->fts_level > 0))
+ (void)printf("%*s# %s\n", indent, "", p->fts_path);
+ (void)printf("%*s..\n", indent, "");
+ if (!dflag)
+ (void)printf("\n");
+ break;
+ case FTS_DNR:
+ case FTS_ERR:
+ case FTS_NS:
+ warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+ break;
+ default:
+ if (!dflag)
+ statf(indent, p);
+ break;
+
+ }
+ }
+ (void)fts_close(t);
+ if (sflag && keys & F_CKSUM)
+ warnx("%s checksum: %lu", fullpath, (unsigned long)crc_total);
+}
+
+static void
+statf(int indent, FTSENT *p)
+{
+ struct group *gr;
+ struct passwd *pw;
+ uint32_t val;
+ off_t len;
+ int fd, offset;
+ char *fflags;
+ char *escaped_name;
+
+ escaped_name = calloc(1, p->fts_namelen * 4 + 1);
+ if (escaped_name == NULL)
+ errx(1, "statf(): calloc() failed");
+ strvis(escaped_name, p->fts_name, VIS_WHITE | VIS_OCTAL | VIS_GLOB);
+
+ if (iflag || S_ISDIR(p->fts_statp->st_mode))
+ offset = printf("%*s%s", indent, "", escaped_name);
+ else
+ offset = printf("%*s %s", indent, "", escaped_name);
+
+ free(escaped_name);
+
+ if (offset > (INDENTNAMELEN + indent))
+ offset = MAXLINELEN;
+ else
+ offset += printf("%*s", (INDENTNAMELEN + indent) - offset, "");
+
+ if (!S_ISREG(p->fts_statp->st_mode) && !dflag)
+ output(indent, &offset, "type=%s", inotype(p->fts_statp->st_mode));
+ if (p->fts_statp->st_uid != uid) {
+ if (keys & F_UNAME) {
+ pw = getpwuid(p->fts_statp->st_uid);
+ if (pw != NULL)
+ output(indent, &offset, "uname=%s", pw->pw_name);
+ else if (wflag)
+ warnx("Could not get uname for uid=%u",
+ p->fts_statp->st_uid);
+ else
+ errx(1,
+ "Could not get uname for uid=%u",
+ p->fts_statp->st_uid);
+ }
+ if (keys & F_UID)
+ output(indent, &offset, "uid=%u", p->fts_statp->st_uid);
+ }
+ if (p->fts_statp->st_gid != gid) {
+ if (keys & F_GNAME) {
+ gr = getgrgid(p->fts_statp->st_gid);
+ if (gr != NULL)
+ output(indent, &offset, "gname=%s", gr->gr_name);
+ else if (wflag)
+ warnx("Could not get gname for gid=%u",
+ p->fts_statp->st_gid);
+ else
+ errx(1,
+ "Could not get gname for gid=%u",
+ p->fts_statp->st_gid);
+ }
+ if (keys & F_GID)
+ output(indent, &offset, "gid=%u", p->fts_statp->st_gid);
+ }
+ if (keys & F_MODE && (p->fts_statp->st_mode & MBITS) != mode)
+ output(indent, &offset, "mode=%#o", p->fts_statp->st_mode & MBITS);
+ if (keys & F_NLINK && p->fts_statp->st_nlink != 1)
+ output(indent, &offset, "nlink=%u", p->fts_statp->st_nlink);
+ if (keys & F_SIZE)
+ output(indent, &offset, "size=%jd",
+ (intmax_t)p->fts_statp->st_size);
+ if (keys & F_TIME)
+ output(indent, &offset, "time=%ld.%09ld",
+ (long)p->fts_statp->st_mtimespec.tv_sec,
+ p->fts_statp->st_mtimespec.tv_nsec);
+ if (keys & F_CKSUM && S_ISREG(p->fts_statp->st_mode)) {
+ if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0 ||
+ crc(fd, &val, &len))
+ err(1, "%s", p->fts_accpath);
+ (void)close(fd);
+ output(indent, &offset, "cksum=%lu", (unsigned long)val);
+ }
+#ifdef MD5
+ if (keys & F_MD5 && S_ISREG(p->fts_statp->st_mode)) {
+ char *digest, buf[33];
+
+ digest = MD5File(p->fts_accpath, buf);
+ if (!digest)
+ err(1, "%s", p->fts_accpath);
+ output(indent, &offset, "md5digest=%s", digest);
+ }
+#endif /* MD5 */
+#ifdef SHA1
+ if (keys & F_SHA1 && S_ISREG(p->fts_statp->st_mode)) {
+ char *digest, buf[41];
+
+ digest = SHA1_File(p->fts_accpath, buf);
+ if (!digest)
+ err(1, "%s", p->fts_accpath);
+ output(indent, &offset, "sha1digest=%s", digest);
+ }
+#endif /* SHA1 */
+#ifdef RMD160
+ if (keys & F_RMD160 && S_ISREG(p->fts_statp->st_mode)) {
+ char *digest, buf[41];
+
+ digest = RIPEMD160_File(p->fts_accpath, buf);
+ if (!digest)
+ err(1, "%s", p->fts_accpath);
+ output(indent, &offset, "ripemd160digest=%s", digest);
+ }
+#endif /* RMD160 */
+#ifdef SHA256
+ if (keys & F_SHA256 && S_ISREG(p->fts_statp->st_mode)) {
+ char *digest, buf[65];
+
+ digest = SHA256_File(p->fts_accpath, buf);
+ if (!digest)
+ err(1, "%s", p->fts_accpath);
+ output(indent, &offset, "sha256digest=%s", digest);
+ }
+#endif /* SHA256 */
+ if (keys & F_SLINK &&
+ (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE))
+ output(indent, &offset, "link=%s", rlink(p->fts_accpath));
+ if (keys & F_FLAGS && p->fts_statp->st_flags != flags) {
+ fflags = flags_to_string(p->fts_statp->st_flags);
+ output(indent, &offset, "flags=%s", fflags);
+ free(fflags);
+ }
+ (void)putchar('\n');
+}
+
+#define MAXGID 5000
+#define MAXUID 5000
+#define MAXMODE MBITS + 1
+#define MAXFLAGS 256
+#define MAXS 16
+
+static int
+statd(FTS *t, FTSENT *parent, uid_t *puid, gid_t *pgid, mode_t *pmode, u_long *pflags)
+{
+ FTSENT *p;
+ gid_t sgid;
+ uid_t suid;
+ mode_t smode;
+ u_long sflags;
+ struct group *gr;
+ struct passwd *pw;
+ gid_t savegid = *pgid;
+ uid_t saveuid = *puid;
+ mode_t savemode = *pmode;
+ u_long saveflags = *pflags;
+ u_short maxgid, maxuid, maxmode, maxflags;
+ u_short g[MAXGID], u[MAXUID], m[MAXMODE], f[MAXFLAGS];
+ char *fflags;
+ static int first = 1;
+
+ if ((p = fts_children(t, 0)) == NULL) {
+ if (errno)
+ err(1, "%s", RP(parent));
+ return (1);
+ }
+
+ bzero(g, sizeof(g));
+ bzero(u, sizeof(u));
+ bzero(m, sizeof(m));
+ bzero(f, sizeof(f));
+
+ maxuid = maxgid = maxmode = maxflags = 0;
+ for (; p; p = p->fts_link) {
+ if (!dflag || (dflag && S_ISDIR(p->fts_statp->st_mode))) {
+ smode = p->fts_statp->st_mode & MBITS;
+ if (smode < MAXMODE && ++m[smode] > maxmode) {
+ savemode = smode;
+ maxmode = m[smode];
+ }
+ sgid = p->fts_statp->st_gid;
+ if (sgid < MAXGID && ++g[sgid] > maxgid) {
+ savegid = sgid;
+ maxgid = g[sgid];
+ }
+ suid = p->fts_statp->st_uid;
+ if (suid < MAXUID && ++u[suid] > maxuid) {
+ saveuid = suid;
+ maxuid = u[suid];
+ }
+
+ /*
+ * XXX
+ * note that the below will break when file flags
+ * are extended beyond the first 4 bytes of each
+ * half word of the flags
+ */
+#define FLAGS2IDX(f) ((f & 0xf) | ((f >> 12) & 0xf0))
+ sflags = p->fts_statp->st_flags;
+ if (FLAGS2IDX(sflags) < MAXFLAGS &&
+ ++f[FLAGS2IDX(sflags)] > maxflags) {
+ saveflags = sflags;
+ maxflags = f[FLAGS2IDX(sflags)];
+ }
+ }
+ }
+ /*
+ * If the /set record is the same as the last one we do not need to output
+ * a new one. So first we check to see if anything changed. Note that we
+ * always output a /set record for the first directory.
+ */
+ if ((((keys & F_UNAME) | (keys & F_UID)) && (*puid != saveuid)) ||
+ (((keys & F_GNAME) | (keys & F_GID)) && (*pgid != savegid)) ||
+ ((keys & F_MODE) && (*pmode != savemode)) ||
+ ((keys & F_FLAGS) && (*pflags != saveflags)) ||
+ (first)) {
+ first = 0;
+ if (dflag)
+ (void)printf("/set type=dir");
+ else
+ (void)printf("/set type=file");
+ if (keys & F_UNAME) {
+ pw = getpwuid(saveuid);
+ if (pw != NULL)
+ (void)printf(" uname=%s", pw->pw_name);
+ else if (wflag)
+ warnx( "Could not get uname for uid=%u", saveuid);
+ else
+ errx(1, "Could not get uname for uid=%u", saveuid);
+ }
+ if (keys & F_UID)
+ (void)printf(" uid=%lu", (u_long)saveuid);
+ if (keys & F_GNAME) {
+ gr = getgrgid(savegid);
+ if (gr != NULL)
+ (void)printf(" gname=%s", gr->gr_name);
+ else if (wflag)
+ warnx("Could not get gname for gid=%u", savegid);
+ else
+ errx(1, "Could not get gname for gid=%u", savegid);
+ }
+ if (keys & F_GID)
+ (void)printf(" gid=%lu", (u_long)savegid);
+ if (keys & F_MODE)
+ (void)printf(" mode=%#o", savemode);
+ if (keys & F_NLINK)
+ (void)printf(" nlink=1");
+ if (keys & F_FLAGS) {
+ fflags = flags_to_string(saveflags);
+ (void)printf(" flags=%s", fflags);
+ free(fflags);
+ }
+ (void)printf("\n");
+ *puid = saveuid;
+ *pgid = savegid;
+ *pmode = savemode;
+ *pflags = saveflags;
+ }
+ return (0);
+}
+
+static int
+dsort(const FTSENT * const *a, const FTSENT * const *b)
+{
+ if (S_ISDIR((*a)->fts_statp->st_mode)) {
+ if (!S_ISDIR((*b)->fts_statp->st_mode))
+ return (1);
+ } else if (S_ISDIR((*b)->fts_statp->st_mode))
+ return (-1);
+ return (strcmp((*a)->fts_name, (*b)->fts_name));
+}
+
+#include <stdarg.h>
+
+void
+output(int indent, int *offset, const char *fmt, ...)
+{
+ va_list ap;
+ char buf[1024];
+ va_start(ap, fmt);
+ (void)vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ if (*offset + strlen(buf) > MAXLINELEN - 3) {
+ (void)printf(" \\\n%*s", INDENTNAMELEN + indent, "");
+ *offset = INDENTNAMELEN + indent;
+ }
+ *offset += printf(" %s", buf) + 1;
+}
diff --git a/usr.sbin/mtree/excludes.c b/usr.sbin/mtree/excludes.c
new file mode 100644
index 0000000..21a49b0
--- /dev/null
+++ b/usr.sbin/mtree/excludes.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2000 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that both the above copyright notice and this
+ * permission notice appear in all copies, that both the above
+ * copyright notice and this permission notice appear in all
+ * supporting documentation, and that the name of M.I.T. not be used
+ * in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. M.I.T. makes
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
+ * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (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/time.h> /* XXX for mtree.h */
+#include <sys/queue.h>
+
+#include <err.h>
+#include <fnmatch.h>
+#include <fts.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "mtree.h" /* XXX for extern.h */
+#include "extern.h"
+
+/*
+ * We're assuming that there won't be a whole lot of excludes,
+ * so it's OK to use a stupid algorithm.
+ */
+struct exclude {
+ LIST_ENTRY(exclude) link;
+ const char *glob;
+ int pathname;
+};
+static LIST_HEAD(, exclude) excludes;
+
+void
+init_excludes(void)
+{
+ LIST_INIT(&excludes);
+}
+
+void
+read_excludes_file(const char *name)
+{
+ FILE *fp;
+ char *line, *str;
+ struct exclude *e;
+ size_t len;
+
+ fp = fopen(name, "r");
+ if (fp == 0)
+ err(1, "%s", name);
+
+ while ((line = fgetln(fp, &len)) != 0) {
+ if (line[len - 1] == '\n')
+ len--;
+ if (len == 0)
+ continue;
+
+ str = malloc(len + 1);
+ e = malloc(sizeof *e);
+ if (str == 0 || e == 0)
+ errx(1, "memory allocation error");
+ e->glob = str;
+ memcpy(str, line, len);
+ str[len] = '\0';
+ if (strchr(str, '/'))
+ e->pathname = 1;
+ else
+ e->pathname = 0;
+ LIST_INSERT_HEAD(&excludes, e, link);
+ }
+ fclose(fp);
+}
+
+int
+check_excludes(const char *fname, const char *path)
+{
+ struct exclude *e;
+
+ /* fnmatch(3) has a funny return value convention... */
+#define MATCH(g, n) (fnmatch((g), (n), FNM_PATHNAME) == 0)
+
+ LIST_FOREACH(e, &excludes, link) {
+ if ((e->pathname && MATCH(e->glob, path))
+ || MATCH(e->glob, fname))
+ return 1;
+ }
+ return 0;
+}
diff --git a/usr.sbin/mtree/extern.h b/usr.sbin/mtree/extern.h
new file mode 100644
index 0000000..4b6fb3c
--- /dev/null
+++ b/usr.sbin/mtree/extern.h
@@ -0,0 +1,59 @@
+/*-
+ * 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ * $FreeBSD$
+ */
+extern uint32_t crc_total;
+
+#ifdef _FTS_H_
+int compare(char *, NODE *, FTSENT *);
+#endif
+int crc(int, uint32_t *, off_t *);
+void cwalk(void);
+char *flags_to_string(u_long);
+
+const char *inotype(u_int);
+u_int parsekey(char *, int *);
+char *rlink(char *);
+NODE *mtree_readspec(FILE *fi);
+int mtree_verifyspec(FILE *fi);
+int mtree_specspec(FILE *fi, FILE *fj);
+
+int check_excludes(const char *, const char *);
+void init_excludes(void);
+void read_excludes_file(const char *);
+const char * ftype(u_int type);
+
+extern int ftsoptions;
+extern u_int keys;
+extern int lineno;
+extern int dflag, eflag, iflag, nflag, qflag, rflag, sflag, uflag, wflag;
+#ifdef MAXPATHLEN
+extern char fullpath[MAXPATHLEN];
+#endif
diff --git a/usr.sbin/mtree/misc.c b/usr.sbin/mtree/misc.c
new file mode 100644
index 0000000..65667d6
--- /dev/null
+++ b/usr.sbin/mtree/misc.c
@@ -0,0 +1,124 @@
+/*-
+ * 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/6/93";
+#endif /*not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <err.h>
+#include <fts.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "mtree.h"
+#include "extern.h"
+
+typedef struct _key {
+ const char *name; /* key name */
+ u_int val; /* value */
+
+#define NEEDVALUE 0x01
+ u_int flags;
+} KEY;
+
+/* NB: the following table must be sorted lexically. */
+static KEY keylist[] = {
+ {"cksum", F_CKSUM, NEEDVALUE},
+ {"flags", F_FLAGS, NEEDVALUE},
+ {"gid", F_GID, NEEDVALUE},
+ {"gname", F_GNAME, NEEDVALUE},
+ {"ignore", F_IGN, 0},
+ {"link", F_SLINK, NEEDVALUE},
+#ifdef MD5
+ {"md5digest", F_MD5, NEEDVALUE},
+#endif
+ {"mode", F_MODE, NEEDVALUE},
+ {"nlink", F_NLINK, NEEDVALUE},
+ {"nochange", F_NOCHANGE, 0},
+ {"optional", F_OPT, 0},
+#ifdef RMD160
+ {"ripemd160digest", F_RMD160, NEEDVALUE},
+#endif
+#ifdef SHA1
+ {"sha1digest", F_SHA1, NEEDVALUE},
+#endif
+#ifdef SHA256
+ {"sha256digest", F_SHA256, NEEDVALUE},
+#endif
+ {"size", F_SIZE, NEEDVALUE},
+ {"time", F_TIME, NEEDVALUE},
+ {"type", F_TYPE, NEEDVALUE},
+ {"uid", F_UID, NEEDVALUE},
+ {"uname", F_UNAME, NEEDVALUE},
+};
+
+int keycompare(const void *, const void *);
+
+u_int
+parsekey(char *name, int *needvaluep)
+{
+ KEY *k, tmp;
+
+ tmp.name = name;
+ k = (KEY *)bsearch(&tmp, keylist, sizeof(keylist) / sizeof(KEY),
+ sizeof(KEY), keycompare);
+ if (k == NULL)
+ errx(1, "line %d: unknown keyword %s", lineno, name);
+
+ if (needvaluep)
+ *needvaluep = k->flags & NEEDVALUE ? 1 : 0;
+ return (k->val);
+}
+
+int
+keycompare(const void *a, const void *b)
+{
+ return (strcmp(((const KEY *)a)->name, ((const KEY *)b)->name));
+}
+
+char *
+flags_to_string(u_long fflags)
+{
+ char *string;
+
+ string = fflagstostr(fflags);
+ if (string != NULL && *string == '\0') {
+ free(string);
+ string = strdup("none");
+ }
+ if (string == NULL)
+ err(1, NULL);
+
+ return string;
+}
diff --git a/usr.sbin/mtree/mtree.5 b/usr.sbin/mtree/mtree.5
new file mode 100644
index 0000000..375cc78
--- /dev/null
+++ b/usr.sbin/mtree/mtree.5
@@ -0,0 +1,272 @@
+.\" Copyright (c) 1989, 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.
+.\"
+.\" From: @(#)mtree.8 8.2 (Berkeley) 12/11/93
+.\" $FreeBSD$
+.\"
+.Dd December 31, 2007
+.Dt MTREE 5
+.Os
+.Sh NAME
+.Nm mtree
+.Nd format of mtree dir heirarchy files
+.Sh DESCRIPTION
+The
+.Nm
+format is a textual format that describes a collection of filesystem objects.
+Such files are typically used to create or verify directory heirarchies.
+.Ss General Format
+An
+.Nm
+file consists of a series of lines, each providing information
+about a single filesystem object.
+Leading whitespace is always ignored.
+.Pp
+When encoding file or pathnames, any backslash character or
+character outside of the 95 printable ASCII characters must be
+encoded as a a backslash followed by three
+octal digits.
+When reading mtree files, any appearance of a backslash
+followed by three octal digits should be converted into the
+corresponding character.
+.Pp
+Each line is interpreted independently as one of the following types:
+.Bl -tag -width Cm
+.It Signature
+The first line of any mtree file must begin with
+.Dq #mtree .
+If a file contains any full path entries, the first line should
+begin with
+.Dq #mtree v2.0 ,
+otherwise, the first line should begin with
+.Dq #mtree v1.0 .
+.It Blank
+Blank lines are ignored.
+.It Comment
+Lines beginning with
+.Cm #
+are ignored.
+.It Special
+Lines beginning with
+.Cm /
+are special commands that influence
+the interpretation of later lines.
+.It Relative
+If the first whitespace-delimited word has no
+.Cm /
+characters,
+it is the name of a file in the current directory.
+Any relative entry that describes a directory changes the
+current directory.
+.It dot-dot
+As a special case, a relative entry with the filename
+.Pa ..
+changes the current directory to the parent directory.
+Options on dot-dot entries are always ignored.
+.It Full
+If the first whitespace-delimited word has a
+.Cm /
+character after
+the first character, it is the pathname of a file relative to the
+starting directory.
+There can be multiple full entries describing the same file.
+.El
+.Pp
+Some tools that process
+.Nm
+files may require that multiple lines describing the same file
+occur consecutively.
+It is not permitted for the same file to be mentioned using
+both a relative and a full file specification.
+.Ss Special commands
+Two special commands are currently defined:
+.Bl -tag -width Cm
+.It Cm /set
+This command defines default values for one or more keywords.
+It is followed on the same line by one or more whitespace-separated
+keyword definitions.
+These definitions apply to all following files that do not specify
+a value for that keyword.
+.It Cm /unset
+This command removes any default value set by a previous
+.Cm /set
+command.
+It is followed on the same line by one or more keywords
+separated by whitespace.
+.El
+.Ss Keywords
+After the filename, a full or relative entry consists of zero
+or more whitespace-separated keyword definitions.
+Each such definitions consists of a key from the following
+list immediately followed by an '=' sign
+and a value.
+Software programs reading mtree files should warn about
+unrecognized keywords.
+.Pp
+Currently supported keywords are as follows:
+.Bl -tag -width Cm
+.It Cm cksum
+The checksum of the file using the default algorithm specified by
+the
+.Xr cksum 1
+utility.
+.It Cm contents
+The full pathname of a file whose contents should be
+compared to the contents of this file.
+.It Cm flags
+The file flags as a symbolic name.
+See
+.Xr chflags 1
+for information on these names.
+If no flags are to be set the string
+.Dq none
+may be used to override the current default.
+.It Cm ignore
+Ignore any file hierarchy below this file.
+.It Cm gid
+The file group as a numeric value.
+.It Cm gname
+The file group as a symbolic name.
+.It Cm md5
+The MD5 message digest of the file.
+.It Cm md5digest
+A synonym for
+.Cm md5 .
+.It Cm sha1
+The
+.Tn FIPS
+160-1
+.Pq Dq Tn SHA-1
+message digest of the file.
+.It Cm sha1digest
+A synonym for
+.Cm sha1 .
+.It Cm sha256
+The
+.Tn FIPS
+180-2
+.Pq Dq Tn SHA-256
+message digest of the file.
+.It Cm sha256digest
+A synonym for
+.Cm sha256 .
+.It Cm ripemd160digest
+The
+.Tn RIPEMD160
+message digest of the file.
+.It Cm rmd160
+A synonym for
+.Cm ripemd160digest .
+.It Cm rmd160digest
+A synonym for
+.Cm ripemd160digest .
+.It Cm mode
+The current file's permissions as a numeric (octal) or symbolic
+value.
+.It Cm nlink
+The number of hard links the file is expected to have.
+.It Cm nochange
+Make sure this file or directory exists but otherwise ignore all attributes.
+.It Cm uid
+The file owner as a numeric value.
+.It Cm uname
+The file owner as a symbolic name.
+.It Cm size
+The size, in bytes, of the file.
+.It Cm link
+The file the symbolic link is expected to reference.
+.It Cm time
+The last modification time of the file, in seconds and nanoseconds.
+The value should include a period character and exactly nine digits
+after the period.
+.It Cm type
+The type of the file; may be set to any one of the following:
+.Pp
+.Bl -tag -width Cm -compact
+.It Cm block
+block special device
+.It Cm char
+character special device
+.It Cm dir
+directory
+.It Cm fifo
+fifo
+.It Cm file
+regular file
+.It Cm link
+symbolic link
+.It Cm socket
+socket
+.El
+.El
+.Pp
+.Sh SEE ALSO
+.Xr cksum 1 ,
+.Xr find 1 ,
+.Xr mtree 8
+.Sh BUGS
+The
+.Fx
+implementation of mtree does not currently support
+the
+.Nm
+2.0
+format.
+The requirement for a
+.Dq #mtree
+signature line is new and not yet widely implemented.
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.3 Reno .
+The
+.Tn MD5
+digest capability was added in
+.Fx 2.1 ,
+in response to the widespread use of programs which can spoof
+.Xr cksum 1 .
+The
+.Tn SHA-1
+and
+.Tn RIPEMD160
+digests were added in
+.Fx 4.0 ,
+as new attacks have demonstrated weaknesses in
+.Tn MD5 .
+The
+.Tn SHA-256
+digest was added in
+.Fx 6.0 .
+Support for file flags was added in
+.Fx 4.0 ,
+and mostly comes from
+.Nx .
+The
+.Dq full
+entry format was added by
+.Nx .
diff --git a/usr.sbin/mtree/mtree.8 b/usr.sbin/mtree/mtree.8
new file mode 100644
index 0000000..f0a93ed
--- /dev/null
+++ b/usr.sbin/mtree/mtree.8
@@ -0,0 +1,404 @@
+.\" Copyright (c) 1989, 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.
+.\"
+.\" From: @(#)mtree.8 8.2 (Berkeley) 12/11/93
+.\" $FreeBSD$
+.\"
+.Dd June 16, 2007
+.Dt MTREE 8
+.Os
+.Sh NAME
+.Nm mtree
+.Nd map a directory hierarchy
+.Sh SYNOPSIS
+.Nm
+.Op Fl LPUcdeinqruxw
+.Bk -words
+.Op Fl f Ar spec
+.Ek
+.Bk -words
+.Op Fl f Ar spec
+.Ek
+.Bk -words
+.Op Fl K Ar keywords
+.Ek
+.Bk -words
+.Op Fl k Ar keywords
+.Ek
+.Bk -words
+.Op Fl p Ar path
+.Ek
+.Bk -words
+.Op Fl s Ar seed
+.Ek
+.Bk -words
+.Op Fl X Ar exclude-list
+.Ek
+.Sh DESCRIPTION
+The
+.Nm
+utility compares the file hierarchy rooted in the current directory against a
+specification read from the standard input.
+Messages are written to the standard output for any files whose
+characteristics do not match the specifications, or which are
+missing from either the file hierarchy or the specification.
+.Pp
+The options are as follows:
+.Bl -tag -width flag
+.It Fl L
+Follow all symbolic links in the file hierarchy.
+.It Fl P
+Do not follow symbolic links in the file hierarchy, instead consider
+the symbolic link itself in any comparisons.
+This is the default.
+.It Fl U
+Modify the owner, group, permissions, and modification time of existing
+files to match the specification and create any missing directories or
+symbolic links.
+User, group and permissions must all be specified for missing directories
+to be created.
+Corrected mismatches are not considered errors.
+.It Fl c
+Print a specification for the file hierarchy to the standard output.
+.It Fl d
+Ignore everything except directory type files.
+.It Fl e
+Do not complain about files that are in the file hierarchy, but not in the
+specification.
+.It Fl i
+Indent the output 4 spaces each time a directory level is descended when
+create a specification with the
+.Fl c
+option.
+This does not affect either the /set statements or the comment before each
+directory.
+It does however affect the comment before the close of each directory.
+.It Fl n
+Do not emit pathname comments when creating a specification.
+Normally
+a comment is emitted before each directory and before the close of that
+directory when using the
+.Fl c
+option.
+.It Fl q
+Quiet mode.
+Do not complain when a
+.Dq missing
+directory cannot be created because it already exists.
+This occurs when the directory is a symbolic link.
+.It Fl r
+Remove any files in the file hierarchy that are not described in the
+specification.
+.It Fl u
+Same as
+.Fl U
+except a status of 2 is returned if the file hierarchy did not match
+the specification.
+.It Fl w
+Make some errorconditions non-fatal warnings.
+.It Fl x
+Do not descend below mount points in the file hierarchy.
+.It Fl f Ar file
+Read the specification from
+.Ar file ,
+instead of from the standard input.
+.Pp
+If this option is specified twice, the two specifications are compared
+to each other rather than to the file hierarchy.
+The specifications be sorted like output generated using
+.Fl c .
+The output format in this case is somewhat remniscent of
+.Xr comm 1 ,
+having "in first spec only", "in second spec only", and "different"
+columns, prefixed by zero, one and two TAB characters respectively.
+Each entry in the "different" column occupies two lines, one from each specification.
+.It Fl K Ar keywords
+Add the specified (whitespace or comma separated)
+.Ar keywords
+to the current set of keywords.
+.It Fl k Ar keywords
+Use the ``type'' keyword plus the specified (whitespace or comma separated)
+.Ar keywords
+instead of the current set of keywords.
+.It Fl p Ar path
+Use the file hierarchy rooted in
+.Ar path ,
+instead of the current directory.
+.It Fl s Ar seed
+Display a single checksum to the standard error output that represents all
+of the files for which the keyword
+.Cm cksum
+was specified.
+The checksum is seeded with the specified value.
+.It Fl X Ar exclude-list
+The specified file contains
+.Xr fnmatch 3
+patterns matching files to be excluded from
+the specification, one to a line.
+If the pattern contains a
+.Ql \&/
+character, it will be matched against entire pathnames (relative to
+the starting directory); otherwise,
+it will be matched against basenames only.
+No comments are allowed in
+the
+.Ar exclude-list
+file.
+.El
+.Pp
+Specifications are mostly composed of ``keywords'', i.e., strings
+that specify values relating to files.
+No keywords have default values, and if a keyword has no value set, no
+checks based on it are performed.
+.Pp
+Currently supported keywords are as follows:
+.Bl -tag -width Cm
+.It Cm cksum
+The checksum of the file using the default algorithm specified by
+the
+.Xr cksum 1
+utility.
+.It Cm flags
+The file flags as a symbolic name.
+See
+.Xr chflags 1
+for information on these names.
+If no flags are to be set the string
+.Dq none
+may be used to override the current default.
+.It Cm ignore
+Ignore any file hierarchy below this file.
+.It Cm gid
+The file group as a numeric value.
+.It Cm gname
+The file group as a symbolic name.
+.It Cm md5digest
+The MD5 message digest of the file.
+.It Cm sha1digest
+The
+.Tn FIPS
+160-1
+.Pq Dq Tn SHA-1
+message digest of the file.
+.It Cm sha256digest
+The
+.Tn FIPS
+180-2
+.Pq Dq Tn SHA-256
+message digest of the file.
+.It Cm ripemd160digest
+The
+.Tn RIPEMD160
+message digest of the file.
+.It Cm mode
+The current file's permissions as a numeric (octal) or symbolic
+value.
+.It Cm nlink
+The number of hard links the file is expected to have.
+.It Cm nochange
+Make sure this file or directory exists but otherwise ignore all attributes.
+.It Cm optional
+The file is optional; do not complain about the file if it is
+not in the file hierarchy.
+.It Cm uid
+The file owner as a numeric value.
+.It Cm uname
+The file owner as a symbolic name.
+.It Cm size
+The size, in bytes, of the file.
+.It Cm link
+The file the symbolic link is expected to reference.
+.It Cm time
+The last modification time of the file, in seconds and nanoseconds.
+The value should include a period character and exactly nine digits
+after the period.
+.It Cm type
+The type of the file; may be set to any one of the following:
+.Pp
+.Bl -tag -width Cm -compact
+.It Cm block
+block special device
+.It Cm char
+character special device
+.It Cm dir
+directory
+.It Cm fifo
+fifo
+.It Cm file
+regular file
+.It Cm link
+symbolic link
+.It Cm socket
+socket
+.El
+.El
+.Pp
+The default set of keywords are
+.Cm flags ,
+.Cm gid ,
+.Cm mode ,
+.Cm nlink ,
+.Cm size ,
+.Cm link ,
+.Cm time ,
+and
+.Cm uid .
+.Pp
+There are four types of lines in a specification.
+.Pp
+The first type of line sets a global value for a keyword, and consists of
+the string ``/set'' followed by whitespace, followed by sets of keyword/value
+pairs, separated by whitespace.
+Keyword/value pairs consist of a keyword, followed by an equals sign
+(``=''), followed by a value, without whitespace characters.
+Once a keyword has been set, its value remains unchanged until either
+reset or unset.
+.Pp
+The second type of line unsets keywords and consists of the string
+``/unset'', followed by whitespace, followed by one or more keywords,
+separated by whitespace.
+.Pp
+The third type of line is a file specification and consists of a file
+name, followed by whitespace, followed by zero or more whitespace
+separated keyword/value pairs.
+The file name may be preceded by whitespace characters.
+The file name may contain any of the standard file name matching
+characters (``['', ``]'', ``?'' or ``*''), in which case files
+in the hierarchy will be associated with the first pattern that
+they match.
+.Pp
+Each of the keyword/value pairs consist of a keyword, followed by an
+equals sign (``=''), followed by the keyword's value, without
+whitespace characters.
+These values override, without changing, the global value of the
+corresponding keyword.
+.Pp
+All paths are relative.
+Specifying a directory will cause subsequent files to be searched
+for in that directory hierarchy.
+Which brings us to the last type of line in a specification: a line
+containing only the string
+.Dq Pa ..\&
+causes the current directory
+path to ascend one level.
+.Pp
+Empty lines and lines whose first non-whitespace character is a hash
+mark (``#'') are ignored.
+.Pp
+The
+.Nm
+utility exits with a status of 0 on success, 1 if any error occurred,
+and 2 if the file hierarchy did not match the specification.
+A status of 2 is converted to a status of 0 if the
+.Fl U
+option is used.
+.Sh FILES
+.Bl -tag -width /etc/mtree -compact
+.It Pa /etc/mtree
+system specification directory
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+To detect system binaries that have been ``trojan horsed'', it is recommended
+that
+.Nm
+.Fl K
+.Cm sha256digest
+be run on the file systems, and a copy of the results stored on a different
+machine, or, at least, in encrypted form.
+The output file itself should be digested using the
+.Xr sha256 1
+utility.
+Then, periodically,
+.Nm
+and
+.Xr sha256 1
+should be run against the on-line specifications.
+While it is possible for the bad guys to change the on-line specifications
+to conform to their modified binaries, it is believed to be
+impractical for them to create a modified specification which has
+the same SHA-256 digest as the original.
+.Pp
+The
+.Fl d
+and
+.Fl u
+options can be used in combination to create directory hierarchies
+for distributions and other such things; the files in
+.Pa /etc/mtree
+were used to create almost all directories in this
+.Fx
+distribution.
+.Pp
+To create an
+.Pa /etc/mtree
+style BSD.*.dist file, use
+.Nm
+.Fl c
+.Fl d
+.Fl i
+.Fl n
+.Fl k
+.Cm uname,gname,mode,nochange.
+.Sh SEE ALSO
+.Xr chflags 1 ,
+.Xr chgrp 1 ,
+.Xr chmod 1 ,
+.Xr cksum 1 ,
+.Xr md5 1 ,
+.Xr stat 2 ,
+.Xr fts 3 ,
+.Xr md5 3 ,
+.Xr chown 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.3 Reno .
+The
+.Tn MD5
+digest capability was added in
+.Fx 2.1 ,
+in response to the widespread use of programs which can spoof
+.Xr cksum 1 .
+The
+.Tn SHA-1
+and
+.Tn RIPEMD160
+digests were added in
+.Fx 4.0 ,
+as new attacks have demonstrated weaknesses in
+.Tn MD5 .
+The
+.Tn SHA-256
+digest was added in
+.Fx 6.0 .
+Support for file flags was added in
+.Fx 4.0 ,
+and mostly comes from
+.Nx .
diff --git a/usr.sbin/mtree/mtree.c b/usr.sbin/mtree/mtree.c
new file mode 100644
index 0000000..49e3e6b
--- /dev/null
+++ b/usr.sbin/mtree/mtree.c
@@ -0,0 +1,190 @@
+/*-
+ * Copyright (c) 1989, 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1989, 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)mtree.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "mtree.h"
+#include "extern.h"
+
+int ftsoptions = FTS_PHYSICAL;
+int cflag, dflag, eflag, iflag, nflag, qflag, rflag, sflag, uflag, Uflag, wflag;
+u_int keys;
+char fullpath[MAXPATHLEN];
+
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ int ch;
+ char *dir, *p;
+ int status;
+ FILE *spec1, *spec2;
+
+ dir = NULL;
+ keys = KEYDEFAULT;
+ init_excludes();
+ spec1 = stdin;
+ spec2 = NULL;
+
+ while ((ch = getopt(argc, argv, "cdef:iK:k:LnPp:qrs:UuwxX:")) != -1)
+ switch((char)ch) {
+ case 'c':
+ cflag = 1;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'e':
+ eflag = 1;
+ break;
+ case 'f':
+ if (spec1 == stdin) {
+ spec1 = fopen(optarg, "r");
+ if (spec1 == NULL)
+ err(1, "%s", optarg);
+ } else if (spec2 == NULL) {
+ spec2 = fopen(optarg, "r");
+ if (spec2 == NULL)
+ err(1, "%s", optarg);
+ } else
+ usage();
+ break;
+ case 'i':
+ iflag = 1;
+ break;
+ case 'K':
+ while ((p = strsep(&optarg, " \t,")) != NULL)
+ if (*p != '\0')
+ keys |= parsekey(p, NULL);
+ break;
+ case 'k':
+ keys = F_TYPE;
+ while ((p = strsep(&optarg, " \t,")) != NULL)
+ if (*p != '\0')
+ keys |= parsekey(p, NULL);
+ break;
+ case 'L':
+ ftsoptions &= ~FTS_PHYSICAL;
+ ftsoptions |= FTS_LOGICAL;
+ break;
+ case 'n':
+ nflag = 1;
+ break;
+ case 'P':
+ ftsoptions &= ~FTS_LOGICAL;
+ ftsoptions |= FTS_PHYSICAL;
+ break;
+ case 'p':
+ dir = optarg;
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ case 'r':
+ rflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ crc_total = ~strtoul(optarg, &p, 0);
+ if (*p)
+ errx(1, "illegal seed value -- %s", optarg);
+ break;
+ case 'U':
+ Uflag = 1;
+ uflag = 1;
+ break;
+ case 'u':
+ uflag = 1;
+ break;
+ case 'w':
+ wflag = 1;
+ break;
+ case 'x':
+ ftsoptions |= FTS_XDEV;
+ break;
+ case 'X':
+ read_excludes_file(optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc)
+ usage();
+
+ if (dir && chdir(dir))
+ err(1, "%s", dir);
+
+ if ((cflag || sflag) && !getcwd(fullpath, sizeof(fullpath)))
+ errx(1, "%s", fullpath);
+
+ if (cflag) {
+ cwalk();
+ exit(0);
+ }
+ if (spec2 != NULL)
+ status = mtree_specspec(spec1, spec2);
+ else
+ status = mtree_verifyspec(spec1);
+ if (Uflag & (status == MISMATCHEXIT))
+ status = 0;
+ exit(status);
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr,
+"usage: mtree [-LPUcdeinqruxw] [-f spec] [-f spec] [-K key] [-k key] [-p path] [-s seed]\n"
+"\t[-X excludes]\n");
+ exit(1);
+}
diff --git a/usr.sbin/mtree/mtree.h b/usr.sbin/mtree/mtree.h
new file mode 100644
index 0000000..fb22f0d
--- /dev/null
+++ b/usr.sbin/mtree/mtree.h
@@ -0,0 +1,98 @@
+/*-
+ * 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mtree.h 8.1 (Berkeley) 6/6/93
+ * $FreeBSD$
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#define KEYDEFAULT \
+ (F_GID | F_MODE | F_NLINK | F_SIZE | F_SLINK | F_TIME | F_UID | F_FLAGS)
+
+#define MISMATCHEXIT 2
+
+typedef struct _node {
+ struct _node *parent, *child; /* up, down */
+ struct _node *prev, *next; /* left, right */
+ off_t st_size; /* size */
+ struct timespec st_mtimespec; /* last modification time */
+ u_long cksum; /* check sum */
+ char *md5digest; /* MD5 digest */
+ char *sha1digest; /* SHA-1 digest */
+ char *sha256digest; /* SHA-256 digest */
+ char *rmd160digest; /* RIPEMD160 digest */
+ char *slink; /* symbolic link reference */
+ uid_t st_uid; /* uid */
+ gid_t st_gid; /* gid */
+#define MBITS (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO)
+ mode_t st_mode; /* mode */
+ u_long st_flags; /* flags */
+ nlink_t st_nlink; /* link count */
+
+#define F_CKSUM 0x0001 /* check sum */
+#define F_DONE 0x0002 /* directory done */
+#define F_GID 0x0004 /* gid */
+#define F_GNAME 0x0008 /* group name */
+#define F_IGN 0x0010 /* ignore */
+#define F_MAGIC 0x0020 /* name has magic chars */
+#define F_MODE 0x0040 /* mode */
+#define F_NLINK 0x0080 /* number of links */
+#define F_SIZE 0x0100 /* size */
+#define F_SLINK 0x0200 /* link count */
+#define F_TIME 0x0400 /* modification time */
+#define F_TYPE 0x0800 /* file type */
+#define F_UID 0x1000 /* uid */
+#define F_UNAME 0x2000 /* user name */
+#define F_VISIT 0x4000 /* file visited */
+#define F_MD5 0x8000 /* MD5 digest */
+#define F_NOCHANGE 0x10000 /* If owner/mode "wrong", do */
+ /* not change */
+#define F_SHA1 0x20000 /* SHA-1 digest */
+#define F_RMD160 0x40000 /* RIPEMD160 digest */
+#define F_FLAGS 0x80000 /* file flags */
+#define F_SHA256 0x100000 /* SHA-256 digest */
+#define F_OPT 0x200000 /* existence optional */
+ u_int flags; /* items set */
+
+#define F_BLOCK 0x001 /* block special */
+#define F_CHAR 0x002 /* char special */
+#define F_DIR 0x004 /* directory */
+#define F_FIFO 0x008 /* fifo */
+#define F_FILE 0x010 /* regular file */
+#define F_LINK 0x020 /* symbolic link */
+#define F_SOCK 0x040 /* socket */
+ u_char type; /* file type */
+
+ char name[1]; /* file name (must be last) */
+} NODE;
+
+#define RP(p) \
+ ((p)->fts_path[0] == '.' && (p)->fts_path[1] == '/' ? \
+ (p)->fts_path + 2 : (p)->fts_path)
diff --git a/usr.sbin/mtree/spec.c b/usr.sbin/mtree/spec.c
new file mode 100644
index 0000000..c7c6460
--- /dev/null
+++ b/usr.sbin/mtree/spec.c
@@ -0,0 +1,323 @@
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)spec.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <vis.h>
+#include "mtree.h"
+#include "extern.h"
+
+int lineno; /* Current spec line number. */
+
+static void set(char *, NODE *);
+static void unset(char *, NODE *);
+
+NODE *
+mtree_readspec(FILE *fi)
+{
+ NODE *centry, *last;
+ char *p;
+ NODE ginfo, *root;
+ int c_cur, c_next;
+ char buf[2048];
+
+ centry = last = root = NULL;
+ bzero(&ginfo, sizeof(ginfo));
+ c_cur = c_next = 0;
+ for (lineno = 1; fgets(buf, sizeof(buf), fi);
+ ++lineno, c_cur = c_next, c_next = 0) {
+ /* Skip empty lines. */
+ if (buf[0] == '\n')
+ continue;
+
+ /* Find end of line. */
+ if ((p = index(buf, '\n')) == NULL)
+ errx(1, "line %d too long", lineno);
+
+ /* See if next line is continuation line. */
+ if (p[-1] == '\\') {
+ --p;
+ c_next = 1;
+ }
+
+ /* Null-terminate the line. */
+ *p = '\0';
+
+ /* Skip leading whitespace. */
+ for (p = buf; *p && isspace(*p); ++p);
+
+ /* If nothing but whitespace or comment char, continue. */
+ if (!*p || *p == '#')
+ continue;
+
+#ifdef DEBUG
+ (void)fprintf(stderr, "line %d: {%s}\n", lineno, p);
+#endif
+ if (c_cur) {
+ set(p, centry);
+ continue;
+ }
+
+ /* Grab file name, "$", "set", or "unset". */
+ if ((p = strtok(p, "\n\t ")) == NULL)
+ errx(1, "line %d: missing field", lineno);
+
+ if (p[0] == '/')
+ switch(p[1]) {
+ case 's':
+ if (strcmp(p + 1, "set"))
+ break;
+ set(NULL, &ginfo);
+ continue;
+ case 'u':
+ if (strcmp(p + 1, "unset"))
+ break;
+ unset(NULL, &ginfo);
+ continue;
+ }
+
+ if (index(p, '/'))
+ errx(1, "line %d: slash character in file name",
+ lineno);
+
+ if (!strcmp(p, "..")) {
+ /* Don't go up, if haven't gone down. */
+ if (!root)
+ goto noparent;
+ if (last->type != F_DIR || last->flags & F_DONE) {
+ if (last == root)
+ goto noparent;
+ last = last->parent;
+ }
+ last->flags |= F_DONE;
+ continue;
+
+noparent: errx(1, "line %d: no parent node", lineno);
+ }
+
+ if ((centry = calloc(1, sizeof(NODE) + strlen(p))) == NULL)
+ errx(1, "calloc");
+ *centry = ginfo;
+#define MAGIC "?*["
+ if (strpbrk(p, MAGIC))
+ centry->flags |= F_MAGIC;
+ if (strunvis(centry->name, p) == -1)
+ errx(1, "filename %s is ill-encoded", p);
+ set(NULL, centry);
+
+ if (!root) {
+ last = root = centry;
+ root->parent = root;
+ } else if (last->type == F_DIR && !(last->flags & F_DONE)) {
+ centry->parent = last;
+ last = last->child = centry;
+ } else {
+ centry->parent = last->parent;
+ centry->prev = last;
+ last = last->next = centry;
+ }
+ }
+ return (root);
+}
+
+static void
+set(char *t, NODE *ip)
+{
+ int type;
+ char *kw, *val = NULL;
+ struct group *gr;
+ struct passwd *pw;
+ mode_t *m;
+ int value;
+ char *ep;
+
+ for (; (kw = strtok(t, "= \t\n")); t = NULL) {
+ ip->flags |= type = parsekey(kw, &value);
+ if (value && (val = strtok(NULL, " \t\n")) == NULL)
+ errx(1, "line %d: missing value", lineno);
+ switch(type) {
+ case F_CKSUM:
+ ip->cksum = strtoul(val, &ep, 10);
+ if (*ep)
+ errx(1, "line %d: invalid checksum %s",
+ lineno, val);
+ break;
+ case F_MD5:
+ ip->md5digest = strdup(val);
+ if(!ip->md5digest)
+ errx(1, "strdup");
+ break;
+ case F_SHA1:
+ ip->sha1digest = strdup(val);
+ if(!ip->sha1digest)
+ errx(1, "strdup");
+ break;
+ case F_SHA256:
+ ip->sha256digest = strdup(val);
+ if(!ip->sha256digest)
+ errx(1, "strdup");
+ break;
+ case F_RMD160:
+ ip->rmd160digest = strdup(val);
+ if(!ip->rmd160digest)
+ errx(1, "strdup");
+ break;
+ case F_FLAGS:
+ if (strcmp("none", val) == 0)
+ ip->st_flags = 0;
+ else if (strtofflags(&val, &ip->st_flags, NULL) != 0)
+ errx(1, "line %d: invalid flag %s",lineno, val);
+ break;
+ case F_GID:
+ ip->st_gid = strtoul(val, &ep, 10);
+ if (*ep)
+ errx(1, "line %d: invalid gid %s", lineno, val);
+ break;
+ case F_GNAME:
+ if ((gr = getgrnam(val)) == NULL)
+ errx(1, "line %d: unknown group %s", lineno, val);
+ ip->st_gid = gr->gr_gid;
+ break;
+ case F_IGN:
+ /* just set flag bit */
+ break;
+ case F_MODE:
+ if ((m = setmode(val)) == NULL)
+ errx(1, "line %d: invalid file mode %s",
+ lineno, val);
+ ip->st_mode = getmode(m, 0);
+ free(m);
+ break;
+ case F_NLINK:
+ ip->st_nlink = strtoul(val, &ep, 10);
+ if (*ep)
+ errx(1, "line %d: invalid link count %s",
+ lineno, val);
+ break;
+ case F_OPT:
+ /* just set flag bit */
+ break;
+ case F_SIZE:
+ ip->st_size = strtoq(val, &ep, 10);
+ if (*ep)
+ errx(1, "line %d: invalid size %s",
+ lineno, val);
+ break;
+ case F_SLINK:
+ ip->slink = malloc(strlen(val) + 1);
+ if (ip->slink == NULL)
+ errx(1, "malloc");
+ if (strunvis(ip->slink, val) == -1)
+ errx(1, "symlink %s is ill-encoded", val);
+ break;
+ case F_TIME:
+ ip->st_mtimespec.tv_sec = strtoul(val, &ep, 10);
+ if (*ep == '.') {
+ /* Note: we require exactly nine
+ * digits after the decimal point. */
+ val = ep + 1;
+ ip->st_mtimespec.tv_nsec
+ = strtoul(val, &ep, 10);
+ } else
+ ip->st_mtimespec.tv_nsec = 0;
+ if (*ep)
+ errx(1, "line %d: invalid time %s",
+ lineno, val);
+ break;
+ case F_TYPE:
+ switch(*val) {
+ case 'b':
+ if (!strcmp(val, "block"))
+ ip->type = F_BLOCK;
+ break;
+ case 'c':
+ if (!strcmp(val, "char"))
+ ip->type = F_CHAR;
+ break;
+ case 'd':
+ if (!strcmp(val, "dir"))
+ ip->type = F_DIR;
+ break;
+ case 'f':
+ if (!strcmp(val, "file"))
+ ip->type = F_FILE;
+ if (!strcmp(val, "fifo"))
+ ip->type = F_FIFO;
+ break;
+ case 'l':
+ if (!strcmp(val, "link"))
+ ip->type = F_LINK;
+ break;
+ case 's':
+ if (!strcmp(val, "socket"))
+ ip->type = F_SOCK;
+ break;
+ default:
+ errx(1, "line %d: unknown file type %s",
+ lineno, val);
+ }
+ break;
+ case F_UID:
+ ip->st_uid = strtoul(val, &ep, 10);
+ if (*ep)
+ errx(1, "line %d: invalid uid %s", lineno, val);
+ break;
+ case F_UNAME:
+ if ((pw = getpwnam(val)) == NULL)
+ errx(1, "line %d: unknown user %s", lineno, val);
+ ip->st_uid = pw->pw_uid;
+ break;
+ }
+ }
+}
+
+static void
+unset(char *t, NODE *ip)
+{
+ char *p;
+
+ while ((p = strtok(t, "\n\t ")))
+ ip->flags &= ~parsekey(p, NULL);
+}
diff --git a/usr.sbin/mtree/specspec.c b/usr.sbin/mtree/specspec.c
new file mode 100644
index 0000000..f85882e
--- /dev/null
+++ b/usr.sbin/mtree/specspec.c
@@ -0,0 +1,256 @@
+/*-
+ * Copyright (c) 2003 Poul-Henning Kamp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <unistd.h>
+#include "mtree.h"
+#include "extern.h"
+
+#define FF(a, b, c, d) \
+ (((a)->flags & (c)) && ((b)->flags & (c)) && ((a)->d) != ((b)->d))
+#define FS(a, b, c, d) \
+ (((a)->flags & (c)) && ((b)->flags & (c)) && strcmp((a)->d,(b)->d))
+#define FM(a, b, c, d) \
+ (((a)->flags & (c)) && ((b)->flags & (c)) && memcmp(&(a)->d,&(b)->d, sizeof (a)->d))
+
+static void
+shownode(NODE *n, int f, char const *path)
+{
+ struct group *gr;
+ struct passwd *pw;
+
+ printf("%s%s %s", path, n->name, ftype(n->type));
+ if (f & F_CKSUM)
+ printf(" cksum=%lu", n->cksum);
+ if (f & F_GID)
+ printf(" gid=%d", n->st_gid);
+ if (f & F_GNAME) {
+ gr = getgrgid(n->st_gid);
+ if (gr == NULL)
+ printf(" gid=%d", n->st_gid);
+ else
+ printf(" gname=%s", gr->gr_name);
+ }
+ if (f & F_MODE)
+ printf(" mode=%o", n->st_mode);
+ if (f & F_NLINK)
+ printf(" nlink=%d", n->st_nlink);
+ if (f & F_SIZE)
+ printf(" size=%jd", (intmax_t)n->st_size);
+ if (f & F_UID)
+ printf(" uid=%d", n->st_uid);
+ if (f & F_UNAME) {
+ pw = getpwuid(n->st_uid);
+ if (pw == NULL)
+ printf(" uid=%d", n->st_uid);
+ else
+ printf(" uname=%s", pw->pw_name);
+ }
+ if (f & F_MD5)
+ printf(" md5digest=%s", n->md5digest);
+ if (f & F_SHA1)
+ printf(" sha1digest=%s", n->sha1digest);
+ if (f & F_RMD160)
+ printf(" rmd160digest=%s", n->rmd160digest);
+ if (f & F_SHA256)
+ printf(" sha256digest=%s", n->sha256digest);
+ if (f & F_FLAGS)
+ printf(" flags=%s", flags_to_string(n->st_flags));
+ printf("\n");
+}
+
+static int
+mismatch(NODE *n1, NODE *n2, int differ, char const *path)
+{
+
+ if (n2 == NULL) {
+ shownode(n1, differ, path);
+ return (1);
+ }
+ if (n1 == NULL) {
+ printf("\t");
+ shownode(n2, differ, path);
+ return (1);
+ }
+ if (!(differ & keys))
+ return(0);
+ printf("\t\t");
+ shownode(n1, differ, path);
+ printf("\t\t");
+ shownode(n2, differ, path);
+ return (1);
+}
+
+static int
+compare_nodes(NODE *n1, NODE *n2, char const *path)
+{
+ int differs;
+
+ if (n1 != NULL && n1->type == F_LINK)
+ n1->flags &= ~F_MODE;
+ if (n2 != NULL && n2->type == F_LINK)
+ n2->flags &= ~F_MODE;
+ differs = 0;
+ if (n1 == NULL && n2 != NULL) {
+ differs = n2->flags;
+ mismatch(n1, n2, differs, path);
+ return (1);
+ }
+ if (n1 != NULL && n2 == NULL) {
+ differs = n1->flags;
+ mismatch(n1, n2, differs, path);
+ return (1);
+ }
+ if (n1->type != n2->type) {
+ differs = 0;
+ mismatch(n1, n2, differs, path);
+ return (1);
+ }
+ if (FF(n1, n2, F_CKSUM, cksum))
+ differs |= F_CKSUM;
+ if (FF(n1, n2, F_GID, st_gid))
+ differs |= F_GID;
+ if (FF(n1, n2, F_GNAME, st_gid))
+ differs |= F_GNAME;
+ if (FF(n1, n2, F_MODE, st_mode))
+ differs |= F_MODE;
+ if (FF(n1, n2, F_NLINK, st_nlink))
+ differs |= F_NLINK;
+ if (FF(n1, n2, F_SIZE, st_size))
+ differs |= F_SIZE;
+ if (FS(n1, n2, F_SLINK, slink))
+ differs |= F_SLINK;
+ if (FM(n1, n2, F_TIME, st_mtimespec))
+ differs |= F_TIME;
+ if (FF(n1, n2, F_UID, st_uid))
+ differs |= F_UID;
+ if (FF(n1, n2, F_UNAME, st_uid))
+ differs |= F_UNAME;
+ if (FS(n1, n2, F_MD5, md5digest))
+ differs |= F_MD5;
+ if (FS(n1, n2, F_SHA1, sha1digest))
+ differs |= F_SHA1;
+ if (FS(n1, n2, F_RMD160, rmd160digest))
+ differs |= F_RMD160;
+ if (FS(n1, n2, F_SHA256, sha256digest))
+ differs |= F_SHA256;
+ if (FF(n1, n2, F_FLAGS, st_flags))
+ differs |= F_FLAGS;
+ if (differs) {
+ mismatch(n1, n2, differs, path);
+ return (1);
+ }
+ return (0);
+}
+static int
+walk_in_the_forest(NODE *t1, NODE *t2, char const *path)
+{
+ int r, i;
+ NODE *c1, *c2, *n1, *n2;
+ char *np;
+
+ r = 0;
+
+ if (t1 != NULL)
+ c1 = t1->child;
+ else
+ c1 = NULL;
+ if (t2 != NULL)
+ c2 = t2->child;
+ else
+ c2 = NULL;
+ while (c1 != NULL || c2 != NULL) {
+ n1 = n2 = NULL;
+ if (c1 != NULL)
+ n1 = c1->next;
+ if (c2 != NULL)
+ n2 = c2->next;
+ if (c1 != NULL && c2 != NULL) {
+ if (c1->type != F_DIR && c2->type == F_DIR) {
+ n2 = c2;
+ c2 = NULL;
+ } else if (c1->type == F_DIR && c2->type != F_DIR) {
+ n1 = c1;
+ c1 = NULL;
+ } else {
+ i = strcmp(c1->name, c2->name);
+ if (i > 0) {
+ n1 = c1;
+ c1 = NULL;
+ } else if (i < 0) {
+ n2 = c2;
+ c2 = NULL;
+ }
+ }
+ }
+ if (c1 == NULL && c2->type == F_DIR) {
+ asprintf(&np, "%s%s/", path, c2->name);
+ i = walk_in_the_forest(c1, c2, np);
+ free(np);
+ i += compare_nodes(c1, c2, path);
+ } else if (c2 == NULL && c1->type == F_DIR) {
+ asprintf(&np, "%s%s/", path, c1->name);
+ i = walk_in_the_forest(c1, c2, np);
+ free(np);
+ i += compare_nodes(c1, c2, path);
+ } else if (c1 == NULL || c2 == NULL) {
+ i = compare_nodes(c1, c2, path);
+ } else if (c1->type == F_DIR && c2->type == F_DIR) {
+ asprintf(&np, "%s%s/", path, c1->name);
+ i = walk_in_the_forest(c1, c2, np);
+ free(np);
+ i += compare_nodes(c1, c2, path);
+ } else {
+ i = compare_nodes(c1, c2, path);
+ }
+ r += i;
+ c1 = n1;
+ c2 = n2;
+ }
+ return (r);
+}
+
+int
+mtree_specspec(FILE *fi, FILE *fj)
+{
+ int rval;
+ NODE *root1, *root2;
+
+ root1 = mtree_readspec(fi);
+ root2 = mtree_readspec(fj);
+ rval = walk_in_the_forest(root1, root2, "");
+ rval += compare_nodes(root1, root2, "");
+ if (rval > 0)
+ return (MISMATCHEXIT);
+ return (0);
+}
diff --git a/usr.sbin/mtree/test/test00.sh b/usr.sbin/mtree/test/test00.sh
new file mode 100644
index 0000000..fce801b
--- /dev/null
+++ b/usr.sbin/mtree/test/test00.sh
@@ -0,0 +1,67 @@
+#!/bin/sh
+#
+# Copyright (c) 2003 Poul-Henning Kamp
+# All rights reserved.
+#
+# Please see src/share/examples/etc/bsd-style-copyright.
+#
+# $FreeBSD$
+#
+
+set -e
+
+TMP=/tmp/mtree.$$
+
+rm -rf ${TMP}
+mkdir -p ${TMP} ${TMP}/mr ${TMP}/mt
+
+
+mkdir ${TMP}/mt/foo
+mkdir ${TMP}/mr/\*
+mtree -c -p ${TMP}/mr | mtree -U -r -p ${TMP}/mt > /dev/null 2>&1
+if [ -d ${TMP}/mt/foo ] ; then
+ echo "ERROR Mtree create fell for filename with '*' char" 1>&2
+ rm -rf ${TMP}
+ exit 1
+fi
+rmdir ${TMP}/mr/\*
+
+mkdir -p ${TMP}/mt/foo
+mkdir ${TMP}/mr/\[f\]oo
+mtree -c -p ${TMP}/mr | mtree -U -r -p ${TMP}/mt > /dev/null 2>&1
+if [ -d ${TMP}/mt/foo ] ; then
+ echo "ERROR Mtree create fell for filename with '[' char" 1>&2
+ rm -rf ${TMP}
+ exit 1
+fi
+rmdir ${TMP}/mr/\[f\]oo
+
+mkdir -p ${TMP}/mt/foo
+mkdir ${TMP}/mr/\?oo
+mtree -c -p ${TMP}/mr | mtree -U -r -p ${TMP}/mt > /dev/null 2>&1
+if [ -d ${TMP}/mt/foo ] ; then
+ echo "ERROR Mtree create fell for filename with '?' char" 1>&2
+ rm -rf ${TMP}
+ exit 1
+fi
+rmdir ${TMP}/mr/\?oo
+
+mkdir ${TMP}/mr/\#
+mtree -c -p ${TMP}/mr > ${TMP}/_
+if mtree -U -r -p ${TMP}/mt < ${TMP}/_ > /dev/null 2>&1 ; then
+ true
+else
+ echo "ERROR Mtree create fell for filename with '#' char" 1>&2
+ rm -rf ${TMP}
+ exit 1
+fi
+
+if [ ! -d ${TMP}/mt/\# ] ; then
+ echo "ERROR Mtree update failed to create name with '#' char" 1>&2
+ rm -rf ${TMP}
+ exit 1
+fi
+rmdir ${TMP}/mr/\#
+
+rm -rf ${TMP}
+exit 0
diff --git a/usr.sbin/mtree/test/test01.sh b/usr.sbin/mtree/test/test01.sh
new file mode 100644
index 0000000..e4d3fc3
--- /dev/null
+++ b/usr.sbin/mtree/test/test01.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+#
+# Copyright (c) 2003 Poul-Henning Kamp
+# All rights reserved.
+#
+# Please see src/share/examples/etc/bsd-style-copyright.
+#
+# $FreeBSD$
+#
+
+set -e
+
+TMP=/tmp/mtree.$$
+
+rm -rf ${TMP}
+mkdir -p ${TMP} ${TMP}/mr ${TMP}/mt
+
+
+ln -s "xx this=is=wrong" ${TMP}/mr/foo
+mtree -c -p ${TMP}/mr > ${TMP}/_
+
+if mtree -U -r -p ${TMP}/mt < ${TMP}/_ > /dev/null 2>&1 ; then
+ true
+else
+ echo "ERROR Mtree failed on symlink with space char" 1>&2
+ rm -rf ${TMP}
+ exit 1
+fi
+
+x=x`(cd ${TMP}/mr ; ls -l foo 2>&1) || true`
+y=x`(cd ${TMP}/mt ; ls -l foo 2>&1) || true`
+
+if [ "$x" != "$y" ] ; then
+ echo "ERROR Recreation of spaced symlink failed" 1>&2
+ rm -rf ${TMP}
+ exit 1
+fi
+
+rm -rf ${TMP}
+exit 0
diff --git a/usr.sbin/mtree/test/test02.sh b/usr.sbin/mtree/test/test02.sh
new file mode 100644
index 0000000..a7aa916
--- /dev/null
+++ b/usr.sbin/mtree/test/test02.sh
@@ -0,0 +1,36 @@
+#!/bin/sh
+#
+# Copyright (c) 2003 Dan Nelson
+# All rights reserved.
+#
+# Please see src/share/examples/etc/bsd-style-copyright.
+#
+# $FreeBSD$
+#
+
+set -e
+
+TMP=/tmp/mtree.$$
+
+rm -rf ${TMP}
+mkdir -p ${TMP} ${TMP}/mr ${TMP}/mt
+
+touch -t 199901020304 ${TMP}/mr/oldfile
+touch ${TMP}/mt/oldfile
+
+mtree -c -p ${TMP}/mr > ${TMP}/_
+
+mtree -U -r -p ${TMP}/mt < ${TMP}/_ > /dev/null
+
+x=x`(cd ${TMP}/mr ; ls -l 2>&1) || true`
+y=x`(cd ${TMP}/mt ; ls -l 2>&1) || true`
+
+if [ "$x" != "$y" ] ; then
+ echo "ERROR Update of mtime failed" 1>&2
+ rm -rf ${TMP}
+ exit 1
+fi
+
+rm -rf ${TMP}
+exit 0
+
diff --git a/usr.sbin/mtree/test/test03.sh b/usr.sbin/mtree/test/test03.sh
new file mode 100644
index 0000000..bb3a5b5
--- /dev/null
+++ b/usr.sbin/mtree/test/test03.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+#
+# Copyright (c) 2003 Poul-Henning Kamp
+# All rights reserved.
+#
+# Please see src/share/examples/etc/bsd-style-copyright.
+#
+# $FreeBSD$
+#
+
+set -e
+
+TMP=/tmp/mtree.$$
+
+rm -rf ${TMP}
+mkdir -p ${TMP}
+
+K=uid,uname,gid,gname,flags,md5digest,size,ripemd160digest,sha1digest,sha256digest,cksum
+
+rm -rf _FOO
+mkdir _FOO
+touch _FOO/_uid
+touch _FOO/_size
+touch _FOO/zztype
+
+touch _FOO/_bar
+mtree -c -K $K -p .. > ${TMP}/_r
+mtree -c -K $K -p .. > ${TMP}/_r2
+rm -rf _FOO/_bar
+
+rm -rf _FOO/zztype
+mkdir _FOO/zztype
+
+date > _FOO/_size
+
+chown nobody _FOO/_uid
+
+touch _FOO/_foo
+mtree -c -K $K -p .. > ${TMP}/_t
+
+rm -fr _FOO
+
+if mtree -f ${TMP}/_r -f ${TMP}/_r2 ; then
+ true
+else
+ echo "ERROR Compare identical failed" 1>&2
+ exit 1
+fi
+
+if mtree -f ${TMP}/_r -f ${TMP}/_t > ${TMP}/_ ; then
+ echo "ERROR Compare different succeeded" 1>&2
+ exit 1
+fi
+
+if [ `wc -l < ${TMP}/_` -ne 10 ] ; then
+ echo "ERROR wrong number of lines: `wc -l ${TMP}/_`" 1>&2
+ exit 1
+fi
+
+exit 0
diff --git a/usr.sbin/mtree/test/test04.sh b/usr.sbin/mtree/test/test04.sh
new file mode 100644
index 0000000..38acda2
--- /dev/null
+++ b/usr.sbin/mtree/test/test04.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+#
+# Copyright (c) 2003 Dan Nelson
+# All rights reserved.
+#
+# Please see src/share/examples/etc/bsd-style-copyright.
+#
+# $FreeBSD$
+#
+
+set -e
+
+TMP=/tmp/mtree.$$
+
+rm -rf ${TMP}
+mkdir -p ${TMP} ${TMP}/mr ${TMP}/mt
+
+mkdir ${TMP}/mr/a
+mkdir ${TMP}/mr/b
+mkdir ${TMP}/mt/a
+mkdir ${TMP}/mt/b
+touch ${TMP}/mt/z
+
+mtree -c -p ${TMP}/mr > ${TMP}/_r
+mtree -c -p ${TMP}/mt > ${TMP}/_t
+
+if mtree -f ${TMP}/_r -f ${TMP}/_t > ${TMP}/_ ; then
+ echo "ERROR wrong exit on difference" 1>&2
+ exit 1
+fi
+
+if [ `wc -l < ${TMP}/_` -ne 1 ] ; then
+ echo "ERROR spec/spec compare generated wrong output" 1>&2
+ rm -rf ${TMP}
+ exit 1
+fi
+
+if mtree -f ${TMP}/_t -f ${TMP}/_r > ${TMP}/_ ; then
+ echo "ERROR wrong exit on difference" 1>&2
+ exit 1
+fi
+
+if [ `wc -l < ${TMP}/_` -ne 1 ] ; then
+ echo "ERROR spec/spec compare generated wrong output" 1>&2
+ rm -rf ${TMP}
+ exit 1
+fi
+
+rm -rf ${TMP}
+exit 0
+
diff --git a/usr.sbin/mtree/test/test05.sh b/usr.sbin/mtree/test/test05.sh
new file mode 100644
index 0000000..eda544f
--- /dev/null
+++ b/usr.sbin/mtree/test/test05.sh
@@ -0,0 +1,25 @@
+#!/bin/sh
+#
+# $FreeBSD$
+#
+# Test for 'optional' keyword.
+#
+
+TMP=`mktemp -d /tmp/mtree.XXXXXX`
+mkdir -p ${TMP}/mr ${TMP}/mr/optional-dir ${TMP}/mr/some-dir
+touch ${TMP}/mr/optional-file ${TMP}/mr/some-file
+
+mtree -c -p ${TMP}/mr > ${TMP}/_
+rm -rf ${TMP}/mr/optional-file ${TMP}/mr/optional-dir
+mtree -p ${TMP}/mr -K optional < ${TMP}/_ > /dev/null
+
+res=$?
+
+if [ $res -ne 0 ] ; then
+ echo "ERROR 'optional' keyword failed" 1>&2
+ rm -rf ${TMP}
+ exit 1
+fi
+
+rm -rf ${TMP}
+exit 0
diff --git a/usr.sbin/mtree/verify.c b/usr.sbin/mtree/verify.c
new file mode 100644
index 0000000..b7c4fd9
--- /dev/null
+++ b/usr.sbin/mtree/verify.c
@@ -0,0 +1,259 @@
+/*-
+ * 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)verify.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <fnmatch.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "mtree.h"
+#include "extern.h"
+
+static NODE *root;
+static char path[MAXPATHLEN];
+
+static void miss(NODE *, char *);
+static int vwalk(void);
+
+int
+mtree_verifyspec(FILE *fi)
+{
+ int rval;
+
+ root = mtree_readspec(fi);
+ rval = vwalk();
+ miss(root, path);
+ return (rval);
+}
+
+static int
+nsort(const FTSENT * const *a, const FTSENT * const *b)
+{
+ return (strcmp((*a)->fts_name, (*b)->fts_name));
+}
+
+static int
+vwalk(void)
+{
+ FTS *t;
+ FTSENT *p;
+ NODE *ep, *level;
+ int specdepth, rval;
+ char *argv[2];
+ char dot[] = ".";
+
+ argv[0] = dot;
+ argv[1] = NULL;
+ if ((t = fts_open(argv, ftsoptions, nsort)) == NULL)
+ err(1, "line %d: fts_open", lineno);
+ level = root;
+ specdepth = rval = 0;
+ while ((p = fts_read(t))) {
+ if (check_excludes(p->fts_name, p->fts_path)) {
+ fts_set(t, p, FTS_SKIP);
+ continue;
+ }
+ switch(p->fts_info) {
+ case FTS_D:
+ case FTS_SL:
+ break;
+ case FTS_DP:
+ if (specdepth > p->fts_level) {
+ for (level = level->parent; level->prev;
+ level = level->prev);
+ --specdepth;
+ }
+ continue;
+ case FTS_DNR:
+ case FTS_ERR:
+ case FTS_NS:
+ warnx("%s: %s", RP(p), strerror(p->fts_errno));
+ continue;
+ default:
+ if (dflag)
+ continue;
+ }
+
+ if (specdepth != p->fts_level)
+ goto extra;
+ for (ep = level; ep; ep = ep->next)
+ if ((ep->flags & F_MAGIC &&
+ !fnmatch(ep->name, p->fts_name, FNM_PATHNAME)) ||
+ !strcmp(ep->name, p->fts_name)) {
+ ep->flags |= F_VISIT;
+ if ((ep->flags & F_NOCHANGE) == 0 &&
+ compare(ep->name, ep, p))
+ rval = MISMATCHEXIT;
+ if (ep->flags & F_IGN)
+ (void)fts_set(t, p, FTS_SKIP);
+ else if (ep->child && ep->type == F_DIR &&
+ p->fts_info == FTS_D) {
+ level = ep->child;
+ ++specdepth;
+ }
+ break;
+ }
+
+ if (ep)
+ continue;
+extra:
+ if (!eflag) {
+ (void)printf("%s extra", RP(p));
+ if (rflag) {
+ if ((S_ISDIR(p->fts_statp->st_mode)
+ ? rmdir : unlink)(p->fts_accpath)) {
+ (void)printf(", not removed: %s",
+ strerror(errno));
+ } else
+ (void)printf(", removed");
+ }
+ (void)putchar('\n');
+ }
+ (void)fts_set(t, p, FTS_SKIP);
+ }
+ (void)fts_close(t);
+ if (sflag)
+ warnx("%s checksum: %lu", fullpath, (unsigned long)crc_total);
+ return (rval);
+}
+
+static void
+miss(NODE *p, char *tail)
+{
+ int create;
+ char *tp;
+ const char *type, *what;
+ int serr;
+
+ for (; p; p = p->next) {
+ if (p->flags & F_OPT && !(p->flags & F_VISIT))
+ continue;
+ if (p->type != F_DIR && (dflag || p->flags & F_VISIT))
+ continue;
+ (void)strcpy(tail, p->name);
+ if (!(p->flags & F_VISIT)) {
+ /* Don't print missing message if file exists as a
+ symbolic link and the -q flag is set. */
+ struct stat statbuf;
+
+ if (qflag && stat(path, &statbuf) == 0)
+ p->flags |= F_VISIT;
+ else
+ (void)printf("%s missing", path);
+ }
+ if (p->type != F_DIR && p->type != F_LINK) {
+ putchar('\n');
+ continue;
+ }
+
+ create = 0;
+ if (p->type == F_LINK)
+ type = "symlink";
+ else
+ type = "directory";
+ if (!(p->flags & F_VISIT) && uflag) {
+ if (!(p->flags & (F_UID | F_UNAME)))
+ (void)printf(" (%s not created: user not specified)", type);
+ else if (!(p->flags & (F_GID | F_GNAME)))
+ (void)printf(" (%s not created: group not specified)", type);
+ else if (p->type == F_LINK) {
+ if (symlink(p->slink, path))
+ (void)printf(" (symlink not created: %s)\n",
+ strerror(errno));
+ else
+ (void)printf(" (created)\n");
+ if (lchown(path, p->st_uid, p->st_gid) == -1) {
+ serr = errno;
+ if (p->st_uid == (uid_t)-1)
+ what = "group";
+ else if (lchown(path, (uid_t)-1,
+ p->st_gid) == -1)
+ what = "user & group";
+ else {
+ what = "user";
+ errno = serr;
+ }
+ (void)printf("%s: %s not modified: %s"
+ "\n", path, what, strerror(errno));
+ }
+ continue;
+ } else if (!(p->flags & F_MODE))
+ (void)printf(" (directory not created: mode not specified)");
+ else if (mkdir(path, S_IRWXU))
+ (void)printf(" (directory not created: %s)",
+ strerror(errno));
+ else {
+ create = 1;
+ (void)printf(" (created)");
+ }
+ }
+ if (!(p->flags & F_VISIT))
+ (void)putchar('\n');
+
+ for (tp = tail; *tp; ++tp);
+ *tp = '/';
+ miss(p->child, tp + 1);
+ *tp = '\0';
+
+ if (!create)
+ continue;
+ if (chown(path, p->st_uid, p->st_gid) == -1) {
+ serr = errno;
+ if (p->st_uid == (uid_t)-1)
+ what = "group";
+ else if (chown(path, (uid_t)-1, p->st_gid) == -1)
+ what = "user & group";
+ else {
+ what = "user";
+ errno = serr;
+ }
+ (void)printf("%s: %s not modified: %s\n",
+ path, what, strerror(errno));
+ }
+ if (chmod(path, p->st_mode))
+ (void)printf("%s: permissions not set: %s\n",
+ path, strerror(errno));
+ if ((p->flags & F_FLAGS) && p->st_flags &&
+ chflags(path, p->st_flags))
+ (void)printf("%s: file flags not set: %s\n",
+ path, strerror(errno));
+ }
+}
diff --git a/usr.sbin/named-checkconf/Makefile b/usr.sbin/named-checkconf/Makefile
new file mode 100644
index 0000000..8728b4a
--- /dev/null
+++ b/usr.sbin/named-checkconf/Makefile
@@ -0,0 +1,24 @@
+# $FreeBSD$
+
+BIND_DIR= ${.CURDIR}/../../contrib/bind9
+LIB_BIND_REL= ../../lib/bind
+LIB_BIND_DIR= ${.CURDIR}/${LIB_BIND_REL}
+SRCDIR= ${BIND_DIR}/bin/check
+
+.include "${LIB_BIND_DIR}/config.mk"
+
+PROG= named-checkconf
+
+.PATH: ${SRCDIR}
+SRCS+= named-checkconf.c check-tool.c
+
+CFLAGS+= -I${LIB_BIND_DIR}
+
+DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD}
+LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD}
+
+MAN= named-checkconf.8
+
+MANFILTER= sed -e "s@/etc/named\.conf@/etc/namedb/named.conf@g"
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/named-checkzone/Makefile b/usr.sbin/named-checkzone/Makefile
new file mode 100644
index 0000000..169c00a
--- /dev/null
+++ b/usr.sbin/named-checkzone/Makefile
@@ -0,0 +1,26 @@
+# $FreeBSD$
+
+BIND_DIR= ${.CURDIR}/../../contrib/bind9
+LIB_BIND_REL= ../../lib/bind
+LIB_BIND_DIR= ${.CURDIR}/${LIB_BIND_REL}
+SRCDIR= ${BIND_DIR}/bin/check
+
+.include "${LIB_BIND_DIR}/config.mk"
+
+PROG= named-checkzone
+
+.PATH: ${SRCDIR}
+SRCS+= named-checkzone.c check-tool.c
+
+CFLAGS+= -I${LIB_BIND_DIR}
+
+DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD}
+LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD}
+
+LINKS= ${BINDIR}/named-checkzone ${BINDIR}/named-compilezone
+
+MAN= named-checkzone.8
+
+MLINKS= named-checkzone.8 named-compilezone.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/named.reload/Makefile b/usr.sbin/named.reload/Makefile
new file mode 100644
index 0000000..7ea9d06
--- /dev/null
+++ b/usr.sbin/named.reload/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+SCRIPTS= named.reload.sh
+MAN= named.reload.8
+
+LINKS= ${BINDIR}/named.reload ${BINDIR}/named.reconfig
+MLINKS= named.reload.8 named.reconfig.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/named.reload/named.reload.8 b/usr.sbin/named.reload/named.reload.8
new file mode 100644
index 0000000..6458774
--- /dev/null
+++ b/usr.sbin/named.reload/named.reload.8
@@ -0,0 +1,68 @@
+.\"-
+.\" Copyright (c) 2004 Dag-Erling Coïdan Smørgrav
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer
+.\" in this position and unchanged.
+.\" 2. Redistributions in binary form must 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 September 21, 2004
+.Dt NAMED.RELOAD 8
+.Os
+.Sh NAME
+.Nm named.reconfig ,
+.Nm named.reload
+.Nd reload name server configuration
+.Sh SYNOPSIS
+.Nm named.reconfig
+.Op Fl V
+.Op Fl c config-file
+.Op Fl k key-file
+.Op Fl s server
+.Op Fl p port
+.Op Fl y keyid
+.Nm named.reload
+.Op Fl V
+.Op Fl c config-file
+.Op Fl k key-file
+.Op Fl s server
+.Op Fl p port
+.Op Fl y keyid
+.Sh DESCRIPTION
+The
+.Nm named.reconfig
+command signals the name server to reload its configuration files and
+load any new zones.
+.Pp
+The
+.Nm named.reload
+command signals the name server to reload its configuration files and
+all zones.
+.Pp
+Both these commands take the same command-line arguments as
+.Xr rndc 8 ,
+except for the command argument, which is implied by the name.
+.Sh SEE ALSO
+.Xr named.conf 5 ,
+.Xr rndc.conf 5 ,
+.Xr named 8 ,
+.Xr rndc 8
diff --git a/usr.sbin/named.reload/named.reload.sh b/usr.sbin/named.reload/named.reload.sh
new file mode 100644
index 0000000..d1ee3af
--- /dev/null
+++ b/usr.sbin/named.reload/named.reload.sh
@@ -0,0 +1,42 @@
+#!/bin/sh
+#-
+# Copyright (c) 2004 Dag-Erling Coïdan Smørgrav
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer
+# in this position and unchanged.
+# 2. Redistributions in binary form must 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$
+#
+
+rndc=/usr/sbin/rndc
+me=$(basename $0)
+cmd=${me#named.}
+cmd=${cmd%.sh}
+
+args=$(getopt "c:k:s:p:Vy:" "$@")
+if [ $? -ne 0 -o $# -ne 0 ] ; then
+ echo "usage: ${me} [-V] [-c config-file] [-k key-file]" 1>&2
+ echo " [-s server] [-p port] [-y keyid]" 1>&2
+ exit 1
+fi
+
+exec "${rndc}" "$@" "${cmd}"
diff --git a/usr.sbin/named/Makefile b/usr.sbin/named/Makefile
new file mode 100644
index 0000000..6447058
--- /dev/null
+++ b/usr.sbin/named/Makefile
@@ -0,0 +1,40 @@
+# $FreeBSD$
+
+BIND_DIR= ${.CURDIR}/../../contrib/bind9
+LIB_BIND_REL= ../../lib/bind
+LIB_BIND_DIR= ${.CURDIR}/${LIB_BIND_REL}
+SRCDIR= ${BIND_DIR}/bin/named
+
+.include "${LIB_BIND_DIR}/config.mk"
+
+PROG= named
+
+.PATH: ${SRCDIR}/unix
+SRCS+= os.c
+
+.PATH: ${SRCDIR}
+SRCS+= builtin.c client.c config.c control.c \
+ controlconf.c interfacemgr.c \
+ listenlist.c log.c logconf.c main.c notify.c \
+ query.c server.c sortlist.c \
+ tkeyconf.c tsigconf.c update.c xfrout.c \
+ zoneconf.c \
+ lwaddr.c lwresd.c lwdclient.c lwderror.c lwdgabn.c \
+ lwdgnba.c lwdgrbn.c lwdnoop.c lwsearch.c
+
+CFLAGS+= -I${SRCDIR}/unix/include -I${SRCDIR}/include -I${LIB_BIND_DIR}
+CFLAGS+= -I${BIND_DIR}/lib/isc/${ISC_ATOMIC_ARCH}/include
+
+# Remove the date stamp to make it more obvious when real changes happen
+CFLAGS+= -U__DATE__
+
+DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD}
+LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD}
+
+MAN= named.8 lwresd.8 named.conf.5
+
+MANFILTER= sed -e "s@/etc/named\.conf@/etc/namedb/named.conf@g"
+
+LINKS= ${BINDIR}/named ${BINDIR}/lwresd
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ndiscvt/Makefile b/usr.sbin/ndiscvt/Makefile
new file mode 100644
index 0000000..90ecebb
--- /dev/null
+++ b/usr.sbin/ndiscvt/Makefile
@@ -0,0 +1,30 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../sys/compat/ndis
+
+PROG= ndiscvt
+SRCS= ndiscvt.c
+SRCS+= subr_pe.c
+SRCS+= inf.c inf-token.l inf-parse.y y.tab.h
+
+MAN= ndiscvt.8
+MAN+= ndisgen.8
+
+WARNS?= 4
+
+DPADD= ${LIBL}
+LDADD= -ll
+
+YFLAGS+=-v
+
+CFLAGS+=-I. -I${.CURDIR} -I${.CURDIR}/../../sys
+
+CLEANFILES= y.output
+
+FILES= windrv_stub.c
+FILESNAME= windrv_stub.c
+FILESDIR= /usr/share/misc
+
+SCRIPTS= ndisgen.sh
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ndiscvt/inf-parse.y b/usr.sbin/ndiscvt/inf-parse.y
new file mode 100644
index 0000000..8a84956
--- /dev/null
+++ b/usr.sbin/ndiscvt/inf-parse.y
@@ -0,0 +1,111 @@
+%{
+/*
+ * Copyright (c) 2003
+ * Bill Paul <wpaul@windriver.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 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/queue.h>
+
+#include "inf.h"
+
+extern int yyparse (void);
+extern int yylex (void);
+extern void yyerror(const char *);
+%}
+
+%token EQUALS COMMA EOL
+%token <str> SECTION
+%token <str> STRING
+%token <str> WORD
+
+%union {
+ char *str;
+}
+
+%%
+
+inf_file
+ : inf_list
+ |
+ ;
+
+inf_list
+ : inf
+ | inf_list inf
+ ;
+
+inf
+ : SECTION EOL
+ { section_add($1); }
+ | WORD EQUALS assign EOL
+ { assign_add($1); }
+ | WORD COMMA regkey EOL
+ { regkey_add($1); }
+ | WORD EOL
+ { define_add($1); }
+ | EOL
+ ;
+
+assign
+ : WORD
+ { push_word($1); }
+ | STRING
+ { push_word($1); }
+ | WORD COMMA assign
+ { push_word($1); }
+ | STRING COMMA assign
+ { push_word($1); }
+ | COMMA assign
+ { push_word(NULL); }
+ | COMMA
+ { push_word(NULL); }
+ |
+ ;
+
+regkey
+ : WORD
+ { push_word($1); }
+ | STRING
+ { push_word($1); }
+ | WORD COMMA regkey
+ { push_word($1); }
+ | STRING COMMA regkey
+ { push_word($1); }
+ | COMMA regkey
+ { push_word(NULL); }
+ | COMMA
+ { push_word(NULL); }
+ ;
+%%
diff --git a/usr.sbin/ndiscvt/inf-token.l b/usr.sbin/ndiscvt/inf-token.l
new file mode 100644
index 0000000..cc1508b
--- /dev/null
+++ b/usr.sbin/ndiscvt/inf-token.l
@@ -0,0 +1,129 @@
+%{
+/*
+ * Copyright (c) 2003
+ * Bill Paul <wpaul@windriver.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 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <regex.h>
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "y.tab.h"
+
+int lineno = 1;
+#define YY_NO_UNPUT
+
+int yylex(void);
+void yyerror(const char *);
+
+static void
+update_lineno(const char *cp)
+{
+ while (*cp)
+ if (*cp++ == '\n')
+ lineno++;
+}
+
+%}
+
+%%
+
+[ \t]+ ;
+\n { lineno++; return EOL; }
+\r ;
+;.*$ ;
+\/\/.*$ ;
+= { return EQUALS; }
+, { return COMMA; }
+\"(\\\"|[^"]|\"\")*\" {
+ int len = strlen(yytext) - 2;
+ int blen = len + 1;
+ char *walker;
+ int i;
+ update_lineno(yytext);
+ yylval.str = (char *)malloc(blen);
+ if (yylval.str == NULL)
+ goto out;
+ walker = yylval.str;
+ for (i = 1; i <= len; i++) {
+ if (yytext[i] == '\"') {
+ switch (yytext[i + 1]) {
+ case '\"':
+ i++;
+ break;
+ default:
+ break;
+ }
+ }
+ if (yytext[i] == '\\') {
+ switch (yytext[i + 1]) {
+ case '\n':
+ i += 2;
+ while(isspace(yytext[i]))
+ i++;
+ break;
+ case '\"':
+ i++;
+ break;
+ case '(':
+ i++;
+ break;
+ default:
+ break;
+ }
+ }
+ *walker++ = yytext[i];
+ }
+ *walker++ = '\0';
+ out:;
+ return STRING;
+ }
+\[[a-zA-Z0-9%&\{\}\-\.\/_\\\*\ ]+\] {
+ int len = strlen(yytext);
+ yytext[len-1] = '\0';
+ yylval.str = strdup(yytext+1);
+ return SECTION;
+ }
+[a-zA-Z0-9%&\{\}\-\.\/_\\\*]+ {
+ yylval.str = strdup(yytext);
+ return WORD;
+ }
+%%
+
+void
+yyerror(const char *s)
+{
+ errx(1, "line %d: %s%s %s.", lineno, yytext, yytext?":":"", s);
+}
diff --git a/usr.sbin/ndiscvt/inf.c b/usr.sbin/ndiscvt/inf.c
new file mode 100644
index 0000000..fe4db6a
--- /dev/null
+++ b/usr.sbin/ndiscvt/inf.c
@@ -0,0 +1,910 @@
+/*
+ * Copyright (c) 2003
+ * Bill Paul <wpaul@windriver.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 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include <sys/queue.h>
+
+#include "inf.h"
+
+extern FILE *yyin;
+int yyparse (void);
+
+const char *words[W_MAX]; /* More than we'll need. */
+int idx;
+
+static struct section_head sh;
+static struct reg_head rh;
+static struct assign_head ah;
+
+static char *sstrdup (const char *);
+static struct assign
+ *find_assign (const char *, const char *);
+static struct assign
+ *find_next_assign
+ (struct assign *);
+static struct section
+ *find_section (const char *);
+static void dump_deviceids_pci (void);
+static void dump_deviceids_pcmcia (void);
+static void dump_deviceids_usb (void);
+static void dump_pci_id (const char *);
+static void dump_pcmcia_id (const char *);
+static void dump_usb_id (const char *);
+static void dump_regvals (void);
+static void dump_paramreg (const struct section *,
+ const struct reg *, int);
+
+static FILE *ofp;
+
+int
+inf_parse (FILE *fp, FILE *outfp)
+{
+ TAILQ_INIT(&sh);
+ TAILQ_INIT(&rh);
+ TAILQ_INIT(&ah);
+
+ ofp = outfp;
+ yyin = fp;
+ yyparse();
+
+ dump_deviceids_pci();
+ dump_deviceids_pcmcia();
+ dump_deviceids_usb();
+ fprintf(outfp, "#ifdef NDIS_REGVALS\n");
+ dump_regvals();
+ fprintf(outfp, "#endif /* NDIS_REGVALS */\n");
+
+ return (0);
+}
+
+void
+section_add (const char *s)
+{
+ struct section *sec;
+
+ sec = malloc(sizeof(struct section));
+ bzero(sec, sizeof(struct section));
+ sec->name = s;
+ TAILQ_INSERT_TAIL(&sh, sec, link);
+
+ return;
+}
+
+static struct assign *
+find_assign (const char *s, const char *k)
+{
+ struct assign *assign;
+ char newkey[256];
+
+ /* Deal with string section lookups. */
+
+ if (k != NULL && k[0] == '%') {
+ bzero(newkey, sizeof(newkey));
+ strncpy(newkey, k + 1, strlen(k) - 2);
+ k = newkey;
+ }
+
+ TAILQ_FOREACH(assign, &ah, link) {
+ if (strcasecmp(assign->section->name, s) == 0) {
+ if (k == NULL)
+ return(assign);
+ else
+ if (strcasecmp(assign->key, k) == 0)
+ return(assign);
+ }
+ }
+ return(NULL);
+}
+
+static struct assign *
+find_next_assign (struct assign *a)
+{
+ struct assign *assign;
+
+ TAILQ_FOREACH(assign, &ah, link) {
+ if (assign == a)
+ break;
+ }
+
+ assign = assign->link.tqe_next;
+
+ if (assign == NULL || assign->section != a->section)
+ return(NULL);
+
+ return (assign);
+}
+
+static const char *
+stringcvt(const char *s)
+{
+ struct assign *manf;
+
+ manf = find_assign("strings", s);
+ if (manf == NULL)
+ return(s);
+ return(manf->vals[0]);
+}
+
+struct section *
+find_section (const char *s)
+{
+ struct section *section;
+
+ TAILQ_FOREACH(section, &sh, link) {
+ if (strcasecmp(section->name, s) == 0)
+ return(section);
+ }
+ return(NULL);
+}
+
+static void
+dump_pcmcia_id(const char *s)
+{
+ char *manstr, *devstr;
+ char *p0, *p;
+
+ p0 = __DECONST(char *, s);
+
+ p = strchr(p0, '\\');
+ if (p == NULL)
+ return;
+ p0 = p + 1;
+
+ p = strchr(p0, '-');
+ if (p == NULL)
+ return;
+ *p = '\0';
+
+ manstr = p0;
+
+ /* Convert any underscores to spaces. */
+
+ while (*p0 != '\0') {
+ if (*p0 == '_')
+ *p0 = ' ';
+ p0++;
+ }
+
+ p0 = p + 1;
+ p = strchr(p0, '-');
+ if (p == NULL)
+ return;
+ *p = '\0';
+
+ devstr = p0;
+
+ /* Convert any underscores to spaces. */
+
+ while (*p0 != '\0') {
+ if (*p0 == '_')
+ *p0 = ' ';
+ p0++;
+ }
+
+ fprintf(ofp, "\t\\\n\t{ \"%s\", \"%s\", ", manstr, devstr);
+ return;
+}
+
+static void
+dump_pci_id(const char *s)
+{
+ char *p;
+ char vidstr[7], didstr[7], subsysstr[14];
+
+ p = strcasestr(s, "VEN_");
+ if (p == NULL)
+ return;
+ p += 4;
+ strcpy(vidstr, "0x");
+ strncat(vidstr, p, 4);
+ p = strcasestr(s, "DEV_");
+ if (p == NULL)
+ return;
+ p += 4;
+ strcpy(didstr, "0x");
+ strncat(didstr, p, 4);
+ if (p == NULL)
+ return;
+ p = strcasestr(s, "SUBSYS_");
+ if (p == NULL)
+ strcpy(subsysstr, "0x00000000");
+ else {
+ p += 7;
+ strcpy(subsysstr, "0x");
+ strncat(subsysstr, p, 8);
+ }
+
+ fprintf(ofp, "\t\\\n\t{ %s, %s, %s, ", vidstr, didstr, subsysstr);
+ return;
+}
+
+static void
+dump_usb_id(const char *s)
+{
+ char *p;
+ char vidstr[7], pidstr[7];
+
+ p = strcasestr(s, "VID_");
+ if (p == NULL)
+ return;
+ p += 4;
+ strcpy(vidstr, "0x");
+ strncat(vidstr, p, 4);
+ p = strcasestr(s, "PID_");
+ if (p == NULL)
+ return;
+ p += 4;
+ strcpy(pidstr, "0x");
+ strncat(pidstr, p, 4);
+ if (p == NULL)
+ return;
+
+ fprintf(ofp, "\t\\\n\t{ %s, %s, ", vidstr, pidstr);
+}
+
+static void
+dump_deviceids_pci()
+{
+ struct assign *manf, *dev;
+ struct section *sec;
+ struct assign *assign;
+ char xpsec[256];
+ int first = 1, found = 0;
+
+ /* Find manufacturer name */
+ manf = find_assign("Manufacturer", NULL);
+
+nextmanf:
+
+ /* Find manufacturer section */
+ if (manf->vals[1] != NULL &&
+ (strcasecmp(manf->vals[1], "NT.5.1") == 0 ||
+ strcasecmp(manf->vals[1], "NTx86") == 0 ||
+ strcasecmp(manf->vals[1], "NTx86.5.1") == 0 ||
+ strcasecmp(manf->vals[1], "NTamd64") == 0)) {
+ /* Handle Windows XP INF files. */
+ snprintf(xpsec, sizeof(xpsec), "%s.%s",
+ manf->vals[0], manf->vals[1]);
+ sec = find_section(xpsec);
+ } else
+ sec = find_section(manf->vals[0]);
+
+ /* See if there are any PCI device definitions. */
+
+ TAILQ_FOREACH(assign, &ah, link) {
+ if (assign->section == sec) {
+ dev = find_assign("strings", assign->key);
+ if (strcasestr(assign->vals[1], "PCI") != NULL) {
+ found++;
+ break;
+ }
+ }
+ }
+
+ if (found == 0)
+ goto done;
+
+ found = 0;
+
+ if (first == 1) {
+ /* Emit start of PCI device table */
+ fprintf (ofp, "#define NDIS_PCI_DEV_TABLE");
+ first = 0;
+ }
+
+retry:
+
+ /*
+ * Now run through all the device names listed
+ * in the manufacturer section and dump out the
+ * device descriptions and vendor/device IDs.
+ */
+
+ TAILQ_FOREACH(assign, &ah, link) {
+ if (assign->section == sec) {
+ dev = find_assign("strings", assign->key);
+ /* Emit device IDs. */
+ if (strcasestr(assign->vals[1], "PCI") != NULL)
+ dump_pci_id(assign->vals[1]);
+ else
+ continue;
+ /* Emit device description */
+ fprintf (ofp, "\t\\\n\t\"%s\" },", dev->vals[0]);
+ found++;
+ }
+ }
+
+ /* Someone tried to fool us. Shame on them. */
+ if (!found) {
+ found++;
+ sec = find_section(manf->vals[0]);
+ goto retry;
+ }
+
+ /* Handle Manufacturer sections with multiple entries. */
+ manf = find_next_assign(manf);
+
+ if (manf != NULL)
+ goto nextmanf;
+
+done:
+ /* Emit end of table */
+
+ fprintf(ofp, "\n\n");
+
+ return;
+}
+
+static void
+dump_deviceids_pcmcia()
+{
+ struct assign *manf, *dev;
+ struct section *sec;
+ struct assign *assign;
+ char xpsec[256];
+ int first = 1, found = 0;
+
+ /* Find manufacturer name */
+ manf = find_assign("Manufacturer", NULL);
+
+nextmanf:
+
+ /* Find manufacturer section */
+ if (manf->vals[1] != NULL &&
+ (strcasecmp(manf->vals[1], "NT.5.1") == 0 ||
+ strcasecmp(manf->vals[1], "NTx86") == 0 ||
+ strcasecmp(manf->vals[1], "NTx86.5.1") == 0 ||
+ strcasecmp(manf->vals[1], "NTamd64") == 0)) {
+ /* Handle Windows XP INF files. */
+ snprintf(xpsec, sizeof(xpsec), "%s.%s",
+ manf->vals[0], manf->vals[1]);
+ sec = find_section(xpsec);
+ } else
+ sec = find_section(manf->vals[0]);
+
+ /* See if there are any PCMCIA device definitions. */
+
+ TAILQ_FOREACH(assign, &ah, link) {
+ if (assign->section == sec) {
+ dev = find_assign("strings", assign->key);
+ if (strcasestr(assign->vals[1], "PCMCIA") != NULL) {
+ found++;
+ break;
+ }
+ }
+ }
+
+ if (found == 0)
+ goto done;
+
+ found = 0;
+
+ if (first == 1) {
+ /* Emit start of PCMCIA device table */
+ fprintf (ofp, "#define NDIS_PCMCIA_DEV_TABLE");
+ first = 0;
+ }
+
+retry:
+
+ /*
+ * Now run through all the device names listed
+ * in the manufacturer section and dump out the
+ * device descriptions and vendor/device IDs.
+ */
+
+ TAILQ_FOREACH(assign, &ah, link) {
+ if (assign->section == sec) {
+ dev = find_assign("strings", assign->key);
+ /* Emit device IDs. */
+ if (strcasestr(assign->vals[1], "PCMCIA") != NULL)
+ dump_pcmcia_id(assign->vals[1]);
+ else
+ continue;
+ /* Emit device description */
+ fprintf (ofp, "\t\\\n\t\"%s\" },", dev->vals[0]);
+ found++;
+ }
+ }
+
+ /* Someone tried to fool us. Shame on them. */
+ if (!found) {
+ found++;
+ sec = find_section(manf->vals[0]);
+ goto retry;
+ }
+
+ /* Handle Manufacturer sections with multiple entries. */
+ manf = find_next_assign(manf);
+
+ if (manf != NULL)
+ goto nextmanf;
+
+done:
+ /* Emit end of table */
+
+ fprintf(ofp, "\n\n");
+
+ return;
+}
+
+static void
+dump_deviceids_usb()
+{
+ struct assign *manf, *dev;
+ struct section *sec;
+ struct assign *assign;
+ char xpsec[256];
+ int first = 1, found = 0;
+
+ /* Find manufacturer name */
+ manf = find_assign("Manufacturer", NULL);
+
+nextmanf:
+
+ /* Find manufacturer section */
+ if (manf->vals[1] != NULL &&
+ (strcasecmp(manf->vals[1], "NT.5.1") == 0 ||
+ strcasecmp(manf->vals[1], "NTx86") == 0 ||
+ strcasecmp(manf->vals[1], "NTx86.5.1") == 0 ||
+ strcasecmp(manf->vals[1], "NTamd64") == 0)) {
+ /* Handle Windows XP INF files. */
+ snprintf(xpsec, sizeof(xpsec), "%s.%s",
+ manf->vals[0], manf->vals[1]);
+ sec = find_section(xpsec);
+ } else
+ sec = find_section(manf->vals[0]);
+
+ /* See if there are any USB device definitions. */
+
+ TAILQ_FOREACH(assign, &ah, link) {
+ if (assign->section == sec) {
+ dev = find_assign("strings", assign->key);
+ if (strcasestr(assign->vals[1], "USB") != NULL) {
+ found++;
+ break;
+ }
+ }
+ }
+
+ if (found == 0)
+ goto done;
+
+ found = 0;
+
+ if (first == 1) {
+ /* Emit start of USB device table */
+ fprintf (ofp, "#define NDIS_USB_DEV_TABLE");
+ first = 0;
+ }
+
+retry:
+
+ /*
+ * Now run through all the device names listed
+ * in the manufacturer section and dump out the
+ * device descriptions and vendor/device IDs.
+ */
+
+ TAILQ_FOREACH(assign, &ah, link) {
+ if (assign->section == sec) {
+ dev = find_assign("strings", assign->key);
+ /* Emit device IDs. */
+ if (strcasestr(assign->vals[1], "USB") != NULL)
+ dump_usb_id(assign->vals[1]);
+ else
+ continue;
+ /* Emit device description */
+ fprintf (ofp, "\t\\\n\t\"%s\" },", dev->vals[0]);
+ found++;
+ }
+ }
+
+ /* Someone tried to fool us. Shame on them. */
+ if (!found) {
+ found++;
+ sec = find_section(manf->vals[0]);
+ goto retry;
+ }
+
+ /* Handle Manufacturer sections with multiple entries. */
+ manf = find_next_assign(manf);
+
+ if (manf != NULL)
+ goto nextmanf;
+
+done:
+ /* Emit end of table */
+
+ fprintf(ofp, "\n\n");
+
+ return;
+}
+
+static void
+dump_addreg(const char *s, int devidx)
+{
+ struct section *sec;
+ struct reg *reg;
+
+ /* Find the addreg section */
+ sec = find_section(s);
+
+ /* Dump all the keys defined in it. */
+ TAILQ_FOREACH(reg, &rh, link) {
+ /*
+ * Keys with an empty subkey are very easy to parse,
+ * so just deal with them here. If a parameter key
+ * of the same name also exists, prefer that one and
+ * skip this one.
+ */
+ if (reg->section == sec) {
+ if (reg->subkey == NULL) {
+ fprintf(ofp, "\n\t{ \"%s\",", reg->key);
+ fprintf(ofp,"\n\t\"%s \",", reg->key);
+ fprintf(ofp, "\n\t{ \"%s\" }, %d },",
+ reg->value == NULL ? "" :
+ stringcvt(reg->value), devidx);
+ } else if (strncasecmp(reg->subkey,
+ "Ndi\\params", strlen("Ndi\\params")-1) == 0 &&
+ (reg->key != NULL && strcasecmp(reg->key,
+ "ParamDesc") == 0))
+ dump_paramreg(sec, reg, devidx);
+ }
+ }
+
+ return;
+}
+
+static void
+dump_enumreg(const struct section *s, const struct reg *r)
+{
+ struct reg *reg;
+ char enumkey[256];
+
+ sprintf(enumkey, "%s\\enum", r->subkey);
+ TAILQ_FOREACH(reg, &rh, link) {
+ if (reg->section != s)
+ continue;
+ if (reg->subkey == NULL || strcasecmp(reg->subkey, enumkey))
+ continue;
+ fprintf(ofp, " [%s=%s]", reg->key,
+ stringcvt(reg->value));
+ }
+ return;
+}
+
+static void
+dump_editreg(const struct section *s, const struct reg *r)
+{
+ struct reg *reg;
+
+ TAILQ_FOREACH(reg, &rh, link) {
+ if (reg->section != s)
+ continue;
+ if (reg->subkey == NULL || strcasecmp(reg->subkey, r->subkey))
+ continue;
+ if (reg->key == NULL)
+ continue;
+ if (strcasecmp(reg->key, "LimitText") == 0)
+ fprintf(ofp, " [maxchars=%s]", reg->value);
+ if (strcasecmp(reg->key, "Optional") == 0 &&
+ strcmp(reg->value, "1") == 0)
+ fprintf(ofp, " [optional]");
+ }
+ return;
+}
+
+/* Use this for int too */
+static void
+dump_dwordreg(const struct section *s, const struct reg *r)
+{
+ struct reg *reg;
+
+ TAILQ_FOREACH(reg, &rh, link) {
+ if (reg->section != s)
+ continue;
+ if (reg->subkey == NULL || strcasecmp(reg->subkey, r->subkey))
+ continue;
+ if (reg->key == NULL)
+ continue;
+ if (strcasecmp(reg->key, "min") == 0)
+ fprintf(ofp, " [min=%s]", reg->value);
+ if (strcasecmp(reg->key, "max") == 0)
+ fprintf(ofp, " [max=%s]", reg->value);
+ }
+ return;
+}
+
+static void
+dump_defaultinfo(const struct section *s, const struct reg *r, int devidx)
+{
+ struct reg *reg;
+ TAILQ_FOREACH(reg, &rh, link) {
+ if (reg->section != s)
+ continue;
+ if (reg->subkey == NULL || strcasecmp(reg->subkey, r->subkey))
+ continue;
+ if (reg->key == NULL || strcasecmp(reg->key, "Default"))
+ continue;
+ fprintf(ofp, "\n\t{ \"%s\" }, %d },", reg->value == NULL ? "" :
+ stringcvt(reg->value), devidx);
+ return;
+ }
+ /* Default registry entry missing */
+ fprintf(ofp, "\n\t{ \"\" }, %d },", devidx);
+ return;
+}
+
+static void
+dump_paramdesc(const struct section *s, const struct reg *r)
+{
+ struct reg *reg;
+ TAILQ_FOREACH(reg, &rh, link) {
+ if (reg->section != s)
+ continue;
+ if (reg->subkey == NULL || strcasecmp(reg->subkey, r->subkey))
+ continue;
+ if (reg->key == NULL || strcasecmp(reg->key, "ParamDesc"))
+ continue;
+ fprintf(ofp, "\n\t\"%s", stringcvt(r->value));
+ break;
+ }
+ return;
+}
+
+static void
+dump_typeinfo(const struct section *s, const struct reg *r)
+{
+ struct reg *reg;
+ TAILQ_FOREACH(reg, &rh, link) {
+ if (reg->section != s)
+ continue;
+ if (reg->subkey == NULL || strcasecmp(reg->subkey, r->subkey))
+ continue;
+ if (reg->key == NULL)
+ continue;
+ if (strcasecmp(reg->key, "type"))
+ continue;
+ if (strcasecmp(reg->value, "dword") == 0 ||
+ strcasecmp(reg->value, "int") == 0)
+ dump_dwordreg(s, r);
+ if (strcasecmp(reg->value, "enum") == 0)
+ dump_enumreg(s, r);
+ if (strcasecmp(reg->value, "edit") == 0)
+ dump_editreg(s, r);
+ }
+ return;
+}
+
+static void
+dump_paramreg(const struct section *s, const struct reg *r, int devidx)
+{
+ const char *keyname;
+
+ keyname = r->subkey + strlen("Ndi\\params\\");
+ fprintf(ofp, "\n\t{ \"%s\",", keyname);
+ dump_paramdesc(s, r);
+ dump_typeinfo(s, r);
+ fprintf(ofp, "\",");
+ dump_defaultinfo(s, r, devidx);
+
+ return;
+}
+
+static void
+dump_regvals(void)
+{
+ struct assign *manf, *dev;
+ struct section *sec;
+ struct assign *assign;
+ char sname[256];
+ int found = 0, i, is_winxp = 0, is_winnt = 0, devidx = 0;
+
+ /* Find signature to check for special case of WinNT. */
+ assign = find_assign("version", "signature");
+ if (strcasecmp(assign->vals[0], "$windows nt$") == 0)
+ is_winnt++;
+
+ /* Emit start of block */
+ fprintf (ofp, "ndis_cfg ndis_regvals[] = {");
+
+ /* Find manufacturer name */
+ manf = find_assign("Manufacturer", NULL);
+
+nextmanf:
+
+ /* Find manufacturer section */
+ if (manf->vals[1] != NULL &&
+ (strcasecmp(manf->vals[1], "NT.5.1") == 0 ||
+ strcasecmp(manf->vals[1], "NTx86") == 0 ||
+ strcasecmp(manf->vals[1], "NTx86.5.1") == 0 ||
+ strcasecmp(manf->vals[1], "NTamd64") == 0)) {
+ is_winxp++;
+ /* Handle Windows XP INF files. */
+ snprintf(sname, sizeof(sname), "%s.%s",
+ manf->vals[0], manf->vals[1]);
+ sec = find_section(sname);
+ } else
+ sec = find_section(manf->vals[0]);
+
+retry:
+
+ TAILQ_FOREACH(assign, &ah, link) {
+ if (assign->section == sec) {
+ found++;
+ /*
+ * Find all the AddReg sections.
+ * Look for section names with .NT, unless
+ * this is a WinXP .INF file.
+ */
+
+ if (is_winxp) {
+ sprintf(sname, "%s.NTx86", assign->vals[0]);
+ dev = find_assign(sname, "AddReg");
+ if (dev == NULL) {
+ sprintf(sname, "%s.NT",
+ assign->vals[0]);
+ dev = find_assign(sname, "AddReg");
+ }
+ if (dev == NULL)
+ dev = find_assign(assign->vals[0],
+ "AddReg");
+ } else {
+ sprintf(sname, "%s.NT", assign->vals[0]);
+ dev = find_assign(sname, "AddReg");
+ if (dev == NULL && is_winnt)
+ dev = find_assign(assign->vals[0],
+ "AddReg");
+ }
+ /* Section not found. */
+ if (dev == NULL)
+ continue;
+ for (i = 0; i < W_MAX; i++) {
+ if (dev->vals[i] != NULL)
+ dump_addreg(dev->vals[i], devidx);
+ }
+ devidx++;
+ }
+ }
+
+ if (!found) {
+ sec = find_section(manf->vals[0]);
+ is_winxp = 0;
+ found++;
+ goto retry;
+ }
+
+ manf = find_next_assign(manf);
+
+ if (manf != NULL)
+ goto nextmanf;
+
+ fprintf(ofp, "\n\t{ NULL, NULL, { 0 }, 0 }\n};\n\n");
+
+ return;
+}
+
+void
+assign_add (const char *a)
+{
+ struct assign *assign;
+ int i;
+
+ assign = malloc(sizeof(struct assign));
+ bzero(assign, sizeof(struct assign));
+ assign->section = TAILQ_LAST(&sh, section_head);
+ assign->key = sstrdup(a);
+ for (i = 0; i < idx; i++)
+ assign->vals[(idx - 1) - i] = sstrdup(words[i]);
+ TAILQ_INSERT_TAIL(&ah, assign, link);
+
+ clear_words();
+ return;
+}
+
+void
+define_add (const char *d __unused)
+{
+#ifdef notdef
+ fprintf(stderr, "define \"%s\"\n", d);
+#endif
+ return;
+}
+
+static char *
+sstrdup(const char *str)
+{
+ if (str != NULL && strlen(str))
+ return (strdup(str));
+ return (NULL);
+}
+
+static int
+satoi (const char *nptr)
+{
+ if (nptr != NULL && strlen(nptr))
+ return (atoi(nptr));
+ return (0);
+}
+
+void
+regkey_add (const char *r)
+{
+ struct reg *reg;
+
+ reg = malloc(sizeof(struct reg));
+ bzero(reg, sizeof(struct reg));
+ reg->section = TAILQ_LAST(&sh, section_head);
+ reg->root = sstrdup(r);
+ reg->subkey = sstrdup(words[3]);
+ reg->key = sstrdup(words[2]);
+ reg->flags = satoi(words[1]);
+ reg->value = sstrdup(words[0]);
+ TAILQ_INSERT_TAIL(&rh, reg, link);
+
+ free(__DECONST(char *, r));
+ clear_words();
+ return;
+}
+
+void
+push_word (const char *w)
+{
+ if (w && strlen(w))
+ words[idx++] = w;
+ else
+ words[idx++] = NULL;
+ return;
+}
+
+void
+clear_words (void)
+{
+ int i;
+
+ for (i = 0; i < idx; i++) {
+ if (words[i]) {
+ free(__DECONST(char *, words[i]));
+ }
+ }
+ idx = 0;
+ bzero(words, sizeof(words));
+ return;
+}
diff --git a/usr.sbin/ndiscvt/inf.h b/usr.sbin/ndiscvt/inf.h
new file mode 100644
index 0000000..8d0b0c1
--- /dev/null
+++ b/usr.sbin/ndiscvt/inf.h
@@ -0,0 +1,61 @@
+/*
+ * $Id: inf.h,v 1.3 2003/11/30 21:58:16 winter Exp $
+ *
+ * $FreeBSD$
+ */
+
+#define W_MAX 16
+
+struct section {
+ const char * name;
+
+ TAILQ_ENTRY(section) link;
+};
+TAILQ_HEAD(section_head, section);
+
+struct assign {
+ struct section *section;
+
+ const char * key;
+ const char * vals[W_MAX];
+
+ TAILQ_ENTRY(assign) link;
+};
+TAILQ_HEAD(assign_head, assign);
+
+struct reg {
+ struct section *section;
+
+ const char * root;
+ const char * subkey;
+ const char * key;
+ u_int flags;
+ const char * value;
+
+ TAILQ_ENTRY(reg) link;
+};
+TAILQ_HEAD(reg_head, reg);
+
+#define FLG_ADDREG_TYPE_SZ 0x00000000
+#define FLG_ADDREG_BINVALUETYPE 0x00000001
+#define FLG_ADDREG_NOCLOBBER 0x00000002
+#define FLG_ADDREG_DELVAL 0x00000004
+#define FLG_ADDREG_APPEND 0x00000008
+#define FLG_ADDREG_KEYONLY 0x00000010
+#define FLG_ADDREG_OVERWRITEONLY 0x00000020
+#define FLG_ADDREG_64BITKEY 0x00001000
+#define FLG_ADDREG_KEYONLY_COMMON 0x00002000
+#define FLG_ADDREG_32BITKEY 0x00004000
+#define FLG_ADDREG_TYPE_MULTI_SZ 0x00010000
+#define FLG_ADDREG_TYPE_EXPAND_SZ 0x00020000
+#define FLG_ADDREG_TYPE_DWORD 0x00010001
+#define FLG_ADDREG_TYPE_NONE 0x00020001
+
+extern void section_add (const char *);
+extern void assign_add (const char *);
+extern void define_add (const char *);
+extern void regkey_add (const char *);
+
+extern void push_word (const char *);
+extern void clear_words (void);
+extern int inf_parse (FILE *, FILE *);
diff --git a/usr.sbin/ndiscvt/ndiscvt.8 b/usr.sbin/ndiscvt/ndiscvt.8
new file mode 100644
index 0000000..532de8e
--- /dev/null
+++ b/usr.sbin/ndiscvt/ndiscvt.8
@@ -0,0 +1,283 @@
+.\" Copyright (c) 2003
+.\" Bill Paul <wpaul@windriver.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 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 10, 2003
+.Dt NDISCVT 8
+.Os
+.Sh NAME
+.Nm ndiscvt
+.Nd convert
+.Tn Windows\[rg]
+NDIS drivers for use with FreeBSD
+.Sh SYNOPSIS
+.Nm
+.Op Fl O
+.Op Fl i Ar inffile
+.Fl s Ar sysfile
+.Op Fl n Ar devname
+.Op Fl o Ar outfile
+.Nm
+.Op Fl f Ar firmfile
+.Sh DESCRIPTION
+The
+.Nm
+utility transforms a
+.Tn Windows\[rg]
+NDIS driver into a data file which
+is used to build an
+.Xr ndis 4
+compatibility driver module.
+.Tn Windows\[rg]
+drivers consist of two main parts: a
+.Pa .SYS
+file, which contains the actual driver executable code,
+and an
+.Pa .INF
+file, which provides the
+.Tn Windows\[rg]
+installer with device
+identifier information and a list of driver-specific registry keys.
+The
+.Nm
+utility can convert these files into a header file that is compiled
+into
+.Pa if_ndis.c
+to create an object code module that can be linked into
+the
+.Fx
+kernel.
+.Pp
+The
+.Pa .INF
+file is typically required since only it contains device
+identification data such as PCI vendor and device IDs or PCMCIA
+identifier strings.
+The
+.Pa .INF
+file may be optionally omitted however,
+in which case the
+.Nm
+utility will only perform the conversion of the
+.Pa .SYS
+file.
+This is useful for debugging purposes only.
+.Sh OPTIONS
+The options are as follows:
+.Bl -tag -width indent
+.It Fl i Ar inffile
+Open and parse the specified
+.Pa .INF
+file when performing conversion.
+The
+.Nm
+utility will parse this file and emit a device identification
+structure and registry key configuration structures which will be
+used by the
+.Xr ndis 4
+driver and
+.Xr ndisapi 9
+kernel subsystem.
+If this is omitted,
+.Nm
+will emit a dummy configuration structure only.
+.It Fl s Ar sysfile
+Open and parse the specified
+.Pa .SYS
+file.
+This file must contain a
+.Tn Windows\[rg]
+driver image.
+The
+.Nm
+utility will perform some manipulation of the sections within the
+executable file to make runtime linking within the kernel a little
+easier and then convert the image into a data array.
+.It Fl n Ar devname
+Specify an alternate name for the network device/interface which will
+be created when the driver is instantiated.
+If you need to load more
+than one NDIS driver into your system (i.e., if you have two different
+network cards in your system which require NDIS driver support), each
+module you create must have a unique name.
+Device can not be larger than
+.Dv IFNAMSIZ .
+If no name is specified, the driver will use the
+default a default name
+.Pq Dq Li ndis .
+.It Fl o Ar outfile
+Specify the output file in which to place the resulting data.
+This can be any file pathname.
+If
+.Ar outfile
+is a single dash
+.Pq Sq Fl ,
+the data will be written to the standard output.
+The
+.Pa if_ndis.c
+module expects to find the driver data in a file called
+.Pa ndis_driver_data.h ,
+so it is recommended that this name be used.
+.It Fl O
+Generate both an
+.Pa ndis_driver_data.h
+file and
+an
+.Pa ndis_driver.data.o
+file.
+The latter file will contain a copy of the
+.Tn Windows\[rg]
+.Pa .SYS
+driver image encoded as a
+.Fx
+ELF object file
+(created with
+.Xr objcopy 1 ) .
+Turning the
+.Tn Windows\[rg]
+driver image directly into an object code file saves disk space
+and compilation time.
+.It Fl f Ar firmfile
+A few NDIS drivers come with additional files that the core
+driver module will load during initialization time.
+Typically,
+these files contain firmware which the driver will transfer to
+the device in order to make it fully operational.
+In
+.Tn Windows\[rg] ,
+these files are usually just copied into one of the system
+directories along with the driver itself.
+.Pp
+In
+.Fx
+there are two mechanism for loading these files.
+If the driver
+is built as a loadable kernel module which is loaded after the
+kernel has finished booting
+(and after the root file system has
+been mounted),
+the extra files can simply be copied to the
+.Pa /compat/ndis
+directory, and they will be loaded into the kernel on demand when the
+the driver needs them.
+.Pp
+If however the driver is required to bootstrap the system
+(i.e., if
+the NDIS-based network interface is to be used for diskless/PXE
+booting),
+the files need to be pre-loaded by the bootstrap
+loader in order to be accessible, since the driver will need them
+before the root file system has been mounted.
+However, the bootstrap
+loader is only able to load files that are shared
+.Fx
+binary objects.
+.Pp
+The
+.Fl f
+flag can be used to convert an arbitrary file
+.Ar firmfile
+into shared object format
+(the actual conversion is done using
+the
+.Xr objcopy 1
+and
+.Xr ld 1
+commands).
+The resulting files can then be copied to the
+.Pa /boot/kernel
+directory, and can be pre-loaded directly from the boot loader
+prompt, or automatically by editing the
+.Xr loader.conf 5
+file.
+If desired, the files can also be loaded into memory
+at runtime using the
+.Xr kldload 8
+command.
+.Pp
+When an NDIS driver tries to open an external file, the
+.Xr ndisapi 9
+code will first search for a loaded kernel module that matches the
+name specified in the open request, and if that fails, it will then
+try to open the file from the
+.Pa /compat/ndis
+directory as well.
+Note that during kernel bootstrap, the ability
+to open files from
+.Pa /compat/ndis
+is disabled: only the module search will be performed.
+.Pp
+When using the
+.Fl f
+flag,
+.Nm
+will generate both a relocatable object file
+(with a
+.Pa .o
+extension)
+and a shared object file
+(with a
+.Pa .ko
+extension).
+The shared object is the one that should be placed in
+the
+.Pa /boot/kernel
+directory.
+The relocatable object file is useful if the user wishes
+to create a completely static kernel image: the object file can be
+linked into the kernel directly along with the driver itself.
+Some
+editing of the kernel configuration files will be necessary in order
+to have the extra object included in the build.
+.El
+.Sh SEE ALSO
+.Xr ld 1 ,
+.Xr objcopy 1 ,
+.Xr ndis 4 ,
+.Xr kldload 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 5.3 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility was written by
+.An Bill Paul Aq wpaul@windriver.com .
+The
+.Xr lex 1
+and
+.Xr yacc 1
+.Pa INF
+file parser was written by
+.An Matthew Dodd Aq mdodd@FreeBSD.org .
diff --git a/usr.sbin/ndiscvt/ndiscvt.c b/usr.sbin/ndiscvt/ndiscvt.c
new file mode 100644
index 0000000..34d3120
--- /dev/null
+++ b/usr.sbin/ndiscvt/ndiscvt.c
@@ -0,0 +1,432 @@
+/*
+ * Copyright (c) 2003
+ * Bill Paul <wpaul@windriver.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 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <libgen.h>
+#include <err.h>
+#include <ctype.h>
+
+#include <compat/ndis/pe_var.h>
+
+#include "inf.h"
+
+static int insert_padding(void **, int *);
+extern const char *__progname;
+
+/*
+ * Sections within Windows PE files are defined using virtual
+ * and physical address offsets and virtual and physical sizes.
+ * The physical values define how the section data is stored in
+ * the executable file while the virtual values describe how the
+ * sections will look once loaded into memory. It happens that
+ * the linker in the Microsoft(r) DDK will tend to generate
+ * binaries where the virtual and physical values are identical,
+ * which means in most cases we can just transfer the file
+ * directly to memory without any fixups. This is not always
+ * the case though, so we have to be prepared to handle files
+ * where the in-memory section layout differs from the disk file
+ * section layout.
+ *
+ * There are two kinds of variations that can occur: the relative
+ * virtual address of the section might be different from the
+ * physical file offset, and the virtual section size might be
+ * different from the physical size (for example, the physical
+ * size of the .data section might be 1024 bytes, but the virtual
+ * size might be 1384 bytes, indicating that the data section should
+ * actually use up 1384 bytes in RAM and be padded with zeros). What we
+ * do is read the original file into memory and then make an in-memory
+ * copy with all of the sections relocated, re-sized and zero padded
+ * according to the virtual values specified in the section headers.
+ * We then emit the fixed up image file for use by the if_ndis driver.
+ * This way, we don't have to do the fixups inside the kernel.
+ */
+
+#define ROUND_DOWN(n, align) (((uintptr_t)n) & ~((align) - 1l))
+#define ROUND_UP(n, align) ROUND_DOWN(((uintptr_t)n) + (align) - 1l, \
+ (align))
+
+#define SET_HDRS(x) \
+ dos_hdr = (image_dos_header *)x; \
+ nt_hdr = (image_nt_header *)(x + dos_hdr->idh_lfanew); \
+ sect_hdr = IMAGE_FIRST_SECTION(nt_hdr);
+
+static
+int insert_padding(imgbase, imglen)
+ void **imgbase;
+ int *imglen;
+{
+ image_section_header *sect_hdr;
+ image_dos_header *dos_hdr;
+ image_nt_header *nt_hdr;
+ image_optional_header opt_hdr;
+ int i = 0, sections, curlen = 0;
+ int offaccum = 0, oldraddr, oldrlen;
+ uint8_t *newimg, *tmp;
+
+ newimg = malloc(*imglen);
+
+ if (newimg == NULL)
+ return(ENOMEM);
+
+ bcopy(*imgbase, newimg, *imglen);
+ curlen = *imglen;
+
+ if (pe_get_optional_header((vm_offset_t)newimg, &opt_hdr))
+ return(0);
+
+ sections = pe_numsections((vm_offset_t)newimg);
+
+ SET_HDRS(newimg);
+
+ for (i = 0; i < sections; i++) {
+ oldraddr = sect_hdr->ish_rawdataaddr;
+ oldrlen = sect_hdr->ish_rawdatasize;
+ sect_hdr->ish_rawdataaddr = sect_hdr->ish_vaddr;
+ offaccum += ROUND_UP(sect_hdr->ish_vaddr - oldraddr,
+ opt_hdr.ioh_filealign);
+ offaccum +=
+ ROUND_UP(sect_hdr->ish_misc.ish_vsize,
+ opt_hdr.ioh_filealign) -
+ ROUND_UP(sect_hdr->ish_rawdatasize,
+ opt_hdr.ioh_filealign);
+ tmp = realloc(newimg, *imglen + offaccum);
+ if (tmp == NULL) {
+ free(newimg);
+ return(ENOMEM);
+ }
+ newimg = tmp;
+ SET_HDRS(newimg);
+ sect_hdr += i;
+ bzero(newimg + sect_hdr->ish_rawdataaddr,
+ ROUND_UP(sect_hdr->ish_misc.ish_vsize,
+ opt_hdr.ioh_filealign));
+ bcopy((uint8_t *)(*imgbase) + oldraddr,
+ newimg + sect_hdr->ish_rawdataaddr, oldrlen);
+ sect_hdr++;
+ }
+
+ free(*imgbase);
+
+ *imgbase = newimg;
+ *imglen += offaccum;
+
+ return(0);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "Usage: %s [-O] [-i <inffile>] -s <sysfile> "
+ "[-n devname] [-o outfile]\n", __progname);
+ fprintf(stderr, " %s -f <firmfile>\n", __progname);
+
+ exit(1);
+}
+
+static void
+bincvt(char *sysfile, char *outfile, void *img, int fsize)
+{
+ char *ptr;
+ char tname[] = "/tmp/ndiscvt.XXXXXX";
+ char sysbuf[1024];
+ FILE *binfp;
+
+ mkstemp(tname);
+
+ binfp = fopen(tname, "a+");
+ if (binfp == NULL)
+ err(1, "opening %s failed", tname);
+
+ if (fwrite(img, fsize, 1, binfp) != 1)
+ err(1, "failed to output binary image");
+
+ fclose(binfp);
+
+ outfile = strdup(basename(outfile));
+ if (strchr(outfile, '.'))
+ *strchr(outfile, '.') = '\0';
+
+ snprintf(sysbuf, sizeof(sysbuf),
+#ifdef __i386__
+ "objcopy -I binary -O elf32-i386-freebsd -B i386 %s %s.o\n",
+#endif
+#ifdef __amd64__
+ "objcopy -I binary -O elf64-x86-64 -B i386 %s %s.o\n",
+#endif
+ tname, outfile);
+ printf("%s", sysbuf);
+ system(sysbuf);
+ unlink(tname);
+
+ ptr = tname;
+ while (*ptr) {
+ if (*ptr == '/' || *ptr == '.')
+ *ptr = '_';
+ ptr++;
+ }
+
+ snprintf(sysbuf, sizeof(sysbuf),
+ "objcopy --redefine-sym _binary_%s_start=ndis_%s_drv_data_start "
+ "--strip-symbol _binary_%s_size "
+ "--redefine-sym _binary_%s_end=ndis_%s_drv_data_end %s.o %s.o\n",
+ tname, sysfile, tname, tname, sysfile, outfile, outfile);
+ printf("%s", sysbuf);
+ system(sysbuf);
+
+ return;
+}
+
+static void
+firmcvt(char *firmfile)
+{
+ char *basefile, *outfile, *ptr;
+ char sysbuf[1024];
+
+ outfile = strdup(basename(firmfile));
+ basefile = strdup(outfile);
+
+ snprintf(sysbuf, sizeof(sysbuf),
+#ifdef __i386__
+ "objcopy -I binary -O elf32-i386-freebsd -B i386 %s %s.o\n",
+#endif
+#ifdef __amd64__
+ "objcopy -I binary -O elf64-x86-64 -B i386 %s %s.o\n",
+#endif
+ firmfile, outfile);
+ printf("%s", sysbuf);
+ system(sysbuf);
+
+ ptr = firmfile;
+ while (*ptr) {
+ if (*ptr == '/' || *ptr == '.')
+ *ptr = '_';
+ ptr++;
+ }
+ ptr = basefile;
+ while (*ptr) {
+ if (*ptr == '/' || *ptr == '.')
+ *ptr = '_';
+ else
+ *ptr = tolower(*ptr);
+ ptr++;
+ }
+
+ snprintf(sysbuf, sizeof(sysbuf),
+ "objcopy --redefine-sym _binary_%s_start=%s_start "
+ "--strip-symbol _binary_%s_size "
+ "--redefine-sym _binary_%s_end=%s_end %s.o %s.o\n",
+ firmfile, basefile, firmfile, firmfile,
+ basefile, outfile, outfile);
+ ptr = sysbuf;
+ printf("%s", sysbuf);
+ system(sysbuf);
+
+ snprintf(sysbuf, sizeof(sysbuf),
+ "ld -Bshareable -d -warn-common -o %s.ko %s.o\n",
+ outfile, outfile);
+ printf("%s", sysbuf);
+ system(sysbuf);
+
+ free(basefile);
+
+ exit(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ FILE *fp, *outfp;
+ int i, bin = 0;
+ void *img;
+ int n, fsize, cnt;
+ unsigned char *ptr;
+ char *inffile = NULL, *sysfile = NULL;
+ char *outfile = NULL, *firmfile = NULL;
+ char *dname = NULL;
+ int ch;
+
+ while((ch = getopt(argc, argv, "i:s:o:n:f:O")) != -1) {
+ switch(ch) {
+ case 'f':
+ firmfile = optarg;
+ break;
+ case 'i':
+ inffile = optarg;
+ break;
+ case 's':
+ sysfile = optarg;
+ break;
+ case 'o':
+ outfile = optarg;
+ break;
+ case 'n':
+ dname = optarg;
+ break;
+ case 'O':
+ bin = 1;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if (firmfile != NULL)
+ firmcvt(firmfile);
+
+ if (sysfile == NULL)
+ usage();
+
+ /* Open the .SYS file and load it into memory */
+ fp = fopen(sysfile, "r");
+ if (fp == NULL)
+ err(1, "opening .SYS file '%s' failed", sysfile);
+ fseek (fp, 0L, SEEK_END);
+ fsize = ftell (fp);
+ rewind (fp);
+ img = calloc(fsize, 1);
+ n = fread (img, fsize, 1, fp);
+
+ fclose(fp);
+
+ if (insert_padding(&img, &fsize)) {
+ fprintf(stderr, "section relocation failed\n");
+ exit(1);
+ }
+
+ if (outfile == NULL || strcmp(outfile, "-") == 0)
+ outfp = stdout;
+ else {
+ outfp = fopen(outfile, "w");
+ if (outfp == NULL)
+ err(1, "opening output file '%s' failed", outfile);
+ }
+
+ fprintf(outfp, "\n/*\n");
+ fprintf(outfp, " * Generated from %s and %s (%d bytes)\n",
+ inffile == NULL ? "<notused>" : inffile, sysfile, fsize);
+ fprintf(outfp, " */\n\n");
+
+ if (dname != NULL) {
+ if (strlen(dname) > IFNAMSIZ)
+ err(1, "selected device name '%s' is "
+ "too long (max chars: %d)", dname, IFNAMSIZ);
+ fprintf (outfp, "#define NDIS_DEVNAME \"%s\"\n", dname);
+ fprintf (outfp, "#define NDIS_MODNAME %s\n\n", dname);
+ }
+
+ if (inffile == NULL) {
+ fprintf (outfp, "#ifdef NDIS_REGVALS\n");
+ fprintf (outfp, "ndis_cfg ndis_regvals[] = {\n");
+ fprintf (outfp, "\t{ NULL, NULL, { 0 }, 0 }\n");
+ fprintf (outfp, "#endif /* NDIS_REGVALS */\n");
+
+ fprintf (outfp, "};\n\n");
+ } else {
+ fp = fopen(inffile, "r");
+ if (fp == NULL)
+ err(1, "opening .INF file '%s' failed", inffile);
+
+
+ inf_parse(fp, outfp);
+ fclose(fp);
+ }
+
+ fprintf(outfp, "\n#ifdef NDIS_IMAGE\n");
+
+ if (bin) {
+ sysfile = strdup(basename(sysfile));
+ ptr = (unsigned char *)sysfile;
+ while (*ptr) {
+ if (*ptr == '.')
+ *ptr = '_';
+ ptr++;
+ }
+ fprintf(outfp,
+ "\nextern unsigned char ndis_%s_drv_data_start[];\n",
+ sysfile);
+ fprintf(outfp, "static unsigned char *drv_data = "
+ "ndis_%s_drv_data_start;\n\n", sysfile);
+ bincvt(sysfile, outfile, img, fsize);
+ goto done;
+ }
+
+
+ fprintf(outfp, "\nextern unsigned char drv_data[];\n\n");
+
+ fprintf(outfp, "__asm__(\".data\");\n");
+ fprintf(outfp, "__asm__(\".globl drv_data\");\n");
+ fprintf(outfp, "__asm__(\".type drv_data, @object\");\n");
+ fprintf(outfp, "__asm__(\".size drv_data, %d\");\n", fsize);
+ fprintf(outfp, "__asm__(\"drv_data:\");\n");
+
+ ptr = img;
+ cnt = 0;
+ while(cnt < fsize) {
+ fprintf (outfp, "__asm__(\".byte ");
+ for (i = 0; i < 10; i++) {
+ cnt++;
+ if (cnt == fsize) {
+ fprintf(outfp, "0x%.2X\");\n", ptr[i]);
+ goto done;
+ } else {
+ if (i == 9)
+ fprintf(outfp, "0x%.2X\");\n", ptr[i]);
+ else
+ fprintf(outfp, "0x%.2X, ", ptr[i]);
+ }
+ }
+ ptr += 10;
+ }
+
+done:
+
+ fprintf(outfp, "#endif /* NDIS_IMAGE */\n");
+
+ if (fp != NULL)
+ fclose(fp);
+ fclose(outfp);
+ free(img);
+ exit(0);
+}
diff --git a/usr.sbin/ndiscvt/ndisgen.8 b/usr.sbin/ndiscvt/ndisgen.8
new file mode 100644
index 0000000..c4ad495
--- /dev/null
+++ b/usr.sbin/ndiscvt/ndisgen.8
@@ -0,0 +1,86 @@
+.\" Copyright (c) 2005
+.\" Bill Paul <wpaul@windriver.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 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 24, 2005
+.Dt NDISGEN 8
+.Os
+.Sh NAME
+.Nm ndisgen
+.Nd generate a FreeBSD driver module from a
+.Tn Windows\[rg]
+NDIS driver distribution
+.Sh SYNOPSIS
+.Nm
+.Op Ar /path/to/INF /path/to/SYS
+.Sh DESCRIPTION
+The
+.Nm
+script uses the
+.Xr ndiscvt 8
+utility and other tools to generate a
+.Fx
+loadable driver module
+and a static ELF object module from a
+.Tn Windows\[rg]
+NDIS driver, for use with the
+.Xr ndis 4
+compatibility module.
+.Pp
+The
+.Nm
+script is interactive and contains its own help section.
+Unless the paths to both files are supplied on the command line,
+the script will prompt the user for the
+.Pa .INF
+and
+.Pa .SYS
+files needed to generate the
+.Fx
+driver module.
+The script will also prompt for
+any firmware or other external files needed.
+.Sh SEE ALSO
+.Xr ld 1 ,
+.Xr objcopy 1 ,
+.Xr ndis 4 ,
+.Xr kldload 8 ,
+.Xr ndiscvt 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 6.0 .
+.Sh AUTHORS
+The
+.Nm
+utility was written by
+.An Bill Paul Aq wpaul@windriver.com .
diff --git a/usr.sbin/ndiscvt/ndisgen.sh b/usr.sbin/ndiscvt/ndisgen.sh
new file mode 100644
index 0000000..e8c8ce3
--- /dev/null
+++ b/usr.sbin/ndiscvt/ndisgen.sh
@@ -0,0 +1,559 @@
+#!/bin/sh
+#
+# Copyright (c) 2005
+# Bill Paul <wpaul@windriver.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 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.
+#
+# $FreeBSD$
+#
+
+header () {
+clear
+echo " =================================================================="
+echo " ------------------ Windows(r) driver converter -------------------"
+echo " =================================================================="
+echo ""
+}
+
+mainmenu() {
+header
+echo " This script is designed to guide you through the process"
+echo " of converting a Windows(r) binary driver module and .INF"
+echo " specification file into a FreeBSD ELF kernel module for use"
+echo " with the NDIS compatibility system."
+echo ""
+echo " The following options are available:"
+echo ""
+echo " 1] Learn about the NDIS compatibility system"
+echo " 2] Convert individual firmware files"
+echo " 3] Convert driver"
+echo " 4] Exit"
+echo ""
+echo -n " Enter your selection here and press return: "
+read KEYPRESS
+return
+}
+
+
+help1 () {
+header
+echo " General information"
+echo ""
+echo " The NDIS compatibility system is designed to let you use Windows(r)"
+echo " binary drivers for networking devices with FreeBSD, in cases where"
+echo " a native FreeBSD driver is not available due to hardware manufacturer"
+echo " oversight or stupidity. NDIS stands for Network Driver Interface"
+echo " Standard, and refers to the programming model used to write Windows(r)"
+echo " network drivers. (These are often called \"NDIS miniport\" drivers.)"
+echo ""
+echo " In order to use your network device in NDIS compatibility mode,"
+echo " you need the Windows(r) driver that goes with it. Also, the driver"
+echo " must be compiled for the same architecture as the release of FreeBSD"
+echo " you have installed. At this time, the i386 and amd64 architectures"
+echo " are both supported. Note that you cannot use a Windows/i386 driver"
+echo " with FreeBSD/amd64: you must obtain a Windows/amd64 driver."
+echo ""
+echo -n " Press return to continue... "
+read KEYPRESS
+return
+}
+
+help2() {
+header
+echo " Where to get drivers"
+echo ""
+echo " If you purchased your network card separately from your computer,"
+echo " there should have been a driver distribution CD included with the"
+echo " card which contains Windows(r) drivers. The NDIS compatibility"
+echo " system is designed to emulate the NDIS API of a couple of different"
+echo " Windows(r) releases, however it works best with drivers designed"
+echo " for NDIS 5.0 or later. Drivers distributed for Windows 2000 should"
+echo " work; however, for best results you should use a driver designed"
+echo " for Windows XP or Windows Server 2003."
+echo ""
+echo " If your card was supplied with your computer, or is a built-in device,"
+echo " drivers may have been included on a special driver bundle CD shipped"
+echo " with the computer."
+echo ""
+echo " If you don't have a driver CD, you should be able to find a driver"
+echo " kit on the card or computer vendor's web site."
+echo ""
+echo -n " Press return to continue... "
+read KEYPRESS
+return
+}
+
+help3 () {
+header
+echo " What files do I need?"
+echo ""
+echo " In most cases, you will need only two files: a .INF file and a .SYS"
+echo " file. The .INF file is a text file used by the Windows(r) installer to"
+echo " perform the driver installation. It contains information that tells"
+echo " the installer what devices the driver supports and what registry keys"
+echo " should be created to control driver configuration. The .SYS file"
+echo " is the actual driver executable code in Windows(r) Portable Executable"
+echo " (PE) format. Note that sometimes the .INF file is supplied in Unicode"
+echo " format. Unicode .INF files must be converted to ASCII form with the"
+echo " iconv(1) utility before this installer script can use them."
+echo " Occasionally, a driver may require firmware or register setup"
+echo " files that are external to the main .SYS file. These are provided"
+echo " on the same CD with the driver itself, and sometimes have a .BIN"
+echo " extension, though they can be named almost anything. You will need"
+echo " these additional files to make your device work with the NDIS"
+echo " compatibility system as well."
+echo ""
+echo -n " Press return to continue... "
+read KEYPRESS
+return
+}
+
+help4 () {
+header
+echo " How does it all work?"
+echo ""
+echo " The installer script uses the ndiscvt(1) utility to convert the .INF,"
+echo " .SYS and optional firmware files into a FreeBSD kernel loadable module"
+echo " (.ko) file. This module can be loaded via the kldload(8) utility or"
+echo " loaded automatically via the /boot/loader.conf file. The ndiscvt(1)"
+echo " utility extracts the device ID information and registry key data"
+echo " from the .INF file and converts it into a C header file. It also uses"
+echo " the objcopy(1) utility to convert the .SYS file and optional firmware"
+echo " files into ELF objects. The header file is compiled into a small C"
+echo " stub file which contains a small amount of code to interface with"
+echo " the FreeBSD module system. This stub is linked together with the"
+echo " converted ELF objects to form a FreeBSD kernel module. A static ELF"
+echo " object (.o) file is also created. This file can be linked into a"
+echo " static kernel image for those who want/need a fully linked kernel"
+echo " image (possibly for embedded bootstrap purposes, or just plain old"
+echo " experimentation)."
+echo ""
+echo -n " Press return to continue... "
+read KEYPRESS
+return
+}
+
+help5 () {
+header
+echo " Prerequisites"
+echo ""
+echo " Converting a driver requires the following utilities:"
+echo ""
+echo " - The FreeBSD C compiler, cc(1) (part of the base install)."
+echo " - The FreeBSD linker, ld(1) (part of the base install)."
+echo " - The objcopy(1) utility (part of the base install)."
+echo " - The ndiscvt(1) utility (part of the base install)."
+echo ""
+echo " If you happen to end up with a .INF file that's in Unicode format,"
+echo " then you'll also need:"
+echo ""
+echo " - The iconv(1) utility."
+echo ""
+echo " If you have installed the X Window system or some sort of desktop"
+echo " environment, then iconv(1) should already be present. If not, you"
+echo " will need to install the libiconv package or port."
+echo ""
+echo -n " Press return to continue... "
+read KEYPRESS
+return
+}
+
+infconv () {
+header
+echo " INF file validation"
+
+if [ -z "$INFPATH" ]; then
+ echo ""
+ echo ""
+ echo " A .INF file is most often provided as an ASCII file, however"
+ echo " files with multilanguage support are provided in Unicode format."
+ echo " Please type in the path to your .INF file now."
+ echo ""
+ echo -n " > "
+ read INFPATH
+fi
+
+if [ ${INFPATH} ] && [ -e ${INFPATH} ]; then
+ INFTYPE=`${EGREP} -i -c "Signature|.S.i.g.n.a.t.u.r.e" ${INFPATH}`
+ if [ ${INFTYPE} -le 0 ]; then
+ echo ""
+ echo " I don't recognize this file format. It may not be a valid .INF file."
+ echo ""
+ echo -n " Press enter to try again, or ^C to quit. "
+ read KEYPRESS
+ INFPATH=""
+ return
+ fi
+
+ INFTYPE=`${EGREP} -i -c "Class.*=.*Net" ${INFPATH}`
+ if [ ${INFTYPE} -gt 0 ]; then
+ echo ""
+ echo " This .INF file appears to be ASCII."
+ echo ""
+ echo -n " Press return to continue... "
+ read KEYPRESS
+ return
+ fi
+
+ INFTYPE=`${EGREP} -i -c ".C.l.a.s.s.*=.*N.e.t" ${INFPATH}`
+ if [ ${INFTYPE} -gt 0 ]; then
+ echo ""
+ echo " This .INF file appears to be Unicode."
+ if [ -e ${ICONVPATH} ]; then
+ echo " Trying to convert to ASCII..."
+ ${ICONVPATH} -f utf-16 -t utf-8 ${INFPATH} > ${INFFILE}
+ INFPATH=${INFFILE}
+ echo " Done."
+ echo ""
+ echo -n " Press return to continue... "
+ read KEYPRESS
+ else
+ echo " The iconv(1) utility does not appear to be installed."
+ echo " Please install this utility or convert the .INF file"
+ echo " to ASCII and run this utility again."
+ echo ""
+ exit
+ fi
+ return
+ fi
+
+ echo ""
+ echo " I don't recognize this file format. It may not be a valid .INF file."
+ echo ""
+ echo -n " Press enter to try again, or ^C to quit. "
+ read KEYPRESS
+ INFPATH=""
+else
+ echo ""
+ echo " The file '${INFPATH}' was not found."
+ echo ""
+ echo -n " Press enter to try again, or ^C to quit. "
+ read KEYPRESS
+ INFPATH=""
+fi
+return
+}
+
+sysconv() {
+header
+echo " Driver file validation"
+
+if [ ! -r "$SYSPATH" ]; then
+ echo ""
+ echo ""
+ echo " Now you need to specify the name of the Windows(r) driver .SYS"
+ echo " file for your device. Note that if you are running FreeBSD/amd64,"
+ echo " then you must provide a driver that has been compiled for the"
+ echo " 64-bit Windows(r) platform. If a 64-bit driver is not available"
+ echo " for your device, you must install FreeBSD/i386 and use the"
+ echo " 32-bit driver instead."
+ echo ""
+ echo " Please type in the path to the Windows(r) driver .SYS file now."
+ echo ""
+ echo -n " > "
+ read SYSPATH
+fi
+
+if [ ${SYSPATH} ] && [ -e ${SYSPATH} ]; then
+ SYSTYPE=`${FILE} ${SYSPATH}`
+
+ case ${SYSTYPE} in
+ *Windows*)
+ echo ""
+ echo " This .SYS file appears to be in Windows(r) PE format."
+ echo ""
+ echo -n " Press return to continue... "
+ read KEYPRESS
+ SYSBASE=`${BASENAME} ${SYSPATH} | ${TR} '.' '_'`
+ ;;
+ *)
+ echo ""
+ echo " I don't recognize this file format. It may not be a valid .SYS file."
+ echo ""
+
+ echo -n " Press enter to try again, or ^C to quit. "
+ read KEYPRESS
+ SYSPATH=""
+ ;;
+ esac
+else
+ echo ""
+ echo " The file '${SYSPATH}' was not found."
+ echo ""
+ echo -n " Press enter to try again, or ^C to quit. "
+ read KEYPRESS
+ SYSPATH=""
+fi
+return
+}
+
+ndiscvt() {
+header
+echo " Driver file conversion"
+echo ""
+echo " The script will now try to convert the .INF and .SYS files"
+echo " using the ndiscvt(1) utility. This utility can handle most"
+echo " .INF files; however, occasionally it can fail to parse some files"
+echo " due to subtle syntax issues: the .INF syntax is very complex,"
+echo " and the Windows(r) parser will sometimes allow files with small"
+echo " syntax errors to be processed correctly which ndiscvt(1) will"
+echo " not. If the conversion fails, you may have to edit the .INF"
+echo " file by hand to remove the offending lines."
+echo ""
+echo -n " Press enter to try converting the files now: "
+read KEYPRESS
+if ! ${NDISCVT} -i ${INFPATH} -s ${SYSPATH} -O -o ${DNAME}.h > /dev/null; then
+ echo "CONVERSION FAILED"
+ exit
+else
+ echo ""
+ echo " Conversion was successful."
+ echo ""
+ echo -n " Press enter to continue... "
+ read KEYPRESS
+fi
+return
+}
+
+firmcvt() {
+ while : ; do
+header
+echo " Firmware file conversion"
+echo ""
+echo " If your driver uses additional firmware files, please list them"
+echo " below. When you're finished, just press enter to continue. (If your"
+echo " driver doesn't need any extra firmware files, just press enter"
+echo " to move to the next step.)"
+echo ""
+ echo -n " > "
+ read FIRMPATH
+
+ if [ ${FIRMPATH} ]; then
+ if [ ! -e ${FIRMPATH} ]; then
+ echo ""
+ echo " The file '${FIRMPATH}' was not found"
+ echo ""
+ echo -n " Press enter to try again, or ^C to quit. "
+ read KEYPRESS
+ continue
+ fi
+ if ! ${NDISCVT} -f ${FIRMPATH} > /dev/null; then
+ echo ""
+ echo "CONVERSION FAILED"
+ else
+ echo ""
+ echo " Conversion was successful."
+ echo ""
+ FRMBASE=`${BASENAME} ${FIRMPATH}`
+ FRMBASE="${FRMBASE}.o"
+ FRMLIST="${FRMLIST} ${FRMBASE}"
+ fi
+ echo -n " Press enter to continue... "
+ read KEYPRESS
+ else
+ break
+ fi
+ done
+
+header
+echo ""
+echo " List of files converted firmware files:"
+echo ""
+for i in ${FRMLIST}
+do
+ echo " "$i
+done
+echo ""
+echo -n " Press enter to continue... "
+read KEYPRESS
+return
+}
+
+drvgen () {
+header
+echo " Kernel module generation"
+echo ""
+echo ""
+echo " The script will now try to generate the kernel driver module."
+echo " This is the last step. Once this module is generated, you should"
+echo " be able to load it just like any other FreeBSD driver module."
+echo ""
+echo " Press enter to compile the stub module and generate the driver"
+echo -n " module now: "
+read KEYPRESS
+echo ""
+echo -n " Generating Makefile... "
+echo ".PATH: ${PWD} ${STUBPATH}" > ${MAKEFILE}
+echo "KMOD= ${SYSBASE}" >> ${MAKEFILE}
+echo "SRCS+= ${STUBFILE} ${DNAME}.h bus_if.h device_if.h" >> ${MAKEFILE}
+echo "OBJS+=${FRMLIST} ${DNAME}.o" >> ${MAKEFILE}
+echo "CFLAGS+= \\" >> ${MAKEFILE}
+echo " -DDRV_DATA_START=ndis_${SYSBASE}_drv_data_start \\" >> ${MAKEFILE}
+echo " -DDRV_NAME=ndis_${SYSBASE} \\" >> ${MAKEFILE}
+echo " -DDRV_DATA_END=ndis_${SYSBASE}_drv_data_end" >> ${MAKEFILE}
+echo "CLEANFILES+= \\" >> ${MAKEFILE}
+echo " ${INFFILE} \\" >> ${MAKEFILE}
+echo " ${DNAME}.h \\" >> ${MAKEFILE}
+echo " ${DNAME}.o" >> ${MAKEFILE}
+echo ".include <bsd.kmod.mk>" >> ${MAKEFILE}
+if [ -f ${MAKEFILE} ]; then
+ echo "done."
+else
+ echo "generating Makefile failed. Exiting."
+ echo ""
+ exit
+fi
+echo -n " Building kernel module... "
+echo "" > bus_if.h
+echo "" > device_if.h
+if ! ${MAKE} -f ${MAKEFILE} depend > /dev/null; then
+ echo "build failed. Exiting."
+ echo ""
+ exit
+fi
+if ! ${MAKE} -f ${MAKEFILE} all > /dev/null; then
+ echo "build failed. Exiting."
+ echo ""
+ exit
+else
+ if [ -f ${SYSBASE}.ko ]; then
+ ${MV} ${SYSBASE}.ko ${SYSBASE}.kmod
+ echo "done."
+ else
+ echo "build failed. Exiting."
+ echo ""
+ exit
+ fi
+fi
+echo -n " Cleaning up... "
+if ! ${MAKE} -f ${MAKEFILE} clean cleandepend > /dev/null; then
+ echo "cleanup failed. Exiting."
+ echo ""
+ exit
+else
+ echo "done."
+fi
+${RM} ${MAKEFILE}
+${MV} ${SYSBASE}.kmod ${SYSBASE}.ko
+echo ""
+echo " The file ${SYSBASE}.ko has been successfully generated."
+echo " You can kldload this module to get started."
+echo ""
+echo -n " Press return to exit. "
+read KEYPRESS
+echo ""
+echo ""
+return
+}
+
+convert_driver () {
+ while : ; do
+ infconv
+ if [ ${INFPATH} ]; then
+ break
+ fi
+ done
+
+ while : ; do
+ sysconv
+ if [ ${SYSPATH} ]; then
+ break
+ fi
+ done
+
+ ndiscvt
+ firmcvt
+ drvgen
+ return
+}
+
+ICONVPATH=/usr/local/bin/iconv
+NDISCVT=/usr/sbin/ndiscvt
+STUBPATH=/usr/share/misc
+STUBFILE=windrv_stub.c
+DNAME=windrv
+CP=/bin/cp
+MV=/bin/mv
+RM=/bin/rm
+TR=/usr/bin/tr
+FILE=/usr/bin/file
+EGREP=/usr/bin/egrep
+MAKE=/usr/bin/make
+BASENAME=/usr/bin/basename
+TOUCH=/usr/bin/touch
+MKTEMP=/usr/bin/mktemp
+
+MAKEFILE=`${MKTEMP} /tmp/Makefile.XXXXXX`
+INFFILE=`${MKTEMP} /tmp/ascii_inf.XXXXXX`
+
+INFPATH=""
+FRMLIST=""
+SYSPATH=""
+SYSBASE=""
+FRMBASE=""
+
+if [ -r "$1" -a -r "$2" ]; then
+ # Looks like the user supplied .INF and .SYS files on the command line
+ INFPATH=$1
+ SYSPATH=$2
+ convert_driver && exit 0
+fi
+
+while : ; do
+ mainmenu
+ case ${KEYPRESS} in
+ 1)
+ help1
+ help2
+ help3
+ help4
+ help5
+ ;;
+ 2)
+ firmcvt
+ ;;
+ 3)
+ convert_driver
+ ;;
+ 4)
+ header
+ echo ""
+ echo " Be seeing you!"
+ echo ""
+ exit
+ ;;
+ *)
+ header
+ echo ""
+ echo -n " Sorry, I didn't understand that. Press enter to try again: "
+ read KEYPRESS
+ ;;
+ esac
+done
+exit
diff --git a/usr.sbin/ndiscvt/windrv_stub.c b/usr.sbin/ndiscvt/windrv_stub.c
new file mode 100644
index 0000000..46a0e83
--- /dev/null
+++ b/usr.sbin/ndiscvt/windrv_stub.c
@@ -0,0 +1,266 @@
+/*-
+ * Copyright (c) 2005
+ * Bill Paul <wpaul@windriver.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 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/conf.h>
+
+#include <machine/bus.h>
+#include <machine/resource.h>
+#include <sys/bus.h>
+
+#define NDIS_REGVALS
+
+struct ndis_cfg {
+ char *nc_cfgkey;
+ char *nc_cfgdesc;
+ char nc_val[256];
+ int nc_idx;
+};
+
+typedef struct ndis_cfg ndis_cfg;
+
+#include "windrv.h"
+
+struct ndis_pci_type {
+ uint16_t ndis_vid;
+ uint16_t ndis_did;
+ uint32_t ndis_subsys;
+ char *ndis_name;
+};
+
+struct ndis_pccard_type {
+ const char *ndis_vid;
+ const char *ndis_did;
+ char *ndis_name;
+};
+
+struct ndis_usb_type {
+ uint16_t ndis_vid;
+ uint16_t ndis_did;
+ char *ndis_name;
+};
+
+#ifdef NDIS_PCI_DEV_TABLE
+static struct ndis_pci_type ndis_devs_pci[] = {
+ NDIS_PCI_DEV_TABLE
+ { 0, 0, 0, NULL }
+};
+#endif
+
+#ifdef NDIS_PCMCIA_DEV_TABLE
+static struct ndis_pccard_type ndis_devs_pccard[] = {
+ NDIS_PCMCIA_DEV_TABLE
+ { NULL, NULL, NULL }
+};
+#endif
+
+#ifdef NDIS_USB_DEV_TABLE
+static struct ndis_usb_type ndis_devs_usb[] = {
+ NDIS_USB_DEV_TABLE
+ { 0, 0, NULL }
+};
+#endif
+
+enum interface_type {
+ InterfaceTypeUndefined = -1,
+ Internal,
+ Isa,
+ Eisa,
+ MicroChannel,
+ TurboChannel,
+ PCIBus,
+ VMEBus,
+ NuBus,
+ PCMCIABus,
+ CBus,
+ MPIBus,
+ MPSABus,
+ ProcessorInternal,
+ InternalPowerBus,
+ PNPISABus,
+ PNPBus,
+ MaximumInterfaceType
+};
+
+typedef enum interface_type interface_type;
+
+/*
+ * XXX
+ * Ordinarily, device_probe_desc is defined in device_if.h, which
+ * is created from device_if.m. The problem is, the latter file
+ * is only available if you have the kernel source code installed,
+ * and not all users choose to install it. I'd like to let people
+ * load Windows driver modules with the minimal amount of hassle
+ * and dependencies. <sys/bus.h> wants both device_if.h and bus_if.h
+ * to be defined, but it turns out the only thing we really need
+ * to get this module compiled is device_probe_desc, so we define
+ * that here, and let the build script create empty copies of
+ * device_if.h and bus_if.h to make the compiler happy.
+ */
+
+extern struct kobjop_desc device_probe_desc;
+typedef int device_probe_t(device_t dev);
+
+extern int windrv_load(module_t, vm_offset_t, size_t,
+ interface_type, void *, void *);
+extern int windrv_unload(module_t, vm_offset_t, size_t);
+
+#ifndef DRV_DATA_START
+#define DRV_DATA_START UNDEF_START
+#endif
+
+#ifndef DRV_DATA_END
+#define DRV_DATA_END UNDEF_END
+#endif
+
+#ifndef DRV_NAME
+#define DRV_NAME UNDEF_NAME
+#endif
+
+extern uint8_t DRV_DATA_START;
+extern uint8_t DRV_DATA_END;
+
+/*
+ * The following is stub code that makes it look as though we want
+ * to be a child device of all the buses that our supported devices
+ * might want to attach to. Our probe routine always fails. The
+ * reason we need this code is so that loading an ELF-ified Windows
+ * driver module will trigger a bus reprobe.
+ */
+
+#define MODULE_DECL(x) \
+ MODULE_DEPEND(x, ndisapi, 1, 1, 1); \
+ MODULE_DEPEND(x, ndis, 1, 1, 1)
+
+MODULE_DECL(DRV_NAME);
+
+static int windrv_probe(device_t);
+static int windrv_modevent(module_t, int, void *);
+static int windrv_loaded = 0;
+
+static device_method_t windrv_methods[] = {
+ /* Device interface */
+ DEVMETHOD(device_probe, windrv_probe),
+
+ { 0, 0 }
+};
+
+static driver_t windrv_driver = {
+ "windrv_stub",
+ windrv_methods,
+ 0
+};
+
+static devclass_t windrv_devclass;
+
+#define DRIVER_DECL(x) \
+ DRIVER_MODULE(x, pci, windrv_driver, \
+ windrv_devclass, windrv_modevent, NULL); \
+ DRIVER_MODULE(x, cardbus, windrv_driver, \
+ windrv_devclass, windrv_modevent, NULL); \
+ DRIVER_MODULE(x, pccard, windrv_driver, \
+ windrv_devclass, windrv_modevent, NULL); \
+ DRIVER_MODULE(x, uhub, windrv_driver, \
+ windrv_devclass, windrv_modevent, NULL); \
+ MODULE_VERSION(x, 1)
+
+DRIVER_DECL(DRV_NAME);
+
+static int
+windrv_probe(dev)
+ device_t dev;
+{
+ return (ENXIO);
+}
+
+static int
+windrv_modevent(mod, cmd, arg)
+ module_t mod;
+ int cmd;
+ void *arg;
+{
+ int drv_data_len;
+ int error = 0;
+ vm_offset_t drv_data_start;
+ vm_offset_t drv_data_end;
+
+ drv_data_start = (vm_offset_t)&DRV_DATA_START;
+ drv_data_end = (vm_offset_t)&DRV_DATA_END;
+
+ drv_data_len = drv_data_end - drv_data_start;
+ switch (cmd) {
+ case MOD_LOAD:
+ windrv_loaded++;
+ if (windrv_loaded > 1)
+ break;
+#ifdef NDIS_PCI_DEV_TABLE
+ windrv_load(mod, drv_data_start, drv_data_len, PCIBus,
+ ndis_devs_pci, &ndis_regvals);
+#endif
+#ifdef NDIS_PCMCIA_DEV_TABLE
+ windrv_load(mod, drv_data_start, drv_data_len, PCMCIABus,
+ ndis_devs_pccard, &ndis_regvals);
+#endif
+#ifdef NDIS_USB_DEV_TABLE
+ windrv_load(mod, drv_data_start, drv_data_len, PNPBus,
+ ndis_devs_usb, &ndis_regvals);
+#endif
+ break;
+ case MOD_UNLOAD:
+ windrv_loaded--;
+ if (windrv_loaded > 0)
+ break;
+#ifdef NDIS_PCI_DEV_TABLE
+ windrv_unload(mod, drv_data_start, drv_data_len);
+#endif
+#ifdef NDIS_PCMCIA_DEV_TABLE
+ windrv_unload(mod, drv_data_start, drv_data_len);
+#endif
+#ifdef NDIS_USB_DEV_TABLE
+ windrv_unload(mod, drv_data_start, drv_data_len);
+#endif
+ break;
+ case MOD_SHUTDOWN:
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+
+ return (error);
+}
diff --git a/usr.sbin/ndp/Makefile b/usr.sbin/ndp/Makefile
new file mode 100644
index 0000000..f156691
--- /dev/null
+++ b/usr.sbin/ndp/Makefile
@@ -0,0 +1,25 @@
+# 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$
+
+.PATH: ${.CURDIR}/../../contrib/tcpdump
+
+PROG= ndp
+MAN= ndp.8
+SRCS= ndp.c gmt2local.c
+
+CFLAGS+= -I. -I${.CURDIR} -I${.CURDIR}/../../contrib/tcpdump
+CFLAGS+= -D_U_=""
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ndp/gnuc.h b/usr.sbin/ndp/gnuc.h
new file mode 100644
index 0000000..c641296
--- /dev/null
+++ b/usr.sbin/ndp/gnuc.h
@@ -0,0 +1,2 @@
+/* $FreeBSD$ */
+/* this is dummy to pacify gmt2local.c. */
diff --git a/usr.sbin/ndp/ndp.8 b/usr.sbin/ndp/ndp.8
new file mode 100644
index 0000000..70f5156
--- /dev/null
+++ b/usr.sbin/ndp/ndp.8
@@ -0,0 +1,258 @@
+.\" $KAME: ndp.8,v 1.28 2002/07/17 08:46:33 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 NDP 8
+.Os
+.\"
+.Sh NAME
+.Nm ndp
+.Nd control/diagnose IPv6 neighbor discovery protocol
+.\"
+.Sh SYNOPSIS
+.Nm
+.Op Fl nt
+.Ar hostname
+.Nm
+.Op Fl nt
+.Fl a | c | p
+.Nm
+.Op Fl nt
+.Fl r
+.Nm
+.Op Fl nt
+.Fl H | P | R
+.Nm
+.Op Fl nt
+.Fl A Ar wait
+.Nm
+.Op Fl nt
+.Fl d Ar hostname
+.Nm
+.Op Fl nt
+.Fl f Ar filename
+.Nm
+.Op Fl nt
+.Fl i
+.Ar interface
+.Op Ar expressions ...
+.Nm
+.Op Fl nt
+.Fl I Op Ar interface | Li delete
+.Nm
+.Op Fl nt
+.Fl s Ar nodename etheraddr
+.Op Li temp
+.Op Li proxy
+.\"
+.Sh DESCRIPTION
+The
+.Nm
+utility manipulates the address mapping table
+used by the Neighbor Discovery Protocol (NDP).
+.Bl -tag -width indent
+.It Fl a
+Dump the currently existing NDP entries.
+The following information will be printed:
+.Bl -tag -width "Neighbor"
+.It Neighbor
+IPv6 address of the neighbor.
+.It Linklayer Address
+Linklayer address of the neighbor.
+It could be
+.Dq Li (incomplete)
+when the address is not available.
+.It Netif
+Network interface associated with the neighbor cache entry.
+.It Expire
+The time until expiry of the entry.
+The entry could become
+.Dq Li permanent ,
+in which case it will never expire.
+.It S
+State of the neighbor cache entry, as a single letter:
+.Pp
+.Bl -tag -width indent -compact
+.It N
+Nostate
+.It W
+Waitdelete
+.It I
+Incomplete
+.It R
+Reachable
+.It S
+Stale
+.It D
+Delay
+.It P
+Probe
+.It ?\&
+Unknown state (should never happen).
+.El
+.It Flags
+Flags on the neighbor cache entry, in a single letter.
+They are: Router, proxy neighbor advertisement
+.Pq Dq p .
+The field could be followed by a decimal number,
+which means the number of NS probes the node has sent during the current state.
+.El
+.It Fl A Ar wait
+Repeat
+.Fl a
+(dump NDP entries)
+every
+.Ar wait
+seconds.
+.It Fl c
+Erase all the NDP entries.
+.It Fl d
+Delete specified NDP entry.
+.It Fl f
+Parse the file specified by
+.Ar filename .
+.It Fl H
+Harmonize consistency between the routing table and the default router
+list; install the top entry of the list into the kernel routing table.
+.It Fl I
+Shows the default interface used as the default route when
+there is no default router.
+.It Fl I Ar interface
+Specifies the default interface used as the default route when
+there is no default router.
+The
+.Ar interface
+will be used as the default.
+.It Fl I Li delete
+The current default interface will be deleted from the kernel.
+.It Fl i Ar interface Op Ar expressions ...
+View ND information for the specified interface.
+If additional arguments
+.Ar expressions
+are given,
+.Nm
+sets or clears the flags or variables for the interface as specified in
+the expression.
+Each expression should be separated by white spaces or tab characters.
+Possible expressions are as follows.
+Some of the expressions can begin with the
+special character
+.Ql - ,
+which means the flag specified in the expression should be cleared.
+Note that you need
+.Fl -
+before
+.Fl foo
+in this case.
+.\"
+.Bl -tag -width indent
+.It Ic nud
+Turn on or off NUD (Neighbor Unreachability Detection) on the
+interface.
+NUD is usually turned on by default.
+.It Ic accept_rtadv
+Specify whether or not to accept Router Advertisement messages
+received on the
+.Ar interface .
+Note that the kernel does not accept Router Advertisement messages
+unless the
+.Li net.inet6.ip6.accept_rtadv
+variable is non-0, even if the flag is on.
+This flag is set to 1 by default.
+.It Ic prefer_source
+Prefer addresses on the
+.Ar interface
+as candidates of the source address for outgoing packets.
+The default value of this flag is off.
+For more details about the entire algorithm of source address
+selection, see the
+.Pa IMPLEMENTATION
+file supplied with the KAME kit.
+.It Ic disabled
+Disable IPv6 operation on the interface.
+When disabled, the interface discards any IPv6 packets
+received on or being sent to the interface.
+In the sending case, an error of ENETDOWN will be returned to the
+application.
+This flag is typically set automatically in the kernel as a result of
+a certain failure of Duplicate Address Detection.
+While the flag can be set or cleared by hand with the
+.Nm
+command, it is not generally advisable to modify this flag manually.
+.It Ic basereachable Ns Li = Ns Pq Ar number
+Specify the BaseReachbleTimer on the interface in millisecond.
+.It Ic retrans Ns Li = Ns Pq Ar number
+Specify the RetransTimer on the interface in millisecond.
+.It Ic curhlim Ns Li = Ns Pq Ar number
+Specify the Cur Hop Limit on the interface.
+.El
+.It Fl n
+Do not try to resolve numeric addresses to hostnames.
+.It Fl p
+Show prefix list.
+.It Fl P
+Flush all the entries in the prefix list.
+.It Fl r
+Show default router list.
+.It Fl R
+Flush all the entries in the default router list.
+.It Fl s
+Register an NDP entry for a node.
+The entry will be permanent unless the word
+.Li temp
+is given in the command.
+If the word
+.Li proxy
+is given, this system will act as a proxy NDP server,
+responding to requests for
+.Ar hostname
+even though the host address is not its own.
+.It Fl t
+Print timestamp on each entry,
+making it possible to merge output with
+.Xr tcpdump 1 .
+Most useful when used with
+.Fl A .
+.El
+.\"
+.Sh EXIT STATUS
+.Ex -std
+.\"
+.Sh SEE ALSO
+.Xr arp 8
+.\"
+.Sh HISTORY
+The
+.Nm
+utility first appeared in the WIDE Hydrangea IPv6 protocol stack kit.
+.\"
+.\" .Sh BUGS
+.\" (to be written)
diff --git a/usr.sbin/ndp/ndp.c b/usr.sbin/ndp/ndp.c
new file mode 100644
index 0000000..570961a
--- /dev/null
+++ b/usr.sbin/ndp/ndp.c
@@ -0,0 +1,1631 @@
+/* $FreeBSD$ */
+/* $KAME: ndp.c,v 1.104 2003/06/27 07:48:39 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.
+ */
+/*
+ * Copyright (c) 1984, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Sun Microsystems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 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.
+ */
+
+/*
+ * Based on:
+ * "@(#) Copyright (c) 1984, 1993\n\
+ * The Regents of the University of California. All rights reserved.\n";
+ *
+ * "@(#)arp.c 8.2 (Berkeley) 1/2/94";
+ */
+
+/*
+ * ndp - display, set, delete and flush neighbor cache
+ */
+
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <netinet/icmp6.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/nd6.h>
+
+#include <arpa/inet.h>
+
+#include <netdb.h>
+#include <errno.h>
+#include <nlist.h>
+#include <stdio.h>
+#include <string.h>
+#include <paths.h>
+#include <err.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "gmt2local.h"
+
+/* packing rule for routing socket */
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+
+#define NEXTADDR(w, s) \
+ if (rtm->rtm_addrs & (w)) { \
+ bcopy((char *)&s, cp, sizeof(s)); cp += sizeof(s);}
+
+
+static pid_t pid;
+static int nflag;
+static int tflag;
+static int32_t thiszone; /* time difference with gmt */
+static int s = -1;
+static int repeat = 0;
+
+char ntop_buf[INET6_ADDRSTRLEN]; /* inet_ntop() */
+char host_buf[NI_MAXHOST]; /* getnameinfo() */
+char ifix_buf[IFNAMSIZ]; /* if_indextoname() */
+
+int main(int, char **);
+int file(char *);
+void getsocket(void);
+int set(int, char **);
+void get(char *);
+int delete(char *);
+void dump(struct in6_addr *, int);
+static struct in6_nbrinfo *getnbrinfo(struct in6_addr *, int, int);
+static char *ether_str(struct sockaddr_dl *);
+int ndp_ether_aton(char *, u_char *);
+void usage(void);
+int rtmsg(int);
+void ifinfo(char *, int, char **);
+void rtrlist(void);
+void plist(void);
+void pfx_flush(void);
+void rtr_flush(void);
+void harmonize_rtr(void);
+#ifdef SIOCSDEFIFACE_IN6 /* XXX: check SIOCGDEFIFACE_IN6 as well? */
+static void getdefif(void);
+static void setdefif(char *);
+#endif
+static char *sec2str(time_t);
+static char *ether_str(struct sockaddr_dl *);
+static void ts_print(const struct timeval *);
+
+#ifdef ICMPV6CTL_ND6_DRLIST
+static char *rtpref_str[] = {
+ "medium", /* 00 */
+ "high", /* 01 */
+ "rsv", /* 10 */
+ "low" /* 11 */
+};
+#endif
+
+int mode = 0;
+char *arg = NULL;
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int ch;
+
+ pid = getpid();
+ thiszone = gmt2local(0);
+ while ((ch = getopt(argc, argv, "acd:f:Ii:nprstA:HPR")) != -1)
+ switch (ch) {
+ case 'a':
+ case 'c':
+ case 'p':
+ case 'r':
+ case 'H':
+ case 'P':
+ case 'R':
+ case 's':
+ case 'I':
+ if (mode) {
+ usage();
+ /*NOTREACHED*/
+ }
+ mode = ch;
+ arg = NULL;
+ break;
+ case 'd':
+ case 'f':
+ case 'i' :
+ if (mode) {
+ usage();
+ /*NOTREACHED*/
+ }
+ mode = ch;
+ arg = optarg;
+ break;
+ case 'n':
+ nflag = 1;
+ break;
+ case 't':
+ tflag = 1;
+ break;
+ case 'A':
+ if (mode) {
+ usage();
+ /*NOTREACHED*/
+ }
+ mode = 'a';
+ repeat = atoi(optarg);
+ if (repeat < 0) {
+ usage();
+ /*NOTREACHED*/
+ }
+ break;
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ switch (mode) {
+ case 'a':
+ case 'c':
+ if (argc != 0) {
+ usage();
+ /*NOTREACHED*/
+ }
+ dump(0, mode == 'c');
+ break;
+ case 'd':
+ if (argc != 0) {
+ usage();
+ /*NOTREACHED*/
+ }
+ delete(arg);
+ break;
+ case 'I':
+#ifdef SIOCSDEFIFACE_IN6 /* XXX: check SIOCGDEFIFACE_IN6 as well? */
+ if (argc > 1) {
+ usage();
+ /*NOTREACHED*/
+ } else if (argc == 1) {
+ if (strcmp(*argv, "delete") == 0 ||
+ if_nametoindex(*argv))
+ setdefif(*argv);
+ else
+ errx(1, "invalid interface %s", *argv);
+ }
+ getdefif(); /* always call it to print the result */
+ break;
+#else
+ errx(1, "not supported yet");
+ /*NOTREACHED*/
+#endif
+ case 'p':
+ if (argc != 0) {
+ usage();
+ /*NOTREACHED*/
+ }
+ plist();
+ break;
+ case 'i':
+ ifinfo(arg, argc, argv);
+ break;
+ case 'r':
+ if (argc != 0) {
+ usage();
+ /*NOTREACHED*/
+ }
+ rtrlist();
+ break;
+ case 's':
+ if (argc < 2 || argc > 4)
+ usage();
+ exit(set(argc, argv) ? 1 : 0);
+ case 'H':
+ if (argc != 0) {
+ usage();
+ /*NOTREACHED*/
+ }
+ harmonize_rtr();
+ break;
+ case 'P':
+ if (argc != 0) {
+ usage();
+ /*NOTREACHED*/
+ }
+ pfx_flush();
+ break;
+ case 'R':
+ if (argc != 0) {
+ usage();
+ /*NOTREACHED*/
+ }
+ rtr_flush();
+ break;
+ case 0:
+ if (argc != 1) {
+ usage();
+ /*NOTREACHED*/
+ }
+ get(argv[0]);
+ break;
+ }
+ exit(0);
+}
+
+/*
+ * Process a file to set standard ndp entries
+ */
+int
+file(name)
+ char *name;
+{
+ FILE *fp;
+ int i, retval;
+ char line[100], arg[5][50], *args[5];
+
+ if ((fp = fopen(name, "r")) == NULL) {
+ fprintf(stderr, "ndp: cannot open %s\n", name);
+ exit(1);
+ }
+ args[0] = &arg[0][0];
+ args[1] = &arg[1][0];
+ args[2] = &arg[2][0];
+ args[3] = &arg[3][0];
+ args[4] = &arg[4][0];
+ retval = 0;
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ i = sscanf(line, "%49s %49s %49s %49s %49s",
+ arg[0], arg[1], arg[2], arg[3], arg[4]);
+ if (i < 2) {
+ fprintf(stderr, "ndp: bad line: %s\n", line);
+ retval = 1;
+ continue;
+ }
+ if (set(i, args))
+ retval = 1;
+ }
+ fclose(fp);
+ return (retval);
+}
+
+void
+getsocket()
+{
+ if (s < 0) {
+ s = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (s < 0) {
+ err(1, "socket");
+ /* NOTREACHED */
+ }
+ }
+}
+
+struct sockaddr_in6 so_mask = {sizeof(so_mask), AF_INET6 };
+struct sockaddr_in6 blank_sin = {sizeof(blank_sin), AF_INET6 }, sin_m;
+struct sockaddr_dl blank_sdl = {sizeof(blank_sdl), AF_LINK }, sdl_m;
+int expire_time, flags, found_entry;
+struct {
+ struct rt_msghdr m_rtm;
+ char m_space[512];
+} m_rtmsg;
+
+/*
+ * Set an individual neighbor cache entry
+ */
+int
+set(argc, argv)
+ int argc;
+ char **argv;
+{
+ register struct sockaddr_in6 *sin = &sin_m;
+ register struct sockaddr_dl *sdl;
+ register struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
+ struct addrinfo hints, *res;
+ int gai_error;
+ u_char *ea;
+ char *host = argv[0], *eaddr = argv[1];
+
+ getsocket();
+ argc -= 2;
+ argv += 2;
+ sdl_m = blank_sdl;
+ sin_m = blank_sin;
+
+ bzero(&hints, sizeof(hints));
+ hints.ai_family = AF_INET6;
+ gai_error = getaddrinfo(host, NULL, &hints, &res);
+ if (gai_error) {
+ fprintf(stderr, "ndp: %s: %s\n", host,
+ gai_strerror(gai_error));
+ return 1;
+ }
+ sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
+#ifdef __KAME__
+ if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr)) {
+ *(u_int16_t *)&sin->sin6_addr.s6_addr[2] =
+ htons(((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id);
+ }
+#endif
+ ea = (u_char *)LLADDR(&sdl_m);
+ if (ndp_ether_aton(eaddr, ea) == 0)
+ sdl_m.sdl_alen = 6;
+ flags = expire_time = 0;
+ while (argc-- > 0) {
+ if (strncmp(argv[0], "temp", 4) == 0) {
+ struct timeval time;
+
+ gettimeofday(&time, 0);
+ expire_time = time.tv_sec + 20 * 60;
+ } else if (strncmp(argv[0], "proxy", 5) == 0)
+ flags |= RTF_ANNOUNCE;
+ argv++;
+ }
+ if (rtmsg(RTM_GET) < 0) {
+ errx(1, "RTM_GET(%s) failed", host);
+ /* NOTREACHED */
+ }
+ sin = (struct sockaddr_in6 *)(rtm + 1);
+ sdl = (struct sockaddr_dl *)(ROUNDUP(sin->sin6_len) + (char *)sin);
+ if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) {
+ if (sdl->sdl_family == AF_LINK &&
+ !(rtm->rtm_flags & RTF_GATEWAY)) {
+ switch (sdl->sdl_type) {
+ case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
+ case IFT_ISO88024: case IFT_ISO88025:
+ goto overwrite;
+ }
+ }
+ /*
+ * IPv4 arp command retries with sin_other = SIN_PROXY here.
+ */
+ fprintf(stderr, "set: cannot configure a new entry\n");
+ return 1;
+ }
+
+overwrite:
+ if (sdl->sdl_family != AF_LINK) {
+ printf("cannot intuit interface index and type for %s\n", host);
+ return (1);
+ }
+ sdl_m.sdl_type = sdl->sdl_type;
+ sdl_m.sdl_index = sdl->sdl_index;
+ return (rtmsg(RTM_ADD));
+}
+
+/*
+ * Display an individual neighbor cache entry
+ */
+void
+get(host)
+ char *host;
+{
+ struct sockaddr_in6 *sin = &sin_m;
+ struct addrinfo hints, *res;
+ int gai_error;
+
+ sin_m = blank_sin;
+ bzero(&hints, sizeof(hints));
+ hints.ai_family = AF_INET6;
+ gai_error = getaddrinfo(host, NULL, &hints, &res);
+ if (gai_error) {
+ fprintf(stderr, "ndp: %s: %s\n", host,
+ gai_strerror(gai_error));
+ return;
+ }
+ sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
+#ifdef __KAME__
+ if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr)) {
+ *(u_int16_t *)&sin->sin6_addr.s6_addr[2] =
+ htons(((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id);
+ }
+#endif
+ dump(&sin->sin6_addr, 0);
+ if (found_entry == 0) {
+ getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf,
+ sizeof(host_buf), NULL ,0,
+ (nflag ? NI_NUMERICHOST : 0));
+ printf("%s (%s) -- no entry\n", host, host_buf);
+ exit(1);
+ }
+}
+
+/*
+ * Delete a neighbor cache entry
+ */
+int
+delete(host)
+ char *host;
+{
+ struct sockaddr_in6 *sin = &sin_m;
+ register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
+ register char *cp = m_rtmsg.m_space;
+ struct sockaddr_dl *sdl;
+ struct addrinfo hints, *res;
+ int gai_error;
+
+ getsocket();
+ sin_m = blank_sin;
+
+ bzero(&hints, sizeof(hints));
+ hints.ai_family = AF_INET6;
+ gai_error = getaddrinfo(host, NULL, &hints, &res);
+ if (gai_error) {
+ fprintf(stderr, "ndp: %s: %s\n", host,
+ gai_strerror(gai_error));
+ return 1;
+ }
+ sin->sin6_addr = ((struct sockaddr_in6 *)res->ai_addr)->sin6_addr;
+#ifdef __KAME__
+ if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr)) {
+ *(u_int16_t *)&sin->sin6_addr.s6_addr[2] =
+ htons(((struct sockaddr_in6 *)res->ai_addr)->sin6_scope_id);
+ }
+#endif
+ if (rtmsg(RTM_GET) < 0) {
+ errx(1, "RTM_GET(%s) failed", host);
+ /* NOTREACHED */
+ }
+ sin = (struct sockaddr_in6 *)(rtm + 1);
+ sdl = (struct sockaddr_dl *)(ROUNDUP(sin->sin6_len) + (char *)sin);
+ if (IN6_ARE_ADDR_EQUAL(&sin->sin6_addr, &sin_m.sin6_addr)) {
+ if (sdl->sdl_family == AF_LINK &&
+ !(rtm->rtm_flags & RTF_GATEWAY)) {
+ goto delete;
+ }
+ /*
+ * IPv4 arp command retries with sin_other = SIN_PROXY here.
+ */
+ fprintf(stderr, "delete: cannot delete non-NDP entry\n");
+ return 1;
+ }
+
+delete:
+ if (sdl->sdl_family != AF_LINK) {
+ printf("cannot locate %s\n", host);
+ return (1);
+ }
+ /*
+ * need to reinit the field because it has rt_key
+ * but we want the actual address
+ */
+ NEXTADDR(RTA_DST, sin_m);
+ rtm->rtm_flags |= RTF_LLDATA;
+ if (rtmsg(RTM_DELETE) == 0) {
+ struct sockaddr_in6 s6 = *sin; /* XXX: for safety */
+
+#ifdef __KAME__
+ if (IN6_IS_ADDR_LINKLOCAL(&s6.sin6_addr)) {
+ s6.sin6_scope_id = ntohs(*(u_int16_t *)&s6.sin6_addr.s6_addr[2]);
+ *(u_int16_t *)&s6.sin6_addr.s6_addr[2] = 0;
+ }
+#endif
+ getnameinfo((struct sockaddr *)&s6,
+ s6.sin6_len, host_buf,
+ sizeof(host_buf), NULL, 0,
+ (nflag ? NI_NUMERICHOST : 0));
+ printf("%s (%s) deleted\n", host, host_buf);
+ }
+
+ return 0;
+}
+
+#define W_ADDR 36
+#define W_LL 17
+#define W_IF 6
+
+/*
+ * Dump the entire neighbor cache
+ */
+void
+dump(addr, cflag)
+ struct in6_addr *addr;
+ int cflag;
+{
+ int mib[6];
+ size_t needed;
+ char *lim, *buf, *next;
+ struct rt_msghdr *rtm;
+ struct sockaddr_in6 *sin;
+ struct sockaddr_dl *sdl;
+ extern int h_errno;
+ struct in6_nbrinfo *nbi;
+ struct timeval time;
+ int addrwidth;
+ int llwidth;
+ int ifwidth;
+ char flgbuf[8];
+ char *ifname;
+
+ /* Print header */
+ if (!tflag && !cflag)
+ printf("%-*.*s %-*.*s %*.*s %-9.9s %1s %5s\n",
+ W_ADDR, W_ADDR, "Neighbor", W_LL, W_LL, "Linklayer Address",
+ W_IF, W_IF, "Netif", "Expire", "S", "Flags");
+
+again:;
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET6;
+ mib[4] = NET_RT_FLAGS;
+#ifdef RTF_LLINFO
+ mib[5] = RTF_LLINFO;
+#else
+ mib[5] = 0;
+#endif
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
+ err(1, "sysctl(PF_ROUTE estimate)");
+ if (needed > 0) {
+ if ((buf = malloc(needed)) == NULL)
+ err(1, "malloc");
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
+ err(1, "sysctl(PF_ROUTE, NET_RT_FLAGS)");
+ lim = buf + needed;
+ } else
+ buf = lim = NULL;
+
+ for (next = buf; next && next < lim; next += rtm->rtm_msglen) {
+ int isrouter = 0, prbs = 0;
+
+ rtm = (struct rt_msghdr *)next;
+ sin = (struct sockaddr_in6 *)(rtm + 1);
+ sdl = (struct sockaddr_dl *)((char *)sin + ROUNDUP(sin->sin6_len));
+
+ /*
+ * Some OSes can produce a route that has the LINK flag but
+ * has a non-AF_LINK gateway (e.g. fe80::xx%lo0 on FreeBSD
+ * and BSD/OS, where xx is not the interface identifier on
+ * lo0). Such routes entry would annoy getnbrinfo() below,
+ * so we skip them.
+ * XXX: such routes should have the GATEWAY flag, not the
+ * LINK flag. However, there is rotten routing software
+ * that advertises all routes that have the GATEWAY flag.
+ * Thus, KAME kernel intentionally does not set the LINK flag.
+ * What is to be fixed is not ndp, but such routing software
+ * (and the kernel workaround)...
+ */
+ if (sdl->sdl_family != AF_LINK)
+ continue;
+
+ if (!(rtm->rtm_flags & RTF_HOST))
+ continue;
+
+ if (addr) {
+ if (!IN6_ARE_ADDR_EQUAL(addr, &sin->sin6_addr))
+ continue;
+ found_entry = 1;
+ } else if (IN6_IS_ADDR_MULTICAST(&sin->sin6_addr))
+ continue;
+ if (IN6_IS_ADDR_LINKLOCAL(&sin->sin6_addr) ||
+ IN6_IS_ADDR_MC_LINKLOCAL(&sin->sin6_addr)) {
+ /* XXX: should scope id be filled in the kernel? */
+ if (sin->sin6_scope_id == 0)
+ sin->sin6_scope_id = sdl->sdl_index;
+#ifdef __KAME__
+ /* KAME specific hack; removed the embedded id */
+ *(u_int16_t *)&sin->sin6_addr.s6_addr[2] = 0;
+#endif
+ }
+ getnameinfo((struct sockaddr *)sin, sin->sin6_len, host_buf,
+ sizeof(host_buf), NULL, 0, (nflag ? NI_NUMERICHOST : 0));
+ if (cflag) {
+#ifdef RTF_WASCLONED
+ if (rtm->rtm_flags & RTF_WASCLONED)
+ delete(host_buf);
+#elif defined(RTF_CLONED)
+ if (rtm->rtm_flags & RTF_CLONED)
+ delete(host_buf);
+#else
+ delete(host_buf);
+#endif
+ continue;
+ }
+ gettimeofday(&time, 0);
+ if (tflag)
+ ts_print(&time);
+
+ addrwidth = strlen(host_buf);
+ if (addrwidth < W_ADDR)
+ addrwidth = W_ADDR;
+ llwidth = strlen(ether_str(sdl));
+ if (W_ADDR + W_LL - addrwidth > llwidth)
+ llwidth = W_ADDR + W_LL - addrwidth;
+ ifname = if_indextoname(sdl->sdl_index, ifix_buf);
+ if (!ifname)
+ ifname = "?";
+ ifwidth = strlen(ifname);
+ if (W_ADDR + W_LL + W_IF - addrwidth - llwidth > ifwidth)
+ ifwidth = W_ADDR + W_LL + W_IF - addrwidth - llwidth;
+
+ printf("%-*.*s %-*.*s %*.*s", addrwidth, addrwidth, host_buf,
+ llwidth, llwidth, ether_str(sdl), ifwidth, ifwidth, ifname);
+
+ /* Print neighbor discovery specific informations */
+ nbi = getnbrinfo(&sin->sin6_addr, sdl->sdl_index, 1);
+ if (nbi) {
+ if (nbi->expire > time.tv_sec) {
+ printf(" %-9.9s",
+ sec2str(nbi->expire - time.tv_sec));
+ } else if (nbi->expire == 0)
+ printf(" %-9.9s", "permanent");
+ else
+ printf(" %-9.9s", "expired");
+
+ switch (nbi->state) {
+ case ND6_LLINFO_NOSTATE:
+ printf(" N");
+ break;
+#ifdef ND6_LLINFO_WAITDELETE
+ case ND6_LLINFO_WAITDELETE:
+ printf(" W");
+ break;
+#endif
+ case ND6_LLINFO_INCOMPLETE:
+ printf(" I");
+ break;
+ case ND6_LLINFO_REACHABLE:
+ printf(" R");
+ break;
+ case ND6_LLINFO_STALE:
+ printf(" S");
+ break;
+ case ND6_LLINFO_DELAY:
+ printf(" D");
+ break;
+ case ND6_LLINFO_PROBE:
+ printf(" P");
+ break;
+ default:
+ printf(" ?");
+ break;
+ }
+
+ isrouter = nbi->isrouter;
+ prbs = nbi->asked;
+ } else {
+ warnx("failed to get neighbor information");
+ printf(" ");
+ }
+
+ /*
+ * other flags. R: router, P: proxy, W: ??
+ */
+ if ((rtm->rtm_addrs & RTA_NETMASK) == 0) {
+ snprintf(flgbuf, sizeof(flgbuf), "%s%s",
+ isrouter ? "R" : "",
+ (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : "");
+ } else {
+ sin = (struct sockaddr_in6 *)
+ (sdl->sdl_len + (char *)sdl);
+#if 0 /* W and P are mystery even for us */
+ snprintf(flgbuf, sizeof(flgbuf), "%s%s%s%s",
+ isrouter ? "R" : "",
+ !IN6_IS_ADDR_UNSPECIFIED(&sin->sin6_addr) ? "P" : "",
+ (sin->sin6_len != sizeof(struct sockaddr_in6)) ? "W" : "",
+ (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : "");
+#else
+ snprintf(flgbuf, sizeof(flgbuf), "%s%s",
+ isrouter ? "R" : "",
+ (rtm->rtm_flags & RTF_ANNOUNCE) ? "p" : "");
+#endif
+ }
+ printf(" %s", flgbuf);
+
+ if (prbs)
+ printf(" %d", prbs);
+
+ printf("\n");
+ }
+ if (buf != NULL)
+ free(buf);
+
+ if (repeat) {
+ printf("\n");
+ fflush(stdout);
+ sleep(repeat);
+ goto again;
+ }
+}
+
+static struct in6_nbrinfo *
+getnbrinfo(addr, ifindex, warning)
+ struct in6_addr *addr;
+ int ifindex;
+ int warning;
+{
+ static struct in6_nbrinfo nbi;
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
+ err(1, "socket");
+
+ bzero(&nbi, sizeof(nbi));
+ if_indextoname(ifindex, nbi.ifname);
+ nbi.addr = *addr;
+ if (ioctl(s, SIOCGNBRINFO_IN6, (caddr_t)&nbi) < 0) {
+ if (warning)
+ warn("ioctl(SIOCGNBRINFO_IN6)");
+ close(s);
+ return(NULL);
+ }
+
+ close(s);
+ return(&nbi);
+}
+
+static char *
+ether_str(sdl)
+ struct sockaddr_dl *sdl;
+{
+ static char hbuf[NI_MAXHOST];
+ u_char *cp;
+
+ if (sdl->sdl_alen) {
+ cp = (u_char *)LLADDR(sdl);
+ snprintf(hbuf, sizeof(hbuf), "%x:%x:%x:%x:%x:%x",
+ cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]);
+ } else
+ snprintf(hbuf, sizeof(hbuf), "(incomplete)");
+
+ return(hbuf);
+}
+
+int
+ndp_ether_aton(a, n)
+ char *a;
+ u_char *n;
+{
+ int i, o[6];
+
+ i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o[0], &o[1], &o[2],
+ &o[3], &o[4], &o[5]);
+ if (i != 6) {
+ fprintf(stderr, "ndp: invalid Ethernet address '%s'\n", a);
+ return (1);
+ }
+ for (i = 0; i < 6; i++)
+ n[i] = o[i];
+ return (0);
+}
+
+void
+usage()
+{
+ printf("usage: ndp [-nt] hostname\n");
+ printf(" ndp [-nt] -a | -c | -p | -r | -H | -P | -R\n");
+ printf(" ndp [-nt] -A wait\n");
+ printf(" ndp [-nt] -d hostname\n");
+ printf(" ndp [-nt] -f filename\n");
+ printf(" ndp [-nt] -i interface [flags...]\n");
+#ifdef SIOCSDEFIFACE_IN6
+ printf(" ndp [-nt] -I [interface|delete]\n");
+#endif
+ printf(" ndp [-nt] -s nodename etheraddr [temp] [proxy]\n");
+ exit(1);
+}
+
+int
+rtmsg(cmd)
+ int cmd;
+{
+ static int seq;
+ int rlen;
+ register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
+ register char *cp = m_rtmsg.m_space;
+ register int l;
+
+ errno = 0;
+ if (cmd == RTM_DELETE)
+ goto doit;
+ bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
+ rtm->rtm_flags = flags;
+ rtm->rtm_version = RTM_VERSION;
+
+ switch (cmd) {
+ default:
+ fprintf(stderr, "ndp: internal wrong cmd\n");
+ exit(1);
+ case RTM_ADD:
+ rtm->rtm_addrs |= RTA_GATEWAY;
+ if (expire_time) {
+ rtm->rtm_rmx.rmx_expire = expire_time;
+ rtm->rtm_inits = RTV_EXPIRE;
+ }
+ rtm->rtm_flags |= (RTF_HOST | RTF_STATIC | RTF_LLDATA);
+#if 0 /* we don't support ipv6addr/128 type proxying */
+ if (rtm->rtm_flags & RTF_ANNOUNCE) {
+ rtm->rtm_flags &= ~RTF_HOST;
+ rtm->rtm_addrs |= RTA_NETMASK;
+ }
+#endif
+ /* FALLTHROUGH */
+ case RTM_GET:
+ rtm->rtm_addrs |= RTA_DST;
+ }
+
+ NEXTADDR(RTA_DST, sin_m);
+ NEXTADDR(RTA_GATEWAY, sdl_m);
+#if 0 /* we don't support ipv6addr/128 type proxying */
+ memset(&so_mask.sin6_addr, 0xff, sizeof(so_mask.sin6_addr));
+ NEXTADDR(RTA_NETMASK, so_mask);
+#endif
+
+ rtm->rtm_msglen = cp - (char *)&m_rtmsg;
+doit:
+ l = rtm->rtm_msglen;
+ rtm->rtm_seq = ++seq;
+ rtm->rtm_type = cmd;
+ if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
+ if (errno != ESRCH || cmd != RTM_DELETE) {
+ err(1, "writing to routing socket");
+ /* NOTREACHED */
+ }
+ }
+ do {
+ l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
+ } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid));
+ if (l < 0)
+ (void) fprintf(stderr, "ndp: read from routing socket: %s\n",
+ strerror(errno));
+ return (0);
+}
+
+void
+ifinfo(ifname, argc, argv)
+ char *ifname;
+ int argc;
+ char **argv;
+{
+ struct in6_ndireq nd;
+ int i, s;
+ u_int32_t newflags;
+#ifdef IPV6CTL_USETEMPADDR
+ u_int8_t nullbuf[8];
+#endif
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ err(1, "socket");
+ /* NOTREACHED */
+ }
+ bzero(&nd, sizeof(nd));
+ strlcpy(nd.ifname, ifname, sizeof(nd.ifname));
+ if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) {
+ err(1, "ioctl(SIOCGIFINFO_IN6)");
+ /* NOTREACHED */
+ }
+#define ND nd.ndi
+ newflags = ND.flags;
+ for (i = 0; i < argc; i++) {
+ int clear = 0;
+ char *cp = argv[i];
+
+ if (*cp == '-') {
+ clear = 1;
+ cp++;
+ }
+
+#define SETFLAG(s, f) \
+ do {\
+ if (strcmp(cp, (s)) == 0) {\
+ if (clear)\
+ newflags &= ~(f);\
+ else\
+ newflags |= (f);\
+ }\
+ } while (0)
+/*
+ * XXX: this macro is not 100% correct, in that it matches "nud" against
+ * "nudbogus". But we just let it go since this is minor.
+ */
+#define SETVALUE(f, v) \
+ do { \
+ char *valptr; \
+ unsigned long newval; \
+ v = 0; /* unspecified */ \
+ if (strncmp(cp, f, strlen(f)) == 0) { \
+ valptr = strchr(cp, '='); \
+ if (valptr == NULL) \
+ err(1, "syntax error in %s field", (f)); \
+ errno = 0; \
+ newval = strtoul(++valptr, NULL, 0); \
+ if (errno) \
+ err(1, "syntax error in %s's value", (f)); \
+ v = newval; \
+ } \
+ } while (0)
+
+ SETFLAG("disabled", ND6_IFF_IFDISABLED);
+ SETFLAG("nud", ND6_IFF_PERFORMNUD);
+#ifdef ND6_IFF_ACCEPT_RTADV
+ SETFLAG("accept_rtadv", ND6_IFF_ACCEPT_RTADV);
+#endif
+#ifdef ND6_IFF_PREFER_SOURCE
+ SETFLAG("prefer_source", ND6_IFF_PREFER_SOURCE);
+#endif
+ SETVALUE("basereachable", ND.basereachable);
+ SETVALUE("retrans", ND.retrans);
+ SETVALUE("curhlim", ND.chlim);
+
+ ND.flags = newflags;
+ if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&nd) < 0) {
+ err(1, "ioctl(SIOCSIFINFO_IN6)");
+ /* NOTREACHED */
+ }
+#undef SETFLAG
+#undef SETVALUE
+ }
+
+ if (!ND.initialized) {
+ errx(1, "%s: not initialized yet", ifname);
+ /* NOTREACHED */
+ }
+
+ if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&nd) < 0) {
+ err(1, "ioctl(SIOCGIFINFO_IN6)");
+ /* NOTREACHED */
+ }
+ printf("linkmtu=%d", ND.linkmtu);
+ printf(", maxmtu=%d", ND.maxmtu);
+ printf(", curhlim=%d", ND.chlim);
+ printf(", basereachable=%ds%dms",
+ ND.basereachable / 1000, ND.basereachable % 1000);
+ printf(", reachable=%ds", ND.reachable);
+ printf(", retrans=%ds%dms", ND.retrans / 1000, ND.retrans % 1000);
+#ifdef IPV6CTL_USETEMPADDR
+ memset(nullbuf, 0, sizeof(nullbuf));
+ if (memcmp(nullbuf, ND.randomid, sizeof(nullbuf)) != 0) {
+ int j;
+ u_int8_t *rbuf;
+
+ for (i = 0; i < 3; i++) {
+ switch (i) {
+ case 0:
+ printf("\nRandom seed(0): ");
+ rbuf = ND.randomseed0;
+ break;
+ case 1:
+ printf("\nRandom seed(1): ");
+ rbuf = ND.randomseed1;
+ break;
+ case 2:
+ printf("\nRandom ID: ");
+ rbuf = ND.randomid;
+ break;
+ default:
+ errx(1, "impossible case for tempaddr display");
+ }
+ for (j = 0; j < 8; j++)
+ printf("%02x", rbuf[j]);
+ }
+ }
+#endif
+ if (ND.flags) {
+ printf("\nFlags: ");
+#ifdef ND6_IFF_IFDISABLED
+ if ((ND.flags & ND6_IFF_IFDISABLED))
+ printf("disabled ");
+#endif
+ if ((ND.flags & ND6_IFF_PERFORMNUD))
+ printf("nud ");
+#ifdef ND6_IFF_ACCEPT_RTADV
+ if ((ND.flags & ND6_IFF_ACCEPT_RTADV))
+ printf("accept_rtadv ");
+#endif
+#ifdef ND6_IFF_PREFER_SOURCE
+ if ((ND.flags & ND6_IFF_PREFER_SOURCE))
+ printf("prefer_source ");
+#endif
+ }
+ putc('\n', stdout);
+#undef ND
+
+ close(s);
+}
+
+#ifndef ND_RA_FLAG_RTPREF_MASK /* XXX: just for compilation on *BSD release */
+#define ND_RA_FLAG_RTPREF_MASK 0x18 /* 00011000 */
+#endif
+
+void
+rtrlist()
+{
+#ifdef ICMPV6CTL_ND6_DRLIST
+ int mib[] = { CTL_NET, PF_INET6, IPPROTO_ICMPV6, ICMPV6CTL_ND6_DRLIST };
+ char *buf;
+ struct in6_defrouter *p, *ep;
+ size_t l;
+ struct timeval time;
+
+ if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) {
+ err(1, "sysctl(ICMPV6CTL_ND6_DRLIST)");
+ /*NOTREACHED*/
+ }
+ if (l == 0)
+ return;
+ buf = malloc(l);
+ if (!buf) {
+ err(1, "malloc");
+ /*NOTREACHED*/
+ }
+ if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) {
+ err(1, "sysctl(ICMPV6CTL_ND6_DRLIST)");
+ /*NOTREACHED*/
+ }
+
+ ep = (struct in6_defrouter *)(buf + l);
+ for (p = (struct in6_defrouter *)buf; p < ep; p++) {
+ int rtpref;
+
+ if (getnameinfo((struct sockaddr *)&p->rtaddr,
+ p->rtaddr.sin6_len, host_buf, sizeof(host_buf), NULL, 0,
+ (nflag ? NI_NUMERICHOST : 0)) != 0)
+ strlcpy(host_buf, "?", sizeof(host_buf));
+
+ printf("%s if=%s", host_buf,
+ if_indextoname(p->if_index, ifix_buf));
+ printf(", flags=%s%s",
+ p->flags & ND_RA_FLAG_MANAGED ? "M" : "",
+ p->flags & ND_RA_FLAG_OTHER ? "O" : "");
+ rtpref = ((p->flags & ND_RA_FLAG_RTPREF_MASK) >> 3) & 0xff;
+ printf(", pref=%s", rtpref_str[rtpref]);
+
+ gettimeofday(&time, 0);
+ if (p->expire == 0)
+ printf(", expire=Never\n");
+ else
+ printf(", expire=%s\n",
+ sec2str(p->expire - time.tv_sec));
+ }
+ free(buf);
+#else
+ struct in6_drlist dr;
+ int s, i;
+ struct timeval time;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ err(1, "socket");
+ /* NOTREACHED */
+ }
+ bzero(&dr, sizeof(dr));
+ strlcpy(dr.ifname, "lo0", sizeof(dr.ifname)); /* dummy */
+ if (ioctl(s, SIOCGDRLST_IN6, (caddr_t)&dr) < 0) {
+ err(1, "ioctl(SIOCGDRLST_IN6)");
+ /* NOTREACHED */
+ }
+#define DR dr.defrouter[i]
+ for (i = 0 ; DR.if_index && i < DRLSTSIZ ; i++) {
+ struct sockaddr_in6 sin6;
+
+ bzero(&sin6, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_len = sizeof(sin6);
+ sin6.sin6_addr = DR.rtaddr;
+ getnameinfo((struct sockaddr *)&sin6, sin6.sin6_len, host_buf,
+ sizeof(host_buf), NULL, 0,
+ (nflag ? NI_NUMERICHOST : 0));
+
+ printf("%s if=%s", host_buf,
+ if_indextoname(DR.if_index, ifix_buf));
+ printf(", flags=%s%s",
+ DR.flags & ND_RA_FLAG_MANAGED ? "M" : "",
+ DR.flags & ND_RA_FLAG_OTHER ? "O" : "");
+ gettimeofday(&time, 0);
+ if (DR.expire == 0)
+ printf(", expire=Never\n");
+ else
+ printf(", expire=%s\n",
+ sec2str(DR.expire - time.tv_sec));
+ }
+#undef DR
+ close(s);
+#endif
+}
+
+void
+plist()
+{
+#ifdef ICMPV6CTL_ND6_PRLIST
+ int mib[] = { CTL_NET, PF_INET6, IPPROTO_ICMPV6, ICMPV6CTL_ND6_PRLIST };
+ char *buf;
+ struct in6_prefix *p, *ep, *n;
+ struct sockaddr_in6 *advrtr;
+ size_t l;
+ struct timeval time;
+ const int niflags = NI_NUMERICHOST;
+ int ninflags = nflag ? NI_NUMERICHOST : 0;
+ char namebuf[NI_MAXHOST];
+
+ if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), NULL, &l, NULL, 0) < 0) {
+ err(1, "sysctl(ICMPV6CTL_ND6_PRLIST)");
+ /*NOTREACHED*/
+ }
+ buf = malloc(l);
+ if (!buf) {
+ err(1, "malloc");
+ /*NOTREACHED*/
+ }
+ if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), buf, &l, NULL, 0) < 0) {
+ err(1, "sysctl(ICMPV6CTL_ND6_PRLIST)");
+ /*NOTREACHED*/
+ }
+
+ ep = (struct in6_prefix *)(buf + l);
+ for (p = (struct in6_prefix *)buf; p < ep; p = n) {
+ advrtr = (struct sockaddr_in6 *)(p + 1);
+ n = (struct in6_prefix *)&advrtr[p->advrtrs];
+
+ if (getnameinfo((struct sockaddr *)&p->prefix,
+ p->prefix.sin6_len, namebuf, sizeof(namebuf),
+ NULL, 0, niflags) != 0)
+ strlcpy(namebuf, "?", sizeof(namebuf));
+ printf("%s/%d if=%s\n", namebuf, p->prefixlen,
+ if_indextoname(p->if_index, ifix_buf));
+
+ gettimeofday(&time, 0);
+ /*
+ * meaning of fields, especially flags, is very different
+ * by origin. notify the difference to the users.
+ */
+ printf("flags=%s%s%s%s%s",
+ p->raflags.onlink ? "L" : "",
+ p->raflags.autonomous ? "A" : "",
+ (p->flags & NDPRF_ONLINK) != 0 ? "O" : "",
+ (p->flags & NDPRF_DETACHED) != 0 ? "D" : "",
+#ifdef NDPRF_HOME
+ (p->flags & NDPRF_HOME) != 0 ? "H" : ""
+#else
+ ""
+#endif
+ );
+ if (p->vltime == ND6_INFINITE_LIFETIME)
+ printf(" vltime=infinity");
+ else
+ printf(" vltime=%lu", (unsigned long)p->vltime);
+ if (p->pltime == ND6_INFINITE_LIFETIME)
+ printf(", pltime=infinity");
+ else
+ printf(", pltime=%lu", (unsigned long)p->pltime);
+ if (p->expire == 0)
+ printf(", expire=Never");
+ else if (p->expire >= time.tv_sec)
+ printf(", expire=%s",
+ sec2str(p->expire - time.tv_sec));
+ else
+ printf(", expired");
+ printf(", ref=%d", p->refcnt);
+ printf("\n");
+ /*
+ * "advertising router" list is meaningful only if the prefix
+ * information is from RA.
+ */
+ if (p->advrtrs) {
+ int j;
+ struct sockaddr_in6 *sin6;
+
+ sin6 = advrtr;
+ printf(" advertised by\n");
+ for (j = 0; j < p->advrtrs; j++) {
+ struct in6_nbrinfo *nbi;
+
+ if (getnameinfo((struct sockaddr *)sin6,
+ sin6->sin6_len, namebuf, sizeof(namebuf),
+ NULL, 0, ninflags) != 0)
+ strlcpy(namebuf, "?", sizeof(namebuf));
+ printf(" %s", namebuf);
+
+ nbi = getnbrinfo(&sin6->sin6_addr,
+ p->if_index, 0);
+ if (nbi) {
+ switch (nbi->state) {
+ case ND6_LLINFO_REACHABLE:
+ case ND6_LLINFO_STALE:
+ case ND6_LLINFO_DELAY:
+ case ND6_LLINFO_PROBE:
+ printf(" (reachable)\n");
+ break;
+ default:
+ printf(" (unreachable)\n");
+ }
+ } else
+ printf(" (no neighbor state)\n");
+ sin6++;
+ }
+ } else
+ printf(" No advertising router\n");
+ }
+ free(buf);
+#else
+ struct in6_prlist pr;
+ int s, i;
+ struct timeval time;
+
+ gettimeofday(&time, 0);
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ err(1, "socket");
+ /* NOTREACHED */
+ }
+ bzero(&pr, sizeof(pr));
+ strlcpy(pr.ifname, "lo0", sizeof(pr.ifname)); /* dummy */
+ if (ioctl(s, SIOCGPRLST_IN6, (caddr_t)&pr) < 0) {
+ err(1, "ioctl(SIOCGPRLST_IN6)");
+ /* NOTREACHED */
+ }
+#define PR pr.prefix[i]
+ for (i = 0; PR.if_index && i < PRLSTSIZ ; i++) {
+ struct sockaddr_in6 p6;
+ char namebuf[NI_MAXHOST];
+ int niflags;
+
+#ifdef NDPRF_ONLINK
+ p6 = PR.prefix;
+#else
+ memset(&p6, 0, sizeof(p6));
+ p6.sin6_family = AF_INET6;
+ p6.sin6_len = sizeof(p6);
+ p6.sin6_addr = PR.prefix;
+#endif
+
+ /*
+ * copy link index to sin6_scope_id field.
+ * XXX: KAME specific.
+ */
+ if (IN6_IS_ADDR_LINKLOCAL(&p6.sin6_addr)) {
+ u_int16_t linkid;
+
+ memcpy(&linkid, &p6.sin6_addr.s6_addr[2],
+ sizeof(linkid));
+ linkid = ntohs(linkid);
+ p6.sin6_scope_id = linkid;
+ p6.sin6_addr.s6_addr[2] = 0;
+ p6.sin6_addr.s6_addr[3] = 0;
+ }
+
+ niflags = NI_NUMERICHOST;
+ if (getnameinfo((struct sockaddr *)&p6,
+ sizeof(p6), namebuf, sizeof(namebuf),
+ NULL, 0, niflags)) {
+ warnx("getnameinfo failed");
+ continue;
+ }
+ printf("%s/%d if=%s\n", namebuf, PR.prefixlen,
+ if_indextoname(PR.if_index, ifix_buf));
+
+ gettimeofday(&time, 0);
+ /*
+ * meaning of fields, especially flags, is very different
+ * by origin. notify the difference to the users.
+ */
+#if 0
+ printf(" %s",
+ PR.origin == PR_ORIG_RA ? "" : "advertise: ");
+#endif
+#ifdef NDPRF_ONLINK
+ printf("flags=%s%s%s%s%s",
+ PR.raflags.onlink ? "L" : "",
+ PR.raflags.autonomous ? "A" : "",
+ (PR.flags & NDPRF_ONLINK) != 0 ? "O" : "",
+ (PR.flags & NDPRF_DETACHED) != 0 ? "D" : "",
+#ifdef NDPRF_HOME
+ (PR.flags & NDPRF_HOME) != 0 ? "H" : ""
+#else
+ ""
+#endif
+ );
+#else
+ printf("flags=%s%s",
+ PR.raflags.onlink ? "L" : "",
+ PR.raflags.autonomous ? "A" : "");
+#endif
+ if (PR.vltime == ND6_INFINITE_LIFETIME)
+ printf(" vltime=infinity");
+ else
+ printf(" vltime=%lu", PR.vltime);
+ if (PR.pltime == ND6_INFINITE_LIFETIME)
+ printf(", pltime=infinity");
+ else
+ printf(", pltime=%lu", PR.pltime);
+ if (PR.expire == 0)
+ printf(", expire=Never");
+ else if (PR.expire >= time.tv_sec)
+ printf(", expire=%s",
+ sec2str(PR.expire - time.tv_sec));
+ else
+ printf(", expired");
+#ifdef NDPRF_ONLINK
+ printf(", ref=%d", PR.refcnt);
+#endif
+#if 0
+ switch (PR.origin) {
+ case PR_ORIG_RA:
+ printf(", origin=RA");
+ break;
+ case PR_ORIG_RR:
+ printf(", origin=RR");
+ break;
+ case PR_ORIG_STATIC:
+ printf(", origin=static");
+ break;
+ case PR_ORIG_KERNEL:
+ printf(", origin=kernel");
+ break;
+ default:
+ printf(", origin=?");
+ break;
+ }
+#endif
+ printf("\n");
+ /*
+ * "advertising router" list is meaningful only if the prefix
+ * information is from RA.
+ */
+ if (0 && /* prefix origin is almost obsolted */
+ PR.origin != PR_ORIG_RA)
+ ;
+ else if (PR.advrtrs) {
+ int j;
+ printf(" advertised by\n");
+ for (j = 0; j < PR.advrtrs; j++) {
+ struct sockaddr_in6 sin6;
+ struct in6_nbrinfo *nbi;
+
+ bzero(&sin6, sizeof(sin6));
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_len = sizeof(sin6);
+ sin6.sin6_addr = PR.advrtr[j];
+ sin6.sin6_scope_id = PR.if_index; /* XXX */
+ getnameinfo((struct sockaddr *)&sin6,
+ sin6.sin6_len, host_buf,
+ sizeof(host_buf), NULL, 0,
+ (nflag ? NI_NUMERICHOST : 0));
+ printf(" %s", host_buf);
+
+ nbi = getnbrinfo(&sin6.sin6_addr,
+ PR.if_index, 0);
+ if (nbi) {
+ switch (nbi->state) {
+ case ND6_LLINFO_REACHABLE:
+ case ND6_LLINFO_STALE:
+ case ND6_LLINFO_DELAY:
+ case ND6_LLINFO_PROBE:
+ printf(" (reachable)\n");
+ break;
+ default:
+ printf(" (unreachable)\n");
+ }
+ } else
+ printf(" (no neighbor state)\n");
+ }
+ if (PR.advrtrs > DRLSTSIZ)
+ printf(" and %d routers\n",
+ PR.advrtrs - DRLSTSIZ);
+ } else
+ printf(" No advertising router\n");
+ }
+#undef PR
+ close(s);
+#endif
+}
+
+void
+pfx_flush()
+{
+ char dummyif[IFNAMSIZ+8];
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
+ err(1, "socket");
+ strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */
+ if (ioctl(s, SIOCSPFXFLUSH_IN6, (caddr_t)&dummyif) < 0)
+ err(1, "ioctl(SIOCSPFXFLUSH_IN6)");
+}
+
+void
+rtr_flush()
+{
+ char dummyif[IFNAMSIZ+8];
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
+ err(1, "socket");
+ strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */
+ if (ioctl(s, SIOCSRTRFLUSH_IN6, (caddr_t)&dummyif) < 0)
+ err(1, "ioctl(SIOCSRTRFLUSH_IN6)");
+
+ close(s);
+}
+
+void
+harmonize_rtr()
+{
+ char dummyif[IFNAMSIZ+8];
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
+ err(1, "socket");
+ strlcpy(dummyif, "lo0", sizeof(dummyif)); /* dummy */
+ if (ioctl(s, SIOCSNDFLUSH_IN6, (caddr_t)&dummyif) < 0)
+ err(1, "ioctl(SIOCSNDFLUSH_IN6)");
+
+ close(s);
+}
+
+#ifdef SIOCSDEFIFACE_IN6 /* XXX: check SIOCGDEFIFACE_IN6 as well? */
+static void
+setdefif(ifname)
+ char *ifname;
+{
+ struct in6_ndifreq ndifreq;
+ unsigned int ifindex;
+
+ if (strcasecmp(ifname, "delete") == 0)
+ ifindex = 0;
+ else {
+ if ((ifindex = if_nametoindex(ifname)) == 0)
+ err(1, "failed to resolve i/f index for %s", ifname);
+ }
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
+ err(1, "socket");
+
+ strlcpy(ndifreq.ifname, "lo0", sizeof(ndifreq.ifname)); /* dummy */
+ ndifreq.ifindex = ifindex;
+
+ if (ioctl(s, SIOCSDEFIFACE_IN6, (caddr_t)&ndifreq) < 0)
+ err(1, "ioctl(SIOCSDEFIFACE_IN6)");
+
+ close(s);
+}
+
+static void
+getdefif()
+{
+ struct in6_ndifreq ndifreq;
+ char ifname[IFNAMSIZ+8];
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
+ err(1, "socket");
+
+ memset(&ndifreq, 0, sizeof(ndifreq));
+ strlcpy(ndifreq.ifname, "lo0", sizeof(ndifreq.ifname)); /* dummy */
+
+ if (ioctl(s, SIOCGDEFIFACE_IN6, (caddr_t)&ndifreq) < 0)
+ err(1, "ioctl(SIOCGDEFIFACE_IN6)");
+
+ if (ndifreq.ifindex == 0)
+ printf("No default interface.\n");
+ else {
+ if ((if_indextoname(ndifreq.ifindex, ifname)) == NULL)
+ err(1, "failed to resolve ifname for index %lu",
+ ndifreq.ifindex);
+ printf("ND default interface = %s\n", ifname);
+ }
+
+ close(s);
+}
+#endif
+
+static char *
+sec2str(total)
+ time_t total;
+{
+ static char result[256];
+ int days, hours, mins, secs;
+ int first = 1;
+ char *p = result;
+ char *ep = &result[sizeof(result)];
+ int n;
+
+ days = total / 3600 / 24;
+ hours = (total / 3600) % 24;
+ mins = (total / 60) % 60;
+ secs = total % 60;
+
+ if (days) {
+ first = 0;
+ n = snprintf(p, ep - p, "%dd", days);
+ if (n < 0 || n >= ep - p)
+ return "?";
+ p += n;
+ }
+ if (!first || hours) {
+ first = 0;
+ n = snprintf(p, ep - p, "%dh", hours);
+ if (n < 0 || n >= ep - p)
+ return "?";
+ p += n;
+ }
+ if (!first || mins) {
+ first = 0;
+ n = snprintf(p, ep - p, "%dm", mins);
+ if (n < 0 || n >= ep - p)
+ return "?";
+ p += n;
+ }
+ snprintf(p, ep - p, "%ds", secs);
+
+ return(result);
+}
+
+/*
+ * Print the timestamp
+ * from tcpdump/util.c
+ */
+static void
+ts_print(tvp)
+ const struct timeval *tvp;
+{
+ int s;
+
+ /* Default */
+ s = (tvp->tv_sec + thiszone) % 86400;
+ (void)printf("%02d:%02d:%02d.%06u ",
+ s / 3600, (s % 3600) / 60, s % 60, (u_int32_t)tvp->tv_usec);
+}
+
+#undef NEXTADDR
diff --git a/usr.sbin/newsyslog/Makefile b/usr.sbin/newsyslog/Makefile
new file mode 100644
index 0000000..9c301a1
--- /dev/null
+++ b/usr.sbin/newsyslog/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= newsyslog
+MAN= newsyslog.8 newsyslog.conf.5
+SRCS= newsyslog.c ptimes.c
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/newsyslog/extern.h b/usr.sbin/newsyslog/extern.h
new file mode 100644
index 0000000..c350226
--- /dev/null
+++ b/usr.sbin/newsyslog/extern.h
@@ -0,0 +1,68 @@
+/*-
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ * Copyright (c) 2003 - Garance Alistair Drosehn <gad@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.
+ *
+ * The views and conclusions contained in the software and documentation
+ * are those of the authors and should not be interpreted as representing
+ * official policies, either expressed or implied, of the FreeBSD Project.
+ *
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ * $FreeBSD$
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ */
+
+#include <sys/cdefs.h>
+#include <time.h>
+
+#define PTM_PARSE_ISO8601 0x0001 /* Parse ISO-standard format */
+#define PTM_PARSE_DWM 0x0002 /* Parse Day-Week-Month format */
+#define PTM_PARSE_MATCHDOM 0x0004 /* If the user specifies a day-of-month,
+ * then the result should be a month
+ * which actually has that day. Eg:
+ * the user requests "day 31" when
+ * the present month is February. */
+
+struct ptime_data;
+
+/* Some global variables from newsyslog.c which might be of interest */
+extern int dbg_at_times; /* cmdline debugging option */
+extern int noaction; /* command-line option */
+extern int verbose; /* command-line option */
+extern struct ptime_data *dbg_timenow;
+
+__BEGIN_DECLS
+struct ptime_data *ptime_init(const struct ptime_data *_optsrc);
+int ptime_adjust4dst(struct ptime_data *_ptime, const struct
+ ptime_data *_dstsrc);
+int ptime_free(struct ptime_data *_ptime);
+int ptime_relparse(struct ptime_data *_ptime, int _parseopts,
+ time_t _basetime, const char *_str);
+const char *ptimeget_ctime(const struct ptime_data *_ptime);
+double ptimeget_diff(const struct ptime_data *_minuend,
+ const struct ptime_data *_subtrahend);
+time_t ptimeget_secs(const struct ptime_data *_ptime);
+int ptimeset_nxtime(struct ptime_data *_ptime);
+int ptimeset_time(struct ptime_data *_ptime, time_t _secs);
+__END_DECLS
diff --git a/usr.sbin/newsyslog/newsyslog.8 b/usr.sbin/newsyslog/newsyslog.8
new file mode 100644
index 0000000..3db563f
--- /dev/null
+++ b/usr.sbin/newsyslog/newsyslog.8
@@ -0,0 +1,257 @@
+.\" This file contains changes from the Open Software Foundation.
+.\"
+.\" from: @(#)newsyslog.8
+.\" $FreeBSD$
+.\"
+.\" Copyright 1988, 1989 by the Massachusetts Institute of Technology
+.\"
+.\" Permission to use, copy, modify, and distribute this software
+.\" and its documentation for any purpose and without fee is
+.\" hereby granted, provided that the above copyright notice
+.\" appear in all copies and that both that copyright notice and
+.\" this permission notice appear in supporting documentation,
+.\" and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
+.\" used in advertising or publicity pertaining to distribution
+.\" of the software without specific, written prior permission.
+.\" M.I.T. and the M.I.T. S.I.P.B. make no representations about
+.\" the suitability of this software for any purpose. It is
+.\" provided "as is" without express or implied warranty.
+.\"
+.Dd February 24, 2005
+.Dt NEWSYSLOG 8
+.Os
+.Sh NAME
+.Nm newsyslog
+.Nd maintain system log files to manageable sizes
+.Sh SYNOPSIS
+.Nm
+.Op Fl CFNnrsv
+.Op Fl R Ar tagname
+.Op Fl a Ar directory
+.Op Fl d Ar directory
+.Op Fl f Ar config_file
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility should be scheduled to run periodically by
+.Xr cron 8 .
+When it is executed it archives log files if necessary.
+If a log file
+is determined to require archiving,
+.Nm
+rearranges the files so that
+.Dq Va logfile
+is empty,
+.Dq Va logfile Ns Li \&.0
+has
+the last period's logs in it,
+.Dq Va logfile Ns Li \&.1
+has the next to last
+period's logs in it, and so on, up to a user-specified number of
+archived logs.
+Optionally the archived logs can be compressed to save
+space.
+.Pp
+A log can be archived for three reasons:
+.Bl -enum -offset indent
+.It
+It is larger than the configured size (in kilobytes).
+.It
+A configured number of hours have elapsed since the log was last
+archived.
+.It
+This is the specific configured hour for rotation of the log.
+.El
+.Pp
+The granularity of
+.Nm
+is dependent on how often it is scheduled to run by
+.Xr cron 8 .
+Since the program is quite fast, it may be scheduled to run every hour
+without any ill effects,
+and mode three (above) assumes that this is so.
+.Sh OPTIONS
+The following options can be used with
+.Nm :
+.Bl -tag -width indent
+.It Fl f Ar config_file
+Instruct
+.Nm
+to use
+.Ar config_file
+instead of
+.Pa /etc/newsyslog.conf
+for its configuration file.
+.It Fl a Ar directory
+Specify a
+.Ar directory
+into which archived log files will be written.
+If a relative path is given,
+it is appended to the path of each log file
+and the resulting path is used as the directory
+into which the archived log for that log file will be written.
+If an absolute path is given,
+all archived logs are written into the given
+.Ar directory .
+If any component of the path
+.Ar directory
+does not exist,
+it will be created when
+.Nm
+is run.
+.It Fl d Ar directory
+Specify a
+.Ar directory
+which all log files will be relative to.
+To allow archiving of logs outside the root, the
+.Ar directory
+passed to the
+.Fl a
+option is unaffected.
+.It Fl v
+Place
+.Nm
+in verbose mode.
+In this mode it will print out each log and its
+reasons for either trimming that log or skipping it.
+.It Fl n
+Cause
+.Nm
+not to trim the logs, but to print out what it would do if this option
+were not specified.
+.It Fl r
+Remove the restriction that
+.Nm
+must be running as root.
+Of course,
+.Nm
+will not be able to send a HUP signal to
+.Xr syslogd 8
+so this option should only be used in debugging.
+.It Fl s
+Specify that
+.Nm
+should not send any signals to any daemon processes that it would
+normally signal when rotating a log file.
+For any log file which is rotated, this option will usually also
+mean the rotated log file will not be compressed if there is a
+daemon which would have been signalled without this option.
+However, this option is most likely to be useful when specified
+with the
+.Fl R
+option, and in that case the compression will be done.
+.It Fl C
+If specified once, then
+.Nm
+will create any log files which do not exist, and which have the
+.Sy C
+flag specified in their config file entry.
+If specified multiple times, then
+.Nm
+will create all log files which do not already exist.
+If log files are given on the command-line, then the
+.Fl C
+or
+.Fl CC
+will only apply to those specific log files.
+.It Fl F
+Force
+.Nm
+to trim the logs, even if the trim conditions have not been met.
+This
+option is useful for diagnosing system problems by providing you with
+fresh logs that contain only the problems.
+.It Fl N
+Do not perform any rotations.
+This option is intended to be used with the
+.Fl C
+or
+.Fl CC
+options when creating log files is the only objective.
+.It Fl R Ar tagname
+Specify that
+.Nm
+should rotate a given list of files, even if trim conditions are not
+met for those files.
+The
+.Ar tagname
+is only used in the messages written to the log files which are
+rotated.
+This differs from the
+.Fl F
+option in that one or more log files must also be specified, so that
+.Nm
+will only operate on those specific files.
+This option is mainly intended for the daemons or programs which write
+some log files, and want to trigger a rotate based on their own criteria.
+With this option they can execute
+.Nm
+to trigger the rotate when they want it to happen, and still give the
+system administrator a way to specify the rules of rotation (such as how
+many backup copies are kept, and what kind of compression is done).
+When a daemon does execute
+.Nm
+with the
+.Fl R
+option, it should make sure all of the log files are closed before
+calling
+.Nm ,
+and then it should re-open the files after
+.Nm
+returns.
+Usually the calling process will also want to specify the
+.Fl s
+option, so
+.Nm
+will not send a signal to the very process which called it to force
+the rotate.
+Skipping the signal step will also mean that
+.Nm
+will return faster, since
+.Nm
+normally waits a few seconds after any signal that is sent.
+.El
+.Pp
+If additional command line arguments are given,
+.Nm
+will only examine log files that match those arguments; otherwise, it
+will examine all files listed in the configuration file.
+.Sh FILES
+.Bl -tag -width /etc/newsyslog.confxxxx -compact
+.It Pa /etc/newsyslog.conf
+.Nm
+configuration file
+.El
+.Sh COMPATIBILITY
+Previous versions of the
+.Nm
+utility used the dot (``.'') character to
+distinguish the group name.
+Beginning with
+.Fx 3.3 ,
+this has been changed to a colon (``:'') character so that user and group
+names may contain the dot character.
+The dot (``.'') character is still
+accepted for backwards compatibility.
+.Sh HISTORY
+The
+.Nm
+utility originated from
+.Nx
+and first appeared in
+.Fx 2.2 .
+.Sh AUTHORS
+.An Theodore Ts'o ,
+MIT Project Athena
+.Pp
+Copyright 1987, Massachusetts Institute of Technology
+.Sh "SEE ALSO"
+.Xr bzip2 1 ,
+.Xr gzip 1 ,
+.Xr syslog 3 ,
+.Xr newsyslog.conf 5 ,
+.Xr chown 8 ,
+.Xr syslogd 8
+.Sh BUGS
+Does not yet automatically read the logs to find security breaches.
diff --git a/usr.sbin/newsyslog/newsyslog.c b/usr.sbin/newsyslog/newsyslog.c
new file mode 100644
index 0000000..ef74f23
--- /dev/null
+++ b/usr.sbin/newsyslog/newsyslog.c
@@ -0,0 +1,2172 @@
+/*-
+ * ------+---------+---------+-------- + --------+---------+---------+---------*
+ * This file includes significant modifications done by:
+ * Copyright (c) 2003, 2004 - Garance Alistair Drosehn <gad@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * ------+---------+---------+-------- + --------+---------+---------+---------*
+ */
+
+/*
+ * This file contains changes from the Open Software Foundation.
+ */
+
+/*
+ * Copyright 1988, 1989 by the Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
+ * used in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. M.I.T. and the M.I.T.
+ * S.I.P.B. make no representations about the suitability of this software
+ * for any purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ */
+
+/*
+ * newsyslog - roll over selected logs at the appropriate time, keeping the a
+ * specified number of backup files around.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#define OSF
+#ifndef COMPRESS_POSTFIX
+#define COMPRESS_POSTFIX ".gz"
+#endif
+#ifndef BZCOMPRESS_POSTFIX
+#define BZCOMPRESS_POSTFIX ".bz2"
+#endif
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <glob.h>
+#include <grp.h>
+#include <paths.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "pathnames.h"
+#include "extern.h"
+
+/*
+ * Bit-values for the 'flags' parsed from a config-file entry.
+ */
+#define CE_COMPACT 0x0001 /* Compact the archived log files with gzip. */
+#define CE_BZCOMPACT 0x0002 /* Compact the archived log files with bzip2. */
+#define CE_BINARY 0x0008 /* Logfile is in binary, do not add status */
+ /* messages to logfile(s) when rotating. */
+#define CE_NOSIGNAL 0x0010 /* There is no process to signal when */
+ /* trimming this file. */
+#define CE_TRIMAT 0x0020 /* trim file at a specific time. */
+#define CE_GLOB 0x0040 /* name of the log is file name pattern. */
+#define CE_SIGNALGROUP 0x0080 /* Signal a process-group instead of a single */
+ /* process when trimming this file. */
+#define CE_CREATE 0x0100 /* Create the log file if it does not exist. */
+#define CE_NODUMP 0x0200 /* Set 'nodump' on newly created log file. */
+
+#define MIN_PID 5 /* Don't touch pids lower than this */
+#define MAX_PID 99999 /* was lower, see /usr/include/sys/proc.h */
+
+#define kbytes(size) (((size) + 1023) >> 10)
+
+#define DEFAULT_MARKER "<default>"
+#define DEBUG_MARKER "<debug>"
+
+struct conf_entry {
+ char *log; /* Name of the log */
+ char *pid_file; /* PID file */
+ char *r_reason; /* The reason this file is being rotated */
+ int firstcreate; /* Creating log for the first time (-C). */
+ int rotate; /* Non-zero if this file should be rotated */
+ int fsize; /* size found for the log file */
+ uid_t uid; /* Owner of log */
+ gid_t gid; /* Group of log */
+ int numlogs; /* Number of logs to keep */
+ int trsize; /* Size cutoff to trigger trimming the log */
+ int hours; /* Hours between log trimming */
+ struct ptime_data *trim_at; /* Specific time to do trimming */
+ unsigned int permissions; /* File permissions on the log */
+ int flags; /* CE_COMPACT, CE_BZCOMPACT, CE_BINARY */
+ int sig; /* Signal to send */
+ int def_cfg; /* Using the <default> rule for this file */
+ struct conf_entry *next;/* Linked list pointer */
+};
+
+struct sigwork_entry {
+ SLIST_ENTRY(sigwork_entry) sw_nextp;
+ int sw_signum; /* the signal to send */
+ int sw_pidok; /* true if pid value is valid */
+ pid_t sw_pid; /* the process id from the PID file */
+ const char *sw_pidtype; /* "daemon" or "process group" */
+ char sw_fname[1]; /* file the PID was read from */
+};
+
+struct zipwork_entry {
+ SLIST_ENTRY(zipwork_entry) zw_nextp;
+ const struct conf_entry *zw_conf; /* for chown/perm/flag info */
+ const struct sigwork_entry *zw_swork; /* to know success of signal */
+ int zw_fsize; /* size of the file to compress */
+ char zw_fname[1]; /* the file to compress */
+};
+
+typedef enum {
+ FREE_ENT, KEEP_ENT
+} fk_entry;
+
+SLIST_HEAD(swlisthead, sigwork_entry) swhead = SLIST_HEAD_INITIALIZER(swhead);
+SLIST_HEAD(zwlisthead, zipwork_entry) zwhead = SLIST_HEAD_INITIALIZER(zwhead);
+
+int dbg_at_times; /* -D Show details of 'trim_at' code */
+
+int archtodir = 0; /* Archive old logfiles to other directory */
+int createlogs; /* Create (non-GLOB) logfiles which do not */
+ /* already exist. 1=='for entries with */
+ /* C flag', 2=='for all entries'. */
+int verbose = 0; /* Print out what's going on */
+int needroot = 1; /* Root privs are necessary */
+int noaction = 0; /* Don't do anything, just show it */
+int norotate = 0; /* Don't rotate */
+int nosignal; /* Do not send any signals */
+int force = 0; /* Force the trim no matter what */
+int rotatereq = 0; /* -R = Always rotate the file(s) as given */
+ /* on the command (this also requires */
+ /* that a list of files *are* given on */
+ /* the run command). */
+char *requestor; /* The name given on a -R request */
+char *archdirname; /* Directory path to old logfiles archive */
+char *destdir = NULL; /* Directory to treat at root for logs */
+const char *conf; /* Configuration file to use */
+
+struct ptime_data *dbg_timenow; /* A "timenow" value set via -D option */
+struct ptime_data *timenow; /* The time to use for checking at-fields */
+
+#define DAYTIME_LEN 16
+char daytime[DAYTIME_LEN]; /* The current time in human readable form,
+ * used for rotation-tracking messages. */
+char hostname[MAXHOSTNAMELEN]; /* hostname */
+
+static struct conf_entry *get_worklist(char **files);
+static void parse_file(FILE *cf, const char *cfname, struct conf_entry **work_p,
+ struct conf_entry **glob_p, struct conf_entry **defconf_p);
+static char *sob(char *p);
+static char *son(char *p);
+static int isnumberstr(const char *);
+static char *missing_field(char *p, char *errline);
+static void change_attrs(const char *, const struct conf_entry *);
+static fk_entry do_entry(struct conf_entry *);
+static fk_entry do_rotate(const struct conf_entry *);
+static void do_sigwork(struct sigwork_entry *);
+static void do_zipwork(struct zipwork_entry *);
+static struct sigwork_entry *
+ save_sigwork(const struct conf_entry *);
+static struct zipwork_entry *
+ save_zipwork(const struct conf_entry *, const struct
+ sigwork_entry *, int, const char *);
+static void set_swpid(struct sigwork_entry *, const struct conf_entry *);
+static int sizefile(const char *);
+static void expand_globs(struct conf_entry **work_p,
+ struct conf_entry **glob_p);
+static void free_clist(struct conf_entry **firstent);
+static void free_entry(struct conf_entry *ent);
+static struct conf_entry *init_entry(const char *fname,
+ struct conf_entry *src_entry);
+static void parse_args(int argc, char **argv);
+static int parse_doption(const char *doption);
+static void usage(void);
+static int log_trim(const char *logname, const struct conf_entry *log_ent);
+static int age_old_log(char *file);
+static void savelog(char *from, char *to);
+static void createdir(const struct conf_entry *ent, char *dirpart);
+static void createlog(const struct conf_entry *ent);
+
+/*
+ * All the following take a parameter of 'int', but expect values in the
+ * range of unsigned char. Define wrappers which take values of type 'char',
+ * whether signed or unsigned, and ensure they end up in the right range.
+ */
+#define isdigitch(Anychar) isdigit((u_char)(Anychar))
+#define isprintch(Anychar) isprint((u_char)(Anychar))
+#define isspacech(Anychar) isspace((u_char)(Anychar))
+#define tolowerch(Anychar) tolower((u_char)(Anychar))
+
+int
+main(int argc, char **argv)
+{
+ fk_entry free_or_keep;
+ struct conf_entry *p, *q;
+ struct sigwork_entry *stmp;
+ struct zipwork_entry *ztmp;
+
+ SLIST_INIT(&swhead);
+ SLIST_INIT(&zwhead);
+
+ parse_args(argc, argv);
+ argc -= optind;
+ argv += optind;
+
+ if (needroot && getuid() && geteuid())
+ errx(1, "must have root privs");
+ p = q = get_worklist(argv);
+
+ /*
+ * Rotate all the files which need to be rotated. Note that
+ * some users have *hundreds* of entries in newsyslog.conf!
+ */
+ while (p) {
+ free_or_keep = do_entry(p);
+ p = p->next;
+ if (free_or_keep == FREE_ENT)
+ free_entry(q);
+ q = p;
+ }
+
+ /*
+ * Send signals to any processes which need a signal to tell
+ * them to close and re-open the log file(s) we have rotated.
+ * Note that zipwork_entries include pointers to these
+ * sigwork_entry's, so we can not free the entries here.
+ */
+ if (!SLIST_EMPTY(&swhead)) {
+ if (noaction || verbose)
+ printf("Signal all daemon process(es)...\n");
+ SLIST_FOREACH(stmp, &swhead, sw_nextp)
+ do_sigwork(stmp);
+ if (noaction)
+ printf("\tsleep 10\n");
+ else {
+ if (verbose)
+ printf("Pause 10 seconds to allow daemon(s)"
+ " to close log file(s)\n");
+ sleep(10);
+ }
+ }
+ /*
+ * Compress all files that we're expected to compress, now
+ * that all processes should have closed the files which
+ * have been rotated.
+ */
+ if (!SLIST_EMPTY(&zwhead)) {
+ if (noaction || verbose)
+ printf("Compress all rotated log file(s)...\n");
+ while (!SLIST_EMPTY(&zwhead)) {
+ ztmp = SLIST_FIRST(&zwhead);
+ do_zipwork(ztmp);
+ SLIST_REMOVE_HEAD(&zwhead, zw_nextp);
+ free(ztmp);
+ }
+ }
+ /* Now free all the sigwork entries. */
+ while (!SLIST_EMPTY(&swhead)) {
+ stmp = SLIST_FIRST(&swhead);
+ SLIST_REMOVE_HEAD(&swhead, sw_nextp);
+ free(stmp);
+ }
+
+ while (wait(NULL) > 0 || errno == EINTR)
+ ;
+ return (0);
+}
+
+static struct conf_entry *
+init_entry(const char *fname, struct conf_entry *src_entry)
+{
+ struct conf_entry *tempwork;
+
+ if (verbose > 4)
+ printf("\t--> [creating entry for %s]\n", fname);
+
+ tempwork = malloc(sizeof(struct conf_entry));
+ if (tempwork == NULL)
+ err(1, "malloc of conf_entry for %s", fname);
+
+ if (destdir == NULL || fname[0] != '/')
+ tempwork->log = strdup(fname);
+ else
+ asprintf(&tempwork->log, "%s%s", destdir, fname);
+ if (tempwork->log == NULL)
+ err(1, "strdup for %s", fname);
+
+ if (src_entry != NULL) {
+ tempwork->pid_file = NULL;
+ if (src_entry->pid_file)
+ tempwork->pid_file = strdup(src_entry->pid_file);
+ tempwork->r_reason = NULL;
+ tempwork->firstcreate = 0;
+ tempwork->rotate = 0;
+ tempwork->fsize = -1;
+ tempwork->uid = src_entry->uid;
+ tempwork->gid = src_entry->gid;
+ tempwork->numlogs = src_entry->numlogs;
+ tempwork->trsize = src_entry->trsize;
+ tempwork->hours = src_entry->hours;
+ tempwork->trim_at = NULL;
+ if (src_entry->trim_at != NULL)
+ tempwork->trim_at = ptime_init(src_entry->trim_at);
+ tempwork->permissions = src_entry->permissions;
+ tempwork->flags = src_entry->flags;
+ tempwork->sig = src_entry->sig;
+ tempwork->def_cfg = src_entry->def_cfg;
+ } else {
+ /* Initialize as a "do-nothing" entry */
+ tempwork->pid_file = NULL;
+ tempwork->r_reason = NULL;
+ tempwork->firstcreate = 0;
+ tempwork->rotate = 0;
+ tempwork->fsize = -1;
+ tempwork->uid = (uid_t)-1;
+ tempwork->gid = (gid_t)-1;
+ tempwork->numlogs = 1;
+ tempwork->trsize = -1;
+ tempwork->hours = -1;
+ tempwork->trim_at = NULL;
+ tempwork->permissions = 0;
+ tempwork->flags = 0;
+ tempwork->sig = SIGHUP;
+ tempwork->def_cfg = 0;
+ }
+ tempwork->next = NULL;
+
+ return (tempwork);
+}
+
+static void
+free_entry(struct conf_entry *ent)
+{
+
+ if (ent == NULL)
+ return;
+
+ if (ent->log != NULL) {
+ if (verbose > 4)
+ printf("\t--> [freeing entry for %s]\n", ent->log);
+ free(ent->log);
+ ent->log = NULL;
+ }
+
+ if (ent->pid_file != NULL) {
+ free(ent->pid_file);
+ ent->pid_file = NULL;
+ }
+
+ if (ent->r_reason != NULL) {
+ free(ent->r_reason);
+ ent->r_reason = NULL;
+ }
+
+ if (ent->trim_at != NULL) {
+ ptime_free(ent->trim_at);
+ ent->trim_at = NULL;
+ }
+
+ free(ent);
+}
+
+static void
+free_clist(struct conf_entry **firstent)
+{
+ struct conf_entry *ent, *nextent;
+
+ if (firstent == NULL)
+ return; /* There is nothing to do. */
+
+ ent = *firstent;
+ firstent = NULL;
+
+ while (ent) {
+ nextent = ent->next;
+ free_entry(ent);
+ ent = nextent;
+ }
+}
+
+static fk_entry
+do_entry(struct conf_entry * ent)
+{
+#define REASON_MAX 80
+ int modtime;
+ fk_entry free_or_keep;
+ double diffsecs;
+ char temp_reason[REASON_MAX];
+
+ free_or_keep = FREE_ENT;
+ if (verbose) {
+ if (ent->flags & CE_COMPACT)
+ printf("%s <%dZ>: ", ent->log, ent->numlogs);
+ else if (ent->flags & CE_BZCOMPACT)
+ printf("%s <%dJ>: ", ent->log, ent->numlogs);
+ else
+ printf("%s <%d>: ", ent->log, ent->numlogs);
+ }
+ ent->fsize = sizefile(ent->log);
+ modtime = age_old_log(ent->log);
+ ent->rotate = 0;
+ ent->firstcreate = 0;
+ if (ent->fsize < 0) {
+ /*
+ * If either the C flag or the -C option was specified,
+ * and if we won't be creating the file, then have the
+ * verbose message include a hint as to why the file
+ * will not be created.
+ */
+ temp_reason[0] = '\0';
+ if (createlogs > 1)
+ ent->firstcreate = 1;
+ else if ((ent->flags & CE_CREATE) && createlogs)
+ ent->firstcreate = 1;
+ else if (ent->flags & CE_CREATE)
+ strlcpy(temp_reason, " (no -C option)", REASON_MAX);
+ else if (createlogs)
+ strlcpy(temp_reason, " (no C flag)", REASON_MAX);
+
+ if (ent->firstcreate) {
+ if (verbose)
+ printf("does not exist -> will create.\n");
+ createlog(ent);
+ } else if (verbose) {
+ printf("does not exist, skipped%s.\n", temp_reason);
+ }
+ } else {
+ if (ent->flags & CE_TRIMAT && !force && !rotatereq) {
+ diffsecs = ptimeget_diff(timenow, ent->trim_at);
+ if (diffsecs < 0.0) {
+ /* trim_at is some time in the future. */
+ if (verbose) {
+ ptime_adjust4dst(ent->trim_at,
+ timenow);
+ printf("--> will trim at %s",
+ ptimeget_ctime(ent->trim_at));
+ }
+ return (free_or_keep);
+ } else if (diffsecs >= 3600.0) {
+ /*
+ * trim_at is more than an hour in the past,
+ * so find the next valid trim_at time, and
+ * tell the user what that will be.
+ */
+ if (verbose && dbg_at_times)
+ printf("\n\t--> prev trim at %s\t",
+ ptimeget_ctime(ent->trim_at));
+ if (verbose) {
+ ptimeset_nxtime(ent->trim_at);
+ printf("--> will trim at %s",
+ ptimeget_ctime(ent->trim_at));
+ }
+ return (free_or_keep);
+ } else if (verbose && noaction && dbg_at_times) {
+ /*
+ * If we are just debugging at-times, then
+ * a detailed message is helpful. Also
+ * skip "doing" any commands, since they
+ * would all be turned off by no-action.
+ */
+ printf("\n\t--> timematch at %s",
+ ptimeget_ctime(ent->trim_at));
+ return (free_or_keep);
+ } else if (verbose && ent->hours <= 0) {
+ printf("--> time is up\n");
+ }
+ }
+ if (verbose && (ent->trsize > 0))
+ printf("size (Kb): %d [%d] ", ent->fsize, ent->trsize);
+ if (verbose && (ent->hours > 0))
+ printf(" age (hr): %d [%d] ", modtime, ent->hours);
+
+ /*
+ * Figure out if this logfile needs to be rotated.
+ */
+ temp_reason[0] = '\0';
+ if (rotatereq) {
+ ent->rotate = 1;
+ snprintf(temp_reason, REASON_MAX, " due to -R from %s",
+ requestor);
+ } else if (force) {
+ ent->rotate = 1;
+ snprintf(temp_reason, REASON_MAX, " due to -F request");
+ } else if ((ent->trsize > 0) && (ent->fsize >= ent->trsize)) {
+ ent->rotate = 1;
+ snprintf(temp_reason, REASON_MAX, " due to size>%dK",
+ ent->trsize);
+ } else if (ent->hours <= 0 && (ent->flags & CE_TRIMAT)) {
+ ent->rotate = 1;
+ } else if ((ent->hours > 0) && ((modtime >= ent->hours) ||
+ (modtime < 0))) {
+ ent->rotate = 1;
+ }
+
+ /*
+ * If the file needs to be rotated, then rotate it.
+ */
+ if (ent->rotate && !norotate) {
+ if (temp_reason[0] != '\0')
+ ent->r_reason = strdup(temp_reason);
+ if (verbose)
+ printf("--> trimming log....\n");
+ if (noaction && !verbose) {
+ if (ent->flags & CE_COMPACT)
+ printf("%s <%dZ>: trimming\n",
+ ent->log, ent->numlogs);
+ else if (ent->flags & CE_BZCOMPACT)
+ printf("%s <%dJ>: trimming\n",
+ ent->log, ent->numlogs);
+ else
+ printf("%s <%d>: trimming\n",
+ ent->log, ent->numlogs);
+ }
+ free_or_keep = do_rotate(ent);
+ } else {
+ if (verbose)
+ printf("--> skipping\n");
+ }
+ }
+ return (free_or_keep);
+#undef REASON_MAX
+}
+
+static void
+parse_args(int argc, char **argv)
+{
+ int ch;
+ char *p;
+
+ timenow = ptime_init(NULL);
+ ptimeset_time(timenow, time(NULL));
+ strlcpy(daytime, ptimeget_ctime(timenow) + 4, DAYTIME_LEN);
+
+ /* Let's get our hostname */
+ (void)gethostname(hostname, sizeof(hostname));
+
+ /* Truncate domain */
+ if ((p = strchr(hostname, '.')) != NULL)
+ *p = '\0';
+
+ /* Parse command line options. */
+ while ((ch = getopt(argc, argv, "a:d:f:nrsvCD:FNR:")) != -1)
+ switch (ch) {
+ case 'a':
+ archtodir++;
+ archdirname = optarg;
+ break;
+ case 'd':
+ destdir = optarg;
+ break;
+ case 'f':
+ conf = optarg;
+ break;
+ case 'n':
+ noaction++;
+ break;
+ case 'r':
+ needroot = 0;
+ break;
+ case 's':
+ nosignal = 1;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'C':
+ /* Useful for things like rc.diskless... */
+ createlogs++;
+ break;
+ case 'D':
+ /*
+ * Set some debugging option. The specific option
+ * depends on the value of optarg. These options
+ * may come and go without notice or documentation.
+ */
+ if (parse_doption(optarg))
+ break;
+ usage();
+ /* NOTREACHED */
+ case 'F':
+ force++;
+ break;
+ case 'N':
+ norotate++;
+ break;
+ case 'R':
+ rotatereq++;
+ requestor = strdup(optarg);
+ break;
+ case 'm': /* Used by OpenBSD for "monitor mode" */
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+
+ if (force && norotate) {
+ warnx("Only one of -F and -N may be specified.");
+ usage();
+ /* NOTREACHED */
+ }
+
+ if (rotatereq) {
+ if (optind == argc) {
+ warnx("At least one filename must be given when -R is specified.");
+ usage();
+ /* NOTREACHED */
+ }
+ /* Make sure "requestor" value is safe for a syslog message. */
+ for (p = requestor; *p != '\0'; p++) {
+ if (!isprintch(*p) && (*p != '\t'))
+ *p = '.';
+ }
+ }
+
+ if (dbg_timenow) {
+ /*
+ * Note that the 'daytime' variable is not changed.
+ * That is only used in messages that track when a
+ * logfile is rotated, and if a file *is* rotated,
+ * then it will still rotated at the "real now" time.
+ */
+ ptime_free(timenow);
+ timenow = dbg_timenow;
+ fprintf(stderr, "Debug: Running as if TimeNow is %s",
+ ptimeget_ctime(dbg_timenow));
+ }
+
+}
+
+/*
+ * These debugging options are mainly meant for developer use, such
+ * as writing regression-tests. They would not be needed by users
+ * during normal operation of newsyslog...
+ */
+static int
+parse_doption(const char *doption)
+{
+ const char TN[] = "TN=";
+ int res;
+
+ if (strncmp(doption, TN, sizeof(TN) - 1) == 0) {
+ /*
+ * The "TimeNow" debugging option. This might be off
+ * by an hour when crossing a timezone change.
+ */
+ dbg_timenow = ptime_init(NULL);
+ res = ptime_relparse(dbg_timenow, PTM_PARSE_ISO8601,
+ time(NULL), doption + sizeof(TN) - 1);
+ if (res == -2) {
+ warnx("Non-existent time specified on -D %s", doption);
+ return (0); /* failure */
+ } else if (res < 0) {
+ warnx("Malformed time given on -D %s", doption);
+ return (0); /* failure */
+ }
+ return (1); /* successfully parsed */
+
+ }
+
+ if (strcmp(doption, "ats") == 0) {
+ dbg_at_times++;
+ return (1); /* successfully parsed */
+ }
+
+ /* XXX - This check could probably be dropped. */
+ if ((strcmp(doption, "neworder") == 0) || (strcmp(doption, "oldorder")
+ == 0)) {
+ warnx("NOTE: newsyslog always uses 'neworder'.");
+ return (1); /* successfully parsed */
+ }
+
+ warnx("Unknown -D (debug) option: '%s'", doption);
+ return (0); /* failure */
+}
+
+static void
+usage(void)
+{
+
+ fprintf(stderr,
+ "usage: newsyslog [-CFNnrsv] [-a directory] [-d directory] [-f config-file]\n"
+ " [ [-R requestor] filename ... ]\n");
+ exit(1);
+}
+
+/*
+ * Parse a configuration file and return a linked list of all the logs
+ * which should be processed.
+ */
+static struct conf_entry *
+get_worklist(char **files)
+{
+ FILE *f;
+ const char *fname;
+ char **given;
+ struct conf_entry *defconf, *dupent, *ent, *firstnew;
+ struct conf_entry *globlist, *lastnew, *worklist;
+ int gmatch, fnres;
+
+ defconf = globlist = worklist = NULL;
+
+ fname = conf;
+ if (fname == NULL)
+ fname = _PATH_CONF;
+
+ if (strcmp(fname, "-") != 0)
+ f = fopen(fname, "r");
+ else {
+ f = stdin;
+ fname = "<stdin>";
+ }
+ if (!f)
+ err(1, "%s", fname);
+
+ parse_file(f, fname, &worklist, &globlist, &defconf);
+ (void) fclose(f);
+
+ /*
+ * All config-file information has been read in and turned into
+ * a worklist and a globlist. If there were no specific files
+ * given on the run command, then the only thing left to do is to
+ * call a routine which finds all files matched by the globlist
+ * and adds them to the worklist. Then return the worklist.
+ */
+ if (*files == NULL) {
+ expand_globs(&worklist, &globlist);
+ free_clist(&globlist);
+ if (defconf != NULL)
+ free_entry(defconf);
+ return (worklist);
+ /* NOTREACHED */
+ }
+
+ /*
+ * If newsyslog was given a specific list of files to process,
+ * it may be that some of those files were not listed in any
+ * config file. Those unlisted files should get the default
+ * rotation action. First, create the default-rotation action
+ * if none was found in a system config file.
+ */
+ if (defconf == NULL) {
+ defconf = init_entry(DEFAULT_MARKER, NULL);
+ defconf->numlogs = 3;
+ defconf->trsize = 50;
+ defconf->permissions = S_IRUSR|S_IWUSR;
+ }
+
+ /*
+ * If newsyslog was run with a list of specific filenames,
+ * then create a new worklist which has only those files in
+ * it, picking up the rotation-rules for those files from
+ * the original worklist.
+ *
+ * XXX - Note that this will copy multiple rules for a single
+ * logfile, if multiple entries are an exact match for
+ * that file. That matches the historic behavior, but do
+ * we want to continue to allow it? If so, it should
+ * probably be handled more intelligently.
+ */
+ firstnew = lastnew = NULL;
+ for (given = files; *given; ++given) {
+ /*
+ * First try to find exact-matches for this given file.
+ */
+ gmatch = 0;
+ for (ent = worklist; ent; ent = ent->next) {
+ if (strcmp(ent->log, *given) == 0) {
+ gmatch++;
+ dupent = init_entry(*given, ent);
+ if (!firstnew)
+ firstnew = dupent;
+ else
+ lastnew->next = dupent;
+ lastnew = dupent;
+ }
+ }
+ if (gmatch) {
+ if (verbose > 2)
+ printf("\t+ Matched entry %s\n", *given);
+ continue;
+ }
+
+ /*
+ * There was no exact-match for this given file, so look
+ * for a "glob" entry which does match.
+ */
+ gmatch = 0;
+ if (verbose > 2 && globlist != NULL)
+ printf("\t+ Checking globs for %s\n", *given);
+ for (ent = globlist; ent; ent = ent->next) {
+ fnres = fnmatch(ent->log, *given, FNM_PATHNAME);
+ if (verbose > 2)
+ printf("\t+ = %d for pattern %s\n", fnres,
+ ent->log);
+ if (fnres == 0) {
+ gmatch++;
+ dupent = init_entry(*given, ent);
+ if (!firstnew)
+ firstnew = dupent;
+ else
+ lastnew->next = dupent;
+ lastnew = dupent;
+ /* This new entry is not a glob! */
+ dupent->flags &= ~CE_GLOB;
+ /* Only allow a match to one glob-entry */
+ break;
+ }
+ }
+ if (gmatch) {
+ if (verbose > 2)
+ printf("\t+ Matched %s via %s\n", *given,
+ ent->log);
+ continue;
+ }
+
+ /*
+ * This given file was not found in any config file, so
+ * add a worklist item based on the default entry.
+ */
+ if (verbose > 2)
+ printf("\t+ No entry matched %s (will use %s)\n",
+ *given, DEFAULT_MARKER);
+ dupent = init_entry(*given, defconf);
+ if (!firstnew)
+ firstnew = dupent;
+ else
+ lastnew->next = dupent;
+ /* Mark that it was *not* found in a config file */
+ dupent->def_cfg = 1;
+ lastnew = dupent;
+ }
+
+ /*
+ * Free all the entries in the original work list, the list of
+ * glob entries, and the default entry.
+ */
+ free_clist(&worklist);
+ free_clist(&globlist);
+ free_entry(defconf);
+
+ /* And finally, return a worklist which matches the given files. */
+ return (firstnew);
+}
+
+/*
+ * Expand the list of entries with filename patterns, and add all files
+ * which match those glob-entries onto the worklist.
+ */
+static void
+expand_globs(struct conf_entry **work_p, struct conf_entry **glob_p)
+{
+ int gmatch, gres;
+ size_t i;
+ char *mfname;
+ struct conf_entry *dupent, *ent, *firstmatch, *globent;
+ struct conf_entry *lastmatch;
+ glob_t pglob;
+ struct stat st_fm;
+
+ if ((glob_p == NULL) || (*glob_p == NULL))
+ return; /* There is nothing to do. */
+
+ /*
+ * The worklist contains all fully-specified (non-GLOB) names.
+ *
+ * Now expand the list of filename-pattern (GLOB) entries into
+ * a second list, which (by definition) will only match files
+ * that already exist. Do not add a glob-related entry for any
+ * file which already exists in the fully-specified list.
+ */
+ firstmatch = lastmatch = NULL;
+ for (globent = *glob_p; globent; globent = globent->next) {
+
+ gres = glob(globent->log, GLOB_NOCHECK, NULL, &pglob);
+ if (gres != 0) {
+ warn("cannot expand pattern (%d): %s", gres,
+ globent->log);
+ continue;
+ }
+
+ if (verbose > 2)
+ printf("\t+ Expanding pattern %s\n", globent->log);
+ for (i = 0; i < pglob.gl_matchc; i++) {
+ mfname = pglob.gl_pathv[i];
+
+ /* See if this file already has a specific entry. */
+ gmatch = 0;
+ for (ent = *work_p; ent; ent = ent->next) {
+ if (strcmp(mfname, ent->log) == 0) {
+ gmatch++;
+ break;
+ }
+ }
+ if (gmatch)
+ continue;
+
+ /* Make sure the named matched is a file. */
+ gres = lstat(mfname, &st_fm);
+ if (gres != 0) {
+ /* Error on a file that glob() matched?!? */
+ warn("Skipping %s - lstat() error", mfname);
+ continue;
+ }
+ if (!S_ISREG(st_fm.st_mode)) {
+ /* We only rotate files! */
+ if (verbose > 2)
+ printf("\t+ . skipping %s (!file)\n",
+ mfname);
+ continue;
+ }
+
+ if (verbose > 2)
+ printf("\t+ . add file %s\n", mfname);
+ dupent = init_entry(mfname, globent);
+ if (!firstmatch)
+ firstmatch = dupent;
+ else
+ lastmatch->next = dupent;
+ lastmatch = dupent;
+ /* This new entry is not a glob! */
+ dupent->flags &= ~CE_GLOB;
+ }
+ globfree(&pglob);
+ if (verbose > 2)
+ printf("\t+ Done with pattern %s\n", globent->log);
+ }
+
+ /* Add the list of matched files to the end of the worklist. */
+ if (!*work_p)
+ *work_p = firstmatch;
+ else {
+ ent = *work_p;
+ while (ent->next)
+ ent = ent->next;
+ ent->next = firstmatch;
+ }
+
+}
+
+/*
+ * Parse a configuration file and update a linked list of all the logs to
+ * process.
+ */
+static void
+parse_file(FILE *cf, const char *cfname, struct conf_entry **work_p,
+ struct conf_entry **glob_p, struct conf_entry **defconf_p)
+{
+ char line[BUFSIZ], *parse, *q;
+ char *cp, *errline, *group;
+ struct conf_entry *lastglob, *lastwork, *working;
+ struct passwd *pwd;
+ struct group *grp;
+ int eol, ptm_opts, res, special;
+
+ /*
+ * XXX - for now, assume that only one config file will be read,
+ * ie, this routine is only called one time.
+ */
+ lastglob = lastwork = NULL;
+
+ errline = NULL;
+ while (fgets(line, BUFSIZ, cf)) {
+ if ((line[0] == '\n') || (line[0] == '#') ||
+ (strlen(line) == 0))
+ continue;
+ if (errline != NULL)
+ free(errline);
+ errline = strdup(line);
+ for (cp = line + 1; *cp != '\0'; cp++) {
+ if (*cp != '#')
+ continue;
+ if (*(cp - 1) == '\\') {
+ strcpy(cp - 1, cp);
+ cp--;
+ continue;
+ }
+ *cp = '\0';
+ break;
+ }
+
+ q = parse = missing_field(sob(line), errline);
+ parse = son(line);
+ if (!*parse)
+ errx(1, "malformed line (missing fields):\n%s",
+ errline);
+ *parse = '\0';
+
+ /*
+ * Allow people to set debug options via the config file.
+ * (NOTE: debug optons are undocumented, and may disappear
+ * at any time, etc).
+ */
+ if (strcasecmp(DEBUG_MARKER, q) == 0) {
+ q = parse = missing_field(sob(++parse), errline);
+ parse = son(parse);
+ if (!*parse)
+ warnx("debug line specifies no option:\n%s",
+ errline);
+ else {
+ *parse = '\0';
+ parse_doption(q);
+ }
+ continue;
+ }
+
+ special = 0;
+ working = init_entry(q, NULL);
+ if (strcasecmp(DEFAULT_MARKER, q) == 0) {
+ special = 1;
+ if (defconf_p == NULL) {
+ warnx("Ignoring entry for %s in %s!", q,
+ cfname);
+ free_entry(working);
+ continue;
+ } else if (*defconf_p != NULL) {
+ warnx("Ignoring duplicate entry for %s!", q);
+ free_entry(working);
+ continue;
+ }
+ *defconf_p = working;
+ }
+
+ q = parse = missing_field(sob(++parse), errline);
+ parse = son(parse);
+ if (!*parse)
+ errx(1, "malformed line (missing fields):\n%s",
+ errline);
+ *parse = '\0';
+ if ((group = strchr(q, ':')) != NULL ||
+ (group = strrchr(q, '.')) != NULL) {
+ *group++ = '\0';
+ if (*q) {
+ if (!(isnumberstr(q))) {
+ if ((pwd = getpwnam(q)) == NULL)
+ errx(1,
+ "error in config file; unknown user:\n%s",
+ errline);
+ working->uid = pwd->pw_uid;
+ } else
+ working->uid = atoi(q);
+ } else
+ working->uid = (uid_t)-1;
+
+ q = group;
+ if (*q) {
+ if (!(isnumberstr(q))) {
+ if ((grp = getgrnam(q)) == NULL)
+ errx(1,
+ "error in config file; unknown group:\n%s",
+ errline);
+ working->gid = grp->gr_gid;
+ } else
+ working->gid = atoi(q);
+ } else
+ working->gid = (gid_t)-1;
+
+ q = parse = missing_field(sob(++parse), errline);
+ parse = son(parse);
+ if (!*parse)
+ errx(1, "malformed line (missing fields):\n%s",
+ errline);
+ *parse = '\0';
+ } else {
+ working->uid = (uid_t)-1;
+ working->gid = (gid_t)-1;
+ }
+
+ if (!sscanf(q, "%o", &working->permissions))
+ errx(1, "error in config file; bad permissions:\n%s",
+ errline);
+
+ q = parse = missing_field(sob(++parse), errline);
+ parse = son(parse);
+ if (!*parse)
+ errx(1, "malformed line (missing fields):\n%s",
+ errline);
+ *parse = '\0';
+ if (!sscanf(q, "%d", &working->numlogs) || working->numlogs < 0)
+ errx(1, "error in config file; bad value for count of logs to save:\n%s",
+ errline);
+
+ q = parse = missing_field(sob(++parse), errline);
+ parse = son(parse);
+ if (!*parse)
+ errx(1, "malformed line (missing fields):\n%s",
+ errline);
+ *parse = '\0';
+ if (isdigitch(*q))
+ working->trsize = atoi(q);
+ else if (strcmp(q, "*") == 0)
+ working->trsize = -1;
+ else {
+ warnx("Invalid value of '%s' for 'size' in line:\n%s",
+ q, errline);
+ working->trsize = -1;
+ }
+
+ working->flags = 0;
+ q = parse = missing_field(sob(++parse), errline);
+ parse = son(parse);
+ eol = !*parse;
+ *parse = '\0';
+ {
+ char *ep;
+ u_long ul;
+
+ ul = strtoul(q, &ep, 10);
+ if (ep == q)
+ working->hours = 0;
+ else if (*ep == '*')
+ working->hours = -1;
+ else if (ul > INT_MAX)
+ errx(1, "interval is too large:\n%s", errline);
+ else
+ working->hours = ul;
+
+ if (*ep == '\0' || strcmp(ep, "*") == 0)
+ goto no_trimat;
+ if (*ep != '@' && *ep != '$')
+ errx(1, "malformed interval/at:\n%s", errline);
+
+ working->flags |= CE_TRIMAT;
+ working->trim_at = ptime_init(NULL);
+ ptm_opts = PTM_PARSE_ISO8601;
+ if (*ep == '$')
+ ptm_opts = PTM_PARSE_DWM;
+ ptm_opts |= PTM_PARSE_MATCHDOM;
+ res = ptime_relparse(working->trim_at, ptm_opts,
+ ptimeget_secs(timenow), ep + 1);
+ if (res == -2)
+ errx(1, "nonexistent time for 'at' value:\n%s",
+ errline);
+ else if (res < 0)
+ errx(1, "malformed 'at' value:\n%s", errline);
+ }
+no_trimat:
+
+ if (eol)
+ q = NULL;
+ else {
+ q = parse = sob(++parse); /* Optional field */
+ parse = son(parse);
+ if (!*parse)
+ eol = 1;
+ *parse = '\0';
+ }
+
+ for (; q && *q && !isspacech(*q); q++) {
+ switch (tolowerch(*q)) {
+ case 'b':
+ working->flags |= CE_BINARY;
+ break;
+ case 'c':
+ /*
+ * XXX - Ick! Ugly! Remove ASAP!
+ * We want `c' and `C' for "create". But we
+ * will temporarily treat `c' as `g', because
+ * FreeBSD releases <= 4.8 have a typo of
+ * checking ('G' || 'c') for CE_GLOB.
+ */
+ if (*q == 'c') {
+ warnx("Assuming 'g' for 'c' in flags for line:\n%s",
+ errline);
+ warnx("The 'c' flag will eventually mean 'CREATE'");
+ working->flags |= CE_GLOB;
+ break;
+ }
+ working->flags |= CE_CREATE;
+ break;
+ case 'd':
+ working->flags |= CE_NODUMP;
+ break;
+ case 'g':
+ working->flags |= CE_GLOB;
+ break;
+ case 'j':
+ working->flags |= CE_BZCOMPACT;
+ break;
+ case 'n':
+ working->flags |= CE_NOSIGNAL;
+ break;
+ case 'u':
+ working->flags |= CE_SIGNALGROUP;
+ break;
+ case 'w':
+ /* Depreciated flag - keep for compatibility purposes */
+ break;
+ case 'z':
+ working->flags |= CE_COMPACT;
+ break;
+ case '-':
+ break;
+ case 'f': /* Used by OpenBSD for "CE_FOLLOW" */
+ case 'm': /* Used by OpenBSD for "CE_MONITOR" */
+ case 'p': /* Used by NetBSD for "CE_PLAIN0" */
+ default:
+ errx(1, "illegal flag in config file -- %c",
+ *q);
+ }
+ }
+
+ if (eol)
+ q = NULL;
+ else {
+ q = parse = sob(++parse); /* Optional field */
+ parse = son(parse);
+ if (!*parse)
+ eol = 1;
+ *parse = '\0';
+ }
+
+ working->pid_file = NULL;
+ if (q && *q) {
+ if (*q == '/')
+ working->pid_file = strdup(q);
+ else if (isdigit(*q))
+ goto got_sig;
+ else
+ errx(1,
+ "illegal pid file or signal number in config file:\n%s",
+ errline);
+ }
+ if (eol)
+ q = NULL;
+ else {
+ q = parse = sob(++parse); /* Optional field */
+ *(parse = son(parse)) = '\0';
+ }
+
+ working->sig = SIGHUP;
+ if (q && *q) {
+ if (isdigit(*q)) {
+ got_sig:
+ working->sig = atoi(q);
+ } else {
+ err_sig:
+ errx(1,
+ "illegal signal number in config file:\n%s",
+ errline);
+ }
+ if (working->sig < 1 || working->sig >= NSIG)
+ goto err_sig;
+ }
+
+ /*
+ * Finish figuring out what pid-file to use (if any) in
+ * later processing if this logfile needs to be rotated.
+ */
+ if ((working->flags & CE_NOSIGNAL) == CE_NOSIGNAL) {
+ /*
+ * This config-entry specified 'n' for nosignal,
+ * see if it also specified an explicit pid_file.
+ * This would be a pretty pointless combination.
+ */
+ if (working->pid_file != NULL) {
+ warnx("Ignoring '%s' because flag 'n' was specified in line:\n%s",
+ working->pid_file, errline);
+ free(working->pid_file);
+ working->pid_file = NULL;
+ }
+ } else if (working->pid_file == NULL) {
+ /*
+ * This entry did not specify the 'n' flag, which
+ * means it should signal syslogd unless it had
+ * specified some other pid-file (and obviously the
+ * syslog pid-file will not be for a process-group).
+ * Also, we should only try to notify syslog if we
+ * are root.
+ */
+ if (working->flags & CE_SIGNALGROUP) {
+ warnx("Ignoring flag 'U' in line:\n%s",
+ errline);
+ working->flags &= ~CE_SIGNALGROUP;
+ }
+ if (needroot)
+ working->pid_file = strdup(_PATH_SYSLOGPID);
+ }
+
+ /*
+ * Add this entry to the appropriate list of entries, unless
+ * it was some kind of special entry (eg: <default>).
+ */
+ if (special) {
+ ; /* Do not add to any list */
+ } else if (working->flags & CE_GLOB) {
+ if (!*glob_p)
+ *glob_p = working;
+ else
+ lastglob->next = working;
+ lastglob = working;
+ } else {
+ if (!*work_p)
+ *work_p = working;
+ else
+ lastwork->next = working;
+ lastwork = working;
+ }
+ }
+ if (errline != NULL)
+ free(errline);
+}
+
+static char *
+missing_field(char *p, char *errline)
+{
+
+ if (!p || !*p)
+ errx(1, "missing field in config file:\n%s", errline);
+ return (p);
+}
+
+static fk_entry
+do_rotate(const struct conf_entry *ent)
+{
+ char dirpart[MAXPATHLEN], namepart[MAXPATHLEN];
+ char file1[MAXPATHLEN], file2[MAXPATHLEN];
+ char zfile1[MAXPATHLEN], zfile2[MAXPATHLEN];
+ char jfile1[MAXPATHLEN];
+ int flags, numlogs_c;
+ fk_entry free_or_keep;
+ struct sigwork_entry *swork;
+ struct stat st;
+
+ flags = ent->flags;
+ free_or_keep = FREE_ENT;
+
+ if (archtodir) {
+ char *p;
+
+ /* build complete name of archive directory into dirpart */
+ if (*archdirname == '/') { /* absolute */
+ strlcpy(dirpart, archdirname, sizeof(dirpart));
+ } else { /* relative */
+ /* get directory part of logfile */
+ strlcpy(dirpart, ent->log, sizeof(dirpart));
+ if ((p = rindex(dirpart, '/')) == NULL)
+ dirpart[0] = '\0';
+ else
+ *(p + 1) = '\0';
+ strlcat(dirpart, archdirname, sizeof(dirpart));
+ }
+
+ /* check if archive directory exists, if not, create it */
+ if (lstat(dirpart, &st))
+ createdir(ent, dirpart);
+
+ /* get filename part of logfile */
+ if ((p = rindex(ent->log, '/')) == NULL)
+ strlcpy(namepart, ent->log, sizeof(namepart));
+ else
+ strlcpy(namepart, p + 1, sizeof(namepart));
+
+ /* name of oldest log */
+ (void) snprintf(file1, sizeof(file1), "%s/%s.%d", dirpart,
+ namepart, ent->numlogs);
+ (void) snprintf(zfile1, sizeof(zfile1), "%s%s", file1,
+ COMPRESS_POSTFIX);
+ snprintf(jfile1, sizeof(jfile1), "%s%s", file1,
+ BZCOMPRESS_POSTFIX);
+ } else {
+ /* name of oldest log */
+ (void) snprintf(file1, sizeof(file1), "%s.%d", ent->log,
+ ent->numlogs);
+ (void) snprintf(zfile1, sizeof(zfile1), "%s%s", file1,
+ COMPRESS_POSTFIX);
+ snprintf(jfile1, sizeof(jfile1), "%s%s", file1,
+ BZCOMPRESS_POSTFIX);
+ }
+
+ if (noaction) {
+ printf("\trm -f %s\n", file1);
+ printf("\trm -f %s\n", zfile1);
+ printf("\trm -f %s\n", jfile1);
+ } else {
+ (void) unlink(file1);
+ (void) unlink(zfile1);
+ (void) unlink(jfile1);
+ }
+
+ /* Move down log files */
+ numlogs_c = ent->numlogs; /* copy for countdown */
+ while (numlogs_c--) {
+
+ (void) strlcpy(file2, file1, sizeof(file2));
+
+ if (archtodir)
+ (void) snprintf(file1, sizeof(file1), "%s/%s.%d",
+ dirpart, namepart, numlogs_c);
+ else
+ (void) snprintf(file1, sizeof(file1), "%s.%d",
+ ent->log, numlogs_c);
+
+ (void) strlcpy(zfile1, file1, sizeof(zfile1));
+ (void) strlcpy(zfile2, file2, sizeof(zfile2));
+ if (lstat(file1, &st)) {
+ (void) strlcat(zfile1, COMPRESS_POSTFIX,
+ sizeof(zfile1));
+ (void) strlcat(zfile2, COMPRESS_POSTFIX,
+ sizeof(zfile2));
+ if (lstat(zfile1, &st)) {
+ strlcpy(zfile1, file1, sizeof(zfile1));
+ strlcpy(zfile2, file2, sizeof(zfile2));
+ strlcat(zfile1, BZCOMPRESS_POSTFIX,
+ sizeof(zfile1));
+ strlcat(zfile2, BZCOMPRESS_POSTFIX,
+ sizeof(zfile2));
+ if (lstat(zfile1, &st))
+ continue;
+ }
+ }
+ if (noaction)
+ printf("\tmv %s %s\n", zfile1, zfile2);
+ else {
+ /* XXX - Ought to be checking for failure! */
+ (void)rename(zfile1, zfile2);
+ }
+ change_attrs(zfile2, ent);
+ }
+
+ if (ent->numlogs > 0) {
+ if (noaction) {
+ /*
+ * Note that savelog() may succeed with using link()
+ * for the archtodir case, but there is no good way
+ * of knowing if it will when doing "noaction", so
+ * here we claim that it will have to do a copy...
+ */
+ if (archtodir)
+ printf("\tcp %s %s\n", ent->log, file1);
+ else
+ printf("\tln %s %s\n", ent->log, file1);
+ } else {
+ if (!(flags & CE_BINARY)) {
+ /* Report the trimming to the old log */
+ log_trim(ent->log, ent);
+ }
+ savelog(ent->log, file1);
+ }
+ change_attrs(file1, ent);
+ }
+
+ /* Create the new log file and move it into place */
+ if (noaction)
+ printf("Start new log...\n");
+ createlog(ent);
+
+ /*
+ * Save all signalling and file-compression to be done after log
+ * files from all entries have been rotated. This way any one
+ * process will not be sent the same signal multiple times when
+ * multiple log files had to be rotated.
+ */
+ swork = NULL;
+ if (ent->pid_file != NULL)
+ swork = save_sigwork(ent);
+ if (ent->numlogs > 0 && (flags & (CE_COMPACT | CE_BZCOMPACT))) {
+ /*
+ * The zipwork_entry will include a pointer to this
+ * conf_entry, so the conf_entry should not be freed.
+ */
+ free_or_keep = KEEP_ENT;
+ save_zipwork(ent, swork, ent->fsize, file1);
+ }
+
+ return (free_or_keep);
+}
+
+static void
+do_sigwork(struct sigwork_entry *swork)
+{
+ struct sigwork_entry *nextsig;
+ int kres, secs;
+
+ if (!(swork->sw_pidok) || swork->sw_pid == 0)
+ return; /* no work to do... */
+
+ /*
+ * If nosignal (-s) was specified, then do not signal any process.
+ * Note that a nosignal request triggers a warning message if the
+ * rotated logfile needs to be compressed, *unless* -R was also
+ * specified. We assume that an `-sR' request came from a process
+ * which writes to the logfile, and as such, we assume that process
+ * has already made sure the logfile is not presently in use. This
+ * just sets swork->sw_pidok to a special value, and do_zipwork
+ * will print any necessary warning(s).
+ */
+ if (nosignal) {
+ if (!rotatereq)
+ swork->sw_pidok = -1;
+ return;
+ }
+
+ /*
+ * Compute the pause between consecutive signals. Use a longer
+ * sleep time if we will be sending two signals to the same
+ * deamon or process-group.
+ */
+ secs = 0;
+ nextsig = SLIST_NEXT(swork, sw_nextp);
+ if (nextsig != NULL) {
+ if (swork->sw_pid == nextsig->sw_pid)
+ secs = 10;
+ else
+ secs = 1;
+ }
+
+ if (noaction) {
+ printf("\tkill -%d %d \t\t# %s\n", swork->sw_signum,
+ (int)swork->sw_pid, swork->sw_fname);
+ if (secs > 0)
+ printf("\tsleep %d\n", secs);
+ return;
+ }
+
+ kres = kill(swork->sw_pid, swork->sw_signum);
+ if (kres != 0) {
+ /*
+ * Assume that "no such process" (ESRCH) is something
+ * to warn about, but is not an error. Presumably the
+ * process which writes to the rotated log file(s) is
+ * gone, in which case we should have no problem with
+ * compressing the rotated log file(s).
+ */
+ if (errno != ESRCH)
+ swork->sw_pidok = 0;
+ warn("can't notify %s, pid %d", swork->sw_pidtype,
+ (int)swork->sw_pid);
+ } else {
+ if (verbose)
+ printf("Notified %s pid %d = %s\n", swork->sw_pidtype,
+ (int)swork->sw_pid, swork->sw_fname);
+ if (secs > 0) {
+ if (verbose)
+ printf("Pause %d second(s) between signals\n",
+ secs);
+ sleep(secs);
+ }
+ }
+}
+
+static void
+do_zipwork(struct zipwork_entry *zwork)
+{
+ const char *pgm_name, *pgm_path;
+ int errsav, fcount, zstatus;
+ pid_t pidzip, wpid;
+ char zresult[MAXPATHLEN];
+
+ pgm_path = NULL;
+ strlcpy(zresult, zwork->zw_fname, sizeof(zresult));
+ if (zwork != NULL && zwork->zw_conf != NULL) {
+ if (zwork->zw_conf->flags & CE_COMPACT) {
+ pgm_path = _PATH_GZIP;
+ strlcat(zresult, COMPRESS_POSTFIX, sizeof(zresult));
+ } else if (zwork->zw_conf->flags & CE_BZCOMPACT) {
+ pgm_path = _PATH_BZIP2;
+ strlcat(zresult, BZCOMPRESS_POSTFIX, sizeof(zresult));
+ }
+ }
+ if (pgm_path == NULL) {
+ warnx("invalid entry for %s in do_zipwork", zwork->zw_fname);
+ return;
+ }
+ pgm_name = strrchr(pgm_path, '/');
+ if (pgm_name == NULL)
+ pgm_name = pgm_path;
+ else
+ pgm_name++;
+
+ if (zwork->zw_swork != NULL && zwork->zw_swork->sw_pidok <= 0) {
+ warnx(
+ "log %s not compressed because daemon(s) not notified",
+ zwork->zw_fname);
+ change_attrs(zwork->zw_fname, zwork->zw_conf);
+ return;
+ }
+
+ if (noaction) {
+ printf("\t%s %s\n", pgm_name, zwork->zw_fname);
+ change_attrs(zresult, zwork->zw_conf);
+ return;
+ }
+
+ fcount = 1;
+ pidzip = fork();
+ while (pidzip < 0) {
+ /*
+ * The fork failed. If the failure was due to a temporary
+ * problem, then wait a short time and try it again.
+ */
+ errsav = errno;
+ warn("fork() for `%s %s'", pgm_name, zwork->zw_fname);
+ if (errsav != EAGAIN || fcount > 5)
+ errx(1, "Exiting...");
+ sleep(fcount * 12);
+ fcount++;
+ pidzip = fork();
+ }
+ if (!pidzip) {
+ /* The child process executes the compression command */
+ execl(pgm_path, pgm_path, "-f", zwork->zw_fname, (char *)0);
+ err(1, "execl(`%s -f %s')", pgm_path, zwork->zw_fname);
+ }
+
+ wpid = waitpid(pidzip, &zstatus, 0);
+ if (wpid == -1) {
+ /* XXX - should this be a fatal error? */
+ warn("%s: waitpid(%d)", pgm_path, pidzip);
+ return;
+ }
+ if (!WIFEXITED(zstatus)) {
+ warnx("`%s -f %s' did not terminate normally", pgm_name,
+ zwork->zw_fname);
+ return;
+ }
+ if (WEXITSTATUS(zstatus)) {
+ warnx("`%s -f %s' terminated with a non-zero status (%d)",
+ pgm_name, zwork->zw_fname, WEXITSTATUS(zstatus));
+ return;
+ }
+
+ /* Compression was successful, set file attributes on the result. */
+ change_attrs(zresult, zwork->zw_conf);
+}
+
+/*
+ * Save information on any process we need to signal. Any single
+ * process may need to be sent different signal-values for different
+ * log files, but usually a single signal-value will cause the process
+ * to close and re-open all of it's log files.
+ */
+static struct sigwork_entry *
+save_sigwork(const struct conf_entry *ent)
+{
+ struct sigwork_entry *sprev, *stmp;
+ int ndiff;
+ size_t tmpsiz;
+
+ sprev = NULL;
+ ndiff = 1;
+ SLIST_FOREACH(stmp, &swhead, sw_nextp) {
+ ndiff = strcmp(ent->pid_file, stmp->sw_fname);
+ if (ndiff > 0)
+ break;
+ if (ndiff == 0) {
+ if (ent->sig == stmp->sw_signum)
+ break;
+ if (ent->sig > stmp->sw_signum) {
+ ndiff = 1;
+ break;
+ }
+ }
+ sprev = stmp;
+ }
+ if (stmp != NULL && ndiff == 0)
+ return (stmp);
+
+ tmpsiz = sizeof(struct sigwork_entry) + strlen(ent->pid_file) + 1;
+ stmp = malloc(tmpsiz);
+ set_swpid(stmp, ent);
+ stmp->sw_signum = ent->sig;
+ strcpy(stmp->sw_fname, ent->pid_file);
+ if (sprev == NULL)
+ SLIST_INSERT_HEAD(&swhead, stmp, sw_nextp);
+ else
+ SLIST_INSERT_AFTER(sprev, stmp, sw_nextp);
+ return (stmp);
+}
+
+/*
+ * Save information on any file we need to compress. We may see the same
+ * file multiple times, so check the full list to avoid duplicates. The
+ * list itself is sorted smallest-to-largest, because that's the order we
+ * want to compress the files. If the partition is very low on disk space,
+ * then the smallest files are the most likely to compress, and compressing
+ * them first will free up more space for the larger files.
+ */
+static struct zipwork_entry *
+save_zipwork(const struct conf_entry *ent, const struct sigwork_entry *swork,
+ int zsize, const char *zipfname)
+{
+ struct zipwork_entry *zprev, *ztmp;
+ int ndiff;
+ size_t tmpsiz;
+
+ /* Compute the size if the caller did not know it. */
+ if (zsize < 0)
+ zsize = sizefile(zipfname);
+
+ zprev = NULL;
+ ndiff = 1;
+ SLIST_FOREACH(ztmp, &zwhead, zw_nextp) {
+ ndiff = strcmp(zipfname, ztmp->zw_fname);
+ if (ndiff == 0)
+ break;
+ if (zsize > ztmp->zw_fsize)
+ zprev = ztmp;
+ }
+ if (ztmp != NULL && ndiff == 0)
+ return (ztmp);
+
+ tmpsiz = sizeof(struct zipwork_entry) + strlen(zipfname) + 1;
+ ztmp = malloc(tmpsiz);
+ ztmp->zw_conf = ent;
+ ztmp->zw_swork = swork;
+ ztmp->zw_fsize = zsize;
+ strcpy(ztmp->zw_fname, zipfname);
+ if (zprev == NULL)
+ SLIST_INSERT_HEAD(&zwhead, ztmp, zw_nextp);
+ else
+ SLIST_INSERT_AFTER(zprev, ztmp, zw_nextp);
+ return (ztmp);
+}
+
+/* Send a signal to the pid specified by pidfile */
+static void
+set_swpid(struct sigwork_entry *swork, const struct conf_entry *ent)
+{
+ FILE *f;
+ long minok, maxok, rval;
+ char *endp, *linep, line[BUFSIZ];
+
+ minok = MIN_PID;
+ maxok = MAX_PID;
+ swork->sw_pidok = 0;
+ swork->sw_pid = 0;
+ swork->sw_pidtype = "daemon";
+ if (ent->flags & CE_SIGNALGROUP) {
+ /*
+ * If we are expected to signal a process-group when
+ * rotating this logfile, then the value read in should
+ * be the negative of a valid process ID.
+ */
+ minok = -MAX_PID;
+ maxok = -MIN_PID;
+ swork->sw_pidtype = "process-group";
+ }
+
+ f = fopen(ent->pid_file, "r");
+ if (f == NULL) {
+ warn("can't open pid file: %s", ent->pid_file);
+ return;
+ }
+
+ if (fgets(line, BUFSIZ, f) == NULL) {
+ /*
+ * Warn if the PID file is empty, but do not consider
+ * it an error. Most likely it means the process has
+ * has terminated, so it should be safe to rotate any
+ * log files that the process would have been using.
+ */
+ if (feof(f)) {
+ swork->sw_pidok = 1;
+ warnx("pid file is empty: %s", ent->pid_file);
+ } else
+ warn("can't read from pid file: %s", ent->pid_file);
+ (void)fclose(f);
+ return;
+ }
+ (void)fclose(f);
+
+ errno = 0;
+ linep = line;
+ while (*linep == ' ')
+ linep++;
+ rval = strtol(linep, &endp, 10);
+ if (*endp != '\0' && !isspacech(*endp)) {
+ warnx("pid file does not start with a valid number: %s",
+ ent->pid_file);
+ } else if (rval < minok || rval > maxok) {
+ warnx("bad value '%ld' for process number in %s",
+ rval, ent->pid_file);
+ if (verbose)
+ warnx("\t(expecting value between %ld and %ld)",
+ minok, maxok);
+ } else {
+ swork->sw_pidok = 1;
+ swork->sw_pid = rval;
+ }
+
+ return;
+}
+
+/* Log the fact that the logs were turned over */
+static int
+log_trim(const char *logname, const struct conf_entry *log_ent)
+{
+ FILE *f;
+ const char *xtra;
+
+ if ((f = fopen(logname, "a")) == NULL)
+ return (-1);
+ xtra = "";
+ if (log_ent->def_cfg)
+ xtra = " using <default> rule";
+ if (log_ent->firstcreate)
+ fprintf(f, "%s %s newsyslog[%d]: logfile first created%s\n",
+ daytime, hostname, (int) getpid(), xtra);
+ else if (log_ent->r_reason != NULL)
+ fprintf(f, "%s %s newsyslog[%d]: logfile turned over%s%s\n",
+ daytime, hostname, (int) getpid(), log_ent->r_reason, xtra);
+ else
+ fprintf(f, "%s %s newsyslog[%d]: logfile turned over%s\n",
+ daytime, hostname, (int) getpid(), xtra);
+ if (fclose(f) == EOF)
+ err(1, "log_trim: fclose");
+ return (0);
+}
+
+/* Return size in kilobytes of a file */
+static int
+sizefile(const char *file)
+{
+ struct stat sb;
+
+ if (stat(file, &sb) < 0)
+ return (-1);
+ return (kbytes(dbtob(sb.st_blocks)));
+}
+
+/* Return the age of old log file (file.0) */
+static int
+age_old_log(char *file)
+{
+ struct stat sb;
+ char *endp;
+ char tmp[MAXPATHLEN + sizeof(".0") + sizeof(COMPRESS_POSTFIX) +
+ sizeof(BZCOMPRESS_POSTFIX) + 1];
+
+ if (archtodir) {
+ char *p;
+
+ /* build name of archive directory into tmp */
+ if (*archdirname == '/') { /* absolute */
+ strlcpy(tmp, archdirname, sizeof(tmp));
+ } else { /* relative */
+ /* get directory part of logfile */
+ strlcpy(tmp, file, sizeof(tmp));
+ if ((p = rindex(tmp, '/')) == NULL)
+ tmp[0] = '\0';
+ else
+ *(p + 1) = '\0';
+ strlcat(tmp, archdirname, sizeof(tmp));
+ }
+
+ strlcat(tmp, "/", sizeof(tmp));
+
+ /* get filename part of logfile */
+ if ((p = rindex(file, '/')) == NULL)
+ strlcat(tmp, file, sizeof(tmp));
+ else
+ strlcat(tmp, p + 1, sizeof(tmp));
+ } else {
+ (void) strlcpy(tmp, file, sizeof(tmp));
+ }
+
+ strlcat(tmp, ".0", sizeof(tmp));
+ if (stat(tmp, &sb) < 0) {
+ /*
+ * A plain '.0' file does not exist. Try again, first
+ * with the added suffix of '.gz', then with an added
+ * suffix of '.bz2' instead of '.gz'.
+ */
+ endp = strchr(tmp, '\0');
+ strlcat(tmp, COMPRESS_POSTFIX, sizeof(tmp));
+ if (stat(tmp, &sb) < 0) {
+ *endp = '\0'; /* Remove .gz */
+ strlcat(tmp, BZCOMPRESS_POSTFIX, sizeof(tmp));
+ if (stat(tmp, &sb) < 0)
+ return (-1);
+ }
+ }
+ return ((int)(ptimeget_secs(timenow) - sb.st_mtime + 1800) / 3600);
+}
+
+/* Skip Over Blanks */
+static char *
+sob(char *p)
+{
+ while (p && *p && isspace(*p))
+ p++;
+ return (p);
+}
+
+/* Skip Over Non-Blanks */
+static char *
+son(char *p)
+{
+ while (p && *p && !isspace(*p))
+ p++;
+ return (p);
+}
+
+/* Check if string is actually a number */
+static int
+isnumberstr(const char *string)
+{
+ while (*string) {
+ if (!isdigitch(*string++))
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * Save the active log file under a new name. A link to the new name
+ * is the quick-and-easy way to do this. If that fails (which it will
+ * if the destination is on another partition), then make a copy of
+ * the file to the new location.
+ */
+static void
+savelog(char *from, char *to)
+{
+ FILE *src, *dst;
+ int c, res;
+
+ res = link(from, to);
+ if (res == 0)
+ return;
+
+ if ((src = fopen(from, "r")) == NULL)
+ err(1, "can't fopen %s for reading", from);
+ if ((dst = fopen(to, "w")) == NULL)
+ err(1, "can't fopen %s for writing", to);
+
+ while ((c = getc(src)) != EOF) {
+ if ((putc(c, dst)) == EOF)
+ err(1, "error writing to %s", to);
+ }
+
+ if (ferror(src))
+ err(1, "error reading from %s", from);
+ if ((fclose(src)) != 0)
+ err(1, "can't fclose %s", to);
+ if ((fclose(dst)) != 0)
+ err(1, "can't fclose %s", from);
+}
+
+/* create one or more directory components of a path */
+static void
+createdir(const struct conf_entry *ent, char *dirpart)
+{
+ int res;
+ char *s, *d;
+ char mkdirpath[MAXPATHLEN];
+ struct stat st;
+
+ s = dirpart;
+ d = mkdirpath;
+
+ for (;;) {
+ *d++ = *s++;
+ if (*s != '/' && *s != '\0')
+ continue;
+ *d = '\0';
+ res = lstat(mkdirpath, &st);
+ if (res != 0) {
+ if (noaction) {
+ printf("\tmkdir %s\n", mkdirpath);
+ } else {
+ res = mkdir(mkdirpath, 0755);
+ if (res != 0)
+ err(1, "Error on mkdir(\"%s\") for -a",
+ mkdirpath);
+ }
+ }
+ if (*s == '\0')
+ break;
+ }
+ if (verbose) {
+ if (ent->firstcreate)
+ printf("Created directory '%s' for new %s\n",
+ dirpart, ent->log);
+ else
+ printf("Created directory '%s' for -a\n", dirpart);
+ }
+}
+
+/*
+ * Create a new log file, destroying any currently-existing version
+ * of the log file in the process. If the caller wants a backup copy
+ * of the file to exist, they should call 'link(logfile,logbackup)'
+ * before calling this routine.
+ */
+void
+createlog(const struct conf_entry *ent)
+{
+ int fd, failed;
+ struct stat st;
+ char *realfile, *slash, tempfile[MAXPATHLEN];
+
+ fd = -1;
+ realfile = ent->log;
+
+ /*
+ * If this log file is being created for the first time (-C option),
+ * then it may also be true that the parent directory does not exist
+ * yet. Check, and create that directory if it is missing.
+ */
+ if (ent->firstcreate) {
+ strlcpy(tempfile, realfile, sizeof(tempfile));
+ slash = strrchr(tempfile, '/');
+ if (slash != NULL) {
+ *slash = '\0';
+ failed = stat(tempfile, &st);
+ if (failed && errno != ENOENT)
+ err(1, "Error on stat(%s)", tempfile);
+ if (failed)
+ createdir(ent, tempfile);
+ else if (!S_ISDIR(st.st_mode))
+ errx(1, "%s exists but is not a directory",
+ tempfile);
+ }
+ }
+
+ /*
+ * First create an unused filename, so it can be chown'ed and
+ * chmod'ed before it is moved into the real location. mkstemp
+ * will create the file mode=600 & owned by us. Note that all
+ * temp files will have a suffix of '.z<something>'.
+ */
+ strlcpy(tempfile, realfile, sizeof(tempfile));
+ strlcat(tempfile, ".zXXXXXX", sizeof(tempfile));
+ if (noaction)
+ printf("\tmktemp %s\n", tempfile);
+ else {
+ fd = mkstemp(tempfile);
+ if (fd < 0)
+ err(1, "can't mkstemp logfile %s", tempfile);
+
+ /*
+ * Add status message to what will become the new log file.
+ */
+ if (!(ent->flags & CE_BINARY)) {
+ if (log_trim(tempfile, ent))
+ err(1, "can't add status message to log");
+ }
+ }
+
+ /* Change the owner/group, if we are supposed to */
+ if (ent->uid != (uid_t)-1 || ent->gid != (gid_t)-1) {
+ if (noaction)
+ printf("\tchown %u:%u %s\n", ent->uid, ent->gid,
+ tempfile);
+ else {
+ failed = fchown(fd, ent->uid, ent->gid);
+ if (failed)
+ err(1, "can't fchown temp file %s", tempfile);
+ }
+ }
+
+ /* Turn on NODUMP if it was requested in the config-file. */
+ if (ent->flags & CE_NODUMP) {
+ if (noaction)
+ printf("\tchflags nodump %s\n", tempfile);
+ else {
+ failed = fchflags(fd, UF_NODUMP);
+ if (failed) {
+ warn("log_trim: fchflags(NODUMP)");
+ }
+ }
+ }
+
+ /*
+ * Note that if the real logfile still exists, and if the call
+ * to rename() fails, then "neither the old file nor the new
+ * file shall be changed or created" (to quote the standard).
+ * If the call succeeds, then the file will be replaced without
+ * any window where some other process might find that the file
+ * did not exist.
+ * XXX - ? It may be that for some error conditions, we could
+ * retry by first removing the realfile and then renaming.
+ */
+ if (noaction) {
+ printf("\tchmod %o %s\n", ent->permissions, tempfile);
+ printf("\tmv %s %s\n", tempfile, realfile);
+ } else {
+ failed = fchmod(fd, ent->permissions);
+ if (failed)
+ err(1, "can't fchmod temp file '%s'", tempfile);
+ failed = rename(tempfile, realfile);
+ if (failed)
+ err(1, "can't mv %s to %s", tempfile, realfile);
+ }
+
+ if (fd >= 0)
+ close(fd);
+}
+
+/*
+ * Change the attributes of a given filename to what was specified in
+ * the newsyslog.conf entry. This routine is only called for files
+ * that newsyslog expects that it has created, and thus it is a fatal
+ * error if this routine finds that the file does not exist.
+ */
+static void
+change_attrs(const char *fname, const struct conf_entry *ent)
+{
+ int failed;
+
+ if (noaction) {
+ printf("\tchmod %o %s\n", ent->permissions, fname);
+
+ if (ent->uid != (uid_t)-1 || ent->gid != (gid_t)-1)
+ printf("\tchown %u:%u %s\n",
+ ent->uid, ent->gid, fname);
+
+ if (ent->flags & CE_NODUMP)
+ printf("\tchflags nodump %s\n", fname);
+ return;
+ }
+
+ failed = chmod(fname, ent->permissions);
+ if (failed) {
+ if (errno != EPERM)
+ err(1, "chmod(%s) in change_attrs", fname);
+ warn("change_attrs couldn't chmod(%s)", fname);
+ }
+
+ if (ent->uid != (uid_t)-1 || ent->gid != (gid_t)-1) {
+ failed = chown(fname, ent->uid, ent->gid);
+ if (failed)
+ warn("can't chown %s", fname);
+ }
+
+ if (ent->flags & CE_NODUMP) {
+ failed = chflags(fname, UF_NODUMP);
+ if (failed)
+ warn("can't chflags %s NODUMP", fname);
+ }
+}
diff --git a/usr.sbin/newsyslog/newsyslog.conf.5 b/usr.sbin/newsyslog/newsyslog.conf.5
new file mode 100644
index 0000000..138adc0
--- /dev/null
+++ b/usr.sbin/newsyslog/newsyslog.conf.5
@@ -0,0 +1,347 @@
+.\" This file was split from the newsyslog(8) manual page by Tom Rhodes
+.\" and includes modifications as appropriate.
+.\" The original header is included below:
+.\"
+.\" This file contains changes from the Open Software Foundation.
+.\"
+.\" from: @(#)newsyslog.8
+.\" $FreeBSD$
+.\"
+.\" Copyright 1988, 1989 by the Massachusetts Institute of Technology
+.\"
+.\" Permission to use, copy, modify, and distribute this software
+.\" and its documentation for any purpose and without fee is
+.\" hereby granted, provided that the above copyright notice
+.\" appear in all copies and that both that copyright notice and
+.\" this permission notice appear in supporting documentation,
+.\" and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
+.\" used in advertising or publicity pertaining to distribution
+.\" of the software without specific, written prior permission.
+.\" M.I.T. and the M.I.T. S.I.P.B. make no representations about
+.\" the suitability of this software for any purpose. It is
+.\" provided "as is" without express or implied warranty.
+.\"
+.Dd November 27, 2006
+.Dt NEWSYSLOG.CONF 5
+.Os
+.Sh NAME
+.Nm newsyslog.conf
+.Nd
+.Xr newsyslog 8
+configuration file
+.Sh DESCRIPTION
+The
+.Nm
+file is used to set log file rotation configuration for the
+.Xr newsyslog 8
+utility.
+Configuration may designate that logs are rotated based on
+size, last rotation time, or time of day.
+The
+.Nm
+file can also be used to designate secure permissions to log
+files at rotation time.
+During initialization,
+.Xr newsyslog 8
+reads a configuration file,
+normally
+.Pa /etc/newsyslog.conf ,
+to determine which logs may potentially be rotated and archived.
+Each line has five mandatory fields and four optional fields,
+separated with whitespace.
+Blank lines or lines beginning with
+.Ql #
+are ignored.
+If
+.Ql #
+is placed in the middle of the line, the
+.Ql #
+character and the rest of the line after it is ignored.
+To prevent special meaning, the
+.Ql #
+character may be escaped with
+.Ql \e ;
+in this case preceding
+.Ql \e
+is removed and
+.Ql #
+is treated as an ordinary character.
+The fields of the configuration file are as follows:
+.Bl -tag -width indent
+.It Ar logfile_name
+Name of the system log file to be archived, or the literal string
+.Dq Aq Li default .
+The special default entry will only be used if a log file
+name is given as a command line argument to
+.Xr newsyslog 8 ,
+and if that log file name is not matched by any other
+line in the configuration file.
+.It Ar owner : Ns Ar group
+This optional field specifies the owner and group for the archive file.
+The
+.Ql \&:
+is essential regardless if the
+.Ar owner
+or
+.Ar group
+field is left blank or contains a value.
+The field may be numeric, or a name which is present in
+.Pa /etc/passwd
+or
+.Pa /etc/group .
+.It Ar mode
+Specify the file mode of the log file and archives.
+.It Ar count
+Specify the maximum number of archive files which may exist.
+This does not consider the current log file.
+.It Ar size
+When the size of the log file reaches
+.Ar size
+in kilobytes, the log file will be trimmed as described above.
+If this field contains an asterisk
+.Pq Ql * ,
+the log file will not be trimmed based on size.
+.It Ar when
+The
+.Ar when
+field may consist of an interval, a specific time, or both.
+If the
+.Ar when
+field contains an asterisk
+.Pq Ql * ,
+log rotation will solely depend on the contents of the
+.Ar size
+field.
+Otherwise, the
+.Ar when
+field consists of an optional interval in hours, usually followed
+by an
+.So Li \&@ Sc Ns No -sign
+and a time in restricted
+.Tn ISO 8601
+format.
+Additionally, the format may also be constructed with a
+.Ql $
+sign along with a rotation time specification of once
+a day, once a week, or once a month.
+.Pp
+If a time is specified, the log file will only be trimmed if
+.Xr newsyslog 8
+is run within one hour of the specified time.
+If an interval is specified, the log file will be trimmed if that many
+hours have passed since the last rotation.
+When both a time and an interval are
+specified then both conditions must be satisfied for the rotation to
+take place.
+.Pp
+There is no provision for the specification of a timezone.
+There is little point in specifying an explicit minutes or
+seconds component in the current implementation, since the only comparison is
+.Dq within the hour .
+.Pp
+.Sy ISO 8601 restricted time format :
+.Pp
+The lead-in character for a restricted
+.Tn ISO 8601
+time is an
+.Ql @
+sign.
+The particular format of the time in restricted
+.Tn ISO 8601
+is:
+.Sm off
+.Oo
+.Op Oo Oo Oo Va cc Oc Va yy Oc Va mm Oc Va dd
+.Oo
+.Li T
+.Op Va hh Oo Va mm Oo Va ss Oc Oc Oc
+.Oc .
+.Sm on
+Optional date fields default to the appropriate component of the
+current date; optional time fields default to midnight; hence if today
+is January 22, 1999, the following date specifications are all
+equivalent:
+.Pp
+.Bl -item -compact -offset indent
+.It
+.Sq Li 19990122T000000
+.It
+.Sq Li 990122T000000
+.It
+.Sq Li 0122T000000
+.It
+.Sq Li 22T000000
+.It
+.Sq Li T000000
+.It
+.Sq Li T0000
+.It
+.Sq Li T00
+.It
+.Sq Li 22T
+.It
+.Sq Li T
+.It
+.Sq Li \&
+.El
+.Pp
+.Sy Day, week, and month time format:
+.Pp
+The lead-in character for day, week, and month specification is a
+.Ql $
+sign.
+The particular format of day, week, and month specification is:
+.Op Li D Ns Va hh ,
+.Op Li W Ns Va w Ns Op Li D Ns Va hh ,
+and
+.Op Li M Ns Va dd Ns Op Li D Ns Va hh ,
+respectively.
+Optional time fields default to midnight.
+The ranges for day and hour specifications are:
+.Pp
+.Bl -tag -width indent -offset indent -compact
+.It Ar hh
+hours, range 0..23
+.It Ar w
+day of week, range 0..6, 0 = Sunday
+.It Ar dd
+day of month, range 1..31, or one of the letters
+.Ql L
+or
+.Ql l
+to specify the last day of the month.
+.El
+.Pp
+Some examples:
+.Pp
+.Bl -tag -width indent -offset indent -compact
+.It Li $D0
+rotate every night at midnight
+(same as
+.Li @T00 )
+.It Li $D23
+rotate every day at 23:00
+(same as
+.Li @T23 )
+.It Li $W0D23
+rotate every week on Sunday at 23:00
+.It Li $W5D16
+rotate every week on Friday at 16:00
+.It Li $M1D0
+rotate at the first day of every month at midnight
+(i.e., the start of the day; same as
+.Li @01T00 )
+.It Li $M5D6
+rotate on every 5th day of month at 6:00
+(same as
+.Li @05T06 )
+.El
+.It Ar flags
+This optional field is made up of one or more characters
+that specify any special processing to be done for the log
+files matched by this line.
+The following are valid flags:
+.Bl -tag -width indent
+.It Cm B
+indicates that the log file is a binary file, or has some
+special format.
+Usually
+.Xr newsyslog 8
+inserts an
+.Tn ASCII
+message into a log file during rotation.
+This message is used to indicate
+when, and sometimes why the log file was rotated.
+If
+.Cm B
+is specified, then that informational message will not be
+inserted into the log file.
+.It Cm C
+indicates that the log file should be created if it does not
+already exist, and if the
+.Fl C
+option was also specified on the command line.
+.It Cm D
+indicates that
+.Xr newsyslog 8
+should set the
+.Dv UF_NODUMP
+flag when creating a new version of
+this log file.
+This option would affect how the
+.Xr dump 8
+command treats the log file when making a file system backup.
+.It Cm G
+indicates that the specified
+.Ar logfile_name
+is a shell pattern, and that
+.Xr newsyslog 8
+should archive all filenames matching that pattern using the
+other options on this line.
+See
+.Xr glob 3
+for details on syntax and matching rules.
+.It Cm J
+indicates that
+.Xr newsyslog 8
+should attempt to save disk space by compressing the rotated
+log file using
+.Xr bzip2 1 .
+.It Cm N
+indicates that there is no process which needs to be signaled
+when this log file is rotated.
+.It Cm U
+indicates that the file specified by
+.Ar path_to_pid_file
+will contain the ID for a process group instead of a process.
+This option also requires that the first line in that file
+be a negative value to distinguish it from a process ID.
+.It Cm Z
+indicates that
+.Xr newsyslog 8
+should attempt to save disk space by compressing the rotated
+log file using
+.Xr gzip 1 .
+.It Fl
+a minus sign will not cause any special processing, but it
+can be used as a placeholder to create a
+.Ar flags
+field when you need to specify any of the following fields.
+.El
+.It Ar path_to_pid_file
+This optional field specifies the file name containing a daemon's
+process ID or to find a group process ID if the
+.Cm U
+flag was specified.
+If this field is present, a
+.Ar signal_number
+is sent the process ID contained in this file.
+If this field is not present, then a
+.Dv SIGHUP
+signal will be sent to
+.Xr syslogd 8 ,
+unless the
+.Cm N
+flag has been specified.
+This field must start with
+.Ql /
+in order to be recognized properly.
+.It Ar signal_number
+This optional field specifies the signal number that will be sent
+to the daemon process (or to all processes in a process group, if the
+.Cm U
+flag was specified).
+If this field is not present, then a
+.Dv SIGHUP
+signal will be sent.
+.El
+.Sh SEE ALSO
+.Xr bzip2 1 ,
+.Xr gzip 1 ,
+.Xr syslog 3 ,
+.Xr chown 8 ,
+.Xr newsyslog 8 ,
+.Xr syslogd 8
+.Sh HISTORY
+This manual page first appeared in
+.Fx 4.10 .
diff --git a/usr.sbin/newsyslog/pathnames.h b/usr.sbin/newsyslog/pathnames.h
new file mode 100644
index 0000000..3d2c6ff
--- /dev/null
+++ b/usr.sbin/newsyslog/pathnames.h
@@ -0,0 +1,28 @@
+/*
+ * This file contains changes from the Open Software Foundation.
+ */
+
+/*
+
+Copyright 1988, 1989 by the Massachusetts Institute of Technology
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is
+hereby granted, provided that the above copyright notice
+appear in all copies and that both that copyright notice and
+this permission notice appear in supporting documentation,
+and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
+used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+M.I.T. and the M.I.T. S.I.P.B. make no representations about
+the suitability of this software for any purpose. It is
+provided "as is" without express or implied warranty.
+
+ $FreeBSD$
+
+*/
+
+#define _PATH_CONF "/etc/newsyslog.conf"
+#define _PATH_SYSLOGPID _PATH_VARRUN "syslog.pid"
+#define _PATH_BZIP2 "/usr/bin/bzip2"
+#define _PATH_GZIP "/usr/bin/gzip"
diff --git a/usr.sbin/newsyslog/ptimes.c b/usr.sbin/newsyslog/ptimes.c
new file mode 100644
index 0000000..474c69d
--- /dev/null
+++ b/usr.sbin/newsyslog/ptimes.c
@@ -0,0 +1,618 @@
+/*-
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ * Initial version of parse8601 was originally added to newsyslog.c in
+ * FreeBSD on Jan 22, 1999 by Garrett Wollman <wollman@FreeBSD.org>.
+ * Initial version of parseDWM was originally added to newsyslog.c in
+ * FreeBSD on Apr 4, 2000 by Hellmuth Michaelis <hm@FreeBSD.org>.
+ *
+ * Copyright (c) 2003 - Garance Alistair Drosehn <gad@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.
+ *
+ * The views and conclusions contained in the software and documentation
+ * are those of the authors and should not be interpreted as representing
+ * official policies, either expressed or implied, of the FreeBSD Project.
+ *
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ * This is intended to be a set of general-purpose routines to process times.
+ * Right now it probably still has a number of assumptions in it, such that
+ * it works fine for newsyslog but might not work for other uses.
+ * ------+---------+---------+---------+---------+---------+---------+---------*
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <ctype.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "extern.h"
+
+#define SECS_PER_HOUR 3600
+
+/*
+ * Bit-values which indicate which components of time were specified
+ * by the string given to parse8601 or parseDWM. These are needed to
+ * calculate what time-in-the-future will match that string.
+ */
+#define TSPEC_YEAR 0x0001
+#define TSPEC_MONTHOFYEAR 0x0002
+#define TSPEC_LDAYOFMONTH 0x0004
+#define TSPEC_DAYOFMONTH 0x0008
+#define TSPEC_DAYOFWEEK 0x0010
+#define TSPEC_HOUROFDAY 0x0020
+
+#define TNYET_ADJ4DST -10 /* DST has "not yet" been adjusted */
+
+struct ptime_data {
+ time_t basesecs; /* Base point for relative times */
+ time_t tsecs; /* Time in seconds */
+ struct tm basetm; /* Base Time expanded into fields */
+ struct tm tm; /* Time expanded into fields */
+ int did_adj4dst; /* Track calls to ptime_adjust4dst */
+ int parseopts; /* Options given for parsing */
+ int tmspec; /* Indicates which time fields had
+ * been specified by the user */
+};
+
+static int days_pmonth(int month, int year);
+static int parse8601(struct ptime_data *ptime, const char *str);
+static int parseDWM(struct ptime_data *ptime, const char *str);
+
+/*
+ * Simple routine to calculate the number of days in a given month.
+ */
+static int
+days_pmonth(int month, int year)
+{
+ static const int mtab[] = {31, 28, 31, 30, 31, 30, 31, 31,
+ 30, 31, 30, 31};
+ int ndays;
+
+ ndays = mtab[month];
+
+ if (month == 1) {
+ /*
+ * We are usually called with a 'tm-year' value
+ * (ie, the value = the number of years past 1900).
+ */
+ if (year < 1900)
+ year += 1900;
+ if (year % 4 == 0) {
+ /*
+ * This is a leap year, as long as it is not a
+ * multiple of 100, or if it is a multiple of
+ * both 100 and 400.
+ */
+ if (year % 100 != 0)
+ ndays++; /* not multiple of 100 */
+ else if (year % 400 == 0)
+ ndays++; /* is multiple of 100 and 400 */
+ }
+ }
+ return (ndays);
+}
+
+/*-
+ * Parse a limited subset of ISO 8601. The specific format is as follows:
+ *
+ * [CC[YY[MM[DD]]]][THH[MM[SS]]] (where `T' is the literal letter)
+ *
+ * We don't accept a timezone specification; missing fields (including timezone)
+ * are defaulted to the current date but time zero.
+ */
+static int
+parse8601(struct ptime_data *ptime, const char *s)
+{
+ char *t;
+ long l;
+ struct tm tm;
+
+ l = strtol(s, &t, 10);
+ if (l < 0 || l >= INT_MAX || (*t != '\0' && *t != 'T'))
+ return (-1);
+
+ /*
+ * Now t points either to the end of the string (if no time was
+ * provided) or to the letter `T' which separates date and time in
+ * ISO 8601. The pointer arithmetic is the same for either case.
+ */
+ tm = ptime->tm;
+ ptime->tmspec = TSPEC_HOUROFDAY;
+ switch (t - s) {
+ case 8:
+ tm.tm_year = ((l / 1000000) - 19) * 100;
+ l = l % 1000000;
+ case 6:
+ ptime->tmspec |= TSPEC_YEAR;
+ tm.tm_year -= tm.tm_year % 100;
+ tm.tm_year += l / 10000;
+ l = l % 10000;
+ case 4:
+ ptime->tmspec |= TSPEC_MONTHOFYEAR;
+ tm.tm_mon = (l / 100) - 1;
+ l = l % 100;
+ case 2:
+ ptime->tmspec |= TSPEC_DAYOFMONTH;
+ tm.tm_mday = l;
+ case 0:
+ break;
+ default:
+ return (-1);
+ }
+
+ /* sanity check */
+ if (tm.tm_year < 70 || tm.tm_mon < 0 || tm.tm_mon > 12
+ || tm.tm_mday < 1 || tm.tm_mday > 31)
+ return (-1);
+
+ if (*t != '\0') {
+ s = ++t;
+ l = strtol(s, &t, 10);
+ if (l < 0 || l >= INT_MAX || (*t != '\0' && !isspace(*t)))
+ return (-1);
+
+ switch (t - s) {
+ case 6:
+ tm.tm_sec = l % 100;
+ l /= 100;
+ case 4:
+ tm.tm_min = l % 100;
+ l /= 100;
+ case 2:
+ ptime->tmspec |= TSPEC_HOUROFDAY;
+ tm.tm_hour = l;
+ case 0:
+ break;
+ default:
+ return (-1);
+ }
+
+ /* sanity check */
+ if (tm.tm_sec < 0 || tm.tm_sec > 60 || tm.tm_min < 0
+ || tm.tm_min > 59 || tm.tm_hour < 0 || tm.tm_hour > 23)
+ return (-1);
+ }
+
+ ptime->tm = tm;
+ return (0);
+}
+
+/*-
+ * Parse a cyclic time specification, the format is as follows:
+ *
+ * [Dhh] or [Wd[Dhh]] or [Mdd[Dhh]]
+ *
+ * to rotate a logfile cyclic at
+ *
+ * - every day (D) within a specific hour (hh) (hh = 0...23)
+ * - once a week (W) at a specific day (d) OR (d = 0..6, 0 = Sunday)
+ * - once a month (M) at a specific day (d) (d = 1..31,l|L)
+ *
+ * We don't accept a timezone specification; missing fields
+ * are defaulted to the current date but time zero.
+ */
+static int
+parseDWM(struct ptime_data *ptime, const char *s)
+{
+ int daysmon, Dseen, WMseen;
+ const char *endval;
+ char *tmp;
+ long l;
+ struct tm tm;
+
+ /* Save away the number of days in this month */
+ tm = ptime->tm;
+ daysmon = days_pmonth(tm.tm_mon, tm.tm_year);
+
+ WMseen = Dseen = 0;
+ ptime->tmspec = TSPEC_HOUROFDAY;
+ for (;;) {
+ endval = NULL;
+ switch (*s) {
+ case 'D':
+ if (Dseen)
+ return (-1);
+ Dseen++;
+ ptime->tmspec |= TSPEC_HOUROFDAY;
+ s++;
+ l = strtol(s, &tmp, 10);
+ if (l < 0 || l > 23)
+ return (-1);
+ endval = tmp;
+ tm.tm_hour = l;
+ break;
+
+ case 'W':
+ if (WMseen)
+ return (-1);
+ WMseen++;
+ ptime->tmspec |= TSPEC_DAYOFWEEK;
+ s++;
+ l = strtol(s, &tmp, 10);
+ if (l < 0 || l > 6)
+ return (-1);
+ endval = tmp;
+ if (l != tm.tm_wday) {
+ int save;
+
+ if (l < tm.tm_wday) {
+ save = 6 - tm.tm_wday;
+ save += (l + 1);
+ } else {
+ save = l - tm.tm_wday;
+ }
+
+ tm.tm_mday += save;
+
+ if (tm.tm_mday > daysmon) {
+ tm.tm_mon++;
+ tm.tm_mday = tm.tm_mday - daysmon;
+ }
+ }
+ break;
+
+ case 'M':
+ if (WMseen)
+ return (-1);
+ WMseen++;
+ ptime->tmspec |= TSPEC_DAYOFMONTH;
+ s++;
+ if (tolower(*s) == 'l') {
+ /* User wants the last day of the month. */
+ ptime->tmspec |= TSPEC_LDAYOFMONTH;
+ tm.tm_mday = daysmon;
+ endval = s + 1;
+ } else {
+ l = strtol(s, &tmp, 10);
+ if (l < 1 || l > 31)
+ return (-1);
+
+ if (l > daysmon)
+ return (-1);
+ endval = tmp;
+ tm.tm_mday = l;
+ }
+ break;
+
+ default:
+ return (-1);
+ break;
+ }
+
+ if (endval == NULL)
+ return (-1);
+ else if (*endval == '\0' || isspace(*endval))
+ break;
+ else
+ s = endval;
+ }
+
+ ptime->tm = tm;
+ return (0);
+}
+
+/*
+ * Initialize a new ptime-related data area.
+ */
+struct ptime_data *
+ptime_init(const struct ptime_data *optsrc)
+{
+ struct ptime_data *newdata;
+
+ newdata = malloc(sizeof(struct ptime_data));
+ if (optsrc != NULL) {
+ memcpy(newdata, optsrc, sizeof(struct ptime_data));
+ } else {
+ memset(newdata, '\0', sizeof(struct ptime_data));
+ newdata->did_adj4dst = TNYET_ADJ4DST;
+ }
+
+ return (newdata);
+}
+
+/*
+ * Adjust a given time if that time is in a different timezone than
+ * some other time.
+ */
+int
+ptime_adjust4dst(struct ptime_data *ptime, const struct ptime_data *dstsrc)
+{
+ struct ptime_data adjtime;
+
+ if (ptime == NULL)
+ return (-1);
+
+ /*
+ * Changes are not made to the given time until after all
+ * of the calculations have been successful.
+ */
+ adjtime = *ptime;
+
+ /* Check to see if this adjustment was already made */
+ if ((adjtime.did_adj4dst != TNYET_ADJ4DST) &&
+ (adjtime.did_adj4dst == dstsrc->tm.tm_isdst))
+ return (0); /* yes, so don't make it twice */
+
+ /* See if daylight-saving has changed between the two times. */
+ if (dstsrc->tm.tm_isdst != adjtime.tm.tm_isdst) {
+ if (adjtime.tm.tm_isdst == 1)
+ adjtime.tsecs -= SECS_PER_HOUR;
+ else if (adjtime.tm.tm_isdst == 0)
+ adjtime.tsecs += SECS_PER_HOUR;
+ adjtime.tm = *(localtime(&adjtime.tsecs));
+ /* Remember that this adjustment has been made */
+ adjtime.did_adj4dst = dstsrc->tm.tm_isdst;
+ /*
+ * XXX - Should probably check to see if changing the
+ * hour also changed the value of is_dst. What
+ * should we do in that case?
+ */
+ }
+
+ *ptime = adjtime;
+ return (0);
+}
+
+int
+ptime_relparse(struct ptime_data *ptime, int parseopts, time_t basetime,
+ const char *str)
+{
+ int dpm, pres;
+ struct tm temp_tm;
+
+ ptime->parseopts = parseopts;
+ ptime->basesecs = basetime;
+ ptime->basetm = *(localtime(&ptime->basesecs));
+ ptime->tm = ptime->basetm;
+ ptime->tm.tm_hour = ptime->tm.tm_min = ptime->tm.tm_sec = 0;
+
+ /*
+ * Call a routine which sets ptime.tm and ptime.tspecs based
+ * on the given string and parsing-options. Note that the
+ * routine should not call mktime to set ptime.tsecs.
+ */
+ if (parseopts & PTM_PARSE_DWM)
+ pres = parseDWM(ptime, str);
+ else
+ pres = parse8601(ptime, str);
+ if (pres < 0) {
+ ptime->tsecs = (time_t)pres;
+ return (pres);
+ }
+
+ /*
+ * Before calling mktime, check to see if we ended up with a
+ * "day-of-month" that does not exist in the selected month.
+ * If we did call mktime with that info, then mktime will
+ * make it look like the user specifically requested a day
+ * in the following month (eg: Feb 31 turns into Mar 3rd).
+ */
+ dpm = days_pmonth(ptime->tm.tm_mon, ptime->tm.tm_year);
+ if ((parseopts & PTM_PARSE_MATCHDOM) &&
+ (ptime->tmspec & TSPEC_DAYOFMONTH) &&
+ (ptime->tm.tm_mday> dpm)) {
+ /*
+ * ptime_nxtime() will want a ptime->tsecs value,
+ * but we need to avoid mktime resetting all the
+ * ptime->tm values.
+ */
+ if (verbose && dbg_at_times > 1)
+ fprintf(stderr,
+ "\t-- dom fixed: %4d/%02d/%02d %02d:%02d (%02d)",
+ ptime->tm.tm_year, ptime->tm.tm_mon,
+ ptime->tm.tm_mday, ptime->tm.tm_hour,
+ ptime->tm.tm_min, dpm);
+ temp_tm = ptime->tm;
+ ptime->tsecs = mktime(&temp_tm);
+ if (ptime->tsecs > (time_t)-1)
+ ptimeset_nxtime(ptime);
+ if (verbose && dbg_at_times > 1)
+ fprintf(stderr,
+ " to: %4d/%02d/%02d %02d:%02d\n",
+ ptime->tm.tm_year, ptime->tm.tm_mon,
+ ptime->tm.tm_mday, ptime->tm.tm_hour,
+ ptime->tm.tm_min);
+ }
+
+ /*
+ * Convert the ptime.tm into standard time_t seconds. Check
+ * for invalid times, which includes things like the hour lost
+ * when switching from "standard time" to "daylight saving".
+ */
+ ptime->tsecs = mktime(&ptime->tm);
+ if (ptime->tsecs == (time_t)-1) {
+ ptime->tsecs = (time_t)-2;
+ return (-2);
+ }
+
+ return (0);
+}
+
+int
+ptime_free(struct ptime_data *ptime)
+{
+
+ if (ptime == NULL)
+ return (-1);
+
+ free(ptime);
+ return (0);
+}
+
+/*
+ * Some trivial routines so ptime_data can remain a completely
+ * opaque type.
+ */
+const char *
+ptimeget_ctime(const struct ptime_data *ptime)
+{
+
+ if (ptime == NULL)
+ return ("Null time in ptimeget_ctime()\n");
+
+ return (ctime(&ptime->tsecs));
+}
+
+double
+ptimeget_diff(const struct ptime_data *minuend, const struct
+ ptime_data *subtrahend)
+{
+
+ /* Just like difftime(), we have no good error-return */
+ if (minuend == NULL || subtrahend == NULL)
+ return (0.0);
+
+ return (difftime(minuend->tsecs, subtrahend->tsecs));
+}
+
+time_t
+ptimeget_secs(const struct ptime_data *ptime)
+{
+
+ if (ptime == NULL)
+ return (-1);
+
+ return (ptime->tsecs);
+}
+
+/*
+ * Generate an approximate timestamp for the next event, based on
+ * what parts of time were specified by the original parameter to
+ * ptime_relparse(). The result may be -1 if there is no obvious
+ * "next time" which will work.
+ */
+int
+ptimeset_nxtime(struct ptime_data *ptime)
+{
+ int moredays, tdpm, tmon, tyear;
+ struct ptime_data nextmatch;
+
+ if (ptime == NULL)
+ return (-1);
+
+ /*
+ * Changes are not made to the given time until after all
+ * of the calculations have been successful.
+ */
+ nextmatch = *ptime;
+ /*
+ * If the user specified a year and we're already past that
+ * time, then there will never be another one!
+ */
+ if (ptime->tmspec & TSPEC_YEAR)
+ return (-1);
+
+ /*
+ * The caller gave us a time in the past. Calculate how much
+ * time is needed to go from that valid rotate time to the
+ * next valid rotate time. We only need to get to the nearest
+ * hour, because newsyslog is only run once per hour.
+ */
+ moredays = 0;
+ if (ptime->tmspec & TSPEC_MONTHOFYEAR) {
+ /* Special case: Feb 29th does not happen every year. */
+ if (ptime->tm.tm_mon == 1 && ptime->tm.tm_mday == 29) {
+ nextmatch.tm.tm_year += 4;
+ if (days_pmonth(1, nextmatch.tm.tm_year) < 29)
+ nextmatch.tm.tm_year += 4;
+ } else {
+ nextmatch.tm.tm_year += 1;
+ }
+ nextmatch.tm.tm_isdst = -1;
+ nextmatch.tsecs = mktime(&nextmatch.tm);
+
+ } else if (ptime->tmspec & TSPEC_LDAYOFMONTH) {
+ /*
+ * Need to get to the last day of next month. Origtm is
+ * already at the last day of this month, so just add to
+ * it number of days in the next month.
+ */
+ if (ptime->tm.tm_mon < 11)
+ moredays = days_pmonth(ptime->tm.tm_mon + 1,
+ ptime->tm.tm_year);
+ else
+ moredays = days_pmonth(0, ptime->tm.tm_year + 1);
+
+ } else if (ptime->tmspec & TSPEC_DAYOFMONTH) {
+ /* Jump to the same day in the next month */
+ moredays = days_pmonth(ptime->tm.tm_mon, ptime->tm.tm_year);
+ /*
+ * In some cases, the next month may not *have* the
+ * desired day-of-the-month. If that happens, then
+ * move to the next month that does have enough days.
+ */
+ tmon = ptime->tm.tm_mon;
+ tyear = ptime->tm.tm_year;
+ for (;;) {
+ if (tmon < 11)
+ tmon += 1;
+ else {
+ tmon = 0;
+ tyear += 1;
+ }
+ tdpm = days_pmonth(tmon, tyear);
+ if (tdpm >= ptime->tm.tm_mday)
+ break;
+ moredays += tdpm;
+ }
+
+ } else if (ptime->tmspec & TSPEC_DAYOFWEEK) {
+ moredays = 7;
+ } else if (ptime->tmspec & TSPEC_HOUROFDAY) {
+ moredays = 1;
+ }
+
+ if (moredays != 0) {
+ nextmatch.tsecs += SECS_PER_HOUR * 24 * moredays;
+ nextmatch.tm = *(localtime(&nextmatch.tsecs));
+ }
+
+ /*
+ * The new time will need to be adjusted if the setting of
+ * daylight-saving has changed between the two times.
+ */
+ ptime_adjust4dst(&nextmatch, ptime);
+
+ /* Everything worked. Update the given time and return. */
+ *ptime = nextmatch;
+ return (0);
+}
+
+int
+ptimeset_time(struct ptime_data *ptime, time_t secs)
+{
+
+ if (ptime == NULL)
+ return (-1);
+
+ ptime->tsecs = secs;
+ ptime->tm = *(localtime(&ptime->tsecs));
+ ptime->parseopts = 0;
+ /* ptime->tmspec = ? */
+ return (0);
+}
diff --git a/usr.sbin/nfsd/Makefile b/usr.sbin/nfsd/Makefile
new file mode 100644
index 0000000..21ca3f3
--- /dev/null
+++ b/usr.sbin/nfsd/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+PROG= nfsd
+MAN= nfsd.8
+
+WARNS?= 6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/nfsd/nfsd.8 b/usr.sbin/nfsd/nfsd.8
new file mode 100644
index 0000000..64c181e
--- /dev/null
+++ b/usr.sbin/nfsd/nfsd.8
@@ -0,0 +1,190 @@
+.\" 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.
+.\"
+.\" @(#)nfsd.8 8.4 (Berkeley) 3/29/95
+.\" $FreeBSD$
+.\"
+.Dd March 29, 1995
+.Dt NFSD 8
+.Os
+.Sh NAME
+.Nm nfsd
+.Nd remote
+.Tn NFS
+server
+.Sh SYNOPSIS
+.Nm
+.Op Fl ardut
+.Op Fl n Ar num_servers
+.Op Fl h Ar bindip
+.Sh DESCRIPTION
+The
+.Nm
+utility runs on a server machine to service
+.Tn NFS
+requests from client machines.
+At least one
+.Nm
+must be running for a machine to operate as a server.
+.Pp
+Unless otherwise specified, four servers for
+.Tn UDP
+transport are started.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl r
+Register the
+.Tn NFS
+service with
+.Xr rpcbind 8
+without creating any servers.
+This option can be used along with the
+.Fl u
+or
+.Fl t
+options to re-register NFS if the rpcbind server is restarted.
+.It Fl d
+Unregister the
+.Tn NFS
+service with
+.Xr rpcbind 8
+without creating any servers.
+.It Fl n
+Specifies how many servers to create.
+.It Fl h Ar bindip
+Specifies which IP address or hostname to bind to on the local host.
+This option is recommended when a host has multiple interfaces.
+Multiple
+.Fl h
+options may be specified.
+.It Fl a
+Specifies that nfsd should bind to the wildcard IP address.
+This is the default if no
+.Fl h
+options are given.
+It may also be specified in addition to any
+.Fl h
+options given.
+Note that NFS/UDP does not operate properly when
+bound to the wildcard IP address whether you use -a or do not use -h.
+.It Fl t
+Serve
+.Tn TCP NFS
+clients.
+.It Fl u
+Serve
+.Tn UDP NFS
+clients.
+.El
+.Pp
+For example,
+.Dq Li "nfsd -u -t -n 6"
+serves
+.Tn UDP
+and
+.Tn TCP
+transports using six daemons.
+.Pp
+A server should run enough daemons to handle
+the maximum level of concurrency from its clients,
+typically four to six.
+.Pp
+The
+.Nm
+utility listens for service requests at the port indicated in the
+.Tn NFS
+server specification; see
+.%T "Network File System Protocol Specification" ,
+RFC1094 and
+.%T "NFS: Network File System Version 3 Protocol Specification" .
+.Pp
+If
+.Nm
+detects that
+.Tn NFS
+is not loaded in the running kernel, it will attempt
+to load a loadable kernel module containing
+.Tn NFS
+support using
+.Xr kldload 2 .
+If this fails, or no
+.Tn NFS
+KLD is available,
+.Nm
+will exit with an error.
+.Pp
+If
+.Nm
+is to be run on a host with multiple interfaces or interface aliases, use
+of the
+.Fl h
+option is recommended.
+If you do not use the option NFS may not respond to
+UDP packets from the same IP address they were sent to.
+Use of this option
+is also recommended when securing NFS exports on a firewalling machine such
+that the NFS sockets can only be accessed by the inside interface.
+The
+.Nm ipfw
+utility
+would then be used to block nfs-related packets that come in on the outside
+interface.
+.Pp
+The
+.Nm
+utility has to be terminated with
+.Dv SIGUSR1
+and cannot be killed with
+.Dv SIGTERM
+or
+.Dv SIGQUIT .
+The
+.Nm
+utility needs to ignore these signals in order to stay alive as long
+as possible during a shutdown, otherwise loopback mounts will
+not be able to unmount.
+If you have to kill
+.Nm
+just do a
+.Dq Li "kill -USR1 <PID of master nfsd>"
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr nfsstat 1 ,
+.Xr kldload 2 ,
+.Xr nfssvc 2 ,
+.Xr exports 5 ,
+.Xr ipfw 8 ,
+.Xr mountd 8 ,
+.Xr nfsiod 8 ,
+.Xr rpcbind 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Bx 4.4 .
diff --git a/usr.sbin/nfsd/nfsd.c b/usr.sbin/nfsd/nfsd.c
new file mode 100644
index 0000000..788d1c2
--- /dev/null
+++ b/usr.sbin/nfsd/nfsd.c
@@ -0,0 +1,890 @@
+/*
+ * Copyright (c) 1989, 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.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 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[] = "@(#)nfsd.c 8.9 (Berkeley) 3/29/95";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/syslog.h>
+#include <sys/wait.h>
+#include <sys/mount.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+
+#include <netdb.h>
+#include <arpa/inet.h>
+#include <nfs/rpcv2.h>
+#include <nfs/nfsproto.h>
+#include <nfsserver/nfs.h>
+
+#include <err.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <netdb.h>
+
+/* Global defs */
+#ifdef DEBUG
+#define syslog(e, s...) fprintf(stderr,s)
+int debug = 1;
+#else
+int debug = 0;
+#endif
+
+#define MAXNFSDCNT 256
+#define DEFNFSDCNT 4
+pid_t children[MAXNFSDCNT]; /* PIDs of children */
+int nfsdcnt; /* number of children */
+int new_syscall;
+
+void cleanup(int);
+void child_cleanup(int);
+void killchildren(void);
+void nfsd_exit(int);
+void nonfs(int);
+void reapchild(int);
+int setbindhost(struct addrinfo **ia, const char *bindhost,
+ struct addrinfo hints);
+void start_server(int);
+void unregistration(void);
+void usage(void);
+
+/*
+ * Nfs server daemon mostly just a user context for nfssvc()
+ *
+ * 1 - do file descriptor and signal cleanup
+ * 2 - fork the nfsd(s)
+ * 3 - create server socket(s)
+ * 4 - register socket with rpcbind
+ *
+ * For connectionless protocols, just pass the socket into the kernel via.
+ * nfssvc().
+ * For connection based sockets, loop doing accepts. When you get a new
+ * socket from accept, pass the msgsock into the kernel via. nfssvc().
+ * The arguments are:
+ * -r - reregister with rpcbind
+ * -d - unregister with rpcbind
+ * -t - support tcp nfs clients
+ * -u - support udp nfs clients
+ * followed by "n" which is the number of nfsds' to fork off
+ */
+int
+main(int argc, char **argv)
+{
+ struct nfsd_addsock_args addsockargs;
+ struct addrinfo *ai_udp, *ai_tcp, *ai_udp6, *ai_tcp6, hints;
+ struct netconfig *nconf_udp, *nconf_tcp, *nconf_udp6, *nconf_tcp6;
+ struct netbuf nb_udp, nb_tcp, nb_udp6, nb_tcp6;
+ struct sockaddr_in inetpeer;
+ struct sockaddr_in6 inet6peer;
+ fd_set ready, sockbits;
+ fd_set v4bits, v6bits;
+ int ch, connect_type_cnt, i, maxsock, msgsock;
+ socklen_t len;
+ int on = 1, unregister, reregister, sock;
+ int tcp6sock, ip6flag, tcpflag, tcpsock;
+ int udpflag, ecode, s, srvcnt;
+ int bindhostc, bindanyflag, rpcbreg, rpcbregcnt;
+ char **bindhost = NULL;
+ pid_t pid;
+
+ if (modfind("nfsserver") < 0) {
+ /* Not present in kernel, try loading it */
+ if (kldload("nfsserver") < 0 || modfind("nfsserver") < 0)
+ errx(1, "NFS server is not available");
+ }
+
+ nfsdcnt = DEFNFSDCNT;
+ unregister = reregister = tcpflag = maxsock = 0;
+ bindanyflag = udpflag = connect_type_cnt = bindhostc = 0;
+#define GETOPT "ah:n:rdtu"
+#define USAGE "[-ardtu] [-n num_servers] [-h bindip]"
+ while ((ch = getopt(argc, argv, GETOPT)) != -1)
+ switch (ch) {
+ case 'a':
+ bindanyflag = 1;
+ break;
+ case 'n':
+ nfsdcnt = atoi(optarg);
+ if (nfsdcnt < 1 || nfsdcnt > MAXNFSDCNT) {
+ warnx("nfsd count %d; reset to %d", nfsdcnt,
+ DEFNFSDCNT);
+ nfsdcnt = DEFNFSDCNT;
+ }
+ break;
+ case 'h':
+ bindhostc++;
+ bindhost = realloc(bindhost,sizeof(char *)*bindhostc);
+ if (bindhost == NULL)
+ errx(1, "Out of memory");
+ bindhost[bindhostc-1] = strdup(optarg);
+ if (bindhost[bindhostc-1] == NULL)
+ errx(1, "Out of memory");
+ break;
+ case 'r':
+ reregister = 1;
+ break;
+ case 'd':
+ unregister = 1;
+ break;
+ case 't':
+ tcpflag = 1;
+ break;
+ case 'u':
+ udpflag = 1;
+ break;
+ default:
+ case '?':
+ usage();
+ };
+ if (!tcpflag && !udpflag)
+ udpflag = 1;
+ argv += optind;
+ argc -= optind;
+
+ /*
+ * XXX
+ * Backward compatibility, trailing number is the count of daemons.
+ */
+ if (argc > 1)
+ usage();
+ if (argc == 1) {
+ nfsdcnt = atoi(argv[0]);
+ if (nfsdcnt < 1 || nfsdcnt > MAXNFSDCNT) {
+ warnx("nfsd count %d; reset to %d", nfsdcnt,
+ DEFNFSDCNT);
+ nfsdcnt = DEFNFSDCNT;
+ }
+ }
+
+ ip6flag = 1;
+ s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ if (s == -1) {
+ if (errno != EPROTONOSUPPORT)
+ err(1, "socket");
+ ip6flag = 0;
+ } else if (getnetconfigent("udp6") == NULL ||
+ getnetconfigent("tcp6") == NULL) {
+ ip6flag = 0;
+ }
+ if (s != -1)
+ close(s);
+
+ if (bindhostc == 0 || bindanyflag) {
+ bindhostc++;
+ bindhost = realloc(bindhost,sizeof(char *)*bindhostc);
+ if (bindhost == NULL)
+ errx(1, "Out of memory");
+ bindhost[bindhostc-1] = strdup("*");
+ if (bindhost[bindhostc-1] == NULL)
+ errx(1, "Out of memory");
+ }
+
+ if (unregister) {
+ unregistration();
+ exit (0);
+ }
+ if (reregister) {
+ if (udpflag) {
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ ecode = getaddrinfo(NULL, "nfs", &hints, &ai_udp);
+ if (ecode != 0)
+ err(1, "getaddrinfo udp: %s", gai_strerror(ecode));
+ nconf_udp = getnetconfigent("udp");
+ if (nconf_udp == NULL)
+ err(1, "getnetconfigent udp failed");
+ nb_udp.buf = ai_udp->ai_addr;
+ nb_udp.len = nb_udp.maxlen = ai_udp->ai_addrlen;
+ if ((!rpcb_set(RPCPROG_NFS, 2, nconf_udp, &nb_udp)) ||
+ (!rpcb_set(RPCPROG_NFS, 3, nconf_udp, &nb_udp)))
+ err(1, "rpcb_set udp failed");
+ freeaddrinfo(ai_udp);
+ }
+ if (udpflag && ip6flag) {
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ ecode = getaddrinfo(NULL, "nfs", &hints, &ai_udp6);
+ if (ecode != 0)
+ err(1, "getaddrinfo udp6: %s", gai_strerror(ecode));
+ nconf_udp6 = getnetconfigent("udp6");
+ if (nconf_udp6 == NULL)
+ err(1, "getnetconfigent udp6 failed");
+ nb_udp6.buf = ai_udp6->ai_addr;
+ nb_udp6.len = nb_udp6.maxlen = ai_udp6->ai_addrlen;
+ if ((!rpcb_set(RPCPROG_NFS, 2, nconf_udp6, &nb_udp6)) ||
+ (!rpcb_set(RPCPROG_NFS, 3, nconf_udp6, &nb_udp6)))
+ err(1, "rpcb_set udp6 failed");
+ freeaddrinfo(ai_udp6);
+ }
+ if (tcpflag) {
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ ecode = getaddrinfo(NULL, "nfs", &hints, &ai_tcp);
+ if (ecode != 0)
+ err(1, "getaddrinfo tcp: %s", gai_strerror(ecode));
+ nconf_tcp = getnetconfigent("tcp");
+ if (nconf_tcp == NULL)
+ err(1, "getnetconfigent tcp failed");
+ nb_tcp.buf = ai_tcp->ai_addr;
+ nb_tcp.len = nb_tcp.maxlen = ai_tcp->ai_addrlen;
+ if ((!rpcb_set(RPCPROG_NFS, 2, nconf_tcp, &nb_tcp)) ||
+ (!rpcb_set(RPCPROG_NFS, 3, nconf_tcp, &nb_tcp)))
+ err(1, "rpcb_set tcp failed");
+ freeaddrinfo(ai_tcp);
+ }
+ if (tcpflag && ip6flag) {
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ ecode = getaddrinfo(NULL, "nfs", &hints, &ai_tcp6);
+ if (ecode != 0)
+ err(1, "getaddrinfo tcp6: %s", gai_strerror(ecode));
+ nconf_tcp6 = getnetconfigent("tcp6");
+ if (nconf_tcp6 == NULL)
+ err(1, "getnetconfigent tcp6 failed");
+ nb_tcp6.buf = ai_tcp6->ai_addr;
+ nb_tcp6.len = nb_tcp6.maxlen = ai_tcp6->ai_addrlen;
+ if ((!rpcb_set(RPCPROG_NFS, 2, nconf_tcp6, &nb_tcp6)) ||
+ (!rpcb_set(RPCPROG_NFS, 3, nconf_tcp6, &nb_tcp6)))
+ err(1, "rpcb_set tcp6 failed");
+ freeaddrinfo(ai_tcp6);
+ }
+ exit (0);
+ }
+ if (debug == 0) {
+ daemon(0, 0);
+ (void)signal(SIGHUP, SIG_IGN);
+ (void)signal(SIGINT, SIG_IGN);
+ /*
+ * nfsd sits in the kernel most of the time. It needs
+ * to ignore SIGTERM/SIGQUIT in order to stay alive as long
+ * as possible during a shutdown, otherwise loopback
+ * mounts will not be able to unmount.
+ */
+ (void)signal(SIGTERM, SIG_IGN);
+ (void)signal(SIGQUIT, SIG_IGN);
+ }
+ (void)signal(SIGSYS, nonfs);
+ (void)signal(SIGCHLD, reapchild);
+
+ openlog("nfsd", LOG_PID, LOG_DAEMON);
+
+ /*
+ * Figure out if the kernel supports the new-style
+ * NFSSVC_NFSD. Old kernels will return ENXIO because they
+ * don't recognise the flag value, new ones will return EINVAL
+ * because argp is NULL.
+ */
+ new_syscall = FALSE;
+ if (nfssvc(NFSSVC_NFSD, NULL) < 0 && errno == EINVAL)
+ new_syscall = TRUE;
+ new_syscall = FALSE;
+
+ if (!new_syscall) {
+ /* If we use UDP only, we start the last server below. */
+ srvcnt = tcpflag ? nfsdcnt : nfsdcnt - 1;
+ for (i = 0; i < srvcnt; i++) {
+ switch ((pid = fork())) {
+ case -1:
+ syslog(LOG_ERR, "fork: %m");
+ nfsd_exit(1);
+ case 0:
+ break;
+ default:
+ children[i] = pid;
+ continue;
+ }
+ (void)signal(SIGUSR1, child_cleanup);
+ setproctitle("server");
+
+ start_server(0);
+ }
+ } else if (tcpflag) {
+ /*
+ * For TCP mode, we fork once to start the first
+ * kernel nfsd thread. The kernel will add more
+ * threads as needed.
+ */
+ pid = fork();
+ if (pid == -1) {
+ syslog(LOG_ERR, "fork: %m");
+ nfsd_exit(1);
+ }
+ if (pid) {
+ children[0] = pid;
+ } else {
+ (void)signal(SIGUSR1, child_cleanup);
+ setproctitle("server");
+ start_server(0);
+ }
+ }
+
+ (void)signal(SIGUSR1, cleanup);
+ FD_ZERO(&v4bits);
+ FD_ZERO(&v6bits);
+ FD_ZERO(&sockbits);
+
+ rpcbregcnt = 0;
+ /* Set up the socket for udp and rpcb register it. */
+ if (udpflag) {
+ rpcbreg = 0;
+ for (i = 0; i < bindhostc; i++) {
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ if (setbindhost(&ai_udp, bindhost[i], hints) == 0) {
+ rpcbreg = 1;
+ rpcbregcnt++;
+ if ((sock = socket(ai_udp->ai_family,
+ ai_udp->ai_socktype,
+ ai_udp->ai_protocol)) < 0) {
+ syslog(LOG_ERR,
+ "can't create udp socket");
+ nfsd_exit(1);
+ }
+ if (bind(sock, ai_udp->ai_addr,
+ ai_udp->ai_addrlen) < 0) {
+ syslog(LOG_ERR,
+ "can't bind udp addr %s: %m",
+ bindhost[i]);
+ nfsd_exit(1);
+ }
+ freeaddrinfo(ai_udp);
+ addsockargs.sock = sock;
+ addsockargs.name = NULL;
+ addsockargs.namelen = 0;
+ if (nfssvc(NFSSVC_ADDSOCK, &addsockargs) < 0) {
+ syslog(LOG_ERR, "can't Add UDP socket");
+ nfsd_exit(1);
+ }
+ (void)close(sock);
+ }
+ }
+ if (rpcbreg == 1) {
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ ecode = getaddrinfo(NULL, "nfs", &hints, &ai_udp);
+ if (ecode != 0) {
+ syslog(LOG_ERR, "getaddrinfo udp: %s",
+ gai_strerror(ecode));
+ nfsd_exit(1);
+ }
+ nconf_udp = getnetconfigent("udp");
+ if (nconf_udp == NULL)
+ err(1, "getnetconfigent udp failed");
+ nb_udp.buf = ai_udp->ai_addr;
+ nb_udp.len = nb_udp.maxlen = ai_udp->ai_addrlen;
+ if ((!rpcb_set(RPCPROG_NFS, 2, nconf_udp, &nb_udp)) ||
+ (!rpcb_set(RPCPROG_NFS, 3, nconf_udp, &nb_udp)))
+ err(1, "rpcb_set udp failed");
+ freeaddrinfo(ai_udp);
+ }
+ }
+
+ /* Set up the socket for udp6 and rpcb register it. */
+ if (udpflag && ip6flag) {
+ rpcbreg = 0;
+ for (i = 0; i < bindhostc; i++) {
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ if (setbindhost(&ai_udp6, bindhost[i], hints) == 0) {
+ rpcbreg = 1;
+ rpcbregcnt++;
+ if ((sock = socket(ai_udp6->ai_family,
+ ai_udp6->ai_socktype,
+ ai_udp6->ai_protocol)) < 0) {
+ syslog(LOG_ERR,
+ "can't create udp6 socket");
+ nfsd_exit(1);
+ }
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY,
+ &on, sizeof on) < 0) {
+ syslog(LOG_ERR,
+ "can't set v6-only binding for "
+ "udp6 socket: %m");
+ nfsd_exit(1);
+ }
+ if (bind(sock, ai_udp6->ai_addr,
+ ai_udp6->ai_addrlen) < 0) {
+ syslog(LOG_ERR,
+ "can't bind udp6 addr %s: %m",
+ bindhost[i]);
+ nfsd_exit(1);
+ }
+ freeaddrinfo(ai_udp6);
+ addsockargs.sock = sock;
+ addsockargs.name = NULL;
+ addsockargs.namelen = 0;
+ if (nfssvc(NFSSVC_ADDSOCK, &addsockargs) < 0) {
+ syslog(LOG_ERR,
+ "can't add UDP6 socket");
+ nfsd_exit(1);
+ }
+ (void)close(sock);
+ }
+ }
+ if (rpcbreg == 1) {
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ ecode = getaddrinfo(NULL, "nfs", &hints, &ai_udp6);
+ if (ecode != 0) {
+ syslog(LOG_ERR, "getaddrinfo udp6: %s",
+ gai_strerror(ecode));
+ nfsd_exit(1);
+ }
+ nconf_udp6 = getnetconfigent("udp6");
+ if (nconf_udp6 == NULL)
+ err(1, "getnetconfigent udp6 failed");
+ nb_udp6.buf = ai_udp6->ai_addr;
+ nb_udp6.len = nb_udp6.maxlen = ai_udp6->ai_addrlen;
+ if ((!rpcb_set(RPCPROG_NFS, 2, nconf_udp6, &nb_udp6)) ||
+ (!rpcb_set(RPCPROG_NFS, 3, nconf_udp6, &nb_udp6)))
+ err(1, "rpcb_set udp6 failed");
+ freeaddrinfo(ai_udp6);
+ }
+ }
+
+ /* Set up the socket for tcp and rpcb register it. */
+ if (tcpflag) {
+ rpcbreg = 0;
+ for (i = 0; i < bindhostc; i++) {
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ if (setbindhost(&ai_tcp, bindhost[i], hints) == 0) {
+ rpcbreg = 1;
+ rpcbregcnt++;
+ if ((tcpsock = socket(AF_INET, SOCK_STREAM,
+ 0)) < 0) {
+ syslog(LOG_ERR,
+ "can't create tpc socket");
+ nfsd_exit(1);
+ }
+ if (setsockopt(tcpsock, SOL_SOCKET,
+ SO_REUSEADDR,
+ (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR,
+ "setsockopt SO_REUSEADDR: %m");
+ if (bind(tcpsock, ai_tcp->ai_addr,
+ ai_tcp->ai_addrlen) < 0) {
+ syslog(LOG_ERR,
+ "can't bind tcp addr %s: %m",
+ bindhost[i]);
+ nfsd_exit(1);
+ }
+ if (listen(tcpsock, 5) < 0) {
+ syslog(LOG_ERR, "listen failed");
+ nfsd_exit(1);
+ }
+ freeaddrinfo(ai_tcp);
+ FD_SET(tcpsock, &sockbits);
+ FD_SET(tcpsock, &v4bits);
+ maxsock = tcpsock;
+ connect_type_cnt++;
+ }
+ }
+ if (rpcbreg == 1) {
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ ecode = getaddrinfo(NULL, "nfs", &hints,
+ &ai_tcp);
+ if (ecode != 0) {
+ syslog(LOG_ERR, "getaddrinfo tcp: %s",
+ gai_strerror(ecode));
+ nfsd_exit(1);
+ }
+ nconf_tcp = getnetconfigent("tcp");
+ if (nconf_tcp == NULL)
+ err(1, "getnetconfigent tcp failed");
+ nb_tcp.buf = ai_tcp->ai_addr;
+ nb_tcp.len = nb_tcp.maxlen = ai_tcp->ai_addrlen;
+ if ((!rpcb_set(RPCPROG_NFS, 2, nconf_tcp,
+ &nb_tcp)) || (!rpcb_set(RPCPROG_NFS, 3,
+ nconf_tcp, &nb_tcp)))
+ err(1, "rpcb_set tcp failed");
+ freeaddrinfo(ai_tcp);
+ }
+ }
+
+ /* Set up the socket for tcp6 and rpcb register it. */
+ if (tcpflag && ip6flag) {
+ rpcbreg = 0;
+ for (i = 0; i < bindhostc; i++) {
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ if (setbindhost(&ai_tcp6, bindhost[i], hints) == 0) {
+ rpcbreg = 1;
+ rpcbregcnt++;
+ if ((tcp6sock = socket(ai_tcp6->ai_family,
+ ai_tcp6->ai_socktype,
+ ai_tcp6->ai_protocol)) < 0) {
+ syslog(LOG_ERR,
+ "can't create tcp6 socket");
+ nfsd_exit(1);
+ }
+ if (setsockopt(tcp6sock, SOL_SOCKET,
+ SO_REUSEADDR,
+ (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR,
+ "setsockopt SO_REUSEADDR: %m");
+ if (setsockopt(tcp6sock, IPPROTO_IPV6,
+ IPV6_V6ONLY, &on, sizeof on) < 0) {
+ syslog(LOG_ERR,
+ "can't set v6-only binding for tcp6 "
+ "socket: %m");
+ nfsd_exit(1);
+ }
+ if (bind(tcp6sock, ai_tcp6->ai_addr,
+ ai_tcp6->ai_addrlen) < 0) {
+ syslog(LOG_ERR,
+ "can't bind tcp6 addr %s: %m",
+ bindhost[i]);
+ nfsd_exit(1);
+ }
+ if (listen(tcp6sock, 5) < 0) {
+ syslog(LOG_ERR, "listen failed");
+ nfsd_exit(1);
+ }
+ freeaddrinfo(ai_tcp6);
+ FD_SET(tcp6sock, &sockbits);
+ FD_SET(tcp6sock, &v6bits);
+ if (maxsock < tcp6sock)
+ maxsock = tcp6sock;
+ connect_type_cnt++;
+ }
+ }
+ if (rpcbreg == 1) {
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ ecode = getaddrinfo(NULL, "nfs", &hints, &ai_tcp6);
+ if (ecode != 0) {
+ syslog(LOG_ERR, "getaddrinfo tcp6: %s",
+ gai_strerror(ecode));
+ nfsd_exit(1);
+ }
+ nconf_tcp6 = getnetconfigent("tcp6");
+ if (nconf_tcp6 == NULL)
+ err(1, "getnetconfigent tcp6 failed");
+ nb_tcp6.buf = ai_tcp6->ai_addr;
+ nb_tcp6.len = nb_tcp6.maxlen = ai_tcp6->ai_addrlen;
+ if ((!rpcb_set(RPCPROG_NFS, 2, nconf_tcp6, &nb_tcp6)) ||
+ (!rpcb_set(RPCPROG_NFS, 3, nconf_tcp6, &nb_tcp6)))
+ err(1, "rpcb_set tcp6 failed");
+ freeaddrinfo(ai_tcp6);
+ }
+ }
+
+ if (rpcbregcnt == 0) {
+ syslog(LOG_ERR, "rpcb_set() failed, nothing to do: %m");
+ nfsd_exit(1);
+ }
+
+ if (tcpflag && connect_type_cnt == 0) {
+ syslog(LOG_ERR, "tcp connects == 0, nothing to do: %m");
+ nfsd_exit(1);
+ }
+
+ setproctitle("master");
+ /*
+ * We always want a master to have a clean way to to shut nfsd down
+ * (with unregistration): if the master is killed, it unregisters and
+ * kills all children. If we run for UDP only (and so do not have to
+ * loop waiting waiting for accept), we instead make the parent
+ * a "server" too. start_server will not return.
+ */
+ if (!tcpflag)
+ start_server(1);
+
+ /*
+ * Loop forever accepting connections and passing the sockets
+ * into the kernel for the mounts.
+ */
+ for (;;) {
+ ready = sockbits;
+ if (connect_type_cnt > 1) {
+ if (select(maxsock + 1,
+ &ready, NULL, NULL, NULL) < 1) {
+ syslog(LOG_ERR, "select failed: %m");
+ if (errno == EINTR)
+ continue;
+ nfsd_exit(1);
+ }
+ }
+ for (tcpsock = 0; tcpsock <= maxsock; tcpsock++) {
+ if (FD_ISSET(tcpsock, &ready)) {
+ if (FD_ISSET(tcpsock, &v4bits)) {
+ len = sizeof(inetpeer);
+ if ((msgsock = accept(tcpsock,
+ (struct sockaddr *)&inetpeer, &len)) < 0) {
+ syslog(LOG_ERR, "accept failed: %m");
+ if (errno == ECONNABORTED ||
+ errno == EINTR)
+ continue;
+ nfsd_exit(1);
+ }
+ memset(inetpeer.sin_zero, 0,
+ sizeof(inetpeer.sin_zero));
+ if (setsockopt(msgsock, SOL_SOCKET,
+ SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR,
+ "setsockopt SO_KEEPALIVE: %m");
+ addsockargs.sock = msgsock;
+ addsockargs.name = (caddr_t)&inetpeer;
+ addsockargs.namelen = len;
+ nfssvc(NFSSVC_ADDSOCK, &addsockargs);
+ (void)close(msgsock);
+ } else if (FD_ISSET(tcpsock, &v6bits)) {
+ len = sizeof(inet6peer);
+ if ((msgsock = accept(tcpsock,
+ (struct sockaddr *)&inet6peer,
+ &len)) < 0) {
+ syslog(LOG_ERR,
+ "accept failed: %m");
+ if (errno == ECONNABORTED ||
+ errno == EINTR)
+ continue;
+ nfsd_exit(1);
+ }
+ if (setsockopt(msgsock, SOL_SOCKET,
+ SO_KEEPALIVE, (char *)&on,
+ sizeof(on)) < 0)
+ syslog(LOG_ERR, "setsockopt "
+ "SO_KEEPALIVE: %m");
+ addsockargs.sock = msgsock;
+ addsockargs.name = (caddr_t)&inet6peer;
+ addsockargs.namelen = len;
+ nfssvc(NFSSVC_ADDSOCK, &addsockargs);
+ (void)close(msgsock);
+ }
+ }
+ }
+ }
+}
+
+int
+setbindhost(struct addrinfo **ai, const char *bindhost, struct addrinfo hints)
+{
+ int ecode;
+ u_int32_t host_addr[4]; /* IPv4 or IPv6 */
+ const char *hostptr;
+
+ if (bindhost == NULL || strcmp("*", bindhost) == 0)
+ hostptr = NULL;
+ else
+ hostptr = bindhost;
+
+ if (hostptr != NULL) {
+ switch (hints.ai_family) {
+ case AF_INET:
+ if (inet_pton(AF_INET, hostptr, host_addr) == 1) {
+ hints.ai_flags = AI_NUMERICHOST;
+ } else {
+ if (inet_pton(AF_INET6, hostptr,
+ host_addr) == 1)
+ return (1);
+ }
+ break;
+ case AF_INET6:
+ if (inet_pton(AF_INET6, hostptr, host_addr) == 1) {
+ hints.ai_flags = AI_NUMERICHOST;
+ } else {
+ if (inet_pton(AF_INET, hostptr,
+ host_addr) == 1)
+ return (1);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ ecode = getaddrinfo(hostptr, "nfs", &hints, ai);
+ if (ecode != 0) {
+ syslog(LOG_ERR, "getaddrinfo %s: %s", bindhost,
+ gai_strerror(ecode));
+ return (1);
+ }
+ return (0);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: nfsd %s\n", USAGE);
+ exit(1);
+}
+
+void
+nonfs(__unused int signo)
+{
+ syslog(LOG_ERR, "missing system call: NFS not available");
+}
+
+void
+reapchild(__unused int signo)
+{
+ pid_t pid;
+ int i;
+
+ while ((pid = wait3(NULL, WNOHANG, NULL)) > 0) {
+ for (i = 0; i < nfsdcnt; i++)
+ if (pid == children[i])
+ children[i] = -1;
+ }
+}
+
+void
+unregistration()
+{
+ if ((!rpcb_unset(RPCPROG_NFS, 2, NULL)) ||
+ (!rpcb_unset(RPCPROG_NFS, 3, NULL)))
+ syslog(LOG_ERR, "rpcb_unset failed");
+}
+
+void
+killchildren()
+{
+ int i;
+
+ for (i = 0; i < nfsdcnt; i++) {
+ if (children[i] > 0)
+ kill(children[i], SIGKILL);
+ }
+}
+
+/*
+ * Cleanup master after SIGUSR1.
+ */
+void
+cleanup(__unused int signo)
+{
+ nfsd_exit(0);
+}
+
+/*
+ * Cleanup child after SIGUSR1.
+ */
+void
+child_cleanup(__unused int signo)
+{
+ exit(0);
+}
+
+void
+nfsd_exit(int status)
+{
+ killchildren();
+ unregistration();
+ exit(status);
+}
+
+void
+start_server(int master)
+{
+ char principal[128];
+ char hostname[128];
+ struct nfsd_nfsd_args nfsdargs;
+ int status;
+
+ status = 0;
+ if (new_syscall) {
+ gethostname(hostname, sizeof(hostname));
+ snprintf(principal, sizeof(principal), "nfs@%s", hostname);
+ nfsdargs.principal = principal;
+ nfsdargs.minthreads = nfsdcnt;
+ nfsdargs.maxthreads = nfsdcnt;
+ if (nfssvc(NFSSVC_NFSD, &nfsdargs) < 0) {
+ syslog(LOG_ERR, "nfssvc: %m");
+ status = 1;
+ }
+ } else {
+ if (nfssvc(NFSSVC_OLDNFSD, NULL) < 0) {
+ syslog(LOG_ERR, "nfssvc: %m");
+ status = 1;
+ }
+ }
+ if (master)
+ nfsd_exit(status);
+ else
+ exit(status);
+}
diff --git a/usr.sbin/ngctl/Makefile b/usr.sbin/ngctl/Makefile
new file mode 100644
index 0000000..071f1c3
--- /dev/null
+++ b/usr.sbin/ngctl/Makefile
@@ -0,0 +1,29 @@
+# $FreeBSD$
+# $Whistle: Makefile,v 1.3 1999/01/16 00:10:11 archie Exp $
+
+.include <bsd.own.mk>
+
+PROG= ngctl
+MAN= ngctl.8
+SRCS= main.c mkpeer.c config.c connect.c dot.c name.c show.c list.c \
+ msg.c debug.c shutdown.c rmhook.c status.c types.c write.c
+WARNS?= 3
+
+.if defined(RELEASE_CRUNCH)
+NGCTL_NO_LIBEDIT=
+.endif
+
+.if ${MK_LIBTHR} == "no"
+NGCTL_NO_LIBEDIT=
+.endif
+
+DPADD= ${LIBNETGRAPH}
+LDADD= -lnetgraph
+
+.if !defined(NGCTL_NO_LIBEDIT)
+CFLAGS+= -DEDITLINE
+DPADD+= ${LIBPTHREAD} ${LIBEDIT} ${LIBTERMCAP}
+LDADD+= -lpthread -ledit -ltermcap
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ngctl/config.c b/usr.sbin/ngctl/config.c
new file mode 100644
index 0000000..9b1a275
--- /dev/null
+++ b/usr.sbin/ngctl/config.c
@@ -0,0 +1,111 @@
+/*
+ * config.c
+ *
+ * Copyright (c) 1996-1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <err.h>
+#include <errno.h>
+#include <netgraph.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "ngctl.h"
+
+#define NOCONFIG "<no config>"
+
+static int ConfigCmd(int ac, char **av);
+
+const struct ngcmd config_cmd = {
+ ConfigCmd,
+ "config <path> [arguments]",
+ "get or set configuration of node at <path>",
+ NULL,
+ { NULL }
+};
+
+static int
+ConfigCmd(int ac, char **av)
+{
+ u_char sbuf[sizeof(struct ng_mesg) + NG_TEXTRESPONSE];
+ struct ng_mesg *const resp = (struct ng_mesg *) sbuf;
+ char *const status = (char *) resp->data;
+ char *path;
+ char buf[NG_TEXTRESPONSE];
+ int nostat = 0, i;
+
+ /* Get arguments */
+ if (ac < 2)
+ return (CMDRTN_USAGE);
+ path = av[1];
+
+ *buf = '\0';
+ for (i = 2; i < ac; i++) {
+ if (i != 2)
+ strcat(buf, " ");
+ strcat(buf, av[i]);
+ }
+
+ /* Get node config summary */
+ if (*buf != '\0')
+ i = NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
+ NGM_TEXT_CONFIG, buf, strlen(buf) + 1);
+ else
+ i = NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
+ NGM_TEXT_CONFIG, NULL, 0);
+ if (i < 0) {
+ switch (errno) {
+ case EINVAL:
+ nostat = 1;
+ break;
+ default:
+ warn("send msg");
+ return (CMDRTN_ERROR);
+ }
+ } else {
+ if (NgRecvMsg(csock, resp, sizeof(sbuf), NULL) < 0
+ || (resp->header.flags & NGF_RESP) == 0)
+ nostat = 1;
+ }
+
+ /* Show it */
+ if (nostat)
+ printf("No config available for \"%s\"\n", path);
+ else
+ printf("Config for \"%s\":\n%s\n", path, status);
+ return (CMDRTN_OK);
+}
+
diff --git a/usr.sbin/ngctl/connect.c b/usr.sbin/ngctl/connect.c
new file mode 100644
index 0000000..cd5307e
--- /dev/null
+++ b/usr.sbin/ngctl/connect.c
@@ -0,0 +1,90 @@
+
+/*
+ * connect.c
+ *
+ * Copyright (c) 1996-1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <err.h>
+#include <netgraph.h>
+#include <stdio.h>
+
+#include "ngctl.h"
+
+static int ConnectCmd(int ac, char **av);
+
+const struct ngcmd connect_cmd = {
+ ConnectCmd,
+ "connect [path] <relpath> <hook> <peerhook>",
+ "Connects hook <peerhook> of the node at <relpath> to <hook>",
+ "The connect command creates a link between the two nodes at"
+ " \"path\" and \"relpath\" using hooks \"hook\" and \"peerhook\","
+ " respectively. The \"relpath\", if not absolute, is specified"
+ " relative to the node at \"path\"."
+ " If \"path\" is omitted then \".\" is assumed.",
+ { "join" }
+};
+
+static int
+ConnectCmd(int ac, char **av)
+{
+ struct ngm_connect con;
+ const char *path = ".";
+
+ /* Get arguments */
+ switch (ac) {
+ case 5:
+ path = av[1];
+ ac--;
+ av++;
+ /* FALLTHROUGH */
+ case 4:
+ snprintf(con.path, sizeof(con.path), "%s", av[1]);
+ snprintf(con.ourhook, sizeof(con.ourhook), "%s", av[2]);
+ snprintf(con.peerhook, sizeof(con.peerhook), "%s", av[3]);
+ break;
+ default:
+ return (CMDRTN_USAGE);
+ }
+
+ /* Send message */
+ if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
+ NGM_CONNECT, &con, sizeof(con)) < 0) {
+ warn("send msg");
+ return (CMDRTN_ERROR);
+ }
+ return (CMDRTN_OK);
+}
+
diff --git a/usr.sbin/ngctl/debug.c b/usr.sbin/ngctl/debug.c
new file mode 100644
index 0000000..d64e32d
--- /dev/null
+++ b/usr.sbin/ngctl/debug.c
@@ -0,0 +1,84 @@
+
+/*
+ * debug.c
+ *
+ * Copyright (c) 1996-1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <netgraph.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ngctl.h"
+
+static int DebugCmd(int ac, char **av);
+
+const struct ngcmd debug_cmd = {
+ DebugCmd,
+ "debug [level]",
+ "Get/set debugging verbosity level",
+ "Without any argument, this command displays the current"
+ " debugging verbosity level. If the argument is ``+'' or ``-''"
+ " the debug level is incremented or decremented; otherwise,"
+ " it must be an absolute numerical level.",
+ { NULL }
+};
+
+static int
+DebugCmd(int ac, char **av)
+{
+ int level;
+
+ /* Get arguments */
+ switch (ac) {
+ case 2:
+ if (!strcmp(av[1], "+"))
+ level = NgSetDebug(-1) + 1;
+ else if (!strcmp(av[1], "-"))
+ level = NgSetDebug(-1) - 1;
+ else if ((level = atoi(av[1])) < 0)
+ return (CMDRTN_USAGE);
+ NgSetDebug(level);
+ break;
+ case 1:
+ printf("Current debug level is %d\n", NgSetDebug(-1));
+ break;
+ default:
+ return (CMDRTN_USAGE);
+ }
+ return (CMDRTN_OK);
+}
+
diff --git a/usr.sbin/ngctl/dot.c b/usr.sbin/ngctl/dot.c
new file mode 100644
index 0000000..bd4673f
--- /dev/null
+++ b/usr.sbin/ngctl/dot.c
@@ -0,0 +1,200 @@
+
+/*
+ * dot.c
+ *
+ * Copyright (c) 2004 Brian Fundakowski Feldman
+ * Copyright (c) 1996-1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <err.h>
+#include <inttypes.h>
+#include <netgraph.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "ngctl.h"
+
+#define UNNAMED "\\<unnamed\\>"
+
+static int DotCmd(int ac, char **av);
+
+const struct ngcmd dot_cmd = {
+ DotCmd,
+ "dot [outputfile]",
+ "Produce a GraphViz (.dot) of the entire netgraph.",
+ "If no outputfile is specified, stdout will be assumed.",
+ { "graphviz", "confdot" }
+};
+
+static int
+DotCmd(int ac, char **av)
+{
+ struct ng_mesg *nlresp;
+ struct namelist *nlist;
+ FILE *f = stdout;
+ int ch;
+ u_int i;
+
+ /* Get options */
+ optind = 1;
+ while ((ch = getopt(ac, av, "")) != -1) {
+ switch (ch) {
+ case '?':
+ default:
+ return (CMDRTN_USAGE);
+ break;
+ }
+ }
+ ac -= optind;
+ av += optind;
+
+ /* Get arguments */
+ switch (ac) {
+ case 1:
+ f = fopen(av[0], "w");
+ if (f == NULL) {
+ warn("Could not open %s for writing", av[0]);
+ return (CMDRTN_ERROR);
+ }
+ case 0:
+ break;
+ default:
+ if (f != stdout)
+ (void)fclose(f);
+ return (CMDRTN_USAGE);
+ }
+
+ /* Get list of nodes */
+ if (NgSendMsg(csock, ".", NGM_GENERIC_COOKIE, NGM_LISTNODES, NULL,
+ 0) < 0) {
+ warn("send listnodes msg");
+ goto error;
+ }
+ if (NgAllocRecvMsg(csock, &nlresp, NULL) < 0) {
+ warn("recv listnodes msg");
+ goto error;
+ }
+
+ nlist = (struct namelist *)nlresp->data;
+ fprintf(f, "graph netgraph {\n");
+ /* TODO: implement rank = same or subgraphs at some point */
+ fprintf(f, "\tedge [ weight = 1.0 ];\n");
+ fprintf(f, "\tnode [ shape = record, fontsize = 12 ] {\n");
+ for (i = 0; i < nlist->numnames; i++)
+ fprintf(f, "\t\t\"%jx\" [ label = \"{%s:|{%s|[%jx]:}}\" ];\n",
+ (uintmax_t)nlist->nodeinfo[i].id,
+ nlist->nodeinfo[i].name[0] != '\0' ?
+ nlist->nodeinfo[i].name : UNNAMED,
+ nlist->nodeinfo[i].type, (uintmax_t)nlist->nodeinfo[i].id);
+ fprintf(f, "\t};\n");
+
+ fprintf(f, "\tsubgraph cluster_disconnected {\n");
+ fprintf(f, "\t\tbgcolor = pink;\n");
+ for (i = 0; i < nlist->numnames; i++)
+ if (nlist->nodeinfo[i].hooks == 0)
+ fprintf(f, "\t\t\"%jx\";\n",
+ (uintmax_t)nlist->nodeinfo[i].id);
+ fprintf(f, "\t};\n");
+
+ for (i = 0; i < nlist->numnames; i++) {
+ struct ng_mesg *hlresp;
+ struct hooklist *hlist;
+ struct nodeinfo *ninfo;
+ char path[NG_PATHSIZ];
+ u_int j;
+
+ (void)snprintf(path, sizeof(path), "[%jx]:",
+ (uintmax_t)nlist->nodeinfo[i].id);
+
+ /* Get node info and hook list */
+ if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,
+ NULL, 0) < 0) {
+ free(nlresp);
+ warn("send listhooks msg");
+ goto error;
+ }
+ if (NgAllocRecvMsg(csock, &hlresp, NULL) < 0) {
+ free(nlresp);
+ warn("recv listhooks msg");
+ goto error;
+ }
+
+ hlist = (struct hooklist *)hlresp->data;
+ ninfo = &hlist->nodeinfo;
+ if (ninfo->hooks == 0) {
+ free(hlresp);
+ continue;
+ }
+
+ fprintf(f, "\tnode [ shape = octagon, fontsize = 10 ] {\n");
+ for (j = 0; j < ninfo->hooks; j++)
+ fprintf(f, "\t\t\"%jx.%s\" [ label = \"%s\" ];\n",
+ (uintmax_t)nlist->nodeinfo[i].id,
+ hlist->link[j].ourhook, hlist->link[j].ourhook);
+ fprintf(f, "\t};\n");
+
+ fprintf(f, "\t{\n\t\tedge [ weight = 2.0, style = bold ];\n");
+ for (j = 0; j < ninfo->hooks; j++)
+ fprintf(f, "\t\t\"%jx\" -- \"%jx.%s\";\n",
+ (uintmax_t)nlist->nodeinfo[i].id,
+ (uintmax_t)nlist->nodeinfo[i].id,
+ hlist->link[j].ourhook);
+ fprintf(f, "\t};\n");
+
+ for (j = 0; j < ninfo->hooks; j++) {
+ /* Only print the edges going in one direction. */
+ if (hlist->link[j].nodeinfo.id > nlist->nodeinfo[i].id)
+ continue;
+ fprintf(f, "\t\"%jx.%s\" -- \"%jx.%s\";\n",
+ (uintmax_t)nlist->nodeinfo[i].id,
+ hlist->link[j].ourhook,
+ (uintmax_t)hlist->link[j].nodeinfo.id,
+ hlist->link[j].peerhook);
+ }
+ free(hlresp);
+ }
+
+ fprintf(f, "};\n");
+
+ free(nlresp);
+ if (f != stdout)
+ (void)fclose(f);
+ return (CMDRTN_OK);
+error:
+ if (f != stdout)
+ (void)fclose(f);
+ return (CMDRTN_ERROR);
+}
diff --git a/usr.sbin/ngctl/list.c b/usr.sbin/ngctl/list.c
new file mode 100644
index 0000000..da4174c
--- /dev/null
+++ b/usr.sbin/ngctl/list.c
@@ -0,0 +1,146 @@
+
+/*
+ * list.c
+ *
+ * Copyright (c) 1996-1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <err.h>
+#include <netgraph.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "ngctl.h"
+
+#define UNNAMED "<unnamed>"
+
+static int ListCmd(int ac, char **av);
+
+const struct ngcmd list_cmd = {
+ ListCmd,
+ "list [-ln]",
+ "Show information about all nodes",
+ "The list command shows information about every node that currently"
+ " exists in the netgraph system. The optional -n argument limits"
+ " this list to only those nodes with a global name assignment."
+ " The optional -l argument provides verbose output that includes"
+ " hook information as well.",
+ { "ls" }
+};
+
+static int
+ListCmd(int ac, char **av)
+{
+ struct ng_mesg *resp;
+ struct namelist *nlist;
+ struct nodeinfo *ninfo;
+ int list_hooks = 0;
+ int named_only = 0;
+ int ch, rtn = CMDRTN_OK;
+
+ /* Get options */
+ optind = 1;
+ while ((ch = getopt(ac, av, "ln")) != -1) {
+ switch (ch) {
+ case 'l':
+ list_hooks = 1;
+ break;
+ case 'n':
+ named_only = 1;
+ break;
+ case '?':
+ default:
+ return (CMDRTN_USAGE);
+ break;
+ }
+ }
+ ac -= optind;
+ av += optind;
+
+ /* Get arguments */
+ switch (ac) {
+ case 0:
+ break;
+ default:
+ return (CMDRTN_USAGE);
+ }
+
+ /* Get list of nodes */
+ if (NgSendMsg(csock, ".", NGM_GENERIC_COOKIE,
+ named_only ? NGM_LISTNAMES : NGM_LISTNODES, NULL, 0) < 0) {
+ warn("send msg");
+ return (CMDRTN_ERROR);
+ }
+ if (NgAllocRecvMsg(csock, &resp, NULL) < 0) {
+ warn("recv msg");
+ return (CMDRTN_ERROR);
+ }
+
+ /* Show each node */
+ nlist = (struct namelist *) resp->data;
+ printf("There are %d total %snodes:\n",
+ nlist->numnames, named_only ? "named " : "");
+ ninfo = nlist->nodeinfo;
+ if (list_hooks) {
+ char path[NG_PATHSIZ];
+ char *argv[2] = { "show", path };
+
+ while (nlist->numnames > 0) {
+ snprintf(path, sizeof(path),
+ "[%lx]:", (u_long)ninfo->id);
+ if ((rtn = (*show_cmd.func)(2, argv)) != CMDRTN_OK)
+ break;
+ ninfo++;
+ nlist->numnames--;
+ }
+ } else {
+ while (nlist->numnames > 0) {
+ if (!*ninfo->name)
+ snprintf(ninfo->name, sizeof(ninfo->name),
+ "%s", UNNAMED);
+ printf(" Name: %-15s Type: %-15s ID: %08x "
+ "Num hooks: %d\n",
+ ninfo->name, ninfo->type, ninfo->id, ninfo->hooks);
+ ninfo++;
+ nlist->numnames--;
+ }
+ }
+
+ /* Done */
+ free(resp);
+ return (rtn);
+}
+
diff --git a/usr.sbin/ngctl/main.c b/usr.sbin/ngctl/main.c
new file mode 100644
index 0000000..f998513
--- /dev/null
+++ b/usr.sbin/ngctl/main.c
@@ -0,0 +1,661 @@
+
+/*
+ * main.c
+ *
+ * Copyright (c) 1996-1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ * $Whistle: main.c,v 1.12 1999/11/29 19:17:46 archie Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+#ifdef EDITLINE
+#include <signal.h>
+#include <histedit.h>
+#include <pthread.h>
+#endif
+
+#include <netgraph.h>
+
+#include "ngctl.h"
+
+#define PROMPT "+ "
+#define MAX_ARGS 512
+#define WHITESPACE " \t\r\n\v\f"
+#define DUMP_BYTES_PER_LINE 16
+
+/* Internal functions */
+static int ReadFile(FILE *fp);
+static void ReadSockets(fd_set *);
+static int DoParseCommand(const char *line);
+static int DoCommand(int ac, char **av);
+static int DoInteractive(void);
+static const struct ngcmd *FindCommand(const char *string);
+static int MatchCommand(const struct ngcmd *cmd, const char *s);
+static void Usage(const char *msg);
+static int ReadCmd(int ac, char **av);
+static int HelpCmd(int ac, char **av);
+static int QuitCmd(int ac, char **av);
+#ifdef EDITLINE
+static sig_atomic_t unblock;
+static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+#endif
+
+/* List of commands */
+static const struct ngcmd *const cmds[] = {
+ &config_cmd,
+ &connect_cmd,
+ &debug_cmd,
+ &dot_cmd,
+ &help_cmd,
+ &list_cmd,
+ &mkpeer_cmd,
+ &msg_cmd,
+ &name_cmd,
+ &read_cmd,
+ &rmhook_cmd,
+ &show_cmd,
+ &shutdown_cmd,
+ &status_cmd,
+ &types_cmd,
+ &write_cmd,
+ &quit_cmd,
+ NULL
+};
+
+/* Commands defined in this file */
+const struct ngcmd read_cmd = {
+ ReadCmd,
+ "read <filename>",
+ "Read and execute commands from a file",
+ NULL,
+ { "source", "." }
+};
+const struct ngcmd help_cmd = {
+ HelpCmd,
+ "help [command]",
+ "Show command summary or get more help on a specific command",
+ NULL,
+ { "?" }
+};
+const struct ngcmd quit_cmd = {
+ QuitCmd,
+ "quit",
+ "Exit program",
+ NULL,
+ { "exit" }
+};
+
+/* Our control and data sockets */
+int csock, dsock;
+
+/*
+ * main()
+ */
+int
+main(int ac, char *av[])
+{
+ char name[NG_NODESIZ];
+ int interactive = isatty(0) && isatty(1);
+ FILE *fp = NULL;
+ int ch, rtn = 0;
+
+ /* Set default node name */
+ snprintf(name, sizeof(name), "ngctl%d", getpid());
+
+ /* Parse command line */
+ while ((ch = getopt(ac, av, "df:n:")) != -1) {
+ switch (ch) {
+ case 'd':
+ NgSetDebug(NgSetDebug(-1) + 1);
+ break;
+ case 'f':
+ if (strcmp(optarg, "-") == 0)
+ fp = stdin;
+ else if ((fp = fopen(optarg, "r")) == NULL)
+ err(EX_NOINPUT, "%s", optarg);
+ break;
+ case 'n':
+ snprintf(name, sizeof(name), "%s", optarg);
+ break;
+ case '?':
+ default:
+ Usage((char *)NULL);
+ break;
+ }
+ }
+ ac -= optind;
+ av += optind;
+
+ /* Create a new socket node */
+ if (NgMkSockNode(name, &csock, &dsock) < 0)
+ err(EX_OSERR, "can't create node");
+
+ /* Do commands as requested */
+ if (ac == 0) {
+ if (fp != NULL) {
+ rtn = ReadFile(fp);
+ } else if (interactive) {
+ rtn = DoInteractive();
+ } else
+ Usage("no command specified");
+ } else {
+ rtn = DoCommand(ac, av);
+ }
+
+ /* Convert command return code into system exit code */
+ switch (rtn) {
+ case CMDRTN_OK:
+ case CMDRTN_QUIT:
+ rtn = 0;
+ break;
+ case CMDRTN_USAGE:
+ rtn = EX_USAGE;
+ break;
+ case CMDRTN_ERROR:
+ rtn = EX_OSERR;
+ break;
+ }
+ return (rtn);
+}
+
+/*
+ * Process commands from a file
+ */
+static int
+ReadFile(FILE *fp)
+{
+ char line[LINE_MAX];
+ int num, rtn;
+
+ for (num = 1; fgets(line, sizeof(line), fp) != NULL; num++) {
+ if (*line == '#')
+ continue;
+ if ((rtn = DoParseCommand(line)) != 0) {
+ warnx("line %d: error in file", num);
+ return (rtn);
+ }
+ }
+ return (CMDRTN_OK);
+}
+
+#ifdef EDITLINE
+/* Signal handler for Monitor() thread. */
+static void
+Unblock(int signal)
+{
+
+ unblock = 1;
+}
+
+/*
+ * Thread that monitors csock and dsock while main thread
+ * can be blocked in el_gets().
+ */
+static void *
+Monitor(void *v)
+{
+ struct sigaction act;
+ const int maxfd = MAX(csock, dsock) + 1;
+
+ act.sa_handler = Unblock;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ sigaction(SIGUSR1, &act, NULL);
+
+ pthread_mutex_lock(&mutex);
+ for (;;) {
+ fd_set rfds;
+
+ /* See if any data or control messages are arriving. */
+ FD_ZERO(&rfds);
+ FD_SET(csock, &rfds);
+ FD_SET(dsock, &rfds);
+ unblock = 0;
+ if (select(maxfd, &rfds, NULL, NULL, NULL) <= 0) {
+ if (errno == EINTR) {
+ if (unblock == 1)
+ pthread_cond_wait(&cond, &mutex);
+ continue;
+ }
+ err(EX_OSERR, "select");
+ }
+ ReadSockets(&rfds);
+ }
+
+ return (NULL);
+}
+
+static char *
+Prompt(EditLine *el)
+{
+
+ return (PROMPT);
+}
+
+/*
+ * Here we start a thread, that will monitor the netgraph
+ * sockets and catch any unexpected messages or data on them,
+ * that can arrive while user edits his/her commands.
+ *
+ * Whenever we expect data on netgraph sockets, we send signal
+ * to monitoring thread. The signal forces it to exit select()
+ * system call and sleep on condvar until we wake it. While
+ * monitoring thread sleeps, we can do our work with netgraph
+ * sockets.
+ */
+static int
+DoInteractive(void)
+{
+ pthread_t monitor;
+ EditLine *el;
+ History *hist;
+ HistEvent hev = { 0, "" };
+
+ (*help_cmd.func)(0, NULL);
+ pthread_create(&monitor, NULL, Monitor, NULL);
+ el = el_init(getprogname(), stdin, stdout, stderr);
+ if (el == NULL)
+ return (CMDRTN_ERROR);
+ el_set(el, EL_PROMPT, Prompt);
+ el_set(el, EL_SIGNAL, 1);
+ el_set(el, EL_EDITOR, "emacs");
+ hist = history_init();
+ if (hist == NULL)
+ return (CMDRTN_ERROR);
+ history(hist, &hev, H_SETSIZE, 100);
+ history(hist, &hev, H_SETUNIQUE, 1);
+ el_set(el, EL_HIST, history, (const char *)hist);
+ el_source(el, NULL);
+
+ for (;;) {
+ const char *buf;
+ int count;
+
+ if ((buf = el_gets(el, &count)) == NULL) {
+ printf("\n");
+ break;
+ }
+ history(hist, &hev, H_ENTER, buf);
+ pthread_kill(monitor, SIGUSR1);
+ pthread_mutex_lock(&mutex);
+ if (DoParseCommand(buf) == CMDRTN_QUIT)
+ break;
+ pthread_cond_signal(&cond);
+ pthread_mutex_unlock(&mutex);
+ }
+
+ history_end(hist);
+ el_end(el);
+ pthread_cancel(monitor);
+
+ return (CMDRTN_QUIT);
+}
+
+#else /* !EDITLINE */
+
+/*
+ * Interactive mode w/o libedit functionality.
+ */
+static int
+DoInteractive(void)
+{
+ const int maxfd = MAX(csock, dsock) + 1;
+
+ (*help_cmd.func)(0, NULL);
+ while (1) {
+ struct timeval tv;
+ fd_set rfds;
+
+ /* See if any data or control messages are arriving */
+ FD_ZERO(&rfds);
+ FD_SET(csock, &rfds);
+ FD_SET(dsock, &rfds);
+ memset(&tv, 0, sizeof(tv));
+ if (select(maxfd, &rfds, NULL, NULL, &tv) <= 0) {
+
+ /* Issue prompt and wait for anything to happen */
+ printf("%s", PROMPT);
+ fflush(stdout);
+ FD_ZERO(&rfds);
+ FD_SET(0, &rfds);
+ FD_SET(csock, &rfds);
+ FD_SET(dsock, &rfds);
+ if (select(maxfd, &rfds, NULL, NULL, NULL) < 0)
+ err(EX_OSERR, "select");
+
+ /* If not user input, print a newline first */
+ if (!FD_ISSET(0, &rfds))
+ printf("\n");
+ }
+
+ ReadSockets(&rfds);
+
+ /* Get any user input */
+ if (FD_ISSET(0, &rfds)) {
+ char buf[LINE_MAX];
+
+ if (fgets(buf, sizeof(buf), stdin) == NULL) {
+ printf("\n");
+ break;
+ }
+ if (DoParseCommand(buf) == CMDRTN_QUIT)
+ break;
+ }
+ }
+ return (CMDRTN_QUIT);
+}
+#endif /* !EDITLINE */
+
+/*
+ * Read and process data on netgraph control and data sockets.
+ */
+static void
+ReadSockets(fd_set *rfds)
+{
+ /* Display any incoming control message. */
+ if (FD_ISSET(csock, rfds))
+ MsgRead();
+
+ /* Display any incoming data packet. */
+ if (FD_ISSET(dsock, rfds)) {
+ char hook[NG_HOOKSIZ];
+ u_char *buf;
+ int rl;
+
+ /* Read packet from socket. */
+ if ((rl = NgAllocRecvData(dsock, &buf, hook)) < 0)
+ err(EX_OSERR, "reading hook \"%s\"", hook);
+ if (rl == 0)
+ errx(EX_OSERR, "EOF from hook \"%s\"?", hook);
+
+ /* Write packet to stdout. */
+ printf("Rec'd data packet on hook \"%s\":\n", hook);
+ DumpAscii(buf, rl);
+ free(buf);
+ }
+}
+
+/*
+ * Parse a command line and execute the command
+ */
+static int
+DoParseCommand(const char *line)
+{
+ char *av[MAX_ARGS];
+ int ac;
+
+ /* Parse line */
+ for (ac = 0, av[0] = strtok((char *)line, WHITESPACE);
+ ac < MAX_ARGS - 1 && av[ac];
+ av[++ac] = strtok(NULL, WHITESPACE));
+
+ /* Do command */
+ return (DoCommand(ac, av));
+}
+
+/*
+ * Execute the command
+ */
+static int
+DoCommand(int ac, char **av)
+{
+ const struct ngcmd *cmd;
+ int rtn;
+
+ if (ac == 0 || *av[0] == 0)
+ return (CMDRTN_OK);
+ if ((cmd = FindCommand(av[0])) == NULL)
+ return (CMDRTN_ERROR);
+ if ((rtn = (*cmd->func)(ac, av)) == CMDRTN_USAGE)
+ warnx("usage: %s", cmd->cmd);
+ return (rtn);
+}
+
+/*
+ * Find a command
+ */
+static const struct ngcmd *
+FindCommand(const char *string)
+{
+ int k, found = -1;
+
+ for (k = 0; cmds[k] != NULL; k++) {
+ if (MatchCommand(cmds[k], string)) {
+ if (found != -1) {
+ warnx("\"%s\": ambiguous command", string);
+ return (NULL);
+ }
+ found = k;
+ }
+ }
+ if (found == -1) {
+ warnx("\"%s\": unknown command", string);
+ return (NULL);
+ }
+ return (cmds[found]);
+}
+
+/*
+ * See if string matches a prefix of "cmd" (or an alias) case insensitively
+ */
+static int
+MatchCommand(const struct ngcmd *cmd, const char *s)
+{
+ int a;
+
+ /* Try to match command, ignoring the usage stuff */
+ if (strlen(s) <= strcspn(cmd->cmd, WHITESPACE)) {
+ if (strncasecmp(s, cmd->cmd, strlen(s)) == 0)
+ return (1);
+ }
+
+ /* Try to match aliases */
+ for (a = 0; a < MAX_CMD_ALIAS && cmd->aliases[a] != NULL; a++) {
+ if (strlen(cmd->aliases[a]) >= strlen(s)) {
+ if (strncasecmp(s, cmd->aliases[a], strlen(s)) == 0)
+ return (1);
+ }
+ }
+
+ /* No match */
+ return (0);
+}
+
+/*
+ * ReadCmd()
+ */
+static int
+ReadCmd(int ac, char **av)
+{
+ FILE *fp;
+ int rtn;
+
+ /* Open file */
+ switch (ac) {
+ case 2:
+ if ((fp = fopen(av[1], "r")) == NULL) {
+ warn("%s", av[1]);
+ return (CMDRTN_ERROR);
+ }
+ break;
+ default:
+ return (CMDRTN_USAGE);
+ }
+
+ /* Process it */
+ rtn = ReadFile(fp);
+ fclose(fp);
+ return (rtn);
+}
+
+/*
+ * HelpCmd()
+ */
+static int
+HelpCmd(int ac, char **av)
+{
+ const struct ngcmd *cmd;
+ int k;
+
+ switch (ac) {
+ case 0:
+ case 1:
+ /* Show all commands */
+ printf("Available commands:\n");
+ for (k = 0; cmds[k] != NULL; k++) {
+ char *s, buf[100];
+
+ cmd = cmds[k];
+ snprintf(buf, sizeof(buf), "%s", cmd->cmd);
+ for (s = buf; *s != '\0' && !isspace(*s); s++);
+ *s = '\0';
+ printf(" %-10s %s\n", buf, cmd->desc);
+ }
+ return (CMDRTN_OK);
+ default:
+ /* Show help on a specific command */
+ if ((cmd = FindCommand(av[1])) != NULL) {
+ printf("usage: %s\n", cmd->cmd);
+ if (cmd->aliases[0] != NULL) {
+ int a = 0;
+
+ printf("Aliases: ");
+ while (1) {
+ printf("%s", cmd->aliases[a++]);
+ if (a == MAX_CMD_ALIAS
+ || cmd->aliases[a] == NULL) {
+ printf("\n");
+ break;
+ }
+ printf(", ");
+ }
+ }
+ printf("Summary: %s\n", cmd->desc);
+ if (cmd->help != NULL) {
+ const char *s;
+ char buf[65];
+ int tot, len, done;
+
+ printf("Description:\n");
+ for (s = cmd->help; *s != '\0'; s += len) {
+ while (isspace(*s))
+ s++;
+ tot = snprintf(buf,
+ sizeof(buf), "%s", s);
+ len = strlen(buf);
+ done = len == tot;
+ if (!done) {
+ while (len > 0
+ && !isspace(buf[len-1]))
+ buf[--len] = '\0';
+ }
+ printf(" %s\n", buf);
+ }
+ }
+ }
+ }
+ return (CMDRTN_OK);
+}
+
+/*
+ * QuitCmd()
+ */
+static int
+QuitCmd(int ac __unused, char **av __unused)
+{
+ return (CMDRTN_QUIT);
+}
+
+/*
+ * Dump data in hex and ASCII form
+ */
+void
+DumpAscii(const u_char *buf, int len)
+{
+ char ch, sbuf[100];
+ int k, count;
+
+ for (count = 0; count < len; count += DUMP_BYTES_PER_LINE) {
+ snprintf(sbuf, sizeof(sbuf), "%04x: ", count);
+ for (k = 0; k < DUMP_BYTES_PER_LINE; k++) {
+ if (count + k < len) {
+ snprintf(sbuf + strlen(sbuf),
+ sizeof(sbuf) - strlen(sbuf),
+ "%02x ", buf[count + k]);
+ } else {
+ snprintf(sbuf + strlen(sbuf),
+ sizeof(sbuf) - strlen(sbuf), " ");
+ }
+ }
+ snprintf(sbuf + strlen(sbuf), sizeof(sbuf) - strlen(sbuf), " ");
+ for (k = 0; k < DUMP_BYTES_PER_LINE; k++) {
+ if (count + k < len) {
+ ch = isprint(buf[count + k]) ?
+ buf[count + k] : '.';
+ snprintf(sbuf + strlen(sbuf),
+ sizeof(sbuf) - strlen(sbuf), "%c", ch);
+ } else {
+ snprintf(sbuf + strlen(sbuf),
+ sizeof(sbuf) - strlen(sbuf), " ");
+ }
+ }
+ printf("%s\n", sbuf);
+ }
+}
+
+/*
+ * Usage()
+ */
+static void
+Usage(const char *msg)
+{
+ if (msg)
+ warnx("%s", msg);
+ fprintf(stderr,
+ "usage: ngctl [-d] [-f file] [-n name] [command ...]\n");
+ exit(EX_USAGE);
+}
diff --git a/usr.sbin/ngctl/mkpeer.c b/usr.sbin/ngctl/mkpeer.c
new file mode 100644
index 0000000..1c6896b
--- /dev/null
+++ b/usr.sbin/ngctl/mkpeer.c
@@ -0,0 +1,90 @@
+
+/*
+ * mkpeer.c
+ *
+ * Copyright (c) 1996-1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <err.h>
+#include <netgraph.h>
+#include <stdio.h>
+
+#include "ngctl.h"
+
+static int MkPeerCmd(int ac, char **av);
+
+const struct ngcmd mkpeer_cmd = {
+ MkPeerCmd,
+ "mkpeer [path] <type> <hook> <peerhook>",
+ "Create and connect a new node to the node at \"path\"",
+ "The mkpeer command atomically creates a new node of type \"type\""
+ " and connects it to the node at \"path\". The hooks used for the"
+ " connection are \"hook\" on the original node and \"peerhook\""
+ " on the new node."
+ " If \"path\" is omitted then \".\" is assumed.",
+ { NULL }
+};
+
+static int
+MkPeerCmd(int ac, char **av)
+{
+ struct ngm_mkpeer mkp;
+ const char *path = ".";
+
+ /* Get arguments */
+ switch (ac) {
+ case 5:
+ path = av[1];
+ ac--;
+ av++;
+ /* FALLTHROUGH */
+ case 4:
+ snprintf(mkp.type, sizeof(mkp.type), "%s", av[1]);
+ snprintf(mkp.ourhook, sizeof(mkp.ourhook), "%s", av[2]);
+ snprintf(mkp.peerhook, sizeof(mkp.peerhook), "%s", av[3]);
+ break;
+ default:
+ return (CMDRTN_USAGE);
+ }
+
+ /* Send message */
+ if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
+ NGM_MKPEER, &mkp, sizeof(mkp)) < 0) {
+ warn("send msg");
+ return (CMDRTN_ERROR);
+ }
+ return (CMDRTN_OK);
+}
+
diff --git a/usr.sbin/ngctl/msg.c b/usr.sbin/ngctl/msg.c
new file mode 100644
index 0000000..f74efde
--- /dev/null
+++ b/usr.sbin/ngctl/msg.c
@@ -0,0 +1,163 @@
+
+/*
+ * msg.c
+ *
+ * Copyright (c) 1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $Whistle: msg.c,v 1.2 1999/11/29 23:38:35 archie Exp $
+ * $FreeBSD$
+ */
+
+#include <err.h>
+#include <netgraph.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "ngctl.h"
+
+static int MsgCmd(int ac, char **av);
+
+const struct ngcmd msg_cmd = {
+ MsgCmd,
+ "msg path command [args ... ]",
+ "Send a netgraph control message to the node at \"path\"",
+ "The msg command constructs a netgraph control message from the"
+ " command name and ASCII arguments (if any) and sends that message"
+ " to the node. It does this by first asking the node to convert"
+ " the ASCII message into binary format, and resending the result.",
+ { "cmd" }
+};
+
+static int
+MsgCmd(int ac, char **av)
+{
+ char *buf;
+ char *path, *cmdstr;
+ int i, len;
+
+ /* Get arguments */
+ if (ac < 3)
+ return (CMDRTN_USAGE);
+ path = av[1];
+ cmdstr = av[2];
+
+ /* Put command and arguments back together as one string */
+ for (len = 1, i = 3; i < ac; i++)
+ len += strlen(av[i]) + 1;
+ if ((buf = malloc(len)) == NULL) {
+ warn("malloc");
+ return (CMDRTN_ERROR);
+ }
+ for (*buf = '\0', i = 3; i < ac; i++) {
+ snprintf(buf + strlen(buf),
+ len - strlen(buf), " %s", av[i]);
+ }
+
+ /* Send it */
+ if (NgSendAsciiMsg(csock, path, "%s%s", cmdstr, buf) < 0) {
+ free(buf);
+ warn("send msg");
+ return (CMDRTN_ERROR);
+ }
+ free(buf);
+
+ /* See if a synchronous reply awaits */
+ {
+ struct timeval tv;
+ fd_set rfds;
+
+ FD_ZERO(&rfds);
+ FD_SET(csock, &rfds);
+ memset(&tv, 0, sizeof(tv));
+ switch (select(csock + 1, &rfds, NULL, NULL, &tv)) {
+ case -1:
+ err(EX_OSERR, "select");
+ case 0:
+ break;
+ default:
+ MsgRead();
+ break;
+ }
+ }
+
+ /* Done */
+ return (CMDRTN_OK);
+}
+
+/*
+ * Read and display the next incoming control message
+ */
+void
+MsgRead()
+{
+ struct ng_mesg *m, *m2;
+ struct ng_mesg *ascii;
+ char path[NG_PATHSIZ];
+
+ /* Get incoming message (in binary form) */
+ if (NgAllocRecvMsg(csock, &m, path) < 0) {
+ warn("recv incoming message");
+ return;
+ }
+
+ /* Ask originating node to convert message to ASCII */
+ if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
+ NGM_BINARY2ASCII, m, sizeof(*m) + m->header.arglen) < 0
+ || NgAllocRecvMsg(csock, &m2, NULL) < 0) {
+ printf("Rec'd %s %d from \"%s\":\n",
+ (m->header.flags & NGF_RESP) != 0 ? "response" : "command",
+ m->header.cmd, path);
+ if (m->header.arglen == 0)
+ printf("No arguments\n");
+ else
+ DumpAscii((const u_char *)m->data, m->header.arglen);
+ free(m);
+ return;
+ }
+
+ /* Display message in ASCII form */
+ free(m);
+ ascii = (struct ng_mesg *)m2->data;
+ printf("Rec'd %s \"%s\" (%d) from \"%s\":\n",
+ (ascii->header.flags & NGF_RESP) != 0 ? "response" : "command",
+ ascii->header.cmdstr, ascii->header.cmd, path);
+ if (*ascii->data != '\0')
+ printf("Args:\t%s\n", ascii->data);
+ else
+ printf("No arguments\n");
+ free(m2);
+}
+
diff --git a/usr.sbin/ngctl/name.c b/usr.sbin/ngctl/name.c
new file mode 100644
index 0000000..7149d35
--- /dev/null
+++ b/usr.sbin/ngctl/name.c
@@ -0,0 +1,81 @@
+
+/*
+ * name.c
+ *
+ * Copyright (c) 1996-1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <err.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <netgraph.h>
+
+#include "ngctl.h"
+
+static int NameCmd(int ac, char **av);
+
+const struct ngcmd name_cmd = {
+ NameCmd,
+ "name <path> <name>",
+ "Assign name <name> to the node at <path>",
+ NULL,
+ { NULL }
+};
+
+static int
+NameCmd(int ac, char **av)
+{
+ struct ngm_name name;
+ char *path;
+
+ /* Get arguments */
+ switch (ac) {
+ case 3:
+ path = av[1];
+ snprintf(name.name, sizeof(name.name), "%s", av[2]);
+ break;
+ default:
+ return (CMDRTN_USAGE);
+ }
+
+ /* Send message */
+ if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
+ NGM_NAME, &name, sizeof(name)) < 0) {
+ warn("send msg");
+ return (CMDRTN_ERROR);
+ }
+ return (CMDRTN_OK);
+}
+
diff --git a/usr.sbin/ngctl/ngctl.8 b/usr.sbin/ngctl/ngctl.8
new file mode 100644
index 0000000..ce77069
--- /dev/null
+++ b/usr.sbin/ngctl/ngctl.8
@@ -0,0 +1,141 @@
+.\" Copyright (c) 1996-1999 Whistle Communications, Inc.
+.\" All rights reserved.
+.\"
+.\" Subject to the following obligations and disclaimer of warranty, use and
+.\" redistribution of this software, in source or object code forms, with or
+.\" without modifications are expressly permitted by Whistle Communications;
+.\" provided, however, that:
+.\" 1. Any and all reproductions of the source or object code must include the
+.\" copyright notice above and the following disclaimer of warranties; and
+.\" 2. No rights are granted, in any manner or form, to use Whistle
+.\" Communications, Inc. trademarks, including the mark "WHISTLE
+.\" COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+.\" such appears in the above copyright notice or in the software.
+.\"
+.\" THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+.\" TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+.\" REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+.\" INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+.\" WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+.\" REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+.\" SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+.\" IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+.\" RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+.\" WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+.\" PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+.\" SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+.\" OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\" $Whistle: ngctl.8,v 1.6 1999/01/20 03:19:44 archie Exp $
+.\"
+.Dd January 19, 1999
+.Dt NGCTL 8
+.Os
+.Sh NAME
+.Nm ngctl
+.Nd netgraph control utility
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl f Ar filename
+.Op Fl n Ar nodename
+.Op Ar command ...
+.Sh DESCRIPTION
+The
+.Nm
+utility creates a new netgraph node of type
+.Em socket
+which can be used to issue netgraph commands.
+If no
+.Fl f
+flag is given, no
+command is supplied on the command line, and standard input is a tty,
+.Nm
+will enter interactive mode.
+Otherwise
+.Nm
+will execute the supplied command(s) and exit immediately.
+.Pp
+Nodes can be created, removed, joined together, etc.
+.Tn ASCII
+formatted control messages can be sent to any node if that node
+supports binary/ASCII control message conversion.
+.Pp
+In interactive mode,
+.Nm
+will display any control messages and data packets received by the socket node.
+In the case of control messages, the message arguments are displayed in
+.Tn ASCII
+form if the originating node supports conversion.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl f Ar nodeinfo
+Read commands from the named file.
+A single dash represents the standard input.
+Blank lines and lines starting with a
+.Dq #
+are ignored.
+.It Fl n Ar nodename
+Assign
+.Em nodename
+to the newly created netgraph node.
+The default name is
+.Em ngctlXXX
+where XXX is the process ID number.
+.It Fl d
+Increase the debugging verbosity level.
+.El
+.Sh COMMANDS
+The currently supported commands in
+.Nm
+are:
+.Pp
+.Bd -literal -offset indent -compact
+config get or set configuration of node at <path>
+connect Connects hook <peerhook> of the node at <relpath> to <hook>
+debug Get/set debugging verbosity level
+dot Produce a GraphViz (.dot) of the entire netgraph.
+help Show command summary or get more help on a specific command
+list Show information about all nodes
+mkpeer Create and connect a new node to the node at "path"
+msg Send a netgraph control message to the node at "path"
+name Assign name <name> to the node at <path>
+read Read and execute commands from a file
+rmhook Disconnect hook "hook" of the node at "path"
+show Show information about the node at <path>
+shutdown Shutdown the node at <path>
+status Get human readable status information from the node at <path>
+types Show information about all installed node types
+write Send a data packet down the hook named by "hook".
+quit Exit program
+.Ed
+.Pp
+Some commands have aliases, e.g.,
+.Dq ls
+is the same as
+.Dq list .
+The
+.Dq help
+command displays the available
+commands, their usage and aliases, and a brief description.
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr netgraph 3 ,
+.Xr netgraph 4 ,
+.Xr nghook 8
+.Sh HISTORY
+The
+.Nm netgraph
+system was designed and first implemented at Whistle Communications, Inc.\& in
+a version of
+.Fx 2.2
+customized for the Whistle InterJet.
+.Sh AUTHORS
+.An Archie Cobbs Aq archie@whistle.com
diff --git a/usr.sbin/ngctl/ngctl.h b/usr.sbin/ngctl/ngctl.h
new file mode 100644
index 0000000..4d8c2ad
--- /dev/null
+++ b/usr.sbin/ngctl/ngctl.h
@@ -0,0 +1,82 @@
+
+/*
+ * ngctl.h
+ *
+ * Copyright (c) 1996-1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define MAX_CMD_ALIAS 8
+
+/* Command descriptors */
+struct ngcmd {
+ int (*func)(int ac, char **av); /* command function */
+ const char *cmd; /* command usage */
+ const char *desc; /* description */
+ const char *help; /* help text */
+ const char *aliases[MAX_CMD_ALIAS]; /* command aliases */
+};
+
+/* Command return values */
+#define CMDRTN_OK 0
+#define CMDRTN_USAGE 1
+#define CMDRTN_ERROR 2
+#define CMDRTN_QUIT 3
+
+/* Available commands */
+extern const struct ngcmd config_cmd;
+extern const struct ngcmd connect_cmd;
+extern const struct ngcmd debug_cmd;
+extern const struct ngcmd dot_cmd;
+extern const struct ngcmd help_cmd;
+extern const struct ngcmd list_cmd;
+extern const struct ngcmd mkpeer_cmd;
+extern const struct ngcmd msg_cmd;
+extern const struct ngcmd name_cmd;
+extern const struct ngcmd read_cmd;
+extern const struct ngcmd rmhook_cmd;
+extern const struct ngcmd show_cmd;
+extern const struct ngcmd shutdown_cmd;
+extern const struct ngcmd status_cmd;
+extern const struct ngcmd types_cmd;
+extern const struct ngcmd write_cmd;
+extern const struct ngcmd quit_cmd;
+
+/* Data and control sockets */
+extern int csock, dsock;
+
+/* Misc functions */
+extern void MsgRead(void);
+extern void DumpAscii(const u_char *buf, int len);
+
diff --git a/usr.sbin/ngctl/rmhook.c b/usr.sbin/ngctl/rmhook.c
new file mode 100644
index 0000000..9a47a9d
--- /dev/null
+++ b/usr.sbin/ngctl/rmhook.c
@@ -0,0 +1,86 @@
+
+/*
+ * rmhook.c
+ *
+ * Copyright (c) 1996-1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <err.h>
+#include <netgraph.h>
+#include <stdio.h>
+
+#include "ngctl.h"
+
+static int RmHookCmd(int ac, char **av);
+
+const struct ngcmd rmhook_cmd = {
+ RmHookCmd,
+ "rmhook [path] <hook>",
+ "Disconnect hook \"hook\" of the node at \"path\"",
+ "The rmhook command forces the node at \"path\" to break the link"
+ " formed by its hook \"hook\", if connected."
+ " If \"path\" is omitted then \".\" is assumed.",
+ { "disconnect" }
+};
+
+static int
+RmHookCmd(int ac, char **av)
+{
+ struct ngm_rmhook rmh;
+ const char *path = ".";
+
+ /* Get arguments */
+ switch (ac) {
+ case 3:
+ path = av[1];
+ ac--;
+ av++;
+ /* FALLTHROUGH */
+ case 2:
+ snprintf(rmh.ourhook, sizeof(rmh.ourhook), "%s", av[1]);
+ break;
+ default:
+ return (CMDRTN_USAGE);
+ }
+
+ /* Send message */
+ if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
+ NGM_RMHOOK, &rmh, sizeof(rmh)) < 0) {
+ warn("send msg");
+ return (CMDRTN_ERROR);
+ }
+ return (CMDRTN_OK);
+}
+
diff --git a/usr.sbin/ngctl/show.c b/usr.sbin/ngctl/show.c
new file mode 100644
index 0000000..26eea04
--- /dev/null
+++ b/usr.sbin/ngctl/show.c
@@ -0,0 +1,139 @@
+
+/*
+ * show.c
+ *
+ * Copyright (c) 1996-1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <err.h>
+#include <netgraph.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "ngctl.h"
+
+#define FMT " %-15s %-15s %-12s %-15s %-15s\n"
+#define UNNAMED "<unnamed>"
+#define NOSTATUS "<no status>"
+
+static int ShowCmd(int ac, char **av);
+
+const struct ngcmd show_cmd = {
+ ShowCmd,
+ "show [-n] <path>",
+ "Show information about the node at <path>",
+ "If the -n flag is given, hooks are not listed.",
+ { "inquire", "info" }
+};
+
+static int
+ShowCmd(int ac, char **av)
+{
+ char *path;
+ struct ng_mesg *resp;
+ struct hooklist *hlist;
+ struct nodeinfo *ninfo;
+ int ch, no_hooks = 0;
+
+ /* Get options */
+ optind = 1;
+ while ((ch = getopt(ac, av, "n")) != -1) {
+ switch (ch) {
+ case 'n':
+ no_hooks = 1;
+ break;
+ case '?':
+ default:
+ return (CMDRTN_USAGE);
+ break;
+ }
+ }
+ ac -= optind;
+ av += optind;
+
+ /* Get arguments */
+ switch (ac) {
+ case 1:
+ path = av[0];
+ break;
+ default:
+ return (CMDRTN_USAGE);
+ }
+
+ /* Get node info and hook list */
+ if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
+ NGM_LISTHOOKS, NULL, 0) < 0) {
+ warn("send msg");
+ return (CMDRTN_ERROR);
+ }
+ if (NgAllocRecvMsg(csock, &resp, NULL) < 0) {
+ warn("recv msg");
+ return (CMDRTN_ERROR);
+ }
+
+ /* Show node information */
+ hlist = (struct hooklist *) resp->data;
+ ninfo = &hlist->nodeinfo;
+ if (!*ninfo->name)
+ snprintf(ninfo->name, sizeof(ninfo->name), "%s", UNNAMED);
+ printf(" Name: %-15s Type: %-15s ID: %08x Num hooks: %d\n",
+ ninfo->name, ninfo->type, ninfo->id, ninfo->hooks);
+ if (!no_hooks && ninfo->hooks > 0) {
+ u_int k;
+
+ printf(FMT, "Local hook", "Peer name",
+ "Peer type", "Peer ID", "Peer hook");
+ printf(FMT, "----------", "---------",
+ "---------", "-------", "---------");
+ for (k = 0; k < ninfo->hooks; k++) {
+ struct linkinfo *const link = &hlist->link[k];
+ struct nodeinfo *const peer = &hlist->link[k].nodeinfo;
+ char idbuf[20];
+
+ if (!*peer->name) {
+ snprintf(peer->name, sizeof(peer->name),
+ "%s", UNNAMED);
+ }
+ snprintf(idbuf, sizeof(idbuf), "%08x", peer->id);
+ printf(FMT, link->ourhook, peer->name,
+ peer->type, idbuf, link->peerhook);
+ }
+ }
+ free(resp);
+ return (CMDRTN_OK);
+}
+
+
diff --git a/usr.sbin/ngctl/shutdown.c b/usr.sbin/ngctl/shutdown.c
new file mode 100644
index 0000000..b06beb5
--- /dev/null
+++ b/usr.sbin/ngctl/shutdown.c
@@ -0,0 +1,79 @@
+
+/*
+ * shutdown.c
+ *
+ * Copyright (c) 1996-1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <err.h>
+#include <netgraph.h>
+#include <unistd.h>
+
+#include "ngctl.h"
+
+static int ShutdownCmd(int ac, char **av);
+
+const struct ngcmd shutdown_cmd = {
+ ShutdownCmd,
+ "shutdown <path>",
+ "Shutdown the node at <path>",
+ NULL,
+ { "kill", "rmnode" }
+};
+
+static int
+ShutdownCmd(int ac, char **av)
+{
+ char *path;
+
+ /* Get arguments */
+ switch (ac) {
+ case 2:
+ path = av[1];
+ break;
+ default:
+ return (CMDRTN_USAGE);
+ }
+
+ /* Shutdown node */
+ if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
+ NGM_SHUTDOWN, NULL, 0) < 0) {
+ warn("shutdown");
+ return (CMDRTN_ERROR);
+ }
+ return (CMDRTN_OK);
+}
+
+
diff --git a/usr.sbin/ngctl/status.c b/usr.sbin/ngctl/status.c
new file mode 100644
index 0000000..7827c63
--- /dev/null
+++ b/usr.sbin/ngctl/status.c
@@ -0,0 +1,101 @@
+
+/*
+ * status.c
+ *
+ * Copyright (c) 1996-1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <err.h>
+#include <errno.h>
+#include <netgraph.h>
+#include <stdio.h>
+
+#include "ngctl.h"
+
+#define NOSTATUS "<no status>"
+
+static int StatusCmd(int ac, char **av);
+
+const struct ngcmd status_cmd = {
+ StatusCmd,
+ "status <path>",
+ "Get human readable status information from the node at <path>",
+ NULL,
+ { NULL }
+};
+
+static int
+StatusCmd(int ac, char **av)
+{
+ u_char sbuf[sizeof(struct ng_mesg) + NG_TEXTRESPONSE];
+ struct ng_mesg *const resp = (struct ng_mesg *) sbuf;
+ char *const status = (char *) resp->data;
+ char *path;
+ int nostat = 0;
+
+ /* Get arguments */
+ switch (ac) {
+ case 2:
+ path = av[1];
+ break;
+ default:
+ return (CMDRTN_USAGE);
+ }
+
+ /* Get node status summary */
+ if (NgSendMsg(csock, path, NGM_GENERIC_COOKIE,
+ NGM_TEXT_STATUS, NULL, 0) < 0) {
+ switch (errno) {
+ case EINVAL:
+ nostat = 1;
+ break;
+ default:
+ warn("send msg");
+ return (CMDRTN_ERROR);
+ }
+ } else {
+ if (NgRecvMsg(csock, resp, sizeof(sbuf), NULL) < 0
+ || (resp->header.flags & NGF_RESP) == 0)
+ nostat = 1;
+ }
+
+ /* Show it */
+ if (nostat)
+ printf("No status available for \"%s\"\n", path);
+ else
+ printf("Status for \"%s\":\n%s\n", path, status);
+ return (CMDRTN_OK);
+}
+
diff --git a/usr.sbin/ngctl/types.c b/usr.sbin/ngctl/types.c
new file mode 100644
index 0000000..5ed4b53
--- /dev/null
+++ b/usr.sbin/ngctl/types.c
@@ -0,0 +1,101 @@
+
+/*
+ * types.c
+ *
+ * Copyright (c) 1996-1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <err.h>
+#include <netgraph.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "ngctl.h"
+
+static int TypesCmd(int ac, char **av);
+
+const struct ngcmd types_cmd = {
+ TypesCmd,
+ "types",
+ "Show information about all installed node types",
+ NULL,
+ { NULL }
+};
+
+static int
+TypesCmd(int ac, char **av __unused)
+{
+ struct ng_mesg *resp;
+ struct typelist *tlist;
+ int rtn = CMDRTN_OK;
+ u_int k;
+
+ /* Get arguments */
+ switch (ac) {
+ case 1:
+ break;
+ default:
+ return (CMDRTN_USAGE);
+ }
+
+ /* Get list of types */
+ if (NgSendMsg(csock, ".", NGM_GENERIC_COOKIE,
+ NGM_LISTTYPES, NULL, 0) < 0) {
+ warn("send msg");
+ return (CMDRTN_ERROR);
+ }
+ if (NgAllocRecvMsg(csock, &resp, NULL) < 0) {
+ warn("recv msg");
+ return (CMDRTN_ERROR);
+ }
+
+ /* Show each type */
+ tlist = (struct typelist *) resp->data;
+ printf("There are %d total types:\n", tlist->numtypes);
+ if (tlist->numtypes > 0) {
+ printf("%15s Number of living nodes\n", "Type name");
+ printf("%15s ----------------------\n", "---------");
+ }
+ for (k = 0; k < tlist->numtypes; k++) {
+ struct typeinfo *const ti = &tlist->typeinfo[k];
+ printf("%15s %5d\n", ti->type_name, ti->numnodes);
+ }
+
+ /* Done */
+ free(resp);
+ return (rtn);
+}
+
diff --git a/usr.sbin/ngctl/write.c b/usr.sbin/ngctl/write.c
new file mode 100644
index 0000000..4114211
--- /dev/null
+++ b/usr.sbin/ngctl/write.c
@@ -0,0 +1,121 @@
+
+/*
+ * write.c
+ *
+ * Copyright (c) 2002 Archie L. Cobbs
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Archie L. Cobbs;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY ARCHIE L. COBBS AS IS", AND TO
+ * THE MAXIMUM EXTENT PERMITTED BY LAW, ARCHIE L. COBBS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * ARCHIE L. COBBS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL ARCHIE L. COBBS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ARCHIE L. COBBS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <netgraph/ng_socket.h>
+
+#include "ngctl.h"
+
+#define BUF_SIZE 8192
+
+static int WriteCmd(int ac, char **av);
+
+const struct ngcmd write_cmd = {
+ WriteCmd,
+ "write hook < -f file | byte ... >",
+ "Send a data packet down the hook named by \"hook\".",
+ "The data may be contained in a file, or may be described directly"
+ " on the command line by supplying a sequence of bytes.",
+ { "w" }
+};
+
+static int
+WriteCmd(int ac, char **av)
+{
+ u_int32_t sagbuf[64];
+ struct sockaddr_ng *sag = (struct sockaddr_ng *)sagbuf;
+ u_char buf[BUF_SIZE];
+ const char *hook;
+ FILE *fp;
+ u_int len;
+ int byte;
+ int i;
+
+ /* Get arguments */
+ if (ac < 3)
+ return (CMDRTN_USAGE);
+ hook = av[1];
+
+ /* Get data */
+ if (strcmp(av[2], "-f") == 0) {
+ if (ac != 4)
+ return (CMDRTN_USAGE);
+ if ((fp = fopen(av[3], "r")) == NULL) {
+ warn("can't read file \"%s\"", av[3]);
+ return (CMDRTN_ERROR);
+ }
+ if ((len = fread(buf, 1, sizeof(buf), fp)) == 0) {
+ if (ferror(fp))
+ warn("can't read file \"%s\"", av[3]);
+ else
+ warnx("file \"%s\" is empty", av[3]);
+ fclose(fp);
+ return (CMDRTN_ERROR);
+ }
+ fclose(fp);
+ } else {
+ for (i = 2, len = 0; i < ac && len < sizeof(buf); i++, len++) {
+ if (sscanf(av[i], "%i", &byte) != 1
+ || (byte < -128 || byte > 255)) {
+ warnx("invalid byte \"%s\"", av[i]);
+ return (CMDRTN_ERROR);
+ }
+ buf[len] = (u_char)byte;
+ }
+ if (len == 0)
+ return (CMDRTN_USAGE);
+ }
+
+ /* Send data */
+ sag->sg_len = 3 + strlen(hook);
+ sag->sg_family = AF_NETGRAPH;
+ strlcpy(sag->sg_data, hook, sizeof(sagbuf) - 2);
+ if (sendto(dsock, buf, len,
+ 0, (struct sockaddr *)sag, sag->sg_len) == -1) {
+ warn("writing to hook \"%s\"", hook);
+ return (CMDRTN_ERROR);
+ }
+
+ /* Done */
+ return (CMDRTN_OK);
+}
+
diff --git a/usr.sbin/nghook/Makefile b/usr.sbin/nghook/Makefile
new file mode 100644
index 0000000..73f2df3
--- /dev/null
+++ b/usr.sbin/nghook/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+# $Whistle: Makefile,v 1.4 1999/01/16 04:44:33 archie Exp $
+
+PROG= nghook
+MAN= nghook.8
+SRCS= main.c
+WARNS?= 6
+
+DPADD= ${LIBNETGRAPH}
+LDADD= -lnetgraph
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/nghook/main.c b/usr.sbin/nghook/main.c
new file mode 100644
index 0000000..700ca5e
--- /dev/null
+++ b/usr.sbin/nghook/main.c
@@ -0,0 +1,309 @@
+/*
+ * main.c
+ *
+ * Copyright (c) 1996-1999 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * $Whistle: main.c,v 1.9 1999/01/20 00:26:26 archie Exp $
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <sysexits.h>
+#include <errno.h>
+#include <err.h>
+#include <stringlist.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/select.h>
+
+#include <netgraph.h>
+
+#define DEFAULT_HOOKNAME "debug"
+#define NG_SOCK_HOOK_NAME "hook"
+
+#define BUF_SIZE (64 * 1024)
+
+static void WriteAscii(u_char * buf, int len);
+static void Usage(void);
+static void send_msgs(int, const char *);
+
+static int outfd = STDOUT_FILENO;
+static int infd = STDIN_FILENO;
+
+static StringList *msgs;
+
+/*
+ * main()
+ */
+int
+main(int ac, char *av[])
+{
+ struct ngm_connect ngc;
+ const char *path = NULL;
+ const char *hook = DEFAULT_HOOKNAME;
+ int csock, dsock;
+ int asciiFlag = 0;
+ int loopFlag = 0;
+ int noInput = 0;
+ int execFlag = 0;
+ int ch;
+
+ if ((msgs = sl_init()) == NULL)
+ err(EX_OSERR, NULL);
+
+ /* Parse flags */
+ while ((ch = getopt(ac, av, "aedlm:nsS")) != -1) {
+ switch (ch) {
+ case 'a':
+ asciiFlag = 1;
+ break;
+ case 'd':
+ NgSetDebug(NgSetDebug(-1) + 1);
+ break;
+ case 'e':
+ execFlag = 1;
+ break;
+ case 'l':
+ loopFlag = 1;
+ break;
+ case 'n':
+ noInput = 1;
+ break;
+ case 'm':
+ if (sl_add(msgs, optarg) == -1)
+ err(EX_OSERR, NULL);
+ break;
+ case 's':
+ outfd = STDIN_FILENO;
+ break;
+ case 'S':
+ infd = STDOUT_FILENO;
+ break;
+ case '?':
+ default:
+ Usage();
+ }
+ }
+ ac -= optind;
+ av += optind;
+
+ if (execFlag) {
+ if (asciiFlag || loopFlag) {
+ fprintf(stderr, "conflicting options\n");
+ Usage();
+ }
+ if (ac < 3)
+ Usage();
+ path = av[0];
+ hook = av[1];
+ av += 2;
+ ac -= 2;
+ } else {
+ /* Get params */
+ switch (ac) {
+ case 2:
+ hook = av[1];
+ /* FALLTHROUGH */
+ case 1:
+ path = av[0];
+ break;
+ default:
+ Usage();
+ }
+ }
+
+ /* Get sockets */
+ if (NgMkSockNode(NULL, &csock, &dsock) < 0)
+ errx(EX_OSERR, "can't get sockets");
+
+ /* Connect socket node to specified node */
+ snprintf(ngc.path, sizeof(ngc.path), "%s", path);
+ snprintf(ngc.ourhook, sizeof(ngc.ourhook), NG_SOCK_HOOK_NAME);
+ snprintf(ngc.peerhook, sizeof(ngc.peerhook), "%s", hook);
+
+ if (NgSendMsg(csock, ".",
+ NGM_GENERIC_COOKIE, NGM_CONNECT, &ngc, sizeof(ngc)) < 0)
+ errx(EX_OSERR, "can't connect to node");
+
+ if (execFlag) {
+ /* move dsock to fd 0 and 1 */
+ (void)close(0);
+ (void)close(1);
+ if (!noInput)
+ (void)dup2(dsock, 0);
+ (void)dup2(dsock, 1);
+
+ send_msgs(csock, path);
+
+ /* try executing the program */
+ (void)execv(av[0], av);
+ err(EX_OSERR, "%s", av[0]);
+
+ } else
+ send_msgs(csock, path);
+
+ /* Close standard input if not reading from it */
+ if (noInput)
+ fclose(stdin);
+
+ /* Relay data */
+ while (1) {
+ fd_set rfds;
+
+ /* Setup bits */
+ FD_ZERO(&rfds);
+ if (!noInput)
+ FD_SET(infd, &rfds);
+ FD_SET(dsock, &rfds);
+
+ /* Wait for something to happen */
+ if (select(FD_SETSIZE, &rfds, NULL, NULL, NULL) < 0)
+ err(EX_OSERR, "select");
+
+ /* Check data from socket */
+ if (FD_ISSET(dsock, &rfds)) {
+ char buf[BUF_SIZE];
+ int rl, wl;
+
+ /* Read packet from socket */
+ if ((rl = NgRecvData(dsock,
+ buf, sizeof(buf), NULL)) < 0)
+ err(EX_OSERR, "read(hook)");
+ if (rl == 0)
+ errx(EX_OSERR, "read EOF from hook?!");
+
+ /* Write packet to stdout */
+ if (asciiFlag)
+ WriteAscii((u_char *) buf, rl);
+ else if ((wl = write(outfd, buf, rl)) != rl) {
+ if (wl < 0) {
+ err(EX_OSERR, "write(stdout)");
+ } else {
+ errx(EX_OSERR,
+ "stdout: read %d, wrote %d",
+ rl, wl);
+ }
+ }
+ /* Loopback */
+ if (loopFlag) {
+ if (NgSendData(dsock, NG_SOCK_HOOK_NAME, buf, rl) < 0)
+ err(EX_OSERR, "write(hook)");
+ }
+ }
+
+ /* Check data from stdin */
+ if (FD_ISSET(infd, &rfds)) {
+ char buf[BUF_SIZE];
+ int rl;
+
+ /* Read packet from stdin */
+ if ((rl = read(infd, buf, sizeof(buf))) < 0)
+ err(EX_OSERR, "read(stdin)");
+ if (rl == 0)
+ errx(EX_OSERR, "EOF(stdin)");
+
+ /* Write packet to socket */
+ if (NgSendData(dsock, NG_SOCK_HOOK_NAME, buf, rl) < 0)
+ err(EX_OSERR, "write(hook)");
+ }
+ }
+}
+
+/*
+ * Dump data in hex and ASCII form
+ */
+static void
+WriteAscii(u_char *buf, int len)
+{
+ char ch, sbuf[100];
+ int k, count;
+
+ for (count = 0; count < len; count += 16) {
+ snprintf(sbuf, sizeof(sbuf), "%04x: ", count);
+ for (k = 0; k < 16; k++)
+ if (count + k < len)
+ snprintf(sbuf + strlen(sbuf),
+ sizeof(sbuf) - strlen(sbuf),
+ "%02x ", buf[count + k]);
+ else
+ snprintf(sbuf + strlen(sbuf),
+ sizeof(sbuf) - strlen(sbuf), " ");
+ snprintf(sbuf + strlen(sbuf), sizeof(sbuf) - strlen(sbuf), " ");
+ for (k = 0; k < 16; k++)
+ if (count + k < len) {
+ ch = isprint(buf[count + k]) ?
+ buf[count + k] : '.';
+ snprintf(sbuf + strlen(sbuf),
+ sizeof(sbuf) - strlen(sbuf), "%c", ch);
+ } else
+ snprintf(sbuf + strlen(sbuf),
+ sizeof(sbuf) - strlen(sbuf), " ");
+ snprintf(sbuf + strlen(sbuf),
+ sizeof(sbuf) - strlen(sbuf), "\n");
+ (void) write(outfd, sbuf, strlen(sbuf));
+ }
+ ch = '\n';
+ write(outfd, &ch, 1);
+}
+
+/*
+ * Display usage and exit
+ */
+static void
+Usage(void)
+{
+ fprintf(stderr, "usage: nghook [-adlnsS] path [hookname]\n");
+ fprintf(stderr, " or: nghook -e [-n] [-m msg]* path hookname prog "
+ "[args...]\n");
+ exit(EX_USAGE);
+}
+
+/*
+ * Send the messages to the node
+ */
+static void
+send_msgs(int cs, const char *path)
+{
+ u_int i;
+
+ for (i = 0; i < msgs->sl_cur; i++)
+ if (NgSendAsciiMsg(cs, path, "%s", msgs->sl_str[i]) == -1)
+ err(EX_OSERR, "sending message '%s'", msgs->sl_str[i]);
+}
diff --git a/usr.sbin/nghook/nghook.8 b/usr.sbin/nghook/nghook.8
new file mode 100644
index 0000000..7d2c7fa
--- /dev/null
+++ b/usr.sbin/nghook/nghook.8
@@ -0,0 +1,145 @@
+.\" Copyright (c) 1996-1999 Whistle Communications, Inc.
+.\" All rights reserved.
+.\"
+.\" Subject to the following obligations and disclaimer of warranty, use and
+.\" redistribution of this software, in source or object code forms, with or
+.\" without modifications are expressly permitted by Whistle Communications;
+.\" provided, however, that:
+.\" 1. Any and all reproductions of the source or object code must include the
+.\" copyright notice above and the following disclaimer of warranties; and
+.\" 2. No rights are granted, in any manner or form, to use Whistle
+.\" Communications, Inc. trademarks, including the mark "WHISTLE
+.\" COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+.\" such appears in the above copyright notice or in the software.
+.\"
+.\" THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+.\" TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+.\" REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+.\" INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+.\" WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+.\" REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+.\" SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+.\" IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+.\" RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+.\" WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+.\" PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+.\" SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+.\" OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\" $Whistle: nghook.8,v 1.4 1999/01/20 03:19:45 archie Exp $
+.\"
+.Dd October 24, 2003
+.Dt NGHOOK 8
+.Os
+.Sh NAME
+.Nm nghook
+.Nd connect to a
+.Xr netgraph 4
+node
+.Sh SYNOPSIS
+.Nm
+.Op Fl adlnSs
+.Op Fl m Ar msg
+.Ar path
+.Op Ar hookname
+.Nm
+.Fl e
+.Op Fl n
+.Op Fl m Ar msg
+.Ar path
+.Ar hookname
+.Ar program
+.Op Ar args ...
+.Sh DESCRIPTION
+The
+.Nm
+utility creates a
+.Xr ng_socket 4
+socket type node and connects it to hook
+.Ar hookname
+of the node found at
+.Ar path .
+If
+.Ar hookname
+is omitted,
+.Dq Li debug
+is assumed.
+.Pp
+If the
+.Fl e
+option is given, the third argument is interpreted as the path to a program,
+and this program is executed with the remaining arguments as its arguments.
+Before executing, the program Netgraph messages (specified by the
+.Fl m
+option) are sent to the node.
+The program is executed with its standard input (unless closed by
+.Fl n )
+and output connected to the hook.
+.Pp
+If the
+.Fl e
+option is not given, all data written to standard input is sent
+to the node, and all data received from the node is relayed
+to standard output.
+Messages specified with
+.Fl m
+are sent to the node before the loop is entered.
+The
+.Nm
+utility exits when
+.Dv EOF
+is detected on standard input in this case.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl a
+Output each packet read in human-readable decoded
+.Tn ASCII
+form instead of raw binary.
+.It Fl d
+Increase the debugging verbosity level.
+.It Fl e
+Execute the program specified by the third argument.
+.It Fl l
+Loops all received data back to the hook in addition to writing it
+to standard output.
+.It Fl m Ar msg
+Before executing the program (in
+.Fl e
+mode) send the given ASCII control message to the node.
+This option may be given more than once.
+.It Fl n
+Do not attempt to read any data from standard input.
+The
+.Nm
+utility will continue reading from the node until stopped by a signal.
+.It Fl S
+Use file descriptor 0 for output instead of the default 1.
+.It Fl s
+Use file descriptor 1 for input instead of the default 0.
+.El
+.Sh SEE ALSO
+.Xr netgraph 3 ,
+.Xr netgraph 4 ,
+.Xr ngctl 8
+.Sh HISTORY
+The
+.Nm netgraph
+system was designed and first implemented at Whistle Communications, Inc.\&
+in a version of
+.Fx 2.2
+customized for the Whistle InterJet.
+.Sh AUTHORS
+.An Archie Cobbs Aq archie@whistle.com
+.Sh BUGS
+Although all input is read in unbuffered mode,
+there is no way to control the packetization of the input.
+.Pp
+If the node sends a response to a message (specified by
+.Fl m ) ,
+this response is lost.
diff --git a/usr.sbin/nologin/Makefile b/usr.sbin/nologin/Makefile
new file mode 100644
index 0000000..0b4c0db
--- /dev/null
+++ b/usr.sbin/nologin/Makefile
@@ -0,0 +1,16 @@
+# @(#)Makefile 8.2 (Berkeley) 4/22/94
+# $FreeBSD$
+
+PROG= nologin
+MAN= nologin.5 nologin.8
+
+SYMLINKS= ${BINDIR}/nologin /sbin/nologin
+
+# It is important that nologin be statically linked for security
+# reasons. A dynamic non-setuid binary can be linked against a trojan
+# libc by setting LD_LIBRARY_PATH appropriately. Both sshd(8) and
+# login(1) make it possible to log in with an unsanitized environment,
+# rendering a dynamic nologin binary virtually useless.
+NO_SHARED= YES
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/nologin/nologin.5 b/usr.sbin/nologin/nologin.5
new file mode 100644
index 0000000..f8a21be
--- /dev/null
+++ b/usr.sbin/nologin/nologin.5
@@ -0,0 +1,96 @@
+.\" 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.
+.\"
+.\" @(#)nologin.8 8.1 (Berkeley) 6/19/93
+.\" $FreeBSD$
+.\"
+.Dd May 10, 2007
+.Dt NOLOGIN 5
+.Os
+.Sh NAME
+.Nm nologin
+.Nd disallow logins
+.Sh DESCRIPTION
+Programs such as
+.Xr login 1
+disallow logins if the
+.Nm
+file exists.
+The programs display the contents of
+.Nm
+to the user if possible and interrupt the login sequence.
+This makes it simple to temporarily prevent incoming logins systemwide.
+.Pp
+To disable logins on a per-account basis,
+investigate
+.Xr nologin 8 .
+.Sh SECURITY
+The
+.Nm
+file is ignored for user root by default.
+.Sh IMPLEMENTATION NOTES
+The
+.Nm
+feature is implemented through
+.Xr login.conf 5 ,
+which allows to change the pathname of the
+file and to extend the list of users
+exempt from temporary login restriction.
+.Pp
+PAM-aware programs can be selectively configured to respect
+.Nm
+using the
+.Xr pam_nologin 8
+module via
+.Xr pam.conf 5 .
+.Pp
+The
+.Nm
+file will be removed at system boot if it resides in
+.Pa /var/run
+and
+.Va cleanvar_enable
+is set to
+.Dq Li YES
+in
+.Xr rc.conf 5 ,
+which is default.
+Therefore system reboot can effectively re-enable logins.
+.Sh FILES
+.Bl -tag -width ".Pa /var/run/nologin" -compact
+.It Pa /var/run/nologin
+default location of
+.Nm
+.El
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr login.conf 5 ,
+.Xr pam.conf 5 ,
+.Xr rc.conf 5 ,
+.Xr nologin 8 ,
+.Xr pam_nologin 8 ,
+.Xr shutdown 8
diff --git a/usr.sbin/nologin/nologin.8 b/usr.sbin/nologin/nologin.8
new file mode 100644
index 0000000..d03aa33
--- /dev/null
+++ b/usr.sbin/nologin/nologin.8
@@ -0,0 +1,57 @@
+.\" 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.
+.\"
+.\" @(#)nologin.8 8.1 (Berkeley) 6/19/93
+.\" $FreeBSD$
+.\"
+.Dd June 19, 1993
+.Dt NOLOGIN 8
+.Os
+.Sh NAME
+.Nm nologin
+.Nd politely refuse a login
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+utility displays a message that an account is not available and
+exits non-zero.
+It is intended as a replacement shell field for accounts that
+have been disabled.
+.Pp
+To disable all logins,
+investigate
+.Xr nologin 5 .
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr nologin 5
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.4 .
diff --git a/usr.sbin/nologin/nologin.c b/usr.sbin/nologin/nologin.c
new file mode 100644
index 0000000..658637e
--- /dev/null
+++ b/usr.sbin/nologin/nologin.c
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 2004 The FreeBSD Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdio.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#define MESSAGE "This account is currently not available.\n"
+
+int
+main(__unused int argc, __unused char *argv[])
+{
+ const char *user, *tt;
+
+ if ((tt = ttyname(0)) == NULL)
+ tt = "UNKNOWN";
+ if ((user = getlogin()) == NULL)
+ user = "UNKNOWN";
+ openlog("nologin", LOG_CONS, LOG_AUTH);
+ syslog(LOG_CRIT, "Attempted login by %s on %s", user, tt);
+ closelog();
+
+ printf("%s", MESSAGE);
+ return 1;
+}
diff --git a/usr.sbin/nscd/Makefile b/usr.sbin/nscd/Makefile
new file mode 100644
index 0000000..69fd126
--- /dev/null
+++ b/usr.sbin/nscd/Makefile
@@ -0,0 +1,17 @@
+# $FreeBSD$
+
+PROG= nscd
+MAN= nscd.conf.5 nscd.8
+
+WARNS?= 2
+CFLAGS+=-fno-strict-aliasing
+SRCS= agent.c nscd.c nscdcli.c cachelib.c cacheplcs.c debug.c log.c \
+ config.c query.c mp_ws_query.c mp_rs_query.c singletons.c protocol.c \
+ parser.c
+CFLAGS+= -DCONFIG_PATH="\"${PREFIX}/etc/nscd.conf\""
+DPADD= ${LIBM} ${LIBPTHREAD} ${LIBUTIL}
+LDADD= -lm -lpthread -lutil
+
+.PATH: ${.CURDIR}/agents
+.include "${.CURDIR}/agents/Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/usr.sbin/nscd/agent.c b/usr.sbin/nscd/agent.c
new file mode 100644
index 0000000..a193f95
--- /dev/null
+++ b/usr.sbin/nscd/agent.c
@@ -0,0 +1,126 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include "agent.h"
+#include "debug.h"
+
+static int
+agent_cmp_func(const void *a1, const void *a2)
+{
+ struct agent const *ap1 = *((struct agent const **)a1);
+ struct agent const *ap2 = *((struct agent const **)a2);
+ int res;
+
+ res = strcmp(ap1->name, ap2->name);
+ if (res == 0) {
+ if (ap1->type == ap2->type)
+ res = 0;
+ else if (ap1->type < ap2->type)
+ res = -1;
+ else
+ res = 1;
+ }
+
+ return (res);
+}
+
+struct agent_table *
+init_agent_table()
+{
+ struct agent_table *retval;
+
+ TRACE_IN(init_agent_table);
+ retval = (struct agent_table *)calloc(1, sizeof(struct agent_table));
+ assert(retval != NULL);
+
+ TRACE_OUT(init_agent_table);
+ return (retval);
+}
+
+void
+register_agent(struct agent_table *at, struct agent *a)
+{
+ struct agent **new_agents;
+ size_t new_agents_num;
+
+ TRACE_IN(register_agent);
+ assert(at != NULL);
+ assert(a != NULL);
+ new_agents_num = at->agents_num + 1;
+ new_agents = (struct agent **)malloc(sizeof(struct agent *) *
+ new_agents_num);
+ assert(new_agents != NULL);
+ memcpy(new_agents, at->agents, at->agents_num * sizeof(struct agent *));
+ new_agents[new_agents_num - 1] = a;
+ qsort(new_agents, new_agents_num, sizeof(struct agent *),
+ agent_cmp_func);
+
+ free(at->agents);
+ at->agents = new_agents;
+ at->agents_num = new_agents_num;
+ TRACE_OUT(register_agent);
+}
+
+struct agent *
+find_agent(struct agent_table *at, const char *name, enum agent_type type)
+{
+ struct agent **res;
+ struct agent model, *model_p;
+
+ TRACE_IN(find_agent);
+ model.name = (char *)name;
+ model.type = type;
+ model_p = &model;
+ res = bsearch(&model_p, at->agents, at->agents_num,
+ sizeof(struct agent *), agent_cmp_func);
+
+ TRACE_OUT(find_agent);
+ return ( res == NULL ? NULL : *res);
+}
+
+void
+destroy_agent_table(struct agent_table *at)
+{
+ size_t i;
+
+ TRACE_IN(destroy_agent_table);
+ assert(at != NULL);
+ for (i = 0; i < at->agents_num; ++i) {
+ free(at->agents[i]->name);
+ free(at->agents[i]);
+ }
+
+ free(at->agents);
+ free(at);
+ TRACE_OUT(destroy_agent_table);
+}
diff --git a/usr.sbin/nscd/agent.h b/usr.sbin/nscd/agent.h
new file mode 100644
index 0000000..ebe8974
--- /dev/null
+++ b/usr.sbin/nscd/agent.h
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 __NSCD_AGENT_H__
+#define __NSCD_AGENT_H__
+
+/*
+ * Agents are used to perform the actual lookups from the caching daemon.
+ * There are two types of daemons: for common requests and for multipart
+ * requests.
+ * All agents are stored in the agents table, which is the singleton.
+ */
+
+enum agent_type {
+ COMMON_AGENT = 0,
+ MULTIPART_AGENT = 1
+};
+
+struct agent {
+ char *name;
+ enum agent_type type;
+};
+
+struct common_agent {
+ struct agent parent;
+ int (*lookup_func)(const char *, size_t, char **, size_t *);
+};
+
+struct multipart_agent {
+ struct agent parent;
+ void *(*mp_init_func)();
+ int (*mp_lookup_func)(char **, size_t *, void *);
+ void (*mp_destroy_func)(void *);
+};
+
+struct agent_table {
+ struct agent **agents;
+ size_t agents_num;
+};
+
+extern struct agent_table *init_agent_table();
+extern void register_agent(struct agent_table *, struct agent *);
+extern struct agent *find_agent(struct agent_table *, const char *,
+ enum agent_type);
+extern void destroy_agent_table(struct agent_table *);
+
+#endif
diff --git a/usr.sbin/nscd/agents/Makefile.inc b/usr.sbin/nscd/agents/Makefile.inc
new file mode 100644
index 0000000..1be32e1
--- /dev/null
+++ b/usr.sbin/nscd/agents/Makefile.inc
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+SRCS += passwd.c group.c services.c
diff --git a/usr.sbin/nscd/agents/group.c b/usr.sbin/nscd/agents/group.c
new file mode 100644
index 0000000..ba61db7
--- /dev/null
+++ b/usr.sbin/nscd/agents/group.c
@@ -0,0 +1,259 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <assert.h>
+#include <nsswitch.h>
+#include <grp.h>
+#include <string.h>
+#include <stdlib.h>
+#include "../debug.h"
+#include "passwd.h"
+
+static int group_marshal_func(struct group *, char *, size_t *);
+static int group_lookup_func(const char *, size_t, char **, size_t *);
+static void *group_mp_init_func();
+static int group_mp_lookup_func(char **, size_t *, void *);
+static void group_mp_destroy_func(void *);
+
+static int
+group_marshal_func(struct group *grp, char *buffer, size_t *buffer_size)
+{
+ struct group new_grp;
+ size_t desired_size, size, mem_size;
+ char *p, **mem;
+
+ TRACE_IN(group_marshal_func);
+ desired_size = ALIGNBYTES + sizeof(struct group) + sizeof(char *);
+
+ if (grp->gr_name != NULL)
+ desired_size += strlen(grp->gr_name) + 1;
+ if (grp->gr_passwd != NULL)
+ desired_size += strlen(grp->gr_passwd) + 1;
+
+ if (grp->gr_mem != NULL) {
+ mem_size = 0;
+ for (mem = grp->gr_mem; *mem; ++mem) {
+ desired_size += strlen(*mem) + 1;
+ ++mem_size;
+ }
+
+ desired_size += ALIGNBYTES + (mem_size + 1) * sizeof(char *);
+ }
+
+ if ((desired_size > *buffer_size) || (buffer == NULL)) {
+ *buffer_size = desired_size;
+ TRACE_OUT(group_marshal_func);
+ return (NS_RETURN);
+ }
+
+ memcpy(&new_grp, grp, sizeof(struct group));
+ memset(buffer, 0, desired_size);
+
+ *buffer_size = desired_size;
+ p = buffer + sizeof(struct group) + sizeof(char *);
+ memcpy(buffer + sizeof(struct group), &p, sizeof(char *));
+ p = (char *)ALIGN(p);
+
+ if (new_grp.gr_name != NULL) {
+ size = strlen(new_grp.gr_name);
+ memcpy(p, new_grp.gr_name, size);
+ new_grp.gr_name = p;
+ p += size + 1;
+ }
+
+ if (new_grp.gr_passwd != NULL) {
+ size = strlen(new_grp.gr_passwd);
+ memcpy(p, new_grp.gr_passwd, size);
+ new_grp.gr_passwd = p;
+ p += size + 1;
+ }
+
+ if (new_grp.gr_mem != NULL) {
+ p = (char *)ALIGN(p);
+ memcpy(p, new_grp.gr_mem, sizeof(char *) * mem_size);
+ new_grp.gr_mem = (char **)p;
+ p += sizeof(char *) * (mem_size + 1);
+
+ for (mem = new_grp.gr_mem; *mem; ++mem) {
+ size = strlen(*mem);
+ memcpy(p, *mem, size);
+ *mem = p;
+ p += size + 1;
+ }
+ }
+
+ memcpy(buffer, &new_grp, sizeof(struct group));
+ TRACE_OUT(group_marshal_func);
+ return (NS_SUCCESS);
+}
+
+static int
+group_lookup_func(const char *key, size_t key_size, char **buffer,
+ size_t *buffer_size)
+{
+ enum nss_lookup_type lookup_type;
+ char *name;
+ size_t size;
+ gid_t gid;
+
+ struct group *result;
+
+ TRACE_IN(group_lookup_func);
+ assert(buffer != NULL);
+ assert(buffer_size != NULL);
+
+ if (key_size < sizeof(enum nss_lookup_type)) {
+ TRACE_OUT(group_lookup_func);
+ return (NS_UNAVAIL);
+ }
+ memcpy(&lookup_type, key, sizeof(enum nss_lookup_type));
+
+ switch (lookup_type) {
+ case nss_lt_name:
+ size = key_size - sizeof(enum nss_lookup_type) + 1;
+ name = (char *)calloc(1, size);
+ assert(name != NULL);
+ memcpy(name, key + sizeof(enum nss_lookup_type), size - 1);
+ break;
+ case nss_lt_id:
+ if (key_size < sizeof(enum nss_lookup_type) +
+ sizeof(gid_t)) {
+ TRACE_OUT(passwd_lookup_func);
+ return (NS_UNAVAIL);
+ }
+
+ memcpy(&gid, key + sizeof(enum nss_lookup_type), sizeof(gid_t));
+ break;
+ default:
+ TRACE_OUT(group_lookup_func);
+ return (NS_UNAVAIL);
+ }
+
+ switch (lookup_type) {
+ case nss_lt_name:
+ TRACE_STR(name);
+ result = getgrnam(name);
+ free(name);
+ break;
+ case nss_lt_id:
+ result = getgrgid(gid);
+ break;
+ default:
+ /* SHOULD NOT BE REACHED */
+ break;
+ }
+
+ if (result != NULL) {
+ group_marshal_func(result, NULL, buffer_size);
+ *buffer = (char *)malloc(*buffer_size);
+ assert(*buffer != NULL);
+ group_marshal_func(result, *buffer, buffer_size);
+ }
+
+ TRACE_OUT(group_lookup_func);
+ return (result == NULL ? NS_NOTFOUND : NS_SUCCESS);
+}
+
+static void *
+group_mp_init_func()
+{
+ TRACE_IN(group_mp_init_func);
+ setgrent();
+ TRACE_OUT(group_mp_init_func);
+
+ return (NULL);
+}
+
+static int
+group_mp_lookup_func(char **buffer, size_t *buffer_size, void *mdata)
+{
+ struct group *result;
+
+ TRACE_IN(group_mp_lookup_func);
+ result = getgrent();
+ if (result != NULL) {
+ group_marshal_func(result, NULL, buffer_size);
+ *buffer = (char *)malloc(*buffer_size);
+ assert(*buffer != NULL);
+ group_marshal_func(result, *buffer, buffer_size);
+ }
+
+ TRACE_OUT(group_mp_lookup_func);
+ return (result == NULL ? NS_NOTFOUND : NS_SUCCESS);
+}
+
+static void
+group_mp_destroy_func(void *mdata)
+{
+ TRACE_IN(group_mp_destroy_func);
+ TRACE_OUT(group_mp_destroy_func);
+}
+
+struct agent *
+init_group_agent()
+{
+ struct common_agent *retval;
+
+ TRACE_IN(init_group_agent);
+ retval = (struct common_agent *)calloc(1, sizeof(struct common_agent));
+ assert(retval != NULL);
+
+ retval->parent.name = strdup("group");
+ assert(retval->parent.name != NULL);
+
+ retval->parent.type = COMMON_AGENT;
+ retval->lookup_func = group_lookup_func;
+
+ TRACE_OUT(init_group_agent);
+ return ((struct agent *)retval);
+}
+
+struct agent *
+init_group_mp_agent()
+{
+ struct multipart_agent *retval;
+
+ TRACE_IN(init_group_mp_agent);
+ retval = (struct multipart_agent *)calloc(1,
+ sizeof(struct multipart_agent));
+ assert(retval != NULL);
+
+ retval->parent.name = strdup("group");
+ retval->parent.type = MULTIPART_AGENT;
+ retval->mp_init_func = group_mp_init_func;
+ retval->mp_lookup_func = group_mp_lookup_func;
+ retval->mp_destroy_func = group_mp_destroy_func;
+ assert(retval->parent.name != NULL);
+
+ TRACE_OUT(init_group_mp_agent);
+ return ((struct agent *)retval);
+}
diff --git a/usr.sbin/nscd/agents/group.h b/usr.sbin/nscd/agents/group.h
new file mode 100644
index 0000000..e6c7397
--- /dev/null
+++ b/usr.sbin/nscd/agents/group.h
@@ -0,0 +1,32 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 "../agent.h"
+
+extern struct agent *init_group_agent();
+extern struct agent *init_group_mp_agent();
diff --git a/usr.sbin/nscd/agents/passwd.c b/usr.sbin/nscd/agents/passwd.c
new file mode 100644
index 0000000..b068d19
--- /dev/null
+++ b/usr.sbin/nscd/agents/passwd.c
@@ -0,0 +1,266 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <assert.h>
+#include <nsswitch.h>
+#include <pwd.h>
+#include <string.h>
+#include <stdlib.h>
+#include "../debug.h"
+#include "passwd.h"
+
+static int passwd_marshal_func(struct passwd *, char *, size_t *);
+static int passwd_lookup_func(const char *, size_t, char **, size_t *);
+static void *passwd_mp_init_func();
+static int passwd_mp_lookup_func(char **, size_t *, void *);
+static void passwd_mp_destroy_func(void *mdata);
+
+static int
+passwd_marshal_func(struct passwd *pwd, char *buffer, size_t *buffer_size)
+{
+ char *p;
+ struct passwd new_pwd;
+ size_t desired_size, size;
+
+ TRACE_IN(passwd_marshal_func);
+ desired_size = sizeof(struct passwd) + sizeof(char *) +
+ strlen(pwd->pw_name) + 1;
+ if (pwd->pw_passwd != NULL)
+ desired_size += strlen(pwd->pw_passwd) + 1;
+ if (pwd->pw_class != NULL)
+ desired_size += strlen(pwd->pw_class) + 1;
+ if (pwd->pw_gecos != NULL)
+ desired_size += strlen(pwd->pw_gecos) + 1;
+ if (pwd->pw_dir != NULL)
+ desired_size += strlen(pwd->pw_dir) + 1;
+ if (pwd->pw_shell != NULL)
+ desired_size += strlen(pwd->pw_shell) + 1;
+
+ if ((*buffer_size < desired_size) || (buffer == NULL)) {
+ *buffer_size = desired_size;
+ TRACE_OUT(passwd_marshal_func);
+ return (NS_RETURN);
+ }
+
+ memcpy(&new_pwd, pwd, sizeof(struct passwd));
+ memset(buffer, 0, desired_size);
+
+ *buffer_size = desired_size;
+ p = buffer + sizeof(struct passwd) + sizeof(char *);
+ memcpy(buffer + sizeof(struct passwd), &p, sizeof(char *));
+
+ if (new_pwd.pw_name != NULL) {
+ size = strlen(new_pwd.pw_name);
+ memcpy(p, new_pwd.pw_name, size);
+ new_pwd.pw_name = p;
+ p += size + 1;
+ }
+
+ if (new_pwd.pw_passwd != NULL) {
+ size = strlen(new_pwd.pw_passwd);
+ memcpy(p, new_pwd.pw_passwd, size);
+ new_pwd.pw_passwd = p;
+ p += size + 1;
+ }
+
+ if (new_pwd.pw_class != NULL) {
+ size = strlen(new_pwd.pw_class);
+ memcpy(p, new_pwd.pw_class, size);
+ new_pwd.pw_class = p;
+ p += size + 1;
+ }
+
+ if (new_pwd.pw_gecos != NULL) {
+ size = strlen(new_pwd.pw_gecos);
+ memcpy(p, new_pwd.pw_gecos, size);
+ new_pwd.pw_gecos = p;
+ p += size + 1;
+ }
+
+ if (new_pwd.pw_dir != NULL) {
+ size = strlen(new_pwd.pw_dir);
+ memcpy(p, new_pwd.pw_dir, size);
+ new_pwd.pw_dir = p;
+ p += size + 1;
+ }
+
+ if (new_pwd.pw_shell != NULL) {
+ size = strlen(new_pwd.pw_shell);
+ memcpy(p, new_pwd.pw_shell, size);
+ new_pwd.pw_shell = p;
+ p += size + 1;
+ }
+
+ memcpy(buffer, &new_pwd, sizeof(struct passwd));
+ TRACE_OUT(passwd_marshal_func);
+ return (NS_SUCCESS);
+}
+
+static int
+passwd_lookup_func(const char *key, size_t key_size, char **buffer,
+ size_t *buffer_size)
+{
+ enum nss_lookup_type lookup_type;
+ char *login;
+ size_t size;
+ uid_t uid;
+
+ struct passwd *result;
+
+ TRACE_IN(passwd_lookup_func);
+ assert(buffer != NULL);
+ assert(buffer_size != NULL);
+
+ if (key_size < sizeof(enum nss_lookup_type)) {
+ TRACE_OUT(passwd_lookup_func);
+ return (NS_UNAVAIL);
+ }
+ memcpy(&lookup_type, key, sizeof(enum nss_lookup_type));
+
+ switch (lookup_type) {
+ case nss_lt_name:
+ size = key_size - sizeof(enum nss_lookup_type) + 1;
+ login = (char *)calloc(1, size);
+ assert(login != NULL);
+ memcpy(login, key + sizeof(enum nss_lookup_type), size - 1);
+ break;
+ case nss_lt_id:
+ if (key_size < sizeof(enum nss_lookup_type) +
+ sizeof(uid_t)) {
+ TRACE_OUT(passwd_lookup_func);
+ return (NS_UNAVAIL);
+ }
+
+ memcpy(&uid, key + sizeof(enum nss_lookup_type), sizeof(uid_t));
+ break;
+ default:
+ TRACE_OUT(passwd_lookup_func);
+ return (NS_UNAVAIL);
+ }
+
+ switch (lookup_type) {
+ case nss_lt_name:
+ result = getpwnam(login);
+ free(login);
+ break;
+ case nss_lt_id:
+ result = getpwuid(uid);
+ break;
+ default:
+ /* SHOULD NOT BE REACHED */
+ break;
+ }
+
+ if (result != NULL) {
+ passwd_marshal_func(result, NULL, buffer_size);
+ *buffer = (char *)malloc(*buffer_size);
+ assert(*buffer != NULL);
+ passwd_marshal_func(result, *buffer, buffer_size);
+ }
+
+ TRACE_OUT(passwd_lookup_func);
+ return (result == NULL ? NS_NOTFOUND : NS_SUCCESS);
+}
+
+static void *
+passwd_mp_init_func()
+{
+ TRACE_IN(passwd_mp_init_func);
+ setpwent();
+ TRACE_OUT(passwd_mp_init_func);
+
+ return (NULL);
+}
+
+static int
+passwd_mp_lookup_func(char **buffer, size_t *buffer_size, void *mdata)
+{
+ struct passwd *result;
+
+ TRACE_IN(passwd_mp_lookup_func);
+ result = getpwent();
+ if (result != NULL) {
+ passwd_marshal_func(result, NULL, buffer_size);
+ *buffer = (char *)malloc(*buffer_size);
+ assert(*buffer != NULL);
+ passwd_marshal_func(result, *buffer, buffer_size);
+ }
+
+ TRACE_OUT(passwd_mp_lookup_func);
+ return (result == NULL ? NS_NOTFOUND : NS_SUCCESS);
+}
+
+static void
+passwd_mp_destroy_func(void *mdata)
+{
+ TRACE_IN(passwd_mp_destroy_func);
+ TRACE_OUT(passwd_mp_destroy_func);
+}
+
+struct agent *
+init_passwd_agent()
+{
+ struct common_agent *retval;
+
+ TRACE_IN(init_passwd_agent);
+ retval = (struct common_agent *)calloc(1, sizeof(struct common_agent));
+ assert(retval != NULL);
+
+ retval->parent.name = strdup("passwd");
+ assert(retval->parent.name != NULL);
+
+ retval->parent.type = COMMON_AGENT;
+ retval->lookup_func = passwd_lookup_func;
+
+ TRACE_OUT(init_passwd_agent);
+ return ((struct agent *)retval);
+}
+
+struct agent *
+init_passwd_mp_agent()
+{
+ struct multipart_agent *retval;
+
+ TRACE_IN(init_passwd_mp_agent);
+ retval = (struct multipart_agent *)calloc(1,
+ sizeof(struct multipart_agent));
+ assert(retval != NULL);
+
+ retval->parent.name = strdup("passwd");
+ retval->parent.type = MULTIPART_AGENT;
+ retval->mp_init_func = passwd_mp_init_func;
+ retval->mp_lookup_func = passwd_mp_lookup_func;
+ retval->mp_destroy_func = passwd_mp_destroy_func;
+ assert(retval->parent.name != NULL);
+
+ TRACE_OUT(init_passwd_mp_agent);
+ return ((struct agent *)retval);
+}
diff --git a/usr.sbin/nscd/agents/passwd.h b/usr.sbin/nscd/agents/passwd.h
new file mode 100644
index 0000000..956a50d
--- /dev/null
+++ b/usr.sbin/nscd/agents/passwd.h
@@ -0,0 +1,32 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 "../agent.h"
+
+extern struct agent *init_passwd_agent();
+extern struct agent *init_passwd_mp_agent();
diff --git a/usr.sbin/nscd/agents/services.c b/usr.sbin/nscd/agents/services.c
new file mode 100644
index 0000000..44dec39
--- /dev/null
+++ b/usr.sbin/nscd/agents/services.c
@@ -0,0 +1,280 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <assert.h>
+#include <nsswitch.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdlib.h>
+#include "../debug.h"
+#include "services.h"
+
+static int services_marshal_func(struct servent *, char *, size_t *);
+static int services_lookup_func(const char *, size_t, char **, size_t *);
+static void *services_mp_init_func();
+static int services_mp_lookup_func(char **, size_t *, void *);
+static void services_mp_destroy_func(void *);
+
+static int
+services_marshal_func(struct servent *serv, char *buffer, size_t *buffer_size)
+{
+ struct servent new_serv;
+ size_t desired_size;
+ char **alias;
+ char *p;
+ size_t size;
+ size_t aliases_size;
+
+ TRACE_IN(services_marshal_func);
+ desired_size = ALIGNBYTES + sizeof(struct servent) + sizeof(char *);
+ if (serv->s_name != NULL)
+ desired_size += strlen(serv->s_name) + 1;
+ if (serv->s_proto != NULL)
+ desired_size += strlen(serv->s_proto) + 1;
+
+ aliases_size = 0;
+ if (serv->s_aliases != NULL) {
+ for (alias = serv->s_aliases; *alias; ++alias) {
+ desired_size += strlen(*alias) + 1;
+ ++aliases_size;
+ }
+
+ desired_size += ALIGNBYTES + sizeof(char *) *
+ (aliases_size + 1);
+ }
+
+ if ((*buffer_size < desired_size) || (buffer == NULL)) {
+ *buffer_size = desired_size;
+ TRACE_OUT(services_marshal_func);
+ return (NS_RETURN);
+ }
+
+ memcpy(&new_serv, serv, sizeof(struct servent));
+ memset(buffer, 0, desired_size);
+
+ *buffer_size = desired_size;
+ p = buffer + sizeof(struct servent) + sizeof(char *);
+ memcpy(buffer + sizeof(struct servent), &p, sizeof(char *));
+ p = (char *)ALIGN(p);
+
+ if (new_serv.s_name != NULL) {
+ size = strlen(new_serv.s_name);
+ memcpy(p, new_serv.s_name, size);
+ new_serv.s_name = p;
+ p += size + 1;
+ }
+
+ if (new_serv.s_proto != NULL) {
+ size = strlen(new_serv.s_proto);
+ memcpy(p, new_serv.s_proto, size);
+ new_serv.s_proto = p;
+ p += size + 1;
+ }
+
+ if (new_serv.s_aliases != NULL) {
+ p = (char *)ALIGN(p);
+ memcpy(p, new_serv.s_aliases, sizeof(char *) * aliases_size);
+ new_serv.s_aliases = (char **)p;
+ p += sizeof(char *) * (aliases_size + 1);
+
+ for (alias = new_serv.s_aliases; *alias; ++alias) {
+ size = strlen(*alias);
+ memcpy(p, *alias, size);
+ *alias = p;
+ p += size + 1;
+ }
+ }
+
+ memcpy(buffer, &new_serv, sizeof(struct servent));
+ TRACE_OUT(services_marshal_func);
+ return (NS_SUCCESS);
+}
+
+static int
+services_lookup_func(const char *key, size_t key_size, char **buffer,
+ size_t *buffer_size)
+{
+ enum nss_lookup_type lookup_type;
+ char *name = NULL;
+ char *proto = NULL;
+ size_t size, size2;
+ int port;
+
+ struct servent *result;
+
+ TRACE_IN(services_lookup_func);
+
+ assert(buffer != NULL);
+ assert(buffer_size != NULL);
+
+ if (key_size < sizeof(enum nss_lookup_type)) {
+ TRACE_OUT(passwd_lookup_func);
+ return (NS_UNAVAIL);
+ }
+ memcpy(&lookup_type, key, sizeof(enum nss_lookup_type));
+
+ switch (lookup_type) {
+ case nss_lt_name:
+ size = key_size - sizeof(enum nss_lookup_type);
+ name = (char *)calloc(1, size + 1);
+ assert(name != NULL);
+ memcpy(name, key + sizeof(enum nss_lookup_type), size);
+
+ size2 = strlen(name) + 1;
+
+ if (size2 < size)
+ proto = name + size2;
+ else
+ proto = NULL;
+ break;
+ case nss_lt_id:
+ if (key_size < sizeof(enum nss_lookup_type) +
+ sizeof(int)) {
+ TRACE_OUT(passwd_lookup_func);
+ return (NS_UNAVAIL);
+ }
+
+ memcpy(&port, key + sizeof(enum nss_lookup_type),
+ sizeof(int));
+
+ size = key_size - sizeof(enum nss_lookup_type) - sizeof(int);
+ if (size > 0) {
+ proto = (char *)calloc(1, size + 1);
+ assert(proto != NULL);
+ memcpy(proto, key + sizeof(enum nss_lookup_type) +
+ sizeof(int), size);
+ }
+ break;
+ default:
+ TRACE_OUT(passwd_lookup_func);
+ return (NS_UNAVAIL);
+ }
+
+ switch (lookup_type) {
+ case nss_lt_name:
+ result = getservbyname(name, proto);
+ free(name);
+ break;
+ case nss_lt_id:
+ result = getservbyport(port, proto);
+ free(proto);
+ break;
+ default:
+ /* SHOULD NOT BE REACHED */
+ break;
+ }
+
+ if (result != NULL) {
+ services_marshal_func(result, NULL, buffer_size);
+ *buffer = (char *)malloc(*buffer_size);
+ assert(*buffer != NULL);
+ services_marshal_func(result, *buffer, buffer_size);
+ }
+
+ TRACE_OUT(services_lookup_func);
+ return (result == NULL ? NS_NOTFOUND : NS_SUCCESS);
+}
+
+static void *
+services_mp_init_func()
+{
+ TRACE_IN(services_mp_init_func);
+ setservent(0);
+ TRACE_OUT(services_mp_init_func);
+
+ return (NULL);
+}
+
+static int
+services_mp_lookup_func(char **buffer, size_t *buffer_size, void *mdata)
+{
+ struct servent *result;
+
+ TRACE_IN(services_mp_lookup_func);
+ result = getservent();
+ if (result != NULL) {
+ services_marshal_func(result, NULL, buffer_size);
+ *buffer = (char *)malloc(*buffer_size);
+ assert(*buffer != NULL);
+ services_marshal_func(result, *buffer, buffer_size);
+ }
+
+ TRACE_OUT(services_mp_lookup_func);
+ return (result == NULL ? NS_NOTFOUND : NS_SUCCESS);
+}
+
+static void
+services_mp_destroy_func(void *mdata)
+{
+ TRACE_IN(services_mp_destroy_func);
+ TRACE_OUT(services_mp_destroy_func);
+}
+
+struct agent *
+init_services_agent()
+{
+ struct common_agent *retval;
+ TRACE_IN(init_services_agent);
+
+ retval = (struct common_agent *)calloc(1, sizeof(struct common_agent));
+ assert(retval != NULL);
+
+ retval->parent.name = strdup("services");
+ assert(retval->parent.name != NULL);
+
+ retval->parent.type = COMMON_AGENT;
+ retval->lookup_func = services_lookup_func;
+
+ TRACE_OUT(init_services_agent);
+ return ((struct agent *)retval);
+}
+
+struct agent *
+init_services_mp_agent()
+{
+ struct multipart_agent *retval;
+
+ TRACE_IN(init_services_mp_agent);
+ retval = (struct multipart_agent *)calloc(1,
+ sizeof(struct multipart_agent));
+ assert(retval != NULL);
+
+ retval->parent.name = strdup("services");
+ retval->parent.type = MULTIPART_AGENT;
+ retval->mp_init_func = services_mp_init_func;
+ retval->mp_lookup_func = services_mp_lookup_func;
+ retval->mp_destroy_func = services_mp_destroy_func;
+ assert(retval->parent.name != NULL);
+
+ TRACE_OUT(init_services_mp_agent);
+ return ((struct agent *)retval);
+}
diff --git a/usr.sbin/nscd/agents/services.h b/usr.sbin/nscd/agents/services.h
new file mode 100644
index 0000000..0b77c87
--- /dev/null
+++ b/usr.sbin/nscd/agents/services.h
@@ -0,0 +1,32 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 "../agent.h"
+
+extern struct agent *init_services_agent();
+extern struct agent *init_services_mp_agent();
diff --git a/usr.sbin/nscd/cachelib.c b/usr.sbin/nscd/cachelib.c
new file mode 100644
index 0000000..daa8193
--- /dev/null
+++ b/usr.sbin/nscd/cachelib.c
@@ -0,0 +1,1218 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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/time.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include "cachelib.h"
+#include "debug.h"
+
+#define INITIAL_ENTRIES_CAPACITY 32
+#define ENTRIES_CAPACITY_STEP 32
+
+#define STRING_SIMPLE_HASH_BODY(in_var, var, a, M) \
+ for ((var) = 0; *(in_var) != '\0'; ++(in_var)) \
+ (var) = ((a)*(var) + *(in_var)) % (M)
+
+#define STRING_SIMPLE_MP2_HASH_BODY(in_var, var, a, M) \
+ for ((var) = 0; *(in_var) != 0; ++(in_var)) \
+ (var) = ((a)*(var) + *(in_var)) & (M - 1)
+
+static int cache_elemsize_common_continue_func(struct cache_common_entry_ *,
+ struct cache_policy_item_ *);
+static int cache_lifetime_common_continue_func(struct cache_common_entry_ *,
+ struct cache_policy_item_ *);
+static void clear_cache_entry(struct cache_entry_ *);
+static void destroy_cache_entry(struct cache_entry_ *);
+static void destroy_cache_mp_read_session(struct cache_mp_read_session_ *);
+static void destroy_cache_mp_write_session(struct cache_mp_write_session_ *);
+static int entries_bsearch_cmp_func(const void *, const void *);
+static int entries_qsort_cmp_func(const void *, const void *);
+static struct cache_entry_ ** find_cache_entry_p(struct cache_ *,
+ const char *);
+static void flush_cache_entry(struct cache_entry_ *);
+static void flush_cache_policy(struct cache_common_entry_ *,
+ struct cache_policy_ *, struct cache_policy_ *,
+ int (*)(struct cache_common_entry_ *,
+ struct cache_policy_item_ *));
+static int ht_items_cmp_func(const void *, const void *);
+static int ht_items_fixed_size_left_cmp_func(const void *, const void *);
+static hashtable_index_t ht_item_hash_func(const void *, size_t);
+
+/*
+ * Hashing and comparing routines, that are used with the hash tables
+ */
+static int
+ht_items_cmp_func(const void *p1, const void *p2)
+{
+ struct cache_ht_item_data_ *hp1, *hp2;
+ size_t min_size;
+ int result;
+
+ hp1 = (struct cache_ht_item_data_ *)p1;
+ hp2 = (struct cache_ht_item_data_ *)p2;
+
+ assert(hp1->key != NULL);
+ assert(hp2->key != NULL);
+
+ if (hp1->key_size != hp2->key_size) {
+ min_size = (hp1->key_size < hp2->key_size) ? hp1->key_size :
+ hp2->key_size;
+ result = memcmp(hp1->key, hp2->key, min_size);
+
+ if (result == 0)
+ return ((hp1->key_size < hp2->key_size) ? -1 : 1);
+ else
+ return (result);
+ } else
+ return (memcmp(hp1->key, hp2->key, hp1->key_size));
+}
+
+static int
+ht_items_fixed_size_left_cmp_func(const void *p1, const void *p2)
+{
+ struct cache_ht_item_data_ *hp1, *hp2;
+ size_t min_size;
+ int result;
+
+ hp1 = (struct cache_ht_item_data_ *)p1;
+ hp2 = (struct cache_ht_item_data_ *)p2;
+
+ assert(hp1->key != NULL);
+ assert(hp2->key != NULL);
+
+ if (hp1->key_size != hp2->key_size) {
+ min_size = (hp1->key_size < hp2->key_size) ? hp1->key_size :
+ hp2->key_size;
+ result = memcmp(hp1->key, hp2->key, min_size);
+
+ if (result == 0)
+ if (min_size == hp1->key_size)
+ return (0);
+ else
+ return ((hp1->key_size < hp2->key_size) ? -1 : 1);
+ else
+ return (result);
+ } else
+ return (memcmp(hp1->key, hp2->key, hp1->key_size));
+}
+
+static hashtable_index_t
+ht_item_hash_func(const void *p, size_t cache_entries_size)
+{
+ struct cache_ht_item_data_ *hp;
+ size_t i;
+
+ hashtable_index_t retval;
+
+ hp = (struct cache_ht_item_data_ *)p;
+ assert(hp->key != NULL);
+
+ retval = 0;
+ for (i = 0; i < hp->key_size; ++i)
+ retval = (127 * retval + (unsigned char)hp->key[i]) %
+ cache_entries_size;
+
+ return retval;
+}
+
+HASHTABLE_GENERATE(cache_ht_, cache_ht_item_, struct cache_ht_item_data_, data,
+ ht_item_hash_func, ht_items_cmp_func);
+
+/*
+ * Routines to sort and search the entries by name
+ */
+static int
+entries_bsearch_cmp_func(const void *key, const void *ent)
+{
+
+ assert(key != NULL);
+ assert(ent != NULL);
+
+ return (strcmp((char const *)key,
+ (*(struct cache_entry_ const **)ent)->name));
+}
+
+static int
+entries_qsort_cmp_func(const void *e1, const void *e2)
+{
+
+ assert(e1 != NULL);
+ assert(e2 != NULL);
+
+ return (strcmp((*(struct cache_entry_ const **)e1)->name,
+ (*(struct cache_entry_ const **)e2)->name));
+}
+
+static struct cache_entry_ **
+find_cache_entry_p(struct cache_ *the_cache, const char *entry_name)
+{
+
+ return ((struct cache_entry_ **)(bsearch(entry_name, the_cache->entries,
+ the_cache->entries_size, sizeof(struct cache_entry_ *),
+ entries_bsearch_cmp_func)));
+}
+
+static void
+destroy_cache_mp_write_session(struct cache_mp_write_session_ *ws)
+{
+
+ struct cache_mp_data_item_ *data_item;
+
+ TRACE_IN(destroy_cache_mp_write_session);
+ assert(ws != NULL);
+ while (!TAILQ_EMPTY(&ws->items)) {
+ data_item = TAILQ_FIRST(&ws->items);
+ TAILQ_REMOVE(&ws->items, data_item, entries);
+ free(data_item->value);
+ free(data_item);
+ }
+
+ free(ws);
+ TRACE_OUT(destroy_cache_mp_write_session);
+}
+
+static void
+destroy_cache_mp_read_session(struct cache_mp_read_session_ *rs)
+{
+
+ TRACE_IN(destroy_cache_mp_read_session);
+ assert(rs != NULL);
+ free(rs);
+ TRACE_OUT(destroy_cache_mp_read_session);
+}
+
+static void
+destroy_cache_entry(struct cache_entry_ *entry)
+{
+ struct cache_common_entry_ *common_entry;
+ struct cache_mp_entry_ *mp_entry;
+ struct cache_mp_read_session_ *rs;
+ struct cache_mp_write_session_ *ws;
+ struct cache_ht_item_ *ht_item;
+ struct cache_ht_item_data_ *ht_item_data;
+
+ TRACE_IN(destroy_cache_entry);
+ assert(entry != NULL);
+
+ if (entry->params->entry_type == CET_COMMON) {
+ common_entry = (struct cache_common_entry_ *)entry;
+
+ HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
+ HASHTABLE_ENTRY_FOREACH(ht_item, data, ht_item_data)
+ {
+ free(ht_item_data->key);
+ free(ht_item_data->value);
+ }
+ HASHTABLE_ENTRY_CLEAR(ht_item, data);
+ }
+
+ HASHTABLE_DESTROY(&(common_entry->items), data);
+
+ /* FIFO policy is always first */
+ destroy_cache_fifo_policy(common_entry->policies[0]);
+ switch (common_entry->common_params.policy) {
+ case CPT_LRU:
+ destroy_cache_lru_policy(common_entry->policies[1]);
+ break;
+ case CPT_LFU:
+ destroy_cache_lfu_policy(common_entry->policies[1]);
+ break;
+ default:
+ break;
+ }
+ free(common_entry->policies);
+ } else {
+ mp_entry = (struct cache_mp_entry_ *)entry;
+
+ while (!TAILQ_EMPTY(&mp_entry->ws_head)) {
+ ws = TAILQ_FIRST(&mp_entry->ws_head);
+ TAILQ_REMOVE(&mp_entry->ws_head, ws, entries);
+ destroy_cache_mp_write_session(ws);
+ }
+
+ while (!TAILQ_EMPTY(&mp_entry->rs_head)) {
+ rs = TAILQ_FIRST(&mp_entry->rs_head);
+ TAILQ_REMOVE(&mp_entry->rs_head, rs, entries);
+ destroy_cache_mp_read_session(rs);
+ }
+
+ if (mp_entry->completed_write_session != NULL)
+ destroy_cache_mp_write_session(
+ mp_entry->completed_write_session);
+
+ if (mp_entry->pending_write_session != NULL)
+ destroy_cache_mp_write_session(
+ mp_entry->pending_write_session);
+ }
+
+ free(entry->name);
+ free(entry);
+ TRACE_OUT(destroy_cache_entry);
+}
+
+static void
+clear_cache_entry(struct cache_entry_ *entry)
+{
+ struct cache_mp_entry_ *mp_entry;
+ struct cache_common_entry_ *common_entry;
+ struct cache_ht_item_ *ht_item;
+ struct cache_ht_item_data_ *ht_item_data;
+ struct cache_policy_ *policy;
+ struct cache_policy_item_ *item, *next_item;
+ size_t entry_size;
+ int i;
+
+ if (entry->params->entry_type == CET_COMMON) {
+ common_entry = (struct cache_common_entry_ *)entry;
+
+ entry_size = 0;
+ HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
+ HASHTABLE_ENTRY_FOREACH(ht_item, data, ht_item_data)
+ {
+ free(ht_item_data->key);
+ free(ht_item_data->value);
+ }
+ entry_size += HASHTABLE_ENTRY_SIZE(ht_item, data);
+ HASHTABLE_ENTRY_CLEAR(ht_item, data);
+ }
+
+ common_entry->items_size -= entry_size;
+ for (i = 0; i < common_entry->policies_size; ++i) {
+ policy = common_entry->policies[i];
+
+ next_item = NULL;
+ item = policy->get_first_item_func(policy);
+ while (item != NULL) {
+ next_item = policy->get_next_item_func(policy,
+ item);
+ policy->remove_item_func(policy, item);
+ policy->destroy_item_func(item);
+ item = next_item;
+ }
+ }
+ } else {
+ mp_entry = (struct cache_mp_entry_ *)entry;
+
+ if (mp_entry->rs_size == 0) {
+ if (mp_entry->completed_write_session != NULL) {
+ destroy_cache_mp_write_session(
+ mp_entry->completed_write_session);
+ mp_entry->completed_write_session = NULL;
+ }
+
+ memset(&mp_entry->creation_time, 0,
+ sizeof(struct timeval));
+ memset(&mp_entry->last_request_time, 0,
+ sizeof(struct timeval));
+ }
+ }
+}
+
+/*
+ * When passed to the flush_cache_policy, ensures that all old elements are
+ * deleted.
+ */
+static int
+cache_lifetime_common_continue_func(struct cache_common_entry_ *entry,
+ struct cache_policy_item_ *item)
+{
+
+ return ((item->last_request_time.tv_sec - item->creation_time.tv_sec >
+ entry->common_params.max_lifetime.tv_sec) ? 1: 0);
+}
+
+/*
+ * When passed to the flush_cache_policy, ensures that all elements, that
+ * exceed the size limit, are deleted.
+ */
+static int
+cache_elemsize_common_continue_func(struct cache_common_entry_ *entry,
+ struct cache_policy_item_ *item)
+{
+
+ return ((entry->items_size > entry->common_params.satisf_elemsize) ? 1
+ : 0);
+}
+
+/*
+ * Removes the elements from the cache entry, while the continue_func returns 1.
+ */
+static void
+flush_cache_policy(struct cache_common_entry_ *entry,
+ struct cache_policy_ *policy,
+ struct cache_policy_ *connected_policy,
+ int (*continue_func)(struct cache_common_entry_ *,
+ struct cache_policy_item_ *))
+{
+ struct cache_policy_item_ *item, *next_item, *connected_item;
+ struct cache_ht_item_ *ht_item;
+ struct cache_ht_item_data_ *ht_item_data, ht_key;
+ hashtable_index_t hash;
+
+ assert(policy != NULL);
+
+ next_item = NULL;
+ item = policy->get_first_item_func(policy);
+ while ((item != NULL) && (continue_func(entry, item) == 1)) {
+ next_item = policy->get_next_item_func(policy, item);
+
+ connected_item = item->connected_item;
+ policy->remove_item_func(policy, item);
+
+ memset(&ht_key, 0, sizeof(struct cache_ht_item_data_));
+ ht_key.key = item->key;
+ ht_key.key_size = item->key_size;
+
+ hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &entry->items,
+ &ht_key);
+ assert(hash >= 0);
+ assert(hash < HASHTABLE_ENTRIES_COUNT(&entry->items));
+
+ ht_item = HASHTABLE_GET_ENTRY(&(entry->items), hash);
+ ht_item_data = HASHTABLE_ENTRY_FIND(cache_ht_, ht_item,
+ &ht_key);
+ assert(ht_item_data != NULL);
+ free(ht_item_data->key);
+ free(ht_item_data->value);
+ HASHTABLE_ENTRY_REMOVE(cache_ht_, ht_item, ht_item_data);
+ --entry->items_size;
+
+ policy->destroy_item_func(item);
+
+ if (connected_item != NULL) {
+ connected_policy->remove_item_func(connected_policy,
+ connected_item);
+ connected_policy->destroy_item_func(connected_item);
+ }
+
+ item = next_item;
+ }
+}
+
+static void
+flush_cache_entry(struct cache_entry_ *entry)
+{
+ struct cache_mp_entry_ *mp_entry;
+ struct cache_common_entry_ *common_entry;
+ struct cache_policy_ *policy, *connected_policy;
+
+ connected_policy = NULL;
+ if (entry->params->entry_type == CET_COMMON) {
+ common_entry = (struct cache_common_entry_ *)entry;
+ if ((common_entry->common_params.max_lifetime.tv_sec != 0) ||
+ (common_entry->common_params.max_lifetime.tv_usec != 0)) {
+
+ policy = common_entry->policies[0];
+ if (common_entry->policies_size > 1)
+ connected_policy = common_entry->policies[1];
+
+ flush_cache_policy(common_entry, policy,
+ connected_policy,
+ cache_lifetime_common_continue_func);
+ }
+
+
+ if ((common_entry->common_params.max_elemsize != 0) &&
+ common_entry->items_size >
+ common_entry->common_params.max_elemsize) {
+
+ if (common_entry->policies_size > 1) {
+ policy = common_entry->policies[1];
+ connected_policy = common_entry->policies[0];
+ } else {
+ policy = common_entry->policies[0];
+ connected_policy = NULL;
+ }
+
+ flush_cache_policy(common_entry, policy,
+ connected_policy,
+ cache_elemsize_common_continue_func);
+ }
+ } else {
+ mp_entry = (struct cache_mp_entry_ *)entry;
+
+ if ((mp_entry->mp_params.max_lifetime.tv_sec != 0)
+ || (mp_entry->mp_params.max_lifetime.tv_usec != 0)) {
+
+ if (mp_entry->last_request_time.tv_sec -
+ mp_entry->last_request_time.tv_sec >
+ mp_entry->mp_params.max_lifetime.tv_sec)
+ clear_cache_entry(entry);
+ }
+ }
+}
+
+struct cache_ *
+init_cache(struct cache_params const *params)
+{
+ struct cache_ *retval;
+
+ TRACE_IN(init_cache);
+ assert(params != NULL);
+
+ retval = (struct cache_ *)calloc(1, sizeof(struct cache_));
+ assert(retval != NULL);
+
+ assert(params != NULL);
+ memcpy(&retval->params, params, sizeof(struct cache_params));
+
+ retval->entries = (struct cache_entry_ **)calloc(1,
+ sizeof(struct cache_entry_ *) * INITIAL_ENTRIES_CAPACITY);
+ assert(retval->entries != NULL);
+
+ retval->entries_capacity = INITIAL_ENTRIES_CAPACITY;
+ retval->entries_size = 0;
+
+ TRACE_OUT(init_cache);
+ return (retval);
+}
+
+void
+destroy_cache(struct cache_ *the_cache)
+{
+
+ TRACE_IN(destroy_cache);
+ assert(the_cache != NULL);
+
+ if (the_cache->entries != NULL) {
+ size_t i;
+ for (i = 0; i < the_cache->entries_size; ++i)
+ destroy_cache_entry(the_cache->entries[i]);
+
+ free(the_cache->entries);
+ }
+
+ free(the_cache);
+ TRACE_OUT(destroy_cache);
+}
+
+int
+register_cache_entry(struct cache_ *the_cache,
+ struct cache_entry_params const *params)
+{
+ int policies_size;
+ size_t entry_name_size;
+ struct cache_common_entry_ *new_common_entry;
+ struct cache_mp_entry_ *new_mp_entry;
+
+ TRACE_IN(register_cache_entry);
+ assert(the_cache != NULL);
+
+ if (find_cache_entry(the_cache, params->entry_name) != NULL) {
+ TRACE_OUT(register_cache_entry);
+ return (-1);
+ }
+
+ if (the_cache->entries_size == the_cache->entries_capacity) {
+ struct cache_entry_ **new_entries;
+ size_t new_capacity;
+
+ new_capacity = the_cache->entries_capacity +
+ ENTRIES_CAPACITY_STEP;
+ new_entries = (struct cache_entry_ **)calloc(1,
+ sizeof(struct cache_entry_ *) * new_capacity);
+ assert(new_entries != NULL);
+
+ memcpy(new_entries, the_cache->entries,
+ sizeof(struct cache_entry_ *)
+ * the_cache->entries_size);
+
+ free(the_cache->entries);
+ the_cache->entries = new_entries;
+ }
+
+ entry_name_size = strlen(params->entry_name) + 1;
+ switch (params->entry_type)
+ {
+ case CET_COMMON:
+ new_common_entry = (struct cache_common_entry_ *)calloc(1,
+ sizeof(struct cache_common_entry_));
+ assert(new_common_entry != NULL);
+
+ memcpy(&new_common_entry->common_params, params,
+ sizeof(struct common_cache_entry_params));
+ new_common_entry->params =
+ (struct cache_entry_params *)&new_common_entry->common_params;
+
+ new_common_entry->common_params.entry_name = (char *)calloc(1,
+ entry_name_size);
+ assert(new_common_entry->common_params.entry_name != NULL);
+ strlcpy(new_common_entry->common_params.entry_name,
+ params->entry_name, entry_name_size);
+ new_common_entry->name =
+ new_common_entry->common_params.entry_name;
+
+ HASHTABLE_INIT(&(new_common_entry->items),
+ struct cache_ht_item_data_, data,
+ new_common_entry->common_params.cache_entries_size);
+
+ if (new_common_entry->common_params.policy == CPT_FIFO)
+ policies_size = 1;
+ else
+ policies_size = 2;
+
+ new_common_entry->policies = (struct cache_policy_ **)calloc(1,
+ sizeof(struct cache_policy_ *) * policies_size);
+ assert(new_common_entry->policies != NULL);
+
+ new_common_entry->policies_size = policies_size;
+ new_common_entry->policies[0] = init_cache_fifo_policy();
+
+ if (policies_size > 1) {
+ switch (new_common_entry->common_params.policy) {
+ case CPT_LRU:
+ new_common_entry->policies[1] =
+ init_cache_lru_policy();
+ break;
+ case CPT_LFU:
+ new_common_entry->policies[1] =
+ init_cache_lfu_policy();
+ break;
+ default:
+ break;
+ }
+ }
+
+ new_common_entry->get_time_func =
+ the_cache->params.get_time_func;
+ the_cache->entries[the_cache->entries_size++] =
+ (struct cache_entry_ *)new_common_entry;
+ break;
+ case CET_MULTIPART:
+ new_mp_entry = (struct cache_mp_entry_ *)calloc(1,
+ sizeof(struct cache_mp_entry_));
+ assert(new_mp_entry != NULL);
+
+ memcpy(&new_mp_entry->mp_params, params,
+ sizeof(struct mp_cache_entry_params));
+ new_mp_entry->params =
+ (struct cache_entry_params *)&new_mp_entry->mp_params;
+
+ new_mp_entry->mp_params.entry_name = (char *)calloc(1,
+ entry_name_size);
+ assert(new_mp_entry->mp_params.entry_name != NULL);
+ strlcpy(new_mp_entry->mp_params.entry_name, params->entry_name,
+ entry_name_size);
+ new_mp_entry->name = new_mp_entry->mp_params.entry_name;
+
+ TAILQ_INIT(&new_mp_entry->ws_head);
+ TAILQ_INIT(&new_mp_entry->rs_head);
+
+ new_mp_entry->get_time_func = the_cache->params.get_time_func;
+ the_cache->entries[the_cache->entries_size++] =
+ (struct cache_entry_ *)new_mp_entry;
+ break;
+ }
+
+
+ qsort(the_cache->entries, the_cache->entries_size,
+ sizeof(struct cache_entry_ *), entries_qsort_cmp_func);
+
+ TRACE_OUT(register_cache_entry);
+ return (0);
+}
+
+int
+unregister_cache_entry(struct cache_ *the_cache, const char *entry_name)
+{
+ struct cache_entry_ **del_ent;
+
+ TRACE_IN(unregister_cache_entry);
+ assert(the_cache != NULL);
+
+ del_ent = find_cache_entry_p(the_cache, entry_name);
+ if (del_ent != NULL) {
+ destroy_cache_entry(*del_ent);
+ --the_cache->entries_size;
+
+ memmove(del_ent, del_ent + 1,
+ (&(the_cache->entries[--the_cache->entries_size]) -
+ del_ent) * sizeof(struct cache_entry_ *));
+
+ TRACE_OUT(unregister_cache_entry);
+ return (0);
+ } else {
+ TRACE_OUT(unregister_cache_entry);
+ return (-1);
+ }
+}
+
+struct cache_entry_ *
+find_cache_entry(struct cache_ *the_cache, const char *entry_name)
+{
+ struct cache_entry_ **result;
+
+ TRACE_IN(find_cache_entry);
+ result = find_cache_entry_p(the_cache, entry_name);
+
+ if (result == NULL) {
+ TRACE_OUT(find_cache_entry);
+ return (NULL);
+ } else {
+ TRACE_OUT(find_cache_entry);
+ return (*result);
+ }
+}
+
+/*
+ * Tries to read the element with the specified key from the cache. If the
+ * value_size is too small, it will be filled with the proper number, and
+ * the user will need to call cache_read again with the value buffer, that
+ * is large enough.
+ * Function returns 0 on success, -1 on error, and -2 if the value_size is too
+ * small.
+ */
+int
+cache_read(struct cache_entry_ *entry, const char *key, size_t key_size,
+ char *value, size_t *value_size)
+{
+ struct cache_common_entry_ *common_entry;
+ struct cache_ht_item_data_ item_data, *find_res;
+ struct cache_ht_item_ *item;
+ hashtable_index_t hash;
+ struct cache_policy_item_ *connected_item;
+
+ TRACE_IN(cache_read);
+ assert(entry != NULL);
+ assert(key != NULL);
+ assert(value_size != NULL);
+ assert(entry->params->entry_type == CET_COMMON);
+
+ common_entry = (struct cache_common_entry_ *)entry;
+
+ memset(&item_data, 0, sizeof(struct cache_ht_item_data_));
+ /* can't avoid the cast here */
+ item_data.key = (char *)key;
+ item_data.key_size = key_size;
+
+ hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &common_entry->items,
+ &item_data);
+ assert(hash >= 0);
+ assert(hash < HASHTABLE_ENTRIES_COUNT(&common_entry->items));
+
+ item = HASHTABLE_GET_ENTRY(&(common_entry->items), hash);
+ find_res = HASHTABLE_ENTRY_FIND(cache_ht_, item, &item_data);
+ if (find_res == NULL) {
+ TRACE_OUT(cache_read);
+ return (-1);
+ }
+
+ if ((common_entry->common_params.max_lifetime.tv_sec != 0) ||
+ (common_entry->common_params.max_lifetime.tv_usec != 0)) {
+
+ if (find_res->fifo_policy_item->last_request_time.tv_sec -
+ find_res->fifo_policy_item->creation_time.tv_sec >
+ common_entry->common_params.max_lifetime.tv_sec) {
+
+ free(find_res->key);
+ free(find_res->value);
+
+ connected_item =
+ find_res->fifo_policy_item->connected_item;
+ if (connected_item != NULL) {
+ common_entry->policies[1]->remove_item_func(
+ common_entry->policies[1],
+ connected_item);
+ common_entry->policies[1]->destroy_item_func(
+ connected_item);
+ }
+
+ common_entry->policies[0]->remove_item_func(
+ common_entry->policies[0],
+ find_res->fifo_policy_item);
+ common_entry->policies[0]->destroy_item_func(
+ find_res->fifo_policy_item);
+
+ HASHTABLE_ENTRY_REMOVE(cache_ht_, item, find_res);
+ --common_entry->items_size;
+ }
+ }
+
+ if ((*value_size < find_res->value_size) || (value == NULL)) {
+ *value_size = find_res->value_size;
+ TRACE_OUT(cache_read);
+ return (-2);
+ }
+
+ *value_size = find_res->value_size;
+ memcpy(value, find_res->value, find_res->value_size);
+
+ ++find_res->fifo_policy_item->request_count;
+ common_entry->get_time_func(
+ &find_res->fifo_policy_item->last_request_time);
+ common_entry->policies[0]->update_item_func(common_entry->policies[0],
+ find_res->fifo_policy_item);
+
+ if (find_res->fifo_policy_item->connected_item != NULL) {
+ connected_item = find_res->fifo_policy_item->connected_item;
+ memcpy(&connected_item->last_request_time,
+ &find_res->fifo_policy_item->last_request_time,
+ sizeof(struct timeval));
+ connected_item->request_count =
+ find_res->fifo_policy_item->request_count;
+
+ common_entry->policies[1]->update_item_func(
+ common_entry->policies[1], connected_item);
+ }
+
+ TRACE_OUT(cache_read);
+ return (0);
+}
+
+/*
+ * Writes the value with the specified key into the cache entry.
+ * Functions returns 0 on success, and -1 on error.
+ */
+int
+cache_write(struct cache_entry_ *entry, const char *key, size_t key_size,
+ char const *value, size_t value_size)
+{
+ struct cache_common_entry_ *common_entry;
+ struct cache_ht_item_data_ item_data, *find_res;
+ struct cache_ht_item_ *item;
+ hashtable_index_t hash;
+
+ struct cache_policy_ *policy, *connected_policy;
+ struct cache_policy_item_ *policy_item;
+ struct cache_policy_item_ *connected_policy_item;
+
+ TRACE_IN(cache_write);
+ assert(entry != NULL);
+ assert(key != NULL);
+ assert(value != NULL);
+ assert(entry->params->entry_type == CET_COMMON);
+
+ common_entry = (struct cache_common_entry_ *)entry;
+
+ memset(&item_data, 0, sizeof(struct cache_ht_item_data_));
+ /* can't avoid the cast here */
+ item_data.key = (char *)key;
+ item_data.key_size = key_size;
+
+ hash = HASHTABLE_CALCULATE_HASH(cache_ht_, &common_entry->items,
+ &item_data);
+ assert(hash >= 0);
+ assert(hash < HASHTABLE_ENTRIES_COUNT(&common_entry->items));
+
+ item = HASHTABLE_GET_ENTRY(&(common_entry->items), hash);
+ find_res = HASHTABLE_ENTRY_FIND(cache_ht_, item, &item_data);
+ if (find_res != NULL) {
+ TRACE_OUT(cache_write);
+ return (-1);
+ }
+
+ item_data.key = (char *)malloc(key_size);
+ memcpy(item_data.key, key, key_size);
+
+ item_data.value = (char *)malloc(value_size);
+ assert(item_data.value != NULL);
+
+ memcpy(item_data.value, value, value_size);
+ item_data.value_size = value_size;
+
+ policy_item = common_entry->policies[0]->create_item_func();
+ policy_item->key = item_data.key;
+ policy_item->key_size = item_data.key_size;
+ common_entry->get_time_func(&policy_item->creation_time);
+
+ if (common_entry->policies_size > 1) {
+ connected_policy_item =
+ common_entry->policies[1]->create_item_func();
+ memcpy(&connected_policy_item->creation_time,
+ &policy_item->creation_time,
+ sizeof(struct timeval));
+ connected_policy_item->key = policy_item->key;
+ connected_policy_item->key_size = policy_item->key_size;
+
+ connected_policy_item->connected_item = policy_item;
+ policy_item->connected_item = connected_policy_item;
+ }
+
+ item_data.fifo_policy_item = policy_item;
+
+ common_entry->policies[0]->add_item_func(common_entry->policies[0],
+ policy_item);
+ if (common_entry->policies_size > 1)
+ common_entry->policies[1]->add_item_func(
+ common_entry->policies[1], connected_policy_item);
+
+ HASHTABLE_ENTRY_STORE(cache_ht_, item, &item_data);
+ ++common_entry->items_size;
+
+ if ((common_entry->common_params.max_elemsize != 0) &&
+ (common_entry->items_size >
+ common_entry->common_params.max_elemsize)) {
+ if (common_entry->policies_size > 1) {
+ policy = common_entry->policies[1];
+ connected_policy = common_entry->policies[0];
+ } else {
+ policy = common_entry->policies[0];
+ connected_policy = NULL;
+ }
+
+ flush_cache_policy(common_entry, policy, connected_policy,
+ cache_elemsize_common_continue_func);
+ }
+
+ TRACE_OUT(cache_write);
+ return (0);
+}
+
+/*
+ * Initializes the write session for the specified multipart entry. This
+ * session then should be filled with data either committed or abandoned by
+ * using close_cache_mp_write_session or abandon_cache_mp_write_session
+ * respectively.
+ * Returns NULL on errors (when there are too many opened write sessions for
+ * the entry).
+ */
+struct cache_mp_write_session_ *
+open_cache_mp_write_session(struct cache_entry_ *entry)
+{
+ struct cache_mp_entry_ *mp_entry;
+ struct cache_mp_write_session_ *retval;
+
+ TRACE_IN(open_cache_mp_write_session);
+ assert(entry != NULL);
+ assert(entry->params->entry_type == CET_MULTIPART);
+ mp_entry = (struct cache_mp_entry_ *)entry;
+
+ if ((mp_entry->mp_params.max_sessions > 0) &&
+ (mp_entry->ws_size == mp_entry->mp_params.max_sessions)) {
+ TRACE_OUT(open_cache_mp_write_session);
+ return (NULL);
+ }
+
+ retval = (struct cache_mp_write_session_ *)calloc(1,
+ sizeof(struct cache_mp_write_session_));
+ assert(retval != NULL);
+
+ TAILQ_INIT(&retval->items);
+ retval->parent_entry = mp_entry;
+
+ TAILQ_INSERT_HEAD(&mp_entry->ws_head, retval, entries);
+ ++mp_entry->ws_size;
+
+ TRACE_OUT(open_cache_mp_write_session);
+ return (retval);
+}
+
+/*
+ * Writes data to the specified session. Return 0 on success and -1 on errors
+ * (when write session size limit is exceeded).
+ */
+int
+cache_mp_write(struct cache_mp_write_session_ *ws, char *data,
+ size_t data_size)
+{
+ struct cache_mp_data_item_ *new_item;
+
+ TRACE_IN(cache_mp_write);
+ assert(ws != NULL);
+ assert(ws->parent_entry != NULL);
+ assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
+
+ if ((ws->parent_entry->mp_params.max_elemsize > 0) &&
+ (ws->parent_entry->mp_params.max_elemsize == ws->items_size)) {
+ TRACE_OUT(cache_mp_write);
+ return (-1);
+ }
+
+ new_item = (struct cache_mp_data_item_ *)calloc(1,
+ sizeof(struct cache_mp_data_item_));
+ assert(new_item != NULL);
+
+ new_item->value = (char *)malloc(data_size);
+ assert(new_item->value != NULL);
+ memcpy(new_item->value, data, data_size);
+ new_item->value_size = data_size;
+
+ TAILQ_INSERT_TAIL(&ws->items, new_item, entries);
+ ++ws->items_size;
+
+ TRACE_OUT(cache_mp_write);
+ return (0);
+}
+
+/*
+ * Abandons the write session and frees all the connected resources.
+ */
+void
+abandon_cache_mp_write_session(struct cache_mp_write_session_ *ws)
+{
+
+ TRACE_IN(abandon_cache_mp_write_session);
+ assert(ws != NULL);
+ assert(ws->parent_entry != NULL);
+ assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
+
+ TAILQ_REMOVE(&ws->parent_entry->ws_head, ws, entries);
+ --ws->parent_entry->ws_size;
+
+ destroy_cache_mp_write_session(ws);
+ TRACE_OUT(abandon_cache_mp_write_session);
+}
+
+/*
+ * Commits the session to the entry, for which it was created.
+ */
+void
+close_cache_mp_write_session(struct cache_mp_write_session_ *ws)
+{
+
+ TRACE_IN(close_cache_mp_write_session);
+ assert(ws != NULL);
+ assert(ws->parent_entry != NULL);
+ assert(ws->parent_entry->params->entry_type == CET_MULTIPART);
+
+ TAILQ_REMOVE(&ws->parent_entry->ws_head, ws, entries);
+ --ws->parent_entry->ws_size;
+
+ if (ws->parent_entry->completed_write_session == NULL) {
+ /*
+ * If there is no completed session yet, this will be the one
+ */
+ ws->parent_entry->get_time_func(
+ &ws->parent_entry->creation_time);
+ ws->parent_entry->completed_write_session = ws;
+ } else {
+ /*
+ * If there is a completed session, then we'll save our session
+ * as a pending session. If there is already a pending session,
+ * it would be destroyed.
+ */
+ if (ws->parent_entry->pending_write_session != NULL)
+ destroy_cache_mp_write_session(
+ ws->parent_entry->pending_write_session);
+
+ ws->parent_entry->pending_write_session = ws;
+ }
+ TRACE_OUT(close_cache_mp_write_session);
+}
+
+/*
+ * Opens read session for the specified entry. Returns NULL on errors (when
+ * there are no data in the entry, or the data are obsolete).
+ */
+struct cache_mp_read_session_ *
+open_cache_mp_read_session(struct cache_entry_ *entry)
+{
+ struct cache_mp_entry_ *mp_entry;
+ struct cache_mp_read_session_ *retval;
+
+ TRACE_IN(open_cache_mp_read_session);
+ assert(entry != NULL);
+ assert(entry->params->entry_type == CET_MULTIPART);
+ mp_entry = (struct cache_mp_entry_ *)entry;
+
+ if (mp_entry->completed_write_session == NULL) {
+ TRACE_OUT(open_cache_mp_read_session);
+ return (NULL);
+ }
+
+ if ((mp_entry->mp_params.max_lifetime.tv_sec != 0)
+ || (mp_entry->mp_params.max_lifetime.tv_usec != 0)) {
+ if (mp_entry->last_request_time.tv_sec -
+ mp_entry->last_request_time.tv_sec >
+ mp_entry->mp_params.max_lifetime.tv_sec) {
+ flush_cache_entry(entry);
+ TRACE_OUT(open_cache_mp_read_session);
+ return (NULL);
+ }
+ }
+
+ retval = (struct cache_mp_read_session_ *)calloc(1,
+ sizeof(struct cache_mp_read_session_));
+ assert(retval != NULL);
+
+ retval->parent_entry = mp_entry;
+ retval->current_item = TAILQ_FIRST(
+ &mp_entry->completed_write_session->items);
+
+ TAILQ_INSERT_HEAD(&mp_entry->rs_head, retval, entries);
+ ++mp_entry->rs_size;
+
+ mp_entry->get_time_func(&mp_entry->last_request_time);
+ TRACE_OUT(open_cache_mp_read_session);
+ return (retval);
+}
+
+/*
+ * Reads the data from the read session - step by step.
+ * Returns 0 on success, -1 on error (when there are no more data), and -2 if
+ * the data_size is too small. In the last case, data_size would be filled
+ * the proper value.
+ */
+int
+cache_mp_read(struct cache_mp_read_session_ *rs, char *data, size_t *data_size)
+{
+
+ TRACE_IN(cache_mp_read);
+ assert(rs != NULL);
+
+ if (rs->current_item == NULL) {
+ TRACE_OUT(cache_mp_read);
+ return (-1);
+ }
+
+ if (rs->current_item->value_size > *data_size) {
+ *data_size = rs->current_item->value_size;
+ if (data == NULL) {
+ TRACE_OUT(cache_mp_read);
+ return (0);
+ }
+
+ TRACE_OUT(cache_mp_read);
+ return (-2);
+ }
+
+ *data_size = rs->current_item->value_size;
+ memcpy(data, rs->current_item->value, rs->current_item->value_size);
+ rs->current_item = TAILQ_NEXT(rs->current_item, entries);
+
+ TRACE_OUT(cache_mp_read);
+ return (0);
+}
+
+/*
+ * Closes the read session. If there are no more read sessions and there is
+ * a pending write session, it will be committed and old
+ * completed_write_session will be destroyed.
+ */
+void
+close_cache_mp_read_session(struct cache_mp_read_session_ *rs)
+{
+
+ TRACE_IN(close_cache_mp_read_session);
+ assert(rs != NULL);
+ assert(rs->parent_entry != NULL);
+
+ TAILQ_REMOVE(&rs->parent_entry->rs_head, rs, entries);
+ --rs->parent_entry->rs_size;
+
+ if ((rs->parent_entry->rs_size == 0) &&
+ (rs->parent_entry->pending_write_session != NULL)) {
+ destroy_cache_mp_write_session(
+ rs->parent_entry->completed_write_session);
+ rs->parent_entry->completed_write_session =
+ rs->parent_entry->pending_write_session;
+ rs->parent_entry->pending_write_session = NULL;
+ }
+
+ destroy_cache_mp_read_session(rs);
+ TRACE_OUT(close_cache_mp_read_session);
+}
+
+int
+transform_cache_entry(struct cache_entry_ *entry,
+ enum cache_transformation_t transformation)
+{
+
+ TRACE_IN(transform_cache_entry);
+ switch (transformation) {
+ case CTT_CLEAR:
+ clear_cache_entry(entry);
+ TRACE_OUT(transform_cache_entry);
+ return (0);
+ case CTT_FLUSH:
+ flush_cache_entry(entry);
+ TRACE_OUT(transform_cache_entry);
+ return (0);
+ default:
+ TRACE_OUT(transform_cache_entry);
+ return (-1);
+ }
+}
+
+int
+transform_cache_entry_part(struct cache_entry_ *entry,
+ enum cache_transformation_t transformation, const char *key_part,
+ size_t key_part_size, enum part_position_t part_position)
+{
+ struct cache_common_entry_ *common_entry;
+ struct cache_ht_item_ *ht_item;
+ struct cache_ht_item_data_ *ht_item_data, ht_key;
+
+ struct cache_policy_item_ *item, *connected_item;
+
+ TRACE_IN(transform_cache_entry_part);
+ if (entry->params->entry_type != CET_COMMON) {
+ TRACE_OUT(transform_cache_entry_part);
+ return (-1);
+ }
+
+ if (transformation != CTT_CLEAR) {
+ TRACE_OUT(transform_cache_entry_part);
+ return (-1);
+ }
+
+ memset(&ht_key, 0, sizeof(struct cache_ht_item_data_));
+ ht_key.key = (char *)key_part; /* can't avoid casting here */
+ ht_key.key_size = key_part_size;
+
+ common_entry = (struct cache_common_entry_ *)entry;
+ HASHTABLE_FOREACH(&(common_entry->items), ht_item) {
+ do {
+ ht_item_data = HASHTABLE_ENTRY_FIND_SPECIAL(cache_ht_,
+ ht_item, &ht_key,
+ ht_items_fixed_size_left_cmp_func);
+
+ if (ht_item_data != NULL) {
+ item = ht_item_data->fifo_policy_item;
+ connected_item = item->connected_item;
+
+ common_entry->policies[0]->remove_item_func(
+ common_entry->policies[0],
+ item);
+
+ free(ht_item_data->key);
+ free(ht_item_data->value);
+ HASHTABLE_ENTRY_REMOVE(cache_ht_, ht_item,
+ ht_item_data);
+ --common_entry->items_size;
+
+ common_entry->policies[0]->destroy_item_func(
+ item);
+ if (common_entry->policies_size == 2) {
+ common_entry->policies[1]->remove_item_func(
+ common_entry->policies[1],
+ connected_item);
+ common_entry->policies[1]->destroy_item_func(
+ connected_item);
+ }
+ }
+ } while (ht_item_data != NULL);
+ }
+
+ TRACE_OUT(transform_cache_entry_part);
+ return (0);
+}
diff --git a/usr.sbin/nscd/cachelib.h b/usr.sbin/nscd/cachelib.h
new file mode 100644
index 0000000..b1c8af8
--- /dev/null
+++ b/usr.sbin/nscd/cachelib.h
@@ -0,0 +1,281 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 __NSCD_CACHELIB_H__
+#define __NSCD_CACHELIB_H__
+
+#include <sys/queue.h>
+#include <sys/time.h>
+#include <stdlib.h>
+#include "hashtable.h"
+#include "cacheplcs.h"
+
+enum cache_entry_t {
+ CET_COMMON = 0, /* cache item is atomic */
+ CET_MULTIPART /* cache item is formed part by part */
+};
+
+enum cache_transformation_t {
+ CTT_FLUSH = 0, /* flush the cache - delete all obsolete items */
+ CTT_CLEAR = 1 /* delete all items in the cache */
+};
+
+/* cache deletion policy type enum */
+enum cache_policy_t {
+ CPT_FIFO = 0, /* first-in first-out */
+ CPT_LRU = 1, /* least recently used */
+ CPT_LFU = 2 /* least frequently used */
+};
+
+/* multipart sessions can be used for reading and writing */
+enum cache_mp_session_t {
+ CMPT_READ_SESSION,
+ CMPT_WRITE_SESSION
+};
+
+/*
+ * When doing partial transformations of entries (which are applied for
+ * elements with keys, that contain specified buffer in its left or
+ * right part), this enum will show the needed position of the key part.
+ */
+enum part_position_t {
+ KPPT_LEFT,
+ KPPT_RIGHT
+};
+
+/* num_levels attribute is obsolete, i think - user can always emulate it
+ * by using one entry.
+ * get_time_func is needed to have the clocks-independent counter
+ */
+struct cache_params
+{
+ void (*get_time_func)(struct timeval *);
+};
+
+/*
+ * base structure - normal_cache_entry_params and multipart_cache_entry_params
+ * are "inherited" from it
+ */
+struct cache_entry_params
+{
+ enum cache_entry_t entry_type;
+ char *entry_name;
+};
+
+/* params, used for most entries */
+struct common_cache_entry_params
+{
+ /* inherited fields */
+ enum cache_entry_t entry_type;
+
+ /* unique fields */
+ char *entry_name;
+ size_t cache_entries_size;
+
+ size_t max_elemsize; /* if 0 then no check is made */
+ size_t satisf_elemsize; /* if entry size is exceeded,
+ * this number of elements will be left,
+ * others will be deleted */
+ struct timeval max_lifetime; /* if 0 then no check is made */
+ enum cache_policy_t policy; /* policy used for transformations */
+};
+
+/* params, used for multipart entries */
+struct mp_cache_entry_params
+{
+ /* inherited fields */
+ enum cache_entry_t entry_type;
+ char *entry_name;
+
+ /* unique fields */
+ size_t max_elemsize; /* if 0 then no check is made */
+ size_t max_sessions; /* maximum number of active sessions */
+
+ struct timeval max_lifetime; /* maximum elements lifetime */
+};
+
+struct cache_ht_item_data_
+{
+ /* key is the bytes sequence only - not the null-terminated string */
+ char *key;
+ size_t key_size;
+
+ char *value;
+ size_t value_size;
+
+ struct cache_policy_item_ *fifo_policy_item;
+};
+
+struct cache_ht_item_
+{
+ HASHTABLE_ENTRY_HEAD(ht_item_, struct cache_ht_item_data_) data;
+};
+
+struct cache_entry_
+{
+ char *name;
+ struct cache_entry_params *params;
+};
+
+struct cache_common_entry_
+{
+ char *name;
+ struct cache_entry_params *params;
+
+ struct common_cache_entry_params common_params;
+
+ HASHTABLE_HEAD(cache_ht_, cache_ht_item_) items;
+ size_t items_size;
+
+ /*
+ * Entry always has the FIFO policy, that is used to eliminate old
+ * elements (the ones, with lifetime more than max_lifetime). Besides,
+ * user can specify another policy to be applied, when there are too
+ * many elements in the entry. So policies_size can be 1 or 2.
+ */
+ struct cache_policy_ **policies;
+ size_t policies_size;
+
+ void (*get_time_func)(struct timeval *);
+};
+
+struct cache_mp_data_item_ {
+ char *value;
+ size_t value_size;
+
+ TAILQ_ENTRY(cache_mp_data_item_) entries;
+};
+
+struct cache_mp_write_session_
+{
+ struct cache_mp_entry_ *parent_entry;
+
+ /*
+ * All items are accumulated in this queue. When the session is
+ * committed, they all will be copied to the multipart entry.
+ */
+ TAILQ_HEAD(cache_mp_data_item_head, cache_mp_data_item_) items;
+ size_t items_size;
+
+ TAILQ_ENTRY(cache_mp_write_session_) entries;
+};
+
+struct cache_mp_read_session_
+{
+ struct cache_mp_entry_ *parent_entry;
+ struct cache_mp_data_item_ *current_item;
+
+ TAILQ_ENTRY(cache_mp_read_session_) entries;
+};
+
+struct cache_mp_entry_
+{
+ char *name;
+ struct cache_entry_params *params;
+
+ struct mp_cache_entry_params mp_params;
+
+ /* All opened write sessions */
+ TAILQ_HEAD(write_sessions_head, cache_mp_write_session_) ws_head;
+ size_t ws_size;
+
+ /* All opened read sessions */
+ TAILQ_HEAD(read_sessions_head, cache_mp_read_session_) rs_head;
+ size_t rs_size;
+
+ /*
+ * completed_write_session is the committed write sessions. All read
+ * sessions use data from it. If the completed_write_session is out of
+ * date, but still in use by some of the read sessions, the newly
+ * committed write session is stored in the pending_write_session.
+ * In such a case, completed_write_session will be substituted with
+ * pending_write_session as soon as it won't be used by any of
+ * the read sessions.
+ */
+ struct cache_mp_write_session_ *completed_write_session;
+ struct cache_mp_write_session_ *pending_write_session;
+ struct timeval creation_time;
+ struct timeval last_request_time;
+
+ void (*get_time_func)(struct timeval *);
+};
+
+struct cache_
+{
+ struct cache_params params;
+
+ struct cache_entry_ **entries;
+ size_t entries_capacity;
+ size_t entries_size;
+};
+
+/* simple abstractions - for not to write "struct" every time */
+typedef struct cache_ *cache;
+typedef struct cache_entry_ *cache_entry;
+typedef struct cache_mp_write_session_ *cache_mp_write_session;
+typedef struct cache_mp_read_session_ *cache_mp_read_session;
+
+#define INVALID_CACHE (NULL)
+#define INVALID_CACHE_ENTRY (NULL)
+#define INVALID_CACHE_MP_WRITE_SESSION (NULL)
+#define INVALID_CACHE_MP_READ_SESSION (NULL)
+
+/*
+ * NOTE: all cache operations are thread-unsafe. You must ensure thread-safety
+ * externally, by yourself.
+ */
+
+/* cache initialization/destruction routines */
+extern cache init_cache(struct cache_params const *);
+extern void destroy_cache(cache);
+
+/* cache entries manipulation routines */
+extern int register_cache_entry(cache, struct cache_entry_params const *);
+extern int unregister_cache_entry(cache, const char *);
+extern cache_entry find_cache_entry(cache, const char *);
+
+/* read/write operations used on common entries */
+extern int cache_read(cache_entry, const char *, size_t, char *, size_t *);
+extern int cache_write(cache_entry, const char *, size_t, char const *, size_t);
+
+/* read/write operations used on multipart entries */
+extern cache_mp_write_session open_cache_mp_write_session(cache_entry);
+extern int cache_mp_write(cache_mp_write_session, char *, size_t);
+extern void abandon_cache_mp_write_session(cache_mp_write_session);
+extern void close_cache_mp_write_session(cache_mp_write_session);
+
+extern cache_mp_read_session open_cache_mp_read_session(cache_entry);
+extern int cache_mp_read(cache_mp_read_session, char *, size_t *);
+extern void close_cache_mp_read_session(cache_mp_read_session);
+
+/* transformation routines */
+extern int transform_cache_entry(cache_entry, enum cache_transformation_t);
+extern int transform_cache_entry_part(cache_entry, enum cache_transformation_t,
+ const char *, size_t, enum part_position_t);
+
+#endif
diff --git a/usr.sbin/nscd/cacheplcs.c b/usr.sbin/nscd/cacheplcs.c
new file mode 100644
index 0000000..7b0c8eb
--- /dev/null
+++ b/usr.sbin/nscd/cacheplcs.c
@@ -0,0 +1,586 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <assert.h>
+#include <string.h>
+#include "cacheplcs.h"
+#include "debug.h"
+
+static void cache_fifo_policy_update_item(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+static void cache_lfu_policy_add_item(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+static struct cache_policy_item_ * cache_lfu_policy_create_item(void);
+static void cache_lfu_policy_destroy_item(struct cache_policy_item_ *);
+static struct cache_policy_item_ *cache_lfu_policy_get_first_item(
+ struct cache_policy_ *);
+static struct cache_policy_item_ *cache_lfu_policy_get_last_item(
+ struct cache_policy_ *);
+static struct cache_policy_item_ *cache_lfu_policy_get_next_item(
+ struct cache_policy_ *, struct cache_policy_item_ *);
+static struct cache_policy_item_ *cache_lfu_policy_get_prev_item(
+ struct cache_policy_ *, struct cache_policy_item_ *);
+static void cache_lfu_policy_remove_item(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+static void cache_lfu_policy_update_item(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+static void cache_lru_policy_update_item(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+static void cache_queue_policy_add_item(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+static struct cache_policy_item_ * cache_queue_policy_create_item();
+static void cache_queue_policy_destroy_item(struct cache_policy_item_ *);
+static struct cache_policy_item_ *cache_queue_policy_get_first_item(
+ struct cache_policy_ *);
+static struct cache_policy_item_ *cache_queue_policy_get_last_item(
+ struct cache_policy_ *);
+static struct cache_policy_item_ *cache_queue_policy_get_next_item(
+ struct cache_policy_ *, struct cache_policy_item_ *);
+static struct cache_policy_item_ *cache_queue_policy_get_prev_item(
+ struct cache_policy_ *, struct cache_policy_item_ *);
+static void cache_queue_policy_remove_item(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+static void destroy_cache_queue_policy(struct cache_queue_policy_ *);
+static struct cache_queue_policy_ *init_cache_queue_policy(void);
+
+/*
+ * All cache_queue_policy_XXX functions below will be used to fill
+ * the cache_queue_policy structure. They implement the most functionality of
+ * LRU and FIFO policies. LRU and FIFO policies are actually the
+ * cache_queue_policy_ with cache_update_item function changed.
+ */
+static struct cache_policy_item_ *
+cache_queue_policy_create_item()
+{
+ struct cache_queue_policy_item_ *retval;
+
+ TRACE_IN(cache_queue_policy_create_item);
+ retval = (struct cache_queue_policy_item_ *)calloc(1,
+ sizeof(struct cache_queue_policy_item_));
+ assert(retval != NULL);
+
+ TRACE_OUT(cache_queue_policy_create_item);
+ return ((struct cache_policy_item_ *)retval);
+}
+
+static void
+cache_queue_policy_destroy_item(struct cache_policy_item_ *item)
+{
+
+ TRACE_IN(cache_queue_policy_destroy_item);
+ assert(item != NULL);
+ free(item);
+ TRACE_OUT(cache_queue_policy_destroy_item);
+}
+
+static void
+cache_queue_policy_add_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_queue_policy_ *queue_policy;
+ struct cache_queue_policy_item_ *queue_item;
+
+ TRACE_IN(cache_queue_policy_add_item);
+ queue_policy = (struct cache_queue_policy_ *)policy;
+ queue_item = (struct cache_queue_policy_item_ *)item;
+ TAILQ_INSERT_TAIL(&queue_policy->head, queue_item, entries);
+ TRACE_OUT(cache_queue_policy_add_item);
+}
+
+static void
+cache_queue_policy_remove_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_queue_policy_ *queue_policy;
+ struct cache_queue_policy_item_ *queue_item;
+
+ TRACE_IN(cache_queue_policy_remove_item);
+ queue_policy = (struct cache_queue_policy_ *)policy;
+ queue_item = (struct cache_queue_policy_item_ *)item;
+ TAILQ_REMOVE(&queue_policy->head, queue_item, entries);
+ TRACE_OUT(cache_queue_policy_remove_item);
+}
+
+static struct cache_policy_item_ *
+cache_queue_policy_get_first_item(struct cache_policy_ *policy)
+{
+ struct cache_queue_policy_ *queue_policy;
+
+ TRACE_IN(cache_queue_policy_get_first_item);
+ queue_policy = (struct cache_queue_policy_ *)policy;
+ TRACE_OUT(cache_queue_policy_get_first_item);
+ return ((struct cache_policy_item_ *)TAILQ_FIRST(&queue_policy->head));
+}
+
+static struct cache_policy_item_ *
+cache_queue_policy_get_last_item(struct cache_policy_ *policy)
+{
+ struct cache_queue_policy_ *queue_policy;
+
+ TRACE_IN(cache_queue_policy_get_last_item);
+ queue_policy = (struct cache_queue_policy_ *)policy;
+ TRACE_OUT(cache_queue_policy_get_last_item);
+ return ((struct cache_policy_item_ *)TAILQ_LAST(&queue_policy->head,
+ cache_queue_policy_head_));
+}
+
+static struct cache_policy_item_ *
+cache_queue_policy_get_next_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_queue_policy_ *queue_policy;
+ struct cache_queue_policy_item_ *queue_item;
+
+ TRACE_IN(cache_queue_policy_get_next_item);
+ queue_policy = (struct cache_queue_policy_ *)policy;
+ queue_item = (struct cache_queue_policy_item_ *)item;
+
+ TRACE_OUT(cache_queue_policy_get_next_item);
+ return ((struct cache_policy_item_ *)TAILQ_NEXT(queue_item, entries));
+}
+
+static struct cache_policy_item_ *
+cache_queue_policy_get_prev_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_queue_policy_ *queue_policy;
+ struct cache_queue_policy_item_ *queue_item;
+
+ TRACE_IN(cache_queue_policy_get_prev_item);
+ queue_policy = (struct cache_queue_policy_ *)policy;
+ queue_item = (struct cache_queue_policy_item_ *)item;
+
+ TRACE_OUT(cache_queue_policy_get_prev_item);
+ return ((struct cache_policy_item_ *)TAILQ_PREV(queue_item,
+ cache_queue_policy_head_, entries));
+}
+
+/*
+ * Initializes cache_queue_policy_ by filling the structure with the functions
+ * pointers, defined above
+ */
+static struct cache_queue_policy_ *
+init_cache_queue_policy(void)
+{
+ struct cache_queue_policy_ *retval;
+
+ TRACE_IN(init_cache_queue_policy);
+ retval = (struct cache_queue_policy_ *)calloc(1,
+ sizeof(struct cache_queue_policy_));
+ assert(retval != NULL);
+
+ retval->parent_data.create_item_func = cache_queue_policy_create_item;
+ retval->parent_data.destroy_item_func = cache_queue_policy_destroy_item;
+
+ retval->parent_data.add_item_func = cache_queue_policy_add_item;
+ retval->parent_data.remove_item_func = cache_queue_policy_remove_item;
+
+ retval->parent_data.get_first_item_func =
+ cache_queue_policy_get_first_item;
+ retval->parent_data.get_last_item_func =
+ cache_queue_policy_get_last_item;
+ retval->parent_data.get_next_item_func =
+ cache_queue_policy_get_next_item;
+ retval->parent_data.get_prev_item_func =
+ cache_queue_policy_get_prev_item;
+
+ TAILQ_INIT(&retval->head);
+ TRACE_OUT(init_cache_queue_policy);
+ return (retval);
+}
+
+static void
+destroy_cache_queue_policy(struct cache_queue_policy_ *queue_policy)
+{
+ struct cache_queue_policy_item_ *queue_item;
+
+ TRACE_IN(destroy_cache_queue_policy);
+ while (!TAILQ_EMPTY(&queue_policy->head)) {
+ queue_item = TAILQ_FIRST(&queue_policy->head);
+ TAILQ_REMOVE(&queue_policy->head, queue_item, entries);
+ cache_queue_policy_destroy_item(
+ (struct cache_policy_item_ *)queue_item);
+ }
+ free(queue_policy);
+ TRACE_OUT(destroy_cache_queue_policy);
+}
+
+/*
+ * Makes cache_queue_policy_ behave like FIFO policy - we don't do anything,
+ * when the cache element is updated. So it always stays in its initial
+ * position in the queue - that is exactly the FIFO functionality.
+ */
+static void
+cache_fifo_policy_update_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+
+ TRACE_IN(cache_fifo_policy_update_item);
+ /* policy and item arguments are ignored */
+ TRACE_OUT(cache_fifo_policy_update_item);
+}
+
+struct cache_policy_ *
+init_cache_fifo_policy()
+{
+ struct cache_queue_policy_ *retval;
+
+ TRACE_IN(init_cache_fifo_policy);
+ retval = init_cache_queue_policy();
+ retval->parent_data.update_item_func = cache_fifo_policy_update_item;
+
+ TRACE_OUT(init_cache_fifo_policy);
+ return ((struct cache_policy_ *)retval);
+}
+
+void
+destroy_cache_fifo_policy(struct cache_policy_ *policy)
+{
+ struct cache_queue_policy_ *queue_policy;
+
+ TRACE_IN(destroy_cache_fifo_policy);
+ queue_policy = (struct cache_queue_policy_ *)policy;
+ destroy_cache_queue_policy(queue_policy);
+ TRACE_OUT(destroy_cache_fifo_policy);
+}
+
+/*
+ * Makes cache_queue_policy_ behave like LRU policy. On each update, cache
+ * element is moved to the end of the queue - so it would be deleted in last
+ * turn. That is exactly the LRU policy functionality.
+ */
+static void
+cache_lru_policy_update_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_queue_policy_ *queue_policy;
+ struct cache_queue_policy_item_ *queue_item;
+
+ TRACE_IN(cache_lru_policy_update_item);
+ queue_policy = (struct cache_queue_policy_ *)policy;
+ queue_item = (struct cache_queue_policy_item_ *)item;
+
+ TAILQ_REMOVE(&queue_policy->head, queue_item, entries);
+ TAILQ_INSERT_TAIL(&queue_policy->head, queue_item, entries);
+ TRACE_OUT(cache_lru_policy_update_item);
+}
+
+struct cache_policy_ *
+init_cache_lru_policy()
+{
+ struct cache_queue_policy_ *retval;
+
+ TRACE_IN(init_cache_lru_policy);
+ retval = init_cache_queue_policy();
+ retval->parent_data.update_item_func = cache_lru_policy_update_item;
+
+ TRACE_OUT(init_cache_lru_policy);
+ return ((struct cache_policy_ *)retval);
+}
+
+void
+destroy_cache_lru_policy(struct cache_policy_ *policy)
+{
+ struct cache_queue_policy_ *queue_policy;
+
+ TRACE_IN(destroy_cache_lru_policy);
+ queue_policy = (struct cache_queue_policy_ *)policy;
+ destroy_cache_queue_policy(queue_policy);
+ TRACE_OUT(destroy_cache_lru_policy);
+}
+
+/*
+ * LFU (least frequently used) policy implementation differs much from the
+ * LRU and FIFO (both based on cache_queue_policy_). Almost all cache_policy_
+ * functions are implemented specifically for this policy. The idea of this
+ * policy is to represent frequency (real number) as the integer number and
+ * use it as the index in the array. Each array's element is
+ * the list of elements. For example, if we have the 100-elements
+ * array for this policy, the elements with frequency 0.1 (calls per-second)
+ * would be in 10th element of the array.
+ */
+static struct cache_policy_item_ *
+cache_lfu_policy_create_item(void)
+{
+ struct cache_lfu_policy_item_ *retval;
+
+ TRACE_IN(cache_lfu_policy_create_item);
+ retval = (struct cache_lfu_policy_item_ *)calloc(1,
+ sizeof(struct cache_lfu_policy_item_));
+ assert(retval != NULL);
+
+ TRACE_OUT(cache_lfu_policy_create_item);
+ return ((struct cache_policy_item_ *)retval);
+}
+
+static void
+cache_lfu_policy_destroy_item(struct cache_policy_item_ *item)
+{
+
+ TRACE_IN(cache_lfu_policy_destroy_item);
+ assert(item != NULL);
+ free(item);
+ TRACE_OUT(cache_lfu_policy_destroy_item);
+}
+
+/*
+ * When placed in the LFU policy queue for the first time, the maximum
+ * frequency is assigned to the element
+ */
+static void
+cache_lfu_policy_add_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_lfu_policy_ *lfu_policy;
+ struct cache_lfu_policy_item_ *lfu_item;
+
+ TRACE_IN(cache_lfu_policy_add_item);
+ lfu_policy = (struct cache_lfu_policy_ *)policy;
+ lfu_item = (struct cache_lfu_policy_item_ *)item;
+
+ lfu_item->frequency = CACHELIB_MAX_FREQUENCY - 1;
+ TAILQ_INSERT_HEAD(&(lfu_policy->groups[CACHELIB_MAX_FREQUENCY - 1]),
+ lfu_item, entries);
+ TRACE_OUT(cache_lfu_policy_add_item);
+}
+
+/*
+ * On each update the frequency of the element is recalculated and, if it
+ * changed, the element would be moved to the another place in the array.
+ */
+static void
+cache_lfu_policy_update_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_lfu_policy_ *lfu_policy;
+ struct cache_lfu_policy_item_ *lfu_item;
+ int index;
+
+ TRACE_IN(cache_lfu_policy_update_item);
+ lfu_policy = (struct cache_lfu_policy_ *)policy;
+ lfu_item = (struct cache_lfu_policy_item_ *)item;
+
+ /*
+ * We calculate the square of the request_count to avoid grouping of
+ * all elements at the start of the array (for example, if array size is
+ * 100 and most of its elements has frequency below the 0.01, they
+ * all would be grouped in the first array's position). Other
+ * techniques should be used here later to ensure, that elements are
+ * equally distributed in the array and not grouped in its beginning.
+ */
+ if (lfu_item->parent_data.last_request_time.tv_sec !=
+ lfu_item->parent_data.creation_time.tv_sec) {
+ index = ((double)lfu_item->parent_data.request_count *
+ (double)lfu_item->parent_data.request_count /
+ (lfu_item->parent_data.last_request_time.tv_sec -
+ lfu_item->parent_data.creation_time.tv_sec + 1)) *
+ CACHELIB_MAX_FREQUENCY;
+ if (index >= CACHELIB_MAX_FREQUENCY)
+ index = CACHELIB_MAX_FREQUENCY - 1;
+ } else
+ index = CACHELIB_MAX_FREQUENCY - 1;
+
+ TAILQ_REMOVE(&(lfu_policy->groups[lfu_item->frequency]), lfu_item,
+ entries);
+ lfu_item->frequency = index;
+ TAILQ_INSERT_HEAD(&(lfu_policy->groups[index]), lfu_item, entries);
+
+ TRACE_OUT(cache_lfu_policy_update_item);
+}
+
+static void
+cache_lfu_policy_remove_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_lfu_policy_ *lfu_policy;
+ struct cache_lfu_policy_item_ *lfu_item;
+
+ TRACE_IN(cache_lfu_policy_remove_item);
+ lfu_policy = (struct cache_lfu_policy_ *)policy;
+ lfu_item = (struct cache_lfu_policy_item_ *)item;
+
+ TAILQ_REMOVE(&(lfu_policy->groups[lfu_item->frequency]), lfu_item,
+ entries);
+ TRACE_OUT(cache_lfu_policy_remove_item);
+}
+
+static struct cache_policy_item_ *
+cache_lfu_policy_get_first_item(struct cache_policy_ *policy)
+{
+ struct cache_lfu_policy_ *lfu_policy;
+ struct cache_lfu_policy_item_ *lfu_item;
+ int i;
+
+ TRACE_IN(cache_lfu_policy_get_first_item);
+ lfu_item = NULL;
+ lfu_policy = (struct cache_lfu_policy_ *)policy;
+ for (i = 0; i < CACHELIB_MAX_FREQUENCY; ++i)
+ if (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) {
+ lfu_item = TAILQ_FIRST(&(lfu_policy->groups[i]));
+ break;
+ }
+
+ TRACE_OUT(cache_lfu_policy_get_first_item);
+ return ((struct cache_policy_item_ *)lfu_item);
+}
+
+static struct cache_policy_item_ *
+cache_lfu_policy_get_last_item(struct cache_policy_ *policy)
+{
+ struct cache_lfu_policy_ *lfu_policy;
+ struct cache_lfu_policy_item_ *lfu_item;
+ int i;
+
+ TRACE_IN(cache_lfu_policy_get_last_item);
+ lfu_item = NULL;
+ lfu_policy = (struct cache_lfu_policy_ *)policy;
+ for (i = CACHELIB_MAX_FREQUENCY - 1; i >= 0; --i)
+ if (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) {
+ lfu_item = TAILQ_LAST(&(lfu_policy->groups[i]),
+ cache_lfu_policy_group_);
+ break;
+ }
+
+ TRACE_OUT(cache_lfu_policy_get_last_item);
+ return ((struct cache_policy_item_ *)lfu_item);
+}
+
+static struct cache_policy_item_ *
+cache_lfu_policy_get_next_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_lfu_policy_ *lfu_policy;
+ struct cache_lfu_policy_item_ *lfu_item;
+ int i;
+
+ TRACE_IN(cache_lfu_policy_get_next_item);
+ lfu_policy = (struct cache_lfu_policy_ *)policy;
+ lfu_item = TAILQ_NEXT((struct cache_lfu_policy_item_ *)item, entries);
+ if (lfu_item == NULL)
+ {
+ for (i = ((struct cache_lfu_policy_item_ *)item)->frequency + 1;
+ i < CACHELIB_MAX_FREQUENCY; ++i) {
+ if (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) {
+ lfu_item = TAILQ_FIRST(&(lfu_policy->groups[i]));
+ break;
+ }
+ }
+ }
+
+ TRACE_OUT(cache_lfu_policy_get_next_item);
+ return ((struct cache_policy_item_ *)lfu_item);
+}
+
+static struct cache_policy_item_ *
+cache_lfu_policy_get_prev_item(struct cache_policy_ *policy,
+ struct cache_policy_item_ *item)
+{
+ struct cache_lfu_policy_ *lfu_policy;
+ struct cache_lfu_policy_item_ *lfu_item;
+ int i;
+
+ TRACE_IN(cache_lfu_policy_get_prev_item);
+ lfu_policy = (struct cache_lfu_policy_ *)policy;
+ lfu_item = TAILQ_PREV((struct cache_lfu_policy_item_ *)item,
+ cache_lfu_policy_group_, entries);
+ if (lfu_item == NULL)
+ {
+ for (i = ((struct cache_lfu_policy_item_ *)item)->frequency - 1;
+ i >= 0; --i)
+ if (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) {
+ lfu_item = TAILQ_LAST(&(lfu_policy->groups[i]),
+ cache_lfu_policy_group_);
+ break;
+ }
+ }
+
+ TRACE_OUT(cache_lfu_policy_get_prev_item);
+ return ((struct cache_policy_item_ *)lfu_item);
+}
+
+/*
+ * Initializes the cache_policy_ structure by filling it with appropriate
+ * functions pointers
+ */
+struct cache_policy_ *
+init_cache_lfu_policy()
+{
+ int i;
+ struct cache_lfu_policy_ *retval;
+
+ TRACE_IN(init_cache_lfu_policy);
+ retval = (struct cache_lfu_policy_ *)calloc(1,
+ sizeof(struct cache_lfu_policy_));
+ assert(retval != NULL);
+
+ retval->parent_data.create_item_func = cache_lfu_policy_create_item;
+ retval->parent_data.destroy_item_func = cache_lfu_policy_destroy_item;
+
+ retval->parent_data.add_item_func = cache_lfu_policy_add_item;
+ retval->parent_data.update_item_func = cache_lfu_policy_update_item;
+ retval->parent_data.remove_item_func = cache_lfu_policy_remove_item;
+
+ retval->parent_data.get_first_item_func =
+ cache_lfu_policy_get_first_item;
+ retval->parent_data.get_last_item_func =
+ cache_lfu_policy_get_last_item;
+ retval->parent_data.get_next_item_func =
+ cache_lfu_policy_get_next_item;
+ retval->parent_data.get_prev_item_func =
+ cache_lfu_policy_get_prev_item;
+
+ for (i = 0; i < CACHELIB_MAX_FREQUENCY; ++i)
+ TAILQ_INIT(&(retval->groups[i]));
+
+ TRACE_OUT(init_cache_lfu_policy);
+ return ((struct cache_policy_ *)retval);
+}
+
+void
+destroy_cache_lfu_policy(struct cache_policy_ *policy)
+{
+ int i;
+ struct cache_lfu_policy_ *lfu_policy;
+ struct cache_lfu_policy_item_ *lfu_item;
+
+ TRACE_IN(destroy_cache_lfu_policy);
+ lfu_policy = (struct cache_lfu_policy_ *)policy;
+ for (i = 0; i < CACHELIB_MAX_FREQUENCY; ++i) {
+ while (!TAILQ_EMPTY(&(lfu_policy->groups[i]))) {
+ lfu_item = TAILQ_FIRST(&(lfu_policy->groups[i]));
+ TAILQ_REMOVE(&(lfu_policy->groups[i]), lfu_item,
+ entries);
+ cache_lfu_policy_destroy_item(
+ (struct cache_policy_item_ *)lfu_item);
+ }
+ }
+ free(policy);
+ TRACE_OUT(destroy_cache_lfu_policy);
+}
diff --git a/usr.sbin/nscd/cacheplcs.h b/usr.sbin/nscd/cacheplcs.h
new file mode 100644
index 0000000..8eba15f
--- /dev/null
+++ b/usr.sbin/nscd/cacheplcs.h
@@ -0,0 +1,137 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 __NSCD_CACHEPLCS_H__
+#define __NSCD_CACHEPLCS_H__
+
+#include <sys/queue.h>
+#include <sys/time.h>
+#include <stdlib.h>
+
+/* common policy definitions */
+#define CACHELIB_MAX_FREQUENCY 100
+
+/*
+ * cache_policy_item_ represents some abstract cache element in the policy
+ * queue. connected_item pointers to the corresponding cache_policy_item_ in
+ * another policy queue.
+ */
+struct cache_policy_item_
+{
+ char *key;
+ size_t key_size;
+
+ size_t request_count;
+ struct timeval last_request_time;
+ struct timeval creation_time;
+
+ struct cache_policy_item_ *connected_item;
+};
+
+/*
+ * cache_policy_ represents an abstract policy queue. It can be customized by
+ * setting appropriate function pointers
+ */
+struct cache_policy_
+{
+ struct cache_policy_item_* (*create_item_func)();
+ void (*destroy_item_func)(struct cache_policy_item_ *);
+
+ void (*add_item_func)(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+ void (*remove_item_func)(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+ void (*update_item_func)(struct cache_policy_ *,
+ struct cache_policy_item_ *);
+
+ struct cache_policy_item_ *(*get_first_item_func)(
+ struct cache_policy_ *);
+ struct cache_policy_item_ *(*get_last_item_func)(
+ struct cache_policy_ *);
+ struct cache_policy_item_ *(*get_next_item_func)(
+ struct cache_policy_ *, struct cache_policy_item_ *);
+ struct cache_policy_item_ *(*get_prev_item_func)(
+ struct cache_policy_ *, struct cache_policy_item_ *);
+};
+
+/*
+ * LFU cache policy item "inherited" from cache_policy_item_ structure
+ */
+struct cache_lfu_policy_item_
+{
+ struct cache_policy_item_ parent_data;
+ int frequency;
+
+ TAILQ_ENTRY(cache_lfu_policy_item_) entries;
+};
+
+TAILQ_HEAD(cache_lfu_policy_group_, cache_lfu_policy_item_);
+
+/*
+ * LFU policy queue "inherited" from cache_policy_.
+ */
+struct cache_lfu_policy_
+{
+ struct cache_policy_ parent_data;
+ struct cache_lfu_policy_group_ groups[CACHELIB_MAX_FREQUENCY];
+};
+
+/*
+ * LRU and FIFO policies item "inherited" from cache_policy_item_
+ */
+struct cache_queue_policy_item_
+{
+ struct cache_policy_item_ parent_data;
+ TAILQ_ENTRY(cache_queue_policy_item_) entries;
+};
+
+/*
+ * LRU and FIFO policies "inherited" from cache_policy_
+ */
+struct cache_queue_policy_
+{
+ struct cache_policy_ parent_data;
+ TAILQ_HEAD(cache_queue_policy_head_, cache_queue_policy_item_) head;
+};
+
+typedef struct cache_queue_policy_ cache_fifo_policy_;
+typedef struct cache_queue_policy_ cache_lru_policy_;
+
+/* fifo policy routines */
+extern struct cache_policy_ *init_cache_fifo_policy();
+extern void destroy_cache_fifo_policy(struct cache_policy_ *);
+
+/* lru policy routines */
+extern struct cache_policy_ *init_cache_lru_policy();
+extern void destroy_cache_lru_policy(struct cache_policy_ *);
+
+/* lfu policy routines */
+extern struct cache_policy_ *init_cache_lfu_policy();
+extern void destroy_cache_lfu_policy(struct cache_policy_ *);
+
+#endif
diff --git a/usr.sbin/nscd/config.c b/usr.sbin/nscd/config.c
new file mode 100644
index 0000000..5f20ad3
--- /dev/null
+++ b/usr.sbin/nscd/config.c
@@ -0,0 +1,579 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <assert.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "config.h"
+#include "debug.h"
+#include "log.h"
+
+/*
+ * Default entries, which always exist in the configuration
+ */
+const char *c_default_entries[6] = {
+ NSDB_PASSWD,
+ NSDB_GROUP,
+ NSDB_HOSTS,
+ NSDB_SERVICES,
+ NSDB_PROTOCOLS,
+ NSDB_RPC
+ };
+
+static int configuration_entry_cmp(const void *, const void *);
+static int configuration_entry_sort_cmp(const void *, const void *);
+static int configuration_entry_cache_mp_sort_cmp(const void *, const void *);
+static int configuration_entry_cache_mp_cmp(const void *, const void *);
+static int configuration_entry_cache_mp_part_cmp(const void *, const void *);
+static struct configuration_entry *create_configuration_entry(const char *,
+ struct timeval const *, struct timeval const *,
+ struct common_cache_entry_params const *,
+ struct common_cache_entry_params const *,
+ struct mp_cache_entry_params const *);
+
+static int
+configuration_entry_sort_cmp(const void *e1, const void *e2)
+{
+ return (strcmp((*((struct configuration_entry **)e1))->name,
+ (*((struct configuration_entry **)e2))->name
+ ));
+}
+
+static int
+configuration_entry_cmp(const void *e1, const void *e2)
+{
+ return (strcmp((const char *)e1,
+ (*((struct configuration_entry **)e2))->name
+ ));
+}
+
+static int
+configuration_entry_cache_mp_sort_cmp(const void *e1, const void *e2)
+{
+ return (strcmp((*((cache_entry *)e1))->params->entry_name,
+ (*((cache_entry *)e2))->params->entry_name
+ ));
+}
+
+static int
+configuration_entry_cache_mp_cmp(const void *e1, const void *e2)
+{
+ return (strcmp((const char *)e1,
+ (*((cache_entry *)e2))->params->entry_name
+ ));
+}
+
+static int
+configuration_entry_cache_mp_part_cmp(const void *e1, const void *e2)
+{
+ return (strncmp((const char *)e1,
+ (*((cache_entry *)e2))->params->entry_name,
+ strlen((const char *)e1)
+ ));
+}
+
+static struct configuration_entry *
+create_configuration_entry(const char *name,
+ struct timeval const *common_timeout,
+ struct timeval const *mp_timeout,
+ struct common_cache_entry_params const *positive_params,
+ struct common_cache_entry_params const *negative_params,
+ struct mp_cache_entry_params const *mp_params)
+{
+ struct configuration_entry *retval;
+ size_t size;
+ int res;
+
+ TRACE_IN(create_configuration_entry);
+ assert(name != NULL);
+ assert(positive_params != NULL);
+ assert(negative_params != NULL);
+ assert(mp_params != NULL);
+
+ retval = (struct configuration_entry *)calloc(1,
+ sizeof(struct configuration_entry));
+ assert(retval != NULL);
+
+ res = pthread_mutex_init(&retval->positive_cache_lock, NULL);
+ if (res != 0) {
+ free(retval);
+ LOG_ERR_2("create_configuration_entry",
+ "can't create positive cache lock");
+ TRACE_OUT(create_configuration_entry);
+ return (NULL);
+ }
+
+ res = pthread_mutex_init(&retval->negative_cache_lock, NULL);
+ if (res != 0) {
+ pthread_mutex_destroy(&retval->positive_cache_lock);
+ free(retval);
+ LOG_ERR_2("create_configuration_entry",
+ "can't create negative cache lock");
+ TRACE_OUT(create_configuration_entry);
+ return (NULL);
+ }
+
+ res = pthread_mutex_init(&retval->mp_cache_lock, NULL);
+ if (res != 0) {
+ pthread_mutex_destroy(&retval->positive_cache_lock);
+ pthread_mutex_destroy(&retval->negative_cache_lock);
+ free(retval);
+ LOG_ERR_2("create_configuration_entry",
+ "can't create negative cache lock");
+ TRACE_OUT(create_configuration_entry);
+ return (NULL);
+ }
+
+ memcpy(&retval->positive_cache_params, positive_params,
+ sizeof(struct common_cache_entry_params));
+ memcpy(&retval->negative_cache_params, negative_params,
+ sizeof(struct common_cache_entry_params));
+ memcpy(&retval->mp_cache_params, mp_params,
+ sizeof(struct mp_cache_entry_params));
+
+ size = strlen(name);
+ retval->name = (char *)calloc(1, size + 1);
+ assert(retval->name != NULL);
+ memcpy(retval->name, name, size);
+
+ memcpy(&retval->common_query_timeout, common_timeout,
+ sizeof(struct timeval));
+ memcpy(&retval->mp_query_timeout, mp_timeout,
+ sizeof(struct timeval));
+
+ asprintf(&retval->positive_cache_params.entry_name, "%s+", name);
+ assert(retval->positive_cache_params.entry_name != NULL);
+
+ asprintf(&retval->negative_cache_params.entry_name, "%s-", name);
+ assert(retval->negative_cache_params.entry_name != NULL);
+
+ asprintf(&retval->mp_cache_params.entry_name, "%s*", name);
+ assert(retval->mp_cache_params.entry_name != NULL);
+
+ TRACE_OUT(create_configuration_entry);
+ return (retval);
+}
+
+/*
+ * Creates configuration entry and fills it with default values
+ */
+struct configuration_entry *
+create_def_configuration_entry(const char *name)
+{
+ struct common_cache_entry_params positive_params, negative_params;
+ struct mp_cache_entry_params mp_params;
+ struct timeval default_common_timeout, default_mp_timeout;
+
+ struct configuration_entry *res = NULL;
+
+ TRACE_IN(create_def_configuration_entry);
+ memset(&positive_params, 0,
+ sizeof(struct common_cache_entry_params));
+ positive_params.entry_type = CET_COMMON;
+ positive_params.cache_entries_size = DEFAULT_CACHE_HT_SIZE;
+ positive_params.max_elemsize = DEFAULT_POSITIVE_ELEMENTS_SIZE;
+ positive_params.satisf_elemsize = DEFAULT_POSITIVE_ELEMENTS_SIZE / 2;
+ positive_params.max_lifetime.tv_sec = DEFAULT_POSITIVE_LIFETIME;
+ positive_params.policy = CPT_LRU;
+
+ memcpy(&negative_params, &positive_params,
+ sizeof(struct common_cache_entry_params));
+ negative_params.max_elemsize = DEFAULT_NEGATIVE_ELEMENTS_SIZE;
+ negative_params.satisf_elemsize = DEFAULT_NEGATIVE_ELEMENTS_SIZE / 2;
+ negative_params.max_lifetime.tv_sec = DEFAULT_NEGATIVE_LIFETIME;
+ negative_params.policy = CPT_FIFO;
+
+ memset(&default_common_timeout, 0, sizeof(struct timeval));
+ default_common_timeout.tv_sec = DEFAULT_COMMON_ENTRY_TIMEOUT;
+
+ memset(&default_mp_timeout, 0, sizeof(struct timeval));
+ default_mp_timeout.tv_sec = DEFAULT_MP_ENTRY_TIMEOUT;
+
+ memset(&mp_params, 0,
+ sizeof(struct mp_cache_entry_params));
+ mp_params.entry_type = CET_MULTIPART;
+ mp_params.max_elemsize = DEFAULT_MULTIPART_ELEMENTS_SIZE;
+ mp_params.max_sessions = DEFAULT_MULITPART_SESSIONS_SIZE;
+ mp_params.max_lifetime.tv_sec = DEFAULT_MULITPART_LIFETIME;
+
+ res = create_configuration_entry(name, &default_common_timeout,
+ &default_mp_timeout, &positive_params, &negative_params,
+ &mp_params);
+
+ TRACE_OUT(create_def_configuration_entry);
+ return (res);
+}
+
+void
+destroy_configuration_entry(struct configuration_entry *entry)
+{
+ TRACE_IN(destroy_configuration_entry);
+ assert(entry != NULL);
+ pthread_mutex_destroy(&entry->positive_cache_lock);
+ pthread_mutex_destroy(&entry->negative_cache_lock);
+ pthread_mutex_destroy(&entry->mp_cache_lock);
+ free(entry->name);
+ free(entry->positive_cache_params.entry_name);
+ free(entry->negative_cache_params.entry_name);
+ free(entry->mp_cache_params.entry_name);
+ free(entry->mp_cache_entries);
+ free(entry);
+ TRACE_OUT(destroy_configuration_entry);
+}
+
+int
+add_configuration_entry(struct configuration *config,
+ struct configuration_entry *entry)
+{
+ TRACE_IN(add_configuration_entry);
+ assert(entry != NULL);
+ assert(entry->name != NULL);
+ if (configuration_find_entry(config, entry->name) != NULL) {
+ TRACE_OUT(add_configuration_entry);
+ return (-1);
+ }
+
+ if (config->entries_size == config->entries_capacity) {
+ struct configuration_entry **new_entries;
+
+ config->entries_capacity *= 2;
+ new_entries = (struct configuration_entry **)calloc(1,
+ sizeof(struct configuration_entry *) *
+ config->entries_capacity);
+ assert(new_entries != NULL);
+ memcpy(new_entries, config->entries,
+ sizeof(struct configuration_entry *) *
+ config->entries_size);
+
+ free(config->entries);
+ config->entries = new_entries;
+ }
+
+ config->entries[config->entries_size++] = entry;
+ qsort(config->entries, config->entries_size,
+ sizeof(struct configuration_entry *),
+ configuration_entry_sort_cmp);
+
+ TRACE_OUT(add_configuration_entry);
+ return (0);
+}
+
+size_t
+configuration_get_entries_size(struct configuration *config)
+{
+ TRACE_IN(configuration_get_entries_size);
+ assert(config != NULL);
+ TRACE_OUT(configuration_get_entries_size);
+ return (config->entries_size);
+}
+
+struct configuration_entry *
+configuration_get_entry(struct configuration *config, size_t index)
+{
+ TRACE_IN(configuration_get_entry);
+ assert(config != NULL);
+ assert(index < config->entries_size);
+ TRACE_OUT(configuration_get_entry);
+ return (config->entries[index]);
+}
+
+struct configuration_entry *
+configuration_find_entry(struct configuration *config,
+ const char *name)
+{
+ struct configuration_entry **retval;
+
+ TRACE_IN(configuration_find_entry);
+
+ retval = bsearch(name, config->entries, config->entries_size,
+ sizeof(struct configuration_entry *), configuration_entry_cmp);
+ TRACE_OUT(configuration_find_entry);
+
+ return ((retval != NULL) ? *retval : NULL);
+}
+
+/*
+ * All multipart cache entries are stored in the configuration_entry in the
+ * sorted array (sorted by names). The 3 functions below manage this array.
+ */
+
+int
+configuration_entry_add_mp_cache_entry(struct configuration_entry *config_entry,
+ cache_entry c_entry)
+{
+ cache_entry *new_mp_entries, *old_mp_entries;
+
+ TRACE_IN(configuration_entry_add_mp_cache_entry);
+ ++config_entry->mp_cache_entries_size;
+ new_mp_entries = (cache_entry *)malloc(sizeof(cache_entry) *
+ config_entry->mp_cache_entries_size);
+ assert(new_mp_entries != NULL);
+ new_mp_entries[0] = c_entry;
+
+ if (config_entry->mp_cache_entries_size - 1 > 0) {
+ memcpy(new_mp_entries + 1,
+ config_entry->mp_cache_entries,
+ (config_entry->mp_cache_entries_size - 1) *
+ sizeof(cache_entry));
+ }
+
+ old_mp_entries = config_entry->mp_cache_entries;
+ config_entry->mp_cache_entries = new_mp_entries;
+ free(old_mp_entries);
+
+ qsort(config_entry->mp_cache_entries,
+ config_entry->mp_cache_entries_size,
+ sizeof(cache_entry),
+ configuration_entry_cache_mp_sort_cmp);
+
+ TRACE_OUT(configuration_entry_add_mp_cache_entry);
+ return (0);
+}
+
+cache_entry
+configuration_entry_find_mp_cache_entry(
+ struct configuration_entry *config_entry, const char *mp_name)
+{
+ cache_entry *result;
+
+ TRACE_IN(configuration_entry_find_mp_cache_entry);
+ result = bsearch(mp_name, config_entry->mp_cache_entries,
+ config_entry->mp_cache_entries_size,
+ sizeof(cache_entry), configuration_entry_cache_mp_cmp);
+
+ if (result == NULL) {
+ TRACE_OUT(configuration_entry_find_mp_cache_entry);
+ return (NULL);
+ } else {
+ TRACE_OUT(configuration_entry_find_mp_cache_entry);
+ return (*result);
+ }
+}
+
+/*
+ * Searches for all multipart entries with names starting with mp_name.
+ * Needed for cache flushing.
+ */
+int
+configuration_entry_find_mp_cache_entries(
+ struct configuration_entry *config_entry, const char *mp_name,
+ cache_entry **start, cache_entry **finish)
+{
+ cache_entry *result;
+
+ TRACE_IN(configuration_entry_find_mp_cache_entries);
+ result = bsearch(mp_name, config_entry->mp_cache_entries,
+ config_entry->mp_cache_entries_size,
+ sizeof(cache_entry), configuration_entry_cache_mp_part_cmp);
+
+ if (result == NULL) {
+ TRACE_OUT(configuration_entry_find_mp_cache_entries);
+ return (-1);
+ }
+
+ *start = result;
+ *finish = result + 1;
+
+ while (*start != config_entry->mp_cache_entries) {
+ if (configuration_entry_cache_mp_part_cmp(mp_name, *start - 1) == 0)
+ *start = *start - 1;
+ else
+ break;
+ }
+
+ while (*finish != config_entry->mp_cache_entries +
+ config_entry->mp_cache_entries_size) {
+
+ if (configuration_entry_cache_mp_part_cmp(
+ mp_name, *finish) == 0)
+ *finish = *finish + 1;
+ else
+ break;
+ }
+
+ TRACE_OUT(configuration_entry_find_mp_cache_entries);
+ return (0);
+}
+
+/*
+ * Configuration entry uses rwlock to handle access to its fields.
+ */
+void
+configuration_lock_rdlock(struct configuration *config)
+{
+ TRACE_IN(configuration_lock_rdlock);
+ pthread_rwlock_rdlock(&config->rwlock);
+ TRACE_OUT(configuration_lock_rdlock);
+}
+
+void
+configuration_lock_wrlock(struct configuration *config)
+{
+ TRACE_IN(configuration_lock_wrlock);
+ pthread_rwlock_wrlock(&config->rwlock);
+ TRACE_OUT(configuration_lock_wrlock);
+}
+
+void
+configuration_unlock(struct configuration *config)
+{
+ TRACE_IN(configuration_unlock);
+ pthread_rwlock_unlock(&config->rwlock);
+ TRACE_OUT(configuration_unlock);
+}
+
+/*
+ * Configuration entry uses 3 mutexes to handle cache operations. They are
+ * acquired by configuration_lock_entry and configuration_unlock_entry
+ * functions.
+ */
+void
+configuration_lock_entry(struct configuration_entry *entry,
+ enum config_entry_lock_type lock_type)
+{
+ TRACE_IN(configuration_lock_entry);
+ assert(entry != NULL);
+
+ switch (lock_type) {
+ case CELT_POSITIVE:
+ pthread_mutex_lock(&entry->positive_cache_lock);
+ break;
+ case CELT_NEGATIVE:
+ pthread_mutex_lock(&entry->negative_cache_lock);
+ break;
+ case CELT_MULTIPART:
+ pthread_mutex_lock(&entry->mp_cache_lock);
+ break;
+ default:
+ /* should be unreachable */
+ break;
+ }
+ TRACE_OUT(configuration_lock_entry);
+}
+
+void
+configuration_unlock_entry(struct configuration_entry *entry,
+ enum config_entry_lock_type lock_type)
+{
+ TRACE_IN(configuration_unlock_entry);
+ assert(entry != NULL);
+
+ switch (lock_type) {
+ case CELT_POSITIVE:
+ pthread_mutex_unlock(&entry->positive_cache_lock);
+ break;
+ case CELT_NEGATIVE:
+ pthread_mutex_unlock(&entry->negative_cache_lock);
+ break;
+ case CELT_MULTIPART:
+ pthread_mutex_unlock(&entry->mp_cache_lock);
+ break;
+ default:
+ /* should be unreachable */
+ break;
+ }
+ TRACE_OUT(configuration_unlock_entry);
+}
+
+struct configuration *
+init_configuration(void)
+{
+ struct configuration *retval;
+
+ TRACE_IN(init_configuration);
+ retval = (struct configuration *)calloc(1, sizeof(struct configuration));
+ assert(retval != NULL);
+
+ retval->entries_capacity = INITIAL_ENTRIES_CAPACITY;
+ retval->entries = (struct configuration_entry **)calloc(1,
+ sizeof(struct configuration_entry *) *
+ retval->entries_capacity);
+ assert(retval->entries != NULL);
+
+ pthread_rwlock_init(&retval->rwlock, NULL);
+
+ TRACE_OUT(init_configuration);
+ return (retval);
+}
+
+void
+fill_configuration_defaults(struct configuration *config)
+{
+ size_t len, i;
+
+ TRACE_IN(fill_configuration_defaults);
+ assert(config != NULL);
+
+ if (config->socket_path != NULL)
+ free(config->socket_path);
+
+ len = strlen(DEFAULT_SOCKET_PATH);
+ config->socket_path = (char *)calloc(1, len + 1);
+ assert(config->socket_path != NULL);
+ memcpy(config->socket_path, DEFAULT_SOCKET_PATH, len);
+
+ len = strlen(DEFAULT_PIDFILE_PATH);
+ config->pidfile_path = (char *)calloc(1, len + 1);
+ assert(config->pidfile_path != NULL);
+ memcpy(config->pidfile_path, DEFAULT_PIDFILE_PATH, len);
+
+ config->socket_mode = S_IFSOCK | S_IRUSR | S_IWUSR |
+ S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+ config->force_unlink = 1;
+
+ config->query_timeout = DEFAULT_QUERY_TIMEOUT;
+ config->threads_num = DEFAULT_THREADS_NUM;
+
+ for (i = 0; i < config->entries_size; ++i)
+ destroy_configuration_entry(config->entries[i]);
+ config->entries_size = 0;
+
+ TRACE_OUT(fill_configuration_defaults);
+}
+
+void
+destroy_configuration(struct configuration *config)
+{
+ int i;
+ TRACE_IN(destroy_configuration);
+ assert(config != NULL);
+ free(config->pidfile_path);
+ free(config->socket_path);
+
+ for (i = 0; i < config->entries_size; ++i)
+ destroy_configuration_entry(config->entries[i]);
+ free(config->entries);
+
+ pthread_rwlock_destroy(&config->rwlock);
+ free(config);
+ TRACE_OUT(destroy_configuration);
+}
diff --git a/usr.sbin/nscd/config.h b/usr.sbin/nscd/config.h
new file mode 100644
index 0000000..a3205c9
--- /dev/null
+++ b/usr.sbin/nscd/config.h
@@ -0,0 +1,156 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 __NSCD_CONFIG_H__
+#define __NSCD_CONFIG_H__
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <pthread.h>
+#include <nsswitch.h>
+#include <unistd.h>
+#include "cachelib.h"
+
+#define DEFAULT_QUERY_TIMEOUT 8
+#define DEFAULT_THREADS_NUM 8
+
+#define DEFAULT_COMMON_ENTRY_TIMEOUT 10
+#define DEFAULT_MP_ENTRY_TIMEOUT 60
+#define DEFAULT_CACHE_HT_SIZE 257
+
+#define INITIAL_ENTRIES_CAPACITY 8
+#define DEFAULT_SOCKET_PATH "/var/run/nscd"
+#define DEFAULT_PIDFILE_PATH "/var/run/nscd.pid"
+
+#define DEFAULT_POSITIVE_ELEMENTS_SIZE (2048)
+#define DEFAULT_POSITIVE_LIFETIME (3600)
+
+#define DEFAULT_NEGATIVE_ELEMENTS_SIZE (2048)
+#define DEFAULT_NEGATIVE_LIFETIME (60)
+
+#define DEFAULT_MULTIPART_ELEMENTS_SIZE (1024 * 8)
+#define DEFAULT_MULITPART_SESSIONS_SIZE (1024)
+#define DEFAULT_MULITPART_LIFETIME (3600)
+
+extern const char *c_default_entries[6];
+
+/*
+ * Configuration entry represents the details of each cache entry in the
+ * config file (i.e. passwd or group). Its purpose also is to acquire locks
+ * of three different types (for usual read/write caching, for multipart
+ * caching and for caching of the negative results) for that cache entry.
+ */
+struct configuration_entry {
+ struct common_cache_entry_params positive_cache_params;
+ struct common_cache_entry_params negative_cache_params;
+ struct mp_cache_entry_params mp_cache_params;
+
+ /*
+ * configuration_entry holds pointers for all actual cache_entries,
+ * which are used for it. There is one for positive caching, one for
+ * for negative caching, and several (one per each euid/egid) for
+ * multipart caching.
+ */
+ cache_entry positive_cache_entry;
+ cache_entry negative_cache_entry;
+
+ cache_entry *mp_cache_entries;
+ size_t mp_cache_entries_size;
+
+ struct timeval common_query_timeout;
+ struct timeval mp_query_timeout;
+
+ char *name;
+ pthread_mutex_t positive_cache_lock;
+ pthread_mutex_t negative_cache_lock;
+ pthread_mutex_t mp_cache_lock;
+
+ int perform_actual_lookups;
+ int enabled;
+};
+
+/*
+ * Contains global configuration options and array of all configuration entries
+ */
+struct configuration {
+ char *pidfile_path;
+ char *socket_path;
+
+ struct configuration_entry **entries;
+ size_t entries_capacity;
+ size_t entries_size;
+
+ pthread_rwlock_t rwlock;
+
+ mode_t socket_mode;
+ int force_unlink;
+ int query_timeout;
+
+ int threads_num;
+};
+
+enum config_entry_lock_type {
+ CELT_POSITIVE,
+ CELT_NEGATIVE,
+ CELT_MULTIPART
+};
+
+extern struct configuration *init_configuration(void);
+extern void destroy_configuration(struct configuration *);
+extern void fill_configuration_defaults(struct configuration *);
+
+extern int add_configuration_entry(struct configuration *,
+ struct configuration_entry *);
+extern struct configuration_entry *create_def_configuration_entry(
+ const char *);
+extern void destroy_configuration_entry(struct configuration_entry *);
+extern size_t configuration_get_entries_size(struct configuration *);
+extern struct configuration_entry *configuration_get_entry(
+ struct configuration *, size_t);
+extern struct configuration_entry *configuration_find_entry(
+ struct configuration *, const char *);
+
+extern int configuration_entry_add_mp_cache_entry(struct configuration_entry *,
+ cache_entry);
+extern cache_entry configuration_entry_find_mp_cache_entry(
+ struct configuration_entry *,
+ const char *);
+extern int configuration_entry_find_mp_cache_entries(
+ struct configuration_entry *, const char *, cache_entry **,
+ cache_entry **);
+
+extern void configuration_lock_rdlock(struct configuration *config);
+extern void configuration_lock_wrlock(struct configuration *config);
+extern void configuration_unlock(struct configuration *config);
+
+extern void configuration_lock_entry(struct configuration_entry *,
+ enum config_entry_lock_type);
+extern void configuration_unlock_entry(struct configuration_entry *,
+ enum config_entry_lock_type);
+
+#endif
diff --git a/usr.sbin/nscd/debug.c b/usr.sbin/nscd/debug.c
new file mode 100644
index 0000000..420c517
--- /dev/null
+++ b/usr.sbin/nscd/debug.c
@@ -0,0 +1,149 @@
+/*-
+ * Copyright (c) 2004 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <stdio.h>
+#include "debug.h"
+
+static int trace_level = 0;
+static int trace_level_bk = 0;
+
+void
+__trace_in(const char *s, const char *f, int l)
+{
+ int i;
+ if (trace_level < TRACE_WANTED)
+ {
+ for (i = 0; i < trace_level; ++i)
+ printf("\t");
+
+ printf("=> %s\n", s);
+ }
+
+ ++trace_level;
+}
+
+void
+__trace_point(const char *f, int l)
+{
+ int i;
+
+ if (trace_level < TRACE_WANTED)
+ {
+ for (i = 0; i < trace_level - 1; ++i)
+ printf("\t");
+
+ printf("= %s: %d\n", f, l);
+ }
+}
+
+void
+__trace_msg(const char *msg, const char *f, int l)
+{
+ int i;
+
+ if (trace_level < TRACE_WANTED)
+ {
+ for (i = 0; i < trace_level - 1; ++i)
+ printf("\t");
+
+ printf("= MSG %s, %s: %d\n", msg, f, l);
+ }
+}
+
+void
+__trace_ptr(const char *desc, const void *p, const char *f, int l)
+{
+ int i;
+
+ if (trace_level < TRACE_WANTED)
+ {
+ for (i = 0; i < trace_level - 1; ++i)
+ printf("\t");
+
+ printf("= PTR %s: %p, %s: %d\n", desc, p, f, l);
+ }
+}
+
+void
+__trace_int(const char *desc, int i, const char *f, int l)
+{
+ int j;
+
+ if (trace_level < TRACE_WANTED)
+ {
+ for (j = 0; j < trace_level - 1; ++j)
+ printf("\t");
+
+ printf("= INT %s: %i, %s: %d\n",desc, i, f, l);
+ }
+}
+
+void
+__trace_str(const char *desc, const char *s, const char *f, int l)
+{
+ int i;
+
+ if (trace_level < TRACE_WANTED)
+ {
+ for (i = 0; i < trace_level - 1; ++i)
+ printf("\t");
+
+ printf("= STR %s: '%s', %s: %d\n", desc, s, f, l);
+ }
+}
+
+void
+__trace_out(const char *s, const char *f, int l)
+{
+ int i;
+
+ --trace_level;
+ if (trace_level < TRACE_WANTED)
+ {
+ for (i = 0; i < trace_level; ++i)
+ printf("\t");
+
+ printf("<= %s\n", s);
+ }
+}
+
+void
+__trace_on()
+{
+ trace_level = trace_level_bk;
+ trace_level_bk = 0;
+}
+
+void
+__trace_off()
+{
+ trace_level_bk = trace_level;
+ trace_level = 1024;
+}
diff --git a/usr.sbin/nscd/debug.h b/usr.sbin/nscd/debug.h
new file mode 100644
index 0000000..71f5300
--- /dev/null
+++ b/usr.sbin/nscd/debug.h
@@ -0,0 +1,67 @@
+/*-
+ * Copyright (c) 2004 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 __NSCD_DEBUG_H__
+#define __NSCD_DEBUG_H__
+
+#define TRACE_WANTED 32
+
+/* #ifndef NDEBUG */
+#if 0
+#define TRACE_IN(x) __trace_in(#x, __FILE__, __LINE__)
+#define TRACE_POINT() __trace_point(__FILE__, __LINE__)
+#define TRACE_MSG(x) __trace_msg(x, __FILE__, __LINE__)
+#define TRACE_PTR(p) __trace_ptr(#p, p, __FILE__, __LINE__)
+#define TRACE_INT(i) __trace_int(#i, i, __FILE__, __LINE__)
+#define TRACE_STR(s) __trace_str(#s, s, __FILE__, __LINE__)
+#define TRACE_OUT(x) __trace_out(#x, __FILE__, __LINE__)
+#define TRACE_ON() __trace_on()
+#define TRACE_OFF() __trace_off()
+#else
+#define TRACE_IN(x)
+#define TRACE_POINT()
+#define TRACE_MSG(x)
+#define TRACE_PTR(p)
+#define TRACE_INT(i)
+#define TRACE_STR(s)
+#define TRACE_OUT(x)
+#define TRACE_ON()
+#define TRACE_OFF()
+#endif
+
+extern void __trace_in(const char *, const char *, int);
+extern void __trace_point(const char *, int);
+extern void __trace_msg(const char *, const char *, int);
+extern void __trace_ptr(const char *, const void *, const char *, int);
+extern void __trace_int(const char *, int, const char *, int);
+extern void __trace_str(const char *, const char *, const char *, int);
+extern void __trace_out(const char *, const char *, int);
+extern void __trace_on();
+extern void __trace_off();
+
+#endif
diff --git a/usr.sbin/nscd/hashtable.h b/usr.sbin/nscd/hashtable.h
new file mode 100644
index 0000000..137820e
--- /dev/null
+++ b/usr.sbin/nscd/hashtable.h
@@ -0,0 +1,216 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 __CACHELIB_HASHTABLE_H__
+#define __CACHELIB_HASHTABLE_H__
+
+#include <search.h>
+#include <string.h>
+
+#define HASHTABLE_INITIAL_ENTRIES_CAPACITY 8
+typedef int hashtable_index_t;
+
+/*
+ * This file contains queue.h-like macro definitions for hash tables.
+ * Hash table is organized as an array of the specified size of the user
+ * defined (with HASTABLE_ENTRY_HEAD) structures. Each hash table
+ * entry (user defined structure) stores its elements in the sorted array.
+ * You can place elements into the hash table, retrieve elements with
+ * specified key, traverse through all elements, and delete them.
+ * New elements are placed into the hash table by using the compare and
+ * hashing functions, provided by the user.
+ */
+
+/*
+ * Defines the hash table entry structure, that uses specified type of
+ * elements.
+ */
+#define HASHTABLE_ENTRY_HEAD(name, type) struct name { \
+ type *values; \
+ size_t capacity; \
+ size_t size; \
+}
+
+/*
+ * Defines the hash table structure, which uses the specified type of entries.
+ * The only restriction for entries is that is that they should have the field,
+ * defined with HASHTABLE_ENTRY_HEAD macro.
+ */
+#define HASHTABLE_HEAD(name, entry) struct name { \
+ struct entry *entries; \
+ size_t entries_size; \
+}
+
+#define HASHTABLE_ENTRIES_COUNT(table) ((table)->entries_size)
+
+/*
+ * Unlike most of queue.h data types, hash tables can not be initialized
+ * statically - so there is no HASHTABLE_HEAD_INITIALIZED macro.
+ */
+#define HASHTABLE_INIT(table, type, field, _entries_size) \
+ do { \
+ hashtable_index_t var; \
+ (table)->entries = (void *)calloc(1, \
+ sizeof(*(table)->entries) * (_entries_size)); \
+ (table)->entries_size = (_entries_size); \
+ for (var = 0; var < HASHTABLE_ENTRIES_COUNT(table); ++var) {\
+ (table)->entries[var].field.capacity = \
+ HASHTABLE_INITIAL_ENTRIES_CAPACITY; \
+ (table)->entries[var].field.size = 0; \
+ (table)->entries[var].field.values = (type *)malloc(\
+ sizeof(type) * \
+ HASHTABLE_INITIAL_ENTRIES_CAPACITY); \
+ assert((table)->entries[var].field.values != NULL);\
+ } \
+ } while (0)
+
+/*
+ * All initialized hashtables should be destroyed with this macro.
+ */
+#define HASHTABLE_DESTROY(table, field) \
+ do { \
+ hashtable_index_t var; \
+ for (var = 0; var < HASHTABLE_ENTRIES_COUNT(table); ++var) {\
+ free((table)->entries[var].field.values); \
+ } \
+ } while (0)
+
+#define HASHTABLE_GET_ENTRY(table, hash) (&((table)->entries[hash]))
+
+/*
+ * Traverses through all hash table entries
+ */
+#define HASHTABLE_FOREACH(table, var) \
+ for ((var) = &((table)->entries[0]); \
+ (var) < &((table)->entries[HASHTABLE_ENTRIES_COUNT(table)]);\
+ ++(var))
+
+/*
+ * Traverses through all elements of the specified hash table entry
+ */
+#define HASHTABLE_ENTRY_FOREACH(entry, field, var) \
+ for ((var) = &((entry)->field.values[0]); \
+ (var) < &((entry)->field.values[(entry)->field.size]); \
+ ++(var))
+
+#define HASHTABLE_ENTRY_CLEAR(entry, field) \
+ ((entry)->field.size = 0)
+
+#define HASHTABLE_ENTRY_SIZE(entry, field) \
+ ((entry)->field.size)
+
+#define HASHTABLE_ENTRY_CAPACITY(entry, field) \
+ ((entry)->field.capacity)
+
+#define HASHTABLE_ENTRY_CAPACITY_INCREASE(entry, field, type) \
+ (entry)->field.capacity *= 2; \
+ (entry)->field.values = (type *)realloc((entry)->field.values, \
+ (entry)->field.capacity * sizeof(type));
+
+#define HASHTABLE_ENTRY_CAPACITY_DECREASE(entry, field, type) \
+ (entry)->field.capacity /= 2; \
+ (entry)->field.values = (type *)realloc((entry)->field.values, \
+ (entry)->field.capacity * sizeof(type));
+
+/*
+ * Generates prototypes for the hash table functions
+ */
+#define HASHTABLE_PROTOTYPE(name, entry_, type) \
+hashtable_index_t name##_CALCULATE_HASH(struct name *, type *); \
+void name##_ENTRY_STORE(struct entry_*, type *); \
+type *name##_ENTRY_FIND(struct entry_*, type *); \
+type *name##_ENTRY_FIND_SPECIAL(struct entry_ *, type *, \
+ int (*) (const void *, const void *)); \
+void name##_ENTRY_REMOVE(struct entry_*, type *);
+
+/*
+ * Generates implementations of the hash table functions
+ */
+#define HASHTABLE_GENERATE(name, entry_, type, field, HASH, CMP) \
+hashtable_index_t name##_CALCULATE_HASH(struct name *table, type *data) \
+{ \
+ \
+ return HASH(data, table->entries_size); \
+} \
+ \
+void name##_ENTRY_STORE(struct entry_ *the_entry, type *data) \
+{ \
+ \
+ if (the_entry->field.size == the_entry->field.capacity) \
+ HASHTABLE_ENTRY_CAPACITY_INCREASE(the_entry, field, type);\
+ \
+ memcpy(&(the_entry->field.values[the_entry->field.size++]), \
+ data, \
+ sizeof(type)); \
+ qsort(the_entry->field.values, the_entry->field.size, \
+ sizeof(type), CMP); \
+} \
+ \
+type *name##_ENTRY_FIND(struct entry_ *the_entry, type *key) \
+{ \
+ \
+ return ((type *)bsearch(key, the_entry->field.values, \
+ the_entry->field.size, sizeof(type), CMP)); \
+} \
+ \
+type *name##_ENTRY_FIND_SPECIAL(struct entry_ *the_entry, type *key, \
+ int (*compar) (const void *, const void *)) \
+{ \
+ return ((type *)bsearch(key, the_entry->field.values, \
+ the_entry->field.size, sizeof(type), compar)); \
+} \
+ \
+void name##_ENTRY_REMOVE(struct entry_ *the_entry, type *del_elm) \
+{ \
+ \
+ memmove(del_elm, del_elm + 1, \
+ (&the_entry->field.values[--the_entry->field.size] - del_elm) *\
+ sizeof(type)); \
+}
+
+/*
+ * Macro definitions below wrap the functions, generaed with
+ * HASHTABLE_GENERATE macro. You should use them and avoid using generated
+ * functions directly.
+ */
+#define HASHTABLE_CALCULATE_HASH(name, table, data) \
+ (name##_CALCULATE_HASH((table), data))
+
+#define HASHTABLE_ENTRY_STORE(name, entry, data) \
+ name##_ENTRY_STORE((entry), data)
+
+#define HASHTABLE_ENTRY_FIND(name, entry, key) \
+ (name##_ENTRY_FIND((entry), (key)))
+
+#define HASHTABLE_ENTRY_FIND_SPECIAL(name, entry, key, cmp) \
+ (name##_ENTRY_FIND_SPECIAL((entry), (key), (cmp)))
+
+#define HASHTABLE_ENTRY_REMOVE(name, entry, del_elm) \
+ name##_ENTRY_REMOVE((entry), (del_elm))
+
+#endif
diff --git a/usr.sbin/nscd/log.c b/usr.sbin/nscd/log.c
new file mode 100644
index 0000000..adfc5bf
--- /dev/null
+++ b/usr.sbin/nscd/log.c
@@ -0,0 +1,78 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <assert.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include "log.h"
+
+void
+__log_msg(int level, const char *sender, const char *message, ...)
+{
+ va_list ap;
+ char *fmessage;
+
+ fmessage = NULL;
+ va_start(ap, message);
+ vasprintf(&fmessage, message, ap);
+ va_end(ap);
+ assert(fmessage != NULL);
+
+ printf("M%d from %s: %s\n", level, sender, fmessage);
+#ifndef NO_SYSLOG
+ if (level == 0)
+ syslog(LOG_INFO, "nscd message (from %s): %s", sender,
+ fmessage);
+#endif
+ free(fmessage);
+}
+
+void
+__log_err(int level, const char *sender, const char *error, ...)
+{
+ va_list ap;
+ char *ferror;
+
+ ferror = NULL;
+ va_start(ap, error);
+ vasprintf(&ferror, error, ap);
+ va_end(ap);
+ assert(ferror != NULL);
+
+ printf("E%d from %s: %s\n", level, sender, ferror);
+
+#ifndef NO_SYSLOG
+ if (level == 0)
+ syslog(LOG_ERR, "nscd error (from %s): %s", sender, ferror);
+#endif
+ free(ferror);
+}
diff --git a/usr.sbin/nscd/log.h b/usr.sbin/nscd/log.h
new file mode 100644
index 0000000..e02efb5
--- /dev/null
+++ b/usr.sbin/nscd/log.h
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 __NSCD_LOG_H__
+#define __NSCD_LOG_H__
+
+#define LOG_MSG_1(sender, msg, ...) __log_msg(1, sender, msg, ##__VA_ARGS__)
+#define LOG_MSG_2(sender, msg, ...) __log_msg(2, sender, msg, ##__VA_ARGS__)
+#define LOG_MSG_3(sender, msg, ...) __log_msg(3, sedner, msg, ##__VA_ARGS__)
+
+#define LOG_ERR_1(sender, err, ...) __log_err(1, sender, err, ##__VA_ARGS__)
+#define LOG_ERR_2(sender, err, ...) __log_err(2, sender, err, ##__VA_ARGS__)
+#define LOG_ERR_3(sender, err, ...) __log_err(3, sender, err, ##__VA_ARGS__)
+
+extern void __log_msg(int, const char *, const char *, ...);
+extern void __log_err(int, const char *, const char *, ...);
+
+#endif
diff --git a/usr.sbin/nscd/mp_rs_query.c b/usr.sbin/nscd/mp_rs_query.c
new file mode 100644
index 0000000..c1569e9
--- /dev/null
+++ b/usr.sbin/nscd/mp_rs_query.c
@@ -0,0 +1,535 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/event.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "cachelib.h"
+#include "config.h"
+#include "debug.h"
+#include "log.h"
+#include "query.h"
+#include "mp_rs_query.h"
+#include "mp_ws_query.h"
+#include "singletons.h"
+
+static int on_mp_read_session_close_notification(struct query_state *);
+static void on_mp_read_session_destroy(struct query_state *);
+static int on_mp_read_session_mapper(struct query_state *);
+/* int on_mp_read_session_request_read1(struct query_state *); */
+static int on_mp_read_session_request_read2(struct query_state *);
+static int on_mp_read_session_request_process(struct query_state *);
+static int on_mp_read_session_response_write1(struct query_state *);
+static int on_mp_read_session_read_request_process(struct query_state *);
+static int on_mp_read_session_read_response_write1(struct query_state *);
+static int on_mp_read_session_read_response_write2(struct query_state *);
+
+/*
+ * This function is used as the query_state's destroy_func to make the
+ * proper cleanup in case of errors.
+ */
+static void
+on_mp_read_session_destroy(struct query_state *qstate)
+{
+ TRACE_IN(on_mp_read_session_destroy);
+ finalize_comm_element(&qstate->request);
+ finalize_comm_element(&qstate->response);
+
+ if (qstate->mdata != NULL) {
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ close_cache_mp_read_session(
+ (cache_mp_read_session)qstate->mdata);
+ configuration_unlock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+ }
+ TRACE_OUT(on_mp_read_session_destroy);
+}
+
+/*
+ * The functions below are used to process multipart read session initiation
+ * requests.
+ * - on_mp_read_session_request_read1 and on_mp_read_session_request_read2 read
+ * the request itself
+ * - on_mp_read_session_request_process processes it
+ * - on_mp_read_session_response_write1 sends the response
+ */
+int
+on_mp_read_session_request_read1(struct query_state *qstate)
+{
+ struct cache_mp_read_session_request *c_mp_rs_request;
+ ssize_t result;
+
+ TRACE_IN(on_mp_read_session_request_read1);
+ if (qstate->kevent_watermark == 0)
+ qstate->kevent_watermark = sizeof(size_t);
+ else {
+ init_comm_element(&qstate->request,
+ CET_MP_READ_SESSION_REQUEST);
+ c_mp_rs_request = get_cache_mp_read_session_request(
+ &qstate->request);
+
+ result = qstate->read_func(qstate,
+ &c_mp_rs_request->entry_length, sizeof(size_t));
+
+ if (result != sizeof(size_t)) {
+ TRACE_OUT(on_mp_read_session_request_read1);
+ return (-1);
+ }
+
+ if (BUFSIZE_INVALID(c_mp_rs_request->entry_length)) {
+ TRACE_OUT(on_mp_read_session_request_read1);
+ return (-1);
+ }
+
+ c_mp_rs_request->entry = (char *)calloc(1,
+ c_mp_rs_request->entry_length + 1);
+ assert(c_mp_rs_request->entry != NULL);
+
+ qstate->kevent_watermark = c_mp_rs_request->entry_length;
+ qstate->process_func = on_mp_read_session_request_read2;
+ }
+ TRACE_OUT(on_mp_read_session_request_read1);
+ return (0);
+}
+
+static int
+on_mp_read_session_request_read2(struct query_state *qstate)
+{
+ struct cache_mp_read_session_request *c_mp_rs_request;
+ ssize_t result;
+
+ TRACE_IN(on_mp_read_session_request_read2);
+ c_mp_rs_request = get_cache_mp_read_session_request(&qstate->request);
+
+ result = qstate->read_func(qstate, c_mp_rs_request->entry,
+ c_mp_rs_request->entry_length);
+
+ if (result != qstate->kevent_watermark) {
+ LOG_ERR_3("on_mp_read_session_request_read2",
+ "read failed");
+ TRACE_OUT(on_mp_read_session_request_read2);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = on_mp_read_session_request_process;
+ TRACE_OUT(on_mp_read_session_request_read2);
+ return (0);
+}
+
+static int
+on_mp_read_session_request_process(struct query_state *qstate)
+{
+ struct cache_mp_read_session_request *c_mp_rs_request;
+ struct cache_mp_read_session_response *c_mp_rs_response;
+ cache_mp_read_session rs;
+ cache_entry c_entry;
+ char *dec_cache_entry_name;
+
+ char *buffer;
+ size_t buffer_size;
+ cache_mp_write_session ws;
+ struct agent *lookup_agent;
+ struct multipart_agent *mp_agent;
+ void *mdata;
+ int res;
+
+ TRACE_IN(on_mp_read_session_request_process);
+ init_comm_element(&qstate->response, CET_MP_READ_SESSION_RESPONSE);
+ c_mp_rs_response = get_cache_mp_read_session_response(
+ &qstate->response);
+ c_mp_rs_request = get_cache_mp_read_session_request(&qstate->request);
+
+ qstate->config_entry = configuration_find_entry(
+ s_configuration, c_mp_rs_request->entry);
+ if (qstate->config_entry == NULL) {
+ c_mp_rs_response->error_code = ENOENT;
+
+ LOG_ERR_2("read_session_request",
+ "can't find configuration entry '%s'."
+ " aborting request", c_mp_rs_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->enabled == 0) {
+ c_mp_rs_response->error_code = EACCES;
+
+ LOG_ERR_2("read_session_request",
+ "configuration entry '%s' is disabled",
+ c_mp_rs_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->perform_actual_lookups != 0)
+ dec_cache_entry_name = strdup(
+ qstate->config_entry->mp_cache_params.entry_name);
+ else {
+#ifdef NS_NSCD_EID_CHECKING
+ if (check_query_eids(qstate) != 0) {
+ c_mp_rs_response->error_code = EPERM;
+ goto fin;
+ }
+#endif
+
+ asprintf(&dec_cache_entry_name, "%s%s", qstate->eid_str,
+ qstate->config_entry->mp_cache_params.entry_name);
+ }
+
+ assert(dec_cache_entry_name != NULL);
+
+ configuration_lock_rdlock(s_configuration);
+ c_entry = find_cache_entry(s_cache, dec_cache_entry_name);
+ configuration_unlock(s_configuration);
+
+ if ((c_entry == INVALID_CACHE) &&
+ (qstate->config_entry->perform_actual_lookups != 0))
+ c_entry = register_new_mp_cache_entry(qstate,
+ dec_cache_entry_name);
+
+ free(dec_cache_entry_name);
+
+ if (c_entry != INVALID_CACHE_ENTRY) {
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ rs = open_cache_mp_read_session(c_entry);
+ configuration_unlock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+
+ if ((rs == INVALID_CACHE_MP_READ_SESSION) &&
+ (qstate->config_entry->perform_actual_lookups != 0)) {
+ lookup_agent = find_agent(s_agent_table,
+ c_mp_rs_request->entry, MULTIPART_AGENT);
+
+ if ((lookup_agent != NULL) &&
+ (lookup_agent->type == MULTIPART_AGENT)) {
+ mp_agent = (struct multipart_agent *)
+ lookup_agent;
+ mdata = mp_agent->mp_init_func();
+
+ /*
+ * Multipart agents read the whole snapshot
+ * of the data at one time.
+ */
+ configuration_lock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+ ws = open_cache_mp_write_session(c_entry);
+ configuration_unlock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+ if (ws != NULL) {
+ do {
+ buffer = NULL;
+ res = mp_agent->mp_lookup_func(&buffer,
+ &buffer_size,
+ mdata);
+
+ if ((res & NS_TERMINATE) &&
+ (buffer != NULL)) {
+ configuration_lock_entry(
+ qstate->config_entry,
+ CELT_MULTIPART);
+ if (cache_mp_write(ws, buffer,
+ buffer_size) != 0) {
+ abandon_cache_mp_write_session(ws);
+ ws = NULL;
+ }
+ configuration_unlock_entry(
+ qstate->config_entry,
+ CELT_MULTIPART);
+
+ free(buffer);
+ buffer = NULL;
+ } else {
+ configuration_lock_entry(
+ qstate->config_entry,
+ CELT_MULTIPART);
+ close_cache_mp_write_session(ws);
+ configuration_unlock_entry(
+ qstate->config_entry,
+ CELT_MULTIPART);
+
+ free(buffer);
+ buffer = NULL;
+ }
+ } while ((res & NS_TERMINATE) &&
+ (ws != NULL));
+ }
+
+ configuration_lock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+ rs = open_cache_mp_read_session(c_entry);
+ configuration_unlock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+ }
+ }
+
+ if (rs == INVALID_CACHE_MP_READ_SESSION)
+ c_mp_rs_response->error_code = -1;
+ else {
+ qstate->mdata = rs;
+ qstate->destroy_func = on_mp_read_session_destroy;
+
+ configuration_lock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+ if ((qstate->config_entry->mp_query_timeout.tv_sec != 0) ||
+ (qstate->config_entry->mp_query_timeout.tv_usec != 0))
+ memcpy(&qstate->timeout,
+ &qstate->config_entry->mp_query_timeout,
+ sizeof(struct timeval));
+ configuration_unlock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+ }
+ } else
+ c_mp_rs_response->error_code = -1;
+
+fin:
+ qstate->process_func = on_mp_read_session_response_write1;
+ qstate->kevent_watermark = sizeof(int);
+ qstate->kevent_filter = EVFILT_WRITE;
+
+ TRACE_OUT(on_mp_read_session_request_process);
+ return (0);
+}
+
+static int
+on_mp_read_session_response_write1(struct query_state *qstate)
+{
+ struct cache_mp_read_session_response *c_mp_rs_response;
+ ssize_t result;
+
+ TRACE_IN(on_mp_read_session_response_write1);
+ c_mp_rs_response = get_cache_mp_read_session_response(
+ &qstate->response);
+ result = qstate->write_func(qstate, &c_mp_rs_response->error_code,
+ sizeof(int));
+
+ if (result != sizeof(int)) {
+ LOG_ERR_3("on_mp_read_session_response_write1",
+ "write failed");
+ TRACE_OUT(on_mp_read_session_response_write1);
+ return (-1);
+ }
+
+ if (c_mp_rs_response->error_code == 0) {
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_mp_read_session_mapper;
+ qstate->kevent_filter = EVFILT_READ;
+ } else {
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ }
+ TRACE_OUT(on_mp_read_session_response_write1);
+ return (0);
+}
+
+/*
+ * Mapper function is used to avoid multiple connections for each session
+ * write or read requests. After processing the request, it does not close
+ * the connection, but waits for the next request.
+ */
+static int
+on_mp_read_session_mapper(struct query_state *qstate)
+{
+ ssize_t result;
+ int elem_type;
+
+ TRACE_IN(on_mp_read_session_mapper);
+ if (qstate->kevent_watermark == 0) {
+ qstate->kevent_watermark = sizeof(int);
+ } else {
+ result = qstate->read_func(qstate, &elem_type, sizeof(int));
+ if (result != sizeof(int)) {
+ LOG_ERR_3("on_mp_read_session_mapper",
+ "read failed");
+ TRACE_OUT(on_mp_read_session_mapper);
+ return (-1);
+ }
+
+ switch (elem_type) {
+ case CET_MP_READ_SESSION_READ_REQUEST:
+ qstate->kevent_watermark = 0;
+ qstate->process_func =
+ on_mp_read_session_read_request_process;
+ break;
+ case CET_MP_READ_SESSION_CLOSE_NOTIFICATION:
+ qstate->kevent_watermark = 0;
+ qstate->process_func =
+ on_mp_read_session_close_notification;
+ break;
+ default:
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ LOG_ERR_3("on_mp_read_session_mapper",
+ "unknown element type");
+ TRACE_OUT(on_mp_read_session_mapper);
+ return (-1);
+ }
+ }
+ TRACE_OUT(on_mp_read_session_mapper);
+ return (0);
+}
+
+/*
+ * The functions below are used to process multipart read sessions read
+ * requests. User doesn't have to pass any kind of data, besides the
+ * request identificator itself. So we don't need any XXX_read functions and
+ * start with the XXX_process function.
+ * - on_mp_read_session_read_request_process processes it
+ * - on_mp_read_session_read_response_write1 and
+ * on_mp_read_session_read_response_write2 sends the response
+ */
+static int
+on_mp_read_session_read_request_process(struct query_state *qstate)
+{
+ struct cache_mp_read_session_read_response *read_response;
+
+ TRACE_IN(on_mp_read_session_response_process);
+ init_comm_element(&qstate->response, CET_MP_READ_SESSION_READ_RESPONSE);
+ read_response = get_cache_mp_read_session_read_response(
+ &qstate->response);
+
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ read_response->error_code = cache_mp_read(
+ (cache_mp_read_session)qstate->mdata, NULL,
+ &read_response->data_size);
+
+ if (read_response->error_code == 0) {
+ read_response->data = (char *)malloc(read_response->data_size);
+ assert(read_response != NULL);
+ read_response->error_code = cache_mp_read(
+ (cache_mp_read_session)qstate->mdata,
+ read_response->data,
+ &read_response->data_size);
+ }
+ configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART);
+
+ if (read_response->error_code == 0)
+ qstate->kevent_watermark = sizeof(size_t) + sizeof(int);
+ else
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_mp_read_session_read_response_write1;
+ qstate->kevent_filter = EVFILT_WRITE;
+
+ TRACE_OUT(on_mp_read_session_response_process);
+ return (0);
+}
+
+static int
+on_mp_read_session_read_response_write1(struct query_state *qstate)
+{
+ struct cache_mp_read_session_read_response *read_response;
+ ssize_t result;
+
+ TRACE_IN(on_mp_read_session_read_response_write1);
+ read_response = get_cache_mp_read_session_read_response(
+ &qstate->response);
+
+ result = qstate->write_func(qstate, &read_response->error_code,
+ sizeof(int));
+ if (read_response->error_code == 0) {
+ result += qstate->write_func(qstate, &read_response->data_size,
+ sizeof(size_t));
+ if (result != qstate->kevent_watermark) {
+ TRACE_OUT(on_mp_read_session_read_response_write1);
+ LOG_ERR_3("on_mp_read_session_read_response_write1",
+ "write failed");
+ return (-1);
+ }
+
+ qstate->kevent_watermark = read_response->data_size;
+ qstate->process_func = on_mp_read_session_read_response_write2;
+ } else {
+ if (result != qstate->kevent_watermark) {
+ LOG_ERR_3("on_mp_read_session_read_response_write1",
+ "write failed");
+ TRACE_OUT(on_mp_read_session_read_response_write1);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ }
+
+ TRACE_OUT(on_mp_read_session_read_response_write1);
+ return (0);
+}
+
+static int
+on_mp_read_session_read_response_write2(struct query_state *qstate)
+{
+ struct cache_mp_read_session_read_response *read_response;
+ ssize_t result;
+
+ TRACE_IN(on_mp_read_session_read_response_write2);
+ read_response = get_cache_mp_read_session_read_response(
+ &qstate->response);
+ result = qstate->write_func(qstate, read_response->data,
+ read_response->data_size);
+ if (result != qstate->kevent_watermark) {
+ LOG_ERR_3("on_mp_read_session_read_response_write2",
+ "write failed");
+ TRACE_OUT(on_mp_read_session_read_response_write2);
+ return (-1);
+ }
+
+ finalize_comm_element(&qstate->request);
+ finalize_comm_element(&qstate->response);
+
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_mp_read_session_mapper;
+ qstate->kevent_filter = EVFILT_READ;
+
+ TRACE_OUT(on_mp_read_session_read_response_write2);
+ return (0);
+}
+
+/*
+ * Handles session close notification by calling close_cache_mp_read_session
+ * function.
+ */
+static int
+on_mp_read_session_close_notification(struct query_state *qstate)
+{
+
+ TRACE_IN(on_mp_read_session_close_notification);
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ close_cache_mp_read_session((cache_mp_read_session)qstate->mdata);
+ configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART);
+ qstate->mdata = NULL;
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ TRACE_OUT(on_mp_read_session_close_notification);
+ return (0);
+}
diff --git a/usr.sbin/nscd/mp_rs_query.h b/usr.sbin/nscd/mp_rs_query.h
new file mode 100644
index 0000000..91f13cc
--- /dev/null
+++ b/usr.sbin/nscd/mp_rs_query.h
@@ -0,0 +1,34 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 __NSCD_MP_RS_QUERY_H__
+#define __NSCD_MP_RS_QUERY_H__
+
+extern int on_mp_read_session_request_read1(struct query_state *);
+
+#endif
diff --git a/usr.sbin/nscd/mp_ws_query.c b/usr.sbin/nscd/mp_ws_query.c
new file mode 100644
index 0000000..f5c889b
--- /dev/null
+++ b/usr.sbin/nscd/mp_ws_query.c
@@ -0,0 +1,545 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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/socket.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/event.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "cachelib.h"
+#include "config.h"
+#include "debug.h"
+#include "log.h"
+#include "query.h"
+#include "mp_ws_query.h"
+#include "singletons.h"
+
+static int on_mp_write_session_abandon_notification(struct query_state *);
+static int on_mp_write_session_close_notification(struct query_state *);
+static void on_mp_write_session_destroy(struct query_state *);
+static int on_mp_write_session_mapper(struct query_state *);
+/* int on_mp_write_session_request_read1(struct query_state *); */
+static int on_mp_write_session_request_read2(struct query_state *);
+static int on_mp_write_session_request_process(struct query_state *);
+static int on_mp_write_session_response_write1(struct query_state *);
+static int on_mp_write_session_write_request_read1(struct query_state *);
+static int on_mp_write_session_write_request_read2(struct query_state *);
+static int on_mp_write_session_write_request_process(struct query_state *);
+static int on_mp_write_session_write_response_write1(struct query_state *);
+
+/*
+ * This function is used as the query_state's destroy_func to make the
+ * proper cleanup in case of errors.
+ */
+static void
+on_mp_write_session_destroy(struct query_state *qstate)
+{
+
+ TRACE_IN(on_mp_write_session_destroy);
+ finalize_comm_element(&qstate->request);
+ finalize_comm_element(&qstate->response);
+
+ if (qstate->mdata != NULL) {
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ abandon_cache_mp_write_session(
+ (cache_mp_write_session)qstate->mdata);
+ configuration_unlock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+ }
+ TRACE_OUT(on_mp_write_session_destroy);
+}
+
+/*
+ * The functions below are used to process multipart write session initiation
+ * requests.
+ * - on_mp_write_session_request_read1 and on_mp_write_session_request_read2
+ * read the request itself
+ * - on_mp_write_session_request_process processes it
+ * - on_mp_write_session_response_write1 sends the response
+ */
+int
+on_mp_write_session_request_read1(struct query_state *qstate)
+{
+ struct cache_mp_write_session_request *c_mp_ws_request;
+ ssize_t result;
+
+ TRACE_IN(on_mp_write_session_request_read1);
+ if (qstate->kevent_watermark == 0)
+ qstate->kevent_watermark = sizeof(size_t);
+ else {
+ init_comm_element(&qstate->request,
+ CET_MP_WRITE_SESSION_REQUEST);
+ c_mp_ws_request = get_cache_mp_write_session_request(
+ &qstate->request);
+
+ result = qstate->read_func(qstate,
+ &c_mp_ws_request->entry_length, sizeof(size_t));
+
+ if (result != sizeof(size_t)) {
+ LOG_ERR_3("on_mp_write_session_request_read1",
+ "read failed");
+ TRACE_OUT(on_mp_write_session_request_read1);
+ return (-1);
+ }
+
+ if (BUFSIZE_INVALID(c_mp_ws_request->entry_length)) {
+ LOG_ERR_3("on_mp_write_session_request_read1",
+ "invalid entry_length value");
+ TRACE_OUT(on_mp_write_session_request_read1);
+ return (-1);
+ }
+
+ c_mp_ws_request->entry = (char *)calloc(1,
+ c_mp_ws_request->entry_length + 1);
+ assert(c_mp_ws_request->entry != NULL);
+
+ qstate->kevent_watermark = c_mp_ws_request->entry_length;
+ qstate->process_func = on_mp_write_session_request_read2;
+ }
+ TRACE_OUT(on_mp_write_session_request_read1);
+ return (0);
+}
+
+static int
+on_mp_write_session_request_read2(struct query_state *qstate)
+{
+ struct cache_mp_write_session_request *c_mp_ws_request;
+ ssize_t result;
+
+ TRACE_IN(on_mp_write_session_request_read2);
+ c_mp_ws_request = get_cache_mp_write_session_request(&qstate->request);
+
+ result = qstate->read_func(qstate, c_mp_ws_request->entry,
+ c_mp_ws_request->entry_length);
+
+ if (result != qstate->kevent_watermark) {
+ LOG_ERR_3("on_mp_write_session_request_read2",
+ "read failed");
+ TRACE_OUT(on_mp_write_session_request_read2);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = on_mp_write_session_request_process;
+
+ TRACE_OUT(on_mp_write_session_request_read2);
+ return (0);
+}
+
+static int
+on_mp_write_session_request_process(struct query_state *qstate)
+{
+ struct cache_mp_write_session_request *c_mp_ws_request;
+ struct cache_mp_write_session_response *c_mp_ws_response;
+ cache_mp_write_session ws;
+ cache_entry c_entry;
+ char *dec_cache_entry_name;
+
+ TRACE_IN(on_mp_write_session_request_process);
+ init_comm_element(&qstate->response, CET_MP_WRITE_SESSION_RESPONSE);
+ c_mp_ws_response = get_cache_mp_write_session_response(
+ &qstate->response);
+ c_mp_ws_request = get_cache_mp_write_session_request(&qstate->request);
+
+ qstate->config_entry = configuration_find_entry(
+ s_configuration, c_mp_ws_request->entry);
+ if (qstate->config_entry == NULL) {
+ c_mp_ws_response->error_code = ENOENT;
+
+ LOG_ERR_2("write_session_request",
+ "can't find configuration entry '%s'. "
+ "aborting request", c_mp_ws_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->enabled == 0) {
+ c_mp_ws_response->error_code = EACCES;
+
+ LOG_ERR_2("write_session_request",
+ "configuration entry '%s' is disabled",
+ c_mp_ws_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->perform_actual_lookups != 0) {
+ c_mp_ws_response->error_code = EOPNOTSUPP;
+
+ LOG_ERR_2("write_session_request",
+ "entry '%s' performs lookups by itself: "
+ "can't write to it", c_mp_ws_request->entry);
+ goto fin;
+ } else {
+#ifdef NS_NSCD_EID_CHECKING
+ if (check_query_eids(qstate) != 0) {
+ c_mp_ws_response->error_code = EPERM;
+ goto fin;
+ }
+#endif
+ }
+
+ /*
+ * All multipart entries are separated by their name decorations.
+ * For one configuration entry there will be a lot of multipart
+ * cache entries - each with its own decorated name.
+ */
+ asprintf(&dec_cache_entry_name, "%s%s", qstate->eid_str,
+ qstate->config_entry->mp_cache_params.entry_name);
+ assert(dec_cache_entry_name != NULL);
+
+ configuration_lock_rdlock(s_configuration);
+ c_entry = find_cache_entry(s_cache,
+ dec_cache_entry_name);
+ configuration_unlock(s_configuration);
+
+ if (c_entry == INVALID_CACHE_ENTRY)
+ c_entry = register_new_mp_cache_entry(qstate,
+ dec_cache_entry_name);
+
+ free(dec_cache_entry_name);
+
+ assert(c_entry != NULL);
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ ws = open_cache_mp_write_session(c_entry);
+ if (ws == INVALID_CACHE_MP_WRITE_SESSION)
+ c_mp_ws_response->error_code = -1;
+ else {
+ qstate->mdata = ws;
+ qstate->destroy_func = on_mp_write_session_destroy;
+
+ if ((qstate->config_entry->mp_query_timeout.tv_sec != 0) ||
+ (qstate->config_entry->mp_query_timeout.tv_usec != 0))
+ memcpy(&qstate->timeout,
+ &qstate->config_entry->mp_query_timeout,
+ sizeof(struct timeval));
+ }
+ configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART);
+
+fin:
+ qstate->process_func = on_mp_write_session_response_write1;
+ qstate->kevent_watermark = sizeof(int);
+ qstate->kevent_filter = EVFILT_WRITE;
+
+ TRACE_OUT(on_mp_write_session_request_process);
+ return (0);
+}
+
+static int
+on_mp_write_session_response_write1(struct query_state *qstate)
+{
+ struct cache_mp_write_session_response *c_mp_ws_response;
+ ssize_t result;
+
+ TRACE_IN(on_mp_write_session_response_write1);
+ c_mp_ws_response = get_cache_mp_write_session_response(
+ &qstate->response);
+ result = qstate->write_func(qstate, &c_mp_ws_response->error_code,
+ sizeof(int));
+ if (result != sizeof(int)) {
+ LOG_ERR_3("on_mp_write_session_response_write1",
+ "write failed");
+ TRACE_OUT(on_mp_write_session_response_write1);
+ return (-1);
+ }
+
+ if (c_mp_ws_response->error_code == 0) {
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_mp_write_session_mapper;
+ qstate->kevent_filter = EVFILT_READ;
+ } else {
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ }
+ TRACE_OUT(on_mp_write_session_response_write1);
+ return (0);
+}
+
+/*
+ * Mapper function is used to avoid multiple connections for each session
+ * write or read requests. After processing the request, it does not close
+ * the connection, but waits for the next request.
+ */
+static int
+on_mp_write_session_mapper(struct query_state *qstate)
+{
+ ssize_t result;
+ int elem_type;
+
+ TRACE_IN(on_mp_write_session_mapper);
+ if (qstate->kevent_watermark == 0) {
+ qstate->kevent_watermark = sizeof(int);
+ } else {
+ result = qstate->read_func(qstate, &elem_type, sizeof(int));
+ if (result != sizeof(int)) {
+ LOG_ERR_3("on_mp_write_session_mapper",
+ "read failed");
+ TRACE_OUT(on_mp_write_session_mapper);
+ return (-1);
+ }
+
+ switch (elem_type) {
+ case CET_MP_WRITE_SESSION_WRITE_REQUEST:
+ qstate->kevent_watermark = sizeof(size_t);
+ qstate->process_func =
+ on_mp_write_session_write_request_read1;
+ break;
+ case CET_MP_WRITE_SESSION_ABANDON_NOTIFICATION:
+ qstate->kevent_watermark = 0;
+ qstate->process_func =
+ on_mp_write_session_abandon_notification;
+ break;
+ case CET_MP_WRITE_SESSION_CLOSE_NOTIFICATION:
+ qstate->kevent_watermark = 0;
+ qstate->process_func =
+ on_mp_write_session_close_notification;
+ break;
+ default:
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ LOG_ERR_2("on_mp_write_session_mapper",
+ "unknown element type");
+ TRACE_OUT(on_mp_write_session_mapper);
+ return (-1);
+ }
+ }
+ TRACE_OUT(on_mp_write_session_mapper);
+ return (0);
+}
+
+/*
+ * The functions below are used to process multipart write sessions write
+ * requests.
+ * - on_mp_write_session_write_request_read1 and
+ * on_mp_write_session_write_request_read2 read the request itself
+ * - on_mp_write_session_write_request_process processes it
+ * - on_mp_write_session_write_response_write1 sends the response
+ */
+static int
+on_mp_write_session_write_request_read1(struct query_state *qstate)
+{
+ struct cache_mp_write_session_write_request *write_request;
+ ssize_t result;
+
+ TRACE_IN(on_mp_write_session_write_request_read1);
+ init_comm_element(&qstate->request,
+ CET_MP_WRITE_SESSION_WRITE_REQUEST);
+ write_request = get_cache_mp_write_session_write_request(
+ &qstate->request);
+
+ result = qstate->read_func(qstate, &write_request->data_size,
+ sizeof(size_t));
+
+ if (result != sizeof(size_t)) {
+ LOG_ERR_3("on_mp_write_session_write_request_read1",
+ "read failed");
+ TRACE_OUT(on_mp_write_session_write_request_read1);
+ return (-1);
+ }
+
+ if (BUFSIZE_INVALID(write_request->data_size)) {
+ LOG_ERR_3("on_mp_write_session_write_request_read1",
+ "invalid data_size value");
+ TRACE_OUT(on_mp_write_session_write_request_read1);
+ return (-1);
+ }
+
+ write_request->data = (char *)calloc(1, write_request->data_size);
+ assert(write_request->data != NULL);
+
+ qstate->kevent_watermark = write_request->data_size;
+ qstate->process_func = on_mp_write_session_write_request_read2;
+ TRACE_OUT(on_mp_write_session_write_request_read1);
+ return (0);
+}
+
+static int
+on_mp_write_session_write_request_read2(struct query_state *qstate)
+{
+ struct cache_mp_write_session_write_request *write_request;
+ ssize_t result;
+
+ TRACE_IN(on_mp_write_session_write_request_read2);
+ write_request = get_cache_mp_write_session_write_request(
+ &qstate->request);
+
+ result = qstate->read_func(qstate, write_request->data,
+ write_request->data_size);
+
+ if (result != qstate->kevent_watermark) {
+ LOG_ERR_3("on_mp_write_session_write_request_read2",
+ "read failed");
+ TRACE_OUT(on_mp_write_session_write_request_read2);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = on_mp_write_session_write_request_process;
+ TRACE_OUT(on_mp_write_session_write_request_read2);
+ return (0);
+}
+
+static int
+on_mp_write_session_write_request_process(struct query_state *qstate)
+{
+ struct cache_mp_write_session_write_request *write_request;
+ struct cache_mp_write_session_write_response *write_response;
+
+ TRACE_IN(on_mp_write_session_write_request_process);
+ init_comm_element(&qstate->response,
+ CET_MP_WRITE_SESSION_WRITE_RESPONSE);
+ write_response = get_cache_mp_write_session_write_response(
+ &qstate->response);
+ write_request = get_cache_mp_write_session_write_request(
+ &qstate->request);
+
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ write_response->error_code = cache_mp_write(
+ (cache_mp_write_session)qstate->mdata,
+ write_request->data,
+ write_request->data_size);
+ configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART);
+
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_mp_write_session_write_response_write1;
+ qstate->kevent_filter = EVFILT_WRITE;
+
+ TRACE_OUT(on_mp_write_session_write_request_process);
+ return (0);
+}
+
+static int
+on_mp_write_session_write_response_write1(struct query_state *qstate)
+{
+ struct cache_mp_write_session_write_response *write_response;
+ ssize_t result;
+
+ TRACE_IN(on_mp_write_session_write_response_write1);
+ write_response = get_cache_mp_write_session_write_response(
+ &qstate->response);
+ result = qstate->write_func(qstate, &write_response->error_code,
+ sizeof(int));
+ if (result != sizeof(int)) {
+ LOG_ERR_3("on_mp_write_session_write_response_write1",
+ "write failed");
+ TRACE_OUT(on_mp_write_session_write_response_write1);
+ return (-1);
+ }
+
+ if (write_response->error_code == 0) {
+ finalize_comm_element(&qstate->request);
+ finalize_comm_element(&qstate->response);
+
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_mp_write_session_mapper;
+ qstate->kevent_filter = EVFILT_READ;
+ } else {
+ qstate->kevent_watermark = 0;
+ qstate->process_func = 0;
+ }
+
+ TRACE_OUT(on_mp_write_session_write_response_write1);
+ return (0);
+}
+
+/*
+ * Handles abandon notifications. Destroys the session by calling the
+ * abandon_cache_mp_write_session.
+ */
+static int
+on_mp_write_session_abandon_notification(struct query_state *qstate)
+{
+ TRACE_IN(on_mp_write_session_abandon_notification);
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ abandon_cache_mp_write_session((cache_mp_write_session)qstate->mdata);
+ configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART);
+ qstate->mdata = INVALID_CACHE_MP_WRITE_SESSION;
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ TRACE_OUT(on_mp_write_session_abandon_notification);
+ return (0);
+}
+
+/*
+ * Handles close notifications. Commits the session by calling
+ * the close_cache_mp_write_session.
+ */
+static int
+on_mp_write_session_close_notification(struct query_state *qstate)
+{
+ TRACE_IN(on_mp_write_session_close_notification);
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+ close_cache_mp_write_session((cache_mp_write_session)qstate->mdata);
+ configuration_unlock_entry(qstate->config_entry, CELT_MULTIPART);
+ qstate->mdata = INVALID_CACHE_MP_WRITE_SESSION;
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ TRACE_OUT(on_mp_write_session_close_notification);
+ return (0);
+}
+
+cache_entry register_new_mp_cache_entry(struct query_state *qstate,
+ const char *dec_cache_entry_name)
+{
+ cache_entry c_entry;
+ char *en_bkp;
+
+ TRACE_IN(register_new_mp_cache_entry);
+ c_entry = INVALID_CACHE_ENTRY;
+ configuration_lock_entry(qstate->config_entry, CELT_MULTIPART);
+
+ configuration_lock_wrlock(s_configuration);
+ en_bkp = qstate->config_entry->mp_cache_params.entry_name;
+ qstate->config_entry->mp_cache_params.entry_name =
+ (char *)dec_cache_entry_name;
+ register_cache_entry(s_cache, (struct cache_entry_params *)
+ &qstate->config_entry->mp_cache_params);
+ qstate->config_entry->mp_cache_params.entry_name = en_bkp;
+ configuration_unlock(s_configuration);
+
+ configuration_lock_rdlock(s_configuration);
+ c_entry = find_cache_entry(s_cache,
+ dec_cache_entry_name);
+ configuration_unlock(s_configuration);
+
+ configuration_entry_add_mp_cache_entry(qstate->config_entry,
+ c_entry);
+
+ configuration_unlock_entry(qstate->config_entry,
+ CELT_MULTIPART);
+
+ TRACE_OUT(register_new_mp_cache_entry);
+ return (c_entry);
+}
diff --git a/usr.sbin/nscd/mp_ws_query.h b/usr.sbin/nscd/mp_ws_query.h
new file mode 100644
index 0000000..ead1d35
--- /dev/null
+++ b/usr.sbin/nscd/mp_ws_query.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 __NSCD_MP_WS_QUERY_H__
+#define __NSCD_MP_WS_QUERY_H__
+
+extern int on_mp_write_session_request_read1(struct query_state *);
+extern cache_entry register_new_mp_cache_entry(struct query_state *,
+ const char *);
+
+#endif
diff --git a/usr.sbin/nscd/nscd.8 b/usr.sbin/nscd/nscd.8
new file mode 100644
index 0000000..1def32c
--- /dev/null
+++ b/usr.sbin/nscd/nscd.8
@@ -0,0 +1,165 @@
+.\" Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 20, 2005
+.Dt NSCD 8
+.Os
+.Sh NAME
+.Nm nscd
+.Nd "name service caching daemon"
+.Sh SYNOPSIS
+.Nm
+.Op Fl dnst
+.Op Fl i Ar cachename
+.Op Fl I Ar cachename
+.Sh DESCRIPTION
+The
+.Nm
+utility
+is the system caching daemon.
+It can cache almost all types of data and is basically intended to be used
+with the
+.Nm nsswitch
+subsystem.
+The cache is actually per-user.
+This means that each user can work only with the
+cached data that were cached by themselves, and cannot poison the
+cache of other users.
+The
+.Nm
+utility supports two types of caching:
+.Bl -tag -width ".Sy Type"
+.It Sy Type
+.Sy Description
+.It Common caching
+Each cached element is the key+value pair.
+This type of caching supports policies which are applied when maximum
+number of cached elements is exceeded.
+Three policies are available:
+.Cm FIFO
+(first in - first out),
+.Cm LRU
+(least recently used) and
+.Cm LFU
+(least frequently used).
+This type of caching is used with the
+.Fn getXXXbyname
+family of functions.
+.It Multipart caching
+Each cached element is the part of the elements sequence.
+This type of caching is intended to be used with the
+.Fn getXXXent
+family of functions.
+.El
+.Pp
+The
+.Nm
+utility is able not only to cache elements, but to perform the actual nsswitch
+lookups by itself.
+To enable this feature, use the
+.Va perform-actual-lookups
+parameter in
+.Xr nscd.conf 5 .
+.Pp
+The
+.Nm
+utility recognizes the following runtime options:
+.Bl -tag -width indent
+.\" .It Fl d
+.\" XXX Document me!
+.It Fl n
+Do not daemonize;
+.Nm
+will not fork or disconnect itself from the terminal.
+.It Fl s
+Single-threaded mode.
+Forces using only one thread for all processing purposes (it overrides
+the
+.Va threads
+parameter in the
+.Xr nscd.conf 5
+file).
+.It Fl t
+Trace mode.
+All trace messages will be written to stdout.
+This mode is usually used with
+.Fl n
+and
+.Fl s
+flags are used for debugging purposes.
+.It Fl i Ar cachename
+Invalidates personal cache.
+When specified,
+.Nm
+acts as the administration tool.
+It asks the already running
+.Nm
+to invalidate the specified part of the cache of the
+calling user.
+For example, sometimes you may want to invalidate your
+.Dq Li hosts
+cache.
+You can specify
+.Dq Li all
+as the
+.Ar cachename
+to invalidate your personal cache as a whole.
+You cannot use this option for the
+.Ar cachename
+for which the
+.Va perform-actual-lookups
+option is enabled.
+.It Fl I Ar cachename
+Invalidates the cache for every user.
+When specified,
+.Nm
+acts as the administration tool.
+It asks the already
+running
+.Nm
+to invalidate the specified part of the cache for
+every user.
+You can specify
+.Dq Li all
+as the
+.Ar cachename
+to invalidate the whole cache.
+Only the root can use this option.
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /etc/nscd.conf" -compact
+.It Pa /etc/nscd.conf
+The default configuration file.
+.El
+.Sh SEE ALSO
+.Xr nsdispatch 3 ,
+.Xr nscd.conf 5 ,
+.Xr nsswitch.conf 5
+.Sh AUTHORS
+.An Michael Bushkov Aq bushman@freebsd.org
+.Sh BUGS
+Please send bug reports and suggestions to
+.Aq bushman@freebsd.org .
diff --git a/usr.sbin/nscd/nscd.c b/usr.sbin/nscd/nscd.c
new file mode 100644
index 0000000..34a724d
--- /dev/null
+++ b/usr.sbin/nscd/nscd.c
@@ -0,0 +1,869 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in thereg
+ * 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/event.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/un.h>
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libutil.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "agents/passwd.h"
+#include "agents/group.h"
+#include "agents/services.h"
+#include "cachelib.h"
+#include "config.h"
+#include "debug.h"
+#include "log.h"
+#include "nscdcli.h"
+#include "parser.h"
+#include "query.h"
+#include "singletons.h"
+
+#ifndef CONFIG_PATH
+#define CONFIG_PATH "/etc/nscd.conf"
+#endif
+#define DEFAULT_CONFIG_PATH "nscd.conf"
+
+#define MAX_SOCKET_IO_SIZE 4096
+
+struct processing_thread_args {
+ cache the_cache;
+ struct configuration *the_configuration;
+ struct runtime_env *the_runtime_env;
+};
+
+static void accept_connection(struct kevent *, struct runtime_env *,
+ struct configuration *);
+static void destroy_cache_(cache);
+static void destroy_runtime_env(struct runtime_env *);
+static cache init_cache_(struct configuration *);
+static struct runtime_env *init_runtime_env(struct configuration *);
+static void processing_loop(cache, struct runtime_env *,
+ struct configuration *);
+static void process_socket_event(struct kevent *, struct runtime_env *,
+ struct configuration *);
+static void process_timer_event(struct kevent *, struct runtime_env *,
+ struct configuration *);
+static void *processing_thread(void *);
+static void usage(void);
+
+void get_time_func(struct timeval *);
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: nscd [-dnst] [-i cachename] [-I cachename]\n");
+ exit(1);
+}
+
+static cache
+init_cache_(struct configuration *config)
+{
+ struct cache_params params;
+ cache retval;
+
+ struct configuration_entry *config_entry;
+ size_t size, i;
+ int res;
+
+ TRACE_IN(init_cache_);
+
+ memset(&params, 0, sizeof(struct cache_params));
+ params.get_time_func = get_time_func;
+ retval = init_cache(&params);
+
+ size = configuration_get_entries_size(config);
+ for (i = 0; i < size; ++i) {
+ config_entry = configuration_get_entry(config, i);
+ /*
+ * We should register common entries now - multipart entries
+ * would be registered automatically during the queries.
+ */
+ res = register_cache_entry(retval, (struct cache_entry_params *)
+ &config_entry->positive_cache_params);
+ config_entry->positive_cache_entry = find_cache_entry(retval,
+ config_entry->positive_cache_params.entry_name);
+ assert(config_entry->positive_cache_entry !=
+ INVALID_CACHE_ENTRY);
+
+ res = register_cache_entry(retval, (struct cache_entry_params *)
+ &config_entry->negative_cache_params);
+ config_entry->negative_cache_entry = find_cache_entry(retval,
+ config_entry->negative_cache_params.entry_name);
+ assert(config_entry->negative_cache_entry !=
+ INVALID_CACHE_ENTRY);
+ }
+
+ LOG_MSG_2("cache", "cache was successfully initialized");
+ TRACE_OUT(init_cache_);
+ return (retval);
+}
+
+static void
+destroy_cache_(cache the_cache)
+{
+ TRACE_IN(destroy_cache_);
+ destroy_cache(the_cache);
+ TRACE_OUT(destroy_cache_);
+}
+
+/*
+ * Socket and kqueues are prepared here. We have one global queue for both
+ * socket and timers events.
+ */
+static struct runtime_env *
+init_runtime_env(struct configuration *config)
+{
+ int serv_addr_len;
+ struct sockaddr_un serv_addr;
+
+ struct kevent eventlist;
+ struct timespec timeout;
+
+ struct runtime_env *retval;
+
+ TRACE_IN(init_runtime_env);
+ retval = (struct runtime_env *)calloc(1, sizeof(struct runtime_env));
+ assert(retval != NULL);
+
+ retval->sockfd = socket(PF_LOCAL, SOCK_STREAM, 0);
+
+ if (config->force_unlink == 1)
+ unlink(config->socket_path);
+
+ memset(&serv_addr, 0, sizeof(struct sockaddr_un));
+ serv_addr.sun_family = PF_LOCAL;
+ strlcpy(serv_addr.sun_path, config->socket_path,
+ sizeof(serv_addr.sun_path));
+ serv_addr_len = sizeof(serv_addr.sun_family) +
+ strlen(serv_addr.sun_path) + 1;
+
+ if (bind(retval->sockfd, (struct sockaddr *)&serv_addr,
+ serv_addr_len) == -1) {
+ close(retval->sockfd);
+ free(retval);
+
+ LOG_ERR_2("runtime environment", "can't bind socket to path: "
+ "%s", config->socket_path);
+ TRACE_OUT(init_runtime_env);
+ return (NULL);
+ }
+ LOG_MSG_2("runtime environment", "using socket %s",
+ config->socket_path);
+
+ /*
+ * Here we're marking socket as non-blocking and setting its backlog
+ * to the maximum value
+ */
+ chmod(config->socket_path, config->socket_mode);
+ listen(retval->sockfd, -1);
+ fcntl(retval->sockfd, F_SETFL, O_NONBLOCK);
+
+ retval->queue = kqueue();
+ assert(retval->queue != -1);
+
+ EV_SET(&eventlist, retval->sockfd, EVFILT_READ, EV_ADD | EV_ONESHOT,
+ 0, 0, 0);
+ memset(&timeout, 0, sizeof(struct timespec));
+ kevent(retval->queue, &eventlist, 1, NULL, 0, &timeout);
+
+ LOG_MSG_2("runtime environment", "successfully initialized");
+ TRACE_OUT(init_runtime_env);
+ return (retval);
+}
+
+static void
+destroy_runtime_env(struct runtime_env *env)
+{
+ TRACE_IN(destroy_runtime_env);
+ close(env->queue);
+ close(env->sockfd);
+ free(env);
+ TRACE_OUT(destroy_runtime_env);
+}
+
+static void
+accept_connection(struct kevent *event_data, struct runtime_env *env,
+ struct configuration *config)
+{
+ struct kevent eventlist[2];
+ struct timespec timeout;
+ struct query_state *qstate;
+
+ int fd;
+ int res;
+
+ uid_t euid;
+ gid_t egid;
+
+ TRACE_IN(accept_connection);
+ fd = accept(event_data->ident, NULL, NULL);
+ if (fd == -1) {
+ LOG_ERR_2("accept_connection", "error %d during accept()",
+ errno);
+ TRACE_OUT(accept_connection);
+ return;
+ }
+
+ if (getpeereid(fd, &euid, &egid) != 0) {
+ LOG_ERR_2("accept_connection", "error %d during getpeereid()",
+ errno);
+ TRACE_OUT(accept_connection);
+ return;
+ }
+
+ qstate = init_query_state(fd, sizeof(int), euid, egid);
+ if (qstate == NULL) {
+ LOG_ERR_2("accept_connection", "can't init query_state");
+ TRACE_OUT(accept_connection);
+ return;
+ }
+
+ memset(&timeout, 0, sizeof(struct timespec));
+ EV_SET(&eventlist[0], fd, EVFILT_TIMER, EV_ADD | EV_ONESHOT,
+ 0, qstate->timeout.tv_sec * 1000, qstate);
+ EV_SET(&eventlist[1], fd, EVFILT_READ, EV_ADD | EV_ONESHOT,
+ NOTE_LOWAT, qstate->kevent_watermark, qstate);
+ res = kevent(env->queue, eventlist, 2, NULL, 0, &timeout);
+ if (res < 0)
+ LOG_ERR_2("accept_connection", "kevent error");
+
+ TRACE_OUT(accept_connection);
+}
+
+static void
+process_socket_event(struct kevent *event_data, struct runtime_env *env,
+ struct configuration *config)
+{
+ struct kevent eventlist[2];
+ struct timeval query_timeout;
+ struct timespec kevent_timeout;
+ int nevents;
+ int eof_res, res;
+ ssize_t io_res;
+ struct query_state *qstate;
+
+ TRACE_IN(process_socket_event);
+ eof_res = event_data->flags & EV_EOF ? 1 : 0;
+ res = 0;
+
+ memset(&kevent_timeout, 0, sizeof(struct timespec));
+ EV_SET(&eventlist[0], event_data->ident, EVFILT_TIMER, EV_DELETE,
+ 0, 0, NULL);
+ nevents = kevent(env->queue, eventlist, 1, NULL, 0, &kevent_timeout);
+ if (nevents == -1) {
+ if (errno == ENOENT) {
+ /* the timer is already handling this event */
+ TRACE_OUT(process_socket_event);
+ return;
+ } else {
+ /* some other error happened */
+ LOG_ERR_2("process_socket_event", "kevent error, errno"
+ " is %d", errno);
+ TRACE_OUT(process_socket_event);
+ return;
+ }
+ }
+ qstate = (struct query_state *)event_data->udata;
+
+ /*
+ * If the buffer that is to be send/received is too large,
+ * we send it implicitly, by using query_io_buffer_read and
+ * query_io_buffer_write functions in the query_state. These functions
+ * use the temporary buffer, which is later send/received in parts.
+ * The code below implements buffer splitting/mergind for send/receive
+ * operations. It also does the actual socket IO operations.
+ */
+ if (((qstate->use_alternate_io == 0) &&
+ (qstate->kevent_watermark <= event_data->data)) ||
+ ((qstate->use_alternate_io != 0) &&
+ (qstate->io_buffer_watermark <= event_data->data))) {
+ if (qstate->use_alternate_io != 0) {
+ switch (qstate->io_buffer_filter) {
+ case EVFILT_READ:
+ io_res = query_socket_read(qstate,
+ qstate->io_buffer_p,
+ qstate->io_buffer_watermark);
+ if (io_res < 0) {
+ qstate->use_alternate_io = 0;
+ qstate->process_func = NULL;
+ } else {
+ qstate->io_buffer_p += io_res;
+ if (qstate->io_buffer_p ==
+ qstate->io_buffer +
+ qstate->io_buffer_size) {
+ qstate->io_buffer_p =
+ qstate->io_buffer;
+ qstate->use_alternate_io = 0;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (qstate->use_alternate_io == 0) {
+ do {
+ res = qstate->process_func(qstate);
+ } while ((qstate->kevent_watermark == 0) &&
+ (qstate->process_func != NULL) &&
+ (res == 0));
+
+ if (res != 0)
+ qstate->process_func = NULL;
+ }
+
+ if ((qstate->use_alternate_io != 0) &&
+ (qstate->io_buffer_filter == EVFILT_WRITE)) {
+ io_res = query_socket_write(qstate, qstate->io_buffer_p,
+ qstate->io_buffer_watermark);
+ if (io_res < 0) {
+ qstate->use_alternate_io = 0;
+ qstate->process_func = NULL;
+ } else
+ qstate->io_buffer_p += io_res;
+ }
+ } else {
+ /* assuming that socket was closed */
+ qstate->process_func = NULL;
+ qstate->use_alternate_io = 0;
+ }
+
+ if (((qstate->process_func == NULL) &&
+ (qstate->use_alternate_io == 0)) ||
+ (eof_res != 0) || (res != 0)) {
+ destroy_query_state(qstate);
+ close(event_data->ident);
+ TRACE_OUT(process_socket_event);
+ return;
+ }
+
+ /* updating the query_state lifetime variable */
+ get_time_func(&query_timeout);
+ query_timeout.tv_usec = 0;
+ query_timeout.tv_sec -= qstate->creation_time.tv_sec;
+ if (query_timeout.tv_sec > qstate->timeout.tv_sec)
+ query_timeout.tv_sec = 0;
+ else
+ query_timeout.tv_sec = qstate->timeout.tv_sec -
+ query_timeout.tv_sec;
+
+ if ((qstate->use_alternate_io != 0) && (qstate->io_buffer_p ==
+ qstate->io_buffer + qstate->io_buffer_size))
+ qstate->use_alternate_io = 0;
+
+ if (qstate->use_alternate_io == 0) {
+ /*
+ * If we must send/receive the large block of data,
+ * we should prepare the query_state's io_XXX fields.
+ * We should also substitute its write_func and read_func
+ * with the query_io_buffer_write and query_io_buffer_read,
+ * which will allow us to implicitly send/receive this large
+ * buffer later (in the subsequent calls to the
+ * process_socket_event).
+ */
+ if (qstate->kevent_watermark > MAX_SOCKET_IO_SIZE) {
+ if (qstate->io_buffer != NULL)
+ free(qstate->io_buffer);
+
+ qstate->io_buffer = (char *)calloc(1,
+ qstate->kevent_watermark);
+ assert(qstate->io_buffer != NULL);
+
+ qstate->io_buffer_p = qstate->io_buffer;
+ qstate->io_buffer_size = qstate->kevent_watermark;
+ qstate->io_buffer_filter = qstate->kevent_filter;
+
+ qstate->write_func = query_io_buffer_write;
+ qstate->read_func = query_io_buffer_read;
+
+ if (qstate->kevent_filter == EVFILT_READ)
+ qstate->use_alternate_io = 1;
+
+ qstate->io_buffer_watermark = MAX_SOCKET_IO_SIZE;
+ EV_SET(&eventlist[1], event_data->ident,
+ qstate->kevent_filter, EV_ADD | EV_ONESHOT,
+ NOTE_LOWAT, MAX_SOCKET_IO_SIZE, qstate);
+ } else {
+ EV_SET(&eventlist[1], event_data->ident,
+ qstate->kevent_filter, EV_ADD | EV_ONESHOT,
+ NOTE_LOWAT, qstate->kevent_watermark, qstate);
+ }
+ } else {
+ if (qstate->io_buffer + qstate->io_buffer_size -
+ qstate->io_buffer_p <
+ MAX_SOCKET_IO_SIZE) {
+ qstate->io_buffer_watermark = qstate->io_buffer +
+ qstate->io_buffer_size - qstate->io_buffer_p;
+ EV_SET(&eventlist[1], event_data->ident,
+ qstate->io_buffer_filter,
+ EV_ADD | EV_ONESHOT, NOTE_LOWAT,
+ qstate->io_buffer_watermark,
+ qstate);
+ } else {
+ qstate->io_buffer_watermark = MAX_SOCKET_IO_SIZE;
+ EV_SET(&eventlist[1], event_data->ident,
+ qstate->io_buffer_filter, EV_ADD | EV_ONESHOT,
+ NOTE_LOWAT, MAX_SOCKET_IO_SIZE, qstate);
+ }
+ }
+ EV_SET(&eventlist[0], event_data->ident, EVFILT_TIMER,
+ EV_ADD | EV_ONESHOT, 0, query_timeout.tv_sec * 1000, qstate);
+ kevent(env->queue, eventlist, 2, NULL, 0, &kevent_timeout);
+
+ TRACE_OUT(process_socket_event);
+}
+
+/*
+ * This routine is called if timer event has been signaled in the kqueue. It
+ * just closes the socket and destroys the query_state.
+ */
+static void
+process_timer_event(struct kevent *event_data, struct runtime_env *env,
+ struct configuration *config)
+{
+ struct query_state *qstate;
+
+ TRACE_IN(process_timer_event);
+ qstate = (struct query_state *)event_data->udata;
+ destroy_query_state(qstate);
+ close(event_data->ident);
+ TRACE_OUT(process_timer_event);
+}
+
+/*
+ * Processing loop is the basic processing routine, that forms a body of each
+ * procssing thread
+ */
+static void
+processing_loop(cache the_cache, struct runtime_env *env,
+ struct configuration *config)
+{
+ struct timespec timeout;
+ const int eventlist_size = 1;
+ struct kevent eventlist[eventlist_size];
+ int nevents, i;
+
+ TRACE_MSG("=> processing_loop");
+ memset(&timeout, 0, sizeof(struct timespec));
+ memset(&eventlist, 0, sizeof(struct kevent) * eventlist_size);
+
+ for (;;) {
+ nevents = kevent(env->queue, NULL, 0, eventlist,
+ eventlist_size, NULL);
+ /*
+ * we can only receive 1 event on success
+ */
+ if (nevents == 1) {
+ struct kevent *event_data;
+ event_data = &eventlist[0];
+
+ if (event_data->ident == env->sockfd) {
+ for (i = 0; i < event_data->data; ++i)
+ accept_connection(event_data, env, config);
+
+ EV_SET(eventlist, s_runtime_env->sockfd,
+ EVFILT_READ, EV_ADD | EV_ONESHOT,
+ 0, 0, 0);
+ memset(&timeout, 0,
+ sizeof(struct timespec));
+ kevent(s_runtime_env->queue, eventlist,
+ 1, NULL, 0, &timeout);
+
+ } else {
+ switch (event_data->filter) {
+ case EVFILT_READ:
+ case EVFILT_WRITE:
+ process_socket_event(event_data,
+ env, config);
+ break;
+ case EVFILT_TIMER:
+ process_timer_event(event_data,
+ env, config);
+ break;
+ default:
+ break;
+ }
+ }
+ } else {
+ /* this branch shouldn't be currently executed */
+ }
+ }
+
+ TRACE_MSG("<= processing_loop");
+}
+
+/*
+ * Wrapper above the processing loop function. It sets the thread signal mask
+ * to avoid SIGPIPE signals (which can happen if the client works incorrectly).
+ */
+static void *
+processing_thread(void *data)
+{
+ struct processing_thread_args *args;
+ sigset_t new;
+
+ TRACE_MSG("=> processing_thread");
+ args = (struct processing_thread_args *)data;
+
+ sigemptyset(&new);
+ sigaddset(&new, SIGPIPE);
+ if (pthread_sigmask(SIG_BLOCK, &new, NULL) != 0)
+ LOG_ERR_1("processing thread",
+ "thread can't block the SIGPIPE signal");
+
+ processing_loop(args->the_cache, args->the_runtime_env,
+ args->the_configuration);
+ free(args);
+ TRACE_MSG("<= processing_thread");
+
+ return (NULL);
+}
+
+void
+get_time_func(struct timeval *time)
+{
+ struct timespec res;
+ memset(&res, 0, sizeof(struct timespec));
+ clock_gettime(CLOCK_MONOTONIC, &res);
+
+ time->tv_sec = res.tv_sec;
+ time->tv_usec = 0;
+}
+
+/*
+ * The idea of _nss_cache_cycle_prevention_function is that nsdispatch will
+ * search for this symbol in the executable. This symbol is the attribute of
+ * the caching daemon. So, if it exists, nsdispatch won't try to connect to
+ * the caching daemon and will just ignore the 'cache' source in the
+ * nsswitch.conf. This method helps to avoid cycles and organize
+ * self-performing requests.
+ */
+void
+_nss_cache_cycle_prevention_function(void)
+{
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct processing_thread_args *thread_args;
+ pthread_t *threads;
+
+ struct pidfh *pidfile;
+ pid_t pid;
+
+ char const *config_file;
+ char const *error_str;
+ int error_line;
+ int i, res;
+
+ int trace_mode_enabled;
+ int force_single_threaded;
+ int do_not_daemonize;
+ int clear_user_cache_entries, clear_all_cache_entries;
+ char *user_config_entry_name, *global_config_entry_name;
+ int show_statistics;
+ int daemon_mode, interactive_mode;
+
+
+ /* by default all debug messages are omitted */
+ TRACE_OFF();
+
+ /* parsing command line arguments */
+ trace_mode_enabled = 0;
+ force_single_threaded = 0;
+ do_not_daemonize = 0;
+ clear_user_cache_entries = 0;
+ clear_all_cache_entries = 0;
+ show_statistics = 0;
+ user_config_entry_name = NULL;
+ global_config_entry_name = NULL;
+ while ((res = getopt(argc, argv, "nstdi:I:")) != -1) {
+ switch (res) {
+ case 'n':
+ do_not_daemonize = 1;
+ break;
+ case 's':
+ force_single_threaded = 1;
+ break;
+ case 't':
+ trace_mode_enabled = 1;
+ break;
+ case 'i':
+ clear_user_cache_entries = 1;
+ if (optarg != NULL)
+ if (strcmp(optarg, "all") != 0)
+ user_config_entry_name = strdup(optarg);
+ break;
+ case 'I':
+ clear_all_cache_entries = 1;
+ if (optarg != NULL)
+ if (strcmp(optarg, "all") != 0)
+ global_config_entry_name =
+ strdup(optarg);
+ break;
+ case 'd':
+ show_statistics = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ /* NOT REACHED */
+ }
+ }
+
+ daemon_mode = do_not_daemonize | force_single_threaded |
+ trace_mode_enabled;
+ interactive_mode = clear_user_cache_entries | clear_all_cache_entries |
+ show_statistics;
+
+ if ((daemon_mode != 0) && (interactive_mode != 0)) {
+ LOG_ERR_1("main", "daemon mode and interactive_mode arguments "
+ "can't be used together");
+ usage();
+ }
+
+ if (interactive_mode != 0) {
+ FILE *pidfin = fopen(DEFAULT_PIDFILE_PATH, "r");
+ char pidbuf[256];
+
+ struct nscd_connection_params connection_params;
+ nscd_connection connection;
+
+ int result;
+
+ if (pidfin == NULL)
+ errx(EXIT_FAILURE, "There is no daemon running.");
+
+ memset(pidbuf, 0, sizeof(pidbuf));
+ fread(pidbuf, sizeof(pidbuf) - 1, 1, pidfin);
+ fclose(pidfin);
+
+ if (ferror(pidfin) != 0)
+ errx(EXIT_FAILURE, "Can't read from pidfile.");
+
+ if (sscanf(pidbuf, "%d", &pid) != 1)
+ errx(EXIT_FAILURE, "Invalid pidfile.");
+ LOG_MSG_1("main", "daemon PID is %d", pid);
+
+
+ memset(&connection_params, 0,
+ sizeof(struct nscd_connection_params));
+ connection_params.socket_path = DEFAULT_SOCKET_PATH;
+ connection = open_nscd_connection__(&connection_params);
+ if (connection == INVALID_NSCD_CONNECTION)
+ errx(EXIT_FAILURE, "Can't connect to the daemon.");
+
+ if (clear_user_cache_entries != 0) {
+ result = nscd_transform__(connection,
+ user_config_entry_name, TT_USER);
+ if (result != 0)
+ LOG_MSG_1("main",
+ "user cache transformation failed");
+ else
+ LOG_MSG_1("main",
+ "user cache_transformation "
+ "succeeded");
+ }
+
+ if (clear_all_cache_entries != 0) {
+ if (geteuid() != 0)
+ errx(EXIT_FAILURE, "Only root can initiate "
+ "global cache transformation.");
+
+ result = nscd_transform__(connection,
+ global_config_entry_name, TT_ALL);
+ if (result != 0)
+ LOG_MSG_1("main",
+ "global cache transformation "
+ "failed");
+ else
+ LOG_MSG_1("main",
+ "global cache transformation "
+ "succeeded");
+ }
+
+ close_nscd_connection__(connection);
+
+ free(user_config_entry_name);
+ free(global_config_entry_name);
+ return (EXIT_SUCCESS);
+ }
+
+ pidfile = pidfile_open(DEFAULT_PIDFILE_PATH, 0644, &pid);
+ if (pidfile == NULL) {
+ if (errno == EEXIST)
+ errx(EXIT_FAILURE, "Daemon already running, pid: %d.",
+ pid);
+ warn("Cannot open or create pidfile");
+ }
+
+ if (trace_mode_enabled == 1)
+ TRACE_ON();
+
+ /* blocking the main thread from receiving SIGPIPE signal */
+ sigblock(sigmask(SIGPIPE));
+
+ /* daemonization */
+ if (do_not_daemonize == 0) {
+ res = daemon(0, trace_mode_enabled == 0 ? 0 : 1);
+ if (res != 0) {
+ LOG_ERR_1("main", "can't daemonize myself: %s",
+ strerror(errno));
+ pidfile_remove(pidfile);
+ goto fin;
+ } else
+ LOG_MSG_1("main", "successfully daemonized");
+ }
+
+ pidfile_write(pidfile);
+
+ s_agent_table = init_agent_table();
+ register_agent(s_agent_table, init_passwd_agent());
+ register_agent(s_agent_table, init_passwd_mp_agent());
+ register_agent(s_agent_table, init_group_agent());
+ register_agent(s_agent_table, init_group_mp_agent());
+ register_agent(s_agent_table, init_services_agent());
+ register_agent(s_agent_table, init_services_mp_agent());
+ LOG_MSG_1("main", "request agents registered successfully");
+
+ /*
+ * Hosts agent can't work properly until we have access to the
+ * appropriate dtab structures, which are used in nsdispatch
+ * calls
+ *
+ register_agent(s_agent_table, init_hosts_agent());
+ */
+
+ /* configuration initialization */
+ s_configuration = init_configuration();
+ fill_configuration_defaults(s_configuration);
+
+ error_str = NULL;
+ error_line = 0;
+ config_file = CONFIG_PATH;
+
+ res = parse_config_file(s_configuration, config_file, &error_str,
+ &error_line);
+ if ((res != 0) && (error_str == NULL)) {
+ config_file = DEFAULT_CONFIG_PATH;
+ res = parse_config_file(s_configuration, config_file,
+ &error_str, &error_line);
+ }
+
+ if (res != 0) {
+ if (error_str != NULL) {
+ LOG_ERR_1("main", "error in configuration file(%s, %d): %s\n",
+ config_file, error_line, error_str);
+ } else {
+ LOG_ERR_1("main", "no configuration file found "
+ "- was looking for %s and %s",
+ CONFIG_PATH, DEFAULT_CONFIG_PATH);
+ }
+ destroy_configuration(s_configuration);
+ return (-1);
+ }
+
+ if (force_single_threaded == 1)
+ s_configuration->threads_num = 1;
+
+ /* cache initialization */
+ s_cache = init_cache_(s_configuration);
+ if (s_cache == NULL) {
+ LOG_ERR_1("main", "can't initialize the cache");
+ destroy_configuration(s_configuration);
+ return (-1);
+ }
+
+ /* runtime environment initialization */
+ s_runtime_env = init_runtime_env(s_configuration);
+ if (s_runtime_env == NULL) {
+ LOG_ERR_1("main", "can't initialize the runtime environment");
+ destroy_configuration(s_configuration);
+ destroy_cache_(s_cache);
+ return (-1);
+ }
+
+ if (s_configuration->threads_num > 1) {
+ threads = (pthread_t *)calloc(1, sizeof(pthread_t) *
+ s_configuration->threads_num);
+ for (i = 0; i < s_configuration->threads_num; ++i) {
+ thread_args = (struct processing_thread_args *)malloc(
+ sizeof(struct processing_thread_args));
+ thread_args->the_cache = s_cache;
+ thread_args->the_runtime_env = s_runtime_env;
+ thread_args->the_configuration = s_configuration;
+
+ LOG_MSG_1("main", "thread #%d was successfully created",
+ i);
+ pthread_create(&threads[i], NULL, processing_thread,
+ thread_args);
+
+ thread_args = NULL;
+ }
+
+ for (i = 0; i < s_configuration->threads_num; ++i)
+ pthread_join(threads[i], NULL);
+ } else {
+ LOG_MSG_1("main", "working in single-threaded mode");
+ processing_loop(s_cache, s_runtime_env, s_configuration);
+ }
+
+fin:
+ /* runtime environment destruction */
+ destroy_runtime_env(s_runtime_env);
+
+ /* cache destruction */
+ destroy_cache_(s_cache);
+
+ /* configuration destruction */
+ destroy_configuration(s_configuration);
+
+ /* agents table destruction */
+ destroy_agent_table(s_agent_table);
+
+ pidfile_remove(pidfile);
+ return (EXIT_SUCCESS);
+}
diff --git a/usr.sbin/nscd/nscd.conf.5 b/usr.sbin/nscd/nscd.conf.5
new file mode 100644
index 0000000..f64ce27
--- /dev/null
+++ b/usr.sbin/nscd/nscd.conf.5
@@ -0,0 +1,148 @@
+.\" Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 30, 2006
+.Dt NSCD.CONF 5
+.Os
+.Sh NAME
+.Nm nscd.conf
+.Nd "nscd configuration file"
+.Sh DESCRIPTION
+The
+.Nm
+file
+is used by the
+.Xr nscd 8
+daemon and is read on its startup.
+Its syntax is mostly similar to the
+.Pa nscd.conf
+syntax in
+.Tn Linux
+and
+.Tn Solaris .
+It has some differences, though \[em] see them below.
+.Pp
+Each line specifies either an attribute and a
+.Ar value ,
+or an attribute, a
+.Ar cachename
+and a
+.Ar value .
+Usual cachenames are
+.Dq Li passwd ,
+.Dq Li group ,
+.Dq Li hosts ,
+.Dq Li services ,
+.Dq Li protocols
+and
+.Dq Li rpc .
+You can also use any other
+.Ar cachename
+(for example, if some third-party
+application uses nsswitch).
+.Bl -tag -width indent
+.It Va threads Op Ar value
+Number of threads, which would listen for connections and process requests.
+The minimum is 1.
+The default value is 8.
+.It Va enable-cache Oo Ar cachename Oc Op Cm yes | no
+Enables or disables the cache for specified
+.Ar cachename .
+.It Va positive-time-to-live Oo Ar cachename Oc Op Ar value
+Sets the TTL (time-to-live) for the specified cache in seconds.
+Larger values can increase system's performance, but they also can affect
+the cache coherence.
+The default value is 3600.
+.It Va positive-policy Oo Ar cachename Oc Op Cm fifo | lru | lfu
+The policy that is applied to erase some of the cache elements, when the
+size limit of the given
+.Ar cachename
+is exceeded.
+Possible policies are:
+.Cm fifo
+(first-in-first-out),
+.Cm lru
+(least-recently-used), and
+.Cm lfu
+(least-frequently-used).
+The default policy is
+.Cm lru .
+.It Va negative-time-to-live Oo Ar cachename Oc Op Ar value
+The TTL of the negative cached elements in seconds.
+The larger values can significantly increase system performance in some
+environments (when dealing with files with UIDs, which are not in system
+databases, for example).
+This number should be kept low to avoid the cache coherence problems.
+The default value is 60.
+.It Va negative-policy Oo Ar cachename Oc Op Cm fifo | lru | lfu
+The same as the positive-policy, but this one is applied to the negative
+elements of the given
+.Ar cachename .
+The default policy is fifo.
+.It Va suggested-size Oo Ar cachename Oc Op Ar value
+This is the internal hash table size.
+The value should be a prime number for optimum performance.
+You should only change this value when the number of cached elements is
+significantly (in 5-10 times) greater then the default hash table size (255).
+.It Va keep-hot-count Oo Ar cachename Oc Op Ar value
+The size limit of the cache with the given
+.Ar cachename .
+When it is exceeded, the policy will be applied.
+The default value is 2048.
+.It Va perform-actual-lookups Oo Ar cachename Oc Op Cm yes | no
+If enabled, the
+.Xr nscd 8
+does not simply receive and cache the NSS-requests results, but performs
+all the lookups by itself and only returns the responses.
+If this feature is enabled, then for the given
+.Ar cachename
+.Xr nscd 8
+will act similarly to the NSCD.
+.Pp
+.Sy NOTE :
+this feature is currently experimental \[em] it supports only
+.Dq Li passwd ,
+.Dq Li group
+and
+.Dq Li services
+cachenames.
+.El
+.Sh NOTES
+You can use the
+.Ql #
+symbol at the beginning of the line for comments.
+.Sh FILES
+.Bl -tag -width ".Pa /etc/nscd.conf" -compact
+.It Pa /etc/nscd.conf
+.El
+.Sh SEE ALSO
+.Xr nscd 8
+.Sh AUTHORS
+.An Michael Bushkov
+.Aq bushman@freebsd.org
+.Sh BUGS
+Please send bug reports and suggestions to
+.Aq bushman@freebsd.org .
diff --git a/usr.sbin/nscd/nscdcli.c b/usr.sbin/nscd/nscdcli.c
new file mode 100644
index 0000000..3b6996d
--- /dev/null
+++ b/usr.sbin/nscd/nscdcli.c
@@ -0,0 +1,283 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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/socket.h>
+#include <sys/event.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "debug.h"
+#include "nscdcli.h"
+#include "protocol.h"
+
+#define DEFAULT_NSCD_IO_TIMEOUT 4
+
+static int safe_write(struct nscd_connection_ *, const void *, size_t);
+static int safe_read(struct nscd_connection_ *, void *, size_t);
+static int send_credentials(struct nscd_connection_ *, int);
+
+static int
+safe_write(struct nscd_connection_ *connection, const void *data,
+ size_t data_size)
+{
+ struct kevent eventlist;
+ int nevents;
+ size_t result;
+ ssize_t s_result;
+ struct timespec timeout;
+
+ if (data_size == 0)
+ return (0);
+
+ timeout.tv_sec = DEFAULT_NSCD_IO_TIMEOUT;
+ timeout.tv_nsec = 0;
+ result = 0;
+ do {
+ nevents = kevent(connection->write_queue, NULL, 0, &eventlist,
+ 1, &timeout);
+ if ((nevents == 1) && (eventlist.filter == EVFILT_WRITE)) {
+ s_result = write(connection->sockfd, data + result,
+ eventlist.data < data_size - result ?
+ eventlist.data : data_size - result);
+ if (s_result == -1)
+ return (-1);
+ else
+ result += s_result;
+
+ if (eventlist.flags & EV_EOF)
+ return (result < data_size ? -1 : 0);
+ } else
+ return (-1);
+ } while (result < data_size);
+
+ return (0);
+}
+
+static int
+safe_read(struct nscd_connection_ *connection, void *data, size_t data_size)
+{
+ struct kevent eventlist;
+ size_t result;
+ ssize_t s_result;
+ struct timespec timeout;
+ int nevents;
+
+ if (data_size == 0)
+ return (0);
+
+ timeout.tv_sec = DEFAULT_NSCD_IO_TIMEOUT;
+ timeout.tv_nsec = 0;
+ result = 0;
+ do {
+ nevents = kevent(connection->read_queue, NULL, 0, &eventlist, 1,
+ &timeout);
+ if ((nevents == 1) && (eventlist.filter == EVFILT_READ)) {
+ s_result = read(connection->sockfd, data + result,
+ eventlist.data <= data_size - result ? eventlist.data :
+ data_size - result);
+ if (s_result == -1)
+ return (-1);
+ else
+ result += s_result;
+
+ if (eventlist.flags & EV_EOF)
+ return (result < data_size ? -1 : 0);
+ } else
+ return (-1);
+ } while (result < data_size);
+
+ return (0);
+}
+
+static int
+send_credentials(struct nscd_connection_ *connection, int type)
+{
+ struct kevent eventlist;
+ int nevents;
+ ssize_t result;
+ int res;
+
+ struct msghdr cred_hdr;
+ struct iovec iov;
+
+ struct {
+ struct cmsghdr hdr;
+ struct cmsgcred creds;
+ } cmsg;
+
+ TRACE_IN(send_credentials);
+ memset(&cmsg, 0, sizeof(cmsg));
+ cmsg.hdr.cmsg_len = sizeof(cmsg);
+ cmsg.hdr.cmsg_level = SOL_SOCKET;
+ cmsg.hdr.cmsg_type = SCM_CREDS;
+
+ memset(&cred_hdr, 0, sizeof(struct msghdr));
+ cred_hdr.msg_iov = &iov;
+ cred_hdr.msg_iovlen = 1;
+ cred_hdr.msg_control = &cmsg;
+ cred_hdr.msg_controllen = sizeof(cmsg);
+
+ iov.iov_base = &type;
+ iov.iov_len = sizeof(int);
+
+ EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD,
+ NOTE_LOWAT, sizeof(int), NULL);
+ res = kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL);
+
+ nevents = kevent(connection->write_queue, NULL, 0, &eventlist, 1, NULL);
+ if ((nevents == 1) && (eventlist.filter == EVFILT_WRITE)) {
+ result = (sendmsg(connection->sockfd, &cred_hdr, 0) == -1) ? -1
+ : 0;
+ EV_SET(&eventlist, connection->sockfd, EVFILT_WRITE, EV_ADD,
+ 0, 0, NULL);
+ kevent(connection->write_queue, &eventlist, 1, NULL, 0, NULL);
+ TRACE_OUT(send_credentials);
+ return (result);
+ } else {
+ TRACE_OUT(send_credentials);
+ return (-1);
+ }
+}
+
+struct nscd_connection_ *
+open_nscd_connection__(struct nscd_connection_params const *params)
+{
+ struct nscd_connection_ *retval;
+ struct kevent eventlist;
+ struct sockaddr_un client_address;
+ int client_address_len, client_socket;
+ int res;
+
+ TRACE_IN(open_nscd_connection);
+ assert(params != NULL);
+
+ client_socket = socket(PF_LOCAL, SOCK_STREAM, 0);
+ client_address.sun_family = PF_LOCAL;
+ strlcpy(client_address.sun_path, params->socket_path,
+ sizeof(client_address.sun_path));
+ client_address_len = sizeof(client_address.sun_family) +
+ strlen(client_address.sun_path) + 1;
+
+ res = connect(client_socket, (struct sockaddr *)&client_address,
+ client_address_len);
+ if (res == -1) {
+ close(client_socket);
+ TRACE_OUT(open_nscd_connection);
+ return (NULL);
+ }
+ fcntl(client_socket, F_SETFL, O_NONBLOCK);
+
+ retval = calloc(1, sizeof(struct nscd_connection_));
+ assert(retval != NULL);
+
+ retval->sockfd = client_socket;
+
+ retval->write_queue = kqueue();
+ assert(retval->write_queue != -1);
+
+ EV_SET(&eventlist, retval->sockfd, EVFILT_WRITE, EV_ADD,
+ 0, 0, NULL);
+ res = kevent(retval->write_queue, &eventlist, 1, NULL, 0, NULL);
+
+ retval->read_queue = kqueue();
+ assert(retval->read_queue != -1);
+
+ EV_SET(&eventlist, retval->sockfd, EVFILT_READ, EV_ADD,
+ 0, 0, NULL);
+ res = kevent(retval->read_queue, &eventlist, 1, NULL, 0, NULL);
+
+ TRACE_OUT(open_nscd_connection);
+ return (retval);
+}
+
+void
+close_nscd_connection__(struct nscd_connection_ *connection)
+{
+
+ TRACE_IN(close_nscd_connection);
+ assert(connection != NULL);
+
+ close(connection->sockfd);
+ close(connection->read_queue);
+ close(connection->write_queue);
+ free(connection);
+ TRACE_OUT(close_nscd_connection);
+}
+
+int
+nscd_transform__(struct nscd_connection_ *connection,
+ const char *entry_name, int transformation_type)
+{
+ size_t name_size;
+ int error_code;
+ int result;
+
+ TRACE_IN(nscd_transform);
+
+ error_code = -1;
+ result = 0;
+ result = send_credentials(connection, CET_TRANSFORM_REQUEST);
+ if (result != 0)
+ goto fin;
+
+ if (entry_name != NULL)
+ name_size = strlen(entry_name);
+ else
+ name_size = 0;
+
+ result = safe_write(connection, &name_size, sizeof(size_t));
+ if (result != 0)
+ goto fin;
+
+ result = safe_write(connection, &transformation_type, sizeof(int));
+ if (result != 0)
+ goto fin;
+
+ if (entry_name != NULL) {
+ result = safe_write(connection, entry_name, name_size);
+ if (result != 0)
+ goto fin;
+ }
+
+ result = safe_read(connection, &error_code, sizeof(int));
+ if (result != 0)
+ error_code = -1;
+
+fin:
+ TRACE_OUT(nscd_transform);
+ return (error_code);
+}
diff --git a/usr.sbin/nscd/nscdcli.h b/usr.sbin/nscd/nscdcli.h
new file mode 100644
index 0000000..cfbd7e6
--- /dev/null
+++ b/usr.sbin/nscd/nscdcli.h
@@ -0,0 +1,57 @@
+/*-
+ * Copyright (c) 2004 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 __NSCD_NSCDCLI_H__
+#define __NSCD_NSCDCLI_H__
+
+struct nscd_connection_params {
+ char *socket_path;
+ struct timeval timeout;
+};
+
+struct nscd_connection_ {
+ int sockfd;
+ int read_queue;
+ int write_queue;
+};
+
+/* simple abstractions for not to write "struct" every time */
+typedef struct nscd_connection_ *nscd_connection;
+typedef struct nscd_connection_ *nscd_mp_write_session;
+typedef struct nscd_connection_ *nscd_mp_read_session;
+
+#define INVALID_NSCD_CONNECTION (NULL)
+
+/* initialization/destruction routines */
+extern nscd_connection open_nscd_connection__(
+ struct nscd_connection_params const *);
+extern void close_nscd_connection__(nscd_connection);
+
+extern int nscd_transform__(nscd_connection, const char *, int);
+
+#endif
diff --git a/usr.sbin/nscd/parser.c b/usr.sbin/nscd/parser.c
new file mode 100644
index 0000000..b877efa
--- /dev/null
+++ b/usr.sbin/nscd/parser.c
@@ -0,0 +1,474 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <assert.h>
+#include <stdio.h>
+#include <string.h>
+#include "config.h"
+#include "debug.h"
+#include "log.h"
+#include "parser.h"
+
+static void enable_cache(struct configuration *,const char *, int);
+static struct configuration_entry *find_create_entry(struct configuration *,
+ const char *);
+static int get_number(const char *, int, int);
+static enum cache_policy_t get_policy(const char *);
+static int get_yesno(const char *);
+static int check_cachename(const char *);
+static void check_files(struct configuration *, const char *, int);
+static void set_keep_hot_count(struct configuration *, const char *, int);
+static void set_negative_policy(struct configuration *, const char *,
+ enum cache_policy_t);
+static void set_negative_time_to_live(struct configuration *,
+ const char *, int);
+static void set_positive_policy(struct configuration *, const char *,
+ enum cache_policy_t);
+static void set_perform_actual_lookups(struct configuration *, const char *,
+ int);
+static void set_positive_time_to_live(struct configuration *,
+ const char *, int);
+static void set_suggested_size(struct configuration *, const char *,
+ int size);
+static void set_threads_num(struct configuration *, int);
+static int strbreak(char *, char **, int);
+
+static int
+strbreak(char *str, char **fields, int fields_size)
+{
+ char *c = str;
+ int i, num;
+
+ TRACE_IN(strbreak);
+ num = 0;
+ for (i = 0;
+ ((*fields =
+ strsep(i < fields_size ? &c : NULL, "\n\t ")) != NULL);
+ ++i)
+ if ((*(*fields)) != '\0') {
+ ++fields;
+ ++num;
+ }
+
+ TRACE_OUT(strbreak);
+ return (num);
+}
+
+/*
+ * Tries to find the configuration entry with the specified name. If search
+ * fails, the new entry with the default parameters will be created.
+ */
+static struct configuration_entry *
+find_create_entry(struct configuration *config,
+ const char *entry_name)
+{
+ struct configuration_entry *entry = NULL;
+ int res;
+
+ TRACE_IN(find_create_entry);
+ entry = configuration_find_entry(config, entry_name);
+ if (entry == NULL) {
+ entry = create_def_configuration_entry(entry_name);
+ assert( entry != NULL);
+ res = add_configuration_entry(config, entry);
+ assert(res == 0);
+ }
+
+ TRACE_OUT(find_create_entry);
+ return (entry);
+}
+
+/*
+ * The vast majority of the functions below corresponds to the particular
+ * keywords in the configuration file.
+ */
+static void
+enable_cache(struct configuration *config, const char *entry_name, int flag)
+{
+ struct configuration_entry *entry;
+
+ TRACE_IN(enable_cache);
+ entry = find_create_entry(config, entry_name);
+ entry->enabled = flag;
+ TRACE_OUT(enable_cache);
+}
+
+static void
+set_positive_time_to_live(struct configuration *config,
+ const char *entry_name, int ttl)
+{
+ struct configuration_entry *entry;
+ struct timeval lifetime;
+
+ TRACE_IN(set_positive_time_to_live);
+ assert(ttl >= 0);
+ assert(entry_name != NULL);
+ memset(&lifetime, 0, sizeof(struct timeval));
+ lifetime.tv_sec = ttl;
+
+ entry = find_create_entry(config, entry_name);
+ memcpy(&entry->positive_cache_params.max_lifetime,
+ &lifetime, sizeof(struct timeval));
+ memcpy(&entry->mp_cache_params.max_lifetime,
+ &lifetime, sizeof(struct timeval));
+
+ TRACE_OUT(set_positive_time_to_live);
+}
+
+static void
+set_negative_time_to_live(struct configuration *config,
+ const char *entry_name, int nttl)
+{
+ struct configuration_entry *entry;
+ struct timeval lifetime;
+
+ TRACE_IN(set_negative_time_to_live);
+ assert(nttl > 0);
+ assert(entry_name != NULL);
+ memset(&lifetime, 0, sizeof(struct timeval));
+ lifetime.tv_sec = nttl;
+
+ entry = find_create_entry(config, entry_name);
+ assert(entry != NULL);
+ memcpy(&entry->negative_cache_params.max_lifetime,
+ &lifetime, sizeof(struct timeval));
+
+ TRACE_OUT(set_negative_time_to_live);
+}
+
+/*
+ * Hot count is actually the elements size limit.
+ */
+static void
+set_keep_hot_count(struct configuration *config,
+ const char *entry_name, int count)
+{
+ struct configuration_entry *entry;
+
+ TRACE_IN(set_keep_hot_count);
+ assert(count >= 0);
+ assert(entry_name != NULL);
+
+ entry = find_create_entry(config, entry_name);
+ assert(entry != NULL);
+ entry->positive_cache_params.max_elemsize = count;
+
+ entry = find_create_entry(config, entry_name);
+ assert(entry != NULL);
+ entry->negative_cache_params.max_elemsize = count;
+
+ TRACE_OUT(set_keep_hot_count);
+}
+
+static void
+set_positive_policy(struct configuration *config,
+ const char *entry_name, enum cache_policy_t policy)
+{
+ struct configuration_entry *entry;
+
+ TRACE_IN(set_positive_policy);
+ assert(entry_name != NULL);
+
+ entry = find_create_entry(config, entry_name);
+ assert(entry != NULL);
+ entry->positive_cache_params.policy = policy;
+
+ TRACE_OUT(set_positive_policy);
+}
+
+static void
+set_negative_policy(struct configuration *config,
+ const char *entry_name, enum cache_policy_t policy)
+{
+ struct configuration_entry *entry;
+
+ TRACE_IN(set_negative_policy);
+ assert(entry_name != NULL);
+
+ entry = find_create_entry(config, entry_name);
+ assert(entry != NULL);
+ entry->negative_cache_params.policy = policy;
+
+ TRACE_OUT(set_negative_policy);
+}
+
+static void
+set_perform_actual_lookups(struct configuration *config,
+ const char *entry_name, int flag)
+{
+ struct configuration_entry *entry;
+
+ TRACE_IN(set_perform_actual_lookups);
+ assert(entry_name != NULL);
+
+ entry = find_create_entry(config, entry_name);
+ assert(entry != NULL);
+ entry->perform_actual_lookups = flag;
+
+ TRACE_OUT(set_perform_actual_lookups);
+}
+
+static void
+set_suggested_size(struct configuration *config,
+ const char *entry_name, int size)
+{
+ struct configuration_entry *entry;
+
+ TRACE_IN(set_suggested_size);
+ assert(config != NULL);
+ assert(entry_name != NULL);
+ assert(size > 0);
+
+ entry = find_create_entry(config, entry_name);
+ assert(entry != NULL);
+ entry->positive_cache_params.cache_entries_size = size;
+ entry->negative_cache_params.cache_entries_size = size;
+
+ TRACE_OUT(set_suggested_size);
+}
+
+static void
+check_files(struct configuration *config, const char *entry_name, int flag)
+{
+
+ TRACE_IN(check_files);
+ assert(entry_name != NULL);
+ TRACE_OUT(check_files);
+}
+
+static int
+get_yesno(const char *str)
+{
+
+ if (strcmp(str, "yes") == 0)
+ return (1);
+ else if (strcmp(str, "no") == 0)
+ return (0);
+ else
+ return (-1);
+}
+
+static int
+get_number(const char *str, int low, int max)
+{
+
+ char *end = NULL;
+ int res = 0;
+
+ if (str[0] == '\0')
+ return (-1);
+
+ res = strtol(str, &end, 10);
+ if (*end != '\0')
+ return (-1);
+ else
+ if (((res >= low) || (low == -1)) &&
+ ((res <= max) || (max == -1)))
+ return (res);
+ else
+ return (-2);
+}
+
+static enum cache_policy_t
+get_policy(const char *str)
+{
+
+ if (strcmp(str, "fifo") == 0)
+ return (CPT_FIFO);
+ else if (strcmp(str, "lru") == 0)
+ return (CPT_LRU);
+ else if (strcmp(str, "lfu") == 0)
+ return (CPT_LFU);
+
+ return (-1);
+}
+
+static int
+check_cachename(const char *str)
+{
+
+ assert(str != NULL);
+ return ((strlen(str) > 0) ? 0 : -1);
+}
+
+static void
+set_threads_num(struct configuration *config, int value)
+{
+
+ assert(config != NULL);
+ config->threads_num = value;
+}
+
+/*
+ * The main configuration routine. Its implementation is hugely inspired by the
+ * the same routine implementation in Solaris NSCD.
+ */
+int
+parse_config_file(struct configuration *config,
+ const char *fname, char const **error_str, int *error_line)
+{
+ FILE *fin;
+ char buffer[255];
+ char *fields[128];
+ int field_count, line_num, value;
+ int res;
+
+ TRACE_IN(parse_config_file);
+ assert(config != NULL);
+ assert(fname != NULL);
+
+ fin = fopen(fname, "r");
+ if (fin == NULL) {
+ TRACE_OUT(parse_config_file);
+ return (-1);
+ }
+
+ res = 0;
+ line_num = 0;
+ memset(buffer, 0, sizeof(buffer));
+ while ((res == 0) && (fgets(buffer, sizeof(buffer) - 1, fin) != NULL)) {
+ field_count = strbreak(buffer, fields, sizeof(fields));
+ ++line_num;
+
+ if (field_count == 0)
+ continue;
+
+ switch (fields[0][0]) {
+ case '#':
+ case '\0':
+ continue;
+ case 'e':
+ if ((field_count == 3) &&
+ (strcmp(fields[0], "enable-cache") == 0) &&
+ (check_cachename(fields[1]) == 0) &&
+ ((value = get_yesno(fields[2])) != -1)) {
+ enable_cache(config, fields[1], value);
+ continue;
+ }
+ break;
+ case 'd':
+ if ((field_count == 2) &&
+ (strcmp(fields[0], "debug-level") == 0) &&
+ ((value = get_number(fields[1], 0, 10)) != -1)) {
+ continue;
+ }
+ break;
+ case 'p':
+ if ((field_count == 3) &&
+ (strcmp(fields[0], "positive-time-to-live") == 0) &&
+ (check_cachename(fields[1]) == 0) &&
+ ((value = get_number(fields[2], 0, -1)) != -1)) {
+ set_positive_time_to_live(config,
+ fields[1], value);
+ continue;
+ } else if ((field_count == 3) &&
+ (strcmp(fields[0], "positive-policy") == 0) &&
+ (check_cachename(fields[1]) == 0) &&
+ ((value = get_policy(fields[2])) != -1)) {
+ set_positive_policy(config, fields[1], value);
+ continue;
+ } else if ((field_count == 3) &&
+ (strcmp(fields[0], "perform-actual-lookups") == 0) &&
+ (check_cachename(fields[1]) == 0) &&
+ ((value = get_yesno(fields[2])) != -1)) {
+ set_perform_actual_lookups(config, fields[1],
+ value);
+ continue;
+ }
+ break;
+ case 'n':
+ if ((field_count == 3) &&
+ (strcmp(fields[0], "negative-time-to-live") == 0) &&
+ (check_cachename(fields[1]) == 0) &&
+ ((value = get_number(fields[2], 0, -1)) != -1)) {
+ set_negative_time_to_live(config,
+ fields[1], value);
+ continue;
+ } else if ((field_count == 3) &&
+ (strcmp(fields[0], "negative-policy") == 0) &&
+ (check_cachename(fields[1]) == 0) &&
+ ((value = get_policy(fields[2])) != -1)) {
+ set_negative_policy(config,
+ fields[1], value);
+ continue;
+ }
+ break;
+ case 's':
+ if ((field_count == 3) &&
+ (strcmp(fields[0], "suggested-size") == 0) &&
+ (check_cachename(fields[1]) == 0) &&
+ ((value = get_number(fields[2], 1, -1)) != -1)) {
+ set_suggested_size(config, fields[1], value);
+ continue;
+ }
+ break;
+ case 't':
+ if ((field_count == 2) &&
+ (strcmp(fields[0], "threads") == 0) &&
+ ((value = get_number(fields[1], 1, -1)) != -1)) {
+ set_threads_num(config, value);
+ continue;
+ }
+ break;
+ case 'k':
+ if ((field_count == 3) &&
+ (strcmp(fields[0], "keep-hot-count") == 0) &&
+ (check_cachename(fields[1]) == 0) &&
+ ((value = get_number(fields[2], 0, -1)) != -1)) {
+ set_keep_hot_count(config,
+ fields[1], value);
+ continue;
+ }
+ break;
+ case 'c':
+ if ((field_count == 3) &&
+ (strcmp(fields[0], "check-files") == 0) &&
+ (check_cachename(fields[1]) == 0) &&
+ ((value = get_yesno(fields[2])) != -1)) {
+ check_files(config,
+ fields[1], value);
+ continue;
+ }
+ break;
+ default:
+ break;
+ }
+
+ LOG_ERR_2("config file parser", "error in file "
+ "%s on line %d", fname, line_num);
+ *error_str = "syntax error";
+ *error_line = line_num;
+ res = -1;
+ }
+ fclose(fin);
+
+ TRACE_OUT(parse_config_file);
+ return (res);
+}
diff --git a/usr.sbin/nscd/parser.h b/usr.sbin/nscd/parser.h
new file mode 100644
index 0000000..413b784
--- /dev/null
+++ b/usr.sbin/nscd/parser.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 __NSCD_PARSER_H__
+#define __NSCD_PARSER_H__
+
+extern int parse_config_file(struct configuration *,
+ const char *, char const **, int *);
+
+#endif
diff --git a/usr.sbin/nscd/protocol.c b/usr.sbin/nscd/protocol.c
new file mode 100644
index 0000000..08cea92
--- /dev/null
+++ b/usr.sbin/nscd/protocol.c
@@ -0,0 +1,550 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include "debug.h"
+#include "log.h"
+#include "protocol.h"
+
+/*
+ * Initializes the comm_element with any given type of data
+ */
+void
+init_comm_element(struct comm_element *element, enum comm_element_t type)
+{
+
+ TRACE_IN(init_comm_element);
+ memset(element, 0, sizeof(struct comm_element));
+
+ switch (type) {
+ case CET_WRITE_REQUEST:
+ init_cache_write_request(&element->c_write_request);
+ break;
+ case CET_WRITE_RESPONSE:
+ init_cache_write_response(&element->c_write_response);
+ break;
+ case CET_READ_REQUEST:
+ init_cache_read_request(&element->c_read_request);
+ break;
+ case CET_READ_RESPONSE:
+ init_cache_read_response(&element->c_read_response);
+ break;
+ case CET_TRANSFORM_REQUEST:
+ init_cache_transform_request(&element->c_transform_request);
+ break;
+ case CET_TRANSFORM_RESPONSE:
+ init_cache_transform_response(&element->c_transform_response);
+ break;
+ case CET_MP_WRITE_SESSION_REQUEST:
+ init_cache_mp_write_session_request(&element->c_mp_ws_request);
+ break;
+ case CET_MP_WRITE_SESSION_RESPONSE:
+ init_cache_mp_write_session_response(&element->c_mp_ws_response);
+ break;
+ case CET_MP_WRITE_SESSION_WRITE_REQUEST:
+ init_cache_mp_write_session_write_request(
+ &element->c_mp_ws_write_request);
+ break;
+ case CET_MP_WRITE_SESSION_WRITE_RESPONSE:
+ init_cache_mp_write_session_write_response(
+ &element->c_mp_ws_write_response);
+ break;
+ case CET_MP_READ_SESSION_REQUEST:
+ init_cache_mp_read_session_request(&element->c_mp_rs_request);
+ break;
+ case CET_MP_READ_SESSION_RESPONSE:
+ init_cache_mp_read_session_response(&element->c_mp_rs_response);
+ break;
+ case CET_MP_READ_SESSION_READ_RESPONSE:
+ init_cache_mp_read_session_read_response(
+ &element->c_mp_rs_read_response);
+ break;
+ case CET_UNDEFINED:
+ break;
+ default:
+ LOG_ERR_2("init_comm_element", "invalid communication element");
+ TRACE_OUT(init_comm_element);
+ return;
+ }
+
+ element->type = type;
+ TRACE_OUT(init_comm_element);
+}
+
+void
+finalize_comm_element(struct comm_element *element)
+{
+
+ TRACE_IN(finalize_comm_element);
+ switch (element->type) {
+ case CET_WRITE_REQUEST:
+ finalize_cache_write_request(&element->c_write_request);
+ break;
+ case CET_WRITE_RESPONSE:
+ finalize_cache_write_response(&element->c_write_response);
+ break;
+ case CET_READ_REQUEST:
+ finalize_cache_read_request(&element->c_read_request);
+ break;
+ case CET_READ_RESPONSE:
+ finalize_cache_read_response(&element->c_read_response);
+ break;
+ case CET_TRANSFORM_REQUEST:
+ finalize_cache_transform_request(&element->c_transform_request);
+ break;
+ case CET_TRANSFORM_RESPONSE:
+ finalize_cache_transform_response(
+ &element->c_transform_response);
+ break;
+ case CET_MP_WRITE_SESSION_REQUEST:
+ finalize_cache_mp_write_session_request(
+ &element->c_mp_ws_request);
+ break;
+ case CET_MP_WRITE_SESSION_RESPONSE:
+ finalize_cache_mp_write_session_response(
+ &element->c_mp_ws_response);
+ break;
+ case CET_MP_WRITE_SESSION_WRITE_REQUEST:
+ finalize_cache_mp_write_session_write_request(
+ &element->c_mp_ws_write_request);
+ break;
+ case CET_MP_WRITE_SESSION_WRITE_RESPONSE:
+ finalize_cache_mp_write_session_write_response(
+ &element->c_mp_ws_write_response);
+ break;
+ case CET_MP_READ_SESSION_REQUEST:
+ finalize_cache_mp_read_session_request(
+ &element->c_mp_rs_request);
+ break;
+ case CET_MP_READ_SESSION_RESPONSE:
+ finalize_cache_mp_read_session_response(
+ &element->c_mp_rs_response);
+ break;
+ case CET_MP_READ_SESSION_READ_RESPONSE:
+ finalize_cache_mp_read_session_read_response(
+ &element->c_mp_rs_read_response);
+ break;
+ case CET_UNDEFINED:
+ break;
+ default:
+ break;
+ }
+
+ element->type = CET_UNDEFINED;
+ TRACE_OUT(finalize_comm_element);
+}
+
+void
+init_cache_write_request(struct cache_write_request *write_request)
+{
+
+ TRACE_IN(init_cache_write_request);
+ memset(write_request, 0, sizeof(struct cache_write_request));
+ TRACE_OUT(init_cache_write_request);
+}
+
+void
+finalize_cache_write_request(struct cache_write_request *write_request)
+{
+
+ TRACE_IN(finalize_cache_write_request);
+ free(write_request->entry);
+ free(write_request->cache_key);
+ free(write_request->data);
+ TRACE_OUT(finalize_cache_write_request);
+}
+
+struct cache_write_request *
+get_cache_write_request(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_write_request);
+ assert(element->type == CET_WRITE_REQUEST);
+ TRACE_OUT(get_cache_write_request);
+ return (&element->c_write_request);
+}
+
+void
+init_cache_write_response(struct cache_write_response *write_response)
+{
+
+ TRACE_IN(init_cache_write_response);
+ memset(write_response, 0, sizeof(struct cache_write_response));
+ TRACE_OUT(init_cache_write_response);
+}
+
+void
+finalize_cache_write_response(struct cache_write_response *write_response)
+{
+
+ TRACE_IN(finalize_cache_write_response);
+ TRACE_OUT(finalize_cache_write_response);
+}
+
+struct cache_write_response *
+get_cache_write_response(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_write_response);
+ assert(element->type == CET_WRITE_RESPONSE);
+ TRACE_OUT(get_cache_write_response);
+ return (&element->c_write_response);
+}
+
+void
+init_cache_read_request(struct cache_read_request *read_request)
+{
+
+ TRACE_IN(init_cache_read_request);
+ memset(read_request, 0, sizeof(struct cache_read_request));
+ TRACE_OUT(init_cache_read_request);
+}
+
+void
+finalize_cache_read_request(struct cache_read_request *read_request)
+{
+
+ TRACE_IN(finalize_cache_read_request);
+ free(read_request->entry);
+ free(read_request->cache_key);
+ TRACE_OUT(finalize_cache_read_request);
+}
+
+struct cache_read_request *
+get_cache_read_request(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_read_request);
+ assert(element->type == CET_READ_REQUEST);
+ TRACE_OUT(get_cache_read_request);
+ return (&element->c_read_request);
+}
+
+void
+init_cache_read_response(struct cache_read_response *read_response)
+{
+
+ TRACE_IN(init_cache_read_response);
+ memset(read_response, 0, sizeof(struct cache_read_response));
+ TRACE_OUT(init_cache_read_response);
+}
+
+void
+finalize_cache_read_response(struct cache_read_response *read_response)
+{
+
+ TRACE_IN(finalize_cache_read_response);
+ free(read_response->data);
+ TRACE_OUT(finalize_cache_read_response);
+}
+
+struct cache_read_response *
+get_cache_read_response(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_read_response);
+ assert(element->type == CET_READ_RESPONSE);
+ TRACE_OUT(get_cache_read_response);
+ return (&element->c_read_response);
+}
+
+void
+init_cache_transform_request(struct cache_transform_request *transform_request)
+{
+
+ TRACE_IN(init_cache_transform_request);
+ memset(transform_request, 0, sizeof(struct cache_transform_request));
+ TRACE_OUT(init_cache_transform_request);
+}
+
+void
+finalize_cache_transform_request(
+ struct cache_transform_request *transform_request)
+{
+
+ TRACE_IN(finalize_cache_transform_request);
+ free(transform_request->entry);
+ TRACE_OUT(finalize_cache_transform_request);
+}
+
+struct cache_transform_request *
+get_cache_transform_request(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_transform_request);
+ assert(element->type == CET_TRANSFORM_REQUEST);
+ TRACE_OUT(get_cache_transform_request);
+ return (&element->c_transform_request);
+}
+
+void
+init_cache_transform_response(
+ struct cache_transform_response *transform_response)
+{
+
+ TRACE_IN(init_cache_transform_request);
+ memset(transform_response, 0, sizeof(struct cache_transform_response));
+ TRACE_OUT(init_cache_transform_request);
+}
+
+void
+finalize_cache_transform_response(
+ struct cache_transform_response *transform_response)
+{
+
+ TRACE_IN(finalize_cache_transform_response);
+ TRACE_OUT(finalize_cache_transform_response);
+}
+
+struct cache_transform_response *
+get_cache_transform_response(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_transform_response);
+ assert(element->type == CET_TRANSFORM_RESPONSE);
+ TRACE_OUT(get_cache_transform_response);
+ return (&element->c_transform_response);
+}
+
+
+void
+init_cache_mp_write_session_request(
+ struct cache_mp_write_session_request *mp_ws_request)
+{
+
+ TRACE_IN(init_cache_mp_write_session_request);
+ memset(mp_ws_request, 0,
+ sizeof(struct cache_mp_write_session_request));
+ TRACE_OUT(init_cache_mp_write_session_request);
+}
+
+void
+finalize_cache_mp_write_session_request(
+ struct cache_mp_write_session_request *mp_ws_request)
+{
+
+ TRACE_IN(finalize_cache_mp_write_session_request);
+ free(mp_ws_request->entry);
+ TRACE_OUT(finalize_cache_mp_write_session_request);
+}
+
+struct cache_mp_write_session_request *
+get_cache_mp_write_session_request(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_mp_write_session_request);
+ assert(element->type == CET_MP_WRITE_SESSION_REQUEST);
+ TRACE_OUT(get_cache_mp_write_session_request);
+ return (&element->c_mp_ws_request);
+}
+
+void
+init_cache_mp_write_session_response(
+ struct cache_mp_write_session_response *mp_ws_response)
+{
+
+ TRACE_IN(init_cache_mp_write_session_response);
+ memset(mp_ws_response, 0,
+ sizeof(struct cache_mp_write_session_response));
+ TRACE_OUT(init_cache_mp_write_session_response);
+}
+
+void
+finalize_cache_mp_write_session_response(
+ struct cache_mp_write_session_response *mp_ws_response)
+{
+
+ TRACE_IN(finalize_cache_mp_write_session_response);
+ TRACE_OUT(finalize_cache_mp_write_session_response);
+}
+
+struct cache_mp_write_session_response *
+get_cache_mp_write_session_response(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_mp_write_session_response);
+ assert(element->type == CET_MP_WRITE_SESSION_RESPONSE);
+ TRACE_OUT(get_cache_mp_write_session_response);
+ return (&element->c_mp_ws_response);
+}
+
+void
+init_cache_mp_write_session_write_request(
+ struct cache_mp_write_session_write_request *mp_ws_write_request)
+{
+
+ TRACE_IN(init_cache_mp_write_session_write_request);
+ memset(mp_ws_write_request, 0,
+ sizeof(struct cache_mp_write_session_write_request));
+ TRACE_OUT(init_cache_mp_write_session_write_response);
+}
+
+void
+finalize_cache_mp_write_session_write_request(
+ struct cache_mp_write_session_write_request *mp_ws_write_request)
+{
+
+ TRACE_IN(finalize_cache_mp_write_session_write_request);
+ free(mp_ws_write_request->data);
+ TRACE_OUT(finalize_cache_mp_write_session_write_request);
+}
+
+struct cache_mp_write_session_write_request *
+get_cache_mp_write_session_write_request(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_mp_write_session_write_request);
+ assert(element->type == CET_MP_WRITE_SESSION_WRITE_REQUEST);
+ TRACE_OUT(get_cache_mp_write_session_write_request);
+ return (&element->c_mp_ws_write_request);
+}
+
+void
+init_cache_mp_write_session_write_response(
+ struct cache_mp_write_session_write_response *mp_ws_write_response)
+{
+
+ TRACE_IN(init_cache_mp_write_session_write_response);
+ memset(mp_ws_write_response, 0,
+ sizeof(struct cache_mp_write_session_write_response));
+ TRACE_OUT(init_cache_mp_write_session_write_response);
+}
+
+void
+finalize_cache_mp_write_session_write_response(
+ struct cache_mp_write_session_write_response *mp_ws_write_response)
+{
+
+ TRACE_IN(finalize_cache_mp_write_session_write_response);
+ TRACE_OUT(finalize_cache_mp_write_session_write_response);
+}
+
+struct cache_mp_write_session_write_response *
+get_cache_mp_write_session_write_response(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_mp_write_session_write_response);
+ assert(element->type == CET_MP_WRITE_SESSION_WRITE_RESPONSE);
+ TRACE_OUT(get_cache_mp_write_session_write_response);
+ return (&element->c_mp_ws_write_response);
+}
+
+void
+init_cache_mp_read_session_request(
+ struct cache_mp_read_session_request *mp_rs_request)
+{
+
+ TRACE_IN(init_cache_mp_read_session_request);
+ memset(mp_rs_request, 0, sizeof(struct cache_mp_read_session_request));
+ TRACE_OUT(init_cache_mp_read_session_request);
+}
+
+void
+finalize_cache_mp_read_session_request(
+ struct cache_mp_read_session_request *mp_rs_request)
+{
+
+ TRACE_IN(finalize_cache_mp_read_session_request);
+ free(mp_rs_request->entry);
+ TRACE_OUT(finalize_cache_mp_read_session_request);
+}
+
+struct cache_mp_read_session_request *
+get_cache_mp_read_session_request(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_mp_read_session_request);
+ assert(element->type == CET_MP_READ_SESSION_REQUEST);
+ TRACE_OUT(get_cache_mp_read_session_request);
+ return (&element->c_mp_rs_request);
+}
+
+void
+init_cache_mp_read_session_response(
+ struct cache_mp_read_session_response *mp_rs_response)
+{
+
+ TRACE_IN(init_cache_mp_read_session_response);
+ memset(mp_rs_response, 0,
+ sizeof(struct cache_mp_read_session_response));
+ TRACE_OUT(init_cache_mp_read_session_response);
+}
+
+void
+finalize_cache_mp_read_session_response(
+ struct cache_mp_read_session_response *mp_rs_response)
+{
+
+ TRACE_IN(finalize_cache_mp_read_session_response);
+ TRACE_OUT(finalize_cache_mp_read_session_response);
+}
+
+struct cache_mp_read_session_response *
+get_cache_mp_read_session_response(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_mp_read_session_response);
+ assert(element->type == CET_MP_READ_SESSION_RESPONSE);
+ TRACE_OUT(get_cache_mp_read_session_response);
+ return (&element->c_mp_rs_response);
+}
+
+void
+init_cache_mp_read_session_read_response(
+ struct cache_mp_read_session_read_response *mp_ws_read_response)
+{
+
+ TRACE_IN(init_cache_mp_read_session_read_response);
+ memset(mp_ws_read_response, 0,
+ sizeof(struct cache_mp_read_session_read_response));
+ TRACE_OUT(init_cache_mp_read_session_read_response);
+}
+
+void
+finalize_cache_mp_read_session_read_response(
+ struct cache_mp_read_session_read_response *mp_rs_read_response)
+{
+
+ TRACE_IN(finalize_cache_mp_read_session_read_response);
+ free(mp_rs_read_response->data);
+ TRACE_OUT(finalize_cache_mp_read_session_read_response);
+}
+
+struct cache_mp_read_session_read_response *
+get_cache_mp_read_session_read_response(struct comm_element *element)
+{
+
+ TRACE_IN(get_cache_mp_read_session_read_response);
+ assert(element->type == CET_MP_READ_SESSION_READ_RESPONSE);
+ TRACE_OUT(get_cache_mp_read_session_read_response);
+ return (&element->c_mp_rs_read_response);
+}
diff --git a/usr.sbin/nscd/protocol.h b/usr.sbin/nscd/protocol.h
new file mode 100644
index 0000000..6894d3c
--- /dev/null
+++ b/usr.sbin/nscd/protocol.h
@@ -0,0 +1,265 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 __NSCD_PROTOCOL_H__
+#define __NSCD_PROTOCOL_H__
+
+#include <stdlib.h>
+
+/* maximum buffer size to receive - larger buffers are not allowed */
+#define MAX_BUFFER_SIZE (1 << 20)
+
+/* buffer size correctness checking routine */
+#define BUFSIZE_CORRECT(x) (((x) > 0) && ((x) < MAX_BUFFER_SIZE))
+#define BUFSIZE_INVALID(x) (!BUFSIZE_CORRECT(x))
+
+/* structures below represent the data that are sent/received by the daemon */
+struct cache_write_request
+{
+ char *entry;
+ char *cache_key;
+ char *data;
+
+ size_t entry_length;
+ size_t cache_key_size;
+ size_t data_size;
+};
+
+struct cache_write_response
+{
+ int error_code;
+};
+
+struct cache_read_request
+{
+ char *entry;
+ char *cache_key;
+
+ size_t entry_length;
+ size_t cache_key_size;
+};
+
+struct cache_read_response
+{
+ char *data; // ignored if error_code is not 0
+ size_t data_size; // ignored if error_code is not 0
+
+ int error_code;
+};
+
+enum transformation_type {
+ TT_USER = 0, // tranform only the entries of the caller
+ TT_ALL = 1 // transform all entries
+};
+
+struct cache_transform_request
+{
+ char *entry; // ignored if entry_length is 0
+ size_t entry_length;
+
+ int transformation_type;
+};
+
+struct cache_transform_response
+{
+ int error_code;
+};
+
+struct cache_mp_write_session_request {
+ char *entry;
+ size_t entry_length;
+};
+
+struct cache_mp_write_session_response {
+ int error_code;
+};
+
+struct cache_mp_write_session_write_request {
+ char *data;
+ size_t data_size;
+};
+
+struct cache_mp_write_session_write_response {
+ int error_code;
+};
+
+struct cache_mp_read_session_request {
+ char *entry;
+ size_t entry_length;
+};
+
+struct cache_mp_read_session_response {
+ int error_code;
+};
+
+struct cache_mp_read_session_read_response {
+ char *data;
+ size_t data_size;
+
+ int error_code;
+};
+
+
+enum comm_element_t {
+ CET_UNDEFINED = 0,
+ CET_WRITE_REQUEST = 1,
+ CET_WRITE_RESPONSE = 2,
+ CET_READ_REQUEST = 3,
+ CET_READ_RESPONSE = 4,
+ CET_TRANSFORM_REQUEST = 5,
+ CET_TRANSFORM_RESPONSE = 6,
+ CET_MP_WRITE_SESSION_REQUEST = 7,
+ CET_MP_WRITE_SESSION_RESPONSE = 8,
+ CET_MP_WRITE_SESSION_WRITE_REQUEST = 9,
+ CET_MP_WRITE_SESSION_WRITE_RESPONSE = 10,
+ CET_MP_WRITE_SESSION_CLOSE_NOTIFICATION = 11,
+ CET_MP_WRITE_SESSION_ABANDON_NOTIFICATION = 12,
+ CET_MP_READ_SESSION_REQUEST = 13,
+ CET_MP_READ_SESSION_RESPONSE = 14,
+ CET_MP_READ_SESSION_READ_REQUEST = 15,
+ CET_MP_READ_SESSION_READ_RESPONSE = 16,
+ CET_MP_READ_SESSION_CLOSE_NOTIFICATION = 17,
+ CET_MAX = 18
+};
+
+/*
+ * The comm_element is used as the holder of any known (defined above) data
+ * type that is to be sent/received.
+ */
+struct comm_element
+{
+ union {
+ struct cache_write_request c_write_request;
+ struct cache_write_response c_write_response;
+ struct cache_read_request c_read_request;
+ struct cache_read_response c_read_response;
+ struct cache_transform_request c_transform_request;
+ struct cache_transform_response c_transform_response;
+
+ struct cache_mp_write_session_request c_mp_ws_request;
+ struct cache_mp_write_session_response c_mp_ws_response;
+ struct cache_mp_write_session_write_request c_mp_ws_write_request;
+ struct cache_mp_write_session_write_response c_mp_ws_write_response;
+
+ struct cache_mp_read_session_request c_mp_rs_request;
+ struct cache_mp_read_session_response c_mp_rs_response;
+ struct cache_mp_read_session_read_response c_mp_rs_read_response;
+ };
+ enum comm_element_t type;
+};
+
+extern void init_comm_element(struct comm_element *, enum comm_element_t type);
+extern void finalize_comm_element(struct comm_element *);
+
+/*
+ * For each type of data, there is three functions (init/finalize/get), that
+ * used with comm_element structure
+ */
+extern void init_cache_write_request(struct cache_write_request *);
+extern void finalize_cache_write_request(struct cache_write_request *);
+extern struct cache_write_request *get_cache_write_request(
+ struct comm_element *);
+
+extern void init_cache_write_response(struct cache_write_response *);
+extern void finalize_cache_write_response(struct cache_write_response *);
+extern struct cache_write_response *get_cache_write_response(
+ struct comm_element *);
+
+extern void init_cache_read_request(struct cache_read_request *);
+extern void finalize_cache_read_request(struct cache_read_request *);
+extern struct cache_read_request *get_cache_read_request(
+ struct comm_element *);
+
+extern void init_cache_read_response(struct cache_read_response *);
+extern void finalize_cache_read_response(struct cache_read_response *);
+extern struct cache_read_response *get_cache_read_response(
+ struct comm_element *);
+
+extern void init_cache_transform_request(struct cache_transform_request *);
+extern void finalize_cache_transform_request(struct cache_transform_request *);
+extern struct cache_transform_request *get_cache_transform_request(
+ struct comm_element *);
+
+extern void init_cache_transform_response(struct cache_transform_response *);
+extern void finalize_cache_transform_response(
+ struct cache_transform_response *);
+extern struct cache_transform_response *get_cache_transform_response(
+ struct comm_element *);
+
+extern void init_cache_mp_write_session_request(
+ struct cache_mp_write_session_request *);
+extern void finalize_cache_mp_write_session_request(
+ struct cache_mp_write_session_request *);
+extern struct cache_mp_write_session_request *
+ get_cache_mp_write_session_request(
+ struct comm_element *);
+
+extern void init_cache_mp_write_session_response(
+ struct cache_mp_write_session_response *);
+extern void finalize_cache_mp_write_session_response(
+ struct cache_mp_write_session_response *);
+extern struct cache_mp_write_session_response *
+ get_cache_mp_write_session_response(struct comm_element *);
+
+extern void init_cache_mp_write_session_write_request(
+ struct cache_mp_write_session_write_request *);
+extern void finalize_cache_mp_write_session_write_request(
+ struct cache_mp_write_session_write_request *);
+extern struct cache_mp_write_session_write_request *
+ get_cache_mp_write_session_write_request(struct comm_element *);
+
+extern void init_cache_mp_write_session_write_response(
+ struct cache_mp_write_session_write_response *);
+extern void finalize_cache_mp_write_session_write_response(
+ struct cache_mp_write_session_write_response *);
+extern struct cache_mp_write_session_write_response *
+ get_cache_mp_write_session_write_response(struct comm_element *);
+
+extern void init_cache_mp_read_session_request(
+ struct cache_mp_read_session_request *);
+extern void finalize_cache_mp_read_session_request(
+ struct cache_mp_read_session_request *);
+extern struct cache_mp_read_session_request *get_cache_mp_read_session_request(
+ struct comm_element *);
+
+extern void init_cache_mp_read_session_response(
+ struct cache_mp_read_session_response *);
+extern void finalize_cache_mp_read_session_response(
+ struct cache_mp_read_session_response *);
+extern struct cache_mp_read_session_response *
+ get_cache_mp_read_session_response(
+ struct comm_element *);
+
+extern void init_cache_mp_read_session_read_response(
+ struct cache_mp_read_session_read_response *);
+extern void finalize_cache_mp_read_session_read_response(
+ struct cache_mp_read_session_read_response *);
+extern struct cache_mp_read_session_read_response *
+ get_cache_mp_read_session_read_response(struct comm_element *);
+
+#endif
diff --git a/usr.sbin/nscd/query.c b/usr.sbin/nscd/query.c
new file mode 100644
index 0000000..4b75093
--- /dev/null
+++ b/usr.sbin/nscd/query.c
@@ -0,0 +1,1268 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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/socket.h>
+#include <sys/time.h>
+#include <sys/event.h>
+#include <assert.h>
+#include <errno.h>
+#include <nsswitch.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "config.h"
+#include "debug.h"
+#include "query.h"
+#include "log.h"
+#include "mp_ws_query.h"
+#include "mp_rs_query.h"
+#include "singletons.h"
+
+static const char negative_data[1] = { 0 };
+
+extern void get_time_func(struct timeval *);
+
+static void clear_config_entry(struct configuration_entry *);
+static void clear_config_entry_part(struct configuration_entry *,
+ const char *, size_t);
+
+static int on_query_startup(struct query_state *);
+static void on_query_destroy(struct query_state *);
+
+static int on_read_request_read1(struct query_state *);
+static int on_read_request_read2(struct query_state *);
+static int on_read_request_process(struct query_state *);
+static int on_read_response_write1(struct query_state *);
+static int on_read_response_write2(struct query_state *);
+
+static int on_rw_mapper(struct query_state *);
+
+static int on_transform_request_read1(struct query_state *);
+static int on_transform_request_read2(struct query_state *);
+static int on_transform_request_process(struct query_state *);
+static int on_transform_response_write1(struct query_state *);
+
+static int on_write_request_read1(struct query_state *);
+static int on_write_request_read2(struct query_state *);
+static int on_negative_write_request_process(struct query_state *);
+static int on_write_request_process(struct query_state *);
+static int on_write_response_write1(struct query_state *);
+
+/*
+ * Clears the specified configuration entry (clears the cache for positive and
+ * and negative entries) and also for all multipart entries.
+ */
+static void
+clear_config_entry(struct configuration_entry *config_entry)
+{
+ size_t i;
+
+ TRACE_IN(clear_config_entry);
+ configuration_lock_entry(config_entry, CELT_POSITIVE);
+ if (config_entry->positive_cache_entry != NULL)
+ transform_cache_entry(
+ config_entry->positive_cache_entry,
+ CTT_CLEAR);
+ configuration_unlock_entry(config_entry, CELT_POSITIVE);
+
+ configuration_lock_entry(config_entry, CELT_NEGATIVE);
+ if (config_entry->negative_cache_entry != NULL)
+ transform_cache_entry(
+ config_entry->negative_cache_entry,
+ CTT_CLEAR);
+ configuration_unlock_entry(config_entry, CELT_NEGATIVE);
+
+ configuration_lock_entry(config_entry, CELT_MULTIPART);
+ for (i = 0; i < config_entry->mp_cache_entries_size; ++i)
+ transform_cache_entry(
+ config_entry->mp_cache_entries[i],
+ CTT_CLEAR);
+ configuration_unlock_entry(config_entry, CELT_MULTIPART);
+
+ TRACE_OUT(clear_config_entry);
+}
+
+/*
+ * Clears the specified configuration entry by deleting only the elements,
+ * that are owned by the user with specified eid_str.
+ */
+static void
+clear_config_entry_part(struct configuration_entry *config_entry,
+ const char *eid_str, size_t eid_str_length)
+{
+ cache_entry *start, *finish, *mp_entry;
+ TRACE_IN(clear_config_entry_part);
+ configuration_lock_entry(config_entry, CELT_POSITIVE);
+ if (config_entry->positive_cache_entry != NULL)
+ transform_cache_entry_part(
+ config_entry->positive_cache_entry,
+ CTT_CLEAR, eid_str, eid_str_length, KPPT_LEFT);
+ configuration_unlock_entry(config_entry, CELT_POSITIVE);
+
+ configuration_lock_entry(config_entry, CELT_NEGATIVE);
+ if (config_entry->negative_cache_entry != NULL)
+ transform_cache_entry_part(
+ config_entry->negative_cache_entry,
+ CTT_CLEAR, eid_str, eid_str_length, KPPT_LEFT);
+ configuration_unlock_entry(config_entry, CELT_NEGATIVE);
+
+ configuration_lock_entry(config_entry, CELT_MULTIPART);
+ if (configuration_entry_find_mp_cache_entries(config_entry,
+ eid_str, &start, &finish) == 0) {
+ for (mp_entry = start; mp_entry != finish; ++mp_entry)
+ transform_cache_entry(*mp_entry, CTT_CLEAR);
+ }
+ configuration_unlock_entry(config_entry, CELT_MULTIPART);
+
+ TRACE_OUT(clear_config_entry_part);
+}
+
+/*
+ * This function is assigned to the query_state structue on its creation.
+ * It's main purpose is to receive credentials from the client.
+ */
+static int
+on_query_startup(struct query_state *qstate)
+{
+ struct msghdr cred_hdr;
+ struct iovec iov;
+ struct cmsgcred *cred;
+ int elem_type;
+
+ struct {
+ struct cmsghdr hdr;
+ char cred[CMSG_SPACE(sizeof(struct cmsgcred))];
+ } cmsg;
+
+ TRACE_IN(on_query_startup);
+ assert(qstate != NULL);
+
+ memset(&cred_hdr, 0, sizeof(struct msghdr));
+ cred_hdr.msg_iov = &iov;
+ cred_hdr.msg_iovlen = 1;
+ cred_hdr.msg_control = (caddr_t)&cmsg;
+ cred_hdr.msg_controllen = CMSG_LEN(sizeof(struct cmsgcred));
+
+ memset(&iov, 0, sizeof(struct iovec));
+ iov.iov_base = &elem_type;
+ iov.iov_len = sizeof(int);
+
+ if (recvmsg(qstate->sockfd, &cred_hdr, 0) == -1) {
+ TRACE_OUT(on_query_startup);
+ return (-1);
+ }
+
+ if (cmsg.hdr.cmsg_len < CMSG_LEN(sizeof(struct cmsgcred))
+ || cmsg.hdr.cmsg_level != SOL_SOCKET
+ || cmsg.hdr.cmsg_type != SCM_CREDS) {
+ TRACE_OUT(on_query_startup);
+ return (-1);
+ }
+
+ cred = (struct cmsgcred *)CMSG_DATA(&cmsg);
+ qstate->uid = cred->cmcred_uid;
+ qstate->gid = cred->cmcred_gid;
+
+#if defined(NS_NSCD_EID_CHECKING) || defined(NS_STRICT_NSCD_EID_CHECKING)
+/*
+ * This check is probably a bit redundant - per-user cache is always separated
+ * by the euid/egid pair
+ */
+ if (check_query_eids(qstate) != 0) {
+#ifdef NS_STRICT_NSCD_EID_CHECKING
+ TRACE_OUT(on_query_startup);
+ return (-1);
+#else
+ if ((elem_type != CET_READ_REQUEST) &&
+ (elem_type != CET_MP_READ_SESSION_REQUEST) &&
+ (elem_type != CET_WRITE_REQUEST) &&
+ (elem_type != CET_MP_WRITE_SESSION_REQUEST)) {
+ TRACE_OUT(on_query_startup);
+ return (-1);
+ }
+#endif
+ }
+#endif
+
+ switch (elem_type) {
+ case CET_WRITE_REQUEST:
+ qstate->process_func = on_write_request_read1;
+ break;
+ case CET_READ_REQUEST:
+ qstate->process_func = on_read_request_read1;
+ break;
+ case CET_TRANSFORM_REQUEST:
+ qstate->process_func = on_transform_request_read1;
+ break;
+ case CET_MP_WRITE_SESSION_REQUEST:
+ qstate->process_func = on_mp_write_session_request_read1;
+ break;
+ case CET_MP_READ_SESSION_REQUEST:
+ qstate->process_func = on_mp_read_session_request_read1;
+ break;
+ default:
+ TRACE_OUT(on_query_startup);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = 0;
+ TRACE_OUT(on_query_startup);
+ return (0);
+}
+
+/*
+ * on_rw_mapper is used to process multiple read/write requests during
+ * one connection session. It's never called in the beginning (on query_state
+ * creation) as it does not process the multipart requests and does not
+ * receive credentials
+ */
+static int
+on_rw_mapper(struct query_state *qstate)
+{
+ ssize_t result;
+ int elem_type;
+
+ TRACE_IN(on_rw_mapper);
+ if (qstate->kevent_watermark == 0) {
+ qstate->kevent_watermark = sizeof(int);
+ } else {
+ result = qstate->read_func(qstate, &elem_type, sizeof(int));
+ if (result != sizeof(int)) {
+ TRACE_OUT(on_rw_mapper);
+ return (-1);
+ }
+
+ switch (elem_type) {
+ case CET_WRITE_REQUEST:
+ qstate->kevent_watermark = sizeof(size_t);
+ qstate->process_func = on_write_request_read1;
+ break;
+ case CET_READ_REQUEST:
+ qstate->kevent_watermark = sizeof(size_t);
+ qstate->process_func = on_read_request_read1;
+ break;
+ default:
+ TRACE_OUT(on_rw_mapper);
+ return (-1);
+ break;
+ }
+ }
+ TRACE_OUT(on_rw_mapper);
+ return (0);
+}
+
+/*
+ * The default query_destroy function
+ */
+static void
+on_query_destroy(struct query_state *qstate)
+{
+
+ TRACE_IN(on_query_destroy);
+ finalize_comm_element(&qstate->response);
+ finalize_comm_element(&qstate->request);
+ TRACE_OUT(on_query_destroy);
+}
+
+/*
+ * The functions below are used to process write requests.
+ * - on_write_request_read1 and on_write_request_read2 read the request itself
+ * - on_write_request_process processes it (if the client requests to
+ * cache the negative result, the on_negative_write_request_process is used)
+ * - on_write_response_write1 sends the response
+ */
+static int
+on_write_request_read1(struct query_state *qstate)
+{
+ struct cache_write_request *write_request;
+ ssize_t result;
+
+ TRACE_IN(on_write_request_read1);
+ if (qstate->kevent_watermark == 0)
+ qstate->kevent_watermark = sizeof(size_t) * 3;
+ else {
+ init_comm_element(&qstate->request, CET_WRITE_REQUEST);
+ write_request = get_cache_write_request(&qstate->request);
+
+ result = qstate->read_func(qstate, &write_request->entry_length,
+ sizeof(size_t));
+ result += qstate->read_func(qstate,
+ &write_request->cache_key_size, sizeof(size_t));
+ result += qstate->read_func(qstate,
+ &write_request->data_size, sizeof(size_t));
+
+ if (result != sizeof(size_t) * 3) {
+ TRACE_OUT(on_write_request_read1);
+ return (-1);
+ }
+
+ if (BUFSIZE_INVALID(write_request->entry_length) ||
+ BUFSIZE_INVALID(write_request->cache_key_size) ||
+ (BUFSIZE_INVALID(write_request->data_size) &&
+ (write_request->data_size != 0))) {
+ TRACE_OUT(on_write_request_read1);
+ return (-1);
+ }
+
+ write_request->entry = (char *)calloc(1,
+ write_request->entry_length + 1);
+ assert(write_request->entry != NULL);
+
+ write_request->cache_key = (char *)calloc(1,
+ write_request->cache_key_size +
+ qstate->eid_str_length);
+ assert(write_request->cache_key != NULL);
+ memcpy(write_request->cache_key, qstate->eid_str,
+ qstate->eid_str_length);
+
+ if (write_request->data_size != 0) {
+ write_request->data = (char *)calloc(1,
+ write_request->data_size);
+ assert(write_request->data != NULL);
+ }
+
+ qstate->kevent_watermark = write_request->entry_length +
+ write_request->cache_key_size +
+ write_request->data_size;
+ qstate->process_func = on_write_request_read2;
+ }
+
+ TRACE_OUT(on_write_request_read1);
+ return (0);
+}
+
+static int
+on_write_request_read2(struct query_state *qstate)
+{
+ struct cache_write_request *write_request;
+ ssize_t result;
+
+ TRACE_IN(on_write_request_read2);
+ write_request = get_cache_write_request(&qstate->request);
+
+ result = qstate->read_func(qstate, write_request->entry,
+ write_request->entry_length);
+ result += qstate->read_func(qstate, write_request->cache_key +
+ qstate->eid_str_length, write_request->cache_key_size);
+ if (write_request->data_size != 0)
+ result += qstate->read_func(qstate, write_request->data,
+ write_request->data_size);
+
+ if (result != qstate->kevent_watermark) {
+ TRACE_OUT(on_write_request_read2);
+ return (-1);
+ }
+ write_request->cache_key_size += qstate->eid_str_length;
+
+ qstate->kevent_watermark = 0;
+ if (write_request->data_size != 0)
+ qstate->process_func = on_write_request_process;
+ else
+ qstate->process_func = on_negative_write_request_process;
+ TRACE_OUT(on_write_request_read2);
+ return (0);
+}
+
+static int
+on_write_request_process(struct query_state *qstate)
+{
+ struct cache_write_request *write_request;
+ struct cache_write_response *write_response;
+ cache_entry c_entry;
+
+ TRACE_IN(on_write_request_process);
+ init_comm_element(&qstate->response, CET_WRITE_RESPONSE);
+ write_response = get_cache_write_response(&qstate->response);
+ write_request = get_cache_write_request(&qstate->request);
+
+ qstate->config_entry = configuration_find_entry(
+ s_configuration, write_request->entry);
+
+ if (qstate->config_entry == NULL) {
+ write_response->error_code = ENOENT;
+
+ LOG_ERR_2("write_request", "can't find configuration"
+ " entry '%s'. aborting request", write_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->enabled == 0) {
+ write_response->error_code = EACCES;
+
+ LOG_ERR_2("write_request",
+ "configuration entry '%s' is disabled",
+ write_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->perform_actual_lookups != 0) {
+ write_response->error_code = EOPNOTSUPP;
+
+ LOG_ERR_2("write_request",
+ "entry '%s' performs lookups by itself: "
+ "can't write to it", write_request->entry);
+ goto fin;
+ }
+
+ configuration_lock_rdlock(s_configuration);
+ c_entry = find_cache_entry(s_cache,
+ qstate->config_entry->positive_cache_params.entry_name);
+ configuration_unlock(s_configuration);
+ if (c_entry != NULL) {
+ configuration_lock_entry(qstate->config_entry, CELT_POSITIVE);
+ qstate->config_entry->positive_cache_entry = c_entry;
+ write_response->error_code = cache_write(c_entry,
+ write_request->cache_key,
+ write_request->cache_key_size,
+ write_request->data,
+ write_request->data_size);
+ configuration_unlock_entry(qstate->config_entry, CELT_POSITIVE);
+
+ if ((qstate->config_entry->common_query_timeout.tv_sec != 0) ||
+ (qstate->config_entry->common_query_timeout.tv_usec != 0))
+ memcpy(&qstate->timeout,
+ &qstate->config_entry->common_query_timeout,
+ sizeof(struct timeval));
+
+ } else
+ write_response->error_code = -1;
+
+fin:
+ qstate->kevent_filter = EVFILT_WRITE;
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_write_response_write1;
+
+ TRACE_OUT(on_write_request_process);
+ return (0);
+}
+
+static int
+on_negative_write_request_process(struct query_state *qstate)
+{
+ struct cache_write_request *write_request;
+ struct cache_write_response *write_response;
+ cache_entry c_entry;
+
+ TRACE_IN(on_negative_write_request_process);
+ init_comm_element(&qstate->response, CET_WRITE_RESPONSE);
+ write_response = get_cache_write_response(&qstate->response);
+ write_request = get_cache_write_request(&qstate->request);
+
+ qstate->config_entry = configuration_find_entry (
+ s_configuration, write_request->entry);
+
+ if (qstate->config_entry == NULL) {
+ write_response->error_code = ENOENT;
+
+ LOG_ERR_2("negative_write_request",
+ "can't find configuration"
+ " entry '%s'. aborting request", write_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->enabled == 0) {
+ write_response->error_code = EACCES;
+
+ LOG_ERR_2("negative_write_request",
+ "configuration entry '%s' is disabled",
+ write_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->perform_actual_lookups != 0) {
+ write_response->error_code = EOPNOTSUPP;
+
+ LOG_ERR_2("negative_write_request",
+ "entry '%s' performs lookups by itself: "
+ "can't write to it", write_request->entry);
+ goto fin;
+ } else {
+#ifdef NS_NSCD_EID_CHECKING
+ if (check_query_eids(qstate) != 0) {
+ write_response->error_code = EPERM;
+ goto fin;
+ }
+#endif
+ }
+
+ configuration_lock_rdlock(s_configuration);
+ c_entry = find_cache_entry(s_cache,
+ qstate->config_entry->negative_cache_params.entry_name);
+ configuration_unlock(s_configuration);
+ if (c_entry != NULL) {
+ configuration_lock_entry(qstate->config_entry, CELT_NEGATIVE);
+ qstate->config_entry->negative_cache_entry = c_entry;
+ write_response->error_code = cache_write(c_entry,
+ write_request->cache_key,
+ write_request->cache_key_size,
+ negative_data,
+ sizeof(negative_data));
+ configuration_unlock_entry(qstate->config_entry, CELT_NEGATIVE);
+
+ if ((qstate->config_entry->common_query_timeout.tv_sec != 0) ||
+ (qstate->config_entry->common_query_timeout.tv_usec != 0))
+ memcpy(&qstate->timeout,
+ &qstate->config_entry->common_query_timeout,
+ sizeof(struct timeval));
+ } else
+ write_response->error_code = -1;
+
+fin:
+ qstate->kevent_filter = EVFILT_WRITE;
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_write_response_write1;
+
+ TRACE_OUT(on_negative_write_request_process);
+ return (0);
+}
+
+static int
+on_write_response_write1(struct query_state *qstate)
+{
+ struct cache_write_response *write_response;
+ ssize_t result;
+
+ TRACE_IN(on_write_response_write1);
+ write_response = get_cache_write_response(&qstate->response);
+ result = qstate->write_func(qstate, &write_response->error_code,
+ sizeof(int));
+ if (result != sizeof(int)) {
+ TRACE_OUT(on_write_response_write1);
+ return (-1);
+ }
+
+ finalize_comm_element(&qstate->request);
+ finalize_comm_element(&qstate->response);
+
+ qstate->kevent_watermark = sizeof(int);
+ qstate->kevent_filter = EVFILT_READ;
+ qstate->process_func = on_rw_mapper;
+
+ TRACE_OUT(on_write_response_write1);
+ return (0);
+}
+
+/*
+ * The functions below are used to process read requests.
+ * - on_read_request_read1 and on_read_request_read2 read the request itself
+ * - on_read_request_process processes it
+ * - on_read_response_write1 and on_read_response_write2 send the response
+ */
+static int
+on_read_request_read1(struct query_state *qstate)
+{
+ struct cache_read_request *read_request;
+ ssize_t result;
+
+ TRACE_IN(on_read_request_read1);
+ if (qstate->kevent_watermark == 0)
+ qstate->kevent_watermark = sizeof(size_t) * 2;
+ else {
+ init_comm_element(&qstate->request, CET_READ_REQUEST);
+ read_request = get_cache_read_request(&qstate->request);
+
+ result = qstate->read_func(qstate,
+ &read_request->entry_length, sizeof(size_t));
+ result += qstate->read_func(qstate,
+ &read_request->cache_key_size, sizeof(size_t));
+
+ if (result != sizeof(size_t) * 2) {
+ TRACE_OUT(on_read_request_read1);
+ return (-1);
+ }
+
+ if (BUFSIZE_INVALID(read_request->entry_length) ||
+ BUFSIZE_INVALID(read_request->cache_key_size)) {
+ TRACE_OUT(on_read_request_read1);
+ return (-1);
+ }
+
+ read_request->entry = (char *)calloc(1,
+ read_request->entry_length + 1);
+ assert(read_request->entry != NULL);
+
+ read_request->cache_key = (char *)calloc(1,
+ read_request->cache_key_size +
+ qstate->eid_str_length);
+ assert(read_request->cache_key != NULL);
+ memcpy(read_request->cache_key, qstate->eid_str,
+ qstate->eid_str_length);
+
+ qstate->kevent_watermark = read_request->entry_length +
+ read_request->cache_key_size;
+ qstate->process_func = on_read_request_read2;
+ }
+
+ TRACE_OUT(on_read_request_read1);
+ return (0);
+}
+
+static int
+on_read_request_read2(struct query_state *qstate)
+{
+ struct cache_read_request *read_request;
+ ssize_t result;
+
+ TRACE_IN(on_read_request_read2);
+ read_request = get_cache_read_request(&qstate->request);
+
+ result = qstate->read_func(qstate, read_request->entry,
+ read_request->entry_length);
+ result += qstate->read_func(qstate,
+ read_request->cache_key + qstate->eid_str_length,
+ read_request->cache_key_size);
+
+ if (result != qstate->kevent_watermark) {
+ TRACE_OUT(on_read_request_read2);
+ return (-1);
+ }
+ read_request->cache_key_size += qstate->eid_str_length;
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = on_read_request_process;
+
+ TRACE_OUT(on_read_request_read2);
+ return (0);
+}
+
+static int
+on_read_request_process(struct query_state *qstate)
+{
+ struct cache_read_request *read_request;
+ struct cache_read_response *read_response;
+ cache_entry c_entry, neg_c_entry;
+
+ struct agent *lookup_agent;
+ struct common_agent *c_agent;
+ int res;
+
+ TRACE_IN(on_read_request_process);
+ init_comm_element(&qstate->response, CET_READ_RESPONSE);
+ read_response = get_cache_read_response(&qstate->response);
+ read_request = get_cache_read_request(&qstate->request);
+
+ qstate->config_entry = configuration_find_entry(
+ s_configuration, read_request->entry);
+ if (qstate->config_entry == NULL) {
+ read_response->error_code = ENOENT;
+
+ LOG_ERR_2("read_request",
+ "can't find configuration "
+ "entry '%s'. aborting request", read_request->entry);
+ goto fin;
+ }
+
+ if (qstate->config_entry->enabled == 0) {
+ read_response->error_code = EACCES;
+
+ LOG_ERR_2("read_request",
+ "configuration entry '%s' is disabled",
+ read_request->entry);
+ goto fin;
+ }
+
+ /*
+ * if we perform lookups by ourselves, then we don't need to separate
+ * cache entries by euid and egid
+ */
+ if (qstate->config_entry->perform_actual_lookups != 0)
+ memset(read_request->cache_key, 0, qstate->eid_str_length);
+ else {
+#ifdef NS_NSCD_EID_CHECKING
+ if (check_query_eids(qstate) != 0) {
+ /* if the lookup is not self-performing, we check for clients euid/egid */
+ read_response->error_code = EPERM;
+ goto fin;
+ }
+#endif
+ }
+
+ configuration_lock_rdlock(s_configuration);
+ c_entry = find_cache_entry(s_cache,
+ qstate->config_entry->positive_cache_params.entry_name);
+ neg_c_entry = find_cache_entry(s_cache,
+ qstate->config_entry->negative_cache_params.entry_name);
+ configuration_unlock(s_configuration);
+ if ((c_entry != NULL) && (neg_c_entry != NULL)) {
+ configuration_lock_entry(qstate->config_entry, CELT_POSITIVE);
+ qstate->config_entry->positive_cache_entry = c_entry;
+ read_response->error_code = cache_read(c_entry,
+ read_request->cache_key,
+ read_request->cache_key_size, NULL,
+ &read_response->data_size);
+
+ if (read_response->error_code == -2) {
+ read_response->data = (char *)malloc(
+ read_response->data_size);
+ assert(read_response != NULL);
+ read_response->error_code = cache_read(c_entry,
+ read_request->cache_key,
+ read_request->cache_key_size,
+ read_response->data,
+ &read_response->data_size);
+ }
+ configuration_unlock_entry(qstate->config_entry, CELT_POSITIVE);
+
+ configuration_lock_entry(qstate->config_entry, CELT_NEGATIVE);
+ qstate->config_entry->negative_cache_entry = neg_c_entry;
+ if (read_response->error_code == -1) {
+ read_response->error_code = cache_read(neg_c_entry,
+ read_request->cache_key,
+ read_request->cache_key_size, NULL,
+ &read_response->data_size);
+
+ if (read_response->error_code == -2) {
+ read_response->error_code = 0;
+ read_response->data = NULL;
+ read_response->data_size = 0;
+ }
+ }
+ configuration_unlock_entry(qstate->config_entry, CELT_NEGATIVE);
+
+ if ((read_response->error_code == -1) &&
+ (qstate->config_entry->perform_actual_lookups != 0)) {
+ free(read_response->data);
+ read_response->data = NULL;
+ read_response->data_size = 0;
+
+ lookup_agent = find_agent(s_agent_table,
+ read_request->entry, COMMON_AGENT);
+
+ if ((lookup_agent != NULL) &&
+ (lookup_agent->type == COMMON_AGENT)) {
+ c_agent = (struct common_agent *)lookup_agent;
+ res = c_agent->lookup_func(
+ read_request->cache_key +
+ qstate->eid_str_length,
+ read_request->cache_key_size -
+ qstate->eid_str_length,
+ &read_response->data,
+ &read_response->data_size);
+
+ if (res == NS_SUCCESS) {
+ read_response->error_code = 0;
+ configuration_lock_entry(
+ qstate->config_entry,
+ CELT_POSITIVE);
+ cache_write(c_entry,
+ read_request->cache_key,
+ read_request->cache_key_size,
+ read_response->data,
+ read_response->data_size);
+ configuration_unlock_entry(
+ qstate->config_entry,
+ CELT_POSITIVE);
+ } else if ((res == NS_NOTFOUND) ||
+ (res == NS_RETURN)) {
+ configuration_lock_entry(
+ qstate->config_entry,
+ CELT_NEGATIVE);
+ cache_write(neg_c_entry,
+ read_request->cache_key,
+ read_request->cache_key_size,
+ negative_data,
+ sizeof(negative_data));
+ configuration_unlock_entry(
+ qstate->config_entry,
+ CELT_NEGATIVE);
+
+ read_response->error_code = 0;
+ read_response->data = NULL;
+ read_response->data_size = 0;
+ }
+ }
+ }
+
+ if ((qstate->config_entry->common_query_timeout.tv_sec != 0) ||
+ (qstate->config_entry->common_query_timeout.tv_usec != 0))
+ memcpy(&qstate->timeout,
+ &qstate->config_entry->common_query_timeout,
+ sizeof(struct timeval));
+ } else
+ read_response->error_code = -1;
+
+fin:
+ qstate->kevent_filter = EVFILT_WRITE;
+ if (read_response->error_code == 0)
+ qstate->kevent_watermark = sizeof(int) + sizeof(size_t);
+ else
+ qstate->kevent_watermark = sizeof(int);
+ qstate->process_func = on_read_response_write1;
+
+ TRACE_OUT(on_read_request_process);
+ return (0);
+}
+
+static int
+on_read_response_write1(struct query_state *qstate)
+{
+ struct cache_read_response *read_response;
+ ssize_t result;
+
+ TRACE_IN(on_read_response_write1);
+ read_response = get_cache_read_response(&qstate->response);
+
+ result = qstate->write_func(qstate, &read_response->error_code,
+ sizeof(int));
+
+ if (read_response->error_code == 0) {
+ result += qstate->write_func(qstate, &read_response->data_size,
+ sizeof(size_t));
+ if (result != qstate->kevent_watermark) {
+ TRACE_OUT(on_read_response_write1);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = read_response->data_size;
+ qstate->process_func = on_read_response_write2;
+ } else {
+ if (result != qstate->kevent_watermark) {
+ TRACE_OUT(on_read_response_write1);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ }
+
+ TRACE_OUT(on_read_response_write1);
+ return (0);
+}
+
+static int
+on_read_response_write2(struct query_state *qstate)
+{
+ struct cache_read_response *read_response;
+ ssize_t result;
+
+ TRACE_IN(on_read_response_write2);
+ read_response = get_cache_read_response(&qstate->response);
+ if (read_response->data_size > 0) {
+ result = qstate->write_func(qstate, read_response->data,
+ read_response->data_size);
+ if (result != qstate->kevent_watermark) {
+ TRACE_OUT(on_read_response_write2);
+ return (-1);
+ }
+ }
+
+ finalize_comm_element(&qstate->request);
+ finalize_comm_element(&qstate->response);
+
+ qstate->kevent_watermark = sizeof(int);
+ qstate->kevent_filter = EVFILT_READ;
+ qstate->process_func = on_rw_mapper;
+ TRACE_OUT(on_read_response_write2);
+ return (0);
+}
+
+/*
+ * The functions below are used to process write requests.
+ * - on_transform_request_read1 and on_transform_request_read2 read the
+ * request itself
+ * - on_transform_request_process processes it
+ * - on_transform_response_write1 sends the response
+ */
+static int
+on_transform_request_read1(struct query_state *qstate)
+{
+ struct cache_transform_request *transform_request;
+ ssize_t result;
+
+ TRACE_IN(on_transform_request_read1);
+ if (qstate->kevent_watermark == 0)
+ qstate->kevent_watermark = sizeof(size_t) + sizeof(int);
+ else {
+ init_comm_element(&qstate->request, CET_TRANSFORM_REQUEST);
+ transform_request =
+ get_cache_transform_request(&qstate->request);
+
+ result = qstate->read_func(qstate,
+ &transform_request->entry_length, sizeof(size_t));
+ result += qstate->read_func(qstate,
+ &transform_request->transformation_type, sizeof(int));
+
+ if (result != sizeof(size_t) + sizeof(int)) {
+ TRACE_OUT(on_transform_request_read1);
+ return (-1);
+ }
+
+ if ((transform_request->transformation_type != TT_USER) &&
+ (transform_request->transformation_type != TT_ALL)) {
+ TRACE_OUT(on_transform_request_read1);
+ return (-1);
+ }
+
+ if (transform_request->entry_length != 0) {
+ if (BUFSIZE_INVALID(transform_request->entry_length)) {
+ TRACE_OUT(on_transform_request_read1);
+ return (-1);
+ }
+
+ transform_request->entry = (char *)calloc(1,
+ transform_request->entry_length + 1);
+ assert(transform_request->entry != NULL);
+
+ qstate->process_func = on_transform_request_read2;
+ } else
+ qstate->process_func = on_transform_request_process;
+
+ qstate->kevent_watermark = transform_request->entry_length;
+ }
+
+ TRACE_OUT(on_transform_request_read1);
+ return (0);
+}
+
+static int
+on_transform_request_read2(struct query_state *qstate)
+{
+ struct cache_transform_request *transform_request;
+ ssize_t result;
+
+ TRACE_IN(on_transform_request_read2);
+ transform_request = get_cache_transform_request(&qstate->request);
+
+ result = qstate->read_func(qstate, transform_request->entry,
+ transform_request->entry_length);
+
+ if (result != qstate->kevent_watermark) {
+ TRACE_OUT(on_transform_request_read2);
+ return (-1);
+ }
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = on_transform_request_process;
+
+ TRACE_OUT(on_transform_request_read2);
+ return (0);
+}
+
+static int
+on_transform_request_process(struct query_state *qstate)
+{
+ struct cache_transform_request *transform_request;
+ struct cache_transform_response *transform_response;
+ struct configuration_entry *config_entry;
+ size_t i, size;
+
+ TRACE_IN(on_transform_request_process);
+ init_comm_element(&qstate->response, CET_TRANSFORM_RESPONSE);
+ transform_response = get_cache_transform_response(&qstate->response);
+ transform_request = get_cache_transform_request(&qstate->request);
+
+ switch (transform_request->transformation_type) {
+ case TT_USER:
+ if (transform_request->entry == NULL) {
+ size = configuration_get_entries_size(s_configuration);
+ for (i = 0; i < size; ++i) {
+ config_entry = configuration_get_entry(
+ s_configuration, i);
+
+ if (config_entry->perform_actual_lookups == 0)
+ clear_config_entry_part(config_entry,
+ qstate->eid_str, qstate->eid_str_length);
+ }
+ } else {
+ qstate->config_entry = configuration_find_entry(
+ s_configuration, transform_request->entry);
+
+ if (qstate->config_entry == NULL) {
+ LOG_ERR_2("transform_request",
+ "can't find configuration"
+ " entry '%s'. aborting request",
+ transform_request->entry);
+ transform_response->error_code = -1;
+ goto fin;
+ }
+
+ if (qstate->config_entry->perform_actual_lookups != 0) {
+ LOG_ERR_2("transform_request",
+ "can't transform the cache entry %s"
+ ", because it ised for actual lookups",
+ transform_request->entry);
+ transform_response->error_code = -1;
+ goto fin;
+ }
+
+ clear_config_entry_part(qstate->config_entry,
+ qstate->eid_str, qstate->eid_str_length);
+ }
+ break;
+ case TT_ALL:
+ if (qstate->euid != 0)
+ transform_response->error_code = -1;
+ else {
+ if (transform_request->entry == NULL) {
+ size = configuration_get_entries_size(
+ s_configuration);
+ for (i = 0; i < size; ++i) {
+ clear_config_entry(
+ configuration_get_entry(
+ s_configuration, i));
+ }
+ } else {
+ qstate->config_entry = configuration_find_entry(
+ s_configuration,
+ transform_request->entry);
+
+ if (qstate->config_entry == NULL) {
+ LOG_ERR_2("transform_request",
+ "can't find configuration"
+ " entry '%s'. aborting request",
+ transform_request->entry);
+ transform_response->error_code = -1;
+ goto fin;
+ }
+
+ clear_config_entry(qstate->config_entry);
+ }
+ }
+ break;
+ default:
+ transform_response->error_code = -1;
+ }
+
+fin:
+ qstate->kevent_watermark = 0;
+ qstate->process_func = on_transform_response_write1;
+ TRACE_OUT(on_transform_request_process);
+ return (0);
+}
+
+static int
+on_transform_response_write1(struct query_state *qstate)
+{
+ struct cache_transform_response *transform_response;
+ ssize_t result;
+
+ TRACE_IN(on_transform_response_write1);
+ transform_response = get_cache_transform_response(&qstate->response);
+ result = qstate->write_func(qstate, &transform_response->error_code,
+ sizeof(int));
+ if (result != sizeof(int)) {
+ TRACE_OUT(on_transform_response_write1);
+ return (-1);
+ }
+
+ finalize_comm_element(&qstate->request);
+ finalize_comm_element(&qstate->response);
+
+ qstate->kevent_watermark = 0;
+ qstate->process_func = NULL;
+ TRACE_OUT(on_transform_response_write1);
+ return (0);
+}
+
+/*
+ * Checks if the client's euid and egid do not differ from its uid and gid.
+ * Returns 0 on success.
+ */
+int
+check_query_eids(struct query_state *qstate)
+{
+
+ return ((qstate->uid != qstate->euid) || (qstate->gid != qstate->egid) ? -1 : 0);
+}
+
+/*
+ * Uses the qstate fields to process an "alternate" read - when the buffer is
+ * too large to be received during one socket read operation
+ */
+ssize_t
+query_io_buffer_read(struct query_state *qstate, void *buf, size_t nbytes)
+{
+ ssize_t result;
+
+ TRACE_IN(query_io_buffer_read);
+ if ((qstate->io_buffer_size == 0) || (qstate->io_buffer == NULL))
+ return (-1);
+
+ if (nbytes < qstate->io_buffer + qstate->io_buffer_size -
+ qstate->io_buffer_p)
+ result = nbytes;
+ else
+ result = qstate->io_buffer + qstate->io_buffer_size -
+ qstate->io_buffer_p;
+
+ memcpy(buf, qstate->io_buffer_p, result);
+ qstate->io_buffer_p += result;
+
+ if (qstate->io_buffer_p == qstate->io_buffer + qstate->io_buffer_size) {
+ free(qstate->io_buffer);
+ qstate->io_buffer = NULL;
+
+ qstate->write_func = query_socket_write;
+ qstate->read_func = query_socket_read;
+ }
+
+ TRACE_OUT(query_io_buffer_read);
+ return (result);
+}
+
+/*
+ * Uses the qstate fields to process an "alternate" write - when the buffer is
+ * too large to be sent during one socket write operation
+ */
+ssize_t
+query_io_buffer_write(struct query_state *qstate, const void *buf,
+ size_t nbytes)
+{
+ ssize_t result;
+
+ TRACE_IN(query_io_buffer_write);
+ if ((qstate->io_buffer_size == 0) || (qstate->io_buffer == NULL))
+ return (-1);
+
+ if (nbytes < qstate->io_buffer + qstate->io_buffer_size -
+ qstate->io_buffer_p)
+ result = nbytes;
+ else
+ result = qstate->io_buffer + qstate->io_buffer_size -
+ qstate->io_buffer_p;
+
+ memcpy(qstate->io_buffer_p, buf, result);
+ qstate->io_buffer_p += result;
+
+ if (qstate->io_buffer_p == qstate->io_buffer + qstate->io_buffer_size) {
+ qstate->use_alternate_io = 1;
+ qstate->io_buffer_p = qstate->io_buffer;
+
+ qstate->write_func = query_socket_write;
+ qstate->read_func = query_socket_read;
+ }
+
+ TRACE_OUT(query_io_buffer_write);
+ return (result);
+}
+
+/*
+ * The default "read" function, which reads data directly from socket
+ */
+ssize_t
+query_socket_read(struct query_state *qstate, void *buf, size_t nbytes)
+{
+ ssize_t result;
+
+ TRACE_IN(query_socket_read);
+ if (qstate->socket_failed != 0) {
+ TRACE_OUT(query_socket_read);
+ return (-1);
+ }
+
+ result = read(qstate->sockfd, buf, nbytes);
+ if ((result == -1) || (result < nbytes))
+ qstate->socket_failed = 1;
+
+ TRACE_OUT(query_socket_read);
+ return (result);
+}
+
+/*
+ * The default "write" function, which writes data directly to socket
+ */
+ssize_t
+query_socket_write(struct query_state *qstate, const void *buf, size_t nbytes)
+{
+ ssize_t result;
+
+ TRACE_IN(query_socket_write);
+ if (qstate->socket_failed != 0) {
+ TRACE_OUT(query_socket_write);
+ return (-1);
+ }
+
+ result = write(qstate->sockfd, buf, nbytes);
+ if ((result == -1) || (result < nbytes))
+ qstate->socket_failed = 1;
+
+ TRACE_OUT(query_socket_write);
+ return (result);
+}
+
+/*
+ * Initializes the query_state structure by filling it with the default values.
+ */
+struct query_state *
+init_query_state(int sockfd, size_t kevent_watermark, uid_t euid, gid_t egid)
+{
+ struct query_state *retval;
+
+ TRACE_IN(init_query_state);
+ retval = (struct query_state *)calloc(1, sizeof(struct query_state));
+ assert(retval != NULL);
+
+ retval->sockfd = sockfd;
+ retval->kevent_filter = EVFILT_READ;
+ retval->kevent_watermark = kevent_watermark;
+
+ retval->euid = euid;
+ retval->egid = egid;
+ retval->uid = retval->gid = -1;
+
+ if (asprintf(&retval->eid_str, "%d_%d_", retval->euid,
+ retval->egid) == -1) {
+ free(retval);
+ return (NULL);
+ }
+ retval->eid_str_length = strlen(retval->eid_str);
+
+ init_comm_element(&retval->request, CET_UNDEFINED);
+ init_comm_element(&retval->response, CET_UNDEFINED);
+ retval->process_func = on_query_startup;
+ retval->destroy_func = on_query_destroy;
+
+ retval->write_func = query_socket_write;
+ retval->read_func = query_socket_read;
+
+ get_time_func(&retval->creation_time);
+ memcpy(&retval->timeout, &s_configuration->query_timeout,
+ sizeof(struct timeval));
+
+ TRACE_OUT(init_query_state);
+ return (retval);
+}
+
+void
+destroy_query_state(struct query_state *qstate)
+{
+
+ TRACE_IN(destroy_query_state);
+ if (qstate->eid_str != NULL)
+ free(qstate->eid_str);
+
+ if (qstate->io_buffer != NULL)
+ free(qstate->io_buffer);
+
+ qstate->destroy_func(qstate);
+ free(qstate);
+ TRACE_OUT(destroy_query_state);
+}
diff --git a/usr.sbin/nscd/query.h b/usr.sbin/nscd/query.h
new file mode 100644
index 0000000..383d806
--- /dev/null
+++ b/usr.sbin/nscd/query.h
@@ -0,0 +1,110 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 __NSCD_QUERY_H__
+#define __NSCD_QUERY_H__
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "cachelib.h"
+#include "config.h"
+#include "protocol.h"
+
+struct query_state;
+struct configuration;
+struct configuration_entry;
+
+typedef int (*query_process_func)(struct query_state *);
+typedef void (*query_destroy_func)(struct query_state *);
+typedef ssize_t (*query_read_func)(struct query_state *, void *, size_t);
+typedef ssize_t (*query_write_func)(struct query_state *, const void *,
+ size_t);
+
+/*
+ * The query state structure contains the information to process all types of
+ * requests and to send all types of responses.
+ */
+struct query_state {
+ struct timeval creation_time;
+ struct timeval timeout;
+
+ struct comm_element request;
+ struct comm_element response;
+ struct configuration_entry *config_entry;
+ void *mdata;
+
+ query_process_func process_func; /* called on each event */
+ query_destroy_func destroy_func; /* called on destroy */
+
+ /*
+ * By substituting these functions we can opaquely send and received
+ * very large buffers
+ */
+ query_write_func write_func; /* data write function */
+ query_read_func read_func; /* data read function */
+
+ char *eid_str; /* the user-identifying string (euid_egid_) */
+ size_t eid_str_length;
+
+ uid_t euid; /* euid of the caller, received via getpeereid */
+ uid_t uid; /* uid of the caller, received via credentials */
+ gid_t egid; /* egid of the caller, received via getpeereid */
+ gid_t gid; /* gid of the caller received via credentials */
+
+ size_t io_buffer_size;
+ size_t io_buffer_watermark;
+ size_t kevent_watermark; /* bytes to be sent/received */
+ int sockfd; /* the unix socket to read/write */
+ int kevent_filter; /* EVFILT_READ or EVFILT_WRITE */
+ int socket_failed; /* set to 1 if the socket doesn't work correctly */
+
+ /*
+ * These fields are used to opaquely proceed sending/receiving of
+ * the large buffers
+ */
+ char *io_buffer;
+ char *io_buffer_p;
+ int io_buffer_filter;
+ int use_alternate_io;
+};
+
+extern int check_query_eids(struct query_state *);
+
+extern ssize_t query_io_buffer_read(struct query_state *, void *, size_t);
+extern ssize_t query_io_buffer_write(struct query_state *, const void *,
+ size_t);
+
+extern ssize_t query_socket_read(struct query_state *, void *, size_t);
+extern ssize_t query_socket_write(struct query_state *, const void *,
+ size_t);
+
+extern struct query_state *init_query_state(int, size_t, uid_t, gid_t);
+extern void destroy_query_state(struct query_state *);
+
+#endif
diff --git a/usr.sbin/nscd/singletons.c b/usr.sbin/nscd/singletons.c
new file mode 100644
index 0000000..669d12b
--- /dev/null
+++ b/usr.sbin/nscd/singletons.c
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 "singletons.h"
+
+struct configuration *s_configuration = NULL;
+cache s_cache = INVALID_CACHE;
+struct runtime_env *s_runtime_env = NULL;
+struct agent_table *s_agent_table = NULL;
diff --git a/usr.sbin/nscd/singletons.h b/usr.sbin/nscd/singletons.h
new file mode 100644
index 0000000..dfd891f
--- /dev/null
+++ b/usr.sbin/nscd/singletons.h
@@ -0,0 +1,47 @@
+/*-
+ * Copyright (c) 2005 Michael Bushkov <bushman@rsu.ru>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 __NSCD_SINGLETONS_H__
+#define __NSCD_SINGLETONS_H__
+
+#include "cachelib.h"
+#include "config.h"
+#include "agent.h"
+
+struct runtime_env {
+ int queue;
+ int sockfd;
+ int finished; /* for future use */
+};
+
+extern struct configuration *s_configuration;
+extern cache s_cache;
+extern struct runtime_env *s_runtime_env;
+extern struct agent_table *s_agent_table;
+
+#endif
diff --git a/usr.sbin/ntp/Makefile b/usr.sbin/ntp/Makefile
new file mode 100644
index 0000000..86e447d
--- /dev/null
+++ b/usr.sbin/ntp/Makefile
@@ -0,0 +1,8 @@
+# Makefile for ntpd.
+# $FreeBSD$
+
+SUBDIR= libopts libntp libparse ntpd ntpdc ntpq ntpdate ntptrace \
+ ntptime ntp-keygen sntp
+SUBDIR+= doc
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/ntp/Makefile.inc b/usr.sbin/ntp/Makefile.inc
new file mode 100644
index 0000000..f81905a
--- /dev/null
+++ b/usr.sbin/ntp/Makefile.inc
@@ -0,0 +1,21 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+DEFS_LOCAL= -DPARSE -DHAVE_CONFIG_H
+NTPDEFS= -DSYS_FREEBSD
+# CLOCKDEFS=
+# -DLOCAL_CLOCK -DPST -DWWVB -DAS2201 -DGOES -DGPSTM -DOMEGA \
+# -DLEITCH -DTRAK -DACTS -DATOM -DDATUM -DHEATH -DMSFEES \
+# -DMX4200 -DNMEA -DBOEDER
+CFLAGS+= ${NTPDEFS} ${DEFS_LOCAL} ${CLOCKDEFS}
+
+.if ${MK_OPENSSL} != "no" && !defined(RELEASE_CRUNCH)
+CFLAGS+= -DOPENSSL
+.endif
+
+LIBOPTS= ${.OBJDIR}/../libopts/libopts.a
+LIBPARSE= ${.OBJDIR}/../libparse/libparse.a
+LIBNTP= ${.OBJDIR}/../libntp/libntp.a
+
+.include "../Makefile.inc"
diff --git a/usr.sbin/ntp/config.h b/usr.sbin/ntp/config.h
new file mode 100644
index 0000000..518cd8c
--- /dev/null
+++ b/usr.sbin/ntp/config.h
@@ -0,0 +1,1362 @@
+/* config.h. Generated by configure. */
+/* config.h.in. Generated from configure.ac by autoheader. */
+/* $FreeBSD$ */
+
+/* Is adjtime() accurate? */
+/* #undef ADJTIME_IS_ACCURATE */
+
+/* CHU audio/decoder? */
+/* #undef AUDIO_CHU */
+
+/* Declare char *sys_errlist array */
+/* #undef CHAR_SYS_ERRLIST */
+
+/* ACTS modem service */
+/* #undef CLOCK_ACTS */
+
+/* Arbiter 1088A/B GPS receiver */
+/* #undef CLOCK_ARBITER */
+
+/* ARCRON support? */
+/* #undef CLOCK_ARCRON_MSF */
+
+/* Austron 2200A/2201A GPS receiver? */
+/* #undef CLOCK_AS2201 */
+
+/* PPS interface? */
+#define CLOCK_ATOM 1
+
+/* Datum/Bancomm bc635/VME interface? */
+/* #undef CLOCK_BANC */
+
+/* Chronolog K-series WWVB receiver? */
+/* #undef CLOCK_CHRONOLOG */
+
+/* CHU modem/decoder */
+/* #undef CLOCK_CHU */
+
+/* Diems Computime Radio Clock? */
+/* #undef CLOCK_COMPUTIME */
+
+/* Datum Programmable Time System? */
+/* #undef CLOCK_DATUM */
+
+/* ELV/DCF7000 clock? */
+/* #undef CLOCK_DCF7000 */
+
+/* Dumb generic hh:mm:ss local clock? */
+#define CLOCK_DUMBCLOCK 1
+
+/* Forum Graphic GPS datating station driver? */
+/* #undef CLOCK_FG */
+
+/* TrueTime GPS receiver/VME interface? */
+/* #undef CLOCK_GPSVME */
+
+/* Heath GC-1000 WWV/WWVH receiver? */
+/* #undef CLOCK_HEATH */
+
+/* HOPF 6021 clock? */
+/* #undef CLOCK_HOPF6021 */
+
+/* HOPF PCI clock device? */
+/* #undef CLOCK_HOPF_PCI */
+
+/* HOPF serial clock device? */
+/* #undef CLOCK_HOPF_SERIAL */
+
+/* HP 58503A GPS receiver? */
+/* #undef CLOCK_HPGPS */
+
+/* IRIG audio decoder? */
+/* #undef CLOCK_IRIG */
+
+/* JJY receiver? */
+/* #undef CLOCK_JJY */
+
+/* Rockwell Jupiter GPS clock? */
+/* #undef CLOCK_JUPITER */
+
+/* Leitch CSD 5300 Master Clock System Driver? */
+/* #undef CLOCK_LEITCH */
+
+/* local clock reference? */
+#define CLOCK_LOCAL 1
+
+/* Meinberg clocks */
+/* #undef CLOCK_MEINBERG */
+
+/* Magnavox MX4200 GPS receiver */
+/* #undef CLOCK_MX4200 */
+
+/* NeoClock4X */
+/* #undef CLOCK_NEOCLOCK4X */
+
+/* NMEA GPS receiver */
+#define CLOCK_NMEA 1
+
+/* Motorola UT Oncore GPS */
+#define CLOCK_ONCORE 1
+
+/* Palisade clock */
+/* #undef CLOCK_PALISADE */
+
+/* PARSE driver interface */
+#define CLOCK_PARSE 1
+
+/* Conrad parallel port radio clock */
+/* #undef CLOCK_PCF */
+
+/* PCL 720 clock support */
+/* #undef CLOCK_PPS720 */
+
+/* PST/Traconex 1020 WWV/WWVH receiver */
+/* #undef CLOCK_PST */
+
+/* DCF77 raw time code */
+#define CLOCK_RAWDCF 1
+
+/* RCC 8000 clock */
+/* #undef CLOCK_RCC8000 */
+
+/* RIPE NCC Trimble clock */
+/* #undef CLOCK_RIPENCC */
+
+/* Schmid DCF77 clock */
+/* #undef CLOCK_SCHMID */
+
+/* clock thru shared memory */
+/* #undef CLOCK_SHM */
+
+/* Spectracom 8170/Netclock/2 WWVB receiver */
+/* #undef CLOCK_SPECTRACOM */
+
+/* KSI/Odetics TPRO/S GPS receiver/IRIG interface */
+/* #undef CLOCK_TPRO */
+
+/* Trimble GPS receiver/TAIP protocol */
+/* #undef CLOCK_TRIMTAIP */
+
+/* Trimble GPS receiver/TSIP protocol */
+/* #undef CLOCK_TRIMTSIP */
+
+/* Kinemetrics/TrueTime receivers */
+/* #undef CLOCK_TRUETIME */
+
+/* TrueTime 560 IRIG-B decoder? */
+/* #undef CLOCK_TT560 */
+
+/* Ultralink M320 WWVB receiver? */
+/* #undef CLOCK_ULINK */
+
+/* VARITEXT protocol */
+/* #undef CLOCK_VARITEXT */
+
+/* WHARTON 400A Series protocol */
+/* #undef CLOCK_WHARTON_400A */
+
+/* WWV audio driver */
+/* #undef CLOCK_WWV */
+
+/* Zyfer GPStarplus */
+/* #undef CLOCK_ZYFER */
+
+/* Enable debugging? */
+/* #undef DEBUG */
+
+/* Enable processing time debugging? */
+/* #undef DEBUG_TIMING */
+
+/* Declaration style */
+/* #undef DECL_ADJTIME_0 */
+
+/* Declaration style */
+/* #undef DECL_BCOPY_0 */
+
+/* Declaration style */
+/* #undef DECL_BZERO_0 */
+
+/* Declaration style */
+/* #undef DECL_CFSETISPEED_0 */
+
+/* Declare errno? */
+/* #undef DECL_ERRNO */
+
+/* Declaration style */
+/* #undef DECL_HSTRERROR_0 */
+
+/* Declare h_errno? */
+#define DECL_H_ERRNO 1
+
+/* Declaration style */
+/* #undef DECL_INET_NTOA_0 */
+
+/* Declaration style */
+/* #undef DECL_IOCTL_0 */
+
+/* Declaration style */
+/* #undef DECL_IPC_0 */
+
+/* Declaration style */
+/* #undef DECL_MEMMOVE_0 */
+
+/* Declaration style */
+/* #undef DECL_MKSTEMP_0 */
+
+/* Declaration style */
+/* #undef DECL_MKTEMP_0 */
+
+/* Declaration style */
+/* #undef DECL_NLIST_0 */
+
+/* Declaration style */
+/* #undef DECL_PLOCK_0 */
+
+/* Declaration style */
+/* #undef DECL_RENAME_0 */
+
+/* Declaration style */
+/* #undef DECL_SELECT_0 */
+
+/* Declaration style */
+/* #undef DECL_SETITIMER_0 */
+
+/* Declaration style */
+/* #undef DECL_SETPRIORITY_0 */
+
+/* Declaration style */
+/* #undef DECL_SETPRIORITY_1 */
+
+/* Declaration style */
+/* #undef DECL_SIGVEC_0 */
+
+/* Declaration style */
+/* #undef DECL_STDIO_0 */
+
+/* Declaration style */
+/* #undef DECL_STIME_0 */
+
+/* Declaration style */
+/* #undef DECL_STIME_1 */
+
+/* Declaration style */
+/* #undef DECL_STRERROR_0 */
+
+/* Declaration style */
+/* #undef DECL_STRTOL_0 */
+
+/* Declare syscall()? */
+#define DECL_SYSCALL 1
+
+/* Declaration style */
+/* #undef DECL_SYSLOG_0 */
+
+/* Declaration style */
+/* #undef DECL_TIMEOFDAY_0 */
+
+/* Declaration style */
+/* #undef DECL_TIME_0 */
+
+/* Declaration style */
+/* #undef DECL_TOLOWER_0 */
+
+/* Declaration style */
+/* #undef DECL_TOUPPER_0 */
+
+/* What is the fallback value for HZ? */
+#define DEFAULT_HZ 100
+
+/* synch TODR hourly? */
+/* #undef DOSYNCTODR */
+
+/* The number of minutes in a DST adjustment */
+#define DSTMINUTES 60
+
+/* fopen(3) accepts a 'b' in the mode flag */
+#define FOPEN_BINARY_FLAG "b"
+
+/* fopen(3) accepts a 't' in the mode flag */
+#define FOPEN_TEXT_FLAG "t"
+
+/* force ntpdate to step the clock if !defined(STEP_SLEW) ? */
+/* #undef FORCE_NTPDATE_STEP */
+
+/* What is getsockname()'s socklen type? */
+#define GETSOCKNAME_SOCKLEN_TYPE socklen_t
+
+/* Do we have a routing socket (struct rt_msghdr)? */
+#define HAS_ROUTING_SOCKET 1
+
+/* Define to 1 if you have the <arpa/nameser.h> header file. */
+#define HAVE_ARPA_NAMESER_H 1
+
+/* Do we have audio support? */
+#define HAVE_AUDIO 1
+
+/* Define to 1 if you have the <bstring.h> header file. */
+/* #undef HAVE_BSTRING_H */
+
+/* Define to 1 if you have the `canonicalize_file_name' function. */
+/* #undef HAVE_CANONICALIZE_FILE_NAME */
+
+/* Do we have the CIOGETEV ioctl (SunOS, Linux)? */
+/* #undef HAVE_CIOGETEV */
+
+/* Define to 1 if you have the `clock_gettime' function. */
+#define HAVE_CLOCK_GETTIME 1
+
+/* Define to 1 if you have the `clock_settime' function. */
+#define HAVE_CLOCK_SETTIME 1
+
+/* Define to 1 if you have the `daemon' function. */
+#define HAVE_DAEMON 1
+
+/* Define this if /dev/zero is readable device */
+#define HAVE_DEV_ZERO 1
+
+/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
+ */
+#define HAVE_DIRENT_H 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Use Rendezvous/DNS-SD registration */
+/* #undef HAVE_DNSREGISTRATION */
+
+/* Define to 1 if you don't have `vprintf' but do have `_doprnt.' */
+/* #undef HAVE_DOPRNT */
+
+/* Can we drop root privileges? */
+/* #undef HAVE_DROPROOT */
+
+/* Define to 1 if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define to 1 if you have the `EVP_md2' function. */
+/* #undef HAVE_EVP_MD2 */
+
+/* Define to 1 if you have the `EVP_mdc2' function. */
+/* #undef HAVE_EVP_MDC2 */
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the `finite' function. */
+/* #undef HAVE_FINITE */
+
+/* Define to 1 if you have the `getbootfile' function. */
+#define HAVE_GETBOOTFILE 1
+
+/* Define to 1 if you have the `getclock' function. */
+/* #undef HAVE_GETCLOCK */
+
+/* Define to 1 if you have the `getdtablesize' function. */
+#define HAVE_GETDTABLESIZE 1
+
+/* Define to 1 if you have the `getifaddrs' function. */
+#define HAVE_GETIFADDRS 1
+
+/* Define to 1 if you have the `getrusage' function. */
+#define HAVE_GETRUSAGE 1
+
+/* Define to 1 if you have the `getuid' function. */
+#define HAVE_GETUID 1
+
+/* Define to 1 if you have the `hstrerror' function. */
+#define HAVE_HSTRERROR 1
+
+/* Obvious... */
+#define HAVE_HZ_IN_STRUCT_CLOCKINFO 1
+
+/* Define to 1 if you have the <ieeefp.h> header file. */
+#define HAVE_IEEEFP_H 1
+
+/* ISC: Use iflist_sysctl? */
+#define HAVE_IFLIST_SYSCTL 1
+
+/* Define to 1 if the system has the type `int16_t'. */
+#define HAVE_INT16_T 1
+
+/* Define to 1 if the system has the type `int32_t'. */
+#define HAVE_INT32_T 1
+
+/* Define to 1 if the system has the type `int8_t'. */
+#define HAVE_INT8_T 1
+
+/* Define to 1 if the system has the type `intptr_t'. */
+#define HAVE_INTPTR_T 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the `isfinite' function. */
+#define HAVE_ISFINITE 1
+
+/* Define to 1 if you have the `kvm_open' function. */
+#define HAVE_KVM_OPEN 1
+
+/* Define to 1 if you have the `K_open' function. */
+/* #undef HAVE_K_OPEN */
+
+/* Define to 1 if you have the `advapi32' library (-ladvapi32). */
+/* #undef HAVE_LIBADVAPI32 */
+
+/* Do we have the curses library? */
+/* #undef HAVE_LIBCURSES */
+
+/* Do we have the edit library? */
+/* #undef HAVE_LIBEDIT */
+
+/* Define to 1 if you have the `elf' library (-lelf). */
+#define HAVE_LIBELF 1
+
+/* Define to 1 if you have the `gen' library (-lgen). */
+/* #undef HAVE_LIBGEN */
+
+/* Define to 1 if you have the <libgen.h> header file. */
+#define HAVE_LIBGEN_H 1
+
+/* Define to 1 if you have the `kvm' library (-lkvm). */
+#define HAVE_LIBKVM 1
+
+/* Define to 1 if you have the `ld' library (-lld). */
+/* #undef HAVE_LIBLD */
+
+/* Define to 1 if you have the `md' library (-lmd). */
+#define HAVE_LIBMD 1
+
+/* Define to 1 if you have the `md5' library (-lmd5). */
+/* #undef HAVE_LIBMD5 */
+
+/* Define to 1 if you have the `mld' library (-lmld). */
+/* #undef HAVE_LIBMLD */
+
+/* Define to 1 if you have the `nsl' library (-lnsl). */
+/* #undef HAVE_LIBNSL */
+
+/* Define to 1 if you have the `posix4' library (-lposix4). */
+/* #undef HAVE_LIBPOSIX4 */
+
+/* Define to 1 if you have the `readline' library (-lreadline). */
+/* #undef HAVE_LIBREADLINE */
+
+/* Define to 1 if you have the `rt' library (-lrt). */
+#define HAVE_LIBRT 1
+
+/* Define to 1 if you have the `socket' library (-lsocket). */
+/* #undef HAVE_LIBSOCKET */
+
+/* Define to 1 if you have the `syslog' library (-lsyslog). */
+/* #undef HAVE_LIBSYSLOG */
+
+/* Define to 1 if you have the `xnet' library (-lxnet). */
+/* #undef HAVE_LIBXNET */
+
+/* Define to 1 if you have the <limits.h> header file. */
+#define HAVE_LIMITS_H 1
+
+/* Do we have Linux capabilities? */
+/* #undef HAVE_LINUX_CAPABILITIES */
+
+/* Define to 1 if you have the <machine/inline.h> header file. */
+/* #undef HAVE_MACHINE_INLINE_H */
+
+/* Define to 1 if you have the <machine/soundcard.h> header file. */
+/* #undef HAVE_MACHINE_SOUNDCARD_H */
+
+/* Define to 1 if you have the <math.h> header file. */
+#define HAVE_MATH_H 1
+
+/* Define to 1 if you have the `MD5Init' function. */
+#define HAVE_MD5INIT 1
+
+/* Define to 1 if you have the <md5.h> header file. */
+#define HAVE_MD5_H 1
+
+/* Define to 1 if you have the `memcpy' function. */
+#define HAVE_MEMCPY 1
+
+/* Define to 1 if you have the `memlk' function. */
+/* #undef HAVE_MEMLK */
+
+/* Define to 1 if you have the `memmove' function. */
+#define HAVE_MEMMOVE 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the `memset' function. */
+#define HAVE_MEMSET 1
+
+/* Define to 1 if you have the `mkstemp' function. */
+#define HAVE_MKSTEMP 1
+
+/* Define to 1 if you have the `mktime' function. */
+#define HAVE_MKTIME 1
+
+/* Define to 1 if you have the `mlockall' function. */
+#if __FreeBSD_version >= 500102
+#define HAVE_MLOCKALL 1
+#endif
+
+/* Define to 1 if you have the `mmap' function. */
+#define HAVE_MMAP 1
+
+/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
+/* #undef HAVE_NDIR_H */
+
+/* Define to 1 if you have the <netdb.h> header file. */
+#define HAVE_NETDB_H 1
+
+/* Define to 1 if you have the <netinet/in.h> header file. */
+#define HAVE_NETINET_IN_H 1
+
+/* Define to 1 if you have the <netinet/in_system.h> header file. */
+/* #undef HAVE_NETINET_IN_SYSTEM_H */
+
+/* Define to 1 if you have the <netinet/in_systm.h> header file. */
+#define HAVE_NETINET_IN_SYSTM_H 1
+
+/* Define to 1 if you have the <netinet/ip.h> header file. */
+#define HAVE_NETINET_IP_H 1
+
+/* NetInfo support? */
+/* #undef HAVE_NETINFO */
+
+/* Define to 1 if you have the <netinfo/ni.h> header file. */
+/* #undef HAVE_NETINFO_NI_H */
+
+/* Define to 1 if you have the <net/if6.h> header file. */
+/* #undef HAVE_NET_IF6_H */
+
+/* Define to 1 if you have the <net/if.h> header file. */
+#define HAVE_NET_IF_H 1
+
+/* Define to 1 if you have the <net/route.h> header file. */
+#define HAVE_NET_ROUTE_H 1
+
+/* Define to 1 if you have the `nice' function. */
+#define HAVE_NICE 1
+
+/* Define to 1 if you have the `nlist' function. */
+#define HAVE_NLIST 1
+
+/* Define to 1 if you have the `ntp_adjtime' function. */
+#define HAVE_NTP_ADJTIME 1
+
+/* Define to 1 if you have the `ntp_gettime' function. */
+#define HAVE_NTP_GETTIME 1
+
+/* Define this if pathfind(3) works */
+/* #undef HAVE_PATHFIND */
+
+/* Define to 1 if you have the `plock' function. */
+/* #undef HAVE_PLOCK */
+
+/* Define to 1 if you have the <poll.h> header file. */
+#define HAVE_POLL_H 1
+
+/* Do we have the PPS API per the Draft RFC? */
+#define HAVE_PPSAPI 1
+
+/* Are function prototypes OK? */
+#define HAVE_PROTOTYPES 1
+
+/* Define to 1 if you have the `pututline' function. */
+/* #undef HAVE_PUTUTLINE */
+
+/* Define to 1 if you have the `pututxline' function. */
+/* #undef HAVE_PUTUTXLINE */
+
+/* Define to 1 if you have the <readline/history.h> header file. */
+/* #undef HAVE_READLINE_HISTORY_H */
+
+/* Define to 1 if you have the <readline/readline.h> header file. */
+/* #undef HAVE_READLINE_READLINE_H */
+
+/* Define to 1 if you have the `readlink' function. */
+#define HAVE_READLINK 1
+
+/* Define this if we have a functional realpath(3C) */
+#define HAVE_REALPATH 1
+
+/* Define to 1 if you have the `recvmsg' function. */
+#define HAVE_RECVMSG 1
+
+/* Define to 1 if you have the <resolv.h> header file. */
+#define HAVE_RESOLV_H 1
+
+/* Define to 1 if you have the `rtprio' function. */
+#define HAVE_RTPRIO 1
+
+/* Should be obvious... */
+#define HAVE_SA_LEN_IN_STRUCT_SOCKADDR 1
+
+/* Obvious... */
+#define HAVE_SA_SIGACTION_IN_STRUCT_SIGACTION 1
+
+/* Define to 1 if you have the <sched.h> header file. */
+/* #undef HAVE_SCHED_H */
+
+/* Define to 1 if you have the `sched_setscheduler' function. */
+/* #undef HAVE_SCHED_SETSCHEDULER */
+
+/* Define to 1 if you have the <setjmp.h> header file. */
+#define HAVE_SETJMP_H 1
+
+/* Define to 1 if you have the `setlinebuf' function. */
+#define HAVE_SETLINEBUF 1
+
+/* Define to 1 if you have the `setpgid' function. */
+#define HAVE_SETPGID 1
+
+/* define if setpgrp takes 0 arguments */
+/* #undef HAVE_SETPGRP_0 */
+
+/* Define to 1 if you have the `setpriority' function. */
+#define HAVE_SETPRIORITY 1
+
+/* Define to 1 if you have the `setrlimit' function. */
+#define HAVE_SETRLIMIT 1
+
+/* Define to 1 if you have the `setsid' function. */
+#define HAVE_SETSID 1
+
+/* Define to 1 if you have the `settimeofday' function. */
+#define HAVE_SETTIMEOFDAY 1
+
+/* Define to 1 if you have the `setvbuf' function. */
+#define HAVE_SETVBUF 1
+
+/* Define to 1 if you have the <sgtty.h> header file. */
+/* #undef HAVE_SGTTY_H */
+
+/* Define to 1 if you have the `sigaction' function. */
+#define HAVE_SIGACTION 1
+
+/* Can we use SIGIO for tcp and udp IO? */
+/* #undef HAVE_SIGNALED_IO */
+
+/* Define to 1 if you have the `sigset' function. */
+/* #undef HAVE_SIGSET */
+
+/* Define to 1 if you have the `sigsuspend' function. */
+#define HAVE_SIGSUSPEND 1
+
+/* Define to 1 if you have the `sigvec' function. */
+#define HAVE_SIGVEC 1
+
+/* Define to 1 if you have the `snprintf' function. */
+#define HAVE_SNPRINTF 1
+
+/* Does struct sockaddr_storage have ss_family? */
+#define HAVE_SS_FAMILY_IN_SS 1
+
+/* Does struct sockaddr_storage have ss_len? */
+#define HAVE_SS_LEN_IN_SS 1
+
+/* Define to 1 if you have the <stdarg.h> header file. */
+#define HAVE_STDARG_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the `stime' function. */
+/* #undef HAVE_STIME */
+
+/* Define to 1 if you have the `strchr' function. */
+#define HAVE_STRCHR 1
+
+/* Define to 1 if you have the `strdup' function. */
+#define HAVE_STRDUP 1
+
+/* Define to 1 if you have the `strerror' function. */
+#define HAVE_STRERROR 1
+
+/* Define this if strftime() works */
+#define HAVE_STRFTIME 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the `strrchr' function. */
+#define HAVE_STRRCHR 1
+
+/* Define to 1 if you have the `strstr' function. */
+#define HAVE_STRSTR 1
+
+/* Do we have struct ntptimeval? */
+#define HAVE_STRUCT_NTPTIMEVAL 1
+
+/* Define to 1 if `time.tv_nsec' is member of `struct ntptimeval'. */
+#define HAVE_STRUCT_NTPTIMEVAL_TIME_TV_NSEC 1
+
+/* Does a system header define struct ppsclockev? */
+/* #undef HAVE_STRUCT_PPSCLOCKEV */
+
+/* Do we have struct snd_size? */
+#define HAVE_STRUCT_SND_SIZE 1
+
+/* Define to 1 if `sin6_scope_id' is member of `struct sockaddr_in6'. */
+#define HAVE_STRUCT_SOCKADDR_IN6_SIN6_SCOPE_ID 1
+
+/* Does a system header define struct sockaddr_storage? */
+#define HAVE_STRUCT_SOCKADDR_STORAGE 1
+
+/* Do we have struct timespec? */
+#define HAVE_STRUCT_TIMESPEC 1
+
+/* Define to 1 if you have the <sun/audioio.h> header file. */
+/* #undef HAVE_SUN_AUDIOIO_H */
+
+/* Define to 1 if you have the `sysconf' function. */
+#define HAVE_SYSCONF 1
+
+/* Define to 1 if you have the `sysctl' function. */
+#define HAVE_SYSCTL 1
+
+/* Define to 1 if you have the <sysexits.h> header file. */
+#define HAVE_SYSEXITS_H 1
+
+/* Define to 1 if you have the <sys/audioio.h> header file. */
+/* #undef HAVE_SYS_AUDIOIO_H */
+
+/* Define to 1 if you have the <sys/capability.h> header file. */
+/* #undef HAVE_SYS_CAPABILITY_H */
+
+/* Define to 1 if you have the <sys/clkdefs.h> header file. */
+/* #undef HAVE_SYS_CLKDEFS_H */
+
+/* Define to 1 if you have the <sys/clockctl.h> header file. */
+/* #undef HAVE_SYS_CLOCKCTL_H */
+
+/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
+ */
+/* #undef HAVE_SYS_DIR_H */
+
+/* Define to 1 if you have the <sys/file.h> header file. */
+#define HAVE_SYS_FILE_H 1
+
+/* Define to 1 if you have the <sys/i8253.h> header file. */
+/* #undef HAVE_SYS_I8253_H */
+
+/* Define to 1 if you have the <sys/ioctl.h> header file. */
+#define HAVE_SYS_IOCTL_H 1
+
+/* Define to 1 if you have the <sys/ipc.h> header file. */
+#define HAVE_SYS_IPC_H 1
+
+/* Define to 1 if you have the <sys/limits.h> header file. */
+/* #undef HAVE_SYS_LIMITS_H */
+
+/* Define to 1 if you have the <sys/lock.h> header file. */
+/* #undef HAVE_SYS_LOCK_H */
+
+/* Define to 1 if you have the <sys/mman.h> header file. */
+#define HAVE_SYS_MMAN_H 1
+
+/* Define to 1 if you have the <sys/modem.h> header file. */
+/* #undef HAVE_SYS_MODEM_H */
+
+/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
+ */
+/* #undef HAVE_SYS_NDIR_H */
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#define HAVE_SYS_PARAM_H 1
+
+/* Define to 1 if you have the <sys/pcl720.h> header file. */
+/* #undef HAVE_SYS_PCL720_H */
+
+/* Define to 1 if you have the <sys/poll.h> header file. */
+#define HAVE_SYS_POLL_H 1
+
+/* Define to 1 if you have the <sys/ppsclock.h> header file. */
+/* #undef HAVE_SYS_PPSCLOCK_H */
+
+/* Define to 1 if you have the <sys/ppstime.h> header file. */
+/* #undef HAVE_SYS_PPSTIME_H */
+
+/* Define to 1 if you have the <sys/prctl.h> header file. */
+/* #undef HAVE_SYS_PRCTL_H */
+
+/* Define to 1 if you have the <sys/procset.h> header file. */
+/* #undef HAVE_SYS_PROCSET_H */
+
+/* Define to 1 if you have the <sys/proc.h> header file. */
+#define HAVE_SYS_PROC_H 1
+
+/* Define to 1 if you have the <sys/resource.h> header file. */
+#define HAVE_SYS_RESOURCE_H 1
+
+/* Define to 1 if you have the <sys/sched.h> header file. */
+/* #undef HAVE_SYS_SCHED_H */
+
+/* Define to 1 if you have the <sys/select.h> header file. */
+#define HAVE_SYS_SELECT_H 1
+
+/* Define to 1 if you have the <sys/shm.h> header file. */
+#define HAVE_SYS_SHM_H 1
+
+/* Define to 1 if you have the <sys/signal.h> header file. */
+#define HAVE_SYS_SIGNAL_H 1
+
+/* Define to 1 if you have the <sys/sio.h> header file. */
+/* #undef HAVE_SYS_SIO_H */
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#define HAVE_SYS_SOCKET_H 1
+
+/* Define to 1 if you have the <sys/sockio.h> header file. */
+#define HAVE_SYS_SOCKIO_H 1
+
+/* Define to 1 if you have the <sys/soundcard.h> header file. */
+#define HAVE_SYS_SOUNDCARD_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/stream.h> header file. */
+/* #undef HAVE_SYS_STREAM_H */
+
+/* Define to 1 if you have the <sys/stropts.h> header file. */
+/* #undef HAVE_SYS_STROPTS_H */
+
+/* Define to 1 if you have the <sys/sysctl.h> header file. */
+#define HAVE_SYS_SYSCTL_H 1
+
+/* Define to 1 if you have the <sys/syssgi.h> header file. */
+/* #undef HAVE_SYS_SYSSGI_H */
+
+/* Define to 1 if you have the <sys/systune.h> header file. */
+/* #undef HAVE_SYS_SYSTUNE_H */
+
+/* Define to 1 if you have the <sys/termios.h> header file. */
+#define HAVE_SYS_TERMIOS_H 1
+
+/* Define to 1 if you have the <sys/timepps.h> header file. */
+#define HAVE_SYS_TIMEPPS_H 1
+
+/* Define to 1 if you have the <sys/timers.h> header file. */
+#define HAVE_SYS_TIMERS_H 1
+
+/* Define to 1 if you have the <sys/timex.h> header file. */
+#define HAVE_SYS_TIMEX_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have the <sys/tpro.h> header file. */
+/* #undef HAVE_SYS_TPRO_H */
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Use sys/uio.h for struct iovec help */
+/* #undef HAVE_SYS_UIO_H */
+
+/* Define to 1 if you have the <sys/un.h> header file. */
+#define HAVE_SYS_UN_H 1
+
+/* Define to 1 if you have the <sys/wait.h> header file. */
+#define HAVE_SYS_WAIT_H 1
+
+/* Define to 1 if the system has the type `s_char'. */
+/* #undef HAVE_S_CHAR */
+
+/* Define to 1 if you have the <termios.h> header file. */
+#define HAVE_TERMIOS_H 1
+
+/* Define to 1 if you have the <termio.h> header file. */
+/* #undef HAVE_TERMIO_H */
+
+/* Obvious... */
+/* #undef HAVE_TICKADJ_IN_STRUCT_CLOCKINFO */
+
+/* Define to 1 if you have the `timegm' function. */
+#define HAVE_TIMEGM 1
+
+/* Define to 1 if you have the <timepps.h> header file. */
+/* #undef HAVE_TIMEPPS_H */
+
+/* Define to 1 if you have the `timer_create' function. */
+#define HAVE_TIMER_CREATE 1
+
+/* Define to 1 if you have the `timer_settime' function. */
+#define HAVE_TIMER_SETTIME 1
+
+/* Define to 1 if you have the <timex.h> header file. */
+/* #undef HAVE_TIMEX_H */
+
+/* Do we have the TIOCGPPSEV ioctl (Solaris)? */
+/* #undef HAVE_TIOCGPPSEV */
+
+/* Do we have the TIOCSPPS ioctl (Solaris)? */
+/* #undef HAVE_TIOCSPPS */
+
+/* Do we have the TIO serial stuff? */
+/* #undef HAVE_TIO_SERIAL_STUFF */
+
+/* Does u_int64_t exist? */
+#define HAVE_TYPE_U_INT64_T 1
+
+/* Does u_int8_t exist? */
+#define HAVE_TYPE_U_INT8_T 1
+
+/* Define to 1 if the system has the type `uint16_t'. */
+#define HAVE_UINT16_T 1
+
+/* Define to 1 if the system has the type `uint32_t'. */
+#define HAVE_UINT32_T 1
+
+/* Define to 1 if the system has the type `uint8_t'. */
+#define HAVE_UINT8_T 1
+
+/* Define to 1 if the system has the type `uintptr_t'. */
+#define HAVE_UINTPTR_T 1
+
+/* Define to 1 if the system has the type `uint_t'. */
+/* #undef HAVE_UINT_T */
+
+/* Define to 1 if you have the `umask' function. */
+#define HAVE_UMASK 1
+
+/* Define to 1 if you have the `uname' function. */
+#define HAVE_UNAME 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the `updwtmp' function. */
+/* #undef HAVE_UPDWTMP */
+
+/* Define to 1 if you have the `updwtmpx' function. */
+/* #undef HAVE_UPDWTMPX */
+
+/* Define to 1 if you have the <utime.h> header file. */
+#define HAVE_UTIME_H 1
+
+/* Define to 1 if you have the <utmpx.h> header file. */
+/* #undef HAVE_UTMPX_H */
+
+/* Define to 1 if you have the <utmp.h> header file. */
+#define HAVE_UTMP_H 1
+
+/* Define to 1 if you have the <values.h> header file. */
+/* #undef HAVE_VALUES_H */
+
+/* Define to 1 if you have the <varargs.h> header file. */
+/* #undef HAVE_VARARGS_H */
+
+/* Define to 1 if you have the `vprintf' function. */
+#define HAVE_VPRINTF 1
+
+/* Define to 1 if you have the `vsnprintf' function. */
+#define HAVE_VSNPRINTF 1
+
+/* Define to 1 if you have the `vsprintf' function. */
+#define HAVE_VSPRINTF 1
+
+/* Define to 1 if you have the </sys/sync/queue.h> header file. */
+/* #undef HAVE__SYS_SYNC_QUEUE_H */
+
+/* Define to 1 if you have the </sys/sync/sema.h> header file. */
+/* #undef HAVE__SYS_SYNC_SEMA_H */
+
+/* Define to 1 if you have the `__adjtimex' function. */
+/* #undef HAVE___ADJTIMEX */
+
+/* Define to 1 if you have the `__ntp_gettime' function. */
+/* #undef HAVE___NTP_GETTIME */
+
+/* Does struct sockaddr_storage have __ss_family? */
+/* #undef HAVE___SS_FAMILY_IN_SS */
+
+/* Does struct sockaddr_storage have __ss_len? */
+/* #undef HAVE___SS_LEN_IN_SS */
+
+/* Should we use the IRIG sawtooth filter? */
+/* #undef IRIG_SUCKS */
+
+/* Do we need to fix in6isaddr? */
+/* #undef ISC_PLATFORM_FIXIN6ISADDR */
+
+/* ISC: do we have if_nametoindex()? */
+#define ISC_PLATFORM_HAVEIFNAMETOINDEX 1
+
+/* ISC: have struct if_laddrconf? */
+/* #undef ISC_PLATFORM_HAVEIF_LADDRCONF */
+
+/* ISC: have struct if_laddrreq? */
+/* #undef ISC_PLATFORM_HAVEIF_LADDRREQ */
+
+/* ISC: Have struct in6_pktinfo? */
+#define ISC_PLATFORM_HAVEIN6PKTINFO
+
+/* ISC: Have IPv6? */
+#define ISC_PLATFORM_HAVEIPV6
+
+/* ISC: struct sockaddr as sa_len? */
+#define ISC_PLATFORM_HAVESALEN
+
+/* ISC: Have sin6_scope_id? */
+#define ISC_PLATFORM_HAVESCOPEID
+
+/* ISC: provide inet_aton() */
+/* #undef ISC_PLATFORM_NEEDATON */
+
+/* ISC: Need in6addr_any? */
+/* #undef ISC_PLATFORM_NEEDIN6ADDRANY */
+
+/* ISC: provide inet_ntop() */
+/* #undef ISC_PLATFORM_NEEDNTOP */
+
+/* Do we need our own in_port_t? */
+/* #undef ISC_PLATFORM_NEEDPORTT */
+
+/* ISC: provide inet_pton() */
+/* #undef ISC_PLATFORM_NEEDPTON */
+
+/* Does the kernel have an FLL bug? */
+/* #undef KERNEL_FLL_BUG */
+
+/* Does the kernel support precision time discipline? */
+#define KERNEL_PLL 1
+
+/* What is (probably) the name of DOSYNCTODR in the kernel? */
+#define K_DOSYNCTODR_NAME "_dosynctodr"
+
+/* What is (probably) the name of NOPRINTF in the kernel? */
+#define K_NOPRINTF_NAME "_noprintf"
+
+/* What is the name of TICKADJ in the kernel? */
+#define K_TICKADJ_NAME "_tickadj"
+
+/* What is the name of TICK in the kernel? */
+#define K_TICK_NAME "_tick"
+
+/* Should we align with the NIST lockclock scheme? */
+/* #undef LOCKCLOCK */
+
+/* Does the kernel support multicasting IP? */
+#define MCAST 1
+
+/* Should we recommend a minimum value for tickadj? */
+/* #undef MIN_REC_TICKADJ */
+
+/* Do we need HPUX adjtime() library support? */
+/* #undef NEED_HPUX_ADJTIME */
+
+/* Do we want the HPUX FindConfig()? */
+/* #undef NEED_HPUX_FINDCONFIG */
+
+/* Do we need the qnx adjtime call? */
+/* #undef NEED_QNX_ADJTIME */
+
+/* Do we need extra room for SO_RCVBUF? (HPUX <8) */
+/* #undef NEED_RCVBUF_SLOP */
+
+/* Do we need an s_char typedef? */
+#define NEED_S_CHAR_TYPEDEF 1
+
+/* Might nlist() values require an extra level of indirection (AIX)? */
+/* #undef NLIST_EXTRA_INDIRECTION */
+
+/* does struct nlist use a name union? */
+/* #undef NLIST_NAME_UNION */
+
+/* nlist stuff */
+#define NLIST_STRUCT 1
+
+/* Should we NOT read /dev/kmem? */
+/* #undef NOKMEM */
+
+/* Define to 1 if your C compiler doesn't accept -c and -o together. */
+/* #undef NO_MINUS_C_MINUS_O */
+
+/* Define this if optional arguments are disallowed */
+/* #undef NO_OPTIONAL_OPT_ARGS */
+
+/* Should we avoid #warning on option name collisions? */
+/* #undef NO_OPTION_NAME_WARNINGS */
+
+/* Is there a problem using PARENB and IGNPAR (IRIX)? */
+#define NO_PARENB_IGNPAR 1
+
+/* Default location of crypto key info */
+#define NTP_KEYSDIR "/etc/ntp"
+
+/* Do we have ntp_{adj,get}time in libc? */
+#define NTP_SYSCALLS_LIBC 1
+
+/* Do we have ntp_{adj,get}time in the kernel? */
+/* #undef NTP_SYSCALLS_STD */
+
+/* Do we have support for SHMEM_STATUS? */
+#define ONCORE_SHMEM_STATUS 1
+
+/* Use OpenSSL? */
+/* #undef OPENSSL */
+
+/* Should we open the broadcast socket? */
+#define OPEN_BCAST_SOCKET 1
+
+/* need to recreate sockets on changed routing? */
+/* #undef OS_MISSES_SPECIFIC_ROUTE_UPDATES */
+
+/* wildcard socket needs to set REUSEADDR when binding to interface addresses
+ */
+/* #undef OS_NEEDS_REUSEADDR_FOR_IFADDRBIND */
+
+/* Do we need to override the system's idea of HZ? */
+#define OVERRIDE_HZ 1
+
+/* Name of package */
+#define PACKAGE "ntp"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "roberto@FreeBSD.org"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "ntp"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "ntp 4.2.4p5"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "ntp"
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "4.2.4p5"
+
+/* Do we have the ppsclock streams module? */
+/* #undef PPS */
+
+/* PPS auxiliary interface for ATOM? */
+#define PPS_SAMPLE 1
+
+/* PARSE kernel PLL PPS support */
+/* #undef PPS_SYNC */
+
+/* Preset a value for 'tick'? */
+#define PRESET_TICK 1000000L/hz
+
+/* Preset a value for 'tickadj'? */
+#define PRESET_TICKADJ 500/hz
+
+/* Define to 1 if the C compiler supports function prototypes. */
+#define PROTOTYPES 1
+
+/* Does qsort expect to work on "void *" stuff? */
+#define QSORT_USES_VOID_P 1
+
+/* Should we not IGNPAR (Linux)? */
+/* #undef RAWDCF_NO_IGNPAR */
+
+/* Basic refclock support? */
+#define REFCLOCK 1
+
+/* name of regex header file */
+#define REGEX_HEADER <regex.h>
+
+/* Do we want the ReliantUNIX clock hacks? */
+/* #undef RELIANTUNIX_CLOCK */
+
+/* Define as the return type of signal handlers (`int' or `void'). */
+#define RETSIGTYPE void
+
+/* Do we want the SCO clock hacks? */
+/* #undef SCO5_CLOCK */
+
+/* The size of a `char*', as computed by sizeof. */
+#if defined(__alpha__) || defined(__ia64__) || defined(__sparc64__) || defined(__amd64__)
+#define SIZEOF_CHARP 8
+#else
+#define SIZEOF_CHARP 4
+#endif
+
+/* The size of a `int', as computed by sizeof. */
+#define SIZEOF_INT 4
+
+/* The size of a `long', as computed by sizeof. */
+#if defined(__alpha__) || defined(__ia64__) || defined(__sparc64__) || defined(__amd64__)
+#define SIZEOF_LONG 8
+#else
+#define SIZEOF_LONG 4
+#endif
+
+/* The size of a `short', as computed by sizeof. */
+#define SIZEOF_SHORT 2
+
+/* The size of a `signed char', as computed by sizeof. */
+#define SIZEOF_SIGNED_CHAR 1
+
+/* The size of a `time_t', as computed by sizeof. */
+#if defined(__alpha__) || defined(__ia64__) || defined(__sparc64__) || defined(__amd64__)
+#define SIZEOF_TIME_T 8
+#else
+#define SIZEOF_TIME_T 4
+#endif
+
+/* Does SIOCGIFCONF return size in the buffer? */
+/* #undef SIZE_RETURNED_IN_BUFFER */
+
+/* Slew always? */
+/* #undef SLEWALWAYS */
+
+/* *s*printf() functions are char* */
+/* #undef SPRINTF_CHAR */
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Step, then slew the clock? */
+/* #undef STEP_SLEW */
+
+/* canonical system (cpu-vendor-os) of where we should run */
+#if defined(__alpha__)
+#define STR_SYSTEM "alpha-undermydesk-freebsd"
+#elif defined(__sparc64__)
+#define STR_SYSTEM "sparc64-undermydesk-freebsd"
+#elif defined(__amd64__)
+#define STR_SYSTEM "amd64-undermydesk-freebsd"
+#elif defined(__ia64__)
+#define STR_SYSTEM "ia64-undermydesk-freebsd"
+#else
+#define STR_SYSTEM "i386-undermydesk-freebsd"
+#endif
+
+/* Buggy syscall() (Solaris2.4)? */
+/* #undef SYSCALL_BUG */
+
+/* Does Xettimeofday take 1 arg? */
+/* #undef SYSV_TIMEOFDAY */
+
+/* Do we need to #define _SVID3 when we #include <termios.h>? */
+/* #undef TERMIOS_NEEDS__SVID3 */
+
+/* Is K_TICKADJ_NAME in nanoseconds? */
+/* #undef TICKADJ_NANO */
+
+/* Is K_TICK_NAME in nanoseconds? */
+/* #undef TICK_NANO */
+
+/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+#define TIME_WITH_SYS_TIME 1
+
+/* Define to 1 if your <sys/time.h> declares `struct tm'. */
+/* #undef TM_IN_SYS_TIME */
+
+/* Do we have the tty_clk line discipline/streams module? */
+/* #undef TTYCLK */
+
+/* Provide a typedef for uintptr_t? */
+#ifndef HAVE_UINTPTR_T
+typedef unsigned int uintptr_t;
+#define HAVE_UINTPTR_T 1
+#endif
+
+/* What type to use for setsockopt */
+#define TYPEOF_IP_MULTICAST_LOOP u_char
+
+/* Do we set process groups with -pid? */
+/* #undef UDP_BACKWARDS_SETOWN */
+
+/* How do we create unsigned long constants? */
+#define ULONG_CONST(a) a ## UL
+
+/* Must we have a CTTY for fsetown? */
+#define USE_FSETOWNCTTY 1
+
+/* Can we use SIGPOLL for tty IO? */
+/* #undef USE_TTY_SIGPOLL */
+
+/* Can we use SIGPOLL for UDP? */
+/* #undef USE_UDP_SIGPOLL */
+
+/* Version number of package */
+#define VERSION "4.2.4p5"
+
+/* ISC: Want IPv6? */
+#define WANT_IPV6 1
+
+/* Define this if a working libregex can be found */
+#define WITH_LIBREGEX 1
+
+/* Define to 1 if your processor stores words with the most significant byte
+ first (like Motorola and SPARC, unlike Intel and VAX). */
+#if defined(__sparc64__)
+#define WORDS_BIGENDIAN 1
+#endif
+
+/* Handle ss_family */
+#if !defined(HAVE_SS_FAMILY_IN_SS) && defined(HAVE___SS_FAMILY_IN_SS)
+# define ss_family __ss_family
+#endif /* !defined(HAVE_SS_FAMILY_IN_SS) && defined(HAVE_SA_FAMILY_IN_SS) */
+
+/* Handle ss_len */
+#if !defined(HAVE_SS_LEN_IN_SS) && defined(HAVE___SS_LEN_IN_SS)
+# define ss_len __ss_len
+#endif /* !defined(HAVE_SS_LEN_IN_SS) && defined(HAVE_SA_LEN_IN_SS) */
+
+/* Define to 1 if on AIX 3.
+ System headers sometimes define this.
+ We just want to avoid a redefinition error message. */
+#ifndef _ALL_SOURCE
+/* # undef _ALL_SOURCE */
+#endif
+
+/* Define to 1 if on MINIX. */
+/* #undef _MINIX */
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+ this defined. */
+/* #undef _POSIX_1_SOURCE */
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+/* #undef _POSIX_SOURCE */
+
+/* Define to 1 if type `char' is unsigned and you are not using gcc. */
+#ifndef __CHAR_UNSIGNED__
+/* # undef __CHAR_UNSIGNED__ */
+#endif
+
+/* Define like PROTOTYPES; this can be used by system headers. */
+#define __PROTOTYPES 1
+
+/* Define to empty if `const' does not conform to ANSI C. */
+/* #undef const */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef gid_t */
+
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+ calls it, or to nothing if 'inline' is not supported under any name. */
+#ifndef __cplusplus
+/* #undef inline */
+#endif
+
+/* Define to `long' if <sys/types.h> does not define. */
+/* #undef off_t */
+
+/* Define to `unsigned' if <sys/types.h> does not define. */
+/* #undef size_t */
+
+/* Define to `long' if <sys/types.h> does not define. */
+/* #undef time_t */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef uid_t */
+
+/* Alternate uintptr_t for systems without it. */
+/* #undef uintptr_t */
+
+/* Does the compiler like "volatile"? */
+/* #undef volatile */
diff --git a/usr.sbin/ntp/doc/Makefile b/usr.sbin/ntp/doc/Makefile
new file mode 100644
index 0000000..f1326fc
--- /dev/null
+++ b/usr.sbin/ntp/doc/Makefile
@@ -0,0 +1,33 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+FILESDIR= ${SHAREDIR}/doc/ntp
+
+.if ${MK_HTML} != "no"
+FILES= accopt.html assoc.html audio.html authopt.html build.html \
+ clockopt.html \
+ config.html confopt.html copyright.html debug.html driver1.html \
+ driver10.html driver11.html driver12.html driver16.html driver18.html \
+ driver19.html driver2.html driver20.html driver22.html \
+ driver26.html driver27.html driver28.html driver29.html \
+ driver3.html driver30.html driver32.html driver33.html driver34.html \
+ driver35.html driver36.html driver37.html \
+ driver4.html driver5.html driver6.html driver7.html driver8.html \
+ driver9.html extern.html hints.html \
+ howto.html index.html kern.html \
+ ldisc.html measure.html miscopt.html monopt.html mx4200data.html \
+ notes.html ntpd.html ntpdate.html ntpdc.html ntpq.html ntptime.html \
+ ntptrace.html parsedata.html parsenew.html patches.html porting.html \
+ pps.html prefer.html quick.html rdebug.html refclock.html \
+ release.html tickadj.html
+.endif
+
+MAN= ntp.conf.5 ntp.keys.5
+MAN+= ntp-keygen.8 ntpd.8 ntpdate.8 ntpdc.8 ntpq.8 ntptime.8 ntptrace.8
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/html \
+ ${.CURDIR}/../../../contrib/ntp/html/build \
+ ${.CURDIR}/../../../contrib/ntp/html/drivers
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ntp/doc/ntp-keygen.8 b/usr.sbin/ntp/doc/ntp-keygen.8
new file mode 100644
index 0000000..536900f
--- /dev/null
+++ b/usr.sbin/ntp/doc/ntp-keygen.8
@@ -0,0 +1,602 @@
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 17, 2006
+.Dt NTP-KEYGEN 8
+.Os
+.Sh NAME
+.Nm ntp-keygen
+.Nd key generation program for ntpd
+.Sh SYNOPSIS
+.Nm
+.Op Fl deGgHIMnPT
+.Op Fl c Ar scheme
+.Op Fl i Ar name
+.Op Fl p Ar password
+.Op Fl S Op Cm RSA | DSA
+.Op Fl s Ar name
+.Op Fl v Ar nkeys
+.Sh DESCRIPTION
+This program generates cryptographic data files used by the NTPv4
+authentication and identification schemes.
+It generates MD5 key files used in symmetric key cryptography.
+In addition, if the OpenSSL software library has been installed,
+it generates keys, certificate and identity files used in public key
+cryptography.
+These files are used for cookie encryption,
+digital signature and challenge/response identification algorithms
+compatible with the Internet standard security infrastructure.
+.Pp
+All files are in PEM-encoded printable ASCII format,
+so they can be embedded as MIME attachments in mail to other sites
+and certificate authorities.
+By default, files are not encrypted.
+The
+.Fl p Ar password
+option specifies the write password and
+.Fl q Ar password
+option the read password for previously encrypted files.
+The
+.Nm
+program prompts for the password if it reads an encrypted file
+and the password is missing or incorrect.
+If an encrypted file is read successfully and
+no write password is specified, the read password is used
+as the write password by default.
+.Pp
+The
+.Xr ntpd 8
+configuration command
+.Ic crypto pw Ar password
+specifies the read password for previously encrypted files.
+The daemon expires on the spot if the password is missing
+or incorrect.
+For convenience, if a file has been previously encrypted,
+the default read password is the name of the host running
+the program.
+If the previous write password is specified as the host name,
+these files can be read by that host with no explicit password.
+.Pp
+File names begin with the prefix
+.Cm ntpkey_
+and end with the postfix
+.Ar _hostname.filestamp ,
+where
+.Ar hostname
+is the owner name, usually the string returned
+by the Unix gethostname() routine, and
+.Ar filestamp
+is the NTP seconds when the file was generated, in decimal digits.
+This both guarantees uniqueness and simplifies maintenance
+procedures, since all files can be quickly removed
+by a
+.Ic rm ntpkey\&*
+command or all files generated
+at a specific time can be removed by a
+.Ic rm
+.Ar \&*filestamp
+command.
+To further reduce the risk of misconfiguration,
+the first two lines of a file contain the file name
+and generation date and time as comments.
+.Pp
+All files are installed by default in the keys directory
+.Pa /usr/local/etc ,
+which is normally in a shared filesystem
+in NFS-mounted networks.
+The actual location of the keys directory
+and each file can be overridden by configuration commands,
+but this is not recommended.
+Normally, the files for each host are generated by that host
+and used only by that host, although exceptions exist
+as noted later on this page.
+.Pp
+Normally, files containing private values,
+including the host key, sign key and identification parameters,
+are permitted root read/write-only;
+while others containing public values are permitted world readable.
+Alternatively, files containing private values can be encrypted
+and these files permitted world readable,
+which simplifies maintenance in shared file systems.
+Since uniqueness is insured by the hostname and
+file name extensions, the files for a NFS server and
+dependent clients can all be installed in the same shared directory.
+.Pp
+The recommended practice is to keep the file name extensions
+when installing a file and to install a soft link
+from the generic names specified elsewhere on this page
+to the generated files.
+This allows new file generations to be activated simply
+by changing the link.
+If a link is present, ntpd follows it to the file name
+to extract the filestamp.
+If a link is not present,
+.Xr ntpd 8
+extracts the filestamp from the file itself.
+This allows clients to verify that the file and generation times
+are always current.
+The
+.Nm
+program uses the same timestamp extension for all files generated
+at one time, so each generation is distinct and can be readily
+recognized in monitoring data.
+.Ss Running the program
+The safest way to run the
+.Nm
+program is logged in directly as root.
+The recommended procedure is change to the keys directory,
+usually
+.Pa /usr/local/etc ,
+then run the program.
+When run for the first time,
+or if all
+.Cm ntpkey
+files have been removed,
+the program generates a RSA host key file and matching RSA-MD5 certificate file,
+which is all that is necessary in many cases.
+The program also generates soft links from the generic names
+to the respective files.
+If run again, the program uses the same host key file,
+but generates a new certificate file and link.
+.Pp
+The host key is used to encrypt the cookie when required and so must be RSA type.
+By default, the host key is also the sign key used to encrypt signatures.
+When necessary, a different sign key can be specified and this can be
+either RSA or DSA type.
+By default, the message digest type is MD5, but any combination
+of sign key type and message digest type supported by the OpenSSL library
+can be specified, including those using the MD2, MD5, SHA, SHA1, MDC2
+and RIPE160 message digest algorithms.
+However, the scheme specified in the certificate must be compatible
+with the sign key.
+Certificates using any digest algorithm are compatible with RSA sign keys;
+however, only SHA and SHA1 certificates are compatible with DSA sign keys.
+.Pp
+Private/public key files and certificates are compatible with
+other OpenSSL applications and very likely other libraries as well.
+Certificates or certificate requests derived from them should be compatible
+with extant industry practice, although some users might find
+the interpretation of X509v3 extension fields somewhat liberal.
+However, the identification parameter files, although encoded
+as the other files, are probably not compatible with anything other than Autokey.
+.Pp
+Running the program as other than root and using the Unix
+.Ic su
+command
+to assume root may not work properly, since by default the OpenSSL library
+looks for the random seed file
+.Cm .rnd
+in the user home directory.
+However, there should be only one
+.Cm .rnd ,
+most conveniently
+in the root directory, so it is convenient to define the
+.Cm $RANDFILE
+environment variable used by the OpenSSL library as the path to
+.Cm /.rnd .
+.Pp
+Installing the keys as root might not work in NFS-mounted
+shared file systems, as NFS clients may not be able to write
+to the shared keys directory, even as root.
+In this case, NFS clients can specify the files in another
+directory such as
+.Pa /etc
+using the
+.Ic keysdir
+command.
+There is no need for one client to read the keys and certificates
+of other clients or servers, as these data are obtained automatically
+by the Autokey protocol.
+.Pp
+Ordinarily, cryptographic files are generated by the host that uses them,
+but it is possible for a trusted agent (TA) to generate these files
+for other hosts; however, in such cases files should always be encrypted.
+The subject name and trusted name default to the hostname
+of the host generating the files, but can be changed by command line options.
+It is convenient to designate the owner name and trusted name
+as the subject and issuer fields, respectively, of the certificate.
+The owner name is also used for the host and sign key files,
+while the trusted name is used for the identity files.
+.Pp
+.Ss Trusted Hosts and Groups
+Each cryptographic configuration involves selection of a signature scheme
+and identification scheme, called a cryptotype,
+as explained in the
+.Sx Authentication Options
+section of
+.Xr ntp.conf 5 .
+The default cryptotype uses RSA encryption, MD5 message digest
+and TC identification.
+First, configure a NTP subnet including one or more low-stratum
+trusted hosts from which all other hosts derive synchronization
+directly or indirectly.
+Trusted hosts have trusted certificates;
+all other hosts have nontrusted certificates.
+These hosts will automatically and dynamically build authoritative
+certificate trails to one or more trusted hosts.
+A trusted group is the set of all hosts that have, directly or indirectly,
+a certificate trail ending at a trusted host.
+The trail is defined by static configuration file entries
+or dynamic means described on the
+.Sx Automatic NTP Configuration Options
+section of
+.Xr ntp.conf 5 .
+.Pp
+On each trusted host as root, change to the keys directory.
+To insure a fresh fileset, remove all
+.Cm ntpkey
+files.
+Then run
+.Nm
+.Fl T
+to generate keys and a trusted certificate.
+On all other hosts do the same, but leave off the
+.Fl T
+flag to generate keys and nontrusted certificates.
+When complete, start the NTP daemons beginning at the lowest stratum
+and working up the tree.
+It may take some time for Autokey to instantiate the certificate trails
+throughout the subnet, but setting up the environment is completely automatic.
+.Pp
+If it is necessary to use a different sign key or different digest/signature
+scheme than the default, run
+.Nm
+with the
+.Fl S Ar type
+option, where
+.Ar type
+is either
+.Cm RSA
+or
+.Cm DSA .
+The most often need to do this is when a DSA-signed certificate is used.
+If it is necessary to use a different certificate scheme than the default,
+run
+.Nm
+with the
+.Fl c Ar scheme
+option and selected
+.Ar scheme
+as needed.
+If
+.Nm
+is run again without these options, it generates a new certificate
+using the same scheme and sign key.
+.Pp
+After setting up the environment it is advisable to update certificates
+from time to time, if only to extend the validity interval.
+Simply run
+.Nm
+with the same flags as before to generate new certificates
+using existing keys.
+However, if the host or sign key is changed,
+.Xr ntpd 8
+should be restarted.
+When
+.Xr ntpd 8
+is restarted, it loads any new files and restarts the protocol.
+Other dependent hosts will continue as usual until signatures are refreshed,
+at which time the protocol is restarted.
+.Ss Identity Schemes
+As mentioned on the Autonomous Authentication page,
+the default TC identity scheme is vulnerable to a middleman attack.
+However, there are more secure identity schemes available,
+including PC, IFF, GQ and MV described on the
+.Qq Identification Schemes
+page
+(maybe available at
+.Li http://www.eecis.udel.edu/%7emills/keygen.html ) .
+These schemes are based on a TA, one or more trusted hosts
+and some number of nontrusted hosts.
+Trusted hosts prove identity using values provided by the TA,
+while the remaining hosts prove identity using values provided
+by a trusted host and certificate trails that end on that host.
+The name of a trusted host is also the name of its sugroup
+and also the subject and issuer name on its trusted certificate.
+The TA is not necessarily a trusted host in this sense, but often is.
+.Pp
+In some schemes there are separate keys for servers and clients.
+A server can also be a client of another server,
+but a client can never be a server for another client.
+In general, trusted hosts and nontrusted hosts that operate
+as both server and client have parameter files that contain
+both server and client keys.
+Hosts that operate
+only as clients have key files that contain only client keys.
+.Pp
+The PC scheme supports only one trusted host in the group.
+On trusted host alice run
+.Nm
+.Fl P
+.Fl p Ar password
+to generate the host key file
+.Pa ntpkey_RSAkey_ Ns Ar alice.filestamp
+and trusted private certificate file
+.Pa ntpkey_RSA-MD5_cert_ Ns Ar alice.filestamp .
+Copy both files to all group hosts;
+they replace the files which would be generated in other schemes.
+On each host bob install a soft link from the generic name
+.Pa ntpkey_host_ Ns Ar bob
+to the host key file and soft link
+.Pa ntpkey_cert_ Ns Ar bob
+to the private certificate file.
+Note the generic links are on bob, but point to files generated
+by trusted host alice.
+In this scheme it is not possible to refresh
+either the keys or certificates without copying them
+to all other hosts in the group.
+.Pp
+For the IFF scheme proceed as in the TC scheme to generate keys
+and certificates for all group hosts, then for every trusted host in the group,
+generate the IFF parameter file.
+On trusted host alice run
+.Nm
+.Fl T
+.Fl I
+.Fl p Ar password
+to produce her parameter file
+.Pa ntpkey_IFFpar_ Ns Ar alice.filestamp ,
+which includes both server and client keys.
+Copy this file to all group hosts that operate as both servers
+and clients and install a soft link from the generic
+.Pa ntpkey_iff_ Ns Ar alice
+to this file.
+If there are no hosts restricted to operate only as clients,
+there is nothing further to do.
+As the IFF scheme is independent
+of keys and certificates, these files can be refreshed as needed.
+.Pp
+If a rogue client has the parameter file, it could masquerade
+as a legitimate server and present a middleman threat.
+To eliminate this threat, the client keys can be extracted
+from the parameter file and distributed to all restricted clients.
+After generating the parameter file, on alice run
+.Nm
+.Fl e
+and pipe the output to a file or mail program.
+Copy or mail this file to all restricted clients.
+On these clients install a soft link from the generic
+.Pa ntpkey_iff_ Ns Ar alice
+to this file.
+To further protect the integrity of the keys,
+each file can be encrypted with a secret password.
+.Pp
+For the GQ scheme proceed as in the TC scheme to generate keys
+and certificates for all group hosts, then for every trusted host
+in the group, generate the IFF parameter file.
+On trusted host alice run
+.Nm
+.Fl T
+.Fl G
+.Fl p Ar password
+to produce her parameter file
+.Pa ntpkey_GQpar_ Ns Ar alice.filestamp ,
+which includes both server and client keys.
+Copy this file to all group hosts and install a soft link
+from the generic
+.Pa ntpkey_gq_ Ns Ar alice
+to this file.
+In addition, on each host bob install a soft link
+from generic
+.Pa ntpkey_gq_ Ns Ar bob
+to this file.
+As the GQ scheme updates the GQ parameters file and certificate
+at the same time, keys and certificates can be regenerated as needed.
+.Pp
+For the MV scheme, proceed as in the TC scheme to generate keys
+and certificates for all group hosts.
+For illustration assume trish is the TA, alice one of several trusted hosts
+and bob one of her clients.
+On TA trish run
+.Nm
+.Fl V Ar n
+.Fl p Ar password ,
+where
+.Ar n
+is the number of revokable keys (typically 5) to produce
+the parameter file
+.Pa ntpkeys_MVpar_ Ns Ar trish.filestamp
+and client key files
+.Pa ntpkeys_MVkeyd_ Ns Ar trish.filestamp
+where
+.Ar d
+is the key number (0 \&<
+.Ar d
+\&<
+.Ar n ) .
+Copy the parameter file to alice and install a soft link
+from the generic
+.Pa ntpkey_mv_ Ns Ar alice
+to this file.
+Copy one of the client key files to alice for later distribution
+to her clients.
+It doesn't matter which client key file goes to alice,
+since they all work the same way.
+Alice copies the client key file to all of her cliens.
+On client bob install a soft link from generic
+.Pa ntpkey_mvkey_ Ns Ar bob
+to the client key file.
+As the MV scheme is independent of keys and certificates,
+these files can be refreshed as needed.
+.Ss Command Line Options
+.Bl -tag -width indent
+.It Fl c Ar scheme
+Select certificate message digest/signature encryption scheme.
+The
+.Ar scheme
+can be one of the following:
+. Cm RSA-MD2 , RSA-MD5 , RSA-SHA , RSA-SHA1 , RSA-MDC2 , RSA-RIPEMD160 , DSA-SHA ,
+or
+.Cm DSA-SHA1 .
+Note that RSA schemes must be used with a RSA sign key and DSA
+schemes must be used with a DSA sign key.
+The default without this option is
+.Cm RSA-MD5 .
+.It Fl d
+Enable debugging.
+This option displays the cryptographic data produced in eye-friendly billboards.
+.It Fl e
+Write the IFF client keys to the standard output.
+This is intended for automatic key distribution by mail.
+.It Fl G
+Generate parameters and keys for the GQ identification scheme,
+obsoleting any that may exist.
+.It Fl g
+Generate keys for the GQ identification scheme
+using the existing GQ parameters.
+If the GQ parameters do not yet exist, create them first.
+.It Fl H
+Generate new host keys, obsoleting any that may exist.
+.It Fl I
+Generate parameters for the IFF identification scheme,
+obsoleting any that may exist.
+.It Fl i Ar name
+Set the suject name to
+.Ar name .
+This is used as the subject field in certificates
+and in the file name for host and sign keys.
+.It Fl M
+Generate MD5 keys, obsoleting any that may exist.
+.It Fl P
+Generate a private certificate.
+By default, the program generates public certificates.
+.It Fl p Ar password
+Encrypt generated files containing private data with
+.Ar password
+and the DES-CBC algorithm.
+.It Fl q
+Set the password for reading files to password.
+.It Fl S Oo Cm RSA | DSA Oc
+Generate a new sign key of the designated type,
+obsoleting any that may exist.
+By default, the program uses the host key as the sign key.
+.It Fl s Ar name
+Set the issuer name to
+.Ar name .
+This is used for the issuer field in certificates
+and in the file name for identity files.
+.It Fl T
+Generate a trusted certificate.
+By default, the program generates a non-trusted certificate.
+.It Fl V Ar nkeys
+Generate parameters and keys for the Mu-Varadharajan (MV) identification scheme.
+.El
+.Ss Random Seed File
+All cryptographically sound key generation schemes must have means
+to randomize the entropy seed used to initialize
+the internal pseudo-random number generator used
+by the library routines.
+The OpenSSL library uses a designated random seed file for this purpose.
+The file must be available when starting the NTP daemon and
+.Nm
+program.
+If a site supports OpenSSL or its companion OpenSSH,
+it is very likely that means to do this are already available.
+.Pp
+It is important to understand that entropy must be evolved
+for each generation, for otherwise the random number sequence
+would be predictable.
+Various means dependent on external events, such as keystroke intervals,
+can be used to do this and some systems have built-in entropy sources.
+Suitable means are described in the OpenSSL software documentation,
+but are outside the scope of this page.
+.Pp
+The entropy seed used by the OpenSSL library is contained in a file,
+usually called
+.Cm .rnd ,
+which must be available when starting the NTP daemon
+or the
+.Nm
+program.
+The NTP daemon will first look for the file
+using the path specified by the
+.Ic randfile
+subcommand of the
+.Ic crypto
+configuration command.
+If not specified in this way, or when starting the
+.Nm
+program,
+the OpenSSL library will look for the file using the path specified
+by the
+.Ev RANDFILE
+environment variable in the user home directory,
+whether root or some other user.
+If the
+.Ev RANDFILE
+environment variable is not present,
+the library will look for the
+.Cm .rnd
+file in the user home directory.
+If the file is not available or cannot be written,
+the daemon exits with a message to the system log and the program
+exits with a suitable error message.
+.Ss Cryptographic Data Files
+All other file formats begin with two lines.
+The first contains the file name, including the generated host name
+and filestamp.
+The second contains the datestamp in conventional Unix date format.
+Lines beginning with # are considered comments and ignored by the
+.Nm
+program and
+.Xr ntpd 8
+daemon.
+Cryptographic values are encoded first using ASN.1 rules,
+then encrypted if necessary, and finally written PEM-encoded
+printable ASCII format preceded and followed by MIME content identifier lines.
+.Pp
+The format of the symmetric keys file is somewhat different
+than the other files in the interest of backward compatibility.
+Since DES-CBC is deprecated in NTPv4, the only key format of interest
+is MD5 alphanumeric strings.
+Following hte heard the keys are
+entered one per line in the format
+.D1 Ar keyno type key
+where
+.Ar keyno
+is a positive integer in the range 1-65,535,
+.Ar type
+is the string MD5 defining the key format and
+.Ar key
+is the key itself,
+which is a printable ASCII string 16 characters or less in length.
+Each character is chosen from the 93 printable characters
+in the range 0x21 through 0x7f excluding space and the
+.Ql #
+character.
+.Pp
+Note that the keys used by the
+.Xr ntpq 8
+and
+.Xr ntpdc 8
+programs
+are checked against passwords requested by the programs
+and entered by hand, so it is generally appropriate to specify these keys
+in human readable ASCII format.
+.Pp
+The
+.Nm
+program generates a MD5 symmetric keys file
+.Pa ntpkey_MD5key_ Ns Ar hostname.filestamp .
+Since the file contains private shared keys,
+it should be visible only to root and distributed by secure means
+to other subnet hosts.
+The NTP daemon loads the file
+.Pa ntp.keys ,
+so
+.Nm
+installs a soft link from this name to the generated file.
+Subsequently, similar soft links must be installed by manual
+or automated means on the other subnet hosts.
+While this file is not used with the Autokey Version 2 protocol,
+it is needed to authenticate some remote configuration commands
+used by the
+.Xr ntpq 8
+and
+.Xr ntpdc 8
+utilities.
+.Sh Bugs
+It can take quite a while to generate some cryptographic values,
+from one to several minutes with modern architectures
+such as UltraSPARC and up to tens of minutes to an hour
+with older architectures such as SPARC IPC.
diff --git a/usr.sbin/ntp/doc/ntp.conf.5 b/usr.sbin/ntp/doc/ntp.conf.5
new file mode 100644
index 0000000..264a250
--- /dev/null
+++ b/usr.sbin/ntp/doc/ntp.conf.5
@@ -0,0 +1,2715 @@
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 21, 2006
+.Dt NTP.CONF 5
+.Os
+.Sh NAME
+.Nm ntp.conf
+.Nd Network Time Protocol (NTP) daemon configuration file
+.Sh SYNOPSIS
+.Nm /etc/ntp.conf
+.Sh DESCRIPTION
+The
+.Nm
+configuration file is read at initial startup by the
+.Xr ntpd 8
+daemon in order to specify the synchronization sources,
+modes and other related information.
+Usually, it is installed in the
+.Pa /etc
+directory,
+but could be installed elsewhere
+(see the daemon's
+.Fl c
+command line option).
+.Pp
+The
+.Pa /etc/rc.d/ntpdate
+script reads this file to get a list of NTP servers to use if the
+variable
+.Dq Li ntpdate_hosts
+was not declared.
+Refer to the
+.Xr rc.conf 5
+man page for further info about this.
+.Pp
+The file format is similar to other
+.Ux
+configuration files.
+Comments begin with a
+.Ql #
+character and extend to the end of the line;
+blank lines are ignored.
+Configuration commands consist of an initial keyword
+followed by a list of arguments,
+some of which may be optional, separated by whitespace.
+Commands may not be continued over multiple lines.
+Arguments may be host names,
+host addresses written in numeric, dotted-quad form,
+integers, floating point numbers (when specifying times in seconds)
+and text strings.
+.Pp
+The rest of this page describes the configuration and control options.
+The
+.Qq Notes on Configuring NTP and Setting up a NTP Subnet
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp )
+contains an extended discussion of these options.
+In addition to the discussion of general
+.Sx Configuration Options ,
+there are sections describing the following supported functionality
+and the options used to control it:
+.Bl -bullet -offset indent
+.It
+.Sx Authentication Support
+.It
+.Sx Monitoring Support
+.It
+.Sx Access Control Support
+.It
+.Sx Automatic NTP Configuration Options
+.It
+.Sx Reference Clock Support
+.It
+.Sx Miscellaneous Options
+.El
+.Pp
+Following these is a section describing
+.Sx Miscellaneous Options .
+While there is a rich set of options available,
+the only required option is one or more
+.Ic server ,
+.Ic peer ,
+.Ic broadcast
+or
+.Ic manycastclient
+commands.
+.Sh Configuration Support
+Following is a description of the configuration commands in
+NTPv4.
+These commands have the same basic functions as in NTPv3 and
+in some cases new functions and new arguments.
+There are two
+classes of commands, configuration commands that configure a
+persistent association with a remote server or peer or reference
+clock, and auxiliary commands that specify environmental variables
+that control various related operations.
+.Ss Configuration Commands
+The various modes are determined by the command keyword and the
+type of the required IP address.
+Addresses are classed by type as
+(s) a remote server or peer (IPv4 class A, B and C), (b) the
+broadcast address of a local interface, (m) a multicast address (IPv4
+class D), or (r) a reference clock address (127.127.x.x).
+Note that
+only those options applicable to each command are listed below.
+Use
+of options not listed may not be caught as an error, but may result
+in some weird and even destructive behavior.
+.Pp
+If the Basic Socket Interface Extensions for IPv6 (RFC-2553)
+is detected, support for the IPv6 address family is generated
+in addition to the default support of the IPv4 address family.
+In a few cases, including the reslist billboard generated
+by ntpdc, IPv6 addresses are automatically generated.
+IPv6 addresses can be identified by the presence of colons
+.Dq \&:
+in the address field.
+IPv6 addresses can be used almost everywhere where
+IPv4 addresses can be used,
+with the exception of reference clock addresses,
+which are always IPv4.
+.Pp
+Note that in contexts where a host name is expected, a
+.Fl 4
+qualifier preceding
+the host name forces DNS resolution to the IPv4 namespace,
+while a
+.Fl 6
+qualifier forces DNS resolution to the IPv6 namespace.
+See IPv6 references for the
+equivalent classes for that address family.
+.Bl -tag -width indent
+.It Xo Ic server Ar address
+.Op Cm key Ar key \&| Cm autokey
+.Op Cm burst
+.Op Cm iburst
+.Op Cm version Ar version
+.Op Cm prefer
+.Op Cm minpoll Ar minpoll
+.Op Cm maxpoll Ar maxpoll
+.Xc
+.It Xo Ic peer Ar address
+.Op Cm key Ar key \&| Cm autokey
+.Op Cm version Ar version
+.Op Cm prefer
+.Op Cm minpoll Ar minpoll
+.Op Cm maxpoll Ar maxpoll
+.Xc
+.It Xo Ic broadcast Ar address
+.Op Cm key Ar key \&| Cm autokey
+.Op Cm version Ar version
+.Op Cm prefer
+.Op Cm minpoll Ar minpoll
+.Op Cm ttl Ar ttl
+.Xc
+.It Xo Ic manycastclient Ar address
+.Op Cm key Ar key \&| Cm autokey
+.Op Cm version Ar version
+.Op Cm prefer
+.Op Cm minpoll Ar minpoll
+.Op Cm maxpoll Ar maxpoll
+.Op Cm ttl Ar ttl
+.Xc
+.El
+.Pp
+These four commands specify the time server name or address to
+be used and the mode in which to operate.
+The
+.Ar address
+can be
+either a DNS name or an IP address in dotted-quad notation.
+Additional information on association behavior can be found in the
+.Qq Association Management
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+.Bl -tag -width indent
+.It Ic server
+For type s and r addresses, this command mobilizes a persistent
+client mode association with the specified remote server or local
+radio clock.
+In this mode the local clock can synchronized to the
+remote server, but the remote server can never be synchronized to
+the local clock.
+This command should
+.Em not
+be used for type
+b or m addresses.
+.It Ic peer
+For type s addresses (only), this command mobilizes a
+persistent symmetric-active mode association with the specified
+remote peer.
+In this mode the local clock can be synchronized to
+the remote peer or the remote peer can be synchronized to the local
+clock.
+This is useful in a network of servers where, depending on
+various failure scenarios, either the local or remote peer may be
+the better source of time.
+This command should NOT be used for type
+b, m or r addresses.
+.It Ic broadcast
+For type b and m addresses (only), this
+command mobilizes a persistent broadcast mode association.
+Multiple
+commands can be used to specify multiple local broadcast interfaces
+(subnets) and/or multiple multicast groups.
+Note that local
+broadcast messages go only to the interface associated with the
+subnet specified, but multicast messages go to all interfaces.
+In broadcast mode the local server sends periodic broadcast
+messages to a client population at the
+.Ar address
+specified, which is usually the broadcast address on (one of) the
+local network(s) or a multicast address assigned to NTP.
+The IANA
+has assigned the multicast group address IPv4 224.0.1.1 and
+IPv6 ff05::101 (site local) exclusively to
+NTP, but other nonconflicting addresses can be used to contain the
+messages within administrative boundaries.
+Ordinarily, this
+specification applies only to the local server operating as a
+sender; for operation as a broadcast client, see the
+.Ic broadcastclient
+or
+.Ic multicastclient
+commands
+below.
+.It Ic manycastclient
+For type m addresses (only), this command mobilizes a
+manycast client mode association for the multicast address
+specified.
+In this case a specific address must be supplied which
+matches the address used on the
+.Ic manycastserver
+command for
+the designated manycast servers.
+The NTP multicast address
+224.0.1.1 assigned by the IANA should NOT be used, unless specific
+means are taken to avoid spraying large areas of the Internet with
+these messages and causing a possibly massive implosion of replies
+at the sender.
+The
+.Ic manycastserver
+command specifies that the local server
+is to operate in client mode with the remote servers that are
+discovered as the result of broadcast/multicast messages.
+The
+client broadcasts a request message to the group address associated
+with the specified
+.Ar address
+and specifically enabled
+servers respond to these messages.
+The client selects the servers
+providing the best time and continues as with the
+.Ic server
+command.
+The remaining servers are discarded as if never
+heard.
+.El
+.Pp
+Options:
+.Bl -tag -width indent
+.It Cm autokey
+All packets sent to and received from the server or peer are to
+include authentication fields encrypted using the autokey scheme
+described in
+.Sx Authentication Options .
+.It Cm burst
+when the server is reachable, send a burst of eight packets
+instead of the usual one.
+The packet spacing is normally 2 s;
+however, the spacing between the first and second packets
+can be changed with the calldelay command to allow
+additional time for a modem or ISDN call to complete.
+This is designed to improve timekeeping quality
+with the
+.Ic server
+command and s addresses.
+.It Cm iburst
+When the server is unreachable, send a burst of eight packets
+instead of the usual one.
+The packet spacing is normally 2 s;
+however, the spacing between the first two packets can be
+changed with the calldelay command to allow
+additional time for a modem or ISDN call to complete.
+This is designed to speed the initial synchronization
+acquisition with the
+.Ic server
+command and s addresses and when
+.Xr ntpd 8
+is started with the
+.Fl q
+option.
+.It Cm key Ar key
+All packets sent to and received from the server or peer are to
+include authentication fields encrypted using the specified
+.Ar key
+identifier with values from 1 to 65534, inclusive.
+The
+default is to include no encryption field.
+.It Cm minpoll Ar minpoll
+.It Cm maxpoll Ar maxpoll
+These options specify the minimum and maximum poll intervals
+for NTP messages, as a power of 2 in seconds
+The maximum poll
+interval defaults to 10 (1,024 s), but can be increased by the
+.Cm maxpoll
+option to an upper limit of 17 (36.4 h).
+The
+minimum poll interval defaults to 6 (64 s), but can be decreased by
+the
+.Cm minpoll
+option to a lower limit of 4 (16 s).
+.It Cm noselect
+Marks the server as unused, except for display purposes.
+The server is discarded by the selection algroithm.
+.It Cm prefer
+Marks the server as preferred.
+All other things being equal,
+this host will be chosen for synchronization among a set of
+correctly operating hosts.
+See the
+.Qq Mitigation Rules and the prefer Keyword
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp )
+for further information.
+.It Cm ttl Ar ttl
+This option is used only with broadcast server and manycast
+client modes.
+It specifies the time-to-live
+.Ar ttl
+to
+use on broadcast server and multicast server and the maximum
+.Ar ttl
+for the expanding ring search with manycast
+client packets.
+Selection of the proper value, which defaults to
+127, is something of a black art and should be coordinated with the
+network administrator.
+.It Cm version Ar version
+Specifies the version number to be used for outgoing NTP
+packets.
+Versions 1-4 are the choices, with version 4 the
+default.
+.El
+.Ss Auxiliary Commands
+.Bl -tag -width indent
+.It Ic broadcastclient
+This command enables reception of broadcast server messages to
+any local interface (type b) address.
+Upon receiving a message for
+the first time, the broadcast client measures the nominal server
+propagation delay using a brief client/server exchange with the
+server, then enters the broadcast client mode, in which it
+synchronizes to succeeding broadcast messages.
+Note that, in order
+to avoid accidental or malicious disruption in this mode, both the
+server and client should operate using symmetric-key or public-key
+authentication as described in
+.Sx Authentication Options .
+.It Ic manycastserver Ar address ...
+This command enables reception of manycast client messages to
+the multicast group address(es) (type m) specified.
+At least one
+address is required, but the NTP multicast address 224.0.1.1
+assigned by the IANA should NOT be used, unless specific means are
+taken to limit the span of the reply and avoid a possibly massive
+implosion at the original sender.
+Note that, in order to avoid
+accidental or malicious disruption in this mode, both the server
+and client should operate using symmetric-key or public-key
+authentication as described in
+.Sx Authentication Options .
+.It Ic multicastclient Ar address ...
+This command enables reception of multicast server messages to
+the multicast group address(es) (type m) specified.
+Upon receiving
+a message for the first time, the multicast client measures the
+nominal server propagation delay using a brief client/server
+exchange with the server, then enters the broadcast client mode, in
+which it synchronizes to succeeding multicast messages.
+Note that,
+in order to avoid accidental or malicious disruption in this mode,
+both the server and client should operate using symmetric-key or
+public-key authentication as described in
+.Sx Authentication Options .
+.El
+.Sh Authentication Support
+Authentication support allows the NTP client to verify that the
+server is in fact known and trusted and not an intruder intending
+accidentally or on purpose to masquerade as that server.
+The NTPv3
+specification RFC-1305 defines a scheme which provides
+cryptographic authentication of received NTP packets.
+Originally,
+this was done using the Data Encryption Standard (DES) algorithm
+operating in Cipher Block Chaining (CBC) mode, commonly called
+DES-CBC.
+Subsequently, this was replaced by the RSA Message Digest
+5 (MD5) algorithm using a private key, commonly called keyed-MD5.
+Either algorithm computes a message digest, or one-way hash, which
+can be used to verify the server has the correct private key and
+key identifier.
+.Pp
+NTPv4 retains the NTPv3 scheme, properly described as symmetric key
+cryptography and, in addition, provides a new Autokey scheme
+based on public key cryptography.
+Public key cryptography is generally considered more secure
+than symmetric key cryptography, since the security is based
+on a private value which is generated by each server and
+never revealed.
+With Autokey all key distribution and
+management functions involve only public values, which
+considerably simplifies key distribution and storage.
+Public key management is based on X.509 certificates,
+which can be provided by commercial services or
+produced by utility programs in the OpenSSL software library
+or the NTPv4 distribution.
+.Pp
+While the algorithms for symmetric key cryptography are
+included in the NTPv4 distribution, public key cryptography
+requires the OpenSSL software library to be installed
+before building the NTP distribution.
+Directions for doing that
+are on the Building and Installing the Distribution page.
+.Pp
+Authentication is configured separately for each association
+using the
+.Cm key
+or
+.Cm autokey
+subcommand on the
+.Ic peer ,
+.Ic server ,
+.Ic broadcast
+and
+.Ic manycastclient
+configuration commands as described in
+.Sx Configuration Options
+page.
+The authentication
+options described below specify the locations of the key files,
+if other than default, which symmetric keys are trusted
+and the interval between various operations, if other than default.
+.Pp
+Authentication is always enabled,
+although ineffective if not configured as
+described below.
+If a NTP packet arrives
+including a message authentication
+code (MAC), it is accepted only if it
+passes all cryptographic checks.
+The
+checks require correct key ID, key value
+and message digest.
+If the packet has
+been modified in any way or replayed
+by an intruder, it will fail one or more
+of these checks and be discarded.
+Furthermore, the Autokey scheme requires a
+preliminary protocol exchange to obtain
+the server certificate, verify its
+credentials and initialize the protocol
+.Pp
+The
+.Cm auth
+flag controls whether new associations or
+remote configuration commands require cryptographic authentication.
+This flag can be set or reset by the
+.Ic enable
+and
+.Ic disable
+commands and also by remote
+configuration commands sent by a
+.Xr ntpdc 8
+program running in
+another machine.
+If this flag is enabled, which is the default
+case, new broadcast client and symmetric passive associations and
+remote configuration commands must be cryptographically
+authenticated using either symmetric key or public key cryptography.
+If this
+flag is disabled, these operations are effective
+even if not cryptographic
+authenticated.
+It should be understood
+that operating with the
+.Ic auth
+flag disabled invites a significant vulnerability
+where a rogue hacker can
+masquerade as a falseticker and seriously
+disrupt system timekeeping.
+It is
+important to note that this flag has no purpose
+other than to allow or disallow
+a new association in response to new broadcast
+and symmetric active messages
+and remote configuration commands and, in particular,
+the flag has no effect on
+the authentication process itself.
+.Pp
+An attractive alternative where multicast support is available
+is manycast mode, in which clients periodically troll
+for servers as described in the
+.Sx Automatic NTP Configuration Options
+page.
+Either symmetric key or public key
+cryptographic authentication can be used in this mode.
+The principle advantage
+of manycast mode is that potential servers need not be
+configured in advance,
+since the client finds them during regular operation,
+and the configuration
+files for all clients can be identical.
+.Pp
+The security model and protocol schemes for
+both symmetric key and public key
+cryptography are summarized below;
+further details are in the briefings, papers
+and reports at the NTP project page linked from
+.Li http://www.ntp.org/ .
+.Ss Symmetric-Key Cryptography
+The original RFC-1305 specification allows any one of possibly
+65,534 keys, each distinguished by a 32-bit key identifier, to
+authenticate an association.
+The servers and clients involved must
+agree on the key and key identifier to
+authenticate NTP packets.
+Keys and
+related information are specified in a key
+file, usually called
+.Pa ntp.keys ,
+which must be distributed and stored using
+secure means beyond the scope of the NTP protocol itself.
+Besides the keys used
+for ordinary NTP associations,
+additional keys can be used as passwords for the
+.Xr ntpq 8
+and
+.Xr ntpdc 8
+utility programs.
+.Pp
+When
+.Xr ntpd 8
+is first started, it reads the key file specified in the
+.Ic keys
+configuration command and installs the keys
+in the key cache.
+However,
+individual keys must be activated with the
+.Ic trusted
+command before use.
+This
+allows, for instance, the installation of possibly
+several batches of keys and
+then activating or deactivating each batch
+remotely using
+.Xr ntpdc 8 .
+This also provides a revocation capability that can be used
+if a key becomes compromised.
+The
+.Ic requestkey
+command selects the key used as the password for the
+.Xr ntpdc 8
+utility, while the
+.Ic controlkey
+command selects the key used as the password for the
+.Xr ntpq 8
+utility.
+.Ss Public Key Cryptography
+NTPv4 supports the original NTPv3 symmetric key scheme
+described in RFC-1305 and in addition the Autokey protocol,
+which is based on public key cryptography.
+The Autokey Version 2 protocol described on the Autokey Protocol
+page verifies packet integrity using MD5 message digests
+and verifies the source with digital signatures and any of several
+digest/signature schemes.
+Optional identity schemes described on the Identity Schemes
+page and based on cryptographic challenge/response algorithms
+are also available.
+Using all of these schemes provides strong security against
+replay with or without modification, spoofing, masquerade
+and most forms of clogging attacks.
+.\" .Pp
+.\" The cryptographic means necessary for all Autokey operations
+.\" is provided by the OpenSSL software library.
+.\" This library is available from http://www.openssl.org/
+.\" and can be installed using the procedures outlined
+.\" in the Building and Installing the Distribution page.
+.\" Once installed,
+.\" the configure and build
+.\" process automatically detects the library and links
+.\" the library routines required.
+.Pp
+The Autokey protocol has several modes of operation
+corresponding to the various NTP modes supported.
+Most modes use a special cookie which can be
+computed independently by the client and server,
+but encrypted in transmission.
+All modes use in addition a variant of the S-KEY scheme,
+in which a pseudo-random key list is generated and used
+in reverse order.
+These schemes are described along with an executive summary,
+current status, briefing slides and reading list on the
+.Sx Autonomous Authentication
+page.
+.Pp
+The specific cryptographic environment used by Autokey servers
+and clients is determined by a set of files
+and soft links generated by the
+.Xr ntp-keygen 8
+program.
+This includes a required host key file,
+required certificate file and optional sign key file,
+leapsecond file and identity scheme files.
+The
+digest/signature scheme is specified in the X.509 certificate
+along with the matching sign key.
+There are several schemes
+available in the OpenSSL software library, each identified
+by a specific string such as
+.Cm md5WithRSAEncryption ,
+which stands for the MD5 message digest with RSA
+encryption scheme.
+The current NTP distribution supports
+all the schemes in the OpenSSL library, including
+those based on RSA and DSA digital signatures.
+.Pp
+NTP secure groups can be used to define cryptographic compartments
+and security hierarchies.
+It is important that every host
+in the group be able to construct a certificate trail to one
+or more trusted hosts in the same group.
+Each group
+host runs the Autokey protocol to obtain the certificates
+for all hosts along the trail to one or more trusted hosts.
+This requires the configuration file in all hosts to be
+engineered so that, even under anticipated failure conditions,
+the NTP subnet will form such that every group host can find
+a trail to at least one trusted host.
+.Ss Naming and Addressing
+It is important to note that Autokey does not use DNS to
+resolve addresses, since DNS can't be completely trusted
+until the name servers have synchronized clocks.
+The cryptographic name used by Autokey to bind the host identity
+credentials and cryptographic values must be independent
+of interface, network and any other naming convention.
+The name appears in the host certificate in either or both
+the subject and issuer fields, so protection against
+DNS compromise is essential.
+.Pp
+By convention, the name of an Autokey host is the name returned
+by the Unix
+.Xr gethostname 2
+system call or equivalent in other systems.
+By the system design
+model, there are no provisions to allow alternate names or aliases.
+However, this is not to say that DNS aliases, different names
+for each interface, etc., are constrained in any way.
+.Pp
+It is also important to note that Autokey verifies authenticity
+using the host name, network address and public keys,
+all of which are bound together by the protocol specifically
+to deflect masquerade attacks.
+For this reason Autokey
+includes the source and destinatino IP addresses in message digest
+computations and so the same addresses must be available
+at both the server and client.
+For this reason operation
+with network address translation schemes is not possible.
+This reflects the intended robust security model where government
+and corporate NTP servers are operated outside firewall perimeters.
+.Ss Operation
+A specific combination of authentication scheme (none,
+symmetric key, public key) and identity scheme is called
+a cryptotype, although not all combinations are compatible.
+There may be management configurations where the clients,
+servers and peers may not all support the same cryptotypes.
+A secure NTPv4 subnet can be configured in many ways while
+keeping in mind the principles explained above and
+in this section.
+Note however that some cryptotype
+combinations may successfully interoperate with each other,
+but may not represent good security practice.
+.Pp
+The cryptotype of an association is determined at the time
+of mobilization, either at configuration time or some time
+later when a message of appropriate cryptotype arrives.
+When mobilized by a
+.Ic server
+or
+.Ic peer
+configuration command and no
+.Ic key
+or
+.Ic autokey
+subcommands are present, the association is not
+authenticated; if the
+.Ic key
+subcommand is present, the association is authenticated
+using the symmetric key ID specified; if the
+.Ic autokey
+subcommand is present, the association is authenticated
+using Autokey.
+.Pp
+When multiple identity schemes are supported in the Autokey
+protocol, the first message exchange determines which one is used.
+The client request message contains bits corresponding
+to which schemes it has available.
+The server response message
+contains bits corresponding to which schemes it has available.
+Both server and client match the received bits with their own
+and select a common scheme.
+.Pp
+Following the principle that time is a public value,
+a server responds to any client packet that matches
+its cryptotype capabilities.
+Thus, a server receiving
+an unauthenticated packet will respond with an unauthenticated
+packet, while the same server receiving a packet of a cryptotype
+it supports will respond with packets of that cryptotype.
+However, unconfigured broadcast or manycast client
+associations or symmetric passive associations will not be
+mobilized unless the server supports a cryptotype compatible
+with the first packet received.
+By default, unauthenticated associations will not be mobilized
+unless overridden in a decidedly dangerous way.
+.Pp
+Some examples may help to reduce confusion.
+Client Alice has no specific cryptotype selected.
+Server Bob has both a symmetric key file and minimal Autokey files.
+Alice's unauthenticated messages arrive at Bob, who replies with
+unauthenticated messages.
+Cathy has a copy of Bob's symmetric
+key file and has selected key ID 4 in messages to Bob.
+Bob verifies the message with his key ID 4.
+If it's the
+same key and the message is verified, Bob sends Cathy a reply
+authenticated with that key.
+If verification fails,
+Bob sends Cathy a thing called a crypto-NAK, which tells her
+something broke.
+She can see the evidence using the ntpq program.
+.Pp
+Denise has rolled her own host key and certificate.
+She also uses one of the identity schemes as Bob.
+She sends the first Autokey message to Bob and they
+both dance the protocol authentication and identity steps.
+If all comes out okay, Denise and Bob continue as described above.
+.Pp
+It should be clear from the above that Bob can support
+all the girls at the same time, as long as he has compatible
+authentication and identity credentials.
+Now, Bob can act just like the girls in his own choice of servers;
+he can run multiple configured associations with multiple different
+servers (or the same server, although that might not be useful).
+But, wise security policy might preclude some cryptotype
+combinations; for instance, running an identity scheme
+with one server and no authentication with another might not be wise.
+.Ss Key Management
+The cryptographic values used by the Autokey protocol are
+incorporated as a set of files generated by the
+.Xr ntp-keygen 8
+utility program, including symmetric key, host key and
+public certificate files, as well as sign key, identity parameters
+and leapseconds files.
+Alternatively, host and sign keys and
+certificate files can be generated by the OpenSSL utilities
+and certificates can be imported from public certificate
+authorities.
+Note that symmetric keys are necessary for the
+.Xr ntpq 8
+and
+.Xr ntpdc 8
+utility programs.
+The remaining files are necessary only for the
+Autokey protocol.
+.Pp
+Certificates imported from OpenSSL or public certificate
+authorities have certian limitations.
+The certificate should be in ASN.1 syntax, X.509 Version 3
+format and encoded in PEM, which is the same format
+used by OpenSSL.
+The overall length of the certificate encoded
+in ASN.1 must not exceed 1024 bytes.
+The subject distinguished
+name field (CN) is the fully qualified name of the host
+on which it is used; the remaining subject fields are ignored.
+The certificate extension fields must not contain either
+a subject key identifier or a issuer key identifier field;
+however, an extended key usage field for a trusted host must
+contain the value
+.Cm trustRoot ; .
+Other extension fields are ignored.
+.Ss Authentication Commands
+.Bl -tag -width indent
+.It Ic autokey Op Ar logsec
+Specifies the interval between regenerations of the session key
+list used with the Autokey protocol.
+Note that the size of the key
+list for each association depends on this interval and the current
+poll interval.
+The default value is 12 (4096 s or about 1.1 hours).
+For poll intervals above the specified interval, a session key list
+with a single entry will be regenerated for every message
+sent.
+.It Ic controlkey Ar key
+Specifies the key identifier to use with the
+.Xr ntpq 8
+utility, which uses the standard
+protocol defined in RFC-1305.
+The
+.Ar key
+argument is
+the key identifier for a trusted key, where the value can be in the
+range 1 to 65,534, inclusive.
+.It Xo Ic crypto
+.Op Cm cert Ar file
+.Op Cm leap Ar file
+.Op Cm randfile Ar file
+.Op Cm host Ar file
+.Op Cm sign Ar file
+.Op Cm gq Ar file
+.Op Cm gqpar Ar file
+.Op Cm iffpar Ar file
+.Op Cm mvpar Ar file
+.Op Cm pw Ar password
+.Xc
+This command requires the OpenSSL library.
+It activates public key
+cryptography, selects the message digest and signature
+encryption scheme and loads the required private and public
+values described above.
+If one or more files are left unspecified,
+the default names are used as described above.
+Unless the complete path and name of the file are specified, the
+location of a file is relative to the keys directory specified
+in the
+.Ic keysdir
+command or default
+.Pa /usr/local/etc .
+Following are the subcommands:
+.Bl -tag -width indent
+.It Cm cert Ar file
+Specifies the location of the required host public certificate file.
+This overrides the link
+.Pa ntpkey_cert_ Ns Ar hostname
+in the keys directory.
+.It Cm gqpar Ar file
+Specifies the location of the optional GQ parameters file.
+This
+overrides the link
+.Pa ntpkey_gq_ Ns Ar hostname
+in the keys directory.
+.It Cm host Ar file
+Specifies the location of the required host key file.
+This overrides
+the link
+.Pa ntpkey_key_ Ns Ar hostname
+in the keys directory.
+.It Cm iffpar Ar file
+Specifies the location of the optional IFF parameters file.This
+overrides the link
+.Pa ntpkey_iff_ Ns Ar hostname
+in the keys directory.
+.It Cm leap Ar file
+Specifies the location of the optional leapsecond file.
+This overrides the link
+.Pa ntpkey_leap
+in the keys directory.
+.It Cm mvpar Ar file
+Specifies the location of the optional MV parameters file.
+This
+overrides the link
+.Pa ntpkey_mv_ Ns Ar hostname
+in the keys directory.
+.It Cm pw Ar password
+Specifies the password to decrypt files containing private keys and
+identity parameters.
+This is required only if these files have been
+encrypted.
+.It Cm randfile Ar file
+Specifies the location of the random seed file used by the OpenSSL
+library.
+The defaults are described in the main text above.
+.It Cm sign Ar file
+Specifies the location of the optional sign key file.
+This overrides
+the link
+.Pa ntpkey_sign_ Ns Ar hostname
+in the keys directory.
+If this file is
+not found, the host key is also the sign key.
+.El
+.It Ic keys Ar keyfile
+Specifies the complete path and location of the MD5 key file
+containing the keys and key identifiers used by
+.Xr ntpd 8 ,
+.Xr ntpq 8
+and
+.Xr ntpdc
+when operating with symmetric key cryptography.
+This is the same operation as the
+.Fl k
+command line option.
+.It Ic keysdir Ar path
+This command specifies the default directory path for
+cryptographic keys, parameters and certificates.
+The default is
+.Pa /usr/local/etc/ .
+.It Ic requestkey Ar key
+Specifies the key identifier to use with the
+.Xr ntpdc 8
+utility program, which uses a
+proprietary protocol specific to this implementation of
+.Xr ntpd 8 .
+The
+.Ar key
+argument is a key identifier
+for the trusted key, where the value can be in the range 1 to
+65,534, inclusive.
+.It Ic revoke Ar logsec
+Specifies the interval between re-randomization of certain
+cryptographic values used by the Autokey scheme, as a power of 2 in
+seconds.
+These values need to be updated frequently in order to
+deflect brute-force attacks on the algorithms of the scheme;
+however, updating some values is a relatively expensive operation.
+The default interval is 16 (65,536 s or about 18 hours).
+For poll
+intervals above the specified interval, the values will be updated
+for every message sent.
+.It Ic trustedkey Ar key ...
+Specifies the key identifiers which are trusted for the
+purposes of authenticating peers with symmetric key cryptography,
+as well as keys used by the
+.Xr ntpq 8
+and
+.Xr ntpdc 8
+programs.
+The authentication procedures require that both the local
+and remote servers share the same key and key identifier for this
+purpose, although different keys can be used with different
+servers.
+The
+.Ar key
+arguments are 32-bit unsigned
+integers with values from 1 to 65,534.
+.El
+.Ss Error Codes
+The following error codes are reported via the NTP control
+and monitoring protocol trap mechanism.
+.Bl -tag -width indent
+.It 101
+.Pq bad field format or length
+The packet has invalid version, length or format.
+.It 102
+.Pq bad timestamp
+The packet timestamp is the same or older than the most recent received.
+This could be due to a replay or a server clock time step.
+.It 103
+.Pq bad filestamp
+The packet filestamp is the same or older than the most recent received.
+This could be due to a replay or a key file generation error.
+.It 104
+.Pq bad or missing public key
+The public key is missing, has incorrect format or is an unsupported type.
+.It 105
+.Pq unsupported digest type
+The server requires an unsupported digest/signature scheme.
+.It 106
+.Pq mismatched digest types
+Not used.
+.It 107
+.Pq bad signature length
+The signature length does not match the current public key.
+.It 108
+.Pq signature not verified
+The message fails the signature check.
+It could be bogus or signed by a
+different private key.
+.It 109
+.Pq certificate not verified
+The certificate is invalid or signed with the wrong key.
+.It 110
+.Pq certificate not verified
+The certificate is not yet valid or has expired or the signature could not
+be verified.
+.It 111
+.Pq bad or missing cookie
+The cookie is missing, corrupted or bogus.
+.It 112
+.Pq bad or missing leapseconds table
+The leapseconds table is missing, corrupted or bogus.
+.It 113
+.Pq bad or missing certificate
+The certificate is missing, corrupted or bogus.
+.It 114
+.Pq bad or missing identity
+The identity key is missing, corrupt or bogus.
+.El
+.Sh Monitoring Support
+.Xr ntpd 8
+includes a comprehensive monitoring facility suitable
+for continuous, long term recording of server and client
+timekeeping performance.
+See the
+.Ic statistics
+command below
+for a listing and example of each type of statistics currently
+supported.
+Statistic files are managed using file generation sets
+and scripts in the
+.Pa ./scripts
+directory of this distribution.
+Using
+these facilities and
+.Ux
+.Xr cron 8
+jobs, the data can be
+automatically summarized and archived for retrospective analysis.
+.Ss Monitoring Commands
+.Bl -tag -width indent
+.It Ic statistics Ar name ...
+Enables writing of statistics records.
+Currently, four kinds of
+.Ar name
+statistics are supported.
+.Bl -tag -width indent
+.It Cm clockstats
+Enables recording of clock driver statistics information.
+Each update
+received from a clock driver appends a line of the following form to
+the file generation set named
+.Cm clockstats :
+.Bd -literal
+49213 525.624 127.127.4.1 93 226 00:08:29.606 D
+.Ed
+.Pp
+The first two fields show the date (Modified Julian Day) and time
+(seconds and fraction past UTC midnight).
+The next field shows the
+clock address in dotted-quad notation.
+The final field shows the last
+timecode received from the clock in decoded ASCII format, where
+meaningful.
+In some clock drivers a good deal of additional information
+can be gathered and displayed as well.
+See information specific to each
+clock for further details.
+.It Cm cryptostats
+This option requires the OpenSSL cryptographic software library.
+It
+enables recording of cryptographic public key protocol information.
+Each message received by the protocol module appends a line of the
+following form to the file generation set named
+.Cm cryptostats :
+.Bd -literal
+49213 525.624 127.127.4.1 message
+.Ed
+.Pp
+The first two fields show the date (Modified Julian Day) and time
+(seconds and fraction past UTC midnight).
+The next field shows the peer
+address in dotted-quad notation, The final message field includes the
+message type and certain ancillary information.
+See the
+.Sx Authentication Options
+section for further information.
+.It Cm loopstats
+Enables recording of loop filter statistics information.
+Each
+update of the local clock outputs a line of the following form to
+the file generation set named
+.Cm loopstats :
+.Bd -literal
+50935 75440.031 0.000006019 13.778190 0.000351733 0.0133806
+.Ed
+.Pp
+The first two fields show the date (Modified Julian Day) and
+time (seconds and fraction past UTC midnight).
+The next five fields
+show time offset (seconds), frequency offset (parts per million -
+PPM), RMS jitter (seconds), Allan deviation (PPM) and clock
+discipline time constant.
+.It Cm peerstats
+Enables recording of peer statistics information.
+This includes
+statistics records of all peers of a NTP server and of special
+signals, where present and configured.
+Each valid update appends a
+line of the following form to the current element of a file
+generation set named
+.Cm peerstats :
+.Bd -literal
+48773 10847.650 127.127.4.1 9714 -0.001605376 0.000000000 0.001424877 0.000958674
+.Ed
+.Pp
+The first two fields show the date (Modified Julian Day) and
+time (seconds and fraction past UTC midnight).
+The next two fields
+show the peer address in dotted-quad notation and status,
+respectively.
+The status field is encoded in hex in the format
+described in Appendix A of the NTP specification RFC 1305.
+The final four fields show the offset,
+delay, dispersion and RMS jitter, all in seconds.
+.It Cm rawstats
+Enables recording of raw-timestamp statistics information.
+This
+includes statistics records of all peers of a NTP server and of
+special signals, where present and configured.
+Each NTP message
+received from a peer or clock driver appends a line of the
+following form to the file generation set named
+.Cm rawstats :
+.Bd -literal
+50928 2132.543 128.4.1.1 128.4.1.20 3102453281.584327000 3102453281.58622800031 02453332.540806000 3102453332.541458000
+.Ed
+.Pp
+The first two fields show the date (Modified Julian Day) and
+time (seconds and fraction past UTC midnight).
+The next two fields
+show the remote peer or clock address followed by the local address
+in dotted-quad notation.
+The final four fields show the originate,
+receive, transmit and final NTP timestamps in order.
+The timestamp
+values are as received and before processing by the various data
+smoothing and mitigation algorithms.
+.It Cm sysstats
+Enables recording of ntpd statistics counters on a periodic basis.
+Each
+hour a line of the following form is appended to the file generation
+set named
+.Cm sysstats :
+.Bd -literal
+50928 2132.543 36000 81965 0 9546 56 71793 512 540 10 147
+.Ed
+.Pp
+The first two fields show the date (Modified Julian Day) and time
+(seconds and fraction past UTC midnight).
+The remaining ten fields show
+the statistics counter values accumulated since the last generated
+line.
+.Bl -tag -width indent
+.It Time since restart Cm 36000
+Time in hours since the system was last rebooted.
+.It Packets received Cm 81965
+Total number of packets received.
+.It Packets processed Cm 0
+Number of packets received in response to previous packets sent
+.It Current version Cm 9546
+Number of packets matching the current NTP version.
+.It Previous version Cm 56
+Number of packets matching the previous NTP version.
+.It Bad version Cm 71793
+Number of packets matching neither NTP version.
+.It Access denied Cm 512
+Number of packets denied access for any reason.
+.It Bad length or format Cm 540
+Number of packets with invalid length, format or port number.
+.It Bad authentication Cm 10
+Number of packets not verified as authentic.
+.It Rate exceeded Cm 147
+Number of packets discarded due to rate limitation.
+.El
+.It Cm statsdir Ar directory_path
+Indicates the full path of a directory where statistics files
+should be created (see below).
+This keyword allows
+the (otherwise constant)
+.Cm filegen
+filename prefix to be modified for file generation sets, which
+is useful for handling statistics logs.
+.It Cm filegen Ar name Xo
+.Op Cm file Ar filename
+.Op Cm type Ar typename
+.Op Cm link | nolink
+.Op Cm enable | disable
+.Xc
+Configures setting of generation file set name.
+Generation
+file sets provide a means for handling files that are
+continuously growing during the lifetime of a server.
+Server statistics are a typical example for such files.
+Generation file sets provide access to a set of files used
+to store the actual data.
+At any time at most one element
+of the set is being written to.
+The type given specifies
+when and how data will be directed to a new element of the set.
+This way, information stored in elements of a file set
+that are currently unused are available for administrational
+operations without the risk of disturbing the operation of ntpd.
+(Most important: they can be removed to free space for new data
+produced.)
+.Pp
+Note that this command can be sent from the
+.Xr ntpdc 8
+program running at a remote location.
+.Bl -tag -width indent
+.It Cm name
+This is the type of the statistics records, as shown in the
+.Cm statistics
+command.
+.It Cm file Ar filename
+This is the file name for the statistics records.
+Filenames of set
+members are built from three concatenated elements
+.Ar Cm prefix ,
+.Ar Cm filename
+and
+.Ar Cm suffix :
+.Bl -tag -width indent
+.It Cm prefix
+This is a constant filename path.
+It is not subject to
+modifications via the
+.Ar filegen
+option.
+It is defined by the
+server, usually specified as a compile-time constant.
+It may,
+however, be configurable for individual file generation sets
+via other commands.
+For example, the prefix used with
+.Ar loopstats
+and
+.Ar peerstats
+generation can be configured using the
+.Ar statsdir
+option explained above.
+.It Cm filename
+This string is directly concatenated to the prefix mentioned
+above (no intervening
+.Ql / ) .
+This can be modified using
+the file argument to the
+.Ar filegen
+statement.
+No
+.Pa ..
+elements are
+allowed in this component to prevent filenames referring to
+parts outside the filesystem hierarchy denoted by
+.Ar prefix .
+.It Cm suffix
+This part is reflects individual elements of a file set.
+It is
+generated according to the type of a file set.
+.El
+.It Cm type Ar typename
+A file generation set is characterized by its type.
+The following
+types are supported:
+.Bl -tag -width indent
+.It Cm none
+The file set is actually a single plain file.
+.It Cm pid
+One element of file set is used per incarnation of a ntpd
+server.
+This type does not perform any changes to file set
+members during runtime, however it provides an easy way of
+separating files belonging to different
+.Xr ntpd 8
+server incarnations.
+The set member filename is built by appending a
+.Ql \&.
+to concatenated
+.Ar prefix
+and
+.Ar filename
+strings, and
+appending the decimal representation of the process ID of the
+.Xr ntpd 8
+server process.
+.It Cm day
+One file generation set element is created per day.
+A day is
+defined as the period between 00:00 and 24:00 UTC.
+The file set
+member suffix consists of a
+.Ql \&.
+and a day specification in
+the form
+.Cm YYYYMMdd .
+.Cm YYYY
+is a 4-digit year number (e.g., 1992).
+.Cm MM
+is a two digit month number.
+.Cm dd
+is a two digit day number.
+Thus, all information written at 10 December 1992 would end up
+in a file named
+.Ar prefix
+.Ar filename Ns .19921210 .
+.It Cm week
+Any file set member contains data related to a certain week of
+a year.
+The term week is defined by computing day-of-year
+modulo 7.
+Elements of such a file generation set are
+distinguished by appending the following suffix to the file set
+filename base: A dot, a 4-digit year number, the letter
+.Cm W ,
+and a 2-digit week number.
+For example, information from January,
+10th 1992 would end up in a file with suffix
+.No . Ns Ar 1992W1 .
+.It Cm month
+One generation file set element is generated per month.
+The
+file name suffix consists of a dot, a 4-digit year number, and
+a 2-digit month.
+.It Cm year
+One generation file element is generated per year.
+The filename
+suffix consists of a dot and a 4 digit year number.
+.It Cm age
+This type of file generation sets changes to a new element of
+the file set every 24 hours of server operation.
+The filename
+suffix consists of a dot, the letter
+.Cm a ,
+and an 8-digit number.
+This number is taken to be the number of seconds the server is
+running at the start of the corresponding 24-hour period.
+Information is only written to a file generation by specifying
+.Cm enable ;
+output is prevented by specifying
+.Cm disable .
+.El
+.It Cm link | nolink
+It is convenient to be able to access the current element of a file
+generation set by a fixed name.
+This feature is enabled by
+specifying
+.Cm link
+and disabled using
+.Cm nolink .
+If link is specified, a
+hard link from the current file set element to a file without
+suffix is created.
+When there is already a file with this name and
+the number of links of this file is one, it is renamed appending a
+dot, the letter
+.Cm C ,
+and the pid of the ntpd server process.
+When the
+number of links is greater than one, the file is unlinked.
+This
+allows the current file to be accessed by a constant name.
+.It Cm enable \&| Cm disable
+Enables or disables the recording function.
+.El
+.El
+.El
+.Sh Access Control Support
+The
+.Xr ntpd 8
+daemon implements a general purpose address/mask based restriction
+list.
+The list contains address/match entries sorted first
+by increasing address values and and then by increasing mask values.
+A match occurs when the bitwise AND of the mask and the packet
+source address is equal to the bitwise AND of the mask and
+address in the list.
+The list is searched in order with the
+last match found defining the restriction flags associated
+with the entry.
+Additional information and examples can be found in the
+.Qq Notes on Configuring NTP and Setting up a NTP Subnet
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+.Pp
+The restriction facility was implemented in conformance
+with the access policies for the original NSFnet backbone
+time servers.
+Later the facility was expanded to deflect
+cryptographic and clogging attacks.
+While this facility may
+be useful for keeping unwanted or broken or malicious clients
+from congesting innocent servers, it should not be considered
+an alternative to the NTP authentication facilities.
+Source address based restrictions are easily circumvented
+by a determined cracker.
+.Pp
+Clients can be denied service because they are explicitly
+included in the restrict list created by the restrict command
+or implicitly as the result of cryptographic or rate limit
+violations.
+Cryptographic violations include certificate
+or identity verification failure; rate limit violations generally
+result from defective NTP implementations that send packets
+at abusive rates.
+Some violations cause denied service
+only for the offending packet, others cause denied service
+for a timed period and others cause the denied service for
+an indefinate period.
+When a client or network is denied access
+for an indefinate period, the only way at present to remove
+the restrictions is by restarting the server.
+.Ss The Kiss-of-Death Packet
+Ordinarily, packets denied service are simply dropped with no
+further action except incrementing statistics counters.
+Sometimes a
+more proactive response is needed, such as a server message that
+explicitly requests the client to stop sending and leave a message
+for the system operator.
+A special packet format has been created
+for this purpose called the "kiss-of-death" (KoD) packet.
+KoD packets have the leap bits set unsynchronized and stratum set
+to zero and the reference identifier field set to a four-byte
+ASCII code.
+If the
+.Cm noserve
+or
+.Cm notrust
+flag of the matching restrict list entry is set,
+the code is "DENY"; if the
+.Cm limited
+flag is set and the rate limit
+is exceeded, the code is "RATE".
+Finally, if a cryptographic violation occurs, the code is "CRYP".
+.Pp
+A client receiving a KoD performs a set of sanity checks to
+minimize security exposure, then updates the stratum and
+reference identifier peer variables, sets the access
+denied (TEST4) bit in the peer flash variable and sends
+a message to the log.
+As long as the TEST4 bit is set,
+the client will send no further packets to the server.
+The only way at present to recover from this condition is
+to restart the protocol at both the client and server.
+This
+happens automatically at the client when the association times out.
+It will happen at the server only if the server operator cooperates.
+.Ss Access Control Commands
+.Bl -tag -width indent
+.It Xo Ic discard
+.Op Cm average Ar avg
+.Op Cm minimum Ar min
+.Op Cm monitor Ar prob
+.Xc
+Set the parameters of the
+.Cm limited
+facility which protects the server from
+client abuse.
+The
+.Cm average
+subcommand specifies the minimum average packet
+spacing, while the
+.Cm minimum
+subcommand specifies the minimum packet spacing.
+Packets that violate these minima are discarded
+and a kiss-o'-death packet returned if enabled.
+The default
+minimum average and minimum are 5 and 2, respectively.
+The monitor subcommand specifies the probability of discard
+for packets that overflow the rate-control window.
+.It Xo Ic restrict address
+.Op Cm mask Ar mask
+.Op Ar flag ...
+.Xc
+The
+.Ar address
+argument expressed in
+dotted-quad form is the address of a host or network.
+Alternatively, the
+.Ar address
+argument can be a valid host DNS name.
+The
+.Ar mask
+argument expressed in dotted-quad form defaults to
+.Cm 255.255.255.255 ,
+meaning that the
+.Ar address
+is treated as the address of an individual host.
+A default entry (address
+.Cm 0.0.0.0 ,
+mask
+.Cm 0.0.0.0 )
+is always included and is always the first entry in the list.
+Note that text string
+.Cm default ,
+with no mask option, may
+be used to indicate the default entry.
+In the current implementation,
+.Cm flag
+always
+restricts access, i.e., an entry with no flags indicates that free
+access to the server is to be given.
+The flags are not orthogonal,
+in that more restrictive flags will often make less restrictive
+ones redundant.
+The flags can generally be classed into two
+categories, those which restrict time service and those which
+restrict informational queries and attempts to do run-time
+reconfiguration of the server.
+One or more of the following flags
+may be specified:
+.Bl -tag -width indent
+.It Cm ignore
+Deny packets of all kinds, including
+.Xr ntpq 8
+and
+.Xr ntpdc 8
+queries.
+.It Cm kod
+If this flag is set when an access violation occurs, a kiss-o'-death
+(KoD) packet is sent.
+KoD packets are rate limited to no more than one
+per second.
+If another KoD packet occurs within one second after the
+last one, the packet is dropped.
+.It Cm limited
+Deny service if the packet spacing violates the lower limits specified
+in the discard command.
+A history of clients is kept using the
+monitoring capability of
+.Xr ntpd 8 .
+Thus, monitoring is always active as
+long as there is a restriction entry with the
+.Cm limited
+flag.
+.It Cm lowpriotrap
+Declare traps set by matching hosts to be low priority.
+The
+number of traps a server can maintain is limited (the current limit
+is 3).
+Traps are usually assigned on a first come, first served
+basis, with later trap requestors being denied service.
+This flag
+modifies the assignment algorithm by allowing low priority traps to
+be overridden by later requests for normal priority traps.
+.It Cm nomodify
+Deny
+.Xr ntpq 8
+and
+.Xr ntpdc 8
+queries which attempt to modify the state of the
+server (i.e., run time reconfiguration).
+Queries which return
+information are permitted.
+.It Cm noquery
+Deny
+.Xr ntpq 8
+and
+.Xr ntpdc 8
+queries.
+Time service is not affected.
+.It Cm nopeer
+Deny packets which would result in mobilizing a new association.
+This
+includes broadcast and symmetric active packets when a configured
+association does not exist.
+.It Cm noserve
+Deny all packets except
+.Xr ntpq 8
+and
+.Xr ntpdc 8
+queries.
+.It Cm notrap
+Decline to provide mode 6 control message trap service to matching
+hosts.
+The trap service is a subsystem of the ntpdq control message
+protocol which is intended for use by remote event logging programs.
+.It Cm notrust
+Deny service unless the packet is cryptographically authenticated.
+.It Cm ntpport
+This is actually a match algorithm modifier, rather than a
+restriction flag.
+Its presence causes the restriction entry to be
+matched only if the source port in the packet is the standard NTP
+UDP port (123).
+Both
+.Cm ntpport
+and
+.Cm non-ntpport
+may
+be specified.
+The
+.Cm ntpport
+is considered more specific and
+is sorted later in the list.
+.It Cm version
+Deny packets that do not match the current NTP version.
+.El
+.Pp
+Default restriction list entries with the flags ignore, interface,
+ntpport, for each of the local host's interface addresses are
+inserted into the table at startup to prevent the server
+from attempting to synchronize to its own time.
+A default entry is also always present, though if it is
+otherwise unconfigured; no flags are associated
+with the default entry (i.e., everything besides your own
+NTP server is unrestricted).
+.El
+.Sh Automatic NTP Configuration Options
+.Ss Manycasting
+Manycasting is a automatic discovery and configuration paradigm
+new to NTPv4.
+It is intended as a means for a multicast client
+to troll the nearby network neighborhood to find cooperating
+manycast servers, validate them using cryptographic means
+and evaluate their time values with respect to other servers
+that might be lurking in the vicinity.
+The intended result is that each manycast client mobilizes
+client associations with some number of the "best"
+of the nearby manycast servers, yet automatically reconfigures
+to sustain this number of servers should one or another fail.
+.Pp
+Note that the manycasting paradigm does not coincide
+with the anycast paradigm described in RFC-1546,
+which is designed to find a single server from a clique
+of servers providing the same service.
+The manycast paradigm is designed to find a plurality
+of redundant servers satisfying defined optimality criteria.
+.Pp
+Manycasting can be used with either symmetric key
+or public key cryptography.
+The public key infrastructure (PKI)
+offers the best protection against compromised keys
+and is generally considered stronger, at least with relatively
+large key sizes.
+It is implemented using the Autokey protocol and
+the OpenSSL cryptographic library available from
+.Li http://www.openssl.org/ .
+The library can also be used with other NTPv4 modes
+as well and is highly recommended, especially for broadcast modes.
+.Pp
+A persistent manycast client association is configured
+using the manycastclient command, which is similar to the
+server command but with a multicast (IPv4 class
+.Cm D
+or IPv6 prefix
+.Cm FF )
+group address.
+The IANA has designated IPv4 address 224.1.1.1
+and IPv6 address FF05::101 (site local) for NTP.
+When more servers are needed, it broadcasts manycast
+client messages to this address at the minimum feasible rate
+and minimum feasible time-to-live (TTL) hops, depending
+on how many servers have already been found.
+There can be as many manycast client associations
+as different group address, each one serving as a template
+for a future ephemeral unicast client/server association.
+.Pp
+Manycast servers configured with the
+.Ic manycastserver
+command listen on the specified group address for manycast
+client messages.
+Note the distinction between manycast client,
+which actively broadcasts messages, and manycast server,
+which passively responds to them.
+If a manycast server is
+in scope of the current TTL and is itself synchronized
+to a valid source and operating at a stratum level equal
+to or lower than the manycast client, it replies to the
+manycast client message with an ordinary unicast server message.
+.Pp
+The manycast client receiving this message mobilizes
+an ephemeral client/server association according to the
+matching manycast client template, but only if cryptographically
+authenticated and the server stratum is less than or equal
+to the client stratum.
+Authentication is explicitly required
+and either symmetric key or public key (Autokey) can be used.
+Then, the client polls the server at its unicast address
+in burst mode in order to reliably set the host clock
+and validate the source.
+This normally results
+in a volley of eight client/server at 2-s intervals
+during which both the synchronization and cryptographic
+protocols run concurrently.
+Following the volley,
+the client runs the NTP intersection and clustering
+algorithms, which act to discard all but the "best"
+associations according to stratum and synchronization
+distance.
+The surviving associations then continue
+in ordinary client/server mode.
+.Pp
+The manycast client polling strategy is designed to reduce
+as much as possible the volume of manycast client messages
+and the effects of implosion due to near-simultaneous
+arrival of manycast server messages.
+The strategy is determined by the
+.Ic manycastclient ,
+.Ic tos
+and
+.Ic ttl
+configuration commands.
+The manycast poll interval is
+normally eight times the system poll interval,
+which starts out at the
+.Cm minpoll
+value specified in the
+.Ic manycastclient ,
+command and, under normal circumstances, increments to the
+.Cm maxpolll
+value specified in this command.
+Initially, the TTL is
+set at the minimum hops specified by the ttl command.
+At each retransmission the TTL is increased until reaching
+the maximum hops specified by this command or a sufficient
+number client associations have been found.
+Further retransmissions use the same TTL.
+.Pp
+The quality and reliability of the suite of associations
+discovered by the manycast client is determined by the NTP
+mitigation algorithms and the
+.Cm minclock
+and
+.Cm minsane
+values specified in the
+.Ic tos
+configuration command.
+At least
+.Cm minsane
+candidate servers must be available and the mitigation
+algorithms produce at least
+.Cm minclock
+survivors in order to synchronize the clock.
+Byzantine agreement principles require at least four
+candidates in order to correctly discard a single falseticker.
+For legacy purposes,
+.Cm minsane
+defaults to 1 and
+.Cm minclock
+defaults to 3.
+For manycast service
+.Cm minsane
+should be explicitly set to 4, assuming at least that
+number of servers are available.
+.Pp
+If at least
+.Cm minclock
+servers are found, the manycast poll interval is immediately
+set to eight times
+.Cm maxpoll .
+If less than
+.Cm minclock
+servers are found when the TTL has reached the maximum hops,
+the manycast poll interval is doubled.
+For each transmission
+after that, the poll interval is doubled again until
+reaching the maximum of eight times
+.Cm maxpoll .
+Further transmissions use the same poll interval and
+TTL values.
+Note that while all this is going on,
+each client/server association found is operating normally
+it the system poll interval.
+.Pp
+Administratively scoped multicast boundaries are normally
+specified by the network router configuration and,
+in the case of IPv6, the link/site scope prefix.
+By default, the increment for TTL hops is 32 starting
+from 31; however, the
+.Ic ttl
+configuration command can be
+used to modify the values to match the scope rules.
+.Pp
+It is often useful to narrow the range of acceptable
+servers which can be found by manycast client associations.
+Because manycast servers respond only when the client
+stratum is equal to or greater than the server stratum,
+primary (stratum 1) servers fill find only primary servers
+in TTL range, which is probably the most common objective.
+However, unless configured otherwise, all manycast clients
+in TTL range will eventually find all primary servers
+in TTL range, which is probably not the most common
+objective in large networks.
+The
+.Ic tos
+command can be used to modify this behavior.
+Servers with stratum below
+.Cm floor
+or above
+.Cm ceiling
+specified in the
+.Ic tos
+command are strongly discouraged during the selection
+process; however, these servers may be temporally
+accepted if the number of servers within TTL range is
+less than
+.Cm minclock .
+.Pp
+The above actions occur for each manycast client message,
+which repeats at the designated poll interval.
+However, once the ephemeral client association is mobilized,
+subsequent manycast server replies are discarded,
+since that would result in a duplicate association.
+If during a poll interval the number of client associations
+falls below
+.Cm minclock ,
+all manycast client prototype associations are reset
+to the initial poll interval and TTL hops and operation
+resumes from the beginning.
+It is important to avoid
+frequent manycast client messages, since each one requires
+all manycast servers in TTL range to respond.
+The result could well be an implosion, either minor or major,
+depending on the number of servers in range.
+The recommended value for
+.Cm maxpoll
+is 12 (4,096 s).
+.Pp
+It is possible and frequently useful to configure a host
+as both manycast client and manycast server.
+A number of hosts configured this way and sharing a common
+group address will automatically organize themselves
+in an optimum configuration based on stratum and
+synchronization distance.
+For example, consider an NTP
+subnet of two primary servers and a hundred or more
+dependent clients.
+With two exceptions, all servers
+and clients have identical configuration files including both
+.Ic multicastclient
+and
+.Ic multicastserver
+commands using, for instance, multicast group address
+239.1.1.1.
+The only exception is that each primary server
+configuration file must include commands for the primary
+reference source such as a GPS receiver.
+.Pp
+The remaining configuration files for all secondary
+servers and clients have the same contents, except for the
+.Ic tos
+command, which is specific for each stratum level.
+For stratum 1 and stratum 2 servers, that command is
+not necessary.
+For stratum 3 and above servers the
+.Cm floor
+value is set to the intended stratum number.
+Thus, all stratum 3 configuration files are identical,
+all stratum 4 files are identical and so forth.
+.Pp
+Once operations have stabilized in this scenario,
+the primary servers will find the primary reference source
+and each other, since they both operate at the same
+stratum (1), but not with any secondary server or client,
+since these operate at a higher stratum.
+The secondary
+servers will find the servers at the same stratum level.
+If one of the primary servers loses its GPS receiver,
+it will continue to operate as a client and other clients
+will time out the corresponding association and
+re-associate accordingly.
+.Pp
+Some administrators prefer to avoid running
+.Xr ntpd 8
+continuously and run either
+.Xr ntpdate 8
+or
+.Xr ntpd 8
+.Fl q
+as a cron job.
+In either case the servers must be
+configured in advance and the program fails if none are
+available when the cron job runs.
+A really slick
+application of manycast is with
+.Xr ntpd 8
+.Fl q .
+The program wakes up, scans the local landscape looking
+for the usual suspects, selects the best from among
+the rascals, sets the clock and then departs.
+Servers do not have to be configured in advance and
+all clients throughout the network can have the same
+configuration file.
+.Ss Manycast Interactions with Autokey
+Each time a manycast client sends a client mode packet
+to a multicast group address, all manycast servers
+in scope generate a reply including the host name
+and status word.
+The manycast clients then run
+the Autokey protocol, which collects and verifies
+all certificates involved.
+Following the burst interval
+all but three survivors are cast off,
+but the certificates remain in the local cache.
+It often happens that several complete signing trails
+from the client to the primary servers are collected in this way.
+.Pp
+About once an hour or less often if the poll interval
+exceeds this, the client regenerates the Autokey key list.
+This is in general transparent in client/server mode.
+However, about once per day the server private value
+used to generate cookies is refreshed along with all
+manycast client associations.
+In this case all
+cryptographic values including certificates is refreshed.
+If a new certificate has been generated since
+the last refresh epoch, it will automatically revoke
+all prior certificates that happen to be in the
+certificate cache.
+At the same time, the manycast
+scheme starts all over from the beginning and
+the expanding ring shrinks to the minimum and increments
+from there while collecting all servers in scope.
+.Ss Manycast Options
+.Bl -tag -width indent
+.It Xo Ic tos
+.Oo
+.Cm ceiling Ar ceiling |
+.Cm cohort { 0 | 1 } |
+.Cm floor Ar floor |
+.Cm minclock Ar minclock |
+.Cm minsane Ar minsane
+.Oc
+.Xc
+This command affects the clock selection and clustering
+algorithms.
+It can be used to select the quality and
+quantity of peers used to synchronize the system clock
+and is most useful in manycast mode.
+The variables operate
+as follows:
+.Bl -tag -width indent
+.It Cm ceiling Ar ceiling
+Peers with strata above
+.Cm ceiling
+will be discarded if there are at least
+.Cm minclock
+peers remaining.
+This value defaults to 15, but can be changed
+to any number from 1 to 15.
+.It Cm cohort Bro 0 | 1 Brc
+This is a binary flag which enables (0) or disables (1)
+manycast server replies to manycast clients with the same
+stratum level.
+This is useful to reduce implosions where
+large numbers of clients with the same stratum level
+are present.
+The default is to enable these replies.
+.It Cm floor Ar floor
+Peers with strata below
+.Cm floor
+will be discarded if there are at least
+.Cm minclock
+peers remaining.
+This value defaults to 1, but can be changed
+to any number from 1 to 15.
+.It Cm minclock Ar minclock
+The clustering algorithm repeatedly casts out outlyer
+associations until no more than
+.Cm minclock
+associations remain.
+This value defaults to 3,
+but can be changed to any number from 1 to the number of
+configured sources.
+.It Cm minsane Ar minsane
+This is the minimum number of candidates available
+to the clock selection algorithm in order to produce
+one or more truechimers for the clustering algorithm.
+If fewer than this number are available, the clock is
+undisciplined and allowed to run free.
+The default is 1
+for legacy purposes.
+However, according to principles of
+Byzantine agreement,
+.Cm minsane
+should be at least 4 in order to detect and discard
+a single falseticker.
+.El
+.It Cm ttl Ar hop ...
+This command specifies a list of TTL values in increasing
+order, up to 8 values can be specified.
+In manycast mode these values are used in turn
+in an expanding-ring search.
+The default is eight
+multiples of 32 starting at 31.
+.El
+.Sh Reference Clock Support
+The NTP Version 4 daemon supports some three dozen different radio,
+satellite and modem reference clocks plus a special pseudo-clock
+used for backup or when no other clock source is available.
+Detailed descriptions of individual device drivers and options can
+be found in the
+.Qq Reference Clock Drivers
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+Additional information can be found in the pages linked
+there, including the
+.Qq Debugging Hints for Reference Clock Drivers
+and
+.Qq How To Write a Reference Clock Driver
+pages
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+In addition, support for a PPS
+signal is available as described in the
+.Qq Pulse-per-second (PPS) Signal Interfacing
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+Many
+drivers support special line discipline/streams modules which can
+significantly improve the accuracy using the driver.
+These are
+described in the
+.Qq Line Disciplines and Streams Drivers
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+.Pp
+A reference clock will generally (though not always) be a radio
+timecode receiver which is synchronized to a source of standard
+time such as the services offered by the NRC in Canada and NIST and
+USNO in the US.
+The interface between the computer and the timecode
+receiver is device dependent, but is usually a serial port.
+A
+device driver specific to each reference clock must be selected and
+compiled in the distribution; however, most common radio, satellite
+and modem clocks are included by default.
+Note that an attempt to
+configure a reference clock when the driver has not been compiled
+or the hardware port has not been appropriately configured results
+in a scalding remark to the system log file, but is otherwise non
+hazardous.
+.Pp
+For the purposes of configuration,
+.Xr ntpd 8
+treats
+reference clocks in a manner analogous to normal NTP peers as much
+as possible.
+Reference clocks are identified by a syntactically
+correct but invalid IP address, in order to distinguish them from
+normal NTP peers.
+Reference clock addresses are of the form
+.Sm off
+.Li 127.127. Ar t . Ar u ,
+.Sm on
+where
+.Ar t
+is an integer
+denoting the clock type and
+.Ar u
+indicates the unit
+number in the range 0-3.
+While it may seem overkill, it is in fact
+sometimes useful to configure multiple reference clocks of the same
+type, in which case the unit numbers must be unique.
+.Pp
+The
+.Ic server
+command is used to configure a reference
+clock, where the
+.Ar address
+argument in that command
+is the clock address.
+The
+.Cm key ,
+.Cm version
+and
+.Cm ttl
+options are not used for reference clock support.
+The
+.Cm mode
+option is added for reference clock support, as
+described below.
+The
+.Cm prefer
+option can be useful to
+persuade the server to cherish a reference clock with somewhat more
+enthusiasm than other reference clocks or peers.
+Further
+information on this option can be found in the
+.Qq Mitigation Rules and the prefer Keyword
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp )
+page.
+The
+.Cm minpoll
+and
+.Cm maxpoll
+options have
+meaning only for selected clock drivers.
+See the individual clock
+driver document pages for additional information.
+.Pp
+The
+.Ic fudge
+command is used to provide additional
+information for individual clock drivers and normally follows
+immediately after the
+.Ic server
+command.
+The
+.Ar address
+argument specifies the clock address.
+The
+.Cm refid
+and
+.Cm stratum
+options can be used to
+override the defaults for the device.
+There are two optional
+device-dependent time offsets and four flags that can be included
+in the
+.Ic fudge
+command as well.
+.Pp
+The stratum number of a reference clock is by default zero.
+Since the
+.Xr ntpd 8
+daemon adds one to the stratum of each
+peer, a primary server ordinarily displays an external stratum of
+one.
+In order to provide engineered backups, it is often useful to
+specify the reference clock stratum as greater than zero.
+The
+.Cm stratum
+option is used for this purpose.
+Also, in cases
+involving both a reference clock and a pulse-per-second (PPS)
+discipline signal, it is useful to specify the reference clock
+identifier as other than the default, depending on the driver.
+The
+.Cm refid
+option is used for this purpose.
+Except where noted,
+these options apply to all clock drivers.
+.Ss Reference Clock Commands
+.Bl -tag -width indent
+.It Xo Ic server
+.Sm off
+.Li 127.127. Ar t . Ar u
+.Sm on
+.Op Cm prefer
+.Op Cm mode Ar int
+.Op Cm minpoll Ar int
+.Op Cm maxpoll Ar int
+.Xc
+This command can be used to configure reference clocks in
+special ways.
+The options are interpreted as follows:
+.Bl -tag -width indent
+.It Cm prefer
+Marks the reference clock as preferred.
+All other things being
+equal, this host will be chosen for synchronization among a set of
+correctly operating hosts.
+See the
+.Qq Mitigation Rules and the prefer Keyword
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp )
+for further information.
+.It Cm mode Ar int
+Specifies a mode number which is interpreted in a
+device-specific fashion.
+For instance, it selects a dialing
+protocol in the ACTS driver and a device subtype in the
+parse
+drivers.
+.It Cm minpoll Ar int
+.It Cm maxpoll Ar int
+These options specify the minimum and maximum polling interval
+for reference clock messages, as a power of 2 in seconds
+For
+most directly connected reference clocks, both
+.Cm minpoll
+and
+.Cm maxpoll
+default to 6 (64 s).
+For modem reference clocks,
+.Cm minpoll
+defaults to 10 (17.1 m) and
+.Cm maxpoll
+defaults to 14 (4.5 h).
+The allowable range is 4 (16 s) to 17 (36.4 h) inclusive.
+.El
+.It Xo Ic fudge
+.Sm off
+.Li 127.127. Ar t . Ar u
+.Sm on
+.Op Cm time1 Ar sec
+.Op Cm time2 Ar sec
+.Op Cm stratum Ar int
+.Op Cm refid Ar string
+.Op Cm mode Ar int
+.Op Cm flag1 Cm 0 \&| Cm 1
+.Op Cm flag2 Cm 0 \&| Cm 1
+.Op Cm flag3 Cm 0 \&| Cm 1
+.Op Cm flag4 Cm 0 \&| Cm 1
+.Xc
+This command can be used to configure reference clocks in
+special ways.
+It must immediately follow the
+.Ic server
+command which configures the driver.
+Note that the same capability
+is possible at run time using the
+.Xr ntpdc 8
+program.
+The options are interpreted as
+follows:
+.Bl -tag -width indent
+.It Cm time1 Ar sec
+Specifies a constant to be added to the time offset produced by
+the driver, a fixed-point decimal number in seconds.
+This is used
+as a calibration constant to adjust the nominal time offset of a
+particular clock to agree with an external standard, such as a
+precision PPS signal.
+It also provides a way to correct a
+systematic error or bias due to serial port or operating system
+latencies, different cable lengths or receiver internal delay.
+The
+specified offset is in addition to the propagation delay provided
+by other means, such as internal DIPswitches.
+Where a calibration
+for an individual system and driver is available, an approximate
+correction is noted in the driver documentation pages.
+Note: in order to facilitate calibration when more than one
+radio clock or PPS signal is supported, a special calibration
+feature is available.
+It takes the form of an argument to the
+.Ic enable
+command described in
+.Sx Miscellaneous Options
+page and operates as described in the
+.Qq Reference Clock Drivers
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+.It Cm time2 Ar secs
+Specifies a fixed-point decimal number in seconds, which is
+interpreted in a driver-dependent way.
+See the descriptions of
+specific drivers in the
+.Qq Reference Clock Drivers
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+.It Cm stratum Ar int
+Specifies the stratum number assigned to the driver, an integer
+between 0 and 15.
+This number overrides the default stratum number
+ordinarily assigned by the driver itself, usually zero.
+.It Cm refid Ar string
+Specifies an ASCII string of from one to four characters which
+defines the reference identifier used by the driver.
+This string
+overrides the default identifier ordinarily assigned by the driver
+itself.
+.It Cm mode Ar int
+Specifies a mode number which is interpreted in a
+device-specific fashion.
+For instance, it selects a dialing
+protocol in the ACTS driver and a device subtype in the
+parse
+drivers.
+.It Cm flag1 Cm 0 \&| Cm 1
+.It Cm flag2 Cm 0 \&| Cm 1
+.It Cm flag3 Cm 0 \&| Cm 1
+.It Cm flag4 Cm 0 \&| Cm 1
+These four flags are used for customizing the clock driver.
+The
+interpretation of these values, and whether they are used at all,
+is a function of the particular clock driver.
+However, by
+convention
+.Cm flag4
+is used to enable recording monitoring
+data to the
+.Cm clockstats
+file configured with the
+.Ic filegen
+command.
+Further information on the
+.Ic filegen
+command can be found in
+.Sx Monitoring Options .
+.El
+.El
+.Sh Miscellaneous Options
+.Bl -tag -width indent
+.It Ic broadcastdelay Ar seconds
+The broadcast and multicast modes require a special calibration
+to determine the network delay between the local and remote
+servers.
+Ordinarily, this is done automatically by the initial
+protocol exchanges between the client and server.
+In some cases,
+the calibration procedure may fail due to network or server access
+controls, for example.
+This command specifies the default delay to
+be used under these circumstances.
+Typically (for Ethernet), a
+number between 0.003 and 0.007 seconds is appropriate.
+The default
+when this command is not used is 0.004 seconds.
+.It Ic calldelay Ar delay
+This option controls the delay in seconds between the first and second
+packets sent in burst or iburst mode to allow additional time for a modem
+or ISDN call to complete.
+.It Ic driftfile Ar driftfile
+This command specifies the complete path and name of the file used to
+record the frequency of the local clock oscillator.
+This is the same
+operation as the
+.Fl f
+command line option.
+If the file exists, it is read at
+startup in order to set the initial frequency and then updated once per
+hour with the current frequency computed by the daemon.
+If the file name is
+specified, but the file itself does not exist, the starts with an initial
+frequency of zero and creates the file when writing it for the first time.
+If this command is not given, the daemon will always start with an initial
+frequency of zero.
+.Pp
+The file format consists of a single line containing a single
+floating point number, which records the frequency offset measured
+in parts-per-million (PPM).
+The file is updated by first writing
+the current drift value into a temporary file and then renaming
+this file to replace the old version.
+This implies that
+.Xr ntpd 8
+must have write permission for the directory the
+drift file is located in, and that file system links, symbolic or
+otherwise, should be avoided.
+.It Xo Ic enable
+.Oo
+.Cm auth | Cm bclient |
+.Cm calibrate | Cm kernel |
+.Cm monitor | Cm ntp |
+.Cm pps | Cm stats
+.Oc
+.Xc
+.It Xo Ic disable
+.Oo
+.Cm auth | Cm bclient |
+.Cm calibrate | Cm kernel |
+.Cm monitor | Cm ntp |
+.Cm pps | Cm stats
+.Oc
+.Xc
+Provides a way to enable or disable various server options.
+Flags not mentioned are unaffected.
+Note that all of these flags
+can be controlled remotely using the
+.Xr ntpdc 8
+utility program.
+.Bl -tag -width indent
+.It Cm auth
+Enables the server to synchronize with unconfigured peers only if the
+peer has been correctly authenticated using either public key or
+private key cryptography.
+The default for this flag is
+.Ic enable .
+.It Cm bclient
+Enables the server to listen for a message from a broadcast or
+multicast server, as in the
+.Ic multicastclient
+command with default
+address.
+The default for this flag is
+.Ic disable .
+.It Cm calibrate
+Enables the calibrate feature for reference clocks.
+The default for
+this flag is
+.Ic disable .
+.It Cm kernel
+Enables the kernel time discipline, if available.
+The default for this
+flag is
+.Ic enable
+if support is available, otherwise
+.Ic disable .
+.It Cm monitor
+Enables the monitoring facility.
+See the
+.Xr ntpdc 8
+program
+and the
+.Ic monlist
+command or further information.
+The
+default for this flag is
+.Ic enable .
+.It Cm ntp
+Enables time and frequency discipline.
+In effect, this switch opens and
+closes the feedback loop, which is useful for testing.
+The default for
+this flag is
+.Ic enable .
+.It Cm pps
+Enables the pulse-per-second (PPS) signal when frequency and time is
+disciplined by the precision time kernel modifications.
+See the
+.Qq A Kernel Model for Precision Timekeeping
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp )
+page for further information.
+The default for this flag is
+.Ic disable .
+.It Cm stats
+Enables the statistics facility.
+See the
+.Sx Monitoring Options
+section for further information.
+The default for this flag is
+.Ic disable .
+.El
+.It Ic includefile Ar includefile
+This command allows additional configuration commands
+to be included from a separate file.
+Include files may
+be nested to a depth of five; upon reaching the end of any
+include file, command processing resumes in the previous
+configuration file.
+This option is useful for sites that run
+.Xr ntpd 8
+on multiple hosts, with (mostly) common options (e.g., a
+restriction list).
+.It Ic logconfig Ar configkeyword
+This command controls the amount and type of output written to
+the system
+.Xr syslog 3
+facility or the alternate
+.Ic logfile
+log file.
+By default, all output is turned on.
+All
+.Ar configkeyword
+keywords can be prefixed with
+.Ql = ,
+.Ql +
+and
+.Ql - ,
+where
+.Ql =
+sets the
+.Xr syslog 3
+priority mask,
+.Ql +
+adds and
+.Ql -
+removes
+messages.
+.Xr syslog 3
+messages can be controlled in four
+classes
+.Po
+.Cm clock ,
+.Cm peer ,
+.Cm sys
+and
+.Cm sync
+.Pc .
+Within these classes four types of messages can be
+controlled: informational messages
+.Po
+.Cm info
+.Pc ,
+event messages
+.Po
+.Cm events
+.Pc ,
+statistics messages
+.Po
+.Cm statistics
+.Pc
+and
+status messages
+.Po
+.Cm status
+.Pc .
+.Pp
+Configuration keywords are formed by concatenating the message class with
+the event class.
+The
+.Cm all
+prefix can be used instead of a message class.
+A
+message class may also be followed by the
+.Cm all
+keyword to enable/disable all
+messages of the respective message class.Thus, a minimal log configuration
+could look like this:
+.Bd -literal
+logconfig =syncstatus +sysevents
+.Ed
+.Pp
+This would just list the synchronizations state of
+.Xr ntpd 8
+and the major system events.
+For a simple reference server, the
+following minimum message configuration could be useful:
+.Bd -literal
+logconfig =syncall +clockall
+.Ed
+.Pp
+This configuration will list all clock information and
+synchronization information.
+All other events and messages about
+peers, system events and so on is suppressed.
+.It Ic logfile Ar logfile
+This command specifies the location of an alternate log file to
+be used instead of the default system
+.Xr syslog 3
+facility.
+This is the same operation as the -l command line option.
+.It Ic setvar Ar variable Op Cm default
+This command adds an additional system variable.
+These
+variables can be used to distribute additional information such as
+the access policy.
+If the variable of the form
+.Sm off
+.Va name = Ar value
+.Sm on
+is followed by the
+.Cm default
+keyword, the
+variable will be listed as part of the default system variables
+.Po
+.Xr ntpq 8
+.Ic rv
+command
+.Pc ) .
+These additional variables serve
+informational purposes only.
+They are not related to the protocol
+other that they can be listed.
+The known protocol variables will
+always override any variables defined via the
+.Ic setvar
+mechanism.
+There are three special variables that contain the names
+of all variable of the same group.
+The
+.Va sys_var_list
+holds
+the names of all system variables.
+The
+.Va peer_var_list
+holds
+the names of all peer variables and the
+.Va clock_var_list
+holds the names of the reference clock variables.
+.It Xo Ic tinker
+.Oo
+.Cm allan Ar allan |
+.Cm dispersion Ar dispersion |
+.Cm freq Ar freq |
+.Cm huffpuff Ar huffpuff |
+.Cm panic Ar panic |
+.Cm step Ar srep |
+.Cm stepout Ar stepout
+.Oc
+.Xc
+This command can be used to alter several system variables in
+very exceptional circumstances.
+It should occur in the
+configuration file before any other configuration options.
+The
+default values of these variables have been carefully optimized for
+a wide range of network speeds and reliability expectations.
+In
+general, they interact in intricate ways that are hard to predict
+and some combinations can result in some very nasty behavior.
+Very
+rarely is it necessary to change the default values; but, some
+folks cannot resist twisting the knobs anyway and this command is
+for them.
+Emphasis added: twisters are on their own and can expect
+no help from the support group.
+.Pp
+The variables operate as follows:
+.Bl -tag -width indent
+.It Cm allan Ar allan
+The argument becomes the new value for the minimum Allan
+intercept, which is a parameter of the PLL/FLL clock discipline
+algorithm.
+The value in log2 seconds defaults to 7 (1024 s), which is also the lower
+limit.
+.It Cm dispersion Ar dispersion
+The argument becomes the new value for the dispersion increase rate,
+normally .000015 s/s.
+.It Cm freq Ar freq
+The argument becomes the initial value of the frequency offset in
+parts-per-million.
+This overrides the value in the frequency file, if
+present, and avoids the initial training state if it is not.
+.It Cm huffpuff Ar huffpuff
+The argument becomes the new value for the experimental
+huff-n'-puff filter span, which determines the most recent interval
+the algorithm will search for a minimum delay.
+The lower limit is
+900 s (15 m), but a more reasonable value is 7200 (2 hours).
+There
+is no default, since the filter is not enabled unless this command
+is given.
+.It Cm panic Ar panic
+The argument is the panic threshold, normally 1000 s.
+If set to zero,
+the panic sanity check is disabled and a clock offset of any value will
+be accepted.
+.It Cm step Ar step
+The argument is the step threshold, which by default is 0.128 s.
+It can
+be set to any positive number in seconds.
+If set to zero, step
+adjustments will never occur.
+Note: The kernel time discipline is
+disabled if the step threshold is set to zero or greater than the
+default.
+.It Cm stepout Ar stepout
+The argument is the stepout timeout, which by default is 900 s.
+It can
+be set to any positive number in seconds.
+If set to zero, the stepout
+pulses will not be suppressed.
+.El
+.It Xo Ic trap Ar host_address
+.Op Cm port Ar port_number
+.Op Cm interface Ar interface_address
+.Xc
+This command configures a trap receiver at the given host
+address and port number for sending messages with the specified
+local interface address.
+If the port number is unspecified, a value
+of 18447 is used.
+If the interface address is not specified, the
+message is sent with a source address of the local interface the
+message is sent through.
+Note that on a multihomed host the
+interface used may vary from time to time with routing changes.
+.Pp
+The trap receiver will generally log event messages and other
+information from the server in a log file.
+While such monitor
+programs may also request their own trap dynamically, configuring a
+trap receiver will ensure that no messages are lost when the server
+is started.
+.It Cm hop Ar ...
+This command specifies a list of TTL values in increasing order, up to 8
+values can be specified.
+In manycast mode these values are used in turn in
+an expanding-ring search.
+The default is eight multiples of 32 starting at
+31.
+.El
+.Sh FILES
+.Bl -tag -width /etc/ntp.drift -compact
+.It Pa /etc/ntp.conf
+the default name of the configuration file
+.It Pa ntp.keys
+private MD5 keys
+.It Pa ntpkey
+RSA private key
+.It Pa ntpkey_ Ns Ar host
+RSA public key
+.It Pa ntp_dh
+Diffie-Hellman agreement parameters
+.El
+.Sh SEE ALSO
+.Xr rc.conf 5 ,
+.Xr ntpd 8 ,
+.Xr ntpdc 8 ,
+.Xr ntpq 8
+.Pp
+In addition to the manual pages provided,
+comprehensive documentation is available on the world wide web
+at
+.Li http://www.ntp.org/ .
+A snapshot of this documentation is available in HTML format in
+.Pa /usr/share/doc/ntp .
+.Rs
+.%A David L. Mills
+.%T Network Time Protocol (Version 3)
+.%O RFC1305
+.Re
+.Sh BUGS
+The syntax checking is not picky; some combinations of
+ridiculous and even hilarious options and modes may not be
+detected.
+.Pp
+The
+.Pa ntpkey_ Ns Ar host
+files are really digital
+certificates.
+These should be obtained via secure directory
+services when they become universally available.
diff --git a/usr.sbin/ntp/doc/ntp.keys.5 b/usr.sbin/ntp/doc/ntp.keys.5
new file mode 100644
index 0000000..dc9531c
--- /dev/null
+++ b/usr.sbin/ntp/doc/ntp.keys.5
@@ -0,0 +1,120 @@
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 13, 2000
+.Dt NTP.KEYS 5
+.Os
+.Sh NAME
+.Nm ntp.keys
+.Nd NTP daemon key file format
+.Sh SYNOPSIS
+.Nm /etc/ntp.keys
+.Sh DESCRIPTION
+Following is a description of the format of NTP key files.
+For a description of the use of these files, see the
+.Qq Authentication Support
+section of the
+.Xr ntp.conf 5
+page.
+.Pp
+In the case of DES, the keys are 56 bits long with,
+depending on type, a parity check on each byte.
+In the case of MD5, the keys are 64 bits (8 bytes).
+.Xr ntpd 8
+reads its keys from a file specified using the
+.Fl k
+command line option or the
+.Ic keys
+statement in the configuration file.
+While key number 0 is fixed by the NTP standard
+(as 56 zero bits)
+and may not be changed,
+one or more of the keys numbered 1 through 15
+may be arbitrarily set in the keys file.
+.Pp
+The key file uses the same comment conventions
+as the configuration file.
+Key entries use a fixed format of the form
+.Pp
+.D1 Ar keyno type key
+.Pp
+where
+.Ar keyno
+is a positive integer,
+.Ar type
+is a single character which defines the key format,
+and
+.Ar key
+is the key itself.
+.Pp
+The
+.Ar key
+may be given in one of four different formats,
+controlled by the
+.Ar type
+character.
+The four key types, and corresponding formats,
+are listed following.
+.Bl -tag -width X
+.It Li S
+The key is a 64-bit hexadecimal number in the format
+specified in the DES specification;
+that is, the high order seven bits of each octet are used
+to form the 56-bit key
+while the low order bit of each octet is given a value
+such that odd parity is maintained for the octet.
+Leading zeroes must be specified
+(i.e., the key must be exactly 16 hex digits long)
+and odd parity must be maintained.
+Hence a zero key, in standard format, would be given as
+.Ql 0101010101010101 .
+.It Li N
+The key is a 64-bit hexadecimal number in the format
+specified in the NTP standard.
+This is the same as the DES format,
+except the bits in each octet have been rotated one bit right
+so that the parity bit is now the high order bit of the octet.
+Leading zeroes must be specified and odd parity must be maintained.
+A zero key in NTP format would be specified as
+.Ql 8080808080808080 .
+.It Li A
+The key is a 1-to-8 character ASCII string.
+A key is formed from this by using the low order 7 bits
+of each ASCII character in the string,
+with zeroes added on the right
+when necessary to form a full width 56-bit key,
+in the same way that encryption keys are formed from
+.Ux
+passwords.
+.It Li M
+The key is a 1-to-8 character ASCII string,
+using the MD5 authentication scheme.
+Note that both the keys and the authentication schemes (DES or MD5)
+must be identical between a set of peers sharing the same key number.
+.El
+.Pp
+Note that the keys used by the
+.Xr ntpq 8
+and
+.Xr ntpdc 8
+programs are checked against passwords
+requested by the programs and entered by hand,
+so it is generally appropriate to specify these keys in ASCII format.
+.Sh FILES
+.Bl -tag -width /etc/ntp.drift -compact
+.It Pa /etc/ntp.keys
+the default name of the configuration file
+.El
+.Sh SEE ALSO
+.Xr ntp.conf 5 ,
+.Xr ntpd 8 ,
+.Xr ntpdate 8 ,
+.Xr ntpdc 8
+.Sh BUGS
+.Xr ntpd 8
+has gotten rather fat.
+While not huge, it has gotten larger than might
+be desirable for an elevated-priority daemon running on a workstation,
+particularly since many of the fancy features which consume the space
+were designed more with a busy primary server, rather than a high
+stratum workstation, in mind.
diff --git a/usr.sbin/ntp/doc/ntpd.8 b/usr.sbin/ntp/doc/ntpd.8
new file mode 100644
index 0000000..5194b75
--- /dev/null
+++ b/usr.sbin/ntp/doc/ntpd.8
@@ -0,0 +1,609 @@
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 17, 2006
+.Dt NTPD 8
+.Os
+.Sh NAME
+.Nm ntpd
+.Nd Network Time Protocol (NTP) daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl aAbDdgLmnPqx
+.Op Fl c Ar conffile
+.Op Fl f Ar driftfile
+.Op Fl k Ar keyfile
+.Op Fl l Ar logfile
+.Op Fl p Ar pidfile
+.Op Fl r Ar broadcastdelay
+.Op Fl s Ar statsdir
+.Op Fl t Ar key
+.Op Fl v Ar variable
+.Op Fl V Ar variable
+.Sh DESCRIPTION
+The
+.Nm
+utility is an operating system daemon which sets
+and maintains the system time of day in synchronism with Internet
+standard time servers.
+It is a complete implementation of the
+Network Time Protocol (NTP) version 4, but also retains
+compatibility with version 3, as defined by RFC-1305, and version 1
+and 2, as defined by RFC-1059 and RFC-1119, respectively.
+.Pp
+The
+.Nm
+utility does most computations in 64-bit floating point
+arithmetic and does relatively clumsy 64-bit fixed point operations
+only when necessary to preserve the ultimate precision, about 232
+picoseconds.
+While the ultimate precision is not achievable with
+ordinary workstations and networks of today, it may be required
+with future gigahertz CPU clocks and gigabit LANs.
+.Pp
+Ordinarily,
+.Nm
+reads the
+.Xr ntp.conf 5
+configuration file at startup time in order to determine the
+synchronization sources and operating modes.
+It is also possible to
+specify a working, although limited, configuration entirely on the
+command line, obviating the need for a configuration file.
+This may
+be particularly useful when the local host is to be configured as a
+broadcast/multicast client, with all peers being determined by
+listening to broadcasts at run time.
+.Pp
+If NetInfo support is built into
+.Nm ,
+then
+.Nm
+will attempt to read its configuration from the
+NetInfo if the default
+.Xr ntp.conf 5
+file cannot be read and no file is
+specified by the
+.Fl c
+option.
+.Pp
+Various internal
+.Nm
+variables can be displayed and
+configuration options altered while the
+.Nm
+is running
+using the
+.Xr ntpq 8
+and
+.Xr ntpdc 8
+utility programs.
+.Pp
+When
+.Nm
+starts it looks at the value of
+.Cm umask 2 ,
+and if zero
+.Nm
+will set the
+.Cm umask 2
+to 022.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl a
+Require cryptographic authentication for broadcast client,
+multicast client and symmetric passive associations.
+This is the default.
+.It Fl A
+Do not require cryptographic authentication for broadcast client,
+multicast client and symmetric passive associations.
+This is almost never a good idea.
+.It Fl b
+Enable the client to synchronize to broadcast servers.
+.It Fl c Ar conffile
+Specify the name and path of the configuration file, default
+.Pa /etc/ntp.conf .
+.It Fl d
+Specify debugging mode.
+This option may occur more than once,
+with each occurrence indicating greater detail of display.
+.It Fl D Ar level
+Specify debugging level directly.
+.It Fl f Ar driftfile
+Specify the name and path of the frequency file, default
+.Pa /etc/ntp.drift .
+This is the same operation as the
+.Ic driftfile Ar driftfile
+configuration command.
+.It Fl g
+Normally,
+.Nm
+exits with a message to the system log if the offset exceeds
+the panic threshold, which is 1000 s by default.
+This option allows thetime to be set to any value without restriction;
+however, this can happen only once.
+If the threshold is exceeded after that,
+.Nm
+will exit with a message to the system log.
+This option can be used with the
+.Fl q
+and
+.Fl x
+options.
+See the
+.Ic tinker
+command for other options.
+.It Fl k Ar keyfile
+Specify the name and path of the symmetric key file, default
+.Pa /etc/ntp.keys .
+This is the same operation as the
+.Ic keys Ar keyfile
+configuration command.
+.It Fl l Ar logfile
+Specify the name and path of the log file.
+The default is the system log file.
+This is the same operation as the
+.Ic logfile Ar logfile
+configuration command.
+.It Fl L
+Do not listen to virtual IPs.
+The default is to listen.
+.It Fl m
+Enable the client to synchronize to multicast servers at the IPv4 multicast
+group address 224.0.1.1.
+.It Fl n
+Do not fork.
+.It Fl N
+To the extent permitted by the operating system, run the
+.Nm
+at the highest priority.
+.It Fl p Ar pidfile
+Specify the name and path of the file used to record the
+.Nm
+process ID.
+This is the same operation as the
+.Ic pidfile Ar pidfile
+configuration command.
+.It Fl P Ar priority
+To the extent permitted by the operating system, run the
+.Nm
+at the specified priority.
+.It Fl q
+Exit the
+.Nm
+just after the first time the clock is
+set.
+This behavior mimics that of the
+.Xr ntpdate 8
+program,
+which is to be retired.
+The
+.Fl g
+and
+.Fl x
+options can
+be used with this option.
+Note: The kernel time discipline is disabled with this option.
+.It Fl r Ar broadcastdelay
+Specify the default propagation delay from the
+broadcast/multicast server to this client.
+This is necessary
+only if the delay cannot be computed automatically by the
+protocol.
+.It Fl s Ar statsdir
+Specify the directory path for files created by the statistics
+facility.
+This is the same operation as the
+.Ic statsdir Ar statsdir
+configuration command.
+.It Fl t Ar key
+Add a key number to the trusted key list.
+This option can occur more than once.
+.It Fl v Ar variable
+.It Fl V Ar variable
+Add a system variable listed by default.
+.It Fl x
+Normally, the time is slewed if the offset is less than the
+step threshold, which is 128 ms by default, and stepped if above
+the threshold.
+This option sets the threshold to 600 s,
+which is well within the accuracy window to set the clock manually.
+Note: Since the slew rate of typical Unix kernels is limited to 0.5 ms/s,
+each second of adjustment requires an amortization interval of 2000 s.
+Thus, an adjustment as much as 600 s will take almost 14 days to complete.
+This option can be used with the
+.Fl g
+and
+.Fl q
+options.
+See the
+.Ic tinker
+command for other options.
+Note: The kernel time discipline is disabled with this option.
+.El
+.Ss "How NTP Operates"
+The
+.Nm
+utility operates by exchanging messages with
+one or more configured servers at designated poll intervals.
+When
+started, whether for the first or subsequent times, the program
+requires several exchanges from the majority of these servers so
+the signal processing and mitigation algorithms can accumulate and
+groom the data and set the clock.
+In order to protect the network
+from bursts, the initial poll interval for each server is delayed
+an interval randomized over a few seconds.
+At the default initial poll
+interval of 64s, several minutes can elapse before the clock is
+set.
+The initial delay to set the clock can be reduced using the
+.Cm iburst
+keyword with the
+.Ic server
+configuration
+command, as described in
+.Xr ntp.conf 5 .
+.Pp
+Most operating systems and hardware of today incorporate a
+time-of-year (TOY) chip to maintain the time during periods when
+the power is off.
+When the machine is booted, the chip is used to
+initialize the operating system time.
+After the machine has
+synchronized to a NTP server, the operating system corrects the
+chip from time to time.
+In case there is no TOY chip or for some
+reason its time is more than 1000s from the server time,
+.Nm
+assumes something must be terribly wrong and the only
+reliable action is for the operator to intervene and set the clock
+by hand.
+This causes
+.Nm
+to exit with a panic message to
+the system log.
+The
+.Fl g
+option overrides this check and the
+clock will be set to the server time regardless of the chip time.
+However, and to protect against broken hardware, such as when the
+CMOS battery fails or the clock counter becomes defective, once the
+clock has been set, an error greater than 1000s will cause
+.Nm
+to exit anyway.
+.Pp
+Under ordinary conditions,
+.Nm
+adjusts the clock in
+small steps so that the timescale is effectively continuous and
+without discontinuities.
+Under conditions of extreme network
+congestion, the roundtrip delay jitter can exceed three seconds and
+the synchronization distance, which is equal to one-half the
+roundtrip delay plus error budget terms, can become very large.
+The
+.Nm
+algorithms discard sample offsets exceeding 128 ms,
+unless the interval during which no sample offset is less than 128
+ms exceeds 900s.
+The first sample after that, no matter what the
+offset, steps the clock to the indicated time.
+In practice this
+reduces the false alarm rate where the clock is stepped in error to
+a vanishingly low incidence.
+.Pp
+As the result of this behavior, once the clock has been set, it
+very rarely strays more than 128 ms, even under extreme cases of
+network path congestion and jitter.
+Sometimes, in particular when
+.Nm
+is first started, the error might exceed 128 ms.
+This
+may on occasion cause the clock to be set backwards if the local
+clock time is more than 128 s in the future relative to the server.
+In some applications, this behavior may be unacceptable.
+If the
+.Fl x
+option is included on the command line, the clock will
+never be stepped and only slew corrections will be used.
+.Pp
+The issues should be carefully explored before deciding to use
+the
+.Fl x
+option.
+The maximum slew rate possible is limited
+to 500 parts-per-million (PPM) as a consequence of the correctness
+principles on which the NTP protocol and algorithm design are
+based.
+As a result, the local clock can take a long time to
+converge to an acceptable offset, about 2,000 s for each second the
+clock is outside the acceptable range.
+During this interval the
+local clock will not be consistent with any other network clock and
+the system cannot be used for distributed applications that require
+correctly synchronized network time.
+.Pp
+In spite of the above precautions, sometimes when large
+frequency errors are present the resulting time offsets stray
+outside the 128-ms range and an eventual step or slew time
+correction is required.
+If following such a correction the
+frequency error is so large that the first sample is outside the
+acceptable range,
+.Nm
+enters the same state as when the
+.Pa ntp.drift
+file is not present.
+The intent of this behavior
+is to quickly correct the frequency and restore operation to the
+normal tracking mode.
+In the most extreme cases
+(
+.Cm time.ien.it
+comes to mind), there may be occasional
+step/slew corrections and subsequent frequency corrections.
+It
+helps in these cases to use the
+.Cm burst
+keyword when
+configuring the server.
+.Ss "Frequency Discipline"
+The
+.Nm
+behavior at startup depends on whether the
+frequency file, usually
+.Pa ntp.drift ,
+exists.
+This file
+contains the latest estimate of clock frequency error.
+When the
+.Nm
+is started and the file does not exist, the
+.Nm
+enters a special mode designed to quickly adapt to
+the particular system clock oscillator time and frequency error.
+This takes approximately 15 minutes, after which the time and
+frequency are set to nominal values and the
+.Nm
+enters
+normal mode, where the time and frequency are continuously tracked
+relative to the server.
+After one hour the frequency file is
+created and the current frequency offset written to it.
+When the
+.Nm
+is started and the file does exist, the
+.Nm
+frequency is initialized from the file and enters normal mode
+immediately.
+After that the current frequency offset is written to
+the file at hourly intervals.
+.Ss "Operating Modes"
+The
+.Nm
+utility can operate in any of several modes, including
+symmetric active/passive, client/server broadcast/multicast and
+manycast, as described in the
+.Qq Association Management
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+It normally operates continuously while
+monitoring for small changes in frequency and trimming the clock
+for the ultimate precision.
+However, it can operate in a one-time
+mode where the time is set from an external server and frequency is
+set from a previously recorded frequency file.
+A
+broadcast/multicast or manycast client can discover remote servers,
+compute server-client propagation delay correction factors and
+configure itself automatically.
+This makes it possible to deploy a
+fleet of workstations without specifying configuration details
+specific to the local environment.
+.Pp
+By default,
+.Nm
+runs in continuous mode where each of
+possibly several external servers is polled at intervals determined
+by an intricate state machine.
+The state machine measures the
+incidental roundtrip delay jitter and oscillator frequency wander
+and determines the best poll interval using a heuristic algorithm.
+Ordinarily, and in most operating environments, the state machine
+will start with 64s intervals and eventually increase in steps to
+1024s.
+A small amount of random variation is introduced in order to
+avoid bunching at the servers.
+In addition, should a server become
+unreachable for some time, the poll interval is increased in steps
+to 1024s in order to reduce network overhead.
+.Pp
+In some cases it may not be practical for
+.Nm
+to run
+continuously.
+A common workaround has been to run the
+.Xr ntpdate 8
+program from a
+.Xr cron 8
+job at designated
+times.
+However, this program does not have the crafted signal
+processing, error checking and mitigation algorithms of
+.Nm .
+The
+.Fl q
+option is intended for this purpose.
+Setting this option will cause
+.Nm
+to exit just after
+setting the clock for the first time.
+The procedure for initially
+setting the clock is the same as in continuous mode; most
+applications will probably want to specify the
+.Cm iburst
+keyword with the
+.Ic server
+configuration command.
+With this
+keyword a volley of messages are exchanged to groom the data and
+the clock is set in about 10 s.
+If nothing is heard after a
+couple of minutes, the daemon times out and exits.
+After a suitable
+period of mourning, the
+.Xr ntpdate 8
+program may be
+retired.
+.Pp
+When kernel support is available to discipline the clock
+frequency, which is the case for stock Solaris, Tru64, Linux and
+.Fx ,
+a useful feature is available to discipline the clock
+frequency.
+First,
+.Nm
+is run in continuous mode with
+selected servers in order to measure and record the intrinsic clock
+frequency offset in the frequency file.
+It may take some hours for
+the frequency and offset to settle down.
+Then the
+.Nm
+is
+stopped and run in one-time mode as required.
+At each startup, the
+frequency is read from the file and initializes the kernel
+frequency.
+.Ss "Poll Interval Control"
+This version of NTP includes an intricate state machine to
+reduce the network load while maintaining a quality of
+synchronization consistent with the observed jitter and wander.
+There are a number of ways to tailor the operation in order enhance
+accuracy by reducing the interval or to reduce network overhead by
+increasing it.
+However, the user is advised to carefully consider
+the consequences of changing the poll adjustment range from the
+default minimum of 64 s to the default maximum of 1,024 s.
+The
+default minimum can be changed with the
+.Ic tinker
+.Cm minpoll
+command to a value not less than 16 s.
+This value is used for all
+configured associations, unless overridden by the
+.Cm minpoll
+option on the configuration command.
+Note that most device drivers
+will not operate properly if the poll interval is less than 64 s
+and that the broadcast server and manycast client associations will
+also use the default, unless overridden.
+.Pp
+In some cases involving dial up or toll services, it may be
+useful to increase the minimum interval to a few tens of minutes
+and maximum interval to a day or so.
+Under normal operation
+conditions, once the clock discipline loop has stabilized the
+interval will be increased in steps from the minimum to the
+maximum.
+However, this assumes the intrinsic clock frequency error
+is small enough for the discipline loop correct it.
+The capture
+range of the loop is 500 PPM at an interval of 64s decreasing by a
+factor of two for each doubling of interval.
+At a minimum of 1,024
+s, for example, the capture range is only 31 PPM.
+If the intrinsic
+error is greater than this, the drift file
+.Pa ntp.drift
+will
+have to be specially tailored to reduce the residual error below
+this limit.
+Once this is done, the drift file is automatically
+updated once per hour and is available to initialize the frequency
+on subsequent daemon restarts.
+.Ss "The huff-n'-puff Filter"
+In scenarios where a considerable amount of data are to be
+downloaded or uploaded over telephone modems, timekeeping quality
+can be seriously degraded.
+This occurs because the differential
+delays on the two directions of transmission can be quite large.
+In
+many cases the apparent time errors are so large as to exceed the
+step threshold and a step correction can occur during and after the
+data transfer is in progress.
+.Pp
+The huff-n'-puff filter is designed to correct the apparent time
+offset in these cases.
+It depends on knowledge of the propagation
+delay when no other traffic is present.
+In common scenarios this
+occurs during other than work hours.
+The filter maintains a shift
+register that remembers the minimum delay over the most recent
+interval measured usually in hours.
+Under conditions of severe
+delay, the filter corrects the apparent offset using the sign of
+the offset and the difference between the apparent delay and
+minimum delay.
+The name of the filter reflects the negative (huff)
+and positive (puff) correction, which depends on the sign of the
+offset.
+.Pp
+The filter is activated by the
+.Ic tinker
+command and
+.Cm huffpuff
+keyword, as described in
+.Xr ntp.conf 5 .
+.Sh FILES
+.Bl -tag -width /etc/ntp.drift -compact
+.It Pa /etc/ntp.conf
+the default name of the configuration file
+.It Pa /etc/ntp.drift
+the default name of the drift file
+.It Pa /etc/ntp.keys
+the default name of the key file
+.El
+.Sh SEE ALSO
+.Xr ntp.conf 5 ,
+.Xr ntpdate 8 ,
+.Xr ntpdc 8 ,
+.Xr ntpq 8
+.Pp
+In addition to the manual pages provided,
+comprehensive documentation is available on the world wide web
+at
+.Li http://www.ntp.org/ .
+A snapshot of this documentation is available in HTML format in
+.Pa /usr/share/doc/ntp .
+.Rs
+.%A David L. Mills
+.%T Network Time Protocol (Version 1)
+.%O RFC1059
+.Re
+.Rs
+.%A David L. Mills
+.%T Network Time Protocol (Version 2)
+.%O RFC1119
+.Re
+.Rs
+.%A David L. Mills
+.%T Network Time Protocol (Version 3)
+.%O RFC1305
+.Re
+.Sh BUGS
+The
+.Nm
+utility has gotten rather fat.
+While not huge, it has gotten
+larger than might be desirable for an elevated-priority
+.Nm
+running on a workstation, particularly since many of
+the fancy features which consume the space were designed more with
+a busy primary server, rather than a high stratum workstation in
+mind.
diff --git a/usr.sbin/ntp/doc/ntpdate.8 b/usr.sbin/ntp/doc/ntpdate.8
new file mode 100644
index 0000000..f361f17
--- /dev/null
+++ b/usr.sbin/ntp/doc/ntpdate.8
@@ -0,0 +1,280 @@
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 17, 2006
+.Dt NTPDATE 8
+.Os
+.Sh NAME
+.Nm ntpdate
+.Nd set the date and time via NTP
+.Sh SYNOPSIS
+.Nm
+.Op Fl 46bBdoqsuv
+.Op Fl a Ar key
+.Op Fl e Ar authdelay
+.Op Fl k Ar keyfile
+.Op Fl o Ar version
+.Op Fl p Ar samples
+.Op Fl t Ar timeout
+.Ar server ...
+.Sh DESCRIPTION
+.Pp
+.Em Note :
+The functionality of this program is now available
+in the
+.Xr ntpd 8
+program.
+See the
+.Fl q
+command line
+option in the
+.Xr ntpd 8
+page.
+After a suitable period of
+mourning, the
+.Nm
+utility is to be retired from this
+distribution.
+.Pp
+The
+.Nm
+utility sets the local date and time by polling the
+Network Time Protocol (NTP) server(s) given as the
+.Ar server
+arguments to determine the correct time.
+It must be run as root on
+the local host.
+A number of samples are obtained from each of the
+servers specified and a subset of the NTP clock filter and
+selection algorithms are applied to select the best of these.
+Note
+that the accuracy and reliability of
+.Nm
+depends on
+the number of servers, the number of polls each time it is run and
+the interval between runs.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl 4
+Force DNS resolution of following host names on the command line to the
+IPv4 namespace.
+.It Fl 6
+Force DNS resolution of following host names on the command line to the
+IPv6 namespace.
+.It Fl a Ar key
+Enable the authentication function and specify the key
+identifier to be used for authentication as the argument
+.Ar key .
+The keys and key identifiers must match
+in both the client and server key files.
+The default is to disable
+the authentication function.
+.It Fl B
+Force the time to always be slewed using the
+.Xr adjtime 2
+system
+call, even if the measured offset is greater than +-128 ms.
+The
+default is to step the time using
+.Xr settimeofday 2
+if the offset is
+greater than +-128 ms.
+Note that, if the offset is much greater
+than +-128 ms in this case, it can take a long time (hours) to
+slew the clock to the correct value.
+During this time, the host
+should not be used to synchronize clients.
+.It Fl b
+Force the time to be stepped using the
+.Xr settimeofday 2
+system
+call, rather than slewed (default) using the
+.Xr adjtime 2
+system call.
+This option should be used when called from a startup file at boot
+time.
+.It Fl d
+Enable the debugging mode, in which
+.Nm
+will go
+through all the steps, but not adjust the local clock.
+Information
+useful for general debugging will also be printed.
+.It Fl e Ar authdelay
+Specify the processing delay to perform an authentication
+function as the value
+.Ar authdelay ,
+in seconds and fraction
+(see
+.Xr ntpd 8
+for details).
+This number is usually small
+enough to be negligible for most purposes, though specifying a
+value may improve timekeeping on very slow CPU's.
+.It Fl k Ar keyfile
+Specify the path for the authentication key file as the string
+.Ar keyfile .
+The default is
+.Pa /etc/ntp.keys .
+This file
+should be in the format described in
+.Xr ntpd 8 .
+.It Fl o Ar version
+Specify the NTP version for outgoing packets as the integer
+.Ar version ,
+which can be 1 or 2.
+The default is 3.
+This allows
+.Nm
+to be used with older NTP versions.
+.It Fl p Ar samples
+Specify the number of samples to be acquired from each server
+as the integer
+.Ar samples ,
+with values from 1 to 8 inclusive.
+The default is 4.
+.It Fl q
+Query only - do not set the clock.
+.It Fl s
+Divert logging output from the standard output (default) to the
+system
+.Xr syslog 3
+facility.
+This is designed primarily for
+convenience of
+.Xr cron 8
+scripts.
+.It Fl t Ar timeout
+Specify the maximum time waiting for a server response as the
+value
+.Ar timeout ,
+in seconds and fraction.
+The value is
+rounded to a multiple of 0.2 seconds.
+The default is 1 second, a
+value suitable for polling across a LAN.
+.It Fl u
+Direct
+.Nm
+to use an unprivileged port for outgoing
+packets.
+This is most useful when behind a firewall that blocks
+incoming traffic to privileged ports, and you want to synchronise
+with hosts beyond the firewall.
+Note that the
+.Fl d
+option
+always uses unprivileged ports.
+.It Fl v
+Be verbose.
+This option will cause
+.Nm Ns 's
+version
+identification string to be logged.
+.El
+.Pp
+The
+.Nm
+utility can be run manually as necessary to set the
+host clock, or it can be run from the host startup script to set
+the clock at boot time.
+This is useful in some cases to set the
+clock initially before starting the NTP daemon
+.Xr ntpd 8 .
+It is
+also possible to run
+.Nm
+from a
+.Xr cron 8
+script.
+However, it is important to note that
+.Nm
+with
+contrived
+.Xr cron 8
+scripts is no substitute for the NTP
+daemon, which uses sophisticated algorithms to maximize accuracy
+and reliability while minimizing resource use.
+Finally, since
+.Nm
+does not discipline the host clock frequency as
+does
+.Xr ntpd 8 ,
+the accuracy using
+.Nm
+is
+limited.
+.Pp
+Time adjustments are made by
+.Nm
+in one of two
+ways.
+If
+.Nm
+determines the clock is in error more
+than 0.5 second it will simply step the time by calling the system
+.Xr settimeofday 2
+routine.
+If the error is less than 0.5
+seconds, it will slew the time by calling the system
+.Xr adjtime 2
+routine.
+The latter technique is less disruptive
+and more accurate when the error is small, and works quite well
+when
+.Nm
+is run by
+.Xr cron 8
+every hour or
+two.
+.Pp
+The
+.Nm
+utility will decline to set the date if an NTP server
+daemon (e.g.,
+.Xr ntpd 8 )
+is running on the same host.
+When
+running
+.Nm
+on a regular basis from
+.Xr cron 8
+as
+an alternative to running a daemon, doing so once every hour or two
+will result in precise enough timekeeping to avoid stepping the
+clock.
+.Pp
+Note that in contexts where a host name is expected, a
+.Fl 4
+qualifier preceding the host name forces DNS resolution to the
+IPv4 namespace, while a
+.Fl 6
+qualifier forces DNS resolution to the IPv6 namespace.
+.Pp
+If NetInfo support is compiled into
+.Nm ,
+then the
+.Cm server
+argument is optional if
+.Nm
+can find a
+time server in the NetInfo configuration for
+.Xr ntpd 8 .
+.Sh FILES
+.Bl -tag -width /etc/ntp.keys -compact
+.It Pa /etc/ntp.keys
+contains the encryption keys used by
+.Nm .
+.El
+.Sh SEE ALSO
+.Xr ntpd 8
+.Sh BUGS
+The slew adjustment is actually 50% larger than the measured
+offset, since this (it is argued) will tend to keep a badly
+drifting clock more accurate.
+This is probably not a good idea and
+may cause a troubling hunt for some values of the kernel variables
+.Va kern.clockrate.tick
+and
+.Va kern.clockrate.tickadj .
diff --git a/usr.sbin/ntp/doc/ntpdc.8 b/usr.sbin/ntp/doc/ntpdc.8
new file mode 100644
index 0000000..35ea7aa
--- /dev/null
+++ b/usr.sbin/ntp/doc/ntpdc.8
@@ -0,0 +1,715 @@
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 17, 2006
+.Dt NTPDC 8
+.Os
+.Sh NAME
+.Nm ntpdc
+.Nd special NTP query program
+.Sh SYNOPSIS
+.Nm
+.Op Fl 46ilnps
+.Op Fl c Ar command
+.Op Ar host
+.Op Ar ...
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to query the
+.Xr ntpd 8
+daemon about its
+current state and to request changes in that state.
+The program may
+be run either in interactive mode or controlled using command line
+arguments.
+Extensive state and statistics information is available
+through the
+.Nm
+interface.
+In addition, nearly all the
+configuration options which can be specified at startup using
+ntpd's configuration file may also be specified at run time using
+.Nm .
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl 4
+Force DNS resolution of following host names on the command line to the
+IPv4 namespace.
+.It Fl 6
+Force DNS resolution of following host names on the command line to the
+IPv6 namespace.
+.It Fl c Ar command
+The following argument is interpreted as an interactive format
+command and is added to the list of commands to be executed on the
+specified host(s).
+Multiple
+.Fl c
+options may be given.
+.It Fl i
+Force
+.Nm
+to operate in interactive mode.
+Prompts
+will be written to the standard output and commands read from the
+standard input.
+.It Fl l
+Obtain a list of peers which are known to the server(s).
+This
+switch is equivalent to
+.Ql Fl c Ar listpeers .
+.It Fl n
+Output all host addresses in dotted-quad numeric format rather
+than converting to the canonical host names.
+.It Fl p
+Print a list of the peers known to the server as well as a
+summary of their state.
+This is equivalent to
+.Ql Fn c Ar peers .
+.It Fl s
+Print a list of the peers known to the server as well as a
+summary of their state, but in a slightly different format than the
+.Fl p
+switch.
+This is equivalent to
+.Ql Fl c Ar dmpeers .
+.El
+.Pp
+If one or more request options are included on the command line
+when
+.Nm
+is executed, each of the requests will be sent
+to the NTP servers running on each of the hosts given as command
+line arguments, or on localhost by default.
+If no request options
+are given,
+.Nm
+will attempt to read commands from the
+standard input and execute these on the NTP server running on the
+first host given on the command line, again defaulting to localhost
+when no other host is specified.
+The
+.Nm
+utility will prompt for
+commands if the standard input is a terminal device.
+.Pp
+The
+.Nm
+utility uses NTP mode 7 packets to communicate with the
+NTP server, and hence can be used to query any compatible server on
+the network which permits it.
+Note that since NTP is a UDP protocol
+this communication will be somewhat unreliable, especially over
+large distances in terms of network topology.
+The
+.Nm
+utility makes
+no attempt to retransmit requests, and will time requests out if
+the remote host is not heard from within a suitable timeout
+time.
+.Pp
+The operation of
+.Nm
+are specific to the particular
+implementation of the
+.Xr ntpd 8
+daemon and can be expected to
+work only with this and maybe some previous versions of the daemon.
+Requests from a remote
+.Nm
+utility which affect the
+state of the local server must be authenticated, which requires
+both the remote program and local server share a common key and key
+identifier.
+.Pp
+Note that in contexts where a host name is expected, a
+.Fl 4
+qualifier preceding the host name forces DNS resolution to the IPv4 namespace,
+while a
+.Fl 6
+qualifier forces DNS resolution to the IPv6 namespace.
+Specifying a command line option other than
+.Fl i
+or
+.Fl n
+will cause the specified query (queries) to be sent to
+the indicated host(s) immediately.
+Otherwise,
+.Nm
+will
+attempt to read interactive format commands from the standard
+input.
+.Ss "Interactive Commands"
+Interactive format commands consist of a keyword followed by zero
+to four arguments.
+Only enough characters of the full keyword to
+uniquely identify the command need be typed.
+The output of a
+command is normally sent to the standard output, but optionally the
+output of individual commands may be sent to a file by appending a
+.Ql \&> ,
+followed by a file name, to the command line.
+.Pp
+A number of interactive format commands are executed entirely
+within the
+.Nm
+utility itself and do not result in NTP
+mode 7 requests being sent to a server.
+These are described
+following.
+.Bl -tag -width indent
+.It Ic \&? Ar command_keyword
+.It Ic help Ar command_keyword
+A
+.Sq Ic \&?
+will print a list of all the command
+keywords known to this incarnation of
+.Nm .
+A
+.Sq Ic \&?
+followed by a command keyword will print function and usage
+information about the command.
+This command is probably a better
+source of information about
+.Xr ntpq 8
+than this manual
+page.
+.It Ic delay Ar milliseconds
+Specify a time interval to be added to timestamps included in
+requests which require authentication.
+This is used to enable
+(unreliable) server reconfiguration over long delay network paths
+or between machines whose clocks are unsynchronized.
+Actually the
+server does not now require timestamps in authenticated requests,
+so this command may be obsolete.
+.It Ic host Ar hostname
+Set the host to which future queries will be sent.
+Hostname may
+be either a host name or a numeric address.
+.It Ic hostnames Op Cm yes | Cm no
+If
+.Cm yes
+is specified, host names are printed in
+information displays.
+If
+.Cm no
+is specified, numeric
+addresses are printed instead.
+The default is
+.Cm yes ,
+unless
+modified using the command line
+.Fl n
+switch.
+.It Ic keyid Ar keyid
+This command allows the specification of a key number to be
+used to authenticate configuration requests.
+This must correspond
+to a key number the server has been configured to use for this
+purpose.
+.It Ic quit
+Exit
+.Nm .
+.It Ic passwd
+This command prompts you to type in a password (which will not
+be echoed) which will be used to authenticate configuration
+requests.
+The password must correspond to the key configured for
+use by the NTP server for this purpose if such requests are to be
+successful.
+.It Ic timeout Ar milliseconds
+Specify a timeout period for responses to server queries.
+The
+default is about 8000 milliseconds.
+Note that since
+.Nm
+retries each query once after a timeout, the total waiting time for
+a timeout will be twice the timeout value set.
+.El
+.Ss "Control Message Commands"
+Query commands result in NTP mode 7 packets containing requests for
+information being sent to the server.
+These are read-only commands
+in that they make no modification of the server configuration
+state.
+.Bl -tag -width indent
+.It Ic listpeers
+Obtains and prints a brief list of the peers for which the
+server is maintaining state.
+These should include all configured
+peer associations as well as those peers whose stratum is such that
+they are considered by the server to be possible future
+synchronization candidates.
+.It Ic peers
+Obtains a list of peers for which the server is maintaining
+state, along with a summary of that state.
+Summary information
+includes the address of the remote peer, the local interface
+address (0.0.0.0 if a local address has yet to be determined), the
+stratum of the remote peer (a stratum of 16 indicates the remote
+peer is unsynchronized), the polling interval, in seconds, the
+reachability register, in octal, and the current estimated delay,
+offset and dispersion of the peer, all in seconds.
+.Pp
+The character in the left margin indicates the mode this peer
+entry is operating in.
+A
+.Ql \&+
+denotes symmetric active, a
+.Ql \&-
+indicates symmetric passive, a
+.Ql \&=
+means the
+remote server is being polled in client mode, a
+.Ql \&^
+indicates that the server is broadcasting to this address, a
+.Ql \&~
+denotes that the remote peer is sending broadcasts and a
+.Ql \&*
+marks the peer the server is currently synchronizing
+to.
+.Pp
+The contents of the host field may be one of four forms.
+It may
+be a host name, an IP address, a reference clock implementation
+name with its parameter or
+.Fn REFCLK "implementation_number" "parameter" .
+On
+.Ic hostnames
+.Cm no
+only IP-addresses
+will be displayed.
+.It Ic dmpeers
+A slightly different peer summary list.
+Identical to the output
+of the
+.Ic peers
+command, except for the character in the
+leftmost column.
+Characters only appear beside peers which were
+included in the final stage of the clock selection algorithm.
+A
+.Ql \&.
+indicates that this peer was cast off in the falseticker
+detection, while a
+.Ql \&+
+indicates that the peer made it
+through.
+A
+.Ql \&*
+denotes the peer the server is currently
+synchronizing with.
+.It Ic showpeer Ar peer_address Oo Ar ... Oc
+Shows a detailed display of the current peer variables for one
+or more peers.
+Most of these values are described in the NTP
+Version 2 specification.
+.It Ic pstats Ar peer_address Oo Ar ... Oc
+Show per-peer statistic counters associated with the specified
+peer(s).
+.It Ic clockinfo Ar clock_peer_address Oo Ar ... Oc
+Obtain and print information concerning a peer clock.
+The
+values obtained provide information on the setting of fudge factors
+and other clock performance information.
+.It Ic kerninfo
+Obtain and print kernel phase-lock loop operating parameters.
+This information is available only if the kernel has been specially
+modified for a precision timekeeping function.
+.It Ic loopinfo Op Cm oneline | Cm multiline
+Print the values of selected loop filter variables.
+The loop
+filter is the part of NTP which deals with adjusting the local
+system clock.
+The
+.Sq offset
+is the last offset given to the
+loop filter by the packet processing code.
+The
+.Sq frequency
+is the frequency error of the local clock in parts-per-million
+(ppm).
+The
+.Sq time_const
+controls the stiffness of the
+phase-lock loop and thus the speed at which it can adapt to
+oscillator drift.
+The
+.Sq watchdog timer
+value is the number
+of seconds which have elapsed since the last sample offset was
+given to the loop filter.
+The
+.Cm oneline
+and
+.Cm multiline
+options specify the format in which this
+information is to be printed, with
+.Cm multiline
+as the
+default.
+.It Ic sysinfo
+Print a variety of system state variables, i.e., state related
+to the local server.
+All except the last four lines are described
+in the NTP Version 3 specification, RFC-1305.
+.Pp
+The
+.Sq system flags
+show various system flags, some of
+which can be set and cleared by the
+.Ic enable
+and
+.Ic disable
+configuration commands, respectively.
+These are
+the
+.Cm auth ,
+.Cm bclient ,
+.Cm monitor ,
+.Cm pll ,
+.Cm pps
+and
+.Cm stats
+flags.
+See the
+.Xr ntpd 8
+documentation for the meaning of these flags.
+There
+are two additional flags which are read only, the
+.Cm kernel_pll
+and
+.Cm kernel_pps .
+These flags indicate
+the synchronization status when the precision time kernel
+modifications are in use.
+The
+.Sq kernel_pll
+indicates that
+the local clock is being disciplined by the kernel, while the
+.Sq kernel_pps
+indicates the kernel discipline is provided by the PPS
+signal.
+.Pp
+The
+.Sq stability
+is the residual frequency error remaining
+after the system frequency correction is applied and is intended for
+maintenance and debugging.
+In most architectures, this value will
+initially decrease from as high as 500 ppm to a nominal value in
+the range .01 to 0.1 ppm.
+If it remains high for some time after
+starting the daemon, something may be wrong with the local clock,
+or the value of the kernel variable
+.Va kern.clockrate.tick
+may be
+incorrect.
+.Pp
+The
+.Sq broadcastdelay
+shows the default broadcast delay,
+as set by the
+.Ic broadcastdelay
+configuration command.
+.Pp
+The
+.Sq authdelay
+shows the default authentication delay,
+as set by the
+.Ic authdelay
+configuration command.
+.It Ic sysstats
+Print statistics counters maintained in the protocol
+module.
+.It Ic memstats
+Print statistics counters related to memory allocation
+code.
+.It Ic iostats
+Print statistics counters maintained in the input-output
+module.
+.It Ic timerstats
+Print statistics counters maintained in the timer/event queue
+support code.
+.It Ic reslist
+Obtain and print the server's restriction list.
+This list is
+(usually) printed in sorted order and may help to understand how
+the restrictions are applied.
+.It Ic monlist Op Ar version
+Obtain and print traffic counts collected and maintained by the
+monitor facility.
+The version number should not normally need to be
+specified.
+.It Ic clkbug Ar clock_peer_address Oo Ar ... Oc
+Obtain debugging information for a reference clock driver.
+This
+information is provided only by some clock drivers and is mostly
+undecodable without a copy of the driver source in hand.
+.El
+.Ss "Runtime Configuration Requests"
+All requests which cause state changes in the server are
+authenticated by the server using a configured NTP key (the
+facility can also be disabled by the server by not configuring a
+key).
+The key number and the corresponding key must also be made
+known to
+.Nm .
+This can be done using the
+.Ic keyid
+and
+.Ic passwd
+commands, the latter of which will prompt at the terminal for a
+password to use as the encryption key.
+You will also be prompted
+automatically for both the key number and password the first time a
+command which would result in an authenticated request to the
+server is given.
+Authentication not only provides verification that
+the requester has permission to make such changes, but also gives
+an extra degree of protection again transmission errors.
+.Pp
+Authenticated requests always include a timestamp in the packet
+data, which is included in the computation of the authentication
+code.
+This timestamp is compared by the server to its receive time
+stamp.
+If they differ by more than a small amount the request is
+rejected.
+This is done for two reasons.
+First, it makes simple
+replay attacks on the server, by someone who might be able to
+overhear traffic on your LAN, much more difficult.
+Second, it makes
+it more difficult to request configuration changes to your server
+from topologically remote hosts.
+While the reconfiguration facility
+will work well with a server on the local host, and may work
+adequately between time-synchronized hosts on the same LAN, it will
+work very poorly for more distant hosts.
+As such, if reasonable
+passwords are chosen, care is taken in the distribution and
+protection of keys and appropriate source address restrictions are
+applied, the run time reconfiguration facility should provide an
+adequate level of security.
+.Pp
+The following commands all make authenticated requests.
+.Bl -tag -width indent
+.It Xo Ic addpeer Ar peer_address
+.Op Ar keyid
+.Op Ar version
+.Op Cm prefer
+.Xc
+Add a configured peer association at the given address and
+operating in symmetric active mode.
+Note that an existing
+association with the same peer may be deleted when this command is
+executed, or may simply be converted to conform to the new
+configuration, as appropriate.
+If the optional
+.Ar keyid
+is a
+nonzero integer, all outgoing packets to the remote server will
+have an authentication field attached encrypted with this key.
+If
+the value is 0 (or not given) no authentication will be done.
+The
+.Ar version
+can be 1, 2 or 3 and defaults to 3.
+The
+.Cm prefer
+keyword indicates a preferred peer (and thus will
+be used primarily for clock synchronisation if possible).
+The
+preferred peer also determines the validity of the PPS signal - if
+the preferred peer is suitable for synchronisation so is the PPS
+signal.
+.It Xo Ic addserver Ar peer_address
+.Op Ar keyid
+.Op Ar version
+.Op Cm prefer
+.Xc
+Identical to the addpeer command, except that the operating
+mode is client.
+.It Xo Ic broadcast Ar peer_address
+.Op Ar keyid
+.Op Ar version
+.Op Cm prefer
+.Xc
+Identical to the addpeer command, except that the operating
+mode is broadcast.
+In this case a valid key identifier and key are
+required.
+The
+.Ar peer_address
+parameter can be the broadcast
+address of the local network or a multicast group address assigned
+to NTP.
+If a multicast address, a multicast-capable kernel is
+required.
+.It Ic unconfig Ar peer_address Oo Ar ... Oc
+This command causes the configured bit to be removed from the
+specified peer(s).
+In many cases this will cause the peer
+association to be deleted.
+When appropriate, however, the
+association may persist in an unconfigured mode if the remote peer
+is willing to continue on in this fashion.
+.It Xo Ic fudge Ar peer_address
+.Op Cm time1
+.Op Cm time2
+.Op Ar stratum
+.Op Ar refid
+.Xc
+This command provides a way to set certain data for a reference
+clock.
+See the source listing for further information.
+.It Xo Ic enable
+.Oo
+.Cm auth | Cm bclient |
+.Cm calibrate | Cm kernel |
+.Cm monitor | Cm ntp |
+.Cm pps | Cm stats
+.Oc
+.Xc
+.It Xo Ic disable
+.Oo
+.Cm auth | Cm bclient |
+.Cm calibrate | Cm kernel |
+.Cm monitor | Cm ntp |
+.Cm pps | Cm stats
+.Oc
+.Xc
+These commands operate in the same way as the
+.Ic enable
+and
+.Ic disable
+configuration file commands of
+.Xr ntpd 8 .
+.Bl -tag -width indent
+.It Cm auth
+Enables the server to synchronize with unconfigured peers only
+if the peer has been correctly authenticated using either public key
+or private key cryptography.
+The default for this flag is enable.
+.It Cm bclient
+Enables the server to listen for a message from a broadcast or
+multicast server, as in the multicastclient command with
+default address.
+The default for this flag is disable.
+.It Cm calibrate
+Enables the calibrate feature for reference clocks.
+The default for this flag is disable.
+.It Cm kernel
+Enables the kernel time discipline, if available.
+The default for this flag is enable if support is available, otherwise disable.
+.It Cm monitor
+Enables the monitoring facility.
+See the
+.Xr ntpdc 8 .
+program and the monlist command or further information.
+The default for this flag is enable.
+.It Cm ntp
+Enables time and frequency discipline.
+In effect, this switch opens and closes the feedback loop,
+which is useful for testing.
+The default for this flag is enable.
+.It Cm pps
+Enables the pulse-per-second (PPS) signal when frequency
+and time is disciplined by the precision time kernel modifications.
+See the
+.Qq A Kernel Model for Precision Timekeeping
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp )
+page for further information.
+The default for this flag is disable.
+.It Cm stats
+Enables the statistics facility.
+See the
+.Sx Monitoring Options
+section of
+.Xr ntp.conf 5
+for further information.
+The default for this flag is disable.
+.El
+.It Xo Ic restrict Ar address Ar mask
+.Ar flag Oo Ar ... Oc
+.Xc
+This command operates in the same way as the
+.Ic restrict
+configuration file commands of
+.Xr ntpd 8 .
+.It Xo Ic unrestrict Ar address Ar mask
+.Ar flag Oo Ar ... Oc
+.Xc
+Unrestrict the matching entry from the restrict list.
+.It Xo Ic delrestrict Ar address Ar mask
+.Op Cm ntpport
+.Xc
+Delete the matching entry from the restrict list.
+.It Ic readkeys
+Causes the current set of authentication keys to be purged and
+a new set to be obtained by rereading the keys file (which must
+have been specified in the
+.Xr ntpd 8
+configuration file).
+This
+allows encryption keys to be changed without restarting the
+server.
+.It Ic trustedkey Ar keyid Oo Ar ... Oc
+.It Ic untrustedkey Ar keyid Oo Ar ... Oc
+These commands operate in the same way as the
+.Ic trustedkey
+and
+.Ic untrustedkey
+configuration file
+commands of
+.Xr ntpd 8 .
+.It Ic authinfo
+Returns information concerning the authentication module,
+including known keys and counts of encryptions and decryptions
+which have been done.
+.It Ic traps
+Display the traps set in the server.
+See the source listing for
+further information.
+.It Xo Ic addtrap Ar address
+.Op Ar port
+.Op Ar interface
+.Xc
+Set a trap for asynchronous messages.
+See the source listing
+for further information.
+.It Xo Ic clrtrap Ar address
+.Op Ar port
+.Op Ar interface
+.Xc
+Clear a trap for asynchronous messages.
+See the source listing
+for further information.
+.It Ic reset
+Clear the statistics counters in various modules of the server.
+See the source listing for further information.
+.El
+.Sh SEE ALSO
+.Xr ntp.conf 5 ,
+.Xr ntpd 8
+.Rs
+.%A David L. Mills
+.%T Network Time Protocol (Version 3)
+.%O RFC1305
+.Re
+.Sh BUGS
+The
+.Nm
+utility is a crude hack.
+Much of the information it shows is
+deadly boring and could only be loved by its implementer.
+The
+program was designed so that new (and temporary) features were easy
+to hack in, at great expense to the program's ease of use.
+Despite
+this, the program is occasionally useful.
diff --git a/usr.sbin/ntp/doc/ntpq.8 b/usr.sbin/ntp/doc/ntpq.8
new file mode 100644
index 0000000..60a5fb9
--- /dev/null
+++ b/usr.sbin/ntp/doc/ntpq.8
@@ -0,0 +1,851 @@
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 17, 2006
+.Dt NTPQ 8
+.Os
+.Sh NAME
+.Nm ntpq
+.Nd standard NTP query program
+.Sh SYNOPSIS
+.Nm
+.Op Fl inp
+.Op Fl c Ar command
+.Op Ar host
+.Op Ar ...
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to monitor NTP daemon
+.Xr ntpd 8
+operations and determine performance.
+It uses the standard NTP mode 6 control message formats
+defined in Appendix B of the NTPv3 specification RFC1305.
+The same formats are used in NTPv4, although some of the variables
+have changed and new ones added.
+The description on this page is for the NTPv4 variables.
+.Pp
+The program can be run either in interactive mode or controlled
+using command line arguments.
+Requests to read and write arbitrary variables can be assembled,
+with raw and pretty-printed output options being available.
+The
+.Nm
+can also obtain and print a list of peers in a common format
+by sendingmultiple queries to the server.
+.Pp
+If one or more request options is included on the command line
+when
+.Nm
+is executed, each of the requests will be sent
+to the NTP servers running on each of the hosts given as command
+line arguments, or on localhost by default.
+If no request options
+are given,
+.Nm
+will attempt to read commands from the
+standard input and execute these on the NTP server running on the
+first host given on the command line, again defaulting to localhost
+when no other host is specified.
+The
+.Nm
+utility will prompt for
+commands if the standard input is a terminal device.
+.Pp
+The
+.Nm
+utility uses NTP mode 6 packets to communicate with the
+NTP server, and hence can be used to query any compatible server on
+the network which permits it.
+Note that since NTP is a UDP protocol
+this communication will be somewhat unreliable, especially over
+large distances in terms of network topology.
+The
+.Nm
+utility makes
+one attempt to retransmit requests, and will time requests out if
+the remote host is not heard from within a suitable timeout
+time.
+.Pp
+For examples and usage, see the
+.Qq NTP Debugging Techniques
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl 4
+Force DNS resolution of following host names on the command line to the
+IPv4 namespace.
+.It Fl 6
+Force DNS resolution of following host names on the command line to the
+IPv6 namespace.
+.It Fl c
+The following argument is interpreted as an interactive format
+command and is added to the list of commands to be executed on the
+specified host(s).
+Multiple
+.Fl c
+options may be given.
+.It Fl d
+Turn on debugging mode.
+.It Fl i
+Force
+.Nm
+to operate in interactive mode.
+Prompts
+will be written to the standard output and commands read from the
+standard input.
+.It Fl n
+Output all host addresses in dotted-quad numeric format rather
+than converting to the canonical host names.
+.It Fl p
+Print a list of the peers known to the server as well as a
+summary of their state.
+This is equivalent to the
+.Ic peers
+interactive command.
+.El
+.Pp
+Note that in contexts where a host name is expected, a
+.Fl 4
+qualifier preceding the host name forces DNS resolution to the
+IPv4 namespace, while a
+.Fl 6
+qualifier forces DNS resolution to the IPv6 namespace.
+Specifying a
+command line option other than
+.Fl i
+or
+.Fl n
+will
+cause the specified query (queries) to be sent to the indicated
+host(s) immediately.
+Otherwise,
+.Nm
+will attempt to read
+interactive format commands from the standard input.
+.Ss "Internal Commands"
+Interactive format commands consist of a keyword followed by zero
+to four arguments.
+Only enough characters of the full keyword to
+uniquely identify the command need be typed.
+The output of a
+command is normally sent to the standard output, but optionally the
+output of individual commands may be sent to a file by appending a
+.Ql \&> ,
+followed by a file name, to the command line.
+A
+number of interactive format commands are executed entirely within
+the
+.Nm
+utility itself and do not result in NTP mode 6
+requests being sent to a server.
+These are described following.
+.Bl -tag -width indent
+.It Ic \&? Op Ar command_keyword
+.It Ic help Op Ar command_keyword
+A
+.Sq Ic \&?
+by itself will print a list of all the command
+keywords known to this incarnation of
+.Nm .
+A
+.Sq Ic \&?
+followed by a command keyword will print function and usage
+information about the command.
+This command is probably a better
+source of information about
+.Nm
+than this manual
+page.
+.It Xo Ic addvars
+.Ar variable_name Ns Op = Ns Ar value ...
+.Xc
+.It Ic rmvars Ar variable_name ...
+.It Ic clearvars
+The data carried by NTP mode 6 messages consists of a list of
+items of the form
+.Ql variable_name=value ,
+where the
+.Ql =value
+is ignored, and can be omitted,
+in requests to the server to read variables.
+The
+.Nm
+utility maintains an internal list in which data to be included in control
+messages can be assembled, and sent using the
+.Ic readlist
+and
+.Ic writelist
+commands described below.
+The
+.Ic addvars
+command allows variables and their optional values to be added to
+the list.
+If more than one variable is to be added, the list should
+be comma-separated and not contain white space.
+The
+.Ic rmvars
+command can be used to remove individual variables from the list,
+while the
+.Ic clearlist
+command removes all variables from the
+list.
+.It Ic cooked
+Causes output from query commands to be "cooked", so that
+variables which are recognized by
+.Nm
+will have their
+values reformatted for human consumption.
+Variables which
+.Nm
+thinks should have a decodable value but did not are
+marked with a trailing
+.Ql \&? .
+.It Xo Ic debug
+.Cm more |
+.Cm less |
+.Cm off
+.Xc
+Turns internal query program debugging on and off.
+.It Ic delay Ar milliseconds
+Specify a time interval to be added to timestamps included in
+requests which require authentication.
+This is used to enable
+(unreliable) server reconfiguration over long delay network paths
+or between machines whose clocks are unsynchronized.
+Actually the
+server does not now require timestamps in authenticated requests,
+so this command may be obsolete.
+.It Ic host Ar hostname
+Set the host to which future queries will be sent.
+Hostname may
+be either a host name or a numeric address.
+.It Ic hostnames Cm yes | Cm no
+If
+.Cm yes
+is specified, host names are printed in
+information displays.
+If
+.Cm no
+is specified, numeric
+addresses are printed instead.
+The default is
+.Cm yes ,
+unless
+modified using the command line
+.Fl n
+switch.
+.It Ic keyid Ar keyid
+This command specifies the key number to be used to authenticate
+configuration requests.
+This must correspond to a key number the server has
+been configured to use for this purpose.
+.It Xo Ic ntpversion
+.Cm 1 |
+.Cm 2 |
+.Cm 3 |
+.Cm 4
+.Xc
+Sets the NTP version number which
+.Nm
+claims in
+packets.
+Defaults to 3, Note that mode 6 control messages (and
+modes, for that matter) did not exist in NTP version 1.
+There appear
+to be no servers left which demand version 1.
+.It Ic passwd
+This command prompts for a password (which will not be echoed) which will
+be used to authenticate configuration requests.
+The password must
+correspond to the key configured for NTP server for this purpose.
+.It Ic quit
+Exit
+.Nm .
+.It Ic raw
+Causes all output from query commands is printed as received
+from the remote server.
+The only formating/interpretation done on
+the data is to transform nonascii data into a printable (but barely
+understandable) form.
+.It Ic timeout Ar milliseconds
+Specify a timeout period for responses to server queries.
+The
+default is about 5000 milliseconds.
+Note that since
+.Nm
+retries each query once after a timeout, the total waiting time for
+a timeout will be twice the timeout value set.
+.El
+.Ss Control Message Commands
+Each association known to an NTP server has a 16 bit integer association
+identifier.
+NTP control messages which carry peer variables must identify the
+peer the values correspond to by including its association ID.
+An association
+ID of 0 is special, and indicates the variables are system variables, whose
+names are drawn from a separate name space.
+.Pp
+Control message commands result in one or more NTP mode 6
+messages being sent to the server, and cause the data returned to
+be printed in some format.
+Most commands currently implemented send
+a single message and expect a single response.
+The current
+exceptions are the peers command, which will send a preprogrammed
+series of messages to obtain the data it needs, and the mreadlist
+and mreadvar commands, which will iterate over a range of
+associations.
+.Bl -tag -width indent
+.It Ic associations
+Obtains and prints a list of association identifiers and peer
+statuses for in-spec peers of the server being queried.
+The list is
+printed in columns.
+The first of these is an index numbering the
+associations from 1 for internal use, the second the actual
+association identifier returned by the server and the third the
+status word for the peer.
+This is followed by a number of columns
+containing data decoded from the status word.
+See the peers command
+for a decode of the
+.Sq condition
+field.
+Note that the data
+returned by the
+.Ic associations
+command is cached internally
+in
+.Nm .
+The index is then of use when dealing with stupid
+servers which use association identifiers which are hard for humans
+to type, in that for any subsequent commands which require an
+association identifier as an argument, the form and index may be
+used as an alternative.
+.It Xo Ic clockvar Op Ar assocID
+.Oo
+.Ar variable_name Ns Op = Ns Ar value ...
+.Oc
+.Ar ...
+.Xc
+.It Xo Ic cv Op Ar assocID
+.Oo
+.Ar variable_name Ns Op = Ns Ar value ...
+.Oc
+.Ar ...
+.Xc
+Requests that a list of the server's clock variables be sent.
+Servers which have a radio clock or other external synchronization
+will respond positively to this.
+If the association identifier is
+omitted or zero the request is for the variables of the
+.Sq system clock
+and will generally get a positive response from all
+servers with a clock.
+If the server treats clocks as pseudo-peers,
+and hence can possibly have more than one clock connected at once,
+referencing the appropriate peer association ID will show the
+variables of a particular clock.
+Omitting the variable list will
+cause the server to return a default variable display.
+.It Ic lassociations
+Obtains and prints a list of association identifiers and peer
+statuses for all associations for which the server is maintaining
+state.
+This command differs from the
+.Ic associations
+command
+only for servers which retain state for out-of-spec client
+associations (i.e., fuzzballs).
+Such associations are normally
+omitted from the display when the
+.Ic associations
+command is
+used, but are included in the output of
+.Ic lassociations .
+.It Ic lpassociations
+Print data for all associations, including out-of-spec client
+associations, from the internally cached list of associations.
+This
+command differs from
+.Ic passociations
+only when dealing with
+fuzzballs.
+.It Ic lpeers
+Like R peers, except a summary of all associations for which
+the server is maintaining state is printed.
+This can produce a much
+longer list of peers from fuzzball servers.
+.It Ic mreadlist Ar assocID Ar assocID
+.It Ic mrl Ar assocID Ar assocID
+Like the
+.Ic readlist
+command, except the query is done
+for each of a range of (nonzero) association IDs.
+This range is
+determined from the association list cached by the most recent
+.Ic associations
+command.
+.It Xo Ic mreadvar Ar assocID Ar assocID
+.Oo
+.Ar variable_name Ns Op = Ns Ar value ...
+.Oc
+.Xc
+.It Xo Ic mrv Ar assocID Ar assocID
+.Oo
+.Ar variable_name Ns Op = Ns Ar value ...
+.Oc
+.Xc
+Like the
+.Ic readvar
+command, except the query is done for
+each of a range of (nonzero) association IDs.
+This range is
+determined from the association list cached by the most recent
+.Ic associations
+command.
+.It Ic opeers
+An old form of the
+.Ic peers
+command with the reference ID
+replaced by the local interface address.
+.It Ic passociations
+Displays association data concerning in-spec peers from the
+internally cached list of associations.
+This command performs
+identically to the
+.Ic associations
+except that it displays
+the internally stored data rather than making a new query.
+.It Ic peers
+Obtains a current list peers of the server, along with a
+summary of each peer's state.
+Summary information includes the
+address of the remote peer, the reference ID (0.0.0.0 if this is
+unknown), the stratum of the remote peer, the type of the peer
+(local, unicast, multicast or broadcast), when the last packet was
+received, the polling interval, in seconds, the reachability
+register, in octal, and the current estimated delay,
+offset and dispersion of the peer, all in milliseconds.
+The character at the left margin of each line shows the
+synchronization status of the association and is a valuable
+diagnostic tool.
+The encoding and meaning of this character,
+called the tally code, is given later in this page.
+.It Ic pstatus Ar assocID
+Sends a read status request to the server for the given
+association.
+The names and values of the peer variables returned
+will be printed.
+Note that the status word from the header is
+displayed preceding the variables, both in hexadecimal and in
+pidgeon English.
+.It Ic readlist Ar assocID
+.It Ic rl Ar assocID
+Requests that the values of the variables in the internal
+variable list be returned by the server.
+If the association ID is
+omitted or is 0 the variables are assumed to be system variables.
+Otherwise they are treated as peer variables.
+If the internal
+variable list is empty a request is sent without data, which should
+induce the remote server to return a default display.
+.It Xo Ic readvar Ar assocID
+.Ar variable_name Ns Op = Ns Ar value
+.Ar ...
+.Xc
+.It Xo Ic rv Ar assocID
+.Ar variable_name Ns Op = Ns Ar value
+.Ar ...
+.Xc
+Requests that the values of the specified variables be returned
+by the server by sending a read variables request.
+If the
+association ID is omitted or is given as zero the variables are
+system variables, otherwise they are peer variables and the values
+returned will be those of the corresponding peer.
+Omitting the
+variable list will send a request with no data which should induce
+the server to return a default display.
+The
+encoding and meaning of the variables derived from NTPv3 is given in
+RFC-1305; the encoding and meaning of the additional NTPv4 variables are
+given later in this page.
+.It Xo Ic writevar Ar assocID
+.Ar variable_name Ns Op = Ns Ar value
+.Ar ...
+.Xc
+Like the readvar request, except the specified variables are
+written instead of read.
+.It Ic writelist Op Ar assocID
+Like the readlist request, except the internal list variables
+are written instead of read.
+.El
+.Ss Tally Codes
+The character in the left margin in the
+.Sq peers
+billboard,
+called the tally code, shows the fate of each association
+in the clock selection process.
+Following is a list of these characters, the pigeon used
+in the
+.Ic rv
+command, and a short explanation of the condition revealed.
+.Bl -tag -width indent
+.It space
+.Pq reject
+The peer is discarded as unreachable, synchronized to this server (synch
+loop) or outrageous synchronization distance.
+.It x
+.Pq falsetick
+The peer is discarded by the intersection algorithm as a falseticker.
+.It \&.
+.Pq excess
+The peer is discarded as not among the first ten peers sorted by
+synchronization distance and so is probably a poor candidate for further
+consideration.
+.It \&-
+.Pq outlyer
+The peer is discarded by the clustering algorithm as an outlyer.
+.It \&+
+.Pq candidat
+The peer is a survivor and a candidate for the combining algorithm.
+.It \&#
+.Pq selected
+The peer is a survivor, but not among the first six peers sorted by
+synchronization distance.
+If the association is ephemeral, it may be
+demobilized to conserve resources.
+.It \&*
+.Pq sys.peer
+The peer has been declared the system peer and lends its variables to the
+system variables.
+.It o
+.Pq pps.peer
+The peer has been declared the system peer and lends its variables to
+the system variables.
+However, the actual system synchronization is derived
+from a pulse-per-second (PPS) signal, either indirectly via the PPS
+reference clock driver or directly via kernel interface.
+.El
+.Ss System Variables
+The
+.Cm status ,
+.Cm leap ,
+.Cm stratum ,
+.Cm precision ,
+.Cm rootdelay ,
+.Cm rootdispersion ,
+.Cm refid ,
+.Cm reftime ,
+.Cm poll ,
+.Cm offset ,
+and
+.Cm frequency
+variables are described in RFC-1305
+specification.
+Additional NTPv4 system variables include the following.
+.Bl -tag -width indent
+.It version
+Everything you might need to know about the software version and generation
+time.
+.It processor
+The processor and kernel identification string.
+.It system
+The operating system version and release identifier.
+.It state
+The state of the clock discipline state machine.
+The values are described
+in the architecture briefing on the NTP Project page linked from
+www.ntp.org.
+.It peer
+The internal integer used to identify the association currently designated
+the system peer.
+.It jitter
+The estimated time error of the system clock measured as an exponential
+average of RMS time differences.
+.It stability
+The estimated frequency stability of the system clock measured as an
+exponential average of RMS frequency differences.
+.El
+.Pp
+When the NTPv4 daemon is compiled with the OpenSSL software library, additional
+system variables are displayed, including some or all of the following,
+depending on the particular dance:
+.Bl -tag -width indent
+.It flags
+The current flags word bits and message digest algorithm identifier (NID)
+in hex format.
+The high order 16 bits of the four-byte word contain the NID
+from the OpenSSL ligrary, while the low-order bits are interpreted as
+follows:
+.Bl -tag -width indent
+.It 0x01
+autokey enabled
+.It 0x02
+NIST leapseconds file loaded
+.It 0x10
+PC identity scheme
+.It 0x20
+IFF identity scheme
+.It 0x40
+GQ identity scheme
+.El
+.It hostname
+The name of the host as returned by the Unix
+.Fn gethostname
+library
+function.
+.It hostkey
+The NTP filestamp of the host key file.
+.It cert
+A list of certificates held by the host.
+Each entry includes the subject,
+issuer, flags and NTP filestamp in order.
+The bits are interpreted as
+follows:
+.Bl -tag -width indent
+.It 0x01
+certificate has been signed by the server
+.It 0x02
+certificate is trusted
+.It 0x04
+certificate is private
+.It 0x08
+certificate contains errors and should not be trusted
+.El
+.It leapseconds
+The NTP filestamp of the NIST leapseconds file.
+.It refresh
+The NTP timestamp when the host public cryptographic values were refreshed
+and signed.
+.It signature
+The host digest/signature scheme name from the OpenSSL library.
+.It tai
+The TAI-UTC offset in seconds obtained from the NIST leapseconds table.
+.El
+.Ss Peer Variables
+The
+.Cm status ,
+.Cm srcadr ,
+.Cm srcport ,
+.Cm dstadr ,
+.Cm dstport ,
+.Cm leap ,
+.Cm stratum ,
+.Cm precision ,
+.Cm rootdelay ,
+.Cm rootdispersion ,
+.Cm readh ,
+.Cm hmode ,
+.Cm pmode ,
+.Cm hpoll ,
+.Cm ppoll ,
+.Cm offset ,
+.Cm delay ,
+.Cm dspersion ,
+.Cm reftime
+variables are described in the RFC-1305 specification, as
+are the timestamps
+.Cm org ,
+.Cm rec
+and
+.Cm xmt .
+Additional NTPv4 system variables include
+the following.
+.Bl -tag -width indent
+.It flash
+The flash code for the most recent packet received.
+The encoding and
+meaning of these codes is given later in this page.
+.It jitter
+The estimated time error of the peer clock measured as an exponential
+average of RMS time differences.
+.It unreach
+The value of the counter which records the number of poll intervals since
+the last valid packet was received.
+.El
+.Pp
+When the NTPv4 daemon is compiled with the OpenSSL software library, additional
+peer variables are displayed, including the following:
+.Bl -tag -width indent
+.It flags
+The current flag bits.
+This word is the server host status word with
+additional bits used by the Autokey state machine.
+See the source code for
+the bit encoding.
+.It hostname
+The server host name.
+.It initkey Ar key
+The initial key used by the key list generator in the Autokey protocol.
+.It initsequence Ar index
+The initial index used by the key list generator in the Autokey protocol.
+.It signature
+The server message digest/signature scheme name from the OpenSSL software
+library.
+.It timestamp Ar time
+The NTP timestamp when the last Autokey key list was generated and signed.
+.El
+.Ss Flash Codes
+The
+.Cm flash
+code is a valuable debugging aid displayed in the peer variables
+list.
+It shows the results of the original sanity checks defined in the NTP
+specification RFC-1305 and additional ones added in NTPv4.
+There are 12 tests
+designated
+.Sy TEST1
+through
+.Sy TEST12 .
+The tests are performed in a certain order
+designed to gain maximum diagnostic information while protecting against
+accidental or malicious errors.
+The
+.Sy flash
+variable is initialized to zero as
+each packet is received.
+If after each set of tests one or more bits are set,
+the packet is discarded.
+.Pp
+Tests
+.Sy TEST1
+through
+.Sy TEST3
+check the packet timestamps from which the offset and
+delay are calculated.
+If any bits are set, the packet is discarded; otherwise,
+the packet header variables are saved.
+.Sy TEST4
+and
+.Sy TEST5
+are associated with
+access control and cryptographic authentication.
+If any bits are set, the
+packet is discarded immediately with nothing changed.
+.Pp
+Tests
+.Sy TEST6
+through
+.Sy TEST8
+check the health of the server.
+If any bits are set,
+the packet is discarded; otherwise, the offset and delay relative to the server
+are calculated and saved.
+TEST9 checks the health of the association itself.
+If
+any bits are set, the packet is discarded; otherwise, the saved variables are
+passed to the clock filter and mitigation algorithms.
+.Pp
+Tests
+.Sy TEST10
+through
+.Sy TEST12
+check the authentication state using Autokey
+public-key cryptography, as described in the
+.Sx Authentication Options
+section of
+.Xr ntp.conf 5 .
+If
+any bits are set and the association has previously been marked reachable, the
+packet is discarded; otherwise, the originate and receive timestamps are saved,
+as required by the NTP protocol, and processing continues.
+.Pp
+The
+.Cm flash
+bits for each test are defined as follows.
+.Bl -tag -width indent
+.It 0x001
+.Pq TEST1
+Duplicate packet.
+The packet is at best a casual retransmission and at
+worst a malicious replay.
+.It 0x002
+.Pq TEST2
+Bogus packet.
+The packet is not a reply to a message previously sent.
+This
+can happen when the NTP daemon is restarted and before somebody else
+notices.
+.It 0x004
+.Pq TEST3
+Unsynchronized.
+One or more timestamp fields are invalid.
+This normally
+happens when the first packet from a peer is received.
+.It 0x008
+.Pq TEST4
+Access is denied.
+See the
+.Sx Access Control Support
+section of
+.Xr ntp.conf 5 .
+.It 0x010
+.Pq TEST5
+Cryptographic authentication fails.
+See the
+.Sx Authentication Options
+section of
+.Xr ntp.conf 5 .
+.It 0x020
+.Pq TEST6
+The server is unsynchronized.
+Wind up its clock first.
+.It 0x040
+.Pq TEST7
+The server stratum is at the maximum than 15.
+It is probably unsynchronized
+and its clock needs to be wound up.
+.It 0x080
+.Pq TEST8
+Either the root delay or dispersion is greater than one second, which is
+highly unlikely unless the peer is unsynchronized to Mars.
+.It 0x100
+.Pq TEST9
+Either the peer delay or dispersion is greater than one second, which is
+higly unlikely unless the peer is on Mars.
+.It 0x200
+.Pq TEST10
+The autokey protocol has detected an authentication failure.
+See the
+.Sx Authentication Options
+section of
+.Xr ntp.conf 5 .
+.It 0x400
+.Pq TEST11
+The autokey protocol has not verified the server or peer is proventic and
+has valid public key credentials.
+See the
+.Sx Authentication Options
+section of
+.Xr ntp.conf 5 .
+.It 0x800
+.Pq TEST12
+A protocol or configuration error has occurred in the public key algorithms
+or a possible intrusion event has been detected.
+See the
+.Sx Authentication Options
+section of
+.Xr ntp.conf 5 .
+.El
+.Sh SEE ALSO
+.Xr ntp.conf 5 ,
+.Xr ntpd 8 ,
+.Xr ntpdc 8
+.Sh BUGS
+The
+.Ic peers
+command is non-atomic and may occasionally result in
+spurious error messages about invalid associations occurring and
+terminating the command.
+The timeout time is a fixed constant,
+which means you wait a long time for timeouts since it assumes sort
+of a worst case.
+The program should improve the timeout estimate as
+it sends queries to a particular host, but does not.
diff --git a/usr.sbin/ntp/doc/ntptime.8 b/usr.sbin/ntp/doc/ntptime.8
new file mode 100644
index 0000000..f130307
--- /dev/null
+++ b/usr.sbin/ntp/doc/ntptime.8
@@ -0,0 +1,69 @@
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 7, 2000
+.Dt NTPTIME 8
+.Os
+.Sh NAME
+.Nm ntptime
+.Nd read kernel time variables
+.Sh SYNOPSIS
+.Nm
+.Op Fl chr
+.Op Fl e Ar est_error
+.Op Fl f Ar frequency
+.Op Fl m Ar max_error
+.Op Fl o Ar offset
+.Op Fl s Ar status
+.Op Fl t Ar time_constant
+.Sh DESCRIPTION
+The
+.Nm
+utility is useful only with special kernels
+described in the
+.Qo
+A Kernel Model for Precision Timekeeping
+.Qc
+page
+(available as part of the HTML documentation
+provided in
+.Pa /usr/share/doc/ntp ) .
+It reads and displays time-related kernel variables
+using the
+.Fn gettime
+and
+.Xr adjtime 2
+system calls if available.
+A similar display can be obtained using the
+.Xr ntpdc 8
+program's
+.Ic kerninfo
+command.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl c
+Display the execution time of
+.Nm
+itself.
+.It Fl e Ar est_error
+Specify estimated error, in microseconds.
+.It Fl f Ar frequency
+Specify frequency offset, in parts per million.
+.It Fl h
+Display help information.
+.It Fl l
+Specify the leap bits as a code from 0 to 3.
+.It Fl m Ar max_error
+Specify max possible errors, in microseconds.
+.It Fl o Ar offset
+Specify clock offset, in microseconds.
+.It Fl r
+Display Unix and NTP times in raw format.
+.It Fl s Ar status
+.It Fl t Ar time_constant
+Specify time constant, an integer in the range 0-4.
+.El
+.Sh SEE ALSO
+.Xr adjtime 2 ,
+.Xr ntpdc 8
diff --git a/usr.sbin/ntp/doc/ntptrace.8 b/usr.sbin/ntp/doc/ntptrace.8
new file mode 100644
index 0000000..554a3c0
--- /dev/null
+++ b/usr.sbin/ntp/doc/ntptrace.8
@@ -0,0 +1,76 @@
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 6, 2000
+.Dt NTPTRACE 8
+.Os
+.Sh NAME
+.Nm ntptrace
+.Nd "trace a chain of NTP servers back to the primary source"
+.Sh SYNOPSIS
+.Nm
+.Op Fl vdn
+.Op Fl r Ar retries
+.Op Fl t Ar timeout
+.Op Ar server
+.Sh DESCRIPTION
+The
+.Nm
+utility determines where a given Network Time Protocol (NTP) server gets
+its time from, and follows the chain of NTP servers back to their
+master time source.
+If given no arguments, it starts with
+.Dq localhost .
+.Pp
+Here is an example of the output from
+.Nm :
+.Bd -literal
+% ntptrace
+localhost: stratum 4, offset 0.0019529, synch distance 0.144135
+server2ozo.com: stratum 2, offset 0.0124263, synch distance 0.115784
+usndh.edu: stratum 1, offset 0.0019298, synch distance 0.011993, refid 'WWVB'
+.Ed
+.Pp
+On each line, the fields are (left to right): the host name, the
+host stratum,
+the time offset between that host and the local host
+(as measured by
+.Nm ;
+this is why it is not always zero for
+.Dq localhost ) ,
+the host
+synchronization distance,
+and (only for stratum-1 servers) the reference clock ID.
+All times
+are given in seconds.
+Note that the stratum is the server hop count to the primary source,
+while the synchronization distance is the estimated error
+relative to the primary source.
+These terms are precisely defined in RFC 1305.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d
+Turn on some debugging output.
+.It Fl n
+Turn off the printing of host names; instead, host IP addresses
+are given.
+This may be necessary if a nameserver is down.
+.It Fl r Ar retries
+Set the number of retransmission attempts for each host; the default is 5.
+.It Fl t Ar timeout
+Set the retransmission timeout (in seconds); the default is 2.
+.It Fl v
+Print verbose information about the NTP servers.
+.El
+.Sh SEE ALSO
+.Xr ntpd 8 ,
+.Xr ntpdc 8
+.Rs
+.%A D L Mills
+.%T Network Time Protocol (Version 3)
+.%O RFC1305
+.Re
+.Sh BUGS
+This program makes no attempt to improve accuracy by doing multiple
+samples.
diff --git a/usr.sbin/ntp/libntp/Makefile b/usr.sbin/ntp/libntp/Makefile
new file mode 100644
index 0000000..83e654e
--- /dev/null
+++ b/usr.sbin/ntp/libntp/Makefile
@@ -0,0 +1,37 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/libntp \
+ ${.CURDIR}/../../../contrib/ntp/libisc
+
+LIB= ntp
+INTERNALLIB=
+
+NTP_SRCS= a_md5encrypt.c adjtime.c atoint.c atolfp.c \
+ atouint.c audio.c authkeys.c \
+ authreadkeys.c authusekey.c \
+ buftvtots.c caljulian.c caltontp.c \
+ calyearstart.c clocktime.c clocktypes.c decodenetnum.c \
+ dofptoa.c dolfptoa.c emalloc.c findconfig.c \
+ fptoa.c fptoms.c getopt.c \
+ hextoint.c hextolfp.c humandate.c icom.c \
+ inttoa.c iosignal.c lib_strbuf.c \
+ machines.c md5c.c memmove.c \
+ mfptoa.c mfptoms.c mktime.c modetoa.c \
+ mstolfp.c ntp_random.c \
+ msutotsf.c msyslog.c netof.c ntp_rfc2553.c \
+ numtoa.c numtohost.c octtoint.c prettydate.c \
+ recvbuff.c refnumtoa.c snprintf.c socktoa.c \
+ socktohost.c strstr.c systime.c statestr.c \
+ strerror.c syssignal.c tsftomsu.c tstotv.c \
+ tvtoa.c tvtots.c uglydate.c uinttoa.c \
+ utvtoa.c ymd2yd.c
+
+ISC_SRCS= assertions.c error.c inet_ntop.c inet_pton.c interfaceiter.c \
+ isc_strerror.c lib.c mem.c msgcat.c net.c netscope.c netaddr.c \
+ sockaddr.c
+
+SRCS= ${NTP_SRCS} ${ISC_SRCS}
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/ntp/libopts/Makefile b/usr.sbin/ntp/libopts/Makefile
new file mode 100644
index 0000000..c867ecc
--- /dev/null
+++ b/usr.sbin/ntp/libopts/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/libopts
+
+LIB= opts
+INTERNALLIB=
+
+SRCS= libopts.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../ \
+ -I${.CURDIR}/../../../contrib/ntp/libopts
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/ntp/libparse/Makefile b/usr.sbin/ntp/libparse/Makefile
new file mode 100644
index 0000000..f86988e
--- /dev/null
+++ b/usr.sbin/ntp/libparse/Makefile
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/libparse
+
+LIB= parse
+INTERNALLIB=
+
+SRCS= clk_computime.c clk_dcf7000.c clk_hopf6021.c clk_meinberg.c \
+ clk_rawdcf.c clk_rcc8000.c clk_schmid.c clk_trimtaip.c \
+ clk_trimtsip.c clk_varitext.c clk_wharton.c data_mbg.c \
+ info_trimble.c parse.c parse_conf.c trim_info.c \
+ binio.c gpstolfp.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/ntp/ntp-keygen/Makefile b/usr.sbin/ntp/ntp-keygen/Makefile
new file mode 100644
index 0000000..a0fee44
--- /dev/null
+++ b/usr.sbin/ntp/ntp-keygen/Makefile
@@ -0,0 +1,24 @@
+# $FreeBSD$
+
+NO_MAN=
+
+.include <bsd.own.mk>
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/util \
+ ${.CURDIR}/../../../contrib/ntp/ntpd
+
+PROG= ntp-keygen
+SRCS= ntp-keygen.c ntp-keygen-opts.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../ \
+ -I${.CURDIR}/../../../contrib/ntp/libopts
+
+DPADD= ${LIBNTP} ${LIBOPTS}
+LDADD= ${LIBNTP} ${LIBOPTS}
+
+.if ${MK_OPENSSL} != "no" && !defined(RELEASE_CRUNCH)
+DPADD+= ${LIBMD} ${LIBCRYPTO}
+LDADD+= -lmd -lcrypto
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ntp/ntpd/Makefile b/usr.sbin/ntp/ntpd/Makefile
new file mode 100644
index 0000000..175ad67
--- /dev/null
+++ b/usr.sbin/ntp/ntpd/Makefile
@@ -0,0 +1,48 @@
+# $FreeBSD$
+
+NO_MAN=
+
+.include <bsd.own.mk>
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/ntpd
+
+PROG= ntpd
+SRCS= cmd_args.c ntp_config.c \
+ ntp_control.c ntp_crypto.c ntp_filegen.c \
+ ntp_intres.c ntp_io.c ntp_loopfilter.c \
+ ntp_monitor.c ntp_peer.c ntp_proto.c \
+ ntp_refclock.c ntp_request.c \
+ ntp_restrict.c ntp_timer.c ntp_util.c \
+ ntpd.c refclock_acts.c refclock_arbiter.c \
+ refclock_arc.c refclock_as2201.c refclock_atom.c \
+ refclock_bancomm.c refclock_chronolog.c refclock_chu.c \
+ refclock_conf.c refclock_datum.c refclock_dumbclock.c \
+ refclock_fg.c refclock_gpsvme.c refclock_heath.c \
+ refclock_hopfpci.c refclock_hopfser.c refclock_hpgps.c \
+ refclock_irig.c refclock_jupiter.c refclock_leitch.c \
+ refclock_local.c refclock_msfees.c refclock_mx4200.c \
+ refclock_neoclock4x.c refclock_nmea.c refclock_oncore.c \
+ refclock_palisade.c refclock_parse.c refclock_pcf.c \
+ refclock_pst.c refclock_ripencc.c \
+ refclock_shm.c refclock_tpro.c refclock_trak.c refclock_true.c \
+ refclock_ulink.c refclock_wwv.c \
+ refclock_wwvb.c ntpd-opts.c \
+ version.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../ \
+ -I${.CURDIR}/../../../contrib/ntp/libopts -I${.CURDIR}
+
+DPADD= ${LIBPARSE} ${LIBNTP} ${LIBM} ${LIBMD} ${LIBRT} ${LIBOPTS}
+LDADD= ${LIBPARSE} ${LIBNTP} -lm -lmd -lrt ${LIBOPTS}
+
+.if ${MK_OPENSSL} != "no" && !defined(RELEASE_CRUNCH)
+DPADD+= ${LIBCRYPTO}
+LDADD+= -lcrypto
+.endif
+
+CLEANFILES+= .version version.c
+
+version.c:
+ sh -e ${.CURDIR}/../scripts/mkver ntpd
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ntp/ntpdate/Makefile b/usr.sbin/ntp/ntpdate/Makefile
new file mode 100644
index 0000000..0814e8e
--- /dev/null
+++ b/usr.sbin/ntp/ntpdate/Makefile
@@ -0,0 +1,19 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/ntpdate
+
+PROG= ntpdate
+NO_MAN=
+SRCS= ntpdate.c version.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../
+
+DPADD= ${LIBNTP} ${LIBM} ${LIBMD} ${LIBRT}
+LDADD= ${LIBNTP} -lm -lmd -lrt
+
+CLEANFILES+= .version version.c
+
+version.c:
+ sh -e ${.CURDIR}/../scripts/mkver ntpdate
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ntp/ntpdc/Makefile b/usr.sbin/ntp/ntpdc/Makefile
new file mode 100644
index 0000000..75cfec0
--- /dev/null
+++ b/usr.sbin/ntp/ntpdc/Makefile
@@ -0,0 +1,33 @@
+# $FreeBSD$
+
+NO_MAN=
+
+.include <bsd.own.mk>
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/ntpdc
+
+PROG= ntpdc
+SRCS= ntpdc.c ntpdc_ops.c ntpdc-opts.c version.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../ \
+ -I${.CURDIR}/../../../contrib/ntp/libopts
+
+DPADD= ${LIBNTP} ${LIBM} ${LIBMD} ${LIBOPTS}
+LDADD= ${LIBNTP} -lm -lmd ${LIBOPTS}
+
+.if ${MK_GNU_SUPPORT} != "no"
+DPADD+= ${LIBREADLINE} ${LIBTERMCAP}
+LDADD+= -lreadline -ltermcap
+CFLAGS+= -DHAVE_LIBREADLINE -DHAVE_READLINE_HISTORY_H \
+ -DHAVE_READLINE_READLINE_H
+.endif
+
+CLEANFILES+= .version version.c
+
+version.c:
+ sh -e ${.CURDIR}/../scripts/mkver ntpdc
+
+afterinstall:
+ rm -f ${DESTDIR}/usr/sbin/xntpdc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ntp/ntpq/Makefile b/usr.sbin/ntp/ntpq/Makefile
new file mode 100644
index 0000000..42cc750
--- /dev/null
+++ b/usr.sbin/ntp/ntpq/Makefile
@@ -0,0 +1,32 @@
+# $FreeBSD$
+
+NO_MAN=
+
+.include <bsd.own.mk>
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/ntpq
+
+BINDIR= /usr/bin
+
+PROG= ntpq
+SRCS= ntpq.c ntpq-opts.c ntpq-subs.c version.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../ \
+ -I${.CURDIR}/../../../contrib/ntp/libopts
+
+DPADD= ${LIBNTP} ${LIBM} ${LIBMD} ${LIBOPTS}
+LDADD= ${LIBNTP} -lm -lmd ${LIBOPTS}
+
+.if ${MK_GNU_SUPPORT} != "no"
+DPADD+= ${LIBREADLINE} ${LIBTERMCAP}
+LDADD+= -lreadline -ltermcap
+CFLAGS+= -DHAVE_LIBREADLINE -DHAVE_READLINE_HISTORY_H \
+ -DHAVE_READLINE_READLINE_H
+.endif
+
+CLEANFILES+= .version version.c
+
+version.c:
+ sh -e ${.CURDIR}/../scripts/mkver ntpq
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ntp/ntptime/Makefile b/usr.sbin/ntp/ntptime/Makefile
new file mode 100644
index 0000000..db5885d
--- /dev/null
+++ b/usr.sbin/ntp/ntptime/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/util
+
+PROG= ntptime
+NO_MAN=
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../
+
+DPADD= ${LIBNTP}
+LDADD= ${LIBNTP}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ntp/ntptrace/Makefile b/usr.sbin/ntp/ntptrace/Makefile
new file mode 100644
index 0000000..8d7547c
--- /dev/null
+++ b/usr.sbin/ntp/ntptrace/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../scripts
+
+SCRIPTS= ntptrace
+NO_MAN=
+NO_OBJ=
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ntp/scripts/mkver b/usr.sbin/ntp/scripts/mkver
new file mode 100755
index 0000000..02c6924
--- /dev/null
+++ b/usr.sbin/ntp/scripts/mkver
@@ -0,0 +1,46 @@
+#!/bin/sh
+#
+# $FreeBSD$
+#
+PROG=${1-UNKNOWN}
+
+ConfStr="$PROG"
+
+ConfStr="$ConfStr 4.2.4p5"
+
+case "$CSET" in
+ '') ;;
+ *) ConfStr="$ConfStr@$CSET" ;;
+esac
+
+case "" in
+ '')
+ case "1" in
+ '') ;;
+ *) ConfStr="${ConfStr}-a" ;;
+ esac
+ ;;
+ *) ConfStr="${ConfStr}-r" ;;
+esac
+
+ConfStr="$ConfStr `LC_ALL=C date`"
+
+if [ ! -f .version ]; then
+ echo 0 > .version
+fi
+RUN="`cat .version`"
+RUN="`expr $RUN + 1`"
+echo $RUN > .version
+
+ConfStr="$ConfStr (${RUN})"
+
+echo "Version <${ConfStr}>";
+
+rm -f version.c
+cat > version.c << -EoF-
+/*
+ * version file for $PROG
+ */
+#include <config.h>
+const char * Version = "${ConfStr}";
+-EoF-
diff --git a/usr.sbin/ntp/scripts/ntptrace b/usr.sbin/ntp/scripts/ntptrace
new file mode 100644
index 0000000..8a895c4
--- /dev/null
+++ b/usr.sbin/ntp/scripts/ntptrace
@@ -0,0 +1,62 @@
+#! /usr/local/bin/perl -w
+#
+# $FreeBSD$
+
+# John Hay -- John.Hay@icomtek.csir.co.za / jhay@FreeBSD.org
+
+use Socket;
+use Getopt::Std;
+use vars qw($opt_n);
+
+$ntpq = "ntpq";
+
+getopts('n');
+
+$dodns = 1;
+$dodns = 0 if (defined($opt_n));
+
+$host = shift;
+$host ||= "127.0.0.1";
+
+for (;;) {
+ $stratum = 255;
+ $cmd = "$ntpq -n -c rv $host";
+ open(PH, $cmd . "|") || die "failed to start command $cmd: $!";
+ while (<PH>) {
+ $stratum = $1 if (/stratum=(\d+)/);
+ $peer = $1 if (/peer=(\d+)/);
+ # Very old servers report phase and not offset.
+ $offset = $1 if (/(?:offset|phase)=([^\s,]+)/);
+ $rootdelay = $1 if (/rootdelay=([^\s,]+)/);
+ $refid = $1 if (/refid=([^\s,]+)/);
+ }
+ close(PH) || die "$cmd failed";
+ last if ($stratum == 255);
+ $offset /= 1000;
+ $rootdelay /= 1000;
+ $dhost = $host;
+ # Only do lookups of IPv4 addresses. The standard lookup functions
+ # of perl only do IPv4 and I don't know if we should require extras.
+ if ($dodns && $host =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/) {
+ $iaddr = inet_aton($host);
+ $name = (gethostbyaddr($iaddr, AF_INET))[0];
+ $dhost = $name if (defined($name));
+ }
+ printf("%s: stratum %d, offset %f, root distance %f",
+ $dhost, $stratum, $offset, $rootdelay);
+ printf(", refid '%s'", $refid) if ($stratum == 1);
+ printf("\n");
+ last if ($stratum == 0 || $stratum == 1 || $stratum == 16);
+ last if ($refid =~ /^127\.127\.\d{1,3}\.\d{1,3}$/);
+
+ $cmd = "$ntpq -n -c \"pstat $peer\" $host";
+ open(PH, $cmd . "|") || die "failed to start command $cmd: $!";
+ $thost = "";
+ while (<PH>) {
+ $thost = $1, last if (/srcadr=(\S+),/);
+ }
+ close(PH) || die "$cmd failed";
+ last if ($thost eq "");
+ $host = $thost;
+}
+
diff --git a/usr.sbin/ntp/scripts/ntpver b/usr.sbin/ntp/scripts/ntpver
new file mode 100755
index 0000000..6dbc510
--- /dev/null
+++ b/usr.sbin/ntp/scripts/ntpver
@@ -0,0 +1,8 @@
+#!/bin/sh
+# $FreeBSD$
+# print version string of NTP daemon
+# Copyright (c) 1997 by Ulrich Windl
+# Modified 970318: Harlan Stenn: rewritten...
+# usage: ntpver hostname
+
+ntpq -c "rv 0 daemon_version" $* | awk '/daemon_version/ { print $2 }'
diff --git a/usr.sbin/ntp/sntp/Makefile b/usr.sbin/ntp/sntp/Makefile
new file mode 100644
index 0000000..3bc9084
--- /dev/null
+++ b/usr.sbin/ntp/sntp/Makefile
@@ -0,0 +1,16 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../../contrib/ntp/sntp
+
+PROG= sntp
+NO_MAN=
+SRCS= internet.c main.c socket.c timing.c unix.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/ntp/include -I${.CURDIR}/../ \
+ -DPACKAGE=\"sntp\" -DVERSION=\"1.6\" \
+ -I${.CURDIR}/../../../contrib/ntp/libopts
+
+DPADD= ${LIBM} ${LIBOPTS}
+LDADD= -lm ${LIBOPTS}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/nvram/Makefile b/usr.sbin/nvram/Makefile
new file mode 100644
index 0000000..3cca037
--- /dev/null
+++ b/usr.sbin/nvram/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= nvram
+MAN= nvram.8
+MANSUBDIR= /powerpc
+
+WARNS?= 6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/nvram/nvram.8 b/usr.sbin/nvram/nvram.8
new file mode 100644
index 0000000..5c41a41
--- /dev/null
+++ b/usr.sbin/nvram/nvram.8
@@ -0,0 +1,118 @@
+.\"-
+.\" Copyright (c) 2006 Maxim Sobolev <sobomax@FreeBSD.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+.\" DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+.\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 1, 2006
+.Dt NVRAM 8 powerpc
+.Os
+.Sh NAME
+.Nm nvram
+.Nd "display or modify contents of the EEPROM or NVRAM"
+.Sh SYNOPSIS
+.Nm
+.Fl p
+.Nm
+.Oo Fl d Ar name Oc Ar ...
+.Op Ar name Ns = Ns Ar value ...
+.Sh DESCRIPTION
+The
+.Nm
+utility provides an interface for displaying and changing the system's
+configuration variables contained in EEPROM or NVRAM.
+In the first synopsis form, all available configuration variables and their
+current values are printed.
+In the second form, the variable selected by
+.Ar name
+is either removed or its value is changed to the
+.Ar value
+following by
+.Ql =
+sign.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d Ar name
+Delete the variable selected by
+.Ar name
+from the EEPROM or NVRAM.
+The
+.Fl d
+flag can be specified multiple times, in which case multiple variables
+will be removed.
+.It Fl p
+Print all available configuration variables and their current values.
+.El
+.Sh EXAMPLES
+Print all available configuration variables and their current values:
+.Pp
+.Dl "nvram -p"
+.Pp
+Remove the variable named
+.Va local-mac-address? :
+.Pp
+.Dl "nvram -d local-mac-address\e?"
+.Pp
+Set the value of the
+.Va local-mac-address?
+variable to
+.Dq Li true :
+.Pp
+.Dl "nvram local-mac-address\e?=true"
+.Pp
+Note that the
+.Ql \e
+in the above examples is used to keep the shell from interpreting the
+.Ql \&? .
+.Pp
+Remove variables named
+.Va foo
+and
+.Va bar
+and set variable named
+.Va baz
+to
+.Dq Li 100 :
+.Pp
+.Dl "nvram -d foo -d bar baz=100"
+.Sh SEE ALSO
+.Xr powermac_nvram 4 ,
+.Xr eeprom 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 7.0 .
+It is inspired by the
+Darwin/Mac OS X
+.Nm
+utility.
+.Sh AUTHORS
+.An Maxim Sobolev Aq sobomax@FreeBSD.org
+.Sh BUGS
+Currently,
+.Nm
+only supports systems equipped with AMD flash and is only tested on Apple
+G4-based Mac Mini machines.
diff --git a/usr.sbin/nvram/nvram.c b/usr.sbin/nvram/nvram.c
new file mode 100644
index 0000000..9d56f9d
--- /dev/null
+++ b/usr.sbin/nvram/nvram.c
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2006 Maxim Sobolev <sobomax@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <dev/powermac_nvram/powermac_nvramvar.h>
+
+#define DEVICE_NAME (_PATH_DEV "powermac_nvram")
+
+static void usage(void);
+static int remove_var(uint8_t *, int, const char *);
+static int append_var(uint8_t *, int, const char *, const char *);
+
+struct deletelist {
+ char *name;
+ struct deletelist *next;
+ struct deletelist *last;
+};
+
+int
+main(int argc, char **argv)
+{
+ int opt, dump, fd, res, i, size;
+ uint8_t buf[NVRAM_SIZE], *cp, *common;
+ struct chrp_header *header;
+ struct deletelist *dl;
+
+ dump = 0;
+ dl = NULL;
+
+ while((opt = getopt(argc, argv, "d:p")) != -1) {
+ switch(opt) {
+ case 'p':
+ dump = 1;
+ break;
+
+ case 'd':
+ if (dl == NULL) {
+ dl = malloc(sizeof(*dl));
+ if (dl == NULL)
+ err(1, "malloc");
+ bzero(dl, sizeof(*dl));
+ dl->last = dl;
+ } else {
+ dl->last->next = malloc(sizeof(*dl));
+ if (dl->last->next == NULL)
+ err(1, "malloc");
+ dl->last = dl->last->next;
+ bzero(dl->last, sizeof(*dl));
+ }
+ dl->last->name = optarg;
+ break;
+
+ default:
+ usage();
+ /* Not reached */
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0 && dump == 0 && dl == NULL) {
+ usage();
+ /* Not reached */
+ }
+
+ fd = open(DEVICE_NAME, O_RDWR);
+ if (fd == -1)
+ err(1, DEVICE_NAME);
+ for (i = 0; i < (int)sizeof(buf);) {
+ res = read(fd, buf + i, sizeof(buf) - i);
+ if (res == -1 && errno != EINTR)
+ err(1, DEVICE_NAME);
+ if (res == 0)
+ break;
+ if (res > 0)
+ i += res;
+ }
+ if (i != sizeof(buf))
+ errx(1, "%s: short read", DEVICE_NAME);
+
+ /* Locate common block */
+ size = 0;
+ for (cp = buf; cp < buf + sizeof(buf); cp += size) {
+ header = (struct chrp_header *)cp;
+ size = header->length * 0x10;
+ if (strncmp(header->name, "common", 7) == 0)
+ break;
+ }
+ if (cp >= buf + sizeof(buf) || size <= (int)sizeof(struct chrp_header))
+ errx(1, "%s: no common block", DEVICE_NAME);
+ common = cp + sizeof(struct chrp_header);
+ size -= sizeof(struct chrp_header);
+
+ if (dump != 0) {
+ while (size > 0) {
+ i = strlen(common) + 1;
+ if (i == 1)
+ break;
+ printf("%s\n", common);
+ size -= i;
+ common += i;
+ }
+ exit(0);
+ }
+
+ for (;dl != NULL; dl = dl->next) {
+ if (remove_var(common, size, dl->name) == 0)
+ warnx("%s: no such variable", dl->name);
+ }
+
+ for (; argc > 0; argc--, argv++) {
+ cp = strchr(*argv, '=');
+ if (cp == NULL)
+ errx(1, "%s: invalid argument", *argv);
+ cp[0] = '\0';
+ cp++;
+ remove_var(common, size, *argv);
+ if (append_var(common, size, *argv, cp) == -1)
+ errx(1, "%s: error setting variable", *argv);
+ }
+
+ for (i = 0; i < (int)sizeof(buf);) {
+ res = write(fd, buf + i, sizeof(buf) - i);
+ if (res == -1 && errno != EINTR)
+ err(1, DEVICE_NAME);
+ if (res == 0)
+ break;
+ if (res > 0)
+ i += res;
+ }
+ if (i != sizeof(buf))
+ errx(1, "%s: short write", DEVICE_NAME);
+ if (close(fd) == -1)
+ err(1, DEVICE_NAME);
+
+ exit(0);
+}
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "usage: nvram [-p] | [-d name ...] [name=value ...]\n");
+ exit(1);
+}
+
+static int
+remove_var(uint8_t *buf, int len, const char *var_name)
+{
+ int nremoved, i, name_len;
+
+ nremoved = 0;
+ name_len = strlen(var_name);
+ while (len > 0) {
+ i = strlen(buf) + 1;
+ if (i == 1)
+ break;
+ if (strncmp(buf, var_name, name_len) == 0 && buf[name_len] == '=') {
+ memmove(buf, buf + i, len - i);
+ memset(buf + len - i, '\0', i);
+ nremoved += 1;
+ continue;
+ }
+ len -= i;
+ buf += i;
+ }
+ return nremoved;
+}
+
+static int
+append_var(uint8_t *buf, int len, const char *var_name, const char *var_value)
+{
+ int i, append_len;
+
+ while (len > 0) {
+ i = strlen(buf) + 1;
+ if (i == 1)
+ break;
+ len -= i;
+ buf += i;
+ }
+ append_len = strlen(var_name) + strlen(var_value) + 2;
+ if (len < append_len)
+ return -1;
+ sprintf(buf, "%s=%s", var_name, var_value);
+ return 0;
+}
diff --git a/usr.sbin/ofwdump/Makefile b/usr.sbin/ofwdump/Makefile
new file mode 100644
index 0000000..a20e94f
--- /dev/null
+++ b/usr.sbin/ofwdump/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= ofwdump
+MAN= ofwdump.8
+SRCS= ofwdump.c ofw_util.c
+
+WARNS?= 6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ofwdump/ofw_util.c b/usr.sbin/ofwdump/ofw_util.c
new file mode 100644
index 0000000..71361d4
--- /dev/null
+++ b/usr.sbin/ofwdump/ofw_util.c
@@ -0,0 +1,230 @@
+/*-
+ * Copyright (c) 2002 by Thomas Moestl <tmm@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (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 <dev/ofw/openfirmio.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "pathnames.h"
+#include "ofw_util.h"
+
+#define OFW_IOCTL(fd, cmd, val) do { \
+ if (ioctl(fd, cmd, val) == -1) \
+ err(EX_IOERR, "ioctl(..., " #cmd ", ...) failed"); \
+} while (0)
+
+int
+ofw_open(int mode)
+{
+ int fd;
+
+ if ((fd = open(PATH_DEV_OPENFIRM, mode)) == -1)
+ err(EX_UNAVAILABLE, "could not open " PATH_DEV_OPENFIRM);
+ return (fd);
+}
+
+void
+ofw_close(int fd)
+{
+
+ close(fd);
+}
+
+phandle_t
+ofw_root(int fd)
+{
+
+ return (ofw_peer(fd, 0));
+}
+
+phandle_t
+ofw_optnode(int fd)
+{
+ phandle_t rv;
+
+ OFW_IOCTL(fd, OFIOCGETOPTNODE, &rv);
+ return (rv);
+}
+
+phandle_t
+ofw_peer(int fd, phandle_t node)
+{
+ phandle_t rv;
+
+ rv = node;
+ OFW_IOCTL(fd, OFIOCGETNEXT, &rv);
+ return (rv);
+}
+
+phandle_t
+ofw_child(int fd, phandle_t node)
+{
+ phandle_t rv;
+
+ rv = node;
+ OFW_IOCTL(fd, OFIOCGETCHILD, &rv);
+ return (rv);
+}
+
+phandle_t
+ofw_finddevice(int fd, const char *name)
+{
+ struct ofiocdesc d;
+
+ d.of_nodeid = 0;
+ d.of_namelen = strlen(name);
+ d.of_name = name;
+ d.of_buflen = 0;
+ d.of_buf = NULL;
+ if (ioctl(fd, OFIOCFINDDEVICE, &d) == -1) {
+ if (errno == ENOENT)
+ err(EX_UNAVAILABLE, "Node '%s' not found", name);
+ else
+ err(EX_IOERR,
+ "ioctl(..., OFIOCFINDDEVICE, ...) failed");
+ }
+ return (d.of_nodeid);
+}
+
+int
+ofw_firstprop(int fd, phandle_t node, char *buf, int buflen)
+{
+
+ return (ofw_nextprop(fd, node, NULL, buf, buflen));
+}
+
+int
+ofw_nextprop(int fd, phandle_t node, const char *prev, char *buf, int buflen)
+{
+ struct ofiocdesc d;
+
+ d.of_nodeid = node;
+ d.of_namelen = prev != NULL ? strlen(prev) : 0;
+ d.of_name = prev;
+ d.of_buflen = buflen;
+ d.of_buf = buf;
+ if (ioctl(fd, OFIOCNEXTPROP, &d) == -1) {
+ if (errno == ENOENT)
+ return (0);
+ else
+ err(EX_IOERR, "ioctl(..., OFIOCNEXTPROP, ...) failed");
+ }
+ return (d.of_buflen);
+}
+
+static void *
+ofw_malloc(int size)
+{
+ void *p;
+
+ if ((p = malloc(size)) == NULL)
+ err(EX_OSERR, "malloc() failed");
+ return (p);
+}
+
+int
+ofw_getprop(int fd, phandle_t node, const char *name, void *buf, int buflen)
+{
+ struct ofiocdesc d;
+
+ d.of_nodeid = node;
+ d.of_namelen = strlen(name);
+ d.of_name = name;
+ d.of_buflen = buflen;
+ d.of_buf = buf;
+ OFW_IOCTL(fd, OFIOCGET, &d);
+ return (d.of_buflen);
+}
+
+int
+ofw_setprop(int fd, phandle_t node, const char *name, const void *buf,
+ int buflen)
+{
+ struct ofiocdesc d;
+
+ d.of_nodeid = node;
+ d.of_namelen = strlen(name);
+ d.of_name = name;
+ d.of_buflen = buflen;
+ d.of_buf = ofw_malloc(buflen);
+ memcpy(d.of_buf, buf, buflen);
+ OFW_IOCTL(fd, OFIOCSET, &d);
+ free(d.of_buf);
+ return (d.of_buflen);
+}
+
+int
+ofw_getproplen(int fd, phandle_t node, const char *name)
+{
+ struct ofiocdesc d;
+
+ d.of_nodeid = node;
+ d.of_namelen = strlen(name);
+ d.of_name = name;
+ OFW_IOCTL(fd, OFIOCGETPROPLEN, &d);
+ return (d.of_buflen);
+}
+
+int
+ofw_getprop_alloc(int fd, phandle_t node, const char *name, void **buf,
+ int *buflen, int reserve)
+{
+ struct ofiocdesc d;
+ int len, rv;
+
+ do {
+ len = ofw_getproplen(fd, node, name);
+ if (len < 0)
+ return (len);
+ if (*buflen < len + reserve) {
+ if (*buf != NULL)
+ free(*buf);
+ *buflen = len + reserve + OFIOCMAXVALUE;
+ *buf = ofw_malloc(*buflen);
+ }
+ d.of_nodeid = node;
+ d.of_namelen = strlen(name);
+ d.of_name = name;
+ d.of_buflen = *buflen - reserve;
+ d.of_buf = *buf;
+ rv = ioctl(fd, OFIOCGET, &d);
+ } while (rv == -1 && errno == ENOMEM);
+ if (rv == -1)
+ err(EX_IOERR, "ioctl(..., OFIOCGET, ...) failed");
+ return (d.of_buflen);
+}
diff --git a/usr.sbin/ofwdump/ofw_util.h b/usr.sbin/ofwdump/ofw_util.h
new file mode 100644
index 0000000..af8f01f
--- /dev/null
+++ b/usr.sbin/ofwdump/ofw_util.h
@@ -0,0 +1,50 @@
+/*-
+ * Copyright (c) 2002 by Thomas Moestl <tmm@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (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 OFW_UTIL_H
+#define OFW_UTIL_H
+
+#include <dev/ofw/openfirm.h>
+
+int ofw_open(int);
+void ofw_close(int);
+
+phandle_t ofw_root(int);
+phandle_t ofw_optnode(int);
+phandle_t ofw_peer(int, phandle_t);
+phandle_t ofw_child(int, phandle_t);
+phandle_t ofw_finddevice(int, const char *);
+
+int ofw_firstprop(int, phandle_t, char *, int);
+int ofw_nextprop(int, phandle_t, const char *, char *, int);
+int ofw_getprop(int, phandle_t, const char *, void *, int);
+int ofw_setprop(int, phandle_t, const char *, const void *, int);
+int ofw_getproplen(int, phandle_t, const char *);
+int ofw_getprop_alloc(int, phandle_t, const char *, void **, int *,
+ int);
+
+#endif /* OFW_UTIL_H */
diff --git a/usr.sbin/ofwdump/ofwdump.8 b/usr.sbin/ofwdump/ofwdump.8
new file mode 100644
index 0000000..ad9cfa7
--- /dev/null
+++ b/usr.sbin/ofwdump/ofwdump.8
@@ -0,0 +1,107 @@
+.\" Copyright (c) 2002 by Thomas Moestl <tmm@FreeBSD.org>.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+.\" CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+.\" OR TORT (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 18, 2002
+.Dt OFWDUMP 8
+.Os
+.Sh NAME
+.Nm ofwdump
+.Nd examine the Open Firmware device tree
+.Sh SYNOPSIS
+.Nm
+.Fl a
+.Op Fl p | P Ar property
+.Op Fl R | S
+.Nm
+.Op Fl p | P Ar property
+.Op Fl r
+.Op Fl R | S
+.Op Fl -
+.Ar nodes
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to examine the Open Firmware device tree.
+In the first synopsis form, the complete device tree is printed; in the
+second form, only the selected
+.Ar nodes
+will be examined.
+.Pp
+The following options are available:
+.Bl -tag -width ".Fl P Ar property"
+.It Fl a
+Print the complete device tree.
+.It Fl p
+Print all available properties.
+.It Fl P Ar property
+Only print properties of the given name.
+.It Fl R
+Print properties in
+.Dq raw
+format, i.e., omit all headings and indentation and just write the
+property values unaltered to the standard output.
+This is intended to be used with the
+.Fl P
+option to extract the value of a single property.
+.It Fl S
+Print properties as strings; this is analogous to the
+.Fl R
+option, except that each property is only output to the first
+.Dv NUL
+character, and that newline is appended to each.
+.It Fl r
+Recursively print all children of the specified nodes.
+.El
+.Sh EXAMPLES
+Print the complete device tree:
+.Pp
+.Dl "ofwdump -a"
+.Pp
+Print the complete device subtree of the
+.Dq Li /pci
+node, including all available properties:
+.Pp
+.Dl "ofwdump -pr /pci"
+.Pp
+Print the
+.Dq Li compatible
+property of the
+.Dq Li /pci
+node as plain string:
+.Pp
+.Dl "ofwdump -P compatible -S /pci"
+.Sh SEE ALSO
+.Xr eeprom 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 5.0 .
+.Sh AUTHORS
+The
+.Nm
+utility
+was written by
+.An Thomas Moestl Aq tmm@FreeBSD.org .
diff --git a/usr.sbin/ofwdump/ofwdump.c b/usr.sbin/ofwdump/ofwdump.c
new file mode 100644
index 0000000..ea25caa
--- /dev/null
+++ b/usr.sbin/ofwdump/ofwdump.c
@@ -0,0 +1,240 @@
+/*-
+ * Copyright (c) 2002 by Thomas Moestl <tmm@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (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 <dev/ofw/openfirm.h>
+#include <dev/ofw/openfirmio.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <vis.h>
+
+#include "ofw_util.h"
+
+/* Constants controlling the layout of the output. */
+#define LVLINDENT 2
+#define NAMEINDENT 2
+#define DUMPINDENT 4
+#define CHARSPERLINE 60
+#define BYTESPERLINE (CHARSPERLINE / 3)
+
+static void usage(void);
+static void ofw_indent(int);
+static void ofw_dump_properties(int, phandle_t, int, const char *, int,
+ int);
+static void ofw_dump(int, const char *, int, int, const char *, int, int);
+
+static void
+usage(void)
+{
+
+ fprintf(stderr,
+ "usage: ofwdump -a [-p | -P property] [-R | -S]\n"
+ " ofwdump [-p | -P property] [-r] [-R | -S] [--] nodes\n");
+ exit(EX_USAGE);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int opt, i, fd;
+ int aflag, pflag, rflag, Rflag, Sflag;
+ char *Parg;
+
+ aflag = pflag = rflag = Rflag = Sflag = 0;
+ Parg = NULL;
+ while ((opt = getopt(argc, argv, "-aprP:RS")) != -1) {
+ if (opt == '-')
+ break;
+ switch (opt) {
+ case 'a':
+ aflag = 1;
+ rflag = 1;
+ break;
+ case 'p':
+ if (Parg != NULL)
+ usage();
+ pflag = 1;
+ break;
+ case 'r':
+ rflag = 1;
+ break;
+ case 'P':
+ if (pflag)
+ usage();
+ pflag = 1;
+ Parg = optarg;
+ break;
+ case 'R':
+ if (Sflag)
+ usage();
+ Rflag = 1;
+ break;
+ case 'S':
+ if (Rflag)
+ usage();
+ Sflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ fd = ofw_open(O_RDONLY);
+ if (aflag) {
+ if (argc != 0)
+ usage();
+ ofw_dump(fd, NULL, rflag, pflag, Parg, Rflag, Sflag);
+ } else {
+ /*
+ * For the sake of scripts, usage() is not called here if
+ * argc == 0.
+ */
+ for (i = 0; i < argc; i++)
+ ofw_dump(fd, argv[i], rflag, pflag, Parg, Rflag, Sflag);
+ }
+ ofw_close(fd);
+ return (EX_OK);
+}
+
+static void
+ofw_indent(int level)
+{
+ int i;
+
+ for (i = 0; i < level; i++)
+ putchar(' ');
+}
+
+static void
+ofw_dump_properties(int fd, phandle_t n, int level, const char *pmatch, int raw,
+ int str)
+{
+ static void *pbuf = NULL;
+ static char *visbuf = NULL;
+ static char printbuf[CHARSPERLINE + 1];
+ static int pblen = 0, vblen = 0;
+ char prop[32];
+ int nlen, len, i, j, max, vlen;
+
+ for (nlen = ofw_firstprop(fd, n, prop, sizeof(prop)); nlen != 0;
+ nlen = ofw_nextprop(fd, n, prop, prop, sizeof(prop))) {
+ if (pmatch != NULL && strcmp(pmatch, prop) != 0)
+ continue;
+ len = ofw_getprop_alloc(fd, n, prop, &pbuf, &pblen, 1);
+ if (len < 0)
+ continue;
+ if (raw)
+ write(STDOUT_FILENO, pbuf, len);
+ else if (str)
+ printf("%.*s\n", len, (char *)pbuf);
+ else {
+ ofw_indent(level * LVLINDENT + NAMEINDENT);
+ printf("%s:\n", prop);
+ /* Print in hex. */
+ for (i = 0; i < len; i += BYTESPERLINE) {
+ max = len - i;
+ max = max > BYTESPERLINE ? BYTESPERLINE : max;
+ ofw_indent(level * LVLINDENT + DUMPINDENT);
+ for (j = 0; j < max; j++)
+ printf("%02x ",
+ ((unsigned char *)pbuf)[i + j]);
+ printf("\n");
+ }
+ /*
+ * strvis() and print if it looks like it is
+ * zero-terminated.
+ */
+ if (((char *)pbuf)[len - 1] == '\0' &&
+ strlen(pbuf) == (unsigned)len - 1) {
+ if (vblen < (len - 1) * 4 + 1) {
+ if (visbuf != NULL)
+ free(visbuf);
+ vblen = (OFIOCMAXVALUE + len) * 4 + 1;
+ if ((visbuf = malloc(vblen)) == NULL)
+ err(EX_OSERR,
+ "malloc() failed");
+ }
+ vlen = strvis(visbuf, pbuf, VIS_TAB | VIS_NL);
+ for (i = 0; i < vlen; i += CHARSPERLINE) {
+ ofw_indent(level * LVLINDENT +
+ DUMPINDENT);
+ strlcpy(printbuf, &visbuf[i],
+ sizeof(printbuf));
+ printf("'%s'\n", printbuf);
+ }
+ }
+ }
+ }
+}
+
+static void
+ofw_dump_node(int fd, phandle_t n, int level, int rec, int prop,
+ const char *pmatch, int raw, int str)
+{
+ static void *nbuf = NULL;
+ static int nblen = 0;
+ int plen;
+ phandle_t c;
+
+ if (!(raw || str)) {
+ ofw_indent(level * LVLINDENT);
+ printf("Node %#lx", (unsigned long)n);
+ plen = ofw_getprop_alloc(fd, n, "name", &nbuf, &nblen, 1);
+ if (plen > 0)
+ printf(": %.*s\n", (int)plen, (char *)nbuf);
+ else
+ putchar('\n');
+ }
+ if (prop)
+ ofw_dump_properties(fd, n, level, pmatch, raw, str);
+ if (rec) {
+ for (c = ofw_child(fd, n); c != 0; c = ofw_peer(fd, c)) {
+ ofw_dump_node(fd, c, level + 1, rec, prop, pmatch,
+ raw, str);
+ }
+ }
+}
+
+static void
+ofw_dump(int fd, const char *start, int rec, int prop, const char *pmatch,
+ int raw, int str)
+{
+ phandle_t n;
+
+ n = start == NULL ? ofw_root(fd) : ofw_finddevice(fd, start);
+ ofw_dump_node(fd, n, 0, rec, prop, pmatch, raw, str);
+}
diff --git a/usr.sbin/ofwdump/pathnames.h b/usr.sbin/ofwdump/pathnames.h
new file mode 100644
index 0000000..32f5fe6
--- /dev/null
+++ b/usr.sbin/ofwdump/pathnames.h
@@ -0,0 +1,30 @@
+/*-
+ * Copyright (c) 2002 by Thomas Moestl <tmm@FreeBSD.org>.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (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 <paths.h>
+
+#define PATH_DEV_OPENFIRM _PATH_DEV "openfirm"
diff --git a/usr.sbin/pciconf/Makefile b/usr.sbin/pciconf/Makefile
new file mode 100644
index 0000000..e57e736
--- /dev/null
+++ b/usr.sbin/pciconf/Makefile
@@ -0,0 +1,10 @@
+# $ANA: Makefile,v 1.1.1.1 1996/09/25 21:12:57 wollman Exp $
+# $FreeBSD$
+
+PROG= pciconf
+SRCS= pciconf.c cap.c
+MAN= pciconf.8
+
+CFLAGS+= -I${.CURDIR}/../../sys
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pciconf/cap.c b/usr.sbin/pciconf/cap.c
new file mode 100644
index 0000000..29ec6af
--- /dev/null
+++ b/usr.sbin/pciconf/cap.c
@@ -0,0 +1,510 @@
+/*-
+ * Copyright (c) 2007 Yahoo!, Inc.
+ * All rights reserved.
+ * Written by: John Baldwin <jhb@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the 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 THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <sys/agpio.h>
+#include <sys/pciio.h>
+
+#include <dev/agp/agpreg.h>
+#include <dev/pci/pcireg.h>
+
+#include "pciconf.h"
+
+static void
+cap_power(int fd, struct pci_conf *p, uint8_t ptr)
+{
+ uint16_t cap, status;
+
+ cap = read_config(fd, &p->pc_sel, ptr + PCIR_POWER_CAP, 2);
+ status = read_config(fd, &p->pc_sel, ptr + PCIR_POWER_STATUS, 2);
+ printf("powerspec %d supports D0%s%s D3 current D%d",
+ cap & PCIM_PCAP_SPEC,
+ cap & PCIM_PCAP_D1SUPP ? " D1" : "",
+ cap & PCIM_PCAP_D2SUPP ? " D2" : "",
+ status & PCIM_PSTAT_DMASK);
+}
+
+static void
+cap_agp(int fd, struct pci_conf *p, uint8_t ptr)
+{
+ uint32_t status, command;
+
+ status = read_config(fd, &p->pc_sel, ptr + AGP_STATUS, 4);
+ command = read_config(fd, &p->pc_sel, ptr + AGP_CAPID, 4);
+ printf("AGP ");
+ if (AGP_MODE_GET_MODE_3(status)) {
+ printf("v3 ");
+ if (AGP_MODE_GET_RATE(status) & AGP_MODE_V3_RATE_8x)
+ printf("8x ");
+ if (AGP_MODE_GET_RATE(status) & AGP_MODE_V3_RATE_4x)
+ printf("4x ");
+ } else {
+ if (AGP_MODE_GET_RATE(status) & AGP_MODE_V2_RATE_4x)
+ printf("4x ");
+ if (AGP_MODE_GET_RATE(status) & AGP_MODE_V2_RATE_2x)
+ printf("2x ");
+ if (AGP_MODE_GET_RATE(status) & AGP_MODE_V2_RATE_1x)
+ printf("1x ");
+ }
+ if (AGP_MODE_GET_SBA(status))
+ printf("SBA ");
+ if (AGP_MODE_GET_AGP(command)) {
+ printf("enabled at ");
+ if (AGP_MODE_GET_MODE_3(command)) {
+ printf("v3 ");
+ switch (AGP_MODE_GET_RATE(command)) {
+ case AGP_MODE_V3_RATE_8x:
+ printf("8x ");
+ break;
+ case AGP_MODE_V3_RATE_4x:
+ printf("4x ");
+ break;
+ }
+ } else
+ switch (AGP_MODE_GET_RATE(command)) {
+ case AGP_MODE_V2_RATE_4x:
+ printf("4x ");
+ break;
+ case AGP_MODE_V2_RATE_2x:
+ printf("2x ");
+ break;
+ case AGP_MODE_V2_RATE_1x:
+ printf("1x ");
+ break;
+ }
+ if (AGP_MODE_GET_SBA(command))
+ printf("SBA ");
+ } else
+ printf("disabled");
+}
+
+static void
+cap_vpd(int fd, struct pci_conf *p, uint8_t ptr)
+{
+
+ printf("VPD");
+}
+
+static void
+cap_msi(int fd, struct pci_conf *p, uint8_t ptr)
+{
+ uint16_t ctrl;
+ int msgnum;
+
+ ctrl = read_config(fd, &p->pc_sel, ptr + PCIR_MSI_CTRL, 2);
+ msgnum = 1 << ((ctrl & PCIM_MSICTRL_MMC_MASK) >> 1);
+ printf("MSI supports %d message%s%s%s ", msgnum,
+ (msgnum == 1) ? "" : "s",
+ (ctrl & PCIM_MSICTRL_64BIT) ? ", 64 bit" : "",
+ (ctrl & PCIM_MSICTRL_VECTOR) ? ", vector masks" : "");
+ if (ctrl & PCIM_MSICTRL_MSI_ENABLE) {
+ msgnum = 1 << ((ctrl & PCIM_MSICTRL_MME_MASK) >> 4);
+ printf("enabled with %d message%s", msgnum,
+ (msgnum == 1) ? "" : "s");
+ }
+}
+
+static void
+cap_pcix(int fd, struct pci_conf *p, uint8_t ptr)
+{
+ uint32_t status;
+ int comma, max_splits, max_burst_read;
+
+ status = read_config(fd, &p->pc_sel, ptr + PCIXR_STATUS, 4);
+ printf("PCI-X ");
+ if (status & PCIXM_STATUS_64BIT)
+ printf("64-bit ");
+ if ((p->pc_hdr & PCIM_HDRTYPE) == 1)
+ printf("bridge ");
+ printf("supports");
+ comma = 0;
+ if (status & PCIXM_STATUS_133CAP) {
+ printf("%s 133MHz", comma ? "," : "");
+ comma = 1;
+ }
+ if (status & PCIXM_STATUS_266CAP) {
+ printf("%s 266MHz", comma ? "," : "");
+ comma = 1;
+ }
+ if (status & PCIXM_STATUS_533CAP) {
+ printf("%s 533MHz", comma ? "," : "");
+ comma = 1;
+ }
+ if ((p->pc_hdr & PCIM_HDRTYPE) == 1)
+ return;
+ switch (status & PCIXM_STATUS_MAX_READ) {
+ case PCIXM_STATUS_MAX_READ_512:
+ max_burst_read = 512;
+ break;
+ case PCIXM_STATUS_MAX_READ_1024:
+ max_burst_read = 1024;
+ break;
+ case PCIXM_STATUS_MAX_READ_2048:
+ max_burst_read = 2048;
+ break;
+ case PCIXM_STATUS_MAX_READ_4096:
+ max_burst_read = 4096;
+ break;
+ }
+ switch (status & PCIXM_STATUS_MAX_SPLITS) {
+ case PCIXM_STATUS_MAX_SPLITS_1:
+ max_splits = 1;
+ break;
+ case PCIXM_STATUS_MAX_SPLITS_2:
+ max_splits = 2;
+ break;
+ case PCIXM_STATUS_MAX_SPLITS_3:
+ max_splits = 3;
+ break;
+ case PCIXM_STATUS_MAX_SPLITS_4:
+ max_splits = 4;
+ break;
+ case PCIXM_STATUS_MAX_SPLITS_8:
+ max_splits = 8;
+ break;
+ case PCIXM_STATUS_MAX_SPLITS_12:
+ max_splits = 12;
+ break;
+ case PCIXM_STATUS_MAX_SPLITS_16:
+ max_splits = 16;
+ break;
+ case PCIXM_STATUS_MAX_SPLITS_32:
+ max_splits = 32;
+ break;
+ }
+ printf("%s %d burst read, %d split transaction%s", comma ? "," : "",
+ max_burst_read, max_splits, max_splits == 1 ? "" : "s");
+}
+
+static void
+cap_ht(int fd, struct pci_conf *p, uint8_t ptr)
+{
+ uint32_t reg;
+ uint16_t command;
+
+ command = read_config(fd, &p->pc_sel, ptr + PCIR_HT_COMMAND, 2);
+ printf("HT ");
+ if ((command & 0xe000) == PCIM_HTCAP_SLAVE)
+ printf("slave");
+ else if ((command & 0xe000) == PCIM_HTCAP_HOST)
+ printf("host");
+ else
+ switch (command & PCIM_HTCMD_CAP_MASK) {
+ case PCIM_HTCAP_SWITCH:
+ printf("switch");
+ break;
+ case PCIM_HTCAP_INTERRUPT:
+ printf("interrupt");
+ break;
+ case PCIM_HTCAP_REVISION_ID:
+ printf("revision ID");
+ break;
+ case PCIM_HTCAP_UNITID_CLUMPING:
+ printf("unit ID clumping");
+ break;
+ case PCIM_HTCAP_EXT_CONFIG_SPACE:
+ printf("extended config space");
+ break;
+ case PCIM_HTCAP_ADDRESS_MAPPING:
+ printf("address mapping");
+ break;
+ case PCIM_HTCAP_MSI_MAPPING:
+ printf("MSI %saddress window %s at 0x",
+ command & PCIM_HTCMD_MSI_FIXED ? "fixed " : "",
+ command & PCIM_HTCMD_MSI_ENABLE ? "enabled" :
+ "disabled");
+ if (command & PCIM_HTCMD_MSI_FIXED)
+ printf("fee00000");
+ else {
+ reg = read_config(fd, &p->pc_sel,
+ ptr + PCIR_HTMSI_ADDRESS_HI, 4);
+ if (reg != 0)
+ printf("%08x", reg);
+ reg = read_config(fd, &p->pc_sel,
+ ptr + PCIR_HTMSI_ADDRESS_LO, 4);
+ printf("%08x", reg);
+ }
+ break;
+ case PCIM_HTCAP_DIRECT_ROUTE:
+ printf("direct route");
+ break;
+ case PCIM_HTCAP_VCSET:
+ printf("VC set");
+ break;
+ case PCIM_HTCAP_RETRY_MODE:
+ printf("retry mode");
+ break;
+ case PCIM_HTCAP_X86_ENCODING:
+ printf("X86 encoding");
+ break;
+ default:
+ printf("unknown %02x", command);
+ break;
+ }
+}
+
+static void
+cap_vendor(int fd, struct pci_conf *p, uint8_t ptr)
+{
+ uint8_t length;
+
+ length = read_config(fd, &p->pc_sel, ptr + PCIR_VENDOR_LENGTH, 1);
+ printf("vendor (length %d)", length);
+ if (p->pc_vendor == 0x8086) {
+ /* Intel */
+ uint8_t version;
+
+ version = read_config(fd, &p->pc_sel, ptr + PCIR_VENDOR_DATA,
+ 1);
+ printf(" Intel cap %d version %d", version >> 4, version & 0xf);
+ if (version >> 4 == 1 && length == 12) {
+ /* Feature Detection */
+ uint32_t fvec;
+ int comma;
+
+ comma = 0;
+ fvec = read_config(fd, &p->pc_sel, ptr +
+ PCIR_VENDOR_DATA + 5, 4);
+ printf("\n\t\t features:");
+ if (fvec & (1 << 0)) {
+ printf(" AMT");
+ comma = 1;
+ }
+ fvec = read_config(fd, &p->pc_sel, ptr +
+ PCIR_VENDOR_DATA + 1, 4);
+ if (fvec & (1 << 21)) {
+ printf("%s Quick Resume", comma ? "," : "");
+ comma = 1;
+ }
+ if (fvec & (1 << 18)) {
+ printf("%s SATA RAID-5", comma ? "," : "");
+ comma = 1;
+ }
+ if (fvec & (1 << 9)) {
+ printf("%s Mobile", comma ? "," : "");
+ comma = 1;
+ }
+ if (fvec & (1 << 7)) {
+ printf("%s 6 PCI-e x1 slots", comma ? "," : "");
+ comma = 1;
+ } else {
+ printf("%s 4 PCI-e x1 slots", comma ? "," : "");
+ comma = 1;
+ }
+ if (fvec & (1 << 5)) {
+ printf("%s SATA RAID-0/1/10", comma ? "," : "");
+ comma = 1;
+ }
+ if (fvec & (1 << 3)) {
+ printf("%s SATA AHCI", comma ? "," : "");
+ comma = 1;
+ }
+ }
+ }
+}
+
+static void
+cap_debug(int fd, struct pci_conf *p, uint8_t ptr)
+{
+ uint16_t debug_port;
+
+ debug_port = read_config(fd, &p->pc_sel, ptr + PCIR_DEBUG_PORT, 2);
+ printf("EHCI Debug Port at offset 0x%x in map 0x%x", debug_port &
+ PCIM_DEBUG_PORT_OFFSET, PCIR_BAR(debug_port >> 13));
+}
+
+static void
+cap_subvendor(int fd, struct pci_conf *p, uint8_t ptr)
+{
+ uint32_t id;
+
+ id = read_config(fd, &p->pc_sel, ptr + PCIR_SUBVENDCAP_ID, 4);
+ printf("PCI Bridge card=0x%08x", id);
+}
+
+static void
+cap_express(int fd, struct pci_conf *p, uint8_t ptr)
+{
+ uint16_t flags;
+
+ flags = read_config(fd, &p->pc_sel, ptr + PCIR_EXPRESS_FLAGS, 2);
+ printf("PCI-Express %d ", flags & PCIM_EXP_FLAGS_VERSION);
+ switch (flags & PCIM_EXP_FLAGS_TYPE) {
+ case PCIM_EXP_TYPE_ENDPOINT:
+ printf("endpoint");
+ break;
+ case PCIM_EXP_TYPE_LEGACY_ENDPOINT:
+ printf("legacy endpoint");
+ break;
+ case PCIM_EXP_TYPE_ROOT_PORT:
+ printf("root port");
+ break;
+ case PCIM_EXP_TYPE_UPSTREAM_PORT:
+ printf("upstream port");
+ break;
+ case PCIM_EXP_TYPE_DOWNSTREAM_PORT:
+ printf("downstream port");
+ break;
+ case PCIM_EXP_TYPE_PCI_BRIDGE:
+ printf("PCI bridge");
+ break;
+ default:
+ printf("type %d", (flags & PCIM_EXP_FLAGS_TYPE) >> 8);
+ break;
+ }
+ if (flags & PCIM_EXP_FLAGS_IRQ)
+ printf(" IRQ %d", (flags & PCIM_EXP_FLAGS_IRQ) >> 17);
+}
+
+static void
+cap_msix(int fd, struct pci_conf *p, uint8_t ptr)
+{
+ uint32_t val;
+ uint16_t ctrl;
+ int msgnum, table_bar, pba_bar;
+
+ ctrl = read_config(fd, &p->pc_sel, ptr + PCIR_MSIX_CTRL, 2);
+ msgnum = (ctrl & PCIM_MSIXCTRL_TABLE_SIZE) + 1;
+ val = read_config(fd, &p->pc_sel, ptr + PCIR_MSIX_TABLE, 4);
+ table_bar = PCIR_BAR(val & PCIM_MSIX_BIR_MASK);
+ val = read_config(fd, &p->pc_sel, ptr + PCIR_MSIX_PBA, 4);
+ pba_bar = PCIR_BAR(val & PCIM_MSIX_BIR_MASK);
+ printf("MSI-X supports %d message%s ", msgnum,
+ (msgnum == 1) ? "" : "s");
+ if (table_bar == pba_bar)
+ printf("in map 0x%x", table_bar);
+ else
+ printf("in maps 0x%x and 0x%x", table_bar, pba_bar);
+ if (ctrl & PCIM_MSIXCTRL_MSIX_ENABLE)
+ printf(" enabled");
+}
+
+static void
+cap_sata(int fd, struct pci_conf *p, uint8_t ptr)
+{
+
+ printf("SATA Index-Data Pair");
+}
+
+static void
+cap_pciaf(int fd, struct pci_conf *p, uint8_t ptr)
+{
+ uint8_t cap;
+
+ cap = read_config(fd, &p->pc_sel, ptr + PCIR_PCIAF_CAP, 1);
+ printf("PCI Advanced Features:%s%s",
+ cap & PCIM_PCIAFCAP_FLR ? " FLR" : "",
+ cap & PCIM_PCIAFCAP_TP ? " TP" : "");
+}
+
+void
+list_caps(int fd, struct pci_conf *p)
+{
+ uint16_t cmd;
+ uint8_t ptr, cap;
+
+ /* Are capabilities present for this device? */
+ cmd = read_config(fd, &p->pc_sel, PCIR_STATUS, 2);
+ if (!(cmd & PCIM_STATUS_CAPPRESENT))
+ return;
+
+ switch (p->pc_hdr & PCIM_HDRTYPE) {
+ case 0:
+ case 1:
+ ptr = PCIR_CAP_PTR;
+ break;
+ case 2:
+ ptr = PCIR_CAP_PTR_2;
+ break;
+ default:
+ errx(1, "list_caps: bad header type");
+ }
+
+ /* Walk the capability list. */
+ ptr = read_config(fd, &p->pc_sel, ptr, 1);
+ while (ptr != 0 && ptr != 0xff) {
+ cap = read_config(fd, &p->pc_sel, ptr + PCICAP_ID, 1);
+ printf(" cap %02x[%02x] = ", cap, ptr);
+ switch (cap) {
+ case PCIY_PMG:
+ cap_power(fd, p, ptr);
+ break;
+ case PCIY_AGP:
+ cap_agp(fd, p, ptr);
+ break;
+ case PCIY_VPD:
+ cap_vpd(fd, p, ptr);
+ break;
+ case PCIY_MSI:
+ cap_msi(fd, p, ptr);
+ break;
+ case PCIY_PCIX:
+ cap_pcix(fd, p, ptr);
+ break;
+ case PCIY_HT:
+ cap_ht(fd, p, ptr);
+ break;
+ case PCIY_VENDOR:
+ cap_vendor(fd, p, ptr);
+ break;
+ case PCIY_DEBUG:
+ cap_debug(fd, p, ptr);
+ break;
+ case PCIY_SUBVENDOR:
+ cap_subvendor(fd, p, ptr);
+ break;
+ case PCIY_EXPRESS:
+ cap_express(fd, p, ptr);
+ break;
+ case PCIY_MSIX:
+ cap_msix(fd, p, ptr);
+ break;
+ case PCIY_SATA:
+ cap_sata(fd, p, ptr);
+ break;
+ case PCIY_PCIAF:
+ cap_pciaf(fd, p, ptr);
+ break;
+ default:
+ printf("unknown");
+ break;
+ }
+ printf("\n");
+ ptr = read_config(fd, &p->pc_sel, ptr + PCICAP_NEXTPTR, 1);
+ }
+}
diff --git a/usr.sbin/pciconf/pathnames.h b/usr.sbin/pciconf/pathnames.h
new file mode 100644
index 0000000..719615a
--- /dev/null
+++ b/usr.sbin/pciconf/pathnames.h
@@ -0,0 +1,3 @@
+/* $FreeBSD$ */
+#define _PATH_DEVPCI "/dev/pci"
+#define _PATH_PCIVDB "/usr/share/misc/pci_vendors"
diff --git a/usr.sbin/pciconf/pciconf.8 b/usr.sbin/pciconf/pciconf.8
new file mode 100644
index 0000000..7492b5d
--- /dev/null
+++ b/usr.sbin/pciconf/pciconf.8
@@ -0,0 +1,271 @@
+.\" Copyright (c) 1997
+.\" Stefan Esser <se@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 November 7, 2007
+.Dt PCICONF 8
+.Os
+.Sh NAME
+.Nm pciconf
+.Nd diagnostic utility for the PCI bus
+.Sh SYNOPSIS
+.Nm
+.Fl l Op Fl bcv
+.Nm
+.Fl a Ar selector
+.Nm
+.Fl r Oo Fl b | h Oc Ar selector addr Ns Op : Ns Ar addr2
+.Nm
+.Fl w Oo Fl b | h Oc Ar selector addr value
+.Sh DESCRIPTION
+The
+.Nm
+utility provides a command line interface to functionality provided by the
+.Xr pci 4
+.Xr ioctl 2
+interface.
+As such, some of the functions are only available to users with write
+access to
+.Pa /dev/pci ,
+normally only the super-user.
+.Pp
+With the
+.Fl l
+option, it lists all devices found by the boot probe in the following format:
+.Bd -literal
+foo0@pci0:0:4:0: class=0x010000 card=0x00000000 chip=0x000f1000 rev=0x01 \
+hdr=0x00
+bar0@pci0:0:5:0: class=0x000100 card=0x00000000 chip=0x88c15333 rev=0x00 \
+hdr=0x00
+none0@pci0:0:6:0: class=0x020000 card=0x00000000 chip=0x802910ec rev=0x00 \
+hdr=0x00
+.Ed
+.Pp
+The first column gives the
+device name, unit number, and
+.Ar selector .
+If there is no device configured in the kernel for the
+.Tn PCI
+device in question, the device name will be
+.Dq none .
+Unit numbers for unconfigured devices start at zero and are incremented for
+each unconfigured device that is encountered.
+The
+.Ar selector
+is in a form which may directly be used for the other forms of the command.
+The second column is the class code, with the class byte printed as two
+hex digits, followed by the sub-class and the interface bytes.
+The third column gives the contents of the subvendorid register, introduced
+in revision 2.1 of the
+.Tn PCI
+standard.
+Note that it will be 0 for older cards.
+The field consists of the card ID in the upper
+half and the card vendor ID in the lower half of the value.
+.Pp
+The fourth column contains the chip device ID, which identifies the chip
+this card is based on.
+It consists of two fields, identifying the chip and
+its vendor, as above.
+The fifth column prints the chip's revision.
+The sixth column describes the header type.
+Currently assigned header types include 0 for most devices,
+1 for
+.Tn PCI
+to
+.Tn PCI
+bridges, and 2 for
+.Tn PCI
+to
+.Tn CardBus
+bridges.
+If the most significant bit
+of the header type register is set for
+function 0 of a
+.Tn PCI
+device, it is a
+.Em multi-function
+device, which contains several (similar or independent) functions on
+one chip.
+.Pp
+If the
+.Fl b
+option is supplied,
+.Nm
+will list any base address registers
+.Pq BARs
+that are assigned resources for each device.
+Each BAR will be enumerated via a line in the following format:
+.Bd -literal
+ bar [10] = type Memory, range 32, base 0xda060000, size 131072, enabled
+.Ed
+.Pp
+The first value after the
+.Dq Li bar
+prefix in the square brackets is the offset of the BAR in config space in
+hexadecimal.
+The type of a BAR is one of
+.Dq Memory ,
+.Dq Prefetchable Memory ,
+or
+.Dq I/O Port .
+The range indicates the maximum address the BAR decodes.
+The base and size indicate the start and length of the BAR's address window,
+respectively.
+Finally, the last flag indicates if the BAR is enabled or disabled.
+.Pp
+If the
+.Fl c
+option is supplied,
+.Nm
+will list any capabilities supported by each device.
+Each capability will be enumerated via a line in the following format:
+.Bd -literal
+ cap 10[40] = PCI-Express 1 root port
+.Ed
+.Pp
+The first value after the
+.Dq Li cap
+prefix is the capability ID in hexadecimal.
+The second value in the square brackets is the offset of the capability
+in config space in hexadecimal.
+The format of the text after the equals sign is capability-specific.
+.Pp
+If the
+.Fl v
+option is supplied,
+.Nm
+will attempt to load the vendor/device information database, and print
+vendor, device, class and subclass identification strings for each device.
+.Pp
+All invocations of
+.Nm
+except for
+.Fl l
+require a
+.Ar selector
+of the form
+.Li pci Ns Va domain Ns \&: Ns Va bus Ns \&: Ns Va device Ns \&: \
+Ns Va function Ns ,
+.Li pci Ns Va bus Ns \&: Ns Va device Ns \&: Ns Va function Ns , or
+.Li pci Ns Va bus Ns \&: Ns Va device Ns .
+In case of an abrigded form, omitted selector components are assumed to be 0.
+An optional leading device name followed by @ and an optional final colon
+will be ignored; this is so that the first column in the output of
+.Nm
+.Fl l
+can be used without modification.
+All numbers are base 10.
+.Pp
+With the
+.Fl a
+flag,
+.Nm
+determines whether any driver has been assigned to the device
+identified by
+.Ar selector .
+An exit status of zero indicates that the device has a driver;
+non-zero indicates that it does not.
+.Pp
+The
+.Fl r
+option reads a configuration space register at byte offset
+.Ar addr
+of device
+.Ar selector
+and prints out its value in hexadecimal.
+The optional second address
+.Ar addr2
+specifies a range to read.
+The
+.Fl w
+option writes the
+.Ar value
+into a configuration space register at byte offset
+.Ar addr
+of device
+.Ar selector .
+For both operations, the flags
+.Fl b
+and
+.Fl h
+select the width of the operation;
+.Fl b
+indicates a byte operation, and
+.Fl h
+indicates a halfword (two-byte) operation.
+The default is to read or
+write a longword (four bytes).
+.Sh ENVIRONMENT
+The PCI vendor/device information database is normally read from
+.Pa /usr/share/misc/pci_vendors .
+This path can be overridden by setting the environment variable
+.Ev PCICONF_VENDOR_DATABASE .
+.Sh SEE ALSO
+.Xr ioctl 2 ,
+.\" .Xr pci 4 ,
+.Xr devinfo 8 ,
+.Xr kldload 8
+.Sh HISTORY
+The
+.Nm
+utility appeared first in
+.Fx 2.2 .
+The
+.Fl a
+option was added for
+.Tn PCI
+KLD support in
+.Fx 3.0 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility was written by
+.An Stefan Esser
+and
+.An Garrett Wollman .
+.Sh BUGS
+The
+.Fl b
+and
+.Fl h
+options are implemented in
+.Nm ,
+but not in the underlying
+.Xr ioctl 2 .
+.Pp
+It might be useful to give non-root users access to the
+.Fl a
+and
+.Fl r
+options.
+But only root will be able to execute a
+.Nm kldload
+to provide the device with a driver KLD, and reading of configuration space
+registers may cause a failure in badly designed
+.Tn PCI
+chips.
diff --git a/usr.sbin/pciconf/pciconf.c b/usr.sbin/pciconf/pciconf.c
new file mode 100644
index 0000000..7d1da2d
--- /dev/null
+++ b/usr.sbin/pciconf/pciconf.c
@@ -0,0 +1,668 @@
+/*
+ * Copyright 1996 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that both the above copyright notice and this
+ * permission notice appear in all copies, that both the above
+ * copyright notice and this permission notice appear in all
+ * supporting documentation, and that the name of M.I.T. not be used
+ * in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. M.I.T. makes
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
+ * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (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/fcntl.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/pciio.h>
+#include <sys/queue.h>
+
+#include <dev/pci/pcireg.h>
+
+#include "pathnames.h"
+#include "pciconf.h"
+
+struct pci_device_info
+{
+ TAILQ_ENTRY(pci_device_info) link;
+ int id;
+ char *desc;
+};
+
+struct pci_vendor_info
+{
+ TAILQ_ENTRY(pci_vendor_info) link;
+ TAILQ_HEAD(,pci_device_info) devs;
+ int id;
+ char *desc;
+};
+
+TAILQ_HEAD(,pci_vendor_info) pci_vendors;
+
+static void list_bars(int fd, struct pci_conf *p);
+static void list_devs(int verbose, int bars, int caps);
+static void list_verbose(struct pci_conf *p);
+static const char *guess_class(struct pci_conf *p);
+static const char *guess_subclass(struct pci_conf *p);
+static int load_vendors(void);
+static void readit(const char *, const char *, int);
+static void writeit(const char *, const char *, const char *, int);
+static void chkattached(const char *, int);
+
+static int exitstatus = 0;
+
+static void
+usage(void)
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n",
+ "usage: pciconf -l [-bcv]",
+ " pciconf -a selector",
+ " pciconf -r [-b | -h] selector addr[:addr2]",
+ " pciconf -w [-b | -h] selector addr value");
+ exit (1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int c;
+ int listmode, readmode, writemode, attachedmode, bars, caps, verbose;
+ int byte, isshort;
+
+ listmode = readmode = writemode = attachedmode = bars = caps = verbose = byte = isshort = 0;
+
+ while ((c = getopt(argc, argv, "abchlrwv")) != -1) {
+ switch(c) {
+ case 'a':
+ attachedmode = 1;
+ break;
+
+ case 'b':
+ bars = 1;
+ byte = 1;
+ break;
+
+ case 'c':
+ caps = 1;
+ break;
+
+ case 'h':
+ isshort = 1;
+ break;
+
+ case 'l':
+ listmode = 1;
+ break;
+
+ case 'r':
+ readmode = 1;
+ break;
+
+ case 'w':
+ writemode = 1;
+ break;
+
+ case 'v':
+ verbose = 1;
+ break;
+
+ default:
+ usage();
+ }
+ }
+
+ if ((listmode && optind != argc)
+ || (writemode && optind + 3 != argc)
+ || (readmode && optind + 2 != argc)
+ || (attachedmode && optind + 1 != argc))
+ usage();
+
+ if (listmode) {
+ list_devs(verbose, bars, caps);
+ } else if (attachedmode) {
+ chkattached(argv[optind],
+ byte ? 1 : isshort ? 2 : 4);
+ } else if (readmode) {
+ readit(argv[optind], argv[optind + 1],
+ byte ? 1 : isshort ? 2 : 4);
+ } else if (writemode) {
+ writeit(argv[optind], argv[optind + 1], argv[optind + 2],
+ byte ? 1 : isshort ? 2 : 4);
+ } else {
+ usage();
+ }
+
+ return exitstatus;
+}
+
+static void
+list_devs(int verbose, int bars, int caps)
+{
+ int fd;
+ struct pci_conf_io pc;
+ struct pci_conf conf[255], *p;
+ int none_count = 0;
+
+ if (verbose)
+ load_vendors();
+
+ fd = open(_PATH_DEVPCI, caps ? O_RDWR : O_RDONLY, 0);
+ if (fd < 0)
+ err(1, "%s", _PATH_DEVPCI);
+
+ bzero(&pc, sizeof(struct pci_conf_io));
+ pc.match_buf_len = sizeof(conf);
+ pc.matches = conf;
+
+ do {
+ if (ioctl(fd, PCIOCGETCONF, &pc) == -1)
+ err(1, "ioctl(PCIOCGETCONF)");
+
+ /*
+ * 255 entries should be more than enough for most people,
+ * but if someone has more devices, and then changes things
+ * around between ioctls, we'll do the cheezy thing and
+ * just bail. The alternative would be to go back to the
+ * beginning of the list, and print things twice, which may
+ * not be desireable.
+ */
+ if (pc.status == PCI_GETCONF_LIST_CHANGED) {
+ warnx("PCI device list changed, please try again");
+ exitstatus = 1;
+ close(fd);
+ return;
+ } else if (pc.status == PCI_GETCONF_ERROR) {
+ warnx("error returned from PCIOCGETCONF ioctl");
+ exitstatus = 1;
+ close(fd);
+ return;
+ }
+ for (p = conf; p < &conf[pc.num_matches]; p++) {
+ printf("%s%d@pci%d:%d:%d:%d:\tclass=0x%06x card=0x%08x "
+ "chip=0x%08x rev=0x%02x hdr=0x%02x\n",
+ (p->pd_name && *p->pd_name) ? p->pd_name :
+ "none",
+ (p->pd_name && *p->pd_name) ? (int)p->pd_unit :
+ none_count++, p->pc_sel.pc_domain,
+ p->pc_sel.pc_bus, p->pc_sel.pc_dev,
+ p->pc_sel.pc_func, (p->pc_class << 16) |
+ (p->pc_subclass << 8) | p->pc_progif,
+ (p->pc_subdevice << 16) | p->pc_subvendor,
+ (p->pc_device << 16) | p->pc_vendor,
+ p->pc_revid, p->pc_hdr);
+ if (verbose)
+ list_verbose(p);
+ if (bars)
+ list_bars(fd, p);
+ if (caps)
+ list_caps(fd, p);
+ }
+ } while (pc.status == PCI_GETCONF_MORE_DEVS);
+
+ close(fd);
+}
+
+static void
+list_bars(int fd, struct pci_conf *p)
+{
+ struct pci_bar_io bar;
+ uint64_t base;
+ const char *type;
+ int i, range, max;
+
+ switch (p->pc_hdr & PCIM_HDRTYPE) {
+ case PCIM_HDRTYPE_NORMAL:
+ max = PCIR_MAX_BAR_0;
+ break;
+ case PCIM_HDRTYPE_BRIDGE:
+ max = PCIR_MAX_BAR_1;
+ break;
+ case PCIM_HDRTYPE_CARDBUS:
+ max = PCIR_MAX_BAR_2;
+ break;
+ default:
+ return;
+ }
+
+ for (i = 0; i <= max; i++) {
+ bar.pbi_sel = p->pc_sel;
+ bar.pbi_reg = PCIR_BAR(i);
+ if (ioctl(fd, PCIOCGETBAR, &bar) < 0)
+ continue;
+ if (PCI_BAR_IO(bar.pbi_base)) {
+ type = "I/O Port";
+ range = 32;
+ base = bar.pbi_base & PCIM_BAR_IO_BASE;
+ } else {
+ if (bar.pbi_base & PCIM_BAR_MEM_PREFETCH)
+ type = "Prefetchable Memory";
+ else
+ type = "Memory";
+ switch (bar.pbi_base & PCIM_BAR_MEM_TYPE) {
+ case PCIM_BAR_MEM_32:
+ range = 32;
+ break;
+ case PCIM_BAR_MEM_1MB:
+ range = 20;
+ break;
+ case PCIM_BAR_MEM_64:
+ range = 64;
+ break;
+ default:
+ range = -1;
+ }
+ base = bar.pbi_base & ~((uint64_t)0xf);
+ }
+ printf(" bar [%02x] = type %s, range %2d, base %#jx, ",
+ PCIR_BAR(i), type, range, (uintmax_t)base);
+ printf("size %2d, %s\n", (int)bar.pbi_length,
+ bar.pbi_enabled ? "enabled" : "disabled");
+ }
+}
+
+static void
+list_verbose(struct pci_conf *p)
+{
+ struct pci_vendor_info *vi;
+ struct pci_device_info *di;
+ const char *dp;
+
+ TAILQ_FOREACH(vi, &pci_vendors, link) {
+ if (vi->id == p->pc_vendor) {
+ printf(" vendor = '%s'\n", vi->desc);
+ break;
+ }
+ }
+ if (vi == NULL) {
+ di = NULL;
+ } else {
+ TAILQ_FOREACH(di, &vi->devs, link) {
+ if (di->id == p->pc_device) {
+ printf(" device = '%s'\n", di->desc);
+ break;
+ }
+ }
+ }
+ if ((dp = guess_class(p)) != NULL)
+ printf(" class = %s\n", dp);
+ if ((dp = guess_subclass(p)) != NULL)
+ printf(" subclass = %s\n", dp);
+}
+
+/*
+ * This is a direct cut-and-paste from the table in sys/dev/pci/pci.c.
+ */
+static struct
+{
+ int class;
+ int subclass;
+ const char *desc;
+} pci_nomatch_tab[] = {
+ {PCIC_OLD, -1, "old"},
+ {PCIC_OLD, PCIS_OLD_NONVGA, "non-VGA display device"},
+ {PCIC_OLD, PCIS_OLD_VGA, "VGA-compatible display device"},
+ {PCIC_STORAGE, -1, "mass storage"},
+ {PCIC_STORAGE, PCIS_STORAGE_SCSI, "SCSI"},
+ {PCIC_STORAGE, PCIS_STORAGE_IDE, "ATA"},
+ {PCIC_STORAGE, PCIS_STORAGE_FLOPPY, "floppy disk"},
+ {PCIC_STORAGE, PCIS_STORAGE_IPI, "IPI"},
+ {PCIC_STORAGE, PCIS_STORAGE_RAID, "RAID"},
+ {PCIC_STORAGE, PCIS_STORAGE_ATA_ADMA, "ATA (ADMA)"},
+ {PCIC_STORAGE, PCIS_STORAGE_SATA, "SATA"},
+ {PCIC_STORAGE, PCIS_STORAGE_SAS, "SAS"},
+ {PCIC_NETWORK, -1, "network"},
+ {PCIC_NETWORK, PCIS_NETWORK_ETHERNET, "ethernet"},
+ {PCIC_NETWORK, PCIS_NETWORK_TOKENRING, "token ring"},
+ {PCIC_NETWORK, PCIS_NETWORK_FDDI, "fddi"},
+ {PCIC_NETWORK, PCIS_NETWORK_ATM, "ATM"},
+ {PCIC_NETWORK, PCIS_NETWORK_ISDN, "ISDN"},
+ {PCIC_DISPLAY, -1, "display"},
+ {PCIC_DISPLAY, PCIS_DISPLAY_VGA, "VGA"},
+ {PCIC_DISPLAY, PCIS_DISPLAY_XGA, "XGA"},
+ {PCIC_DISPLAY, PCIS_DISPLAY_3D, "3D"},
+ {PCIC_MULTIMEDIA, -1, "multimedia"},
+ {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_VIDEO, "video"},
+ {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_AUDIO, "audio"},
+ {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_TELE, "telephony"},
+ {PCIC_MULTIMEDIA, PCIS_MULTIMEDIA_HDA, "HDA"},
+ {PCIC_MEMORY, -1, "memory"},
+ {PCIC_MEMORY, PCIS_MEMORY_RAM, "RAM"},
+ {PCIC_MEMORY, PCIS_MEMORY_FLASH, "flash"},
+ {PCIC_BRIDGE, -1, "bridge"},
+ {PCIC_BRIDGE, PCIS_BRIDGE_HOST, "HOST-PCI"},
+ {PCIC_BRIDGE, PCIS_BRIDGE_ISA, "PCI-ISA"},
+ {PCIC_BRIDGE, PCIS_BRIDGE_EISA, "PCI-EISA"},
+ {PCIC_BRIDGE, PCIS_BRIDGE_MCA, "PCI-MCA"},
+ {PCIC_BRIDGE, PCIS_BRIDGE_PCI, "PCI-PCI"},
+ {PCIC_BRIDGE, PCIS_BRIDGE_PCMCIA, "PCI-PCMCIA"},
+ {PCIC_BRIDGE, PCIS_BRIDGE_NUBUS, "PCI-NuBus"},
+ {PCIC_BRIDGE, PCIS_BRIDGE_CARDBUS, "PCI-CardBus"},
+ {PCIC_BRIDGE, PCIS_BRIDGE_RACEWAY, "PCI-RACEway"},
+ {PCIC_SIMPLECOMM, -1, "simple comms"},
+ {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_UART, "UART"}, /* could detect 16550 */
+ {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_PAR, "parallel port"},
+ {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_MULSER, "multiport serial"},
+ {PCIC_SIMPLECOMM, PCIS_SIMPLECOMM_MODEM, "generic modem"},
+ {PCIC_BASEPERIPH, -1, "base peripheral"},
+ {PCIC_BASEPERIPH, PCIS_BASEPERIPH_PIC, "interrupt controller"},
+ {PCIC_BASEPERIPH, PCIS_BASEPERIPH_DMA, "DMA controller"},
+ {PCIC_BASEPERIPH, PCIS_BASEPERIPH_TIMER, "timer"},
+ {PCIC_BASEPERIPH, PCIS_BASEPERIPH_RTC, "realtime clock"},
+ {PCIC_BASEPERIPH, PCIS_BASEPERIPH_PCIHOT, "PCI hot-plug controller"},
+ {PCIC_BASEPERIPH, PCIS_BASEPERIPH_SDHC, "SD host controller"},
+ {PCIC_INPUTDEV, -1, "input device"},
+ {PCIC_INPUTDEV, PCIS_INPUTDEV_KEYBOARD, "keyboard"},
+ {PCIC_INPUTDEV, PCIS_INPUTDEV_DIGITIZER,"digitizer"},
+ {PCIC_INPUTDEV, PCIS_INPUTDEV_MOUSE, "mouse"},
+ {PCIC_INPUTDEV, PCIS_INPUTDEV_SCANNER, "scanner"},
+ {PCIC_INPUTDEV, PCIS_INPUTDEV_GAMEPORT, "gameport"},
+ {PCIC_DOCKING, -1, "docking station"},
+ {PCIC_PROCESSOR, -1, "processor"},
+ {PCIC_SERIALBUS, -1, "serial bus"},
+ {PCIC_SERIALBUS, PCIS_SERIALBUS_FW, "FireWire"},
+ {PCIC_SERIALBUS, PCIS_SERIALBUS_ACCESS, "AccessBus"},
+ {PCIC_SERIALBUS, PCIS_SERIALBUS_SSA, "SSA"},
+ {PCIC_SERIALBUS, PCIS_SERIALBUS_USB, "USB"},
+ {PCIC_SERIALBUS, PCIS_SERIALBUS_FC, "Fibre Channel"},
+ {PCIC_SERIALBUS, PCIS_SERIALBUS_SMBUS, "SMBus"},
+ {PCIC_WIRELESS, -1, "wireless controller"},
+ {PCIC_WIRELESS, PCIS_WIRELESS_IRDA, "iRDA"},
+ {PCIC_WIRELESS, PCIS_WIRELESS_IR, "IR"},
+ {PCIC_WIRELESS, PCIS_WIRELESS_RF, "RF"},
+ {PCIC_INTELLIIO, -1, "intelligent I/O controller"},
+ {PCIC_INTELLIIO, PCIS_INTELLIIO_I2O, "I2O"},
+ {PCIC_SATCOM, -1, "satellite communication"},
+ {PCIC_SATCOM, PCIS_SATCOM_TV, "sat TV"},
+ {PCIC_SATCOM, PCIS_SATCOM_AUDIO, "sat audio"},
+ {PCIC_SATCOM, PCIS_SATCOM_VOICE, "sat voice"},
+ {PCIC_SATCOM, PCIS_SATCOM_DATA, "sat data"},
+ {PCIC_CRYPTO, -1, "encrypt/decrypt"},
+ {PCIC_CRYPTO, PCIS_CRYPTO_NETCOMP, "network/computer crypto"},
+ {PCIC_CRYPTO, PCIS_CRYPTO_NETCOMP, "entertainment crypto"},
+ {PCIC_DASP, -1, "dasp"},
+ {PCIC_DASP, PCIS_DASP_DPIO, "DPIO module"},
+ {0, 0, NULL}
+};
+
+static const char *
+guess_class(struct pci_conf *p)
+{
+ int i;
+
+ for (i = 0; pci_nomatch_tab[i].desc != NULL; i++) {
+ if (pci_nomatch_tab[i].class == p->pc_class)
+ return(pci_nomatch_tab[i].desc);
+ }
+ return(NULL);
+}
+
+static const char *
+guess_subclass(struct pci_conf *p)
+{
+ int i;
+
+ for (i = 0; pci_nomatch_tab[i].desc != NULL; i++) {
+ if ((pci_nomatch_tab[i].class == p->pc_class) &&
+ (pci_nomatch_tab[i].subclass == p->pc_subclass))
+ return(pci_nomatch_tab[i].desc);
+ }
+ return(NULL);
+}
+
+static int
+load_vendors(void)
+{
+ const char *dbf;
+ FILE *db;
+ struct pci_vendor_info *cv;
+ struct pci_device_info *cd;
+ char buf[1024], str[1024];
+ char *ch;
+ int id, error;
+
+ /*
+ * Locate the database and initialise.
+ */
+ TAILQ_INIT(&pci_vendors);
+ if ((dbf = getenv("PCICONF_VENDOR_DATABASE")) == NULL)
+ dbf = _PATH_PCIVDB;
+ if ((db = fopen(dbf, "r")) == NULL)
+ return(1);
+ cv = NULL;
+ cd = NULL;
+ error = 0;
+
+ /*
+ * Scan input lines from the database
+ */
+ for (;;) {
+ if (fgets(buf, sizeof(buf), db) == NULL)
+ break;
+
+ if ((ch = strchr(buf, '#')) != NULL)
+ *ch = '\0';
+ ch = strchr(buf, '\0') - 1;
+ while (ch > buf && isspace(*ch))
+ *ch-- = '\0';
+ if (ch <= buf)
+ continue;
+
+ /* Can't handle subvendor / subdevice entries yet */
+ if (buf[0] == '\t' && buf[1] == '\t')
+ continue;
+
+ /* Check for vendor entry */
+ if (buf[0] != '\t' && sscanf(buf, "%04x %[^\n]", &id, str) == 2) {
+ if ((id == 0) || (strlen(str) < 1))
+ continue;
+ if ((cv = malloc(sizeof(struct pci_vendor_info))) == NULL) {
+ warn("allocating vendor entry");
+ error = 1;
+ break;
+ }
+ if ((cv->desc = strdup(str)) == NULL) {
+ free(cv);
+ warn("allocating vendor description");
+ error = 1;
+ break;
+ }
+ cv->id = id;
+ TAILQ_INIT(&cv->devs);
+ TAILQ_INSERT_TAIL(&pci_vendors, cv, link);
+ continue;
+ }
+
+ /* Check for device entry */
+ if (buf[0] == '\t' && sscanf(buf + 1, "%04x %[^\n]", &id, str) == 2) {
+ if ((id == 0) || (strlen(str) < 1))
+ continue;
+ if (cv == NULL) {
+ warnx("device entry with no vendor!");
+ continue;
+ }
+ if ((cd = malloc(sizeof(struct pci_device_info))) == NULL) {
+ warn("allocating device entry");
+ error = 1;
+ break;
+ }
+ if ((cd->desc = strdup(str)) == NULL) {
+ free(cd);
+ warn("allocating device description");
+ error = 1;
+ break;
+ }
+ cd->id = id;
+ TAILQ_INSERT_TAIL(&cv->devs, cd, link);
+ continue;
+ }
+
+ /* It's a comment or junk, ignore it */
+ }
+ if (ferror(db))
+ error = 1;
+ fclose(db);
+
+ return(error);
+}
+
+uint32_t
+read_config(int fd, struct pcisel *sel, long reg, int width)
+{
+ struct pci_io pi;
+
+ pi.pi_sel = *sel;
+ pi.pi_reg = reg;
+ pi.pi_width = width;
+
+ if (ioctl(fd, PCIOCREAD, &pi) < 0)
+ err(1, "ioctl(PCIOCREAD)");
+
+ return (pi.pi_data);
+}
+
+static struct pcisel
+getsel(const char *str)
+{
+ char *ep = strchr(str, '@');
+ char *epbase;
+ struct pcisel sel;
+ unsigned long selarr[4];
+ int i;
+
+ if (ep == NULL)
+ ep = (char *)str;
+ else
+ ep++;
+
+ epbase = ep;
+
+ if (strncmp(ep, "pci", 3) == 0) {
+ ep += 3;
+ i = 0;
+ do {
+ selarr[i++] = strtoul(ep, &ep, 10);
+ } while ((*ep == ':' || *ep == '.') && *++ep != '\0' && i < 4);
+
+ if (i > 2)
+ sel.pc_func = selarr[--i];
+ else
+ sel.pc_func = 0;
+ sel.pc_dev = selarr[--i];
+ sel.pc_bus = selarr[--i];
+ if (i > 0)
+ sel.pc_domain = selarr[--i];
+ else
+ sel.pc_domain = 0;
+ }
+ if (*ep != '\x0' || ep == epbase)
+ errx(1, "cannot parse selector %s", str);
+ return sel;
+}
+
+static void
+readone(int fd, struct pcisel *sel, long reg, int width)
+{
+
+ printf("%0*x", width*2, read_config(fd, sel, reg, width));
+}
+
+static void
+readit(const char *name, const char *reg, int width)
+{
+ long rstart;
+ long rend;
+ long r;
+ char *end;
+ int i;
+ int fd;
+ struct pcisel sel;
+
+ fd = open(_PATH_DEVPCI, O_RDWR, 0);
+ if (fd < 0)
+ err(1, "%s", _PATH_DEVPCI);
+
+ rend = rstart = strtol(reg, &end, 0);
+ if (end && *end == ':') {
+ end++;
+ rend = strtol(end, (char **) 0, 0);
+ }
+ sel = getsel(name);
+ for (i = 1, r = rstart; r <= rend; i++, r += width) {
+ readone(fd, &sel, r, width);
+ if (i && !(i % 8))
+ putchar(' ');
+ putchar(i % (16/width) ? ' ' : '\n');
+ }
+ if (i % (16/width) != 1)
+ putchar('\n');
+ close(fd);
+}
+
+static void
+writeit(const char *name, const char *reg, const char *data, int width)
+{
+ int fd;
+ struct pci_io pi;
+
+ pi.pi_sel = getsel(name);
+ pi.pi_reg = strtoul(reg, (char **)0, 0); /* XXX error check */
+ pi.pi_width = width;
+ pi.pi_data = strtoul(data, (char **)0, 0); /* XXX error check */
+
+ fd = open(_PATH_DEVPCI, O_RDWR, 0);
+ if (fd < 0)
+ err(1, "%s", _PATH_DEVPCI);
+
+ if (ioctl(fd, PCIOCWRITE, &pi) < 0)
+ err(1, "ioctl(PCIOCWRITE)");
+}
+
+static void
+chkattached(const char *name, int width)
+{
+ int fd;
+ struct pci_io pi;
+
+ pi.pi_sel = getsel(name);
+ pi.pi_reg = 0;
+ pi.pi_width = width;
+ pi.pi_data = 0;
+
+ fd = open(_PATH_DEVPCI, O_RDWR, 0);
+ if (fd < 0)
+ err(1, "%s", _PATH_DEVPCI);
+
+ if (ioctl(fd, PCIOCATTACHED, &pi) < 0)
+ err(1, "ioctl(PCIOCATTACHED)");
+
+ exitstatus = pi.pi_data ? 0 : 2; /* exit(2), if NOT attached */
+ printf("%s: %s%s\n", name, pi.pi_data == 0 ? "not " : "", "attached");
+}
diff --git a/usr.sbin/pciconf/pciconf.h b/usr.sbin/pciconf/pciconf.h
new file mode 100644
index 0000000..2e87c63
--- /dev/null
+++ b/usr.sbin/pciconf/pciconf.h
@@ -0,0 +1,39 @@
+/*-
+ * Copyright (c) 2007 Yahoo!, Inc.
+ * All rights reserved.
+ * Written by: John Baldwin <jhb@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the 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 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 __PCICONF_H__
+#define __PCICONF_H__
+
+void list_caps(int fd, struct pci_conf *p);
+uint32_t read_config(int fd, struct pcisel *sel, long reg, int width);
+
+#endif
diff --git a/usr.sbin/periodic/Makefile b/usr.sbin/periodic/Makefile
new file mode 100644
index 0000000..875d078
--- /dev/null
+++ b/usr.sbin/periodic/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+SCRIPTS=periodic.sh
+MAN= periodic.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/periodic/periodic.8 b/usr.sbin/periodic/periodic.8
new file mode 100644
index 0000000..6335930
--- /dev/null
+++ b/usr.sbin/periodic/periodic.8
@@ -0,0 +1,258 @@
+.\" 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$
+.\"
+.Dd August 30, 2007
+.Os
+.Dt PERIODIC 8
+.Sh NAME
+.Nm periodic
+.Nd run periodic system functions
+.Sh SYNOPSIS
+.Nm
+.Ar directory ...
+.Sh DESCRIPTION
+The
+.Nm
+utility is intended to be called by
+.Xr cron 8
+to execute shell scripts
+located in the specified directory.
+.Pp
+One or more of the following arguments must be specified:
+.Bl -tag -width ".Pa monthly"
+.It Pa daily
+Perform the standard daily periodic executable run.
+This usually occurs early in the morning (local time).
+.It Pa weekly
+Perform the standard weekly periodic executable run.
+This usually occurs very early on Saturday mornings.
+.It Pa monthly
+Perform the standard monthly periodic executable run.
+This usually occurs on the first day of the month.
+.It Pa security
+Perform the standard daily security checks.
+This is usually spawned by the
+.Pa daily
+run.
+.It Ar path
+An arbitrary directory containing a set of executables to be run.
+.El
+.Pp
+If an argument is an absolute directory name it is used as is, otherwise
+it is searched for under
+.Pa /etc/periodic
+and any other directories specified by the
+.Va local_periodic
+setting in
+.Xr periodic.conf 5
+(see below).
+.Pp
+The
+.Nm
+utility will run each executable file in the directory or directories
+specified.
+If a file does not have the executable bit set, it is silently ignored.
+.Pp
+Each script is required to exit with one of the following values:
+.Bl -tag -width 4n
+.It 0
+The script has produced nothing notable in its output.
+The
+.Ao Ar basedir Ac Ns Va _show_success
+variable controls the masking of this output.
+.It 1
+The script has produced some notable information in its output.
+The
+.Ao Ar basedir Ac Ns Va _show_info
+variable controls the masking of this output.
+.It 2
+The script has produced some warnings due to invalid configuration settings.
+The
+.Ao Ar basedir Ac Ns Va _show_badconfig
+variable controls the masking of this output.
+.It >2
+The script has produced output that must not be masked.
+.El
+.Pp
+If the relevant variable (where
+.Aq Ar basedir
+is the base directory in which the script resides) is set to
+.Dq Li NO
+in
+.Pa periodic.conf ,
+.Nm
+will mask the script output.
+If the variable is not set to either
+.Dq Li YES
+or
+.Dq Li NO ,
+it will be given a default value as described in
+.Xr periodic.conf 5 .
+.Pp
+All remaining script output is delivered based on the value of the
+.Ao Ar basedir Ac Ns Va _output
+setting.
+.Pp
+If this is set to a path name (beginning with a
+.Ql /
+character), output is simply logged to that file.
+.Xr newsyslog 8
+knows about the files
+.Pa /var/log/daily.log , /var/log/weekly.log
+and
+.Pa /var/log/monthly.log ,
+and if they exist, it will rotate them at the appropriate times.
+These are therefore good values if you wish to log
+.Nm
+output.
+.Pp
+If the
+.Ao Ar basedir Ac Ns Va _output
+value does not begin with a
+.Ql /
+and is not empty, it is assumed to contain a list of email addresses, and
+the output is mailed to them.
+If
+.Ao Ar basedir Ac Ns Va _show_empty_output
+is set to
+.Dq Li NO ,
+then no mail will be sent if the output was empty.
+.Pp
+If
+.Ao Ar basedir Ac Ns Va _output
+is not set or is empty, output is sent to standard output.
+.Sh ENVIRONMENT
+The
+.Nm
+utility sets the
+.Ev PATH
+environment to include all standard system directories, but no additional
+directories, such as
+.Pa /usr/local/bin .
+If executables are added which depend upon other path components, each
+executable must be responsible for configuring its own appropriate environment.
+.Sh FILES
+.Bl -tag -width ".Pa /etc/defaults/periodic.conf"
+.It Pa /etc/crontab
+the
+.Nm
+utility is typically called via entries in the system default
+.Xr cron 8
+table
+.It Pa /etc/periodic
+the top level directory containing
+.Pa daily ,
+.Pa weekly ,
+and
+.Pa monthly
+subdirectories which contain standard system periodic executables
+.It Pa /etc/defaults/periodic.conf
+the
+.Pa periodic.conf
+system registry contains variables that control the behaviour of
+.Nm
+and the standard
+.Pa daily , weekly ,
+and
+.Pa monthly
+scripts
+.It Pa /etc/periodic.conf
+this file contains local overrides for the default
+.Nm
+configuration
+.El
+.Sh EXIT STATUS
+Exit status is 0 on success and 1 if the command fails.
+.Sh EXAMPLES
+The system crontab should have entries for
+.Nm
+similar to the following example:
+.Bd -literal -offset indent
+# do daily/weekly/monthly maintenance
+0 2 * * * root periodic daily
+0 3 * * 6 root periodic weekly
+0 5 1 * * root periodic monthly
+.Ed
+.Pp
+The
+.Pa /etc/defaults/periodic.conf
+system registry will typically have a
+.Va local_periodic
+variable reading:
+.Pp
+.Dl local_periodic="/usr/local/etc/periodic"
+.Pp
+To log
+.Nm
+output instead of receiving it as email, add the following lines to
+.Pa /etc/periodic.conf :
+.Bd -literal -offset indent
+daily_output=/var/log/daily.log
+weekly_output=/var/log/weekly.log
+monthly_output=/var/log/monthly.log
+.Ed
+.Pp
+To only see important information from daily periodic jobs, add the
+following lines to
+.Pa /etc/periodic.conf :
+.Bd -literal -offset indent
+daily_show_success=NO
+daily_show_info=NO
+daily_show_badconfig=NO
+.Ed
+.Sh DIAGNOSTICS
+The command may fail for one of the following reasons:
+.Bl -diag
+.It usage: periodic <directory of files to execute>
+No directory path argument was passed to
+.Nm
+to specify where the script fragments reside.
+.It <directory> not found
+Self explanatory.
+.El
+.Sh SEE ALSO
+.Xr sh 1 ,
+.Xr crontab 5 ,
+.Xr periodic.conf 5 ,
+.Xr cron 8 ,
+.Xr newsyslog 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 3.0 .
+.Sh AUTHORS
+.An Paul Traina Aq pst@FreeBSD.org
+.An Brian Somers Aq brian@Awfulhak.org
+.Sh BUGS
+Since one specifies information about a directory using shell
+variables containing the string,
+.Aq Ar basedir ,
+.Aq Ar basedir
+must only contain characters that are valid within a
+.Xr sh 1
+variable name, alphanumerics and underscores, and the first character
+may not be numeric.
diff --git a/usr.sbin/periodic/periodic.sh b/usr.sbin/periodic/periodic.sh
new file mode 100644
index 0000000..ca954ea
--- /dev/null
+++ b/usr.sbin/periodic/periodic.sh
@@ -0,0 +1,109 @@
+#!/bin/sh -
+#
+# $FreeBSD$
+#
+# Run nightly periodic scripts
+#
+# usage: periodic { daily | weekly | monthly } - run standard periodic scripts
+# periodic /absolute/path/to/directory - run periodic scripts in dir
+#
+
+usage () {
+ echo "usage: $0 <directory of files to execute>" 1>&2
+ echo "or $0 { daily | weekly | monthly }" 1>&2
+ exit 1
+}
+
+if [ $# -lt 1 ] ; then
+ usage
+fi
+
+# If possible, check the global system configuration file,
+# to see if there are additional dirs to check
+if [ -r /etc/defaults/periodic.conf ]; then
+ . /etc/defaults/periodic.conf
+ source_periodic_confs
+fi
+
+host=`hostname`
+export host
+tmp_output=`mktemp ${TMPDIR:-/tmp}/periodic.XXXXXXXXXX`
+
+# Execute each executable file in the directory list. If the x bit is not
+# set, assume the user didn't really want us to muck with it (it's a
+# README file or has been disabled).
+
+for arg
+do
+ # Where's our output going ?
+ eval output=\$${arg##*/}_output
+ case "$output" in
+ /*) pipe="cat >>$output";;
+ "") pipe=cat;;
+ *) pipe="mail -E -s '$host ${arg##*/} run output' $output";;
+ esac
+
+ success=YES info=YES badconfig=NO empty_output=YES # Defaults when ${run}_* aren't YES/NO
+ for var in success info badconfig empty_output
+ do
+ case $(eval echo "\$${arg##*/}_show_$var") in
+ [Yy][Ee][Ss]) eval $var=YES;;
+ [Nn][Oo]) eval $var=NO;;
+ esac
+ done
+
+ case $arg in
+ /*) if [ -d "$arg" ]
+ then
+ dirlist="$arg"
+ else
+ echo "$0: $arg not found" >&2
+ continue
+ fi;;
+ *) dirlist=
+ for top in /etc/periodic ${local_periodic}
+ do
+ [ -d $top/$arg ] && dirlist="$dirlist $top/$arg"
+ done;;
+ esac
+
+ {
+ empty=TRUE
+ processed=0
+ for dir in $dirlist
+ do
+ for file in $dir/*
+ do
+ if [ -x $file -a ! -d $file ]
+ then
+ output=TRUE
+ processed=$(($processed + 1))
+ $file </dev/null >$tmp_output 2>&1
+ rc=$?
+ if [ -s $tmp_output ]
+ then
+ case $rc in
+ 0) [ $success = NO ] && output=FALSE;;
+ 1) [ $info = NO ] && output=FALSE;;
+ 2) [ $badconfig = NO ] && output=FALSE;;
+ esac
+ [ $output = TRUE ] && { cat $tmp_output; empty=FALSE; }
+ fi
+ cp /dev/null $tmp_output
+ fi
+ done
+ done
+ if [ $empty = TRUE ]
+ then
+ if [ $empty_output = TRUE ]
+ then
+ [ $processed = 1 ] && plural= || plural=s
+ echo "No output from the $processed file$plural processed"
+ fi
+ else
+ echo ""
+ echo "-- End of $arg output --"
+ fi
+ } | eval $pipe
+done
+rm -f $tmp_output
diff --git a/usr.sbin/pkg_install/Makefile b/usr.sbin/pkg_install/Makefile
new file mode 100644
index 0000000..bf1a213
--- /dev/null
+++ b/usr.sbin/pkg_install/Makefile
@@ -0,0 +1,20 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+SUBDIR= lib add create delete info updating version
+
+.include <bsd.subdir.mk>
+
+DATE!= grep PKG_INSTALL_VERSION ${.CURDIR}/lib/lib.h | sed 's|.*[ ]||'
+
+distfile: clean
+ @(cd ${.CURDIR}/..; \
+ cp -r pkg_install pkg_install-${DATE}; \
+ tar -czf pkg_install/pkg_install-${DATE}.tar.gz \
+ --exclude .#* --exclude *~ --exclude CVS \
+ --exclude .svn --exclude pkg_install-*.tar.gz \
+ pkg_install-${DATE}; \
+ rm -rf pkg_install-${DATE})
+
+
diff --git a/usr.sbin/pkg_install/Makefile.inc b/usr.sbin/pkg_install/Makefile.inc
new file mode 100644
index 0000000..2fa20aa
--- /dev/null
+++ b/usr.sbin/pkg_install/Makefile.inc
@@ -0,0 +1,17 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+LIBINSTALL= ${.OBJDIR}/../lib/libinstall.a
+
+DPADD+= ${LIBUTIL}
+LDADD+= -lutil
+
+.if ${MK_OPENSSL} != "no" && \
+ defined(LDADD) && ${LDADD:M-lfetch} != ""
+DPADD+= ${LIBSSL} ${LIBCRYPTO}
+LDADD+= -lssl -lcrypto
+.endif
+
+# Inherit BINDIR from one level up.
+.include "../Makefile.inc"
diff --git a/usr.sbin/pkg_install/README b/usr.sbin/pkg_install/README
new file mode 100644
index 0000000..a5a517d
--- /dev/null
+++ b/usr.sbin/pkg_install/README
@@ -0,0 +1,8 @@
+This is the pkg_install suite of tools for doing maintainance of
+software "packages". More documentation is available in the man pages
+for each individual command.
+
+This code was written by Jordan Hubbard for FreeBSD, snatched and
+mildly reshaped by John Kohl in NetBSD and the changes taken back into
+FreeBSD again by Jordan, who then proceeded to add another couple
+of dozen features on top. Whee! :-)
diff --git a/usr.sbin/pkg_install/add/Makefile b/usr.sbin/pkg_install/add/Makefile
new file mode 100644
index 0000000..89988e7
--- /dev/null
+++ b/usr.sbin/pkg_install/add/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+PROG= pkg_add
+SRCS= main.c perform.c futil.c extract.c
+
+CFLAGS+= -I${.CURDIR}/../lib
+
+WARNS?= 3
+WFORMAT?= 1
+
+DPADD= ${LIBINSTALL} ${LIBFETCH} ${LIBMD}
+LDADD= ${LIBINSTALL} -lfetch -lmd
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pkg_install/add/add.h b/usr.sbin/pkg_install/add/add.h
new file mode 100644
index 0000000..f1607ee
--- /dev/null
+++ b/usr.sbin/pkg_install/add/add.h
@@ -0,0 +1,48 @@
+/* $FreeBSD$ */
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Include and define various things wanted by the add command.
+ *
+ */
+
+#ifndef _INST_ADD_H_INCLUDE
+#define _INST_ADD_H_INCLUDE
+
+typedef enum { NORMAL, MASTER, SLAVE } add_mode_t;
+
+extern char *Prefix;
+extern Boolean PrefixRecursive;
+extern Boolean NoInstall;
+extern Boolean NoRecord;
+extern Boolean FailOnAlreadyInstalled;
+extern Boolean KeepPackage;
+extern Boolean IgnoreDeps;
+extern char *Mode;
+extern char *Owner;
+extern char *Group;
+extern char *Directory;
+extern char *PkgName;
+extern char *PkgAddCmd;
+extern char FirstPen[];
+extern add_mode_t AddMode;
+
+int make_hierarchy(char *);
+void extract_plist(const char *, Package *);
+void apply_perms(const char *, const char *);
+
+#endif /* _INST_ADD_H_INCLUDE */
diff --git a/usr.sbin/pkg_install/add/extract.c b/usr.sbin/pkg_install/add/extract.c
new file mode 100644
index 0000000..732a13f
--- /dev/null
+++ b/usr.sbin/pkg_install/add/extract.c
@@ -0,0 +1,287 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the package extraction code for the add module.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <ctype.h>
+#include <err.h>
+#include "lib.h"
+#include "add.h"
+
+
+#define STARTSTRING "/usr/bin/tar cf -"
+#define TOOBIG(str) \
+ (((int)strlen(str) + FILENAME_MAX + where_count > maxargs) ||\
+ ((int)strlen(str) + FILENAME_MAX + perm_count > maxargs))
+
+#define PUSHOUT(todir) /* push out string */ \
+ if (where_count > (int)sizeof(STARTSTRING)-1) { \
+ strcat(where_args, "|/usr/bin/tar --unlink -xpPf - -C "); \
+ strcat(where_args, todir); \
+ if (system(where_args)) { \
+ cleanup(0); \
+ errx(2, "%s: can not invoke %ld byte tar pipeline: %s", \
+ __func__, (long)strlen(where_args), where_args); \
+ } \
+ strcpy(where_args, STARTSTRING); \
+ where_count = sizeof(STARTSTRING)-1; \
+ } \
+ if (perm_count) { \
+ apply_perms(todir, perm_args); \
+ perm_args[0] = 0;\
+ perm_count = 0; \
+ }
+
+static void
+rollback(const char *name, const char *home, PackingList start, PackingList stop)
+{
+ PackingList q;
+ char try[FILENAME_MAX], bup[FILENAME_MAX];
+ const char *dir;
+ char *prefix = NULL;
+
+ dir = home;
+ for (q = start; q != stop; q = q->next) {
+ if (q->type == PLIST_FILE) {
+ snprintf(try, FILENAME_MAX, "%s/%s", dir, q->name);
+ if (make_preserve_name(bup, FILENAME_MAX, name, try) && fexists(bup)) {
+ (void)chflags(try, 0);
+ (void)unlink(try);
+ if (rename(bup, try))
+ warnx("rollback: unable to rename %s back to %s", bup, try);
+ }
+ }
+ else if (q->type == PLIST_CWD) {
+ if (!prefix)
+ prefix = q->name;
+ if (q->name == NULL)
+ q->name = prefix;
+ else if (strcmp(q->name, "."))
+ dir = q->name;
+ else
+ dir = home;
+ }
+ }
+}
+
+#define add_char(buf, len, pos, ch) do {\
+ if ((pos) < (len)) { \
+ buf[(pos)] = (ch); \
+ buf[(pos) + 1] = '\0'; \
+ } \
+ ++(pos); \
+} while (0)
+
+static int
+add_arg(char *buf, int len, const char *str)
+{
+ int i = 0;
+
+ add_char(buf, len, i, ' ');
+ for (; *str != '\0'; ++str) {
+ if (!isalnum(*str) && *str != '/' && *str != '.' && *str != '-')
+ add_char(buf, len, i, '\\');
+ add_char(buf, len, i, *str);
+ }
+ return (i);
+}
+
+void
+extract_plist(const char *home, Package *pkg)
+{
+ PackingList p = pkg->head;
+ char *last_file, *prefix = NULL;
+ char *where_args, *perm_args, *last_chdir;
+ int maxargs, where_count = 0, perm_count = 0, add_count;
+ Boolean preserve;
+
+ maxargs = sysconf(_SC_ARG_MAX) / 2; /* Just use half the argument space */
+ where_args = alloca(maxargs);
+ if (!where_args) {
+ cleanup(0);
+ errx(2, "%s: can't get argument list space", __func__);
+ }
+ perm_args = alloca(maxargs);
+ if (!perm_args) {
+ cleanup(0);
+ errx(2, "%s: can't get argument list space", __func__);
+ }
+
+ strcpy(where_args, STARTSTRING);
+ where_count = sizeof(STARTSTRING)-1;
+ perm_args[0] = 0;
+
+ last_chdir = 0;
+ preserve = find_plist_option(pkg, "preserve") ? TRUE : FALSE;
+
+ /* Reset the world */
+ Owner = NULL;
+ Group = NULL;
+ Mode = NULL;
+ last_file = NULL;
+ Directory = (char *)home;
+
+ /* Do it */
+ while (p) {
+ char cmd[FILENAME_MAX];
+
+ switch(p->type) {
+ case PLIST_NAME:
+ PkgName = p->name;
+ if (Verbose)
+ printf("extract: Package name is %s\n", p->name);
+ break;
+
+ case PLIST_FILE:
+ last_file = p->name;
+ if (Verbose)
+ printf("extract: %s/%s\n", Directory, p->name);
+ if (!Fake) {
+ char try[FILENAME_MAX];
+
+ /* first try to rename it into place */
+ snprintf(try, FILENAME_MAX, "%s/%s", Directory, p->name);
+ if (fexists(try)) {
+ (void)chflags(try, 0); /* XXX hack - if truly immutable, rename fails */
+ if (preserve && PkgName) {
+ char pf[FILENAME_MAX];
+
+ if (make_preserve_name(pf, FILENAME_MAX, PkgName, try)) {
+ if (rename(try, pf)) {
+ warnx(
+ "unable to back up %s to %s, aborting pkg_add",
+ try, pf);
+ rollback(PkgName, home, pkg->head, p);
+ return;
+ }
+ }
+ }
+ }
+ if (rename(p->name, try) == 0) {
+ /* try to add to list of perms to be changed and run in bulk. */
+ if (p->name[0] == '/' || TOOBIG(p->name)) {
+ PUSHOUT(Directory);
+ }
+ add_count = add_arg(&perm_args[perm_count], maxargs - perm_count, p->name);
+ if (add_count < 0 || add_count >= maxargs - perm_count) {
+ cleanup(0);
+ errx(2, "%s: oops, miscounted strings!", __func__);
+ }
+ perm_count += add_count;
+ }
+ else {
+ /* rename failed, try copying with a big tar command */
+ if (last_chdir != Directory) {
+ if (last_chdir == NULL) {
+ PUSHOUT(Directory);
+ } else {
+ PUSHOUT(last_chdir);
+ }
+ last_chdir = Directory;
+ }
+ else if (p->name[0] == '/' || TOOBIG(p->name)) {
+ PUSHOUT(Directory);
+ }
+ add_count = add_arg(&where_args[where_count], maxargs - where_count, p->name);
+ if (add_count < 0 || add_count >= maxargs - where_count) {
+ cleanup(0);
+ errx(2, "%s: oops, miscounted strings!", __func__);
+ }
+ where_count += add_count;
+ add_count = add_arg(&perm_args[perm_count], maxargs - perm_count, p->name);
+ if (add_count < 0 || add_count >= maxargs - perm_count) {
+ cleanup(0);
+ errx(2, "%s: oops, miscounted strings!", __func__);
+ }
+ perm_count += add_count;
+ }
+ }
+ break;
+
+ case PLIST_CWD:
+ if (!prefix)
+ prefix = p->name;
+ if (p->name == NULL)
+ p->name = strdup(prefix);
+ if (Verbose)
+ printf("extract: CWD to %s\n", p->name);
+ PUSHOUT(Directory);
+ if (strcmp(p->name, ".")) {
+ if (!Fake && make_hierarchy(p->name) == FAIL) {
+ cleanup(0);
+ errx(2, "%s: unable to cwd to '%s'", __func__, p->name);
+ }
+ Directory = p->name;
+ }
+ else
+ Directory = (char *)home;
+ break;
+
+ case PLIST_CMD:
+ if ((strstr(p->name, "%B") || strstr(p->name, "%F") ||
+ strstr(p->name, "%f")) && last_file == NULL) {
+ cleanup(0);
+ errx(2, "%s: no last file specified for '%s' command",
+ __func__, p->name);
+ }
+ if (strstr(p->name, "%D") && Directory == NULL) {
+ cleanup(0);
+ errx(2, "%s: no directory specified for '%s' command",
+ __func__, p->name);
+ }
+ format_cmd(cmd, FILENAME_MAX, p->name, Directory, last_file);
+ PUSHOUT(Directory);
+ if (Verbose)
+ printf("extract: execute '%s'\n", cmd);
+ if (!Fake && system(cmd))
+ warnx("command '%s' failed", cmd);
+ break;
+
+ case PLIST_CHMOD:
+ PUSHOUT(Directory);
+ Mode = p->name;
+ break;
+
+ case PLIST_CHOWN:
+ PUSHOUT(Directory);
+ Owner = p->name;
+ break;
+
+ case PLIST_CHGRP:
+ PUSHOUT(Directory);
+ Group = p->name;
+ break;
+
+ case PLIST_COMMENT: /* FALLTHROUGH */
+ case PLIST_NOINST:
+ break;
+
+ case PLIST_IGNORE:
+ p = p->next;
+ break;
+
+ default:
+ break;
+ }
+ p = p->next;
+ }
+ PUSHOUT(Directory);
+}
diff --git a/usr.sbin/pkg_install/add/futil.c b/usr.sbin/pkg_install/add/futil.c
new file mode 100644
index 0000000..e097140
--- /dev/null
+++ b/usr.sbin/pkg_install/add/futil.c
@@ -0,0 +1,97 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Miscellaneous file access utilities.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include "lib.h"
+#include "add.h"
+
+/*
+ * Assuming dir is a desired directory name, make it and all intervening
+ * directories necessary.
+ */
+
+int
+make_hierarchy(char *dir)
+{
+ char *cp1, *cp2;
+
+ if (dir[0] == '/')
+ cp1 = cp2 = dir + 1;
+ else
+ cp1 = cp2 = dir;
+ while (cp2) {
+ if ((cp2 = strchr(cp1, '/')) !=NULL )
+ *cp2 = '\0';
+ if (fexists(dir)) {
+ if (!isdir(dir)) {
+ if (cp2)
+ *cp2 = '/';
+ return FAIL;
+ }
+ }
+ else {
+ if (vsystem("/bin/mkdir %s", dir)) {
+ if (cp2)
+ *cp2 = '/';
+ return FAIL;
+ }
+ apply_perms(NULL, dir);
+ }
+ /* Put it back */
+ if (cp2) {
+ *cp2 = '/';
+ cp1 = cp2 + 1;
+ }
+ }
+ return SUCCESS;
+}
+
+/* Using permission defaults, apply them as necessary */
+void
+apply_perms(const char *dir, const char *arg)
+{
+ const char *cd_to;
+
+ if (!dir || *arg == '/') /* absolute path? */
+ cd_to = "/";
+ else
+ cd_to = dir;
+
+ if (Mode)
+ if (vsystem("cd %s && /bin/chmod -R %s %s", cd_to, Mode, arg))
+ warnx("couldn't change modes of '%s' to '%s'", arg, Mode);
+ if (Owner && Group) {
+ if (vsystem("cd %s && /usr/sbin/chown -R %s:%s %s", cd_to, Owner, Group, arg))
+ warnx("couldn't change owner/group of '%s' to '%s:%s'",
+ arg, Owner, Group);
+ return;
+ }
+ if (Owner) {
+ if (vsystem("cd %s && /usr/sbin/chown -R %s %s", cd_to, Owner, arg))
+ warnx("couldn't change owner of '%s' to '%s'", arg, Owner);
+ return;
+ } else if (Group)
+ if (vsystem("cd %s && /usr/bin/chgrp -R %s %s", cd_to, Group, arg))
+ warnx("couldn't change group of '%s' to '%s'", arg, Group);
+}
+
diff --git a/usr.sbin/pkg_install/add/main.c b/usr.sbin/pkg_install/add/main.c
new file mode 100644
index 0000000..4ad55d3
--- /dev/null
+++ b/usr.sbin/pkg_install/add/main.c
@@ -0,0 +1,349 @@
+/*
+ *
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the add module.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/utsname.h>
+#include <err.h>
+#include <getopt.h>
+
+#include "lib.h"
+#include "add.h"
+
+char *Prefix = NULL;
+Boolean PrefixRecursive = FALSE;
+char *Chroot = NULL;
+Boolean NoInstall = FALSE;
+Boolean NoRecord = FALSE;
+Boolean Remote = FALSE;
+Boolean KeepPackage = FALSE;
+Boolean FailOnAlreadyInstalled = TRUE;
+Boolean IgnoreDeps = FALSE;
+
+char *Mode = NULL;
+char *Owner = NULL;
+char *Group = NULL;
+char *PkgName = NULL;
+char *PkgAddCmd = NULL;
+char *Directory = NULL;
+char FirstPen[FILENAME_MAX];
+add_mode_t AddMode = NORMAL;
+
+char **pkgs;
+
+struct {
+ int lowver; /* Lowest version number to match */
+ int hiver; /* Highest version number to match */
+ const char *directory; /* Directory it lives in */
+} releases[] = {
+ { 410000, 410000, "/packages-4.1-release" },
+ { 420000, 420000, "/packages-4.2-release" },
+ { 430000, 430000, "/packages-4.3-release" },
+ { 440000, 440000, "/packages-4.4-release" },
+ { 450000, 450000, "/packages-4.5-release" },
+ { 460000, 460001, "/packages-4.6-release" },
+ { 460002, 460099, "/packages-4.6.2-release" },
+ { 470000, 470099, "/packages-4.7-release" },
+ { 480000, 480099, "/packages-4.8-release" },
+ { 490000, 490099, "/packages-4.9-release" },
+ { 491000, 491099, "/packages-4.10-release" },
+ { 492000, 492099, "/packages-4.11-release" },
+ { 500000, 500099, "/packages-5.0-release" },
+ { 501000, 501099, "/packages-5.1-release" },
+ { 502000, 502009, "/packages-5.2-release" },
+ { 502010, 502099, "/packages-5.2.1-release" },
+ { 503000, 503099, "/packages-5.3-release" },
+ { 504000, 504099, "/packages-5.4-release" },
+ { 505000, 505099, "/packages-5.5-release" },
+ { 600000, 600099, "/packages-6.0-release" },
+ { 601000, 601099, "/packages-6.1-release" },
+ { 602000, 602099, "/packages-6.2-release" },
+ { 603000, 603099, "/packages-6.3-release" },
+ { 700000, 700099, "/packages-7.0-release" },
+ { 701000, 701099, "/packages-7.1-release" },
+ { 300000, 399000, "/packages-3-stable" },
+ { 400000, 499000, "/packages-4-stable" },
+ { 502100, 502128, "/packages-5-current" },
+ { 503100, 599000, "/packages-5-stable" },
+ { 600100, 699000, "/packages-6-stable" },
+ { 700100, 799000, "/packages-7-stable" },
+ { 800000, 899000, "/packages-8-current" },
+ { 0, 9999999, "/packages-current" },
+ { 0, 0, NULL }
+};
+
+static char *getpackagesite(void);
+int getosreldate(void);
+
+static void usage(void);
+
+static char opts[] = "hviIRfFnrp:P:SMt:C:K";
+static struct option longopts[] = {
+ { "chroot", required_argument, NULL, 'C' },
+ { "dry-run", no_argument, NULL, 'n' },
+ { "force", no_argument, NULL, 'f' },
+ { "help", no_argument, NULL, 'h' },
+ { "keep", no_argument, NULL, 'K' },
+ { "master", no_argument, NULL, 'M' },
+ { "no-deps", no_argument, NULL, 'i' },
+ { "no-record", no_argument, NULL, 'R' },
+ { "no-script", no_argument, NULL, 'I' },
+ { "prefix", required_argument, NULL, 'p' },
+ { "remote", no_argument, NULL, 'r' },
+ { "template", required_argument, NULL, 't' },
+ { "slave", no_argument, NULL, 'S' },
+ { "verbose", no_argument, NULL, 'v' },
+ { NULL, 0, NULL, 0 }
+};
+
+int
+main(int argc, char **argv)
+{
+ int ch, error;
+ char **start;
+ char *cp, *packagesite = NULL, *remotepkg = NULL, *ptr;
+ static char temppackageroot[MAXPATHLEN];
+ static char pkgaddpath[MAXPATHLEN];
+
+ if (*argv[0] != '/' && strchr(argv[0], '/') != NULL)
+ PkgAddCmd = realpath(argv[0], pkgaddpath);
+ else
+ PkgAddCmd = argv[0];
+
+ start = argv;
+ while ((ch = getopt_long(argc, argv, opts, longopts, NULL)) != -1) {
+ switch(ch) {
+ case 'v':
+ Verbose++;
+ break;
+
+ case 'p':
+ Prefix = optarg;
+ PrefixRecursive = FALSE;
+ break;
+
+ case 'P':
+ Prefix = optarg;
+ PrefixRecursive = TRUE;
+ break;
+
+ case 'I':
+ NoInstall = TRUE;
+ break;
+
+ case 'R':
+ NoRecord = TRUE;
+ break;
+
+ case 'f':
+ Force = TRUE;
+ break;
+
+ case 'F':
+ FailOnAlreadyInstalled = FALSE;
+ break;
+
+ case 'K':
+ KeepPackage = TRUE;
+ break;
+
+ case 'n':
+ Fake = TRUE;
+ break;
+
+ case 'r':
+ Remote = TRUE;
+ break;
+
+ case 't':
+ if (strlcpy(FirstPen, optarg, sizeof(FirstPen)) >= sizeof(FirstPen))
+ errx(1, "-t Argument too long.");
+ break;
+
+ case 'S':
+ AddMode = SLAVE;
+ break;
+
+ case 'M':
+ AddMode = MASTER;
+ break;
+
+ case 'C':
+ Chroot = optarg;
+ break;
+
+ case 'i':
+ IgnoreDeps = TRUE;
+ break;
+
+ case 'h':
+ default:
+ usage();
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (AddMode != SLAVE) {
+ pkgs = (char **)malloc((argc+1) * sizeof(char *));
+ for (ch = 0; ch <= argc; pkgs[ch++] = NULL) ;
+
+ /* Get all the remaining package names, if any */
+ for (ch = 0; *argv; ch++, argv++) {
+ char temp[MAXPATHLEN];
+ if (Remote) {
+ if ((packagesite = getpackagesite()) == NULL)
+ errx(1, "package name too long");
+ if (strlcpy(temppackageroot, packagesite,
+ sizeof(temppackageroot)) >= sizeof(temppackageroot))
+ errx(1, "package name too long");
+ if (strlcat(temppackageroot, *argv, sizeof(temppackageroot))
+ >= sizeof(temppackageroot))
+ errx(1, "package name too long");
+ remotepkg = temppackageroot;
+ if (!((ptr = strrchr(remotepkg, '.')) && ptr[1] == 't' &&
+ (ptr[2] == 'b' || ptr[2] == 'g') && ptr[3] == 'z' &&
+ !ptr[4]))
+ if (strlcat(remotepkg, ".tbz",
+ sizeof(temppackageroot)) >= sizeof(temppackageroot))
+ errx(1, "package name too long");
+ }
+ if (!strcmp(*argv, "-")) /* stdin? */
+ pkgs[ch] = (char *)"-";
+ else if (isURL(*argv)) { /* preserve URLs */
+ if (strlcpy(temp, *argv, sizeof(temp)) >= sizeof(temp))
+ errx(1, "package name too long");
+ pkgs[ch] = strdup(temp);
+ }
+ else if ((Remote) && isURL(remotepkg)) {
+ if (strlcpy(temp, remotepkg, sizeof(temp)) >= sizeof(temp))
+ errx(1, "package name too long");
+ pkgs[ch] = strdup(temp);
+ } else { /* expand all pathnames to fullnames */
+ if (fexists(*argv)) /* refers to a file directly */
+ pkgs[ch] = strdup(realpath(*argv, temp));
+ else { /* look for the file in the expected places */
+ if (!(cp = fileFindByPath(NULL, *argv))) {
+ /* let pkg_do() fail later, so that error is reported */
+ if (strlcpy(temp, *argv, sizeof(temp)) >= sizeof(temp))
+ errx(1, "package name too long");
+ pkgs[ch] = strdup(temp);
+ } else {
+ if (strlcpy(temp, cp, sizeof(temp)) >= sizeof(temp))
+ errx(1, "package name too long");
+ pkgs[ch] = strdup(temp);
+ }
+ }
+ }
+ if (packagesite != NULL)
+ packagesite[0] = '\0';
+ }
+ }
+ /* If no packages, yelp */
+ if (!ch) {
+ warnx("missing package name(s)");
+ usage();
+ }
+ else if (ch > 1 && AddMode == MASTER) {
+ warnx("only one package name may be specified with master mode");
+ usage();
+ }
+ /* Perform chroot if requested */
+ if (Chroot != NULL) {
+ if (chroot(Chroot))
+ errx(1, "chroot to %s failed", Chroot);
+ }
+ /* Make sure the sub-execs we invoke get found */
+ setenv("PATH",
+ "/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/usr/X11R6/bin",
+ 1);
+
+ /* Set a reasonable umask */
+ umask(022);
+
+ if ((error = pkg_perform(pkgs)) != 0) {
+ if (Verbose)
+ warnx("%d package addition(s) failed", error);
+ return error;
+ }
+ else
+ return 0;
+}
+
+static char *
+getpackagesite(void)
+{
+ int reldate, i;
+ static char sitepath[MAXPATHLEN];
+ struct utsname u;
+
+ if (getenv("PACKAGESITE")) {
+ if (strlcpy(sitepath, getenv("PACKAGESITE"), sizeof(sitepath))
+ >= sizeof(sitepath))
+ return NULL;
+ return sitepath;
+ }
+
+ if (getenv("PACKAGEROOT")) {
+ if (strlcpy(sitepath, getenv("PACKAGEROOT"), sizeof(sitepath))
+ >= sizeof(sitepath))
+ return NULL;
+ } else {
+ if (strlcat(sitepath, "ftp://ftp.freebsd.org", sizeof(sitepath))
+ >= sizeof(sitepath))
+ return NULL;
+ }
+
+ if (strlcat(sitepath, "/pub/FreeBSD/ports/", sizeof(sitepath))
+ >= sizeof(sitepath))
+ return NULL;
+
+ uname(&u);
+ if (strlcat(sitepath, u.machine, sizeof(sitepath)) >= sizeof(sitepath))
+ return NULL;
+
+ reldate = getosreldate();
+ for(i = 0; releases[i].directory != NULL; i++) {
+ if (reldate >= releases[i].lowver && reldate <= releases[i].hiver) {
+ if (strlcat(sitepath, releases[i].directory, sizeof(sitepath))
+ >= sizeof(sitepath))
+ return NULL;
+ break;
+ }
+ }
+
+ if (strlcat(sitepath, "/Latest/", sizeof(sitepath)) >= sizeof(sitepath))
+ return NULL;
+
+ return sitepath;
+
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: pkg_add [-viInfFrRMSK] [-t template] [-p prefix] [-P prefix] [-C chrootdir]",
+ " pkg-name [pkg-name ...]");
+ exit(1);
+}
diff --git a/usr.sbin/pkg_install/add/perform.c b/usr.sbin/pkg_install/add/perform.c
new file mode 100644
index 0000000..872c7a8
--- /dev/null
+++ b/usr.sbin/pkg_install/add/perform.c
@@ -0,0 +1,652 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the main body of the add module.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include <paths.h>
+#include "lib.h"
+#include "add.h"
+
+#include <libgen.h>
+#include <signal.h>
+#include <sys/wait.h>
+
+static int pkg_do(char *);
+static int sanity_check(char *);
+static char LogDir[FILENAME_MAX];
+static int zapLogDir; /* Should we delete LogDir? */
+
+int
+pkg_perform(char **pkgs)
+{
+ int i, err_cnt = 0;
+
+ signal(SIGINT, cleanup);
+ signal(SIGHUP, cleanup);
+
+ if (AddMode == SLAVE)
+ err_cnt = pkg_do(NULL);
+ else {
+ for (i = 0; pkgs[i]; i++)
+ err_cnt += pkg_do(pkgs[i]);
+ }
+ return err_cnt;
+}
+
+static Package Plist;
+static char *Home;
+
+/*
+ * This is seriously ugly code following. Written very fast!
+ * [And subsequently made even worse.. Sigh! This code was just born
+ * to be hacked, I guess.. :) -jkh]
+ */
+static int
+pkg_do(char *pkg)
+{
+ char pkg_fullname[FILENAME_MAX];
+ char playpen[FILENAME_MAX];
+ char extract_contents[FILENAME_MAX];
+ char *where_to, *extract;
+ FILE *cfile;
+ int code;
+ PackingList p;
+ struct stat sb;
+ int inPlace, conflictsfound, errcode;
+ /* support for separate pre/post install scripts */
+ int new_m = 0;
+ char pre_script[FILENAME_MAX] = INSTALL_FNAME;
+ char post_script[FILENAME_MAX];
+ char pre_arg[FILENAME_MAX], post_arg[FILENAME_MAX];
+ char *conflict[2];
+ char **matched;
+
+ conflictsfound = 0;
+ code = 0;
+ zapLogDir = 0;
+ LogDir[0] = '\0';
+ strcpy(playpen, FirstPen);
+ inPlace = 0;
+
+ /* Are we coming in for a second pass, everything already extracted? */
+ if (!pkg) {
+ fgets(playpen, FILENAME_MAX, stdin);
+ playpen[strlen(playpen) - 1] = '\0'; /* pesky newline! */
+ if (chdir(playpen) == FAIL) {
+ warnx("pkg_add in SLAVE mode can't chdir to %s", playpen);
+ return 1;
+ }
+ read_plist(&Plist, stdin);
+ where_to = playpen;
+ }
+ /* Nope - do it now */
+ else {
+ /* Is it an ftp://foo.bar.baz/file.t[bg]z specification? */
+ if (isURL(pkg)) {
+ if (!(Home = fileGetURL(NULL, pkg, KeepPackage))) {
+ warnx("unable to fetch '%s' by URL", pkg);
+ return 1;
+ }
+ where_to = Home;
+ strcpy(pkg_fullname, pkg);
+ cfile = fopen(CONTENTS_FNAME, "r");
+ if (!cfile) {
+ warnx(
+ "unable to open table of contents file '%s' - not a package?",
+ CONTENTS_FNAME);
+ goto bomb;
+ }
+ read_plist(&Plist, cfile);
+ fclose(cfile);
+ }
+ else {
+ strcpy(pkg_fullname, pkg); /*
+ * Copy for sanity's sake,
+ * could remove pkg_fullname
+ */
+ if (strcmp(pkg, "-")) {
+ if (stat(pkg_fullname, &sb) == FAIL) {
+ warnx("can't stat package file '%s'", pkg_fullname);
+ goto bomb;
+ }
+ sprintf(extract_contents, "--fast-read %s", CONTENTS_FNAME);
+ extract = extract_contents;
+ }
+ else {
+ extract = NULL;
+ sb.st_size = 100000; /* Make up a plausible average size */
+ }
+ Home = make_playpen(playpen, sb.st_size * 4);
+ if (!Home)
+ errx(1, "unable to make playpen for %lld bytes", (long long)sb.st_size * 4);
+ where_to = Home;
+ /* Since we can call ourselves recursively, keep notes on where we came from */
+ if (!getenv("_TOP"))
+ setenv("_TOP", Home, 1);
+ if (unpack(pkg_fullname, extract)) {
+ warnx(
+ "unable to extract table of contents file from '%s' - not a package?",
+ pkg_fullname);
+ goto bomb;
+ }
+ cfile = fopen(CONTENTS_FNAME, "r");
+ if (!cfile) {
+ warnx(
+ "unable to open table of contents file '%s' - not a package?",
+ CONTENTS_FNAME);
+ goto bomb;
+ }
+ read_plist(&Plist, cfile);
+ fclose(cfile);
+
+ /* Extract directly rather than moving? Oh goodie! */
+ if (find_plist_option(&Plist, "extract-in-place")) {
+ if (Verbose)
+ printf("Doing in-place extraction for %s\n", pkg_fullname);
+ p = find_plist(&Plist, PLIST_CWD);
+ if (p) {
+ if (!isdir(p->name) && !Fake) {
+ if (Verbose)
+ printf("Desired prefix of %s does not exist, creating..\n", p->name);
+ vsystem("/bin/mkdir -p %s", p->name);
+ if (chdir(p->name) == -1) {
+ warn("unable to change directory to '%s'", p->name);
+ goto bomb;
+ }
+ }
+ where_to = p->name;
+ inPlace = 1;
+ }
+ else {
+ warnx(
+ "no prefix specified in '%s' - this is a bad package!",
+ pkg_fullname);
+ goto bomb;
+ }
+ }
+
+ /*
+ * Apply a crude heuristic to see how much space the package will
+ * take up once it's unpacked. I've noticed that most packages
+ * compress an average of 75%, so multiply by 4 for good measure.
+ */
+
+ if (!extract && !inPlace && min_free(playpen) < sb.st_size * 4) {
+ warnx("projected size of %lld exceeds available free space.\n"
+"Please set your PKG_TMPDIR variable to point to a location with more\n"
+ "free space and try again", (long long)sb.st_size * 4);
+ warnx("not extracting %s\ninto %s, sorry!",
+ pkg_fullname, where_to);
+ goto bomb;
+ }
+
+ /* If this is a direct extract and we didn't want it, stop now */
+ if (inPlace && Fake)
+ goto success;
+
+ /* Finally unpack the whole mess. If extract is null we
+ already + did so so don't bother doing it again. */
+ if (extract && unpack(pkg_fullname, NULL)) {
+ warnx("unable to extract '%s'!", pkg_fullname);
+ goto bomb;
+ }
+ }
+
+ /* Check for sanity and dependencies */
+ if (sanity_check(pkg))
+ goto bomb;
+
+ /* If we're running in MASTER mode, just output the plist and return */
+ if (AddMode == MASTER) {
+ printf("%s\n", where_playpen());
+ write_plist(&Plist, stdout);
+ return 0;
+ }
+ }
+
+ /*
+ * If we have a prefix, delete the first one we see and add this
+ * one in place of it.
+ */
+ if (Prefix) {
+ delete_plist(&Plist, FALSE, PLIST_CWD, NULL);
+ add_plist_top(&Plist, PLIST_CWD, Prefix);
+ }
+
+ setenv(PKG_PREFIX_VNAME, (p = find_plist(&Plist, PLIST_CWD)) ? p->name : ".", 1);
+ /* Protect against old packages with bogus @name and origin fields */
+ if (Plist.name == NULL)
+ Plist.name = "anonymous";
+ if (Plist.origin == NULL)
+ Plist.origin = "anonymous/anonymous";
+
+ /*
+ * See if we're already registered either with the same name (the same
+ * version) or some other version with the same origin.
+ */
+ if ((isinstalledpkg(Plist.name) > 0 ||
+ matchbyorigin(Plist.origin, NULL) != NULL) && !Force) {
+ warnx("package '%s' or its older version already installed%s",
+ Plist.name, FailOnAlreadyInstalled ? "" : " (ignored)");
+ code = FailOnAlreadyInstalled != FALSE;
+ goto success; /* close enough for government work */
+ }
+
+ /* Now check the packing list for conflicts */
+ if (!IgnoreDeps){
+ for (p = Plist.head; p != NULL; p = p->next) {
+ if (p->type == PLIST_CONFLICTS) {
+ int i;
+ conflict[0] = strdup(p->name);
+ conflict[1] = NULL;
+ matched = matchinstalled(MATCH_GLOB, conflict, &errcode);
+ free(conflict[0]);
+ if (errcode == 0 && matched != NULL)
+ for (i = 0; matched[i] != NULL; i++)
+ if (isinstalledpkg(matched[i]) > 0) {
+ warnx("package '%s' conflicts with %s", Plist.name,
+ matched[i]);
+ conflictsfound = 1;
+ }
+
+ continue;
+ }
+ }
+ if(conflictsfound) {
+ if(!Force) {
+ warnx("please use pkg_delete first to remove conflicting package(s) or -f to force installation");
+ code = 1;
+ goto bomb;
+ } else
+ warnx("-f specified; proceeding anyway");
+ }
+
+ /* Now check the packing list for dependencies */
+ for (p = Plist.head; p ; p = p->next) {
+ char *deporigin;
+
+ if (p->type != PLIST_PKGDEP)
+ continue;
+ deporigin = (p->next->type == PLIST_DEPORIGIN) ? p->next->name : NULL;
+ if (Verbose) {
+ printf("Package '%s' depends on '%s'", Plist.name, p->name);
+ if (deporigin != NULL)
+ printf(" with '%s' origin", deporigin);
+ printf(".\n");
+ }
+ if (isinstalledpkg(p->name) <= 0 &&
+ !(deporigin != NULL && matchbyorigin(deporigin, NULL) != NULL)) {
+ char path[FILENAME_MAX], *cp = NULL;
+
+ if (!Fake) {
+ char prefixArg[2 + MAXPATHLEN]; /* "-P" + Prefix */
+ if (PrefixRecursive) {
+ strlcpy(prefixArg, "-P", sizeof(prefixArg));
+ strlcat(prefixArg, Prefix, sizeof(prefixArg));
+ }
+ if (!isURL(pkg) && !getenv("PKG_ADD_BASE")) {
+ const char *ext;
+
+ ext = strrchr(pkg_fullname, '.');
+ if (ext == NULL)
+ ext = ".tbz";
+ snprintf(path, FILENAME_MAX, "%s/%s%s", getenv("_TOP"), p->name, ext);
+ if (fexists(path))
+ cp = path;
+ else
+ cp = fileFindByPath(pkg, p->name);
+ if (cp) {
+ if (Verbose)
+ printf("Loading it from %s.\n", cp);
+ if (vsystem("%s %s %s '%s'", PkgAddCmd, Verbose ? "-v " : "", PrefixRecursive ? prefixArg : "", cp)) {
+ warnx("autoload of dependency '%s' failed%s",
+ cp, Force ? " (proceeding anyway)" : "!");
+ if (!Force)
+ ++code;
+ }
+ }
+ else {
+ warnx("could not find package %s %s",
+ p->name, Force ? " (proceeding anyway)" : "!");
+ if (!Force)
+ ++code;
+ }
+ }
+ else if ((cp = fileGetURL(pkg, p->name, KeepPackage)) != NULL) {
+ if (Verbose)
+ printf("Finished loading %s over FTP.\n", p->name);
+ if (!fexists("+CONTENTS")) {
+ warnx("autoloaded package %s has no +CONTENTS file?",
+ p->name);
+ if (!Force)
+ ++code;
+ }
+ else if (vsystem("(pwd; /bin/cat +CONTENTS) | %s %s %s %s -S", PkgAddCmd, Verbose ? "-v" : "", PrefixRecursive ? prefixArg : "", KeepPackage ? "-K" : "")) {
+ warnx("pkg_add of dependency '%s' failed%s",
+ p->name, Force ? " (proceeding anyway)" : "!");
+ if (!Force)
+ ++code;
+ }
+ else if (Verbose)
+ printf("\t'%s' loaded successfully.\n", p->name);
+ /* Nuke the temporary playpen */
+ leave_playpen();
+ }
+ }
+ else {
+ if (Verbose)
+ printf("and was not found%s.\n", Force ? " (proceeding anyway)" : "");
+ else
+ printf("Package dependency %s for %s not found%s\n", p->name, pkg,
+ Force ? " (proceeding anyway)" : "!");
+ if (!Force)
+ ++code;
+ }
+ }
+ else if (Verbose)
+ printf(" - already installed.\n");
+ }
+ } /* if (!IgnoreDeps) */
+
+ if (code != 0)
+ goto bomb;
+
+ /* Look for the requirements file */
+ if (fexists(REQUIRE_FNAME)) {
+ vsystem("/bin/chmod +x %s", REQUIRE_FNAME); /* be sure */
+ if (Verbose)
+ printf("Running requirements file first for %s..\n", Plist.name);
+ if (!Fake && vsystem("./%s %s INSTALL", REQUIRE_FNAME, Plist.name)) {
+ warnx("package %s fails requirements %s", pkg_fullname,
+ Force ? "installing anyway" : "- not installed");
+ if (!Force) {
+ code = 1;
+ goto success; /* close enough for government work */
+ }
+ }
+ }
+
+ /*
+ * Test whether to use the old method of passing tokens to installation
+ * scripts, and set appropriate variables..
+ */
+
+ if (fexists(POST_INSTALL_FNAME)) {
+ new_m = 1;
+ sprintf(post_script, "%s", POST_INSTALL_FNAME);
+ pre_arg[0] = '\0';
+ post_arg[0] = '\0';
+ } else {
+ if (fexists(INSTALL_FNAME)) {
+ sprintf(post_script, "%s", INSTALL_FNAME);
+ sprintf(pre_arg, "PRE-INSTALL");
+ sprintf(post_arg, "POST-INSTALL");
+ }
+ }
+
+ /* If we're really installing, and have an installation file, run it */
+ if (!NoInstall && fexists(pre_script)) {
+ vsystem("/bin/chmod +x %s", pre_script); /* make sure */
+ if (Verbose)
+ printf("Running pre-install for %s..\n", Plist.name);
+ if (!Fake && vsystem("./%s %s %s", pre_script, Plist.name, pre_arg)) {
+ warnx("install script returned error status");
+ unlink(pre_script);
+ code = 1;
+ goto success; /* nothing to uninstall yet */
+ }
+ }
+
+ /* Now finally extract the entire show if we're not going direct */
+ if (!inPlace && !Fake)
+ extract_plist(".", &Plist);
+
+ if (!Fake && fexists(MTREE_FNAME)) {
+ if (Verbose)
+ printf("Running mtree for %s..\n", Plist.name);
+ p = find_plist(&Plist, PLIST_CWD);
+ if (Verbose)
+ printf("mtree -U -f %s -d -e -p %s >%s\n", MTREE_FNAME, p ? p->name : "/", _PATH_DEVNULL);
+ if (!Fake) {
+ if (vsystem("/usr/sbin/mtree -U -f %s -d -e -p %s >%s", MTREE_FNAME, p ? p->name : "/", _PATH_DEVNULL))
+ warnx("mtree returned a non-zero status - continuing");
+ }
+ }
+
+ /* Run the installation script one last time? */
+ if (!NoInstall && fexists(post_script)) {
+ vsystem("/bin/chmod +x %s", post_script); /* make sure */
+ if (Verbose)
+ printf("Running post-install for %s..\n", Plist.name);
+ if (!Fake && vsystem("./%s %s %s", post_script, Plist.name, post_arg)) {
+ warnx("install script returned error status");
+ unlink(post_script);
+ code = 1;
+ goto fail;
+ }
+ }
+
+ /* Time to record the deed? */
+ if (!NoRecord && !Fake) {
+ char contents[FILENAME_MAX];
+ char **depnames = NULL, **deporigins = NULL, ***depmatches;
+ int i, dep_count = 0;
+ FILE *contfile;
+
+ if (getuid() != 0)
+ warnx("not running as root - trying to record install anyway");
+ sprintf(LogDir, "%s/%s", LOG_DIR, Plist.name);
+ zapLogDir = 1;
+ if (Verbose)
+ printf("Attempting to record package into %s..\n", LogDir);
+ if (make_hierarchy(LogDir)) {
+ warnx("can't record package into '%s', you're on your own!",
+ LogDir);
+ bzero(LogDir, FILENAME_MAX);
+ code = 1;
+ goto success; /* close enough for government work */
+ }
+ /* Make sure pkg_info can read the entry */
+ vsystem("/bin/chmod a+rx %s", LogDir);
+ move_file(".", DESC_FNAME, LogDir);
+ move_file(".", COMMENT_FNAME, LogDir);
+ if (fexists(INSTALL_FNAME))
+ move_file(".", INSTALL_FNAME, LogDir);
+ if (fexists(POST_INSTALL_FNAME))
+ move_file(".", POST_INSTALL_FNAME, LogDir);
+ if (fexists(DEINSTALL_FNAME))
+ move_file(".", DEINSTALL_FNAME, LogDir);
+ if (fexists(POST_DEINSTALL_FNAME))
+ move_file(".", POST_DEINSTALL_FNAME, LogDir);
+ if (fexists(REQUIRE_FNAME))
+ move_file(".", REQUIRE_FNAME, LogDir);
+ if (fexists(DISPLAY_FNAME))
+ move_file(".", DISPLAY_FNAME, LogDir);
+ if (fexists(MTREE_FNAME))
+ move_file(".", MTREE_FNAME, LogDir);
+ sprintf(contents, "%s/%s", LogDir, CONTENTS_FNAME);
+ contfile = fopen(contents, "w");
+ if (!contfile) {
+ warnx("can't open new contents file '%s'! can't register pkg",
+ contents);
+ goto success; /* can't log, but still keep pkg */
+ }
+ write_plist(&Plist, contfile);
+ fclose(contfile);
+ for (p = Plist.head; p ; p = p->next) {
+ char *deporigin;
+
+ if (p->type != PLIST_PKGDEP)
+ continue;
+ deporigin = (p->next->type == PLIST_DEPORIGIN) ? p->next->name :
+ NULL;
+ if (Verbose) {
+ printf("Trying to record dependency on package '%s'", p->name);
+ if (deporigin != NULL)
+ printf(" with '%s' origin", deporigin);
+ printf(".\n");
+ }
+
+ if (deporigin) {
+ /* Defer to origin lookup */
+ depnames = realloc(depnames, (dep_count + 1) * sizeof(*depnames));
+ depnames[dep_count] = p->name;
+ deporigins = realloc(deporigins, (dep_count + 2) * sizeof(*deporigins));
+ deporigins[dep_count] = deporigin;
+ deporigins[dep_count + 1] = NULL;
+ dep_count++;
+ } else {
+ /* No origin recorded, try to register on literal package name */
+ sprintf(contents, "%s/%s/%s", LOG_DIR, p->name,
+ REQUIRED_BY_FNAME);
+ contfile = fopen(contents, "a");
+ if (!contfile) {
+ warnx("can't open dependency file '%s'!\n"
+ "dependency registration is incomplete", contents);
+ } else {
+ fprintf(contfile, "%s\n", Plist.name);
+ if (fclose(contfile) == EOF) {
+ warnx("cannot properly close file %s", contents);
+ }
+ }
+ }
+ }
+ if (dep_count > 0) {
+ depmatches = matchallbyorigin((const char **)deporigins, NULL);
+ free(deporigins);
+ if (!IgnoreDeps && depmatches) {
+ for (i = 0; i < dep_count; i++) {
+ if (depmatches[i]) {
+ int j;
+ char **tmp = depmatches[i];
+ for (j = 0; tmp[j] != NULL; j++) {
+ /* Origin looked up */
+ sprintf(contents, "%s/%s/%s", LOG_DIR, tmp[j],
+ REQUIRED_BY_FNAME);
+ if (depnames[i] && strcmp(depnames[i], tmp[j]) != 0)
+ warnx("warning: package '%s' requires '%s', but '%s' "
+ "is installed", Plist.name, depnames[i], tmp[j]);
+ contfile = fopen(contents, "a");
+ if (!contfile) {
+ warnx("can't open dependency file '%s'!\n"
+ "dependency registration is incomplete", contents);
+ } else {
+ fprintf(contfile, "%s\n", Plist.name);
+ if (fclose(contfile) == EOF)
+ warnx("cannot properly close file %s", contents);
+ }
+ }
+ } else if (depnames[i]) {
+ /* No package present with this origin, try literal package name */
+ sprintf(contents, "%s/%s/%s", LOG_DIR, depnames[i],
+ REQUIRED_BY_FNAME);
+ contfile = fopen(contents, "a");
+ if (!contfile) {
+ warnx("can't open dependency file '%s'!\n"
+ "dependency registration is incomplete", contents);
+ } else {
+ fprintf(contfile, "%s\n", Plist.name);
+ if (fclose(contfile) == EOF) {
+ warnx("cannot properly close file %s", contents);
+ }
+ }
+ }
+ }
+ }
+ }
+ if (Verbose)
+ printf("Package %s registered in %s\n", Plist.name, LogDir);
+ }
+
+ if ((p = find_plist(&Plist, PLIST_DISPLAY)) != NULL) {
+ FILE *fp;
+ char buf[BUFSIZ];
+
+ snprintf(buf, sizeof buf, "%s/%s", LogDir, p->name);
+ fp = fopen(buf, "r");
+ if (fp) {
+ putc('\n', stdout);
+ while (fgets(buf, sizeof(buf), fp))
+ fputs(buf, stdout);
+ putc('\n', stdout);
+ (void) fclose(fp);
+ } else {
+ if (!Fake) {
+ warnx("cannot open %s as display file", buf);
+ }
+ }
+ }
+
+ goto success;
+
+ bomb:
+ code = 1;
+ goto success;
+
+ fail:
+ /* Nuke the whole (installed) show, XXX but don't clean directories */
+ if (!Fake)
+ delete_package(FALSE, FALSE, &Plist);
+
+ success:
+ /* delete the packing list contents */
+ free_plist(&Plist);
+ leave_playpen();
+ return code;
+}
+
+static int
+sanity_check(char *pkg)
+{
+ int code = 0;
+
+ if (!fexists(CONTENTS_FNAME)) {
+ warnx("package %s has no CONTENTS file!", pkg);
+ code = 1;
+ }
+ else if (!fexists(COMMENT_FNAME)) {
+ warnx("package %s has no COMMENT file!", pkg);
+ code = 1;
+ }
+ else if (!fexists(DESC_FNAME)) {
+ warnx("package %s has no DESC file!", pkg);
+ code = 1;
+ }
+ return code;
+}
+
+void
+cleanup(int sig)
+{
+ static int in_cleanup = 0;
+
+ if (!in_cleanup) {
+ in_cleanup = 1;
+ if (sig)
+ printf("Signal %d received, cleaning up..\n", sig);
+ if (!Fake && zapLogDir && LogDir[0])
+ vsystem("%s -rf %s", REMOVE_CMD, LogDir);
+ leave_playpen();
+ }
+ if (sig)
+ exit(1);
+}
diff --git a/usr.sbin/pkg_install/add/pkg_add.1 b/usr.sbin/pkg_install/add/pkg_add.1
new file mode 100644
index 0000000..d090cb5
--- /dev/null
+++ b/usr.sbin/pkg_install/add/pkg_add.1
@@ -0,0 +1,603 @@
+.\"
+.\" FreeBSD install - a package for the installation and maintainance
+.\" of non-core utilities.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" Jordan K. Hubbard
+.\"
+.\" $FreeBSD$
+.\"
+.Dd January 4, 2009
+.Dt PKG_ADD 1
+.Os
+.Sh NAME
+.Nm pkg_add
+.Nd a utility for installing software package distributions
+.Sh SYNOPSIS
+.Nm
+.Op Fl viInfFrRMSK
+.Op Fl t Ar template
+.Op Fl p Ar prefix
+.Op Fl P Ar prefix
+.Op Fl C Ar chrootdir
+.Ar pkg-name Op Ar pkg-name ...
+.Sh DESCRIPTION
+The
+.Nm
+command is used to extract packages that have been previously created
+with the
+.Xr pkg_create 1
+command.
+.Sh WARNING
+.Bf -emphasis
+Since the
+.Nm
+command may execute scripts or programs contained within a package file,
+your system may be susceptible to
+.Dq Em trojan horses
+or other subtle
+attacks from miscreants who create dangerous package files.
+.Pp
+You are advised to verify the competence and identity of those who
+provide installable package files.
+For extra protection, use the
+.Fl M
+flag to extract the package file, and inspect its contents and scripts to
+ensure it poses no danger to your system's integrity.
+Pay particular
+attention to any +INSTALL, +POST-INSTALL, +DEINSTALL, +POST-DEINSTALL,
++REQUIRE or +MTREE_DIRS files, and inspect the +CONTENTS file for
+.Cm @cwd ,
+.Cm @mode
+(check for setuid),
+.Cm @dirrm ,
+.Cm @exec ,
+and
+.Cm @unexec
+directives, and/or use the
+.Xr pkg_info 1
+command to examine the package file.
+.Ef
+.Sh OPTIONS
+The following command line arguments are supported:
+.Bl -tag -width indent
+.It Ar pkg-name Op Ar pkg-name ...
+The named packages are installed.
+A package name of
+.Fl
+will cause
+.Nm
+to read from stdin.
+If the packages are not found in the current
+working directory,
+.Nm
+will search them in each directory named by
+.Ev PKG_PATH .
+.It Fl v , -verbose
+Turn on verbose output.
+.It Fl K , -keep
+Keep any downloaded package in
+.Ev PKGDIR
+if it is defined or in current directory by default.
+.It Fl i , -no-deps
+Install the package without fetching and installing
+dependencies.
+.It Fl I , -no-script
+If any installation scripts (pre-install or post-install) exist for a given
+package, do not execute them.
+.It Fl n , -dry-run
+Do not actually install a package, just report the steps that
+would be taken if it was.
+.It Fl R , -no-record
+Do not record the installation of a package.
+This means
+that you cannot deinstall it later, so only use this option if
+you know what you are doing!
+.It Fl r , -remote
+Use the remote fetching feature.
+This will determine the appropriate
+objformat and release and then fetch and install the package.
+.It Fl f , -force
+Force installation to proceed even if prerequisite packages are not
+installed or the requirements script fails.
+Although
+.Nm
+will still try to find and auto-install missing prerequisite packages,
+a failure to find one will not be fatal.
+.It Fl F
+Already installed packages are not an error.
+.It Fl p , -prefix Ar prefix
+Set
+.Ar prefix
+as the directory in which to extract files from a package.
+If a package has set its default directory, it will be overridden
+by this flag.
+Note that only the first
+.Cm @cwd
+directive will be replaced, since
+.Nm
+has no way of knowing which directory settings are relative and
+which are absolute.
+It is rare in any case to see more than one
+directory transition made, but when such does happen and you wish
+to have control over *all* directory transitions, then you
+may then wish to look into the use of
+.Cm MASTER
+and
+.Cm SLAVE
+modes (see the
+.Fl M
+and
+.Fl S
+options).
+If the
+.Fl p
+flag appears after any
+.Fl P
+flag on the command line, it overrides its effect, causing
+.Nm
+not to use the given
+.Ar prefix
+recursively.
+.It Fl P Ar prefix
+Does the same as the
+.Fl p
+option, except that the given
+.Ar prefix
+is also used recursively for the dependency packages, if any.
+If the
+.Fl P
+flag appears after any
+.Fl p
+flag on the command line, it overrides its effect, causing
+.Nm
+to use the given
+.Ar prefix
+recursively.
+.It Fl t , -template Ar template
+Use
+.Ar template
+as the input to
+.Xr mktemp 3
+when creating a
+.Dq staging area .
+By default, this is the string
+.Pa /var/tmp/instmp.XXXXXX ,
+but it may be necessary to override it in the situation where
+space in your
+.Pa /var/tmp
+directory is limited.
+Be sure to leave some number of `X' characters
+for
+.Xr mktemp 3
+to fill in with a unique ID.
+.Pp
+You can get a performance boost by setting the staging area
+.Ar template
+to reside on the same disk partition as target directories for package
+file installation; often this is
+.Pa /usr .
+.It Fl M , -master
+Run in
+.Cm MASTER
+mode.
+This is a very specialized mode for running
+.Nm
+and is meant to be run in conjunction with
+.Cm SLAVE
+mode.
+When run in this mode,
+.Nm
+does no work beyond extracting the package into a temporary staging
+area (see the
+.Fl t
+option), reading in the packing list, and then dumping it (prefaced by
+the current staging area) to stdout where it may be filtered by a
+program such as
+.Xr sed 1 .
+When used in conjunction with
+.Cm SLAVE
+mode, it allows you to make radical changes to the package structure
+before acting on its contents.
+.It Fl S , -slave
+Run in
+.Cm SLAVE
+mode.
+This is a very specialized mode for running
+.Nm
+and is meant to be run in conjunction with
+.Cm MASTER
+mode.
+When run in this mode,
+.Nm
+expects the release contents to be already extracted and waiting
+in the staging area, the location of which is read as a string
+from stdin.
+The complete packing list is also read from stdin,
+and the contents then acted on as normal.
+.It Fl C , -chroot Ar chrootdir
+Before doing any operations,
+.Xr chroot 2
+to the
+.Ar chrootdir
+directory so that all package files, and the package database, are
+installed to
+.Ar chrootdir .
+Note that
+.Ar chrootdir
+needs to be a fairly complete file system, including everything normally
+needed by
+.Nm
+to run.
+This flag was added to help support operations done by
+.Xr sysinstall 8
+and is not expected to be useful for much else.
+Be careful that
+.Ar chrootdir
+is properly configured and cannot be modified by normal users,
+versions of commands like
+.Xr fetch 1
+may be run inside
+.Ar chrootdir
+as a side effect.
+.El
+.Pp
+One or more
+.Ar pkg-name
+arguments may be specified, each being either a file containing the
+package (these usually end with a
+.Dq .tbz
+suffix) or a
+URL pointing at a file available on an ftp site.
+Thus you may
+extract files directly from their anonymous ftp locations (e.g.\&
+.Nm
+.Li ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/packages/shells/bash-1.14.7.tbz ) .
+Note: If you wish to use
+.Bf -emphasis
+passive mode
+.Ef
+ftp in such transfers, set
+the variable
+.Bf -emphasis
+FTP_PASSIVE_MODE
+.Ef
+to some value in your environment.
+Otherwise, the more standard
+ACTIVE mode may be used.
+If
+.Nm
+consistently fails to fetch a package from a site known to work,
+it may be because you have a firewall that demands the usage of
+.Bf -emphasis
+passive mode
+.Ef
+ftp.
+.Sh TECHNICAL DETAILS
+The
+.Nm
+utility extracts each package's
+.Dq "packing list"
+into a special staging directory (see
+.Sx ENVIRONMENT ) ,
+parses it, and then runs
+through the following sequence to fully extract the contents of the package:
+.Bl -enum
+.It
+A check is made to determine if the package is already recorded as installed.
+If it is, installation is terminated.
+.It
+A check is made to determine if the package conflicts (from
+.Ic @conflicts
+directives, see
+.Xr pkg_create 1 )
+with an already installed package.
+If it is, installation is terminated.
+.It
+Scan all the package dependencies (from
+.Ic @pkgdep
+directives, see
+.Xr pkg_create 1 )
+are read from the packing list.
+If any of these required packages is not currently installed,
+an attempt is made to find and install it;
+if the missing package cannot be found or installed,
+the installation is terminated.
+.It
+Search for any
+.Ic @option
+directives which control how the package is added to the system.
+At the time of this writing, the only currently implemented option is
+.Ic @option Cm extract-in-place
+which will cause the package to be extracted directly into its
+prefix directory without moving through a staging area.
+.It
+If
+.Ic @option Cm extract-in-place
+is enabled, the package is now extracted directly into its
+final location, otherwise it is extracted into the staging area.
+.It
+If a requirements script
+.Pa +REQUIRE
+exists for the package (see the
+.Fl r
+flag of
+.Xr pkg_create 1 ) ,
+then execute it with the following arguments:
+.Pp
+.D1 Ar pkg-name Li INSTALL
+.Pp
+where
+.Ar pkg-name
+is the name of the package in question and the
+.Dq Li INSTALL
+keyword denotes this as an installation requirements check (useful if
+you want to have one script serving multiple functions).
+.It
+If a pre-install script
+.Pa +INSTALL
+exists for the package,
+it is then executed with the following arguments:
+.Pp
+.D1 Ar pkg-name Li PRE-INSTALL
+.Pp
+where
+.Ar pkg-name
+is the name of the package in question and
+.Dq Li PRE-INSTALL
+is a keyword denoting this as the preinstallation phase.
+.Pp
+.Sy Note :
+The
+.Dq Li PRE-INSTALL
+keyword will not appear if separate scripts for pre-install and post-install
+are given during package creation time (using the
+.Fl i
+and
+.Fl I
+flags to
+.Xr pkg_create 1 ) .
+.It
+If
+.Cm @option Cm extract-in-place
+is not used, then the packing list (this is the
+.Pa +CONTENTS
+file) is now used as a guide for moving (or copying, as necessary) files from
+the staging area into their final locations.
+.It
+If an mtree file
+.Pa +MTREE_DIRS
+exists for the package (see the
+.Fl m
+flag of
+.Xr pkg_create 1 ) ,
+then
+.Xr mtree 8
+is invoked as:
+.Pp
+.D1 Nm mtree Fl U f Pa +MTREE_DIRS Fl d e p Ar prefix
+.Pp
+where
+.Ar prefix
+is either the prefix specified with the
+.Fl p
+or
+.Fl P
+flag or,
+if neither flag was specified, the name of the first directory named by a
+.Ic @cwd
+directive within this package.
+.It
+If a post-install script
+.Pa +POST-INSTALL
+exists for the package,
+it is then executed with the following arguments:
+.Pp
+.D1 Ar pkg-name Li POST-INSTALL
+.Pp
+where
+.Ar pkg-name
+is the name of the package in question and
+.Dq Li POST-INSTALL
+is a keyword denoting this as the post-installation phase.
+.Pp
+.Sy Note :
+The
+.Dq Li POST-INSTALL
+keyword will not appear if separate scripts for pre-install and post-install
+are given during package creation time (using the
+.Fl i
+and
+.Fl I
+flags to
+.Xr pkg_create 1 ) .
+.Pp
+Reasoning behind passing keywords such as
+.Dq Li POST-INSTALL
+and
+.Dq Li PRE-INSTALL
+is that this allows you to write a single
+install
+script that does both
+.Dq before
+and
+.Dq after
+actions.
+But, separating the
+functionality is more advantageous and easier from a maintenance viewpoint.
+.It
+After installation is complete, a copy of the
+description
+.Pq Pa +DESC ,
+comment
+.Pq Pa +COMMENT ,
+pre-install script
+.Pq Pa +INSTALL ,
+post-install script
+.Pq Pa +POST-INSTALL ,
+deinstall script
+.Pq Pa +DEINSTALL ,
+post-deinstall script
+.Pq Pa +POST-DEINSTALL ,
+requirements script
+.Pq Pa +REQUIRE ,
+display
+.Pq Pa +DISPLAY ,
+mtree
+.Pq Pa +MTREE_DIRS ,
+and packing list
+.Pq Pa +CONTENTS
+files are copied into
+.Pa /var/db/pkg/ Ns Aq Ar pkg-name
+for subsequent possible use by
+.Xr pkg_delete 1 .
+Any package dependencies are recorded in the other packages'
+.Pa /var/db/pkg/ Ns Ao Ar other-pkg Ac Ns Pa /+REQUIRED_BY
+file
+(if the environment variable
+.Ev PKG_DBDIR
+is set, this overrides the
+.Pa /var/db/pkg/
+path shown above).
+.It
+Finally, the staging area is deleted and the program terminates.
+.El
+.Pp
+All the scripts are called with the environment variable
+.Ev PKG_PREFIX
+set to the installation prefix (see the
+.Fl p
+and
+.Fl P
+options above).
+This allows a package author to write a script
+that reliably performs some action on the directory where the package
+is installed, even if the user might change it with the
+.Fl p
+or
+.Fl P
+flags to
+.Nm .
+.Sh ENVIRONMENT
+The value of the
+.Ev PKG_PATH
+is used if a given package cannot be found.
+The environment variable
+should be a series of entries separated by colons.
+Each entry
+consists of a directory name.
+The current directory may be indicated
+implicitly by an empty directory name, or explicitly by a single
+period.
+.Pp
+The environment variable
+.Ev PKG_DBDIR
+specifies an alternative location for the installed package database,
+default location is
+.Pa /var/db/pkg .
+.Pp
+The environment variables
+.Ev PKG_TMPDIR
+and
+.Ev TMPDIR ,
+in that order, are taken to name temporary directories where
+.Nm
+will attempt to create its staging area in.
+If these variables are not present or if the directories named lack
+sufficient space, then
+.Nm
+will use the first of
+.Pa /var/tmp ,
+.Pa /tmp
+or
+.Pa /usr/tmp
+with sufficient space.
+.Pp
+The environment variable
+.Ev PACKAGEROOT
+specifies an alternate location for
+.Nm
+to fetch from.
+The fetch URL is built using this environment variable and the automatic
+directory logic that
+.Nm
+uses when the
+.Fl r
+option is invoked.
+An example setting would be
+.Qq Li ftp://ftp3.FreeBSD.org .
+.Pp
+The environment variable
+.Ev PACKAGESITE
+specifies an alternate location for
+.Nm
+to fetch from.
+This variable subverts the automatic directory logic
+that
+.Nm
+uses when the
+.Fl r
+option is invoked.
+Thus it should be a complete URL to the remote package file(s).
+.Pp
+The environment variable
+.Ev PKGDIR
+specifies an alternative location to save downloaded packages to when
+.Fl K
+option is used.
+.Sh FILES
+.Bl -tag -width /var/db/pkg -compact
+.It Pa /var/tmp
+Temporary directory for creating the staging area, if environmental variables
+.Ev PKG_TMPDIR
+or
+.Ev TMPDIR
+do not point to a suitable directory.
+.It Pa /tmp
+Next choice if
+.Pa /var/tmp
+does not exist or has insufficient space.
+.It Pa /usr/tmp
+Last choice if
+.Pa /var/tmp
+and
+.Pa /tmp
+are not suitable for creating the staging area.
+.It Pa /var/db/pkg
+Default location of the installed package database.
+.El
+.Sh SEE ALSO
+.Xr pkg_create 1 ,
+.Xr pkg_delete 1 ,
+.Xr pkg_info 1 ,
+.Xr pkg_version 1 ,
+.Xr mktemp 3 ,
+.Xr sysconf 3 ,
+.Xr mtree 8
+.Sh AUTHORS
+.An Jordan Hubbard
+.Sh CONTRIBUTORS
+.An John Kohl Aq jtk@rational.com
+.Sh BUGS
+Hard links between files in a distribution are only preserved if either
+(1) the staging area is on the same file system as the target directory of
+all the links to the file, or (2) all the links to the file are bracketed by
+.Cm @cwd
+directives in the contents file,
+.Em and
+the link names are extracted with a single
+.Cm tar
+command (not split between
+invocations due to exec argument-space limitations--this depends on the
+value returned by
+.Fn sysconf _SC_ARG_MAX ) .
+.Pp
+Sure to be others.
diff --git a/usr.sbin/pkg_install/create/Makefile b/usr.sbin/pkg_install/create/Makefile
new file mode 100644
index 0000000..42718c6
--- /dev/null
+++ b/usr.sbin/pkg_install/create/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+PROG= pkg_create
+SRCS= main.c perform.c pl.c
+
+CFLAGS+= -I${.CURDIR}/../lib
+
+WARNS?= 3
+WFORMAT?= 1
+
+DPADD= ${LIBINSTALL} ${LIBMD}
+LDADD= ${LIBINSTALL} -lmd
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pkg_install/create/create.h b/usr.sbin/pkg_install/create/create.h
new file mode 100644
index 0000000..310aba0
--- /dev/null
+++ b/usr.sbin/pkg_install/create/create.h
@@ -0,0 +1,58 @@
+/* $FreeBSD$ */
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Include and define various things wanted by the create command.
+ *
+ */
+
+#ifndef _INST_CREATE_H_INCLUDE
+#define _INST_CREATE_H_INCLUDE
+
+extern match_t MatchType;
+extern char *Prefix;
+extern char *Comment;
+extern char *Desc;
+extern char *Display;
+extern char *Install;
+extern char *PostInstall;
+extern char *DeInstall;
+extern char *PostDeInstall;
+extern char *Contents;
+extern char *Require;
+extern char *SrcDir;
+extern char *BaseDir;
+extern char *ExcludeFrom;
+extern char *Mtree;
+extern char *Pkgdeps;
+extern char *Conflicts;
+extern char *Origin;
+extern char *InstalledPkg;
+extern char PlayPen[];
+extern int Dereference;
+extern int PlistOnly;
+extern int Recursive;
+extern int Regenerate;
+
+enum zipper {NONE, GZIP, BZIP, BZIP2 };
+extern enum zipper Zipper;
+
+void add_cksum(Package *, PackingList, const char *);
+void check_list(const char *, Package *);
+void copy_plist(const char *, Package *);
+
+#endif /* _INST_CREATE_H_INCLUDE */
diff --git a/usr.sbin/pkg_install/create/main.c b/usr.sbin/pkg_install/create/main.c
new file mode 100644
index 0000000..b98b21e
--- /dev/null
+++ b/usr.sbin/pkg_install/create/main.c
@@ -0,0 +1,263 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the create module.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <getopt.h>
+#include <err.h>
+
+#include "lib.h"
+#include "create.h"
+
+match_t MatchType = MATCH_GLOB;
+char *Prefix = NULL;
+char *Comment = NULL;
+char *Desc = NULL;
+char *SrcDir = NULL;
+char *BaseDir = NULL;
+char *Display = NULL;
+char *Install = NULL;
+char *PostInstall = NULL;
+char *DeInstall = NULL;
+char *PostDeInstall = NULL;
+char *Contents = NULL;
+char *Require = NULL;
+char *ExcludeFrom = NULL;
+char *Mtree = NULL;
+char *Pkgdeps = NULL;
+char *Conflicts = NULL;
+char *Origin = NULL;
+char *InstalledPkg = NULL;
+char PlayPen[FILENAME_MAX];
+int Dereference = FALSE;
+int PlistOnly = FALSE;
+int Recursive = FALSE;
+int Regenerate = TRUE;
+int Help = FALSE;
+enum zipper Zipper = BZIP2;
+
+
+static void usage(void);
+
+static char opts[] = "EGYNnORhjvxyzf:p:P:C:c:d:i:I:k:K:r:t:X:D:m:s:S:o:b:";
+static struct option longopts[] = {
+ { "backup", required_argument, NULL, 'b' },
+ { "extended", no_argument, NULL, 'E' },
+ { "help", no_argument, &Help, TRUE },
+ { "no", no_argument, NULL, 'N' },
+ { "no-glob", no_argument, NULL, 'G' },
+ { "origin", required_argument, NULL, 'o' },
+ { "plist-only", no_argument, NULL, 'O' },
+ { "prefix", required_argument, NULL, 'p' },
+ { "recursive", no_argument, NULL, 'R' },
+ { "regex", no_argument, NULL, 'x' },
+ { "template", required_argument, NULL, 't' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "yes", no_argument, NULL, 'Y' },
+ { NULL, 0, NULL, 0 },
+};
+
+int
+main(int argc, char **argv)
+{
+ int ch;
+ char **pkgs, **start, *tmp;
+
+ pkgs = start = argv;
+ while ((ch = getopt_long(argc, argv, opts, longopts, NULL)) != -1)
+ switch(ch) {
+ case 'v':
+ Verbose++;
+ break;
+
+ case 'x':
+ MatchType = MATCH_REGEX;
+ break;
+
+ case 'E':
+ MatchType = MATCH_EREGEX;
+ break;
+
+ case 'G':
+ MatchType = MATCH_EXACT;
+ break;
+
+ case 'N':
+ AutoAnswer = NO;
+ break;
+
+ case 'Y':
+ AutoAnswer = YES;
+ break;
+
+ case 'O':
+ PlistOnly = TRUE;
+ break;
+
+ case 'p':
+ Prefix = optarg;
+ break;
+
+ case 's':
+ SrcDir = optarg;
+ break;
+
+ case 'S':
+ BaseDir = optarg;
+ break;
+
+ case 'f':
+ Contents = optarg;
+ break;
+
+ case 'C':
+ Conflicts = optarg;
+ break;
+
+ case 'c':
+ Comment = optarg;
+ break;
+
+ case 'd':
+ Desc = optarg;
+ break;
+
+ case 'i':
+ Install = optarg;
+ break;
+
+ case 'I':
+ PostInstall = optarg;
+ break;
+
+ case 'k':
+ DeInstall = optarg;
+ break;
+
+ case 'K':
+ PostDeInstall = optarg;
+ break;
+
+ case 'r':
+ Require = optarg;
+ break;
+
+ case 't':
+ strlcpy(PlayPen, optarg, sizeof(PlayPen));
+ break;
+
+ case 'X':
+ ExcludeFrom = optarg;
+ break;
+
+ case 'h':
+ Dereference = TRUE;
+ break;
+
+ case 'D':
+ Display = optarg;
+ break;
+
+ case 'm':
+ Mtree = optarg;
+ break;
+
+ case 'P':
+ Pkgdeps = optarg;
+ break;
+
+ case 'o':
+ Origin = optarg;
+ break;
+
+ case 'y':
+ case 'j':
+ Zipper = BZIP2;
+ break;
+
+ case 'z':
+ Zipper = GZIP;
+ break;
+
+ case 'b':
+ InstalledPkg = optarg;
+ while ((tmp = strrchr(optarg, (int)'/')) != NULL) {
+ *tmp++ = '\0';
+ /*
+ * If character after the '/' is alphanumeric, then we've
+ * found the package name. Otherwise we've come across
+ * a trailing '/' and need to continue our quest.
+ */
+ if (isalpha(*tmp)) {
+ InstalledPkg = tmp;
+ break;
+ }
+ }
+ break;
+
+ case 'R':
+ Recursive = TRUE;
+ break;
+
+ case 'n':
+ Regenerate = FALSE;
+ break;
+
+ case 0:
+ if (Help)
+ usage();
+ break;
+
+ default:
+ usage();
+ break;
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* Get all the remaining package names, if any */
+ while (*argv)
+ *pkgs++ = *argv++;
+
+ /* If no packages, yelp */
+ if ((pkgs == start) && (InstalledPkg == NULL))
+ warnx("missing package name"), usage();
+ *pkgs = NULL;
+ if ((start[0] != NULL) && (start[1] != NULL)) {
+ warnx("only one package name allowed ('%s' extraneous)", start[1]);
+ usage();
+ }
+ if (start[0] == NULL)
+ start[0] = InstalledPkg;
+ if (!pkg_perform(start)) {
+ if (Verbose)
+ warnx("package creation failed");
+ return 1;
+ }
+ else
+ return 0;
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
+"usage: pkg_create [-YNOhjnvyz] [-C conflicts] [-P pkgs] [-p prefix]",
+" [-i iscript] [-I piscript] [-k dscript] [-K pdscript]",
+" [-r rscript] [-s srcdir] [-S basedir]",
+" [-t template] [-X excludefile]",
+" [-D displayfile] [-m mtreefile] [-o originpath]",
+" -c comment -d description -f packlist pkg-filename",
+" pkg_create [-EGYNRhnvxy] -b pkg-name [pkg-filename]");
+ exit(1);
+}
diff --git a/usr.sbin/pkg_install/create/perform.c b/usr.sbin/pkg_install/create/perform.c
new file mode 100644
index 0000000..505826e
--- /dev/null
+++ b/usr.sbin/pkg_install/create/perform.c
@@ -0,0 +1,588 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the main body of the create module.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include "create.h"
+
+#include <err.h>
+#include <libgen.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/syslimits.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+static void sanity_check(void);
+static void make_dist(const char *, const char *, const char *, Package *);
+static int create_from_installed_recursive(const char *, const char *);
+static int create_from_installed(const char *, const char *, const char *);
+
+static char *home;
+
+int
+pkg_perform(char **pkgs)
+{
+ char *pkg = *pkgs; /* Only one arg to create */
+ char *cp;
+ FILE *pkg_in, *fp;
+ Package plist;
+ int len;
+ const char *suf;
+
+ /* Preliminary setup */
+ if (InstalledPkg == NULL)
+ sanity_check();
+ if (Verbose && !PlistOnly)
+ printf("Creating package %s\n", pkg);
+
+ /* chop suffix off if already specified, remembering if we want to compress */
+ len = strlen(pkg);
+ if (len > 4) {
+ if (!strcmp(&pkg[len - 4], ".tbz")) {
+ Zipper = BZIP2;
+ pkg[len - 4] = '\0';
+ }
+ else if (!strcmp(&pkg[len - 4], ".tgz")) {
+ Zipper = GZIP;
+ pkg[len - 4] = '\0';
+ }
+ else if (!strcmp(&pkg[len - 4], ".tar")) {
+ Zipper = NONE;
+ pkg[len - 4] = '\0';
+ }
+ }
+ if (Zipper == BZIP2) {
+ suf = "tbz";
+ setenv("BZIP2", "--best", 0);
+ } else if (Zipper == GZIP) {
+ suf = "tgz";
+ setenv("GZIP", "-9", 0);
+ } else
+ suf = "tar";
+
+ if (InstalledPkg != NULL) {
+ char *pkgglob[] = { InstalledPkg, NULL };
+ char **matched, **pkgs;
+ int i, error;
+
+ pkgs = pkgglob;
+ if (MatchType != MATCH_EXACT) {
+ matched = matchinstalled(MatchType, pkgs, &error);
+ if (!error && matched != NULL)
+ pkgs = matched;
+ else if (MatchType != MATCH_GLOB)
+ errx(1, "no packages match pattern");
+ }
+ /*
+ * Is there is only one installed package matching the pattern,
+ * we need to respect the optional pkg-filename parameter. If,
+ * however, the pattern matches several packages, this parameter
+ * makes no sense and is ignored.
+ */
+ if (pkgs[1] == NULL) {
+ if (pkg == InstalledPkg)
+ pkg = *pkgs;
+ InstalledPkg = *pkgs;
+ if (!Recursive)
+ return (create_from_installed(InstalledPkg, pkg, suf));
+ return (create_from_installed_recursive(pkg, suf));
+ }
+ for (i = 0; pkgs[i] != NULL; i++) {
+ InstalledPkg = pkg = pkgs[i];
+ if (!Recursive)
+ create_from_installed(pkg, pkg, suf);
+ else
+ create_from_installed_recursive(pkg, suf);
+ }
+ return TRUE;
+ }
+
+ get_dash_string(&Comment);
+ get_dash_string(&Desc);
+ if (!strcmp(Contents, "-"))
+ pkg_in = stdin;
+ else {
+ pkg_in = fopen(Contents, "r");
+ if (!pkg_in) {
+ cleanup(0);
+ errx(2, "%s: unable to open contents file '%s' for input",
+ __func__, Contents);
+ }
+ }
+ plist.head = plist.tail = NULL;
+
+ /* Stick the dependencies, if any, at the top */
+ if (Pkgdeps) {
+ char **deps, *deporigin;
+ int i;
+ int ndeps = 0;
+
+ if (Verbose && !PlistOnly)
+ printf("Registering depends:");
+
+ /* Count number of dependencies */
+ for (cp = Pkgdeps; cp != NULL && *cp != '\0';
+ cp = strpbrk(++cp, " \t\n")) {
+ ndeps++;
+ }
+
+ if (ndeps != 0) {
+ /* Create easy to use NULL-terminated list */
+ deps = alloca(sizeof(*deps) * ndeps + 1);
+ if (deps == NULL) {
+ errx(2, "%s: alloca() failed", __func__);
+ /* Not reached */
+ }
+ for (i = 0; Pkgdeps;) {
+ cp = strsep(&Pkgdeps, " \t\n");
+ if (*cp) {
+ deps[i] = cp;
+ i++;
+ }
+ }
+ ndeps = i;
+ deps[ndeps] = NULL;
+
+ sortdeps(deps);
+ for (i = 0; i < ndeps; i++) {
+ deporigin = strchr(deps[i], ':');
+ if (deporigin != NULL) {
+ *deporigin = '\0';
+ add_plist_top(&plist, PLIST_DEPORIGIN, ++deporigin);
+ }
+ add_plist_top(&plist, PLIST_PKGDEP, deps[i]);
+ if (Verbose && !PlistOnly)
+ printf(" %s", deps[i]);
+ }
+ }
+
+ if (Verbose && !PlistOnly)
+ printf(".\n");
+ }
+
+ /* Put the conflicts directly after the dependencies, if any */
+ if (Conflicts) {
+ if (Verbose && !PlistOnly)
+ printf("Registering conflicts:");
+ while (Conflicts) {
+ cp = strsep(&Conflicts, " \t\n");
+ if (*cp) {
+ add_plist(&plist, PLIST_CONFLICTS, cp);
+ if (Verbose && !PlistOnly)
+ printf(" %s", cp);
+ }
+ }
+ if (Verbose && !PlistOnly)
+ printf(".\n");
+ }
+
+ /* If a SrcDir override is set, add it now */
+ if (SrcDir) {
+ if (Verbose && !PlistOnly)
+ printf("Using SrcDir value of %s\n", SrcDir);
+ add_plist(&plist, PLIST_SRC, SrcDir);
+ }
+
+ /* Slurp in the packing list */
+ read_plist(&plist, pkg_in);
+
+ /* Prefix should add an @cwd to the packing list */
+ if (Prefix)
+ add_plist_top(&plist, PLIST_CWD, Prefix);
+
+ /* Add the origin if asked, at the top */
+ if (Origin)
+ add_plist_top(&plist, PLIST_ORIGIN, Origin);
+
+ /*
+ * Run down the list and see if we've named it, if not stick in a name
+ * at the top.
+ */
+ if (find_plist(&plist, PLIST_NAME) == NULL)
+ add_plist_top(&plist, PLIST_NAME, basename(pkg));
+
+ if (asprintf(&cp, "PKG_FORMAT_REVISION:%d.%d", PLIST_FMT_VER_MAJOR,
+ PLIST_FMT_VER_MINOR) == -1) {
+ errx(2, "%s: asprintf() failed", __func__);
+ }
+ add_plist_top(&plist, PLIST_COMMENT, cp);
+ free(cp);
+
+ /*
+ * We're just here for to dump out a revised plist for the FreeBSD ports
+ * hack. It's not a real create in progress.
+ */
+ if (PlistOnly) {
+ check_list(home, &plist);
+ write_plist(&plist, stdout);
+ exit(0);
+ }
+
+ /* Make a directory to stomp around in */
+ home = make_playpen(PlayPen, 0);
+ signal(SIGINT, cleanup);
+ signal(SIGHUP, cleanup);
+
+ /* Make first "real contents" pass over it */
+ check_list(home, &plist);
+ (void) umask(022); /*
+ * Make sure gen'ed directories, files don't have
+ * group or other write bits.
+ */
+ /* copy_plist(home, &plist); */
+ /* mark_plist(&plist); */
+
+ /* Now put the release specific items in */
+ add_plist(&plist, PLIST_CWD, ".");
+ write_file(COMMENT_FNAME, Comment);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, COMMENT_FNAME);
+ add_cksum(&plist, plist.tail, COMMENT_FNAME);
+ write_file(DESC_FNAME, Desc);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, DESC_FNAME);
+ add_cksum(&plist, plist.tail, DESC_FNAME);
+
+ if (Install) {
+ copy_file(home, Install, INSTALL_FNAME);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, INSTALL_FNAME);
+ add_cksum(&plist, plist.tail, INSTALL_FNAME);
+ }
+ if (PostInstall) {
+ copy_file(home, PostInstall, POST_INSTALL_FNAME);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, POST_INSTALL_FNAME);
+ add_cksum(&plist, plist.tail, POST_INSTALL_FNAME);
+ }
+ if (DeInstall) {
+ copy_file(home, DeInstall, DEINSTALL_FNAME);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, DEINSTALL_FNAME);
+ add_cksum(&plist, plist.tail, DEINSTALL_FNAME);
+ }
+ if (PostDeInstall) {
+ copy_file(home, PostDeInstall, POST_DEINSTALL_FNAME);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, POST_DEINSTALL_FNAME);
+ add_cksum(&plist, plist.tail, POST_DEINSTALL_FNAME);
+ }
+ if (Require) {
+ copy_file(home, Require, REQUIRE_FNAME);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, REQUIRE_FNAME);
+ add_cksum(&plist, plist.tail, REQUIRE_FNAME);
+ }
+ if (Display) {
+ copy_file(home, Display, DISPLAY_FNAME);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, DISPLAY_FNAME);
+ add_cksum(&plist, plist.tail, DISPLAY_FNAME);
+ add_plist(&plist, PLIST_DISPLAY, DISPLAY_FNAME);
+ }
+ if (Mtree) {
+ copy_file(home, Mtree, MTREE_FNAME);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, MTREE_FNAME);
+ add_cksum(&plist, plist.tail, MTREE_FNAME);
+ add_plist(&plist, PLIST_MTREE, MTREE_FNAME);
+ }
+
+ /* Finally, write out the packing list */
+ fp = fopen(CONTENTS_FNAME, "w");
+ if (!fp) {
+ cleanup(0);
+ errx(2, "%s: can't open file %s for writing",
+ __func__, CONTENTS_FNAME);
+ }
+ write_plist(&plist, fp);
+ if (fclose(fp)) {
+ cleanup(0);
+ errx(2, "%s: error while closing %s",
+ __func__, CONTENTS_FNAME);
+ }
+
+ /* And stick it into a tar ball */
+ make_dist(home, pkg, suf, &plist);
+
+ /* Cleanup */
+ free(Comment);
+ free(Desc);
+ free_plist(&plist);
+ leave_playpen();
+ return TRUE; /* Success */
+}
+
+static void
+make_dist(const char *homedir, const char *pkg, const char *suff, Package *plist)
+{
+ struct stat sb;
+ char tball[FILENAME_MAX];
+ PackingList p;
+ int ret;
+ const char *args[50]; /* Much more than enough. */
+ int nargs = 0;
+ int pipefds[2];
+ FILE *totar;
+ pid_t pid;
+ const char *cname;
+ char *prefix = NULL;
+
+
+ args[nargs++] = "tar"; /* argv[0] */
+
+ if (*pkg == '/')
+ snprintf(tball, FILENAME_MAX, "%s.%s", pkg, suff);
+ else
+ snprintf(tball, FILENAME_MAX, "%s/%s.%s", homedir, pkg, suff);
+
+ /*
+ * If the package tarball exists already, and we are running in `no
+ * clobber' mode, skip this package.
+ */
+ if (stat(tball, &sb) == 0 && Regenerate == FALSE) {
+ if (Verbose)
+ printf("Skipping package '%s'. It already exists.\n", tball);
+ return;
+ }
+
+ args[nargs++] = "-c";
+ args[nargs++] = "-f";
+ args[nargs++] = tball;
+ if (strchr(suff, 'z')) { /* Compress/gzip/bzip2? */
+ if (Zipper == BZIP2) {
+ args[nargs++] = "-j";
+ cname = "bzip'd ";
+ }
+ else {
+ args[nargs++] = "-z";
+ cname = "gzip'd ";
+ }
+ } else {
+ cname = "";
+ }
+ if (Dereference)
+ args[nargs++] = "-h";
+ if (ExcludeFrom) {
+ args[nargs++] = "-X";
+ args[nargs++] = ExcludeFrom;
+ }
+ args[nargs++] = "-T"; /* Take filenames from file instead of args. */
+ args[nargs++] = "-"; /* Use stdin for the file. */
+ args[nargs] = NULL;
+
+ if (Verbose)
+ printf("Creating %star ball in '%s'\n", cname, tball);
+
+ /* Set up a pipe for passing the filenames, and fork off a tar process. */
+ if (pipe(pipefds) == -1) {
+ cleanup(0);
+ errx(2, "%s: cannot create pipe", __func__);
+ }
+ if ((pid = fork()) == -1) {
+ cleanup(0);
+ errx(2, "%s: cannot fork process for tar", __func__);
+ }
+ if (pid == 0) { /* The child */
+ dup2(pipefds[0], 0);
+ close(pipefds[0]);
+ close(pipefds[1]);
+ execv("/usr/bin/tar", (char * const *)(uintptr_t)args);
+ cleanup(0);
+ errx(2, "%s: failed to execute tar command", __func__);
+ }
+
+ /* Meanwhile, back in the parent process ... */
+ close(pipefds[0]);
+ if ((totar = fdopen(pipefds[1], "w")) == NULL) {
+ cleanup(0);
+ errx(2, "%s: fdopen failed", __func__);
+ }
+
+ fprintf(totar, "%s\n", CONTENTS_FNAME);
+ fprintf(totar, "%s\n", COMMENT_FNAME);
+ fprintf(totar, "%s\n", DESC_FNAME);
+
+ if (Install)
+ fprintf(totar, "%s\n", INSTALL_FNAME);
+ if (PostInstall)
+ fprintf(totar, "%s\n", POST_INSTALL_FNAME);
+ if (DeInstall)
+ fprintf(totar, "%s\n", DEINSTALL_FNAME);
+ if (PostDeInstall)
+ fprintf(totar, "%s\n", POST_DEINSTALL_FNAME);
+ if (Require)
+ fprintf(totar, "%s\n", REQUIRE_FNAME);
+ if (Display)
+ fprintf(totar, "%s\n", DISPLAY_FNAME);
+ if (Mtree)
+ fprintf(totar, "%s\n", MTREE_FNAME);
+
+ for (p = plist->head; p; p = p->next) {
+ if (p->type == PLIST_FILE)
+ fprintf(totar, "%s\n", p->name);
+ else if (p->type == PLIST_CWD && p->name == NULL)
+ fprintf(totar, "-C\n%s\n", prefix);
+ else if (p->type == PLIST_CWD && BaseDir && p->name && p->name[0] == '/')
+ fprintf(totar, "-C\n%s%s\n", BaseDir, p->name);
+ else if (p->type == PLIST_CWD || p->type == PLIST_SRC)
+ fprintf(totar, "-C\n%s\n", p->name);
+ else if (p->type == PLIST_IGNORE)
+ p = p->next;
+ if (p->type == PLIST_CWD && !prefix)
+ prefix = p->name;
+
+ }
+
+ fclose(totar);
+ wait(&ret);
+ /* assume either signal or bad exit is enough for us */
+ if (ret) {
+ cleanup(0);
+ errx(2, "%s: tar command failed with code %d", __func__, ret);
+ }
+}
+
+static void
+sanity_check()
+{
+ if (!Comment) {
+ cleanup(0);
+ errx(2, "%s: required package comment string is missing (-c comment)",
+ __func__);
+ }
+ if (!Desc) {
+ cleanup(0);
+ errx(2, "%s: required package description string is missing (-d desc)",
+ __func__);
+ }
+ if (!Contents) {
+ cleanup(0);
+ errx(2, "%s: required package contents list is missing (-f [-]file)",
+ __func__);
+ }
+}
+
+
+/* Clean up those things that would otherwise hang around */
+void
+cleanup(int sig)
+{
+ int in_cleanup = 0;
+
+ if (!in_cleanup) {
+ in_cleanup = 1;
+ leave_playpen();
+ }
+ if (sig)
+ exit(1);
+}
+
+static int
+create_from_installed_recursive(const char *pkg, const char *suf)
+{
+ FILE *fp;
+ Package plist;
+ PackingList p;
+ char tmp[PATH_MAX];
+ int rval;
+
+ if (!create_from_installed(InstalledPkg, pkg, suf))
+ return FALSE;
+ snprintf(tmp, sizeof(tmp), "%s/%s/%s", LOG_DIR, InstalledPkg, CONTENTS_FNAME);
+ if (!fexists(tmp)) {
+ warnx("can't find package '%s' installed!", InstalledPkg);
+ return FALSE;
+ }
+ /* Suck in the contents list */
+ plist.head = plist.tail = NULL;
+ fp = fopen(tmp, "r");
+ if (!fp) {
+ warnx("unable to open %s file", tmp);
+ return FALSE;
+ }
+ read_plist(&plist, fp);
+ fclose(fp);
+ rval = TRUE;
+ for (p = plist.head; p ; p = p->next) {
+ if (p->type != PLIST_PKGDEP)
+ continue;
+ if (Verbose)
+ printf("Creating package %s\n", p->name);
+ if (!create_from_installed(p->name, p->name, suf)) {
+ rval = FALSE;
+ break;
+ }
+ }
+ free_plist(&plist);
+ return rval;
+}
+
+static int
+create_from_installed(const char *ipkg, const char *pkg, const char *suf)
+{
+ FILE *fp;
+ Package plist;
+ char homedir[MAXPATHLEN], log_dir[FILENAME_MAX];
+
+ snprintf(log_dir, sizeof(log_dir), "%s/%s", LOG_DIR, ipkg);
+ if (!fexists(log_dir)) {
+ warnx("can't find package '%s' installed!", ipkg);
+ return FALSE;
+ }
+ getcwd(homedir, sizeof(homedir));
+ if (chdir(log_dir) == FAIL) {
+ warnx("can't change directory to '%s'!", log_dir);
+ return FALSE;
+ }
+ /* Suck in the contents list */
+ plist.head = plist.tail = NULL;
+ fp = fopen(CONTENTS_FNAME, "r");
+ if (!fp) {
+ warnx("unable to open %s file", CONTENTS_FNAME);
+ return FALSE;
+ }
+ read_plist(&plist, fp);
+ fclose(fp);
+
+ Install = isfile(INSTALL_FNAME) ? (char *)INSTALL_FNAME : NULL;
+ PostInstall = isfile(POST_INSTALL_FNAME) ?
+ (char *)POST_INSTALL_FNAME : NULL;
+ DeInstall = isfile(DEINSTALL_FNAME) ? (char *)DEINSTALL_FNAME : NULL;
+ PostDeInstall = isfile(POST_DEINSTALL_FNAME) ?
+ (char *)POST_DEINSTALL_FNAME : NULL;
+ Require = isfile(REQUIRE_FNAME) ? (char *)REQUIRE_FNAME : NULL;
+ Display = isfile(DISPLAY_FNAME) ? (char *)DISPLAY_FNAME : NULL;
+ Mtree = isfile(MTREE_FNAME) ? (char *)MTREE_FNAME : NULL;
+
+ make_dist(homedir, pkg, suf, &plist);
+
+ free_plist(&plist);
+ if (chdir(homedir) == FAIL) {
+ warnx("can't change directory to '%s'!", homedir);
+ return FALSE;
+ }
+ return TRUE;
+}
diff --git a/usr.sbin/pkg_install/create/pkg_create.1 b/usr.sbin/pkg_install/create/pkg_create.1
new file mode 100644
index 0000000..a567fc4
--- /dev/null
+++ b/usr.sbin/pkg_install/create/pkg_create.1
@@ -0,0 +1,652 @@
+.\"
+.\" FreeBSD install - a package for the installation and maintainance
+.\" of non-core utilities.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" Jordan K. Hubbard
+.\"
+.\"
+.\" @(#)pkg_create.1
+.\" $FreeBSD$
+.\"
+.\" hacked up by John Kohl for NetBSD--fixed a few bugs, extended keywords,
+.\" added dependency tracking, etc.
+.\"
+.\" [jkh] Took John's changes back and made some additional extensions for
+.\" better integration with FreeBSD's new ports collection.
+.\"
+.Dd May 30, 2008
+.Dt PKG_CREATE 1
+.Os
+.Sh NAME
+.Nm pkg_create
+.Nd a utility for creating software package distributions
+.Sh SYNOPSIS
+.Nm
+.Op Fl YNOhjnvyz
+.Op Fl C Ar conflicts
+.Op Fl P Ar pkgs
+.Op Fl p Ar prefix
+.Op Fl i Ar iscript
+.Op Fl I Ar piscript
+.Op Fl k Ar dscript
+.Op Fl K Ar pdscript
+.Op Fl r Ar rscript
+.Op Fl s Ar srcdir
+.Op Fl S Ar basedir
+.Op Fl t Ar template
+.Op Fl X Ar excludefile
+.Op Fl D Ar displayfile
+.Op Fl m Ar mtreefile
+.Op Fl o Ar originpath
+.Fl c Ar comment
+.Fl d Ar description
+.Fl f Ar packlist
+.Ar pkg-filename
+.Nm
+.Op Fl EGYNRhnvxy
+.Fl b Ar pkg-name
+.Op Ar pkg-filename
+.Sh DESCRIPTION
+The
+.Nm
+command is used to create packages that will subsequently be fed to
+one of the package extraction/info utilities.
+The input description
+and command line arguments for the creation of a package are not
+meant to be human-generated, though it is easy enough to do so.
+It is more expected that you will use a front-end tool for
+the job rather than muddling through it yourself.
+Nonetheless, a short
+description of the input syntax is included in this document.
+.Sh OPTIONS
+The following command line options are supported:
+.Bl -tag -width indent
+.It Fl f Ar packinglist
+Fetch
+.Dq packing list
+for package from the file
+.Ar packinglist
+or
+.Cm stdin
+if
+.Ar packinglist
+is a
+.Cm -
+(dash).
+.It Fl c Xo
+.Oo Fl Oc Ns Ar desc
+.Xc
+Fetch package
+.Dq one line description
+from file
+.Ar desc
+or, if preceded by
+.Cm - ,
+the argument itself.
+This string should also
+give some idea of which version of the product (if any) the package
+represents.
+.It Fl d Xo
+.Oo Fl Oc Ns Ar desc
+.Xc
+Fetch long description for package from file
+.Ar desc
+or, if preceded by
+.Cm - ,
+the argument itself.
+.It Fl Y , -yes
+Assume a default answer of `Yes' for any questions asked.
+.It Fl N , -no
+Assume a default answer of `No' for any questions asked.
+.It Fl O , -plist-only
+Go into a `packing list Only' mode.
+This is a custom hack for the
+.Fx
+.Em "Ports Collection"
+and is used to do `fake pkg_add' operations when a port is installed.
+In such cases, it is necessary to know what the final, adjusted packing
+list will look like.
+.It Fl v , -verbose
+Turn on verbose output.
+.It Fl h
+Force tar to follow symbolic links, so that the files they point to
+are dumped, rather than the links themselves.
+.It Fl i Ar iscript
+Set
+.Ar iscript
+to be the pre-install procedure for the package.
+This can be any executable
+program (or shell script).
+It will be invoked automatically when the
+package is later installed.
+It will be passed the package's name as the
+first argument.
+.Pp
+.Sy Note :
+if the
+.Fl I
+option is not given, this script will serve as both the pre-install and the
+post-install script for the package, differentiating between the
+functionality by passing the keywords
+.Ar PRE-INSTALL
+and
+.Ar POST-INSTALL
+respectively, after the package's name.
+.It Fl I Ar piscript
+Set
+.Ar piscript
+to be the post-install procedure for the package.
+This can be any
+executable program (or shell script).
+It will be invoked automatically
+when the package is later installed.
+It will be passed the package's name as
+the first argument.
+.It Fl C Ar conflicts
+Set the initial package conflict list to
+.Ar conflicts .
+This is assumed to be a whitespace separated list of package names
+and is meant as a convenient shorthand for specifying multiple
+.Cm @conflicts
+directives in the packing list (see PACKING LIST DETAILS section below).
+.It Fl P Ar pkgs
+Set the initial package dependency list to
+.Ar pkgs .
+This is assumed to be a whitespace separated list of package names
+and is meant as a convenient shorthand for specifying multiple
+.Cm @pkgdep
+directives in the packing list (see
+.Sx "PACKING LIST DETAILS"
+section below).
+Each argument from the
+.Ar pkgs
+list could be in the form
+.Ar pkgname Ns Op : Ns Ar pkgorigin ,
+where optional
+.Ar pkgorigin
+element denotes origin of each dependency from the list and it is
+recorded into the packing list along with the
+.Ar pkgname
+using
+.Cm @comment
+directive.
+.It Fl p , -prefix Ar prefix
+Set
+.Ar prefix
+as the initial directory
+.Dq base
+to start from in selecting files for
+the package.
+.It Fl k Ar dscript
+Set
+.Ar dscript
+to be the de-install procedure for the package.
+This can be any executable
+program (or shell script).
+It will be invoked automatically when the
+package is later (if ever) de-installed.
+It will be passed the package's
+name as the first argument.
+.Pp
+.Sy Note :
+if the
+.Fl K
+option is not given, this script will serve as both the de-install and the
+post-deinstall script for the package, differentiating between the
+functionality by passing the keywords
+.Ar DEINSTALL
+and
+.Ar POST-DEINSTALL
+respectively, along with the package's name.
+.It Fl K Ar pdscript
+Set
+.Ar pdscript
+to be the post-deinstall procedure for the package.
+This can be any
+executable program (or shell script).
+It will be invoked automatically when
+the package is later de-installed.
+It will be passed the package's name as
+the first argument.
+.It Fl r Ar rscript
+Set
+.Ar rscript
+to be the
+.Dq requirements
+procedure for the package.
+This can be any
+executable program (or shell script).
+It will be invoked automatically
+at installation/deinstallation time to determine whether or not
+installation/deinstallation should proceed.
+To differentiate between installation and deinstallation, the keywords
+.Ar INSTALL
+and
+.Ar DEINSTALL
+are passed respectively, along with the package's name.
+.It Fl s Ar srcdir
+.Ar srcdir
+will override the value of
+.Cm @cwd
+during package creation.
+.It Fl S Ar basedir
+.Ar basedir
+will be prefixed to all
+.Cm @cwd
+during package creation.
+.It Fl t , -template Ar template
+Use
+.Ar template
+as the input to
+.Xr mktemp 3 .
+By default, this is the string
+.Pa /tmp/instmp.XXXXXX ,
+but it may be necessary to override it in the situation where
+space in your
+.Pa /tmp
+directory is limited.
+Be sure to leave some number of `X' characters
+for
+.Xr mktemp 3
+to fill in with a unique ID.
+.It Fl X Ar excludefile
+Pass
+.Ar excludefile
+as a
+.Fl exclude-from
+argument to
+.Cm tar
+when creating final package.
+See
+.Cm tar
+man page (or run
+.Cm tar
+with
+.Fl -help
+flag) for further information on using this flag.
+.It Fl D Ar displayfile
+Display the file (by concatenating it to stdout)
+after installing the package.
+Useful for things like
+legal notices on almost-free software, etc.
+.It Fl m Ar mtreefile
+Run
+.Xr mtree 8
+with input from mtreefile before the package is installed.
+Mtree is invoked as
+.Cm mtree
+.Fl u
+.Fl f
+.Ar mtreefile
+.Fl d
+.Fl e
+.Fl p
+.Pa prefix ,
+where
+.Pa prefix
+is the name of the first directory named by a
+.Cm @cwd
+directive.
+.It Fl o , -origin Ar originpath
+Record an
+.Ar originpath ,
+as location of the port from which package has been created in the
+.Fx
+.Em "Ports Collection" .
+It should be in the form
+.Pa MASTERCATEGORY/PORTDIR .
+.It Fl j
+Use
+.Xr bzip2 1
+utility to compress package tarball instead of
+.Xr gzip 1 .
+Please note that this option is a NO-OP if the format of the resulting
+archive is explicitly specified by the recognizable suffix of
+.Ar pkg-filename .
+Currently
+.Nm
+recognizes the following suffixes:
+.Pa .tbz , .tgz
+and
+.Pa .tar .
+.It Fl y
+Compatibility synonym for
+.Fl j .
+.It Fl z
+Use
+.Xr gzip 1
+utility to compress package tarball.
+.It Fl b , -backup Ar pkg-name
+Create package file from a locally installed package named
+.Ar pkg-name .
+If the
+.Ar pkg-filename
+is not specified, then resulting archive will be created in the
+current directory and named
+.Ar pkg-name
+with an appropriate extraction suffix applied.
+.It Fl R , -recursive
+When creating package file from a locally installed package
+also create package files for all packages required by
+.Ar pkg-name .
+Resulting archive(s) will be created in the current directory
+and named using name of the respective package with appropriate
+extraction suffix applied.
+.It Fl x , -regex
+Use basic regular expressions for
+.Ar pkg-name .
+.It Fl E , -extended
+Use extended (modern) regular expressions for
+.Ar pkg-name .
+.It Fl G , -no-glob
+Use exact matching for
+.Ar pkg-name .
+.It Fl n
+Run in
+.Dq no clobber
+mode.
+If a package tarball exists, the
+.Nm
+utility will not overwrite it.
+This is useful, for example, when multiple packages are saved with
+several consecutive runs of
+.Nm
+with the
+.Fl Rb
+options.
+Saving common dependencies multiple times would do a lot of duplicate
+work in this case.
+The
+.Fl n
+option avoids repackaging common dependencies multiple times.
+.El
+.Sh PACKING LIST DETAILS
+The
+.Dq packing list
+format (see
+.Fl f )
+is fairly simple, being
+nothing more than a single column of filenames to include in the
+package.
+However, since absolute pathnames are generally a bad idea
+for a package that could be installed potentially anywhere, there is
+another method of specifying where things are supposed to go
+and, optionally, what ownership and mode information they should be
+installed with.
+This is done by embedding specialized command sequences
+in the packing list.
+Briefly described, these sequences are:
+.Bl -tag -width indent -compact
+.It Cm @cwd Op Ar directory
+Set the internal directory pointer to point to
+.Ar directory .
+All subsequent filenames will be assumed relative to this directory.
+If no
+.Ar directory
+argument is given, it will set the internal directory pointer to the
+first prefix value.
+Note:
+.Cm @cd
+is also an alias for this command.
+.It Cm @srcdir Ar directory
+Set the internal directory pointer for _creation only_ to
+.Ar directory .
+That is to say that it overrides
+.Cm @cwd
+for package creation but not extraction.
+.It Cm @exec Ar command
+Execute
+.Ar command
+as part of the unpacking process.
+If
+.Ar command
+contains any of the following sequences somewhere in it, they will
+be expanded inline.
+For the following examples, assume that
+.Cm @cwd
+is set to
+.Pa /usr/local
+and the last extracted file was
+.Pa bin/emacs .
+.Bl -tag -width indent -compact
+.It Cm "%F"
+Expands to the last filename extracted (as specified), in the example case
+.Pa bin/emacs
+.It Cm "\&%D"
+Expand to the current directory prefix, as set with
+.Cm @cwd ,
+in the example case
+.Pa /usr/local .
+.It Cm "\&%B"
+Expand to the
+.Dq basename
+of the fully qualified filename, that
+is the current directory prefix, plus the last filespec, minus
+the trailing filename.
+In the example case, that would be
+.Pa /usr/local/bin .
+.It Cm "%f"
+Expand to the
+filename
+part of the fully qualified name, or
+the converse of
+.Cm \&%B ,
+being in the example case,
+.Pa emacs .
+.El
+.It Cm @unexec Ar command
+Execute
+.Ar command
+as part of the deinstallation process.
+Expansion of special
+.Cm %
+sequences is the same as for
+.Cm @exec .
+This command is not executed during the package add, as
+.Cm @exec
+is, but rather when the package is deleted.
+This is useful
+for deleting links and other ancillary files that were created
+as a result of adding the package, but not directly known to
+the package's table of contents (and hence not automatically
+removable).
+The advantage of using
+.Cm @unexec
+over a deinstallation script is that you can use the
+.Dq special sequence expansion
+to get at files regardless of where they have
+been potentially redirected (see
+.Fl p ) .
+.It Cm @mode Ar mode
+Set default permission for all subsequently extracted files to
+.Ar mode .
+Format is the same as that used by the
+.Cm chmod
+command (well, considering that it is later handed off to it, that is
+no surprise).
+Use without an arg to set back to default (extraction)
+permissions.
+.It Cm @option Ar option
+Set internal package options, the only two currently supported ones
+being
+.Ar extract-in-place ,
+which tells the pkg_add command not to extract the package's tarball
+into a staging area but rather directly into the target
+hierarchy (this is typically meant to be used only by distributions
+or other special package types), and
+.Ar preserve ,
+which tells pkg_add to move any existing files out of the way,
+preserving the previous contents (which are also resurrected on
+pkg_delete, so caveat emptor).
+.It Cm @owner Ar user
+Set default ownership for all subsequently extracted files to
+.Ar user .
+Use without an arg to set back to default (extraction)
+ownership.
+.It Cm @group Ar group
+Set default group ownership for all subsequently extracted files to
+.Ar group .
+Use without an arg to set back to default (extraction)
+group ownership.
+.It Cm @comment Ar string
+Imbed a comment in the packing list.
+Useful in
+trying to document some particularly hairy sequence that
+may trip someone up later.
+.It Cm @noinst Ar option Ar file
+Specify that the package would have installed
+.Pa file
+if
+.Pa option
+had been specified at build time.
+The action of
+.Cm @noinst
+is the same that
+.Cm @comment
+(which is doing nothing, it is just additional information).
+.It Cm @ignore
+Used internally to tell extraction to ignore the next file (do not
+copy it anywhere), as it is used for some special purpose.
+.It Cm @ignore_inst
+Similar to
+.Cm @ignore ,
+but the ignoring of the next file is delayed one evaluation cycle.
+This
+makes it possible to use this directive in the
+.Ar packinglist
+file, so you can pack a
+specialized datafile in with a distribution for your install script (or
+something) yet have the installer ignore it.
+.It Cm @name Ar name
+Set the name of the package.
+This is mandatory and is usually
+put at the top.
+This name is potentially different from the name of
+the file it came in, and is used when keeping track of the package
+for later deinstallation.
+Note that
+.Nm
+will derive this field from the package name and add it automatically
+if none is given.
+.It Cm @dirrm Ar name
+Declare directory
+.Pa name
+to be deleted at deinstall time.
+By default, directories created by a
+package installation are not deleted when the package is deinstalled;
+this provides an explicit directory cleanup method.
+This directive
+should appear at the end of the package list.
+If more than one
+.Cm @dirrm
+directives are used, the directories are removed in the order specified.
+The
+.Pa name
+directory will not be removed unless it is empty.
+.It Cm @mtree Ar name
+Declare
+.Pa name
+as an
+.Xr mtree 8
+input file to be used at install time (see
+.Fl m
+above).
+Only the first
+.Cm @mtree
+directive is honored.
+.It Cm @display Ar name
+Declare
+.Pa name
+as the file to be displayed at install time (see
+.Fl D
+above).
+.It Cm @pkgdep Ar pkgname
+Declare a dependency on the
+.Ar pkgname
+package.
+The
+.Ar pkgname
+package must be installed before this package may be
+installed, and this package must be deinstalled before the
+.Ar pkgname
+package is deinstalled.
+Multiple
+.Cm @pkgdep
+directives may be used if the package depends on multiple other packages.
+.It Cm @conflicts Ar pkgcflname
+Declare a conflict with the
+.Ar pkgcflname
+package, as the two packages contain references to the same files,
+and so cannot co-exist on the same system.
+.El
+.Sh ENVIRONMENT
+The environment variable
+.Ev PKG_TMPDIR
+names the directory where
+.Nm
+will attempt to create its temporary files.
+If
+.Ev PKG_TMPDIR
+is not set,
+the directory named by the contents of
+.Ev TMPDIR
+will be used.
+If neither of
+.Ev PKG_TMPDIR
+and
+.Ev TMPDIR
+are set, the builtin defaults are used.
+.Sh FILES
+.Bl -tag -width /usr/tmp -compact
+.It Pa /var/tmp
+Temporary directory if environmental variables
+.Ev PKG_TMPDIR
+and
+.Ev TMPDIR
+are not set.
+.It Pa /tmp
+The next choice if
+.Pa /var/tmp
+does not exist.
+.It Pa /usr/tmp
+The last choice if
+.Pa /tmp
+is unsuitable.
+.El
+.Sh SEE ALSO
+.Xr pkg_add 1 ,
+.Xr pkg_delete 1 ,
+.Xr pkg_info 1 ,
+.Xr pkg_version 1 ,
+.Xr sysconf 3
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Fx .
+.Sh AUTHORS
+.An Jordan Hubbard
+.Sh CONTRIBUTORS
+.An John Kohl Aq jtk@rational.com ,
+.An Oliver Eikemeier Aq eik@FreeBSD.org
+.Sh BUGS
+Hard links between files in a distribution must be bracketed by
+.Cm @cwd
+directives in order to be preserved as hard links when the package is
+extracted.
+They additionally must not end up being split between
+.Cm tar
+invocations due to exec argument-space limitations (this depends on the
+value returned by
+.Fn sysconf _SC_ARG_MAX ) .
+.Pp
+Sure to be others.
diff --git a/usr.sbin/pkg_install/create/pl.c b/usr.sbin/pkg_install/create/pl.c
new file mode 100644
index 0000000..18bbaf2
--- /dev/null
+++ b/usr.sbin/pkg_install/create/pl.c
@@ -0,0 +1,280 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Routines for dealing with the packing list.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include "create.h"
+#include <errno.h>
+#include <err.h>
+#include <md5.h>
+
+/* Add an MD5 checksum entry for a file or link */
+void
+add_cksum(Package *pkg, PackingList p, const char *fname)
+{
+ char *cp = NULL, buf[33];
+
+ if (issymlink(fname)) {
+ int len;
+ char lnk[FILENAME_MAX];
+
+ if ((len = readlink(fname, lnk, FILENAME_MAX)) > 0)
+ cp = MD5Data((unsigned char *)lnk, len, buf);
+ } else if (isfile(fname)) {
+ /* Don't record MD5 checksum for device nodes and such */
+ cp = MD5File(fname, buf);
+ }
+
+ if (cp != NULL) {
+ PackingList tmp = new_plist_entry();
+
+ tmp->name = copy_string(strconcat("MD5:", cp));
+ tmp->type = PLIST_COMMENT;
+ tmp->next = p->next;
+ tmp->prev = p;
+ p->next = tmp;
+ if (pkg->tail == p)
+ pkg->tail = tmp;
+ }
+}
+
+/* Check a list for files that require preconversion */
+void
+check_list(const char *home, Package *pkg)
+{
+ const char *where = home;
+ const char *there = NULL;
+ char name[FILENAME_MAX];
+ char *prefix = NULL;
+ PackingList p;
+
+ for (p = pkg->head; p != NULL; p = p->next)
+ switch (p->type) {
+ case PLIST_CWD:
+ if (!prefix)
+ prefix = p->name;
+ where = (p->name == NULL) ? prefix : p->name;
+ break;
+
+ case PLIST_IGNORE:
+ p = p->next;
+ break;
+
+ case PLIST_SRC:
+ there = p->name;
+ break;
+
+ case PLIST_FILE:
+ if (there)
+ snprintf(name, sizeof(name), "%s/%s", there, p->name);
+ else
+ snprintf(name, sizeof(name), "%s%s/%s",
+ BaseDir && where && where[0] == '/' ? BaseDir : "", where, p->name);
+
+ add_cksum(pkg, p, name);
+ break;
+ default:
+ break;
+ }
+}
+
+static int
+trylink(const char *from, const char *to)
+{
+ if (link(from, to) == 0)
+ return 0;
+ if (errno == ENOENT) {
+ /* try making the container directory */
+ char *cp = strrchr(to, '/');
+ if (cp)
+ vsystem("/bin/mkdir -p %.*s", cp - to,
+ to);
+ return link(from, to);
+ }
+ return -1;
+}
+
+#define STARTSTRING "/usr/bin/tar cf -"
+#define TOOBIG(str) (int)strlen(str) + 6 + (int)strlen(home) + where_count > maxargs
+#define PUSHOUT() /* push out string */ \
+ if (where_count > (int)sizeof(STARTSTRING)-1) { \
+ strcat(where_args, "|/usr/bin/tar xpf -"); \
+ if (system(where_args)) { \
+ cleanup(0); \
+ errx(2, "%s: can't invoke tar pipeline", __func__); \
+ } \
+ memset(where_args, 0, maxargs); \
+ last_chdir = NULL; \
+ strcpy(where_args, STARTSTRING); \
+ where_count = sizeof(STARTSTRING)-1; \
+ }
+
+/*
+ * Copy unmarked files in packing list to playpen - marked files
+ * have already been copied in an earlier pass through the list.
+ */
+void
+copy_plist(const char *home, Package *plist)
+{
+ PackingList p = plist->head;
+ const char *where = home;
+ const char *there = NULL, *mythere;
+ char *where_args, *prefix = NULL;
+ const char *last_chdir, *root = "/";
+ int maxargs, where_count = 0, add_count;
+ struct stat stb;
+ dev_t curdir;
+
+ maxargs = sysconf(_SC_ARG_MAX);
+ maxargs -= 64; /*
+ * Some slop for the tar cmd text,
+ * and sh -c
+ */
+ where_args = malloc(maxargs);
+ if (!where_args) {
+ cleanup(0);
+ errx(2, "%s: can't get argument list space", __func__);
+ }
+
+ memset(where_args, 0, maxargs);
+ strcpy(where_args, STARTSTRING);
+ where_count = sizeof(STARTSTRING)-1;
+ last_chdir = 0;
+
+ if (stat(".", &stb) == 0)
+ curdir = stb.st_dev;
+ else
+ curdir = (dev_t) -1; /*
+ * It's ok if this is a valid dev_t;
+ * this is just a hint for an
+ * optimization.
+ */
+
+ while (p) {
+ if (p->type == PLIST_CWD)
+ {
+ if (!prefix)
+ prefix = p->name;
+ where = p->name == NULL ? prefix : p->name;
+ }
+ else if (p->type == PLIST_SRC)
+ there = p->name;
+ else if (p->type == PLIST_IGNORE)
+ p = p->next;
+ else if (p->type == PLIST_FILE && !p->marked) {
+ char fn[FILENAME_MAX];
+
+
+ /* First, look for it in the "home" dir */
+ sprintf(fn, "%s/%s", home, p->name);
+ if (fexists(fn)) {
+ if (lstat(fn, &stb) == 0 && stb.st_dev == curdir &&
+ S_ISREG(stb.st_mode)) {
+ /*
+ * If we can link it to the playpen, that avoids a copy
+ * and saves time.
+ */
+ if (p->name[0] != '/') {
+ /*
+ * Don't link abspn stuff--it doesn't come from
+ * local dir!
+ */
+ if (trylink(fn, p->name) == 0) {
+ p = p->next;
+ continue;
+ }
+ }
+ }
+ if (TOOBIG(fn)) {
+ PUSHOUT();
+ }
+ if (p->name[0] == '/') {
+ add_count = snprintf(&where_args[where_count],
+ maxargs - where_count,
+ " %s %s",
+ last_chdir == root ? "" : "-C /",
+ p->name);
+ last_chdir = root;
+ } else {
+ add_count = snprintf(&where_args[where_count],
+ maxargs - where_count,
+ " %s%s %s",
+ last_chdir == home ? "" : "-C ",
+ last_chdir == home ? "" : home,
+ p->name);
+ last_chdir = home;
+ }
+ if (add_count < 0 || add_count >= maxargs - where_count) {
+ cleanup(0);
+ errx(2, "%s: oops, miscounted strings!", __func__);
+ }
+ where_count += add_count;
+ }
+ /*
+ * Otherwise, try along the actual extraction path..
+ */
+ else {
+ if (p->name[0] == '/')
+ mythere = root;
+ else mythere = there;
+ if (mythere)
+ snprintf(fn, sizeof(fn), "%s/%s", mythere, p->name);
+ else
+ snprintf(fn, sizeof(fn), "%s%s/%s",
+ BaseDir && where && where[0] == '/' ? BaseDir : "", where, p->name);
+ if (lstat(fn, &stb) == 0 && stb.st_dev == curdir &&
+ S_ISREG(stb.st_mode)) {
+ /*
+ * If we can link it to the playpen, that avoids a copy
+ * and saves time.
+ */
+ if (trylink(fn, p->name) == 0) {
+ p = p->next;
+ continue;
+ }
+ }
+ if (TOOBIG(p->name)) {
+ PUSHOUT();
+ }
+ if (last_chdir == (mythere ? mythere : where))
+ add_count = snprintf(&where_args[where_count],
+ maxargs - where_count,
+ " %s", p->name);
+ else
+ add_count = snprintf(&where_args[where_count],
+ maxargs - where_count,
+ " -C %s %s",
+ mythere ? mythere : where,
+ p->name);
+ if (add_count < 0 || add_count >= maxargs - where_count) {
+ cleanup(0);
+ errx(2, "%s: oops, miscounted strings!", __func__);
+ }
+ where_count += add_count;
+ last_chdir = (mythere ? mythere : where);
+ }
+ }
+ p = p->next;
+ }
+ PUSHOUT();
+ free(where_args);
+}
diff --git a/usr.sbin/pkg_install/delete/Makefile b/usr.sbin/pkg_install/delete/Makefile
new file mode 100644
index 0000000..c346ea7
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+PROG= pkg_delete
+SRCS= main.c perform.c
+
+CFLAGS+= -I${.CURDIR}/../lib
+
+WARNS?= 6
+WFORMAT?= 1
+
+DPADD= ${LIBINSTALL} ${LIBMD}
+LDADD= ${LIBINSTALL} -lmd
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pkg_install/delete/delete.h b/usr.sbin/pkg_install/delete/delete.h
new file mode 100644
index 0000000..e0aeca5
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/delete.h
@@ -0,0 +1,35 @@
+/* $FreeBSD$ */
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Include and define various things wanted by the delete command.
+ *
+ */
+
+#ifndef _INST_DELETE_H_INCLUDE
+#define _INST_DELETE_H_INCLUDE
+
+extern char *Prefix;
+extern Boolean CleanDirs;
+extern Boolean Interactive;
+extern Boolean NoDeInstall;
+extern Boolean Recursive;
+extern char *Directory;
+extern char *PkgName;
+extern match_t MatchType;
+
+#endif /* _INST_DELETE_H_INCLUDE */
diff --git a/usr.sbin/pkg_install/delete/main.c b/usr.sbin/pkg_install/delete/main.c
new file mode 100644
index 0000000..6075f72
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/main.c
@@ -0,0 +1,179 @@
+/*
+ *
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the delete module.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <getopt.h>
+#include <err.h>
+
+#include "lib.h"
+#include "delete.h"
+
+char *Prefix = NULL;
+Boolean CleanDirs = FALSE;
+Boolean Interactive = FALSE;
+Boolean NoDeInstall = FALSE;
+Boolean Recursive = FALSE;
+match_t MatchType = MATCH_GLOB;
+
+static void usage(void);
+
+static char opts[] = "adDfGhinp:rvxX";
+static struct option longopts[] = {
+ { "all", no_argument, NULL, 'a' },
+ { "clean-dirs", no_argument, NULL, 'd' },
+ { "dry-run", no_argument, NULL, 'n' },
+ { "extended", no_argument, NULL, 'X' },
+ { "force", no_argument, NULL, 'f' },
+ { "help", no_argument, NULL, 'h' },
+ { "interactive",no_argument, NULL, 'i' },
+ { "prefix", required_argument, NULL, 'p' },
+ { "recursive", no_argument, NULL, 'r' },
+ { "regex", no_argument, NULL, 'x' },
+ { "no-glob", no_argument, NULL, 'G' },
+ { "no-script", no_argument, NULL, 'D' },
+ { "no-scripts", no_argument, NULL, 'D' },
+ { "verbose", no_argument, NULL, 'v' },
+ { NULL, 0, NULL, 0 },
+};
+
+int
+main(int argc, char **argv)
+{
+ int ch, error;
+ char **pkgs, **start;
+ char *pkgs_split;
+ const char *tmp;
+ struct stat stat_s;
+
+ pkgs = start = argv;
+ while ((ch = getopt_long(argc, argv, opts, longopts, NULL)) != -1)
+ switch(ch) {
+ case 'v':
+ Verbose++;
+ break;
+
+ case 'f':
+ Force = TRUE;
+ break;
+
+ case 'p':
+ Prefix = optarg;
+ break;
+
+ case 'D':
+ NoDeInstall = TRUE;
+ break;
+
+ case 'd':
+ CleanDirs = TRUE;
+ break;
+
+ case 'n':
+ Fake = TRUE;
+ Verbose = TRUE;
+ break;
+
+ case 'a':
+ MatchType = MATCH_ALL;
+ break;
+
+ case 'G':
+ MatchType = MATCH_EXACT;
+ break;
+
+ case 'x':
+ MatchType = MATCH_REGEX;
+ break;
+
+ case 'X':
+ MatchType = MATCH_EREGEX;
+ break;
+
+ case 'i':
+ Interactive = TRUE;
+ break;
+
+ case 'r':
+ Recursive = TRUE;
+ break;
+
+ case 'h':
+ default:
+ usage();
+ break;
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* Get all the remaining package names, if any */
+ while (*argv) {
+ /* Don't try to apply heuristics if arguments are regexs */
+ if (MatchType != MATCH_REGEX)
+ while ((pkgs_split = strrchr(*argv, (int)'/')) != NULL) {
+ *pkgs_split++ = '\0';
+ /*
+ * If character after the '/' is alphanumeric, then we've found the
+ * package name. Otherwise we've come across a trailing '/' and
+ * need to continue our quest.
+ */
+ if (isalnum(*pkgs_split) || ((MatchType == MATCH_GLOB) && \
+ strpbrk(pkgs_split, "*?[]") != NULL)) {
+ *argv = pkgs_split;
+ break;
+ }
+ }
+ *pkgs++ = *argv++;
+ }
+
+ /* If no packages, yelp */
+ if (pkgs == start && MatchType != MATCH_ALL)
+ warnx("missing package name(s)"), usage();
+ *pkgs = NULL;
+ tmp = LOG_DIR;
+ (void) stat(tmp, &stat_s);
+ if (!Fake && getuid() && geteuid() != stat_s.st_uid) {
+ if (!Force)
+ errx(1, "you do not own %s, use -f to force", tmp);
+ else
+ warnx("you do not own %s (proceeding anyways)", tmp);
+ }
+ if ((error = pkg_perform(start)) != 0) {
+ if (Verbose)
+ warnx("%d package deletion(s) failed", error);
+ return error;
+ }
+ else
+ return 0;
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: pkg_delete [-dDfGinrvxX] [-p prefix] pkg-name ...",
+ " pkg_delete -a [flags]");
+ exit(1);
+}
diff --git a/usr.sbin/pkg_install/delete/perform.c b/usr.sbin/pkg_install/delete/perform.c
new file mode 100644
index 0000000..1519fd7
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/perform.c
@@ -0,0 +1,417 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the main body of the delete module.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include "lib.h"
+#include "delete.h"
+
+static int pkg_do(char *);
+static void sanity_check(char *);
+static void undepend(char *, char *);
+static char LogDir[FILENAME_MAX];
+
+
+int
+pkg_perform(char **pkgs)
+{
+ char **matched, **rb, **rbtmp;
+ int errcode, i, j;
+ int err_cnt = 0;
+ struct reqr_by_entry *rb_entry;
+ struct reqr_by_head *rb_list;
+
+ if (MatchType != MATCH_EXACT) {
+ matched = matchinstalled(MatchType, pkgs, &errcode);
+ if (errcode != 0)
+ return 1;
+ /* Not reached */
+
+ /*
+ * Copy matched[] into pkgs[], because we'll need to use
+ * matchinstalled() later on.
+ */
+ if (matched != NULL) {
+ pkgs = NULL;
+ for (i = 0; matched[i] != NULL; i++) {
+ pkgs = realloc(pkgs, sizeof(*pkgs) * (i + 2));
+ pkgs[i] = strdup(matched[i]);
+ }
+ pkgs[i] = NULL;
+ }
+ else switch (MatchType) {
+ case MATCH_GLOB:
+ break;
+ case MATCH_ALL:
+ warnx("no packages installed");
+ return 0;
+ case MATCH_EREGEX:
+ case MATCH_REGEX:
+ warnx("no packages match pattern(s)");
+ return 1;
+ default:
+ break;
+ }
+ }
+
+ err_cnt += sortdeps(pkgs);
+ for (i = 0; pkgs[i]; i++) {
+ if (Recursive == TRUE) {
+ errcode = requiredby(pkgs[i], &rb_list, FALSE, TRUE);
+ if (errcode < 0) {
+ err_cnt++;
+ } else if (errcode > 0) {
+ /*
+ * Copy values from the rb_list queue into argv-like NULL
+ * terminated list because requiredby() uses some static
+ * storage, while pkg_do() below will call this function,
+ * thus blowing our rb_list away.
+ */
+ rbtmp = rb = alloca((errcode + 1) * sizeof(*rb));
+ if (rb == NULL) {
+ warnx("%s(): alloca() failed", __func__);
+ err_cnt++;
+ continue;
+ }
+ STAILQ_FOREACH(rb_entry, rb_list, link) {
+ *rbtmp = alloca(strlen(rb_entry->pkgname) + 1);
+ if (*rbtmp == NULL) {
+ warnx("%s(): alloca() failed", __func__);
+ err_cnt++;
+ continue;
+ }
+ strcpy(*rbtmp, rb_entry->pkgname);
+ rbtmp++;
+ }
+ *rbtmp = NULL;
+
+ err_cnt += sortdeps(rb);
+ for (j = 0; rb[j]; j++)
+ err_cnt += pkg_do(rb[j]);
+ }
+ }
+ err_cnt += pkg_do(pkgs[i]);
+ }
+
+ return err_cnt;
+}
+
+static Package Plist;
+
+/* This is seriously ugly code following. Written very fast! */
+static int
+pkg_do(char *pkg)
+{
+ FILE *cfile;
+ char *deporigin, **deporigins = NULL, **depnames = NULL, ***depmatches, home[FILENAME_MAX];
+ PackingList p;
+ int i, len;
+ int isinstalled;
+ /* support for separate pre/post install scripts */
+ int new_m = 0, dep_count = 0;
+ const char *pre_script = DEINSTALL_FNAME;
+ const char *post_script, *pre_arg, *post_arg;
+ struct reqr_by_entry *rb_entry;
+ struct reqr_by_head *rb_list;
+
+ if (!pkg || !(len = strlen(pkg)))
+ return 1;
+ if (pkg[len - 1] == '/')
+ pkg[len - 1] = '\0';
+
+ /* Reset some state */
+ if (Plist.head)
+ free_plist(&Plist);
+
+ sprintf(LogDir, "%s/%s", LOG_DIR, pkg);
+
+ isinstalled = isinstalledpkg(pkg);
+ if (isinstalled == 0) {
+ warnx("no such package '%s' installed", pkg);
+ return 1;
+ } else if (isinstalled < 0) {
+ warnx("the package info for package '%s' is corrupt%s",
+ pkg, Force ? " (but I'll delete it anyway)" : " (use -f to force removal)");
+ if (!Force)
+ return 1;
+ if (!Fake) {
+ if (vsystem("%s -rf %s", REMOVE_CMD, LogDir)) {
+ warnx("couldn't remove log entry in %s, deinstall failed", LogDir);
+ } else {
+ warnx("couldn't completely deinstall package '%s',\n"
+ "only the log entry in %s was removed", pkg, LogDir);
+ }
+ }
+ return 0;
+ }
+
+ if (!getcwd(home, FILENAME_MAX)) {
+ cleanup(0);
+ errx(2, "%s: unable to get current working directory!", __func__);
+ }
+
+ if (chdir(LogDir) == FAIL) {
+ warnx("unable to change directory to %s! deinstall failed", LogDir);
+ return 1;
+ }
+
+ if (Interactive == TRUE) {
+ int first, ch;
+
+ (void)fprintf(stderr, "delete %s? ", pkg);
+ (void)fflush(stderr);
+ first = ch = getchar();
+ while (ch != '\n' && ch != EOF)
+ ch = getchar();
+ if (first != 'y' && first != 'Y')
+ return 0;
+ /* Not reached */
+ }
+
+ if (requiredby(pkg, &rb_list, FALSE, TRUE) < 0)
+ return 1;
+ if (!STAILQ_EMPTY(rb_list)) {
+ warnx("package '%s' is required by these other packages\n"
+ "and may not be deinstalled%s:",
+ pkg, Force ? " (but I'll delete it anyway)" : "");
+ STAILQ_FOREACH(rb_entry, rb_list, link)
+ fprintf(stderr, "%s\n", rb_entry->pkgname);
+ if (!Force)
+ return 1;
+ }
+
+ sanity_check(LogDir);
+ cfile = fopen(CONTENTS_FNAME, "r");
+
+ if (!cfile) {
+ warnx("unable to open '%s' file", CONTENTS_FNAME);
+ return 1;
+ }
+
+ /* If we have a prefix, add it now */
+ if (Prefix)
+ add_plist(&Plist, PLIST_CWD, Prefix);
+ read_plist(&Plist, cfile);
+ fclose(cfile);
+ p = find_plist(&Plist, PLIST_CWD);
+
+ if (!p) {
+ warnx("package '%s' doesn't have a prefix", pkg);
+ return 1;
+ }
+
+ setenv(PKG_PREFIX_VNAME, p->name, 1);
+
+ if (fexists(REQUIRE_FNAME)) {
+ if (Verbose)
+ printf("Executing 'require' script.\n");
+ vsystem("/bin/chmod +x %s", REQUIRE_FNAME); /* be sure */
+ if (vsystem("./%s %s DEINSTALL", REQUIRE_FNAME, pkg)) {
+ warnx("package %s fails requirements %s", pkg,
+ Force ? "" : "- not deleted");
+ if (!Force)
+ return 1;
+ }
+ }
+
+ /*
+ * Test whether to use the old method of passing tokens to deinstallation
+ * scripts, and set appropriate variables..
+ */
+
+ if (fexists(POST_DEINSTALL_FNAME)) {
+ new_m = 1;
+ post_script = POST_DEINSTALL_FNAME;
+ pre_arg = post_arg = "";
+ } else if (fexists(DEINSTALL_FNAME)) {
+ post_script = DEINSTALL_FNAME;
+ pre_arg = "DEINSTALL";
+ post_arg = "POST-DEINSTALL";
+ } else {
+ post_script = pre_arg = post_arg = NULL;
+ }
+
+ if (!NoDeInstall && pre_script != NULL && fexists(pre_script)) {
+ if (Fake)
+ printf("Would execute de-install script at this point.\n");
+ else {
+ vsystem("/bin/chmod +x %s", pre_script); /* make sure */
+ if (vsystem("./%s %s %s", pre_script, pkg, pre_arg)) {
+ warnx("deinstall script returned error status");
+ if (!Force)
+ return 1;
+ }
+ }
+ }
+
+ for (p = Plist.head; p ; p = p->next) {
+ if (p->type != PLIST_PKGDEP)
+ continue;
+ deporigin = (p->next != NULL && p->next->type == PLIST_DEPORIGIN) ? p->next->name :
+ NULL;
+ if (Verbose) {
+ printf("Trying to remove dependency on package '%s'", p->name);
+ if (deporigin != NULL)
+ printf(" with '%s' origin", deporigin);
+ printf(".\n");
+ }
+ if (!Fake) {
+ if (deporigin) {
+ deporigins = realloc(deporigins, (dep_count + 2) * sizeof(*deporigins));
+ depnames = realloc(depnames, (dep_count + 1) * sizeof(*depnames));
+ deporigins[dep_count] = deporigin;
+ deporigins[dep_count + 1] = NULL;
+ depnames[dep_count] = p->name;
+ dep_count++;
+ } else {
+ undepend(p->name, pkg);
+ }
+ }
+ }
+
+ if (dep_count > 0) {
+ /* Undepend all the dependencies at once */
+ depmatches = matchallbyorigin((const char **)deporigins, NULL);
+ free(deporigins);
+ if (depmatches) {
+ for (i = 0; i < dep_count; i++) {
+ if (depmatches[i]) {
+ char **tmp = depmatches[i];
+ int j;
+ for (j = 0; tmp[j] != NULL; j++)
+ undepend(tmp[j], pkg);
+ } else if (depnames[i]) {
+ undepend(depnames[i], pkg);
+ }
+ }
+ }
+ }
+
+ if (chdir(home) == FAIL) {
+ cleanup(0);
+ errx(2, "%s: unable to return to working directory %s!", __func__,
+ home);
+ }
+
+ /*
+ * Some packages aren't packed right, so we need to just ignore
+ * delete_package()'s status. Ugh! :-(
+ */
+ if (delete_package(FALSE, CleanDirs, &Plist) == FAIL)
+ warnx(
+ "couldn't entirely delete package (perhaps the packing list is\n"
+ "incorrectly specified?)");
+
+ if (chdir(LogDir) == FAIL) {
+ warnx("unable to change directory to %s! deinstall failed", LogDir);
+ return 1;
+ }
+
+ if (!NoDeInstall && post_script != NULL && fexists(post_script)) {
+ if (Fake)
+ printf("Would execute post-deinstall script at this point.\n");
+ else {
+ vsystem("/bin/chmod +x %s", post_script); /* make sure */
+ if (vsystem("./%s %s %s", post_script, pkg, post_arg)) {
+ warnx("post-deinstall script returned error status");
+ if (!Force)
+ return 1;
+ }
+ }
+ }
+
+ if (chdir(home) == FAIL) {
+ cleanup(0);
+ errx(2, "%s: unable to return to working directory %s!", __func__,
+ home);
+ }
+
+ if (!Fake) {
+ if (vsystem("%s -r%c %s", REMOVE_CMD, Force ? 'f' : ' ', LogDir)) {
+ warnx("couldn't remove log entry in %s, deinstall failed", LogDir);
+ if (!Force)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void
+sanity_check(char *pkg)
+{
+ if (!fexists(CONTENTS_FNAME)) {
+ cleanup(0);
+ errx(2, "%s: installed package %s has no %s file!", __func__,
+ pkg, CONTENTS_FNAME);
+ }
+}
+
+void
+cleanup(int sig)
+{
+ if (sig)
+ exit(1);
+}
+
+static void
+undepend(char *p, char *pkgname)
+{
+ char fname[FILENAME_MAX], ftmp[FILENAME_MAX];
+ FILE *fpwr;
+ int s;
+ struct reqr_by_entry *rb_entry;
+ struct reqr_by_head *rb_list;
+
+
+ if (requiredby(p, &rb_list, Verbose, FALSE) <= 0)
+ return;
+ snprintf(fname, sizeof(fname), "%s/%s/%s", LOG_DIR, p, REQUIRED_BY_FNAME);
+ snprintf(ftmp, sizeof(ftmp), "%s.XXXXXX", fname);
+ s = mkstemp(ftmp);
+ if (s == -1) {
+ warnx("couldn't open temp file '%s'", ftmp);
+ return;
+ }
+ fpwr = fdopen(s, "w");
+ if (fpwr == NULL) {
+ close(s);
+ warnx("couldn't fdopen temp file '%s'", ftmp);
+ goto cleanexit;
+ }
+ STAILQ_FOREACH(rb_entry, rb_list, link)
+ if (strcmp(rb_entry->pkgname, pkgname)) /* no match */
+ fputs(rb_entry->pkgname, fpwr), putc('\n', fpwr);
+ if (fchmod(s, 0644) == FAIL) {
+ warnx("error changing permission of temp file '%s'", ftmp);
+ fclose(fpwr);
+ goto cleanexit;
+ }
+ if (fclose(fpwr) == EOF) {
+ warnx("error closing temp file '%s'", ftmp);
+ goto cleanexit;
+ }
+ if (rename(ftmp, fname) == -1)
+ warnx("error renaming '%s' to '%s'", ftmp, fname);
+cleanexit:
+ remove(ftmp);
+ return;
+}
diff --git a/usr.sbin/pkg_install/delete/pkg_delete.1 b/usr.sbin/pkg_install/delete/pkg_delete.1
new file mode 100644
index 0000000..aa56a44
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/pkg_delete.1
@@ -0,0 +1,293 @@
+.\"
+.\" FreeBSD install - a package for the installation and maintainance
+.\" of non-core utilities.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" Jordan K. Hubbard
+.\"
+.\"
+.\" @(#)pkg_delete.1
+.\" $FreeBSD$
+.\"
+.Dd May 30, 2008
+.Dt PKG_DELETE 1
+.Os
+.Sh NAME
+.Nm pkg_delete
+.Nd a utility for deleting previously installed software package distributions
+.Sh SYNOPSIS
+.Nm
+.Op Fl dDfGinrvxX
+.Op Fl p Ar prefix
+.Ar pkg-name ...
+.Nm
+.Fl a
+.Op Ar flags
+.Sh DESCRIPTION
+The
+.Nm
+command is used to delete packages that have been previously installed
+with the
+.Xr pkg_add 1
+command.
+.Sh WARNING
+.Bf -emphasis
+Since the
+.Nm
+command may execute scripts or programs provided by a package file,
+your system may be susceptible to
+.Dq Em trojan horses
+or other subtle
+attacks from miscreants who create dangerous package files.
+.Pp
+You are advised to verify the competence and identity of those who
+provide installable package files.
+For extra protection, examine all
+the package control files in the package record directory
+.Pa ( /var/db/pkg/<pkg-name>/ ) .
+Pay particular attention to any +INSTALL, +POST-INSTALL, +DEINSTALL,
++POST-DEINSTALL, +REQUIRE or +MTREE_DIRS files, and inspect the +CONTENTS
+file for
+.Cm @cwd ,
+.Cm @mode
+(check for setuid),
+.Cm @dirrm ,
+.Cm @exec ,
+and
+.Cm @unexec
+directives, and/or use the
+.Xr pkg_info 1
+command to examine the installed package control files.
+.Ef
+.Sh OPTIONS
+The following command line options are supported:
+.Bl -tag -width indent
+.It Ar pkg-name ...
+The named packages are deinstalled.
+.It Fl a , -all
+Unconditionally delete all currently installed packages.
+.It Fl i , -interactive
+Request confirmation before attempting to delete each package,
+regardless whether or not the standard input device is a
+terminal.
+.It Fl v , -verbose
+Turn on verbose output.
+.It Fl D , -no-script , -no-scripts
+If a deinstallation script exists for a given package, do not execute it.
+.It Fl n , -dry-run
+Do not actually deinstall a package, just report the steps that
+would be taken if it were.
+.It Fl p , -prefix Ar prefix
+Set
+.Ar prefix
+as the directory in which to delete files from any installed packages
+which do not explicitly set theirs.
+For most packages, the prefix will
+be set automatically to the installed location by
+.Xr pkg_add 1 .
+.It Fl d , -clean-dirs
+Remove empty directories created by file cleanup.
+By default, only
+files/directories explicitly listed in a package's contents (either as
+normal files/directories or with the
+.Cm @dirrm
+directive) will be removed at deinstallation time.
+This option tells
+.Nm
+to also remove any directories that were emptied as a result of removing
+the package.
+.It Fl f , -force
+Force removal of the package, even if a dependency is recorded or the
+deinstall or require script fails.
+.It Fl G , -no-glob
+Do not try to expand shell glob patterns in the
+.Ar pkg-name
+when selecting packages to be deleted (by default
+.Nm
+automatically expands shell glob patterns in the
+.Ar pkg-name ) .
+.It Fl x , -regex
+Treat the
+.Ar pkg-name
+as a regular expression and delete all packages whose names match
+that regular expression.
+Multiple regular expressions could be
+provided, in that case
+.Nm
+deletes all packages that match at least one
+regular expression from the list.
+.It Fl X , -extended
+Like
+.Fl x ,
+but treats the
+.Ar pkg-name
+as an extended regular expression.
+.It Fl r , -recursive
+Recursive removal.
+In addition to specified packages, delete all
+packages that depend on those packages as well.
+.El
+.Sh TECHNICAL DETAILS
+The
+.Nm
+utility
+does pretty much what it says.
+It examines installed package records in
+.Pa /var/db/pkg/<pkg-name> ,
+deletes the package contents, and finally removes the package records.
+If the environment variable
+.Ev PKG_DBDIR
+is set, this overrides the
+.Pa /var/db/pkg/
+path shown above.
+.Pp
+If a package is required by other installed packages,
+.Nm
+will list those dependent packages and refuse to delete the package
+(unless the
+.Fl f
+option is given).
+.Pp
+If the package contains a
+.Ar require
+file (see
+.Xr pkg_create 1 ) ,
+then this is executed first as
+.Bd -ragged -offset indent -compact
+.Cm require
+.Ar <pkg-name>
+.Ar DEINSTALL
+.Ed
+(where
+.Ar pkg-name
+is the name of the package in question and
+.Ar DEINSTALL
+is a keyword denoting that this is a deinstallation)
+to see whether or not deinstallation should continue.
+A non-zero exit
+status means no, unless the
+.Fl f
+option is specified.
+.Pp
+If a
+.Cm deinstall
+script exists for the package, it is executed before any files are removed.
+It is this script's responsibility to clean up any additional messy details
+around the package's installation, since all
+.Nm
+knows how to do is delete the files created in the original distribution.
+The
+.Nm deinstall
+script is called as:
+.Bd -ragged -offset indent -compact
+.Cm script
+.Ar <pkg-name>
+.Ar DEINSTALL
+.Ed
+where
+.Ar pkg-name
+is the name of the package in question and
+.Ar DEINSTALL
+is a keyword denoting this as the pre-deinstallation phase.
+.Pp
+.Sy Note :
+The
+.Ar DEINSTALL
+keyword will not appear if separate scripts for deinstall and post-deinstall
+are given during package creation time (using the
+.Fl k
+and
+.Fl K
+flags to
+.Xr pkg_create 1 ) .
+.Pp
+If a
+.Cm post-deinstall
+script exists for the package, it is executed
+.Cm after
+all files are removed.
+It is this script's responsibility to clean up any
+additional messy details around the package's installation, and leave the
+system (hopefully) in the same state that it was prior to the installation
+of the package.
+.Pp
+The
+.Nm post-deinstall
+script is called as:
+.Bd -ragged -offset indent -compact
+.Cm script
+.Ar <pkg-name>
+.Ar POST-DEINSTALL
+.Ed
+where
+.Ar pkg-name
+is the name of the package in question and
+.Ar POST-DEINSTALL
+is a keyword denoting this as the post-deinstallation phase.
+.Pp
+.Sy Note :
+The
+.Ar POST-DEINSTALL
+keyword will not appear if separate scripts for deinstall and post-deinstall
+are given during package creation time (using the
+.Fl k
+and
+.Fl K
+flags to
+.Xr pkg_create 1 ) .
+.Pp
+Reasoning behind passing keywords such as
+.Ar DEINSTALL
+and
+.Ar POST-DEINSTALL
+is that it lets you potentially write only one program/script that handles
+all aspects of installation and deletion.
+.Pp
+But experience has proved that this is a lot more difficult to maintain and
+is not as advantageous as having separate scripts that handle each aspect of
+installation and deinstallation.
+.Pp
+All scripts are called with the environment variable
+.Ev PKG_PREFIX
+set to the installation prefix (see the
+.Fl p
+option above).
+This allows a package author to write a script
+that reliably performs some action on the directory where the package
+is installed, even if the user might have changed it by specifying the
+.Fl p
+option when running
+.Nm
+or
+.Cm pkg_add .
+.Sh ENVIRONMENT
+The environment variable
+.Ev PKG_DBDIR
+specifies an alternative location for the installed package database.
+.Sh FILES
+.Bl -tag -width /var/db/pkg -compact
+.It Pa /var/db/pkg
+Default location of the installed package database.
+.El
+.Sh SEE ALSO
+.Xr pkg_add 1 ,
+.Xr pkg_create 1 ,
+.Xr pkg_info 1 ,
+.Xr pkg_version 1 ,
+.Xr mktemp 3 ,
+.Xr mtree 8
+.Sh AUTHORS
+.An Jordan Hubbard
+.Sh CONTRIBUTORS
+.An John Kohl Aq jtk@rational.com ,
+.An Oliver Eikemeier Aq eik@FreeBSD.org
+.Sh BUGS
+Sure to be some.
diff --git a/usr.sbin/pkg_install/info/Makefile b/usr.sbin/pkg_install/info/Makefile
new file mode 100644
index 0000000..675f7ca
--- /dev/null
+++ b/usr.sbin/pkg_install/info/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+PROG= pkg_info
+SRCS= main.c perform.c show.c
+
+CFLAGS+= -I${.CURDIR}/../lib
+
+WARNS?= 6
+WFORMAT?= 1
+
+DPADD= ${LIBINSTALL} ${LIBFETCH} ${LIBMD}
+LDADD= ${LIBINSTALL} -lfetch -lmd
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pkg_install/info/info.h b/usr.sbin/pkg_install/info/info.h
new file mode 100644
index 0000000..46e29b1
--- /dev/null
+++ b/usr.sbin/pkg_install/info/info.h
@@ -0,0 +1,84 @@
+/* $FreeBSD$ */
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 23 August 1993
+ *
+ * Include and define various things wanted by the info command.
+ *
+ */
+
+#ifndef _INST_INFO_H_INCLUDE
+#define _INST_INFO_H_INCLUDE
+
+#include <sys/queue.h>
+
+#ifndef MAXINDEXSIZE
+#define MAXINDEXSIZE 59
+#endif
+
+#ifndef MAXNAMESIZE
+#define MAXNAMESIZE 20
+#endif
+
+#define SHOW_COMMENT 0x00001
+#define SHOW_DESC 0x00002
+#define SHOW_PLIST 0x00004
+#define SHOW_INSTALL 0x00008
+#define SHOW_DEINSTALL 0x00010
+#define SHOW_REQUIRE 0x00020
+#define SHOW_PREFIX 0x00040
+#define SHOW_INDEX 0x00080
+#define SHOW_FILES 0x00100
+#define SHOW_DISPLAY 0x00200
+#define SHOW_REQBY 0x00400
+#define SHOW_MTREE 0x00800
+#define SHOW_SIZE 0x01000
+#define SHOW_ORIGIN 0x02000
+#define SHOW_CKSUM 0x04000
+#define SHOW_FMTREV 0x08000
+#define SHOW_PTREV 0x10000
+#define SHOW_DEPEND 0x20000
+#define SHOW_PKGNAME 0x40000
+
+struct which_entry {
+ TAILQ_ENTRY(which_entry) next;
+ char file[PATH_MAX];
+ char package[PATH_MAX];
+ Boolean skip;
+};
+TAILQ_HEAD(which_head, which_entry);
+
+extern int Flags;
+extern Boolean QUIET;
+extern Boolean UseBlkSz;
+extern Boolean KeepPackage;
+extern char *InfoPrefix;
+extern char PlayPen[];
+extern char *CheckPkg;
+extern char *LookUpOrigin;
+extern match_t MatchType;
+extern struct which_head *whead;
+
+extern void show_file(const char *, const char *);
+extern void show_plist(const char *, Package *, plist_t, Boolean);
+extern void show_files(const char *, Package *);
+extern void show_index(const char *, const char *);
+extern void show_size(const char *, Package *);
+extern void show_cksum(const char *, Package *);
+extern void show_origin(const char *, Package *);
+extern void show_fmtrev(const char *, Package *);
+
+#endif /* _INST_INFO_H_INCLUDE */
diff --git a/usr.sbin/pkg_install/info/main.c b/usr.sbin/pkg_install/info/main.c
new file mode 100644
index 0000000..aaa2a1b
--- /dev/null
+++ b/usr.sbin/pkg_install/info/main.c
@@ -0,0 +1,293 @@
+/*
+ *
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the info module.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <getopt.h>
+#include <err.h>
+
+#include "lib.h"
+#include "info.h"
+
+int Flags = 0;
+match_t MatchType = MATCH_GLOB;
+Boolean QUIET = FALSE;
+Boolean UseBlkSz = FALSE;
+char *InfoPrefix = (char *)(uintptr_t)"";
+char PlayPen[FILENAME_MAX];
+char *CheckPkg = NULL;
+char *LookUpOrigin = NULL;
+Boolean KeepPackage = FALSE;
+struct which_head *whead;
+
+static void usage(void);
+
+static char opts[] = "abcdDe:EfgGhiIjkKl:LmoO:pPqQrRst:vVW:xX";
+static struct option longopts[] = {
+ { "all", no_argument, NULL, 'a' },
+ { "blocksize", no_argument, NULL, 'b' },
+ { "exist", required_argument, NULL, 'X' },
+ { "exists", required_argument, NULL, 'X' },
+ { "extended", no_argument, NULL, 'e' },
+ { "help", no_argument, NULL, 'h' },
+ { "keep", no_argument, NULL, 'K' },
+ { "no-glob", no_argument, NULL, 'G' },
+ { "origin", required_argument, NULL, 'O' },
+ { "quiet", no_argument, NULL, 'q' },
+ { "regex", no_argument, NULL, 'x' },
+ { "template", required_argument, NULL, 't' },
+ { "verbose", no_argument, NULL, 'v' },
+ { "version", no_argument, NULL, 'P' },
+ { "which", required_argument, NULL, 'W' },
+};
+
+int
+main(int argc, char **argv)
+{
+ int ch;
+ char **pkgs, **start;
+ char *pkgs_split;
+
+ whead = malloc(sizeof(struct which_head));
+ if (whead == NULL)
+ err(2, NULL);
+ TAILQ_INIT(whead);
+
+ pkgs = start = argv;
+ if (argc == 1) {
+ MatchType = MATCH_ALL;
+ Flags = SHOW_INDEX;
+ }
+ else while ((ch = getopt_long(argc, argv, opts, longopts, NULL)) != -1) {
+ switch(ch) {
+ case 'a':
+ MatchType = MATCH_ALL;
+ break;
+
+ case 'b':
+ UseBlkSz = TRUE;
+ break;
+
+ case 'v':
+ Verbose++;
+ /* Reasonable definition of 'everything' */
+ Flags = SHOW_COMMENT | SHOW_DESC | SHOW_PLIST | SHOW_INSTALL |
+ SHOW_DEINSTALL | SHOW_REQUIRE | SHOW_DISPLAY | SHOW_MTREE;
+ break;
+
+ case 'E':
+ Flags |= SHOW_PKGNAME;
+ break;
+
+ case 'I':
+ Flags |= SHOW_INDEX;
+ break;
+
+ case 'p':
+ Flags |= SHOW_PREFIX;
+ break;
+
+ case 'c':
+ Flags |= SHOW_COMMENT;
+ break;
+
+ case 'd':
+ Flags |= SHOW_DESC;
+ break;
+
+ case 'D':
+ Flags |= SHOW_DISPLAY;
+ break;
+
+ case 'f':
+ Flags |= SHOW_PLIST;
+ break;
+
+ case 'g':
+ Flags |= SHOW_CKSUM;
+ break;
+
+ case 'G':
+ MatchType = MATCH_EXACT;
+ break;
+
+ case 'i':
+ Flags |= SHOW_INSTALL;
+ break;
+
+ case 'j':
+ Flags |= SHOW_REQUIRE;
+ break;
+
+ case 'k':
+ Flags |= SHOW_DEINSTALL;
+ break;
+
+ case 'K':
+ KeepPackage = TRUE;
+ break;
+
+ case 'r':
+ Flags |= SHOW_DEPEND;
+ break;
+
+ case 'R':
+ Flags |= SHOW_REQBY;
+ break;
+
+ case 'L':
+ Flags |= SHOW_FILES;
+ break;
+
+ case 'm':
+ Flags |= SHOW_MTREE;
+ break;
+
+ case 's':
+ Flags |= SHOW_SIZE;
+ break;
+
+ case 'o':
+ Flags |= SHOW_ORIGIN;
+ break;
+
+ case 'O':
+ LookUpOrigin = strdup(optarg);
+ if (LookUpOrigin == NULL)
+ err(2, NULL);
+ break;
+
+ case 'V':
+ Flags |= SHOW_FMTREV;
+ break;
+
+ case 'l':
+ InfoPrefix = optarg;
+ break;
+
+ case 'q':
+ Quiet = TRUE;
+ break;
+
+ case 'Q':
+ Quiet = TRUE;
+ QUIET = TRUE;
+ break;
+
+ case 't':
+ strlcpy(PlayPen, optarg, sizeof(PlayPen));
+ break;
+
+ case 'x':
+ MatchType = MATCH_REGEX;
+ break;
+
+ case 'X':
+ MatchType = MATCH_EREGEX;
+ break;
+
+ case 'e':
+ CheckPkg = optarg;
+ break;
+
+ case 'W':
+ {
+ struct which_entry *entp;
+
+ entp = calloc(1, sizeof(struct which_entry));
+ if (entp == NULL)
+ err(2, NULL);
+
+ strlcpy(entp->file, optarg, PATH_MAX);
+ entp->skip = FALSE;
+ TAILQ_INSERT_TAIL(whead, entp, next);
+ break;
+ }
+
+ case 'P':
+ Flags = SHOW_PTREV;
+ break;
+
+ case 'h':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (Flags & SHOW_PTREV) {
+ if (!Quiet)
+ printf("Package tools revision: ");
+ printf("%d\n", PKG_INSTALL_VERSION);
+ exit(0);
+ }
+
+ /* Set some reasonable defaults */
+ if (!Flags)
+ Flags = SHOW_COMMENT | SHOW_DESC | SHOW_REQBY;
+
+ /* Get all the remaining package names, if any */
+ while (*argv) {
+ /*
+ * Don't try to apply heuristics if arguments are regexs or if
+ * the argument refers to an existing file.
+ */
+ if (MatchType != MATCH_REGEX && MatchType != MATCH_EREGEX && !isfile(*argv) && !isURL(*argv))
+ while ((pkgs_split = strrchr(*argv, (int)'/')) != NULL) {
+ *pkgs_split++ = '\0';
+ /*
+ * If character after the '/' is alphanumeric or shell
+ * metachar, then we've found the package name. Otherwise
+ * we've come across a trailing '/' and need to continue our
+ * quest.
+ */
+ if (isalnum(*pkgs_split) || ((MatchType == MATCH_GLOB) && \
+ strpbrk(pkgs_split, "*?[]") != NULL)) {
+ *argv = pkgs_split;
+ break;
+ }
+ }
+ *pkgs++ = *argv++;
+ }
+
+ /* If no packages, yelp */
+ if (pkgs == start && MatchType != MATCH_ALL && !CheckPkg &&
+ TAILQ_EMPTY(whead) && LookUpOrigin == NULL)
+ warnx("missing package name(s)"), usage();
+ *pkgs = NULL;
+ return pkg_perform(start);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
+ "usage: pkg_info [-bcdDEfgGiIjkKLmopPqQrRsvVxX] [-e package] [-l prefix]",
+ " [-t template] -a | pkg-name ...",
+ " pkg_info [-qQ] -W filename",
+ " pkg_info [-qQ] -O origin",
+ " pkg_info");
+ exit(1);
+}
diff --git a/usr.sbin/pkg_install/info/perform.c b/usr.sbin/pkg_install/info/perform.c
new file mode 100644
index 0000000..3803d64
--- /dev/null
+++ b/usr.sbin/pkg_install/info/perform.c
@@ -0,0 +1,478 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 23 Aug 1993
+ *
+ * This is the main body of the info module.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include "info.h"
+#include <err.h>
+#include <signal.h>
+
+static int pkg_do(char *);
+static int find_pkg(struct which_head *);
+static int cmp_path(const char *, const char *, const char *);
+static char *abspath(const char *);
+static int find_pkgs_by_origin(const char *);
+static int matched_packages(char **pkgs);
+
+int
+pkg_perform(char **pkgs)
+{
+ char **matched;
+ int err_cnt = 0;
+ int i, errcode;
+
+ signal(SIGINT, cleanup);
+
+ /* Overriding action? */
+ if (Flags & SHOW_PKGNAME) {
+ return matched_packages(pkgs);
+ } else if (CheckPkg) {
+ return isinstalledpkg(CheckPkg) > 0 ? 0 : 1;
+ /* Not reached */
+ } else if (!TAILQ_EMPTY(whead)) {
+ return find_pkg(whead);
+ } else if (LookUpOrigin != NULL) {
+ return find_pkgs_by_origin(LookUpOrigin);
+ }
+
+ if (MatchType != MATCH_EXACT) {
+ matched = matchinstalled(MatchType, pkgs, &errcode);
+ if (errcode != 0)
+ return 1;
+ /* Not reached */
+
+ if (matched != NULL)
+ pkgs = matched;
+ else switch (MatchType) {
+ case MATCH_GLOB:
+ break;
+ case MATCH_ALL:
+ warnx("no packages installed");
+ return 0;
+ /* Not reached */
+ case MATCH_REGEX:
+ case MATCH_EREGEX:
+ warnx("no packages match pattern(s)");
+ return 1;
+ /* Not reached */
+ default:
+ break;
+ }
+ }
+
+ for (i = 0; pkgs[i]; i++)
+ err_cnt += pkg_do(pkgs[i]);
+
+ return err_cnt;
+}
+
+static char *Home;
+
+static int
+pkg_do(char *pkg)
+{
+ Boolean installed = FALSE, isTMP = FALSE;
+ char log_dir[FILENAME_MAX];
+ char fname[FILENAME_MAX];
+ Package plist;
+ FILE *fp;
+ struct stat sb;
+ char *cp = NULL;
+ int code = 0;
+
+ if (isURL(pkg)) {
+ if ((cp = fileGetURL(NULL, pkg, KeepPackage)) != NULL) {
+ if (!getcwd(fname, FILENAME_MAX))
+ upchuck("getcwd");
+ isTMP = TRUE;
+ } else {
+ goto bail;
+ }
+ }
+ else if (fexists(pkg) && isfile(pkg)) {
+ int len;
+
+ if (*pkg != '/') {
+ if (!getcwd(fname, FILENAME_MAX))
+ upchuck("getcwd");
+ len = strlen(fname);
+ snprintf(&fname[len], FILENAME_MAX - len, "/%s", pkg);
+ }
+ else
+ strcpy(fname, pkg);
+ cp = fname;
+ }
+ else {
+ if ((cp = fileFindByPath(NULL, pkg)) != NULL)
+ strncpy(fname, cp, FILENAME_MAX);
+ }
+ if (cp) {
+ if (!isURL(pkg)) {
+ /*
+ * Apply a crude heuristic to see how much space the package will
+ * take up once it's unpacked. I've noticed that most packages
+ * compress an average of 75%, but we're only unpacking the + files so
+ * be very optimistic.
+ */
+ if (stat(fname, &sb) == FAIL) {
+ warnx("can't stat package file '%s'", fname);
+ code = 1;
+ goto bail;
+ }
+ Home = make_playpen(PlayPen, sb.st_size / 2);
+ if (unpack(fname, "'+*'")) {
+ warnx("error during unpacking, no info for '%s' available", pkg);
+ code = 1;
+ goto bail;
+ }
+ }
+ }
+ /* It's not an uninstalled package, try and find it among the installed */
+ else {
+ int isinstalled = isinstalledpkg(pkg);
+ if (isinstalled < 0) {
+ warnx("the package info for package '%s' is corrupt", pkg);
+ return 1;
+ } else if (isinstalled == 0) {
+ warnx("can't find package '%s' installed or in a file!", pkg);
+ return 1;
+ }
+ sprintf(log_dir, "%s/%s", LOG_DIR, pkg);
+ if (chdir(log_dir) == FAIL) {
+ warnx("can't change directory to '%s'!", log_dir);
+ return 1;
+ }
+ installed = TRUE;
+ }
+
+ /* Suck in the contents list */
+ plist.head = plist.tail = NULL;
+ fp = fopen(CONTENTS_FNAME, "r");
+ if (!fp) {
+ warnx("unable to open %s file", CONTENTS_FNAME);
+ code = 1;
+ goto bail;
+ }
+ /* If we have a prefix, add it now */
+ read_plist(&plist, fp);
+ fclose(fp);
+
+ /*
+ * Index is special info type that has to override all others to make
+ * any sense.
+ */
+ if (Flags & SHOW_INDEX) {
+ char tmp[FILENAME_MAX];
+
+ snprintf(tmp, FILENAME_MAX, "%-19s ", pkg);
+ show_index(tmp, COMMENT_FNAME);
+ }
+ else {
+ /* Start showing the package contents */
+ if (!Quiet)
+ printf("%sInformation for %s:\n\n", InfoPrefix, pkg);
+ else if (QUIET)
+ printf("%s%s:", InfoPrefix, pkg);
+ if (Flags & SHOW_COMMENT)
+ show_file("Comment:\n", COMMENT_FNAME);
+ if (Flags & SHOW_DEPEND)
+ show_plist("Depends on:\n", &plist, PLIST_PKGDEP, FALSE);
+ if ((Flags & SHOW_REQBY) && !isemptyfile(REQUIRED_BY_FNAME))
+ show_file("Required by:\n", REQUIRED_BY_FNAME);
+ if (Flags & SHOW_DESC)
+ show_file("Description:\n", DESC_FNAME);
+ if ((Flags & SHOW_DISPLAY) && fexists(DISPLAY_FNAME))
+ show_file("Install notice:\n", DISPLAY_FNAME);
+ if (Flags & SHOW_PLIST)
+ show_plist("Packing list:\n", &plist, (plist_t)0, TRUE);
+ if (Flags & SHOW_REQUIRE && fexists(REQUIRE_FNAME))
+ show_file("Requirements script:\n", REQUIRE_FNAME);
+ if ((Flags & SHOW_INSTALL) && fexists(INSTALL_FNAME))
+ show_file("Install script:\n", INSTALL_FNAME);
+ if ((Flags & SHOW_INSTALL) && fexists(POST_INSTALL_FNAME))
+ show_file("Post-Install script:\n", POST_INSTALL_FNAME);
+ if ((Flags & SHOW_DEINSTALL) && fexists(DEINSTALL_FNAME))
+ show_file("De-Install script:\n", DEINSTALL_FNAME);
+ if ((Flags & SHOW_DEINSTALL) && fexists(POST_DEINSTALL_FNAME))
+ show_file("Post-DeInstall script:\n", POST_DEINSTALL_FNAME);
+ if ((Flags & SHOW_MTREE) && fexists(MTREE_FNAME))
+ show_file("mtree file:\n", MTREE_FNAME);
+ if (Flags & SHOW_PREFIX)
+ show_plist("Prefix(s):\n", &plist, PLIST_CWD, FALSE);
+ if (Flags & SHOW_FILES)
+ show_files("Files:\n", &plist);
+ if ((Flags & SHOW_SIZE) && installed)
+ show_size("Package Size:\n", &plist);
+ if ((Flags & SHOW_CKSUM) && installed)
+ show_cksum("Mismatched Checksums:\n", &plist);
+ if (Flags & SHOW_ORIGIN)
+ show_origin("Origin:\n", &plist);
+ if (Flags & SHOW_FMTREV)
+ show_fmtrev("Packing list format revision:\n", &plist);
+ if (!Quiet)
+ puts(InfoPrefix);
+ }
+ free_plist(&plist);
+ bail:
+ leave_playpen();
+ if (isTMP)
+ unlink(fname);
+ return code;
+}
+
+void
+cleanup(int sig)
+{
+ static int in_cleanup = 0;
+
+ if (!in_cleanup) {
+ in_cleanup = 1;
+ leave_playpen();
+ }
+ if (sig)
+ exit(1);
+}
+
+/*
+ * Return an absolute path, additionally removing all .'s, ..'s, and extraneous
+ * /'s, as realpath() would, but without resolving symlinks, because that can
+ * potentially screw up our comparisons later.
+ */
+static char *
+abspath(const char *pathname)
+{
+ char *tmp, *tmp1, *resolved_path;
+ char *cwd = NULL;
+ int len;
+
+ if (pathname[0] != '/') {
+ cwd = getcwd(NULL, MAXPATHLEN);
+ asprintf(&resolved_path, "%s/%s/", cwd, pathname);
+ } else
+ asprintf(&resolved_path, "%s/", pathname);
+
+ if (resolved_path == NULL)
+ errx(2, NULL);
+
+ if (cwd != NULL)
+ free(cwd);
+
+ while ((tmp = strstr(resolved_path, "//")) != NULL)
+ strcpy(tmp, tmp + 1);
+
+ while ((tmp = strstr(resolved_path, "/./")) != NULL)
+ strcpy(tmp, tmp + 2);
+
+ while ((tmp = strstr(resolved_path, "/../")) != NULL) {
+ *tmp = '\0';
+ if ((tmp1 = strrchr(resolved_path, '/')) == NULL)
+ tmp1 = resolved_path;
+ strcpy(tmp1, tmp + 3);
+ }
+
+ len = strlen(resolved_path);
+ if (len > 1 && resolved_path[len - 1] == '/')
+ resolved_path[len - 1] = '\0';
+
+ return resolved_path;
+}
+
+/*
+ * Comparison to see if the path we're on matches the
+ * one we are looking for.
+ */
+static int
+cmp_path(const char *target, const char *current, const char *cwd)
+{
+ char *resolved, *temp;
+ int rval;
+
+ asprintf(&temp, "%s/%s", cwd, current);
+ if (temp == NULL)
+ errx(2, NULL);
+
+ /*
+ * Make sure there's no multiple /'s or other weird things in the PLIST,
+ * since some plists seem to have them and it could screw up our strncmp.
+ */
+ resolved = abspath(temp);
+
+ if (strcmp(target, resolved) == 0)
+ rval = 1;
+ else
+ rval = 0;
+
+ free(temp);
+ free(resolved);
+ return rval;
+}
+
+/*
+ * Look through package dbs in LOG_DIR and find which
+ * packages installed the files in which_list.
+ */
+static int
+find_pkg(struct which_head *which_list)
+{
+ char **installed;
+ int errcode, i;
+ struct which_entry *wp;
+
+ TAILQ_FOREACH(wp, which_list, next) {
+ const char *msg = "file cannot be found";
+ char *tmp;
+
+ wp->skip = TRUE;
+ /* If it's not a file, we'll see if it's an executable. */
+ if (isfile(wp->file) == FALSE) {
+ if (strchr(wp->file, '/') == NULL) {
+ tmp = vpipe("/usr/bin/which %s", wp->file);
+ if (tmp != NULL) {
+ strlcpy(wp->file, tmp, PATH_MAX);
+ wp->skip = FALSE;
+ free(tmp);
+ } else
+ msg = "file is not in PATH";
+ }
+ } else {
+ tmp = abspath(wp->file);
+ if (isfile(tmp)) {
+ strlcpy(wp->file, tmp, PATH_MAX);
+ wp->skip = FALSE;
+ }
+ free(tmp);
+ }
+ if (wp->skip == TRUE)
+ warnx("%s: %s", wp->file, msg);
+ }
+
+ installed = matchinstalled(MATCH_ALL, NULL, &errcode);
+ if (installed == NULL)
+ return errcode;
+
+ for (i = 0; installed[i] != NULL; i++) {
+ FILE *fp;
+ Package pkg;
+ PackingList itr;
+ char *cwd = NULL;
+ char tmp[PATH_MAX];
+
+ snprintf(tmp, PATH_MAX, "%s/%s/%s", LOG_DIR, installed[i],
+ CONTENTS_FNAME);
+ fp = fopen(tmp, "r");
+ if (fp == NULL) {
+ warn("%s", tmp);
+ return 1;
+ }
+
+ pkg.head = pkg.tail = NULL;
+ read_plist(&pkg, fp);
+ fclose(fp);
+ for (itr = pkg.head; itr != pkg.tail; itr = itr->next) {
+ if (itr->type == PLIST_CWD) {
+ cwd = itr->name;
+ } else if (itr->type == PLIST_FILE) {
+ TAILQ_FOREACH(wp, which_list, next) {
+ if (wp->skip == TRUE)
+ continue;
+ if (!cmp_path(wp->file, itr->name, cwd))
+ continue;
+ if (wp->package[0] != '\0') {
+ warnx("both %s and %s claim to have installed %s\n",
+ wp->package, installed[i], wp->file);
+ } else {
+ strlcpy(wp->package, installed[i], PATH_MAX);
+ }
+ }
+ }
+ }
+ free_plist(&pkg);
+ }
+
+ TAILQ_FOREACH(wp, which_list, next) {
+ if (wp->package[0] != '\0') {
+ if (Quiet)
+ puts(wp->package);
+ else
+ printf("%s was installed by package %s\n", \
+ wp->file, wp->package);
+ }
+ }
+ while (!TAILQ_EMPTY(which_list)) {
+ wp = TAILQ_FIRST(which_list);
+ TAILQ_REMOVE(which_list, wp, next);
+ free(wp);
+ }
+
+ free(which_list);
+ return 0;
+}
+
+/*
+ * Look through package dbs in LOG_DIR and find which
+ * packages have the given origin. Don't use read_plist()
+ * because this increases time necessary for lookup by 40
+ * times, as we don't really have to parse all plist to
+ * get origin.
+ */
+static int
+find_pkgs_by_origin(const char *origin)
+{
+ char **matched;
+ int errcode, i;
+
+ if (!Quiet)
+ printf("The following installed package(s) has %s origin:\n", origin);
+
+ matched = matchbyorigin(origin, &errcode);
+ if (matched == NULL)
+ return errcode;
+
+ for (i = 0; matched[i] != NULL; i++)
+ puts(matched[i]);
+
+ return 0;
+}
+
+/*
+ * List only the matching package names.
+ * Mainly intended for scripts.
+ */
+static int
+matched_packages(char **pkgs)
+{
+ char **matched;
+ int i, errcode;
+
+ matched = matchinstalled(MatchType == MATCH_GLOB ? MATCH_NGLOB : MatchType, pkgs, &errcode);
+
+ if (errcode != 0 || matched == NULL)
+ return 1;
+
+ for (i = 0; matched[i]; i++)
+ if (!Quiet)
+ printf("%s\n", matched[i]);
+ else if (QUIET)
+ printf("%s%s\n", InfoPrefix, matched[i]);
+
+ return 0;
+}
diff --git a/usr.sbin/pkg_install/info/pkg_info.1 b/usr.sbin/pkg_install/info/pkg_info.1
new file mode 100644
index 0000000..8b032c1
--- /dev/null
+++ b/usr.sbin/pkg_install/info/pkg_info.1
@@ -0,0 +1,294 @@
+.\"
+.\" FreeBSD install - a package for the installation and maintenance
+.\" of non-core utilities.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" Jordan K. Hubbard
+.\"
+.\"
+.\" @(#)pkg_info.1
+.\" $FreeBSD$
+.\"
+.Dd May 30, 2008
+.Dt PKG_INFO 1
+.Os
+.Sh NAME
+.Nm pkg_info
+.Nd a utility for displaying information on software packages
+.Sh SYNOPSIS
+.Nm
+.Op Fl bcdDEfghGiIjkKLmopPqQrRsvVxX
+.Op Fl e Ar package
+.Op Fl l Ar prefix
+.Op Fl t Ar template
+.Fl a | Ar pkg-name ...
+.Nm
+.Op Fl qQ
+.Fl W Ar filename
+.Nm
+.Op Fl qQ
+.Fl O Ar origin
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+command is used to dump out information for packages, either packed up in
+files with the
+.Xr pkg_create 1
+command or already installed on the system
+with the
+.Xr pkg_add 1
+command.
+.Sh OPTIONS
+The following command line options are supported:
+.Bl -tag -width indent
+.It Ar pkg-name ...
+The named packages are described.
+A package name may either be the name of
+an installed package, the pathname to a package distribution file or a
+URL to an FTP available package.
+Package version numbers can also be matched in a relational manner using the
+.Pa >= , <= , >
+and
+.Pa <
+operators.
+For example,
+.Pp
+.Dl "pkg_info 'portupgrade>=20030723'"
+.Pp
+will match versions 20030723 and later of the
+.Pa portupgrade
+package.
+.It Fl a , -all
+Show all currently installed packages.
+.It Fl b , -blocksize
+Use the
+.Ev BLOCKSIZE
+environment variable for output even when the
+.Fl q
+or
+.Fl Q
+flag is present.
+.It Fl h , -help
+Print help message.
+.It Fl v , -verbose
+Turn on verbose output.
+.It Fl p
+Show the installation prefix for each package.
+.It Fl q , -quiet
+Be
+.Dq quiet
+in emitting report headers and such, just dump the
+raw info (basically, assume a non-human reading).
+.It Fl Q
+Be
+.Dq quiet
+as above but print preface output with the package name.
+.It Fl c
+Show the (one line) comment field for each package.
+.It Fl d
+Show the long description field for each package.
+.It Fl D
+Show the install-message file for each package.
+.It Fl f
+Show the packing list instructions for each package.
+.It Fl g
+Show files that do not match the recorded checksum.
+.It Fl i
+Show the install script (if any) for each package.
+.It Fl I
+Show an index line for each package.
+This option takes
+precedence over all other package formatting options.
+.It Fl j
+Show the requirements script (if any) for each package.
+.It Fl k
+Show the de-install script (if any) for each package.
+.It Fl K , -keep
+Keep any downloaded package in
+.Ev PKGDIR
+if it is defined or in current directory by default.
+.It Fl r
+For each of the specified packages,
+show the list of packages on which it depends.
+.It Fl R
+For each of the specified packages,
+show the list of installed packages which require it.
+.It Fl m
+Show the
+.Xr mtree 8
+file (if any) for each package.
+.It Fl L
+Show the files within each package.
+This is different from just
+viewing the packing list, since full pathnames for everything
+are generated.
+.It Fl s
+Show the total size occupied by files installed within each package.
+.It Fl o
+Show the
+.Dq origin
+path recorded on package generation.
+This path is the directory name in the
+.Fx
+.Em "Ports Collection"
+of the underlying port from which the package was generated.
+.It Fl G , -no-glob
+Do not try to expand shell glob patterns in the
+.Ar pkg-name
+when selecting packages to be displayed (by default
+.Nm
+automatically expands shell glob patterns in the
+.Ar pkg-name ) .
+.It Fl W , -which Ar filename
+For the specified
+.Ar filename
+argument show which package it belongs to.
+If the file is not in the
+current directory, and does not have an absolute path, then the
+directories specified in the environment variable
+.Ev PATH
+are searched using
+.Xr which 1 .
+.It Fl O , -origin Ar origin
+List all packages having the specified
+.Ar origin .
+.It Fl x , -regex
+Treat the
+.Ar pkg-name
+as a regular expression and display information only for packages
+whose names match that regular expression.
+Multiple regular
+expressions could be provided, in that case
+.Nm
+displays information about all packages that match at least one
+regular expression from the list.
+.It Fl X , -extended
+Like
+.Fl x ,
+but treats the
+.Ar pkg-name
+as an extended regular expression.
+.It Fl e , -exists Ar package
+If the package identified by
+.Ar package
+is currently installed, return 0, otherwise return 1.
+This option
+allows you to easily test for the presence of another (perhaps
+prerequisite) package from a script.
+.It Fl E
+Show only matching package names.
+This option takes
+precedence over all other package formatting options.
+If any packages match, return 0, otherwise return 1.
+.It Fl l Ar prefix
+Prefix each information category header (see
+.Fl q )
+shown with
+.Ar prefix .
+This is primarily of use to front-end programs that want to request a
+lot of different information fields at once for a package, but do not
+necessarily want the output intermingled in such a way that they cannot
+organize it.
+This lets you add a special token to the start of
+each field.
+.It Fl t , -template Ar template
+Use
+.Ar template
+as the argument to
+.Xr mktemp 3
+when creating a
+.Dq staging area .
+By default, this is the string
+.Pa /tmp/instmp.XXXXXX ,
+but it may be necessary to override it in the situation where
+space in your
+.Pa /tmp
+directory is limited.
+Be sure to leave some number of
+.Ql X
+characters for
+.Xr mktemp 3
+to fill in with a unique ID.
+.Bd -ragged -offset indent -compact
+Note: This should really not be necessary with
+.Nm ,
+since very little information is extracted from each package
+and one would have to have a very small
+.Pa /tmp
+indeed to overflow it.
+.Ed
+.It Fl V
+Show revision number of the packing list format.
+.It Fl P , -version
+Show revision number of package tools.
+.El
+.Sh TECHNICAL DETAILS
+Package info is either extracted from package files named on the
+command line, or from already installed package information
+in
+.Pa /var/db/pkg/ Ns Aq Ar pkg-name .
+.Sh ENVIRONMENT
+.Bl -tag -width ".Ev PKG_TMPDIR"
+.It Ev BLOCKSIZE
+If the environment variable
+.Ev BLOCKSIZE
+is set the block counts will be displayed in units of that
+size block.
+.It Ev PKG_TMPDIR
+Points to the directory where
+.Nm
+creates its temporary files.
+If this variable is not set,
+.Ev TMPDIR
+is used.
+If both are unset, the builtin defaults are used.
+.It Ev PKG_DBDIR
+Specifies an alternative location for the installed package database.
+.It Ev PKG_PATH
+Specifies an alternative package location, if a given package cannot be
+found.
+.It Ev PKGDIR
+Specifies an alternative location to save downloaded packages to.
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /var/db/pkg" -compact
+.It Pa /var/tmp
+Used if the environment variables
+.Ev PKG_TMPDIR
+and
+.Ev TMPDIR
+are not set, or if the directories named have insufficient space.
+.It Pa /tmp
+The next choice if
+.Pa /var/tmp
+does not exist or has insufficient space.
+.It Pa /usr/tmp
+The last choice if
+.Pa /tmp
+is unsuitable.
+.It Pa /var/db/pkg
+Default location of the installed package database.
+.El
+.Sh SEE ALSO
+.Xr pkg_add 1 ,
+.Xr pkg_create 1 ,
+.Xr pkg_delete 1 ,
+.Xr pkg_version 1 ,
+.Xr mktemp 3 ,
+.Xr mtree 8
+.Sh AUTHORS
+.An Jordan Hubbard
+.Sh CONTRIBUTORS
+.An John Kohl Aq jtk@rational.com ,
+.An Oliver Eikemeier Aq eik@FreeBSD.org
+.Sh BUGS
+Sure to be some.
diff --git a/usr.sbin/pkg_install/info/show.c b/usr.sbin/pkg_install/info/show.c
new file mode 100644
index 0000000..c65c312
--- /dev/null
+++ b/usr.sbin/pkg_install/info/show.c
@@ -0,0 +1,381 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 23 Aug 1993
+ *
+ * Various display routines for the info module.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include "info.h"
+#include <err.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <md5.h>
+
+void
+show_file(const char *title, const char *fname)
+{
+ FILE *fp;
+ char line[1024];
+ int n;
+
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+ fp = fopen(fname, "r");
+ if (fp == (FILE *) NULL)
+ printf("ERROR: show_file: Can't open '%s' for reading!\n", fname);
+ else {
+ int append_nl = 0;
+ while ((n = fread(line, 1, 1024, fp)) != 0)
+ fwrite(line, 1, n, stdout);
+ fclose(fp);
+ append_nl = (line[n - 1] != '\n'); /* Do we have a trailing \n ? */
+ if (append_nl)
+ printf("\n");
+ }
+ printf("\n"); /* just in case */
+}
+
+void
+show_index(const char *title, const char *fname)
+{
+ FILE *fp;
+ char line[MAXINDEXSIZE+2];
+
+ strlcpy(line, "???\n", sizeof(line));
+
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+ fp = fopen(fname, "r");
+ if (fp == (FILE *) NULL) {
+ warnx("show_file: can't open '%s' for reading", fname);
+ } else {
+ if(fgets(line, MAXINDEXSIZE + 1, fp)) {
+ size_t line_length = strlen(line);
+
+ if (line[line_length - 1] != '\n') { /* Do we have a trailing \n ? */
+ line[line_length] = '\n'; /* Add a trailing \n */
+ line[line_length + 1] = '\0'; /* Terminate string */
+ }
+ }
+ fclose(fp);
+ }
+ fputs(line, stdout);
+}
+
+/* Show a packing list item type. If showall is TRUE, show all */
+void
+show_plist(const char *title, Package *plist, plist_t type, Boolean showall)
+{
+ PackingList p;
+ Boolean ign = FALSE;
+ char *prefix = NULL;
+
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+ p = plist->head;
+ while (p) {
+ if (p->type != type && showall != TRUE) {
+ p = p->next;
+ continue;
+ }
+ switch(p->type) {
+ case PLIST_FILE:
+ if (ign) {
+ printf(Quiet ? "%s\n" : "File: %s (ignored)\n", p->name);
+ ign = FALSE;
+ }
+ else
+ printf(Quiet ? "%s\n" : "File: %s\n", p->name);
+ break;
+
+ case PLIST_CWD:
+ if (!prefix)
+ prefix = p->name;
+ printf(Quiet ? "@cwd %s\n" : "\tCWD to %s\n", (p->name == NULL) ? prefix : p->name);
+ break;
+
+ case PLIST_SRC:
+ printf(Quiet ? "@srcdir %s\n" : "\tSRCDIR to %s\n", p->name);
+ break;
+
+ case PLIST_CMD:
+ printf(Quiet ? "@exec %s\n" : "\tEXEC '%s'\n", p->name);
+ break;
+
+ case PLIST_UNEXEC:
+ printf(Quiet ? "@unexec %s\n" : "\tUNEXEC '%s'\n", p->name);
+ break;
+
+ case PLIST_CHMOD:
+ printf(Quiet ? "@chmod %s\n" : "\tCHMOD to %s\n",
+ p->name ? p->name : "(clear default)");
+ break;
+
+ case PLIST_CHOWN:
+ printf(Quiet ? "@chown %s\n" : "\tCHOWN to %s\n",
+ p->name ? p->name : "(clear default)");
+ break;
+
+ case PLIST_CHGRP:
+ printf(Quiet ? "@chgrp %s\n" : "\tCHGRP to %s\n",
+ p->name ? p->name : "(clear default)");
+ break;
+
+ case PLIST_COMMENT:
+ printf(Quiet ? "@comment %s\n" : "\tComment: %s\n", p->name);
+ break;
+
+ case PLIST_NOINST:
+ printf(Quiet ? "@noinst %s\n" : "\tNot installed: %s\n", p->name);
+ break;
+
+ case PLIST_IGNORE:
+ ign = TRUE;
+ break;
+
+ case PLIST_IGNORE_INST:
+ printf(Quiet ? "@ignore_inst ??? doesn't belong here.\n" :
+ "\tIgnore next file installation directive (doesn't belong)\n");
+ ign = TRUE;
+ break;
+
+ case PLIST_NAME:
+ printf(Quiet ? "@name %s\n" : "\tPackage name: %s\n", p->name);
+ break;
+
+ case PLIST_DISPLAY:
+ printf(Quiet ? "@display %s\n" : "\tInstall message file: %s\n", p->name);
+ break;
+
+ case PLIST_PKGDEP:
+ printf(Quiet ? "@pkgdep %s\n" : "Dependency: %s\n", p->name);
+ break;
+
+ case PLIST_DEPORIGIN:
+ printf(Quiet ? "@comment DEPORIGIN:%s\n" :
+ "\tdependency origin: %s\n", p->name);
+ break;
+
+ case PLIST_CONFLICTS:
+ printf(Quiet ? "@conflicts %s\n" : "Conflicts: %s\n", p->name);
+ break;
+
+ case PLIST_MTREE:
+ printf(Quiet ? "@mtree %s\n" : "\tPackage mtree file: %s\n", p->name);
+ break;
+
+ case PLIST_DIR_RM:
+ printf(Quiet ? "@dirrm %s\n" : "\tDeinstall directory remove: %s\n", p->name);
+ break;
+
+ case PLIST_OPTION:
+ printf(Quiet ? "@option %s\n" :
+ "\tOption \"%s\" controlling package installation behaviour\n",
+ p->name);
+ break;
+
+ case PLIST_ORIGIN:
+ printf(Quiet ? "@comment ORIGIN:%s\n" :
+ "\tPackage origin: %s\n", p->name);
+ break;
+
+ default:
+ cleanup(0);
+ errx(2, "%s: unknown command type %d (%s)",
+ __func__, p->type, p->name);
+ break;
+ }
+ p = p->next;
+ }
+}
+
+/* Show all files in the packing list (except ignored ones) */
+void
+show_files(const char *title, Package *plist)
+{
+ PackingList p;
+ Boolean ign = FALSE;
+ char *prefix = NULL;
+ const char *dir = ".";
+
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+ p = plist->head;
+ while (p) {
+ switch(p->type) {
+ case PLIST_FILE:
+ if (!ign)
+ printf("%s/%s\n", dir, p->name);
+ ign = FALSE;
+ break;
+
+ case PLIST_CWD:
+ if (!prefix)
+ prefix = p->name;
+ if (p->name == NULL)
+ dir = prefix;
+ else
+ dir = p->name;
+ break;
+
+ case PLIST_IGNORE:
+ ign = TRUE;
+ break;
+
+ /* Silence GCC in the -Wall mode */
+ default:
+ break;
+ }
+ p = p->next;
+ }
+}
+
+/* Calculate and show size of all installed package files (except ignored ones) */
+void
+show_size(const char *title, Package *plist)
+{
+ PackingList p;
+ Boolean ign = FALSE;
+ const char *dir = ".";
+ struct stat sb;
+ char tmp[FILENAME_MAX];
+ unsigned long size = 0;
+ long blksize;
+ int headerlen;
+ char *descr;
+ char *prefix = NULL;
+
+ descr = getbsize(&headerlen, &blksize);
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+ for (p = plist->head; p != NULL; p = p->next) {
+ switch (p->type) {
+ case PLIST_FILE:
+ if (!ign) {
+ snprintf(tmp, FILENAME_MAX, "%s/%s", dir, p->name);
+ if (!lstat(tmp, &sb)) {
+ size += sb.st_size;
+ if (Verbose)
+ printf("%lu\t%s\n", (unsigned long) howmany(sb.st_size, blksize), tmp);
+ }
+ }
+ ign = FALSE;
+ break;
+
+ case PLIST_CWD:
+ if (!prefix)
+ prefix = p->name;
+ if (p->name == NULL)
+ dir = prefix;
+ else
+ dir = p->name;
+ break;
+
+ case PLIST_IGNORE:
+ ign = TRUE;
+ break;
+
+ /* Silence GCC in the -Wall mode */
+ default:
+ break;
+ }
+ }
+ if (!Quiet)
+ printf("%lu\t(%s)\n", howmany(size, blksize), descr);
+ else
+ if (UseBlkSz)
+ printf("%lu\n", howmany(size, blksize));
+ else
+ printf("%lu\n", size);
+}
+
+/* Show files that don't match the recorded checksum */
+void
+show_cksum(const char *title, Package *plist)
+{
+ PackingList p;
+ const char *dir = ".";
+ char *prefix = NULL;
+ char tmp[FILENAME_MAX];
+
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+
+ for (p = plist->head; p != NULL; p = p->next)
+ if (p->type == PLIST_CWD) {
+ if (!prefix)
+ prefix = p->name;
+ if (p->name == NULL)
+ dir = prefix;
+ else
+ dir = p->name;
+ } else if (p->type == PLIST_FILE) {
+ snprintf(tmp, FILENAME_MAX, "%s/%s", dir, p->name);
+ if (!fexists(tmp))
+ warnx("%s doesn't exist", tmp);
+ else if (p->next && p->next->type == PLIST_COMMENT &&
+ (strncmp(p->next->name, "MD5:", 4) == 0)) {
+ char *cp = NULL, buf[33];
+
+ /*
+ * For packing lists whose version is 1.1 or greater, the md5
+ * hash for a symlink is calculated on the string returned
+ * by readlink().
+ */
+ if (issymlink(tmp) && verscmp(plist, 1, 0) > 0) {
+ int len;
+ char linkbuf[FILENAME_MAX];
+
+ if ((len = readlink(tmp, linkbuf, FILENAME_MAX)) > 0)
+ cp = MD5Data((unsigned char *)linkbuf, len, buf);
+ } else if (isfile(tmp) || verscmp(plist, 1, 1) < 0)
+ cp = MD5File(tmp, buf);
+
+ if (cp != NULL) {
+ /* Mismatch? */
+ if (strcmp(cp, p->next->name + 4))
+ printf("%s fails the original MD5 checksum\n", tmp);
+ else if (Verbose)
+ printf("%s matched the original MD5 checksum\n", tmp);
+ }
+ }
+ }
+}
+
+/* Show an "origin" path (usually category/portname) */
+void
+show_origin(const char *title, Package *plist)
+{
+
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+ printf("%s\n", plist->origin != NULL ? plist->origin : "");
+}
+
+/* Show revision number of the packing list */
+void
+show_fmtrev(const char *title, Package *plist)
+{
+
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+ printf("%d.%d\n", plist->fmtver_maj, plist->fmtver_mnr);
+}
diff --git a/usr.sbin/pkg_install/lib/Makefile b/usr.sbin/pkg_install/lib/Makefile
new file mode 100644
index 0000000..84a41b8
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+LIB= install
+INTERNALLIB=
+SRCS= file.c msg.c plist.c str.c exec.c global.c pen.c match.c \
+ deps.c version.c pkgwrap.c url.c
+
+WARNS?= 3
+WFORMAT?= 1
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/pkg_install/lib/deps.c b/usr.sbin/pkg_install/lib/deps.c
new file mode 100644
index 0000000..66f44a9
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/deps.c
@@ -0,0 +1,241 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Maxim Sobolev
+ * 14 March 2001
+ *
+ * Routines used to do various operations with dependencies
+ * among installed packages.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include <err.h>
+#include <stdio.h>
+
+void list_deps(const char *pkgname, char **pkgs, char *listed,
+ char *check_loop, char **newpkgs, int *nrnewpkgs,
+ int *err_cnt);
+
+/*
+ * Sort given NULL-terminated list of installed packages (pkgs) in
+ * such a way that if package A depends on package B then after
+ * sorting A will be listed before B no matter how they were
+ * originally positioned in the list.
+ *
+ * Works by performing a recursive depth-first search on the
+ * required-by lists.
+ */
+
+int
+sortdeps(char **pkgs)
+{
+ int i, err_cnt=0;
+ int nrpkgs, nrnewpkgs;
+ char *listed, *check_loop, **newpkgs;
+ char *cp;
+
+ if (pkgs[0] == NULL || pkgs[1] == NULL)
+ return (0);
+
+ nrpkgs = 0;
+ while (pkgs[nrpkgs]) nrpkgs++;
+ listed = alloca(nrpkgs);
+ if (listed == NULL) {
+ warnx("%s(): alloca() failed", __func__);
+ return 1;
+ }
+ bzero(listed,nrpkgs);
+ check_loop = alloca(nrpkgs);
+ if (check_loop == NULL) {
+ warnx("%s(): alloca() failed", __func__);
+ return 1;
+ }
+ bzero(check_loop,nrpkgs);
+ newpkgs = alloca(nrpkgs*sizeof(char*));
+ if (newpkgs == NULL) {
+ warnx("%s(): alloca() failed", __func__);
+ return 1;
+ }
+ nrnewpkgs = 0;
+
+ for (i = 0; pkgs[i]; i++) if (!listed[i]) {
+ check_loop[i] = 1;
+ cp = strchr(pkgs[i], ':');
+ if (cp != NULL)
+ *cp = '\0';
+ list_deps(pkgs[i],pkgs,listed,check_loop,newpkgs,&nrnewpkgs,&err_cnt);
+ if (cp != NULL)
+ *cp = ':';
+ listed[i] = 1;
+ newpkgs[nrnewpkgs] = pkgs[i];
+ nrnewpkgs++;
+ }
+
+ if (nrnewpkgs != nrpkgs) {
+ fprintf(stderr,"This shouldn't happen, and indicates a huge error in the code.\n");
+ exit(1);
+ }
+ for (i = 0; i < nrnewpkgs; i++) pkgs[i] = newpkgs[i];
+
+ return err_cnt;
+}
+
+/*
+ * This recursive function lists the dependencies (that is, the
+ * "required-by"s) for pkgname, putting them into newpkgs.
+ */
+
+void list_deps(const char *pkgname, char **pkgs, char *listed,
+ char *check_loop, char **newpkgs, int *nrnewpkgs,
+ int *err_cnt) {
+ char **rb, **rbtmp;
+ char *cp;
+ int errcode, i, j;
+ struct reqr_by_entry *rb_entry;
+ struct reqr_by_head *rb_list;
+
+ if (isinstalledpkg(pkgname) <= 0)
+ return;
+
+ errcode = requiredby(pkgname, &rb_list, FALSE, TRUE);
+ if (errcode < 0)
+ return;
+ /*
+ * We put rb_list into an argv style NULL terminated list,
+ * because requiredby uses some static storage, and list_deps
+ * is a recursive function.
+ */
+
+ rbtmp = rb = alloca((errcode + 1) * sizeof(*rb));
+ if (rb == NULL) {
+ warnx("%s(): alloca() failed", __func__);
+ (*err_cnt)++;
+ return;
+ }
+ STAILQ_FOREACH(rb_entry, rb_list, link) {
+ *rbtmp = alloca(strlen(rb_entry->pkgname) + 1);
+ if (*rbtmp == NULL) {
+ warnx("%s(): alloca() failed", __func__);
+ (*err_cnt)++;
+ return;
+ }
+ strcpy(*rbtmp, rb_entry->pkgname);
+ rbtmp++;
+ }
+ *rbtmp = NULL;
+
+ for (i = 0; rb[i]; i++)
+ for (j = 0; pkgs[j]; j++) if (!listed[j]) {
+ cp = strchr(pkgs[j], ':');
+ if (cp != NULL)
+ *cp = '\0';
+ if (strcmp(rb[i], pkgs[j]) == 0) { /*match */
+ /*
+ * Try to avoid deadlock if package A depends on B which in
+ * turn depends on C and C due to an error depends on A.
+ * It Should Never Happen[tm] in real life.
+ */
+ if (check_loop[j]) {
+ warnx("dependency loop detected for package %s", pkgs[j]);
+ (*err_cnt)++;
+ }
+ else {
+ check_loop[j] = 1;
+ list_deps(pkgs[j],pkgs,listed,check_loop,newpkgs,nrnewpkgs,err_cnt);
+ listed[j] = 1;
+ newpkgs[*nrnewpkgs] = pkgs[j];
+ (*nrnewpkgs)++;
+ }
+ }
+ if (cp != NULL)
+ *cp = ':';
+ }
+}
+
+/*
+ * Load +REQUIRED_BY file and return a list with names of
+ * packages that require package reffered to by `pkgname'.
+ *
+ * Optionally check that packages listed there are actually
+ * installed and filter out those that don't (filter == TRUE).
+ *
+ * strict argument controls whether the caller want warnings
+ * to be emitted when there are some non-fatal conditions,
+ * i.e. package doesn't have +REQUIRED_BY file or some packages
+ * listed in +REQUIRED_BY don't exist.
+ *
+ * Result returned in the **list, while return value is equal
+ * to the number of entries in the resulting list. Print error
+ * message and return -1 on error.
+ */
+int
+requiredby(const char *pkgname, struct reqr_by_head **list, Boolean strict, Boolean filter)
+{
+ FILE *fp;
+ char fbuf[FILENAME_MAX], fname[FILENAME_MAX];
+ int retval;
+ struct reqr_by_entry *rb_entry;
+ static struct reqr_by_head rb_list = STAILQ_HEAD_INITIALIZER(rb_list);
+
+ *list = &rb_list;
+ /* Deallocate any previously allocated space */
+ while (!STAILQ_EMPTY(&rb_list)) {
+ rb_entry = STAILQ_FIRST(&rb_list);
+ STAILQ_REMOVE_HEAD(&rb_list, link);
+ free(rb_entry);
+ }
+
+ if (isinstalledpkg(pkgname) <= 0) {
+ if (strict == TRUE)
+ warnx("no such package '%s' installed", pkgname);
+ return -1;
+ }
+
+ snprintf(fname, sizeof(fname), "%s/%s/%s", LOG_DIR, pkgname,
+ REQUIRED_BY_FNAME);
+ fp = fopen(fname, "r");
+ if (fp == NULL) {
+ /* Probably pkgname doesn't have any packages that depend on it */
+ if (strict == TRUE)
+ warnx("couldn't open dependency file '%s'", fname);
+ return 0;
+ }
+
+ retval = 0;
+ while (fgets(fbuf, sizeof(fbuf), fp) != NULL) {
+ if (fbuf[strlen(fbuf) - 1] == '\n')
+ fbuf[strlen(fbuf) - 1] = '\0';
+ if (filter == TRUE && isinstalledpkg(fbuf) <= 0) {
+ if (strict == TRUE)
+ warnx("package '%s' is recorded in the '%s' but isn't "
+ "actually installed", fbuf, fname);
+ continue;
+ }
+ retval++;
+ rb_entry = malloc(sizeof(*rb_entry));
+ if (rb_entry == NULL) {
+ warnx("%s(): malloc() failed", __func__);
+ retval = -1;
+ break;
+ }
+ strlcpy(rb_entry->pkgname, fbuf, sizeof(rb_entry->pkgname));
+ STAILQ_INSERT_TAIL(&rb_list, rb_entry, link);
+ }
+ fclose(fp);
+
+ return retval;
+}
diff --git a/usr.sbin/pkg_install/lib/exec.c b/usr.sbin/pkg_install/lib/exec.c
new file mode 100644
index 0000000..fc8220c
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/exec.c
@@ -0,0 +1,106 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Miscellaneous system routines.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include <err.h>
+
+/*
+ * Unusual system() substitute. Accepts format string and args,
+ * builds and executes command. Returns exit code.
+ */
+
+int
+vsystem(const char *fmt, ...)
+{
+ va_list args;
+ char *cmd;
+ int ret, maxargs;
+
+ maxargs = sysconf(_SC_ARG_MAX);
+ maxargs -= 32; /* some slop for the sh -c */
+ cmd = malloc(maxargs);
+ if (!cmd) {
+ warnx("vsystem can't alloc arg space");
+ return 1;
+ }
+
+ va_start(args, fmt);
+ if (vsnprintf(cmd, maxargs, fmt, args) > maxargs) {
+ warnx("vsystem args are too long");
+ return 1;
+ }
+#ifdef DEBUG
+printf("Executing %s\n", cmd);
+#endif
+ ret = system(cmd);
+ va_end(args);
+ free(cmd);
+ return ret;
+}
+
+char *
+vpipe(const char *fmt, ...)
+{
+ FILE *fp;
+ char *cmd, *rp;
+ int maxargs;
+ va_list args;
+
+ rp = malloc(MAXPATHLEN);
+ if (!rp) {
+ warnx("vpipe can't alloc buffer space");
+ return NULL;
+ }
+ maxargs = sysconf(_SC_ARG_MAX);
+ maxargs -= 32; /* some slop for the sh -c */
+ cmd = alloca(maxargs);
+ if (!cmd) {
+ warnx("vpipe can't alloc arg space");
+ return NULL;
+ }
+
+ va_start(args, fmt);
+ if (vsnprintf(cmd, maxargs, fmt, args) > maxargs) {
+ warnx("vsystem args are too long");
+ return NULL;
+ }
+#ifdef DEBUG
+ fprintf(stderr, "Executing %s\n", cmd);
+#endif
+ fflush(NULL);
+ fp = popen(cmd, "r");
+ if (fp == NULL) {
+ warnx("popen() failed");
+ return NULL;
+ }
+ get_string(rp, MAXPATHLEN, fp);
+#ifdef DEBUG
+ fprintf(stderr, "Returned %s\n", rp);
+#endif
+ va_end(args);
+ if (pclose(fp) || (strlen(rp) == 0)) {
+ free(rp);
+ return NULL;
+ }
+ return rp;
+}
diff --git a/usr.sbin/pkg_install/lib/file.c b/usr.sbin/pkg_install/lib/file.c
new file mode 100644
index 0000000..0b74ddf
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/file.c
@@ -0,0 +1,427 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Miscellaneous file access utilities.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include <err.h>
+#include <pwd.h>
+#include <time.h>
+#include <sys/wait.h>
+
+/* Quick check to see if a file exists */
+Boolean
+fexists(const char *fname)
+{
+ struct stat dummy;
+ if (!lstat(fname, &dummy))
+ return TRUE;
+ return FALSE;
+}
+
+/* Quick check to see if something is a directory or symlink to a directory */
+Boolean
+isdir(const char *fname)
+{
+ struct stat sb;
+
+ if (lstat(fname, &sb) != FAIL && S_ISDIR(sb.st_mode))
+ return TRUE;
+ else if (lstat(strconcat(fname, "/."), &sb) != FAIL && S_ISDIR(sb.st_mode))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/* Check to see if file is a dir or symlink to a dir, and is empty */
+Boolean
+isemptydir(const char *fname)
+{
+ if (isdir(fname)) {
+ DIR *dirp;
+ struct dirent *dp;
+
+ dirp = opendir(fname);
+ if (!dirp)
+ return FALSE; /* no perms, leave it alone */
+ for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
+ if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, "..")) {
+ closedir(dirp);
+ return FALSE;
+ }
+ }
+ (void)closedir(dirp);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * Returns TRUE if file is a regular file or symlink pointing to a regular
+ * file
+ */
+Boolean
+isfile(const char *fname)
+{
+ struct stat sb;
+ if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode))
+ return TRUE;
+ return FALSE;
+}
+
+/*
+ * Check to see if file is a file or symlink pointing to a file and is empty.
+ * If nonexistent or not a file, say "it's empty", otherwise return TRUE if
+ * zero sized.
+ */
+Boolean
+isemptyfile(const char *fname)
+{
+ struct stat sb;
+ if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode)) {
+ if (sb.st_size != 0)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Returns TRUE if file is a symbolic link. */
+Boolean
+issymlink(const char *fname)
+{
+ struct stat sb;
+ if (lstat(fname, &sb) != FAIL && S_ISLNK(sb.st_mode))
+ return TRUE;
+ return FALSE;
+}
+
+/* Returns TRUE if file is a URL specification */
+Boolean
+isURL(const char *fname)
+{
+ /*
+ * I'm sure there are other types of URL specifications that I could
+ * also be looking for here, but for now I'll just be happy to get ftp
+ * and http working.
+ */
+ if (!fname)
+ return FALSE;
+ while (isspace(*fname))
+ ++fname;
+ if (!strncmp(fname, "ftp://", 6) || !strncmp(fname, "http://", 7) ||
+ !strncmp(fname, "https://", 8) || !strncmp(fname, "file://", 7))
+ return TRUE;
+ return FALSE;
+}
+
+char *
+fileFindByPath(const char *base, const char *fname)
+{
+ static char tmp[FILENAME_MAX];
+ char *cp;
+ const char *suffixes[] = {".tbz", ".tgz", ".tar", NULL};
+ int i;
+
+ if (fexists(fname) && isfile(fname)) {
+ strcpy(tmp, fname);
+ return tmp;
+ }
+ if (base) {
+ strcpy(tmp, base);
+
+ cp = strrchr(tmp, '/');
+ if (cp) {
+ *cp = '\0'; /* chop name */
+ cp = strrchr(tmp, '/');
+ }
+ if (cp)
+ for (i = 0; suffixes[i] != NULL; i++) {
+ *(cp + 1) = '\0';
+ strcat(cp, "All/");
+ strcat(cp, fname);
+ strcat(cp, suffixes[i]);
+ if (fexists(tmp))
+ return tmp;
+ }
+ }
+
+ cp = getenv("PKG_PATH");
+ while (cp) {
+ char *cp2 = strsep(&cp, ":");
+
+ for (i = 0; suffixes[i] != NULL; i++) {
+ snprintf(tmp, FILENAME_MAX, "%s/%s%s", cp2 ? cp2 : cp, fname, suffixes[i]);
+ if (fexists(tmp) && isfile(tmp))
+ return tmp;
+ }
+ }
+ return NULL;
+}
+
+char *
+fileGetContents(const char *fname)
+{
+ char *contents;
+ struct stat sb;
+ int fd;
+
+ if (stat(fname, &sb) == FAIL) {
+ cleanup(0);
+ errx(2, "%s: can't stat '%s'", __func__, fname);
+ }
+
+ contents = (char *)malloc(sb.st_size + 1);
+ fd = open(fname, O_RDONLY, 0);
+ if (fd == FAIL) {
+ cleanup(0);
+ errx(2, "%s: unable to open '%s' for reading", __func__, fname);
+ }
+ if (read(fd, contents, sb.st_size) != sb.st_size) {
+ cleanup(0);
+ errx(2, "%s: short read on '%s' - did not get %lld bytes", __func__,
+ fname, (long long)sb.st_size);
+ }
+ close(fd);
+ contents[sb.st_size] = '\0';
+ return contents;
+}
+
+/*
+ * Takes a filename and package name, returning (in "try") the
+ * canonical "preserve" name for it.
+ */
+Boolean
+make_preserve_name(char *try, int max, const char *name, const char *file)
+{
+ int len, i;
+
+ if ((len = strlen(file)) == 0)
+ return FALSE;
+ else
+ i = len - 1;
+ strncpy(try, file, max);
+ if (try[i] == '/') /* Catch trailing slash early and save checking in the loop */
+ --i;
+ for (; i; i--) {
+ if (try[i] == '/') {
+ try[i + 1]= '.';
+ strncpy(&try[i + 2], &file[i + 1], max - i - 2);
+ break;
+ }
+ }
+ if (!i) {
+ try[0] = '.';
+ strncpy(try + 1, file, max - 1);
+ }
+ /* I should probably be called rude names for these inline assignments */
+ strncat(try, ".", max -= strlen(try));
+ strncat(try, name, max -= strlen(name));
+ strncat(try, ".", max--);
+ strncat(try, "backup", max -= 6);
+ return TRUE;
+}
+
+/* Write the contents of "str" to a file */
+void
+write_file(const char *name, const char *str)
+{
+ FILE *fp;
+ size_t len;
+
+ fp = fopen(name, "w");
+ if (!fp) {
+ cleanup(0);
+ errx(2, "%s: cannot fopen '%s' for writing", __func__, name);
+ }
+ len = strlen(str);
+ if (fwrite(str, 1, len, fp) != len) {
+ cleanup(0);
+ errx(2, "%s: short fwrite on '%s', tried to write %ld bytes",
+ __func__, name, (long)len);
+ }
+ if (fclose(fp)) {
+ cleanup(0);
+ errx(2, "%s: failure to fclose '%s'", __func__, name);
+ }
+}
+
+void
+copy_file(const char *dir, const char *fname, const char *to)
+{
+ char cmd[FILENAME_MAX];
+
+ if (fname[0] == '/')
+ snprintf(cmd, FILENAME_MAX, "/bin/cp -r %s %s", fname, to);
+ else
+ snprintf(cmd, FILENAME_MAX, "/bin/cp -r %s/%s %s", dir, fname, to);
+ if (vsystem(cmd)) {
+ cleanup(0);
+ errx(2, "%s: could not perform '%s'", __func__, cmd);
+ }
+}
+
+void
+move_file(const char *dir, const char *fname, const char *to)
+{
+ char cmd[FILENAME_MAX];
+
+ if (fname[0] == '/')
+ snprintf(cmd, FILENAME_MAX, "/bin/mv %s %s", fname, to);
+ else
+ snprintf(cmd, FILENAME_MAX, "/bin/mv %s/%s %s", dir, fname, to);
+ if (vsystem(cmd)) {
+ cleanup(0);
+ errx(2, "%s: could not perform '%s'", __func__, cmd);
+ }
+}
+
+/*
+ * Copy a hierarchy (possibly from dir) to the current directory, or
+ * if "to" is TRUE, from the current directory to a location someplace
+ * else.
+ *
+ * Though slower, using tar to copy preserves symlinks and everything
+ * without me having to write some big hairy routine to do it.
+ */
+void
+copy_hierarchy(const char *dir, const char *fname, Boolean to)
+{
+ char cmd[FILENAME_MAX * 3];
+
+ if (!to) {
+ /* If absolute path, use it */
+ if (*fname == '/')
+ dir = "/";
+ snprintf(cmd, FILENAME_MAX * 3, "/usr/bin/tar cf - -C %s %s | /usr/bin/tar xpf -",
+ dir, fname);
+ }
+ else
+ snprintf(cmd, FILENAME_MAX * 3, "/usr/bin/tar cf - %s | /usr/bin/tar xpf - -C %s",
+ fname, dir);
+#ifdef DEBUG
+ printf("Using '%s' to copy trees.\n", cmd);
+#endif
+ if (system(cmd)) {
+ cleanup(0);
+ errx(2, "%s: could not perform '%s'", __func__, cmd);
+ }
+}
+
+/* Unpack a tar file */
+int
+unpack(const char *pkg, const char *flist)
+{
+ const char *comp, *cp;
+ char suff[80];
+
+ comp = "";
+ /*
+ * Figure out by a crude heuristic whether this or not this is probably
+ * compressed and whichever compression utility was used (gzip or bzip2).
+ */
+ if (strcmp(pkg, "-")) {
+ cp = strrchr(pkg, '.');
+ if (cp) {
+ strcpy(suff, cp + 1);
+ if (strchr(suff, 'z') || strchr(suff, 'Z')) {
+ if (strchr(suff, 'b'))
+ comp = "-j";
+ else
+ comp = "-z";
+ }
+ }
+ }
+ else
+ comp = "-j";
+ if (vsystem("/usr/bin/tar -xp %s -f '%s' %s", comp, pkg, flist ? flist : "")) {
+ warnx("tar extract of %s failed!", pkg);
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Using fmt, replace all instances of:
+ *
+ * %F With the parameter "name"
+ * %D With the parameter "dir"
+ * %B Return the directory part ("base") of %D/%F
+ * %f Return the filename part of %D/%F
+ *
+ * Does not check for overflow - caution!
+ *
+ */
+void
+format_cmd(char *buf, int max, const char *fmt, const char *dir, const char *name)
+{
+ char *cp, scratch[FILENAME_MAX * 2];
+ int l;
+
+ while (*fmt && max > 0) {
+ if (*fmt == '%') {
+ switch (*++fmt) {
+ case 'F':
+ strncpy(buf, name, max);
+ l = strlen(name);
+ buf += l, max -= l;
+ break;
+
+ case 'D':
+ strncpy(buf, dir, max);
+ l = strlen(dir);
+ buf += l, max -= l;
+ break;
+
+ case 'B':
+ snprintf(scratch, FILENAME_MAX * 2, "%s/%s", dir, name);
+ cp = &scratch[strlen(scratch) - 1];
+ while (cp != scratch && *cp != '/')
+ --cp;
+ *cp = '\0';
+ strncpy(buf, scratch, max);
+ l = strlen(scratch);
+ buf += l, max -= l;
+ break;
+
+ case 'f':
+ snprintf(scratch, FILENAME_MAX * 2, "%s/%s", dir, name);
+ cp = &scratch[strlen(scratch) - 1];
+ while (cp != scratch && *(cp - 1) != '/')
+ --cp;
+ strncpy(buf, cp, max);
+ l = strlen(cp);
+ buf += l, max -= l;
+ break;
+
+ default:
+ *buf++ = *fmt;
+ --max;
+ break;
+ }
+ ++fmt;
+ }
+ else {
+ *buf++ = *fmt++;
+ --max;
+ }
+ }
+ *buf = '\0';
+}
diff --git a/usr.sbin/pkg_install/lib/global.c b/usr.sbin/pkg_install/lib/global.c
new file mode 100644
index 0000000..e136ec8
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/global.c
@@ -0,0 +1,32 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+
+ * 18 July 1993
+ *
+ * Semi-convenient place to stick some needed globals.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+
+/* These are global for all utils */
+Boolean Quiet = FALSE;
+Boolean Fake = FALSE;
+Boolean Force = FALSE;
+int AutoAnswer = FALSE;
+int Verbose = 0; /* Allow multiple levels of verbose. */
diff --git a/usr.sbin/pkg_install/lib/lib.h b/usr.sbin/pkg_install/lib/lib.h
new file mode 100644
index 0000000..422912e
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/lib.h
@@ -0,0 +1,248 @@
+/* $FreeBSD$ */
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Include and define various things wanted by the library routines.
+ *
+ */
+
+#ifndef _INST_LIB_LIB_H_
+#define _INST_LIB_LIB_H_
+
+/* Includes */
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/queue.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* Macros */
+#define SUCCESS (0)
+#define FAIL (-1)
+
+#ifndef TRUE
+#define TRUE (1)
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#define YES 2
+#define NO 1
+
+/* Usually "rm", but often "echo" during debugging! */
+#define REMOVE_CMD "/bin/rm"
+
+/* Usually "rm", but often "echo" during debugging! */
+#define RMDIR_CMD "/bin/rmdir"
+
+/* Where the ports lives by default */
+#define DEF_PORTS_DIR "/usr/ports"
+/* just in case we change the environment variable name */
+#define PORTSDIR "PORTSDIR"
+/* macro to get name of directory where the ports lives */
+#define PORTS_DIR (getenv(PORTSDIR) ? getenv(PORTSDIR) : DEF_PORTS_DIR)
+
+/* Where we put logging information by default, else ${PKG_DBDIR} if set */
+#define DEF_LOG_DIR "/var/db/pkg"
+/* just in case we change the environment variable name */
+#define PKG_DBDIR "PKG_DBDIR"
+/* macro to get name of directory where we put logging information */
+#define LOG_DIR (getenv(PKG_DBDIR) ? getenv(PKG_DBDIR) : DEF_LOG_DIR)
+
+/* The names of our "special" files */
+#define CONTENTS_FNAME "+CONTENTS"
+#define COMMENT_FNAME "+COMMENT"
+#define DESC_FNAME "+DESC"
+#define INSTALL_FNAME "+INSTALL"
+#define POST_INSTALL_FNAME "+POST-INSTALL"
+#define DEINSTALL_FNAME "+DEINSTALL"
+#define POST_DEINSTALL_FNAME "+POST-DEINSTALL"
+#define REQUIRE_FNAME "+REQUIRE"
+#define REQUIRED_BY_FNAME "+REQUIRED_BY"
+#define DISPLAY_FNAME "+DISPLAY"
+#define MTREE_FNAME "+MTREE_DIRS"
+
+#if defined(__FreeBSD_version) && __FreeBSD_version >= 800000
+#define INDEX_FNAME "INDEX-8"
+#elif defined(__FreeBSD_version) && __FreeBSD_version >= 700000
+#define INDEX_FNAME "INDEX-7"
+#elif defined(__FreeBSD_version) && __FreeBSD_version >= 600000
+#define INDEX_FNAME "INDEX-6"
+#elif defined(__FreeBSD_version) && __FreeBSD_version >= 500036
+#define INDEX_FNAME "INDEX-5"
+#else
+#define INDEX_FNAME "INDEX"
+#endif
+
+#define CMD_CHAR '@' /* prefix for extended PLIST cmd */
+
+/* The name of the "prefix" environment variable given to scripts */
+#define PKG_PREFIX_VNAME "PKG_PREFIX"
+
+/*
+ * Version of the package tools - increase only when some
+ * functionality used by bsd.port.mk is changed, added or removed
+ */
+#define PKG_INSTALL_VERSION 20090106
+
+#define PKG_WRAPCONF_FNAME "/var/db/pkg_install.conf"
+#define main(argc, argv) real_main(argc, argv)
+
+/* Version numbers to assist with changes in package file format */
+#define PLIST_FMT_VER_MAJOR 1
+#define PLIST_FMT_VER_MINOR 1
+
+enum _plist_t {
+ PLIST_FILE, PLIST_CWD, PLIST_CMD, PLIST_CHMOD,
+ PLIST_CHOWN, PLIST_CHGRP, PLIST_COMMENT, PLIST_IGNORE,
+ PLIST_NAME, PLIST_UNEXEC, PLIST_SRC, PLIST_DISPLAY,
+ PLIST_PKGDEP, PLIST_CONFLICTS, PLIST_MTREE, PLIST_DIR_RM,
+ PLIST_IGNORE_INST, PLIST_OPTION, PLIST_ORIGIN, PLIST_DEPORIGIN,
+ PLIST_NOINST
+};
+typedef enum _plist_t plist_t;
+
+enum _match_t {
+ MATCH_ALL, MATCH_EXACT, MATCH_GLOB, MATCH_NGLOB, MATCH_EREGEX, MATCH_REGEX
+};
+typedef enum _match_t match_t;
+
+/* Types */
+typedef unsigned int Boolean;
+
+struct _plist {
+ struct _plist *prev, *next;
+ char *name;
+ Boolean marked;
+ plist_t type;
+};
+typedef struct _plist *PackingList;
+
+struct _pack {
+ struct _plist *head, *tail;
+ const char *name;
+ const char *origin;
+ int fmtver_maj, fmtver_mnr;
+};
+typedef struct _pack Package;
+
+struct reqr_by_entry {
+ STAILQ_ENTRY(reqr_by_entry) link;
+ char pkgname[PATH_MAX];
+};
+STAILQ_HEAD(reqr_by_head, reqr_by_entry);
+
+/* Prototypes */
+/* Misc */
+int vsystem(const char *, ...);
+char *vpipe(const char *, ...);
+void cleanup(int);
+char *make_playpen(char *, off_t);
+char *where_playpen(void);
+void leave_playpen(void);
+off_t min_free(const char *);
+
+/* String */
+char *get_dash_string(char **);
+char *copy_string(const char *);
+char *copy_string_adds_newline(const char *);
+Boolean suffix(const char *, const char *);
+void nuke_suffix(char *);
+void str_lowercase(char *);
+char *strconcat(const char *, const char *);
+char *get_string(char *, int, FILE *);
+
+/* File */
+Boolean fexists(const char *);
+Boolean isdir(const char *);
+Boolean isemptydir(const char *fname);
+Boolean isemptyfile(const char *fname);
+Boolean isfile(const char *);
+Boolean isempty(const char *);
+Boolean issymlink(const char *);
+Boolean isURL(const char *);
+char *fileGetURL(const char *, const char *, int);
+char *fileFindByPath(const char *, const char *);
+char *fileGetContents(const char *);
+void write_file(const char *, const char *);
+void copy_file(const char *, const char *, const char *);
+void move_file(const char *, const char *, const char *);
+void copy_hierarchy(const char *, const char *, Boolean);
+int delete_hierarchy(const char *, Boolean, Boolean);
+int unpack(const char *, const char *);
+void format_cmd(char *, int, const char *, const char *, const char *);
+
+/* Msg */
+void upchuck(const char *);
+void barf(const char *, ...);
+void whinge(const char *, ...);
+Boolean y_or_n(Boolean, const char *, ...);
+
+/* Packing list */
+PackingList new_plist_entry(void);
+PackingList last_plist(Package *);
+PackingList find_plist(Package *, plist_t);
+char *find_plist_option(Package *, const char *name);
+void plist_delete(Package *, Boolean, plist_t, const char *);
+void free_plist(Package *);
+void mark_plist(Package *);
+void csum_plist_entry(char *, PackingList);
+void add_plist(Package *, plist_t, const char *);
+void add_plist_top(Package *, plist_t, const char *);
+void delete_plist(Package *pkg, Boolean all, plist_t type, const char *name);
+void write_plist(Package *, FILE *);
+void read_plist(Package *, FILE *);
+int plist_cmd(const char *, char **);
+int delete_package(Boolean, Boolean, Package *);
+Boolean make_preserve_name(char *, int, const char *, const char *);
+
+/* For all */
+int pkg_perform(char **);
+int real_main(int, char **);
+
+/* Query installed packages */
+char **matchinstalled(match_t, char **, int *);
+char **matchbyorigin(const char *, int *);
+char ***matchallbyorigin(const char **, int *);
+int isinstalledpkg(const char *name);
+int pattern_match(match_t MatchType, char *pattern, const char *pkgname);
+
+/* Dependencies */
+int sortdeps(char **);
+int chkifdepends(const char *, const char *);
+int requiredby(const char *, struct reqr_by_head **, Boolean, Boolean);
+
+/* Version */
+int verscmp(Package *, int, int);
+int version_cmp(const char *, const char *);
+
+/* Externs */
+extern Boolean Quiet;
+extern Boolean Fake;
+extern Boolean Force;
+extern int AutoAnswer;
+extern int Verbose;
+
+#endif /* _INST_LIB_LIB_H_ */
diff --git a/usr.sbin/pkg_install/lib/match.c b/usr.sbin/pkg_install/lib/match.c
new file mode 100644
index 0000000..1f8b02a
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/match.c
@@ -0,0 +1,603 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Maxim Sobolev
+ * 24 February 2001
+ *
+ * Routines used to query installed packages.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include <err.h>
+#include <fnmatch.h>
+#include <fts.h>
+#include <regex.h>
+
+/*
+ * Simple structure representing argv-like
+ * NULL-terminated list.
+ */
+struct store {
+ int currlen;
+ int used;
+ char **store;
+};
+
+static int rex_match(const char *, const char *, int);
+static int csh_match(const char *, const char *, int);
+struct store *storecreate(struct store *);
+static int storeappend(struct store *, const char *);
+static int fname_cmp(const FTSENT * const *, const FTSENT * const *);
+
+/*
+ * Function to query names of installed packages.
+ * MatchType - one of MATCH_ALL, MATCH_EREGEX, MATCH_REGEX, MATCH_GLOB, MATCH_NGLOB;
+ * patterns - NULL-terminated list of glob or regex patterns
+ * (could be NULL for MATCH_ALL);
+ * retval - return value (could be NULL if you don't want/need
+ * return value).
+ * Returns NULL-terminated list with matching names.
+ * Names in list returned are dynamically allocated and should
+ * not be altered by the caller.
+ */
+char **
+matchinstalled(match_t MatchType, char **patterns, int *retval)
+{
+ int i, errcode, len;
+ char *matched;
+ const char *paths[2] = {LOG_DIR, NULL};
+ static struct store *store = NULL;
+ FTS *ftsp;
+ FTSENT *f;
+ Boolean *lmatched = NULL;
+
+ store = storecreate(store);
+ if (store == NULL) {
+ if (retval != NULL)
+ *retval = 1;
+ return NULL;
+ }
+
+ if (retval != NULL)
+ *retval = 0;
+
+ if (!isdir(paths[0])) {
+ if (retval != NULL)
+ *retval = 1;
+ return NULL;
+ /* Not reached */
+ }
+
+ /* Count number of patterns */
+ if (patterns != NULL) {
+ for (len = 0; patterns[len]; len++) {}
+ lmatched = alloca(sizeof(*lmatched) * len);
+ if (lmatched == NULL) {
+ warnx("%s(): alloca() failed", __func__);
+ if (retval != NULL)
+ *retval = 1;
+ return NULL;
+ }
+ } else
+ len = 0;
+
+ for (i = 0; i < len; i++)
+ lmatched[i] = FALSE;
+
+ ftsp = fts_open((char * const *)(uintptr_t)paths, FTS_LOGICAL | FTS_NOCHDIR | FTS_NOSTAT, fname_cmp);
+ if (ftsp != NULL) {
+ while ((f = fts_read(ftsp)) != NULL) {
+ if (f->fts_info == FTS_D && f->fts_level == 1) {
+ fts_set(ftsp, f, FTS_SKIP);
+ matched = NULL;
+ errcode = 0;
+ if (MatchType == MATCH_ALL)
+ matched = f->fts_name;
+ else
+ for (i = 0; patterns[i]; i++) {
+ errcode = pattern_match(MatchType, patterns[i], f->fts_name);
+ if (errcode == 1) {
+ matched = f->fts_name;
+ lmatched[i] = TRUE;
+ errcode = 0;
+ }
+ if (matched != NULL || errcode != 0)
+ break;
+ }
+ if (errcode == 0 && matched != NULL)
+ errcode = storeappend(store, matched);
+ if (errcode != 0) {
+ if (retval != NULL)
+ *retval = 1;
+ return NULL;
+ /* Not reached */
+ }
+ }
+ }
+ fts_close(ftsp);
+ }
+
+ if (MatchType == MATCH_GLOB) {
+ for (i = 0; i < len; i++)
+ if (lmatched[i] == FALSE)
+ storeappend(store, patterns[i]);
+ }
+
+ if (store->used == 0)
+ return NULL;
+ else
+ return store->store;
+}
+
+int
+pattern_match(match_t MatchType, char *pattern, const char *pkgname)
+{
+ int errcode = 0;
+ const char *fname = pkgname;
+ char basefname[PATH_MAX];
+ char condchar = '\0';
+ char *condition;
+
+ /* do we have an appended condition? */
+ condition = strpbrk(pattern, "<>=");
+ if (condition) {
+ const char *ch;
+ /* yes, isolate the pattern from the condition ... */
+ if (condition > pattern && condition[-1] == '!')
+ condition--;
+ condchar = *condition;
+ *condition = '\0';
+ /* ... and compare the name without version */
+ ch = strrchr(fname, '-');
+ if (ch && ch - fname < PATH_MAX) {
+ strlcpy(basefname, fname, ch - fname + 1);
+ fname = basefname;
+ }
+ }
+
+ switch (MatchType) {
+ case MATCH_EREGEX:
+ case MATCH_REGEX:
+ errcode = rex_match(pattern, fname, MatchType == MATCH_EREGEX ? 1 : 0);
+ break;
+ case MATCH_NGLOB:
+ case MATCH_GLOB:
+ errcode = (csh_match(pattern, fname, 0) == 0) ? 1 : 0;
+ break;
+ case MATCH_EXACT:
+ errcode = (strcmp(pattern, fname) == 0) ? 1 : 0;
+ break;
+ case MATCH_ALL:
+ errcode = 1;
+ break;
+ default:
+ break;
+ }
+
+ /* loop over all appended conditions */
+ while (condition) {
+ /* restore the pattern */
+ *condition = condchar;
+ /* parse the condition (fun with bits) */
+ if (errcode == 1) {
+ char *nextcondition;
+ /* compare version numbers */
+ int match = 0;
+ if (*++condition == '=') {
+ match = 2;
+ condition++;
+ }
+ switch(condchar) {
+ case '<':
+ match |= 1;
+ break;
+ case '>':
+ match |= 4;
+ break;
+ case '=':
+ match |= 2;
+ break;
+ case '!':
+ match = 5;
+ break;
+ }
+ /* isolate the version number from the next condition ... */
+ nextcondition = strpbrk(condition, "<>=!");
+ if (nextcondition) {
+ condchar = *nextcondition;
+ *nextcondition = '\0';
+ }
+ /* and compare the versions (version_cmp removes the filename for us) */
+ if ((match & (1 << (version_cmp(pkgname, condition) + 1))) == 0)
+ errcode = 0;
+ condition = nextcondition;
+ } else {
+ break;
+ }
+ }
+
+ return errcode;
+}
+
+/*
+ * Synopsis is similar to matchinstalled(), but use origin
+ * as a key for matching packages.
+ */
+char ***
+matchallbyorigin(const char **origins, int *retval)
+{
+ char **installed, **allorigins = NULL;
+ char ***matches = NULL;
+ int i, j;
+
+ if (retval != NULL)
+ *retval = 0;
+
+ installed = matchinstalled(MATCH_ALL, NULL, retval);
+ if (installed == NULL)
+ return NULL;
+
+ /* Gather origins for all installed packages */
+ for (i = 0; installed[i] != NULL; i++) {
+ FILE *fp;
+ char *buf, *cp, tmp[PATH_MAX];
+ int cmd;
+
+ allorigins = realloc(allorigins, (i + 1) * sizeof(*allorigins));
+ allorigins[i] = NULL;
+
+ snprintf(tmp, PATH_MAX, "%s/%s", LOG_DIR, installed[i]);
+ /*
+ * SPECIAL CASE: ignore empty dirs, since we can can see them
+ * during port installation.
+ */
+ if (isemptydir(tmp))
+ continue;
+ snprintf(tmp, PATH_MAX, "%s/%s", tmp, CONTENTS_FNAME);
+ fp = fopen(tmp, "r");
+ if (fp == NULL) {
+ warnx("the package info for package '%s' is corrupt", installed[i]);
+ continue;
+ }
+
+ cmd = -1;
+ while (fgets(tmp, sizeof(tmp), fp)) {
+ int len = strlen(tmp);
+
+ while (len && isspace(tmp[len - 1]))
+ tmp[--len] = '\0';
+ if (!len)
+ continue;
+ cp = tmp;
+ if (tmp[0] != CMD_CHAR)
+ continue;
+ cmd = plist_cmd(tmp + 1, &cp);
+ if (cmd == PLIST_ORIGIN) {
+ asprintf(&buf, "%s", cp);
+ allorigins[i] = buf;
+ break;
+ }
+ }
+ if (cmd != PLIST_ORIGIN && ( Verbose || 0 != strncmp("bsdpan-", installed[i], 7 ) ) )
+ warnx("package %s has no origin recorded", installed[i]);
+ fclose(fp);
+ }
+
+ /* Resolve origins into package names, retaining the sequence */
+ for (i = 0; origins[i] != NULL; i++) {
+ matches = realloc(matches, (i + 1) * sizeof(*matches));
+ struct store *store = NULL;
+ store = storecreate(store);
+
+ for (j = 0; installed[j] != NULL; j++) {
+ if (allorigins[j]) {
+ if (csh_match(origins[i], allorigins[j], FNM_PATHNAME) == 0) {
+ storeappend(store, installed[j]);
+ }
+ }
+ }
+ if (store->used == 0)
+ matches[i] = NULL;
+ else
+ matches[i] = store->store;
+ }
+
+ if (allorigins) {
+ for (i = 0; installed[i] != NULL; i++)
+ if (allorigins[i])
+ free(allorigins[i]);
+ free(allorigins);
+ }
+
+ return matches;
+}
+
+/*
+ * Synopsis is similar to matchinstalled(), but use origin
+ * as a key for matching packages.
+ */
+char **
+matchbyorigin(const char *origin, int *retval)
+{
+ const char *origins[2];
+ char ***tmp;
+
+ origins[0] = origin;
+ origins[1] = NULL;
+
+ tmp = matchallbyorigin(origins, retval);
+ if (tmp && tmp[0]) {
+ return tmp[0];
+ } else {
+ return NULL;
+ }
+}
+
+/*
+ * Small linked list to memoize results of isinstalledpkg(). A hash table
+ * would be faster but for n ~= 1000 may be overkill.
+ */
+struct iip_memo {
+ LIST_ENTRY(iip_memo) iip_link;
+ char *iip_name;
+ int iip_result;
+};
+LIST_HEAD(, iip_memo) iip_memo = LIST_HEAD_INITIALIZER(iip_memo);
+
+/*
+ *
+ * Return 1 if the specified package is installed,
+ * 0 if not, and -1 if an error occured.
+ */
+int
+isinstalledpkg(const char *name)
+{
+ int result;
+ char *buf, *buf2;
+ struct iip_memo *memo;
+
+ LIST_FOREACH(memo, &iip_memo, iip_link) {
+ if (strcmp(memo->iip_name, name) == 0)
+ return memo->iip_result;
+ }
+
+ buf2 = NULL;
+ asprintf(&buf, "%s/%s", LOG_DIR, name);
+ if (buf == NULL)
+ goto errout;
+ if (!isdir(buf) || access(buf, R_OK) == FAIL) {
+ result = 0;
+ } else {
+ asprintf(&buf2, "%s/%s", buf, CONTENTS_FNAME);
+ if (buf2 == NULL)
+ goto errout;
+
+ if (!isfile(buf2) || access(buf2, R_OK) == FAIL)
+ result = -1;
+ else
+ result = 1;
+ }
+
+ free(buf);
+ buf = strdup(name);
+ if (buf == NULL)
+ goto errout;
+ free(buf2);
+ buf2 = NULL;
+
+ memo = malloc(sizeof *memo);
+ if (memo == NULL)
+ goto errout;
+ memo->iip_name = buf;
+ memo->iip_result = result;
+ LIST_INSERT_HEAD(&iip_memo, memo, iip_link);
+ return result;
+
+errout:
+ if (buf != NULL)
+ free(buf);
+ if (buf2 != NULL)
+ free(buf2);
+ return -1;
+}
+
+/*
+ * Returns 1 if specified pkgname matches RE pattern.
+ * Otherwise returns 0 if doesn't match or -1 if RE
+ * engine reported an error (usually invalid syntax).
+ */
+static int
+rex_match(const char *pattern, const char *pkgname, int extended)
+{
+ char errbuf[128];
+ int errcode;
+ int retval;
+ regex_t rex;
+
+ retval = 0;
+
+ errcode = regcomp(&rex, pattern, (extended ? REG_EXTENDED : REG_BASIC) | REG_NOSUB);
+ if (errcode == 0)
+ errcode = regexec(&rex, pkgname, 0, NULL, 0);
+
+ if (errcode == 0) {
+ retval = 1;
+ } else if (errcode != REG_NOMATCH) {
+ regerror(errcode, &rex, errbuf, sizeof(errbuf));
+ warnx("%s: %s", pattern, errbuf);
+ retval = -1;
+ }
+
+ regfree(&rex);
+
+ return retval;
+}
+
+/*
+ * Match string by a csh-style glob pattern. Returns 0 on
+ * match and FNM_NOMATCH otherwise, to be compatible with
+ * fnmatch(3).
+ */
+static int
+csh_match(const char *pattern, const char *string, int flags)
+{
+ int ret = FNM_NOMATCH;
+
+
+ const char *nextchoice = pattern;
+ const char *current = NULL;
+
+ int prefixlen = -1;
+ int currentlen = 0;
+
+ int level = 0;
+
+ do {
+ const char *pos = nextchoice;
+ const char *postfix = NULL;
+
+ Boolean quoted = FALSE;
+
+ nextchoice = NULL;
+
+ do {
+ const char *eb;
+ if (!*pos) {
+ postfix = pos;
+ } else if (quoted) {
+ quoted = FALSE;
+ } else {
+ switch (*pos) {
+ case '{':
+ ++level;
+ if (level == 1) {
+ current = pos+1;
+ prefixlen = pos-pattern;
+ }
+ break;
+ case ',':
+ if (level == 1 && !nextchoice) {
+ nextchoice = pos+1;
+ currentlen = pos-current;
+ }
+ break;
+ case '}':
+ if (level == 1) {
+ postfix = pos+1;
+ if (!nextchoice)
+ currentlen = pos-current;
+ }
+ level--;
+ break;
+ case '[':
+ eb = pos+1;
+ if (*eb == '!' || *eb == '^')
+ eb++;
+ if (*eb == ']')
+ eb++;
+ while(*eb && *eb != ']')
+ eb++;
+ if (*eb)
+ pos=eb;
+ break;
+ case '\\':
+ quoted = TRUE;
+ break;
+ default:
+ ;
+ }
+ }
+ pos++;
+ } while (!postfix);
+
+ if (current) {
+ char buf[FILENAME_MAX];
+ snprintf(buf, sizeof(buf), "%.*s%.*s%s", prefixlen, pattern, currentlen, current, postfix);
+ ret = csh_match(buf, string, flags);
+ if (ret) {
+ current = nextchoice;
+ level = 1;
+ } else
+ current = NULL;
+ } else
+ ret = fnmatch(pattern, string, flags);
+ } while (current);
+
+ return ret;
+}
+
+/*
+ * Create an empty store, optionally deallocating
+ * any previously allocated space if store != NULL.
+ */
+struct store *
+storecreate(struct store *store)
+{
+ int i;
+
+ if (store == NULL) {
+ store = malloc(sizeof *store);
+ if (store == NULL) {
+ warnx("%s(): malloc() failed", __func__);
+ return NULL;
+ }
+ store->currlen = 0;
+ store->store = NULL;
+ } else if (store->store != NULL) {
+ /* Free previously allocated memory */
+ for (i = 0; store->store[i] != NULL; i++)
+ free(store->store[i]);
+ store->store[0] = NULL;
+ }
+ store->used = 0;
+
+ return store;
+}
+
+/*
+ * Append specified element to the provided store.
+ */
+static int
+storeappend(struct store *store, const char *item)
+{
+ if (store->used + 2 > store->currlen) {
+ store->currlen += 16;
+ store->store = reallocf(store->store,
+ store->currlen * sizeof(*(store->store)));
+ if (store->store == NULL) {
+ store->currlen = 0;
+ warnx("%s(): reallocf() failed", __func__);
+ return 1;
+ }
+ }
+
+ asprintf(&(store->store[store->used]), "%s", item);
+ if (store->store[store->used] == NULL) {
+ warnx("%s(): malloc() failed", __func__);
+ return 1;
+ }
+ store->used++;
+ store->store[store->used] = NULL;
+
+ return 0;
+}
+
+static int
+fname_cmp(const FTSENT * const *a, const FTSENT * const *b)
+{
+ return strcmp((*a)->fts_name, (*b)->fts_name);
+}
diff --git a/usr.sbin/pkg_install/lib/msg.c b/usr.sbin/pkg_install/lib/msg.c
new file mode 100644
index 0000000..5b17624
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/msg.c
@@ -0,0 +1,75 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Miscellaneous message routines.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include <err.h>
+#include <paths.h>
+
+/* Die a relatively simple death */
+void
+upchuck(const char *message)
+{
+ cleanup(0);
+ errx(1, "fatal error during execution: %s", message);
+}
+
+/*
+ * As a yes/no question, prompting from the varargs string and using
+ * default if user just hits return.
+ */
+Boolean
+y_or_n(Boolean def, const char *msg, ...)
+{
+ va_list args;
+ int ch = 0;
+ FILE *tty;
+
+ va_start(args, msg);
+ /*
+ * Need to open /dev/tty because file collection may have been
+ * collected on stdin
+ */
+ tty = fopen(_PATH_TTY, "r");
+ if (!tty) {
+ cleanup(0);
+ errx(2, "can't open %s!", _PATH_TTY);
+ }
+ while (ch != 'Y' && ch != 'N') {
+ vfprintf(stderr, msg, args);
+ if (def)
+ fprintf(stderr, " [yes]? ");
+ else
+ fprintf(stderr, " [no]? ");
+ fflush(stderr);
+ if (AutoAnswer) {
+ ch = (AutoAnswer == YES) ? 'Y' : 'N';
+ fprintf(stderr, "%c\n", ch);
+ }
+ else
+ ch = toupper(fgetc(tty));
+ if (ch == '\n')
+ ch = (def) ? 'Y' : 'N';
+ }
+ fclose(tty) ;
+ return (ch == 'Y') ? TRUE : FALSE;
+}
diff --git a/usr.sbin/pkg_install/lib/pen.c b/usr.sbin/pkg_install/lib/pen.c
new file mode 100644
index 0000000..fef3f9d
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/pen.c
@@ -0,0 +1,187 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Routines for managing the "play pen".
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include <err.h>
+#include <libutil.h>
+#include <libgen.h>
+#include <sys/signal.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+
+/* For keeping track of where we are */
+static char PenLocation[FILENAME_MAX];
+static char Previous[FILENAME_MAX];
+
+char *
+where_playpen(void)
+{
+ return PenLocation;
+}
+
+/* Find a good place to play. */
+static char *
+find_play_pen(char *pen, off_t sz)
+{
+ char *cp;
+ struct stat sb;
+ char humbuf[6];
+
+ if (pen[0] && isdir(dirname(pen)) == TRUE && (min_free(dirname(pen)) >= sz))
+ return pen;
+ else if ((cp = getenv("PKG_TMPDIR")) != NULL && stat(cp, &sb) != FAIL && (min_free(cp) >= sz))
+ sprintf(pen, "%s/instmp.XXXXXX", cp);
+ else if ((cp = getenv("TMPDIR")) != NULL && stat(cp, &sb) != FAIL && (min_free(cp) >= sz))
+ sprintf(pen, "%s/instmp.XXXXXX", cp);
+ else if (stat("/var/tmp", &sb) != FAIL && min_free("/var/tmp") >= sz)
+ strcpy(pen, "/var/tmp/instmp.XXXXXX");
+ else if (stat("/tmp", &sb) != FAIL && min_free("/tmp") >= sz)
+ strcpy(pen, "/tmp/instmp.XXXXXX");
+ else if ((stat("/usr/tmp", &sb) == SUCCESS || mkdir("/usr/tmp", 01777) == SUCCESS) && min_free("/usr/tmp") >= sz)
+ strcpy(pen, "/usr/tmp/instmp.XXXXXX");
+ else {
+ cleanup(0);
+ humanize_number(humbuf, sizeof humbuf, sz, "", HN_AUTOSCALE,
+ HN_NOSPACE);
+ errx(2,
+"%s: can't find enough temporary space to extract the files, please set your\n"
+"PKG_TMPDIR environment variable to a location with at least %s bytes\n"
+"free", __func__, humbuf);
+ return NULL;
+ }
+ return pen;
+}
+
+#define MAX_STACK 20
+static char *pstack[MAX_STACK];
+static int pdepth = -1;
+
+static void
+pushPen(const char *pen)
+{
+ if (++pdepth == MAX_STACK)
+ errx(2, "%s: stack overflow.\n", __func__);
+ pstack[pdepth] = strdup(pen);
+}
+
+static void
+popPen(char *pen)
+{
+ if (pdepth == -1) {
+ pen[0] = '\0';
+ return;
+ }
+ strcpy(pen, pstack[pdepth]);
+ free(pstack[pdepth--]);
+}
+
+/*
+ * Make a temporary directory to play in and chdir() to it, returning
+ * pathname of previous working directory.
+ */
+char *
+make_playpen(char *pen, off_t sz)
+{
+ char humbuf1[6], humbuf2[6];
+
+ if (!find_play_pen(pen, sz))
+ return NULL;
+
+ if (!mkdtemp(pen)) {
+ cleanup(0);
+ errx(2, "%s: can't mktemp '%s'", __func__, pen);
+ }
+ if (chmod(pen, 0700) == FAIL) {
+ cleanup(0);
+ errx(2, "%s: can't mkdir '%s'", __func__, pen);
+ }
+
+ if (Verbose) {
+ if (sz) {
+ humanize_number(humbuf1, sizeof humbuf1, sz, "", HN_AUTOSCALE,
+ HN_NOSPACE);
+ humanize_number(humbuf2, sizeof humbuf2, min_free(pen),
+ "", HN_AUTOSCALE, HN_NOSPACE);
+ fprintf(stderr, "Requested space: %s bytes, free space: %s bytes in %s\n", humbuf1, humbuf2, pen);
+ }
+ }
+
+ if (min_free(pen) < sz) {
+ rmdir(pen);
+ cleanup(0);
+ errx(2, "%s: not enough free space to create '%s'.\n"
+ "Please set your PKG_TMPDIR environment variable to a location\n"
+ "with more space and\ntry the command again", __func__, pen);
+ }
+
+ if (!getcwd(Previous, FILENAME_MAX)) {
+ upchuck("getcwd");
+ return NULL;
+ }
+
+ if (chdir(pen) == FAIL) {
+ cleanup(0);
+ errx(2, "%s: can't chdir to '%s'", __func__, pen);
+ }
+
+ if (PenLocation[0])
+ pushPen(PenLocation);
+
+ strcpy(PenLocation, pen);
+ return Previous;
+}
+
+/* Convenience routine for getting out of playpen */
+void
+leave_playpen()
+{
+ void (*oldsig)(int);
+
+ /* Don't interrupt while we're cleaning up */
+ oldsig = signal(SIGINT, SIG_IGN);
+ if (Previous[0]) {
+ if (chdir(Previous) == FAIL) {
+ cleanup(0);
+ errx(2, "%s: can't chdir back to '%s'", __func__, Previous);
+ }
+ Previous[0] = '\0';
+ }
+ if (PenLocation[0]) {
+ if (PenLocation[0] == '/' && vsystem("/bin/rm -rf %s", PenLocation))
+ warnx("couldn't remove temporary dir '%s'", PenLocation);
+ popPen(PenLocation);
+ }
+ signal(SIGINT, oldsig);
+}
+
+off_t
+min_free(const char *tmpdir)
+{
+ struct statfs buf;
+
+ if (statfs(tmpdir, &buf) != 0) {
+ warn("statfs");
+ return -1;
+ }
+ return (off_t)buf.f_bavail * (off_t)buf.f_bsize;
+}
diff --git a/usr.sbin/pkg_install/lib/pkgwrap.c b/usr.sbin/pkg_install/lib/pkgwrap.c
new file mode 100644
index 0000000..cbd15cd
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/pkgwrap.c
@@ -0,0 +1,89 @@
+/*
+ * FreeBSD install - a package for the installation and maintenance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Maxim Sobolev
+ * 8 September 2002
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#undef main
+
+#define SEPARATORS " \t"
+
+extern char **environ;
+
+int
+main(int argc, char **argv)
+{
+ FILE *f;
+ char buffer[FILENAME_MAX], *cp, *verstr;
+ int len;
+
+ if (getenv("PKG_NOWRAP") != NULL)
+ goto nowrap;
+ f = fopen(PKG_WRAPCONF_FNAME, "r");
+ if (f == NULL)
+ goto nowrap;
+ cp = fgets(buffer, 256, f);
+ fclose(f);
+ if (cp == NULL)
+ goto nowrap;
+ len = strlen(cp);
+ if (cp[len - 1] == '\n')
+ cp[len - 1] = '\0';
+ while (strchr(SEPARATORS, *cp) != NULL)
+ cp++;
+ verstr = cp;
+ cp = strpbrk(cp, SEPARATORS);
+ if (cp == NULL)
+ goto nowrap;
+ *cp = '\0';
+ for (cp = verstr; *cp != '\0'; cp++)
+ if (isdigit(*cp) == 0)
+ goto nowrap;
+ if (atoi(verstr) < PKG_INSTALL_VERSION)
+ goto nowrap;
+ cp++;
+ while (*cp != '\0' && strchr(SEPARATORS, *cp) != NULL)
+ cp++;
+ if (*cp == '\0')
+ goto nowrap;
+ bcopy(cp, buffer, strlen(cp) + 1);
+ cp = strpbrk(buffer, SEPARATORS);
+ if (cp != NULL)
+ *cp = '\0';
+ if (!isdir(buffer))
+ goto nowrap;
+ cp = strrchr(argv[0], '/');
+ if (cp == NULL)
+ cp = argv[0];
+ else
+ cp++;
+ strlcat(buffer, "/", sizeof(buffer));
+ strlcat(buffer, cp, sizeof(buffer));
+ setenv("PKG_NOWRAP", "1", 1);
+ execve(buffer, argv, environ);
+
+nowrap:
+ unsetenv("PKG_NOWRAP");
+ return(real_main(argc, argv));
+}
diff --git a/usr.sbin/pkg_install/lib/plist.c b/usr.sbin/pkg_install/lib/plist.c
new file mode 100644
index 0000000..283b87f
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/plist.c
@@ -0,0 +1,588 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * General packing list routines.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include <err.h>
+#include <md5.h>
+
+/* Add an item to a packing list */
+void
+add_plist(Package *p, plist_t type, const char *arg)
+{
+ PackingList tmp;
+
+ tmp = new_plist_entry();
+ tmp->name = copy_string(arg);
+ tmp->type = type;
+
+ if (!p->head)
+ p->head = p->tail = tmp;
+ else {
+ tmp->prev = p->tail;
+ p->tail->next = tmp;
+ p->tail = tmp;
+ }
+ switch (type) {
+ case PLIST_NAME:
+ p->name = tmp->name;
+ break;
+
+ case PLIST_ORIGIN:
+ p->origin = tmp->name;
+ break;
+
+ default:
+ break;
+ }
+}
+
+void
+add_plist_top(Package *p, plist_t type, const char *arg)
+{
+ PackingList tmp;
+
+ tmp = new_plist_entry();
+ tmp->name = copy_string(arg);
+ tmp->type = type;
+
+ if (!p->head)
+ p->head = p->tail = tmp;
+ else {
+ tmp->next = p->head;
+ p->head->prev = tmp;
+ p->head = tmp;
+ }
+}
+
+/* Return the last (most recent) entry in a packing list */
+PackingList
+last_plist(Package *p)
+{
+ return p->tail;
+}
+
+/* Mark all items in a packing list to prevent iteration over them */
+void
+mark_plist(Package *pkg)
+{
+ PackingList p = pkg->head;
+
+ while (p) {
+ p->marked = TRUE;
+ p = p->next;
+ }
+}
+
+/* Find a given item in a packing list and, if so, return it (else NULL) */
+PackingList
+find_plist(Package *pkg, plist_t type)
+{
+ PackingList p = pkg->head;
+
+ while (p) {
+ if (p->type == type)
+ return p;
+ p = p->next;
+ }
+ return NULL;
+}
+
+/* Look for a specific boolean option argument in the list */
+char *
+find_plist_option(Package *pkg, const char *name)
+{
+ PackingList p = pkg->head;
+
+ while (p) {
+ if (p->type == PLIST_OPTION && !strcmp(p->name, name))
+ return p->name;
+ p = p->next;
+ }
+ return NULL;
+}
+
+/*
+ * Delete plist item 'type' in the list (if 'name' is non-null, match it
+ * too.) If 'all' is set, delete all items, not just the first occurance.
+ */
+void
+delete_plist(Package *pkg, Boolean all, plist_t type, const char *name)
+{
+ PackingList p = pkg->head;
+
+ while (p) {
+ PackingList pnext = p->next;
+
+ if (p->type == type && (!name || !strcmp(name, p->name))) {
+ free(p->name);
+ if (p->prev)
+ p->prev->next = pnext;
+ else
+ pkg->head = pnext;
+ if (pnext)
+ pnext->prev = p->prev;
+ else
+ pkg->tail = p->prev;
+ free(p);
+ if (!all)
+ return;
+ p = pnext;
+ }
+ else
+ p = p->next;
+ }
+}
+
+/* Allocate a new packing list entry */
+PackingList
+new_plist_entry(void)
+{
+ PackingList ret;
+
+ ret = (PackingList)malloc(sizeof(struct _plist));
+ bzero(ret, sizeof(struct _plist));
+ return ret;
+}
+
+/* Free an entire packing list */
+void
+free_plist(Package *pkg)
+{
+ PackingList p = pkg->head;
+
+ while (p) {
+ PackingList p1 = p->next;
+
+ free(p->name);
+ free(p);
+ p = p1;
+ }
+ pkg->head = pkg->tail = NULL;
+}
+
+/*
+ * For an ascii string denoting a plist command, return its code and
+ * optionally its argument(s)
+ */
+int
+plist_cmd(const char *s, char **arg)
+{
+ char cmd[FILENAME_MAX + 20]; /* 20 == fudge for max cmd len */
+ char *cp;
+ const char *sp;
+
+ strcpy(cmd, s);
+ str_lowercase(cmd);
+ cp = cmd;
+ sp = s;
+ while (*cp) {
+ if (isspace(*cp)) {
+ *cp = '\0';
+ while (isspace(*sp)) /* Never sure if macro, increment later */
+ ++sp;
+ break;
+ }
+ ++cp, ++sp;
+ }
+ if (arg)
+ *arg = (char *)sp;
+ if (!strcmp(cmd, "cwd"))
+ return PLIST_CWD;
+ else if (!strcmp(cmd, "srcdir"))
+ return PLIST_SRC;
+ else if (!strcmp(cmd, "cd"))
+ return PLIST_CWD;
+ else if (!strcmp(cmd, "exec"))
+ return PLIST_CMD;
+ else if (!strcmp(cmd, "unexec"))
+ return PLIST_UNEXEC;
+ else if (!strcmp(cmd, "mode"))
+ return PLIST_CHMOD;
+ else if (!strcmp(cmd, "owner"))
+ return PLIST_CHOWN;
+ else if (!strcmp(cmd, "group"))
+ return PLIST_CHGRP;
+ else if (!strcmp(cmd, "noinst"))
+ return PLIST_NOINST;
+ else if (!strcmp(cmd, "comment")) {
+ if (!strncmp(*arg, "ORIGIN:", 7)) {
+ *arg += 7;
+ return PLIST_ORIGIN;
+ } else if (!strncmp(*arg, "DEPORIGIN:", 10)) {
+ *arg += 10;
+ return PLIST_DEPORIGIN;
+ }
+ return PLIST_COMMENT;
+ } else if (!strcmp(cmd, "ignore"))
+ return PLIST_IGNORE;
+ else if (!strcmp(cmd, "ignore_inst"))
+ return PLIST_IGNORE_INST;
+ else if (!strcmp(cmd, "name"))
+ return PLIST_NAME;
+ else if (!strcmp(cmd, "display"))
+ return PLIST_DISPLAY;
+ else if (!strcmp(cmd, "pkgdep"))
+ return PLIST_PKGDEP;
+ else if (!strcmp(cmd, "conflicts"))
+ return PLIST_CONFLICTS;
+ else if (!strcmp(cmd, "mtree"))
+ return PLIST_MTREE;
+ else if (!strcmp(cmd, "dirrm"))
+ return PLIST_DIR_RM;
+ else if (!strcmp(cmd, "option"))
+ return PLIST_OPTION;
+ else
+ return FAIL;
+}
+
+/* Read a packing list from a file */
+void
+read_plist(Package *pkg, FILE *fp)
+{
+ char *cp, pline[FILENAME_MAX];
+ int cmd, major, minor;
+
+ pkg->fmtver_maj = 1;
+ pkg->fmtver_mnr = 0;
+ pkg->origin = NULL;
+ while (fgets(pline, FILENAME_MAX, fp)) {
+ int len = strlen(pline);
+
+ while (len && isspace(pline[len - 1]))
+ pline[--len] = '\0';
+ if (!len)
+ continue;
+ cp = pline;
+ if (pline[0] != CMD_CHAR) {
+ cmd = PLIST_FILE;
+ goto bottom;
+ }
+ cmd = plist_cmd(pline + 1, &cp);
+ if (cmd == FAIL) {
+ warnx("%s: unknown command '%s' (package tools out of date?)",
+ __func__, pline);
+ goto bottom;
+ }
+ if (*cp == '\0') {
+ cp = NULL;
+ goto bottom;
+ }
+ if (cmd == PLIST_COMMENT && sscanf(cp, "PKG_FORMAT_REVISION:%d.%d\n",
+ &major, &minor) == 2) {
+ pkg->fmtver_maj = major;
+ pkg->fmtver_mnr = minor;
+ if (verscmp(pkg, PLIST_FMT_VER_MAJOR, PLIST_FMT_VER_MINOR) <= 0)
+ goto bottom;
+
+ warnx("plist format revision (%d.%d) is higher than supported"
+ "(%d.%d)", pkg->fmtver_maj, pkg->fmtver_mnr,
+ PLIST_FMT_VER_MAJOR, PLIST_FMT_VER_MINOR);
+ if (pkg->fmtver_maj > PLIST_FMT_VER_MAJOR) {
+ cleanup(0);
+ exit(2);
+ }
+ }
+bottom:
+ add_plist(pkg, cmd, cp);
+ }
+}
+
+/* Write a packing list to a file, converting commands to ascii equivs */
+void
+write_plist(Package *pkg, FILE *fp)
+{
+ PackingList plist = pkg->head;
+
+ while (plist) {
+ switch(plist->type) {
+ case PLIST_FILE:
+ fprintf(fp, "%s\n", plist->name);
+ break;
+
+ case PLIST_CWD:
+ fprintf(fp, "%ccwd %s\n", CMD_CHAR, (plist->name == NULL) ? "" : plist->name);
+ break;
+
+ case PLIST_SRC:
+ fprintf(fp, "%csrcdir %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_CMD:
+ fprintf(fp, "%cexec %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_UNEXEC:
+ fprintf(fp, "%cunexec %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_CHMOD:
+ fprintf(fp, "%cmode %s\n", CMD_CHAR, plist->name ? plist->name : "");
+ break;
+
+ case PLIST_CHOWN:
+ fprintf(fp, "%cowner %s\n", CMD_CHAR, plist->name ? plist->name : "");
+ break;
+
+ case PLIST_CHGRP:
+ fprintf(fp, "%cgroup %s\n", CMD_CHAR, plist->name ? plist->name : "");
+ break;
+
+ case PLIST_COMMENT:
+ fprintf(fp, "%ccomment %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_NOINST:
+ fprintf(fp, "%cnoinst %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_IGNORE:
+ case PLIST_IGNORE_INST: /* a one-time non-ignored file */
+ fprintf(fp, "%cignore\n", CMD_CHAR);
+ break;
+
+ case PLIST_NAME:
+ fprintf(fp, "%cname %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_DISPLAY:
+ fprintf(fp, "%cdisplay %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_PKGDEP:
+ fprintf(fp, "%cpkgdep %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_CONFLICTS:
+ fprintf(fp, "%cconflicts %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_MTREE:
+ fprintf(fp, "%cmtree %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_DIR_RM:
+ fprintf(fp, "%cdirrm %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_OPTION:
+ fprintf(fp, "%coption %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_ORIGIN:
+ fprintf(fp, "%ccomment ORIGIN:%s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_DEPORIGIN:
+ fprintf(fp, "%ccomment DEPORIGIN:%s\n", CMD_CHAR, plist->name);
+ break;
+
+ default:
+ cleanup(0);
+ errx(2, "%s: unknown command type %d (%s)", __func__,
+ plist->type, plist->name);
+ break;
+ }
+ plist = plist->next;
+ }
+}
+
+/*
+ * Delete the results of a package installation.
+ *
+ * This is here rather than in the pkg_delete code because pkg_add needs to
+ * run it too in cases of failure.
+ */
+int
+delete_package(Boolean ign_err, Boolean nukedirs, Package *pkg)
+{
+ PackingList p;
+ const char *Where = ".", *last_file = "";
+ Boolean fail = SUCCESS;
+ Boolean preserve;
+ char tmp[FILENAME_MAX], *name = NULL;
+ char *prefix = NULL;
+
+ preserve = find_plist_option(pkg, "preserve") ? TRUE : FALSE;
+ for (p = pkg->head; p; p = p->next) {
+ switch (p->type) {
+ case PLIST_NAME:
+ name = p->name;
+ break;
+
+ case PLIST_IGNORE:
+ p = p->next;
+ break;
+
+ case PLIST_CWD:
+ if (!prefix)
+ prefix = p->name;
+ Where = (p->name == NULL) ? prefix : p->name;
+ if (Verbose)
+ printf("Change working directory to %s\n", Where);
+ break;
+
+ case PLIST_UNEXEC:
+ format_cmd(tmp, FILENAME_MAX, p->name, Where, last_file);
+ if (Verbose)
+ printf("Execute '%s'\n", tmp);
+ if (!Fake && system(tmp)) {
+ warnx("unexec command for '%s' failed", tmp);
+ fail = FAIL;
+ }
+ break;
+
+ case PLIST_FILE:
+ last_file = p->name;
+ sprintf(tmp, "%s/%s", Where, p->name);
+ if (isdir(tmp) && fexists(tmp) && !issymlink(tmp)) {
+ warnx("cannot delete specified file '%s' - it is a directory!\n"
+ "this packing list is incorrect - ignoring delete request", tmp);
+ }
+ else {
+ if (p->next && p->next->type == PLIST_COMMENT && !strncmp(p->next->name, "MD5:", 4)) {
+ char *cp = NULL, buf[33];
+
+ /*
+ * For packing lists whose version is 1.1 or greater, the md5
+ * hash for a symlink is calculated on the string returned
+ * by readlink().
+ */
+ if (issymlink(tmp) && verscmp(pkg, 1, 0) > 0) {
+ int len;
+ char linkbuf[FILENAME_MAX];
+
+ if ((len = readlink(tmp, linkbuf, FILENAME_MAX)) > 0)
+ cp = MD5Data((unsigned char *)linkbuf, len, buf);
+ } else if (isfile(tmp) || verscmp(pkg, 1, 1) < 0)
+ cp = MD5File(tmp, buf);
+
+ if (cp != NULL) {
+ /* Mismatch? */
+ if (strcmp(cp, p->next->name + 4)) {
+ warnx("'%s' fails original MD5 checksum - %s",
+ tmp, Force ? "deleted anyway." : "not deleted.");
+ if (!Force) {
+ fail = FAIL;
+ continue;
+ }
+ }
+ }
+ }
+ if (Verbose)
+ printf("Delete file %s\n", tmp);
+ if (!Fake) {
+ if (delete_hierarchy(tmp, ign_err, nukedirs))
+ fail = FAIL;
+ if (preserve && name) {
+ char tmp2[FILENAME_MAX];
+
+ if (make_preserve_name(tmp2, FILENAME_MAX, name, tmp)) {
+ if (fexists(tmp2)) {
+ if (rename(tmp2, tmp))
+ warn("preserve: unable to restore %s as %s",
+ tmp2, tmp);
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case PLIST_DIR_RM:
+ sprintf(tmp, "%s/%s", Where, p->name);
+ if (!isdir(tmp) && fexists(tmp)) {
+ warnx("cannot delete specified directory '%s' - it is a file!\n"
+ "this packing list is incorrect - ignoring delete request", tmp);
+ }
+ else {
+ if (Verbose)
+ printf("Delete directory %s\n", tmp);
+ if (!Fake && delete_hierarchy(tmp, ign_err, FALSE)) {
+ warnx("unable to completely remove directory '%s'", tmp);
+ fail = FAIL;
+ }
+ }
+ last_file = p->name;
+ break;
+
+ default:
+ break;
+ }
+ }
+ return fail;
+}
+
+#ifdef DEBUG
+#define RMDIR(dir) vsystem("%s %s", RMDIR_CMD, dir)
+#define REMOVE(dir,ie) vsystem("%s %s%s", REMOVE_CMD, (ie ? "-f " : ""), dir)
+#else
+#define RMDIR rmdir
+#define REMOVE(file,ie) (remove(file) && !(ie))
+#endif
+
+/* Selectively delete a hierarchy */
+int
+delete_hierarchy(const char *dir, Boolean ign_err, Boolean nukedirs)
+{
+ char *cp1, *cp2;
+
+ cp1 = cp2 = strdup(dir);
+ if (!fexists(dir)) {
+ if (!ign_err)
+ warnx("%s '%s' doesn't exist",
+ isdir(dir) ? "directory" : "file", dir);
+ return !ign_err;
+ }
+ else if (nukedirs) {
+ if (vsystem("%s -r%s %s", REMOVE_CMD, (ign_err ? "f" : ""), dir))
+ return 1;
+ }
+ else if (isdir(dir) && !issymlink(dir)) {
+ if (RMDIR(dir) && !ign_err)
+ return 1;
+ }
+ else {
+ if (REMOVE(dir, ign_err))
+ return 1;
+ }
+
+ if (!nukedirs)
+ return 0;
+ while (cp2) {
+ if ((cp2 = strrchr(cp1, '/')) != NULL)
+ *cp2 = '\0';
+ if (!isemptydir(dir))
+ return 0;
+ if (RMDIR(dir) && !ign_err) {
+ if (!fexists(dir))
+ warnx("directory '%s' doesn't exist", dir);
+ else
+ return 1;
+ }
+ /* back up the pathname one component */
+ if (cp2) {
+ cp1 = strdup(dir);
+ }
+ }
+ return 0;
+}
diff --git a/usr.sbin/pkg_install/lib/str.c b/usr.sbin/pkg_install/lib/str.c
new file mode 100644
index 0000000..0d9e288
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/str.c
@@ -0,0 +1,129 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Miscellaneous string utilities.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+
+char *
+strconcat(const char *s1, const char *s2)
+{
+ static char tmp[FILENAME_MAX];
+
+ tmp[0] = '\0';
+ strncpy(tmp, s1 ? s1 : s2, FILENAME_MAX); /* XXX: what if both are NULL? */
+ if (s1 && s2)
+ strncat(tmp, s2, FILENAME_MAX - strlen(tmp));
+ return tmp;
+}
+
+/* Get a string parameter as a file spec or as a "contents follow -" spec */
+char *
+get_dash_string(char **str)
+{
+ char *s = *str;
+
+ if (*s == '-')
+ *str = copy_string_adds_newline(s + 1);
+ else
+ *str = fileGetContents(s);
+ return *str;
+}
+
+/* Rather Obvious */
+char *
+copy_string(const char *str)
+{
+ return (str ? strdup(str) : NULL);
+}
+
+/* Rather Obvious but adds a trailing \n newline */
+char *
+copy_string_adds_newline(const char *str)
+{
+ if (str == NULL) {
+ return (NULL);
+ } else {
+ char *copy;
+ size_t line_length;
+
+ line_length = strlen(str) + 2;
+ if ((copy = malloc(line_length)) == NULL)
+ return (NULL);
+ memcpy(copy, str, line_length - 2);
+ copy[line_length - 2] = '\n'; /* Adds trailing \n */
+ copy[line_length - 1] = '\0';
+
+ return (copy);
+ }
+}
+
+/* Return TRUE if 'str' ends in suffix 'suff' */
+Boolean
+suffix(const char *str, const char *suff)
+{
+ char *idx;
+ Boolean ret = FALSE;
+
+ idx = strrchr(str, '.');
+ if (idx && !strcmp(idx + 1, suff))
+ ret = TRUE;
+ return ret;
+}
+
+/* Assuming str has a suffix, brutally murder it! */
+void
+nuke_suffix(char *str)
+{
+ char *idx;
+
+ idx = strrchr(str, '.');
+ if (idx)
+ *idx = '\0'; /* Yow! Don't try this on a const! */
+}
+
+/* Lowercase a whole string */
+void
+str_lowercase(char *str)
+{
+ while (*str) {
+ *str = tolower(*str);
+ ++str;
+ }
+}
+
+char *
+get_string(char *str, int max, FILE *fp)
+{
+ int len;
+
+ if (!str)
+ return NULL;
+ str[0] = '\0';
+ while (fgets(str, max, fp)) {
+ len = strlen(str);
+ while (len && isspace(str[len - 1]))
+ str[--len] = '\0';
+ if (len)
+ return str;
+ }
+ return NULL;
+}
diff --git a/usr.sbin/pkg_install/lib/url.c b/usr.sbin/pkg_install/lib/url.c
new file mode 100644
index 0000000..1cf5d31
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/url.c
@@ -0,0 +1,166 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * URL file access utilities.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include <err.h>
+#include <fetch.h>
+#include <libgen.h>
+#include <sys/wait.h>
+#include <stdio.h>
+
+/*
+ * Try and fetch a file by URL, returning the directory name for where
+ * it's unpacked, if successful.
+ */
+char *
+fileGetURL(const char *base, const char *spec, int keep_package)
+{
+ char *cp, *rp, *tmp;
+ char fname[FILENAME_MAX];
+ char pen[FILENAME_MAX];
+ char pkg[FILENAME_MAX];
+ char buf[8192];
+ FILE *ftp;
+ pid_t tpid;
+ int pfd[2], pstat, r, w = 0;
+ char *hint;
+ int fd, pkgfd = 0;
+
+ rp = NULL;
+ /* Special tip that sysinstall left for us */
+ hint = getenv("PKG_ADD_BASE");
+ if (!isURL(spec)) {
+ if (!base && !hint)
+ return NULL;
+ /*
+ * We've been given an existing URL (that's known-good) and now we need
+ * to construct a composite one out of that and the basename we were
+ * handed as a dependency.
+ */
+ if (base) {
+ strcpy(fname, base);
+ /*
+ * Advance back two slashes to get to the root of the package
+ * hierarchy
+ */
+ cp = strrchr(fname, '/');
+ if (cp) {
+ *cp = '\0'; /* chop name */
+ cp = strrchr(fname, '/');
+ }
+ if (cp) {
+ *(cp + 1) = '\0';
+ strcat(cp, "All/");
+ strcat(cp, spec);
+ strcat(cp, ".tbz");
+ }
+ else
+ return NULL;
+ }
+ else {
+ /*
+ * Otherwise, we've been given an environment variable hinting
+ * at the right location from sysinstall
+ */
+ strcpy(fname, hint);
+ strcat(fname, spec);
+ strcat(fname, ".tbz");
+ }
+ }
+ else
+ strcpy(fname, spec);
+
+ if (keep_package) {
+ tmp = getenv("PKGDIR");
+ strlcpy(pkg, tmp ? tmp : ".", sizeof(pkg));
+ tmp = basename(fname);
+ strlcat(pkg, "/", sizeof(pkg));
+ strlcat(pkg, tmp, sizeof(pkg));
+ if ((pkgfd = open(pkg, O_WRONLY|O_CREAT|O_TRUNC, 0644)) == -1) {
+ printf("Error: Unable to open %s\n", pkg);
+ perror("open");
+ return NULL;
+ }
+ }
+
+ fetchDebug = (Verbose > 0);
+ if ((ftp = fetchGetURL(fname, Verbose ? "v" : NULL)) == NULL) {
+ printf("Error: FTP Unable to get %s: %s\n",
+ fname, fetchLastErrString);
+ return NULL;
+ }
+
+ if (isatty(0) || Verbose)
+ printf("Fetching %s...", fname), fflush(stdout);
+ pen[0] = '\0';
+ if ((rp = make_playpen(pen, 0)) == NULL) {
+ printf("Error: Unable to construct a new playpen for FTP!\n");
+ fclose(ftp);
+ return NULL;
+ }
+ if (pipe(pfd) == -1) {
+ warn("pipe()");
+ cleanup(0);
+ exit(2);
+ }
+ if ((tpid = fork()) == -1) {
+ warn("pipe()");
+ cleanup(0);
+ exit(2);
+ }
+ if (!tpid) {
+ dup2(pfd[0], 0);
+ for (fd = getdtablesize() - 1; fd >= 3; --fd)
+ close(fd);
+ execl("/usr/bin/tar", "tar",
+ Verbose ? "-xpjvf" : "-xpjf",
+ "-", (char *)0);
+ _exit(2);
+ }
+ close(pfd[0]);
+ for (;;) {
+ if ((r = fread(buf, 1, sizeof buf, ftp)) < 1)
+ break;
+ if ((w = write(pfd[1], buf, r)) != r)
+ break;
+ if (keep_package) {
+ if ((w = write(pkgfd, buf, r)) != r)
+ break;
+ }
+ }
+ if (ferror(ftp))
+ warn("warning: error reading from server");
+ fclose(ftp);
+ if (keep_package) {
+ close(pkgfd);
+ }
+ close(pfd[1]);
+ if (w == -1)
+ warn("warning: error writing to tar");
+ tpid = waitpid(tpid, &pstat, 0);
+ if (Verbose)
+ printf("tar command returns %d status\n", WEXITSTATUS(pstat));
+ if (rp && (isatty(0) || Verbose))
+ printf(" Done.\n");
+ return rp;
+}
diff --git a/usr.sbin/pkg_install/lib/version.c b/usr.sbin/pkg_install/lib/version.c
new file mode 100644
index 0000000..d9c4fe7
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/version.c
@@ -0,0 +1,328 @@
+/*
+ * FreeBSD install - a package for the installation and maintenance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Maxim Sobolev
+ * 31 July 2001
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include <err.h>
+
+/*
+ * Routines to assist with PLIST_FMT_VER numbers in the packing
+ * lists.
+ *
+ * Following is the PLIST_FMT_VER history:
+ * 1.0 - Initial revision;
+ * 1.1 - When recording/checking checksum of symlink use hash of readlink()
+ * value instead of the hash of an object this links points to.
+ *
+ */
+int
+verscmp(Package *pkg, int major, int minor)
+{
+ int rval = 0;
+
+ if ((pkg->fmtver_maj < major) || (pkg->fmtver_maj == major &&
+ pkg->fmtver_mnr < minor))
+ rval = -1;
+ else if ((pkg->fmtver_maj > major) || (pkg->fmtver_maj == major &&
+ pkg->fmtver_mnr > minor))
+ rval = 1;
+
+ return rval;
+}
+
+/*
+ * split_version(pkgname, endname, epoch, revision) returns a pointer to
+ * the version portion of a package name and the two special components.
+ *
+ * Syntax is: ${PORTNAME}-${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}]
+ *
+ * Written by Oliver Eikemeier
+ * Based on work of Jeremy D. Lea.
+ */
+static const char *
+split_version(const char *pkgname, const char **endname, unsigned long *epoch, unsigned long *revision)
+{
+ char *ch;
+ const char *versionstr;
+ const char *endversionstr;
+
+ if (pkgname == NULL)
+ errx(2, "%s: Passed NULL pkgname.", __func__);
+
+ /* Look for the last '-' the the pkgname */
+ ch = strrchr(pkgname, '-');
+ /* Cheat if we are just passed a version, not a valid package name */
+ versionstr = ch ? ch + 1 : pkgname;
+
+ /* Look for the last '_' in the version string, advancing the end pointer */
+ ch = strrchr(versionstr, '_');
+ if (revision != NULL) {
+ *revision = ch ? strtoul(ch + 1, NULL, 10) : 0;
+ }
+ endversionstr = ch;
+
+ /* Look for the last ',' in the remaining version string */
+ ch = strrchr(endversionstr ? endversionstr + 1 : versionstr, ',');
+ if (epoch != NULL) {
+ *epoch = ch ? strtoul(ch + 1, NULL, 10) : 0;
+ }
+ if (ch && !endversionstr)
+ endversionstr = ch;
+
+ /* set the pointer behind the last character of the version without revision or epoch */
+ if (endname)
+ *endname = endversionstr ? endversionstr : strrchr(versionstr, '\0');
+
+ return versionstr;
+}
+
+/*
+ * PORTVERSIONs are composed of components separated by dots. A component
+ * consists of a version number, a letter and a patchlevel number. This does
+ * not conform to the porter's handbook, but let us formulate rules that
+ * fit the current practice and are far simpler than to make decisions
+ * based on the order of netters and lumbers. Besides, people use versions
+ * like 10b2 in the ports...
+ */
+
+typedef struct {
+#ifdef __LONG_LONG_SUPPORTED
+ long long n;
+ long long pl;
+#else
+ long n;
+ long pl;
+#endif
+ int a;
+} version_component;
+
+/*
+ * get_component(position, component) gets the value of the next component
+ * (number - letter - number triple) and returns a pointer to the next character
+ * after any leading separators
+ *
+ * - components are separated by dots
+ * - characters !~ [a-zA-Z0-9.+*] are treated as separators
+ * (1.0:2003.09.16 = 1.0.2003.09.16), this may not be what you expect:
+ * 1.0.1:2003.09.16 < 1.0:2003.09.16
+ * - consecutive separators are collapsed (10..1 = 10.1)
+ * - missing separators are inserted, essentially
+ * letter number letter => letter number . letter (10a1b2 = 10a1.b2)
+ * - missing components are assumed to be equal to 0 (10 = 10.0 = 10.0.0)
+ * - the letter sort order is: [none], a, b, ..., z; numbers without letters
+ * sort first (10 < 10a < 10b)
+ * - missing version numbers (in components starting with a letter) sort as -1
+ * (a < 0, 10.a < 10)
+ * - a separator is inserted before the special strings "pl", "alpha", "beta",
+ * "pre" and "rc".
+ * - "pl" sorts before every other letter, "alpha", "beta", "pre" and "rc"
+ * sort as a, b, p and r. (10alpha = 10.a < 10, but 10 < 10a; pl11 < alpha3
+ * < 0.1beta2 = 0.1.b2 < 0.1)
+ * - other strings use only the first letter for sorting, case is ignored
+ * (1.d2 = 1.dev2 = 1.Development2)
+ * - The special component `*' is guaranteed to be the smallest possible
+ * component (2.* < 2pl1 < 2alpha3 < 2.9f7 < 3.*)
+ * - components separated by `+' are handled by version_cmp below
+ *
+ * Oliver Eikemeier
+ */
+
+static const struct {
+ const char *name;
+ size_t namelen;
+ int value;
+} stage[] = {
+ { "pl", 2, 0 },
+ { "alpha", 5, 'a'-'a'+1 },
+ { "beta", 4, 'b'-'a'+1 },
+ { "pre", 3, 'p'-'a'+1 },
+ { "rc", 2, 'r'-'a'+1 },
+ { NULL, 0, -1 }
+};
+
+static const char *
+get_component(const char *position, version_component *component)
+{
+ const char *pos = position;
+ int hasstage = 0, haspatchlevel = 0;
+
+ if (!pos)
+ errx(2, "%s: Passed NULL position.", __func__);
+
+ /* handle version number */
+ if (isdigit(*pos)) {
+ char *endptr;
+#ifdef __LONG_LONG_SUPPORTED
+ component->n = strtoll(pos, &endptr, 10);
+#else
+ component->n = strtol(pos, &endptr, 10);
+#endif
+ /* should we test for errno == ERANGE? */
+ pos = endptr;
+ } else if (*pos == '*') {
+ component->n = -2;
+ do {
+ pos++;
+ } while(*pos && *pos != '+');
+ } else {
+ component->n = -1;
+ hasstage = 1;
+ }
+
+ /* handle letter */
+ if (isalpha(*pos)) {
+ int c = tolower(*pos);
+ haspatchlevel = 1;
+ /* handle special suffixes */
+ if (isalpha(pos[1])) {
+ int i;
+ for (i = 0; stage[i].name; i++) {
+ if (strncasecmp(pos, stage[i].name, stage[i].namelen) == 0
+ && !isalpha(pos[stage[i].namelen])) {
+ if (hasstage) {
+ /* stage to value */
+ component->a = stage[i].value;
+ pos += stage[i].namelen;
+ } else {
+ /* insert dot */
+ component->a = 0;
+ haspatchlevel = 0;
+ }
+ c = 0;
+ break;
+ }
+ }
+ }
+ /* unhandled above */
+ if (c) {
+ /* use the first letter and skip following */
+ component->a = c - 'a' + 1;
+ do {
+ ++pos;
+ } while (isalpha(*pos));
+ }
+ } else {
+ component->a = 0;
+ haspatchlevel = 0;
+ }
+
+ if (haspatchlevel) {
+ /* handle patch number */
+ if (isdigit(*pos)) {
+ char *endptr;
+#ifdef __LONG_LONG_SUPPORTED
+ component->pl = strtoll(pos, &endptr, 10);
+#else
+ component->pl = strtol(pos, &endptr, 10);
+#endif
+ /* should we test for errno == ERANGE? */
+ pos = endptr;
+ } else {
+ component->pl = -1;
+ }
+ } else {
+ component->pl = 0;
+ }
+
+ /* skip trailing separators */
+ while (*pos && !isdigit(*pos) && !isalpha(*pos) && *pos != '+' && *pos != '*') {
+ pos++;
+ }
+
+ return pos;
+}
+
+/*
+ * version_cmp(pkg1, pkg2) returns -1, 0 or 1 depending on if the version
+ * components of pkg1 is less than, equal to or greater than pkg2. No
+ * comparison of the basenames is done.
+ *
+ * The port version is defined by:
+ * ${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}]
+ * ${PORTEPOCH} supersedes ${PORTVERSION} supersedes ${PORTREVISION}.
+ * See the commit log for revision 1.349 of ports/Mk/bsd.port.mk
+ * for more information.
+ *
+ * The epoch and revision are defined to be a single number, while the rest
+ * of the version should conform to the porting guidelines. It can contain
+ * multiple components, separated by a period, including letters.
+ *
+ * The tests allow for significantly more latitude in the version numbers
+ * than is allowed in the guidelines. No point in enforcing them here.
+ * That's what portlint is for.
+ *
+ * Jeremy D. Lea.
+ * reimplemented by Oliver Eikemeier
+ */
+int
+version_cmp(const char *pkg1, const char *pkg2)
+{
+ const char *v1, *v2, *ve1, *ve2;
+ unsigned long e1, e2, r1, r2;
+ int result = 0;
+
+ v1 = split_version(pkg1, &ve1, &e1, &r1);
+ v2 = split_version(pkg2, &ve2, &e2, &r2);
+
+ /* Check epoch, port version, and port revision, in that order. */
+ if (e1 != e2) {
+ result = (e1 < e2 ? -1 : 1);
+ }
+
+ /* Shortcut check for equality before invoking the parsing routines. */
+ if (result == 0 && (ve1 - v1 != ve2 - v2 || strncasecmp(v1, v2, ve1 - v1) != 0)) {
+ /* Loop over different components (the parts separated by dots).
+ * If any component differs, we have the basis for an inequality. */
+ while(result == 0 && (v1 < ve1 || v2 < ve2)) {
+ int block_v1 = 0;
+ int block_v2 = 0;
+ version_component vc1 = {0, 0, 0};
+ version_component vc2 = {0, 0, 0};
+ if (v1 < ve1 && *v1 != '+') {
+ v1 = get_component(v1, &vc1);
+ } else {
+ block_v1 = 1;
+ }
+ if (v2 < ve2 && *v2 != '+') {
+ v2 = get_component(v2, &vc2);
+ } else {
+ block_v2 = 1;
+ }
+ if (block_v1 && block_v2) {
+ if (v1 < ve1)
+ v1++;
+ if (v2 < ve2)
+ v2++;
+ } else if (vc1.n != vc2.n) {
+ result = (vc1.n < vc2.n ? -1 : 1);
+ } else if (vc1.a != vc2.a) {
+ result = (vc1.a < vc2.a ? -1 : 1);
+ } else if (vc1.pl != vc2.pl) {
+ result = (vc1.pl < vc2.pl ? -1 : 1);
+ }
+ }
+ }
+
+ /* Compare FreeBSD revision numbers. */
+ if (result == 0 && r1 != r2) {
+ result = (r1 < r2 ? -1 : 1);
+ }
+ return result;
+}
diff --git a/usr.sbin/pkg_install/tkpkg b/usr.sbin/pkg_install/tkpkg
new file mode 100755
index 0000000..9071155
--- /dev/null
+++ b/usr.sbin/pkg_install/tkpkg
@@ -0,0 +1,177 @@
+#!/usr/local/bin/wish -f
+#$FreeBSD$
+#
+#$Log: tkpkg,v $
+#Revision 1.2 1994/12/06 00:51:21 jkh
+#Many of John T. Kohl's patches from NetBSD. Thanks, John!
+#Submitted by: jkohl
+#
+# Revision 1.1 1994/01/06 08:16:20 jkh
+# Cleaning house.
+#
+# Revision 1.1 1993/09/04 17:06:09 jkh
+# Added Rich's wish front-end.
+#
+# Revision 1.6 1993/09/03 23:37:22 rich
+# warn user if no tar archives are found in the current directory.
+# removed the revision string from the lower text frame.
+#
+# Revision 1.5 1993/09/03 15:48:04 rich
+# glob for .tar.gz, .tar.z and .tar.Z looking for archives
+#
+# Revision 1.4 1993/08/28 15:53:59 rich
+# added version and date info to lower text window.
+#
+# Revision 1.3 1993/08/28 15:47:12 rich
+# filtered out ^Ls in pkg_* output.
+#
+#
+set pkgname ""
+wm title . "Package Installation"
+#--------------------------------------------------------------
+# The top level main window, consisting of a bar of buttons and a list
+# of packages and a description of the current package.
+#--------------------------------------------------------------
+frame .menu -relief raised -borderwidth 1
+frame .frame -borderwidth 4
+
+scrollbar .frame.scroll -relief sunken -command ".frame.list yview"
+listbox .frame.list -yscroll ".frame.scroll set" -relief sunken -setgrid 1
+pack append .frame .frame.scroll {right filly} \
+ .frame.list {left expand fill}
+
+# build the lower window shoing the complete description of a pacage
+frame .f -borderwidth 4
+text .f.t -width 80 -height 20 -yscrollcommand ".f.s set" -relief sunken
+
+# Initially display instructions in this window. Erase the
+# instructions and show the package description when the user clicks
+# on a package.
+#
+.f.t insert end "Double click on a package above to see its
+complete description here."
+scrollbar .f.s -relief sunken -command ".f.t yview"
+pack append .f .f.s {right filly} .f.t {left expand fill}
+
+bind .frame.list <Double-Button-1> \
+ {foreach i [selection get] {do_description $i}}
+pack append . .menu {top fill} \
+ .f {bottom expand fill} \
+ .frame {bottom expand fill}
+
+#----------------------------------------------------------------
+# Make menu bar:
+#----------------------------------------------------------------
+button .menu.inst -text "Install" \
+ -command "apply_to_pkg \"pkg_add -v\""
+button .menu.dein -text "Deinstall" \
+ -command "apply_to_pkg \"pkg_delete -v\""
+button .menu.installed -text "What is Installed?" \
+ -command "list_pkgs \"pkg_info -I -a |tr ' ' ' '\""
+button .menu.available -text "What can I install?" \
+ -command "list_pkgs \"pkg_info -I -c [glob -nocomplain *.{tgz,tar.z,tar.gz,tar.Z}] |tr ' ' ' '\""
+button .menu.cont -text "Contents?" \
+ -command "apply_to_pkg \"pkg_info -d -v\""
+button .menu.quit -text "Quit" -command "destroy ."
+button .menu.help -text "Help" -command "do_help"
+
+pack append .menu \
+ .menu.inst left \
+ .menu.dein left \
+ .menu.installed left \
+ .menu.available left \
+ .menu.cont left \
+ .menu.quit left \
+ .menu.help right
+#-------------------------------------------------------
+# Display the package description.
+#-------------------------------------------------------
+proc list_pkgs {s} {
+ set line ""
+ set f [eval "open {| sh -c \"$s\" } r"]
+ .frame.list delete 0 end
+ while {[gets $f line] > 0} {
+ .frame.list insert end $line
+ }
+ close $f
+}
+
+# display the list of available packages
+set archives [glob -nocomplain *.{tgz,tar.z,tar.gz,tar.Z}]
+if {$archives == ""} {
+ .frame.list delete 0 end
+ .frame.list insert end "Warning: no compressed tar archives files found."
+} else {
+ list_pkgs "pkg_info -I -c $archives |tr ' ' ' '"
+}
+
+#-------------------------------------------------------
+# Display the package description.
+#-------------------------------------------------------
+proc do_description {s} {
+ global pkgname
+ regexp {[^ ]*} $s filename
+ set pkgname $filename
+ .f.t delete 0.0 end
+ set cmd "pkg_info -d $filename |tr -d ' '"
+ set f [eval "open {| csh -c \"$cmd\" } r"]
+ while {![eof $f]} {
+ .f.t insert end [read $f]
+ }
+}
+#-------------------------------------------------------
+# package install window.
+#-------------------------------------------------------
+proc do_help {{w .help}} {
+ catch {destroy $w}
+ toplevel $w
+ wm title $w "Help"
+ wm iconname $w "Help"
+ button $w.ok -text OK -command "destroy $w"
+ message $w.t -relief raised -bd 2 \
+ -text "You can install, deinstall and list info on the available packages. To select a package and see its complete description, press mouse button 1 over the package name. To install a selected package, press the Install button. To exit, press the \"Quit\" button."
+ pack append $w $w.ok {bottom fillx} $w.t {expand fill}
+}
+#-------------------------------------------------------
+# Apply a command to a package.
+#-------------------------------------------------------
+proc apply_to_pkg {s} {
+ apply_to_pkg_err $s ""
+}
+#-------------------------------------------------------
+# Apply a command to a package, with error stream redirection instructions.
+#-------------------------------------------------------
+proc apply_to_pkg_err {s errredir} {
+ global pkgname
+ .f.t delete 0.0 end
+ if {$pkgname == ""} {
+ .f.t insert end "You must double click on a package name first!"
+ } else {
+ apply_to_pkg_int "$s $pkgname" "2>&1"
+ }
+}
+proc apply_to_pkg_int {s errredir} {
+ .f.t delete 0.0 end
+ .f.t insert end "Running: $s\n"
+ set f [eval "open {| sh -c \"$s $errredir\" } r"]
+ while {![eof $f]} {
+ .f.t insert end [read $f 64]
+ }
+}
+#-------------------------------------------------------
+# Invoke an arbitrary command.
+#-------------------------------------------------------
+proc do_command {s} {
+ .f.t delete 0.0 end
+ .f.t insert end "Running: $s\n"
+ set f [eval "open {| $s} r"]
+ while {![eof $f]} {
+ .f.t insert end [read $f 64]
+ }
+}
+# local variables:
+# mode: csh
+# compile-command: ""
+# comment-start: "# "
+# comment-start-skip: "# "
+# end:
diff --git a/usr.sbin/pkg_install/updating/Makefile b/usr.sbin/pkg_install/updating/Makefile
new file mode 100644
index 0000000..cf8bfc8
--- /dev/null
+++ b/usr.sbin/pkg_install/updating/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+PROG= pkg_updating
+SRCS= main.c
+
+CFLAGS+= -I${.CURDIR}/../lib
+
+WARNS?= 6
+WFORMAT?= 1
+
+DPADD= ${LIBINSTALL} ${LIBFETCH} ${LIBMD}
+LDADD= ${LIBINSTALL} -lfetch -lmd
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pkg_install/updating/main.c b/usr.sbin/pkg_install/updating/main.c
new file mode 100644
index 0000000..e189c01
--- /dev/null
+++ b/usr.sbin/pkg_install/updating/main.c
@@ -0,0 +1,270 @@
+/*-
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <beat@chruetertee.ch> 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. Beat Gätzi
+ * ----------------------------------------------------------------------------
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <errno.h>
+#include <limits.h>
+#include <sysexits.h>
+#include <getopt.h>
+
+#include "lib.h"
+#include "pathnames.h"
+
+typedef struct installedport {
+ struct installedport *next; /* List of installed ports. */
+ char name[LINE_MAX]; /* Name of the installed port. */
+} INSTALLEDPORT;
+
+int usage(void);
+
+static char opts[] = "d:f:h";
+static struct option longopts[] = {
+ { "date", required_argument, NULL, 'd' },
+ { "file", required_argument, NULL, 'f' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, 0, NULL, 0 },
+};
+
+/*
+ * Parse /usr/port/UPDATING for corresponding entries. If no argument is
+ * passed to pkg_updating all entries for all installed ports are displayed.
+ * If a list of portnames is passed to pkg_updating only entries for the
+ * given portnames are displayed. Use the -d option to define that only newer
+ * entries as this date are shown.
+ */
+int
+main(int argc, char *argv[])
+{
+ /* Keyword for searching portname in UPDATING. */
+ const char *affects = "AFFECTS";
+ /* Indicate a date -> end of a entry. Will fail on 2100-01-01... */
+ const char *end = "20";
+ /* Keyword for searching origin portname of installed port. */
+ const char *origin = "@comment ORIGIN:";
+ const char *pkgdbpath = LOG_DIR; /* Location of pkgdb */
+ const char *updatingfile = UPDATING; /* Location of UPDATING */
+
+ char *date = NULL; /* Passed -d argument */
+ char *dateline = NULL; /* Saved date of an entry */
+ /* Tmp lines for parsing file */
+ char *tmpline1 = NULL;
+ char *tmpline2 = NULL;
+
+ char originline[LINE_MAX]; /* Line of +CONTENTS */
+ /* Temporary variable to create path to +CONTENTS for installed ports. */
+ char tmp_file[MAXPATHLEN];
+ char updatingline[LINE_MAX]; /* Line of UPDATING */
+
+ int ch; /* Char used by getopt */
+ int found = 0; /* Found an entry */
+ int linelength; /* Length of parsed line */
+ int maxcharperline = LINE_MAX; /* Max chars per line */
+ int dflag = 0; /* -d option set */
+ /* If pflag = 0 UPDATING will be checked for all installed ports. */
+ int pflag = 0;
+
+ size_t n; /* Offset to create path */
+
+ struct dirent *pkgdbdir; /* pkgdb directory */
+ struct stat attribute; /* attribute of pkgdb element */
+
+ /* Needed nodes for linked list with installed ports. */
+ INSTALLEDPORT *head = (INSTALLEDPORT *) NULL;
+ INSTALLEDPORT *curr = (INSTALLEDPORT *) NULL;
+
+ DIR *dir;
+ FILE *fd;
+
+ while ((ch = getopt_long(argc, argv, opts, longopts, NULL)) != -1) {
+ switch (ch) {
+ case 'd':
+ dflag = 1;
+ date = optarg;
+ break;
+ case 'f':
+ updatingfile = optarg;
+ break;
+ case 'h':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* Check if passed date has a correct format. */
+ if (dflag == 1) {
+ linelength = strlen(date);
+ if (linelength != 8)
+ exit(EX_DATAERR);
+ if (strspn(date, "0123456789") != 8) {
+ fprintf(stderr, "unknown date format: %s\n", date);
+ exit(EX_DATAERR);
+ }
+ }
+
+ /* Save the list of passed portnames. */
+ if (argc != 0) {
+ pflag = 1;
+ while (*argv) {
+ if((curr = (INSTALLEDPORT *)
+ malloc(sizeof(INSTALLEDPORT))) == NULL)
+ (void)exit(EXIT_FAILURE);
+ strlcpy (curr->name, *argv, strlen(*argv) + 1);
+ curr->next = head;
+ head = curr;
+ (void)*argv++;
+ }
+ }
+
+ /*
+ * UPDATING will be parsed for all installed ports
+ * if no portname is passed.
+ */
+ if (pflag == 0) {
+ /* Open /var/db/pkg and search for all installed ports. */
+ if((dir = opendir(pkgdbpath)) != NULL) {
+ while ((pkgdbdir = readdir(dir)) != NULL) {
+ if (strcmp(pkgdbdir->d_name, ".") != 0 &&
+ strcmp(pkgdbdir->d_name, "..") !=0) {
+
+ /* Create path to +CONTENTS file for each installed port */
+ n = strlcpy(tmp_file, pkgdbpath, strlen(pkgdbpath)+1);
+ n = strlcpy(tmp_file + n, "/", sizeof(tmp_file) - n);
+ n = strlcat(tmp_file + n, pkgdbdir->d_name,
+ sizeof(tmp_file) - n);
+ if(stat(tmp_file, &attribute) == -1) {
+ fprintf(stderr, "can't open %s: %s\n",
+ tmp_file, strerror(errno));
+ return EXIT_FAILURE;
+ }
+ if(attribute.st_mode & S_IFREG)
+ continue;
+ (void)strlcat(tmp_file + n, "/",
+ sizeof(tmp_file) - n);
+ (void)strlcat(tmp_file + n, CONTENTS_FNAME,
+ sizeof(tmp_file) - n);
+
+ /* Open +CONTENT file */
+ fd = fopen(tmp_file, "r");
+ if(fd == NULL) {
+ fprintf(stderr, "warning: can't open %s: %s\n",
+ tmp_file, strerror(errno));
+ continue;
+ }
+
+ /*
+ * Parses +CONTENT for ORIGIN line and
+ * put element into linked list.
+ */
+ while(fgets(originline, maxcharperline, fd) != NULL) {
+ tmpline1 = strstr(originline, origin);
+ if( tmpline1 != NULL ) {
+ /* Tmp variable to store port name. */
+ char *pname;
+ pname = strrchr(originline, (int)':');
+ pname++;
+ if((curr = (INSTALLEDPORT *)
+ malloc(sizeof(INSTALLEDPORT))) == NULL)
+ (void)exit(EXIT_FAILURE);
+ if (pname[strlen(pname) - 1] == '\n')
+ pname[strlen(pname) - 1] = '\0';
+ strlcpy (curr->name, pname, strlen(pname)+1);
+ curr->next = head;
+ head = curr;
+ }
+ }
+
+ if(ferror(fd)) {
+ fprintf(stderr, "error reading input\n");
+ exit(EX_IOERR);
+ }
+
+ (void)fclose(fd);
+ }
+ }
+ closedir(dir);
+ }
+ }
+
+ /* Open UPDATING file */
+ fd = fopen(updatingfile, "r");
+ if(fd == NULL) {
+ fprintf(stderr, "can't open %s: %s\n",
+ updatingfile, strerror(errno));
+ exit(EX_UNAVAILABLE);
+ }
+
+ /* Parse opened UPDATING file. */
+ while(fgets(updatingline, maxcharperline, fd) != NULL) {
+ /* No entry is found so far */
+ if (found == 0) {
+ /* Search for AFFECTS line to parse the portname. */
+ tmpline1 = strstr(updatingline, affects);
+
+ if( tmpline1 != NULL ) {
+ curr = head;
+ while(curr != NULL) {
+ tmpline2 = strstr(updatingline, curr->name);
+ if( tmpline2 != NULL )
+ break;
+ curr = curr->next;
+ }
+ if( tmpline2 != NULL ) {
+ /* If -d is set, check if entry is newer than the date. */
+ if ( (dflag == 1) && (strncmp(dateline, date, 8) < 0))
+ continue;
+ printf("%s", dateline);
+ printf("%s", updatingline);
+ found = 1;
+ }
+ }
+ }
+ /* Search for the end of an entry, if not found print the line. */
+ else {
+ tmpline1 = strstr(updatingline, end);
+ if( tmpline1 == NULL )
+ printf("%s", updatingline);
+ else {
+ linelength = strlen(updatingline);
+ if (linelength == 10)
+ found = 0;
+ else
+ printf("%s", updatingline);
+ }
+ }
+ /* Save the actual line, it could be a date. */
+ dateline = strdup(updatingline);
+ }
+
+ if(ferror(fd)) {
+ fprintf(stderr, "error reading input\n");
+ exit(EX_IOERR);
+ }
+ (void)fclose(fd);
+
+ exit(EX_OK);
+}
+
+int
+usage(void)
+{
+ fprintf(stderr,
+ "usage: pkg_updating [-h] [-d YYYYMMDD] [-f file] [portname ...]\n");
+ exit(EX_USAGE);
+}
+
+void
+cleanup(int sig)
+{
+ if (sig)
+ exit(1);
+}
diff --git a/usr.sbin/pkg_install/updating/pathnames.h b/usr.sbin/pkg_install/updating/pathnames.h
new file mode 100644
index 0000000..7eed1b4
--- /dev/null
+++ b/usr.sbin/pkg_install/updating/pathnames.h
@@ -0,0 +1,17 @@
+/*-
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <beat@chruetertee.ch> 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. Beat Gätzi
+ * ----------------------------------------------------------------------------
+ *
+ * $FreeBSD$
+ *
+ */
+
+/* Where the updating file lives by default */
+#define DEF_UPDATING "/usr/ports/UPDATING"
+/* macro to define location of the UPDATING file */
+#define UPDATING (getenv(PORTSDIR) ? strcat(getenv(PORTSDIR), \
+ "/UPDATING") : DEF_UPDATING)
diff --git a/usr.sbin/pkg_install/updating/pkg_updating.1 b/usr.sbin/pkg_install/updating/pkg_updating.1
new file mode 100644
index 0000000..e3bcb54
--- /dev/null
+++ b/usr.sbin/pkg_install/updating/pkg_updating.1
@@ -0,0 +1,94 @@
+.\"
+.\" FreeBSD updating - Scan the installed ports and show all UPDATING entries
+.\" that affect one of the installed ports. Alternative a list of portnames
+.\" could be passed to pkg_updating
+.\"
+.\" "THE BEER-WARE LICENSE" (Revision 42):
+.\" <beat@chruetertee.ch> 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. Beat Gätzi
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 30, 2008
+.Dt PKG_UPDATING 1
+.Os
+.Sh NAME
+.Nm pkg_updating
+.Nd a utility for displaying UPDATING entries of software packages
+.Sh SYNOPSIS
+.Nm
+.Op Fl h
+.Op Fl d Ar date
+.Op Fl f Ar file
+.Op Ar pkg-name ...
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+command scans the installed ports and show all UPDATING entries that affect one
+of the installed ports. Alternative a list of pkg-names could be passed.
+.Sh OPTIONS
+The following command line options are supported:
+.Bl -tag -width indent
+.It Ar pkg-name ...
+UPDATING entries for the named packages are displayed.
+.It Fl d , -date Ar date
+Only entries newer than
+.Ar date
+are shown. Use a YYYYMMDD date format.
+.It Fl f , -file Ar file
+Defines a alternative location of the UPDATING
+.Ar file .
+.It Fl h , -help
+Print help message.
+.El
+.Sh EXAMPLES
+.Bl -tag -width indent
+.Dl pkg_updating
+.Pp
+Shows all entries of all installed ports.
+.Pp
+.Dl pkg_updating -d 20070101
+.Pp
+Shows all entries of all installed ports since 2007-01-01.
+.Pp
+.Dl pkg_updating apache mysql
+.Pp
+Shows all entries for all apache and mysql ports.
+.Pp
+.Dl pkg_updating -d 20060101 apache
+.Pp
+Shows all apache entries since 2006-01-01.
+.Pp
+.Dl pkg_updating -f /tmp/UPDATING
+.Pp
+Defines that the UPDATING file is in /tmp and shows all entries of all
+installed ports
+.Pp
+.El
+.Sh ENVIRONMENT
+.Bl -tag -width PKG_DBDIR
+.It Ev PKG_DBDIR
+Specifies an alternative location for the installed package database.
+.It Ev PORTSDIR
+Location of the ports tree.
+.El
+.Sh FILES
+.Bl -tag -width /var/db/pkg -compact
+.It Pa /var/db/pkg
+Default location of the installed package database.
+.It Pa /usr/ports
+The default ports directory and default location of the UPDATING file
+.El
+.Sh SEE ALSO
+.Xr pkg_add 1 ,
+.Xr pkg_create 1 ,
+.Xr pkg_delete 1 ,
+.Xr pkg_version 1
+.Sh AUTHORS
+.An Beat Gätzi Aq beat@chruetertee.ch
+.Sh CONTRIBUTORS
+.An Martin Tournoij Aq carpetsmoker@xs4all.nl
+.Sh BUGS
+Sure to be some.
diff --git a/usr.sbin/pkg_install/version/Makefile b/usr.sbin/pkg_install/version/Makefile
new file mode 100644
index 0000000..71168ee
--- /dev/null
+++ b/usr.sbin/pkg_install/version/Makefile
@@ -0,0 +1,17 @@
+# $FreeBSD$
+
+PROG= pkg_version
+SRCS= main.c perform.c
+
+CFLAGS+= -I${.CURDIR}/../lib
+
+WARNS?= 6
+WFORMAT?= 1
+
+DPADD= ${LIBINSTALL} ${LIBFETCH} ${LIBMD}
+LDADD= ${LIBINSTALL} -lfetch -lmd
+
+test:
+ sh ${.CURDIR}/test-pkg_version.sh
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pkg_install/version/main.c b/usr.sbin/pkg_install/version/main.c
new file mode 100644
index 0000000..f46d945
--- /dev/null
+++ b/usr.sbin/pkg_install/version/main.c
@@ -0,0 +1,137 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jeremy D. Lea.
+ * 11 May 2002
+ *
+ * This is the version module. Based on pkg_version.pl by Bruce A. Mah.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+
+#include <getopt.h>
+#include <err.h>
+
+#include "lib.h"
+#include "version.h"
+
+char *LimitChars = NULL;
+char *PreventChars = NULL;
+char *MatchName = NULL;
+char *LookUpOrigin = NULL;
+Boolean RegexExtended = FALSE;
+Boolean UseINDEXOnly = FALSE;
+Boolean ShowOrigin = FALSE;
+
+static void usage(void);
+
+static char opts[] = "dIhl:L:qs:XtTO:ov";
+static struct option longopts[] = {
+ { "extended", no_argument, NULL, 'X' },
+ { "help", no_argument, NULL, 'h' },
+ { "match", required_argument, NULL, 's' },
+ { "no-status", required_argument, NULL, 'L' },
+ { "origin", required_argument, NULL, 'O' },
+ { "quiet", no_argument, NULL, 'q' },
+ { "show-origin",no_argument, NULL, 'o' },
+ { "status", required_argument, NULL, 'l' },
+ { "index-only", no_argument, NULL, 'I' },
+ { "verbose", no_argument, NULL, 'v' },
+ { NULL, 0, NULL, 0 }
+};
+
+int
+main(int argc, char **argv)
+{
+ int ch, cmp = 0;
+
+ if (argc == 4 && !strcmp(argv[1], "-t")) {
+ cmp = version_cmp(argv[2], argv[3]);
+ printf(cmp > 0 ? ">\n" : (cmp < 0 ? "<\n" : "=\n"));
+ exit(0);
+ }
+ else if (argc == 4 && !strcmp(argv[1], "-T")) {
+ cmp = version_match(argv[3], argv[2]);
+ exit(cmp == 1 ? 0 : 1);
+ }
+ else while ((ch = getopt_long(argc, argv, opts, longopts, NULL)) != -1) {
+ switch(ch) {
+ case 'v':
+ Verbose++;
+ break;
+
+ case 'I':
+ UseINDEXOnly = TRUE;
+ break;
+
+ case 'l':
+ LimitChars = optarg;
+ break;
+
+ case 'L':
+ PreventChars = optarg;
+ break;
+
+ case 'q':
+ Quiet = TRUE;
+ break;
+
+ case 's':
+ MatchName = optarg;
+ break;
+
+ case 'O':
+ LookUpOrigin = optarg;
+ break;
+
+ case 'o':
+ ShowOrigin = TRUE;
+ break;
+
+ case 't':
+ errx(2, "Invalid -t usage.");
+ break;
+
+ case 'T':
+ errx(2, "Invalid -T usage.");
+ break;
+
+ case 'X':
+ RegexExtended = TRUE;
+ break;
+
+ case 'h':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ return pkg_perform(argv);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n%s\n",
+ "usage: pkg_version [-hIoqv] [-l limchar] [-L limchar] [[-X] -s string] [-O origin] [index]",
+ " pkg_version -t v1 v2",
+ " pkg_version -T name pattern");
+ exit(1);
+}
diff --git a/usr.sbin/pkg_install/version/perform.c b/usr.sbin/pkg_install/version/perform.c
new file mode 100644
index 0000000..e39adaf
--- /dev/null
+++ b/usr.sbin/pkg_install/version/perform.c
@@ -0,0 +1,408 @@
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jeremy D. Lea.
+ * 11 May 2002
+ *
+ * This is the version module. Based on pkg_version.pl by Bruce A. Mah.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "lib.h"
+#include "version.h"
+#include <err.h>
+#include <fetch.h>
+#include <signal.h>
+
+FILE *IndexFile;
+char IndexPath[PATH_MAX] = "";
+struct index_head Index = SLIST_HEAD_INITIALIZER(Index);
+
+static int pkg_do(char *);
+static void show_version(Package, const char *, const char *);
+
+/*
+ * This is the traditional pkg_perform, except that the argument is _not_
+ * a list of packages. It is the index file from the command line.
+ *
+ * We loop over the installed packages, matching them with the -s flag
+ * if needed and calling pkg_do(). Before hand we set up a few things,
+ * and after we tear them down...
+ */
+int
+pkg_perform(char **indexarg)
+{
+ char **pkgs, *pat[2], **patterns;
+ struct index_entry *ie;
+ int i, err_cnt = 0;
+ int MatchType;
+
+ /*
+ * Try to find and open the INDEX. We only check IndexFile != NULL
+ * later, if we actually need the INDEX.
+ */
+ if (*indexarg == NULL)
+ snprintf(IndexPath, sizeof(IndexPath), "%s/%s", PORTS_DIR, INDEX_FNAME);
+ else
+ strlcpy(IndexPath, *indexarg, sizeof(IndexPath));
+ if (isURL(IndexPath))
+ IndexFile = fetchGetURL(IndexPath, "");
+ else
+ IndexFile = fopen(IndexPath, "r");
+
+ /* Get either a list of matching or all packages */
+ if (MatchName != NULL) {
+ pat[0] = MatchName;
+ pat[1] = NULL;
+ MatchType = RegexExtended ? MATCH_EREGEX : MATCH_REGEX;
+ patterns = pat;
+ } else {
+ MatchType = MATCH_ALL;
+ patterns = NULL;
+ }
+
+ if (LookUpOrigin != NULL)
+ pkgs = matchbyorigin(LookUpOrigin, &err_cnt);
+ else
+ pkgs = matchinstalled(MatchType, patterns, &err_cnt);
+
+ if (err_cnt != 0)
+ errx(2, "Unable to find package database directory!");
+ if (pkgs == NULL) {
+ if (LookUpOrigin != NULL) {
+ warnx("no packages recorded with this origin");
+ return (1);
+ } else {
+ switch (MatchType) {
+ case MATCH_ALL:
+ warnx("no packages installed");
+ return (0);
+ case MATCH_EREGEX:
+ case MATCH_REGEX:
+ warnx("no packages match pattern");
+ return (1);
+ default:
+ break;
+ }
+ }
+ }
+
+ for (i = 0; pkgs[i] != NULL; i++)
+ err_cnt += pkg_do(pkgs[i]);
+
+ /* If we opened the INDEX in pkg_do(), clean up. */
+ while (!SLIST_EMPTY(&Index)) {
+ ie = SLIST_FIRST(&Index);
+ SLIST_REMOVE_HEAD(&Index, next);
+ if (ie->name != NULL)
+ free(ie->name);
+ if (ie->origin != NULL)
+ free(ie->origin);
+ free(ie);
+ }
+ if (IndexFile != NULL)
+ fclose(IndexFile);
+
+ return err_cnt;
+}
+
+/*
+ * Traditional pkg_do(). We take the package name we are passed and
+ * first slurp in the CONTENTS file, getting name and origin, then
+ * we look for it's corresponding Makefile. If that fails we pull in
+ * the INDEX, and check there.
+ */
+static int
+pkg_do(char *pkg)
+{
+ char *ch, tmp[PATH_MAX], tmp2[PATH_MAX], *latest = NULL;
+ Package plist;
+ struct index_entry *ie;
+ FILE *fp;
+ size_t len;
+
+ /* Suck in the contents list. */
+ plist.head = plist.tail = NULL;
+ plist.name = plist.origin = NULL;
+ snprintf(tmp, PATH_MAX, "%s/%s/%s", LOG_DIR, pkg, CONTENTS_FNAME);
+ fp = fopen(tmp, "r");
+ if (!fp) {
+ warnx("the package info for package '%s' is corrupt", pkg);
+ return 1;
+ }
+ read_plist(&plist, fp);
+ fclose(fp);
+ if (plist.name == NULL) {
+ warnx("%s does not appear to be a valid package!", pkg);
+ return 1;
+ }
+
+ /*
+ * First we check if the installed package has an origin, and try
+ * looking for it's Makefile. If we find the Makefile we get the
+ * latest version from there. If we fail, we start looking in the
+ * INDEX, first matching the origin and then the package name.
+ */
+ if (plist.origin != NULL && !UseINDEXOnly) {
+ snprintf(tmp, PATH_MAX, "%s/%s", PORTS_DIR, plist.origin);
+ if (isdir(tmp) && chdir(tmp) != FAIL && isfile("Makefile")) {
+ if ((latest = vpipe("/usr/bin/make -V PKGNAME", tmp)) == NULL)
+ warnx("Failed to get PKGNAME from %s/Makefile!", tmp);
+ else
+ show_version(plist, latest, "port");
+ }
+ }
+ if (latest == NULL) {
+ /* Report package as not found in INDEX if the INDEX is not required. */
+ if (IndexFile == NULL && !UseINDEXOnly)
+ show_version(plist, NULL, plist.origin);
+ else {
+ /* We only pull in the INDEX once, if needed. */
+ if (SLIST_EMPTY(&Index)) {
+ if (!IndexFile)
+ errx(2, "Unable to open %s in %s.", IndexPath, __func__);
+ while ((ch = fgetln(IndexFile, &len)) != NULL) {
+ /*
+ * Don't use strlcpy() because fgetln() doesn't
+ * return a valid C string.
+ */
+ strncpy(tmp, ch, MIN(len, PATH_MAX));
+ tmp[PATH_MAX-1] = '\0';
+ /* The INDEX has pkgname|portdir|... */
+ if ((ch = strchr(tmp, '|')) != NULL)
+ ch[0] = '\0';
+ if (ch != NULL && (ch = strchr(&ch[1], '|')) != NULL)
+ ch[0] = '\0';
+ /* Look backwards for the last two dirs = origin */
+ while (ch != NULL && *--ch != '/')
+ if (ch[0] == '\0')
+ ch = NULL;
+ while (ch != NULL && *--ch != '/')
+ if (ch[0] == '\0')
+ ch = NULL;
+ if (ch == NULL)
+ errx(2, "The INDEX does not appear to be valid!");
+ if ((ie = malloc(sizeof(struct index_entry))) == NULL)
+ errx(2, "Unable to allocate memory in %s.", __func__);
+ bzero(ie, sizeof(struct index_entry));
+ ie->name = strdup(tmp);
+ ie->origin = strdup(&ch[1]);
+ /* Who really cares if we reverse the index... */
+ SLIST_INSERT_HEAD(&Index, ie, next);
+ }
+ }
+ /* Now that we've slurped in the INDEX... */
+ SLIST_FOREACH(ie, &Index, next) {
+ if (plist.origin != NULL) {
+ if (strcmp(plist.origin, ie->origin) == 0)
+ latest = strdup(ie->name);
+ } else {
+ strlcpy(tmp, ie->name, PATH_MAX);
+ strlcpy(tmp2, plist.name, PATH_MAX);
+ /* Chop off the versions and compare. */
+ if ((ch = strrchr(tmp, '-')) == NULL)
+ errx(2, "The INDEX does not appear to be valid!");
+ ch[0] = '\0';
+ if ((ch = strrchr(tmp2, '-')) == NULL)
+ warnx("%s is not a valid package!", plist.name);
+ else
+ ch[0] = '\0';
+ if (strcmp(tmp2, tmp) == 0) {
+ if (latest != NULL) {
+ /* Multiple matches */
+ snprintf(tmp, PATH_MAX, "%s|%s", latest, ie->name);
+ free(latest);
+ latest = strdup(tmp);
+ } else
+ latest = strdup(ie->name);
+ }
+ }
+ }
+ if (latest == NULL)
+ show_version(plist, NULL, NULL);
+ else
+ show_version(plist, latest, "index");
+ }
+ }
+ if (latest != NULL)
+ free(latest);
+ free_plist(&plist);
+ return 0;
+}
+
+#define OUTPUT(c) ((PreventChars != NULL && !strchr(PreventChars, (c))) || \
+ (LimitChars != NULL && strchr(LimitChars, (c))) || \
+ (PreventChars == NULL && LimitChars == NULL))
+
+/*
+ * Do the work of comparing and outputing. Ugly, but well that's what
+ * You get when you try to match perl output in C ;-).
+ */
+void
+show_version(Package plist, const char *latest, const char *source)
+{
+ char *ch, tmp[PATH_MAX];
+ const char *ver;
+ int cmp = 0;
+
+ if (!plist.name || strlen(plist.name) == 0)
+ return;
+ if (ShowOrigin != FALSE && plist.origin != NULL)
+ strlcpy(tmp, plist.origin, PATH_MAX);
+ else {
+ strlcpy(tmp, plist.name, PATH_MAX);
+ if (!Verbose) {
+ if ((ch = strrchr(tmp, '-')) != NULL)
+ ch[0] = '\0';
+ }
+ }
+ if (latest == NULL) {
+ if (source == NULL && OUTPUT('!')) {
+ printf("%-34s !", tmp);
+ if (Verbose)
+ printf(" Comparison failed");
+ printf("\n");
+ } else if (OUTPUT('?')) {
+ printf("%-34s ?", tmp);
+ if (Verbose)
+ printf(" orphaned: %s", plist.origin);
+ printf("\n");
+ }
+ } else if (strchr(latest,'|') != NULL) {
+ if (OUTPUT('*')) {
+ printf("%-34s *", tmp);
+ if (Verbose) {
+ strlcpy(tmp, latest, PATH_MAX);
+ ch = strchr(tmp, '|');
+ ch[0] = '\0';
+
+ ver = strrchr(tmp, '-');
+ ver = ver ? &ver[1] : tmp;
+ printf(" multiple versions (index has %s", ver);
+ do {
+ ver = strrchr(&ch[1], '-');
+ ver = ver ? &ver[1] : &ch[1];
+ if ((ch = strchr(&ch[1], '|')) != NULL)
+ ch[0] = '\0';
+ printf(", %s", ver);
+ } while (ch != NULL);
+ printf(")");
+ }
+ printf("\n");
+ }
+ } else {
+ cmp = version_cmp(plist.name, latest);
+ ver = strrchr(latest, '-');
+ ver = ver ? &ver[1] : latest;
+ if (cmp < 0 && OUTPUT('<')) {
+ printf("%-34s %c", tmp, Quiet ? '\0' : '<');
+ if (Verbose)
+ printf(" needs updating (%s has %s)", source, ver);
+ printf("\n");
+ } else if (cmp == 0 && OUTPUT('=')) {
+ printf("%-34s %c", tmp, Quiet ? '\0' : '=');
+ if (Verbose)
+ printf(" up-to-date with %s", source);
+ printf("\n");
+ } else if (cmp > 0 && OUTPUT('>')) {
+ printf("%-34s %c", tmp, Quiet ? '\0' : '>');
+ if (Verbose)
+ printf(" succeeds %s (%s has %s)", source, source, ver);
+ printf("\n");
+ }
+ }
+}
+
+int
+version_match(char *pattern, const char *pkgname)
+{
+ int ret = 0;
+ int matchstream = 0;
+ FILE *fp = NULL;
+ Boolean isTMP = FALSE;
+
+ if (isURL(pkgname)) {
+ fp = fetchGetURL(pkgname, "");
+ isTMP = TRUE;
+ matchstream = 1;
+ if (fp == NULL)
+ errx(2, "Unable to open %s.", pkgname);
+ } else if (pkgname[0] == '/') {
+ fp = fopen(pkgname, "r");
+ isTMP = TRUE;
+ matchstream = 1;
+ if (fp == NULL)
+ errx(2, "Unable to open %s.", pkgname);
+ } else if (strcmp(pkgname, "-") == 0) {
+ fp = stdin;
+ matchstream = 1;
+ } else if (isURL(pattern)) {
+ fp = fetchGetURL(pattern, "");
+ isTMP = TRUE;
+ matchstream = -1;
+ if (fp == NULL)
+ errx(2, "Unable to open %s.", pattern);
+ } else if (pattern[0] == '/') {
+ fp = fopen(pattern, "r");
+ isTMP = TRUE;
+ matchstream = -1;
+ if (fp == NULL)
+ errx(2, "Unable to open %s.", pattern);
+ } else if (strcmp(pattern, "-") == 0) {
+ fp = stdin;
+ matchstream = -1;
+ } else {
+ ret = pattern_match(MATCH_GLOB, pattern, pkgname);
+ }
+
+ if (fp != NULL) {
+ size_t len;
+ char *line;
+ while ((line = fgetln(fp, &len)) != NULL) {
+ int match;
+ char *ch, ln[2048];
+ size_t lnlen;
+ if (len > 0 && line[len-1] == '\n')
+ len --;
+ lnlen = len;
+ if (lnlen > sizeof(ln)-1)
+ lnlen = sizeof(ln)-1;
+ memcpy(ln, line, lnlen);
+ ln[lnlen] = '\0';
+ if ((ch = strchr(ln, '|')) != NULL)
+ ch[0] = '\0';
+ if (matchstream > 0)
+ match = pattern_match(MATCH_GLOB, pattern, ln);
+ else
+ match = pattern_match(MATCH_GLOB, ln, pkgname);
+ if (match == 1) {
+ ret = 1;
+ printf("%.*s\n", (int)len, line);
+ }
+ }
+ if (isTMP)
+ fclose(fp);
+ }
+
+ return ret;
+}
+
+void
+cleanup(int sig)
+{
+ if (sig)
+ exit(1);
+}
diff --git a/usr.sbin/pkg_install/version/pkg_version.1 b/usr.sbin/pkg_install/version/pkg_version.1
new file mode 100644
index 0000000..db0d7af
--- /dev/null
+++ b/usr.sbin/pkg_install/version/pkg_version.1
@@ -0,0 +1,256 @@
+.\"
+.\" Copyright 1998 Bruce A. Mah
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 30, 2008
+.Dt PKG_VERSION 1
+.Os
+.Sh NAME
+.Nm pkg_version
+.Nd summarize installed versions of packages
+.Sh SYNOPSIS
+.Nm
+.Op Fl hIoqv
+.Op Fl l Ar limchar
+.Op Fl L Ar limchar
+.Oo
+.Op Fl X
+.Fl s Ar string
+.Oc
+.Op Fl O Ar origin
+.Op Ar index
+.Nm
+.Fl t Ar version1 version2
+.Nm
+.Fl T Ar pkgname pattern
+.Sh DESCRIPTION
+The
+.Nm
+command is used to produce a report of non-base software packages
+installed using the
+.Xr pkg_add 1
+command.
+.Pp
+Each package's version number is checked against one of two sources to
+see if that package may require updating.
+If the package contains
+information about its origin in the
+.Fx
+ports tree, and a version number can be determined from the port's
+.Pa Makefile ,
+then the version number from the
+.Pa Makefile
+will be used to determine whether the installed package is up-to-date
+or requires updating.
+.Pp
+If no origin for a package can be found, or if the port's
+.Pa Makefile
+cannot be located,
+.Nm
+will search for the package in the ports collection index file
+(typically
+.Pa /usr/ports/INDEX-8 ) .
+Any matching version number(s) there will be used to determine whether
+the installed package is up-to-date or requires updating.
+.Pp
+Generally, using the version number from a port's
+.Pa Makefile
+will provide a more accurate result, since, unlike the index file, it
+provides an unambiguous current version number, even when multiple
+versions of a port exist in the ports collection.
+Moreover, the ports collection index file is only updated at
+intervals, meaning that it may not completely reflect the version
+numbers of the software contained in the ports collection.
+.Pp
+Each package name is printed, along with a one-character status flag:
+.Bl -tag -width indent
+.It Li =
+The installed version of the package is current.
+.It Li \&<
+The installed version of the package is older than the current version.
+.It Li \&>
+The installed version of the package is newer than the current version.
+This situation can arise with an out-of-date index file, or when
+testing new ports.
+.It Li \&?
+The installed package does not appear in the index.
+This could be due to an out of date index or a package taken from a PR
+that has not yet been committed.
+.It Li *
+There are multiple versions of a particular software package
+listed in the index file.
+Examples from the
+.Fx
+ports collection are the Tcl toolkit or the
+.Tn EMACS
+editor.
+.It Li \&!
+The installed package exists in the index but for some reason,
+.Nm
+was unable to compare the version number of the installed package
+with the corresponding entry in the index.
+.El
+.Sh OPTIONS
+The
+.Nm
+utility supports several command-line arguments:
+.Bl -tag -width indent
+.It Fl h , -help
+Print help message.
+.It Fl I , -index-only
+Use only the index file for determining if a package is out of date.
+This is much faster than using the version number from a port's
+Makefile, at the expense of potentially giving an incorrect result if
+the index file is out of date.
+.It Fl l , -status Ar limchar
+Limit the output to those packages whose status flag matches the
+character(s) in
+.Ar limchar .
+More than one character can be specified in
+.Ar limchar .
+Note that because some of the status flag characters are also special
+to the shell, it is best to quote
+.Ar limchar
+with single quotes.
+.It Fl L , -no-status Ar limchar
+Limit the output to those packages whose status flag does not match
+.Ar limchar .
+You may specify more than one character to match in
+.Ar limchar .
+Note that because some of the status flag characters are also special
+to the shell, it is best to quote
+.Ar limchar
+with single quotes.
+.It Fl o , -show-origin
+Show the origin recorded on package generation instead of the package
+name.
+.It Fl O , -origin Ar origin
+Only list packages whose registered origin is
+.Ar origin .
+.It Fl q , -quiet
+Enable quiet output.
+Quiet output precludes printing the
+.Ar limchar
+when used with
+.Fl l
+or
+.Fl L .
+This is useful when used as the input to
+.Xr portupgrade 8 .
+.It Fl s , -match Ar string
+Limit the output to those packages whose names match a given
+.Ar string .
+.It Fl X , -extended
+Interpret
+.Ar string
+as a extended regular expression.
+.It Fl t
+Test a pair of version number strings and exit.
+The output consists of one of the single characters
+.Li =
+(equal),
+.Li \&<
+(right-hand number greater), or
+.Li \&>
+(left-hand number greater) on standard output.
+This flag is mostly useful for scripts or for testing.
+.It Fl T
+Test whether
+.Ar pkgname
+is matched by
+.Ar pattern
+and set the exit code accordingly.
+.Fl T
+can also be used in `filter mode':
+When one of the arguments is `-', standard input is used, and lines
+with matching package names/patterns are echoed to standard output.
+.It Fl v
+Enable verbose output.
+Verbose output includes some English-text
+interpretations of the version number comparisons, as well as the
+version numbers compared for each package.
+Non-verbose output is
+probably easier for programs or scripts to parse.
+.It Ar index
+Specify the index to be used as a basis of comparison.
+This index can
+be specified as a filename (in the local file system) or a URL.
+Any
+URL understandable by
+.Xr fetch 1
+can be used here.
+If no
+.Ar index
+file is specified on the command line,
+.Pa /usr/ports/INDEX-8
+is used.
+.El
+.Sh FILES
+.Bl -tag -width /usr/ports/INDEX-8 -compact
+.It Pa /usr/ports/INDEX-8
+Default index file.
+.El
+.Sh EXAMPLES
+The following is a typical invocation of the
+.Nm
+command, which checks the installed packages against the local ports
+index file:
+.Pp
+.Dl % pkg_version -v
+.Pp
+The command below generates a report against
+the version numbers in the on-line ports collection:
+.Pp
+.Dl % pkg_version http://www.FreeBSD.org/ports/INDEX-8
+.Pp
+The following command compares two package version strings:
+.Pp
+.Dl % pkg_version -t 1.5 1.5.1
+.Sh COMPATIBILITY
+The
+.Fl c
+option has been deprecated and is no longer supported.
+.Sh SEE ALSO
+.Xr fetch 1 ,
+.Xr pkg_add 1 ,
+.Xr pkg_create 1 ,
+.Xr pkg_delete 1 ,
+.Xr pkg_info 1 ,
+.Xr portupgrade 8
+.Sh AUTHORS
+The
+.Nm
+utility was written by
+.An Jeremy D. Lea Aq reg@FreeBSD.org ,
+partially based on a Perl script written by
+.An Bruce A. Mah Aq bmah@FreeBSD.org .
+.Sh CONTRIBUTORS
+.An Nik Clayton Aq nik@FreeBSD.org ,
+.An Dominic Mitchell Aq dom@palmerharvey.co.uk ,
+.An Mark Ovens Aq marko@FreeBSD.org ,
+.An Doug Barton Aq DougB@gorean.org ,
+.An Akinori MUSHA Aq knu@FreeBSD.org ,
+.An Oliver Eikemeier Aq eik@FreeBSD.org
diff --git a/usr.sbin/pkg_install/version/test-pkg_version.sh b/usr.sbin/pkg_install/version/test-pkg_version.sh
new file mode 100755
index 0000000..95486c1
--- /dev/null
+++ b/usr.sbin/pkg_install/version/test-pkg_version.sh
@@ -0,0 +1,94 @@
+#!/bin/sh
+#
+# Copyright 2001 Bruce A. Mah
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must 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.
+#
+# test-pkg_version.sh
+#
+# Regression testing for pkg_version
+# Originally from an idea by "Akinori MUSHA" <knu@iDaemons.org>
+#
+# $FreeBSD$
+#
+
+ECHO=echo
+PKG_VERSION=./pkg_version
+
+test-pv ( ) { \
+ setvar v1 $1
+ setvar answer $2
+ setvar v2 $3
+ setvar type $4
+ res=`${PKG_VERSION} -t ${v1} ${v2}`
+ if [ ${res} != ${answer} ]; then \
+ ${ECHO} "${type} test failed (${v1} ${res} ${v2}, should have been ${answer})"; \
+ fi
+}
+
+# Test coercion of default PORTREVISION and PORTEPOCH
+test-pv 0.10 "=" 0.10_0 coercion
+test-pv 0.10 "=" 0.10,0 coercion
+test-pv 0.10 "=" 0.10_0,0 coercion
+
+# Test various comparisons
+test-pv 1.0 "=" 1.0 equality
+test-pv 2.15a "=" 2.15a equality
+
+test-pv 0.10 ">" 0.9 inequality
+test-pv 0.9 "<" 0.10 inequality
+
+test-pv 2.3p10 ">" 2.3p9 number/letter
+test-pv 1.6.0 ">" 1.6.0.p3 number/letter
+test-pv 1.0.b ">" 1.0.a3 number/letter
+test-pv 1.0a ">" 1.0 number/letter
+test-pv 1.0a "<" 1.0b number/letter
+test-pv 5.0a ">" 5.0.b number/letter
+
+test-pv 1.5_1 ">" 1.5 portrevision
+test-pv 1.5_2 ">" 1.5_1 portrevision
+test-pv 1.5_1 "<" 1.5.0.1 portrevision
+test-pv 1.5 "<" 1.5.0.1 portrevision
+
+test-pv 00.01.01,1 ">" 99.12.31 portepoch
+test-pv 0.0.1_1,2 ">" 0.0.1,2 portrevision/portepoch
+test-pv 0.0.1_1,3 ">" 0.0.1_2,2 portrevision/portepoch
+
+test-pv 2.0 ">" 2.a2 number/letter
+test-pv 3 "=" 3.0 equality
+test-pv 4a "<" 4a0 letter/zero
+test-pv 10a1b2 "=" 10a1.b2 separator
+
+test-pv 7pl "=" 7.pl patchevel
+test-pv 8.0.a "=" 8.0alpha alpha
+test-pv 9.b3.0 "=" 9beta3 beta
+test-pv 10.pre7 "=" 10pre7.0 pre
+test-pv 11.r "=" 11.rc rc
+
+test-pv 12pl "<" 12alpha alpha/patchevel
+test-pv 13.* "<" 13.pl star/patchevel
+
+test-pv 1.0.0+2003.09.06 "=" 1.0+2003.09.06 plus/multiple
+test-pv 1.0.1+2003.09.06 ">" 1.0+2003.09.06 plus/multiple
+test-pv 1.0.0+2003.09.06 "<" 1.0+2003.09.06_1 plus/portrevision
+test-pv 1.0.1+2003.09.06 ">" 1.0+2003.09.06_1 plus/portrevision
diff --git a/usr.sbin/pkg_install/version/version.h b/usr.sbin/pkg_install/version/version.h
new file mode 100644
index 0000000..412c395
--- /dev/null
+++ b/usr.sbin/pkg_install/version/version.h
@@ -0,0 +1,43 @@
+/* $FreeBSD$ */
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jeremy D. Lea.
+ * 11 May 2002
+ *
+ * This is the version module. Based on pkg_version.pl by Bruce A. Mah.
+ *
+ */
+
+#ifndef _INST_VERSION_H_INCLUDE
+#define _INST_VERSION_H_INCLUDE
+
+struct index_entry {
+ SLIST_ENTRY(index_entry) next;
+ char *name;
+ char *origin;
+};
+SLIST_HEAD(index_head, index_entry);
+
+extern char *LimitChars;
+extern char *PreventChars;
+extern char *MatchName;
+extern char *LookUpOrigin;
+extern Boolean RegexExtended;
+extern Boolean UseINDEXOnly;
+extern Boolean ShowOrigin;
+
+extern int version_match(char *, const char *);
+
+#endif /* _INST_VERSION_H_INCLUDE */
diff --git a/usr.sbin/pmcannotate/Makefile b/usr.sbin/pmcannotate/Makefile
new file mode 100644
index 0000000..e087bb3
--- /dev/null
+++ b/usr.sbin/pmcannotate/Makefile
@@ -0,0 +1,12 @@
+#
+# $FreeBSD$
+#
+
+PROG= pmcannotate
+MAN= pmcannotate.8
+
+WARNS?= 6
+
+SRCS= pmcannotate.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pmcannotate/pmcannotate.8 b/usr.sbin/pmcannotate/pmcannotate.8
new file mode 100644
index 0000000..38a413a
--- /dev/null
+++ b/usr.sbin/pmcannotate/pmcannotate.8
@@ -0,0 +1,108 @@
+.\" Copyright (c) 2008 Nokia Corporation
+.\" All rights reserved.
+.\"
+.\" This software was developed by Attilio Rao for the IPSO project under
+.\" contract to Nokia Corporation.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 ``as is'' and
+.\" any express or implied 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 November 20, 2008
+.Os
+.Dt PMCANNOTATE 8
+.Sh NAME
+.Nm pmcannotate
+.Nd "sources printout with inlined profiling"
+.Sh SYNOPSIS
+.Nm
+.Op Fl a
+.Op Fl h
+.Op Fl k Ar pathname
+.Op Fl l Ar level
+.Ar pmcout.out binaryobj
+.Sh DESCRIPTION
+The
+.Nm
+utility can produce both C sources or assembly sources of a program with
+a line-by-line based profiling.
+The profiling informations are retrieved through a
+.Xr pmcstat 8
+raw output while the program operations are retrieved through the
+.Xr objdump 1
+tool.
+.Pp
+When calling
+.Nm
+the raw output is passed through the
+.Ar pmcout.out
+argument, while the program is passed through the
+.Ar binaryobj
+argument.
+.Pp
+As long as
+.Nm
+relies on
+.Xr objdump 1
+and
+.Xr pmcstat 8
+to work, it will fail if one of them is not available.
+.Sh OPTIONS
+The following options are available:
+.Bl -tag -width indent
+.It Fl a
+Shows the program profiling inlined in the assembly code only.
+No C informations involving C sources are provided.
+.It Fl h
+Prints out informations about the usage of the tool.
+.It Fl l Ar level
+Changes the lower bound (expressed in percentage) for traced functions
+that will be printed out in the report.
+The default value is 0.5%.
+.It Fl k Ar kerneldir
+Set the pathname of the kernel directory to argument
+.Ar kerneldir .
+This directory specifies where
+.Nm
+should look for the kernel and its modules.
+The default is
+.Pa /boot/kernel .
+.Sh LIMITATIONS
+As long as
+.Nm
+relies on the
+.Xr objdump 1
+utility to retrieve the C code, the program needs to be compiled with
+debugging options.
+Sometimes, in particular with heavy optimization levels, the
+.Xr objdump 1
+utility embeds the code of inlining functions directly in the callers,
+making an output difficult to read.
+The x86 version reports the sampling from pmcstat collecting the following
+instruction in regard of the interrupted one.
+This means that the samples may be attributed to the line below the one
+of interest.
+.Sh SEE ALSO
+.Xr objdump 1 ,
+.Xr pmcstat 8
+.Sh AUTHORS
+.An Attilio Rao Aq attilio@FreeBSD.org
diff --git a/usr.sbin/pmcannotate/pmcannotate.c b/usr.sbin/pmcannotate/pmcannotate.c
new file mode 100644
index 0000000..1a5dec2
--- /dev/null
+++ b/usr.sbin/pmcannotate/pmcannotate.c
@@ -0,0 +1,804 @@
+/*-
+ * Copyright (c) 2008 Nokia Corporation
+ * All rights reserved.
+ *
+ * This software was developed by Attilio Rao for the IPSO project under
+ * contract to Nokia Corporation.
+ *
+ * Redistribution and use in source and binary forms, 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 ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED 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/queue.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <unistd.h>
+
+#define FNBUFF 161
+#define LNBUFF 161
+
+#define TMPPATH "/tmp/pmcannotate.XXXXXX"
+
+#define FATAL(ptr, x ...) do { \
+ fqueue_deleteall(); \
+ general_deleteall(); \
+ if ((ptr) != NULL) \
+ perror(ptr); \
+ fprintf(stderr, ##x); \
+ remove(tbfl); \
+ remove(tofl); \
+ exit(EXIT_FAILURE); \
+} while (0)
+
+#define PERCSAMP(x) ((x) * 100 / totalsamples)
+
+struct entry {
+ TAILQ_ENTRY(entry) en_iter;
+ char *en_name;
+ uintptr_t en_pc;
+ uintptr_t en_ostart;
+ uintptr_t en_oend;
+ u_int en_nsamples;
+};
+
+struct aggent {
+ TAILQ_ENTRY(aggent) ag_fiter;
+ long ag_offset;
+ uintptr_t ag_ostart;
+ uintptr_t ag_oend;
+ char *ag_name;
+ u_int ag_nsamples;
+};
+
+static struct aggent *agg_create(const char *name, u_int nsamples,
+ uintptr_t start, uintptr_t end);
+static void agg_destroy(struct aggent *agg) __unused;
+static void asmparse(FILE *fp);
+static int cparse(FILE *fp);
+static void entry_acqref(struct entry *entry);
+static struct entry *entry_create(const char *name, uintptr_t pc,
+ uintptr_t start, uintptr_t end);
+static void entry_destroy(struct entry *entry) __unused;
+static void fqueue_compact(float th);
+static void fqueue_deleteall(void);
+static struct aggent *fqueue_findent_by_name(const char *name);
+static int fqueue_getall(const char *bin, char *temp, int asmf);
+static int fqueue_insertent(struct entry *entry);
+static int fqueue_insertgen(void);
+static void general_deleteall(void);
+static struct entry *general_findent(uintptr_t pc);
+static void general_insertent(struct entry *entry);
+static void general_printasm(FILE *fp, struct aggent *agg);
+static int general_printc(FILE *fp, struct aggent *agg);
+static int printblock(FILE *fp, struct aggent *agg);
+static void usage(const char *progname) __dead2;
+
+static TAILQ_HEAD(, entry) mainlst = TAILQ_HEAD_INITIALIZER(mainlst);
+static TAILQ_HEAD(, aggent) fqueue = TAILQ_HEAD_INITIALIZER(fqueue);
+
+/*
+ * Use a float value in order to automatically promote operations
+ * to return a float value rather than use casts.
+ */
+static float totalsamples;
+
+/*
+ * Identifies a string cointaining objdump's assembly printout.
+ */
+static inline int
+isasminline(const char *str)
+{
+ void *ptr;
+ int nbytes;
+
+ if (isxdigit(str[1]) == 0)
+ return (0);
+ if (sscanf(str, " %p%n", &ptr, &nbytes) != 1)
+ return (0);
+ if (str[nbytes] != ':' || isspace(str[nbytes + 1]) == 0)
+ return (0);
+ return (1);
+}
+
+/*
+ * Identifies a string containing objdump's assembly printout
+ * for a new function.
+ */
+static inline int
+newfunction(const char *str)
+{
+ char fname[FNBUFF];
+ void *ptr;
+ int nbytes;
+
+ if (isspace(str[0]))
+ return (0);
+ if (sscanf(str, "%p <%[^>:]>:%n", &ptr, fname, &nbytes) != 2)
+ return (0);
+ return (nbytes);
+}
+
+/*
+ * Create a new first-level aggregation object for a specified
+ * function.
+ */
+static struct aggent *
+agg_create(const char *name, u_int nsamples, uintptr_t start, uintptr_t end)
+{
+ struct aggent *agg;
+
+ agg = calloc(1, sizeof(struct aggent));
+ if (agg == NULL)
+ return (NULL);
+ agg->ag_name = strdup(name);
+ if (agg->ag_name == NULL) {
+ free(agg);
+ return (NULL);
+ }
+ agg->ag_nsamples = nsamples;
+ agg->ag_ostart = start;
+ agg->ag_oend = end;
+ return (agg);
+}
+
+/*
+ * Destroy a first-level aggregation object for a specified
+ * function.
+ */
+static void
+agg_destroy(struct aggent *agg)
+{
+
+ free(agg->ag_name);
+ free(agg);
+}
+
+/*
+ * Analyze the "objdump -d" output, locate functions and start
+ * printing out the assembly functions content.
+ * We do not use newfunction() because we actually need the
+ * function name in available form, but the heurstic used is
+ * the same.
+ */
+static void
+asmparse(FILE *fp)
+{
+ char buffer[LNBUFF], fname[FNBUFF];
+ struct aggent *agg;
+ void *ptr;
+
+ while (fgets(buffer, LNBUFF, fp) != NULL) {
+ if (isspace(buffer[0]))
+ continue;
+ if (sscanf(buffer, "%p <%[^>:]>:", &ptr, fname) != 2)
+ continue;
+ agg = fqueue_findent_by_name(fname);
+ if (agg == NULL)
+ continue;
+ agg->ag_offset = ftell(fp);
+ }
+
+ TAILQ_FOREACH(agg, &fqueue, ag_fiter) {
+ if (fseek(fp, agg->ag_offset, SEEK_SET) == -1)
+ return;
+ printf("Profile trace for function: %s() [%.2f%%]\n",
+ agg->ag_name, PERCSAMP(agg->ag_nsamples));
+ general_printasm(fp, agg);
+ printf("\n");
+ }
+}
+
+/*
+ * Analyze the "objdump -S" output, locate functions and start
+ * printing out the C functions content.
+ * We do not use newfunction() because we actually need the
+ * function name in available form, but the heurstic used is
+ * the same.
+ * In order to maintain the printout sorted, on the first pass it
+ * simply stores the file offsets in order to fastly moved later
+ * (when the file is hot-cached also) when the real printout will
+ * happen.
+ */
+static int
+cparse(FILE *fp)
+{
+ char buffer[LNBUFF], fname[FNBUFF];
+ struct aggent *agg;
+ void *ptr;
+
+ while (fgets(buffer, LNBUFF, fp) != NULL) {
+ if (isspace(buffer[0]))
+ continue;
+ if (sscanf(buffer, "%p <%[^>:]>:", &ptr, fname) != 2)
+ continue;
+ agg = fqueue_findent_by_name(fname);
+ if (agg == NULL)
+ continue;
+ agg->ag_offset = ftell(fp);
+ }
+
+ TAILQ_FOREACH(agg, &fqueue, ag_fiter) {
+ if (fseek(fp, agg->ag_offset, SEEK_SET) == -1)
+ return (-1);
+ printf("Profile trace for function: %s() [%.2f%%]\n",
+ agg->ag_name, PERCSAMP(agg->ag_nsamples));
+ if (general_printc(fp, agg) == -1)
+ return (-1);
+ printf("\n");
+ }
+ return (0);
+}
+
+/*
+ * Bump the number of samples for any raw entry.
+ */
+static void
+entry_acqref(struct entry *entry)
+{
+
+ entry->en_nsamples++;
+}
+
+/*
+ * Create a new raw entry object for a specified function.
+ */
+static struct entry *
+entry_create(const char *name, uintptr_t pc, uintptr_t start, uintptr_t end)
+{
+ struct entry *obj;
+
+ obj = calloc(1, sizeof(struct entry));
+ if (obj == NULL)
+ return (NULL);
+ obj->en_name = strdup(name);
+ if (obj->en_name == NULL) {
+ free(obj);
+ return (NULL);
+ }
+ obj->en_pc = pc;
+ obj->en_ostart = start;
+ obj->en_oend = end;
+ obj->en_nsamples = 1;
+ return (obj);
+}
+
+/*
+ * Destroy a raw entry object for a specified function.
+ */
+static void
+entry_destroy(struct entry *entry)
+{
+
+ free(entry->en_name);
+ free(entry);
+}
+
+/*
+ * Specify a lower bound in percentage and drop from the
+ * first-level aggregation queue all the objects with a
+ * smaller impact.
+ */
+static void
+fqueue_compact(float th)
+{
+ u_int thi;
+ struct aggent *agg, *tmpagg;
+
+ if (totalsamples == 0)
+ return;
+
+ /* Revert the percentage calculation. */
+ thi = th * totalsamples / 100;
+ TAILQ_FOREACH_SAFE(agg, &fqueue, ag_fiter, tmpagg)
+ if (agg->ag_nsamples < thi)
+ TAILQ_REMOVE(&fqueue, agg, ag_fiter);
+}
+
+/*
+ * Flush the first-level aggregates queue.
+ */
+static void
+fqueue_deleteall()
+{
+ struct aggent *agg;
+
+ while (TAILQ_EMPTY(&fqueue) == 0) {
+ agg = TAILQ_FIRST(&fqueue);
+ TAILQ_REMOVE(&fqueue, agg, ag_fiter);
+ }
+}
+
+/*
+ * Insert a raw entry into the aggregations queue.
+ * If the respective first-level aggregation object
+ * does not exist create it and maintain it sorted
+ * in respect of the number of samples.
+ */
+static int
+fqueue_insertent(struct entry *entry)
+{
+ struct aggent *obj, *tmp;
+ int found;
+
+ found = 0;
+ TAILQ_FOREACH(obj, &fqueue, ag_fiter)
+ if (!strcmp(obj->ag_name, entry->en_name)) {
+ found = 1;
+ obj->ag_nsamples += entry->en_nsamples;
+ break;
+ }
+
+ /*
+ * If the firt-level aggregation object alredy exist,
+ * just aggregate the samples and, if needed, resort
+ * it.
+ */
+ if (found) {
+ TAILQ_REMOVE(&fqueue, obj, ag_fiter);
+ found = 0;
+ TAILQ_FOREACH(tmp, &fqueue, ag_fiter)
+ if (obj->ag_nsamples > tmp->ag_nsamples) {
+ found = 1;
+ break;
+ }
+ if (found)
+ TAILQ_INSERT_BEFORE(tmp, obj, ag_fiter);
+ else
+ TAILQ_INSERT_TAIL(&fqueue, obj, ag_fiter);
+ return (0);
+ }
+
+ /*
+ * If the first-level aggregation object does not
+ * exist, create it and put in the sorted queue.
+ * If this is the first object, we need to set the
+ * head of the queue.
+ */
+ obj = agg_create(entry->en_name, entry->en_nsamples, entry->en_ostart,
+ entry->en_oend);
+ if (obj == NULL)
+ return (-1);
+ if (TAILQ_EMPTY(&fqueue) != 0) {
+ TAILQ_INSERT_HEAD(&fqueue, obj, ag_fiter);
+ return (0);
+ }
+ TAILQ_FOREACH(tmp, &fqueue, ag_fiter)
+ if (obj->ag_nsamples > tmp->ag_nsamples) {
+ found = 1;
+ break;
+ }
+ if (found)
+ TAILQ_INSERT_BEFORE(tmp, obj, ag_fiter);
+ else
+ TAILQ_INSERT_TAIL(&fqueue, obj, ag_fiter);
+ return (0);
+}
+
+/*
+ * Lookup a first-level aggregation object by name.
+ */
+static struct aggent *
+fqueue_findent_by_name(const char *name)
+{
+ struct aggent *obj;
+
+ TAILQ_FOREACH(obj, &fqueue, ag_fiter)
+ if (!strcmp(obj->ag_name, name))
+ return (obj);
+ return (NULL);
+}
+
+/*
+ * Return the number of object in the first-level aggregations queue.
+ */
+static int
+fqueue_getall(const char *bin, char *temp, int asmf)
+{
+ char tmpf[MAXPATHLEN * 2 + 50];
+ struct aggent *agg;
+ uintptr_t start, end;
+
+ if (mkstemp(temp) == -1)
+ return (-1);
+ TAILQ_FOREACH(agg, &fqueue, ag_fiter) {
+ bzero(tmpf, sizeof(tmpf));
+ start = agg->ag_ostart;
+ end = agg->ag_oend;
+
+ /*
+ * Fix-up the end address in order to show it in the objdump's
+ * trace.
+ */
+ end++;
+ if (asmf)
+ snprintf(tmpf, sizeof(tmpf),
+ "objdump --start-address=%p "
+ "--stop-address=%p -d %s >> %s", (void *)start,
+ (void *)end, bin, temp);
+ else
+ snprintf(tmpf, sizeof(tmpf),
+ "objdump --start-address=%p "
+ "--stop-address=%p -S %s >> %s", (void *)start,
+ (void *)end, bin, temp);
+ if (system(tmpf) != 0)
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Insert all the raw entries present in the general queue
+ * into the first-level aggregations queue.
+ */
+static int
+fqueue_insertgen(void)
+{
+ struct entry *obj;
+
+ TAILQ_FOREACH(obj, &mainlst, en_iter)
+ if (fqueue_insertent(obj) == -1)
+ return (-1);
+ return (0);
+}
+
+/*
+ * Flush the raw entries general queue.
+ */
+static void
+general_deleteall()
+{
+ struct entry *obj;
+
+ while (TAILQ_EMPTY(&mainlst) == 0) {
+ obj = TAILQ_FIRST(&mainlst);
+ TAILQ_REMOVE(&mainlst, obj, en_iter);
+ }
+}
+
+/*
+ * Lookup a raw entry by the PC.
+ */
+static struct entry *
+general_findent(uintptr_t pc)
+{
+ struct entry *obj;
+
+ TAILQ_FOREACH(obj, &mainlst, en_iter)
+ if (obj->en_pc == pc)
+ return (obj);
+ return (NULL);
+}
+
+/*
+ * Insert a new raw entry in the general queue.
+ */
+static void
+general_insertent(struct entry *entry)
+{
+
+ TAILQ_INSERT_TAIL(&mainlst, entry, en_iter);
+}
+
+/*
+ * Printout the body of an "objdump -d" assembly function.
+ * It does simply stops when a new function is encountered,
+ * bringing back the file position in order to not mess up
+ * subsequent analysis.
+ * C lines and others not recognized are simply skipped.
+ */
+static void
+general_printasm(FILE *fp, struct aggent *agg)
+{
+ char buffer[LNBUFF];
+ struct entry *obj;
+ int nbytes;
+ void *ptr;
+
+ while (fgets(buffer, LNBUFF, fp) != NULL) {
+ if ((nbytes = newfunction(buffer)) != 0) {
+ fseek(fp, nbytes * -1, SEEK_CUR);
+ break;
+ }
+ if (!isasminline(buffer))
+ continue;
+ if (sscanf(buffer, " %p:", &ptr) != 1)
+ continue;
+ obj = general_findent((uintptr_t)ptr);
+ if (obj == NULL)
+ printf("\t| %s", buffer);
+ else
+ printf("%.2f%%\t| %s",
+ (float)obj->en_nsamples * 100 / agg->ag_nsamples,
+ buffer);
+ }
+}
+
+/*
+ * Printout the body of an "objdump -S" function.
+ * It does simply stops when a new function is encountered,
+ * bringing back the file position in order to not mess up
+ * subsequent analysis.
+ * It expect from the starting to the end to find, always, valid blocks
+ * (see below for an explanation of the "block" concept).
+ */
+static int
+general_printc(FILE *fp, struct aggent *agg)
+{
+ char buffer[LNBUFF];
+
+ while (fgets(buffer, LNBUFF, fp) != NULL) {
+ fseek(fp, strlen(buffer) * -1, SEEK_CUR);
+ if (newfunction(buffer) != 0)
+ break;
+ if (printblock(fp, agg) == -1)
+ return (-1);
+ }
+ return (0);
+}
+
+/*
+ * Printout a single block inside an "objdump -S" function.
+ * The block is composed of a first part in C and subsequent translation
+ * in assembly.
+ * This code also operates a second-level aggregation packing together
+ * samples relative to PCs into a (lower bottom) block with their
+ * C (higher half) counterpart.
+ */
+static int
+printblock(FILE *fp, struct aggent *agg)
+{
+ char buffer[LNBUFF];
+ long lstart;
+ struct entry *obj;
+ u_int tnsamples;
+ int done, nbytes, sentinel;
+ void *ptr;
+
+ /*
+ * We expect the first thing of the block is C code, so simply give
+ * up if asm line is found.
+ */
+ lstart = ftell(fp);
+ sentinel = 0;
+ for (;;) {
+ if (fgets(buffer, LNBUFF, fp) == NULL)
+ return (0);
+ if (isasminline(buffer) != 0)
+ break;
+ sentinel = 1;
+ nbytes = newfunction(buffer);
+ if (nbytes != 0) {
+ if (fseek(fp, nbytes * -1, SEEK_CUR) == -1)
+ return (-1);
+ return (0);
+ }
+ }
+
+ /*
+ * If the sentinel is not set, it means it did not match any
+ * "high half" for this code so simply give up.
+ * Operates the second-level aggregation.
+ */
+ tnsamples = 0;
+ do {
+ if (sentinel == 0)
+ return (-1);
+ if (sscanf(buffer, " %p:", &ptr) != 1)
+ return (-1);
+ obj = general_findent((uintptr_t)ptr);
+ if (obj != NULL)
+ tnsamples += obj->en_nsamples;
+ } while (fgets(buffer, LNBUFF, fp) != NULL && isasminline(buffer) != 0);
+
+ /* Rewind to the start of the block in order to start the printout. */
+ if (fseek(fp, lstart, SEEK_SET) == -1)
+ return (-1);
+
+ /* Again the high half of the block rappresenting the C part. */
+ done = 0;
+ while (fgets(buffer, LNBUFF, fp) != NULL && isasminline(buffer) == 0) {
+ if (tnsamples == 0 || done != 0)
+ printf("\t| %s", buffer);
+ else {
+ done = 1;
+ printf("%.2f%%\t| %s",
+ (float)tnsamples * 100 / agg->ag_nsamples, buffer);
+ }
+ }
+
+ /*
+ * Again the low half of the block rappresenting the asm
+ * translation part.
+ */
+ for (;;) {
+ if (fgets(buffer, LNBUFF, fp) == NULL)
+ return (0);
+ if (isasminline(buffer) == 0)
+ break;
+ nbytes = newfunction(buffer);
+ if (nbytes != 0) {
+ if (fseek(fp, nbytes * -1, SEEK_CUR) == -1)
+ return (-1);
+ return (0);
+ }
+ }
+ if (fseek(fp, strlen(buffer) * -1, SEEK_CUR) == -1)
+ return (-1);
+ return (0);
+}
+
+/*
+ * Helper printout functions.
+ */
+static void
+usage(const char *progname)
+{
+
+ fprintf(stderr,
+ "usage: %s [-a] [-h] [-k kfile] [-l lb] pmcraw.out binary\n",
+ progname);
+ exit(EXIT_SUCCESS);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char buffer[LNBUFF], fname[FNBUFF], tbfl[] = TMPPATH, tofl[] = TMPPATH;
+ char tmpf[MAXPATHLEN * 2 + 50];
+ float limit;
+ char *bin, *exec, *kfile, *ofile;
+ struct entry *obj;
+ FILE *gfp, *bfp;
+ void *ptr, *hstart, *hend;
+ uintptr_t tmppc, ostart, oend;
+ int cget, asmsrc;
+
+ exec = argv[0];
+ ofile = NULL;
+ bin = NULL;
+ kfile = NULL;
+ asmsrc = 0;
+ limit = 0.5;
+ while ((cget = getopt(argc, argv, "ahl:k:")) != -1)
+ switch(cget) {
+ case 'a':
+ asmsrc = 1;
+ break;
+ case 'k':
+ kfile = optarg;
+ break;
+ case 'l':
+ limit = (float)atof(optarg);
+ break;
+ case 'h':
+ case '?':
+ default:
+ usage(exec);
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc != 2)
+ usage(exec);
+ ofile = argv[0];
+ bin = argv[1];
+
+ if (access(bin, R_OK | F_OK) == -1)
+ FATAL(exec, "%s: Impossible to locate the binary file\n",
+ exec);
+ if (access(ofile, R_OK | F_OK) == -1)
+ FATAL(exec, "%s: Impossible to locate the pmcstat file\n",
+ exec);
+ if (kfile != NULL && access(kfile, R_OK | F_OK) == -1)
+ FATAL(exec, "%s: Impossible to locate the kernel file\n",
+ exec);
+
+ bzero(tmpf, sizeof(tmpf));
+ if (mkstemp(tofl) == -1)
+ FATAL(exec, "%s: Impossible to create the tmp file\n",
+ exec);
+ if (kfile != NULL)
+ snprintf(tmpf, sizeof(tmpf), "pmcstat -k %s -R %s -m %s",
+ kfile, ofile, tofl);
+ else
+ snprintf(tmpf, sizeof(tmpf), "pmcstat -R %s -m %s", ofile,
+ tofl);
+ if (system(tmpf) != 0)
+ FATAL(exec, "%s: Impossible to create the tmp file\n",
+ exec);
+
+ gfp = fopen(tofl, "r");
+ if (gfp == NULL)
+ FATAL(exec, "%s: Impossible to open the map file\n",
+ exec);
+
+ /*
+ * Make the collection of raw entries from a pmcstat mapped file.
+ * The heuristic here wants strings in the form:
+ * "addr funcname startfaddr endfaddr".
+ */
+ while (fgets(buffer, LNBUFF, gfp) != NULL) {
+ if (isspace(buffer[0]))
+ continue;
+ if (sscanf(buffer, "%p %s %p %p\n", &ptr, fname,
+ &hstart, &hend) != 4)
+ FATAL(NULL,
+ "%s: Invalid scan of function in the map file\n",
+ exec);
+ ostart = (uintptr_t)hstart;
+ oend = (uintptr_t)hend;
+ tmppc = (uintptr_t)ptr;
+ totalsamples++;
+ obj = general_findent(tmppc);
+ if (obj != NULL) {
+ entry_acqref(obj);
+ continue;
+ }
+ obj = entry_create(fname, tmppc, ostart, oend);
+ if (obj == NULL)
+ FATAL(exec,
+ "%s: Impossible to create a new object\n", exec);
+ general_insertent(obj);
+ }
+ if (fclose(gfp) == EOF)
+ FATAL(exec, "%s: Impossible to close the filedesc\n",
+ exec);
+ if (remove(tofl) == -1)
+ FATAL(exec, "%s: Impossible to remove the tmpfile\n",
+ exec);
+
+ /*
+ * Remove the loose end objects and feed the first-level aggregation
+ * queue.
+ */
+ if (fqueue_insertgen() == -1)
+ FATAL(exec, "%s: Impossible to generate an analysis\n",
+ exec);
+ fqueue_compact(limit);
+ if (fqueue_getall(bin, tbfl, asmsrc) == -1)
+ FATAL(exec, "%s: Impossible to create the tmp file\n",
+ exec);
+
+ bfp = fopen(tbfl, "r");
+ if (bfp == NULL)
+ FATAL(exec, "%s: Impossible to open the binary file\n",
+ exec);
+
+ if (asmsrc != 0)
+ asmparse(bfp);
+ else if (cparse(bfp) == -1)
+ FATAL(NULL, "%s: Invalid format for the C file\n", exec);
+ if (fclose(bfp) == EOF)
+ FATAL(exec, "%s: Impossible to close the filedesc\n",
+ exec);
+ if (remove(tbfl) == -1)
+ FATAL(exec, "%s: Impossible to remove the tmpfile\n",
+ exec);
+ return (0);
+}
diff --git a/usr.sbin/pmccontrol/Makefile b/usr.sbin/pmccontrol/Makefile
new file mode 100644
index 0000000..9dafe1c
--- /dev/null
+++ b/usr.sbin/pmccontrol/Makefile
@@ -0,0 +1,15 @@
+#
+# $FreeBSD$
+#
+
+PROG= pmccontrol
+MAN= pmccontrol.8
+
+DPADD= ${LIBPMC}
+LDADD= -lpmc
+
+WARNS?= 6
+
+SRCS= pmccontrol.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pmccontrol/pmccontrol.8 b/usr.sbin/pmccontrol/pmccontrol.8
new file mode 100644
index 0000000..e521a1f
--- /dev/null
+++ b/usr.sbin/pmccontrol/pmccontrol.8
@@ -0,0 +1,127 @@
+.\" Copyright (c) 2003,2008 Joseph Koshy. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 Joseph Koshy ``as is'' and
+.\" any express or implied warranties, including, but not limited to, the
+.\" implied warranties of merchantability and fitness for a particular purpose
+.\" are disclaimed. in no event shall Joseph Koshy be liable
+.\" for any direct, indirect, incidental, special, exemplary, or consequential
+.\" damages (including, but not limited to, procurement of substitute goods
+.\" or services; loss of use, data, or profits; or business interruption)
+.\" however caused and on any theory of liability, whether in contract, strict
+.\" liability, or tort (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 9, 2008
+.Os
+.Dt PMCCONTROL 8
+.Sh NAME
+.Nm pmccontrol
+.Nd "control hardware performance monitoring counters"
+.Sh SYNOPSIS
+.Nm
+.Oo Fl c Ar cpu | Fl d Ar pmc | Fl e Ar pmc Oc ...
+.Nm
+.Fl l
+.Nm
+.Fl L
+.Nm
+.Fl s
+.Sh DESCRIPTION
+The
+.Nm
+utility controls the operation of the system's hardware performance
+monitoring counters.
+.Sh OPTIONS
+The
+.Nm
+utility processes options in command line order, so later options modify
+the effect of earlier ones.
+The following options are available:
+.Bl -tag -width indent
+.It Fl c Ar cpu
+Subsequent enable and disable options affect the CPU
+denoted by argument
+.Ar cpu .
+The argument
+.Ar cpu
+is a number denoting a CPU in the system, or
+.Dq Li * ,
+denoting all unhalted CPUs in the system.
+.It Fl d Ar pmc
+Disable PMC number
+.Ar pmc
+on the CPU specified by
+.Fl c ,
+preventing it from being used till subsequently re-enabled.
+The argument
+.Ar pmc
+is a number denoting a specific PMC, or
+.Dq Li *
+denoting all the PMCs on the specified CPU.
+.Pp
+Only idle PMCs may be disabled.
+.\" XXX this probably needs to be fixed.
+.It Fl e Ar pmc
+Enable PMC number
+.Ar pmc ,
+on the CPU specified by
+.Fl c ,
+allowing it to be used in the future.
+The argument
+.Ar pmc
+is a number denoting a specific PMC, or
+.Dq Li *
+denoting all the PMCs on the specified CPU.
+If PMC
+.Ar pmc
+is already enabled, this option has no effect.
+.It Fl l
+List available hardware performance counters and their current
+disposition.
+.It Fl L
+List available hardware performance counter classes and their
+supported event names.
+.It Fl s
+Print driver statistics maintained by
+.Xr hwpmc 4 .
+.El
+.Sh EXAMPLES
+To disable all PMCs on all CPUs, use the command:
+.Dl "pmccontrol -d*"
+.Pp
+To enable all PMCs on all CPUs, use:
+.Dl "pmccontrol -e*"
+.Pp
+To disable PMCs 0 and 1 on CPU 2, use:
+.Dl "pmccontrol -c2 -d0 -d1"
+.Pp
+To disable PMC 0 of CPU 0 only, and enable all other PMCS on all other
+CPUs, use:
+.Dl "pmccontrol -c* -e* -c0 -d0"
+.Sh DIAGNOSTICS
+.Ex -std
+.Sh SEE ALSO
+.Xr pmc 3 ,
+.Xr pmclog 3 ,
+.Xr hwpmc 4 ,
+.Xr pmcstat 8 ,
+.Xr sysctl 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 6.0 .
+.Sh AUTHORS
+.An Joseph Koshy Aq jkoshy@FreeBSD.org
diff --git a/usr.sbin/pmccontrol/pmccontrol.c b/usr.sbin/pmccontrol/pmccontrol.c
new file mode 100644
index 0000000..84c4f17
--- /dev/null
+++ b/usr.sbin/pmccontrol/pmccontrol.c
@@ -0,0 +1,497 @@
+/*-
+ * Copyright (c) 2003,2004 Joseph Koshy
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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/queue.h>
+#include <sys/sysctl.h>
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pmc.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+/* Compile time defaults */
+
+#define PMCC_PRINT_USAGE 0
+#define PMCC_PRINT_EVENTS 1
+#define PMCC_LIST_STATE 2
+#define PMCC_ENABLE_DISABLE 3
+#define PMCC_SHOW_STATISTICS 4
+
+#define PMCC_CPU_ALL -1
+#define PMCC_CPU_WILDCARD '*'
+
+#define PMCC_PMC_ALL -1
+#define PMCC_PMC_WILDCARD '*'
+
+#define PMCC_OP_IGNORE 0
+#define PMCC_OP_DISABLE 1
+#define PMCC_OP_ENABLE 2
+
+#define PMCC_PROGRAM_NAME "pmccontrol"
+
+STAILQ_HEAD(pmcc_op_list, pmcc_op) head = STAILQ_HEAD_INITIALIZER(head);
+
+struct pmcc_op {
+ char op_cpu;
+ char op_pmc;
+ char op_op;
+ STAILQ_ENTRY(pmcc_op) op_next;
+};
+
+/* Function Prototypes */
+#if DEBUG
+static void pmcc_init_debug(void);
+#endif
+
+static int pmcc_do_list_state(void);
+static int pmcc_do_enable_disable(struct pmcc_op_list *);
+static int pmcc_do_list_events(void);
+
+/* Globals */
+
+static char usage_message[] =
+ "Usage:\n"
+ " " PMCC_PROGRAM_NAME " -L\n"
+ " " PMCC_PROGRAM_NAME " -l\n"
+ " " PMCC_PROGRAM_NAME " -s\n"
+ " " PMCC_PROGRAM_NAME " [-e pmc | -d pmc | -c cpu] ...";
+
+#if DEBUG
+FILE *debug_stream = NULL;
+#endif
+
+#if DEBUG
+#define DEBUG_MSG(...) \
+ (void) fprintf(debug_stream, "[pmccontrol] " __VA_ARGS__);
+#else
+#define DEBUG_MSG(m) /* */
+#endif /* !DEBUG */
+
+int pmc_syscall = -1;
+
+#define PMC_CALL(cmd, params) \
+if ((error = syscall(pmc_syscall, PMC_OP_##cmd, (params))) != 0) \
+{ \
+ DEBUG_MSG("ERROR: syscall [" #cmd "]"); \
+ exit(EX_OSERR); \
+}
+
+#if DEBUG
+/* log debug messages to a separate file */
+static void
+pmcc_init_debug(void)
+{
+ char *fn;
+
+ fn = getenv("PMCCONTROL_DEBUG");
+ if (fn != NULL)
+ {
+ debug_stream = fopen(fn, "w");
+ if (debug_stream == NULL)
+ debug_stream = stderr;
+ } else
+ debug_stream = stderr;
+}
+#endif
+
+static int
+pmcc_do_enable_disable(struct pmcc_op_list *op_list)
+{
+ int c, error, i, j, ncpu, npmc, t;
+ cpumask_t haltedcpus, cpumask;
+ struct pmcc_op *np;
+ unsigned char *map;
+ unsigned char op;
+ int cpu, pmc;
+ size_t dummy;
+
+ if ((ncpu = pmc_ncpu()) < 0)
+ err(EX_OSERR, "Unable to determine the number of cpus");
+
+ /* Determine the set of active CPUs. */
+ cpumask = (1 << ncpu) - 1;
+ dummy = sizeof(int);
+ haltedcpus = (cpumask_t) 0;
+ if (ncpu > 1 && sysctlbyname("machdep.hlt_cpus", &haltedcpus,
+ &dummy, NULL, 0) < 0)
+ err(EX_OSERR, "ERROR: Cannot determine which CPUs are "
+ "halted");
+ cpumask &= ~haltedcpus;
+
+ /* Determine the maximum number of PMCs in any CPU. */
+ npmc = 0;
+ for (c = 0; c < ncpu; c++) {
+ if ((t = pmc_npmc(c)) < 0)
+ err(EX_OSERR, "Unable to determine the number of "
+ "PMCs in CPU %d", c);
+ npmc = t > npmc ? t : npmc;
+ }
+
+ if (npmc == 0)
+ errx(EX_CONFIG, "No PMCs found");
+
+ if ((map = malloc(npmc * ncpu)) == NULL)
+ err(EX_SOFTWARE, "Out of memory");
+
+ (void) memset(map, PMCC_OP_IGNORE, npmc*ncpu);
+
+ error = 0;
+ STAILQ_FOREACH(np, op_list, op_next) {
+
+ cpu = np->op_cpu;
+ pmc = np->op_pmc;
+ op = np->op_op;
+
+ if (cpu >= ncpu)
+ errx(EX_DATAERR, "CPU id too large: \"%d\"", cpu);
+
+ if (pmc >= npmc)
+ errx(EX_DATAERR, "PMC id too large: \"%d\"", pmc);
+
+#define MARKMAP(M,C,P,V) do { \
+ *((M) + (C)*npmc + (P)) = (V); \
+} while (0)
+
+#define SET_PMCS(C,P,V) do { \
+ if ((P) == PMCC_PMC_ALL) { \
+ for (j = 0; j < npmc; j++) \
+ MARKMAP(map, (C), j, (V)); \
+ } else \
+ MARKMAP(map, (C), (P), (V)); \
+} while (0)
+
+#define MAP(M,C,P) (*((M) + (C)*npmc + (P)))
+
+ if (cpu == PMCC_CPU_ALL)
+ for (i = 0; i < ncpu; i++) {
+ if ((1 << i) & cpumask)
+ SET_PMCS(i, pmc, op);
+ }
+ else
+ SET_PMCS(cpu, pmc, op);
+ }
+
+ /* Configure PMCS */
+ for (i = 0; i < ncpu; i++)
+ for (j = 0; j < npmc; j++) {
+ unsigned char b;
+
+ b = MAP(map, i, j);
+
+ error = 0;
+
+ if (b == PMCC_OP_ENABLE)
+ error = pmc_enable(i, j);
+ else if (b == PMCC_OP_DISABLE)
+ error = pmc_disable(i, j);
+
+ if (error < 0)
+ err(EX_OSERR, "%s of PMC %d on CPU %d failed",
+ b == PMCC_OP_ENABLE ? "Enable" :
+ "Disable", j, i);
+ }
+
+ return error;
+}
+
+static int
+pmcc_do_list_state(void)
+{
+ size_t dummy;
+ int c, cpu, n, npmc, ncpu;
+ unsigned int logical_cpus_mask;
+ struct pmc_info *pd;
+ struct pmc_pmcinfo *pi;
+ const struct pmc_cpuinfo *pc;
+
+ if (pmc_cpuinfo(&pc) != 0)
+ err(EX_OSERR, "Unable to determine CPU information");
+
+ dummy = sizeof(logical_cpus_mask);
+ if (sysctlbyname("machdep.logical_cpus_mask", &logical_cpus_mask,
+ &dummy, NULL, 0) < 0)
+ logical_cpus_mask = 0;
+
+ ncpu = pc->pm_ncpu;
+
+ for (c = cpu = 0; cpu < ncpu; cpu++) {
+#if defined(__i386__) || defined(__amd64__)
+ if (pc->pm_cputype == PMC_CPU_INTEL_PIV &&
+ (logical_cpus_mask & (1 << cpu)))
+ continue; /* skip P4-style 'logical' cpus */
+#endif
+ if (pmc_pmcinfo(cpu, &pi) < 0) {
+ if (errno == ENXIO)
+ continue;
+ err(EX_OSERR, "Unable to get PMC status for CPU %d",
+ cpu);
+ }
+
+ printf("#CPU %d:\n", c++);
+ npmc = pmc_npmc(cpu);
+ printf("#N NAME CLASS STATE ROW-DISP\n");
+
+ for (n = 0; n < npmc; n++) {
+ pd = &pi->pm_pmcs[n];
+
+ printf(" %-2d %-16s %-6s %-8s %-10s",
+ n,
+ pd->pm_name,
+ pmc_name_of_class(pd->pm_class),
+ pd->pm_enabled ? "ENABLED" : "DISABLED",
+ pmc_name_of_disposition(pd->pm_rowdisp));
+
+ if (pd->pm_ownerpid != -1) {
+ printf(" (pid %d)", pd->pm_ownerpid);
+ printf(" %-32s",
+ pmc_name_of_event(pd->pm_event));
+ if (PMC_IS_SAMPLING_MODE(pd->pm_mode))
+ printf(" (reload count %jd)",
+ pd->pm_reloadcount);
+ }
+ printf("\n");
+ }
+ free(pi);
+ }
+ return 0;
+}
+
+static int
+pmcc_do_list_events(void)
+{
+ enum pmc_class c;
+ unsigned int i, j, nevents;
+ const char **eventnamelist;
+ const struct pmc_cpuinfo *ci;
+
+ if (pmc_cpuinfo(&ci) != 0)
+ err(EX_OSERR, "Unable to determine CPU information");
+
+ eventnamelist = NULL;
+
+ for (i = 0; i < ci->pm_nclass; i++) {
+ c = ci->pm_classes[i].pm_class;
+
+ printf("%s\n", pmc_name_of_class(c));
+ if (pmc_event_names_of_class(c, &eventnamelist, &nevents) < 0)
+ err(EX_OSERR, "ERROR: Cannot find information for "
+ "event class \"%s\"", pmc_name_of_class(c));
+
+ for (j = 0; j < nevents; j++)
+ printf("\t%s\n", eventnamelist[j]);
+
+ free(eventnamelist);
+ }
+ return 0;
+}
+
+static int
+pmcc_show_statistics(void)
+{
+
+ struct pmc_driverstats gms;
+
+ if (pmc_get_driver_stats(&gms) < 0)
+ err(EX_OSERR, "ERROR: cannot retrieve driver statistics");
+
+ /*
+ * Print statistics.
+ */
+
+#define PRINT(N,V) (void) printf("%-40s %d\n", (N), gms.pm_##V)
+ PRINT("interrupts processed:", intr_processed);
+ PRINT("non-PMC interrupts:", intr_ignored);
+ PRINT("sampling stalls due to space shortages:", intr_bufferfull);
+ PRINT("system calls:", syscalls);
+ PRINT("system calls with errors:", syscall_errors);
+ PRINT("buffer requests:", buffer_requests);
+ PRINT("buffer requests failed:", buffer_requests_failed);
+ PRINT("sampling log sweeps:", log_sweeps);
+
+ return 0;
+}
+
+/*
+ * Main
+ */
+
+int
+main(int argc, char **argv)
+{
+ int error, command, currentcpu, option, pmc;
+ char *dummy;
+ struct pmcc_op *p;
+
+#if DEBUG
+ pmcc_init_debug();
+#endif
+
+ /* parse args */
+
+ currentcpu = PMCC_CPU_ALL;
+ command = PMCC_PRINT_USAGE;
+ error = 0;
+
+ STAILQ_INIT(&head);
+
+ while ((option = getopt(argc, argv, ":c:d:e:lLs")) != -1)
+ switch (option) {
+ case 'L':
+ if (command != PMCC_PRINT_USAGE) {
+ error = 1;
+ break;
+ }
+ command = PMCC_PRINT_EVENTS;
+ break;
+
+ case 'c':
+ if (command != PMCC_PRINT_USAGE &&
+ command != PMCC_ENABLE_DISABLE) {
+ error = 1;
+ break;
+ }
+ command = PMCC_ENABLE_DISABLE;
+
+ if (*optarg == PMCC_CPU_WILDCARD)
+ currentcpu = PMCC_CPU_ALL;
+ else {
+ currentcpu = strtoul(optarg, &dummy, 0);
+ if (*dummy != '\0' || currentcpu < 0)
+ errx(EX_DATAERR,
+ "\"%s\" is not a valid CPU id",
+ optarg);
+ }
+ break;
+
+ case 'd':
+ case 'e':
+ if (command != PMCC_PRINT_USAGE &&
+ command != PMCC_ENABLE_DISABLE) {
+ error = 1;
+ break;
+ }
+ command = PMCC_ENABLE_DISABLE;
+
+ if (*optarg == PMCC_PMC_WILDCARD)
+ pmc = PMCC_PMC_ALL;
+ else {
+ pmc = strtoul(optarg, &dummy, 0);
+ if (*dummy != '\0' || pmc < 0)
+ errx(EX_DATAERR,
+ "\"%s\" is not a valid PMC id",
+ optarg);
+ }
+
+ if ((p = malloc(sizeof(*p))) == NULL)
+ err(EX_SOFTWARE, "Out of memory");
+
+ p->op_cpu = currentcpu;
+ p->op_pmc = pmc;
+ p->op_op = option == 'd' ? PMCC_OP_DISABLE :
+ PMCC_OP_ENABLE;
+
+ STAILQ_INSERT_TAIL(&head, p, op_next);
+ break;
+
+ case 'l':
+ if (command != PMCC_PRINT_USAGE) {
+ error = 1;
+ break;
+ }
+ command = PMCC_LIST_STATE;
+ break;
+
+ case 's':
+ if (command != PMCC_PRINT_USAGE) {
+ error = 1;
+ break;
+ }
+ command = PMCC_SHOW_STATISTICS;
+ break;
+
+ case ':':
+ errx(EX_USAGE,
+ "Missing argument to option '-%c'", optopt);
+ break;
+
+ case '?':
+ warnx("Unrecognized option \"-%c\"", optopt);
+ errx(EX_USAGE, usage_message);
+ break;
+
+ default:
+ error = 1;
+ break;
+
+ }
+
+ if (command == PMCC_PRINT_USAGE)
+ (void) errx(EX_USAGE, usage_message);
+
+ if (error)
+ exit(EX_USAGE);
+
+ if (pmc_init() < 0)
+ err(EX_UNAVAILABLE,
+ "Initialization of the pmc(3) library failed");
+
+ switch (command) {
+ case PMCC_LIST_STATE:
+ error = pmcc_do_list_state();
+ break;
+ case PMCC_PRINT_EVENTS:
+ error = pmcc_do_list_events();
+ break;
+ case PMCC_SHOW_STATISTICS:
+ error = pmcc_show_statistics();
+ break;
+ case PMCC_ENABLE_DISABLE:
+ if (STAILQ_EMPTY(&head))
+ errx(EX_USAGE, "No PMCs specified to enable or disable");
+ error = pmcc_do_enable_disable(&head);
+ break;
+ default:
+ assert(0);
+
+ }
+
+ if (error != 0)
+ err(EX_OSERR, "Command failed");
+ exit(0);
+}
diff --git a/usr.sbin/pmcstat/Makefile b/usr.sbin/pmcstat/Makefile
new file mode 100644
index 0000000..6b11d8d
--- /dev/null
+++ b/usr.sbin/pmcstat/Makefile
@@ -0,0 +1,15 @@
+#
+# $FreeBSD$
+#
+
+PROG= pmcstat
+MAN= pmcstat.8
+
+DPADD= ${LIBELF} ${LIBKVM} ${LIBPMC} ${LIBM}
+LDADD= -lelf -lkvm -lpmc -lm
+
+WARNS?= 6
+
+SRCS= pmcstat.c pmcstat.h pmcstat_log.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pmcstat/pmcstat.8 b/usr.sbin/pmcstat/pmcstat.8
new file mode 100644
index 0000000..3ce35cc
--- /dev/null
+++ b/usr.sbin/pmcstat/pmcstat.8
@@ -0,0 +1,430 @@
+.\" Copyright (c) 2003-2008 Joseph Koshy
+.\" Copyright (c) 2007 The FreeBSD Foundation
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 Joseph Koshy ``as is'' and
+.\" any express or implied warranties, including, but not limited to, the
+.\" implied warranties of merchantability and fitness for a particular purpose
+.\" are disclaimed. in no event shall Joseph Koshy be liable
+.\" for any direct, indirect, incidental, special, exemplary, or consequential
+.\" damages (including, but not limited to, procurement of substitute goods
+.\" or services; loss of use, data, or profits; or business interruption)
+.\" however caused and on any theory of liability, whether in contract, strict
+.\" liability, or tort (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 19, 2008
+.Os
+.Dt PMCSTAT 8
+.Sh NAME
+.Nm pmcstat
+.Nd "performance measurement with performance monitoring hardware"
+.Sh SYNOPSIS
+.Nm
+.Op Fl C
+.Op Fl D Ar pathname
+.Op Fl E
+.Op Fl G Ar pathname
+.Op Fl M Ar mapfilename
+.Op Fl N
+.Op Fl O Ar logfilename
+.Op Fl P Ar event-spec
+.Op Fl R Ar logfilename
+.Op Fl S Ar event-spec
+.Op Fl W
+.Op Fl c Ar cpu-spec
+.Op Fl d
+.Op Fl g
+.Op Fl k Ar kerneldir
+.Op Fl m Ar pathname
+.Op Fl n Ar rate
+.Op Fl o Ar outputfile
+.Op Fl p Ar event-spec
+.Op Fl q
+.Op Fl r Ar fsroot
+.Op Fl s Ar event-spec
+.Op Fl t Ar process-spec
+.Op Fl v
+.Op Fl w Ar secs
+.Op Fl z Ar graphdepth
+.Op Ar command Op Ar args
+.Sh DESCRIPTION
+The
+.Nm
+utility measures system performance using the facilities provided by
+.Xr hwpmc 4 .
+.Pp
+The
+.Nm
+utility can measure both hardware events seen by the system as a
+whole, and those seen when a specified set of processes are executing
+on the system's CPUs.
+If a specific set of processes is being targeted (for example,
+if the
+.Fl t Ar process-spec
+option is specified, or if a command line is specified using
+.Ar command ) ,
+then measurement occurs till
+.Ar command
+exits, or till all target processes specified by the
+.Fl t Ar process-spec
+options exit, or till the
+.Nm
+utility is interrupted by the user.
+If a specific set of processes is not targeted for measurement, then
+.Nm
+will perform system-wide measurements till interrupted by the
+user.
+.Pp
+A given invocation of
+.Nm
+can mix allocations of system-mode and process-mode PMCs, of both
+counting and sampling flavors.
+The values of all counting PMCs are printed in human readable form
+at regular intervals by
+.Nm .
+The output of sampling PMCs may be configured to go to a log file for
+subsequent offline analysis, or, at the expense of greater
+overhead, may be configured to be printed in text form on the fly.
+.Pp
+Hardware events to measure are specified to
+.Nm
+using event specifier strings
+.Ar event-spec .
+The syntax of these event specifiers is machine dependent and is
+documented in
+.Xr pmc 3 .
+.Pp
+A process-mode PMC may be configured to be inheritable by the target
+process' current and future children.
+.Sh OPTIONS
+The following options are available:
+.Bl -tag -width indent
+.It Fl C
+Toggle between showing cumulative or incremental counts for
+subsequent counting mode PMCs specified on the command line.
+The default is to show incremental counts.
+.It Fl D Ar pathname
+Create files with per-program samples in the directory named
+by
+.Ar pathname .
+The default is to create these files in the current directory.
+.It Fl E
+Toggle showing per-process counts at the time a tracked process
+exits for subsequent process-mode PMCs specified on the command line.
+This option is useful for mapping the performance characteristics of a
+complex pipeline of processes when used in conjunction with the
+.Fl d
+option.
+The default is to not to enable per-process tracking.
+.It Fl G Ar pathname
+Print callchain information to file
+.Ar pathname .
+If argument
+.Ar pathname
+is a
+.Dq Li -
+this information is sent to the output file specified by the
+.Fl o
+option.
+.It Fl M Ar mapfilename
+Write the mapping between executable objects encountered in the event
+log and the abbreviated pathnames used for
+.Xr gprof 1
+profiles to file
+.Ar mapfilename .
+If this option is not specified, mapping information is not written.
+Argument
+.Ar mapfilename
+may be a
+.Dq Li -
+in which case this mapping information is sent to the output
+file configured by the
+.Fl o
+option.
+.It Fl N
+Toggle capturing callchain information for subsequent sampling PMCs.
+The default is for sampling PMCs to capture callchain information.
+.It Fl O Ar logfilename
+Send logging output to file
+.Ar logfilename .
+If
+.Ar logfilename
+is of the form
+.Ar hostname Ns : Ns Ar port ,
+where
+.Ar hostname
+does not start with a
+.Ql \&.
+or a
+.Ql / ,
+then
+.Nm
+will open a network socket to host
+.Ar hostname
+on port
+.Ar port .
+.Pp
+If the
+.Fl O
+option is not specified and one of the logging options is requested,
+then
+.Nm
+will print a textual form of the logged events to the configured
+output file.
+.It Fl P Ar event-spec
+Allocate a process mode sampling PMC measuring hardware events
+specified in
+.Ar event-spec .
+.It Fl R Ar logfilename
+Perform offline analysis using sampling data in file
+.Ar logfilename .
+.It Fl S Ar event-spec
+Allocate a system mode sampling PMC measuring hardware events
+specified in
+.Ar event-spec .
+.It Fl W
+Toggle logging the incremental counts seen by the threads of a
+tracked process each time they are scheduled on a CPU.
+This is an experimental feature intended to help analyse the
+dynamic behaviour of processes in the system.
+It may incur substantial overhead if enabled.
+The default is for this feature to be disabled.
+.It Fl c Ar cpu-spec
+Set the cpus for subsequent system mode PMCs specified on the
+command line to
+.Ar cpu-spec .
+Argument
+.Ar cpu-spec
+is a comma separated list of CPU numbers, or the literal
+.Sq *
+denoting all unhalted CPUs.
+The default is to allocate system mode PMCs on all unhalted
+CPUs.
+.It Fl d
+Toggle between process mode PMCs measuring events for the target
+process' current and future children or only measuring events for
+the target process.
+The default is to measure events for the target process alone.
+.It Fl g
+Produce profiles in a format compatible with
+.Xr gprof 1 .
+A separate profile file is generated for each executable object
+encountered.
+Profile files are placed in sub-directories named by their PMC
+event name.
+.It Fl k Ar kerneldir
+Set the pathname of the kernel directory to argument
+.Ar kerneldir .
+This directory specifies where
+.Nm
+should look for the kernel and its modules.
+The default is
+.Pa /boot/kernel .
+.It Fl m Ar pathname
+Print the sampled PCs with the name, the start and ending addresses
+of the function within they live.
+The
+.Ar pathname
+argument is mandatory and indicates where informations will be stored.
+If argument
+.Ar pathname
+is a
+.Dq Li -
+this information is sent to the output file specified by the
+.Fl o
+option.
+.It Fl n Ar rate
+Set the default sampling rate for subsequent sampling mode
+PMCs specified on the command line.
+The default is to configure PMCs to sample the CPU's instruction
+pointer every 65536 events.
+.It Fl o Ar outputfile
+Send counter readings and textual representations of logged data
+to file
+.Ar outputfile .
+The default is to send output to
+.Pa stderr
+when collecting live data and to
+.Pa stdout
+when processing a pre-existing logfile.
+.It Fl p Ar event-spec
+Allocate a process mode counting PMC measuring hardware events
+specified in
+.Ar event-spec .
+.It Fl q
+Decrease verbosity.
+.It Fl r Ar fsroot
+Set the top of the filesystem hierarchy under which executables
+are located to argument
+.Ar fsroot .
+The default is
+.Pa / .
+.It Fl s Ar event-spec
+Allocate a system mode counting PMC measuring hardware events
+specified in
+.Ar event-spec .
+.It Fl t Ar process-spec
+Attach process mode PMCs to the processes named by argument
+.Ar process-spec .
+Argument
+.Ar process-spec
+may be a non-negative integer denoting a specific process id, or a
+regular expression for selecting processes based on their command names.
+.It Fl v
+Increase verbosity.
+.It Fl w Ar secs
+Print the values of all counting mode PMCs every
+.Ar secs
+seconds.
+The argument
+.Ar secs
+may be a fractional value.
+The default interval is 5 seconds.
+.It Fl z Ar graphdepth
+When printing system-wide callgraphs, limit callgraphs to the depth
+specified by argument
+.Ar graphdepth .
+.El
+.Pp
+If
+.Ar command
+is specified, it is executed using
+.Xr execvp 3 .
+.Sh EXAMPLES
+To perform system-wide statistical sampling on an AMD Athlon CPU with
+samples taken every 32768 instruction retirals and data being sampled
+to file
+.Pa sample.stat ,
+use:
+.Dl "pmcstat -O sample.stat -n 32768 -S k7-retired-instructions"
+.Pp
+To execute
+.Nm mozilla
+and measure the number of data cache misses suffered
+by it and its children every 12 seconds on an AMD Athlon, use:
+.Dl "pmcstat -d -w 12 -p k7-dc-misses mozilla"
+.Pp
+To measure instructions retired for all processes named
+.Dq emacs
+use:
+.Dl "pmcstat -t '^emacs$' -p instructions"
+.Pp
+To measure instructions retired for processes named
+.Dq emacs
+for a period of 10 seconds use:
+.Dl "pmcstat -t '^emacs$' -p instructions sleep 10"
+.Pp
+To count instruction tlb-misses on CPUs 0 and 2 on a Intel
+Pentium Pro/Pentium III SMP system use:
+.Dl "pmcstat -c 0,2 -s p6-itlb-miss"
+.Pp
+To collect profiling information for a specific process with pid 1234
+based on instruction cache misses seen by it use:
+.Dl "pmcstat -P ic-misses -t 1234 -O /tmp/sample.out"
+.Pp
+To perform system-wide sampling on all configured processors
+based on processor instructions retired use:
+.Dl "pmcstat -S instructions -O /tmp/sample.out"
+If callgraph capture is not desired use:
+.Dl "pmcstat -N -S instructions -O /tmp/sample.out"
+.Pp
+To send the generated event log to a remote machine use:
+.Dl "pmcstat -S instructions -O remotehost:port"
+On the remote machine, the sample log can be collected using
+.Xr nc 1 :
+.Dl "nc -l remotehost port > /tmp/sample.out"
+.Pp
+To generate
+.Xr gprof 1
+compatible profiles from a sample file use:
+.Dl "pmcstat -R /tmp/sample.out -g"
+.Pp
+To print a system-wide profile with callgraphs to file
+.Pa "foo.graph"
+use:
+.Dl "pmcstat -R /tmp/sample.out -G foo.graph"
+.Sh DIAGNOSTICS
+If option
+.Fl v
+is specified,
+.Nm
+may issue the following diagnostic messages:
+.Bl -diag -width indent
+.It "#callchain/dubious-frames"
+The number of callchain records that had an
+.Dq impossible
+value for a return address.
+.It "#exec handling errors"
+The number of
+.Xr exec 2
+events in the log file that named executables that could not be
+analyzed.
+.It "#exec/elf"
+The number of
+.Xr exec 2
+events that named ELF executables.
+.It "#exec/unknown"
+The number of
+.Xr exec 2
+events that named executables with unrecognized formats.
+.It "#samples/total"
+The total number of samples in the log file.
+.It "#samples/unclaimed"
+The number of samples that could not be correlated to a known
+executable object (i.e., to an executable, shared library, the
+kernel or the runtime loader).
+.It "#samples/unknown-object"
+The number of samples that were associated with an executable
+with an unrecognized object format.
+.El
+.Pp
+.Ex -std
+.Sh COMPATIBILITY
+Due to the limitations of the
+.Pa gmon.out
+file format,
+.Xr gprof 1
+compatible profiles generated by the
+.Fl g
+option do not contain information about calls that cross executable
+boundaries.
+The generated
+.Pa gmon.out
+files are also only meaningful for native executables.
+.Sh SEE ALSO
+.Xr gprof 1 ,
+.Xr nc 1 ,
+.Xr execvp 3 ,
+.Xr pmc 3 ,
+.Xr pmclog 3 ,
+.Xr hwpmc 4 ,
+.Xr pmccontrol 8 ,
+.Xr sysctl 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 6.0 .
+It is
+.Ud
+.Sh AUTHORS
+.An Joseph Koshy Aq jkoshy@FreeBSD.org
+.Sh BUGS
+The
+.Nm
+utility cannot yet analyse
+.Xr hwpmc 4
+logs generated by non-native architectures.
diff --git a/usr.sbin/pmcstat/pmcstat.c b/usr.sbin/pmcstat/pmcstat.c
new file mode 100644
index 0000000..6496ddb
--- /dev/null
+++ b/usr.sbin/pmcstat/pmcstat.c
@@ -0,0 +1,1326 @@
+/*-
+ * Copyright (c) 2003-2008, Joseph Koshy
+ * Copyright (c) 2007 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Portions of this software were developed by A. Joseph Koshy under
+ * sponsorship from the FreeBSD Foundation and Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/event.h>
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#include <sys/ttycom.h>
+#include <sys/user.h>
+#include <sys/wait.h>
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <libgen.h>
+#include <limits.h>
+#include <math.h>
+#include <pmc.h>
+#include <pmclog.h>
+#include <regex.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "pmcstat.h"
+
+/*
+ * A given invocation of pmcstat(8) can manage multiple PMCs of both
+ * the system-wide and per-process variety. Each of these could be in
+ * 'counting mode' or in 'sampling mode'.
+ *
+ * For 'counting mode' PMCs, pmcstat(8) will periodically issue a
+ * pmc_read() at the configured time interval and print out the value
+ * of the requested PMCs.
+ *
+ * For 'sampling mode' PMCs it can log to a file for offline analysis,
+ * or can analyse sampling data "on the fly", either by converting
+ * samples to printed textual form or by creating gprof(1) compatible
+ * profiles, one per program executed. When creating gprof(1)
+ * profiles it can optionally merge entries from multiple processes
+ * for a given executable into a single profile file.
+ *
+ * pmcstat(8) can also execute a command line and attach PMCs to the
+ * resulting child process. The protocol used is as follows:
+ *
+ * - parent creates a socketpair for two way communication and
+ * fork()s.
+ * - subsequently:
+ *
+ * /Parent/ /Child/
+ *
+ * - Wait for childs token.
+ * - Sends token.
+ * - Awaits signal to start.
+ * - Attaches PMCs to the child's pid
+ * and starts them. Sets up
+ * monitoring for the child.
+ * - Signals child to start.
+ * - Recieves signal, attempts exec().
+ *
+ * After this point normal processing can happen.
+ */
+
+/* Globals */
+
+int pmcstat_interrupt = 0;
+int pmcstat_displayheight = DEFAULT_DISPLAY_HEIGHT;
+int pmcstat_sockpair[NSOCKPAIRFD];
+int pmcstat_kq;
+kvm_t *pmcstat_kvm;
+struct kinfo_proc *pmcstat_plist;
+
+void
+pmcstat_attach_pmcs(struct pmcstat_args *a)
+{
+ struct pmcstat_ev *ev;
+ struct pmcstat_target *pt;
+ int count;
+
+ /* Attach all process PMCs to target processes. */
+ count = 0;
+ STAILQ_FOREACH(ev, &a->pa_events, ev_next) {
+ if (PMC_IS_SYSTEM_MODE(ev->ev_mode))
+ continue;
+ SLIST_FOREACH(pt, &a->pa_targets, pt_next)
+ if (pmc_attach(ev->ev_pmcid, pt->pt_pid) == 0)
+ count++;
+ else if (errno != ESRCH)
+ err(EX_OSERR, "ERROR: cannot attach pmc "
+ "\"%s\" to process %d", ev->ev_name,
+ (int) pt->pt_pid);
+ }
+
+ if (count == 0)
+ errx(EX_DATAERR, "ERROR: No processes were attached to.");
+}
+
+
+void
+pmcstat_cleanup(struct pmcstat_args *a)
+{
+ struct pmcstat_ev *ev, *tmp;
+
+ /* release allocated PMCs. */
+ STAILQ_FOREACH_SAFE(ev, &a->pa_events, ev_next, tmp)
+ if (ev->ev_pmcid != PMC_ID_INVALID) {
+ if (pmc_stop(ev->ev_pmcid) < 0)
+ err(EX_OSERR, "ERROR: cannot stop pmc 0x%x "
+ "\"%s\"", ev->ev_pmcid, ev->ev_name);
+ if (pmc_release(ev->ev_pmcid) < 0)
+ err(EX_OSERR, "ERROR: cannot release pmc "
+ "0x%x \"%s\"", ev->ev_pmcid, ev->ev_name);
+ free(ev->ev_name);
+ free(ev->ev_spec);
+ STAILQ_REMOVE(&a->pa_events, ev, pmcstat_ev, ev_next);
+ free(ev);
+ }
+
+ /* de-configure the log file if present. */
+ if (a->pa_flags & (FLAG_HAS_PIPE | FLAG_HAS_OUTPUT_LOGFILE))
+ (void) pmc_configure_logfile(-1);
+
+ if (a->pa_logparser) {
+ pmclog_close(a->pa_logparser);
+ a->pa_logparser = NULL;
+ }
+
+ if (a->pa_flags & (FLAG_HAS_PIPE | FLAG_HAS_OUTPUT_LOGFILE))
+ pmcstat_shutdown_logging(a);
+}
+
+void
+pmcstat_clone_event_descriptor(struct pmcstat_args *a, struct pmcstat_ev *ev,
+ uint32_t cpumask)
+{
+ int cpu;
+ struct pmcstat_ev *ev_clone;
+
+ while ((cpu = ffs(cpumask)) > 0) {
+ cpu--;
+
+ if ((ev_clone = malloc(sizeof(*ev_clone))) == NULL)
+ errx(EX_SOFTWARE, "ERROR: Out of memory");
+ (void) memset(ev_clone, 0, sizeof(*ev_clone));
+
+ ev_clone->ev_count = ev->ev_count;
+ ev_clone->ev_cpu = cpu;
+ ev_clone->ev_cumulative = ev->ev_cumulative;
+ ev_clone->ev_flags = ev->ev_flags;
+ ev_clone->ev_mode = ev->ev_mode;
+ ev_clone->ev_name = strdup(ev->ev_name);
+ ev_clone->ev_pmcid = ev->ev_pmcid;
+ ev_clone->ev_saved = ev->ev_saved;
+ ev_clone->ev_spec = strdup(ev->ev_spec);
+
+ STAILQ_INSERT_TAIL(&a->pa_events, ev_clone, ev_next);
+
+ cpumask &= ~(1 << cpu);
+ }
+}
+
+void
+pmcstat_create_process(struct pmcstat_args *a)
+{
+ char token;
+ pid_t pid;
+ struct kevent kev;
+ struct pmcstat_target *pt;
+
+ if (socketpair(AF_UNIX, SOCK_STREAM, 0, pmcstat_sockpair) < 0)
+ err(EX_OSERR, "ERROR: cannot create socket pair");
+
+ switch (pid = fork()) {
+ case -1:
+ err(EX_OSERR, "ERROR: cannot fork");
+ /*NOTREACHED*/
+
+ case 0: /* child */
+ (void) close(pmcstat_sockpair[PARENTSOCKET]);
+
+ /* Write a token to tell our parent we've started executing. */
+ if (write(pmcstat_sockpair[CHILDSOCKET], "+", 1) != 1)
+ err(EX_OSERR, "ERROR (child): cannot write token");
+
+ /* Wait for our parent to signal us to start. */
+ if (read(pmcstat_sockpair[CHILDSOCKET], &token, 1) < 0)
+ err(EX_OSERR, "ERROR (child): cannot read token");
+ (void) close(pmcstat_sockpair[CHILDSOCKET]);
+
+ /* exec() the program requested */
+ execvp(*a->pa_argv, a->pa_argv);
+ /* and if that fails, notify the parent */
+ kill(getppid(), SIGCHLD);
+ err(EX_OSERR, "ERROR: execvp \"%s\" failed", *a->pa_argv);
+ /*NOTREACHED*/
+
+ default: /* parent */
+ (void) close(pmcstat_sockpair[CHILDSOCKET]);
+ break;
+ }
+
+ /* Ask to be notified via a kevent when the target process exits. */
+ EV_SET(&kev, pid, EVFILT_PROC, EV_ADD|EV_ONESHOT, NOTE_EXIT, 0,
+ NULL);
+ if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
+ err(EX_OSERR, "ERROR: cannot monitor child process %d", pid);
+
+ if ((pt = malloc(sizeof(*pt))) == NULL)
+ errx(EX_SOFTWARE, "ERROR: Out of memory.");
+
+ pt->pt_pid = pid;
+ SLIST_INSERT_HEAD(&a->pa_targets, pt, pt_next);
+
+ /* Wait for the child to signal that its ready to go. */
+ if (read(pmcstat_sockpair[PARENTSOCKET], &token, 1) < 0)
+ err(EX_OSERR, "ERROR (parent): cannot read token");
+
+ return;
+}
+
+void
+pmcstat_find_targets(struct pmcstat_args *a, const char *spec)
+{
+ int n, nproc, pid, rv;
+ struct pmcstat_target *pt;
+ char errbuf[_POSIX2_LINE_MAX], *end;
+ static struct kinfo_proc *kp;
+ regex_t reg;
+ regmatch_t regmatch;
+
+ /* First check if we've been given a process id. */
+ pid = strtol(spec, &end, 0);
+ if (end != spec && pid >= 0) {
+ if ((pt = malloc(sizeof(*pt))) == NULL)
+ goto outofmemory;
+ pt->pt_pid = pid;
+ SLIST_INSERT_HEAD(&a->pa_targets, pt, pt_next);
+ return;
+ }
+
+ /* Otherwise treat arg as a regular expression naming processes. */
+ if (pmcstat_kvm == NULL) {
+ if ((pmcstat_kvm = kvm_openfiles(NULL, "/dev/null", NULL, 0,
+ errbuf)) == NULL)
+ err(EX_OSERR, "ERROR: Cannot open kernel \"%s\"",
+ errbuf);
+ if ((pmcstat_plist = kvm_getprocs(pmcstat_kvm, KERN_PROC_PROC,
+ 0, &nproc)) == NULL)
+ err(EX_OSERR, "ERROR: Cannot get process list: %s",
+ kvm_geterr(pmcstat_kvm));
+ }
+
+ if ((rv = regcomp(&reg, spec, REG_EXTENDED|REG_NOSUB)) != 0) {
+ regerror(rv, &reg, errbuf, sizeof(errbuf));
+ err(EX_DATAERR, "ERROR: Failed to compile regex \"%s\": %s",
+ spec, errbuf);
+ }
+
+ for (n = 0, kp = pmcstat_plist; n < nproc; n++, kp++) {
+ if ((rv = regexec(&reg, kp->ki_comm, 1, &regmatch, 0)) == 0) {
+ if ((pt = malloc(sizeof(*pt))) == NULL)
+ goto outofmemory;
+ pt->pt_pid = kp->ki_pid;
+ SLIST_INSERT_HEAD(&a->pa_targets, pt, pt_next);
+ } else if (rv != REG_NOMATCH) {
+ regerror(rv, &reg, errbuf, sizeof(errbuf));
+ errx(EX_SOFTWARE, "ERROR: Regex evalation failed: %s",
+ errbuf);
+ }
+ }
+
+ regfree(&reg);
+
+ return;
+
+ outofmemory:
+ errx(EX_SOFTWARE, "Out of memory.");
+ /*NOTREACHED*/
+}
+
+uint32_t
+pmcstat_get_cpumask(const char *cpuspec)
+{
+ uint32_t cpumask;
+ int cpu;
+ const char *s;
+ char *end;
+
+ s = cpuspec;
+ cpumask = 0ULL;
+
+ do {
+ cpu = strtol(s, &end, 0);
+ if (cpu < 0 || end == s)
+ errx(EX_USAGE, "ERROR: Illegal CPU specification "
+ "\"%s\".", cpuspec);
+ cpumask |= (1 << cpu);
+ s = end + strspn(end, ", \t");
+ } while (*s);
+
+ return (cpumask);
+}
+
+void
+pmcstat_kill_process(struct pmcstat_args *a)
+{
+ struct pmcstat_target *pt;
+
+ assert(a->pa_flags & FLAG_HAS_COMMANDLINE);
+
+ /*
+ * If a command line was specified, it would be the very first
+ * in the list, before any other processes specified by -t.
+ */
+ pt = SLIST_FIRST(&a->pa_targets);
+ assert(pt != NULL);
+
+ if (kill(pt->pt_pid, SIGINT) != 0)
+ err(EX_OSERR, "ERROR: cannot signal child process");
+}
+
+void
+pmcstat_start_pmcs(struct pmcstat_args *a)
+{
+ struct pmcstat_ev *ev;
+
+ STAILQ_FOREACH(ev, &args.pa_events, ev_next) {
+
+ assert(ev->ev_pmcid != PMC_ID_INVALID);
+
+ if (pmc_start(ev->ev_pmcid) < 0) {
+ warn("ERROR: Cannot start pmc 0x%x \"%s\"",
+ ev->ev_pmcid, ev->ev_name);
+ pmcstat_cleanup(a);
+ exit(EX_OSERR);
+ }
+ }
+
+}
+
+void
+pmcstat_print_headers(struct pmcstat_args *a)
+{
+ struct pmcstat_ev *ev;
+ int c, w;
+
+ (void) fprintf(a->pa_printfile, PRINT_HEADER_PREFIX);
+
+ STAILQ_FOREACH(ev, &a->pa_events, ev_next) {
+ if (PMC_IS_SAMPLING_MODE(ev->ev_mode))
+ continue;
+
+ c = PMC_IS_SYSTEM_MODE(ev->ev_mode) ? 's' : 'p';
+
+ if (ev->ev_fieldskip != 0)
+ (void) fprintf(a->pa_printfile, "%*s",
+ ev->ev_fieldskip, "");
+ w = ev->ev_fieldwidth - ev->ev_fieldskip - 2;
+
+ if (c == 's')
+ (void) fprintf(a->pa_printfile, "s/%02d/%-*s ",
+ ev->ev_cpu, w-3, ev->ev_name);
+ else
+ (void) fprintf(a->pa_printfile, "p/%*s ", w,
+ ev->ev_name);
+ }
+
+ (void) fflush(a->pa_printfile);
+}
+
+void
+pmcstat_print_counters(struct pmcstat_args *a)
+{
+ int extra_width;
+ struct pmcstat_ev *ev;
+ pmc_value_t value;
+
+ extra_width = sizeof(PRINT_HEADER_PREFIX) - 1;
+
+ STAILQ_FOREACH(ev, &a->pa_events, ev_next) {
+
+ /* skip sampling mode counters */
+ if (PMC_IS_SAMPLING_MODE(ev->ev_mode))
+ continue;
+
+ if (pmc_read(ev->ev_pmcid, &value) < 0)
+ err(EX_OSERR, "ERROR: Cannot read pmc "
+ "\"%s\"", ev->ev_name);
+
+ (void) fprintf(a->pa_printfile, "%*ju ",
+ ev->ev_fieldwidth + extra_width,
+ (uintmax_t) ev->ev_cumulative ? value :
+ (value - ev->ev_saved));
+
+ if (ev->ev_cumulative == 0)
+ ev->ev_saved = value;
+ extra_width = 0;
+ }
+
+ (void) fflush(a->pa_printfile);
+}
+
+/*
+ * Print output
+ */
+
+void
+pmcstat_print_pmcs(struct pmcstat_args *a)
+{
+ static int linecount = 0;
+
+ /* check if we need to print a header line */
+ if (++linecount > pmcstat_displayheight) {
+ (void) fprintf(a->pa_printfile, "\n");
+ linecount = 1;
+ }
+ if (linecount == 1)
+ pmcstat_print_headers(a);
+ (void) fprintf(a->pa_printfile, "\n");
+
+ pmcstat_print_counters(a);
+
+ return;
+}
+
+/*
+ * Do process profiling
+ *
+ * If a pid was specified, attach each allocated PMC to the target
+ * process. Otherwise, fork a child and attach the PMCs to the child,
+ * and have the child exec() the target program.
+ */
+
+void
+pmcstat_start_process(void)
+{
+ /* Signal the child to proceed. */
+ if (write(pmcstat_sockpair[PARENTSOCKET], "!", 1) != 1)
+ err(EX_OSERR, "ERROR (parent): write of token failed");
+
+ (void) close(pmcstat_sockpair[PARENTSOCKET]);
+}
+
+void
+pmcstat_show_usage(void)
+{
+ errx(EX_USAGE,
+ "[options] [commandline]\n"
+ "\t Measure process and/or system performance using hardware\n"
+ "\t performance monitoring counters.\n"
+ "\t Options include:\n"
+ "\t -C\t\t (toggle) show cumulative counts\n"
+ "\t -D path\t create profiles in directory \"path\"\n"
+ "\t -E\t\t (toggle) show counts at process exit\n"
+ "\t -G file\t write a system-wide callgraph to \"file\"\n"
+ "\t -M file\t print executable/gmon file map to \"file\"\n"
+ "\t -N\t\t (toggle) capture callchains\n"
+ "\t -O file\t send log output to \"file\"\n"
+ "\t -P spec\t allocate a process-private sampling PMC\n"
+ "\t -R file\t read events from \"file\"\n"
+ "\t -S spec\t allocate a system-wide sampling PMC\n"
+ "\t -W\t\t (toggle) show counts per context switch\n"
+ "\t -c cpu-list\t set cpus for subsequent system-wide PMCs\n"
+ "\t -d\t\t (toggle) track descendants\n"
+ "\t -g\t\t produce gprof(1) compatible profiles\n"
+ "\t -k dir\t\t set the path to the kernel\n"
+ "\t -n rate\t set sampling rate\n"
+ "\t -o file\t send print output to \"file\"\n"
+ "\t -p spec\t allocate a process-private counting PMC\n"
+ "\t -q\t\t suppress verbosity\n"
+ "\t -r fsroot\t specify FS root directory\n"
+ "\t -s spec\t allocate a system-wide counting PMC\n"
+ "\t -t process-spec attach to running processes matching "
+ "\"process-spec\"\n"
+ "\t -v\t\t increase verbosity\n"
+ "\t -w secs\t set printing time interval\n"
+ "\t -z depth\t limit callchain display depth"
+ );
+}
+
+/*
+ * Main
+ */
+
+int
+main(int argc, char **argv)
+{
+ double interval;
+ int option, npmc, ncpu, haltedcpus;
+ int c, check_driver_stats, current_cpu, current_sampling_count;
+ int do_callchain, do_descendants, do_logproccsw, do_logprocexit;
+ int do_print;
+ size_t dummy;
+ int graphdepth;
+ int pipefd[2];
+ int use_cumulative_counts;
+ uint32_t cpumask;
+ char *end, *tmp;
+ const char *errmsg, *graphfilename;
+ enum pmcstat_state runstate;
+ struct pmc_driverstats ds_start, ds_end;
+ struct pmcstat_ev *ev;
+ struct sigaction sa;
+ struct kevent kev;
+ struct winsize ws;
+ struct stat sb;
+ char buffer[PATH_MAX];
+
+ check_driver_stats = 0;
+ current_cpu = 0;
+ current_sampling_count = DEFAULT_SAMPLE_COUNT;
+ do_callchain = 1;
+ do_descendants = 0;
+ do_logproccsw = 0;
+ do_logprocexit = 0;
+ use_cumulative_counts = 0;
+ graphfilename = "-";
+ args.pa_required = 0;
+ args.pa_flags = 0;
+ args.pa_verbosity = 1;
+ args.pa_logfd = -1;
+ args.pa_fsroot = "";
+ args.pa_kernel = strdup("/boot/kernel");
+ args.pa_samplesdir = ".";
+ args.pa_printfile = stderr;
+ args.pa_graphdepth = DEFAULT_CALLGRAPH_DEPTH;
+ args.pa_graphfile = NULL;
+ args.pa_interval = DEFAULT_WAIT_INTERVAL;
+ args.pa_mapfilename = NULL;
+ args.pa_inputpath = NULL;
+ args.pa_outputpath = NULL;
+ STAILQ_INIT(&args.pa_events);
+ SLIST_INIT(&args.pa_targets);
+ bzero(&ds_start, sizeof(ds_start));
+ bzero(&ds_end, sizeof(ds_end));
+ ev = NULL;
+
+ /*
+ * The initial CPU mask specifies all non-halted CPUS in the
+ * system.
+ */
+ dummy = sizeof(int);
+ if (sysctlbyname("hw.ncpu", &ncpu, &dummy, NULL, 0) < 0)
+ err(EX_OSERR, "ERROR: Cannot determine the number of CPUs");
+ cpumask = (1 << ncpu) - 1;
+ haltedcpus = 0;
+ if (ncpu > 1) {
+ if (sysctlbyname("machdep.hlt_cpus", &haltedcpus, &dummy,
+ NULL, 0) < 0)
+ err(EX_OSERR, "ERROR: Cannot determine which CPUs are "
+ "halted");
+ cpumask &= ~haltedcpus;
+ }
+
+ while ((option = getopt(argc, argv,
+ "CD:EG:M:NO:P:R:S:Wc:dgk:m:n:o:p:qr:s:t:vw:z:")) != -1)
+ switch (option) {
+ case 'C': /* cumulative values */
+ use_cumulative_counts = !use_cumulative_counts;
+ args.pa_required |= FLAG_HAS_COUNTING_PMCS;
+ break;
+
+ case 'c': /* CPU */
+
+ if (optarg[0] == '*' && optarg[1] == '\0')
+ cpumask = ((1 << ncpu) - 1) & ~haltedcpus;
+ else
+ cpumask = pmcstat_get_cpumask(optarg);
+
+ args.pa_required |= FLAG_HAS_SYSTEM_PMCS;
+ break;
+
+ case 'D':
+ if (stat(optarg, &sb) < 0)
+ err(EX_OSERR, "ERROR: Cannot stat \"%s\"",
+ optarg);
+ if (!S_ISDIR(sb.st_mode))
+ errx(EX_USAGE, "ERROR: \"%s\" is not a "
+ "directory.", optarg);
+ args.pa_samplesdir = optarg;
+ args.pa_flags |= FLAG_HAS_SAMPLESDIR;
+ args.pa_required |= FLAG_DO_GPROF;
+ break;
+
+ case 'd': /* toggle descendents */
+ do_descendants = !do_descendants;
+ args.pa_required |= FLAG_HAS_PROCESS_PMCS;
+ break;
+
+ case 'G': /* produce a system-wide callgraph */
+ args.pa_flags |= FLAG_DO_CALLGRAPHS;
+ graphfilename = optarg;
+ break;
+
+ case 'g': /* produce gprof compatible profiles */
+ args.pa_flags |= FLAG_DO_GPROF;
+ break;
+
+ case 'k': /* pathname to the kernel */
+ free(args.pa_kernel);
+ args.pa_kernel = strdup(optarg);
+ args.pa_required |= FLAG_DO_ANALYSIS;
+ args.pa_flags |= FLAG_HAS_KERNELPATH;
+ break;
+
+ case 'm':
+ args.pa_flags |= FLAG_WANTS_MAPPINGS;
+ graphfilename = optarg;
+ break;
+
+ case 'E': /* log process exit */
+ do_logprocexit = !do_logprocexit;
+ args.pa_required |= (FLAG_HAS_PROCESS_PMCS |
+ FLAG_HAS_COUNTING_PMCS | FLAG_HAS_OUTPUT_LOGFILE);
+ break;
+
+ case 'M': /* mapfile */
+ args.pa_mapfilename = optarg;
+ break;
+
+ case 'N':
+ do_callchain = !do_callchain;
+ args.pa_required |= FLAG_HAS_SAMPLING_PMCS;
+ break;
+
+ case 'p': /* process virtual counting PMC */
+ case 's': /* system-wide counting PMC */
+ case 'P': /* process virtual sampling PMC */
+ case 'S': /* system-wide sampling PMC */
+ if ((ev = malloc(sizeof(*ev))) == NULL)
+ errx(EX_SOFTWARE, "ERROR: Out of memory.");
+
+ switch (option) {
+ case 'p': ev->ev_mode = PMC_MODE_TC; break;
+ case 's': ev->ev_mode = PMC_MODE_SC; break;
+ case 'P': ev->ev_mode = PMC_MODE_TS; break;
+ case 'S': ev->ev_mode = PMC_MODE_SS; break;
+ }
+
+ if (option == 'P' || option == 'p') {
+ args.pa_flags |= FLAG_HAS_PROCESS_PMCS;
+ args.pa_required |= (FLAG_HAS_COMMANDLINE |
+ FLAG_HAS_TARGET);
+ }
+
+ if (option == 'P' || option == 'S') {
+ args.pa_flags |= FLAG_HAS_SAMPLING_PMCS;
+ args.pa_required |= (FLAG_HAS_PIPE |
+ FLAG_HAS_OUTPUT_LOGFILE);
+ }
+
+ if (option == 'p' || option == 's')
+ args.pa_flags |= FLAG_HAS_COUNTING_PMCS;
+
+ if (option == 's' || option == 'S')
+ args.pa_flags |= FLAG_HAS_SYSTEM_PMCS;
+
+ ev->ev_spec = strdup(optarg);
+
+ if (option == 'S' || option == 'P')
+ ev->ev_count = current_sampling_count;
+ else
+ ev->ev_count = -1;
+
+ if (option == 'S' || option == 's')
+ ev->ev_cpu = ffs(cpumask) - 1;
+ else
+ ev->ev_cpu = PMC_CPU_ANY;
+
+ ev->ev_flags = 0;
+ if (do_callchain)
+ ev->ev_flags |= PMC_F_CALLCHAIN;
+ if (do_descendants)
+ ev->ev_flags |= PMC_F_DESCENDANTS;
+ if (do_logprocexit)
+ ev->ev_flags |= PMC_F_LOG_PROCEXIT;
+ if (do_logproccsw)
+ ev->ev_flags |= PMC_F_LOG_PROCCSW;
+
+ ev->ev_cumulative = use_cumulative_counts;
+
+ ev->ev_saved = 0LL;
+ ev->ev_pmcid = PMC_ID_INVALID;
+
+ /* extract event name */
+ c = strcspn(optarg, ", \t");
+ ev->ev_name = malloc(c + 1);
+ (void) strncpy(ev->ev_name, optarg, c);
+ *(ev->ev_name + c) = '\0';
+
+ STAILQ_INSERT_TAIL(&args.pa_events, ev, ev_next);
+
+ if (option == 's' || option == 'S')
+ pmcstat_clone_event_descriptor(&args, ev,
+ cpumask & ~(1 << ev->ev_cpu));
+
+ break;
+
+ case 'n': /* sampling count */
+ current_sampling_count = strtol(optarg, &end, 0);
+ if (*end != '\0' || current_sampling_count <= 0)
+ errx(EX_USAGE,
+ "ERROR: Illegal count value \"%s\".",
+ optarg);
+ args.pa_required |= FLAG_HAS_SAMPLING_PMCS;
+ break;
+
+ case 'o': /* outputfile */
+ if (args.pa_printfile != NULL)
+ (void) fclose(args.pa_printfile);
+ if ((args.pa_printfile = fopen(optarg, "w")) == NULL)
+ errx(EX_OSERR, "ERROR: cannot open \"%s\" for "
+ "writing.", optarg);
+ args.pa_flags |= FLAG_DO_PRINT;
+ break;
+
+ case 'O': /* sampling output */
+ if (args.pa_outputpath)
+ errx(EX_USAGE, "ERROR: option -O may only be "
+ "specified once.");
+ args.pa_outputpath = optarg;
+ args.pa_flags |= FLAG_HAS_OUTPUT_LOGFILE;
+ break;
+
+ case 'q': /* quiet mode */
+ args.pa_verbosity = 0;
+ break;
+
+ case 'r': /* root FS path */
+ args.pa_fsroot = optarg;
+ break;
+
+ case 'R': /* read an existing log file */
+ if (args.pa_inputpath != NULL)
+ errx(EX_USAGE, "ERROR: option -R may only be "
+ "specified once.");
+ args.pa_inputpath = optarg;
+ if (args.pa_printfile == stderr)
+ args.pa_printfile = stdout;
+ args.pa_flags |= FLAG_READ_LOGFILE;
+ break;
+
+ case 't': /* target pid or process name */
+ pmcstat_find_targets(&args, optarg);
+
+ args.pa_flags |= FLAG_HAS_TARGET;
+ args.pa_required |= FLAG_HAS_PROCESS_PMCS;
+ break;
+
+ case 'v': /* verbose */
+ args.pa_verbosity++;
+ break;
+
+ case 'w': /* wait interval */
+ interval = strtod(optarg, &end);
+ if (*end != '\0' || interval <= 0)
+ errx(EX_USAGE, "ERROR: Illegal wait interval "
+ "value \"%s\".", optarg);
+ args.pa_flags |= FLAG_HAS_WAIT_INTERVAL;
+ args.pa_required |= FLAG_HAS_COUNTING_PMCS;
+ args.pa_interval = interval;
+ break;
+
+ case 'W': /* toggle LOG_CSW */
+ do_logproccsw = !do_logproccsw;
+ args.pa_required |= (FLAG_HAS_PROCESS_PMCS |
+ FLAG_HAS_COUNTING_PMCS | FLAG_HAS_OUTPUT_LOGFILE);
+ break;
+
+ case 'z':
+ graphdepth = strtod(optarg, &end);
+ if (*end != '\0' || graphdepth <= 0)
+ errx(EX_USAGE, "ERROR: Illegal callchain "
+ "depth \"%s\".", optarg);
+ args.pa_graphdepth = graphdepth;
+ args.pa_required |= FLAG_DO_CALLGRAPHS;
+ break;
+
+ case '?':
+ default:
+ pmcstat_show_usage();
+ break;
+
+ }
+
+ args.pa_argc = (argc -= optind);
+ args.pa_argv = (argv += optind);
+
+ args.pa_cpumask = cpumask; /* For selecting CPUs using -R. */
+
+ if (argc) /* command line present */
+ args.pa_flags |= FLAG_HAS_COMMANDLINE;
+
+ if (args.pa_flags & (FLAG_DO_GPROF | FLAG_DO_CALLGRAPHS |
+ FLAG_WANTS_MAPPINGS))
+ args.pa_flags |= FLAG_DO_ANALYSIS;
+
+ /*
+ * Check invocation syntax.
+ */
+
+ /* disallow -O and -R together */
+ if (args.pa_outputpath && args.pa_inputpath)
+ errx(EX_USAGE, "ERROR: options -O and -R are mutually "
+ "exclusive.");
+
+ /* -m option is allowed with -R only. */
+ if (args.pa_flags & FLAG_WANTS_MAPPINGS && args.pa_inputpath == NULL)
+ errx(EX_USAGE, "ERROR: option -m requires an input file");
+
+ /* -m option is not allowed combined with -g or -G. */
+ if (args.pa_flags & FLAG_WANTS_MAPPINGS &&
+ args.pa_flags & (FLAG_DO_GPROF | FLAG_DO_CALLGRAPHS))
+ errx(EX_USAGE, "ERROR: option -m and -g | -G are mutually "
+ "exclusive");
+
+ if (args.pa_flags & FLAG_READ_LOGFILE) {
+ errmsg = NULL;
+ if (args.pa_flags & FLAG_HAS_COMMANDLINE)
+ errmsg = "a command line specification";
+ else if (args.pa_flags & FLAG_HAS_TARGET)
+ errmsg = "option -t";
+ else if (!STAILQ_EMPTY(&args.pa_events))
+ errmsg = "a PMC event specification";
+ if (errmsg)
+ errx(EX_USAGE, "ERROR: option -R may not be used with "
+ "%s.", errmsg);
+ } else if (STAILQ_EMPTY(&args.pa_events))
+ /* All other uses require a PMC spec. */
+ pmcstat_show_usage();
+
+ /* check for -t pid without a process PMC spec */
+ if ((args.pa_required & FLAG_HAS_TARGET) &&
+ (args.pa_flags & FLAG_HAS_PROCESS_PMCS) == 0)
+ errx(EX_USAGE, "ERROR: option -t requires a process mode PMC "
+ "to be specified.");
+
+ /* check for process-mode options without a command or -t pid */
+ if ((args.pa_required & FLAG_HAS_PROCESS_PMCS) &&
+ (args.pa_flags & (FLAG_HAS_COMMANDLINE | FLAG_HAS_TARGET)) == 0)
+ errx(EX_USAGE, "ERROR: options -d, -E, -p, -P, and -W require "
+ "a command line or target process.");
+
+ /* check for -p | -P without a target process of some sort */
+ if ((args.pa_required & (FLAG_HAS_COMMANDLINE | FLAG_HAS_TARGET)) &&
+ (args.pa_flags & (FLAG_HAS_COMMANDLINE | FLAG_HAS_TARGET)) == 0)
+ errx(EX_USAGE, "ERROR: options -P and -p require a "
+ "target process or a command line.");
+
+ /* check for process-mode options without a process-mode PMC */
+ if ((args.pa_required & FLAG_HAS_PROCESS_PMCS) &&
+ (args.pa_flags & FLAG_HAS_PROCESS_PMCS) == 0)
+ errx(EX_USAGE, "ERROR: options -d, -E, and -W require a "
+ "process mode PMC to be specified.");
+
+ /* check for -c cpu with no system mode PMCs or logfile. */
+ if ((args.pa_required & FLAG_HAS_SYSTEM_PMCS) &&
+ (args.pa_flags & FLAG_HAS_SYSTEM_PMCS) == 0 &&
+ (args.pa_flags & FLAG_READ_LOGFILE) == 0)
+ errx(EX_USAGE, "ERROR: option -c requires at least one "
+ "system mode PMC to be specified.");
+
+ /* check for counting mode options without a counting PMC */
+ if ((args.pa_required & FLAG_HAS_COUNTING_PMCS) &&
+ (args.pa_flags & FLAG_HAS_COUNTING_PMCS) == 0)
+ errx(EX_USAGE, "ERROR: options -C, -W, -o and -w require at "
+ "least one counting mode PMC to be specified.");
+
+ /* check for sampling mode options without a sampling PMC spec */
+ if ((args.pa_required & FLAG_HAS_SAMPLING_PMCS) &&
+ (args.pa_flags & FLAG_HAS_SAMPLING_PMCS) == 0)
+ errx(EX_USAGE, "ERROR: options -N, -n and -O require at "
+ "least one sampling mode PMC to be specified.");
+
+ /* check if -g/-G are being used correctly */
+ if ((args.pa_flags & FLAG_DO_ANALYSIS) &&
+ !(args.pa_flags & (FLAG_HAS_SAMPLING_PMCS|FLAG_READ_LOGFILE)))
+ errx(EX_USAGE, "ERROR: options -g/-G require sampling PMCs "
+ "or -R to be specified.");
+
+ /* check if -O was spuriously specified */
+ if ((args.pa_flags & FLAG_HAS_OUTPUT_LOGFILE) &&
+ (args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) == 0)
+ errx(EX_USAGE,
+ "ERROR: option -O is used only with options "
+ "-E, -P, -S and -W.");
+
+ /* -k kernel path require -g/-G or -R */
+ if ((args.pa_flags & FLAG_HAS_KERNELPATH) &&
+ (args.pa_flags & FLAG_DO_ANALYSIS) == 0 &&
+ (args.pa_flags & FLAG_READ_LOGFILE) == 0)
+ errx(EX_USAGE, "ERROR: option -k is only used with -g/-R.");
+
+ /* -D only applies to gprof output mode (-g) */
+ if ((args.pa_flags & FLAG_HAS_SAMPLESDIR) &&
+ (args.pa_flags & FLAG_DO_GPROF) == 0)
+ errx(EX_USAGE, "ERROR: option -D is only used with -g.");
+
+ /* -M mapfile requires -g or -R */
+ if (args.pa_mapfilename != NULL &&
+ (args.pa_flags & FLAG_DO_GPROF) == 0 &&
+ (args.pa_flags & FLAG_READ_LOGFILE) == 0)
+ errx(EX_USAGE, "ERROR: option -M is only used with -g/-R.");
+
+ /*
+ * Disallow textual output of sampling PMCs if counting PMCs
+ * have also been asked for, mostly because the combined output
+ * is difficult to make sense of.
+ */
+ if ((args.pa_flags & FLAG_HAS_COUNTING_PMCS) &&
+ (args.pa_flags & FLAG_HAS_SAMPLING_PMCS) &&
+ ((args.pa_flags & FLAG_HAS_OUTPUT_LOGFILE) == 0))
+ errx(EX_USAGE, "ERROR: option -O is required if counting and "
+ "sampling PMCs are specified together.");
+
+ /*
+ * Check if "-k kerneldir" was specified, and if whether
+ * 'kerneldir' actually refers to a a file. If so, use
+ * `dirname path` to determine the kernel directory.
+ */
+ if (args.pa_flags & FLAG_HAS_KERNELPATH) {
+ (void) snprintf(buffer, sizeof(buffer), "%s%s", args.pa_fsroot,
+ args.pa_kernel);
+ if (stat(buffer, &sb) < 0)
+ err(EX_OSERR, "ERROR: Cannot locate kernel \"%s\"",
+ buffer);
+ if (!S_ISREG(sb.st_mode) && !S_ISDIR(sb.st_mode))
+ errx(EX_USAGE, "ERROR: \"%s\": Unsupported file type.",
+ buffer);
+ if (!S_ISDIR(sb.st_mode)) {
+ tmp = args.pa_kernel;
+ args.pa_kernel = strdup(dirname(args.pa_kernel));
+ free(tmp);
+ (void) snprintf(buffer, sizeof(buffer), "%s%s",
+ args.pa_fsroot, args.pa_kernel);
+ if (stat(buffer, &sb) < 0)
+ err(EX_OSERR, "ERROR: Cannot stat \"%s\"",
+ buffer);
+ if (!S_ISDIR(sb.st_mode))
+ errx(EX_USAGE, "ERROR: \"%s\" is not a "
+ "directory.", buffer);
+ }
+ }
+
+ /*
+ * If we have a callgraph be created, select the outputfile.
+ */
+ if (args.pa_flags & FLAG_DO_CALLGRAPHS) {
+ if (strcmp(graphfilename, "-") == 0)
+ args.pa_graphfile = args.pa_printfile;
+ else {
+ args.pa_graphfile = fopen(graphfilename, "w");
+ if (args.pa_graphfile == NULL)
+ err(EX_OSERR, "ERROR: cannot open \"%s\" "
+ "for writing", graphfilename);
+ }
+ }
+ if (args.pa_flags & FLAG_WANTS_MAPPINGS) {
+ args.pa_graphfile = fopen(graphfilename, "w");
+ if (args.pa_graphfile == NULL)
+ err(EX_OSERR, "ERROR: cannot open \"%s\" for writing",
+ graphfilename);
+ }
+
+ /* if we've been asked to process a log file, do that and exit */
+ if (args.pa_flags & FLAG_READ_LOGFILE) {
+ /*
+ * Print the log in textual form if we haven't been
+ * asked to generate profiling information.
+ */
+ if ((args.pa_flags & FLAG_DO_ANALYSIS) == 0)
+ args.pa_flags |= FLAG_DO_PRINT;
+
+ pmcstat_initialize_logging(&args);
+ args.pa_logfd = pmcstat_open_log(args.pa_inputpath,
+ PMCSTAT_OPEN_FOR_READ);
+ if ((args.pa_logparser = pmclog_open(args.pa_logfd)) == NULL)
+ err(EX_OSERR, "ERROR: Cannot create parser");
+ pmcstat_process_log(&args);
+ pmcstat_shutdown_logging(&args);
+ exit(EX_OK);
+ }
+
+ /* otherwise, we've been asked to collect data */
+ if (pmc_init() < 0)
+ err(EX_UNAVAILABLE,
+ "ERROR: Initialization of the pmc(3) library failed");
+
+ if ((npmc = pmc_npmc(0)) < 0) /* assume all CPUs are identical */
+ err(EX_OSERR, "ERROR: Cannot determine the number of PMCs "
+ "on CPU %d", 0);
+
+ /* Allocate a kqueue */
+ if ((pmcstat_kq = kqueue()) < 0)
+ err(EX_OSERR, "ERROR: Cannot allocate kqueue");
+
+ /*
+ * Configure the specified log file or setup a default log
+ * consumer via a pipe.
+ */
+ if (args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) {
+ if (args.pa_outputpath)
+ args.pa_logfd = pmcstat_open_log(args.pa_outputpath,
+ PMCSTAT_OPEN_FOR_WRITE);
+ else {
+ /*
+ * process the log on the fly by reading it in
+ * through a pipe.
+ */
+ if (pipe(pipefd) < 0)
+ err(EX_OSERR, "ERROR: pipe(2) failed");
+
+ if (fcntl(pipefd[READPIPEFD], F_SETFL, O_NONBLOCK) < 0)
+ err(EX_OSERR, "ERROR: fcntl(2) failed");
+
+ EV_SET(&kev, pipefd[READPIPEFD], EVFILT_READ, EV_ADD,
+ 0, 0, NULL);
+
+ if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
+ err(EX_OSERR, "ERROR: Cannot register kevent");
+
+ args.pa_logfd = pipefd[WRITEPIPEFD];
+
+ args.pa_flags |= (FLAG_HAS_PIPE | FLAG_DO_PRINT);
+ args.pa_logparser = pmclog_open(pipefd[READPIPEFD]);
+ }
+
+ if (pmc_configure_logfile(args.pa_logfd) < 0)
+ err(EX_OSERR, "ERROR: Cannot configure log file");
+ }
+
+ /* remember to check for driver errors if we are sampling or logging */
+ check_driver_stats = (args.pa_flags & FLAG_HAS_SAMPLING_PMCS) ||
+ (args.pa_flags & FLAG_HAS_OUTPUT_LOGFILE);
+
+ /*
+ * Allocate PMCs.
+ */
+
+ STAILQ_FOREACH(ev, &args.pa_events, ev_next) {
+ if (pmc_allocate(ev->ev_spec, ev->ev_mode,
+ ev->ev_flags, ev->ev_cpu, &ev->ev_pmcid) < 0)
+ err(EX_OSERR, "ERROR: Cannot allocate %s-mode pmc with "
+ "specification \"%s\"",
+ PMC_IS_SYSTEM_MODE(ev->ev_mode) ? "system" : "process",
+ ev->ev_spec);
+
+ if (PMC_IS_SAMPLING_MODE(ev->ev_mode) &&
+ pmc_set(ev->ev_pmcid, ev->ev_count) < 0)
+ err(EX_OSERR, "ERROR: Cannot set sampling count "
+ "for PMC \"%s\"", ev->ev_name);
+ }
+
+ /* compute printout widths */
+ STAILQ_FOREACH(ev, &args.pa_events, ev_next) {
+ int counter_width;
+ int display_width;
+ int header_width;
+
+ (void) pmc_width(ev->ev_pmcid, &counter_width);
+ header_width = strlen(ev->ev_name) + 2; /* prefix '%c/' */
+ display_width = (int) floor(counter_width / 3.32193) + 1;
+
+ if (PMC_IS_SYSTEM_MODE(ev->ev_mode))
+ header_width += 3; /* 2 digit CPU number + '/' */
+
+ if (header_width > display_width) {
+ ev->ev_fieldskip = 0;
+ ev->ev_fieldwidth = header_width;
+ } else {
+ ev->ev_fieldskip = display_width -
+ header_width;
+ ev->ev_fieldwidth = display_width;
+ }
+ }
+
+ /*
+ * If our output is being set to a terminal, register a handler
+ * for window size changes.
+ */
+
+ if (isatty(fileno(args.pa_printfile))) {
+
+ if (ioctl(fileno(args.pa_printfile), TIOCGWINSZ, &ws) < 0)
+ err(EX_OSERR, "ERROR: Cannot determine window size");
+
+ pmcstat_displayheight = ws.ws_row - 1;
+
+ EV_SET(&kev, SIGWINCH, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
+
+ if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
+ err(EX_OSERR, "ERROR: Cannot register kevent for "
+ "SIGWINCH");
+ }
+
+ EV_SET(&kev, SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
+ if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
+ err(EX_OSERR, "ERROR: Cannot register kevent for SIGINT");
+
+ EV_SET(&kev, SIGIO, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
+ if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
+ err(EX_OSERR, "ERROR: Cannot register kevent for SIGIO");
+
+ /*
+ * An exec() failure of a forked child is signalled by the
+ * child sending the parent a SIGCHLD. We don't register an
+ * actual signal handler for SIGCHLD, but instead use our
+ * kqueue to pick up the signal.
+ */
+ EV_SET(&kev, SIGCHLD, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
+ if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
+ err(EX_OSERR, "ERROR: Cannot register kevent for SIGCHLD");
+
+ /* setup a timer if we have counting mode PMCs needing to be printed */
+ if ((args.pa_flags & FLAG_HAS_COUNTING_PMCS) &&
+ (args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) == 0) {
+ EV_SET(&kev, 0, EVFILT_TIMER, EV_ADD, 0,
+ args.pa_interval * 1000, NULL);
+
+ if (kevent(pmcstat_kq, &kev, 1, NULL, 0, NULL) < 0)
+ err(EX_OSERR, "ERROR: Cannot register kevent for "
+ "timer");
+ }
+
+ /* attach PMCs to the target process, starting it if specified */
+ if (args.pa_flags & FLAG_HAS_COMMANDLINE)
+ pmcstat_create_process(&args);
+
+ if (check_driver_stats && pmc_get_driver_stats(&ds_start) < 0)
+ err(EX_OSERR, "ERROR: Cannot retrieve driver statistics");
+
+ /* Attach process pmcs to the target process. */
+ if (args.pa_flags & (FLAG_HAS_TARGET | FLAG_HAS_COMMANDLINE)) {
+ if (SLIST_EMPTY(&args.pa_targets))
+ errx(EX_DATAERR, "ERROR: No matching target "
+ "processes.");
+ if (args.pa_flags & FLAG_HAS_PROCESS_PMCS)
+ pmcstat_attach_pmcs(&args);
+
+ if (pmcstat_kvm) {
+ kvm_close(pmcstat_kvm);
+ pmcstat_kvm = NULL;
+ }
+ }
+
+ /* start the pmcs */
+ pmcstat_start_pmcs(&args);
+
+ /* start the (commandline) process if needed */
+ if (args.pa_flags & FLAG_HAS_COMMANDLINE)
+ pmcstat_start_process();
+
+ /* initialize logging if printing the configured log */
+ if ((args.pa_flags & FLAG_DO_PRINT) &&
+ (args.pa_flags & (FLAG_HAS_PIPE | FLAG_HAS_OUTPUT_LOGFILE)))
+ pmcstat_initialize_logging(&args);
+
+ /* Handle SIGINT using the kqueue loop */
+ sa.sa_handler = SIG_IGN;
+ sa.sa_flags = 0;
+ (void) sigemptyset(&sa.sa_mask);
+
+ if (sigaction(SIGINT, &sa, NULL) < 0)
+ err(EX_OSERR, "ERROR: Cannot install signal handler");
+
+ /*
+ * loop till either the target process (if any) exits, or we
+ * are killed by a SIGINT.
+ */
+ runstate = PMCSTAT_RUNNING;
+ do_print = 0;
+ do {
+ if ((c = kevent(pmcstat_kq, NULL, 0, &kev, 1, NULL)) <= 0) {
+ if (errno != EINTR)
+ err(EX_OSERR, "ERROR: kevent failed");
+ else
+ continue;
+ }
+
+ if (kev.flags & EV_ERROR)
+ errc(EX_OSERR, kev.data, "ERROR: kevent failed");
+
+ switch (kev.filter) {
+ case EVFILT_PROC: /* target has exited */
+ if (args.pa_flags & (FLAG_HAS_OUTPUT_LOGFILE |
+ FLAG_HAS_PIPE))
+ runstate = pmcstat_close_log(&args);
+ else
+ runstate = PMCSTAT_FINISHED;
+ do_print = 1;
+ break;
+
+ case EVFILT_READ: /* log file data is present */
+ runstate = pmcstat_process_log(&args);
+ break;
+
+ case EVFILT_SIGNAL:
+ if (kev.ident == SIGCHLD) {
+ /*
+ * The child process sends us a
+ * SIGCHLD if its exec() failed. We
+ * wait for it to exit and then exit
+ * ourselves.
+ */
+ (void) wait(&c);
+ runstate = PMCSTAT_FINISHED;
+ } else if (kev.ident == SIGIO) {
+ /*
+ * We get a SIGIO if a PMC loses all
+ * of its targets, or if logfile
+ * writes encounter an error.
+ */
+ if (args.pa_flags & (FLAG_HAS_OUTPUT_LOGFILE |
+ FLAG_HAS_PIPE)) {
+ runstate = pmcstat_close_log(&args);
+ if (args.pa_flags &
+ (FLAG_DO_PRINT|FLAG_DO_ANALYSIS))
+ pmcstat_process_log(&args);
+ }
+ do_print = 1; /* print PMCs at exit */
+ runstate = PMCSTAT_FINISHED;
+ } else if (kev.ident == SIGINT) {
+ /* Kill the child process if we started it */
+ if (args.pa_flags & FLAG_HAS_COMMANDLINE)
+ pmcstat_kill_process(&args);
+ /* Close the pipe to self, if present. */
+ if (args.pa_flags & FLAG_HAS_PIPE)
+ (void) close(pipefd[READPIPEFD]);
+ runstate = PMCSTAT_FINISHED;
+ } else if (kev.ident == SIGWINCH) {
+ if (ioctl(fileno(args.pa_printfile),
+ TIOCGWINSZ, &ws) < 0)
+ err(EX_OSERR, "ERROR: Cannot determine "
+ "window size");
+ pmcstat_displayheight = ws.ws_row - 1;
+ } else
+ assert(0);
+
+ break;
+
+ case EVFILT_TIMER: /* print out counting PMCs */
+ do_print = 1;
+ break;
+
+ }
+
+ if (do_print &&
+ (args.pa_required & FLAG_HAS_OUTPUT_LOGFILE) == 0) {
+ pmcstat_print_pmcs(&args);
+ if (runstate == PMCSTAT_FINISHED && /* final newline */
+ (args.pa_flags & FLAG_DO_PRINT) == 0)
+ (void) fprintf(args.pa_printfile, "\n");
+ do_print = 0;
+ }
+
+ } while (runstate != PMCSTAT_FINISHED);
+
+ /* flush any pending log entries */
+ if (args.pa_flags & (FLAG_HAS_OUTPUT_LOGFILE | FLAG_HAS_PIPE))
+ pmc_flush_logfile();
+
+ pmcstat_cleanup(&args);
+
+ free(args.pa_kernel);
+
+ /* check if the driver lost any samples or events */
+ if (check_driver_stats) {
+ if (pmc_get_driver_stats(&ds_end) < 0)
+ err(EX_OSERR, "ERROR: Cannot retrieve driver "
+ "statistics");
+ if (ds_start.pm_intr_bufferfull != ds_end.pm_intr_bufferfull &&
+ args.pa_verbosity > 0)
+ warnx("WARNING: some samples were dropped. Please "
+ "consider tuning the \"kern.hwpmc.nsamples\" "
+ "tunable.");
+ if (ds_start.pm_buffer_requests_failed !=
+ ds_end.pm_buffer_requests_failed &&
+ args.pa_verbosity > 0)
+ warnx("WARNING: some events were discarded. Please "
+ "consider tuning the \"kern.hwpmc.nbuffers\" "
+ "tunable.");
+ }
+
+ exit(EX_OK);
+}
diff --git a/usr.sbin/pmcstat/pmcstat.h b/usr.sbin/pmcstat/pmcstat.h
new file mode 100644
index 0000000..5d6f5f2
--- /dev/null
+++ b/usr.sbin/pmcstat/pmcstat.h
@@ -0,0 +1,154 @@
+/*-
+ * Copyright (c) 2005-2007, Joseph Koshy
+ * Copyright (c) 2007 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Portions of this software were developed by A. Joseph Koshy under
+ * sponsorship from the FreeBSD Foundation and Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _PMCSTAT_H_
+#define _PMCSTAT_H_
+
+#define FLAG_HAS_TARGET 0x00000001 /* process target */
+#define FLAG_HAS_WAIT_INTERVAL 0x00000002 /* -w secs */
+#define FLAG_HAS_OUTPUT_LOGFILE 0x00000004 /* -O file or pipe */
+#define FLAG_HAS_COMMANDLINE 0x00000008 /* command */
+#define FLAG_HAS_SAMPLING_PMCS 0x00000010 /* -S or -P */
+#define FLAG_HAS_COUNTING_PMCS 0x00000020 /* -s or -p */
+#define FLAG_HAS_PROCESS_PMCS 0x00000040 /* -P or -p */
+#define FLAG_HAS_SYSTEM_PMCS 0x00000080 /* -S or -s */
+#define FLAG_HAS_PIPE 0x00000100 /* implicit log */
+#define FLAG_READ_LOGFILE 0x00000200 /* -R file */
+#define FLAG_DO_GPROF 0x00000400 /* -g */
+#define FLAG_HAS_SAMPLESDIR 0x00000800 /* -D dir */
+#define FLAG_HAS_KERNELPATH 0x00001000 /* -k kernel */
+#define FLAG_DO_PRINT 0x00002000 /* -o */
+#define FLAG_DO_CALLGRAPHS 0x00004000 /* -G */
+#define FLAG_DO_ANALYSIS 0x00008000 /* -g or -G */
+#define FLAG_WANTS_MAPPINGS 0x00010000 /* -m */
+
+#define DEFAULT_SAMPLE_COUNT 65536
+#define DEFAULT_WAIT_INTERVAL 5.0
+#define DEFAULT_DISPLAY_HEIGHT 23
+#define DEFAULT_BUFFER_SIZE 4096
+#define DEFAULT_CALLGRAPH_DEPTH 4
+
+#define PRINT_HEADER_PREFIX "# "
+#define READPIPEFD 0
+#define WRITEPIPEFD 1
+#define NPIPEFD 2
+
+#define NSOCKPAIRFD 2
+#define PARENTSOCKET 0
+#define CHILDSOCKET 1
+
+#define PMCSTAT_OPEN_FOR_READ 0
+#define PMCSTAT_OPEN_FOR_WRITE 1
+#define PMCSTAT_DEFAULT_NW_HOST "localhost"
+#define PMCSTAT_DEFAULT_NW_PORT "9000"
+#define PMCSTAT_NHASH 256
+#define PMCSTAT_HASH_MASK 0xFF
+
+#define PMCSTAT_LDD_COMMAND "/usr/bin/ldd"
+
+#define PMCSTAT_PRINT_ENTRY(A,T,...) do { \
+ (void) fprintf((A)->pa_printfile, "%-9s", T); \
+ (void) fprintf((A)->pa_printfile, " " __VA_ARGS__); \
+ (void) fprintf((A)->pa_printfile, "\n"); \
+ } while (0)
+
+enum pmcstat_state {
+ PMCSTAT_FINISHED = 0,
+ PMCSTAT_EXITING = 1,
+ PMCSTAT_RUNNING = 2
+};
+
+struct pmcstat_ev {
+ STAILQ_ENTRY(pmcstat_ev) ev_next;
+ int ev_count; /* associated count if in sampling mode */
+ uint32_t ev_cpu; /* cpus for this event */
+ int ev_cumulative; /* show cumulative counts */
+ int ev_flags; /* PMC_F_* */
+ int ev_fieldskip; /* #leading spaces */
+ int ev_fieldwidth; /* print width */
+ enum pmc_mode ev_mode; /* desired mode */
+ char *ev_name; /* (derived) event name */
+ pmc_id_t ev_pmcid; /* allocated ID */
+ pmc_value_t ev_saved; /* for incremental counts */
+ char *ev_spec; /* event specification */
+};
+
+struct pmcstat_target {
+ SLIST_ENTRY(pmcstat_target) pt_next;
+ pid_t pt_pid;
+};
+
+struct pmcstat_args {
+ int pa_flags; /* argument flags */
+ int pa_required; /* required features */
+ int pa_verbosity; /* verbosity level */
+ FILE *pa_printfile; /* where to send printed output */
+ int pa_logfd; /* output log file */
+ char *pa_inputpath; /* path to input log */
+ char *pa_outputpath; /* path to output log */
+ void *pa_logparser; /* log file parser */
+ const char *pa_fsroot; /* FS root where executables reside */
+ char *pa_kernel; /* pathname of the kernel */
+ const char *pa_samplesdir; /* directory for profile files */
+ const char *pa_mapfilename;/* mapfile name */
+ FILE *pa_graphfile; /* where to send the callgraph */
+ int pa_graphdepth; /* print depth for callgraphs */
+ double pa_interval; /* printing interval in seconds */
+ uint32_t pa_cpumask; /* filter for CPUs analysed */
+ int pa_argc;
+ char **pa_argv;
+ STAILQ_HEAD(, pmcstat_ev) pa_events;
+ SLIST_HEAD(, pmcstat_target) pa_targets;
+} args;
+
+/* Function prototypes */
+void pmcstat_attach_pmcs(struct pmcstat_args *_a);
+void pmcstat_cleanup(struct pmcstat_args *_a);
+void pmcstat_clone_event_descriptor(struct pmcstat_args *_a,
+ struct pmcstat_ev *_ev, uint32_t _cpumask);
+int pmcstat_close_log(struct pmcstat_args *_a);
+void pmcstat_create_process(struct pmcstat_args *_a);
+void pmcstat_find_targets(struct pmcstat_args *_a, const char *_arg);
+void pmcstat_initialize_logging(struct pmcstat_args *_a);
+void pmcstat_kill_process(struct pmcstat_args *_a);
+int pmcstat_open_log(const char *_p, int _mode);
+void pmcstat_print_counters(struct pmcstat_args *_a);
+void pmcstat_print_headers(struct pmcstat_args *_a);
+void pmcstat_print_pmcs(struct pmcstat_args *_a);
+void pmcstat_show_usage(void);
+void pmcstat_shutdown_logging(struct pmcstat_args *_a);
+void pmcstat_start_pmcs(struct pmcstat_args *_a);
+void pmcstat_start_process(void);
+int pmcstat_process_log(struct pmcstat_args *_a);
+uint32_t pmcstat_get_cpumask(const char *_a);
+
+#endif /* _PMCSTAT_H_ */
diff --git a/usr.sbin/pmcstat/pmcstat_log.c b/usr.sbin/pmcstat/pmcstat_log.c
new file mode 100644
index 0000000..9814126
--- /dev/null
+++ b/usr.sbin/pmcstat/pmcstat_log.c
@@ -0,0 +1,2634 @@
+/*-
+ * Copyright (c) 2005-2007, Joseph Koshy
+ * Copyright (c) 2007 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * Portions of this software were developed by A. Joseph Koshy under
+ * sponsorship from the FreeBSD Foundation and Google, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Transform a hwpmc(4) log into human readable form, and into
+ * gprof(1) compatible profiles.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/endian.h>
+#include <sys/gmon.h>
+#include <sys/imgact_aout.h>
+#include <sys/imgact_elf.h>
+#include <sys/mman.h>
+#include <sys/pmc.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <netinet/in.h>
+
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <gelf.h>
+#include <libgen.h>
+#include <limits.h>
+#include <netdb.h>
+#include <pmc.h>
+#include <pmclog.h>
+#include <sysexits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pmcstat.h"
+
+#define min(A,B) ((A) < (B) ? (A) : (B))
+#define max(A,B) ((A) > (B) ? (A) : (B))
+
+#define PMCSTAT_ALLOCATE 1
+
+/*
+ * PUBLIC INTERFACES
+ *
+ * pmcstat_initialize_logging() initialize this module, called first
+ * pmcstat_shutdown_logging() orderly shutdown, called last
+ * pmcstat_open_log() open an eventlog for processing
+ * pmcstat_process_log() print/convert an event log
+ * pmcstat_close_log() finish processing an event log
+ *
+ * IMPLEMENTATION NOTES
+ *
+ * We correlate each 'callchain' or 'sample' entry seen in the event
+ * log back to an executable object in the system. Executable objects
+ * include:
+ * - program executables,
+ * - shared libraries loaded by the runtime loader,
+ * - dlopen()'ed objects loaded by the program,
+ * - the runtime loader itself,
+ * - the kernel and kernel modules.
+ *
+ * Each process that we know about is treated as a set of regions that
+ * map to executable objects. Processes are described by
+ * 'pmcstat_process' structures. Executable objects are tracked by
+ * 'pmcstat_image' structures. The kernel and kernel modules are
+ * common to all processes (they reside at the same virtual addresses
+ * for all processes). Individual processes can have their text
+ * segments and shared libraries loaded at process-specific locations.
+ *
+ * A given executable object can be in use by multiple processes
+ * (e.g., libc.so) and loaded at a different address in each.
+ * pmcstat_pcmap structures track per-image mappings.
+ *
+ * The sample log could have samples from multiple PMCs; we
+ * generate one 'gmon.out' profile per PMC.
+ *
+ * IMPLEMENTATION OF GMON OUTPUT
+ *
+ * Each executable object gets one 'gmon.out' profile, per PMC in
+ * use. Creation of 'gmon.out' profiles is done lazily. The
+ * 'gmon.out' profiles generated for a given sampling PMC are
+ * aggregates of all the samples for that particular executable
+ * object.
+ *
+ * IMPLEMENTATION OF SYSTEM-WIDE CALLGRAPH OUTPUT
+ *
+ * Each active pmcid has its own callgraph structure, described by a
+ * 'struct pmcstat_callgraph'. Given a process id and a list of pc
+ * values, we map each pc value to a tuple (image, symbol), where
+ * 'image' denotes an executable object and 'symbol' is the closest
+ * symbol that precedes the pc value. Each pc value in the list is
+ * also given a 'rank' that reflects its depth in the call stack.
+ */
+
+typedef const void *pmcstat_interned_string;
+
+/*
+ * 'pmcstat_pmcrecord' is a mapping from PMC ids to human-readable
+ * names.
+ */
+
+struct pmcstat_pmcrecord {
+ LIST_ENTRY(pmcstat_pmcrecord) pr_next;
+ pmc_id_t pr_pmcid;
+ pmcstat_interned_string pr_pmcname;
+};
+
+static LIST_HEAD(,pmcstat_pmcrecord) pmcstat_pmcs =
+ LIST_HEAD_INITIALIZER(&pmcstat_pmcs);
+
+
+/*
+ * struct pmcstat_gmonfile tracks a given 'gmon.out' file. These
+ * files are mmap()'ed in as needed.
+ */
+
+struct pmcstat_gmonfile {
+ LIST_ENTRY(pmcstat_gmonfile) pgf_next; /* list of entries */
+ int pgf_overflow; /* whether a count overflowed */
+ pmc_id_t pgf_pmcid; /* id of the associated pmc */
+ size_t pgf_nbuckets; /* #buckets in this gmon.out */
+ unsigned int pgf_nsamples; /* #samples in this gmon.out */
+ pmcstat_interned_string pgf_name; /* pathname of gmon.out file */
+ size_t pgf_ndatabytes; /* number of bytes mapped */
+ void *pgf_gmondata; /* pointer to mmap'ed data */
+ FILE *pgf_file; /* used when writing gmon arcs */
+};
+
+/*
+ * A 'pmcstat_image' structure describes an executable program on
+ * disk. 'pi_execpath' is a cookie representing the pathname of
+ * the executable. 'pi_start' and 'pi_end' are the least and greatest
+ * virtual addresses for the text segments in the executable.
+ * 'pi_gmonlist' contains a linked list of gmon.out files associated
+ * with this image.
+ */
+
+enum pmcstat_image_type {
+ PMCSTAT_IMAGE_UNKNOWN = 0, /* never looked at the image */
+ PMCSTAT_IMAGE_INDETERMINABLE, /* can't tell what the image is */
+ PMCSTAT_IMAGE_ELF32, /* ELF 32 bit object */
+ PMCSTAT_IMAGE_ELF64, /* ELF 64 bit object */
+ PMCSTAT_IMAGE_AOUT /* AOUT object */
+};
+
+struct pmcstat_image {
+ LIST_ENTRY(pmcstat_image) pi_next; /* hash link */
+ TAILQ_ENTRY(pmcstat_image) pi_lru; /* LRU list */
+ pmcstat_interned_string pi_execpath; /* cookie */
+ pmcstat_interned_string pi_samplename; /* sample path name */
+ pmcstat_interned_string pi_fullpath; /* path to FS object */
+
+ enum pmcstat_image_type pi_type; /* executable type */
+
+ /*
+ * Executables have pi_start and pi_end; these are zero
+ * for shared libraries.
+ */
+ uintfptr_t pi_start; /* start address (inclusive) */
+ uintfptr_t pi_end; /* end address (exclusive) */
+ uintfptr_t pi_entry; /* entry address */
+ uintfptr_t pi_vaddr; /* virtual address where loaded */
+ int pi_isdynamic; /* whether a dynamic object */
+ int pi_iskernelmodule;
+ pmcstat_interned_string pi_dynlinkerpath; /* path in .interp */
+
+ /* All symbols associated with this object. */
+ struct pmcstat_symbol *pi_symbols;
+ size_t pi_symcount;
+
+ /*
+ * An image can be associated with one or more gmon.out files;
+ * one per PMC.
+ */
+ LIST_HEAD(,pmcstat_gmonfile) pi_gmlist;
+};
+
+/*
+ * All image descriptors are kept in a hash table.
+ */
+static LIST_HEAD(,pmcstat_image) pmcstat_image_hash[PMCSTAT_NHASH];
+
+/*
+ * A 'pmcstat_pcmap' structure maps a virtual address range to an
+ * underlying 'pmcstat_image' descriptor.
+ */
+struct pmcstat_pcmap {
+ TAILQ_ENTRY(pmcstat_pcmap) ppm_next;
+ uintfptr_t ppm_lowpc;
+ uintfptr_t ppm_highpc;
+ struct pmcstat_image *ppm_image;
+};
+
+/*
+ * A 'pmcstat_process' structure models processes. Each process is
+ * associated with a set of pmcstat_pcmap structures that map
+ * addresses inside it to executable objects. This set is implemented
+ * as a list, kept sorted in ascending order of mapped addresses.
+ *
+ * 'pp_pid' holds the pid of the process. When a process exits, the
+ * 'pp_isactive' field is set to zero, but the process structure is
+ * not immediately reclaimed because there may still be samples in the
+ * log for this process.
+ */
+
+struct pmcstat_process {
+ LIST_ENTRY(pmcstat_process) pp_next; /* hash-next */
+ pid_t pp_pid; /* associated pid */
+ int pp_isactive; /* whether active */
+ uintfptr_t pp_entryaddr; /* entry address */
+ TAILQ_HEAD(,pmcstat_pcmap) pp_map; /* address range map */
+};
+
+/*
+ * All process descriptors are kept in a hash table.
+ */
+static LIST_HEAD(,pmcstat_process) pmcstat_process_hash[PMCSTAT_NHASH];
+
+static struct pmcstat_process *pmcstat_kernproc; /* kernel 'process' */
+
+/*
+ * Each function symbol tracked by pmcstat(8).
+ */
+
+struct pmcstat_symbol {
+ pmcstat_interned_string ps_name;
+ uint64_t ps_start;
+ uint64_t ps_end;
+};
+
+/*
+ * Each call graph node is tracked by a pmcstat_cgnode struct.
+ */
+
+struct pmcstat_cgnode {
+ struct pmcstat_image *pcg_image;
+ uintfptr_t pcg_func;
+ uint32_t pcg_count;
+ uint32_t pcg_nchildren;
+ LIST_ENTRY(pmcstat_cgnode) pcg_sibling;
+ LIST_HEAD(,pmcstat_cgnode) pcg_children;
+};
+
+struct pmcstat_cgnode_hash {
+ struct pmcstat_cgnode *pch_cgnode;
+ uint32_t pch_pmcid;
+ LIST_ENTRY(pmcstat_cgnode_hash) pch_next;
+};
+
+static int pmcstat_cgnode_hash_count;
+static pmcstat_interned_string pmcstat_previous_filename_printed;
+
+/*
+ * The toplevel CG nodes (i.e., with rank == 0) are placed in a hash table.
+ */
+
+static LIST_HEAD(,pmcstat_cgnode_hash) pmcstat_cgnode_hash[PMCSTAT_NHASH];
+
+/* Misc. statistics */
+static struct pmcstat_stats {
+ int ps_exec_aout; /* # a.out executables seen */
+ int ps_exec_elf; /* # elf executables seen */
+ int ps_exec_errors; /* # errors processing executables */
+ int ps_exec_indeterminable; /* # unknown executables seen */
+ int ps_samples_total; /* total number of samples processed */
+ int ps_samples_skipped; /* #samples filtered out for any reason */
+ int ps_samples_unknown_offset; /* #samples of rank 0 not in a map */
+ int ps_samples_indeterminable; /* #samples in indeterminable images */
+ int ps_callchain_dubious_frames;/* #dubious frame pointers seen */
+} pmcstat_stats;
+
+
+/*
+ * Prototypes
+ */
+
+static void pmcstat_gmon_create_file(struct pmcstat_gmonfile *_pgf,
+ struct pmcstat_image *_image);
+static pmcstat_interned_string pmcstat_gmon_create_name(const char *_sd,
+ struct pmcstat_image *_img, pmc_id_t _pmcid);
+static void pmcstat_gmon_map_file(struct pmcstat_gmonfile *_pgf);
+static void pmcstat_gmon_unmap_file(struct pmcstat_gmonfile *_pgf);
+
+static void pmcstat_image_determine_type(struct pmcstat_image *_image,
+ struct pmcstat_args *_a);
+static struct pmcstat_gmonfile *pmcstat_image_find_gmonfile(struct
+ pmcstat_image *_i, pmc_id_t _id);
+static struct pmcstat_image *pmcstat_image_from_path(pmcstat_interned_string
+ _path, int _iskernelmodule);
+static void pmcstat_image_get_aout_params(struct pmcstat_image *_image,
+ struct pmcstat_args *_a);
+static void pmcstat_image_get_elf_params(struct pmcstat_image *_image,
+ struct pmcstat_args *_a);
+static void pmcstat_image_increment_bucket(struct pmcstat_pcmap *_pcm,
+ uintfptr_t _pc, pmc_id_t _pmcid, struct pmcstat_args *_a);
+static void pmcstat_image_link(struct pmcstat_process *_pp,
+ struct pmcstat_image *_i, uintfptr_t _lpc);
+
+static void pmcstat_pmcid_add(pmc_id_t _pmcid,
+ pmcstat_interned_string _name, struct pmcstat_args *_a);
+static const char *pmcstat_pmcid_to_name(pmc_id_t _pmcid);
+
+static void pmcstat_process_aout_exec(struct pmcstat_process *_pp,
+ struct pmcstat_image *_image, uintfptr_t _entryaddr,
+ struct pmcstat_args *_a);
+static void pmcstat_process_elf_exec(struct pmcstat_process *_pp,
+ struct pmcstat_image *_image, uintfptr_t _entryaddr,
+ struct pmcstat_args *_a);
+static void pmcstat_process_exec(struct pmcstat_process *_pp,
+ pmcstat_interned_string _path, uintfptr_t _entryaddr,
+ struct pmcstat_args *_ao);
+static struct pmcstat_process *pmcstat_process_lookup(pid_t _pid,
+ int _allocate);
+static struct pmcstat_pcmap *pmcstat_process_find_map(
+ struct pmcstat_process *_p, uintfptr_t _pc);
+
+static int pmcstat_string_compute_hash(const char *_string);
+static void pmcstat_string_initialize(void);
+static pmcstat_interned_string pmcstat_string_intern(const char *_s);
+static pmcstat_interned_string pmcstat_string_lookup(const char *_s);
+static int pmcstat_string_lookup_hash(pmcstat_interned_string _is);
+static void pmcstat_string_shutdown(void);
+static const char *pmcstat_string_unintern(pmcstat_interned_string _is);
+
+
+/*
+ * A simple implementation of interned strings. Each interned string
+ * is assigned a unique address, so that subsequent string compares
+ * can be done by a simple pointer comparision instead of using
+ * strcmp(). This speeds up hash table lookups and saves memory if
+ * duplicate strings are the norm.
+ */
+struct pmcstat_string {
+ LIST_ENTRY(pmcstat_string) ps_next; /* hash link */
+ int ps_len;
+ int ps_hash;
+ char *ps_string;
+};
+
+static LIST_HEAD(,pmcstat_string) pmcstat_string_hash[PMCSTAT_NHASH];
+
+/*
+ * Compute a 'hash' value for a string.
+ */
+
+static int
+pmcstat_string_compute_hash(const char *s)
+{
+ int hash;
+
+ for (hash = 0; *s; s++)
+ hash ^= *s;
+
+ return (hash & PMCSTAT_HASH_MASK);
+}
+
+/*
+ * Intern a copy of string 's', and return a pointer to the
+ * interned structure.
+ */
+
+static pmcstat_interned_string
+pmcstat_string_intern(const char *s)
+{
+ struct pmcstat_string *ps;
+ const struct pmcstat_string *cps;
+ int hash, len;
+
+ if ((cps = pmcstat_string_lookup(s)) != NULL)
+ return (cps);
+
+ hash = pmcstat_string_compute_hash(s);
+ len = strlen(s);
+
+ if ((ps = malloc(sizeof(*ps))) == NULL)
+ err(EX_OSERR, "ERROR: Could not intern string");
+ ps->ps_len = len;
+ ps->ps_hash = hash;
+ ps->ps_string = strdup(s);
+ LIST_INSERT_HEAD(&pmcstat_string_hash[hash], ps, ps_next);
+ return ((pmcstat_interned_string) ps);
+}
+
+static const char *
+pmcstat_string_unintern(pmcstat_interned_string str)
+{
+ const char *s;
+
+ s = ((const struct pmcstat_string *) str)->ps_string;
+ return (s);
+}
+
+static pmcstat_interned_string
+pmcstat_string_lookup(const char *s)
+{
+ struct pmcstat_string *ps;
+ int hash, len;
+
+ hash = pmcstat_string_compute_hash(s);
+ len = strlen(s);
+
+ LIST_FOREACH(ps, &pmcstat_string_hash[hash], ps_next)
+ if (ps->ps_len == len && ps->ps_hash == hash &&
+ strcmp(ps->ps_string, s) == 0)
+ return (ps);
+ return (NULL);
+}
+
+static int
+pmcstat_string_lookup_hash(pmcstat_interned_string s)
+{
+ const struct pmcstat_string *ps;
+
+ ps = (const struct pmcstat_string *) s;
+ return (ps->ps_hash);
+}
+
+/*
+ * Initialize the string interning facility.
+ */
+
+static void
+pmcstat_string_initialize(void)
+{
+ int i;
+
+ for (i = 0; i < PMCSTAT_NHASH; i++)
+ LIST_INIT(&pmcstat_string_hash[i]);
+}
+
+/*
+ * Destroy the string table, free'ing up space.
+ */
+
+static void
+pmcstat_string_shutdown(void)
+{
+ int i;
+ struct pmcstat_string *ps, *pstmp;
+
+ for (i = 0; i < PMCSTAT_NHASH; i++)
+ LIST_FOREACH_SAFE(ps, &pmcstat_string_hash[i], ps_next,
+ pstmp) {
+ LIST_REMOVE(ps, ps_next);
+ free(ps->ps_string);
+ free(ps);
+ }
+}
+
+/*
+ * Create a gmon.out file and size it.
+ */
+
+static void
+pmcstat_gmon_create_file(struct pmcstat_gmonfile *pgf,
+ struct pmcstat_image *image)
+{
+ int fd;
+ size_t count;
+ struct gmonhdr gm;
+ const char *pathname;
+ char buffer[DEFAULT_BUFFER_SIZE];
+
+ pathname = pmcstat_string_unintern(pgf->pgf_name);
+ if ((fd = open(pathname, O_RDWR|O_NOFOLLOW|O_CREAT,
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0)
+ err(EX_OSERR, "ERROR: Cannot open \"%s\"", pathname);
+
+ gm.lpc = image->pi_start;
+ gm.hpc = image->pi_end;
+ gm.ncnt = (pgf->pgf_nbuckets * sizeof(HISTCOUNTER)) +
+ sizeof(struct gmonhdr);
+ gm.version = GMONVERSION;
+ gm.profrate = 0; /* use ticks */
+ gm.histcounter_type = 0; /* compatibility with moncontrol() */
+ gm.spare[0] = gm.spare[1] = 0;
+
+ /* Write out the gmon header */
+ if (write(fd, &gm, sizeof(gm)) < 0)
+ goto error;
+
+ /* Zero fill the samples[] array */
+ (void) memset(buffer, 0, sizeof(buffer));
+
+ count = pgf->pgf_ndatabytes - sizeof(struct gmonhdr);
+ while (count > sizeof(buffer)) {
+ if (write(fd, &buffer, sizeof(buffer)) < 0)
+ goto error;
+ count -= sizeof(buffer);
+ }
+
+ if (write(fd, &buffer, count) < 0)
+ goto error;
+
+ (void) close(fd);
+
+ return;
+
+ error:
+ err(EX_OSERR, "ERROR: Cannot write \"%s\"", pathname);
+}
+
+/*
+ * Determine the full pathname of a gmon.out file for a given
+ * (image,pmcid) combination. Return the interned string.
+ */
+
+pmcstat_interned_string
+pmcstat_gmon_create_name(const char *samplesdir, struct pmcstat_image *image,
+ pmc_id_t pmcid)
+{
+ const char *pmcname;
+ char fullpath[PATH_MAX];
+
+ pmcname = pmcstat_pmcid_to_name(pmcid);
+
+ (void) snprintf(fullpath, sizeof(fullpath),
+ "%s/%s/%s", samplesdir, pmcname,
+ pmcstat_string_unintern(image->pi_samplename));
+
+ return (pmcstat_string_intern(fullpath));
+}
+
+
+/*
+ * Mmap in a gmon.out file for processing.
+ */
+
+static void
+pmcstat_gmon_map_file(struct pmcstat_gmonfile *pgf)
+{
+ int fd;
+ const char *pathname;
+
+ pathname = pmcstat_string_unintern(pgf->pgf_name);
+
+ /* the gmon.out file must already exist */
+ if ((fd = open(pathname, O_RDWR | O_NOFOLLOW, 0)) < 0)
+ err(EX_OSERR, "ERROR: cannot open \"%s\"", pathname);
+
+ pgf->pgf_gmondata = mmap(NULL, pgf->pgf_ndatabytes,
+ PROT_READ|PROT_WRITE, MAP_NOSYNC|MAP_SHARED, fd, 0);
+
+ if (pgf->pgf_gmondata == MAP_FAILED)
+ err(EX_OSERR, "ERROR: cannot map \"%s\"", pathname);
+
+ (void) close(fd);
+}
+
+/*
+ * Unmap a gmon.out file after sync'ing its data to disk.
+ */
+
+static void
+pmcstat_gmon_unmap_file(struct pmcstat_gmonfile *pgf)
+{
+ (void) msync(pgf->pgf_gmondata, pgf->pgf_ndatabytes,
+ MS_SYNC);
+ (void) munmap(pgf->pgf_gmondata, pgf->pgf_ndatabytes);
+ pgf->pgf_gmondata = NULL;
+}
+
+static void
+pmcstat_gmon_append_arc(struct pmcstat_image *image, pmc_id_t pmcid,
+ uintptr_t rawfrom, uintptr_t rawto, uint32_t count)
+{
+ struct rawarc arc; /* from <sys/gmon.h> */
+ const char *pathname;
+ struct pmcstat_gmonfile *pgf;
+
+ if ((pgf = pmcstat_image_find_gmonfile(image, pmcid)) == NULL)
+ return;
+
+ if (pgf->pgf_file == NULL) {
+ pathname = pmcstat_string_unintern(pgf->pgf_name);
+ if ((pgf->pgf_file = fopen(pathname, "a")) == NULL)
+ return;
+ }
+
+ arc.raw_frompc = rawfrom + image->pi_vaddr;
+ arc.raw_selfpc = rawto + image->pi_vaddr;
+ arc.raw_count = count;
+
+ (void) fwrite(&arc, sizeof(arc), 1, pgf->pgf_file);
+
+}
+
+static struct pmcstat_gmonfile *
+pmcstat_image_find_gmonfile(struct pmcstat_image *image, pmc_id_t pmcid)
+{
+ struct pmcstat_gmonfile *pgf;
+ LIST_FOREACH(pgf, &image->pi_gmlist, pgf_next)
+ if (pgf->pgf_pmcid == pmcid)
+ return (pgf);
+ return (NULL);
+}
+
+
+/*
+ * Determine whether a given executable image is an A.OUT object, and
+ * if so, fill in its parameters from the text file.
+ * Sets image->pi_type.
+ */
+
+static void
+pmcstat_image_get_aout_params(struct pmcstat_image *image,
+ struct pmcstat_args *a)
+{
+ int fd;
+ ssize_t nbytes;
+ struct exec ex;
+ const char *path;
+ char buffer[PATH_MAX];
+
+ path = pmcstat_string_unintern(image->pi_execpath);
+ assert(path != NULL);
+
+ if (image->pi_iskernelmodule)
+ errx(EX_SOFTWARE, "ERROR: a.out kernel modules are "
+ "unsupported \"%s\"", path);
+
+ (void) snprintf(buffer, sizeof(buffer), "%s%s",
+ a->pa_fsroot, path);
+
+ if ((fd = open(buffer, O_RDONLY, 0)) < 0 ||
+ (nbytes = read(fd, &ex, sizeof(ex))) < 0) {
+ warn("WARNING: Cannot determine type of \"%s\"", path);
+ image->pi_type = PMCSTAT_IMAGE_INDETERMINABLE;
+ if (fd != -1)
+ (void) close(fd);
+ return;
+ }
+
+ (void) close(fd);
+
+ if ((unsigned) nbytes != sizeof(ex) ||
+ N_BADMAG(ex))
+ return;
+
+ image->pi_type = PMCSTAT_IMAGE_AOUT;
+
+ /* TODO: the rest of a.out processing */
+
+ return;
+}
+
+/*
+ * Helper function.
+ */
+
+static int
+pmcstat_symbol_compare(const void *a, const void *b)
+{
+ const struct pmcstat_symbol *sym1, *sym2;
+
+ sym1 = (const struct pmcstat_symbol *) a;
+ sym2 = (const struct pmcstat_symbol *) b;
+
+ if (sym1->ps_end <= sym2->ps_start)
+ return (-1);
+ if (sym1->ps_start >= sym2->ps_end)
+ return (1);
+ return (0);
+}
+
+/*
+ * Map an address to a symbol in an image.
+ */
+
+static struct pmcstat_symbol *
+pmcstat_symbol_search(struct pmcstat_image *image, uintfptr_t addr)
+{
+ struct pmcstat_symbol sym;
+
+ if (image->pi_symbols == NULL)
+ return (NULL);
+
+ sym.ps_name = NULL;
+ sym.ps_start = addr;
+ sym.ps_end = addr + 1;
+
+ return (bsearch((void *) &sym, image->pi_symbols,
+ image->pi_symcount, sizeof(struct pmcstat_symbol),
+ pmcstat_symbol_compare));
+}
+
+/*
+ * Add the list of symbols in the given section to the list associated
+ * with the object.
+ */
+static void
+pmcstat_image_add_symbols(struct pmcstat_image *image, Elf *e,
+ Elf_Scn *scn, GElf_Shdr *sh)
+{
+ int firsttime;
+ size_t n, newsyms, nshsyms, nfuncsyms;
+ struct pmcstat_symbol *symptr;
+ char *fnname;
+ GElf_Sym sym;
+ Elf_Data *data;
+
+ if ((data = elf_getdata(scn, NULL)) == NULL)
+ return;
+
+ /*
+ * Determine the number of functions named in this
+ * section.
+ */
+
+ nshsyms = sh->sh_size / sh->sh_entsize;
+ for (n = nfuncsyms = 0; n < nshsyms; n++) {
+ if (gelf_getsym(data, (int) n, &sym) != &sym)
+ return;
+ if (GELF_ST_TYPE(sym.st_info) == STT_FUNC)
+ nfuncsyms++;
+ }
+
+ if (nfuncsyms == 0)
+ return;
+
+ /*
+ * Allocate space for the new entries.
+ */
+ firsttime = image->pi_symbols == NULL;
+ symptr = realloc(image->pi_symbols,
+ sizeof(*symptr) * (image->pi_symcount + nfuncsyms));
+ if (symptr == image->pi_symbols) /* realloc() failed. */
+ return;
+ image->pi_symbols = symptr;
+
+ /*
+ * Append new symbols to the end of the current table.
+ */
+ symptr += image->pi_symcount;
+
+ for (n = newsyms = 0; n < nshsyms; n++) {
+ if (gelf_getsym(data, (int) n, &sym) != &sym)
+ return;
+ if (GELF_ST_TYPE(sym.st_info) != STT_FUNC)
+ continue;
+
+ if (!firsttime && pmcstat_symbol_search(image, sym.st_value))
+ continue; /* We've seen this symbol already. */
+
+ if ((fnname = elf_strptr(e, sh->sh_link, sym.st_name))
+ == NULL)
+ continue;
+
+ symptr->ps_name = pmcstat_string_intern(fnname);
+ symptr->ps_start = sym.st_value - image->pi_vaddr;
+ symptr->ps_end = symptr->ps_start + sym.st_size;
+ symptr++;
+
+ newsyms++;
+ }
+
+ image->pi_symcount += newsyms;
+
+ assert(newsyms <= nfuncsyms);
+
+ /*
+ * Return space to the system if there were duplicates.
+ */
+ if (newsyms < nfuncsyms)
+ image->pi_symbols = realloc(image->pi_symbols,
+ sizeof(*symptr) * image->pi_symcount);
+
+ /*
+ * Keep the list of symbols sorted.
+ */
+ qsort(image->pi_symbols, image->pi_symcount, sizeof(*symptr),
+ pmcstat_symbol_compare);
+
+ /*
+ * Deal with function symbols that have a size of 'zero' by
+ * making them extend to the next higher address. These
+ * symbols are usually defined in assembly code.
+ */
+ for (symptr = image->pi_symbols;
+ symptr < image->pi_symbols + (image->pi_symcount - 1);
+ symptr++)
+ if (symptr->ps_start == symptr->ps_end)
+ symptr->ps_end = (symptr+1)->ps_start;
+}
+
+/*
+ * Examine an ELF file to determine the size of its text segment.
+ * Sets image->pi_type if anything conclusive can be determined about
+ * this image.
+ */
+
+static void
+pmcstat_image_get_elf_params(struct pmcstat_image *image,
+ struct pmcstat_args *a)
+{
+ int fd;
+ size_t i, nph, nsh;
+ const char *path, *elfbase;
+ uintfptr_t minva, maxva;
+ Elf *e;
+ Elf_Scn *scn;
+ GElf_Ehdr eh;
+ GElf_Phdr ph;
+ GElf_Shdr sh;
+ enum pmcstat_image_type image_type;
+ char buffer[PATH_MAX];
+
+ assert(image->pi_type == PMCSTAT_IMAGE_UNKNOWN);
+
+ image->pi_start = minva = ~(uintfptr_t) 0;
+ image->pi_end = maxva = (uintfptr_t) 0;
+ image->pi_type = image_type = PMCSTAT_IMAGE_INDETERMINABLE;
+ image->pi_isdynamic = 0;
+ image->pi_dynlinkerpath = NULL;
+ image->pi_vaddr = 0;
+
+ path = pmcstat_string_unintern(image->pi_execpath);
+ assert(path != NULL);
+
+ /*
+ * Look for kernel modules under FSROOT/KERNELPATH/NAME,
+ * and user mode executable objects under FSROOT/PATHNAME.
+ */
+ if (image->pi_iskernelmodule)
+ (void) snprintf(buffer, sizeof(buffer), "%s%s/%s",
+ a->pa_fsroot, a->pa_kernel, path);
+ else
+ (void) snprintf(buffer, sizeof(buffer), "%s%s",
+ a->pa_fsroot, path);
+
+ e = NULL;
+ if ((fd = open(buffer, O_RDONLY, 0)) < 0 ||
+ (e = elf_begin(fd, ELF_C_READ, NULL)) == NULL ||
+ (elf_kind(e) != ELF_K_ELF)) {
+ warnx("WARNING: Cannot determine the type of \"%s\".",
+ buffer);
+ goto done;
+ }
+
+ if (gelf_getehdr(e, &eh) != &eh) {
+ warnx("WARNING: Cannot retrieve the ELF Header for "
+ "\"%s\": %s.", buffer, elf_errmsg(-1));
+ goto done;
+ }
+
+ if (eh.e_type != ET_EXEC && eh.e_type != ET_DYN &&
+ !(image->pi_iskernelmodule && eh.e_type == ET_REL)) {
+ warnx("WARNING: \"%s\" is of an unsupported ELF type.",
+ buffer);
+ goto done;
+ }
+
+ image_type = eh.e_ident[EI_CLASS] == ELFCLASS32 ?
+ PMCSTAT_IMAGE_ELF32 : PMCSTAT_IMAGE_ELF64;
+
+ /*
+ * Determine the virtual address where an executable would be
+ * loaded. Additionally, for dynamically linked executables,
+ * save the pathname to the runtime linker.
+ */
+ if (eh.e_type == ET_EXEC) {
+ if (elf_getphnum(e, &nph) == 0) {
+ warnx("WARNING: Could not determine the number of "
+ "program headers in \"%s\": %s.", buffer,
+ elf_errmsg(-1));
+ goto done;
+ }
+ for (i = 0; i < eh.e_phnum; i++) {
+ if (gelf_getphdr(e, i, &ph) != &ph) {
+ warnx("WARNING: Retrieval of PHDR entry #%ju "
+ "in \"%s\" failed: %s.", (uintmax_t) i,
+ buffer, elf_errmsg(-1));
+ goto done;
+ }
+ switch (ph.p_type) {
+ case PT_DYNAMIC:
+ image->pi_isdynamic = 1;
+ break;
+ case PT_INTERP:
+ if ((elfbase = elf_rawfile(e, NULL)) == NULL) {
+ warnx("WARNING: Cannot retrieve the "
+ "interpreter for \"%s\": %s.",
+ buffer, elf_errmsg(-1));
+ goto done;
+ }
+ image->pi_dynlinkerpath =
+ pmcstat_string_intern(elfbase +
+ ph.p_offset);
+ break;
+ case PT_LOAD:
+ if (ph.p_offset == 0)
+ image->pi_vaddr = ph.p_vaddr;
+ break;
+ }
+ }
+ }
+
+ /*
+ * Get the min and max VA associated with this ELF object.
+ */
+ if (elf_getshnum(e, &nsh) == 0) {
+ warnx("WARNING: Could not determine the number of sections "
+ "for \"%s\": %s.", buffer, elf_errmsg(-1));
+ goto done;
+ }
+
+ for (i = 0; i < nsh; i++) {
+ if ((scn = elf_getscn(e, i)) == NULL ||
+ gelf_getshdr(scn, &sh) != &sh) {
+ warnx("WARNING: Could not retrieve section header "
+ "#%ju in \"%s\": %s.", (uintmax_t) i, buffer,
+ elf_errmsg(-1));
+ goto done;
+ }
+ if (sh.sh_flags & SHF_EXECINSTR) {
+ minva = min(minva, sh.sh_addr);
+ maxva = max(maxva, sh.sh_addr + sh.sh_size);
+ }
+ if (sh.sh_type == SHT_SYMTAB || sh.sh_type == SHT_DYNSYM)
+ pmcstat_image_add_symbols(image, e, scn, &sh);
+ }
+
+ image->pi_start = minva;
+ image->pi_end = maxva;
+ image->pi_type = image_type;
+ image->pi_fullpath = pmcstat_string_intern(buffer);
+
+ done:
+ (void) elf_end(e);
+ if (fd >= 0)
+ (void) close(fd);
+ return;
+}
+
+/*
+ * Given an image descriptor, determine whether it is an ELF, or AOUT.
+ * If no handler claims the image, set its type to 'INDETERMINABLE'.
+ */
+
+static void
+pmcstat_image_determine_type(struct pmcstat_image *image,
+ struct pmcstat_args *a)
+{
+ assert(image->pi_type == PMCSTAT_IMAGE_UNKNOWN);
+
+ /* Try each kind of handler in turn */
+ if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN)
+ pmcstat_image_get_elf_params(image, a);
+ if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN)
+ pmcstat_image_get_aout_params(image, a);
+
+ /*
+ * Otherwise, remember that we tried to determine
+ * the object's type and had failed.
+ */
+ if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN)
+ image->pi_type = PMCSTAT_IMAGE_INDETERMINABLE;
+}
+
+/*
+ * Locate an image descriptor given an interned path, adding a fresh
+ * descriptor to the cache if necessary. This function also finds a
+ * suitable name for this image's sample file.
+ *
+ * We defer filling in the file format specific parts of the image
+ * structure till the time we actually see a sample that would fall
+ * into this image.
+ */
+
+static struct pmcstat_image *
+pmcstat_image_from_path(pmcstat_interned_string internedpath,
+ int iskernelmodule)
+{
+ int count, hash, nlen;
+ struct pmcstat_image *pi;
+ char *sn;
+ char name[NAME_MAX];
+
+ hash = pmcstat_string_lookup_hash(internedpath);
+
+ /* First, look for an existing entry. */
+ LIST_FOREACH(pi, &pmcstat_image_hash[hash], pi_next)
+ if (pi->pi_execpath == internedpath &&
+ pi->pi_iskernelmodule == iskernelmodule)
+ return (pi);
+
+ /*
+ * Allocate a new entry and place it at the head of the hash
+ * and LRU lists.
+ */
+ pi = malloc(sizeof(*pi));
+ if (pi == NULL)
+ return (NULL);
+
+ pi->pi_type = PMCSTAT_IMAGE_UNKNOWN;
+ pi->pi_execpath = internedpath;
+ pi->pi_start = ~0;
+ pi->pi_end = 0;
+ pi->pi_entry = 0;
+ pi->pi_vaddr = 0;
+ pi->pi_isdynamic = 0;
+ pi->pi_iskernelmodule = iskernelmodule;
+ pi->pi_dynlinkerpath = NULL;
+ pi->pi_symbols = NULL;
+ pi->pi_symcount = 0;
+
+ /*
+ * Look for a suitable name for the sample files associated
+ * with this image: if `basename(path)`+".gmon" is available,
+ * we use that, otherwise we try iterating through
+ * `basename(path)`+ "~" + NNN + ".gmon" till we get a free
+ * entry.
+ */
+ if ((sn = basename(pmcstat_string_unintern(internedpath))) == NULL)
+ err(EX_OSERR, "ERROR: Cannot process \"%s\"",
+ pmcstat_string_unintern(internedpath));
+
+ nlen = strlen(sn);
+ nlen = min(nlen, (int) (sizeof(name) - sizeof(".gmon")));
+
+ snprintf(name, sizeof(name), "%.*s.gmon", nlen, sn);
+
+ /* try use the unabridged name first */
+ if (pmcstat_string_lookup(name) == NULL)
+ pi->pi_samplename = pmcstat_string_intern(name);
+ else {
+ /*
+ * Otherwise use a prefix from the original name and
+ * upto 3 digits.
+ */
+ nlen = strlen(sn);
+ nlen = min(nlen, (int) (sizeof(name)-sizeof("~NNN.gmon")));
+ count = 0;
+ do {
+ if (++count > 999)
+ errx(EX_CANTCREAT, "ERROR: cannot create a "
+ "gmon file for \"%s\"", name);
+ snprintf(name, sizeof(name), "%.*s~%3.3d.gmon",
+ nlen, sn, count);
+ if (pmcstat_string_lookup(name) == NULL) {
+ pi->pi_samplename =
+ pmcstat_string_intern(name);
+ count = 0;
+ }
+ } while (count > 0);
+ }
+
+
+ LIST_INIT(&pi->pi_gmlist);
+
+ LIST_INSERT_HEAD(&pmcstat_image_hash[hash], pi, pi_next);
+
+ return (pi);
+}
+
+/*
+ * Increment the bucket in the gmon.out file corresponding to 'pmcid'
+ * and 'pc'.
+ */
+
+static void
+pmcstat_image_increment_bucket(struct pmcstat_pcmap *map, uintfptr_t pc,
+ pmc_id_t pmcid, struct pmcstat_args *a)
+{
+ struct pmcstat_image *image;
+ struct pmcstat_gmonfile *pgf;
+ uintfptr_t bucket;
+ HISTCOUNTER *hc;
+
+ assert(pc >= map->ppm_lowpc && pc < map->ppm_highpc);
+
+ image = map->ppm_image;
+
+ /*
+ * If this is the first time we are seeing a sample for
+ * this executable image, try determine its parameters.
+ */
+ if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN)
+ pmcstat_image_determine_type(image, a);
+
+ assert(image->pi_type != PMCSTAT_IMAGE_UNKNOWN);
+
+ /* Ignore samples in images that we know nothing about. */
+ if (image->pi_type == PMCSTAT_IMAGE_INDETERMINABLE) {
+ pmcstat_stats.ps_samples_indeterminable++;
+ return;
+ }
+
+ /*
+ * Find the gmon file corresponding to 'pmcid', creating it if
+ * needed.
+ */
+ pgf = pmcstat_image_find_gmonfile(image, pmcid);
+ if (pgf == NULL) {
+ if ((pgf = calloc(1, sizeof(*pgf))) == NULL)
+ err(EX_OSERR, "ERROR:");
+
+ pgf->pgf_gmondata = NULL; /* mark as unmapped */
+ pgf->pgf_name = pmcstat_gmon_create_name(a->pa_samplesdir,
+ image, pmcid);
+ pgf->pgf_pmcid = pmcid;
+ assert(image->pi_end > image->pi_start);
+ pgf->pgf_nbuckets = (image->pi_end - image->pi_start) /
+ FUNCTION_ALIGNMENT; /* see <machine/profile.h> */
+ pgf->pgf_ndatabytes = sizeof(struct gmonhdr) +
+ pgf->pgf_nbuckets * sizeof(HISTCOUNTER);
+ pgf->pgf_nsamples = 0;
+ pgf->pgf_file = NULL;
+
+ pmcstat_gmon_create_file(pgf, image);
+
+ LIST_INSERT_HEAD(&image->pi_gmlist, pgf, pgf_next);
+ }
+
+ /*
+ * Map the gmon file in if needed. It may have been mapped
+ * out under memory pressure.
+ */
+ if (pgf->pgf_gmondata == NULL)
+ pmcstat_gmon_map_file(pgf);
+
+ assert(pgf->pgf_gmondata != NULL);
+
+ /*
+ *
+ */
+
+ bucket = (pc - map->ppm_lowpc) / FUNCTION_ALIGNMENT;
+
+ assert(bucket < pgf->pgf_nbuckets);
+
+ hc = (HISTCOUNTER *) ((uintptr_t) pgf->pgf_gmondata +
+ sizeof(struct gmonhdr));
+
+ /* saturating add */
+ if (hc[bucket] < 0xFFFFU) /* XXX tie this to sizeof(HISTCOUNTER) */
+ hc[bucket]++;
+ else /* mark that an overflow occurred */
+ pgf->pgf_overflow = 1;
+
+ pgf->pgf_nsamples++;
+}
+
+/*
+ * Record the fact that PC values from 'start' to 'end' come from
+ * image 'image'.
+ */
+
+static void
+pmcstat_image_link(struct pmcstat_process *pp, struct pmcstat_image *image,
+ uintfptr_t start)
+{
+ struct pmcstat_pcmap *pcm, *pcmnew;
+ uintfptr_t offset;
+
+ assert(image->pi_type != PMCSTAT_IMAGE_UNKNOWN &&
+ image->pi_type != PMCSTAT_IMAGE_INDETERMINABLE);
+
+ if ((pcmnew = malloc(sizeof(*pcmnew))) == NULL)
+ err(EX_OSERR, "ERROR: Cannot create a map entry");
+
+ /*
+ * Adjust the map entry to only cover the text portion
+ * of the object.
+ */
+
+ offset = start - image->pi_vaddr;
+ pcmnew->ppm_lowpc = image->pi_start + offset;
+ pcmnew->ppm_highpc = image->pi_end + offset;
+ pcmnew->ppm_image = image;
+
+ assert(pcmnew->ppm_lowpc < pcmnew->ppm_highpc);
+
+ /* Overlapped mmap()'s are assumed to never occur. */
+ TAILQ_FOREACH(pcm, &pp->pp_map, ppm_next)
+ if (pcm->ppm_lowpc >= pcmnew->ppm_highpc)
+ break;
+
+ if (pcm == NULL)
+ TAILQ_INSERT_TAIL(&pp->pp_map, pcmnew, ppm_next);
+ else
+ TAILQ_INSERT_BEFORE(pcm, pcmnew, ppm_next);
+}
+
+/*
+ * Unmap images in the range [start..end) associated with process
+ * 'pp'.
+ */
+
+static void
+pmcstat_image_unmap(struct pmcstat_process *pp, uintfptr_t start,
+ uintfptr_t end)
+{
+ struct pmcstat_pcmap *pcm, *pcmtmp, *pcmnew;
+
+ assert(pp != NULL);
+ assert(start < end);
+
+ /*
+ * Cases:
+ * - we could have the range completely in the middle of an
+ * existing pcmap; in this case we have to split the pcmap
+ * structure into two (i.e., generate a 'hole').
+ * - we could have the range covering multiple pcmaps; these
+ * will have to be removed.
+ * - we could have either 'start' or 'end' falling in the
+ * middle of a pcmap; in this case shorten the entry.
+ */
+ TAILQ_FOREACH_SAFE(pcm, &pp->pp_map, ppm_next, pcmtmp) {
+ assert(pcm->ppm_lowpc < pcm->ppm_highpc);
+ if (pcm->ppm_highpc <= start)
+ continue;
+ if (pcm->ppm_lowpc >= end)
+ return;
+ if (pcm->ppm_lowpc >= start && pcm->ppm_highpc <= end) {
+ /*
+ * The current pcmap is completely inside the
+ * unmapped range: remove it entirely.
+ */
+ TAILQ_REMOVE(&pp->pp_map, pcm, ppm_next);
+ free(pcm);
+ } else if (pcm->ppm_lowpc < start && pcm->ppm_highpc > end) {
+ /*
+ * Split this pcmap into two; curtail the
+ * current map to end at [start-1], and start
+ * the new one at [end].
+ */
+ if ((pcmnew = malloc(sizeof(*pcmnew))) == NULL)
+ err(EX_OSERR, "ERROR: Cannot split a map "
+ "entry");
+
+ pcmnew->ppm_image = pcm->ppm_image;
+
+ pcmnew->ppm_lowpc = end;
+ pcmnew->ppm_highpc = pcm->ppm_highpc;
+
+ pcm->ppm_highpc = start;
+
+ TAILQ_INSERT_AFTER(&pp->pp_map, pcm, pcmnew, ppm_next);
+
+ return;
+ } else if (pcm->ppm_lowpc < start && pcm->ppm_highpc <= end)
+ pcm->ppm_highpc = start;
+ else if (pcm->ppm_lowpc >= start && pcm->ppm_highpc > end)
+ pcm->ppm_lowpc = end;
+ else
+ assert(0);
+ }
+}
+
+/*
+ * Add a {pmcid,name} mapping.
+ */
+
+static void
+pmcstat_pmcid_add(pmc_id_t pmcid, pmcstat_interned_string ps,
+ struct pmcstat_args *a)
+{
+ struct pmcstat_pmcrecord *pr;
+ struct stat st;
+ char fullpath[PATH_MAX];
+
+ /* Replace an existing name for the PMC. */
+ LIST_FOREACH(pr, &pmcstat_pmcs, pr_next)
+ if (pr->pr_pmcid == pmcid) {
+ pr->pr_pmcname = ps;
+ return;
+ }
+
+ /*
+ * Otherwise, allocate a new descriptor and create the
+ * appropriate directory to hold gmon.out files.
+ */
+ if ((pr = malloc(sizeof(*pr))) == NULL)
+ err(EX_OSERR, "ERROR: Cannot allocate pmc record");
+
+ pr->pr_pmcid = pmcid;
+ pr->pr_pmcname = ps;
+ LIST_INSERT_HEAD(&pmcstat_pmcs, pr, pr_next);
+
+ (void) snprintf(fullpath, sizeof(fullpath), "%s/%s", a->pa_samplesdir,
+ pmcstat_string_unintern(ps));
+
+ /* If the path name exists, it should be a directory */
+ if (stat(fullpath, &st) == 0 && S_ISDIR(st.st_mode))
+ return;
+
+ if (mkdir(fullpath, S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) < 0)
+ err(EX_OSERR, "ERROR: Cannot create directory \"%s\"",
+ fullpath);
+}
+
+/*
+ * Given a pmcid in use, find its human-readable name.
+ */
+
+static const char *
+pmcstat_pmcid_to_name(pmc_id_t pmcid)
+{
+ struct pmcstat_pmcrecord *pr;
+ char fullpath[PATH_MAX];
+
+ LIST_FOREACH(pr, &pmcstat_pmcs, pr_next)
+ if (pr->pr_pmcid == pmcid)
+ return (pmcstat_string_unintern(pr->pr_pmcname));
+
+ /* create a default name and add this entry */
+ if ((pr = malloc(sizeof(*pr))) == NULL)
+ err(EX_OSERR, "ERROR: ");
+ pr->pr_pmcid = pmcid;
+
+ (void) snprintf(fullpath, sizeof(fullpath), "%X", (unsigned int) pmcid);
+ pr->pr_pmcname = pmcstat_string_intern(fullpath);
+
+ LIST_INSERT_HEAD(&pmcstat_pmcs, pr, pr_next);
+
+ return (pmcstat_string_unintern(pr->pr_pmcname));
+}
+
+/*
+ * Associate an AOUT image with a process.
+ */
+
+static void
+pmcstat_process_aout_exec(struct pmcstat_process *pp,
+ struct pmcstat_image *image, uintfptr_t entryaddr,
+ struct pmcstat_args *a)
+{
+ (void) pp;
+ (void) image;
+ (void) entryaddr;
+ (void) a;
+ /* TODO Implement a.out handling */
+}
+
+/*
+ * Associate an ELF image with a process.
+ */
+
+static void
+pmcstat_process_elf_exec(struct pmcstat_process *pp,
+ struct pmcstat_image *image, uintfptr_t entryaddr,
+ struct pmcstat_args *a)
+{
+ uintmax_t libstart;
+ struct pmcstat_image *rtldimage;
+
+ assert(image->pi_type == PMCSTAT_IMAGE_ELF32 ||
+ image->pi_type == PMCSTAT_IMAGE_ELF64);
+
+ /* Create a map entry for the base executable. */
+ pmcstat_image_link(pp, image, image->pi_vaddr);
+
+ /*
+ * For dynamically linked executables we need to determine
+ * where the dynamic linker was mapped to for this process,
+ * Subsequent executable objects that are mapped in by the
+ * dynamic linker will be tracked by log events of type
+ * PMCLOG_TYPE_MAP_IN.
+ */
+
+ if (image->pi_isdynamic) {
+
+ /*
+ * The runtime loader gets loaded just after the maximum
+ * possible heap address. Like so:
+ *
+ * [ TEXT DATA BSS HEAP -->*RTLD SHLIBS <--STACK]
+ * ^ ^
+ * 0 VM_MAXUSER_ADDRESS
+
+ *
+ * The exact address where the loader gets mapped in
+ * will vary according to the size of the executable
+ * and the limits on the size of the process'es data
+ * segment at the time of exec(). The entry address
+ * recorded at process exec time corresponds to the
+ * 'start' address inside the dynamic linker. From
+ * this we can figure out the address where the
+ * runtime loader's file object had been mapped to.
+ */
+ rtldimage = pmcstat_image_from_path(image->pi_dynlinkerpath,
+ 0);
+ if (rtldimage == NULL) {
+ warnx("WARNING: Cannot find image for \"%s\".",
+ pmcstat_string_unintern(image->pi_dynlinkerpath));
+ pmcstat_stats.ps_exec_errors++;
+ return;
+ }
+
+ if (rtldimage->pi_type == PMCSTAT_IMAGE_UNKNOWN)
+ pmcstat_image_get_elf_params(rtldimage, a);
+
+ if (rtldimage->pi_type != PMCSTAT_IMAGE_ELF32 &&
+ rtldimage->pi_type != PMCSTAT_IMAGE_ELF64) {
+ warnx("WARNING: rtld not an ELF object \"%s\".",
+ pmcstat_string_unintern(image->pi_dynlinkerpath));
+ return;
+ }
+
+ libstart = entryaddr - rtldimage->pi_entry;
+ pmcstat_image_link(pp, rtldimage, libstart);
+ }
+}
+
+/*
+ * Find the process descriptor corresponding to a PID. If 'allocate'
+ * is zero, we return a NULL if a pid descriptor could not be found or
+ * a process descriptor process. If 'allocate' is non-zero, then we
+ * will attempt to allocate a fresh process descriptor. Zombie
+ * process descriptors are only removed if a fresh allocation for the
+ * same PID is requested.
+ */
+
+static struct pmcstat_process *
+pmcstat_process_lookup(pid_t pid, int allocate)
+{
+ uint32_t hash;
+ struct pmcstat_pcmap *ppm, *ppmtmp;
+ struct pmcstat_process *pp, *pptmp;
+
+ hash = (uint32_t) pid & PMCSTAT_HASH_MASK; /* simplicity wins */
+
+ LIST_FOREACH_SAFE(pp, &pmcstat_process_hash[hash], pp_next, pptmp)
+ if (pp->pp_pid == pid) {
+ /* Found a descriptor, check and process zombies */
+ if (allocate && pp->pp_isactive == 0) {
+ /* remove maps */
+ TAILQ_FOREACH_SAFE(ppm, &pp->pp_map, ppm_next,
+ ppmtmp) {
+ TAILQ_REMOVE(&pp->pp_map, ppm, ppm_next);
+ free(ppm);
+ }
+ /* remove process entry */
+ LIST_REMOVE(pp, pp_next);
+ free(pp);
+ break;
+ }
+ return (pp);
+ }
+
+ if (!allocate)
+ return (NULL);
+
+ if ((pp = malloc(sizeof(*pp))) == NULL)
+ err(EX_OSERR, "ERROR: Cannot allocate pid descriptor");
+
+ pp->pp_pid = pid;
+ pp->pp_isactive = 1;
+
+ TAILQ_INIT(&pp->pp_map);
+
+ LIST_INSERT_HEAD(&pmcstat_process_hash[hash], pp, pp_next);
+ return (pp);
+}
+
+/*
+ * Associate an image and a process.
+ */
+
+static void
+pmcstat_process_exec(struct pmcstat_process *pp,
+ pmcstat_interned_string path, uintfptr_t entryaddr,
+ struct pmcstat_args *a)
+{
+ struct pmcstat_image *image;
+
+ if ((image = pmcstat_image_from_path(path, 0)) == NULL) {
+ pmcstat_stats.ps_exec_errors++;
+ return;
+ }
+
+ if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN)
+ pmcstat_image_determine_type(image, a);
+
+ assert(image->pi_type != PMCSTAT_IMAGE_UNKNOWN);
+
+ switch (image->pi_type) {
+ case PMCSTAT_IMAGE_ELF32:
+ case PMCSTAT_IMAGE_ELF64:
+ pmcstat_stats.ps_exec_elf++;
+ pmcstat_process_elf_exec(pp, image, entryaddr, a);
+ break;
+
+ case PMCSTAT_IMAGE_AOUT:
+ pmcstat_stats.ps_exec_aout++;
+ pmcstat_process_aout_exec(pp, image, entryaddr, a);
+ break;
+
+ case PMCSTAT_IMAGE_INDETERMINABLE:
+ pmcstat_stats.ps_exec_indeterminable++;
+ break;
+
+ default:
+ err(EX_SOFTWARE, "ERROR: Unsupported executable type for "
+ "\"%s\"", pmcstat_string_unintern(path));
+ }
+}
+
+
+/*
+ * Find the map entry associated with process 'p' at PC value 'pc'.
+ */
+
+static struct pmcstat_pcmap *
+pmcstat_process_find_map(struct pmcstat_process *p, uintfptr_t pc)
+{
+ struct pmcstat_pcmap *ppm;
+
+ TAILQ_FOREACH(ppm, &p->pp_map, ppm_next) {
+ if (pc >= ppm->ppm_lowpc && pc < ppm->ppm_highpc)
+ return (ppm);
+ if (pc < ppm->ppm_lowpc)
+ return (NULL);
+ }
+
+ return (NULL);
+}
+
+static struct pmcstat_cgnode *
+pmcstat_cgnode_allocate(struct pmcstat_image *image, uintfptr_t pc)
+{
+ struct pmcstat_cgnode *cg;
+
+ if ((cg = malloc(sizeof(*cg))) == NULL)
+ err(EX_OSERR, "ERROR: Cannot allocate callgraph node");
+
+ cg->pcg_image = image;
+ cg->pcg_func = pc;
+
+ cg->pcg_count = 0;
+ cg->pcg_nchildren = 0;
+ LIST_INIT(&cg->pcg_children);
+
+ return (cg);
+}
+
+/*
+ * Free a node and its children.
+ */
+static void
+pmcstat_cgnode_free(struct pmcstat_cgnode *cg)
+{
+ struct pmcstat_cgnode *cgc, *cgtmp;
+
+ LIST_FOREACH_SAFE(cgc, &cg->pcg_children, pcg_sibling, cgtmp)
+ pmcstat_cgnode_free(cgc);
+ free(cg);
+}
+
+/*
+ * Look for a callgraph node associated with pmc `pmcid' in the global
+ * hash table that corresponds to the given `pc' value in the process
+ * `pp'.
+ */
+static struct pmcstat_cgnode *
+pmcstat_cgnode_hash_lookup_pc(struct pmcstat_process *pp, uint32_t pmcid,
+ uintfptr_t pc, int usermode)
+{
+ struct pmcstat_pcmap *ppm;
+ struct pmcstat_symbol *sym;
+ struct pmcstat_image *image;
+ struct pmcstat_cgnode *cg;
+ struct pmcstat_cgnode_hash *h;
+ uintfptr_t loadaddress;
+ unsigned int i, hash;
+
+ ppm = pmcstat_process_find_map(usermode ? pp : pmcstat_kernproc, pc);
+ if (ppm == NULL)
+ return (NULL);
+
+ image = ppm->ppm_image;
+
+ loadaddress = ppm->ppm_lowpc + image->pi_vaddr - image->pi_start;
+ pc -= loadaddress; /* Convert to an offset in the image. */
+
+ /*
+ * Try determine the function at this offset. If we can't
+ * find a function round leave the `pc' value alone.
+ */
+ if ((sym = pmcstat_symbol_search(image, pc)) != NULL)
+ pc = sym->ps_start;
+
+ for (hash = i = 0; i < sizeof(uintfptr_t); i++)
+ hash += (pc >> i) & 0xFF;
+
+ hash &= PMCSTAT_HASH_MASK;
+
+ cg = NULL;
+ LIST_FOREACH(h, &pmcstat_cgnode_hash[hash], pch_next)
+ {
+ if (h->pch_pmcid != pmcid)
+ continue;
+
+ cg = h->pch_cgnode;
+
+ assert(cg != NULL);
+
+ if (cg->pcg_image == image && cg->pcg_func == pc)
+ return (cg);
+ }
+
+ /*
+ * We haven't seen this (pmcid, pc) tuple yet, so allocate a
+ * new callgraph node and a new hash table entry for it.
+ */
+ cg = pmcstat_cgnode_allocate(image, pc);
+ if ((h = malloc(sizeof(*h))) == NULL)
+ err(EX_OSERR, "ERROR: Could not allocate callgraph node");
+
+ h->pch_pmcid = pmcid;
+ h->pch_cgnode = cg;
+ LIST_INSERT_HEAD(&pmcstat_cgnode_hash[hash], h, pch_next);
+
+ pmcstat_cgnode_hash_count++;
+
+ return (cg);
+}
+
+/*
+ * Compare two callgraph nodes for sorting.
+ */
+static int
+pmcstat_cgnode_compare(const void *a, const void *b)
+{
+ const struct pmcstat_cgnode *const *pcg1, *const *pcg2, *cg1, *cg2;
+
+ pcg1 = (const struct pmcstat_cgnode *const *) a;
+ cg1 = *pcg1;
+ pcg2 = (const struct pmcstat_cgnode *const *) b;
+ cg2 = *pcg2;
+
+ /* Sort in reverse order */
+ if (cg1->pcg_count < cg2->pcg_count)
+ return (1);
+ if (cg1->pcg_count > cg2->pcg_count)
+ return (-1);
+ return (0);
+}
+
+/*
+ * Find (allocating if a needed) a callgraph node in the given
+ * parent with the same (image, pcoffset) pair.
+ */
+
+static struct pmcstat_cgnode *
+pmcstat_cgnode_find(struct pmcstat_cgnode *parent, struct pmcstat_image *image,
+ uintfptr_t pcoffset)
+{
+ struct pmcstat_cgnode *child;
+
+ LIST_FOREACH(child, &parent->pcg_children, pcg_sibling) {
+ if (child->pcg_image == image &&
+ child->pcg_func == pcoffset)
+ return (child);
+ }
+
+ /*
+ * Allocate a new structure.
+ */
+
+ child = pmcstat_cgnode_allocate(image, pcoffset);
+
+ /*
+ * Link it into the parent.
+ */
+ LIST_INSERT_HEAD(&parent->pcg_children, child, pcg_sibling);
+ parent->pcg_nchildren++;
+
+ return (child);
+}
+
+/*
+ * Print one callgraph node. The output format is:
+ *
+ * indentation %(parent's samples) #nsamples function@object
+ */
+static void
+pmcstat_cgnode_print(struct pmcstat_args *a, struct pmcstat_cgnode *cg,
+ int depth, uint32_t total)
+{
+ uint32_t n;
+ const char *space;
+ struct pmcstat_symbol *sym;
+ struct pmcstat_cgnode **sortbuffer, **cgn, *pcg;
+
+ space = " ";
+
+ if (depth > 0)
+ (void) fprintf(a->pa_graphfile, "%*s", depth, space);
+
+ if (cg->pcg_count == total)
+ (void) fprintf(a->pa_graphfile, "100.0%% ");
+ else
+ (void) fprintf(a->pa_graphfile, "%05.2f%% ",
+ 100.0 * cg->pcg_count / total);
+
+ n = fprintf(a->pa_graphfile, " [%u] ", cg->pcg_count);
+
+ /* #samples is a 12 character wide field. */
+ if (n < 12)
+ (void) fprintf(a->pa_graphfile, "%*s", 12 - n, space);
+
+ if (depth > 0)
+ (void) fprintf(a->pa_graphfile, "%*s", depth, space);
+
+ sym = pmcstat_symbol_search(cg->pcg_image, cg->pcg_func);
+ if (sym)
+ (void) fprintf(a->pa_graphfile, "%s",
+ pmcstat_string_unintern(sym->ps_name));
+ else
+ (void) fprintf(a->pa_graphfile, "%p",
+ (void *) (cg->pcg_image->pi_vaddr + cg->pcg_func));
+
+ if (pmcstat_previous_filename_printed !=
+ cg->pcg_image->pi_fullpath) {
+ pmcstat_previous_filename_printed = cg->pcg_image->pi_fullpath;
+ (void) fprintf(a->pa_graphfile, " @ %s\n",
+ pmcstat_string_unintern(
+ pmcstat_previous_filename_printed));
+ } else
+ (void) fprintf(a->pa_graphfile, "\n");
+
+ if (cg->pcg_nchildren == 0)
+ return;
+
+ if ((sortbuffer = (struct pmcstat_cgnode **)
+ malloc(sizeof(struct pmcstat_cgnode *) *
+ cg->pcg_nchildren)) == NULL)
+ err(EX_OSERR, "ERROR: Cannot print callgraph");
+ cgn = sortbuffer;
+
+ LIST_FOREACH(pcg, &cg->pcg_children, pcg_sibling)
+ *cgn++ = pcg;
+
+ assert(cgn - sortbuffer == (int) cg->pcg_nchildren);
+
+ qsort(sortbuffer, cg->pcg_nchildren, sizeof(struct pmcstat_cgnode *),
+ pmcstat_cgnode_compare);
+
+ for (cgn = sortbuffer, n = 0; n < cg->pcg_nchildren; n++, cgn++)
+ pmcstat_cgnode_print(a, *cgn, depth+1, cg->pcg_count);
+
+ free(sortbuffer);
+}
+
+/*
+ * Record a callchain.
+ */
+
+static void
+pmcstat_record_callchain(struct pmcstat_process *pp, uint32_t pmcid,
+ uint32_t nsamples, uintfptr_t *cc, int usermode, struct pmcstat_args *a)
+{
+ uintfptr_t pc, loadaddress;
+ uint32_t n;
+ struct pmcstat_image *image;
+ struct pmcstat_pcmap *ppm;
+ struct pmcstat_symbol *sym;
+ struct pmcstat_cgnode *parent, *child;
+
+ /*
+ * Find the callgraph node recorded in the global hash table
+ * for this (pmcid, pc).
+ */
+
+ pc = cc[0];
+ parent = pmcstat_cgnode_hash_lookup_pc(pp, pmcid, pc, usermode);
+ if (parent == NULL) {
+ pmcstat_stats.ps_callchain_dubious_frames++;
+ return;
+ }
+
+ parent->pcg_count++;
+
+ /*
+ * For each return address in the call chain record, subject
+ * to the maximum depth desired.
+ * - Find the image associated with the sample. Stop if there
+ * there is no valid image at that address.
+ * - Find the function that overlaps the return address.
+ * - If found: use the start address of the function.
+ * If not found (say an object's symbol table is not present or
+ * is incomplete), round down to th gprof bucket granularity.
+ * - Convert return virtual address to an offset in the image.
+ * - Look for a child with the same {offset,image} tuple,
+ * inserting one if needed.
+ * - Increment the count of occurrences of the child.
+ */
+
+ for (n = 1; n < (uint32_t) a->pa_graphdepth && n < nsamples; n++,
+ parent = child) {
+ pc = cc[n];
+
+ ppm = pmcstat_process_find_map(usermode ? pp :
+ pmcstat_kernproc, pc);
+ if (ppm == NULL)
+ return;
+
+ image = ppm->ppm_image;
+ loadaddress = ppm->ppm_lowpc + image->pi_vaddr -
+ image->pi_start;
+ pc -= loadaddress;
+
+ if ((sym = pmcstat_symbol_search(image, pc)) != NULL)
+ pc = sym->ps_start;
+
+ child = pmcstat_cgnode_find(parent, image, pc);
+ child->pcg_count++;
+ }
+}
+
+/*
+ * Printing a callgraph for a PMC.
+ */
+static void
+pmcstat_callgraph_print_for_pmcid(struct pmcstat_args *a,
+ struct pmcstat_pmcrecord *pmcr)
+{
+ int n, nentries;
+ uint32_t nsamples, pmcid;
+ struct pmcstat_cgnode **sortbuffer, **cgn;
+ struct pmcstat_cgnode_hash *pch;
+
+ /*
+ * We pull out all callgraph nodes in the top-level hash table
+ * with a matching PMC id. We then sort these based on the
+ * frequency of occurrence. Each callgraph node is then
+ * printed.
+ */
+
+ nsamples = 0;
+ pmcid = pmcr->pr_pmcid;
+ if ((sortbuffer = (struct pmcstat_cgnode **)
+ malloc(sizeof(struct pmcstat_cgnode *) *
+ pmcstat_cgnode_hash_count)) == NULL)
+ err(EX_OSERR, "ERROR: Cannot sort callgraph");
+ cgn = sortbuffer;
+
+ memset(sortbuffer, 0xFF, pmcstat_cgnode_hash_count *
+ sizeof(struct pmcstat_cgnode **));
+
+ for (n = 0; n < PMCSTAT_NHASH; n++)
+ LIST_FOREACH(pch, &pmcstat_cgnode_hash[n], pch_next)
+ if (pch->pch_pmcid == pmcid) {
+ nsamples += pch->pch_cgnode->pcg_count;
+ *cgn++ = pch->pch_cgnode;
+ }
+
+ nentries = cgn - sortbuffer;
+ assert(nentries <= pmcstat_cgnode_hash_count);
+
+ if (nentries == 0)
+ return;
+
+ qsort(sortbuffer, nentries, sizeof(struct pmcstat_cgnode *),
+ pmcstat_cgnode_compare);
+
+ (void) fprintf(a->pa_graphfile,
+ "@ %s [%u samples]\n\n",
+ pmcstat_string_unintern(pmcr->pr_pmcname),
+ nsamples);
+
+ for (cgn = sortbuffer, n = 0; n < nentries; n++, cgn++) {
+ pmcstat_previous_filename_printed = NULL;
+ pmcstat_cgnode_print(a, *cgn, 0, nsamples);
+ (void) fprintf(a->pa_graphfile, "\n");
+ }
+
+ free(sortbuffer);
+}
+
+/*
+ * Print out callgraphs.
+ */
+
+static void
+pmcstat_callgraph_print(struct pmcstat_args *a)
+{
+ struct pmcstat_pmcrecord *pmcr;
+
+ LIST_FOREACH(pmcr, &pmcstat_pmcs, pr_next)
+ pmcstat_callgraph_print_for_pmcid(a, pmcr);
+}
+
+static void
+pmcstat_cgnode_do_gmon_arcs(struct pmcstat_cgnode *cg, pmc_id_t pmcid)
+{
+ struct pmcstat_cgnode *cgc;
+
+ /*
+ * Look for child nodes that belong to the same image.
+ */
+
+ LIST_FOREACH(cgc, &cg->pcg_children, pcg_sibling) {
+ if (cgc->pcg_image == cg->pcg_image)
+ pmcstat_gmon_append_arc(cg->pcg_image, pmcid,
+ cgc->pcg_func, cg->pcg_func, cgc->pcg_count);
+ if (cgc->pcg_nchildren > 0)
+ pmcstat_cgnode_do_gmon_arcs(cgc, pmcid);
+ }
+}
+
+static void
+pmcstat_callgraph_do_gmon_arcs_for_pmcid(pmc_id_t pmcid)
+{
+ int n;
+ struct pmcstat_cgnode_hash *pch;
+
+ for (n = 0; n < PMCSTAT_NHASH; n++)
+ LIST_FOREACH(pch, &pmcstat_cgnode_hash[n], pch_next)
+ if (pch->pch_pmcid == pmcid &&
+ pch->pch_cgnode->pcg_nchildren > 1)
+ pmcstat_cgnode_do_gmon_arcs(pch->pch_cgnode,
+ pmcid);
+}
+
+
+static void
+pmcstat_callgraph_do_gmon_arcs(void)
+{
+ struct pmcstat_pmcrecord *pmcr;
+
+ LIST_FOREACH(pmcr, &pmcstat_pmcs, pr_next)
+ pmcstat_callgraph_do_gmon_arcs_for_pmcid(pmcr->pr_pmcid);
+}
+
+/*
+ * Convert a hwpmc(4) log to profile information. A system-wide
+ * callgraph is generated if FLAG_DO_CALLGRAPHS is set. gmon.out
+ * files usable by gprof(1) are created if FLAG_DO_GPROF is set.
+ */
+static int
+pmcstat_analyze_log(struct pmcstat_args *a)
+{
+ uint32_t cpu, cpuflags;
+ uintfptr_t pc, newpc;
+ pid_t pid;
+ struct pmcstat_image *image;
+ struct pmcstat_symbol *sym;
+ struct pmcstat_process *pp, *ppnew;
+ struct pmcstat_pcmap *ppm, *ppmtmp;
+ struct pmclog_ev ev;
+ pmcstat_interned_string image_path;
+
+ assert(a->pa_flags & FLAG_DO_ANALYSIS);
+
+ if (elf_version(EV_CURRENT) == EV_NONE)
+ err(EX_UNAVAILABLE, "Elf library intialization failed");
+
+ while (pmclog_read(a->pa_logparser, &ev) == 0) {
+ assert(ev.pl_state == PMCLOG_OK);
+
+ switch (ev.pl_type) {
+ case PMCLOG_TYPE_INITIALIZE:
+ if ((ev.pl_u.pl_i.pl_version & 0xFF000000) !=
+ PMC_VERSION_MAJOR << 24 && a->pa_verbosity > 0)
+ warnx("WARNING: Log version 0x%x does not "
+ "match compiled version 0x%x.",
+ ev.pl_u.pl_i.pl_version,
+ PMC_VERSION_MAJOR);
+ break;
+
+ case PMCLOG_TYPE_MAP_IN:
+ /*
+ * Introduce an address range mapping for a
+ * userland process or the kernel (pid == -1).
+ *
+ * We always allocate a process descriptor so
+ * that subsequent samples seen for this
+ * address range are mapped to the current
+ * object being mapped in.
+ */
+ pid = ev.pl_u.pl_mi.pl_pid;
+ if (pid == -1)
+ pp = pmcstat_kernproc;
+ else
+ pp = pmcstat_process_lookup(pid,
+ PMCSTAT_ALLOCATE);
+
+ assert(pp != NULL);
+
+ image_path = pmcstat_string_intern(ev.pl_u.pl_mi.
+ pl_pathname);
+ image = pmcstat_image_from_path(image_path, pid == -1);
+ if (image->pi_type == PMCSTAT_IMAGE_UNKNOWN)
+ pmcstat_image_determine_type(image, a);
+ if (image->pi_type != PMCSTAT_IMAGE_INDETERMINABLE)
+ pmcstat_image_link(pp, image,
+ ev.pl_u.pl_mi.pl_start);
+ break;
+
+ case PMCLOG_TYPE_MAP_OUT:
+ /*
+ * Remove an address map.
+ */
+ pid = ev.pl_u.pl_mo.pl_pid;
+ if (pid == -1)
+ pp = pmcstat_kernproc;
+ else
+ pp = pmcstat_process_lookup(pid, 0);
+
+ if (pp == NULL) /* unknown process */
+ break;
+
+ pmcstat_image_unmap(pp, ev.pl_u.pl_mo.pl_start,
+ ev.pl_u.pl_mo.pl_end);
+ break;
+
+ case PMCLOG_TYPE_PCSAMPLE:
+ /*
+ * Note: the `PCSAMPLE' log entry is not
+ * generated by hpwmc(4) after version 2.
+ */
+
+ /*
+ * We bring in the gmon file for the image
+ * currently associated with the PMC & pid
+ * pair and increment the appropriate entry
+ * bin inside this.
+ */
+ pmcstat_stats.ps_samples_total++;
+
+ pc = ev.pl_u.pl_s.pl_pc;
+ pp = pmcstat_process_lookup(ev.pl_u.pl_s.pl_pid,
+ PMCSTAT_ALLOCATE);
+ if ((ppm = pmcstat_process_find_map(pp, pc)) == NULL &&
+ (ppm = pmcstat_process_find_map(pmcstat_kernproc,
+ pc)) == NULL) { /* unknown process,offset pair */
+ pmcstat_stats.ps_samples_unknown_offset++;
+ break;
+ }
+
+ pmcstat_image_increment_bucket(ppm, pc,
+ ev.pl_u.pl_s.pl_pmcid, a);
+
+ break;
+
+ case PMCLOG_TYPE_CALLCHAIN:
+ pmcstat_stats.ps_samples_total++;
+
+ cpuflags = ev.pl_u.pl_cc.pl_cpuflags;
+ cpu = PMC_CALLCHAIN_CPUFLAGS_TO_CPU(cpuflags);
+
+ /* Filter on the CPU id. */
+ if ((a->pa_cpumask & (1 << cpu)) == 0) {
+ pmcstat_stats.ps_samples_skipped++;
+ break;
+ }
+
+ pp = pmcstat_process_lookup(ev.pl_u.pl_cc.pl_pid,
+ PMCSTAT_ALLOCATE);
+
+ if ((a->pa_flags & FLAG_WANTS_MAPPINGS) == 0)
+ pmcstat_record_callchain(pp,
+ ev.pl_u.pl_cc.pl_pmcid,
+ ev.pl_u.pl_cc.pl_npc, ev.pl_u.pl_cc.pl_pc,
+ PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(cpuflags), a);
+
+ if ((a->pa_flags &
+ (FLAG_DO_GPROF | FLAG_WANTS_MAPPINGS)) == 0)
+ break;
+
+ pc = ev.pl_u.pl_cc.pl_pc[0];
+ if (PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(cpuflags) == 0)
+ pp = pmcstat_kernproc;
+ ppm = pmcstat_process_find_map(pp, pc);
+ if (ppm == NULL) {
+
+ /* Unknown offset. */
+ pmcstat_stats.ps_samples_unknown_offset++;
+ break;
+ }
+ if (a->pa_flags & FLAG_WANTS_MAPPINGS) {
+ image = ppm->ppm_image;
+ newpc = pc - (ppm->ppm_lowpc +
+ (image->pi_vaddr - image->pi_start));
+ sym = pmcstat_symbol_search(image, newpc);
+ if (sym == NULL)
+ break;
+ fprintf(a->pa_graphfile, "%p %s 0x%jx 0x%jx\n",
+ (void *)pc,
+ pmcstat_string_unintern(sym->ps_name),
+ (uintmax_t)(sym->ps_start +
+ image->pi_vaddr), (uintmax_t)(sym->ps_end +
+ image->pi_vaddr));
+ break;
+ }
+
+ pmcstat_image_increment_bucket(ppm, pc,
+ ev.pl_u.pl_cc.pl_pmcid, a);
+
+ break;
+
+ case PMCLOG_TYPE_PMCALLOCATE:
+ /*
+ * Record the association pmc id between this
+ * PMC and its name.
+ */
+ pmcstat_pmcid_add(ev.pl_u.pl_a.pl_pmcid,
+ pmcstat_string_intern(ev.pl_u.pl_a.pl_evname), a);
+ break;
+
+ case PMCLOG_TYPE_PROCEXEC:
+
+ /*
+ * Change the executable image associated with
+ * a process.
+ */
+ pp = pmcstat_process_lookup(ev.pl_u.pl_x.pl_pid,
+ PMCSTAT_ALLOCATE);
+
+ /* delete the current process map */
+ TAILQ_FOREACH_SAFE(ppm, &pp->pp_map, ppm_next, ppmtmp) {
+ TAILQ_REMOVE(&pp->pp_map, ppm, ppm_next);
+ free(ppm);
+ }
+
+ /* associate this process image */
+ image_path = pmcstat_string_intern(
+ ev.pl_u.pl_x.pl_pathname);
+ assert(image_path != NULL);
+ pmcstat_process_exec(pp, image_path,
+ ev.pl_u.pl_x.pl_entryaddr, a);
+ break;
+
+ case PMCLOG_TYPE_PROCEXIT:
+
+ /*
+ * Due to the way the log is generated, the
+ * last few samples corresponding to a process
+ * may appear in the log after the process
+ * exit event is recorded. Thus we keep the
+ * process' descriptor and associated data
+ * structures around, but mark the process as
+ * having exited.
+ */
+ pp = pmcstat_process_lookup(ev.pl_u.pl_e.pl_pid, 0);
+ if (pp == NULL)
+ break;
+ pp->pp_isactive = 0; /* mark as a zombie */
+ break;
+
+ case PMCLOG_TYPE_SYSEXIT:
+ pp = pmcstat_process_lookup(ev.pl_u.pl_se.pl_pid, 0);
+ if (pp == NULL)
+ break;
+ pp->pp_isactive = 0; /* make a zombie */
+ break;
+
+ case PMCLOG_TYPE_PROCFORK:
+
+ /*
+ * Allocate a process descriptor for the new
+ * (child) process.
+ */
+ ppnew =
+ pmcstat_process_lookup(ev.pl_u.pl_f.pl_newpid,
+ PMCSTAT_ALLOCATE);
+
+ /*
+ * If we had been tracking the parent, clone
+ * its address maps.
+ */
+ pp = pmcstat_process_lookup(ev.pl_u.pl_f.pl_oldpid, 0);
+ if (pp == NULL)
+ break;
+ TAILQ_FOREACH(ppm, &pp->pp_map, ppm_next)
+ pmcstat_image_link(ppnew, ppm->ppm_image,
+ ppm->ppm_lowpc);
+ break;
+
+ default: /* other types of entries are not relevant */
+ break;
+ }
+ }
+
+ if (ev.pl_state == PMCLOG_EOF)
+ return (PMCSTAT_FINISHED);
+ else if (ev.pl_state == PMCLOG_REQUIRE_DATA)
+ return (PMCSTAT_RUNNING);
+
+ err(EX_DATAERR, "ERROR: event parsing failed (record %jd, "
+ "offset 0x%jx)", (uintmax_t) ev.pl_count + 1, ev.pl_offset);
+}
+
+/*
+ * Print log entries as text.
+ */
+
+static int
+pmcstat_print_log(struct pmcstat_args *a)
+{
+ struct pmclog_ev ev;
+ uint32_t npc;
+
+ while (pmclog_read(a->pa_logparser, &ev) == 0) {
+ assert(ev.pl_state == PMCLOG_OK);
+ switch (ev.pl_type) {
+ case PMCLOG_TYPE_CALLCHAIN:
+ PMCSTAT_PRINT_ENTRY(a, "callchain",
+ "%d 0x%x %d %d %c", ev.pl_u.pl_cc.pl_pid,
+ ev.pl_u.pl_cc.pl_pmcid,
+ PMC_CALLCHAIN_CPUFLAGS_TO_CPU(ev.pl_u.pl_cc. \
+ pl_cpuflags), ev.pl_u.pl_cc.pl_npc,
+ PMC_CALLCHAIN_CPUFLAGS_TO_USERMODE(ev.pl_u.pl_cc.\
+ pl_cpuflags) ? 'u' : 's');
+ for (npc = 0; npc < ev.pl_u.pl_cc.pl_npc; npc++)
+ PMCSTAT_PRINT_ENTRY(a, "...", "%p",
+ (void *) ev.pl_u.pl_cc.pl_pc[npc]);
+ break;
+ case PMCLOG_TYPE_CLOSELOG:
+ PMCSTAT_PRINT_ENTRY(a,"closelog",);
+ break;
+ case PMCLOG_TYPE_DROPNOTIFY:
+ PMCSTAT_PRINT_ENTRY(a,"drop",);
+ break;
+ case PMCLOG_TYPE_INITIALIZE:
+ PMCSTAT_PRINT_ENTRY(a,"initlog","0x%x \"%s\"",
+ ev.pl_u.pl_i.pl_version,
+ pmc_name_of_cputype(ev.pl_u.pl_i.pl_arch));
+ if ((ev.pl_u.pl_i.pl_version & 0xFF000000) !=
+ PMC_VERSION_MAJOR << 24 && a->pa_verbosity > 0)
+ warnx("WARNING: Log version 0x%x != expected "
+ "version 0x%x.", ev.pl_u.pl_i.pl_version,
+ PMC_VERSION);
+ break;
+ case PMCLOG_TYPE_MAP_IN:
+ PMCSTAT_PRINT_ENTRY(a,"map-in","%d %p \"%s\"",
+ ev.pl_u.pl_mi.pl_pid,
+ (void *) ev.pl_u.pl_mi.pl_start,
+ ev.pl_u.pl_mi.pl_pathname);
+ break;
+ case PMCLOG_TYPE_MAP_OUT:
+ PMCSTAT_PRINT_ENTRY(a,"map-out","%d %p %p",
+ ev.pl_u.pl_mo.pl_pid,
+ (void *) ev.pl_u.pl_mo.pl_start,
+ (void *) ev.pl_u.pl_mo.pl_end);
+ break;
+ case PMCLOG_TYPE_PCSAMPLE:
+ PMCSTAT_PRINT_ENTRY(a,"sample","0x%x %d %p %c",
+ ev.pl_u.pl_s.pl_pmcid,
+ ev.pl_u.pl_s.pl_pid,
+ (void *) ev.pl_u.pl_s.pl_pc,
+ ev.pl_u.pl_s.pl_usermode ? 'u' : 's');
+ break;
+ case PMCLOG_TYPE_PMCALLOCATE:
+ PMCSTAT_PRINT_ENTRY(a,"allocate","0x%x \"%s\" 0x%x",
+ ev.pl_u.pl_a.pl_pmcid,
+ ev.pl_u.pl_a.pl_evname,
+ ev.pl_u.pl_a.pl_flags);
+ break;
+ case PMCLOG_TYPE_PMCATTACH:
+ PMCSTAT_PRINT_ENTRY(a,"attach","0x%x %d \"%s\"",
+ ev.pl_u.pl_t.pl_pmcid,
+ ev.pl_u.pl_t.pl_pid,
+ ev.pl_u.pl_t.pl_pathname);
+ break;
+ case PMCLOG_TYPE_PMCDETACH:
+ PMCSTAT_PRINT_ENTRY(a,"detach","0x%x %d",
+ ev.pl_u.pl_d.pl_pmcid,
+ ev.pl_u.pl_d.pl_pid);
+ break;
+ case PMCLOG_TYPE_PROCCSW:
+ PMCSTAT_PRINT_ENTRY(a,"cswval","0x%x %d %jd",
+ ev.pl_u.pl_c.pl_pmcid,
+ ev.pl_u.pl_c.pl_pid,
+ ev.pl_u.pl_c.pl_value);
+ break;
+ case PMCLOG_TYPE_PROCEXEC:
+ PMCSTAT_PRINT_ENTRY(a,"exec","0x%x %d %p \"%s\"",
+ ev.pl_u.pl_x.pl_pmcid,
+ ev.pl_u.pl_x.pl_pid,
+ (void *) ev.pl_u.pl_x.pl_entryaddr,
+ ev.pl_u.pl_x.pl_pathname);
+ break;
+ case PMCLOG_TYPE_PROCEXIT:
+ PMCSTAT_PRINT_ENTRY(a,"exitval","0x%x %d %jd",
+ ev.pl_u.pl_e.pl_pmcid,
+ ev.pl_u.pl_e.pl_pid,
+ ev.pl_u.pl_e.pl_value);
+ break;
+ case PMCLOG_TYPE_PROCFORK:
+ PMCSTAT_PRINT_ENTRY(a,"fork","%d %d",
+ ev.pl_u.pl_f.pl_oldpid,
+ ev.pl_u.pl_f.pl_newpid);
+ break;
+ case PMCLOG_TYPE_USERDATA:
+ PMCSTAT_PRINT_ENTRY(a,"userdata","0x%x",
+ ev.pl_u.pl_u.pl_userdata);
+ break;
+ case PMCLOG_TYPE_SYSEXIT:
+ PMCSTAT_PRINT_ENTRY(a,"exit","%d",
+ ev.pl_u.pl_se.pl_pid);
+ break;
+ default:
+ fprintf(a->pa_printfile, "unknown event (type %d).\n",
+ ev.pl_type);
+ }
+ }
+
+ if (ev.pl_state == PMCLOG_EOF)
+ return (PMCSTAT_FINISHED);
+ else if (ev.pl_state == PMCLOG_REQUIRE_DATA)
+ return (PMCSTAT_RUNNING);
+
+ errx(EX_DATAERR, "ERROR: event parsing failed "
+ "(record %jd, offset 0x%jx).",
+ (uintmax_t) ev.pl_count + 1, ev.pl_offset);
+ /*NOTREACHED*/
+}
+
+/*
+ * Public Interfaces.
+ */
+
+/*
+ * Close a logfile, after first flushing all in-module queued data.
+ */
+
+int
+pmcstat_close_log(struct pmcstat_args *a)
+{
+ if (pmc_flush_logfile() < 0 ||
+ pmc_configure_logfile(-1) < 0)
+ err(EX_OSERR, "ERROR: logging failed");
+ a->pa_flags &= ~(FLAG_HAS_OUTPUT_LOGFILE | FLAG_HAS_PIPE);
+ return (a->pa_flags & FLAG_HAS_PIPE ? PMCSTAT_EXITING :
+ PMCSTAT_FINISHED);
+}
+
+
+
+/*
+ * Open a log file, for reading or writing.
+ *
+ * The function returns the fd of a successfully opened log or -1 in
+ * case of failure.
+ */
+
+int
+pmcstat_open_log(const char *path, int mode)
+{
+ int error, fd;
+ size_t hlen;
+ const char *p, *errstr;
+ struct addrinfo hints, *res, *res0;
+ char hostname[MAXHOSTNAMELEN];
+
+ errstr = NULL;
+ fd = -1;
+
+ /*
+ * If 'path' is "-" then open one of stdin or stdout depending
+ * on the value of 'mode'.
+ *
+ * If 'path' contains a ':' and does not start with a '/' or '.',
+ * and is being opened for writing, treat it as a "host:port"
+ * specification and open a network socket.
+ *
+ * Otherwise, treat 'path' as a file name and open that.
+ */
+ if (path[0] == '-' && path[1] == '\0')
+ fd = (mode == PMCSTAT_OPEN_FOR_READ) ? 0 : 1;
+ else if (mode == PMCSTAT_OPEN_FOR_WRITE && path[0] != '/' &&
+ path[0] != '.' && strchr(path, ':') != NULL) {
+
+ p = strrchr(path, ':');
+ hlen = p - path;
+ if (p == path || hlen >= sizeof(hostname)) {
+ errstr = strerror(EINVAL);
+ goto done;
+ }
+
+ assert(hlen < sizeof(hostname));
+ (void) strncpy(hostname, path, hlen);
+ hostname[hlen] = '\0';
+
+ (void) memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ if ((error = getaddrinfo(hostname, p+1, &hints, &res0)) != 0) {
+ errstr = gai_strerror(error);
+ goto done;
+ }
+
+ fd = -1;
+ for (res = res0; res; res = res->ai_next) {
+ if ((fd = socket(res->ai_family, res->ai_socktype,
+ res->ai_protocol)) < 0) {
+ errstr = strerror(errno);
+ continue;
+ }
+ if (connect(fd, res->ai_addr, res->ai_addrlen) < 0) {
+ errstr = strerror(errno);
+ (void) close(fd);
+ fd = -1;
+ continue;
+ }
+ errstr = NULL;
+ break;
+ }
+ freeaddrinfo(res0);
+
+ } else if ((fd = open(path, mode == PMCSTAT_OPEN_FOR_READ ?
+ O_RDONLY : (O_WRONLY|O_CREAT|O_TRUNC),
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) < 0)
+ errstr = strerror(errno);
+
+ done:
+ if (errstr)
+ errx(EX_OSERR, "ERROR: Cannot open \"%s\" for %s: %s.", path,
+ (mode == PMCSTAT_OPEN_FOR_READ ? "reading" : "writing"),
+ errstr);
+
+ return (fd);
+}
+
+/*
+ * Process a log file in offline analysis mode.
+ */
+
+int
+pmcstat_process_log(struct pmcstat_args *a)
+{
+
+ /*
+ * If analysis has not been asked for, just print the log to
+ * the current output file.
+ */
+ if (a->pa_flags & FLAG_DO_PRINT)
+ return (pmcstat_print_log(a));
+ else
+ return (pmcstat_analyze_log(a));
+}
+
+/*
+ * Initialize module.
+ */
+
+void
+pmcstat_initialize_logging(struct pmcstat_args *a)
+{
+ int i;
+
+ (void) a;
+
+ /* use a convenient format for 'ldd' output */
+ if (setenv("LD_TRACE_LOADED_OBJECTS_FMT1","%o \"%p\" %x\n",1) != 0)
+ err(EX_OSERR, "ERROR: Cannot setenv");
+
+ /* Initialize hash tables */
+ pmcstat_string_initialize();
+ for (i = 0; i < PMCSTAT_NHASH; i++) {
+ LIST_INIT(&pmcstat_image_hash[i]);
+ LIST_INIT(&pmcstat_process_hash[i]);
+ }
+
+ /*
+ * Create a fake 'process' entry for the kernel with pid -1.
+ * hwpmc(4) will subsequently inform us about where the kernel
+ * and any loaded kernel modules are mapped.
+ */
+ if ((pmcstat_kernproc = pmcstat_process_lookup((pid_t) -1,
+ PMCSTAT_ALLOCATE)) == NULL)
+ err(EX_OSERR, "ERROR: Cannot initialize logging");
+}
+
+/*
+ * Shutdown module.
+ */
+
+void
+pmcstat_shutdown_logging(struct pmcstat_args *a)
+{
+ int i;
+ FILE *mf;
+ struct pmcstat_gmonfile *pgf, *pgftmp;
+ struct pmcstat_image *pi, *pitmp;
+ struct pmcstat_process *pp, *pptmp;
+ struct pmcstat_cgnode_hash *pch, *pchtmp;
+
+ /* determine where to send the map file */
+ mf = NULL;
+ if (a->pa_mapfilename != NULL)
+ mf = (strcmp(a->pa_mapfilename, "-") == 0) ?
+ a->pa_printfile : fopen(a->pa_mapfilename, "w");
+
+ if (mf == NULL && a->pa_flags & FLAG_DO_GPROF &&
+ a->pa_verbosity >= 2)
+ mf = a->pa_printfile;
+
+ if (mf)
+ (void) fprintf(mf, "MAP:\n");
+
+
+ if (a->pa_flags & FLAG_DO_CALLGRAPHS)
+ pmcstat_callgraph_print(a);
+
+ /*
+ * Sync back all gprof flat profile data.
+ */
+ for (i = 0; i < PMCSTAT_NHASH; i++) {
+ LIST_FOREACH(pi, &pmcstat_image_hash[i], pi_next) {
+ if (mf)
+ (void) fprintf(mf, " \"%s\" => \"%s\"",
+ pmcstat_string_unintern(pi->pi_execpath),
+ pmcstat_string_unintern(
+ pi->pi_samplename));
+
+ /* flush gmon.out data to disk */
+ LIST_FOREACH(pgf, &pi->pi_gmlist, pgf_next) {
+ pmcstat_gmon_unmap_file(pgf);
+ if (mf)
+ (void) fprintf(mf, " %s/%d",
+ pmcstat_pmcid_to_name(
+ pgf->pgf_pmcid),
+ pgf->pgf_nsamples);
+ if (pgf->pgf_overflow && a->pa_verbosity >= 1)
+ warnx("WARNING: profile \"%s\" "
+ "overflowed.",
+ pmcstat_string_unintern(
+ pgf->pgf_name));
+ }
+
+ if (mf)
+ (void) fprintf(mf, "\n");
+ }
+ }
+
+ /*
+ * Compute arcs and add these to the gprof files.
+ */
+ if (a->pa_flags & FLAG_DO_GPROF && a->pa_graphdepth > 1)
+ pmcstat_callgraph_do_gmon_arcs();
+
+ /*
+ * Free memory.
+ */
+ for (i = 0; i < PMCSTAT_NHASH; i++) {
+ LIST_FOREACH_SAFE(pch, &pmcstat_cgnode_hash[i], pch_next,
+ pchtmp) {
+ pmcstat_cgnode_free(pch->pch_cgnode);
+ free(pch);
+ }
+ }
+
+ for (i = 0; i < PMCSTAT_NHASH; i++) {
+ LIST_FOREACH_SAFE(pi, &pmcstat_image_hash[i], pi_next, pitmp)
+ {
+ LIST_FOREACH_SAFE(pgf, &pi->pi_gmlist, pgf_next,
+ pgftmp) {
+ if (pgf->pgf_file)
+ (void) fclose(pgf->pgf_file);
+ LIST_REMOVE(pgf, pgf_next);
+ free(pgf);
+ }
+ if (pi->pi_symbols)
+ free(pi->pi_symbols);
+
+ LIST_REMOVE(pi, pi_next);
+ free(pi);
+ }
+
+ LIST_FOREACH_SAFE(pp, &pmcstat_process_hash[i], pp_next,
+ pptmp) {
+ LIST_REMOVE(pp, pp_next);
+ free(pp);
+ }
+ }
+
+ pmcstat_string_shutdown();
+
+ /*
+ * Print errors unless -q was specified. Print all statistics
+ * if verbosity > 1.
+ */
+#define PRINT(N,V,A) do { \
+ if (pmcstat_stats.ps_##V || (A)->pa_verbosity >= 2) \
+ (void) fprintf((A)->pa_printfile, " %-40s %d\n",\
+ N, pmcstat_stats.ps_##V); \
+ } while (0)
+
+ if (a->pa_verbosity >= 1 && a->pa_flags & FLAG_DO_GPROF) {
+ (void) fprintf(a->pa_printfile, "CONVERSION STATISTICS:\n");
+ PRINT("#exec/a.out", exec_aout, a);
+ PRINT("#exec/elf", exec_elf, a);
+ PRINT("#exec/unknown", exec_indeterminable, a);
+ PRINT("#exec handling errors", exec_errors, a);
+ PRINT("#samples/total", samples_total, a);
+ PRINT("#samples/unclaimed", samples_unknown_offset, a);
+ PRINT("#samples/unknown-object", samples_indeterminable, a);
+ PRINT("#callchain/dubious-frames", callchain_dubious_frames,
+ a);
+ }
+
+ if (mf)
+ (void) fclose(mf);
+}
diff --git a/usr.sbin/pnpinfo/Makefile b/usr.sbin/pnpinfo/Makefile
new file mode 100644
index 0000000..294b4f7
--- /dev/null
+++ b/usr.sbin/pnpinfo/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../contrib/pnpinfo
+
+PROG= pnpinfo
+MAN= pnpinfo.8
+
+CFLAGS+= -I${.CURDIR}/../../sys
+
+.if ${MACHINE} == "pc98"
+CFLAGS+= -DPC98
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/portsnap/Makefile b/usr.sbin/portsnap/Makefile
new file mode 100644
index 0000000..0f77bcb
--- /dev/null
+++ b/usr.sbin/portsnap/Makefile
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+SUBDIR= portsnap make_index phttpget
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/portsnap/Makefile.inc b/usr.sbin/portsnap/Makefile.inc
new file mode 100644
index 0000000..5f6527e
--- /dev/null
+++ b/usr.sbin/portsnap/Makefile.inc
@@ -0,0 +1,5 @@
+# $FreeBSD$
+
+LIBEXECDIR?= /usr/libexec
+
+.include "../Makefile.inc"
diff --git a/usr.sbin/portsnap/make_index/Makefile b/usr.sbin/portsnap/make_index/Makefile
new file mode 100644
index 0000000..65b1a8e
--- /dev/null
+++ b/usr.sbin/portsnap/make_index/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= make_index
+NO_MAN=
+WARNS?= 6
+
+BINDIR= ${LIBEXECDIR}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/portsnap/make_index/make_index.c b/usr.sbin/portsnap/make_index/make_index.c
new file mode 100644
index 0000000..c15ce10
--- /dev/null
+++ b/usr.sbin/portsnap/make_index/make_index.c
@@ -0,0 +1,513 @@
+/*-
+ * Copyright 2005 Colin Percival
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted providing that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct port;
+
+typedef union {
+ char * name;
+ struct port * p;
+} DEP;
+
+typedef struct port {
+ char * pkgname;
+ char * portdir;
+ char * prefix;
+ char * comment;
+ char * pkgdescr;
+ char * maintainer;
+ char * categories;
+ size_t n_edep;
+ DEP * edep;
+ size_t n_pdep;
+ DEP * pdep;
+ size_t n_fdep;
+ DEP * fdep;
+ size_t n_bdep;
+ DEP * bdep;
+ size_t n_rdep;
+ DEP * rdep;
+ char * www;
+ int recursed;
+} PORT;
+
+static void usage(void);
+static char * strdup2(const char *str);
+static DEP * makelist(char * str, size_t * n);
+static PORT * portify(char * line);
+static int portcompare(char * a, char * b);
+static void heapifyports(PORT **pp, size_t size, size_t pos);
+static PORT * findport(PORT ** pp, size_t st, size_t en, char * name, char * from);
+static void translateport(PORT ** pp, size_t pplen, PORT * p);
+static DEP * recurse_one(DEP * d, size_t * nd);
+static void recurse(PORT * p);
+static void heapifypkgs(DEP * d, size_t size, size_t pos);
+static void sortpkgs(DEP * d, size_t nd);
+static void printport(PORT * p);
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "usage: make_index file\n");
+ exit(1);
+ /* NOTREACHED */
+}
+
+static char *
+strdup2(const char *str)
+{
+ char * r;
+
+ r = strdup(str);
+ if (r == NULL)
+ err(1, "strdup");
+ return r;
+}
+
+/* Take a space-separated list and return an array of (char *) */
+static DEP *
+makelist(char * str, size_t * n)
+{
+ DEP * d;
+ size_t i;
+
+ /* No depends at all? */
+ if (str[0] == 0) {
+ *n = 0;
+ return NULL;
+ }
+
+ /* Count the number of fields */
+ *n = 1;
+ for (i = 0; str[i] != 0; i++)
+ if (str[i] == ' ')
+ (*n)++;
+
+ /* Allocate and fill an array */
+ d = malloc(*n * sizeof(DEP));
+ if (d == NULL)
+ err(1, "malloc(DEP)");
+ for (i = 0; i < *n; i++) {
+ d[i].name = strdup2(strsep(&str, " "));
+
+ /* Strip trailing slashes */
+ if (d[i].name[strlen(d[i].name) - 1] == '/')
+ d[i].name[strlen(d[i].name) - 1] = 0;
+ }
+
+ return d;
+}
+
+/* Take a port's describe line and split it into fields */
+static PORT *
+portify(char * line)
+{
+ PORT * p;
+ size_t i, n;
+
+ /* Verify that line has the right number of fields */
+ for (n = i = 0; line[i] != 0; i++)
+ if (line[i] == '|')
+ n++;
+ if (n != 12)
+ errx(1, "Port describe line is corrupt:\n%s\n", line);
+
+ p = malloc(sizeof(PORT));
+ if (p == NULL)
+ err(1, "malloc(PORT)");
+
+ p->pkgname = strdup2(strsep(&line, "|"));
+ p->portdir = strdup2(strsep(&line, "|"));
+ p->prefix = strdup2(strsep(&line, "|"));
+ p->comment = strdup2(strsep(&line, "|"));
+ p->pkgdescr = strdup2(strsep(&line, "|"));
+ p->maintainer = strdup2(strsep(&line, "|"));
+ p->categories = strdup2(strsep(&line, "|"));
+ p->edep = makelist(strsep(&line, "|"), &p->n_edep);
+ p->pdep = makelist(strsep(&line, "|"), &p->n_pdep);
+ p->fdep = makelist(strsep(&line, "|"), &p->n_fdep);
+ p->bdep = makelist(strsep(&line, "|"), &p->n_bdep);
+ p->rdep = makelist(strsep(&line, "|"), &p->n_rdep);
+ p->www = strdup2(strsep(&line, "|"));
+
+ p->recursed = 0;
+
+ /*
+ * line will now be equal to NULL -- we counted the field
+ * separators at the top of the function.
+ */
+
+ return p;
+}
+
+/* Returns -1, 0, or 1 based on a comparison of the portdir strings */
+static int
+portcompare(char * a, char * b)
+{
+ size_t i;
+
+ /* Find first non-matching position */
+ for (i = 0; ; i++) {
+ if (a[i] != b[i])
+ break;
+ if (a[i] == 0) /* End of strings */
+ return 0;
+ }
+
+ /* One string is a prefix of the other */
+ if (a[i] == 0)
+ return -1;
+ if (b[i] == 0)
+ return 1;
+
+ /* One string has a category which is a prefix of the other */
+ if (a[i] == '/')
+ return -1;
+ if (b[i] == '/')
+ return 1;
+
+ /* The two strings are simply different */
+ if (a[i] < b[i])
+ return -1;
+ else
+ return 1;
+}
+
+/* Heapify (PORT *) number pos in a pseudo-heap pp[0]..pp[size - 1] */
+static void
+heapifyports(PORT **pp, size_t size, size_t pos)
+{
+ size_t i = pos;
+ PORT * tmp;
+
+top:
+ /* Find the largest value out of {pos, 2*pos+1, 2*pos+2} */
+ if ((2 * pos + 1 < size) &&
+ (portcompare(pp[i]->portdir, pp[2 * pos + 1]->portdir) < 0))
+ i = 2 * pos + 1;
+ if ((2 * pos + 2 < size) &&
+ (portcompare(pp[i]->portdir, pp[2 * pos + 2]->portdir) < 0))
+ i = 2 * pos + 2;
+
+ /* If necessary, swap elements and iterate down the tree. */
+ if (i != pos) {
+ tmp = pp[pos];
+ pp[pos] = pp[i];
+ pp[i] = tmp;
+ pos = i;
+ goto top;
+ }
+}
+
+/* Translate a port directory name into a (PORT *), and free the name */
+static PORT *
+findport(PORT ** pp, size_t st, size_t en, char * name, char * from)
+{
+ size_t mid;
+ int r;
+
+ if (st == en)
+ errx(1, "%s: no entry for %s", from, name);
+
+ mid = (st + en) / 2;
+ r = portcompare(pp[mid]->portdir, name);
+
+ if (r == 0) {
+ free(name);
+ return pp[mid];
+ } else if (r < 0)
+ return findport(pp, mid + 1, en, name, from);
+ else
+ return findport(pp, st, mid, name, from);
+}
+
+/* Translate all depends from names into PORT *s */
+static void
+translateport(PORT ** pp, size_t pplen, PORT * p)
+{
+ size_t i;
+
+ for (i = 0; i < p->n_edep; i++)
+ p->edep[i].p = findport(pp, 0, pplen, p->edep[i].name, p->portdir);
+ for (i = 0; i < p->n_pdep; i++)
+ p->pdep[i].p = findport(pp, 0, pplen, p->pdep[i].name, p->portdir);
+ for (i = 0; i < p->n_fdep; i++)
+ p->fdep[i].p = findport(pp, 0, pplen, p->fdep[i].name, p->portdir);
+ for (i = 0; i < p->n_bdep; i++)
+ p->bdep[i].p = findport(pp, 0, pplen, p->bdep[i].name, p->portdir);
+ for (i = 0; i < p->n_rdep; i++)
+ p->rdep[i].p = findport(pp, 0, pplen, p->rdep[i].name, p->portdir);
+}
+
+/* Recurse on one specific depends list */
+static DEP *
+recurse_one(DEP * d, size_t * nd)
+{
+ size_t i, j, k, n, N;
+
+ N = n = *nd;
+ for (i = 0; i < n; i++) {
+ recurse(d[i].p);
+ for (j = 0; j < d[i].p->n_rdep; j++) {
+ for (k = 0; k < N; k++) {
+ if (d[i].p->rdep[j].p == d[k].p)
+ break;
+ }
+ if (k == N) {
+ N++;
+ if (N >= *nd) {
+ *nd += *nd;
+ d = realloc(d, *nd * sizeof(DEP));
+ if (d == NULL)
+ err(1, "realloc(d)");
+ }
+ d[k].p = d[i].p->rdep[j].p;
+ }
+ }
+ }
+ *nd = N;
+
+ return d;
+}
+
+/* Recurse on the depends lists */
+static void
+recurse(PORT * p)
+{
+ switch (p->recursed) {
+ case 0:
+ /* First time we've seen this port */
+ p->recursed = 1;
+ break;
+ case 1:
+ /* We're in the middle of recursing this port */
+ errx(1, "Circular dependency loop found: %s"
+ " depends upon itself.\n", p->pkgname);
+ case 2:
+ /* This port has already been recursed */
+ return;
+ }
+
+ p->edep = recurse_one(p->edep, &p->n_edep);
+ p->pdep = recurse_one(p->pdep, &p->n_pdep);
+ p->fdep = recurse_one(p->fdep, &p->n_fdep);
+ p->bdep = recurse_one(p->bdep, &p->n_bdep);
+ p->rdep = recurse_one(p->rdep, &p->n_rdep);
+
+ /* Finished recursing on this port */
+ p->recursed = 2;
+}
+
+/* Heapify an element in a package list */
+static void
+heapifypkgs(DEP * d, size_t size, size_t pos)
+{
+ size_t i = pos;
+ PORT * tmp;
+
+top:
+ /* Find the largest value out of {pos, 2*pos+1, 2*pos+2} */
+ if ((2 * pos + 1 < size) &&
+ (strcmp(d[i].p->pkgname, d[2 * pos + 1].p->pkgname) < 0))
+ i = 2 * pos + 1;
+ if ((2 * pos + 2 < size) &&
+ (strcmp(d[i].p->pkgname, d[2 * pos + 2].p->pkgname) < 0))
+ i = 2 * pos + 2;
+
+ /* If necessary, swap elements and iterate down the tree. */
+ if (i != pos) {
+ tmp = d[pos].p;
+ d[pos].p = d[i].p;
+ d[i].p = tmp;
+ pos = i;
+ goto top;
+ }
+}
+
+/* Sort a list of dependent packages in alphabetical order */
+static void
+sortpkgs(DEP * d, size_t nd)
+{
+ size_t i;
+ PORT * tmp;
+
+ if (nd == 0)
+ return;
+
+ for (i = nd; i > 0; i--)
+ heapifypkgs(d, nd, i - 1); /* Build a heap */
+ for (i = nd - 1; i > 0; i--) {
+ tmp = d[0].p; /* Extract elements */
+ d[0].p = d[i].p;
+ d[i].p = tmp;
+ heapifypkgs(d, i, 0); /* And re-heapify */
+ }
+}
+
+/* Output an index line for the given port. */
+static void
+printport(PORT * p)
+{
+ size_t i;
+
+ sortpkgs(p->edep, p->n_edep);
+ sortpkgs(p->pdep, p->n_pdep);
+ sortpkgs(p->fdep, p->n_fdep);
+ sortpkgs(p->bdep, p->n_bdep);
+ sortpkgs(p->rdep, p->n_rdep);
+
+ printf("%s|%s|%s|%s|%s|%s|%s|",
+ p->pkgname, p->portdir, p->prefix, p->comment, p->pkgdescr,
+ p->maintainer, p->categories);
+ for (i = 0; i < p->n_bdep; i++)
+ printf("%s%s", i ? " " : "", p->bdep[i].p->pkgname);
+ printf("|");
+ for (i = 0; i < p->n_rdep; i++)
+ printf("%s%s", i ? " " : "", p->rdep[i].p->pkgname);
+ printf("|");
+ printf("%s|", p->www);
+ for (i = 0; i < p->n_edep; i++)
+ printf("%s%s", i ? " " : "", p->edep[i].p->pkgname);
+ printf("|");
+ for (i = 0; i < p->n_pdep; i++)
+ printf("%s%s", i ? " " : "", p->pdep[i].p->pkgname);
+ printf("|");
+ for (i = 0; i < p->n_fdep; i++)
+ printf("%s%s", i ? " " : "", p->fdep[i].p->pkgname);
+ printf("\n");
+}
+
+/*
+ * Algorithm:
+ * 1. Suck in all the data, splitting into fields.
+ * 1a. If there are no ports, there is no INDEX.
+ * 2. Sort the ports according to port directory.
+ * 3. Using a binary search, translate each dependency from a
+ * port directory name into a pointer to a port.
+ * 4. Recursively follow dependencies, expanding the lists of
+ * pointers as needed (using realloc).
+ * 5. Iterate through the ports, printing them out (remembering
+ * to list the dependent ports in alphabetical order).
+ */
+
+int
+main(int argc, char *argv[])
+{
+ FILE * f;
+ char * line;
+ size_t linelen;
+ PORT ** pp; /* Array of pointers to PORTs */
+ PORT * tmp;
+ size_t pplen; /* Allocated size of array */
+ size_t i;
+
+ if (argc != 2)
+ usage();
+ if ((f = fopen(argv[1], "r")) == NULL)
+ err(1, "fopen(%s)", argv[1]);
+
+ pplen = 1024;
+ if ((pp = malloc(pplen * sizeof(PORT *))) == NULL)
+ err(1, "malloc(pp)");
+
+ /*
+ * 1. Suck in all the data, splitting into fields.
+ */
+ for(i = 0; (line = fgetln(f, &linelen)) != NULL; i++) {
+ if (line[linelen - 1] != '\n')
+ errx(1, "Unterminated line encountered");
+ line[linelen - 1] = 0;
+
+ /* Enlarge array if needed */
+ if (i >= pplen) {
+ pplen *= 2;
+ if ((pp = realloc(pp, pplen * sizeof(PORT *))) == NULL)
+ err(1, "realloc(pp)");
+ }
+
+ pp[i] = portify(line);
+ }
+ /* Reallocate to the correct size */
+ pplen = i;
+ if ((pp = realloc(pp, pplen * sizeof(PORT *))) == NULL)
+ err(1, "realloc(pp)");
+
+ /* Make sure we actually reached the EOF */
+ if (!feof(f))
+ err(1, "fgetln(%s)", argv[1]);
+ /* Close the describes file */
+ if (fclose(f) != 0)
+ err(1, "fclose(%s)", argv[1]);
+
+ /*
+ * 1a. If there are no ports, there is no INDEX.
+ */
+ if (pplen == 0)
+ return 0;
+
+ /*
+ * 2. Sort the ports according to port directory.
+ */
+ for (i = pplen; i > 0; i--)
+ heapifyports(pp, pplen, i - 1); /* Build a heap */
+ for (i = pplen - 1; i > 0; i--) {
+ tmp = pp[0]; /* Extract elements */
+ pp[0] = pp[i];
+ pp[i] = tmp;
+ heapifyports(pp, i, 0); /* And re-heapify */
+ }
+
+ /*
+ * 3. Using a binary search, translate each dependency from a
+ * port directory name into a pointer to a port.
+ */
+ for (i = 0; i < pplen; i++)
+ translateport(pp, pplen, pp[i]);
+
+ /*
+ * 4. Recursively follow dependencies, expanding the lists of
+ * pointers as needed (using realloc).
+ */
+ for (i = 0; i < pplen; i++)
+ recurse(pp[i]);
+
+ /*
+ * 5. Iterate through the ports, printing them out (remembering
+ * to list the dependent ports in alphabetical order).
+ */
+ for (i = 0; i < pplen; i++)
+ printport(pp[i]);
+
+ return 0;
+}
diff --git a/usr.sbin/portsnap/phttpget/Makefile b/usr.sbin/portsnap/phttpget/Makefile
new file mode 100644
index 0000000..154ff15
--- /dev/null
+++ b/usr.sbin/portsnap/phttpget/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= phttpget
+NO_MAN=
+WARNS?= 6
+
+BINDIR= ${LIBEXECDIR}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/portsnap/phttpget/phttpget.c b/usr.sbin/portsnap/phttpget/phttpget.c
new file mode 100644
index 0000000..e109124
--- /dev/null
+++ b/usr.sbin/portsnap/phttpget/phttpget.c
@@ -0,0 +1,724 @@
+/*-
+ * Copyright 2005 Colin Percival
+ * All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted providing that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <netdb.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+static const char * env_HTTP_PROXY;
+static char * env_HTTP_PROXY_AUTH;
+static const char * env_HTTP_USER_AGENT;
+static char * env_HTTP_TIMEOUT;
+static const char * proxyport;
+static char * proxyauth;
+
+static struct timeval timo = { 15, 0};
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "usage: phttpget server [file ...]\n");
+ exit(EX_USAGE);
+}
+
+/*
+ * Base64 encode a string; the string returned, if non-NULL, is
+ * allocated using malloc() and must be freed by the caller.
+ */
+static char *
+b64enc(const char *ptext)
+{
+ static const char base64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
+ const char *pt;
+ char *ctext, *pc;
+ size_t ptlen, ctlen;
+ uint32_t t;
+ unsigned int j;
+
+ /*
+ * Encoded length is 4 characters per 3-byte block or partial
+ * block of plaintext, plus one byte for the terminating NUL
+ */
+ ptlen = strlen(ptext);
+ if (ptlen > ((SIZE_MAX - 1) / 4) * 3 - 2)
+ return NULL; /* Possible integer overflow */
+ ctlen = 4 * ((ptlen + 2) / 3) + 1;
+ if ((ctext = malloc(ctlen)) == NULL)
+ return NULL;
+ ctext[ctlen - 1] = 0;
+
+ /*
+ * Scan through ptext, reading up to 3 bytes from ptext and
+ * writing 4 bytes to ctext, until we run out of input.
+ */
+ for (pt = ptext, pc = ctext; ptlen; ptlen -= 3, pc += 4) {
+ /* Read 3 bytes */
+ for (t = j = 0; j < 3; j++) {
+ t <<= 8;
+ if (j < ptlen)
+ t += *pt++;
+ }
+
+ /* Write 4 bytes */
+ for (j = 0; j < 4; j++) {
+ if (j <= ptlen + 1)
+ pc[j] = base64[(t >> 18) & 0x3f];
+ else
+ pc[j] = '=';
+ t <<= 6;
+ }
+
+ /* If we're done, exit the loop */
+ if (ptlen <= 3)
+ break;
+ }
+
+ return (ctext);
+}
+
+static void
+readenv(void)
+{
+ char *proxy_auth_userpass, *proxy_auth_userpass64, *p;
+ char *proxy_auth_user = NULL;
+ char *proxy_auth_pass = NULL;
+ long http_timeout;
+
+ env_HTTP_PROXY = getenv("HTTP_PROXY");
+ if (env_HTTP_PROXY == NULL)
+ env_HTTP_PROXY = getenv("http_proxy");
+ if (env_HTTP_PROXY != NULL) {
+ if (strncmp(env_HTTP_PROXY, "http://", 7) == 0)
+ env_HTTP_PROXY += 7;
+ p = strchr(env_HTTP_PROXY, '/');
+ if (p != NULL)
+ *p = 0;
+ p = strchr(env_HTTP_PROXY, ':');
+ if (p != NULL) {
+ *p = 0;
+ proxyport = p + 1;
+ } else
+ proxyport = "3128";
+ }
+
+ env_HTTP_PROXY_AUTH = getenv("HTTP_PROXY_AUTH");
+ if ((env_HTTP_PROXY != NULL) &&
+ (env_HTTP_PROXY_AUTH != NULL) &&
+ (strncasecmp(env_HTTP_PROXY_AUTH, "basic:" , 6) == 0)) {
+ /* Ignore authentication scheme */
+ (void) strsep(&env_HTTP_PROXY_AUTH, ":");
+
+ /* Ignore realm */
+ (void) strsep(&env_HTTP_PROXY_AUTH, ":");
+
+ /* Obtain username and password */
+ proxy_auth_user = strsep(&env_HTTP_PROXY_AUTH, ":");
+ proxy_auth_pass = env_HTTP_PROXY_AUTH;
+ }
+
+ if ((proxy_auth_user != NULL) && (proxy_auth_pass != NULL)) {
+ asprintf(&proxy_auth_userpass, "%s:%s",
+ proxy_auth_user, proxy_auth_pass);
+ if (proxy_auth_userpass == NULL)
+ err(1, "asprintf");
+
+ proxy_auth_userpass64 = b64enc(proxy_auth_userpass);
+ if (proxy_auth_userpass64 == NULL)
+ err(1, "malloc");
+
+ asprintf(&proxyauth, "Proxy-Authorization: Basic %s\r\n",
+ proxy_auth_userpass64);
+ if (proxyauth == NULL)
+ err(1, "asprintf");
+
+ free(proxy_auth_userpass);
+ free(proxy_auth_userpass64);
+ } else
+ proxyauth = NULL;
+
+ env_HTTP_USER_AGENT = getenv("HTTP_USER_AGENT");
+ if (env_HTTP_USER_AGENT == NULL)
+ env_HTTP_USER_AGENT = "phttpget/0.1";
+
+ env_HTTP_TIMEOUT = getenv("HTTP_TIMEOUT");
+ if (env_HTTP_TIMEOUT != NULL) {
+ http_timeout = strtol(env_HTTP_TIMEOUT, &p, 10);
+ if ((*env_HTTP_TIMEOUT == '\0') || (*p != '\0') ||
+ (http_timeout < 0))
+ warnx("HTTP_TIMEOUT (%s) is not a positive integer",
+ env_HTTP_TIMEOUT);
+ else
+ timo.tv_sec = http_timeout;
+ }
+}
+
+static int
+makerequest(char ** buf, char * path, char * server, int connclose)
+{
+ int buflen;
+
+ buflen = asprintf(buf,
+ "GET %s%s/%s HTTP/1.1\r\n"
+ "Host: %s\r\n"
+ "User-Agent: %s\r\n"
+ "%s"
+ "%s"
+ "\r\n",
+ env_HTTP_PROXY ? "http://" : "",
+ env_HTTP_PROXY ? server : "",
+ path, server, env_HTTP_USER_AGENT,
+ proxyauth ? proxyauth : "",
+ connclose ? "Connection: Close\r\n" : "Connection: Keep-Alive\r\n");
+ if (buflen == -1)
+ err(1, "asprintf");
+ return(buflen);
+}
+
+static int
+readln(int sd, char * resbuf, int * resbuflen, int * resbufpos)
+{
+ ssize_t len;
+
+ while (strnstr(resbuf + *resbufpos, "\r\n",
+ *resbuflen - *resbufpos) == NULL) {
+ /* Move buffered data to the start of the buffer */
+ if (*resbufpos != 0) {
+ memmove(resbuf, resbuf + *resbufpos,
+ *resbuflen - *resbufpos);
+ *resbuflen -= *resbufpos;
+ *resbufpos = 0;
+ }
+
+ /* If the buffer is full, complain */
+ if (*resbuflen == BUFSIZ)
+ return -1;
+
+ /* Read more data into the buffer */
+ len = recv(sd, resbuf + *resbuflen, BUFSIZ - *resbuflen, 0);
+ if ((len == 0) ||
+ ((len == -1) && (errno != EINTR)))
+ return -1;
+
+ if (len != -1)
+ *resbuflen += len;
+ }
+
+ return 0;
+}
+
+static int
+copybytes(int sd, int fd, off_t copylen, char * resbuf, int * resbuflen,
+ int * resbufpos)
+{
+ ssize_t len;
+
+ while (copylen) {
+ /* Write data from resbuf to fd */
+ len = *resbuflen - *resbufpos;
+ if (copylen < len)
+ len = copylen;
+ if (len > 0) {
+ if (fd != -1)
+ len = write(fd, resbuf + *resbufpos, len);
+ if (len == -1)
+ err(1, "write");
+ *resbufpos += len;
+ copylen -= len;
+ continue;
+ }
+
+ /* Read more data into buffer */
+ len = recv(sd, resbuf, BUFSIZ, 0);
+ if (len == -1) {
+ if (errno == EINTR)
+ continue;
+ return -1;
+ } else if (len == 0) {
+ return -2;
+ } else {
+ *resbuflen = len;
+ *resbufpos = 0;
+ }
+ }
+
+ return 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct addrinfo hints; /* Hints to getaddrinfo */
+ struct addrinfo *res; /* Pointer to server address being used */
+ struct addrinfo *res0; /* Pointer to server addresses */
+ char * resbuf = NULL; /* Response buffer */
+ int resbufpos = 0; /* Response buffer position */
+ int resbuflen = 0; /* Response buffer length */
+ char * eolp; /* Pointer to "\r\n" within resbuf */
+ char * hln; /* Pointer within header line */
+ char * servername; /* Name of server */
+ char * fname = NULL; /* Name of downloaded file */
+ char * reqbuf = NULL; /* Request buffer */
+ int reqbufpos = 0; /* Request buffer position */
+ int reqbuflen = 0; /* Request buffer length */
+ ssize_t len; /* Length sent or received */
+ int nreq = 0; /* Number of next request to send */
+ int nres = 0; /* Number of next reply to receive */
+ int pipelined = 0; /* != 0 if connection in pipelined mode. */
+ int keepalive; /* != 0 if HTTP/1.0 keep-alive rcvd. */
+ int sd = -1; /* Socket descriptor */
+ int sdflags = 0; /* Flags on the socket sd */
+ int fd = -1; /* Descriptor for download target file */
+ int error; /* Error code */
+ int statuscode; /* HTTP Status code */
+ off_t contentlength; /* Value from Content-Length header */
+ int chunked; /* != if transfer-encoding is chunked */
+ off_t clen; /* Chunk length */
+ int firstreq = 0; /* # of first request for this connection */
+
+ /* Check that the arguments are sensible */
+ if (argc < 2)
+ usage();
+
+ /* Read important environment variables */
+ readenv();
+
+ /* Get server name and adjust arg[cv] to point at file names */
+ servername = argv[1];
+ argv += 2;
+ argc -= 2;
+
+ /* Allocate response buffer */
+ resbuf = malloc(BUFSIZ);
+ if (resbuf == NULL)
+ err(1, "malloc");
+
+ /* Look up server */
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ error = getaddrinfo(env_HTTP_PROXY ? env_HTTP_PROXY : servername,
+ env_HTTP_PROXY ? proxyport : "http", &hints, &res0);
+ if (error)
+ errx(1, "host = %s, port = %s: %s",
+ env_HTTP_PROXY ? env_HTTP_PROXY : servername,
+ env_HTTP_PROXY ? proxyport : "http",
+ gai_strerror(error));
+ if (res0 == NULL)
+ errx(1, "could not look up %s", servername);
+ res = res0;
+
+ /* Do the fetching */
+ while (nres < argc) {
+ /* Make sure we have a connected socket */
+ for (; sd == -1; res = res->ai_next) {
+ /* No addresses left to try :-( */
+ if (res == NULL)
+ errx(1, "Could not connect to %s", servername);
+
+ /* Create a socket... */
+ sd = socket(res->ai_family, res->ai_socktype,
+ res->ai_protocol);
+ if (sd == -1)
+ continue;
+
+ /* ... set 15-second timeouts ... */
+ setsockopt(sd, SOL_SOCKET, SO_SNDTIMEO,
+ (void *)&timo, (socklen_t)sizeof(timo));
+ setsockopt(sd, SOL_SOCKET, SO_RCVTIMEO,
+ (void *)&timo, (socklen_t)sizeof(timo));
+
+ /* ... and connect to the server. */
+ if(connect(sd, res->ai_addr, res->ai_addrlen)) {
+ close(sd);
+ sd = -1;
+ continue;
+ }
+
+ firstreq = nres;
+ }
+
+ /*
+ * If in pipelined HTTP mode, put socket into non-blocking
+ * mode, since we're probably going to want to try to send
+ * several HTTP requests.
+ */
+ if (pipelined) {
+ sdflags = fcntl(sd, F_GETFL);
+ if (fcntl(sd, F_SETFL, sdflags | O_NONBLOCK) == -1)
+ err(1, "fcntl");
+ }
+
+ /* Construct requests and/or send them without blocking */
+ while ((nreq < argc) && ((reqbuf == NULL) || pipelined)) {
+ /* If not in the middle of a request, make one */
+ if (reqbuf == NULL) {
+ reqbuflen = makerequest(&reqbuf, argv[nreq],
+ servername, (nreq == argc - 1));
+ reqbufpos = 0;
+ }
+
+ /* If in pipelined mode, try to send the request */
+ if (pipelined) {
+ while (reqbufpos < reqbuflen) {
+ len = send(sd, reqbuf + reqbufpos,
+ reqbuflen - reqbufpos, 0);
+ if (len == -1)
+ break;
+ reqbufpos += len;
+ }
+ if (reqbufpos < reqbuflen) {
+ if (errno != EAGAIN)
+ goto conndied;
+ break;
+ } else {
+ free(reqbuf);
+ reqbuf = NULL;
+ nreq++;
+ }
+ }
+ }
+
+ /* Put connection back into blocking mode */
+ if (pipelined) {
+ if (fcntl(sd, F_SETFL, sdflags) == -1)
+ err(1, "fcntl");
+ }
+
+ /* Do we need to blocking-send a request? */
+ if (nres == nreq) {
+ while (reqbufpos < reqbuflen) {
+ len = send(sd, reqbuf + reqbufpos,
+ reqbuflen - reqbufpos, 0);
+ if (len == -1)
+ goto conndied;
+ reqbufpos += len;
+ }
+ free(reqbuf);
+ reqbuf = NULL;
+ nreq++;
+ }
+
+ /* Scan through the response processing headers. */
+ statuscode = 0;
+ contentlength = -1;
+ chunked = 0;
+ keepalive = 0;
+ do {
+ /* Get a header line */
+ error = readln(sd, resbuf, &resbuflen, &resbufpos);
+ if (error)
+ goto conndied;
+ hln = resbuf + resbufpos;
+ eolp = strnstr(hln, "\r\n", resbuflen - resbufpos);
+ resbufpos = (eolp - resbuf) + 2;
+ *eolp = '\0';
+
+ /* Make sure it doesn't contain a NUL character */
+ if (strchr(hln, '\0') != eolp)
+ goto conndied;
+
+ if (statuscode == 0) {
+ /* The first line MUST be HTTP/1.x xxx ... */
+ if ((strncmp(hln, "HTTP/1.", 7) != 0) ||
+ ! isdigit(hln[7]))
+ goto conndied;
+
+ /*
+ * If the minor version number isn't zero,
+ * then we can assume that pipelining our
+ * requests is OK -- as long as we don't
+ * see a "Connection: close" line later
+ * and we either have a Content-Length or
+ * Transfer-Encoding: chunked header to
+ * tell us the length.
+ */
+ if (hln[7] != '0')
+ pipelined = 1;
+
+ /* Skip over the minor version number */
+ hln = strchr(hln + 7, ' ');
+ if (hln == NULL)
+ goto conndied;
+ else
+ hln++;
+
+ /* Read the status code */
+ while (isdigit(*hln)) {
+ statuscode = statuscode * 10 +
+ *hln - '0';
+ hln++;
+ }
+
+ if (statuscode < 100 || statuscode > 599)
+ goto conndied;
+
+ /* Ignore the rest of the line */
+ continue;
+ }
+
+ /*
+ * Check for "Connection: close" or
+ * "Connection: Keep-Alive" header
+ */
+ if (strncasecmp(hln, "Connection:", 11) == 0) {
+ hln += 11;
+ if (strcasestr(hln, "close") != NULL)
+ pipelined = 0;
+ if (strcasestr(hln, "Keep-Alive") != NULL)
+ keepalive = 1;
+
+ /* Next header... */
+ continue;
+ }
+
+ /* Check for "Content-Length:" header */
+ if (strncasecmp(hln, "Content-Length:", 15) == 0) {
+ hln += 15;
+ contentlength = 0;
+
+ /* Find the start of the length */
+ while (!isdigit(*hln) && (*hln != '\0'))
+ hln++;
+
+ /* Compute the length */
+ while (isdigit(*hln)) {
+ if (contentlength >= OFF_MAX / 10) {
+ /* Nasty people... */
+ goto conndied;
+ }
+ contentlength = contentlength * 10 +
+ *hln - '0';
+ hln++;
+ }
+
+ /* Next header... */
+ continue;
+ }
+
+ /* Check for "Transfer-Encoding: chunked" header */
+ if (strncasecmp(hln, "Transfer-Encoding:", 18) == 0) {
+ hln += 18;
+ if (strcasestr(hln, "chunked") != NULL)
+ chunked = 1;
+
+ /* Next header... */
+ continue;
+ }
+
+ /* We blithely ignore any other header lines */
+
+ /* No more header lines */
+ if (strlen(hln) == 0) {
+ /*
+ * If the status code was 1xx, then there will
+ * be a real header later. Servers may emit
+ * 1xx header blocks at will, but since we
+ * don't expect one, we should just ignore it.
+ */
+ if (100 <= statuscode && statuscode <= 199) {
+ statuscode = 0;
+ continue;
+ }
+
+ /* End of header; message body follows */
+ break;
+ }
+ } while (1);
+
+ /* No message body for 204 or 304 */
+ if (statuscode == 204 || statuscode == 304) {
+ nres++;
+ continue;
+ }
+
+ /*
+ * There should be a message body coming, but we only want
+ * to send it to a file if the status code is 200
+ */
+ if (statuscode == 200) {
+ /* Generate a file name for the download */
+ fname = strrchr(argv[nres], '/');
+ if (fname == NULL)
+ fname = argv[nres];
+ else
+ fname++;
+ if (strlen(fname) == 0)
+ errx(1, "Cannot obtain file name from %s\n",
+ argv[nres]);
+
+ fd = open(fname, O_CREAT | O_TRUNC | O_WRONLY, 0644);
+ if (fd == -1)
+ errx(1, "open(%s)", fname);
+ };
+
+ /* Read the message and send data to fd if appropriate */
+ if (chunked) {
+ /* Handle a chunked-encoded entity */
+
+ /* Read chunks */
+ do {
+ error = readln(sd, resbuf, &resbuflen,
+ &resbufpos);
+ if (error)
+ goto conndied;
+ hln = resbuf + resbufpos;
+ eolp = strstr(hln, "\r\n");
+ resbufpos = (eolp - resbuf) + 2;
+
+ clen = 0;
+ while (isxdigit(*hln)) {
+ if (clen >= OFF_MAX / 16) {
+ /* Nasty people... */
+ goto conndied;
+ }
+ if (isdigit(*hln))
+ clen = clen * 16 + *hln - '0';
+ else
+ clen = clen * 16 + 10 +
+ tolower(*hln) - 'a';
+ hln++;
+ }
+
+ error = copybytes(sd, fd, clen, resbuf,
+ &resbuflen, &resbufpos);
+ if (error) {
+ goto conndied;
+ }
+ } while (clen != 0);
+
+ /* Read trailer and final CRLF */
+ do {
+ error = readln(sd, resbuf, &resbuflen,
+ &resbufpos);
+ if (error)
+ goto conndied;
+ hln = resbuf + resbufpos;
+ eolp = strstr(hln, "\r\n");
+ resbufpos = (eolp - resbuf) + 2;
+ } while (hln != eolp);
+ } else if (contentlength != -1) {
+ error = copybytes(sd, fd, contentlength, resbuf,
+ &resbuflen, &resbufpos);
+ if (error)
+ goto conndied;
+ } else {
+ /*
+ * Not chunked, and no content length header.
+ * Read everything until the server closes the
+ * socket.
+ */
+ error = copybytes(sd, fd, OFF_MAX, resbuf,
+ &resbuflen, &resbufpos);
+ if (error == -1)
+ goto conndied;
+ pipelined = 0;
+ }
+
+ if (fd != -1) {
+ close(fd);
+ fd = -1;
+ }
+
+ fprintf(stderr, "http://%s/%s: %d ", servername, argv[nres],
+ statuscode);
+ if (statuscode == 200)
+ fprintf(stderr, "OK\n");
+ else if (statuscode < 300)
+ fprintf(stderr, "Successful (ignored)\n");
+ else if (statuscode < 400)
+ fprintf(stderr, "Redirection (ignored)\n");
+ else
+ fprintf(stderr, "Error (ignored)\n");
+
+ /* We've finished this file! */
+ nres++;
+
+ /*
+ * If necessary, clean up this connection so that we
+ * can start a new one.
+ */
+ if (pipelined == 0 && keepalive == 0)
+ goto cleanupconn;
+ continue;
+
+conndied:
+ /*
+ * Something went wrong -- our connection died, the server
+ * sent us garbage, etc. If this happened on the first
+ * request we sent over this connection, give up. Otherwise,
+ * close this connection, open a new one, and reissue the
+ * request.
+ */
+ if (nres == firstreq)
+ errx(1, "Connection failure");
+
+cleanupconn:
+ /*
+ * Clean up our connection and keep on going
+ */
+ shutdown(sd, SHUT_RDWR);
+ close(sd);
+ sd = -1;
+ if (fd != -1) {
+ close(fd);
+ fd = -1;
+ }
+ if (reqbuf != NULL) {
+ free(reqbuf);
+ reqbuf = NULL;
+ }
+ nreq = nres;
+ res = res0;
+ pipelined = 0;
+ resbufpos = resbuflen = 0;
+ continue;
+ }
+
+ free(resbuf);
+ freeaddrinfo(res0);
+
+ return 0;
+}
diff --git a/usr.sbin/portsnap/portsnap/Makefile b/usr.sbin/portsnap/portsnap/Makefile
new file mode 100644
index 0000000..e8f42b8
--- /dev/null
+++ b/usr.sbin/portsnap/portsnap/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+SCRIPTS=portsnap.sh
+MAN= portsnap.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/portsnap/portsnap/portsnap.8 b/usr.sbin/portsnap/portsnap/portsnap.8
new file mode 100644
index 0000000..3bbe333
--- /dev/null
+++ b/usr.sbin/portsnap/portsnap/portsnap.8
@@ -0,0 +1,245 @@
+.\"-
+.\" Copyright 2004-2005 Colin Percival
+.\" All rights reserved
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted providing that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+.\" DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+.\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd September 15, 2008
+.Dt PORTSNAP 8
+.Os FreeBSD
+.Sh NAME
+.Nm portsnap
+.Nd fetch and extract compressed snapshots of the ports tree
+.Sh SYNOPSIS
+.Nm
+.Op Fl I
+.Op Fl d Ar workdir
+.Op Fl f Ar conffile
+.Op Fl k Ar KEY
+.Op Fl l Ar descfile
+.Op Fl p Ar portsdir
+.Op Fl s Ar server
+.Cm command ...
+.Op Ar path
+.Sh DESCRIPTION
+The
+.Nm
+tool is used to fetch and update compressed snapshots
+of the
+.Fx
+ports tree, and extract and update an
+uncompressed ports tree.
+.Pp
+In a normal update operation,
+.Nm
+will routinely restore modified files to their unmodified state and
+delete unrecognized local files.
+This behavior is different to
+.Xr cvs 1
+and
+.Xr cvsup 1 .
+.Sh OPTIONS
+The following options are supported:
+.Bl -tag -width "-f conffile"
+.It Fl d Ar workdir
+Store working files (e.g.\& downloaded updates) in
+.Ar workdir .
+(default:
+.Pa /var/db/portsnap ,
+or as given in the configuration file.)
+.It Fl f Ar conffile
+Read the configuration from
+.Ar conffile .
+(default:
+.Pa /etc/portsnap.conf )
+.It Fl I
+For the
+.Cm update
+command, update INDEX files, but not the rest of the ports tree.
+.It Fl k Ar KEY
+Expect a public key with given SHA256 hash.
+(default: read value from configuration file.)
+.It Fl l Ar descfile
+Merge the specified local describes file into the INDEX files being
+built.
+The
+.Ar descfile
+should be generated by running
+.Cm make describe
+in each of the local port directories.
+.It Fl p Ar portsdir
+When extracting or updating an uncompressed snapshot,
+operate on the directory
+.Ar portsdir .
+(default:
+.Pa /usr/ports/ ,
+or as given in the configuration file.)
+.It Fl s Ar server
+Fetch files from the specified server or server pool.
+(default: portsnap.FreeBSD.org , or as given in the
+configuration file.)
+.It path
+For
+.Cm extract
+command only, operate only on parts of the ports tree starting with
+.Ar path .
+(e.g.\&
+.Nm
+.Cm extract
+.Ar sysutils/port
+would extract sysutils/portsman, sysutils/portsnap,
+sysutils/portupgrade, etc.)
+.El
+.Sh COMMANDS
+The
+.Cm command
+can be any one of the following:
+.Pp
+.Bl -tag -width "-f conffile"
+.It fetch
+Fetch a compressed snapshot of the ports tree, or update
+the existing snapshot.
+This command should only be used interactively; for
+non-interactive use, you should use the
+.Cm cron
+command.
+.It cron
+Sleep a random amount of time between 1 and 3600 seconds,
+then operate as if the
+.Cm fetch
+command was specified.
+As the name suggests, this command is designed for running
+from
+.Xr cron 8 ;
+the random delay serves to minimize the probability that
+a large number of machines will simultaneously attempt to
+fetch updates.
+.It extract
+Extract a ports tree, replacing existing files and directories.
+NOTE: This will remove anything occupying the location where
+files or directories are being extracted; in particular, any
+changes made locally to the ports tree (for example, adding new
+patches) will be silently obliterated.
+.Pp
+Only run this command to initialize your portsnap-maintained
+ports tree for the first time, if you wish to start over with
+a clean, completely unmodified tree, or if you wish to extract
+a specific part of the tree (using the
+.Ar path
+option).
+.It update
+Update a ports tree extracted using the
+.Cm extract
+command.
+You must run this command to apply changes to your ports tree
+after downloading updates via the
+.Cm fetch
+or
+.Cm cron
+commands.
+Again, note that in the parts of the ports tree which are being
+updated, any local changes or additions will be removed.
+.El
+.Sh TIPS
+.Bl -bullet
+.It
+If your clock is set to local time, adding the line
+.Pp
+.Dl 0 3 * * * root /usr/sbin/portsnap cron
+.Pp
+to /etc/crontab is a good way to make sure you always have
+an up-to-date snapshot of the ports tree available which
+can quickly be extracted into
+.Pa /usr/ports .
+If your clock is set to UTC, please pick a random time other
+than 3AM, to avoid overly imposing an uneven load on the
+server(s) hosting the snapshots.
+.It
+Running
+.Nm
+.Cm update
+from
+.Xr cron 8
+is a bad idea -- if you are ever installing or updating a
+port at the time the cron job runs, you will probably end up
+in a mess when
+.Nm
+updates or removes files which are being used by the port
+build.
+However, running
+.Nm
+.Fl I
+.Cm update
+is probably safe, and can be used together with
+.Xr portversion 1
+to identify installed software which is out of date.
+.It
+If you wish to use
+.Nm
+to keep a large number of machines up to date, you may wish
+to set up a caching HTTP proxy.
+Since
+.Nm
+uses
+.Xr fetch 1
+to download updates, setting the
+.Ev HTTP_PROXY
+environment variable will direct it to fetch updates from
+the given proxy.
+This is much more efficient than
+.Em mirroring
+the files on the portsnap server, since the vast majority
+of files are not needed by any particular client.
+.El
+.Sh PRIVACY NOTICE
+As an unavoidable part of its operation, a machine running
+.Nm
+will make its public IP address and the list of files it fetches
+available to the server from which it fetches updates.
+Using these it may be possible to recognize a machine over an extended
+period of time, determine when it is updated, and identify which
+portions of the FreeBSD ports tree, if any, are being ignored using
+"REFUSE" directives in
+.Pa portsnap.conf .
+In addition, the FreeBSD release level is transmitted to the server.
+.Pp
+Statistical data generated from information collected in this manner
+may be published, but only in aggregate and after anonymizing the
+individual systems.
+.Sh FILES
+.Bl -tag -width "/etc/portsnap.conf"
+.It /etc/portsnap.conf
+Default location of the portsnap configuration file.
+.It /var/db/portsnap
+Default location where compressed snapshots are stored.
+.It /usr/ports
+Default location where the ports tree is extracted.
+.El
+.Sh SEE ALSO
+.Xr fetch 1 ,
+.Xr sha256 1 ,
+.Xr fetch 3 ,
+.Xr portsnap.conf 5
+.Sh AUTHORS
+.An Colin Percival Aq cperciva@FreeBSD.org
diff --git a/usr.sbin/portsnap/portsnap/portsnap.sh b/usr.sbin/portsnap/portsnap/portsnap.sh
new file mode 100644
index 0000000..0660b41
--- /dev/null
+++ b/usr.sbin/portsnap/portsnap/portsnap.sh
@@ -0,0 +1,1054 @@
+#!/bin/sh
+
+#-
+# Copyright 2004-2005 Colin Percival
+# All rights reserved
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted providing that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+
+# $FreeBSD$
+
+#### Usage function -- called from command-line handling code.
+
+# Usage instructions. Options not listed:
+# --debug -- don't filter output from utilities
+# --no-stats -- don't show progress statistics while fetching files
+usage() {
+ cat <<EOF
+usage: `basename $0` [options] command ... [path]
+
+Options:
+ -d workdir -- Store working files in workdir
+ (default: /var/db/portsnap/)
+ -f conffile -- Read configuration options from conffile
+ (default: /etc/portsnap.conf)
+ -I -- Update INDEX only. (update command only)
+ -k KEY -- Trust an RSA key with SHA256 hash of KEY
+ -l descfile -- Merge the specified local describes file into the INDEX.
+ -p portsdir -- Location of uncompressed ports tree
+ (default: /usr/ports/)
+ -s server -- Server from which to fetch updates.
+ (default: portsnap.FreeBSD.org)
+ path -- Extract only parts of the tree starting with the given
+ string. (extract command only)
+Commands:
+ fetch -- Fetch a compressed snapshot of the ports tree,
+ or update an existing snapshot.
+ cron -- Sleep rand(3600) seconds, and then fetch updates.
+ extract -- Extract snapshot of ports tree, replacing existing
+ files and directories.
+ update -- Update ports tree to match current snapshot, replacing
+ files and directories which have changed.
+EOF
+ exit 0
+}
+
+#### Parameter handling functions.
+
+# Initialize parameters to null, just in case they're
+# set in the environment.
+init_params() {
+ KEYPRINT=""
+ EXTRACTPATH=""
+ WORKDIR=""
+ PORTSDIR=""
+ CONFFILE=""
+ COMMAND=""
+ COMMANDS=""
+ QUIETREDIR=""
+ QUIETFLAG=""
+ STATSREDIR=""
+ XARGST=""
+ NDEBUG=""
+ DDSTATS=""
+ INDEXONLY=""
+ SERVERNAME=""
+ REFUSE=""
+ LOCALDESC=""
+}
+
+# Parse the command line
+parse_cmdline() {
+ while [ $# -gt 0 ]; do
+ case "$1" in
+ -d)
+ if [ $# -eq 1 ]; then usage; fi
+ if [ ! -z "${WORKDIR}" ]; then usage; fi
+ shift; WORKDIR="$1"
+ ;;
+ --debug)
+ QUIETREDIR="/dev/stderr"
+ STATSREDIR="/dev/stderr"
+ QUIETFLAG=" "
+ NDEBUG=" "
+ XARGST="-t"
+ DDSTATS=".."
+ ;;
+ -f)
+ if [ $# -eq 1 ]; then usage; fi
+ if [ ! -z "${CONFFILE}" ]; then usage; fi
+ shift; CONFFILE="$1"
+ ;;
+ -h | --help | help)
+ usage
+ ;;
+ -I)
+ INDEXONLY="YES"
+ ;;
+ -k)
+ if [ $# -eq 1 ]; then usage; fi
+ if [ ! -z "${KEYPRINT}" ]; then usage; fi
+ shift; KEYPRINT="$1"
+ ;;
+ -l)
+ if [ $# -eq 1 ]; then usage; fi
+ if [ ! -z "${LOCALDESC}" ]; then usage; fi
+ shift; LOCALDESC="$1"
+ ;;
+ --no-stats)
+ if [ -z "${STATSREDIR}" ]; then
+ STATSREDIR="/dev/null"
+ DDSTATS=".. "
+ fi
+ ;;
+ -p)
+ if [ $# -eq 1 ]; then usage; fi
+ if [ ! -z "${PORTSDIR}" ]; then usage; fi
+ shift; PORTSDIR="$1"
+ ;;
+ -s)
+ if [ $# -eq 1 ]; then usage; fi
+ if [ ! -z "${SERVERNAME}" ]; then usage; fi
+ shift; SERVERNAME="$1"
+ ;;
+ cron | extract | fetch | update)
+ COMMANDS="${COMMANDS} $1"
+ ;;
+ *)
+ if [ $# -gt 1 ]; then usage; fi
+ if echo ${COMMANDS} | grep -vq extract; then
+ usage
+ fi
+ EXTRACTPATH="$1"
+ ;;
+ esac
+ shift
+ done
+
+ if [ -z "${COMMANDS}" ]; then
+ usage
+ fi
+}
+
+# If CONFFILE was specified at the command-line, make
+# sure that it exists and is readable.
+sanity_conffile() {
+ if [ ! -z "${CONFFILE}" ] && [ ! -r "${CONFFILE}" ]; then
+ echo -n "File does not exist "
+ echo -n "or is not readable: "
+ echo ${CONFFILE}
+ exit 1
+ fi
+}
+
+# If a configuration file hasn't been specified, use
+# the default value (/etc/portsnap.conf)
+default_conffile() {
+ if [ -z "${CONFFILE}" ]; then
+ CONFFILE="/etc/portsnap.conf"
+ fi
+}
+
+# Read {KEYPRINT, SERVERNAME, WORKDIR, PORTSDIR} from the configuration
+# file if they haven't already been set. If the configuration
+# file doesn't exist, do nothing.
+# Also read REFUSE (which cannot be set via the command line) if it is
+# present in the configuration file.
+parse_conffile() {
+ if [ -r "${CONFFILE}" ]; then
+ for X in KEYPRINT WORKDIR PORTSDIR SERVERNAME; do
+ eval _=\$${X}
+ if [ -z "${_}" ]; then
+ eval ${X}=`grep "^${X}=" "${CONFFILE}" |
+ cut -f 2- -d '=' | tail -1`
+ fi
+ done
+
+ if grep -qE "^REFUSE[[:space:]]" ${CONFFILE}; then
+ REFUSE="^(`
+ grep -E "^REFUSE[[:space:]]" "${CONFFILE}" |
+ cut -c 7- | xargs echo | tr ' ' '|'
+ `)"
+ fi
+
+ if grep -qE "^INDEX[[:space:]]" ${CONFFILE}; then
+ INDEXPAIRS="`
+ grep -E "^INDEX[[:space:]]" "${CONFFILE}" |
+ cut -c 7- | tr ' ' '|' | xargs echo`"
+ fi
+ fi
+}
+
+# If parameters have not been set, use default values
+default_params() {
+ _QUIETREDIR="/dev/null"
+ _QUIETFLAG="-q"
+ _STATSREDIR="/dev/stdout"
+ _WORKDIR="/var/db/portsnap"
+ _PORTSDIR="/usr/ports"
+ _NDEBUG="-n"
+ _LOCALDESC="/dev/null"
+ for X in QUIETREDIR QUIETFLAG STATSREDIR WORKDIR PORTSDIR \
+ NDEBUG LOCALDESC; do
+ eval _=\$${X}
+ eval __=\$_${X}
+ if [ -z "${_}" ]; then
+ eval ${X}=${__}
+ fi
+ done
+}
+
+# Perform sanity checks and set some final parameters
+# in preparation for fetching files. Also chdir into
+# the working directory.
+fetch_check_params() {
+ export HTTP_USER_AGENT="portsnap (${COMMAND}, `uname -r`)"
+
+ _SERVERNAME_z=\
+"SERVERNAME must be given via command line or configuration file."
+ _KEYPRINT_z="Key must be given via -k option or configuration file."
+ _KEYPRINT_bad="Invalid key fingerprint: "
+ _WORKDIR_bad="Directory does not exist or is not writable: "
+
+ if [ -z "${SERVERNAME}" ]; then
+ echo -n "`basename $0`: "
+ echo "${_SERVERNAME_z}"
+ exit 1
+ fi
+ if [ -z "${KEYPRINT}" ]; then
+ echo -n "`basename $0`: "
+ echo "${_KEYPRINT_z}"
+ exit 1
+ fi
+ if ! echo "${KEYPRINT}" | grep -qE "^[0-9a-f]{64}$"; then
+ echo -n "`basename $0`: "
+ echo -n "${_KEYPRINT_bad}"
+ echo ${KEYPRINT}
+ exit 1
+ fi
+ if ! [ -d "${WORKDIR}" -a -w "${WORKDIR}" ]; then
+ echo -n "`basename $0`: "
+ echo -n "${_WORKDIR_bad}"
+ echo ${WORKDIR}
+ exit 1
+ fi
+ cd ${WORKDIR} || exit 1
+
+ BSPATCH=/usr/bin/bspatch
+ SHA256=/sbin/sha256
+ PHTTPGET=/usr/libexec/phttpget
+}
+
+# Perform sanity checks and set some final parameters
+# in preparation for extracting or updating ${PORTSDIR}
+# Complain if ${PORTSDIR} exists but is not writable,
+# but don't complain if ${PORTSDIR} doesn't exist.
+extract_check_params() {
+ _WORKDIR_bad="Directory does not exist: "
+ _PORTSDIR_bad="Directory is not writable: "
+
+ if ! [ -d "${WORKDIR}" ]; then
+ echo -n "`basename $0`: "
+ echo -n "${_WORKDIR_bad}"
+ echo ${WORKDIR}
+ exit 1
+ fi
+ if [ -d "${PORTSDIR}" ] && ! [ -w "${PORTSDIR}" ]; then
+ echo -n "`basename $0`: "
+ echo -n "${_PORTSDIR_bad}"
+ echo ${PORTSDIR}
+ exit 1
+ fi
+
+ if ! [ -d "${WORKDIR}/files" -a -r "${WORKDIR}/tag" \
+ -a -r "${WORKDIR}/INDEX" -a -r "${WORKDIR}/tINDEX" ]; then
+ echo "No snapshot available. Try running"
+ echo "# `basename $0` fetch"
+ exit 1
+ fi
+
+ MKINDEX=/usr/libexec/make_index
+}
+
+# Perform sanity checks and set some final parameters
+# in preparation for updating ${PORTSDIR}
+update_check_params() {
+ extract_check_params
+
+ if ! [ -r ${PORTSDIR}/.portsnap.INDEX ]; then
+ echo "${PORTSDIR} was not created by portsnap."
+ echo -n "You must run '`basename $0` extract' before "
+ echo "running '`basename $0` update'."
+ exit 1
+ fi
+
+}
+
+#### Core functionality -- the actual work gets done here
+
+# Use an SRV query to pick a server. If the SRV query doesn't provide
+# a useful answer, use the server name specified by the user.
+# Put another way... look up _http._tcp.${SERVERNAME} and pick a server
+# from that; or if no servers are returned, use ${SERVERNAME}.
+# This allows a user to specify "portsnap.freebsd.org" (in which case
+# portsnap will select one of the mirrors) or "portsnap5.tld.freebsd.org"
+# (in which case portsnap will use that particular server, since there
+# won't be an SRV entry for that name).
+#
+# We ignore the Port field, since we are always going to use port 80.
+
+# Fetch the mirror list, but do not pick a mirror yet. Returns 1 if
+# no mirrors are available for any reason.
+fetch_pick_server_init() {
+ : > serverlist_tried
+
+# Check that host(1) exists (i.e., that the system wasn't built with the
+# WITHOUT_BIND set) and don't try to find a mirror if it doesn't exist.
+ if ! which -s host; then
+ : > serverlist_full
+ return 1
+ fi
+
+ echo -n "Looking up ${SERVERNAME} mirrors... "
+
+# Issue the SRV query and pull out the Priority, Weight, and Target fields.
+# BIND 9 prints "$name has SRV record ..." while BIND 8 prints
+# "$name server selection ..."; we allow either format.
+ MLIST="_http._tcp.${SERVERNAME}"
+ host -t srv "${MLIST}" |
+ sed -nE "s/${MLIST} (has SRV record|server selection) //p" |
+ cut -f 1,2,4 -d ' ' |
+ sed -e 's/\.$//' |
+ sort > serverlist_full
+
+# If no records, give up -- we'll just use the server name we were given.
+ if [ `wc -l < serverlist_full` -eq 0 ]; then
+ echo "none found."
+ return 1
+ fi
+
+# Report how many mirrors we found.
+ echo `wc -l < serverlist_full` "mirrors found."
+
+# Generate a random seed for use in picking mirrors. If HTTP_PROXY
+# is set, this will be used to generate the seed; otherwise, the seed
+# will be random.
+ if [ -n "${HTTP_PROXY}${http_proxy}" ]; then
+ RANDVALUE=`sha256 -qs "${HTTP_PROXY}${http_proxy}" |
+ tr -d 'a-f' |
+ cut -c 1-9`
+ else
+ RANDVALUE=`jot -r 1 0 999999999`
+ fi
+}
+
+# Pick a mirror. Returns 1 if we have run out of mirrors to try.
+fetch_pick_server() {
+# Generate a list of not-yet-tried mirrors
+ sort serverlist_tried |
+ comm -23 serverlist_full - > serverlist
+
+# Have we run out of mirrors?
+ if [ `wc -l < serverlist` -eq 0 ]; then
+ echo "No mirrors remaining, giving up."
+ return 1
+ fi
+
+# Find the highest priority level (lowest numeric value).
+ SRV_PRIORITY=`cut -f 1 -d ' ' serverlist | sort -n | head -1`
+
+# Add up the weights of the response lines at that priority level.
+ SRV_WSUM=0;
+ while read X; do
+ case "$X" in
+ ${SRV_PRIORITY}\ *)
+ SRV_W=`echo $X | cut -f 2 -d ' '`
+ SRV_WSUM=$(($SRV_WSUM + $SRV_W))
+ ;;
+ esac
+ done < serverlist
+
+# If all the weights are 0, pretend that they are all 1 instead.
+ if [ ${SRV_WSUM} -eq 0 ]; then
+ SRV_WSUM=`grep -E "^${SRV_PRIORITY} " serverlist | wc -l`
+ SRV_W_ADD=1
+ else
+ SRV_W_ADD=0
+ fi
+
+# Pick a value between 0 and the sum of the weights - 1
+ SRV_RND=`expr ${RANDVALUE} % ${SRV_WSUM}`
+
+# Read through the list of mirrors and set SERVERNAME. Write the line
+# corresponding to the mirror we selected into serverlist_tried so that
+# we won't try it again.
+ while read X; do
+ case "$X" in
+ ${SRV_PRIORITY}\ *)
+ SRV_W=`echo $X | cut -f 2 -d ' '`
+ SRV_W=$(($SRV_W + $SRV_W_ADD))
+ if [ $SRV_RND -lt $SRV_W ]; then
+ SERVERNAME=`echo $X | cut -f 3 -d ' '`
+ echo "$X" >> serverlist_tried
+ break
+ else
+ SRV_RND=$(($SRV_RND - $SRV_W))
+ fi
+ ;;
+ esac
+ done < serverlist
+}
+
+# Check that we have a public key with an appropriate hash, or
+# fetch the key if it doesn't exist. Returns 1 if the key has
+# not yet been fetched.
+fetch_key() {
+ if [ -r pub.ssl ] && [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
+ return 0
+ fi
+
+ echo -n "Fetching public key from ${SERVERNAME}... "
+ rm -f pub.ssl
+ fetch ${QUIETFLAG} http://${SERVERNAME}/pub.ssl \
+ 2>${QUIETREDIR} || true
+ if ! [ -r pub.ssl ]; then
+ echo "failed."
+ return 1
+ fi
+ if ! [ `${SHA256} -q pub.ssl` = ${KEYPRINT} ]; then
+ echo "key has incorrect hash."
+ rm -f pub.ssl
+ return 1
+ fi
+ echo "done."
+}
+
+# Fetch a snapshot tag
+fetch_tag() {
+ rm -f snapshot.ssl tag.new
+
+ echo ${NDEBUG} "Fetching snapshot tag from ${SERVERNAME}... "
+ fetch ${QUIETFLAG} http://${SERVERNAME}/$1.ssl \
+ 2>${QUIETREDIR} || true
+ if ! [ -r $1.ssl ]; then
+ echo "failed."
+ return 1
+ fi
+
+ openssl rsautl -pubin -inkey pub.ssl -verify \
+ < $1.ssl > tag.new 2>${QUIETREDIR} || true
+ rm $1.ssl
+
+ if ! [ `wc -l < tag.new` = 1 ] ||
+ ! grep -qE "^portsnap\|[0-9]{10}\|[0-9a-f]{64}" tag.new; then
+ echo "invalid snapshot tag."
+ return 1
+ fi
+
+ echo "done."
+
+ SNAPSHOTDATE=`cut -f 2 -d '|' < tag.new`
+ SNAPSHOTHASH=`cut -f 3 -d '|' < tag.new`
+}
+
+# Sanity-check the date on a snapshot tag
+fetch_snapshot_tagsanity() {
+ if [ `date "+%s"` -gt `expr ${SNAPSHOTDATE} + 31536000` ]; then
+ echo "Snapshot appears to be more than a year old!"
+ echo "(Is the system clock correct?)"
+ echo "Cowardly refusing to proceed any further."
+ return 1
+ fi
+ if [ `date "+%s"` -lt `expr ${SNAPSHOTDATE} - 86400` ]; then
+ echo -n "Snapshot appears to have been created more than "
+ echo "one day into the future!"
+ echo "(Is the system clock correct?)"
+ echo "Cowardly refusing to proceed any further."
+ return 1
+ fi
+}
+
+# Sanity-check the date on a snapshot update tag
+fetch_update_tagsanity() {
+ fetch_snapshot_tagsanity || return 1
+
+ if [ ${OLDSNAPSHOTDATE} -gt ${SNAPSHOTDATE} ]; then
+ echo -n "Latest snapshot on server is "
+ echo "older than what we already have!"
+ echo -n "Cowardly refusing to downgrade from "
+ date -r ${OLDSNAPSHOTDATE}
+ echo "to `date -r ${SNAPSHOTDATE}`."
+ return 1
+ fi
+}
+
+# Compare old and new tags; return 1 if update is unnecessary
+fetch_update_neededp() {
+ if [ ${OLDSNAPSHOTDATE} -eq ${SNAPSHOTDATE} ]; then
+ echo -n "Latest snapshot on server matches "
+ echo "what we already have."
+ echo "No updates needed."
+ rm tag.new
+ return 1
+ fi
+ if [ ${OLDSNAPSHOTHASH} = ${SNAPSHOTHASH} ]; then
+ echo -n "Ports tree hasn't changed since "
+ echo "last snapshot."
+ echo "No updates needed."
+ rm tag.new
+ return 1
+ fi
+
+ return 0
+}
+
+# Fetch snapshot metadata file
+fetch_metadata() {
+ rm -f ${SNAPSHOTHASH} tINDEX.new
+
+ echo ${NDEBUG} "Fetching snapshot metadata... "
+ fetch ${QUIETFLAG} http://${SERVERNAME}/t/${SNAPSHOTHASH}
+ 2>${QUIETREDIR} || return
+ if [ `${SHA256} -q ${SNAPSHOTHASH}` != ${SNAPSHOTHASH} ]; then
+ echo "snapshot metadata corrupt."
+ return 1
+ fi
+ mv ${SNAPSHOTHASH} tINDEX.new
+ echo "done."
+}
+
+# Warn user about bogus metadata
+fetch_metadata_freakout() {
+ echo
+ echo "Portsnap metadata is correctly signed, but contains"
+ echo "at least one line which appears bogus."
+ echo "Cowardly refusing to proceed any further."
+}
+
+# Sanity-check a snapshot metadata file
+fetch_metadata_sanity() {
+ if grep -qvE "^[0-9A-Z.]+\|[0-9a-f]{64}$" tINDEX.new; then
+ fetch_metadata_freakout
+ return 1
+ fi
+ if [ `look INDEX tINDEX.new | wc -l` != 1 ]; then
+ echo
+ echo "Portsnap metadata appears bogus."
+ echo "Cowardly refusing to proceed any further."
+ return 1
+ fi
+}
+
+# Take a list of ${oldhash}|${newhash} and output a list of needed patches
+fetch_make_patchlist() {
+ grep -vE "^([0-9a-f]{64})\|\1$" |
+ while read LINE; do
+ X=`echo ${LINE} | cut -f 1 -d '|'`
+ Y=`echo ${LINE} | cut -f 2 -d '|'`
+ if [ -f "files/${Y}.gz" ]; then continue; fi
+ if [ ! -f "files/${X}.gz" ]; then continue; fi
+ echo "${LINE}"
+ done
+}
+
+# Print user-friendly progress statistics
+fetch_progress() {
+ LNC=0
+ while read x; do
+ LNC=$(($LNC + 1))
+ if [ $(($LNC % 10)) = 0 ]; then
+ echo -n $LNC
+ elif [ $(($LNC % 2)) = 0 ]; then
+ echo -n .
+ fi
+ done
+ echo -n " "
+}
+
+# Sanity-check an index file
+fetch_index_sanity() {
+ if grep -qvE "^[-_+./@0-9A-Za-z]+\|[0-9a-f]{64}$" INDEX.new ||
+ fgrep -q "./" INDEX.new; then
+ fetch_metadata_freakout
+ return 1
+ fi
+}
+
+# Verify a list of files
+fetch_snapshot_verify() {
+ while read F; do
+ if [ `gunzip -c snap/${F} | ${SHA256} -q` != ${F} ]; then
+ echo "snapshot corrupt."
+ return 1
+ fi
+ done
+ return 0
+}
+
+# Fetch a snapshot tarball, extract, and verify.
+fetch_snapshot() {
+ while ! fetch_tag snapshot; do
+ fetch_pick_server || return 1
+ done
+ fetch_snapshot_tagsanity || return 1
+ fetch_metadata || return 1
+ fetch_metadata_sanity || return 1
+
+ rm -rf snap/
+
+# Don't ask fetch(1) to be quiet -- downloading a snapshot of ~ 35MB will
+# probably take a while, so the progrees reports that fetch(1) generates
+# will be useful for keeping the users' attention from drifting.
+ echo "Fetching snapshot generated at `date -r ${SNAPSHOTDATE}`:"
+ fetch -r http://${SERVERNAME}/s/${SNAPSHOTHASH}.tgz || return 1
+
+ echo -n "Extracting snapshot... "
+ tar -xzf ${SNAPSHOTHASH}.tgz snap/ || return 1
+ rm ${SNAPSHOTHASH}.tgz
+ echo "done."
+
+ echo -n "Verifying snapshot integrity... "
+# Verify the metadata files
+ cut -f 2 -d '|' tINDEX.new | fetch_snapshot_verify || return 1
+# Extract the index
+ rm -f INDEX.new
+ gunzip -c snap/`look INDEX tINDEX.new |
+ cut -f 2 -d '|'`.gz > INDEX.new
+ fetch_index_sanity || return 1
+# Verify the snapshot contents
+ cut -f 2 -d '|' INDEX.new | fetch_snapshot_verify || return 1
+ echo "done."
+
+# Move files into their proper locations
+ rm -f tag INDEX tINDEX
+ rm -rf files
+ mv tag.new tag
+ mv tINDEX.new tINDEX
+ mv INDEX.new INDEX
+ mv snap/ files/
+
+ return 0
+}
+
+# Update a compressed snapshot
+fetch_update() {
+ rm -f patchlist diff OLD NEW filelist INDEX.new
+
+ OLDSNAPSHOTDATE=`cut -f 2 -d '|' < tag`
+ OLDSNAPSHOTHASH=`cut -f 3 -d '|' < tag`
+
+ while ! fetch_tag latest; do
+ fetch_pick_server || return 1
+ done
+ fetch_update_tagsanity || return 1
+ fetch_update_neededp || return 0
+ fetch_metadata || return 1
+ fetch_metadata_sanity || return 1
+
+ echo -n "Updating from `date -r ${OLDSNAPSHOTDATE}` "
+ echo "to `date -r ${SNAPSHOTDATE}`."
+
+# Generate a list of wanted metadata patches
+ join -t '|' -o 1.2,2.2 tINDEX tINDEX.new |
+ fetch_make_patchlist > patchlist
+
+# Attempt to fetch metadata patches
+ echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
+ echo ${NDEBUG} "metadata patches.${DDSTATS}"
+ tr '|' '-' < patchlist |
+ lam -s "tp/" - -s ".gz" |
+ xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \
+ 2>${STATSREDIR} | fetch_progress
+ echo "done."
+
+# Attempt to apply metadata patches
+ echo -n "Applying metadata patches... "
+ while read LINE; do
+ X=`echo ${LINE} | cut -f 1 -d '|'`
+ Y=`echo ${LINE} | cut -f 2 -d '|'`
+ if [ ! -f "${X}-${Y}.gz" ]; then continue; fi
+ gunzip -c < ${X}-${Y}.gz > diff
+ gunzip -c < files/${X}.gz > OLD
+ cut -c 2- diff | join -t '|' -v 2 - OLD > ptmp
+ grep '^\+' diff | cut -c 2- |
+ sort -k 1,1 -t '|' -m - ptmp > NEW
+ if [ `${SHA256} -q NEW` = ${Y} ]; then
+ mv NEW files/${Y}
+ gzip -n files/${Y}
+ fi
+ rm -f diff OLD NEW ${X}-${Y}.gz ptmp
+ done < patchlist 2>${QUIETREDIR}
+ echo "done."
+
+# Update metadata without patches
+ join -t '|' -v 2 tINDEX tINDEX.new |
+ cut -f 2 -d '|' /dev/stdin patchlist |
+ while read Y; do
+ if [ ! -f "files/${Y}.gz" ]; then
+ echo ${Y};
+ fi
+ done > filelist
+ echo -n "Fetching `wc -l < filelist | tr -d ' '` "
+ echo ${NDEBUG} "metadata files... "
+ lam -s "f/" - -s ".gz" < filelist |
+ xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \
+ 2>${QUIETREDIR}
+
+ while read Y; do
+ if [ `gunzip -c < ${Y}.gz | ${SHA256} -q` = ${Y} ]; then
+ mv ${Y}.gz files/${Y}.gz
+ else
+ echo "metadata is corrupt."
+ return 1
+ fi
+ done < filelist
+ echo "done."
+
+# Extract the index
+ gunzip -c files/`look INDEX tINDEX.new |
+ cut -f 2 -d '|'`.gz > INDEX.new
+ fetch_index_sanity || return 1
+
+# If we have decided to refuse certain updates, construct a hybrid index which
+# is equal to the old index for parts of the tree which we don't want to
+# update, and equal to the new index for parts of the tree which gets updates.
+# This means that we should always have a "complete snapshot" of the ports
+# tree -- with the caveat that it isn't actually a snapshot.
+ if [ ! -z "${REFUSE}" ]; then
+ echo "Refusing to download updates for ${REFUSE}" \
+ >${QUIETREDIR}
+
+ grep -Ev "${REFUSE}" INDEX.new > INDEX.tmp
+ grep -E "${REFUSE}" INDEX |
+ sort -m -k 1,1 -t '|' - INDEX.tmp > INDEX.new
+ rm -f INDEX.tmp
+ fi
+
+# Generate a list of wanted ports patches
+ join -t '|' -o 1.2,2.2 INDEX INDEX.new |
+ fetch_make_patchlist > patchlist
+
+# Attempt to fetch ports patches
+ echo -n "Fetching `wc -l < patchlist | tr -d ' '` "
+ echo ${NDEBUG} "patches.${DDSTATS}"
+ tr '|' '-' < patchlist | lam -s "bp/" - |
+ xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \
+ 2>${STATSREDIR} | fetch_progress
+ echo "done."
+
+# Attempt to apply ports patches
+ echo -n "Applying patches... "
+ while read LINE; do
+ X=`echo ${LINE} | cut -f 1 -d '|'`
+ Y=`echo ${LINE} | cut -f 2 -d '|'`
+ if [ ! -f "${X}-${Y}" ]; then continue; fi
+ gunzip -c < files/${X}.gz > OLD
+ ${BSPATCH} OLD NEW ${X}-${Y}
+ if [ `${SHA256} -q NEW` = ${Y} ]; then
+ mv NEW files/${Y}
+ gzip -n files/${Y}
+ fi
+ rm -f diff OLD NEW ${X}-${Y}
+ done < patchlist 2>${QUIETREDIR}
+ echo "done."
+
+# Update ports without patches
+ join -t '|' -v 2 INDEX INDEX.new |
+ cut -f 2 -d '|' /dev/stdin patchlist |
+ while read Y; do
+ if [ ! -f "files/${Y}.gz" ]; then
+ echo ${Y};
+ fi
+ done > filelist
+ echo -n "Fetching `wc -l < filelist | tr -d ' '` "
+ echo ${NDEBUG} "new ports or files... "
+ lam -s "f/" - -s ".gz" < filelist |
+ xargs ${XARGST} ${PHTTPGET} ${SERVERNAME} \
+ 2>${QUIETREDIR}
+
+ while read Y; do
+ if [ `gunzip -c < ${Y}.gz | ${SHA256} -q` = ${Y} ]; then
+ mv ${Y}.gz files/${Y}.gz
+ else
+ echo "snapshot is corrupt."
+ return 1
+ fi
+ done < filelist
+ echo "done."
+
+# Remove files which are no longer needed
+ cut -f 2 -d '|' tINDEX INDEX | sort > oldfiles
+ cut -f 2 -d '|' tINDEX.new INDEX.new | sort | comm -13 - oldfiles |
+ lam -s "files/" - -s ".gz" | xargs rm -f
+ rm patchlist filelist oldfiles
+
+# We're done!
+ mv INDEX.new INDEX
+ mv tINDEX.new tINDEX
+ mv tag.new tag
+
+ return 0
+}
+
+# Do the actual work involved in "fetch" / "cron".
+fetch_run() {
+ fetch_pick_server_init && fetch_pick_server
+
+ while ! fetch_key; do
+ fetch_pick_server || return 1
+ done
+
+ if ! [ -d files -a -r tag -a -r INDEX -a -r tINDEX ]; then
+ fetch_snapshot || return 1
+ fi
+ fetch_update || return 1
+}
+
+# Build a ports INDEX file
+extract_make_index() {
+ if ! look $1 ${WORKDIR}/tINDEX > /dev/null; then
+ echo -n "$1 not provided by portsnap server; "
+ echo "$2 not being generated."
+ else
+ gunzip -c "${WORKDIR}/files/`look $1 ${WORKDIR}/tINDEX |
+ cut -f 2 -d '|'`.gz" |
+ cat - ${LOCALDESC} |
+ ${MKINDEX} /dev/stdin > ${PORTSDIR}/$2
+ fi
+}
+
+# Create INDEX, INDEX-5, INDEX-6
+extract_indices() {
+ echo -n "Building new INDEX files... "
+ for PAIR in ${INDEXPAIRS}; do
+ INDEXFILE=`echo ${PAIR} | cut -f 1 -d '|'`
+ DESCRIBEFILE=`echo ${PAIR} | cut -f 2 -d '|'`
+ extract_make_index ${DESCRIBEFILE} ${INDEXFILE} || return 1
+ done
+ echo "done."
+}
+
+# Create .portsnap.INDEX; if we are REFUSEing to touch certain directories,
+# merge the values from any exiting .portsnap.INDEX file.
+extract_metadata() {
+ if [ -z "${REFUSE}" ]; then
+ sort ${WORKDIR}/INDEX > ${PORTSDIR}/.portsnap.INDEX
+ elif [ -f ${PORTSDIR}/.portsnap.INDEX ]; then
+ grep -E "${REFUSE}" ${PORTSDIR}/.portsnap.INDEX \
+ > ${PORTSDIR}/.portsnap.INDEX.tmp
+ grep -vE "${REFUSE}" ${WORKDIR}/INDEX | sort |
+ sort -m - ${PORTSDIR}/.portsnap.INDEX.tmp \
+ > ${PORTSDIR}/.portsnap.INDEX
+ rm -f ${PORTSDIR}/.portsnap.INDEX.tmp
+ else
+ grep -vE "${REFUSE}" ${WORKDIR}/INDEX | sort \
+ > ${PORTSDIR}/.portsnap.INDEX
+ fi
+}
+
+# Do the actual work involved in "extract"
+extract_run() {
+ mkdir -p ${PORTSDIR} || return 1
+
+ if !
+ if ! [ -z "${EXTRACTPATH}" ]; then
+ grep "^${EXTRACTPATH}" ${WORKDIR}/INDEX
+ elif ! [ -z "${REFUSE}" ]; then
+ grep -vE "${REFUSE}" ${WORKDIR}/INDEX
+ else
+ cat ${WORKDIR}/INDEX
+ fi | tr '|' ' ' | while read FILE HASH; do
+ echo ${PORTSDIR}/${FILE}
+ if ! [ -r "${WORKDIR}/files/${HASH}.gz" ]; then
+ echo "files/${HASH}.gz not found -- snapshot corrupt."
+ return 1
+ fi
+ case ${FILE} in
+ */)
+ rm -rf ${PORTSDIR}/${FILE%/}
+ mkdir -p ${PORTSDIR}/${FILE}
+ tar -xzf ${WORKDIR}/files/${HASH}.gz \
+ -C ${PORTSDIR}/${FILE}
+ ;;
+ *)
+ rm -f ${PORTSDIR}/${FILE}
+ tar -xzf ${WORKDIR}/files/${HASH}.gz \
+ -C ${PORTSDIR} ${FILE}
+ ;;
+ esac
+ done; then
+ return 1
+ fi
+ if [ ! -z "${EXTRACTPATH}" ]; then
+ return 0;
+ fi
+
+ extract_metadata
+ extract_indices
+}
+
+# Do the actual work involved in "update"
+update_run() {
+ if ! [ -z "${INDEXONLY}" ]; then
+ extract_indices >/dev/null || return 1
+ return 0
+ fi
+
+ if sort ${WORKDIR}/INDEX |
+ cmp -s ${PORTSDIR}/.portsnap.INDEX -; then
+ echo "Ports tree is already up to date."
+ return 0
+ fi
+
+# If we are REFUSEing to touch certain directories, don't remove files
+# from those directories (even if they are out of date)
+ echo -n "Removing old files and directories... "
+ if ! [ -z "${REFUSE}" ]; then
+ sort ${WORKDIR}/INDEX |
+ comm -23 ${PORTSDIR}/.portsnap.INDEX - | cut -f 1 -d '|' |
+ grep -vE "${REFUSE}" |
+ lam -s "${PORTSDIR}/" - |
+ sed -e 's|/$||' | xargs rm -rf
+ else
+ sort ${WORKDIR}/INDEX |
+ comm -23 ${PORTSDIR}/.portsnap.INDEX - | cut -f 1 -d '|' |
+ lam -s "${PORTSDIR}/" - |
+ sed -e 's|/$||' | xargs rm -rf
+ fi
+ echo "done."
+
+# Install new files
+ echo "Extracting new files:"
+ if !
+ if ! [ -z "${REFUSE}" ]; then
+ grep -vE "${REFUSE}" ${WORKDIR}/INDEX | sort
+ else
+ sort ${WORKDIR}/INDEX
+ fi |
+ comm -13 ${PORTSDIR}/.portsnap.INDEX - |
+ while read LINE; do
+ FILE=`echo ${LINE} | cut -f 1 -d '|'`
+ HASH=`echo ${LINE} | cut -f 2 -d '|'`
+ echo ${PORTSDIR}/${FILE}
+ if ! [ -r "${WORKDIR}/files/${HASH}.gz" ]; then
+ echo "files/${HASH}.gz not found -- snapshot corrupt."
+ return 1
+ fi
+ case ${FILE} in
+ */)
+ mkdir -p ${PORTSDIR}/${FILE}
+ tar -xzf ${WORKDIR}/files/${HASH}.gz \
+ -C ${PORTSDIR}/${FILE}
+ ;;
+ *)
+ tar -xzf ${WORKDIR}/files/${HASH}.gz \
+ -C ${PORTSDIR} ${FILE}
+ ;;
+ esac
+ done; then
+ return 1
+ fi
+
+ extract_metadata
+ extract_indices
+}
+
+#### Main functions -- call parameter-handling and core functions
+
+# Using the command line, configuration file, and defaults,
+# set all the parameters which are needed later.
+get_params() {
+ init_params
+ parse_cmdline $@
+ sanity_conffile
+ default_conffile
+ parse_conffile
+ default_params
+}
+
+# Fetch command. Make sure that we're being called
+# interactively, then run fetch_check_params and fetch_run
+cmd_fetch() {
+ if [ ! -t 0 ]; then
+ echo -n "`basename $0` fetch should not "
+ echo "be run non-interactively."
+ echo "Run `basename $0` cron instead."
+ exit 1
+ fi
+ fetch_check_params
+ fetch_run || exit 1
+}
+
+# Cron command. Make sure the parameters are sensible; wait
+# rand(3600) seconds; then fetch updates. While fetching updates,
+# send output to a temporary file; only print that file if the
+# fetching failed.
+cmd_cron() {
+ fetch_check_params
+ sleep `jot -r 1 0 3600`
+
+ TMPFILE=`mktemp /tmp/portsnap.XXXXXX` || exit 1
+ if ! fetch_run >> ${TMPFILE}; then
+ cat ${TMPFILE}
+ rm ${TMPFILE}
+ exit 1
+ fi
+
+ rm ${TMPFILE}
+}
+
+# Extract command. Make sure the parameters are sensible,
+# then extract the ports tree (or part thereof).
+cmd_extract() {
+ extract_check_params
+ extract_run || exit 1
+}
+
+# Update command. Make sure the parameters are sensible,
+# then update the ports tree.
+cmd_update() {
+ update_check_params
+ update_run || exit 1
+}
+
+#### Entry point
+
+# Make sure we find utilities from the base system
+export PATH=/sbin:/bin:/usr/sbin:/usr/bin:${PATH}
+
+# Set LC_ALL in order to avoid problems with character ranges like [A-Z].
+export LC_ALL=C
+
+get_params $@
+for COMMAND in ${COMMANDS}; do
+ cmd_${COMMAND}
+done
diff --git a/usr.sbin/powerd/Makefile b/usr.sbin/powerd/Makefile
new file mode 100644
index 0000000..6adb188
--- /dev/null
+++ b/usr.sbin/powerd/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+PROG= powerd
+MAN= powerd.8
+WARNS?= 6
+
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+
+.if ${MACHINE_ARCH} == "i386"
+CFLAGS+=-DUSE_APM
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/powerd/powerd.8 b/usr.sbin/powerd/powerd.8
new file mode 100644
index 0000000..7465410
--- /dev/null
+++ b/usr.sbin/powerd/powerd.8
@@ -0,0 +1,140 @@
+.\" Copyright (c) 2005 Nate Lawson
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 December 24, 2008
+.Dt POWERD 8
+.Os
+.Sh NAME
+.Nm powerd
+.Nd "system power control utility"
+.Sh SYNOPSIS
+.Nm
+.Op Fl a Ar mode
+.Op Fl b Ar mode
+.Op Fl i Ar percent
+.Op Fl n Ar mode
+.Op Fl p Ar ival
+.Op Fl P Ar pidfile
+.Op Fl r Ar percent
+.Op Fl v
+.Sh DESCRIPTION
+The
+.Nm
+utility monitors the system state and sets various power control options
+accordingly.
+It offers three modes (maximum, minimum, and adaptive) that can be
+individually selected while on AC power or batteries.
+The modes maximum, minimum, adaptive and hiadaptive may be abbreviated
+max, min, adp, hadp.
+.Pp
+Maximum mode chooses the highest performance values.
+Minimum mode selects the lowest performance values to get the most power
+savings.
+Adaptive mode attempts to strike a balance by degrading performance when
+the system appears idle and increasing it when the system is busy.
+It offers a good balance between a small performance loss for greatly
+increased power savings.
+Hiadaptive mode is alike adaptive mode, but tuned for systems where
+performance and interactivity are more important then power consumption.
+It rises frequency faster, drops slower and keeps twice lower CPU load.
+The default mode is adaptive for battery power and hiadaptive for the rest.
+.Pp
+The
+.Nm
+utility recognizes the following runtime options:
+.Bl -tag -width ".Fl r Ar percent"
+.It Fl a Ar mode
+Selects the
+.Ar mode
+to use while on AC power.
+.It Fl b Ar mode
+Selects the
+.Ar mode
+to use while on battery power.
+.It Fl i Ar percent
+Specifies the CPU load percent level when adaptive
+mode should begin to degrade performance to save power.
+The default is 50% or lower.
+.It Fl n Ar mode
+Selects the
+.Ar mode
+to use normally when the AC line state is unknown.
+.It Fl p Ar ival
+Specifies a different polling interval (in milliseconds) for AC line state
+and system idle levels.
+The default is 250 ms.
+.It Fl P Ar pidfile
+Specifies an alternative file in which the process ID should be stored.
+The default is
+.Pa /var/run/powerd.pid .
+.It Fl r Ar percent
+Specifies the CPU load percent level where adaptive
+mode should consider the CPU running and increase performance.
+The default is 75% or higher.
+.It Fl v
+Verbose mode.
+Messages about power changes will be printed to stdout and
+.Nm
+will operate in the foreground.
+.El
+.Sh SEE ALSO
+.Xr acpi 4 ,
+.Xr apm 4 ,
+.Xr cpufreq 4
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 6.0 .
+.Sh AUTHORS
+.An -nosplit
+.An Colin Percival
+first wrote
+.Nm estctrl ,
+the utility that
+.Nm
+is based on.
+.An Nate Lawson
+then updated it for
+.Xr cpufreq 4 ,
+added features, and wrote this manual page.
+.Sh BUGS
+The
+.Nm
+utility should also power down idle disks and other components besides the CPU.
+.Pp
+If
+.Nm
+is used with
+.Pa power_profile ,
+they may override each other.
+.Pp
+The
+.Nm
+utility
+should probably use the
+.Xr devctl 4
+interface instead of polling for AC line state.
diff --git a/usr.sbin/powerd/powerd.c b/usr.sbin/powerd/powerd.c
new file mode 100644
index 0000000..d421232
--- /dev/null
+++ b/usr.sbin/powerd/powerd.c
@@ -0,0 +1,694 @@
+/*-
+ * Copyright (c) 2004 Colin Percival
+ * Copyright (c) 2005 Nate Lawson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted providing that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+#include <sys/resource.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/un.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libutil.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef USE_APM
+#include <machine/apm_bios.h>
+#endif
+
+#define DEFAULT_ACTIVE_PERCENT 75
+#define DEFAULT_IDLE_PERCENT 50
+#define DEFAULT_POLL_INTERVAL 250 /* Poll interval in milliseconds */
+
+typedef enum {
+ MODE_MIN,
+ MODE_ADAPTIVE,
+ MODE_HIADAPTIVE,
+ MODE_MAX,
+} modes_t;
+
+typedef enum {
+ SRC_AC,
+ SRC_BATTERY,
+ SRC_UNKNOWN,
+} power_src_t;
+
+const char *modes[] = {
+ "AC",
+ "battery",
+ "unknown"
+};
+
+#define ACPIAC "hw.acpi.acline"
+#define APMDEV "/dev/apm"
+#define DEVDPIPE "/var/run/devd.pipe"
+#define DEVCTL_MAXBUF 1024
+
+static int read_usage_times(int *load);
+static int read_freqs(int *numfreqs, int **freqs, int **power);
+static int set_freq(int freq);
+static void acline_init(void);
+static void acline_read(void);
+static int devd_init(void);
+static void devd_close(void);
+static void handle_sigs(int sig);
+static void parse_mode(char *arg, int *mode, int ch);
+static void usage(void);
+
+/* Sysctl data structures. */
+static int cp_times_mib[2];
+static int freq_mib[4];
+static int levels_mib[4];
+static int acline_mib[3];
+
+/* Configuration */
+static int cpu_running_mark;
+static int cpu_idle_mark;
+static int poll_ival;
+static int vflag;
+
+static volatile sig_atomic_t exit_requested;
+static power_src_t acline_status;
+static enum {
+ ac_none,
+ ac_acpi_sysctl,
+ ac_acpi_devd,
+#ifdef USE_APM
+ ac_apm,
+#endif
+} acline_mode;
+#ifdef USE_APM
+static int apm_fd = -1;
+#endif
+static int devd_pipe = -1;
+
+#define DEVD_RETRY_INTERVAL 60 /* seconds */
+static struct timeval tried_devd;
+
+static int
+read_usage_times(int *load)
+{
+ static long *cp_times = NULL, *cp_times_old = NULL;
+ static int ncpus = 0;
+ size_t cp_times_len;
+ int error, cpu, i, total;
+
+ if (cp_times == NULL) {
+ cp_times_len = 0;
+ error = sysctl(cp_times_mib, 2, NULL, &cp_times_len, NULL, 0);
+ if (error)
+ return (error);
+ if ((cp_times = malloc(cp_times_len)) == NULL)
+ return (errno);
+ if ((cp_times_old = malloc(cp_times_len)) == NULL) {
+ free(cp_times);
+ cp_times = NULL;
+ return (errno);
+ }
+ ncpus = cp_times_len / (sizeof(long) * CPUSTATES);
+ }
+
+ cp_times_len = sizeof(long) * CPUSTATES * ncpus;
+ error = sysctl(cp_times_mib, 2, cp_times, &cp_times_len, NULL, 0);
+ if (error)
+ return (error);
+
+ if (load) {
+ *load = 0;
+ for (cpu = 0; cpu < ncpus; cpu++) {
+ total = 0;
+ for (i = 0; i < CPUSTATES; i++) {
+ total += cp_times[cpu * CPUSTATES + i] -
+ cp_times_old[cpu * CPUSTATES + i];
+ }
+ if (total == 0)
+ continue;
+ *load += 100 - (cp_times[cpu * CPUSTATES + CP_IDLE] -
+ cp_times_old[cpu * CPUSTATES + CP_IDLE]) * 100 / total;
+ }
+ }
+
+ memcpy(cp_times_old, cp_times, cp_times_len);
+
+ return (0);
+}
+
+static int
+read_freqs(int *numfreqs, int **freqs, int **power)
+{
+ char *freqstr, *p, *q;
+ int i;
+ size_t len = 0;
+
+ if (sysctl(levels_mib, 4, NULL, &len, NULL, 0))
+ return (-1);
+ if ((freqstr = malloc(len)) == NULL)
+ return (-1);
+ if (sysctl(levels_mib, 4, freqstr, &len, NULL, 0))
+ return (-1);
+
+ *numfreqs = 1;
+ for (p = freqstr; *p != '\0'; p++)
+ if (*p == ' ')
+ (*numfreqs)++;
+
+ if ((*freqs = malloc(*numfreqs * sizeof(int))) == NULL) {
+ free(freqstr);
+ return (-1);
+ }
+ if ((*power = malloc(*numfreqs * sizeof(int))) == NULL) {
+ free(freqstr);
+ free(*freqs);
+ return (-1);
+ }
+ for (i = 0, p = freqstr; i < *numfreqs; i++) {
+ q = strchr(p, ' ');
+ if (q != NULL)
+ *q = '\0';
+ if (sscanf(p, "%d/%d", &(*freqs)[i], &(*power)[i]) != 2) {
+ free(freqstr);
+ free(*freqs);
+ free(*power);
+ return (-1);
+ }
+ p = q + 1;
+ }
+
+ free(freqstr);
+ return (0);
+}
+
+static int
+get_freq(void)
+{
+ size_t len;
+ int curfreq;
+
+ len = sizeof(curfreq);
+ if (sysctl(freq_mib, 4, &curfreq, &len, NULL, 0) != 0) {
+ if (vflag)
+ warn("error reading current CPU frequency");
+ curfreq = 0;
+ }
+ return (curfreq);
+}
+
+static int
+set_freq(int freq)
+{
+
+ if (sysctl(freq_mib, 4, NULL, NULL, &freq, sizeof(freq))) {
+ if (errno != EPERM)
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+get_freq_id(int freq, int *freqs, int numfreqs)
+{
+ int i = 1;
+
+ while (i < numfreqs) {
+ if (freqs[i] < freq)
+ break;
+ i++;
+ }
+ return (i - 1);
+}
+
+/*
+ * Try to use ACPI to find the AC line status. If this fails, fall back
+ * to APM. If nothing succeeds, we'll just run in default mode.
+ */
+static void
+acline_init()
+{
+ size_t len;
+
+ len = 3;
+ if (sysctlnametomib(ACPIAC, acline_mib, &len) == 0) {
+ acline_mode = ac_acpi_sysctl;
+ if (vflag)
+ warnx("using sysctl for AC line status");
+#ifdef USE_APM
+ } else if ((apm_fd = open(APMDEV, O_RDONLY)) >= 0) {
+ if (vflag)
+ warnx("using APM for AC line status");
+ acline_mode = ac_apm;
+#endif
+ } else {
+ warnx("unable to determine AC line status");
+ acline_mode = ac_none;
+ }
+}
+
+static void
+acline_read(void)
+{
+ if (acline_mode == ac_acpi_devd) {
+ char buf[DEVCTL_MAXBUF], *ptr;
+ ssize_t rlen;
+ int notify;
+
+ rlen = read(devd_pipe, buf, sizeof(buf));
+ if (rlen == 0 || (rlen < 0 && errno != EWOULDBLOCK)) {
+ if (vflag)
+ warnx("lost devd connection, switching to sysctl");
+ devd_close();
+ acline_mode = ac_acpi_sysctl;
+ /* FALLTHROUGH */
+ }
+ if (rlen > 0 &&
+ (ptr = strstr(buf, "system=ACPI")) != NULL &&
+ (ptr = strstr(ptr, "subsystem=ACAD")) != NULL &&
+ (ptr = strstr(ptr, "notify=")) != NULL &&
+ sscanf(ptr, "notify=%x", &notify) == 1)
+ acline_status = (notify ? SRC_AC : SRC_BATTERY);
+ }
+ if (acline_mode == ac_acpi_sysctl) {
+ int acline;
+ size_t len;
+
+ len = sizeof(acline);
+ if (sysctl(acline_mib, 3, &acline, &len, NULL, 0) == 0)
+ acline_status = (acline ? SRC_AC : SRC_BATTERY);
+ else
+ acline_status = SRC_UNKNOWN;
+ }
+#ifdef USE_APM
+ if (acline_mode == ac_apm) {
+ struct apm_info info;
+
+ if (ioctl(apm_fd, APMIO_GETINFO, &info) == 0) {
+ acline_status = (info.ai_acline ? SRC_AC : SRC_BATTERY);
+ } else {
+ close(apm_fd);
+ apm_fd = -1;
+ acline_mode = ac_none;
+ acline_status = SRC_UNKNOWN;
+ }
+ }
+#endif
+ /* try to (re)connect to devd */
+ if (acline_mode == ac_acpi_sysctl) {
+ struct timeval now;
+
+ gettimeofday(&now, NULL);
+ if (now.tv_sec > tried_devd.tv_sec + DEVD_RETRY_INTERVAL) {
+ if (devd_init() >= 0) {
+ if (vflag)
+ warnx("using devd for AC line status");
+ acline_mode = ac_acpi_devd;
+ }
+ tried_devd = now;
+ }
+ }
+}
+
+static int
+devd_init(void)
+{
+ struct sockaddr_un devd_addr;
+
+ bzero(&devd_addr, sizeof(devd_addr));
+ if ((devd_pipe = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) {
+ if (vflag)
+ warn("%s(): socket()", __func__);
+ return (-1);
+ }
+
+ devd_addr.sun_family = PF_LOCAL;
+ strlcpy(devd_addr.sun_path, DEVDPIPE, sizeof(devd_addr.sun_path));
+ if (connect(devd_pipe, (struct sockaddr *)&devd_addr,
+ sizeof(devd_addr)) == -1) {
+ if (vflag)
+ warn("%s(): connect()", __func__);
+ close(devd_pipe);
+ devd_pipe = -1;
+ return (-1);
+ }
+
+ if (fcntl(devd_pipe, F_SETFL, O_NONBLOCK) == -1) {
+ if (vflag)
+ warn("%s(): fcntl()", __func__);
+ close(devd_pipe);
+ return (-1);
+ }
+
+ return (devd_pipe);
+}
+
+static void
+devd_close(void)
+{
+
+ close(devd_pipe);
+ devd_pipe = -1;
+}
+
+static void
+parse_mode(char *arg, int *mode, int ch)
+{
+
+ if (strcmp(arg, "minimum") == 0 || strcmp(arg, "min") == 0)
+ *mode = MODE_MIN;
+ else if (strcmp(arg, "maximum") == 0 || strcmp(arg, "max") == 0)
+ *mode = MODE_MAX;
+ else if (strcmp(arg, "adaptive") == 0 || strcmp(arg, "adp") == 0)
+ *mode = MODE_ADAPTIVE;
+ else if (strcmp(arg, "hiadaptive") == 0 || strcmp(arg, "hadp") == 0)
+ *mode = MODE_HIADAPTIVE;
+ else
+ errx(1, "bad option: -%c %s", (char)ch, optarg);
+}
+
+static void
+handle_sigs(int __unused sig)
+{
+
+ exit_requested = 1;
+}
+
+static void
+usage(void)
+{
+
+ fprintf(stderr,
+"usage: powerd [-v] [-a mode] [-b mode] [-i %%] [-n mode] [-p ival] [-r %%] [-P pidfile]\n");
+ exit(1);
+}
+
+int
+main(int argc, char * argv[])
+{
+ struct timeval timeout;
+ fd_set fdset;
+ int nfds;
+ struct pidfh *pfh = NULL;
+ const char *pidfile = NULL;
+ int freq, curfreq, initfreq, *freqs, i, j, *mwatts, numfreqs, load;
+ int ch, mode, mode_ac, mode_battery, mode_none;
+ uint64_t mjoules_used;
+ size_t len;
+
+ /* Default mode for all AC states is adaptive. */
+ mode_ac = mode_none = MODE_HIADAPTIVE;
+ mode_battery = MODE_ADAPTIVE;
+ cpu_running_mark = DEFAULT_ACTIVE_PERCENT;
+ cpu_idle_mark = DEFAULT_IDLE_PERCENT;
+ poll_ival = DEFAULT_POLL_INTERVAL;
+ mjoules_used = 0;
+ vflag = 0;
+
+ /* User must be root to control frequencies. */
+ if (geteuid() != 0)
+ errx(1, "must be root to run");
+
+ while ((ch = getopt(argc, argv, "a:b:i:n:p:P:r:v")) != -1)
+ switch (ch) {
+ case 'a':
+ parse_mode(optarg, &mode_ac, ch);
+ break;
+ case 'b':
+ parse_mode(optarg, &mode_battery, ch);
+ break;
+ case 'i':
+ cpu_idle_mark = atoi(optarg);
+ if (cpu_idle_mark < 0 || cpu_idle_mark > 100) {
+ warnx("%d is not a valid percent",
+ cpu_idle_mark);
+ usage();
+ }
+ break;
+ case 'n':
+ parse_mode(optarg, &mode_none, ch);
+ break;
+ case 'p':
+ poll_ival = atoi(optarg);
+ if (poll_ival < 5) {
+ warnx("poll interval is in units of ms");
+ usage();
+ }
+ break;
+ case 'P':
+ pidfile = optarg;
+ break;
+ case 'r':
+ cpu_running_mark = atoi(optarg);
+ if (cpu_running_mark <= 0 || cpu_running_mark > 100) {
+ warnx("%d is not a valid percent",
+ cpu_running_mark);
+ usage();
+ }
+ break;
+ case 'v':
+ vflag = 1;
+ break;
+ default:
+ usage();
+ }
+
+ mode = mode_none;
+
+ /* Poll interval is in units of ms. */
+ poll_ival *= 1000;
+
+ /* Look up various sysctl MIBs. */
+ len = 2;
+ if (sysctlnametomib("kern.cp_times", cp_times_mib, &len))
+ err(1, "lookup kern.cp_times");
+ len = 4;
+ if (sysctlnametomib("dev.cpu.0.freq", freq_mib, &len))
+ err(1, "lookup freq");
+ len = 4;
+ if (sysctlnametomib("dev.cpu.0.freq_levels", levels_mib, &len))
+ err(1, "lookup freq_levels");
+
+ /* Check if we can read the load and supported freqs. */
+ if (read_usage_times(NULL))
+ err(1, "read_usage_times");
+ if (read_freqs(&numfreqs, &freqs, &mwatts))
+ err(1, "error reading supported CPU frequencies");
+
+ /* Run in the background unless in verbose mode. */
+ if (!vflag) {
+ pid_t otherpid;
+
+ pfh = pidfile_open(pidfile, 0600, &otherpid);
+ if (pfh == NULL) {
+ if (errno == EEXIST) {
+ errx(1, "powerd already running, pid: %d",
+ otherpid);
+ }
+ warn("cannot open pid file");
+ }
+ if (daemon(0, 0) != 0) {
+ warn("cannot enter daemon mode, exiting");
+ pidfile_remove(pfh);
+ exit(EXIT_FAILURE);
+
+ }
+ pidfile_write(pfh);
+ }
+
+ /* Decide whether to use ACPI or APM to read the AC line status. */
+ acline_init();
+
+ /*
+ * Exit cleanly on signals.
+ */
+ signal(SIGINT, handle_sigs);
+ signal(SIGTERM, handle_sigs);
+
+ freq = initfreq = get_freq();
+ if (freq < 1)
+ freq = 1;
+ /* Main loop. */
+ for (;;) {
+ FD_ZERO(&fdset);
+ if (devd_pipe >= 0) {
+ FD_SET(devd_pipe, &fdset);
+ nfds = devd_pipe + 1;
+ } else {
+ nfds = 0;
+ }
+ timeout.tv_sec = poll_ival / 1000000;
+ timeout.tv_usec = poll_ival % 1000000;
+ select(nfds, &fdset, NULL, &fdset, &timeout);
+
+ /* If the user requested we quit, print some statistics. */
+ if (exit_requested) {
+ if (vflag && mjoules_used != 0)
+ printf("total joules used: %u.%03u\n",
+ (u_int)(mjoules_used / 1000),
+ (int)mjoules_used % 1000);
+ break;
+ }
+
+ /* Read the current AC status and record the mode. */
+ acline_read();
+ switch (acline_status) {
+ case SRC_AC:
+ mode = mode_ac;
+ break;
+ case SRC_BATTERY:
+ mode = mode_battery;
+ break;
+ case SRC_UNKNOWN:
+ mode = mode_none;
+ break;
+ default:
+ errx(1, "invalid AC line status %d", acline_status);
+ }
+
+ /* Read the current frequency. */
+ if ((curfreq = get_freq()) == 0)
+ continue;
+
+ i = get_freq_id(curfreq, freqs, numfreqs);
+
+ if (vflag) {
+ /* Keep a sum of all power actually used. */
+ if (mwatts[i] != -1)
+ mjoules_used +=
+ (mwatts[i] * (poll_ival / 1000)) / 1000;
+ }
+
+ /* Always switch to the lowest frequency in min mode. */
+ if (mode == MODE_MIN) {
+ freq = freqs[numfreqs - 1];
+ if (curfreq != freq) {
+ if (vflag) {
+ printf("now operating on %s power; "
+ "changing frequency to %d MHz\n",
+ modes[acline_status], freq);
+ }
+ if (set_freq(freq) != 0) {
+ warn("error setting CPU freq %d",
+ freq);
+ continue;
+ }
+ }
+ continue;
+ }
+
+ /* Always switch to the highest frequency in max mode. */
+ if (mode == MODE_MAX) {
+ freq = freqs[0];
+ if (curfreq != freq) {
+ if (vflag) {
+ printf("now operating on %s power; "
+ "changing frequency to %d MHz\n",
+ modes[acline_status], freq);
+ }
+ if (set_freq(freq) != 0) {
+ warn("error setting CPU freq %d",
+ freq);
+ continue;
+ }
+ }
+ continue;
+ }
+
+ /* Adaptive mode; get the current CPU usage times. */
+ if (read_usage_times(&load)) {
+ if (vflag)
+ warn("read_usage_times() failed");
+ continue;
+ }
+
+ if (mode == MODE_ADAPTIVE) {
+ if (load > cpu_running_mark) {
+ if (load > 95 || load > cpu_running_mark * 2)
+ freq *= 2;
+ else
+ freq = freq * load / cpu_running_mark;
+ if (freq > freqs[0])
+ freq = freqs[0];
+ } else if (load < cpu_idle_mark &&
+ curfreq * load < freqs[get_freq_id(
+ freq * 7 / 8, freqs, numfreqs)] *
+ cpu_running_mark) {
+ freq = freq * 7 / 8;
+ if (freq < freqs[numfreqs - 1])
+ freq = freqs[numfreqs - 1];
+ }
+ } else { /* MODE_HIADAPTIVE */
+ if (load > cpu_running_mark / 2) {
+ if (load > 95 || load > cpu_running_mark)
+ freq *= 4;
+ else
+ freq = freq * load * 2 / cpu_running_mark;
+ if (freq > freqs[0] * 2)
+ freq = freqs[0] * 2;
+ } else if (load < cpu_idle_mark / 2 &&
+ curfreq * load < freqs[get_freq_id(
+ freq * 31 / 32, freqs, numfreqs)] *
+ cpu_running_mark / 2) {
+ freq = freq * 31 / 32;
+ if (freq < freqs[numfreqs - 1])
+ freq = freqs[numfreqs - 1];
+ }
+ }
+ if (vflag) {
+ printf("load %3d%%, current freq %4d MHz (%2d), wanted freq %4d MHz\n",
+ load, curfreq, i, freq);
+ }
+ j = get_freq_id(freq, freqs, numfreqs);
+ if (i != j) {
+ if (vflag) {
+ printf("changing clock"
+ " speed from %d MHz to %d MHz\n",
+ freqs[i], freqs[j]);
+ }
+ if (set_freq(freqs[j]))
+ warn("error setting CPU frequency %d",
+ freqs[j]);
+ }
+ }
+ if (set_freq(initfreq))
+ warn("error setting CPU frequency %d", initfreq);
+ free(freqs);
+ free(mwatts);
+ devd_close();
+ if (!vflag)
+ pidfile_remove(pfh);
+
+ exit(0);
+}
diff --git a/usr.sbin/ppp/Makefile b/usr.sbin/ppp/Makefile
new file mode 100644
index 0000000..e8ae0e3
--- /dev/null
+++ b/usr.sbin/ppp/Makefile
@@ -0,0 +1,119 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+PROG= ppp
+MAN= ppp.8
+SRCS= acf.c arp.c async.c auth.c bundle.c cbcp.c ccp.c chap.c chat.c \
+ command.c datalink.c deflate.c defs.c exec.c filter.c fsm.c hdlc.c \
+ iface.c ip.c ipcp.c ipv6cp.c iplist.c lcp.c link.c log.c lqr.c main.c \
+ mbuf.c mp.c ncp.c ncpaddr.c pap.c physical.c pred.c probe.c prompt.c \
+ proto.c route.c server.c sig.c slcompress.c sync.c systems.c tcp.c \
+ tcpmss.c throughput.c timer.c tty.c tun.c udp.c vjcomp.c
+WARNS?= 3
+.if defined(RELEASE_CRUNCH)
+CFLAGS+=-DRELEASE_CRUNCH
+PPP_NO_ATM=
+PPP_NO_DES=
+PPP_NO_KLDLOAD=
+PPP_NO_NAT=
+PPP_NO_PAM=
+PPP_NO_RADIUS=
+PPP_NO_SUID=
+.endif
+
+.if ${MK_ATM} == "no"
+PPP_NO_ATM=
+.endif
+.if ${MK_PAM_SUPPORT} == "no"
+PPP_NO_PAM=
+.endif
+
+.if defined(PPP_NO_SUID)
+BINMODE=550
+.else
+BINMODE=4550
+BINOWN= root
+.endif
+BINGRP= network
+M4FLAGS=
+
+LDADD= -lcrypt -lmd -lutil -lz
+DPADD= ${LIBCRYPT} ${LIBMD} ${LIBUTIL} ${LIBZ}
+
+.SUFFIXES: .8 .8.m4
+
+.8.m4.8:
+ m4 ${M4FLAGS} ${.IMPSRC} >${.TARGET}
+
+CLEANFILES= ppp.8
+
+.if defined(PPP_CONFDIR) && !empty(PPP_CONFDIR)
+CFLAGS+=-DPPP_CONFDIR=\"${PPP_CONFDIR}\"
+.endif
+
+.if defined(PPP_NO_KLDLOAD)
+CFLAGS+=-DNOKLDLOAD
+.endif
+
+.if ${MK_INET6_SUPPORT} == "no"
+CFLAGS+=-DNOINET6
+.endif
+
+.if defined(PPP_NO_NAT)
+CFLAGS+=-DNONAT
+.else
+SRCS+= nat_cmd.c
+LDADD+= -lalias
+DPADD+= ${LIBALIAS}
+.endif
+
+.if defined(PPP_NO_ATM)
+CFLAGS+=-DNOATM
+.else
+SRCS+= atm.c
+.endif
+
+.if defined(PPP_NO_SUID)
+CFLAGS+=-DNOSUID
+.else
+SRCS+= id.c
+.endif
+
+.if defined(RELEASE_CRUNCH) || ${MK_OPENSSL} == "no" || \
+ defined(PPP_NO_DES)
+CFLAGS+=-DNODES
+.else
+SRCS+= chap_ms.c mppe.c
+LDADD+= -lcrypto
+DPADD+= ${LIBCRYPTO}
+.endif
+
+.if defined(PPP_NO_RADIUS)
+CFLAGS+=-DNORADIUS
+.else
+SRCS+= radius.c
+LDADD+= -lradius
+DPADD+= ${LIBRADIUS}
+.endif
+
+.if defined(PPP_NO_NETGRAPH)
+CFLAGS+=-DNONETGRAPH
+.else
+SRCS+= ether.c
+LDADD+= -lnetgraph
+DPADD+= ${LIBNETGRAPH}
+.if defined(EXPERIMENTAL_NETGRAPH)
+CFLAGS+=-DEXPERIMENTAL_NETGRAPH
+SRCS+= netgraph.c
+.endif
+.endif
+
+.if defined(PPP_NO_PAM)
+CFLAGS+=-DNOPAM
+.else
+LDADD+= ${MINUSLPAM}
+DPADD+= ${LIBPAM}
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ppp/README.changes b/usr.sbin/ppp/README.changes
new file mode 100644
index 0000000..bf626e3
--- /dev/null
+++ b/usr.sbin/ppp/README.changes
@@ -0,0 +1,140 @@
+Copyright (c) 2001 Brian Somers <brian@Awfulhak.org>
+ based on work by Eivind Eklund <perhaps@yes.no>,
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must 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$
+
+This file summarises changes made to ppp that effect
+its configuration.
+
+It does not describe new features, rather it attempts
+to answer any `this used to work, why doesn't it now?'
+questions.
+
+o The `set debug' command was replaced with `set log'.
+o The `set log LCP' command was split into LCP, IPCP and CCP logs.
+o Syslogd is used for logging. /etc/syslog.conf must be updated.
+o LQR is disabled by default.
+o Openmode is active by default.
+o Users must be a member of group `network' for ppp access. Furthermore,
+ they must be `allow'ed to run ppp via the `allow' command in the
+ configuration file.
+ For a brief period, ppp could only be run as root.
+o No diagnostic socket is created by default. The `set server' command
+ must be used.
+o The diagnostic socket password must be specified *only* on the `set
+ server' command line.
+o When `set server' is used to re-select a diagnostic port, all existing
+ diagnostic connections are dropped.
+o pppd-deflate is now called deflate24.
+o Filter IPs of 0.0.0.0 have a default width of 0, not 32.
+o Errors in `add' and `delete' are logged as warnings rather than being
+ written to the TCP/IP log.
+o Any number of diagnostic prompts are allowed, and they are allowed in
+ interactive mode.
+o The default `device' is cuad1, then cuad0
+o A password of "*" in ppp.secret causes a passwd database lookup in
+ pap mode.
+o The value of the CONNECT environment variable is logged in the
+ utmp host field in -direct mode.
+o Out-of-sequence FSM packets (IPCP/LCP/CCP) are dropped by default.
+o Reconnect values are used after an LQR timeout.
+o ^C works on the parent in -background mode.
+o The dial/call/open command works asynchronously. As a result, prompts
+ do not lose control while dialing.
+o The `display' command has been removed. All information is available
+ with the appropriate `show' command.
+o Msext does not need to be enabled/disabled. Setting the NBNS (set nbns)
+ will auto enable it. The DNS side may be enabled/disabled, and if
+ enabled without a `set dns' (was `set ns') will use values from
+ /etc/resolv.conf.
+o Filters are now called `allow', `dial', `in' and `out'. `set
+ ifilter ...' becomes `set filter in ...' etc.
+o Authname and Authkey may only be `set' in phase DEAD.
+o Set encrypt is no longer necessary. Ppp will respond to M$CHAP
+ servers correctly if it's built with DES.
+o Throughput statistics are enabled by default.
+o `Set stopped' only has two parameters. It's no longer possible to
+ have an IPCP stopped timer.
+o `Set timeout' only has one or two parameters. Use `set lqrperiod' and
+ `set {lcp,ccp,ipcp,chap,pap}retry' for the other timers. These timeout
+ values can be seen using the relevant show commands.
+o `set loopback' is now `enable/disable loopback'.
+o `show auto', `show loopback' and `show mtu' are all part of `show bundle'.
+o `show mru' is part of `show lcp'
+o `show msext' and `show vj' are part of `show ipcp'
+o `show reconnect' and `show redial' are part of `show link'
+o A signal 15 (TERM) will now shut down the link gracefully.
+o A signal 2 (HUP) will drop all links immediately.
+o Signal 30 (USR1) is now ignored.
+o Add & delete commands are not necessary in ppp.linkup if they are
+ `sticky routes' (ie, contain MYADDR or HISADDR).
+o LINK and CARRIER logging are no longer available.
+o Timer based DEBUG messages are now logged in the new TIMER log.
+o Ppp can use tun devices > tun255.
+o Protocol-compressed packets are accepted even if they were denied
+ at LCP negotiation time.
+o Passwords aren't logged when logging the ``set server'' line.
+o Command line options only need enough characters to uniquely identify
+ them. -a == -auto, -dd == -ddial etc. -interactive is also allowed.
+o If you don't like seeing additional interface aliases when running in
+ -auto -alias mode, add ``iface clear'' to your ppp.linkdown file -
+ check the sample file.
+o Ppp waits for 1 second before checking whether the device supports
+ carrier. This is controllable with ``set cd''.
+o Random dial timeouts are now between 1 and 30 seconds inclusive rather
+ than between 0 and 29.
+o Ppp now accepts M$CHAP (as well as normal CHAP) by default. If this
+ is not required, you must ``deny chap05 chap80''.
+o The ``set device'' command now expects each device to be specified as an
+ argument rather than concatentating all arguments and splitting based
+ on commas and spaces.
+o The ``show modem'' command is deprecated and has been changed to
+ ``show physical''.
+o The words ``host'' and ``port'' are no longer accepted by the ``set filter''
+ command. Removing them should yield the same results as before.
+o The ``set weight'' command has been deprecated. The ``set bandwidth''
+ command should now be used instead.
+o The ``set autoload'' command syntax and implementation have changed as the
+ old implementation was mis-designed and dysfunctional.
+o Ppp now waits either the full ``set cd'' time or until carrier is detected
+ before running the login script (whichever comes first).
+o The -alias flag has been deprecated. The -nat flag should be used instead.
+o Unbalanced quotes in commands are now warned about and the entire command
+ is ignored.
+o It is now only necessary to escape the `-' character in chat scripts twice.
+ See the example files for details.
+o Environment variables and ~ are expanded on in commands
+o ``nat pptp'' is no longer necessary as this is now done transparently
+o The ``!'' at the start of chat scripts and authkey can be made literal
+ (rather than meaning execute) by doubling it to ``!!''.
+o MP autoload throughput measurements are now based on the maximum of input
+ and output averages rather than on the total.
+o When only one link is open in MP mode, MP link level compression is not
+ open and the peer MRU >= the peer MRRU, ppp sends outbound traffic as
+ PROTO_IP traffic rather than PROTO_MP.
+o MSCHAPv2 is now accepted by default. If you don't wish to negotiate
+ this, you must explicitly deny it.
+o MPPE is enabled and accepted by default (although deflate and predictor1
+ are preferred.
diff --git a/usr.sbin/ppp/README.nat b/usr.sbin/ppp/README.nat
new file mode 100644
index 0000000..9ff240a9
--- /dev/null
+++ b/usr.sbin/ppp/README.nat
@@ -0,0 +1,378 @@
+Copyright (c) 2001 Charles Mott <cm@linktel.net>
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must 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$
+
+User PPP NAT (Packet Aliasing)
+
+
+
+0. Contents
+ 1. Background
+ 2. Setup
+ 3. New commands in ppp
+ 4. Future Work
+ 5. Authors / Acknowledgements
+ 6. Revision History for Aliasing Code
+
+
+
+1. Background
+
+User mode ppp has embedded NAT (Network Address Translation) code.
+Enabling this, either by the "-nat" command line option or the
+"nat enable yes" command in a ppp.conf file, makes the ppp host
+automatically NAT IP packets forwarded from a local network, making
+them appear to come from the ppp host machine. Incoming packets
+from the outside world are then appropriately de-NAT'd.
+
+The process of NAT'ing involves both the IP address and the TCP or UDP
+port numbers. ICMP echo and timestamp packets are natted by their id
+numbers. ICMP error messages can be properly directed by examining the
+fragment of the offending packet which is contained in the body of the
+message.
+
+This software was specifically meant to support users who have
+unregistered, private address IP networks (e.g. 192.168.0.x or 10.0.0.x
+addresses). The ppp host can act as a gateway for these networks, and
+computers on the local area net will have some degree of Internet access
+without the need for a registered IP address. Additionally, there will
+be no need for an Internet service provider to maintain routing tables
+for the local area network.
+
+A disadvantage of NAT is that machines on the local network,
+behind the ppp host, are not visible from the outside world. They can
+establish TCP connections and make UDP inquiries (such as domain name
+service requests) but the connections seem to come from the ppp host
+itself. There is, in effect, a partial firewall. Of course, if this is
+what you want, the disadvantage becomes an advantage.
+
+A second disadvantage is that "IP encoding" protocols, which send IP
+address or port information within the data stream, are not supported
+for the cases where exception code exists. This implementation has
+workarounds for FTP and IRC DCC, the most well known of the IP encoding
+protocols. This frees users from depending on using the ftp passive
+mode and avoiding IRC DCC sends, as is sometimes the case with other
+masquerading solutions.
+
+The implementation supports all standard, non-encoding TCP and UDP protocols.
+Examples of these protocols are http, gopher and telnet. The standard UDP
+mode of Real-Audio is not presently supported, but the TCP mode does work
+correctly.
+
+The NAT code also handles many ICMP messages. In particular,
+ping and traceroute are supported.
+
+
+
+2. Packet Aliasing Setup
+
+It is recommended that users first verify correct ppp operation without
+NAT enabled. This will confirm that the ppp.conf file is
+properly set up and that there are no ppp problems. Then start ppp with
+the "-nat" option on the command line. The user should verify that
+the ppp host can correctly connect to the Internet in NAT
+mode. Finally, check that machines on the private network can access
+the Internet.
+
+The NAT software handles all packets, whether they come from
+the host or another computer on the local area network. Thus, a correctly
+operating ppp host indicates that the software should work properly for
+other computers on the private network.
+
+If the ppp host can access the Internet, but other computers on the local
+network cannot, check that IP forwarding is enabled on the ppp host. Also,
+verify that the other computers use this machine as a gateway. Of course,
+you should also verify that machines within the local area network
+communicate properly. A common error is inconsistent subnet addresses
+and masks.
+
+
+
+3. New commands in ppp
+
+In order to control NAT behaviour in a simple manner (no need for
+recompilation), a new command has been added to ppp: nat. This
+is in addition to the -nat command line option. System managers and
+more experienced users may prefer to use the ppp command syntax
+within the ppp.conf file. The nat command also allows NAT
+behaviour to be more precisely specified.
+
+The decision to add a command instead of extending 'set' or 'option' was
+to make obvious that these options only work when NAT is enabled.
+
+The syntax for 'nat' is
+
+ ppp> nat option [yes|no]
+
+where option is given by one of the following templates.
+
+
+ - nat enable [yes|no] (default no)
+
+Enable NAT functionality. If disabled, no other NAT
+options will have any effect. You should usually enable NAT
+before routing any packets over the link; good points are in the
+initial script or right before adding a route. If you do not always
+want NAT, consider using the -nat option to ppp instead of this
+command.
+
+
+ - nat deny_incoming [yes|no] (default yes)
+
+Set to "yes" to disable all incoming connections. This just drops
+connections to, for example, ftp, telnet or web servers. The NAT
+mechanism prevents these connections. Technically, this option denies
+all incoming TCP and UDP requests, making the NAT software a
+fairly efficient one-way firewall. The default is no, which will allow
+all incoming connections to telnetd, ftpd, etc.
+
+
+ - nat log [yes|no]
+
+Controls logging of NAT link creation to "/var/log/alias.log" - this
+is usually only useful if debugging a setup, to see if the bug is in
+the PPP NATing. The debugging information is fairly limited, listing
+the number of NAT links open for different protocols.
+
+
+ - nat same_ports [yes|no] (default yes)
+
+When a connection is being established going through the NAT
+routines, it will normally have its port number changed to allow the
+NAT code to track it. If same_ports is enabled, the NAT
+software attempts to keep the connection's source port unchanged.
+This will allow rsh, RPC and other specialised protocols to work
+_most of the time_, at least on the host machine. Please, do not
+report this being unstable as a bug - it is a result of the way
+NAT has to work. TCP/IP was intended to have one IP address
+per machine.
+
+
+ - nat use_sockets [yes|no] (default yes)
+
+This is a fairly obscure option. For the most part, the NAT
+software does not have to allocate system sockets when it chooses a
+NAT port number. Under very specific circumstances, FTP data
+connections (which don't know the remote port number, though it is
+usually 20) and IRC DCC send (which doesn't know either the address or
+the port from which the connection will come), there can potentially be
+some interference with an open server socket having the same port number
+on the ppp host machine. This possibility for interference only exists
+until the TCP connection has been acknowledged on both sides. The safe
+option is yes, though fewer system resources are consumed by specifying
+no.
+
+
+ - nat unregistered_only [yes|no] (default no)
+
+NAT normally remaps all packets coming from the local area
+network to the ppp host machine address. Set this option to only map
+addresses from the following standard ranges for private, unregistered
+addresses:
+
+ 10.0.0.0 -> 10.255.255.255
+ 172.16.0.0 -> 172.31.255.255
+ 192.168.0.0 -> 192.168.255.255 */
+
+In the instance that there is a subnet of public addresses and another
+subnet of private addresses being routed by the ppp host, then only the
+packets on the private subnet will be NAT'd.
+
+
+- nat port <proto> <local addr>:<port> <nat port>
+
+This command allows incoming traffic to <nat port> on the host
+machine to be redirected to a specific machine and port on the
+local area network. One example of this would be:
+
+ nat port tcp 192.168.0.4:telnet 8066
+
+All traffic to port 8066 of the ppp host would then be sent to
+the telnet port (23) of machine 192.168.0.4. Port numbers
+can either be designated numerically or by symbolic names
+listed in /etc/services. Similarly, addresses can be either
+in dotted quad notation or in /etc/hosts.
+
+
+- nat addr <local addr> <public addr>
+
+This command allows traffic for a public IP address to be
+redirected to a machine on the local network. This function
+is known as "static NAT". An address assignment of 0 refers
+to the default address of the ppp host. Normally static
+NAT is useful if your ISP has allocated a small block of
+IP addresses to the user, but it can even be used in the
+case of a single, dynamically allocated IP address:
+
+ nat addr 10.0.0.8 0
+
+The above command would redirect all incoming traffic to
+machine 10.0.0.8.
+
+If several address NATs specify the same public address
+as follows
+
+ nat addr 192.168.0.2 public_addr
+ nat addr 192.168.0.3 public_addr
+ nat addr 192.168.0.4 public_addr
+
+then incoming traffic will be directed to the last
+translated local address (192.168.0.4), but outgoing
+traffic to the first two addresses will still be NAT'd
+to the specified public address.
+
+
+
+4. Future Work
+
+What is called NAT here has been variously called masquerading, packet
+aliasing and transparent proxying by others. It is an extremely useful
+function to many users, but it is also necessarily imperfect. The
+occasional IP-encoding protocols always need workarounds (hacks).
+Users who are interested in supporting new IP-encoding protocols
+can follow the examples of alias_ftp.c and alias_irc.c.
+
+ICMP error messages are currently handled only in the incoming direction.
+A handler needs to be added to correctly NAT outgoing error messages.
+
+IRC and FTP exception handling make reasonable, though not strictly correct
+assumptions, about how IP encoded messages will appear in the control
+stream. Programmers may wish to consider how to make this process more
+robust.
+
+The NAT engine (alias.c, alias_db.c, alias_ftp.c, alias_irc.c
+and alias_util.c) runs in user space, and is intended to be both portable
+and reusable for interfaces other than ppp. To access the basic engine
+only requires four simple function calls (initialisation, communication of
+host address, outgoing NAT and incoming de-NATing).
+
+
+
+5. Authors / Acknowledgements
+
+Charles Mott (cm@linktel.net) <versions 1.0 - 1.8, 2.0, 2.1>
+Eivind Eklund (perhaps@yes.no) <versions 1.8b - 1.9, new ppp commands>
+
+Listed below, in chronological order, are individuals who have provided
+valuable comments and/or debugging assistance.
+
+ Gary Roberts
+ Tom Torrance
+ Reto Burkhalter
+ Martin Renters
+ Brian Somers
+ Paul Traina
+ Ari Suutari
+ J. Fortes
+ Andrzej Bialeki
+
+
+
+6. Revision History for Aliasing Code
+
+Version 1.0: August 11, 1996 (cjm)
+
+Version 1.1: August 20, 1996 (cjm)
+ PPP host accepts incoming connections for ports 0 to 1023.
+
+Version 1.2: September 7, 1996 (cjm)
+ Fragment handling error in alias_db.c corrected.
+
+Version 1.3: September 15, 1996 (cjm)
+ - Generalised mechanism for handling incoming connections
+ (no more 0 to 1023 restriction).
+ - Increased ICMP support (will handle traceroute now).
+ - Improved TCP close connection logic.
+
+Version 1.4: September 16, 1996
+ Can't remember (this version only lasted a day -- cjm).
+
+Version 1.5: September 17, 1996 (cjm)
+ Corrected error in handling incoming UDP packets
+ with zero checksum.
+
+Version 1.6: September 18, 1996
+ Simplified ICMP data storage. Will now handle
+ tracert from Win95 as well as FreeBSD traceroute.
+
+Version 1.7: January 9, 1997 (cjm)
+ - Reduced malloc() activity for ICMP echo and
+ timestamp requests.
+ - Added handling for out-of-order IP fragments.
+ - Switched to differential checksum computation
+ for IP headers (TCP, UDP and ICMP checksums
+ were already differential).
+ - Accepts FTP data connections from other than
+ port 20. This allows one ftp connections
+ from two hosts which are both running packet
+ aliasing.
+
+Version 1.8: January 14, 1997 (cjm)
+ - Fixed data type error in function StartPoint()
+ in alias_db.c (this bug did not exist before v1.7)
+
+Version 1.8b: January 16, 1997 (Eivind Eklund <perhaps@yes.no>)
+ - Upgraded base PPP version to be the source code from
+ FreeBSD 2.1.6, with additional security patches. This
+ version should still be possible to run on 2.1.5, though -
+ I've run it with a 2.1.5 kernel without problems.
+ (Update done with the permission of cjm)
+
+Version 1.9: February 1, 1997 (Eivind Eklund <perhaps@yes.no>)
+ - Added support for IRC DCC (ee)
+ - Changed the aliasing routines to use ANSI style throughout -
+ minor API changes for integration with other programs than PPP (ee)
+ - Changed the build process, making all options switchable
+ from the Makefile (ee)
+ - Fixed minor security hole in alias_ftp.c for other applications
+ of the aliasing software. Hole could _not_ manifest in
+ PPP+pktAlias, but could potentially manifest in other
+ applications of the aliasing. (ee)
+ - Connections initiated from packet aliasing host machine will
+ not have their port number aliased unless it conflicts with
+ an aliasing port already being used. (There is an option to
+ disable this for debugging) (cjm)
+ - Sockets will be allocated in cases where there might be
+ port interference with the host machine. This can be disabled
+ in cases where the ppp host will be acting purely as a
+ masquerading router and not generate any traffic of its own.
+ (cjm)
+
+Version 2.0: March, 1997 (cjm)
+ - Incoming packets which are not recognised by the packet
+ aliasing engine are now completely dropped in ip.c.
+ - Aliasing links are cleared when a host interface address
+ changes (due to re-dial and dynamic address allocation).
+ - PacketAliasPermanentLink() API added.
+ - Option for only aliasing private, unregistered IP addresses
+ added.
+ - Substantial rework to the aliasing lookup engine.
+
+Version 2.1: May, 1997 (cjm)
+ - Continuing rework to the aliasing lookup engine to support
+ multiple incoming addresses and static NAT.
+ - Now supports outgoing as well as incoming ICMP error messages/
+ - PPP commands to support address and port redirection.
+
diff --git a/usr.sbin/ppp/acf.c b/usr.sbin/ppp/acf.c
new file mode 100644
index 0000000..6c3ec05
--- /dev/null
+++ b/usr.sbin/ppp/acf.c
@@ -0,0 +1,116 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <termios.h>
+
+#include "defs.h"
+#include "layer.h"
+#include "timer.h"
+#include "fsm.h"
+#include "log.h"
+#include "mbuf.h"
+#include "acf.h"
+#include "proto.h"
+#include "throughput.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "descriptor.h"
+#include "async.h"
+#include "physical.h"
+
+int
+acf_WrapperOctets(struct lcp *lcp, u_short proto)
+{
+ return (proto == PROTO_LCP || lcp->his_acfcomp == 0) ? 2 : 0;
+}
+
+static struct mbuf *
+acf_LayerPush(struct bundle *b __unused, struct link *l, struct mbuf *bp,
+ int pri __unused, u_short *proto)
+{
+ const u_char cp[2] = { HDLC_ADDR, HDLC_UI };
+
+ if (*proto == PROTO_LCP || l->lcp.his_acfcomp == 0) {
+ bp = m_prepend(bp, cp, 2, 0);
+ m_settype(bp, MB_ACFOUT);
+ }
+
+ return bp;
+}
+
+static struct mbuf *
+acf_LayerPull(struct bundle *b __unused, struct link *l, struct mbuf *bp,
+ u_short *proto __unused)
+{
+ struct physical *p = link2physical(l);
+ u_char cp[2];
+
+ if (!p) {
+ log_Printf(LogERROR, "Can't Pull an acf packet from a logical link\n");
+ return bp;
+ }
+
+ if (mbuf_View(bp, cp, 2) == 2) {
+ if (!p->link.lcp.want_acfcomp) {
+ /* We expect the packet not to be compressed */
+ bp = mbuf_Read(bp, cp, 2);
+ if (cp[0] != HDLC_ADDR) {
+ p->hdlc.lqm.ifInErrors++;
+ p->hdlc.stats.badaddr++;
+ log_Printf(LogDEBUG, "acf_LayerPull: addr 0x%02x\n", cp[0]);
+ m_freem(bp);
+ return NULL;
+ }
+ if (cp[1] != HDLC_UI) {
+ p->hdlc.lqm.ifInErrors++;
+ p->hdlc.stats.badcommand++;
+ log_Printf(LogDEBUG, "acf_LayerPull: control 0x%02x\n", cp[1]);
+ m_freem(bp);
+ return NULL;
+ }
+ m_settype(bp, MB_ACFIN);
+ } else if (cp[0] == HDLC_ADDR && cp[1] == HDLC_UI) {
+ /*
+ * We can receive compressed packets, but the peer still sends
+ * uncompressed packets (or maybe this is a PROTO_LCP packet) !
+ */
+ bp = mbuf_Read(bp, cp, 2);
+ m_settype(bp, MB_ACFIN);
+ }
+ }
+
+ return bp;
+}
+
+struct layer acflayer = { LAYER_ACF, "acf", acf_LayerPush, acf_LayerPull };
diff --git a/usr.sbin/ppp/acf.h b/usr.sbin/ppp/acf.h
new file mode 100644
index 0000000..e32adbc
--- /dev/null
+++ b/usr.sbin/ppp/acf.h
@@ -0,0 +1,33 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct lcp;
+
+extern int acf_WrapperOctets(struct lcp *, u_short);
+
+extern struct layer acflayer;
diff --git a/usr.sbin/ppp/arp.c b/usr.sbin/ppp/arp.c
new file mode 100644
index 0000000..02dce51
--- /dev/null
+++ b/usr.sbin/ppp/arp.c
@@ -0,0 +1,317 @@
+/*
+ * sys-bsd.c - System-dependent procedures for setting up
+ * PPP interfaces on bsd-4.4-ish systems (including 386BSD, NetBSD, etc.)
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 Carnegie Mellon University. The name of the
+ * University 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+/*
+ * TODO:
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+#include <arpa/inet.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/sysctl.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "mbuf.h"
+#include "log.h"
+#include "id.h"
+#include "timer.h"
+#include "fsm.h"
+#include "defs.h"
+#include "iplist.h"
+#include "throughput.h"
+#include "slcompress.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "ipv6cp.h"
+#include "descriptor.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "mp.h"
+#include "ncp.h"
+#include "filter.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "bundle.h"
+#include "iface.h"
+#include "arp.h"
+
+/*
+ * SET_SA_FAMILY - set the sa_family field of a struct sockaddr,
+ * if it exists.
+ */
+#define SET_SA_FAMILY(addr, family) \
+ memset((char *) &(addr), '\0', sizeof(addr)); \
+ addr.sa_family = (family); \
+ addr.sa_len = sizeof(addr);
+
+
+#if RTM_VERSION >= 3
+
+/*
+ * arp_SetProxy - Make a proxy ARP entry for the peer.
+ */
+static struct {
+ struct rt_msghdr hdr;
+ struct sockaddr_inarp dst;
+ struct sockaddr_dl hwa;
+ char extra[128];
+} arpmsg;
+
+static int
+arp_ProxySub(struct bundle *bundle, struct in_addr addr, int add)
+{
+ int routes;
+
+ /*
+ * Get the hardware address of an interface on the same subnet as our local
+ * address.
+ */
+
+ memset(&arpmsg, 0, sizeof arpmsg);
+ if (!arp_EtherAddr(addr, &arpmsg.hwa, 0)) {
+ log_Printf(LogWARN, "%s: Cannot determine ethernet address for proxy ARP\n",
+ inet_ntoa(addr));
+ return 0;
+ }
+ routes = ID0socket(PF_ROUTE, SOCK_RAW, AF_INET);
+ if (routes < 0) {
+ log_Printf(LogERROR, "arp_SetProxy: opening routing socket: %s\n",
+ strerror(errno));
+ return 0;
+ }
+ arpmsg.hdr.rtm_type = add ? RTM_ADD : RTM_DELETE;
+ arpmsg.hdr.rtm_flags = RTF_ANNOUNCE | RTF_HOST | RTF_STATIC;
+ arpmsg.hdr.rtm_version = RTM_VERSION;
+ arpmsg.hdr.rtm_seq = ++bundle->routing_seq;
+ arpmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY;
+ arpmsg.hdr.rtm_inits = RTV_EXPIRE;
+ arpmsg.dst.sin_len = sizeof(struct sockaddr_inarp);
+ arpmsg.dst.sin_family = AF_INET;
+ arpmsg.dst.sin_addr.s_addr = addr.s_addr;
+ arpmsg.dst.sin_other = SIN_PROXY;
+
+ arpmsg.hdr.rtm_msglen = (char *) &arpmsg.hwa - (char *) &arpmsg
+ + arpmsg.hwa.sdl_len;
+
+
+ if (ID0write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0 &&
+ !(!add && errno == ESRCH)) {
+ log_Printf(LogERROR, "%s proxy arp entry %s: %s\n",
+ add ? "Add" : "Delete", inet_ntoa(addr), strerror(errno));
+ close(routes);
+ return 0;
+ }
+ close(routes);
+ return 1;
+}
+
+int
+arp_SetProxy(struct bundle *bundle, struct in_addr addr)
+{
+ return (arp_ProxySub(bundle, addr, 1));
+}
+
+/*
+ * arp_ClearProxy - Delete the proxy ARP entry for the peer.
+ */
+int
+arp_ClearProxy(struct bundle *bundle, struct in_addr addr)
+{
+ return (arp_ProxySub(bundle, addr, 0));
+}
+
+#else /* RTM_VERSION */
+
+/*
+ * arp_SetProxy - Make a proxy ARP entry for the peer.
+ */
+int
+arp_SetProxy(struct bundle *bundle, struct in_addr addr, int s)
+{
+ struct arpreq arpreq;
+ struct {
+ struct sockaddr_dl sdl;
+ char space[128];
+ } dls;
+
+ memset(&arpreq, '\0', sizeof arpreq);
+
+ /*
+ * Get the hardware address of an interface on the same subnet as our local
+ * address.
+ */
+ if (!arp_EtherAddr(addr, &dls.sdl, 1)) {
+ log_Printf(LOG_PHASE_BIT, "Cannot determine ethernet address for "
+ "proxy ARP\n");
+ return 0;
+ }
+ arpreq.arp_ha.sa_len = sizeof(struct sockaddr);
+ arpreq.arp_ha.sa_family = AF_UNSPEC;
+ memcpy(arpreq.arp_ha.sa_data, LLADDR(&dls.sdl), dls.sdl.sdl_alen);
+ SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
+ ((struct sockaddr_in *)&arpreq.arp_pa)->sin_addr.s_addr = addr.s_addr;
+ arpreq.arp_flags = ATF_PERM | ATF_PUBL;
+ if (ID0ioctl(s, SIOCSARP, (caddr_t) & arpreq) < 0) {
+ log_Printf(LogERROR, "arp_SetProxy: ioctl(SIOCSARP): %s\n",
+ strerror(errno));
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * arp_ClearProxy - Delete the proxy ARP entry for the peer.
+ */
+int
+arp_ClearProxy(struct bundle *bundle, struct in_addr addr, int s)
+{
+ struct arpreq arpreq;
+
+ memset(&arpreq, '\0', sizeof arpreq);
+ SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
+ ((struct sockaddr_in *)&arpreq.arp_pa)->sin_addr.s_addr = addr.s_addr;
+ if (ID0ioctl(s, SIOCDARP, (caddr_t) & arpreq) < 0) {
+ log_Printf(LogERROR, "arp_ClearProxy: ioctl(SIOCDARP): %s\n",
+ strerror(errno));
+ return 0;
+ }
+ return 1;
+}
+
+#endif /* RTM_VERSION */
+
+
+/*
+ * arp_EtherAddr - get the hardware address of an interface on the
+ * the same subnet as ipaddr.
+ */
+
+int
+arp_EtherAddr(struct in_addr ipaddr, struct sockaddr_dl *hwaddr,
+ int verbose)
+{
+ int mib[6], skip;
+ size_t needed;
+ char *buf, *ptr, *end;
+ struct if_msghdr *ifm;
+ struct ifa_msghdr *ifam;
+ struct sockaddr_dl *dl;
+ struct sockaddr *sa[RTAX_MAX];
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = 0;
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0;
+
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
+ log_Printf(LogERROR, "arp_EtherAddr: sysctl: estimate: %s\n",
+ strerror(errno));
+ return 0;
+ }
+
+ if ((buf = malloc(needed)) == NULL)
+ return 0;
+
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
+ free(buf);
+ return 0;
+ }
+ end = buf + needed;
+
+ ptr = buf;
+ while (ptr < end) {
+ ifm = (struct if_msghdr *)ptr; /* On if_msghdr */
+ if (ifm->ifm_type != RTM_IFINFO)
+ break;
+ dl = (struct sockaddr_dl *)(ifm + 1); /* Single _dl at end */
+ skip = (ifm->ifm_flags & (IFF_UP | IFF_BROADCAST | IFF_POINTOPOINT |
+ IFF_NOARP | IFF_LOOPBACK)) != (IFF_UP | IFF_BROADCAST);
+ ptr += ifm->ifm_msglen; /* First ifa_msghdr */
+ while (ptr < end) {
+ ifam = (struct ifa_msghdr *)ptr; /* Next ifa_msghdr (alias) */
+ if (ifam->ifam_type != RTM_NEWADDR) /* finished ? */
+ break;
+ ptr += ifam->ifam_msglen;
+ if (skip || (ifam->ifam_addrs & (RTA_NETMASK|RTA_IFA)) !=
+ (RTA_NETMASK|RTA_IFA))
+ continue;
+ /* Found a candidate. Do the addresses match ? */
+ if (log_IsKept(LogDEBUG) &&
+ ptr == (char *)ifm + ifm->ifm_msglen + ifam->ifam_msglen)
+ log_Printf(LogDEBUG, "%.*s interface is a candidate for proxy\n",
+ dl->sdl_nlen, dl->sdl_data);
+
+ iface_ParseHdr(ifam, sa);
+
+ if (sa[RTAX_IFA]->sa_family == AF_INET) {
+ struct sockaddr_in *ifa, *netmask;
+
+ ifa = (struct sockaddr_in *)sa[RTAX_IFA];
+ netmask = (struct sockaddr_in *)sa[RTAX_NETMASK];
+
+ if (log_IsKept(LogDEBUG)) {
+ char a[16];
+
+ strncpy(a, inet_ntoa(netmask->sin_addr), sizeof a - 1);
+ a[sizeof a - 1] = '\0';
+ log_Printf(LogDEBUG, "Check addr %s, mask %s\n",
+ inet_ntoa(ifa->sin_addr), a);
+ }
+
+ if ((ifa->sin_addr.s_addr & netmask->sin_addr.s_addr) ==
+ (ipaddr.s_addr & netmask->sin_addr.s_addr)) {
+ log_Printf(verbose ? LogPHASE : LogDEBUG,
+ "Found interface %.*s for %s\n", dl->sdl_nlen,
+ dl->sdl_data, inet_ntoa(ipaddr));
+ memcpy(hwaddr, dl, dl->sdl_len);
+ free(buf);
+ return 1;
+ }
+ }
+ }
+ }
+ free(buf);
+
+ return 0;
+}
diff --git a/usr.sbin/ppp/arp.h b/usr.sbin/ppp/arp.h
new file mode 100644
index 0000000..81e3fa6
--- /dev/null
+++ b/usr.sbin/ppp/arp.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+struct sockaddr_dl;
+struct bundle;
+
+extern int arp_ClearProxy(struct bundle *, struct in_addr);
+extern int arp_SetProxy(struct bundle *, struct in_addr);
+extern int arp_EtherAddr(struct in_addr, struct sockaddr_dl *, int);
diff --git a/usr.sbin/ppp/async.c b/usr.sbin/ppp/async.c
new file mode 100644
index 0000000..a2a8e04
--- /dev/null
+++ b/usr.sbin/ppp/async.c
@@ -0,0 +1,220 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <string.h>
+#include <termios.h>
+
+#include "layer.h"
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "proto.h"
+#include "async.h"
+#include "throughput.h"
+#include "ccp.h"
+#include "link.h"
+#include "descriptor.h"
+#include "physical.h"
+
+#define MODE_HUNT 0x01
+#define MODE_ESC 0x02
+
+void
+async_Init(struct async *async)
+{
+ async_Setup(async);
+ memset(async->cfg.EscMap, '\0', sizeof async->cfg.EscMap);
+}
+
+void
+async_Setup(struct async *async)
+{
+ async->mode = MODE_HUNT;
+ async->length = 0;
+ async->my_accmap = async->his_accmap = 0xffffffff;
+}
+
+void
+async_SetLinkParams(struct async *async, u_int32_t mymap, u_int32_t hismap)
+{
+ async->my_accmap = mymap;
+ async->his_accmap = hismap | mymap;
+}
+
+/*
+ * Encode into async HDLC byte code
+ */
+static void
+async_Encode(struct async *async, u_char **cp, u_char c, int proto)
+{
+ u_char *wp;
+
+ wp = *cp;
+ if ((c < 0x20 && (proto == PROTO_LCP || (async->his_accmap & (1 << c))))
+ || (c == HDLC_ESC) || (c == HDLC_SYN)) {
+ *wp++ = HDLC_ESC;
+ c ^= HDLC_XOR;
+ }
+ if (async->cfg.EscMap[32] && async->cfg.EscMap[c >> 3] & (1 << (c & 7))) {
+ *wp++ = HDLC_ESC;
+ c ^= HDLC_XOR;
+ }
+ *wp++ = c;
+ *cp = wp;
+}
+
+static struct mbuf *
+async_LayerPush(struct bundle *b __unused, struct link *l, struct mbuf *bp,
+ int pri __unused, u_short *proto)
+{
+ struct physical *p = link2physical(l);
+ u_char *cp, *sp, *ep;
+ struct mbuf *wp;
+ size_t oldcnt;
+ size_t cnt;
+
+ if (!p || m_length(bp) > HDLCSIZE) {
+ m_freem(bp);
+ return NULL;
+ }
+
+ oldcnt = m_length(bp);
+
+ cp = p->async.xbuff;
+ ep = cp + HDLCSIZE - 10;
+ wp = bp;
+ *cp++ = HDLC_SYN;
+ while (wp) {
+ sp = MBUF_CTOP(wp);
+ for (cnt = wp->m_len; cnt > 0; cnt--) {
+ async_Encode(&p->async, &cp, *sp++, *proto);
+ if (cp >= ep) {
+ m_freem(bp);
+ return NULL;
+ }
+ }
+ wp = wp->m_next;
+ }
+ *cp++ = HDLC_SYN;
+
+ cnt = cp - p->async.xbuff;
+ m_freem(bp);
+ bp = m_get(cnt, MB_ASYNCOUT);
+ memcpy(MBUF_CTOP(bp), p->async.xbuff, cnt);
+ bp->priv = cnt - oldcnt;
+ log_DumpBp(LogASYNC, "Write", bp);
+
+ return bp;
+}
+
+static struct mbuf *
+async_Decode(struct async *async, u_char c)
+{
+ struct mbuf *bp;
+
+ if ((async->mode & MODE_HUNT) && c != HDLC_SYN)
+ return NULL;
+
+ switch (c) {
+ case HDLC_SYN:
+ async->mode &= ~MODE_HUNT;
+ if (async->length) { /* packet is ready. */
+ bp = m_get(async->length, MB_ASYNCIN);
+ mbuf_Write(bp, async->hbuff, async->length);
+ async->length = 0;
+ return bp;
+ }
+ break;
+ case HDLC_ESC:
+ if (!(async->mode & MODE_ESC)) {
+ async->mode |= MODE_ESC;
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ if (async->length >= HDLCSIZE) {
+ /* packet is too large, discard it */
+ log_Printf(LogWARN, "Packet too large (%d), discarding.\n",
+ async->length);
+ async->length = 0;
+ async->mode = MODE_HUNT;
+ break;
+ }
+ if (async->mode & MODE_ESC) {
+ c ^= HDLC_XOR;
+ async->mode &= ~MODE_ESC;
+ }
+ async->hbuff[async->length++] = c;
+ break;
+ }
+ return NULL;
+}
+
+static struct mbuf *
+async_LayerPull(struct bundle *b __unused, struct link *l, struct mbuf *bp,
+ u_short *proto __unused)
+{
+ struct mbuf *nbp, **last;
+ struct physical *p = link2physical(l);
+ u_char *ch;
+ size_t cnt;
+
+ if (!p) {
+ log_Printf(LogERROR, "Can't Pull an async packet from a logical link\n");
+ return bp;
+ }
+
+ last = &nbp;
+
+ log_DumpBp(LogASYNC, "Read", bp);
+ while (bp) {
+ ch = MBUF_CTOP(bp);
+ for (cnt = bp->m_len; cnt; cnt--) {
+ *last = async_Decode(&p->async, *ch++);
+ if (*last != NULL)
+ last = &(*last)->m_nextpkt;
+ }
+ bp = m_free(bp);
+ }
+
+ return nbp;
+}
+
+struct layer asynclayer =
+ { LAYER_ASYNC, "async", async_LayerPush, async_LayerPull };
diff --git a/usr.sbin/ppp/async.h b/usr.sbin/ppp/async.h
new file mode 100644
index 0000000..309f597
--- /dev/null
+++ b/usr.sbin/ppp/async.h
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define HDLCSIZE (MAX_MRU*2+6)
+
+struct async {
+ int mode;
+ int length;
+ u_char hbuff[HDLCSIZE]; /* recv buffer */
+ u_char xbuff[HDLCSIZE]; /* xmit buffer */
+ u_int32_t my_accmap;
+ u_int32_t his_accmap;
+
+ struct {
+ u_char EscMap[33];
+ } cfg;
+};
+
+struct lcp;
+struct mbuf;
+struct physical;
+struct bundle;
+
+extern void async_Init(struct async *);
+extern void async_Setup(struct async *);
+extern void async_SetLinkParams(struct async *, u_int32_t, u_int32_t);
+
+extern struct layer asynclayer;
diff --git a/usr.sbin/ppp/atm.c b/usr.sbin/ppp/atm.c
new file mode 100644
index 0000000..6e0ce75
--- /dev/null
+++ b/usr.sbin/ppp/atm.c
@@ -0,0 +1,237 @@
+/*-
+ * Copyright (c) 2000 Jakob Stoklund Olesen <stoklund@taxidriver.dk>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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/socket.h>
+#include <net/if.h>
+#include <netnatm/natm.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <sys/uio.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "async.h"
+#include "descriptor.h"
+#include "physical.h"
+#include "main.h"
+#include "atm.h"
+
+/* String identifying PPPoA */
+#define PPPOA "PPPoA"
+#define PPPOA_LEN (sizeof(PPPOA) - 1)
+
+struct atmdevice {
+ struct device dev; /* What struct physical knows about */
+};
+
+#define device2atm(d) ((d)->type == ATM_DEVICE ? (struct atmdevice *)d : NULL)
+
+unsigned
+atm_DeviceSize(void)
+{
+ return sizeof(struct atmdevice);
+}
+
+static ssize_t
+atm_Sendto(struct physical *p, const void *v, size_t n)
+{
+ ssize_t ret = write(p->fd, v, n);
+ if (ret < 0) {
+ log_Printf(LogDEBUG, "atm_Sendto(%ld): %s\n", (long)n, strerror(errno));
+ return ret;
+ }
+ return ret;
+}
+
+static ssize_t
+atm_Recvfrom(struct physical *p, void *v, size_t n)
+{
+ ssize_t ret = read(p->fd, (char*)v, n);
+ if (ret < 0) {
+ log_Printf(LogDEBUG, "atm_Recvfrom(%ld): %s\n", (long)n, strerror(errno));
+ return ret;
+ }
+ return ret;
+}
+
+static void
+atm_Free(struct physical *p)
+{
+ struct atmdevice *dev = device2atm(p->handler);
+
+ free(dev);
+}
+
+static void
+atm_device2iov(struct device *d, struct iovec *iov, int *niov,
+ int maxiov __unused, int *auxfd __unused, int *nauxfd __unused)
+{
+ int sz = physical_MaxDeviceSize();
+
+ iov[*niov].iov_base = realloc(d, sz);
+ if (iov[*niov].iov_base == NULL) {
+ log_Printf(LogALERT, "Failed to allocate memory: %d\n", sz);
+ AbortProgram(EX_OSERR);
+ }
+ iov[*niov].iov_len = sz;
+ (*niov)++;
+}
+
+static const struct device baseatmdevice = {
+ ATM_DEVICE,
+ "atm",
+ 0,
+ { CD_NOTREQUIRED, 0 },
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ atm_Free,
+ atm_Recvfrom,
+ atm_Sendto,
+ atm_device2iov,
+ NULL,
+ NULL,
+ NULL
+};
+
+struct device *
+atm_iov2device(int type, struct physical *p, struct iovec *iov, int *niov,
+ int maxiov __unused, int *auxfd __unused, int *nauxfd __unused)
+{
+ if (type == ATM_DEVICE) {
+ struct atmdevice *dev = (struct atmdevice *)iov[(*niov)++].iov_base;
+
+ dev = realloc(dev, sizeof *dev); /* Reduce to the correct size */
+ if (dev == NULL) {
+ log_Printf(LogALERT, "Failed to allocate memory: %d\n",
+ (int)(sizeof *dev));
+ AbortProgram(EX_OSERR);
+ }
+
+ /* Refresh function pointers etc */
+ memcpy(&dev->dev, &baseatmdevice, sizeof dev->dev);
+
+ physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF);
+ return &dev->dev;
+ }
+
+ return NULL;
+}
+
+static struct atmdevice *
+atm_CreateDevice(struct physical *p, const char *iface, unsigned vpi,
+ unsigned vci)
+{
+ struct atmdevice *dev;
+ struct sockaddr_natm sock;
+
+ if ((dev = calloc(1, sizeof *dev)) == NULL) {
+ log_Printf(LogWARN, "%s: Cannot allocate an atm device: %s\n",
+ p->link.name, strerror(errno));
+ return NULL;
+ }
+
+ sock.snatm_len = sizeof sock;
+ sock.snatm_family = AF_NATM;
+ strncpy(sock.snatm_if, iface, IFNAMSIZ);
+ sock.snatm_vpi = vpi;
+ sock.snatm_vci = vci;
+
+ log_Printf(LogPHASE, "%s: Connecting to %s:%u.%u\n", p->link.name,
+ iface, vpi, vci);
+
+ p->fd = socket(PF_NATM, SOCK_DGRAM, PROTO_NATMAAL5);
+ if (p->fd >= 0) {
+ log_Printf(LogDEBUG, "%s: Opened atm socket %s\n", p->link.name,
+ p->name.full);
+ if (connect(p->fd, (struct sockaddr *)&sock, sizeof sock) == 0)
+ return dev;
+ else
+ log_Printf(LogWARN, "%s: connect: %s\n", p->name.full, strerror(errno));
+ } else
+ log_Printf(LogWARN, "%s: socket: %s\n", p->name.full, strerror(errno));
+
+ close(p->fd);
+ p->fd = -1;
+ free(dev);
+
+ return NULL;
+}
+
+struct device *
+atm_Create(struct physical *p)
+{
+ struct atmdevice *dev;
+
+ dev = NULL;
+ if (p->fd < 0 && !strncasecmp(p->name.full, PPPOA, PPPOA_LEN)
+ && p->name.full[PPPOA_LEN] == ':') {
+ char iface[25];
+ unsigned vci, vpi;
+
+ if (sscanf(p->name.full + PPPOA_LEN + 1, "%25[A-Za-z0-9]:%u.%u", iface,
+ &vpi, &vci) != 3) {
+ log_Printf(LogWARN, "Malformed ATM device name \'%s\', "
+ "PPPoA:if:vpi.vci expected\n", p->name.full);
+ return NULL;
+ }
+
+ dev = atm_CreateDevice(p, iface, vpi, vci);
+ }
+
+ if (dev) {
+ memcpy(&dev->dev, &baseatmdevice, sizeof dev->dev);
+ physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF);
+ if (p->cfg.cd.necessity != CD_DEFAULT)
+ log_Printf(LogWARN, "Carrier settings ignored\n");
+ return &dev->dev;
+ }
+
+ return NULL;
+}
diff --git a/usr.sbin/ppp/atm.h b/usr.sbin/ppp/atm.h
new file mode 100644
index 0000000..ccfad8d
--- /dev/null
+++ b/usr.sbin/ppp/atm.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 2000 Jakob Stoklund Olesen <stoklund@taxidriver.dk>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+struct physical;
+struct device;
+
+extern struct device *atm_Create(struct physical *);
+extern struct device *atm_iov2device(int, struct physical *,
+ struct iovec *, int *, int, int *, int *);
+extern unsigned atm_DeviceSize(void);
diff --git a/usr.sbin/ppp/auth.c b/usr.sbin/ppp/auth.c
new file mode 100644
index 0000000..66a3de7
--- /dev/null
+++ b/usr.sbin/ppp/auth.c
@@ -0,0 +1,479 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#ifndef NOPAM
+#include <security/pam_appl.h>
+#ifdef OPENPAM
+#include <security/openpam.h>
+#endif
+#endif /* !NOPAM */
+
+#include "layer.h"
+#include "mbuf.h"
+#include "defs.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "iplist.h"
+#include "throughput.h"
+#include "slcompress.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "auth.h"
+#include "systems.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "descriptor.h"
+#include "chat.h"
+#include "proto.h"
+#include "filter.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "cbcp.h"
+#include "chap.h"
+#include "async.h"
+#include "physical.h"
+#include "datalink.h"
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+
+const char *
+Auth2Nam(u_short auth, u_char type)
+{
+ static char chap[10];
+
+ switch (auth) {
+ case PROTO_PAP:
+ return "PAP";
+ case PROTO_CHAP:
+ snprintf(chap, sizeof chap, "CHAP 0x%02x", type);
+ return chap;
+ case 0:
+ return "none";
+ }
+ return "unknown";
+}
+
+#if !defined(NOPAM) && !defined(OPENPAM)
+static int
+pam_conv(int n, const struct pam_message **msg, struct pam_response **resp,
+ void *data)
+{
+
+ if (n != 1 || msg[0]->msg_style != PAM_PROMPT_ECHO_OFF)
+ return (PAM_CONV_ERR);
+ if ((*resp = malloc(sizeof(struct pam_response))) == NULL)
+ return (PAM_CONV_ERR);
+ (*resp)[0].resp = strdup((const char *)data);
+ (*resp)[0].resp_retcode = 0;
+
+ return ((*resp)[0].resp != NULL ? PAM_SUCCESS : PAM_CONV_ERR);
+}
+#endif /* !defined(NOPAM) && !defined(OPENPAM) */
+
+static int
+auth_CheckPasswd(const char *name, const char *data, const char *key)
+{
+ if (!strcmp(data, "*")) {
+#ifdef NOPAM
+ /* Then look up the real password database */
+ struct passwd *pw;
+ int result;
+
+ result = (pw = getpwnam(name)) &&
+ !strcmp(crypt(key, pw->pw_passwd), pw->pw_passwd);
+ endpwent();
+ return result;
+#else /* !NOPAM */
+ /* Then consult with PAM. */
+ pam_handle_t *pamh;
+ int status;
+
+ struct pam_conv pamc = {
+#ifdef OPENPAM
+ &openpam_nullconv, NULL
+#else
+ &pam_conv, key
+#endif
+ };
+
+ if (pam_start("ppp", name, &pamc, &pamh) != PAM_SUCCESS)
+ return (0);
+#ifdef OPENPAM
+ if ((status = pam_set_item(pamh, PAM_AUTHTOK, key)) == PAM_SUCCESS)
+#endif
+ status = pam_authenticate(pamh, 0);
+ pam_end(pamh, status);
+ return (status == PAM_SUCCESS);
+#endif /* !NOPAM */
+ }
+
+ return !strcmp(data, key);
+}
+
+int
+auth_SetPhoneList(const char *name, char *phone, int phonelen)
+{
+ FILE *fp;
+ int n, lineno;
+ char *vector[6], buff[LINE_LEN];
+ const char *slash;
+
+ fp = OpenSecret(SECRETFILE);
+ if (fp != NULL) {
+again:
+ lineno = 0;
+ while (fgets(buff, sizeof buff, fp)) {
+ lineno++;
+ if (buff[0] == '#')
+ continue;
+ buff[strlen(buff) - 1] = '\0';
+ memset(vector, '\0', sizeof vector);
+ if ((n = MakeArgs(buff, vector, VECSIZE(vector), PARSE_REDUCE)) < 0)
+ log_Printf(LogWARN, "%s: %d: Invalid line\n", SECRETFILE, lineno);
+ if (n < 5)
+ continue;
+ if (strcmp(vector[0], name) == 0) {
+ CloseSecret(fp);
+ if (*vector[4] == '\0')
+ return 0;
+ strncpy(phone, vector[4], phonelen - 1);
+ phone[phonelen - 1] = '\0';
+ return 1; /* Valid */
+ }
+ }
+
+ if ((slash = strrchr(name, '\\')) != NULL && slash[1]) {
+ /* Look for the name without the leading domain */
+ name = slash + 1;
+ rewind(fp);
+ goto again;
+ }
+
+ CloseSecret(fp);
+ }
+ *phone = '\0';
+ return 0;
+}
+
+int
+auth_Select(struct bundle *bundle, const char *name)
+{
+ FILE *fp;
+ int n, lineno;
+ char *vector[5], buff[LINE_LEN];
+ const char *slash;
+
+ if (*name == '\0') {
+ ipcp_Setup(&bundle->ncp.ipcp, INADDR_NONE);
+ return 1;
+ }
+
+#ifndef NORADIUS
+ if (bundle->radius.valid && bundle->radius.ip.s_addr != INADDR_NONE &&
+ bundle->radius.ip.s_addr != RADIUS_INADDR_POOL) {
+ /* We've got a radius IP - it overrides everything */
+ if (!ipcp_UseHisIPaddr(bundle, bundle->radius.ip))
+ return 0;
+ ipcp_Setup(&bundle->ncp.ipcp, bundle->radius.mask.s_addr);
+ /* Continue with ppp.secret in case we've got a new label */
+ }
+#endif
+
+ fp = OpenSecret(SECRETFILE);
+ if (fp != NULL) {
+again:
+ lineno = 0;
+ while (fgets(buff, sizeof buff, fp)) {
+ lineno++;
+ if (buff[0] == '#')
+ continue;
+ buff[strlen(buff) - 1] = '\0';
+ memset(vector, '\0', sizeof vector);
+ if ((n = MakeArgs(buff, vector, VECSIZE(vector), PARSE_REDUCE)) < 0)
+ log_Printf(LogWARN, "%s: %d: Invalid line\n", SECRETFILE, lineno);
+ if (n < 2)
+ continue;
+ if (strcmp(vector[0], name) == 0) {
+ CloseSecret(fp);
+#ifndef NORADIUS
+ if (!bundle->radius.valid || bundle->radius.ip.s_addr == INADDR_NONE) {
+#endif
+ if (n > 2 && *vector[2] && strcmp(vector[2], "*") &&
+ !ipcp_UseHisaddr(bundle, vector[2], 1))
+ return 0;
+ ipcp_Setup(&bundle->ncp.ipcp, INADDR_NONE);
+#ifndef NORADIUS
+ }
+#endif
+ if (n > 3 && *vector[3] && strcmp(vector[3], "*"))
+ bundle_SetLabel(bundle, vector[3]);
+ return 1; /* Valid */
+ }
+ }
+
+ if ((slash = strrchr(name, '\\')) != NULL && slash[1]) {
+ /* Look for the name without the leading domain */
+ name = slash + 1;
+ rewind(fp);
+ goto again;
+ }
+
+ CloseSecret(fp);
+ }
+
+#ifndef NOPASSWDAUTH
+ /* Let 'em in anyway - they must have been in the passwd file */
+ ipcp_Setup(&bundle->ncp.ipcp, INADDR_NONE);
+ return 1;
+#else
+#ifndef NORADIUS
+ if (bundle->radius.valid)
+ return 1;
+#endif
+
+ /* Disappeared from ppp.secret ??? */
+ return 0;
+#endif
+}
+
+int
+auth_Validate(struct bundle *bundle, const char *name, const char *key)
+{
+ /* Used by PAP routines */
+
+ FILE *fp;
+ int n, lineno;
+ char *vector[5], buff[LINE_LEN];
+ const char *slash;
+
+ fp = OpenSecret(SECRETFILE);
+again:
+ lineno = 0;
+ if (fp != NULL) {
+ while (fgets(buff, sizeof buff, fp)) {
+ lineno++;
+ if (buff[0] == '#')
+ continue;
+ buff[strlen(buff) - 1] = 0;
+ memset(vector, '\0', sizeof vector);
+ if ((n = MakeArgs(buff, vector, VECSIZE(vector), PARSE_REDUCE)) < 0)
+ log_Printf(LogWARN, "%s: %d: Invalid line\n", SECRETFILE, lineno);
+ if (n < 2)
+ continue;
+ if (strcmp(vector[0], name) == 0) {
+ CloseSecret(fp);
+ return auth_CheckPasswd(name, vector[1], key);
+ }
+ }
+ }
+
+ if ((slash = strrchr(name, '\\')) != NULL && slash[1]) {
+ /* Look for the name without the leading domain */
+ name = slash + 1;
+ if (fp != NULL) {
+ rewind(fp);
+ goto again;
+ }
+ }
+
+ if (fp != NULL)
+ CloseSecret(fp);
+
+#ifndef NOPASSWDAUTH
+ if (Enabled(bundle, OPT_PASSWDAUTH))
+ return auth_CheckPasswd(name, "*", key);
+#endif
+
+ return 0; /* Invalid */
+}
+
+char *
+auth_GetSecret(const char *name, size_t len)
+{
+ /* Used by CHAP routines */
+
+ FILE *fp;
+ int n, lineno;
+ char *vector[5];
+ const char *slash;
+ static char buff[LINE_LEN]; /* vector[] will point here when returned */
+
+ fp = OpenSecret(SECRETFILE);
+ if (fp == NULL)
+ return (NULL);
+
+again:
+ lineno = 0;
+ while (fgets(buff, sizeof buff, fp)) {
+ lineno++;
+ if (buff[0] == '#')
+ continue;
+ n = strlen(buff) - 1;
+ if (buff[n] == '\n')
+ buff[n] = '\0'; /* Trim the '\n' */
+ memset(vector, '\0', sizeof vector);
+ if ((n = MakeArgs(buff, vector, VECSIZE(vector), PARSE_REDUCE)) < 0)
+ log_Printf(LogWARN, "%s: %d: Invalid line\n", SECRETFILE, lineno);
+ if (n < 2)
+ continue;
+ if (strlen(vector[0]) == len && strncmp(vector[0], name, len) == 0) {
+ CloseSecret(fp);
+ return vector[1];
+ }
+ }
+
+ if ((slash = strrchr(name, '\\')) != NULL && slash[1]) {
+ /* Go back and look for the name without the leading domain */
+ len -= slash - name + 1;
+ name = slash + 1;
+ rewind(fp);
+ goto again;
+ }
+
+ CloseSecret(fp);
+ return (NULL); /* Invalid */
+}
+
+static void
+AuthTimeout(void *vauthp)
+{
+ struct authinfo *authp = (struct authinfo *)vauthp;
+
+ timer_Stop(&authp->authtimer);
+ if (--authp->retry > 0) {
+ authp->id++;
+ (*authp->fn.req)(authp);
+ timer_Start(&authp->authtimer);
+ } else {
+ log_Printf(LogPHASE, "Auth: No response from server\n");
+ datalink_AuthNotOk(authp->physical->dl);
+ }
+}
+
+void
+auth_Init(struct authinfo *authp, struct physical *p, auth_func req,
+ auth_func success, auth_func failure)
+{
+ memset(authp, '\0', sizeof(struct authinfo));
+ authp->cfg.fsm.timeout = DEF_FSMRETRY;
+ authp->cfg.fsm.maxreq = DEF_FSMAUTHTRIES;
+ authp->cfg.fsm.maxtrm = 0; /* not used */
+ authp->fn.req = req;
+ authp->fn.success = success;
+ authp->fn.failure = failure;
+ authp->physical = p;
+}
+
+void
+auth_StartReq(struct authinfo *authp)
+{
+ timer_Stop(&authp->authtimer);
+ authp->authtimer.func = AuthTimeout;
+ authp->authtimer.name = "auth";
+ authp->authtimer.load = authp->cfg.fsm.timeout * SECTICKS;
+ authp->authtimer.arg = (void *)authp;
+ authp->retry = authp->cfg.fsm.maxreq;
+ authp->id = 1;
+ (*authp->fn.req)(authp);
+ timer_Start(&authp->authtimer);
+}
+
+void
+auth_StopTimer(struct authinfo *authp)
+{
+ timer_Stop(&authp->authtimer);
+}
+
+struct mbuf *
+auth_ReadHeader(struct authinfo *authp, struct mbuf *bp)
+{
+ size_t len;
+
+ len = m_length(bp);
+ if (len >= sizeof authp->in.hdr) {
+ bp = mbuf_Read(bp, (u_char *)&authp->in.hdr, sizeof authp->in.hdr);
+ if (len >= ntohs(authp->in.hdr.length))
+ return bp;
+ authp->in.hdr.length = htons(0);
+ log_Printf(LogWARN, "auth_ReadHeader: Short packet (%u > %zu) !\n",
+ ntohs(authp->in.hdr.length), len);
+ } else {
+ authp->in.hdr.length = htons(0);
+ log_Printf(LogWARN, "auth_ReadHeader: Short packet header (%u > %zu) !\n",
+ (int)(sizeof authp->in.hdr), len);
+ }
+
+ m_freem(bp);
+ return NULL;
+}
+
+struct mbuf *
+auth_ReadName(struct authinfo *authp, struct mbuf *bp, size_t len)
+{
+ if (len > sizeof authp->in.name - 1)
+ log_Printf(LogWARN, "auth_ReadName: Name too long (%zu) !\n", len);
+ else {
+ size_t mlen = m_length(bp);
+
+ if (len > mlen)
+ log_Printf(LogWARN, "auth_ReadName: Short packet (%zu > %zu) !\n",
+ len, mlen);
+ else {
+ bp = mbuf_Read(bp, (u_char *)authp->in.name, len);
+ authp->in.name[len] = '\0';
+ return bp;
+ }
+ }
+
+ *authp->in.name = '\0';
+ m_freem(bp);
+ return NULL;
+}
diff --git a/usr.sbin/ppp/auth.h b/usr.sbin/ppp/auth.h
new file mode 100644
index 0000000..3e1047d
--- /dev/null
+++ b/usr.sbin/ppp/auth.h
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+struct physical;
+struct bundle;
+struct authinfo;
+typedef void (*auth_func)(struct authinfo *);
+
+struct authinfo {
+ struct {
+ auth_func req;
+ auth_func success;
+ auth_func failure;
+ } fn;
+ struct {
+ struct fsmheader hdr;
+ char name[AUTHLEN];
+ } in;
+ struct pppTimer authtimer;
+ int retry;
+ int id;
+ struct physical *physical;
+ struct {
+ struct fsm_retry fsm; /* How often/frequently to resend requests */
+ } cfg;
+};
+
+#define auth_Failure(a) (*(a)->fn.failure)(a)
+#define auth_Success(a) (*(a)->fn.success)(a)
+
+extern const char *Auth2Nam(u_short, u_char);
+extern void auth_Init(struct authinfo *, struct physical *,
+ auth_func, auth_func, auth_func);
+extern void auth_StopTimer(struct authinfo *);
+extern void auth_StartReq(struct authinfo *);
+extern int auth_Validate(struct bundle *, const char *, const char *);
+extern char *auth_GetSecret(const char *, size_t);
+extern int auth_SetPhoneList(const char *, char *, int);
+extern int auth_Select(struct bundle *, const char *);
+extern struct mbuf *auth_ReadHeader(struct authinfo *, struct mbuf *);
+extern struct mbuf *auth_ReadName(struct authinfo *, struct mbuf *, size_t);
diff --git a/usr.sbin/ppp/bundle.c b/usr.sbin/ppp/bundle.c
new file mode 100644
index 0000000..912f855
--- /dev/null
+++ b/usr.sbin/ppp/bundle.c
@@ -0,0 +1,2019 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <net/if_tun.h> /* For TUNS* ioctls */
+#include <net/route.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#ifdef __OpenBSD__
+#include <util.h>
+#else
+#include <libutil.h>
+#endif
+#include <paths.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "id.h"
+#include "timer.h"
+#include "fsm.h"
+#include "iplist.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "slcompress.h"
+#include "ncpaddr.h"
+#include "ip.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "route.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "async.h"
+#include "physical.h"
+#include "auth.h"
+#include "proto.h"
+#include "chap.h"
+#include "tun.h"
+#include "prompt.h"
+#include "chat.h"
+#include "cbcp.h"
+#include "datalink.h"
+#include "iface.h"
+#include "server.h"
+#include "probe.h"
+#ifndef NODES
+#include "mppe.h"
+#endif
+
+#define SCATTER_SEGMENTS 7 /* version, datalink, name, physical,
+ throughput, throughput, device */
+
+#define SEND_MAXFD 3 /* Max file descriptors passed through
+ the local domain socket */
+
+static int bundle_RemainingIdleTime(struct bundle *);
+
+static const char * const PhaseNames[] = {
+ "Dead", "Establish", "Authenticate", "Network", "Terminate"
+};
+
+const char *
+bundle_PhaseName(struct bundle *bundle)
+{
+ return bundle->phase <= PHASE_TERMINATE ?
+ PhaseNames[bundle->phase] : "unknown";
+}
+
+void
+bundle_NewPhase(struct bundle *bundle, u_int new)
+{
+ if (new == bundle->phase)
+ return;
+
+ if (new <= PHASE_TERMINATE)
+ log_Printf(LogPHASE, "bundle: %s\n", PhaseNames[new]);
+
+ switch (new) {
+ case PHASE_DEAD:
+ bundle->phase = new;
+#ifndef NODES
+ MPPE_MasterKeyValid = 0;
+#endif
+ log_DisplayPrompts();
+ break;
+
+ case PHASE_ESTABLISH:
+ bundle->phase = new;
+ break;
+
+ case PHASE_AUTHENTICATE:
+ bundle->phase = new;
+ log_DisplayPrompts();
+ break;
+
+ case PHASE_NETWORK:
+ if (ncp_fsmStart(&bundle->ncp, bundle)) {
+ bundle->phase = new;
+ log_DisplayPrompts();
+ } else {
+ log_Printf(LogPHASE, "bundle: All NCPs are disabled\n");
+ bundle_Close(bundle, NULL, CLOSE_STAYDOWN);
+ }
+ break;
+
+ case PHASE_TERMINATE:
+ bundle->phase = new;
+ mp_Down(&bundle->ncp.mp);
+ log_DisplayPrompts();
+ break;
+ }
+}
+
+static void
+bundle_LayerStart(void *v __unused, struct fsm *fp __unused)
+{
+ /* The given FSM is about to start up ! */
+}
+
+
+void
+bundle_Notify(struct bundle *bundle, char c)
+{
+ if (bundle->notify.fd != -1) {
+ int ret;
+
+ ret = write(bundle->notify.fd, &c, 1);
+ if (c != EX_REDIAL && c != EX_RECONNECT) {
+ if (ret == 1)
+ log_Printf(LogCHAT, "Parent notified of %s\n",
+ c == EX_NORMAL ? "success" : "failure");
+ else
+ log_Printf(LogERROR, "Failed to notify parent of success\n");
+ close(bundle->notify.fd);
+ bundle->notify.fd = -1;
+ } else if (ret == 1)
+ log_Printf(LogCHAT, "Parent notified of %s\n", ex_desc(c));
+ else
+ log_Printf(LogERROR, "Failed to notify parent of %s\n", ex_desc(c));
+ }
+}
+
+static void
+bundle_ClearQueues(void *v)
+{
+ struct bundle *bundle = (struct bundle *)v;
+ struct datalink *dl;
+
+ log_Printf(LogPHASE, "Clearing choked output queue\n");
+ timer_Stop(&bundle->choked.timer);
+
+ /*
+ * Emergency time:
+ *
+ * We've had a full queue for PACKET_DEL_SECS seconds without being
+ * able to get rid of any of the packets. We've probably given up
+ * on the redials at this point, and the queued data has almost
+ * definitely been timed out by the layer above. As this is preventing
+ * us from reading the TUN_NAME device (we don't want to buffer stuff
+ * indefinitely), we may as well nuke this data and start with a clean
+ * slate !
+ *
+ * Unfortunately, this has the side effect of shafting any compression
+ * dictionaries in use (causing the relevant RESET_REQ/RESET_ACK).
+ */
+
+ ncp_DeleteQueues(&bundle->ncp);
+ for (dl = bundle->links; dl; dl = dl->next)
+ physical_DeleteQueue(dl->physical);
+}
+
+static void
+bundle_LinkAdded(struct bundle *bundle, struct datalink *dl)
+{
+ bundle->phys_type.all |= dl->physical->type;
+ if (dl->state == DATALINK_OPEN)
+ bundle->phys_type.open |= dl->physical->type;
+
+#ifndef NORADIUS
+ if ((bundle->phys_type.open & (PHYS_DEDICATED|PHYS_DDIAL))
+ != bundle->phys_type.open && bundle->session.timer.state == TIMER_STOPPED)
+ if (bundle->radius.sessiontime)
+ bundle_StartSessionTimer(bundle, 0);
+#endif
+
+ if ((bundle->phys_type.open & (PHYS_DEDICATED|PHYS_DDIAL))
+ != bundle->phys_type.open && bundle->idle.timer.state == TIMER_STOPPED)
+ /* We may need to start our idle timer */
+ bundle_StartIdleTimer(bundle, 0);
+}
+
+void
+bundle_LinksRemoved(struct bundle *bundle)
+{
+ struct datalink *dl;
+
+ bundle->phys_type.all = bundle->phys_type.open = 0;
+ for (dl = bundle->links; dl; dl = dl->next)
+ bundle_LinkAdded(bundle, dl);
+
+ bundle_CalculateBandwidth(bundle);
+ mp_CheckAutoloadTimer(&bundle->ncp.mp);
+
+ if ((bundle->phys_type.open & (PHYS_DEDICATED|PHYS_DDIAL))
+ == bundle->phys_type.open) {
+#ifndef NORADIUS
+ if (bundle->radius.sessiontime)
+ bundle_StopSessionTimer(bundle);
+#endif
+ bundle_StopIdleTimer(bundle);
+ }
+}
+
+static void
+bundle_LayerUp(void *v, struct fsm *fp)
+{
+ /*
+ * The given fsm is now up
+ * If it's an LCP, adjust our phys_mode.open value and check the
+ * autoload timer.
+ * If it's the first NCP, calculate our bandwidth
+ * If it's the first NCP, set our ``upat'' time
+ * If it's the first NCP, start the idle timer.
+ * If it's an NCP, tell our -background parent to go away.
+ * If it's the first NCP, start the autoload timer
+ */
+ struct bundle *bundle = (struct bundle *)v;
+
+ if (fp->proto == PROTO_LCP) {
+ struct physical *p = link2physical(fp->link);
+
+ bundle_LinkAdded(bundle, p->dl);
+ mp_CheckAutoloadTimer(&bundle->ncp.mp);
+ } else if (isncp(fp->proto)) {
+ if (ncp_LayersOpen(&fp->bundle->ncp) == 1) {
+ bundle_CalculateBandwidth(fp->bundle);
+ time(&bundle->upat);
+#ifndef NORADIUS
+ if (bundle->radius.sessiontime)
+ bundle_StartSessionTimer(bundle, 0);
+#endif
+ bundle_StartIdleTimer(bundle, 0);
+ mp_CheckAutoloadTimer(&fp->bundle->ncp.mp);
+ }
+ bundle_Notify(bundle, EX_NORMAL);
+ } else if (fp->proto == PROTO_CCP)
+ bundle_CalculateBandwidth(fp->bundle); /* Against ccp_MTUOverhead */
+}
+
+static void
+bundle_LayerDown(void *v, struct fsm *fp)
+{
+ /*
+ * The given FSM has been told to come down.
+ * If it's our last NCP, stop the idle timer.
+ * If it's our last NCP, clear our ``upat'' value.
+ * If it's our last NCP, stop the autoload timer
+ * If it's an LCP, adjust our phys_type.open value and any timers.
+ * If it's an LCP and we're in multilink mode, adjust our tun
+ * If it's the last LCP, down all NCPs
+ * speed and make sure our minimum sequence number is adjusted.
+ */
+
+ struct bundle *bundle = (struct bundle *)v;
+
+ if (isncp(fp->proto)) {
+ if (ncp_LayersOpen(&fp->bundle->ncp) == 0) {
+#ifndef NORADIUS
+ if (bundle->radius.sessiontime)
+ bundle_StopSessionTimer(bundle);
+#endif
+ bundle_StopIdleTimer(bundle);
+ bundle->upat = 0;
+ mp_StopAutoloadTimer(&bundle->ncp.mp);
+ }
+ } else if (fp->proto == PROTO_LCP) {
+ struct datalink *dl;
+ struct datalink *lost;
+ int others_active;
+
+ bundle_LinksRemoved(bundle); /* adjust timers & phys_type values */
+
+ lost = NULL;
+ others_active = 0;
+ for (dl = bundle->links; dl; dl = dl->next) {
+ if (fp == &dl->physical->link.lcp.fsm)
+ lost = dl;
+ else if (dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP)
+ others_active++;
+ }
+
+ if (bundle->ncp.mp.active) {
+ bundle_CalculateBandwidth(bundle);
+
+ if (lost)
+ mp_LinkLost(&bundle->ncp.mp, lost);
+ else
+ log_Printf(LogALERT, "Oops, lost an unrecognised datalink (%s) !\n",
+ fp->link->name);
+ }
+
+ if (!others_active) {
+ /* Down the NCPs. We don't expect to get fsm_Close()d ourself ! */
+ ncp2initial(&bundle->ncp);
+ mp_Down(&bundle->ncp.mp);
+ }
+ }
+}
+
+static void
+bundle_LayerFinish(void *v, struct fsm *fp)
+{
+ /* The given fsm is now down (fp cannot be NULL)
+ *
+ * If it's the last NCP, fsm_Close all LCPs
+ * If it's the last NCP, bring any MP layer down
+ */
+
+ struct bundle *bundle = (struct bundle *)v;
+ struct datalink *dl;
+
+ if (isncp(fp->proto) && !ncp_LayersUnfinished(&bundle->ncp)) {
+ if (bundle_Phase(bundle) != PHASE_DEAD)
+ bundle_NewPhase(bundle, PHASE_TERMINATE);
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (dl->state == DATALINK_OPEN)
+ datalink_Close(dl, CLOSE_STAYDOWN);
+ fsm2initial(fp);
+ mp_Down(&bundle->ncp.mp);
+ }
+}
+
+void
+bundle_Close(struct bundle *bundle, const char *name, int how)
+{
+ /*
+ * Please close the given datalink.
+ * If name == NULL or name is the last datalink, fsm_Close all NCPs
+ * (except our MP)
+ * If it isn't the last datalink, just Close that datalink.
+ */
+
+ struct datalink *dl, *this_dl;
+ int others_active;
+
+ others_active = 0;
+ this_dl = NULL;
+
+ for (dl = bundle->links; dl; dl = dl->next) {
+ if (name && !strcasecmp(name, dl->name))
+ this_dl = dl;
+ if (name == NULL || this_dl == dl) {
+ switch (how) {
+ case CLOSE_LCP:
+ datalink_DontHangup(dl);
+ break;
+ case CLOSE_STAYDOWN:
+ datalink_StayDown(dl);
+ break;
+ }
+ } else if (dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP)
+ others_active++;
+ }
+
+ if (name && this_dl == NULL) {
+ log_Printf(LogWARN, "%s: Invalid datalink name\n", name);
+ return;
+ }
+
+ if (!others_active) {
+#ifndef NORADIUS
+ if (bundle->radius.sessiontime)
+ bundle_StopSessionTimer(bundle);
+#endif
+ bundle_StopIdleTimer(bundle);
+ if (ncp_LayersUnfinished(&bundle->ncp))
+ ncp_Close(&bundle->ncp);
+ else {
+ ncp2initial(&bundle->ncp);
+ mp_Down(&bundle->ncp.mp);
+ for (dl = bundle->links; dl; dl = dl->next)
+ datalink_Close(dl, how);
+ }
+ } else if (this_dl && this_dl->state != DATALINK_CLOSED &&
+ this_dl->state != DATALINK_HANGUP)
+ datalink_Close(this_dl, how);
+}
+
+void
+bundle_Down(struct bundle *bundle, int how)
+{
+ struct datalink *dl;
+
+ for (dl = bundle->links; dl; dl = dl->next)
+ datalink_Down(dl, how);
+}
+
+static int
+bundle_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
+{
+ struct bundle *bundle = descriptor2bundle(d);
+ struct datalink *dl;
+ int result, nlinks;
+ u_short ifqueue;
+ size_t queued;
+
+ result = 0;
+
+ /* If there are aren't many packets queued, look for some more. */
+ for (nlinks = 0, dl = bundle->links; dl; dl = dl->next)
+ nlinks++;
+
+ if (nlinks) {
+ queued = r ? ncp_FillPhysicalQueues(&bundle->ncp, bundle) :
+ ncp_QueueLen(&bundle->ncp);
+
+ if (r && (bundle->phase == PHASE_NETWORK ||
+ bundle->phys_type.all & PHYS_AUTO)) {
+ /* enough surplus so that we can tell if we're getting swamped */
+ ifqueue = nlinks > bundle->cfg.ifqueue ? nlinks : bundle->cfg.ifqueue;
+ if (queued < ifqueue) {
+ /* Not enough - select() for more */
+ if (bundle->choked.timer.state == TIMER_RUNNING)
+ timer_Stop(&bundle->choked.timer); /* Not needed any more */
+ FD_SET(bundle->dev.fd, r);
+ if (*n < bundle->dev.fd + 1)
+ *n = bundle->dev.fd + 1;
+ log_Printf(LogTIMER, "%s: fdset(r) %d\n", TUN_NAME, bundle->dev.fd);
+ result++;
+ } else if (bundle->choked.timer.state == TIMER_STOPPED) {
+ bundle->choked.timer.func = bundle_ClearQueues;
+ bundle->choked.timer.name = "output choke";
+ bundle->choked.timer.load = bundle->cfg.choked.timeout * SECTICKS;
+ bundle->choked.timer.arg = bundle;
+ timer_Start(&bundle->choked.timer);
+ }
+ }
+ }
+
+#ifndef NORADIUS
+ result += descriptor_UpdateSet(&bundle->radius.desc, r, w, e, n);
+#endif
+
+ /* Which links need a select() ? */
+ for (dl = bundle->links; dl; dl = dl->next)
+ result += descriptor_UpdateSet(&dl->desc, r, w, e, n);
+
+ /*
+ * This *MUST* be called after the datalink UpdateSet()s as it
+ * might be ``holding'' one of the datalinks (death-row) and
+ * wants to be able to de-select() it from the descriptor set.
+ */
+ result += descriptor_UpdateSet(&bundle->ncp.mp.server.desc, r, w, e, n);
+
+ return result;
+}
+
+static int
+bundle_IsSet(struct fdescriptor *d, const fd_set *fdset)
+{
+ struct bundle *bundle = descriptor2bundle(d);
+ struct datalink *dl;
+
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (descriptor_IsSet(&dl->desc, fdset))
+ return 1;
+
+#ifndef NORADIUS
+ if (descriptor_IsSet(&bundle->radius.desc, fdset))
+ return 1;
+#endif
+
+ if (descriptor_IsSet(&bundle->ncp.mp.server.desc, fdset))
+ return 1;
+
+ return FD_ISSET(bundle->dev.fd, fdset);
+}
+
+static void
+bundle_DescriptorRead(struct fdescriptor *d __unused, struct bundle *bundle,
+ const fd_set *fdset)
+{
+ struct datalink *dl;
+ unsigned secs;
+ u_int32_t af;
+
+ if (descriptor_IsSet(&bundle->ncp.mp.server.desc, fdset))
+ descriptor_Read(&bundle->ncp.mp.server.desc, bundle, fdset);
+
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (descriptor_IsSet(&dl->desc, fdset))
+ descriptor_Read(&dl->desc, bundle, fdset);
+
+#ifndef NORADIUS
+ if (descriptor_IsSet(&bundle->radius.desc, fdset))
+ descriptor_Read(&bundle->radius.desc, bundle, fdset);
+#endif
+
+ if (FD_ISSET(bundle->dev.fd, fdset)) {
+ struct tun_data tun;
+ int n, pri;
+ u_char *data;
+ size_t sz;
+
+ if (bundle->dev.header) {
+ data = (u_char *)&tun;
+ sz = sizeof tun;
+ } else {
+ data = tun.data;
+ sz = sizeof tun.data;
+ }
+
+ /* something to read from tun */
+
+ n = read(bundle->dev.fd, data, sz);
+ if (n < 0) {
+ log_Printf(LogWARN, "%s: read: %s\n", bundle->dev.Name, strerror(errno));
+ return;
+ }
+
+ if (bundle->dev.header) {
+ n -= sz - sizeof tun.data;
+ if (n <= 0) {
+ log_Printf(LogERROR, "%s: read: Got only %d bytes of data !\n",
+ bundle->dev.Name, n);
+ return;
+ }
+ af = ntohl(tun.header.family);
+#ifndef NOINET6
+ if (af != AF_INET && af != AF_INET6)
+#else
+ if (af != AF_INET)
+#endif
+ /* XXX: Should be maintaining drop/family counts ! */
+ return;
+ } else
+ af = AF_INET;
+
+ if (af == AF_INET && ((struct ip *)tun.data)->ip_dst.s_addr ==
+ bundle->ncp.ipcp.my_ip.s_addr) {
+ /* we've been asked to send something addressed *to* us :( */
+ if (Enabled(bundle, OPT_LOOPBACK)) {
+ pri = PacketCheck(bundle, af, tun.data, n, &bundle->filter.in,
+ NULL, NULL);
+ if (pri >= 0) {
+ n += sz - sizeof tun.data;
+ write(bundle->dev.fd, data, n);
+ log_Printf(LogDEBUG, "Looped back packet addressed to myself\n");
+ }
+ return;
+ } else
+ log_Printf(LogDEBUG, "Oops - forwarding packet addressed to myself\n");
+ }
+
+ /*
+ * Process on-demand dialup. Output packets are queued within the tunnel
+ * device until the appropriate NCP is opened.
+ */
+
+ if (bundle_Phase(bundle) == PHASE_DEAD) {
+ /*
+ * Note, we must be in AUTO mode :-/ otherwise our interface should
+ * *not* be UP and we can't receive data
+ */
+ pri = PacketCheck(bundle, af, tun.data, n, &bundle->filter.dial,
+ NULL, NULL);
+ if (pri >= 0)
+ bundle_Open(bundle, NULL, PHYS_AUTO, 0);
+ else
+ /*
+ * Drop the packet. If we were to queue it, we'd just end up with
+ * a pile of timed-out data in our output queue by the time we get
+ * around to actually dialing. We'd also prematurely reach the
+ * threshold at which we stop select()ing to read() the tun
+ * device - breaking auto-dial.
+ */
+ return;
+ }
+
+ secs = 0;
+ pri = PacketCheck(bundle, af, tun.data, n, &bundle->filter.out,
+ NULL, &secs);
+ if (pri >= 0) {
+ /* Prepend the number of seconds timeout given in the filter */
+ tun.header.timeout = secs;
+ ncp_Enqueue(&bundle->ncp, af, pri, (char *)&tun, n + sizeof tun.header);
+ }
+ }
+}
+
+static int
+bundle_DescriptorWrite(struct fdescriptor *d __unused, struct bundle *bundle,
+ const fd_set *fdset)
+{
+ struct datalink *dl;
+ int result = 0;
+
+ /* This is not actually necessary as struct mpserver doesn't Write() */
+ if (descriptor_IsSet(&bundle->ncp.mp.server.desc, fdset))
+ if (descriptor_Write(&bundle->ncp.mp.server.desc, bundle, fdset) == 1)
+ result++;
+
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (descriptor_IsSet(&dl->desc, fdset))
+ switch (descriptor_Write(&dl->desc, bundle, fdset)) {
+ case -1:
+ datalink_ComeDown(dl, CLOSE_NORMAL);
+ break;
+ case 1:
+ result++;
+ }
+
+ return result;
+}
+
+void
+bundle_LockTun(struct bundle *bundle)
+{
+ FILE *lockfile;
+ char pidfile[PATH_MAX];
+
+ snprintf(pidfile, sizeof pidfile, "%stun%d.pid", _PATH_VARRUN, bundle->unit);
+ lockfile = ID0fopen(pidfile, "w");
+ if (lockfile != NULL) {
+ fprintf(lockfile, "%d\n", (int)getpid());
+ fclose(lockfile);
+ }
+#ifndef RELEASE_CRUNCH
+ else
+ log_Printf(LogERROR, "Warning: Can't create %s: %s\n",
+ pidfile, strerror(errno));
+#endif
+}
+
+static void
+bundle_UnlockTun(struct bundle *bundle)
+{
+ char pidfile[PATH_MAX];
+
+ snprintf(pidfile, sizeof pidfile, "%stun%d.pid", _PATH_VARRUN, bundle->unit);
+ ID0unlink(pidfile);
+}
+
+struct bundle *
+bundle_Create(const char *prefix, int type, int unit)
+{
+ static struct bundle bundle; /* there can be only one */
+ int enoentcount, err, minunit, maxunit;
+ const char *ifname;
+#if defined(__FreeBSD__) && !defined(NOKLDLOAD)
+ int kldtried;
+#endif
+#if defined(TUNSIFMODE) || defined(TUNSLMODE) || defined(TUNSIFHEAD)
+ int iff;
+#endif
+
+ if (bundle.iface != NULL) { /* Already allocated ! */
+ log_Printf(LogALERT, "bundle_Create: There's only one BUNDLE !\n");
+ return NULL;
+ }
+
+ if (unit == -1) {
+ minunit = 0;
+ maxunit = -1;
+ } else {
+ minunit = unit;
+ maxunit = unit + 1;
+ }
+ err = ENOENT;
+ enoentcount = 0;
+#if defined(__FreeBSD__) && !defined(NOKLDLOAD)
+ kldtried = 0;
+#endif
+ for (bundle.unit = minunit; bundle.unit != maxunit; bundle.unit++) {
+ snprintf(bundle.dev.Name, sizeof bundle.dev.Name, "%s%d",
+ prefix, bundle.unit);
+ bundle.dev.fd = ID0open(bundle.dev.Name, O_RDWR);
+ if (bundle.dev.fd >= 0)
+ break;
+ else if (errno == ENXIO || errno == ENOENT) {
+#if defined(__FreeBSD__) && !defined(NOKLDLOAD)
+ if (bundle.unit == minunit && !kldtried++) {
+ /*
+ * Attempt to load the tunnel interface KLD if it isn't loaded
+ * already.
+ */
+ if (loadmodules(LOAD_VERBOSLY, "if_tun", NULL))
+ bundle.unit--;
+ continue;
+ }
+#endif
+ if (errno != ENOENT || ++enoentcount > 2) {
+ err = errno;
+ break;
+ }
+ } else
+ err = errno;
+ }
+
+ if (bundle.dev.fd < 0) {
+ if (unit == -1)
+ log_Printf(LogWARN, "No available tunnel devices found (%s)\n",
+ strerror(err));
+ else
+ log_Printf(LogWARN, "%s%d: %s\n", prefix, unit, strerror(err));
+ return NULL;
+ }
+
+ log_SetTun(bundle.unit);
+
+ ifname = strrchr(bundle.dev.Name, '/');
+ if (ifname == NULL)
+ ifname = bundle.dev.Name;
+ else
+ ifname++;
+
+ bundle.iface = iface_Create(ifname);
+ if (bundle.iface == NULL) {
+ close(bundle.dev.fd);
+ return NULL;
+ }
+
+#ifdef TUNSIFMODE
+ /* Make sure we're POINTOPOINT & IFF_MULTICAST */
+ iff = IFF_POINTOPOINT | IFF_MULTICAST;
+ if (ID0ioctl(bundle.dev.fd, TUNSIFMODE, &iff) < 0)
+ log_Printf(LogERROR, "bundle_Create: ioctl(TUNSIFMODE): %s\n",
+ strerror(errno));
+#endif
+
+#ifdef TUNSLMODE
+ /* Make sure we're not prepending sockaddrs */
+ iff = 0;
+ if (ID0ioctl(bundle.dev.fd, TUNSLMODE, &iff) < 0)
+ log_Printf(LogERROR, "bundle_Create: ioctl(TUNSLMODE): %s\n",
+ strerror(errno));
+#endif
+
+#ifdef TUNSIFHEAD
+ /* We want the address family please ! */
+ iff = 1;
+ if (ID0ioctl(bundle.dev.fd, TUNSIFHEAD, &iff) < 0) {
+ log_Printf(LogERROR, "bundle_Create: ioctl(TUNSIFHEAD): %s\n",
+ strerror(errno));
+ bundle.dev.header = 0;
+ } else
+ bundle.dev.header = 1;
+#else
+#ifdef __OpenBSD__
+ /* Always present for OpenBSD */
+ bundle.dev.header = 1;
+#else
+ /*
+ * If TUNSIFHEAD isn't available and we're not OpenBSD, assume
+ * everything's AF_INET (hopefully the tun device won't pass us
+ * anything else !).
+ */
+ bundle.dev.header = 0;
+#endif
+#endif
+
+ log_Printf(LogPHASE, "Using interface: %s\n", ifname);
+
+ bundle.bandwidth = 0;
+ bundle.routing_seq = 0;
+ bundle.phase = PHASE_DEAD;
+ bundle.CleaningUp = 0;
+ bundle.NatEnabled = 0;
+
+ bundle.fsm.LayerStart = bundle_LayerStart;
+ bundle.fsm.LayerUp = bundle_LayerUp;
+ bundle.fsm.LayerDown = bundle_LayerDown;
+ bundle.fsm.LayerFinish = bundle_LayerFinish;
+ bundle.fsm.object = &bundle;
+
+ bundle.cfg.idle.timeout = NCP_IDLE_TIMEOUT;
+ bundle.cfg.idle.min_timeout = 0;
+ *bundle.cfg.auth.name = '\0';
+ *bundle.cfg.auth.key = '\0';
+ bundle.cfg.optmask = (1ull << OPT_IDCHECK) | (1ull << OPT_LOOPBACK) |
+ (1ull << OPT_SROUTES) | (1ull << OPT_TCPMSSFIXUP) |
+ (1ull << OPT_THROUGHPUT) | (1ull << OPT_UTMP) |
+ (1ull << OPT_NAS_IP_ADDRESS) |
+ (1ull << OPT_NAS_IDENTIFIER);
+#ifndef NOINET6
+ opt_enable(&bundle, OPT_IPCP);
+ if (probe.ipv6_available)
+ opt_enable(&bundle, OPT_IPV6CP);
+#endif
+ *bundle.cfg.label = '\0';
+ bundle.cfg.ifqueue = DEF_IFQUEUE;
+ bundle.cfg.choked.timeout = CHOKED_TIMEOUT;
+ bundle.phys_type.all = type;
+ bundle.phys_type.open = 0;
+ bundle.upat = 0;
+
+ bundle.links = datalink_Create("deflink", &bundle, type);
+ if (bundle.links == NULL) {
+ log_Printf(LogALERT, "Cannot create data link: %s\n", strerror(errno));
+ iface_Destroy(bundle.iface);
+ bundle.iface = NULL;
+ close(bundle.dev.fd);
+ return NULL;
+ }
+
+ bundle.desc.type = BUNDLE_DESCRIPTOR;
+ bundle.desc.UpdateSet = bundle_UpdateSet;
+ bundle.desc.IsSet = bundle_IsSet;
+ bundle.desc.Read = bundle_DescriptorRead;
+ bundle.desc.Write = bundle_DescriptorWrite;
+
+ ncp_Init(&bundle.ncp, &bundle);
+
+ memset(&bundle.filter, '\0', sizeof bundle.filter);
+ bundle.filter.in.fragok = bundle.filter.in.logok = 1;
+ bundle.filter.in.name = "IN";
+ bundle.filter.out.fragok = bundle.filter.out.logok = 1;
+ bundle.filter.out.name = "OUT";
+ bundle.filter.dial.name = "DIAL";
+ bundle.filter.dial.logok = 1;
+ bundle.filter.alive.name = "ALIVE";
+ bundle.filter.alive.logok = 1;
+ {
+ int i;
+ for (i = 0; i < MAXFILTERS; i++) {
+ bundle.filter.in.rule[i].f_action = A_NONE;
+ bundle.filter.out.rule[i].f_action = A_NONE;
+ bundle.filter.dial.rule[i].f_action = A_NONE;
+ bundle.filter.alive.rule[i].f_action = A_NONE;
+ }
+ }
+ memset(&bundle.idle.timer, '\0', sizeof bundle.idle.timer);
+ bundle.idle.done = 0;
+ bundle.notify.fd = -1;
+ memset(&bundle.choked.timer, '\0', sizeof bundle.choked.timer);
+#ifndef NORADIUS
+ radius_Init(&bundle.radius);
+#endif
+
+ /* Clean out any leftover crud */
+ iface_Clear(bundle.iface, &bundle.ncp, 0, IFACE_CLEAR_ALL);
+
+ bundle_LockTun(&bundle);
+
+ return &bundle;
+}
+
+static void
+bundle_DownInterface(struct bundle *bundle)
+{
+ route_IfDelete(bundle, 1);
+ iface_ClearFlags(bundle->iface->name, IFF_UP);
+}
+
+void
+bundle_Destroy(struct bundle *bundle)
+{
+ struct datalink *dl;
+
+ /*
+ * Clean up the interface. We don't really need to do the timer_Stop()s,
+ * mp_Down(), iface_Clear() and bundle_DownInterface() unless we're getting
+ * out under exceptional conditions such as a descriptor exception.
+ */
+ timer_Stop(&bundle->idle.timer);
+ timer_Stop(&bundle->choked.timer);
+ mp_Down(&bundle->ncp.mp);
+ iface_Clear(bundle->iface, &bundle->ncp, 0, IFACE_CLEAR_ALL);
+ bundle_DownInterface(bundle);
+
+#ifndef NORADIUS
+ /* Tell the radius server the bad news */
+ radius_Destroy(&bundle->radius);
+#endif
+
+ /* Again, these are all DATALINK_CLOSED unless we're abending */
+ dl = bundle->links;
+ while (dl)
+ dl = datalink_Destroy(dl);
+
+ ncp_Destroy(&bundle->ncp);
+
+ close(bundle->dev.fd);
+ bundle_UnlockTun(bundle);
+
+ /* In case we never made PHASE_NETWORK */
+ bundle_Notify(bundle, EX_ERRDEAD);
+
+ iface_Destroy(bundle->iface);
+ bundle->iface = NULL;
+}
+
+void
+bundle_LinkClosed(struct bundle *bundle, struct datalink *dl)
+{
+ /*
+ * Our datalink has closed.
+ * CleanDatalinks() (called from DoLoop()) will remove closed
+ * BACKGROUND, FOREGROUND and DIRECT links.
+ * If it's the last data link, enter phase DEAD.
+ *
+ * NOTE: dl may not be in our list (bundle_SendDatalink()) !
+ */
+
+ struct datalink *odl;
+ int other_links;
+
+ log_SetTtyCommandMode(dl);
+
+ other_links = 0;
+ for (odl = bundle->links; odl; odl = odl->next)
+ if (odl != dl && odl->state != DATALINK_CLOSED)
+ other_links++;
+
+ if (!other_links) {
+ if (dl->physical->type != PHYS_AUTO) /* Not in -auto mode */
+ bundle_DownInterface(bundle);
+ ncp2initial(&bundle->ncp);
+ mp_Down(&bundle->ncp.mp);
+ bundle_NewPhase(bundle, PHASE_DEAD);
+#ifndef NORADIUS
+ if (bundle->radius.sessiontime)
+ bundle_StopSessionTimer(bundle);
+#endif
+ bundle_StopIdleTimer(bundle);
+ }
+}
+
+void
+bundle_Open(struct bundle *bundle, const char *name, int mask, int force)
+{
+ /*
+ * Please open the given datalink, or all if name == NULL
+ */
+ struct datalink *dl;
+
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (name == NULL || !strcasecmp(dl->name, name)) {
+ if ((mask & dl->physical->type) &&
+ (dl->state == DATALINK_CLOSED ||
+ (force && dl->state == DATALINK_OPENING &&
+ dl->dial.timer.state == TIMER_RUNNING) ||
+ dl->state == DATALINK_READY)) {
+ timer_Stop(&dl->dial.timer); /* We're finished with this */
+ datalink_Up(dl, 1, 1);
+ if (mask & PHYS_AUTO)
+ break; /* Only one AUTO link at a time */
+ }
+ if (name != NULL)
+ break;
+ }
+}
+
+struct datalink *
+bundle2datalink(struct bundle *bundle, const char *name)
+{
+ struct datalink *dl;
+
+ if (name != NULL) {
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (!strcasecmp(dl->name, name))
+ return dl;
+ } else if (bundle->links && !bundle->links->next)
+ return bundle->links;
+
+ return NULL;
+}
+
+int
+bundle_ShowLinks(struct cmdargs const *arg)
+{
+ struct datalink *dl;
+ struct pppThroughput *t;
+ unsigned long long octets;
+ int secs;
+
+ for (dl = arg->bundle->links; dl; dl = dl->next) {
+ octets = MAX(dl->physical->link.stats.total.in.OctetsPerSecond,
+ dl->physical->link.stats.total.out.OctetsPerSecond);
+
+ prompt_Printf(arg->prompt, "Name: %s [%s, %s]",
+ dl->name, mode2Nam(dl->physical->type), datalink_State(dl));
+ if (dl->physical->link.stats.total.rolling && dl->state == DATALINK_OPEN)
+ prompt_Printf(arg->prompt, " bandwidth %d, %llu bps (%llu bytes/sec)",
+ dl->mp.bandwidth ? dl->mp.bandwidth :
+ physical_GetSpeed(dl->physical),
+ octets * 8, octets);
+ prompt_Printf(arg->prompt, "\n");
+ }
+
+ t = &arg->bundle->ncp.mp.link.stats.total;
+ octets = MAX(t->in.OctetsPerSecond, t->out.OctetsPerSecond);
+ secs = t->downtime ? 0 : throughput_uptime(t);
+ if (secs > t->SamplePeriod)
+ secs = t->SamplePeriod;
+ if (secs)
+ prompt_Printf(arg->prompt, "Currently averaging %llu bps (%llu bytes/sec)"
+ " over the last %d secs\n", octets * 8, octets, secs);
+
+ return 0;
+}
+
+static const char *
+optval(struct bundle *bundle, int opt)
+{
+ return Enabled(bundle, opt) ? "enabled" : "disabled";
+}
+
+int
+bundle_ShowStatus(struct cmdargs const *arg)
+{
+ int remaining;
+
+ prompt_Printf(arg->prompt, "Phase %s\n", bundle_PhaseName(arg->bundle));
+ prompt_Printf(arg->prompt, " Device: %s\n", arg->bundle->dev.Name);
+ prompt_Printf(arg->prompt, " Interface: %s @ %lubps",
+ arg->bundle->iface->name, arg->bundle->bandwidth);
+
+ if (arg->bundle->upat) {
+ int secs = bundle_Uptime(arg->bundle);
+
+ prompt_Printf(arg->prompt, ", up time %d:%02d:%02d", secs / 3600,
+ (secs / 60) % 60, secs % 60);
+ }
+ prompt_Printf(arg->prompt, "\n Queued: %lu of %u\n",
+ (unsigned long)ncp_QueueLen(&arg->bundle->ncp),
+ arg->bundle->cfg.ifqueue);
+
+ prompt_Printf(arg->prompt, "\nDefaults:\n");
+ prompt_Printf(arg->prompt, " Label: %s\n",
+ arg->bundle->cfg.label);
+ prompt_Printf(arg->prompt, " Auth name: %s\n",
+ arg->bundle->cfg.auth.name);
+ prompt_Printf(arg->prompt, " Diagnostic socket: ");
+ if (*server.cfg.sockname != '\0') {
+ prompt_Printf(arg->prompt, "%s", server.cfg.sockname);
+ if (server.cfg.mask != (mode_t)-1)
+ prompt_Printf(arg->prompt, ", mask 0%03o", (int)server.cfg.mask);
+ prompt_Printf(arg->prompt, "%s\n", server.fd == -1 ? " (not open)" : "");
+ } else if (server.cfg.port != 0)
+ prompt_Printf(arg->prompt, "TCP port %d%s\n", server.cfg.port,
+ server.fd == -1 ? " (not open)" : "");
+ else
+ prompt_Printf(arg->prompt, "none\n");
+
+ prompt_Printf(arg->prompt, " Choked Timer: %us\n",
+ arg->bundle->cfg.choked.timeout);
+
+#ifndef NORADIUS
+ radius_Show(&arg->bundle->radius, arg->prompt);
+#endif
+
+ prompt_Printf(arg->prompt, " Idle Timer: ");
+ if (arg->bundle->cfg.idle.timeout) {
+ prompt_Printf(arg->prompt, "%us", arg->bundle->cfg.idle.timeout);
+ if (arg->bundle->cfg.idle.min_timeout)
+ prompt_Printf(arg->prompt, ", min %us",
+ arg->bundle->cfg.idle.min_timeout);
+ remaining = bundle_RemainingIdleTime(arg->bundle);
+ if (remaining != -1)
+ prompt_Printf(arg->prompt, " (%ds remaining)", remaining);
+ prompt_Printf(arg->prompt, "\n");
+ } else
+ prompt_Printf(arg->prompt, "disabled\n");
+
+ prompt_Printf(arg->prompt, " Filter Decap: %-20.20s",
+ optval(arg->bundle, OPT_FILTERDECAP));
+ prompt_Printf(arg->prompt, " ID check: %s\n",
+ optval(arg->bundle, OPT_IDCHECK));
+ prompt_Printf(arg->prompt, " Iface-Alias: %-20.20s",
+ optval(arg->bundle, OPT_IFACEALIAS));
+#ifndef NOINET6
+ prompt_Printf(arg->prompt, " IPCP: %s\n",
+ optval(arg->bundle, OPT_IPCP));
+ prompt_Printf(arg->prompt, " IPV6CP: %-20.20s",
+ optval(arg->bundle, OPT_IPV6CP));
+#endif
+ prompt_Printf(arg->prompt, " Keep-Session: %s\n",
+ optval(arg->bundle, OPT_KEEPSESSION));
+ prompt_Printf(arg->prompt, " Loopback: %-20.20s",
+ optval(arg->bundle, OPT_LOOPBACK));
+ prompt_Printf(arg->prompt, " PasswdAuth: %s\n",
+ optval(arg->bundle, OPT_PASSWDAUTH));
+ prompt_Printf(arg->prompt, " Proxy: %-20.20s",
+ optval(arg->bundle, OPT_PROXY));
+ prompt_Printf(arg->prompt, " Proxyall: %s\n",
+ optval(arg->bundle, OPT_PROXYALL));
+ prompt_Printf(arg->prompt, " Sticky Routes: %-20.20s",
+ optval(arg->bundle, OPT_SROUTES));
+ prompt_Printf(arg->prompt, " TCPMSS Fixup: %s\n",
+ optval(arg->bundle, OPT_TCPMSSFIXUP));
+ prompt_Printf(arg->prompt, " Throughput: %-20.20s",
+ optval(arg->bundle, OPT_THROUGHPUT));
+ prompt_Printf(arg->prompt, " Utmp Logging: %s\n",
+ optval(arg->bundle, OPT_UTMP));
+ prompt_Printf(arg->prompt, " NAS-IP-Address: %-20.20s",
+ optval(arg->bundle, OPT_NAS_IP_ADDRESS));
+ prompt_Printf(arg->prompt, " NAS-Identifier: %s\n",
+ optval(arg->bundle, OPT_NAS_IDENTIFIER));
+
+ return 0;
+}
+
+static void
+bundle_IdleTimeout(void *v)
+{
+ struct bundle *bundle = (struct bundle *)v;
+
+ log_Printf(LogPHASE, "Idle timer expired\n");
+ bundle_StopIdleTimer(bundle);
+ bundle_Close(bundle, NULL, CLOSE_STAYDOWN);
+}
+
+/*
+ * Start Idle timer. If timeout is reached, we call bundle_Close() to
+ * close LCP and link.
+ */
+void
+bundle_StartIdleTimer(struct bundle *bundle, unsigned secs)
+{
+ timer_Stop(&bundle->idle.timer);
+ if ((bundle->phys_type.open & (PHYS_DEDICATED|PHYS_DDIAL)) !=
+ bundle->phys_type.open && bundle->cfg.idle.timeout) {
+ time_t now = time(NULL);
+
+ if (secs == 0)
+ secs = bundle->cfg.idle.timeout;
+
+ /* We want at least `secs' */
+ if (bundle->cfg.idle.min_timeout > secs && bundle->upat) {
+ unsigned up = now - bundle->upat;
+
+ if (bundle->cfg.idle.min_timeout > up &&
+ bundle->cfg.idle.min_timeout - up > (long long)secs)
+ /* Only increase from the current `remaining' value */
+ secs = bundle->cfg.idle.min_timeout - up;
+ }
+ bundle->idle.timer.func = bundle_IdleTimeout;
+ bundle->idle.timer.name = "idle";
+ bundle->idle.timer.load = secs * SECTICKS;
+ bundle->idle.timer.arg = bundle;
+ timer_Start(&bundle->idle.timer);
+ bundle->idle.done = now + secs;
+ }
+}
+
+void
+bundle_SetIdleTimer(struct bundle *bundle, unsigned timeout,
+ unsigned min_timeout)
+{
+ bundle->cfg.idle.timeout = timeout;
+ bundle->cfg.idle.min_timeout = min_timeout;
+ if (ncp_LayersOpen(&bundle->ncp))
+ bundle_StartIdleTimer(bundle, 0);
+}
+
+void
+bundle_StopIdleTimer(struct bundle *bundle)
+{
+ timer_Stop(&bundle->idle.timer);
+ bundle->idle.done = 0;
+}
+
+static int
+bundle_RemainingIdleTime(struct bundle *bundle)
+{
+ if (bundle->idle.done)
+ return bundle->idle.done - time(NULL);
+ return -1;
+}
+
+#ifndef NORADIUS
+
+static void
+bundle_SessionTimeout(void *v)
+{
+ struct bundle *bundle = (struct bundle *)v;
+
+ log_Printf(LogPHASE, "Session-Timeout timer expired\n");
+ bundle_StopSessionTimer(bundle);
+ bundle_Close(bundle, NULL, CLOSE_STAYDOWN);
+}
+
+void
+bundle_StartSessionTimer(struct bundle *bundle, unsigned secs)
+{
+ timer_Stop(&bundle->session.timer);
+ if ((bundle->phys_type.open & (PHYS_DEDICATED|PHYS_DDIAL)) !=
+ bundle->phys_type.open && bundle->radius.sessiontime) {
+ time_t now = time(NULL);
+
+ if (secs == 0)
+ secs = bundle->radius.sessiontime;
+
+ bundle->session.timer.func = bundle_SessionTimeout;
+ bundle->session.timer.name = "session";
+ bundle->session.timer.load = secs * SECTICKS;
+ bundle->session.timer.arg = bundle;
+ timer_Start(&bundle->session.timer);
+ bundle->session.done = now + secs;
+ }
+}
+
+void
+bundle_StopSessionTimer(struct bundle *bundle)
+{
+ timer_Stop(&bundle->session.timer);
+ bundle->session.done = 0;
+}
+
+#endif
+
+int
+bundle_IsDead(struct bundle *bundle)
+{
+ return !bundle->links || (bundle->phase == PHASE_DEAD && bundle->CleaningUp);
+}
+
+static struct datalink *
+bundle_DatalinkLinkout(struct bundle *bundle, struct datalink *dl)
+{
+ struct datalink **dlp;
+
+ for (dlp = &bundle->links; *dlp; dlp = &(*dlp)->next)
+ if (*dlp == dl) {
+ *dlp = dl->next;
+ dl->next = NULL;
+ bundle_LinksRemoved(bundle);
+ return dl;
+ }
+
+ return NULL;
+}
+
+static void
+bundle_DatalinkLinkin(struct bundle *bundle, struct datalink *dl)
+{
+ struct datalink **dlp = &bundle->links;
+
+ while (*dlp)
+ dlp = &(*dlp)->next;
+
+ *dlp = dl;
+ dl->next = NULL;
+
+ bundle_LinkAdded(bundle, dl);
+ mp_CheckAutoloadTimer(&bundle->ncp.mp);
+}
+
+void
+bundle_CleanDatalinks(struct bundle *bundle)
+{
+ struct datalink **dlp = &bundle->links;
+ int found = 0;
+
+ while (*dlp)
+ if ((*dlp)->state == DATALINK_CLOSED &&
+ (*dlp)->physical->type &
+ (PHYS_DIRECT|PHYS_BACKGROUND|PHYS_FOREGROUND)) {
+ *dlp = datalink_Destroy(*dlp);
+ found++;
+ } else
+ dlp = &(*dlp)->next;
+
+ if (found)
+ bundle_LinksRemoved(bundle);
+}
+
+int
+bundle_DatalinkClone(struct bundle *bundle, struct datalink *dl,
+ const char *name)
+{
+ if (bundle2datalink(bundle, name)) {
+ log_Printf(LogWARN, "Clone: %s: name already exists\n", name);
+ return 0;
+ }
+
+ bundle_DatalinkLinkin(bundle, datalink_Clone(dl, name));
+ return 1;
+}
+
+void
+bundle_DatalinkRemove(struct bundle *bundle, struct datalink *dl)
+{
+ dl = bundle_DatalinkLinkout(bundle, dl);
+ if (dl)
+ datalink_Destroy(dl);
+}
+
+void
+bundle_SetLabel(struct bundle *bundle, const char *label)
+{
+ if (label)
+ strncpy(bundle->cfg.label, label, sizeof bundle->cfg.label - 1);
+ else
+ *bundle->cfg.label = '\0';
+}
+
+const char *
+bundle_GetLabel(struct bundle *bundle)
+{
+ return *bundle->cfg.label ? bundle->cfg.label : NULL;
+}
+
+int
+bundle_LinkSize()
+{
+ struct iovec iov[SCATTER_SEGMENTS];
+ int niov, expect, f;
+
+ iov[0].iov_len = strlen(Version) + 1;
+ iov[0].iov_base = NULL;
+ niov = 1;
+ if (datalink2iov(NULL, iov, &niov, SCATTER_SEGMENTS, NULL, NULL) == -1) {
+ log_Printf(LogERROR, "Cannot determine space required for link\n");
+ return 0;
+ }
+
+ for (f = expect = 0; f < niov; f++)
+ expect += iov[f].iov_len;
+
+ return expect;
+}
+
+void
+bundle_ReceiveDatalink(struct bundle *bundle, int s)
+{
+ char cmsgbuf[sizeof(struct cmsghdr) + sizeof(int) * SEND_MAXFD];
+ int niov, expect, f, *fd, nfd, onfd;
+ ssize_t got;
+ struct iovec iov[SCATTER_SEGMENTS];
+ struct cmsghdr *cmsg;
+ struct msghdr msg;
+ struct datalink *dl;
+ pid_t pid;
+
+ log_Printf(LogPHASE, "Receiving datalink\n");
+
+ /*
+ * Create our scatter/gather array - passing NULL gets the space
+ * allocation requirement rather than actually flattening the
+ * structures.
+ */
+ iov[0].iov_len = strlen(Version) + 1;
+ iov[0].iov_base = NULL;
+ niov = 1;
+ if (datalink2iov(NULL, iov, &niov, SCATTER_SEGMENTS, NULL, NULL) == -1) {
+ log_Printf(LogERROR, "Cannot determine space required for link\n");
+ return;
+ }
+
+ /* Allocate the scatter/gather array for recvmsg() */
+ for (f = expect = 0; f < niov; f++) {
+ if ((iov[f].iov_base = malloc(iov[f].iov_len)) == NULL) {
+ log_Printf(LogERROR, "Cannot allocate space to receive link\n");
+ return;
+ }
+ if (f)
+ expect += iov[f].iov_len;
+ }
+
+ /* Set up our message */
+ cmsg = (struct cmsghdr *)cmsgbuf;
+ cmsg->cmsg_len = sizeof cmsgbuf;
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = 0;
+
+ memset(&msg, '\0', sizeof msg);
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1; /* Only send the version at the first pass */
+ msg.msg_control = cmsgbuf;
+ msg.msg_controllen = sizeof cmsgbuf;
+
+ log_Printf(LogDEBUG, "Expecting %u scatter/gather bytes\n",
+ (unsigned)iov[0].iov_len);
+
+ if ((got = recvmsg(s, &msg, MSG_WAITALL)) != (ssize_t)iov[0].iov_len) {
+ if (got == -1)
+ log_Printf(LogERROR, "Failed recvmsg: %s\n", strerror(errno));
+ else
+ log_Printf(LogERROR, "Failed recvmsg: Got %zd, not %u\n",
+ got, (unsigned)iov[0].iov_len);
+ while (niov--)
+ free(iov[niov].iov_base);
+ return;
+ }
+
+ if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) {
+ log_Printf(LogERROR, "Recvmsg: no descriptors received !\n");
+ while (niov--)
+ free(iov[niov].iov_base);
+ return;
+ }
+
+ fd = (int *)CMSG_DATA(cmsg);
+ nfd = ((caddr_t)cmsg + cmsg->cmsg_len - (caddr_t)fd) / sizeof(int);
+
+ if (nfd < 2) {
+ log_Printf(LogERROR, "Recvmsg: %d descriptor%s received (too few) !\n",
+ nfd, nfd == 1 ? "" : "s");
+ while (nfd--)
+ close(fd[nfd]);
+ while (niov--)
+ free(iov[niov].iov_base);
+ return;
+ }
+
+ /*
+ * We've successfully received two or more open file descriptors
+ * through our socket, plus a version string. Make sure it's the
+ * correct version, and drop the connection if it's not.
+ */
+ if (strncmp(Version, iov[0].iov_base, iov[0].iov_len)) {
+ log_Printf(LogWARN, "Cannot receive datalink, incorrect version"
+ " (\"%.*s\", not \"%s\")\n", (int)iov[0].iov_len,
+ (char *)iov[0].iov_base, Version);
+ while (nfd--)
+ close(fd[nfd]);
+ while (niov--)
+ free(iov[niov].iov_base);
+ return;
+ }
+
+ /*
+ * Everything looks good. Send the other side our process id so that
+ * they can transfer lock ownership, and wait for them to send the
+ * actual link data.
+ */
+ pid = getpid();
+ if ((got = write(fd[1], &pid, sizeof pid)) != sizeof pid) {
+ if (got == -1)
+ log_Printf(LogERROR, "Failed write: %s\n", strerror(errno));
+ else
+ log_Printf(LogERROR, "Failed write: Got %zd, not %d\n", got,
+ (int)(sizeof pid));
+ while (nfd--)
+ close(fd[nfd]);
+ while (niov--)
+ free(iov[niov].iov_base);
+ return;
+ }
+
+ if ((got = readv(fd[1], iov + 1, niov - 1)) != expect) {
+ if (got == -1)
+ log_Printf(LogERROR, "Failed write: %s\n", strerror(errno));
+ else
+ log_Printf(LogERROR, "Failed write: Got %zd, not %d\n", got, expect);
+ while (nfd--)
+ close(fd[nfd]);
+ while (niov--)
+ free(iov[niov].iov_base);
+ return;
+ }
+ close(fd[1]);
+
+ onfd = nfd; /* We've got this many in our array */
+ nfd -= 2; /* Don't include p->fd and our reply descriptor */
+ niov = 1; /* Skip the version id */
+ dl = iov2datalink(bundle, iov, &niov, sizeof iov / sizeof *iov, fd[0],
+ fd + 2, &nfd);
+ if (dl) {
+
+ if (nfd) {
+ log_Printf(LogERROR, "bundle_ReceiveDatalink: Failed to handle %d "
+ "auxiliary file descriptors (%d remain)\n", onfd, nfd);
+ datalink_Destroy(dl);
+ while (nfd--)
+ close(fd[onfd--]);
+ close(fd[0]);
+ } else {
+ bundle_DatalinkLinkin(bundle, dl);
+ datalink_AuthOk(dl);
+ bundle_CalculateBandwidth(dl->bundle);
+ }
+ } else {
+ while (nfd--)
+ close(fd[onfd--]);
+ close(fd[0]);
+ close(fd[1]);
+ }
+
+ free(iov[0].iov_base);
+}
+
+void
+bundle_SendDatalink(struct datalink *dl, int s, struct sockaddr_un *sun)
+{
+ char cmsgbuf[CMSG_SPACE(sizeof(int) * SEND_MAXFD)];
+ const char *constlock;
+ char *lock;
+ struct cmsghdr *cmsg;
+ struct msghdr msg;
+ struct iovec iov[SCATTER_SEGMENTS];
+ int niov, f, expect, newsid, fd[SEND_MAXFD], nfd, reply[2];
+ ssize_t got;
+ pid_t newpid;
+
+ log_Printf(LogPHASE, "Transmitting datalink %s\n", dl->name);
+
+ /* Record the base device name for a lock transfer later */
+ constlock = physical_LockedDevice(dl->physical);
+ if (constlock) {
+ lock = alloca(strlen(constlock) + 1);
+ strcpy(lock, constlock);
+ } else
+ lock = NULL;
+
+ bundle_LinkClosed(dl->bundle, dl);
+ bundle_DatalinkLinkout(dl->bundle, dl);
+
+ /* Build our scatter/gather array */
+ iov[0].iov_len = strlen(Version) + 1;
+ iov[0].iov_base = strdup(Version);
+ niov = 1;
+ nfd = 0;
+
+ fd[0] = datalink2iov(dl, iov, &niov, SCATTER_SEGMENTS, fd + 2, &nfd);
+
+ if (fd[0] != -1 && socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, reply) != -1) {
+ /*
+ * fd[1] is used to get the peer process id back, then to confirm that
+ * we've transferred any device locks to that process id.
+ */
+ fd[1] = reply[1];
+
+ nfd += 2; /* Include fd[0] and fd[1] */
+ memset(&msg, '\0', sizeof msg);
+
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+ /*
+ * Only send the version to start... We used to send the whole lot, but
+ * this caused problems with our RECVBUF size as a single link is about
+ * 22k ! This way, we should bump into no limits.
+ */
+ msg.msg_iovlen = 1;
+ msg.msg_iov = iov;
+ msg.msg_control = cmsgbuf;
+ msg.msg_controllen = CMSG_SPACE(sizeof(int) * nfd);
+ msg.msg_flags = 0;
+
+ cmsg = (struct cmsghdr *)cmsgbuf;
+ cmsg->cmsg_len = msg.msg_controllen;
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+
+ for (f = 0; f < nfd; f++)
+ *((int *)CMSG_DATA(cmsg) + f) = fd[f];
+
+ for (f = 1, expect = 0; f < niov; f++)
+ expect += iov[f].iov_len;
+
+ if (setsockopt(reply[0], SOL_SOCKET, SO_SNDBUF, &expect, sizeof(int)) == -1)
+ log_Printf(LogERROR, "setsockopt(SO_RCVBUF, %d): %s\n", expect,
+ strerror(errno));
+ if (setsockopt(reply[1], SOL_SOCKET, SO_RCVBUF, &expect, sizeof(int)) == -1)
+ log_Printf(LogERROR, "setsockopt(SO_RCVBUF, %d): %s\n", expect,
+ strerror(errno));
+
+ log_Printf(LogDEBUG, "Sending %d descriptor%s and %u bytes in scatter"
+ "/gather array\n", nfd, nfd == 1 ? "" : "s",
+ (unsigned)iov[0].iov_len);
+
+ if ((got = sendmsg(s, &msg, 0)) == -1)
+ log_Printf(LogERROR, "Failed sendmsg: %s: %s\n",
+ sun->sun_path, strerror(errno));
+ else if (got != (ssize_t)iov[0].iov_len)
+ log_Printf(LogERROR, "%s: Failed initial sendmsg: Only sent %zd of %u\n",
+ sun->sun_path, got, (unsigned)iov[0].iov_len);
+ else {
+ /* We must get the ACK before closing the descriptor ! */
+ int res;
+
+ if ((got = read(reply[0], &newpid, sizeof newpid)) == sizeof newpid) {
+ log_Printf(LogDEBUG, "Received confirmation from pid %ld\n",
+ (long)newpid);
+ if (lock && (res = ID0uu_lock_txfr(lock, newpid)) != UU_LOCK_OK)
+ log_Printf(LogERROR, "uu_lock_txfr: %s\n", uu_lockerr(res));
+
+ log_Printf(LogDEBUG, "Transmitting link (%d bytes)\n", expect);
+ if ((got = writev(reply[0], iov + 1, niov - 1)) != expect) {
+ if (got == -1)
+ log_Printf(LogERROR, "%s: Failed writev: %s\n",
+ sun->sun_path, strerror(errno));
+ else
+ log_Printf(LogERROR, "%s: Failed writev: Wrote %zd of %d\n",
+ sun->sun_path, got, expect);
+ }
+ } else if (got == -1)
+ log_Printf(LogERROR, "%s: Failed socketpair read: %s\n",
+ sun->sun_path, strerror(errno));
+ else
+ log_Printf(LogERROR, "%s: Failed socketpair read: Got %zd of %d\n",
+ sun->sun_path, got, (int)(sizeof newpid));
+ }
+
+ close(reply[0]);
+ close(reply[1]);
+
+ newsid = Enabled(dl->bundle, OPT_KEEPSESSION) ||
+ tcgetpgrp(fd[0]) == getpgrp();
+ while (nfd)
+ close(fd[--nfd]);
+ if (newsid)
+ bundle_setsid(dl->bundle, got != -1);
+ }
+ close(s);
+
+ while (niov--)
+ free(iov[niov].iov_base);
+}
+
+int
+bundle_RenameDatalink(struct bundle *bundle, struct datalink *ndl,
+ const char *name)
+{
+ struct datalink *dl;
+
+ if (!strcasecmp(ndl->name, name))
+ return 1;
+
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (!strcasecmp(dl->name, name))
+ return 0;
+
+ datalink_Rename(ndl, name);
+ return 1;
+}
+
+int
+bundle_SetMode(struct bundle *bundle, struct datalink *dl, int mode)
+{
+ int omode;
+
+ omode = dl->physical->type;
+ if (omode == mode)
+ return 1;
+
+ if (mode == PHYS_AUTO && !(bundle->phys_type.all & PHYS_AUTO))
+ /* First auto link */
+ if (bundle->ncp.ipcp.peer_ip.s_addr == INADDR_ANY) {
+ log_Printf(LogWARN, "You must `set ifaddr' or `open' before"
+ " changing mode to %s\n", mode2Nam(mode));
+ return 0;
+ }
+
+ if (!datalink_SetMode(dl, mode))
+ return 0;
+
+ if (mode == PHYS_AUTO && !(bundle->phys_type.all & PHYS_AUTO) &&
+ bundle->phase != PHASE_NETWORK)
+ /* First auto link, we need an interface */
+ ipcp_InterfaceUp(&bundle->ncp.ipcp);
+
+ /* Regenerate phys_type and adjust idle timer */
+ bundle_LinksRemoved(bundle);
+
+ return 1;
+}
+
+void
+bundle_setsid(struct bundle *bundle, int holdsession)
+{
+ /*
+ * Lose the current session. This means getting rid of our pid
+ * too so that the tty device will really go away, and any getty
+ * etc will be allowed to restart.
+ */
+ pid_t pid, orig;
+ int fds[2];
+ char done;
+ struct datalink *dl;
+
+ if (!holdsession && bundle_IsDead(bundle)) {
+ /*
+ * No need to lose our session after all... we're going away anyway
+ *
+ * We should really stop the timer and pause if holdsession is set and
+ * the bundle's dead, but that leaves other resources lying about :-(
+ */
+ return;
+ }
+
+ orig = getpid();
+ if (pipe(fds) == -1) {
+ log_Printf(LogERROR, "pipe: %s\n", strerror(errno));
+ return;
+ }
+ switch ((pid = fork())) {
+ case -1:
+ log_Printf(LogERROR, "fork: %s\n", strerror(errno));
+ close(fds[0]);
+ close(fds[1]);
+ return;
+ case 0:
+ close(fds[1]);
+ read(fds[0], &done, 1); /* uu_locks are mine ! */
+ close(fds[0]);
+ if (pipe(fds) == -1) {
+ log_Printf(LogERROR, "pipe(2): %s\n", strerror(errno));
+ return;
+ }
+ switch ((pid = fork())) {
+ case -1:
+ log_Printf(LogERROR, "fork(2): %s\n", strerror(errno));
+ close(fds[0]);
+ close(fds[1]);
+ return;
+ case 0:
+ close(fds[1]);
+ bundle_LockTun(bundle); /* update pid */
+ read(fds[0], &done, 1); /* uu_locks are mine ! */
+ close(fds[0]);
+ setsid();
+ bundle_ChangedPID(bundle);
+ log_Printf(LogDEBUG, "%ld -> %ld: %s session control\n",
+ (long)orig, (long)getpid(),
+ holdsession ? "Passed" : "Dropped");
+ timer_InitService(0); /* Start the Timer Service */
+ break;
+ default:
+ close(fds[0]);
+ /* Give away all our physical locks (to the final process) */
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (dl->state != DATALINK_CLOSED)
+ physical_ChangedPid(dl->physical, pid);
+ write(fds[1], "!", 1); /* done */
+ close(fds[1]);
+ _exit(0);
+ break;
+ }
+ break;
+ default:
+ close(fds[0]);
+ /* Give away all our physical locks (to the intermediate process) */
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (dl->state != DATALINK_CLOSED)
+ physical_ChangedPid(dl->physical, pid);
+ write(fds[1], "!", 1); /* done */
+ close(fds[1]);
+ if (holdsession) {
+ int fd, status;
+
+ timer_TermService();
+ signal(SIGPIPE, SIG_DFL);
+ signal(SIGALRM, SIG_DFL);
+ signal(SIGHUP, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ signal(SIGINT, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+ for (fd = getdtablesize(); fd >= 0; fd--)
+ close(fd);
+ /*
+ * Reap the intermediate process. As we're not exiting but the
+ * intermediate is, we don't want it to become defunct.
+ */
+ waitpid(pid, &status, 0);
+ /* Tweak our process arguments.... */
+ SetTitle("session owner");
+#ifndef NOSUID
+ setuid(ID0realuid());
+#endif
+ /*
+ * Hang around for a HUP. This should happen as soon as the
+ * ppp that we passed our ctty descriptor to closes it.
+ * NOTE: If this process dies, the passed descriptor becomes
+ * invalid and will give a select() error by setting one
+ * of the error fds, aborting the other ppp. We don't
+ * want that to happen !
+ */
+ pause();
+ }
+ _exit(0);
+ break;
+ }
+}
+
+unsigned
+bundle_HighestState(struct bundle *bundle)
+{
+ struct datalink *dl;
+ unsigned result = DATALINK_CLOSED;
+
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (result < dl->state)
+ result = dl->state;
+
+ return result;
+}
+
+int
+bundle_Exception(struct bundle *bundle, int fd)
+{
+ struct datalink *dl;
+
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (dl->physical->fd == fd) {
+ datalink_Down(dl, CLOSE_NORMAL);
+ return 1;
+ }
+
+ return 0;
+}
+
+void
+bundle_AdjustFilters(struct bundle *bundle, struct ncpaddr *local,
+ struct ncpaddr *remote)
+{
+ filter_AdjustAddr(&bundle->filter.in, local, remote, NULL);
+ filter_AdjustAddr(&bundle->filter.out, local, remote, NULL);
+ filter_AdjustAddr(&bundle->filter.dial, local, remote, NULL);
+ filter_AdjustAddr(&bundle->filter.alive, local, remote, NULL);
+}
+
+void
+bundle_AdjustDNS(struct bundle *bundle)
+{
+ struct in_addr *dns = bundle->ncp.ipcp.ns.dns;
+
+ filter_AdjustAddr(&bundle->filter.in, NULL, NULL, dns);
+ filter_AdjustAddr(&bundle->filter.out, NULL, NULL, dns);
+ filter_AdjustAddr(&bundle->filter.dial, NULL, NULL, dns);
+ filter_AdjustAddr(&bundle->filter.alive, NULL, NULL, dns);
+}
+
+void
+bundle_CalculateBandwidth(struct bundle *bundle)
+{
+ struct datalink *dl;
+ int sp, overhead, maxoverhead;
+
+ bundle->bandwidth = 0;
+ bundle->iface->mtu = 0;
+ maxoverhead = 0;
+
+ for (dl = bundle->links; dl; dl = dl->next) {
+ overhead = ccp_MTUOverhead(&dl->physical->link.ccp);
+ if (maxoverhead < overhead)
+ maxoverhead = overhead;
+ if (dl->state == DATALINK_OPEN) {
+ if ((sp = dl->mp.bandwidth) == 0 &&
+ (sp = physical_GetSpeed(dl->physical)) == 0)
+ log_Printf(LogDEBUG, "%s: %s: Cannot determine bandwidth\n",
+ dl->name, dl->physical->name.full);
+ else
+ bundle->bandwidth += sp;
+ if (!bundle->ncp.mp.active) {
+ bundle->iface->mtu = dl->physical->link.lcp.his_mru;
+ break;
+ }
+ }
+ }
+
+ if (bundle->bandwidth == 0)
+ bundle->bandwidth = 115200; /* Shrug */
+
+ if (bundle->ncp.mp.active) {
+ bundle->iface->mtu = bundle->ncp.mp.peer_mrru;
+ overhead = ccp_MTUOverhead(&bundle->ncp.mp.link.ccp);
+ if (maxoverhead < overhead)
+ maxoverhead = overhead;
+ } else if (!bundle->iface->mtu)
+ bundle->iface->mtu = DEF_MRU;
+
+#ifndef NORADIUS
+ if (bundle->radius.valid && bundle->radius.mtu &&
+ bundle->radius.mtu < bundle->iface->mtu) {
+ log_Printf(LogLCP, "Reducing MTU to radius value %lu\n",
+ bundle->radius.mtu);
+ bundle->iface->mtu = bundle->radius.mtu;
+ }
+#endif
+
+ if (maxoverhead) {
+ log_Printf(LogLCP, "Reducing MTU from %lu to %lu (CCP requirement)\n",
+ bundle->iface->mtu, bundle->iface->mtu - maxoverhead);
+ bundle->iface->mtu -= maxoverhead;
+ }
+
+ tun_configure(bundle);
+
+ route_UpdateMTU(bundle);
+}
+
+void
+bundle_AutoAdjust(struct bundle *bundle, int percent, int what)
+{
+ struct datalink *dl, *choice, *otherlinkup;
+
+ choice = otherlinkup = NULL;
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (dl->physical->type == PHYS_AUTO) {
+ if (dl->state == DATALINK_OPEN) {
+ if (what == AUTO_DOWN) {
+ if (choice)
+ otherlinkup = choice;
+ choice = dl;
+ }
+ } else if (dl->state == DATALINK_CLOSED) {
+ if (what == AUTO_UP) {
+ choice = dl;
+ break;
+ }
+ } else {
+ /* An auto link in an intermediate state - forget it for the moment */
+ choice = NULL;
+ break;
+ }
+ } else if (dl->state == DATALINK_OPEN && what == AUTO_DOWN)
+ otherlinkup = dl;
+
+ if (choice) {
+ if (what == AUTO_UP) {
+ log_Printf(LogPHASE, "%d%% saturation -> Opening link ``%s''\n",
+ percent, choice->name);
+ datalink_Up(choice, 1, 1);
+ mp_CheckAutoloadTimer(&bundle->ncp.mp);
+ } else if (otherlinkup) { /* Only bring the second-last link down */
+ log_Printf(LogPHASE, "%d%% saturation -> Closing link ``%s''\n",
+ percent, choice->name);
+ datalink_Close(choice, CLOSE_STAYDOWN);
+ mp_CheckAutoloadTimer(&bundle->ncp.mp);
+ }
+ }
+}
+
+int
+bundle_WantAutoloadTimer(struct bundle *bundle)
+{
+ struct datalink *dl;
+ int autolink, opened;
+
+ if (bundle->phase == PHASE_NETWORK) {
+ for (autolink = opened = 0, dl = bundle->links; dl; dl = dl->next)
+ if (dl->physical->type == PHYS_AUTO) {
+ if (++autolink == 2 || (autolink == 1 && opened))
+ /* Two auto links or one auto and one open in NETWORK phase */
+ return 1;
+ } else if (dl->state == DATALINK_OPEN) {
+ opened++;
+ if (autolink)
+ /* One auto and one open link in NETWORK phase */
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+void
+bundle_ChangedPID(struct bundle *bundle)
+{
+#ifdef TUNSIFPID
+ ioctl(bundle->dev.fd, TUNSIFPID, 0);
+#endif
+}
+
+int
+bundle_Uptime(struct bundle *bundle)
+{
+ if (bundle->upat)
+ return time(NULL) - bundle->upat;
+
+ return 0;
+}
diff --git a/usr.sbin/ppp/bundle.h b/usr.sbin/ppp/bundle.h
new file mode 100644
index 0000000..e2f9e7f
--- /dev/null
+++ b/usr.sbin/ppp/bundle.h
@@ -0,0 +1,216 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define PHASE_DEAD 0 /* Link is dead */
+#define PHASE_ESTABLISH 1 /* Establishing link */
+#define PHASE_AUTHENTICATE 2 /* Being authenticated */
+#define PHASE_NETWORK 3 /* We're alive ! */
+#define PHASE_TERMINATE 4 /* Terminating link */
+
+/* cfg.opt bit settings */
+#define OPT_FILTERDECAP 1
+#define OPT_FORCE_SCRIPTS 2 /* force chat scripts */
+#define OPT_IDCHECK 3
+#define OPT_IFACEALIAS 4
+#ifndef NOINET6
+#define OPT_IPCP 5
+#define OPT_IPV6CP 6
+#endif
+#define OPT_KEEPSESSION 7
+#define OPT_LOOPBACK 8
+#define OPT_NAS_IP_ADDRESS 9
+#define OPT_NAS_IDENTIFIER 10
+#define OPT_PASSWDAUTH 11
+#define OPT_PROXY 12
+#define OPT_PROXYALL 13
+#define OPT_SROUTES 14
+#define OPT_TCPMSSFIXUP 15
+#define OPT_THROUGHPUT 16
+#define OPT_UTMP 17
+#define OPT_MAX 17
+
+#define MAX_ENDDISC_CLASS 5
+
+#define Enabled(b, o) ((b)->cfg.optmask & (1ull << (o)))
+#define opt_enable(b, o) ((b)->cfg.optmask |= (1ull << (o)))
+#define opt_disable(b, o) ((b)->cfg.optmask &= ~(1ull << (o)))
+
+/* AutoAdjust() values */
+#define AUTO_UP 1
+#define AUTO_DOWN 2
+
+struct sockaddr_un;
+struct datalink;
+struct physical;
+struct link;
+struct server;
+struct prompt;
+struct iface;
+
+struct bundle {
+ struct fdescriptor desc; /* really all our datalinks */
+ int unit; /* The device/interface unit number */
+
+ struct {
+ char Name[20]; /* The /dev/XXXX name */
+ int fd; /* The /dev/XXXX descriptor */
+ unsigned header : 1; /* Family header sent & received ? */
+ } dev;
+
+ u_long bandwidth; /* struct tuninfo speed */
+ struct iface *iface; /* Interface information */
+
+ int routing_seq; /* The current routing sequence number */
+ u_int phase; /* Curent phase */
+
+ struct {
+ int all; /* Union of all physical::type's */
+ int open; /* Union of all open physical::type's */
+ } phys_type;
+
+ unsigned CleaningUp : 1; /* Going to exit.... */
+ unsigned NatEnabled : 1; /* Are we using libalias ? */
+
+ struct fsm_parent fsm; /* Our callback functions */
+ struct datalink *links; /* Our data links */
+
+ time_t upat; /* When the link came up */
+
+ struct {
+ struct {
+ unsigned timeout; /* NCP Idle timeout value */
+ unsigned min_timeout; /* Don't idle out before this */
+ } idle;
+ struct {
+ char name[AUTHLEN]; /* PAP/CHAP system name */
+ char key[AUTHLEN]; /* PAP/CHAP key */
+ } auth;
+ unsigned long long optmask; /* Uses OPT_ bits from above */
+ char label[50]; /* last thing `load'ed */
+ u_short ifqueue; /* Interface queue size */
+
+ struct {
+ unsigned timeout; /* How long to leave the output queue choked */
+ } choked;
+ } cfg;
+
+ struct ncp ncp;
+
+ struct {
+ struct filter in; /* incoming packet filter */
+ struct filter out; /* outgoing packet filter */
+ struct filter dial; /* dial-out packet filter */
+ struct filter alive; /* keep-alive packet filter */
+ } filter;
+
+ struct {
+ struct pppTimer timer; /* timeout after cfg.idle_timeout */
+ time_t done;
+ } idle;
+
+#ifndef NORADIUS
+ struct {
+ struct pppTimer timer;
+ time_t done;
+ } session;
+#endif
+
+ struct {
+ int fd; /* write status here */
+ } notify;
+
+ struct {
+ struct pppTimer timer; /* choked output queue timer */
+ } choked;
+
+#ifndef NORADIUS
+ struct radius radius; /* Info retrieved from radius server */
+ struct radacct radacct;
+#ifndef NOINET6
+ struct radacct radacct6;
+#endif
+#endif
+};
+
+#define descriptor2bundle(d) \
+ ((d)->type == BUNDLE_DESCRIPTOR ? (struct bundle *)(d) : NULL)
+
+extern struct bundle *bundle_Create(const char *, int, int);
+extern void bundle_Destroy(struct bundle *);
+extern const char *bundle_PhaseName(struct bundle *);
+#define bundle_Phase(b) ((b)->phase)
+extern void bundle_NewPhase(struct bundle *, u_int);
+extern void bundle_LinksRemoved(struct bundle *);
+extern void bundle_Close(struct bundle *, const char *, int);
+extern void bundle_Down(struct bundle *, int);
+extern void bundle_Open(struct bundle *, const char *, int, int);
+extern void bundle_LinkClosed(struct bundle *, struct datalink *);
+
+extern int bundle_ShowLinks(struct cmdargs const *);
+extern int bundle_ShowStatus(struct cmdargs const *);
+extern void bundle_StartIdleTimer(struct bundle *, unsigned secs);
+extern void bundle_SetIdleTimer(struct bundle *, unsigned, unsigned);
+extern void bundle_StopIdleTimer(struct bundle *);
+extern int bundle_IsDead(struct bundle *);
+extern struct datalink *bundle2datalink(struct bundle *, const char *);
+
+#ifndef NORADIUS
+extern void bundle_StartSessionTimer(struct bundle *, unsigned secs);
+extern void bundle_StopSessionTimer(struct bundle *);
+#endif
+
+extern void bundle_RegisterDescriptor(struct bundle *, struct fdescriptor *);
+extern void bundle_UnRegisterDescriptor(struct bundle *, struct fdescriptor *);
+
+extern void bundle_SetTtyCommandMode(struct bundle *, struct datalink *);
+
+extern int bundle_DatalinkClone(struct bundle *, struct datalink *,
+ const char *);
+extern void bundle_DatalinkRemove(struct bundle *, struct datalink *);
+extern void bundle_CleanDatalinks(struct bundle *);
+extern void bundle_SetLabel(struct bundle *, const char *);
+extern const char *bundle_GetLabel(struct bundle *);
+extern void bundle_SendDatalink(struct datalink *, int, struct sockaddr_un *);
+extern int bundle_LinkSize(void);
+extern void bundle_ReceiveDatalink(struct bundle *, int);
+extern int bundle_SetMode(struct bundle *, struct datalink *, int);
+extern int bundle_RenameDatalink(struct bundle *, struct datalink *,
+ const char *);
+extern void bundle_setsid(struct bundle *, int);
+extern void bundle_LockTun(struct bundle *);
+extern unsigned bundle_HighestState(struct bundle *);
+extern int bundle_Exception(struct bundle *, int);
+extern void bundle_AdjustFilters(struct bundle *, struct ncpaddr *,
+ struct ncpaddr *);
+extern void bundle_AdjustDNS(struct bundle *);
+extern void bundle_CalculateBandwidth(struct bundle *);
+extern void bundle_AutoAdjust(struct bundle *, int, int);
+extern int bundle_WantAutoloadTimer(struct bundle *);
+extern void bundle_ChangedPID(struct bundle *);
+extern void bundle_Notify(struct bundle *, char);
+extern int bundle_Uptime(struct bundle *);
diff --git a/usr.sbin/ppp/cbcp.c b/usr.sbin/ppp/cbcp.c
new file mode 100644
index 0000000..be68e48
--- /dev/null
+++ b/usr.sbin/ppp/cbcp.c
@@ -0,0 +1,763 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+
+#ifdef __FreeBSD__
+#include <netinet/in.h>
+#endif
+#include <sys/un.h>
+
+#include <string.h>
+#include <termios.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "log.h"
+#include "timer.h"
+#include "descriptor.h"
+#include "lqr.h"
+#include "mbuf.h"
+#include "fsm.h"
+#include "throughput.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "async.h"
+#include "physical.h"
+#include "proto.h"
+#include "cbcp.h"
+#include "mp.h"
+#include "chat.h"
+#include "auth.h"
+#include "chap.h"
+#include "datalink.h"
+
+void
+cbcp_Init(struct cbcp *cbcp, struct physical *p)
+{
+ cbcp->required = 0;
+ cbcp->fsm.state = CBCP_CLOSED;
+ cbcp->fsm.id = 0;
+ cbcp->fsm.delay = 0;
+ *cbcp->fsm.phone = '\0';
+ memset(&cbcp->fsm.timer, '\0', sizeof cbcp->fsm.timer);
+ cbcp->p = p;
+}
+
+static void cbcp_SendReq(struct cbcp *);
+static void cbcp_SendResponse(struct cbcp *);
+static void cbcp_SendAck(struct cbcp *);
+
+static void
+cbcp_Timeout(void *v)
+{
+ struct cbcp *cbcp = (struct cbcp *)v;
+
+ timer_Stop(&cbcp->fsm.timer);
+ if (cbcp->fsm.restart) {
+ switch (cbcp->fsm.state) {
+ case CBCP_CLOSED:
+ case CBCP_STOPPED:
+ log_Printf(LogCBCP, "%s: Urk - unexpected CBCP timeout !\n",
+ cbcp->p->dl->name);
+ break;
+
+ case CBCP_REQSENT:
+ cbcp_SendReq(cbcp);
+ break;
+ case CBCP_RESPSENT:
+ cbcp_SendResponse(cbcp);
+ break;
+ case CBCP_ACKSENT:
+ cbcp_SendAck(cbcp);
+ break;
+ }
+ } else {
+ const char *missed;
+
+ switch (cbcp->fsm.state) {
+ case CBCP_STOPPED:
+ missed = "REQ";
+ break;
+ case CBCP_REQSENT:
+ missed = "RESPONSE";
+ break;
+ case CBCP_RESPSENT:
+ missed = "ACK";
+ break;
+ case CBCP_ACKSENT:
+ missed = "Terminate REQ";
+ break;
+ default:
+ log_Printf(LogCBCP, "%s: Urk - unexpected CBCP timeout !\n",
+ cbcp->p->dl->name);
+ missed = NULL;
+ break;
+ }
+ if (missed)
+ log_Printf(LogCBCP, "%s: Timeout waiting for peer %s\n",
+ cbcp->p->dl->name, missed);
+ datalink_CBCPFailed(cbcp->p->dl);
+ }
+}
+
+static void
+cbcp_StartTimer(struct cbcp *cbcp, int timeout)
+{
+ timer_Stop(&cbcp->fsm.timer);
+ cbcp->fsm.timer.func = cbcp_Timeout;
+ cbcp->fsm.timer.name = "cbcp";
+ cbcp->fsm.timer.load = timeout * SECTICKS;
+ cbcp->fsm.timer.arg = cbcp;
+ timer_Start(&cbcp->fsm.timer);
+}
+
+#define CBCP_CLOSED (0) /* Not in use */
+#define CBCP_STOPPED (1) /* Waiting for a REQ */
+#define CBCP_REQSENT (2) /* Waiting for a RESP */
+#define CBCP_RESPSENT (3) /* Waiting for an ACK */
+#define CBCP_ACKSENT (4) /* Waiting for an LCP Term REQ */
+
+static const char * const cbcpname[] = {
+ "closed", "stopped", "req-sent", "resp-sent", "ack-sent"
+};
+
+static const char *
+cbcpstate(unsigned s)
+{
+ if (s < sizeof cbcpname / sizeof cbcpname[0])
+ return cbcpname[s];
+ return HexStr(s, NULL, 0);
+}
+
+static void
+cbcp_NewPhase(struct cbcp *cbcp, int new)
+{
+ if (cbcp->fsm.state != new) {
+ log_Printf(LogCBCP, "%s: State change %s --> %s\n", cbcp->p->dl->name,
+ cbcpstate(cbcp->fsm.state), cbcpstate(new));
+ cbcp->fsm.state = new;
+ }
+}
+
+struct cbcp_header {
+ u_char code;
+ u_char id;
+ u_int16_t length; /* Network byte order */
+};
+
+
+/* cbcp_header::code values */
+#define CBCP_REQ (1)
+#define CBCP_RESPONSE (2)
+#define CBCP_ACK (3)
+
+struct cbcp_data {
+ u_char type;
+ u_char length;
+ u_char delay;
+ char addr_start[253]; /* max cbcp_data length 255 + 1 for NULL */
+};
+
+/* cbcp_data::type values */
+#define CBCP_NONUM (1)
+#define CBCP_CLIENTNUM (2)
+#define CBCP_SERVERNUM (3)
+#define CBCP_LISTNUM (4)
+
+static void
+cbcp_Output(struct cbcp *cbcp, u_char code, struct cbcp_data *data)
+{
+ struct cbcp_header *head;
+ struct mbuf *bp;
+
+ bp = m_get(sizeof *head + data->length, MB_CBCPOUT);
+ head = (struct cbcp_header *)MBUF_CTOP(bp);
+ head->code = code;
+ head->id = cbcp->fsm.id;
+ head->length = htons(sizeof *head + data->length);
+ memcpy(MBUF_CTOP(bp) + sizeof *head, data, data->length);
+ log_DumpBp(LogDEBUG, "cbcp_Output", bp);
+ link_PushPacket(&cbcp->p->link, bp, cbcp->p->dl->bundle,
+ LINK_QUEUES(&cbcp->p->link) - 1, PROTO_CBCP);
+}
+
+static const char *
+cbcp_data_Type(unsigned type)
+{
+ static const char * const types[] = {
+ "No callback", "User-spec", "Server-spec", "list"
+ };
+
+ if (type < 1 || type > sizeof types / sizeof types[0])
+ return HexStr(type, NULL, 0);
+ return types[type-1];
+}
+
+struct cbcp_addr {
+ u_char type;
+ char addr[sizeof ((struct cbcp_data *)0)->addr_start - 1]; /* ASCIIZ */
+};
+
+/* cbcp_data::type values */
+#define CBCP_ADDR_PSTN (1)
+
+static void
+cbcp_data_Show(struct cbcp_data *data)
+{
+ struct cbcp_addr *addr;
+ char *end;
+
+ addr = (struct cbcp_addr *)data->addr_start;
+ end = (char *)data + data->length;
+ *end = '\0';
+
+ log_Printf(LogCBCP, " TYPE %s\n", cbcp_data_Type(data->type));
+ if ((char *)&data->delay < end) {
+ log_Printf(LogCBCP, " DELAY %d\n", data->delay);
+ while (addr->addr < end) {
+ if (addr->type == CBCP_ADDR_PSTN)
+ log_Printf(LogCBCP, " ADDR %s\n", addr->addr);
+ else
+ log_Printf(LogCBCP, " ADDR type %d ??\n", (int)addr->type);
+ addr = (struct cbcp_addr *)(addr->addr + strlen(addr->addr) + 1);
+ }
+ }
+}
+
+static void
+cbcp_SendReq(struct cbcp *cbcp)
+{
+ struct cbcp_data data;
+ struct cbcp_addr *addr;
+ char list[sizeof cbcp->fsm.phone], *next;
+ int len, max;
+
+ /* Only callees send REQs */
+
+ log_Printf(LogCBCP, "%s: SendReq(%d) state = %s\n", cbcp->p->dl->name,
+ cbcp->fsm.id, cbcpstate(cbcp->fsm.state));
+ data.type = cbcp->fsm.type;
+ data.delay = 0;
+ strncpy(list, cbcp->fsm.phone, sizeof list - 1);
+ list[sizeof list - 1] = '\0';
+
+ switch (data.type) {
+ case CBCP_CLIENTNUM:
+ addr = (struct cbcp_addr *)data.addr_start;
+ addr->type = CBCP_ADDR_PSTN;
+ *addr->addr = '\0';
+ data.length = addr->addr - (char *)&data;
+ break;
+
+ case CBCP_LISTNUM:
+ addr = (struct cbcp_addr *)data.addr_start;
+ for (next = strtok(list, ","); next; next = strtok(NULL, ",")) {
+ len = strlen(next);
+ max = data.addr_start + sizeof data.addr_start - addr->addr - 1;
+ if (len <= max) {
+ addr->type = CBCP_ADDR_PSTN;
+ strncpy(addr->addr, next, sizeof addr->addr - 1);
+ addr->addr[sizeof addr->addr - 1] = '\0';
+ addr = (struct cbcp_addr *)((char *)addr + len + 2);
+ } else
+ log_Printf(LogWARN, "CBCP ADDR \"%s\" skipped - packet too large\n",
+ next);
+ }
+ data.length = (char *)addr - (char *)&data;
+ break;
+
+ case CBCP_SERVERNUM:
+ data.length = data.addr_start - (char *)&data;
+ break;
+
+ default:
+ data.length = (char *)&data.delay - (char *)&data;
+ break;
+ }
+
+ cbcp_data_Show(&data);
+ cbcp_Output(cbcp, CBCP_REQ, &data);
+ cbcp->fsm.restart--;
+ cbcp_StartTimer(cbcp, cbcp->fsm.delay);
+ cbcp_NewPhase(cbcp, CBCP_REQSENT); /* Wait for a RESPONSE */
+}
+
+void
+cbcp_Up(struct cbcp *cbcp)
+{
+ struct lcp *lcp = &cbcp->p->link.lcp;
+
+ cbcp->fsm.delay = cbcp->p->dl->cfg.cbcp.delay;
+ if (*cbcp->p->dl->peer.authname == '\0' ||
+ !auth_SetPhoneList(cbcp->p->dl->peer.authname, cbcp->fsm.phone,
+ sizeof cbcp->fsm.phone)) {
+ strncpy(cbcp->fsm.phone, cbcp->p->dl->cfg.cbcp.phone,
+ sizeof cbcp->fsm.phone - 1);
+ cbcp->fsm.phone[sizeof cbcp->fsm.phone - 1] = '\0';
+ }
+
+ if (lcp->want_callback.opmask) {
+ if (*cbcp->fsm.phone == '\0')
+ cbcp->fsm.type = CBCP_NONUM;
+ else if (!strcmp(cbcp->fsm.phone, "*")) {
+ cbcp->fsm.type = CBCP_SERVERNUM;
+ *cbcp->fsm.phone = '\0';
+ } else
+ cbcp->fsm.type = CBCP_CLIENTNUM;
+ cbcp_NewPhase(cbcp, CBCP_STOPPED); /* Wait for a REQ */
+ cbcp_StartTimer(cbcp, cbcp->fsm.delay * DEF_FSMTRIES);
+ } else {
+ if (*cbcp->fsm.phone == '\0')
+ cbcp->fsm.type = CBCP_NONUM;
+ else if (!strcmp(cbcp->fsm.phone, "*")) {
+ cbcp->fsm.type = CBCP_CLIENTNUM;
+ *cbcp->fsm.phone = '\0';
+ } else if (strchr(cbcp->fsm.phone, ','))
+ cbcp->fsm.type = CBCP_LISTNUM;
+ else
+ cbcp->fsm.type = CBCP_SERVERNUM;
+ cbcp->fsm.restart = DEF_FSMTRIES;
+ cbcp_SendReq(cbcp);
+ }
+}
+
+static int
+cbcp_AdjustResponse(struct cbcp *cbcp, struct cbcp_data *data)
+{
+ /*
+ * We've received a REQ (data). Adjust our reponse (cbcp->fsm.*)
+ * so that we (hopefully) agree with the peer
+ */
+ struct cbcp_addr *addr;
+
+ switch (data->type) {
+ case CBCP_NONUM:
+ if (cbcp->p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_NONE))
+ /*
+ * if ``none'' is a configured callback possibility
+ * (ie, ``set callback cbcp none''), go along with the callees
+ * request
+ */
+ cbcp->fsm.type = CBCP_NONUM;
+
+ /*
+ * Otherwise, we send our desired response anyway. This seems to be
+ * what Win95 does - although I can't find this behaviour documented
+ * in the CBCP spec....
+ */
+
+ return 1;
+
+ case CBCP_CLIENTNUM:
+ if (cbcp->fsm.type == CBCP_CLIENTNUM) {
+ char *ptr;
+
+ if (data->length > data->addr_start - (char *)data) {
+ /*
+ * The peer has given us an address type spec - make sure we
+ * understand !
+ */
+ addr = (struct cbcp_addr *)data->addr_start;
+ if (addr->type != CBCP_ADDR_PSTN) {
+ log_Printf(LogPHASE, "CBCP: Unrecognised address type %d !\n",
+ (int)addr->type);
+ return 0;
+ }
+ }
+ /* we accept the REQ even if the peer didn't specify an addr->type */
+ ptr = strchr(cbcp->fsm.phone, ',');
+ if (ptr)
+ *ptr = '\0'; /* Just use the first number in our list */
+ return 1;
+ }
+ log_Printf(LogPHASE, "CBCP: no number to pass to the peer !\n");
+ return 0;
+
+ case CBCP_SERVERNUM:
+ if (cbcp->fsm.type == CBCP_SERVERNUM) {
+ *cbcp->fsm.phone = '\0';
+ return 1;
+ }
+ if (data->length > data->addr_start - (char *)data) {
+ /*
+ * This violates the spec, but if the peer has told us the
+ * number it wants to call back, take advantage of this fact
+ * and allow things to proceed if we've specified the same
+ * number
+ */
+ addr = (struct cbcp_addr *)data->addr_start;
+ if (addr->type != CBCP_ADDR_PSTN) {
+ log_Printf(LogPHASE, "CBCP: Unrecognised address type %d !\n",
+ (int)addr->type);
+ return 0;
+ } else if (cbcp->fsm.type == CBCP_CLIENTNUM) {
+ /*
+ * If the peer's insisting on deciding the number, make sure
+ * it's one of the ones in our list. If it is, let the peer
+ * think it's in control :-)
+ */
+ char list[sizeof cbcp->fsm.phone], *next;
+
+ strncpy(list, cbcp->fsm.phone, sizeof list - 1);
+ list[sizeof list - 1] = '\0';
+ for (next = strtok(list, ","); next; next = strtok(NULL, ","))
+ if (!strcmp(next, addr->addr)) {
+ cbcp->fsm.type = CBCP_SERVERNUM;
+ strcpy(cbcp->fsm.phone, next);
+ return 1;
+ }
+ }
+ }
+ log_Printf(LogPHASE, "CBCP: Peer won't allow local decision !\n");
+ return 0;
+
+ case CBCP_LISTNUM:
+ if (cbcp->fsm.type == CBCP_CLIENTNUM || cbcp->fsm.type == CBCP_LISTNUM) {
+ /*
+ * Search through ``data''s addresses and see if cbcp->fsm.phone
+ * contains any of them
+ */
+ char list[sizeof cbcp->fsm.phone], *next, *end;
+
+ addr = (struct cbcp_addr *)data->addr_start;
+ end = (char *)data + data->length;
+
+ while (addr->addr < end) {
+ if (addr->type == CBCP_ADDR_PSTN) {
+ strncpy(list, cbcp->fsm.phone, sizeof list - 1);
+ list[sizeof list - 1] = '\0';
+ for (next = strtok(list, ","); next; next = strtok(NULL, ","))
+ if (!strcmp(next, addr->addr)) {
+ cbcp->fsm.type = CBCP_LISTNUM;
+ strcpy(cbcp->fsm.phone, next);
+ return 1;
+ }
+ } else
+ log_Printf(LogCBCP, "Warning: Unrecognised address type %d !\n",
+ (int)addr->type);
+ addr = (struct cbcp_addr *)(addr->addr + strlen(addr->addr) + 1);
+ }
+ }
+ log_Printf(LogPHASE, "CBCP: no good number to pass to the peer !\n");
+ return 0;
+ }
+
+ log_Printf(LogCBCP, "Unrecognised REQ type %d !\n", (int)data->type);
+ return 0;
+}
+
+static void
+cbcp_SendResponse(struct cbcp *cbcp)
+{
+ struct cbcp_data data;
+ struct cbcp_addr *addr;
+
+ /* Only callers send RESPONSEs */
+
+ log_Printf(LogCBCP, "%s: SendResponse(%d) state = %s\n", cbcp->p->dl->name,
+ cbcp->fsm.id, cbcpstate(cbcp->fsm.state));
+
+ data.type = cbcp->fsm.type;
+ data.delay = cbcp->fsm.delay;
+ addr = (struct cbcp_addr *)data.addr_start;
+ if (data.type == CBCP_NONUM)
+ data.length = (char *)&data.delay - (char *)&data;
+ else if (*cbcp->fsm.phone) {
+ addr->type = CBCP_ADDR_PSTN;
+ strncpy(addr->addr, cbcp->fsm.phone, sizeof addr->addr - 1);
+ addr->addr[sizeof addr->addr - 1] = '\0';
+ data.length = (addr->addr + strlen(addr->addr) + 1) - (char *)&data;
+ } else
+ data.length = data.addr_start - (char *)&data;
+
+ cbcp_data_Show(&data);
+ cbcp_Output(cbcp, CBCP_RESPONSE, &data);
+ cbcp->fsm.restart--;
+ cbcp_StartTimer(cbcp, cbcp->fsm.delay);
+ cbcp_NewPhase(cbcp, CBCP_RESPSENT); /* Wait for an ACK */
+}
+
+/* What to do after checking an incoming response */
+#define CBCP_ACTION_DOWN (0)
+#define CBCP_ACTION_REQ (1)
+#define CBCP_ACTION_ACK (2)
+
+static int
+cbcp_CheckResponse(struct cbcp *cbcp, struct cbcp_data *data)
+{
+ /*
+ * We've received a RESPONSE (data). Check if it agrees with
+ * our REQ (cbcp->fsm)
+ */
+ struct cbcp_addr *addr;
+
+ addr = (struct cbcp_addr *)data->addr_start;
+
+ if (data->type == cbcp->fsm.type) {
+ switch (cbcp->fsm.type) {
+ case CBCP_NONUM:
+ return CBCP_ACTION_ACK;
+
+ case CBCP_CLIENTNUM:
+ if ((char *)data + data->length <= addr->addr)
+ log_Printf(LogPHASE, "CBCP: peer didn't respond with a number !\n");
+ else if (addr->type != CBCP_ADDR_PSTN)
+ log_Printf(LogPHASE, "CBCP: Unrecognised address type %d !\n",
+ addr->type);
+ else {
+ strncpy(cbcp->fsm.phone, addr->addr, sizeof cbcp->fsm.phone - 1);
+ cbcp->fsm.phone[sizeof cbcp->fsm.phone - 1] = '\0';
+ cbcp->fsm.delay = data->delay;
+ return CBCP_ACTION_ACK;
+ }
+ return CBCP_ACTION_DOWN;
+
+ case CBCP_SERVERNUM:
+ cbcp->fsm.delay = data->delay;
+ return CBCP_ACTION_ACK;
+
+ case CBCP_LISTNUM:
+ if ((char *)data + data->length <= addr->addr)
+ log_Printf(LogPHASE, "CBCP: peer didn't respond with a number !\n");
+ else if (addr->type != CBCP_ADDR_PSTN)
+ log_Printf(LogPHASE, "CBCP: Unrecognised address type %d !\n",
+ addr->type);
+ else {
+ char list[sizeof cbcp->fsm.phone], *next;
+
+ strncpy(list, cbcp->fsm.phone, sizeof list - 1);
+ list[sizeof list - 1] = '\0';
+ for (next = strtok(list, ","); next; next = strtok(NULL, ","))
+ if (!strcmp(addr->addr, next)) {
+ strcpy(cbcp->fsm.phone, next);
+ cbcp->fsm.delay = data->delay;
+ return CBCP_ACTION_ACK;
+ }
+ log_Printf(LogPHASE, "CBCP: peer didn't respond with a "
+ "valid number !\n");
+ }
+ return CBCP_ACTION_DOWN;
+ }
+ log_Printf(LogPHASE, "Internal CBCP error - agreed on %d !\n",
+ (int)cbcp->fsm.type);
+ return CBCP_ACTION_DOWN;
+ } else if (data->type == CBCP_NONUM && cbcp->fsm.type == CBCP_CLIENTNUM) {
+ /*
+ * Client doesn't want CBCP after all....
+ * We only allow this when ``set cbcp *'' has been specified.
+ */
+ cbcp->fsm.type = CBCP_NONUM;
+ return CBCP_ACTION_ACK;
+ }
+ log_Printf(LogCBCP, "Invalid peer RESPONSE\n");
+ return CBCP_ACTION_REQ;
+}
+
+static void
+cbcp_SendAck(struct cbcp *cbcp)
+{
+ struct cbcp_data data;
+ struct cbcp_addr *addr;
+
+ /* Only callees send ACKs */
+
+ log_Printf(LogCBCP, "%s: SendAck(%d) state = %s\n", cbcp->p->dl->name,
+ cbcp->fsm.id, cbcpstate(cbcp->fsm.state));
+
+ data.type = cbcp->fsm.type;
+ switch (data.type) {
+ case CBCP_NONUM:
+ data.length = (char *)&data.delay - (char *)&data;
+ break;
+ case CBCP_CLIENTNUM:
+ addr = (struct cbcp_addr *)data.addr_start;
+ addr->type = CBCP_ADDR_PSTN;
+ strncpy(addr->addr, cbcp->fsm.phone, sizeof addr->addr - 1);
+ addr->addr[sizeof addr->addr - 1] = '\0';
+ data.delay = cbcp->fsm.delay;
+ data.length = addr->addr + strlen(addr->addr) + 1 - (char *)&data;
+ break;
+ default:
+ data.delay = cbcp->fsm.delay;
+ data.length = data.addr_start - (char *)&data;
+ break;
+ }
+
+ cbcp_data_Show(&data);
+ cbcp_Output(cbcp, CBCP_ACK, &data);
+ cbcp->fsm.restart--;
+ cbcp_StartTimer(cbcp, cbcp->fsm.delay);
+ cbcp_NewPhase(cbcp, CBCP_ACKSENT); /* Wait for an ACK */
+}
+
+extern struct mbuf *
+cbcp_Input(struct bundle *bundle __unused, struct link *l, struct mbuf *bp)
+{
+ struct physical *p = link2physical(l);
+ struct cbcp_header *head;
+ struct cbcp_data *data;
+ struct cbcp *cbcp = &p->dl->cbcp;
+ size_t len;
+
+ if (p == NULL) {
+ log_Printf(LogERROR, "cbcp_Input: Not a physical link - dropped\n");
+ m_freem(bp);
+ return NULL;
+ }
+
+ bp = m_pullup(bp);
+ len = m_length(bp);
+ if (len < sizeof(struct cbcp_header)) {
+ m_freem(bp);
+ return NULL;
+ }
+ head = (struct cbcp_header *)MBUF_CTOP(bp);
+ if (ntohs(head->length) != len) {
+ log_Printf(LogWARN, "Corrupt CBCP packet (code %d, length %u not %zu)"
+ " - ignored\n", head->code, ntohs(head->length), len);
+ m_freem(bp);
+ return NULL;
+ }
+ m_settype(bp, MB_CBCPIN);
+
+ /* XXX check the id */
+
+ bp->m_offset += sizeof(struct cbcp_header);
+ bp->m_len -= sizeof(struct cbcp_header);
+ data = (struct cbcp_data *)MBUF_CTOP(bp);
+
+ switch (head->code) {
+ case CBCP_REQ:
+ log_Printf(LogCBCP, "%s: RecvReq(%d) state = %s\n",
+ p->dl->name, head->id, cbcpstate(cbcp->fsm.state));
+ cbcp_data_Show(data);
+ if (cbcp->fsm.state == CBCP_STOPPED || cbcp->fsm.state == CBCP_RESPSENT) {
+ timer_Stop(&cbcp->fsm.timer);
+ if (cbcp_AdjustResponse(cbcp, data)) {
+ cbcp->fsm.restart = DEF_FSMTRIES;
+ cbcp->fsm.id = head->id;
+ cbcp_SendResponse(cbcp);
+ } else
+ datalink_CBCPFailed(cbcp->p->dl);
+ } else
+ log_Printf(LogCBCP, "%s: unexpected REQ dropped\n", p->dl->name);
+ break;
+
+ case CBCP_RESPONSE:
+ log_Printf(LogCBCP, "%s: RecvResponse(%d) state = %s\n",
+ p->dl->name, head->id, cbcpstate(cbcp->fsm.state));
+ cbcp_data_Show(data);
+ if (cbcp->fsm.id != head->id) {
+ log_Printf(LogCBCP, "Warning: Expected id was %d, not %d\n",
+ cbcp->fsm.id, head->id);
+ cbcp->fsm.id = head->id;
+ }
+ if (cbcp->fsm.state == CBCP_REQSENT || cbcp->fsm.state == CBCP_ACKSENT) {
+ timer_Stop(&cbcp->fsm.timer);
+ switch (cbcp_CheckResponse(cbcp, data)) {
+ case CBCP_ACTION_REQ:
+ cbcp_SendReq(cbcp);
+ break;
+
+ case CBCP_ACTION_ACK:
+ cbcp->fsm.restart = DEF_FSMTRIES;
+ cbcp_SendAck(cbcp);
+ if (cbcp->fsm.type == CBCP_NONUM) {
+ /*
+ * Don't change state in case the peer doesn't get our ACK,
+ * just bring the layer up.
+ */
+ timer_Stop(&cbcp->fsm.timer);
+ datalink_NCPUp(cbcp->p->dl);
+ }
+ break;
+
+ default:
+ datalink_CBCPFailed(cbcp->p->dl);
+ break;
+ }
+ } else
+ log_Printf(LogCBCP, "%s: unexpected RESPONSE dropped\n", p->dl->name);
+ break;
+
+ case CBCP_ACK:
+ log_Printf(LogCBCP, "%s: RecvAck(%d) state = %s\n",
+ p->dl->name, head->id, cbcpstate(cbcp->fsm.state));
+ cbcp_data_Show(data);
+ if (cbcp->fsm.id != head->id) {
+ log_Printf(LogCBCP, "Warning: Expected id was %d, not %d\n",
+ cbcp->fsm.id, head->id);
+ cbcp->fsm.id = head->id;
+ }
+ if (cbcp->fsm.type == CBCP_NONUM) {
+ /*
+ * Don't change state in case the peer doesn't get our ACK,
+ * just bring the layer up.
+ */
+ timer_Stop(&cbcp->fsm.timer);
+ datalink_NCPUp(cbcp->p->dl);
+ } else if (cbcp->fsm.state == CBCP_RESPSENT) {
+ timer_Stop(&cbcp->fsm.timer);
+ datalink_CBCPComplete(cbcp->p->dl);
+ log_Printf(LogPHASE, "%s: CBCP: Peer will dial back\n", p->dl->name);
+ } else
+ log_Printf(LogCBCP, "%s: unexpected ACK dropped\n", p->dl->name);
+ break;
+
+ default:
+ log_Printf(LogWARN, "Unrecognised CBCP packet (code %d, length %zd)\n",
+ head->code, len);
+ break;
+ }
+
+ m_freem(bp);
+ return NULL;
+}
+
+void
+cbcp_Down(struct cbcp *cbcp)
+{
+ timer_Stop(&cbcp->fsm.timer);
+ cbcp_NewPhase(cbcp, CBCP_CLOSED);
+ cbcp->required = 0;
+}
+
+void
+cbcp_ReceiveTerminateReq(struct physical *p)
+{
+ if (p->dl->cbcp.fsm.state == CBCP_ACKSENT) {
+ /* Don't change our state in case the peer doesn't get the ACK */
+ p->dl->cbcp.required = 1;
+ log_Printf(LogPHASE, "%s: CBCP: Will dial back on %s\n", p->dl->name,
+ p->dl->cbcp.fsm.phone);
+ } else
+ cbcp_NewPhase(&p->dl->cbcp, CBCP_CLOSED);
+}
diff --git a/usr.sbin/ppp/cbcp.h b/usr.sbin/ppp/cbcp.h
new file mode 100644
index 0000000..46bf274
--- /dev/null
+++ b/usr.sbin/ppp/cbcp.h
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct mbuf;
+struct physical;
+struct datalink;
+
+/* fsm states */
+#define CBCP_CLOSED (0) /* Not in use */
+#define CBCP_STOPPED (1) /* Waiting for a REQ */
+#define CBCP_REQSENT (2) /* Waiting for a RESP */
+#define CBCP_RESPSENT (3) /* Waiting for an ACK */
+#define CBCP_ACKSENT (4) /* Waiting for an LCP Term REQ */
+
+struct cbcpcfg {
+ u_char delay;
+ char phone[SCRIPT_LEN];
+ long fsmretry;
+};
+
+struct cbcp {
+ unsigned required : 1; /* Are we gonna call back ? */
+ struct physical *p; /* On this physical link */
+ struct {
+ u_char type; /* cbcp_data::type (none/me/him/list) */
+ u_char delay; /* How long to delay */
+ char phone[SCRIPT_LEN]; /* What to dial */
+
+ int state; /* Our FSM state */
+ u_char id; /* Our FSM ID */
+ u_char restart; /* FSM Send again ? */
+ struct pppTimer timer; /* Resend last option */
+ } fsm;
+};
+
+extern void cbcp_Init(struct cbcp *, struct physical *);
+extern void cbcp_Up(struct cbcp *);
+extern struct mbuf *cbcp_Input(struct bundle *, struct link *, struct mbuf *);
+extern void cbcp_Down(struct cbcp *);
+extern void cbcp_ReceiveTerminateReq(struct physical *);
diff --git a/usr.sbin/ppp/ccp.c b/usr.sbin/ppp/ccp.c
new file mode 100644
index 0000000..f5bbf0c
--- /dev/null
+++ b/usr.sbin/ppp/ccp.c
@@ -0,0 +1,826 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h> /* memcpy() on some archs */
+#include <termios.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "proto.h"
+#include "pred.h"
+#include "deflate.h"
+#include "throughput.h"
+#include "iplist.h"
+#include "slcompress.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "prompt.h"
+#include "link.h"
+#include "mp.h"
+#include "async.h"
+#include "physical.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#ifndef NODES
+#include "mppe.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+
+static void CcpSendConfigReq(struct fsm *);
+static void CcpSentTerminateReq(struct fsm *);
+static void CcpSendTerminateAck(struct fsm *, u_char);
+static void CcpDecodeConfig(struct fsm *, u_char *, u_char *, int,
+ struct fsm_decode *);
+static void CcpLayerStart(struct fsm *);
+static void CcpLayerFinish(struct fsm *);
+static int CcpLayerUp(struct fsm *);
+static void CcpLayerDown(struct fsm *);
+static void CcpInitRestartCounter(struct fsm *, int);
+static int CcpRecvResetReq(struct fsm *);
+static void CcpRecvResetAck(struct fsm *, u_char);
+
+static struct fsm_callbacks ccp_Callbacks = {
+ CcpLayerUp,
+ CcpLayerDown,
+ CcpLayerStart,
+ CcpLayerFinish,
+ CcpInitRestartCounter,
+ CcpSendConfigReq,
+ CcpSentTerminateReq,
+ CcpSendTerminateAck,
+ CcpDecodeConfig,
+ CcpRecvResetReq,
+ CcpRecvResetAck
+};
+
+static const char * const ccp_TimerNames[] =
+ {"CCP restart", "CCP openmode", "CCP stopped"};
+
+static const char *
+protoname(int proto)
+{
+ static char const * const cftypes[] = {
+ /* Check out the latest ``Compression Control Protocol'' rfc (1962) */
+ "OUI", /* 0: OUI */
+ "PRED1", /* 1: Predictor type 1 */
+ "PRED2", /* 2: Predictor type 2 */
+ "PUDDLE", /* 3: Puddle Jumber */
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ "HWPPC", /* 16: Hewlett-Packard PPC */
+ "STAC", /* 17: Stac Electronics LZS (rfc1974) */
+ "MPPE", /* 18: Microsoft PPC (rfc2118) and */
+ /* Microsoft PPE (draft-ietf-pppext-mppe) */
+ "GAND", /* 19: Gandalf FZA (rfc1993) */
+ "V42BIS", /* 20: ARG->DATA.42bis compression */
+ "BSD", /* 21: BSD LZW Compress */
+ NULL,
+ "LZS-DCP", /* 23: LZS-DCP Compression Protocol (rfc1967) */
+ "MAGNALINK/DEFLATE",/* 24: Magnalink Variable Resource (rfc1975) */
+ /* 24: Deflate (according to pppd-2.3.*) */
+ "DCE", /* 25: Data Circuit-Terminating Equip (rfc1976) */
+ "DEFLATE", /* 26: Deflate (rfc1979) */
+ };
+
+ if (proto < 0 || (unsigned)proto > sizeof cftypes / sizeof *cftypes ||
+ cftypes[proto] == NULL) {
+ if (proto == -1)
+ return "none";
+ return HexStr(proto, NULL, 0);
+ }
+
+ return cftypes[proto];
+}
+
+/* We support these algorithms, and Req them in the given order */
+static const struct ccp_algorithm * const algorithm[] = {
+ &DeflateAlgorithm,
+ &Pred1Algorithm,
+ &PppdDeflateAlgorithm
+#ifndef NODES
+ , &MPPEAlgorithm
+#endif
+};
+
+#define NALGORITHMS (sizeof algorithm/sizeof algorithm[0])
+
+int
+ccp_ReportStatus(struct cmdargs const *arg)
+{
+ struct ccp_opt **o;
+ struct link *l;
+ struct ccp *ccp;
+ int f;
+
+ l = command_ChooseLink(arg);
+ ccp = &l->ccp;
+
+ prompt_Printf(arg->prompt, "%s: %s [%s]\n", l->name, ccp->fsm.name,
+ State2Nam(ccp->fsm.state));
+ if (ccp->fsm.state == ST_OPENED) {
+ prompt_Printf(arg->prompt, " My protocol = %s, His protocol = %s\n",
+ protoname(ccp->my_proto), protoname(ccp->his_proto));
+ prompt_Printf(arg->prompt, " Output: %ld --> %ld, Input: %ld --> %ld\n",
+ ccp->uncompout, ccp->compout,
+ ccp->compin, ccp->uncompin);
+ }
+
+ if (ccp->in.algorithm != -1)
+ prompt_Printf(arg->prompt, "\n Input Options: %s\n",
+ (*algorithm[ccp->in.algorithm]->Disp)(&ccp->in.opt));
+
+ if (ccp->out.algorithm != -1) {
+ o = &ccp->out.opt;
+ for (f = 0; f < ccp->out.algorithm; f++)
+ if (IsEnabled(ccp->cfg.neg[algorithm[f]->Neg]))
+ o = &(*o)->next;
+ prompt_Printf(arg->prompt, " Output Options: %s\n",
+ (*algorithm[ccp->out.algorithm]->Disp)(&(*o)->val));
+ }
+
+ prompt_Printf(arg->prompt, "\n Defaults: ");
+ prompt_Printf(arg->prompt, "FSM retry = %us, max %u Config"
+ " REQ%s, %u Term REQ%s\n", ccp->cfg.fsm.timeout,
+ ccp->cfg.fsm.maxreq, ccp->cfg.fsm.maxreq == 1 ? "" : "s",
+ ccp->cfg.fsm.maxtrm, ccp->cfg.fsm.maxtrm == 1 ? "" : "s");
+ prompt_Printf(arg->prompt, " deflate windows: ");
+ prompt_Printf(arg->prompt, "incoming = %d, ", ccp->cfg.deflate.in.winsize);
+ prompt_Printf(arg->prompt, "outgoing = %d\n", ccp->cfg.deflate.out.winsize);
+#ifndef NODES
+ prompt_Printf(arg->prompt, " MPPE: ");
+ if (ccp->cfg.mppe.keybits)
+ prompt_Printf(arg->prompt, "%d bits, ", ccp->cfg.mppe.keybits);
+ else
+ prompt_Printf(arg->prompt, "any bits, ");
+ switch (ccp->cfg.mppe.state) {
+ case MPPE_STATEFUL:
+ prompt_Printf(arg->prompt, "stateful");
+ break;
+ case MPPE_STATELESS:
+ prompt_Printf(arg->prompt, "stateless");
+ break;
+ case MPPE_ANYSTATE:
+ prompt_Printf(arg->prompt, "any state");
+ break;
+ }
+ prompt_Printf(arg->prompt, "%s\n",
+ ccp->cfg.mppe.required ? ", required" : "");
+#endif
+
+ prompt_Printf(arg->prompt, "\n DEFLATE: %s\n",
+ command_ShowNegval(ccp->cfg.neg[CCP_NEG_DEFLATE]));
+ prompt_Printf(arg->prompt, " PREDICTOR1: %s\n",
+ command_ShowNegval(ccp->cfg.neg[CCP_NEG_PRED1]));
+ prompt_Printf(arg->prompt, " DEFLATE24: %s\n",
+ command_ShowNegval(ccp->cfg.neg[CCP_NEG_DEFLATE24]));
+#ifndef NODES
+ prompt_Printf(arg->prompt, " MPPE: %s\n",
+ command_ShowNegval(ccp->cfg.neg[CCP_NEG_MPPE]));
+#endif
+ return 0;
+}
+
+void
+ccp_SetupCallbacks(struct ccp *ccp)
+{
+ ccp->fsm.fn = &ccp_Callbacks;
+ ccp->fsm.FsmTimer.name = ccp_TimerNames[0];
+ ccp->fsm.OpenTimer.name = ccp_TimerNames[1];
+ ccp->fsm.StoppedTimer.name = ccp_TimerNames[2];
+}
+
+void
+ccp_Init(struct ccp *ccp, struct bundle *bundle, struct link *l,
+ const struct fsm_parent *parent)
+{
+ /* Initialise ourselves */
+
+ fsm_Init(&ccp->fsm, "CCP", PROTO_CCP, 1, CCP_MAXCODE, LogCCP,
+ bundle, l, parent, &ccp_Callbacks, ccp_TimerNames);
+
+ ccp->cfg.deflate.in.winsize = 0;
+ ccp->cfg.deflate.out.winsize = 15;
+ ccp->cfg.fsm.timeout = DEF_FSMRETRY;
+ ccp->cfg.fsm.maxreq = DEF_FSMTRIES;
+ ccp->cfg.fsm.maxtrm = DEF_FSMTRIES;
+ ccp->cfg.neg[CCP_NEG_DEFLATE] = NEG_ENABLED|NEG_ACCEPTED;
+ ccp->cfg.neg[CCP_NEG_PRED1] = NEG_ENABLED|NEG_ACCEPTED;
+ ccp->cfg.neg[CCP_NEG_DEFLATE24] = 0;
+#ifndef NODES
+ ccp->cfg.mppe.keybits = 0;
+ ccp->cfg.mppe.state = MPPE_ANYSTATE;
+ ccp->cfg.mppe.required = 0;
+ ccp->cfg.neg[CCP_NEG_MPPE] = NEG_ENABLED|NEG_ACCEPTED;
+#endif
+
+ ccp_Setup(ccp);
+}
+
+void
+ccp_Setup(struct ccp *ccp)
+{
+ /* Set ourselves up for a startup */
+ ccp->fsm.open_mode = 0;
+ ccp->his_proto = ccp->my_proto = -1;
+ ccp->reset_sent = ccp->last_reset = -1;
+ ccp->in.algorithm = ccp->out.algorithm = -1;
+ ccp->in.state = ccp->out.state = NULL;
+ ccp->in.opt.hdr.id = -1;
+ ccp->out.opt = NULL;
+ ccp->his_reject = ccp->my_reject = 0;
+ ccp->uncompout = ccp->compout = 0;
+ ccp->uncompin = ccp->compin = 0;
+}
+
+/*
+ * Is ccp *REQUIRED* ?
+ * We ask each of the configured ccp protocols if they're required and
+ * return TRUE if they are.
+ *
+ * It's not possible for the peer to reject a required ccp protocol
+ * without our state machine bringing the supporting lcp layer down.
+ *
+ * If ccp is required but not open, the NCP layer should not push
+ * any data into the link.
+ */
+int
+ccp_Required(struct ccp *ccp)
+{
+ unsigned f;
+
+ for (f = 0; f < NALGORITHMS; f++)
+ if (IsEnabled(ccp->cfg.neg[algorithm[f]->Neg]) &&
+ (*algorithm[f]->Required)(&ccp->fsm))
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Report whether it's possible to increase a packet's size after
+ * compression (and by how much).
+ */
+int
+ccp_MTUOverhead(struct ccp *ccp)
+{
+ if (ccp->fsm.state == ST_OPENED && ccp->out.algorithm >= 0)
+ return algorithm[ccp->out.algorithm]->o.MTUOverhead;
+
+ return 0;
+}
+
+static void
+CcpInitRestartCounter(struct fsm *fp, int what)
+{
+ /* Set fsm timer load */
+ struct ccp *ccp = fsm2ccp(fp);
+
+ fp->FsmTimer.load = ccp->cfg.fsm.timeout * SECTICKS;
+ switch (what) {
+ case FSM_REQ_TIMER:
+ fp->restart = ccp->cfg.fsm.maxreq;
+ break;
+ case FSM_TRM_TIMER:
+ fp->restart = ccp->cfg.fsm.maxtrm;
+ break;
+ default:
+ fp->restart = 1;
+ break;
+ }
+}
+
+static void
+CcpSendConfigReq(struct fsm *fp)
+{
+ /* Send config REQ please */
+ struct ccp *ccp = fsm2ccp(fp);
+ struct ccp_opt **o;
+ u_char *cp, buff[100];
+ unsigned f;
+ int alloc;
+
+ cp = buff;
+ o = &ccp->out.opt;
+ alloc = ccp->his_reject == 0 && ccp->out.opt == NULL;
+ ccp->my_proto = -1;
+ ccp->out.algorithm = -1;
+ for (f = 0; f < NALGORITHMS; f++)
+ if (IsEnabled(ccp->cfg.neg[algorithm[f]->Neg]) &&
+ !REJECTED(ccp, algorithm[f]->id) &&
+ (*algorithm[f]->Usable)(fp)) {
+
+ if (!alloc)
+ for (o = &ccp->out.opt; *o != NULL; o = &(*o)->next)
+ if ((*o)->val.hdr.id == algorithm[f]->id && (*o)->algorithm == (int)f)
+ break;
+
+ if (alloc || *o == NULL) {
+ if ((*o = (struct ccp_opt *)malloc(sizeof(struct ccp_opt))) == NULL) {
+ log_Printf(LogERROR, "%s: Not enough memory for CCP REQ !\n",
+ fp->link->name);
+ break;
+ }
+ (*o)->val.hdr.id = algorithm[f]->id;
+ (*o)->val.hdr.len = 2;
+ (*o)->next = NULL;
+ (*o)->algorithm = f;
+ (*algorithm[f]->o.OptInit)(fp->bundle, &(*o)->val, &ccp->cfg);
+ }
+
+ if (cp + (*o)->val.hdr.len > buff + sizeof buff) {
+ log_Printf(LogERROR, "%s: CCP REQ buffer overrun !\n", fp->link->name);
+ break;
+ }
+ memcpy(cp, &(*o)->val, (*o)->val.hdr.len);
+ cp += (*o)->val.hdr.len;
+
+ ccp->my_proto = (*o)->val.hdr.id;
+ ccp->out.algorithm = f;
+
+ if (alloc)
+ o = &(*o)->next;
+ }
+
+ fsm_Output(fp, CODE_CONFIGREQ, fp->reqid, buff, cp - buff, MB_CCPOUT);
+}
+
+void
+ccp_SendResetReq(struct fsm *fp)
+{
+ /* We can't read our input - ask peer to reset */
+ struct ccp *ccp = fsm2ccp(fp);
+
+ ccp->reset_sent = fp->reqid;
+ ccp->last_reset = -1;
+ fsm_Output(fp, CODE_RESETREQ, fp->reqid, NULL, 0, MB_CCPOUT);
+}
+
+static void
+CcpSentTerminateReq(struct fsm *fp __unused)
+{
+ /* Term REQ just sent by FSM */
+}
+
+static void
+CcpSendTerminateAck(struct fsm *fp, u_char id)
+{
+ /* Send Term ACK please */
+ fsm_Output(fp, CODE_TERMACK, id, NULL, 0, MB_CCPOUT);
+}
+
+static int
+CcpRecvResetReq(struct fsm *fp)
+{
+ /* Got a reset REQ, reset outgoing dictionary */
+ struct ccp *ccp = fsm2ccp(fp);
+ if (ccp->out.state == NULL)
+ return 1;
+ return (*algorithm[ccp->out.algorithm]->o.Reset)(ccp->out.state);
+}
+
+static void
+CcpLayerStart(struct fsm *fp)
+{
+ /* We're about to start up ! */
+ struct ccp *ccp = fsm2ccp(fp);
+
+ log_Printf(LogCCP, "%s: LayerStart.\n", fp->link->name);
+ fp->more.reqs = fp->more.naks = fp->more.rejs = ccp->cfg.fsm.maxreq * 3;
+}
+
+static void
+CcpLayerDown(struct fsm *fp)
+{
+ /* About to come down */
+ struct ccp *ccp = fsm2ccp(fp);
+ struct ccp_opt *next;
+
+ log_Printf(LogCCP, "%s: LayerDown.\n", fp->link->name);
+ if (ccp->in.state != NULL) {
+ (*algorithm[ccp->in.algorithm]->i.Term)(ccp->in.state);
+ ccp->in.state = NULL;
+ ccp->in.algorithm = -1;
+ }
+ if (ccp->out.state != NULL) {
+ (*algorithm[ccp->out.algorithm]->o.Term)(ccp->out.state);
+ ccp->out.state = NULL;
+ ccp->out.algorithm = -1;
+ }
+ ccp->his_reject = ccp->my_reject = 0;
+
+ while (ccp->out.opt) {
+ next = ccp->out.opt->next;
+ free(ccp->out.opt);
+ ccp->out.opt = next;
+ }
+ ccp_Setup(ccp);
+}
+
+static void
+CcpLayerFinish(struct fsm *fp)
+{
+ /* We're now down */
+ struct ccp *ccp = fsm2ccp(fp);
+ struct ccp_opt *next;
+
+ log_Printf(LogCCP, "%s: LayerFinish.\n", fp->link->name);
+
+ /*
+ * Nuke options that may be left over from sending a REQ but never
+ * coming up.
+ */
+ while (ccp->out.opt) {
+ next = ccp->out.opt->next;
+ free(ccp->out.opt);
+ ccp->out.opt = next;
+ }
+
+ if (ccp_Required(ccp)) {
+ if (fp->link->lcp.fsm.state == ST_OPENED)
+ log_Printf(LogLCP, "%s: Closing due to CCP completion\n", fp->link->name);
+ fsm_Close(&fp->link->lcp.fsm);
+ }
+}
+
+/* Called when CCP has reached the OPEN state */
+static int
+CcpLayerUp(struct fsm *fp)
+{
+ /* We're now up */
+ struct ccp *ccp = fsm2ccp(fp);
+ struct ccp_opt **o;
+ unsigned f, fail;
+
+ for (f = fail = 0; f < NALGORITHMS; f++)
+ if (IsEnabled(ccp->cfg.neg[algorithm[f]->Neg]) &&
+ (*algorithm[f]->Required)(&ccp->fsm) &&
+ (ccp->in.algorithm != (int)f || ccp->out.algorithm != (int)f)) {
+ /* Blow it all away - we haven't negotiated a required algorithm */
+ log_Printf(LogWARN, "%s: Failed to negotiate (required) %s\n",
+ fp->link->name, protoname(algorithm[f]->id));
+ fail = 1;
+ }
+
+ if (fail) {
+ ccp->his_proto = ccp->my_proto = -1;
+ fsm_Close(fp);
+ fsm_Close(&fp->link->lcp.fsm);
+ return 0;
+ }
+
+ log_Printf(LogCCP, "%s: LayerUp.\n", fp->link->name);
+
+ if (ccp->in.state == NULL && ccp->in.algorithm >= 0 &&
+ ccp->in.algorithm < (int)NALGORITHMS) {
+ ccp->in.state = (*algorithm[ccp->in.algorithm]->i.Init)
+ (fp->bundle, &ccp->in.opt);
+ if (ccp->in.state == NULL) {
+ log_Printf(LogERROR, "%s: %s (in) initialisation failure\n",
+ fp->link->name, protoname(ccp->his_proto));
+ ccp->his_proto = ccp->my_proto = -1;
+ fsm_Close(fp);
+ return 0;
+ }
+ }
+
+ o = &ccp->out.opt;
+ if (ccp->out.algorithm > 0)
+ for (f = 0; f < (unsigned)ccp->out.algorithm; f++)
+ if (IsEnabled(ccp->cfg.neg[algorithm[f]->Neg]))
+ o = &(*o)->next;
+
+ if (ccp->out.state == NULL && ccp->out.algorithm >= 0 &&
+ ccp->out.algorithm < (int)NALGORITHMS) {
+ ccp->out.state = (*algorithm[ccp->out.algorithm]->o.Init)
+ (fp->bundle, &(*o)->val);
+ if (ccp->out.state == NULL) {
+ log_Printf(LogERROR, "%s: %s (out) initialisation failure\n",
+ fp->link->name, protoname(ccp->my_proto));
+ ccp->his_proto = ccp->my_proto = -1;
+ fsm_Close(fp);
+ return 0;
+ }
+ }
+
+ fp->more.reqs = fp->more.naks = fp->more.rejs = ccp->cfg.fsm.maxreq * 3;
+
+ log_Printf(LogCCP, "%s: Out = %s[%d], In = %s[%d]\n",
+ fp->link->name, protoname(ccp->my_proto), ccp->my_proto,
+ protoname(ccp->his_proto), ccp->his_proto);
+
+ return 1;
+}
+
+static void
+CcpDecodeConfig(struct fsm *fp, u_char *cp, u_char *end, int mode_type,
+ struct fsm_decode *dec)
+{
+ /* Deal with incoming data */
+ struct ccp *ccp = fsm2ccp(fp);
+ int f;
+ const char *disp;
+ struct fsm_opt *opt;
+
+ if (mode_type == MODE_REQ)
+ ccp->in.algorithm = -1; /* In case we've received two REQs in a row */
+
+ while (end >= cp + sizeof(opt->hdr)) {
+ if ((opt = fsm_readopt(&cp)) == NULL)
+ break;
+
+ for (f = NALGORITHMS-1; f > -1; f--)
+ if (algorithm[f]->id == opt->hdr.id)
+ break;
+
+ disp = f == -1 ? "" : (*algorithm[f]->Disp)(opt);
+ if (disp == NULL)
+ disp = "";
+
+ log_Printf(LogCCP, " %s[%d] %s\n", protoname(opt->hdr.id),
+ opt->hdr.len, disp);
+
+ if (f == -1) {
+ /* Don't understand that :-( */
+ if (mode_type == MODE_REQ) {
+ ccp->my_reject |= (1 << opt->hdr.id);
+ fsm_rej(dec, opt);
+ }
+ } else {
+ struct ccp_opt *o;
+
+ switch (mode_type) {
+ case MODE_REQ:
+ if (IsAccepted(ccp->cfg.neg[algorithm[f]->Neg]) &&
+ (*algorithm[f]->Usable)(fp) &&
+ ccp->in.algorithm == -1) {
+ memcpy(&ccp->in.opt, opt, opt->hdr.len);
+ switch ((*algorithm[f]->i.Set)(fp->bundle, &ccp->in.opt, &ccp->cfg)) {
+ case MODE_REJ:
+ fsm_rej(dec, &ccp->in.opt);
+ break;
+ case MODE_NAK:
+ fsm_nak(dec, &ccp->in.opt);
+ break;
+ case MODE_ACK:
+ fsm_ack(dec, &ccp->in.opt);
+ ccp->his_proto = opt->hdr.id;
+ ccp->in.algorithm = (int)f; /* This one'll do :-) */
+ break;
+ }
+ } else {
+ fsm_rej(dec, opt);
+ }
+ break;
+ case MODE_NAK:
+ for (o = ccp->out.opt; o != NULL; o = o->next)
+ if (o->val.hdr.id == opt->hdr.id)
+ break;
+ if (o == NULL)
+ log_Printf(LogCCP, "%s: Warning: Ignoring peer NAK of unsent"
+ " option\n", fp->link->name);
+ else {
+ memcpy(&o->val, opt, opt->hdr.len);
+ if ((*algorithm[f]->o.Set)(fp->bundle, &o->val, &ccp->cfg) ==
+ MODE_ACK)
+ ccp->my_proto = algorithm[f]->id;
+ else {
+ ccp->his_reject |= (1 << opt->hdr.id);
+ ccp->my_proto = -1;
+ if (algorithm[f]->Required(fp)) {
+ log_Printf(LogWARN, "%s: Cannot understand peers (required)"
+ " %s negotiation\n", fp->link->name,
+ protoname(algorithm[f]->id));
+ fsm_Close(&fp->link->lcp.fsm);
+ }
+ }
+ }
+ break;
+ case MODE_REJ:
+ ccp->his_reject |= (1 << opt->hdr.id);
+ ccp->my_proto = -1;
+ if (algorithm[f]->Required(fp)) {
+ log_Printf(LogWARN, "%s: Peer rejected (required) %s negotiation\n",
+ fp->link->name, protoname(algorithm[f]->id));
+ fsm_Close(&fp->link->lcp.fsm);
+ }
+ break;
+ }
+ }
+ }
+
+ if (mode_type != MODE_NOP) {
+ fsm_opt_normalise(dec);
+ if (dec->rejend != dec->rej || dec->nakend != dec->nak) {
+ if (ccp->in.state == NULL) {
+ ccp->his_proto = -1;
+ ccp->in.algorithm = -1;
+ }
+ }
+ }
+}
+
+extern struct mbuf *
+ccp_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
+{
+ /* Got PROTO_CCP from link */
+ m_settype(bp, MB_CCPIN);
+ if (bundle_Phase(bundle) == PHASE_NETWORK)
+ fsm_Input(&l->ccp.fsm, bp);
+ else {
+ if (bundle_Phase(bundle) < PHASE_NETWORK)
+ log_Printf(LogCCP, "%s: Error: Unexpected CCP in phase %s (ignored)\n",
+ l->ccp.fsm.link->name, bundle_PhaseName(bundle));
+ m_freem(bp);
+ }
+ return NULL;
+}
+
+static void
+CcpRecvResetAck(struct fsm *fp, u_char id)
+{
+ /* Got a reset ACK, reset incoming dictionary */
+ struct ccp *ccp = fsm2ccp(fp);
+
+ if (ccp->reset_sent != -1) {
+ if (id != ccp->reset_sent) {
+ log_Printf(LogCCP, "%s: Incorrect ResetAck (id %d, not %d)"
+ " ignored\n", fp->link->name, id, ccp->reset_sent);
+ return;
+ }
+ /* Whaddaya know - a correct reset ack */
+ } else if (id == ccp->last_reset)
+ log_Printf(LogCCP, "%s: Duplicate ResetAck (resetting again)\n",
+ fp->link->name);
+ else {
+ log_Printf(LogCCP, "%s: Unexpected ResetAck (id %d) ignored\n",
+ fp->link->name, id);
+ return;
+ }
+
+ ccp->last_reset = ccp->reset_sent;
+ ccp->reset_sent = -1;
+ if (ccp->in.state != NULL)
+ (*algorithm[ccp->in.algorithm]->i.Reset)(ccp->in.state);
+}
+
+static struct mbuf *
+ccp_LayerPush(struct bundle *b __unused, struct link *l, struct mbuf *bp,
+ int pri, u_short *proto)
+{
+ if (PROTO_COMPRESSIBLE(*proto)) {
+ if (l->ccp.fsm.state != ST_OPENED) {
+ if (ccp_Required(&l->ccp)) {
+ /* The NCP layer shouldn't have let this happen ! */
+ log_Printf(LogERROR, "%s: Unexpected attempt to use an unopened and"
+ " required CCP layer\n", l->name);
+ m_freem(bp);
+ bp = NULL;
+ }
+ } else if (l->ccp.out.state != NULL) {
+ bp = (*algorithm[l->ccp.out.algorithm]->o.Write)
+ (l->ccp.out.state, &l->ccp, l, pri, proto, bp);
+ switch (*proto) {
+ case PROTO_ICOMPD:
+ m_settype(bp, MB_ICOMPDOUT);
+ break;
+ case PROTO_COMPD:
+ m_settype(bp, MB_COMPDOUT);
+ break;
+ }
+ }
+ }
+
+ return bp;
+}
+
+static struct mbuf *
+ccp_LayerPull(struct bundle *b __unused, struct link *l, struct mbuf *bp,
+ u_short *proto)
+{
+ /*
+ * If proto isn't PROTO_[I]COMPD, we still want to pass it to the
+ * decompression routines so that the dictionary's updated
+ */
+ if (l->ccp.fsm.state == ST_OPENED) {
+ if (*proto == PROTO_COMPD || *proto == PROTO_ICOMPD) {
+ /* Decompress incoming data */
+ if (l->ccp.reset_sent != -1)
+ /* Send another REQ and put the packet in the bit bucket */
+ fsm_Output(&l->ccp.fsm, CODE_RESETREQ, l->ccp.reset_sent, NULL, 0,
+ MB_CCPOUT);
+ else if (l->ccp.in.state != NULL) {
+ bp = (*algorithm[l->ccp.in.algorithm]->i.Read)
+ (l->ccp.in.state, &l->ccp, proto, bp);
+ switch (*proto) {
+ case PROTO_ICOMPD:
+ m_settype(bp, MB_ICOMPDIN);
+ break;
+ case PROTO_COMPD:
+ m_settype(bp, MB_COMPDIN);
+ break;
+ }
+ return bp;
+ }
+ m_freem(bp);
+ bp = NULL;
+ } else if (PROTO_COMPRESSIBLE(*proto) && l->ccp.in.state != NULL) {
+ /* Add incoming Network Layer traffic to our dictionary */
+ (*algorithm[l->ccp.in.algorithm]->i.DictSetup)
+ (l->ccp.in.state, &l->ccp, *proto, bp);
+ }
+ }
+
+ return bp;
+}
+
+u_short
+ccp_Proto(struct ccp *ccp)
+{
+ return !link2physical(ccp->fsm.link) || !ccp->fsm.bundle->ncp.mp.active ?
+ PROTO_COMPD : PROTO_ICOMPD;
+}
+
+int
+ccp_SetOpenMode(struct ccp *ccp)
+{
+ int f;
+
+ for (f = 0; f < CCP_NEG_TOTAL; f++)
+ if (IsEnabled(ccp->cfg.neg[f])) {
+ ccp->fsm.open_mode = 0;
+ return 1;
+ }
+
+ ccp->fsm.open_mode = OPEN_PASSIVE; /* Go straight to ST_STOPPED ? */
+
+ for (f = 0; f < CCP_NEG_TOTAL; f++)
+ if (IsAccepted(ccp->cfg.neg[f]))
+ return 1;
+
+ return 0; /* No CCP at all */
+}
+
+int
+ccp_DefaultUsable(struct fsm *fp __unused)
+{
+ return 1;
+}
+
+int
+ccp_DefaultRequired(struct fsm *fp __unused)
+{
+ return 0;
+}
+
+struct layer ccplayer = { LAYER_CCP, "ccp", ccp_LayerPush, ccp_LayerPull };
diff --git a/usr.sbin/ppp/ccp.h b/usr.sbin/ppp/ccp.h
new file mode 100644
index 0000000..bc867b6
--- /dev/null
+++ b/usr.sbin/ppp/ccp.h
@@ -0,0 +1,165 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+#define CCP_MAXCODE CODE_RESETACK
+
+#define TY_OUI 0 /* OUI */
+#define TY_PRED1 1 /* Predictor type 1 */
+#define TY_PRED2 2 /* Predictor type 2 */
+#define TY_PUDDLE 3 /* Puddle Jumper */
+#define TY_HWPPC 16 /* Hewlett-Packard PPC */
+#define TY_STAC 17 /* Stac Electronics LZS */
+#define TY_MSPPC 18 /* Microsoft PPC */
+#define TY_MPPE 18 /* Microsoft PPE */
+#define TY_GAND 19 /* Gandalf FZA */
+#define TY_V42BIS 20 /* V.42bis compression */
+#define TY_BSD 21 /* BSD LZW Compress */
+#define TY_PPPD_DEFLATE 24 /* Deflate (gzip) - (mis) numbered by pppd */
+#define TY_DEFLATE 26 /* Deflate (gzip) - rfc 1979 */
+
+#define CCP_NEG_DEFLATE 0
+#define CCP_NEG_PRED1 1
+#define CCP_NEG_DEFLATE24 2
+#ifndef NODES
+#define CCP_NEG_MPPE 3
+#define CCP_NEG_TOTAL 4
+#else
+#define CCP_NEG_TOTAL 3
+#endif
+
+#ifndef NODES
+enum mppe_negstate {
+ MPPE_ANYSTATE,
+ MPPE_STATELESS,
+ MPPE_STATEFUL
+};
+#endif
+
+struct mbuf;
+struct link;
+
+struct ccp_config {
+ struct {
+ struct {
+ int winsize;
+ } in, out;
+ } deflate;
+#ifndef NODES
+ struct {
+ int keybits;
+ enum mppe_negstate state;
+ unsigned required : 1;
+ } mppe;
+#endif
+ struct fsm_retry fsm; /* How often/frequently to resend requests */
+ unsigned neg[CCP_NEG_TOTAL];
+};
+
+struct ccp_opt {
+ struct ccp_opt *next;
+ int algorithm;
+ struct fsm_opt val;
+};
+
+struct ccp {
+ struct fsm fsm; /* The finite state machine */
+
+ int his_proto; /* peer's compression protocol */
+ int my_proto; /* our compression protocol */
+
+ int reset_sent; /* If != -1, ignore compressed 'till ack */
+ int last_reset; /* We can receive more (dups) w/ this id */
+
+ struct {
+ int algorithm; /* Algorithm in use */
+ void *state; /* Returned by implementations Init() */
+ struct fsm_opt opt; /* Set by implementation's OptInit() */
+ } in;
+
+ struct {
+ int algorithm; /* Algorithm in use */
+ void *state; /* Returned by implementations Init() */
+ struct ccp_opt *opt; /* Set by implementation's OptInit() */
+ } out;
+
+ u_int32_t his_reject; /* Request codes rejected by peer */
+ u_int32_t my_reject; /* Request codes I have rejected */
+
+ u_long uncompout, compout; /* Outgoing bytes before/after compression */
+ u_long uncompin, compin; /* Incoming bytes after/before decompression */
+
+ struct ccp_config cfg;
+};
+
+#define fsm2ccp(fp) (fp->proto == PROTO_CCP ? (struct ccp *)fp : NULL)
+
+struct ccp_algorithm {
+ int id;
+ int Neg; /* ccp_config neg array item */
+ const char *(*Disp)(struct fsm_opt *); /* Use result immediately ! */
+ int (*Usable)(struct fsm *); /* Ok to negotiate ? */
+ int (*Required)(struct fsm *); /* Must negotiate ? */
+ struct {
+ int (*Set)(struct bundle *, struct fsm_opt *, const struct ccp_config *);
+ void *(*Init)(struct bundle *, struct fsm_opt *);
+ void (*Term)(void *);
+ void (*Reset)(void *);
+ struct mbuf *(*Read)(void *, struct ccp *, u_short *, struct mbuf *);
+ void (*DictSetup)(void *, struct ccp *, u_short, struct mbuf *);
+ } i;
+ struct {
+ int MTUOverhead;
+ void (*OptInit)(struct bundle *, struct fsm_opt *,
+ const struct ccp_config *);
+ int (*Set)(struct bundle *, struct fsm_opt *, const struct ccp_config *);
+ void *(*Init)(struct bundle *, struct fsm_opt *);
+ void (*Term)(void *);
+ int (*Reset)(void *);
+ struct mbuf *(*Write)(void *, struct ccp *, struct link *, int, u_short *,
+ struct mbuf *);
+ } o;
+};
+
+extern void ccp_Init(struct ccp *, struct bundle *, struct link *,
+ const struct fsm_parent *);
+extern void ccp_Setup(struct ccp *);
+extern int ccp_Required(struct ccp *);
+extern int ccp_MTUOverhead(struct ccp *);
+
+extern void ccp_SendResetReq(struct fsm *);
+extern struct mbuf *ccp_Input(struct bundle *, struct link *, struct mbuf *);
+extern int ccp_ReportStatus(struct cmdargs const *);
+extern u_short ccp_Proto(struct ccp *);
+extern void ccp_SetupCallbacks(struct ccp *);
+extern int ccp_SetOpenMode(struct ccp *);
+extern int ccp_DefaultUsable(struct fsm *);
+extern int ccp_DefaultRequired(struct fsm *);
+
+extern struct layer ccplayer;
diff --git a/usr.sbin/ppp/chap.c b/usr.sbin/ppp/chap.c
new file mode 100644
index 0000000..75de650
--- /dev/null
+++ b/usr.sbin/ppp/chap.c
@@ -0,0 +1,972 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#ifndef NODES
+#include <md4.h>
+#endif
+#include <md5.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "proto.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "auth.h"
+#include "async.h"
+#include "throughput.h"
+#include "descriptor.h"
+#include "chap.h"
+#include "iplist.h"
+#include "slcompress.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "ccp.h"
+#include "link.h"
+#include "physical.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "chat.h"
+#include "cbcp.h"
+#include "command.h"
+#include "datalink.h"
+#ifndef NODES
+#include "chap_ms.h"
+#include "mppe.h"
+#endif
+#include "id.h"
+
+static const char * const chapcodes[] = {
+ "???", "CHALLENGE", "RESPONSE", "SUCCESS", "FAILURE"
+};
+#define MAXCHAPCODE (sizeof chapcodes / sizeof chapcodes[0] - 1)
+
+static void
+ChapOutput(struct physical *physical, u_int code, u_int id,
+ const u_char *ptr, int count, const char *text)
+{
+ int plen;
+ struct fsmheader lh;
+ struct mbuf *bp;
+
+ plen = sizeof(struct fsmheader) + count;
+ lh.code = code;
+ lh.id = id;
+ lh.length = htons(plen);
+ bp = m_get(plen, MB_CHAPOUT);
+ memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader));
+ if (count)
+ memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count);
+ log_DumpBp(LogDEBUG, "ChapOutput", bp);
+ if (text == NULL)
+ log_Printf(LogPHASE, "Chap Output: %s\n", chapcodes[code]);
+ else
+ log_Printf(LogPHASE, "Chap Output: %s (%s)\n", chapcodes[code], text);
+ link_PushPacket(&physical->link, bp, physical->dl->bundle,
+ LINK_QUEUES(&physical->link) - 1, PROTO_CHAP);
+}
+
+static char *
+chap_BuildAnswer(char *name, char *key, u_char id, char *challenge
+#ifndef NODES
+ , u_char type, char *peerchallenge, char *authresponse,
+ int lanman
+#endif
+ )
+{
+ char *result, *digest;
+ size_t nlen, klen;
+
+ nlen = strlen(name);
+ klen = strlen(key);
+
+#ifndef NODES
+ if (type == 0x80) {
+ char expkey[AUTHLEN << 2];
+ MD4_CTX MD4context;
+ size_t f;
+
+ if ((result = malloc(1 + nlen + MS_CHAP_RESPONSE_LEN)) == NULL)
+ return result;
+
+ digest = result; /* the response */
+ *digest++ = MS_CHAP_RESPONSE_LEN; /* 49 */
+ memcpy(digest + MS_CHAP_RESPONSE_LEN, name, nlen);
+ if (lanman) {
+ memset(digest + 24, '\0', 25);
+ mschap_LANMan(digest, challenge + 1, key); /* LANMan response */
+ } else {
+ memset(digest, '\0', 25);
+ digest += 24;
+
+ for (f = 0; f < klen; f++) {
+ expkey[2*f] = key[f];
+ expkey[2*f+1] = '\0';
+ }
+ /*
+ * -----------
+ * expkey = | k\0e\0y\0 |
+ * -----------
+ */
+ MD4Init(&MD4context);
+ MD4Update(&MD4context, expkey, klen << 1);
+ MD4Final(digest, &MD4context);
+
+ /*
+ * ---- -------- ---------------- ------- ------
+ * result = | 49 | LANMan | 16 byte digest | 9 * ? | name |
+ * ---- -------- ---------------- ------- ------
+ */
+ mschap_NT(digest, challenge + 1);
+ }
+ /*
+ * ---- -------- ------------- ----- ------
+ * | | struct MS_ChapResponse24 | |
+ * result = | 49 | LANMan | NT digest | 0/1 | name |
+ * ---- -------- ------------- ----- ------
+ * where only one of LANMan & NT digest are set.
+ */
+ } else if (type == 0x81) {
+ char expkey[AUTHLEN << 2];
+ char pwdhash[CHAP81_HASH_LEN];
+ char pwdhashhash[CHAP81_HASH_LEN];
+ char *ntresponse;
+ size_t f;
+
+ if ((result = malloc(1 + nlen + CHAP81_RESPONSE_LEN)) == NULL)
+ return result;
+
+ memset(result, 0, 1 + nlen + CHAP81_RESPONSE_LEN);
+
+ digest = result;
+ *digest++ = CHAP81_RESPONSE_LEN; /* value size */
+
+ /* Copy our challenge */
+ memcpy(digest, peerchallenge + 1, CHAP81_CHALLENGE_LEN);
+
+ /* Expand password to Unicode XXX */
+ for (f = 0; f < klen; f++) {
+ expkey[2*f] = key[f];
+ expkey[2*f+1] = '\0';
+ }
+
+ ntresponse = digest + CHAP81_NTRESPONSE_OFF;
+
+ /* Get some needed hashes */
+ NtPasswordHash(expkey, klen * 2, pwdhash);
+ HashNtPasswordHash(pwdhash, pwdhashhash);
+
+ /* Generate NTRESPONSE to respond on challenge call */
+ GenerateNTResponse(challenge + 1, peerchallenge + 1, name,
+ expkey, klen * 2, ntresponse);
+
+ /* Generate MPPE MASTERKEY */
+ GetMasterKey(pwdhashhash, ntresponse, MPPE_MasterKey); /* XXX Global ! */
+
+ /* Generate AUTHRESPONSE to verify on auth success */
+ GenerateAuthenticatorResponse(expkey, klen * 2, ntresponse,
+ peerchallenge + 1, challenge + 1, name,
+ authresponse);
+
+ authresponse[CHAP81_AUTHRESPONSE_LEN] = 0;
+
+ memcpy(digest + CHAP81_RESPONSE_LEN, name, nlen);
+ } else
+#endif
+ if ((result = malloc(nlen + 17)) != NULL) {
+ /* Normal MD5 stuff */
+ MD5_CTX MD5context;
+
+ digest = result;
+ *digest++ = 16; /* value size */
+
+ MD5Init(&MD5context);
+ MD5Update(&MD5context, &id, 1);
+ MD5Update(&MD5context, key, klen);
+ MD5Update(&MD5context, challenge + 1, *challenge);
+ MD5Final(digest, &MD5context);
+
+ memcpy(digest + 16, name, nlen);
+ /*
+ * ---- -------- ------
+ * result = | 16 | digest | name |
+ * ---- -------- ------
+ */
+ }
+
+ return result;
+}
+
+static void
+chap_StartChild(struct chap *chap, char *prog, const char *name)
+{
+ char *argv[MAXARGS], *nargv[MAXARGS];
+ int argc, fd;
+ int in[2], out[2];
+ pid_t pid;
+
+ if (chap->child.fd != -1) {
+ log_Printf(LogWARN, "Chap: %s: Program already running\n", prog);
+ return;
+ }
+
+ if (pipe(in) == -1) {
+ log_Printf(LogERROR, "Chap: pipe: %s\n", strerror(errno));
+ return;
+ }
+
+ if (pipe(out) == -1) {
+ log_Printf(LogERROR, "Chap: pipe: %s\n", strerror(errno));
+ close(in[0]);
+ close(in[1]);
+ return;
+ }
+
+ pid = getpid();
+ switch ((chap->child.pid = fork())) {
+ case -1:
+ log_Printf(LogERROR, "Chap: fork: %s\n", strerror(errno));
+ close(in[0]);
+ close(in[1]);
+ close(out[0]);
+ close(out[1]);
+ chap->child.pid = 0;
+ return;
+
+ case 0:
+ timer_TermService();
+
+ if ((argc = command_Interpret(prog, strlen(prog), argv)) <= 0) {
+ if (argc < 0) {
+ log_Printf(LogWARN, "CHAP: Invalid command syntax\n");
+ _exit(255);
+ }
+ _exit(0);
+ }
+
+ close(in[1]);
+ close(out[0]);
+ if (out[1] == STDIN_FILENO)
+ out[1] = dup(out[1]);
+ dup2(in[0], STDIN_FILENO);
+ dup2(out[1], STDOUT_FILENO);
+ close(STDERR_FILENO);
+ if (open(_PATH_DEVNULL, O_RDWR) != STDERR_FILENO) {
+ log_Printf(LogALERT, "Chap: Failed to open %s: %s\n",
+ _PATH_DEVNULL, strerror(errno));
+ exit(1);
+ }
+ for (fd = getdtablesize(); fd > STDERR_FILENO; fd--)
+ fcntl(fd, F_SETFD, 1);
+#ifndef NOSUID
+ setuid(ID0realuid());
+#endif
+ command_Expand(nargv, argc, (char const *const *)argv,
+ chap->auth.physical->dl->bundle, 0, pid);
+ execvp(nargv[0], nargv);
+ printf("exec() of %s failed: %s\n", nargv[0], strerror(errno));
+ _exit(255);
+
+ default:
+ close(in[0]);
+ close(out[1]);
+ chap->child.fd = out[0];
+ chap->child.buf.len = 0;
+ write(in[1], chap->auth.in.name, strlen(chap->auth.in.name));
+ write(in[1], "\n", 1);
+ write(in[1], chap->challenge.peer + 1, *chap->challenge.peer);
+ write(in[1], "\n", 1);
+ write(in[1], name, strlen(name));
+ write(in[1], "\n", 1);
+ close(in[1]);
+ break;
+ }
+}
+
+static void
+chap_Cleanup(struct chap *chap, int sig)
+{
+ if (chap->child.pid) {
+ int status;
+
+ close(chap->child.fd);
+ chap->child.fd = -1;
+ if (sig)
+ kill(chap->child.pid, SIGTERM);
+ chap->child.pid = 0;
+ chap->child.buf.len = 0;
+
+ if (wait(&status) == -1)
+ log_Printf(LogERROR, "Chap: wait: %s\n", strerror(errno));
+ else if (WIFSIGNALED(status))
+ log_Printf(LogWARN, "Chap: Child received signal %d\n", WTERMSIG(status));
+ else if (WIFEXITED(status) && WEXITSTATUS(status))
+ log_Printf(LogERROR, "Chap: Child exited %d\n", WEXITSTATUS(status));
+ }
+ *chap->challenge.local = *chap->challenge.peer = '\0';
+#ifndef NODES
+ chap->peertries = 0;
+#endif
+}
+
+static void
+chap_Respond(struct chap *chap, char *name, char *key
+#ifndef NODES
+ , u_char type, int lm
+#endif
+ )
+{
+ u_char *ans;
+
+ ans = chap_BuildAnswer(name, key, chap->auth.id, chap->challenge.peer
+#ifndef NODES
+ , type, chap->challenge.local, chap->authresponse, lm
+#endif
+ );
+
+ if (ans) {
+ ChapOutput(chap->auth.physical, CHAP_RESPONSE, chap->auth.id,
+ ans, *ans + 1 + strlen(name), name);
+#ifndef NODES
+ chap->NTRespSent = !lm;
+ MPPE_IsServer = 0; /* XXX Global ! */
+#endif
+ free(ans);
+ } else
+ ChapOutput(chap->auth.physical, CHAP_FAILURE, chap->auth.id,
+ "Out of memory!", 14, NULL);
+}
+
+static int
+chap_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w __unused,
+ fd_set *e __unused, int *n)
+{
+ struct chap *chap = descriptor2chap(d);
+
+ if (r && chap && chap->child.fd != -1) {
+ FD_SET(chap->child.fd, r);
+ if (*n < chap->child.fd + 1)
+ *n = chap->child.fd + 1;
+ log_Printf(LogTIMER, "Chap: fdset(r) %d\n", chap->child.fd);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int
+chap_IsSet(struct fdescriptor *d, const fd_set *fdset)
+{
+ struct chap *chap = descriptor2chap(d);
+
+ return chap && chap->child.fd != -1 && FD_ISSET(chap->child.fd, fdset);
+}
+
+static void
+chap_Read(struct fdescriptor *d, struct bundle *bundle __unused,
+ const fd_set *fdset __unused)
+{
+ struct chap *chap = descriptor2chap(d);
+ int got;
+
+ got = read(chap->child.fd, chap->child.buf.ptr + chap->child.buf.len,
+ sizeof chap->child.buf.ptr - chap->child.buf.len - 1);
+ if (got == -1) {
+ log_Printf(LogERROR, "Chap: Read: %s\n", strerror(errno));
+ chap_Cleanup(chap, SIGTERM);
+ } else if (got == 0) {
+ log_Printf(LogWARN, "Chap: Read: Child terminated connection\n");
+ chap_Cleanup(chap, SIGTERM);
+ } else {
+ char *name, *key, *end;
+
+ chap->child.buf.len += got;
+ chap->child.buf.ptr[chap->child.buf.len] = '\0';
+ name = chap->child.buf.ptr;
+ name += strspn(name, " \t");
+ if ((key = strchr(name, '\n')) == NULL)
+ end = NULL;
+ else
+ end = strchr(++key, '\n');
+
+ if (end == NULL) {
+ if (chap->child.buf.len == sizeof chap->child.buf.ptr - 1) {
+ log_Printf(LogWARN, "Chap: Read: Input buffer overflow\n");
+ chap_Cleanup(chap, SIGTERM);
+ }
+ } else {
+#ifndef NODES
+ int lanman = chap->auth.physical->link.lcp.his_authtype == 0x80 &&
+ ((chap->NTRespSent &&
+ IsAccepted(chap->auth.physical->link.lcp.cfg.chap80lm)) ||
+ !IsAccepted(chap->auth.physical->link.lcp.cfg.chap80nt));
+#endif
+
+ while (end >= name && strchr(" \t\r\n", *end))
+ *end-- = '\0';
+ end = key - 1;
+ while (end >= name && strchr(" \t\r\n", *end))
+ *end-- = '\0';
+ key += strspn(key, " \t");
+
+ chap_Respond(chap, name, key
+#ifndef NODES
+ , chap->auth.physical->link.lcp.his_authtype, lanman
+#endif
+ );
+ chap_Cleanup(chap, 0);
+ }
+ }
+}
+
+static int
+chap_Write(struct fdescriptor *d __unused, struct bundle *bundle __unused,
+ const fd_set *fdset __unused)
+{
+ /* We never want to write here ! */
+ log_Printf(LogALERT, "chap_Write: Internal error: Bad call !\n");
+ return 0;
+}
+
+static void
+chap_ChallengeInit(struct authinfo *authp)
+{
+ struct chap *chap = auth2chap(authp);
+ int len, i;
+ char *cp;
+
+ len = strlen(authp->physical->dl->bundle->cfg.auth.name);
+
+ if (!*chap->challenge.local) {
+ randinit();
+ cp = chap->challenge.local;
+
+#ifndef NORADIUS
+ if (*authp->physical->dl->bundle->radius.cfg.file) {
+ /* For radius, our challenge is 16 readable NUL terminated bytes :*/
+ *cp++ = 16;
+ for (i = 0; i < 16; i++)
+ *cp++ = (random() % 10) + '0';
+ } else
+#endif
+ {
+#ifndef NODES
+ if (authp->physical->link.lcp.want_authtype == 0x80)
+ *cp++ = 8; /* MS does 8 byte callenges :-/ */
+ else if (authp->physical->link.lcp.want_authtype == 0x81)
+ *cp++ = 16; /* MS-CHAP-V2 does 16 bytes challenges */
+ else
+#endif
+ *cp++ = random() % (CHAPCHALLENGELEN-16) + 16;
+ for (i = 0; i < *chap->challenge.local; i++)
+ *cp++ = random() & 0xff;
+ }
+ memcpy(cp, authp->physical->dl->bundle->cfg.auth.name, len);
+ }
+}
+
+static void
+chap_Challenge(struct authinfo *authp)
+{
+ struct chap *chap = auth2chap(authp);
+ int len;
+
+ log_Printf(LogDEBUG, "CHAP%02X: Challenge\n",
+ authp->physical->link.lcp.want_authtype);
+
+ len = strlen(authp->physical->dl->bundle->cfg.auth.name);
+
+ /* Generate new local challenge value */
+ if (!*chap->challenge.local)
+ chap_ChallengeInit(authp);
+
+#ifndef NODES
+ if (authp->physical->link.lcp.want_authtype == 0x81)
+ ChapOutput(authp->physical, CHAP_CHALLENGE, authp->id,
+ chap->challenge.local, 1 + *chap->challenge.local, NULL);
+ else
+#endif
+ ChapOutput(authp->physical, CHAP_CHALLENGE, authp->id,
+ chap->challenge.local, 1 + *chap->challenge.local + len, NULL);
+}
+
+static void
+chap_Success(struct authinfo *authp)
+{
+ struct bundle *bundle = authp->physical->dl->bundle;
+ const char *msg;
+
+ datalink_GotAuthname(authp->physical->dl, authp->in.name);
+#ifndef NODES
+ if (authp->physical->link.lcp.want_authtype == 0x81) {
+#ifndef NORADIUS
+ if (*bundle->radius.cfg.file && bundle->radius.msrepstr)
+ msg = bundle->radius.msrepstr;
+ else
+#endif
+ msg = auth2chap(authp)->authresponse;
+ MPPE_MasterKeyValid = 1; /* XXX Global ! */
+ } else
+#endif
+#ifndef NORADIUS
+ if (*bundle->radius.cfg.file && bundle->radius.repstr)
+ msg = bundle->radius.repstr;
+ else
+#endif
+ msg = "Welcome!!";
+
+ ChapOutput(authp->physical, CHAP_SUCCESS, authp->id, msg, strlen(msg),
+ NULL);
+
+ authp->physical->link.lcp.auth_ineed = 0;
+ if (Enabled(bundle, OPT_UTMP))
+ physical_Login(authp->physical, authp->in.name);
+
+ if (authp->physical->link.lcp.auth_iwait == 0)
+ /*
+ * Either I didn't need to authenticate, or I've already been
+ * told that I got the answer right.
+ */
+ datalink_AuthOk(authp->physical->dl);
+}
+
+static void
+chap_Failure(struct authinfo *authp)
+{
+#ifndef NODES
+ char buf[1024], *ptr;
+#endif
+ const char *msg;
+
+#ifndef NORADIUS
+ struct bundle *bundle = authp->physical->link.lcp.fsm.bundle;
+ if (*bundle->radius.cfg.file && bundle->radius.errstr)
+ msg = bundle->radius.errstr;
+ else
+#endif
+#ifndef NODES
+ if (authp->physical->link.lcp.want_authtype == 0x80) {
+ sprintf(buf, "E=691 R=1 M=Invalid!");
+ msg = buf;
+ } else if (authp->physical->link.lcp.want_authtype == 0x81) {
+ int i;
+
+ ptr = buf;
+ ptr += sprintf(buf, "E=691 R=0 C=");
+ for (i=0; i<16; i++)
+ ptr += sprintf(ptr, "%02X", *(auth2chap(authp)->challenge.local+1+i));
+
+ sprintf(ptr, " V=3 M=Invalid!");
+ msg = buf;
+ } else
+#endif
+ msg = "Invalid!!";
+
+ ChapOutput(authp->physical, CHAP_FAILURE, authp->id, msg, strlen(msg) + 1,
+ NULL);
+ datalink_AuthNotOk(authp->physical->dl);
+}
+
+static int
+chap_Cmp(char *myans, int mylen, char *hisans, int hislen
+#ifndef NODES
+ , u_char type, int lm
+#endif
+ )
+{
+ int off;
+
+ if (mylen != hislen)
+ return 0;
+
+ off = 0;
+
+#ifndef NODES
+ if (type == 0x80) {
+ off = lm ? 0 : 24;
+ mylen = 24;
+ }
+#endif
+
+ for (; mylen; off++, mylen--)
+ if (toupper(myans[off]) != toupper(hisans[off]))
+ return 0;
+
+ return 1;
+}
+
+#ifndef NODES
+static int
+chap_HaveAnotherGo(struct chap *chap)
+{
+ if (++chap->peertries < 3) {
+ /* Give the peer another shot */
+ *chap->challenge.local = '\0';
+ chap_Challenge(&chap->auth);
+ return 1;
+ }
+
+ return 0;
+}
+#endif
+
+void
+chap_Init(struct chap *chap, struct physical *p)
+{
+ chap->desc.type = CHAP_DESCRIPTOR;
+ chap->desc.UpdateSet = chap_UpdateSet;
+ chap->desc.IsSet = chap_IsSet;
+ chap->desc.Read = chap_Read;
+ chap->desc.Write = chap_Write;
+ chap->child.pid = 0;
+ chap->child.fd = -1;
+ auth_Init(&chap->auth, p, chap_Challenge, chap_Success, chap_Failure);
+ *chap->challenge.local = *chap->challenge.peer = '\0';
+#ifndef NODES
+ chap->NTRespSent = 0;
+ chap->peertries = 0;
+#endif
+}
+
+void
+chap_ReInit(struct chap *chap)
+{
+ chap_Cleanup(chap, SIGTERM);
+}
+
+struct mbuf *
+chap_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
+{
+ struct physical *p = link2physical(l);
+ struct chap *chap = &p->dl->chap;
+ char *name, *key, *ans;
+ int len;
+ size_t nlen;
+ u_char alen;
+#ifndef NODES
+ int lanman;
+#endif
+
+ if (p == NULL) {
+ log_Printf(LogERROR, "chap_Input: Not a physical link - dropped\n");
+ m_freem(bp);
+ return NULL;
+ }
+
+ if (bundle_Phase(bundle) != PHASE_NETWORK &&
+ bundle_Phase(bundle) != PHASE_AUTHENTICATE) {
+ log_Printf(LogPHASE, "Unexpected chap input - dropped !\n");
+ m_freem(bp);
+ return NULL;
+ }
+
+ m_settype(bp, MB_CHAPIN);
+ if ((bp = auth_ReadHeader(&chap->auth, bp)) == NULL &&
+ ntohs(chap->auth.in.hdr.length) == 0)
+ log_Printf(LogWARN, "Chap Input: Truncated header !\n");
+ else if (chap->auth.in.hdr.code == 0 || chap->auth.in.hdr.code > MAXCHAPCODE)
+ log_Printf(LogPHASE, "Chap Input: %d: Bad CHAP code !\n",
+ chap->auth.in.hdr.code);
+ else {
+ len = m_length(bp);
+ ans = NULL;
+
+ if (chap->auth.in.hdr.code != CHAP_CHALLENGE &&
+ chap->auth.id != chap->auth.in.hdr.id &&
+ Enabled(bundle, OPT_IDCHECK)) {
+ /* Wrong conversation dude ! */
+ log_Printf(LogPHASE, "Chap Input: %s dropped (got id %d, not %d)\n",
+ chapcodes[chap->auth.in.hdr.code], chap->auth.in.hdr.id,
+ chap->auth.id);
+ m_freem(bp);
+ return NULL;
+ }
+ chap->auth.id = chap->auth.in.hdr.id; /* We respond with this id */
+
+#ifndef NODES
+ lanman = 0;
+#endif
+ switch (chap->auth.in.hdr.code) {
+ case CHAP_CHALLENGE:
+ bp = mbuf_Read(bp, &alen, 1);
+ len -= alen + 1;
+ if (len < 0) {
+ log_Printf(LogERROR, "Chap Input: Truncated challenge !\n");
+ m_freem(bp);
+ return NULL;
+ }
+ *chap->challenge.peer = alen;
+ bp = mbuf_Read(bp, chap->challenge.peer + 1, alen);
+ bp = auth_ReadName(&chap->auth, bp, len);
+#ifndef NODES
+ lanman = p->link.lcp.his_authtype == 0x80 &&
+ ((chap->NTRespSent && IsAccepted(p->link.lcp.cfg.chap80lm)) ||
+ !IsAccepted(p->link.lcp.cfg.chap80nt));
+
+ /* Generate local challenge value */
+ chap_ChallengeInit(&chap->auth);
+#endif
+ break;
+
+ case CHAP_RESPONSE:
+ auth_StopTimer(&chap->auth);
+ bp = mbuf_Read(bp, &alen, 1);
+ len -= alen + 1;
+ if (len < 0) {
+ log_Printf(LogERROR, "Chap Input: Truncated response !\n");
+ m_freem(bp);
+ return NULL;
+ }
+ if ((ans = malloc(alen + 1)) == NULL) {
+ log_Printf(LogERROR, "Chap Input: Out of memory !\n");
+ m_freem(bp);
+ return NULL;
+ }
+ *ans = chap->auth.id;
+ bp = mbuf_Read(bp, ans + 1, alen);
+ bp = auth_ReadName(&chap->auth, bp, len);
+#ifndef NODES
+ lanman = p->link.lcp.want_authtype == 0x80 &&
+ alen == 49 && ans[alen] == 0;
+#endif
+ break;
+
+ case CHAP_SUCCESS:
+ case CHAP_FAILURE:
+ /* chap->auth.in.name is already set up at CHALLENGE time */
+ if ((ans = malloc(len + 1)) == NULL) {
+ log_Printf(LogERROR, "Chap Input: Out of memory !\n");
+ m_freem(bp);
+ return NULL;
+ }
+ bp = mbuf_Read(bp, ans, len);
+ ans[len] = '\0';
+ break;
+ }
+
+ switch (chap->auth.in.hdr.code) {
+ case CHAP_CHALLENGE:
+ case CHAP_RESPONSE:
+ if (*chap->auth.in.name)
+ log_Printf(LogPHASE, "Chap Input: %s (%d bytes from %s%s)\n",
+ chapcodes[chap->auth.in.hdr.code], alen,
+ chap->auth.in.name,
+#ifndef NODES
+ lanman && chap->auth.in.hdr.code == CHAP_RESPONSE ?
+ " - lanman" :
+#endif
+ "");
+ else
+ log_Printf(LogPHASE, "Chap Input: %s (%d bytes%s)\n",
+ chapcodes[chap->auth.in.hdr.code], alen,
+#ifndef NODES
+ lanman && chap->auth.in.hdr.code == CHAP_RESPONSE ?
+ " - lanman" :
+#endif
+ "");
+ break;
+
+ case CHAP_SUCCESS:
+ case CHAP_FAILURE:
+ if (*ans)
+ log_Printf(LogPHASE, "Chap Input: %s (%s)\n",
+ chapcodes[chap->auth.in.hdr.code], ans);
+ else
+ log_Printf(LogPHASE, "Chap Input: %s\n",
+ chapcodes[chap->auth.in.hdr.code]);
+ break;
+ }
+
+ switch (chap->auth.in.hdr.code) {
+ case CHAP_CHALLENGE:
+ if (*bundle->cfg.auth.key == '!' && bundle->cfg.auth.key[1] != '!')
+ chap_StartChild(chap, bundle->cfg.auth.key + 1,
+ bundle->cfg.auth.name);
+ else
+ chap_Respond(chap, bundle->cfg.auth.name, bundle->cfg.auth.key +
+ (*bundle->cfg.auth.key == '!' ? 1 : 0)
+
+#ifndef NODES
+ , p->link.lcp.his_authtype, lanman
+#endif
+ );
+ break;
+
+ case CHAP_RESPONSE:
+ name = chap->auth.in.name;
+ nlen = strlen(name);
+#ifndef NODES
+ if (p->link.lcp.want_authtype == 0x81) {
+ struct MSCHAPv2_resp *resp = (struct MSCHAPv2_resp *)(ans + 1);
+
+ chap->challenge.peer[0] = sizeof resp->PeerChallenge;
+ memcpy(chap->challenge.peer + 1, resp->PeerChallenge,
+ sizeof resp->PeerChallenge);
+ }
+#endif
+
+#ifndef NORADIUS
+ if (*bundle->radius.cfg.file) {
+ if (!radius_Authenticate(&bundle->radius, &chap->auth,
+ chap->auth.in.name, ans, alen + 1,
+ chap->challenge.local + 1,
+ *chap->challenge.local))
+ chap_Failure(&chap->auth);
+ } else
+#endif
+ {
+ if (p->link.lcp.want_authtype == 0x81 && ans[alen] != '\0' &&
+ alen == sizeof(struct MSCHAPv2_resp)) {
+ struct MSCHAPv2_resp *resp = (struct MSCHAPv2_resp *)(ans + 1);
+
+ log_Printf(LogWARN, "%s: Compensating for corrupt (Win98/WinME?) "
+ "CHAP81 RESPONSE\n", l->name);
+ resp->Flags = '\0'; /* rfc2759 says it *MUST* be zero */
+ }
+ key = auth_GetSecret(name, nlen);
+ if (key) {
+#ifndef NODES
+ if (p->link.lcp.want_authtype == 0x80 &&
+ lanman && !IsEnabled(p->link.lcp.cfg.chap80lm)) {
+ log_Printf(LogPHASE, "Auth failure: LANMan not enabled\n");
+ if (chap_HaveAnotherGo(chap))
+ break;
+ key = NULL;
+ } else if (p->link.lcp.want_authtype == 0x80 &&
+ !lanman && !IsEnabled(p->link.lcp.cfg.chap80nt)) {
+ log_Printf(LogPHASE, "Auth failure: mschap not enabled\n");
+ if (chap_HaveAnotherGo(chap))
+ break;
+ key = NULL;
+ } else if (p->link.lcp.want_authtype == 0x81 &&
+ !IsEnabled(p->link.lcp.cfg.chap81)) {
+ log_Printf(LogPHASE, "Auth failure: CHAP81 not enabled\n");
+ key = NULL;
+ } else
+#endif
+ {
+ char *myans = chap_BuildAnswer(name, key, chap->auth.id,
+ chap->challenge.local
+#ifndef NODES
+ , p->link.lcp.want_authtype,
+ chap->challenge.peer,
+ chap->authresponse, lanman);
+ MPPE_IsServer = 1; /* XXX Global ! */
+#else
+ );
+#endif
+ if (myans == NULL)
+ key = NULL;
+ else {
+ if (!chap_Cmp(myans + 1, *myans, ans + 1, alen
+#ifndef NODES
+ , p->link.lcp.want_authtype, lanman
+#endif
+ ))
+ key = NULL;
+ free(myans);
+ }
+ }
+ }
+
+ if (key)
+ chap_Success(&chap->auth);
+ else
+ chap_Failure(&chap->auth);
+ }
+
+ break;
+
+ case CHAP_SUCCESS:
+ if (p->link.lcp.auth_iwait == PROTO_CHAP) {
+ p->link.lcp.auth_iwait = 0;
+ if (p->link.lcp.auth_ineed == 0) {
+#ifndef NODES
+ if (p->link.lcp.his_authtype == 0x81) {
+ if (strncasecmp(ans, chap->authresponse, 42)) {
+ datalink_AuthNotOk(p->dl);
+ log_Printf(LogWARN, "CHAP81: AuthenticatorResponse: (%.42s)"
+ " != ans: (%.42s)\n", chap->authresponse, ans);
+
+ } else {
+ /* Successful login */
+ MPPE_MasterKeyValid = 1; /* XXX Global ! */
+ datalink_AuthOk(p->dl);
+ }
+ } else
+#endif
+ /*
+ * We've succeeded in our ``login''
+ * If we're not expecting the peer to authenticate (or he already
+ * has), proceed to network phase.
+ */
+ datalink_AuthOk(p->dl);
+ }
+ }
+ break;
+
+ case CHAP_FAILURE:
+ datalink_AuthNotOk(p->dl);
+ break;
+ }
+ free(ans);
+ }
+
+ m_freem(bp);
+ return NULL;
+}
diff --git a/usr.sbin/ppp/chap.h b/usr.sbin/ppp/chap.h
new file mode 100644
index 0000000..08c6865
--- /dev/null
+++ b/usr.sbin/ppp/chap.h
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+struct mbuf;
+struct physical;
+
+#define CHAP_CHALLENGE 1
+#define CHAP_RESPONSE 2
+#define CHAP_SUCCESS 3
+#define CHAP_FAILURE 4
+
+struct chap {
+ struct fdescriptor desc;
+ struct {
+ pid_t pid;
+ int fd;
+ struct {
+ char ptr[AUTHLEN * 2 + 3]; /* Allow for \r\n at the end (- NUL) */
+ int len;
+ } buf;
+ } child;
+ struct authinfo auth;
+ struct {
+ u_char local[CHAPCHALLENGELEN + AUTHLEN]; /* I invented this one */
+ u_char peer[CHAPCHALLENGELEN + AUTHLEN]; /* Peer gave us this one */
+ } challenge;
+#ifndef NODES
+ unsigned NTRespSent : 1; /* Our last response */
+ int peertries;
+ u_char authresponse[CHAPAUTHRESPONSELEN]; /* CHAP 81 response */
+#endif
+};
+
+#define descriptor2chap(d) \
+ ((d)->type == CHAP_DESCRIPTOR ? (struct chap *)(d) : NULL)
+#define auth2chap(a) \
+ ((struct chap *)((char *)a - (uintptr_t)&((struct chap *)0)->auth))
+
+struct MSCHAPv2_resp { /* rfc2759 */
+ char PeerChallenge[16];
+ char Reserved[8];
+ char NTResponse[24];
+ char Flags;
+};
+
+extern void chap_Init(struct chap *, struct physical *);
+extern void chap_ReInit(struct chap *);
+extern struct mbuf *chap_Input(struct bundle *, struct link *, struct mbuf *);
diff --git a/usr.sbin/ppp/chap_ms.c b/usr.sbin/ppp/chap_ms.c
new file mode 100644
index 0000000..24e959c
--- /dev/null
+++ b/usr.sbin/ppp/chap_ms.c
@@ -0,0 +1,415 @@
+/*-
+ * Copyright (c) 1997 Gabor Kincses <gabor@acm.org>
+ * 1997 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Eric Rosenquist
+ * Strata Software Limited.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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>
+#ifdef __FreeBSD__
+#include <openssl/des.h>
+#include <sha.h>
+#else
+#include <sys/types.h>
+#include <stdlib.h>
+#ifdef __NetBSD__
+#include <openssl/des.h>
+#else
+#include <des.h>
+#endif
+#include <openssl/sha.h>
+#endif
+#include <md4.h>
+#include <string.h>
+
+#include "chap_ms.h"
+
+/*
+ * Documentation & specifications:
+ *
+ * MS-CHAP (CHAP80) rfc2433
+ * MS-CHAP-V2 (CHAP81) rfc2759
+ * MPPE key management draft-ietf-pppext-mppe-keys-02.txt
+ */
+
+static char SHA1_Pad1[40] =
+ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+
+static char SHA1_Pad2[40] =
+ {0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
+ 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
+ 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2,
+ 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2, 0xF2};
+
+/* unused, for documentation only */
+/* only NTResp is filled in for FreeBSD */
+struct MS_ChapResponse {
+ u_char LANManResp[24];
+ u_char NTResp[24];
+ u_char UseNT; /* If 1, ignore the LANMan response field */
+};
+
+static u_char
+Get7Bits(u_char *input, int startBit)
+{
+ register unsigned int word;
+
+ word = (unsigned)input[startBit / 8] << 8;
+ word |= (unsigned)input[startBit / 8 + 1];
+
+ word >>= 15 - (startBit % 8 + 7);
+
+ return word & 0xFE;
+}
+
+/* IN 56 bit DES key missing parity bits
+ OUT 64 bit DES key with parity bits added */
+static void
+MakeKey(u_char *key, u_char *des_key)
+{
+ des_key[0] = Get7Bits(key, 0);
+ des_key[1] = Get7Bits(key, 7);
+ des_key[2] = Get7Bits(key, 14);
+ des_key[3] = Get7Bits(key, 21);
+ des_key[4] = Get7Bits(key, 28);
+ des_key[5] = Get7Bits(key, 35);
+ des_key[6] = Get7Bits(key, 42);
+ des_key[7] = Get7Bits(key, 49);
+
+ des_set_odd_parity((des_cblock *)des_key);
+}
+
+static void /* IN 8 octets IN 7 octest OUT 8 octets */
+DesEncrypt(u_char *clear, u_char *key, u_char *cipher)
+{
+ des_cblock des_key;
+ des_key_schedule key_schedule;
+
+ MakeKey(key, des_key);
+ des_set_key(&des_key, key_schedule);
+ des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1);
+}
+
+static void /* IN 8 octets IN 16 octets OUT 24 octets */
+ChallengeResponse(u_char *challenge, u_char *pwHash, u_char *response)
+{
+ char ZPasswordHash[21];
+
+ memset(ZPasswordHash, '\0', sizeof ZPasswordHash);
+ memcpy(ZPasswordHash, pwHash, 16);
+
+ DesEncrypt(challenge, ZPasswordHash + 0, response + 0);
+ DesEncrypt(challenge, ZPasswordHash + 7, response + 8);
+ DesEncrypt(challenge, ZPasswordHash + 14, response + 16);
+}
+
+void
+NtPasswordHash(char *key, int keylen, char *hash)
+{
+ MD4_CTX MD4context;
+
+ MD4Init(&MD4context);
+ MD4Update(&MD4context, key, keylen);
+ MD4Final(hash, &MD4context);
+}
+
+void
+HashNtPasswordHash(char *hash, char *hashhash)
+{
+ MD4_CTX MD4context;
+
+ MD4Init(&MD4context);
+ MD4Update(&MD4context, hash, 16);
+ MD4Final(hashhash, &MD4context);
+}
+
+static void
+ChallengeHash(char *PeerChallenge, char *AuthenticatorChallenge,
+ char *UserName, char *Challenge)
+{
+ SHA_CTX Context;
+ char Digest[SHA_DIGEST_LENGTH];
+ char *Name;
+
+ Name = strrchr(UserName, '\\');
+ if(NULL == Name)
+ Name = UserName;
+ else
+ Name++;
+
+ SHA1_Init(&Context);
+
+ SHA1_Update(&Context, PeerChallenge, 16);
+ SHA1_Update(&Context, AuthenticatorChallenge, 16);
+ SHA1_Update(&Context, Name, strlen(Name));
+
+ SHA1_Final(Digest, &Context);
+ memcpy(Challenge, Digest, 8);
+}
+
+void
+GenerateNTResponse(char *AuthenticatorChallenge, char *PeerChallenge,
+ char *UserName, char *Password,
+ int PasswordLen, char *Response)
+{
+ char Challenge[8];
+ char PasswordHash[16];
+
+ ChallengeHash(PeerChallenge, AuthenticatorChallenge, UserName, Challenge);
+ NtPasswordHash(Password, PasswordLen, PasswordHash);
+ ChallengeResponse(Challenge, PasswordHash, Response);
+}
+
+#ifndef __FreeBSD__
+#define LENGTH 20
+static char *
+SHA1_End(SHA_CTX *ctx, char *buf)
+{
+ int i;
+ unsigned char digest[LENGTH];
+ static const char hex[]="0123456789abcdef";
+
+ if (!buf)
+ buf = malloc(2*LENGTH + 1);
+ if (!buf)
+ return 0;
+ SHA1_Final(digest, ctx);
+ for (i = 0; i < LENGTH; i++) {
+ buf[i+i] = hex[digest[i] >> 4];
+ buf[i+i+1] = hex[digest[i] & 0x0f];
+ }
+ buf[i+i] = '\0';
+ return buf;
+}
+#endif
+
+void
+GenerateAuthenticatorResponse(char *Password, int PasswordLen,
+ char *NTResponse, char *PeerChallenge,
+ char *AuthenticatorChallenge, char *UserName,
+ char *AuthenticatorResponse)
+{
+ SHA_CTX Context;
+ char PasswordHash[16];
+ char PasswordHashHash[16];
+ char Challenge[8];
+ u_char Digest[SHA_DIGEST_LENGTH];
+ int i;
+
+ /*
+ * "Magic" constants used in response generation
+ */
+ char Magic1[39] =
+ {0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
+ 0x65, 0x72, 0x20, 0x74, 0x6F, 0x20, 0x63, 0x6C, 0x69, 0x65,
+ 0x6E, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6E, 0x69, 0x6E, 0x67,
+ 0x20, 0x63, 0x6F, 0x6E, 0x73, 0x74, 0x61, 0x6E, 0x74};
+
+
+ char Magic2[41] =
+ {0x50, 0x61, 0x64, 0x20, 0x74, 0x6F, 0x20, 0x6D, 0x61, 0x6B,
+ 0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6F, 0x20, 0x6D, 0x6F,
+ 0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6E, 0x20, 0x6F, 0x6E,
+ 0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6F,
+ 0x6E};
+ /*
+ * Hash the password with MD4
+ */
+ NtPasswordHash(Password, PasswordLen, PasswordHash);
+ /*
+ * Now hash the hash
+ */
+ HashNtPasswordHash(PasswordHash, PasswordHashHash);
+
+ SHA1_Init(&Context);
+ SHA1_Update(&Context, PasswordHashHash, 16);
+ SHA1_Update(&Context, NTResponse, 24);
+ SHA1_Update(&Context, Magic1, 39);
+ SHA1_Final(Digest, &Context);
+ ChallengeHash(PeerChallenge, AuthenticatorChallenge, UserName, Challenge);
+ SHA1_Init(&Context);
+ SHA1_Update(&Context, Digest, 20);
+ SHA1_Update(&Context, Challenge, 8);
+ SHA1_Update(&Context, Magic2, 41);
+
+ /*
+ * Encode the value of 'Digest' as "S=" followed by
+ * 40 ASCII hexadecimal digits and return it in
+ * AuthenticatorResponse.
+ * For example,
+ * "S=0123456789ABCDEF0123456789ABCDEF01234567"
+ */
+ AuthenticatorResponse[0] = 'S';
+ AuthenticatorResponse[1] = '=';
+ SHA1_End(&Context, AuthenticatorResponse + 2);
+ for (i=2; i<42; i++)
+ AuthenticatorResponse[i] = toupper(AuthenticatorResponse[i]);
+
+}
+
+void
+GetMasterKey(char *PasswordHashHash, char *NTResponse, char *MasterKey)
+{
+ char Digest[SHA_DIGEST_LENGTH];
+ SHA_CTX Context;
+ static char Magic1[27] =
+ {0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74,
+ 0x68, 0x65, 0x20, 0x4d, 0x50, 0x50, 0x45, 0x20, 0x4d,
+ 0x61, 0x73, 0x74, 0x65, 0x72, 0x20, 0x4b, 0x65, 0x79};
+
+ SHA1_Init(&Context);
+ SHA1_Update(&Context, PasswordHashHash, 16);
+ SHA1_Update(&Context, NTResponse, 24);
+ SHA1_Update(&Context, Magic1, 27);
+ SHA1_Final(Digest, &Context);
+ memcpy(MasterKey, Digest, 16);
+}
+
+void
+GetAsymetricStartKey(char *MasterKey, char *SessionKey, int SessionKeyLength,
+ int IsSend, int IsServer)
+{
+ char Digest[SHA_DIGEST_LENGTH];
+ SHA_CTX Context;
+ char *s;
+
+ static char Magic2[84] =
+ {0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+ 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20, 0x6b, 0x65, 0x79,
+ 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x73,
+ 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73, 0x69, 0x64, 0x65,
+ 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+ 0x6b, 0x65, 0x79, 0x2e};
+
+ static char Magic3[84] =
+ {0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69,
+ 0x65, 0x6e, 0x74, 0x20, 0x73, 0x69, 0x64, 0x65, 0x2c, 0x20,
+ 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x72, 0x65, 0x63, 0x65, 0x69, 0x76, 0x65, 0x20,
+ 0x6b, 0x65, 0x79, 0x3b, 0x20, 0x6f, 0x6e, 0x20, 0x74, 0x68,
+ 0x65, 0x20, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x73,
+ 0x69, 0x64, 0x65, 0x2c, 0x20, 0x69, 0x74, 0x20, 0x69, 0x73,
+ 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x65, 0x6e, 0x64, 0x20,
+ 0x6b, 0x65, 0x79, 0x2e};
+
+ if (IsSend) {
+ if (IsServer) {
+ s = Magic3;
+ } else {
+ s = Magic2;
+ }
+ } else {
+ if (IsServer) {
+ s = Magic2;
+ } else {
+ s = Magic3;
+ }
+ }
+
+ SHA1_Init(&Context);
+ SHA1_Update(&Context, MasterKey, 16);
+ SHA1_Update(&Context, SHA1_Pad1, 40);
+ SHA1_Update(&Context, s, 84);
+ SHA1_Update(&Context, SHA1_Pad2, 40);
+ SHA1_Final(Digest, &Context);
+
+ memcpy(SessionKey, Digest, SessionKeyLength);
+}
+
+void
+GetNewKeyFromSHA(char *StartKey, char *SessionKey, long SessionKeyLength,
+ char *InterimKey)
+{
+ SHA_CTX Context;
+ char Digest[SHA_DIGEST_LENGTH];
+
+ SHA1_Init(&Context);
+ SHA1_Update(&Context, StartKey, SessionKeyLength);
+ SHA1_Update(&Context, SHA1_Pad1, 40);
+ SHA1_Update(&Context, SessionKey, SessionKeyLength);
+ SHA1_Update(&Context, SHA1_Pad2, 40);
+ SHA1_Final(Digest, &Context);
+
+ memcpy(InterimKey, Digest, SessionKeyLength);
+}
+
+#if 0
+static void
+Get_Key(char *InitialSessionKey, char *CurrentSessionKey,
+ int LengthOfDesiredKey)
+{
+ SHA_CTX Context;
+ char Digest[SHA_DIGEST_LENGTH];
+
+ SHA1_Init(&Context);
+ SHA1_Update(&Context, InitialSessionKey, LengthOfDesiredKey);
+ SHA1_Update(&Context, SHA1_Pad1, 40);
+ SHA1_Update(&Context, CurrentSessionKey, LengthOfDesiredKey);
+ SHA1_Update(&Context, SHA1_Pad2, 40);
+ SHA1_Final(Digest, &Context);
+
+ memcpy(CurrentSessionKey, Digest, LengthOfDesiredKey);
+}
+#endif
+
+/* passwordHash 16-bytes MD4 hashed password
+ challenge 8-bytes peer CHAP challenge
+ since passwordHash is in a 24-byte buffer, response is written in there */
+void
+mschap_NT(char *passwordHash, char *challenge)
+{
+ u_char response[24];
+
+ ChallengeResponse(challenge, passwordHash, response);
+ memcpy(passwordHash, response, 24);
+ passwordHash[24] = 1; /* NT-style response */
+}
+
+void
+mschap_LANMan(char *digest, char *challenge, char *secret)
+{
+ static u_char salt[] = "KGS!@#$%"; /* RASAPI32.dll */
+ char SECRET[14], *ptr, *end;
+ u_char hash[16];
+
+ end = SECRET + sizeof SECRET;
+ for (ptr = SECRET; *secret && ptr < end; ptr++, secret++)
+ *ptr = toupper(*secret);
+ if (ptr < end)
+ memset(ptr, '\0', end - ptr);
+
+ DesEncrypt(salt, SECRET, hash);
+ DesEncrypt(salt, SECRET + 7, hash + 8);
+
+ ChallengeResponse(challenge, hash, digest);
+}
diff --git a/usr.sbin/ppp/chap_ms.h b/usr.sbin/ppp/chap_ms.h
new file mode 100644
index 0000000..8a69f93
--- /dev/null
+++ b/usr.sbin/ppp/chap_ms.h
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 1997 Gabor Kincses <gabor@acm.org>
+ * 1997 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Eric Rosenquist
+ * Strata Software Limited.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+/* Max # of (Unicode) chars in an NT password */
+#define MAX_NT_PASSWORD 256
+
+/* Don't rely on sizeof(MS_ChapResponse) in case of struct padding */
+#define MS_CHAP_RESPONSE_LEN 49
+#define CHAP81_RESPONSE_LEN 49
+#define CHAP81_NTRESPONSE_LEN 24
+#define CHAP81_NTRESPONSE_OFF 24
+#define CHAP81_HASH_LEN 16
+#define CHAP81_AUTHRESPONSE_LEN 42
+#define CHAP81_CHALLENGE_LEN 16
+
+extern void mschap_NT(char *, char *);
+extern void mschap_LANMan(char *, char *, char *);
+extern void GenerateNTResponse(char *, char *, char *, char *, int , char *);
+extern void HashNtPasswordHash(char *, char *);
+extern void NtPasswordHash(char *, int, char *);
+extern void GenerateAuthenticatorResponse(char *, int, char *, char *, char *, char *, char *);
+extern void GetAsymetricStartKey(char *, char *, int, int, int);
+extern void GetMasterKey(char *, char *, char *);
+extern void GetNewKeyFromSHA(char *, char *, long, char *);
diff --git a/usr.sbin/ppp/chat.c b/usr.sbin/ppp/chat.c
new file mode 100644
index 0000000..04b2679
--- /dev/null
+++ b/usr.sbin/ppp/chat.c
@@ -0,0 +1,797 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "async.h"
+#include "descriptor.h"
+#include "physical.h"
+#include "chat.h"
+#include "mp.h"
+#include "auth.h"
+#include "chap.h"
+#include "slcompress.h"
+#include "iplist.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "cbcp.h"
+#include "command.h"
+#include "datalink.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "id.h"
+
+#define BUFLEFT(c) (sizeof (c)->buf - ((c)->bufend - (c)->buf))
+
+static void ExecStr(struct physical *, char *, char *, int);
+static char *ExpandString(struct chat *, const char *, char *, int, int);
+
+static void
+chat_PauseTimer(void *v)
+{
+ struct chat *c = (struct chat *)v;
+ timer_Stop(&c->pause);
+ c->pause.load = 0;
+}
+
+static void
+chat_Pause(struct chat *c, u_long load)
+{
+ timer_Stop(&c->pause);
+ c->pause.load += load;
+ c->pause.func = chat_PauseTimer;
+ c->pause.name = "chat pause";
+ c->pause.arg = c;
+ timer_Start(&c->pause);
+}
+
+static void
+chat_TimeoutTimer(void *v)
+{
+ struct chat *c = (struct chat *)v;
+ timer_Stop(&c->timeout);
+ c->TimedOut = 1;
+}
+
+static void
+chat_SetTimeout(struct chat *c)
+{
+ timer_Stop(&c->timeout);
+ if (c->TimeoutSec > 0) {
+ c->timeout.load = SECTICKS * c->TimeoutSec;
+ c->timeout.func = chat_TimeoutTimer;
+ c->timeout.name = "chat timeout";
+ c->timeout.arg = c;
+ timer_Start(&c->timeout);
+ }
+}
+
+static char *
+chat_NextChar(char *ptr, char ch)
+{
+ for (; *ptr; ptr++)
+ if (*ptr == ch)
+ return ptr;
+ else if (*ptr == '\\')
+ if (*++ptr == '\0')
+ return NULL;
+
+ return NULL;
+}
+
+static int
+chat_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
+{
+ struct chat *c = descriptor2chat(d);
+ int special, gotabort, gottimeout, needcr;
+ int TimedOut = c->TimedOut;
+ static char arg_term; /* An empty string */
+
+ if (c->pause.state == TIMER_RUNNING)
+ return 0;
+
+ if (TimedOut) {
+ log_Printf(LogCHAT, "Expect timeout\n");
+ if (c->nargptr == NULL)
+ c->state = CHAT_FAILED;
+ else {
+ /* c->state = CHAT_EXPECT; */
+ c->argptr = &arg_term;
+ }
+ c->TimedOut = 0;
+ }
+
+ if (c->state != CHAT_EXPECT && c->state != CHAT_SEND)
+ return 0;
+
+ gottimeout = gotabort = 0;
+
+ if (c->arg < c->argc && (c->arg < 0 || *c->argptr == '\0')) {
+ /* Go get the next string */
+ if (c->arg < 0 || c->state == CHAT_SEND)
+ c->state = CHAT_EXPECT;
+ else
+ c->state = CHAT_SEND;
+
+ special = 1;
+ while (special && (c->nargptr || c->arg < c->argc - 1)) {
+ if (c->arg < 0 || (!TimedOut && c->state == CHAT_SEND))
+ c->nargptr = NULL;
+
+ if (c->nargptr != NULL) {
+ /* We're doing expect-send-expect.... */
+ c->argptr = c->nargptr;
+ /* Put the '-' back in case we ever want to rerun our script */
+ c->nargptr[-1] = '-';
+ c->nargptr = chat_NextChar(c->nargptr, '-');
+ if (c->nargptr != NULL)
+ *c->nargptr++ = '\0';
+ } else {
+ int minus;
+
+ if ((c->argptr = c->argv[++c->arg]) == NULL) {
+ /* End of script - all ok */
+ c->state = CHAT_DONE;
+ return 0;
+ }
+
+ if (c->state == CHAT_EXPECT) {
+ /* Look for expect-send-expect sequence */
+ c->nargptr = c->argptr;
+ minus = 0;
+ while ((c->nargptr = chat_NextChar(c->nargptr, '-'))) {
+ c->nargptr++;
+ minus++;
+ }
+
+ if (minus % 2)
+ log_Printf(LogWARN, "chat_UpdateSet: \"%s\": Uneven number of"
+ " '-' chars, all ignored\n", c->argptr);
+ else if (minus) {
+ c->nargptr = chat_NextChar(c->argptr, '-');
+ *c->nargptr++ = '\0';
+ }
+ }
+ }
+
+ /*
+ * c->argptr now temporarily points into c->script (via c->argv)
+ * If it's an expect-send-expect sequence, we've just got the correct
+ * portion of that sequence.
+ */
+
+ needcr = c->state == CHAT_SEND &&
+ (*c->argptr != '!' || c->argptr[1] == '!');
+
+ /* We leave room for a potential HDLC header in the target string */
+ ExpandString(c, c->argptr, c->exp + 2, sizeof c->exp - 2, needcr);
+
+ /*
+ * Now read our string. If it's not a special string, we unset
+ * ``special'' to break out of the loop.
+ */
+ if (gotabort) {
+ if (c->abort.num < MAXABORTS) {
+ int len, i;
+
+ len = strlen(c->exp+2);
+ for (i = 0; i < c->abort.num; i++)
+ if (len > c->abort.string[i].len) {
+ int last;
+
+ for (last = c->abort.num; last > i; last--) {
+ c->abort.string[last].data = c->abort.string[last-1].data;
+ c->abort.string[last].len = c->abort.string[last-1].len;
+ }
+ break;
+ }
+ c->abort.string[i].len = len;
+ if ((c->abort.string[i].data = (char *)malloc(len+1)) != NULL) {
+ memcpy(c->abort.string[i].data, c->exp+2, len+1);
+ c->abort.num++;
+ }
+ } else
+ log_Printf(LogERROR, "chat_UpdateSet: too many abort strings\n");
+ gotabort = 0;
+ } else if (gottimeout) {
+ c->TimeoutSec = atoi(c->exp + 2);
+ if (c->TimeoutSec <= 0)
+ c->TimeoutSec = 30;
+ gottimeout = 0;
+ } else if (c->nargptr == NULL && !strcmp(c->exp+2, "ABORT"))
+ gotabort = 1;
+ else if (c->nargptr == NULL && !strcmp(c->exp+2, "TIMEOUT"))
+ gottimeout = 1;
+ else {
+ if (c->exp[2] == '!' && c->exp[3] != '!')
+ ExecStr(c->physical, c->exp + 3, c->exp + 3, sizeof c->exp - 3);
+
+ if (c->exp[2] == '\0') {
+ /* Empty string, reparse (this may be better as a `goto start') */
+ c->argptr = &arg_term;
+ return chat_UpdateSet(d, r, w, e, n);
+ }
+
+ special = 0;
+ }
+ }
+
+ if (special) {
+ if (gottimeout)
+ log_Printf(LogWARN, "chat_UpdateSet: TIMEOUT: Argument expected\n");
+ else if (gotabort)
+ log_Printf(LogWARN, "chat_UpdateSet: ABORT: Argument expected\n");
+
+ /* End of script - all ok */
+ c->state = CHAT_DONE;
+ return 0;
+ }
+
+ /* set c->argptr to point in the right place */
+ c->argptr = c->exp + (c->exp[2] == '!' ? 3 : 2);
+ c->arglen = strlen(c->argptr);
+
+ if (c->state == CHAT_EXPECT) {
+ /* We must check to see if the string's already been found ! */
+ char *begin, *end;
+
+ end = c->bufend - c->arglen + 1;
+ if (end < c->bufstart)
+ end = c->bufstart;
+ for (begin = c->bufstart; begin < end; begin++)
+ if (!strncmp(begin, c->argptr, c->arglen)) {
+ c->bufstart = begin + c->arglen;
+ c->argptr += c->arglen;
+ c->arglen = 0;
+ /* Continue - we've already read our expect string */
+ return chat_UpdateSet(d, r, w, e, n);
+ }
+
+ log_Printf(LogCHAT, "Expect(%d): %s\n", c->TimeoutSec, c->argptr);
+ chat_SetTimeout(c);
+ }
+ }
+
+ /*
+ * We now have c->argptr pointing at what we want to expect/send and
+ * c->state saying what we want to do... we now know what to put in
+ * the fd_set :-)
+ */
+
+ if (c->state == CHAT_EXPECT)
+ return physical_doUpdateSet(&c->physical->desc, r, NULL, e, n, 1);
+ else
+ return physical_doUpdateSet(&c->physical->desc, NULL, w, e, n, 1);
+}
+
+static int
+chat_IsSet(struct fdescriptor *d, const fd_set *fdset)
+{
+ struct chat *c = descriptor2chat(d);
+ return c->argptr && physical_IsSet(&c->physical->desc, fdset);
+}
+
+static void
+chat_UpdateLog(struct chat *c, int in)
+{
+ if (log_IsKept(LogCHAT) || log_IsKept(LogCONNECT)) {
+ /*
+ * If a linefeed appears in the last `in' characters of `c's input
+ * buffer, output from there, all the way back to the last linefeed.
+ * This is called for every read of `in' bytes.
+ */
+ char *ptr, *end, *stop, ch;
+ int level;
+
+ level = log_IsKept(LogCHAT) ? LogCHAT : LogCONNECT;
+ if (in == -1)
+ end = ptr = c->bufend;
+ else {
+ ptr = c->bufend - in;
+ for (end = c->bufend - 1; end >= ptr; end--)
+ if (*end == '\n')
+ break;
+ }
+
+ if (end >= ptr) {
+ for (ptr = c->bufend - (in == -1 ? 1 : in + 1); ptr >= c->bufstart; ptr--)
+ if (*ptr == '\n')
+ break;
+ ptr++;
+ stop = NULL;
+ while (stop < end) {
+ if ((stop = memchr(ptr, '\n', end - ptr)) == NULL)
+ stop = end;
+ ch = *stop;
+ *stop = '\0';
+ if (level == LogCHAT || strstr(ptr, "CONNECT"))
+ log_Printf(level, "Received: %s\n", ptr);
+ *stop = ch;
+ ptr = stop + 1;
+ }
+ }
+ }
+}
+
+static void
+chat_Read(struct fdescriptor *d, struct bundle *bundle __unused,
+ const fd_set *fdset __unused)
+{
+ struct chat *c = descriptor2chat(d);
+
+ if (c->state == CHAT_EXPECT) {
+ ssize_t in;
+ char *abegin, *ebegin, *begin, *aend, *eend, *end;
+ int n;
+
+ /*
+ * XXX - should this read only 1 byte to guarantee that we don't
+ * swallow any ppp talk from the peer ?
+ */
+ in = BUFLEFT(c);
+ if (in > (ssize_t)sizeof c->buf / 2)
+ in = sizeof c->buf / 2;
+
+ in = physical_Read(c->physical, c->bufend, in);
+ if (in <= 0)
+ return;
+
+ /* `begin' and `end' delimit where we're going to strncmp() from */
+ ebegin = c->bufend - c->arglen + 1;
+ eend = ebegin + in;
+ if (ebegin < c->bufstart)
+ ebegin = c->bufstart;
+
+ if (c->abort.num) {
+ abegin = c->bufend - c->abort.string[0].len + 1;
+ aend = c->bufend - c->abort.string[c->abort.num-1].len + in + 1;
+ if (abegin < c->bufstart)
+ abegin = c->bufstart;
+ } else {
+ abegin = ebegin;
+ aend = eend;
+ }
+ begin = abegin < ebegin ? abegin : ebegin;
+ end = aend < eend ? eend : aend;
+
+ c->bufend += in;
+
+ chat_UpdateLog(c, in);
+
+ if (c->bufend > c->buf + sizeof c->buf / 2) {
+ /* Shuffle our receive buffer back a bit */
+ int chop;
+
+ for (chop = begin - c->buf; chop; chop--)
+ if (c->buf[chop] == '\n')
+ /* found some already-logged garbage to remove :-) */
+ break;
+
+ if (!chop)
+ chop = begin - c->buf;
+
+ if (chop) {
+ char *from, *to;
+
+ to = c->buf;
+ from = to + chop;
+ while (from < c->bufend)
+ *to++ = *from++;
+ c->bufstart -= chop;
+ c->bufend -= chop;
+ begin -= chop;
+ end -= chop;
+ abegin -= chop;
+ aend -= chop;
+ ebegin -= chop;
+ eend -= chop;
+ }
+ }
+
+ for (; begin < end; begin++)
+ if (begin >= ebegin && begin < eend &&
+ !strncmp(begin, c->argptr, c->arglen)) {
+ /* Got it ! */
+ timer_Stop(&c->timeout);
+ if (memchr(begin + c->arglen - 1, '\n',
+ c->bufend - begin - c->arglen + 1) == NULL) {
+ /* force it into the log */
+ end = c->bufend;
+ c->bufend = begin + c->arglen;
+ chat_UpdateLog(c, -1);
+ c->bufend = end;
+ }
+ c->bufstart = begin + c->arglen;
+ c->argptr += c->arglen;
+ c->arglen = 0;
+ break;
+ } else if (begin >= abegin && begin < aend) {
+ for (n = c->abort.num - 1; n >= 0; n--) {
+ if (begin + c->abort.string[n].len > c->bufend)
+ break;
+ if (!strncmp(begin, c->abort.string[n].data,
+ c->abort.string[n].len)) {
+ if (memchr(begin + c->abort.string[n].len - 1, '\n',
+ c->bufend - begin - c->abort.string[n].len + 1) == NULL) {
+ /* force it into the log */
+ end = c->bufend;
+ c->bufend = begin + c->abort.string[n].len;
+ chat_UpdateLog(c, -1);
+ c->bufend = end;
+ }
+ c->bufstart = begin + c->abort.string[n].len;
+ c->state = CHAT_FAILED;
+ return;
+ }
+ }
+ }
+ }
+}
+
+static int
+chat_Write(struct fdescriptor *d, struct bundle *bundle __unused,
+ const fd_set *fdset __unused)
+{
+ struct chat *c = descriptor2chat(d);
+ int result = 0;
+
+ if (c->state == CHAT_SEND) {
+ int wrote;
+
+ if (strstr(c->argv[c->arg], "\\P")) /* Don't log the password */
+ log_Printf(LogCHAT, "Send: %s\n", c->argv[c->arg]);
+ else {
+ int sz;
+
+ sz = c->arglen - 1;
+ while (sz >= 0 && c->argptr[sz] == '\n')
+ sz--;
+ log_Printf(LogCHAT, "Send: %.*s\n", sz + 1, c->argptr);
+ }
+
+ if (physical_IsSync(c->physical)) {
+ /*
+ * XXX: Fix me
+ * This data should be stuffed down through the link layers
+ */
+ /* There's always room for the HDLC header */
+ c->argptr -= 2;
+ c->arglen += 2;
+ memcpy(c->argptr, "\377\003", 2); /* Prepend HDLC header */
+ }
+
+ wrote = physical_Write(c->physical, c->argptr, c->arglen);
+ result = wrote > 0 ? 1 : 0;
+ if (wrote == -1) {
+ if (errno != EINTR) {
+ log_Printf(LogWARN, "chat_Write: %s\n", strerror(errno));
+ result = -1;
+ }
+ if (physical_IsSync(c->physical)) {
+ c->argptr += 2;
+ c->arglen -= 2;
+ }
+ } else if (wrote < 2 && physical_IsSync(c->physical)) {
+ /* Oops - didn't even write our HDLC header ! */
+ c->argptr += 2;
+ c->arglen -= 2;
+ } else {
+ c->argptr += wrote;
+ c->arglen -= wrote;
+ }
+ }
+
+ return result;
+}
+
+void
+chat_Init(struct chat *c, struct physical *p)
+{
+ c->desc.type = CHAT_DESCRIPTOR;
+ c->desc.UpdateSet = chat_UpdateSet;
+ c->desc.IsSet = chat_IsSet;
+ c->desc.Read = chat_Read;
+ c->desc.Write = chat_Write;
+ c->physical = p;
+ *c->script = '\0';
+ c->argc = 0;
+ c->arg = -1;
+ c->argptr = NULL;
+ c->nargptr = NULL;
+ c->bufstart = c->bufend = c->buf;
+
+ memset(&c->pause, '\0', sizeof c->pause);
+ memset(&c->timeout, '\0', sizeof c->timeout);
+}
+
+int
+chat_Setup(struct chat *c, const char *data, const char *phone)
+{
+ c->state = CHAT_EXPECT;
+
+ if (data == NULL) {
+ *c->script = '\0';
+ c->argc = 0;
+ } else {
+ strncpy(c->script, data, sizeof c->script - 1);
+ c->script[sizeof c->script - 1] = '\0';
+ c->argc = MakeArgs(c->script, c->argv, VECSIZE(c->argv), PARSE_NOHASH);
+ }
+
+ c->arg = -1;
+ c->argptr = NULL;
+ c->nargptr = NULL;
+
+ c->TimeoutSec = 30;
+ c->TimedOut = 0;
+ c->phone = phone;
+ c->abort.num = 0;
+
+ timer_Stop(&c->pause);
+ timer_Stop(&c->timeout);
+
+ return c->argc >= 0;
+}
+
+void
+chat_Finish(struct chat *c)
+{
+ timer_Stop(&c->pause);
+ timer_Stop(&c->timeout);
+ while (c->abort.num)
+ free(c->abort.string[--c->abort.num].data);
+ c->abort.num = 0;
+}
+
+void
+chat_Destroy(struct chat *c)
+{
+ chat_Finish(c);
+}
+
+/*
+ * \c don't add a cr
+ * \d Sleep a little (delay 2 seconds
+ * \n Line feed character
+ * \P Auth Key password
+ * \p pause 0.25 sec
+ * \r Carrige return character
+ * \s Space character
+ * \T Telephone number(s) (defined via `set phone')
+ * \t Tab character
+ * \U Auth User
+ */
+static char *
+ExpandString(struct chat *c, const char *str, char *result, int reslen, int cr)
+{
+ int len;
+
+ result[--reslen] = '\0';
+ while (*str && reslen > 0) {
+ switch (*str) {
+ case '\\':
+ str++;
+ switch (*str) {
+ case 'c':
+ cr = 0;
+ break;
+ case 'd': /* Delay 2 seconds */
+ chat_Pause(c, 2 * SECTICKS);
+ break;
+ case 'p':
+ chat_Pause(c, SECTICKS / 4);
+ break; /* Delay 0.25 seconds */
+ case 'n':
+ *result++ = '\n';
+ reslen--;
+ break;
+ case 'r':
+ *result++ = '\r';
+ reslen--;
+ break;
+ case 's':
+ *result++ = ' ';
+ reslen--;
+ break;
+ case 't':
+ *result++ = '\t';
+ reslen--;
+ break;
+ case 'P':
+ strncpy(result, c->physical->dl->bundle->cfg.auth.key, reslen);
+ len = strlen(result);
+ reslen -= len;
+ result += len;
+ break;
+ case 'T':
+ if (c->phone) {
+ strncpy(result, c->phone, reslen);
+ len = strlen(result);
+ reslen -= len;
+ result += len;
+ }
+ break;
+ case 'U':
+ strncpy(result, c->physical->dl->bundle->cfg.auth.name, reslen);
+ len = strlen(result);
+ reslen -= len;
+ result += len;
+ break;
+ default:
+ reslen--;
+ *result++ = *str;
+ break;
+ }
+ if (*str)
+ str++;
+ break;
+ case '^':
+ str++;
+ if (*str) {
+ *result++ = *str++ & 0x1f;
+ reslen--;
+ }
+ break;
+ default:
+ *result++ = *str++;
+ reslen--;
+ break;
+ }
+ }
+ if (--reslen > 0) {
+ if (cr)
+ *result++ = '\r';
+ }
+ if (--reslen > 0)
+ *result++ = '\0';
+ return (result);
+}
+
+static void
+ExecStr(struct physical *physical, char *command, char *out, int olen)
+{
+ pid_t pid;
+ int fids[2];
+ char *argv[MAXARGS], *vector[MAXARGS], *startout, *endout;
+ int stat, nb, argc, i;
+
+ log_Printf(LogCHAT, "Exec: %s\n", command);
+ if ((argc = MakeArgs(command, vector, VECSIZE(vector),
+ PARSE_REDUCE|PARSE_NOHASH)) <= 0) {
+ if (argc < 0)
+ log_Printf(LogWARN, "Syntax error in exec command\n");
+ *out = '\0';
+ return;
+ }
+
+ if (pipe(fids) < 0) {
+ log_Printf(LogCHAT, "Unable to create pipe in ExecStr: %s\n",
+ strerror(errno));
+ *out = '\0';
+ return;
+ }
+ if ((pid = fork()) == 0) {
+ command_Expand(argv, argc, (char const *const *)vector,
+ physical->dl->bundle, 0, getpid());
+ close(fids[0]);
+ timer_TermService();
+ if (fids[1] == STDIN_FILENO)
+ fids[1] = dup(fids[1]);
+ dup2(physical->fd, STDIN_FILENO);
+ dup2(fids[1], STDERR_FILENO);
+ dup2(STDIN_FILENO, STDOUT_FILENO);
+ close(3);
+ if (open(_PATH_TTY, O_RDWR) != 3)
+ open(_PATH_DEVNULL, O_RDWR); /* Leave it closed if it fails... */
+ for (i = getdtablesize(); i > 3; i--)
+ fcntl(i, F_SETFD, 1);
+#ifndef NOSUID
+ setuid(ID0realuid());
+#endif
+ execvp(argv[0], argv);
+ fprintf(stderr, "execvp: %s: %s\n", argv[0], strerror(errno));
+ _exit(127);
+ } else {
+ char *name = strdup(vector[0]);
+
+ close(fids[1]);
+ endout = out + olen - 1;
+ startout = out;
+ while (out < endout) {
+ nb = read(fids[0], out, 1);
+ if (nb <= 0)
+ break;
+ out++;
+ }
+ *out = '\0';
+ close(fids[0]);
+ close(fids[1]);
+ waitpid(pid, &stat, WNOHANG);
+ if (WIFSIGNALED(stat)) {
+ log_Printf(LogWARN, "%s: signal %d\n", name, WTERMSIG(stat));
+ free(name);
+ *out = '\0';
+ return;
+ } else if (WIFEXITED(stat)) {
+ switch (WEXITSTATUS(stat)) {
+ case 0:
+ free(name);
+ break;
+ case 127:
+ log_Printf(LogWARN, "%s: %s\n", name, startout);
+ free(name);
+ *out = '\0';
+ return;
+ break;
+ default:
+ log_Printf(LogWARN, "%s: exit %d\n", name, WEXITSTATUS(stat));
+ free(name);
+ *out = '\0';
+ return;
+ break;
+ }
+ } else {
+ log_Printf(LogWARN, "%s: Unexpected exit result\n", name);
+ free(name);
+ *out = '\0';
+ return;
+ }
+ }
+}
diff --git a/usr.sbin/ppp/chat.h b/usr.sbin/ppp/chat.h
new file mode 100644
index 0000000..b8162bf
--- /dev/null
+++ b/usr.sbin/ppp/chat.h
@@ -0,0 +1,82 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define CHAT_EXPECT 0
+#define CHAT_SEND 1
+#define CHAT_DONE 2
+#define CHAT_FAILED 3
+
+#define MAXABORTS 50
+
+struct physical;
+
+struct chat {
+ struct fdescriptor desc;
+ struct physical *physical;
+
+ int state; /* Our CHAT_* status */
+
+ char script[LINE_LEN]; /* Our arg buffer */
+ char *argv[MAXARGS]; /* Our arguments (pointing to script) */
+ int argc; /* Number of argv's */
+
+ int arg; /* Our current arg number */
+ char exp[LINE_LEN]; /* Our translated current argument */
+ char *argptr; /* Our current arg pointer */
+ int arglen; /* The length of argptr */
+ char *nargptr; /* Our next for expect-send-expect */
+
+ char buf[LINE_LEN*2]; /* Our input */
+ char *bufstart; /* start of relevent data */
+ char *bufend; /* end of relevent data */
+
+ int TimeoutSec; /* Expect timeout value */
+ int TimedOut; /* We timed out */
+
+ const char *phone; /* Our phone number */
+
+ struct {
+ struct {
+ char *data; /* Abort the dial if we get one */
+ int len;
+ } string[MAXABORTS];
+ int num; /* How many AbortStrings */
+ } abort;
+
+ struct pppTimer pause; /* Inactivity timer */
+ struct pppTimer timeout; /* TimeoutSec timer */
+};
+
+#define descriptor2chat(d) \
+ ((d)->type == CHAT_DESCRIPTOR ? (struct chat *)(d) : NULL)
+#define VECSIZE(v) (sizeof(v) / sizeof(v[0]))
+
+extern void chat_Init(struct chat *, struct physical *);
+extern int chat_Setup(struct chat *, const char *, const char *);
+extern void chat_Finish(struct chat *);
+extern void chat_Destroy(struct chat *);
diff --git a/usr.sbin/ppp/command.c b/usr.sbin/ppp/command.c
new file mode 100644
index 0000000..cc23518
--- /dev/null
+++ b/usr.sbin/ppp/command.c
@@ -0,0 +1,3312 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <net/route.h>
+#include <netdb.h>
+#include <sys/un.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <unistd.h>
+
+#ifndef NONAT
+#ifdef LOCALNAT
+#include "alias.h"
+#else
+#include <alias.h>
+#endif
+#endif
+
+#include "layer.h"
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "iplist.h"
+#include "throughput.h"
+#include "slcompress.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#ifndef NONAT
+#include "nat_cmd.h"
+#endif
+#include "systems.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "main.h"
+#include "route.h"
+#include "ccp.h"
+#include "auth.h"
+#include "async.h"
+#include "link.h"
+#include "physical.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "server.h"
+#include "prompt.h"
+#include "chat.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+#include "iface.h"
+#include "id.h"
+#include "probe.h"
+
+/* ``set'' values */
+#define VAR_AUTHKEY 0
+#define VAR_DIAL 1
+#define VAR_LOGIN 2
+#define VAR_AUTHNAME 3
+#define VAR_AUTOLOAD 4
+#define VAR_WINSIZE 5
+#define VAR_DEVICE 6
+#define VAR_ACCMAP 7
+#define VAR_MRRU 8
+#define VAR_MRU 9
+#define VAR_MTU 10
+#define VAR_OPENMODE 11
+#define VAR_PHONE 12
+#define VAR_HANGUP 13
+#define VAR_IDLETIMEOUT 14
+#define VAR_LQRPERIOD 15
+#define VAR_LCPRETRY 16
+#define VAR_CHAPRETRY 17
+#define VAR_PAPRETRY 18
+#define VAR_CCPRETRY 19
+#define VAR_IPCPRETRY 20
+#define VAR_DNS 21
+#define VAR_NBNS 22
+#define VAR_MODE 23
+#define VAR_CALLBACK 24
+#define VAR_CBCP 25
+#define VAR_CHOKED 26
+#define VAR_SENDPIPE 27
+#define VAR_RECVPIPE 28
+#define VAR_RADIUS 29
+#define VAR_CD 30
+#define VAR_PARITY 31
+#define VAR_CRTSCTS 32
+#define VAR_URGENTPORTS 33
+#define VAR_LOGOUT 34
+#define VAR_IFQUEUE 35
+#define VAR_MPPE 36
+#define VAR_IPV6CPRETRY 37
+#define VAR_RAD_ALIVE 38
+#define VAR_PPPOE 39
+#define VAR_PORT_ID 40
+
+/* ``accept|deny|disable|enable'' masks */
+#define NEG_HISMASK (1)
+#define NEG_MYMASK (2)
+
+/* ``accept|deny|disable|enable'' values */
+#define NEG_ACFCOMP 40
+#define NEG_CHAP05 41
+#define NEG_CHAP80 42
+#define NEG_CHAP80LM 43
+#define NEG_DEFLATE 44
+#define NEG_DNS 45
+#define NEG_ECHO 46
+#define NEG_ENDDISC 47
+#define NEG_LQR 48
+#define NEG_PAP 49
+#define NEG_PPPDDEFLATE 50
+#define NEG_PRED1 51
+#define NEG_PROTOCOMP 52
+#define NEG_SHORTSEQ 53
+#define NEG_VJCOMP 54
+#define NEG_MPPE 55
+#define NEG_CHAP81 56
+
+const char Version[] = "3.4.2";
+
+static int ShowCommand(struct cmdargs const *);
+static int TerminalCommand(struct cmdargs const *);
+static int QuitCommand(struct cmdargs const *);
+static int OpenCommand(struct cmdargs const *);
+static int CloseCommand(struct cmdargs const *);
+static int DownCommand(struct cmdargs const *);
+static int SetCommand(struct cmdargs const *);
+static int LinkCommand(struct cmdargs const *);
+static int AddCommand(struct cmdargs const *);
+static int DeleteCommand(struct cmdargs const *);
+static int NegotiateCommand(struct cmdargs const *);
+static int ClearCommand(struct cmdargs const *);
+static int RunListCommand(struct cmdargs const *);
+static int IfaceAddCommand(struct cmdargs const *);
+static int IfaceDeleteCommand(struct cmdargs const *);
+static int IfaceClearCommand(struct cmdargs const *);
+static int SetProcTitle(struct cmdargs const *);
+#ifndef NONAT
+static int NatEnable(struct cmdargs const *);
+static int NatOption(struct cmdargs const *);
+#endif
+
+extern struct libalias *la;
+
+static const char *
+showcx(struct cmdtab const *cmd)
+{
+ if (cmd->lauth & LOCAL_CX)
+ return "(c)";
+ else if (cmd->lauth & LOCAL_CX_OPT)
+ return "(o)";
+
+ return "";
+}
+
+static int
+HelpCommand(struct cmdargs const *arg)
+{
+ struct cmdtab const *cmd;
+ int n, cmax, dmax, cols, cxlen;
+ const char *cx;
+
+ if (!arg->prompt) {
+ log_Printf(LogWARN, "help: Cannot help without a prompt\n");
+ return 0;
+ }
+
+ if (arg->argc > arg->argn) {
+ for (cmd = arg->cmdtab; cmd->name || cmd->alias; cmd++)
+ if ((cmd->lauth & arg->prompt->auth) &&
+ ((cmd->name && !strcasecmp(cmd->name, arg->argv[arg->argn])) ||
+ (cmd->alias && !strcasecmp(cmd->alias, arg->argv[arg->argn])))) {
+ prompt_Printf(arg->prompt, "%s %s\n", cmd->syntax, showcx(cmd));
+ return 0;
+ }
+ return -1;
+ }
+
+ cmax = dmax = 0;
+ for (cmd = arg->cmdtab; cmd->func; cmd++)
+ if (cmd->name && (cmd->lauth & arg->prompt->auth)) {
+ if ((n = strlen(cmd->name) + strlen(showcx(cmd))) > cmax)
+ cmax = n;
+ if ((n = strlen(cmd->helpmes)) > dmax)
+ dmax = n;
+ }
+
+ cols = 80 / (dmax + cmax + 3);
+ n = 0;
+ prompt_Printf(arg->prompt, "(o) = Optional context,"
+ " (c) = Context required\n");
+ for (cmd = arg->cmdtab; cmd->func; cmd++)
+ if (cmd->name && (cmd->lauth & arg->prompt->auth)) {
+ cx = showcx(cmd);
+ cxlen = cmax - strlen(cmd->name);
+ if (n % cols != 0)
+ prompt_Printf(arg->prompt, " ");
+ prompt_Printf(arg->prompt, "%s%-*.*s: %-*.*s",
+ cmd->name, cxlen, cxlen, cx, dmax, dmax, cmd->helpmes);
+ if (++n % cols == 0)
+ prompt_Printf(arg->prompt, "\n");
+ }
+ if (n % cols != 0)
+ prompt_Printf(arg->prompt, "\n");
+
+ return 0;
+}
+
+static int
+IdentCommand(struct cmdargs const *arg)
+{
+ Concatinate(arg->cx->physical->link.lcp.cfg.ident,
+ sizeof arg->cx->physical->link.lcp.cfg.ident,
+ arg->argc - arg->argn, arg->argv + arg->argn);
+ return 0;
+}
+
+static int
+SendIdentification(struct cmdargs const *arg)
+{
+ if (arg->cx->state < DATALINK_LCP) {
+ log_Printf(LogWARN, "sendident: link has not reached LCP\n");
+ return 2;
+ }
+ return lcp_SendIdentification(&arg->cx->physical->link.lcp) ? 0 : 1;
+}
+
+static int
+CloneCommand(struct cmdargs const *arg)
+{
+ char namelist[LINE_LEN];
+ char *name;
+ int f;
+
+ if (arg->argc == arg->argn)
+ return -1;
+
+ namelist[sizeof namelist - 1] = '\0';
+ for (f = arg->argn; f < arg->argc; f++) {
+ strncpy(namelist, arg->argv[f], sizeof namelist - 1);
+ for(name = strtok(namelist, ", "); name; name = strtok(NULL,", "))
+ bundle_DatalinkClone(arg->bundle, arg->cx, name);
+ }
+
+ return 0;
+}
+
+static int
+RemoveCommand(struct cmdargs const *arg)
+{
+ if (arg->argc != arg->argn)
+ return -1;
+
+ if (arg->cx->state != DATALINK_CLOSED) {
+ log_Printf(LogWARN, "remove: Cannot delete links that aren't closed\n");
+ return 2;
+ }
+
+ bundle_DatalinkRemove(arg->bundle, arg->cx);
+ return 0;
+}
+
+static int
+RenameCommand(struct cmdargs const *arg)
+{
+ if (arg->argc != arg->argn + 1)
+ return -1;
+
+ if (bundle_RenameDatalink(arg->bundle, arg->cx, arg->argv[arg->argn]))
+ return 0;
+
+ log_Printf(LogWARN, "%s -> %s: target name already exists\n",
+ arg->cx->name, arg->argv[arg->argn]);
+ return 1;
+}
+
+static int
+LoadCommand(struct cmdargs const *arg)
+{
+ const char *err;
+ int n, mode;
+
+ mode = arg->bundle->phys_type.all;
+
+ if (arg->argn < arg->argc) {
+ for (n = arg->argn; n < arg->argc; n++)
+ if ((err = system_IsValid(arg->argv[n], arg->prompt, mode)) != NULL) {
+ log_Printf(LogWARN, "%s: %s\n", arg->argv[n], err);
+ return 1;
+ }
+
+ for (n = arg->argn; n < arg->argc; n++) {
+ bundle_SetLabel(arg->bundle, arg->argv[arg->argc - 1]);
+ system_Select(arg->bundle, arg->argv[n], CONFFILE, arg->prompt, arg->cx);
+ }
+ bundle_SetLabel(arg->bundle, arg->argv[arg->argc - 1]);
+ } else if ((err = system_IsValid("default", arg->prompt, mode)) != NULL) {
+ log_Printf(LogWARN, "default: %s\n", err);
+ return 1;
+ } else {
+ bundle_SetLabel(arg->bundle, "default");
+ system_Select(arg->bundle, "default", CONFFILE, arg->prompt, arg->cx);
+ bundle_SetLabel(arg->bundle, "default");
+ }
+
+ return 0;
+}
+
+static int
+LogCommand(struct cmdargs const *arg)
+{
+ char buf[LINE_LEN];
+
+ if (arg->argn < arg->argc) {
+ char *argv[MAXARGS];
+ int argc = arg->argc - arg->argn;
+
+ if (argc >= (int)(sizeof argv / sizeof argv[0])) {
+ argc = sizeof argv / sizeof argv[0] - 1;
+ log_Printf(LogWARN, "Truncating log command to %d args\n", argc);
+ }
+ command_Expand(argv, argc, arg->argv + arg->argn, arg->bundle, 1, getpid());
+ Concatinate(buf, sizeof buf, argc, (const char *const *)argv);
+ log_Printf(LogLOG, "%s\n", buf);
+ command_Free(argc, argv);
+ return 0;
+ }
+
+ return -1;
+}
+
+static int
+SaveCommand(struct cmdargs const *arg __unused)
+{
+ log_Printf(LogWARN, "save command is not yet implemented.\n");
+ return 1;
+}
+
+static int
+DialCommand(struct cmdargs const *arg)
+{
+ int res;
+
+ if ((arg->cx && !(arg->cx->physical->type & (PHYS_INTERACTIVE|PHYS_AUTO)))
+ || (!arg->cx &&
+ (arg->bundle->phys_type.all & ~(PHYS_INTERACTIVE|PHYS_AUTO)))) {
+ log_Printf(LogWARN, "Manual dial is only available for auto and"
+ " interactive links\n");
+ return 1;
+ }
+
+ if (arg->argc > arg->argn && (res = LoadCommand(arg)) != 0)
+ return res;
+
+ bundle_Open(arg->bundle, arg->cx ? arg->cx->name : NULL, PHYS_ALL, 1);
+
+ return 0;
+}
+
+#define isinword(ch) (isalnum(ch) || (ch) == '_')
+
+static char *
+strstrword(char *big, const char *little)
+{
+ /* Get the first occurance of the word ``little'' in ``big'' */
+ char *pos;
+ int len;
+
+ pos = big;
+ len = strlen(little);
+
+ while ((pos = strstr(pos, little)) != NULL)
+ if ((pos != big && isinword(pos[-1])) || isinword(pos[len]))
+ pos++;
+ else if (pos != big && pos[-1] == '\\')
+ memmove(pos - 1, pos, strlen(pos) + 1);
+ else
+ break;
+
+ return pos;
+}
+
+static char *
+subst(char *tgt, const char *oldstr, const char *newstr)
+{
+ /* tgt is a malloc()d area... realloc() as necessary */
+ char *word, *ntgt;
+ int ltgt, loldstr, lnewstr, pos;
+
+ if ((word = strstrword(tgt, oldstr)) == NULL)
+ return tgt;
+
+ ltgt = strlen(tgt) + 1;
+ loldstr = strlen(oldstr);
+ lnewstr = strlen(newstr);
+ do {
+ pos = word - tgt;
+ if (loldstr > lnewstr)
+ bcopy(word + loldstr, word + lnewstr, ltgt - pos - loldstr);
+ if (loldstr != lnewstr) {
+ ntgt = realloc(tgt, ltgt += lnewstr - loldstr);
+ if (ntgt == NULL)
+ break; /* Oh wonderful ! */
+ word = ntgt + pos;
+ tgt = ntgt;
+ }
+ if (lnewstr > loldstr)
+ bcopy(word + loldstr, word + lnewstr, ltgt - pos - lnewstr);
+ bcopy(newstr, word, lnewstr);
+ } while ((word = strstrword(word, oldstr)));
+
+ return tgt;
+}
+
+static char *
+substip(char *tgt, const char *oldstr, struct in_addr ip)
+{
+ return subst(tgt, oldstr, inet_ntoa(ip));
+}
+
+static char *
+substlong(char *tgt, const char *oldstr, long l)
+{
+ char buf[23];
+
+ snprintf(buf, sizeof buf, "%ld", l);
+
+ return subst(tgt, oldstr, buf);
+}
+
+static char *
+substull(char *tgt, const char *oldstr, unsigned long long ull)
+{
+ char buf[21];
+
+ snprintf(buf, sizeof buf, "%llu", ull);
+
+ return subst(tgt, oldstr, buf);
+}
+
+
+#ifndef NOINET6
+static char *
+substipv6(char *tgt, const char *oldstr, const struct ncpaddr *ip)
+{
+ return subst(tgt, oldstr, ncpaddr_ntoa(ip));
+}
+
+#ifndef NORADIUS
+static char *
+substipv6prefix(char *tgt, const char *oldstr, const uint8_t *ipv6prefix)
+{
+ uint8_t ipv6addr[INET6_ADDRSTRLEN];
+ uint8_t prefix[INET6_ADDRSTRLEN + sizeof("/128") - 1];
+
+ if (ipv6prefix) {
+ inet_ntop(AF_INET6, &ipv6prefix[2], ipv6addr, sizeof(ipv6addr));
+ snprintf(prefix, sizeof(prefix), "%s/%d", ipv6addr, ipv6prefix[1]);
+ } else
+ prefix[0] = '\0';
+ return subst(tgt, oldstr, prefix);
+}
+#endif
+#endif
+
+void
+command_Expand(char **nargv, int argc, char const *const *oargv,
+ struct bundle *bundle, int inc0, pid_t pid)
+{
+ int arg, secs;
+ char uptime[20];
+ unsigned long long oin, oout, pin, pout;
+
+ if (inc0)
+ arg = 0; /* Start at arg 0 */
+ else {
+ nargv[0] = strdup(oargv[0]);
+ arg = 1;
+ }
+
+ secs = bundle_Uptime(bundle);
+ snprintf(uptime, sizeof uptime, "%d:%02d:%02d",
+ secs / 3600, (secs / 60) % 60, secs % 60);
+ oin = bundle->ncp.ipcp.throughput.OctetsIn;
+ oout = bundle->ncp.ipcp.throughput.OctetsOut;
+ pin = bundle->ncp.ipcp.throughput.PacketsIn;
+ pout = bundle->ncp.ipcp.throughput.PacketsOut;
+#ifndef NOINET6
+ oin += bundle->ncp.ipv6cp.throughput.OctetsIn;
+ oout += bundle->ncp.ipv6cp.throughput.OctetsOut;
+ pin += bundle->ncp.ipv6cp.throughput.PacketsIn;
+ pout += bundle->ncp.ipv6cp.throughput.PacketsOut;
+#endif
+
+ for (; arg < argc; arg++) {
+ nargv[arg] = strdup(oargv[arg]);
+ nargv[arg] = subst(nargv[arg], "AUTHNAME", bundle->cfg.auth.name);
+ nargv[arg] = substip(nargv[arg], "DNS0", bundle->ncp.ipcp.ns.dns[0]);
+ nargv[arg] = substip(nargv[arg], "DNS1", bundle->ncp.ipcp.ns.dns[1]);
+ nargv[arg] = subst(nargv[arg], "ENDDISC",
+ mp_Enddisc(bundle->ncp.mp.cfg.enddisc.class,
+ bundle->ncp.mp.cfg.enddisc.address,
+ bundle->ncp.mp.cfg.enddisc.len));
+ nargv[arg] = substip(nargv[arg], "HISADDR", bundle->ncp.ipcp.peer_ip);
+#ifndef NOINET6
+ nargv[arg] = substipv6(nargv[arg], "HISADDR6", &bundle->ncp.ipv6cp.hisaddr);
+#endif
+ nargv[arg] = subst(nargv[arg], "INTERFACE", bundle->iface->name);
+ nargv[arg] = substull(nargv[arg], "IPOCTETSIN",
+ bundle->ncp.ipcp.throughput.OctetsIn);
+ nargv[arg] = substull(nargv[arg], "IPOCTETSOUT",
+ bundle->ncp.ipcp.throughput.OctetsOut);
+ nargv[arg] = substull(nargv[arg], "IPPACKETSIN",
+ bundle->ncp.ipcp.throughput.PacketsIn);
+ nargv[arg] = substull(nargv[arg], "IPPACKETSOUT",
+ bundle->ncp.ipcp.throughput.PacketsOut);
+#ifndef NOINET6
+ nargv[arg] = substull(nargv[arg], "IPV6OCTETSIN",
+ bundle->ncp.ipv6cp.throughput.OctetsIn);
+ nargv[arg] = substull(nargv[arg], "IPV6OCTETSOUT",
+ bundle->ncp.ipv6cp.throughput.OctetsOut);
+ nargv[arg] = substull(nargv[arg], "IPV6PACKETSIN",
+ bundle->ncp.ipv6cp.throughput.PacketsIn);
+ nargv[arg] = substull(nargv[arg], "IPV6PACKETSOUT",
+ bundle->ncp.ipv6cp.throughput.PacketsOut);
+#endif
+ nargv[arg] = subst(nargv[arg], "LABEL", bundle_GetLabel(bundle));
+ nargv[arg] = substip(nargv[arg], "MYADDR", bundle->ncp.ipcp.my_ip);
+#ifndef NOINET6
+ nargv[arg] = substipv6(nargv[arg], "MYADDR6", &bundle->ncp.ipv6cp.myaddr);
+#ifndef NORADIUS
+ nargv[arg] = substipv6prefix(nargv[arg], "IPV6PREFIX",
+ bundle->radius.ipv6prefix);
+#endif
+#endif
+ nargv[arg] = substull(nargv[arg], "OCTETSIN", oin);
+ nargv[arg] = substull(nargv[arg], "OCTETSOUT", oout);
+ nargv[arg] = substull(nargv[arg], "PACKETSIN", pin);
+ nargv[arg] = substull(nargv[arg], "PACKETSOUT", pout);
+ nargv[arg] = subst(nargv[arg], "PEER_ENDDISC",
+ mp_Enddisc(bundle->ncp.mp.peer.enddisc.class,
+ bundle->ncp.mp.peer.enddisc.address,
+ bundle->ncp.mp.peer.enddisc.len));
+ nargv[arg] = substlong(nargv[arg], "PROCESSID", pid);
+ if (server.cfg.port)
+ nargv[arg] = substlong(nargv[arg], "SOCKNAME", server.cfg.port);
+ else
+ nargv[arg] = subst(nargv[arg], "SOCKNAME", server.cfg.sockname);
+ nargv[arg] = subst(nargv[arg], "UPTIME", uptime);
+ nargv[arg] = subst(nargv[arg], "USER", bundle->ncp.mp.peer.authname);
+ nargv[arg] = subst(nargv[arg], "VERSION", Version);
+ }
+ nargv[arg] = NULL;
+}
+
+void
+command_Free(int argc, char **argv)
+{
+ while (argc) {
+ free(*argv);
+ argc--;
+ argv++;
+ }
+}
+
+static int
+ShellCommand(struct cmdargs const *arg, int bg)
+{
+ const char *shell;
+ pid_t shpid, pid;
+
+#ifdef SHELL_ONLY_INTERACTIVELY
+ /* we're only allowed to shell when we run ppp interactively */
+ if (arg->prompt && arg->prompt->owner) {
+ log_Printf(LogWARN, "Can't start a shell from a network connection\n");
+ return 1;
+ }
+#endif
+
+ if (arg->argc == arg->argn) {
+ if (!arg->prompt) {
+ log_Printf(LogWARN, "Can't start an interactive shell from"
+ " a config file\n");
+ return 1;
+ } else if (arg->prompt->owner) {
+ log_Printf(LogWARN, "Can't start an interactive shell from"
+ " a socket connection\n");
+ return 1;
+ } else if (bg) {
+ log_Printf(LogWARN, "Can only start an interactive shell in"
+ " the foreground mode\n");
+ return 1;
+ }
+ }
+
+ pid = getpid();
+ if ((shpid = fork()) == 0) {
+ int i, fd;
+
+ if ((shell = getenv("SHELL")) == 0)
+ shell = _PATH_BSHELL;
+
+ timer_TermService();
+
+ if (arg->prompt)
+ fd = arg->prompt->fd_out;
+ else if ((fd = open(_PATH_DEVNULL, O_RDWR)) == -1) {
+ log_Printf(LogALERT, "Failed to open %s: %s\n",
+ _PATH_DEVNULL, strerror(errno));
+ exit(1);
+ }
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ for (i = getdtablesize(); i > STDERR_FILENO; i--)
+ fcntl(i, F_SETFD, 1);
+
+#ifndef NOSUID
+ setuid(ID0realuid());
+#endif
+ if (arg->argc > arg->argn) {
+ /* substitute pseudo args */
+ char *argv[MAXARGS];
+ int argc = arg->argc - arg->argn;
+
+ if (argc >= (int)(sizeof argv / sizeof argv[0])) {
+ argc = sizeof argv / sizeof argv[0] - 1;
+ log_Printf(LogWARN, "Truncating shell command to %d args\n", argc);
+ }
+ command_Expand(argv, argc, arg->argv + arg->argn, arg->bundle, 0, pid);
+ if (bg) {
+ pid_t p;
+
+ p = getpid();
+ if (daemon(1, 1) == -1) {
+ log_Printf(LogERROR, "%ld: daemon: %s\n", (long)p, strerror(errno));
+ exit(1);
+ }
+ } else if (arg->prompt)
+ printf("ppp: Pausing until %s finishes\n", arg->argv[arg->argn]);
+ execvp(argv[0], argv);
+ } else {
+ if (arg->prompt)
+ printf("ppp: Pausing until %s finishes\n", shell);
+ prompt_TtyOldMode(arg->prompt);
+ execl(shell, shell, (char *)NULL);
+ }
+
+ log_Printf(LogWARN, "exec() of %s failed: %s\n",
+ arg->argc > arg->argn ? arg->argv[arg->argn] : shell,
+ strerror(errno));
+ _exit(255);
+ }
+
+ if (shpid == (pid_t)-1)
+ log_Printf(LogERROR, "Fork failed: %s\n", strerror(errno));
+ else {
+ int status;
+ waitpid(shpid, &status, 0);
+ }
+
+ if (arg->prompt && !arg->prompt->owner)
+ prompt_TtyCommandMode(arg->prompt);
+
+ return 0;
+}
+
+static int
+BgShellCommand(struct cmdargs const *arg)
+{
+ if (arg->argc == arg->argn)
+ return -1;
+ return ShellCommand(arg, 1);
+}
+
+static int
+FgShellCommand(struct cmdargs const *arg)
+{
+ return ShellCommand(arg, 0);
+}
+
+static int
+ResolvCommand(struct cmdargs const *arg)
+{
+ if (arg->argc == arg->argn + 1) {
+ if (!strcasecmp(arg->argv[arg->argn], "reload"))
+ ipcp_LoadDNS(&arg->bundle->ncp.ipcp);
+ else if (!strcasecmp(arg->argv[arg->argn], "restore"))
+ ipcp_RestoreDNS(&arg->bundle->ncp.ipcp);
+ else if (!strcasecmp(arg->argv[arg->argn], "rewrite"))
+ ipcp_WriteDNS(&arg->bundle->ncp.ipcp);
+ else if (!strcasecmp(arg->argv[arg->argn], "readonly"))
+ arg->bundle->ncp.ipcp.ns.writable = 0;
+ else if (!strcasecmp(arg->argv[arg->argn], "writable"))
+ arg->bundle->ncp.ipcp.ns.writable = 1;
+ else
+ return -1;
+
+ return 0;
+ }
+
+ return -1;
+}
+
+#ifndef NONAT
+static struct cmdtab const NatCommands[] =
+{
+ {"addr", NULL, nat_RedirectAddr, LOCAL_AUTH,
+ "static address translation", "nat addr [addr_local addr_alias]", NULL},
+ {"deny_incoming", NULL, NatOption, LOCAL_AUTH,
+ "stop incoming connections", "nat deny_incoming yes|no",
+ (const void *) PKT_ALIAS_DENY_INCOMING},
+ {"enable", NULL, NatEnable, LOCAL_AUTH,
+ "enable NAT", "nat enable yes|no", NULL},
+ {"log", NULL, NatOption, LOCAL_AUTH,
+ "log NAT link creation", "nat log yes|no",
+ (const void *) PKT_ALIAS_LOG},
+ {"port", NULL, nat_RedirectPort, LOCAL_AUTH, "port redirection",
+ "nat port proto localaddr:port[-port] aliasport[-aliasport]", NULL},
+ {"proto", NULL, nat_RedirectProto, LOCAL_AUTH, "protocol redirection",
+ "nat proto proto localIP [publicIP [remoteIP]]", NULL},
+ {"proxy", NULL, nat_ProxyRule, LOCAL_AUTH,
+ "proxy control", "nat proxy server host[:port] ...", NULL},
+#ifndef NO_FW_PUNCH
+ {"punch_fw", NULL, nat_PunchFW, LOCAL_AUTH,
+ "firewall control", "nat punch_fw [base count]", NULL},
+#endif
+ {"skinny_port", NULL, nat_SkinnyPort, LOCAL_AUTH,
+ "TCP port used by Skinny Station protocol", "nat skinny_port [port]", NULL},
+ {"same_ports", NULL, NatOption, LOCAL_AUTH,
+ "try to leave port numbers unchanged", "nat same_ports yes|no",
+ (const void *) PKT_ALIAS_SAME_PORTS},
+ {"target", NULL, nat_SetTarget, LOCAL_AUTH,
+ "Default address for incoming connections", "nat target addr", NULL},
+ {"unregistered_only", NULL, NatOption, LOCAL_AUTH,
+ "translate unregistered (private) IP address space only",
+ "nat unregistered_only yes|no",
+ (const void *) PKT_ALIAS_UNREGISTERED_ONLY},
+ {"use_sockets", NULL, NatOption, LOCAL_AUTH,
+ "allocate host sockets", "nat use_sockets yes|no",
+ (const void *) PKT_ALIAS_USE_SOCKETS},
+ {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
+ "Display this message", "nat help|? [command]", NatCommands},
+ {NULL, NULL, NULL, 0, NULL, NULL, NULL},
+};
+#endif
+
+static struct cmdtab const AllowCommands[] = {
+ {"modes", "mode", AllowModes, LOCAL_AUTH,
+ "Only allow certain ppp modes", "allow modes mode...", NULL},
+ {"users", "user", AllowUsers, LOCAL_AUTH,
+ "Only allow ppp access to certain users", "allow users logname...", NULL},
+ {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
+ "Display this message", "allow help|? [command]", AllowCommands},
+ {NULL, NULL, NULL, 0, NULL, NULL, NULL},
+};
+
+static struct cmdtab const IfaceCommands[] =
+{
+ {"add", NULL, IfaceAddCommand, LOCAL_AUTH,
+ "Add iface address", "iface add addr[/bits| mask] peer", NULL},
+ {NULL, "add!", IfaceAddCommand, LOCAL_AUTH,
+ "Add or change an iface address", "iface add! addr[/bits| mask] peer",
+ (void *)1},
+ {"clear", NULL, IfaceClearCommand, LOCAL_AUTH,
+ "Clear iface address(es)", "iface clear [INET | INET6]", NULL},
+ {"delete", "rm", IfaceDeleteCommand, LOCAL_AUTH,
+ "Delete iface address", "iface delete addr", NULL},
+ {NULL, "rm!", IfaceDeleteCommand, LOCAL_AUTH,
+ "Delete iface address", "iface delete addr", (void *)1},
+ {NULL, "delete!", IfaceDeleteCommand, LOCAL_AUTH,
+ "Delete iface address", "iface delete addr", (void *)1},
+ {"show", NULL, iface_Show, LOCAL_AUTH,
+ "Show iface address(es)", "iface show", NULL},
+ {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
+ "Display this message", "nat help|? [command]", IfaceCommands},
+ {NULL, NULL, NULL, 0, NULL, NULL, NULL},
+};
+
+static struct cmdtab const Commands[] = {
+ {"accept", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "accept option request", "accept option ..", NULL},
+ {"add", NULL, AddCommand, LOCAL_AUTH,
+ "add route", "add dest mask gateway", NULL},
+ {NULL, "add!", AddCommand, LOCAL_AUTH,
+ "add or change route", "add! dest mask gateway", (void *)1},
+ {"allow", "auth", RunListCommand, LOCAL_AUTH,
+ "Allow ppp access", "allow users|modes ....", AllowCommands},
+ {"bg", "!bg", BgShellCommand, LOCAL_AUTH,
+ "Run a background command", "[!]bg command", NULL},
+ {"clear", NULL, ClearCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Clear throughput statistics",
+ "clear ipcp|ipv6cp|physical [current|overall|peak]...", NULL},
+ {"clone", NULL, CloneCommand, LOCAL_AUTH | LOCAL_CX,
+ "Clone a link", "clone newname...", NULL},
+ {"close", NULL, CloseCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Close an FSM", "close [lcp|ccp]", NULL},
+ {"delete", NULL, DeleteCommand, LOCAL_AUTH,
+ "delete route", "delete dest", NULL},
+ {NULL, "delete!", DeleteCommand, LOCAL_AUTH,
+ "delete a route if it exists", "delete! dest", (void *)1},
+ {"deny", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Deny option request", "deny option ..", NULL},
+ {"dial", "call", DialCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Dial and login", "dial|call [system ...]", NULL},
+ {"disable", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Disable option", "disable option ..", NULL},
+ {"down", NULL, DownCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Generate a down event", "down [ccp|lcp]", NULL},
+ {"enable", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Enable option", "enable option ..", NULL},
+ {"ident", NULL, IdentCommand, LOCAL_AUTH | LOCAL_CX,
+ "Set the link identity", "ident text...", NULL},
+ {"iface", "interface", RunListCommand, LOCAL_AUTH,
+ "interface control", "iface option ...", IfaceCommands},
+ {"link", "datalink", LinkCommand, LOCAL_AUTH,
+ "Link specific commands", "link name command ...", NULL},
+ {"load", NULL, LoadCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Load settings", "load [system ...]", NULL},
+ {"log", NULL, LogCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "log information", "log word ...", NULL},
+#ifndef NONAT
+ {"nat", "alias", RunListCommand, LOCAL_AUTH,
+ "NAT control", "nat option yes|no", NatCommands},
+#endif
+ {"open", NULL, OpenCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Open an FSM", "open! [lcp|ccp|ipcp]", (void *)1},
+ {"passwd", NULL, PasswdCommand, LOCAL_NO_AUTH,
+ "Password for manipulation", "passwd LocalPassword", NULL},
+ {"quit", "bye", QuitCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
+ "Quit PPP program", "quit|bye [all]", NULL},
+ {"remove", "rm", RemoveCommand, LOCAL_AUTH | LOCAL_CX,
+ "Remove a link", "remove", NULL},
+ {"rename", "mv", RenameCommand, LOCAL_AUTH | LOCAL_CX,
+ "Rename a link", "rename name", NULL},
+ {"resolv", NULL, ResolvCommand, LOCAL_AUTH,
+ "Manipulate resolv.conf", "resolv readonly|reload|restore|rewrite|writable",
+ NULL},
+ {"save", NULL, SaveCommand, LOCAL_AUTH,
+ "Save settings", "save", NULL},
+ {"sendident", NULL, SendIdentification, LOCAL_AUTH | LOCAL_CX,
+ "Transmit the link identity", "sendident", NULL},
+ {"set", "setup", SetCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Set parameters", "set[up] var value", NULL},
+ {"shell", "!", FgShellCommand, LOCAL_AUTH,
+ "Run a subshell", "shell|! [sh command]", NULL},
+ {"show", NULL, ShowCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Show status and stats", "show var", NULL},
+ {"term", NULL, TerminalCommand, LOCAL_AUTH | LOCAL_CX,
+ "Enter terminal mode", "term", NULL},
+ {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
+ "Display this message", "help|? [command]", Commands},
+ {NULL, NULL, NULL, 0, NULL, NULL, NULL},
+};
+
+static int
+ShowEscape(struct cmdargs const *arg)
+{
+ if (arg->cx->physical->async.cfg.EscMap[32]) {
+ int code, bit;
+ const char *sep = "";
+
+ for (code = 0; code < 32; code++)
+ if (arg->cx->physical->async.cfg.EscMap[code])
+ for (bit = 0; bit < 8; bit++)
+ if (arg->cx->physical->async.cfg.EscMap[code] & (1 << bit)) {
+ prompt_Printf(arg->prompt, "%s0x%02x", sep, (code << 3) + bit);
+ sep = ", ";
+ }
+ prompt_Printf(arg->prompt, "\n");
+ }
+ return 0;
+}
+
+static int
+ShowTimerList(struct cmdargs const *arg)
+{
+ timer_Show(0, arg->prompt);
+ return 0;
+}
+
+static int
+ShowStopped(struct cmdargs const *arg)
+{
+ prompt_Printf(arg->prompt, " Stopped Timer: LCP: ");
+ if (!arg->cx->physical->link.lcp.fsm.StoppedTimer.load)
+ prompt_Printf(arg->prompt, "Disabled");
+ else
+ prompt_Printf(arg->prompt, "%ld secs",
+ arg->cx->physical->link.lcp.fsm.StoppedTimer.load / SECTICKS);
+
+ prompt_Printf(arg->prompt, ", CCP: ");
+ if (!arg->cx->physical->link.ccp.fsm.StoppedTimer.load)
+ prompt_Printf(arg->prompt, "Disabled");
+ else
+ prompt_Printf(arg->prompt, "%ld secs",
+ arg->cx->physical->link.ccp.fsm.StoppedTimer.load / SECTICKS);
+
+ prompt_Printf(arg->prompt, "\n");
+
+ return 0;
+}
+
+static int
+ShowVersion(struct cmdargs const *arg)
+{
+ prompt_Printf(arg->prompt, "PPP Version %s\n", Version);
+ return 0;
+}
+
+static int
+ShowProtocolStats(struct cmdargs const *arg)
+{
+ struct link *l = command_ChooseLink(arg);
+
+ prompt_Printf(arg->prompt, "%s:\n", l->name);
+ link_ReportProtocolStatus(l, arg->prompt);
+ return 0;
+}
+
+static struct cmdtab const ShowCommands[] = {
+ {"bundle", NULL, bundle_ShowStatus, LOCAL_AUTH,
+ "bundle details", "show bundle", NULL},
+ {"ccp", NULL, ccp_ReportStatus, LOCAL_AUTH | LOCAL_CX_OPT,
+ "CCP status", "show cpp", NULL},
+ {"compress", NULL, sl_Show, LOCAL_AUTH,
+ "VJ compression stats", "show compress", NULL},
+ {"escape", NULL, ShowEscape, LOCAL_AUTH | LOCAL_CX,
+ "escape characters", "show escape", NULL},
+ {"filter", NULL, filter_Show, LOCAL_AUTH,
+ "packet filters", "show filter [in|out|dial|alive]", NULL},
+ {"hdlc", NULL, hdlc_ReportStatus, LOCAL_AUTH | LOCAL_CX,
+ "HDLC errors", "show hdlc", NULL},
+ {"iface", "interface", iface_Show, LOCAL_AUTH,
+ "Interface status", "show iface", NULL},
+ {"ipcp", NULL, ipcp_Show, LOCAL_AUTH,
+ "IPCP status", "show ipcp", NULL},
+#ifndef NOINET6
+ {"ipv6cp", NULL, ipv6cp_Show, LOCAL_AUTH,
+ "IPV6CP status", "show ipv6cp", NULL},
+#endif
+ {"layers", NULL, link_ShowLayers, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Protocol layers", "show layers", NULL},
+ {"lcp", NULL, lcp_ReportStatus, LOCAL_AUTH | LOCAL_CX,
+ "LCP status", "show lcp", NULL},
+ {"link", "datalink", datalink_Show, LOCAL_AUTH | LOCAL_CX,
+ "(high-level) link info", "show link", NULL},
+ {"links", NULL, bundle_ShowLinks, LOCAL_AUTH,
+ "available link names", "show links", NULL},
+ {"log", NULL, log_ShowLevel, LOCAL_AUTH,
+ "log levels", "show log", NULL},
+ {"mem", NULL, mbuf_Show, LOCAL_AUTH,
+ "mbuf allocations", "show mem", NULL},
+ {"ncp", NULL, ncp_Show, LOCAL_AUTH,
+ "NCP status", "show ncp", NULL},
+ {"physical", NULL, physical_ShowStatus, LOCAL_AUTH | LOCAL_CX,
+ "(low-level) link info", "show physical", NULL},
+ {"mp", "multilink", mp_ShowStatus, LOCAL_AUTH,
+ "multilink setup", "show mp", NULL},
+ {"proto", NULL, ShowProtocolStats, LOCAL_AUTH | LOCAL_CX_OPT,
+ "protocol summary", "show proto", NULL},
+ {"route", NULL, route_Show, LOCAL_AUTH,
+ "routing table", "show route", NULL},
+ {"stopped", NULL, ShowStopped, LOCAL_AUTH | LOCAL_CX,
+ "STOPPED timeout", "show stopped", NULL},
+ {"timers", NULL, ShowTimerList, LOCAL_AUTH,
+ "alarm timers", "show timers", NULL},
+ {"version", NULL, ShowVersion, LOCAL_NO_AUTH | LOCAL_AUTH,
+ "version string", "show version", NULL},
+ {"who", NULL, log_ShowWho, LOCAL_AUTH,
+ "client list", "show who", NULL},
+ {"help", "?", HelpCommand, LOCAL_NO_AUTH | LOCAL_AUTH,
+ "Display this message", "show help|? [command]", ShowCommands},
+ {NULL, NULL, NULL, 0, NULL, NULL, NULL},
+};
+
+static struct cmdtab const *
+FindCommand(struct cmdtab const *cmds, const char *str, int *pmatch)
+{
+ int nmatch;
+ int len;
+ struct cmdtab const *found;
+
+ found = NULL;
+ len = strlen(str);
+ nmatch = 0;
+ while (cmds->func) {
+ if (cmds->name && strncasecmp(str, cmds->name, len) == 0) {
+ if (cmds->name[len] == '\0') {
+ *pmatch = 1;
+ return cmds;
+ }
+ nmatch++;
+ found = cmds;
+ } else if (cmds->alias && strncasecmp(str, cmds->alias, len) == 0) {
+ if (cmds->alias[len] == '\0') {
+ *pmatch = 1;
+ return cmds;
+ }
+ nmatch++;
+ found = cmds;
+ }
+ cmds++;
+ }
+ *pmatch = nmatch;
+ return found;
+}
+
+static const char *
+mkPrefix(int argc, char const *const *argv, char *tgt, int sz)
+{
+ int f, tlen, len;
+
+ tlen = 0;
+ for (f = 0; f < argc && tlen < sz - 2; f++) {
+ if (f)
+ tgt[tlen++] = ' ';
+ len = strlen(argv[f]);
+ if (len > sz - tlen - 1)
+ len = sz - tlen - 1;
+ strncpy(tgt+tlen, argv[f], len);
+ tlen += len;
+ }
+ tgt[tlen] = '\0';
+ return tgt;
+}
+
+static int
+FindExec(struct bundle *bundle, struct cmdtab const *cmds, int argc, int argn,
+ char const *const *argv, struct prompt *prompt, struct datalink *cx)
+{
+ struct cmdtab const *cmd;
+ int val = 1;
+ int nmatch;
+ struct cmdargs arg;
+ char prefix[100];
+
+ cmd = FindCommand(cmds, argv[argn], &nmatch);
+ if (nmatch > 1)
+ log_Printf(LogWARN, "%s: Ambiguous command\n",
+ mkPrefix(argn+1, argv, prefix, sizeof prefix));
+ else if (cmd && (!prompt || (cmd->lauth & prompt->auth))) {
+ if ((cmd->lauth & LOCAL_CX) && !cx)
+ /* We've got no context, but we require it */
+ cx = bundle2datalink(bundle, NULL);
+
+ if ((cmd->lauth & LOCAL_CX) && !cx)
+ log_Printf(LogWARN, "%s: No context (use the `link' command)\n",
+ mkPrefix(argn+1, argv, prefix, sizeof prefix));
+ else {
+ if (cx && !(cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) {
+ log_Printf(LogWARN, "%s: Redundant context (%s) ignored\n",
+ mkPrefix(argn+1, argv, prefix, sizeof prefix), cx->name);
+ cx = NULL;
+ }
+ arg.cmdtab = cmds;
+ arg.cmd = cmd;
+ arg.argc = argc;
+ arg.argn = argn+1;
+ arg.argv = argv;
+ arg.bundle = bundle;
+ arg.cx = cx;
+ arg.prompt = prompt;
+ val = (*cmd->func) (&arg);
+ }
+ } else
+ log_Printf(LogWARN, "%s: Invalid command\n",
+ mkPrefix(argn+1, argv, prefix, sizeof prefix));
+
+ if (val == -1)
+ log_Printf(LogWARN, "usage: %s\n", cmd->syntax);
+ else if (val)
+ log_Printf(LogWARN, "%s: Failed %d\n",
+ mkPrefix(argn+1, argv, prefix, sizeof prefix), val);
+
+ return val;
+}
+
+int
+command_Expand_Interpret(char *buff, int nb, char *argv[MAXARGS], int offset)
+{
+ char buff2[LINE_LEN-offset];
+
+ InterpretArg(buff, buff2);
+ strncpy(buff, buff2, LINE_LEN - offset - 1);
+ buff[LINE_LEN - offset - 1] = '\0';
+
+ return command_Interpret(buff, nb, argv);
+}
+
+int
+command_Interpret(char *buff, int nb, char *argv[MAXARGS])
+{
+ char *cp;
+
+ if (nb > 0) {
+ cp = buff + strcspn(buff, "\r\n");
+ if (cp)
+ *cp = '\0';
+ return MakeArgs(buff, argv, MAXARGS, PARSE_REDUCE);
+ }
+ return 0;
+}
+
+static int
+arghidden(char const *const *argv, int n)
+{
+ /* Is arg n of the given command to be hidden from the log ? */
+
+ /* set authkey xxxxx */
+ /* set key xxxxx */
+ if (n == 2 && !strncasecmp(argv[0], "se", 2) &&
+ (!strncasecmp(argv[1], "authk", 5) || !strncasecmp(argv[1], "ke", 2)))
+ return 1;
+
+ /* passwd xxxxx */
+ if (n == 1 && !strncasecmp(argv[0], "p", 1))
+ return 1;
+
+ /* set server port xxxxx .... */
+ if (n == 3 && !strncasecmp(argv[0], "se", 2) &&
+ !strncasecmp(argv[1], "se", 2))
+ return 1;
+
+ return 0;
+}
+
+void
+command_Run(struct bundle *bundle, int argc, char const *const *argv,
+ struct prompt *prompt, const char *label, struct datalink *cx)
+{
+ if (argc > 0) {
+ if (log_IsKept(LogCOMMAND)) {
+ char buf[LINE_LEN];
+ int f;
+ size_t n;
+
+ if (label) {
+ strncpy(buf, label, sizeof buf - 3);
+ buf[sizeof buf - 3] = '\0';
+ strcat(buf, ": ");
+ n = strlen(buf);
+ } else {
+ *buf = '\0';
+ n = 0;
+ }
+ buf[sizeof buf - 1] = '\0'; /* In case we run out of room in buf */
+
+ for (f = 0; f < argc; f++) {
+ if (n < sizeof buf - 1 && f)
+ buf[n++] = ' ';
+ if (arghidden(argv, f))
+ strncpy(buf+n, "********", sizeof buf - n - 1);
+ else
+ strncpy(buf+n, argv[f], sizeof buf - n - 1);
+ n += strlen(buf+n);
+ }
+ log_Printf(LogCOMMAND, "%s\n", buf);
+ }
+ FindExec(bundle, Commands, argc, 0, argv, prompt, cx);
+ }
+}
+
+int
+command_Decode(struct bundle *bundle, char *buff, int nb, struct prompt *prompt,
+ const char *label)
+{
+ int argc;
+ char *argv[MAXARGS];
+
+ if ((argc = command_Expand_Interpret(buff, nb, argv, 0)) < 0)
+ return 0;
+
+ command_Run(bundle, argc, (char const *const *)argv, prompt, label, NULL);
+ return 1;
+}
+
+static int
+ShowCommand(struct cmdargs const *arg)
+{
+ if (!arg->prompt)
+ log_Printf(LogWARN, "show: Cannot show without a prompt\n");
+ else if (arg->argc > arg->argn)
+ FindExec(arg->bundle, ShowCommands, arg->argc, arg->argn, arg->argv,
+ arg->prompt, arg->cx);
+ else
+ prompt_Printf(arg->prompt, "Use ``show ?'' to get a list.\n");
+
+ return 0;
+}
+
+static int
+TerminalCommand(struct cmdargs const *arg)
+{
+ if (!arg->prompt) {
+ log_Printf(LogWARN, "term: Need a prompt\n");
+ return 1;
+ }
+
+ if (arg->cx->physical->link.lcp.fsm.state > ST_CLOSED) {
+ prompt_Printf(arg->prompt, "LCP state is [%s]\n",
+ State2Nam(arg->cx->physical->link.lcp.fsm.state));
+ return 1;
+ }
+
+ datalink_Up(arg->cx, 0, 0);
+ prompt_TtyTermMode(arg->prompt, arg->cx);
+ return 0;
+}
+
+static int
+QuitCommand(struct cmdargs const *arg)
+{
+ if (!arg->prompt || prompt_IsController(arg->prompt) ||
+ (arg->argc > arg->argn && !strcasecmp(arg->argv[arg->argn], "all") &&
+ (arg->prompt->auth & LOCAL_AUTH)))
+ Cleanup();
+ if (arg->prompt)
+ prompt_Destroy(arg->prompt, 1);
+
+ return 0;
+}
+
+static int
+OpenCommand(struct cmdargs const *arg)
+{
+ if (arg->argc == arg->argn)
+ bundle_Open(arg->bundle, arg->cx ? arg->cx->name : NULL, PHYS_ALL, 1);
+ else if (arg->argc == arg->argn + 1) {
+ if (!strcasecmp(arg->argv[arg->argn], "lcp")) {
+ struct datalink *cx = arg->cx ?
+ arg->cx : bundle2datalink(arg->bundle, NULL);
+ if (cx) {
+ if (cx->physical->link.lcp.fsm.state == ST_OPENED)
+ fsm_Reopen(&cx->physical->link.lcp.fsm);
+ else
+ bundle_Open(arg->bundle, cx->name, PHYS_ALL, 1);
+ } else
+ log_Printf(LogWARN, "open lcp: You must specify a link\n");
+ } else if (!strcasecmp(arg->argv[arg->argn], "ccp")) {
+ struct fsm *fp;
+
+ fp = &command_ChooseLink(arg)->ccp.fsm;
+ if (fp->link->lcp.fsm.state != ST_OPENED)
+ log_Printf(LogWARN, "open: LCP must be open before opening CCP\n");
+ else if (fp->state == ST_OPENED)
+ fsm_Reopen(fp);
+ else {
+ fp->open_mode = 0; /* Not passive any more */
+ if (fp->state == ST_STOPPED) {
+ fsm_Down(fp);
+ fsm_Up(fp);
+ } else {
+ fsm_Up(fp);
+ fsm_Open(fp);
+ }
+ }
+ } else if (!strcasecmp(arg->argv[arg->argn], "ipcp")) {
+ if (arg->cx)
+ log_Printf(LogWARN, "open ipcp: You need not specify a link\n");
+ if (arg->bundle->ncp.ipcp.fsm.state == ST_OPENED)
+ fsm_Reopen(&arg->bundle->ncp.ipcp.fsm);
+ else
+ bundle_Open(arg->bundle, NULL, PHYS_ALL, 1);
+ } else
+ return -1;
+ } else
+ return -1;
+
+ return 0;
+}
+
+static int
+CloseCommand(struct cmdargs const *arg)
+{
+ if (arg->argc == arg->argn)
+ bundle_Close(arg->bundle, arg->cx ? arg->cx->name : NULL, CLOSE_STAYDOWN);
+ else if (arg->argc == arg->argn + 1) {
+ if (!strcasecmp(arg->argv[arg->argn], "lcp"))
+ bundle_Close(arg->bundle, arg->cx ? arg->cx->name : NULL, CLOSE_LCP);
+ else if (!strcasecmp(arg->argv[arg->argn], "ccp") ||
+ !strcasecmp(arg->argv[arg->argn], "ccp!")) {
+ struct fsm *fp;
+
+ fp = &command_ChooseLink(arg)->ccp.fsm;
+ if (fp->state == ST_OPENED) {
+ fsm_Close(fp);
+ if (arg->argv[arg->argn][3] == '!')
+ fp->open_mode = 0; /* Stay ST_CLOSED */
+ else
+ fp->open_mode = OPEN_PASSIVE; /* Wait for the peer to start */
+ }
+ } else
+ return -1;
+ } else
+ return -1;
+
+ return 0;
+}
+
+static int
+DownCommand(struct cmdargs const *arg)
+{
+ if (arg->argc == arg->argn) {
+ if (arg->cx)
+ datalink_Down(arg->cx, CLOSE_STAYDOWN);
+ else
+ bundle_Down(arg->bundle, CLOSE_STAYDOWN);
+ } else if (arg->argc == arg->argn + 1) {
+ if (!strcasecmp(arg->argv[arg->argn], "lcp")) {
+ if (arg->cx)
+ datalink_Down(arg->cx, CLOSE_LCP);
+ else
+ bundle_Down(arg->bundle, CLOSE_LCP);
+ } else if (!strcasecmp(arg->argv[arg->argn], "ccp")) {
+ struct fsm *fp = arg->cx ? &arg->cx->physical->link.ccp.fsm :
+ &arg->bundle->ncp.mp.link.ccp.fsm;
+ fsm2initial(fp);
+ } else
+ return -1;
+ } else
+ return -1;
+
+ return 0;
+}
+
+static int
+SetModemSpeed(struct cmdargs const *arg)
+{
+ long speed;
+ char *end;
+
+ if (arg->argc > arg->argn && *arg->argv[arg->argn]) {
+ if (arg->argc > arg->argn+1) {
+ log_Printf(LogWARN, "SetModemSpeed: Too many arguments\n");
+ return -1;
+ }
+ if (strcasecmp(arg->argv[arg->argn], "sync") == 0) {
+ physical_SetSync(arg->cx->physical);
+ return 0;
+ }
+ end = NULL;
+ speed = strtol(arg->argv[arg->argn], &end, 10);
+ if (*end || speed < 0) {
+ log_Printf(LogWARN, "SetModemSpeed: Bad argument \"%s\"",
+ arg->argv[arg->argn]);
+ return -1;
+ }
+ if (physical_SetSpeed(arg->cx->physical, speed))
+ return 0;
+ log_Printf(LogWARN, "%s: Invalid speed\n", arg->argv[arg->argn]);
+ } else
+ log_Printf(LogWARN, "SetModemSpeed: No speed specified\n");
+
+ return -1;
+}
+
+static int
+SetStoppedTimeout(struct cmdargs const *arg)
+{
+ struct link *l = &arg->cx->physical->link;
+
+ l->lcp.fsm.StoppedTimer.load = 0;
+ l->ccp.fsm.StoppedTimer.load = 0;
+ if (arg->argc <= arg->argn+2) {
+ if (arg->argc > arg->argn) {
+ l->lcp.fsm.StoppedTimer.load = atoi(arg->argv[arg->argn]) * SECTICKS;
+ if (arg->argc > arg->argn+1)
+ l->ccp.fsm.StoppedTimer.load = atoi(arg->argv[arg->argn+1]) * SECTICKS;
+ }
+ return 0;
+ }
+ return -1;
+}
+
+static int
+SetServer(struct cmdargs const *arg)
+{
+ int res = -1;
+
+ if (arg->argc > arg->argn && arg->argc < arg->argn+4) {
+ const char *port, *passwd, *mask;
+ size_t mlen;
+
+ /* What's what ? */
+ port = arg->argv[arg->argn];
+ if (arg->argc == arg->argn + 2) {
+ passwd = arg->argv[arg->argn+1];
+ mask = NULL;
+ } else if (arg->argc == arg->argn + 3) {
+ passwd = arg->argv[arg->argn+1];
+ mask = arg->argv[arg->argn+2];
+ mlen = strlen(mask);
+ if (mlen == 0 || mlen > 4 || strspn(mask, "01234567") != mlen ||
+ (mlen == 4 && *mask != '0')) {
+ log_Printf(LogWARN, "%s %s: %s: Invalid mask\n",
+ arg->argv[arg->argn - 2], arg->argv[arg->argn - 1], mask);
+ return -1;
+ }
+ } else if (arg->argc != arg->argn + 1)
+ return -1;
+ else if (strcasecmp(port, "none") == 0) {
+ if (server_Clear(arg->bundle))
+ log_Printf(LogPHASE, "Disabled server socket\n");
+ return 0;
+ } else if (strcasecmp(port, "open") == 0) {
+ switch (server_Reopen(arg->bundle)) {
+ case SERVER_OK:
+ return 0;
+ case SERVER_FAILED:
+ log_Printf(LogWARN, "Failed to reopen server port\n");
+ return 1;
+ case SERVER_UNSET:
+ log_Printf(LogWARN, "Cannot reopen unset server socket\n");
+ return 1;
+ default:
+ break;
+ }
+ return -1;
+ } else if (strcasecmp(port, "closed") == 0) {
+ if (server_Close(arg->bundle))
+ log_Printf(LogPHASE, "Closed server socket\n");
+ else
+ log_Printf(LogWARN, "Server socket not open\n");
+
+ return 0;
+ } else
+ return -1;
+
+ strncpy(server.cfg.passwd, passwd, sizeof server.cfg.passwd - 1);
+ server.cfg.passwd[sizeof server.cfg.passwd - 1] = '\0';
+
+ if (*port == '/') {
+ mode_t imask;
+ char *ptr, name[LINE_LEN + 12];
+
+ if (mask == NULL)
+ imask = (mode_t)-1;
+ else for (imask = mlen = 0; mask[mlen]; mlen++)
+ imask = (imask * 8) + mask[mlen] - '0';
+
+ ptr = strstr(port, "%d");
+ if (ptr) {
+ snprintf(name, sizeof name, "%.*s%d%s",
+ (int)(ptr - port), port, arg->bundle->unit, ptr + 2);
+ port = name;
+ }
+ res = server_LocalOpen(arg->bundle, port, imask);
+ } else {
+ int iport, add = 0;
+
+ if (mask != NULL)
+ return -1;
+
+ if (*port == '+') {
+ port++;
+ add = 1;
+ }
+ if (strspn(port, "0123456789") != strlen(port)) {
+ struct servent *s;
+
+ if ((s = getservbyname(port, "tcp")) == NULL) {
+ iport = 0;
+ log_Printf(LogWARN, "%s: Invalid port or service\n", port);
+ } else
+ iport = ntohs(s->s_port);
+ } else
+ iport = atoi(port);
+
+ if (iport) {
+ if (add)
+ iport += arg->bundle->unit;
+ res = server_TcpOpen(arg->bundle, iport);
+ } else
+ res = -1;
+ }
+ }
+
+ return res;
+}
+
+static int
+SetEscape(struct cmdargs const *arg)
+{
+ int code;
+ int argc = arg->argc - arg->argn;
+ char const *const *argv = arg->argv + arg->argn;
+
+ for (code = 0; code < 33; code++)
+ arg->cx->physical->async.cfg.EscMap[code] = 0;
+
+ while (argc-- > 0) {
+ sscanf(*argv++, "%x", &code);
+ code &= 0xff;
+ arg->cx->physical->async.cfg.EscMap[code >> 3] |= (1 << (code & 7));
+ arg->cx->physical->async.cfg.EscMap[32] = 1;
+ }
+ return 0;
+}
+
+static int
+SetInterfaceAddr(struct cmdargs const *arg)
+{
+ struct ncp *ncp = &arg->bundle->ncp;
+ struct ncpaddr ncpaddr;
+ const char *hisaddr;
+
+ if (arg->argc > arg->argn + 4)
+ return -1;
+
+ hisaddr = NULL;
+ memset(&ncp->ipcp.cfg.my_range, '\0', sizeof ncp->ipcp.cfg.my_range);
+ memset(&ncp->ipcp.cfg.peer_range, '\0', sizeof ncp->ipcp.cfg.peer_range);
+ ncp->ipcp.cfg.HaveTriggerAddress = 0;
+ ncp->ipcp.cfg.netmask.s_addr = INADDR_ANY;
+ iplist_reset(&ncp->ipcp.cfg.peer_list);
+
+ if (arg->argc > arg->argn) {
+ if (!ncprange_aton(&ncp->ipcp.cfg.my_range, ncp, arg->argv[arg->argn]))
+ return 1;
+ if (arg->argc > arg->argn+1) {
+ hisaddr = arg->argv[arg->argn+1];
+ if (arg->argc > arg->argn+2) {
+ ncp->ipcp.ifmask = ncp->ipcp.cfg.netmask =
+ GetIpAddr(arg->argv[arg->argn+2]);
+ if (arg->argc > arg->argn+3) {
+ ncp->ipcp.cfg.TriggerAddress = GetIpAddr(arg->argv[arg->argn+3]);
+ ncp->ipcp.cfg.HaveTriggerAddress = 1;
+ }
+ }
+ }
+ }
+
+ /* 0.0.0.0 means any address (0 bits) */
+ ncprange_getaddr(&ncp->ipcp.cfg.my_range, &ncpaddr);
+ ncpaddr_getip4(&ncpaddr, &ncp->ipcp.my_ip);
+ if (ncp->ipcp.my_ip.s_addr == INADDR_ANY)
+ ncprange_setwidth(&ncp->ipcp.cfg.my_range, 0);
+ bundle_AdjustFilters(arg->bundle, &ncpaddr, NULL);
+
+ if (hisaddr && !ipcp_UseHisaddr(arg->bundle, hisaddr,
+ arg->bundle->phys_type.all & PHYS_AUTO))
+ return 4;
+
+ return 0;
+}
+
+static int
+SetRetry(int argc, char const *const *argv, u_int *timeout, u_int *maxreq,
+ u_int *maxtrm, int def)
+{
+ if (argc == 0) {
+ *timeout = DEF_FSMRETRY;
+ *maxreq = def;
+ if (maxtrm != NULL)
+ *maxtrm = def;
+ } else {
+ long l = atol(argv[0]);
+
+ if (l < MIN_FSMRETRY) {
+ log_Printf(LogWARN, "%ld: Invalid FSM retry period - min %d\n",
+ l, MIN_FSMRETRY);
+ return 1;
+ } else
+ *timeout = l;
+
+ if (argc > 1) {
+ l = atol(argv[1]);
+ if (l < 1) {
+ log_Printf(LogWARN, "%ld: Invalid FSM REQ tries - changed to 1\n", l);
+ l = 1;
+ }
+ *maxreq = l;
+
+ if (argc > 2 && maxtrm != NULL) {
+ l = atol(argv[2]);
+ if (l < 1) {
+ log_Printf(LogWARN, "%ld: Invalid FSM TRM tries - changed to 1\n", l);
+ l = 1;
+ }
+ *maxtrm = l;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int
+SetVariable(struct cmdargs const *arg)
+{
+ long long_val, param = (long)arg->cmd->args;
+ int mode, dummyint, f, first, res;
+ u_short *change;
+ const char *argp;
+ struct datalink *cx = arg->cx; /* LOCAL_CX uses this */
+ struct link *l = command_ChooseLink(arg); /* LOCAL_CX_OPT uses this */
+ struct in_addr *ipaddr;
+ struct ncpaddr ncpaddr[2];
+
+ if (arg->argc > arg->argn)
+ argp = arg->argv[arg->argn];
+ else
+ argp = "";
+
+ res = 0;
+
+ if ((arg->cmd->lauth & LOCAL_CX) && !cx) {
+ log_Printf(LogWARN, "set %s: No context (use the `link' command)\n",
+ arg->cmd->name);
+ return 1;
+ } else if (cx && !(arg->cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) {
+ log_Printf(LogWARN, "set %s: Redundant context (%s) ignored\n",
+ arg->cmd->name, cx->name);
+ cx = NULL;
+ }
+
+ switch (param) {
+ case VAR_AUTHKEY:
+ strncpy(arg->bundle->cfg.auth.key, argp,
+ sizeof arg->bundle->cfg.auth.key - 1);
+ arg->bundle->cfg.auth.key[sizeof arg->bundle->cfg.auth.key - 1] = '\0';
+ break;
+
+ case VAR_AUTHNAME:
+ switch (bundle_Phase(arg->bundle)) {
+ default:
+ log_Printf(LogWARN, "Altering authname while at phase %s\n",
+ bundle_PhaseName(arg->bundle));
+ /* drop through */
+ case PHASE_DEAD:
+ case PHASE_ESTABLISH:
+ strncpy(arg->bundle->cfg.auth.name, argp,
+ sizeof arg->bundle->cfg.auth.name - 1);
+ arg->bundle->cfg.auth.name[sizeof arg->bundle->cfg.auth.name-1] = '\0';
+ break;
+ }
+ break;
+
+ case VAR_AUTOLOAD:
+ if (arg->argc == arg->argn + 3) {
+ int v1, v2, v3;
+ char *end;
+
+ v1 = strtol(arg->argv[arg->argn], &end, 0);
+ if (v1 < 0 || *end) {
+ log_Printf(LogWARN, "autoload: %s: Invalid min percentage\n",
+ arg->argv[arg->argn]);
+ res = 1;
+ break;
+ }
+
+ v2 = strtol(arg->argv[arg->argn + 1], &end, 0);
+ if (v2 < 0 || *end) {
+ log_Printf(LogWARN, "autoload: %s: Invalid max percentage\n",
+ arg->argv[arg->argn + 1]);
+ res = 1;
+ break;
+ }
+ if (v2 < v1) {
+ v3 = v1;
+ v1 = v2;
+ v2 = v3;
+ }
+
+ v3 = strtol(arg->argv[arg->argn + 2], &end, 0);
+ if (v3 <= 0 || *end) {
+ log_Printf(LogWARN, "autoload: %s: Invalid throughput period\n",
+ arg->argv[arg->argn + 2]);
+ res = 1;
+ break;
+ }
+
+ arg->bundle->ncp.mp.cfg.autoload.min = v1;
+ arg->bundle->ncp.mp.cfg.autoload.max = v2;
+ arg->bundle->ncp.mp.cfg.autoload.period = v3;
+ mp_RestartAutoloadTimer(&arg->bundle->ncp.mp);
+ } else {
+ log_Printf(LogWARN, "Set autoload requires three arguments\n");
+ res = 1;
+ }
+ break;
+
+ case VAR_DIAL:
+ strncpy(cx->cfg.script.dial, argp, sizeof cx->cfg.script.dial - 1);
+ cx->cfg.script.dial[sizeof cx->cfg.script.dial - 1] = '\0';
+ break;
+
+ case VAR_LOGIN:
+ strncpy(cx->cfg.script.login, argp, sizeof cx->cfg.script.login - 1);
+ cx->cfg.script.login[sizeof cx->cfg.script.login - 1] = '\0';
+ break;
+
+ case VAR_WINSIZE:
+ if (arg->argc > arg->argn) {
+ l->ccp.cfg.deflate.out.winsize = atoi(arg->argv[arg->argn]);
+ if (l->ccp.cfg.deflate.out.winsize < 8 ||
+ l->ccp.cfg.deflate.out.winsize > 15) {
+ log_Printf(LogWARN, "%d: Invalid outgoing window size\n",
+ l->ccp.cfg.deflate.out.winsize);
+ l->ccp.cfg.deflate.out.winsize = 15;
+ }
+ if (arg->argc > arg->argn+1) {
+ l->ccp.cfg.deflate.in.winsize = atoi(arg->argv[arg->argn+1]);
+ if (l->ccp.cfg.deflate.in.winsize < 8 ||
+ l->ccp.cfg.deflate.in.winsize > 15) {
+ log_Printf(LogWARN, "%d: Invalid incoming window size\n",
+ l->ccp.cfg.deflate.in.winsize);
+ l->ccp.cfg.deflate.in.winsize = 15;
+ }
+ } else
+ l->ccp.cfg.deflate.in.winsize = 0;
+ } else {
+ log_Printf(LogWARN, "No window size specified\n");
+ res = 1;
+ }
+ break;
+
+#ifndef NODES
+ case VAR_MPPE:
+ if (arg->argc > arg->argn + 2) {
+ res = -1;
+ break;
+ }
+
+ if (arg->argc == arg->argn) {
+ l->ccp.cfg.mppe.keybits = 0;
+ l->ccp.cfg.mppe.state = MPPE_ANYSTATE;
+ l->ccp.cfg.mppe.required = 0;
+ break;
+ }
+
+ if (!strcmp(argp, "*"))
+ long_val = 0;
+ else {
+ long_val = atol(argp);
+ if (long_val != 40 && long_val != 56 && long_val != 128) {
+ log_Printf(LogWARN, "%s: Invalid bits value\n", argp);
+ res = -1;
+ break;
+ }
+ }
+
+ if (arg->argc == arg->argn + 2) {
+ if (!strcmp(arg->argv[arg->argn + 1], "*"))
+ l->ccp.cfg.mppe.state = MPPE_ANYSTATE;
+ else if (!strcasecmp(arg->argv[arg->argn + 1], "stateless"))
+ l->ccp.cfg.mppe.state = MPPE_STATELESS;
+ else if (!strcasecmp(arg->argv[arg->argn + 1], "stateful"))
+ l->ccp.cfg.mppe.state = MPPE_STATEFUL;
+ else {
+ log_Printf(LogWARN, "%s: Invalid state value\n",
+ arg->argv[arg->argn + 1]);
+ res = -1;
+ break;
+ }
+ } else
+ l->ccp.cfg.mppe.state = MPPE_ANYSTATE;
+ l->ccp.cfg.mppe.keybits = long_val;
+ l->ccp.cfg.mppe.required = 1;
+ break;
+#endif
+
+ case VAR_DEVICE:
+ physical_SetDeviceList(cx->physical, arg->argc - arg->argn,
+ arg->argv + arg->argn);
+ break;
+
+ case VAR_ACCMAP:
+ if (arg->argc > arg->argn) {
+ u_long ulong_val;
+ sscanf(argp, "%lx", &ulong_val);
+ cx->physical->link.lcp.cfg.accmap = (u_int32_t)ulong_val;
+ } else {
+ log_Printf(LogWARN, "No accmap specified\n");
+ res = 1;
+ }
+ break;
+
+ case VAR_MODE:
+ mode = Nam2mode(argp);
+ if (mode == PHYS_NONE || mode == PHYS_ALL) {
+ log_Printf(LogWARN, "%s: Invalid mode\n", argp);
+ res = -1;
+ break;
+ }
+ bundle_SetMode(arg->bundle, cx, mode);
+ break;
+
+ case VAR_MRRU:
+ switch (bundle_Phase(arg->bundle)) {
+ case PHASE_DEAD:
+ break;
+ case PHASE_ESTABLISH:
+ /* Make sure none of our links are DATALINK_LCP or greater */
+ if (bundle_HighestState(arg->bundle) >= DATALINK_LCP) {
+ log_Printf(LogWARN, "mrru: Only changable before LCP negotiations\n");
+ res = 1;
+ break;
+ }
+ break;
+ default:
+ log_Printf(LogWARN, "mrru: Only changable at phase DEAD/ESTABLISH\n");
+ res = 1;
+ break;
+ }
+ if (res != 0)
+ break;
+ long_val = atol(argp);
+ if (long_val && long_val < MIN_MRU) {
+ log_Printf(LogWARN, "MRRU %ld: too small - min %d\n", long_val, MIN_MRU);
+ res = 1;
+ break;
+ } else if (long_val > MAX_MRU) {
+ log_Printf(LogWARN, "MRRU %ld: too big - max %d\n", long_val, MAX_MRU);
+ res = 1;
+ break;
+ } else
+ arg->bundle->ncp.mp.cfg.mrru = long_val;
+ break;
+
+ case VAR_MRU:
+ long_val = 0; /* silence gcc */
+ change = NULL; /* silence gcc */
+ switch(arg->argc - arg->argn) {
+ case 1:
+ if (argp[strspn(argp, "0123456789")] != '\0') {
+ res = -1;
+ break;
+ }
+ /*FALLTHRU*/
+ case 0:
+ long_val = atol(argp);
+ change = &l->lcp.cfg.mru;
+ if (long_val > l->lcp.cfg.max_mru) {
+ log_Printf(LogWARN, "MRU %ld: too large - max set to %d\n", long_val,
+ l->lcp.cfg.max_mru);
+ res = 1;
+ break;
+ }
+ break;
+ case 2:
+ if (strcasecmp(argp, "max") && strcasecmp(argp, "maximum")) {
+ res = -1;
+ break;
+ }
+ long_val = atol(arg->argv[arg->argn + 1]);
+ change = &l->lcp.cfg.max_mru;
+ if (long_val > MAX_MRU) {
+ log_Printf(LogWARN, "MRU %ld: too large - maximum is %d\n", long_val,
+ MAX_MRU);
+ res = 1;
+ break;
+ }
+ break;
+ default:
+ res = -1;
+ break;
+ }
+ if (res != 0)
+ break;
+
+ if (long_val == 0)
+ *change = 0;
+ else if (long_val < MIN_MRU) {
+ log_Printf(LogWARN, "MRU %ld: too small - min %d\n", long_val, MIN_MRU);
+ res = 1;
+ break;
+ } else if (long_val > MAX_MRU) {
+ log_Printf(LogWARN, "MRU %ld: too big - max %d\n", long_val, MAX_MRU);
+ res = 1;
+ break;
+ } else
+ *change = long_val;
+ if (l->lcp.cfg.mru > *change)
+ l->lcp.cfg.mru = *change;
+ break;
+
+ case VAR_MTU:
+ long_val = 0; /* silence gcc */
+ change = NULL; /* silence gcc */
+ switch(arg->argc - arg->argn) {
+ case 1:
+ if (argp[strspn(argp, "0123456789")] != '\0') {
+ res = -1;
+ break;
+ }
+ /*FALLTHRU*/
+ case 0:
+ long_val = atol(argp);
+ change = &l->lcp.cfg.mtu;
+ if (long_val > l->lcp.cfg.max_mtu) {
+ log_Printf(LogWARN, "MTU %ld: too large - max set to %d\n", long_val,
+ l->lcp.cfg.max_mtu);
+ res = 1;
+ break;
+ }
+ break;
+ case 2:
+ if (strcasecmp(argp, "max") && strcasecmp(argp, "maximum")) {
+ res = -1;
+ break;
+ }
+ long_val = atol(arg->argv[arg->argn + 1]);
+ change = &l->lcp.cfg.max_mtu;
+ if (long_val > MAX_MTU) {
+ log_Printf(LogWARN, "MTU %ld: too large - maximum is %d\n", long_val,
+ MAX_MTU);
+ res = 1;
+ break;
+ }
+ break;
+ default:
+ res = -1;
+ break;
+ }
+
+ if (res != 0)
+ break;
+
+ if (long_val && long_val < MIN_MTU) {
+ log_Printf(LogWARN, "MTU %ld: too small - min %d\n", long_val, MIN_MTU);
+ res = 1;
+ break;
+ } else if (long_val > MAX_MTU) {
+ log_Printf(LogWARN, "MTU %ld: too big - max %d\n", long_val, MAX_MTU);
+ res = 1;
+ break;
+ } else
+ *change = long_val;
+ if (l->lcp.cfg.mtu > *change)
+ l->lcp.cfg.mtu = *change;
+ break;
+
+ case VAR_OPENMODE:
+ if (strcasecmp(argp, "active") == 0)
+ cx->physical->link.lcp.cfg.openmode = arg->argc > arg->argn+1 ?
+ atoi(arg->argv[arg->argn+1]) : 1;
+ else if (strcasecmp(argp, "passive") == 0)
+ cx->physical->link.lcp.cfg.openmode = OPEN_PASSIVE;
+ else {
+ log_Printf(LogWARN, "%s: Invalid openmode\n", argp);
+ res = 1;
+ }
+ break;
+
+ case VAR_PHONE:
+ strncpy(cx->cfg.phone.list, argp, sizeof cx->cfg.phone.list - 1);
+ cx->cfg.phone.list[sizeof cx->cfg.phone.list - 1] = '\0';
+ cx->phone.alt = cx->phone.next = NULL;
+ break;
+
+ case VAR_HANGUP:
+ strncpy(cx->cfg.script.hangup, argp, sizeof cx->cfg.script.hangup - 1);
+ cx->cfg.script.hangup[sizeof cx->cfg.script.hangup - 1] = '\0';
+ break;
+
+ case VAR_IFQUEUE:
+ long_val = atol(argp);
+ arg->bundle->cfg.ifqueue = long_val < 0 ? 0 : long_val;
+ break;
+
+ case VAR_LOGOUT:
+ strncpy(cx->cfg.script.logout, argp, sizeof cx->cfg.script.logout - 1);
+ cx->cfg.script.logout[sizeof cx->cfg.script.logout - 1] = '\0';
+ break;
+
+ case VAR_IDLETIMEOUT:
+ if (arg->argc > arg->argn+2) {
+ log_Printf(LogWARN, "Too many idle timeout values\n");
+ res = 1;
+ } else if (arg->argc == arg->argn) {
+ log_Printf(LogWARN, "Too few idle timeout values\n");
+ res = 1;
+ } else {
+ unsigned long timeout, min;
+
+ timeout = strtoul(argp, NULL, 10);
+ min = arg->bundle->cfg.idle.min_timeout;
+ if (arg->argc == arg->argn + 2)
+ min = strtoul(arg->argv[arg->argn + 1], NULL, 10);
+ bundle_SetIdleTimer(arg->bundle, timeout, min);
+ }
+ break;
+
+#ifndef NORADIUS
+ case VAR_RAD_ALIVE:
+ if (arg->argc > arg->argn + 2) {
+ log_Printf(LogWARN, "Too many RADIUS alive interval values\n");
+ res = 1;
+ } else if (arg->argc == arg->argn) {
+ log_Printf(LogWARN, "Too few RADIUS alive interval values\n");
+ res = 1;
+ } else {
+ arg->bundle->radius.alive.interval = atoi(argp);
+ if (arg->bundle->radius.alive.interval && !arg->bundle->radius.cfg.file) {
+ log_Printf(LogWARN, "rad_alive requires radius to be configured\n");
+ res = 1;
+ } else if (arg->bundle->ncp.ipcp.fsm.state == ST_OPENED) {
+ if (arg->bundle->radius.alive.interval)
+ radius_StartTimer(arg->bundle);
+ else
+ radius_StopTimer(&arg->bundle->radius);
+ }
+ }
+ break;
+#endif
+
+ case VAR_LQRPERIOD:
+ long_val = atol(argp);
+ if (long_val < MIN_LQRPERIOD) {
+ log_Printf(LogWARN, "%ld: Invalid lqr period - min %d\n",
+ long_val, MIN_LQRPERIOD);
+ res = 1;
+ } else
+ l->lcp.cfg.lqrperiod = long_val;
+ break;
+
+ case VAR_LCPRETRY:
+ res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
+ &cx->physical->link.lcp.cfg.fsm.timeout,
+ &cx->physical->link.lcp.cfg.fsm.maxreq,
+ &cx->physical->link.lcp.cfg.fsm.maxtrm, DEF_FSMTRIES);
+ break;
+
+ case VAR_CHAPRETRY:
+ res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
+ &cx->chap.auth.cfg.fsm.timeout,
+ &cx->chap.auth.cfg.fsm.maxreq, NULL, DEF_FSMAUTHTRIES);
+ break;
+
+ case VAR_PAPRETRY:
+ res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
+ &cx->pap.cfg.fsm.timeout, &cx->pap.cfg.fsm.maxreq,
+ NULL, DEF_FSMAUTHTRIES);
+ break;
+
+ case VAR_CCPRETRY:
+ res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
+ &l->ccp.cfg.fsm.timeout, &l->ccp.cfg.fsm.maxreq,
+ &l->ccp.cfg.fsm.maxtrm, DEF_FSMTRIES);
+ break;
+
+ case VAR_IPCPRETRY:
+ res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
+ &arg->bundle->ncp.ipcp.cfg.fsm.timeout,
+ &arg->bundle->ncp.ipcp.cfg.fsm.maxreq,
+ &arg->bundle->ncp.ipcp.cfg.fsm.maxtrm, DEF_FSMTRIES);
+ break;
+
+#ifndef NOINET6
+ case VAR_IPV6CPRETRY:
+ res = SetRetry(arg->argc - arg->argn, arg->argv + arg->argn,
+ &arg->bundle->ncp.ipv6cp.cfg.fsm.timeout,
+ &arg->bundle->ncp.ipv6cp.cfg.fsm.maxreq,
+ &arg->bundle->ncp.ipv6cp.cfg.fsm.maxtrm, DEF_FSMTRIES);
+ break;
+#endif
+
+ case VAR_NBNS:
+ case VAR_DNS:
+ if (param == VAR_DNS) {
+ ipaddr = arg->bundle->ncp.ipcp.cfg.ns.dns;
+ ipaddr[0].s_addr = ipaddr[1].s_addr = INADDR_NONE;
+ } else {
+ ipaddr = arg->bundle->ncp.ipcp.cfg.ns.nbns;
+ ipaddr[0].s_addr = ipaddr[1].s_addr = INADDR_ANY;
+ }
+
+ if (arg->argc > arg->argn) {
+ ncpaddr_aton(ncpaddr, &arg->bundle->ncp, arg->argv[arg->argn]);
+ if (!ncpaddr_getip4(ncpaddr, ipaddr))
+ return -1;
+ if (arg->argc > arg->argn+1) {
+ ncpaddr_aton(ncpaddr + 1, &arg->bundle->ncp, arg->argv[arg->argn + 1]);
+ if (!ncpaddr_getip4(ncpaddr + 1, ipaddr + 1))
+ return -1;
+ }
+
+ if (ipaddr[0].s_addr == INADDR_ANY) {
+ ipaddr[0] = ipaddr[1];
+ ipaddr[1].s_addr = INADDR_ANY;
+ }
+ if (ipaddr[0].s_addr == INADDR_NONE) {
+ ipaddr[0] = ipaddr[1];
+ ipaddr[1].s_addr = INADDR_NONE;
+ }
+ }
+ break;
+
+ case VAR_CALLBACK:
+ cx->cfg.callback.opmask = 0;
+ for (dummyint = arg->argn; dummyint < arg->argc; dummyint++) {
+ if (!strcasecmp(arg->argv[dummyint], "auth"))
+ cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_AUTH);
+ else if (!strcasecmp(arg->argv[dummyint], "cbcp"))
+ cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_CBCP);
+ else if (!strcasecmp(arg->argv[dummyint], "e.164")) {
+ if (dummyint == arg->argc - 1)
+ log_Printf(LogWARN, "No E.164 arg (E.164 ignored) !\n");
+ else {
+ cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_E164);
+ strncpy(cx->cfg.callback.msg, arg->argv[++dummyint],
+ sizeof cx->cfg.callback.msg - 1);
+ cx->cfg.callback.msg[sizeof cx->cfg.callback.msg - 1] = '\0';
+ }
+ } else if (!strcasecmp(arg->argv[dummyint], "none"))
+ cx->cfg.callback.opmask |= CALLBACK_BIT(CALLBACK_NONE);
+ else {
+ res = -1;
+ break;
+ }
+ }
+ if (cx->cfg.callback.opmask == CALLBACK_BIT(CALLBACK_NONE))
+ cx->cfg.callback.opmask = 0;
+ break;
+
+ case VAR_CBCP:
+ cx->cfg.cbcp.delay = 0;
+ *cx->cfg.cbcp.phone = '\0';
+ cx->cfg.cbcp.fsmretry = DEF_FSMRETRY;
+ if (arg->argc > arg->argn) {
+ strncpy(cx->cfg.cbcp.phone, arg->argv[arg->argn],
+ sizeof cx->cfg.cbcp.phone - 1);
+ cx->cfg.cbcp.phone[sizeof cx->cfg.cbcp.phone - 1] = '\0';
+ if (arg->argc > arg->argn + 1) {
+ cx->cfg.cbcp.delay = atoi(arg->argv[arg->argn + 1]);
+ if (arg->argc > arg->argn + 2) {
+ long_val = atol(arg->argv[arg->argn + 2]);
+ if (long_val < MIN_FSMRETRY)
+ log_Printf(LogWARN, "%ld: Invalid CBCP FSM retry period - min %d\n",
+ long_val, MIN_FSMRETRY);
+ else
+ cx->cfg.cbcp.fsmretry = long_val;
+ }
+ }
+ }
+ break;
+
+ case VAR_CHOKED:
+ arg->bundle->cfg.choked.timeout = atoi(argp);
+ if (arg->bundle->cfg.choked.timeout <= 0)
+ arg->bundle->cfg.choked.timeout = CHOKED_TIMEOUT;
+ break;
+
+ case VAR_SENDPIPE:
+ long_val = atol(argp);
+ arg->bundle->ncp.cfg.sendpipe = long_val;
+ break;
+
+ case VAR_RECVPIPE:
+ long_val = atol(argp);
+ arg->bundle->ncp.cfg.recvpipe = long_val;
+ break;
+
+#ifndef NORADIUS
+ case VAR_RADIUS:
+ if (!*argp)
+ *arg->bundle->radius.cfg.file = '\0';
+ else if (access(argp, R_OK)) {
+ log_Printf(LogWARN, "%s: %s\n", argp, strerror(errno));
+ res = 1;
+ break;
+ } else {
+ strncpy(arg->bundle->radius.cfg.file, argp,
+ sizeof arg->bundle->radius.cfg.file - 1);
+ arg->bundle->radius.cfg.file
+ [sizeof arg->bundle->radius.cfg.file - 1] = '\0';
+ }
+ break;
+#endif
+
+ case VAR_CD:
+ if (*argp) {
+ if (strcasecmp(argp, "off")) {
+ long_val = atol(argp);
+ if (long_val < 0)
+ long_val = 0;
+ cx->physical->cfg.cd.delay = long_val;
+ cx->physical->cfg.cd.necessity = argp[strlen(argp)-1] == '!' ?
+ CD_REQUIRED : CD_VARIABLE;
+ } else
+ cx->physical->cfg.cd.necessity = CD_NOTREQUIRED;
+ } else {
+ cx->physical->cfg.cd.delay = 0;
+ cx->physical->cfg.cd.necessity = CD_DEFAULT;
+ }
+ break;
+
+ case VAR_PARITY:
+ if (arg->argc == arg->argn + 1)
+ res = physical_SetParity(arg->cx->physical, argp);
+ else {
+ log_Printf(LogWARN, "Parity value must be odd, even or none\n");
+ res = 1;
+ }
+ break;
+
+ case VAR_CRTSCTS:
+ if (strcasecmp(argp, "on") == 0)
+ physical_SetRtsCts(arg->cx->physical, 1);
+ else if (strcasecmp(argp, "off") == 0)
+ physical_SetRtsCts(arg->cx->physical, 0);
+ else {
+ log_Printf(LogWARN, "RTS/CTS value must be on or off\n");
+ res = 1;
+ }
+ break;
+
+ case VAR_URGENTPORTS:
+ if (arg->argn == arg->argc) {
+ ncp_SetUrgentTOS(&arg->bundle->ncp);
+ ncp_ClearUrgentTcpPorts(&arg->bundle->ncp);
+ ncp_ClearUrgentUdpPorts(&arg->bundle->ncp);
+ } else if (!strcasecmp(arg->argv[arg->argn], "udp")) {
+ ncp_SetUrgentTOS(&arg->bundle->ncp);
+ if (arg->argn == arg->argc - 1)
+ ncp_ClearUrgentUdpPorts(&arg->bundle->ncp);
+ else for (f = arg->argn + 1; f < arg->argc; f++)
+ if (*arg->argv[f] == '+')
+ ncp_AddUrgentUdpPort(&arg->bundle->ncp, atoi(arg->argv[f] + 1));
+ else if (*arg->argv[f] == '-')
+ ncp_RemoveUrgentUdpPort(&arg->bundle->ncp, atoi(arg->argv[f] + 1));
+ else {
+ if (f == arg->argn)
+ ncp_ClearUrgentUdpPorts(&arg->bundle->ncp);
+ ncp_AddUrgentUdpPort(&arg->bundle->ncp, atoi(arg->argv[f]));
+ }
+ } else if (arg->argn == arg->argc - 1 &&
+ !strcasecmp(arg->argv[arg->argn], "none")) {
+ ncp_ClearUrgentTcpPorts(&arg->bundle->ncp);
+ ncp_ClearUrgentUdpPorts(&arg->bundle->ncp);
+ ncp_ClearUrgentTOS(&arg->bundle->ncp);
+ } else {
+ ncp_SetUrgentTOS(&arg->bundle->ncp);
+ first = arg->argn;
+ if (!strcasecmp(arg->argv[first], "tcp") && ++first == arg->argc)
+ ncp_ClearUrgentTcpPorts(&arg->bundle->ncp);
+
+ for (f = first; f < arg->argc; f++)
+ if (*arg->argv[f] == '+')
+ ncp_AddUrgentTcpPort(&arg->bundle->ncp, atoi(arg->argv[f] + 1));
+ else if (*arg->argv[f] == '-')
+ ncp_RemoveUrgentTcpPort(&arg->bundle->ncp, atoi(arg->argv[f] + 1));
+ else {
+ if (f == first)
+ ncp_ClearUrgentTcpPorts(&arg->bundle->ncp);
+ ncp_AddUrgentTcpPort(&arg->bundle->ncp, atoi(arg->argv[f]));
+ }
+ }
+ break;
+
+ case VAR_PPPOE:
+ if (strcasecmp(argp, "3Com") == 0)
+ physical_SetPPPoEnonstandard(arg->cx->physical, 1);
+ else if (strcasecmp(argp, "standard") == 0)
+ physical_SetPPPoEnonstandard(arg->cx->physical, 0);
+ else {
+ log_Printf(LogWARN, "PPPoE standard value must be \"standard\" or \"3Com\"\n");
+ res = 1;
+ }
+ break;
+
+#ifndef NORADIUS
+ case VAR_PORT_ID:
+ if (strcasecmp(argp, "default") == 0)
+ arg->bundle->radius.port_id_type = RPI_DEFAULT;
+ else if (strcasecmp(argp, "pid") == 0)
+ arg->bundle->radius.port_id_type = RPI_PID;
+ else if (strcasecmp(argp, "ifnum") == 0)
+ arg->bundle->radius.port_id_type = RPI_IFNUM;
+ else if (strcasecmp(argp, "tunnum") == 0)
+ arg->bundle->radius.port_id_type = RPI_TUNNUM;
+ else {
+ log_Printf(LogWARN,
+ "RADIUS port id must be one of \"default\", \"pid\", \"ifnum\" or \"tunnum\"\n");
+ res = 1;
+ }
+
+ if (arg->bundle->radius.port_id_type && !arg->bundle->radius.cfg.file) {
+ log_Printf(LogWARN, "rad_port_id requires radius to be configured\n");
+ res = 1;
+ }
+
+ break;
+#endif
+ }
+
+ return res;
+}
+
+static struct cmdtab const SetCommands[] = {
+ {"accmap", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
+ "accmap value", "set accmap hex-value", (const void *)VAR_ACCMAP},
+ {"authkey", "key", SetVariable, LOCAL_AUTH,
+ "authentication key", "set authkey|key key", (const void *)VAR_AUTHKEY},
+ {"authname", NULL, SetVariable, LOCAL_AUTH,
+ "authentication name", "set authname name", (const void *)VAR_AUTHNAME},
+ {"autoload", NULL, SetVariable, LOCAL_AUTH,
+ "auto link [de]activation", "set autoload maxtime maxload mintime minload",
+ (const void *)VAR_AUTOLOAD},
+ {"bandwidth", NULL, mp_SetDatalinkBandwidth, LOCAL_AUTH | LOCAL_CX,
+ "datalink bandwidth", "set bandwidth value", NULL},
+ {"callback", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
+ "callback control", "set callback [none|auth|cbcp|"
+ "E.164 *|number[,number]...]...", (const void *)VAR_CALLBACK},
+ {"cbcp", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
+ "CBCP control", "set cbcp [*|phone[,phone...] [delay [timeout]]]",
+ (const void *)VAR_CBCP},
+ {"ccpretry", "ccpretries", SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
+ "CCP retries", "set ccpretry value [attempts]", (const void *)VAR_CCPRETRY},
+ {"cd", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "Carrier delay requirement",
+ "set cd value[!]", (const void *)VAR_CD},
+ {"chapretry", "chapretries", SetVariable, LOCAL_AUTH | LOCAL_CX,
+ "CHAP retries", "set chapretry value [attempts]",
+ (const void *)VAR_CHAPRETRY},
+ {"choked", NULL, SetVariable, LOCAL_AUTH,
+ "choked timeout", "set choked [secs]", (const void *)VAR_CHOKED},
+ {"ctsrts", "crtscts", SetVariable, LOCAL_AUTH | LOCAL_CX,
+ "Use hardware flow control", "set ctsrts [on|off]",
+ (const char *)VAR_CRTSCTS},
+ {"deflate", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
+ "deflate window sizes", "set deflate out-winsize in-winsize",
+ (const void *) VAR_WINSIZE},
+#ifndef NODES
+ {"mppe", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
+ "MPPE key size and state", "set mppe [40|56|128|* [stateful|stateless|*]]",
+ (const void *) VAR_MPPE},
+#endif
+ {"device", "line", SetVariable, LOCAL_AUTH | LOCAL_CX,
+ "physical device name", "set device|line device-name[,device-name]",
+ (const void *) VAR_DEVICE},
+ {"dial", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
+ "dialing script", "set dial chat-script", (const void *) VAR_DIAL},
+ {"dns", NULL, SetVariable, LOCAL_AUTH, "Domain Name Server",
+ "set dns pri-addr [sec-addr]", (const void *)VAR_DNS},
+ {"enddisc", NULL, mp_SetEnddisc, LOCAL_AUTH,
+ "Endpoint Discriminator", "set enddisc [IP|magic|label|psn value]", NULL},
+ {"escape", NULL, SetEscape, LOCAL_AUTH | LOCAL_CX,
+ "escape characters", "set escape hex-digit ...", NULL},
+ {"filter", NULL, filter_Set, LOCAL_AUTH,
+ "packet filters", "set filter alive|dial|in|out rule-no permit|deny "
+ "[src_addr[/width]] [dst_addr[/width]] [proto "
+ "[src [lt|eq|gt port]] [dst [lt|eq|gt port]] [estab] [syn] [finrst]]", NULL},
+ {"hangup", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
+ "hangup script", "set hangup chat-script", (const void *) VAR_HANGUP},
+ {"ifaddr", NULL, SetInterfaceAddr, LOCAL_AUTH, "destination address",
+ "set ifaddr [src-addr [dst-addr [netmask [trg-addr]]]]", NULL},
+ {"ifqueue", NULL, SetVariable, LOCAL_AUTH, "interface queue",
+ "set ifqueue packets", (const void *)VAR_IFQUEUE},
+ {"ipcpretry", "ipcpretries", SetVariable, LOCAL_AUTH, "IPCP retries",
+ "set ipcpretry value [attempts]", (const void *)VAR_IPCPRETRY},
+ {"ipv6cpretry", "ipv6cpretries", SetVariable, LOCAL_AUTH, "IPV6CP retries",
+ "set ipv6cpretry value [attempts]", (const void *)VAR_IPV6CPRETRY},
+ {"lcpretry", "lcpretries", SetVariable, LOCAL_AUTH | LOCAL_CX, "LCP retries",
+ "set lcpretry value [attempts]", (const void *)VAR_LCPRETRY},
+ {"log", NULL, log_SetLevel, LOCAL_AUTH, "log level",
+ "set log [local] [+|-]all|async|cbcp|ccp|chat|command|connect|debug|dns|hdlc|"
+ "id0|ipcp|lcp|lqm|phase|physical|radius|sync|tcp/ip|timer|tun...", NULL},
+ {"login", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
+ "login script", "set login chat-script", (const void *) VAR_LOGIN},
+ {"logout", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
+ "logout script", "set logout chat-script", (const void *) VAR_LOGOUT},
+ {"lqrperiod", "echoperiod", SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
+ "LQR period", "set lqr/echo period value", (const void *)VAR_LQRPERIOD},
+ {"mode", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "mode value",
+ "set mode interactive|auto|ddial|background", (const void *)VAR_MODE},
+ {"mrru", NULL, SetVariable, LOCAL_AUTH, "MRRU value",
+ "set mrru value", (const void *)VAR_MRRU},
+ {"mru", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
+ "MRU value", "set mru [max[imum]] [value]", (const void *)VAR_MRU},
+ {"mtu", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
+ "interface MTU value", "set mtu [max[imum]] [value]", (const void *)VAR_MTU},
+ {"nbns", NULL, SetVariable, LOCAL_AUTH, "NetBIOS Name Server",
+ "set nbns pri-addr [sec-addr]", (const void *)VAR_NBNS},
+ {"openmode", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "open mode",
+ "set openmode active|passive [secs]", (const void *)VAR_OPENMODE},
+ {"papretry", "papretries", SetVariable, LOCAL_AUTH | LOCAL_CX, "PAP retries",
+ "set papretry value [attempts]", (const void *)VAR_PAPRETRY},
+ {"parity", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "serial parity",
+ "set parity [odd|even|none]", (const void *)VAR_PARITY},
+ {"phone", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX, "telephone number(s)",
+ "set phone phone1[:phone2[...]]", (const void *)VAR_PHONE},
+ {"proctitle", "title", SetProcTitle, LOCAL_AUTH,
+ "Process title", "set proctitle [value]", NULL},
+#ifndef NORADIUS
+ {"radius", NULL, SetVariable, LOCAL_AUTH,
+ "RADIUS Config", "set radius cfgfile", (const void *)VAR_RADIUS},
+ {"rad_alive", NULL, SetVariable, LOCAL_AUTH,
+ "Raduis alive interval", "set rad_alive value",
+ (const void *)VAR_RAD_ALIVE},
+ {"rad_port_id", NULL, SetVariable, LOCAL_AUTH,
+ "NAS-Port-Id", "set rad_port_id [default|pid|ifnum|tunnum]", (const void *)VAR_PORT_ID},
+#endif
+ {"reconnect", NULL, datalink_SetReconnect, LOCAL_AUTH | LOCAL_CX,
+ "Reconnect timeout", "set reconnect value ntries", NULL},
+ {"recvpipe", NULL, SetVariable, LOCAL_AUTH,
+ "RECVPIPE value", "set recvpipe value", (const void *)VAR_RECVPIPE},
+ {"redial", NULL, datalink_SetRedial, LOCAL_AUTH | LOCAL_CX,
+ "Redial timeout", "set redial secs[+inc[-incmax]][.next] [attempts]", NULL},
+ {"sendpipe", NULL, SetVariable, LOCAL_AUTH,
+ "SENDPIPE value", "set sendpipe value", (const void *)VAR_SENDPIPE},
+ {"server", "socket", SetServer, LOCAL_AUTH, "diagnostic port",
+ "set server|socket TcpPort|LocalName|none|open|closed [password [mask]]",
+ NULL},
+ {"speed", NULL, SetModemSpeed, LOCAL_AUTH | LOCAL_CX,
+ "physical speed", "set speed value|sync", NULL},
+ {"stopped", NULL, SetStoppedTimeout, LOCAL_AUTH | LOCAL_CX,
+ "STOPPED timeouts", "set stopped [LCPseconds [CCPseconds]]", NULL},
+ {"timeout", NULL, SetVariable, LOCAL_AUTH, "Idle timeout",
+ "set timeout idletime", (const void *)VAR_IDLETIMEOUT},
+ {"urgent", NULL, SetVariable, LOCAL_AUTH, "urgent ports",
+ "set urgent [tcp|udp] [+|-]port...", (const void *)VAR_URGENTPORTS},
+ {"vj", NULL, ipcp_vjset, LOCAL_AUTH,
+ "vj values", "set vj slots|slotcomp [value]", NULL},
+ {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
+ "Display this message", "set help|? [command]", SetCommands},
+ {"pppoe", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
+ "Connect using standard/3Com mode", "set pppoe [standard|3Com]",
+ (const char *)VAR_PPPOE},
+ {NULL, NULL, NULL, 0, NULL, NULL, NULL},
+};
+
+static int
+SetCommand(struct cmdargs const *arg)
+{
+ if (arg->argc > arg->argn)
+ FindExec(arg->bundle, SetCommands, arg->argc, arg->argn, arg->argv,
+ arg->prompt, arg->cx);
+ else if (arg->prompt)
+ prompt_Printf(arg->prompt, "Use `set ?' to get a list or `set ? <var>' for"
+ " syntax help.\n");
+ else
+ log_Printf(LogWARN, "set command must have arguments\n");
+
+ return 0;
+}
+
+static int
+AddCommand(struct cmdargs const *arg)
+{
+ struct ncpaddr gw;
+ struct ncprange dest;
+ struct in_addr host;
+#ifndef NOINET6
+ struct in6_addr host6;
+#endif
+ int dest_default, gw_arg, addrs;
+
+ if (arg->argc != arg->argn+3 && arg->argc != arg->argn+2)
+ return -1;
+
+ addrs = 0;
+ dest_default = 0;
+ if (arg->argc == arg->argn + 2) {
+ if (!strcasecmp(arg->argv[arg->argn], "default"))
+ dest_default = 1;
+ else {
+ if (!ncprange_aton(&dest, &arg->bundle->ncp, arg->argv[arg->argn]))
+ return -1;
+ if (!strncasecmp(arg->argv[arg->argn], "MYADDR", 6))
+ addrs = ROUTE_DSTMYADDR;
+ else if (!strncasecmp(arg->argv[arg->argn], "MYADDR6", 7))
+ addrs = ROUTE_DSTMYADDR6;
+ else if (!strncasecmp(arg->argv[arg->argn], "HISADDR", 7))
+ addrs = ROUTE_DSTHISADDR;
+ else if (!strncasecmp(arg->argv[arg->argn], "HISADDR6", 8))
+ addrs = ROUTE_DSTHISADDR6;
+ else if (!strncasecmp(arg->argv[arg->argn], "DNS0", 4))
+ addrs = ROUTE_DSTDNS0;
+ else if (!strncasecmp(arg->argv[arg->argn], "DNS1", 4))
+ addrs = ROUTE_DSTDNS1;
+ }
+ gw_arg = 1;
+ } else {
+ if (strcasecmp(arg->argv[arg->argn], "MYADDR") == 0) {
+ addrs = ROUTE_DSTMYADDR;
+ host = arg->bundle->ncp.ipcp.my_ip;
+ } else if (strcasecmp(arg->argv[arg->argn], "HISADDR") == 0) {
+ addrs = ROUTE_DSTHISADDR;
+ host = arg->bundle->ncp.ipcp.peer_ip;
+ } else if (strcasecmp(arg->argv[arg->argn], "DNS0") == 0) {
+ addrs = ROUTE_DSTDNS0;
+ host = arg->bundle->ncp.ipcp.ns.dns[0];
+ } else if (strcasecmp(arg->argv[arg->argn], "DNS1") == 0) {
+ addrs = ROUTE_DSTDNS1;
+ host = arg->bundle->ncp.ipcp.ns.dns[1];
+ } else {
+ host = GetIpAddr(arg->argv[arg->argn]);
+ if (host.s_addr == INADDR_NONE) {
+ log_Printf(LogWARN, "%s: Invalid destination address\n",
+ arg->argv[arg->argn]);
+ return -1;
+ }
+ }
+ ncprange_setip4(&dest, host, GetIpAddr(arg->argv[arg->argn + 1]));
+ gw_arg = 2;
+ }
+
+ if (strcasecmp(arg->argv[arg->argn + gw_arg], "HISADDR") == 0) {
+ ncpaddr_setip4(&gw, arg->bundle->ncp.ipcp.peer_ip);
+ addrs |= ROUTE_GWHISADDR;
+#ifndef NOINET6
+ } else if (strcasecmp(arg->argv[arg->argn + gw_arg], "HISADDR6") == 0) {
+ if (!ncpaddr_getip6(&arg->bundle->ncp.ipv6cp.hisaddr, &host6))
+ memset(&host6, '\0', sizeof host6);
+ ncpaddr_setip6(&gw, &host6);
+ addrs |= ROUTE_GWHISADDR6;
+#endif
+ } else {
+ if (!ncpaddr_aton(&gw, &arg->bundle->ncp, arg->argv[arg->argn + gw_arg])) {
+ log_Printf(LogWARN, "%s: Invalid gateway address\n",
+ arg->argv[arg->argn + gw_arg]);
+ return -1;
+ }
+ }
+
+ if (dest_default)
+ ncprange_setdefault(&dest, ncpaddr_family(&gw));
+
+ if (rt_Set(arg->bundle, RTM_ADD, &dest, &gw, arg->cmd->args ? 1 : 0,
+ ((addrs & ROUTE_GWHISADDR) || (addrs & ROUTE_GWHISADDR6)) ? 1 : 0)
+ && addrs != ROUTE_STATIC)
+ route_Add(&arg->bundle->ncp.route, addrs, &dest, &gw);
+
+ return 0;
+}
+
+static int
+DeleteCommand(struct cmdargs const *arg)
+{
+ struct ncprange dest;
+ int addrs;
+
+ if (arg->argc == arg->argn+1) {
+ if(strcasecmp(arg->argv[arg->argn], "all") == 0) {
+ route_IfDelete(arg->bundle, 0);
+ route_DeleteAll(&arg->bundle->ncp.route);
+ } else {
+ addrs = 0;
+ if (strcasecmp(arg->argv[arg->argn], "MYADDR") == 0) {
+ ncprange_setip4host(&dest, arg->bundle->ncp.ipcp.my_ip);
+ addrs = ROUTE_DSTMYADDR;
+#ifndef NOINET6
+ } else if (strcasecmp(arg->argv[arg->argn], "MYADDR6") == 0) {
+ ncprange_sethost(&dest, &arg->bundle->ncp.ipv6cp.myaddr);
+ addrs = ROUTE_DSTMYADDR6;
+#endif
+ } else if (strcasecmp(arg->argv[arg->argn], "HISADDR") == 0) {
+ ncprange_setip4host(&dest, arg->bundle->ncp.ipcp.peer_ip);
+ addrs = ROUTE_DSTHISADDR;
+#ifndef NOINET6
+ } else if (strcasecmp(arg->argv[arg->argn], "HISADDR6") == 0) {
+ ncprange_sethost(&dest, &arg->bundle->ncp.ipv6cp.hisaddr);
+ addrs = ROUTE_DSTHISADDR6;
+#endif
+ } else if (strcasecmp(arg->argv[arg->argn], "DNS0") == 0) {
+ ncprange_setip4host(&dest, arg->bundle->ncp.ipcp.ns.dns[0]);
+ addrs = ROUTE_DSTDNS0;
+ } else if (strcasecmp(arg->argv[arg->argn], "DNS1") == 0) {
+ ncprange_setip4host(&dest, arg->bundle->ncp.ipcp.ns.dns[1]);
+ addrs = ROUTE_DSTDNS1;
+ } else {
+ ncprange_aton(&dest, &arg->bundle->ncp, arg->argv[arg->argn]);
+ addrs = ROUTE_STATIC;
+ }
+ rt_Set(arg->bundle, RTM_DELETE, &dest, NULL, arg->cmd->args ? 1 : 0, 0);
+ route_Delete(&arg->bundle->ncp.route, addrs, &dest);
+ }
+ } else
+ return -1;
+
+ return 0;
+}
+
+#ifndef NONAT
+static int
+NatEnable(struct cmdargs const *arg)
+{
+ if (arg->argc == arg->argn+1) {
+ if (strcasecmp(arg->argv[arg->argn], "yes") == 0) {
+ if (!arg->bundle->NatEnabled) {
+ if (arg->bundle->ncp.ipcp.fsm.state == ST_OPENED)
+ LibAliasSetAddress(la, arg->bundle->ncp.ipcp.my_ip);
+ arg->bundle->NatEnabled = 1;
+ }
+ return 0;
+ } else if (strcasecmp(arg->argv[arg->argn], "no") == 0) {
+ arg->bundle->NatEnabled = 0;
+ opt_disable(arg->bundle, OPT_IFACEALIAS);
+ /* Don't iface_Clear() - there may be manually configured addresses */
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+
+static int
+NatOption(struct cmdargs const *arg)
+{
+ long param = (long)arg->cmd->args;
+
+ if (arg->argc == arg->argn+1) {
+ if (strcasecmp(arg->argv[arg->argn], "yes") == 0) {
+ if (arg->bundle->NatEnabled) {
+ LibAliasSetMode(la, param, param);
+ return 0;
+ }
+ log_Printf(LogWARN, "nat not enabled\n");
+ } else if (strcmp(arg->argv[arg->argn], "no") == 0) {
+ if (arg->bundle->NatEnabled) {
+ LibAliasSetMode(la, 0, param);
+ return 0;
+ }
+ log_Printf(LogWARN, "nat not enabled\n");
+ }
+ }
+ return -1;
+}
+#endif /* #ifndef NONAT */
+
+static int
+LinkCommand(struct cmdargs const *arg)
+{
+ if (arg->argc > arg->argn+1) {
+ char namelist[LINE_LEN];
+ struct datalink *cx;
+ char *name;
+ int result = 0;
+
+ if (!strcmp(arg->argv[arg->argn], "*")) {
+ struct datalink *dl;
+
+ cx = arg->bundle->links;
+ while (cx) {
+ /* Watch it, the command could be a ``remove'' */
+ dl = cx->next;
+ FindExec(arg->bundle, Commands, arg->argc, arg->argn+1, arg->argv,
+ arg->prompt, cx);
+ for (cx = arg->bundle->links; cx; cx = cx->next)
+ if (cx == dl)
+ break; /* Pointer's still valid ! */
+ }
+ } else {
+ strncpy(namelist, arg->argv[arg->argn], sizeof namelist - 1);
+ namelist[sizeof namelist - 1] = '\0';
+ for(name = strtok(namelist, ", "); name; name = strtok(NULL,", "))
+ if (!bundle2datalink(arg->bundle, name)) {
+ log_Printf(LogWARN, "link: %s: Invalid link name\n", name);
+ return 1;
+ }
+
+ strncpy(namelist, arg->argv[arg->argn], sizeof namelist - 1);
+ namelist[sizeof namelist - 1] = '\0';
+ for(name = strtok(namelist, ", "); name; name = strtok(NULL,", ")) {
+ cx = bundle2datalink(arg->bundle, name);
+ if (cx)
+ FindExec(arg->bundle, Commands, arg->argc, arg->argn+1, arg->argv,
+ arg->prompt, cx);
+ else {
+ log_Printf(LogWARN, "link: %s: Invalidated link name !\n", name);
+ result++;
+ }
+ }
+ }
+ return result;
+ }
+
+ log_Printf(LogWARN, "usage: %s\n", arg->cmd->syntax);
+ return 2;
+}
+
+struct link *
+command_ChooseLink(struct cmdargs const *arg)
+{
+ if (arg->cx)
+ return &arg->cx->physical->link;
+ else if (!arg->bundle->ncp.mp.cfg.mrru) {
+ struct datalink *dl = bundle2datalink(arg->bundle, NULL);
+ if (dl)
+ return &dl->physical->link;
+ }
+ return &arg->bundle->ncp.mp.link;
+}
+
+static const char *
+ident_cmd(const char *cmd, unsigned *keep, unsigned *add)
+{
+ const char *result;
+
+ switch (*cmd) {
+ case 'A':
+ case 'a':
+ result = "accept";
+ *keep = NEG_MYMASK;
+ *add = NEG_ACCEPTED;
+ break;
+ case 'D':
+ case 'd':
+ switch (cmd[1]) {
+ case 'E':
+ case 'e':
+ result = "deny";
+ *keep = NEG_MYMASK;
+ *add = 0;
+ break;
+ case 'I':
+ case 'i':
+ result = "disable";
+ *keep = NEG_HISMASK;
+ *add = 0;
+ break;
+ default:
+ return NULL;
+ }
+ break;
+ case 'E':
+ case 'e':
+ result = "enable";
+ *keep = NEG_HISMASK;
+ *add = NEG_ENABLED;
+ break;
+ default:
+ return NULL;
+ }
+
+ return result;
+}
+
+static int
+OptSet(struct cmdargs const *arg)
+{
+ int opt = (int)(long)arg->cmd->args;
+ unsigned keep; /* Keep this opt */
+ unsigned add; /* Add this opt */
+
+ if (ident_cmd(arg->argv[arg->argn - 2], &keep, &add) == NULL)
+ return 1;
+
+#ifndef NOINET6
+ if (add == NEG_ENABLED && opt == OPT_IPV6CP && !probe.ipv6_available) {
+ log_Printf(LogWARN, "IPv6 is not available on this machine\n");
+ return 1;
+ }
+#endif
+ if (!add && ((opt == OPT_NAS_IP_ADDRESS &&
+ !Enabled(arg->bundle, OPT_NAS_IDENTIFIER)) ||
+ (opt == OPT_NAS_IDENTIFIER &&
+ !Enabled(arg->bundle, OPT_NAS_IP_ADDRESS)))) {
+ log_Printf(LogWARN,
+ "Cannot disable both NAS-IP-Address and NAS-Identifier\n");
+ return 1;
+ }
+
+ if (add)
+ opt_enable(arg->bundle, opt);
+ else
+ opt_disable(arg->bundle, opt);
+
+ return 0;
+}
+
+static int
+IfaceAliasOptSet(struct cmdargs const *arg)
+{
+ unsigned long long save = arg->bundle->cfg.optmask;
+ int result = OptSet(arg);
+
+ if (result == 0)
+ if (Enabled(arg->bundle, OPT_IFACEALIAS) && !arg->bundle->NatEnabled) {
+ arg->bundle->cfg.optmask = save;
+ log_Printf(LogWARN, "Cannot enable iface-alias without NAT\n");
+ result = 2;
+ }
+
+ return result;
+}
+
+static int
+NegotiateSet(struct cmdargs const *arg)
+{
+ long param = (long)arg->cmd->args;
+ struct link *l = command_ChooseLink(arg); /* LOCAL_CX_OPT uses this */
+ struct datalink *cx = arg->cx; /* LOCAL_CX uses this */
+ const char *cmd;
+ unsigned keep; /* Keep these bits */
+ unsigned add; /* Add these bits */
+
+ if ((cmd = ident_cmd(arg->argv[arg->argn-2], &keep, &add)) == NULL)
+ return 1;
+
+ if ((arg->cmd->lauth & LOCAL_CX) && !cx) {
+ log_Printf(LogWARN, "%s %s: No context (use the `link' command)\n",
+ cmd, arg->cmd->name);
+ return 2;
+ } else if (cx && !(arg->cmd->lauth & (LOCAL_CX|LOCAL_CX_OPT))) {
+ log_Printf(LogWARN, "%s %s: Redundant context (%s) ignored\n",
+ cmd, arg->cmd->name, cx->name);
+ cx = NULL;
+ }
+
+ switch (param) {
+ case NEG_ACFCOMP:
+ cx->physical->link.lcp.cfg.acfcomp &= keep;
+ cx->physical->link.lcp.cfg.acfcomp |= add;
+ break;
+ case NEG_CHAP05:
+ cx->physical->link.lcp.cfg.chap05 &= keep;
+ cx->physical->link.lcp.cfg.chap05 |= add;
+ break;
+#ifndef NODES
+ case NEG_CHAP80:
+ cx->physical->link.lcp.cfg.chap80nt &= keep;
+ cx->physical->link.lcp.cfg.chap80nt |= add;
+ break;
+ case NEG_CHAP80LM:
+ cx->physical->link.lcp.cfg.chap80lm &= keep;
+ cx->physical->link.lcp.cfg.chap80lm |= add;
+ break;
+ case NEG_CHAP81:
+ cx->physical->link.lcp.cfg.chap81 &= keep;
+ cx->physical->link.lcp.cfg.chap81 |= add;
+ break;
+ case NEG_MPPE:
+ l->ccp.cfg.neg[CCP_NEG_MPPE] &= keep;
+ l->ccp.cfg.neg[CCP_NEG_MPPE] |= add;
+ break;
+#endif
+ case NEG_DEFLATE:
+ l->ccp.cfg.neg[CCP_NEG_DEFLATE] &= keep;
+ l->ccp.cfg.neg[CCP_NEG_DEFLATE] |= add;
+ break;
+ case NEG_DNS:
+ arg->bundle->ncp.ipcp.cfg.ns.dns_neg &= keep;
+ arg->bundle->ncp.ipcp.cfg.ns.dns_neg |= add;
+ break;
+ case NEG_ECHO: /* probably misplaced in this function ! */
+ if (cx->physical->link.lcp.cfg.echo && !add) {
+ cx->physical->link.lcp.cfg.echo = 0;
+ cx->physical->hdlc.lqm.method &= ~LQM_ECHO;
+ if (cx->physical->hdlc.lqm.method & LQM_ECHO &&
+ !cx->physical->link.lcp.want_lqrperiod &&
+ cx->physical->hdlc.lqm.timer.load) {
+ cx->physical->hdlc.lqm.timer.load = 0;
+ lqr_StopTimer(cx->physical);
+ }
+ } else if (!cx->physical->link.lcp.cfg.echo && add) {
+ cx->physical->link.lcp.cfg.echo = 1;
+ cx->physical->hdlc.lqm.method |= LQM_ECHO;
+ cx->physical->hdlc.lqm.timer.load =
+ cx->physical->link.lcp.cfg.lqrperiod * SECTICKS;
+ if (cx->physical->link.lcp.fsm.state == ST_OPENED)
+ (*cx->physical->hdlc.lqm.timer.func)(&cx->physical->link.lcp);
+ }
+ break;
+ case NEG_ENDDISC:
+ arg->bundle->ncp.mp.cfg.negenddisc &= keep;
+ arg->bundle->ncp.mp.cfg.negenddisc |= add;
+ break;
+ case NEG_LQR:
+ cx->physical->link.lcp.cfg.lqr &= keep;
+ cx->physical->link.lcp.cfg.lqr |= add;
+ break;
+ case NEG_PAP:
+ cx->physical->link.lcp.cfg.pap &= keep;
+ cx->physical->link.lcp.cfg.pap |= add;
+ break;
+ case NEG_PPPDDEFLATE:
+ l->ccp.cfg.neg[CCP_NEG_DEFLATE24] &= keep;
+ l->ccp.cfg.neg[CCP_NEG_DEFLATE24] |= add;
+ break;
+ case NEG_PRED1:
+ l->ccp.cfg.neg[CCP_NEG_PRED1] &= keep;
+ l->ccp.cfg.neg[CCP_NEG_PRED1] |= add;
+ break;
+ case NEG_PROTOCOMP:
+ cx->physical->link.lcp.cfg.protocomp &= keep;
+ cx->physical->link.lcp.cfg.protocomp |= add;
+ break;
+ case NEG_SHORTSEQ:
+ switch (bundle_Phase(arg->bundle)) {
+ case PHASE_DEAD:
+ break;
+ case PHASE_ESTABLISH:
+ /* Make sure none of our links are DATALINK_LCP or greater */
+ if (bundle_HighestState(arg->bundle) >= DATALINK_LCP) {
+ log_Printf(LogWARN, "shortseq: Only changable before"
+ " LCP negotiations\n");
+ return 1;
+ }
+ break;
+ default:
+ log_Printf(LogWARN, "shortseq: Only changable at phase"
+ " DEAD/ESTABLISH\n");
+ return 1;
+ }
+ arg->bundle->ncp.mp.cfg.shortseq &= keep;
+ arg->bundle->ncp.mp.cfg.shortseq |= add;
+ break;
+ case NEG_VJCOMP:
+ arg->bundle->ncp.ipcp.cfg.vj.neg &= keep;
+ arg->bundle->ncp.ipcp.cfg.vj.neg |= add;
+ break;
+ }
+
+ return 0;
+}
+
+static struct cmdtab const NegotiateCommands[] = {
+ {"echo", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX, "Send echo requests",
+ "disable|enable", (const void *)NEG_ECHO},
+ {"filter-decapsulation", NULL, OptSet, LOCAL_AUTH,
+ "filter on PPPoUDP payloads", "disable|enable",
+ (const void *)OPT_FILTERDECAP},
+ {"force-scripts", NULL, OptSet, LOCAL_AUTH,
+ "Force execution of the configured chat scripts", "disable|enable",
+ (const void *)OPT_FORCE_SCRIPTS},
+ {"idcheck", NULL, OptSet, LOCAL_AUTH, "Check FSM reply ids",
+ "disable|enable", (const void *)OPT_IDCHECK},
+ {"iface-alias", NULL, IfaceAliasOptSet, LOCAL_AUTH,
+ "retain interface addresses", "disable|enable",
+ (const void *)OPT_IFACEALIAS},
+#ifndef NOINET6
+ {"ipcp", NULL, OptSet, LOCAL_AUTH, "IP Network Control Protocol",
+ "disable|enable", (const void *)OPT_IPCP},
+ {"ipv6cp", NULL, OptSet, LOCAL_AUTH, "IPv6 Network Control Protocol",
+ "disable|enable", (const void *)OPT_IPV6CP},
+#endif
+ {"keep-session", NULL, OptSet, LOCAL_AUTH, "Retain device session leader",
+ "disable|enable", (const void *)OPT_KEEPSESSION},
+ {"loopback", NULL, OptSet, LOCAL_AUTH, "Loop packets for local iface",
+ "disable|enable", (const void *)OPT_LOOPBACK},
+ {"nas-ip-address", NULL, OptSet, LOCAL_AUTH, "Send NAS-IP-Address to RADIUS",
+ "disable|enable", (const void *)OPT_NAS_IP_ADDRESS},
+ {"nas-identifier", NULL, OptSet, LOCAL_AUTH, "Send NAS-Identifier to RADIUS",
+ "disable|enable", (const void *)OPT_NAS_IDENTIFIER},
+ {"passwdauth", NULL, OptSet, LOCAL_AUTH, "Use passwd file",
+ "disable|enable", (const void *)OPT_PASSWDAUTH},
+ {"proxy", NULL, OptSet, LOCAL_AUTH, "Create a proxy ARP entry",
+ "disable|enable", (const void *)OPT_PROXY},
+ {"proxyall", NULL, OptSet, LOCAL_AUTH, "Proxy ARP for all remote hosts",
+ "disable|enable", (const void *)OPT_PROXYALL},
+ {"sroutes", NULL, OptSet, LOCAL_AUTH, "Use sticky routes",
+ "disable|enable", (const void *)OPT_SROUTES},
+ {"tcpmssfixup", "mssfixup", OptSet, LOCAL_AUTH, "Modify MSS options",
+ "disable|enable", (const void *)OPT_TCPMSSFIXUP},
+ {"throughput", NULL, OptSet, LOCAL_AUTH, "Rolling throughput",
+ "disable|enable", (const void *)OPT_THROUGHPUT},
+ {"utmp", NULL, OptSet, LOCAL_AUTH, "Log connections in utmp",
+ "disable|enable", (const void *)OPT_UTMP},
+
+#ifndef NOINET6
+#define NEG_OPT_MAX 17 /* accept/deny allowed below and not above */
+#else
+#define NEG_OPT_MAX 15
+#endif
+
+ {"acfcomp", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
+ "Address & Control field compression", "accept|deny|disable|enable",
+ (const void *)NEG_ACFCOMP},
+ {"chap", "chap05", NegotiateSet, LOCAL_AUTH | LOCAL_CX,
+ "Challenge Handshake Authentication Protocol", "accept|deny|disable|enable",
+ (const void *)NEG_CHAP05},
+#ifndef NODES
+ {"mschap", "chap80nt", NegotiateSet, LOCAL_AUTH | LOCAL_CX,
+ "Microsoft (NT) CHAP", "accept|deny|disable|enable",
+ (const void *)NEG_CHAP80},
+ {"LANMan", "chap80lm", NegotiateSet, LOCAL_AUTH | LOCAL_CX,
+ "Microsoft (NT) CHAP", "accept|deny|disable|enable",
+ (const void *)NEG_CHAP80LM},
+ {"mschapv2", "chap81", NegotiateSet, LOCAL_AUTH | LOCAL_CX,
+ "Microsoft CHAP v2", "accept|deny|disable|enable",
+ (const void *)NEG_CHAP81},
+ {"mppe", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
+ "MPPE encryption", "accept|deny|disable|enable",
+ (const void *)NEG_MPPE},
+#endif
+ {"deflate", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Deflate compression", "accept|deny|disable|enable",
+ (const void *)NEG_DEFLATE},
+ {"deflate24", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Deflate (type 24) compression", "accept|deny|disable|enable",
+ (const void *)NEG_PPPDDEFLATE},
+ {"dns", NULL, NegotiateSet, LOCAL_AUTH,
+ "DNS specification", "accept|deny|disable|enable", (const void *)NEG_DNS},
+ {"enddisc", NULL, NegotiateSet, LOCAL_AUTH, "ENDDISC negotiation",
+ "accept|deny|disable|enable", (const void *)NEG_ENDDISC},
+ {"lqr", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
+ "Link Quality Reports", "accept|deny|disable|enable",
+ (const void *)NEG_LQR},
+ {"pap", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
+ "Password Authentication protocol", "accept|deny|disable|enable",
+ (const void *)NEG_PAP},
+ {"pred1", "predictor1", NegotiateSet, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Predictor 1 compression", "accept|deny|disable|enable",
+ (const void *)NEG_PRED1},
+ {"protocomp", NULL, NegotiateSet, LOCAL_AUTH | LOCAL_CX,
+ "Protocol field compression", "accept|deny|disable|enable",
+ (const void *)NEG_PROTOCOMP},
+ {"shortseq", NULL, NegotiateSet, LOCAL_AUTH,
+ "MP Short Sequence Numbers", "accept|deny|disable|enable",
+ (const void *)NEG_SHORTSEQ},
+ {"vjcomp", NULL, NegotiateSet, LOCAL_AUTH,
+ "Van Jacobson header compression", "accept|deny|disable|enable",
+ (const void *)NEG_VJCOMP},
+ {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
+ "Display this message", "accept|deny|disable|enable help|? [value]",
+ NegotiateCommands},
+ {NULL, NULL, NULL, 0, NULL, NULL, NULL},
+};
+
+static int
+NegotiateCommand(struct cmdargs const *arg)
+{
+ if (arg->argc > arg->argn) {
+ char const *argv[3];
+ unsigned keep, add;
+ int n;
+
+ if ((argv[0] = ident_cmd(arg->argv[arg->argn-1], &keep, &add)) == NULL)
+ return -1;
+ argv[2] = NULL;
+
+ for (n = arg->argn; n < arg->argc; n++) {
+ argv[1] = arg->argv[n];
+ FindExec(arg->bundle, NegotiateCommands + (keep == NEG_HISMASK ?
+ 0 : NEG_OPT_MAX), 2, 1, argv, arg->prompt, arg->cx);
+ }
+ } else if (arg->prompt)
+ prompt_Printf(arg->prompt, "Use `%s ?' to get a list.\n",
+ arg->argv[arg->argn-1]);
+ else
+ log_Printf(LogWARN, "%s command must have arguments\n",
+ arg->argv[arg->argn] );
+
+ return 0;
+}
+
+const char *
+command_ShowNegval(unsigned val)
+{
+ switch (val&3) {
+ case 1: return "disabled & accepted";
+ case 2: return "enabled & denied";
+ case 3: return "enabled & accepted";
+ }
+ return "disabled & denied";
+}
+
+static int
+ClearCommand(struct cmdargs const *arg)
+{
+ struct pppThroughput *t;
+ struct datalink *cx;
+ int i, clear_type;
+
+ if (arg->argc < arg->argn + 1)
+ return -1;
+
+ if (strcasecmp(arg->argv[arg->argn], "physical") == 0) {
+ cx = arg->cx;
+ if (!cx)
+ cx = bundle2datalink(arg->bundle, NULL);
+ if (!cx) {
+ log_Printf(LogWARN, "A link must be specified for ``clear physical''\n");
+ return 1;
+ }
+ t = &cx->physical->link.stats.total;
+ } else if (strcasecmp(arg->argv[arg->argn], "ipcp") == 0)
+ t = &arg->bundle->ncp.ipcp.throughput;
+#ifndef NOINET6
+ else if (strcasecmp(arg->argv[arg->argn], "ipv6cp") == 0)
+ t = &arg->bundle->ncp.ipv6cp.throughput;
+#endif
+ else
+ return -1;
+
+ if (arg->argc > arg->argn + 1) {
+ clear_type = 0;
+ for (i = arg->argn + 1; i < arg->argc; i++)
+ if (strcasecmp(arg->argv[i], "overall") == 0)
+ clear_type |= THROUGHPUT_OVERALL;
+ else if (strcasecmp(arg->argv[i], "current") == 0)
+ clear_type |= THROUGHPUT_CURRENT;
+ else if (strcasecmp(arg->argv[i], "peak") == 0)
+ clear_type |= THROUGHPUT_PEAK;
+ else
+ return -1;
+ } else
+ clear_type = THROUGHPUT_ALL;
+
+ throughput_clear(t, clear_type, arg->prompt);
+ return 0;
+}
+
+static int
+RunListCommand(struct cmdargs const *arg)
+{
+ const char *cmd = arg->argc ? arg->argv[arg->argc - 1] : "???";
+
+#ifndef NONAT
+ if (arg->cmd->args == NatCommands &&
+ tolower(*arg->argv[arg->argn - 1]) == 'a') {
+ if (arg->prompt)
+ prompt_Printf(arg->prompt, "The alias command is deprecated\n");
+ else
+ log_Printf(LogWARN, "The alias command is deprecated\n");
+ }
+#endif
+
+ if (arg->argc > arg->argn)
+ FindExec(arg->bundle, arg->cmd->args, arg->argc, arg->argn, arg->argv,
+ arg->prompt, arg->cx);
+ else if (arg->prompt)
+ prompt_Printf(arg->prompt, "Use `%s help' to get a list or `%s help"
+ " <option>' for syntax help.\n", cmd, cmd);
+ else
+ log_Printf(LogWARN, "%s command must have arguments\n", cmd);
+
+ return 0;
+}
+
+static int
+IfaceAddCommand(struct cmdargs const *arg)
+{
+ struct ncpaddr peer, addr;
+ struct ncprange ifa;
+ struct in_addr mask;
+ int n, how;
+
+ if (arg->argc == arg->argn + 1) {
+ if (!ncprange_aton(&ifa, NULL, arg->argv[arg->argn]))
+ return -1;
+ ncpaddr_init(&peer);
+ } else {
+ if (arg->argc == arg->argn + 2) {
+ if (!ncprange_aton(&ifa, NULL, arg->argv[arg->argn]))
+ return -1;
+ n = 1;
+ } else if (arg->argc == arg->argn + 3) {
+ if (!ncpaddr_aton(&addr, NULL, arg->argv[arg->argn]))
+ return -1;
+ if (ncpaddr_family(&addr) != AF_INET)
+ return -1;
+ ncprange_sethost(&ifa, &addr);
+ if (!ncpaddr_aton(&addr, NULL, arg->argv[arg->argn + 1]))
+ return -1;
+ if (!ncpaddr_getip4(&addr, &mask))
+ return -1;
+ if (!ncprange_setip4mask(&ifa, mask))
+ return -1;
+ n = 2;
+ } else
+ return -1;
+
+ if (!ncpaddr_aton(&peer, NULL, arg->argv[arg->argn + n]))
+ return -1;
+
+ if (ncprange_family(&ifa) != ncpaddr_family(&peer)) {
+ log_Printf(LogWARN, "IfaceAddCommand: src and dst address families"
+ " differ\n");
+ return -1;
+ }
+ }
+
+ how = IFACE_ADD_LAST;
+ if (arg->cmd->args)
+ how |= IFACE_FORCE_ADD;
+
+ return !iface_Add(arg->bundle->iface, &arg->bundle->ncp, &ifa, &peer, how);
+}
+
+static int
+IfaceDeleteCommand(struct cmdargs const *arg)
+{
+ struct ncpaddr ifa;
+ struct in_addr ifa4;
+ int ok;
+
+ if (arg->argc != arg->argn + 1)
+ return -1;
+
+ if (!ncpaddr_aton(&ifa, NULL, arg->argv[arg->argn]))
+ return -1;
+
+ if (arg->bundle->ncp.ipcp.fsm.state == ST_OPENED &&
+ ncpaddr_getip4(&ifa, &ifa4) &&
+ arg->bundle->ncp.ipcp.my_ip.s_addr == ifa4.s_addr) {
+ log_Printf(LogWARN, "%s: Cannot remove active interface address\n",
+ ncpaddr_ntoa(&ifa));
+ return 1;
+ }
+
+ ok = iface_Delete(arg->bundle->iface, &arg->bundle->ncp, &ifa);
+ if (!ok) {
+ if (arg->cmd->args)
+ ok = 1;
+ else if (arg->prompt)
+ prompt_Printf(arg->prompt, "%s: No such interface address\n",
+ ncpaddr_ntoa(&ifa));
+ else
+ log_Printf(LogWARN, "%s: No such interface address\n",
+ ncpaddr_ntoa(&ifa));
+ }
+
+ return !ok;
+}
+
+static int
+IfaceClearCommand(struct cmdargs const *arg)
+{
+ int family, how;
+
+ family = 0;
+ if (arg->argc == arg->argn + 1) {
+ if (strcasecmp(arg->argv[arg->argn], "inet") == 0)
+ family = AF_INET;
+#ifndef NOINET6
+ else if (strcasecmp(arg->argv[arg->argn], "inet6") == 0)
+ family = AF_INET6;
+#endif
+ else
+ return -1;
+ } else if (arg->argc != arg->argn)
+ return -1;
+
+ how = arg->bundle->ncp.ipcp.fsm.state == ST_OPENED ||
+ arg->bundle->phys_type.all & PHYS_AUTO ?
+ IFACE_CLEAR_ALIASES : IFACE_CLEAR_ALL;
+ iface_Clear(arg->bundle->iface, &arg->bundle->ncp, family, how);
+
+ return 0;
+}
+
+static int
+SetProcTitle(struct cmdargs const *arg)
+{
+ static char title[LINE_LEN];
+ char *argv[MAXARGS];
+ int argc = arg->argc - arg->argn;
+
+ if (arg->argc <= arg->argn) {
+ SetTitle(NULL);
+ return 0;
+ }
+
+ if ((unsigned)argc >= sizeof argv / sizeof argv[0]) {
+ argc = sizeof argv / sizeof argv[0] - 1;
+ log_Printf(LogWARN, "Truncating proc title to %d args\n", argc);
+ }
+ command_Expand(argv, argc, arg->argv + arg->argn, arg->bundle, 1, getpid());
+ Concatinate(title, sizeof title, argc, (const char *const *)argv);
+ SetTitle(title);
+ command_Free(argc, argv);
+
+ return 0;
+}
diff --git a/usr.sbin/ppp/command.h b/usr.sbin/ppp/command.h
new file mode 100644
index 0000000..8eb1e38
--- /dev/null
+++ b/usr.sbin/ppp/command.h
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+struct cmdtab;
+struct bundle;
+struct datalink;
+struct prompt;
+
+struct cmdargs {
+ struct cmdtab const *cmdtab; /* The entire command table */
+ struct cmdtab const *cmd; /* This command entry */
+ int argc; /* Number of arguments (excluding cmd */
+ int argn; /* Argument to start processing from */
+ char const *const *argv; /* Arguments */
+ struct bundle *bundle; /* Our bundle */
+ struct datalink *cx; /* Our context */
+ struct prompt *prompt; /* Who executed us */
+};
+
+struct cmdtab {
+ const char *name;
+ const char *alias;
+ int (*func) (struct cmdargs const *);
+ u_char lauth;
+ const char *helpmes;
+ const char *syntax;
+ const void *args;
+};
+
+#define NEG_ACCEPTED (1)
+#define NEG_ENABLED (2)
+#define IsAccepted(x) ((x) & NEG_ACCEPTED)
+#define IsEnabled(x) ((x) & NEG_ENABLED)
+
+extern const char Version[];
+
+extern void command_Expand(char **, int, char const *const *, struct bundle *,
+ int, pid_t);
+extern void command_Free(int, char **);
+extern int command_Expand_Interpret(char *, int, char *vector[MAXARGS], int);
+extern int command_Interpret(char *, int, char *vector[MAXARGS]);
+extern void command_Run(struct bundle *, int, char const *const *,
+ struct prompt *, const char *, struct datalink *);
+extern int command_Decode(struct bundle *, char *, int, struct prompt *,
+ const char *);
+extern struct link *command_ChooseLink(struct cmdargs const *);
+extern const char *command_ShowNegval(unsigned);
+
diff --git a/usr.sbin/ppp/datalink.c b/usr.sbin/ppp/datalink.c
new file mode 100644
index 0000000..d47b11a
--- /dev/null
+++ b/usr.sbin/ppp/datalink.c
@@ -0,0 +1,1478 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <termios.h>
+
+#include "layer.h"
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "descriptor.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "async.h"
+#include "throughput.h"
+#include "ccp.h"
+#include "link.h"
+#include "physical.h"
+#include "iplist.h"
+#include "slcompress.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "chat.h"
+#include "auth.h"
+#include "prompt.h"
+#include "proto.h"
+#include "pap.h"
+#include "chap.h"
+#include "command.h"
+#include "cbcp.h"
+#include "datalink.h"
+
+static void datalink_LoginDone(struct datalink *);
+static void datalink_NewState(struct datalink *, unsigned);
+static char *datalink_NextName(struct datalink *);
+
+static void
+datalink_OpenTimeout(void *v)
+{
+ struct datalink *dl = (struct datalink *)v;
+
+ timer_Stop(&dl->dial.timer);
+ if (dl->state == DATALINK_OPENING)
+ log_Printf(LogCHAT, "%s: Redial timer expired.\n", dl->name);
+}
+
+static int
+datalink_StartDialTimer(struct datalink *dl, int Timeout)
+{
+ int result = Timeout;
+
+ timer_Stop(&dl->dial.timer);
+ if (Timeout < 0)
+ result = (random() % DIAL_TIMEOUT) + 1;
+ dl->dial.timer.load = result ? result * SECTICKS : 1;
+ dl->dial.timer.func = datalink_OpenTimeout;
+ dl->dial.timer.name = "dial";
+ dl->dial.timer.arg = dl;
+ timer_Start(&dl->dial.timer);
+ if (dl->state == DATALINK_OPENING)
+ log_Printf(LogPHASE, "%s: Enter pause (%d) for redialing.\n",
+ dl->name, result);
+ return result;
+}
+
+static void
+datalink_HangupDone(struct datalink *dl)
+{
+ if (dl->physical->type == PHYS_DEDICATED && !dl->bundle->CleaningUp &&
+ dl->physical->fd != -1) {
+ /* Don't close our device if the link is dedicated */
+ datalink_LoginDone(dl);
+ return;
+ }
+
+ chat_Finish(&dl->chat);
+ physical_Close(dl->physical);
+ dl->phone.chosen = "N/A";
+
+ if (dl->cbcp.required) {
+ log_Printf(LogPHASE, "Call peer back on %s\n", dl->cbcp.fsm.phone);
+ dl->cfg.callback.opmask = 0;
+ strncpy(dl->cfg.phone.list, dl->cbcp.fsm.phone,
+ sizeof dl->cfg.phone.list - 1);
+ dl->cfg.phone.list[sizeof dl->cfg.phone.list - 1] = '\0';
+ dl->phone.alt = dl->phone.next = NULL;
+ dl->reconnect_tries = dl->cfg.reconnect.max;
+ dl->dial.tries = dl->cfg.dial.max;
+ dl->dial.incs = 0;
+ dl->script.run = 1;
+ dl->script.packetmode = 1;
+ if (!physical_SetMode(dl->physical, PHYS_BACKGROUND))
+ log_Printf(LogERROR, "Oops - can't change mode to BACKGROUND (gulp) !\n");
+ bundle_LinksRemoved(dl->bundle);
+ /* if dial.timeout is < 0 (random), we don't override fsm.delay */
+ if (dl->cbcp.fsm.delay < dl->cfg.dial.timeout)
+ dl->cbcp.fsm.delay = dl->cfg.dial.timeout;
+ datalink_StartDialTimer(dl, dl->cbcp.fsm.delay);
+ cbcp_Down(&dl->cbcp);
+ datalink_NewState(dl, DATALINK_OPENING);
+ if (bundle_Phase(dl->bundle) == PHASE_DEAD ||
+ bundle_Phase(dl->bundle) == PHASE_TERMINATE)
+ bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
+ } else if (dl->bundle->CleaningUp ||
+ (dl->physical->type == PHYS_DIRECT) ||
+ ((!dl->dial.tries || (dl->dial.tries < 0 && !dl->reconnect_tries)) &&
+ !(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)))) {
+ datalink_NewState(dl, DATALINK_CLOSED);
+ dl->dial.tries = -1;
+ dl->dial.incs = 0;
+ dl->reconnect_tries = 0;
+ bundle_LinkClosed(dl->bundle, dl);
+ if (!dl->bundle->CleaningUp &&
+ !(dl->physical->type & (PHYS_DIRECT|PHYS_BACKGROUND|PHYS_FOREGROUND)))
+ datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
+ } else {
+ datalink_NewState(dl, DATALINK_OPENING);
+ if (bundle_Phase(dl->bundle) == PHASE_DEAD ||
+ bundle_Phase(dl->bundle) == PHASE_TERMINATE)
+ bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
+ if (dl->dial.tries < 0) {
+ datalink_StartDialTimer(dl, dl->cfg.reconnect.timeout);
+ dl->dial.tries = dl->cfg.dial.max;
+ dl->dial.incs = 0;
+ dl->reconnect_tries--;
+ log_Printf(LogCHAT, "%s: Reconnect try %d of %d\n",
+ dl->name, dl->cfg.reconnect.max - dl->reconnect_tries,
+ dl->cfg.reconnect.max);
+ bundle_Notify(dl->bundle, EX_RECONNECT);
+ } else {
+ if (dl->phone.next == NULL)
+ datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
+ else
+ datalink_StartDialTimer(dl, dl->cfg.dial.next_timeout);
+ bundle_Notify(dl->bundle, EX_REDIAL);
+ }
+ }
+}
+
+const char *
+datalink_ChoosePhoneNumber(struct datalink *dl)
+{
+ char *phone;
+
+ if (dl->phone.alt == NULL) {
+ if (dl->phone.next == NULL) {
+ strncpy(dl->phone.list, dl->cfg.phone.list, sizeof dl->phone.list - 1);
+ dl->phone.list[sizeof dl->phone.list - 1] = '\0';
+ if (*dl->phone.list == '\0')
+ return "";
+ dl->phone.next = dl->phone.list;
+ }
+ dl->phone.alt = strsep(&dl->phone.next, ":");
+ }
+ phone = strsep(&dl->phone.alt, "|");
+ dl->phone.chosen = *phone ? phone : "[NONE]";
+ if (*phone)
+ log_Printf(LogCHAT, "Phone: %s\n", phone);
+ return phone;
+}
+
+static void
+datalink_LoginDone(struct datalink *dl)
+{
+ chat_Finish(&dl->chat);
+
+ if (!dl->script.packetmode) {
+ dl->dial.tries = -1;
+ dl->dial.incs = 0;
+ datalink_NewState(dl, DATALINK_READY);
+ } else if (!physical_Raw(dl->physical)) {
+ dl->dial.tries = 0;
+ log_Printf(LogWARN, "datalink_LoginDone: Not connected.\n");
+ if (dl->script.run) {
+ datalink_NewState(dl, DATALINK_LOGOUT);
+ if (!chat_Setup(&dl->chat, dl->cfg.script.logout, NULL))
+ log_Printf(LogWARN, "Invalid logout script\n");
+ } else {
+ physical_StopDeviceTimer(dl->physical);
+ if (dl->physical->type == PHYS_DEDICATED)
+ /* force a redial timeout */
+ physical_Close(dl->physical);
+ datalink_HangupDone(dl);
+ }
+ } else {
+ dl->dial.tries = -1;
+ dl->dial.incs = 0;
+
+ hdlc_Init(&dl->physical->hdlc, &dl->physical->link.lcp);
+ async_Setup(&dl->physical->async);
+
+ lcp_Setup(&dl->physical->link.lcp, dl->state == DATALINK_READY ?
+ 0 : dl->physical->link.lcp.cfg.openmode);
+ ccp_Setup(&dl->physical->link.ccp);
+
+ datalink_NewState(dl, DATALINK_LCP);
+ fsm_Up(&dl->physical->link.lcp.fsm);
+ fsm_Open(&dl->physical->link.lcp.fsm);
+ }
+}
+
+static int
+datalink_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e,
+ int *n)
+{
+ struct datalink *dl = descriptor2datalink(d);
+ int result;
+
+ result = 0;
+ switch (dl->state) {
+ case DATALINK_CLOSED:
+ if ((dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED|PHYS_BACKGROUND|
+ PHYS_FOREGROUND|PHYS_DDIAL)) &&
+ !dl->bundle->CleaningUp)
+ /*
+ * Our first time in - DEDICATED & DDIAL never come down, and
+ * DIRECT, FOREGROUND & BACKGROUND get deleted when they enter
+ * DATALINK_CLOSED. Go to DATALINK_OPENING via datalink_Up()
+ * and fall through.
+ */
+ datalink_Up(dl, 1, 1);
+ else
+ break;
+ /* FALLTHROUGH */
+
+ case DATALINK_OPENING:
+ if (dl->dial.timer.state != TIMER_RUNNING) {
+ if (--dl->dial.tries < 0)
+ dl->dial.tries = 0;
+ if (physical_Open(dl->physical) >= 0) {
+ log_WritePrompts(dl, "%s: Entering terminal mode on %s\r\n"
+ "Type `~?' for help\r\n", dl->name,
+ dl->physical->name.full);
+ if (dl->script.run) {
+ datalink_NewState(dl, DATALINK_DIAL);
+ if (!chat_Setup(&dl->chat, dl->cfg.script.dial,
+ *dl->cfg.script.dial ?
+ datalink_ChoosePhoneNumber(dl) : ""))
+ log_Printf(LogWARN, "Invalid dial script\n");
+ if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
+ dl->cfg.dial.max)
+ log_Printf(LogCHAT, "%s: Dial attempt %u of %d\n",
+ dl->name, dl->cfg.dial.max - dl->dial.tries,
+ dl->cfg.dial.max);
+ } else
+ datalink_NewState(dl, DATALINK_CARRIER);
+ return datalink_UpdateSet(d, r, w, e, n);
+ } else {
+ if (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
+ dl->cfg.dial.max)
+ log_Printf(LogCHAT, "Failed to open device (attempt %u of %d)\n",
+ dl->cfg.dial.max - dl->dial.tries, dl->cfg.dial.max);
+ else
+ log_Printf(LogCHAT, "Failed to open device\n");
+
+ if (dl->bundle->CleaningUp ||
+ (!(dl->physical->type & (PHYS_DDIAL|PHYS_DEDICATED)) &&
+ dl->cfg.dial.max && dl->dial.tries == 0)) {
+ datalink_NewState(dl, DATALINK_CLOSED);
+ dl->reconnect_tries = 0;
+ dl->dial.tries = -1;
+ log_WritePrompts(dl, "Failed to open %s\n",
+ dl->physical->name.full);
+ bundle_LinkClosed(dl->bundle, dl);
+ }
+ if (!dl->bundle->CleaningUp) {
+ int timeout;
+
+ timeout = datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
+ bundle_Notify(dl->bundle, EX_REDIAL);
+ log_WritePrompts(dl, "Failed to open %s, pause %d seconds\n",
+ dl->physical->name.full, timeout);
+ }
+ }
+ }
+ break;
+
+ case DATALINK_CARRIER:
+ /* Wait for carrier on the device */
+ switch (physical_AwaitCarrier(dl->physical)) {
+ case CARRIER_PENDING:
+ log_Printf(LogDEBUG, "Waiting for carrier\n");
+ return 0; /* A device timer is running to wake us up again */
+
+ case CARRIER_OK:
+ if (dl->script.run) {
+ datalink_NewState(dl, DATALINK_LOGIN);
+ if (!chat_Setup(&dl->chat, dl->cfg.script.login, NULL))
+ log_Printf(LogWARN, "Invalid login script\n");
+ } else
+ datalink_LoginDone(dl);
+ return datalink_UpdateSet(d, r, w, e, n);
+
+ case CARRIER_LOST:
+ physical_Offline(dl->physical); /* Is this required ? */
+ if (dl->script.run) {
+ datalink_NewState(dl, DATALINK_HANGUP);
+ if (!chat_Setup(&dl->chat, dl->cfg.script.hangup, NULL))
+ log_Printf(LogWARN, "Invalid hangup script\n");
+ return datalink_UpdateSet(d, r, w, e, n);
+ } else {
+ datalink_HangupDone(dl);
+ return 0; /* Maybe bundle_CleanDatalinks() has something to do */
+ }
+ }
+
+ case DATALINK_HANGUP:
+ case DATALINK_DIAL:
+ case DATALINK_LOGOUT:
+ case DATALINK_LOGIN:
+ result = descriptor_UpdateSet(&dl->chat.desc, r, w, e, n);
+ switch (dl->chat.state) {
+ case CHAT_DONE:
+ /* script succeeded */
+ switch(dl->state) {
+ case DATALINK_HANGUP:
+ datalink_HangupDone(dl);
+ break;
+ case DATALINK_DIAL:
+ datalink_NewState(dl, DATALINK_CARRIER);
+ return datalink_UpdateSet(d, r, w, e, n);
+ case DATALINK_LOGOUT:
+ datalink_NewState(dl, DATALINK_HANGUP);
+ physical_Offline(dl->physical);
+ if (!chat_Setup(&dl->chat, dl->cfg.script.hangup, NULL))
+ log_Printf(LogWARN, "Invalid hangup script\n");
+ return datalink_UpdateSet(d, r, w, e, n);
+ case DATALINK_LOGIN:
+ dl->phone.alt = NULL;
+ datalink_LoginDone(dl);
+ return datalink_UpdateSet(d, r, w, e, n);
+ }
+ break;
+ case CHAT_FAILED:
+ /* Going down - script failed */
+ log_Printf(LogWARN, "Chat script failed\n");
+ switch(dl->state) {
+ case DATALINK_HANGUP:
+ datalink_HangupDone(dl);
+ break;
+ case DATALINK_DIAL:
+ case DATALINK_LOGOUT:
+ case DATALINK_LOGIN:
+ datalink_NewState(dl, DATALINK_HANGUP);
+ physical_Offline(dl->physical);
+ if (!chat_Setup(&dl->chat, dl->cfg.script.hangup, NULL))
+ log_Printf(LogWARN, "Invalid hangup script\n");
+ return datalink_UpdateSet(d, r, w, e, n);
+ }
+ break;
+ }
+ break;
+
+ case DATALINK_READY:
+ case DATALINK_LCP:
+ case DATALINK_AUTH:
+ case DATALINK_CBCP:
+ case DATALINK_OPEN:
+ result = descriptor_UpdateSet(&dl->chap.desc, r, w, e, n) +
+ descriptor_UpdateSet(&dl->physical->desc, r, w, e, n);
+ break;
+ }
+ return result;
+}
+
+int
+datalink_RemoveFromSet(struct datalink *dl, fd_set *r, fd_set *w, fd_set *e)
+{
+ return physical_RemoveFromSet(dl->physical, r, w, e);
+}
+
+static int
+datalink_IsSet(struct fdescriptor *d, const fd_set *fdset)
+{
+ struct datalink *dl = descriptor2datalink(d);
+
+ switch (dl->state) {
+ case DATALINK_CLOSED:
+ case DATALINK_OPENING:
+ break;
+
+ case DATALINK_HANGUP:
+ case DATALINK_DIAL:
+ case DATALINK_LOGOUT:
+ case DATALINK_LOGIN:
+ return descriptor_IsSet(&dl->chat.desc, fdset);
+
+ case DATALINK_READY:
+ case DATALINK_LCP:
+ case DATALINK_AUTH:
+ case DATALINK_CBCP:
+ case DATALINK_OPEN:
+ return descriptor_IsSet(&dl->chap.desc, fdset) ? 1 :
+ descriptor_IsSet(&dl->physical->desc, fdset);
+ }
+ return 0;
+}
+
+static void
+datalink_Read(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
+{
+ struct datalink *dl = descriptor2datalink(d);
+
+ switch (dl->state) {
+ case DATALINK_CLOSED:
+ case DATALINK_OPENING:
+ break;
+
+ case DATALINK_HANGUP:
+ case DATALINK_DIAL:
+ case DATALINK_LOGOUT:
+ case DATALINK_LOGIN:
+ descriptor_Read(&dl->chat.desc, bundle, fdset);
+ break;
+
+ case DATALINK_READY:
+ case DATALINK_LCP:
+ case DATALINK_AUTH:
+ case DATALINK_CBCP:
+ case DATALINK_OPEN:
+ if (descriptor_IsSet(&dl->chap.desc, fdset))
+ descriptor_Read(&dl->chap.desc, bundle, fdset);
+ if (descriptor_IsSet(&dl->physical->desc, fdset))
+ descriptor_Read(&dl->physical->desc, bundle, fdset);
+ break;
+ }
+}
+
+static int
+datalink_Write(struct fdescriptor *d, struct bundle *bundle,
+ const fd_set *fdset)
+{
+ struct datalink *dl = descriptor2datalink(d);
+ int result = 0;
+
+ switch (dl->state) {
+ case DATALINK_CLOSED:
+ case DATALINK_OPENING:
+ break;
+
+ case DATALINK_HANGUP:
+ case DATALINK_DIAL:
+ case DATALINK_LOGOUT:
+ case DATALINK_LOGIN:
+ if ((result = descriptor_Write(&dl->chat.desc, bundle, fdset)) == -1) {
+ datalink_ComeDown(dl, CLOSE_NORMAL);
+ result = 0;
+ }
+ break;
+
+ case DATALINK_READY:
+ case DATALINK_LCP:
+ case DATALINK_AUTH:
+ case DATALINK_CBCP:
+ case DATALINK_OPEN:
+ if (descriptor_IsSet(&dl->chap.desc, fdset))
+ switch (descriptor_Write(&dl->chap.desc, bundle, fdset)) {
+ case -1:
+ datalink_ComeDown(dl, CLOSE_NORMAL);
+ break;
+ case 1:
+ result++;
+ }
+ if (descriptor_IsSet(&dl->physical->desc, fdset))
+ switch (descriptor_Write(&dl->physical->desc, bundle, fdset)) {
+ case -1:
+ datalink_ComeDown(dl, CLOSE_NORMAL);
+ break;
+ case 1:
+ result++;
+ }
+ break;
+ }
+
+ return result;
+}
+
+void
+datalink_ComeDown(struct datalink *dl, int how)
+{
+ int stayonline;
+
+ if (how == CLOSE_LCP)
+ datalink_DontHangup(dl);
+ else if (how == CLOSE_STAYDOWN)
+ datalink_StayDown(dl);
+
+ stayonline = dl->stayonline;
+ dl->stayonline = 0;
+
+ if (dl->state >= DATALINK_READY && stayonline) {
+ physical_StopDeviceTimer(dl->physical);
+ datalink_NewState(dl, DATALINK_READY);
+ } else if (dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP) {
+ physical_Offline(dl->physical);
+ if (dl->script.run && dl->state != DATALINK_OPENING) {
+ if (dl->state == DATALINK_LOGOUT) {
+ datalink_NewState(dl, DATALINK_HANGUP);
+ if (!chat_Setup(&dl->chat, dl->cfg.script.hangup, NULL))
+ log_Printf(LogWARN, "Invalid hangup script\n");
+ } else {
+ datalink_NewState(dl, DATALINK_LOGOUT);
+ if (!chat_Setup(&dl->chat, dl->cfg.script.logout, NULL))
+ log_Printf(LogWARN, "Invalid logout script\n");
+ }
+ } else
+ datalink_HangupDone(dl);
+ }
+}
+
+static void
+datalink_LayerStart(void *v, struct fsm *fp)
+{
+ /* The given FSM is about to start up ! */
+ struct datalink *dl = (struct datalink *)v;
+
+ if (fp->proto == PROTO_LCP)
+ (*dl->parent->LayerStart)(dl->parent->object, fp);
+}
+
+static void
+datalink_LayerUp(void *v, struct fsm *fp)
+{
+ /* The given fsm is now up */
+ struct datalink *dl = (struct datalink *)v;
+ struct lcp *lcp = &dl->physical->link.lcp;
+
+ if (fp->proto == PROTO_LCP) {
+ datalink_GotAuthname(dl, "");
+ lcp->auth_ineed = lcp->want_auth;
+ lcp->auth_iwait = lcp->his_auth;
+ if (lcp->his_auth || lcp->want_auth) {
+ if (bundle_Phase(dl->bundle) != PHASE_NETWORK)
+ bundle_NewPhase(dl->bundle, PHASE_AUTHENTICATE);
+ log_Printf(LogPHASE, "%s: his = %s, mine = %s\n", dl->name,
+ Auth2Nam(lcp->his_auth, lcp->his_authtype),
+ Auth2Nam(lcp->want_auth, lcp->want_authtype));
+ if (lcp->his_auth == PROTO_PAP)
+ auth_StartReq(&dl->pap);
+ if (lcp->want_auth == PROTO_CHAP)
+ auth_StartReq(&dl->chap.auth);
+ } else
+ datalink_AuthOk(dl);
+ } else if (fp->proto == PROTO_CCP)
+ (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.ccp.fsm);
+}
+
+static void
+datalink_AuthReInit(struct datalink *dl)
+{
+ auth_StopTimer(&dl->pap);
+ auth_StopTimer(&dl->chap.auth);
+ chap_ReInit(&dl->chap);
+}
+
+void
+datalink_GotAuthname(struct datalink *dl, const char *name)
+{
+ strncpy(dl->peer.authname, name, sizeof dl->peer.authname - 1);
+ dl->peer.authname[sizeof dl->peer.authname - 1] = '\0';
+}
+
+void
+datalink_NCPUp(struct datalink *dl)
+{
+ int ccpok = ccp_SetOpenMode(&dl->physical->link.ccp);
+
+ if (dl->physical->link.lcp.want_mrru && dl->physical->link.lcp.his_mrru) {
+ /* we've authenticated in multilink mode ! */
+ switch (mp_Up(&dl->bundle->ncp.mp, dl)) {
+ case MP_LINKSENT:
+ /* We've handed the link off to another ppp (well, we will soon) ! */
+ return;
+ case MP_UP:
+ /* First link in the bundle */
+ auth_Select(dl->bundle, dl->peer.authname);
+ bundle_CalculateBandwidth(dl->bundle);
+ /* FALLTHROUGH */
+ case MP_ADDED:
+ /* We're in multilink mode ! */
+ dl->physical->link.ccp.fsm.open_mode = OPEN_PASSIVE; /* override */
+ bundle_CalculateBandwidth(dl->bundle);
+ break;
+ case MP_FAILED:
+ datalink_AuthNotOk(dl);
+ return;
+ }
+ } else if (bundle_Phase(dl->bundle) == PHASE_NETWORK) {
+ log_Printf(LogPHASE, "%s: Already in NETWORK phase\n", dl->name);
+ datalink_NewState(dl, DATALINK_OPEN);
+ bundle_CalculateBandwidth(dl->bundle);
+ (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
+ return;
+ } else {
+ dl->bundle->ncp.mp.peer = dl->peer;
+ ncp_SetLink(&dl->bundle->ncp, &dl->physical->link);
+ auth_Select(dl->bundle, dl->peer.authname);
+ }
+
+ if (ccpok) {
+ fsm_Up(&dl->physical->link.ccp.fsm);
+ fsm_Open(&dl->physical->link.ccp.fsm);
+ }
+ datalink_NewState(dl, DATALINK_OPEN);
+ bundle_NewPhase(dl->bundle, PHASE_NETWORK);
+ (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
+}
+
+void
+datalink_CBCPComplete(struct datalink *dl)
+{
+ datalink_NewState(dl, DATALINK_LCP);
+ datalink_AuthReInit(dl);
+ fsm_Close(&dl->physical->link.lcp.fsm);
+}
+
+void
+datalink_CBCPFailed(struct datalink *dl)
+{
+ cbcp_Down(&dl->cbcp);
+ datalink_CBCPComplete(dl);
+}
+
+void
+datalink_AuthOk(struct datalink *dl)
+{
+ if ((dl->physical->link.lcp.his_callback.opmask &
+ CALLBACK_BIT(CALLBACK_CBCP) ||
+ dl->physical->link.lcp.want_callback.opmask &
+ CALLBACK_BIT(CALLBACK_CBCP)) &&
+ !(dl->physical->link.lcp.want_callback.opmask &
+ CALLBACK_BIT(CALLBACK_AUTH))) {
+ /* We must have agreed CBCP if AUTH isn't there any more */
+ datalink_NewState(dl, DATALINK_CBCP);
+ cbcp_Up(&dl->cbcp);
+ } else if (dl->physical->link.lcp.want_callback.opmask) {
+ /* It's not CBCP */
+ log_Printf(LogPHASE, "%s: Shutdown and await peer callback\n", dl->name);
+ datalink_NewState(dl, DATALINK_LCP);
+ datalink_AuthReInit(dl);
+ fsm_Close(&dl->physical->link.lcp.fsm);
+ } else
+ switch (dl->physical->link.lcp.his_callback.opmask) {
+ case 0:
+ datalink_NCPUp(dl);
+ break;
+
+ case CALLBACK_BIT(CALLBACK_AUTH):
+ auth_SetPhoneList(dl->peer.authname, dl->cbcp.fsm.phone,
+ sizeof dl->cbcp.fsm.phone);
+ if (*dl->cbcp.fsm.phone == '\0' || !strcmp(dl->cbcp.fsm.phone, "*")) {
+ log_Printf(LogPHASE, "%s: %s cannot be called back\n", dl->name,
+ dl->peer.authname);
+ *dl->cbcp.fsm.phone = '\0';
+ } else {
+ char *ptr = strchr(dl->cbcp.fsm.phone, ',');
+ if (ptr)
+ *ptr = '\0'; /* Call back on the first number */
+ log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
+ dl->cbcp.fsm.phone);
+ dl->cbcp.required = 1;
+ }
+ dl->cbcp.fsm.delay = 0;
+ datalink_NewState(dl, DATALINK_LCP);
+ datalink_AuthReInit(dl);
+ fsm_Close(&dl->physical->link.lcp.fsm);
+ break;
+
+ case CALLBACK_BIT(CALLBACK_E164):
+ strncpy(dl->cbcp.fsm.phone, dl->physical->link.lcp.his_callback.msg,
+ sizeof dl->cbcp.fsm.phone - 1);
+ dl->cbcp.fsm.phone[sizeof dl->cbcp.fsm.phone - 1] = '\0';
+ log_Printf(LogPHASE, "%s: Calling peer back on %s\n", dl->name,
+ dl->cbcp.fsm.phone);
+ dl->cbcp.required = 1;
+ dl->cbcp.fsm.delay = 0;
+ datalink_NewState(dl, DATALINK_LCP);
+ datalink_AuthReInit(dl);
+ fsm_Close(&dl->physical->link.lcp.fsm);
+ break;
+
+ default:
+ log_Printf(LogPHASE, "%s: Oops - Should have NAK'd peer callback !\n",
+ dl->name);
+ datalink_NewState(dl, DATALINK_LCP);
+ datalink_AuthReInit(dl);
+ fsm_Close(&dl->physical->link.lcp.fsm);
+ break;
+ }
+}
+
+void
+datalink_AuthNotOk(struct datalink *dl)
+{
+ datalink_NewState(dl, DATALINK_LCP);
+ datalink_AuthReInit(dl);
+ fsm_Close(&dl->physical->link.lcp.fsm);
+}
+
+static void
+datalink_LayerDown(void *v, struct fsm *fp)
+{
+ /* The given FSM has been told to come down */
+ struct datalink *dl = (struct datalink *)v;
+
+ if (fp->proto == PROTO_LCP) {
+ switch (dl->state) {
+ case DATALINK_OPEN:
+ peerid_Init(&dl->peer);
+ fsm2initial(&dl->physical->link.ccp.fsm);
+ datalink_NewState(dl, DATALINK_LCP); /* before parent TLD */
+ (*dl->parent->LayerDown)(dl->parent->object, fp);
+ /* FALLTHROUGH (just in case) */
+
+ case DATALINK_CBCP:
+ if (!dl->cbcp.required)
+ cbcp_Down(&dl->cbcp);
+ /* FALLTHROUGH (just in case) */
+
+ case DATALINK_AUTH:
+ timer_Stop(&dl->pap.authtimer);
+ timer_Stop(&dl->chap.auth.authtimer);
+ }
+ datalink_NewState(dl, DATALINK_LCP);
+ datalink_AuthReInit(dl);
+ }
+}
+
+static void
+datalink_LayerFinish(void *v, struct fsm *fp)
+{
+ /* The given fsm is now down */
+ struct datalink *dl = (struct datalink *)v;
+
+ if (fp->proto == PROTO_LCP) {
+ fsm2initial(fp);
+ (*dl->parent->LayerFinish)(dl->parent->object, fp);
+ datalink_ComeDown(dl, CLOSE_NORMAL);
+ } else if (fp->state == ST_CLOSED && fp->open_mode == OPEN_PASSIVE)
+ fsm_Open(fp); /* CCP goes to ST_STOPPED */
+}
+
+struct datalink *
+datalink_Create(const char *name, struct bundle *bundle, int type)
+{
+ struct datalink *dl;
+
+ dl = (struct datalink *)malloc(sizeof(struct datalink));
+ if (dl == NULL)
+ return dl;
+
+ dl->desc.type = DATALINK_DESCRIPTOR;
+ dl->desc.UpdateSet = datalink_UpdateSet;
+ dl->desc.IsSet = datalink_IsSet;
+ dl->desc.Read = datalink_Read;
+ dl->desc.Write = datalink_Write;
+
+ dl->state = DATALINK_CLOSED;
+
+ *dl->cfg.script.dial = '\0';
+ *dl->cfg.script.login = '\0';
+ *dl->cfg.script.logout = '\0';
+ *dl->cfg.script.hangup = '\0';
+ *dl->cfg.phone.list = '\0';
+ *dl->phone.list = '\0';
+ dl->phone.next = NULL;
+ dl->phone.alt = NULL;
+ dl->phone.chosen = "N/A";
+ dl->stayonline = 0;
+ dl->script.run = 1;
+ dl->script.packetmode = 1;
+ mp_linkInit(&dl->mp);
+
+ dl->bundle = bundle;
+ dl->next = NULL;
+
+ memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
+
+ dl->dial.tries = 0;
+ dl->cfg.dial.max = 1;
+ dl->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
+ dl->cfg.dial.timeout = DIAL_TIMEOUT;
+ dl->cfg.dial.inc = 0;
+ dl->cfg.dial.maxinc = 10;
+
+ dl->reconnect_tries = 0;
+ dl->cfg.reconnect.max = 0;
+ dl->cfg.reconnect.timeout = RECONNECT_TIMEOUT;
+
+ dl->cfg.callback.opmask = 0;
+ dl->cfg.cbcp.delay = 0;
+ *dl->cfg.cbcp.phone = '\0';
+ dl->cfg.cbcp.fsmretry = DEF_FSMRETRY;
+
+ dl->name = strdup(name);
+ peerid_Init(&dl->peer);
+ dl->parent = &bundle->fsm;
+ dl->fsmp.LayerStart = datalink_LayerStart;
+ dl->fsmp.LayerUp = datalink_LayerUp;
+ dl->fsmp.LayerDown = datalink_LayerDown;
+ dl->fsmp.LayerFinish = datalink_LayerFinish;
+ dl->fsmp.object = dl;
+
+ if ((dl->physical = physical_Create(dl, type)) == NULL) {
+ free(dl->name);
+ free(dl);
+ return NULL;
+ }
+
+ pap_Init(&dl->pap, dl->physical);
+ chap_Init(&dl->chap, dl->physical);
+ cbcp_Init(&dl->cbcp, dl->physical);
+
+ memset(&dl->chat, '\0', sizeof dl->chat); /* Force buf{start,end} reset */
+ chat_Init(&dl->chat, dl->physical);
+
+ log_Printf(LogPHASE, "%s: Created in %s state\n",
+ dl->name, datalink_State(dl));
+
+ return dl;
+}
+
+struct datalink *
+datalink_Clone(struct datalink *odl, const char *name)
+{
+ struct datalink *dl;
+
+ dl = (struct datalink *)malloc(sizeof(struct datalink));
+ if (dl == NULL)
+ return dl;
+
+ dl->desc.type = DATALINK_DESCRIPTOR;
+ dl->desc.UpdateSet = datalink_UpdateSet;
+ dl->desc.IsSet = datalink_IsSet;
+ dl->desc.Read = datalink_Read;
+ dl->desc.Write = datalink_Write;
+
+ dl->state = DATALINK_CLOSED;
+
+ memcpy(&dl->cfg, &odl->cfg, sizeof dl->cfg);
+ mp_linkInit(&dl->mp);
+ *dl->phone.list = '\0';
+ dl->phone.next = NULL;
+ dl->phone.alt = NULL;
+ dl->phone.chosen = "N/A";
+ dl->bundle = odl->bundle;
+ dl->next = NULL;
+ memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
+ dl->dial.tries = 0;
+ dl->reconnect_tries = 0;
+ dl->name = strdup(name);
+ peerid_Init(&dl->peer);
+ dl->parent = odl->parent;
+ memcpy(&dl->fsmp, &odl->fsmp, sizeof dl->fsmp);
+ dl->fsmp.object = dl;
+
+ if ((dl->physical = physical_Create(dl, PHYS_INTERACTIVE)) == NULL) {
+ free(dl->name);
+ free(dl);
+ return NULL;
+ }
+ pap_Init(&dl->pap, dl->physical);
+ dl->pap.cfg = odl->pap.cfg;
+
+ chap_Init(&dl->chap, dl->physical);
+ dl->chap.auth.cfg = odl->chap.auth.cfg;
+
+ memcpy(&dl->physical->cfg, &odl->physical->cfg, sizeof dl->physical->cfg);
+ memcpy(&dl->physical->link.lcp.cfg, &odl->physical->link.lcp.cfg,
+ sizeof dl->physical->link.lcp.cfg);
+ memcpy(&dl->physical->link.ccp.cfg, &odl->physical->link.ccp.cfg,
+ sizeof dl->physical->link.ccp.cfg);
+ memcpy(&dl->physical->async.cfg, &odl->physical->async.cfg,
+ sizeof dl->physical->async.cfg);
+
+ cbcp_Init(&dl->cbcp, dl->physical);
+
+ memset(&dl->chat, '\0', sizeof dl->chat); /* Force buf{start,end} reset */
+ chat_Init(&dl->chat, dl->physical);
+
+ log_Printf(LogPHASE, "%s: Cloned in %s state\n",
+ dl->name, datalink_State(dl));
+
+ return dl;
+}
+
+struct datalink *
+datalink_Destroy(struct datalink *dl)
+{
+ struct datalink *result;
+
+ if (dl->state != DATALINK_CLOSED) {
+ log_Printf(LogERROR, "Oops, destroying a datalink in state %s\n",
+ datalink_State(dl));
+ switch (dl->state) {
+ case DATALINK_HANGUP:
+ case DATALINK_DIAL:
+ case DATALINK_LOGIN:
+ chat_Finish(&dl->chat); /* Gotta blat the timers ! */
+ break;
+ }
+ }
+
+ chat_Destroy(&dl->chat);
+ timer_Stop(&dl->dial.timer);
+ result = dl->next;
+ physical_Destroy(dl->physical);
+ free(dl->name);
+ free(dl);
+
+ return result;
+}
+
+void
+datalink_Up(struct datalink *dl, int runscripts, int packetmode)
+{
+ if (!Enabled(dl->bundle, OPT_FORCE_SCRIPTS) &&
+ (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED)))
+ /* Ignore scripts */
+ runscripts = 0;
+
+ switch (dl->state) {
+ case DATALINK_CLOSED:
+ if (bundle_Phase(dl->bundle) == PHASE_DEAD ||
+ bundle_Phase(dl->bundle) == PHASE_TERMINATE)
+ bundle_NewPhase(dl->bundle, PHASE_ESTABLISH);
+ datalink_NewState(dl, DATALINK_OPENING);
+ dl->reconnect_tries =
+ dl->physical->type == PHYS_DIRECT ? 0 : dl->cfg.reconnect.max;
+ dl->dial.tries = dl->cfg.dial.max;
+ dl->script.run = runscripts;
+ dl->script.packetmode = packetmode;
+ break;
+
+ case DATALINK_OPENING:
+ if (!dl->script.run && runscripts)
+ dl->script.run = 1;
+ /* FALLTHROUGH */
+
+ case DATALINK_DIAL:
+ case DATALINK_LOGIN:
+ case DATALINK_READY:
+ if (!dl->script.packetmode && packetmode) {
+ dl->script.packetmode = 1;
+ if (dl->state == DATALINK_READY) {
+ dl->script.run = 0;
+ datalink_NewState(dl, DATALINK_CARRIER);
+ }
+ }
+ break;
+ }
+}
+
+void
+datalink_Close(struct datalink *dl, int how)
+{
+ /* Please close */
+ switch (dl->state) {
+ case DATALINK_OPEN:
+ peerid_Init(&dl->peer);
+ fsm2initial(&dl->physical->link.ccp.fsm);
+ /* FALLTHROUGH */
+
+ case DATALINK_CBCP:
+ case DATALINK_AUTH:
+ case DATALINK_LCP:
+ datalink_AuthReInit(dl);
+ if (how == CLOSE_LCP)
+ datalink_DontHangup(dl);
+ else if (how == CLOSE_STAYDOWN)
+ datalink_StayDown(dl);
+ fsm_Close(&dl->physical->link.lcp.fsm);
+ break;
+
+ default:
+ datalink_ComeDown(dl, how);
+ }
+}
+
+void
+datalink_Down(struct datalink *dl, int how)
+{
+ /* Carrier is lost */
+ switch (dl->state) {
+ case DATALINK_OPEN:
+ peerid_Init(&dl->peer);
+ fsm2initial(&dl->physical->link.ccp.fsm);
+ /* FALLTHROUGH */
+
+ case DATALINK_CBCP:
+ case DATALINK_AUTH:
+ case DATALINK_LCP:
+ fsm2initial(&dl->physical->link.lcp.fsm);
+ if (dl->state == DATALINK_OPENING)
+ return; /* we're doing a callback... */
+ /* FALLTHROUGH */
+
+ default:
+ datalink_ComeDown(dl, how);
+ }
+}
+
+void
+datalink_StayDown(struct datalink *dl)
+{
+ dl->dial.tries = -1;
+ dl->reconnect_tries = 0;
+ dl->stayonline = 0;
+}
+
+void
+datalink_DontHangup(struct datalink *dl)
+{
+ dl->dial.tries = -1;
+ dl->reconnect_tries = 0;
+ dl->stayonline = dl->state >= DATALINK_LCP ? 1 : 0;
+}
+
+int
+datalink_Show(struct cmdargs const *arg)
+{
+ prompt_Printf(arg->prompt, "Name: %s\n", arg->cx->name);
+ prompt_Printf(arg->prompt, " State: %s\n",
+ datalink_State(arg->cx));
+ prompt_Printf(arg->prompt, " Peer name: ");
+ if (*arg->cx->peer.authname)
+ prompt_Printf(arg->prompt, "%s\n", arg->cx->peer.authname);
+ else if (arg->cx->state == DATALINK_OPEN)
+ prompt_Printf(arg->prompt, "None requested\n");
+ else
+ prompt_Printf(arg->prompt, "N/A\n");
+ prompt_Printf(arg->prompt, " Discriminator: %s\n",
+ mp_Enddisc(arg->cx->peer.enddisc.class,
+ arg->cx->peer.enddisc.address,
+ arg->cx->peer.enddisc.len));
+
+ prompt_Printf(arg->prompt, "\nDefaults:\n");
+ prompt_Printf(arg->prompt, " Phone List: %s\n",
+ arg->cx->cfg.phone.list);
+ if (arg->cx->cfg.dial.max)
+ prompt_Printf(arg->prompt, " Dial tries: %d, delay ",
+ arg->cx->cfg.dial.max);
+ else
+ prompt_Printf(arg->prompt, " Dial tries: infinite, delay ");
+ if (arg->cx->cfg.dial.next_timeout >= 0)
+ prompt_Printf(arg->prompt, "%ds/", arg->cx->cfg.dial.next_timeout);
+ else
+ prompt_Printf(arg->prompt, "random/");
+ if (arg->cx->cfg.dial.timeout >= 0)
+ prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.dial.timeout);
+ else
+ prompt_Printf(arg->prompt, "random\n");
+ prompt_Printf(arg->prompt, " Reconnect tries: %d, delay ",
+ arg->cx->cfg.reconnect.max);
+ if (arg->cx->cfg.reconnect.timeout > 0)
+ prompt_Printf(arg->prompt, "%ds\n", arg->cx->cfg.reconnect.timeout);
+ else
+ prompt_Printf(arg->prompt, "random\n");
+ prompt_Printf(arg->prompt, " Callback %s ", arg->cx->physical->type ==
+ PHYS_DIRECT ? "accepted: " : "requested:");
+ if (!arg->cx->cfg.callback.opmask)
+ prompt_Printf(arg->prompt, "none\n");
+ else {
+ int comma = 0;
+
+ if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_NONE)) {
+ prompt_Printf(arg->prompt, "none");
+ comma = 1;
+ }
+ if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) {
+ prompt_Printf(arg->prompt, "%sauth", comma ? ", " : "");
+ comma = 1;
+ }
+ if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_E164)) {
+ prompt_Printf(arg->prompt, "%sE.164", comma ? ", " : "");
+ if (arg->cx->physical->type != PHYS_DIRECT)
+ prompt_Printf(arg->prompt, " (%s)", arg->cx->cfg.callback.msg);
+ comma = 1;
+ }
+ if (arg->cx->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_CBCP)) {
+ prompt_Printf(arg->prompt, "%scbcp\n", comma ? ", " : "");
+ prompt_Printf(arg->prompt, " CBCP: delay: %ds\n",
+ arg->cx->cfg.cbcp.delay);
+ prompt_Printf(arg->prompt, " phone: ");
+ if (!strcmp(arg->cx->cfg.cbcp.phone, "*")) {
+ if (arg->cx->physical->type & PHYS_DIRECT)
+ prompt_Printf(arg->prompt, "Caller decides\n");
+ else
+ prompt_Printf(arg->prompt, "Dialback server decides\n");
+ } else
+ prompt_Printf(arg->prompt, "%s\n", arg->cx->cfg.cbcp.phone);
+ prompt_Printf(arg->prompt, " timeout: %lds\n",
+ arg->cx->cfg.cbcp.fsmretry);
+ } else
+ prompt_Printf(arg->prompt, "\n");
+ }
+
+ prompt_Printf(arg->prompt, " Dial Script: %s\n",
+ arg->cx->cfg.script.dial);
+ prompt_Printf(arg->prompt, " Login Script: %s\n",
+ arg->cx->cfg.script.login);
+ prompt_Printf(arg->prompt, " Logout Script: %s\n",
+ arg->cx->cfg.script.logout);
+ prompt_Printf(arg->prompt, " Hangup Script: %s\n",
+ arg->cx->cfg.script.hangup);
+ return 0;
+}
+
+int
+datalink_SetReconnect(struct cmdargs const *arg)
+{
+ if (arg->argc == arg->argn+2) {
+ arg->cx->cfg.reconnect.timeout = atoi(arg->argv[arg->argn]);
+ arg->cx->cfg.reconnect.max = atoi(arg->argv[arg->argn+1]);
+ return 0;
+ }
+ return -1;
+}
+
+int
+datalink_SetRedial(struct cmdargs const *arg)
+{
+ const char *sep, *osep;
+ int timeout, inc, maxinc, tries;
+
+ if (arg->argc == arg->argn+1 || arg->argc == arg->argn+2) {
+ if (strncasecmp(arg->argv[arg->argn], "random", 6) == 0 &&
+ (arg->argv[arg->argn][6] == '\0' || arg->argv[arg->argn][6] == '.')) {
+ arg->cx->cfg.dial.timeout = -1;
+ randinit();
+ } else {
+ timeout = atoi(arg->argv[arg->argn]);
+
+ if (timeout >= 0)
+ arg->cx->cfg.dial.timeout = timeout;
+ else {
+ log_Printf(LogWARN, "Invalid redial timeout\n");
+ return -1;
+ }
+ }
+
+ sep = strchr(arg->argv[arg->argn], '+');
+ if (sep) {
+ inc = atoi(++sep);
+ osep = sep;
+ if (inc >= 0)
+ arg->cx->cfg.dial.inc = inc;
+ else {
+ log_Printf(LogWARN, "Invalid timeout increment\n");
+ return -1;
+ }
+ sep = strchr(sep, '-');
+ if (sep) {
+ maxinc = atoi(++sep);
+ if (maxinc >= 0)
+ arg->cx->cfg.dial.maxinc = maxinc;
+ else {
+ log_Printf(LogWARN, "Invalid maximum timeout increments\n");
+ return -1;
+ }
+ } else {
+ /* Default timeout increment */
+ arg->cx->cfg.dial.maxinc = 10;
+ sep = osep;
+ }
+ } else {
+ /* Default timeout increment & max increment */
+ arg->cx->cfg.dial.inc = 0;
+ arg->cx->cfg.dial.maxinc = 10;
+ sep = arg->argv[arg->argn];
+ }
+
+ sep = strchr(sep, '.');
+ if (sep) {
+ if (strcasecmp(++sep, "random") == 0) {
+ arg->cx->cfg.dial.next_timeout = -1;
+ randinit();
+ } else {
+ timeout = atoi(sep);
+ if (timeout >= 0)
+ arg->cx->cfg.dial.next_timeout = timeout;
+ else {
+ log_Printf(LogWARN, "Invalid next redial timeout\n");
+ return -1;
+ }
+ }
+ } else
+ /* Default next timeout */
+ arg->cx->cfg.dial.next_timeout = DIAL_NEXT_TIMEOUT;
+
+ if (arg->argc == arg->argn+2) {
+ tries = atoi(arg->argv[arg->argn+1]);
+
+ if (tries >= 0) {
+ arg->cx->cfg.dial.max = tries;
+ } else {
+ log_Printf(LogWARN, "Invalid retry value\n");
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ return -1;
+}
+
+static const char * const states[] = {
+ "closed",
+ "opening",
+ "hangup",
+ "dial",
+ "carrier",
+ "logout",
+ "login",
+ "ready",
+ "lcp",
+ "auth",
+ "cbcp",
+ "open"
+};
+
+const char *
+datalink_State(struct datalink *dl)
+{
+ if (dl->state >= sizeof states / sizeof states[0])
+ return "unknown";
+ return states[dl->state];
+}
+
+static void
+datalink_NewState(struct datalink *dl, unsigned state)
+{
+ if (state != dl->state) {
+ if (state < sizeof states / sizeof states[0]) {
+ log_Printf(LogPHASE, "%s: %s -> %s\n", dl->name, datalink_State(dl),
+ states[state]);
+ dl->state = state;
+ } else
+ log_Printf(LogERROR, "%s: Can't enter state %d !\n", dl->name, state);
+ }
+}
+
+struct datalink *
+iov2datalink(struct bundle *bundle, struct iovec *iov, int *niov, int maxiov,
+ int fd, int *auxfd, int *nauxfd)
+{
+ struct datalink *dl, *cdl;
+ struct fsm_retry copy;
+ char *oname, *pname;
+
+ dl = (struct datalink *)iov[(*niov)++].iov_base;
+ dl->name = iov[*niov].iov_base;
+
+ if (dl->name[DATALINK_MAXNAME-1]) {
+ dl->name[DATALINK_MAXNAME-1] = '\0';
+ if (strlen(dl->name) == DATALINK_MAXNAME - 1)
+ log_Printf(LogWARN, "Datalink name truncated to \"%s\"\n", dl->name);
+ }
+
+ /* Make sure the name is unique ! */
+ oname = NULL;
+ do {
+ for (cdl = bundle->links; cdl; cdl = cdl->next)
+ if (!strcasecmp(dl->name, cdl->name)) {
+ if ((pname = datalink_NextName(dl)) == NULL) {
+ for ((*niov)--; *niov < maxiov; (*niov)++)
+ free(iov[*niov].iov_base);
+ return NULL;
+ } else if (oname)
+ free(pname);
+ else
+ oname = pname;
+ break; /* Keep renaming 'till we have no conflicts */
+ }
+ } while (cdl);
+
+ if (oname) {
+ log_Printf(LogPHASE, "Rename link %s to %s\n", oname, dl->name);
+ free(oname);
+ } else {
+ dl->name = strdup(dl->name);
+ free(iov[*niov].iov_base);
+ }
+ (*niov)++;
+
+ dl->desc.type = DATALINK_DESCRIPTOR;
+ dl->desc.UpdateSet = datalink_UpdateSet;
+ dl->desc.IsSet = datalink_IsSet;
+ dl->desc.Read = datalink_Read;
+ dl->desc.Write = datalink_Write;
+
+ mp_linkInit(&dl->mp);
+ *dl->phone.list = '\0';
+ dl->phone.next = NULL;
+ dl->phone.alt = NULL;
+ dl->phone.chosen = "N/A";
+
+ dl->bundle = bundle;
+ dl->next = NULL;
+ memset(&dl->dial.timer, '\0', sizeof dl->dial.timer);
+ dl->dial.tries = 0;
+ dl->reconnect_tries = 0;
+ dl->parent = &bundle->fsm;
+ dl->fsmp.LayerStart = datalink_LayerStart;
+ dl->fsmp.LayerUp = datalink_LayerUp;
+ dl->fsmp.LayerDown = datalink_LayerDown;
+ dl->fsmp.LayerFinish = datalink_LayerFinish;
+ dl->fsmp.object = dl;
+
+ dl->physical = iov2physical(dl, iov, niov, maxiov, fd, auxfd, nauxfd);
+
+ if (!dl->physical) {
+ free(dl->name);
+ free(dl);
+ dl = NULL;
+ } else {
+ copy = dl->pap.cfg.fsm;
+ pap_Init(&dl->pap, dl->physical);
+ dl->pap.cfg.fsm = copy;
+
+ copy = dl->chap.auth.cfg.fsm;
+ chap_Init(&dl->chap, dl->physical);
+ dl->chap.auth.cfg.fsm = copy;
+
+ cbcp_Init(&dl->cbcp, dl->physical);
+
+ memset(&dl->chat, '\0', sizeof dl->chat); /* Force buf{start,end} reset */
+ chat_Init(&dl->chat, dl->physical);
+
+ log_Printf(LogPHASE, "%s: Transferred in %s state\n",
+ dl->name, datalink_State(dl));
+ }
+
+ return dl;
+}
+
+int
+datalink2iov(struct datalink *dl, struct iovec *iov, int *niov, int maxiov,
+ int *auxfd, int *nauxfd)
+{
+ /* If `dl' is NULL, we're allocating before a Fromiov() */
+ int link_fd;
+
+ if (dl) {
+ timer_Stop(&dl->dial.timer);
+ /* The following is purely for the sake of paranoia */
+ cbcp_Down(&dl->cbcp);
+ timer_Stop(&dl->pap.authtimer);
+ timer_Stop(&dl->chap.auth.authtimer);
+ }
+
+ if (*niov >= maxiov - 1) {
+ log_Printf(LogERROR, "Toiov: No room for datalink !\n");
+ if (dl) {
+ free(dl->name);
+ free(dl);
+ }
+ return -1;
+ }
+
+ iov[*niov].iov_base = (void *)dl;
+ iov[(*niov)++].iov_len = sizeof *dl;
+ iov[*niov].iov_base = dl ? realloc(dl->name, DATALINK_MAXNAME) : NULL;
+ iov[(*niov)++].iov_len = DATALINK_MAXNAME;
+
+ link_fd = physical2iov(dl ? dl->physical : NULL, iov, niov, maxiov, auxfd,
+ nauxfd);
+
+ if (link_fd == -1 && dl) {
+ free(dl->name);
+ free(dl);
+ }
+
+ return link_fd;
+}
+
+void
+datalink_Rename(struct datalink *dl, const char *name)
+{
+ free(dl->name);
+ dl->physical->link.name = dl->name = strdup(name);
+}
+
+static char *
+datalink_NextName(struct datalink *dl)
+{
+ int f, n;
+ char *name, *oname;
+
+ n = strlen(dl->name);
+ if ((name = (char *)malloc(n+3)) == NULL) {
+ log_Printf(LogERROR, "datalink_NextName: Out of memory !\n");
+ return NULL;
+ }
+ for (f = n - 1; f >= 0; f--)
+ if (!isdigit(dl->name[f]))
+ break;
+ n = sprintf(name, "%.*s-", dl->name[f] == '-' ? f : f + 1, dl->name);
+ sprintf(name + n, "%d", atoi(dl->name + f + 1) + 1);
+ oname = dl->name;
+ dl->name = name;
+ /* our physical link name isn't updated (it probably isn't created yet) */
+ return oname;
+}
+
+int
+datalink_SetMode(struct datalink *dl, int mode)
+{
+ if (!physical_SetMode(dl->physical, mode))
+ return 0;
+ if (dl->physical->type & (PHYS_DIRECT|PHYS_DEDICATED))
+ dl->script.run = 0;
+ if (dl->physical->type == PHYS_DIRECT)
+ dl->reconnect_tries = 0;
+ if (mode & (PHYS_DDIAL|PHYS_BACKGROUND|PHYS_FOREGROUND) &&
+ dl->state <= DATALINK_READY)
+ datalink_Up(dl, 1, 1);
+ return 1;
+}
+
+int
+datalink_GetDialTimeout(struct datalink *dl)
+{
+ int result = dl->cfg.dial.timeout + dl->dial.incs * dl->cfg.dial.inc;
+
+ if (dl->dial.incs < dl->cfg.dial.maxinc)
+ dl->dial.incs++;
+
+ return result;
+}
diff --git a/usr.sbin/ppp/datalink.h b/usr.sbin/ppp/datalink.h
new file mode 100644
index 0000000..6fd7e9b
--- /dev/null
+++ b/usr.sbin/ppp/datalink.h
@@ -0,0 +1,156 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define DATALINK_CLOSED (0)
+#define DATALINK_OPENING (1)
+#define DATALINK_HANGUP (2)
+#define DATALINK_DIAL (3)
+#define DATALINK_CARRIER (4)
+#define DATALINK_LOGOUT (5)
+#define DATALINK_LOGIN (6)
+#define DATALINK_READY (7)
+#define DATALINK_LCP (8)
+#define DATALINK_AUTH (9)
+#define DATALINK_CBCP (10)
+#define DATALINK_OPEN (11)
+
+#define DATALINK_MAXNAME (20) /* Maximum datalink::name length */
+
+/* How to close the link */
+#define CLOSE_NORMAL 0
+#define CLOSE_STAYDOWN 1
+#define CLOSE_LCP 2
+
+struct iovec;
+struct prompt;
+struct physical;
+struct bundle;
+
+struct datalink {
+ struct fdescriptor desc; /* We play either a physical or a chat */
+ unsigned state; /* Our DATALINK_* state */
+ struct physical *physical; /* Our link */
+
+ struct chat chat; /* For bringing the link up & down */
+
+ unsigned stayonline : 1; /* stay online when LCP is closed ? */
+ struct {
+ unsigned run : 1; /* run scripts ? */
+ unsigned packetmode : 1; /* Go into packet mode after login ? */
+ } script;
+
+ struct {
+ struct {
+ char dial[SCRIPT_LEN];
+ char login[SCRIPT_LEN];
+ char logout[SCRIPT_LEN];
+ char hangup[SCRIPT_LEN];
+ } script; /* various chat scripts */
+ struct {
+ char list[SCRIPT_LEN]; /* Telephone Numbers */
+ } phone;
+ struct {
+ int max; /* initially try again this number of times */
+ int next_timeout; /* Redial next timeout value */
+ int inc; /* Increment timeout by `inc' each time read */
+ int maxinc; /* Maximum number of increments */
+ int timeout; /* Redial timeout value (end of phone list) */
+ } dial;
+ struct {
+ int max; /* initially try again this number of times */
+ int timeout; /* Timeout before reconnect on carrier loss */
+ } reconnect;
+ struct callback callback; /* Direction depends on physical type */
+ struct cbcpcfg cbcp; /* Direction depends on phys type & callback */
+ } cfg; /* All our config data is in here */
+
+ struct {
+ char list[SCRIPT_LEN]; /* copy of cfg.list for strsep() */
+ char *next; /* Next phone from the list */
+ char *alt; /* Alternate (after fail) phone from the list */
+ const char *chosen; /* Chosen phone number after DIAL */
+ } phone;
+
+ struct cbcp cbcp;
+
+ struct {
+ struct pppTimer timer; /* For timing between close & open */
+ int tries; /* currently try again this number of times */
+ int incs; /* # times our timeout has been incremented */
+ } dial;
+
+ unsigned reconnect_tries; /* currently try again this number of times */
+
+ char *name; /* Our name */
+
+ struct peerid peer; /* Peer identification */
+
+ struct fsm_parent fsmp; /* Our callback functions */
+ const struct fsm_parent *parent; /* Our parent */
+
+ struct authinfo pap; /* Authentication using pap */
+ struct chap chap; /* Authentication using chap */
+
+ struct mp_link mp; /* multilink data */
+
+ struct bundle *bundle; /* for the moment */
+ struct datalink *next; /* Next in the list */
+};
+
+#define descriptor2datalink(d) \
+ ((d)->type == DATALINK_DESCRIPTOR ? (struct datalink *)(d) : NULL)
+
+extern struct datalink *datalink_Create(const char *name, struct bundle *, int);
+extern struct datalink *datalink_Clone(struct datalink *, const char *);
+extern struct datalink *iov2datalink(struct bundle *, struct iovec *, int *,
+ int, int, int *, int *);
+extern int datalink2iov(struct datalink *, struct iovec *, int *, int, int *,
+ int *);
+extern struct datalink *datalink_Destroy(struct datalink *);
+extern void datalink_GotAuthname(struct datalink *, const char *);
+extern void datalink_Up(struct datalink *, int, int);
+extern void datalink_Close(struct datalink *, int);
+extern void datalink_Down(struct datalink *, int);
+extern void datalink_StayDown(struct datalink *);
+extern void datalink_DontHangup(struct datalink *);
+extern void datalink_AuthOk(struct datalink *);
+extern void datalink_AuthNotOk(struct datalink *);
+extern void datalink_NCPUp(struct datalink *);
+extern void datalink_CBCPComplete(struct datalink *);
+extern void datalink_CBCPFailed(struct datalink *);
+extern int datalink_Show(struct cmdargs const *);
+extern int datalink_SetRedial(struct cmdargs const *);
+extern int datalink_SetReconnect(struct cmdargs const *);
+extern const char *datalink_State(struct datalink *);
+extern void datalink_Rename(struct datalink *, const char *);
+extern int datalink_RemoveFromSet(struct datalink *, fd_set *, fd_set *,
+ fd_set *);
+extern int datalink_SetMode(struct datalink *, int);
+extern int datalink_GetDialTimeout(struct datalink *);
+extern const char *datalink_ChoosePhoneNumber(struct datalink *);
+extern void datalink_ComeDown(struct datalink *, int);
diff --git a/usr.sbin/ppp/deflate.c b/usr.sbin/ppp/deflate.c
new file mode 100644
index 0000000..57c9004
--- /dev/null
+++ b/usr.sbin/ppp/deflate.c
@@ -0,0 +1,601 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <zlib.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "ccp.h"
+#include "deflate.h"
+
+/* Our state */
+struct deflate_state {
+ u_short seqno;
+ int uncomp_rec;
+ int winsize;
+ z_stream cx;
+};
+
+static char garbage[10];
+static u_char EMPTY_BLOCK[4] = { 0x00, 0x00, 0xff, 0xff };
+
+#define DEFLATE_CHUNK_LEN (1536 - sizeof(struct mbuf))
+
+static int
+DeflateResetOutput(void *v)
+{
+ struct deflate_state *state = (struct deflate_state *)v;
+
+ state->seqno = 0;
+ state->uncomp_rec = 0;
+ deflateReset(&state->cx);
+ log_Printf(LogCCP, "Deflate: Output channel reset\n");
+
+ return 1; /* Ask FSM to ACK */
+}
+
+static struct mbuf *
+DeflateOutput(void *v, struct ccp *ccp, struct link *l __unused,
+ int pri __unused, u_short *proto, struct mbuf *mp)
+{
+ struct deflate_state *state = (struct deflate_state *)v;
+ u_char *wp, *rp;
+ int olen, ilen, len, res, flush;
+ struct mbuf *mo_head, *mo, *mi_head, *mi;
+
+ ilen = m_length(mp);
+ log_Printf(LogDEBUG, "DeflateOutput: Proto %02x (%d bytes)\n", *proto, ilen);
+ log_DumpBp(LogDEBUG, "DeflateOutput: Compress packet:", mp);
+
+ /* Stuff the protocol in front of the input */
+ mi_head = mi = m_get(2, MB_CCPOUT);
+ mi->m_next = mp;
+ rp = MBUF_CTOP(mi);
+ if (*proto < 0x100) { /* Compress the protocol */
+ rp[0] = *proto & 0377;
+ mi->m_len = 1;
+ } else { /* Don't compress the protocol */
+ rp[0] = *proto >> 8;
+ rp[1] = *proto & 0377;
+ mi->m_len = 2;
+ }
+
+ /* Allocate the initial output mbuf */
+ mo_head = mo = m_get(DEFLATE_CHUNK_LEN, MB_CCPOUT);
+ mo->m_len = 2;
+ wp = MBUF_CTOP(mo);
+ *wp++ = state->seqno >> 8;
+ *wp++ = state->seqno & 0377;
+ log_Printf(LogDEBUG, "DeflateOutput: Seq %d\n", state->seqno);
+ state->seqno++;
+
+ /* Set up the deflation context */
+ state->cx.next_out = wp;
+ state->cx.avail_out = DEFLATE_CHUNK_LEN - 2;
+ state->cx.next_in = MBUF_CTOP(mi);
+ state->cx.avail_in = mi->m_len;
+ flush = Z_NO_FLUSH;
+
+ olen = 0;
+ while (1) {
+ if ((res = deflate(&state->cx, flush)) != Z_OK) {
+ if (res == Z_STREAM_END)
+ break; /* Done */
+ log_Printf(LogWARN, "DeflateOutput: deflate returned %d (%s)\n",
+ res, state->cx.msg ? state->cx.msg : "");
+ m_freem(mo_head);
+ m_free(mi_head);
+ state->seqno--;
+ return mp; /* Our dictionary's probably dead now :-( */
+ }
+
+ if (flush == Z_SYNC_FLUSH && state->cx.avail_out != 0)
+ break;
+
+ if (state->cx.avail_in == 0 && mi->m_next != NULL) {
+ mi = mi->m_next;
+ state->cx.next_in = MBUF_CTOP(mi);
+ state->cx.avail_in = mi->m_len;
+ if (mi->m_next == NULL)
+ flush = Z_SYNC_FLUSH;
+ }
+
+ if (state->cx.avail_out == 0) {
+ mo->m_next = m_get(DEFLATE_CHUNK_LEN, MB_CCPOUT);
+ olen += (mo->m_len = DEFLATE_CHUNK_LEN);
+ mo = mo->m_next;
+ mo->m_len = 0;
+ state->cx.next_out = MBUF_CTOP(mo);
+ state->cx.avail_out = DEFLATE_CHUNK_LEN;
+ }
+ }
+
+ olen += (mo->m_len = DEFLATE_CHUNK_LEN - state->cx.avail_out);
+ olen -= 4; /* exclude the trailing EMPTY_BLOCK */
+
+ /*
+ * If the output packet (including seqno and excluding the EMPTY_BLOCK)
+ * got bigger, send the original.
+ */
+ if (olen >= ilen) {
+ m_freem(mo_head);
+ m_free(mi_head);
+ log_Printf(LogDEBUG, "DeflateOutput: %d => %d: Uncompressible (0x%04x)\n",
+ ilen, olen, *proto);
+ ccp->uncompout += ilen;
+ ccp->compout += ilen; /* We measure this stuff too */
+ return mp;
+ }
+
+ m_freem(mi_head);
+
+ /*
+ * Lose the last four bytes of our output.
+ * XXX: We should probably assert that these are the same as the
+ * contents of EMPTY_BLOCK.
+ */
+ mo = mo_head;
+ for (len = mo->m_len; len < olen; mo = mo->m_next, len += mo->m_len)
+ ;
+ mo->m_len -= len - olen;
+ if (mo->m_next != NULL) {
+ m_freem(mo->m_next);
+ mo->m_next = NULL;
+ }
+
+ ccp->uncompout += ilen;
+ ccp->compout += olen;
+
+ log_Printf(LogDEBUG, "DeflateOutput: %d => %d bytes, proto 0x%04x\n",
+ ilen, olen, *proto);
+
+ *proto = ccp_Proto(ccp);
+ return mo_head;
+}
+
+static void
+DeflateResetInput(void *v)
+{
+ struct deflate_state *state = (struct deflate_state *)v;
+
+ state->seqno = 0;
+ state->uncomp_rec = 0;
+ inflateReset(&state->cx);
+ log_Printf(LogCCP, "Deflate: Input channel reset\n");
+}
+
+static struct mbuf *
+DeflateInput(void *v, struct ccp *ccp, u_short *proto, struct mbuf *mi)
+{
+ struct deflate_state *state = (struct deflate_state *)v;
+ struct mbuf *mo, *mo_head, *mi_head;
+ u_char *wp;
+ int ilen, olen;
+ int seq, flush, res, first;
+ u_char hdr[2];
+
+ log_DumpBp(LogDEBUG, "DeflateInput: Decompress packet:", mi);
+ mi_head = mi = mbuf_Read(mi, hdr, 2);
+ ilen = 2;
+
+ /* Check the sequence number. */
+ seq = (hdr[0] << 8) + hdr[1];
+ log_Printf(LogDEBUG, "DeflateInput: Seq %d\n", seq);
+ if (seq != state->seqno) {
+ if (seq <= state->uncomp_rec)
+ /*
+ * So the peer's started at zero again - fine ! If we're wrong,
+ * inflate() will fail. This is better than getting into a loop
+ * trying to get a ResetReq to a busy sender.
+ */
+ state->seqno = seq;
+ else {
+ log_Printf(LogCCP, "DeflateInput: Seq error: Got %d, expected %d\n",
+ seq, state->seqno);
+ m_freem(mi_head);
+ ccp_SendResetReq(&ccp->fsm);
+ return NULL;
+ }
+ }
+ state->seqno++;
+ state->uncomp_rec = 0;
+
+ /* Allocate an output mbuf */
+ mo_head = mo = m_get(DEFLATE_CHUNK_LEN, MB_CCPIN);
+
+ /* Our proto starts with 0 if it's compressed */
+ wp = MBUF_CTOP(mo);
+ wp[0] = '\0';
+
+ /*
+ * We set avail_out to 1 initially so we can look at the first
+ * byte of the output and decide whether we have a compressed
+ * proto field.
+ */
+ state->cx.next_in = MBUF_CTOP(mi);
+ state->cx.avail_in = mi->m_len;
+ state->cx.next_out = wp + 1;
+ state->cx.avail_out = 1;
+ ilen += mi->m_len;
+
+ flush = mi->m_next ? Z_NO_FLUSH : Z_SYNC_FLUSH;
+ first = 1;
+ olen = 0;
+
+ while (1) {
+ if ((res = inflate(&state->cx, flush)) != Z_OK) {
+ if (res == Z_STREAM_END)
+ break; /* Done */
+ log_Printf(LogCCP, "DeflateInput: inflate returned %d (%s)\n",
+ res, state->cx.msg ? state->cx.msg : "");
+ m_freem(mo_head);
+ m_freem(mi);
+ ccp_SendResetReq(&ccp->fsm);
+ return NULL;
+ }
+
+ if (flush == Z_SYNC_FLUSH && state->cx.avail_out != 0)
+ break;
+
+ if (state->cx.avail_in == 0 && mi && (mi = m_free(mi)) != NULL) {
+ /* underflow */
+ state->cx.next_in = MBUF_CTOP(mi);
+ ilen += (state->cx.avail_in = mi->m_len);
+ if (mi->m_next == NULL)
+ flush = Z_SYNC_FLUSH;
+ }
+
+ if (state->cx.avail_out == 0) {
+ /* overflow */
+ if (first) {
+ if (!(wp[1] & 1)) {
+ /* 2 byte proto, shuffle it back in output */
+ wp[0] = wp[1];
+ state->cx.next_out--;
+ state->cx.avail_out = DEFLATE_CHUNK_LEN-1;
+ } else
+ state->cx.avail_out = DEFLATE_CHUNK_LEN-2;
+ first = 0;
+ } else {
+ olen += (mo->m_len = DEFLATE_CHUNK_LEN);
+ mo->m_next = m_get(DEFLATE_CHUNK_LEN, MB_CCPIN);
+ mo = mo->m_next;
+ state->cx.next_out = MBUF_CTOP(mo);
+ state->cx.avail_out = DEFLATE_CHUNK_LEN;
+ }
+ }
+ }
+
+ if (mi != NULL)
+ m_freem(mi);
+
+ if (first) {
+ log_Printf(LogCCP, "DeflateInput: Length error\n");
+ m_freem(mo_head);
+ ccp_SendResetReq(&ccp->fsm);
+ return NULL;
+ }
+
+ olen += (mo->m_len = DEFLATE_CHUNK_LEN - state->cx.avail_out);
+
+ *proto = ((u_short)wp[0] << 8) | wp[1];
+ mo_head->m_offset += 2;
+ mo_head->m_len -= 2;
+ olen -= 2;
+
+ ccp->compin += ilen;
+ ccp->uncompin += olen;
+
+ log_Printf(LogDEBUG, "DeflateInput: %d => %d bytes, proto 0x%04x\n",
+ ilen, olen, *proto);
+
+ /*
+ * Simulate an EMPTY_BLOCK so that our dictionary stays in sync.
+ * The peer will have silently removed this!
+ */
+ state->cx.next_out = garbage;
+ state->cx.avail_out = sizeof garbage;
+ state->cx.next_in = EMPTY_BLOCK;
+ state->cx.avail_in = sizeof EMPTY_BLOCK;
+ inflate(&state->cx, Z_SYNC_FLUSH);
+
+ return mo_head;
+}
+
+static void
+DeflateDictSetup(void *v, struct ccp *ccp, u_short proto, struct mbuf *mi)
+{
+ struct deflate_state *state = (struct deflate_state *)v;
+ int res, flush, expect_error;
+ u_char *rp;
+ struct mbuf *mi_head;
+ short len;
+
+ log_Printf(LogDEBUG, "DeflateDictSetup: Got seq %d\n", state->seqno);
+
+ /*
+ * Stuff an ``uncompressed data'' block header followed by the
+ * protocol in front of the input
+ */
+ mi_head = m_get(7, MB_CCPOUT);
+ mi_head->m_next = mi;
+ len = m_length(mi);
+ mi = mi_head;
+ rp = MBUF_CTOP(mi);
+ if (proto < 0x100) { /* Compress the protocol */
+ rp[5] = proto & 0377;
+ mi->m_len = 6;
+ len++;
+ } else { /* Don't compress the protocol */
+ rp[5] = proto >> 8;
+ rp[6] = proto & 0377;
+ mi->m_len = 7;
+ len += 2;
+ }
+ rp[0] = 0x80; /* BITS: 100xxxxx */
+ rp[1] = len & 0377; /* The length */
+ rp[2] = len >> 8;
+ rp[3] = (~len) & 0377; /* One's compliment of the length */
+ rp[4] = (~len) >> 8;
+
+ state->cx.next_in = rp;
+ state->cx.avail_in = mi->m_len;
+ state->cx.next_out = garbage;
+ state->cx.avail_out = sizeof garbage;
+ flush = Z_NO_FLUSH;
+ expect_error = 0;
+
+ while (1) {
+ if ((res = inflate(&state->cx, flush)) != Z_OK) {
+ if (res == Z_STREAM_END)
+ break; /* Done */
+ if (expect_error && res == Z_BUF_ERROR)
+ break;
+ log_Printf(LogCCP, "DeflateDictSetup: inflate returned %d (%s)\n",
+ res, state->cx.msg ? state->cx.msg : "");
+ log_Printf(LogCCP, "DeflateDictSetup: avail_in %d, avail_out %d\n",
+ state->cx.avail_in, state->cx.avail_out);
+ ccp_SendResetReq(&ccp->fsm);
+ m_free(mi_head); /* lose our allocated ``head'' buf */
+ return;
+ }
+
+ if (flush == Z_SYNC_FLUSH && state->cx.avail_out != 0)
+ break;
+
+ if (state->cx.avail_in == 0 && mi && (mi = mi->m_next) != NULL) {
+ /* underflow */
+ state->cx.next_in = MBUF_CTOP(mi);
+ state->cx.avail_in = mi->m_len;
+ if (mi->m_next == NULL)
+ flush = Z_SYNC_FLUSH;
+ }
+
+ if (state->cx.avail_out == 0) {
+ if (state->cx.avail_in == 0)
+ /*
+ * This seems to be a bug in libz ! If inflate() finished
+ * with 0 avail_in and 0 avail_out *and* this is the end of
+ * our input *and* inflate() *has* actually written all the
+ * output it's going to, it *doesn't* return Z_STREAM_END !
+ * When we subsequently call it with no more input, it gives
+ * us Z_BUF_ERROR :-( It seems pretty safe to ignore this
+ * error (the dictionary seems to stay in sync). In the worst
+ * case, we'll drop the next compressed packet and do a
+ * CcpReset() then.
+ */
+ expect_error = 1;
+ /* overflow */
+ state->cx.next_out = garbage;
+ state->cx.avail_out = sizeof garbage;
+ }
+ }
+
+ ccp->compin += len;
+ ccp->uncompin += len;
+
+ state->seqno++;
+ state->uncomp_rec++;
+ m_free(mi_head); /* lose our allocated ``head'' buf */
+}
+
+static const char *
+DeflateDispOpts(struct fsm_opt *o)
+{
+ static char disp[7]; /* Must be used immediately */
+
+ sprintf(disp, "win %d", (o->data[0]>>4) + 8);
+ return disp;
+}
+
+static void
+DeflateInitOptsOutput(struct bundle *bundle __unused, struct fsm_opt *o,
+ const struct ccp_config *cfg)
+{
+ o->hdr.len = 4;
+ o->data[0] = ((cfg->deflate.out.winsize - 8) << 4) + 8;
+ o->data[1] = '\0';
+}
+
+static int
+DeflateSetOptsOutput(struct bundle *bundle __unused, struct fsm_opt *o,
+ const struct ccp_config *cfg __unused)
+{
+ if (o->hdr.len != 4 || (o->data[0] & 15) != 8 || o->data[1] != '\0')
+ return MODE_REJ;
+
+ if ((o->data[0] >> 4) + 8 > 15) {
+ o->data[0] = ((15 - 8) << 4) + 8;
+ return MODE_NAK;
+ }
+
+ return MODE_ACK;
+}
+
+static int
+DeflateSetOptsInput(struct bundle *bundle __unused, struct fsm_opt *o,
+ const struct ccp_config *cfg)
+{
+ int want;
+
+ if (o->hdr.len != 4 || (o->data[0] & 15) != 8 || o->data[1] != '\0')
+ return MODE_REJ;
+
+ want = (o->data[0] >> 4) + 8;
+ if (cfg->deflate.in.winsize == 0) {
+ if (want < 8 || want > 15) {
+ o->data[0] = ((15 - 8) << 4) + 8;
+ }
+ } else if (want != cfg->deflate.in.winsize) {
+ o->data[0] = ((cfg->deflate.in.winsize - 8) << 4) + 8;
+ return MODE_NAK;
+ }
+
+ return MODE_ACK;
+}
+
+static void *
+DeflateInitInput(struct bundle *bundle __unused, struct fsm_opt *o)
+{
+ struct deflate_state *state;
+
+ state = (struct deflate_state *)malloc(sizeof(struct deflate_state));
+ if (state != NULL) {
+ state->winsize = (o->data[0] >> 4) + 8;
+ state->cx.zalloc = NULL;
+ state->cx.opaque = NULL;
+ state->cx.zfree = NULL;
+ state->cx.next_out = NULL;
+ if (inflateInit2(&state->cx, -state->winsize) == Z_OK)
+ DeflateResetInput(state);
+ else {
+ free(state);
+ state = NULL;
+ }
+ }
+
+ return state;
+}
+
+static void *
+DeflateInitOutput(struct bundle *bundle __unused, struct fsm_opt *o)
+{
+ struct deflate_state *state;
+
+ state = (struct deflate_state *)malloc(sizeof(struct deflate_state));
+ if (state != NULL) {
+ state->winsize = (o->data[0] >> 4) + 8;
+ state->cx.zalloc = NULL;
+ state->cx.opaque = NULL;
+ state->cx.zfree = NULL;
+ state->cx.next_in = NULL;
+ if (deflateInit2(&state->cx, Z_DEFAULT_COMPRESSION, 8,
+ -state->winsize, 8, Z_DEFAULT_STRATEGY) == Z_OK)
+ DeflateResetOutput(state);
+ else {
+ free(state);
+ state = NULL;
+ }
+ }
+
+ return state;
+}
+
+static void
+DeflateTermInput(void *v)
+{
+ struct deflate_state *state = (struct deflate_state *)v;
+
+ inflateEnd(&state->cx);
+ free(state);
+}
+
+static void
+DeflateTermOutput(void *v)
+{
+ struct deflate_state *state = (struct deflate_state *)v;
+
+ deflateEnd(&state->cx);
+ free(state);
+}
+
+const struct ccp_algorithm PppdDeflateAlgorithm = {
+ TY_PPPD_DEFLATE, /* Older versions of pppd expected this ``type'' */
+ CCP_NEG_DEFLATE24,
+ DeflateDispOpts,
+ ccp_DefaultUsable,
+ ccp_DefaultRequired,
+ {
+ DeflateSetOptsInput,
+ DeflateInitInput,
+ DeflateTermInput,
+ DeflateResetInput,
+ DeflateInput,
+ DeflateDictSetup
+ },
+ {
+ 0,
+ DeflateInitOptsOutput,
+ DeflateSetOptsOutput,
+ DeflateInitOutput,
+ DeflateTermOutput,
+ DeflateResetOutput,
+ DeflateOutput
+ },
+};
+
+const struct ccp_algorithm DeflateAlgorithm = {
+ TY_DEFLATE, /* rfc 1979 */
+ CCP_NEG_DEFLATE,
+ DeflateDispOpts,
+ ccp_DefaultUsable,
+ ccp_DefaultRequired,
+ {
+ DeflateSetOptsInput,
+ DeflateInitInput,
+ DeflateTermInput,
+ DeflateResetInput,
+ DeflateInput,
+ DeflateDictSetup
+ },
+ {
+ 0,
+ DeflateInitOptsOutput,
+ DeflateSetOptsOutput,
+ DeflateInitOutput,
+ DeflateTermOutput,
+ DeflateResetOutput,
+ DeflateOutput
+ },
+};
diff --git a/usr.sbin/ppp/deflate.h b/usr.sbin/ppp/deflate.h
new file mode 100644
index 0000000..a478b24
--- /dev/null
+++ b/usr.sbin/ppp/deflate.h
@@ -0,0 +1,30 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+extern const struct ccp_algorithm PppdDeflateAlgorithm;
+extern const struct ccp_algorithm DeflateAlgorithm;
diff --git a/usr.sbin/ppp/defs.c b/usr.sbin/ppp/defs.c
new file mode 100644
index 0000000..dcd9e47
--- /dev/null
+++ b/usr.sbin/ppp/defs.c
@@ -0,0 +1,450 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+
+#include <sys/param.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if defined(__FreeBSD__) && !defined(NOKLDLOAD)
+#include <sys/module.h>
+#endif
+#include <termios.h>
+#if !defined(__FreeBSD__) || __FreeBSD__ < 3
+#include <time.h>
+#endif
+#include <unistd.h>
+
+#if defined(__FreeBSD__) && !defined(NOKLDLOAD)
+#include "id.h"
+#include "log.h"
+#endif
+#include "defs.h"
+
+#define issep(c) ((c) == '\t' || (c) == ' ')
+
+#if defined(__NetBSD__) || __FreeBSD__ < 3
+void
+randinit()
+{
+#if defined(__FreeBSD__)
+ static int initdone; /* srandomdev() call is only required once */
+
+ if (!initdone) {
+ initdone = 1;
+ srandomdev();
+ }
+#else
+ srandom((time(NULL)^getpid())+random());
+#endif
+}
+#endif
+
+ssize_t
+fullread(int fd, void *v, size_t n)
+{
+ size_t got, total;
+
+ for (total = 0; total < n; total += got)
+ switch ((got = read(fd, (char *)v + total, n - total))) {
+ case 0:
+ return total;
+ case -1:
+ if (errno == EINTR)
+ got = 0;
+ else
+ return -1;
+ }
+ return total;
+}
+
+static struct {
+ int mode;
+ const char *name;
+} modes[] = {
+ { PHYS_INTERACTIVE, "interactive" },
+ { PHYS_AUTO, "auto" },
+ { PHYS_DIRECT, "direct" },
+ { PHYS_DEDICATED, "dedicated" },
+ { PHYS_DDIAL, "ddial" },
+ { PHYS_BACKGROUND, "background" },
+ { PHYS_FOREGROUND, "foreground" },
+ { PHYS_ALL, "*" },
+ { 0, 0 }
+};
+
+const char *
+mode2Nam(int mode)
+{
+ int m;
+
+ for (m = 0; modes[m].mode; m++)
+ if (modes[m].mode == mode)
+ return modes[m].name;
+
+ return "unknown";
+}
+
+int
+Nam2mode(const char *name)
+{
+ int m, got, len;
+
+ len = strlen(name);
+ got = -1;
+ for (m = 0; modes[m].mode; m++)
+ if (!strncasecmp(name, modes[m].name, len)) {
+ if (modes[m].name[len] == '\0')
+ return modes[m].mode;
+ if (got != -1)
+ return 0;
+ got = m;
+ }
+
+ return got == -1 ? 0 : modes[got].mode;
+}
+
+struct in_addr
+GetIpAddr(const char *cp)
+{
+ struct in_addr ipaddr;
+
+ if (!strcasecmp(cp, "default"))
+ ipaddr.s_addr = INADDR_ANY;
+ else if (inet_aton(cp, &ipaddr) == 0) {
+ const char *ptr;
+
+ /* Any illegal characters ? */
+ for (ptr = cp; *ptr != '\0'; ptr++)
+ if (!isalnum(*ptr) && strchr("-.", *ptr) == NULL)
+ break;
+
+ if (*ptr == '\0') {
+ struct hostent *hp;
+
+ hp = gethostbyname(cp);
+ if (hp && hp->h_addrtype == AF_INET)
+ memcpy(&ipaddr, hp->h_addr, hp->h_length);
+ else
+ ipaddr.s_addr = INADDR_NONE;
+ } else
+ ipaddr.s_addr = INADDR_NONE;
+ }
+
+ return ipaddr;
+}
+
+static const struct speeds {
+ unsigned nspeed;
+ speed_t speed;
+} speeds[] = {
+#ifdef B50
+ { 50, B50, },
+#endif
+#ifdef B75
+ { 75, B75, },
+#endif
+#ifdef B110
+ { 110, B110, },
+#endif
+#ifdef B134
+ { 134, B134, },
+#endif
+#ifdef B150
+ { 150, B150, },
+#endif
+#ifdef B200
+ { 200, B200, },
+#endif
+#ifdef B300
+ { 300, B300, },
+#endif
+#ifdef B600
+ { 600, B600, },
+#endif
+#ifdef B1200
+ { 1200, B1200, },
+#endif
+#ifdef B1800
+ { 1800, B1800, },
+#endif
+#ifdef B2400
+ { 2400, B2400, },
+#endif
+#ifdef B4800
+ { 4800, B4800, },
+#endif
+#ifdef B9600
+ { 9600, B9600, },
+#endif
+#ifdef B19200
+ { 19200, B19200, },
+#endif
+#ifdef B38400
+ { 38400, B38400, },
+#endif
+#ifndef _POSIX_SOURCE
+#ifdef B7200
+ { 7200, B7200, },
+#endif
+#ifdef B14400
+ { 14400, B14400, },
+#endif
+#ifdef B28800
+ { 28800, B28800, },
+#endif
+#ifdef B57600
+ { 57600, B57600, },
+#endif
+#ifdef B76800
+ { 76800, B76800, },
+#endif
+#ifdef B115200
+ { 115200, B115200, },
+#endif
+#ifdef B230400
+ { 230400, B230400, },
+#endif
+#ifdef B460800
+ { 460800, B460800, },
+#endif
+#ifdef B921600
+ { 921600, B921600, },
+#endif
+#ifdef EXTA
+ { 19200, EXTA, },
+#endif
+#ifdef EXTB
+ { 38400, EXTB, },
+#endif
+#endif /* _POSIX_SOURCE */
+ { 0, 0 }
+};
+
+unsigned
+SpeedToUnsigned(speed_t speed)
+{
+ const struct speeds *sp;
+
+ for (sp = speeds; sp->nspeed; sp++) {
+ if (sp->speed == speed) {
+ return sp->nspeed;
+ }
+ }
+ return 0;
+}
+
+speed_t
+UnsignedToSpeed(unsigned nspeed)
+{
+ const struct speeds *sp;
+
+ for (sp = speeds; sp->nspeed; sp++) {
+ if (sp->nspeed == nspeed) {
+ return sp->speed;
+ }
+ }
+ return B0;
+}
+
+char *
+findblank(char *p, int flags)
+{
+ int instring;
+
+ instring = 0;
+ while (*p) {
+ if (*p == '\\') {
+ if (flags & PARSE_REDUCE) {
+ memmove(p, p + 1, strlen(p));
+ if (!*p)
+ break;
+ } else
+ p++;
+ } else if (*p == '"') {
+ memmove(p, p + 1, strlen(p));
+ instring = !instring;
+ continue;
+ } else if (!instring && (issep(*p) ||
+ (*p == '#' && !(flags & PARSE_NOHASH))))
+ return p;
+ p++;
+ }
+
+ return instring ? NULL : p;
+}
+
+int
+MakeArgs(char *script, char **pvect, int maxargs, int flags)
+{
+ int nargs;
+
+ nargs = 0;
+ while (*script) {
+ script += strspn(script, " \t");
+ if (*script == '#' && !(flags & PARSE_NOHASH)) {
+ *script = '\0';
+ break;
+ }
+ if (*script) {
+ if (nargs >= maxargs - 1)
+ break;
+ *pvect++ = script;
+ nargs++;
+ script = findblank(script, flags);
+ if (script == NULL)
+ return -1;
+ else if (!(flags & PARSE_NOHASH) && *script == '#')
+ *script = '\0';
+ else if (*script)
+ *script++ = '\0';
+ }
+ }
+ *pvect = NULL;
+ return nargs;
+}
+
+const char *
+NumStr(long val, char *buf, size_t sz)
+{
+ static char result[23]; /* handles 64 bit numbers */
+
+ if (buf == NULL || sz == 0) {
+ buf = result;
+ sz = sizeof result;
+ }
+ snprintf(buf, sz, "<%ld>", val);
+ return buf;
+}
+
+const char *
+HexStr(long val, char *buf, size_t sz)
+{
+ static char result[21]; /* handles 64 bit numbers */
+
+ if (buf == NULL || sz == 0) {
+ buf = result;
+ sz = sizeof result;
+ }
+ snprintf(buf, sz, "<0x%lx>", val);
+ return buf;
+}
+
+const char *
+ex_desc(int ex)
+{
+ static char num[12]; /* Used immediately if returned */
+ static const char * const desc[] = {
+ "normal", "start", "sock", "modem", "dial", "dead", "done",
+ "reboot", "errdead", "hangup", "term", "nodial", "nologin",
+ "redial", "reconnect"
+ };
+
+ if (ex >= 0 && ex < (int)(sizeof desc / sizeof *desc))
+ return desc[ex];
+ snprintf(num, sizeof num, "%d", ex);
+ return num;
+}
+
+void
+SetTitle(const char *title)
+{
+ if (title == NULL)
+ setproctitle(NULL);
+ else if (title[0] == '-' && title[1] != '\0')
+ setproctitle("-%s", title + 1);
+ else
+ setproctitle("%s", title);
+}
+
+fd_set *
+mkfdset()
+{
+ return (fd_set *)malloc(howmany(getdtablesize(), NFDBITS) * sizeof (fd_mask));
+}
+
+void
+zerofdset(fd_set *s)
+{
+ memset(s, '\0', howmany(getdtablesize(), NFDBITS) * sizeof (fd_mask));
+}
+
+void
+Concatinate(char *buf, size_t sz, int argc, const char *const *argv)
+{
+ int i, n;
+ unsigned pos;
+
+ *buf = '\0';
+ for (pos = i = 0; i < argc; i++) {
+ n = snprintf(buf + pos, sz - pos, "%s%s", i ? " " : "", argv[i]);
+ if (n < 0) {
+ buf[pos] = '\0';
+ break;
+ }
+ if ((pos += n) >= sz)
+ break;
+ }
+}
+
+#if defined(__FreeBSD__) && !defined(NOKLDLOAD)
+int
+loadmodules(int how, const char *module, ...)
+{
+ int loaded = 0;
+ va_list ap;
+
+ va_start(ap, module);
+ while (module != NULL) {
+ if (modfind(module) == -1) {
+ if (ID0kldload(module) == -1) {
+ if (how == LOAD_VERBOSLY)
+ log_Printf(LogWARN, "%s: Cannot load module\n", module);
+ } else
+ loaded++;
+ }
+ module = va_arg(ap, const char *);
+ }
+ va_end(ap);
+ return loaded;
+}
+#else
+int
+loadmodules(int how __unused, const char *module __unused, ...)
+{
+ return 0;
+}
+#endif
diff --git a/usr.sbin/ppp/defs.h b/usr.sbin/ppp/defs.h
new file mode 100644
index 0000000..f56ee32
--- /dev/null
+++ b/usr.sbin/ppp/defs.h
@@ -0,0 +1,142 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+/* Check the following definitions for your machine environment */
+#ifdef __FreeBSD__
+# define MODEM_LIST "/dev/cuad1\0/dev/cuad0" /* name of tty device */
+#else
+# ifdef __OpenBSD__
+# define MODEM_LIST "/dev/cua01\0/dev/cua00" /* name of tty device */
+# else
+# define MODEM_LIST "/dev/tty01\0/dev/tty00" /* name of tty device */
+# endif
+#endif
+#define NMODEMS 2
+
+#ifndef PPP_CONFDIR
+#define PPP_CONFDIR "/etc/ppp"
+#endif
+
+#define TUN_NAME "tun"
+#define TUN_PREFIX (_PATH_DEV TUN_NAME) /* /dev/tun */
+
+#define MODEM_SPEED B38400 /* tty speed */
+#define SERVER_PORT 3000 /* Base server port no. */
+#define MODEM_CTSRTS 1 /* Default (true): use CTS/RTS signals */
+#define RECONNECT_TIMEOUT 3 /* Default timer for carrier loss */
+#define DIAL_TIMEOUT 30 /* Default and Max random time to redial */
+#define DIAL_NEXT_TIMEOUT 3 /* Default Hold time to next number redial */
+#define SCRIPT_LEN 512 /* Size of login/dial/hangup scripts */
+#define LINE_LEN SCRIPT_LEN /* Size of lines */
+#define DEVICE_LEN SCRIPT_LEN /* Size of individual devices */
+#define AUTHLEN 100 /* Size of authname/authkey */
+#define CHAPDIGESTLEN 100 /* Maximum chap digest */
+#define CHAPCHALLENGELEN 48 /* Maximum chap challenge */
+#define CHAPAUTHRESPONSELEN 48 /* Maximum chap authresponse (chap81) */
+#define MAXARGS 40 /* How many args per config line */
+#define NCP_IDLE_TIMEOUT 180 /* Drop all links */
+#define CHOKED_TIMEOUT 120 /* Delete queued packets w/ blocked tun */
+
+#define MIN_LQRPERIOD 1 /* Minimum LQR frequency */
+#define DEF_LQRPERIOD 30 /* Default LQR frequency */
+#define MIN_FSMRETRY 1 /* Minimum FSM retry frequency */
+#define DEF_FSMRETRY 3 /* FSM retry frequency */
+#define DEF_FSMTRIES 5 /* Default max retries */
+#define DEF_FSMAUTHTRIES 3 /* Default max auth retries */
+#define DEF_IFQUEUE 30 /* Default interface queue size */
+
+#define CONFFILE "ppp.conf"
+#define LINKUPFILE "ppp.linkup"
+#define LINKDOWNFILE "ppp.linkdown"
+#define SECRETFILE "ppp.secret"
+
+#define EX_SIG -1
+#define EX_NORMAL 0
+#define EX_START 1
+#define EX_SOCK 2
+#define EX_MODEM 3
+#define EX_DIAL 4
+#define EX_DEAD 5
+#define EX_DONE 6
+#define EX_REBOOT 7
+#define EX_ERRDEAD 8
+#define EX_HANGUP 9
+#define EX_TERM 10
+#define EX_NODIAL 11
+#define EX_NOLOGIN 12
+/* return values for -background mode, not really exits */
+#define EX_REDIAL 13
+#define EX_RECONNECT 14
+
+/* physical::type values (OR'd in bundle::phys_type) */
+#define PHYS_NONE 0
+#define PHYS_INTERACTIVE 1 /* Manual link */
+#define PHYS_AUTO 2 /* Dial-on-demand link */
+#define PHYS_DIRECT 4 /* Incoming link, deleted when closed */
+#define PHYS_DEDICATED 8 /* Dedicated link */
+#define PHYS_DDIAL 16 /* Dial immediately, stay connected */
+#define PHYS_BACKGROUND 32 /* Dial immediately, deleted when closed */
+#define PHYS_FOREGROUND 64 /* Pseudo mode, same as background */
+#define PHYS_ALL 127
+
+/* flags passed to findblank() and MakeArgs() */
+#define PARSE_NORMAL 0
+#define PARSE_REDUCE 1
+#define PARSE_NOHASH 2
+
+/* flags passed to loadmodules */
+#define LOAD_QUIETLY 1
+#define LOAD_VERBOSLY 2
+
+#define ROUNDUP(x) ((x) ? (1 + (((x) - 1) | (sizeof(long) - 1))) : sizeof(long))
+
+#if defined(__NetBSD__) || __FreeBSD__ < 3
+extern void randinit(void);
+#else
+#define random arc4random
+#define randinit()
+#endif
+
+extern ssize_t fullread(int, void *, size_t);
+extern const char *mode2Nam(int);
+extern int Nam2mode(const char *);
+extern struct in_addr GetIpAddr(const char *);
+extern unsigned SpeedToUnsigned(speed_t);
+extern speed_t UnsignedToSpeed(unsigned);
+extern char *findblank(char *, int);
+extern int MakeArgs(char *, char **, int, int);
+extern const char *NumStr(long, char *, size_t);
+extern const char *HexStr(long, char *, size_t);
+extern const char *ex_desc(int);
+extern void SetTitle(const char *);
+extern fd_set *mkfdset(void);
+extern void zerofdset(fd_set *);
+extern void Concatinate(char *, size_t, int, const char *const *);
+extern int loadmodules(int, const char *, ...);
diff --git a/usr.sbin/ppp/descriptor.h b/usr.sbin/ppp/descriptor.h
new file mode 100644
index 0000000..a3c1b10
--- /dev/null
+++ b/usr.sbin/ppp/descriptor.h
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define PHYSICAL_DESCRIPTOR (1)
+#define SERVER_DESCRIPTOR (2)
+#define PROMPT_DESCRIPTOR (3)
+#define CHAT_DESCRIPTOR (4)
+#define DATALINK_DESCRIPTOR (5)
+#define BUNDLE_DESCRIPTOR (6)
+#define MPSERVER_DESCRIPTOR (7)
+#define RADIUS_DESCRIPTOR (8)
+#define CHAP_DESCRIPTOR (9)
+
+struct bundle;
+
+struct fdescriptor {
+ int type;
+
+ int (*UpdateSet)(struct fdescriptor *, fd_set *, fd_set *, fd_set *, int *);
+ int (*IsSet)(struct fdescriptor *, const fd_set *);
+ void (*Read)(struct fdescriptor *, struct bundle *, const fd_set *);
+ int (*Write)(struct fdescriptor *, struct bundle *, const fd_set *);
+};
+
+#define descriptor_UpdateSet(d, r, w, e, n) ((*(d)->UpdateSet)(d, r, w, e, n))
+#define descriptor_IsSet(d, s) ((*(d)->IsSet)(d, s))
+#define descriptor_Read(d, b, f) ((*(d)->Read)(d, b, f))
+#define descriptor_Write(d, b, f) ((*(d)->Write)(d, b, f))
diff --git a/usr.sbin/ppp/ether.c b/usr.sbin/ppp/ether.c
new file mode 100644
index 0000000..e4e154a
--- /dev/null
+++ b/usr.sbin/ppp/ether.c
@@ -0,0 +1,736 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netgraph.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netgraph/ng_ether.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/ng_pppoe.h>
+#include <netgraph/ng_socket.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <termios.h>
+#include <sys/time.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "async.h"
+#include "descriptor.h"
+#include "physical.h"
+#include "main.h"
+#include "mp.h"
+#include "chat.h"
+#include "auth.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+#include "slcompress.h"
+#include "iplist.h"
+#include "ncpaddr.h"
+#include "ip.h"
+#include "ipcp.h"
+#include "filter.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "id.h"
+#include "iface.h"
+#include "route.h"
+#include "ether.h"
+
+
+#define PPPOE_NODE_TYPE_LEN (sizeof NG_PPPOE_NODE_TYPE - 1) /* "PPPoE" */
+
+struct etherdevice {
+ struct device dev; /* What struct physical knows about */
+ int cs; /* Control socket */
+ int connected; /* Are we connected yet ? */
+ int timeout; /* Seconds attempting to connect */
+ char hook[sizeof TUN_NAME + 11]; /* Our socket node hook */
+ u_int32_t slot; /* ifindex << 24 | unit */
+};
+
+#define device2ether(d) \
+ ((d)->type == ETHER_DEVICE ? (struct etherdevice *)d : NULL)
+
+unsigned
+ether_DeviceSize(void)
+{
+ return sizeof(struct etherdevice);
+}
+
+static ssize_t
+ether_Write(struct physical *p, const void *v, size_t n)
+{
+ struct etherdevice *dev = device2ether(p->handler);
+
+ return NgSendData(p->fd, dev->hook, v, n) == -1 ? -1 : (ssize_t)n;
+}
+
+static ssize_t
+ether_Read(struct physical *p, void *v, size_t n)
+{
+ char hook[sizeof TUN_NAME + 11];
+
+ return NgRecvData(p->fd, v, n, hook);
+}
+
+static int
+ether_RemoveFromSet(struct physical *p, fd_set *r, fd_set *w, fd_set *e)
+{
+ struct etherdevice *dev = device2ether(p->handler);
+ int result;
+
+ if (r && dev->cs >= 0 && FD_ISSET(dev->cs, r)) {
+ FD_CLR(dev->cs, r);
+ log_Printf(LogTIMER, "%s: fdunset(ctrl) %d\n", p->link.name, dev->cs);
+ result = 1;
+ } else
+ result = 0;
+
+ /* Careful... physical_RemoveFromSet() called us ! */
+
+ p->handler->removefromset = NULL;
+ result += physical_RemoveFromSet(p, r, w, e);
+ p->handler->removefromset = ether_RemoveFromSet;
+
+ return result;
+}
+
+static void
+ether_Free(struct physical *p)
+{
+ struct etherdevice *dev = device2ether(p->handler);
+
+ physical_SetDescriptor(p);
+ if (dev->cs != -1)
+ close(dev->cs);
+ free(dev);
+}
+
+static const char *
+ether_OpenInfo(struct physical *p)
+{
+ struct etherdevice *dev = device2ether(p->handler);
+
+ switch (dev->connected) {
+ case CARRIER_PENDING:
+ return "negotiating";
+ case CARRIER_OK:
+ return "established";
+ }
+
+ return "disconnected";
+}
+
+static int
+ether_Slot(struct physical *p)
+{
+ struct etherdevice *dev = device2ether(p->handler);
+
+ return dev->slot;
+}
+
+
+static void
+ether_device2iov(struct device *d, struct iovec *iov, int *niov,
+ int maxiov __unused, int *auxfd, int *nauxfd)
+{
+ struct etherdevice *dev = device2ether(d);
+ int sz = physical_MaxDeviceSize();
+
+ iov[*niov].iov_base = realloc(d, sz);
+ if (iov[*niov].iov_base == NULL) {
+ log_Printf(LogALERT, "Failed to allocate memory: %d\n", sz);
+ AbortProgram(EX_OSERR);
+ }
+ iov[*niov].iov_len = sz;
+ (*niov)++;
+
+ if (dev->cs >= 0) {
+ *auxfd = dev->cs;
+ (*nauxfd)++;
+ }
+}
+
+static void
+ether_MessageIn(struct etherdevice *dev)
+{
+ char msgbuf[sizeof(struct ng_mesg) + sizeof(struct ngpppoe_sts)];
+ struct ng_mesg *rep = (struct ng_mesg *)msgbuf;
+ struct ngpppoe_sts *sts = (struct ngpppoe_sts *)(msgbuf + sizeof *rep);
+ char *end, unknown[14], sessionid[5];
+ const char *msg;
+ struct timeval t;
+ fd_set *r;
+ u_long slot;
+ int asciilen, ret;
+
+ if (dev->cs < 0)
+ return;
+
+ if ((r = mkfdset()) == NULL) {
+ log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n");
+ return;
+ }
+
+ while (1) {
+ zerofdset(r);
+ FD_SET(dev->cs, r);
+ t.tv_sec = t.tv_usec = 0;
+ ret = select(dev->cs + 1, r, NULL, NULL, &t);
+
+ if (ret <= 0)
+ break;
+
+ if (NgRecvMsg(dev->cs, rep, sizeof msgbuf, NULL) <= 0)
+ break;
+
+ if (rep->header.version != NG_VERSION) {
+ log_Printf(LogWARN, "%ld: Unexpected netgraph version, expected %ld\n",
+ (long)rep->header.version, (long)NG_VERSION);
+ break;
+ }
+
+ if (rep->header.typecookie != NGM_PPPOE_COOKIE) {
+ log_Printf(LogWARN, "%ld: Unexpected netgraph cookie, expected %ld\n",
+ (long)rep->header.typecookie, (long)NGM_PPPOE_COOKIE);
+ break;
+ }
+
+ asciilen = 0;
+ switch (rep->header.cmd) {
+ case NGM_PPPOE_SET_FLAG: msg = "SET_FLAG"; break;
+ case NGM_PPPOE_CONNECT: msg = "CONNECT"; break;
+ case NGM_PPPOE_LISTEN: msg = "LISTEN"; break;
+ case NGM_PPPOE_OFFER: msg = "OFFER"; break;
+ case NGM_PPPOE_SUCCESS: msg = "SUCCESS"; break;
+ case NGM_PPPOE_FAIL: msg = "FAIL"; break;
+ case NGM_PPPOE_CLOSE: msg = "CLOSE"; break;
+ case NGM_PPPOE_GET_STATUS: msg = "GET_STATUS"; break;
+ case NGM_PPPOE_ACNAME:
+ msg = "ACNAME";
+ if (setenv("ACNAME", sts->hook, 1) != 0)
+ log_Printf(LogWARN, "setenv: cannot set ACNAME=%s: %m", sts->hook);
+ asciilen = rep->header.arglen;
+ break;
+ case NGM_PPPOE_SESSIONID:
+ msg = "SESSIONID";
+ snprintf(sessionid, sizeof sessionid, "%04x", *(u_int16_t *)sts);
+ if (setenv("SESSIONID", sessionid, 1) != 0)
+ syslog(LOG_WARNING, "setenv: cannot set SESSIONID=%s: %m",
+ sessionid);
+ /* Use this in preference to our interface index */
+ slot = strtoul(sessionid, &end, 16);
+ if (end != sessionid && *end == '\0')
+ dev->slot = slot;
+ break;
+ default:
+ snprintf(unknown, sizeof unknown, "<%d>", (int)rep->header.cmd);
+ msg = unknown;
+ break;
+ }
+
+ if (asciilen)
+ log_Printf(LogPHASE, "Received NGM_PPPOE_%s (hook \"%.*s\")\n",
+ msg, asciilen, sts->hook);
+ else
+ log_Printf(LogPHASE, "Received NGM_PPPOE_%s\n", msg);
+
+ switch (rep->header.cmd) {
+ case NGM_PPPOE_SUCCESS:
+ dev->connected = CARRIER_OK;
+ break;
+ case NGM_PPPOE_FAIL:
+ case NGM_PPPOE_CLOSE:
+ dev->connected = CARRIER_LOST;
+ break;
+ }
+ }
+ free(r);
+}
+
+static int
+ether_AwaitCarrier(struct physical *p)
+{
+ struct etherdevice *dev = device2ether(p->handler);
+
+ if (dev->connected != CARRIER_OK && !dev->timeout--)
+ dev->connected = CARRIER_LOST;
+ else if (dev->connected == CARRIER_PENDING)
+ ether_MessageIn(dev);
+
+ return dev->connected;
+}
+
+static const struct device baseetherdevice = {
+ ETHER_DEVICE,
+ "ether",
+ 1492,
+ { CD_REQUIRED, DEF_ETHERCDDELAY },
+ ether_AwaitCarrier,
+ ether_RemoveFromSet,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ ether_Free,
+ ether_Read,
+ ether_Write,
+ ether_device2iov,
+ NULL,
+ ether_OpenInfo,
+ ether_Slot
+};
+
+struct device *
+ether_iov2device(int type, struct physical *p, struct iovec *iov, int *niov,
+ int maxiov __unused, int *auxfd, int *nauxfd)
+{
+ if (type == ETHER_DEVICE) {
+ struct etherdevice *dev = (struct etherdevice *)iov[(*niov)++].iov_base;
+
+ dev = realloc(dev, sizeof *dev); /* Reduce to the correct size */
+ if (dev == NULL) {
+ log_Printf(LogALERT, "Failed to allocate memory: %d\n",
+ (int)(sizeof *dev));
+ AbortProgram(EX_OSERR);
+ }
+
+ if (*nauxfd) {
+ dev->cs = *auxfd;
+ (*nauxfd)--;
+ } else
+ dev->cs = -1;
+
+ /* Refresh function pointers etc */
+ memcpy(&dev->dev, &baseetherdevice, sizeof dev->dev);
+
+ physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF);
+ return &dev->dev;
+ }
+
+ return NULL;
+}
+
+static int
+ether_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
+{
+ struct physical *p = descriptor2physical(d);
+ struct etherdevice *dev = device2ether(p->handler);
+ int result;
+
+ if (r && dev->cs >= 0) {
+ FD_SET(dev->cs, r);
+ log_Printf(LogTIMER, "%s(ctrl): fdset(r) %d\n", p->link.name, dev->cs);
+ result = 1;
+ } else
+ result = 0;
+
+ result += physical_doUpdateSet(d, r, w, e, n, 0);
+
+ return result;
+}
+
+static int
+ether_IsSet(struct fdescriptor *d, const fd_set *fdset)
+{
+ struct physical *p = descriptor2physical(d);
+ struct etherdevice *dev = device2ether(p->handler);
+ int result;
+
+ result = dev->cs >= 0 && FD_ISSET(dev->cs, fdset);
+ result += physical_IsSet(d, fdset);
+
+ return result;
+}
+
+static void
+ether_DescriptorRead(struct fdescriptor *d, struct bundle *bundle,
+ const fd_set *fdset)
+{
+ struct physical *p = descriptor2physical(d);
+ struct etherdevice *dev = device2ether(p->handler);
+
+ if (dev->cs >= 0 && FD_ISSET(dev->cs, fdset)) {
+ ether_MessageIn(dev);
+ if (dev->connected == CARRIER_LOST) {
+ log_Printf(LogPHASE, "%s: Device disconnected\n", p->link.name);
+ datalink_Down(p->dl, CLOSE_NORMAL);
+ return;
+ }
+ }
+
+ if (physical_IsSet(d, fdset))
+ physical_DescriptorRead(d, bundle, fdset);
+}
+
+static struct device *
+ether_Abandon(struct etherdevice *dev, struct physical *p)
+{
+ /* Abandon our node construction */
+ close(dev->cs);
+ close(p->fd);
+ p->fd = -2; /* Nobody else need try.. */
+ free(dev);
+
+ return NULL;
+}
+
+struct device *
+ether_Create(struct physical *p)
+{
+ u_char rbuf[2048];
+ struct etherdevice *dev;
+ struct ng_mesg *resp;
+ const struct hooklist *hlist;
+ const struct nodeinfo *ninfo;
+ char *path, *sessionid;
+ const char *mode;
+ size_t ifacelen;
+ unsigned f;
+
+ dev = NULL;
+ path = NULL;
+ ifacelen = 0;
+ if (p->fd < 0 && !strncasecmp(p->name.full, NG_PPPOE_NODE_TYPE,
+ PPPOE_NODE_TYPE_LEN) &&
+ p->name.full[PPPOE_NODE_TYPE_LEN] == ':') {
+ const struct linkinfo *nlink;
+ struct ngpppoe_init_data *data;
+ struct ngm_mkpeer mkp;
+ struct ngm_connect ngc;
+ const char *iface, *provider;
+ char etherid[12];
+ int providerlen;
+ char connectpath[sizeof dev->hook + 2]; /* .:<hook> */
+
+ p->fd--; /* We own the device - change fd */
+
+ loadmodules(LOAD_VERBOSLY, "netgraph", "ng_ether", "ng_pppoe", "ng_socket",
+ NULL);
+
+ if ((dev = malloc(sizeof *dev)) == NULL)
+ return NULL;
+
+ iface = p->name.full + PPPOE_NODE_TYPE_LEN + 1;
+
+ provider = strchr(iface, ':');
+ if (provider) {
+ ifacelen = provider - iface;
+ provider++;
+ providerlen = strlen(provider);
+ } else {
+ ifacelen = strlen(iface);
+ provider = "";
+ providerlen = 0;
+ }
+
+ /*
+ * We're going to do this (where tunN is our tunnel device):
+ *
+ * .---------.
+ * | ether |
+ * | <iface> | dev->cs
+ * `---------' |
+ * (orphan) p->fd |
+ * | | |
+ * | | |
+ * (ethernet) | |
+ * .---------. .-----------.
+ * | pppoe | | socket |
+ * | <iface> |(tunN)<---->(tunN)| <unnamed> |
+ * `--------- `-----------'
+ * (tunX)
+ * ^
+ * |
+ * `--->(tunX)
+ */
+
+ /* Create a socket node */
+ if (ID0NgMkSockNode(NULL, &dev->cs, &p->fd) == -1) {
+ log_Printf(LogWARN, "Cannot create netgraph socket node: %s\n",
+ strerror(errno));
+ free(dev);
+ p->fd = -2;
+ return NULL;
+ }
+
+ /*
+ * Ask for a list of hooks attached to the "ether" node. This node should
+ * magically exist as a way of hooking stuff onto an ethernet device
+ */
+ path = (char *)alloca(ifacelen + 2);
+ sprintf(path, "%.*s:", (int)ifacelen, iface);
+ if (NgSendMsg(dev->cs, path, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,
+ NULL, 0) < 0) {
+ log_Printf(LogWARN, "%s Cannot send a netgraph message: %s\n",
+ path, strerror(errno));
+ return ether_Abandon(dev, p);
+ }
+
+ /* Get our list back */
+ resp = (struct ng_mesg *)rbuf;
+ if (NgRecvMsg(dev->cs, resp, sizeof rbuf, NULL) <= 0) {
+ log_Printf(LogWARN, "Cannot get netgraph response: %s\n",
+ strerror(errno));
+ return ether_Abandon(dev, p);
+ }
+
+ hlist = (const struct hooklist *)resp->data;
+ ninfo = &hlist->nodeinfo;
+
+ /* Make sure we've got the right type of node */
+ if (strncmp(ninfo->type, NG_ETHER_NODE_TYPE,
+ sizeof NG_ETHER_NODE_TYPE - 1)) {
+ log_Printf(LogWARN, "%s Unexpected node type ``%s'' (wanted ``"
+ NG_ETHER_NODE_TYPE "'')\n", path, ninfo->type);
+ return ether_Abandon(dev, p);
+ }
+
+ log_Printf(LogDEBUG, "List of netgraph node ``%s'' (id %x) hooks:\n",
+ path, ninfo->id);
+
+ /* look for a hook already attached. */
+ for (f = 0; f < ninfo->hooks; f++) {
+ nlink = &hlist->link[f];
+
+ log_Printf(LogDEBUG, " Found %s -> %s\n", nlink->ourhook,
+ nlink->peerhook);
+
+ if (!strcmp(nlink->ourhook, NG_ETHER_HOOK_ORPHAN) ||
+ !strcmp(nlink->ourhook, NG_ETHER_HOOK_DIVERT)) {
+ /*
+ * Something is using the data coming out of this ``ether'' node.
+ * If it's a PPPoE node, we use that node, otherwise we complain that
+ * someone else is using the node.
+ */
+ if (!strcmp(nlink->nodeinfo.type, NG_PPPOE_NODE_TYPE))
+ /* Use this PPPoE node ! */
+ snprintf(ngc.path, sizeof ngc.path, "[%x]:", nlink->nodeinfo.id);
+ else {
+ log_Printf(LogWARN, "%s Node type ``%s'' is currently active\n",
+ path, nlink->nodeinfo.type);
+ return ether_Abandon(dev, p);
+ }
+ break;
+ }
+ }
+
+ if (f == ninfo->hooks) {
+ /*
+ * Create a new ``PPPoE'' node connected to the ``ether'' node using
+ * the ``orphan'' and ``ethernet'' hooks
+ */
+ snprintf(mkp.type, sizeof mkp.type, "%s", NG_PPPOE_NODE_TYPE);
+ snprintf(mkp.ourhook, sizeof mkp.ourhook, "%s", NG_ETHER_HOOK_ORPHAN);
+ snprintf(mkp.peerhook, sizeof mkp.peerhook, "%s", NG_PPPOE_HOOK_ETHERNET);
+ snprintf(etherid, sizeof etherid, "[%x]:", ninfo->id);
+
+ log_Printf(LogDEBUG, "Creating PPPoE netgraph node %s%s -> %s\n",
+ etherid, mkp.ourhook, mkp.peerhook);
+
+ if (NgSendMsg(dev->cs, etherid, NGM_GENERIC_COOKIE,
+ NGM_MKPEER, &mkp, sizeof mkp) < 0) {
+ log_Printf(LogWARN, "%s Cannot create PPPoE netgraph node: %s\n",
+ etherid, strerror(errno));
+ return ether_Abandon(dev, p);
+ }
+
+ snprintf(ngc.path, sizeof ngc.path, "%s%s", path, NG_ETHER_HOOK_ORPHAN);
+ }
+
+ snprintf(dev->hook, sizeof dev->hook, "%s%d",
+ TUN_NAME, p->dl->bundle->unit);
+
+ /*
+ * Connect the PPPoE node to our socket node.
+ * ngc.path has already been set up
+ */
+ snprintf(ngc.ourhook, sizeof ngc.ourhook, "%s", dev->hook);
+ memcpy(ngc.peerhook, ngc.ourhook, sizeof ngc.peerhook);
+
+ log_Printf(LogDEBUG, "Connecting netgraph socket .:%s -> %s:%s\n",
+ ngc.ourhook, ngc.path, ngc.peerhook);
+ if (NgSendMsg(dev->cs, ".:", NGM_GENERIC_COOKIE,
+ NGM_CONNECT, &ngc, sizeof ngc) < 0) {
+ log_Printf(LogWARN, "Cannot connect PPPoE and socket netgraph "
+ "nodes: %s\n", strerror(errno));
+ return ether_Abandon(dev, p);
+ }
+
+ /* Bring the Ethernet interface up */
+ path[ifacelen] = '\0'; /* Remove the trailing ':' */
+ if (!iface_SetFlags(path, IFF_UP))
+ log_Printf(LogWARN, "%s: Failed to set the IFF_UP flag on %s\n",
+ p->link.name, path);
+
+ snprintf(connectpath, sizeof connectpath, ".:%s", dev->hook);
+
+ /* Configure node to 3Com mode if needed */
+ if (p->cfg.pppoe_configured) {
+ mode = p->cfg.nonstandard_pppoe ? NG_PPPOE_NONSTANDARD : NG_PPPOE_STANDARD;
+ if (NgSendMsg(dev->cs, connectpath, NGM_PPPOE_COOKIE,
+ NGM_PPPOE_SETMODE, mode, strlen(mode) + 1) == -1) {
+ log_Printf(LogWARN, "``%s'': Cannot configure netgraph node: %s\n",
+ connectpath, strerror(errno));
+ return ether_Abandon(dev, p);
+ }
+ }
+
+ /* And finally, request a connection to the given provider */
+
+ data = (struct ngpppoe_init_data *)alloca(sizeof *data + providerlen);
+ snprintf(data->hook, sizeof data->hook, "%s", dev->hook);
+ memcpy(data->data, provider, providerlen);
+ data->data_len = providerlen;
+
+ log_Printf(LogDEBUG, "Sending PPPOE_CONNECT to %s\n", connectpath);
+ if (NgSendMsg(dev->cs, connectpath, NGM_PPPOE_COOKIE,
+ NGM_PPPOE_CONNECT, data, sizeof *data + providerlen) == -1) {
+ log_Printf(LogWARN, "``%s'': Cannot start netgraph node: %s\n",
+ connectpath, strerror(errno));
+ return ether_Abandon(dev, p);
+ }
+
+ /* Hook things up so that we monitor dev->cs */
+ p->desc.UpdateSet = ether_UpdateSet;
+ p->desc.IsSet = ether_IsSet;
+ p->desc.Read = ether_DescriptorRead;
+
+ memcpy(&dev->dev, &baseetherdevice, sizeof dev->dev);
+ switch (p->cfg.cd.necessity) {
+ case CD_VARIABLE:
+ dev->dev.cd.delay = p->cfg.cd.delay;
+ break;
+ case CD_REQUIRED:
+ dev->dev.cd = p->cfg.cd;
+ break;
+ case CD_NOTREQUIRED:
+ log_Printf(LogWARN, "%s: Carrier must be set, using ``set cd %d!''\n",
+ p->link.name, dev->dev.cd.delay);
+ case CD_DEFAULT:
+ break;
+ }
+
+ dev->timeout = dev->dev.cd.delay;
+ dev->connected = CARRIER_PENDING;
+ /* This will be overridden by our session id - if provided by netgraph */
+ dev->slot = GetIfIndex(path);
+ } else {
+ /* See if we're a netgraph socket */
+ struct stat st;
+
+ if (fstat(p->fd, &st) != -1 && (st.st_mode & S_IFSOCK)) {
+ struct sockaddr_storage ssock;
+ struct sockaddr *sock = (struct sockaddr *)&ssock;
+ int sz;
+
+ sz = sizeof ssock;
+ if (getsockname(p->fd, sock, &sz) == -1) {
+ log_Printf(LogPHASE, "%s: Link is a closed socket !\n", p->link.name);
+ close(p->fd);
+ p->fd = -1;
+ return NULL;
+ }
+
+ if (sock->sa_family == AF_NETGRAPH) {
+ /*
+ * It's a netgraph node... We can't determine hook names etc, so we
+ * stay pretty impartial....
+ */
+ log_Printf(LogPHASE, "%s: Link is a netgraph node\n", p->link.name);
+
+ if ((dev = malloc(sizeof *dev)) == NULL) {
+ log_Printf(LogWARN, "%s: Cannot allocate an ether device: %s\n",
+ p->link.name, strerror(errno));
+ return NULL;
+ }
+
+ memcpy(&dev->dev, &baseetherdevice, sizeof dev->dev);
+ dev->cs = -1;
+ dev->timeout = 0;
+ dev->connected = CARRIER_OK;
+ *dev->hook = '\0';
+
+ /*
+ * If we're being envoked from pppoed(8), we may have a SESSIONID
+ * set in the environment. If so, use it as the slot
+ */
+ if ((sessionid = getenv("SESSIONID")) != NULL) {
+ char *end;
+ u_long slot;
+
+ slot = strtoul(sessionid, &end, 16);
+ dev->slot = end != sessionid && *end == '\0' ? slot : 0;
+ } else
+ dev->slot = 0;
+ }
+ }
+ }
+
+ if (dev) {
+ physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF);
+ return &dev->dev;
+ }
+
+ return NULL;
+}
diff --git a/usr.sbin/ppp/ether.h b/usr.sbin/ppp/ether.h
new file mode 100644
index 0000000..94511bd
--- /dev/null
+++ b/usr.sbin/ppp/ether.h
@@ -0,0 +1,37 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct physical;
+struct device;
+
+#define DEF_ETHERCDDELAY 5 /* Default ``set cd'' value */
+
+extern struct device *ether_Create(struct physical *);
+extern struct device *ether_iov2device(int, struct physical *, struct iovec *,
+ int *, int, int *, int *);
+extern unsigned ether_DeviceSize(void);
diff --git a/usr.sbin/ppp/exec.c b/usr.sbin/ppp/exec.c
new file mode 100644
index 0000000..7c80034
--- /dev/null
+++ b/usr.sbin/ppp/exec.c
@@ -0,0 +1,234 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <sys/uio.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "async.h"
+#include "descriptor.h"
+#include "physical.h"
+#include "mp.h"
+#include "chat.h"
+#include "command.h"
+#include "auth.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+#include "id.h"
+#include "exec.h"
+
+static struct device execdevice = {
+ EXEC_DEVICE,
+ "exec",
+ 0,
+ { CD_NOTREQUIRED, 0 },
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+struct device *
+exec_iov2device(int type, struct physical *p, struct iovec *iov,
+ int *niov, int maxiov __unused, int *auxfd __unused,
+ int *nauxfd __unused)
+{
+ if (type == EXEC_DEVICE) {
+ free(iov[(*niov)++].iov_base);
+ physical_SetupStack(p, execdevice.name, PHYSICAL_NOFORCE);
+ return &execdevice;
+ }
+
+ return NULL;
+}
+
+struct device *
+exec_Create(struct physical *p)
+{
+ if (p->fd < 0 && *p->name.full == '!') {
+ int fids[2], type;
+
+ p->fd--; /* We own the device but maybe can't use it - change fd */
+ type = physical_IsSync(p) ? SOCK_DGRAM : SOCK_STREAM;
+
+ if (socketpair(AF_UNIX, type, PF_UNSPEC, fids) < 0)
+ log_Printf(LogPHASE, "Unable to create pipe for line exec: %s\n",
+ strerror(errno));
+ else {
+ static int child_status; /* This variable is abused ! */
+ int stat, argc, i, ret, wret, pidpipe[2];
+ pid_t pid, realpid;
+ char *argv[MAXARGS];
+
+ stat = fcntl(fids[0], F_GETFL, 0);
+ if (stat > 0) {
+ stat |= O_NONBLOCK;
+ fcntl(fids[0], F_SETFL, stat);
+ }
+ realpid = getpid();
+ if (pipe(pidpipe) == -1) {
+ log_Printf(LogPHASE, "Unable to pipe for line exec: %s\n",
+ strerror(errno));
+ close(fids[1]);
+ } else switch ((pid = fork())) {
+ case -1:
+ log_Printf(LogPHASE, "Unable to fork for line exec: %s\n",
+ strerror(errno));
+ close(pidpipe[0]);
+ close(pidpipe[1]);
+ close(fids[1]);
+ break;
+
+ case 0:
+ close(pidpipe[0]);
+ close(fids[0]);
+ timer_TermService();
+#ifndef NOSUID
+ setuid(ID0realuid());
+#endif
+
+ child_status = 0;
+ switch ((pid = vfork())) {
+ case 0:
+ close(pidpipe[1]);
+ break;
+
+ case -1:
+ ret = errno;
+ log_Printf(LogPHASE, "Unable to vfork to drop parent: %s\n",
+ strerror(errno));
+ close(pidpipe[1]);
+ _exit(ret);
+
+ default:
+ write(pidpipe[1], &pid, sizeof pid);
+ close(pidpipe[1]);
+ _exit(child_status); /* The error from exec() ! */
+ }
+
+ log_Printf(LogDEBUG, "Exec'ing ``%s''\n", p->name.base);
+
+ if ((argc = MakeArgs(p->name.base, argv, VECSIZE(argv),
+ PARSE_REDUCE|PARSE_NOHASH)) < 0) {
+ log_Printf(LogWARN, "Syntax error in exec command\n");
+ _exit(ESRCH);
+ }
+
+ command_Expand(argv, argc, (char const *const *)argv,
+ p->dl->bundle, 0, realpid);
+
+ dup2(fids[1], STDIN_FILENO);
+ dup2(fids[1], STDOUT_FILENO);
+ dup2(fids[1], STDERR_FILENO);
+ for (i = getdtablesize(); i > STDERR_FILENO; i--)
+ fcntl(i, F_SETFD, 1);
+
+ execvp(*argv, argv);
+ child_status = errno; /* Only works for vfork() */
+ printf("execvp failed: %s: %s\r\n", *argv, strerror(child_status));
+ _exit(child_status);
+ break;
+
+ default:
+ close(pidpipe[1]);
+ close(fids[1]);
+ if (read(pidpipe[0], &p->session_owner, sizeof p->session_owner) !=
+ sizeof p->session_owner)
+ p->session_owner = (pid_t)-1;
+ close(pidpipe[0]);
+ while ((wret = waitpid(pid, &stat, 0)) == -1 && errno == EINTR)
+ ;
+ if (wret == -1) {
+ log_Printf(LogWARN, "Waiting for child process: %s\n",
+ strerror(errno));
+ close(fids[0]);
+ p->session_owner = (pid_t)-1;
+ break;
+ } else if (WIFSIGNALED(stat)) {
+ log_Printf(LogWARN, "Child process received sig %d !\n",
+ WTERMSIG(stat));
+ close(fids[0]);
+ p->session_owner = (pid_t)-1;
+ break;
+ } else if (WIFSTOPPED(stat)) {
+ log_Printf(LogWARN, "Child process received stop sig %d !\n",
+ WSTOPSIG(stat));
+ /* I guess that's ok.... */
+ } else if ((ret = WEXITSTATUS(stat))) {
+ log_Printf(LogWARN, "Cannot exec \"%s\": %s\n", p->name.base,
+ strerror(ret));
+ close(fids[0]);
+ p->session_owner = (pid_t)-1;
+ break;
+ }
+ p->fd = fids[0];
+ log_Printf(LogDEBUG, "Using descriptor %d for child\n", p->fd);
+ physical_SetupStack(p, execdevice.name, PHYSICAL_NOFORCE);
+ if (p->cfg.cd.necessity != CD_DEFAULT)
+ log_Printf(LogWARN, "Carrier settings ignored\n");
+ return &execdevice;
+ }
+ close(fids[0]);
+ }
+ }
+
+ return NULL;
+}
diff --git a/usr.sbin/ppp/exec.h b/usr.sbin/ppp/exec.h
new file mode 100644
index 0000000..d4b3387
--- /dev/null
+++ b/usr.sbin/ppp/exec.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct physical;
+struct device;
+
+extern struct device *exec_Create(struct physical *);
+extern struct device *exec_iov2device(int, struct physical *,
+ struct iovec *, int *, int, int *, int *);
+#define exec_DeviceSize physical_DeviceSize
diff --git a/usr.sbin/ppp/filter.c b/usr.sbin/ppp/filter.c
new file mode 100644
index 0000000..f59848e
--- /dev/null
+++ b/usr.sbin/ppp/filter.c
@@ -0,0 +1,604 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <netinet/in.h>
+#include <netdb.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "iplist.h"
+#include "timer.h"
+#include "throughput.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "slcompress.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "prompt.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+
+static unsigned filter_Nam2Op(const char *);
+
+static int
+ParsePort(const char *service, const char *proto)
+{
+ struct servent *servent;
+ char *cp;
+ int port;
+
+ servent = getservbyname(service, proto);
+ if (servent != 0)
+ return ntohs(servent->s_port);
+
+ port = strtol(service, &cp, 0);
+ if (cp == service) {
+ log_Printf(LogWARN, "ParsePort: %s is not a port name or number.\n",
+ service);
+ return 0;
+ }
+ return port;
+}
+
+/*
+ * ICMP Syntax: src eq icmp_message_type
+ */
+static int
+ParseIcmp(int argc, char const *const *argv, struct filterent *tgt)
+{
+ int type;
+ char *cp;
+
+ switch (argc) {
+ case 0:
+ /* permit/deny all ICMP types */
+ tgt->f_srcop = tgt->f_dstop = OP_NONE;
+ break;
+
+ case 3:
+ if (!strcmp(*argv, "src") && !strcmp(argv[1], "eq")) {
+ type = strtol(argv[2], &cp, 0);
+ if (cp == argv[2]) {
+ log_Printf(LogWARN, "ParseIcmp: type is expected.\n");
+ return 0;
+ }
+ tgt->f_srcop = OP_EQ;
+ tgt->f_srcport = type;
+ tgt->f_dstop = OP_NONE;
+ }
+ break;
+
+ default:
+ log_Printf(LogWARN, "ParseIcmp: bad icmp syntax.\n");
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * UDP Syntax: [src op port] [dst op port]
+ */
+static int
+ParseUdpOrTcp(int argc, char const *const *argv, const struct protoent *pe,
+ struct filterent *tgt)
+{
+ tgt->f_srcop = tgt->f_dstop = OP_NONE;
+ tgt->f_estab = tgt->f_syn = tgt->f_finrst = 0;
+
+ if (argc >= 3 && !strcmp(*argv, "src")) {
+ tgt->f_srcop = filter_Nam2Op(argv[1]);
+ if (tgt->f_srcop == OP_NONE) {
+ log_Printf(LogWARN, "ParseUdpOrTcp: bad operator\n");
+ return 0;
+ }
+ if (pe == NULL)
+ return 0;
+ tgt->f_srcport = ParsePort(argv[2], pe->p_name);
+ if (tgt->f_srcport == 0)
+ return 0;
+ argc -= 3;
+ argv += 3;
+ }
+
+ if (argc >= 3 && !strcmp(argv[0], "dst")) {
+ tgt->f_dstop = filter_Nam2Op(argv[1]);
+ if (tgt->f_dstop == OP_NONE) {
+ log_Printf(LogWARN, "ParseUdpOrTcp: bad operator\n");
+ return 0;
+ }
+ if (pe == NULL)
+ return 0;
+ tgt->f_dstport = ParsePort(argv[2], pe->p_name);
+ if (tgt->f_dstport == 0)
+ return 0;
+ argc -= 3;
+ argv += 3;
+ }
+
+ if (pe && pe->p_proto == IPPROTO_TCP) {
+ for (; argc > 0; argc--, argv++)
+ if (!strcmp(*argv, "estab"))
+ tgt->f_estab = 1;
+ else if (!strcmp(*argv, "syn"))
+ tgt->f_syn = 1;
+ else if (!strcmp(*argv, "finrst"))
+ tgt->f_finrst = 1;
+ else
+ break;
+ }
+
+ if (argc > 0) {
+ log_Printf(LogWARN, "ParseUdpOrTcp: bad src/dst port syntax: %s\n", *argv);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+ParseGeneric(int argc, struct filterent *tgt)
+{
+ /*
+ * Filter currently is a catch-all. Requests are either permitted or
+ * dropped.
+ */
+ if (argc != 0) {
+ log_Printf(LogWARN, "ParseGeneric: Too many parameters\n");
+ return 0;
+ } else
+ tgt->f_srcop = tgt->f_dstop = OP_NONE;
+
+ return 1;
+}
+
+static unsigned
+addrtype(const char *addr)
+{
+ if (!strncasecmp(addr, "MYADDR", 6) && (addr[6] == '\0' || addr[6] == '/'))
+ return T_MYADDR;
+ if (!strncasecmp(addr, "MYADDR6", 7) && (addr[7] == '\0' || addr[7] == '/'))
+ return T_MYADDR6;
+ if (!strncasecmp(addr, "HISADDR", 7) && (addr[7] == '\0' || addr[7] == '/'))
+ return T_HISADDR;
+ if (!strncasecmp(addr, "HISADDR6", 8) && (addr[8] == '\0' || addr[8] == '/'))
+ return T_HISADDR6;
+ if (!strncasecmp(addr, "DNS0", 4) && (addr[4] == '\0' || addr[4] == '/'))
+ return T_DNS0;
+ if (!strncasecmp(addr, "DNS1", 4) && (addr[4] == '\0' || addr[4] == '/'))
+ return T_DNS1;
+
+ return T_ADDR;
+}
+
+static const char *
+addrstr(struct ncprange *addr, unsigned type)
+{
+ switch (type) {
+ case T_MYADDR:
+ return "MYADDR";
+ case T_HISADDR:
+ return "HISADDR";
+ case T_DNS0:
+ return "DNS0";
+ case T_DNS1:
+ return "DNS1";
+ }
+ return ncprange_ntoa(addr);
+}
+
+static int
+filter_Parse(struct ncp *ncp, int argc, char const *const *argv,
+ struct filterent *ofp)
+{
+ struct filterent fe;
+ struct protoent *pe;
+ char *wp;
+ int action, family, ruleno, val, width;
+
+ ruleno = strtol(*argv, &wp, 0);
+ if (*argv == wp || ruleno >= MAXFILTERS) {
+ log_Printf(LogWARN, "Parse: invalid filter number.\n");
+ return 0;
+ }
+ if (ruleno < 0) {
+ for (ruleno = 0; ruleno < MAXFILTERS; ruleno++) {
+ ofp->f_action = A_NONE;
+ ofp++;
+ }
+ log_Printf(LogWARN, "Parse: filter cleared.\n");
+ return 1;
+ }
+ ofp += ruleno;
+
+ if (--argc == 0) {
+ log_Printf(LogWARN, "Parse: missing action.\n");
+ return 0;
+ }
+ argv++;
+
+ memset(&fe, '\0', sizeof fe);
+
+ val = strtol(*argv, &wp, 0);
+ if (!*wp && val >= 0 && val < MAXFILTERS) {
+ if (val <= ruleno) {
+ log_Printf(LogWARN, "Parse: Can only jump forward from rule %d\n",
+ ruleno);
+ return 0;
+ }
+ action = val;
+ } else if (!strcmp(*argv, "permit")) {
+ action = A_PERMIT;
+ } else if (!strcmp(*argv, "deny")) {
+ action = A_DENY;
+ } else if (!strcmp(*argv, "clear")) {
+ ofp->f_action = A_NONE;
+ return 1;
+ } else {
+ log_Printf(LogWARN, "Parse: %s: bad action\n", *argv);
+ return 0;
+ }
+ fe.f_action = action;
+
+ argc--;
+ argv++;
+
+ if (argc && argv[0][0] == '!' && !argv[0][1]) {
+ fe.f_invert = 1;
+ argc--;
+ argv++;
+ }
+
+ ncprange_init(&fe.f_src);
+ ncprange_init(&fe.f_dst);
+
+ if (argc == 0)
+ pe = NULL;
+ else if ((pe = getprotobyname(*argv)) == NULL && strcmp(*argv, "all") != 0) {
+ if (argc < 2) {
+ log_Printf(LogWARN, "Parse: Protocol or address pair expected\n");
+ return 0;
+ } else if (strcasecmp(*argv, "any") == 0 ||
+ ncprange_aton(&fe.f_src, ncp, *argv)) {
+ family = ncprange_family(&fe.f_src);
+ if (!ncprange_getwidth(&fe.f_src, &width))
+ width = 0;
+ if (width == 0)
+ ncprange_init(&fe.f_src);
+ fe.f_srctype = addrtype(*argv);
+ argc--;
+ argv++;
+
+ if (strcasecmp(*argv, "any") == 0 ||
+ ncprange_aton(&fe.f_dst, ncp, *argv)) {
+ if (ncprange_family(&fe.f_dst) != AF_UNSPEC &&
+ ncprange_family(&fe.f_src) != AF_UNSPEC &&
+ family != ncprange_family(&fe.f_dst)) {
+ log_Printf(LogWARN, "Parse: src and dst address families differ\n");
+ return 0;
+ }
+ if (!ncprange_getwidth(&fe.f_dst, &width))
+ width = 0;
+ if (width == 0)
+ ncprange_init(&fe.f_dst);
+ fe.f_dsttype = addrtype(*argv);
+ argc--;
+ argv++;
+ } else {
+ log_Printf(LogWARN, "Parse: Protocol or address pair expected\n");
+ return 0;
+ }
+
+ if (argc) {
+ if ((pe = getprotobyname(*argv)) == NULL && strcmp(*argv, "all") != 0) {
+ log_Printf(LogWARN, "Parse: %s: Protocol expected\n", *argv);
+ return 0;
+ } else {
+ argc--;
+ argv++;
+ }
+ }
+ } else {
+ log_Printf(LogWARN, "Parse: Protocol or address pair expected\n");
+ return 0;
+ }
+ } else {
+ argc--;
+ argv++;
+ }
+
+ if (argc >= 2 && strcmp(*argv, "timeout") == 0) {
+ fe.timeout = strtoul(argv[1], NULL, 10);
+ argc -= 2;
+ argv += 2;
+ }
+
+ val = 1;
+ fe.f_proto = (pe == NULL) ? 0 : pe->p_proto;
+
+ switch (fe.f_proto) {
+ case IPPROTO_TCP:
+ case IPPROTO_UDP:
+ case IPPROTO_IPIP:
+#ifndef NOINET6
+ case IPPROTO_IPV6:
+#endif
+ val = ParseUdpOrTcp(argc, argv, pe, &fe);
+ break;
+ case IPPROTO_ICMP:
+#ifndef NOINET6
+ case IPPROTO_ICMPV6:
+#endif
+ val = ParseIcmp(argc, argv, &fe);
+ break;
+ default:
+ val = ParseGeneric(argc, &fe);
+ break;
+ }
+
+ log_Printf(LogDEBUG, "Parse: Src: %s\n", ncprange_ntoa(&fe.f_src));
+ log_Printf(LogDEBUG, "Parse: Dst: %s\n", ncprange_ntoa(&fe.f_dst));
+ log_Printf(LogDEBUG, "Parse: Proto: %d\n", fe.f_proto);
+
+ log_Printf(LogDEBUG, "Parse: src: %s (%d)\n",
+ filter_Op2Nam(fe.f_srcop), fe.f_srcport);
+ log_Printf(LogDEBUG, "Parse: dst: %s (%d)\n",
+ filter_Op2Nam(fe.f_dstop), fe.f_dstport);
+ log_Printf(LogDEBUG, "Parse: estab: %u\n", fe.f_estab);
+ log_Printf(LogDEBUG, "Parse: syn: %u\n", fe.f_syn);
+ log_Printf(LogDEBUG, "Parse: finrst: %u\n", fe.f_finrst);
+
+ if (val)
+ *ofp = fe;
+
+ return val;
+}
+
+int
+filter_Set(struct cmdargs const *arg)
+{
+ struct filter *filter;
+
+ if (arg->argc < arg->argn+2)
+ return -1;
+
+ if (!strcmp(arg->argv[arg->argn], "in"))
+ filter = &arg->bundle->filter.in;
+ else if (!strcmp(arg->argv[arg->argn], "out"))
+ filter = &arg->bundle->filter.out;
+ else if (!strcmp(arg->argv[arg->argn], "dial"))
+ filter = &arg->bundle->filter.dial;
+ else if (!strcmp(arg->argv[arg->argn], "alive"))
+ filter = &arg->bundle->filter.alive;
+ else {
+ log_Printf(LogWARN, "filter_Set: %s: Invalid filter name.\n",
+ arg->argv[arg->argn]);
+ return -1;
+ }
+
+ filter_Parse(&arg->bundle->ncp, arg->argc - arg->argn - 1,
+ arg->argv + arg->argn + 1, filter->rule);
+ return 0;
+}
+
+const char *
+filter_Action2Nam(unsigned act)
+{
+ static const char * const actname[] = { " none ", "permit ", " deny " };
+ static char buf[8];
+
+ if (act < MAXFILTERS) {
+ snprintf(buf, sizeof buf, "%6d ", act);
+ return buf;
+ } else if (act >= A_NONE && act < A_NONE + sizeof(actname)/sizeof(char *))
+ return actname[act - A_NONE];
+ else
+ return "?????? ";
+}
+
+static void
+doShowFilter(struct filterent *fp, struct prompt *prompt)
+{
+ struct protoent *pe;
+ int n;
+
+ for (n = 0; n < MAXFILTERS; n++, fp++) {
+ if (fp->f_action != A_NONE) {
+ prompt_Printf(prompt, " %2d %s", n, filter_Action2Nam(fp->f_action));
+ prompt_Printf(prompt, "%c ", fp->f_invert ? '!' : ' ');
+
+ if (ncprange_isset(&fp->f_src))
+ prompt_Printf(prompt, "%s ", addrstr(&fp->f_src, fp->f_srctype));
+ else
+ prompt_Printf(prompt, "any ");
+
+ if (ncprange_isset(&fp->f_dst))
+ prompt_Printf(prompt, "%s ", addrstr(&fp->f_dst, fp->f_dsttype));
+ else
+ prompt_Printf(prompt, "any ");
+
+ if (fp->f_proto) {
+ if ((pe = getprotobynumber(fp->f_proto)) == NULL)
+ prompt_Printf(prompt, "P:%d", fp->f_proto);
+ else
+ prompt_Printf(prompt, "%s", pe->p_name);
+
+ if (fp->f_srcop)
+ prompt_Printf(prompt, " src %s %d", filter_Op2Nam(fp->f_srcop),
+ fp->f_srcport);
+ if (fp->f_dstop)
+ prompt_Printf(prompt, " dst %s %d", filter_Op2Nam(fp->f_dstop),
+ fp->f_dstport);
+ if (fp->f_estab)
+ prompt_Printf(prompt, " estab");
+ if (fp->f_syn)
+ prompt_Printf(prompt, " syn");
+ if (fp->f_finrst)
+ prompt_Printf(prompt, " finrst");
+ } else
+ prompt_Printf(prompt, "all");
+ if (fp->timeout != 0)
+ prompt_Printf(prompt, " timeout %u", fp->timeout);
+ prompt_Printf(prompt, "\n");
+ }
+ }
+}
+
+int
+filter_Show(struct cmdargs const *arg)
+{
+ if (arg->argc > arg->argn+1)
+ return -1;
+
+ if (arg->argc == arg->argn+1) {
+ struct filter *filter;
+
+ if (!strcmp(arg->argv[arg->argn], "in"))
+ filter = &arg->bundle->filter.in;
+ else if (!strcmp(arg->argv[arg->argn], "out"))
+ filter = &arg->bundle->filter.out;
+ else if (!strcmp(arg->argv[arg->argn], "dial"))
+ filter = &arg->bundle->filter.dial;
+ else if (!strcmp(arg->argv[arg->argn], "alive"))
+ filter = &arg->bundle->filter.alive;
+ else
+ return -1;
+ doShowFilter(filter->rule, arg->prompt);
+ } else {
+ struct filter *filter[4];
+ int f;
+
+ filter[0] = &arg->bundle->filter.in;
+ filter[1] = &arg->bundle->filter.out;
+ filter[2] = &arg->bundle->filter.dial;
+ filter[3] = &arg->bundle->filter.alive;
+ for (f = 0; f < 4; f++) {
+ if (f)
+ prompt_Printf(arg->prompt, "\n");
+ prompt_Printf(arg->prompt, "%s:\n", filter[f]->name);
+ doShowFilter(filter[f]->rule, arg->prompt);
+ }
+ }
+
+ return 0;
+}
+
+static const char * const opname[] = {"none", "eq", "gt", "lt"};
+
+const char *
+filter_Op2Nam(unsigned op)
+{
+ if (op >= sizeof opname / sizeof opname[0])
+ return "unknown";
+ return opname[op];
+
+}
+
+static unsigned
+filter_Nam2Op(const char *cp)
+{
+ unsigned op;
+
+ for (op = sizeof opname / sizeof opname[0] - 1; op; op--)
+ if (!strcasecmp(cp, opname[op]))
+ break;
+
+ return op;
+}
+
+void
+filter_AdjustAddr(struct filter *filter, struct ncpaddr *local,
+ struct ncpaddr *remote, struct in_addr *dns)
+{
+ struct filterent *fp;
+ int n;
+
+ for (fp = filter->rule, n = 0; n < MAXFILTERS; fp++, n++)
+ if (fp->f_action != A_NONE) {
+ if (local) {
+ if (fp->f_srctype == T_MYADDR && ncpaddr_family(local) == AF_INET)
+ ncprange_sethost(&fp->f_src, local);
+ if (fp->f_dsttype == T_MYADDR && ncpaddr_family(local) == AF_INET)
+ ncprange_sethost(&fp->f_dst, local);
+#ifndef NOINET6
+ if (fp->f_srctype == T_MYADDR6 && ncpaddr_family(local) == AF_INET6)
+ ncprange_sethost(&fp->f_src, local);
+ if (fp->f_dsttype == T_MYADDR6 && ncpaddr_family(local) == AF_INET6)
+ ncprange_sethost(&fp->f_dst, local);
+#endif
+ }
+ if (remote) {
+ if (fp->f_srctype == T_HISADDR && ncpaddr_family(remote) == AF_INET)
+ ncprange_sethost(&fp->f_src, remote);
+ if (fp->f_dsttype == T_HISADDR && ncpaddr_family(remote) == AF_INET)
+ ncprange_sethost(&fp->f_dst, remote);
+#ifndef NOINET6
+ if (fp->f_srctype == T_HISADDR6 && ncpaddr_family(remote) == AF_INET6)
+ ncprange_sethost(&fp->f_src, remote);
+ if (fp->f_dsttype == T_HISADDR6 && ncpaddr_family(remote) == AF_INET6)
+ ncprange_sethost(&fp->f_dst, remote);
+#endif
+ }
+ if (dns) {
+ if (fp->f_srctype == T_DNS0)
+ ncprange_setip4host(&fp->f_src, dns[0]);
+ if (fp->f_dsttype == T_DNS0)
+ ncprange_setip4host(&fp->f_dst, dns[0]);
+ if (fp->f_srctype == T_DNS1)
+ ncprange_setip4host(&fp->f_src, dns[1]);
+ if (fp->f_dsttype == T_DNS1)
+ ncprange_setip4host(&fp->f_dst, dns[1]);
+ }
+ }
+}
diff --git a/usr.sbin/ppp/filter.h b/usr.sbin/ppp/filter.h
new file mode 100644
index 0000000..ce67420
--- /dev/null
+++ b/usr.sbin/ppp/filter.h
@@ -0,0 +1,101 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+/* Operations - f_srcop, f_dstop */
+#define OP_NONE 0
+#define OP_EQ 1
+#define OP_GT 2
+#define OP_LT 3
+
+/* srctype or dsttype */
+#define T_ADDR 0
+#define T_MYADDR 1
+#define T_MYADDR6 2
+#define T_HISADDR 3
+#define T_HISADDR6 4
+#define T_DNS0 5
+#define T_DNS1 6
+
+/*
+ * There's a struct filterent for each possible filter rule. The
+ * layout is designed to minimise size (there are 4 * MAXFILTERS of
+ * them) - which is also conveniently a power of 2 (32 bytes) on
+ * architectures where sizeof(int)==4 (this makes indexing faster).
+ *
+ * Note that there are four free bits in the initial word for future
+ * extensions.
+ */
+struct filterent {
+ int f_proto; /* Protocol: getprotoby*() */
+ unsigned f_action : 8; /* Filtering action: goto or A_... */
+ unsigned f_srcop : 2; /* Source port operation: OP_... */
+ unsigned f_dstop : 2; /* Destination port operation: OP_... */
+ unsigned f_srctype : 3; /* T_ value of src */
+ unsigned f_dsttype : 3; /* T_ value of dst */
+ unsigned f_estab : 1; /* Check TCP ACK bit */
+ unsigned f_syn : 1; /* Check TCP SYN bit */
+ unsigned f_finrst : 1; /* Check TCP FIN/RST bits */
+ unsigned f_invert : 1; /* true to complement match */
+ struct ncprange f_src; /* Source address and mask */
+ struct ncprange f_dst; /* Destination address and mask */
+ u_short f_srcport; /* Source port, compared with f_srcop */
+ u_short f_dstport; /* Destination port, compared with f_dstop */
+ unsigned timeout; /* Keep alive value for passed packet */
+};
+
+#define MAXFILTERS 40 /* in each filter set */
+
+/* f_action values [0..MAXFILTERS) specify the next filter rule, others are: */
+#define A_NONE (MAXFILTERS)
+#define A_PERMIT (A_NONE+1)
+#define A_DENY (A_PERMIT+1)
+
+struct filter {
+ struct filterent rule[MAXFILTERS]; /* incoming packet filter */
+ const char *name;
+ unsigned fragok : 1;
+ unsigned logok : 1;
+};
+
+/* Which filter set */
+#define FL_IN 0
+#define FL_OUT 1
+#define FL_DIAL 2
+#define FL_KEEP 3
+
+struct ipcp;
+struct cmdargs;
+
+extern int filter_Show(struct cmdargs const *);
+extern int filter_Set(struct cmdargs const *);
+extern const char * filter_Action2Nam(unsigned);
+extern const char *filter_Op2Nam(unsigned);
+extern void filter_AdjustAddr(struct filter *, struct ncpaddr *,
+ struct ncpaddr *, struct in_addr *);
diff --git a/usr.sbin/ppp/fsm.c b/usr.sbin/ppp/fsm.c
new file mode 100644
index 0000000..78530b9
--- /dev/null
+++ b/usr.sbin/ppp/fsm.c
@@ -0,0 +1,1213 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <string.h>
+#include <termios.h>
+
+#include "layer.h"
+#include "ua.h"
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "iplist.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "slcompress.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "async.h"
+#include "physical.h"
+#include "proto.h"
+
+static void FsmSendConfigReq(struct fsm *);
+static void FsmSendTerminateReq(struct fsm *);
+static void FsmInitRestartCounter(struct fsm *, int);
+
+typedef void (recvfn)(struct fsm *, struct fsmheader *, struct mbuf *);
+static recvfn FsmRecvConfigReq, FsmRecvConfigAck, FsmRecvConfigNak,
+ FsmRecvConfigRej, FsmRecvTermReq, FsmRecvTermAck,
+ FsmRecvCodeRej, FsmRecvProtoRej, FsmRecvEchoReq,
+ FsmRecvEchoRep, FsmRecvDiscReq, FsmRecvIdent,
+ FsmRecvTimeRemain, FsmRecvResetReq, FsmRecvResetAck;
+
+static const struct fsmcodedesc {
+ recvfn *recv;
+ unsigned check_reqid : 1;
+ unsigned inc_reqid : 1;
+ const char *name;
+} FsmCodes[] = {
+ { FsmRecvConfigReq, 0, 0, "ConfigReq" },
+ { FsmRecvConfigAck, 1, 1, "ConfigAck" },
+ { FsmRecvConfigNak, 1, 1, "ConfigNak" },
+ { FsmRecvConfigRej, 1, 1, "ConfigRej" },
+ { FsmRecvTermReq, 0, 0, "TerminateReq" },
+ { FsmRecvTermAck, 1, 1, "TerminateAck" },
+ { FsmRecvCodeRej, 0, 0, "CodeRej" },
+ { FsmRecvProtoRej, 0, 0, "ProtocolRej" },
+ { FsmRecvEchoReq, 0, 0, "EchoRequest" },
+ { FsmRecvEchoRep, 0, 0, "EchoReply" },
+ { FsmRecvDiscReq, 0, 0, "DiscardReq" },
+ { FsmRecvIdent, 0, 1, "Ident" },
+ { FsmRecvTimeRemain,0, 0, "TimeRemain" },
+ { FsmRecvResetReq, 0, 0, "ResetReq" },
+ { FsmRecvResetAck, 0, 1, "ResetAck" }
+};
+
+static const char *
+Code2Nam(u_int code)
+{
+ if (code == 0 || code > sizeof FsmCodes / sizeof FsmCodes[0])
+ return "Unknown";
+ return FsmCodes[code-1].name;
+}
+
+const char *
+State2Nam(u_int state)
+{
+ static const char * const StateNames[] = {
+ "Initial", "Starting", "Closed", "Stopped", "Closing", "Stopping",
+ "Req-Sent", "Ack-Rcvd", "Ack-Sent", "Opened",
+ };
+
+ if (state >= sizeof StateNames / sizeof StateNames[0])
+ return "unknown";
+ return StateNames[state];
+}
+
+static void
+StoppedTimeout(void *v)
+{
+ struct fsm *fp = (struct fsm *)v;
+
+ log_Printf(fp->LogLevel, "%s: Stopped timer expired\n", fp->link->name);
+ if (fp->OpenTimer.state == TIMER_RUNNING) {
+ log_Printf(LogWARN, "%s: %s: aborting open delay due to stopped timer\n",
+ fp->link->name, fp->name);
+ timer_Stop(&fp->OpenTimer);
+ }
+ if (fp->state == ST_STOPPED)
+ fsm2initial(fp);
+}
+
+void
+fsm_Init(struct fsm *fp, const char *name, u_short proto, int mincode,
+ int maxcode, int LogLevel, struct bundle *bundle,
+ struct link *l, const struct fsm_parent *parent,
+ struct fsm_callbacks *fn, const char * const timer_names[3])
+{
+ fp->name = name;
+ fp->proto = proto;
+ fp->min_code = mincode;
+ fp->max_code = maxcode;
+ fp->state = fp->min_code > CODE_TERMACK ? ST_OPENED : ST_INITIAL;
+ fp->reqid = 1;
+ fp->restart = 1;
+ fp->more.reqs = fp->more.naks = fp->more.rejs = 3;
+ memset(&fp->FsmTimer, '\0', sizeof fp->FsmTimer);
+ memset(&fp->OpenTimer, '\0', sizeof fp->OpenTimer);
+ memset(&fp->StoppedTimer, '\0', sizeof fp->StoppedTimer);
+ fp->LogLevel = LogLevel;
+ fp->link = l;
+ fp->bundle = bundle;
+ fp->parent = parent;
+ fp->fn = fn;
+ fp->FsmTimer.name = timer_names[0];
+ fp->OpenTimer.name = timer_names[1];
+ fp->StoppedTimer.name = timer_names[2];
+}
+
+static void
+NewState(struct fsm *fp, int new)
+{
+ log_Printf(fp->LogLevel, "%s: State change %s --> %s\n",
+ fp->link->name, State2Nam(fp->state), State2Nam(new));
+ if (fp->state == ST_STOPPED && fp->StoppedTimer.state == TIMER_RUNNING)
+ timer_Stop(&fp->StoppedTimer);
+ fp->state = new;
+ if ((new >= ST_INITIAL && new <= ST_STOPPED) || (new == ST_OPENED)) {
+ timer_Stop(&fp->FsmTimer);
+ if (new == ST_STOPPED && fp->StoppedTimer.load) {
+ timer_Stop(&fp->StoppedTimer);
+ fp->StoppedTimer.func = StoppedTimeout;
+ fp->StoppedTimer.arg = (void *) fp;
+ timer_Start(&fp->StoppedTimer);
+ }
+ }
+}
+
+void
+fsm_Output(struct fsm *fp, u_int code, u_int id, u_char *ptr, unsigned count,
+ int mtype)
+{
+ int plen;
+ struct fsmheader lh;
+ struct mbuf *bp;
+
+ if (log_IsKept(fp->LogLevel)) {
+ log_Printf(fp->LogLevel, "%s: Send%s(%d) state = %s\n",
+ fp->link->name, Code2Nam(code), id, State2Nam(fp->state));
+ switch (code) {
+ case CODE_CONFIGREQ:
+ case CODE_CONFIGACK:
+ case CODE_CONFIGREJ:
+ case CODE_CONFIGNAK:
+ (*fp->fn->DecodeConfig)(fp, ptr, ptr + count, MODE_NOP, NULL);
+ if (count < sizeof(struct fsm_opt_hdr))
+ log_Printf(fp->LogLevel, " [EMPTY]\n");
+ break;
+ }
+ }
+
+ plen = sizeof(struct fsmheader) + count;
+ lh.code = code;
+ lh.id = id;
+ lh.length = htons(plen);
+ bp = m_get(plen, mtype);
+ memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader));
+ if (count)
+ memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count);
+ log_DumpBp(LogDEBUG, "fsm_Output", bp);
+ link_PushPacket(fp->link, bp, fp->bundle, LINK_QUEUES(fp->link) - 1,
+ fp->proto);
+
+ if (code == CODE_CONFIGREJ)
+ lcp_SendIdentification(&fp->link->lcp);
+}
+
+static void
+FsmOpenNow(void *v)
+{
+ struct fsm *fp = (struct fsm *)v;
+
+ timer_Stop(&fp->OpenTimer);
+ if (fp->state <= ST_STOPPED) {
+ if (fp->state != ST_STARTING) {
+ /*
+ * In practice, we're only here in ST_STOPPED (when delaying the
+ * first config request) or ST_CLOSED (when openmode == 0).
+ *
+ * The ST_STOPPED bit is breaking the RFC already :-(
+ *
+ * According to the RFC (1661) state transition table, a TLS isn't
+ * required for an Open event when state == Closed, but the RFC
+ * must be wrong as TLS hasn't yet been called (since the last TLF)
+ * ie, Initial gets an `Up' event, Closing gets a RTA etc.
+ */
+ (*fp->fn->LayerStart)(fp);
+ (*fp->parent->LayerStart)(fp->parent->object, fp);
+ }
+ FsmInitRestartCounter(fp, FSM_REQ_TIMER);
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ }
+}
+
+void
+fsm_Open(struct fsm *fp)
+{
+ switch (fp->state) {
+ case ST_INITIAL:
+ NewState(fp, ST_STARTING);
+ (*fp->fn->LayerStart)(fp);
+ (*fp->parent->LayerStart)(fp->parent->object, fp);
+ break;
+ case ST_CLOSED:
+ if (fp->open_mode == OPEN_PASSIVE) {
+ NewState(fp, ST_STOPPED); /* XXX: This is a hack ! */
+ } else if (fp->open_mode > 0) {
+ if (fp->open_mode > 1)
+ log_Printf(LogPHASE, "%s: Entering STOPPED state for %d seconds\n",
+ fp->link->name, fp->open_mode);
+ NewState(fp, ST_STOPPED); /* XXX: This is a not-so-bad hack ! */
+ timer_Stop(&fp->OpenTimer);
+ fp->OpenTimer.load = fp->open_mode * SECTICKS;
+ fp->OpenTimer.func = FsmOpenNow;
+ fp->OpenTimer.arg = (void *)fp;
+ timer_Start(&fp->OpenTimer);
+ } else
+ FsmOpenNow(fp);
+ break;
+ case ST_STOPPED: /* XXX: restart option */
+ case ST_REQSENT:
+ case ST_ACKRCVD:
+ case ST_ACKSENT:
+ case ST_OPENED: /* XXX: restart option */
+ break;
+ case ST_CLOSING: /* XXX: restart option */
+ case ST_STOPPING: /* XXX: restart option */
+ NewState(fp, ST_STOPPING);
+ break;
+ }
+}
+
+void
+fsm_Up(struct fsm *fp)
+{
+ switch (fp->state) {
+ case ST_INITIAL:
+ log_Printf(fp->LogLevel, "FSM: Using \"%s\" as a transport\n",
+ fp->link->name);
+ NewState(fp, ST_CLOSED);
+ break;
+ case ST_STARTING:
+ FsmInitRestartCounter(fp, FSM_REQ_TIMER);
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ break;
+ default:
+ log_Printf(fp->LogLevel, "%s: Oops, Up at %s\n",
+ fp->link->name, State2Nam(fp->state));
+ break;
+ }
+}
+
+void
+fsm_Down(struct fsm *fp)
+{
+ switch (fp->state) {
+ case ST_CLOSED:
+ NewState(fp, ST_INITIAL);
+ break;
+ case ST_CLOSING:
+ /* This TLF contradicts the RFC (1661), which ``misses it out'' ! */
+ (*fp->fn->LayerFinish)(fp);
+ NewState(fp, ST_INITIAL);
+ (*fp->parent->LayerFinish)(fp->parent->object, fp);
+ break;
+ case ST_STOPPED:
+ NewState(fp, ST_STARTING);
+ (*fp->fn->LayerStart)(fp);
+ (*fp->parent->LayerStart)(fp->parent->object, fp);
+ break;
+ case ST_STOPPING:
+ case ST_REQSENT:
+ case ST_ACKRCVD:
+ case ST_ACKSENT:
+ NewState(fp, ST_STARTING);
+ break;
+ case ST_OPENED:
+ (*fp->fn->LayerDown)(fp);
+ NewState(fp, ST_STARTING);
+ (*fp->parent->LayerDown)(fp->parent->object, fp);
+ break;
+ }
+}
+
+void
+fsm_Close(struct fsm *fp)
+{
+ switch (fp->state) {
+ case ST_STARTING:
+ (*fp->fn->LayerFinish)(fp);
+ NewState(fp, ST_INITIAL);
+ (*fp->parent->LayerFinish)(fp->parent->object, fp);
+ break;
+ case ST_STOPPED:
+ NewState(fp, ST_CLOSED);
+ break;
+ case ST_STOPPING:
+ NewState(fp, ST_CLOSING);
+ break;
+ case ST_OPENED:
+ (*fp->fn->LayerDown)(fp);
+ if (fp->state == ST_OPENED) {
+ FsmInitRestartCounter(fp, FSM_TRM_TIMER);
+ FsmSendTerminateReq(fp);
+ NewState(fp, ST_CLOSING);
+ (*fp->parent->LayerDown)(fp->parent->object, fp);
+ }
+ break;
+ case ST_REQSENT:
+ case ST_ACKRCVD:
+ case ST_ACKSENT:
+ FsmInitRestartCounter(fp, FSM_TRM_TIMER);
+ FsmSendTerminateReq(fp);
+ NewState(fp, ST_CLOSING);
+ break;
+ }
+}
+
+/*
+ * Send functions
+ */
+static void
+FsmSendConfigReq(struct fsm *fp)
+{
+ if (fp->more.reqs-- > 0 && fp->restart-- > 0) {
+ (*fp->fn->SendConfigReq)(fp);
+ timer_Start(&fp->FsmTimer); /* Start restart timer */
+ } else {
+ if (fp->more.reqs < 0)
+ log_Printf(LogPHASE, "%s: Too many %s REQs sent - abandoning "
+ "negotiation\n", fp->link->name, fp->name);
+ lcp_SendIdentification(&fp->link->lcp);
+ fsm_Close(fp);
+ }
+}
+
+static void
+FsmSendTerminateReq(struct fsm *fp)
+{
+ fsm_Output(fp, CODE_TERMREQ, fp->reqid, NULL, 0, MB_UNKNOWN);
+ (*fp->fn->SentTerminateReq)(fp);
+ timer_Start(&fp->FsmTimer); /* Start restart timer */
+ fp->restart--; /* Decrement restart counter */
+}
+
+/*
+ * Timeout actions
+ */
+static void
+FsmTimeout(void *v)
+{
+ struct fsm *fp = (struct fsm *)v;
+
+ if (fp->restart) {
+ switch (fp->state) {
+ case ST_CLOSING:
+ case ST_STOPPING:
+ FsmSendTerminateReq(fp);
+ break;
+ case ST_REQSENT:
+ case ST_ACKSENT:
+ FsmSendConfigReq(fp);
+ break;
+ case ST_ACKRCVD:
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ break;
+ }
+ timer_Start(&fp->FsmTimer);
+ } else {
+ switch (fp->state) {
+ case ST_CLOSING:
+ (*fp->fn->LayerFinish)(fp);
+ NewState(fp, ST_CLOSED);
+ (*fp->parent->LayerFinish)(fp->parent->object, fp);
+ break;
+ case ST_STOPPING:
+ (*fp->fn->LayerFinish)(fp);
+ NewState(fp, ST_STOPPED);
+ (*fp->parent->LayerFinish)(fp->parent->object, fp);
+ break;
+ case ST_REQSENT: /* XXX: 3p */
+ case ST_ACKSENT:
+ case ST_ACKRCVD:
+ (*fp->fn->LayerFinish)(fp);
+ NewState(fp, ST_STOPPED);
+ (*fp->parent->LayerFinish)(fp->parent->object, fp);
+ break;
+ }
+ }
+}
+
+static void
+FsmInitRestartCounter(struct fsm *fp, int what)
+{
+ timer_Stop(&fp->FsmTimer);
+ fp->FsmTimer.func = FsmTimeout;
+ fp->FsmTimer.arg = (void *)fp;
+ (*fp->fn->InitRestartCounter)(fp, what);
+}
+
+/*
+ * Actions when receive packets
+ */
+static void
+FsmRecvConfigReq(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+/* RCR */
+{
+ struct fsm_decode dec;
+ int plen, flen;
+ int ackaction = 0;
+ u_char *cp;
+
+ bp = m_pullup(bp);
+ plen = m_length(bp);
+ flen = ntohs(lhp->length) - sizeof *lhp;
+ if (plen < flen) {
+ log_Printf(LogWARN, "%s: FsmRecvConfigReq: plen (%d) < flen (%d)\n",
+ fp->link->name, plen, flen);
+ m_freem(bp);
+ return;
+ }
+
+ /* Some things must be done before we Decode the packet */
+ switch (fp->state) {
+ case ST_OPENED:
+ (*fp->fn->LayerDown)(fp);
+ }
+
+ dec.ackend = dec.ack;
+ dec.nakend = dec.nak;
+ dec.rejend = dec.rej;
+ cp = MBUF_CTOP(bp);
+ (*fp->fn->DecodeConfig)(fp, cp, cp + flen, MODE_REQ, &dec);
+ if (flen < (int)sizeof(struct fsm_opt_hdr))
+ log_Printf(fp->LogLevel, " [EMPTY]\n");
+
+ if (dec.nakend == dec.nak && dec.rejend == dec.rej)
+ ackaction = 1;
+
+ /* Check and process easy case */
+ switch (fp->state) {
+ case ST_INITIAL:
+ if (fp->proto == PROTO_CCP && fp->link->lcp.fsm.state == ST_OPENED) {
+ /*
+ * ccp_SetOpenMode() leaves us in initial if we're disabling
+ * & denying everything.
+ */
+ bp = m_prepend(bp, lhp, sizeof *lhp, 2);
+ bp = proto_Prepend(bp, fp->proto, 0, 0);
+ bp = m_pullup(bp);
+ lcp_SendProtoRej(&fp->link->lcp, MBUF_CTOP(bp), bp->m_len);
+ m_freem(bp);
+ return;
+ }
+ /* Drop through */
+ case ST_STARTING:
+ log_Printf(fp->LogLevel, "%s: Oops, RCR in %s.\n",
+ fp->link->name, State2Nam(fp->state));
+ m_freem(bp);
+ return;
+ case ST_CLOSED:
+ (*fp->fn->SendTerminateAck)(fp, lhp->id);
+ m_freem(bp);
+ return;
+ case ST_CLOSING:
+ log_Printf(fp->LogLevel, "%s: Error: Got ConfigReq while state = %s\n",
+ fp->link->name, State2Nam(fp->state));
+ case ST_STOPPING:
+ m_freem(bp);
+ return;
+ case ST_STOPPED:
+ FsmInitRestartCounter(fp, FSM_REQ_TIMER);
+ /* Drop through */
+ case ST_OPENED:
+ FsmSendConfigReq(fp);
+ break;
+ }
+
+ if (dec.rejend != dec.rej)
+ fsm_Output(fp, CODE_CONFIGREJ, lhp->id, dec.rej, dec.rejend - dec.rej,
+ MB_UNKNOWN);
+ if (dec.nakend != dec.nak)
+ fsm_Output(fp, CODE_CONFIGNAK, lhp->id, dec.nak, dec.nakend - dec.nak,
+ MB_UNKNOWN);
+ if (ackaction)
+ fsm_Output(fp, CODE_CONFIGACK, lhp->id, dec.ack, dec.ackend - dec.ack,
+ MB_UNKNOWN);
+
+ switch (fp->state) {
+ case ST_STOPPED:
+ /*
+ * According to the RFC (1661) state transition table, a TLS isn't
+ * required for a RCR when state == ST_STOPPED, but the RFC
+ * must be wrong as TLS hasn't yet been called (since the last TLF)
+ */
+ (*fp->fn->LayerStart)(fp);
+ (*fp->parent->LayerStart)(fp->parent->object, fp);
+ /* FALLTHROUGH */
+
+ case ST_OPENED:
+ if (ackaction)
+ NewState(fp, ST_ACKSENT);
+ else
+ NewState(fp, ST_REQSENT);
+ (*fp->parent->LayerDown)(fp->parent->object, fp);
+ break;
+ case ST_REQSENT:
+ if (ackaction)
+ NewState(fp, ST_ACKSENT);
+ break;
+ case ST_ACKRCVD:
+ if (ackaction) {
+ NewState(fp, ST_OPENED);
+ if ((*fp->fn->LayerUp)(fp))
+ (*fp->parent->LayerUp)(fp->parent->object, fp);
+ else {
+ (*fp->fn->LayerDown)(fp);
+ FsmInitRestartCounter(fp, FSM_TRM_TIMER);
+ FsmSendTerminateReq(fp);
+ NewState(fp, ST_CLOSING);
+ lcp_SendIdentification(&fp->link->lcp);
+ }
+ }
+ break;
+ case ST_ACKSENT:
+ if (!ackaction)
+ NewState(fp, ST_REQSENT);
+ break;
+ }
+ m_freem(bp);
+
+ if (dec.rejend != dec.rej && --fp->more.rejs <= 0) {
+ log_Printf(LogPHASE, "%s: Too many %s REJs sent - abandoning negotiation\n",
+ fp->link->name, fp->name);
+ lcp_SendIdentification(&fp->link->lcp);
+ fsm_Close(fp);
+ }
+
+ if (dec.nakend != dec.nak && --fp->more.naks <= 0) {
+ log_Printf(LogPHASE, "%s: Too many %s NAKs sent - abandoning negotiation\n",
+ fp->link->name, fp->name);
+ lcp_SendIdentification(&fp->link->lcp);
+ fsm_Close(fp);
+ }
+}
+
+static void
+FsmRecvConfigAck(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+/* RCA */
+{
+ struct fsm_decode dec;
+ int plen, flen;
+ u_char *cp;
+
+ plen = m_length(bp);
+ flen = ntohs(lhp->length) - sizeof *lhp;
+ if (plen < flen) {
+ m_freem(bp);
+ return;
+ }
+
+ bp = m_pullup(bp);
+ dec.ackend = dec.ack;
+ dec.nakend = dec.nak;
+ dec.rejend = dec.rej;
+ cp = MBUF_CTOP(bp);
+ (*fp->fn->DecodeConfig)(fp, cp, cp + flen, MODE_ACK, &dec);
+ if (flen < (int)sizeof(struct fsm_opt_hdr))
+ log_Printf(fp->LogLevel, " [EMPTY]\n");
+
+ switch (fp->state) {
+ case ST_CLOSED:
+ case ST_STOPPED:
+ (*fp->fn->SendTerminateAck)(fp, lhp->id);
+ break;
+ case ST_CLOSING:
+ case ST_STOPPING:
+ break;
+ case ST_REQSENT:
+ FsmInitRestartCounter(fp, FSM_REQ_TIMER);
+ NewState(fp, ST_ACKRCVD);
+ break;
+ case ST_ACKRCVD:
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ break;
+ case ST_ACKSENT:
+ FsmInitRestartCounter(fp, FSM_REQ_TIMER);
+ NewState(fp, ST_OPENED);
+ if ((*fp->fn->LayerUp)(fp))
+ (*fp->parent->LayerUp)(fp->parent->object, fp);
+ else {
+ (*fp->fn->LayerDown)(fp);
+ FsmInitRestartCounter(fp, FSM_TRM_TIMER);
+ FsmSendTerminateReq(fp);
+ NewState(fp, ST_CLOSING);
+ lcp_SendIdentification(&fp->link->lcp);
+ }
+ break;
+ case ST_OPENED:
+ (*fp->fn->LayerDown)(fp);
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ (*fp->parent->LayerDown)(fp->parent->object, fp);
+ break;
+ }
+ m_freem(bp);
+}
+
+static void
+FsmRecvConfigNak(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+/* RCN */
+{
+ struct fsm_decode dec;
+ int plen, flen;
+ u_char *cp;
+
+ plen = m_length(bp);
+ flen = ntohs(lhp->length) - sizeof *lhp;
+ if (plen < flen) {
+ m_freem(bp);
+ return;
+ }
+
+ /*
+ * Check and process easy case
+ */
+ switch (fp->state) {
+ case ST_INITIAL:
+ case ST_STARTING:
+ log_Printf(fp->LogLevel, "%s: Oops, RCN in %s.\n",
+ fp->link->name, State2Nam(fp->state));
+ m_freem(bp);
+ return;
+ case ST_CLOSED:
+ case ST_STOPPED:
+ (*fp->fn->SendTerminateAck)(fp, lhp->id);
+ m_freem(bp);
+ return;
+ case ST_CLOSING:
+ case ST_STOPPING:
+ m_freem(bp);
+ return;
+ }
+
+ bp = m_pullup(bp);
+ dec.ackend = dec.ack;
+ dec.nakend = dec.nak;
+ dec.rejend = dec.rej;
+ cp = MBUF_CTOP(bp);
+ (*fp->fn->DecodeConfig)(fp, cp, cp + flen, MODE_NAK, &dec);
+ if (flen < (int)sizeof(struct fsm_opt_hdr))
+ log_Printf(fp->LogLevel, " [EMPTY]\n");
+
+ switch (fp->state) {
+ case ST_REQSENT:
+ case ST_ACKSENT:
+ FsmInitRestartCounter(fp, FSM_REQ_TIMER);
+ FsmSendConfigReq(fp);
+ break;
+ case ST_OPENED:
+ (*fp->fn->LayerDown)(fp);
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ (*fp->parent->LayerDown)(fp->parent->object, fp);
+ break;
+ case ST_ACKRCVD:
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ break;
+ }
+
+ m_freem(bp);
+}
+
+static void
+FsmRecvTermReq(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+/* RTR */
+{
+ switch (fp->state) {
+ case ST_INITIAL:
+ case ST_STARTING:
+ log_Printf(fp->LogLevel, "%s: Oops, RTR in %s\n",
+ fp->link->name, State2Nam(fp->state));
+ break;
+ case ST_CLOSED:
+ case ST_STOPPED:
+ case ST_CLOSING:
+ case ST_STOPPING:
+ case ST_REQSENT:
+ (*fp->fn->SendTerminateAck)(fp, lhp->id);
+ break;
+ case ST_ACKRCVD:
+ case ST_ACKSENT:
+ (*fp->fn->SendTerminateAck)(fp, lhp->id);
+ NewState(fp, ST_REQSENT);
+ break;
+ case ST_OPENED:
+ (*fp->fn->LayerDown)(fp);
+ (*fp->fn->SendTerminateAck)(fp, lhp->id);
+ FsmInitRestartCounter(fp, FSM_TRM_TIMER);
+ timer_Start(&fp->FsmTimer); /* Start restart timer */
+ fp->restart = 0;
+ NewState(fp, ST_STOPPING);
+ (*fp->parent->LayerDown)(fp->parent->object, fp);
+ /* A delayed ST_STOPPED is now scheduled */
+ break;
+ }
+ m_freem(bp);
+}
+
+static void
+FsmRecvTermAck(struct fsm *fp, struct fsmheader *lhp __unused, struct mbuf *bp)
+/* RTA */
+{
+ switch (fp->state) {
+ case ST_CLOSING:
+ (*fp->fn->LayerFinish)(fp);
+ NewState(fp, ST_CLOSED);
+ (*fp->parent->LayerFinish)(fp->parent->object, fp);
+ break;
+ case ST_STOPPING:
+ (*fp->fn->LayerFinish)(fp);
+ NewState(fp, ST_STOPPED);
+ (*fp->parent->LayerFinish)(fp->parent->object, fp);
+ break;
+ case ST_ACKRCVD:
+ NewState(fp, ST_REQSENT);
+ break;
+ case ST_OPENED:
+ (*fp->fn->LayerDown)(fp);
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ (*fp->parent->LayerDown)(fp->parent->object, fp);
+ break;
+ }
+ m_freem(bp);
+}
+
+static void
+FsmRecvConfigRej(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+/* RCJ */
+{
+ struct fsm_decode dec;
+ size_t plen;
+ int flen;
+ u_char *cp;
+
+ plen = m_length(bp);
+ flen = ntohs(lhp->length) - sizeof *lhp;
+ if ((int)plen < flen) {
+ m_freem(bp);
+ return;
+ }
+
+ lcp_SendIdentification(&fp->link->lcp);
+
+ /*
+ * Check and process easy case
+ */
+ switch (fp->state) {
+ case ST_INITIAL:
+ case ST_STARTING:
+ log_Printf(fp->LogLevel, "%s: Oops, RCJ in %s.\n",
+ fp->link->name, State2Nam(fp->state));
+ m_freem(bp);
+ return;
+ case ST_CLOSED:
+ case ST_STOPPED:
+ (*fp->fn->SendTerminateAck)(fp, lhp->id);
+ m_freem(bp);
+ return;
+ case ST_CLOSING:
+ case ST_STOPPING:
+ m_freem(bp);
+ return;
+ }
+
+ bp = m_pullup(bp);
+ dec.ackend = dec.ack;
+ dec.nakend = dec.nak;
+ dec.rejend = dec.rej;
+ cp = MBUF_CTOP(bp);
+ (*fp->fn->DecodeConfig)(fp, cp, cp + flen, MODE_REJ, &dec);
+ if (flen < (int)sizeof(struct fsm_opt_hdr))
+ log_Printf(fp->LogLevel, " [EMPTY]\n");
+
+ switch (fp->state) {
+ case ST_REQSENT:
+ case ST_ACKSENT:
+ FsmInitRestartCounter(fp, FSM_REQ_TIMER);
+ FsmSendConfigReq(fp);
+ break;
+ case ST_OPENED:
+ (*fp->fn->LayerDown)(fp);
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ (*fp->parent->LayerDown)(fp->parent->object, fp);
+ break;
+ case ST_ACKRCVD:
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ break;
+ }
+ m_freem(bp);
+}
+
+static void
+FsmRecvCodeRej(struct fsm *fp __unused, struct fsmheader *lhp __unused,
+ struct mbuf *bp)
+{
+ m_freem(bp);
+}
+
+static void
+FsmRecvProtoRej(struct fsm *fp, struct fsmheader *lhp __unused, struct mbuf *bp)
+{
+ struct physical *p = link2physical(fp->link);
+ u_short proto;
+
+ if (m_length(bp) < 2) {
+ m_freem(bp);
+ return;
+ }
+ bp = mbuf_Read(bp, &proto, 2);
+ proto = ntohs(proto);
+ log_Printf(fp->LogLevel, "%s: -- Protocol 0x%04x (%s) was rejected!\n",
+ fp->link->name, proto, hdlc_Protocol2Nam(proto));
+
+ switch (proto) {
+ case PROTO_LQR:
+ if (p)
+ lqr_Stop(p, LQM_LQR);
+ else
+ log_Printf(LogERROR, "%s: FsmRecvProtoRej: Not a physical link !\n",
+ fp->link->name);
+ break;
+ case PROTO_CCP:
+ if (fp->proto == PROTO_LCP) {
+ fp = &fp->link->ccp.fsm;
+ /* Despite the RFC (1661), don't do an out-of-place TLF */
+ /* (*fp->fn->LayerFinish)(fp); */
+ switch (fp->state) {
+ case ST_CLOSED:
+ case ST_CLOSING:
+ NewState(fp, ST_CLOSED);
+ break;
+ default:
+ NewState(fp, ST_STOPPED);
+ break;
+ }
+ /* See above */
+ /* (*fp->parent->LayerFinish)(fp->parent->object, fp); */
+ }
+ break;
+ case PROTO_IPCP:
+ if (fp->proto == PROTO_LCP) {
+ log_Printf(LogPHASE, "%s: IPCP protocol reject closes IPCP !\n",
+ fp->link->name);
+ fsm_Close(&fp->bundle->ncp.ipcp.fsm);
+ }
+ break;
+#ifndef NOINET6
+ case PROTO_IPV6CP:
+ if (fp->proto == PROTO_LCP) {
+ log_Printf(LogPHASE, "%s: IPV6CP protocol reject closes IPV6CP !\n",
+ fp->link->name);
+ fsm_Close(&fp->bundle->ncp.ipv6cp.fsm);
+ }
+ break;
+#endif
+ case PROTO_MP:
+ if (fp->proto == PROTO_LCP) {
+ struct lcp *lcp = fsm2lcp(fp);
+
+ if (lcp->want_mrru && lcp->his_mrru) {
+ log_Printf(LogPHASE, "%s: MP protocol reject is fatal !\n",
+ fp->link->name);
+ fsm_Close(fp);
+ }
+ }
+ break;
+ }
+ m_freem(bp);
+}
+
+static void
+FsmRecvEchoReq(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+{
+ struct lcp *lcp = fsm2lcp(fp);
+ u_char *cp;
+ u_int32_t magic;
+
+ bp = m_pullup(bp);
+ m_settype(bp, MB_ECHOIN);
+
+ if (lcp && ntohs(lhp->length) - sizeof *lhp >= 4) {
+ cp = MBUF_CTOP(bp);
+ ua_ntohl(cp, &magic);
+ if (magic != lcp->his_magic) {
+ log_Printf(fp->LogLevel, "%s: RecvEchoReq: magic 0x%08lx is wrong,"
+ " expecting 0x%08lx\n", fp->link->name, (u_long)magic,
+ (u_long)lcp->his_magic);
+ /* XXX: We should send terminate request */
+ }
+ if (fp->state == ST_OPENED) {
+ ua_htonl(&lcp->want_magic, cp); /* local magic */
+ fsm_Output(fp, CODE_ECHOREP, lhp->id, cp,
+ ntohs(lhp->length) - sizeof *lhp, MB_ECHOOUT);
+ }
+ }
+ m_freem(bp);
+}
+
+static void
+FsmRecvEchoRep(struct fsm *fp, struct fsmheader *lhp __unused, struct mbuf *bp)
+{
+ if (fsm2lcp(fp))
+ bp = lqr_RecvEcho(fp, bp);
+
+ m_freem(bp);
+}
+
+static void
+FsmRecvDiscReq(struct fsm *fp __unused, struct fsmheader *lhp __unused,
+ struct mbuf *bp)
+{
+ m_freem(bp);
+}
+
+static void
+FsmRecvIdent(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+{
+ u_int32_t magic;
+ u_short len;
+ u_char *cp;
+
+ len = ntohs(lhp->length) - sizeof *lhp;
+ if (len >= 4) {
+ bp = m_pullup(m_append(bp, "", 1));
+ cp = MBUF_CTOP(bp);
+ ua_ntohl(cp, &magic);
+ if (magic != fp->link->lcp.his_magic)
+ log_Printf(fp->LogLevel, "%s: RecvIdent: magic 0x%08lx is wrong,"
+ " expecting 0x%08lx\n", fp->link->name, (u_long)magic,
+ (u_long)fp->link->lcp.his_magic);
+ cp[len] = '\0';
+ lcp_RecvIdentification(&fp->link->lcp, cp + 4);
+ }
+ m_freem(bp);
+}
+
+static void
+FsmRecvTimeRemain(struct fsm *fp __unused, struct fsmheader *lhp __unused,
+ struct mbuf *bp)
+{
+ m_freem(bp);
+}
+
+static void
+FsmRecvResetReq(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+{
+ if ((*fp->fn->RecvResetReq)(fp)) {
+ /*
+ * All sendable compressed packets are queued in the first (lowest
+ * priority) modem output queue.... dump 'em to the priority queue
+ * so that they arrive at the peer before our ResetAck.
+ */
+ link_SequenceQueue(fp->link);
+ fsm_Output(fp, CODE_RESETACK, lhp->id, NULL, 0, MB_CCPOUT);
+ }
+ m_freem(bp);
+}
+
+static void
+FsmRecvResetAck(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+{
+ (*fp->fn->RecvResetAck)(fp, lhp->id);
+ m_freem(bp);
+}
+
+void
+fsm_Input(struct fsm *fp, struct mbuf *bp)
+{
+ size_t len;
+ struct fsmheader lh;
+ const struct fsmcodedesc *codep;
+
+ len = m_length(bp);
+ if (len < sizeof(struct fsmheader)) {
+ m_freem(bp);
+ return;
+ }
+ bp = mbuf_Read(bp, &lh, sizeof lh);
+
+ if (ntohs(lh.length) > len) {
+ log_Printf(LogWARN, "%s: Oops: Got %zu bytes but %d byte payload "
+ "- dropped\n", fp->link->name, len, (int)ntohs(lh.length));
+ m_freem(bp);
+ return;
+ }
+
+ if (lh.code < fp->min_code || lh.code > fp->max_code ||
+ lh.code > sizeof FsmCodes / sizeof *FsmCodes) {
+ /*
+ * Use a private id. This is really a response-type packet, but we
+ * MUST send a unique id for each REQ....
+ */
+ static u_char id;
+
+ bp = m_prepend(bp, &lh, sizeof lh, 0);
+ bp = m_pullup(bp);
+ fsm_Output(fp, CODE_CODEREJ, id++, MBUF_CTOP(bp), bp->m_len, MB_UNKNOWN);
+ m_freem(bp);
+ return;
+ }
+
+ codep = FsmCodes + lh.code - 1;
+ if (lh.id != fp->reqid && codep->check_reqid &&
+ Enabled(fp->bundle, OPT_IDCHECK)) {
+ log_Printf(fp->LogLevel, "%s: Recv%s(%d), dropped (expected %d)\n",
+ fp->link->name, codep->name, lh.id, fp->reqid);
+ return;
+ }
+
+ log_Printf(fp->LogLevel, "%s: Recv%s(%d) state = %s\n",
+ fp->link->name, codep->name, lh.id, State2Nam(fp->state));
+
+ if (codep->inc_reqid && (lh.id == fp->reqid ||
+ (!Enabled(fp->bundle, OPT_IDCHECK) && codep->check_reqid)))
+ fp->reqid++; /* That's the end of that ``exchange''.... */
+
+ (*codep->recv)(fp, &lh, bp);
+}
+
+int
+fsm_NullRecvResetReq(struct fsm *fp)
+{
+ log_Printf(fp->LogLevel, "%s: Oops - received unexpected reset req\n",
+ fp->link->name);
+ return 1;
+}
+
+void
+fsm_NullRecvResetAck(struct fsm *fp, u_char id __unused)
+{
+ log_Printf(fp->LogLevel, "%s: Oops - received unexpected reset ack\n",
+ fp->link->name);
+}
+
+void
+fsm_Reopen(struct fsm *fp)
+{
+ if (fp->state == ST_OPENED) {
+ (*fp->fn->LayerDown)(fp);
+ FsmInitRestartCounter(fp, FSM_REQ_TIMER);
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ (*fp->parent->LayerDown)(fp->parent->object, fp);
+ }
+}
+
+void
+fsm2initial(struct fsm *fp)
+{
+ timer_Stop(&fp->FsmTimer);
+ timer_Stop(&fp->OpenTimer);
+ timer_Stop(&fp->StoppedTimer);
+ if (fp->state == ST_STOPPED)
+ fsm_Close(fp);
+ if (fp->state > ST_INITIAL)
+ fsm_Down(fp);
+ if (fp->state > ST_INITIAL)
+ fsm_Close(fp);
+}
+
+struct fsm_opt *
+fsm_readopt(u_char **cp)
+{
+ struct fsm_opt *o = (struct fsm_opt *)*cp;
+
+ if (o->hdr.len < sizeof(struct fsm_opt_hdr)) {
+ log_Printf(LogERROR, "Bad option length %d (out of phase?)\n", o->hdr.len);
+ return NULL;
+ }
+
+ *cp += o->hdr.len;
+
+ if (o->hdr.len > sizeof(struct fsm_opt)) {
+ log_Printf(LogERROR, "Warning: Truncating option length from %d to %d\n",
+ o->hdr.len, (int)sizeof(struct fsm_opt));
+ o->hdr.len = sizeof(struct fsm_opt);
+ }
+
+ return o;
+}
+
+static int
+fsm_opt(u_char *opt, int optlen, const struct fsm_opt *o)
+{
+ unsigned cplen = o->hdr.len;
+
+ if (optlen < (int)sizeof(struct fsm_opt_hdr))
+ optlen = 0;
+
+ if ((int)cplen > optlen) {
+ log_Printf(LogERROR, "Can't REJ length %d - trunating to %d\n",
+ cplen, optlen);
+ cplen = optlen;
+ }
+ memcpy(opt, o, cplen);
+ if (cplen)
+ opt[1] = cplen;
+
+ return cplen;
+}
+
+void
+fsm_rej(struct fsm_decode *dec, const struct fsm_opt *o)
+{
+ if (!dec)
+ return;
+ dec->rejend += fsm_opt(dec->rejend, FSM_OPTLEN - (dec->rejend - dec->rej), o);
+}
+
+void
+fsm_ack(struct fsm_decode *dec, const struct fsm_opt *o)
+{
+ if (!dec)
+ return;
+ dec->ackend += fsm_opt(dec->ackend, FSM_OPTLEN - (dec->ackend - dec->ack), o);
+}
+
+void
+fsm_nak(struct fsm_decode *dec, const struct fsm_opt *o)
+{
+ if (!dec)
+ return;
+ dec->nakend += fsm_opt(dec->nakend, FSM_OPTLEN - (dec->nakend - dec->nak), o);
+}
+
+void
+fsm_opt_normalise(struct fsm_decode *dec)
+{
+ if (dec->rejend != dec->rej) {
+ /* rejects are preferred */
+ dec->ackend = dec->ack;
+ dec->nakend = dec->nak;
+ } else if (dec->nakend != dec->nak)
+ /* then NAKs */
+ dec->ackend = dec->ack;
+}
diff --git a/usr.sbin/ppp/fsm.h b/usr.sbin/ppp/fsm.h
new file mode 100644
index 0000000..d233899
--- /dev/null
+++ b/usr.sbin/ppp/fsm.h
@@ -0,0 +1,201 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+/*
+ * State of machine
+ */
+#define ST_INITIAL 0
+#define ST_STARTING 1
+#define ST_CLOSED 2
+#define ST_STOPPED 3
+#define ST_CLOSING 4
+#define ST_STOPPING 5
+#define ST_REQSENT 6
+#define ST_ACKRCVD 7
+#define ST_ACKSENT 8
+#define ST_OPENED 9
+
+#define ST_MAX 10
+#define ST_UNDEF -1
+
+#define MODE_REQ 0
+#define MODE_NAK 1
+#define MODE_REJ 2
+#define MODE_NOP 3
+#define MODE_ACK 4 /* pseudo mode for ccp negotiations */
+
+#define OPEN_PASSIVE -1
+
+#define FSM_REQ_TIMER 1
+#define FSM_TRM_TIMER 2
+
+#define FSM_OPTLEN 100
+
+struct fsm;
+
+struct fsm_retry {
+ u_int timeout; /* FSM retry frequency */
+ u_int maxreq; /* Max Config REQ retries */
+ u_int maxtrm; /* Max Term REQ retries */
+};
+
+struct fsm_decode {
+ u_char ack[FSM_OPTLEN], *ackend;
+ u_char nak[FSM_OPTLEN], *nakend;
+ u_char rej[FSM_OPTLEN], *rejend;
+};
+
+struct fsm_callbacks {
+ int (*LayerUp)(struct fsm *); /* Layer is now up (tlu) */
+ void (*LayerDown)(struct fsm *); /* About to come down (tld) */
+ void (*LayerStart)(struct fsm *); /* Layer about to start (tls) */
+ void (*LayerFinish)(struct fsm *); /* Layer now down (tlf) */
+ void (*InitRestartCounter)(struct fsm *, int);/* Set fsm timer load */
+ void (*SendConfigReq)(struct fsm *); /* Send REQ please */
+ void (*SentTerminateReq)(struct fsm *); /* Term REQ just sent */
+ void (*SendTerminateAck)(struct fsm *, u_char); /* Send Term ACK please */
+ void (*DecodeConfig)(struct fsm *, u_char *, u_char *, int,
+ struct fsm_decode *); /* Deal with incoming data */
+ int (*RecvResetReq)(struct fsm *fp); /* Reset output */
+ void (*RecvResetAck)(struct fsm *fp, u_char); /* Reset input */
+};
+
+struct fsm_parent {
+ void (*LayerStart) (void *, struct fsm *); /* tls */
+ void (*LayerUp) (void *, struct fsm *); /* tlu */
+ void (*LayerDown) (void *, struct fsm *); /* tld */
+ void (*LayerFinish) (void *, struct fsm *); /* tlf */
+ void *object;
+};
+
+struct link;
+struct bundle;
+
+struct fsm {
+ const char *name; /* Name of protocol */
+ u_short proto; /* Protocol number */
+ u_short min_code;
+ u_short max_code;
+ int open_mode; /* Delay before config REQ (-1 forever) */
+ unsigned state; /* State of the machine */
+ u_char reqid; /* Next request id */
+ int restart; /* Restart counter value */
+
+ struct {
+ int reqs; /* Max config REQs before a close() */
+ int naks; /* Max config NAKs before a close() */
+ int rejs; /* Max config REJs before a close() */
+ } more;
+
+ struct pppTimer FsmTimer; /* Restart Timer */
+ struct pppTimer OpenTimer; /* Delay before opening */
+
+ /*
+ * This timer times the ST_STOPPED state out after the given value
+ * (specified via "set stopped ..."). Although this isn't specified in the
+ * rfc, the rfc *does* say that "the application may use higher level
+ * timers to avoid deadlock". The StoppedTimer takes effect when the other
+ * side ABENDs rather than going into ST_ACKSENT (and sending the ACK),
+ * causing ppp to time out and drop into ST_STOPPED. At this point,
+ * nothing will change this state :-(
+ */
+ struct pppTimer StoppedTimer;
+ int LogLevel;
+
+ /* The link layer active with this FSM (may be our bundle below) */
+ struct link *link;
+
+ /* Our high-level link */
+ struct bundle *bundle;
+
+ const struct fsm_parent *parent;
+ const struct fsm_callbacks *fn;
+};
+
+struct fsmheader {
+ u_char code; /* Request code */
+ u_char id; /* Identification */
+ u_short length; /* Length of packet */
+};
+
+#define CODE_CONFIGREQ 1
+#define CODE_CONFIGACK 2
+#define CODE_CONFIGNAK 3
+#define CODE_CONFIGREJ 4
+#define CODE_TERMREQ 5
+#define CODE_TERMACK 6
+#define CODE_CODEREJ 7
+#define CODE_PROTOREJ 8
+#define CODE_ECHOREQ 9 /* Used in LCP */
+#define CODE_ECHOREP 10 /* Used in LCP */
+#define CODE_DISCREQ 11
+#define CODE_IDENT 12 /* Used in LCP Extension */
+#define CODE_TIMEREM 13 /* Used in LCP Extension */
+#define CODE_RESETREQ 14 /* Used in CCP */
+#define CODE_RESETACK 15 /* Used in CCP */
+
+struct fsm_opt_hdr {
+ u_char id;
+ u_char len;
+} __packed;
+
+#define MAX_FSM_OPT_LEN 52
+struct fsm_opt {
+ struct fsm_opt_hdr hdr;
+ u_char data[MAX_FSM_OPT_LEN-2];
+};
+
+#define INC_FSM_OPT(ty, length, o) \
+ do { \
+ (o)->hdr.id = (ty); \
+ (o)->hdr.len = (length); \
+ (o) = (struct fsm_opt *)((u_char *)(o) + (length)); \
+ } while (0)
+
+
+extern void fsm_Init(struct fsm *, const char *, u_short, int, int, int,
+ struct bundle *, struct link *, const struct fsm_parent *,
+ struct fsm_callbacks *, const char * const [3]);
+extern void fsm_Output(struct fsm *, u_int, u_int, u_char *, unsigned, int);
+extern void fsm_Open(struct fsm *);
+extern void fsm_Up(struct fsm *);
+extern void fsm_Down(struct fsm *);
+extern void fsm_Input(struct fsm *, struct mbuf *);
+extern void fsm_Close(struct fsm *);
+extern int fsm_NullRecvResetReq(struct fsm *);
+extern void fsm_NullRecvResetAck(struct fsm *, u_char);
+extern void fsm_Reopen(struct fsm *);
+extern void fsm2initial(struct fsm *);
+extern const char *State2Nam(u_int);
+extern struct fsm_opt *fsm_readopt(u_char **);
+extern void fsm_rej(struct fsm_decode *, const struct fsm_opt *);
+extern void fsm_ack(struct fsm_decode *, const struct fsm_opt *);
+extern void fsm_nak(struct fsm_decode *, const struct fsm_opt *);
+extern void fsm_opt_normalise(struct fsm_decode *);
diff --git a/usr.sbin/ppp/hdlc.c b/usr.sbin/ppp/hdlc.c
new file mode 100644
index 0000000..517c3c5
--- /dev/null
+++ b/usr.sbin/ppp/hdlc.c
@@ -0,0 +1,438 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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/un.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "defs.h"
+#include "layer.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "auth.h"
+#include "lcp.h"
+#include "async.h"
+#include "ccp.h"
+#include "link.h"
+#include "descriptor.h"
+#include "chap.h"
+#include "physical.h"
+#include "prompt.h"
+#include "chat.h"
+#include "mp.h"
+#include "cbcp.h"
+#include "datalink.h"
+
+static u_int16_t const fcstab[256] = {
+ /* 00 */ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ /* 08 */ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ /* 10 */ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ /* 18 */ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ /* 20 */ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ /* 28 */ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ /* 30 */ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ /* 38 */ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ /* 40 */ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ /* 48 */ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ /* 50 */ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ /* 58 */ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ /* 60 */ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ /* 68 */ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ /* 70 */ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ /* 78 */ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ /* 80 */ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ /* 88 */ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ /* 90 */ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ /* 98 */ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ /* a0 */ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ /* a8 */ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ /* b0 */ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ /* b8 */ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ /* c0 */ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ /* c8 */ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ /* d0 */ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ /* d8 */ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ /* e0 */ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ /* e8 */ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ /* f0 */ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ /* f8 */ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+void
+hdlc_Init(struct hdlc *hdlc, struct lcp *lcp)
+{
+ memset(hdlc, '\0', sizeof(struct hdlc));
+ hdlc->lqm.owner = lcp;
+}
+
+/*
+ * HDLC FCS computation. Read RFC 1171 Appendix B and CCITT X.25 section
+ * 2.27 for further details.
+ */
+u_short
+hdlc_Fcs(u_char *cp, size_t len)
+{
+ u_short fcs = INITFCS;
+
+ while (len--)
+ fcs = (fcs >> 8) ^ fcstab[(fcs ^ *cp++) & 0xff];
+
+ return fcs;
+}
+
+static inline u_short
+HdlcFcsBuf(u_short fcs, struct mbuf *m)
+{
+ int len;
+ u_char *pos, *end;
+
+ len = m_length(m);
+ pos = MBUF_CTOP(m);
+ end = pos + m->m_len;
+ while (len--) {
+ fcs = (fcs >> 8) ^ fcstab[(fcs ^ *pos++) & 0xff];
+ if (pos == end && len) {
+ m = m->m_next;
+ pos = MBUF_CTOP(m);
+ end = pos + m->m_len;
+ }
+ }
+ return (fcs);
+}
+
+static struct mbuf *
+hdlc_LayerPush(struct bundle *bundle __unused, struct link *l __unused,
+ struct mbuf *bp, int pri __unused, u_short *proto __unused)
+{
+ struct mbuf *last;
+ u_char *cp;
+ u_short fcs;
+
+ m_settype(bp, MB_HDLCOUT);
+ fcs = HdlcFcsBuf(INITFCS, bp);
+ fcs = ~fcs;
+
+ for (last = bp; last->m_next; last = last->m_next)
+ ;
+
+ if (last->m_size - last->m_offset - last->m_len >= 2) {
+ cp = MBUF_CTOP(last) + last->m_len;
+ last->m_len += 2;
+ } else {
+ struct mbuf *tail = m_get(2, MB_HDLCOUT);
+ last->m_next = tail;
+ cp = MBUF_CTOP(tail);
+ }
+
+ *cp++ = fcs & 0377; /* Low byte first (nothing like consistency) */
+ *cp++ = fcs >> 8;
+
+ log_DumpBp(LogHDLC, "hdlc_Output", bp);
+
+ return bp;
+}
+
+/* Check out the latest ``Assigned numbers'' rfc (rfc1700.txt) */
+static struct {
+ u_short from;
+ u_short to;
+ const char *name;
+} protocols[] = {
+ { 0x0001, 0x0001, "Padding Protocol" },
+ { 0x0003, 0x001f, "reserved (transparency inefficient)" },
+ { 0x0021, 0x0021, "Internet Protocol" },
+ { 0x0023, 0x0023, "OSI Network Layer" },
+ { 0x0025, 0x0025, "Xerox NS IDP" },
+ { 0x0027, 0x0027, "DECnet Phase IV" },
+ { 0x0029, 0x0029, "Appletalk" },
+ { 0x002b, 0x002b, "Novell IPX" },
+ { 0x002d, 0x002d, "Van Jacobson Compressed TCP/IP" },
+ { 0x002f, 0x002f, "Van Jacobson Uncompressed TCP/IP" },
+ { 0x0031, 0x0031, "Bridging PDU" },
+ { 0x0033, 0x0033, "Stream Protocol (ST-II)" },
+ { 0x0035, 0x0035, "Banyan Vines" },
+ { 0x0037, 0x0037, "reserved (until 1993)" },
+ { 0x0039, 0x0039, "AppleTalk EDDP" },
+ { 0x003b, 0x003b, "AppleTalk SmartBuffered" },
+ { 0x003d, 0x003d, "Multi-Link" },
+ { 0x003f, 0x003f, "NETBIOS Framing" },
+ { 0x0041, 0x0041, "Cisco Systems" },
+ { 0x0043, 0x0043, "Ascom Timeplex" },
+ { 0x0045, 0x0045, "Fujitsu Link Backup and Load Balancing (LBLB)" },
+ { 0x0047, 0x0047, "DCA Remote Lan" },
+ { 0x0049, 0x0049, "Serial Data Transport Protocol (PPP-SDTP)" },
+ { 0x004b, 0x004b, "SNA over 802.2" },
+ { 0x004d, 0x004d, "SNA" },
+ { 0x004f, 0x004f, "IP6 Header Compression" },
+ { 0x0051, 0x0051, "KNX Bridging Data" },
+ { 0x0053, 0x0053, "Encryption" },
+ { 0x0055, 0x0055, "Individual Link Encryption" },
+ { 0x0057, 0x0057, "Internet Protocol V6" },
+ { 0x006f, 0x006f, "Stampede Bridging" },
+ { 0x0071, 0x0071, "BAP Bandwidth Allocation Protocol" },
+ { 0x0073, 0x0073, "MP+ Protocol" },
+ { 0x007d, 0x007d, "reserved (Control Escape)" },
+ { 0x007f, 0x007f, "reserved (compression inefficient)" },
+ { 0x00cf, 0x00cf, "reserved (PPP NLPID)" },
+ { 0x00fb, 0x00fb, "compression on single link in multilink group" },
+ { 0x00fd, 0x00fd, "1st choice compression" },
+ { 0x00ff, 0x00ff, "reserved (compression inefficient)" },
+ { 0x0200, 0x02ff, "(compression inefficient)" },
+ { 0x0201, 0x0201, "802.1d Hello Packets" },
+ { 0x0203, 0x0203, "IBM Source Routing BPDU" },
+ { 0x0205, 0x0205, "DEC LANBridge100 Spanning Tree" },
+ { 0x0207, 0x0207, "Cisco Discovery Protocol" },
+ { 0x0209, 0x0209, "Netcs Twin Routing" },
+ { 0x0231, 0x0231, "Luxcom" },
+ { 0x0233, 0x0233, "Sigma Network Systems" },
+ { 0x0235, 0x0235, "Apple Client Server Protocol" },
+ { 0x1e00, 0x1eff, "(compression inefficient)" },
+ { 0x4001, 0x4001, "Cray Communications Control Protocol" },
+ { 0x4003, 0x4003, "CDPD Mobile Network Registration Protocol" },
+ { 0x4021, 0x4021, "Stacker LZS" },
+ { 0x8001, 0x801f, "Not Used - reserved" },
+ { 0x8021, 0x8021, "Internet Protocol Control Protocol" },
+ { 0x8023, 0x8023, "OSI Network Layer Control Protocol" },
+ { 0x8025, 0x8025, "Xerox NS IDP Control Protocol" },
+ { 0x8027, 0x8027, "DECnet Phase IV Control Protocol" },
+ { 0x8029, 0x8029, "Appletalk Control Protocol" },
+ { 0x802b, 0x802b, "Novell IPX Control Protocol" },
+ { 0x802d, 0x802d, "reserved" },
+ { 0x802f, 0x802f, "reserved" },
+ { 0x8031, 0x8031, "Bridging NCP" },
+ { 0x8033, 0x8033, "Stream Protocol Control Protocol" },
+ { 0x8035, 0x8035, "Banyan Vines Control Protocol" },
+ { 0x8037, 0x8037, "reserved till 1993" },
+ { 0x8039, 0x8039, "reserved" },
+ { 0x803b, 0x803b, "reserved" },
+ { 0x803d, 0x803d, "Multi-Link Control Protocol" },
+ { 0x803f, 0x803f, "NETBIOS Framing Control Protocol" },
+ { 0x8041, 0x8041, "Cisco Systems Control Protocol" },
+ { 0x8043, 0x8043, "Ascom Timeplex" },
+ { 0x8045, 0x8045, "Fujitsu LBLB Control Protocol" },
+ { 0x8047, 0x8047, "DCA Remote Lan Network Control Protocol (RLNCP)" },
+ { 0x8049, 0x8049, "Serial Data Control Protocol (PPP-SDCP)" },
+ { 0x804b, 0x804b, "SNA over 802.2 Control Protocol" },
+ { 0x804d, 0x804d, "SNA Control Protocol" },
+ { 0x804f, 0x804f, "IP6 Header Compression Control Protocol" },
+ { 0x8051, 0x8051, "KNX Bridging Control Protocol" },
+ { 0x8053, 0x8053, "Encryption Control Protocol" },
+ { 0x8055, 0x8055, "Individual Link Encryption Control Protocol" },
+ { 0x8057, 0x8057, "Internet Protocol V6 Control Protocol" },
+ { 0x806f, 0x806f, "Stampede Bridging Control Protocol" },
+ { 0x8073, 0x8073, "MP+ Control Protocol" },
+ { 0x8071, 0x8071, "BACP Bandwidth Allocation Control Protocol" },
+ { 0x807d, 0x807d, "Not Used - reserved" },
+ { 0x80cf, 0x80cf, "Not Used - reserved" },
+ { 0x80fb, 0x80fb, "compression on single link in multilink group control" },
+ { 0x80fd, 0x80fd, "Compression Control Protocol" },
+ { 0x80ff, 0x80ff, "Not Used - reserved" },
+ { 0x8207, 0x8207, "Cisco Discovery Protocol Control" },
+ { 0x8209, 0x8209, "Netcs Twin Routing" },
+ { 0x8235, 0x8235, "Apple Client Server Protocol Control" },
+ { 0xc021, 0xc021, "Link Control Protocol" },
+ { 0xc023, 0xc023, "Password Authentication Protocol" },
+ { 0xc025, 0xc025, "Link Quality Report" },
+ { 0xc027, 0xc027, "Shiva Password Authentication Protocol" },
+ { 0xc029, 0xc029, "CallBack Control Protocol (CBCP)" },
+ { 0xc081, 0xc081, "Container Control Protocol" },
+ { 0xc223, 0xc223, "Challenge Handshake Authentication Protocol" },
+ { 0xc225, 0xc225, "RSA Authentication Protocol" },
+ { 0xc227, 0xc227, "Extensible Authentication Protocol" },
+ { 0xc26f, 0xc26f, "Stampede Bridging Authorization Protocol" },
+ { 0xc281, 0xc281, "Proprietary Authentication Protocol" },
+ { 0xc283, 0xc283, "Proprietary Authentication Protocol" },
+ { 0xc481, 0xc481, "Proprietary Node ID Authentication Protocol" }
+};
+
+#define NPROTOCOLS (sizeof protocols/sizeof protocols[0])
+
+const char *
+hdlc_Protocol2Nam(u_short proto)
+{
+ unsigned f;
+
+ for (f = 0; f < NPROTOCOLS; f++)
+ if (proto >= protocols[f].from && proto <= protocols[f].to)
+ return protocols[f].name;
+ else if (proto < protocols[f].from)
+ break;
+ return "unrecognised protocol";
+}
+
+static struct mbuf *
+hdlc_LayerPull(struct bundle *b __unused, struct link *l, struct mbuf *bp,
+ u_short *proto __unused)
+{
+ struct physical *p = link2physical(l);
+ u_short fcs;
+ int len;
+
+ if (!p) {
+ log_Printf(LogERROR, "Can't Pull a hdlc packet from a logical link\n");
+ return bp;
+ }
+
+ log_DumpBp(LogHDLC, "hdlc_LayerPull:", bp);
+
+ bp = m_pullup(bp);
+ len = m_length(bp);
+ fcs = hdlc_Fcs(MBUF_CTOP(bp), len);
+
+ log_Printf(LogDEBUG, "%s: hdlc_LayerPull: fcs = %04x (%s)\n",
+ p->link.name, fcs, (fcs == GOODFCS) ? "good" : "BAD!");
+
+ p->hdlc.lqm.ifInOctets += len + 1; /* plus 1 flag octet! */
+
+ if (fcs != GOODFCS) {
+ p->hdlc.lqm.ifInErrors++;
+ p->hdlc.stats.badfcs++;
+ m_freem(bp);
+ return NULL;
+ }
+
+ /* Either done here or by the sync layer */
+ p->hdlc.lqm.lqr.InGoodOctets += len + 1; /* plus 1 flag octet! */
+ p->hdlc.lqm.ifInUniPackets++;
+
+ if (len < 4) { /* rfc1662 section 4.3 */
+ m_freem(bp);
+ bp = NULL;
+ }
+
+ bp = m_adj(bp, -2); /* discard the FCS */
+ m_settype(bp, MB_HDLCIN);
+
+ return bp;
+}
+
+/* Detect a HDLC frame */
+
+static const struct frameheader {
+ const u_char *data;
+ int len;
+} FrameHeaders[] = {
+ { "\176\377\003\300\041", 5 },
+ { "\176\377\175\043\300\041", 6 },
+ { "\176\177\175\043\100\041", 6 },
+ { "\176\175\337\175\043\300\041", 7 },
+ { "\176\175\137\175\043\100\041", 7 },
+ { NULL, 0 }
+};
+
+int
+hdlc_Detect(u_char const **cp, unsigned n, int issync)
+{
+ const struct frameheader *fh;
+ const u_char *h;
+ size_t len, cmp;
+
+ while (n) {
+ for (fh = FrameHeaders; fh->len; fh++) {
+ h = issync ? fh->data + 1 : fh->data;
+ len = issync ? fh->len - 1 : fh->len;
+ cmp = n >= len ? len : n;
+ if (memcmp(*cp, h, cmp) == 0)
+ return cmp == len;
+ }
+ n--;
+ (*cp)++;
+ }
+
+ return 0;
+}
+
+int
+hdlc_ReportStatus(struct cmdargs const *arg)
+{
+ struct hdlc *hdlc = &arg->cx->physical->hdlc;
+
+ prompt_Printf(arg->prompt, "%s HDLC level errors:\n", arg->cx->name);
+ prompt_Printf(arg->prompt, " Bad Frame Check Sequence fields: %u\n",
+ hdlc->stats.badfcs);
+ prompt_Printf(arg->prompt, " Bad address (!= 0x%02x) fields: %u\n",
+ HDLC_ADDR, hdlc->stats.badaddr);
+ prompt_Printf(arg->prompt, " Bad command (!= 0x%02x) fields: %u\n",
+ HDLC_UI, hdlc->stats.badcommand);
+ prompt_Printf(arg->prompt, " Unrecognised protocol fields: %u\n",
+ hdlc->stats.unknownproto);
+ return 0;
+}
+
+static void
+hdlc_ReportTime(void *v)
+{
+ /* Moan about HDLC errors */
+ struct hdlc *hdlc = (struct hdlc *)v;
+
+ timer_Stop(&hdlc->ReportTimer);
+
+ if (memcmp(&hdlc->laststats, &hdlc->stats, sizeof hdlc->stats)) {
+ log_Printf(LogPHASE,
+ "%s: HDLC errors -> FCS: %u, ADDR: %u, COMD: %u, PROTO: %u\n",
+ hdlc->lqm.owner->fsm.link->name,
+ hdlc->stats.badfcs - hdlc->laststats.badfcs,
+ hdlc->stats.badaddr - hdlc->laststats.badaddr,
+ hdlc->stats.badcommand - hdlc->laststats.badcommand,
+ hdlc->stats.unknownproto - hdlc->laststats.unknownproto);
+ hdlc->laststats = hdlc->stats;
+ }
+
+ timer_Start(&hdlc->ReportTimer);
+}
+
+void
+hdlc_StartTimer(struct hdlc *hdlc)
+{
+ timer_Stop(&hdlc->ReportTimer);
+ hdlc->ReportTimer.load = 60 * SECTICKS;
+ hdlc->ReportTimer.arg = hdlc;
+ hdlc->ReportTimer.func = hdlc_ReportTime;
+ hdlc->ReportTimer.name = "hdlc";
+ timer_Start(&hdlc->ReportTimer);
+}
+
+void
+hdlc_StopTimer(struct hdlc *hdlc)
+{
+ timer_Stop(&hdlc->ReportTimer);
+}
+
+struct layer hdlclayer = { LAYER_HDLC, "hdlc", hdlc_LayerPush, hdlc_LayerPull };
diff --git a/usr.sbin/ppp/hdlc.h b/usr.sbin/ppp/hdlc.h
new file mode 100644
index 0000000..b9211a6
--- /dev/null
+++ b/usr.sbin/ppp/hdlc.h
@@ -0,0 +1,116 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+/*
+ * Definition for Async HDLC
+ */
+#define HDLC_SYN 0x7e /* SYNC character */
+#define HDLC_ESC 0x7d /* Escape character */
+#define HDLC_XOR 0x20 /* Modifier value */
+
+#define HDLC_ADDR 0xff
+#define HDLC_UI 0x03
+/*
+ * Definition for HDLC Frame Check Sequence
+ */
+#define INITFCS 0xffff /* Initial value for FCS computation */
+#define GOODFCS 0xf0b8 /* Good FCS value */
+
+#define DEF_MRU 1500
+#define MAX_MRU 2048
+#define MIN_MRU 296
+
+#define DEF_MTU 0 /* whatever peer says */
+#define MAX_MTU 2048
+#define MIN_MTU 296
+
+struct physical;
+struct link;
+struct lcp;
+struct bundle;
+struct mbuf;
+struct cmdargs;
+
+struct hdlc {
+ struct pppTimer ReportTimer;
+
+ struct {
+ int badfcs;
+ int badaddr;
+ int badcommand;
+ int unknownproto;
+ } laststats, stats;
+
+ struct {
+ struct lcp *owner; /* parent LCP */
+ struct pppTimer timer; /* When to send */
+ int method; /* bit-mask for LQM_* from lqr.h */
+
+ u_int32_t ifOutUniPackets; /* Packets sent by me */
+ u_int32_t ifOutOctets; /* Octets sent by me */
+ u_int32_t ifInUniPackets; /* Packets received from peer */
+ u_int32_t ifInDiscards; /* Discards */
+ u_int32_t ifInErrors; /* Errors */
+ u_int32_t ifInOctets; /* Octets received from peer (unused) */
+
+ struct {
+ u_int32_t InGoodOctets; /* Good octets received from peer */
+ u_int32_t OutLQRs; /* LQRs sent by me */
+ u_int32_t InLQRs; /* LQRs received from peer */
+
+ struct lqrsavedata Save; /* Our last LQR */
+ struct lqrsavedata prevSave; /* Our last-but-one LQR (analysis) */
+
+ struct lqrdata peer; /* Last LQR from peer */
+ int peer_timeout; /* peers max lqr timeout */
+ int resent; /* Resent last packet `resent' times */
+ } lqr;
+
+ struct {
+ u_int32_t seq_sent; /* last echo sent */
+ u_int32_t seq_recv; /* last echo received */
+ } echo;
+ } lqm;
+};
+
+
+extern void hdlc_Init(struct hdlc *, struct lcp *);
+extern void hdlc_StartTimer(struct hdlc *);
+extern void hdlc_StopTimer(struct hdlc *);
+extern int hdlc_ReportStatus(struct cmdargs const *);
+extern const char *hdlc_Protocol2Nam(u_short);
+extern void hdlc_DecodePacket(struct bundle *, u_short, struct mbuf *,
+ struct link *);
+
+extern u_short hdlc_Fcs(u_char *, size_t);
+extern int hdlc_Detect(u_char const **, unsigned, int);
+#define hdlc_WrapperOctets() (2)
+
+extern struct layer hdlclayer;
diff --git a/usr.sbin/ppp/i4b.h b/usr.sbin/ppp/i4b.h
new file mode 100644
index 0000000..3545c84
--- /dev/null
+++ b/usr.sbin/ppp/i4b.h
@@ -0,0 +1,37 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct physical;
+struct device;
+
+#define DEF_I4BCDDELAY 6 /* Default ``set cd'' value */
+
+extern struct device *i4b_Create(struct physical *);
+extern struct device *i4b_iov2device(int, struct physical *,
+ struct iovec *, int *, int, int *, int *);
+extern unsigned i4b_DeviceSize(void);
diff --git a/usr.sbin/ppp/id.c b/usr.sbin/ppp/id.c
new file mode 100644
index 0000000..440d460
--- /dev/null
+++ b/usr.sbin/ppp/id.c
@@ -0,0 +1,303 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#ifndef NONETGRAPH
+#include <netgraph.h>
+#endif
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <sysexits.h>
+#if defined(__FreeBSD__) && !defined(NOKLDLOAD)
+#include <sys/linker.h>
+#endif
+#include <unistd.h>
+#ifdef __OpenBSD__
+#include <util.h>
+#else
+#include <libutil.h>
+#endif
+#include <utmp.h>
+
+#include "log.h"
+#include "main.h"
+#include "id.h"
+
+static int uid;
+static int euid;
+
+void
+ID0init()
+{
+ uid = getuid();
+ euid = geteuid();
+}
+
+static void
+ID0setuser(void)
+{
+ if (seteuid(uid) == -1) {
+ log_Printf(LogERROR, "ID0setuser: Unable to seteuid!\n");
+ AbortProgram(EX_NOPERM);
+ }
+}
+
+uid_t
+ID0realuid()
+{
+ return uid;
+}
+
+static void
+ID0set0(void)
+{
+ if (seteuid(euid) == -1) {
+ log_Printf(LogERROR, "ID0set0: Unable to seteuid!\n");
+ AbortProgram(EX_NOPERM);
+ }
+}
+
+int
+ID0ioctl(int fd, unsigned long req, void *arg)
+{
+ int ret;
+
+ ID0set0();
+ ret = ioctl(fd, req, arg);
+ log_Printf(LogID0, "%d = ioctl(%d, %lu, %p)\n", ret, fd, req, arg);
+ ID0setuser();
+ return ret;
+}
+
+int
+ID0unlink(const char *name)
+{
+ int ret;
+
+ ID0set0();
+ ret = unlink(name);
+ log_Printf(LogID0, "%d = unlink(\"%s\")\n", ret, name);
+ ID0setuser();
+ return ret;
+}
+
+int
+ID0socket(int domain, int type, int protocol)
+{
+ int ret;
+
+ ID0set0();
+ ret = socket(domain, type, protocol);
+ log_Printf(LogID0, "%d = socket(%d, %d, %d)\n", ret, domain, type, protocol);
+ ID0setuser();
+ return ret;
+}
+
+FILE *
+ID0fopen(const char *path, const char *mode)
+{
+ FILE *ret;
+
+ ID0set0();
+ ret = fopen(path, mode);
+ log_Printf(LogID0, "%p = fopen(\"%s\", \"%s\")\n", ret, path, mode);
+ ID0setuser();
+ return ret;
+}
+
+int
+ID0open(const char *path, int flags, ...)
+{
+ int ret;
+ va_list ap;
+
+ va_start(ap, flags);
+ ID0set0();
+ ret = open(path, flags, va_arg(ap, int));
+ log_Printf(LogID0, "%d = open(\"%s\", %d)\n", ret, path, flags);
+ ID0setuser();
+ va_end(ap);
+ return ret;
+}
+
+int
+ID0write(int fd, const void *data, size_t len)
+{
+ int ret;
+
+ ID0set0();
+ ret = write(fd, data, len);
+ log_Printf(LogID0, "%d = write(%d, data, %ld)\n", ret, fd, (long)len);
+ ID0setuser();
+ return ret;
+}
+
+int
+ID0uu_lock(const char *basettyname)
+{
+ int ret;
+
+ ID0set0();
+ ret = uu_lock(basettyname);
+ log_Printf(LogID0, "%d = uu_lock(\"%s\")\n", ret, basettyname);
+ ID0setuser();
+ return ret;
+}
+
+int
+ID0uu_lock_txfr(const char *basettyname, pid_t newpid)
+{
+ int ret;
+
+ ID0set0();
+ ret = uu_lock_txfr(basettyname, newpid);
+ log_Printf(LogID0, "%d = uu_lock_txfr(\"%s\", %ld)\n", ret, basettyname,
+ (long)newpid);
+ ID0setuser();
+ return ret;
+}
+
+int
+ID0uu_unlock(const char *basettyname)
+{
+ int ret;
+
+ ID0set0();
+ ret = uu_unlock(basettyname);
+ log_Printf(LogID0, "%d = uu_unlock(\"%s\")\n", ret, basettyname);
+ ID0setuser();
+ return ret;
+}
+
+void
+ID0login(struct utmp *ut)
+{
+ ID0set0();
+ if (logout(ut->ut_line)) {
+ log_Printf(LogID0, "logout(\"%s\")\n", ut->ut_line);
+ logwtmp(ut->ut_line, "", "");
+ log_Printf(LogID0, "logwtmp(\"%s\", \"\", \"\")\n", ut->ut_line);
+ }
+ login(ut);
+ log_Printf(LogID0, "login(\"%s\", \"%.*s\")\n",
+ ut->ut_line, (int)(sizeof ut->ut_name), ut->ut_name);
+ ID0setuser();
+}
+
+void
+ID0logout(const char *device, int nologout)
+{
+ struct utmp ut;
+ char ut_line[sizeof ut.ut_line + 1];
+
+ strncpy(ut_line, device, sizeof ut_line - 1);
+ ut_line[sizeof ut_line - 1] = '\0';
+
+ ID0set0();
+ if (nologout || logout(ut_line)) {
+ log_Printf(LogID0, "logout(\"%s\")\n", ut_line);
+ logwtmp(ut_line, "", "");
+ log_Printf(LogID0, "logwtmp(\"%s\", \"\", \"\")\n", ut_line);
+ } else
+ log_Printf(LogERROR, "ID0logout: No longer logged in on %s\n", ut_line);
+ ID0setuser();
+}
+
+int
+ID0bind_un(int s, const struct sockaddr_un *name)
+{
+ int result;
+
+ ID0set0();
+ result = bind(s, (const struct sockaddr *)name, sizeof *name);
+ log_Printf(LogID0, "%d = bind(%d, \"%s\", %d)\n",
+ result, s, name->sun_path, (int)sizeof(*name));
+ ID0setuser();
+ return result;
+}
+
+int
+ID0connect_un(int s, const struct sockaddr_un *name)
+{
+ int result;
+
+ ID0set0();
+ result = connect(s, (const struct sockaddr *)name, sizeof *name);
+ log_Printf(LogID0, "%d = connect(%d, \"%s\", %d)\n",
+ result, s, name->sun_path, (int)sizeof(*name));
+ ID0setuser();
+ return result;
+}
+
+int
+ID0kill(pid_t pid, int sig)
+{
+ int result;
+
+ ID0set0();
+ result = kill(pid, sig);
+ log_Printf(LogID0, "%d = kill(%ld, %d)\n", result, (long)pid, sig);
+ ID0setuser();
+ return result;
+}
+
+#if defined(__FreeBSD__) && !defined(NOKLDLOAD)
+int
+ID0kldload(const char *dev)
+{
+ int result;
+
+ ID0set0();
+ result = kldload(dev);
+ log_Printf(LogID0, "%d = kldload(\"%s\")\n", result, dev);
+ ID0setuser();
+ return result;
+}
+#endif
+
+#ifndef NONETGRAPH
+int
+ID0NgMkSockNode(const char *name, int *cs, int *ds)
+{
+ int result;
+
+ ID0set0();
+ result = NgMkSockNode(name, cs, ds);
+ log_Printf(LogID0, "%d = NgMkSockNode(\"%s\", &cs, &ds)\n",
+ result, name ? name : "");
+ ID0setuser();
+ return result;
+}
+#endif
diff --git a/usr.sbin/ppp/id.h b/usr.sbin/ppp/id.h
new file mode 100644
index 0000000..6125ab5
--- /dev/null
+++ b/usr.sbin/ppp/id.h
@@ -0,0 +1,93 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef NOSUID
+struct utmp;
+struct sockaddr_un;
+
+extern void ID0init(void);
+extern uid_t ID0realuid(void);
+extern int ID0ioctl(int, unsigned long, void *);
+extern int ID0unlink(const char *);
+extern int ID0socket(int, int, int);
+extern FILE *ID0fopen(const char *, const char *);
+extern int ID0open(const char *, int, ...);
+extern int ID0write(int, const void *, size_t);
+extern int ID0uu_lock(const char *);
+extern int ID0uu_lock_txfr(const char *, pid_t);
+extern int ID0uu_unlock(const char *);
+extern void ID0login(struct utmp *);
+extern void ID0logout(const char *, int);
+extern int ID0bind_un(int, const struct sockaddr_un *);
+extern int ID0connect_un(int, const struct sockaddr_un *);
+extern int ID0kill(pid_t, int);
+#if defined(__FreeBSD__) && !defined(NOKLDLOAD)
+extern int ID0kldload(const char *);
+#endif
+#ifndef NONETGRAPH
+extern int ID0NgMkSockNode(const char *, int *, int *);
+#endif
+#else /* NOSUID */
+#define ID0init()
+#define ID0realuid() (0)
+#define ID0ioctl ioctl
+#define ID0unlink unlink
+#define ID0socket socket
+#define ID0fopen fopen
+#define ID0open open
+#define ID0write write
+#define ID0uu_lock uu_lock
+#define ID0uu_lock_txfr uu_lock_txfr
+#define ID0uu_unlock uu_unlock
+#define ID0login(u) \
+ do { \
+ if (logout((u)->ut_line)) \
+ logwtmp((u)->ut_line, "", ""); \
+ login(u); \
+ } while (0)
+#define ID0logout(dev, no) \
+ do { \
+ struct utmp ut; \
+ strncpy(ut.ut_line, dev, sizeof ut.ut_line - 1); \
+ ut.ut_line[sizeof ut.ut_line - 1] = '\0'; \
+ if (no || logout(ut.ut_line)) \
+ logwtmp(ut.ut_line, "", ""); \
+ } while (0)
+#define ID0bind_un(s, n) bind(s, (const struct sockaddr *)(n), sizeof *(n))
+#define ID0connect_un(s, n) \
+ connect(s, (const struct sockaddr *)(n), sizeof *(n))
+#define ID0kill kill
+#if defined(__FreeBSD__) && !defined(NOKLDLOAD)
+#include <sys/param.h>
+#include <sys/linker.h>
+#define ID0kldload kldload
+#endif
+#ifndef NONETGRAPH
+#define ID0NgMkSockNode NgMkSockNode
+#endif
+#endif
diff --git a/usr.sbin/ppp/iface.c b/usr.sbin/ppp/iface.c
new file mode 100644
index 0000000..5c7f7d0
--- /dev/null
+++ b/usr.sbin/ppp/iface.c
@@ -0,0 +1,729 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#ifdef __FreeBSD__
+#include <net/if_var.h>
+#endif
+#include <net/route.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#ifndef NOINET6
+#include <netinet6/nd6.h>
+#endif
+#include <sys/un.h>
+
+#include <errno.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "id.h"
+#include "timer.h"
+#include "fsm.h"
+#include "iplist.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "slcompress.h"
+#include "descriptor.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "prompt.h"
+#include "iface.h"
+
+#define IN6MASK128 {{{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, \
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }}}
+static const struct in6_addr in6mask128 = IN6MASK128;
+
+
+struct iface *
+iface_Create(const char *name)
+{
+ int mib[6], maxtries, err;
+ size_t needed, namelen;
+ char *buf, *ptr, *end;
+ struct if_msghdr *ifm;
+ struct ifa_msghdr *ifam;
+ struct sockaddr_dl *dl;
+ struct sockaddr *sa[RTAX_MAX];
+ struct iface *iface;
+ struct iface_addr *addr;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = 0;
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0;
+
+ maxtries = 20;
+ err = 0;
+ do {
+ if (maxtries-- == 0 || (err && err != ENOMEM)) {
+ fprintf(stderr, "iface_Create: sysctl: %s\n", strerror(err));
+ return NULL;
+ }
+
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
+ fprintf(stderr, "iface_Create: sysctl: estimate: %s\n",
+ strerror(errno));
+ return NULL;
+ }
+
+ if ((buf = (char *)malloc(needed)) == NULL) {
+ fprintf(stderr, "iface_Create: malloc failed: %s\n", strerror(errno));
+ return NULL;
+ }
+
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
+ err = errno;
+ free(buf);
+ buf = NULL;
+ }
+ } while (buf == NULL);
+
+ ptr = buf;
+ end = buf + needed;
+ iface = NULL;
+ namelen = strlen(name);
+
+ while (ptr < end && iface == NULL) {
+ ifm = (struct if_msghdr *)ptr; /* On if_msghdr */
+ if (ifm->ifm_type != RTM_IFINFO)
+ break;
+ dl = (struct sockaddr_dl *)(ifm + 1); /* Single _dl at end */
+ if (dl->sdl_nlen == namelen && !strncmp(name, dl->sdl_data, namelen)) {
+ iface = (struct iface *)malloc(sizeof *iface);
+ if (iface == NULL) {
+ fprintf(stderr, "iface_Create: malloc: %s\n", strerror(errno));
+ return NULL;
+ }
+ iface->name = strdup(name);
+ iface->index = ifm->ifm_index;
+ iface->flags = ifm->ifm_flags;
+ iface->mtu = 0;
+ iface->addrs = 0;
+ iface->addr = NULL;
+ }
+ ptr += ifm->ifm_msglen; /* First ifa_msghdr */
+ for (; ptr < end; ptr += ifam->ifam_msglen) {
+ ifam = (struct ifa_msghdr *)ptr; /* Next if address */
+
+ if (ifam->ifam_type != RTM_NEWADDR) /* finished this if */
+ break;
+
+ if (iface != NULL && ifam->ifam_addrs & RTA_IFA) {
+ /* Found a configured interface ! */
+ iface_ParseHdr(ifam, sa);
+
+ if (sa[RTAX_IFA] && (sa[RTAX_IFA]->sa_family == AF_INET
+#ifndef NOINET6
+ || sa[RTAX_IFA]->sa_family == AF_INET6
+#endif
+ )) {
+ /* Record the address */
+
+ addr = (struct iface_addr *)
+ realloc(iface->addr, (iface->addrs + 1) * sizeof iface->addr[0]);
+ if (addr == NULL)
+ break;
+ iface->addr = addr;
+
+ addr += iface->addrs;
+ iface->addrs++;
+
+ ncprange_setsa(&addr->ifa, sa[RTAX_IFA], sa[RTAX_NETMASK]);
+ if (sa[RTAX_BRD])
+ ncpaddr_setsa(&addr->peer, sa[RTAX_BRD]);
+ else
+ ncpaddr_init(&addr->peer);
+ }
+ }
+ }
+ }
+
+ free(buf);
+
+ return iface;
+}
+
+static int
+iface_addr_Zap(const char *name, struct iface_addr *addr, int s)
+{
+ struct ifaliasreq ifra;
+#ifndef NOINET6
+ struct in6_aliasreq ifra6;
+#endif
+ struct sockaddr_in *me4, *msk4, *peer4;
+ struct sockaddr_storage ssme, sspeer, ssmsk;
+ int res;
+
+ ncprange_getsa(&addr->ifa, &ssme, &ssmsk);
+ ncpaddr_getsa(&addr->peer, &sspeer);
+ res = 0;
+
+ switch (ncprange_family(&addr->ifa)) {
+ case AF_INET:
+ memset(&ifra, '\0', sizeof ifra);
+ strncpy(ifra.ifra_name, name, sizeof ifra.ifra_name - 1);
+
+ me4 = (struct sockaddr_in *)&ifra.ifra_addr;
+ memcpy(me4, &ssme, sizeof *me4);
+
+ msk4 = (struct sockaddr_in *)&ifra.ifra_mask;
+ memcpy(msk4, &ssmsk, sizeof *msk4);
+
+ peer4 = (struct sockaddr_in *)&ifra.ifra_broadaddr;
+ if (ncpaddr_family(&addr->peer) == AF_UNSPEC) {
+ peer4->sin_family = AF_INET;
+ peer4->sin_len = sizeof(*peer4);
+ peer4->sin_addr.s_addr = INADDR_NONE;
+ } else
+ memcpy(peer4, &sspeer, sizeof *peer4);
+
+ res = ID0ioctl(s, SIOCDIFADDR, &ifra);
+ if (log_IsKept(LogDEBUG)) {
+ char buf[100];
+
+ snprintf(buf, sizeof buf, "%s", ncprange_ntoa(&addr->ifa));
+ log_Printf(LogWARN, "%s: DIFADDR %s -> %s returns %d\n",
+ ifra.ifra_name, buf, ncpaddr_ntoa(&addr->peer), res);
+ }
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ memset(&ifra6, '\0', sizeof ifra6);
+ strncpy(ifra6.ifra_name, name, sizeof ifra6.ifra_name - 1);
+
+ memcpy(&ifra6.ifra_addr, &ssme, sizeof ifra6.ifra_addr);
+ memcpy(&ifra6.ifra_prefixmask, &ssmsk, sizeof ifra6.ifra_prefixmask);
+ ifra6.ifra_prefixmask.sin6_family = AF_UNSPEC;
+ if (ncpaddr_family(&addr->peer) == AF_UNSPEC)
+ ifra6.ifra_dstaddr.sin6_family = AF_UNSPEC;
+ else
+ memcpy(&ifra6.ifra_dstaddr, &sspeer, sizeof ifra6.ifra_dstaddr);
+ ifra6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
+ ifra6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
+
+ res = ID0ioctl(s, SIOCDIFADDR_IN6, &ifra6);
+ break;
+#endif
+ }
+
+ if (res == -1) {
+ char dst[40];
+ const char *end =
+#ifndef NOINET6
+ ncprange_family(&addr->ifa) == AF_INET6 ? "_IN6" :
+#endif
+ "";
+
+ if (ncpaddr_family(&addr->peer) == AF_UNSPEC)
+ log_Printf(LogWARN, "iface rm: ioctl(SIOCDIFADDR%s, %s): %s\n",
+ end, ncprange_ntoa(&addr->ifa), strerror(errno));
+ else {
+ snprintf(dst, sizeof dst, "%s", ncpaddr_ntoa(&addr->peer));
+ log_Printf(LogWARN, "iface rm: ioctl(SIOCDIFADDR%s, %s -> %s): %s\n",
+ end, ncprange_ntoa(&addr->ifa), dst, strerror(errno));
+ }
+ }
+
+ return res != -1;
+}
+
+static int
+iface_addr_Add(const char *name, struct iface_addr *addr, int s)
+{
+ struct ifaliasreq ifra;
+#ifndef NOINET6
+ struct in6_aliasreq ifra6;
+#endif
+ struct sockaddr_in *me4, *msk4, *peer4;
+ struct sockaddr_storage ssme, sspeer, ssmsk;
+ int res;
+
+ ncprange_getsa(&addr->ifa, &ssme, &ssmsk);
+ ncpaddr_getsa(&addr->peer, &sspeer);
+ res = 0;
+
+ switch (ncprange_family(&addr->ifa)) {
+ case AF_INET:
+ memset(&ifra, '\0', sizeof ifra);
+ strncpy(ifra.ifra_name, name, sizeof ifra.ifra_name - 1);
+
+ me4 = (struct sockaddr_in *)&ifra.ifra_addr;
+ memcpy(me4, &ssme, sizeof *me4);
+
+ msk4 = (struct sockaddr_in *)&ifra.ifra_mask;
+ memcpy(msk4, &ssmsk, sizeof *msk4);
+
+ peer4 = (struct sockaddr_in *)&ifra.ifra_broadaddr;
+ if (ncpaddr_family(&addr->peer) == AF_UNSPEC) {
+ peer4->sin_family = AF_INET;
+ peer4->sin_len = sizeof(*peer4);
+ peer4->sin_addr.s_addr = INADDR_NONE;
+ } else
+ memcpy(peer4, &sspeer, sizeof *peer4);
+
+ res = ID0ioctl(s, SIOCAIFADDR, &ifra);
+ if (log_IsKept(LogDEBUG)) {
+ char buf[100];
+
+ snprintf(buf, sizeof buf, "%s", ncprange_ntoa(&addr->ifa));
+ log_Printf(LogWARN, "%s: AIFADDR %s -> %s returns %d\n",
+ ifra.ifra_name, buf, ncpaddr_ntoa(&addr->peer), res);
+ }
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ memset(&ifra6, '\0', sizeof ifra6);
+ strncpy(ifra6.ifra_name, name, sizeof ifra6.ifra_name - 1);
+
+ memcpy(&ifra6.ifra_addr, &ssme, sizeof ifra6.ifra_addr);
+ memcpy(&ifra6.ifra_prefixmask, &ssmsk, sizeof ifra6.ifra_prefixmask);
+ if (ncpaddr_family(&addr->peer) == AF_UNSPEC)
+ ifra6.ifra_dstaddr.sin6_family = AF_UNSPEC;
+ else if (memcmp(&((struct sockaddr_in6 *)&ssmsk)->sin6_addr, &in6mask128,
+ sizeof in6mask128) == 0)
+ memcpy(&ifra6.ifra_dstaddr, &sspeer, sizeof ifra6.ifra_dstaddr);
+ ifra6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
+ ifra6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
+
+ res = ID0ioctl(s, SIOCAIFADDR_IN6, &ifra6);
+ break;
+#endif
+ }
+
+ if (res == -1) {
+ char dst[40];
+ const char *end =
+#ifndef NOINET6
+ ncprange_family(&addr->ifa) == AF_INET6 ? "_IN6" :
+#endif
+ "";
+
+ if (ncpaddr_family(&addr->peer) == AF_UNSPEC)
+ log_Printf(LogWARN, "iface add: ioctl(SIOCAIFADDR%s, %s): %s\n",
+ end, ncprange_ntoa(&addr->ifa), strerror(errno));
+ else {
+ snprintf(dst, sizeof dst, "%s", ncpaddr_ntoa(&addr->peer));
+ log_Printf(LogWARN, "iface add: ioctl(SIOCAIFADDR%s, %s -> %s): %s\n",
+ end, ncprange_ntoa(&addr->ifa), dst, strerror(errno));
+ }
+ }
+
+ return res != -1;
+}
+
+
+void
+iface_Clear(struct iface *iface, struct ncp *ncp, int family, int how)
+{
+ int addrs, af, inskip, in6skip, s4 = -1, s6 = -1, *s;
+ unsigned n;
+
+ if (iface->addrs) {
+ inskip = in6skip = how == IFACE_CLEAR_ALL ? 0 : 1;
+ addrs = 0;
+
+ for (n = 0; n < iface->addrs; n++) {
+ af = ncprange_family(&iface->addr[n].ifa);
+ if (family == 0 || family == af) {
+ if (!iface->addr[n].system && (how & IFACE_SYSTEM))
+ continue;
+ switch (af) {
+ case AF_INET:
+ if (inskip) {
+ inskip = 0;
+ continue;
+ }
+ s = &s4;
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ if (in6skip) {
+ in6skip = 0;
+ continue;
+ }
+ s = &s6;
+ break;
+#endif
+ default:
+ continue;
+ }
+
+ if (*s == -1 && (*s = ID0socket(af, SOCK_DGRAM, 0)) == -1)
+ log_Printf(LogERROR, "iface_Clear: socket(): %s\n", strerror(errno));
+ else if (iface_addr_Zap(iface->name, iface->addr + n, *s)) {
+ ncp_IfaceAddrDeleted(ncp, iface->addr + n);
+ bcopy(iface->addr + n + 1, iface->addr + n,
+ (iface->addrs - n - 1) * sizeof *iface->addr);
+ iface->addrs--;
+ n--;
+ }
+ }
+ }
+
+ /* Don't bother realloc()ing - we have little to gain */
+
+ if (s4)
+ close(s4);
+ if (s6)
+ close(s6);
+ }
+}
+
+int
+iface_Add(struct iface *iface, struct ncp *ncp, const struct ncprange *ifa,
+ const struct ncpaddr *peer, int how)
+{
+ int af, removed, s;
+ unsigned n;
+ struct ncpaddr ncplocal;
+ struct iface_addr *addr, newaddr;
+
+ af = ncprange_family(ifa);
+ if ((s = ID0socket(af, SOCK_DGRAM, 0)) == -1) {
+ log_Printf(LogERROR, "iface_Add: socket(): %s\n", strerror(errno));
+ return 0;
+ }
+ ncprange_getaddr(ifa, &ncplocal);
+
+ for (n = 0; n < iface->addrs; n++) {
+ if (ncprange_contains(&iface->addr[n].ifa, &ncplocal) ||
+ ncpaddr_equal(&iface->addr[n].peer, peer)) {
+ /* Replace this sockaddr */
+ if (!(how & IFACE_FORCE_ADD)) {
+ close(s);
+ return 0; /* errno = EEXIST; */
+ }
+
+ if (ncprange_equal(&iface->addr[n].ifa, ifa) &&
+ ncpaddr_equal(&iface->addr[n].peer, peer)) {
+ close(s);
+ return 1; /* Already there */
+ }
+
+ removed = iface_addr_Zap(iface->name, iface->addr + n, s);
+ if (removed)
+ ncp_IfaceAddrDeleted(ncp, iface->addr + n);
+ ncprange_copy(&iface->addr[n].ifa, ifa);
+ ncpaddr_copy(&iface->addr[n].peer, peer);
+ if (!iface_addr_Add(iface->name, iface->addr + n, s)) {
+ if (removed) {
+ bcopy(iface->addr + n + 1, iface->addr + n,
+ (iface->addrs - n - 1) * sizeof *iface->addr);
+ iface->addrs--;
+ n--;
+ }
+ close(s);
+ return 0;
+ }
+ close(s);
+ ncp_IfaceAddrAdded(ncp, iface->addr + n);
+ return 1;
+ }
+ }
+
+ addr = (struct iface_addr *)realloc
+ (iface->addr, (iface->addrs + 1) * sizeof iface->addr[0]);
+ if (addr == NULL) {
+ log_Printf(LogERROR, "iface_inAdd: realloc: %s\n", strerror(errno));
+ close(s);
+ return 0;
+ }
+ iface->addr = addr;
+
+ ncprange_copy(&newaddr.ifa, ifa);
+ ncpaddr_copy(&newaddr.peer, peer);
+ newaddr.system = !!(how & IFACE_SYSTEM);
+ if (!iface_addr_Add(iface->name, &newaddr, s)) {
+ close(s);
+ return 0;
+ }
+
+ if (how & IFACE_ADD_FIRST) {
+ /* Stuff it at the start of our list */
+ n = 0;
+ bcopy(iface->addr, iface->addr + 1, iface->addrs * sizeof *iface->addr);
+ } else
+ n = iface->addrs;
+
+ iface->addrs++;
+ memcpy(iface->addr + n, &newaddr, sizeof(*iface->addr));
+
+ close(s);
+ ncp_IfaceAddrAdded(ncp, iface->addr + n);
+
+ return 1;
+}
+
+int
+iface_Delete(struct iface *iface, struct ncp *ncp, const struct ncpaddr *del)
+{
+ struct ncpaddr found;
+ unsigned n;
+ int res, s;
+
+ if ((s = ID0socket(ncpaddr_family(del), SOCK_DGRAM, 0)) == -1) {
+ log_Printf(LogERROR, "iface_Delete: socket(): %s\n", strerror(errno));
+ return 0;
+ }
+
+ for (n = res = 0; n < iface->addrs; n++) {
+ ncprange_getaddr(&iface->addr[n].ifa, &found);
+ if (ncpaddr_equal(&found, del)) {
+ if (iface_addr_Zap(iface->name, iface->addr + n, s)) {
+ ncp_IfaceAddrDeleted(ncp, iface->addr + n);
+ bcopy(iface->addr + n + 1, iface->addr + n,
+ (iface->addrs - n - 1) * sizeof *iface->addr);
+ iface->addrs--;
+ res = 1;
+ }
+ break;
+ }
+ }
+
+ close(s);
+
+ return res;
+}
+
+#define IFACE_ADDFLAGS 1
+#define IFACE_DELFLAGS 2
+
+static int
+iface_ChangeFlags(const char *ifname, int flags, int how)
+{
+ struct ifreq ifrq;
+ int s, new_flags;
+
+ s = ID0socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ log_Printf(LogERROR, "iface_ChangeFlags: socket: %s\n", strerror(errno));
+ return 0;
+ }
+
+ memset(&ifrq, '\0', sizeof ifrq);
+ strncpy(ifrq.ifr_name, ifname, sizeof ifrq.ifr_name - 1);
+ ifrq.ifr_name[sizeof ifrq.ifr_name - 1] = '\0';
+ if (ID0ioctl(s, SIOCGIFFLAGS, &ifrq) < 0) {
+ log_Printf(LogERROR, "iface_ChangeFlags: ioctl(SIOCGIFFLAGS): %s\n",
+ strerror(errno));
+ close(s);
+ return 0;
+ }
+#ifdef __FreeBSD__
+ new_flags = (ifrq.ifr_flags & 0xffff) | (ifrq.ifr_flagshigh << 16);
+#else
+ new_flags = ifrq.ifr_flags & 0xffff;
+#endif
+
+ if (how == IFACE_ADDFLAGS)
+ new_flags |= flags;
+ else
+ new_flags &= ~flags;
+ ifrq.ifr_flags = new_flags & 0xffff;
+#ifdef __FreeBSD__
+ ifrq.ifr_flagshigh = new_flags >> 16;
+#endif
+
+ if (ID0ioctl(s, SIOCSIFFLAGS, &ifrq) < 0) {
+ log_Printf(LogERROR, "iface_ChangeFlags: ioctl(SIOCSIFFLAGS): %s\n",
+ strerror(errno));
+ close(s);
+ return 0;
+ }
+ close(s);
+
+ return 1; /* Success */
+}
+
+int
+iface_SetFlags(const char *ifname, int flags)
+{
+ return iface_ChangeFlags(ifname, flags, IFACE_ADDFLAGS);
+}
+
+int
+iface_ClearFlags(const char *ifname, int flags)
+{
+ return iface_ChangeFlags(ifname, flags, IFACE_DELFLAGS);
+}
+
+void
+iface_Destroy(struct iface *iface)
+{
+ /*
+ * iface_Clear(iface, IFACE_CLEAR_ALL) must be called manually
+ * if that's what the user wants. It's better to leave the interface
+ * allocated so that existing connections can continue to work.
+ */
+
+ if (iface != NULL) {
+ free(iface->name);
+ free(iface->addr);
+ free(iface);
+ }
+}
+
+#define if_entry(x) { IFF_##x, #x }
+
+struct {
+ int flag;
+ const char *value;
+} if_flags[] = {
+ if_entry(UP),
+ if_entry(BROADCAST),
+ if_entry(DEBUG),
+ if_entry(LOOPBACK),
+ if_entry(POINTOPOINT),
+ if_entry(RUNNING),
+ if_entry(NOARP),
+ if_entry(PROMISC),
+ if_entry(ALLMULTI),
+ if_entry(OACTIVE),
+ if_entry(SIMPLEX),
+ if_entry(LINK0),
+ if_entry(LINK1),
+ if_entry(LINK2),
+ if_entry(MULTICAST),
+ { 0, "???" }
+};
+
+int
+iface_Show(struct cmdargs const *arg)
+{
+ struct ncpaddr ncpaddr;
+ struct iface *iface = arg->bundle->iface, *current;
+ unsigned f;
+ int flags;
+#ifndef NOINET6
+ int scopeid, width;
+#endif
+ struct in_addr mask;
+
+ current = iface_Create(iface->name);
+ flags = iface->flags = current->flags;
+ iface_Destroy(current);
+
+ prompt_Printf(arg->prompt, "%s (idx %d) <", iface->name, iface->index);
+ for (f = 0; f < sizeof if_flags / sizeof if_flags[0]; f++)
+ if ((if_flags[f].flag & flags)) {
+ prompt_Printf(arg->prompt, "%s%s", flags == iface->flags ? "" : ",",
+ if_flags[f].value);
+ flags &= ~if_flags[f].flag;
+ }
+
+#if 0
+ if (flags)
+ prompt_Printf(arg->prompt, "%s0x%x", flags == iface->flags ? "" : ",",
+ flags);
+#endif
+
+ prompt_Printf(arg->prompt, "> mtu %lu has %d address%s:\n", iface->mtu,
+ iface->addrs, iface->addrs == 1 ? "" : "es");
+
+ for (f = 0; f < iface->addrs; f++) {
+ ncprange_getaddr(&iface->addr[f].ifa, &ncpaddr);
+ switch (ncprange_family(&iface->addr[f].ifa)) {
+ case AF_INET:
+ prompt_Printf(arg->prompt, " inet %s --> ", ncpaddr_ntoa(&ncpaddr));
+ if (ncpaddr_family(&iface->addr[f].peer) == AF_UNSPEC)
+ prompt_Printf(arg->prompt, "255.255.255.255");
+ else
+ prompt_Printf(arg->prompt, "%s", ncpaddr_ntoa(&iface->addr[f].peer));
+ ncprange_getip4mask(&iface->addr[f].ifa, &mask);
+ prompt_Printf(arg->prompt, " netmask 0x%08lx", (long)ntohl(mask.s_addr));
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ prompt_Printf(arg->prompt, " inet6 %s", ncpaddr_ntoa(&ncpaddr));
+ if (ncpaddr_family(&iface->addr[f].peer) != AF_UNSPEC)
+ prompt_Printf(arg->prompt, " --> %s",
+ ncpaddr_ntoa(&iface->addr[f].peer));
+ ncprange_getwidth(&iface->addr[f].ifa, &width);
+ if (ncpaddr_family(&iface->addr[f].peer) == AF_UNSPEC)
+ prompt_Printf(arg->prompt, " prefixlen %d", width);
+ if ((scopeid = ncprange_scopeid(&iface->addr[f].ifa)) != -1)
+ prompt_Printf(arg->prompt, " scopeid 0x%x", (unsigned)scopeid);
+ break;
+#endif
+ }
+ prompt_Printf(arg->prompt, "\n");
+ }
+
+ return 0;
+}
+
+void
+iface_ParseHdr(struct ifa_msghdr *ifam, struct sockaddr *sa[RTAX_MAX])
+{
+ char *wp;
+ int rtax;
+
+ wp = (char *)(ifam + 1);
+
+ for (rtax = 0; rtax < RTAX_MAX; rtax++)
+ if (ifam->ifam_addrs & (1 << rtax)) {
+ sa[rtax] = (struct sockaddr *)wp;
+ wp += ROUNDUP(sa[rtax]->sa_len);
+ } else
+ sa[rtax] = NULL;
+}
diff --git a/usr.sbin/ppp/iface.h b/usr.sbin/ppp/iface.h
new file mode 100644
index 0000000..1fd0a70
--- /dev/null
+++ b/usr.sbin/ppp/iface.h
@@ -0,0 +1,65 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct ifa_msghdr;
+
+struct iface_addr {
+ unsigned system : 1; /* System alias ? */
+ struct ncprange ifa; /* local address/mask */
+ struct ncpaddr peer; /* peer address */
+};
+
+struct iface {
+ char *name; /* Interface name (malloc'd) */
+ int index; /* Interface index */
+ int flags; /* Interface flags (IFF_*) */
+ unsigned long mtu; /* struct tuninfo MTU */
+
+ unsigned addrs; /* How many in_addr's */
+ struct iface_addr *addr; /* Array of addresses (malloc'd) */
+};
+
+#define IFACE_CLEAR_ALL 0 /* Nuke 'em all */
+#define IFACE_CLEAR_ALIASES 1 /* Leave the NCP address */
+
+#define IFACE_ADD_LAST 0 /* Just another alias */
+#define IFACE_ADD_FIRST 1 /* The IPCP address */
+#define IFACE_FORCE_ADD 2 /* OR'd with IFACE_ADD_{FIRST,LAST} */
+
+#define IFACE_SYSTEM 4 /* Set/clear SYSTEM entries */
+
+extern struct iface *iface_Create(const char *name);
+extern void iface_Clear(struct iface *, struct ncp *, int, int);
+extern int iface_Add(struct iface *, struct ncp *, const struct ncprange *,
+ const struct ncpaddr *, int);
+extern int iface_Delete(struct iface *, struct ncp *, const struct ncpaddr *);
+extern int iface_Show(struct cmdargs const *);
+extern int iface_SetFlags(const char *, int);
+extern int iface_ClearFlags(const char *, int);
+extern void iface_Destroy(struct iface *);
+extern void iface_ParseHdr(struct ifa_msghdr *, struct sockaddr *[RTAX_MAX]);
diff --git a/usr.sbin/ppp/ip.c b/usr.sbin/ppp/ip.c
new file mode 100644
index 0000000..5cd2272
--- /dev/null
+++ b/usr.sbin/ppp/ip.c
@@ -0,0 +1,993 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#ifndef NOINET6
+#include <netinet/icmp6.h>
+#include <netinet/ip6.h>
+#endif
+#include <netinet/ip_icmp.h>
+#include <netinet/udp.h>
+#include <netinet/tcp.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "proto.h"
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "iplist.h"
+#include "slcompress.h"
+#include "ncpaddr.h"
+#include "ip.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "tun.h"
+
+
+#define OPCODE_QUERY 0
+#define OPCODE_IQUERY 1
+#define OPCODE_STATUS 2
+
+struct dns_header {
+ u_short id;
+ unsigned qr : 1;
+ unsigned opcode : 4;
+ unsigned aa : 1;
+ unsigned tc : 1;
+ unsigned rd : 1;
+ unsigned ra : 1;
+ unsigned z : 3;
+ unsigned rcode : 4;
+ u_short qdcount;
+ u_short ancount;
+ u_short nscount;
+ u_short arcount;
+};
+
+static const char *
+dns_Qclass2Txt(u_short qclass)
+{
+ static char failure[6];
+ struct {
+ u_short id;
+ const char *txt;
+ } qtxt[] = {
+ /* rfc1035 */
+ { 1, "IN" }, { 2, "CS" }, { 3, "CH" }, { 4, "HS" }, { 255, "*" }
+ };
+ unsigned f;
+
+ for (f = 0; f < sizeof qtxt / sizeof *qtxt; f++)
+ if (qtxt[f].id == qclass)
+ return qtxt[f].txt;
+
+ return HexStr(qclass, failure, sizeof failure);
+}
+
+static const char *
+dns_Qtype2Txt(u_short qtype)
+{
+ static char failure[6];
+ struct {
+ u_short id;
+ const char *txt;
+ } qtxt[] = {
+ /* rfc1035/rfc1700 */
+ { 1, "A" }, { 2, "NS" }, { 3, "MD" }, { 4, "MF" }, { 5, "CNAME" },
+ { 6, "SOA" }, { 7, "MB" }, { 8, "MG" }, { 9, "MR" }, { 10, "NULL" },
+ { 11, "WKS" }, { 12, "PTR" }, { 13, "HINFO" }, { 14, "MINFO" },
+ { 15, "MX" }, { 16, "TXT" }, { 17, "RP" }, { 18, "AFSDB" },
+ { 19, "X25" }, { 20, "ISDN" }, { 21, "RT" }, { 22, "NSAP" },
+ { 23, "NSAP-PTR" }, { 24, "SIG" }, { 25, "KEY" }, { 26, "PX" },
+ { 27, "GPOS" }, { 28, "AAAA" }, { 252, "AXFR" }, { 253, "MAILB" },
+ { 254, "MAILA" }, { 255, "*" }
+ };
+ unsigned f;
+
+ for (f = 0; f < sizeof qtxt / sizeof *qtxt; f++)
+ if (qtxt[f].id == qtype)
+ return qtxt[f].txt;
+
+ return HexStr(qtype, failure, sizeof failure);
+}
+
+static __inline int
+PortMatch(int op, u_short pport, u_short rport)
+{
+ switch (op) {
+ case OP_EQ:
+ return pport == rport;
+ case OP_GT:
+ return pport > rport;
+ case OP_LT:
+ return pport < rport;
+ default:
+ return 0;
+ }
+}
+
+/*
+ * Return a text string representing the cproto protocol number.
+ *
+ * The purpose of this routine is calculate this result, for
+ * the many times it is needed in FilterCheck, only on demand
+ * (i.e. when the corresponding logging functions are invoked).
+ *
+ * This optimization saves, over the previous implementation, which
+ * calculated prototxt at the beginning of FilterCheck, an
+ * open/read/close system call sequence per packet, approximately
+ * halving the ppp system overhead and reducing the overall (u + s)
+ * time by 38%.
+ *
+ * The caching performed here is just a side effect.
+ */
+static const char *
+prototxt(int cproto)
+{
+ static int oproto = -1;
+ static char protobuff[16] = "-1";
+ struct protoent *pe;
+
+ if (cproto == oproto)
+ return protobuff;
+ if ((pe = getprotobynumber(cproto)) == NULL)
+ snprintf(protobuff, sizeof protobuff, "%d", cproto);
+ else
+ snprintf(protobuff, sizeof protobuff, "%s", pe->p_name);
+ oproto = cproto;
+ return (protobuff);
+}
+
+/*
+ * Check a packet against the given filter
+ * Returns 0 to accept the packet, non-zero to drop the packet.
+ * If psecs is not NULL, populate it with the timeout associated
+ * with the filter rule matched.
+ *
+ * If filtering is enabled, the initial fragment of a datagram must
+ * contain the complete protocol header, and subsequent fragments
+ * must not attempt to over-write it.
+ *
+ * One (and only one) of pip or pip6 must be set.
+ */
+int
+FilterCheck(const unsigned char *packet,
+#ifdef NOINET6
+ u_int32_t family __unused,
+#else
+ u_int32_t family,
+#endif
+ const struct filter *filter, unsigned *psecs)
+{
+ int gotinfo; /* true if IP payload decoded */
+ int cproto; /* IPPROTO_* protocol number if (gotinfo) */
+ int estab, syn, finrst; /* TCP state flags if (gotinfo) */
+ u_short sport, dport; /* src, dest port from packet if (gotinfo) */
+ int n; /* filter rule to process */
+ int len; /* bytes used in dbuff */
+ int didname; /* true if filter header printed */
+ int match; /* true if condition matched */
+ int mindata; /* minimum data size or zero */
+ const struct filterent *fp = filter->rule;
+ char dbuff[100], dstip[16];
+ struct ncpaddr srcaddr, dstaddr;
+ const char *payload; /* IP payload */
+ int datalen; /* IP datagram length */
+
+ if (fp->f_action == A_NONE)
+ return 0; /* No rule is given. Permit this packet */
+
+#ifndef NOINET6
+ if (family == AF_INET6) {
+ const struct ip6_hdr *pip6 = (const struct ip6_hdr *)packet;
+
+ ncpaddr_setip6(&srcaddr, &pip6->ip6_src);
+ ncpaddr_setip6(&dstaddr, &pip6->ip6_dst);
+ datalen = ntohs(pip6->ip6_plen);
+ payload = packet + sizeof *pip6;
+ cproto = pip6->ip6_nxt;
+ } else
+#endif
+ {
+ /*
+ * Deny any packet fragment that tries to over-write the header.
+ * Since we no longer have the real header available, punt on the
+ * largest normal header - 20 bytes for TCP without options, rounded
+ * up to the next possible fragment boundary. Since the smallest
+ * `legal' MTU is 576, and the smallest recommended MTU is 296, any
+ * fragmentation within this range is dubious at best
+ */
+ const struct ip *pip = (const struct ip *)packet;
+
+ len = ntohs(pip->ip_off) & IP_OFFMASK; /* fragment offset */
+ if (len > 0) { /* Not first fragment within datagram */
+ if (len < (24 >> 3)) { /* don't allow fragment to over-write header */
+ log_Printf(LogFILTER, " error: illegal header\n");
+ return 1;
+ }
+ /* permit fragments on in and out filter */
+ if (!filter->fragok) {
+ log_Printf(LogFILTER, " error: illegal fragmentation\n");
+ return 1;
+ } else
+ return 0;
+ }
+
+ ncpaddr_setip4(&srcaddr, pip->ip_src);
+ ncpaddr_setip4(&dstaddr, pip->ip_dst);
+ datalen = ntohs(pip->ip_len) - (pip->ip_hl << 2);
+ payload = packet + (pip->ip_hl << 2);
+ cproto = pip->ip_p;
+ }
+
+
+ gotinfo = estab = syn = finrst = didname = 0;
+ sport = dport = 0;
+
+ for (n = 0; n < MAXFILTERS; ) {
+ if (fp->f_action == A_NONE) {
+ n++;
+ fp++;
+ continue;
+ }
+
+ if (!didname) {
+ log_Printf(LogDEBUG, "%s filter:\n", filter->name);
+ didname = 1;
+ }
+
+ match = 0;
+
+ if ((ncprange_family(&fp->f_src) == AF_UNSPEC ||
+ ncprange_contains(&fp->f_src, &srcaddr)) &&
+ (ncprange_family(&fp->f_dst) == AF_UNSPEC ||
+ ncprange_contains(&fp->f_dst, &dstaddr))) {
+ if (fp->f_proto != 0) {
+ if (!gotinfo) {
+ const struct tcphdr *th;
+ const struct udphdr *uh;
+ const struct icmp *ih;
+#ifndef NOINET6
+ const struct icmp6_hdr *ih6;
+#endif
+ mindata = 0;
+ sport = dport = 0;
+ estab = syn = finrst = -1;
+
+ switch (cproto) {
+ case IPPROTO_ICMP:
+ mindata = 8; /* ICMP must be at least 8 octets */
+ ih = (const struct icmp *)payload;
+ sport = ih->icmp_type;
+ if (log_IsKept(LogDEBUG))
+ snprintf(dbuff, sizeof dbuff, "sport = %d", sport);
+ break;
+
+#ifndef NOINET6
+ case IPPROTO_ICMPV6:
+ mindata = 8; /* ICMP must be at least 8 octets */
+ ih6 = (const struct icmp6_hdr *)payload;
+ sport = ih6->icmp6_type;
+ if (log_IsKept(LogDEBUG))
+ snprintf(dbuff, sizeof dbuff, "sport = %d", sport);
+ break;
+#endif
+
+ case IPPROTO_IGMP:
+ mindata = 8; /* IGMP uses 8-octet messages */
+ break;
+
+#ifdef IPPROTO_GRE
+ case IPPROTO_GRE:
+ mindata = 2; /* GRE uses 2-octet+ messages */
+ break;
+#endif
+#ifdef IPPROTO_OSPFIGP
+ case IPPROTO_OSPFIGP:
+ mindata = 8; /* IGMP uses 8-octet messages */
+ break;
+#endif
+#ifndef NOINET6
+ case IPPROTO_IPV6:
+ mindata = 20; /* RFC2893 Section 3.5: 5 * 32bit words */
+ break;
+#endif
+
+ case IPPROTO_UDP:
+ mindata = 8; /* UDP header is 8 octets */
+ uh = (const struct udphdr *)payload;
+ sport = ntohs(uh->uh_sport);
+ dport = ntohs(uh->uh_dport);
+ if (log_IsKept(LogDEBUG))
+ snprintf(dbuff, sizeof dbuff, "sport = %d, dport = %d",
+ sport, dport);
+ break;
+
+ case IPPROTO_TCP:
+ th = (const struct tcphdr *)payload;
+ /*
+ * TCP headers are variable length. The following code
+ * ensures that the TCP header length isn't de-referenced if
+ * the datagram is too short
+ */
+ if (datalen < 20 || datalen < (th->th_off << 2)) {
+ log_Printf(LogFILTER, " error: TCP header incorrect\n");
+ return 1;
+ }
+ sport = ntohs(th->th_sport);
+ dport = ntohs(th->th_dport);
+ estab = (th->th_flags & TH_ACK);
+ syn = (th->th_flags & TH_SYN);
+ finrst = (th->th_flags & (TH_FIN|TH_RST));
+ if (log_IsKept(LogDEBUG)) {
+ if (!estab)
+ snprintf(dbuff, sizeof dbuff,
+ "flags = %02x, sport = %d, dport = %d",
+ th->th_flags, sport, dport);
+ else
+ *dbuff = '\0';
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (datalen < mindata) {
+ log_Printf(LogFILTER, " error: proto %s must be at least"
+ " %d octets\n", prototxt(cproto), mindata);
+ return 1;
+ }
+
+ if (log_IsKept(LogDEBUG)) {
+ if (estab != -1) {
+ len = strlen(dbuff);
+ snprintf(dbuff + len, sizeof dbuff - len,
+ ", estab = %d, syn = %d, finrst = %d",
+ estab, syn, finrst);
+ }
+ log_Printf(LogDEBUG, " Filter: proto = %s, %s\n",
+ prototxt(cproto), dbuff);
+ }
+ gotinfo = 1;
+ }
+
+ if (log_IsKept(LogDEBUG)) {
+ if (fp->f_srcop != OP_NONE) {
+ snprintf(dbuff, sizeof dbuff, ", src %s %d",
+ filter_Op2Nam(fp->f_srcop), fp->f_srcport);
+ len = strlen(dbuff);
+ } else
+ len = 0;
+ if (fp->f_dstop != OP_NONE) {
+ snprintf(dbuff + len, sizeof dbuff - len,
+ ", dst %s %d", filter_Op2Nam(fp->f_dstop),
+ fp->f_dstport);
+ } else if (!len)
+ *dbuff = '\0';
+
+ log_Printf(LogDEBUG, " rule = %d: Address match, "
+ "check against proto %d%s, action = %s\n",
+ n, fp->f_proto, dbuff, filter_Action2Nam(fp->f_action));
+ }
+
+ if (cproto == fp->f_proto) {
+ if ((fp->f_srcop == OP_NONE ||
+ PortMatch(fp->f_srcop, sport, fp->f_srcport)) &&
+ (fp->f_dstop == OP_NONE ||
+ PortMatch(fp->f_dstop, dport, fp->f_dstport)) &&
+ (fp->f_estab == 0 || estab) &&
+ (fp->f_syn == 0 || syn) &&
+ (fp->f_finrst == 0 || finrst)) {
+ match = 1;
+ }
+ }
+ } else {
+ /* Address is matched and no protocol specified. Make a decision. */
+ log_Printf(LogDEBUG, " rule = %d: Address match, action = %s\n", n,
+ filter_Action2Nam(fp->f_action));
+ match = 1;
+ }
+ } else
+ log_Printf(LogDEBUG, " rule = %d: Address mismatch\n", n);
+
+ if (match != fp->f_invert) {
+ /* Take specified action */
+ if (fp->f_action < A_NONE)
+ fp = &filter->rule[n = fp->f_action];
+ else {
+ if (fp->f_action == A_PERMIT) {
+ if (psecs != NULL)
+ *psecs = fp->timeout;
+ if (strcmp(filter->name, "DIAL") == 0) {
+ /* If dial filter then even print out accept packets */
+ if (log_IsKept(LogFILTER)) {
+ snprintf(dstip, sizeof dstip, "%s", ncpaddr_ntoa(&dstaddr));
+ log_Printf(LogFILTER, "%sbound rule = %d accept %s "
+ "src = %s:%d dst = %s:%d\n", filter->name, n,
+ prototxt(cproto), ncpaddr_ntoa(&srcaddr), sport,
+ dstip, dport);
+ }
+ }
+ return 0;
+ } else {
+ if (log_IsKept(LogFILTER)) {
+ snprintf(dstip, sizeof dstip, "%s", ncpaddr_ntoa(&dstaddr));
+ log_Printf(LogFILTER,
+ "%sbound rule = %d deny %s src = %s/%d dst = %s/%d\n",
+ filter->name, n, prototxt(cproto),
+ ncpaddr_ntoa(&srcaddr), sport, dstip, dport);
+ }
+ return 1;
+ } /* Explict match. Deny this packet */
+ }
+ } else {
+ n++;
+ fp++;
+ }
+ }
+
+ if (log_IsKept(LogFILTER)) {
+ snprintf(dstip, sizeof dstip, "%s", ncpaddr_ntoa(&dstaddr));
+ log_Printf(LogFILTER,
+ "%sbound rule = implicit deny %s src = %s/%d dst = %s/%d\n",
+ filter->name, prototxt(cproto), ncpaddr_ntoa(&srcaddr),
+ sport, dstip, dport);
+ }
+
+ return 1; /* No rule matched, deny this packet */
+}
+
+static void
+ip_LogDNS(const struct udphdr *uh, const char *direction)
+{
+ struct dns_header header;
+ const u_short *pktptr;
+ const u_char *ptr;
+ u_short *hptr, tmp;
+ unsigned len;
+
+ ptr = (const char *)uh + sizeof *uh;
+ len = ntohs(uh->uh_ulen) - sizeof *uh;
+ if (len < sizeof header + 5) /* rfc1024 */
+ return;
+
+ pktptr = (const u_short *)ptr;
+ hptr = (u_short *)&header;
+ ptr += sizeof header;
+ len -= sizeof header;
+
+ while (pktptr < (const u_short *)ptr) {
+ *hptr++ = ntohs(*pktptr); /* Careful of macro side-effects ! */
+ pktptr++;
+ }
+
+ if (header.opcode == OPCODE_QUERY && header.qr == 0) {
+ /* rfc1035 */
+ char namewithdot[MAXHOSTNAMELEN + 1], *n;
+ const char *qtype, *qclass;
+ const u_char *end;
+
+ n = namewithdot;
+ end = ptr + len - 4;
+ if (end - ptr >= (int)sizeof namewithdot)
+ end = ptr + sizeof namewithdot - 1;
+ while (ptr < end) {
+ len = *ptr++;
+ if ((int)len > end - ptr)
+ len = end - ptr;
+ if (n != namewithdot)
+ *n++ = '.';
+ memcpy(n, ptr, len);
+ ptr += len;
+ n += len;
+ }
+ *n = '\0';
+
+ if (log_IsKept(LogDNS)) {
+ memcpy(&tmp, end, sizeof tmp);
+ qtype = dns_Qtype2Txt(ntohs(tmp));
+ memcpy(&tmp, end + 2, sizeof tmp);
+ qclass = dns_Qclass2Txt(ntohs(tmp));
+
+ log_Printf(LogDNS, "%sbound query %s %s %s\n",
+ direction, qclass, qtype, namewithdot);
+ }
+ }
+}
+
+/*
+ * Check if the given packet matches the given filter.
+ * One of pip or pip6 must be set.
+ */
+int
+PacketCheck(struct bundle *bundle, u_int32_t family,
+ const unsigned char *packet, int nb, struct filter *filter,
+ const char *prefix, unsigned *psecs)
+{
+ char logbuf[200];
+ static const char *const TcpFlags[] = {
+ "FIN", "SYN", "RST", "PSH", "ACK", "URG"
+ };
+ const struct tcphdr *th;
+ const struct udphdr *uh;
+ const struct icmp *icmph;
+#ifndef NOINET6
+ const struct icmp6_hdr *icmp6h;
+#endif
+ const unsigned char *payload;
+ struct ncpaddr srcaddr, dstaddr;
+ int cproto, mask, len, n, pri, logit, result, datalen, frag;
+ unsigned loglen;
+ u_char tos;
+
+ logit = (log_IsKept(LogTCPIP) || log_IsKept(LogDNS)) &&
+ (!filter || filter->logok);
+ loglen = 0;
+ pri = 0;
+
+#ifndef NOINET6
+ if (family == AF_INET6) {
+ const struct ip6_hdr *pip6 = (const struct ip6_hdr *)packet;
+
+ ncpaddr_setip6(&srcaddr, &pip6->ip6_src);
+ ncpaddr_setip6(&dstaddr, &pip6->ip6_dst);
+ datalen = ntohs(pip6->ip6_plen);
+ payload = packet + sizeof *pip6;
+ cproto = pip6->ip6_nxt;
+ tos = 0; /* XXX: pip6->ip6_vfc >> 4 ? */
+ frag = 0; /* XXX: ??? */
+ } else
+#endif
+ {
+ const struct ip *pip = (const struct ip *)packet;
+
+ ncpaddr_setip4(&srcaddr, pip->ip_src);
+ ncpaddr_setip4(&dstaddr, pip->ip_dst);
+ datalen = ntohs(pip->ip_len) - (pip->ip_hl << 2);
+ payload = packet + (pip->ip_hl << 2);
+ cproto = pip->ip_p;
+ tos = pip->ip_tos;
+ frag = ntohs(pip->ip_off) & IP_OFFMASK;
+ }
+
+ uh = NULL;
+
+ if (logit && loglen < sizeof logbuf) {
+ if (prefix)
+ snprintf(logbuf + loglen, sizeof logbuf - loglen, "%s", prefix);
+ else if (filter)
+ snprintf(logbuf + loglen, sizeof logbuf - loglen, "%s ", filter->name);
+ else
+ snprintf(logbuf + loglen, sizeof logbuf - loglen, " ");
+ loglen += strlen(logbuf + loglen);
+ }
+
+ switch (cproto) {
+ case IPPROTO_ICMP:
+ if (logit && loglen < sizeof logbuf) {
+ len = datalen - sizeof *icmph;
+ icmph = (const struct icmp *)payload;
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "ICMP: %s:%d ---> ", ncpaddr_ntoa(&srcaddr), icmph->icmp_type);
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "%s (%d/%d)", ncpaddr_ntoa(&dstaddr), len, nb);
+ loglen += strlen(logbuf + loglen);
+ }
+ break;
+
+#ifndef NOINET6
+ case IPPROTO_ICMPV6:
+ if (logit && loglen < sizeof logbuf) {
+ len = datalen - sizeof *icmp6h;
+ icmp6h = (const struct icmp6_hdr *)payload;
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "ICMP: %s:%d ---> ", ncpaddr_ntoa(&srcaddr), icmp6h->icmp6_type);
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "%s (%d/%d)", ncpaddr_ntoa(&dstaddr), len, nb);
+ loglen += strlen(logbuf + loglen);
+ }
+ break;
+#endif
+
+ case IPPROTO_UDP:
+ uh = (const struct udphdr *)payload;
+ if (tos == IPTOS_LOWDELAY && bundle->ncp.cfg.urgent.tos)
+ pri++;
+
+ if (!frag && ncp_IsUrgentUdpPort(&bundle->ncp, ntohs(uh->uh_sport),
+ ntohs(uh->uh_dport)))
+ pri++;
+
+ if (logit && loglen < sizeof logbuf) {
+ len = datalen - sizeof *uh;
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "UDP: %s:%d ---> ", ncpaddr_ntoa(&srcaddr), ntohs(uh->uh_sport));
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "%s:%d (%d/%d)", ncpaddr_ntoa(&dstaddr), ntohs(uh->uh_dport),
+ len, nb);
+ loglen += strlen(logbuf + loglen);
+ }
+
+ if (Enabled(bundle, OPT_FILTERDECAP) &&
+ payload[sizeof *uh] == HDLC_ADDR &&
+ payload[sizeof *uh + 1] == HDLC_UI) {
+ u_short proto;
+ const char *type;
+
+ memcpy(&proto, payload + sizeof *uh + 2, sizeof proto);
+ type = NULL;
+
+ switch (ntohs(proto)) {
+ case PROTO_IP:
+ snprintf(logbuf + loglen, sizeof logbuf - loglen, " contains ");
+ result = PacketCheck(bundle, AF_INET, payload + sizeof *uh + 4,
+ nb - (payload - packet) - sizeof *uh - 4, filter,
+ logbuf, psecs);
+ if (result != -2)
+ return result;
+ type = "IP";
+ break;
+
+ case PROTO_VJUNCOMP: type = "compressed VJ"; break;
+ case PROTO_VJCOMP: type = "uncompressed VJ"; break;
+ case PROTO_MP: type = "Multi-link"; break;
+ case PROTO_ICOMPD: type = "Individual link CCP"; break;
+ case PROTO_COMPD: type = "CCP"; break;
+ case PROTO_IPCP: type = "IPCP"; break;
+ case PROTO_LCP: type = "LCP"; break;
+ case PROTO_PAP: type = "PAP"; break;
+ case PROTO_CBCP: type = "CBCP"; break;
+ case PROTO_LQR: type = "LQR"; break;
+ case PROTO_CHAP: type = "CHAP"; break;
+ }
+ if (type) {
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ " - %s data", type);
+ loglen += strlen(logbuf + loglen);
+ }
+ }
+
+ break;
+
+#ifdef IPPROTO_GRE
+ case IPPROTO_GRE:
+ if (logit && loglen < sizeof logbuf) {
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "GRE: %s ---> ", ncpaddr_ntoa(&srcaddr));
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "%s (%d/%d)", ncpaddr_ntoa(&dstaddr), datalen, nb);
+ loglen += strlen(logbuf + loglen);
+ }
+ break;
+#endif
+
+#ifdef IPPROTO_OSPFIGP
+ case IPPROTO_OSPFIGP:
+ if (logit && loglen < sizeof logbuf) {
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "OSPF: %s ---> ", ncpaddr_ntoa(&srcaddr));
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "%s (%d/%d)", ncpaddr_ntoa(&dstaddr), datalen, nb);
+ loglen += strlen(logbuf + loglen);
+ }
+ break;
+#endif
+
+#ifndef NOINET6
+ case IPPROTO_IPV6:
+ if (logit && loglen < sizeof logbuf) {
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "IPv6: %s ---> ", ncpaddr_ntoa(&srcaddr));
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "%s (%d/%d)", ncpaddr_ntoa(&dstaddr), datalen, nb);
+ loglen += strlen(logbuf + loglen);
+ }
+
+ if (Enabled(bundle, OPT_FILTERDECAP)) {
+ snprintf(logbuf + loglen, sizeof logbuf - loglen, " contains ");
+ result = PacketCheck(bundle, AF_INET6, payload, nb - (payload - packet),
+ filter, logbuf, psecs);
+ if (result != -2)
+ return result;
+ }
+ break;
+#endif
+
+ case IPPROTO_IPIP:
+ if (logit && loglen < sizeof logbuf) {
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "IPIP: %s ---> ", ncpaddr_ntoa(&srcaddr));
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "%s", ncpaddr_ntoa(&dstaddr));
+ loglen += strlen(logbuf + loglen);
+ }
+
+ if (Enabled(bundle, OPT_FILTERDECAP) &&
+ ((const struct ip *)payload)->ip_v == 4) {
+ snprintf(logbuf + loglen, sizeof logbuf - loglen, " contains ");
+ result = PacketCheck(bundle, AF_INET, payload, nb - (payload - packet),
+ filter, logbuf, psecs);
+ loglen += strlen(logbuf + loglen);
+ if (result != -2)
+ return result;
+ }
+ break;
+
+ case IPPROTO_ESP:
+ if (logit && loglen < sizeof logbuf) {
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "ESP: %s ---> ", ncpaddr_ntoa(&srcaddr));
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen, "%s, spi %p",
+ ncpaddr_ntoa(&dstaddr), payload);
+ loglen += strlen(logbuf + loglen);
+ }
+ break;
+
+ case IPPROTO_AH:
+ if (logit && loglen < sizeof logbuf) {
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "AH: %s ---> ", ncpaddr_ntoa(&srcaddr));
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen, "%s, spi %p",
+ ncpaddr_ntoa(&dstaddr), payload + sizeof(u_int32_t));
+ loglen += strlen(logbuf + loglen);
+ }
+ break;
+
+ case IPPROTO_IGMP:
+ if (logit && loglen < sizeof logbuf) {
+ uh = (const struct udphdr *)payload;
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "IGMP: %s:%d ---> ", ncpaddr_ntoa(&srcaddr),
+ ntohs(uh->uh_sport));
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "%s:%d", ncpaddr_ntoa(&dstaddr), ntohs(uh->uh_dport));
+ loglen += strlen(logbuf + loglen);
+ }
+ break;
+
+ case IPPROTO_TCP:
+ th = (const struct tcphdr *)payload;
+ if (tos == IPTOS_LOWDELAY && bundle->ncp.cfg.urgent.tos)
+ pri++;
+
+ if (!frag && ncp_IsUrgentTcpPort(&bundle->ncp, ntohs(th->th_sport),
+ ntohs(th->th_dport)))
+ pri++;
+
+ if (logit && loglen < sizeof logbuf) {
+ len = datalen - (th->th_off << 2);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "TCP: %s:%d ---> ", ncpaddr_ntoa(&srcaddr), ntohs(th->th_sport));
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "%s:%d", ncpaddr_ntoa(&dstaddr), ntohs(th->th_dport));
+ loglen += strlen(logbuf + loglen);
+ n = 0;
+ for (mask = TH_FIN; mask != 0x40; mask <<= 1) {
+ if (th->th_flags & mask) {
+ snprintf(logbuf + loglen, sizeof logbuf - loglen, " %s", TcpFlags[n]);
+ loglen += strlen(logbuf + loglen);
+ }
+ n++;
+ }
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ " seq:%lx ack:%lx (%d/%d)",
+ (u_long)ntohl(th->th_seq), (u_long)ntohl(th->th_ack), len, nb);
+ loglen += strlen(logbuf + loglen);
+ if ((th->th_flags & TH_SYN) && nb > 40) {
+ const u_short *sp;
+
+ sp = (const u_short *)(payload + 20);
+ if (ntohs(sp[0]) == 0x0204) {
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ " MSS = %d", ntohs(sp[1]));
+ loglen += strlen(logbuf + loglen);
+ }
+ }
+ }
+ break;
+
+ default:
+ if (prefix)
+ return -2;
+
+ if (logit && loglen < sizeof logbuf) {
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "<%d>: %s ---> ", cproto, ncpaddr_ntoa(&srcaddr));
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "%s (%d)", ncpaddr_ntoa(&dstaddr), nb);
+ loglen += strlen(logbuf + loglen);
+ }
+ break;
+ }
+
+ if (filter && FilterCheck(packet, family, filter, psecs)) {
+ if (logit)
+ log_Printf(LogTCPIP, "%s - BLOCKED\n", logbuf);
+ result = -1;
+ } else {
+ /* Check Keep Alive filter */
+ if (logit && log_IsKept(LogTCPIP)) {
+ unsigned alivesecs;
+
+ alivesecs = 0;
+ if (filter &&
+ FilterCheck(packet, family, &bundle->filter.alive, &alivesecs))
+ log_Printf(LogTCPIP, "%s - NO KEEPALIVE\n", logbuf);
+ else if (psecs != NULL) {
+ if(*psecs == 0)
+ *psecs = alivesecs;
+ if (*psecs) {
+ if (*psecs != alivesecs)
+ log_Printf(LogTCPIP, "%s - (timeout = %d / ALIVE = %d secs)\n",
+ logbuf, *psecs, alivesecs);
+ else
+ log_Printf(LogTCPIP, "%s - (timeout = %d secs)\n", logbuf, *psecs);
+ } else
+ log_Printf(LogTCPIP, "%s\n", logbuf);
+ }
+ }
+ result = pri;
+ }
+
+ if (filter && uh && ntohs(uh->uh_dport) == 53 && log_IsKept(LogDNS))
+ ip_LogDNS(uh, filter->name);
+
+ return result;
+}
+
+static size_t
+ip_Input(struct bundle *bundle, struct link *l, struct mbuf *bp, u_int32_t af)
+{
+ ssize_t nw;
+ size_t nb;
+ struct tun_data tun;
+ char *data;
+ unsigned secs, alivesecs;
+
+ nb = m_length(bp);
+ if (nb > sizeof tun.data) {
+ log_Printf(LogWARN, "ip_Input: %s: Packet too large (got %zd, max %d)\n",
+ l->name, nb, (int)(sizeof tun.data));
+ m_freem(bp);
+ return 0;
+ }
+ mbuf_Read(bp, tun.data, nb);
+
+ secs = 0;
+ if (PacketCheck(bundle, af, tun.data, nb, &bundle->filter.in,
+ NULL, &secs) < 0)
+ return 0;
+
+ alivesecs = 0;
+ if (!FilterCheck(tun.data, af, &bundle->filter.alive, &alivesecs)) {
+ if (secs == 0)
+ secs = alivesecs;
+ bundle_StartIdleTimer(bundle, secs);
+ }
+
+ if (bundle->dev.header) {
+ tun.header.family = htonl(af);
+ nb += sizeof tun - sizeof tun.data;
+ data = (char *)&tun;
+ } else
+ data = tun.data;
+
+ nw = write(bundle->dev.fd, data, nb);
+ if (nw != (ssize_t)nb) {
+ if (nw == -1)
+ log_Printf(LogERROR, "ip_Input: %s: wrote %zd, got %s\n",
+ l->name, nb, strerror(errno));
+ else
+ log_Printf(LogERROR, "ip_Input: %s: wrote %zd, got %zd\n", l->name, nb,
+ nw);
+ }
+
+ return nb;
+}
+
+struct mbuf *
+ipv4_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
+{
+ int nb;
+
+ if (bundle->ncp.ipcp.fsm.state != ST_OPENED) {
+ log_Printf(LogWARN, "ipv4_Input: IPCP not open - packet dropped\n");
+ m_freem(bp);
+ return NULL;
+ }
+
+ m_settype(bp, MB_IPIN);
+
+ nb = ip_Input(bundle, l, bp, AF_INET);
+ ipcp_AddInOctets(&bundle->ncp.ipcp, nb);
+
+ return NULL;
+}
+
+#ifndef NOINET6
+struct mbuf *
+ipv6_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
+{
+ int nb;
+
+ if (bundle->ncp.ipv6cp.fsm.state != ST_OPENED) {
+ log_Printf(LogWARN, "ipv6_Input: IPV6CP not open - packet dropped\n");
+ m_freem(bp);
+ return NULL;
+ }
+
+ m_settype(bp, MB_IPV6IN);
+
+ nb = ip_Input(bundle, l, bp, AF_INET6);
+ ipv6cp_AddInOctets(&bundle->ncp.ipv6cp, nb);
+
+ return NULL;
+}
+#endif
diff --git a/usr.sbin/ppp/ip.h b/usr.sbin/ppp/ip.h
new file mode 100644
index 0000000..a4c4179
--- /dev/null
+++ b/usr.sbin/ppp/ip.h
@@ -0,0 +1,44 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+struct mbuf;
+struct filter;
+struct link;
+struct bundle;
+
+extern int ip_PushPacket(struct link *, struct bundle *);
+extern int PacketCheck(struct bundle *, u_int32_t, const unsigned char *, int,
+ struct filter *, const char *, unsigned *secs);
+extern int FilterCheck(const unsigned char *, u_int32_t, const struct filter *,
+ unsigned *);
+extern struct mbuf *ipv4_Input(struct bundle *, struct link *, struct mbuf *);
+#ifndef NOINET6
+extern struct mbuf *ipv6_Input(struct bundle *, struct link *, struct mbuf *);
+#endif
diff --git a/usr.sbin/ppp/ipcp.c b/usr.sbin/ppp/ipcp.c
new file mode 100644
index 0000000..8991063
--- /dev/null
+++ b/usr.sbin/ppp/ipcp.c
@@ -0,0 +1,1479 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <netdb.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <resolv.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <unistd.h>
+
+#ifndef NONAT
+#ifdef LOCALNAT
+#include "alias.h"
+#else
+#include <alias.h>
+#endif
+#endif
+
+#include "layer.h"
+#include "ua.h"
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "proto.h"
+#include "iplist.h"
+#include "throughput.h"
+#include "slcompress.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ncpaddr.h"
+#include "ip.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "vjcomp.h"
+#include "async.h"
+#include "ccp.h"
+#include "link.h"
+#include "physical.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "id.h"
+#include "arp.h"
+#include "systems.h"
+#include "prompt.h"
+#include "route.h"
+#include "iface.h"
+
+#undef REJECTED
+#define REJECTED(p, x) ((p)->peer_reject & (1<<(x)))
+#define issep(ch) ((ch) == ' ' || (ch) == '\t')
+#define isip(ch) (((ch) >= '0' && (ch) <= '9') || (ch) == '.')
+
+struct compreq {
+ u_short proto;
+ u_char slots;
+ u_char compcid;
+};
+
+static int IpcpLayerUp(struct fsm *);
+static void IpcpLayerDown(struct fsm *);
+static void IpcpLayerStart(struct fsm *);
+static void IpcpLayerFinish(struct fsm *);
+static void IpcpInitRestartCounter(struct fsm *, int);
+static void IpcpSendConfigReq(struct fsm *);
+static void IpcpSentTerminateReq(struct fsm *);
+static void IpcpSendTerminateAck(struct fsm *, u_char);
+static void IpcpDecodeConfig(struct fsm *, u_char *, u_char *, int,
+ struct fsm_decode *);
+
+extern struct libalias *la;
+
+static struct fsm_callbacks ipcp_Callbacks = {
+ IpcpLayerUp,
+ IpcpLayerDown,
+ IpcpLayerStart,
+ IpcpLayerFinish,
+ IpcpInitRestartCounter,
+ IpcpSendConfigReq,
+ IpcpSentTerminateReq,
+ IpcpSendTerminateAck,
+ IpcpDecodeConfig,
+ fsm_NullRecvResetReq,
+ fsm_NullRecvResetAck
+};
+
+static const char *
+protoname(int proto)
+{
+ static struct {
+ int id;
+ const char *txt;
+ } cftypes[] = {
+ /* Check out the latest ``Assigned numbers'' rfc (rfc1700.txt) */
+ { 1, "IPADDRS" }, /* IP-Addresses */ /* deprecated */
+ { 2, "COMPPROTO" }, /* IP-Compression-Protocol */
+ { 3, "IPADDR" }, /* IP-Address */
+ { 129, "PRIDNS" }, /* 129: Primary DNS Server Address */
+ { 130, "PRINBNS" }, /* 130: Primary NBNS Server Address */
+ { 131, "SECDNS" }, /* 131: Secondary DNS Server Address */
+ { 132, "SECNBNS" } /* 132: Secondary NBNS Server Address */
+ };
+ unsigned f;
+
+ for (f = 0; f < sizeof cftypes / sizeof *cftypes; f++)
+ if (cftypes[f].id == proto)
+ return cftypes[f].txt;
+
+ return NumStr(proto, NULL, 0);
+}
+
+void
+ipcp_AddInOctets(struct ipcp *ipcp, int n)
+{
+ throughput_addin(&ipcp->throughput, n);
+}
+
+void
+ipcp_AddOutOctets(struct ipcp *ipcp, int n)
+{
+ throughput_addout(&ipcp->throughput, n);
+}
+
+void
+ipcp_LoadDNS(struct ipcp *ipcp)
+{
+ int fd;
+
+ ipcp->ns.dns[0].s_addr = ipcp->ns.dns[1].s_addr = INADDR_NONE;
+
+ if (ipcp->ns.resolv != NULL) {
+ free(ipcp->ns.resolv);
+ ipcp->ns.resolv = NULL;
+ }
+ if (ipcp->ns.resolv_nons != NULL) {
+ free(ipcp->ns.resolv_nons);
+ ipcp->ns.resolv_nons = NULL;
+ }
+ ipcp->ns.resolver = 0;
+
+ if ((fd = open(_PATH_RESCONF, O_RDONLY)) != -1) {
+ struct stat st;
+
+ if (fstat(fd, &st) == 0) {
+ ssize_t got;
+
+ /*
+ * Note, ns.resolv and ns.resolv_nons are assumed to always point to
+ * buffers of the same size! See the strcpy() below.
+ */
+ if ((ipcp->ns.resolv_nons = (char *)malloc(st.st_size + 1)) == NULL)
+ log_Printf(LogERROR, "Failed to malloc %lu for %s: %s\n",
+ (unsigned long)st.st_size, _PATH_RESCONF, strerror(errno));
+ else if ((ipcp->ns.resolv = (char *)malloc(st.st_size + 1)) == NULL) {
+ log_Printf(LogERROR, "Failed(2) to malloc %lu for %s: %s\n",
+ (unsigned long)st.st_size, _PATH_RESCONF, strerror(errno));
+ free(ipcp->ns.resolv_nons);
+ ipcp->ns.resolv_nons = NULL;
+ } else if ((got = read(fd, ipcp->ns.resolv, st.st_size)) != st.st_size) {
+ if (got == -1)
+ log_Printf(LogERROR, "Failed to read %s: %s\n",
+ _PATH_RESCONF, strerror(errno));
+ else
+ log_Printf(LogERROR, "Failed to read %s, got %lu not %lu\n",
+ _PATH_RESCONF, (unsigned long)got,
+ (unsigned long)st.st_size);
+ free(ipcp->ns.resolv_nons);
+ ipcp->ns.resolv_nons = NULL;
+ free(ipcp->ns.resolv);
+ ipcp->ns.resolv = NULL;
+ } else {
+ char *cp, *cp_nons, *ncp, ch;
+ int n;
+
+ ipcp->ns.resolv[st.st_size] = '\0';
+ ipcp->ns.resolver = 1;
+
+ cp_nons = ipcp->ns.resolv_nons;
+ cp = ipcp->ns.resolv;
+ n = 0;
+
+ while ((ncp = strstr(cp, "nameserver")) != NULL) {
+ if (ncp != cp) {
+ memcpy(cp_nons, cp, ncp - cp);
+ cp_nons += ncp - cp;
+ }
+ if ((ncp != cp && ncp[-1] != '\n') || !issep(ncp[10])) {
+ memcpy(cp_nons, ncp, 9);
+ cp_nons += 9;
+ cp = ncp + 9; /* Can't match "nameserver" at cp... */
+ continue;
+ }
+
+ for (cp = ncp + 11; issep(*cp); cp++) /* Skip whitespace */
+ ;
+
+ for (ncp = cp; isip(*ncp); ncp++) /* Jump over IP */
+ ;
+
+ ch = *ncp;
+ *ncp = '\0';
+ if (n < 2 && inet_aton(cp, ipcp->ns.dns))
+ n++;
+ *ncp = ch;
+
+ if ((cp = strchr(ncp, '\n')) == NULL) /* Point at next line */
+ cp = ncp + strlen(ncp);
+ else
+ cp++;
+ }
+ /*
+ * Note, cp_nons and cp always point to buffers of the same size, so
+ * strcpy is ok!
+ */
+ strcpy(cp_nons, cp); /* Copy the end - including the NUL */
+ cp_nons += strlen(cp_nons) - 1;
+ while (cp_nons >= ipcp->ns.resolv_nons && *cp_nons == '\n')
+ *cp_nons-- = '\0';
+ if (n == 2 && ipcp->ns.dns[0].s_addr == INADDR_ANY) {
+ ipcp->ns.dns[0].s_addr = ipcp->ns.dns[1].s_addr;
+ ipcp->ns.dns[1].s_addr = INADDR_ANY;
+ }
+ bundle_AdjustDNS(ipcp->fsm.bundle);
+ }
+ } else
+ log_Printf(LogERROR, "Failed to stat opened %s: %s\n",
+ _PATH_RESCONF, strerror(errno));
+
+ close(fd);
+ }
+}
+
+int
+ipcp_WriteDNS(struct ipcp *ipcp)
+{
+ const char *paddr;
+ mode_t mask;
+ FILE *fp;
+
+ if (ipcp->ns.dns[0].s_addr == INADDR_ANY &&
+ ipcp->ns.dns[1].s_addr == INADDR_ANY) {
+ log_Printf(LogIPCP, "%s not modified: All nameservers NAKd\n",
+ _PATH_RESCONF);
+ return 0;
+ }
+
+ if (ipcp->ns.dns[0].s_addr == INADDR_ANY) {
+ ipcp->ns.dns[0].s_addr = ipcp->ns.dns[1].s_addr;
+ ipcp->ns.dns[1].s_addr = INADDR_ANY;
+ }
+
+ mask = umask(022);
+ if ((fp = ID0fopen(_PATH_RESCONF, "w")) != NULL) {
+ umask(mask);
+ if (ipcp->ns.resolv_nons)
+ fputs(ipcp->ns.resolv_nons, fp);
+ paddr = inet_ntoa(ipcp->ns.dns[0]);
+ log_Printf(LogIPCP, "Primary nameserver set to %s\n", paddr);
+ fprintf(fp, "\nnameserver %s\n", paddr);
+ if (ipcp->ns.dns[1].s_addr != INADDR_ANY &&
+ ipcp->ns.dns[1].s_addr != INADDR_NONE &&
+ ipcp->ns.dns[1].s_addr != ipcp->ns.dns[0].s_addr) {
+ paddr = inet_ntoa(ipcp->ns.dns[1]);
+ log_Printf(LogIPCP, "Secondary nameserver set to %s\n", paddr);
+ fprintf(fp, "nameserver %s\n", paddr);
+ }
+ if (fclose(fp) == EOF) {
+ log_Printf(LogERROR, "write(): Failed updating %s: %s\n", _PATH_RESCONF,
+ strerror(errno));
+ return 0;
+ }
+ } else
+ umask(mask);
+
+ return 1;
+}
+
+void
+ipcp_RestoreDNS(struct ipcp *ipcp)
+{
+ if (ipcp->ns.resolver) {
+ ssize_t got, len;
+ int fd;
+
+ if ((fd = ID0open(_PATH_RESCONF, O_WRONLY|O_TRUNC, 0644)) != -1) {
+ len = strlen(ipcp->ns.resolv);
+ if ((got = write(fd, ipcp->ns.resolv, len)) != len) {
+ if (got == -1)
+ log_Printf(LogERROR, "Failed rewriting %s: write: %s\n",
+ _PATH_RESCONF, strerror(errno));
+ else
+ log_Printf(LogERROR, "Failed rewriting %s: wrote %ld of %ld\n",
+ _PATH_RESCONF, (long)got, (long)len);
+ }
+ close(fd);
+ } else
+ log_Printf(LogERROR, "Failed rewriting %s: open: %s\n", _PATH_RESCONF,
+ strerror(errno));
+ } else if (remove(_PATH_RESCONF) == -1)
+ log_Printf(LogERROR, "Failed removing %s: %s\n", _PATH_RESCONF,
+ strerror(errno));
+
+}
+
+int
+ipcp_Show(struct cmdargs const *arg)
+{
+ struct ipcp *ipcp = &arg->bundle->ncp.ipcp;
+
+ prompt_Printf(arg->prompt, "%s [%s]\n", ipcp->fsm.name,
+ State2Nam(ipcp->fsm.state));
+ if (ipcp->fsm.state == ST_OPENED) {
+ prompt_Printf(arg->prompt, " His side: %s, %s\n",
+ inet_ntoa(ipcp->peer_ip), vj2asc(ipcp->peer_compproto));
+ prompt_Printf(arg->prompt, " My side: %s, %s\n",
+ inet_ntoa(ipcp->my_ip), vj2asc(ipcp->my_compproto));
+ prompt_Printf(arg->prompt, " Queued packets: %lu\n",
+ (unsigned long)ipcp_QueueLen(ipcp));
+ }
+
+ prompt_Printf(arg->prompt, "\nDefaults:\n");
+ prompt_Printf(arg->prompt, " FSM retry = %us, max %u Config"
+ " REQ%s, %u Term REQ%s\n", ipcp->cfg.fsm.timeout,
+ ipcp->cfg.fsm.maxreq, ipcp->cfg.fsm.maxreq == 1 ? "" : "s",
+ ipcp->cfg.fsm.maxtrm, ipcp->cfg.fsm.maxtrm == 1 ? "" : "s");
+ prompt_Printf(arg->prompt, " My Address: %s\n",
+ ncprange_ntoa(&ipcp->cfg.my_range));
+ if (ipcp->cfg.HaveTriggerAddress)
+ prompt_Printf(arg->prompt, " Trigger address: %s\n",
+ inet_ntoa(ipcp->cfg.TriggerAddress));
+
+ prompt_Printf(arg->prompt, " VJ compression: %s (%d slots %s slot "
+ "compression)\n", command_ShowNegval(ipcp->cfg.vj.neg),
+ ipcp->cfg.vj.slots, ipcp->cfg.vj.slotcomp ? "with" : "without");
+
+ if (iplist_isvalid(&ipcp->cfg.peer_list))
+ prompt_Printf(arg->prompt, " His Address: %s\n",
+ ipcp->cfg.peer_list.src);
+ else
+ prompt_Printf(arg->prompt, " His Address: %s\n",
+ ncprange_ntoa(&ipcp->cfg.peer_range));
+
+ prompt_Printf(arg->prompt, " DNS: %s",
+ ipcp->cfg.ns.dns[0].s_addr == INADDR_NONE ?
+ "none" : inet_ntoa(ipcp->cfg.ns.dns[0]));
+ if (ipcp->cfg.ns.dns[1].s_addr != INADDR_NONE)
+ prompt_Printf(arg->prompt, ", %s",
+ inet_ntoa(ipcp->cfg.ns.dns[1]));
+ prompt_Printf(arg->prompt, ", %s\n",
+ command_ShowNegval(ipcp->cfg.ns.dns_neg));
+ prompt_Printf(arg->prompt, " Resolver DNS: %s",
+ ipcp->ns.dns[0].s_addr == INADDR_NONE ?
+ "none" : inet_ntoa(ipcp->ns.dns[0]));
+ if (ipcp->ns.dns[1].s_addr != INADDR_NONE &&
+ ipcp->ns.dns[1].s_addr != ipcp->ns.dns[0].s_addr)
+ prompt_Printf(arg->prompt, ", %s",
+ inet_ntoa(ipcp->ns.dns[1]));
+ prompt_Printf(arg->prompt, "\n NetBIOS NS: %s, ",
+ inet_ntoa(ipcp->cfg.ns.nbns[0]));
+ prompt_Printf(arg->prompt, "%s\n\n",
+ inet_ntoa(ipcp->cfg.ns.nbns[1]));
+
+ throughput_disp(&ipcp->throughput, arg->prompt);
+
+ return 0;
+}
+
+int
+ipcp_vjset(struct cmdargs const *arg)
+{
+ if (arg->argc != arg->argn+2)
+ return -1;
+ if (!strcasecmp(arg->argv[arg->argn], "slots")) {
+ int slots;
+
+ slots = atoi(arg->argv[arg->argn+1]);
+ if (slots < 4 || slots > 16)
+ return 1;
+ arg->bundle->ncp.ipcp.cfg.vj.slots = slots;
+ return 0;
+ } else if (!strcasecmp(arg->argv[arg->argn], "slotcomp")) {
+ if (!strcasecmp(arg->argv[arg->argn+1], "on"))
+ arg->bundle->ncp.ipcp.cfg.vj.slotcomp = 1;
+ else if (!strcasecmp(arg->argv[arg->argn+1], "off"))
+ arg->bundle->ncp.ipcp.cfg.vj.slotcomp = 0;
+ else
+ return 2;
+ return 0;
+ }
+ return -1;
+}
+
+void
+ipcp_Init(struct ipcp *ipcp, struct bundle *bundle, struct link *l,
+ const struct fsm_parent *parent)
+{
+ struct hostent *hp;
+ struct in_addr host;
+ char name[MAXHOSTNAMELEN];
+ static const char * const timer_names[] =
+ {"IPCP restart", "IPCP openmode", "IPCP stopped"};
+
+ fsm_Init(&ipcp->fsm, "IPCP", PROTO_IPCP, 1, IPCP_MAXCODE, LogIPCP,
+ bundle, l, parent, &ipcp_Callbacks, timer_names);
+
+ ipcp->cfg.vj.slots = DEF_VJ_STATES;
+ ipcp->cfg.vj.slotcomp = 1;
+ memset(&ipcp->cfg.my_range, '\0', sizeof ipcp->cfg.my_range);
+
+ host.s_addr = htonl(INADDR_LOOPBACK);
+ ipcp->cfg.netmask.s_addr = INADDR_ANY;
+ if (gethostname(name, sizeof name) == 0) {
+ hp = gethostbyname(name);
+ if (hp && hp->h_addrtype == AF_INET && hp->h_length == sizeof host.s_addr)
+ memcpy(&host.s_addr, hp->h_addr, sizeof host.s_addr);
+ }
+ ncprange_setip4(&ipcp->cfg.my_range, host, ipcp->cfg.netmask);
+ ncprange_setip4(&ipcp->cfg.peer_range, ipcp->cfg.netmask, ipcp->cfg.netmask);
+
+ iplist_setsrc(&ipcp->cfg.peer_list, "");
+ ipcp->cfg.HaveTriggerAddress = 0;
+
+ ipcp->cfg.ns.dns[0].s_addr = INADDR_NONE;
+ ipcp->cfg.ns.dns[1].s_addr = INADDR_NONE;
+ ipcp->cfg.ns.dns_neg = 0;
+ ipcp->cfg.ns.nbns[0].s_addr = INADDR_ANY;
+ ipcp->cfg.ns.nbns[1].s_addr = INADDR_ANY;
+
+ ipcp->cfg.fsm.timeout = DEF_FSMRETRY;
+ ipcp->cfg.fsm.maxreq = DEF_FSMTRIES;
+ ipcp->cfg.fsm.maxtrm = DEF_FSMTRIES;
+ ipcp->cfg.vj.neg = NEG_ENABLED|NEG_ACCEPTED;
+
+ memset(&ipcp->vj, '\0', sizeof ipcp->vj);
+
+ ipcp->ns.resolv = NULL;
+ ipcp->ns.resolv_nons = NULL;
+ ipcp->ns.writable = 1;
+ ipcp_LoadDNS(ipcp);
+
+ throughput_init(&ipcp->throughput, SAMPLE_PERIOD);
+ memset(ipcp->Queue, '\0', sizeof ipcp->Queue);
+ ipcp_Setup(ipcp, INADDR_NONE);
+}
+
+void
+ipcp_Destroy(struct ipcp *ipcp)
+{
+ throughput_destroy(&ipcp->throughput);
+
+ if (ipcp->ns.resolv != NULL) {
+ free(ipcp->ns.resolv);
+ ipcp->ns.resolv = NULL;
+ }
+ if (ipcp->ns.resolv_nons != NULL) {
+ free(ipcp->ns.resolv_nons);
+ ipcp->ns.resolv_nons = NULL;
+ }
+}
+
+void
+ipcp_SetLink(struct ipcp *ipcp, struct link *l)
+{
+ ipcp->fsm.link = l;
+}
+
+void
+ipcp_Setup(struct ipcp *ipcp, u_int32_t mask)
+{
+ struct iface *iface = ipcp->fsm.bundle->iface;
+ struct ncpaddr ipaddr;
+ struct in_addr peer;
+ int pos;
+ unsigned n;
+
+ ipcp->fsm.open_mode = 0;
+ ipcp->ifmask.s_addr = mask == INADDR_NONE ? ipcp->cfg.netmask.s_addr : mask;
+
+ if (iplist_isvalid(&ipcp->cfg.peer_list)) {
+ /* Try to give the peer a previously configured IP address */
+ for (n = 0; n < iface->addrs; n++) {
+ if (!ncpaddr_getip4(&iface->addr[n].peer, &peer))
+ continue;
+ if ((pos = iplist_ip2pos(&ipcp->cfg.peer_list, peer)) != -1) {
+ ncpaddr_setip4(&ipaddr, iplist_setcurpos(&ipcp->cfg.peer_list, pos));
+ break;
+ }
+ }
+ if (n == iface->addrs)
+ /* Ok, so none of 'em fit.... pick a random one */
+ ncpaddr_setip4(&ipaddr, iplist_setrandpos(&ipcp->cfg.peer_list));
+
+ ncprange_sethost(&ipcp->cfg.peer_range, &ipaddr);
+ }
+
+ ipcp->heis1172 = 0;
+ ipcp->peer_req = 0;
+ ncprange_getip4addr(&ipcp->cfg.peer_range, &ipcp->peer_ip);
+ ipcp->peer_compproto = 0;
+
+ if (ipcp->cfg.HaveTriggerAddress) {
+ /*
+ * Some implementations of PPP require that we send a
+ * *special* value as our address, even though the rfc specifies
+ * full negotiation (e.g. "0.0.0.0" or Not "0.0.0.0").
+ */
+ ipcp->my_ip = ipcp->cfg.TriggerAddress;
+ log_Printf(LogIPCP, "Using trigger address %s\n",
+ inet_ntoa(ipcp->cfg.TriggerAddress));
+ } else {
+ /*
+ * Otherwise, if we've used an IP number before and it's still within
+ * the network specified on the ``set ifaddr'' line, we really
+ * want to keep that IP number so that we can keep any existing
+ * connections that are bound to that IP.
+ */
+ for (n = 0; n < iface->addrs; n++) {
+ ncprange_getaddr(&iface->addr[n].ifa, &ipaddr);
+ if (ncprange_contains(&ipcp->cfg.my_range, &ipaddr)) {
+ ncpaddr_getip4(&ipaddr, &ipcp->my_ip);
+ break;
+ }
+ }
+ if (n == iface->addrs)
+ ncprange_getip4addr(&ipcp->cfg.my_range, &ipcp->my_ip);
+ }
+
+ if (IsEnabled(ipcp->cfg.vj.neg)
+#ifndef NORADIUS
+ || (ipcp->fsm.bundle->radius.valid && ipcp->fsm.bundle->radius.vj)
+#endif
+ )
+ ipcp->my_compproto = (PROTO_VJCOMP << 16) +
+ ((ipcp->cfg.vj.slots - 1) << 8) +
+ ipcp->cfg.vj.slotcomp;
+ else
+ ipcp->my_compproto = 0;
+ sl_compress_init(&ipcp->vj.cslc, ipcp->cfg.vj.slots - 1);
+
+ ipcp->peer_reject = 0;
+ ipcp->my_reject = 0;
+
+ /* Copy startup values into ipcp->ns.dns */
+ if (ipcp->cfg.ns.dns[0].s_addr != INADDR_NONE)
+ memcpy(ipcp->ns.dns, ipcp->cfg.ns.dns, sizeof ipcp->ns.dns);
+}
+
+static int
+numaddresses(struct in_addr mask)
+{
+ u_int32_t bit, haddr;
+ int n;
+
+ haddr = ntohl(mask.s_addr);
+ bit = 1;
+ n = 1;
+
+ do {
+ if (!(haddr & bit))
+ n <<= 1;
+ } while (bit <<= 1);
+
+ return n;
+}
+
+static int
+ipcp_proxyarp(struct ipcp *ipcp,
+ int (*proxyfun)(struct bundle *, struct in_addr),
+ const struct iface_addr *addr)
+{
+ struct bundle *bundle = ipcp->fsm.bundle;
+ struct in_addr peer, mask, ip;
+ int n, ret;
+
+ if (!ncpaddr_getip4(&addr->peer, &peer)) {
+ log_Printf(LogERROR, "Oops, ipcp_proxyarp() called with unexpected addr\n");
+ return 0;
+ }
+
+ ret = 0;
+
+ if (Enabled(bundle, OPT_PROXYALL)) {
+ ncprange_getip4mask(&addr->ifa, &mask);
+ if ((n = numaddresses(mask)) > 256) {
+ log_Printf(LogWARN, "%s: Too many addresses for proxyall\n",
+ ncprange_ntoa(&addr->ifa));
+ return 0;
+ }
+ ip.s_addr = peer.s_addr & mask.s_addr;
+ if (n >= 4) {
+ ip.s_addr = htonl(ntohl(ip.s_addr) + 1);
+ n -= 2;
+ }
+ while (n) {
+ if (!((ip.s_addr ^ peer.s_addr) & mask.s_addr)) {
+ if (!(ret = (*proxyfun)(bundle, ip)))
+ break;
+ n--;
+ }
+ ip.s_addr = htonl(ntohl(ip.s_addr) + 1);
+ }
+ ret = !n;
+ } else if (Enabled(bundle, OPT_PROXY))
+ ret = (*proxyfun)(bundle, peer);
+
+ return ret;
+}
+
+static int
+ipcp_SetIPaddress(struct ipcp *ipcp, struct in_addr myaddr,
+ struct in_addr hisaddr)
+{
+ struct bundle *bundle = ipcp->fsm.bundle;
+ struct ncpaddr myncpaddr, hisncpaddr;
+ struct ncprange myrange;
+ struct in_addr mask;
+ struct sockaddr_storage ssdst, ssgw, ssmask;
+ struct sockaddr *sadst, *sagw, *samask;
+
+ sadst = (struct sockaddr *)&ssdst;
+ sagw = (struct sockaddr *)&ssgw;
+ samask = (struct sockaddr *)&ssmask;
+
+ ncpaddr_setip4(&hisncpaddr, hisaddr);
+ ncpaddr_setip4(&myncpaddr, myaddr);
+ ncprange_sethost(&myrange, &myncpaddr);
+
+ mask = addr2mask(myaddr);
+
+ if (ipcp->ifmask.s_addr != INADDR_ANY &&
+ (ipcp->ifmask.s_addr & mask.s_addr) == mask.s_addr)
+ ncprange_setip4mask(&myrange, ipcp->ifmask);
+
+ if (!iface_Add(bundle->iface, &bundle->ncp, &myrange, &hisncpaddr,
+ IFACE_ADD_FIRST|IFACE_FORCE_ADD|IFACE_SYSTEM))
+ return 0;
+
+ if (!Enabled(bundle, OPT_IFACEALIAS))
+ iface_Clear(bundle->iface, &bundle->ncp, AF_INET,
+ IFACE_CLEAR_ALIASES|IFACE_SYSTEM);
+
+ if (bundle->ncp.cfg.sendpipe > 0 || bundle->ncp.cfg.recvpipe > 0) {
+ ncprange_getsa(&myrange, &ssgw, &ssmask);
+ ncpaddr_getsa(&hisncpaddr, &ssdst);
+ rt_Update(bundle, sadst, sagw, samask);
+ }
+
+ if (Enabled(bundle, OPT_SROUTES))
+ route_Change(bundle, bundle->ncp.route, &myncpaddr, &hisncpaddr);
+
+#ifndef NORADIUS
+ if (bundle->radius.valid)
+ route_Change(bundle, bundle->radius.routes, &myncpaddr, &hisncpaddr);
+#endif
+
+ return 1; /* Ok */
+}
+
+static struct in_addr
+ChooseHisAddr(struct bundle *bundle, struct in_addr gw)
+{
+ struct in_addr try;
+ u_long f;
+
+ for (f = 0; f < bundle->ncp.ipcp.cfg.peer_list.nItems; f++) {
+ try = iplist_next(&bundle->ncp.ipcp.cfg.peer_list);
+ log_Printf(LogDEBUG, "ChooseHisAddr: Check item %ld (%s)\n",
+ f, inet_ntoa(try));
+ if (ipcp_SetIPaddress(&bundle->ncp.ipcp, gw, try)) {
+ log_Printf(LogIPCP, "Selected IP address %s\n", inet_ntoa(try));
+ break;
+ }
+ }
+
+ if (f == bundle->ncp.ipcp.cfg.peer_list.nItems) {
+ log_Printf(LogDEBUG, "ChooseHisAddr: All addresses in use !\n");
+ try.s_addr = INADDR_ANY;
+ }
+
+ return try;
+}
+
+static void
+IpcpInitRestartCounter(struct fsm *fp, int what)
+{
+ /* Set fsm timer load */
+ struct ipcp *ipcp = fsm2ipcp(fp);
+
+ fp->FsmTimer.load = ipcp->cfg.fsm.timeout * SECTICKS;
+ switch (what) {
+ case FSM_REQ_TIMER:
+ fp->restart = ipcp->cfg.fsm.maxreq;
+ break;
+ case FSM_TRM_TIMER:
+ fp->restart = ipcp->cfg.fsm.maxtrm;
+ break;
+ default:
+ fp->restart = 1;
+ break;
+ }
+}
+
+static void
+IpcpSendConfigReq(struct fsm *fp)
+{
+ /* Send config REQ please */
+ struct physical *p = link2physical(fp->link);
+ struct ipcp *ipcp = fsm2ipcp(fp);
+ u_char buff[MAX_FSM_OPT_LEN];
+ struct fsm_opt *o;
+
+ o = (struct fsm_opt *)buff;
+
+ if ((p && !physical_IsSync(p)) || !REJECTED(ipcp, TY_IPADDR)) {
+ memcpy(o->data, &ipcp->my_ip.s_addr, 4);
+ INC_FSM_OPT(TY_IPADDR, 6, o);
+ }
+
+ if (ipcp->my_compproto && !REJECTED(ipcp, TY_COMPPROTO)) {
+ if (ipcp->heis1172) {
+ u_int16_t proto = PROTO_VJCOMP;
+
+ ua_htons(&proto, o->data);
+ INC_FSM_OPT(TY_COMPPROTO, 4, o);
+ } else {
+ struct compreq req;
+
+ req.proto = htons(ipcp->my_compproto >> 16);
+ req.slots = (ipcp->my_compproto >> 8) & 255;
+ req.compcid = ipcp->my_compproto & 1;
+ memcpy(o->data, &req, 4);
+ INC_FSM_OPT(TY_COMPPROTO, 6, o);
+ }
+ }
+
+ if (IsEnabled(ipcp->cfg.ns.dns_neg)) {
+ if (!REJECTED(ipcp, TY_PRIMARY_DNS - TY_ADJUST_NS)) {
+ memcpy(o->data, &ipcp->ns.dns[0].s_addr, 4);
+ INC_FSM_OPT(TY_PRIMARY_DNS, 6, o);
+ }
+
+ if (!REJECTED(ipcp, TY_SECONDARY_DNS - TY_ADJUST_NS)) {
+ memcpy(o->data, &ipcp->ns.dns[1].s_addr, 4);
+ INC_FSM_OPT(TY_SECONDARY_DNS, 6, o);
+ }
+ }
+
+ fsm_Output(fp, CODE_CONFIGREQ, fp->reqid, buff, (u_char *)o - buff,
+ MB_IPCPOUT);
+}
+
+static void
+IpcpSentTerminateReq(struct fsm *fp __unused)
+{
+ /* Term REQ just sent by FSM */
+}
+
+static void
+IpcpSendTerminateAck(struct fsm *fp, u_char id)
+{
+ /* Send Term ACK please */
+ fsm_Output(fp, CODE_TERMACK, id, NULL, 0, MB_IPCPOUT);
+}
+
+static void
+IpcpLayerStart(struct fsm *fp)
+{
+ /* We're about to start up ! */
+ struct ipcp *ipcp = fsm2ipcp(fp);
+
+ log_Printf(LogIPCP, "%s: LayerStart.\n", fp->link->name);
+ throughput_start(&ipcp->throughput, "IPCP throughput",
+ Enabled(fp->bundle, OPT_THROUGHPUT));
+ fp->more.reqs = fp->more.naks = fp->more.rejs = ipcp->cfg.fsm.maxreq * 3;
+ ipcp->peer_req = 0;
+}
+
+static void
+IpcpLayerFinish(struct fsm *fp)
+{
+ /* We're now down */
+ struct ipcp *ipcp = fsm2ipcp(fp);
+
+ log_Printf(LogIPCP, "%s: LayerFinish.\n", fp->link->name);
+ throughput_stop(&ipcp->throughput);
+ throughput_log(&ipcp->throughput, LogIPCP, NULL);
+}
+
+/*
+ * Called from iface_Add() via ncp_IfaceAddrAdded()
+ */
+void
+ipcp_IfaceAddrAdded(struct ipcp *ipcp, const struct iface_addr *addr)
+{
+ struct bundle *bundle = ipcp->fsm.bundle;
+
+ if (Enabled(bundle, OPT_PROXY) || Enabled(bundle, OPT_PROXYALL))
+ ipcp_proxyarp(ipcp, arp_SetProxy, addr);
+}
+
+/*
+ * Called from iface_Clear() and iface_Delete() via ncp_IfaceAddrDeleted()
+ */
+void
+ipcp_IfaceAddrDeleted(struct ipcp *ipcp, const struct iface_addr *addr)
+{
+ struct bundle *bundle = ipcp->fsm.bundle;
+
+ if (Enabled(bundle, OPT_PROXY) || Enabled(bundle, OPT_PROXYALL))
+ ipcp_proxyarp(ipcp, arp_ClearProxy, addr);
+}
+
+static void
+IpcpLayerDown(struct fsm *fp)
+{
+ /* About to come down */
+ struct ipcp *ipcp = fsm2ipcp(fp);
+ static int recursing;
+ char addr[16];
+
+ if (!recursing++) {
+ snprintf(addr, sizeof addr, "%s", inet_ntoa(ipcp->my_ip));
+ log_Printf(LogIPCP, "%s: LayerDown: %s\n", fp->link->name, addr);
+
+#ifndef NORADIUS
+ radius_Flush(&fp->bundle->radius);
+ radius_Account(&fp->bundle->radius, &fp->bundle->radacct,
+ fp->bundle->links, RAD_STOP, &ipcp->throughput);
+
+ if (fp->bundle->radius.cfg.file && fp->bundle->radius.filterid)
+ system_Select(fp->bundle, fp->bundle->radius.filterid, LINKDOWNFILE,
+ NULL, NULL);
+ radius_StopTimer(&fp->bundle->radius);
+#endif
+
+ /*
+ * XXX this stuff should really live in the FSM. Our config should
+ * associate executable sections in files with events.
+ */
+ if (system_Select(fp->bundle, addr, LINKDOWNFILE, NULL, NULL) < 0) {
+ if (bundle_GetLabel(fp->bundle)) {
+ if (system_Select(fp->bundle, bundle_GetLabel(fp->bundle),
+ LINKDOWNFILE, NULL, NULL) < 0)
+ system_Select(fp->bundle, "MYADDR", LINKDOWNFILE, NULL, NULL);
+ } else
+ system_Select(fp->bundle, "MYADDR", LINKDOWNFILE, NULL, NULL);
+ }
+
+ ipcp_Setup(ipcp, INADDR_NONE);
+ }
+ recursing--;
+}
+
+int
+ipcp_InterfaceUp(struct ipcp *ipcp)
+{
+ if (!ipcp_SetIPaddress(ipcp, ipcp->my_ip, ipcp->peer_ip)) {
+ log_Printf(LogERROR, "ipcp_InterfaceUp: unable to set ip address\n");
+ return 0;
+ }
+
+ if (!iface_SetFlags(ipcp->fsm.bundle->iface->name, IFF_UP)) {
+ log_Printf(LogERROR, "ipcp_InterfaceUp: Can't set the IFF_UP flag on %s\n",
+ ipcp->fsm.bundle->iface->name);
+ return 0;
+ }
+
+#ifndef NONAT
+ if (ipcp->fsm.bundle->NatEnabled)
+ LibAliasSetAddress(la, ipcp->my_ip);
+#endif
+
+ return 1;
+}
+
+static int
+IpcpLayerUp(struct fsm *fp)
+{
+ /* We're now up */
+ struct ipcp *ipcp = fsm2ipcp(fp);
+ char tbuff[16];
+
+ log_Printf(LogIPCP, "%s: LayerUp.\n", fp->link->name);
+ snprintf(tbuff, sizeof tbuff, "%s", inet_ntoa(ipcp->my_ip));
+ log_Printf(LogIPCP, "myaddr %s hisaddr = %s\n",
+ tbuff, inet_ntoa(ipcp->peer_ip));
+
+ if (ipcp->peer_compproto >> 16 == PROTO_VJCOMP)
+ sl_compress_init(&ipcp->vj.cslc, (ipcp->peer_compproto >> 8) & 255);
+
+ if (!ipcp_InterfaceUp(ipcp))
+ return 0;
+
+#ifndef NORADIUS
+ radius_Account_Set_Ip(&fp->bundle->radacct, &ipcp->peer_ip, &ipcp->ifmask);
+ radius_Account(&fp->bundle->radius, &fp->bundle->radacct, fp->bundle->links,
+ RAD_START, &ipcp->throughput);
+
+ if (fp->bundle->radius.cfg.file && fp->bundle->radius.filterid)
+ system_Select(fp->bundle, fp->bundle->radius.filterid, LINKUPFILE,
+ NULL, NULL);
+ radius_StartTimer(fp->bundle);
+#endif
+
+ /*
+ * XXX this stuff should really live in the FSM. Our config should
+ * associate executable sections in files with events.
+ */
+ if (system_Select(fp->bundle, tbuff, LINKUPFILE, NULL, NULL) < 0) {
+ if (bundle_GetLabel(fp->bundle)) {
+ if (system_Select(fp->bundle, bundle_GetLabel(fp->bundle),
+ LINKUPFILE, NULL, NULL) < 0)
+ system_Select(fp->bundle, "MYADDR", LINKUPFILE, NULL, NULL);
+ } else
+ system_Select(fp->bundle, "MYADDR", LINKUPFILE, NULL, NULL);
+ }
+
+ fp->more.reqs = fp->more.naks = fp->more.rejs = ipcp->cfg.fsm.maxreq * 3;
+ log_DisplayPrompts();
+
+ return 1;
+}
+
+static void
+ipcp_ValidateReq(struct ipcp *ipcp, struct in_addr ip, struct fsm_decode *dec)
+{
+ struct bundle *bundle = ipcp->fsm.bundle;
+ struct iface *iface = bundle->iface;
+ struct in_addr myaddr, peer;
+ unsigned n;
+
+ if (iplist_isvalid(&ipcp->cfg.peer_list)) {
+ ncprange_getip4addr(&ipcp->cfg.my_range, &myaddr);
+ if (ip.s_addr == INADDR_ANY ||
+ iplist_ip2pos(&ipcp->cfg.peer_list, ip) < 0 ||
+ !ipcp_SetIPaddress(ipcp, myaddr, ip)) {
+ log_Printf(LogIPCP, "%s: Address invalid or already in use\n",
+ inet_ntoa(ip));
+ /*
+ * If we've already had a valid address configured for the peer,
+ * try NAKing with that so that we don't have to upset things
+ * too much.
+ */
+ for (n = 0; n < iface->addrs; n++) {
+ if (!ncpaddr_getip4(&iface->addr[n].peer, &peer))
+ continue;
+ if (iplist_ip2pos(&ipcp->cfg.peer_list, peer) >= 0) {
+ ipcp->peer_ip = peer;
+ break;
+ }
+ }
+
+ if (n == iface->addrs) {
+ /* Just pick an IP number from our list */
+ ipcp->peer_ip = ChooseHisAddr(bundle, myaddr);
+ }
+
+ if (ipcp->peer_ip.s_addr == INADDR_ANY) {
+ *dec->rejend++ = TY_IPADDR;
+ *dec->rejend++ = 6;
+ memcpy(dec->rejend, &ip.s_addr, 4);
+ dec->rejend += 4;
+ } else {
+ *dec->nakend++ = TY_IPADDR;
+ *dec->nakend++ = 6;
+ memcpy(dec->nakend, &ipcp->peer_ip.s_addr, 4);
+ dec->nakend += 4;
+ }
+ return;
+ }
+ } else if (ip.s_addr == INADDR_ANY ||
+ !ncprange_containsip4(&ipcp->cfg.peer_range, ip)) {
+ /*
+ * If the destination address is not acceptable, NAK with what we
+ * want to use.
+ */
+ *dec->nakend++ = TY_IPADDR;
+ *dec->nakend++ = 6;
+ for (n = 0; n < iface->addrs; n++)
+ if (ncprange_contains(&ipcp->cfg.peer_range, &iface->addr[n].peer)) {
+ /* We prefer the already-configured address */
+ ncpaddr_getip4addr(&iface->addr[n].peer, (u_int32_t *)dec->nakend);
+ break;
+ }
+
+ if (n == iface->addrs)
+ memcpy(dec->nakend, &ipcp->peer_ip.s_addr, 4);
+
+ dec->nakend += 4;
+ return;
+ }
+
+ ipcp->peer_ip = ip;
+ *dec->ackend++ = TY_IPADDR;
+ *dec->ackend++ = 6;
+ memcpy(dec->ackend, &ip.s_addr, 4);
+ dec->ackend += 4;
+}
+
+static void
+IpcpDecodeConfig(struct fsm *fp, u_char *cp, u_char *end, int mode_type,
+ struct fsm_decode *dec)
+{
+ /* Deal with incoming PROTO_IPCP */
+ struct ncpaddr ncpaddr;
+ struct ipcp *ipcp = fsm2ipcp(fp);
+ int gotdnsnak;
+ u_int32_t compproto;
+ struct compreq pcomp;
+ struct in_addr ipaddr, dstipaddr, have_ip;
+ char tbuff[100], tbuff2[100];
+ struct fsm_opt *opt, nak;
+
+ gotdnsnak = 0;
+
+ while (end - cp >= (int)sizeof(opt->hdr)) {
+ if ((opt = fsm_readopt(&cp)) == NULL)
+ break;
+
+ snprintf(tbuff, sizeof tbuff, " %s[%d]", protoname(opt->hdr.id),
+ opt->hdr.len);
+
+ switch (opt->hdr.id) {
+ case TY_IPADDR: /* RFC1332 */
+ memcpy(&ipaddr.s_addr, opt->data, 4);
+ log_Printf(LogIPCP, "%s %s\n", tbuff, inet_ntoa(ipaddr));
+
+ switch (mode_type) {
+ case MODE_REQ:
+ ipcp->peer_req = 1;
+ ipcp_ValidateReq(ipcp, ipaddr, dec);
+ break;
+
+ case MODE_NAK:
+ if (ncprange_containsip4(&ipcp->cfg.my_range, ipaddr)) {
+ /* Use address suggested by peer */
+ snprintf(tbuff2, sizeof tbuff2, "%s changing address: %s ", tbuff,
+ inet_ntoa(ipcp->my_ip));
+ log_Printf(LogIPCP, "%s --> %s\n", tbuff2, inet_ntoa(ipaddr));
+ ipcp->my_ip = ipaddr;
+ ncpaddr_setip4(&ncpaddr, ipcp->my_ip);
+ bundle_AdjustFilters(fp->bundle, &ncpaddr, NULL);
+ } else {
+ log_Printf(log_IsKept(LogIPCP) ? LogIPCP : LogPHASE,
+ "%s: Unacceptable address!\n", inet_ntoa(ipaddr));
+ fsm_Close(&ipcp->fsm);
+ }
+ break;
+
+ case MODE_REJ:
+ ipcp->peer_reject |= (1 << opt->hdr.id);
+ break;
+ }
+ break;
+
+ case TY_COMPPROTO:
+ memcpy(&pcomp, opt->data, sizeof pcomp);
+ compproto = (ntohs(pcomp.proto) << 16) + ((int)pcomp.slots << 8) +
+ pcomp.compcid;
+ log_Printf(LogIPCP, "%s %s\n", tbuff, vj2asc(compproto));
+
+ switch (mode_type) {
+ case MODE_REQ:
+ if (!IsAccepted(ipcp->cfg.vj.neg))
+ fsm_rej(dec, opt);
+ else {
+ switch (opt->hdr.len) {
+ case 4: /* RFC1172 */
+ if (ntohs(pcomp.proto) == PROTO_VJCOMP) {
+ log_Printf(LogWARN, "Peer is speaking RFC1172 compression "
+ "protocol !\n");
+ ipcp->heis1172 = 1;
+ ipcp->peer_compproto = compproto;
+ fsm_ack(dec, opt);
+ } else {
+ pcomp.proto = htons(PROTO_VJCOMP);
+ nak.hdr.id = TY_COMPPROTO;
+ nak.hdr.len = 4;
+ memcpy(nak.data, &pcomp, 2);
+ fsm_nak(dec, &nak);
+ }
+ break;
+ case 6: /* RFC1332 */
+ if (ntohs(pcomp.proto) == PROTO_VJCOMP) {
+ /* We know pcomp.slots' max value == MAX_VJ_STATES */
+ if (pcomp.slots >= MIN_VJ_STATES) {
+ /* Ok, we can do that */
+ ipcp->peer_compproto = compproto;
+ ipcp->heis1172 = 0;
+ fsm_ack(dec, opt);
+ } else {
+ /* Get as close as we can to what he wants */
+ ipcp->heis1172 = 0;
+ pcomp.slots = MIN_VJ_STATES;
+ nak.hdr.id = TY_COMPPROTO;
+ nak.hdr.len = 4;
+ memcpy(nak.data, &pcomp, 2);
+ fsm_nak(dec, &nak);
+ }
+ } else {
+ /* What we really want */
+ pcomp.proto = htons(PROTO_VJCOMP);
+ pcomp.slots = DEF_VJ_STATES;
+ pcomp.compcid = 1;
+ nak.hdr.id = TY_COMPPROTO;
+ nak.hdr.len = 6;
+ memcpy(nak.data, &pcomp, sizeof pcomp);
+ fsm_nak(dec, &nak);
+ }
+ break;
+ default:
+ fsm_rej(dec, opt);
+ break;
+ }
+ }
+ break;
+
+ case MODE_NAK:
+ if (ntohs(pcomp.proto) == PROTO_VJCOMP) {
+ /* We know pcomp.slots' max value == MAX_VJ_STATES */
+ if (pcomp.slots < MIN_VJ_STATES)
+ pcomp.slots = MIN_VJ_STATES;
+ compproto = (ntohs(pcomp.proto) << 16) + (pcomp.slots << 8) +
+ pcomp.compcid;
+ } else
+ compproto = 0;
+ log_Printf(LogIPCP, "%s changing compproto: %08x --> %08x\n",
+ tbuff, ipcp->my_compproto, compproto);
+ ipcp->my_compproto = compproto;
+ break;
+
+ case MODE_REJ:
+ ipcp->peer_reject |= (1 << opt->hdr.id);
+ break;
+ }
+ break;
+
+ case TY_IPADDRS: /* RFC1172 */
+ memcpy(&ipaddr.s_addr, opt->data, 4);
+ memcpy(&dstipaddr.s_addr, opt->data + 4, 4);
+ snprintf(tbuff2, sizeof tbuff2, "%s %s,", tbuff, inet_ntoa(ipaddr));
+ log_Printf(LogIPCP, "%s %s\n", tbuff2, inet_ntoa(dstipaddr));
+
+ switch (mode_type) {
+ case MODE_REQ:
+ fsm_rej(dec, opt);
+ break;
+
+ case MODE_NAK:
+ case MODE_REJ:
+ break;
+ }
+ break;
+
+ case TY_PRIMARY_DNS: /* DNS negotiation (rfc1877) */
+ case TY_SECONDARY_DNS:
+ memcpy(&ipaddr.s_addr, opt->data, 4);
+ log_Printf(LogIPCP, "%s %s\n", tbuff, inet_ntoa(ipaddr));
+
+ switch (mode_type) {
+ case MODE_REQ:
+ if (!IsAccepted(ipcp->cfg.ns.dns_neg)) {
+ ipcp->my_reject |= (1 << (opt->hdr.id - TY_ADJUST_NS));
+ fsm_rej(dec, opt);
+ break;
+ }
+ have_ip = ipcp->ns.dns[opt->hdr.id == TY_PRIMARY_DNS ? 0 : 1];
+
+ if (opt->hdr.id == TY_PRIMARY_DNS && ipaddr.s_addr != have_ip.s_addr &&
+ ipaddr.s_addr == ipcp->ns.dns[1].s_addr) {
+ /* Swap 'em 'round */
+ ipcp->ns.dns[0] = ipcp->ns.dns[1];
+ ipcp->ns.dns[1] = have_ip;
+ have_ip = ipcp->ns.dns[0];
+ }
+
+ if (ipaddr.s_addr != have_ip.s_addr) {
+ /*
+ * The client has got the DNS stuff wrong (first request) so
+ * we'll tell 'em how it is
+ */
+ nak.hdr.id = opt->hdr.id;
+ nak.hdr.len = 6;
+ memcpy(nak.data, &have_ip.s_addr, 4);
+ fsm_nak(dec, &nak);
+ } else {
+ /*
+ * Otherwise they have it right (this time) so we send an ack packet
+ * back confirming it... end of story
+ */
+ fsm_ack(dec, opt);
+ }
+ break;
+
+ case MODE_NAK:
+ if (IsEnabled(ipcp->cfg.ns.dns_neg)) {
+ gotdnsnak = 1;
+ memcpy(&ipcp->ns.dns[opt->hdr.id == TY_PRIMARY_DNS ? 0 : 1].s_addr,
+ opt->data, 4);
+ }
+ break;
+
+ case MODE_REJ: /* Can't do much, stop asking */
+ ipcp->peer_reject |= (1 << (opt->hdr.id - TY_ADJUST_NS));
+ break;
+ }
+ break;
+
+ case TY_PRIMARY_NBNS: /* M$ NetBIOS nameserver hack (rfc1877) */
+ case TY_SECONDARY_NBNS:
+ memcpy(&ipaddr.s_addr, opt->data, 4);
+ log_Printf(LogIPCP, "%s %s\n", tbuff, inet_ntoa(ipaddr));
+
+ switch (mode_type) {
+ case MODE_REQ:
+ have_ip.s_addr =
+ ipcp->cfg.ns.nbns[opt->hdr.id == TY_PRIMARY_NBNS ? 0 : 1].s_addr;
+
+ if (have_ip.s_addr == INADDR_ANY) {
+ log_Printf(LogIPCP, "NBNS REQ - rejected - nbns not set\n");
+ ipcp->my_reject |= (1 << (opt->hdr.id - TY_ADJUST_NS));
+ fsm_rej(dec, opt);
+ break;
+ }
+
+ if (ipaddr.s_addr != have_ip.s_addr) {
+ nak.hdr.id = opt->hdr.id;
+ nak.hdr.len = 6;
+ memcpy(nak.data, &have_ip.s_addr, 4);
+ fsm_nak(dec, &nak);
+ } else
+ fsm_ack(dec, opt);
+ break;
+
+ case MODE_NAK:
+ log_Printf(LogIPCP, "MS NBNS req %d - NAK??\n", opt->hdr.id);
+ break;
+
+ case MODE_REJ:
+ log_Printf(LogIPCP, "MS NBNS req %d - REJ??\n", opt->hdr.id);
+ break;
+ }
+ break;
+
+ default:
+ if (mode_type != MODE_NOP) {
+ ipcp->my_reject |= (1 << opt->hdr.id);
+ fsm_rej(dec, opt);
+ }
+ break;
+ }
+ }
+
+ if (gotdnsnak) {
+ if (ipcp->ns.writable) {
+ log_Printf(LogDEBUG, "Updating resolver\n");
+ if (!ipcp_WriteDNS(ipcp)) {
+ ipcp->peer_reject |= (1 << (TY_PRIMARY_DNS - TY_ADJUST_NS));
+ ipcp->peer_reject |= (1 << (TY_SECONDARY_DNS - TY_ADJUST_NS));
+ } else
+ bundle_AdjustDNS(fp->bundle);
+ } else {
+ log_Printf(LogDEBUG, "Not updating resolver (readonly)\n");
+ bundle_AdjustDNS(fp->bundle);
+ }
+ }
+
+ if (mode_type != MODE_NOP) {
+ if (mode_type == MODE_REQ && !ipcp->peer_req) {
+ if (dec->rejend == dec->rej && dec->nakend == dec->nak) {
+ /*
+ * Pretend the peer has requested an IP.
+ * We do this to ensure that we only send one NAK if the only
+ * reason for the NAK is because the peer isn't sending a
+ * TY_IPADDR REQ. This stops us from repeatedly trying to tell
+ * the peer that we have to have an IP address on their end.
+ */
+ ipcp->peer_req = 1;
+ }
+ ipaddr.s_addr = INADDR_ANY;
+ ipcp_ValidateReq(ipcp, ipaddr, dec);
+ }
+ fsm_opt_normalise(dec);
+ }
+}
+
+extern struct mbuf *
+ipcp_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
+{
+ /* Got PROTO_IPCP from link */
+ m_settype(bp, MB_IPCPIN);
+ if (bundle_Phase(bundle) == PHASE_NETWORK)
+ fsm_Input(&bundle->ncp.ipcp.fsm, bp);
+ else {
+ if (bundle_Phase(bundle) < PHASE_NETWORK)
+ log_Printf(LogIPCP, "%s: Error: Unexpected IPCP in phase %s (ignored)\n",
+ l->name, bundle_PhaseName(bundle));
+ m_freem(bp);
+ }
+ return NULL;
+}
+
+int
+ipcp_UseHisIPaddr(struct bundle *bundle, struct in_addr hisaddr)
+{
+ struct ipcp *ipcp = &bundle->ncp.ipcp;
+ struct in_addr myaddr;
+
+ memset(&ipcp->cfg.peer_range, '\0', sizeof ipcp->cfg.peer_range);
+ iplist_reset(&ipcp->cfg.peer_list);
+ ipcp->peer_ip = hisaddr;
+ ncprange_setip4host(&ipcp->cfg.peer_range, hisaddr);
+ ncprange_getip4addr(&ipcp->cfg.my_range, &myaddr);
+
+ return ipcp_SetIPaddress(ipcp, myaddr, hisaddr);
+}
+
+int
+ipcp_UseHisaddr(struct bundle *bundle, const char *hisaddr, int setaddr)
+{
+ struct in_addr myaddr;
+ struct ncp *ncp = &bundle->ncp;
+ struct ipcp *ipcp = &ncp->ipcp;
+ struct ncpaddr ncpaddr;
+
+ /* Use `hisaddr' for the peers address (set iface if `setaddr') */
+ memset(&ipcp->cfg.peer_range, '\0', sizeof ipcp->cfg.peer_range);
+ iplist_reset(&ipcp->cfg.peer_list);
+ if (strpbrk(hisaddr, ",-")) {
+ iplist_setsrc(&ipcp->cfg.peer_list, hisaddr);
+ if (iplist_isvalid(&ipcp->cfg.peer_list)) {
+ iplist_setrandpos(&ipcp->cfg.peer_list);
+ ipcp->peer_ip = ChooseHisAddr(bundle, ipcp->my_ip);
+ if (ipcp->peer_ip.s_addr == INADDR_ANY) {
+ log_Printf(LogWARN, "%s: None available !\n", ipcp->cfg.peer_list.src);
+ return 0;
+ }
+ ncprange_setip4host(&ipcp->cfg.peer_range, ipcp->peer_ip);
+ } else {
+ log_Printf(LogWARN, "%s: Invalid range !\n", hisaddr);
+ return 0;
+ }
+ } else if (ncprange_aton(&ipcp->cfg.peer_range, ncp, hisaddr) != 0) {
+ if (ncprange_family(&ipcp->cfg.my_range) != AF_INET) {
+ log_Printf(LogWARN, "%s: Not an AF_INET address !\n", hisaddr);
+ return 0;
+ }
+ ncprange_getip4addr(&ipcp->cfg.my_range, &myaddr);
+ ncprange_getip4addr(&ipcp->cfg.peer_range, &ipcp->peer_ip);
+
+ if (setaddr && !ipcp_SetIPaddress(ipcp, myaddr, ipcp->peer_ip))
+ return 0;
+ } else
+ return 0;
+
+ ncpaddr_setip4(&ncpaddr, ipcp->peer_ip);
+ bundle_AdjustFilters(bundle, NULL, &ncpaddr);
+
+ return 1; /* Ok */
+}
+
+struct in_addr
+addr2mask(struct in_addr addr)
+{
+ u_int32_t haddr = ntohl(addr.s_addr);
+
+ haddr = IN_CLASSA(haddr) ? IN_CLASSA_NET :
+ IN_CLASSB(haddr) ? IN_CLASSB_NET :
+ IN_CLASSC_NET;
+ addr.s_addr = htonl(haddr);
+
+ return addr;
+}
+
+size_t
+ipcp_QueueLen(struct ipcp *ipcp)
+{
+ struct mqueue *q;
+ size_t result;
+
+ result = 0;
+ for (q = ipcp->Queue; q < ipcp->Queue + IPCP_QUEUES(ipcp); q++)
+ result += q->len;
+
+ return result;
+}
+
+int
+ipcp_PushPacket(struct ipcp *ipcp, struct link *l)
+{
+ struct bundle *bundle = ipcp->fsm.bundle;
+ struct mqueue *queue;
+ struct mbuf *bp;
+ int m_len;
+ u_int32_t secs = 0;
+ unsigned alivesecs = 0;
+
+ if (ipcp->fsm.state != ST_OPENED)
+ return 0;
+
+ /*
+ * If ccp is not open but is required, do nothing.
+ */
+ if (l->ccp.fsm.state != ST_OPENED && ccp_Required(&l->ccp)) {
+ log_Printf(LogPHASE, "%s: Not transmitting... waiting for CCP\n", l->name);
+ return 0;
+ }
+
+ queue = ipcp->Queue + IPCP_QUEUES(ipcp) - 1;
+ do {
+ if (queue->top) {
+ bp = m_dequeue(queue);
+ bp = mbuf_Read(bp, &secs, sizeof secs);
+ bp = m_pullup(bp);
+ m_len = m_length(bp);
+ if (!FilterCheck(MBUF_CTOP(bp), AF_INET, &bundle->filter.alive,
+ &alivesecs)) {
+ if (secs == 0)
+ secs = alivesecs;
+ bundle_StartIdleTimer(bundle, secs);
+ }
+ link_PushPacket(l, bp, bundle, 0, PROTO_IP);
+ ipcp_AddOutOctets(ipcp, m_len);
+ return 1;
+ }
+ } while (queue-- != ipcp->Queue);
+
+ return 0;
+}
diff --git a/usr.sbin/ppp/ipcp.h b/usr.sbin/ppp/ipcp.h
new file mode 100644
index 0000000..dc1ed9e
--- /dev/null
+++ b/usr.sbin/ppp/ipcp.h
@@ -0,0 +1,132 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+#define IPCP_MAXCODE CODE_CODEREJ
+
+#define TY_IPADDRS 1
+#define TY_COMPPROTO 2
+#define TY_IPADDR 3
+
+/* Domain NameServer and NetBIOS NameServer options */
+
+#define TY_PRIMARY_DNS 129
+#define TY_PRIMARY_NBNS 130
+#define TY_SECONDARY_DNS 131
+#define TY_SECONDARY_NBNS 132
+#define TY_ADJUST_NS 119 /* subtract from NS val for REJECT bit */
+
+struct ipcp {
+ struct fsm fsm; /* The finite state machine */
+
+ struct {
+ struct {
+ int slots; /* Maximum VJ slots */
+ unsigned slotcomp : 1; /* Slot compression */
+ unsigned neg : 2; /* VJ negotiation */
+ } vj;
+
+ struct ncprange my_range; /* MYADDR spec */
+ struct in_addr netmask; /* Iface netmask (unused by most OSs) */
+ struct ncprange peer_range; /* HISADDR spec */
+ struct iplist peer_list; /* Ranges of HISADDR values */
+
+ struct in_addr TriggerAddress; /* Address to suggest in REQ */
+ unsigned HaveTriggerAddress : 1; /* Trigger address specified */
+
+ struct {
+ struct in_addr dns[2]; /* DNS addresses offered */
+ unsigned dns_neg : 2; /* dns negotiation */
+ struct in_addr nbns[2]; /* NetBIOS NS addresses offered */
+ } ns;
+
+ struct fsm_retry fsm; /* frequency to resend requests */
+ } cfg;
+
+ struct {
+ struct slcompress cslc; /* VJ state */
+ struct slstat slstat; /* VJ statistics */
+ } vj;
+
+ struct {
+ unsigned resolver : 1; /* Found resolv.conf ? */
+ unsigned writable : 1; /* Can write resolv.conf ? */
+ struct in_addr dns[2]; /* Current DNS addresses */
+ char *resolv; /* Contents of resolv.conf */
+ char *resolv_nons; /* Contents of resolv.conf without ns */
+ } ns;
+
+ unsigned heis1172 : 1; /* True if he is speaking rfc1172 */
+
+ unsigned peer_req : 1; /* Any TY_IPADDR REQs from the peer ? */
+ struct in_addr peer_ip; /* IP address he's willing to use */
+ u_int32_t peer_compproto; /* VJ params he's willing to use */
+
+ struct in_addr ifmask; /* Interface netmask */
+
+ struct in_addr my_ip; /* IP address I'm willing to use */
+ u_int32_t my_compproto; /* VJ params I'm willing to use */
+
+ u_int32_t peer_reject; /* Request codes rejected by peer */
+ u_int32_t my_reject; /* Request codes I have rejected */
+
+ struct pppThroughput throughput; /* throughput statistics */
+ struct mqueue Queue[3]; /* Output packet queues */
+};
+
+#define fsm2ipcp(fp) (fp->proto == PROTO_IPCP ? (struct ipcp *)fp : NULL)
+#define IPCP_QUEUES(ipcp) (sizeof ipcp->Queue / sizeof ipcp->Queue[0])
+
+struct bundle;
+struct link;
+struct cmdargs;
+struct iface_addr;
+
+extern void ipcp_Init(struct ipcp *, struct bundle *, struct link *,
+ const struct fsm_parent *);
+extern void ipcp_Destroy(struct ipcp *);
+extern void ipcp_Setup(struct ipcp *, u_int32_t);
+extern void ipcp_SetLink(struct ipcp *, struct link *);
+
+extern int ipcp_Show(struct cmdargs const *);
+extern struct mbuf *ipcp_Input(struct bundle *, struct link *, struct mbuf *);
+extern void ipcp_AddInOctets(struct ipcp *, int);
+extern void ipcp_AddOutOctets(struct ipcp *, int);
+extern int ipcp_UseHisIPaddr(struct bundle *, struct in_addr);
+extern int ipcp_UseHisaddr(struct bundle *, const char *, int);
+extern int ipcp_vjset(struct cmdargs const *);
+extern void ipcp_IfaceAddrAdded(struct ipcp *, const struct iface_addr *);
+extern void ipcp_IfaceAddrDeleted(struct ipcp *, const struct iface_addr *);
+extern int ipcp_InterfaceUp(struct ipcp *);
+extern struct in_addr addr2mask(struct in_addr);
+extern int ipcp_WriteDNS(struct ipcp *);
+extern void ipcp_RestoreDNS(struct ipcp *);
+extern void ipcp_LoadDNS(struct ipcp *);
+extern size_t ipcp_QueueLen(struct ipcp *);
+extern int ipcp_PushPacket(struct ipcp *, struct link *);
diff --git a/usr.sbin/ppp/iplist.c b/usr.sbin/ppp/iplist.c
new file mode 100644
index 0000000..a7fae6e
--- /dev/null
+++ b/usr.sbin/ppp/iplist.c
@@ -0,0 +1,225 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "log.h"
+#include "defs.h"
+#include "iplist.h"
+
+static int
+do_inet_aton(const char *start, const char *end, struct in_addr *ip)
+{
+ char ipstr[16];
+
+ if (end - start > 15) {
+ log_Printf(LogWARN, "%.*s: Invalid IP address\n", (int)(end-start), start);
+ return 0;
+ }
+ strncpy(ipstr, start, end-start);
+ ipstr[end-start] = '\0';
+ return inet_aton(ipstr, ip);
+}
+
+static void
+iplist_first(struct iplist *list)
+{
+ list->cur.pos = -1;
+}
+
+static int
+iplist_setrange(struct iplist *list, char *range)
+{
+ char *ptr, *to;
+
+ if ((ptr = strpbrk(range, ",-")) == NULL) {
+ if (!inet_aton(range, &list->cur.ip))
+ return 0;
+ list->cur.lstart = ntohl(list->cur.ip.s_addr);
+ list->cur.nItems = 1;
+ } else {
+ if (!do_inet_aton(range, ptr, &list->cur.ip))
+ return 0;
+ if (*ptr == ',') {
+ list->cur.lstart = ntohl(list->cur.ip.s_addr);
+ list->cur.nItems = 1;
+ } else {
+ struct in_addr endip;
+
+ to = ptr+1;
+ if ((ptr = strpbrk(to, ",-")) == NULL)
+ ptr = to + strlen(to);
+ if (*to == '-')
+ return 0;
+ if (!do_inet_aton(to, ptr, &endip))
+ return 0;
+ list->cur.lstart = ntohl(list->cur.ip.s_addr);
+ list->cur.nItems = ntohl(endip.s_addr) - list->cur.lstart + 1;
+ if (list->cur.nItems < 1)
+ return 0;
+ }
+ }
+ list->cur.srcitem = 0;
+ list->cur.srcptr = range;
+ return 1;
+}
+
+static int
+iplist_nextrange(struct iplist *list)
+{
+ char *ptr, *to, *end;
+
+ ptr = list->cur.srcptr;
+ if (ptr != NULL && (ptr = strchr(ptr, ',')) != NULL)
+ ptr++;
+ else
+ ptr = list->src;
+
+ while (*ptr != '\0' && !iplist_setrange(list, ptr)) {
+ if ((end = strchr(ptr, ',')) == NULL)
+ end = ptr + strlen(ptr);
+ if (end == ptr)
+ return 0;
+ log_Printf(LogWARN, "%.*s: Invalid IP range (skipping)\n",
+ (int)(end - ptr), ptr);
+ to = ptr;
+ do
+ *to = *end++;
+ while (*to++ != '\0');
+ if (*ptr == '\0')
+ ptr = list->src;
+ }
+
+ return 1;
+}
+
+struct in_addr
+iplist_next(struct iplist *list)
+{
+ if (list->cur.pos == -1) {
+ list->cur.srcptr = NULL;
+ if (!iplist_nextrange(list)) {
+ list->cur.ip.s_addr = INADDR_ANY;
+ return list->cur.ip;
+ }
+ } else if (++list->cur.srcitem == list->cur.nItems) {
+ if (!iplist_nextrange(list)) {
+ list->cur.ip.s_addr = INADDR_ANY;
+ list->cur.pos = -1;
+ return list->cur.ip;
+ }
+ } else
+ list->cur.ip.s_addr = htonl(list->cur.lstart + list->cur.srcitem);
+ list->cur.pos++;
+
+ return list->cur.ip;
+}
+
+int
+iplist_setsrc(struct iplist *list, const char *src)
+{
+ strncpy(list->src, src, sizeof list->src - 1);
+ list->src[sizeof list->src - 1] = '\0';
+ list->cur.srcptr = list->src;
+ do {
+ if (iplist_nextrange(list))
+ list->nItems += list->cur.nItems;
+ else
+ return 0;
+ } while (list->cur.srcptr != list->src);
+ return 1;
+}
+
+void
+iplist_reset(struct iplist *list)
+{
+ list->src[0] = '\0';
+ list->nItems = 0;
+ list->cur.pos = -1;
+}
+
+struct in_addr
+iplist_setcurpos(struct iplist *list, long pos)
+{
+ if (pos < 0 || (unsigned)pos >= list->nItems) {
+ list->cur.pos = -1;
+ list->cur.ip.s_addr = INADDR_ANY;
+ return list->cur.ip;
+ }
+
+ list->cur.srcptr = NULL;
+ list->cur.pos = 0;
+ while (1) {
+ iplist_nextrange(list);
+ if (pos < (int)list->cur.nItems) {
+ if (pos) {
+ list->cur.srcitem = pos;
+ list->cur.pos += pos;
+ list->cur.ip.s_addr = htonl(list->cur.lstart + list->cur.srcitem);
+ }
+ break;
+ }
+ pos -= list->cur.nItems;
+ list->cur.pos += list->cur.nItems;
+ }
+
+ return list->cur.ip;
+}
+
+struct in_addr
+iplist_setrandpos(struct iplist *list)
+{
+ randinit();
+ return iplist_setcurpos(list, random() % list->nItems);
+}
+
+int
+iplist_ip2pos(struct iplist *list, struct in_addr ip)
+{
+ struct iplist_cur cur;
+ u_long f;
+ int result;
+
+ result = -1;
+ memcpy(&cur, &list->cur, sizeof cur);
+
+ for (iplist_first(list), f = 0; f < list->nItems; f++)
+ if (iplist_next(list).s_addr == ip.s_addr) {
+ result = list->cur.pos;
+ break;
+ }
+
+ memcpy(&list->cur, &cur, sizeof list->cur);
+ return result;
+}
diff --git a/usr.sbin/ppp/iplist.h b/usr.sbin/ppp/iplist.h
new file mode 100644
index 0000000..5805a2c
--- /dev/null
+++ b/usr.sbin/ppp/iplist.h
@@ -0,0 +1,51 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct iplist_cur {
+ struct in_addr ip;
+ int pos;
+ char *srcptr;
+ u_long srcitem;
+ u_int32_t lstart;
+ u_long nItems;
+};
+
+struct iplist {
+ struct iplist_cur cur;
+ u_long nItems;
+ char src[LINE_LEN];
+};
+
+extern int iplist_setsrc(struct iplist *, const char *);
+extern void iplist_reset(struct iplist *);
+extern struct in_addr iplist_setcurpos(struct iplist *, long);
+extern struct in_addr iplist_setrandpos(struct iplist *);
+extern int iplist_ip2pos(struct iplist *, struct in_addr);
+extern struct in_addr iplist_next(struct iplist *);
+
+#define iplist_isvalid(x) ((x)->src[0] != '\0')
diff --git a/usr.sbin/ppp/ipv6cp.c b/usr.sbin/ppp/ipv6cp.c
new file mode 100644
index 0000000..06b003b
--- /dev/null
+++ b/usr.sbin/ppp/ipv6cp.c
@@ -0,0 +1,785 @@
+/*-
+ * Copyright (c) 2001 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <net/route.h>
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <sys/un.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <ifaddrs.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "mbuf.h"
+#include "timer.h"
+#include "fsm.h"
+#include "iplist.h"
+#include "throughput.h"
+#include "slcompress.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ncpaddr.h"
+#include "ip.h"
+#include "ipcp.h"
+#include "ipv6cp.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "ccp.h"
+#include "link.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ncp.h"
+#include "bundle.h"
+#include "route.h"
+#include "iface.h"
+#include "log.h"
+#include "proto.h"
+#include "command.h"
+#include "prompt.h"
+#include "async.h"
+#include "physical.h"
+#include "probe.h"
+#include "systems.h"
+
+
+#ifndef NOINET6
+#define IN6ADDR_LINKLOCAL_MCAST_INIT \
+ {{{ 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }}}
+static const struct in6_addr in6addr_linklocal_mcast =
+ IN6ADDR_LINKLOCAL_MCAST_INIT;
+
+static int ipv6cp_LayerUp(struct fsm *);
+static void ipv6cp_LayerDown(struct fsm *);
+static void ipv6cp_LayerStart(struct fsm *);
+static void ipv6cp_LayerFinish(struct fsm *);
+static void ipv6cp_InitRestartCounter(struct fsm *, int);
+static void ipv6cp_SendConfigReq(struct fsm *);
+static void ipv6cp_SentTerminateReq(struct fsm *);
+static void ipv6cp_SendTerminateAck(struct fsm *, u_char);
+static void ipv6cp_DecodeConfig(struct fsm *, u_char *, u_char *, int,
+ struct fsm_decode *);
+
+static struct fsm_callbacks ipv6cp_Callbacks = {
+ ipv6cp_LayerUp,
+ ipv6cp_LayerDown,
+ ipv6cp_LayerStart,
+ ipv6cp_LayerFinish,
+ ipv6cp_InitRestartCounter,
+ ipv6cp_SendConfigReq,
+ ipv6cp_SentTerminateReq,
+ ipv6cp_SendTerminateAck,
+ ipv6cp_DecodeConfig,
+ fsm_NullRecvResetReq,
+ fsm_NullRecvResetAck
+};
+
+static void
+SetInterfaceID(u_char *ifid, int userandom)
+{
+ struct ifaddrs *ifa, *ifap = NULL;
+ struct sockaddr_dl *sdl;
+ const u_long i32_max = 0xffffffff;
+ u_long r1, r2;
+
+ /* configure an interface ID based on Section 4.1 of RFC 2472 */
+ memset(ifid, 0, IPV6CP_IFIDLEN);
+
+ /*
+ * 1) If an IEEE global identifier (EUI-48 or EUI-64) is
+ * available anywhere on the node, it should be used to construct
+ * the tentative Interface-Identifier due to its uniqueness
+ * properties.
+ */
+ if (userandom)
+ goto randomid;
+ if (getifaddrs(&ifap) < 0)
+ goto randomid;
+
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ char *cp;
+
+ if (ifa->ifa_addr->sa_family != AF_LINK)
+ continue;
+
+ sdl = (struct sockaddr_dl *)ifa->ifa_addr;
+ if (sdl->sdl_alen < 6)
+ continue;
+ /* we're only interested in IEEE hardware addresses */
+ switch(sdl->sdl_type) {
+ case IFT_ETHER:
+ case IFT_FDDI:
+ /* XXX need more cases? */
+ break;
+ default:
+ continue;
+ }
+
+ cp = (char *)(sdl->sdl_data + sdl->sdl_nlen);
+ ifid[0] = cp[0];
+ ifid[0] ^= 0x02; /* reverse the u/l bit*/
+ ifid[1] = cp[1];
+ ifid[2] = cp[2];
+ ifid[3] = 0xff;
+ ifid[4] = 0xfe;
+ ifid[5] = cp[3];
+ ifid[6] = cp[4];
+ ifid[7] = cp[5];
+
+ freeifaddrs(ifap);
+ return;
+ }
+
+ freeifaddrs(ifap);
+
+ /*
+ * 2) If an IEEE global identifier is not available a different source
+ * of uniqueness should be used.
+ * XXX: we skip this case.
+ */
+
+ /*
+ * 3) If a good source of uniqueness cannot be found, it is
+ * recommended that a random number be generated. In this case the
+ * "u" bit of the interface identifier MUST be set to zero (0).
+ */
+ randomid:
+ randinit();
+ r1 = (((u_long)random()) % i32_max) + 1;
+ r2 = (((u_long)random()) % i32_max) + 1;
+ memcpy(ifid, &r1, sizeof(r1));
+ memcpy(ifid + 4, &r2, sizeof(r2));
+ ifid[0] &= 0xfd;
+ return;
+}
+
+static int
+ipcp_SetIPv6address(struct ipv6cp *ipv6cp, u_char *myifid, u_char *hisifid)
+{
+ struct bundle *bundle = ipv6cp->fsm.bundle;
+ struct in6_addr myaddr, hisaddr;
+ struct ncprange myrange, range;
+ struct ncpaddr addr;
+ struct sockaddr_storage ssdst, ssgw, ssmask;
+ struct sockaddr *sadst, *sagw, *samask;
+
+ sadst = (struct sockaddr *)&ssdst;
+ sagw = (struct sockaddr *)&ssgw;
+ samask = (struct sockaddr *)&ssmask;
+
+ memset(&myaddr, '\0', sizeof myaddr);
+ memset(&hisaddr, '\0', sizeof hisaddr);
+
+ myaddr.s6_addr[0] = 0xfe;
+ myaddr.s6_addr[1] = 0x80;
+ memcpy(&myaddr.s6_addr[8], myifid, IPV6CP_IFIDLEN);
+#if 0
+ myaddr.s6_addr[8] |= 0x02; /* set 'universal' bit */
+#endif
+
+ hisaddr.s6_addr[0] = 0xfe;
+ hisaddr.s6_addr[1] = 0x80;
+ memcpy(&hisaddr.s6_addr[8], hisifid, IPV6CP_IFIDLEN);
+#if 0
+ hisaddr.s6_addr[8] |= 0x02; /* set 'universal' bit */
+#endif
+
+ ncpaddr_setip6(&ipv6cp->myaddr, &myaddr);
+ ncpaddr_setip6(&ipv6cp->hisaddr, &hisaddr);
+ ncprange_set(&myrange, &ipv6cp->myaddr, 64);
+
+ if (!iface_Add(bundle->iface, &bundle->ncp, &myrange, &ipv6cp->hisaddr,
+ IFACE_ADD_FIRST|IFACE_FORCE_ADD|IFACE_SYSTEM))
+ return 0;
+
+ if (!Enabled(bundle, OPT_IFACEALIAS))
+ iface_Clear(bundle->iface, &bundle->ncp, AF_INET6,
+ IFACE_CLEAR_ALIASES|IFACE_SYSTEM);
+
+ ncpaddr_setip6(&addr, &in6addr_linklocal_mcast);
+ ncprange_set(&range, &addr, 32);
+ rt_Set(bundle, RTM_ADD, &range, &ipv6cp->myaddr, 1, 0);
+
+ if (bundle->ncp.cfg.sendpipe > 0 || bundle->ncp.cfg.recvpipe > 0) {
+ ncprange_getsa(&myrange, &ssgw, &ssmask);
+ if (ncpaddr_isset(&ipv6cp->hisaddr))
+ ncpaddr_getsa(&ipv6cp->hisaddr, &ssdst);
+ else
+ sadst = NULL;
+ rt_Update(bundle, sadst, sagw, samask);
+ }
+
+ if (Enabled(bundle, OPT_SROUTES))
+ route_Change(bundle, bundle->ncp.route, &ipv6cp->myaddr, &ipv6cp->hisaddr);
+
+#ifndef NORADIUS
+ if (bundle->radius.valid)
+ route_Change(bundle, bundle->radius.ipv6routes, &ipv6cp->myaddr,
+ &ipv6cp->hisaddr);
+#endif
+
+ return 1; /* Ok */
+}
+
+void
+ipv6cp_Init(struct ipv6cp *ipv6cp, struct bundle *bundle, struct link *l,
+ const struct fsm_parent *parent)
+{
+ static const char * const timer_names[] =
+ {"IPV6CP restart", "IPV6CP openmode", "IPV6CP stopped"};
+ int n;
+
+ fsm_Init(&ipv6cp->fsm, "IPV6CP", PROTO_IPV6CP, 1, IPV6CP_MAXCODE, LogIPV6CP,
+ bundle, l, parent, &ipv6cp_Callbacks, timer_names);
+
+ ipv6cp->cfg.fsm.timeout = DEF_FSMRETRY;
+ ipv6cp->cfg.fsm.maxreq = DEF_FSMTRIES;
+ ipv6cp->cfg.fsm.maxtrm = DEF_FSMTRIES;
+
+ SetInterfaceID(ipv6cp->my_ifid, 0);
+ do {
+ SetInterfaceID(ipv6cp->his_ifid, 1);
+ } while (memcmp(ipv6cp->his_ifid, ipv6cp->my_ifid, IPV6CP_IFIDLEN) == 0);
+
+ if (probe.ipv6_available) {
+ n = 100;
+ while (n &&
+ !ipcp_SetIPv6address(ipv6cp, ipv6cp->my_ifid, ipv6cp->his_ifid)) {
+ do {
+ n--;
+ SetInterfaceID(ipv6cp->my_ifid, 1);
+ } while (n
+ && memcmp(ipv6cp->his_ifid, ipv6cp->my_ifid, IPV6CP_IFIDLEN) == 0);
+ }
+ }
+
+ throughput_init(&ipv6cp->throughput, SAMPLE_PERIOD);
+ memset(ipv6cp->Queue, '\0', sizeof ipv6cp->Queue);
+ ipv6cp_Setup(ipv6cp);
+}
+
+void
+ipv6cp_Destroy(struct ipv6cp *ipv6cp)
+{
+ throughput_destroy(&ipv6cp->throughput);
+}
+
+void
+ipv6cp_Setup(struct ipv6cp *ipv6cp)
+{
+ ncpaddr_init(&ipv6cp->myaddr);
+ ncpaddr_init(&ipv6cp->hisaddr);
+
+ ipv6cp->his_reject = 0;
+ ipv6cp->my_reject = 0;
+}
+
+void
+ipv6cp_SetLink(struct ipv6cp *ipv6cp, struct link *l)
+{
+ ipv6cp->fsm.link = l;
+}
+
+int
+ipv6cp_Show(struct cmdargs const *arg)
+{
+ struct ipv6cp *ipv6cp = &arg->bundle->ncp.ipv6cp;
+
+ prompt_Printf(arg->prompt, "%s [%s]\n", ipv6cp->fsm.name,
+ State2Nam(ipv6cp->fsm.state));
+ if (ipv6cp->fsm.state == ST_OPENED) {
+ prompt_Printf(arg->prompt, " His side: %s\n",
+ ncpaddr_ntoa(&ipv6cp->hisaddr));
+ prompt_Printf(arg->prompt, " My side: %s\n",
+ ncpaddr_ntoa(&ipv6cp->myaddr));
+ prompt_Printf(arg->prompt, " Queued packets: %lu\n",
+ (unsigned long)ipv6cp_QueueLen(ipv6cp));
+ }
+
+ prompt_Printf(arg->prompt, "\nDefaults:\n");
+ prompt_Printf(arg->prompt, " FSM retry = %us, max %u Config"
+ " REQ%s, %u Term REQ%s\n\n", ipv6cp->cfg.fsm.timeout,
+ ipv6cp->cfg.fsm.maxreq, ipv6cp->cfg.fsm.maxreq == 1 ? "" : "s",
+ ipv6cp->cfg.fsm.maxtrm, ipv6cp->cfg.fsm.maxtrm == 1 ? "" : "s");
+
+ throughput_disp(&ipv6cp->throughput, arg->prompt);
+
+ return 0;
+}
+
+struct mbuf *
+ipv6cp_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
+{
+ /* Got PROTO_IPV6CP from link */
+ m_settype(bp, MB_IPV6CPIN);
+ if (bundle_Phase(bundle) == PHASE_NETWORK)
+ fsm_Input(&bundle->ncp.ipv6cp.fsm, bp);
+ else {
+ if (bundle_Phase(bundle) < PHASE_NETWORK)
+ log_Printf(LogIPV6CP, "%s: Error: Unexpected IPV6CP in phase %s"
+ " (ignored)\n", l->name, bundle_PhaseName(bundle));
+ m_freem(bp);
+ }
+ return NULL;
+}
+
+void
+ipv6cp_AddInOctets(struct ipv6cp *ipv6cp, int n)
+{
+ throughput_addin(&ipv6cp->throughput, n);
+}
+
+void
+ipv6cp_AddOutOctets(struct ipv6cp *ipv6cp, int n)
+{
+ throughput_addout(&ipv6cp->throughput, n);
+}
+
+void
+ipv6cp_IfaceAddrAdded(struct ipv6cp *ipv6cp __unused,
+ const struct iface_addr *addr __unused)
+{
+}
+
+void
+ipv6cp_IfaceAddrDeleted(struct ipv6cp *ipv6cp __unused,
+ const struct iface_addr *addr __unused)
+{
+}
+
+int
+ipv6cp_InterfaceUp(struct ipv6cp *ipv6cp)
+{
+ if (!ipcp_SetIPv6address(ipv6cp, ipv6cp->my_ifid, ipv6cp->his_ifid)) {
+ log_Printf(LogERROR, "ipv6cp_InterfaceUp: unable to set ipv6 address\n");
+ return 0;
+ }
+
+ if (!iface_SetFlags(ipv6cp->fsm.bundle->iface->name, IFF_UP)) {
+ log_Printf(LogERROR, "ipv6cp_InterfaceUp: Can't set the IFF_UP"
+ " flag on %s\n", ipv6cp->fsm.bundle->iface->name);
+ return 0;
+ }
+
+ return 1;
+}
+
+size_t
+ipv6cp_QueueLen(struct ipv6cp *ipv6cp)
+{
+ struct mqueue *q;
+ size_t result;
+
+ result = 0;
+ for (q = ipv6cp->Queue; q < ipv6cp->Queue + IPV6CP_QUEUES(ipv6cp); q++)
+ result += q->len;
+
+ return result;
+}
+
+int
+ipv6cp_PushPacket(struct ipv6cp *ipv6cp, struct link *l)
+{
+ struct bundle *bundle = ipv6cp->fsm.bundle;
+ struct mqueue *queue;
+ struct mbuf *bp;
+ int m_len;
+ u_int32_t secs = 0;
+ unsigned alivesecs = 0;
+
+ if (ipv6cp->fsm.state != ST_OPENED)
+ return 0;
+
+ /*
+ * If ccp is not open but is required, do nothing.
+ */
+ if (l->ccp.fsm.state != ST_OPENED && ccp_Required(&l->ccp)) {
+ log_Printf(LogPHASE, "%s: Not transmitting... waiting for CCP\n", l->name);
+ return 0;
+ }
+
+ queue = ipv6cp->Queue + IPV6CP_QUEUES(ipv6cp) - 1;
+ do {
+ if (queue->top) {
+ bp = m_dequeue(queue);
+ bp = mbuf_Read(bp, &secs, sizeof secs);
+ bp = m_pullup(bp);
+ m_len = m_length(bp);
+ if (!FilterCheck(MBUF_CTOP(bp), AF_INET6, &bundle->filter.alive,
+ &alivesecs)) {
+ if (secs == 0)
+ secs = alivesecs;
+ bundle_StartIdleTimer(bundle, secs);
+ }
+ link_PushPacket(l, bp, bundle, 0, PROTO_IPV6);
+ ipv6cp_AddOutOctets(ipv6cp, m_len);
+ return 1;
+ }
+ } while (queue-- != ipv6cp->Queue);
+
+ return 0;
+}
+
+static int
+ipv6cp_LayerUp(struct fsm *fp)
+{
+ /* We're now up */
+ struct ipv6cp *ipv6cp = fsm2ipv6cp(fp);
+ char tbuff[40];
+
+ log_Printf(LogIPV6CP, "%s: LayerUp.\n", fp->link->name);
+ if (!ipv6cp_InterfaceUp(ipv6cp))
+ return 0;
+
+ snprintf(tbuff, sizeof tbuff, "%s", ncpaddr_ntoa(&ipv6cp->myaddr));
+ log_Printf(LogIPV6CP, "myaddr %s hisaddr = %s\n",
+ tbuff, ncpaddr_ntoa(&ipv6cp->hisaddr));
+
+#ifndef NORADIUS
+ radius_Account_Set_Ipv6(&fp->bundle->radacct6, ipv6cp->his_ifid);
+ radius_Account(&fp->bundle->radius, &fp->bundle->radacct6,
+ fp->bundle->links, RAD_START, &ipv6cp->throughput);
+
+ /*
+ * XXX: Avoid duplicate evaluation of filterid between IPCP and
+ * IPV6CP. When IPCP is enabled and rejected, filterid is not
+ * evaluated.
+ */
+ if (!Enabled(fp->bundle, OPT_IPCP)) {
+ if (fp->bundle->radius.cfg.file && fp->bundle->radius.filterid)
+ system_Select(fp->bundle, fp->bundle->radius.filterid, LINKUPFILE,
+ NULL, NULL);
+ }
+#endif
+
+ /*
+ * XXX this stuff should really live in the FSM. Our config should
+ * associate executable sections in files with events.
+ */
+ if (system_Select(fp->bundle, tbuff, LINKUPFILE, NULL, NULL) < 0) {
+ /*
+ * XXX: Avoid duplicate evaluation of label between IPCP and
+ * IPV6CP. When IPCP is enabled and rejected, label is not
+ * evaluated.
+ */
+ if (bundle_GetLabel(fp->bundle) && !Enabled(fp->bundle, OPT_IPCP)) {
+ if (system_Select(fp->bundle, bundle_GetLabel(fp->bundle),
+ LINKUPFILE, NULL, NULL) < 0)
+ system_Select(fp->bundle, "MYADDR6", LINKUPFILE, NULL, NULL);
+ } else
+ system_Select(fp->bundle, "MYADDR6", LINKUPFILE, NULL, NULL);
+ }
+
+ fp->more.reqs = fp->more.naks = fp->more.rejs = ipv6cp->cfg.fsm.maxreq * 3;
+ log_DisplayPrompts();
+
+ return 1;
+}
+
+static void
+ipv6cp_LayerDown(struct fsm *fp)
+{
+ /* About to come down */
+ struct ipv6cp *ipv6cp = fsm2ipv6cp(fp);
+ static int recursing;
+ char addr[40];
+
+ if (!recursing++) {
+ snprintf(addr, sizeof addr, "%s", ncpaddr_ntoa(&ipv6cp->myaddr));
+ log_Printf(LogIPV6CP, "%s: LayerDown: %s\n", fp->link->name, addr);
+
+#ifndef NORADIUS
+ radius_Flush(&fp->bundle->radius);
+ radius_Account(&fp->bundle->radius, &fp->bundle->radacct6,
+ fp->bundle->links, RAD_STOP, &ipv6cp->throughput);
+
+ /*
+ * XXX: Avoid duplicate evaluation of filterid between IPCP and
+ * IPV6CP. When IPCP is enabled and rejected, filterid is not
+ * evaluated.
+ */
+ if (!Enabled(fp->bundle, OPT_IPCP)) {
+ if (fp->bundle->radius.cfg.file && fp->bundle->radius.filterid)
+ system_Select(fp->bundle, fp->bundle->radius.filterid, LINKDOWNFILE,
+ NULL, NULL);
+ }
+#endif
+
+ /*
+ * XXX this stuff should really live in the FSM. Our config should
+ * associate executable sections in files with events.
+ */
+ if (system_Select(fp->bundle, addr, LINKDOWNFILE, NULL, NULL) < 0) {
+ /*
+ * XXX: Avoid duplicate evaluation of label between IPCP and
+ * IPV6CP. When IPCP is enabled and rejected, label is not
+ * evaluated.
+ */
+ if (bundle_GetLabel(fp->bundle) && !Enabled(fp->bundle, OPT_IPCP)) {
+ if (system_Select(fp->bundle, bundle_GetLabel(fp->bundle),
+ LINKDOWNFILE, NULL, NULL) < 0)
+ system_Select(fp->bundle, "MYADDR6", LINKDOWNFILE, NULL, NULL);
+ } else
+ system_Select(fp->bundle, "MYADDR6", LINKDOWNFILE, NULL, NULL);
+ }
+
+ ipv6cp_Setup(ipv6cp);
+ }
+ recursing--;
+}
+
+static void
+ipv6cp_LayerStart(struct fsm *fp)
+{
+ /* We're about to start up ! */
+ struct ipv6cp *ipv6cp = fsm2ipv6cp(fp);
+
+ log_Printf(LogIPV6CP, "%s: LayerStart.\n", fp->link->name);
+ throughput_start(&ipv6cp->throughput, "IPV6CP throughput",
+ Enabled(fp->bundle, OPT_THROUGHPUT));
+ fp->more.reqs = fp->more.naks = fp->more.rejs = ipv6cp->cfg.fsm.maxreq * 3;
+ ipv6cp->peer_tokenreq = 0;
+}
+
+static void
+ipv6cp_LayerFinish(struct fsm *fp)
+{
+ /* We're now down */
+ struct ipv6cp *ipv6cp = fsm2ipv6cp(fp);
+
+ log_Printf(LogIPV6CP, "%s: LayerFinish.\n", fp->link->name);
+ throughput_stop(&ipv6cp->throughput);
+ throughput_log(&ipv6cp->throughput, LogIPV6CP, NULL);
+}
+
+static void
+ipv6cp_InitRestartCounter(struct fsm *fp, int what)
+{
+ /* Set fsm timer load */
+ struct ipv6cp *ipv6cp = fsm2ipv6cp(fp);
+
+ fp->FsmTimer.load = ipv6cp->cfg.fsm.timeout * SECTICKS;
+ switch (what) {
+ case FSM_REQ_TIMER:
+ fp->restart = ipv6cp->cfg.fsm.maxreq;
+ break;
+ case FSM_TRM_TIMER:
+ fp->restart = ipv6cp->cfg.fsm.maxtrm;
+ break;
+ default:
+ fp->restart = 1;
+ break;
+ }
+}
+
+static void
+ipv6cp_SendConfigReq(struct fsm *fp)
+{
+ /* Send config REQ please */
+ struct physical *p = link2physical(fp->link);
+ struct ipv6cp *ipv6cp = fsm2ipv6cp(fp);
+ u_char buff[IPV6CP_IFIDLEN+2];
+ struct fsm_opt *o;
+
+ o = (struct fsm_opt *)buff;
+
+ if ((p && !physical_IsSync(p)) || !REJECTED(ipv6cp, TY_TOKEN)) {
+ memcpy(o->data, ipv6cp->my_ifid, IPV6CP_IFIDLEN);
+ INC_FSM_OPT(TY_TOKEN, IPV6CP_IFIDLEN + 2, o);
+ }
+
+ fsm_Output(fp, CODE_CONFIGREQ, fp->reqid, buff, (u_char *)o - buff,
+ MB_IPV6CPOUT);
+}
+
+static void
+ipv6cp_SentTerminateReq(struct fsm *fp __unused)
+{
+ /* Term REQ just sent by FSM */
+}
+
+static void
+ipv6cp_SendTerminateAck(struct fsm *fp, u_char id)
+{
+ /* Send Term ACK please */
+ fsm_Output(fp, CODE_TERMACK, id, NULL, 0, MB_IPV6CPOUT);
+}
+
+static const char *
+protoname(unsigned proto)
+{
+ static const char *cftypes[] = { "IFACEID", "COMPPROTO" };
+
+ if (proto > 0 && proto <= sizeof cftypes / sizeof *cftypes)
+ return cftypes[proto - 1];
+
+ return NumStr(proto, NULL, 0);
+}
+
+static void
+ipv6cp_ValidateInterfaceID(struct ipv6cp *ipv6cp, u_char *ifid,
+ struct fsm_decode *dec)
+{
+ struct fsm_opt opt;
+ u_char zero[IPV6CP_IFIDLEN];
+
+ memset(zero, 0, IPV6CP_IFIDLEN);
+
+ if (memcmp(ifid, zero, IPV6CP_IFIDLEN) != 0
+ && memcmp(ifid, ipv6cp->my_ifid, IPV6CP_IFIDLEN) != 0)
+ memcpy(ipv6cp->his_ifid, ifid, IPV6CP_IFIDLEN);
+
+ opt.hdr.id = TY_TOKEN;
+ opt.hdr.len = IPV6CP_IFIDLEN + 2;
+ memcpy(opt.data, &ipv6cp->his_ifid, IPV6CP_IFIDLEN);
+ if (memcmp(ifid, ipv6cp->his_ifid, IPV6CP_IFIDLEN) == 0)
+ fsm_ack(dec, &opt);
+ else
+ fsm_nak(dec, &opt);
+}
+
+static void
+ipv6cp_DecodeConfig(struct fsm *fp, u_char *cp, u_char *end, int mode_type,
+ struct fsm_decode *dec)
+{
+ /* Deal with incoming PROTO_IPV6CP */
+ struct ipv6cp *ipv6cp = fsm2ipv6cp(fp);
+ int n;
+ char tbuff[100];
+ u_char ifid[IPV6CP_IFIDLEN], zero[IPV6CP_IFIDLEN];
+ struct fsm_opt *opt;
+
+ memset(zero, 0, IPV6CP_IFIDLEN);
+
+ while (end - cp >= (int)sizeof(opt->hdr)) {
+ if ((opt = fsm_readopt(&cp)) == NULL)
+ break;
+
+ snprintf(tbuff, sizeof tbuff, " %s[%d]", protoname(opt->hdr.id),
+ opt->hdr.len);
+
+ switch (opt->hdr.id) {
+ case TY_TOKEN:
+ memcpy(ifid, opt->data, IPV6CP_IFIDLEN);
+ log_Printf(LogIPV6CP, "%s 0x%02x%02x%02x%02x%02x%02x%02x%02x\n", tbuff,
+ ifid[0], ifid[1], ifid[2], ifid[3], ifid[4], ifid[5], ifid[6], ifid[7]);
+
+ switch (mode_type) {
+ case MODE_REQ:
+ ipv6cp->peer_tokenreq = 1;
+ ipv6cp_ValidateInterfaceID(ipv6cp, ifid, dec);
+ break;
+
+ case MODE_NAK:
+ if (memcmp(ifid, zero, IPV6CP_IFIDLEN) == 0) {
+ log_Printf(log_IsKept(LogIPV6CP) ? LogIPV6CP : LogPHASE,
+ "0x0000000000000000: Unacceptable IntefaceID!\n");
+ fsm_Close(&ipv6cp->fsm);
+ } else if (memcmp(ifid, ipv6cp->his_ifid, IPV6CP_IFIDLEN) == 0) {
+ log_Printf(log_IsKept(LogIPV6CP) ? LogIPV6CP : LogPHASE,
+ "0x%02x%02x%02x%02x%02x%02x%02x%02x: "
+ "Unacceptable IntefaceID!\n",
+ ifid[0], ifid[1], ifid[2], ifid[3],
+ ifid[4], ifid[5], ifid[6], ifid[7]);
+ } else if (memcmp(ifid, ipv6cp->my_ifid, IPV6CP_IFIDLEN) != 0) {
+ n = 100;
+ while (n && !ipcp_SetIPv6address(ipv6cp, ifid, ipv6cp->his_ifid)) {
+ do {
+ n--;
+ SetInterfaceID(ifid, 1);
+ } while (n && memcmp(ifid, ipv6cp->his_ifid, IPV6CP_IFIDLEN) == 0);
+ }
+
+ if (n == 0) {
+ log_Printf(log_IsKept(LogIPV6CP) ? LogIPV6CP : LogPHASE,
+ "0x0000000000000000: Unacceptable IntefaceID!\n");
+ fsm_Close(&ipv6cp->fsm);
+ } else {
+ log_Printf(LogIPV6CP, "%s changing IntefaceID: "
+ "0x%02x%02x%02x%02x%02x%02x%02x%02x "
+ "--> 0x%02x%02x%02x%02x%02x%02x%02x%02x\n", tbuff,
+ ipv6cp->my_ifid[0], ipv6cp->my_ifid[1],
+ ipv6cp->my_ifid[2], ipv6cp->my_ifid[3],
+ ipv6cp->my_ifid[4], ipv6cp->my_ifid[5],
+ ipv6cp->my_ifid[6], ipv6cp->my_ifid[7],
+ ifid[0], ifid[1], ifid[2], ifid[3],
+ ifid[4], ifid[5], ifid[6], ifid[7]);
+ memcpy(ipv6cp->my_ifid, ifid, IPV6CP_IFIDLEN);
+ bundle_AdjustFilters(fp->bundle, &ipv6cp->myaddr, NULL);
+ }
+ }
+ break;
+
+ case MODE_REJ:
+ ipv6cp->his_reject |= (1 << opt->hdr.id);
+ break;
+ }
+ break;
+
+ default:
+ if (mode_type != MODE_NOP) {
+ ipv6cp->my_reject |= (1 << opt->hdr.id);
+ fsm_rej(dec, opt);
+ }
+ break;
+ }
+ }
+
+ if (mode_type != MODE_NOP) {
+ if (mode_type == MODE_REQ && !ipv6cp->peer_tokenreq) {
+ if (dec->rejend == dec->rej && dec->nakend == dec->nak) {
+ /*
+ * Pretend the peer has requested a TOKEN.
+ * We do this to ensure that we only send one NAK if the only
+ * reason for the NAK is because the peer isn't sending a
+ * TY_TOKEN REQ. This stops us from repeatedly trying to tell
+ * the peer that we have to have an IP address on their end.
+ */
+ ipv6cp->peer_tokenreq = 1;
+ }
+ memset(ifid, 0, IPV6CP_IFIDLEN);
+ ipv6cp_ValidateInterfaceID(ipv6cp, ifid, dec);
+ }
+ fsm_opt_normalise(dec);
+ }
+}
+#endif
diff --git a/usr.sbin/ppp/ipv6cp.h b/usr.sbin/ppp/ipv6cp.h
new file mode 100644
index 0000000..53f7153
--- /dev/null
+++ b/usr.sbin/ppp/ipv6cp.h
@@ -0,0 +1,83 @@
+/*-
+ * Copyright (c) 2001 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef NOINET6
+#define IPV6CP_MAXCODE CODE_CODEREJ
+
+#define TY_TOKEN 1
+#define TY_COMPPROTO 2
+
+#define IPV6CP_IFIDLEN 8 /* RFC2472 */
+
+struct ipv6cp {
+ struct fsm fsm; /* The finite state machine */
+
+ struct {
+ struct fsm_retry fsm; /* frequency to resend requests */
+ } cfg;
+
+ unsigned peer_tokenreq : 1; /* Any TY_TOKEN REQs from the peer ? */
+
+ u_char my_ifid[IPV6CP_IFIDLEN]; /* Local Interface Identifier */
+ u_char his_ifid[IPV6CP_IFIDLEN]; /* Peer Interface Identifier */
+
+ struct ncpaddr myaddr; /* Local address */
+ struct ncpaddr hisaddr; /* Peer address */
+
+ u_int32_t his_reject; /* Request codes rejected by peer */
+ u_int32_t my_reject; /* Request codes I have rejected */
+
+ struct pppThroughput throughput; /* throughput statistics */
+ struct mqueue Queue[2]; /* Output packet queues */
+};
+
+#define fsm2ipv6cp(fp) (fp->proto == PROTO_IPV6CP ? (struct ipv6cp *)fp : NULL)
+#define IPV6CP_QUEUES(ipv6cp) (sizeof ipv6cp->Queue / sizeof ipv6cp->Queue[0])
+
+struct bundle;
+struct link;
+struct cmdargs;
+struct iface_addr;
+
+extern void ipv6cp_Init(struct ipv6cp *, struct bundle *, struct link *,
+ const struct fsm_parent *);
+extern void ipv6cp_Destroy(struct ipv6cp *);
+extern void ipv6cp_Setup(struct ipv6cp *);
+extern void ipv6cp_SetLink(struct ipv6cp *, struct link *);
+
+extern int ipv6cp_Show(struct cmdargs const *);
+extern struct mbuf *ipv6cp_Input(struct bundle *, struct link *, struct mbuf *);
+extern void ipv6cp_AddInOctets(struct ipv6cp *, int);
+extern void ipv6cp_AddOutOctets(struct ipv6cp *, int);
+
+extern void ipv6cp_IfaceAddrAdded(struct ipv6cp *, const struct iface_addr *);
+extern void ipv6cp_IfaceAddrDeleted(struct ipv6cp *, const struct iface_addr *);
+extern int ipv6cp_InterfaceUp(struct ipv6cp *);
+extern size_t ipv6cp_QueueLen(struct ipv6cp *);
+extern int ipv6cp_PushPacket(struct ipv6cp *, struct link *);
+#endif
diff --git a/usr.sbin/ppp/layer.h b/usr.sbin/ppp/layer.h
new file mode 100644
index 0000000..4ee59a1
--- /dev/null
+++ b/usr.sbin/ppp/layer.h
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define LAYER_ASYNC 2
+#define LAYER_SYNC 3
+#define LAYER_HDLC 4
+#define LAYER_ACF 5
+#define LAYER_PROTO 6
+#define LAYER_LQR 7
+#define LAYER_CCP 8
+#define LAYER_VJ 9
+#define LAYER_NAT 10
+
+#define LAYER_MAX 10 /* How many layers we can handle on a link */
+
+struct mbuf;
+struct link;
+struct bundle;
+
+struct layer {
+ int type;
+ const char *name;
+ struct mbuf *(*push)(struct bundle *, struct link *, struct mbuf *,
+ int pri, u_short *proto);
+ struct mbuf *(*pull)(struct bundle *, struct link *, struct mbuf *,
+ u_short *);
+};
diff --git a/usr.sbin/ppp/lcp.c b/usr.sbin/ppp/lcp.c
new file mode 100644
index 0000000..cf75718
--- /dev/null
+++ b/usr.sbin/ppp/lcp.c
@@ -0,0 +1,1305 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "ua.h"
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "iplist.h"
+#include "throughput.h"
+#include "proto.h"
+#include "descriptor.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "async.h"
+#include "link.h"
+#include "physical.h"
+#include "prompt.h"
+#include "slcompress.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "mp.h"
+#include "chat.h"
+#include "auth.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+
+/* for received LQRs */
+struct lqrreq {
+ struct fsm_opt_hdr hdr;
+ u_short proto; /* Quality protocol */
+ u_int32_t period; /* Reporting interval */
+};
+
+static int LcpLayerUp(struct fsm *);
+static void LcpLayerDown(struct fsm *);
+static void LcpLayerStart(struct fsm *);
+static void LcpLayerFinish(struct fsm *);
+static void LcpInitRestartCounter(struct fsm *, int);
+static void LcpSendConfigReq(struct fsm *);
+static void LcpSentTerminateReq(struct fsm *);
+static void LcpSendTerminateAck(struct fsm *, u_char);
+static void LcpDecodeConfig(struct fsm *, u_char *, u_char *, int,
+ struct fsm_decode *);
+
+static struct fsm_callbacks lcp_Callbacks = {
+ LcpLayerUp,
+ LcpLayerDown,
+ LcpLayerStart,
+ LcpLayerFinish,
+ LcpInitRestartCounter,
+ LcpSendConfigReq,
+ LcpSentTerminateReq,
+ LcpSendTerminateAck,
+ LcpDecodeConfig,
+ fsm_NullRecvResetReq,
+ fsm_NullRecvResetAck
+};
+
+static const char * const lcp_TimerNames[] =
+ {"LCP restart", "LCP openmode", "LCP stopped"};
+
+static const char *
+protoname(unsigned proto)
+{
+ static const char * const cftypes[] = {
+ /* Check out the latest ``Assigned numbers'' rfc (1700) */
+ NULL,
+ "MRU", /* 1: Maximum-Receive-Unit */
+ "ACCMAP", /* 2: Async-Control-Character-Map */
+ "AUTHPROTO", /* 3: Authentication-Protocol */
+ "QUALPROTO", /* 4: Quality-Protocol */
+ "MAGICNUM", /* 5: Magic-Number */
+ "RESERVED", /* 6: RESERVED */
+ "PROTOCOMP", /* 7: Protocol-Field-Compression */
+ "ACFCOMP", /* 8: Address-and-Control-Field-Compression */
+ "FCSALT", /* 9: FCS-Alternatives */
+ "SDP", /* 10: Self-Describing-Pad */
+ "NUMMODE", /* 11: Numbered-Mode */
+ "MULTIPROC", /* 12: Multi-Link-Procedure */
+ "CALLBACK", /* 13: Callback */
+ "CONTIME", /* 14: Connect-Time */
+ "COMPFRAME", /* 15: Compound-Frames */
+ "NDE", /* 16: Nominal-Data-Encapsulation */
+ "MRRU", /* 17: Multilink-MRRU */
+ "SHORTSEQ", /* 18: Multilink-Short-Sequence-Number-Header */
+ "ENDDISC", /* 19: Multilink-Endpoint-Discriminator */
+ "PROPRIETRY", /* 20: Proprietary */
+ "DCEID", /* 21: DCE-Identifier */
+ "MULTIPP", /* 22: Multi-Link-Plus-Procedure */
+ "LDBACP", /* 23: Link Discriminator for BACP */
+ };
+
+ if (proto > sizeof cftypes / sizeof *cftypes || cftypes[proto] == NULL)
+ return HexStr(proto, NULL, 0);
+
+ return cftypes[proto];
+}
+
+int
+lcp_ReportStatus(struct cmdargs const *arg)
+{
+ struct link *l;
+ struct lcp *lcp;
+
+ l = command_ChooseLink(arg);
+ lcp = &l->lcp;
+
+ prompt_Printf(arg->prompt, "%s: %s [%s]\n", l->name, lcp->fsm.name,
+ State2Nam(lcp->fsm.state));
+ prompt_Printf(arg->prompt,
+ " his side: MRU %d, ACCMAP %08lx, PROTOCOMP %s, ACFCOMP %s,\n"
+ " MAGIC %08lx, MRRU %u, SHORTSEQ %s, REJECT %04x\n",
+ lcp->his_mru, (u_long)lcp->his_accmap,
+ lcp->his_protocomp ? "on" : "off",
+ lcp->his_acfcomp ? "on" : "off",
+ (u_long)lcp->his_magic, lcp->his_mrru,
+ lcp->his_shortseq ? "on" : "off", lcp->his_reject);
+ prompt_Printf(arg->prompt,
+ " my side: MRU %d, ACCMAP %08lx, PROTOCOMP %s, ACFCOMP %s,\n"
+ " MAGIC %08lx, MRRU %u, SHORTSEQ %s, REJECT %04x\n",
+ lcp->want_mru, (u_long)lcp->want_accmap,
+ lcp->want_protocomp ? "on" : "off",
+ lcp->want_acfcomp ? "on" : "off",
+ (u_long)lcp->want_magic, lcp->want_mrru,
+ lcp->want_shortseq ? "on" : "off", lcp->my_reject);
+
+ if (lcp->cfg.mru)
+ prompt_Printf(arg->prompt, "\n Defaults: MRU = %d (max %d), ",
+ lcp->cfg.mru, lcp->cfg.max_mru);
+ else
+ prompt_Printf(arg->prompt, "\n Defaults: MRU = any (max %d), ",
+ lcp->cfg.max_mru);
+ if (lcp->cfg.mtu)
+ prompt_Printf(arg->prompt, "MTU = %d (max %d), ",
+ lcp->cfg.mtu, lcp->cfg.max_mtu);
+ else
+ prompt_Printf(arg->prompt, "MTU = any (max %d), ", lcp->cfg.max_mtu);
+ prompt_Printf(arg->prompt, "ACCMAP = %08lx\n", (u_long)lcp->cfg.accmap);
+ prompt_Printf(arg->prompt, " LQR period = %us, ",
+ lcp->cfg.lqrperiod);
+ prompt_Printf(arg->prompt, "Open Mode = %s",
+ lcp->cfg.openmode == OPEN_PASSIVE ? "passive" : "active");
+ if (lcp->cfg.openmode > 0)
+ prompt_Printf(arg->prompt, " (delay %ds)", lcp->cfg.openmode);
+ prompt_Printf(arg->prompt, "\n FSM retry = %us, max %u Config"
+ " REQ%s, %u Term REQ%s\n", lcp->cfg.fsm.timeout,
+ lcp->cfg.fsm.maxreq, lcp->cfg.fsm.maxreq == 1 ? "" : "s",
+ lcp->cfg.fsm.maxtrm, lcp->cfg.fsm.maxtrm == 1 ? "" : "s");
+ prompt_Printf(arg->prompt, " Ident: %s\n", lcp->cfg.ident);
+ prompt_Printf(arg->prompt, "\n Negotiation:\n");
+ prompt_Printf(arg->prompt, " ACFCOMP = %s\n",
+ command_ShowNegval(lcp->cfg.acfcomp));
+ prompt_Printf(arg->prompt, " CHAP = %s\n",
+ command_ShowNegval(lcp->cfg.chap05));
+#ifndef NODES
+ prompt_Printf(arg->prompt, " CHAP80 = %s\n",
+ command_ShowNegval(lcp->cfg.chap80nt));
+ prompt_Printf(arg->prompt, " LANMan = %s\n",
+ command_ShowNegval(lcp->cfg.chap80lm));
+ prompt_Printf(arg->prompt, " CHAP81 = %s\n",
+ command_ShowNegval(lcp->cfg.chap81));
+#endif
+ prompt_Printf(arg->prompt, " LQR = %s\n",
+ command_ShowNegval(lcp->cfg.lqr));
+ prompt_Printf(arg->prompt, " LCP ECHO = %s\n",
+ lcp->cfg.echo ? "enabled" : "disabled");
+ prompt_Printf(arg->prompt, " PAP = %s\n",
+ command_ShowNegval(lcp->cfg.pap));
+ prompt_Printf(arg->prompt, " PROTOCOMP = %s\n",
+ command_ShowNegval(lcp->cfg.protocomp));
+
+ return 0;
+}
+
+static u_int32_t
+GenerateMagic(void)
+{
+ /* Generate random number which will be used as magic number */
+ randinit();
+ return random();
+}
+
+void
+lcp_SetupCallbacks(struct lcp *lcp)
+{
+ lcp->fsm.fn = &lcp_Callbacks;
+ lcp->fsm.FsmTimer.name = lcp_TimerNames[0];
+ lcp->fsm.OpenTimer.name = lcp_TimerNames[1];
+ lcp->fsm.StoppedTimer.name = lcp_TimerNames[2];
+}
+
+void
+lcp_Init(struct lcp *lcp, struct bundle *bundle, struct link *l,
+ const struct fsm_parent *parent)
+{
+ /* Initialise ourselves */
+ int mincode = parent ? 1 : LCP_MINMPCODE;
+
+ fsm_Init(&lcp->fsm, "LCP", PROTO_LCP, mincode, LCP_MAXCODE, LogLCP,
+ bundle, l, parent, &lcp_Callbacks, lcp_TimerNames);
+
+ lcp->cfg.mru = 0;
+ lcp->cfg.max_mru = MAX_MRU;
+ lcp->cfg.mtu = 0;
+ lcp->cfg.max_mtu = MAX_MTU;
+ lcp->cfg.accmap = 0;
+ lcp->cfg.openmode = 1;
+ lcp->cfg.lqrperiod = DEF_LQRPERIOD;
+ lcp->cfg.fsm.timeout = DEF_FSMRETRY;
+ lcp->cfg.fsm.maxreq = DEF_FSMTRIES;
+ lcp->cfg.fsm.maxtrm = DEF_FSMTRIES;
+
+ lcp->cfg.acfcomp = NEG_ENABLED|NEG_ACCEPTED;
+ lcp->cfg.chap05 = NEG_ACCEPTED;
+#ifndef NODES
+ lcp->cfg.chap80nt = NEG_ACCEPTED;
+ lcp->cfg.chap80lm = 0;
+ lcp->cfg.chap81 = NEG_ACCEPTED;
+#endif
+ lcp->cfg.lqr = NEG_ACCEPTED;
+ lcp->cfg.echo = 0;
+ lcp->cfg.pap = NEG_ACCEPTED;
+ lcp->cfg.protocomp = NEG_ENABLED|NEG_ACCEPTED;
+ *lcp->cfg.ident = '\0';
+
+ lcp_Setup(lcp, lcp->cfg.openmode);
+}
+
+void
+lcp_Setup(struct lcp *lcp, int openmode)
+{
+ struct physical *p = link2physical(lcp->fsm.link);
+
+ lcp->fsm.open_mode = openmode;
+
+ lcp->his_mru = DEF_MRU;
+ lcp->his_mrru = 0;
+ lcp->his_magic = 0;
+ lcp->his_lqrperiod = 0;
+ lcp->his_acfcomp = 0;
+ lcp->his_auth = 0;
+ lcp->his_authtype = 0;
+ lcp->his_callback.opmask = 0;
+ lcp->his_shortseq = 0;
+ lcp->mru_req = 0;
+
+ if ((lcp->want_mru = lcp->cfg.mru) == 0)
+ lcp->want_mru = DEF_MRU;
+ lcp->want_mrru = lcp->fsm.bundle->ncp.mp.cfg.mrru;
+ lcp->want_shortseq = IsEnabled(lcp->fsm.bundle->ncp.mp.cfg.shortseq) ? 1 : 0;
+ lcp->want_acfcomp = IsEnabled(lcp->cfg.acfcomp) ? 1 : 0;
+
+ if (lcp->fsm.parent) {
+ lcp->his_accmap = 0xffffffff;
+ lcp->want_accmap = lcp->cfg.accmap;
+ lcp->his_protocomp = 0;
+ lcp->want_protocomp = IsEnabled(lcp->cfg.protocomp) ? 1 : 0;
+ lcp->want_magic = GenerateMagic();
+
+ if (IsEnabled(lcp->cfg.chap05)) {
+ lcp->want_auth = PROTO_CHAP;
+ lcp->want_authtype = 0x05;
+#ifndef NODES
+ } else if (IsEnabled(lcp->cfg.chap80nt) ||
+ IsEnabled(lcp->cfg.chap80lm)) {
+ lcp->want_auth = PROTO_CHAP;
+ lcp->want_authtype = 0x80;
+ } else if (IsEnabled(lcp->cfg.chap81)) {
+ lcp->want_auth = PROTO_CHAP;
+ lcp->want_authtype = 0x81;
+#endif
+ } else if (IsEnabled(lcp->cfg.pap)) {
+ lcp->want_auth = PROTO_PAP;
+ lcp->want_authtype = 0;
+ } else {
+ lcp->want_auth = 0;
+ lcp->want_authtype = 0;
+ }
+
+ if (p->type != PHYS_DIRECT)
+ memcpy(&lcp->want_callback, &p->dl->cfg.callback,
+ sizeof(struct callback));
+ else
+ lcp->want_callback.opmask = 0;
+ lcp->want_lqrperiod = IsEnabled(lcp->cfg.lqr) ?
+ lcp->cfg.lqrperiod * 100 : 0;
+ } else {
+ lcp->his_accmap = lcp->want_accmap = 0;
+ lcp->his_protocomp = lcp->want_protocomp = 1;
+ lcp->want_magic = 0;
+ lcp->want_auth = 0;
+ lcp->want_authtype = 0;
+ lcp->want_callback.opmask = 0;
+ lcp->want_lqrperiod = 0;
+ }
+
+ lcp->his_reject = lcp->my_reject = 0;
+ lcp->auth_iwait = lcp->auth_ineed = 0;
+ lcp->LcpFailedMagic = 0;
+}
+
+static void
+LcpInitRestartCounter(struct fsm *fp, int what)
+{
+ /* Set fsm timer load */
+ struct lcp *lcp = fsm2lcp(fp);
+
+ fp->FsmTimer.load = lcp->cfg.fsm.timeout * SECTICKS;
+ switch (what) {
+ case FSM_REQ_TIMER:
+ fp->restart = lcp->cfg.fsm.maxreq;
+ break;
+ case FSM_TRM_TIMER:
+ fp->restart = lcp->cfg.fsm.maxtrm;
+ break;
+ default:
+ fp->restart = 1;
+ break;
+ }
+}
+
+static void
+LcpSendConfigReq(struct fsm *fp)
+{
+ /* Send config REQ please */
+ struct physical *p = link2physical(fp->link);
+ struct lcp *lcp = fsm2lcp(fp);
+ u_char buff[200];
+ struct fsm_opt *o;
+ struct mp *mp;
+ u_int16_t proto;
+ u_short maxmru;
+
+ if (!p) {
+ log_Printf(LogERROR, "%s: LcpSendConfigReq: Not a physical link !\n",
+ fp->link->name);
+ return;
+ }
+
+ o = (struct fsm_opt *)buff;
+ if (!physical_IsSync(p)) {
+ if (lcp->want_acfcomp && !REJECTED(lcp, TY_ACFCOMP))
+ INC_FSM_OPT(TY_ACFCOMP, 2, o);
+
+ if (lcp->want_protocomp && !REJECTED(lcp, TY_PROTOCOMP))
+ INC_FSM_OPT(TY_PROTOCOMP, 2, o);
+
+ if (!REJECTED(lcp, TY_ACCMAP)) {
+ ua_htonl(&lcp->want_accmap, o->data);
+ INC_FSM_OPT(TY_ACCMAP, 6, o);
+ }
+ }
+
+ maxmru = p ? physical_DeviceMTU(p) : 0;
+ if (lcp->cfg.max_mru && (!maxmru || maxmru > lcp->cfg.max_mru))
+ maxmru = lcp->cfg.max_mru;
+ if (maxmru && lcp->want_mru > maxmru) {
+ log_Printf(LogWARN, "%s: Reducing configured MRU from %u to %u\n",
+ fp->link->name, lcp->want_mru, maxmru);
+ lcp->want_mru = maxmru;
+ }
+ if (!REJECTED(lcp, TY_MRU)) {
+ ua_htons(&lcp->want_mru, o->data);
+ INC_FSM_OPT(TY_MRU, 4, o);
+ }
+
+ if (lcp->want_magic && !REJECTED(lcp, TY_MAGICNUM)) {
+ ua_htonl(&lcp->want_magic, o->data);
+ INC_FSM_OPT(TY_MAGICNUM, 6, o);
+ }
+
+ if (lcp->want_lqrperiod && !REJECTED(lcp, TY_QUALPROTO)) {
+ proto = PROTO_LQR;
+ ua_htons(&proto, o->data);
+ ua_htonl(&lcp->want_lqrperiod, o->data + 2);
+ INC_FSM_OPT(TY_QUALPROTO, 8, o);
+ }
+
+ switch (lcp->want_auth) {
+ case PROTO_PAP:
+ proto = PROTO_PAP;
+ ua_htons(&proto, o->data);
+ INC_FSM_OPT(TY_AUTHPROTO, 4, o);
+ break;
+
+ case PROTO_CHAP:
+ proto = PROTO_CHAP;
+ ua_htons(&proto, o->data);
+ o->data[2] = lcp->want_authtype;
+ INC_FSM_OPT(TY_AUTHPROTO, 5, o);
+ break;
+ }
+
+ if (!REJECTED(lcp, TY_CALLBACK)) {
+ if (lcp->want_callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) {
+ *o->data = CALLBACK_AUTH;
+ INC_FSM_OPT(TY_CALLBACK, 3, o);
+ } else if (lcp->want_callback.opmask & CALLBACK_BIT(CALLBACK_CBCP)) {
+ *o->data = CALLBACK_CBCP;
+ INC_FSM_OPT(TY_CALLBACK, 3, o);
+ } else if (lcp->want_callback.opmask & CALLBACK_BIT(CALLBACK_E164)) {
+ size_t sz = strlen(lcp->want_callback.msg);
+
+ if (sz > sizeof o->data - 1) {
+ sz = sizeof o->data - 1;
+ log_Printf(LogWARN, "Truncating E164 data to %zu octets (oops!)\n",
+ sz);
+ }
+ *o->data = CALLBACK_E164;
+ memcpy(o->data + 1, lcp->want_callback.msg, sz);
+ INC_FSM_OPT(TY_CALLBACK, sz + 3, o);
+ }
+ }
+
+ if (lcp->want_mrru && !REJECTED(lcp, TY_MRRU)) {
+ ua_htons(&lcp->want_mrru, o->data);
+ INC_FSM_OPT(TY_MRRU, 4, o);
+
+ if (lcp->want_shortseq && !REJECTED(lcp, TY_SHORTSEQ))
+ INC_FSM_OPT(TY_SHORTSEQ, 2, o);
+ }
+
+ mp = &lcp->fsm.bundle->ncp.mp;
+ if (mp->cfg.enddisc.class != 0 && IsEnabled(mp->cfg.negenddisc) &&
+ !REJECTED(lcp, TY_ENDDISC)) {
+ *o->data = mp->cfg.enddisc.class;
+ memcpy(o->data+1, mp->cfg.enddisc.address, mp->cfg.enddisc.len);
+ INC_FSM_OPT(TY_ENDDISC, mp->cfg.enddisc.len + 3, o);
+ }
+
+ fsm_Output(fp, CODE_CONFIGREQ, fp->reqid, buff, (u_char *)o - buff,
+ MB_LCPOUT);
+}
+
+void
+lcp_SendProtoRej(struct lcp *lcp, u_char *option, int count)
+{
+ /* Don't understand `option' */
+ fsm_Output(&lcp->fsm, CODE_PROTOREJ, lcp->fsm.reqid, option, count,
+ MB_LCPOUT);
+}
+
+int
+lcp_SendIdentification(struct lcp *lcp)
+{
+ static u_char id; /* Use a private id */
+ u_char msg[DEF_MRU - 3];
+ const char *argv[2];
+ char *exp[2];
+
+ if (*lcp->cfg.ident == '\0')
+ return 0;
+
+ argv[0] = lcp->cfg.ident;
+ argv[1] = NULL;
+
+ command_Expand(exp, 1, argv, lcp->fsm.bundle, 1, getpid());
+
+ ua_htonl(&lcp->want_magic, msg);
+ strncpy(msg + 4, exp[0], sizeof msg - 5);
+ msg[sizeof msg - 1] = '\0';
+
+ fsm_Output(&lcp->fsm, CODE_IDENT, id++, msg, 4 + strlen(msg + 4), MB_LCPOUT);
+ log_Printf(LogLCP, " MAGICNUM %08x\n", lcp->want_magic);
+ log_Printf(LogLCP, " TEXT %s\n", msg + 4);
+
+ command_Free(1, exp);
+ return 1;
+}
+
+void
+lcp_RecvIdentification(struct lcp *lcp, char *data)
+{
+ log_Printf(LogLCP, " MAGICNUM %08x\n", lcp->his_magic);
+ log_Printf(LogLCP, " TEXT %s\n", data);
+}
+
+static void
+LcpSentTerminateReq(struct fsm *fp __unused)
+{
+ /* Term REQ just sent by FSM */
+}
+
+static void
+LcpSendTerminateAck(struct fsm *fp, u_char id)
+{
+ /* Send Term ACK please */
+ struct physical *p = link2physical(fp->link);
+
+ if (p && p->dl->state == DATALINK_CBCP)
+ cbcp_ReceiveTerminateReq(p);
+
+ fsm_Output(fp, CODE_TERMACK, id, NULL, 0, MB_LCPOUT);
+}
+
+static void
+LcpLayerStart(struct fsm *fp)
+{
+ /* We're about to start up ! */
+ struct lcp *lcp = fsm2lcp(fp);
+
+ log_Printf(LogLCP, "%s: LayerStart\n", fp->link->name);
+ lcp->LcpFailedMagic = 0;
+ fp->more.reqs = fp->more.naks = fp->more.rejs = lcp->cfg.fsm.maxreq * 3;
+ lcp->mru_req = 0;
+}
+
+static void
+LcpLayerFinish(struct fsm *fp)
+{
+ /* We're now down */
+ log_Printf(LogLCP, "%s: LayerFinish\n", fp->link->name);
+}
+
+static int
+LcpLayerUp(struct fsm *fp)
+{
+ /* We're now up */
+ struct physical *p = link2physical(fp->link);
+ struct lcp *lcp = fsm2lcp(fp);
+
+ log_Printf(LogLCP, "%s: LayerUp\n", fp->link->name);
+ physical_SetAsyncParams(p, lcp->want_accmap, lcp->his_accmap);
+ lqr_Start(lcp);
+ hdlc_StartTimer(&p->hdlc);
+ fp->more.reqs = fp->more.naks = fp->more.rejs = lcp->cfg.fsm.maxreq * 3;
+
+ lcp_SendIdentification(lcp);
+
+ return 1;
+}
+
+static void
+LcpLayerDown(struct fsm *fp)
+{
+ /* About to come down */
+ struct physical *p = link2physical(fp->link);
+
+ log_Printf(LogLCP, "%s: LayerDown\n", fp->link->name);
+ hdlc_StopTimer(&p->hdlc);
+ lqr_StopTimer(p);
+ lcp_Setup(fsm2lcp(fp), 0);
+}
+
+static int
+E164ok(struct callback *cb, char *req, int sz)
+{
+ char list[sizeof cb->msg], *next;
+ int len;
+
+ if (!strcmp(cb->msg, "*"))
+ return 1;
+
+ strncpy(list, cb->msg, sizeof list - 1);
+ list[sizeof list - 1] = '\0';
+ for (next = strtok(list, ","); next; next = strtok(NULL, ",")) {
+ len = strlen(next);
+ if (sz == len && !memcmp(list, req, sz))
+ return 1;
+ }
+ return 0;
+}
+
+static int
+lcp_auth_nak(struct lcp *lcp, struct fsm_decode *dec)
+{
+ struct fsm_opt nak;
+
+ nak.hdr.id = TY_AUTHPROTO;
+
+ if (IsAccepted(lcp->cfg.pap)) {
+ nak.hdr.len = 4;
+ nak.data[0] = (unsigned char)(PROTO_PAP >> 8);
+ nak.data[1] = (unsigned char)PROTO_PAP;
+ fsm_nak(dec, &nak);
+ return 1;
+ }
+
+ nak.hdr.len = 5;
+ nak.data[0] = (unsigned char)(PROTO_CHAP >> 8);
+ nak.data[1] = (unsigned char)PROTO_CHAP;
+
+ if (IsAccepted(lcp->cfg.chap05)) {
+ nak.data[2] = 0x05;
+ fsm_nak(dec, &nak);
+#ifndef NODES
+ } else if (IsAccepted(lcp->cfg.chap80nt) ||
+ IsAccepted(lcp->cfg.chap80lm)) {
+ nak.data[2] = 0x80;
+ fsm_nak(dec, &nak);
+ } else if (IsAccepted(lcp->cfg.chap81)) {
+ nak.data[2] = 0x81;
+ fsm_nak(dec, &nak);
+#endif
+ } else {
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+LcpDecodeConfig(struct fsm *fp, u_char *cp, u_char *end, int mode_type,
+ struct fsm_decode *dec)
+{
+ /* Deal with incoming PROTO_LCP */
+ struct lcp *lcp = fsm2lcp(fp);
+ int pos, op, callback_req, chap_type;
+ size_t sz;
+ u_int32_t magic, accmap;
+ u_short mru, phmtu, maxmtu, maxmru, wantmtu, wantmru, proto;
+ struct lqrreq req;
+ char request[20], desc[22];
+ struct mp *mp;
+ struct physical *p = link2physical(fp->link);
+ struct fsm_opt *opt, nak;
+
+ sz = 0;
+ op = callback_req = 0;
+
+ while (end - cp >= (int)sizeof(opt->hdr)) {
+ if ((opt = fsm_readopt(&cp)) == NULL)
+ break;
+
+ snprintf(request, sizeof request, " %s[%d]", protoname(opt->hdr.id),
+ opt->hdr.len);
+
+ switch (opt->hdr.id) {
+ case TY_MRRU:
+ mp = &lcp->fsm.bundle->ncp.mp;
+ ua_ntohs(opt->data, &mru);
+ log_Printf(LogLCP, "%s %u\n", request, mru);
+
+ switch (mode_type) {
+ case MODE_REQ:
+ if (mp->cfg.mrru) {
+ if (REJECTED(lcp, TY_MRRU))
+ /* Ignore his previous reject so that we REQ next time */
+ lcp->his_reject &= ~(1 << opt->hdr.id);
+
+ if (mru > MAX_MRU) {
+ /* Push him down to MAX_MRU */
+ lcp->his_mrru = MAX_MRU;
+ nak.hdr.id = TY_MRRU;
+ nak.hdr.len = 4;
+ ua_htons(&lcp->his_mrru, nak.data);
+ fsm_nak(dec, &nak);
+ } else if (mru < MIN_MRU) {
+ /* Push him up to MIN_MRU */
+ lcp->his_mrru = MIN_MRU;
+ nak.hdr.id = TY_MRRU;
+ nak.hdr.len = 4;
+ ua_htons(&lcp->his_mrru, nak.data);
+ fsm_nak(dec, &nak);
+ } else {
+ lcp->his_mrru = mru;
+ fsm_ack(dec, opt);
+ }
+ break;
+ } else {
+ fsm_rej(dec, opt);
+ lcp->my_reject |= (1 << opt->hdr.id);
+ }
+ break;
+ case MODE_NAK:
+ if (mp->cfg.mrru) {
+ if (REJECTED(lcp, TY_MRRU))
+ /* Must have changed his mind ! */
+ lcp->his_reject &= ~(1 << opt->hdr.id);
+
+ if (mru > MAX_MRU)
+ lcp->want_mrru = MAX_MRU;
+ else if (mru < MIN_MRU)
+ lcp->want_mrru = MIN_MRU;
+ else
+ lcp->want_mrru = mru;
+ }
+ /* else we honour our config and don't send the suggested REQ */
+ break;
+ case MODE_REJ:
+ lcp->his_reject |= (1 << opt->hdr.id);
+ lcp->want_mrru = 0; /* Ah well, no multilink :-( */
+ break;
+ }
+ break;
+
+ case TY_MRU:
+ lcp->mru_req = 1;
+ ua_ntohs(opt->data, &mru);
+ log_Printf(LogLCP, "%s %d\n", request, mru);
+
+ switch (mode_type) {
+ case MODE_REQ:
+ maxmtu = p ? physical_DeviceMTU(p) : 0;
+ if (lcp->cfg.max_mtu && (!maxmtu || maxmtu > lcp->cfg.max_mtu))
+ maxmtu = lcp->cfg.max_mtu;
+ wantmtu = lcp->cfg.mtu;
+ if (maxmtu && wantmtu > maxmtu) {
+ log_Printf(LogWARN, "%s: Reducing configured MTU from %u to %u\n",
+ fp->link->name, wantmtu, maxmtu);
+ wantmtu = maxmtu;
+ }
+
+ if (maxmtu && mru > maxmtu) {
+ lcp->his_mru = maxmtu;
+ nak.hdr.id = TY_MRU;
+ nak.hdr.len = 4;
+ ua_htons(&lcp->his_mru, nak.data);
+ fsm_nak(dec, &nak);
+ } else if (wantmtu && mru < wantmtu) {
+ /* Push him up to MTU or MIN_MRU */
+ lcp->his_mru = wantmtu;
+ nak.hdr.id = TY_MRU;
+ nak.hdr.len = 4;
+ ua_htons(&lcp->his_mru, nak.data);
+ fsm_nak(dec, &nak);
+ } else {
+ lcp->his_mru = mru;
+ fsm_ack(dec, opt);
+ }
+ break;
+ case MODE_NAK:
+ maxmru = p ? physical_DeviceMTU(p) : 0;
+ if (lcp->cfg.max_mru && (!maxmru || maxmru > lcp->cfg.max_mru))
+ maxmru = lcp->cfg.max_mru;
+ wantmru = lcp->cfg.mru > maxmru ? maxmru : lcp->cfg.mru;
+
+ if (wantmru && mru > wantmru)
+ lcp->want_mru = wantmru;
+ else if (mru > maxmru)
+ lcp->want_mru = maxmru;
+ else if (mru < MIN_MRU)
+ lcp->want_mru = MIN_MRU;
+ else
+ lcp->want_mru = mru;
+ break;
+ case MODE_REJ:
+ lcp->his_reject |= (1 << opt->hdr.id);
+ /* Set the MTU to what we want anyway - the peer won't care! */
+ if (lcp->his_mru > lcp->want_mru)
+ lcp->his_mru = lcp->want_mru;
+ break;
+ }
+ break;
+
+ case TY_ACCMAP:
+ ua_ntohl(opt->data, &accmap);
+ log_Printf(LogLCP, "%s 0x%08lx\n", request, (u_long)accmap);
+
+ switch (mode_type) {
+ case MODE_REQ:
+ lcp->his_accmap = accmap;
+ fsm_ack(dec, opt);
+ break;
+ case MODE_NAK:
+ lcp->want_accmap = accmap;
+ break;
+ case MODE_REJ:
+ lcp->his_reject |= (1 << opt->hdr.id);
+ break;
+ }
+ break;
+
+ case TY_AUTHPROTO:
+ ua_ntohs(opt->data, &proto);
+ chap_type = opt->hdr.len == 5 ? opt->data[2] : 0;
+
+ log_Printf(LogLCP, "%s 0x%04x (%s)\n", request, proto,
+ Auth2Nam(proto, chap_type));
+
+ switch (mode_type) {
+ case MODE_REQ:
+ switch (proto) {
+ case PROTO_PAP:
+ if (opt->hdr.len == 4 && IsAccepted(lcp->cfg.pap)) {
+ lcp->his_auth = proto;
+ lcp->his_authtype = 0;
+ fsm_ack(dec, opt);
+ } else if (!lcp_auth_nak(lcp, dec)) {
+ lcp->my_reject |= (1 << opt->hdr.id);
+ fsm_rej(dec, opt);
+ }
+ break;
+
+ case PROTO_CHAP:
+ if ((chap_type == 0x05 && IsAccepted(lcp->cfg.chap05))
+#ifndef NODES
+ || (chap_type == 0x80 && (IsAccepted(lcp->cfg.chap80nt) ||
+ (IsAccepted(lcp->cfg.chap80lm))))
+ || (chap_type == 0x81 && IsAccepted(lcp->cfg.chap81))
+#endif
+ ) {
+ lcp->his_auth = proto;
+ lcp->his_authtype = chap_type;
+ fsm_ack(dec, opt);
+ } else {
+#ifdef NODES
+ if (chap_type == 0x80) {
+ log_Printf(LogWARN, "CHAP 0x80 not available without DES\n");
+ } else if (chap_type == 0x81) {
+ log_Printf(LogWARN, "CHAP 0x81 not available without DES\n");
+ } else
+#endif
+ if (chap_type != 0x05)
+ log_Printf(LogWARN, "%s not supported\n",
+ Auth2Nam(PROTO_CHAP, chap_type));
+
+ if (!lcp_auth_nak(lcp, dec)) {
+ lcp->my_reject |= (1 << opt->hdr.id);
+ fsm_rej(dec, opt);
+ }
+ }
+ break;
+
+ default:
+ log_Printf(LogLCP, "%s 0x%04x - not recognised\n",
+ request, proto);
+ if (!lcp_auth_nak(lcp, dec)) {
+ lcp->my_reject |= (1 << opt->hdr.id);
+ fsm_rej(dec, opt);
+ }
+ break;
+ }
+ break;
+
+ case MODE_NAK:
+ switch (proto) {
+ case PROTO_PAP:
+ if (IsEnabled(lcp->cfg.pap)) {
+ lcp->want_auth = PROTO_PAP;
+ lcp->want_authtype = 0;
+ } else {
+ log_Printf(LogLCP, "Peer will only send PAP (not enabled)\n");
+ lcp->his_reject |= (1 << opt->hdr.id);
+ }
+ break;
+ case PROTO_CHAP:
+ if (chap_type == 0x05 && IsEnabled(lcp->cfg.chap05)) {
+ lcp->want_auth = PROTO_CHAP;
+ lcp->want_authtype = 0x05;
+#ifndef NODES
+ } else if (chap_type == 0x80 && (IsEnabled(lcp->cfg.chap80nt) ||
+ IsEnabled(lcp->cfg.chap80lm))) {
+ lcp->want_auth = PROTO_CHAP;
+ lcp->want_authtype = 0x80;
+ } else if (chap_type == 0x81 && IsEnabled(lcp->cfg.chap81)) {
+ lcp->want_auth = PROTO_CHAP;
+ lcp->want_authtype = 0x81;
+#endif
+ } else {
+#ifdef NODES
+ if (chap_type == 0x80) {
+ log_Printf(LogLCP, "Peer will only send MSCHAP (not available"
+ " without DES)\n");
+ } else if (chap_type == 0x81) {
+ log_Printf(LogLCP, "Peer will only send MSCHAPV2 (not available"
+ " without DES)\n");
+ } else
+#endif
+ log_Printf(LogLCP, "Peer will only send %s (not %s)\n",
+ Auth2Nam(PROTO_CHAP, chap_type),
+#ifndef NODES
+ (chap_type == 0x80 || chap_type == 0x81) ? "configured" :
+#endif
+ "supported");
+ lcp->his_reject |= (1 << opt->hdr.id);
+ }
+ break;
+ default:
+ /* We've been NAK'd with something we don't understand :-( */
+ lcp->his_reject |= (1 << opt->hdr.id);
+ break;
+ }
+ break;
+
+ case MODE_REJ:
+ lcp->his_reject |= (1 << opt->hdr.id);
+ break;
+ }
+ break;
+
+ case TY_QUALPROTO:
+ memcpy(&req, opt, sizeof req);
+ log_Printf(LogLCP, "%s proto %x, interval %lums\n",
+ request, ntohs(req.proto), (u_long)ntohl(req.period) * 10);
+ switch (mode_type) {
+ case MODE_REQ:
+ if (ntohs(req.proto) != PROTO_LQR || !IsAccepted(lcp->cfg.lqr)) {
+ fsm_rej(dec, opt);
+ lcp->my_reject |= (1 << opt->hdr.id);
+ } else {
+ lcp->his_lqrperiod = ntohl(req.period);
+ if (lcp->his_lqrperiod < MIN_LQRPERIOD * 100)
+ lcp->his_lqrperiod = MIN_LQRPERIOD * 100;
+ req.period = htonl(lcp->his_lqrperiod);
+ fsm_ack(dec, opt);
+ }
+ break;
+ case MODE_NAK:
+ lcp->want_lqrperiod = ntohl(req.period);
+ break;
+ case MODE_REJ:
+ lcp->his_reject |= (1 << opt->hdr.id);
+ break;
+ }
+ break;
+
+ case TY_MAGICNUM:
+ ua_ntohl(opt->data, &magic);
+ log_Printf(LogLCP, "%s 0x%08lx\n", request, (u_long)magic);
+
+ switch (mode_type) {
+ case MODE_REQ:
+ if (lcp->want_magic) {
+ /* Validate magic number */
+ if (magic == lcp->want_magic) {
+ sigset_t emptyset;
+
+ log_Printf(LogLCP, "Magic is same (%08lx) - %d times\n",
+ (u_long)magic, ++lcp->LcpFailedMagic);
+ lcp->want_magic = GenerateMagic();
+ fsm_nak(dec, opt);
+ ualarm(TICKUNIT * (4 + 4 * lcp->LcpFailedMagic), 0);
+ sigemptyset(&emptyset);
+ sigsuspend(&emptyset);
+ } else {
+ lcp->his_magic = magic;
+ lcp->LcpFailedMagic = 0;
+ fsm_ack(dec, opt);
+ }
+ } else {
+ lcp->my_reject |= (1 << opt->hdr.id);
+ fsm_rej(dec, opt);
+ }
+ break;
+ case MODE_NAK:
+ log_Printf(LogLCP, " Magic 0x%08lx is NAKed!\n", (u_long)magic);
+ lcp->want_magic = GenerateMagic();
+ break;
+ case MODE_REJ:
+ log_Printf(LogLCP, " Magic 0x%08x is REJected!\n", magic);
+ lcp->want_magic = 0;
+ lcp->his_reject |= (1 << opt->hdr.id);
+ break;
+ }
+ break;
+
+ case TY_PROTOCOMP:
+ log_Printf(LogLCP, "%s\n", request);
+
+ switch (mode_type) {
+ case MODE_REQ:
+ if (IsAccepted(lcp->cfg.protocomp)) {
+ lcp->his_protocomp = 1;
+ fsm_ack(dec, opt);
+ } else {
+#ifdef OLDMST
+ /* MorningStar before v1.3 needs NAK */
+ fsm_nak(dec, opt);
+#else
+ fsm_rej(dec, opt);
+ lcp->my_reject |= (1 << opt->hdr.id);
+#endif
+ }
+ break;
+ case MODE_NAK:
+ case MODE_REJ:
+ lcp->want_protocomp = 0;
+ lcp->his_reject |= (1 << opt->hdr.id);
+ break;
+ }
+ break;
+
+ case TY_ACFCOMP:
+ log_Printf(LogLCP, "%s\n", request);
+ switch (mode_type) {
+ case MODE_REQ:
+ if (IsAccepted(lcp->cfg.acfcomp)) {
+ lcp->his_acfcomp = 1;
+ fsm_ack(dec, opt);
+ } else {
+#ifdef OLDMST
+ /* MorningStar before v1.3 needs NAK */
+ fsm_nak(dec, opt);
+#else
+ fsm_rej(dec, opt);
+ lcp->my_reject |= (1 << opt->hdr.id);
+#endif
+ }
+ break;
+ case MODE_NAK:
+ case MODE_REJ:
+ lcp->want_acfcomp = 0;
+ lcp->his_reject |= (1 << opt->hdr.id);
+ break;
+ }
+ break;
+
+ case TY_SDP:
+ log_Printf(LogLCP, "%s\n", request);
+ switch (mode_type) {
+ case MODE_REQ:
+ case MODE_NAK:
+ case MODE_REJ:
+ break;
+ }
+ break;
+
+ case TY_CALLBACK:
+ if (opt->hdr.len == 2) {
+ op = CALLBACK_NONE;
+ sz = 0;
+ } else {
+ op = (int)opt->data[0];
+ sz = opt->hdr.len - 3;
+ }
+ switch (op) {
+ case CALLBACK_AUTH:
+ log_Printf(LogLCP, "%s Auth\n", request);
+ break;
+ case CALLBACK_DIALSTRING:
+ log_Printf(LogLCP, "%s Dialstring %.*s\n", request, (int)sz,
+ opt->data + 1);
+ break;
+ case CALLBACK_LOCATION:
+ log_Printf(LogLCP, "%s Location %.*s\n", request, (int)sz,
+ opt->data + 1);
+ break;
+ case CALLBACK_E164:
+ log_Printf(LogLCP, "%s E.164 (%.*s)\n", request, (int)sz,
+ opt->data + 1);
+ break;
+ case CALLBACK_NAME:
+ log_Printf(LogLCP, "%s Name %.*s\n", request, (int)sz,
+ opt->data + 1);
+ break;
+ case CALLBACK_CBCP:
+ log_Printf(LogLCP, "%s CBCP\n", request);
+ break;
+ default:
+ log_Printf(LogLCP, "%s ???\n", request);
+ break;
+ }
+
+ switch (mode_type) {
+ case MODE_REQ:
+ callback_req = 1;
+ if (p->type != PHYS_DIRECT) {
+ fsm_rej(dec, opt);
+ lcp->my_reject |= (1 << opt->hdr.id);
+ }
+ nak.hdr.id = opt->hdr.id;
+ nak.hdr.len = 3;
+ if ((p->dl->cfg.callback.opmask & CALLBACK_BIT(op)) &&
+ (op != CALLBACK_AUTH || p->link.lcp.want_auth) &&
+ (op != CALLBACK_E164 ||
+ E164ok(&p->dl->cfg.callback, opt->data + 1, sz))) {
+ lcp->his_callback.opmask = CALLBACK_BIT(op);
+ if (sz > sizeof lcp->his_callback.msg - 1) {
+ sz = sizeof lcp->his_callback.msg - 1;
+ log_Printf(LogWARN, "Truncating option arg to %zu octets\n", sz);
+ }
+ memcpy(lcp->his_callback.msg, opt->data + 1, sz);
+ lcp->his_callback.msg[sz] = '\0';
+ fsm_ack(dec, opt);
+ } else if ((p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) &&
+ p->link.lcp.auth_ineed) {
+ nak.data[0] = CALLBACK_AUTH;
+ fsm_nak(dec, &nak);
+ } else if (p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_CBCP)) {
+ nak.data[0] = CALLBACK_CBCP;
+ fsm_nak(dec, &nak);
+ } else if (p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_E164)) {
+ nak.data[0] = CALLBACK_E164;
+ fsm_nak(dec, &nak);
+ } else if (p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) {
+ log_Printf(LogWARN, "Cannot insist on auth callback without"
+ " PAP or CHAP enabled !\n");
+ nak.data[0] = 2;
+ fsm_nak(dec, &nak);
+ } else {
+ lcp->my_reject |= (1 << opt->hdr.id);
+ fsm_rej(dec, opt);
+ }
+ break;
+ case MODE_NAK:
+ /* We don't do what he NAKs with, we do things in our preferred order */
+ if (lcp->want_callback.opmask & CALLBACK_BIT(CALLBACK_AUTH))
+ lcp->want_callback.opmask &= ~CALLBACK_BIT(CALLBACK_AUTH);
+ else if (lcp->want_callback.opmask & CALLBACK_BIT(CALLBACK_CBCP))
+ lcp->want_callback.opmask &= ~CALLBACK_BIT(CALLBACK_CBCP);
+ else if (lcp->want_callback.opmask & CALLBACK_BIT(CALLBACK_E164))
+ lcp->want_callback.opmask &= ~CALLBACK_BIT(CALLBACK_E164);
+ if (lcp->want_callback.opmask == CALLBACK_BIT(CALLBACK_NONE)) {
+ log_Printf(LogPHASE, "Peer NAKd all callbacks, trying none\n");
+ lcp->want_callback.opmask = 0;
+ } else if (!lcp->want_callback.opmask) {
+ log_Printf(LogPHASE, "Peer NAKd last configured callback\n");
+ fsm_Close(&lcp->fsm);
+ }
+ break;
+ case MODE_REJ:
+ if (lcp->want_callback.opmask & CALLBACK_BIT(CALLBACK_NONE)) {
+ lcp->his_reject |= (1 << opt->hdr.id);
+ lcp->want_callback.opmask = 0;
+ } else {
+ log_Printf(LogPHASE, "Peer rejected *required* callback\n");
+ fsm_Close(&lcp->fsm);
+ }
+ break;
+ }
+ break;
+
+ case TY_SHORTSEQ:
+ mp = &lcp->fsm.bundle->ncp.mp;
+ log_Printf(LogLCP, "%s\n", request);
+
+ switch (mode_type) {
+ case MODE_REQ:
+ if (lcp->want_mrru && IsAccepted(mp->cfg.shortseq)) {
+ lcp->his_shortseq = 1;
+ fsm_ack(dec, opt);
+ } else {
+ fsm_rej(dec, opt);
+ lcp->my_reject |= (1 << opt->hdr.id);
+ }
+ break;
+ case MODE_NAK:
+ /*
+ * He's trying to get us to ask for short sequence numbers.
+ * We ignore the NAK and honour our configuration file instead.
+ */
+ break;
+ case MODE_REJ:
+ lcp->his_reject |= (1 << opt->hdr.id);
+ lcp->want_shortseq = 0; /* For when we hit MP */
+ break;
+ }
+ break;
+
+ case TY_ENDDISC:
+ mp = &lcp->fsm.bundle->ncp.mp;
+ log_Printf(LogLCP, "%s %s\n", request,
+ mp_Enddisc(opt->data[0], opt->data + 1, opt->hdr.len - 3));
+ switch (mode_type) {
+ case MODE_REQ:
+ if (!p) {
+ log_Printf(LogLCP, " ENDDISC rejected - not a physical link\n");
+ fsm_rej(dec, opt);
+ lcp->my_reject |= (1 << opt->hdr.id);
+ } else if (!IsAccepted(mp->cfg.negenddisc)) {
+ lcp->my_reject |= (1 << opt->hdr.id);
+ fsm_rej(dec, opt);
+ } else if (opt->hdr.len < sizeof p->dl->peer.enddisc.address + 3 &&
+ opt->data[0] <= MAX_ENDDISC_CLASS) {
+ p->dl->peer.enddisc.class = opt->data[0];
+ p->dl->peer.enddisc.len = opt->hdr.len - 3;
+ memcpy(p->dl->peer.enddisc.address, opt->data + 1, opt->hdr.len - 3);
+ p->dl->peer.enddisc.address[opt->hdr.len - 3] = '\0';
+ /* XXX: If mp->active, compare and NAK with mp->peer ? */
+ fsm_ack(dec, opt);
+ } else {
+ if (opt->data[0] > MAX_ENDDISC_CLASS)
+ log_Printf(LogLCP, " ENDDISC rejected - unrecognised class %d\n",
+ opt->data[0]);
+ else
+ log_Printf(LogLCP, " ENDDISC rejected - local max length is %ld\n",
+ (long)(sizeof p->dl->peer.enddisc.address - 1));
+ fsm_rej(dec, opt);
+ lcp->my_reject |= (1 << opt->hdr.id);
+ }
+ break;
+
+ case MODE_NAK: /* Treat this as a REJ, we don't vary our disc (yet) */
+ case MODE_REJ:
+ lcp->his_reject |= (1 << opt->hdr.id);
+ break;
+ }
+ break;
+
+ default:
+ sz = (sizeof desc - 2) / 2;
+ if (sz + 2 > opt->hdr.len)
+ sz = opt->hdr.len - 2;
+ pos = 0;
+ desc[0] = sz ? ' ' : '\0';
+ for (pos = 0; sz--; pos++)
+ sprintf(desc+(pos<<1)+1, "%02x", opt->data[pos]);
+
+ log_Printf(LogLCP, "%s%s\n", request, desc);
+
+ if (mode_type == MODE_REQ) {
+ fsm_rej(dec, opt);
+ lcp->my_reject |= (1 << opt->hdr.id);
+ }
+ break;
+ }
+ }
+
+ if (mode_type != MODE_NOP) {
+ if (mode_type == MODE_REQ && p && p->type == PHYS_DIRECT &&
+ p->dl->cfg.callback.opmask && !callback_req &&
+ !(p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_NONE))) {
+ /* We *REQUIRE* that the peer requests callback */
+ nak.hdr.id = TY_CALLBACK;
+ nak.hdr.len = 3;
+ if ((p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) &&
+ p->link.lcp.want_auth)
+ nak.data[0] = CALLBACK_AUTH;
+ else if (p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_CBCP))
+ nak.data[0] = CALLBACK_CBCP;
+ else if (p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_E164))
+ nak.data[0] = CALLBACK_E164;
+ else {
+ log_Printf(LogWARN, "Cannot insist on auth callback without"
+ " PAP or CHAP enabled !\n");
+ nak.hdr.len = 2; /* XXX: Silly ! */
+ }
+ fsm_nak(dec, &nak);
+ }
+ if (mode_type == MODE_REQ && !lcp->mru_req) {
+ mru = DEF_MRU;
+ phmtu = p ? physical_DeviceMTU(p) : 0;
+ if (phmtu && mru > phmtu)
+ mru = phmtu;
+ if (mru > lcp->cfg.max_mtu)
+ mru = lcp->cfg.max_mtu;
+ if (mru < DEF_MRU) {
+ /* Don't let the peer use the default MRU */
+ lcp->his_mru = lcp->cfg.mtu && lcp->cfg.mtu < mru ? lcp->cfg.mtu : mru;
+ nak.hdr.id = TY_MRU;
+ nak.hdr.len = 4;
+ ua_htons(&lcp->his_mru, nak.data);
+ fsm_nak(dec, &nak);
+ lcp->mru_req = 1; /* Don't keep NAK'ing this */
+ }
+ }
+ fsm_opt_normalise(dec);
+ }
+}
+
+extern struct mbuf *
+lcp_Input(struct bundle *bundle __unused, struct link *l, struct mbuf *bp)
+{
+ /* Got PROTO_LCP from link */
+ m_settype(bp, MB_LCPIN);
+ fsm_Input(&l->lcp.fsm, bp);
+ return NULL;
+}
diff --git a/usr.sbin/ppp/lcp.h b/usr.sbin/ppp/lcp.h
new file mode 100644
index 0000000..e0382a8
--- /dev/null
+++ b/usr.sbin/ppp/lcp.h
@@ -0,0 +1,143 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+/* callback::opmask values */
+#define CALLBACK_AUTH (0)
+#define CALLBACK_DIALSTRING (1) /* Don't do this */
+#define CALLBACK_LOCATION (2) /* Don't do this */
+#define CALLBACK_E164 (3)
+#define CALLBACK_NAME (4) /* Don't do this */
+#define CALLBACK_CBCP (6)
+#define CALLBACK_NONE (14) /* No callback is ok */
+
+#define CALLBACK_BIT(n) ((n) < 0 ? 0 : 1 << (n))
+
+struct callback {
+ int opmask; /* want these types of callback */
+ char msg[SCRIPT_LEN]; /* with this data (E.164) */
+};
+
+#define REJECTED(p, x) ((p)->his_reject & (1<<(x)))
+
+struct lcp {
+ struct fsm fsm; /* The finite state machine */
+ u_int16_t his_mru; /* Peers maximum packet size */
+ u_int16_t his_mrru; /* Peers maximum reassembled packet size (MP) */
+ u_int32_t his_accmap; /* Peeers async char control map */
+ u_int32_t his_magic; /* Peers magic number */
+ u_int32_t his_lqrperiod; /* Peers LQR frequency (100ths of seconds) */
+ u_short his_auth; /* Peer wants this type of authentication */
+ u_char his_authtype; /* Fifth octet of REQ/NAK/REJ */
+ struct callback his_callback; /* Peer wants callback ? */
+ unsigned his_shortseq : 1; /* Peer would like only 12bit seqs (MP) */
+ unsigned his_protocomp : 1; /* Does peer do Protocol field compression */
+ unsigned his_acfcomp : 1; /* Does peer do addr & cntrl fld compression */
+ unsigned mru_req : 1; /* Has the peer requested an MRU */
+
+ u_short want_mru; /* Our maximum packet size */
+ u_short want_mrru; /* Our maximum reassembled packet size (MP) */
+ u_int32_t want_accmap; /* Our async char control map */
+ u_int32_t want_magic; /* Our magic number */
+ u_int32_t want_lqrperiod; /* Our LQR frequency (100ths of seconds) */
+ u_short want_auth; /* We want this type of authentication */
+ u_char want_authtype; /* Fifth octet of REQ/NAK/REJ */
+ struct callback want_callback;/* We want callback ? */
+ unsigned want_shortseq : 1; /* I'd like only 12bit seqs (MP) */
+ unsigned want_protocomp : 1; /* Do we do protocol field compression */
+ unsigned want_acfcomp : 1; /* Do we do addr & cntrl fld compression */
+
+ u_int32_t his_reject; /* Request codes rejected by peer */
+ u_int32_t my_reject; /* Request codes I have rejected */
+
+ u_short auth_iwait; /* I must authenticate to the peer */
+ u_short auth_ineed; /* I require that the peer authenticates */
+
+ int LcpFailedMagic; /* Number of `magic is same' errors */
+
+ struct {
+ u_short mru; /* Preferred MRU value */
+ u_short max_mru; /* Preferred MRU value */
+ u_short mtu; /* Preferred MTU */
+ u_short max_mtu; /* Preferred MTU */
+ u_int32_t accmap; /* Initial ACCMAP value */
+ int openmode; /* when to start CFG REQs */
+ u_int32_t lqrperiod; /* LQR frequency (seconds) */
+ struct fsm_retry fsm; /* How often/frequently to resend requests */
+ unsigned acfcomp : 2; /* Address & Control Field Compression neg */
+ unsigned chap05 : 2; /* Challenge Handshake Authentication proto */
+#ifndef NODES
+ unsigned chap80nt : 2; /* Microsoft (NT) CHAP */
+ unsigned chap80lm : 2; /* Microsoft (LANMan) CHAP */
+ unsigned chap81 : 2; /* Microsoft CHAP v2 */
+#endif
+ unsigned lqr : 2; /* Link Quality Report */
+ unsigned echo : 1; /* Send echo Requests */
+ unsigned pap : 2; /* Password Authentication protocol */
+ unsigned protocomp : 2; /* Protocol field compression */
+ char ident[DEF_MRU - 7]; /* SendIdentification() data */
+ } cfg;
+};
+
+#define LCP_MAXCODE CODE_IDENT
+#define LCP_MINMPCODE CODE_CODEREJ
+
+#define TY_MRU 1 /* Maximum-Receive-Unit */
+#define TY_ACCMAP 2 /* Async-Control-Character-Map */
+#define TY_AUTHPROTO 3 /* Authentication-Protocol */
+#define TY_QUALPROTO 4 /* Quality-Protocol */
+#define TY_MAGICNUM 5 /* Magic-Number */
+#define TY_RESERVED 6 /* RESERVED */
+#define TY_PROTOCOMP 7 /* Protocol-Field-Compression */
+#define TY_ACFCOMP 8 /* Address-and-Control-Field-Compression */
+#define TY_FCSALT 9 /* FCS-Alternatives */
+#define TY_SDP 10 /* Self-Describing-Padding */
+#define TY_CALLBACK 13 /* Callback */
+#define TY_CFRAMES 15 /* Compound-frames */
+#define TY_MRRU 17 /* Max Reconstructed Receive Unit (MP) */
+#define TY_SHORTSEQ 18 /* Want short seqs (12bit) please (see mp.h) */
+#define TY_ENDDISC 19 /* Endpoint discriminator */
+
+struct mbuf;
+struct link;
+struct bundle;
+struct cmdargs;
+
+#define fsm2lcp(fp) (fp->proto == PROTO_LCP ? (struct lcp *)fp : NULL)
+
+extern void lcp_Init(struct lcp *, struct bundle *, struct link *,
+ const struct fsm_parent *);
+extern void lcp_Setup(struct lcp *, int);
+
+extern void lcp_SendProtoRej(struct lcp *, u_char *, int);
+extern int lcp_SendIdentification(struct lcp *);
+extern void lcp_RecvIdentification(struct lcp *, char *);
+extern int lcp_ReportStatus(struct cmdargs const *);
+extern struct mbuf *lcp_Input(struct bundle *, struct link *, struct mbuf *);
+extern void lcp_SetupCallbacks(struct lcp *);
diff --git a/usr.sbin/ppp/link.c b/usr.sbin/ppp/link.c
new file mode 100644
index 0000000..dc5507d
--- /dev/null
+++ b/usr.sbin/ppp/link.c
@@ -0,0 +1,412 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include <sys/types.h>
+#include <netinet/in_systm.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "defs.h"
+#include "layer.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "proto.h"
+#include "fsm.h"
+#include "descriptor.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "prompt.h"
+#include "async.h"
+#include "physical.h"
+#include "mp.h"
+#include "iplist.h"
+#include "slcompress.h"
+#include "ncpaddr.h"
+#include "ip.h"
+#include "ipcp.h"
+#include "ipv6cp.h"
+#include "auth.h"
+#include "pap.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "command.h"
+
+static void Despatch(struct bundle *, struct link *, struct mbuf *, u_short);
+
+static inline void
+link_AddInOctets(struct link *l, int n)
+{
+ if (l->stats.gather) {
+ throughput_addin(&l->stats.total, n);
+ if (l->stats.parent)
+ throughput_addin(l->stats.parent, n);
+ }
+}
+
+static inline void
+link_AddOutOctets(struct link *l, int n)
+{
+ if (l->stats.gather) {
+ throughput_addout(&l->stats.total, n);
+ if (l->stats.parent)
+ throughput_addout(l->stats.parent, n);
+ }
+}
+
+void
+link_SequenceQueue(struct link *l)
+{
+ struct mqueue *queue, *highest;
+
+ log_Printf(LogDEBUG, "link_SequenceQueue\n");
+
+ highest = LINK_HIGHQ(l);
+ for (queue = l->Queue; queue < highest; queue++)
+ while (queue->len)
+ m_enqueue(highest, m_dequeue(queue));
+}
+
+void
+link_DeleteQueue(struct link *l)
+{
+ struct mqueue *queue, *highest;
+
+ highest = LINK_HIGHQ(l);
+ for (queue = l->Queue; queue <= highest; queue++)
+ while (queue->top)
+ m_freem(m_dequeue(queue));
+}
+
+size_t
+link_QueueLen(struct link *l)
+{
+ unsigned i;
+ size_t len;
+
+ for (i = 0, len = 0; i < LINK_QUEUES(l); i++)
+ len += l->Queue[i].len;
+
+ return len;
+}
+
+size_t
+link_QueueBytes(struct link *l)
+{
+ unsigned i;
+ size_t len, bytes;
+ struct mbuf *m;
+
+ bytes = 0;
+ for (i = 0, len = 0; i < LINK_QUEUES(l); i++) {
+ len = l->Queue[i].len;
+ m = l->Queue[i].top;
+ while (len--) {
+ bytes += m_length(m);
+ m = m->m_nextpkt;
+ }
+ }
+
+ return bytes;
+}
+
+void
+link_PendingLowPriorityData(struct link *l, size_t *pkts, size_t *octets)
+{
+ struct mqueue *queue, *highest;
+ struct mbuf *m;
+ size_t len;
+
+ /*
+ * This is all rfc1989 stuff... because our LQR packet is going to bypass
+ * everything that's not in the highest priority queue, we must be able to
+ * subtract that data from our outgoing packet/octet counts. However,
+ * we've already async-encoded our data at this point, but the async
+ * encodings MUSTn't be a part of the LQR-reported payload :( So, we have
+ * the async layer record how much it's padded the packet in the mbuf's
+ * priv field, and when we calculate our outgoing LQR values we subtract
+ * this value for each packet from the octet count sent.
+ */
+
+ highest = LINK_HIGHQ(l);
+ *pkts = *octets = 0;
+ for (queue = l->Queue; queue < highest; queue++) {
+ len = queue->len;
+ *pkts += len;
+ for (m = queue->top; len--; m = m->m_nextpkt)
+ *octets += m_length(m) - m->priv;
+ }
+}
+
+struct mbuf *
+link_Dequeue(struct link *l)
+{
+ int pri;
+ struct mbuf *bp;
+
+ for (bp = NULL, pri = LINK_QUEUES(l) - 1; pri >= 0; pri--)
+ if (l->Queue[pri].len) {
+ bp = m_dequeue(l->Queue + pri);
+ log_Printf(LogDEBUG, "link_Dequeue: Dequeued from queue %d,"
+ " containing %lu more packets\n", pri,
+ (u_long)l->Queue[pri].len);
+ break;
+ }
+
+ return bp;
+}
+
+static struct protostatheader {
+ u_short number;
+ const char *name;
+} ProtocolStat[NPROTOSTAT] = {
+ { PROTO_IP, "IP" },
+ { PROTO_VJUNCOMP, "VJ_UNCOMP" },
+ { PROTO_VJCOMP, "VJ_COMP" },
+ { PROTO_COMPD, "COMPD" },
+ { PROTO_ICOMPD, "ICOMPD" },
+ { PROTO_LCP, "LCP" },
+ { PROTO_IPCP, "IPCP" },
+ { PROTO_CCP, "CCP" },
+ { PROTO_PAP, "PAP" },
+ { PROTO_LQR, "LQR" },
+ { PROTO_CHAP, "CHAP" },
+ { PROTO_MP, "MULTILINK" },
+ { 0, "Others" }
+};
+
+void
+link_ProtocolRecord(struct link *l, u_short proto, int type)
+{
+ int i;
+
+ for (i = 0; i < NPROTOSTAT; i++)
+ if (ProtocolStat[i].number == proto)
+ break;
+
+ if (type == PROTO_IN)
+ l->proto_in[i]++;
+ else
+ l->proto_out[i]++;
+}
+
+void
+link_ReportProtocolStatus(struct link *l, struct prompt *prompt)
+{
+ int i;
+
+ prompt_Printf(prompt, " Protocol in out "
+ "Protocol in out\n");
+ for (i = 0; i < NPROTOSTAT; i++) {
+ prompt_Printf(prompt, " %-9s: %8lu, %8lu",
+ ProtocolStat[i].name, l->proto_in[i], l->proto_out[i]);
+ if ((i % 2) == 0)
+ prompt_Printf(prompt, "\n");
+ }
+ if (!(i % 2))
+ prompt_Printf(prompt, "\n");
+}
+
+void
+link_PushPacket(struct link *l, struct mbuf *bp, struct bundle *b, int pri,
+ u_short proto)
+{
+ int layer;
+
+ /*
+ * When we ``push'' a packet into the link, it gets processed by the
+ * ``push'' function in each layer starting at the top.
+ * We never expect the result of a ``push'' to be more than one
+ * packet (as we do with ``pull''s).
+ */
+
+ if(pri < 0 || (unsigned)pri >= LINK_QUEUES(l))
+ pri = 0;
+
+ bp->priv = 0; /* Adjusted by the async layer ! */
+ for (layer = l->nlayers; layer && bp; layer--)
+ if (l->layer[layer - 1]->push != NULL)
+ bp = (*l->layer[layer - 1]->push)(b, l, bp, pri, &proto);
+
+ if (bp) {
+ link_AddOutOctets(l, m_length(bp));
+ log_Printf(LogDEBUG, "link_PushPacket: Transmit proto 0x%04x\n", proto);
+ m_enqueue(l->Queue + pri, m_pullup(bp));
+ }
+}
+
+void
+link_PullPacket(struct link *l, char *buf, size_t len, struct bundle *b)
+{
+ struct mbuf *bp, *lbp[LAYER_MAX], *next;
+ u_short lproto[LAYER_MAX], proto;
+ int layer;
+
+ /*
+ * When we ``pull'' a packet from the link, it gets processed by the
+ * ``pull'' function in each layer starting at the bottom.
+ * Each ``pull'' may produce multiple packets, chained together using
+ * bp->m_nextpkt.
+ * Each packet that results from each pull has to be pulled through
+ * all of the higher layers before the next resulting packet is pulled
+ * through anything; this ensures that packets that depend on the
+ * fsm state resulting from the receipt of the previous packet aren't
+ * surprised.
+ */
+
+ link_AddInOctets(l, len);
+
+ memset(lbp, '\0', sizeof lbp);
+ lbp[0] = m_get(len, MB_UNKNOWN);
+ memcpy(MBUF_CTOP(lbp[0]), buf, len);
+ lproto[0] = 0;
+ layer = 0;
+
+ while (layer || lbp[layer]) {
+ if (lbp[layer] == NULL) {
+ layer--;
+ continue;
+ }
+ bp = lbp[layer];
+ lbp[layer] = bp->m_nextpkt;
+ bp->m_nextpkt = NULL;
+ proto = lproto[layer];
+
+ if (l->layer[layer]->pull != NULL)
+ bp = (*l->layer[layer]->pull)(b, l, bp, &proto);
+
+ if (layer == l->nlayers - 1) {
+ /* We've just done the top layer, despatch the packet(s) */
+ while (bp) {
+ next = bp->m_nextpkt;
+ bp->m_nextpkt = NULL;
+ log_Printf(LogDEBUG, "link_PullPacket: Despatch proto 0x%04x\n", proto);
+ Despatch(b, l, bp, proto);
+ bp = next;
+ }
+ } else {
+ lbp[++layer] = bp;
+ lproto[layer] = proto;
+ }
+ }
+}
+
+int
+link_Stack(struct link *l, struct layer *layer)
+{
+ if (l->nlayers == sizeof l->layer / sizeof l->layer[0]) {
+ log_Printf(LogERROR, "%s: Oops, cannot stack a %s layer...\n",
+ l->name, layer->name);
+ return 0;
+ }
+ l->layer[l->nlayers++] = layer;
+ return 1;
+}
+
+void
+link_EmptyStack(struct link *l)
+{
+ l->nlayers = 0;
+}
+
+static const struct {
+ u_short proto;
+ struct mbuf *(*fn)(struct bundle *, struct link *, struct mbuf *);
+} despatcher[] = {
+ { PROTO_IP, ipv4_Input },
+#ifndef NOINET6
+ { PROTO_IPV6, ipv6_Input },
+#endif
+ { PROTO_MP, mp_Input },
+ { PROTO_LCP, lcp_Input },
+ { PROTO_IPCP, ipcp_Input },
+#ifndef NOINET6
+ { PROTO_IPV6CP, ipv6cp_Input },
+#endif
+ { PROTO_PAP, pap_Input },
+ { PROTO_CHAP, chap_Input },
+ { PROTO_CCP, ccp_Input },
+ { PROTO_LQR, lqr_Input },
+ { PROTO_CBCP, cbcp_Input }
+};
+
+#define DSIZE (sizeof despatcher / sizeof despatcher[0])
+
+static void
+Despatch(struct bundle *bundle, struct link *l, struct mbuf *bp, u_short proto)
+{
+ unsigned f;
+
+ for (f = 0; f < DSIZE; f++)
+ if (despatcher[f].proto == proto) {
+ bp = (*despatcher[f].fn)(bundle, l, bp);
+ break;
+ }
+
+ if (bp) {
+ struct physical *p = link2physical(l);
+
+ log_Printf(LogPHASE, "%s protocol 0x%04x (%s)\n",
+ f == DSIZE ? "Unknown" : "Unexpected", proto,
+ hdlc_Protocol2Nam(proto));
+ bp = m_pullup(proto_Prepend(bp, proto, 0, 0));
+ lcp_SendProtoRej(&l->lcp, MBUF_CTOP(bp), bp->m_len);
+ if (p) {
+ p->hdlc.lqm.ifInDiscards++;
+ p->hdlc.stats.unknownproto++;
+ }
+ m_freem(bp);
+ }
+}
+
+int
+link_ShowLayers(struct cmdargs const *arg)
+{
+ struct link *l = command_ChooseLink(arg);
+ int layer;
+
+ for (layer = l->nlayers; layer; layer--)
+ prompt_Printf(arg->prompt, "%s%s", layer == l->nlayers ? "" : ", ",
+ l->layer[layer - 1]->name);
+ if (l->nlayers)
+ prompt_Printf(arg->prompt, "\n");
+
+ return 0;
+}
diff --git a/usr.sbin/ppp/link.h b/usr.sbin/ppp/link.h
new file mode 100644
index 0000000..822474b
--- /dev/null
+++ b/usr.sbin/ppp/link.h
@@ -0,0 +1,81 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+
+#define PHYSICAL_LINK 1
+#define LOGICAL_LINK 2
+
+#define NPROTOSTAT 13
+
+struct bundle;
+struct prompt;
+struct cmdargs;
+
+struct link {
+ int type; /* _LINK type */
+ const char *name; /* Points to datalink::name */
+ int len; /* full size of parent struct */
+ struct {
+ unsigned gather : 1; /* Gather statistics ourself ? */
+ struct pppThroughput total; /* Link throughput statistics */
+ struct pppThroughput *parent; /* MP link throughput statistics */
+ } stats;
+ struct mqueue Queue[2]; /* Our output queue of mbufs */
+
+ u_long proto_in[NPROTOSTAT]; /* outgoing protocol stats */
+ u_long proto_out[NPROTOSTAT]; /* incoming protocol stats */
+
+ struct lcp lcp; /* Our line control FSM */
+ struct ccp ccp; /* Our compression FSM */
+
+ struct layer const *layer[LAYER_MAX]; /* i/o layers */
+ int nlayers;
+};
+
+#define LINK_QUEUES(link) (sizeof (link)->Queue / sizeof (link)->Queue[0])
+#define LINK_HIGHQ(link) ((link)->Queue + LINK_QUEUES(link) - 1)
+
+extern void link_SequenceQueue(struct link *);
+extern void link_DeleteQueue(struct link *);
+extern size_t link_QueueLen(struct link *);
+extern size_t link_QueueBytes(struct link *);
+extern void link_PendingLowPriorityData(struct link *, size_t *, size_t *);
+extern struct mbuf *link_Dequeue(struct link *);
+
+extern void link_PushPacket(struct link *, struct mbuf *, struct bundle *,
+ int, u_short);
+extern void link_PullPacket(struct link *, char *, size_t, struct bundle *);
+extern int link_Stack(struct link *, struct layer *);
+extern void link_EmptyStack(struct link *);
+
+#define PROTO_IN 1 /* third arg to link_ProtocolRecord */
+#define PROTO_OUT 2
+extern void link_ProtocolRecord(struct link *, u_short, int);
+extern void link_ReportProtocolStatus(struct link *, struct prompt *);
+extern int link_ShowLayers(struct cmdargs const *);
diff --git a/usr.sbin/ppp/log.c b/usr.sbin/ppp/log.c
new file mode 100644
index 0000000..c90720b
--- /dev/null
+++ b/usr.sbin/ppp/log.c
@@ -0,0 +1,521 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <termios.h>
+
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "descriptor.h"
+#include "prompt.h"
+
+static const char *const LogNames[] = {
+ "Async",
+ "CBCP",
+ "CCP",
+ "Chat",
+ "Command",
+ "Connect",
+ "Debug",
+ "DNS",
+ "Filter", /* Log discarded packets */
+ "HDLC",
+ "ID0",
+ "IPCP",
+ "IPV6CP",
+ "LCP",
+ "LQM",
+ "Phase",
+ "Physical",
+ "Radius",
+ "Sync",
+ "TCP/IP",
+ "Timer",
+ "Tun",
+ "Warning",
+ "Error",
+ "Alert"
+};
+
+#define MSK(n) (1<<((n)-1))
+
+static u_long LogMask = MSK(LogPHASE);
+static u_long LogMaskLocal = MSK(LogERROR) | MSK(LogALERT) | MSK(LogWARN);
+static int LogTunno = -1;
+static struct prompt *promptlist; /* Where to log local stuff */
+struct prompt *log_PromptContext;
+int log_PromptListChanged;
+
+struct prompt *
+log_PromptList()
+{
+ return promptlist;
+}
+
+void
+log_RegisterPrompt(struct prompt *prompt)
+{
+ prompt->next = promptlist;
+ promptlist = prompt;
+ prompt->active = 1;
+ log_DiscardAllLocal(&prompt->logmask);
+}
+
+void
+log_ActivatePrompt(struct prompt *prompt)
+{
+ prompt->active = 1;
+ LogMaskLocal |= prompt->logmask;
+}
+
+static void
+LogSetMaskLocal(void)
+{
+ struct prompt *p;
+
+ LogMaskLocal = MSK(LogERROR) | MSK(LogALERT) | MSK(LogWARN);
+ for (p = promptlist; p; p = p->next)
+ LogMaskLocal |= p->logmask;
+}
+
+void
+log_DeactivatePrompt(struct prompt *prompt)
+{
+ if (prompt->active) {
+ prompt->active = 0;
+ LogSetMaskLocal();
+ }
+}
+
+void
+log_UnRegisterPrompt(struct prompt *prompt)
+{
+ if (prompt) {
+ struct prompt **p;
+
+ for (p = &promptlist; *p; p = &(*p)->next)
+ if (*p == prompt) {
+ *p = prompt->next;
+ prompt->next = NULL;
+ break;
+ }
+ LogSetMaskLocal();
+ log_PromptListChanged++;
+ }
+}
+
+void
+log_DestroyPrompts(struct server *s)
+{
+ struct prompt *p, *pn, *pl;
+
+ p = promptlist;
+ pl = NULL;
+ while (p) {
+ pn = p->next;
+ if (s && p->owner == s) {
+ if (pl)
+ pl->next = p->next;
+ else
+ promptlist = p->next;
+ p->next = NULL;
+ prompt_Destroy(p, 1);
+ } else
+ pl = p;
+ p = pn;
+ }
+}
+
+void
+log_DisplayPrompts()
+{
+ struct prompt *p;
+
+ for (p = promptlist; p; p = p->next)
+ prompt_Required(p);
+}
+
+void
+log_WritePrompts(struct datalink *dl, const char *fmt,...)
+{
+ va_list ap;
+ struct prompt *p;
+
+ va_start(ap, fmt);
+ for (p = promptlist; p; p = p->next)
+ if (prompt_IsTermMode(p, dl))
+ prompt_vPrintf(p, fmt, ap);
+ va_end(ap);
+}
+
+void
+log_SetTtyCommandMode(struct datalink *dl)
+{
+ struct prompt *p;
+
+ for (p = promptlist; p; p = p->next)
+ if (prompt_IsTermMode(p, dl))
+ prompt_TtyCommandMode(p);
+}
+
+static int
+syslogLevel(int lev)
+{
+ switch (lev) {
+ case LogLOG:
+ return LOG_INFO;
+ case LogDEBUG:
+ case LogTIMER:
+ return LOG_DEBUG;
+ case LogWARN:
+ return LOG_WARNING;
+ case LogERROR:
+ return LOG_ERR;
+ case LogALERT:
+ return LOG_ALERT;
+ }
+ return lev >= LogMIN && lev <= LogMAX ? LOG_INFO : 0;
+}
+
+const char *
+log_Name(int id)
+{
+ if (id == LogLOG)
+ return "LOG";
+ return id < LogMIN || id > LogMAX ? "Unknown" : LogNames[id - 1];
+}
+
+void
+log_Keep(int id)
+{
+ if (id >= LogMIN && id <= LogMAXCONF)
+ LogMask |= MSK(id);
+}
+
+void
+log_KeepLocal(int id, u_long *mask)
+{
+ if (id >= LogMIN && id <= LogMAXCONF) {
+ LogMaskLocal |= MSK(id);
+ *mask |= MSK(id);
+ }
+}
+
+void
+log_Discard(int id)
+{
+ if (id >= LogMIN && id <= LogMAXCONF)
+ LogMask &= ~MSK(id);
+}
+
+void
+log_DiscardLocal(int id, u_long *mask)
+{
+ if (id >= LogMIN && id <= LogMAXCONF) {
+ *mask &= ~MSK(id);
+ LogSetMaskLocal();
+ }
+}
+
+void
+log_DiscardAll()
+{
+ LogMask = 0;
+}
+
+void
+log_DiscardAllLocal(u_long *mask)
+{
+ *mask = MSK(LogERROR) | MSK(LogALERT) | MSK(LogWARN);
+ LogSetMaskLocal();
+}
+
+int
+log_IsKept(int id)
+{
+ if (id == LogLOG)
+ return LOG_KEPT_SYSLOG;
+ if (id < LogMIN || id > LogMAX)
+ return 0;
+ if (id > LogMAXCONF)
+ return LOG_KEPT_LOCAL | LOG_KEPT_SYSLOG;
+
+ return ((LogMaskLocal & MSK(id)) ? LOG_KEPT_LOCAL : 0) |
+ ((LogMask & MSK(id)) ? LOG_KEPT_SYSLOG : 0);
+}
+
+int
+log_IsKeptLocal(int id, u_long mask)
+{
+ if (id < LogMIN || id > LogMAX)
+ return 0;
+ if (id > LogMAXCONF)
+ return LOG_KEPT_LOCAL | LOG_KEPT_SYSLOG;
+
+ return ((mask & MSK(id)) ? LOG_KEPT_LOCAL : 0) |
+ ((LogMask & MSK(id)) ? LOG_KEPT_SYSLOG : 0);
+}
+
+void
+log_Open(const char *Name)
+{
+ openlog(Name, LOG_PID, LOG_DAEMON);
+}
+
+void
+log_SetTun(int tunno)
+{
+ LogTunno = tunno;
+}
+
+void
+log_Close()
+{
+ closelog();
+ LogTunno = -1;
+}
+
+void
+log_Printf(int lev, const char *fmt,...)
+{
+ va_list ap;
+ struct prompt *prompt;
+
+ if (log_IsKept(lev)) {
+ char nfmt[200];
+
+ va_start(ap, fmt);
+ if (promptlist && (log_IsKept(lev) & LOG_KEPT_LOCAL)) {
+ if ((log_IsKept(LogTUN) & LOG_KEPT_LOCAL) && LogTunno != -1)
+ snprintf(nfmt, sizeof nfmt, "%s%d: %s: %s", TUN_NAME,
+ LogTunno, log_Name(lev), fmt);
+ else
+ snprintf(nfmt, sizeof nfmt, "%s: %s", log_Name(lev), fmt);
+
+ if (log_PromptContext && lev == LogWARN)
+ /* Warnings just go to the current prompt */
+ prompt_vPrintf(log_PromptContext, nfmt, ap);
+ else for (prompt = promptlist; prompt; prompt = prompt->next)
+ if (lev > LogMAXCONF || (prompt->logmask & MSK(lev)))
+ prompt_vPrintf(prompt, nfmt, ap);
+ }
+ va_end(ap);
+
+ va_start(ap, fmt);
+ if ((log_IsKept(lev) & LOG_KEPT_SYSLOG) &&
+ (lev != LogWARN || !log_PromptContext)) {
+ if ((log_IsKept(LogTUN) & LOG_KEPT_SYSLOG) && LogTunno != -1)
+ snprintf(nfmt, sizeof nfmt, "%s%d: %s: %s", TUN_NAME,
+ LogTunno, log_Name(lev), fmt);
+ else
+ snprintf(nfmt, sizeof nfmt, "%s: %s", log_Name(lev), fmt);
+ vsyslog(syslogLevel(lev), nfmt, ap);
+ }
+ va_end(ap);
+ }
+}
+
+void
+log_DumpBp(int lev, const char *hdr, const struct mbuf *bp)
+{
+ if (log_IsKept(lev)) {
+ char buf[68];
+ char *b, *c;
+ const u_char *ptr;
+ int f;
+
+ if (hdr && *hdr)
+ log_Printf(lev, "%s\n", hdr);
+
+ b = buf;
+ c = b + 50;
+ do {
+ f = bp->m_len;
+ ptr = CONST_MBUF_CTOP(bp);
+ while (f--) {
+ sprintf(b, " %02x", (int) *ptr);
+ *c++ = isprint(*ptr) ? *ptr : '.';
+ ptr++;
+ b += 3;
+ if (b == buf + 48) {
+ memset(b, ' ', 2);
+ *c = '\0';
+ log_Printf(lev, "%s\n", buf);
+ b = buf;
+ c = b + 50;
+ }
+ }
+ } while ((bp = bp->m_next) != NULL);
+
+ if (b > buf) {
+ memset(b, ' ', 50 - (b - buf));
+ *c = '\0';
+ log_Printf(lev, "%s\n", buf);
+ }
+ }
+}
+
+void
+log_DumpBuff(int lev, const char *hdr, const u_char *ptr, int n)
+{
+ if (log_IsKept(lev)) {
+ char buf[68];
+ char *b, *c;
+
+ if (hdr && *hdr)
+ log_Printf(lev, "%s\n", hdr);
+ while (n > 0) {
+ b = buf;
+ c = b + 50;
+ for (b = buf; b != buf + 48 && n--; b += 3, ptr++) {
+ sprintf(b, " %02x", (int) *ptr);
+ *c++ = isprint(*ptr) ? *ptr : '.';
+ }
+ memset(b, ' ', 50 - (b - buf));
+ *c = '\0';
+ log_Printf(lev, "%s\n", buf);
+ }
+ }
+}
+
+int
+log_ShowLevel(struct cmdargs const *arg)
+{
+ int i;
+
+ prompt_Printf(arg->prompt, "Log: ");
+ for (i = LogMIN; i <= LogMAX; i++)
+ if (log_IsKept(i) & LOG_KEPT_SYSLOG)
+ prompt_Printf(arg->prompt, " %s", log_Name(i));
+
+ prompt_Printf(arg->prompt, "\nLocal:");
+ for (i = LogMIN; i <= LogMAX; i++)
+ if (log_IsKeptLocal(i, arg->prompt->logmask) & LOG_KEPT_LOCAL)
+ prompt_Printf(arg->prompt, " %s", log_Name(i));
+
+ prompt_Printf(arg->prompt, "\n");
+
+ return 0;
+}
+
+int
+log_SetLevel(struct cmdargs const *arg)
+{
+ int i, res, argc, local;
+ char const *const *argv, *argp;
+
+ argc = arg->argc - arg->argn;
+ argv = arg->argv + arg->argn;
+ res = 0;
+
+ if (argc == 0 || strcasecmp(argv[0], "local"))
+ local = 0;
+ else {
+ if (arg->prompt == NULL) {
+ log_Printf(LogWARN, "set log local: Only available on the"
+ " command line\n");
+ return 1;
+ }
+ argc--;
+ argv++;
+ local = 1;
+ }
+
+ if (argc == 0 || (argv[0][0] != '+' && argv[0][0] != '-')) {
+ if (local)
+ log_DiscardAllLocal(&arg->prompt->logmask);
+ else
+ log_DiscardAll();
+ }
+
+ while (argc--) {
+ argp = **argv == '+' || **argv == '-' ? *argv + 1 : *argv;
+ /* Special case 'all' */
+ if (strcasecmp(argp, "all") == 0) {
+ if (**argv == '-') {
+ if (local)
+ for (i = LogMIN; i <= LogMAX; i++)
+ log_DiscardLocal(i, &arg->prompt->logmask);
+ else
+ for (i = LogMIN; i <= LogMAX; i++)
+ log_Discard(i);
+ } else if (local)
+ for (i = LogMIN; i <= LogMAX; i++)
+ log_KeepLocal(i, &arg->prompt->logmask);
+ else
+ for (i = LogMIN; i <= LogMAX; i++)
+ log_Keep(i);
+ argv++;
+ continue;
+ }
+ for (i = LogMIN; i <= LogMAX; i++)
+ if (strcasecmp(argp, log_Name(i)) == 0) {
+ if (**argv == '-') {
+ if (local)
+ log_DiscardLocal(i, &arg->prompt->logmask);
+ else
+ log_Discard(i);
+ } else if (local)
+ log_KeepLocal(i, &arg->prompt->logmask);
+ else
+ log_Keep(i);
+ break;
+ }
+ if (i > LogMAX) {
+ log_Printf(LogWARN, "%s: Invalid log value\n", argp);
+ res = -1;
+ }
+ argv++;
+ }
+ return res;
+}
+
+int
+log_ShowWho(struct cmdargs const *arg)
+{
+ struct prompt *p;
+
+ for (p = promptlist; p; p = p->next) {
+ prompt_Printf(arg->prompt, "%s (%s)", p->src.type, p->src.from);
+ if (p == arg->prompt)
+ prompt_Printf(arg->prompt, " *");
+ if (!p->active)
+ prompt_Printf(arg->prompt, " ^Z");
+ prompt_Printf(arg->prompt, "\n");
+ }
+
+ return 0;
+}
diff --git a/usr.sbin/ppp/log.h b/usr.sbin/ppp/log.h
new file mode 100644
index 0000000..0da4b8c
--- /dev/null
+++ b/usr.sbin/ppp/log.h
@@ -0,0 +1,105 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define LogLOG (0)
+#define LogMIN (1)
+#define LogASYNC (1) /* syslog(LOG_INFO, ....) */
+#define LogCBCP (2)
+#define LogCCP (3)
+#define LogCHAT (4)
+#define LogCOMMAND (5)
+#define LogCONNECT (6)
+#define LogDEBUG (7) /* syslog(LOG_DEBUG, ....) */
+#define LogDNS (8)
+#define LogFILTER (9)
+#define LogHDLC (10)
+#define LogID0 (11)
+#define LogIPCP (12)
+#define LogIPV6CP (13)
+#define LogLCP (14)
+#define LogLQM (15)
+#define LogPHASE (16)
+#define LogPHYSICAL (17) /* syslog(LOG_INFO, ....) */
+#define LogRADIUS (18) /* syslog(LOG_INFO, ....) */
+#define LogSYNC (19) /* syslog(LOG_INFO, ....) */
+#define LogTCPIP (20)
+#define LogTIMER (21) /* syslog(LOG_DEBUG, ....) */
+#define LogTUN (22) /* If set, tun%d is output with each message */
+#define LogWARN (23) /* Sent to VarTerm else syslog(LOG_WARNING, ) */
+#define LogERROR (24) /* syslog(LOG_ERR, ....), + sent to VarTerm */
+#define LogALERT (25) /* syslog(LOG_ALERT, ....) */
+
+#define LogMAXCONF (22)
+#define LogMAX (25)
+
+struct mbuf;
+struct cmdargs;
+struct prompt;
+struct server;
+struct datalink;
+
+/* The first int arg for all of the following is one of the above values */
+extern const char *log_Name(int);
+extern void log_Keep(int);
+extern void log_KeepLocal(int, u_long *);
+extern void log_Discard(int);
+extern void log_DiscardLocal(int, u_long *);
+extern void log_DiscardAll(void);
+extern void log_DiscardAllLocal(u_long *);
+#define LOG_KEPT_SYSLOG (1) /* Results of log_IsKept() */
+#define LOG_KEPT_LOCAL (2) /* Results of log_IsKept() */
+extern int log_IsKept(int);
+extern int log_IsKeptLocal(int, u_long);
+extern void log_Open(const char *);
+extern void log_SetTun(int);
+extern void log_Close(void);
+#ifdef __GNUC__
+extern void log_Printf(int, const char *,...)
+ __attribute__ ((format (printf, 2, 3)));
+extern void log_WritePrompts(struct datalink *, const char *, ...)
+ __attribute__ ((format (printf, 2, 3)));
+#else
+extern void log_Printf(int, const char *,...);
+extern void log_WritePrompts(struct datalink *, const char *, ...);
+#endif
+extern void log_DumpBp(int, const char *, const struct mbuf *);
+extern void log_DumpBuff(int, const char *, const u_char *, int);
+extern int log_ShowLevel(struct cmdargs const *);
+extern int log_SetLevel(struct cmdargs const *);
+extern int log_ShowWho(struct cmdargs const *);
+
+extern struct prompt *log_PromptContext;
+extern int log_PromptListChanged;
+extern void log_RegisterPrompt(struct prompt *);
+extern void log_UnRegisterPrompt(struct prompt *);
+extern void log_DestroyPrompts(struct server *);
+extern void log_DisplayPrompts(void);
+extern void log_ActivatePrompt(struct prompt *);
+extern void log_DeactivatePrompt(struct prompt *);
+extern void log_SetTtyCommandMode(struct datalink *);
+extern struct prompt *log_PromptList(void);
diff --git a/usr.sbin/ppp/lqr.c b/usr.sbin/ppp/lqr.c
new file mode 100644
index 0000000..cb04741
--- /dev/null
+++ b/usr.sbin/ppp/lqr.c
@@ -0,0 +1,532 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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>
+
+#ifdef __FreeBSD__
+#include <netinet/in.h>
+#endif
+#include <sys/un.h>
+
+#include <string.h>
+#include <termios.h>
+
+#include "layer.h"
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "acf.h"
+#include "proto.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "async.h"
+#include "throughput.h"
+#include "ccp.h"
+#include "link.h"
+#include "descriptor.h"
+#include "physical.h"
+#include "mp.h"
+#include "chat.h"
+#include "auth.h"
+#include "chap.h"
+#include "command.h"
+#include "cbcp.h"
+#include "datalink.h"
+
+struct echolqr {
+ u_int32_t magic;
+ u_int32_t signature;
+ u_int32_t sequence;
+};
+
+#define SIGNATURE 0x594e4f54
+
+static void
+SendEchoReq(struct lcp *lcp)
+{
+ struct hdlc *hdlc = &link2physical(lcp->fsm.link)->hdlc;
+ struct echolqr echo;
+
+ echo.magic = htonl(lcp->want_magic);
+ echo.signature = htonl(SIGNATURE);
+ echo.sequence = htonl(hdlc->lqm.echo.seq_sent);
+ fsm_Output(&lcp->fsm, CODE_ECHOREQ, hdlc->lqm.echo.seq_sent++,
+ (u_char *)&echo, sizeof echo, MB_ECHOOUT);
+}
+
+struct mbuf *
+lqr_RecvEcho(struct fsm *fp, struct mbuf *bp)
+{
+ struct hdlc *hdlc = &link2physical(fp->link)->hdlc;
+ struct lcp *lcp = fsm2lcp(fp);
+ struct echolqr lqr;
+
+ if (m_length(bp) >= sizeof lqr) {
+ m_freem(mbuf_Read(bp, &lqr, sizeof lqr));
+ bp = NULL;
+ lqr.magic = ntohl(lqr.magic);
+ lqr.signature = ntohl(lqr.signature);
+ lqr.sequence = ntohl(lqr.sequence);
+
+ /* Tolerate echo replies with either magic number */
+ if (lqr.magic != 0 && lqr.magic != lcp->his_magic &&
+ lqr.magic != lcp->want_magic) {
+ log_Printf(LogWARN, "%s: lqr_RecvEcho: Bad magic: expected 0x%08x,"
+ " got 0x%08x\n", fp->link->name, lcp->his_magic, lqr.magic);
+ /*
+ * XXX: We should send a terminate request. But poor implementations may
+ * die as a result.
+ */
+ }
+ if (lqr.signature == SIGNATURE) {
+ /* careful not to update lqm.echo.seq_recv with older values */
+ if ((hdlc->lqm.echo.seq_recv > (u_int32_t)0 - 5 && lqr.sequence < 5) ||
+ (hdlc->lqm.echo.seq_recv <= (u_int32_t)0 - 5 &&
+ lqr.sequence > hdlc->lqm.echo.seq_recv))
+ hdlc->lqm.echo.seq_recv = lqr.sequence;
+ } else
+ log_Printf(LogWARN, "lqr_RecvEcho: Got sig 0x%08lx, not 0x%08lx !\n",
+ (u_long)lqr.signature, (u_long)SIGNATURE);
+ } else
+ log_Printf(LogWARN, "lqr_RecvEcho: Got packet size %zd, expecting %ld !\n",
+ m_length(bp), (long)sizeof(struct echolqr));
+ return bp;
+}
+
+void
+lqr_ChangeOrder(struct lqrdata *src, struct lqrdata *dst)
+{
+ u_int32_t *sp, *dp;
+ unsigned n;
+
+ sp = (u_int32_t *) src;
+ dp = (u_int32_t *) dst;
+ for (n = 0; n < sizeof(struct lqrdata) / sizeof(u_int32_t); n++, sp++, dp++)
+ *dp = ntohl(*sp);
+}
+
+static void
+SendLqrData(struct lcp *lcp)
+{
+ struct mbuf *bp;
+ int extra;
+
+ extra = proto_WrapperOctets(lcp, PROTO_LQR) +
+ acf_WrapperOctets(lcp, PROTO_LQR);
+ bp = m_get(sizeof(struct lqrdata) + extra, MB_LQROUT);
+ bp->m_len -= extra;
+ bp->m_offset += extra;
+
+ /*
+ * Send on the highest priority queue. We send garbage - the real data
+ * is written by lqr_LayerPush() where we know how to fill in all the
+ * fields. Note, lqr_LayerPush() ``knows'' that we're pushing onto the
+ * highest priority queue, and factors out packet & octet values from
+ * other queues!
+ */
+ link_PushPacket(lcp->fsm.link, bp, lcp->fsm.bundle,
+ LINK_QUEUES(lcp->fsm.link) - 1, PROTO_LQR);
+}
+
+static void
+SendLqrReport(void *v)
+{
+ struct lcp *lcp = (struct lcp *)v;
+ struct physical *p = link2physical(lcp->fsm.link);
+
+ timer_Stop(&p->hdlc.lqm.timer);
+
+ if (p->hdlc.lqm.method & LQM_LQR) {
+ if (p->hdlc.lqm.lqr.resent > 5) {
+ /* XXX: Should implement LQM strategy */
+ log_Printf(LogPHASE, "%s: ** Too many LQR packets lost **\n",
+ lcp->fsm.link->name);
+ log_Printf(LogLQM, "%s: Too many LQR packets lost\n",
+ lcp->fsm.link->name);
+ p->hdlc.lqm.method = 0;
+ datalink_Down(p->dl, CLOSE_NORMAL);
+ } else {
+ SendLqrData(lcp);
+ p->hdlc.lqm.lqr.resent++;
+ }
+ } else if (p->hdlc.lqm.method & LQM_ECHO) {
+ if ((p->hdlc.lqm.echo.seq_sent > 5 &&
+ p->hdlc.lqm.echo.seq_sent - 5 > p->hdlc.lqm.echo.seq_recv) ||
+ (p->hdlc.lqm.echo.seq_sent <= 5 &&
+ p->hdlc.lqm.echo.seq_sent > p->hdlc.lqm.echo.seq_recv + 5)) {
+ log_Printf(LogPHASE, "%s: ** Too many LCP ECHO packets lost **\n",
+ lcp->fsm.link->name);
+ log_Printf(LogLQM, "%s: Too many LCP ECHO packets lost\n",
+ lcp->fsm.link->name);
+ p->hdlc.lqm.method = 0;
+ datalink_Down(p->dl, CLOSE_NORMAL);
+ } else
+ SendEchoReq(lcp);
+ }
+ if (p->hdlc.lqm.method && p->hdlc.lqm.timer.load)
+ timer_Start(&p->hdlc.lqm.timer);
+}
+
+struct mbuf *
+lqr_Input(struct bundle *bundle __unused, struct link *l, struct mbuf *bp)
+{
+ struct physical *p = link2physical(l);
+ struct lcp *lcp = p->hdlc.lqm.owner;
+ int len;
+
+ if (p == NULL) {
+ log_Printf(LogERROR, "lqr_Input: Not a physical link - dropped\n");
+ m_freem(bp);
+ return NULL;
+ }
+
+ len = m_length(bp);
+ if (len != sizeof(struct lqrdata))
+ log_Printf(LogWARN, "lqr_Input: Got packet size %d, expecting %ld !\n",
+ len, (long)sizeof(struct lqrdata));
+ else if (!IsAccepted(l->lcp.cfg.lqr) && !(p->hdlc.lqm.method & LQM_LQR)) {
+ bp = m_pullup(proto_Prepend(bp, PROTO_LQR, 0, 0));
+ lcp_SendProtoRej(lcp, MBUF_CTOP(bp), bp->m_len);
+ } else {
+ struct lqrdata *lqr;
+
+ bp = m_pullup(bp);
+ lqr = (struct lqrdata *)MBUF_CTOP(bp);
+ if (ntohl(lqr->MagicNumber) != lcp->his_magic)
+ log_Printf(LogWARN, "lqr_Input: magic 0x%08lx is wrong,"
+ " expecting 0x%08lx\n",
+ (u_long)ntohl(lqr->MagicNumber), (u_long)lcp->his_magic);
+ else {
+ struct lqrdata lastlqr;
+
+ memcpy(&lastlqr, &p->hdlc.lqm.lqr.peer, sizeof lastlqr);
+ lqr_ChangeOrder(lqr, &p->hdlc.lqm.lqr.peer);
+ lqr_Dump(l->name, "Input", &p->hdlc.lqm.lqr.peer);
+ /* we have received an LQR from our peer */
+ p->hdlc.lqm.lqr.resent = 0;
+
+ /* Snapshot our state when the LQR packet was received */
+ memcpy(&p->hdlc.lqm.lqr.prevSave, &p->hdlc.lqm.lqr.Save,
+ sizeof p->hdlc.lqm.lqr.prevSave);
+ p->hdlc.lqm.lqr.Save.InLQRs = ++p->hdlc.lqm.lqr.InLQRs;
+ p->hdlc.lqm.lqr.Save.InPackets = p->hdlc.lqm.ifInUniPackets;
+ p->hdlc.lqm.lqr.Save.InDiscards = p->hdlc.lqm.ifInDiscards;
+ p->hdlc.lqm.lqr.Save.InErrors = p->hdlc.lqm.ifInErrors;
+ p->hdlc.lqm.lqr.Save.InOctets = p->hdlc.lqm.lqr.InGoodOctets;
+
+ lqr_Analyse(&p->hdlc, &lastlqr, &p->hdlc.lqm.lqr.peer);
+
+ /*
+ * Generate an LQR response if we're not running an LQR timer OR
+ * two successive LQR's PeerInLQRs are the same.
+ */
+ if (p->hdlc.lqm.timer.load == 0 || !(p->hdlc.lqm.method & LQM_LQR) ||
+ (lastlqr.PeerInLQRs &&
+ lastlqr.PeerInLQRs == p->hdlc.lqm.lqr.peer.PeerInLQRs))
+ SendLqrData(lcp);
+ }
+ }
+ m_freem(bp);
+ return NULL;
+}
+
+/*
+ * When LCP is reached to opened state, We'll start LQM activity.
+ */
+static void
+lqr_Setup(struct lcp *lcp)
+{
+ struct physical *physical = link2physical(lcp->fsm.link);
+ int period;
+
+ physical->hdlc.lqm.lqr.resent = 0;
+ physical->hdlc.lqm.echo.seq_sent = 0;
+ physical->hdlc.lqm.echo.seq_recv = 0;
+ memset(&physical->hdlc.lqm.lqr.peer, '\0',
+ sizeof physical->hdlc.lqm.lqr.peer);
+
+ physical->hdlc.lqm.method = lcp->cfg.echo ? LQM_ECHO : 0;
+ if (IsEnabled(lcp->cfg.lqr) && !REJECTED(lcp, TY_QUALPROTO))
+ physical->hdlc.lqm.method |= LQM_LQR;
+ timer_Stop(&physical->hdlc.lqm.timer);
+
+ physical->hdlc.lqm.lqr.peer_timeout = lcp->his_lqrperiod;
+ if (lcp->his_lqrperiod)
+ log_Printf(LogLQM, "%s: Expecting LQR every %d.%02d secs\n",
+ physical->link.name, lcp->his_lqrperiod / 100,
+ lcp->his_lqrperiod % 100);
+
+ period = lcp->want_lqrperiod ?
+ lcp->want_lqrperiod : lcp->cfg.lqrperiod * 100;
+ physical->hdlc.lqm.timer.func = SendLqrReport;
+ physical->hdlc.lqm.timer.name = "lqm";
+ physical->hdlc.lqm.timer.arg = lcp;
+
+ if (lcp->want_lqrperiod || physical->hdlc.lqm.method & LQM_ECHO) {
+ log_Printf(LogLQM, "%s: Will send %s every %d.%02d secs\n",
+ physical->link.name, lcp->want_lqrperiod ? "LQR" : "LCP ECHO",
+ period / 100, period % 100);
+ physical->hdlc.lqm.timer.load = period * SECTICKS / 100;
+ } else {
+ physical->hdlc.lqm.timer.load = 0;
+ if (!lcp->his_lqrperiod)
+ log_Printf(LogLQM, "%s: LQR/LCP ECHO not negotiated\n",
+ physical->link.name);
+ }
+}
+
+void
+lqr_Start(struct lcp *lcp)
+{
+ struct physical *p = link2physical(lcp->fsm.link);
+
+ lqr_Setup(lcp);
+ if (p->hdlc.lqm.timer.load)
+ SendLqrReport(lcp);
+}
+
+void
+lqr_reStart(struct lcp *lcp)
+{
+ struct physical *p = link2physical(lcp->fsm.link);
+
+ lqr_Setup(lcp);
+ if (p->hdlc.lqm.timer.load)
+ timer_Start(&p->hdlc.lqm.timer);
+}
+
+void
+lqr_StopTimer(struct physical *physical)
+{
+ timer_Stop(&physical->hdlc.lqm.timer);
+}
+
+void
+lqr_Stop(struct physical *physical, int method)
+{
+ if (method == LQM_LQR)
+ log_Printf(LogLQM, "%s: Stop sending LQR, Use LCP ECHO instead.\n",
+ physical->link.name);
+ if (method == LQM_ECHO)
+ log_Printf(LogLQM, "%s: Stop sending LCP ECHO.\n",
+ physical->link.name);
+ physical->hdlc.lqm.method &= ~method;
+ if (physical->hdlc.lqm.method)
+ SendLqrReport(physical->hdlc.lqm.owner);
+ else
+ timer_Stop(&physical->hdlc.lqm.timer);
+}
+
+void
+lqr_Dump(const char *link, const char *message, const struct lqrdata *lqr)
+{
+ if (log_IsKept(LogLQM)) {
+ log_Printf(LogLQM, "%s: %s:\n", link, message);
+ log_Printf(LogLQM, " Magic: %08x LastOutLQRs: %08x\n",
+ lqr->MagicNumber, lqr->LastOutLQRs);
+ log_Printf(LogLQM, " LastOutPackets: %08x LastOutOctets: %08x\n",
+ lqr->LastOutPackets, lqr->LastOutOctets);
+ log_Printf(LogLQM, " PeerInLQRs: %08x PeerInPackets: %08x\n",
+ lqr->PeerInLQRs, lqr->PeerInPackets);
+ log_Printf(LogLQM, " PeerInDiscards: %08x PeerInErrors: %08x\n",
+ lqr->PeerInDiscards, lqr->PeerInErrors);
+ log_Printf(LogLQM, " PeerInOctets: %08x PeerOutLQRs: %08x\n",
+ lqr->PeerInOctets, lqr->PeerOutLQRs);
+ log_Printf(LogLQM, " PeerOutPackets: %08x PeerOutOctets: %08x\n",
+ lqr->PeerOutPackets, lqr->PeerOutOctets);
+ }
+}
+
+void
+lqr_Analyse(const struct hdlc *hdlc, const struct lqrdata *oldlqr,
+ const struct lqrdata *newlqr)
+{
+ u_int32_t LQRs, transitLQRs, pkts, octets, disc, err;
+
+ if (!newlqr->PeerInLQRs) /* No analysis possible yet! */
+ return;
+
+ log_Printf(LogLQM, "Analysis:\n");
+
+ LQRs = (newlqr->LastOutLQRs - oldlqr->LastOutLQRs) -
+ (newlqr->PeerInLQRs - oldlqr->PeerInLQRs);
+ transitLQRs = hdlc->lqm.lqr.OutLQRs - newlqr->LastOutLQRs;
+ pkts = (newlqr->LastOutPackets - oldlqr->LastOutPackets) -
+ (newlqr->PeerInPackets - oldlqr->PeerInPackets);
+ octets = (newlqr->LastOutOctets - oldlqr->LastOutOctets) -
+ (newlqr->PeerInOctets - oldlqr->PeerInOctets);
+ log_Printf(LogLQM, " Outbound lossage: %d LQR%s (%d en route), %d packet%s,"
+ " %d octet%s\n", (int)LQRs, LQRs == 1 ? "" : "s", (int)transitLQRs,
+ (int)pkts, pkts == 1 ? "" : "s",
+ (int)octets, octets == 1 ? "" : "s");
+
+ pkts = (newlqr->PeerOutPackets - oldlqr->PeerOutPackets) -
+ (hdlc->lqm.lqr.Save.InPackets - hdlc->lqm.lqr.prevSave.InPackets);
+ octets = (newlqr->PeerOutOctets - oldlqr->PeerOutOctets) -
+ (hdlc->lqm.lqr.Save.InOctets - hdlc->lqm.lqr.prevSave.InOctets);
+ log_Printf(LogLQM, " Inbound lossage: %d packet%s, %d octet%s\n",
+ (int)pkts, pkts == 1 ? "" : "s",
+ (int)octets, octets == 1 ? "" : "s");
+
+ disc = newlqr->PeerInDiscards - oldlqr->PeerInDiscards;
+ err = newlqr->PeerInErrors - oldlqr->PeerInErrors;
+ if (disc && err)
+ log_Printf(LogLQM, " Likely due to both peer congestion"
+ " and physical errors\n");
+ else if (disc)
+ log_Printf(LogLQM, " Likely due to peer congestion\n");
+ else if (err)
+ log_Printf(LogLQM, " Likely due to physical errors\n");
+ else if (pkts)
+ log_Printf(LogLQM, " Likely due to transport "
+ "congestion\n");
+}
+
+static struct mbuf *
+lqr_LayerPush(struct bundle *b __unused, struct link *l, struct mbuf *bp,
+ int pri __unused, u_short *proto)
+{
+ struct physical *p = link2physical(l);
+ int len, layer, extra_async_bytes;
+
+ if (!p) {
+ /* Oops - can't happen :-] */
+ m_freem(bp);
+ return NULL;
+ }
+
+ bp = m_pullup(bp);
+ len = m_length(bp);
+
+ /*-
+ * From rfc1989:
+ *
+ * All octets which are included in the FCS calculation MUST be counted,
+ * including the packet header, the information field, and any padding.
+ * The FCS octets MUST also be counted, and one flag octet per frame
+ * MUST be counted. All other octets (such as additional flag
+ * sequences, and escape bits or octets) MUST NOT be counted.
+ *
+ * As we're stacked higher than the HDLC layer (otherwise HDLC wouldn't be
+ * able to calculate the FCS), we must not forget about these additional
+ * bytes when we're asynchronous.
+ *
+ * We're also expecting to be stacked *before* the likes of the proto and
+ * acf layers (to avoid alignment issues), so deal with this too.
+ */
+
+ extra_async_bytes = 0;
+ p->hdlc.lqm.ifOutUniPackets++;
+ p->hdlc.lqm.ifOutOctets += len + 1; /* plus 1 flag octet! */
+ for (layer = 0; layer < l->nlayers; layer++)
+ switch (l->layer[layer]->type) {
+ case LAYER_ACF:
+ p->hdlc.lqm.ifOutOctets += acf_WrapperOctets(&l->lcp, *proto);
+ break;
+ case LAYER_ASYNC:
+ /* Not included - see rfc1989 */
+ break;
+ case LAYER_HDLC:
+ p->hdlc.lqm.ifOutOctets += hdlc_WrapperOctets();
+ break;
+ case LAYER_LQR:
+ layer = l->nlayers;
+ break;
+ case LAYER_PROTO:
+ p->hdlc.lqm.ifOutOctets += proto_WrapperOctets(&l->lcp, *proto);
+ break;
+ case LAYER_SYNC:
+ /* Nothing to add on */
+ break;
+ default:
+ log_Printf(LogWARN, "Oops, don't know how to do octets for %s layer\n",
+ l->layer[layer]->name);
+ break;
+ }
+
+ if (*proto == PROTO_LQR) {
+ /* Overwrite the entire packet (created in SendLqrData()) */
+ struct lqrdata lqr;
+ size_t pending_pkts, pending_octets;
+
+ p->hdlc.lqm.lqr.OutLQRs++;
+
+ /*
+ * We need to compensate for the fact that we're pushing our data
+ * onto the highest priority queue by factoring out packet & octet
+ * values from other queues!
+ */
+ link_PendingLowPriorityData(l, &pending_pkts, &pending_octets);
+
+ memset(&lqr, '\0', sizeof lqr);
+ lqr.MagicNumber = p->link.lcp.want_magic;
+ lqr.LastOutLQRs = p->hdlc.lqm.lqr.peer.PeerOutLQRs;
+ lqr.LastOutPackets = p->hdlc.lqm.lqr.peer.PeerOutPackets;
+ lqr.LastOutOctets = p->hdlc.lqm.lqr.peer.PeerOutOctets;
+ lqr.PeerInLQRs = p->hdlc.lqm.lqr.Save.InLQRs;
+ lqr.PeerInPackets = p->hdlc.lqm.lqr.Save.InPackets;
+ lqr.PeerInDiscards = p->hdlc.lqm.lqr.Save.InDiscards;
+ lqr.PeerInErrors = p->hdlc.lqm.lqr.Save.InErrors;
+ lqr.PeerInOctets = p->hdlc.lqm.lqr.Save.InOctets;
+ lqr.PeerOutLQRs = p->hdlc.lqm.lqr.OutLQRs;
+ lqr.PeerOutPackets = p->hdlc.lqm.ifOutUniPackets - pending_pkts;
+ /* Don't forget our ``flag'' octets.... */
+ lqr.PeerOutOctets = p->hdlc.lqm.ifOutOctets - pending_octets - pending_pkts;
+ lqr_Dump(l->name, "Output", &lqr);
+ lqr_ChangeOrder(&lqr, (struct lqrdata *)MBUF_CTOP(bp));
+ }
+
+ return bp;
+}
+
+static struct mbuf *
+lqr_LayerPull(struct bundle *b __unused, struct link *l __unused,
+ struct mbuf *bp, u_short *proto)
+{
+ /*
+ * This is the ``Rx'' process from rfc1989, although a part of it is
+ * actually performed by sync_LayerPull() & hdlc_LayerPull() so that
+ * our octet counts are correct.
+ */
+
+ if (*proto == PROTO_LQR)
+ m_settype(bp, MB_LQRIN);
+ return bp;
+}
+
+/*
+ * Statistics for pulled packets are recorded either in hdlc_PullPacket()
+ * or sync_PullPacket()
+ */
+
+struct layer lqrlayer = { LAYER_LQR, "lqr", lqr_LayerPush, lqr_LayerPull };
diff --git a/usr.sbin/ppp/lqr.h b/usr.sbin/ppp/lqr.h
new file mode 100644
index 0000000..13d378f
--- /dev/null
+++ b/usr.sbin/ppp/lqr.h
@@ -0,0 +1,82 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+/*
+ * Structure of LQR packet defined in RFC1989
+ */
+struct lqrdata {
+ u_int32_t MagicNumber;
+ u_int32_t LastOutLQRs; /* most recently received PeerOutLQRs */
+ u_int32_t LastOutPackets; /* most recently received PeerOutPackets */
+ u_int32_t LastOutOctets; /* most recently received PeerOutOctets */
+ u_int32_t PeerInLQRs; /* Peers SaveInLQRs */
+ u_int32_t PeerInPackets; /* Peers SaveInPackets */
+ u_int32_t PeerInDiscards; /* Peers SaveInDiscards */
+ u_int32_t PeerInErrors; /* Peers SaveInErrors */
+ u_int32_t PeerInOctets; /* Peers SaveInOctets */
+ u_int32_t PeerOutLQRs; /* Peers OutLQRs (hdlc.h) */
+ u_int32_t PeerOutPackets; /* Peers OutPackets (hdlc.h) */
+ u_int32_t PeerOutOctets; /* Peers OutOctets (hdlc.h) */
+};
+
+struct lqrsavedata { /* Saved on receipt of an LQR */
+ u_int32_t InLQRs; /* From ifInLQRs */
+ u_int32_t InPackets; /* From ifInPackets */
+ u_int32_t InDiscards; /* From ifInDiscards */
+ u_int32_t InErrors; /* From ifInErrors */
+ u_int32_t InOctets; /* From InGoodOctets ! */
+};
+
+/*
+ * We support LQR and ECHO as LQM method
+ */
+#define LQM_LQR 1
+#define LQM_ECHO 2
+
+struct mbuf;
+struct physical;
+struct lcp;
+struct fsm;
+struct hdlc;
+struct link;
+struct bundle;
+
+extern void lqr_Dump(const char *, const char *, const struct lqrdata *);
+extern void lqr_Analyse(const struct hdlc *, const struct lqrdata *,
+ const struct lqrdata *);
+extern void lqr_ChangeOrder(struct lqrdata *, struct lqrdata *);
+extern void lqr_Start(struct lcp *);
+extern void lqr_reStart(struct lcp *);
+extern void lqr_Stop(struct physical *, int);
+extern void lqr_StopTimer(struct physical *);
+extern struct mbuf *lqr_RecvEcho(struct fsm *, struct mbuf *);
+extern struct mbuf *lqr_Input(struct bundle *, struct link *, struct mbuf *);
+
+extern struct layer lqrlayer;
diff --git a/usr.sbin/ppp/main.c b/usr.sbin/ppp/main.c
new file mode 100644
index 0000000..b4d5e29
--- /dev/null
+++ b/usr.sbin/ppp/main.c
@@ -0,0 +1,677 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/un.h>
+#include <sys/socket.h>
+#include <net/route.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <termios.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#ifndef NONAT
+#ifdef LOCALNAT
+#include "alias.h"
+#else
+#include <alias.h>
+#endif
+#endif
+
+#include "layer.h"
+#include "probe.h"
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "id.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "iplist.h"
+#include "throughput.h"
+#include "slcompress.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "link.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "auth.h"
+#include "systems.h"
+#include "sig.h"
+#include "main.h"
+#include "server.h"
+#include "prompt.h"
+#include "chat.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+#include "iface.h"
+
+#ifndef O_NONBLOCK
+#ifdef O_NDELAY
+#define O_NONBLOCK O_NDELAY
+#endif
+#endif
+
+static void DoLoop(struct bundle *);
+static void TerminalStop(int);
+
+static struct bundle *SignalBundle;
+static struct prompt *SignalPrompt;
+struct libalias *la;
+
+void
+Cleanup()
+{
+ SignalBundle->CleaningUp = 1;
+ bundle_Close(SignalBundle, NULL, CLOSE_STAYDOWN);
+}
+
+void
+AbortProgram(int excode)
+{
+ if (SignalBundle)
+ server_Close(SignalBundle);
+ log_Printf(LogPHASE, "PPP Terminated (%s).\n", ex_desc(excode));
+ if (SignalBundle) {
+ bundle_Close(SignalBundle, NULL, CLOSE_STAYDOWN);
+ bundle_Destroy(SignalBundle);
+ }
+ log_Close();
+ exit(excode);
+}
+
+static void
+CloseConnection(int signo)
+{
+ /* NOTE, these are manual, we've done a setsid() */
+ sig_signal(SIGINT, SIG_IGN);
+ log_Printf(LogPHASE, "Caught signal %d, abort connection(s)\n", signo);
+ bundle_Down(SignalBundle, CLOSE_STAYDOWN);
+ sig_signal(SIGINT, CloseConnection);
+}
+
+static void
+CloseSession(int signo)
+{
+ log_Printf(LogPHASE, "Signal %d, terminate.\n", signo);
+ Cleanup();
+}
+
+static pid_t BGPid = 0;
+
+static void
+KillChild(int signo)
+{
+ signal(signo, SIG_IGN);
+ log_Printf(LogPHASE, "Parent: Signal %d\n", signo);
+ kill(BGPid, SIGINT);
+}
+
+static void
+TerminalCont(int signo __unused)
+{
+ signal(SIGCONT, SIG_DFL);
+ prompt_Continue(SignalPrompt);
+}
+
+static void
+TerminalStop(int signo __unused)
+{
+ prompt_Suspend(SignalPrompt);
+ signal(SIGCONT, TerminalCont);
+ raise(SIGSTOP);
+}
+
+static void
+BringDownServer(int signo __unused)
+{
+ /* Drops all child prompts too ! */
+ if (server_Close(SignalBundle))
+ log_Printf(LogPHASE, "Closed server socket\n");
+}
+
+static void
+RestartServer(int signo __unused)
+{
+ /* Drops all child prompts and re-opens the socket */
+ server_Reopen(SignalBundle);
+}
+
+static void
+Usage(void)
+{
+ fprintf(stderr, "usage: ppp [-auto | -foreground | -background | -direct |"
+ " -dedicated | -ddial | -interactive]"
+#ifndef NONAT
+ " [-nat]"
+#endif
+ " [-quiet] [-unit N] [system ...]\n");
+ exit(EX_START);
+}
+
+struct switches {
+ unsigned nat : 1;
+ unsigned fg : 1;
+ unsigned quiet : 1;
+ int mode;
+ int unit;
+};
+
+static int
+ProcessArgs(int argc, char **argv, struct switches *sw)
+{
+ int optc, newmode, arg;
+ char *cp;
+
+ optc = 0;
+ memset(sw, '\0', sizeof *sw);
+ sw->mode = PHYS_INTERACTIVE;
+ sw->unit = -1;
+
+ for (arg = 1; arg < argc && *argv[arg] == '-'; arg++, optc++) {
+ cp = argv[arg] + 1;
+ newmode = Nam2mode(cp);
+ switch (newmode) {
+ case PHYS_NONE:
+ if (strcmp(cp, "nat") == 0) {
+#ifdef NONAT
+ log_Printf(LogWARN, "%s ignored: NAT is compiled out\n", argv[arg]);
+#else
+ sw->nat = 1;
+#endif
+ optc--; /* this option isn't exclusive */
+ } else if (strcmp(cp, "alias") == 0) {
+#ifdef NONAT
+ log_Printf(LogWARN, "%s ignored: NAT is compiled out\n", argv[arg]);
+ fprintf(stderr, "%s ignored: NAT is compiled out\n", argv[arg]);
+#else
+ log_Printf(LogWARN, "%s is deprecated\n", argv[arg]);
+ fprintf(stderr, "%s is deprecated\n", argv[arg]);
+ sw->nat = 1;
+#endif
+ optc--; /* this option isn't exclusive */
+ } else if (strncmp(cp, "unit", 4) == 0) {
+ optc--; /* this option isn't exclusive */
+ if (cp[4] == '\0') {
+ optc--; /* nor is the argument */
+ if (++arg == argc) {
+ fprintf(stderr, "-unit: Expected unit number\n");
+ Usage();
+ } else
+ sw->unit = atoi(argv[arg]);
+ } else
+ sw->unit = atoi(cp + 4);
+ } else if (strcmp(cp, "quiet") == 0) {
+ sw->quiet = 1;
+ optc--; /* this option isn't exclusive */
+ } else
+ Usage();
+ break;
+
+ case PHYS_ALL:
+ Usage();
+ break;
+
+ default:
+ sw->mode = newmode;
+ if (newmode == PHYS_FOREGROUND)
+ sw->fg = 1;
+ }
+ }
+
+ if (optc > 1) {
+ fprintf(stderr, "You may specify only one mode.\n");
+ exit(EX_START);
+ }
+
+ if (sw->mode == PHYS_AUTO && arg == argc) {
+ fprintf(stderr, "A system must be specified in auto mode.\n");
+ exit(EX_START);
+ }
+
+ return arg; /* Don't SetLabel yet ! */
+}
+
+static void
+CheckLabel(const char *label, struct prompt *prompt, int mode)
+{
+ const char *err;
+
+ if ((err = system_IsValid(label, prompt, mode)) != NULL) {
+ fprintf(stderr, "%s: %s\n", label, err);
+ if (mode == PHYS_DIRECT)
+ log_Printf(LogWARN, "Label %s rejected -direct connection: %s\n",
+ label, err);
+ log_Close();
+ exit(1);
+ }
+}
+
+
+int
+main(int argc, char **argv)
+{
+ char *name;
+ const char *lastlabel;
+ int arg, holdfd[3], label;
+ unsigned f;
+ struct bundle *bundle;
+ struct prompt *prompt;
+ struct switches sw;
+
+ probe_Init();
+
+ /*
+ * We open 3 descriptors to ensure that STDIN_FILENO, STDOUT_FILENO and
+ * STDERR_FILENO are always open. These are closed before DoLoop(),
+ * but *after* we've avoided the possibility of erroneously closing
+ * an important descriptor with close(STD{IN,OUT,ERR}_FILENO).
+ */
+ if ((holdfd[0] = open(_PATH_DEVNULL, O_RDWR)) == -1) {
+ fprintf(stderr, "Cannot open %s !\n", _PATH_DEVNULL);
+ return 2;
+ }
+ for (f = 1; f < sizeof holdfd / sizeof *holdfd; f++)
+ holdfd[f] = dup(holdfd[0]);
+
+ name = strrchr(argv[0], '/');
+ log_Open(name ? name + 1 : argv[0]);
+
+#ifndef NONAT
+ la = LibAliasInit(NULL);
+#endif
+ label = ProcessArgs(argc, argv, &sw);
+
+ /*
+ * A FreeBSD & OpenBSD hack to dodge a bug in the tty driver that drops
+ * output occasionally.... I must find the real reason some time. To
+ * display the dodgy behaviour, comment out this bit, make yourself a large
+ * routing table and then run ppp in interactive mode. The `show route'
+ * command will drop chunks of data !!!
+ */
+ if (sw.mode == PHYS_INTERACTIVE) {
+ close(STDIN_FILENO);
+ if (open(_PATH_TTY, O_RDONLY) != STDIN_FILENO) {
+ fprintf(stderr, "Cannot open %s for input !\n", _PATH_TTY);
+ return 2;
+ }
+ }
+
+ /* Allow output for the moment (except in direct mode) */
+ if (sw.mode == PHYS_DIRECT)
+ prompt = NULL;
+ else
+ SignalPrompt = prompt = prompt_Create(NULL, NULL, PROMPT_STD);
+
+ ID0init();
+ if (ID0realuid() != 0) {
+ char conf[200], *ptr;
+
+ snprintf(conf, sizeof conf, "%s/%s", PPP_CONFDIR, CONFFILE);
+ do {
+ struct stat sb;
+
+ if (stat(conf, &sb) == 0 && sb.st_mode & S_IWOTH) {
+ log_Printf(LogALERT, "ppp: Access violation: Please protect %s\n",
+ conf);
+ return -1;
+ }
+ ptr = conf + strlen(conf)-2;
+ while (ptr > conf && *ptr != '/')
+ *ptr-- = '\0';
+ } while (ptr >= conf);
+ }
+
+ if (label < argc)
+ for (arg = label; arg < argc; arg++)
+ CheckLabel(argv[arg], prompt, sw.mode);
+ else
+ CheckLabel("default", prompt, sw.mode);
+
+ if (!sw.quiet)
+ prompt_Printf(prompt, "Working in %s mode\n", mode2Nam(sw.mode));
+
+ if ((bundle = bundle_Create(TUN_PREFIX, sw.mode, sw.unit)) == NULL)
+ return EX_START;
+
+ /* NOTE: We may now have changed argv[1] via a ``set proctitle'' */
+
+ if (prompt) {
+ prompt->bundle = bundle; /* couldn't do it earlier */
+ if (!sw.quiet)
+ prompt_Printf(prompt, "Using interface: %s\n", bundle->iface->name);
+ }
+ SignalBundle = bundle;
+ bundle->NatEnabled = sw.nat;
+ if (sw.nat)
+ opt_enable(bundle, OPT_IFACEALIAS);
+
+ if (system_Select(bundle, "default", CONFFILE, prompt, NULL) < 0)
+ prompt_Printf(prompt, "Warning: No default entry found in config file.\n");
+
+ sig_signal(SIGHUP, CloseSession);
+ sig_signal(SIGTERM, CloseSession);
+ sig_signal(SIGINT, CloseConnection);
+ sig_signal(SIGQUIT, CloseSession);
+ sig_signal(SIGALRM, SIG_IGN);
+ signal(SIGPIPE, SIG_IGN);
+
+ if (sw.mode == PHYS_INTERACTIVE)
+ sig_signal(SIGTSTP, TerminalStop);
+
+ sig_signal(SIGUSR1, RestartServer);
+ sig_signal(SIGUSR2, BringDownServer);
+
+ lastlabel = argv[argc - 1];
+ for (arg = label; arg < argc; arg++) {
+ /* In case we use LABEL or ``set enddisc label'' */
+ bundle_SetLabel(bundle, lastlabel);
+ system_Select(bundle, argv[arg], CONFFILE, prompt, NULL);
+ }
+
+ if (label < argc)
+ /* In case the last label did a ``load'' */
+ bundle_SetLabel(bundle, lastlabel);
+
+ if (sw.mode == PHYS_AUTO &&
+ ncprange_family(&bundle->ncp.ipcp.cfg.peer_range) == AF_UNSPEC) {
+ prompt_Printf(prompt, "You must ``set ifaddr'' with a peer address "
+ "in auto mode.\n");
+ AbortProgram(EX_START);
+ }
+
+ if (sw.mode != PHYS_INTERACTIVE) {
+ if (sw.mode != PHYS_DIRECT) {
+ if (!sw.fg) {
+ int bgpipe[2];
+ pid_t bgpid;
+
+ if (sw.mode == PHYS_BACKGROUND && pipe(bgpipe)) {
+ log_Printf(LogERROR, "pipe: %s\n", strerror(errno));
+ AbortProgram(EX_SOCK);
+ }
+
+ bgpid = fork();
+ if (bgpid == -1) {
+ log_Printf(LogERROR, "fork: %s\n", strerror(errno));
+ AbortProgram(EX_SOCK);
+ }
+
+ if (bgpid) {
+ char c = EX_NORMAL;
+ int ret;
+
+ if (sw.mode == PHYS_BACKGROUND) {
+ close(bgpipe[1]);
+ BGPid = bgpid;
+ /* If we get a signal, kill the child */
+ signal(SIGHUP, KillChild);
+ signal(SIGTERM, KillChild);
+ signal(SIGINT, KillChild);
+ signal(SIGQUIT, KillChild);
+
+ /* Wait for our child to close its pipe before we exit */
+ while ((ret = read(bgpipe[0], &c, 1)) == 1) {
+ switch (c) {
+ case EX_NORMAL:
+ if (!sw.quiet) {
+ prompt_Printf(prompt, "PPP enabled\n");
+ log_Printf(LogPHASE, "Parent: PPP enabled\n");
+ }
+ break;
+ case EX_REDIAL:
+ if (!sw.quiet)
+ prompt_Printf(prompt, "Attempting redial\n");
+ continue;
+ case EX_RECONNECT:
+ if (!sw.quiet)
+ prompt_Printf(prompt, "Attempting reconnect\n");
+ continue;
+ default:
+ prompt_Printf(prompt, "Child failed (%s)\n",
+ ex_desc((int)c));
+ log_Printf(LogPHASE, "Parent: Child failed (%s)\n",
+ ex_desc((int) c));
+ }
+ break;
+ }
+ if (ret != 1) {
+ prompt_Printf(prompt, "Child exit, no status.\n");
+ log_Printf(LogPHASE, "Parent: Child exit, no status.\n");
+ }
+ close(bgpipe[0]);
+ }
+ return c;
+ } else if (sw.mode == PHYS_BACKGROUND) {
+ close(bgpipe[0]);
+ bundle->notify.fd = bgpipe[1];
+ }
+
+ bundle_ChangedPID(bundle);
+ bundle_LockTun(bundle); /* we have a new pid */
+ }
+
+ /* -auto, -dedicated, -ddial, -foreground & -background */
+ prompt_Destroy(prompt, 0);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ close(STDIN_FILENO);
+ if (!sw.fg)
+ setsid();
+ } else {
+ /* -direct - STDIN_FILENO gets used by physical_Open */
+ prompt_TtyInit(NULL);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ }
+ } else {
+ /* -interactive */
+ close(STDERR_FILENO);
+ prompt_TtyInit(prompt);
+ prompt_TtyCommandMode(prompt);
+ prompt_Required(prompt);
+ }
+
+ /* We can get rid of these now */
+ for (f = 0; f < sizeof holdfd / sizeof *holdfd; f++)
+ close(holdfd[f]);
+
+ log_Printf(LogPHASE, "PPP Started (%s mode).\n", mode2Nam(sw.mode));
+ DoLoop(bundle);
+ AbortProgram(EX_NORMAL);
+
+ return EX_NORMAL;
+}
+
+static void
+DoLoop(struct bundle *bundle)
+{
+ fd_set *rfds, *wfds, *efds;
+ int i, nfds, nothing_done;
+
+ if ((rfds = mkfdset()) == NULL) {
+ log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n");
+ return;
+ }
+
+ if ((wfds = mkfdset()) == NULL) {
+ log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n");
+ free(rfds);
+ return;
+ }
+
+ if ((efds = mkfdset()) == NULL) {
+ log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n");
+ free(rfds);
+ free(wfds);
+ return;
+ }
+
+ for (; !bundle_IsDead(bundle); bundle_CleanDatalinks(bundle)) {
+ nfds = 0;
+ zerofdset(rfds);
+ zerofdset(wfds);
+ zerofdset(efds);
+
+ /* All our datalinks, the tun device and the MP socket */
+ descriptor_UpdateSet(&bundle->desc, rfds, wfds, efds, &nfds);
+
+ /* All our prompts and the diagnostic socket */
+ descriptor_UpdateSet(&server.desc, rfds, NULL, NULL, &nfds);
+
+ bundle_CleanDatalinks(bundle);
+ if (bundle_IsDead(bundle))
+ /* Don't select - we'll be here forever */
+ break;
+
+ /*
+ * It's possible that we've had a signal since we last checked. If
+ * we don't check again before calling select(), we may end up stuck
+ * after having missed the event.... sig_Handle() tries to be as
+ * quick as possible if nothing is likely to have happened.
+ * This is only really likely if we block in open(... O_NONBLOCK)
+ * which will happen with a misconfigured device.
+ */
+ if (sig_Handle())
+ continue;
+
+ i = select(nfds, rfds, wfds, efds, NULL);
+
+ if (i < 0 && errno != EINTR) {
+ log_Printf(LogERROR, "DoLoop: select(): %s\n", strerror(errno));
+ if (log_IsKept(LogTIMER)) {
+ struct timeval t;
+
+ for (i = 0; i <= nfds; i++) {
+ if (FD_ISSET(i, rfds)) {
+ log_Printf(LogTIMER, "Read set contains %d\n", i);
+ FD_CLR(i, rfds);
+ t.tv_sec = t.tv_usec = 0;
+ if (select(nfds, rfds, wfds, efds, &t) != -1) {
+ log_Printf(LogTIMER, "The culprit !\n");
+ break;
+ }
+ }
+ if (FD_ISSET(i, wfds)) {
+ log_Printf(LogTIMER, "Write set contains %d\n", i);
+ FD_CLR(i, wfds);
+ t.tv_sec = t.tv_usec = 0;
+ if (select(nfds, rfds, wfds, efds, &t) != -1) {
+ log_Printf(LogTIMER, "The culprit !\n");
+ break;
+ }
+ }
+ if (FD_ISSET(i, efds)) {
+ log_Printf(LogTIMER, "Error set contains %d\n", i);
+ FD_CLR(i, efds);
+ t.tv_sec = t.tv_usec = 0;
+ if (select(nfds, rfds, wfds, efds, &t) != -1) {
+ log_Printf(LogTIMER, "The culprit !\n");
+ break;
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ log_Printf(LogTIMER, "Select returns %d\n", i);
+
+ sig_Handle();
+
+ if (i <= 0)
+ continue;
+
+ for (i = 0; i <= nfds; i++)
+ if (FD_ISSET(i, efds)) {
+ log_Printf(LogPHASE, "Exception detected on descriptor %d\n", i);
+ /* We deal gracefully with link descriptor exceptions */
+ if (!bundle_Exception(bundle, i)) {
+ log_Printf(LogERROR, "Exception cannot be handled !\n");
+ break;
+ }
+ }
+
+ if (i <= nfds)
+ break;
+
+ nothing_done = 1;
+
+ if (descriptor_IsSet(&server.desc, rfds)) {
+ descriptor_Read(&server.desc, bundle, rfds);
+ nothing_done = 0;
+ }
+
+ if (descriptor_IsSet(&bundle->desc, rfds)) {
+ descriptor_Read(&bundle->desc, bundle, rfds);
+ nothing_done = 0;
+ }
+
+ if (descriptor_IsSet(&bundle->desc, wfds))
+ if (descriptor_Write(&bundle->desc, bundle, wfds) <= 0 && nothing_done) {
+ /*
+ * This is disastrous. The OS has told us that something is
+ * writable, and all our write()s have failed. Rather than
+ * going back immediately to do our UpdateSet()s and select(),
+ * we sleep for a bit to avoid gobbling up all cpu time.
+ */
+ struct timeval t;
+
+ t.tv_sec = 0;
+ t.tv_usec = 100000;
+ select(0, NULL, NULL, NULL, &t);
+ }
+ }
+
+ log_Printf(LogDEBUG, "DoLoop done.\n");
+}
diff --git a/usr.sbin/ppp/main.h b/usr.sbin/ppp/main.h
new file mode 100644
index 0000000..0c7399a
--- /dev/null
+++ b/usr.sbin/ppp/main.h
@@ -0,0 +1,32 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+extern void Cleanup(void);
+extern void AbortProgram(int);
diff --git a/usr.sbin/ppp/mbuf.c b/usr.sbin/ppp/mbuf.c
new file mode 100644
index 0000000..4773224
--- /dev/null
+++ b/usr.sbin/ppp/mbuf.c
@@ -0,0 +1,440 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <termios.h>
+
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "descriptor.h"
+#include "prompt.h"
+#include "main.h"
+
+#define BUCKET_CHUNK 20
+#define BUCKET_HASH 256
+
+struct mbucket;
+
+struct mfree {
+ struct mbucket *next;
+ size_t count;
+};
+
+static struct mbucket {
+ union {
+ struct mbuf m;
+ struct mfree f;
+ } u;
+} *bucket[(M_MAXLEN + sizeof(struct mbuf)) / BUCKET_HASH];
+
+#define M_BINDEX(sz) (((sz) + sizeof(struct mbuf) - 1) / BUCKET_HASH)
+#define M_BUCKET(sz) (bucket + M_BINDEX(sz))
+#define M_ROUNDUP(sz) ((M_BINDEX(sz) + 1) * BUCKET_HASH)
+
+static struct memmap {
+ struct mbuf *queue;
+ size_t fragments;
+ size_t octets;
+} MemMap[MB_MAX + 1];
+
+static unsigned long long mbuf_Mallocs, mbuf_Frees;
+
+size_t
+m_length(struct mbuf *bp)
+{
+ size_t len;
+
+ for (len = 0; bp; bp = bp->m_next)
+ len += bp->m_len;
+ return len;
+}
+
+static const char *
+mbuftype(int type)
+{
+ static const char * const mbufdesc[MB_MAX] = {
+ "ip in", "ip out", "ipv6 in", "ipv6 out", "nat in", "nat out",
+ "mp in", "mp out", "vj in", "vj out", "icompd in", "icompd out",
+ "compd in", "compd out", "lqr in", "lqr out", "echo in", "echo out",
+ "proto in", "proto out", "acf in", "acf out", "sync in", "sync out",
+ "hdlc in", "hdlc out", "async in", "async out", "cbcp in", "cbcp out",
+ "chap in", "chap out", "pap in", "pap out", "ccp in", "ccp out",
+ "ipcp in", "ipcp out", "ipv6cp in", "ipv6cp out", "lcp in", "lcp out"
+ };
+
+ return type < 0 || type >= MB_MAX ? "unknown" : mbufdesc[type];
+}
+
+struct mbuf *
+m_get(size_t m_len, int type)
+{
+ struct mbucket **mb;
+ struct mbuf *bp;
+ size_t size;
+
+ if (type > MB_MAX) {
+ log_Printf(LogERROR, "Bad mbuf type %d\n", type);
+ type = MB_UNKNOWN;
+ }
+
+ if (m_len > M_MAXLEN || m_len == 0) {
+ log_Printf(LogERROR, "Request for mbuf size %lu (\"%s\") denied !\n",
+ (u_long)m_len, mbuftype(type));
+ AbortProgram(EX_OSERR);
+ }
+
+ mb = M_BUCKET(m_len);
+ size = M_ROUNDUP(m_len);
+
+ if (*mb) {
+ /* We've got some free blocks of the right size */
+ bp = &(*mb)->u.m;
+ if (--(*mb)->u.f.count == 0)
+ *mb = (*mb)->u.f.next;
+ else {
+ ((struct mbucket *)((char *)*mb + size))->u.f.count = (*mb)->u.f.count;
+ *mb = (struct mbucket *)((char *)*mb + size);
+ (*mb)->u.f.next = NULL;
+ }
+ } else {
+ /*
+ * Allocate another chunk of mbufs, use the first and put the rest on
+ * the free list
+ */
+ *mb = (struct mbucket *)malloc(BUCKET_CHUNK * size);
+ if (*mb == NULL) {
+ log_Printf(LogALERT, "Failed to allocate memory (%lu)\n",
+ (unsigned long)BUCKET_CHUNK * size);
+ AbortProgram(EX_OSERR);
+ }
+ bp = &(*mb)->u.m;
+ *mb = (struct mbucket *)((char *)*mb + size);
+ (*mb)->u.f.count = BUCKET_CHUNK - 1;
+ (*mb)->u.f.next = NULL;
+ }
+
+ mbuf_Mallocs++;
+
+ memset(bp, '\0', sizeof(struct mbuf));
+ bp->m_size = size - sizeof *bp;
+ bp->m_len = m_len;
+ bp->m_type = type;
+
+ MemMap[type].fragments++;
+ MemMap[type].octets += bp->m_size;
+
+ return bp;
+}
+
+struct mbuf *
+m_free(struct mbuf *bp)
+{
+ struct mbucket **mb, *f;
+ struct mbuf *nbp;
+
+ if ((f = (struct mbucket *)bp) != NULL) {
+ MemMap[bp->m_type].fragments--;
+ MemMap[bp->m_type].octets -= bp->m_size;
+
+ nbp = bp->m_next;
+ mb = M_BUCKET(bp->m_size);
+ f->u.f.next = *mb;
+ f->u.f.count = 1;
+ *mb = f;
+
+ mbuf_Frees++;
+ bp = nbp;
+ }
+
+ return bp;
+}
+
+void
+m_freem(struct mbuf *bp)
+{
+ while (bp)
+ bp = m_free(bp);
+}
+
+struct mbuf *
+mbuf_Read(struct mbuf *bp, void *v, size_t len)
+{
+ int nb;
+ u_char *ptr = v;
+
+ while (bp && len > 0) {
+ if (len > bp->m_len)
+ nb = bp->m_len;
+ else
+ nb = len;
+ if (nb) {
+ memcpy(ptr, MBUF_CTOP(bp), nb);
+ ptr += nb;
+ bp->m_len -= nb;
+ len -= nb;
+ bp->m_offset += nb;
+ }
+ if (bp->m_len == 0)
+ bp = m_free(bp);
+ }
+
+ while (bp && bp->m_len == 0)
+ bp = m_free(bp);
+
+ return bp;
+}
+
+size_t
+mbuf_View(struct mbuf *bp, void *v, size_t len)
+{
+ size_t nb, l = len;
+ u_char *ptr = v;
+
+ while (bp && l > 0) {
+ if (l > bp->m_len)
+ nb = bp->m_len;
+ else
+ nb = l;
+ memcpy(ptr, MBUF_CTOP(bp), nb);
+ ptr += nb;
+ l -= nb;
+ bp = bp->m_next;
+ }
+
+ return len - l;
+}
+
+struct mbuf *
+m_prepend(struct mbuf *bp, const void *ptr, size_t len, u_short extra)
+{
+ struct mbuf *head;
+
+ if (bp && bp->m_offset) {
+ if (bp->m_offset >= len) {
+ bp->m_offset -= len;
+ bp->m_len += len;
+ if (ptr)
+ memcpy(MBUF_CTOP(bp), ptr, len);
+ return bp;
+ }
+ len -= bp->m_offset;
+ if (ptr)
+ memcpy(bp + 1, (const char *)ptr + len, bp->m_offset);
+ bp->m_len += bp->m_offset;
+ bp->m_offset = 0;
+ }
+
+ head = m_get(len + extra, bp ? bp->m_type : MB_UNKNOWN);
+ head->m_offset = extra;
+ head->m_len -= extra;
+ if (ptr)
+ memcpy(MBUF_CTOP(head), ptr, len);
+ head->m_next = bp;
+
+ return head;
+}
+
+struct mbuf *
+m_adj(struct mbuf *bp, ssize_t n)
+{
+ if (n > 0) {
+ while (bp) {
+ if ((size_t)n < bp->m_len) {
+ bp->m_len = n;
+ bp->m_offset += n;
+ return bp;
+ }
+ n -= bp->m_len;
+ bp = m_free(bp);
+ }
+ } else {
+ if ((n = m_length(bp) + n) <= 0) {
+ m_freem(bp);
+ return NULL;
+ }
+ for (; bp; bp = bp->m_next, n -= bp->m_len)
+ if ((size_t)n < bp->m_len) {
+ bp->m_len = n;
+ m_freem(bp->m_next);
+ bp->m_next = NULL;
+ break;
+ }
+ }
+
+ return bp;
+}
+
+void
+mbuf_Write(struct mbuf *bp, const void *ptr, size_t m_len)
+{
+ size_t plen;
+ int nb;
+
+ plen = m_length(bp);
+ if (plen < m_len)
+ m_len = plen;
+
+ while (m_len > 0) {
+ nb = (m_len < bp->m_len) ? m_len : bp->m_len;
+ memcpy(MBUF_CTOP(bp), ptr, nb);
+ m_len -= bp->m_len;
+ bp = bp->m_next;
+ }
+}
+
+int
+mbuf_Show(struct cmdargs const *arg)
+{
+ int i;
+
+ prompt_Printf(arg->prompt, "Fragments (octets) in use:\n");
+ for (i = 0; i < MB_MAX; i += 2)
+ prompt_Printf(arg->prompt, "%10.10s: %04lu (%06lu)\t"
+ "%10.10s: %04lu (%06lu)\n",
+ mbuftype(i), (u_long)MemMap[i].fragments,
+ (u_long)MemMap[i].octets, mbuftype(i+1),
+ (u_long)MemMap[i+1].fragments, (u_long)MemMap[i+1].octets);
+
+ if (i == MB_MAX)
+ prompt_Printf(arg->prompt, "%10.10s: %04lu (%06lu)\n",
+ mbuftype(i), (u_long)MemMap[i].fragments,
+ (u_long)MemMap[i].octets);
+
+ prompt_Printf(arg->prompt, "Mallocs: %llu, Frees: %llu\n",
+ mbuf_Mallocs, mbuf_Frees);
+
+ return 0;
+}
+
+struct mbuf *
+m_dequeue(struct mqueue *q)
+{
+ struct mbuf *bp;
+
+ log_Printf(LogDEBUG, "m_dequeue: queue len = %lu\n", (u_long)q->len);
+ bp = q->top;
+ if (bp) {
+ q->top = q->top->m_nextpkt;
+ q->len--;
+ if (q->top == NULL) {
+ q->last = q->top;
+ if (q->len)
+ log_Printf(LogERROR, "m_dequeue: Not zero (%lu)!!!\n",
+ (u_long)q->len);
+ }
+ bp->m_nextpkt = NULL;
+ }
+
+ return bp;
+}
+
+void
+m_enqueue(struct mqueue *queue, struct mbuf *bp)
+{
+ if (bp != NULL) {
+ if (queue->last) {
+ queue->last->m_nextpkt = bp;
+ queue->last = bp;
+ } else
+ queue->last = queue->top = bp;
+ queue->len++;
+ log_Printf(LogDEBUG, "m_enqueue: len = %lu\n", (unsigned long)queue->len);
+ }
+}
+
+struct mbuf *
+m_pullup(struct mbuf *bp)
+{
+ /* Put it all in one contigous (aligned) mbuf */
+
+ if (bp != NULL) {
+ if (bp->m_next != NULL) {
+ struct mbuf *nbp;
+ u_char *cp;
+
+ nbp = m_get(m_length(bp), bp->m_type);
+
+ for (cp = MBUF_CTOP(nbp); bp; bp = m_free(bp)) {
+ memcpy(cp, MBUF_CTOP(bp), bp->m_len);
+ cp += bp->m_len;
+ }
+ bp = nbp;
+ }
+#ifndef __i386__ /* Do any other archs not care about alignment ? */
+ else if ((bp->m_offset & (sizeof(long) - 1)) != 0) {
+ bcopy(MBUF_CTOP(bp), bp + 1, bp->m_len);
+ bp->m_offset = 0;
+ }
+#endif
+ }
+
+ return bp;
+}
+
+void
+m_settype(struct mbuf *bp, int type)
+{
+ for (; bp; bp = bp->m_next)
+ if (type != bp->m_type) {
+ MemMap[bp->m_type].fragments--;
+ MemMap[bp->m_type].octets -= bp->m_size;
+ bp->m_type = type;
+ MemMap[type].fragments++;
+ MemMap[type].octets += bp->m_size;
+ }
+}
+
+struct mbuf *
+m_append(struct mbuf *bp, const void *v, size_t sz)
+{
+ struct mbuf *m = bp;
+
+ if (m) {
+ while (m->m_next)
+ m = m->m_next;
+ if (m->m_size - m->m_len >= sz) {
+ if (v)
+ memcpy((char *)(m + 1) + m->m_len, v, sz);
+ m->m_len += sz;
+ } else
+ m->m_next = m_prepend(NULL, v, sz, 0);
+ } else
+ bp = m_prepend(NULL, v, sz, 0);
+
+ return bp;
+}
diff --git a/usr.sbin/ppp/mbuf.h b/usr.sbin/ppp/mbuf.h
new file mode 100644
index 0000000..980d649
--- /dev/null
+++ b/usr.sbin/ppp/mbuf.h
@@ -0,0 +1,119 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+struct mbuf {
+ size_t m_size; /* size allocated (excluding header) */
+ u_short m_offset; /* offset from header end to start position */
+ size_t m_len; /* available byte count in buffer */
+ short m_type; /* MB_* below */
+ struct mbuf *m_next; /* link to next mbuf */
+ struct mbuf *m_nextpkt; /* link to next packet */
+ unsigned long priv; /* private data - holds HDLC escape count */
+ /* buffer space is malloc()d directly after the header */
+};
+
+struct mqueue {
+ struct mbuf *top;
+ struct mbuf *last;
+ size_t len;
+};
+
+#define MBUF_CTOP(bp) \
+ ((bp) ? (u_char *)((bp)+1) + (bp)->m_offset : (u_char *)bp)
+
+#define CONST_MBUF_CTOP(bp) \
+ ((bp) ? (const u_char *)((bp)+1) + (bp)->m_offset : (const u_char *)bp)
+
+#define MB_IPIN 0
+#define MB_IPOUT 1
+#define MB_IPV6IN 2
+#define MB_IPV6OUT 3
+#define MB_NATIN 4
+#define MB_NATOUT 5
+#define MB_MPIN 6
+#define MB_MPOUT 7
+#define MB_VJIN 8
+#define MB_VJOUT 9
+#define MB_ICOMPDIN 10
+#define MB_ICOMPDOUT 11
+#define MB_COMPDIN 12
+#define MB_COMPDOUT 13
+#define MB_LQRIN 14
+#define MB_LQROUT 15
+#define MB_ECHOIN 16
+#define MB_ECHOOUT 17
+#define MB_PROTOIN 18
+#define MB_PROTOOUT 19
+#define MB_ACFIN 20
+#define MB_ACFOUT 21
+#define MB_SYNCIN 22
+#define MB_SYNCOUT 23
+#define MB_HDLCIN 24
+#define MB_HDLCOUT 25
+#define MB_ASYNCIN 26
+#define MB_ASYNCOUT 27
+#define MB_CBCPIN 28
+#define MB_CBCPOUT 29
+#define MB_CHAPIN 30
+#define MB_CHAPOUT 31
+#define MB_PAPIN 32
+#define MB_PAPOUT 33
+#define MB_CCPIN 34
+#define MB_CCPOUT 35
+#define MB_IPCPIN 36
+#define MB_IPCPOUT 37
+#define MB_IPV6CPIN 38
+#define MB_IPV6CPOUT 39
+#define MB_LCPIN 40
+#define MB_LCPOUT 41
+#define MB_UNKNOWN 42
+#define MB_MAX MB_UNKNOWN
+
+#define M_MAXLEN (4352 - sizeof(struct mbuf)) /* > HDLCSIZE */
+
+struct cmdargs;
+
+extern size_t m_length(struct mbuf *);
+extern struct mbuf *m_get(size_t, int);
+extern struct mbuf *m_free(struct mbuf *);
+extern void m_freem(struct mbuf *);
+extern void mbuf_Write(struct mbuf *, const void *, size_t);
+extern struct mbuf *mbuf_Read(struct mbuf *, void *, size_t);
+extern size_t mbuf_View(struct mbuf *, void *, size_t);
+extern struct mbuf *m_prepend(struct mbuf *, const void *, size_t, u_short);
+extern struct mbuf *m_adj(struct mbuf *, ssize_t);
+extern struct mbuf *m_pullup(struct mbuf *);
+extern void m_settype(struct mbuf *, int);
+extern struct mbuf *m_append(struct mbuf *, const void *, size_t);
+
+extern int mbuf_Show(struct cmdargs const *);
+
+extern void m_enqueue(struct mqueue *, struct mbuf *);
+extern struct mbuf *m_dequeue(struct mqueue *);
diff --git a/usr.sbin/ppp/mp.c b/usr.sbin/ppp/mp.c
new file mode 100644
index 0000000..095f9bb
--- /dev/null
+++ b/usr.sbin/ppp/mp.c
@@ -0,0 +1,1209 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <net/if_dl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <paths.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#ifndef NONAT
+#include "nat_cmd.h"
+#endif
+#include "vjcomp.h"
+#include "ua.h"
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "iplist.h"
+#include "throughput.h"
+#include "slcompress.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "auth.h"
+#include "lcp.h"
+#include "async.h"
+#include "ccp.h"
+#include "link.h"
+#include "descriptor.h"
+#include "physical.h"
+#include "chat.h"
+#include "proto.h"
+#include "filter.h"
+#include "mp.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "prompt.h"
+#include "id.h"
+#include "arp.h"
+
+void
+peerid_Init(struct peerid *peer)
+{
+ peer->enddisc.class = 0;
+ *peer->enddisc.address = '\0';
+ peer->enddisc.len = 0;
+ *peer->authname = '\0';
+}
+
+int
+peerid_Equal(const struct peerid *p1, const struct peerid *p2)
+{
+ return !strcmp(p1->authname, p2->authname) &&
+ p1->enddisc.class == p2->enddisc.class &&
+ p1->enddisc.len == p2->enddisc.len &&
+ !memcmp(p1->enddisc.address, p2->enddisc.address, p1->enddisc.len);
+}
+
+static u_int32_t
+inc_seq(unsigned is12bit, u_int32_t seq)
+{
+ seq++;
+ if (is12bit) {
+ if (seq & 0xfffff000)
+ seq = 0;
+ } else if (seq & 0xff000000)
+ seq = 0;
+ return seq;
+}
+
+static int
+isbefore(unsigned is12bit, u_int32_t seq1, u_int32_t seq2)
+{
+ u_int32_t max = (is12bit ? 0xfff : 0xffffff) - 0x200;
+
+ if (seq1 > max) {
+ if (seq2 < 0x200 || seq2 > seq1)
+ return 1;
+ } else if ((seq1 > 0x200 || seq2 <= max) && seq1 < seq2)
+ return 1;
+
+ return 0;
+}
+
+static int
+mp_ReadHeader(struct mp *mp, struct mbuf *m, struct mp_header *header)
+{
+ if (mp->local_is12bit) {
+ u_int16_t val;
+
+ ua_ntohs(MBUF_CTOP(m), &val);
+ if (val & 0x3000) {
+ log_Printf(LogWARN, "Oops - MP header without required zero bits\n");
+ return 0;
+ }
+ header->begin = val & 0x8000 ? 1 : 0;
+ header->end = val & 0x4000 ? 1 : 0;
+ header->seq = val & 0x0fff;
+ return 2;
+ } else {
+ ua_ntohl(MBUF_CTOP(m), &header->seq);
+ if (header->seq & 0x3f000000) {
+ log_Printf(LogWARN, "Oops - MP header without required zero bits\n");
+ return 0;
+ }
+ header->begin = header->seq & 0x80000000 ? 1 : 0;
+ header->end = header->seq & 0x40000000 ? 1 : 0;
+ header->seq &= 0x00ffffff;
+ return 4;
+ }
+}
+
+static void
+mp_LayerStart(void *v __unused, struct fsm *fp __unused)
+{
+ /* The given FSM (ccp) is about to start up ! */
+}
+
+static void
+mp_LayerUp(void *v __unused, struct fsm *fp)
+{
+ /* The given fsm (ccp) is now up */
+
+ bundle_CalculateBandwidth(fp->bundle); /* Against ccp_MTUOverhead */
+}
+
+static void
+mp_LayerDown(void *v __unused, struct fsm *fp __unused)
+{
+ /* The given FSM (ccp) has been told to come down */
+}
+
+static void
+mp_LayerFinish(void *v __unused, struct fsm *fp)
+{
+ /* The given fsm (ccp) is now down */
+ if (fp->state == ST_CLOSED && fp->open_mode == OPEN_PASSIVE)
+ fsm_Open(fp); /* CCP goes to ST_STOPPED */
+}
+
+static void
+mp_UpDown(void *v)
+{
+ struct mp *mp = (struct mp *)v;
+ int percent;
+
+ percent = MAX(mp->link.stats.total.in.OctetsPerSecond,
+ mp->link.stats.total.out.OctetsPerSecond) * 800 /
+ mp->bundle->bandwidth;
+ if (percent >= mp->cfg.autoload.max) {
+ log_Printf(LogDEBUG, "%d%% saturation - bring a link up ?\n", percent);
+ bundle_AutoAdjust(mp->bundle, percent, AUTO_UP);
+ } else if (percent <= mp->cfg.autoload.min) {
+ log_Printf(LogDEBUG, "%d%% saturation - bring a link down ?\n", percent);
+ bundle_AutoAdjust(mp->bundle, percent, AUTO_DOWN);
+ }
+}
+
+void
+mp_StopAutoloadTimer(struct mp *mp)
+{
+ throughput_stop(&mp->link.stats.total);
+}
+
+void
+mp_CheckAutoloadTimer(struct mp *mp)
+{
+ if (mp->link.stats.total.SamplePeriod != mp->cfg.autoload.period) {
+ throughput_destroy(&mp->link.stats.total);
+ throughput_init(&mp->link.stats.total, mp->cfg.autoload.period);
+ throughput_callback(&mp->link.stats.total, mp_UpDown, mp);
+ }
+
+ if (bundle_WantAutoloadTimer(mp->bundle))
+ throughput_start(&mp->link.stats.total, "MP throughput", 1);
+ else
+ mp_StopAutoloadTimer(mp);
+}
+
+void
+mp_RestartAutoloadTimer(struct mp *mp)
+{
+ if (mp->link.stats.total.SamplePeriod != mp->cfg.autoload.period)
+ mp_CheckAutoloadTimer(mp);
+ else
+ throughput_clear(&mp->link.stats.total, THROUGHPUT_OVERALL, NULL);
+}
+
+void
+mp_Init(struct mp *mp, struct bundle *bundle)
+{
+ mp->peer_is12bit = mp->local_is12bit = 0;
+ mp->peer_mrru = mp->local_mrru = 0;
+
+ peerid_Init(&mp->peer);
+
+ mp->out.seq = 0;
+ mp->out.link = 0;
+ mp->out.af = AF_INET;
+ mp->seq.min_in = 0;
+ mp->seq.next_in = 0;
+ mp->inbufs = NULL;
+ mp->bundle = bundle;
+
+ mp->link.type = LOGICAL_LINK;
+ mp->link.name = "mp";
+ mp->link.len = sizeof *mp;
+
+ mp->cfg.autoload.period = SAMPLE_PERIOD;
+ mp->cfg.autoload.min = mp->cfg.autoload.max = 0;
+ throughput_init(&mp->link.stats.total, mp->cfg.autoload.period);
+ throughput_callback(&mp->link.stats.total, mp_UpDown, mp);
+ mp->link.stats.parent = NULL;
+ mp->link.stats.gather = 0; /* Let the physical links gather stats */
+ memset(mp->link.Queue, '\0', sizeof mp->link.Queue);
+ memset(mp->link.proto_in, '\0', sizeof mp->link.proto_in);
+ memset(mp->link.proto_out, '\0', sizeof mp->link.proto_out);
+
+ mp->fsmp.LayerStart = mp_LayerStart;
+ mp->fsmp.LayerUp = mp_LayerUp;
+ mp->fsmp.LayerDown = mp_LayerDown;
+ mp->fsmp.LayerFinish = mp_LayerFinish;
+ mp->fsmp.object = mp;
+
+ mpserver_Init(&mp->server);
+
+ mp->cfg.mrru = 0;
+ mp->cfg.shortseq = NEG_ENABLED|NEG_ACCEPTED;
+ mp->cfg.negenddisc = NEG_ENABLED|NEG_ACCEPTED;
+ mp->cfg.enddisc.class = 0;
+ *mp->cfg.enddisc.address = '\0';
+ mp->cfg.enddisc.len = 0;
+
+ lcp_Init(&mp->link.lcp, mp->bundle, &mp->link, NULL);
+ ccp_Init(&mp->link.ccp, mp->bundle, &mp->link, &mp->fsmp);
+
+ link_EmptyStack(&mp->link);
+ link_Stack(&mp->link, &protolayer);
+ link_Stack(&mp->link, &ccplayer);
+ link_Stack(&mp->link, &vjlayer);
+#ifndef NONAT
+ link_Stack(&mp->link, &natlayer);
+#endif
+}
+
+int
+mp_Up(struct mp *mp, struct datalink *dl)
+{
+ struct lcp *lcp = &dl->physical->link.lcp;
+
+ if (mp->active) {
+ /* We're adding a link - do a last validation on our parameters */
+ if (!peerid_Equal(&dl->peer, &mp->peer)) {
+ log_Printf(LogPHASE, "%s: Inappropriate peer !\n", dl->name);
+ log_Printf(LogPHASE, " Attached to peer %s/%s\n", mp->peer.authname,
+ mp_Enddisc(mp->peer.enddisc.class, mp->peer.enddisc.address,
+ mp->peer.enddisc.len));
+ log_Printf(LogPHASE, " New link is peer %s/%s\n", dl->peer.authname,
+ mp_Enddisc(dl->peer.enddisc.class, dl->peer.enddisc.address,
+ dl->peer.enddisc.len));
+ return MP_FAILED;
+ }
+ if (mp->local_mrru != lcp->want_mrru ||
+ mp->peer_mrru != lcp->his_mrru ||
+ mp->local_is12bit != lcp->want_shortseq ||
+ mp->peer_is12bit != lcp->his_shortseq) {
+ log_Printf(LogPHASE, "%s: Invalid MRRU/SHORTSEQ MP parameters !\n",
+ dl->name);
+ return MP_FAILED;
+ }
+ return MP_ADDED;
+ } else {
+ /* First link in multilink mode */
+
+ mp->local_mrru = lcp->want_mrru;
+ mp->peer_mrru = lcp->his_mrru;
+ mp->local_is12bit = lcp->want_shortseq;
+ mp->peer_is12bit = lcp->his_shortseq;
+ mp->peer = dl->peer;
+
+ throughput_destroy(&mp->link.stats.total);
+ throughput_init(&mp->link.stats.total, mp->cfg.autoload.period);
+ throughput_callback(&mp->link.stats.total, mp_UpDown, mp);
+ memset(mp->link.Queue, '\0', sizeof mp->link.Queue);
+ memset(mp->link.proto_in, '\0', sizeof mp->link.proto_in);
+ memset(mp->link.proto_out, '\0', sizeof mp->link.proto_out);
+
+ /* Tell the link who it belongs to */
+ dl->physical->link.stats.parent = &mp->link.stats.total;
+
+ mp->out.seq = 0;
+ mp->out.link = 0;
+ mp->out.af = AF_INET;
+ mp->seq.min_in = 0;
+ mp->seq.next_in = 0;
+
+ /*
+ * Now we create our server socket.
+ * If it already exists, join it. Otherwise, create and own it
+ */
+ switch (mpserver_Open(&mp->server, &mp->peer)) {
+ case MPSERVER_CONNECTED:
+ log_Printf(LogPHASE, "mp: Transfer link on %s\n",
+ mp->server.socket.sun_path);
+ mp->server.send.dl = dl; /* Defer 'till it's safe to send */
+ return MP_LINKSENT;
+ case MPSERVER_FAILED:
+ return MP_FAILED;
+ case MPSERVER_LISTENING:
+ log_Printf(LogPHASE, "mp: Listening on %s\n", mp->server.socket.sun_path);
+ log_Printf(LogPHASE, " First link: %s\n", dl->name);
+
+ /* Re-point our NCP layers at our MP link */
+ ncp_SetLink(&mp->bundle->ncp, &mp->link);
+
+ /* Our lcp's already up 'cos of the NULL parent */
+ if (ccp_SetOpenMode(&mp->link.ccp)) {
+ fsm_Up(&mp->link.ccp.fsm);
+ fsm_Open(&mp->link.ccp.fsm);
+ }
+
+ mp->active = 1;
+ break;
+ }
+ }
+
+ return MP_UP;
+}
+
+void
+mp_Down(struct mp *mp)
+{
+ if (mp->active) {
+ struct mbuf *next;
+
+ /* Stop that ! */
+ mp_StopAutoloadTimer(mp);
+
+ /* Don't want any more of these */
+ mpserver_Close(&mp->server);
+
+ /* CCP goes down with a bang */
+ fsm2initial(&mp->link.ccp.fsm);
+
+ /* Received fragments go in the bit-bucket */
+ while (mp->inbufs) {
+ next = mp->inbufs->m_nextpkt;
+ m_freem(mp->inbufs);
+ mp->inbufs = next;
+ }
+
+ peerid_Init(&mp->peer);
+ mp->active = 0;
+ }
+}
+
+void
+mp_linkInit(struct mp_link *mplink)
+{
+ mplink->seq = 0;
+ mplink->bandwidth = 0;
+}
+
+static void
+mp_Assemble(struct mp *mp, struct mbuf *m, struct physical *p)
+{
+ struct mp_header mh, h;
+ struct mbuf *q, *last;
+ u_int32_t seq;
+
+ /*
+ * When `m' and `p' are NULL, it means our oldest link has gone down.
+ * We want to determine a new min, and process any intermediate stuff
+ * as normal
+ */
+
+ if (m && mp_ReadHeader(mp, m, &mh) == 0) {
+ m_freem(m);
+ return;
+ }
+
+ if (p) {
+ seq = p->dl->mp.seq;
+ p->dl->mp.seq = mh.seq;
+ } else
+ seq = mp->seq.min_in;
+
+ if (mp->seq.min_in == seq) {
+ /*
+ * We've received new data on the link that has our min (oldest) seq.
+ * Figure out which link now has the smallest (oldest) seq.
+ */
+ struct datalink *dl;
+
+ mp->seq.min_in = (u_int32_t)-1;
+ for (dl = mp->bundle->links; dl; dl = dl->next)
+ if (dl->state == DATALINK_OPEN &&
+ (mp->seq.min_in == (u_int32_t)-1 ||
+ isbefore(mp->local_is12bit, dl->mp.seq, mp->seq.min_in)))
+ mp->seq.min_in = dl->mp.seq;
+ }
+
+ /*
+ * Now process as many of our fragments as we can, adding our new
+ * fragment in as we go, and ordering with the oldest at the top of
+ * the queue.
+ */
+
+ last = NULL;
+ seq = mp->seq.next_in;
+ q = mp->inbufs;
+ while (q || m) {
+ if (!q) {
+ if (last)
+ last->m_nextpkt = m;
+ else
+ mp->inbufs = m;
+ q = m;
+ m = NULL;
+ h = mh;
+ } else {
+ mp_ReadHeader(mp, q, &h);
+
+ if (m && isbefore(mp->local_is12bit, mh.seq, h.seq)) {
+ /* Our received fragment fits in before this one, so link it in */
+ if (last)
+ last->m_nextpkt = m;
+ else
+ mp->inbufs = m;
+ m->m_nextpkt = q;
+ q = m;
+ h = mh;
+ m = NULL;
+ }
+ }
+
+ if (h.seq != seq) {
+ /* we're missing something :-( */
+ if (isbefore(mp->local_is12bit, seq, mp->seq.min_in)) {
+ /* we're never gonna get it */
+ struct mbuf *next;
+
+ /* Zap all older fragments */
+ while (mp->inbufs != q) {
+ log_Printf(LogDEBUG, "Drop frag\n");
+ next = mp->inbufs->m_nextpkt;
+ m_freem(mp->inbufs);
+ mp->inbufs = next;
+ }
+
+ /*
+ * Zap everything until the next `end' fragment OR just before
+ * the next `begin' fragment OR 'till seq.min_in - whichever
+ * comes first.
+ */
+ do {
+ mp_ReadHeader(mp, mp->inbufs, &h);
+ if (h.begin) {
+ /* We might be able to process this ! */
+ h.seq--; /* We're gonna look for fragment with h.seq+1 */
+ break;
+ }
+ next = mp->inbufs->m_nextpkt;
+ log_Printf(LogDEBUG, "Drop frag %u\n", h.seq);
+ m_freem(mp->inbufs);
+ mp->inbufs = next;
+ } while (mp->inbufs && (isbefore(mp->local_is12bit, mp->seq.min_in,
+ h.seq) || h.end));
+
+ /*
+ * Continue processing things from here.
+ * This deals with the possibility that we received a fragment
+ * on the slowest link that invalidates some of our data (because
+ * of the hole at `q'), but where there are subsequent `whole'
+ * packets that have already been received.
+ */
+
+ mp->seq.next_in = seq = inc_seq(mp->local_is12bit, h.seq);
+ last = NULL;
+ q = mp->inbufs;
+ } else
+ /* we may still receive the missing fragment */
+ break;
+ } else if (h.end) {
+ /* We've got something, reassemble */
+ struct mbuf **frag = &q;
+ int len;
+ long long first = -1;
+
+ do {
+ *frag = mp->inbufs;
+ mp->inbufs = mp->inbufs->m_nextpkt;
+ len = mp_ReadHeader(mp, *frag, &h);
+ if (first == -1)
+ first = h.seq;
+ if (frag == &q && !h.begin) {
+ log_Printf(LogWARN, "Oops - MP frag %lu should have a begin flag\n",
+ (u_long)h.seq);
+ m_freem(q);
+ q = NULL;
+ } else if (frag != &q && h.begin) {
+ log_Printf(LogWARN, "Oops - MP frag %lu should have an end flag\n",
+ (u_long)h.seq - 1);
+ /*
+ * Stuff our fragment back at the front of the queue and zap
+ * our half-assembled packet.
+ */
+ (*frag)->m_nextpkt = mp->inbufs;
+ mp->inbufs = *frag;
+ *frag = NULL;
+ m_freem(q);
+ q = NULL;
+ frag = &q;
+ h.end = 0; /* just in case it's a whole packet */
+ } else {
+ (*frag)->m_offset += len;
+ (*frag)->m_len -= len;
+ (*frag)->m_nextpkt = NULL;
+ do
+ frag = &(*frag)->m_next;
+ while (*frag != NULL);
+ }
+ } while (!h.end);
+
+ if (q) {
+ q = m_pullup(q);
+ log_Printf(LogDEBUG, "MP: Reassembled frags %lu-%lu, length %zd\n",
+ (u_long)first, (u_long)h.seq, m_length(q));
+ link_PullPacket(&mp->link, MBUF_CTOP(q), q->m_len, mp->bundle);
+ m_freem(q);
+ }
+
+ mp->seq.next_in = seq = inc_seq(mp->local_is12bit, h.seq);
+ last = NULL;
+ q = mp->inbufs;
+ } else {
+ /* Look for the next fragment */
+ seq = inc_seq(mp->local_is12bit, seq);
+ last = q;
+ q = q->m_nextpkt;
+ }
+ }
+
+ if (m) {
+ /* We still have to find a home for our new fragment */
+ last = NULL;
+ for (q = mp->inbufs; q; last = q, q = q->m_nextpkt) {
+ mp_ReadHeader(mp, q, &h);
+ if (isbefore(mp->local_is12bit, mh.seq, h.seq))
+ break;
+ }
+ /* Our received fragment fits in here */
+ if (last)
+ last->m_nextpkt = m;
+ else
+ mp->inbufs = m;
+ m->m_nextpkt = q;
+ }
+}
+
+struct mbuf *
+mp_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
+{
+ struct physical *p = link2physical(l);
+
+ if (!bundle->ncp.mp.active)
+ /* Let someone else deal with it ! */
+ return bp;
+
+ if (p == NULL) {
+ log_Printf(LogWARN, "DecodePacket: Can't do MP inside MP !\n");
+ m_freem(bp);
+ } else {
+ m_settype(bp, MB_MPIN);
+ mp_Assemble(&bundle->ncp.mp, bp, p);
+ }
+
+ return NULL;
+}
+
+static void
+mp_Output(struct mp *mp, struct bundle *bundle, struct link *l,
+ struct mbuf *m, u_int32_t begin, u_int32_t end)
+{
+ char prepend[4];
+
+ /* Stuff an MP header on the front of our packet and send it */
+
+ if (mp->peer_is12bit) {
+ u_int16_t val;
+
+ val = (begin << 15) | (end << 14) | (u_int16_t)mp->out.seq;
+ ua_htons(&val, prepend);
+ m = m_prepend(m, prepend, 2, 0);
+ } else {
+ u_int32_t val;
+
+ val = (begin << 31) | (end << 30) | (u_int32_t)mp->out.seq;
+ ua_htonl(&val, prepend);
+ m = m_prepend(m, prepend, 4, 0);
+ }
+ if (log_IsKept(LogDEBUG))
+ log_Printf(LogDEBUG, "MP[frag %d]: Send %zd bytes on link `%s'\n",
+ mp->out.seq, m_length(m), l->name);
+ mp->out.seq = inc_seq(mp->peer_is12bit, mp->out.seq);
+
+ if (l->ccp.fsm.state != ST_OPENED && ccp_Required(&l->ccp)) {
+ log_Printf(LogPHASE, "%s: Not transmitting... waiting for CCP\n", l->name);
+ return;
+ }
+
+ link_PushPacket(l, m, bundle, LINK_QUEUES(l) - 1, PROTO_MP);
+}
+
+int
+mp_FillPhysicalQueues(struct bundle *bundle)
+{
+ struct mp *mp = &bundle->ncp.mp;
+ struct datalink *dl, *fdl;
+ size_t total, add, len;
+ int thislink, nlinks, nopenlinks, sendasip;
+ u_int32_t begin, end;
+ struct mbuf *m, *mo;
+ struct link *bestlink;
+
+ thislink = nlinks = nopenlinks = 0;
+ for (fdl = NULL, dl = bundle->links; dl; dl = dl->next) {
+ /* Include non-open links here as mp->out.link will stay more correct */
+ if (!fdl) {
+ if (thislink == mp->out.link)
+ fdl = dl;
+ else
+ thislink++;
+ }
+ nlinks++;
+ if (dl->state == DATALINK_OPEN)
+ nopenlinks++;
+ }
+
+ if (!fdl) {
+ fdl = bundle->links;
+ if (!fdl)
+ return 0;
+ thislink = 0;
+ }
+
+ total = 0;
+ for (dl = fdl; nlinks > 0; dl = dl->next, nlinks--, thislink++) {
+ if (!dl) {
+ dl = bundle->links;
+ thislink = 0;
+ }
+
+ if (dl->state != DATALINK_OPEN)
+ continue;
+
+ if (dl->physical->out)
+ /* this link has suffered a short write. Let it continue */
+ continue;
+
+ add = link_QueueLen(&dl->physical->link);
+ if (add) {
+ /* this link has got stuff already queued. Let it continue */
+ total += add;
+ continue;
+ }
+
+ if (!mp_QueueLen(mp)) {
+ int mrutoosmall;
+
+ /*
+ * If there's only a single open link in our bundle and we haven't got
+ * MP level link compression, queue outbound traffic directly via that
+ * link's protocol stack rather than using the MP link. This results
+ * in the outbound traffic going out as PROTO_IP or PROTO_IPV6 rather
+ * than PROTO_MP.
+ */
+
+ mrutoosmall = 0;
+ sendasip = nopenlinks < 2;
+ if (sendasip) {
+ if (dl->physical->link.lcp.his_mru < mp->peer_mrru) {
+ /*
+ * Actually, forget it. This test is done against the MRRU rather
+ * than the packet size so that we don't end up sending some data
+ * in MP fragments and some data in PROTO_IP packets. That's just
+ * too likely to upset some ppp implementations.
+ */
+ mrutoosmall = 1;
+ sendasip = 0;
+ }
+ }
+
+ bestlink = sendasip ? &dl->physical->link : &mp->link;
+ if (!ncp_PushPacket(&bundle->ncp, &mp->out.af, bestlink))
+ break; /* Nothing else to send */
+
+ if (mrutoosmall)
+ log_Printf(LogDEBUG, "Don't send data as PROTO_IP, MRU < MRRU\n");
+ else if (sendasip)
+ log_Printf(LogDEBUG, "Sending data as PROTO_IP, not PROTO_MP\n");
+
+ if (sendasip) {
+ add = link_QueueLen(&dl->physical->link);
+ if (add) {
+ /* this link has got stuff already queued. Let it continue */
+ total += add;
+ continue;
+ }
+ }
+ }
+
+ m = link_Dequeue(&mp->link);
+ if (m) {
+ len = m_length(m);
+ begin = 1;
+ end = 0;
+
+ while (!end) {
+ if (dl->state == DATALINK_OPEN) {
+ /* Write at most his_mru bytes to the physical link */
+ if (len <= dl->physical->link.lcp.his_mru) {
+ mo = m;
+ end = 1;
+ m_settype(mo, MB_MPOUT);
+ } else {
+ /* It's > his_mru, chop the packet (`m') into bits */
+ mo = m_get(dl->physical->link.lcp.his_mru, MB_MPOUT);
+ len -= mo->m_len;
+ m = mbuf_Read(m, MBUF_CTOP(mo), mo->m_len);
+ }
+ mp_Output(mp, bundle, &dl->physical->link, mo, begin, end);
+ begin = 0;
+ }
+
+ if (!end) {
+ nlinks--;
+ dl = dl->next;
+ if (!dl) {
+ dl = bundle->links;
+ thislink = 0;
+ } else
+ thislink++;
+ }
+ }
+ }
+ }
+ mp->out.link = thislink; /* Start here next time */
+
+ return total;
+}
+
+int
+mp_SetDatalinkBandwidth(struct cmdargs const *arg)
+{
+ int val;
+
+ if (arg->argc != arg->argn+1)
+ return -1;
+
+ val = atoi(arg->argv[arg->argn]);
+ if (val <= 0) {
+ log_Printf(LogWARN, "The link bandwidth must be greater than zero\n");
+ return 1;
+ }
+ arg->cx->mp.bandwidth = val;
+
+ if (arg->cx->state == DATALINK_OPEN)
+ bundle_CalculateBandwidth(arg->bundle);
+
+ return 0;
+}
+
+int
+mp_ShowStatus(struct cmdargs const *arg)
+{
+ struct mp *mp = &arg->bundle->ncp.mp;
+
+ prompt_Printf(arg->prompt, "Multilink is %sactive\n", mp->active ? "" : "in");
+ if (mp->active) {
+ struct mbuf *m, *lm;
+ int bufs = 0;
+
+ lm = NULL;
+ prompt_Printf(arg->prompt, "Socket: %s\n",
+ mp->server.socket.sun_path);
+ for (m = mp->inbufs; m; m = m->m_nextpkt) {
+ bufs++;
+ lm = m;
+ }
+ prompt_Printf(arg->prompt, "Pending frags: %d", bufs);
+ if (bufs) {
+ struct mp_header mh;
+ unsigned long first, last;
+
+ first = mp_ReadHeader(mp, mp->inbufs, &mh) ? mh.seq : 0;
+ last = mp_ReadHeader(mp, lm, &mh) ? mh.seq : 0;
+ prompt_Printf(arg->prompt, " (Have %lu - %lu, want %lu, lowest %lu)\n",
+ first, last, (unsigned long)mp->seq.next_in,
+ (unsigned long)mp->seq.min_in);
+ prompt_Printf(arg->prompt, " First has %sbegin bit and "
+ "%send bit", mh.begin ? "" : "no ", mh.end ? "" : "no ");
+ }
+ prompt_Printf(arg->prompt, "\n");
+ }
+
+ prompt_Printf(arg->prompt, "\nMy Side:\n");
+ if (mp->active) {
+ prompt_Printf(arg->prompt, " Output SEQ: %u\n", mp->out.seq);
+ prompt_Printf(arg->prompt, " MRRU: %u\n", mp->local_mrru);
+ prompt_Printf(arg->prompt, " Short Seq: %s\n",
+ mp->local_is12bit ? "on" : "off");
+ }
+ prompt_Printf(arg->prompt, " Discriminator: %s\n",
+ mp_Enddisc(mp->cfg.enddisc.class, mp->cfg.enddisc.address,
+ mp->cfg.enddisc.len));
+
+ prompt_Printf(arg->prompt, "\nHis Side:\n");
+ if (mp->active) {
+ prompt_Printf(arg->prompt, " Auth Name: %s\n", mp->peer.authname);
+ prompt_Printf(arg->prompt, " Input SEQ: %u\n", mp->seq.next_in);
+ prompt_Printf(arg->prompt, " MRRU: %u\n", mp->peer_mrru);
+ prompt_Printf(arg->prompt, " Short Seq: %s\n",
+ mp->peer_is12bit ? "on" : "off");
+ }
+ prompt_Printf(arg->prompt, " Discriminator: %s\n",
+ mp_Enddisc(mp->peer.enddisc.class, mp->peer.enddisc.address,
+ mp->peer.enddisc.len));
+
+ prompt_Printf(arg->prompt, "\nDefaults:\n");
+
+ prompt_Printf(arg->prompt, " MRRU: ");
+ if (mp->cfg.mrru)
+ prompt_Printf(arg->prompt, "%d (multilink enabled)\n", mp->cfg.mrru);
+ else
+ prompt_Printf(arg->prompt, "disabled\n");
+ prompt_Printf(arg->prompt, " Short Seq: %s\n",
+ command_ShowNegval(mp->cfg.shortseq));
+ prompt_Printf(arg->prompt, " Discriminator: %s\n",
+ command_ShowNegval(mp->cfg.negenddisc));
+ prompt_Printf(arg->prompt, " AutoLoad: min %d%%, max %d%%,"
+ " period %d secs\n", mp->cfg.autoload.min,
+ mp->cfg.autoload.max, mp->cfg.autoload.period);
+
+ return 0;
+}
+
+const char *
+mp_Enddisc(u_char c, const char *address, size_t len)
+{
+ static char result[100]; /* Used immediately after it's returned */
+ unsigned f, header;
+
+ switch (c) {
+ case ENDDISC_NULL:
+ sprintf(result, "Null Class");
+ break;
+
+ case ENDDISC_LOCAL:
+ snprintf(result, sizeof result, "Local Addr: %.*s", (int)len,
+ address);
+ break;
+
+ case ENDDISC_IP:
+ if (len == 4)
+ snprintf(result, sizeof result, "IP %s",
+ inet_ntoa(*(const struct in_addr *)address));
+ else
+ sprintf(result, "IP[%zd] ???", len);
+ break;
+
+ case ENDDISC_MAC:
+ if (len == 6) {
+ const u_char *m = (const u_char *)address;
+ snprintf(result, sizeof result, "MAC %02x:%02x:%02x:%02x:%02x:%02x",
+ m[0], m[1], m[2], m[3], m[4], m[5]);
+ } else
+ sprintf(result, "MAC[%zd] ???", len);
+ break;
+
+ case ENDDISC_MAGIC:
+ sprintf(result, "Magic: 0x");
+ header = strlen(result);
+ if (len + header + 1 > sizeof result)
+ len = sizeof result - header - 1;
+ for (f = 0; f < len; f++)
+ sprintf(result + header + 2 * f, "%02x", address[f]);
+ break;
+
+ case ENDDISC_PSN:
+ snprintf(result, sizeof result, "PSN: %.*s", (int)len, address);
+ break;
+
+ default:
+ sprintf(result, "%d: ", (int)c);
+ header = strlen(result);
+ if (len + header + 1 > sizeof result)
+ len = sizeof result - header - 1;
+ for (f = 0; f < len; f++)
+ sprintf(result + header + 2 * f, "%02x", address[f]);
+ break;
+ }
+ return result;
+}
+
+int
+mp_SetEnddisc(struct cmdargs const *arg)
+{
+ struct mp *mp = &arg->bundle->ncp.mp;
+ struct in_addr addr;
+
+ switch (bundle_Phase(arg->bundle)) {
+ case PHASE_DEAD:
+ break;
+ case PHASE_ESTABLISH:
+ /* Make sure none of our links are DATALINK_LCP or greater */
+ if (bundle_HighestState(arg->bundle) >= DATALINK_LCP) {
+ log_Printf(LogWARN, "enddisc: Only changable before"
+ " LCP negotiations\n");
+ return 1;
+ }
+ break;
+ default:
+ log_Printf(LogWARN, "enddisc: Only changable at phase DEAD/ESTABLISH\n");
+ return 1;
+ }
+
+ if (arg->argc == arg->argn) {
+ mp->cfg.enddisc.class = 0;
+ *mp->cfg.enddisc.address = '\0';
+ mp->cfg.enddisc.len = 0;
+ } else if (arg->argc > arg->argn) {
+ if (!strcasecmp(arg->argv[arg->argn], "label")) {
+ mp->cfg.enddisc.class = ENDDISC_LOCAL;
+ strcpy(mp->cfg.enddisc.address, arg->bundle->cfg.label);
+ mp->cfg.enddisc.len = strlen(mp->cfg.enddisc.address);
+ } else if (!strcasecmp(arg->argv[arg->argn], "ip")) {
+ if (arg->bundle->ncp.ipcp.my_ip.s_addr == INADDR_ANY)
+ ncprange_getip4addr(&arg->bundle->ncp.ipcp.cfg.my_range, &addr);
+ else
+ addr = arg->bundle->ncp.ipcp.my_ip;
+ memcpy(mp->cfg.enddisc.address, &addr.s_addr, sizeof addr.s_addr);
+ mp->cfg.enddisc.class = ENDDISC_IP;
+ mp->cfg.enddisc.len = sizeof arg->bundle->ncp.ipcp.my_ip.s_addr;
+ } else if (!strcasecmp(arg->argv[arg->argn], "mac")) {
+ struct sockaddr_dl hwaddr;
+
+ if (arg->bundle->ncp.ipcp.my_ip.s_addr == INADDR_ANY)
+ ncprange_getip4addr(&arg->bundle->ncp.ipcp.cfg.my_range, &addr);
+ else
+ addr = arg->bundle->ncp.ipcp.my_ip;
+
+ if (arp_EtherAddr(addr, &hwaddr, 1)) {
+ mp->cfg.enddisc.class = ENDDISC_MAC;
+ memcpy(mp->cfg.enddisc.address, hwaddr.sdl_data + hwaddr.sdl_nlen,
+ hwaddr.sdl_alen);
+ mp->cfg.enddisc.len = hwaddr.sdl_alen;
+ } else {
+ log_Printf(LogWARN, "set enddisc: Can't locate MAC address for %s\n",
+ inet_ntoa(addr));
+ return 4;
+ }
+ } else if (!strcasecmp(arg->argv[arg->argn], "magic")) {
+ int f;
+
+ randinit();
+ for (f = 0; f < 20; f += sizeof(long))
+ *(long *)(mp->cfg.enddisc.address + f) = random();
+ mp->cfg.enddisc.class = ENDDISC_MAGIC;
+ mp->cfg.enddisc.len = 20;
+ } else if (!strcasecmp(arg->argv[arg->argn], "psn")) {
+ if (arg->argc > arg->argn+1) {
+ mp->cfg.enddisc.class = ENDDISC_PSN;
+ strcpy(mp->cfg.enddisc.address, arg->argv[arg->argn+1]);
+ mp->cfg.enddisc.len = strlen(mp->cfg.enddisc.address);
+ } else {
+ log_Printf(LogWARN, "PSN endpoint requires additional data\n");
+ return 5;
+ }
+ } else {
+ log_Printf(LogWARN, "%s: Unrecognised endpoint type\n",
+ arg->argv[arg->argn]);
+ return 6;
+ }
+ }
+
+ return 0;
+}
+
+static int
+mpserver_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e,
+ int *n)
+{
+ struct mpserver *s = descriptor2mpserver(d);
+ int result;
+
+ result = 0;
+ if (s->send.dl != NULL) {
+ /* We've connect()ed */
+ if (!link_QueueLen(&s->send.dl->physical->link) &&
+ !s->send.dl->physical->out) {
+ /* Only send if we've transmitted all our data (i.e. the ConfigAck) */
+ result -= datalink_RemoveFromSet(s->send.dl, r, w, e);
+ bundle_SendDatalink(s->send.dl, s->fd, &s->socket);
+ s->send.dl = NULL;
+ s->fd = -1;
+ } else
+ /* Never read from a datalink that's on death row ! */
+ result -= datalink_RemoveFromSet(s->send.dl, r, NULL, NULL);
+ } else if (r && s->fd >= 0) {
+ if (*n < s->fd + 1)
+ *n = s->fd + 1;
+ FD_SET(s->fd, r);
+ log_Printf(LogTIMER, "mp: fdset(r) %d\n", s->fd);
+ result++;
+ }
+ return result;
+}
+
+static int
+mpserver_IsSet(struct fdescriptor *d, const fd_set *fdset)
+{
+ struct mpserver *s = descriptor2mpserver(d);
+ return s->fd >= 0 && FD_ISSET(s->fd, fdset);
+}
+
+static void
+mpserver_Read(struct fdescriptor *d, struct bundle *bundle,
+ const fd_set *fdset __unused)
+{
+ struct mpserver *s = descriptor2mpserver(d);
+
+ bundle_ReceiveDatalink(bundle, s->fd);
+}
+
+static int
+mpserver_Write(struct fdescriptor *d __unused, struct bundle *bundle __unused,
+ const fd_set *fdset __unused)
+{
+ /* We never want to write here ! */
+ log_Printf(LogALERT, "mpserver_Write: Internal error: Bad call !\n");
+ return 0;
+}
+
+void
+mpserver_Init(struct mpserver *s)
+{
+ s->desc.type = MPSERVER_DESCRIPTOR;
+ s->desc.UpdateSet = mpserver_UpdateSet;
+ s->desc.IsSet = mpserver_IsSet;
+ s->desc.Read = mpserver_Read;
+ s->desc.Write = mpserver_Write;
+ s->send.dl = NULL;
+ s->fd = -1;
+ memset(&s->socket, '\0', sizeof s->socket);
+}
+
+int
+mpserver_Open(struct mpserver *s, struct peerid *peer)
+{
+ int f, l;
+ mode_t mask;
+
+ if (s->fd != -1) {
+ log_Printf(LogALERT, "Internal error ! mpserver already open\n");
+ mpserver_Close(s);
+ }
+
+ l = snprintf(s->socket.sun_path, sizeof s->socket.sun_path, "%sppp-%s-%02x-",
+ _PATH_VARRUN, peer->authname, peer->enddisc.class);
+ if (l < 0) {
+ log_Printf(LogERROR, "mpserver: snprintf(): %s\n", strerror(errno));
+ return MPSERVER_FAILED;
+ }
+
+ for (f = 0;
+ f < peer->enddisc.len && (size_t)l < sizeof s->socket.sun_path - 2;
+ f++) {
+ snprintf(s->socket.sun_path + l, sizeof s->socket.sun_path - l,
+ "%02x", *(u_char *)(peer->enddisc.address+f));
+ l += 2;
+ }
+
+ s->socket.sun_family = AF_LOCAL;
+ s->socket.sun_len = sizeof s->socket;
+ s->fd = ID0socket(PF_LOCAL, SOCK_DGRAM, 0);
+ if (s->fd < 0) {
+ log_Printf(LogERROR, "mpserver: socket(): %s\n", strerror(errno));
+ return MPSERVER_FAILED;
+ }
+
+ setsockopt(s->fd, SOL_SOCKET, SO_REUSEADDR, (struct sockaddr *)&s->socket,
+ sizeof s->socket);
+ mask = umask(0177);
+
+ /*
+ * Try to bind the socket. If we succeed we play server, if we fail
+ * we connect() and hand the link off.
+ */
+
+ if (ID0bind_un(s->fd, &s->socket) < 0) {
+ if (errno != EADDRINUSE) {
+ log_Printf(LogPHASE, "mpserver: can't create bundle socket %s (%s)\n",
+ s->socket.sun_path, strerror(errno));
+ umask(mask);
+ close(s->fd);
+ s->fd = -1;
+ return MPSERVER_FAILED;
+ }
+
+ /* So we're the sender */
+ umask(mask);
+ if (ID0connect_un(s->fd, &s->socket) < 0) {
+ log_Printf(LogPHASE, "mpserver: can't connect to bundle socket %s (%s)\n",
+ s->socket.sun_path, strerror(errno));
+ if (errno == ECONNREFUSED)
+ log_Printf(LogPHASE, " The previous server died badly !\n");
+ close(s->fd);
+ s->fd = -1;
+ return MPSERVER_FAILED;
+ }
+
+ /* Donate our link to the other guy */
+ return MPSERVER_CONNECTED;
+ }
+
+ return MPSERVER_LISTENING;
+}
+
+void
+mpserver_Close(struct mpserver *s)
+{
+ if (s->send.dl != NULL) {
+ bundle_SendDatalink(s->send.dl, s->fd, &s->socket);
+ s->send.dl = NULL;
+ s->fd = -1;
+ } else if (s->fd >= 0) {
+ close(s->fd);
+ if (ID0unlink(s->socket.sun_path) == -1)
+ log_Printf(LogERROR, "%s: Failed to remove: %s\n", s->socket.sun_path,
+ strerror(errno));
+ memset(&s->socket, '\0', sizeof s->socket);
+ s->fd = -1;
+ }
+}
+
+void
+mp_LinkLost(struct mp *mp, struct datalink *dl)
+{
+ if (mp->seq.min_in == dl->mp.seq)
+ /* We've lost the link that's holding everything up ! */
+ mp_Assemble(mp, NULL, NULL);
+}
+
+size_t
+mp_QueueLen(struct mp *mp)
+{
+ return link_QueueLen(&mp->link);
+}
diff --git a/usr.sbin/ppp/mp.h b/usr.sbin/ppp/mp.h
new file mode 100644
index 0000000..7cf5a8e
--- /dev/null
+++ b/usr.sbin/ppp/mp.h
@@ -0,0 +1,146 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct mbuf;
+struct physical;
+struct bundle;
+struct cmdargs;
+struct datalink;
+
+#define ENDDISC_NULL 0
+#define ENDDISC_LOCAL 1
+#define ENDDISC_IP 2
+#define ENDDISC_MAC 3
+#define ENDDISC_MAGIC 4
+#define ENDDISC_PSN 5
+
+#define MP_LINKSENT 0 /* We attached the link to another ppp */
+#define MP_UP 1 /* We've started MP */
+#define MP_ADDED 2 /* We've added the link to our MP */
+#define MP_FAILED 3 /* No go */
+
+#define MPSERVER_CONNECTED 0
+#define MPSERVER_LISTENING 1
+#define MPSERVER_FAILED 2
+
+struct enddisc {
+ u_char class;
+ char address[50];
+ int len;
+};
+
+struct peerid {
+ struct enddisc enddisc; /* Peers endpoint discriminator */
+ char authname[AUTHLEN]; /* Peers name (authenticated) */
+};
+
+struct mpserver {
+ struct fdescriptor desc;
+ int fd; /* listen()ing or connect()ing here */
+ struct sockaddr_un socket; /* On this socket */
+
+ struct {
+ struct datalink *dl; /* Send this datalink */
+ } send; /* (in UpdateSet()) */
+};
+
+struct mp {
+ struct link link;
+
+ unsigned active : 1;
+ unsigned peer_is12bit : 1; /* 12/24bit seq nos */
+ unsigned local_is12bit : 1;
+ u_short peer_mrru;
+ u_short local_mrru;
+
+ struct peerid peer; /* Who are we talking to */
+ struct mpserver server; /* Our ``sharing'' socket */
+
+ struct {
+ u_int32_t seq; /* next outgoing seq */
+ int link; /* Next link to send on */
+ int af; /* Next address family to send */
+ } out;
+
+ struct {
+ u_int32_t min_in; /* minimum received incoming seq */
+ u_int32_t next_in; /* next incoming seq to process */
+ } seq;
+
+ struct {
+ u_short mrru; /* Max Reconstructed Receive Unit */
+ unsigned shortseq : 2; /* I want short Sequence Numbers */
+ unsigned negenddisc : 2; /* I want an endpoint discriminator */
+ struct enddisc enddisc; /* endpoint discriminator */
+ struct {
+ int min; /* Lowest percent of bundle->bandwidth */
+ int max; /* Highest percent of bundle->bandwidth out */
+ int period; /* link->throughput sample period */
+ } autoload;
+ } cfg;
+
+ struct mbuf *inbufs; /* Received fragments */
+ struct fsm_parent fsmp; /* Our callback functions */
+ struct bundle *bundle; /* Parent */
+};
+
+struct mp_link {
+ u_int32_t seq; /* 12 or 24 bit incoming seq */
+ unsigned bandwidth; /* Our link bandwidth (or zero) */
+};
+
+struct mp_header {
+ unsigned begin : 1;
+ unsigned end : 1;
+ u_int32_t seq;
+};
+
+#define descriptor2mpserver(d) \
+ ((d)->type == MPSERVER_DESCRIPTOR ? (struct mpserver *)(d) : NULL)
+#define mpserver_IsOpen(s) ((s)->fd != -1)
+
+extern void peerid_Init(struct peerid *);
+extern int peerid_Equal(const struct peerid *, const struct peerid *);
+extern void mpserver_Init(struct mpserver *);
+extern int mpserver_Open(struct mpserver *, struct peerid *);
+extern void mpserver_Close(struct mpserver *);
+extern void mp_Init(struct mp *, struct bundle *);
+extern void mp_linkInit(struct mp_link *);
+extern int mp_Up(struct mp *, struct datalink *);
+extern void mp_Down(struct mp *);
+extern struct mbuf *mp_Input(struct bundle *, struct link *, struct mbuf *);
+extern int mp_FillPhysicalQueues(struct bundle *);
+extern int mp_SetDatalinkBandwidth(struct cmdargs const *);
+extern int mp_ShowStatus(struct cmdargs const *);
+extern const char *mp_Enddisc(u_char, const char *, size_t);
+extern int mp_SetEnddisc(struct cmdargs const *);
+extern void mp_LinkLost(struct mp *, struct datalink *);
+extern void mp_RestartAutoloadTimer(struct mp *);
+extern void mp_CheckAutoloadTimer(struct mp *);
+extern void mp_StopAutoloadTimer(struct mp *);
+extern size_t mp_QueueLen(struct mp *);
diff --git a/usr.sbin/ppp/mppe.c b/usr.sbin/ppp/mppe.c
new file mode 100644
index 0000000..141574f
--- /dev/null
+++ b/usr.sbin/ppp/mppe.c
@@ -0,0 +1,817 @@
+/*-
+ * Copyright (c) 2000 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.
+ *
+ * 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/socket.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <sys/un.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <openssl/rc4.h>
+
+#include "defs.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "throughput.h"
+#include "layer.h"
+#include "link.h"
+#include "chap_ms.h"
+#include "proto.h"
+#include "mppe.h"
+#include "ua.h"
+#include "descriptor.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ncpaddr.h"
+#include "iplist.h"
+#include "slcompress.h"
+#include "ipcp.h"
+#include "ipv6cp.h"
+#include "filter.h"
+#include "mp.h"
+#include "ncp.h"
+#include "bundle.h"
+
+/*
+ * Documentation:
+ *
+ * draft-ietf-pppext-mppe-04.txt
+ * draft-ietf-pppext-mppe-keys-02.txt
+ */
+
+#define MPPE_OPT_STATELESS 0x1000000
+#define MPPE_OPT_COMPRESSED 0x01
+#define MPPE_OPT_40BIT 0x20
+#define MPPE_OPT_56BIT 0x80
+#define MPPE_OPT_128BIT 0x40
+#define MPPE_OPT_BITMASK 0xe0
+#define MPPE_OPT_MASK (MPPE_OPT_STATELESS | MPPE_OPT_BITMASK)
+
+#define MPPE_FLUSHED 0x8000
+#define MPPE_ENCRYPTED 0x1000
+#define MPPE_HEADER_BITMASK 0xf000
+#define MPPE_HEADER_FLAG 0x00ff
+#define MPPE_HEADER_FLAGMASK 0x00ff
+#define MPPE_HEADER_FLAGSHIFT 8
+#define MPPE_HEADER_STATEFUL_KEYCHANGES 16
+
+struct mppe_state {
+ unsigned stateless : 1;
+ unsigned flushnext : 1;
+ unsigned flushrequired : 1;
+ int cohnum;
+ unsigned keylen; /* 8 or 16 bytes */
+ int keybits; /* 40, 56 or 128 bits */
+ char sesskey[MPPE_KEY_LEN];
+ char mastkey[MPPE_KEY_LEN];
+ RC4_KEY rc4key;
+};
+
+int MPPE_MasterKeyValid = 0;
+int MPPE_IsServer = 0;
+char MPPE_MasterKey[MPPE_KEY_LEN];
+
+/*
+ * The peer has missed a packet. Mark the next output frame to be FLUSHED
+ */
+static int
+MPPEResetOutput(void *v)
+{
+ struct mppe_state *mop = (struct mppe_state *)v;
+
+ if (mop->stateless)
+ log_Printf(LogCCP, "MPPE: Unexpected output channel reset\n");
+ else {
+ log_Printf(LogCCP, "MPPE: Output channel reset\n");
+ mop->flushnext = 1;
+ }
+
+ return 0; /* Ask FSM not to ACK */
+}
+
+static void
+MPPEReduceSessionKey(struct mppe_state *mp)
+{
+ switch(mp->keybits) {
+ case 40:
+ mp->sesskey[2] = 0x9e;
+ mp->sesskey[1] = 0x26;
+ case 56:
+ mp->sesskey[0] = 0xd1;
+ case 128:
+ break;
+ }
+}
+
+static void
+MPPEKeyChange(struct mppe_state *mp)
+{
+ char InterimKey[MPPE_KEY_LEN];
+ RC4_KEY RC4Key;
+
+ GetNewKeyFromSHA(mp->mastkey, mp->sesskey, mp->keylen, InterimKey);
+ RC4_set_key(&RC4Key, mp->keylen, InterimKey);
+ RC4(&RC4Key, mp->keylen, InterimKey, mp->sesskey);
+
+ MPPEReduceSessionKey(mp);
+}
+
+static struct mbuf *
+MPPEOutput(void *v, struct ccp *ccp, struct link *l __unused, int pri __unused,
+ u_short *proto, struct mbuf *mp)
+{
+ struct mppe_state *mop = (struct mppe_state *)v;
+ struct mbuf *mo;
+ u_short nproto, prefix;
+ int dictinit, ilen, len;
+ char *rp;
+
+ ilen = m_length(mp);
+ dictinit = 0;
+
+ log_Printf(LogDEBUG, "MPPE: Output: Proto %02x (%d bytes)\n", *proto, ilen);
+ if (*proto < 0x21 && *proto > 0xFA) {
+ log_Printf(LogDEBUG, "MPPE: Output: Not encrypting\n");
+ ccp->compout += ilen;
+ ccp->uncompout += ilen;
+ return mp;
+ }
+
+ log_DumpBp(LogDEBUG, "MPPE: Output: Encrypt packet:", mp);
+
+ /* Get mbuf for prefixes */
+ mo = m_get(4, MB_CCPOUT);
+ mo->m_next = mp;
+
+ rp = MBUF_CTOP(mo);
+ prefix = MPPE_ENCRYPTED | mop->cohnum;
+
+ if (mop->stateless ||
+ (mop->cohnum & MPPE_HEADER_FLAGMASK) == MPPE_HEADER_FLAG) {
+ /* Change our key */
+ log_Printf(LogDEBUG, "MPPEOutput: Key changed [%d]\n", mop->cohnum);
+ MPPEKeyChange(mop);
+ dictinit = 1;
+ }
+
+ if (mop->stateless || mop->flushnext) {
+ prefix |= MPPE_FLUSHED;
+ dictinit = 1;
+ mop->flushnext = 0;
+ }
+
+ if (dictinit) {
+ /* Initialise our dictionary */
+ log_Printf(LogDEBUG, "MPPEOutput: Dictionary initialised [%d]\n",
+ mop->cohnum);
+ RC4_set_key(&mop->rc4key, mop->keylen, mop->sesskey);
+ }
+
+ /* Set MPPE packet prefix */
+ ua_htons(&prefix, rp);
+
+ /* Save encrypted protocol number */
+ nproto = htons(*proto);
+ RC4(&mop->rc4key, 2, (char *)&nproto, rp + 2);
+
+ /* Encrypt main packet */
+ rp = MBUF_CTOP(mp);
+ RC4(&mop->rc4key, ilen, rp, rp);
+
+ mop->cohnum++;
+ mop->cohnum &= ~MPPE_HEADER_BITMASK;
+
+ /* Set the protocol number */
+ *proto = ccp_Proto(ccp);
+ len = m_length(mo);
+ ccp->uncompout += ilen;
+ ccp->compout += len;
+
+ log_Printf(LogDEBUG, "MPPE: Output: Encrypted: Proto %02x (%d bytes)\n",
+ *proto, len);
+
+ return mo;
+}
+
+static void
+MPPEResetInput(void *v __unused)
+{
+ log_Printf(LogCCP, "MPPE: Unexpected input channel ack\n");
+}
+
+static struct mbuf *
+MPPEInput(void *v, struct ccp *ccp, u_short *proto, struct mbuf *mp)
+{
+ struct mppe_state *mip = (struct mppe_state *)v;
+ u_short prefix;
+ char *rp;
+ int dictinit, flushed, ilen, len, n;
+
+ ilen = m_length(mp);
+ dictinit = 0;
+ ccp->compin += ilen;
+
+ log_Printf(LogDEBUG, "MPPE: Input: Proto %02x (%d bytes)\n", *proto, ilen);
+ log_DumpBp(LogDEBUG, "MPPE: Input: Packet:", mp);
+
+ mp = mbuf_Read(mp, &prefix, 2);
+ prefix = ntohs(prefix);
+ flushed = prefix & MPPE_FLUSHED;
+ prefix &= ~flushed;
+ if ((prefix & MPPE_HEADER_BITMASK) != MPPE_ENCRYPTED) {
+ log_Printf(LogERROR, "MPPE: Input: Invalid packet (flags = 0x%x)\n",
+ (prefix & MPPE_HEADER_BITMASK) | flushed);
+ m_freem(mp);
+ return NULL;
+ }
+
+ prefix &= ~MPPE_HEADER_BITMASK;
+
+ if (!flushed && mip->stateless) {
+ log_Printf(LogCCP, "MPPEInput: Packet without MPPE_FLUSHED set"
+ " in stateless mode\n");
+ flushed = MPPE_FLUSHED;
+ /* Should we really continue ? */
+ }
+
+ if (mip->stateless) {
+ /* Change our key for each missed packet in stateless mode */
+ while (prefix != mip->cohnum) {
+ log_Printf(LogDEBUG, "MPPEInput: Key changed [%u]\n", prefix);
+ MPPEKeyChange(mip);
+ /*
+ * mip->cohnum contains what we received last time in stateless
+ * mode.
+ */
+ mip->cohnum++;
+ mip->cohnum &= ~MPPE_HEADER_BITMASK;
+ }
+ dictinit = 1;
+ } else {
+ if (flushed) {
+ /*
+ * We can always process a flushed packet.
+ * Catch up on any outstanding key changes.
+ */
+ n = (prefix >> MPPE_HEADER_FLAGSHIFT) -
+ (mip->cohnum >> MPPE_HEADER_FLAGSHIFT);
+ if (n < 0)
+ n += MPPE_HEADER_STATEFUL_KEYCHANGES;
+ while (n--) {
+ log_Printf(LogDEBUG, "MPPEInput: Key changed during catchup [%u]\n",
+ prefix);
+ MPPEKeyChange(mip);
+ }
+ mip->flushrequired = 0;
+ mip->cohnum = prefix;
+ dictinit = 1;
+ }
+
+ if (mip->flushrequired) {
+ /*
+ * Perhaps we should be lenient if
+ * (prefix & MPPE_HEADER_FLAGMASK) == MPPE_HEADER_FLAG
+ * The spec says that we shouldn't be though....
+ */
+ log_Printf(LogDEBUG, "MPPE: Not flushed - discarded\n");
+ fsm_Output(&ccp->fsm, CODE_RESETREQ, ccp->fsm.reqid++, NULL, 0,
+ MB_CCPOUT);
+ m_freem(mp);
+ return NULL;
+ }
+
+ if (prefix != mip->cohnum) {
+ /*
+ * We're in stateful mode and didn't receive the expected
+ * packet. Send a reset request, but don't tell the CCP layer
+ * about it as we don't expect to receive a Reset ACK !
+ * Guess what... M$ invented this !
+ */
+ log_Printf(LogCCP, "MPPE: Input: Got seq %u, not %u\n",
+ prefix, mip->cohnum);
+ fsm_Output(&ccp->fsm, CODE_RESETREQ, ccp->fsm.reqid++, NULL, 0,
+ MB_CCPOUT);
+ mip->flushrequired = 1;
+ m_freem(mp);
+ return NULL;
+ }
+
+ if ((prefix & MPPE_HEADER_FLAGMASK) == MPPE_HEADER_FLAG) {
+ log_Printf(LogDEBUG, "MPPEInput: Key changed [%u]\n", prefix);
+ MPPEKeyChange(mip);
+ dictinit = 1;
+ } else if (flushed)
+ dictinit = 1;
+
+ /*
+ * mip->cohnum contains what we expect to receive next time in stateful
+ * mode.
+ */
+ mip->cohnum++;
+ mip->cohnum &= ~MPPE_HEADER_BITMASK;
+ }
+
+ if (dictinit) {
+ log_Printf(LogDEBUG, "MPPEInput: Dictionary initialised [%u]\n", prefix);
+ RC4_set_key(&mip->rc4key, mip->keylen, mip->sesskey);
+ }
+
+ mp = mbuf_Read(mp, proto, 2);
+ RC4(&mip->rc4key, 2, (char *)proto, (char *)proto);
+ *proto = ntohs(*proto);
+
+ rp = MBUF_CTOP(mp);
+ len = m_length(mp);
+ RC4(&mip->rc4key, len, rp, rp);
+
+ log_Printf(LogDEBUG, "MPPEInput: Decrypted: Proto %02x (%d bytes)\n",
+ *proto, len);
+ log_DumpBp(LogDEBUG, "MPPEInput: Decrypted: Packet:", mp);
+
+ ccp->uncompin += len;
+
+ return mp;
+}
+
+static void
+MPPEDictSetup(void *v __unused, struct ccp *ccp __unused,
+ u_short proto __unused, struct mbuf *mp __unused)
+{
+ /* Nothing to see here */
+}
+
+static const char *
+MPPEDispOpts(struct fsm_opt *o)
+{
+ static char buf[70];
+ u_int32_t val;
+ char ch;
+ int len, n;
+
+ ua_ntohl(o->data, &val);
+ len = 0;
+ if ((n = snprintf(buf, sizeof buf, "value 0x%08x ", (unsigned)val)) > 0)
+ len += n;
+ if (!(val & MPPE_OPT_BITMASK)) {
+ if ((n = snprintf(buf + len, sizeof buf - len, "(0")) > 0)
+ len += n;
+ } else {
+ ch = '(';
+ if (val & MPPE_OPT_128BIT) {
+ if ((n = snprintf(buf + len, sizeof buf - len, "%c128", ch)) > 0)
+ len += n;
+ ch = '/';
+ }
+ if (val & MPPE_OPT_56BIT) {
+ if ((n = snprintf(buf + len, sizeof buf - len, "%c56", ch)) > 0)
+ len += n;
+ ch = '/';
+ }
+ if (val & MPPE_OPT_40BIT) {
+ if ((n = snprintf(buf + len, sizeof buf - len, "%c40", ch)) > 0)
+ len += n;
+ ch = '/';
+ }
+ }
+
+ if ((n = snprintf(buf + len, sizeof buf - len, " bits, state%s",
+ (val & MPPE_OPT_STATELESS) ? "less" : "ful")) > 0)
+ len += n;
+
+ if (val & MPPE_OPT_COMPRESSED) {
+ if ((n = snprintf(buf + len, sizeof buf - len, ", compressed")) > 0)
+ len += n;
+ }
+
+ snprintf(buf + len, sizeof buf - len, ")");
+
+ return buf;
+}
+
+static int
+MPPEUsable(struct fsm *fp)
+{
+ int ok;
+#ifndef NORADIUS
+ struct radius *r = &fp->bundle->radius;
+
+ /*
+ * If the radius server gave us RAD_MICROSOFT_MS_MPPE_ENCRYPTION_TYPES,
+ * use that instead of our configuration value.
+ */
+ if (*r->cfg.file) {
+ ok = r->mppe.sendkeylen && r->mppe.recvkeylen;
+ if (!ok)
+ log_Printf(LogCCP, "MPPE: Not permitted by RADIUS server\n");
+ } else
+#endif
+ {
+ struct lcp *lcp = &fp->link->lcp;
+ ok = (lcp->want_auth == PROTO_CHAP && lcp->want_authtype == 0x81) ||
+ (lcp->his_auth == PROTO_CHAP && lcp->his_authtype == 0x81);
+ if (!ok)
+ log_Printf(LogCCP, "MPPE: Not usable without CHAP81\n");
+ }
+
+ return ok;
+}
+
+static int
+MPPERequired(struct fsm *fp)
+{
+#ifndef NORADIUS
+ /*
+ * If the radius server gave us RAD_MICROSOFT_MS_MPPE_ENCRYPTION_POLICY,
+ * use that instead of our configuration value.
+ */
+ if (*fp->bundle->radius.cfg.file && fp->bundle->radius.mppe.policy)
+ return fp->bundle->radius.mppe.policy == MPPE_POLICY_REQUIRED ? 1 : 0;
+#endif
+
+ return fp->link->ccp.cfg.mppe.required;
+}
+
+static u_int32_t
+MPPE_ConfigVal(struct bundle *bundle __unused, const struct ccp_config *cfg)
+{
+ u_int32_t val;
+
+ val = cfg->mppe.state == MPPE_STATELESS ? MPPE_OPT_STATELESS : 0;
+#ifndef NORADIUS
+ /*
+ * If the radius server gave us RAD_MICROSOFT_MS_MPPE_ENCRYPTION_TYPES,
+ * use that instead of our configuration value.
+ */
+ if (*bundle->radius.cfg.file && bundle->radius.mppe.types) {
+ if (bundle->radius.mppe.types & MPPE_TYPE_40BIT)
+ val |= MPPE_OPT_40BIT;
+ if (bundle->radius.mppe.types & MPPE_TYPE_128BIT)
+ val |= MPPE_OPT_128BIT;
+ } else
+#endif
+ switch(cfg->mppe.keybits) {
+ case 128:
+ val |= MPPE_OPT_128BIT;
+ break;
+ case 56:
+ val |= MPPE_OPT_56BIT;
+ break;
+ case 40:
+ val |= MPPE_OPT_40BIT;
+ break;
+ case 0:
+ val |= MPPE_OPT_128BIT | MPPE_OPT_56BIT | MPPE_OPT_40BIT;
+ break;
+ }
+
+ return val;
+}
+
+/*
+ * What options should we use for our first configure request
+ */
+static void
+MPPEInitOptsOutput(struct bundle *bundle, struct fsm_opt *o,
+ const struct ccp_config *cfg)
+{
+ u_int32_t mval;
+
+ o->hdr.len = 6;
+
+ if (!MPPE_MasterKeyValid) {
+ log_Printf(LogCCP, "MPPE: MasterKey is invalid,"
+ " MPPE is available only with CHAP81 authentication\n");
+ mval = 0;
+ ua_htonl(&mval, o->data);
+ return;
+ }
+
+
+ mval = MPPE_ConfigVal(bundle, cfg);
+ ua_htonl(&mval, o->data);
+}
+
+/*
+ * Our CCP request was NAK'd with the given options
+ */
+static int
+MPPESetOptsOutput(struct bundle *bundle, struct fsm_opt *o,
+ const struct ccp_config *cfg)
+{
+ u_int32_t mval, peer;
+
+ ua_ntohl(o->data, &peer);
+
+ if (!MPPE_MasterKeyValid)
+ /* Treat their NAK as a REJ */
+ return MODE_NAK;
+
+ mval = MPPE_ConfigVal(bundle, cfg);
+
+ /*
+ * If we haven't been configured with a specific number of keybits, allow
+ * whatever the peer asks for.
+ */
+ if (!cfg->mppe.keybits) {
+ mval &= ~MPPE_OPT_BITMASK;
+ mval |= (peer & MPPE_OPT_BITMASK);
+ if (!(mval & MPPE_OPT_BITMASK))
+ mval |= MPPE_OPT_128BIT;
+ }
+
+ /* Adjust our statelessness */
+ if (cfg->mppe.state == MPPE_ANYSTATE) {
+ mval &= ~MPPE_OPT_STATELESS;
+ mval |= (peer & MPPE_OPT_STATELESS);
+ }
+
+ ua_htonl(&mval, o->data);
+
+ return MODE_ACK;
+}
+
+/*
+ * The peer has requested the given options
+ */
+static int
+MPPESetOptsInput(struct bundle *bundle, struct fsm_opt *o,
+ const struct ccp_config *cfg)
+{
+ u_int32_t mval, peer;
+ int res = MODE_ACK;
+
+ ua_ntohl(o->data, &peer);
+ if (!MPPE_MasterKeyValid) {
+ if (peer != 0) {
+ peer = 0;
+ ua_htonl(&peer, o->data);
+ return MODE_NAK;
+ } else
+ return MODE_ACK;
+ }
+
+ mval = MPPE_ConfigVal(bundle, cfg);
+
+ if (peer & ~MPPE_OPT_MASK)
+ /* He's asking for bits we don't know about */
+ res = MODE_NAK;
+
+ if (peer & MPPE_OPT_STATELESS) {
+ if (cfg->mppe.state == MPPE_STATEFUL)
+ /* Peer can't have stateless */
+ res = MODE_NAK;
+ else
+ /* Peer wants stateless, that's ok */
+ mval |= MPPE_OPT_STATELESS;
+ } else {
+ if (cfg->mppe.state == MPPE_STATELESS)
+ /* Peer must have stateless */
+ res = MODE_NAK;
+ else
+ /* Peer doesn't want stateless, that's ok */
+ mval &= ~MPPE_OPT_STATELESS;
+ }
+
+ /* If we've got a configured number of keybits - the peer must use that */
+ if (cfg->mppe.keybits) {
+ ua_htonl(&mval, o->data);
+ return peer == mval ? res : MODE_NAK;
+ }
+
+ /* If a specific number of bits hasn't been requested, we'll need to NAK */
+ switch (peer & MPPE_OPT_BITMASK) {
+ case MPPE_OPT_128BIT:
+ case MPPE_OPT_56BIT:
+ case MPPE_OPT_40BIT:
+ break;
+ default:
+ res = MODE_NAK;
+ }
+
+ /* Suggest the best number of bits */
+ mval &= ~MPPE_OPT_BITMASK;
+ if (peer & MPPE_OPT_128BIT)
+ mval |= MPPE_OPT_128BIT;
+ else if (peer & MPPE_OPT_56BIT)
+ mval |= MPPE_OPT_56BIT;
+ else if (peer & MPPE_OPT_40BIT)
+ mval |= MPPE_OPT_40BIT;
+ else
+ mval |= MPPE_OPT_128BIT;
+ ua_htonl(&mval, o->data);
+
+ return res;
+}
+
+static struct mppe_state *
+MPPE_InitState(struct fsm_opt *o)
+{
+ struct mppe_state *mp;
+ u_int32_t val;
+
+ if ((mp = calloc(1, sizeof *mp)) != NULL) {
+ ua_ntohl(o->data, &val);
+
+ switch (val & MPPE_OPT_BITMASK) {
+ case MPPE_OPT_128BIT:
+ mp->keylen = 16;
+ mp->keybits = 128;
+ break;
+ case MPPE_OPT_56BIT:
+ mp->keylen = 8;
+ mp->keybits = 56;
+ break;
+ case MPPE_OPT_40BIT:
+ mp->keylen = 8;
+ mp->keybits = 40;
+ break;
+ default:
+ log_Printf(LogWARN, "Unexpected MPPE options 0x%08x\n", val);
+ free(mp);
+ return NULL;
+ }
+
+ mp->stateless = !!(val & MPPE_OPT_STATELESS);
+ }
+
+ return mp;
+}
+
+static void *
+MPPEInitInput(struct bundle *bundle __unused, struct fsm_opt *o)
+{
+ struct mppe_state *mip;
+
+ if (!MPPE_MasterKeyValid) {
+ log_Printf(LogWARN, "MPPE: Cannot initialise without CHAP81\n");
+ return NULL;
+ }
+
+ if ((mip = MPPE_InitState(o)) == NULL) {
+ log_Printf(LogWARN, "MPPEInput: Cannot initialise - unexpected options\n");
+ return NULL;
+ }
+
+ log_Printf(LogDEBUG, "MPPE: InitInput: %d-bits\n", mip->keybits);
+
+#ifndef NORADIUS
+ if (*bundle->radius.cfg.file && bundle->radius.mppe.recvkey) {
+ if (mip->keylen > bundle->radius.mppe.recvkeylen)
+ mip->keylen = bundle->radius.mppe.recvkeylen;
+ if (mip->keylen > sizeof mip->mastkey)
+ mip->keylen = sizeof mip->mastkey;
+ memcpy(mip->mastkey, bundle->radius.mppe.recvkey, mip->keylen);
+ } else
+#endif
+ GetAsymetricStartKey(MPPE_MasterKey, mip->mastkey, mip->keylen, 0,
+ MPPE_IsServer);
+
+ GetNewKeyFromSHA(mip->mastkey, mip->mastkey, mip->keylen, mip->sesskey);
+
+ MPPEReduceSessionKey(mip);
+
+ log_Printf(LogCCP, "MPPE: Input channel initiated\n");
+
+ if (!mip->stateless) {
+ /*
+ * We need to initialise our dictionary here as the first packet we
+ * receive is unlikely to have the FLUSHED bit set.
+ */
+ log_Printf(LogDEBUG, "MPPEInitInput: Dictionary initialised [%d]\n",
+ mip->cohnum);
+ RC4_set_key(&mip->rc4key, mip->keylen, mip->sesskey);
+ } else {
+ /*
+ * We do the first key change here as the first packet is expected
+ * to have a sequence number of 0 and we'll therefore not expect
+ * to have to change the key at that point.
+ */
+ log_Printf(LogDEBUG, "MPPEInitInput: Key changed [%d]\n", mip->cohnum);
+ MPPEKeyChange(mip);
+ }
+
+ return mip;
+}
+
+static void *
+MPPEInitOutput(struct bundle *bundle __unused, struct fsm_opt *o)
+{
+ struct mppe_state *mop;
+
+ if (!MPPE_MasterKeyValid) {
+ log_Printf(LogWARN, "MPPE: Cannot initialise without CHAP81\n");
+ return NULL;
+ }
+
+ if ((mop = MPPE_InitState(o)) == NULL) {
+ log_Printf(LogWARN, "MPPEOutput: Cannot initialise - unexpected options\n");
+ return NULL;
+ }
+
+ log_Printf(LogDEBUG, "MPPE: InitOutput: %d-bits\n", mop->keybits);
+
+#ifndef NORADIUS
+ if (*bundle->radius.cfg.file && bundle->radius.mppe.sendkey) {
+ if (mop->keylen > bundle->radius.mppe.sendkeylen)
+ mop->keylen = bundle->radius.mppe.sendkeylen;
+ if (mop->keylen > sizeof mop->mastkey)
+ mop->keylen = sizeof mop->mastkey;
+ memcpy(mop->mastkey, bundle->radius.mppe.sendkey, mop->keylen);
+ } else
+#endif
+ GetAsymetricStartKey(MPPE_MasterKey, mop->mastkey, mop->keylen, 1,
+ MPPE_IsServer);
+
+ GetNewKeyFromSHA(mop->mastkey, mop->mastkey, mop->keylen, mop->sesskey);
+
+ MPPEReduceSessionKey(mop);
+
+ log_Printf(LogCCP, "MPPE: Output channel initiated\n");
+
+ if (!mop->stateless) {
+ /*
+ * We need to initialise our dictionary now as the first packet we
+ * send won't have the FLUSHED bit set.
+ */
+ log_Printf(LogDEBUG, "MPPEInitOutput: Dictionary initialised [%d]\n",
+ mop->cohnum);
+ RC4_set_key(&mop->rc4key, mop->keylen, mop->sesskey);
+ }
+
+ return mop;
+}
+
+static void
+MPPETermInput(void *v)
+{
+ free(v);
+}
+
+static void
+MPPETermOutput(void *v)
+{
+ free(v);
+}
+
+const struct ccp_algorithm MPPEAlgorithm = {
+ TY_MPPE,
+ CCP_NEG_MPPE,
+ MPPEDispOpts,
+ MPPEUsable,
+ MPPERequired,
+ {
+ MPPESetOptsInput,
+ MPPEInitInput,
+ MPPETermInput,
+ MPPEResetInput,
+ MPPEInput,
+ MPPEDictSetup
+ },
+ {
+ 2,
+ MPPEInitOptsOutput,
+ MPPESetOptsOutput,
+ MPPEInitOutput,
+ MPPETermOutput,
+ MPPEResetOutput,
+ MPPEOutput
+ },
+};
diff --git a/usr.sbin/ppp/mppe.h b/usr.sbin/ppp/mppe.h
new file mode 100644
index 0000000..c70a609
--- /dev/null
+++ b/usr.sbin/ppp/mppe.h
@@ -0,0 +1,33 @@
+/*-
+ * Copyright (c) 2000 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.
+ *
+ * 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$
+ */
+
+#define MPPE_KEY_LEN 16
+extern const struct ccp_algorithm MPPEAlgorithm;
+extern int MPPE_MasterKeyValid;
+extern int MPPE_IsServer;
+extern char MPPE_MasterKey[];
diff --git a/usr.sbin/ppp/nat_cmd.c b/usr.sbin/ppp/nat_cmd.c
new file mode 100644
index 0000000..b4d6ad2
--- /dev/null
+++ b/usr.sbin/ppp/nat_cmd.c
@@ -0,0 +1,603 @@
+/*-
+ * Copyright (c) 2001 Charles Mott <cm@linktel.net>
+ * Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#ifdef LOCALNAT
+#include "alias.h"
+#else
+#include <alias.h>
+#endif
+
+#include "layer.h"
+#include "proto.h"
+#include "defs.h"
+#include "command.h"
+#include "log.h"
+#include "nat_cmd.h"
+#include "descriptor.h"
+#include "prompt.h"
+#include "timer.h"
+#include "fsm.h"
+#include "slcompress.h"
+#include "throughput.h"
+#include "iplist.h"
+#include "mbuf.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "ncpaddr.h"
+#include "ip.h"
+#include "ipcp.h"
+#include "ipv6cp.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "mp.h"
+#include "filter.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ncp.h"
+#include "bundle.h"
+
+
+#define NAT_EXTRABUF (13)
+
+static int StrToAddr(const char *, struct in_addr *);
+static int StrToPortRange(const char *, u_short *, u_short *, const char *);
+static int StrToAddrAndPort(const char *, struct in_addr *, u_short *,
+ u_short *, const char *);
+
+extern struct libalias *la;
+
+static void
+lowhigh(u_short *a, u_short *b)
+{
+ if (a > b) {
+ u_short c;
+
+ c = *b;
+ *b = *a;
+ *a = c;
+ }
+}
+
+int
+nat_RedirectPort(struct cmdargs const *arg)
+{
+ if (!arg->bundle->NatEnabled) {
+ prompt_Printf(arg->prompt, "Alias not enabled\n");
+ return 1;
+ } else if (arg->argc == arg->argn + 3 || arg->argc == arg->argn + 4) {
+ char proto_constant;
+ const char *proto;
+ struct in_addr localaddr;
+ u_short hlocalport, llocalport;
+ struct in_addr aliasaddr;
+ u_short haliasport, laliasport;
+ struct in_addr remoteaddr;
+ u_short hremoteport, lremoteport;
+ struct alias_link *link;
+ int error;
+
+ proto = arg->argv[arg->argn];
+ if (strcmp(proto, "tcp") == 0) {
+ proto_constant = IPPROTO_TCP;
+ } else if (strcmp(proto, "udp") == 0) {
+ proto_constant = IPPROTO_UDP;
+ } else {
+ prompt_Printf(arg->prompt, "port redirect: protocol must be"
+ " tcp or udp\n");
+ return -1;
+ }
+
+ error = StrToAddrAndPort(arg->argv[arg->argn+1], &localaddr, &llocalport,
+ &hlocalport, proto);
+ if (error) {
+ prompt_Printf(arg->prompt, "nat port: error reading localaddr:port\n");
+ return -1;
+ }
+
+ error = StrToPortRange(arg->argv[arg->argn+2], &laliasport, &haliasport,
+ proto);
+ if (error) {
+ prompt_Printf(arg->prompt, "nat port: error reading alias port\n");
+ return -1;
+ }
+ aliasaddr.s_addr = INADDR_ANY;
+
+ if (arg->argc == arg->argn + 4) {
+ error = StrToAddrAndPort(arg->argv[arg->argn+3], &remoteaddr,
+ &lremoteport, &hremoteport, proto);
+ if (error) {
+ prompt_Printf(arg->prompt, "nat port: error reading "
+ "remoteaddr:port\n");
+ return -1;
+ }
+ } else {
+ remoteaddr.s_addr = INADDR_ANY;
+ lremoteport = hremoteport = 0;
+ }
+
+ lowhigh(&llocalport, &hlocalport);
+ lowhigh(&laliasport, &haliasport);
+ lowhigh(&lremoteport, &hremoteport);
+
+ if (haliasport - laliasport != hlocalport - llocalport) {
+ prompt_Printf(arg->prompt, "nat port: local & alias port ranges "
+ "are not equal\n");
+ return -1;
+ }
+
+ if (hremoteport && hremoteport - lremoteport != hlocalport - llocalport) {
+ prompt_Printf(arg->prompt, "nat port: local & remote port ranges "
+ "are not equal\n");
+ return -1;
+ }
+
+ while (laliasport <= haliasport) {
+ link = LibAliasRedirectPort(la, localaddr, htons(llocalport),
+ remoteaddr, htons(lremoteport),
+ aliasaddr, htons(laliasport),
+ proto_constant);
+
+ if (link == NULL) {
+ prompt_Printf(arg->prompt, "nat port: %d: error %d\n", laliasport,
+ error);
+ return 1;
+ }
+ llocalport++;
+ laliasport++;
+ if (hremoteport)
+ lremoteport++;
+ }
+
+ return 0;
+ }
+
+ return -1;
+}
+
+
+int
+nat_RedirectAddr(struct cmdargs const *arg)
+{
+ if (!arg->bundle->NatEnabled) {
+ prompt_Printf(arg->prompt, "nat not enabled\n");
+ return 1;
+ } else if (arg->argc == arg->argn+2) {
+ int error;
+ struct in_addr localaddr, aliasaddr;
+ struct alias_link *link;
+
+ error = StrToAddr(arg->argv[arg->argn], &localaddr);
+ if (error) {
+ prompt_Printf(arg->prompt, "address redirect: invalid local address\n");
+ return 1;
+ }
+ error = StrToAddr(arg->argv[arg->argn+1], &aliasaddr);
+ if (error) {
+ prompt_Printf(arg->prompt, "address redirect: invalid alias address\n");
+ prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
+ arg->cmd->syntax);
+ return 1;
+ }
+ link = LibAliasRedirectAddr(la, localaddr, aliasaddr);
+ if (link == NULL) {
+ prompt_Printf(arg->prompt, "address redirect: packet aliasing"
+ " engine error\n");
+ prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
+ arg->cmd->syntax);
+ }
+ } else
+ return -1;
+
+ return 0;
+}
+
+
+int
+nat_RedirectProto(struct cmdargs const *arg)
+{
+ if (!arg->bundle->NatEnabled) {
+ prompt_Printf(arg->prompt, "nat not enabled\n");
+ return 1;
+ } else if (arg->argc >= arg->argn + 2 && arg->argc <= arg->argn + 4) {
+ struct in_addr localIP, publicIP, remoteIP;
+ struct alias_link *link;
+ struct protoent *pe;
+ int error;
+ unsigned len;
+
+ len = strlen(arg->argv[arg->argn]);
+ if (len == 0) {
+ prompt_Printf(arg->prompt, "proto redirect: invalid protocol\n");
+ return 1;
+ }
+ if (strspn(arg->argv[arg->argn], "01234567") == len)
+ pe = getprotobynumber(atoi(arg->argv[arg->argn]));
+ else
+ pe = getprotobyname(arg->argv[arg->argn]);
+ if (pe == NULL) {
+ prompt_Printf(arg->prompt, "proto redirect: invalid protocol\n");
+ return 1;
+ }
+
+ error = StrToAddr(arg->argv[arg->argn + 1], &localIP);
+ if (error) {
+ prompt_Printf(arg->prompt, "proto redirect: invalid src address\n");
+ return 1;
+ }
+
+ if (arg->argc >= arg->argn + 3) {
+ error = StrToAddr(arg->argv[arg->argn + 2], &publicIP);
+ if (error) {
+ prompt_Printf(arg->prompt, "proto redirect: invalid alias address\n");
+ prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
+ arg->cmd->syntax);
+ return 1;
+ }
+ } else
+ publicIP.s_addr = INADDR_ANY;
+
+ if (arg->argc == arg->argn + 4) {
+ error = StrToAddr(arg->argv[arg->argn + 2], &remoteIP);
+ if (error) {
+ prompt_Printf(arg->prompt, "proto redirect: invalid dst address\n");
+ prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
+ arg->cmd->syntax);
+ return 1;
+ }
+ } else
+ remoteIP.s_addr = INADDR_ANY;
+
+ link = LibAliasRedirectProto(la, localIP, remoteIP, publicIP, pe->p_proto);
+ if (link == NULL) {
+ prompt_Printf(arg->prompt, "proto redirect: packet aliasing"
+ " engine error\n");
+ prompt_Printf(arg->prompt, "usage: nat %s %s\n", arg->cmd->name,
+ arg->cmd->syntax);
+ }
+ } else
+ return -1;
+
+ return 0;
+}
+
+
+static int
+StrToAddr(const char *str, struct in_addr *addr)
+{
+ struct hostent *hp;
+
+ if (inet_aton(str, addr))
+ return 0;
+
+ hp = gethostbyname(str);
+ if (!hp) {
+ log_Printf(LogWARN, "StrToAddr: Unknown host %s.\n", str);
+ return -1;
+ }
+ *addr = *((struct in_addr *) hp->h_addr);
+ return 0;
+}
+
+
+static int
+StrToPort(const char *str, u_short *port, const char *proto)
+{
+ struct servent *sp;
+ char *end;
+
+ *port = strtol(str, &end, 10);
+ if (*end != '\0') {
+ sp = getservbyname(str, proto);
+ if (sp == NULL) {
+ log_Printf(LogWARN, "StrToAddr: Unknown port or service %s/%s.\n",
+ str, proto);
+ return -1;
+ }
+ *port = ntohs(sp->s_port);
+ }
+
+ return 0;
+}
+
+static int
+StrToPortRange(const char *str, u_short *low, u_short *high, const char *proto)
+{
+ char *minus;
+ int res;
+
+ minus = strchr(str, '-');
+ if (minus)
+ *minus = '\0'; /* Cheat the const-ness ! */
+
+ res = StrToPort(str, low, proto);
+
+ if (minus)
+ *minus = '-'; /* Cheat the const-ness ! */
+
+ if (res == 0) {
+ if (minus)
+ res = StrToPort(minus + 1, high, proto);
+ else
+ *high = *low;
+ }
+
+ return res;
+}
+
+static int
+StrToAddrAndPort(const char *str, struct in_addr *addr, u_short *low,
+ u_short *high, const char *proto)
+{
+ char *colon;
+ int res;
+
+ colon = strchr(str, ':');
+ if (!colon) {
+ log_Printf(LogWARN, "StrToAddrAndPort: %s is missing port number.\n", str);
+ return -1;
+ }
+
+ *colon = '\0'; /* Cheat the const-ness ! */
+ res = StrToAddr(str, addr);
+ *colon = ':'; /* Cheat the const-ness ! */
+ if (res != 0)
+ return -1;
+
+ return StrToPortRange(colon + 1, low, high, proto);
+}
+
+int
+nat_ProxyRule(struct cmdargs const *arg)
+{
+ char cmd[LINE_LEN];
+ int f, pos;
+ size_t len;
+
+ if (arg->argn >= arg->argc)
+ return -1;
+
+ for (f = arg->argn, pos = 0; f < arg->argc; f++) {
+ len = strlen(arg->argv[f]);
+ if (sizeof cmd - pos < len + (len ? 1 : 0))
+ break;
+ if (len)
+ cmd[pos++] = ' ';
+ strcpy(cmd + pos, arg->argv[f]);
+ pos += len;
+ }
+
+ return LibAliasProxyRule(la, cmd);
+}
+
+int
+nat_SetTarget(struct cmdargs const *arg)
+{
+ struct in_addr addr;
+
+ if (arg->argc == arg->argn) {
+ addr.s_addr = INADDR_ANY;
+ LibAliasSetTarget(la, addr);
+ return 0;
+ }
+
+ if (arg->argc != arg->argn + 1)
+ return -1;
+
+ if (!strcasecmp(arg->argv[arg->argn], "MYADDR")) {
+ addr.s_addr = INADDR_ANY;
+ LibAliasSetTarget(la, addr);
+ return 0;
+ }
+
+ addr = GetIpAddr(arg->argv[arg->argn]);
+ if (addr.s_addr == INADDR_NONE) {
+ log_Printf(LogWARN, "%s: invalid address\n", arg->argv[arg->argn]);
+ return 1;
+ }
+
+ LibAliasSetTarget(la, addr);
+ return 0;
+}
+
+#ifndef NO_FW_PUNCH
+int
+nat_PunchFW(struct cmdargs const *arg)
+{
+ char *end;
+ long base, count;
+
+ if (arg->argc == arg->argn) {
+ LibAliasSetMode(la, 0, PKT_ALIAS_PUNCH_FW);
+ return 0;
+ }
+
+ if (arg->argc != arg->argn + 2)
+ return -1;
+
+ base = strtol(arg->argv[arg->argn], &end, 10);
+ if (*end != '\0' || base < 0)
+ return -1;
+
+ count = strtol(arg->argv[arg->argn + 1], &end, 10);
+ if (*end != '\0' || count < 0)
+ return -1;
+
+ LibAliasSetFWBase(la, base, count);
+ LibAliasSetMode(la, PKT_ALIAS_PUNCH_FW, PKT_ALIAS_PUNCH_FW);
+
+ return 0;
+}
+#endif
+
+int
+nat_SkinnyPort(struct cmdargs const *arg)
+{
+ char *end;
+ long port;
+
+ if (arg->argc == arg->argn) {
+ LibAliasSetSkinnyPort(la, 0);
+ return 0;
+ }
+
+ if (arg->argc != arg->argn + 1)
+ return -1;
+
+ port = strtol(arg->argv[arg->argn], &end, 10);
+ if (*end != '\0' || port < 0)
+ return -1;
+
+ LibAliasSetSkinnyPort(la, port);
+
+ return 0;
+}
+
+static struct mbuf *
+nat_LayerPush(struct bundle *bundle, struct link *l __unused, struct mbuf *bp,
+ int pri __unused, u_short *proto)
+{
+ if (!bundle->NatEnabled || *proto != PROTO_IP)
+ return bp;
+
+ log_Printf(LogDEBUG, "nat_LayerPush: PROTO_IP -> PROTO_IP\n");
+ m_settype(bp, MB_NATOUT);
+ /* Ensure there's a bit of extra buffer for the NAT code... */
+ bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
+ LibAliasOut(la, MBUF_CTOP(bp), bp->m_len);
+ bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
+
+ return bp;
+}
+
+static struct mbuf *
+nat_LayerPull(struct bundle *bundle, struct link *l __unused, struct mbuf *bp,
+ u_short *proto)
+{
+ static int gfrags;
+ int ret, len, nfrags;
+ struct mbuf **last;
+ char *fptr;
+
+ if (!bundle->NatEnabled || *proto != PROTO_IP)
+ return bp;
+
+ log_Printf(LogDEBUG, "nat_LayerPull: PROTO_IP -> PROTO_IP\n");
+ m_settype(bp, MB_NATIN);
+ /* Ensure there's a bit of extra buffer for the NAT code... */
+ bp = m_pullup(m_append(bp, NULL, NAT_EXTRABUF));
+ ret = LibAliasIn(la, MBUF_CTOP(bp), bp->m_len);
+
+ bp->m_len = ntohs(((struct ip *)MBUF_CTOP(bp))->ip_len);
+ if (bp->m_len > MAX_MRU) {
+ log_Printf(LogWARN, "nat_LayerPull: Problem with IP header length (%lu)\n",
+ (unsigned long)bp->m_len);
+ m_freem(bp);
+ return NULL;
+ }
+
+ switch (ret) {
+ case PKT_ALIAS_OK:
+ break;
+
+ case PKT_ALIAS_UNRESOLVED_FRAGMENT:
+ /* Save the data for later */
+ if ((fptr = malloc(bp->m_len)) == NULL) {
+ log_Printf(LogWARN, "nat_LayerPull: Dropped unresolved fragment -"
+ " out of memory!\n");
+ m_freem(bp);
+ bp = NULL;
+ } else {
+ bp = mbuf_Read(bp, fptr, bp->m_len);
+ LibAliasSaveFragment(la, fptr);
+ log_Printf(LogDEBUG, "Store another frag (%lu) - now %d\n",
+ (unsigned long)((struct ip *)fptr)->ip_id, ++gfrags);
+ }
+ break;
+
+ case PKT_ALIAS_FOUND_HEADER_FRAGMENT:
+ /* Fetch all the saved fragments and chain them on the end of `bp' */
+ last = &bp->m_nextpkt;
+ nfrags = 0;
+ while ((fptr = LibAliasGetFragment(la, MBUF_CTOP(bp))) != NULL) {
+ nfrags++;
+ LibAliasFragmentIn(la, MBUF_CTOP(bp), fptr);
+ len = ntohs(((struct ip *)fptr)->ip_len);
+ *last = m_get(len, MB_NATIN);
+ memcpy(MBUF_CTOP(*last), fptr, len);
+ free(fptr);
+ last = &(*last)->m_nextpkt;
+ }
+ gfrags -= nfrags;
+ log_Printf(LogDEBUG, "Found a frag header (%lu) - plus %d more frags (no"
+ "w %d)\n", (unsigned long)((struct ip *)MBUF_CTOP(bp))->ip_id,
+ nfrags, gfrags);
+ break;
+
+ case PKT_ALIAS_IGNORED:
+ if (LibAliasSetMode(la, 0, 0) & PKT_ALIAS_DENY_INCOMING) {
+ log_Printf(LogTCPIP, "NAT engine denied data:\n");
+ m_freem(bp);
+ bp = NULL;
+ } else if (log_IsKept(LogTCPIP)) {
+ log_Printf(LogTCPIP, "NAT engine ignored data:\n");
+ PacketCheck(bundle, AF_INET, MBUF_CTOP(bp), bp->m_len, NULL,
+ NULL, NULL);
+ }
+ break;
+
+ default:
+ log_Printf(LogWARN, "nat_LayerPull: Dropped a packet (%d)....\n", ret);
+ m_freem(bp);
+ bp = NULL;
+ break;
+ }
+
+ return bp;
+}
+
+struct layer natlayer =
+ { LAYER_NAT, "nat", nat_LayerPush, nat_LayerPull };
diff --git a/usr.sbin/ppp/nat_cmd.h b/usr.sbin/ppp/nat_cmd.h
new file mode 100644
index 0000000..f4c3655
--- /dev/null
+++ b/usr.sbin/ppp/nat_cmd.h
@@ -0,0 +1,42 @@
+/*-
+ * Copyright (c) 2001 Charles Mott <cm@linktel.net>
+ * Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct cmdargs;
+
+extern int nat_RedirectPort(struct cmdargs const *);
+extern int nat_RedirectAddr(struct cmdargs const *);
+extern int nat_RedirectProto(struct cmdargs const *);
+extern int nat_ProxyRule(struct cmdargs const *);
+extern int nat_SetTarget(struct cmdargs const *);
+#ifndef NO_FW_PUNCH
+extern int nat_PunchFW(struct cmdargs const *);
+#endif
+extern int nat_SkinnyPort(struct cmdargs const *);
+
+extern struct layer natlayer;
diff --git a/usr.sbin/ppp/ncp.c b/usr.sbin/ppp/ncp.c
new file mode 100644
index 0000000..1c12fc5
--- /dev/null
+++ b/usr.sbin/ppp/ncp.c
@@ -0,0 +1,547 @@
+/*-
+ * Copyright (c) 2001 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <net/route.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <resolv.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "iplist.h"
+#include "throughput.h"
+#include "slcompress.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "async.h"
+#include "ccp.h"
+#include "link.h"
+#include "physical.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "prompt.h"
+#include "route.h"
+#include "iface.h"
+#include "chat.h"
+#include "auth.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+
+
+static u_short default_urgent_tcp_ports[] = {
+ 21, /* ftp */
+ 22, /* ssh */
+ 23, /* telnet */
+ 513, /* login */
+ 514, /* shell */
+ 543, /* klogin */
+ 544 /* kshell */
+};
+
+#define NDEFTCPPORTS \
+ (sizeof default_urgent_tcp_ports / sizeof default_urgent_tcp_ports[0])
+
+void
+ncp_Init(struct ncp *ncp, struct bundle *bundle)
+{
+ ncp->afq = AF_INET;
+ ncp->route = NULL;
+
+ ncp->cfg.urgent.tcp.port = (u_short *)malloc(NDEFTCPPORTS * sizeof(u_short));
+ if (ncp->cfg.urgent.tcp.port == NULL) {
+ log_Printf(LogERROR, "ncp_Init: Out of memory allocating urgent ports\n");
+ ncp->cfg.urgent.tcp.nports = ncp->cfg.urgent.tcp.maxports = 0;
+ } else {
+ ncp->cfg.urgent.tcp.nports = ncp->cfg.urgent.tcp.maxports = NDEFTCPPORTS;
+ memcpy(ncp->cfg.urgent.tcp.port, default_urgent_tcp_ports,
+ NDEFTCPPORTS * sizeof(u_short));
+ }
+ ncp->cfg.urgent.tos = 1;
+
+ ncp->cfg.urgent.udp.nports = ncp->cfg.urgent.udp.maxports = 0;
+ ncp->cfg.urgent.udp.port = NULL;
+
+ mp_Init(&ncp->mp, bundle);
+
+ /* Send over the first physical link by default */
+ ipcp_Init(&ncp->ipcp, bundle, &bundle->links->physical->link,
+ &bundle->fsm);
+#ifndef NOINET6
+ ipv6cp_Init(&ncp->ipv6cp, bundle, &bundle->links->physical->link,
+ &bundle->fsm);
+#endif
+}
+
+void
+ncp_Destroy(struct ncp *ncp)
+{
+ ipcp_Destroy(&ncp->ipcp);
+#ifndef NOINET6
+ ipv6cp_Destroy(&ncp->ipv6cp);
+#endif
+
+ if (ncp->cfg.urgent.tcp.maxports) {
+ ncp->cfg.urgent.tcp.nports = ncp->cfg.urgent.tcp.maxports = 0;
+ free(ncp->cfg.urgent.tcp.port);
+ ncp->cfg.urgent.tcp.port = NULL;
+ }
+ if (ncp->cfg.urgent.udp.maxports) {
+ ncp->cfg.urgent.udp.nports = ncp->cfg.urgent.udp.maxports = 0;
+ free(ncp->cfg.urgent.udp.port);
+ ncp->cfg.urgent.udp.port = NULL;
+ }
+}
+
+int
+ncp_fsmStart(struct ncp *ncp,
+#ifdef NOINET6
+ struct bundle *bundle __unused
+#else
+ struct bundle *bundle
+#endif
+ )
+{
+ int res = 0;
+
+#ifndef NOINET6
+ if (Enabled(bundle, OPT_IPCP)) {
+#endif
+ fsm_Up(&ncp->ipcp.fsm);
+ fsm_Open(&ncp->ipcp.fsm);
+ res++;
+#ifndef NOINET6
+ }
+
+ if (Enabled(bundle, OPT_IPV6CP)) {
+ fsm_Up(&ncp->ipv6cp.fsm);
+ fsm_Open(&ncp->ipv6cp.fsm);
+ res++;
+ }
+#endif
+
+ return res;
+}
+
+void
+ncp_IfaceAddrAdded(struct ncp *ncp, const struct iface_addr *addr)
+{
+ switch (ncprange_family(&addr->ifa)) {
+ case AF_INET:
+ ipcp_IfaceAddrAdded(&ncp->ipcp, addr);
+ break;
+#ifndef NOINET6
+ case AF_INET6:
+ ipv6cp_IfaceAddrAdded(&ncp->ipv6cp, addr);
+ break;
+#endif
+ }
+}
+
+void
+ncp_IfaceAddrDeleted(struct ncp *ncp, const struct iface_addr *addr)
+{
+ if (ncprange_family(&addr->ifa) == AF_INET)
+ ipcp_IfaceAddrDeleted(&ncp->ipcp, addr);
+}
+
+void
+ncp_SetLink(struct ncp *ncp, struct link *l)
+{
+ ipcp_SetLink(&ncp->ipcp, l);
+#ifndef NOINET6
+ ipv6cp_SetLink(&ncp->ipv6cp, l);
+#endif
+}
+
+/*
+ * Enqueue a packet of the given address family. Nothing will make it
+ * down to the physical link level 'till ncp_FillPhysicalQueues() is used.
+ */
+void
+ncp_Enqueue(struct ncp *ncp, int af, unsigned pri, char *ptr, int count)
+{
+#ifndef NOINET6
+ struct ipv6cp *ipv6cp = &ncp->ipv6cp;
+#endif
+ struct ipcp *ipcp = &ncp->ipcp;
+ struct mbuf *bp;
+
+ /*
+ * We allocate an extra 6 bytes, four at the front and two at the end.
+ * This is an optimisation so that we need to do less work in
+ * m_prepend() in acf_LayerPush() and proto_LayerPush() and
+ * appending in hdlc_LayerPush().
+ */
+
+ switch (af) {
+ case AF_INET:
+ if (pri >= IPCP_QUEUES(ipcp)) {
+ log_Printf(LogERROR, "Can't store in ip queue %u\n", pri);
+ break;
+ }
+
+ bp = m_get(count + 6, MB_IPOUT);
+ bp->m_offset += 4;
+ bp->m_len -= 6;
+ memcpy(MBUF_CTOP(bp), ptr, count);
+ m_enqueue(ipcp->Queue + pri, bp);
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ if (pri >= IPV6CP_QUEUES(ipcp)) {
+ log_Printf(LogERROR, "Can't store in ipv6 queue %u\n", pri);
+ break;
+ }
+
+ bp = m_get(count + 6, MB_IPOUT);
+ bp->m_offset += 4;
+ bp->m_len -= 6;
+ memcpy(MBUF_CTOP(bp), ptr, count);
+ m_enqueue(ipv6cp->Queue + pri, bp);
+ break;
+#endif
+
+ default:
+ log_Printf(LogERROR, "Can't enqueue protocol family %d\n", af);
+ }
+}
+
+/*
+ * How many packets are queued to go out ?
+ */
+size_t
+ncp_QueueLen(struct ncp *ncp)
+{
+ size_t result;
+
+ result = ipcp_QueueLen(&ncp->ipcp);
+#ifndef NOINET6
+ result += ipv6cp_QueueLen(&ncp->ipv6cp);
+#endif
+ result += mp_QueueLen(&ncp->mp); /* Usually empty */
+
+ return result;
+}
+
+/*
+ * Ditch all queued packets. This is usually done after our choked timer
+ * has fired - which happens because we couldn't send any traffic over
+ * any links for some time.
+ */
+void
+ncp_DeleteQueues(struct ncp *ncp)
+{
+#ifndef NOINET6
+ struct ipv6cp *ipv6cp = &ncp->ipv6cp;
+#endif
+ struct ipcp *ipcp = &ncp->ipcp;
+ struct mp *mp = &ncp->mp;
+ struct mqueue *q;
+
+ for (q = ipcp->Queue; q < ipcp->Queue + IPCP_QUEUES(ipcp); q++)
+ while (q->top)
+ m_freem(m_dequeue(q));
+
+#ifndef NOINET6
+ for (q = ipv6cp->Queue; q < ipv6cp->Queue + IPV6CP_QUEUES(ipv6cp); q++)
+ while (q->top)
+ m_freem(m_dequeue(q));
+#endif
+
+ link_DeleteQueue(&mp->link); /* Usually empty anyway */
+}
+
+/*
+ * Arrange that each of our links has at least one packet. We keep the
+ * number of packets queued at the link level to a minimum so that the
+ * loss of a link in multi-link mode results in the minimum number of
+ * dropped packets.
+ */
+size_t
+ncp_FillPhysicalQueues(struct ncp *ncp, struct bundle *bundle)
+{
+ size_t total;
+
+ if (bundle->ncp.mp.active)
+ total = mp_FillPhysicalQueues(bundle);
+ else {
+ struct datalink *dl;
+ size_t add;
+
+ for (total = 0, dl = bundle->links; dl; dl = dl->next)
+ if (dl->state == DATALINK_OPEN) {
+ add = link_QueueLen(&dl->physical->link);
+ if (add == 0 && dl->physical->out == NULL)
+ add = ncp_PushPacket(ncp, &ncp->afq, &dl->physical->link);
+ total += add;
+ }
+ }
+
+ return total + ncp_QueueLen(&bundle->ncp);
+}
+
+/*
+ * Push a packet into the given link. ``af'' is used as a persistent record
+ * of what is to be pushed next, coming either from mp->out or ncp->afq.
+ */
+int
+ncp_PushPacket(struct ncp *ncp __unused,
+#ifdef NOINET6
+ int *af __unused,
+#else
+ int *af,
+#endif
+ struct link *l)
+{
+ struct bundle *bundle = l->lcp.fsm.bundle;
+ int res;
+
+#ifndef NOINET6
+ if (*af == AF_INET) {
+ if ((res = ipcp_PushPacket(&bundle->ncp.ipcp, l)))
+ *af = AF_INET6;
+ else
+ res = ipv6cp_PushPacket(&bundle->ncp.ipv6cp, l);
+ } else {
+ if ((res = ipv6cp_PushPacket(&bundle->ncp.ipv6cp, l)))
+ *af = AF_INET;
+ else
+ res = ipcp_PushPacket(&bundle->ncp.ipcp, l);
+ }
+#else
+ res = ipcp_PushPacket(&bundle->ncp.ipcp, l);
+#endif
+
+ return res;
+}
+
+int
+ncp_IsUrgentPort(struct port_range *range, u_short src, u_short dst)
+{
+ unsigned f;
+
+ for (f = 0; f < range->nports; f++)
+ if (range->port[f] == src || range->port[f] == dst)
+ return 1;
+
+ return 0;
+}
+
+void
+ncp_AddUrgentPort(struct port_range *range, u_short port)
+{
+ u_short *newport;
+ unsigned p;
+
+ if (range->nports == range->maxports) {
+ range->maxports += 10;
+ newport = (u_short *)realloc(range->port,
+ range->maxports * sizeof(u_short));
+ if (newport == NULL) {
+ log_Printf(LogERROR, "ncp_AddUrgentPort: realloc: %s\n",
+ strerror(errno));
+ range->maxports -= 10;
+ return;
+ }
+ range->port = newport;
+ }
+
+ for (p = 0; p < range->nports; p++)
+ if (range->port[p] == port) {
+ log_Printf(LogWARN, "%u: Port already set to urgent\n", port);
+ break;
+ } else if (range->port[p] > port) {
+ memmove(range->port + p + 1, range->port + p,
+ (range->nports - p) * sizeof(u_short));
+ range->port[p] = port;
+ range->nports++;
+ break;
+ }
+
+ if (p == range->nports)
+ range->port[range->nports++] = port;
+}
+
+void
+ncp_RemoveUrgentPort(struct port_range *range, u_short port)
+{
+ unsigned p;
+
+ for (p = 0; p < range->nports; p++)
+ if (range->port[p] == port) {
+ if (p + 1 != range->nports)
+ memmove(range->port + p, range->port + p + 1,
+ (range->nports - p - 1) * sizeof(u_short));
+ range->nports--;
+ return;
+ }
+
+ if (p == range->nports)
+ log_Printf(LogWARN, "%u: Port not set to urgent\n", port);
+}
+
+void
+ncp_ClearUrgentPorts(struct port_range *range)
+{
+ range->nports = 0;
+}
+
+int
+ncp_Show(struct cmdargs const *arg)
+{
+ struct ncp *ncp = &arg->bundle->ncp;
+ unsigned p;
+
+#ifndef NOINET6
+ prompt_Printf(arg->prompt, "Next queued AF: %s\n",
+ ncp->afq == AF_INET6 ? "inet6" : "inet");
+#endif
+
+ if (ncp->route) {
+ prompt_Printf(arg->prompt, "\n");
+ route_ShowSticky(arg->prompt, ncp->route, "Sticky routes", 1);
+ }
+
+ prompt_Printf(arg->prompt, "\nDefaults:\n");
+ prompt_Printf(arg->prompt, " sendpipe: ");
+ if (ncp->cfg.sendpipe > 0)
+ prompt_Printf(arg->prompt, "%-20ld\n", ncp->cfg.sendpipe);
+ else
+ prompt_Printf(arg->prompt, "unspecified\n");
+ prompt_Printf(arg->prompt, " recvpipe: ");
+ if (ncp->cfg.recvpipe > 0)
+ prompt_Printf(arg->prompt, "%ld\n", ncp->cfg.recvpipe);
+ else
+ prompt_Printf(arg->prompt, "unspecified\n");
+
+ prompt_Printf(arg->prompt, "\n Urgent ports\n");
+ prompt_Printf(arg->prompt, " TCP: ");
+ if (ncp->cfg.urgent.tcp.nports == 0)
+ prompt_Printf(arg->prompt, "none");
+ else
+ for (p = 0; p < ncp->cfg.urgent.tcp.nports; p++) {
+ if (p)
+ prompt_Printf(arg->prompt, ", ");
+ prompt_Printf(arg->prompt, "%u", ncp->cfg.urgent.tcp.port[p]);
+ }
+
+ prompt_Printf(arg->prompt, "\n UDP: ");
+ if (ncp->cfg.urgent.udp.nports == 0)
+ prompt_Printf(arg->prompt, "none");
+ else
+ for (p = 0; p < ncp->cfg.urgent.udp.nports; p++) {
+ if (p)
+ prompt_Printf(arg->prompt, ", ");
+ prompt_Printf(arg->prompt, "%u", ncp->cfg.urgent.udp.port[p]);
+ }
+ prompt_Printf(arg->prompt, "\n TOS: %s\n\n",
+ ncp->cfg.urgent.tos ? "yes" : "no");
+
+ return 0;
+}
+
+int
+ncp_LayersOpen(struct ncp *ncp)
+{
+ int n;
+
+ n = !!(ncp->ipcp.fsm.state == ST_OPENED);
+#ifndef NOINET6
+ n += !!(ncp->ipv6cp.fsm.state == ST_OPENED);
+#endif
+
+ return n;
+}
+
+int
+ncp_LayersUnfinished(struct ncp *ncp)
+{
+ int n = 0;
+
+ if (ncp->ipcp.fsm.state > ST_CLOSED ||
+ ncp->ipcp.fsm.state == ST_STARTING)
+ n++;
+
+#ifndef NOINET6
+ if (ncp->ipv6cp.fsm.state > ST_CLOSED ||
+ ncp->ipv6cp.fsm.state == ST_STARTING)
+ n++;
+#endif
+
+ return n;
+}
+
+void
+ncp_Close(struct ncp *ncp)
+{
+ if (ncp->ipcp.fsm.state > ST_CLOSED ||
+ ncp->ipcp.fsm.state == ST_STARTING)
+ fsm_Close(&ncp->ipcp.fsm);
+
+#ifndef NOINET6
+ if (ncp->ipv6cp.fsm.state > ST_CLOSED ||
+ ncp->ipv6cp.fsm.state == ST_STARTING)
+ fsm_Close(&ncp->ipv6cp.fsm);
+#endif
+}
+
+void
+ncp2initial(struct ncp *ncp)
+{
+ fsm2initial(&ncp->ipcp.fsm);
+#ifndef NOINET6
+ fsm2initial(&ncp->ipv6cp.fsm);
+#endif
+}
diff --git a/usr.sbin/ppp/ncp.h b/usr.sbin/ppp/ncp.h
new file mode 100644
index 0000000..99580cf
--- /dev/null
+++ b/usr.sbin/ppp/ncp.h
@@ -0,0 +1,101 @@
+/*-
+ * Copyright (c) 2001 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct port_range {
+ unsigned nports; /* How many ports */
+ unsigned maxports; /* How many allocated (malloc) ports */
+ u_short *port; /* The actual ports */
+};
+
+struct ncp {
+ struct {
+ u_long sendpipe; /* route sendpipe size */
+ u_long recvpipe; /* route recvpipe size */
+
+ struct {
+ struct port_range tcp, udp; /* The range of urgent ports */
+ unsigned tos : 1; /* Urgent IPTOS_LOWDELAY packets ? */
+ } urgent;
+ } cfg;
+
+ int afq; /* Next address family to queue */
+
+ struct sticky_route *route; /* List of dynamic routes */
+
+ struct ipcp ipcp; /* Our IPCP FSM */
+#ifndef NOINET6
+ struct ipv6cp ipv6cp; /* Our IPV6CP FSM */
+#endif
+ struct mp mp; /* Our MP */
+};
+
+extern void ncp_Init(struct ncp *, struct bundle *);
+extern void ncp_Destroy(struct ncp *);
+extern int ncp_fsmStart(struct ncp *, struct bundle *);
+extern void ncp_IfaceAddrAdded(struct ncp *, const struct iface_addr *);
+extern void ncp_IfaceAddrDeleted(struct ncp *, const struct iface_addr *);
+extern void ncp_SetLink(struct ncp *, struct link *);
+extern void ncp_Enqueue(struct ncp *, int, unsigned, char *, int);
+extern void ncp_DeleteQueues(struct ncp *);
+extern size_t ncp_QueueLen(struct ncp *);
+extern size_t ncp_FillPhysicalQueues(struct ncp *, struct bundle *);
+extern int ncp_PushPacket(struct ncp *, int *, struct link *);
+extern int ncp_IsUrgentPort(struct port_range *, u_short, u_short);
+extern void ncp_AddUrgentPort(struct port_range *, u_short);
+extern void ncp_RemoveUrgentPort(struct port_range *, u_short);
+extern void ncp_ClearUrgentPorts(struct port_range *);
+extern int ncp_Show(struct cmdargs const *);
+extern int ncp_LayersOpen(struct ncp *);
+extern int ncp_LayersUnfinished(struct ncp *);
+extern void ncp_Close(struct ncp *);
+extern void ncp2initial(struct ncp *);
+
+#define ncp_IsUrgentTcpPort(ncp, p1, p2) \
+ ncp_IsUrgentPort(&(ncp)->cfg.urgent.tcp, p1, p2)
+#define ncp_IsUrgentUdpPort(ncp, p1, p2) \
+ ncp_IsUrgentPort(&(ncp)->cfg.urgent.udp, p1, p2)
+#define ncp_AddUrgentTcpPort(ncp, p) \
+ ncp_AddUrgentPort(&(ncp)->cfg.urgent.tcp, p)
+#define ncp_AddUrgentUdpPort(ncp, p) \
+ ncp_AddUrgentPort(&(ncp)->cfg.urgent.udp, p)
+#define ncp_RemoveUrgentTcpPort(ncp, p) \
+ ncp_RemoveUrgentPort(&(ncp)->cfg.urgent.tcp, p)
+#define ncp_RemoveUrgentUdpPort(ncp, p) \
+ ncp_RemoveUrgentPort(&(ncp)->cfg.urgent.udp, p)
+#define ncp_ClearUrgentTcpPorts(ncp) \
+ ncp_ClearUrgentPorts(&(ncp)->cfg.urgent.tcp)
+#define ncp_ClearUrgentUdpPorts(ncp) \
+ ncp_ClearUrgentPorts(&(ncp)->cfg.urgent.udp)
+#define ncp_ClearUrgentTOS(ncp) (ncp)->cfg.urgent.tos = 0;
+#define ncp_SetUrgentTOS(ncp) (ncp)->cfg.urgent.tos = 1;
+
+#ifndef NOINET6
+#define isncp(proto) ((proto) == PROTO_IPCP || (proto) == PROTO_IPV6CP)
+#else
+#define isncp(proto) ((proto) == PROTO_IPCP)
+#endif
diff --git a/usr.sbin/ppp/ncpaddr.c b/usr.sbin/ppp/ncpaddr.c
new file mode 100644
index 0000000..0b16999
--- /dev/null
+++ b/usr.sbin/ppp/ncpaddr.c
@@ -0,0 +1,1010 @@
+/*-
+ * Copyright (c) 2001 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#ifdef __OpenBSD__
+#include <net/if_types.h>
+#include <net/route.h>
+#endif
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "log.h"
+#include "ncpaddr.h"
+#include "timer.h"
+#include "fsm.h"
+#include "defs.h"
+#include "slcompress.h"
+#include "iplist.h"
+#include "throughput.h"
+#include "mbuf.h"
+#include "ipcp.h"
+#include "descriptor.h"
+#include "layer.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "mp.h"
+#include "ipv6cp.h"
+#include "ncp.h"
+
+
+#define ncprange_ip4addr u.ip4.ipaddr
+#define ncprange_ip4mask u.ip4.mask
+#define ncprange_ip4width u.ip4.width
+#define ncpaddr_ip4addr u.ip4addr
+#ifndef NOINET6
+#define ncprange_ip6addr u.ip6.ipaddr
+#define ncprange_ip6width u.ip6.width
+#define ncpaddr_ip6addr u.ip6addr
+#endif
+
+#define NCP_ASCIIBUFFERSIZE 52
+
+static struct in_addr
+bits2mask4(int bits)
+{
+ struct in_addr result;
+ u_int32_t bit = 0x80000000;
+
+ result.s_addr = 0;
+
+ while (bits) {
+ result.s_addr |= bit;
+ bit >>= 1;
+ bits--;
+ }
+
+ result.s_addr = htonl(result.s_addr);
+ return result;
+}
+
+static int
+mask42bits(struct in_addr mask)
+{
+ u_int32_t msk = ntohl(mask.s_addr);
+ u_int32_t tst;
+ int ret;
+
+ for (ret = 32, tst = 1; tst; ret--, tst <<= 1)
+ if (msk & tst)
+ break;
+
+ for (tst <<= 1; tst; tst <<= 1)
+ if (!(msk & tst))
+ break;
+
+ return tst ? -1 : ret;
+}
+
+#ifndef NOINET6
+static struct in6_addr
+bits2mask6(int bits)
+{
+ struct in6_addr result;
+ u_int32_t bit = 0x80;
+ u_char *c = result.s6_addr;
+
+ memset(&result, '\0', sizeof result);
+
+ while (bits) {
+ if (bit == 0) {
+ bit = 0x80;
+ c++;
+ }
+ *c |= bit;
+ bit >>= 1;
+ bits--;
+ }
+
+ return result;
+}
+
+static int
+mask62bits(const struct in6_addr *mask)
+{
+ const u_char masks[] = { 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe };
+ const u_char *c, *p, *end;
+ int masklen, m;
+
+ p = (const u_char *)mask;
+ for (masklen = 0, end = p + 16; p < end && *p == 0xff; p++)
+ masklen += 8;
+
+ if (p < end) {
+ for (c = masks, m = 0; c < masks + sizeof masks; c++, m++)
+ if (*c == *p) {
+ masklen += m;
+ break;
+ }
+ }
+
+ return masklen;
+}
+
+#if 0
+static void
+adjust_linklocal(struct sockaddr_in6 *sin6)
+{
+ /* XXX: ?????!?!?!!!!! This is horrible ! */
+ /*
+ * The kernel does not understand sin6_scope_id for routing at this moment.
+ * We should rather keep the embedded ID.
+ * jinmei@kame.net, 20011026
+ */
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) ||
+ IN6_IS_ADDR_MC_LINKLOCAL(&sin6->sin6_addr)) {
+ sin6->sin6_scope_id =
+ ntohs(*(u_short *)&sin6->sin6_addr.s6_addr[2]);
+ *(u_short *)&sin6->sin6_addr.s6_addr[2] = 0;
+ }
+}
+#endif
+#endif
+
+void
+ncpaddr_init(struct ncpaddr *addr)
+{
+ addr->ncpaddr_family = AF_UNSPEC;
+}
+
+int
+ncpaddr_isset(const struct ncpaddr *addr)
+{
+ return addr->ncpaddr_family != AF_UNSPEC;
+}
+
+int
+ncpaddr_isdefault(const struct ncpaddr *addr)
+{
+ switch (addr->ncpaddr_family) {
+ case AF_INET:
+ if (addr->ncpaddr_ip4addr.s_addr == INADDR_ANY)
+ return 1;
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ if (IN6_IS_ADDR_UNSPECIFIED(&addr->ncpaddr_ip6addr))
+ return 1;
+ break;
+#endif
+ }
+
+ return 0;
+}
+
+int
+ncpaddr_equal(const struct ncpaddr *addr, const struct ncpaddr *cmp)
+{
+ if (addr->ncpaddr_family != cmp->ncpaddr_family)
+ return 0;
+
+ switch (addr->ncpaddr_family) {
+ case AF_INET:
+ return addr->ncpaddr_ip4addr.s_addr == cmp->ncpaddr_ip4addr.s_addr;
+
+#ifndef NOINET6
+ case AF_INET6:
+ return !memcmp(&addr->ncpaddr_ip6addr, &cmp->ncpaddr_ip6addr,
+ sizeof addr->ncpaddr_ip6addr);
+#endif
+
+ case AF_UNSPEC:
+ return 1;
+ }
+
+ return 0;
+}
+
+void
+ncpaddr_copy(struct ncpaddr *addr, const struct ncpaddr *from)
+{
+ switch (from->ncpaddr_family) {
+ case AF_INET:
+ addr->ncpaddr_family = AF_INET;
+ addr->ncpaddr_ip4addr = from->ncpaddr_ip4addr;
+ break;
+#ifndef NOINET6
+ case AF_INET6:
+ addr->ncpaddr_family = AF_INET6;
+ addr->ncpaddr_ip6addr = from->ncpaddr_ip6addr;
+ break;
+#endif
+ default:
+ addr->ncpaddr_family = AF_UNSPEC;
+ }
+}
+
+void
+ncpaddr_setip4addr(struct ncpaddr *addr, u_int32_t ip)
+{
+ addr->ncpaddr_family = AF_INET;
+ addr->ncpaddr_ip4addr.s_addr = ip;
+}
+
+int
+ncpaddr_getip4addr(const struct ncpaddr *addr, u_int32_t *ip)
+{
+ if (addr->ncpaddr_family != AF_INET)
+ return 0;
+ *ip = addr->ncpaddr_ip4addr.s_addr;
+ return 1;
+}
+
+void
+ncpaddr_setip4(struct ncpaddr *addr, struct in_addr ip)
+{
+ addr->ncpaddr_family = AF_INET;
+ addr->ncpaddr_ip4addr = ip;
+}
+
+int
+ncpaddr_getip4(const struct ncpaddr *addr, struct in_addr *ip)
+{
+ if (addr->ncpaddr_family != AF_INET)
+ return 0;
+ *ip = addr->ncpaddr_ip4addr;
+ return 1;
+}
+
+#ifndef NOINET6
+void
+ncpaddr_setip6(struct ncpaddr *addr, const struct in6_addr *ip6)
+{
+ addr->ncpaddr_family = AF_INET6;
+ addr->ncpaddr_ip6addr = *ip6;
+}
+
+int
+ncpaddr_getip6(const struct ncpaddr *addr, struct in6_addr *ip6)
+{
+ if (addr->ncpaddr_family != AF_INET6)
+ return 0;
+ *ip6 = addr->ncpaddr_ip6addr;
+ return 1;
+}
+#endif
+
+void
+ncpaddr_getsa(const struct ncpaddr *addr, struct sockaddr_storage *host)
+{
+ struct sockaddr_in *host4 = (struct sockaddr_in *)host;
+#ifndef NOINET6
+ struct sockaddr_in6 *host6 = (struct sockaddr_in6 *)host;
+#endif
+
+ memset(host, '\0', sizeof(*host));
+
+ switch (addr->ncpaddr_family) {
+ case AF_INET:
+ host4->sin_family = AF_INET;
+ host4->sin_len = sizeof(*host4);
+ host4->sin_addr = addr->ncpaddr_ip4addr;
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ host6->sin6_family = AF_INET6;
+ host6->sin6_len = sizeof(*host6);
+ host6->sin6_addr = addr->ncpaddr_ip6addr;
+ break;
+#endif
+
+ default:
+ host->ss_family = AF_UNSPEC;
+ break;
+ }
+}
+
+void
+ncpaddr_setsa(struct ncpaddr *addr, const struct sockaddr *host)
+{
+ const struct sockaddr_in *host4 = (const struct sockaddr_in *)host;
+#ifndef NOINET6
+ const struct sockaddr_in6 *host6 = (const struct sockaddr_in6 *)host;
+#endif
+
+ switch (host->sa_family) {
+ case AF_INET:
+ addr->ncpaddr_family = AF_INET;
+ addr->ncpaddr_ip4addr = host4->sin_addr;
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ if (IN6_IS_ADDR_V4MAPPED(&host6->sin6_addr)) {
+ addr->ncpaddr_family = AF_INET;
+ addr->ncpaddr_ip4addr.s_addr =
+ *(const u_int32_t *)(host6->sin6_addr.s6_addr + 12);
+ } else {
+ addr->ncpaddr_family = AF_INET6;
+ addr->ncpaddr_ip6addr = host6->sin6_addr;
+ }
+ break;
+#endif
+
+ default:
+ addr->ncpaddr_family = AF_UNSPEC;
+ }
+}
+
+static char *
+ncpaddr_ntowa(const struct ncpaddr *addr)
+{
+ static char res[NCP_ASCIIBUFFERSIZE];
+#ifndef NOINET6
+ struct sockaddr_in6 sin6;
+#endif
+
+ switch (addr->ncpaddr_family) {
+ case AF_INET:
+ snprintf(res, sizeof res, "%s", inet_ntoa(addr->ncpaddr_ip4addr));
+ return res;
+
+#ifndef NOINET6
+ case AF_INET6:
+ memset(&sin6, '\0', sizeof(sin6));
+ sin6.sin6_len = sizeof(sin6);
+ sin6.sin6_family = AF_INET6;
+ sin6.sin6_addr = addr->ncpaddr_ip6addr;
+#if 0
+ adjust_linklocal(&sin6);
+#endif
+ if (getnameinfo((struct sockaddr *)&sin6, sizeof sin6, res, sizeof(res),
+ NULL, 0, NI_NUMERICHOST) != 0)
+ break;
+
+ return res;
+#endif
+ }
+
+ snprintf(res, sizeof res, "<AF_UNSPEC>");
+ return res;
+}
+
+const char *
+ncpaddr_ntoa(const struct ncpaddr *addr)
+{
+ return ncpaddr_ntowa(addr);
+}
+
+
+int
+ncpaddr_aton(struct ncpaddr *addr, struct ncp *ncp, const char *data)
+{
+ struct ncprange range;
+
+ if (!ncprange_aton(&range, ncp, data))
+ return 0;
+
+ if (range.ncprange_family == AF_INET && range.ncprange_ip4width != 32 &&
+ range.ncprange_ip4addr.s_addr != INADDR_ANY) {
+ log_Printf(LogWARN, "ncpaddr_aton: %s: Only 32 bits allowed\n", data);
+ return 0;
+ }
+
+#ifndef NOINET6
+ if (range.ncprange_family == AF_INET6 && range.ncprange_ip6width != 128 &&
+ !IN6_IS_ADDR_UNSPECIFIED(&range.ncprange_ip6addr)) {
+ log_Printf(LogWARN, "ncpaddr_aton: %s: Only 128 bits allowed\n", data);
+ return 0;
+ }
+#endif
+
+ switch (range.ncprange_family) {
+ case AF_INET:
+ addr->ncpaddr_family = range.ncprange_family;
+ addr->ncpaddr_ip4addr = range.ncprange_ip4addr;
+ return 1;
+
+#ifndef NOINET6
+ case AF_INET6:
+ addr->ncpaddr_family = range.ncprange_family;
+ addr->ncpaddr_ip6addr = range.ncprange_ip6addr;
+ return 1;
+#endif
+ }
+
+ return 0;
+}
+
+void
+ncprange_init(struct ncprange *range)
+{
+ range->ncprange_family = AF_UNSPEC;
+}
+
+int
+ncprange_isset(const struct ncprange *range)
+{
+ return range->ncprange_family != AF_UNSPEC;
+}
+
+int
+ncprange_equal(const struct ncprange *range, const struct ncprange *cmp)
+{
+ if (range->ncprange_family != cmp->ncprange_family)
+ return 0;
+
+ switch (range->ncprange_family) {
+ case AF_INET:
+ if (range->ncprange_ip4addr.s_addr != cmp->ncprange_ip4addr.s_addr)
+ return 0;
+ return range->ncprange_ip4mask.s_addr == cmp->ncprange_ip4mask.s_addr;
+
+#ifndef NOINET6
+ case AF_INET6:
+ if (range->ncprange_ip6width != cmp->ncprange_ip6width)
+ return 0;
+ return !memcmp(&range->ncprange_ip6addr, &cmp->ncprange_ip6addr,
+ sizeof range->ncprange_ip6addr);
+#endif
+
+ case AF_UNSPEC:
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+ncprange_isdefault(const struct ncprange *range)
+{
+ switch (range->ncprange_family) {
+ case AF_INET:
+ if (range->ncprange_ip4addr.s_addr == INADDR_ANY)
+ return 1;
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ if (range->ncprange_ip6width == 0 &&
+ IN6_IS_ADDR_UNSPECIFIED(&range->ncprange_ip6addr))
+ return 1;
+ break;
+#endif
+ }
+
+ return 0;
+}
+
+void
+ncprange_setdefault(struct ncprange *range, int af)
+{
+ memset(range, '\0', sizeof *range);
+ range->ncprange_family = af;
+}
+
+int
+ncprange_contains(const struct ncprange *range, const struct ncpaddr *addr)
+{
+#ifndef NOINET6
+ const u_char masks[] = { 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff };
+ const u_char *addrp, *rangep;
+ int bits;
+#endif
+
+ if (range->ncprange_family != addr->ncpaddr_family)
+ return 0;
+
+ switch (range->ncprange_family) {
+ case AF_INET:
+ return !((addr->ncpaddr_ip4addr.s_addr ^ range->ncprange_ip4addr.s_addr) &
+ range->ncprange_ip4mask.s_addr);
+
+#ifndef NOINET6
+ case AF_INET6:
+ rangep = (const u_char *)range->ncprange_ip6addr.s6_addr;
+ addrp = (const u_char *)addr->ncpaddr_ip6addr.s6_addr;
+
+ for (bits = range->ncprange_ip6width; bits > 0; bits -= 8)
+ if ((*addrp++ ^ *rangep++) & masks[bits > 7 ? 7 : bits - 1])
+ return 0;
+
+ return 1;
+#endif
+ }
+
+ return 0;
+}
+
+int
+ncprange_containsip4(const struct ncprange *range, struct in_addr addr)
+{
+ switch (range->ncprange_family) {
+ case AF_INET:
+ return !((addr.s_addr ^ range->ncprange_ip4addr.s_addr) &
+ range->ncprange_ip4mask.s_addr);
+ }
+
+ return 0;
+}
+
+void
+ncprange_copy(struct ncprange *range, const struct ncprange *from)
+{
+ switch (from->ncprange_family) {
+ case AF_INET:
+ range->ncprange_family = AF_INET;
+ range->ncprange_ip4addr = from->ncprange_ip4addr;
+ range->ncprange_ip4mask = from->ncprange_ip4mask;
+ range->ncprange_ip4width = from->ncprange_ip4width;
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ range->ncprange_family = AF_INET6;
+ range->ncprange_ip6addr = from->ncprange_ip6addr;
+ range->ncprange_ip6width = from->ncprange_ip6width;
+ break;
+#endif
+
+ default:
+ range->ncprange_family = AF_UNSPEC;
+ }
+}
+
+void
+ncprange_set(struct ncprange *range, const struct ncpaddr *addr, int width)
+{
+ ncprange_sethost(range, addr);
+ ncprange_setwidth(range, width);
+}
+
+void
+ncprange_sethost(struct ncprange *range, const struct ncpaddr *from)
+{
+ switch (from->ncpaddr_family) {
+ case AF_INET:
+ range->ncprange_family = AF_INET;
+ range->ncprange_ip4addr = from->ncpaddr_ip4addr;
+ if (from->ncpaddr_ip4addr.s_addr == INADDR_ANY) {
+ range->ncprange_ip4mask.s_addr = INADDR_ANY;
+ range->ncprange_ip4width = 0;
+ } else {
+ range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
+ range->ncprange_ip4width = 32;
+ }
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ range->ncprange_family = AF_INET6;
+ range->ncprange_ip6addr = from->ncpaddr_ip6addr;
+ range->ncprange_ip6width = 128;
+ break;
+#endif
+
+ default:
+ range->ncprange_family = AF_UNSPEC;
+ }
+}
+
+int
+ncprange_ishost(const struct ncprange *range)
+{
+ switch (range->ncprange_family) {
+ case AF_INET:
+ return range->ncprange_ip4width == 32;
+#ifndef NOINET6
+ case AF_INET6:
+ return range->ncprange_ip6width == 128;
+#endif
+ }
+
+ return (0);
+}
+
+int
+ncprange_setwidth(struct ncprange *range, int width)
+{
+ switch (range->ncprange_family) {
+ case AF_INET:
+ if (width < 0 || width > 32)
+ break;
+ range->ncprange_ip4width = width;
+ range->ncprange_ip4mask = bits2mask4(width);
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ if (width < 0 || width > 128)
+ break;
+ range->ncprange_ip6width = width;
+ break;
+#endif
+
+ case AF_UNSPEC:
+ return 1;
+ }
+
+ return 0;
+}
+
+void
+ncprange_setip4host(struct ncprange *range, struct in_addr from)
+{
+ range->ncprange_family = AF_INET;
+ range->ncprange_ip4addr = from;
+ if (from.s_addr == INADDR_ANY) {
+ range->ncprange_ip4mask.s_addr = INADDR_ANY;
+ range->ncprange_ip4width = 0;
+ } else {
+ range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
+ range->ncprange_ip4width = 32;
+ }
+}
+
+void
+ncprange_setip4(struct ncprange *range, struct in_addr from, struct in_addr msk)
+{
+ range->ncprange_family = AF_INET;
+ range->ncprange_ip4addr = from;
+ range->ncprange_ip4mask = msk;
+ range->ncprange_ip4width = mask42bits(msk);
+}
+
+
+int
+ncprange_setip4mask(struct ncprange *range, struct in_addr mask)
+{
+ if (range->ncprange_family != AF_INET)
+ return 0;
+ range->ncprange_ip4mask = mask;
+ range->ncprange_ip4width = mask42bits(mask);
+ return 1;
+}
+
+void
+ncprange_setsa(struct ncprange *range, const struct sockaddr *host,
+ const struct sockaddr *mask)
+{
+ const struct sockaddr_in *host4 = (const struct sockaddr_in *)host;
+ const struct sockaddr_in *mask4 = (const struct sockaddr_in *)mask;
+#ifndef NOINET6
+ const struct sockaddr_in6 *host6 = (const struct sockaddr_in6 *)host;
+ const struct sockaddr_in6 *mask6 = (const struct sockaddr_in6 *)mask;
+#endif
+
+ switch (host->sa_family) {
+ case AF_INET:
+ range->ncprange_family = AF_INET;
+ range->ncprange_ip4addr = host4->sin_addr;
+ if (host4->sin_addr.s_addr == INADDR_ANY) {
+ range->ncprange_ip4mask.s_addr = INADDR_ANY;
+ range->ncprange_ip4width = 0;
+ } else if (mask4 && mask4->sin_family == AF_INET) {
+ range->ncprange_ip4mask.s_addr = mask4->sin_addr.s_addr;
+ range->ncprange_ip4width = mask42bits(mask4->sin_addr);
+ } else {
+ range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
+ range->ncprange_ip4width = 32;
+ }
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ range->ncprange_family = AF_INET6;
+ range->ncprange_ip6addr = host6->sin6_addr;
+ if (IN6_IS_ADDR_UNSPECIFIED(&host6->sin6_addr))
+ range->ncprange_ip6width = 0;
+ else
+ range->ncprange_ip6width = mask6 ? mask62bits(&mask6->sin6_addr) : 128;
+ break;
+#endif
+
+ default:
+ range->ncprange_family = AF_UNSPEC;
+ }
+}
+
+void
+ncprange_getsa(const struct ncprange *range, struct sockaddr_storage *host,
+ struct sockaddr_storage *mask)
+{
+ struct sockaddr_in *host4 = (struct sockaddr_in *)host;
+ struct sockaddr_in *mask4 = (struct sockaddr_in *)mask;
+#ifndef NOINET6
+ struct sockaddr_in6 *host6 = (struct sockaddr_in6 *)host;
+ struct sockaddr_in6 *mask6 = (struct sockaddr_in6 *)mask;
+#endif
+
+ memset(host, '\0', sizeof(*host));
+ if (mask)
+ memset(mask, '\0', sizeof(*mask));
+
+ switch (range->ncprange_family) {
+ case AF_INET:
+ host4->sin_family = AF_INET;
+ host4->sin_len = sizeof(*host4);
+ host4->sin_addr = range->ncprange_ip4addr;
+ if (mask4) {
+ mask4->sin_family = AF_INET;
+ mask4->sin_len = sizeof(*host4);
+ mask4->sin_addr = range->ncprange_ip4mask;
+ }
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ host6->sin6_family = AF_INET6;
+ host6->sin6_len = sizeof(*host6);
+ host6->sin6_addr = range->ncprange_ip6addr;
+ if (mask6) {
+ mask6->sin6_family = AF_INET6;
+ mask6->sin6_len = sizeof(*host6);
+ mask6->sin6_addr = bits2mask6(range->ncprange_ip6width);
+ }
+ break;
+#endif
+
+ default:
+ host->ss_family = AF_UNSPEC;
+ if (mask)
+ mask->ss_family = AF_UNSPEC;
+ break;
+ }
+}
+
+int
+ncprange_getaddr(const struct ncprange *range, struct ncpaddr *addr)
+{
+ switch (range->ncprange_family) {
+ case AF_INET:
+ addr->ncpaddr_family = AF_INET;
+ addr->ncpaddr_ip4addr = range->ncprange_ip4addr;
+ return 1;
+#ifndef NOINET6
+ case AF_INET6:
+ addr->ncpaddr_family = AF_INET6;
+ addr->ncpaddr_ip6addr = range->ncprange_ip6addr;
+ return 1;
+#endif
+ }
+
+ return 0;
+}
+
+int
+ncprange_getip4addr(const struct ncprange *range, struct in_addr *addr)
+{
+ if (range->ncprange_family != AF_INET)
+ return 0;
+
+ *addr = range->ncprange_ip4addr;
+ return 1;
+}
+
+int
+ncprange_getip4mask(const struct ncprange *range, struct in_addr *mask)
+{
+ switch (range->ncprange_family) {
+ case AF_INET:
+ *mask = range->ncprange_ip4mask;
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+ncprange_getwidth(const struct ncprange *range, int *width)
+{
+ switch (range->ncprange_family) {
+ case AF_INET:
+ *width = range->ncprange_ip4width;
+ return 1;
+#ifndef NOINET6
+ case AF_INET6:
+ *width = range->ncprange_ip6width;
+ return 1;
+#endif
+ }
+
+ return 0;
+}
+
+const char *
+ncprange_ntoa(const struct ncprange *range)
+{
+ char *res;
+ struct ncpaddr addr;
+ int len;
+
+ if (!ncprange_getaddr(range, &addr))
+ return "<AF_UNSPEC>";
+
+ res = ncpaddr_ntowa(&addr);
+ len = strlen(res);
+ if (len >= NCP_ASCIIBUFFERSIZE - 1)
+ return res;
+
+ switch (range->ncprange_family) {
+ case AF_INET:
+ if (range->ncprange_ip4width == -1) {
+ /* A non-contiguous mask */
+ for (; len >= 3; res[len -= 2] = '\0')
+ if (strcmp(res + len - 2, ".0"))
+ break;
+ snprintf(res + len, sizeof res - len, "&0x%08lx",
+ (unsigned long)ntohl(range->ncprange_ip4mask.s_addr));
+ } else if (range->ncprange_ip4width < 32)
+ snprintf(res + len, sizeof res - len, "/%d", range->ncprange_ip4width);
+
+ return res;
+
+#ifndef NOINET6
+ case AF_INET6:
+ if (range->ncprange_ip6width != 128)
+ snprintf(res + len, sizeof res - len, "/%d", range->ncprange_ip6width);
+
+ return res;
+#endif
+ }
+
+ return "<AF_UNSPEC>";
+}
+
+#ifndef NOINET6
+int
+ncprange_scopeid(const struct ncprange *range)
+{
+ const struct in6_addr *sin6;
+ int scopeid = -1;
+
+ if (range->ncprange_family == AF_INET6) {
+ sin6 = &range->ncprange_ip6addr;
+ if (IN6_IS_ADDR_LINKLOCAL(sin6) || IN6_IS_ADDR_MC_LINKLOCAL(sin6))
+ if ((scopeid = ntohs(*(const u_short *)&sin6->s6_addr[2])) == 0)
+ scopeid = -1;
+ }
+
+ return scopeid;
+}
+#endif
+
+int
+ncprange_aton(struct ncprange *range, struct ncp *ncp, const char *data)
+{
+ int bits, len;
+ char *wp;
+ const char *cp;
+ char *s;
+
+ len = strcspn(data, "/");
+
+ if (ncp && strncasecmp(data, "HISADDR", len) == 0) {
+ range->ncprange_family = AF_INET;
+ range->ncprange_ip4addr = ncp->ipcp.peer_ip;
+ range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
+ range->ncprange_ip4width = 32;
+ return 1;
+#ifndef NOINET6
+ } else if (ncp && strncasecmp(data, "HISADDR6", len) == 0) {
+ range->ncprange_family = AF_INET6;
+ range->ncprange_ip6addr = ncp->ipv6cp.hisaddr.ncpaddr_ip6addr;
+ range->ncprange_ip6width = 128;
+ return 1;
+#endif
+ } else if (ncp && strncasecmp(data, "MYADDR", len) == 0) {
+ range->ncprange_family = AF_INET;
+ range->ncprange_ip4addr = ncp->ipcp.my_ip;
+ range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
+ range->ncprange_ip4width = 32;
+ return 1;
+#ifndef NOINET6
+ } else if (ncp && strncasecmp(data, "MYADDR6", len) == 0) {
+ range->ncprange_family = AF_INET6;
+ range->ncprange_ip6addr = ncp->ipv6cp.myaddr.ncpaddr_ip6addr;
+ range->ncprange_ip6width = 128;
+ return 1;
+#endif
+ } else if (ncp && strncasecmp(data, "DNS0", len) == 0) {
+ range->ncprange_family = AF_INET;
+ range->ncprange_ip4addr = ncp->ipcp.ns.dns[0];
+ range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
+ range->ncprange_ip4width = 32;
+ return 1;
+ } else if (ncp && strncasecmp(data, "DNS1", len) == 0) {
+ range->ncprange_family = AF_INET;
+ range->ncprange_ip4addr = ncp->ipcp.ns.dns[1];
+ range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
+ range->ncprange_ip4width = 32;
+ return 1;
+ }
+
+ s = (char *)alloca(len + 1);
+ strncpy(s, data, len);
+ s[len] = '\0';
+ bits = -1;
+
+ if (data[len] != '\0') {
+ bits = strtol(data + len + 1, &wp, 0);
+ if (*wp || wp == data + len + 1 || bits < 0 || bits > 128) {
+ log_Printf(LogWARN, "ncprange_aton: bad mask width.\n");
+ return 0;
+ }
+ }
+
+ if ((cp = strchr(data, ':')) == NULL) {
+ range->ncprange_family = AF_INET;
+
+ range->ncprange_ip4addr = GetIpAddr(s);
+
+ if (range->ncprange_ip4addr.s_addr == INADDR_NONE) {
+ log_Printf(LogWARN, "ncprange_aton: %s: Bad address\n", s);
+ return 0;
+ }
+
+ if (range->ncprange_ip4addr.s_addr == INADDR_ANY) {
+ range->ncprange_ip4mask.s_addr = INADDR_ANY;
+ range->ncprange_ip4width = 0;
+ } else if (bits == -1) {
+ range->ncprange_ip4mask.s_addr = INADDR_BROADCAST;
+ range->ncprange_ip4width = 32;
+ } else if (bits > 32) {
+ log_Printf(LogWARN, "ncprange_aton: bad mask width.\n");
+ return 0;
+ } else {
+ range->ncprange_ip4mask = bits2mask4(bits);
+ range->ncprange_ip4width = bits;
+ }
+
+ return 1;
+#ifndef NOINET6
+ } else if (strchr(cp + 1, ':') != NULL) {
+ range->ncprange_family = AF_INET6;
+
+ if (inet_pton(AF_INET6, s, &range->ncprange_ip6addr) != 1) {
+ log_Printf(LogWARN, "ncprange_aton: %s: Bad address\n", s);
+ return 0;
+ }
+
+ if (IN6_IS_ADDR_UNSPECIFIED(&range->ncprange_ip6addr))
+ range->ncprange_ip6width = 0;
+ else
+ range->ncprange_ip6width = (bits == -1) ? 128 : bits;
+ return 1;
+#endif
+ }
+
+ return 0;
+}
diff --git a/usr.sbin/ppp/ncpaddr.h b/usr.sbin/ppp/ncpaddr.h
new file mode 100644
index 0000000..8c6b886
--- /dev/null
+++ b/usr.sbin/ppp/ncpaddr.h
@@ -0,0 +1,109 @@
+/*-
+ * Copyright (c) 2001 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * These structures should be treated as opaque.
+ */
+struct ncprange {
+ sa_family_t ncprange_family;
+ union {
+ struct {
+ struct in_addr ipaddr;
+ struct in_addr mask;
+ int width;
+ } ip4;
+#ifndef NOINET6
+ struct {
+ struct in6_addr ipaddr;
+ int width;
+ } ip6;
+#endif
+ } u;
+};
+
+struct ncpaddr {
+ sa_family_t ncpaddr_family;
+ union {
+ struct in_addr ip4addr;
+#ifndef NOINET6
+ struct in6_addr ip6addr;
+#endif
+ } u;
+};
+
+struct ncp;
+
+extern void ncpaddr_init(struct ncpaddr *);
+extern int ncpaddr_isset(const struct ncpaddr *);
+extern int ncpaddr_isdefault(const struct ncpaddr *);
+extern int ncpaddr_equal(const struct ncpaddr *, const struct ncpaddr *);
+extern void ncpaddr_copy(struct ncpaddr *, const struct ncpaddr *);
+extern void ncpaddr_setip4addr(struct ncpaddr *, u_int32_t);
+extern int ncpaddr_getip4addr(const struct ncpaddr *, u_int32_t *);
+extern void ncpaddr_setip4(struct ncpaddr *, struct in_addr);
+extern int ncpaddr_getip4(const struct ncpaddr *, struct in_addr *);
+#ifndef NOINET6
+extern void ncpaddr_setip6(struct ncpaddr *, const struct in6_addr *);
+extern int ncpaddr_getip6(const struct ncpaddr *, struct in6_addr *);
+#endif
+extern void ncpaddr_getsa(const struct ncpaddr *, struct sockaddr_storage *);
+extern void ncpaddr_setsa(struct ncpaddr *, const struct sockaddr *);
+extern const char *ncpaddr_ntoa(const struct ncpaddr *);
+extern int ncpaddr_aton(struct ncpaddr *, struct ncp *, const char *);
+
+extern void ncprange_init(struct ncprange *);
+extern int ncprange_isset(const struct ncprange *);
+extern int ncprange_equal(const struct ncprange *, const struct ncprange *);
+extern int ncprange_isdefault(const struct ncprange *);
+extern void ncprange_setdefault(struct ncprange *, int);
+extern int ncprange_contains(const struct ncprange *, const struct ncpaddr *);
+extern int ncprange_containsip4(const struct ncprange *, struct in_addr);
+extern void ncprange_copy(struct ncprange *, const struct ncprange *);
+extern void ncprange_set(struct ncprange *, const struct ncpaddr *, int);
+extern void ncprange_sethost(struct ncprange *, const struct ncpaddr *);
+extern int ncprange_ishost(const struct ncprange *);
+extern int ncprange_setwidth(struct ncprange *, int);
+extern void ncprange_setip4(struct ncprange *, struct in_addr, struct in_addr);
+extern void ncprange_setip4host(struct ncprange *, struct in_addr);
+extern int ncprange_setip4mask(struct ncprange *, struct in_addr);
+extern void ncprange_setsa(struct ncprange *, const struct sockaddr *,
+ const struct sockaddr *);
+extern void ncprange_getsa(const struct ncprange *, struct sockaddr_storage *,
+ struct sockaddr_storage *);
+extern int ncprange_getaddr(const struct ncprange *, struct ncpaddr *);
+extern int ncprange_getip4addr(const struct ncprange *, struct in_addr *);
+extern int ncprange_getip4mask(const struct ncprange *, struct in_addr *);
+extern int ncprange_getwidth(const struct ncprange *, int *);
+extern const char *ncprange_ntoa(const struct ncprange *);
+#ifndef NOINET6
+extern int ncprange_scopeid(const struct ncprange *);
+#endif
+extern int ncprange_aton(struct ncprange *, struct ncp *, const char *);
+
+#define ncpaddr_family(a) ((a)->ncpaddr_family)
+#define ncprange_family(r) ((r)->ncprange_family)
diff --git a/usr.sbin/ppp/netgraph.c b/usr.sbin/ppp/netgraph.c
new file mode 100644
index 0000000..471dc0d
--- /dev/null
+++ b/usr.sbin/ppp/netgraph.c
@@ -0,0 +1,743 @@
+/*-
+ * Copyright (c) 2000 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netgraph.h>
+#include <net/ethernet.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netgraph/ng_ether.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/ng_socket.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <sys/fcntl.h>
+#include <sys/uio.h>
+#include <termios.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "async.h"
+#include "descriptor.h"
+#include "physical.h"
+#include "main.h"
+#include "mp.h"
+#include "chat.h"
+#include "auth.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+#include "slcompress.h"
+#include "iplist.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "filter.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "bundle.h"
+#include "id.h"
+#include "netgraph.h"
+
+
+struct ngdevice {
+ struct device dev; /* What struct physical knows about */
+ int cs; /* Control socket */
+ char hook[NG_HOOKSIZ]; /* Our socket node hook */
+};
+
+#define device2ng(d) ((d)->type == NG_DEVICE ? (struct ngdevice *)d : NULL)
+#define NG_MSGBUFSZ 4096
+#define NETGRAPH_PREFIX "netgraph:"
+
+unsigned
+ng_DeviceSize(void)
+{
+ return sizeof(struct ngdevice);
+}
+
+static int
+ng_MessageOut(struct ngdevice *dev, const char *data)
+{
+ char path[NG_PATHSIZ];
+ char *fmt;
+ size_t len;
+ int pos, dpos;
+
+ /*
+ * We expect a node path, one or more spaces, a command, one or more
+ * spaces and an ascii netgraph structure.
+ */
+ data += strspn(data, " \t");
+ len = strcspn(data, " \t");
+ if (len >= sizeof path) {
+ log_Printf(LogWARN, "%s: %.*s: Node path too long\n",
+ dev->dev.name, len, data);
+ return 0;
+ }
+ memcpy(path, data, len);
+ path[len] = '\0';
+ data += len;
+
+ data += strspn(data, " \t");
+ len = strcspn(data, " \t");
+ for (pos = len; pos >= 0; pos--)
+ if (data[pos] == '%')
+ len++;
+ if ((fmt = alloca(len + 4)) == NULL) {
+ log_Printf(LogWARN, "%s: alloca(%d) failure... %s\n",
+ dev->dev.name, len + 4, strerror(errno));
+ return 0;
+ }
+
+ /*
+ * This is probably a waste of time, but we really don't want to end
+ * up stuffing unexpected % escapes into the kernel....
+ */
+ for (pos = dpos = 0; pos < (int)len;) {
+ if (data[dpos] == '%')
+ fmt[pos++] = '%';
+ fmt[pos++] = data[dpos++];
+ }
+ strcpy(fmt + pos, " %s");
+ data += dpos;
+
+ data += strspn(data, " \t");
+ if (NgSendAsciiMsg(dev->cs, path, fmt, data) < 0) {
+ log_Printf(LogDEBUG, "%s: NgSendAsciiMsg (to %s): \"%s\", \"%s\": %s\n",
+ dev->dev.name, path, fmt, data, strerror(errno));
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Get a netgraph message
+ */
+static ssize_t
+ng_MessageIn(struct physical *p, char *buf, size_t sz)
+{
+ char msgbuf[sizeof(struct ng_mesg) * 2 + NG_MSGBUFSZ];
+ struct ngdevice *dev = device2ng(p->handler);
+ struct ng_mesg *rep = (struct ng_mesg *)msgbuf;
+ char path[NG_PATHSIZ];
+ size_t len;
+
+#ifdef BROKEN_SELECT
+ struct timeval t;
+ fd_set *r;
+ int ret;
+
+ if (dev->cs < 0)
+ return 0;
+
+ if ((r = mkfdset()) == NULL) {
+ log_Printf(LogERROR, "DoLoop: Cannot create fd_set\n");
+ return -1;
+ }
+ zerofdset(r);
+ FD_SET(dev->cs, r);
+ t.tv_sec = t.tv_usec = 0;
+ ret = select(dev->cs + 1, r, NULL, NULL, &t);
+ free(r);
+
+ if (ret <= 0)
+ return 0;
+#endif
+
+ if (NgRecvAsciiMsg(dev->cs, rep, sizeof msgbuf, path)) {
+ log_Printf(LogWARN, "%s: NgRecvAsciiMsg: %s\n",
+ dev->dev.name, strerror(errno));
+ return -1;
+ }
+
+ /* XXX: Should we check rep->header.version ? */
+
+ if (sz == 0)
+ log_Printf(LogWARN, "%s: Unexpected message: %s\n", dev->dev.name,
+ rep->header.cmdstr);
+ else {
+ log_Printf(LogDEBUG, "%s: Received message: %s\n", dev->dev.name,
+ rep->header.cmdstr);
+ len = strlen(rep->header.cmdstr);
+ if (sz > len)
+ sz = len;
+ memcpy(buf, rep->header.cmdstr, sz);
+ }
+
+ return sz;
+}
+
+static ssize_t
+ng_Write(struct physical *p, const void *v, size_t n)
+{
+ struct ngdevice *dev = device2ng(p->handler);
+
+ switch (p->dl->state) {
+ case DATALINK_DIAL:
+ case DATALINK_LOGIN:
+ return ng_MessageOut(dev, v) ? (ssize_t)n : -1;
+ }
+ return NgSendData(p->fd, dev->hook, v, n) == -1 ? -1 : (ssize_t)n;
+}
+
+static ssize_t
+ng_Read(struct physical *p, void *v, size_t n)
+{
+ char hook[NG_HOOKSIZ];
+
+log_Printf(LogDEBUG, "ng_Read\n");
+ switch (p->dl->state) {
+ case DATALINK_DIAL:
+ case DATALINK_LOGIN:
+ return ng_MessageIn(p, v, n);
+ }
+
+ return NgRecvData(p->fd, v, n, hook);
+}
+
+static int
+ng_RemoveFromSet(struct physical *p, fd_set *r, fd_set *w, fd_set *e)
+{
+ struct ngdevice *dev = device2ng(p->handler);
+ int result;
+
+ if (r && dev->cs >= 0 && FD_ISSET(dev->cs, r)) {
+ FD_CLR(dev->cs, r);
+ log_Printf(LogTIMER, "%s: fdunset(ctrl) %d\n", p->link.name, dev->cs);
+ result = 1;
+ } else
+ result = 0;
+
+ /* Careful... physical_RemoveFromSet() called us ! */
+
+ p->handler->removefromset = NULL;
+ result += physical_RemoveFromSet(p, r, w, e);
+ p->handler->removefromset = ng_RemoveFromSet;
+
+ return result;
+}
+
+static void
+ng_Free(struct physical *p)
+{
+ struct ngdevice *dev = device2ng(p->handler);
+
+ physical_SetDescriptor(p);
+ if (dev->cs != -1)
+ close(dev->cs);
+ free(dev);
+}
+
+static void
+ng_device2iov(struct device *d, struct iovec *iov, int *niov,
+ int maxiov __unused, int *auxfd, int *nauxfd)
+{
+ struct ngdevice *dev = device2ng(d);
+ int sz = physical_MaxDeviceSize();
+
+ iov[*niov].iov_base = realloc(d, sz);
+ if (iov[*niov].iov_base == NULL) {
+ log_Printf(LogALERT, "Failed to allocate memory: %d\n", sz);
+ AbortProgram(EX_OSERR);
+ }
+ iov[*niov].iov_len = sz;
+ (*niov)++;
+
+ *auxfd = dev->cs;
+ (*nauxfd)++;
+}
+
+static const struct device basengdevice = {
+ NG_DEVICE,
+ "netgraph",
+ 0,
+ { CD_REQUIRED, DEF_NGCDDELAY },
+ NULL,
+ ng_RemoveFromSet,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ ng_Free,
+ ng_Read,
+ ng_Write,
+ ng_device2iov,
+ NULL,
+ NULL,
+ NULL
+};
+
+struct device *
+ng_iov2device(int type, struct physical *p, struct iovec *iov, int *niov,
+ int maxiov __unused, int *auxfd, int *nauxfd)
+{
+ if (type == NG_DEVICE) {
+ struct ngdevice *dev = (struct ngdevice *)iov[(*niov)++].iov_base;
+
+ dev = realloc(dev, sizeof *dev); /* Reduce to the correct size */
+ if (dev == NULL) {
+ log_Printf(LogALERT, "Failed to allocate memory: %d\n",
+ (int)(sizeof *dev));
+ AbortProgram(EX_OSERR);
+ }
+
+ if (*nauxfd) {
+ dev->cs = *auxfd;
+ (*nauxfd)--;
+ } else
+ dev->cs = -1;
+
+ /* Refresh function pointers etc */
+ memcpy(&dev->dev, &basengdevice, sizeof dev->dev);
+
+ /* XXX: Are netgraph always synchronous ? */
+ physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF);
+ return &dev->dev;
+ }
+
+ return NULL;
+}
+
+static int
+ng_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
+{
+ struct physical *p = descriptor2physical(d);
+ struct ngdevice *dev = device2ng(p->handler);
+ int result;
+
+ switch (p->dl->state) {
+ case DATALINK_DIAL:
+ case DATALINK_LOGIN:
+ if (r) {
+ FD_SET(dev->cs, r);
+ log_Printf(LogTIMER, "%s(ctrl): fdset(r) %d\n", p->link.name, dev->cs);
+ result = 1;
+ } else
+ result = 0;
+ break;
+
+ default:
+ result = physical_doUpdateSet(d, r, w, e, n, 0);
+ break;
+ }
+
+ return result;
+}
+
+static int
+ng_IsSet(struct fdescriptor *d, const fd_set *fdset)
+{
+ struct physical *p = descriptor2physical(d);
+ struct ngdevice *dev = device2ng(p->handler);
+ int result;
+
+ result = dev->cs >= 0 && FD_ISSET(dev->cs, fdset);
+ result += physical_IsSet(d, fdset);
+
+ return result;
+}
+
+static void
+ng_DescriptorRead(struct fdescriptor *d, struct bundle *bundle,
+ const fd_set *fdset)
+{
+ struct physical *p = descriptor2physical(d);
+ struct ngdevice *dev = device2ng(p->handler);
+
+ if (dev->cs >= 0 && FD_ISSET(dev->cs, fdset))
+ ng_MessageIn(p, NULL, 0);
+
+ if (physical_IsSet(d, fdset))
+ physical_DescriptorRead(d, bundle, fdset);
+}
+
+static struct device *
+ng_Abandon(struct ngdevice *dev, struct physical *p)
+{
+ /* Abandon our node construction */
+ close(dev->cs);
+ close(p->fd);
+ p->fd = -2; /* Nobody else need try.. */
+ free(dev);
+
+ return NULL;
+}
+
+
+/*
+ * Populate the ``word'' (of size ``sz'') named ``what'' from ``from''
+ * ending with any character from ``sep''. Point ``endp'' at the next
+ * word.
+ */
+
+#define GETSEGMENT(what, from, sep, endp) \
+ getsegment(#what, (what), sizeof(what), from, sep, endp)
+
+static int
+getsegment(const char *what, char *word, size_t sz, const char *from,
+ const char *sep, const char **endp)
+{
+ size_t len;
+
+ if ((len = strcspn(from, sep)) == 0) {
+ log_Printf(LogWARN, "%s name should not be empty !\n", what);
+ return 0;
+ }
+
+ if (len >= sz) {
+ log_Printf(LogWARN, "%s name too long, max %d !\n", what, sz - 1);
+ return 0;
+ }
+
+ strncpy(word, from, len);
+ word[len] = '\0';
+
+ *endp = from + len;
+ *endp += strspn(*endp, sep);
+
+ return 1;
+}
+
+struct device *
+ng_Create(struct physical *p)
+{
+ struct sockaddr_ng ngsock;
+ u_char rbuf[2048];
+ struct sockaddr *sock = (struct sockaddr *)&ngsock;
+ const struct hooklist *hlist;
+ const struct nodeinfo *ninfo;
+ const struct linkinfo *nlink;
+ struct ngdevice *dev;
+ struct ng_mesg *resp;
+ struct ngm_mkpeer mkp;
+ struct ngm_connect ngc;
+ const char *devp, *endp;
+ char lasthook[NG_HOOKSIZ];
+ char hook[NG_HOOKSIZ];
+ char nodetype[NG_TYPESIZ + NG_NODESIZ];
+ char modname[NG_TYPESIZ + 3];
+ char path[NG_PATHSIZ];
+ char *nodename;
+ int len, sz, done;
+ unsigned f;
+
+ dev = NULL;
+ if (p->fd < 0 && !strncasecmp(p->name.full, NETGRAPH_PREFIX,
+ sizeof NETGRAPH_PREFIX - 1)) {
+ p->fd--; /* We own the device - change fd */
+
+ if ((dev = malloc(sizeof *dev)) == NULL)
+ return NULL;
+
+ loadmodules(LOAD_VERBOSLY, "netgraph", "ng_socket", NULL);
+
+ /* Create a socket node */
+ if (ID0NgMkSockNode(NULL, &dev->cs, &p->fd) == -1) {
+ log_Printf(LogWARN, "Cannot create netgraph socket node: %s\n",
+ strerror(errno));
+ free(dev);
+ p->fd = -2;
+ return NULL;
+ }
+
+ devp = p->name.full + sizeof NETGRAPH_PREFIX - 1;
+ *lasthook = *path = '\0';
+ log_Printf(LogDEBUG, "%s: Opening netgraph device \"%s\"\n",
+ p->link.name, devp);
+ done = 0;
+
+ while (*devp != '\0' && !done) {
+ if (*devp != '[') {
+ if (*lasthook == '\0') {
+ log_Printf(LogWARN, "%s: Netgraph devices must start with"
+ " [nodetype:nodename]\n", p->link.name);
+ return ng_Abandon(dev, p);
+ }
+
+ /* Get the hook name of the new node */
+ if (!GETSEGMENT(hook, devp, ".[", &endp))
+ return ng_Abandon(dev, p);
+ log_Printf(LogDEBUG, "%s: Got hook \"%s\"\n", p->link.name, hook);
+ devp = endp;
+ if (*devp == '\0') {
+ log_Printf(LogWARN, "%s: Netgraph device must not end with a second"
+ " hook\n", p->link.name);
+ return ng_Abandon(dev, p);
+ }
+ if (devp[-1] != '[') {
+ log_Printf(LogWARN, "%s: Expected a [nodetype:nodename] at device"
+ " pos %d\n", p->link.name, devp - p->link.name - 1);
+ return ng_Abandon(dev, p);
+ }
+ } else {
+ /* Use lasthook as the hook name */
+ strcpy(hook, lasthook);
+ devp++;
+ }
+
+ /* We've got ``lasthook'' and ``hook'', get the node type */
+ if (!GETSEGMENT(nodetype, devp, "]", &endp))
+ return ng_Abandon(dev, p);
+ log_Printf(LogDEBUG, "%s: Got node \"%s\"\n", p->link.name, nodetype);
+
+ if ((nodename = strchr(nodetype, ':')) != NULL) {
+ *nodename++ = '\0';
+ if (*nodename == '\0' && *nodetype == '\0') {
+ log_Printf(LogWARN, "%s: Empty [nodetype:nodename] at device"
+ " pos %d\n", p->link.name, devp - p->link.name - 1);
+ return ng_Abandon(dev, p);
+ }
+ }
+
+ /* Ignore optional colons after nodes */
+ devp = *endp == ':' ? endp + 1 : endp;
+ if (*devp == '.')
+ devp++;
+
+ if (*lasthook == '\0') {
+ /* This is the first node in the chain */
+ if (nodename == NULL || *nodename == '\0') {
+ log_Printf(LogWARN, "%s: %s: No initial device nodename\n",
+ p->link.name, devp);
+ return ng_Abandon(dev, p);
+ }
+
+ if (*nodetype != '\0') {
+ /* Attempt to load the module */
+ snprintf(modname, sizeof modname, "ng_%s", nodetype);
+ log_Printf(LogDEBUG, "%s: Attempting to load %s.ko\n",
+ p->link.name, modname);
+ loadmodules(LOAD_QUIETLY, modname, NULL);
+ }
+
+ snprintf(path, sizeof path, "%s:", nodename);
+ /* XXX: If we have a node type, ensure it's correct */
+ } else {
+ /*
+ * Ask for a list of hooks attached to the previous node. If we
+ * find the one we're interested in, and if it's connected to a
+ * node of the right type using the correct hook, use that.
+ * If we find the hook connected to something else, fail.
+ * If we find no match, mkpeer the new node.
+ */
+ if (*nodetype == '\0') {
+ log_Printf(LogWARN, "%s: Nodetype missing at device offset %d\n",
+ p->link.name,
+ devp - p->name.full + sizeof NETGRAPH_PREFIX - 1);
+ return ng_Abandon(dev, p);
+ }
+
+ /* Get a list of node hooks */
+ if (NgSendMsg(dev->cs, path, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,
+ NULL, 0) < 0) {
+ log_Printf(LogWARN, "%s: %s Cannot send a LISTHOOOKS message: %s\n",
+ p->link.name, path, strerror(errno));
+ return ng_Abandon(dev, p);
+ }
+
+ /* Get our list back */
+ resp = (struct ng_mesg *)rbuf;
+ if (NgRecvMsg(dev->cs, resp, sizeof rbuf, NULL) <= 0) {
+ log_Printf(LogWARN, "%s: Cannot get netgraph response: %s\n",
+ p->link.name, strerror(errno));
+ return ng_Abandon(dev, p);
+ }
+
+ hlist = (const struct hooklist *)resp->data;
+ ninfo = &hlist->nodeinfo;
+
+ log_Printf(LogDEBUG, "List of netgraph node ``%s'' (id %x) hooks:\n",
+ path, ninfo->id);
+
+ /* look for a hook already attached. */
+ for (f = 0; f < ninfo->hooks; f++) {
+ nlink = &hlist->link[f];
+
+ log_Printf(LogDEBUG, " Found %s -> %s (type %s)\n", nlink->ourhook,
+ nlink->peerhook, nlink->nodeinfo.type);
+
+ if (!strcmp(nlink->ourhook, lasthook)) {
+ if (strcmp(nlink->peerhook, hook) ||
+ strcmp(nlink->nodeinfo.type, nodetype)) {
+ log_Printf(LogWARN, "%s: hook %s:%s is already in use\n",
+ p->link.name, nlink->ourhook, path);
+ return ng_Abandon(dev, p);
+ }
+ /* The node is already hooked up nicely.... reuse it */
+ break;
+ }
+ }
+
+ if (f == ninfo->hooks) {
+ /* Attempt to load the module */
+ snprintf(modname, sizeof modname, "ng_%s", nodetype);
+ log_Printf(LogDEBUG, "%s: Attempting to load %s.ko\n",
+ p->link.name, modname);
+ loadmodules(LOAD_QUIETLY, modname, NULL);
+
+ /* Create (mkpeer) the new node */
+
+ snprintf(mkp.type, sizeof mkp.type, "%s", nodetype);
+ snprintf(mkp.ourhook, sizeof mkp.ourhook, "%s", lasthook);
+ snprintf(mkp.peerhook, sizeof mkp.peerhook, "%s", hook);
+
+ log_Printf(LogDEBUG, "%s: Doing MKPEER %s%s -> %s (type %s)\n",
+ p->link.name, path, mkp.ourhook, mkp.peerhook, nodetype);
+
+ if (NgSendMsg(dev->cs, path, NGM_GENERIC_COOKIE,
+ NGM_MKPEER, &mkp, sizeof mkp) < 0) {
+ log_Printf(LogWARN, "%s Cannot create %s netgraph node: %s\n",
+ path, nodetype, strerror(errno));
+ return ng_Abandon(dev, p);
+ }
+ }
+ len = strlen(path);
+ snprintf(path + len, sizeof path - len, "%s%s",
+ path[len - 1] == ':' ? "" : ".", lasthook);
+ }
+
+ /* Get a list of node hooks */
+ if (NgSendMsg(dev->cs, path, NGM_GENERIC_COOKIE, NGM_LISTHOOKS,
+ NULL, 0) < 0) {
+ log_Printf(LogWARN, "%s: %s Cannot send a LISTHOOOKS message: %s\n",
+ p->link.name, path, strerror(errno));
+ return ng_Abandon(dev, p);
+ }
+
+ /* Get our list back */
+ resp = (struct ng_mesg *)rbuf;
+ if (NgRecvMsg(dev->cs, resp, sizeof rbuf, NULL) <= 0) {
+ log_Printf(LogWARN, "%s: Cannot get netgraph response: %s\n",
+ p->link.name, strerror(errno));
+ return ng_Abandon(dev, p);
+ }
+
+ hlist = (const struct hooklist *)resp->data;
+ ninfo = &hlist->nodeinfo;
+
+ if (*lasthook != '\0' && nodename != NULL && *nodename != '\0' &&
+ strcmp(ninfo->name, nodename) &&
+ NgNameNode(dev->cs, path, "%s", nodename) < 0) {
+ log_Printf(LogWARN, "%s: %s: Cannot name netgraph node: %s\n",
+ p->link.name, path, strerror(errno));
+ return ng_Abandon(dev, p);
+ }
+
+ if (!GETSEGMENT(lasthook, devp, " \t.[", &endp))
+ return ng_Abandon(dev, p);
+ log_Printf(LogDEBUG, "%s: Got hook \"%s\"\n", p->link.name, lasthook);
+
+ len = strlen(lasthook);
+ done = strchr(" \t", devp[len]) ? 1 : 0;
+ devp = endp;
+
+ if (*devp != '\0') {
+ if (devp[-1] == '[')
+ devp--;
+ } /* else should moan about devp[-1] being '[' ? */
+ }
+
+ snprintf(dev->hook, sizeof dev->hook, "%s", lasthook);
+
+ /* Connect the node to our socket node */
+ snprintf(ngc.path, sizeof ngc.path, "%s", path);
+ snprintf(ngc.ourhook, sizeof ngc.ourhook, "%s", dev->hook);
+ memcpy(ngc.peerhook, ngc.ourhook, sizeof ngc.peerhook);
+
+ log_Printf(LogDEBUG, "Connecting netgraph socket .:%s -> %s.%s\n",
+ ngc.ourhook, ngc.path, ngc.peerhook);
+ if (NgSendMsg(dev->cs, ".:", NGM_GENERIC_COOKIE,
+ NGM_CONNECT, &ngc, sizeof ngc) < 0) {
+ log_Printf(LogWARN, "Cannot connect %s and socket netgraph "
+ "nodes: %s\n", path, strerror(errno));
+ return ng_Abandon(dev, p);
+ }
+
+ /* Hook things up so that we monitor dev->cs */
+ p->desc.UpdateSet = ng_UpdateSet;
+ p->desc.IsSet = ng_IsSet;
+ p->desc.Read = ng_DescriptorRead;
+
+ memcpy(&dev->dev, &basengdevice, sizeof dev->dev);
+
+ } else {
+ /* See if we're a netgraph socket */
+
+ sz = sizeof ngsock;
+ if (getsockname(p->fd, sock, &sz) != -1 && sock->sa_family == AF_NETGRAPH) {
+ /*
+ * It's a netgraph node... We can't determine hook names etc, so we
+ * stay pretty impartial....
+ */
+ log_Printf(LogPHASE, "%s: Link is a netgraph node\n", p->link.name);
+
+ if ((dev = malloc(sizeof *dev)) == NULL) {
+ log_Printf(LogWARN, "%s: Cannot allocate an ether device: %s\n",
+ p->link.name, strerror(errno));
+ return NULL;
+ }
+
+ memcpy(&dev->dev, &basengdevice, sizeof dev->dev);
+ dev->cs = -1;
+ *dev->hook = '\0';
+ }
+ }
+
+ if (dev) {
+ physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNCNOACF);
+ return &dev->dev;
+ }
+
+ return NULL;
+}
diff --git a/usr.sbin/ppp/netgraph.h b/usr.sbin/ppp/netgraph.h
new file mode 100644
index 0000000..a5b4082
--- /dev/null
+++ b/usr.sbin/ppp/netgraph.h
@@ -0,0 +1,37 @@
+/*-
+ * Copyright (c) 2000 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct physical;
+struct device;
+
+#define DEF_NGCDDELAY 5 /* Default ``set cd'' value */
+
+extern struct device *ng_Create(struct physical *);
+extern struct device *ng_iov2device(int, struct physical *, struct iovec *,
+ int *, int, int *, int *);
+extern unsigned ng_DeviceSize(void);
diff --git a/usr.sbin/ppp/pap.c b/usr.sbin/ppp/pap.c
new file mode 100644
index 0000000..8eda020
--- /dev/null
+++ b/usr.sbin/ppp/pap.c
@@ -0,0 +1,303 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <stdlib.h>
+#include <string.h> /* strlen/memcpy */
+#include <termios.h>
+
+#include "layer.h"
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "auth.h"
+#include "pap.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "proto.h"
+#include "async.h"
+#include "throughput.h"
+#include "ccp.h"
+#include "link.h"
+#include "descriptor.h"
+#include "physical.h"
+#include "iplist.h"
+#include "slcompress.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "chat.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+
+static const char * const papcodes[] = {
+ "???", "REQUEST", "SUCCESS", "FAILURE"
+};
+#define MAXPAPCODE (sizeof papcodes / sizeof papcodes[0] - 1)
+
+static void
+pap_Req(struct authinfo *authp)
+{
+ struct bundle *bundle = authp->physical->dl->bundle;
+ struct fsmheader lh;
+ struct mbuf *bp;
+ u_char *cp;
+ int namelen, keylen, plen;
+
+ namelen = strlen(bundle->cfg.auth.name);
+ keylen = strlen(bundle->cfg.auth.key);
+ plen = namelen + keylen + 2;
+ log_Printf(LogDEBUG, "pap_Req: namelen = %d, keylen = %d\n", namelen, keylen);
+ log_Printf(LogPHASE, "Pap Output: %s ********\n", bundle->cfg.auth.name);
+ if (*bundle->cfg.auth.name == '\0')
+ log_Printf(LogWARN, "Sending empty PAP authname!\n");
+ lh.code = PAP_REQUEST;
+ lh.id = authp->id;
+ lh.length = htons(plen + sizeof(struct fsmheader));
+ bp = m_get(plen + sizeof(struct fsmheader), MB_PAPOUT);
+ memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader));
+ cp = MBUF_CTOP(bp) + sizeof(struct fsmheader);
+ *cp++ = namelen;
+ memcpy(cp, bundle->cfg.auth.name, namelen);
+ cp += namelen;
+ *cp++ = keylen;
+ memcpy(cp, bundle->cfg.auth.key, keylen);
+ link_PushPacket(&authp->physical->link, bp, bundle,
+ LINK_QUEUES(&authp->physical->link) - 1, PROTO_PAP);
+}
+
+static void
+SendPapCode(struct authinfo *authp, int code, const char *message)
+{
+ struct fsmheader lh;
+ struct mbuf *bp;
+ u_char *cp;
+ int plen, mlen;
+
+ lh.code = code;
+ lh.id = authp->id;
+ mlen = strlen(message);
+ plen = mlen + 1;
+ lh.length = htons(plen + sizeof(struct fsmheader));
+ bp = m_get(plen + sizeof(struct fsmheader), MB_PAPOUT);
+ memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader));
+ cp = MBUF_CTOP(bp) + sizeof(struct fsmheader);
+ /*
+ * If our message is longer than 255 bytes, truncate the length to
+ * 255 and send the entire message anyway. Maybe the other end will
+ * display it... (see pap_Input() !)
+ */
+ *cp++ = mlen > 255 ? 255 : mlen;
+ memcpy(cp, message, mlen);
+ log_Printf(LogPHASE, "Pap Output: %s\n", papcodes[code]);
+
+ link_PushPacket(&authp->physical->link, bp, authp->physical->dl->bundle,
+ LINK_QUEUES(&authp->physical->link) - 1, PROTO_PAP);
+}
+
+static void
+pap_Success(struct authinfo *authp)
+{
+ struct bundle *bundle = authp->physical->dl->bundle;
+
+ datalink_GotAuthname(authp->physical->dl, authp->in.name);
+#ifndef NORADIUS
+ if (*bundle->radius.cfg.file && bundle->radius.repstr)
+ SendPapCode(authp, PAP_ACK, bundle->radius.repstr);
+ else
+#endif
+ SendPapCode(authp, PAP_ACK, "Greetings!!");
+ authp->physical->link.lcp.auth_ineed = 0;
+ if (Enabled(bundle, OPT_UTMP))
+ physical_Login(authp->physical, authp->in.name);
+
+ if (authp->physical->link.lcp.auth_iwait == 0)
+ /*
+ * Either I didn't need to authenticate, or I've already been
+ * told that I got the answer right.
+ */
+ datalink_AuthOk(authp->physical->dl);
+}
+
+static void
+pap_Failure(struct authinfo *authp)
+{
+ SendPapCode(authp, PAP_NAK, "Login incorrect");
+ datalink_AuthNotOk(authp->physical->dl);
+}
+
+void
+pap_Init(struct authinfo *pap, struct physical *p)
+{
+ auth_Init(pap, p, pap_Req, pap_Success, pap_Failure);
+}
+
+struct mbuf *
+pap_Input(struct bundle *bundle, struct link *l, struct mbuf *bp)
+{
+ struct physical *p = link2physical(l);
+ struct authinfo *authp = &p->dl->pap;
+ u_char nlen, klen, *key;
+ const char *txt;
+ int txtlen;
+
+ if (p == NULL) {
+ log_Printf(LogERROR, "pap_Input: Not a physical link - dropped\n");
+ m_freem(bp);
+ return NULL;
+ }
+
+ if (bundle_Phase(bundle) != PHASE_NETWORK &&
+ bundle_Phase(bundle) != PHASE_AUTHENTICATE) {
+ log_Printf(LogPHASE, "Unexpected pap input - dropped !\n");
+ m_freem(bp);
+ return NULL;
+ }
+
+ if ((bp = auth_ReadHeader(authp, bp)) == NULL &&
+ ntohs(authp->in.hdr.length) == 0) {
+ log_Printf(LogWARN, "Pap Input: Truncated header !\n");
+ return NULL;
+ }
+
+ if (authp->in.hdr.code == 0 || authp->in.hdr.code > MAXPAPCODE) {
+ log_Printf(LogPHASE, "Pap Input: %d: Bad PAP code !\n", authp->in.hdr.code);
+ m_freem(bp);
+ return NULL;
+ }
+
+ if (authp->in.hdr.code != PAP_REQUEST && authp->id != authp->in.hdr.id &&
+ Enabled(bundle, OPT_IDCHECK)) {
+ /* Wrong conversation dude ! */
+ log_Printf(LogPHASE, "Pap Input: %s dropped (got id %d, not %d)\n",
+ papcodes[authp->in.hdr.code], authp->in.hdr.id, authp->id);
+ m_freem(bp);
+ return NULL;
+ }
+ m_settype(bp, MB_PAPIN);
+ authp->id = authp->in.hdr.id; /* We respond with this id */
+
+ if (bp) {
+ bp = mbuf_Read(bp, &nlen, 1);
+ if (authp->in.hdr.code == PAP_ACK) {
+ /*
+ * Don't restrict the length of our acknowledgement freetext to
+ * nlen (a one-byte length). Show the rest of the ack packet
+ * instead. This isn't really part of the protocol.....
+ */
+ bp = m_pullup(bp);
+ txt = MBUF_CTOP(bp);
+ txtlen = m_length(bp);
+ } else {
+ bp = auth_ReadName(authp, bp, nlen);
+ txt = authp->in.name;
+ txtlen = strlen(authp->in.name);
+ }
+ } else {
+ txt = "";
+ txtlen = 0;
+ }
+
+ log_Printf(LogPHASE, "Pap Input: %s (%.*s)\n",
+ papcodes[authp->in.hdr.code], txtlen, txt);
+
+ switch (authp->in.hdr.code) {
+ case PAP_REQUEST:
+ if (bp == NULL) {
+ log_Printf(LogPHASE, "Pap Input: No key given !\n");
+ break;
+ }
+ bp = mbuf_Read(bp, &klen, 1);
+ if (m_length(bp) < klen) {
+ log_Printf(LogERROR, "Pap Input: Truncated key !\n");
+ break;
+ }
+ if ((key = malloc(klen+1)) == NULL) {
+ log_Printf(LogERROR, "Pap Input: Out of memory !\n");
+ break;
+ }
+ bp = mbuf_Read(bp, key, klen);
+ key[klen] = '\0';
+
+#ifndef NORADIUS
+ if (*bundle->radius.cfg.file) {
+ if (!radius_Authenticate(&bundle->radius, authp, authp->in.name,
+ key, strlen(key), NULL, 0))
+ pap_Failure(authp);
+ } else
+#endif
+ if (auth_Validate(bundle, authp->in.name, key))
+ pap_Success(authp);
+ else
+ pap_Failure(authp);
+
+ free(key);
+ break;
+
+ case PAP_ACK:
+ auth_StopTimer(authp);
+ if (p->link.lcp.auth_iwait == PROTO_PAP) {
+ p->link.lcp.auth_iwait = 0;
+ if (p->link.lcp.auth_ineed == 0)
+ /*
+ * We've succeeded in our ``login''
+ * If we're not expecting the peer to authenticate (or he already
+ * has), proceed to network phase.
+ */
+ datalink_AuthOk(p->dl);
+ }
+ break;
+
+ case PAP_NAK:
+ auth_StopTimer(authp);
+ datalink_AuthNotOk(p->dl);
+ break;
+ }
+
+ m_freem(bp);
+ return NULL;
+}
diff --git a/usr.sbin/ppp/pap.h b/usr.sbin/ppp/pap.h
new file mode 100644
index 0000000..8e8d457
--- /dev/null
+++ b/usr.sbin/ppp/pap.h
@@ -0,0 +1,40 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+#define PAP_REQUEST 1
+#define PAP_ACK 2
+#define PAP_NAK 3
+
+struct mbuf;
+struct physical;
+struct authinfo;
+
+extern void pap_Init(struct authinfo *, struct physical *);
+extern struct mbuf *pap_Input(struct bundle *, struct link *, struct mbuf *);
diff --git a/usr.sbin/ppp/physical.c b/usr.sbin/ppp/physical.c
new file mode 100644
index 0000000..ed3ab9c
--- /dev/null
+++ b/usr.sbin/ppp/physical.c
@@ -0,0 +1,1140 @@
+/*
+ * Written by Eivind Eklund <eivind@yes.no>
+ * for Yes Interactive
+ *
+ * Copyright (C) 1998, Yes Interactive. All rights reserved.
+ *
+ * Redistribution and use in any form is permitted. Redistribution in
+ * source form should include the above copyright and this set of
+ * conditions, because large sections american law seems to have been
+ * created by a bunch of jerks on drugs that are now illegal, forcing
+ * me to include this copyright-stuff instead of placing this in the
+ * public domain. The name of of 'Yes Interactive' or 'Eivind Eklund'
+ * 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#ifdef NOSUID
+#include <signal.h>
+#endif
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <sysexits.h>
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+#include <utmp.h>
+#if defined(__OpenBSD__) || defined(__NetBSD__)
+#include <sys/ioctl.h>
+#include <util.h>
+#else
+#include <libutil.h>
+#endif
+
+#include "layer.h"
+#ifndef NONAT
+#include "nat_cmd.h"
+#endif
+#include "proto.h"
+#include "acf.h"
+#include "vjcomp.h"
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "id.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "throughput.h"
+#include "sync.h"
+#include "async.h"
+#include "iplist.h"
+#include "slcompress.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "ccp.h"
+#include "link.h"
+#include "physical.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "prompt.h"
+#include "chat.h"
+#include "auth.h"
+#include "main.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+#include "tcp.h"
+#include "udp.h"
+#include "exec.h"
+#include "tty.h"
+#ifndef NONETGRAPH
+#include "ether.h"
+#include "netgraph.h"
+#endif
+#ifndef NOATM
+#include "atm.h"
+#endif
+#include "tcpmss.h"
+
+#define PPPOTCPLINE "ppp"
+
+static int physical_DescriptorWrite(struct fdescriptor *, struct bundle *,
+ const fd_set *);
+
+static unsigned
+physical_DeviceSize(void)
+{
+ return sizeof(struct device);
+}
+
+struct {
+ struct device *(*create)(struct physical *);
+ struct device *(*iov2device)(int, struct physical *, struct iovec *,
+ int *, int, int *, int *);
+ unsigned (*DeviceSize)(void);
+} devices[] = {
+ { tty_Create, tty_iov2device, tty_DeviceSize },
+#ifndef NONETGRAPH
+ /*
+ * This must come before ``udp'' so that the probe routine is
+ * able to identify it as a more specific type of SOCK_DGRAM.
+ */
+ { ether_Create, ether_iov2device, ether_DeviceSize },
+#ifdef EXPERIMENTAL_NETGRAPH
+ { ng_Create, ng_iov2device, ng_DeviceSize },
+#endif
+#endif
+#ifndef NOATM
+ /* Ditto for ATM devices */
+ { atm_Create, atm_iov2device, atm_DeviceSize },
+#endif
+ { tcp_Create, tcp_iov2device, tcp_DeviceSize },
+ { udp_Create, udp_iov2device, udp_DeviceSize },
+ { exec_Create, exec_iov2device, exec_DeviceSize }
+};
+
+#define NDEVICES (sizeof devices / sizeof devices[0])
+
+static int
+physical_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e,
+ int *n)
+{
+ return physical_doUpdateSet(d, r, w, e, n, 0);
+}
+
+void
+physical_SetDescriptor(struct physical *p)
+{
+ p->desc.type = PHYSICAL_DESCRIPTOR;
+ p->desc.UpdateSet = physical_UpdateSet;
+ p->desc.IsSet = physical_IsSet;
+ p->desc.Read = physical_DescriptorRead;
+ p->desc.Write = physical_DescriptorWrite;
+}
+
+struct physical *
+physical_Create(struct datalink *dl, int type)
+{
+ struct physical *p;
+
+ p = (struct physical *)malloc(sizeof(struct physical));
+ if (!p)
+ return NULL;
+
+ p->link.type = PHYSICAL_LINK;
+ p->link.name = dl->name;
+ p->link.len = sizeof *p;
+
+ /* The sample period is fixed - see physical2iov() & iov2physical() */
+ throughput_init(&p->link.stats.total, SAMPLE_PERIOD);
+ p->link.stats.parent = dl->bundle->ncp.mp.active ?
+ &dl->bundle->ncp.mp.link.stats.total : NULL;
+ p->link.stats.gather = 1;
+
+ memset(p->link.Queue, '\0', sizeof p->link.Queue);
+ memset(p->link.proto_in, '\0', sizeof p->link.proto_in);
+ memset(p->link.proto_out, '\0', sizeof p->link.proto_out);
+ link_EmptyStack(&p->link);
+
+ p->handler = NULL;
+ physical_SetDescriptor(p);
+ p->type = type;
+
+ hdlc_Init(&p->hdlc, &p->link.lcp);
+ async_Init(&p->async);
+
+ p->fd = -1;
+ p->out = NULL;
+ p->connect_count = 0;
+ p->dl = dl;
+ p->input.sz = 0;
+ *p->name.full = '\0';
+ p->name.base = p->name.full;
+
+ p->Utmp = 0;
+ p->session_owner = (pid_t)-1;
+
+ p->cfg.rts_cts = MODEM_CTSRTS;
+ p->cfg.speed = MODEM_SPEED;
+ p->cfg.parity = CS8;
+ memcpy(p->cfg.devlist, MODEM_LIST, sizeof MODEM_LIST);
+ p->cfg.ndev = NMODEMS;
+ p->cfg.cd.necessity = CD_DEFAULT;
+ p->cfg.cd.delay = 0; /* reconfigured or device specific default */
+
+ lcp_Init(&p->link.lcp, dl->bundle, &p->link, &dl->fsmp);
+ ccp_Init(&p->link.ccp, dl->bundle, &p->link, &dl->fsmp);
+
+ return p;
+}
+
+static const struct parity {
+ const char *name;
+ const char *name1;
+ int set;
+} validparity[] = {
+ { "even", "P_EVEN", CS7 | PARENB },
+ { "odd", "P_ODD", CS7 | PARENB | PARODD },
+ { "none", "P_ZERO", CS8 },
+ { NULL, NULL, 0 },
+};
+
+static int
+GetParityValue(const char *str)
+{
+ const struct parity *pp;
+
+ for (pp = validparity; pp->name; pp++) {
+ if (strcasecmp(pp->name, str) == 0 ||
+ strcasecmp(pp->name1, str) == 0) {
+ return pp->set;
+ }
+ }
+ return (-1);
+}
+
+int
+physical_SetParity(struct physical *p, const char *str)
+{
+ struct termios rstio;
+ int val;
+
+ val = GetParityValue(str);
+ if (val > 0) {
+ p->cfg.parity = val;
+ if (p->fd >= 0) {
+ tcgetattr(p->fd, &rstio);
+ rstio.c_cflag &= ~(CSIZE | PARODD | PARENB);
+ rstio.c_cflag |= val;
+ tcsetattr(p->fd, TCSADRAIN, &rstio);
+ }
+ return 0;
+ }
+ log_Printf(LogWARN, "%s: %s: Invalid parity\n", p->link.name, str);
+ return -1;
+}
+
+unsigned
+physical_GetSpeed(struct physical *p)
+{
+ if (p->handler && p->handler->speed)
+ return (*p->handler->speed)(p);
+
+ return 0;
+}
+
+int
+physical_SetSpeed(struct physical *p, unsigned speed)
+{
+ if (UnsignedToSpeed(speed) != B0) {
+ p->cfg.speed = speed;
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+physical_Raw(struct physical *p)
+{
+ if (p->handler && p->handler->raw)
+ return (*p->handler->raw)(p);
+
+ return 1;
+}
+
+void
+physical_Offline(struct physical *p)
+{
+ if (p->handler && p->handler->offline)
+ (*p->handler->offline)(p);
+ log_Printf(LogPHASE, "%s: Disconnected!\n", p->link.name);
+}
+
+static int
+physical_Lock(struct physical *p)
+{
+ int res;
+
+ if (*p->name.full == '/' && p->type != PHYS_DIRECT &&
+ (res = ID0uu_lock(p->name.base)) != UU_LOCK_OK) {
+ if (res == UU_LOCK_INUSE)
+ log_Printf(LogPHASE, "%s: %s is in use\n", p->link.name, p->name.full);
+ else
+ log_Printf(LogPHASE, "%s: %s is in use: uu_lock: %s\n",
+ p->link.name, p->name.full, uu_lockerr(res));
+ return 0;
+ }
+
+ return 1;
+}
+
+static void
+physical_Unlock(struct physical *p)
+{
+ if (*p->name.full == '/' && p->type != PHYS_DIRECT &&
+ ID0uu_unlock(p->name.base) == -1)
+ log_Printf(LogALERT, "%s: Can't uu_unlock %s\n", p->link.name,
+ p->name.base);
+}
+
+void
+physical_Close(struct physical *p)
+{
+ int newsid;
+ char fn[PATH_MAX];
+
+ if (p->fd < 0)
+ return;
+
+ log_Printf(LogDEBUG, "%s: Close\n", p->link.name);
+
+ if (p->handler && p->handler->cooked)
+ (*p->handler->cooked)(p);
+
+ physical_StopDeviceTimer(p);
+ if (p->Utmp) {
+ if (p->handler && (p->handler->type == TCP_DEVICE ||
+ p->handler->type == UDP_DEVICE))
+ /* Careful - we logged in on line ``ppp'' with IP as our host */
+ ID0logout(PPPOTCPLINE, 1);
+ else
+ ID0logout(p->name.base, 0);
+ p->Utmp = 0;
+ }
+ newsid = tcgetpgrp(p->fd) == getpgrp();
+ close(p->fd);
+ p->fd = -1;
+ log_SetTtyCommandMode(p->dl);
+
+ throughput_stop(&p->link.stats.total);
+ throughput_log(&p->link.stats.total, LogPHASE, p->link.name);
+
+ if (p->session_owner != (pid_t)-1) {
+ log_Printf(LogPHASE, "%s: HUPing %ld\n", p->link.name,
+ (long)p->session_owner);
+ ID0kill(p->session_owner, SIGHUP);
+ p->session_owner = (pid_t)-1;
+ }
+
+ if (newsid)
+ bundle_setsid(p->dl->bundle, 0);
+
+ if (*p->name.full == '/') {
+ snprintf(fn, sizeof fn, "%s%s.if", _PATH_VARRUN, p->name.base);
+#ifndef RELEASE_CRUNCH
+ if (ID0unlink(fn) == -1)
+ log_Printf(LogALERT, "%s: Can't remove %s: %s\n",
+ p->link.name, fn, strerror(errno));
+#else
+ ID0unlink(fn);
+#endif
+ }
+ physical_Unlock(p);
+ if (p->handler && p->handler->destroy)
+ (*p->handler->destroy)(p);
+ p->handler = NULL;
+ p->name.base = p->name.full;
+ *p->name.full = '\0';
+}
+
+void
+physical_Destroy(struct physical *p)
+{
+ physical_Close(p);
+ throughput_destroy(&p->link.stats.total);
+ free(p);
+}
+
+static int
+physical_DescriptorWrite(struct fdescriptor *d, struct bundle *bundle __unused,
+ const fd_set *fdset __unused)
+{
+ struct physical *p = descriptor2physical(d);
+ int nw, result = 0;
+
+ if (p->out == NULL)
+ p->out = link_Dequeue(&p->link);
+
+ if (p->out) {
+ nw = physical_Write(p, MBUF_CTOP(p->out), p->out->m_len);
+ log_Printf(LogDEBUG, "%s: DescriptorWrite: wrote %d(%lu) to %d\n",
+ p->link.name, nw, (unsigned long)p->out->m_len, p->fd);
+ if (nw > 0) {
+ p->out->m_len -= nw;
+ p->out->m_offset += nw;
+ if (p->out->m_len == 0)
+ p->out = m_free(p->out);
+ result = 1;
+ } else if (nw < 0) {
+ if (errno == EAGAIN)
+ result = 1;
+ else if (errno != ENOBUFS) {
+ log_Printf(LogPHASE, "%s: write (fd %d, len %zd): %s\n", p->link.name,
+ p->fd, p->out->m_len, strerror(errno));
+ datalink_Down(p->dl, CLOSE_NORMAL);
+ }
+ }
+ /* else we shouldn't really have been called ! select() is broken ! */
+ }
+
+ return result;
+}
+
+int
+physical_ShowStatus(struct cmdargs const *arg)
+{
+ struct physical *p = arg->cx->physical;
+ struct cd *cd;
+ const char *dev;
+ int n, slot;
+
+ prompt_Printf(arg->prompt, "Name: %s\n", p->link.name);
+ prompt_Printf(arg->prompt, " State: ");
+ if (p->fd < 0)
+ prompt_Printf(arg->prompt, "closed\n");
+ else {
+ slot = physical_Slot(p);
+ if (p->handler && p->handler->openinfo) {
+ if (slot == -1)
+ prompt_Printf(arg->prompt, "open (%s)\n", (*p->handler->openinfo)(p));
+ else
+ prompt_Printf(arg->prompt, "open (%s, port %d)\n",
+ (*p->handler->openinfo)(p), slot);
+ } else if (slot == -1)
+ prompt_Printf(arg->prompt, "open\n");
+ else
+ prompt_Printf(arg->prompt, "open (port %d)\n", slot);
+ }
+
+ prompt_Printf(arg->prompt, " Device: %s",
+ *p->name.full ? p->name.full :
+ p->type == PHYS_DIRECT ? "unknown" : "N/A");
+ if (p->session_owner != (pid_t)-1)
+ prompt_Printf(arg->prompt, " (session owner: %ld)", (long)p->session_owner);
+
+ prompt_Printf(arg->prompt, "\n Link Type: %s\n", mode2Nam(p->type));
+ prompt_Printf(arg->prompt, " Connect Count: %d\n", p->connect_count);
+#ifdef TIOCOUTQ
+ if (p->fd >= 0 && ioctl(p->fd, TIOCOUTQ, &n) >= 0)
+ prompt_Printf(arg->prompt, " Physical outq: %d\n", n);
+#endif
+
+ prompt_Printf(arg->prompt, " Queued Packets: %lu\n",
+ (u_long)link_QueueLen(&p->link));
+ prompt_Printf(arg->prompt, " Phone Number: %s\n", arg->cx->phone.chosen);
+
+ prompt_Printf(arg->prompt, "\nDefaults:\n");
+
+ prompt_Printf(arg->prompt, " Device List: ");
+ dev = p->cfg.devlist;
+ for (n = 0; n < p->cfg.ndev; n++) {
+ if (n)
+ prompt_Printf(arg->prompt, ", ");
+ prompt_Printf(arg->prompt, "\"%s\"", dev);
+ dev += strlen(dev) + 1;
+ }
+
+ prompt_Printf(arg->prompt, "\n Characteristics: ");
+ if (physical_IsSync(arg->cx->physical))
+ prompt_Printf(arg->prompt, "sync");
+ else
+ prompt_Printf(arg->prompt, "%dbps", p->cfg.speed);
+
+ switch (p->cfg.parity & CSIZE) {
+ case CS7:
+ prompt_Printf(arg->prompt, ", cs7");
+ break;
+ case CS8:
+ prompt_Printf(arg->prompt, ", cs8");
+ break;
+ }
+ if (p->cfg.parity & PARENB) {
+ if (p->cfg.parity & PARODD)
+ prompt_Printf(arg->prompt, ", odd parity");
+ else
+ prompt_Printf(arg->prompt, ", even parity");
+ } else
+ prompt_Printf(arg->prompt, ", no parity");
+
+ prompt_Printf(arg->prompt, ", CTS/RTS %s\n", (p->cfg.rts_cts ? "on" : "off"));
+
+ prompt_Printf(arg->prompt, " CD check delay: ");
+ cd = p->handler ? &p->handler->cd : &p->cfg.cd;
+ if (cd->necessity == CD_NOTREQUIRED)
+ prompt_Printf(arg->prompt, "no cd");
+ else if (p->cfg.cd.necessity == CD_DEFAULT) {
+ prompt_Printf(arg->prompt, "device specific");
+ } else {
+ prompt_Printf(arg->prompt, "%d second%s", p->cfg.cd.delay,
+ p->cfg.cd.delay == 1 ? "" : "s");
+ if (p->cfg.cd.necessity == CD_REQUIRED)
+ prompt_Printf(arg->prompt, " (required!)");
+ }
+ prompt_Printf(arg->prompt, "\n\n");
+
+ throughput_disp(&p->link.stats.total, arg->prompt);
+
+ return 0;
+}
+
+void
+physical_DescriptorRead(struct fdescriptor *d, struct bundle *bundle,
+ const fd_set *fdset __unused)
+{
+ struct physical *p = descriptor2physical(d);
+ u_char *rbuff;
+ int n, found;
+
+ rbuff = p->input.buf + p->input.sz;
+
+ /* something to read */
+ n = physical_Read(p, rbuff, sizeof p->input.buf - p->input.sz);
+ log_Printf(LogDEBUG, "%s: DescriptorRead: read %d/%d from %d\n",
+ p->link.name, n, (int)(sizeof p->input.buf - p->input.sz), p->fd);
+ if (n <= 0) {
+ if (n < 0)
+ log_Printf(LogPHASE, "%s: read (%d): %s\n", p->link.name, p->fd,
+ strerror(errno));
+ else
+ log_Printf(LogPHASE, "%s: read (%d): Got zero bytes\n",
+ p->link.name, p->fd);
+ datalink_Down(p->dl, CLOSE_NORMAL);
+ return;
+ }
+
+ rbuff -= p->input.sz;
+ n += p->input.sz;
+
+ if (p->link.lcp.fsm.state <= ST_CLOSED) {
+ if (p->type != PHYS_DEDICATED) {
+ found = hdlc_Detect((u_char const **)&rbuff, n, physical_IsSync(p));
+ if (rbuff != p->input.buf)
+ log_WritePrompts(p->dl, "%.*s", (int)(rbuff - p->input.buf),
+ p->input.buf);
+ p->input.sz = n - (rbuff - p->input.buf);
+
+ if (found) {
+ /* LCP packet is detected. Turn ourselves into packet mode */
+ log_Printf(LogPHASE, "%s: PPP packet detected, coming up\n",
+ p->link.name);
+ log_SetTtyCommandMode(p->dl);
+ datalink_Up(p->dl, 0, 1);
+ link_PullPacket(&p->link, rbuff, p->input.sz, bundle);
+ p->input.sz = 0;
+ } else
+ bcopy(rbuff, p->input.buf, p->input.sz);
+ } else
+ /* In -dedicated mode, we just discard input until LCP is started */
+ p->input.sz = 0;
+ } else if (n > 0)
+ link_PullPacket(&p->link, rbuff, n, bundle);
+}
+
+struct physical *
+iov2physical(struct datalink *dl, struct iovec *iov, int *niov, int maxiov,
+ int fd, int *auxfd, int *nauxfd)
+{
+ struct physical *p;
+ int len, type;
+ unsigned h;
+
+ p = (struct physical *)iov[(*niov)++].iov_base;
+ p->link.name = dl->name;
+ memset(p->link.Queue, '\0', sizeof p->link.Queue);
+
+ p->desc.UpdateSet = physical_UpdateSet;
+ p->desc.IsSet = physical_IsSet;
+ p->desc.Read = physical_DescriptorRead;
+ p->desc.Write = physical_DescriptorWrite;
+ p->type = PHYS_DIRECT;
+ p->dl = dl;
+ len = strlen(_PATH_DEV);
+ p->out = NULL;
+ p->connect_count = 1;
+
+ physical_SetDevice(p, p->name.full);
+
+ p->link.lcp.fsm.bundle = dl->bundle;
+ p->link.lcp.fsm.link = &p->link;
+ memset(&p->link.lcp.fsm.FsmTimer, '\0', sizeof p->link.lcp.fsm.FsmTimer);
+ memset(&p->link.lcp.fsm.OpenTimer, '\0', sizeof p->link.lcp.fsm.OpenTimer);
+ memset(&p->link.lcp.fsm.StoppedTimer, '\0',
+ sizeof p->link.lcp.fsm.StoppedTimer);
+ p->link.lcp.fsm.parent = &dl->fsmp;
+ lcp_SetupCallbacks(&p->link.lcp);
+
+ p->link.ccp.fsm.bundle = dl->bundle;
+ p->link.ccp.fsm.link = &p->link;
+ /* Our in.state & out.state are NULL (no link-level ccp yet) */
+ memset(&p->link.ccp.fsm.FsmTimer, '\0', sizeof p->link.ccp.fsm.FsmTimer);
+ memset(&p->link.ccp.fsm.OpenTimer, '\0', sizeof p->link.ccp.fsm.OpenTimer);
+ memset(&p->link.ccp.fsm.StoppedTimer, '\0',
+ sizeof p->link.ccp.fsm.StoppedTimer);
+ p->link.ccp.fsm.parent = &dl->fsmp;
+ ccp_SetupCallbacks(&p->link.ccp);
+
+ p->hdlc.lqm.owner = &p->link.lcp;
+ p->hdlc.ReportTimer.state = TIMER_STOPPED;
+ p->hdlc.lqm.timer.state = TIMER_STOPPED;
+
+ p->fd = fd;
+ p->link.stats.total.in.SampleOctets = (long long *)iov[(*niov)++].iov_base;
+ p->link.stats.total.out.SampleOctets = (long long *)iov[(*niov)++].iov_base;
+ p->link.stats.parent = dl->bundle->ncp.mp.active ?
+ &dl->bundle->ncp.mp.link.stats.total : NULL;
+ p->link.stats.gather = 1;
+
+ type = (long)p->handler;
+ p->handler = NULL;
+ for (h = 0; h < NDEVICES && p->handler == NULL; h++)
+ p->handler = (*devices[h].iov2device)(type, p, iov, niov, maxiov,
+ auxfd, nauxfd);
+ if (p->handler == NULL) {
+ log_Printf(LogPHASE, "%s: Unknown link type\n", p->link.name);
+ free(iov[(*niov)++].iov_base);
+ physical_SetupStack(p, "unknown", PHYSICAL_NOFORCE);
+ } else
+ log_Printf(LogPHASE, "%s: Device %s, link type is %s\n",
+ p->link.name, p->name.full, p->handler->name);
+
+ if (p->hdlc.lqm.method && p->hdlc.lqm.timer.load)
+ lqr_reStart(&p->link.lcp);
+ hdlc_StartTimer(&p->hdlc);
+
+ throughput_restart(&p->link.stats.total, "physical throughput",
+ Enabled(dl->bundle, OPT_THROUGHPUT));
+
+ return p;
+}
+
+unsigned
+physical_MaxDeviceSize()
+{
+ unsigned biggest, sz, n;
+
+ biggest = sizeof(struct device);
+ for (n = 0; n < NDEVICES; n++)
+ if (devices[n].DeviceSize) {
+ sz = (*devices[n].DeviceSize)();
+ if (biggest < sz)
+ biggest = sz;
+ }
+
+ return biggest;
+}
+
+int
+physical2iov(struct physical *p, struct iovec *iov, int *niov, int maxiov,
+ int *auxfd, int *nauxfd)
+{
+ struct device *h;
+ int sz;
+
+ h = NULL;
+ if (p) {
+ hdlc_StopTimer(&p->hdlc);
+ lqr_StopTimer(p);
+ timer_Stop(&p->link.lcp.fsm.FsmTimer);
+ timer_Stop(&p->link.ccp.fsm.FsmTimer);
+ timer_Stop(&p->link.lcp.fsm.OpenTimer);
+ timer_Stop(&p->link.ccp.fsm.OpenTimer);
+ timer_Stop(&p->link.lcp.fsm.StoppedTimer);
+ timer_Stop(&p->link.ccp.fsm.StoppedTimer);
+ if (p->handler) {
+ h = p->handler;
+ p->handler = (struct device *)(long)p->handler->type;
+ }
+
+ if (Enabled(p->dl->bundle, OPT_KEEPSESSION) ||
+ tcgetpgrp(p->fd) == getpgrp())
+ p->session_owner = getpid(); /* So I'll eventually get HUP'd */
+ else
+ p->session_owner = (pid_t)-1;
+ timer_Stop(&p->link.stats.total.Timer);
+ }
+
+ if (*niov + 2 >= maxiov) {
+ log_Printf(LogERROR, "physical2iov: No room for physical + throughput"
+ " + device !\n");
+ if (p)
+ free(p);
+ return -1;
+ }
+
+ iov[*niov].iov_base = (void *)p;
+ iov[*niov].iov_len = sizeof *p;
+ (*niov)++;
+
+ iov[*niov].iov_base = p ? (void *)p->link.stats.total.in.SampleOctets : NULL;
+ iov[*niov].iov_len = SAMPLE_PERIOD * sizeof(long long);
+ (*niov)++;
+ iov[*niov].iov_base = p ? (void *)p->link.stats.total.out.SampleOctets : NULL;
+ iov[*niov].iov_len = SAMPLE_PERIOD * sizeof(long long);
+ (*niov)++;
+
+ sz = physical_MaxDeviceSize();
+ if (p) {
+ if (h && h->device2iov)
+ (*h->device2iov)(h, iov, niov, maxiov, auxfd, nauxfd);
+ else {
+ if ((iov[*niov].iov_base = malloc(sz)) == NULL) {
+ log_Printf(LogALERT, "physical2iov: Out of memory (%d bytes)\n", sz);
+ AbortProgram(EX_OSERR);
+ }
+ if (h)
+ memcpy(iov[*niov].iov_base, h, sizeof *h);
+ iov[*niov].iov_len = sz;
+ (*niov)++;
+ }
+ } else {
+ iov[*niov].iov_base = NULL;
+ iov[*niov].iov_len = sz;
+ (*niov)++;
+ }
+
+ return p ? p->fd : 0;
+}
+
+const char *
+physical_LockedDevice(struct physical *p)
+{
+ if (p->fd >= 0 && *p->name.full == '/' && p->type != PHYS_DIRECT)
+ return p->name.base;
+
+ return NULL;
+}
+
+void
+physical_ChangedPid(struct physical *p, pid_t newpid)
+{
+ if (physical_LockedDevice(p)) {
+ int res;
+
+ if ((res = ID0uu_lock_txfr(p->name.base, newpid)) != UU_LOCK_OK)
+ log_Printf(LogPHASE, "uu_lock_txfr: %s\n", uu_lockerr(res));
+ }
+}
+
+int
+physical_IsSync(struct physical *p)
+{
+ return p->cfg.speed == 0;
+}
+
+u_short
+physical_DeviceMTU(struct physical *p)
+{
+ return p->handler ? p->handler->mtu : 0;
+}
+
+const char *physical_GetDevice(struct physical *p)
+{
+ return p->name.full;
+}
+
+void
+physical_SetDeviceList(struct physical *p, int argc, const char *const *argv)
+{
+ unsigned pos;
+ int f;
+
+ p->cfg.devlist[sizeof p->cfg.devlist - 1] = '\0';
+ for (f = 0, pos = 0; f < argc && pos < sizeof p->cfg.devlist - 1; f++) {
+ if (pos)
+ p->cfg.devlist[pos++] = '\0';
+ strncpy(p->cfg.devlist + pos, argv[f], sizeof p->cfg.devlist - pos - 1);
+ pos += strlen(p->cfg.devlist + pos);
+ }
+ p->cfg.ndev = f;
+}
+
+void
+physical_SetSync(struct physical *p)
+{
+ p->cfg.speed = 0;
+}
+
+int
+physical_SetRtsCts(struct physical *p, int enable)
+{
+ p->cfg.rts_cts = enable ? 1 : 0;
+ return 1;
+}
+
+ssize_t
+physical_Read(struct physical *p, void *buf, size_t nbytes)
+{
+ ssize_t ret;
+
+ if (p->handler && p->handler->read)
+ ret = (*p->handler->read)(p, buf, nbytes);
+ else
+ ret = read(p->fd, buf, nbytes);
+
+ log_DumpBuff(LogPHYSICAL, "read", buf, ret);
+
+ return ret;
+}
+
+ssize_t
+physical_Write(struct physical *p, const void *buf, size_t nbytes)
+{
+ log_DumpBuff(LogPHYSICAL, "write", buf, nbytes);
+
+ if (p->handler && p->handler->write)
+ return (*p->handler->write)(p, buf, nbytes);
+
+ return write(p->fd, buf, nbytes);
+}
+
+int
+physical_doUpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e,
+ int *n, int force)
+{
+ struct physical *p = descriptor2physical(d);
+ int sets;
+
+ sets = 0;
+ if (p->fd >= 0) {
+ if (r) {
+ FD_SET(p->fd, r);
+ log_Printf(LogTIMER, "%s: fdset(r) %d\n", p->link.name, p->fd);
+ sets++;
+ }
+ if (e) {
+ FD_SET(p->fd, e);
+ log_Printf(LogTIMER, "%s: fdset(e) %d\n", p->link.name, p->fd);
+ sets++;
+ }
+ if (w && (force || link_QueueLen(&p->link) || p->out)) {
+ FD_SET(p->fd, w);
+ log_Printf(LogTIMER, "%s: fdset(w) %d\n", p->link.name, p->fd);
+ sets++;
+ }
+ if (sets && *n < p->fd + 1)
+ *n = p->fd + 1;
+ }
+
+ return sets;
+}
+
+int
+physical_RemoveFromSet(struct physical *p, fd_set *r, fd_set *w, fd_set *e)
+{
+ if (p->handler && p->handler->removefromset)
+ return (*p->handler->removefromset)(p, r, w, e);
+ else {
+ int sets;
+
+ sets = 0;
+ if (p->fd >= 0) {
+ if (r && FD_ISSET(p->fd, r)) {
+ FD_CLR(p->fd, r);
+ log_Printf(LogTIMER, "%s: fdunset(r) %d\n", p->link.name, p->fd);
+ sets++;
+ }
+ if (e && FD_ISSET(p->fd, e)) {
+ FD_CLR(p->fd, e);
+ log_Printf(LogTIMER, "%s: fdunset(e) %d\n", p->link.name, p->fd);
+ sets++;
+ }
+ if (w && FD_ISSET(p->fd, w)) {
+ FD_CLR(p->fd, w);
+ log_Printf(LogTIMER, "%s: fdunset(w) %d\n", p->link.name, p->fd);
+ sets++;
+ }
+ }
+
+ return sets;
+ }
+}
+
+int
+physical_IsSet(struct fdescriptor *d, const fd_set *fdset)
+{
+ struct physical *p = descriptor2physical(d);
+ return p->fd >= 0 && FD_ISSET(p->fd, fdset);
+}
+
+void
+physical_Login(struct physical *p, const char *name)
+{
+ if (p->type == PHYS_DIRECT && *p->name.base && !p->Utmp) {
+ struct utmp ut;
+ const char *connstr;
+ char *colon;
+
+ memset(&ut, 0, sizeof ut);
+ ut.ut_time = time(NULL);
+ strncpy(ut.ut_name, name, sizeof ut.ut_name);
+ if (p->handler && (p->handler->type == TCP_DEVICE ||
+ p->handler->type == UDP_DEVICE)) {
+ strncpy(ut.ut_line, PPPOTCPLINE, sizeof ut.ut_line);
+ strncpy(ut.ut_host, p->name.base, sizeof ut.ut_host);
+ colon = memchr(ut.ut_host, ':', sizeof ut.ut_host);
+ if (colon)
+ *colon = '\0';
+ } else
+ strncpy(ut.ut_line, p->name.base, sizeof ut.ut_line);
+ if ((connstr = getenv("CONNECT")))
+ /* mgetty sets this to the connection speed */
+ strncpy(ut.ut_host, connstr, sizeof ut.ut_host);
+ ID0login(&ut);
+ p->Utmp = ut.ut_time;
+ }
+}
+
+int
+physical_SetMode(struct physical *p, int mode)
+{
+ if ((p->type & (PHYS_DIRECT|PHYS_DEDICATED) ||
+ mode & (PHYS_DIRECT|PHYS_DEDICATED)) &&
+ (!(p->type & PHYS_DIRECT) || !(mode & PHYS_BACKGROUND))) {
+ /* Note: The -direct -> -background is for callback ! */
+ log_Printf(LogWARN, "%s: Cannot change mode %s to %s\n", p->link.name,
+ mode2Nam(p->type), mode2Nam(mode));
+ return 0;
+ }
+ p->type = mode;
+ return 1;
+}
+
+void
+physical_DeleteQueue(struct physical *p)
+{
+ if (p->out) {
+ m_freem(p->out);
+ p->out = NULL;
+ }
+ link_DeleteQueue(&p->link);
+}
+
+void
+physical_SetDevice(struct physical *p, const char *name)
+{
+ int len = strlen(_PATH_DEV);
+
+ if (name != p->name.full) {
+ strncpy(p->name.full, name, sizeof p->name.full - 1);
+ p->name.full[sizeof p->name.full - 1] = '\0';
+ }
+ p->name.base = *p->name.full == '!' ? p->name.full + 1 :
+ strncmp(p->name.full, _PATH_DEV, len) ?
+ p->name.full : p->name.full + len;
+}
+
+static void
+physical_Found(struct physical *p)
+{
+ FILE *lockfile;
+ char fn[PATH_MAX];
+
+ if (*p->name.full == '/') {
+ snprintf(fn, sizeof fn, "%s%s.if", _PATH_VARRUN, p->name.base);
+ lockfile = ID0fopen(fn, "w");
+ if (lockfile != NULL) {
+ fprintf(lockfile, "%s%d\n", TUN_NAME, p->dl->bundle->unit);
+ fclose(lockfile);
+ }
+#ifndef RELEASE_CRUNCH
+ else
+ log_Printf(LogALERT, "%s: Can't create %s: %s\n",
+ p->link.name, fn, strerror(errno));
+#endif
+ }
+
+ throughput_start(&p->link.stats.total, "physical throughput",
+ Enabled(p->dl->bundle, OPT_THROUGHPUT));
+ p->connect_count++;
+ p->input.sz = 0;
+
+ log_Printf(LogPHASE, "%s: Connected!\n", p->link.name);
+}
+
+int
+physical_Open(struct physical *p)
+{
+ char *dev;
+ int devno, wasfd, err;
+ unsigned h;
+
+ if (p->fd >= 0)
+ log_Printf(LogDEBUG, "%s: Open: Modem is already open!\n", p->link.name);
+ /* We're going back into "term" mode */
+ else if (p->type == PHYS_DIRECT) {
+ physical_SetDevice(p, "");
+ p->fd = STDIN_FILENO;
+ for (h = 0; h < NDEVICES && p->handler == NULL && p->fd >= 0; h++)
+ p->handler = (*devices[h].create)(p);
+ if (p->fd >= 0) {
+ if (p->handler == NULL) {
+ physical_SetupStack(p, "unknown", PHYSICAL_NOFORCE);
+ log_Printf(LogDEBUG, "%s: stdin is unidentified\n", p->link.name);
+ }
+ physical_Found(p);
+ }
+ } else {
+ dev = p->cfg.devlist;
+ devno = 0;
+ while (devno < p->cfg.ndev && p->fd < 0) {
+ physical_SetDevice(p, dev);
+ if (physical_Lock(p)) {
+ err = 0;
+
+ if (*p->name.full == '/') {
+ p->fd = ID0open(p->name.full, O_RDWR | O_NONBLOCK);
+ if (p->fd < 0)
+ err = errno;
+ }
+
+ wasfd = p->fd;
+ for (h = 0; h < NDEVICES && p->handler == NULL; h++)
+ if ((p->handler = (*devices[h].create)(p)) == NULL && wasfd != p->fd)
+ break;
+
+ if (p->fd < 0) {
+ if (h == NDEVICES) {
+ if (err)
+ log_Printf(LogWARN, "%s: %s: %s\n", p->link.name, p->name.full,
+ strerror(errno));
+ else
+ log_Printf(LogWARN, "%s: Device (%s) must begin with a '/',"
+ " a '!' or contain at least one ':'\n", p->link.name,
+ p->name.full);
+ }
+ physical_Unlock(p);
+ } else
+ physical_Found(p);
+ }
+ dev += strlen(dev) + 1;
+ devno++;
+ }
+ }
+
+ return p->fd;
+}
+
+void
+physical_SetupStack(struct physical *p, const char *who, int how)
+{
+ link_EmptyStack(&p->link);
+ if (how == PHYSICAL_FORCE_SYNC || how == PHYSICAL_FORCE_SYNCNOACF ||
+ (how == PHYSICAL_NOFORCE && physical_IsSync(p)))
+ link_Stack(&p->link, &synclayer);
+ else {
+ link_Stack(&p->link, &asynclayer);
+ link_Stack(&p->link, &hdlclayer);
+ }
+ if (how != PHYSICAL_FORCE_SYNCNOACF)
+ link_Stack(&p->link, &acflayer);
+ link_Stack(&p->link, &protolayer);
+ link_Stack(&p->link, &lqrlayer);
+ link_Stack(&p->link, &ccplayer);
+ link_Stack(&p->link, &vjlayer);
+ link_Stack(&p->link, &tcpmsslayer);
+#ifndef NONAT
+ link_Stack(&p->link, &natlayer);
+#endif
+ if (how == PHYSICAL_FORCE_ASYNC && physical_IsSync(p)) {
+ log_Printf(LogWARN, "Sync device setting ignored for ``%s'' device\n", who);
+ p->cfg.speed = MODEM_SPEED;
+ } else if (how == PHYSICAL_FORCE_SYNC && !physical_IsSync(p)) {
+ log_Printf(LogWARN, "Async device setting ignored for ``%s'' device\n",
+ who);
+ physical_SetSync(p);
+ }
+}
+
+void
+physical_StopDeviceTimer(struct physical *p)
+{
+ if (p->handler && p->handler->stoptimer)
+ (*p->handler->stoptimer)(p);
+}
+
+int
+physical_AwaitCarrier(struct physical *p)
+{
+ if (p->handler && p->handler->awaitcarrier)
+ return (*p->handler->awaitcarrier)(p);
+
+ return CARRIER_OK;
+}
+
+
+void
+physical_SetAsyncParams(struct physical *p, u_int32_t mymap, u_int32_t hismap)
+{
+ if (p->handler && p->handler->setasyncparams)
+ return (*p->handler->setasyncparams)(p, mymap, hismap);
+
+ async_SetLinkParams(&p->async, mymap, hismap);
+}
+
+int
+physical_Slot(struct physical *p)
+{
+ if (p->handler && p->handler->slot)
+ return (*p->handler->slot)(p);
+
+ return -1;
+}
+
+int
+physical_SetPPPoEnonstandard(struct physical *p, int enable)
+{
+ p->cfg.nonstandard_pppoe = enable ? 1 : 0;
+ p->cfg.pppoe_configured = 1;
+ return 1;
+}
diff --git a/usr.sbin/ppp/physical.h b/usr.sbin/ppp/physical.h
new file mode 100644
index 0000000..ab0cdba
--- /dev/null
+++ b/usr.sbin/ppp/physical.h
@@ -0,0 +1,176 @@
+/*
+ * Written by Eivind Eklund <eivind@yes.no>
+ * for Yes Interactive
+ *
+ * Copyright (C) 1998, Yes Interactive. All rights reserved.
+ *
+ * Redistribution and use in any form is permitted. Redistribution in
+ * source form should include the above copyright and this set of
+ * conditions, because large sections american law seems to have been
+ * created by a bunch of jerks on drugs that are now illegal, forcing
+ * me to include this copyright-stuff instead of placing this in the
+ * public domain. The name of of 'Yes Interactive' or 'Eivind Eklund'
+ * 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+struct datalink;
+struct bundle;
+struct iovec;
+struct physical;
+struct bundle;
+struct ccp;
+struct cmdargs;
+
+/* Device types (don't use zero, it'll be confused with NULL in physical2iov */
+#define I4B_DEVICE 1
+#define TTY_DEVICE 2
+#define TCP_DEVICE 3
+#define UDP_DEVICE 4
+#define ETHER_DEVICE 5
+#define EXEC_DEVICE 6
+#define ATM_DEVICE 7
+#define NG_DEVICE 8
+
+/* Returns from awaitcarrier() */
+#define CARRIER_PENDING 1
+#define CARRIER_OK 2
+#define CARRIER_LOST 3
+
+/* A cd ``necessity'' value */
+#define CD_VARIABLE 0
+#define CD_REQUIRED 1
+#define CD_NOTREQUIRED 2
+#define CD_DEFAULT 3
+
+struct cd {
+ unsigned necessity : 2; /* A CD_ value */
+ int delay; /* Wait this many seconds after login script */
+};
+
+struct device {
+ int type;
+ const char *name;
+ u_short mtu;
+ struct cd cd;
+
+ int (*awaitcarrier)(struct physical *);
+ int (*removefromset)(struct physical *, fd_set *, fd_set *, fd_set *);
+ int (*raw)(struct physical *);
+ void (*offline)(struct physical *);
+ void (*cooked)(struct physical *);
+ void (*setasyncparams)(struct physical *, u_int32_t, u_int32_t);
+ void (*stoptimer)(struct physical *);
+ void (*destroy)(struct physical *);
+ ssize_t (*read)(struct physical *, void *, size_t);
+ ssize_t (*write)(struct physical *, const void *, size_t);
+ void (*device2iov)(struct device *, struct iovec *, int *, int, int *, int *);
+ unsigned (*speed)(struct physical *);
+ const char *(*openinfo)(struct physical *);
+ int (*slot)(struct physical *);
+};
+
+struct physical {
+ struct link link;
+ struct fdescriptor desc;
+ int type; /* What sort of PHYS_* link are we ? */
+ struct async async; /* Our async state */
+ struct hdlc hdlc; /* Our hdlc state */
+ int fd; /* File descriptor for this device */
+ struct mbuf *out; /* mbuf that suffered a short write */
+ int connect_count;
+ struct datalink *dl; /* my owner */
+
+ struct {
+ u_char buf[MAX_MRU]; /* Our input data buffer */
+ size_t sz;
+ } input;
+
+ struct {
+ char full[DEVICE_LEN]; /* Our current device name */
+ char *base;
+ } name;
+
+ time_t Utmp; /* Are we in utmp ? */
+ pid_t session_owner; /* HUP this when closing the link */
+
+ struct device *handler; /* device specific handler */
+
+ struct {
+ unsigned rts_cts : 1; /* Is rts/cts enabled ? */
+ unsigned nonstandard_pppoe : 1; /* Is PPPoE mode nonstandard */
+ unsigned pppoe_configured : 1; /* temporary hack */
+ unsigned parity; /* What parity is enabled? (tty flags) */
+ unsigned speed; /* tty speed */
+
+ char devlist[LINE_LEN]; /* NUL separated list of devices */
+ int ndev; /* number of devices in list */
+ struct cd cd;
+ } cfg;
+};
+
+#define field2phys(fp, name) \
+ ((struct physical *)((char *)fp - (uintptr_t)(&((struct physical *)0)->name)))
+
+#define link2physical(l) \
+ ((l)->type == PHYSICAL_LINK ? field2phys(l, link) : NULL)
+
+#define descriptor2physical(d) \
+ ((d)->type == PHYSICAL_DESCRIPTOR ? field2phys(d, desc) : NULL)
+
+#define PHYSICAL_NOFORCE 1
+#define PHYSICAL_FORCE_ASYNC 2
+#define PHYSICAL_FORCE_SYNC 3
+#define PHYSICAL_FORCE_SYNCNOACF 4
+
+extern struct physical *physical_Create(struct datalink *, int);
+extern int physical_Open(struct physical *);
+extern int physical_Raw(struct physical *);
+extern unsigned physical_GetSpeed(struct physical *);
+extern int physical_SetSpeed(struct physical *, unsigned);
+extern int physical_SetParity(struct physical *, const char *);
+extern int physical_SetRtsCts(struct physical *, int);
+extern void physical_SetSync(struct physical *);
+extern int physical_ShowStatus(struct cmdargs const *);
+extern void physical_Offline(struct physical *);
+extern void physical_Close(struct physical *);
+extern void physical_Destroy(struct physical *);
+extern struct physical *iov2physical(struct datalink *, struct iovec *, int *,
+ int, int, int *, int *);
+extern int physical2iov(struct physical *, struct iovec *, int *, int, int *,
+ int *);
+extern const char *physical_LockedDevice(struct physical *);
+extern void physical_ChangedPid(struct physical *, pid_t);
+
+extern int physical_IsSync(struct physical *);
+extern u_short physical_DeviceMTU(struct physical *);
+extern const char *physical_GetDevice(struct physical *);
+extern void physical_SetDeviceList(struct physical *, int, const char *const *);
+extern void physical_SetDevice(struct physical *, const char *);
+
+extern ssize_t physical_Read(struct physical *, void *, size_t);
+extern ssize_t physical_Write(struct physical *, const void *, size_t);
+extern int physical_doUpdateSet(struct fdescriptor *, fd_set *, fd_set *,
+ fd_set *, int *, int);
+extern int physical_IsSet(struct fdescriptor *, const fd_set *);
+extern void physical_DescriptorRead(struct fdescriptor *, struct bundle *,
+ const fd_set *);
+extern void physical_Login(struct physical *, const char *);
+extern int physical_RemoveFromSet(struct physical *, fd_set *, fd_set *,
+ fd_set *);
+extern int physical_SetMode(struct physical *, int);
+extern void physical_DeleteQueue(struct physical *);
+extern void physical_SetupStack(struct physical *, const char *, int);
+extern void physical_StopDeviceTimer(struct physical *);
+extern unsigned physical_MaxDeviceSize(void);
+extern int physical_AwaitCarrier(struct physical *);
+extern void physical_SetDescriptor(struct physical *);
+extern void physical_SetAsyncParams(struct physical *, u_int32_t, u_int32_t);
+extern int physical_Slot(struct physical *);
+extern int physical_SetPPPoEnonstandard(struct physical *, int);
diff --git a/usr.sbin/ppp/ppp.8.m4 b/usr.sbin/ppp/ppp.8.m4
new file mode 100644
index 0000000..b42c13c
--- /dev/null
+++ b/usr.sbin/ppp/ppp.8.m4
@@ -0,0 +1,6105 @@
+changequote({,})dnl
+changecom(,)dnl
+.\"
+.\" Copyright (c) 2001 Brian Somers <brian@Awfulhak.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 24, 2007
+.Dt PPP 8
+.Os
+.Sh NAME
+.Nm ppp
+.Nd Point to Point Protocol (a.k.a. user-ppp)
+.Sh SYNOPSIS
+.Nm
+.Op Fl Va mode
+.Op Fl nat
+.Op Fl quiet
+.Op Fl unit Ns Ar N
+.Op Ar system ...
+.Sh DESCRIPTION
+This is a user process
+.Em PPP
+software package.
+Normally,
+.Em PPP
+is implemented as a part of the kernel (e.g., as managed by
+.Xr pppd 8 )
+and it is thus somewhat hard to debug and/or modify its behaviour.
+However, in this implementation
+.Em PPP
+is done as a user process with the help of the
+tunnel device driver (tun).
+.Pp
+The
+.Fl nat
+flag does the equivalent of a
+.Dq nat enable yes ,
+enabling
+.Nm Ns No 's
+network address translation features.
+This allows
+.Nm
+to act as a NAT or masquerading engine for all machines on an internal
+LAN.
+ifdef({LOCALNAT},{},{Refer to
+.Xr libalias 3
+for details on the technical side of the NAT engine.
+})dnl
+Refer to the
+.Sx NETWORK ADDRESS TRANSLATION (PACKET ALIASING)
+section of this manual page for details on how to configure NAT in
+.Nm .
+.Pp
+The
+.Fl quiet
+flag tells
+.Nm
+to be silent at startup rather than displaying the mode and interface
+to standard output.
+.Pp
+The
+.Fl unit
+flag tells
+.Nm
+to only attempt to open
+.Pa /dev/tun Ns Ar N .
+Normally,
+.Nm
+will start with a value of 0 for
+.Ar N ,
+and keep trying to open a tunnel device by incrementing the value of
+.Ar N
+by one each time until it succeeds.
+If it fails three times in a row
+because the device file is missing, it gives up.
+.Pp
+The following
+.Va mode Ns No s
+are understood by
+.Nm :
+.Bl -tag -width XXX -offset XXX
+.It Fl auto
+.Nm
+opens the tun interface, configures it then goes into the background.
+The link is not brought up until outgoing data is detected on the tun
+interface at which point
+.Nm
+attempts to bring up the link.
+Packets received (including the first one) while
+.Nm
+is trying to bring the link up will remain queued for a default of
+2 minutes.
+See the
+.Dq set choked
+command below.
+.Pp
+In
+.Fl auto
+mode, at least one
+.Dq system
+must be given on the command line (see below) and a
+.Dq set ifaddr
+must be done in the system profile that specifies a peer IP address to
+use when configuring the interface.
+Something like
+.Dq 10.0.0.1/0
+is usually appropriate.
+See the
+.Dq pmdemand
+system in
+.Pa /usr/share/examples/ppp/ppp.conf.sample
+for an example.
+.It Fl background
+Here,
+.Nm
+attempts to establish a connection with the peer immediately.
+If it succeeds,
+.Nm
+goes into the background and the parent process returns an exit code
+of 0.
+If it fails,
+.Nm
+exits with a non-zero result.
+.It Fl foreground
+In foreground mode,
+.Nm
+attempts to establish a connection with the peer immediately, but never
+becomes a daemon.
+The link is created in background mode.
+This is useful if you wish to control
+.Nm Ns No 's
+invocation from another process.
+.It Fl direct
+This is used for communicating over an already established connection,
+usually when receiving incoming connections accepted by
+.Xr getty 8 .
+.Nm
+ignores the
+.Dq set device
+line and uses descriptor 0 as the link.
+.Nm
+will also ignore any configured chat scripts unless the
+.Dq force-scripts
+option has been enabled.
+.Pp
+If callback is configured,
+.Nm
+will use the
+.Dq set device
+information when dialing back.
+.It Fl dedicated
+This option is designed for machines connected with a dedicated
+wire.
+.Nm
+will always keep the device open and will ignore any configured
+chat scripts unless the
+.Dq force-scripts
+option has been enabled.
+.It Fl ddial
+This mode is equivalent to
+.Fl auto
+mode except that
+.Nm
+will bring the link back up any time it is dropped for any reason.
+.It Fl interactive
+This is a no-op, and gives the same behaviour as if none of the above
+modes have been specified.
+.Nm
+loads any sections specified on the command line then provides an
+interactive prompt.
+.El
+.Pp
+One or more configuration entries or systems
+(as specified in
+.Pa /etc/ppp/ppp.conf )
+may also be specified on the command line.
+.Nm
+will read the
+.Dq default
+system from
+.Pa /etc/ppp/ppp.conf
+at startup, followed by each of the systems specified on the command line.
+.Sh Major Features
+.Bl -diag
+.It Provides an interactive user interface.
+Using its command mode, the user can
+easily enter commands to establish the connection with the remote end, check
+the status of connection and close the connection.
+All functions can also be optionally password protected for security.
+.It Supports both manual and automatic dialing.
+Interactive mode has a
+.Dq term
+command which enables you to talk to the device directly.
+When you are connected to the remote peer and it starts to talk
+.Em PPP ,
+.Nm
+detects it and switches to packet mode automatically.
+Once you have
+determined the proper sequence for connecting with the remote host, you
+can write a chat script to {define} the necessary dialing and login
+procedure for later convenience.
+.It Supports on-demand dialup capability.
+By using
+.Fl auto
+mode,
+.Nm
+will act as a daemon and wait for a packet to be sent over the
+.Em PPP
+link.
+When this happens, the daemon automatically dials and establishes the
+connection.
+In almost the same manner
+.Fl ddial
+mode (direct-dial mode) also automatically dials and establishes the
+connection.
+However, it differs in that it will dial the remote site
+any time it detects the link is down, even if there are no packets to be
+sent.
+This mode is useful for full-time connections where we worry less
+about line charges and more about being connected full time.
+A third
+.Fl dedicated
+mode is also available.
+This mode is targeted at a dedicated link between two machines.
+.Nm
+will never voluntarily quit from dedicated mode - you must send it the
+.Dq quit all
+command via its diagnostic socket.
+A
+.Dv SIGHUP
+will force an LCP renegotiation, and a
+.Dv SIGTERM
+will force it to exit.
+.It Supports client callback.
+.Nm
+can use either the standard LCP callback protocol or the Microsoft
+CallBack Control Protocol (ftp://ftp.microsoft.com/developr/rfc/cbcp.txt).
+.It Supports NAT or packet aliasing.
+Packet aliasing (a.k.a.\& IP masquerading) allows computers on a
+private, unregistered network to access the Internet.
+The
+.Em PPP
+host acts as a masquerading gateway.
+IP addresses as well as TCP and
+UDP port numbers are NAT'd for outgoing packets and de-NAT'd for
+returning packets.
+.It Supports background PPP connections.
+In background mode, if
+.Nm
+successfully establishes the connection, it will become a daemon.
+Otherwise, it will exit with an error.
+This allows the setup of
+scripts that wish to execute certain commands only if the connection
+is successfully established.
+.It Supports server-side PPP connections.
+In direct mode,
+.Nm
+acts as server which accepts incoming
+.Em PPP
+connections on stdin/stdout.
+.It "Supports PAP and CHAP (rfc 1994, 2433 and 2759) authentication.
+With PAP or CHAP, it is possible to skip the Unix style
+.Xr login 1
+procedure, and use the
+.Em PPP
+protocol for authentication instead.
+If the peer requests Microsoft CHAP authentication and
+.Nm
+is compiled with DES support, an appropriate MD4/DES response will be
+made.
+.It Supports RADIUS (rfc 2138 & 2548) authentication.
+An extension to PAP and CHAP,
+.Em \&R Ns No emote
+.Em \&A Ns No ccess
+.Em \&D Ns No ial
+.Em \&I Ns No n
+.Em \&U Ns No ser
+.Em \&S Ns No ervice
+allows authentication information to be stored in a central or
+distributed database along with various per-user framed connection
+characteristics.
+ifdef({LOCALRAD},{},{If
+.Xr libradius 3
+is available at compile time,
+.Nm
+will use it to make
+.Em RADIUS
+requests when configured to do so.
+})dnl
+.It Supports Proxy Arp.
+.Nm
+can be configured to make one or more proxy arp entries on behalf of
+the peer.
+This allows routing from the peer to the LAN without
+configuring each machine on that LAN.
+.It Supports packet filtering.
+User can {define} four kinds of filters: the
+.Em in
+filter for incoming packets, the
+.Em out
+filter for outgoing packets, the
+.Em dial
+filter to {define} a dialing trigger packet and the
+.Em alive
+filter for keeping a connection alive with the trigger packet.
+.It Tunnel driver supports bpf.
+The user can use
+.Xr tcpdump 1
+to check the packet flow over the
+.Em PPP
+link.
+.It Supports PPP over TCP and PPP over UDP.
+If a device name is specified as
+.Em host Ns No : Ns Em port Ns
+.Xo
+.Op / Ns tcp|udp ,
+.Xc
+.Nm
+will open a TCP or UDP connection for transporting data rather than using a
+conventional serial device.
+UDP connections force
+.Nm
+into synchronous mode.
+.It Supports PPP over Ethernet (rfc 2516).
+If
+.Nm
+is given a device specification of the format
+.No PPPoE: Ns Ar iface Ns Xo
+.Op \&: Ns Ar provider Ns
+.Xc
+and if
+.Xr netgraph 4
+is available,
+.Nm
+will attempt talk
+.Em PPP
+over Ethernet to
+.Ar provider
+using the
+.Ar iface
+network interface.
+.Pp
+On systems that do not support
+.Xr netgraph 4 ,
+an external program such as
+.Xr pppoed 8
+may be used.
+.It "Supports IETF draft Predictor-1 (rfc 1978) and DEFLATE (rfc 1979) compression."
+.Nm
+supports not only VJ-compression but also Predictor-1 and DEFLATE compression.
+Normally, a modem has built-in compression (e.g., v42.bis) and the system
+may receive higher data rates from it as a result of such compression.
+While this is generally a good thing in most other situations, this
+higher speed data imposes a penalty on the system by increasing the
+number of serial interrupts the system has to process in talking to the
+modem and also increases latency.
+Unlike VJ-compression, Predictor-1 and DEFLATE compression pre-compresses
+.Em all
+network traffic flowing through the link, thus reducing overheads to a
+minimum.
+.It Supports Microsoft's IPCP extensions (rfc 1877).
+Name Server Addresses and NetBIOS Name Server Addresses can be negotiated
+with clients using the Microsoft
+.Em PPP
+stack (i.e., Win95, WinNT)
+.It Supports Multi-link PPP (rfc 1990)
+It is possible to configure
+.Nm
+to open more than one physical connection to the peer, combining the
+bandwidth of all links for better throughput.
+.It Supports MPPE (draft-ietf-pppext-mppe)
+MPPE is Microsoft Point to Point Encryption scheme.
+It is possible to configure
+.Nm
+to participate in Microsoft's Windows VPN.
+For now,
+.Nm
+can only get encryption keys from CHAP 81 authentication.
+.Nm
+must be compiled with DES for MPPE to operate.
+.It Supports IPV6CP (rfc 2023).
+An IPv6 connection can be made in addition to or instead of the normal
+IPv4 connection.
+.El
+.Sh PERMISSIONS
+.Nm
+is installed as user
+.Dv root
+and group
+.Dv network ,
+with permissions
+.Dv 04554 .
+By default,
+.Nm
+will not run if the invoking user id is not zero.
+This may be overridden by using the
+.Dq allow users
+command in
+.Pa /etc/ppp/ppp.conf .
+When running as a normal user,
+.Nm
+switches to user id 0 in order to alter the system routing table, set up
+system lock files and read the ppp configuration files.
+All external commands (executed via the "shell" or "!bg" commands) are executed
+as the user id that invoked
+.Nm .
+Refer to the
+.Sq ID0
+logging facility if you are interested in what exactly is done as user id
+zero.
+.Sh GETTING STARTED
+When you first run
+.Nm
+you may need to deal with some initial configuration details.
+.Bl -bullet
+.It
+Make sure that your system has a group named
+.Dq network
+in the
+.Pa /etc/group
+file and that the group contains the names of all users expected to use
+.Nm .
+Refer to the
+.Xr group 5
+manual page for details.
+Each of these users must also be given access using the
+.Dq allow users
+command in
+.Pa /etc/ppp/ppp.conf .
+.It
+Create a log file.
+.Nm
+uses
+.Xr syslog 3
+to log information.
+A common log file name is
+.Pa /var/log/ppp.log .
+To make output go to this file, put the following lines in the
+.Pa /etc/syslog.conf
+file:
+.Bd -literal -offset indent
+!ppp
+*.*<TAB>/var/log/ppp.log
+.Ed
+.Pp
+It is possible to have more than one
+.Em PPP
+log file by creating a link to the
+.Nm
+executable:
+.Pp
+.Dl # cd /usr/sbin
+.Dl # ln ppp ppp0
+.Pp
+and using
+.Bd -literal -offset indent
+!ppp0
+*.*<TAB>/var/log/ppp0.log
+.Ed
+.Pp
+in
+.Pa /etc/syslog.conf .
+Do not forget to send a
+.Dv HUP
+signal to
+.Xr syslogd 8
+after altering
+.Pa /etc/syslog.conf .
+.It
+Although not strictly relevant to
+.Nm Ns No 's
+operation, you should configure your resolver so that it works correctly.
+This can be done by configuring a local DNS
+(using
+.Xr named 8 )
+or by adding the correct
+.Sq nameserver
+lines to the file
+.Pa /etc/resolv.conf .
+Refer to the
+.Xr resolv.conf 5
+manual page for details.
+.Pp
+Alternatively, if the peer supports it,
+.Nm
+can be configured to ask the peer for the nameserver address(es) and to
+update
+.Pa /etc/resolv.conf
+automatically.
+Refer to the
+.Dq enable dns
+and
+.Dq resolv
+commands below for details.
+.El
+.Sh MANUAL DIALING
+In the following examples, we assume that your machine name is
+.Dv awfulhak .
+when you invoke
+.Nm
+(see
+.Sx PERMISSIONS
+above) with no arguments, you are presented with a prompt:
+.Bd -literal -offset indent
+ppp ON awfulhak>
+.Ed
+.Pp
+The
+.Sq ON
+part of your prompt should always be in upper case.
+If it is in lower case, it means that you must supply a password using the
+.Dq passwd
+command.
+This only ever happens if you connect to a running version of
+.Nm
+and have not authenticated yourself using the correct password.
+.Pp
+You can start by specifying the device name and speed:
+.Bd -literal -offset indent
+ppp ON awfulhak> set device /dev/cuad0
+ppp ON awfulhak> set speed 38400
+.Ed
+.Pp
+Normally, hardware flow control (CTS/RTS) is used.
+However, under
+certain circumstances (as may happen when you are connected directly
+to certain PPP-capable terminal servers), this may result in
+.Nm
+hanging as soon as it tries to write data to your communications link
+as it is waiting for the CTS (clear to send) signal - which will never
+come.
+Thus, if you have a direct line and cannot seem to make a
+connection, try turning CTS/RTS off with
+.Dq set ctsrts off .
+If you need to do this, check the
+.Dq set accmap
+description below too - you will probably need to
+.Dq set accmap 000a0000 .
+.Pp
+Usually, parity is set to
+.Dq none ,
+and this is
+.Nm Ns No 's
+default.
+Parity is a rather archaic error checking mechanism that is no
+longer used because modern modems do their own error checking, and most
+link-layer protocols (that is what
+.Nm
+is) use much more reliable checking mechanisms.
+Parity has a relatively
+huge overhead (a 12.5% increase in traffic) and as a result, it is always
+disabled
+(set to
+.Dq none )
+when
+.Dv PPP
+is opened.
+However, some ISPs (Internet Service Providers) may use
+specific parity settings at connection time (before
+.Dv PPP
+is opened).
+Notably, Compuserve insist on even parity when logging in:
+.Bd -literal -offset indent
+ppp ON awfulhak> set parity even
+.Ed
+.Pp
+You can now see what your current device settings look like:
+.Bd -literal -offset indent
+ppp ON awfulhak> show physical
+Name: deflink
+ State: closed
+ Device: N/A
+ Link Type: interactive
+ Connect Count: 0
+ Queued Packets: 0
+ Phone Number: N/A
+
+Defaults:
+ Device List: /dev/cuad0
+ Characteristics: 38400bps, cs8, even parity, CTS/RTS on
+
+Connect time: 0 secs
+0 octets in, 0 octets out
+Overall 0 bytes/sec
+ppp ON awfulhak>
+.Ed
+.Pp
+The term command can now be used to talk directly to the device:
+.Bd -literal -offset indent
+ppp ON awfulhak> term
+at
+OK
+atdt123456
+CONNECT
+login: myispusername
+Password: myisppassword
+Protocol: ppp
+.Ed
+.Pp
+When the peer starts to talk in
+.Em PPP ,
+.Nm
+detects this automatically and returns to command mode.
+.Bd -literal -offset indent
+ppp ON awfulhak> # No link has been established
+Ppp ON awfulhak> # We've connected & finished LCP
+PPp ON awfulhak> # We've authenticated
+PPP ON awfulhak> # We've agreed IP numbers
+.Ed
+.Pp
+If it does not, it is probable that the peer is waiting for your end to
+start negotiating.
+To force
+.Nm
+to start sending
+.Em PPP
+configuration packets to the peer, use the
+.Dq ~p
+command to drop out of terminal mode and enter packet mode.
+.Pp
+If you never even receive a login prompt, it is quite likely that the
+peer wants to use PAP or CHAP authentication instead of using Unix-style
+login/password authentication.
+To set things up properly, drop back to
+the prompt and set your authentication name and key, then reconnect:
+.Bd -literal -offset indent
+~.
+ppp ON awfulhak> set authname myispusername
+ppp ON awfulhak> set authkey myisppassword
+ppp ON awfulhak> term
+at
+OK
+atdt123456
+CONNECT
+.Ed
+.Pp
+You may need to tell ppp to initiate negotiations with the peer here too:
+.Bd -literal -offset indent
+~p
+ppp ON awfulhak> # No link has been established
+Ppp ON awfulhak> # We've connected & finished LCP
+PPp ON awfulhak> # We've authenticated
+PPP ON awfulhak> # We've agreed IP numbers
+.Ed
+.Pp
+You are now connected!
+Note that
+.Sq PPP
+in the prompt has changed to capital letters to indicate that you have
+a peer connection.
+If only some of the three Ps go uppercase, wait until
+either everything is uppercase or lowercase.
+If they revert to lowercase, it means that
+.Nm
+could not successfully negotiate with the peer.
+A good first step for troubleshooting at this point would be to
+.Bd -literal -offset indent
+ppp ON awfulhak> set log local phase lcp ipcp
+.Ed
+.Pp
+and try again.
+Refer to the
+.Dq set log
+command description below for further details.
+If things fail at this point,
+it is quite important that you turn logging on and try again.
+It is also
+important that you note any prompt changes and report them to anyone trying
+to help you.
+.Pp
+When the link is established, the show command can be used to see how
+things are going:
+.Bd -literal -offset indent
+PPP ON awfulhak> show physical
+* Modem related information is shown here *
+PPP ON awfulhak> show ccp
+* CCP (compression) related information is shown here *
+PPP ON awfulhak> show lcp
+* LCP (line control) related information is shown here *
+PPP ON awfulhak> show ipcp
+* IPCP (IP) related information is shown here *
+PPP ON awfulhak> show ipv6cp
+* IPV6CP (IPv6) related information is shown here *
+PPP ON awfulhak> show link
+* Link (high level) related information is shown here *
+PPP ON awfulhak> show bundle
+* Logical (high level) connection related information is shown here *
+.Ed
+.Pp
+At this point, your machine has a host route to the peer.
+This means
+that you can only make a connection with the host on the other side
+of the link.
+If you want to add a default route entry (telling your
+machine to send all packets without another routing entry to the other
+side of the
+.Em PPP
+link), enter the following command:
+.Bd -literal -offset indent
+PPP ON awfulhak> add default HISADDR
+.Ed
+.Pp
+The string
+.Sq HISADDR
+represents the IP address of the connected peer.
+If the
+.Dq add
+command fails due to an existing route, you can overwrite the existing
+route using:
+.Bd -literal -offset indent
+PPP ON awfulhak> add! default HISADDR
+.Ed
+.Pp
+This command can also be executed before actually making the connection.
+If a new IP address is negotiated at connection time,
+.Nm
+will update your default route accordingly.
+.Pp
+You can now use your network applications (ping, telnet, ftp, etc.)
+in other windows or terminals on your machine.
+If you wish to reuse the current terminal, you can put
+.Nm
+into the background using your standard shell suspend and background
+commands (usually
+.Dq ^Z
+followed by
+.Dq bg ) .
+.Pp
+Refer to the
+.Sx PPP COMMAND LIST
+section for details on all available commands.
+.Sh AUTOMATIC DIALING
+To use automatic dialing, you must prepare some Dial and Login chat scripts.
+See the example definitions in
+.Pa /usr/share/examples/ppp/ppp.conf.sample
+(the format of
+.Pa /etc/ppp/ppp.conf
+is pretty simple).
+Each line contains one comment, inclusion, label or command:
+.Bl -bullet
+.It
+A line starting with a
+.Pq Dq #
+character is treated as a comment line.
+Leading whitespace are ignored when identifying comment lines.
+.It
+An inclusion is a line beginning with the word
+.Sq {!include} .
+It must have one argument - the file to {include}.
+You may wish to
+.Dq {!include} ~/.ppp.conf
+for compatibility with older versions of
+.Nm .
+.It
+A label name starts in the first column and is followed by
+a colon
+.Pq Dq \&: .
+.It
+A command line must contain a space or tab in the first column.
+.It
+A string starting with the
+.Dq $
+character is substituted with the value of the environment variable by
+the same name.
+Likewise, a string starting with the
+.Dq ~
+character is substituted with the full path to the home directory of
+the user account by the same name, and the
+.Dq ~
+character by itself is substituted with the full path to the home directory
+of the current user.
+If you want to include a literal
+.Dq $
+or
+.Dq ~
+character in a command or argument, enclose them in double quotes, e.g.,
+.Bd -literal -offset indent
+set password "pa$ss~word"
+.Ed
+.El
+.Pp
+The
+.Pa /etc/ppp/ppp.conf
+file should consist of at least a
+.Dq default
+section.
+This section is always executed.
+It should also contain
+one or more sections, named according to their purpose, for example,
+.Dq MyISP
+would represent your ISP, and
+.Dq ppp-in
+would represent an incoming
+.Nm
+configuration.
+You can now specify the destination label name when you invoke
+.Nm .
+Commands associated with the
+.Dq default
+label are executed, followed by those associated with the destination
+label provided.
+When
+.Nm
+is started with no arguments, the
+.Dq default
+section is still executed.
+The load command can be used to manually load a section from the
+.Pa /etc/ppp/ppp.conf
+file:
+.Bd -literal -offset indent
+ppp ON awfulhak> load MyISP
+.Ed
+.Pp
+Note, no action is taken by
+.Nm
+after a section is loaded, whether it is the result of passing a label on
+the command line or using the
+.Dq load
+command.
+Only the commands specified for that label in the configuration
+file are executed.
+However, when invoking
+.Nm
+with the
+.Fl background ,
+.Fl ddial ,
+or
+.Fl dedicated
+switches, the link mode tells
+.Nm
+to establish a connection.
+Refer to the
+.Dq set mode
+command below for further details.
+.Pp
+Once the connection is made, the
+.Sq ppp
+portion of the prompt will change to
+.Sq PPP :
+.Bd -literal -offset indent
+# ppp MyISP
+\&...
+ppp ON awfulhak> dial
+Ppp ON awfulhak>
+PPp ON awfulhak>
+PPP ON awfulhak>
+.Ed
+.Pp
+The Ppp prompt indicates that
+.Nm
+has entered the authentication phase.
+The PPp prompt indicates that
+.Nm
+has entered the network phase.
+The PPP prompt indicates that
+.Nm
+has successfully negotiated a network layer protocol and is in
+a usable state.
+.Pp
+If the
+.Pa /etc/ppp/ppp.linkup
+file is available, its contents are executed
+when the
+.Em PPP
+connection is established.
+See the provided
+.Dq pmdemand
+example in
+.Pa /usr/share/examples/ppp/ppp.conf.sample
+which runs a script in the background after the connection is established
+(refer to the
+.Dq shell
+and
+.Dq bg
+commands below for a description of possible substitution strings).
+Similarly, when a connection is closed, the contents of the
+.Pa /etc/ppp/ppp.linkdown
+file are executed.
+Both of these files have the same format as
+.Pa /etc/ppp/ppp.conf .
+.Pp
+In previous versions of
+.Nm ,
+it was necessary to re-add routes such as the default route in the
+.Pa ppp.linkup
+file.
+.Nm
+supports
+.Sq sticky routes ,
+where all routes that contain the
+.Dv HISADDR ,
+.Dv MYADDR ,
+.Dv HISADDR6
+or
+.Dv MYADDR6
+literals will automatically be updated when the values of these variables
+change.
+.Sh BACKGROUND DIALING
+If you want to establish a connection using
+.Nm
+non-interactively (such as from a
+.Xr crontab 5
+entry or an
+.Xr at 1
+job) you should use the
+.Fl background
+option.
+When
+.Fl background
+is specified,
+.Nm
+attempts to establish the connection immediately.
+If multiple phone
+numbers are specified, each phone number will be tried once.
+If the attempt fails,
+.Nm
+exits immediately with a non-zero exit code.
+If it succeeds, then
+.Nm
+becomes a daemon, and returns an exit status of zero to its caller.
+The daemon exits automatically if the connection is dropped by the
+remote system, or it receives a
+.Dv TERM
+signal.
+.Sh DIAL ON DEMAND
+Demand dialing is enabled with the
+.Fl auto
+or
+.Fl ddial
+options.
+You must also specify the destination label in
+.Pa /etc/ppp/ppp.conf
+to use.
+It must contain the
+.Dq set ifaddr
+command to {define} the remote peers IP address.
+(refer to
+.Pa /usr/share/examples/ppp/ppp.conf.sample )
+.Bd -literal -offset indent
+# ppp -auto pmdemand
+.Ed
+.Pp
+When
+.Fl auto
+or
+.Fl ddial
+is specified,
+.Nm
+runs as a daemon but you can still configure or examine its
+configuration by using the
+.Dq set server
+command in
+.Pa /etc/ppp/ppp.conf ,
+(for example,
+.Dq Li "set server +3000 mypasswd" )
+and connecting to the diagnostic port as follows:
+.Bd -literal -offset indent
+# pppctl 3000 (assuming tun0)
+Password:
+PPP ON awfulhak> show who
+tcp (127.0.0.1:1028) *
+.Ed
+.Pp
+The
+.Dq show who
+command lists users that are currently connected to
+.Nm
+itself.
+If the diagnostic socket is closed or changed to a different
+socket, all connections are immediately dropped.
+.Pp
+In
+.Fl auto
+mode, when an outgoing packet is detected,
+.Nm
+will perform the dialing action (chat script) and try to connect
+with the peer.
+In
+.Fl ddial
+mode, the dialing action is performed any time the line is found
+to be down.
+If the connect fails, the default behaviour is to wait 30 seconds
+and then attempt to connect when another outgoing packet is detected.
+This behaviour can be changed using the
+.Dq set redial
+command:
+.Pp
+.No set redial Ar secs Ns Xo
+.Oo + Ns Ar inc Ns
+.Op - Ns Ar max Ns
+.Oc Ns Op . Ns Ar next
+.Op Ar attempts
+.Xc
+.Pp
+.Bl -tag -width attempts -compact
+.It Ar secs
+is the number of seconds to wait before attempting
+to connect again.
+If the argument is the literal string
+.Sq Li random ,
+the delay period is a random value between 1 and 30 seconds inclusive.
+.It Ar inc
+is the number of seconds that
+.Ar secs
+should be incremented each time a new dial attempt is made.
+The timeout reverts to
+.Ar secs
+only after a successful connection is established.
+The default value for
+.Ar inc
+is zero.
+.It Ar max
+is the maximum number of times
+.Nm
+should increment
+.Ar secs .
+The default value for
+.Ar max
+is 10.
+.It Ar next
+is the number of seconds to wait before attempting
+to dial the next number in a list of numbers (see the
+.Dq set phone
+command).
+The default is 3 seconds.
+Again, if the argument is the literal string
+.Sq Li random ,
+the delay period is a random value between 1 and 30 seconds.
+.It Ar attempts
+is the maximum number of times to try to connect for each outgoing packet
+that triggers a dial.
+The previous value is unchanged if this parameter is omitted.
+If a value of zero is specified for
+.Ar attempts ,
+.Nm
+will keep trying until a connection is made.
+.El
+.Pp
+So, for example:
+.Bd -literal -offset indent
+set redial 10.3 4
+.Ed
+.Pp
+will attempt to connect 4 times for each outgoing packet that causes
+a dial attempt with a 3 second delay between each number and a 10 second
+delay after all numbers have been tried.
+If multiple phone numbers
+are specified, the total number of attempts is still 4 (it does not
+attempt each number 4 times).
+.Pp
+Alternatively,
+.Pp
+.Bd -literal -offset indent
+set redial 10+10-5.3 20
+.Ed
+.Pp
+tells
+.Nm
+to attempt to connect 20 times.
+After the first attempt,
+.Nm
+pauses for 10 seconds.
+After the next attempt it pauses for 20 seconds
+and so on until after the sixth attempt it pauses for 1 minute.
+The next 14 pauses will also have a duration of one minute.
+If
+.Nm
+connects, disconnects and fails to connect again, the timeout starts again
+at 10 seconds.
+.Pp
+Modifying the dial delay is very useful when running
+.Nm
+in
+.Fl auto
+mode on both ends of the link.
+If each end has the same timeout,
+both ends wind up calling each other at the same time if the link
+drops and both ends have packets queued.
+At some locations, the serial link may not be reliable, and carrier
+may be lost at inappropriate times.
+It is possible to have
+.Nm
+redial should carrier be unexpectedly lost during a session.
+.Bd -literal -offset indent
+set reconnect timeout ntries
+.Ed
+.Pp
+This command tells
+.Nm
+to re-establish the connection
+.Ar ntries
+times on loss of carrier with a pause of
+.Ar timeout
+seconds before each try.
+For example,
+.Bd -literal -offset indent
+set reconnect 3 5
+.Ed
+.Pp
+tells
+.Nm
+that on an unexpected loss of carrier, it should wait
+.Ar 3
+seconds before attempting to reconnect.
+This may happen up to
+.Ar 5
+times before
+.Nm
+gives up.
+The default value of ntries is zero (no reconnect).
+Care should be taken with this option.
+If the local timeout is slightly
+longer than the remote timeout, the reconnect feature will always be
+triggered (up to the given number of times) after the remote side
+times out and hangs up.
+NOTE: In this context, losing too many LQRs constitutes a loss of
+carrier and will trigger a reconnect.
+If the
+.Fl background
+flag is specified, all phone numbers are dialed at most once until
+a connection is made.
+The next number redial period specified with the
+.Dq set redial
+command is honoured, as is the reconnect tries value.
+If your redial
+value is less than the number of phone numbers specified, not all
+the specified numbers will be tried.
+To terminate the program, type
+.Bd -literal -offset indent
+PPP ON awfulhak> close
+ppp ON awfulhak> quit all
+.Ed
+.Pp
+A simple
+.Dq quit
+command will terminate the
+.Xr pppctl 8
+or
+.Xr telnet 1
+connection but not the
+.Nm
+program itself.
+You must use
+.Dq quit all
+to terminate
+.Nm
+as well.
+.Sh RECEIVING INCOMING PPP CONNECTIONS (Method 1)
+To handle an incoming
+.Em PPP
+connection request, follow these steps:
+.Bl -enum
+.It
+Make sure the modem and (optionally)
+.Pa /etc/rc.serial
+is configured correctly.
+.Bl -bullet -compact
+.It
+Use Hardware Handshake (CTS/RTS) for flow control.
+.It
+Modem should be set to NO echo back (ATE0) and NO results string (ATQ1).
+.El
+.Pp
+.It
+Edit
+.Pa /etc/ttys
+to enable a
+.Xr getty 8
+on the port where the modem is attached.
+For example:
+.Pp
+.Dl ttyd1 Qo /usr/libexec/getty std.38400 Qc dialup on secure
+.Pp
+Do not forget to send a
+.Dv HUP
+signal to the
+.Xr init 8
+process to start the
+.Xr getty 8 :
+.Pp
+.Dl # kill -HUP 1
+.Pp
+It is usually also necessary to train your modem to the same DTR speed
+as the getty:
+.Bd -literal -offset indent
+# ppp
+ppp ON awfulhak> set device /dev/cuad1
+ppp ON awfulhak> set speed 38400
+ppp ON awfulhak> term
+deflink: Entering terminal mode on /dev/cuad1
+Type `~?' for help
+at
+OK
+at
+OK
+atz
+OK
+at
+OK
+~.
+ppp ON awfulhak> quit
+.Ed
+.It
+Create a
+.Pa /usr/local/bin/ppplogin
+file with the following contents:
+.Bd -literal -offset indent
+#! /bin/sh
+exec /usr/sbin/ppp -direct incoming
+.Ed
+.Pp
+Direct mode
+.Pq Fl direct
+lets
+.Nm
+work with stdin and stdout.
+You can also use
+.Xr pppctl 8
+to connect to a configured diagnostic port, in the same manner as with
+client-side
+.Nm .
+.Pp
+Here, the
+.Ar incoming
+section must be set up in
+.Pa /etc/ppp/ppp.conf .
+.Pp
+Make sure that the
+.Ar incoming
+section contains the
+.Dq allow users
+command as appropriate.
+.It
+Prepare an account for the incoming user.
+.Bd -literal
+ppp:xxxx:66:66:PPP Login User:/home/ppp:/usr/local/bin/ppplogin
+.Ed
+.Pp
+Refer to the manual entries for
+.Xr adduser 8
+and
+.Xr vipw 8
+for details.
+.It
+Support for IPCP Domain Name Server and NetBIOS Name Server negotiation
+can be enabled using the
+.Dq accept dns
+and
+.Dq set nbns
+commands.
+Refer to their descriptions below.
+.El
+.Sh RECEIVING INCOMING PPP CONNECTIONS (Method 2)
+This method differs in that we use
+.Nm
+to authenticate the connection rather than
+.Xr login 1 :
+.Bl -enum
+.It
+Configure your default section in
+.Pa /etc/gettytab
+with automatic ppp recognition by specifying the
+.Dq pp
+capability:
+.Bd -literal
+default:\\
+ :pp=/usr/local/bin/ppplogin:\\
+ .....
+.Ed
+.It
+Configure your serial device(s), enable a
+.Xr getty 8
+and create
+.Pa /usr/local/bin/ppplogin
+as in the first three steps for method 1 above.
+.It
+Add either
+.Dq enable chap
+or
+.Dq enable pap
+(or both)
+to
+.Pa /etc/ppp/ppp.conf
+under the
+.Sq incoming
+label (or whatever label
+.Pa ppplogin
+uses).
+.It
+Create an entry in
+.Pa /etc/ppp/ppp.secret
+for each incoming user:
+.Bd -literal
+Pfred<TAB>xxxx
+Pgeorge<TAB>yyyy
+.Ed
+.El
+.Pp
+Now, as soon as
+.Xr getty 8
+detects a ppp connection (by recognising the HDLC frame headers), it runs
+.Dq /usr/local/bin/ppplogin .
+.Pp
+It is
+.Em VITAL
+that either PAP or CHAP are enabled as above.
+If they are not, you are
+allowing anybody to establish a ppp session with your machine
+.Em without
+a password, opening yourself up to all sorts of potential attacks.
+.Sh AUTHENTICATING INCOMING CONNECTIONS
+Normally, the receiver of a connection requires that the peer
+authenticates itself.
+This may be done using
+.Xr login 1 ,
+but alternatively, you can use PAP or CHAP.
+CHAP is the more secure of the two, but some clients may not support it.
+Once you decide which you wish to use, add the command
+.Sq enable chap
+or
+.Sq enable pap
+to the relevant section of
+.Pa ppp.conf .
+.Pp
+You must then configure the
+.Pa /etc/ppp/ppp.secret
+file.
+This file contains one line per possible client, each line
+containing up to five fields:
+.Pp
+.Ar name Ar key Oo
+.Ar hisaddr Op Ar label Op Ar callback-number
+.Oc
+.Pp
+The
+.Ar name
+and
+.Ar key
+specify the client username and password.
+If
+.Ar key
+is
+.Dq \&*
+and PAP is being used,
+.Nm
+will look up the password database
+.Pq Xr passwd 5
+when authenticating.
+If the client does not offer a suitable response based on any
+.Ar name Ns No / Ns Ar key
+combination in
+.Pa ppp.secret ,
+authentication fails.
+.Pp
+If authentication is successful,
+.Ar hisaddr
+(if specified)
+is used when negotiating IP numbers.
+See the
+.Dq set ifaddr
+command for details.
+.Pp
+If authentication is successful and
+.Ar label
+is specified, the current system label is changed to match the given
+.Ar label .
+This will change the subsequent parsing of the
+.Pa ppp.linkup
+and
+.Pa ppp.linkdown
+files.
+.Pp
+If authentication is successful and
+.Ar callback-number
+is specified and
+.Dq set callback
+has been used in
+.Pa ppp.conf ,
+the client will be called back on the given number.
+If CBCP is being used,
+.Ar callback-number
+may also contain a list of numbers or a
+.Dq \&* ,
+as if passed to the
+.Dq set cbcp
+command.
+The value will be used in
+.Nm Ns No 's
+subsequent CBCP phase.
+.Sh PPP OVER TCP and UDP (a.k.a Tunnelling)
+Instead of running
+.Nm
+over a serial link, it is possible to
+use a TCP connection instead by specifying the host, port and protocol as the
+device:
+.Pp
+.Dl set device ui-gate:6669/tcp
+.Pp
+Instead of opening a serial device,
+.Nm
+will open a TCP connection to the given machine on the given
+socket.
+It should be noted however that
+.Nm
+does not use the telnet protocol and will be unable to negotiate
+with a telnet server.
+You should set up a port for receiving this
+.Em PPP
+connection on the receiving machine (ui-gate).
+This is done by first updating
+.Pa /etc/services
+to name the service:
+.Pp
+.Dl ppp-in 6669/tcp # Incoming PPP connections over TCP
+.Pp
+and updating
+.Pa /etc/inetd.conf
+to tell
+.Xr inetd 8
+how to deal with incoming connections on that port:
+.Pp
+.Dl ppp-in stream tcp nowait root /usr/sbin/ppp ppp -direct ppp-in
+.Pp
+Do not forget to send a
+.Dv HUP
+signal to
+.Xr inetd 8
+after you have updated
+.Pa /etc/inetd.conf .
+Here, we use a label named
+.Dq ppp-in .
+The entry in
+.Pa /etc/ppp/ppp.conf
+on ui-gate (the receiver) should contain the following:
+.Bd -literal -offset indent
+ppp-in:
+ set timeout 0
+ set ifaddr 10.0.4.1 10.0.4.2
+.Ed
+.Pp
+and the entry in
+.Pa /etc/ppp/ppp.linkup
+should contain:
+.Bd -literal -offset indent
+ppp-in:
+ add 10.0.1.0/24 HISADDR
+.Ed
+.Pp
+It is necessary to put the
+.Dq add
+command in
+.Pa ppp.linkup
+to ensure that the route is only added after
+.Nm
+has negotiated and assigned addresses to its interface.
+.Pp
+You may also want to enable PAP or CHAP for security.
+To enable PAP, add the following line:
+.Bd -literal -offset indent
+ enable PAP
+.Ed
+.Pp
+You will also need to create the following entry in
+.Pa /etc/ppp/ppp.secret :
+.Bd -literal -offset indent
+MyAuthName MyAuthPasswd
+.Ed
+.Pp
+If
+.Ar MyAuthPasswd
+is a
+.Dq * ,
+the password is looked up in the
+.Xr passwd 5
+database.
+.Pp
+The entry in
+.Pa /etc/ppp/ppp.conf
+on awfulhak (the initiator) should contain the following:
+.Bd -literal -offset indent
+ui-gate:
+ set escape 0xff
+ set device ui-gate:ppp-in/tcp
+ set dial
+ set timeout 30
+ set log Phase Chat Connect hdlc LCP IPCP IPV6CP CCP tun
+ set ifaddr 10.0.4.2 10.0.4.1
+.Ed
+.Pp
+with the route setup in
+.Pa /etc/ppp/ppp.linkup :
+.Bd -literal -offset indent
+ui-gate:
+ add 10.0.2.0/24 HISADDR
+.Ed
+.Pp
+Again, if you are enabling PAP, you will also need this in the
+.Pa /etc/ppp/ppp.conf
+profile:
+.Bd -literal -offset indent
+ set authname MyAuthName
+ set authkey MyAuthKey
+.Ed
+.Pp
+We are assigning the address of 10.0.4.1 to ui-gate, and the address
+10.0.4.2 to awfulhak.
+To open the connection, just type
+.Pp
+.Dl awfulhak # ppp -background ui-gate
+.Pp
+The result will be an additional "route" on awfulhak to the
+10.0.2.0/24 network via the TCP connection, and an additional
+"route" on ui-gate to the 10.0.1.0/24 network.
+The networks are effectively bridged - the underlying TCP
+connection may be across a public network (such as the
+Internet), and the
+.Em PPP
+traffic is conceptually encapsulated
+(although not packet by packet) inside the TCP stream between
+the two gateways.
+.Pp
+The major disadvantage of this mechanism is that there are two
+"guaranteed delivery" mechanisms in place - the underlying TCP
+stream and whatever protocol is used over the
+.Em PPP
+link - probably TCP again.
+If packets are lost, both levels will
+get in each others way trying to negotiate sending of the missing
+packet.
+.Pp
+To avoid this overhead, it is also possible to do all this using
+UDP instead of TCP as the transport by simply changing the protocol
+from "tcp" to "udp".
+When using UDP as a transport,
+.Nm
+will operate in synchronous mode.
+This is another gain as the incoming
+data does not have to be rearranged into packets.
+.Pp
+Care should be taken when adding a default route through a tunneled
+setup like this.
+It is quite common for the default route
+(added in
+.Pa /etc/ppp/ppp.linkup )
+to end up routing the link's TCP connection through the tunnel,
+effectively garrotting the connection.
+To avoid this, make sure you add a static route for the benefit of
+the link:
+.Bd -literal -offset indent
+ui-gate:
+ set escape 0xff
+ set device ui-gate:ppp-in/tcp
+ add ui-gate x.x.x.x
+ .....
+.Ed
+.Pp
+where
+.Dq x.x.x.x
+is the IP number that your route to
+.Dq ui-gate
+would normally use.
+.Pp
+When routing your connection accross a public network such as the Internet,
+it is preferable to encrypt the data.
+This can be done with the help of the MPPE protocol, although currently this
+means that you will not be able to also compress the traffic as MPPE is
+implemented as a compression layer (thank Microsoft for this).
+To enable MPPE encryption, add the following lines to
+.Pa /etc/ppp/ppp.conf
+on the server:
+.Bd -literal -offset indent
+ enable MSCHAPv2
+ disable deflate pred1
+ deny deflate pred1
+.Ed
+.Pp
+ensuring that you have put the requisite entry in
+.Pa /etc/ppp/ppp.secret
+(MSCHAPv2 is challenge based, so
+.Xr passwd 5
+cannot be used)
+.Pp
+MSCHAPv2 and MPPE are accepted by default, so the client end should work
+without any additional changes (although ensure you have
+.Dq set authname
+and
+.Dq set authkey
+in your profile).
+.Sh NETWORK ADDRESS TRANSLATION (PACKET ALIASING)
+The
+.Fl nat
+command line option enables network address translation (a.k.a.\& packet
+aliasing).
+This allows the
+.Nm
+host to act as a masquerading gateway for other computers over
+a local area network.
+Outgoing IP packets are NAT'd so that they appear to come from the
+.Nm
+host, and incoming packets are de-NAT'd so that they are routed
+to the correct machine on the local area network.
+NAT allows computers on private, unregistered subnets to have Internet
+access, although they are invisible from the outside world.
+In general, correct
+.Nm
+operation should first be verified with network address translation disabled.
+Then, the
+.Fl nat
+option should be switched on, and network applications (web browser,
+.Xr telnet 1 ,
+.Xr ftp 1 ,
+.Xr ping 8 ,
+.Xr traceroute 8 )
+should be checked on the
+.Nm
+host.
+Finally, the same or similar applications should be checked on other
+computers in the LAN.
+If network applications work correctly on the
+.Nm
+host, but not on other machines in the LAN, then the masquerading
+software is working properly, but the host is either not forwarding
+or possibly receiving IP packets.
+Check that IP forwarding is enabled in
+.Pa /etc/rc.conf
+and that other machines have designated the
+.Nm
+host as the gateway for the LAN.
+.Sh PACKET FILTERING
+This implementation supports packet filtering.
+There are four kinds of
+filters: the
+.Em in
+filter, the
+.Em out
+filter, the
+.Em dial
+filter and the
+.Em alive
+filter.
+Here are the basics:
+.Bl -bullet
+.It
+A filter definition has the following syntax:
+.Pp
+set filter
+.Ar name
+.Ar rule-no
+.Ar action
+.Op !\&
+.Oo
+.Op host
+.Ar src_addr Ns Op / Ns Ar width
+.Op Ar dst_addr Ns Op / Ns Ar width
+.Oc
+.Ar [ proto Op src Ar cmp port
+.Op dst Ar cmp port
+.Op estab
+.Op syn
+.Op finrst
+.Op timeout Ar secs ]
+.Bl -enum
+.It
+.Ar Name
+should be one of
+.Sq in ,
+.Sq out ,
+.Sq dial
+or
+.Sq alive .
+.It
+.Ar Rule-no
+is a numeric value between
+.Sq 0
+and
+.Sq 39
+specifying the rule number.
+Rules are specified in numeric order according to
+.Ar rule-no ,
+but only if rule
+.Sq 0
+is defined.
+.It
+.Ar Action
+may be specified as
+.Sq permit
+or
+.Sq deny ,
+in which case, if a given packet matches the rule, the associated action
+is taken immediately.
+.Ar Action
+can also be specified as
+.Sq clear
+to clear the action associated with that particular rule, or as a new
+rule number greater than the current rule.
+In this case, if a given
+packet matches the current rule, the packet will next be matched against
+the new rule number (rather than the next rule number).
+.Pp
+The
+.Ar action
+may optionally be followed with an exclamation mark
+.Pq Dq !\& ,
+telling
+.Nm
+to reverse the sense of the following match.
+.It
+.Op Ar src_addr Ns Op / Ns Ar width
+and
+.Op Ar dst_addr Ns Op / Ns Ar width
+are the source and destination IP number specifications.
+If
+.Op / Ns Ar width
+is specified, it gives the number of relevant netmask bits,
+allowing the specification of an address range.
+.Pp
+Either
+.Ar src_addr
+or
+.Ar dst_addr
+may be given the values
+.Dv MYADDR ,
+.Dv HISADDR ,
+.Dv MYADDR6
+or
+.Dv HISADDR6
+(refer to the description of the
+.Dq bg
+command for a description of these values).
+When these values are used,
+the filters will be updated any time the values change.
+This is similar to the behaviour of the
+.Dq add
+command below.
+.It
+.Ar Proto
+may be any protocol from
+.Xr protocols 5 .
+.It
+.Ar Cmp
+is one of
+.Sq \&lt ,
+.Sq \&eq
+or
+.Sq \&gt ,
+meaning less-than, equal and greater-than respectively.
+.Ar Port
+can be specified as a numeric port or by service name from
+.Pa /etc/services .
+.It
+The
+.Sq estab ,
+.Sq syn ,
+and
+.Sq finrst
+flags are only allowed when
+.Ar proto
+is set to
+.Sq tcp ,
+and represent the TH_ACK, TH_SYN and TH_FIN or TH_RST TCP flags respectively.
+.It
+The timeout value adjusts the current idle timeout to at least
+.Ar secs
+seconds.
+If a timeout is given in the alive filter as well as in the in/out
+filter, the in/out value is used.
+If no timeout is given, the default timeout (set using
+.Ic set timeout
+and defaulting to 180 seconds) is used.
+.El
+.Pp
+.It
+Each filter can hold up to 40 rules, starting from rule 0.
+The entire rule set is not effective until rule 0 is defined,
+i.e., the default is to allow everything through.
+.It
+If no rule in a defined set of rules matches a packet, that packet will
+be discarded (blocked).
+If there are no rules in a given filter, the packet will be permitted.
+.It
+It is possible to filter based on the payload of UDP frames where those
+frames contain a
+.Em PROTO_IP
+.Em PPP
+frame header.
+See the
+.Ar filter-decapsulation
+option below for further details.
+.It
+Use
+.Dq set filter Ar name No -1
+to flush all rules.
+.El
+.Pp
+See
+.Pa /usr/share/examples/ppp/ppp.conf.sample .
+.Sh SETTING THE IDLE TIMER
+To check/set the idle timer, use the
+.Dq show bundle
+and
+.Dq set timeout
+commands:
+.Bd -literal -offset indent
+ppp ON awfulhak> set timeout 600
+.Ed
+.Pp
+The timeout period is measured in seconds, the default value for which
+is 180 seconds
+(or 3 min).
+To disable the idle timer function, use the command
+.Bd -literal -offset indent
+ppp ON awfulhak> set timeout 0
+.Ed
+.Pp
+In
+.Fl ddial
+and
+.Fl dedicated
+modes, the idle timeout is ignored.
+In
+.Fl auto
+mode, when the idle timeout causes the
+.Em PPP
+session to be
+closed, the
+.Nm
+program itself remains running.
+Another trigger packet will cause it to attempt to re-establish the link.
+.Sh PREDICTOR-1 and DEFLATE COMPRESSION
+.Nm
+supports both Predictor type 1 and deflate compression.
+By default,
+.Nm
+will attempt to use (or be willing to accept) both compression protocols
+when the peer agrees
+(or requests them).
+The deflate protocol is preferred by
+.Nm .
+Refer to the
+.Dq disable
+and
+.Dq deny
+commands if you wish to disable this functionality.
+.Pp
+It is possible to use a different compression algorithm in each direction
+by using only one of
+.Dq disable deflate
+and
+.Dq deny deflate
+(assuming that the peer supports both algorithms).
+.Pp
+By default, when negotiating DEFLATE,
+.Nm
+will use a window size of 15.
+Refer to the
+.Dq set deflate
+command if you wish to change this behaviour.
+.Pp
+A special algorithm called DEFLATE24 is also available, and is disabled
+and denied by default.
+This is exactly the same as DEFLATE except that
+it uses CCP ID 24 to negotiate.
+This allows
+.Nm
+to successfully negotiate DEFLATE with
+.Nm pppd
+version 2.3.*.
+.Sh CONTROLLING IP ADDRESS
+For IPv4,
+.Nm
+uses IPCP to negotiate IP addresses.
+Each side of the connection
+specifies the IP address that it is willing to use, and if the requested
+IP address is acceptable then
+.Nm
+returns an ACK to the requester.
+Otherwise,
+.Nm
+returns NAK to suggest that the peer use a different IP address.
+When
+both sides of the connection agree to accept the received request (and
+send an ACK), IPCP is set to the open state and a network level connection
+is established.
+To control this IPCP behaviour, this implementation has the
+.Dq set ifaddr
+command for defining the local and remote IP address:
+.Bd -ragged -offset indent
+.No set ifaddr Oo Ar src_addr Ns
+.Op / Ns Ar \&nn
+.Oo Ar dst_addr Ns Op / Ns Ar \&nn
+.Oo Ar netmask
+.Op Ar trigger_addr
+.Oc
+.Oc
+.Oc
+.Ed
+.Pp
+where,
+.Sq src_addr
+is the IP address that the local side is willing to use,
+.Sq dst_addr
+is the IP address which the remote side should use and
+.Sq netmask
+is the netmask that should be used.
+.Sq Src_addr
+defaults to the current
+.Xr hostname 1 ,
+.Sq dst_addr
+defaults to 0.0.0.0, and
+.Sq netmask
+defaults to whatever mask is appropriate for
+.Sq src_addr .
+It is only possible to make
+.Sq netmask
+smaller than the default.
+The usual value is 255.255.255.255, as
+most kernels ignore the netmask of a POINTOPOINT interface.
+.Pp
+Some incorrect
+.Em PPP
+implementations require that the peer negotiates a specific IP
+address instead of
+.Sq src_addr .
+If this is the case,
+.Sq trigger_addr
+may be used to specify this IP number.
+This will not affect the
+routing table unless the other side agrees with this proposed number.
+.Bd -literal -offset indent
+set ifaddr 192.244.177.38 192.244.177.2 255.255.255.255 0.0.0.0
+.Ed
+.Pp
+The above specification means:
+.Pp
+.Bl -bullet -compact
+.It
+I will first suggest that my IP address should be 0.0.0.0, but I
+will only accept an address of 192.244.177.38.
+.It
+I strongly insist that the peer uses 192.244.177.2 as his own
+address and will not permit the use of any IP address but 192.244.177.2.
+When the peer requests another IP address, I will always suggest that
+it uses 192.244.177.2.
+.It
+The routing table entry will have a netmask of 0xffffffff.
+.El
+.Pp
+This is all fine when each side has a pre-determined IP address, however
+it is often the case that one side is acting as a server which controls
+all IP addresses and the other side should go along with it.
+In order to allow more flexible behaviour, the
+.Dq set ifaddr
+command allows the user to specify IP addresses more loosely:
+.Pp
+.Dl set ifaddr 192.244.177.38/24 192.244.177.2/20
+.Pp
+A number followed by a slash
+.Pq Dq /
+represents the number of bits significant in the IP address.
+The above example means:
+.Pp
+.Bl -bullet -compact
+.It
+I would like to use 192.244.177.38 as my address if it is possible, but I will
+also accept any IP address between 192.244.177.0 and 192.244.177.255.
+.It
+I would like to make him use 192.244.177.2 as his own address, but I will also
+permit him to use any IP address between 192.244.176.0 and
+192.244.191.255.
+.It
+As you may have already noticed, 192.244.177.2 is equivalent to saying
+192.244.177.2/32.
+.It
+As an exception, 0 is equivalent to 0.0.0.0/0, meaning that I have no
+preferred IP address and will obey the remote peers selection.
+When using zero, no routing table entries will be made until a connection
+is established.
+.It
+192.244.177.2/0 means that I will accept/permit any IP address but I will
+suggest that 192.244.177.2 be used first.
+.El
+.Pp
+When negotiating IPv6 addresses, no control is given to the user.
+IPV6CP negotiation is fully automatic.
+.Sh CONNECTING WITH YOUR INTERNET SERVICE PROVIDER
+The following steps should be taken when connecting to your ISP:
+.Bl -enum
+.It
+Describe your providers phone number(s) in the dial script using the
+.Dq set phone
+command.
+This command allows you to set multiple phone numbers for
+dialing and redialing separated by either a pipe
+.Pq Dq \&|
+or a colon
+.Pq Dq \&: :
+.Bd -ragged -offset indent
+.No set phone Ar telno Ns Xo
+.Oo \&| Ns Ar backupnumber
+.Oc Ns ... Ns Oo : Ns Ar nextnumber
+.Oc Ns ...
+.Xc
+.Ed
+.Pp
+Numbers after the first in a pipe-separated list are only used if the
+previous number was used in a failed dial or login script.
+Numbers
+separated by a colon are used sequentially, irrespective of what happened
+as a result of using the previous number.
+For example:
+.Bd -literal -offset indent
+set phone "1234567|2345678:3456789|4567890"
+.Ed
+.Pp
+Here, the 1234567 number is attempted.
+If the dial or login script fails,
+the 2345678 number is used next time, but *only* if the dial or login script
+fails.
+On the dial after this, the 3456789 number is used.
+The 4567890
+number is only used if the dial or login script using the 3456789 fails.
+If the login script of the 2345678 number fails, the next number is still the
+3456789 number.
+As many pipes and colons can be used as are necessary
+(although a given site would usually prefer to use either the pipe or the
+colon, but not both).
+The next number redial timeout is used between all numbers.
+When the end of the list is reached, the normal redial period is
+used before starting at the beginning again.
+The selected phone number is substituted for the \\\\T string in the
+.Dq set dial
+command (see below).
+.It
+Set up your redial requirements using
+.Dq set redial .
+For example, if you have a bad telephone line or your provider is
+usually engaged (not so common these days), you may want to specify
+the following:
+.Bd -literal -offset indent
+set redial 10 4
+.Ed
+.Pp
+This says that up to 4 phone calls should be attempted with a pause of 10
+seconds before dialing the first number again.
+.It
+Describe your login procedure using the
+.Dq set dial
+and
+.Dq set login
+commands.
+The
+.Dq set dial
+command is used to talk to your modem and establish a link with your
+ISP, for example:
+.Bd -literal -offset indent
+set dial "ABORT BUSY ABORT NO\\\\sCARRIER TIMEOUT 4 \\"\\" \e
+ ATZ OK-ATZ-OK ATDT\\\\T TIMEOUT 60 CONNECT"
+.Ed
+.Pp
+This modem "chat" string means:
+.Bl -bullet
+.It
+Abort if the string "BUSY" or "NO CARRIER" are received.
+.It
+Set the timeout to 4 seconds.
+.It
+Expect nothing.
+.It
+Send ATZ.
+.It
+Expect OK.
+If that is not received within the 4 second timeout, send ATZ
+and expect OK.
+.It
+Send ATDTxxxxxxx where xxxxxxx is the next number in the phone list from
+above.
+.It
+Set the timeout to 60.
+.It
+Wait for the CONNECT string.
+.El
+.Pp
+Once the connection is established, the login script is executed.
+This script is written in the same style as the dial script, but care should
+be taken to avoid having your password logged:
+.Bd -literal -offset indent
+set authkey MySecret
+set login "TIMEOUT 15 login:-\\\\r-login: awfulhak \e
+ word: \\\\P ocol: PPP HELLO"
+.Ed
+.Pp
+This login "chat" string means:
+.Bl -bullet
+.It
+Set the timeout to 15 seconds.
+.It
+Expect "login:".
+If it is not received, send a carriage return and expect
+"login:" again.
+.It
+Send "awfulhak"
+.It
+Expect "word:" (the tail end of a "Password:" prompt).
+.It
+Send whatever our current
+.Ar authkey
+value is set to.
+.It
+Expect "ocol:" (the tail end of a "Protocol:" prompt).
+.It
+Send "PPP".
+.It
+Expect "HELLO".
+.El
+.Pp
+The
+.Dq set authkey
+command is logged specially.
+When
+.Ar command
+or
+.Ar chat
+logging is enabled, the actual password is not logged;
+.Sq ********
+is logged instead.
+.Pp
+Login scripts vary greatly between ISPs.
+If you are setting one up for the first time,
+.Em ENABLE CHAT LOGGING
+so that you can see if your script is behaving as you expect.
+.It
+Use
+.Dq set device
+and
+.Dq set speed
+to specify your serial line and speed, for example:
+.Bd -literal -offset indent
+set device /dev/cuad0
+set speed 115200
+.Ed
+.Pp
+Cuad0 is the first serial port on
+.Fx .
+If you are running
+.Nm
+on
+.Ox ,
+cua00 is the first.
+A speed of 115200 should be specified
+if you have a modem capable of bit rates of 28800 or more.
+In general, the serial speed should be about four times the modem speed.
+.It
+Use the
+.Dq set ifaddr
+command to {define} the IP address.
+.Bl -bullet
+.It
+If you know what IP address your provider uses, then use it as the remote
+address (dst_addr), otherwise choose something like 10.0.0.2/0 (see below).
+.It
+If your provider has assigned a particular IP address to you, then use
+it as your address (src_addr).
+.It
+If your provider assigns your address dynamically, choose a suitably
+unobtrusive and unspecific IP number as your address.
+10.0.0.1/0 would be appropriate.
+The bit after the / specifies how many bits of the
+address you consider to be important, so if you wanted to insist on
+something in the class C network 1.2.3.0, you could specify 1.2.3.1/24.
+.It
+If you find that your ISP accepts the first IP number that you suggest,
+specify third and forth arguments of
+.Dq 0.0.0.0 .
+This will force your ISP to assign a number.
+(The third argument will
+be ignored as it is less restrictive than the default mask for your
+.Sq src_addr ) .
+.El
+.Pp
+An example for a connection where you do not know your IP number or your
+ISPs IP number would be:
+.Bd -literal -offset indent
+set ifaddr 10.0.0.1/0 10.0.0.2/0 0.0.0.0 0.0.0.0
+.Ed
+.Pp
+.It
+In most cases, your ISP will also be your default router.
+If this is the case, add the line
+.Bd -literal -offset indent
+add default HISADDR
+.Ed
+.Pp
+to
+.Pa /etc/ppp/ppp.conf
+(or to
+.Pa /etc/ppp/ppp.linkup
+for setups that do not use
+.Fl auto
+mode).
+.Pp
+This tells
+.Nm
+to add a default route to whatever the peer address is
+(10.0.0.2 in this example).
+This route is
+.Sq sticky ,
+meaning that should the value of
+.Dv HISADDR
+change, the route will be updated accordingly.
+.It
+If your provider requests that you use PAP/CHAP authentication methods, add
+the next lines to your
+.Pa /etc/ppp/ppp.conf
+file:
+.Bd -literal -offset indent
+set authname MyName
+set authkey MyPassword
+.Ed
+.Pp
+Both are accepted by default, so
+.Nm
+will provide whatever your ISP requires.
+.Pp
+It should be noted that a login script is rarely (if ever) required
+when PAP or CHAP are in use.
+.It
+Ask your ISP to authenticate your nameserver address(es) with the line
+.Bd -literal -offset indent
+enable dns
+.Ed
+.Pp
+Do
+.Em NOT
+do this if you are running a local DNS unless you also either use
+.Dq resolv readonly
+or have
+.Dq resolv restore
+in
+.Pa /etc/ppp/ppp.linkdown ,
+as
+.Nm
+will simply circumvent its use by entering some nameserver lines in
+.Pa /etc/resolv.conf .
+.El
+.Pp
+Please refer to
+.Pa /usr/share/examples/ppp/ppp.conf.sample
+and
+.Pa /usr/share/examples/ppp/ppp.linkup.sample
+for some real examples.
+The pmdemand label should be appropriate for most ISPs.
+.Sh LOGGING FACILITY
+.Nm
+is able to generate the following log info either via
+.Xr syslog 3
+or directly to the screen:
+.Pp
+.Bl -tag -width XXXXXXXXX -offset XXX -compact
+.It Li All
+Enable all logging facilities.
+This generates a lot of log.
+The most common use of 'all' is as a basis, where you remove some facilities
+after enabling 'all' ('debug' and 'timer' are usually best disabled.)
+.It Li Async
+Dump async level packet in hex.
+.It Li CBCP
+Generate CBCP (CallBack Control Protocol) logs.
+.It Li CCP
+Generate a CCP packet trace.
+.It Li Chat
+Generate
+.Sq dial ,
+.Sq login ,
+.Sq logout
+and
+.Sq hangup
+chat script trace logs.
+.It Li Command
+Log commands executed either from the command line or any of the configuration
+files.
+.It Li Connect
+Log Chat lines containing the string "CONNECT".
+.It Li Debug
+Log debug information.
+.It Li DNS
+Log DNS QUERY packets.
+.It Li Filter
+Log packets permitted by the dial filter and denied by any filter.
+.It Li HDLC
+Dump HDLC packet in hex.
+.It Li ID0
+Log all function calls specifically made as user id 0.
+.It Li IPCP
+Generate an IPCP packet trace.
+.It Li LCP
+Generate an LCP packet trace.
+.It Li LQM
+Generate LQR reports.
+.It Li Phase
+Phase transition log output.
+.It Li Physical
+Dump physical level packet in hex.
+.It Li Radius
+Dump RADIUS information.
+RADIUS information resulting from the link coming up or down is logged at
+.Dq Phase
+level unless
+.Dq Radius
+logging is enabled.
+This log level is most useful for monitoring RADIUS alive information.
+.It Li Sync
+Dump sync level packet in hex.
+.It Li TCP/IP
+Dump all TCP/IP packets.
+.It Li Timer
+Log timer manipulation.
+.It Li TUN
+Include the tun device on each log line.
+.It Li Warning
+Output to the terminal device.
+If there is currently no terminal,
+output is sent to the log file using syslogs
+.Dv LOG_WARNING .
+.It Li Error
+Output to both the terminal device
+and the log file using syslogs
+.Dv LOG_ERROR .
+.It Li Alert
+Output to the log file using
+.Dv LOG_ALERT .
+.El
+.Pp
+The
+.Dq set log
+command allows you to set the logging output level.
+Multiple levels can be specified on a single command line.
+The default is equivalent to
+.Dq set log Phase .
+.Pp
+It is also possible to log directly to the screen.
+The syntax is the same except that the word
+.Dq local
+should immediately follow
+.Dq set log .
+The default is
+.Dq set log local
+(i.e., only the un-maskable warning, error and alert output).
+.Pp
+If The first argument to
+.Dq set log Op local
+begins with a
+.Sq +
+or a
+.Sq -
+character, the current log levels are
+not cleared, for example:
+.Bd -literal -offset indent
+PPP ON awfulhak> set log phase
+PPP ON awfulhak> show log
+Log: Phase Warning Error Alert
+Local: Warning Error Alert
+PPP ON awfulhak> set log +tcp/ip -warning
+PPP ON awfulhak> set log local +command
+PPP ON awfulhak> show log
+Log: Phase TCP/IP Warning Error Alert
+Local: Command Warning Error Alert
+.Ed
+.Pp
+Log messages of level Warning, Error and Alert are not controllable
+using
+.Dq set log Op local .
+.Pp
+The
+.Ar Warning
+level is special in that it will not be logged if it can be displayed
+locally.
+.Sh SIGNAL HANDLING
+.Nm
+deals with the following signals:
+.Bl -tag -width "USR2"
+.It INT
+Receipt of this signal causes the termination of the current connection
+(if any).
+This will cause
+.Nm
+to exit unless it is in
+.Fl auto
+or
+.Fl ddial
+mode.
+.It HUP, TERM & QUIT
+These signals tell
+.Nm
+to exit.
+.It USR1
+This signal, tells
+.Nm
+to re-open any existing server socket, dropping all existing diagnostic
+connections.
+Sockets that could not previously be opened will be retried.
+.It USR2
+This signal, tells
+.Nm
+to close any existing server socket, dropping all existing diagnostic
+connections.
+.Dv SIGUSR1
+can still be used to re-open the socket.
+.El
+.Sh MULTI-LINK PPP
+If you wish to use more than one physical link to connect to a
+.Em PPP
+peer, that peer must also understand the
+.Em MULTI-LINK PPP
+protocol.
+Refer to RFC 1990 for specification details.
+.Pp
+The peer is identified using a combination of his
+.Dq endpoint discriminator
+and his
+.Dq authentication id .
+Either or both of these may be specified.
+It is recommended that
+at least one is specified, otherwise there is no way of ensuring that
+all links are actually connected to the same peer program, and some
+confusing lock-ups may result.
+Locally, these identification variables are specified using the
+.Dq set enddisc
+and
+.Dq set authname
+commands.
+The
+.Sq authname
+(and
+.Sq authkey )
+must be agreed in advance with the peer.
+.Pp
+Multi-link capabilities are enabled using the
+.Dq set mrru
+command (set maximum reconstructed receive unit).
+Once multi-link is enabled,
+.Nm
+will attempt to negotiate a multi-link connection with the peer.
+.Pp
+By default, only one
+.Sq link
+is available
+(called
+.Sq deflink ) .
+To create more links, the
+.Dq clone
+command is used.
+This command will clone existing links, where all
+characteristics are the same except:
+.Bl -enum
+.It
+The new link has its own name as specified on the
+.Dq clone
+command line.
+.It
+The new link is an
+.Sq interactive
+link.
+Its mode may subsequently be changed using the
+.Dq set mode
+command.
+.It
+The new link is in a
+.Sq closed
+state.
+.El
+.Pp
+A summary of all available links can be seen using the
+.Dq show links
+command.
+.Pp
+Once a new link has been created, command usage varies.
+All link specific commands must be prefixed with the
+.Dq link Ar name
+command, specifying on which link the command is to be applied.
+When only a single link is available,
+.Nm
+is smart enough not to require the
+.Dq link Ar name
+prefix.
+.Pp
+Some commands can still be used without specifying a link - resulting
+in an operation at the
+.Sq bundle
+level.
+For example, once two or more links are available, the command
+.Dq show ccp
+will show CCP configuration and statistics at the multi-link level, and
+.Dq link deflink show ccp
+will show the same information at the
+.Dq deflink
+link level.
+.Pp
+Armed with this information, the following configuration might be used:
+.Pp
+.Bd -literal -offset indent
+mp:
+ set timeout 0
+ set log phase chat
+ set device /dev/cuad0 /dev/cuad1 /dev/cuad2
+ set phone "123456789"
+ set dial "ABORT BUSY ABORT NO\\sCARRIER TIMEOUT 5 \\"\\" ATZ \e
+ OK-AT-OK \\\\dATDT\\\\T TIMEOUT 45 CONNECT"
+ set login
+ set ifaddr 10.0.0.1/0 10.0.0.2/0 0.0.0.0 0.0.0.0
+ set authname ppp
+ set authkey ppppassword
+
+ set mrru 1500
+ clone 1,2,3 # Create 3 new links - duplicates of the default
+ link deflink remove # Delete the default link (called ``deflink'')
+.Ed
+.Pp
+Note how all cloning is done at the end of the configuration.
+Usually, the link will be configured first, then cloned.
+If you wish all links
+to be up all the time, you can add the following line to the end of your
+configuration.
+.Pp
+.Bd -literal -offset indent
+ link 1,2,3 set mode ddial
+.Ed
+.Pp
+If you want the links to dial on demand, this command could be used:
+.Pp
+.Bd -literal -offset indent
+ link * set mode auto
+.Ed
+.Pp
+Links may be tied to specific names by removing the
+.Dq set device
+line above, and specifying the following after the
+.Dq clone
+command:
+.Pp
+.Bd -literal -offset indent
+ link 1 set device /dev/cuad0
+ link 2 set device /dev/cuad1
+ link 3 set device /dev/cuad2
+.Ed
+.Pp
+Use the
+.Dq help
+command to see which commands require context (using the
+.Dq link
+command), which have optional
+context and which should not have any context.
+.Pp
+When
+.Nm
+has negotiated
+.Em MULTI-LINK
+mode with the peer, it creates a local domain socket in the
+.Pa /var/run
+directory.
+This socket is used to pass link information (including
+the actual link file descriptor) between different
+.Nm
+invocations.
+This facilitates
+.Nm Ns No 's
+ability to be run from a
+.Xr getty 8
+or directly from
+.Pa /etc/gettydefs
+(using the
+.Sq pp=
+capability), without needing to have initial control of the serial
+line.
+Once
+.Nm
+negotiates multi-link mode, it will pass its open link to any
+already running process.
+If there is no already running process,
+.Nm
+will act as the master, creating the socket and listening for new
+connections.
+.Sh PPP COMMAND LIST
+This section lists the available commands and their effect.
+They are usable either from an interactive
+.Nm
+session, from a configuration file or from a
+.Xr pppctl 8
+or
+.Xr telnet 1
+session.
+.Bl -tag -width 2n
+.It accept|deny|enable|disable Ar option....
+These directives tell
+.Nm
+how to negotiate the initial connection with the peer.
+Each
+.Dq option
+has a default of either accept or deny and enable or disable.
+.Dq Accept
+means that the option will be ACK'd if the peer asks for it.
+.Dq Deny
+means that the option will be NAK'd if the peer asks for it.
+.Dq Enable
+means that the option will be requested by us.
+.Dq Disable
+means that the option will not be requested by us.
+.Pp
+.Dq Option
+may be one of the following:
+.Bl -tag -width 2n
+.It acfcomp
+Default: Enabled and Accepted.
+ACFComp stands for Address and Control Field Compression.
+Non LCP packets will usually have an address
+field of 0xff (the All-Stations address) and a control field of
+0x03 (the Unnumbered Information command).
+If this option is
+negotiated, these two bytes are simply not sent, thus minimising
+traffic.
+.Pp
+See
+.Pa rfc1662
+for details.
+.It chap Ns Op \&05
+Default: Disabled and Accepted.
+CHAP stands for Challenge Handshake Authentication Protocol.
+Only one of CHAP and PAP (below) may be negotiated.
+With CHAP, the authenticator sends a "challenge" message to its peer.
+The peer uses a one-way hash function to encrypt the
+challenge and sends the result back.
+The authenticator does the same, and compares the results.
+The advantage of this mechanism is that no
+passwords are sent across the connection.
+A challenge is made when the connection is first made.
+Subsequent challenges may occur.
+If you want to have your peer authenticate itself, you must
+.Dq enable chap .
+in
+.Pa /etc/ppp/ppp.conf ,
+and have an entry in
+.Pa /etc/ppp/ppp.secret
+for the peer.
+.Pp
+When using CHAP as the client, you need only specify
+.Dq AuthName
+and
+.Dq AuthKey
+in
+.Pa /etc/ppp/ppp.conf .
+CHAP is accepted by default.
+Some
+.Em PPP
+implementations use "MS-CHAP" rather than MD5 when encrypting the
+challenge.
+MS-CHAP is a combination of MD4 and DES.
+If
+.Nm
+was built on a machine with DES libraries available, it will respond
+to MS-CHAP authentication requests, but will never request them.
+.It deflate
+Default: Enabled and Accepted.
+This option decides if deflate
+compression will be used by the Compression Control Protocol (CCP).
+This is the same algorithm as used by the
+.Xr gzip 1
+program.
+Note: There is a problem negotiating
+.Ar deflate
+capabilities with
+.Xr pppd 8
+- a
+.Em PPP
+implementation available under many operating systems.
+.Nm pppd
+(version 2.3.1) incorrectly attempts to negotiate
+.Ar deflate
+compression using type
+.Em 24
+as the CCP configuration type rather than type
+.Em 26
+as specified in
+.Pa rfc1979 .
+Type
+.Ar 24
+is actually specified as
+.Dq PPP Magna-link Variable Resource Compression
+in
+.Pa rfc1975 !
+.Nm
+is capable of negotiating with
+.Nm pppd ,
+but only if
+.Dq deflate24
+is
+.Ar enable Ns No d
+and
+.Ar accept Ns No ed .
+.It deflate24
+Default: Disabled and Denied.
+This is a variance of the
+.Ar deflate
+option, allowing negotiation with the
+.Xr pppd 8
+program.
+Refer to the
+.Ar deflate
+section above for details.
+It is disabled by default as it violates
+.Pa rfc1975 .
+.It dns
+Default: Disabled and Denied.
+This option allows DNS negotiation.
+.Pp
+If
+.Dq enable Ns No d,
+.Nm
+will request that the peer confirms the entries in
+.Pa /etc/resolv.conf .
+If the peer NAKs our request (suggesting new IP numbers),
+.Pa /etc/resolv.conf
+is updated and another request is sent to confirm the new entries.
+.Pp
+If
+.Dq accept Ns No ed,
+.Nm
+will answer any DNS queries requested by the peer rather than rejecting
+them.
+The answer is taken from
+.Pa /etc/resolv.conf
+unless the
+.Dq set dns
+command is used as an override.
+.It enddisc
+Default: Enabled and Accepted.
+This option allows control over whether we
+negotiate an endpoint discriminator.
+We only send our discriminator if
+.Dq set enddisc
+is used and
+.Ar enddisc
+is enabled.
+We reject the peers discriminator if
+.Ar enddisc
+is denied.
+.It LANMan|chap80lm
+Default: Disabled and Accepted.
+The use of this authentication protocol
+is discouraged as it partially violates the authentication protocol by
+implementing two different mechanisms (LANMan & NT) under the guise of
+a single CHAP type (0x80).
+.Dq LANMan
+uses a simple DES encryption mechanism and is the least secure of the
+CHAP alternatives (although is still more secure than PAP).
+.Pp
+Refer to the
+.Dq MSChap
+description below for more details.
+.It lqr
+Default: Disabled and Accepted.
+This option decides if Link Quality Requests will be sent or accepted.
+LQR is a protocol that allows
+.Nm
+to determine that the link is down without relying on the modems
+carrier detect.
+When LQR is enabled,
+.Nm
+sends the
+.Em QUALPROTO
+option (see
+.Dq set lqrperiod
+below) as part of the LCP request.
+If the peer agrees, both sides will
+exchange LQR packets at the agreed frequency, allowing detailed link
+quality monitoring by enabling LQM logging.
+If the peer does not agree, and if the
+.Dq echo
+option is enabled,
+.Nm
+will send
+.Em LCP ECHO
+requests instead.
+These packets pass no information of interest, but they
+.Em MUST
+be replied to by the peer.
+.Pp
+Whether using
+.Em LQR
+or
+.Em LCP ECHO ,
+.Nm
+will abruptly drop the connection if 5 unacknowledged packets have been
+sent rather than sending a 6th.
+A message is logged at the
+.Em PHASE
+level, and any appropriate
+.Dq reconnect
+values are honoured as if the peer were responsible for dropping the
+connection.
+.Pp
+Refer to the
+.Dq enable echo
+command description for differences in behaviour prior to
+.Nm
+version 3.4.2.
+.It mppe
+Default: Enabled and Accepted.
+This is Microsoft Point to Point Encryption scheme.
+MPPE key size can be
+40-, 56- and 128-bits.
+Refer to
+.Dq set mppe
+command.
+.It MSChapV2|chap81
+Default: Disabled and Accepted.
+It is very similar to standard CHAP (type 0x05)
+except that it issues challenges of a fixed 16 bytes in length and uses a
+combination of MD4, SHA-1 and DES to encrypt the challenge rather than using the
+standard MD5 mechanism.
+.It MSChap|chap80nt
+Default: Disabled and Accepted.
+The use of this authentication protocol
+is discouraged as it partially violates the authentication protocol by
+implementing two different mechanisms (LANMan & NT) under the guise of
+a single CHAP type (0x80).
+It is very similar to standard CHAP (type 0x05)
+except that it issues challenges of a fixed 8 bytes in length and uses a
+combination of MD4 and DES to encrypt the challenge rather than using the
+standard MD5 mechanism.
+CHAP type 0x80 for LANMan is also supported - see
+.Dq enable LANMan
+for details.
+.Pp
+Because both
+.Dq LANMan
+and
+.Dq NT
+use CHAP type 0x80, when acting as authenticator with both
+.Dq enable Ns No d ,
+.Nm
+will rechallenge the peer up to three times if it responds using the wrong
+one of the two protocols.
+This gives the peer a chance to attempt using both protocols.
+.Pp
+Conversely, when
+.Nm
+acts as the authenticatee with both protocols
+.Dq accept Ns No ed ,
+the protocols are used alternately in response to challenges.
+.Pp
+Note: If only LANMan is enabled,
+.Xr pppd 8
+(version 2.3.5) misbehaves when acting as authenticatee.
+It provides both
+the NT and the LANMan answers, but also suggests that only the NT answer
+should be used.
+.It pap
+Default: Disabled and Accepted.
+PAP stands for Password Authentication Protocol.
+Only one of PAP and CHAP (above) may be negotiated.
+With PAP, the ID and Password are sent repeatedly to the peer until
+authentication is acknowledged or the connection is terminated.
+This is a rather poor security mechanism.
+It is only performed when the connection is first established.
+If you want to have your peer authenticate itself, you must
+.Dq enable pap .
+in
+.Pa /etc/ppp/ppp.conf ,
+and have an entry in
+.Pa /etc/ppp/ppp.secret
+for the peer (although see the
+.Dq passwdauth
+and
+.Dq set radius
+options below).
+.Pp
+When using PAP as the client, you need only specify
+.Dq AuthName
+and
+.Dq AuthKey
+in
+.Pa /etc/ppp/ppp.conf .
+PAP is accepted by default.
+.It pred1
+Default: Enabled and Accepted.
+This option decides if Predictor 1
+compression will be used by the Compression Control Protocol (CCP).
+.It protocomp
+Default: Enabled and Accepted.
+This option is used to negotiate
+PFC (Protocol Field Compression), a mechanism where the protocol
+field number is reduced to one octet rather than two.
+.It shortseq
+Default: Enabled and Accepted.
+This option determines if
+.Nm
+will request and accept requests for short
+(12 bit)
+sequence numbers when negotiating multi-link mode.
+This is only applicable if our MRRU is set (thus enabling multi-link).
+.It vjcomp
+Default: Enabled and Accepted.
+This option determines if Van Jacobson header compression will be used.
+.El
+.Pp
+The following options are not actually negotiated with the peer.
+Therefore, accepting or denying them makes no sense.
+.Bl -tag -width 2n
+.It echo
+Default: Disabled.
+When this option is enabled,
+.Nm
+will send
+.Em LCP ECHO
+requests to the peer at the frequency defined by
+.Dq echoperiod .
+Note,
+.Em LQR
+requests will supersede
+.Em LCP ECHO
+requests if enabled and negotiated.
+See
+.Dq set lqrperiod
+below for details.
+.Pp
+Prior to
+.Nm
+version 3.4.2,
+.Dq echo
+was considered enabled if lqr was enabled and negotiated, otherwise it was
+considered disabled.
+For the same behaviour, it is now necessary to
+.Dq enable lqr echo
+rather than just
+.Dq enable lqr .
+.It filter-decapsulation
+Default: Disabled.
+When this option is enabled,
+.Nm
+will examine UDP frames to see if they actually contain a
+.Em PPP
+frame as their payload.
+If this is the case, all filters will operate on the payload rather
+than the actual packet.
+.Pp
+This is useful if you want to send PPPoUDP traffic over a
+.Em PPP
+link, but want that link to do smart things with the real data rather than
+the UDP wrapper.
+.Pp
+The UDP frame payload must not be compressed in any way, otherwise
+.Nm
+will not be able to interpret it.
+It is therefore recommended that you
+.Ic disable vj pred1 deflate
+and
+.Ic deny vj pred1 deflate
+in the configuration for the
+.Nm
+invocation with the udp link.
+.It force-scripts
+Default: Disabled.
+Forces execution of the configured chat scripts in
+.Dv direct
+and
+.Dv dedicated
+modes.
+.It idcheck
+Default: Enabled.
+When
+.Nm
+exchanges low-level LCP, CCP and IPCP configuration traffic, the
+.Em Identifier
+field of any replies is expected to be the same as that of the request.
+By default,
+.Nm
+drops any reply packets that do not contain the expected identifier
+field, reporting the fact at the respective log level.
+If
+.Ar idcheck
+is disabled,
+.Nm
+will ignore the identifier field.
+.It iface-alias
+Default: Enabled if
+.Fl nat
+is specified.
+This option simply tells
+.Nm
+to add new interface addresses to the interface rather than replacing them.
+The option can only be enabled if network address translation is enabled
+.Pq Dq nat enable yes .
+.Pp
+With this option enabled,
+.Nm
+will pass traffic for old interface addresses through the NAT
+ifdef({LOCALNAT},{engine,},{engine
+(see
+.Xr libalias 3 ) ,})
+resulting in the ability (in
+.Fl auto
+mode) to properly connect the process that caused the PPP link to
+come up in the first place.
+.Pp
+Disabling NAT with
+.Dq nat enable no
+will also disable
+.Sq iface-alias .
+.It ipcp
+Default: Enabled.
+This option allows
+.Nm
+to attempt to negotiate IP control protocol capabilities and if
+successful to exchange IP datagrams with the peer.
+.It ipv6cp
+Default: Enabled.
+This option allows
+.Nm
+to attempt to negotiate IPv6 control protocol capabilities and if
+successful to exchange IPv6 datagrams with the peer.
+.It keep-session
+Default: Disabled.
+When
+.Nm
+runs as a Multi-link server, a different
+.Nm
+instance initially receives each connection.
+After determining that
+the link belongs to an already existing bundle (controlled by another
+.Nm
+invocation),
+.Nm
+will transfer the link to that process.
+.Pp
+If the link is a tty device or if this option is enabled,
+.Nm
+will not exit, but will change its process name to
+.Dq session owner
+and wait for the controlling
+.Nm
+to finish with the link and deliver a signal back to the idle process.
+This prevents the confusion that results from
+.Nm Ns No 's
+parent considering the link resource available again.
+.Pp
+For tty devices that have entries in
+.Pa /etc/ttys ,
+this is necessary to prevent another
+.Xr getty 8
+from being started, and for program links such as
+.Xr sshd 8 ,
+it prevents
+.Xr sshd 8
+from exiting due to the death of its child.
+As
+.Nm
+cannot determine its parents requirements (except for the tty case), this
+option must be enabled manually depending on the circumstances.
+.It loopback
+Default: Enabled.
+When
+.Ar loopback
+is enabled,
+.Nm
+will automatically loop back packets being sent
+out with a destination address equal to that of the
+.Em PPP
+interface.
+If disabled,
+.Nm
+will send the packet, probably resulting in an ICMP redirect from
+the other end.
+It is convenient to have this option enabled when
+the interface is also the default route as it avoids the necessity
+of a loopback route.
+.It NAS-IP-Address
+Default: Enabled.
+This option controls whether
+.Nm
+sends the
+.Dq NAS-IP-Address
+attribute to the RADIUS server when RADIUS is in use
+.Pq see Dq set radius .
+.Pp
+Note, at least one of
+.Dq NAS-IP-Address
+and
+.Dq NAS-Identifier
+must be enabled.
+.Pp
+Versions of
+.Nm
+prior to version 3.4.1 did not send the
+.Dq NAS-IP-Address
+attribute as it was reported to break the Radiator RADIUS server.
+As the latest rfc (2865) no longer hints that only one of
+.Dq NAS-IP-Address
+and
+.Dq NAS-Identifier
+should be sent (as rfc 2138 did),
+.Nm
+now sends both and leaves it up to the administrator that chooses to use
+bad RADIUS implementations to
+.Dq disable NAS-IP-Address .
+.It NAS-Identifier
+Default: Enabled.
+This option controls whether
+.Nm
+sends the
+.Dq NAS-Identifier
+attribute to the RADIUS server when RADIUS is in use
+.Pq see Dq set radius .
+.Pp
+Note, at least one of
+.Dq NAS-IP-Address
+and
+.Dq NAS-Identifier
+must be enabled.
+.It passwdauth
+Default: Disabled.
+Enabling this option will tell the PAP authentication
+code to use the password database (see
+.Xr passwd 5 )
+to authenticate the caller if they cannot be found in the
+.Pa /etc/ppp/ppp.secret
+file.
+.Pa /etc/ppp/ppp.secret
+is always checked first.
+If you wish to use passwords from
+.Xr passwd 5 ,
+but also to specify an IP number or label for a given client, use
+.Dq \&*
+as the client password in
+.Pa /etc/ppp/ppp.secret .
+.It proxy
+Default: Disabled.
+Enabling this option will tell
+.Nm
+to proxy ARP for the peer.
+This means that
+.Nm
+will make an entry in the ARP table using
+.Dv HISADDR
+and the
+.Dv MAC
+address of the local network in which
+.Dv HISADDR
+appears.
+This allows other machines connecteed to the LAN to talk to
+the peer as if the peer itself was connected to the LAN.
+The proxy entry cannot be made unless
+.Dv HISADDR
+is an address from a LAN.
+.It proxyall
+Default: Disabled.
+Enabling this will tell
+.Nm
+to add proxy arp entries for every IP address in all class C or
+smaller subnets routed via the tun interface.
+.Pp
+Proxy arp entries are only made for sticky routes that are added
+using the
+.Dq add
+command.
+No proxy arp entries are made for the interface address itself
+(as created by the
+.Dq set ifaddr
+command).
+.It sroutes
+Default: Enabled.
+When the
+.Dq add
+command is used with the
+.Dv HISADDR ,
+.Dv MYADDR ,
+.Dv HISADDR6
+or
+.Dv MYADDR6
+values, entries are stored in the
+.Sq sticky route
+list.
+Each time these variables change, this list is re-applied to the routing table.
+.Pp
+Disabling this option will prevent the re-application of sticky routes,
+although the
+.Sq stick route
+list will still be maintained.
+.It Op tcp Ns Xo
+.No mssfixup
+.Xc
+Default: Enabled.
+This option tells
+.Nm
+to adjust TCP SYN packets so that the maximum receive segment
+size is not greater than the amount allowed by the interface MTU.
+.It throughput
+Default: Enabled.
+This option tells
+.Nm
+to gather throughput statistics.
+Input and output is sampled over
+a rolling 5 second window, and current, best and total figures are retained.
+This data is output when the relevant
+.Em PPP
+layer shuts down, and is also available using the
+.Dq show
+command.
+Throughput statistics are available at the
+.Dq IPCP
+and
+.Dq physical
+levels.
+.It utmp
+Default: Enabled.
+Normally, when a user is authenticated using PAP or CHAP, and when
+.Nm
+is running in
+.Fl direct
+mode, an entry is made in the utmp and wtmp files for that user.
+Disabling this option will tell
+.Nm
+not to make any utmp or wtmp entries.
+This is usually only necessary if
+you require the user to both login and authenticate themselves.
+.El
+.Pp
+.It add Ns Xo
+.Op !\&
+.Ar dest Ns Op / Ns Ar nn
+.Op Ar mask
+.Op Ar gateway
+.Xc
+.Ar Dest
+is the destination IP address.
+The netmask is specified either as a number of bits with
+.Ar /nn
+or as an IP number using
+.Ar mask .
+.Ar 0 0
+or simply
+.Ar 0
+with no mask refers to the default route.
+It is also possible to use the literal name
+.Sq default
+instead of
+.Ar 0 .
+.Ar Gateway
+is the next hop gateway to get to the given
+.Ar dest
+machine/network.
+Refer to the
+.Xr route 8
+command for further details.
+.Pp
+It is possible to use the symbolic names
+.Sq MYADDR ,
+.Sq HISADDR ,
+.Sq MYADDR6
+or
+.Sq HISADDR6
+as the destination, and
+.Sq HISADDR
+or
+.Sq HISADDR6
+as the
+.Ar gateway .
+.Sq MYADDR
+is replaced with the interface IP address,
+.Sq HISADDR
+is replaced with the interface IP destination (peer) address,
+.Sq MYADDR6
+is replaced with the interface IPv6 address, and
+.Sq HISADDR6
+is replaced with the interface IPv6 destination address,
+.Pp
+If the
+.Ar add!\&
+command is used
+(note the trailing
+.Dq !\& ) ,
+then if the route already exists, it will be updated as with the
+.Sq route change
+command (see
+.Xr route 8
+for further details).
+.Pp
+Routes that contain the
+.Dq HISADDR ,
+.Dq MYADDR ,
+.Dq HISADDR6 ,
+.Dq MYADDR6 ,
+.Dq DNS0 ,
+or
+.Dq DNS1
+constants are considered
+.Sq sticky .
+They are stored in a list (use
+.Dq show ncp
+to see the list), and each time the value of one of these variables
+changes, the appropriate routing table entries are updated.
+This facility may be disabled using
+.Dq disable sroutes .
+.It allow Ar command Op Ar args
+This command controls access to
+.Nm
+and its configuration files.
+It is possible to allow user-level access,
+depending on the configuration file label and on the mode that
+.Nm
+is being run in.
+For example, you may wish to configure
+.Nm
+so that only user
+.Sq fred
+may access label
+.Sq fredlabel
+in
+.Fl background
+mode.
+.Pp
+User id 0 is immune to these commands.
+.Bl -tag -width 2n
+.It allow user Ns Xo
+.Op s
+.Ar logname Ns No ...
+.Xc
+By default, only user id 0 is allowed access to
+.Nm .
+If this command is used, all of the listed users are allowed access to
+the section in which the
+.Dq allow users
+command is found.
+The
+.Sq default
+section is always checked first (even though it is only ever automatically
+loaded at startup).
+.Dq allow users
+commands are cumulative in a given section, but users allowed in any given
+section override users allowed in the default section, so it is possible to
+allow users access to everything except a given label by specifying default
+users in the
+.Sq default
+section, and then specifying a new user list for that label.
+.Pp
+If user
+.Sq *
+is specified, access is allowed to all users.
+.It allow mode Ns Xo
+.Op s
+.Ar mode Ns No ...
+.Xc
+By default, access using any
+.Nm
+mode is possible.
+If this command is used, it restricts the access
+.Ar modes
+allowed to load the label under which this command is specified.
+Again, as with the
+.Dq allow users
+command, each
+.Dq allow modes
+command overrides any previous settings, and the
+.Sq default
+section is always checked first.
+.Pp
+Possible modes are:
+.Sq interactive ,
+.Sq auto ,
+.Sq direct ,
+.Sq dedicated ,
+.Sq ddial ,
+.Sq background
+and
+.Sq * .
+.Pp
+When running in multi-link mode, a section can be loaded if it allows
+.Em any
+of the currently existing line modes.
+.El
+.Pp
+.It nat Ar command Op Ar args
+This command allows the control of the network address translation (also
+known as masquerading or IP aliasing) facilities that are built into
+.Nm .
+NAT is done on the external interface only, and is unlikely to make sense
+if used with the
+.Fl direct
+flag.
+.Pp
+If nat is enabled on your system (it may be omitted at compile time),
+the following commands are possible:
+.Bl -tag -width 2n
+.It nat enable yes|no
+This command either switches network address translation on or turns it off.
+The
+.Fl nat
+command line flag is synonymous with
+.Dq nat enable yes .
+.It nat addr Op Ar addr_local addr_alias
+This command allows data for
+.Ar addr_alias
+to be redirected to
+.Ar addr_local .
+It is useful if you own a small number of real IP numbers that
+you wish to map to specific machines behind your gateway.
+.It nat deny_incoming yes|no
+If set to yes, this command will refuse all incoming packets where an
+aliasing link does not already exist.
+ifdef({LOCALNAT},{},{Refer to the
+.Sx CONCEPTUAL BACKGROUND
+section of
+.Xr libalias 3
+for a description of what an
+.Dq aliasing link
+is.
+})dnl
+.Pp
+It should be noted under what circumstances an aliasing link is
+ifdef({LOCALNAT},{created.},{created by
+.Xr libalias 3 .})
+It may be necessary to further protect your network from outside
+connections using the
+.Dq set filter
+or
+.Dq nat target
+commands.
+.It nat help|?
+This command gives a summary of available nat commands.
+.It nat log yes|no
+This option causes various NAT statistics and information to
+be logged to the file
+.Pa /var/log/alias.log .
+.It nat port Ar proto Ar targetIP Ns Xo
+.No : Ns Ar targetPort Ns
+.Oo
+.No - Ns Ar targetPort
+.Oc Ar aliasPort Ns
+.Oo
+.No - Ns Ar aliasPort
+.Oc Oo Ar remoteIP : Ns
+.Ar remotePort Ns
+.Oo
+.No - Ns Ar remotePort
+.Oc Ns
+.Oc
+.Xc
+This command causes incoming
+.Ar proto
+connections to
+.Ar aliasPort
+to be redirected to
+.Ar targetPort
+on
+.Ar targetIP .
+.Ar proto
+is either
+.Dq tcp
+or
+.Dq udp .
+.Pp
+A range of port numbers may be specified as shown above.
+The ranges must be of the same size.
+.Pp
+If
+.Ar remoteIP
+is specified, only data coming from that IP number is redirected.
+.Ar remotePort
+must either be
+.Dq 0
+(indicating any source port)
+or a range of ports the same size as the other ranges.
+.Pp
+This option is useful if you wish to run things like Internet phone on
+machines behind your gateway, but is limited in that connections to only
+one interior machine per source machine and target port are possible.
+.It nat proto Ar proto localIP Oo
+.Ar publicIP Op Ar remoteIP
+.Oc
+This command tells
+.Nm
+to redirect packets of protocol type
+.Ar proto
+(see
+.Xr protocols 5 )
+to the internal address
+.Ar localIP .
+.Pp
+If
+.Ar publicIP
+is specified, only packets destined for that address are matched,
+otherwise the default alias address is used.
+.Pp
+If
+.Ar remoteIP
+is specified, only packets matching that source address are matched,
+.Pp
+This command is useful for redirecting tunnel endpoints to an internal machine,
+for example:
+.Pp
+.Dl nat proto ipencap 10.0.0.1
+.It "nat proxy cmd" Ar arg Ns No ...
+This command tells
+.Nm
+to proxy certain connections, redirecting them to a given server.
+ifdef({LOCALNAT},{},{Refer to the description of
+.Fn PacketAliasProxyRule
+in
+.Xr libalias 3
+for details of the available commands.
+})dnl
+.It nat punch_fw Op Ar base count
+This command tells
+.Nm
+to punch holes in the firewall for FTP or IRC DCC connections.
+This is done dynamically by installing termporary 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 rule number
+.Ar base
+will be used for punching firewall holes.
+The range will be cleared when the
+.Dq nat punch_fw
+command is run.
+.Pp
+If no arguments are given, firewall punching is disabled.
+.It nat skinny_port Op Ar port
+This command tells
+.Nm
+which TCP port is used by the Skinny Station protocol.
+Skinny is used by
+Cisco IP phones to communicate with Cisco Call Managers to setup voice
+over IP calls.
+The typical port used by Skinny is 2000.
+.Pp
+If no argument is given, skinny aliasing is disabled.
+.It nat same_ports yes|no
+When enabled, this command will tell the network address translation engine to
+attempt to avoid changing the port number on outgoing packets.
+This is useful
+if you want to support protocols such as RPC and LPD which require
+connections to come from a well known port.
+.It nat target Op Ar address
+Set the given target address or clear it if no address is given.
+The target address is used
+ifdef({LOCALNAT},{},{by libalias })dnl
+to specify how to NAT incoming packets by default.
+If a target address is not set or if
+.Dq default
+is given, packets are not altered and are allowed to route to the internal
+network.
+.Pp
+The target address may be set to
+.Dq MYADDR ,
+in which case
+ifdef({LOCALNAT},{all packets will be redirected},
+{libalias will redirect all packets})
+to the interface address.
+.It nat use_sockets yes|no
+When enabled, this option tells the network address translation engine to
+create a socket so that it can guarantee a correct incoming ftp data or
+IRC connection.
+.It nat unregistered_only yes|no
+Only alter outgoing packets with an 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.
+.El
+.Pp
+These commands are also discussed in the file
+.Pa README.nat
+which comes with the source distribution.
+.Pp
+.It Op !\& Ns Xo
+.No bg Ar command
+.Xc
+The given
+.Ar command
+is executed in the background with the following words replaced:
+.Bl -tag -width COMPILATIONDATE
+.It Li AUTHNAME
+This is replaced with the local
+.Ar authname
+value.
+See the
+.Dq set authname
+command below.
+.It Li COMPILATIONDATE
+In previous software revisions, this was replaced with the date on which
+.Nm
+was compiled.
+This is no longer supported as it breaks the ability to recompile the same
+code to produce an exact duplicate of a previous compilation.
+.It Li DNS0 & DNS1
+These are replaced with the primary and secondary nameserver IP numbers.
+If nameservers are negotiated by IPCP, the values of these macros will change.
+.It Li ENDDISC
+This is replaced with the local endpoint discriminator value.
+See the
+.Dq set enddisc
+command below.
+.It Li HISADDR
+This is replaced with the peers IP number.
+.It Li HISADDR6
+This is replaced with the peers IPv6 number.
+.It Li INTERFACE
+This is replaced with the name of the interface that is in use.
+.It Li IPOCTETSIN
+This is replaced with the number of IP bytes received since the connection
+was established.
+.It Li IPOCTETSOUT
+This is replaced with the number of IP bytes sent since the connection
+was established.
+.It Li IPPACKETSIN
+This is replaced with the number of IP packets received since the connection
+was established.
+.It Li IPPACKETSOUT
+This is replaced with the number of IP packets sent since the connection
+was established.
+.It Li IPV6OCTETSIN
+This is replaced with the number of IPv6 bytes received since the connection
+was established.
+.It Li IPV6OCTETSOUT
+This is replaced with the number of IPv6 bytes sent since the connection
+was established.
+.It Li IPV6PACKETSIN
+This is replaced with the number of IPv6 packets received since the connection
+was established.
+.It Li IPV6PACKETSOUT
+This is replaced with the number of IPv6 packets sent since the connection
+was established.
+.It Li LABEL
+This is replaced with the last label name used.
+A label may be specified on the
+.Nm
+command line, via the
+.Dq load
+or
+.Dq dial
+commands and in the
+.Pa ppp.secret
+file.
+.It Li MYADDR
+This is replaced with the IP number assigned to the local interface.
+.It Li MYADDR6
+This is replaced with the IPv6 number assigned to the local interface.
+.It Li OCTETSIN
+This is replaced with the number of bytes received since the connection
+was established.
+.It Li OCTETSOUT
+This is replaced with the number of bytes sent since the connection
+was established.
+.It Li PACKETSIN
+This is replaced with the number of packets received since the connection
+was established.
+.It Li PACKETSOUT
+This is replaced with the number of packets sent since the connection
+was established.
+.It Li PEER_ENDDISC
+This is replaced with the value of the peers endpoint discriminator.
+.It Li PROCESSID
+This is replaced with the current process id.
+.It Li SOCKNAME
+This is replaced with the name of the diagnostic socket.
+.It Li UPTIME
+This is replaced with the bundle uptime in HH:MM:SS format.
+.It Li USER
+This is replaced with the username that has been authenticated with PAP or
+CHAP.
+Normally, this variable is assigned only in -direct mode.
+This value is available irrespective of whether utmp logging is enabled.
+.It Li VERSION
+This is replaced with the current version number of
+.Nm .
+.El
+.Pp
+These substitutions are also done by the
+.Dq set proctitle ,
+.Dq ident
+and
+.Dq log
+commands.
+.Pp
+If you wish to pause
+.Nm
+while the command executes, use the
+.Dq shell
+command instead.
+.It clear physical|ipcp|ipv6 Op current|overall|peak...
+Clear the specified throughput values at either the
+.Dq physical ,
+.Dq ipcp
+or
+.Dq ipv6cp
+level.
+If
+.Dq physical
+is specified, context must be given (see the
+.Dq link
+command below).
+If no second argument is given, all values are cleared.
+.It clone Ar name Ns Xo
+.Op \&, Ns Ar name Ns
+.No ...
+.Xc
+Clone the specified link, creating one or more new links according to the
+.Ar name
+argument(s).
+This command must be used from the
+.Dq link
+command below unless you have only got a single link (in which case that
+link becomes the default).
+Links may be removed using the
+.Dq remove
+command below.
+.Pp
+The default link name is
+.Dq deflink .
+.It close Op lcp|ccp Ns Op !\&
+If no arguments are given, the relevant protocol layers will be brought
+down and the link will be closed.
+If
+.Dq lcp
+is specified, the LCP layer is brought down, but
+.Nm
+will not bring the link offline.
+It is subsequently possible to use
+.Dq term
+(see below)
+to talk to the peer machine if, for example, something like
+.Dq slirp
+is being used.
+If
+.Dq ccp
+is specified, only the relevant compression layer is closed.
+If the
+.Dq !\&
+is used, the compression layer will remain in the closed state, otherwise
+it will re-enter the STOPPED state, waiting for the peer to initiate
+further CCP negotiation.
+In any event, this command does not disconnect the user from
+.Nm
+or exit
+.Nm .
+See the
+.Dq quit
+command below.
+.It delete Ns Xo
+.Op !\&
+.Ar dest
+.Xc
+This command deletes the route with the given
+.Ar dest
+IP address.
+If
+.Ar dest
+is specified as
+.Sq ALL ,
+all non-direct entries in the routing table for the current interface,
+and all
+.Sq sticky route
+entries are deleted.
+If
+.Ar dest
+is specified as
+.Sq default ,
+the default route is deleted.
+.Pp
+If the
+.Ar delete!\&
+command is used
+(note the trailing
+.Dq !\& ) ,
+.Nm
+will not complain if the route does not already exist.
+.It dial|call Op Ar label Ns Xo
+.No ...
+.Xc
+This command is the equivalent of
+.Dq load label
+followed by
+.Dq open ,
+and is provided for backwards compatibility.
+.It down Op Ar lcp|ccp
+Bring the relevant layer down ungracefully, as if the underlying layer
+had become unavailable.
+It is not considered polite to use this command on
+a Finite State Machine that is in the OPEN state.
+If no arguments are
+supplied, the entire link is closed (or if no context is given, all links
+are terminated).
+If
+.Sq lcp
+is specified, the
+.Em LCP
+layer is terminated but the device is not brought offline and the link
+is not closed.
+If
+.Sq ccp
+is specified, only the relevant compression layer(s) are terminated.
+.It help|? Op Ar command
+Show a list of available commands.
+If
+.Ar command
+is specified, show the usage string for that command.
+.It ident Op Ar text Ns No ...
+Identify the link to the peer using
+.Ar text .
+If
+.Ar text
+is empty, link identification is disabled.
+It is possible to use any of the words described for the
+.Ic bg
+command above.
+Refer to the
+.Ic sendident
+command for details of when
+.Nm
+identifies itself to the peer.
+.It iface Ar command Op args
+This command is used to control the interface used by
+.Nm .
+.Ar Command
+may be one of the following:
+.Bl -tag -width 2n
+.It iface add Ns Xo
+.Op !\&
+.Ar addr Ns Op / Ns Ar bits
+.Op Ar peer
+.Xc
+.It iface add Ns Xo
+.Op !\&
+.Ar addr
+.Ar mask
+.Ar peer
+.Xc
+Add the given
+.Ar addr mask peer
+combination to the interface.
+Instead of specifying
+.Ar mask ,
+.Ar /bits
+can be used
+(with no space between it and
+.Ar addr ) .
+If the given address already exists, the command fails unless the
+.Dq !\&
+is used - in which case the previous interface address entry is overwritten
+with the new one, allowing a change of netmask or peer address.
+.Pp
+If only
+.Ar addr
+is specified,
+.Ar bits
+defaults to
+.Dq 32
+and
+.Ar peer
+defaults to
+.Dq 255.255.255.255 .
+This address (the broadcast address) is the only duplicate peer address that
+.Nm
+allows.
+.It iface clear Op INET | INET6
+If this command is used while
+.Nm
+is in the OPENED state or while in
+.Fl auto
+mode, all addresses except for the NCP negotiated address are deleted
+from the interface.
+If
+.Nm
+is not in the OPENED state and is not in
+.Fl auto
+mode, all interface addresses are deleted.
+.Pp
+If the INET or INET6 arguments are used, only addresses for that address
+family are cleared.
+.Pp
+.It iface delete Ns Xo
+.Op !\& Ns
+.No |rm Ns Op !\&
+.Ar addr
+.Xc
+This command deletes the given
+.Ar addr
+from the interface.
+If the
+.Dq !\&
+is used, no error is given if the address is not currently assigned to
+the interface (and no deletion takes place).
+.It iface show
+Shows the current state and current addresses for the interface.
+It is much the same as running
+.Dq ifconfig INTERFACE .
+.It iface help Op Ar sub-command
+This command, when invoked without
+.Ar sub-command ,
+will show a list of possible
+.Dq iface
+sub-commands and a brief synopsis for each.
+When invoked with
+.Ar sub-command ,
+only the synopsis for the given sub-command is shown.
+.El
+.It Op data Ns Xo
+.No link
+.Ar name Ns Op , Ns Ar name Ns
+.No ... Ar command Op Ar args
+.Xc
+This command may prefix any other command if the user wishes to
+specify which link the command should affect.
+This is only applicable after multiple links have been created in Multi-link
+mode using the
+.Dq clone
+command.
+.Pp
+.Ar Name
+specifies the name of an existing link.
+If
+.Ar name
+is a comma separated list,
+.Ar command
+is executed on each link.
+If
+.Ar name
+is
+.Dq * ,
+.Ar command
+is executed on all links.
+.It load Op Ar label Ns Xo
+.No ...
+.Xc
+Load the given
+.Ar label Ns No (s)
+from the
+.Pa ppp.conf
+file.
+If
+.Ar label
+is not given, the
+.Ar default
+label is used.
+.Pp
+Unless the
+.Ar label
+section uses the
+.Dq set mode ,
+.Dq open
+or
+.Dq dial
+commands,
+.Nm
+will not attempt to make an immediate connection.
+.It log Ar word Ns No ...
+Send the given word(s) to the log file with the prefix
+.Dq LOG: .
+Word substitutions are done as explained under the
+.Dq !bg
+command above.
+.It open Op lcp|ccp|ipcp
+This is the opposite of the
+.Dq close
+command.
+All closed links are immediately brought up apart from second and subsequent
+.Ar demand-dial
+links - these will come up based on the
+.Dq set autoload
+command that has been used.
+.Pp
+If the
+.Dq lcp
+argument is used while the LCP layer is already open, LCP will be
+renegotiated.
+This allows various LCP options to be changed, after which
+.Dq open lcp
+can be used to put them into effect.
+After renegotiating LCP,
+any agreed authentication will also take place.
+.Pp
+If the
+.Dq ccp
+argument is used, the relevant compression layer is opened.
+Again, if it is already open, it will be renegotiated.
+.Pp
+If the
+.Dq ipcp
+argument is used, the link will be brought up as normal, but if
+IPCP is already open, it will be renegotiated and the network
+interface will be reconfigured.
+.Pp
+It is probably not good practice to re-open the PPP state machines
+like this as it is possible that the peer will not behave correctly.
+It
+.Em is
+however useful as a way of forcing the CCP or VJ dictionaries to be reset.
+.It passwd Ar pass
+Specify the password required for access to the full
+.Nm
+command set.
+This password is required when connecting to the diagnostic port (see the
+.Dq set server
+command).
+.Ar Pass
+is specified on the
+.Dq set server
+command line.
+The value of
+.Ar pass
+is not logged when
+.Ar command
+logging is active, instead, the literal string
+.Sq ********
+is logged.
+.It quit|bye Op all
+If
+.Dq quit
+is executed from the controlling connection or from a command file,
+ppp will exit after closing all connections.
+Otherwise, if the user
+is connected to a diagnostic socket, the connection is simply dropped.
+.Pp
+If the
+.Ar all
+argument is given,
+.Nm
+will exit despite the source of the command after closing all existing
+connections.
+.It remove|rm
+This command removes the given link.
+It is only really useful in multi-link mode.
+A link must be in the
+.Dv CLOSED
+state before it is removed.
+.It rename|mv Ar name
+This command renames the given link to
+.Ar name .
+It will fail if
+.Ar name
+is already used by another link.
+.Pp
+The default link name is
+.Sq deflink .
+Renaming it to
+.Sq modem ,
+.Sq cuad0
+or
+.Sq USR
+may make the log file more readable.
+.It resolv Ar command
+This command controls
+.Nm Ns No 's
+manipulation of the
+.Xr resolv.conf 5
+file.
+When
+.Nm
+starts up, it loads the contents of this file into memory and retains this
+image for future use.
+.Ar command
+is one of the following:
+.Bl -tag -width readonly
+.It Em readonly
+Treat
+.Pa /etc/resolv.conf
+as read only.
+If
+.Dq dns
+is enabled,
+.Nm
+will still attempt to negotiate nameservers with the peer, making the results
+available via the
+.Dv DNS0
+and
+.Dv DNS1
+macros.
+This is the opposite of the
+.Dq resolv writable
+command.
+.It Em reload
+Reload
+.Pa /etc/resolv.conf
+into memory.
+This may be necessary if for example a DHCP client overwrote
+.Pa /etc/resolv.conf .
+.It Em restore
+Replace
+.Pa /etc/resolv.conf
+with the version originally read at startup or with the last
+.Dq resolv reload
+command.
+This is sometimes a useful command to put in the
+.Pa /etc/ppp/ppp.linkdown
+file.
+.It Em rewrite
+Rewrite the
+.Pa /etc/resolv.conf
+file.
+This command will work even if the
+.Dq resolv readonly
+command has been used.
+It may be useful as a command in the
+.Pa /etc/ppp/ppp.linkup
+file if you wish to defer updating
+.Pa /etc/resolv.conf
+until after other commands have finished.
+.It Em writable
+Allow
+.Nm
+to update
+.Pa /etc/resolv.conf
+if
+.Dq dns
+is enabled and
+.Nm
+successfully negotiates a DNS.
+This is the opposite of the
+.Dq resolv readonly
+command.
+.El
+.It save
+This option is not (yet) implemented.
+.It sendident
+This command tells
+.Nm
+to identify itself to the peer.
+The link must be in LCP state or higher.
+If no identity has been set (via the
+.Ic ident
+command),
+.Ic sendident
+will fail.
+.Pp
+When an identity has been set,
+.Nm
+will automatically identify itself when it sends or receives a configure
+reject, when negotiation fails or when LCP reaches the opened state.
+.Pp
+Received identification packets are logged to the LCP log (see
+.Ic set log
+for details) and are never responded to.
+.It set Ns Xo
+.Op up
+.Ar var value
+.Xc
+This option allows the setting of any of the following variables:
+.Bl -tag -width 2n
+.It set accmap Ar hex-value
+ACCMap stands for Asynchronous Control Character Map.
+This is always
+negotiated with the peer, and defaults to a value of 00000000 in hex.
+This protocol is required to defeat hardware that depends on passing
+certain characters from end to end (such as XON/XOFF etc).
+.Pp
+For the XON/XOFF scenario, use
+.Dq set accmap 000a0000 .
+.It set Op auth Ns Xo
+.No key Ar value
+.Xc
+This sets the authentication key (or password) used in client mode
+PAP or CHAP negotiation to the given value.
+It also specifies the
+password to be used in the dial or login scripts in place of the
+.Sq \eP
+sequence, preventing the actual password from being logged.
+If
+.Ar command
+or
+.Ar chat
+logging is in effect,
+.Ar value
+is logged as
+.Sq ********
+for security reasons.
+.Pp
+If the first character of
+.Ar value
+is an exclamation mark
+.Pq Dq !\& ,
+.Nm
+treats the remainder of the string as a program that must be executed
+to determine the
+.Dq authname
+and
+.Dq authkey
+values.
+.Pp
+If the
+.Dq !\&
+is doubled up
+(to
+.Dq !! ) ,
+it is treated as a single literal
+.Dq !\& ,
+otherwise, ignoring the
+.Dq !\& ,
+.Ar value
+is parsed as a program to execute in the same was as the
+.Dq !bg
+command above, substituting special names in the same manner.
+Once executed,
+.Nm
+will feed the program three lines of input, each terminated by a newline
+character:
+.Bl -bullet
+.It
+The host name as sent in the CHAP challenge.
+.It
+The challenge string as sent in the CHAP challenge.
+.It
+The locally defined
+.Dq authname .
+.El
+.Pp
+Two lines of output are expected:
+.Bl -bullet
+.It
+The
+.Dq authname
+to be sent with the CHAP response.
+.It
+The
+.Dq authkey ,
+which is encrypted with the challenge and request id, the answer being sent
+in the CHAP response packet.
+.El
+.Pp
+When configuring
+.Nm
+in this manner, it is expected that the host challenge is a series of ASCII
+digits or characters.
+An encryption device or Secure ID card is usually
+required to calculate the secret appropriate for the given challenge.
+.It set authname Ar id
+This sets the authentication id used in client mode PAP or CHAP negotiation.
+.Pp
+If used in
+.Fl direct
+mode with CHAP enabled,
+.Ar id
+is used in the initial authentication challenge and should normally be set to
+the local machine name.
+.It set autoload Xo
+.Ar min-percent max-percent period
+.Xc
+These settings apply only in multi-link mode and default to zero, zero and
+five respectively.
+When more than one
+.Ar demand-dial
+(also known as
+.Fl auto )
+mode link is available, only the first link is made active when
+.Nm
+first reads data from the tun device.
+The next
+.Ar demand-dial
+link will be opened only when the current bundle throughput is at least
+.Ar max-percent
+percent of the total bundle bandwidth for
+.Ar period
+seconds.
+When the current bundle throughput decreases to
+.Ar min-percent
+percent or less of the total bundle bandwidth for
+.Ar period
+seconds, a
+.Ar demand-dial
+link will be brought down as long as it is not the last active link.
+.Pp
+Bundle throughput is measured as the maximum of inbound and outbound
+traffic.
+.Pp
+The default values cause
+.Ar demand-dial
+links to simply come up one at a time.
+.Pp
+Certain devices cannot determine their physical bandwidth, so it
+is sometimes necessary to use the
+.Dq set bandwidth
+command (described below) to make
+.Dq set autoload
+work correctly.
+.It set bandwidth Ar value
+This command sets the connection bandwidth in bits per second.
+.Ar value
+must be greater than zero.
+It is currently only used by the
+.Dq set autoload
+command above.
+.It set callback Ar option Ns No ...
+If no arguments are given, callback is disabled, otherwise,
+.Nm
+will request (or in
+.Fl direct
+mode, will accept) one of the given
+.Ar option Ns No s .
+In client mode, if an
+.Ar option
+is NAK'd
+.Nm
+will request a different
+.Ar option ,
+until no options remain at which point
+.Nm
+will terminate negotiations (unless
+.Dq none
+is one of the specified
+.Ar option ) .
+In server mode,
+.Nm
+will accept any of the given protocols - but the client
+.Em must
+request one of them.
+If you wish callback to be optional, you must {include}
+.Ar none
+as an option.
+.Pp
+The
+.Ar option Ns No s
+are as follows (in this order of preference):
+.Pp
+.Bl -tag -width Ds
+.It auth
+The callee is expected to decide the callback number based on
+authentication.
+If
+.Nm
+is the callee, the number should be specified as the fifth field of
+the peers entry in
+.Pa /etc/ppp/ppp.secret .
+.It cbcp
+Microsoft's callback control protocol is used.
+See
+.Dq set cbcp
+below.
+.Pp
+If you wish to negotiate
+.Ar cbcp
+in client mode but also wish to allow the server to request no callback at
+CBCP negotiation time, you must specify both
+.Ar cbcp
+and
+.Ar none
+as callback options.
+.It E.164 *| Ns Xo
+.Ar number Ns Op , Ns Ar number Ns
+.No ...
+.Xc
+The caller specifies the
+.Ar number .
+If
+.Nm
+is the callee,
+.Ar number
+should be either a comma separated list of allowable numbers or a
+.Dq \&* ,
+meaning any number is permitted.
+If
+.Nm
+is the caller, only a single number should be specified.
+.Pp
+Note, this option is very unsafe when used with a
+.Dq \&*
+as a malicious caller can tell
+.Nm
+to call any (possibly international) number without first authenticating
+themselves.
+.It none
+If the peer does not wish to do callback at all,
+.Nm
+will accept the fact and continue without callback rather than terminating
+the connection.
+This is required (in addition to one or more other callback
+options) if you wish callback to be optional.
+.El
+.Pp
+.It set cbcp Oo
+.No *| Ns Ar number Ns Oo
+.No , Ns Ar number Ns ...\& Oc
+.Op Ar delay Op Ar retry
+.Oc
+If no arguments are given, CBCP (Microsoft's CallBack Control Protocol)
+is disabled - ie, configuring CBCP in the
+.Dq set callback
+command will result in
+.Nm
+requesting no callback in the CBCP phase.
+Otherwise,
+.Nm
+attempts to use the given phone
+.Ar number Ns No (s).
+.Pp
+In server mode
+.Pq Fl direct ,
+.Nm
+will insist that the client uses one of these numbers, unless
+.Dq \&*
+is used in which case the client is expected to specify the number.
+.Pp
+In client mode,
+.Nm
+will attempt to use one of the given numbers (whichever it finds to
+be agreeable with the peer), or if
+.Dq \&*
+is specified,
+.Nm
+will expect the peer to specify the number.
+.It set cd Oo
+.No off| Ns Ar seconds Ns Op !\&
+.Oc
+Normally,
+.Nm
+checks for the existence of carrier depending on the type of device
+that has been opened:
+.Bl -tag -width XXX -offset XXX
+.It Terminal Devices
+Carrier is checked one second after the login script is complete.
+If it is not set,
+.Nm
+assumes that this is because the device does not support carrier (which
+is true for most
+.Dq laplink
+NULL-modem cables), logs the fact and stops checking
+for carrier.
+.Pp
+As ptys do not support the TIOCMGET ioctl, the tty device will switch all
+carrier detection off when it detects that the device is a pty.
+.It PPPoE (netgraph) Devices
+Carrier is checked once per second for 5 seconds.
+If it is not set after
+the fifth second, the connection attempt is considered to have failed and
+the device is closed.
+Carrier is always required for PPPoE devices.
+.El
+.Pp
+All other device types do not support carrier.
+Setting a carrier value will
+result in a warning when the device is opened.
+.Pp
+Some modems take more than one second after connecting to assert the carrier
+signal.
+If this delay is not increased, this will result in
+.Nm Ns No 's
+inability to detect when the link is dropped, as
+.Nm
+assumes that the device is not asserting carrier.
+.Pp
+The
+.Dq set cd
+command overrides the default carrier behaviour.
+.Ar seconds
+specifies the maximum number of seconds that
+.Nm
+should wait after the dial script has finished before deciding if
+carrier is available or not.
+.Pp
+If
+.Dq off
+is specified,
+.Nm
+will not check for carrier on the device, otherwise
+.Nm
+will not proceed to the login script until either carrier is detected
+or until
+.Ar seconds
+has elapsed, at which point
+.Nm
+assumes that the device will not set carrier.
+.Pp
+If no arguments are given, carrier settings will go back to their default
+values.
+.Pp
+If
+.Ar seconds
+is followed immediately by an exclamation mark
+.Pq Dq !\& ,
+.Nm
+will
+.Em require
+carrier.
+If carrier is not detected after
+.Ar seconds
+seconds, the link will be disconnected.
+.It set choked Op Ar timeout
+This sets the number of seconds that
+.Nm
+will keep a choked output queue before dropping all pending output packets.
+If
+.Ar timeout
+is less than or equal to zero or if
+.Ar timeout
+is not specified, it is set to the default value of
+.Em 120 seconds .
+.Pp
+A choked output queue occurs when
+.Nm
+has read a certain number of packets from the local network for transmission,
+but cannot send the data due to link failure (the peer is busy etc.).
+.Nm
+will not read packets indefinitely.
+Instead, it reads up to
+.Em 30
+packets (or
+.Em 30 No +
+.Em nlinks No *
+.Em 2
+packets in multi-link mode), then stops reading the network interface
+until either
+.Ar timeout
+seconds have passed or at least one packet has been sent.
+.Pp
+If
+.Ar timeout
+seconds pass, all pending output packets are dropped.
+.It set ctsrts|crtscts on|off
+This sets hardware flow control.
+Hardware flow control is
+.Ar on
+by default.
+.It set deflate Ar out-winsize Op Ar in-winsize
+This sets the DEFLATE algorithms default outgoing and incoming window
+sizes.
+Both
+.Ar out-winsize
+and
+.Ar in-winsize
+must be values between
+.Em 8
+and
+.Em 15 .
+If
+.Ar in-winsize
+is specified,
+.Nm
+will insist that this window size is used and will not accept any other
+values from the peer.
+.It set dns Op Ar primary Op Ar secondary
+This command specifies DNS overrides for the
+.Dq accept dns
+command.
+Refer to the
+.Dq accept
+command description above for details.
+This command does not affect the IP numbers requested using
+.Dq enable dns .
+.It set device|line Xo
+.Ar value Ns No ...
+.Xc
+This sets the device(s) to which
+.Nm
+will talk to the given
+.Dq value .
+.Pp
+All serial device names are expected to begin with
+.Pa /dev/ .
+Serial devices are usually called
+.Pa cuaXX .
+.Pp
+If
+.Dq value
+does not begin with
+.Pa /dev/ ,
+it must either begin with an exclamation mark
+.Pq Dq !\& ,
+be of the format
+.No PPPoE: Ns Ar iface Ns Xo
+.Op \&: Ns Ar provider Ns
+.Xc
+(on
+.Xr netgraph 4
+enabled systems), or be of the format
+.Sm off
+.Ar host : port Op /tcp|udp .
+.Sm on
+.Pp
+If it begins with an exclamation mark, the rest of the device name is
+treated as a program name, and that program is executed when the device
+is opened.
+Standard input, output and error are fed back to
+.Nm
+and are read and written as if they were a regular device.
+.Pp
+If a
+.No PPPoE: Ns Ar iface Ns Xo
+.Op \&: Ns Ar provider Ns
+.Xc
+specification is given,
+.Nm
+will attempt to create a
+.Em PPP
+over Ethernet connection using the given
+.Ar iface
+interface by using
+.Xr netgraph 4 .
+If
+.Xr netgraph 4
+is not available,
+.Nm
+will attempt to load it using
+.Xr kldload 2 .
+If this fails, an external program must be used such as the
+.Xr pppoed 8
+program available under
+.Ox .
+The given
+.Ar provider
+is passed as the service name in the PPPoE Discovery Initiation (PADI)
+packet.
+If no provider is given, an empty value will be used.
+.Pp
+When a PPPoE connection is established,
+.Nm
+will place the name of the Access Concentrator in the environment variable
+.Ev ACNAME .
+.Pp
+Refer to
+.Xr netgraph 4
+and
+.Xr ng_pppoe 4
+for further details.
+.Pp
+If a
+.Ar host Ns No : Ns Ar port Ns Oo
+.No /tcp|udp
+.Oc
+specification is given,
+.Nm
+will attempt to connect to the given
+.Ar host
+on the given
+.Ar port .
+If a
+.Dq /tcp
+or
+.Dq /udp
+suffix is not provided, the default is
+.Dq /tcp .
+Refer to the section on
+.Em PPP OVER TCP and UDP
+above for further details.
+.Pp
+If multiple
+.Dq values
+are specified,
+.Nm
+will attempt to open each one in turn until it succeeds or runs out of
+devices.
+.It set dial Ar chat-script
+This specifies the chat script that will be used to dial the other
+side.
+See also the
+.Dq set login
+command below.
+Refer to
+.Xr chat 8
+and to the example configuration files for details of the chat script
+format.
+It is possible to specify some special
+.Sq values
+in your chat script as follows:
+.Bl -tag -width 2n
+.It Li \ec
+When used as the last character in a
+.Sq send
+string, this indicates that a newline should not be appended.
+.It Li \ed
+When the chat script encounters this sequence, it delays two seconds.
+.It Li \ep
+When the chat script encounters this sequence, it delays for one quarter of
+a second.
+.It Li \en
+This is replaced with a newline character.
+.It Li \er
+This is replaced with a carriage return character.
+.It Li \es
+This is replaced with a space character.
+.It Li \et
+This is replaced with a tab character.
+.It Li \eT
+This is replaced by the current phone number (see
+.Dq set phone
+below).
+.It Li \eP
+This is replaced by the current
+.Ar authkey
+value (see
+.Dq set authkey
+above).
+.It Li \eU
+This is replaced by the current
+.Ar authname
+value (see
+.Dq set authname
+above).
+.El
+.Pp
+Note that two parsers will examine these escape sequences, so in order to
+have the
+.Sq chat parser
+see the escape character, it is necessary to escape it from the
+.Sq command parser .
+This means that in practice you should use two escapes, for example:
+.Bd -literal -offset indent
+set dial "... ATDT\\\\T CONNECT"
+.Ed
+.Pp
+It is also possible to execute external commands from the chat script.
+To do this, the first character of the expect or send string is an
+exclamation mark
+.Pq Dq !\& .
+If a literal exclamation mark is required, double it up to
+.Dq !!\&
+and it will be treated as a single literal
+.Dq !\& .
+When the command is executed, standard input and standard output are
+directed to the open device (see the
+.Dq set device
+command), and standard error is read by
+.Nm
+and substituted as the expect or send string.
+If
+.Nm
+is running in interactive mode, file descriptor 3 is attached to
+.Pa /dev/tty .
+.Pp
+For example (wrapped for readability):
+.Bd -literal -offset indent
+set login "TIMEOUT 5 \\"\\" \\"\\" login:--login: ppp \e
+word: ppp \\"!sh \\\\-c \\\\\\"echo \\\\-n label: >&2\\\\\\"\\" \e
+\\"!/bin/echo in\\" HELLO"
+.Ed
+.Pp
+would result in the following chat sequence (output using the
+.Sq set log local chat
+command before dialing):
+.Bd -literal -offset indent
+Dial attempt 1 of 1
+dial OK!
+Chat: Expecting:
+Chat: Sending:
+Chat: Expecting: login:--login:
+Chat: Wait for (5): login:
+Chat: Sending: ppp
+Chat: Expecting: word:
+Chat: Wait for (5): word:
+Chat: Sending: ppp
+Chat: Expecting: !sh \\-c "echo \\-n label: >&2"
+Chat: Exec: sh -c "echo -n label: >&2"
+Chat: Wait for (5): !sh \\-c "echo \\-n label: >&2" --> label:
+Chat: Exec: /bin/echo in
+Chat: Sending:
+Chat: Expecting: HELLO
+Chat: Wait for (5): HELLO
+login OK!
+.Ed
+.Pp
+Note (again) the use of the escape character, allowing many levels of
+nesting.
+Here, there are four parsers at work.
+The first parses the original line, reading it as three arguments.
+The second parses the third argument, reading it as 11 arguments.
+At this point, it is
+important that the
+.Dq \&-
+signs are escaped, otherwise this parser will see them as constituting
+an expect-send-expect sequence.
+When the
+.Dq !\&
+character is seen, the execution parser reads the first command as three
+arguments, and then
+.Xr sh 1
+itself expands the argument after the
+.Fl c .
+As we wish to send the output back to the modem, in the first example
+we redirect our output to file descriptor 2 (stderr) so that
+.Nm
+itself sends and logs it, and in the second example, we just output to stdout,
+which is attached directly to the modem.
+.Pp
+This, of course means that it is possible to execute an entirely external
+.Dq chat
+command rather than using the internal one.
+See
+.Xr chat 8
+for a good alternative.
+.Pp
+The external command that is executed is subjected to the same special
+word expansions as the
+.Dq !bg
+command.
+.It set enddisc Op label|IP|MAC|magic|psn value
+This command sets our local endpoint discriminator.
+If set prior to LCP negotiation, and if no
+.Dq disable enddisc
+command has been used,
+.Nm
+will send the information to the peer using the LCP endpoint discriminator
+option.
+The following discriminators may be set:
+.Bl -tag -width indent
+.It Li label
+The current label is used.
+.It Li IP
+Our local IP number is used.
+As LCP is negotiated prior to IPCP, it is
+possible that the IPCP layer will subsequently change this value.
+If
+it does, the endpoint discriminator stays at the old value unless manually
+reset.
+.It Li MAC
+This is similar to the
+.Ar IP
+option above, except that the MAC address associated with the local IP
+number is used.
+If the local IP number is not resident on any Ethernet
+interface, the command will fail.
+.Pp
+As the local IP number defaults to whatever the machine host name is,
+.Dq set enddisc mac
+is usually done prior to any
+.Dq set ifaddr
+commands.
+.It Li magic
+A 20 digit random number is used.
+Care should be taken when using magic numbers as restarting
+.Nm
+or creating a link using a different
+.Nm
+invocation will also use a different magic number and will therefore not
+be recognised by the peer as belonging to the same bundle.
+This makes it unsuitable for
+.Fl direct
+connections.
+.It Li psn Ar value
+The given
+.Ar value
+is used.
+.Ar Value
+should be set to an absolute public switched network number with the
+country code first.
+.El
+.Pp
+If no arguments are given, the endpoint discriminator is reset.
+.It set escape Ar value...
+This option is similar to the
+.Dq set accmap
+option above.
+It allows the user to specify a set of characters that will be
+.Sq escaped
+as they travel across the link.
+.It set filter dial|alive|in|out Ar rule-no Xo
+.No permit|deny|clear| Ns Ar rule-no
+.Op !\&
+.Oo Op host
+.Ar src_addr Ns Op / Ns Ar width
+.Op Ar dst_addr Ns Op / Ns Ar width
+.Oc [ Ns Ar proto
+.Op src lt|eq|gt Ar port
+.Op dst lt|eq|gt Ar port
+.Op estab
+.Op syn
+.Op finrst
+.Op timeout Ar secs ]
+.Xc
+.Nm
+supports four filter sets.
+The
+.Em alive
+filter specifies packets that keep the connection alive - resetting the
+idle timer.
+The
+.Em dial
+filter specifies packets that cause
+.Nm
+to dial when in
+.Fl auto
+mode.
+The
+.Em in
+filter specifies packets that are allowed to travel
+into the machine and the
+.Em out
+filter specifies packets that are allowed out of the machine.
+.Pp
+Filtering is done prior to any IP alterations that might be done by the
+NAT engine on outgoing packets and after any IP alterations that might
+be done by the NAT engine on incoming packets.
+By default all empty filter sets allow all packets to pass.
+Rules are processed in order according to
+.Ar rule-no
+(unless skipped by specifying a rule number as the
+.Ar action ) .
+Up to 40 rules may be given for each set.
+If a packet does not match
+any of the rules in a given set, it is discarded.
+In the case of
+.Em in
+and
+.Em out
+filters, this means that the packet is dropped.
+In the case of
+.Em alive
+filters it means that the packet will not reset the idle timer (even if
+the
+.Ar in Ns No / Ns Ar out
+filter has a
+.Dq timeout
+value) and in the case of
+.Em dial
+filters it means that the packet will not trigger a dial.
+A packet failing to trigger a dial will be dropped rather than queued.
+Refer to the
+section on
+.Sx PACKET FILTERING
+above for further details.
+.It set hangup Ar chat-script
+This specifies the chat script that will be used to reset the device
+before it is closed.
+It should not normally be necessary, but can
+be used for devices that fail to reset themselves properly on close.
+.It set help|? Op Ar command
+This command gives a summary of available set commands, or if
+.Ar command
+is specified, the command usage is shown.
+.It set ifaddr Oo Ar myaddr Ns
+.Op / Ns Ar \&nn
+.Oo Ar hisaddr Ns Op / Ns Ar \&nn
+.Oo Ar netmask
+.Op Ar triggeraddr
+.Oc Oc
+.Oc
+This command specifies the IP addresses that will be used during
+IPCP negotiation.
+Addresses are specified using the format
+.Pp
+.Dl a.b.c.d/nn
+.Pp
+Where
+.Dq a.b.c.d
+is the preferred IP, but
+.Ar nn
+specifies how many bits of the address we will insist on.
+If
+.No / Ns Ar nn
+is omitted, it defaults to
+.Dq /32
+unless the IP address is 0.0.0.0 in which case it defaults to
+.Dq /0 .
+.Pp
+If you wish to assign a dynamic IP number to the peer,
+.Ar hisaddr
+may also be specified as a range of IP numbers in the format
+.Bd -ragged -offset indent
+.Ar \&IP Ns Oo \&- Ns Ar \&IP Ns Xo
+.Oc Ns Oo , Ns Ar \&IP Ns
+.Op \&- Ns Ar \&IP Ns
+.Oc Ns ...
+.Xc
+.Ed
+.Pp
+for example:
+.Pp
+.Dl set ifaddr 10.0.0.1 10.0.1.2-10.0.1.10,10.0.1.20
+.Pp
+will only negotiate
+.Dq 10.0.0.1
+as the local IP number, but may assign any of the given 10 IP
+numbers to the peer.
+If the peer requests one of these numbers,
+and that number is not already in use,
+.Nm
+will grant the peers request.
+This is useful if the peer wants
+to re-establish a link using the same IP number as was previously
+allocated (thus maintaining any existing tcp or udp connections).
+.Pp
+If the peer requests an IP number that is either outside
+of this range or is already in use,
+.Nm
+will suggest a random unused IP number from the range.
+.Pp
+If
+.Ar triggeraddr
+is specified, it is used in place of
+.Ar myaddr
+in the initial IPCP negotiation.
+However, only an address in the
+.Ar myaddr
+range will be accepted.
+This is useful when negotiating with some
+.Dv PPP
+implementations that will not assign an IP number unless their peer
+requests
+.Dq 0.0.0.0 .
+.Pp
+It should be noted that in
+.Fl auto
+mode,
+.Nm
+will configure the interface immediately upon reading the
+.Dq set ifaddr
+line in the config file.
+In any other mode, these values are just
+used for IPCP negotiations, and the interface is not configured
+until the IPCP layer is up.
+.Pp
+Note that the
+.Ar HISADDR
+argument may be overridden by the third field in the
+.Pa ppp.secret
+file once the client has authenticated itself
+(if PAP or CHAP are
+.Dq enabled ) .
+Refer to the
+.Sx AUTHENTICATING INCOMING CONNECTIONS
+section for details.
+.Pp
+In all cases, if the interface is already configured,
+.Nm
+will try to maintain the interface IP numbers so that any existing
+bound sockets will remain valid.
+.It set ifqueue Ar packets
+Set the maximum number of packets that
+.Nm
+will read from the tunnel interface while data cannot be sent to any of
+the available links.
+This queue limit is necessary to flow control outgoing data as the tunnel
+interface is likely to be far faster than the combined links available to
+.Nm .
+.Pp
+If
+.Ar packets
+is set to a value less than the number of links,
+.Nm
+will read up to that value regardless.
+This prevents any possible latency problems.
+.Pp
+The default value for
+.Ar packets
+is
+.Dq 30 .
+.It set ccpretry|ccpretries Oo Ar timeout
+.Op Ar reqtries Op Ar trmtries
+.Oc
+.It set chapretry|chapretries Oo Ar timeout
+.Op Ar reqtries
+.Oc
+.It set ipcpretry|ipcpretries Oo Ar timeout
+.Op Ar reqtries Op Ar trmtries
+.Oc
+.It set ipv6cpretry|ipv6cpretries Oo Ar timeout
+.Op Ar reqtries Op Ar trmtries
+.Oc
+.It set lcpretry|lcpretries Oo Ar timeout
+.Op Ar reqtries Op Ar trmtries
+.Oc
+.It set papretry|papretries Oo Ar timeout
+.Op Ar reqtries
+.Oc
+These commands set the number of seconds that
+.Nm
+will wait before resending Finite State Machine (FSM) Request packets.
+The default
+.Ar timeout
+for all FSMs is 3 seconds (which should suffice in most cases).
+.Pp
+If
+.Ar reqtries
+is specified, it tells
+.Nm
+how many configuration request attempts it should make while receiving
+no reply from the peer before giving up.
+The default is 5 attempts for
+CCP, LCP and IPCP and 3 attempts for PAP and CHAP.
+.Pp
+If
+.Ar trmtries
+is specified, it tells
+.Nm
+how many terminate requests should be sent before giving up waiting for the
+peers response.
+The default is 3 attempts.
+Authentication protocols are
+not terminated and it is therefore invalid to specify
+.Ar trmtries
+for PAP or CHAP.
+.Pp
+In order to avoid negotiations with the peer that will never converge,
+.Nm
+will only send at most 3 times the configured number of
+.Ar reqtries
+in any given negotiation session before giving up and closing that layer.
+.It set log Xo
+.Op local
+.Op +|- Ns
+.Ar value Ns No ...
+.Xc
+This command allows the adjustment of the current log level.
+Refer to the Logging Facility section for further details.
+.It set login Ar chat-script
+This
+.Ar chat-script
+compliments the dial-script.
+If both are specified, the login
+script will be executed after the dial script.
+Escape sequences available in the dial script are also available here.
+.It set logout Ar chat-script
+This specifies the chat script that will be used to logout
+before the hangup script is called.
+It should not normally be necessary.
+.It set lqrperiod|echoperiod Ar frequency
+This command sets the
+.Ar frequency
+in seconds at which
+.Em LQR
+or
+.Em LCP ECHO
+packets are sent.
+The default is 30 seconds.
+You must also use the
+.Dq enable lqr
+and/or
+.Dq enable echo
+commands if you wish to send
+.Em LQR
+or
+.Em LCP ECHO
+requests to the peer.
+.It set mode Ar interactive|auto|ddial|background
+This command allows you to change the
+.Sq mode
+of the specified link.
+This is normally only useful in multi-link mode,
+but may also be used in uni-link mode.
+.Pp
+It is not possible to change a link that is
+.Sq direct
+or
+.Sq dedicated .
+.Pp
+Note: If you issue the command
+.Dq set mode auto ,
+and have network address translation enabled, it may be useful to
+.Dq enable iface-alias
+afterwards.
+This will allow
+.Nm
+to do the necessary address translations to enable the process that
+triggers the connection to connect once the link is up despite the
+peer assigning us a new (dynamic) IP address.
+.It set mppe Op 40|56|128|* Op stateless|stateful|*
+This option selects the encryption parameters used when negotiation
+MPPE.
+MPPE can be disabled entirely with the
+.Dq disable mppe
+command.
+If no arguments are given,
+.Nm
+will attempt to negotiate a stateful link with a 128 bit key, but
+will agree to whatever the peer requests (including no encryption
+at all).
+.Pp
+If any arguments are given,
+.Nm
+will
+.Em insist
+on using MPPE and will close the link if it is rejected by the peer (Note;
+this behaviour can be overridden by a configured RADIUS server).
+.Pp
+The first argument specifies the number of bits that
+.Nm
+should insist on during negotiations and the second specifies whether
+.Nm
+should insist on stateful or stateless mode.
+In stateless mode, the
+encryption dictionary is re-initialised with every packet according to
+an encryption key that is changed with every packet.
+In stateful mode,
+the encryption dictionary is re-initialised every 256 packets or after
+the loss of any data and the key is changed every 256 packets.
+Stateless mode is less efficient but is better for unreliable transport
+layers.
+.It set mrru Op Ar value
+Setting this option enables Multi-link PPP negotiations, also known as
+Multi-link Protocol or MP.
+There is no default MRRU (Maximum Reconstructed Receive Unit) value.
+If no argument is given, multi-link mode is disabled.
+.It set mru Xo
+.Op max Ns Op imum
+.Op Ar value
+.Xc
+The default MRU (Maximum Receive Unit) is 1500.
+If it is increased, the other side *may* increase its MTU.
+In theory there is no point in decreasing the MRU to below the default as the
+.Em PPP
+protocol says implementations *must* be able to accept packets of at
+least 1500 octets.
+.Pp
+If the
+.Dq maximum
+keyword is used,
+.Nm
+will refuse to negotiate a higher value.
+The maximum MRU can be set to 2048 at most.
+Setting a maximum of less than 1500 violates the
+.Em PPP
+rfc, but may sometimes be necessary.
+For example,
+.Em PPPoE
+imposes a maximum of 1492 due to hardware limitations.
+.Pp
+If no argument is given, 1500 is assumed.
+A value must be given when
+.Dq maximum
+is specified.
+.It set mtu Xo
+.Op max Ns Op imum
+.Op Ar value
+.Xc
+The default MTU is 1500.
+At negotiation time,
+.Nm
+will accept whatever MRU the peer requests (assuming it is
+not less than 296 bytes or greater than the assigned maximum).
+If the MTU is set,
+.Nm
+will not accept MRU values less than
+.Ar value .
+When negotiations are complete, the MTU is used when writing to the
+interface, even if the peer requested a higher value MRU.
+This can be useful for
+limiting your packet size (giving better bandwidth sharing at the expense
+of more header data).
+.Pp
+If the
+.Dq maximum
+keyword is used,
+.Nm
+will refuse to negotiate a higher value.
+The maximum MTU can be set to 2048 at most.
+Note, it is necessary to use the
+.Dq maximum
+keyword to limit the MTU when using PPPoE.
+.Pp
+If no
+.Ar value
+is given, 1500, or whatever the peer asks for is used.
+A value must be given when
+.Dq maximum
+is specified.
+.It set nbns Op Ar x.x.x.x Op Ar y.y.y.y
+This option allows the setting of the Microsoft NetBIOS name server
+values to be returned at the peers request.
+If no values are given,
+.Nm
+will reject any such requests.
+.It set openmode active|passive Op Ar delay
+By default,
+.Ar openmode
+is always
+.Ar active
+with a one second
+.Ar delay .
+That is,
+.Nm
+will always initiate LCP/IPCP/CCP negotiation one second after the line
+comes up.
+If you want to wait for the peer to initiate negotiations, you
+can use the value
+.Ar passive .
+If you want to initiate negotiations immediately or after more than one
+second, the appropriate
+.Ar delay
+may be specified here in seconds.
+.It set parity odd|even|none|mark
+This allows the line parity to be set.
+The default value is
+.Ar none .
+.It set phone Ar telno Ns Xo
+.Oo \&| Ns Ar backupnumber
+.Oc Ns ... Ns Oo : Ns Ar nextnumber
+.Oc Ns ...
+.Xc
+This allows the specification of the phone number to be used in
+place of the \\\\T string in the dial and login chat scripts.
+Multiple phone numbers may be given separated either by a pipe
+.Pq Dq \&|
+or a colon
+.Pq Dq \&: .
+.Pp
+Numbers after the pipe are only dialed if the dial or login
+script for the previous number failed.
+.Pp
+Numbers after the colon are tried sequentially, irrespective of
+the reason the line was dropped.
+.Pp
+If multiple numbers are given,
+.Nm
+will dial them according to these rules until a connection is made, retrying
+the maximum number of times specified by
+.Dq set redial
+below.
+In
+.Fl background
+mode, each number is attempted at most once.
+.It set pppoe Op standard|3Com
+This option configures the underlying
+.Xr ng_pppoe 4
+node to either standard RFC2516 PPPoE or proprietary 3Com mode.
+If not set the system default will be used.
+.It set Op proc Ns Xo
+.No title Op Ar value
+.Xc
+The current process title as displayed by
+.Xr ps 1
+is changed according to
+.Ar value .
+If
+.Ar value
+is not specified, the original process title is restored.
+All the
+word replacements done by the shell commands (see the
+.Dq bg
+command above) are done here too.
+.Pp
+Note, if USER is required in the process title, the
+.Dq set proctitle
+command must appear in
+.Pa ppp.linkup ,
+as it is not known when the commands in
+.Pa ppp.conf
+are executed.
+.It set radius Op Ar config-file
+This command enables RADIUS support (if it is compiled in).
+.Ar config-file
+refers to the radius client configuration file as described in
+.Xr radius.conf 5 .
+If PAP, CHAP, MSCHAP or MSCHAPv2 are
+.Dq enable Ns No d ,
+.Nm
+behaves as a
+.Em \&N Ns No etwork
+.Em \&A Ns No ccess
+.Em \&S Ns No erver
+and uses the configured RADIUS server to authenticate rather than
+authenticating from the
+.Pa ppp.secret
+file or from the passwd database.
+.Pp
+If none of PAP, CHAP, MSCHAP or MSCHAPv2 are enabled,
+.Dq set radius
+will do nothing.
+.Pp
+.Nm
+uses the following attributes from the RADIUS reply:
+.Bl -tag -width XXX -offset XXX
+.It RAD_FRAMED_IP_ADDRESS
+The peer IP address is set to the given value.
+.It RAD_FRAMED_IP_NETMASK
+The tun interface netmask is set to the given value.
+.It RAD_FRAMED_MTU
+If the given MTU is less than the peers MRU as agreed during LCP
+negotiation, *and* it is less that any configured MTU (see the
+.Dq set mru
+command), the tun interface MTU is set to the given value.
+.It RAD_FRAMED_COMPRESSION
+If the received compression type is
+.Dq 1 ,
+.Nm
+will request VJ compression during IPCP negotiations despite any
+.Dq disable vj
+configuration command.
+.It RAD_FILTER_ID
+If this attribute is supplied,
+.Nm
+will attempt to use it as an additional label to load from the
+.Pa ppp.linkup
+and
+.Pa ppp.linkdown
+files.
+The load will be attempted before (and in addition to) the normal
+label search.
+If the label does not exist, no action is taken and
+.Nm
+proceeds to the normal load using the current label.
+.It RAD_FRAMED_ROUTE
+The received string is expected to be in the format
+.Ar dest Ns Op / Ns Ar bits
+.Ar gw
+.Op Ar metrics .
+Any specified metrics are ignored.
+.Dv MYADDR
+and
+.Dv HISADDR
+are understood as valid values for
+.Ar dest
+and
+.Ar gw ,
+.Dq default
+can be used for
+.Ar dest
+to sepcify the default route, and
+.Dq 0.0.0.0
+is understood to be the same as
+.Dq default
+for
+.Ar dest
+and
+.Dv HISADDR
+for
+.Ar gw .
+.Pp
+For example, a returned value of
+.Dq 1.2.3.4/24 0.0.0.0 1 2 -1 3 400
+would result in a routing table entry to the 1.2.3.0/24 network via
+.Dv HISADDR
+and a returned value of
+.Dq 0.0.0.0 0.0.0.0
+or
+.Dq default HISADDR
+would result in a default route to
+.Dv HISADDR .
+.Pp
+All RADIUS routes are applied after any sticky routes are applied, making
+RADIUS routes override configured routes.
+This also applies for RADIUS routes that do not {include} the
+.Dv MYADDR
+or
+.Dv HISADDR
+keywords.
+.Pp
+.It RAD_FRAMED_IPV6_PREFIX
+If this attribute is supplied, the value is substituted for IPV6PREFIX
+in a command.
+You may pass it to such as DHCPv6 for delegating an
+IPv6 prefix to a peer.
+.It RAD_FRAMED_IPV6_ROUTE
+The received string is expected to be in the format
+.Ar dest Ns Op / Ns Ar bits
+.Ar gw
+.Op Ar metrics .
+Any specified metrics are ignored.
+.Dv MYADDR6
+and
+.Dv HISADDR6
+are understood as valid values for
+.Ar dest
+and
+.Ar gw ,
+.Dq default
+can be used for
+.Ar dest
+to sepcify the default route, and
+.Dq ::
+is understood to be the same as
+.Dq default
+for
+.Ar dest
+and
+.Dv HISADDR6
+for
+.Ar gw .
+.Pp
+For example, a returned value of
+.Dq 3ffe:505:abcd::/48 ::
+would result in a routing table entry to the 3ffe:505:abcd::/48 network via
+.Dv HISADDR6
+and a returned value of
+.Dq :: ::
+or
+.Dq default HISADDR6
+would result in a default route to
+.Dv HISADDR6 .
+.Pp
+All RADIUS IPv6 routes are applied after any sticky routes are
+applied, making RADIUS IPv6 routes override configured routes.
+This
+also applies for RADIUS IPv6 routes that do not {include} the
+.Dv MYADDR6
+or
+.Dv HISADDR6
+keywords.
+.Pp
+.It RAD_SESSION_TIMEOUT
+If supplied, the client connection is closed after the given number of
+seconds.
+.It RAD_REPLY_MESSAGE
+If supplied, this message is passed back to the peer as the authentication
+SUCCESS text.
+.It RAD_MICROSOFT_MS_CHAP_ERROR
+If this
+.Dv RAD_VENDOR_MICROSOFT
+vendor specific attribute is supplied, it is passed back to the peer as the
+authentication FAILURE text.
+.It RAD_MICROSOFT_MS_CHAP2_SUCCESS
+If this
+.Dv RAD_VENDOR_MICROSOFT
+vendor specific attribute is supplied and if MS-CHAPv2 authentication is
+being used, it is passed back to the peer as the authentication SUCCESS text.
+.It RAD_MICROSOFT_MS_MPPE_ENCRYPTION_POLICY
+If this
+.Dv RAD_VENDOR_MICROSOFT
+vendor specific attribute is supplied and has a value of 2 (Required),
+.Nm
+will insist that MPPE encryption is used (even if no
+.Dq set mppe
+configuration command has been given with arguments).
+If it is supplied with a value of 1 (Allowed), encryption is made optional
+(despite any
+.Dq set mppe
+configuration commands with arguments).
+.It RAD_MICROSOFT_MS_MPPE_ENCRYPTION_TYPES
+If this
+.Dv RAD_VENDOR_MICROSOFT
+vendor specific attribute is supplied, bits 1 and 2 are examined.
+If either or both are set, 40 bit and/or 128 bit (respectively) encryption
+options are set, overriding any given first argument to the
+.Dq set mppe
+command.
+Note, it is not currently possible for the RADIUS server to specify 56 bit
+encryption.
+.It RAD_MICROSOFT_MS_MPPE_RECV_KEY
+If this
+.Dv RAD_VENDOR_MICROSOFT
+vendor specific attribute is supplied, it is value is used as the master
+key for decryption of incoming data.
+When clients are authenticated using
+MSCHAPv2, the RADIUS server MUST provide this attribute if inbound MPPE is
+to function.
+.It RAD_MICROSOFT_MS_MPPE_SEND_KEY
+If this
+.Dv RAD_VENDOR_MICROSOFT
+vendor specific attribute is supplied, it is value is used as the master
+key for encryption of outgoing data.
+When clients are authenticated using
+MSCHAPv2, the RADIUS server MUST provide this attribute if outbound MPPE is
+to function.
+.El
+.Pp
+Values received from the RADIUS server may be viewed using
+.Dq show bundle .
+.It set rad_alive Ar timeout
+When RADIUS is configured, setting
+.Dq rad_alive
+to a non-zero
+.Ar timeout
+value will tell
+.Nm
+to sent RADIUS accounting information to the RADIUS server every
+.Ar timeout
+seconds.
+.It set rad_port_id Ar option
+When RADIUS is configured, setting the
+.Dq rad_port_id
+value allows to specify what should be sent to the RADIUS server as
+NAS-Port-Id.
+The
+.Ar option Ns No s
+are as follows:
+.Pp
+.Bl -tag -width Ds
+.It pid
+PID of the corresponding tunnel.
+.It tunnum
+.Xr tun 4
+interface number.
+.It ifnum
+index of the interface as returned by
+.Xr if_nametoindex 3 .
+.It default
+keeps the default behavior.
+.El
+.It set reconnect Ar timeout ntries
+Should the line drop unexpectedly (due to loss of CD or LQR
+failure), a connection will be re-established after the given
+.Ar timeout .
+The line will be re-connected at most
+.Ar ntries
+times.
+.Ar Ntries
+defaults to zero.
+A value of
+.Ar random
+for
+.Ar timeout
+will result in a variable pause, somewhere between 1 and 30 seconds.
+.It set recvpipe Op Ar value
+This sets the routing table RECVPIPE value.
+The optimum value is just over twice the MTU value.
+If
+.Ar value
+is unspecified or zero, the default kernel controlled value is used.
+.It set redial Ar secs Ns Xo
+.Oo + Ns Ar inc Ns
+.Op - Ns Ar max Ns
+.Oc Ns Op . Ns Ar next
+.Op Ar attempts
+.Xc
+.Nm
+can be instructed to attempt to redial
+.Ar attempts
+times.
+If more than one phone number is specified (see
+.Dq set phone
+above), a pause of
+.Ar next
+is taken before dialing each number.
+A pause of
+.Ar secs
+is taken before starting at the first number again.
+A literal value of
+.Dq Li random
+may be used here in place of
+.Ar secs
+and
+.Ar next ,
+causing a random delay of between 1 and 30 seconds.
+.Pp
+If
+.Ar inc
+is specified, its value is added onto
+.Ar secs
+each time
+.Nm
+tries a new number.
+.Ar secs
+will only be incremented at most
+.Ar max
+times.
+.Ar max
+defaults to 10.
+.Pp
+Note, the
+.Ar secs
+delay will be effective, even after
+.Ar attempts
+has been exceeded, so an immediate manual dial may appear to have
+done nothing.
+If an immediate dial is required, a
+.Dq !\&
+should immediately follow the
+.Dq open
+keyword.
+See the
+.Dq open
+description above for further details.
+.It set sendpipe Op Ar value
+This sets the routing table SENDPIPE value.
+The optimum value is just over twice the MTU value.
+If
+.Ar value
+is unspecified or zero, the default kernel controlled value is used.
+.It "set server|socket" Ar TcpPort Ns No \&| Ns Xo
+.Ar LocalName Ns No |none|open|closed
+.Op password Op Ar mask
+.Xc
+This command tells
+.Nm
+to listen on the given socket or
+.Sq diagnostic port
+for incoming command connections.
+.Pp
+The word
+.Dq none
+instructs
+.Nm
+to close any existing socket and clear the socket configuration.
+The word
+.Dq open
+instructs
+.Nm
+to attempt to re-open the port.
+The word
+.Dq closed
+instructs
+.Nm
+to close the open port.
+.Pp
+If you wish to specify a local domain socket,
+.Ar LocalName
+must be specified as an absolute file name, otherwise it is assumed
+to be the name or number of a TCP port.
+You may specify the octal umask to be used with a local domain socket.
+Refer to
+.Xr umask 2
+for umask details.
+Refer to
+.Xr services 5
+for details of how to translate TCP port names.
+.Pp
+You must also specify the password that must be entered by the client
+(using the
+.Dq passwd
+variable above) when connecting to this socket.
+If the password is
+specified as an empty string, no password is required for connecting clients.
+.Pp
+When specifying a local domain socket, the first
+.Dq %d
+sequence found in the socket name will be replaced with the current
+interface unit number.
+This is useful when you wish to use the same
+profile for more than one connection.
+.Pp
+In a similar manner TCP sockets may be prefixed with the
+.Dq +
+character, in which case the current interface unit number is added to
+the port number.
+.Pp
+When using
+.Nm
+with a server socket, the
+.Xr pppctl 8
+command is the preferred mechanism of communications.
+Currently,
+.Xr telnet 1
+can also be used, but link encryption may be implemented in the future, so
+.Xr telnet 1
+should be avoided.
+.Pp
+Note;
+.Dv SIGUSR1
+and
+.Dv SIGUSR2
+interact with the diagnostic socket.
+.It set speed Ar value
+This sets the speed of the serial device.
+If speed is specified as
+.Dq sync ,
+.Nm
+treats the device as a synchronous device.
+.Pp
+Certain device types will know whether they should be specified as
+synchronous or asynchronous.
+These devices will override incorrect
+settings and log a warning to this effect.
+.It set stopped Op Ar LCPseconds Op Ar CCPseconds
+If this option is set,
+.Nm
+will time out after the given FSM (Finite State Machine) has been in
+the stopped state for the given number of
+.Dq seconds .
+This option may be useful if the peer sends a terminate request,
+but never actually closes the connection despite our sending a terminate
+acknowledgement.
+This is also useful if you wish to
+.Dq set openmode passive
+and time out if the peer does not send a Configure Request within the
+given time.
+Use
+.Dq set log +lcp +ccp
+to make
+.Nm
+log the appropriate state transitions.
+.Pp
+The default value is zero, where
+.Nm
+does not time out in the stopped state.
+.Pp
+This value should not be set to less than the openmode delay (see
+.Dq set openmode
+above).
+.It set timeout Ar idleseconds Op Ar mintimeout
+This command allows the setting of the idle timer.
+Refer to the section titled
+.Sx SETTING THE IDLE TIMER
+for further details.
+.Pp
+If
+.Ar mintimeout
+is specified,
+.Nm
+will never idle out before the link has been up for at least that number
+of seconds.
+.It set urgent Xo
+.Op tcp|udp|none
+.Oo Op +|- Ns
+.Ar port
+.Oc No ...
+.Xc
+This command controls the ports that
+.Nm
+prioritizes when transmitting data.
+The default priority TCP ports
+are ports 21 (ftp control), 22 (ssh), 23 (telnet), 513 (login), 514 (shell),
+543 (klogin) and 544 (kshell).
+There are no priority UDP ports by default.
+See
+.Xr services 5
+for details.
+.Pp
+If neither
+.Dq tcp
+or
+.Dq udp
+are specified,
+.Dq tcp
+is assumed.
+.Pp
+If no
+.Ar port Ns No s
+are given, the priority port lists are cleared (although if
+.Dq tcp
+or
+.Dq udp
+is specified, only that list is cleared).
+If the first
+.Ar port
+argument is prefixed with a plus
+.Pq Dq \&+
+or a minus
+.Pq Dq \&- ,
+the current list is adjusted, otherwise the list is reassigned.
+.Ar port Ns No s
+prefixed with a plus or not prefixed at all are added to the list and
+.Ar port Ns No s
+prefixed with a minus are removed from the list.
+.Pp
+If
+.Dq none
+is specified, all priority port lists are disabled and even
+.Dv IPTOS_LOWDELAY
+packets are not prioritised.
+.It set vj slotcomp on|off
+This command tells
+.Nm
+whether it should attempt to negotiate VJ slot compression.
+By default, slot compression is turned
+.Ar on .
+.It set vj slots Ar nslots
+This command sets the initial number of slots that
+.Nm
+will try to negotiate with the peer when VJ compression is enabled (see the
+.Sq enable
+command above).
+It defaults to a value of 16.
+.Ar Nslots
+must be between
+.Ar 4
+and
+.Ar 16
+inclusive.
+.El
+.Pp
+.It shell|! Op Ar command
+If
+.Ar command
+is not specified a shell is invoked according to the
+.Dv SHELL
+environment variable.
+Otherwise, the given
+.Ar command
+is executed.
+Word replacement is done in the same way as for the
+.Dq !bg
+command as described above.
+.Pp
+Use of the !\& character
+requires a following space as with any of the other commands.
+You should note that this command is executed in the foreground;
+.Nm
+will not continue running until this process has exited.
+Use the
+.Dv bg
+command if you wish processing to happen in the background.
+.It show Ar var
+This command allows the user to examine the following:
+.Bl -tag -width 2n
+.It show bundle
+Show the current bundle settings.
+.It show ccp
+Show the current CCP compression statistics.
+.It show compress
+Show the current VJ compression statistics.
+.It show escape
+Show the current escape characters.
+.It show filter Op Ar name
+List the current rules for the given filter.
+If
+.Ar name
+is not specified, all filters are shown.
+.It show hdlc
+Show the current HDLC statistics.
+.It show help|?
+Give a summary of available show commands.
+.It show iface
+Show the current interface information
+(the same as
+.Dq iface show ) .
+.It show ipcp
+Show the current IPCP statistics.
+.It show layers
+Show the protocol layers currently in use.
+.It show lcp
+Show the current LCP statistics.
+.It show Op data Ns Xo
+.No link
+.Xc
+Show high level link information.
+.It show links
+Show a list of available logical links.
+.It show log
+Show the current log values.
+.It show mem
+Show current memory statistics.
+.It show ncp
+Show the current NCP statistics.
+.It show physical
+Show low level link information.
+.It show mp
+Show Multi-link information.
+.It show proto
+Show current protocol totals.
+.It show route
+Show the current routing tables.
+.It show stopped
+Show the current stopped timeouts.
+.It show timer
+Show the active alarm timers.
+.It show version
+Show the current version number of
+.Nm .
+.El
+.Pp
+.It term
+Go into terminal mode.
+Characters typed at the keyboard are sent to the device.
+Characters read from the device are displayed on the screen.
+When a remote
+.Em PPP
+peer is detected,
+.Nm
+automatically enables Packet Mode and goes back into command mode.
+.El
+.Sh MORE DETAILS
+.Bl -bullet
+.It
+Read the example configuration files.
+They are a good source of information.
+.It
+Use
+.Dq help ,
+.Dq nat \&? ,
+.Dq enable \&? ,
+.Dq set ?\&
+and
+.Dq show ?\&
+to get online information about what is available.
+.It
+The following URLs contain useful information:
+.Bl -bullet -compact
+.It
+http://www.FreeBSD.org/doc/en_US.ISO8859-1/books/faq/ppp.html
+.It
+http://www.FreeBSD.org/doc/handbook/userppp.html
+.El
+.Pp
+.El
+.Sh FILES
+.Nm
+refers to four files:
+.Pa ppp.conf ,
+.Pa ppp.linkup ,
+.Pa ppp.linkdown
+and
+.Pa ppp.secret .
+These files are placed in the
+.Pa /etc/ppp
+directory.
+.Bl -tag -width 2n
+.It Pa /etc/ppp/ppp.conf
+System default configuration file.
+.It Pa /etc/ppp/ppp.secret
+An authorisation file for each system.
+.It Pa /etc/ppp/ppp.linkup
+A file to check when
+.Nm
+establishes a network level connection.
+.It Pa /etc/ppp/ppp.linkdown
+A file to check when
+.Nm
+closes a network level connection.
+.It Pa /var/log/ppp.log
+Logging and debugging information file.
+Note, this name is specified in
+.Pa /etc/syslog.conf .
+See
+.Xr syslog.conf 5
+for further details.
+.It Pa /var/spool/lock/LCK..*
+tty port locking file.
+Refer to
+.Xr uucplock 3
+for further details.
+.It Pa /var/run/tunN.pid
+The process id (pid) of the
+.Nm
+program connected to the tunN device, where
+.Sq N
+is the number of the device.
+.It Pa /var/run/ttyXX.if
+The tun interface used by this port.
+Again, this file is only created in
+.Fl background ,
+.Fl auto
+and
+.Fl ddial
+modes.
+.It Pa /etc/services
+Get port number if port number is using service name.
+.It Pa /var/run/ppp-authname-class-value
+In multi-link mode, local domain sockets are created using the peer
+authentication name
+.Pq Sq authname ,
+the peer endpoint discriminator class
+.Pq Sq class
+and the peer endpoint discriminator value
+.Pq Sq value .
+As the endpoint discriminator value may be a binary value, it is turned
+to HEX to determine the actual file name.
+.Pp
+This socket is used to pass links between different instances of
+.Nm .
+.El
+.Sh SEE ALSO
+.Xr at 1 ,
+.Xr ftp 1 ,
+.Xr gzip 1 ,
+.Xr hostname 1 ,
+.Xr login 1 ,
+.Xr tcpdump 1 ,
+.Xr telnet 1 ,
+.Xr kldload 2 ,
+ifdef({LOCALNAT},{},{.Xr libalias 3 ,
+})dnl
+ifdef({LOCALRAD},{},{.Xr libradius 3 ,
+})dnl
+.Xr syslog 3 ,
+.Xr uucplock 3 ,
+.Xr netgraph 4 ,
+.Xr ng_pppoe 4 ,
+.Xr crontab 5 ,
+.Xr group 5 ,
+.Xr passwd 5 ,
+.Xr protocols 5 ,
+.Xr radius.conf 5 ,
+.Xr resolv.conf 5 ,
+.Xr syslog.conf 5 ,
+.Xr adduser 8 ,
+.Xr chat 8 ,
+.Xr getty 8 ,
+.Xr inetd 8 ,
+.Xr init 8 ,
+.Xr named 8 ,
+.Xr ping 8 ,
+.Xr pppctl 8 ,
+.Xr pppd 8 ,
+.Xr pppoed 8 ,
+.Xr route 8 ,
+.Xr sshd 8 ,
+.Xr syslogd 8 ,
+.Xr traceroute 8 ,
+.Xr vipw 8
+.Sh HISTORY
+This program was originally written by
+.An Toshiharu OHNO Aq tony-o@iij.ad.jp ,
+and was submitted to
+.Fx 2.0.5
+by
+.An Atsushi Murai Aq amurai@spec.co.jp .
+.Pp
+It was substantially modified during 1997 by
+.An Brian Somers Aq brian@Awfulhak.org ,
+and was ported to
+.Ox
+in November that year
+(just after the 2.2 release).
+.Pp
+Most of the code was rewritten by
+.An Brian Somers
+in early 1998 when multi-link ppp support was added.
diff --git a/usr.sbin/ppp/pred.c b/usr.sbin/ppp/pred.c
new file mode 100644
index 0000000..43d5d94
--- /dev/null
+++ b/usr.sbin/ppp/pred.c
@@ -0,0 +1,345 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * Ian Donaldson <iand@labtam.labtam.oz.au>
+ * Carsten Bormann <cabo@cs.tu-berlin.de>
+ * Dave Rand <dlr@bungi.com>/<dave_rand@novell.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "defs.h"
+#include "layer.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "throughput.h"
+#include "link.h"
+#include "pred.h"
+
+/* The following hash code is the heart of the algorithm:
+ * It builds a sliding hash sum of the previous 3-and-a-bit characters
+ * which will be used to index the guess table.
+ * A better hash function would result in additional compression,
+ * at the expense of time.
+ */
+#define HASH(state, x) state->hash = (state->hash << 4) ^ (x)
+#define GUESS_TABLE_SIZE 65536
+
+struct pred1_state {
+ u_short hash;
+ u_char dict[GUESS_TABLE_SIZE];
+};
+
+static int
+compress(struct pred1_state *state, u_char *source, u_char *dest, int len)
+{
+ int i, bitmask;
+ unsigned char *flagdest, flags, *orgdest;
+
+ orgdest = dest;
+ while (len) {
+ flagdest = dest++;
+ flags = 0; /* All guess wrong initially */
+ for (bitmask = 1, i = 0; i < 8 && len; i++, bitmask <<= 1) {
+ if (state->dict[state->hash] == *source) {
+ flags |= bitmask; /* Guess was right - don't output */
+ } else {
+ state->dict[state->hash] = *source;
+ *dest++ = *source; /* Guess wrong, output char */
+ }
+ HASH(state, *source++);
+ len--;
+ }
+ *flagdest = flags;
+ }
+ return (dest - orgdest);
+}
+
+static void
+SyncTable(struct pred1_state *state, u_char *source, u_char *dest, int len)
+{
+ while (len--) {
+ *dest++ = state->dict[state->hash] = *source;
+ HASH(state, *source++);
+ }
+}
+
+static int
+decompress(struct pred1_state *state, u_char *source, u_char *dest, int len)
+{
+ int i, bitmask;
+ unsigned char flags, *orgdest;
+
+ orgdest = dest;
+ while (len) {
+ flags = *source++;
+ len--;
+ for (i = 0, bitmask = 1; i < 8; i++, bitmask <<= 1) {
+ if (flags & bitmask) {
+ *dest = state->dict[state->hash]; /* Guess correct */
+ } else {
+ if (!len)
+ break; /* we seem to be really done -- cabo */
+ state->dict[state->hash] = *source; /* Guess wrong */
+ *dest = *source++; /* Read from source */
+ len--;
+ }
+ HASH(state, *dest++);
+ }
+ }
+ return (dest - orgdest);
+}
+
+static void
+Pred1Term(void *v)
+{
+ struct pred1_state *state = (struct pred1_state *)v;
+ free(state);
+}
+
+static void
+Pred1ResetInput(void *v)
+{
+ struct pred1_state *state = (struct pred1_state *)v;
+ state->hash = 0;
+ memset(state->dict, '\0', sizeof state->dict);
+ log_Printf(LogCCP, "Predictor1: Input channel reset\n");
+}
+
+static int
+Pred1ResetOutput(void *v)
+{
+ struct pred1_state *state = (struct pred1_state *)v;
+ state->hash = 0;
+ memset(state->dict, '\0', sizeof state->dict);
+ log_Printf(LogCCP, "Predictor1: Output channel reset\n");
+
+ return 1; /* Ask FSM to ACK */
+}
+
+static void *
+Pred1InitInput(struct bundle *bundle __unused, struct fsm_opt *o __unused)
+{
+ struct pred1_state *state;
+ state = (struct pred1_state *)malloc(sizeof(struct pred1_state));
+ if (state != NULL)
+ Pred1ResetInput(state);
+ return state;
+}
+
+static void *
+Pred1InitOutput(struct bundle *bundle __unused, struct fsm_opt *o __unused)
+{
+ struct pred1_state *state;
+ state = (struct pred1_state *)malloc(sizeof(struct pred1_state));
+ if (state != NULL)
+ Pred1ResetOutput(state);
+ return state;
+}
+
+static struct mbuf *
+Pred1Output(void *v, struct ccp *ccp, struct link *l __unused,
+ int pri __unused, u_short *proto, struct mbuf *bp)
+{
+ struct pred1_state *state = (struct pred1_state *)v;
+ struct mbuf *mwp;
+ u_char *cp, *wp, *hp;
+ int orglen, len;
+ u_char bufp[MAX_MTU + 2];
+ u_short fcs;
+
+ orglen = m_length(bp) + 2; /* add count of proto */
+ mwp = m_get((orglen + 2) / 8 * 9 + 12, MB_CCPOUT);
+ hp = wp = MBUF_CTOP(mwp);
+ cp = bufp;
+ *wp++ = *cp++ = orglen >> 8;
+ *wp++ = *cp++ = orglen & 0377;
+ *cp++ = *proto >> 8;
+ *cp++ = *proto & 0377;
+ mbuf_Read(bp, cp, orglen - 2);
+ fcs = hdlc_Fcs(bufp, 2 + orglen);
+ fcs = ~fcs;
+
+ len = compress(state, bufp + 2, wp, orglen);
+ log_Printf(LogDEBUG, "Pred1Output: orglen (%d) --> len (%d)\n", orglen, len);
+ ccp->uncompout += orglen;
+ if (len < orglen) {
+ *hp |= 0x80;
+ wp += len;
+ ccp->compout += len;
+ } else {
+ memcpy(wp, bufp + 2, orglen);
+ wp += orglen;
+ ccp->compout += orglen;
+ }
+
+ *wp++ = fcs & 0377;
+ *wp++ = fcs >> 8;
+ mwp->m_len = wp - MBUF_CTOP(mwp);
+ *proto = ccp_Proto(ccp);
+ return mwp;
+}
+
+static struct mbuf *
+Pred1Input(void *v, struct ccp *ccp, u_short *proto, struct mbuf *bp)
+{
+ struct pred1_state *state = (struct pred1_state *)v;
+ u_char *cp, *pp;
+ int len, olen, len1;
+ struct mbuf *wp;
+ u_char *bufp;
+ u_short fcs;
+
+ wp = m_get(MAX_MRU + 2, MB_CCPIN);
+ cp = MBUF_CTOP(bp);
+ olen = m_length(bp);
+ pp = bufp = MBUF_CTOP(wp);
+ *pp++ = *cp & 0177;
+ len = *cp++ << 8;
+ *pp++ = *cp;
+ len += *cp++;
+ ccp->uncompin += len & 0x7fff;
+ if (len & 0x8000) {
+ len1 = decompress(state, cp, pp, olen - 4);
+ ccp->compin += olen;
+ len &= 0x7fff;
+ if (len != len1) { /* Error is detected. Send reset request */
+ log_Printf(LogCCP, "Pred1: Length error (got %d, not %d)\n", len1, len);
+ fsm_Reopen(&ccp->fsm);
+ m_freem(bp);
+ m_freem(wp);
+ return NULL;
+ }
+ cp += olen - 4;
+ pp += len1;
+ } else if (len + 4 != olen) {
+ log_Printf(LogCCP, "Pred1: Length error (got %d, not %d)\n", len + 4, olen);
+ fsm_Reopen(&ccp->fsm);
+ m_freem(wp);
+ m_freem(bp);
+ return NULL;
+ } else {
+ ccp->compin += len;
+ SyncTable(state, cp, pp, len);
+ cp += len;
+ pp += len;
+ }
+ *pp++ = *cp++; /* CRC */
+ *pp++ = *cp++;
+ fcs = hdlc_Fcs(bufp, wp->m_len = pp - bufp);
+ if (fcs == GOODFCS) {
+ wp->m_offset += 2; /* skip length */
+ wp->m_len -= 4; /* skip length & CRC */
+ pp = MBUF_CTOP(wp);
+ *proto = *pp++;
+ if (*proto & 1) {
+ wp->m_offset++;
+ wp->m_len--;
+ } else {
+ wp->m_offset += 2;
+ wp->m_len -= 2;
+ *proto = (*proto << 8) | *pp++;
+ }
+ m_freem(bp);
+ return wp;
+ } else {
+ const char *pre = *MBUF_CTOP(bp) & 0x80 ? "" : "un";
+ log_Printf(LogDEBUG, "Pred1Input: fcs = 0x%04x (%scompressed), len = 0x%x,"
+ " olen = 0x%x\n", fcs, pre, len, olen);
+ log_Printf(LogCCP, "%s: Bad %scompressed CRC-16\n",
+ ccp->fsm.link->name, pre);
+ fsm_Reopen(&ccp->fsm);
+ m_freem(wp);
+ }
+ m_freem(bp);
+ return NULL;
+}
+
+static void
+Pred1DictSetup(void *v __unused, struct ccp *ccp __unused,
+ u_short proto __unused, struct mbuf *bp __unused)
+{
+ /* Nothing to see here */
+}
+
+static const char *
+Pred1DispOpts(struct fsm_opt *o __unused)
+{
+ return NULL;
+}
+
+static void
+Pred1InitOptsOutput(struct bundle *bundle __unused, struct fsm_opt *o,
+ const struct ccp_config *cfg __unused)
+{
+ o->hdr.len = 2;
+}
+
+static int
+Pred1SetOpts(struct bundle *bundle __unused, struct fsm_opt *o,
+ const struct ccp_config *cfg __unused)
+{
+ if (o->hdr.len != 2) {
+ o->hdr.len = 2;
+ return MODE_NAK;
+ }
+ return MODE_ACK;
+}
+
+const struct ccp_algorithm Pred1Algorithm = {
+ TY_PRED1,
+ CCP_NEG_PRED1,
+ Pred1DispOpts,
+ ccp_DefaultUsable,
+ ccp_DefaultRequired,
+ {
+ Pred1SetOpts,
+ Pred1InitInput,
+ Pred1Term,
+ Pred1ResetInput,
+ Pred1Input,
+ Pred1DictSetup
+ },
+ {
+ 0,
+ Pred1InitOptsOutput,
+ Pred1SetOpts,
+ Pred1InitOutput,
+ Pred1Term,
+ Pred1ResetOutput,
+ Pred1Output
+ },
+};
diff --git a/usr.sbin/ppp/pred.h b/usr.sbin/ppp/pred.h
new file mode 100644
index 0000000..1afb77a
--- /dev/null
+++ b/usr.sbin/ppp/pred.h
@@ -0,0 +1,31 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+extern const struct ccp_algorithm Pred1Algorithm;
diff --git a/usr.sbin/ppp/probe.c b/usr.sbin/ppp/probe.c
new file mode 100644
index 0000000..a33734c
--- /dev/null
+++ b/usr.sbin/ppp/probe.c
@@ -0,0 +1,78 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <stdio.h>
+#include <unistd.h>
+
+#include "probe.h"
+#include "log.h"
+#include "id.h"
+
+struct probe probe;
+
+/* Does select() alter the passed time value ? */
+static int
+select_changes_time(void)
+{
+ struct timeval t;
+
+ t.tv_sec = 0;
+ t.tv_usec = 100000;
+ select(0, NULL, NULL, NULL, &t);
+ return t.tv_usec != 100000;
+}
+
+#ifndef NOINET6
+static int
+ipv6_available(void)
+{
+ int s;
+
+ if ((s = ID0socket(PF_INET6, SOCK_DGRAM, 0)) == -1)
+ return 0;
+
+ close(s);
+ return 1;
+}
+#endif
+
+void
+probe_Init()
+{
+ probe.select_changes_time = select_changes_time() ? 1 : 0;
+ log_Printf(LogDEBUG, "Select changes time: %s\n",
+ probe.select_changes_time ? "yes" : "no");
+#ifndef NOINET6
+ probe.ipv6_available = ipv6_available() ? 1 : 0;
+ log_Printf(LogDEBUG, "IPv6 available: %s\n",
+ probe.ipv6_available ? "yes" : "no");
+#endif
+}
diff --git a/usr.sbin/ppp/probe.h b/usr.sbin/ppp/probe.h
new file mode 100644
index 0000000..5a7dce9
--- /dev/null
+++ b/usr.sbin/ppp/probe.h
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct probe {
+ unsigned select_changes_time : 1;
+#ifndef NOINET6
+ unsigned ipv6_available : 1;
+#endif
+};
+
+extern struct probe probe;
+
+extern void probe_Init(void);
diff --git a/usr.sbin/ppp/prompt.c b/usr.sbin/ppp/prompt.c
new file mode 100644
index 0000000..de331ef
--- /dev/null
+++ b/usr.sbin/ppp/prompt.c
@@ -0,0 +1,574 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/fcntl.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "timer.h"
+#include "command.h"
+#include "log.h"
+#include "descriptor.h"
+#include "prompt.h"
+#include "fsm.h"
+#include "auth.h"
+#include "iplist.h"
+#include "throughput.h"
+#include "slcompress.h"
+#include "mbuf.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "async.h"
+#include "ccp.h"
+#include "link.h"
+#include "physical.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "chat.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+#include "server.h"
+#include "main.h"
+
+static void
+prompt_Display(struct prompt *p)
+{
+ /* XXX: See Index2Nam() - should we only figure this out once ? */
+ static char shostname[MAXHOSTNAMELEN];
+ const char *pconnect, *pauth;
+
+ if (p->TermMode || !p->needprompt)
+ return;
+
+ p->needprompt = 0;
+
+ if (p->nonewline)
+ p->nonewline = 0;
+ else
+ fprintf(p->Term, "\n");
+
+ if (p->auth == LOCAL_AUTH)
+ pauth = " ON ";
+ else
+ pauth = " on ";
+
+ if (p->bundle->ncp.ipcp.fsm.state == ST_OPENED)
+ pconnect = "PPP";
+#ifndef NOINET6
+ else if (!Enabled(p->bundle, OPT_IPCP) &&
+ p->bundle->ncp.ipv6cp.fsm.state == ST_OPENED)
+ pconnect = "PPP";
+#endif
+ else if (bundle_Phase(p->bundle) == PHASE_NETWORK)
+ pconnect = "PPp";
+ else if (bundle_Phase(p->bundle) == PHASE_AUTHENTICATE)
+ pconnect = "Ppp";
+ else
+ pconnect = "ppp";
+
+ if (*shostname == '\0') {
+ char *dot;
+
+ if (gethostname(shostname, sizeof shostname) || *shostname == '\0')
+ strcpy(shostname, "localhost");
+ else if ((dot = strchr(shostname, '.')))
+ *dot = '\0';
+ }
+
+ fprintf(p->Term, "%s%s%s> ", pconnect, pauth, shostname);
+ fflush(p->Term);
+}
+
+static int
+prompt_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w __unused,
+ fd_set *e, int *n)
+{
+ struct prompt *p = descriptor2prompt(d);
+ int sets;
+
+ sets = 0;
+
+ if (!p->active)
+ return sets;
+
+ if (p->fd_in >= 0) {
+ if (r) {
+ FD_SET(p->fd_in, r);
+ log_Printf(LogTIMER, "prompt %s: fdset(r) %d\n", p->src.from, p->fd_in);
+ sets++;
+ }
+ if (e) {
+ FD_SET(p->fd_in, e);
+ log_Printf(LogTIMER, "prompt %s: fdset(e) %d\n", p->src.from, p->fd_in);
+ sets++;
+ }
+ if (sets && *n < p->fd_in + 1)
+ *n = p->fd_in + 1;
+ }
+
+ prompt_Display(p);
+
+ return sets;
+}
+
+static int
+prompt_IsSet(struct fdescriptor *d, const fd_set *fdset)
+{
+ struct prompt *p = descriptor2prompt(d);
+ return p->fd_in >= 0 && FD_ISSET(p->fd_in, fdset);
+}
+
+
+static void
+prompt_ShowHelp(struct prompt *p)
+{
+ prompt_Printf(p, "The following commands are available:\n");
+ prompt_Printf(p, " ~p\tEnter Packet mode\n");
+ prompt_Printf(p, " ~t\tShow timers\n");
+ prompt_Printf(p, " ~m\tShow memory map\n");
+ prompt_Printf(p, " ~.\tTerminate program\n");
+ prompt_Printf(p, " ~?\tThis help\n");
+}
+
+static void
+prompt_Read(struct fdescriptor *d, struct bundle *bundle,
+ const fd_set *fdset __unused)
+{
+ struct prompt *p = descriptor2prompt(d);
+ struct prompt *op;
+ int n;
+ char ch;
+ char linebuff[LINE_LEN];
+
+ if (p->TermMode == NULL) {
+ n = read(p->fd_in, linebuff, sizeof linebuff - 1);
+ if (n > 0) {
+ if (linebuff[n-1] == '\n')
+ linebuff[--n] = '\0';
+ else
+ linebuff[n] = '\0';
+ p->nonewline = 1; /* Maybe command_Decode does a prompt */
+ prompt_Required(p);
+ if (n) {
+ if ((op = log_PromptContext) == NULL)
+ log_PromptContext = p;
+ if (!command_Decode(bundle, linebuff, n, p, p->src.from))
+ prompt_Printf(p, "Syntax error\n");
+ log_PromptContext = op;
+ }
+ } else if (n <= 0) {
+ log_Printf(LogPHASE, "%s: Client connection closed.\n", p->src.from);
+ if (!p->owner)
+ Cleanup();
+ prompt_Destroy(p, 0);
+ }
+ return;
+ }
+
+ switch (p->TermMode->state) {
+ case DATALINK_CLOSED:
+ prompt_Printf(p, "Link lost, terminal mode.\n");
+ prompt_TtyCommandMode(p);
+ p->nonewline = 0;
+ prompt_Required(p);
+ return;
+
+ case DATALINK_READY:
+ break;
+
+ case DATALINK_OPEN:
+ prompt_Printf(p, "\nPacket mode detected.\n");
+ prompt_TtyCommandMode(p);
+ p->nonewline = 0;
+ /* We'll get a prompt because of our status change */
+ /* FALLTHROUGH */
+
+ default:
+ /* Wait 'till we're in a state we care about */
+ return;
+ }
+
+ /*
+ * We are in terminal mode, decode special sequences
+ */
+ n = read(p->fd_in, &ch, 1);
+ log_Printf(LogDEBUG, "Got %d bytes (reading from the terminal)\n", n);
+
+ if (n > 0) {
+ switch (p->readtilde) {
+ case 0:
+ if (ch == '~')
+ p->readtilde = 1;
+ else
+ if (physical_Write(p->TermMode->physical, &ch, n) < 0) {
+ log_Printf(LogWARN, "error writing to modem: %s\n", strerror(errno));
+ prompt_TtyCommandMode(p);
+ }
+ break;
+ case 1:
+ switch (ch) {
+ case '?':
+ prompt_ShowHelp(p);
+ break;
+ case 'p':
+ datalink_Up(p->TermMode, 0, 1);
+ prompt_Printf(p, "\nPacket mode.\n");
+ prompt_TtyCommandMode(p);
+ break;
+ case '.':
+ prompt_TtyCommandMode(p);
+ p->nonewline = 0;
+ prompt_Required(p);
+ break;
+ case 't':
+ timer_Show(0, p);
+ break;
+ case 'm':
+ {
+ struct cmdargs arg;
+
+ arg.cmdtab = NULL;
+ arg.cmd = NULL;
+ arg.argc = 0;
+ arg.argn = 0;
+ arg.argv = NULL;
+ arg.bundle = bundle;
+ arg.cx = p->TermMode;
+ arg.prompt = p;
+
+ mbuf_Show(&arg);
+ }
+ break;
+ default:
+ if (physical_Write(p->TermMode->physical, &ch, n) < 0) {
+ log_Printf(LogWARN, "error writing to modem: %s\n", strerror(errno));
+ prompt_TtyCommandMode(p);
+ }
+ break;
+ }
+ p->readtilde = 0;
+ break;
+ }
+ }
+}
+
+static int
+prompt_Write(struct fdescriptor *d __unused, struct bundle *bundle __unused,
+ const fd_set *fdset __unused)
+{
+ /* We never want to write here ! */
+ log_Printf(LogALERT, "prompt_Write: Internal error: Bad call !\n");
+ return 0;
+}
+
+struct prompt *
+prompt_Create(struct server *s, struct bundle *bundle, int fd)
+{
+ struct prompt *p = (struct prompt *)malloc(sizeof(struct prompt));
+
+ if (p != NULL) {
+ p->desc.type = PROMPT_DESCRIPTOR;
+ p->desc.UpdateSet = prompt_UpdateSet;
+ p->desc.IsSet = prompt_IsSet;
+ p->desc.Read = prompt_Read;
+ p->desc.Write = prompt_Write;
+
+ if (fd == PROMPT_STD) {
+ char *tty = ttyname(STDIN_FILENO);
+
+ if (!tty) {
+ free(p);
+ return NULL;
+ }
+ p->fd_in = STDIN_FILENO;
+ p->fd_out = STDOUT_FILENO;
+ p->Term = stdout;
+ p->owner = NULL;
+ p->auth = LOCAL_AUTH;
+ p->src.type = "Controller";
+ strncpy(p->src.from, tty, sizeof p->src.from - 1);
+ p->src.from[sizeof p->src.from - 1] = '\0';
+ tcgetattr(p->fd_in, &p->oldtio); /* Save original tty mode */
+ } else {
+ p->fd_in = p->fd_out = fd;
+ p->Term = fdopen(fd, "a+");
+ p->owner = s;
+ p->auth = *s->cfg.passwd ? LOCAL_NO_AUTH : LOCAL_AUTH;
+ p->src.type = "unknown";
+ *p->src.from = '\0';
+ }
+ p->TermMode = NULL;
+ p->nonewline = 1;
+ p->needprompt = 1;
+ p->readtilde = 0;
+ p->bundle = bundle;
+ log_RegisterPrompt(p);
+ }
+
+ return p;
+}
+
+void
+prompt_Destroy(struct prompt *p, int verbose)
+{
+ if (p) {
+ if (p->Term != stdout) {
+ fclose(p->Term);
+ close(p->fd_in);
+ if (p->fd_out != p->fd_in)
+ close(p->fd_out);
+ if (verbose)
+ log_Printf(LogPHASE, "%s: Client connection dropped.\n", p->src.from);
+ } else
+ prompt_TtyOldMode(p);
+
+ log_UnRegisterPrompt(p);
+ free(p);
+ }
+}
+
+void
+prompt_Printf(struct prompt *p, const char *fmt,...)
+{
+ if (p && p->active) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ prompt_vPrintf(p, fmt, ap);
+ va_end(ap);
+ }
+}
+
+void
+prompt_vPrintf(struct prompt *p, const char *fmt, va_list ap)
+{
+ if (p && p->active) {
+ char nfmt[LINE_LEN];
+ const char *pfmt;
+
+ if (p->TermMode) {
+ /* Stuff '\r' in front of '\n' 'cos we're in raw mode */
+ size_t len = strlen(fmt);
+
+ if (len && len < sizeof nfmt - 1 && fmt[len-1] == '\n' &&
+ (len == 1 || fmt[len-2] != '\r')) {
+ strcpy(nfmt, fmt);
+ strcpy(nfmt + len - 1, "\r\n");
+ pfmt = nfmt;
+ } else
+ pfmt = fmt;
+ } else
+ pfmt = fmt;
+ vfprintf(p->Term, pfmt, ap);
+ fflush(p->Term);
+ p->nonewline = 1;
+ }
+}
+
+void
+prompt_TtyInit(struct prompt *p)
+{
+ int stat, fd = p ? p->fd_in : STDIN_FILENO;
+ struct termios newtio;
+
+ stat = fcntl(fd, F_GETFL, 0);
+ if (stat > 0) {
+ stat |= O_NONBLOCK;
+ fcntl(fd, F_SETFL, stat);
+ }
+
+ if (p)
+ newtio = p->oldtio;
+ else
+ tcgetattr(fd, &newtio);
+
+ newtio.c_lflag &= ~(ECHO | ISIG | ICANON);
+ newtio.c_iflag = 0;
+ newtio.c_oflag &= ~OPOST;
+ if (!p)
+ newtio.c_cc[VINTR] = _POSIX_VDISABLE;
+ newtio.c_cc[VMIN] = 1;
+ newtio.c_cc[VTIME] = 0;
+ newtio.c_cflag |= CS8;
+ tcsetattr(fd, TCSANOW, &newtio);
+ if (p)
+ p->comtio = newtio;
+}
+
+/*
+ * Set tty into command mode. We allow canonical input and echo processing.
+ */
+void
+prompt_TtyCommandMode(struct prompt *p)
+{
+ struct termios newtio;
+ int stat;
+
+ tcgetattr(p->fd_in, &newtio);
+ newtio.c_lflag |= (ECHO | ISIG | ICANON);
+ newtio.c_iflag = p->oldtio.c_iflag;
+ newtio.c_oflag |= OPOST;
+ tcsetattr(p->fd_in, TCSADRAIN, &newtio);
+
+ stat = fcntl(p->fd_in, F_GETFL, 0);
+ if (stat > 0) {
+ stat |= O_NONBLOCK;
+ fcntl(p->fd_in, F_SETFL, stat);
+ }
+
+ p->TermMode = NULL;
+}
+
+/*
+ * Set tty into terminal mode which is used while we invoke term command.
+ */
+void
+prompt_TtyTermMode(struct prompt *p, struct datalink *dl)
+{
+ int stat;
+
+ if (p->Term == stdout)
+ tcsetattr(p->fd_in, TCSADRAIN, &p->comtio);
+
+ stat = fcntl(p->fd_in, F_GETFL, 0);
+ if (stat > 0) {
+ stat &= ~O_NONBLOCK;
+ fcntl(p->fd_in, F_SETFL, stat);
+ }
+ p->TermMode = dl;
+}
+
+void
+prompt_TtyOldMode(struct prompt *p)
+{
+ int stat;
+
+ stat = fcntl(p->fd_in, F_GETFL, 0);
+ if (stat > 0) {
+ stat &= ~O_NONBLOCK;
+ fcntl(p->fd_in, F_SETFL, stat);
+ }
+
+ if (p->Term == stdout)
+ tcsetattr(p->fd_in, TCSADRAIN, &p->oldtio);
+}
+
+pid_t
+prompt_pgrp(struct prompt *p)
+{
+ return tcgetpgrp(p->fd_in);
+}
+
+int
+PasswdCommand(struct cmdargs const *arg)
+{
+ const char *pass;
+
+ if (!arg->prompt) {
+ log_Printf(LogWARN, "passwd: Cannot specify without a prompt\n");
+ return 0;
+ }
+
+ if (arg->prompt->owner == NULL) {
+ log_Printf(LogWARN, "passwd: Not required\n");
+ return 0;
+ }
+
+ if (arg->argc == arg->argn)
+ pass = "";
+ else if (arg->argc > arg->argn+1)
+ return -1;
+ else
+ pass = arg->argv[arg->argn];
+
+ if (!strcmp(arg->prompt->owner->cfg.passwd, pass))
+ arg->prompt->auth = LOCAL_AUTH;
+ else
+ arg->prompt->auth = LOCAL_NO_AUTH;
+
+ return 0;
+}
+
+static struct pppTimer bgtimer;
+
+static void
+prompt_TimedContinue(void *v)
+{
+ prompt_Continue((struct prompt *)v);
+}
+
+void
+prompt_Continue(struct prompt *p)
+{
+ timer_Stop(&bgtimer);
+ if (getpgrp() == prompt_pgrp(p)) {
+ prompt_TtyCommandMode(p);
+ p->nonewline = 1;
+ prompt_Required(p);
+ log_ActivatePrompt(p);
+ } else if (!p->owner) {
+ bgtimer.func = prompt_TimedContinue;
+ bgtimer.name = "prompt bg";
+ bgtimer.load = SECTICKS;
+ bgtimer.arg = p;
+ timer_Start(&bgtimer);
+ }
+}
+
+void
+prompt_Suspend(struct prompt *p)
+{
+ if (getpgrp() == prompt_pgrp(p)) {
+ prompt_TtyOldMode(p);
+ log_DeactivatePrompt(p);
+ }
+}
diff --git a/usr.sbin/ppp/prompt.h b/usr.sbin/ppp/prompt.h
new file mode 100644
index 0000000..0489338
--- /dev/null
+++ b/usr.sbin/ppp/prompt.h
@@ -0,0 +1,96 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define LOCAL_AUTH 0x01
+#define LOCAL_NO_AUTH 0x02
+#define LOCAL_DENY 0x03
+#define LOCAL_CX 0x04 /* OR'd value - require a context */
+#define LOCAL_CX_OPT 0x08 /* OR'd value - optional context */
+
+struct server;
+struct datalink;
+struct bundle;
+struct cmdargs;
+
+struct prompt {
+ struct fdescriptor desc;
+ int fd_in, fd_out;
+ struct datalink *TermMode; /* The modem we're talking directly to */
+ FILE *Term; /* sits on top of fd_out */
+ u_char auth; /* Local Authorized status */
+ struct server *owner; /* who created me */
+ struct bundle *bundle; /* who I'm controlling */
+ unsigned nonewline : 1; /* need a newline before our prompt ? */
+ unsigned needprompt : 1; /* Show a prompt at the next UpdateSet() */
+ unsigned active : 1; /* Is the prompt active (^Z) */
+ unsigned readtilde : 1; /* We've read a ``~'' from fd_in */
+
+ struct {
+ const char *type; /* Type of connection */
+ char from[40]; /* Source of connection */
+ } src;
+
+ struct prompt *next; /* Maintained in log.c */
+ u_long logmask; /* Maintained in log.c */
+
+ struct termios oldtio; /* Original tty mode */
+ struct termios comtio; /* Command level tty mode */
+};
+
+#define descriptor2prompt(d) \
+ ((d)->type == PROMPT_DESCRIPTOR ? (struct prompt *)(d) : NULL)
+
+#define PROMPT_STD (-1)
+extern struct prompt *prompt_Create(struct server *, struct bundle *, int);
+extern void prompt_Destroy(struct prompt *, int);
+extern void prompt_Required(struct prompt *);
+#ifdef __GNUC__
+extern void prompt_Printf(struct prompt *, const char *, ...)
+ __attribute__ ((format (printf, 2, 3)));
+#else
+extern void prompt_Printf(struct prompt *, const char *, ...);
+#endif
+#ifdef __GNUC__
+extern void prompt_vPrintf(struct prompt *, const char *, va_list)
+ __attribute__ ((format (printf, 2, 0)));
+#else
+extern void prompt_vPrintf(struct prompt *, const char *, va_list);
+#endif
+#define PROMPT_DONT_WANT_INT 1
+#define PROMPT_WANT_INT 0
+extern void prompt_TtyInit(struct prompt *);
+extern void prompt_TtyCommandMode(struct prompt *);
+extern void prompt_TtyTermMode(struct prompt *, struct datalink *);
+extern void prompt_TtyOldMode(struct prompt *);
+extern pid_t prompt_pgrp(struct prompt *);
+extern int PasswdCommand(struct cmdargs const *);
+extern void prompt_Suspend(struct prompt *);
+extern void prompt_Continue(struct prompt *);
+#define prompt_IsTermMode(p, dl) ((p)->TermMode == (dl) ? 1 : 0)
+#define prompt_IsController(p) (!(p) || (p)->owner ? 0 : 1)
+#define prompt_Required(p) ((p)->needprompt = 1)
diff --git a/usr.sbin/ppp/proto.c b/usr.sbin/ppp/proto.c
new file mode 100644
index 0000000..2c2c26d
--- /dev/null
+++ b/usr.sbin/ppp/proto.c
@@ -0,0 +1,115 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <termios.h>
+
+#include "layer.h"
+#include "acf.h"
+#include "defs.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "mbuf.h"
+#include "proto.h"
+#include "throughput.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+
+int
+proto_WrapperOctets(struct lcp *lcp, u_short proto)
+{
+ return (lcp->his_protocomp && !(proto & 0xff00)) ? 1 : 2;
+}
+
+struct mbuf *
+proto_Prepend(struct mbuf *bp, u_short proto, unsigned comp, int extra)
+{
+ u_char cp[2];
+
+ cp[0] = proto >> 8;
+ cp[1] = proto & 0xff;
+
+ if (comp && cp[0] == 0)
+ bp = m_prepend(bp, cp + 1, 1, extra);
+ else
+ bp = m_prepend(bp, cp, 2, extra);
+
+ return bp;
+}
+
+static struct mbuf *
+proto_LayerPush(struct bundle *b __unused, struct link *l, struct mbuf *bp,
+ int pri __unused, u_short *proto)
+{
+ log_Printf(LogDEBUG, "proto_LayerPush: Using 0x%04x\n", *proto);
+ bp = proto_Prepend(bp, *proto, l->lcp.his_protocomp,
+ acf_WrapperOctets(&l->lcp, *proto));
+ m_settype(bp, MB_PROTOOUT);
+ link_ProtocolRecord(l, *proto, PROTO_OUT);
+
+ return bp;
+}
+
+static struct mbuf *
+proto_LayerPull(struct bundle *b __unused, struct link *l, struct mbuf *bp,
+ u_short *proto)
+{
+ u_char cp[2];
+ size_t got;
+
+ if ((got = mbuf_View(bp, cp, 2)) == 0) {
+ m_freem(bp);
+ return NULL;
+ }
+
+ *proto = cp[0];
+ if (!(*proto & 1)) {
+ if (got == 1) {
+ m_freem(bp);
+ return NULL;
+ }
+ bp = mbuf_Read(bp, cp, 2);
+ *proto = (*proto << 8) | cp[1];
+ } else
+ bp = mbuf_Read(bp, cp, 1);
+
+ log_Printf(LogDEBUG, "proto_LayerPull: unknown -> 0x%04x\n", *proto);
+ m_settype(bp, MB_PROTOIN);
+ link_ProtocolRecord(l, *proto, PROTO_IN);
+
+ return bp;
+}
+
+struct layer protolayer =
+ { LAYER_PROTO, "proto", proto_LayerPush, proto_LayerPull };
diff --git a/usr.sbin/ppp/proto.h b/usr.sbin/ppp/proto.h
new file mode 100644
index 0000000..8c93327
--- /dev/null
+++ b/usr.sbin/ppp/proto.h
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+/*
+ * Definition of protocol numbers
+ */
+#define PROTO_IP 0x0021 /* IP */
+#define PROTO_VJUNCOMP 0x002f /* VJ Uncompressed */
+#define PROTO_VJCOMP 0x002d /* VJ Compressed */
+#define PROTO_MP 0x003d /* Multilink fragment */
+#ifndef NOINET6
+#define PROTO_IPV6 0x0057 /* IPv6 */
+#endif
+#define PROTO_ICOMPD 0x00fb /* Individual link compressed */
+#define PROTO_COMPD 0x00fd /* Compressed datagram */
+
+#define PROTO_COMPRESSIBLE(p) (((p) & 0xff81) == 0x01)
+
+#define PROTO_IPCP 0x8021
+#ifndef NOINET6
+#define PROTO_IPV6CP 0x8057
+#endif
+#define PROTO_ICCP 0x80fb
+#define PROTO_CCP 0x80fd
+
+#define PROTO_LCP 0xc021
+#define PROTO_PAP 0xc023
+#define PROTO_CBCP 0xc029
+#define PROTO_LQR 0xc025
+#define PROTO_CHAP 0xc223
+
+struct lcp;
+
+extern int proto_WrapperOctets(struct lcp *, u_short);
+struct mbuf *proto_Prepend(struct mbuf *, u_short, unsigned, int);
+
+extern struct layer protolayer;
diff --git a/usr.sbin/ppp/radius.c b/usr.sbin/ppp/radius.c
new file mode 100644
index 0000000..6b1d685
--- /dev/null
+++ b/usr.sbin/ppp/radius.c
@@ -0,0 +1,1361 @@
+/*
+ * Copyright 1999 Internet Business Solutions Ltd., Switzerland
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <stdint.h>
+#include <sys/param.h>
+
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <net/route.h>
+
+#ifdef LOCALRAD
+#include "radlib.h"
+#include "radlib_vs.h"
+#else
+#include <radlib.h>
+#include <radlib_vs.h>
+#endif
+
+#include <errno.h>
+#ifndef NODES
+#include <md5.h>
+#endif
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <termios.h>
+#include <unistd.h>
+#include <netdb.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "log.h"
+#include "descriptor.h"
+#include "prompt.h"
+#include "timer.h"
+#include "fsm.h"
+#include "iplist.h"
+#include "slcompress.h"
+#include "throughput.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "mbuf.h"
+#include "ncpaddr.h"
+#include "ip.h"
+#include "ipcp.h"
+#include "ipv6cp.h"
+#include "route.h"
+#include "command.h"
+#include "filter.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "mp.h"
+#include "radius.h"
+#include "auth.h"
+#include "async.h"
+#include "physical.h"
+#include "chat.h"
+#include "cbcp.h"
+#include "chap.h"
+#include "datalink.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "proto.h"
+#include "iface.h"
+
+#ifndef NODES
+struct mschap_response {
+ u_char ident;
+ u_char flags;
+ u_char lm_response[24];
+ u_char nt_response[24];
+};
+
+struct mschap2_response {
+ u_char ident;
+ u_char flags;
+ u_char pchallenge[16];
+ u_char reserved[8];
+ u_char response[24];
+};
+
+#define AUTH_LEN 16
+#define SALT_LEN 2
+#endif
+
+static const char *
+radius_policyname(int policy)
+{
+ switch(policy) {
+ case MPPE_POLICY_ALLOWED:
+ return "Allowed";
+ case MPPE_POLICY_REQUIRED:
+ return "Required";
+ }
+ return NumStr(policy, NULL, 0);
+}
+
+static const char *
+radius_typesname(int types)
+{
+ switch(types) {
+ case MPPE_TYPE_40BIT:
+ return "40 bit";
+ case MPPE_TYPE_128BIT:
+ return "128 bit";
+ case MPPE_TYPE_40BIT|MPPE_TYPE_128BIT:
+ return "40 or 128 bit";
+ }
+ return NumStr(types, NULL, 0);
+}
+
+#ifndef NODES
+static void
+demangle(struct radius *r, const void *mangled, size_t mlen,
+ char **buf, size_t *len)
+{
+ char R[AUTH_LEN]; /* variable names as per rfc2548 */
+ const char *S;
+ u_char b[16];
+ const u_char *A, *C;
+ MD5_CTX Context;
+ int Slen, i, Clen, Ppos;
+ u_char *P;
+
+ if (mlen % 16 != SALT_LEN) {
+ log_Printf(LogWARN, "Cannot interpret mangled data of length %ld\n",
+ (u_long)mlen);
+ *buf = NULL;
+ *len = 0;
+ return;
+ }
+
+ /* We need the RADIUS Request-Authenticator */
+ if (rad_request_authenticator(r->cx.rad, R, sizeof R) != AUTH_LEN) {
+ log_Printf(LogWARN, "Cannot obtain the RADIUS request authenticator\n");
+ *buf = NULL;
+ *len = 0;
+ return;
+ }
+
+ A = (const u_char *)mangled; /* Salt comes first */
+ C = (const u_char *)mangled + SALT_LEN; /* Then the ciphertext */
+ Clen = mlen - SALT_LEN;
+ S = rad_server_secret(r->cx.rad); /* We need the RADIUS secret */
+ Slen = strlen(S);
+ P = alloca(Clen); /* We derive our plaintext */
+
+ MD5Init(&Context);
+ MD5Update(&Context, S, Slen);
+ MD5Update(&Context, R, AUTH_LEN);
+ MD5Update(&Context, A, SALT_LEN);
+ MD5Final(b, &Context);
+ Ppos = 0;
+
+ while (Clen) {
+ Clen -= 16;
+
+ for (i = 0; i < 16; i++)
+ P[Ppos++] = C[i] ^ b[i];
+
+ if (Clen) {
+ MD5Init(&Context);
+ MD5Update(&Context, S, Slen);
+ MD5Update(&Context, C, 16);
+ MD5Final(b, &Context);
+ }
+
+ C += 16;
+ }
+
+ /*
+ * The resulting plain text consists of a one-byte length, the text and
+ * maybe some padding.
+ */
+ *len = *P;
+ if (*len > mlen - 1) {
+ log_Printf(LogWARN, "Mangled data seems to be garbage\n");
+ *buf = NULL;
+ *len = 0;
+ return;
+ }
+
+ if ((*buf = malloc(*len)) == NULL) {
+ log_Printf(LogWARN, "demangle: Out of memory (%lu bytes)\n", (u_long)*len);
+ *len = 0;
+ } else
+ memcpy(*buf, P + 1, *len);
+}
+#endif
+
+/* XXX: This should go into librarius. */
+#ifndef NOINET6
+static uint8_t *
+rad_cvt_ipv6prefix(const void *data, size_t len)
+{
+ const size_t ipv6len = sizeof(struct in6_addr) + 2;
+ uint8_t *s;
+
+ if (len > ipv6len)
+ return NULL;
+ s = malloc(ipv6len);
+ if (s != NULL) {
+ memset(s, 0, ipv6len);
+ memcpy(s, data, len);
+ }
+ return s;
+}
+#endif
+
+/*
+ * rad_continue_send_request() has given us `got' (non-zero). Deal with it.
+ */
+static void
+radius_Process(struct radius *r, int got)
+{
+ char *argv[MAXARGS], *nuke;
+ struct bundle *bundle;
+ int argc, addrs, res, width;
+ size_t len;
+ struct ncprange dest;
+ struct ncpaddr gw;
+ const void *data;
+ const char *stype;
+ u_int32_t ipaddr, vendor;
+ struct in_addr ip;
+#ifndef NOINET6
+ uint8_t ipv6addr[INET6_ADDRSTRLEN];
+ struct in6_addr ip6;
+#endif
+
+ r->cx.fd = -1; /* Stop select()ing */
+ stype = r->cx.auth ? "auth" : "acct";
+
+ switch (got) {
+ case RAD_ACCESS_ACCEPT:
+ log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE,
+ "Radius(%s): ACCEPT received\n", stype);
+ if (!r->cx.auth) {
+ rad_close(r->cx.rad);
+ return;
+ }
+ break;
+
+ case RAD_ACCESS_REJECT:
+ log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE,
+ "Radius(%s): REJECT received\n", stype);
+ if (!r->cx.auth) {
+ rad_close(r->cx.rad);
+ return;
+ }
+ break;
+
+ case RAD_ACCESS_CHALLENGE:
+ /* we can't deal with this (for now) ! */
+ log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE,
+ "Radius: CHALLENGE received (can't handle yet)\n");
+ if (r->cx.auth)
+ auth_Failure(r->cx.auth);
+ rad_close(r->cx.rad);
+ return;
+
+ case RAD_ACCOUNTING_RESPONSE:
+ /*
+ * It's probably not ideal to log this at PHASE level as we'll see
+ * too much stuff going to the log when ``set rad_alive'' is used.
+ * So we differ from older behaviour (ppp version 3.1 and before)
+ * and just log accounting responses to LogRADIUS.
+ */
+ log_Printf(LogRADIUS, "Radius(%s): Accounting response received\n",
+ stype);
+ if (r->cx.auth)
+ auth_Failure(r->cx.auth); /* unexpected !!! */
+
+ /* No further processing for accounting requests, please */
+ rad_close(r->cx.rad);
+ return;
+
+ case -1:
+ log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE,
+ "radius(%s): %s\n", stype, rad_strerror(r->cx.rad));
+ if (r->cx.auth)
+ auth_Failure(r->cx.auth);
+ rad_close(r->cx.rad);
+ return;
+
+ default:
+ log_Printf(LogERROR, "rad_send_request(%s): Failed %d: %s\n", stype,
+ got, rad_strerror(r->cx.rad));
+ if (r->cx.auth)
+ auth_Failure(r->cx.auth);
+ rad_close(r->cx.rad);
+ return;
+ }
+
+ /* Let's see what we've got in our reply */
+ r->ip.s_addr = r->mask.s_addr = INADDR_NONE;
+ r->mtu = 0;
+ r->vj = 0;
+ while ((res = rad_get_attr(r->cx.rad, &data, &len)) > 0) {
+ switch (res) {
+ case RAD_FRAMED_IP_ADDRESS:
+ r->ip = rad_cvt_addr(data);
+ log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE,
+ " IP %s\n", inet_ntoa(r->ip));
+ break;
+
+ case RAD_FILTER_ID:
+ free(r->filterid);
+ if ((r->filterid = rad_cvt_string(data, len)) == NULL) {
+ log_Printf(LogERROR, "rad_cvt_string: %s\n", rad_strerror(r->cx.rad));
+ auth_Failure(r->cx.auth);
+ rad_close(r->cx.rad);
+ return;
+ }
+ log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE,
+ " Filter \"%s\"\n", r->filterid);
+ break;
+
+ case RAD_SESSION_TIMEOUT:
+ r->sessiontime = rad_cvt_int(data);
+ log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE,
+ " Session-Timeout %lu\n", r->sessiontime);
+ break;
+
+ case RAD_FRAMED_IP_NETMASK:
+ r->mask = rad_cvt_addr(data);
+ log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE,
+ " Netmask %s\n", inet_ntoa(r->mask));
+ break;
+
+ case RAD_FRAMED_MTU:
+ r->mtu = rad_cvt_int(data);
+ log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE,
+ " MTU %lu\n", r->mtu);
+ break;
+
+ case RAD_FRAMED_ROUTING:
+ /* Disabled for now - should we automatically set up some filters ? */
+ /* rad_cvt_int(data); */
+ /* bit 1 = Send routing packets */
+ /* bit 2 = Receive routing packets */
+ break;
+
+ case RAD_FRAMED_COMPRESSION:
+ r->vj = rad_cvt_int(data) == 1 ? 1 : 0;
+ log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE,
+ " VJ %sabled\n", r->vj ? "en" : "dis");
+ break;
+
+ case RAD_FRAMED_ROUTE:
+ /*
+ * We expect a string of the format ``dest[/bits] gw [metrics]''
+ * Any specified metrics are ignored. MYADDR and HISADDR are
+ * understood for ``dest'' and ``gw'' and ``0.0.0.0'' is the same
+ * as ``HISADDR''.
+ */
+
+ if ((nuke = rad_cvt_string(data, len)) == NULL) {
+ log_Printf(LogERROR, "rad_cvt_string: %s\n", rad_strerror(r->cx.rad));
+ auth_Failure(r->cx.auth);
+ rad_close(r->cx.rad);
+ return;
+ }
+
+ log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE,
+ " Route: %s\n", nuke);
+ bundle = r->cx.auth->physical->dl->bundle;
+ ip.s_addr = INADDR_ANY;
+ ncpaddr_setip4(&gw, ip);
+ ncprange_setip4host(&dest, ip);
+ argc = command_Interpret(nuke, strlen(nuke), argv);
+ if (argc < 0)
+ log_Printf(LogWARN, "radius: %s: Syntax error\n",
+ argc == 1 ? argv[0] : "\"\"");
+ else if (argc < 2)
+ log_Printf(LogWARN, "radius: %s: Invalid route\n",
+ argc == 1 ? argv[0] : "\"\"");
+ else if ((strcasecmp(argv[0], "default") != 0 &&
+ !ncprange_aton(&dest, &bundle->ncp, argv[0])) ||
+ !ncpaddr_aton(&gw, &bundle->ncp, argv[1]))
+ log_Printf(LogWARN, "radius: %s %s: Invalid route\n",
+ argv[0], argv[1]);
+ else {
+ ncprange_getwidth(&dest, &width);
+ if (width == 32 && strchr(argv[0], '/') == NULL) {
+ /* No mask specified - use the natural mask */
+ ncprange_getip4addr(&dest, &ip);
+ ncprange_setip4mask(&dest, addr2mask(ip));
+ }
+ addrs = 0;
+
+ if (!strncasecmp(argv[0], "HISADDR", 7))
+ addrs = ROUTE_DSTHISADDR;
+ else if (!strncasecmp(argv[0], "MYADDR", 6))
+ addrs = ROUTE_DSTMYADDR;
+
+ if (ncpaddr_getip4addr(&gw, &ipaddr) && ipaddr == INADDR_ANY) {
+ addrs |= ROUTE_GWHISADDR;
+ ncpaddr_setip4(&gw, bundle->ncp.ipcp.peer_ip);
+ } else if (strcasecmp(argv[1], "HISADDR") == 0)
+ addrs |= ROUTE_GWHISADDR;
+
+ route_Add(&r->routes, addrs, &dest, &gw);
+ }
+ free(nuke);
+ break;
+
+ case RAD_REPLY_MESSAGE:
+ free(r->repstr);
+ if ((r->repstr = rad_cvt_string(data, len)) == NULL) {
+ log_Printf(LogERROR, "rad_cvt_string: %s\n", rad_strerror(r->cx.rad));
+ auth_Failure(r->cx.auth);
+ rad_close(r->cx.rad);
+ return;
+ }
+ log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE,
+ " Reply-Message \"%s\"\n", r->repstr);
+ break;
+
+#ifndef NOINET6
+ case RAD_FRAMED_IPV6_PREFIX:
+ free(r->ipv6prefix);
+ if ((r->ipv6prefix = rad_cvt_ipv6prefix(data, len)) == NULL) {
+ log_Printf(LogERROR, "rad_cvt_ipv6prefix: %s\n",
+ "Malformed attribute in response");
+ auth_Failure(r->cx.auth);
+ rad_close(r->cx.rad);
+ return;
+ }
+ inet_ntop(AF_INET6, &r->ipv6prefix[2], ipv6addr, sizeof(ipv6addr));
+ log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE,
+ " IPv6 %s/%d\n", ipv6addr, r->ipv6prefix[1]);
+ break;
+
+ case RAD_FRAMED_IPV6_ROUTE:
+ /*
+ * We expect a string of the format ``dest[/bits] gw [metrics]''
+ * Any specified metrics are ignored. MYADDR6 and HISADDR6 are
+ * understood for ``dest'' and ``gw'' and ``::'' is the same
+ * as ``HISADDR6''.
+ */
+
+ if ((nuke = rad_cvt_string(data, len)) == NULL) {
+ log_Printf(LogERROR, "rad_cvt_string: %s\n", rad_strerror(r->cx.rad));
+ auth_Failure(r->cx.auth);
+ rad_close(r->cx.rad);
+ return;
+ }
+
+ log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE,
+ " IPv6 Route: %s\n", nuke);
+ bundle = r->cx.auth->physical->dl->bundle;
+ ncpaddr_setip6(&gw, &in6addr_any);
+ ncprange_set(&dest, &gw, 0);
+ argc = command_Interpret(nuke, strlen(nuke), argv);
+ if (argc < 0)
+ log_Printf(LogWARN, "radius: %s: Syntax error\n",
+ argc == 1 ? argv[0] : "\"\"");
+ else if (argc < 2)
+ log_Printf(LogWARN, "radius: %s: Invalid route\n",
+ argc == 1 ? argv[0] : "\"\"");
+ else if ((strcasecmp(argv[0], "default") != 0 &&
+ !ncprange_aton(&dest, &bundle->ncp, argv[0])) ||
+ !ncpaddr_aton(&gw, &bundle->ncp, argv[1]))
+ log_Printf(LogWARN, "radius: %s %s: Invalid route\n",
+ argv[0], argv[1]);
+ else {
+ addrs = 0;
+
+ if (!strncasecmp(argv[0], "HISADDR6", 8))
+ addrs = ROUTE_DSTHISADDR6;
+ else if (!strncasecmp(argv[0], "MYADDR6", 7))
+ addrs = ROUTE_DSTMYADDR6;
+
+ if (ncpaddr_getip6(&gw, &ip6) && IN6_IS_ADDR_UNSPECIFIED(&ip6)) {
+ addrs |= ROUTE_GWHISADDR6;
+ ncpaddr_copy(&gw, &bundle->ncp.ipv6cp.hisaddr);
+ } else if (strcasecmp(argv[1], "HISADDR6") == 0)
+ addrs |= ROUTE_GWHISADDR6;
+
+ route_Add(&r->ipv6routes, addrs, &dest, &gw);
+ }
+ free(nuke);
+ break;
+#endif
+
+ case RAD_VENDOR_SPECIFIC:
+ if ((res = rad_get_vendor_attr(&vendor, &data, &len)) <= 0) {
+ log_Printf(LogERROR, "rad_get_vendor_attr: %s (failing!)\n",
+ rad_strerror(r->cx.rad));
+ auth_Failure(r->cx.auth);
+ rad_close(r->cx.rad);
+ return;
+ }
+
+ switch (vendor) {
+ case RAD_VENDOR_MICROSOFT:
+ switch (res) {
+#ifndef NODES
+ case RAD_MICROSOFT_MS_CHAP_ERROR:
+ free(r->errstr);
+ if (len == 0)
+ r->errstr = NULL;
+ else {
+ if (len < 3 || ((const char *)data)[1] != '=') {
+ /*
+ * Only point at the String field if we don't think the
+ * peer has misformatted the response.
+ */
+ data = (const char *)data + 1;
+ len--;
+ } else
+ log_Printf(LogWARN, "Warning: The MS-CHAP-Error "
+ "attribute is mis-formatted. Compensating\n");
+ if ((r->errstr = rad_cvt_string((const char *)data,
+ len)) == NULL) {
+ log_Printf(LogERROR, "rad_cvt_string: %s\n",
+ rad_strerror(r->cx.rad));
+ auth_Failure(r->cx.auth);
+ rad_close(r->cx.rad);
+ return;
+ }
+ log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE,
+ " MS-CHAP-Error \"%s\"\n", r->errstr);
+ }
+ break;
+
+ case RAD_MICROSOFT_MS_CHAP2_SUCCESS:
+ free(r->msrepstr);
+ if (len == 0)
+ r->msrepstr = NULL;
+ else {
+ if (len < 3 || ((const char *)data)[1] != '=') {
+ /*
+ * Only point at the String field if we don't think the
+ * peer has misformatted the response.
+ */
+ data = (const char *)data + 1;
+ len--;
+ } else
+ log_Printf(LogWARN, "Warning: The MS-CHAP2-Success "
+ "attribute is mis-formatted. Compensating\n");
+ if ((r->msrepstr = rad_cvt_string((const char *)data,
+ len)) == NULL) {
+ log_Printf(LogERROR, "rad_cvt_string: %s\n",
+ rad_strerror(r->cx.rad));
+ auth_Failure(r->cx.auth);
+ rad_close(r->cx.rad);
+ return;
+ }
+ log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE,
+ " MS-CHAP2-Success \"%s\"\n", r->msrepstr);
+ }
+ break;
+
+ case RAD_MICROSOFT_MS_MPPE_ENCRYPTION_POLICY:
+ r->mppe.policy = rad_cvt_int(data);
+ log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE,
+ " MS-MPPE-Encryption-Policy %s\n",
+ radius_policyname(r->mppe.policy));
+ break;
+
+ case RAD_MICROSOFT_MS_MPPE_ENCRYPTION_TYPES:
+ r->mppe.types = rad_cvt_int(data);
+ log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE,
+ " MS-MPPE-Encryption-Types %s\n",
+ radius_typesname(r->mppe.types));
+ break;
+
+ case RAD_MICROSOFT_MS_MPPE_RECV_KEY:
+ free(r->mppe.recvkey);
+ demangle(r, data, len, &r->mppe.recvkey, &r->mppe.recvkeylen);
+ log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE,
+ " MS-MPPE-Recv-Key ********\n");
+ break;
+
+ case RAD_MICROSOFT_MS_MPPE_SEND_KEY:
+ demangle(r, data, len, &r->mppe.sendkey, &r->mppe.sendkeylen);
+ log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE,
+ " MS-MPPE-Send-Key ********\n");
+ break;
+#endif
+
+ default:
+ log_Printf(LogDEBUG, "Dropping MICROSOFT vendor specific "
+ "RADIUS attribute %d\n", res);
+ break;
+ }
+ break;
+
+ default:
+ log_Printf(LogDEBUG, "Dropping vendor %lu RADIUS attribute %d\n",
+ (unsigned long)vendor, res);
+ break;
+ }
+ break;
+
+ default:
+ log_Printf(LogDEBUG, "Dropping RADIUS attribute %d\n", res);
+ break;
+ }
+ }
+
+ if (res == -1) {
+ log_Printf(LogERROR, "rad_get_attr: %s (failing!)\n",
+ rad_strerror(r->cx.rad));
+ auth_Failure(r->cx.auth);
+ } else if (got == RAD_ACCESS_REJECT)
+ auth_Failure(r->cx.auth);
+ else {
+ r->valid = 1;
+ auth_Success(r->cx.auth);
+ }
+ rad_close(r->cx.rad);
+}
+
+/*
+ * We've either timed out or select()ed on the read descriptor
+ */
+static void
+radius_Continue(struct radius *r, int sel)
+{
+ struct timeval tv;
+ int got;
+
+ timer_Stop(&r->cx.timer);
+ if ((got = rad_continue_send_request(r->cx.rad, sel, &r->cx.fd, &tv)) == 0) {
+ log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE,
+ "Radius: Request re-sent\n");
+ r->cx.timer.load = tv.tv_usec / TICKUNIT + tv.tv_sec * SECTICKS;
+ timer_Start(&r->cx.timer);
+ return;
+ }
+
+ radius_Process(r, got);
+}
+
+/*
+ * Time to call rad_continue_send_request() - timed out.
+ */
+static void
+radius_Timeout(void *v)
+{
+ radius_Continue((struct radius *)v, 0);
+}
+
+/*
+ * Time to call rad_continue_send_request() - something to read.
+ */
+static void
+radius_Read(struct fdescriptor *d, struct bundle *bundle __unused,
+ const fd_set *fdset __unused)
+{
+ radius_Continue(descriptor2radius(d), 1);
+}
+
+/*
+ * Flush any pending transactions
+ */
+void
+radius_Flush(struct radius *r)
+{
+ struct timeval tv;
+ fd_set s;
+
+ while (r->cx.fd != -1) {
+ FD_ZERO(&s);
+ FD_SET(r->cx.fd, &s);
+ tv.tv_sec = 0;
+ tv.tv_usec = TICKUNIT;
+ select(r->cx.fd + 1, &s, NULL, NULL, &tv);
+ radius_Continue(r, 1);
+ }
+}
+
+/*
+ * Behave as a struct fdescriptor (descriptor.h)
+ */
+static int
+radius_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w __unused,
+ fd_set *e __unused, int *n)
+{
+ struct radius *rad = descriptor2radius(d);
+
+ if (r && rad->cx.fd != -1) {
+ FD_SET(rad->cx.fd, r);
+ if (*n < rad->cx.fd + 1)
+ *n = rad->cx.fd + 1;
+ log_Printf(LogTIMER, "Radius: fdset(r) %d\n", rad->cx.fd);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Behave as a struct fdescriptor (descriptor.h)
+ */
+static int
+radius_IsSet(struct fdescriptor *d, const fd_set *fdset)
+{
+ struct radius *r = descriptor2radius(d);
+
+ return r && r->cx.fd != -1 && FD_ISSET(r->cx.fd, fdset);
+}
+
+/*
+ * Behave as a struct fdescriptor (descriptor.h)
+ */
+static int
+radius_Write(struct fdescriptor *d __unused, struct bundle *bundle __unused,
+ const fd_set *fdset __unused)
+{
+ /* We never want to write here ! */
+ log_Printf(LogALERT, "radius_Write: Internal error: Bad call !\n");
+ return 0;
+}
+
+/*
+ * Initialise ourselves
+ */
+void
+radius_Init(struct radius *r)
+{
+ r->desc.type = RADIUS_DESCRIPTOR;
+ r->desc.UpdateSet = radius_UpdateSet;
+ r->desc.IsSet = radius_IsSet;
+ r->desc.Read = radius_Read;
+ r->desc.Write = radius_Write;
+ r->cx.fd = -1;
+ r->cx.rad = NULL;
+ memset(&r->cx.timer, '\0', sizeof r->cx.timer);
+ r->cx.auth = NULL;
+ r->valid = 0;
+ r->vj = 0;
+ r->ip.s_addr = INADDR_ANY;
+ r->mask.s_addr = INADDR_NONE;
+ r->routes = NULL;
+ r->mtu = DEF_MTU;
+ r->msrepstr = NULL;
+ r->repstr = NULL;
+#ifndef NOINET6
+ r->ipv6prefix = NULL;
+ r->ipv6routes = NULL;
+#endif
+ r->errstr = NULL;
+ r->mppe.policy = 0;
+ r->mppe.types = 0;
+ r->mppe.recvkey = NULL;
+ r->mppe.recvkeylen = 0;
+ r->mppe.sendkey = NULL;
+ r->mppe.sendkeylen = 0;
+ *r->cfg.file = '\0';;
+ log_Printf(LogDEBUG, "Radius: radius_Init\n");
+}
+
+/*
+ * Forget everything and go back to initialised state.
+ */
+void
+radius_Destroy(struct radius *r)
+{
+ r->valid = 0;
+ log_Printf(LogDEBUG, "Radius: radius_Destroy\n");
+ timer_Stop(&r->cx.timer);
+ route_DeleteAll(&r->routes);
+#ifndef NOINET6
+ route_DeleteAll(&r->ipv6routes);
+#endif
+ free(r->filterid);
+ r->filterid = NULL;
+ free(r->msrepstr);
+ r->msrepstr = NULL;
+ free(r->repstr);
+ r->repstr = NULL;
+#ifndef NOINET6
+ free(r->ipv6prefix);
+ r->ipv6prefix = NULL;
+#endif
+ free(r->errstr);
+ r->errstr = NULL;
+ free(r->mppe.recvkey);
+ r->mppe.recvkey = NULL;
+ r->mppe.recvkeylen = 0;
+ free(r->mppe.sendkey);
+ r->mppe.sendkey = NULL;
+ r->mppe.sendkeylen = 0;
+ if (r->cx.fd != -1) {
+ r->cx.fd = -1;
+ rad_close(r->cx.rad);
+ }
+}
+
+static int
+radius_put_physical_details(struct radius *rad, struct physical *p)
+{
+ int slot, type;
+
+ type = RAD_VIRTUAL;
+ if (p->handler)
+ switch (p->handler->type) {
+ case I4B_DEVICE:
+ type = RAD_ISDN_SYNC;
+ break;
+
+ case TTY_DEVICE:
+ type = RAD_ASYNC;
+ break;
+
+ case ETHER_DEVICE:
+ type = RAD_ETHERNET;
+ break;
+
+ case TCP_DEVICE:
+ case UDP_DEVICE:
+ case EXEC_DEVICE:
+ case ATM_DEVICE:
+ case NG_DEVICE:
+ type = RAD_VIRTUAL;
+ break;
+ }
+
+ if (rad_put_int(rad->cx.rad, RAD_NAS_PORT_TYPE, type) != 0) {
+ log_Printf(LogERROR, "rad_put: rad_put_int: %s\n", rad_strerror(rad->cx.rad));
+ rad_close(rad->cx.rad);
+ return 0;
+ }
+
+ switch (rad->port_id_type) {
+ case RPI_PID:
+ slot = (int)getpid();
+ break;
+ case RPI_IFNUM:
+ slot = p->dl->bundle->iface->index;
+ break;
+ case RPI_TUNNUM:
+ slot = p->dl->bundle->unit;
+ break;
+ case RPI_DEFAULT:
+ default:
+ slot = physical_Slot(p);
+ break;
+ }
+
+ if (slot >= 0)
+ if (rad_put_int(rad->cx.rad, RAD_NAS_PORT, slot) != 0) {
+ log_Printf(LogERROR, "rad_put: rad_put_int: %s\n", rad_strerror(rad->cx.rad));
+ rad_close(rad->cx.rad);
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * Start an authentication request to the RADIUS server.
+ */
+int
+radius_Authenticate(struct radius *r, struct authinfo *authp, const char *name,
+ const char *key, int klen, const char *nchallenge,
+ int nclen)
+{
+ char hostname[MAXHOSTNAMELEN];
+ struct timeval tv;
+ const char *what = "questionable"; /* silence warnings! */
+ char *mac_addr;
+ int got;
+ struct hostent *hp;
+ struct in_addr hostaddr;
+#ifndef NODES
+ struct mschap_response msresp;
+ struct mschap2_response msresp2;
+ const struct MSCHAPv2_resp *keyv2;
+#endif
+
+ if (!*r->cfg.file)
+ return 0;
+
+ if (r->cx.fd != -1)
+ /*
+ * We assume that our name/key/challenge is the same as last time,
+ * and just continue to wait for the RADIUS server(s).
+ */
+ return 1;
+
+ radius_Destroy(r);
+
+ if ((r->cx.rad = rad_auth_open()) == NULL) {
+ log_Printf(LogERROR, "rad_auth_open: %s\n", strerror(errno));
+ return 0;
+ }
+
+ if (rad_config(r->cx.rad, r->cfg.file) != 0) {
+ log_Printf(LogERROR, "rad_config: %s\n", rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return 0;
+ }
+
+ if (rad_create_request(r->cx.rad, RAD_ACCESS_REQUEST) != 0) {
+ log_Printf(LogERROR, "rad_create_request: %s\n", rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return 0;
+ }
+
+ if (rad_put_string(r->cx.rad, RAD_USER_NAME, name) != 0 ||
+ rad_put_int(r->cx.rad, RAD_SERVICE_TYPE, RAD_FRAMED) != 0 ||
+ rad_put_int(r->cx.rad, RAD_FRAMED_PROTOCOL, RAD_PPP) != 0) {
+ log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return 0;
+ }
+
+ switch (authp->physical->link.lcp.want_auth) {
+ case PROTO_PAP:
+ /* We're talking PAP */
+ if (rad_put_attr(r->cx.rad, RAD_USER_PASSWORD, key, klen) != 0) {
+ log_Printf(LogERROR, "PAP: rad_put_string: %s\n",
+ rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return 0;
+ }
+ what = "PAP";
+ break;
+
+ case PROTO_CHAP:
+ switch (authp->physical->link.lcp.want_authtype) {
+ case 0x5:
+ if (rad_put_attr(r->cx.rad, RAD_CHAP_PASSWORD, key, klen) != 0 ||
+ rad_put_attr(r->cx.rad, RAD_CHAP_CHALLENGE, nchallenge, nclen) != 0) {
+ log_Printf(LogERROR, "CHAP: rad_put_string: %s\n",
+ rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return 0;
+ }
+ what = "CHAP";
+ break;
+
+#ifndef NODES
+ case 0x80:
+ if (klen != 50) {
+ log_Printf(LogERROR, "CHAP80: Unrecognised key length %d\n", klen);
+ rad_close(r->cx.rad);
+ return 0;
+ }
+
+ rad_put_vendor_attr(r->cx.rad, RAD_VENDOR_MICROSOFT,
+ RAD_MICROSOFT_MS_CHAP_CHALLENGE, nchallenge, nclen);
+ msresp.ident = *key;
+ msresp.flags = 0x01;
+ memcpy(msresp.lm_response, key + 1, 24);
+ memcpy(msresp.nt_response, key + 25, 24);
+ rad_put_vendor_attr(r->cx.rad, RAD_VENDOR_MICROSOFT,
+ RAD_MICROSOFT_MS_CHAP_RESPONSE, &msresp,
+ sizeof msresp);
+ what = "MSCHAP";
+ break;
+
+ case 0x81:
+ if (klen != sizeof(*keyv2) + 1) {
+ log_Printf(LogERROR, "CHAP81: Unrecognised key length %d\n", klen);
+ rad_close(r->cx.rad);
+ return 0;
+ }
+
+ keyv2 = (const struct MSCHAPv2_resp *)(key + 1);
+ rad_put_vendor_attr(r->cx.rad, RAD_VENDOR_MICROSOFT,
+ RAD_MICROSOFT_MS_CHAP_CHALLENGE, nchallenge, nclen);
+ msresp2.ident = *key;
+ msresp2.flags = keyv2->Flags;
+ memcpy(msresp2.response, keyv2->NTResponse, sizeof msresp2.response);
+ memset(msresp2.reserved, '\0', sizeof msresp2.reserved);
+ memcpy(msresp2.pchallenge, keyv2->PeerChallenge,
+ sizeof msresp2.pchallenge);
+ rad_put_vendor_attr(r->cx.rad, RAD_VENDOR_MICROSOFT,
+ RAD_MICROSOFT_MS_CHAP2_RESPONSE, &msresp2,
+ sizeof msresp2);
+ what = "MSCHAPv2";
+ break;
+#endif
+ default:
+ log_Printf(LogERROR, "CHAP: Unrecognised type 0x%02x\n",
+ authp->physical->link.lcp.want_authtype);
+ rad_close(r->cx.rad);
+ return 0;
+ }
+ }
+
+ if (gethostname(hostname, sizeof hostname) != 0)
+ log_Printf(LogERROR, "rad_put: gethostname(): %s\n", strerror(errno));
+ else {
+ if (Enabled(authp->physical->dl->bundle, OPT_NAS_IP_ADDRESS) &&
+ (hp = gethostbyname(hostname)) != NULL) {
+ hostaddr.s_addr = *(u_long *)hp->h_addr;
+ if (rad_put_addr(r->cx.rad, RAD_NAS_IP_ADDRESS, hostaddr) != 0) {
+ log_Printf(LogERROR, "rad_put: rad_put_string: %s\n",
+ rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return 0;
+ }
+ }
+ if (Enabled(authp->physical->dl->bundle, OPT_NAS_IDENTIFIER) &&
+ rad_put_string(r->cx.rad, RAD_NAS_IDENTIFIER, hostname) != 0) {
+ log_Printf(LogERROR, "rad_put: rad_put_string: %s\n",
+ rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return 0;
+ }
+ }
+
+ if ((mac_addr = getenv("HISMACADDR")) != NULL &&
+ rad_put_string(r->cx.rad, RAD_CALLING_STATION_ID, mac_addr) != 0) {
+ log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return 0;
+ }
+
+ radius_put_physical_details(r, authp->physical);
+
+ log_Printf(LogRADIUS, "Radius(auth): %s data sent for %s\n", what, name);
+
+ r->cx.auth = authp;
+ if ((got = rad_init_send_request(r->cx.rad, &r->cx.fd, &tv)))
+ radius_Process(r, got);
+ else {
+ log_Printf(log_IsKept(LogRADIUS) ? LogRADIUS : LogPHASE,
+ "Radius: Request sent\n");
+ log_Printf(LogDEBUG, "Using radius_Timeout [%p]\n", radius_Timeout);
+ r->cx.timer.load = tv.tv_usec / TICKUNIT + tv.tv_sec * SECTICKS;
+ r->cx.timer.func = radius_Timeout;
+ r->cx.timer.name = "radius auth";
+ r->cx.timer.arg = r;
+ timer_Start(&r->cx.timer);
+ }
+
+ return 1;
+}
+
+/* Fetch IP, netmask from IPCP */
+void
+radius_Account_Set_Ip(struct radacct *ac, struct in_addr *peer_ip,
+ struct in_addr *netmask)
+{
+ ac->proto = PROTO_IPCP;
+ memcpy(&ac->peer.ip.addr, peer_ip, sizeof(ac->peer.ip.addr));
+ memcpy(&ac->peer.ip.mask, netmask, sizeof(ac->peer.ip.mask));
+}
+
+#ifndef NOINET6
+/* Fetch interface-id from IPV6CP */
+void
+radius_Account_Set_Ipv6(struct radacct *ac, u_char *ifid)
+{
+ ac->proto = PROTO_IPV6CP;
+ memcpy(&ac->peer.ipv6.ifid, ifid, sizeof(ac->peer.ipv6.ifid));
+}
+#endif
+
+/*
+ * Send an accounting request to the RADIUS server
+ */
+void
+radius_Account(struct radius *r, struct radacct *ac, struct datalink *dl,
+ int acct_type, struct pppThroughput *stats)
+{
+ struct timeval tv;
+ int got;
+ char hostname[MAXHOSTNAMELEN];
+ char *mac_addr;
+ struct hostent *hp;
+ struct in_addr hostaddr;
+
+ if (!*r->cfg.file)
+ return;
+
+ if (r->cx.fd != -1)
+ /*
+ * We assume that our name/key/challenge is the same as last time,
+ * and just continue to wait for the RADIUS server(s).
+ */
+ return;
+
+ timer_Stop(&r->cx.timer);
+
+ if ((r->cx.rad = rad_acct_open()) == NULL) {
+ log_Printf(LogERROR, "rad_auth_open: %s\n", strerror(errno));
+ return;
+ }
+
+ if (rad_config(r->cx.rad, r->cfg.file) != 0) {
+ log_Printf(LogERROR, "rad_config: %s\n", rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return;
+ }
+
+ if (rad_create_request(r->cx.rad, RAD_ACCOUNTING_REQUEST) != 0) {
+ log_Printf(LogERROR, "rad_create_request: %s\n", rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return;
+ }
+
+ /* Grab some accounting data and initialize structure */
+ if (acct_type == RAD_START) {
+ ac->rad_parent = r;
+ /* Fetch username from datalink */
+ strncpy(ac->user_name, dl->peer.authname, sizeof ac->user_name);
+ ac->user_name[AUTHLEN-1] = '\0';
+
+ ac->authentic = 2; /* Assume RADIUS verified auth data */
+
+ /* Generate a session ID */
+ snprintf(ac->session_id, sizeof ac->session_id, "%s%ld-%s%lu",
+ dl->bundle->cfg.auth.name, (long)getpid(),
+ dl->peer.authname, (unsigned long)stats->uptime);
+
+ /* And grab our MP socket name */
+ snprintf(ac->multi_session_id, sizeof ac->multi_session_id, "%s",
+ dl->bundle->ncp.mp.active ?
+ dl->bundle->ncp.mp.server.socket.sun_path : "");
+ };
+
+ if (rad_put_string(r->cx.rad, RAD_USER_NAME, ac->user_name) != 0 ||
+ rad_put_int(r->cx.rad, RAD_SERVICE_TYPE, RAD_FRAMED) != 0 ||
+ rad_put_int(r->cx.rad, RAD_FRAMED_PROTOCOL, RAD_PPP) != 0) {
+ log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return;
+ }
+ switch (ac->proto) {
+ case PROTO_IPCP:
+ if (rad_put_addr(r->cx.rad, RAD_FRAMED_IP_ADDRESS,
+ ac->peer.ip.addr) != 0 ||
+ rad_put_addr(r->cx.rad, RAD_FRAMED_IP_NETMASK,
+ ac->peer.ip.mask) != 0) {
+ log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return;
+ }
+ break;
+#ifndef NOINET6
+ case PROTO_IPV6CP:
+ if (rad_put_attr(r->cx.rad, RAD_FRAMED_INTERFACE_ID, ac->peer.ipv6.ifid,
+ sizeof(ac->peer.ipv6.ifid)) != 0) {
+ log_Printf(LogERROR, "rad_put_attr: %s\n", rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return;
+ }
+ if (r->ipv6prefix) {
+ /*
+ * Since PPP doesn't delegate an IPv6 prefix to a peer,
+ * Framed-IPv6-Prefix may be not used, actually.
+ */
+ if (rad_put_attr(r->cx.rad, RAD_FRAMED_IPV6_PREFIX, r->ipv6prefix,
+ sizeof(struct in6_addr) + 2) != 0) {
+ log_Printf(LogERROR, "rad_put_attr: %s\n", rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return;
+ }
+ }
+ break;
+#endif
+ default:
+ /* We don't log any protocol specific information */
+ break;
+ }
+
+ if ((mac_addr = getenv("HISMACADDR")) != NULL &&
+ rad_put_string(r->cx.rad, RAD_CALLING_STATION_ID, mac_addr) != 0) {
+ log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return;
+ }
+
+ if (gethostname(hostname, sizeof hostname) != 0)
+ log_Printf(LogERROR, "rad_put: gethostname(): %s\n", strerror(errno));
+ else {
+ if (Enabled(dl->bundle, OPT_NAS_IP_ADDRESS) &&
+ (hp = gethostbyname(hostname)) != NULL) {
+ hostaddr.s_addr = *(u_long *)hp->h_addr;
+ if (rad_put_addr(r->cx.rad, RAD_NAS_IP_ADDRESS, hostaddr) != 0) {
+ log_Printf(LogERROR, "rad_put: rad_put_string: %s\n",
+ rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return;
+ }
+ }
+ if (Enabled(dl->bundle, OPT_NAS_IDENTIFIER) &&
+ rad_put_string(r->cx.rad, RAD_NAS_IDENTIFIER, hostname) != 0) {
+ log_Printf(LogERROR, "rad_put: rad_put_string: %s\n",
+ rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return;
+ }
+ }
+
+ radius_put_physical_details(r, dl->physical);
+
+ if (rad_put_int(r->cx.rad, RAD_ACCT_STATUS_TYPE, acct_type) != 0 ||
+ rad_put_string(r->cx.rad, RAD_ACCT_SESSION_ID, ac->session_id) != 0 ||
+ rad_put_string(r->cx.rad, RAD_ACCT_MULTI_SESSION_ID,
+ ac->multi_session_id) != 0 ||
+ rad_put_int(r->cx.rad, RAD_ACCT_DELAY_TIME, 0) != 0) {
+/* XXX ACCT_DELAY_TIME should be increased each time a packet is waiting */
+ log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return;
+ }
+
+ if (acct_type == RAD_STOP || acct_type == RAD_ALIVE)
+ /* Show some statistics */
+ if (rad_put_int(r->cx.rad, RAD_ACCT_INPUT_OCTETS, stats->OctetsIn % UINT32_MAX) != 0 ||
+ rad_put_int(r->cx.rad, RAD_ACCT_INPUT_GIGAWORDS, stats->OctetsIn / UINT32_MAX) != 0 ||
+ rad_put_int(r->cx.rad, RAD_ACCT_INPUT_PACKETS, stats->PacketsIn) != 0 ||
+ rad_put_int(r->cx.rad, RAD_ACCT_OUTPUT_OCTETS, stats->OctetsOut % UINT32_MAX) != 0 ||
+ rad_put_int(r->cx.rad, RAD_ACCT_OUTPUT_GIGAWORDS, stats->OctetsOut / UINT32_MAX) != 0 ||
+ rad_put_int(r->cx.rad, RAD_ACCT_OUTPUT_PACKETS, stats->PacketsOut)
+ != 0 ||
+ rad_put_int(r->cx.rad, RAD_ACCT_SESSION_TIME, throughput_uptime(stats))
+ != 0) {
+ log_Printf(LogERROR, "rad_put: %s\n", rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return;
+ }
+
+ if (log_IsKept(LogPHASE) || log_IsKept(LogRADIUS)) {
+ const char *what;
+ int level;
+
+ switch (acct_type) {
+ case RAD_START:
+ what = "START";
+ level = log_IsKept(LogPHASE) ? LogPHASE : LogRADIUS;
+ break;
+ case RAD_STOP:
+ what = "STOP";
+ level = log_IsKept(LogPHASE) ? LogPHASE : LogRADIUS;
+ break;
+ case RAD_ALIVE:
+ what = "ALIVE";
+ level = LogRADIUS;
+ break;
+ default:
+ what = "<unknown>";
+ level = log_IsKept(LogPHASE) ? LogPHASE : LogRADIUS;
+ break;
+ }
+ log_Printf(level, "Radius(acct): %s data sent\n", what);
+ }
+
+ r->cx.auth = NULL; /* Not valid for accounting requests */
+ if ((got = rad_init_send_request(r->cx.rad, &r->cx.fd, &tv)))
+ radius_Process(r, got);
+ else {
+ log_Printf(LogDEBUG, "Using radius_Timeout [%p]\n", radius_Timeout);
+ r->cx.timer.load = tv.tv_usec / TICKUNIT + tv.tv_sec * SECTICKS;
+ r->cx.timer.func = radius_Timeout;
+ r->cx.timer.name = "radius acct";
+ r->cx.timer.arg = r;
+ timer_Start(&r->cx.timer);
+ }
+}
+
+/*
+ * How do things look at the moment ?
+ */
+void
+radius_Show(struct radius *r, struct prompt *p)
+{
+ prompt_Printf(p, " Radius config: %s",
+ *r->cfg.file ? r->cfg.file : "none");
+ if (r->valid) {
+ prompt_Printf(p, "\n IP: %s\n", inet_ntoa(r->ip));
+ prompt_Printf(p, " Netmask: %s\n", inet_ntoa(r->mask));
+ prompt_Printf(p, " MTU: %lu\n", r->mtu);
+ prompt_Printf(p, " VJ: %sabled\n", r->vj ? "en" : "dis");
+ prompt_Printf(p, " Message: %s\n", r->repstr ? r->repstr : "");
+ prompt_Printf(p, " MPPE Enc Policy: %s\n",
+ radius_policyname(r->mppe.policy));
+ prompt_Printf(p, " MPPE Enc Types: %s\n",
+ radius_typesname(r->mppe.types));
+ prompt_Printf(p, " MPPE Recv Key: %seceived\n",
+ r->mppe.recvkey ? "R" : "Not r");
+ prompt_Printf(p, " MPPE Send Key: %seceived\n",
+ r->mppe.sendkey ? "R" : "Not r");
+ prompt_Printf(p, " MS-CHAP2-Response: %s\n",
+ r->msrepstr ? r->msrepstr : "");
+ prompt_Printf(p, " Error Message: %s\n", r->errstr ? r->errstr : "");
+ if (r->routes)
+ route_ShowSticky(p, r->routes, " Routes", 16);
+#ifndef NOINET6
+ if (r->ipv6routes)
+ route_ShowSticky(p, r->ipv6routes, " IPv6 Routes", 16);
+#endif
+ } else
+ prompt_Printf(p, " (not authenticated)\n");
+}
+
+static void
+radius_alive(void *v)
+{
+ struct bundle *bundle = (struct bundle *)v;
+
+ timer_Stop(&bundle->radius.alive.timer);
+ bundle->radius.alive.timer.load = bundle->radius.alive.interval * SECTICKS;
+ if (bundle->radius.alive.timer.load) {
+ radius_Account(&bundle->radius, &bundle->radacct,
+ bundle->links, RAD_ALIVE, &bundle->ncp.ipcp.throughput);
+ timer_Start(&bundle->radius.alive.timer);
+ }
+}
+
+void
+radius_StartTimer(struct bundle *bundle)
+{
+ if (bundle->radius.cfg.file && bundle->radius.alive.interval) {
+ bundle->radius.alive.timer.func = radius_alive;
+ bundle->radius.alive.timer.name = "radius alive";
+ bundle->radius.alive.timer.load = bundle->radius.alive.interval * SECTICKS;
+ bundle->radius.alive.timer.arg = bundle;
+ radius_alive(bundle);
+ }
+}
+
+void
+radius_StopTimer(struct radius *r)
+{
+ timer_Stop(&r->alive.timer);
+}
diff --git a/usr.sbin/ppp/radius.h b/usr.sbin/ppp/radius.h
new file mode 100644
index 0000000..ab144a5
--- /dev/null
+++ b/usr.sbin/ppp/radius.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright 1999 Internet Business Solutions Ltd., Switzerland
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+#define MPPE_POLICY_ALLOWED 1
+#define MPPE_POLICY_REQUIRED 2
+
+#define MPPE_TYPE_40BIT 2
+#define MPPE_TYPE_128BIT 4
+
+#define RPI_DEFAULT 1
+#define RPI_PID 2
+#define RPI_IFNUM 3
+#define RPI_TUNNUM 4
+
+struct radius {
+ struct fdescriptor desc; /* We're a sort of (selectable) fdescriptor */
+ struct {
+ int fd; /* We're selecting on this */
+ struct rad_handle *rad; /* Using this to talk to our lib */
+ struct pppTimer timer; /* for this long */
+ struct authinfo *auth; /* Tell this about success/failure */
+ } cx;
+ unsigned valid : 1; /* Is this structure valid ? */
+ unsigned vj : 1; /* FRAMED Compression */
+ struct in_addr ip; /* FRAMED IP */
+ struct in_addr mask; /* FRAMED Netmask */
+ unsigned long mtu; /* FRAMED MTU */
+ unsigned long sessiontime; /* Session-Timeout */
+ char *filterid; /* FRAMED Filter Id */
+ struct sticky_route *routes; /* FRAMED Routes */
+ char *msrepstr; /* MS-CHAP2-Response */
+ char *repstr; /* Reply-Message */
+ char *errstr; /* Error-Message */
+#ifndef NOINET6
+ uint8_t *ipv6prefix; /* FRAMED IPv6 Prefix */
+ struct sticky_route *ipv6routes; /* FRAMED IPv6 Routes */
+#endif
+ struct {
+ int policy; /* MPPE_POLICY_* */
+ int types; /* MPPE_TYPE_*BIT bitmask */
+ char *recvkey;
+ size_t recvkeylen;
+ char *sendkey;
+ size_t sendkeylen;
+ } mppe;
+ struct {
+ char file[PATH_MAX]; /* Radius config file */
+ } cfg;
+ struct {
+ struct pppTimer timer; /* for this long */
+ int interval;
+ } alive;
+ short unsigned int port_id_type;
+};
+
+struct radacct {
+ struct radius *rad_parent; /* "Parent" struct radius stored in bundle */
+ char user_name[AUTHLEN]; /* Session User-Name */
+ char session_id[256]; /* Unique session ID */
+ char multi_session_id[51]; /* Unique MP session ID */
+ int authentic; /* How the session has been authenticated */
+ u_short proto; /* Protocol number */
+ union {
+ struct {
+ struct in_addr addr;
+ struct in_addr mask;
+ } ip;
+#ifndef NOINET6
+ struct {
+ u_char ifid[8];
+ } ipv6;
+#endif
+ } peer;
+};
+
+#define descriptor2radius(d) \
+ ((d)->type == RADIUS_DESCRIPTOR ? (struct radius *)(d) : NULL)
+
+struct bundle;
+
+extern void radius_Flush(struct radius *);
+extern void radius_Init(struct radius *);
+extern void radius_Destroy(struct radius *);
+
+extern void radius_Show(struct radius *, struct prompt *);
+extern void radius_StartTimer(struct bundle *);
+extern void radius_StopTimer(struct radius *);
+extern int radius_Authenticate(struct radius *, struct authinfo *,
+ const char *, const char *, int,
+ const char *, int);
+extern void radius_Account_Set_Ip(struct radacct *, struct in_addr *,
+ struct in_addr *);
+#ifndef NOINET6
+extern void radius_Account_Set_Ipv6(struct radacct *, u_char *);
+#endif
+extern void radius_Account(struct radius *, struct radacct *,
+ struct datalink *, int, struct pppThroughput *);
+
+/* An (int) parameter to radius_Account, from radlib.h */
+#if !defined(RAD_START)
+#define RAD_START 1
+#define RAD_STOP 2
+#endif
+
+#define RAD_ALIVE 3
+
+/* Get address from NAS pool */
+#define RADIUS_INADDR_POOL htonl(0xfffffffe) /* 255.255.255.254 */
diff --git a/usr.sbin/ppp/route.c b/usr.sbin/ppp/route.c
new file mode 100644
index 0000000..7609681
--- /dev/null
+++ b/usr.sbin/ppp/route.c
@@ -0,0 +1,939 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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/socket.h>
+#include <net/if_types.h>
+#include <net/route.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <net/if_dl.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/sysctl.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "iplist.h"
+#include "timer.h"
+#include "throughput.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "slcompress.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "route.h"
+#include "prompt.h"
+#include "iface.h"
+#include "id.h"
+
+
+static void
+p_sockaddr(struct prompt *prompt, struct sockaddr *phost,
+ struct sockaddr *pmask, int width)
+{
+ struct ncprange range;
+ char buf[29];
+ struct sockaddr_dl *dl = (struct sockaddr_dl *)phost;
+
+ if (log_IsKept(LogDEBUG)) {
+ char tmp[50];
+
+ log_Printf(LogDEBUG, "Found the following sockaddr:\n");
+ log_Printf(LogDEBUG, " Family %d, len %d\n",
+ (int)phost->sa_family, (int)phost->sa_len);
+ inet_ntop(phost->sa_family, phost->sa_data, tmp, sizeof tmp);
+ log_Printf(LogDEBUG, " Addr %s\n", tmp);
+ if (pmask) {
+ inet_ntop(pmask->sa_family, pmask->sa_data, tmp, sizeof tmp);
+ log_Printf(LogDEBUG, " Mask %s\n", tmp);
+ }
+ }
+
+ switch (phost->sa_family) {
+ case AF_INET:
+#ifndef NOINET6
+ case AF_INET6:
+#endif
+ ncprange_setsa(&range, phost, pmask);
+ if (ncprange_isdefault(&range))
+ prompt_Printf(prompt, "%-*s ", width - 1, "default");
+ else
+ prompt_Printf(prompt, "%-*s ", width - 1, ncprange_ntoa(&range));
+ return;
+
+ case AF_LINK:
+ if (dl->sdl_nlen)
+ snprintf(buf, sizeof buf, "%.*s", dl->sdl_nlen, dl->sdl_data);
+ else if (dl->sdl_alen) {
+ if (dl->sdl_type == IFT_ETHER) {
+ if (dl->sdl_alen < sizeof buf / 3) {
+ int f;
+ u_char *MAC;
+
+ MAC = (u_char *)dl->sdl_data + dl->sdl_nlen;
+ for (f = 0; f < dl->sdl_alen; f++)
+ sprintf(buf+f*3, "%02x:", MAC[f]);
+ buf[f*3-1] = '\0';
+ } else
+ strcpy(buf, "??:??:??:??:??:??");
+ } else
+ sprintf(buf, "<IFT type %d>", dl->sdl_type);
+ } else if (dl->sdl_slen)
+ sprintf(buf, "<slen %d?>", dl->sdl_slen);
+ else
+ sprintf(buf, "link#%d", dl->sdl_index);
+ break;
+
+ default:
+ sprintf(buf, "<AF type %d>", phost->sa_family);
+ break;
+ }
+
+ prompt_Printf(prompt, "%-*s ", width-1, buf);
+}
+
+static struct bits {
+ u_int32_t b_mask;
+ char b_val;
+} bits[] = {
+ { RTF_UP, 'U' },
+ { RTF_GATEWAY, 'G' },
+ { RTF_HOST, 'H' },
+ { RTF_REJECT, 'R' },
+ { RTF_DYNAMIC, 'D' },
+ { RTF_MODIFIED, 'M' },
+ { RTF_DONE, 'd' },
+ { RTF_XRESOLVE, 'X' },
+#ifdef RTF_CLONING
+ { RTF_CLONING, 'C' },
+#endif
+ { RTF_STATIC, 'S' },
+ { RTF_PROTO1, '1' },
+ { RTF_PROTO2, '2' },
+ { RTF_BLACKHOLE, 'B' },
+
+#ifdef RTF_LLINFO
+ { RTF_LLINFO, 'L' },
+#endif
+#ifdef RTF_CLONING
+ { RTF_CLONING, 'C' },
+#endif
+#ifdef RTF_WASCLONED
+ { RTF_WASCLONED, 'W' },
+#endif
+#ifdef RTF_PRCLONING
+ { RTF_PRCLONING, 'c' },
+#endif
+#ifdef RTF_PROTO3
+ { RTF_PROTO3, '3' },
+#endif
+#ifdef RTF_BROADCAST
+ { RTF_BROADCAST, 'b' },
+#endif
+ { 0, '\0' }
+};
+
+#ifndef RTF_WASCLONED
+#define RTF_WASCLONED (0)
+#endif
+
+static void
+p_flags(struct prompt *prompt, u_int32_t f, unsigned max)
+{
+ char name[33], *flags;
+ register struct bits *p = bits;
+
+ if (max > sizeof name - 1)
+ max = sizeof name - 1;
+
+ for (flags = name; p->b_mask && flags - name < (int)max; p++)
+ if (p->b_mask & f)
+ *flags++ = p->b_val;
+ *flags = '\0';
+ prompt_Printf(prompt, "%-*.*s", (int)max, (int)max, name);
+}
+
+static int route_nifs = -1;
+
+const char *
+Index2Nam(int idx)
+{
+ /*
+ * XXX: Maybe we should select() on the routing socket so that we can
+ * notice interfaces that come & go (PCCARD support).
+ * Or we could even support a signal that resets these so that
+ * the PCCARD insert/remove events can signal ppp.
+ */
+ static char **ifs; /* Figure these out once */
+ static int debug_done; /* Debug once */
+
+ if (idx > route_nifs || (idx > 0 && ifs[idx-1] == NULL)) {
+ int mib[6], have, had;
+ size_t needed;
+ char *buf, *ptr, *end;
+ struct sockaddr_dl *dl;
+ struct if_msghdr *ifm;
+
+ if (ifs) {
+ free(ifs);
+ ifs = NULL;
+ route_nifs = 0;
+ }
+ debug_done = 0;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = 0;
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0;
+
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
+ log_Printf(LogERROR, "Index2Nam: sysctl: estimate: %s\n",
+ strerror(errno));
+ return NumStr(idx, NULL, 0);
+ }
+ if ((buf = malloc(needed)) == NULL)
+ return NumStr(idx, NULL, 0);
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
+ free(buf);
+ return NumStr(idx, NULL, 0);
+ }
+ end = buf + needed;
+
+ have = 0;
+ for (ptr = buf; ptr < end; ptr += ifm->ifm_msglen) {
+ ifm = (struct if_msghdr *)ptr;
+ if (ifm->ifm_type != RTM_IFINFO)
+ continue;
+ dl = (struct sockaddr_dl *)(ifm + 1);
+ if (ifm->ifm_index > 0) {
+ if (ifm->ifm_index > have) {
+ char **newifs;
+
+ had = have;
+ have = ifm->ifm_index + 5;
+ if (had)
+ newifs = (char **)realloc(ifs, sizeof(char *) * have);
+ else
+ newifs = (char **)malloc(sizeof(char *) * have);
+ if (!newifs) {
+ log_Printf(LogDEBUG, "Index2Nam: %s\n", strerror(errno));
+ route_nifs = 0;
+ if (ifs) {
+ free(ifs);
+ ifs = NULL;
+ }
+ free(buf);
+ return NumStr(idx, NULL, 0);
+ }
+ ifs = newifs;
+ memset(ifs + had, '\0', sizeof(char *) * (have - had));
+ }
+ if (ifs[ifm->ifm_index-1] == NULL) {
+ ifs[ifm->ifm_index-1] = (char *)malloc(dl->sdl_nlen+1);
+ if (ifs[ifm->ifm_index-1] == NULL)
+ log_Printf(LogDEBUG, "Skipping interface %d: Out of memory\n",
+ ifm->ifm_index);
+ else {
+ memcpy(ifs[ifm->ifm_index-1], dl->sdl_data, dl->sdl_nlen);
+ ifs[ifm->ifm_index-1][dl->sdl_nlen] = '\0';
+ if (route_nifs < ifm->ifm_index)
+ route_nifs = ifm->ifm_index;
+ }
+ }
+ } else if (log_IsKept(LogDEBUG))
+ log_Printf(LogDEBUG, "Skipping out-of-range interface %d!\n",
+ ifm->ifm_index);
+ }
+ free(buf);
+ }
+
+ if (log_IsKept(LogDEBUG) && !debug_done) {
+ int f;
+
+ log_Printf(LogDEBUG, "Found the following interfaces:\n");
+ for (f = 0; f < route_nifs; f++)
+ if (ifs[f] != NULL)
+ log_Printf(LogDEBUG, " Index %d, name \"%s\"\n", f+1, ifs[f]);
+ debug_done = 1;
+ }
+
+ if (idx < 1 || idx > route_nifs || ifs[idx-1] == NULL)
+ return NumStr(idx, NULL, 0);
+
+ return ifs[idx-1];
+}
+
+void
+route_ParseHdr(struct rt_msghdr *rtm, struct sockaddr *sa[RTAX_MAX])
+{
+ char *wp;
+ int rtax;
+
+ wp = (char *)(rtm + 1);
+
+ for (rtax = 0; rtax < RTAX_MAX; rtax++)
+ if (rtm->rtm_addrs & (1 << rtax)) {
+ sa[rtax] = (struct sockaddr *)wp;
+ wp += ROUNDUP(sa[rtax]->sa_len);
+ if (sa[rtax]->sa_family == 0)
+ sa[rtax] = NULL; /* ??? */
+ } else
+ sa[rtax] = NULL;
+}
+
+int
+route_Show(struct cmdargs const *arg)
+{
+ struct rt_msghdr *rtm;
+ struct sockaddr *sa[RTAX_MAX];
+ char *sp, *ep, *cp;
+ size_t needed;
+ int mib[6];
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = 0;
+ mib[4] = NET_RT_DUMP;
+ mib[5] = 0;
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
+ log_Printf(LogERROR, "route_Show: sysctl: estimate: %s\n", strerror(errno));
+ return (1);
+ }
+ sp = malloc(needed);
+ if (sp == NULL)
+ return (1);
+ if (sysctl(mib, 6, sp, &needed, NULL, 0) < 0) {
+ log_Printf(LogERROR, "route_Show: sysctl: getroute: %s\n", strerror(errno));
+ free(sp);
+ return (1);
+ }
+ ep = sp + needed;
+
+ prompt_Printf(arg->prompt, "%-20s%-20sFlags Netif\n",
+ "Destination", "Gateway");
+ for (cp = sp; cp < ep; cp += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)cp;
+
+ route_ParseHdr(rtm, sa);
+
+ if (sa[RTAX_DST] && sa[RTAX_GATEWAY]) {
+ p_sockaddr(arg->prompt, sa[RTAX_DST], sa[RTAX_NETMASK], 20);
+ p_sockaddr(arg->prompt, sa[RTAX_GATEWAY], NULL, 20);
+
+ p_flags(arg->prompt, rtm->rtm_flags, 6);
+ prompt_Printf(arg->prompt, " %s\n", Index2Nam(rtm->rtm_index));
+ } else
+ prompt_Printf(arg->prompt, "<can't parse routing entry>\n");
+ }
+ free(sp);
+ return 0;
+}
+
+/*
+ * Delete routes associated with our interface
+ */
+void
+route_IfDelete(struct bundle *bundle, int all)
+{
+ struct rt_msghdr *rtm;
+ struct sockaddr *sa[RTAX_MAX];
+ struct ncprange range;
+ int pass;
+ size_t needed;
+ char *sp, *cp, *ep;
+ int mib[6];
+
+ log_Printf(LogDEBUG, "route_IfDelete (%d)\n", bundle->iface->index);
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = 0;
+ mib[4] = NET_RT_DUMP;
+ mib[5] = 0;
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
+ log_Printf(LogERROR, "route_IfDelete: sysctl: estimate: %s\n",
+ strerror(errno));
+ return;
+ }
+
+ sp = malloc(needed);
+ if (sp == NULL)
+ return;
+
+ if (sysctl(mib, 6, sp, &needed, NULL, 0) < 0) {
+ log_Printf(LogERROR, "route_IfDelete: sysctl: getroute: %s\n",
+ strerror(errno));
+ free(sp);
+ return;
+ }
+ ep = sp + needed;
+
+ for (pass = 0; pass < 2; pass++) {
+ /*
+ * We do 2 passes. The first deletes all cloned routes. The second
+ * deletes all non-cloned routes. This is done to avoid
+ * potential errors from trying to delete route X after route Y where
+ * route X was cloned from route Y (and is no longer there 'cos it
+ * may have gone with route Y).
+ */
+ if (RTF_WASCLONED == 0 && pass == 0)
+ /* So we can't tell ! */
+ continue;
+ for (cp = sp; cp < ep; cp += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)cp;
+ route_ParseHdr(rtm, sa);
+ if (rtm->rtm_index == bundle->iface->index &&
+ sa[RTAX_DST] && sa[RTAX_GATEWAY] &&
+ (sa[RTAX_DST]->sa_family == AF_INET
+#ifndef NOINET6
+ || sa[RTAX_DST]->sa_family == AF_INET6
+#endif
+ ) &&
+ (all || (rtm->rtm_flags & RTF_GATEWAY))) {
+ if (log_IsKept(LogDEBUG)) {
+ char gwstr[41];
+ struct ncpaddr gw;
+ ncprange_setsa(&range, sa[RTAX_DST], sa[RTAX_NETMASK]);
+ ncpaddr_setsa(&gw, sa[RTAX_GATEWAY]);
+ snprintf(gwstr, sizeof gwstr, "%s", ncpaddr_ntoa(&gw));
+ log_Printf(LogDEBUG, "Found %s %s\n", ncprange_ntoa(&range), gwstr);
+ }
+ if (sa[RTAX_GATEWAY]->sa_family == AF_INET ||
+#ifndef NOINET6
+ sa[RTAX_GATEWAY]->sa_family == AF_INET6 ||
+#endif
+ sa[RTAX_GATEWAY]->sa_family == AF_LINK) {
+ if ((pass == 0 && (rtm->rtm_flags & RTF_WASCLONED)) ||
+ (pass == 1 && !(rtm->rtm_flags & RTF_WASCLONED))) {
+ ncprange_setsa(&range, sa[RTAX_DST], sa[RTAX_NETMASK]);
+ rt_Set(bundle, RTM_DELETE, &range, NULL, 0, 0);
+ } else
+ log_Printf(LogDEBUG, "route_IfDelete: Skip it (pass %d)\n", pass);
+ } else
+ log_Printf(LogDEBUG,
+ "route_IfDelete: Can't remove routes for family %d\n",
+ sa[RTAX_GATEWAY]->sa_family);
+ }
+ }
+ }
+ free(sp);
+}
+
+
+/*
+ * Update the MTU on all routes for the given interface
+ */
+void
+route_UpdateMTU(struct bundle *bundle)
+{
+ struct rt_msghdr *rtm;
+ struct sockaddr *sa[RTAX_MAX];
+ struct ncprange dst;
+ size_t needed;
+ char *sp, *cp, *ep;
+ int mib[6];
+
+ log_Printf(LogDEBUG, "route_UpdateMTU (%d)\n", bundle->iface->index);
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = 0;
+ mib[4] = NET_RT_DUMP;
+ mib[5] = 0;
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
+ log_Printf(LogERROR, "route_IfDelete: sysctl: estimate: %s\n",
+ strerror(errno));
+ return;
+ }
+
+ sp = malloc(needed);
+ if (sp == NULL)
+ return;
+
+ if (sysctl(mib, 6, sp, &needed, NULL, 0) < 0) {
+ log_Printf(LogERROR, "route_IfDelete: sysctl: getroute: %s\n",
+ strerror(errno));
+ free(sp);
+ return;
+ }
+ ep = sp + needed;
+
+ for (cp = sp; cp < ep; cp += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)cp;
+ route_ParseHdr(rtm, sa);
+ if (sa[RTAX_DST] && (sa[RTAX_DST]->sa_family == AF_INET
+#ifndef NOINET6
+ || sa[RTAX_DST]->sa_family == AF_INET6
+#endif
+ ) &&
+ sa[RTAX_GATEWAY] && rtm->rtm_index == bundle->iface->index) {
+ if (log_IsKept(LogTCPIP)) {
+ ncprange_setsa(&dst, sa[RTAX_DST], sa[RTAX_NETMASK]);
+ log_Printf(LogTCPIP, "route_UpdateMTU: Netif: %d (%s), dst %s,"
+ " mtu %lu\n", rtm->rtm_index, Index2Nam(rtm->rtm_index),
+ ncprange_ntoa(&dst), bundle->iface->mtu);
+ }
+ rt_Update(bundle, sa[RTAX_DST], sa[RTAX_GATEWAY], sa[RTAX_NETMASK]);
+ }
+ }
+
+ free(sp);
+}
+
+int
+GetIfIndex(char *name)
+{
+ int idx;
+
+ idx = 1;
+ while (route_nifs == -1 || idx < route_nifs)
+ if (strcmp(Index2Nam(idx), name) == 0)
+ return idx;
+ else
+ idx++;
+ return -1;
+}
+
+void
+route_Change(struct bundle *bundle, struct sticky_route *r,
+ const struct ncpaddr *me, const struct ncpaddr *peer)
+{
+ struct ncpaddr dst;
+
+ for (; r; r = r->next) {
+ ncprange_getaddr(&r->dst, &dst);
+ if (ncpaddr_family(me) == AF_INET) {
+ if ((r->type & ROUTE_DSTMYADDR) && !ncpaddr_equal(&dst, me)) {
+ rt_Set(bundle, RTM_DELETE, &r->dst, NULL, 1, 0);
+ ncprange_sethost(&r->dst, me);
+ if (r->type & ROUTE_GWHISADDR)
+ ncpaddr_copy(&r->gw, peer);
+ } else if ((r->type & ROUTE_DSTHISADDR) && !ncpaddr_equal(&dst, peer)) {
+ rt_Set(bundle, RTM_DELETE, &r->dst, NULL, 1, 0);
+ ncprange_sethost(&r->dst, peer);
+ if (r->type & ROUTE_GWHISADDR)
+ ncpaddr_copy(&r->gw, peer);
+ } else if ((r->type & ROUTE_DSTDNS0) && !ncpaddr_equal(&dst, peer)) {
+ if (bundle->ncp.ipcp.ns.dns[0].s_addr == INADDR_NONE)
+ continue;
+ rt_Set(bundle, RTM_DELETE, &r->dst, NULL, 1, 0);
+ if (r->type & ROUTE_GWHISADDR)
+ ncpaddr_copy(&r->gw, peer);
+ } else if ((r->type & ROUTE_DSTDNS1) && !ncpaddr_equal(&dst, peer)) {
+ if (bundle->ncp.ipcp.ns.dns[1].s_addr == INADDR_NONE)
+ continue;
+ rt_Set(bundle, RTM_DELETE, &r->dst, NULL, 1, 0);
+ if (r->type & ROUTE_GWHISADDR)
+ ncpaddr_copy(&r->gw, peer);
+ } else if ((r->type & ROUTE_GWHISADDR) && !ncpaddr_equal(&r->gw, peer))
+ ncpaddr_copy(&r->gw, peer);
+#ifndef NOINET6
+ } else if (ncpaddr_family(me) == AF_INET6) {
+ if ((r->type & ROUTE_DSTMYADDR6) && !ncpaddr_equal(&dst, me)) {
+ rt_Set(bundle, RTM_DELETE, &r->dst, NULL, 1, 0);
+ ncprange_sethost(&r->dst, me);
+ if (r->type & ROUTE_GWHISADDR)
+ ncpaddr_copy(&r->gw, peer);
+ } else if ((r->type & ROUTE_DSTHISADDR6) && !ncpaddr_equal(&dst, peer)) {
+ rt_Set(bundle, RTM_DELETE, &r->dst, NULL, 1, 0);
+ ncprange_sethost(&r->dst, peer);
+ if (r->type & ROUTE_GWHISADDR)
+ ncpaddr_copy(&r->gw, peer);
+ } else if ((r->type & ROUTE_GWHISADDR6) && !ncpaddr_equal(&r->gw, peer))
+ ncpaddr_copy(&r->gw, peer);
+#endif
+ }
+ rt_Set(bundle, RTM_ADD, &r->dst, &r->gw, 1, 0);
+ }
+}
+
+void
+route_Add(struct sticky_route **rp, int type, const struct ncprange *dst,
+ const struct ncpaddr *gw)
+{
+ struct sticky_route *r;
+ int dsttype = type & ROUTE_DSTANY;
+
+ r = NULL;
+ while (*rp) {
+ if ((dsttype && dsttype == ((*rp)->type & ROUTE_DSTANY)) ||
+ (!dsttype && ncprange_equal(&(*rp)->dst, dst))) {
+ /* Oops, we already have this route - unlink it */
+ free(r); /* impossible really */
+ r = *rp;
+ *rp = r->next;
+ } else
+ rp = &(*rp)->next;
+ }
+
+ if (r == NULL) {
+ r = (struct sticky_route *)malloc(sizeof(struct sticky_route));
+ if (r == NULL) {
+ log_Printf(LogERROR, "route_Add: Out of memory!\n");
+ return;
+ }
+ }
+ r->type = type;
+ r->next = NULL;
+ ncprange_copy(&r->dst, dst);
+ ncpaddr_copy(&r->gw, gw);
+ *rp = r;
+}
+
+void
+route_Delete(struct sticky_route **rp, int type, const struct ncprange *dst)
+{
+ struct sticky_route *r;
+ int dsttype = type & ROUTE_DSTANY;
+
+ for (; *rp; rp = &(*rp)->next) {
+ if ((dsttype && dsttype == ((*rp)->type & ROUTE_DSTANY)) ||
+ (!dsttype && ncprange_equal(dst, &(*rp)->dst))) {
+ r = *rp;
+ *rp = r->next;
+ free(r);
+ break;
+ }
+ }
+}
+
+void
+route_DeleteAll(struct sticky_route **rp)
+{
+ struct sticky_route *r, *rn;
+
+ for (r = *rp; r; r = rn) {
+ rn = r->next;
+ free(r);
+ }
+ *rp = NULL;
+}
+
+void
+route_ShowSticky(struct prompt *p, struct sticky_route *r, const char *tag,
+ int indent)
+{
+ int tlen = strlen(tag);
+
+ if (tlen + 2 > indent)
+ prompt_Printf(p, "%s:\n%*s", tag, indent, "");
+ else
+ prompt_Printf(p, "%s:%*s", tag, indent - tlen - 1, "");
+
+ for (; r; r = r->next) {
+ prompt_Printf(p, "%*sadd ", tlen ? 0 : indent, "");
+ tlen = 0;
+ if (r->type & ROUTE_DSTMYADDR)
+ prompt_Printf(p, "MYADDR");
+ else if (r->type & ROUTE_DSTMYADDR6)
+ prompt_Printf(p, "MYADDR6");
+ else if (r->type & ROUTE_DSTHISADDR)
+ prompt_Printf(p, "HISADDR");
+ else if (r->type & ROUTE_DSTHISADDR6)
+ prompt_Printf(p, "HISADDR6");
+ else if (r->type & ROUTE_DSTDNS0)
+ prompt_Printf(p, "DNS0");
+ else if (r->type & ROUTE_DSTDNS1)
+ prompt_Printf(p, "DNS1");
+ else if (ncprange_isdefault(&r->dst))
+ prompt_Printf(p, "default");
+ else
+ prompt_Printf(p, "%s", ncprange_ntoa(&r->dst));
+
+ if (r->type & ROUTE_GWHISADDR)
+ prompt_Printf(p, " HISADDR\n");
+ else if (r->type & ROUTE_GWHISADDR6)
+ prompt_Printf(p, " HISADDR6\n");
+ else
+ prompt_Printf(p, " %s\n", ncpaddr_ntoa(&r->gw));
+ }
+}
+
+struct rtmsg {
+ struct rt_msghdr m_rtm;
+ char m_space[256];
+};
+
+static size_t
+memcpy_roundup(char *cp, const void *data, size_t len)
+{
+ size_t padlen;
+
+ padlen = ROUNDUP(len);
+ memcpy(cp, data, len);
+ if (padlen > len)
+ memset(cp + len, '\0', padlen - len);
+
+ return padlen;
+}
+
+#if defined(__KAME__) && !defined(NOINET6)
+static void
+add_scope(struct sockaddr *sa, int ifindex)
+{
+ struct sockaddr_in6 *sa6;
+
+ if (sa->sa_family != AF_INET6)
+ return;
+ sa6 = (struct sockaddr_in6 *)sa;
+ if (!IN6_IS_ADDR_LINKLOCAL(&sa6->sin6_addr) &&
+ !IN6_IS_ADDR_MC_LINKLOCAL(&sa6->sin6_addr))
+ return;
+ if (*(u_int16_t *)&sa6->sin6_addr.s6_addr[2] != 0)
+ return;
+ *(u_int16_t *)&sa6->sin6_addr.s6_addr[2] = htons(ifindex);
+}
+#endif
+
+int
+rt_Set(struct bundle *bundle, int cmd, const struct ncprange *dst,
+ const struct ncpaddr *gw, int bang, int quiet)
+{
+ struct rtmsg rtmes;
+ int s, nb, wb;
+ char *cp;
+ const char *cmdstr;
+ struct sockaddr_storage sadst, samask, sagw;
+ int result = 1;
+
+ if (bang)
+ cmdstr = (cmd == RTM_ADD ? "Add!" : "Delete!");
+ else
+ cmdstr = (cmd == RTM_ADD ? "Add" : "Delete");
+ s = ID0socket(PF_ROUTE, SOCK_RAW, 0);
+ if (s < 0) {
+ log_Printf(LogERROR, "rt_Set: socket(): %s\n", strerror(errno));
+ return result;
+ }
+ memset(&rtmes, '\0', sizeof rtmes);
+ rtmes.m_rtm.rtm_version = RTM_VERSION;
+ rtmes.m_rtm.rtm_type = cmd;
+ rtmes.m_rtm.rtm_addrs = RTA_DST;
+ rtmes.m_rtm.rtm_seq = ++bundle->routing_seq;
+ rtmes.m_rtm.rtm_pid = getpid();
+ rtmes.m_rtm.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;
+
+ if (cmd == RTM_ADD) {
+ if (bundle->ncp.cfg.sendpipe > 0) {
+ rtmes.m_rtm.rtm_rmx.rmx_sendpipe = bundle->ncp.cfg.sendpipe;
+ rtmes.m_rtm.rtm_inits |= RTV_SPIPE;
+ }
+ if (bundle->ncp.cfg.recvpipe > 0) {
+ rtmes.m_rtm.rtm_rmx.rmx_recvpipe = bundle->ncp.cfg.recvpipe;
+ rtmes.m_rtm.rtm_inits |= RTV_RPIPE;
+ }
+ }
+
+ ncprange_getsa(dst, &sadst, &samask);
+#if defined(__KAME__) && !defined(NOINET6)
+ add_scope((struct sockaddr *)&sadst, bundle->iface->index);
+#endif
+
+ cp = rtmes.m_space;
+ cp += memcpy_roundup(cp, &sadst, sadst.ss_len);
+ if (cmd == RTM_ADD) {
+ if (gw == NULL) {
+ log_Printf(LogERROR, "rt_Set: Program error\n");
+ close(s);
+ return result;
+ }
+ ncpaddr_getsa(gw, &sagw);
+#if defined(__KAME__) && !defined(NOINET6)
+ add_scope((struct sockaddr *)&sagw, bundle->iface->index);
+#endif
+ if (ncpaddr_isdefault(gw)) {
+ if (!quiet)
+ log_Printf(LogERROR, "rt_Set: Cannot add a route with"
+ " gateway 0.0.0.0\n");
+ close(s);
+ return result;
+ } else {
+ cp += memcpy_roundup(cp, &sagw, sagw.ss_len);
+ rtmes.m_rtm.rtm_addrs |= RTA_GATEWAY;
+ }
+ }
+
+ if (!ncprange_ishost(dst)) {
+ cp += memcpy_roundup(cp, &samask, samask.ss_len);
+ rtmes.m_rtm.rtm_addrs |= RTA_NETMASK;
+ }
+
+ nb = cp - (char *)&rtmes;
+ rtmes.m_rtm.rtm_msglen = nb;
+ wb = ID0write(s, &rtmes, nb);
+ if (wb < 0) {
+ log_Printf(LogTCPIP, "rt_Set failure:\n");
+ log_Printf(LogTCPIP, "rt_Set: Cmd = %s\n", cmdstr);
+ log_Printf(LogTCPIP, "rt_Set: Dst = %s\n", ncprange_ntoa(dst));
+ if (gw != NULL)
+ log_Printf(LogTCPIP, "rt_Set: Gateway = %s\n", ncpaddr_ntoa(gw));
+failed:
+ if (cmd == RTM_ADD && (rtmes.m_rtm.rtm_errno == EEXIST ||
+ (rtmes.m_rtm.rtm_errno == 0 && errno == EEXIST))) {
+ if (!bang) {
+ log_Printf(LogWARN, "Add route failed: %s already exists\n",
+ ncprange_ntoa(dst));
+ result = 0; /* Don't add to our dynamic list */
+ } else {
+ rtmes.m_rtm.rtm_type = cmd = RTM_CHANGE;
+ if ((wb = ID0write(s, &rtmes, nb)) < 0)
+ goto failed;
+ }
+ } else if (cmd == RTM_DELETE &&
+ (rtmes.m_rtm.rtm_errno == ESRCH ||
+ (rtmes.m_rtm.rtm_errno == 0 && errno == ESRCH))) {
+ if (!bang)
+ log_Printf(LogWARN, "Del route failed: %s: Non-existent\n",
+ ncprange_ntoa(dst));
+ } else if (rtmes.m_rtm.rtm_errno == 0) {
+ if (!quiet || errno != ENETUNREACH)
+ log_Printf(LogWARN, "%s route failed: %s: errno: %s\n", cmdstr,
+ ncprange_ntoa(dst), strerror(errno));
+ } else
+ log_Printf(LogWARN, "%s route failed: %s: %s\n",
+ cmdstr, ncprange_ntoa(dst), strerror(rtmes.m_rtm.rtm_errno));
+ }
+
+ if (log_IsKept(LogDEBUG)) {
+ char gwstr[40];
+
+ if (gw)
+ snprintf(gwstr, sizeof gwstr, "%s", ncpaddr_ntoa(gw));
+ else
+ snprintf(gwstr, sizeof gwstr, "<none>");
+ log_Printf(LogDEBUG, "wrote %d: cmd = %s, dst = %s, gateway = %s\n",
+ wb, cmdstr, ncprange_ntoa(dst), gwstr);
+ }
+ close(s);
+
+ return result;
+}
+
+void
+rt_Update(struct bundle *bundle, const struct sockaddr *dst,
+ const struct sockaddr *gw, const struct sockaddr *mask)
+{
+ struct ncprange ncpdst;
+ struct rtmsg rtmes;
+ char *p;
+ int s, wb;
+
+ s = ID0socket(PF_ROUTE, SOCK_RAW, 0);
+ if (s < 0) {
+ log_Printf(LogERROR, "rt_Update: socket(): %s\n", strerror(errno));
+ return;
+ }
+
+ memset(&rtmes, '\0', sizeof rtmes);
+ rtmes.m_rtm.rtm_version = RTM_VERSION;
+ rtmes.m_rtm.rtm_type = RTM_CHANGE;
+ rtmes.m_rtm.rtm_addrs = 0;
+ rtmes.m_rtm.rtm_seq = ++bundle->routing_seq;
+ rtmes.m_rtm.rtm_pid = getpid();
+ rtmes.m_rtm.rtm_flags = RTF_UP | RTF_STATIC;
+
+ if (bundle->ncp.cfg.sendpipe > 0) {
+ rtmes.m_rtm.rtm_rmx.rmx_sendpipe = bundle->ncp.cfg.sendpipe;
+ rtmes.m_rtm.rtm_inits |= RTV_SPIPE;
+ }
+
+ if (bundle->ncp.cfg.recvpipe > 0) {
+ rtmes.m_rtm.rtm_rmx.rmx_recvpipe = bundle->ncp.cfg.recvpipe;
+ rtmes.m_rtm.rtm_inits |= RTV_RPIPE;
+ }
+
+ rtmes.m_rtm.rtm_rmx.rmx_mtu = bundle->iface->mtu;
+ rtmes.m_rtm.rtm_inits |= RTV_MTU;
+ p = rtmes.m_space;
+
+ if (dst) {
+ rtmes.m_rtm.rtm_addrs |= RTA_DST;
+ p += memcpy_roundup(p, dst, dst->sa_len);
+ }
+
+ if (gw != NULL && (gw->sa_family != AF_LINK))
+ rtmes.m_rtm.rtm_addrs |= RTA_GATEWAY;
+ p += memcpy_roundup(p, gw, gw->sa_len);
+
+ if (mask) {
+ rtmes.m_rtm.rtm_addrs |= RTA_NETMASK;
+ p += memcpy_roundup(p, mask, mask->sa_len);
+ }
+
+ rtmes.m_rtm.rtm_msglen = p - (char *)&rtmes;
+
+ wb = ID0write(s, &rtmes, rtmes.m_rtm.rtm_msglen);
+ if (wb < 0) {
+ ncprange_setsa(&ncpdst, dst, mask);
+
+ log_Printf(LogTCPIP, "rt_Update failure:\n");
+ log_Printf(LogTCPIP, "rt_Update: Dst = %s\n", ncprange_ntoa(&ncpdst));
+
+ if (rtmes.m_rtm.rtm_errno == 0)
+ log_Printf(LogWARN, "%s: Change route failed: errno: %s\n",
+ ncprange_ntoa(&ncpdst), strerror(errno));
+ else
+ log_Printf(LogWARN, "%s: Change route failed: %s\n",
+ ncprange_ntoa(&ncpdst), strerror(rtmes.m_rtm.rtm_errno));
+ }
+ close(s);
+}
diff --git a/usr.sbin/ppp/route.h b/usr.sbin/ppp/route.h
new file mode 100644
index 0000000..edf6604
--- /dev/null
+++ b/usr.sbin/ppp/route.h
@@ -0,0 +1,73 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+struct bundle;
+struct cmdargs;
+struct rt_msghdr;
+struct sockaddr;
+
+#define ROUTE_STATIC 0x0000
+#define ROUTE_DSTMYADDR 0x0001
+#define ROUTE_DSTMYADDR6 0x0002
+#define ROUTE_DSTHISADDR 0x0004
+#define ROUTE_DSTHISADDR6 0x0008
+#define ROUTE_DSTDNS0 0x0010
+#define ROUTE_DSTDNS1 0x0020
+#define ROUTE_DSTANY 0x0040
+#define ROUTE_GWHISADDR 0x0080 /* May be ORd with DST_* */
+#define ROUTE_GWHISADDR6 0x0100 /* May be ORd with DST_* */
+
+struct sticky_route {
+ int type; /* ROUTE_* value (not _STATIC) */
+ struct sticky_route *next; /* next in list */
+
+ struct ncprange dst;
+ struct ncpaddr gw;
+};
+
+extern int GetIfIndex(char *);
+extern int route_Show(struct cmdargs const *);
+extern void route_IfDelete(struct bundle *, int);
+extern void route_UpdateMTU(struct bundle *);
+extern const char *Index2Nam(int);
+extern void route_Change(struct bundle *, struct sticky_route *,
+ const struct ncpaddr *, const struct ncpaddr *);
+extern void route_Add(struct sticky_route **, int, const struct ncprange *,
+ const struct ncpaddr *);
+extern void route_Delete(struct sticky_route **, int, const struct ncprange *);
+extern void route_DeleteAll(struct sticky_route **);
+extern void route_Clean(struct bundle *, struct sticky_route *);
+extern void route_ShowSticky(struct prompt *, struct sticky_route *,
+ const char *, int);
+extern void route_ParseHdr(struct rt_msghdr *, struct sockaddr *[RTAX_MAX]);
+extern int rt_Set(struct bundle *, int, const struct ncprange *,
+ const struct ncpaddr *, int, int);
+extern void rt_Update(struct bundle *, const struct sockaddr *,
+ const struct sockaddr *, const struct sockaddr *);
diff --git a/usr.sbin/ppp/server.c b/usr.sbin/ppp/server.c
new file mode 100644
index 0000000..864c627
--- /dev/null
+++ b/usr.sbin/ppp/server.c
@@ -0,0 +1,422 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "descriptor.h"
+#include "server.h"
+#include "prompt.h"
+#include "ncpaddr.h"
+#include "probe.h"
+
+static int
+server_UpdateSet(struct fdescriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
+{
+ struct server *s = descriptor2server(d);
+ struct prompt *p;
+ int sets;
+
+ sets = 0;
+ if (r && s->fd >= 0) {
+ if (*n < s->fd + 1)
+ *n = s->fd + 1;
+ FD_SET(s->fd, r);
+ log_Printf(LogTIMER, "server: fdset(r) %d\n", s->fd);
+ sets++;
+ }
+
+ for (p = log_PromptList(); p; p = p->next)
+ sets += descriptor_UpdateSet(&p->desc, r, w, e, n);
+
+ return sets;
+}
+
+static int
+server_IsSet(struct fdescriptor *d, const fd_set *fdset)
+{
+ struct server *s = descriptor2server(d);
+ struct prompt *p;
+
+ if (s->fd >= 0 && FD_ISSET(s->fd, fdset))
+ return 1;
+
+ for (p = log_PromptList(); p; p = p->next)
+ if (descriptor_IsSet(&p->desc, fdset))
+ return 1;
+
+ return 0;
+}
+
+static void
+server_Read(struct fdescriptor *d, struct bundle *bundle, const fd_set *fdset)
+{
+ struct server *s = descriptor2server(d);
+ struct sockaddr_storage ss;
+ struct sockaddr *sa = (struct sockaddr *)&ss;
+ struct sockaddr_in *sin = (struct sockaddr_in *)&ss;
+#ifndef NOINET6
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss;
+#endif
+ int ssize = sizeof ss, wfd;
+ struct prompt *p;
+ struct ncpaddr addr;
+
+ if (s->fd >= 0 && FD_ISSET(s->fd, fdset)) {
+ wfd = accept(s->fd, sa, &ssize);
+ if (wfd < 0)
+ log_Printf(LogERROR, "server_Read: accept(): %s\n", strerror(errno));
+ else if (sa->sa_len == 0) {
+ close(wfd);
+ wfd = -1;
+ }
+ } else
+ wfd = -1;
+
+ if (wfd >= 0)
+ switch (sa->sa_family) {
+ case AF_LOCAL:
+ log_Printf(LogPHASE, "Connected to local client.\n");
+ break;
+
+ case AF_INET:
+ ncpaddr_setsa(&addr, sa);
+ if (ntohs(sin->sin_port) < 1024) {
+ log_Printf(LogALERT, "Rejected client connection from %s:%u"
+ "(invalid port number) !\n",
+ ncpaddr_ntoa(&addr), ntohs(sin->sin_port));
+ close(wfd);
+ wfd = -1;
+ break;
+ }
+ log_Printf(LogPHASE, "Connected to client from %s:%u\n",
+ ncpaddr_ntoa(&addr), ntohs(sin->sin_port));
+ break;
+
+#ifndef NOINET6
+ case AF_INET6:
+ ncpaddr_setsa(&addr, sa);
+ if (ntohs(sin6->sin6_port) < 1024) {
+ log_Printf(LogALERT, "Rejected client connection from %s:%u"
+ "(invalid port number) !\n",
+ ncpaddr_ntoa(&addr), ntohs(sin6->sin6_port));
+ close(wfd);
+ wfd = -1;
+ break;
+ }
+ log_Printf(LogPHASE, "Connected to client from %s:%u\n",
+ ncpaddr_ntoa(&addr), ntohs(sin6->sin6_port));
+ break;
+#endif
+
+ default:
+ write(wfd, "Unrecognised access !\n", 22);
+ close(wfd);
+ wfd = -1;
+ break;
+ }
+
+ if (wfd >= 0) {
+ if ((p = prompt_Create(s, bundle, wfd)) == NULL) {
+ write(wfd, "Connection refused.\n", 20);
+ close(wfd);
+ } else {
+ switch (sa->sa_family) {
+ case AF_LOCAL:
+ p->src.type = "local";
+ strncpy(p->src.from, s->cfg.sockname, sizeof p->src.from - 1);
+ p->src.from[sizeof p->src.from - 1] = '\0';
+ break;
+ case AF_INET:
+ p->src.type = "ip";
+ snprintf(p->src.from, sizeof p->src.from, "%s:%u",
+ ncpaddr_ntoa(&addr), ntohs(sin->sin_port));
+ break;
+#ifndef NOINET6
+ case AF_INET6:
+ p->src.type = "ip6";
+ snprintf(p->src.from, sizeof p->src.from, "%s:%u",
+ ncpaddr_ntoa(&addr), ntohs(sin6->sin6_port));
+ break;
+#endif
+ }
+ prompt_TtyCommandMode(p);
+ prompt_Required(p);
+ }
+ }
+
+ log_PromptListChanged = 0;
+ for (p = log_PromptList(); p; p = p->next)
+ if (descriptor_IsSet(&p->desc, fdset)) {
+ descriptor_Read(&p->desc, bundle, fdset);
+ if (log_PromptListChanged)
+ break;
+ }
+}
+
+static int
+server_Write(struct fdescriptor *d __unused, struct bundle *bundle __unused,
+ const fd_set *fdset __unused)
+{
+ /* We never want to write here ! */
+ log_Printf(LogALERT, "server_Write: Internal error: Bad call !\n");
+ return 0;
+}
+
+struct server server = {
+ {
+ SERVER_DESCRIPTOR,
+ server_UpdateSet,
+ server_IsSet,
+ server_Read,
+ server_Write
+ },
+ -1,
+ { "", "", 0, 0 }
+};
+
+enum server_stat
+server_Reopen(struct bundle *bundle)
+{
+ char name[sizeof server.cfg.sockname];
+ struct stat st;
+ u_short port;
+ mode_t mask;
+ enum server_stat ret;
+
+ if (server.cfg.sockname[0] != '\0') {
+ strcpy(name, server.cfg.sockname);
+ mask = server.cfg.mask;
+ server_Close(bundle);
+ if (server.cfg.sockname[0] != '\0' && stat(server.cfg.sockname, &st) == 0)
+ if (!(st.st_mode & S_IFSOCK) || unlink(server.cfg.sockname) != 0)
+ return SERVER_FAILED;
+ ret = server_LocalOpen(bundle, name, mask);
+ } else if (server.cfg.port != 0) {
+ port = server.cfg.port;
+ server_Close(bundle);
+ ret = server_TcpOpen(bundle, port);
+ } else
+ ret = SERVER_UNSET;
+
+ return ret;
+}
+
+enum server_stat
+server_LocalOpen(struct bundle *bundle, const char *name, mode_t mask)
+{
+ struct sockaddr_un ifsun;
+ mode_t oldmask;
+ int s;
+
+ oldmask = (mode_t)-1; /* Silence compiler */
+
+ if (server.cfg.sockname && !strcmp(server.cfg.sockname, name))
+ server_Close(bundle);
+
+ memset(&ifsun, '\0', sizeof ifsun);
+ ifsun.sun_len = strlen(name);
+ if (ifsun.sun_len > sizeof ifsun.sun_path - 1) {
+ log_Printf(LogERROR, "Local: %s: Path too long\n", name);
+ return SERVER_INVALID;
+ }
+ ifsun.sun_family = AF_LOCAL;
+ strcpy(ifsun.sun_path, name);
+
+ s = socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (s < 0) {
+ log_Printf(LogERROR, "Local: socket: %s\n", strerror(errno));
+ goto failed;
+ }
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &s, sizeof s);
+ if (mask != (mode_t)-1)
+ oldmask = umask(mask);
+ if (bind(s, (struct sockaddr *)&ifsun, sizeof ifsun) < 0) {
+ if (mask != (mode_t)-1)
+ umask(oldmask);
+ log_Printf(LogWARN, "Local: bind: %s\n", strerror(errno));
+ close(s);
+ goto failed;
+ }
+ if (mask != (mode_t)-1)
+ umask(oldmask);
+ if (listen(s, 5) != 0) {
+ log_Printf(LogERROR, "Local: Unable to listen to socket -"
+ " BUNDLE overload?\n");
+ close(s);
+ unlink(name);
+ goto failed;
+ }
+ server_Close(bundle);
+ server.fd = s;
+ server.cfg.port = 0;
+ strncpy(server.cfg.sockname, ifsun.sun_path, sizeof server.cfg.sockname - 1);
+ server.cfg.sockname[sizeof server.cfg.sockname - 1] = '\0';
+ server.cfg.mask = mask;
+ log_Printf(LogPHASE, "Listening at local socket %s.\n", name);
+
+ return SERVER_OK;
+
+failed:
+ if (server.fd == -1) {
+ server.fd = -1;
+ server.cfg.port = 0;
+ strncpy(server.cfg.sockname, ifsun.sun_path,
+ sizeof server.cfg.sockname - 1);
+ server.cfg.sockname[sizeof server.cfg.sockname - 1] = '\0';
+ server.cfg.mask = mask;
+ }
+ return SERVER_FAILED;
+}
+
+enum server_stat
+server_TcpOpen(struct bundle *bundle, u_short port)
+{
+ struct sockaddr_storage ss;
+ struct sockaddr_in *sin = (struct sockaddr_in *)&ss;
+#ifndef NOINET6
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&ss;
+#endif
+ int s, sz;
+
+ if (server.cfg.port == port)
+ server_Close(bundle);
+
+ if (port == 0)
+ return SERVER_INVALID;
+
+ memset(&ss, '\0', sizeof ss);
+#ifndef NOINET6
+ if (probe.ipv6_available) {
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = htons(port);
+ sin6->sin6_len = (u_int8_t)sizeof ss;
+ sz = sizeof *sin6;
+ s = socket(PF_INET6, SOCK_STREAM, 0);
+ } else
+#endif
+ {
+ sin->sin_family = AF_INET;
+ sin->sin_port = htons(port);
+ sin->sin_len = (u_int8_t)sizeof ss;
+ sin->sin_addr.s_addr = INADDR_ANY;
+ sz = sizeof *sin;
+ s = socket(PF_INET, SOCK_STREAM, 0);
+ }
+
+ if (s < 0) {
+ log_Printf(LogERROR, "Tcp: socket: %s\n", strerror(errno));
+ goto failed;
+ }
+
+#ifndef NOINET6
+ if (probe.ipv6_available) {
+ int off = 0;
+ setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&off, sizeof(off));
+ }
+#endif
+
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &s, sizeof s);
+ if (bind(s, (struct sockaddr *)&ss, sz) < 0) {
+ log_Printf(LogWARN, "Tcp: bind: %s\n", strerror(errno));
+ close(s);
+ goto failed;
+ }
+ if (listen(s, 5) != 0) {
+ log_Printf(LogERROR, "Tcp: Unable to listen to socket: %s\n",
+ strerror(errno));
+ close(s);
+ goto failed;
+ }
+ server_Close(bundle);
+ server.fd = s;
+ server.cfg.port = port;
+ *server.cfg.sockname = '\0';
+ server.cfg.mask = 0;
+ log_Printf(LogPHASE, "Listening at port %d.\n", port);
+ return SERVER_OK;
+
+failed:
+ if (server.fd == -1) {
+ server.fd = -1;
+ server.cfg.port = port;
+ *server.cfg.sockname = '\0';
+ server.cfg.mask = 0;
+ }
+ return SERVER_FAILED;
+}
+
+int
+server_Close(struct bundle *bundle __unused)
+{
+ if (server.fd >= 0) {
+ if (*server.cfg.sockname != '\0') {
+ struct sockaddr_un un;
+ int sz = sizeof un;
+
+ if (getsockname(server.fd, (struct sockaddr *)&un, &sz) == 0 &&
+ un.sun_family == AF_LOCAL && sz == sizeof un)
+ unlink(un.sun_path);
+ }
+ close(server.fd);
+ server.fd = -1;
+ /* Drop associated prompts */
+ log_DestroyPrompts(&server);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+server_Clear(struct bundle *bundle)
+{
+ int ret;
+
+ ret = server_Close(bundle);
+
+ server.fd = -1;
+ server.cfg.port = 0;
+ *server.cfg.sockname = '\0';
+ server.cfg.mask = 0;
+
+ return ret;
+}
diff --git a/usr.sbin/ppp/server.h b/usr.sbin/ppp/server.h
new file mode 100644
index 0000000..9fdc52c
--- /dev/null
+++ b/usr.sbin/ppp/server.h
@@ -0,0 +1,61 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct bundle;
+
+struct server {
+ struct fdescriptor desc;
+ int fd;
+
+ struct {
+ char passwd[50];
+
+ char sockname[PATH_MAX]; /* Points to local socket path */
+ mode_t mask;
+
+ u_short port; /* tcp socket */
+ } cfg;
+};
+
+enum server_stat {
+ SERVER_OK, /* Diagnostic socket available */
+ SERVER_INVALID, /* Bad args, can't be set up */
+ SERVER_FAILED, /* Failed - lack of resources */
+ SERVER_UNSET /* Not already set up */
+};
+
+#define descriptor2server(d) \
+ ((d)->type == SERVER_DESCRIPTOR ? (struct server *)(d) : NULL)
+
+extern struct server server;
+
+extern enum server_stat server_LocalOpen(struct bundle *, const char *, mode_t);
+extern enum server_stat server_TcpOpen(struct bundle *, u_short);
+extern enum server_stat server_Reopen(struct bundle *);
+extern int server_Close(struct bundle *);
+extern int server_Clear(struct bundle *);
diff --git a/usr.sbin/ppp/sig.c b/usr.sbin/ppp/sig.c
new file mode 100644
index 0000000..a42194b
--- /dev/null
+++ b/usr.sbin/ppp/sig.c
@@ -0,0 +1,119 @@
+/*-
+ * Copyright (c) 1997 - 1999, 2001 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <signal.h>
+
+#include "log.h"
+#include "sig.h"
+
+static int caused[NSIG]; /* An array of pending signals */
+static int necessary; /* Anything set ? */
+static sig_type handler[NSIG]; /* all start at SIG_DFL */
+
+
+/*
+ * Record a signal in the "caused" array
+ *
+ * This function is the only thing actually called in signal context. It
+ * records that a signal has been caused and that sig_Handle() should be
+ * called (in non-signal context) as soon as possible to process that
+ * signal.
+ */
+static void
+signal_recorder(int sig)
+{
+ caused[sig - 1]++;
+ necessary = 1;
+}
+
+
+/*
+ * Set up signal_recorder to handle the given sig and record ``fn'' as
+ * the function to ultimately call in sig_Handle(). ``fn'' will not be
+ * called in signal context (as sig_Handle() is not called in signal
+ * context).
+ */
+sig_type
+sig_signal(int sig, sig_type fn)
+{
+ sig_type Result;
+
+ if (sig <= 0 || sig > NSIG) {
+ /* Oops - we must be a bit out of date (too many sigs ?) */
+ log_Printf(LogALERT, "Eeek! %s:%d: I must be out of date!\n",
+ __FILE__, __LINE__);
+ return signal(sig, fn);
+ }
+ Result = handler[sig - 1];
+ if (fn == SIG_DFL || fn == SIG_IGN) {
+ signal(sig, fn);
+ handler[sig - 1] = (sig_type) 0;
+ } else {
+ handler[sig - 1] = fn;
+ signal(sig, signal_recorder);
+ }
+ caused[sig - 1] = 0;
+ return Result;
+}
+
+
+/*
+ * Call the handlers for any pending signals
+ *
+ * This function is called from a non-signal context - in fact, it's
+ * called every time select() in DoLoop() returns - just in case
+ * select() returned due to a signal being recorded by signal_recorder().
+ */
+int
+sig_Handle()
+{
+ int sig;
+ int got;
+ int result;
+
+ result = 0;
+ if (necessary) {
+ /* We've *probably* got something in `caused' set */
+ necessary = 0;
+ /* `necessary' might go back to 1 while we're in here.... */
+ do {
+ got = 0;
+ for (sig = 0; sig < NSIG; sig++)
+ if (caused[sig]) {
+ caused[sig]--;
+ got++;
+ result++;
+ (*handler[sig])(sig + 1);
+ }
+ } while (got);
+ }
+
+ return result;
+}
diff --git a/usr.sbin/ppp/sig.h b/usr.sbin/ppp/sig.h
new file mode 100644
index 0000000..27d264c
--- /dev/null
+++ b/usr.sbin/ppp/sig.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+typedef void (*sig_type)(int);
+
+/* Call this instead of signal() */
+extern sig_type sig_signal(int, sig_type);
+
+/* Call this when you want things to *actually* happen */
+extern int sig_Handle(void);
diff --git a/usr.sbin/ppp/slcompress.c b/usr.sbin/ppp/slcompress.c
new file mode 100644
index 0000000..48928c7
--- /dev/null
+++ b/usr.sbin/ppp/slcompress.c
@@ -0,0 +1,605 @@
+/*-
+ * 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.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)slcompress.c 8.2 (Berkeley) 4/16/94
+ */
+
+/*
+ * Routines to compress and uncompess tcp packets (for transmission
+ * over low speed serial lines.
+ *
+ * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ * - Initial distribution.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "slcompress.h"
+#include "descriptor.h"
+#include "prompt.h"
+#include "timer.h"
+#include "fsm.h"
+#include "throughput.h"
+#include "iplist.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+
+void
+sl_compress_init(struct slcompress *comp, int max_state)
+{
+ register u_int i;
+ register struct cstate *tstate = comp->tstate;
+
+ memset(comp, '\0', sizeof *comp);
+ for (i = max_state; i > 0; --i) {
+ tstate[i].cs_id = i;
+ tstate[i].cs_next = &tstate[i - 1];
+ }
+ tstate[0].cs_next = &tstate[max_state];
+ tstate[0].cs_id = 0;
+ comp->last_cs = &tstate[0];
+ comp->last_recv = 255;
+ comp->last_xmit = 255;
+ comp->flags = SLF_TOSS;
+}
+
+
+/* ENCODE encodes a number that is known to be non-zero. ENCODEZ
+ * checks for zero (since zero has to be encoded in the 32-bit, 3 byte
+ * form).
+ */
+#define ENCODE(n) { \
+ if ((u_short)(n) >= 256) { \
+ *cp++ = 0; \
+ cp[1] = (n); \
+ cp[0] = (n) >> 8; \
+ cp += 2; \
+ } else { \
+ *cp++ = (n); \
+ } \
+}
+#define ENCODEZ(n) { \
+ if ((u_short)(n) >= 256 || (u_short)(n) == 0) { \
+ *cp++ = 0; \
+ cp[1] = (n); \
+ cp[0] = (n) >> 8; \
+ cp += 2; \
+ } else { \
+ *cp++ = (n); \
+ } \
+}
+
+#define DECODEL(f) { \
+ if (*cp == 0) {\
+ (f) = htonl(ntohl(f) + ((cp[1] << 8) | cp[2])); \
+ cp += 3; \
+ } else { \
+ (f) = htonl(ntohl(f) + (u_int32_t)*cp++); \
+ } \
+}
+
+#define DECODES(f) { \
+ if (*cp == 0) {\
+ (f) = htons(ntohs(f) + ((cp[1] << 8) | cp[2])); \
+ cp += 3; \
+ } else { \
+ (f) = htons(ntohs(f) + (u_int32_t)*cp++); \
+ } \
+}
+
+#define DECODEU(f) { \
+ if (*cp == 0) {\
+ (f) = htons((cp[1] << 8) | cp[2]); \
+ cp += 3; \
+ } else { \
+ (f) = htons((u_int32_t)*cp++); \
+ } \
+}
+
+
+u_char
+sl_compress_tcp(struct mbuf * m,
+ struct ip * ip,
+ struct slcompress *comp,
+ struct slstat *slstat,
+ int compress_cid)
+{
+ register struct cstate *cs = comp->last_cs->cs_next;
+ register u_int hlen = ip->ip_hl;
+ register struct tcphdr *oth;
+ register struct tcphdr *th;
+ register u_int deltaS, deltaA;
+ register u_int changes = 0;
+ u_char new_seq[16];
+ register u_char *cp = new_seq;
+
+ /*
+ * Bail if this is an IP fragment or if the TCP packet isn't `compressible'
+ * (i.e., ACK isn't set or some other control bit is set). (We assume that
+ * the caller has already made sure the packet is IP proto TCP).
+ */
+ if ((ip->ip_off & htons(0x3fff)) || m->m_len < 40) {
+ log_Printf(LogDEBUG, "??? 1 ip_off = %x, m_len = %lu\n",
+ ip->ip_off, (unsigned long)m->m_len);
+ log_DumpBp(LogDEBUG, "", m);
+ return (TYPE_IP);
+ }
+ th = (struct tcphdr *) & ((int *) ip)[hlen];
+ if ((th->th_flags & (TH_SYN | TH_FIN | TH_RST | TH_ACK)) != TH_ACK) {
+ log_Printf(LogDEBUG, "??? 2 th_flags = %x\n", th->th_flags);
+ log_DumpBp(LogDEBUG, "", m);
+ return (TYPE_IP);
+ }
+
+ /*
+ * Packet is compressible -- we're going to send either a COMPRESSED_TCP or
+ * UNCOMPRESSED_TCP packet. Either way we need to locate (or create) the
+ * connection state. Special case the most recently used connection since
+ * it's most likely to be used again & we don't have to do any reordering
+ * if it's used.
+ */
+ slstat->sls_packets++;
+ if (ip->ip_src.s_addr != cs->cs_ip.ip_src.s_addr ||
+ ip->ip_dst.s_addr != cs->cs_ip.ip_dst.s_addr ||
+ *(int *) th != ((int *) &cs->cs_ip)[cs->cs_ip.ip_hl]) {
+
+ /*
+ * Wasn't the first -- search for it.
+ *
+ * States are kept in a circularly linked list with last_cs pointing to the
+ * end of the list. The list is kept in lru order by moving a state to
+ * the head of the list whenever it is referenced. Since the list is
+ * short and, empirically, the connection we want is almost always near
+ * the front, we locate states via linear search. If we don't find a
+ * state for the datagram, the oldest state is (re-)used.
+ */
+ register struct cstate *lcs;
+ register struct cstate *lastcs = comp->last_cs;
+
+ do {
+ lcs = cs;
+ cs = cs->cs_next;
+ slstat->sls_searches++;
+ if (ip->ip_src.s_addr == cs->cs_ip.ip_src.s_addr
+ && ip->ip_dst.s_addr == cs->cs_ip.ip_dst.s_addr
+ && *(int *) th == ((int *) &cs->cs_ip)[cs->cs_ip.ip_hl])
+ goto found;
+ } while (cs != lastcs);
+
+ /*
+ * Didn't find it -- re-use oldest cstate. Send an uncompressed packet
+ * that tells the other side what connection number we're using for this
+ * conversation. Note that since the state list is circular, the oldest
+ * state points to the newest and we only need to set last_cs to update
+ * the lru linkage.
+ */
+ slstat->sls_misses++;
+ comp->last_cs = lcs;
+#define THOFFSET(th) (th->th_off)
+ hlen += th->th_off;
+ hlen <<= 2;
+ if (hlen > m->m_len)
+ return (TYPE_IP);
+ goto uncompressed;
+
+found:
+
+ /*
+ * Found it -- move to the front on the connection list.
+ */
+ if (cs == lastcs)
+ comp->last_cs = lcs;
+ else {
+ lcs->cs_next = cs->cs_next;
+ cs->cs_next = lastcs->cs_next;
+ lastcs->cs_next = cs;
+ }
+ }
+
+ /*
+ * Make sure that only what we expect to change changed. The first line of
+ * the `if' checks the IP protocol version, header length & type of
+ * service. The 2nd line checks the "Don't fragment" bit. The 3rd line
+ * checks the time-to-live and protocol (the protocol check is unnecessary
+ * but costless). The 4th line checks the TCP header length. The 5th line
+ * checks IP options, if any. The 6th line checks TCP options, if any. If
+ * any of these things are different between the previous & current
+ * datagram, we send the current datagram `uncompressed'.
+ */
+ oth = (struct tcphdr *) & ((int *) &cs->cs_ip)[hlen];
+ deltaS = hlen;
+ hlen += th->th_off;
+ hlen <<= 2;
+ if (hlen > m->m_len)
+ return (TYPE_IP);
+
+ if (((u_short *) ip)[0] != ((u_short *) & cs->cs_ip)[0] ||
+ ((u_short *) ip)[3] != ((u_short *) & cs->cs_ip)[3] ||
+ ((u_short *) ip)[4] != ((u_short *) & cs->cs_ip)[4] ||
+ THOFFSET(th) != THOFFSET(oth) ||
+ (deltaS > 5 &&
+ memcmp(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) ||
+ (THOFFSET(th) > 5 &&
+ memcmp(th + 1, oth + 1, (THOFFSET(th) - 5) << 2))) {
+ goto uncompressed;
+ }
+
+ /*
+ * Figure out which of the changing fields changed. The receiver expects
+ * changes in the order: urgent, window, ack, seq (the order minimizes the
+ * number of temporaries needed in this section of code).
+ */
+ if (th->th_flags & TH_URG) {
+ deltaS = ntohs(th->th_urp);
+ ENCODEZ(deltaS);
+ changes |= NEW_U;
+ } else if (th->th_urp != oth->th_urp) {
+
+ /*
+ * argh! URG not set but urp changed -- a sensible implementation should
+ * never do this but RFC793 doesn't prohibit the change so we have to
+ * deal with it.
+ */
+ goto uncompressed;
+ }
+ deltaS = (u_short) (ntohs(th->th_win) - ntohs(oth->th_win));
+ if (deltaS) {
+ ENCODE(deltaS);
+ changes |= NEW_W;
+ }
+ deltaA = ntohl(th->th_ack) - ntohl(oth->th_ack);
+ if (deltaA) {
+ if (deltaA > 0xffff) {
+ goto uncompressed;
+ }
+ ENCODE(deltaA);
+ changes |= NEW_A;
+ }
+ deltaS = ntohl(th->th_seq) - ntohl(oth->th_seq);
+ if (deltaS) {
+ if (deltaS > 0xffff) {
+ goto uncompressed;
+ }
+ ENCODE(deltaS);
+ changes |= NEW_S;
+ }
+ switch (changes) {
+
+ case 0:
+
+ /*
+ * Nothing changed. If this packet contains data and the last one didn't,
+ * this is probably a data packet following an ack (normal on an
+ * interactive connection) and we send it compressed. Otherwise it's
+ * probably a retransmit, retransmitted ack or window probe. Send it
+ * uncompressed in case the other side missed the compressed version.
+ */
+ if (ip->ip_len != cs->cs_ip.ip_len &&
+ ntohs(cs->cs_ip.ip_len) == hlen)
+ break;
+
+ /* FALLTHROUGH */
+
+ case SPECIAL_I:
+ case SPECIAL_D:
+
+ /*
+ * actual changes match one of our special case encodings -- send packet
+ * uncompressed.
+ */
+ goto uncompressed;
+
+ case NEW_S | NEW_A:
+ if (deltaS == deltaA &&
+ deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
+ /* special case for echoed terminal traffic */
+ changes = SPECIAL_I;
+ cp = new_seq;
+ }
+ break;
+
+ case NEW_S:
+ if (deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
+ /* special case for data xfer */
+ changes = SPECIAL_D;
+ cp = new_seq;
+ }
+ break;
+ }
+
+ deltaS = ntohs(ip->ip_id) - ntohs(cs->cs_ip.ip_id);
+ if (deltaS != 1) {
+ ENCODEZ(deltaS);
+ changes |= NEW_I;
+ }
+ if (th->th_flags & TH_PUSH)
+ changes |= TCP_PUSH_BIT;
+
+ /*
+ * Grab the cksum before we overwrite it below. Then update our state with
+ * this packet's header.
+ */
+ deltaA = ntohs(th->th_sum);
+ memcpy(&cs->cs_ip, ip, hlen);
+
+ /*
+ * We want to use the original packet as our compressed packet. (cp -
+ * new_seq) is the number of bytes we need for compressed sequence numbers.
+ * In addition we need one byte for the change mask, one for the connection
+ * id and two for the tcp checksum. So, (cp - new_seq) + 4 bytes of header
+ * are needed. hlen is how many bytes of the original packet to toss so
+ * subtract the two to get the new packet size.
+ */
+ deltaS = cp - new_seq;
+ cp = (u_char *) ip;
+
+ /*
+ * Since fastq traffic can jump ahead of the background traffic, we don't
+ * know what order packets will go on the line. In this case, we always
+ * send a "new" connection id so the receiver state stays synchronized.
+ */
+ if (comp->last_xmit == cs->cs_id && compress_cid) {
+ hlen -= deltaS + 3;
+ cp += hlen;
+ *cp++ = changes;
+ } else {
+ comp->last_xmit = cs->cs_id;
+ hlen -= deltaS + 4;
+ cp += hlen;
+ *cp++ = changes | NEW_C;
+ *cp++ = cs->cs_id;
+ }
+ m->m_len -= hlen;
+ m->m_offset += hlen;
+ *cp++ = deltaA >> 8;
+ *cp++ = deltaA;
+ memcpy(cp, new_seq, deltaS);
+ slstat->sls_compressed++;
+ return (TYPE_COMPRESSED_TCP);
+
+ /*
+ * Update connection state cs & send uncompressed packet ('uncompressed'
+ * means a regular ip/tcp packet but with the 'conversation id' we hope to
+ * use on future compressed packets in the protocol field).
+ */
+uncompressed:
+ memcpy(&cs->cs_ip, ip, hlen);
+ ip->ip_p = cs->cs_id;
+ comp->last_xmit = cs->cs_id;
+ return (TYPE_UNCOMPRESSED_TCP);
+}
+
+
+int
+sl_uncompress_tcp(u_char ** bufp, int len, u_int type, struct slcompress *comp,
+ struct slstat *slstat, int max_state)
+{
+ register u_char *cp;
+ register u_int hlen, changes;
+ register struct tcphdr *th;
+ register struct cstate *cs;
+ register struct ip *ip;
+ u_short *bp;
+
+ switch (type) {
+
+ case TYPE_UNCOMPRESSED_TCP:
+ ip = (struct ip *) * bufp;
+ if (ip->ip_p > max_state)
+ goto bad;
+ cs = &comp->rstate[comp->last_recv = ip->ip_p];
+ comp->flags &= ~SLF_TOSS;
+ ip->ip_p = IPPROTO_TCP;
+
+ /*
+ * Calculate the size of the TCP/IP header and make sure that we don't
+ * overflow the space we have available for it.
+ */
+ hlen = ip->ip_hl << 2;
+ if ((int)(hlen + sizeof(struct tcphdr)) > len)
+ goto bad;
+ th = (struct tcphdr *) & ((char *) ip)[hlen];
+ hlen += THOFFSET(th) << 2;
+ if (hlen > MAX_HDR)
+ goto bad;
+ memcpy(&cs->cs_ip, ip, hlen);
+ cs->cs_hlen = hlen;
+ slstat->sls_uncompressedin++;
+ return (len);
+
+ default:
+ goto bad;
+
+ case TYPE_COMPRESSED_TCP:
+ break;
+ }
+
+ /* We've got a compressed packet. */
+ slstat->sls_compressedin++;
+ cp = *bufp;
+ changes = *cp++;
+ log_Printf(LogDEBUG, "compressed: changes = %02x\n", changes);
+
+ if (changes & NEW_C) {
+ /*
+ * Make sure the state index is in range, then grab the state. If we have
+ * a good state index, clear the 'discard' flag.
+ */
+ if (*cp > max_state || comp->last_recv == 255)
+ goto bad;
+
+ comp->flags &= ~SLF_TOSS;
+ comp->last_recv = *cp++;
+ } else {
+ /*
+ * this packet has an implicit state index. If we've had a line error
+ * since the last time we got an explicit state index, we have to toss
+ * the packet.
+ */
+ if (comp->flags & SLF_TOSS) {
+ slstat->sls_tossed++;
+ return (0);
+ }
+ }
+ cs = &comp->rstate[comp->last_recv];
+ hlen = cs->cs_ip.ip_hl << 2;
+ th = (struct tcphdr *) & ((u_char *) & cs->cs_ip)[hlen];
+ th->th_sum = htons((*cp << 8) | cp[1]);
+ cp += 2;
+ if (changes & TCP_PUSH_BIT)
+ th->th_flags |= TH_PUSH;
+ else
+ th->th_flags &= ~TH_PUSH;
+
+ switch (changes & SPECIALS_MASK) {
+ case SPECIAL_I:
+ {
+ register u_int i = ntohs(cs->cs_ip.ip_len) - cs->cs_hlen;
+
+ th->th_ack = htonl(ntohl(th->th_ack) + i);
+ th->th_seq = htonl(ntohl(th->th_seq) + i);
+ }
+ break;
+
+ case SPECIAL_D:
+ th->th_seq = htonl(ntohl(th->th_seq) + ntohs(cs->cs_ip.ip_len)
+ - cs->cs_hlen);
+ break;
+
+ default:
+ if (changes & NEW_U) {
+ th->th_flags |= TH_URG;
+ DECODEU(th->th_urp)
+ } else
+ th->th_flags &= ~TH_URG;
+ if (changes & NEW_W)
+ DECODES(th->th_win)
+ if (changes & NEW_A)
+ DECODEL(th->th_ack)
+ if (changes & NEW_S) {
+ log_Printf(LogDEBUG, "NEW_S: %02x, %02x, %02x\n",
+ *cp, cp[1], cp[2]);
+ DECODEL(th->th_seq)
+ }
+ break;
+ }
+ if (changes & NEW_I) {
+ DECODES(cs->cs_ip.ip_id)
+ } else
+ cs->cs_ip.ip_id = htons(ntohs(cs->cs_ip.ip_id) + 1);
+
+ log_Printf(LogDEBUG, "Uncompress: id = %04x, seq = %08lx\n",
+ cs->cs_ip.ip_id, (u_long)ntohl(th->th_seq));
+
+ /*
+ * At this point, cp points to the first byte of data in the packet.
+ * Back up cp by the tcp/ip header length to make room for the
+ * reconstructed header (we assume the packet we were handed has enough
+ * space to prepend 128 bytes of header). Adjust the length to account
+ * for the new header & fill in the IP total length.
+ */
+ len -= (cp - *bufp);
+ if (len < 0)
+ /*
+ * we must have dropped some characters (crc should detect this but the
+ * old slip framing won't)
+ */
+ goto bad;
+
+ *bufp = cp - cs->cs_hlen;
+ len += cs->cs_hlen;
+ cs->cs_ip.ip_len = htons(len);
+
+ /* recompute the ip header checksum */
+ cs->cs_ip.ip_sum = 0;
+ bp = (u_short *)&cs->cs_ip;
+ for (changes = 0; hlen > 0; hlen -= 2)
+ changes += *bp++;
+ changes = (changes & 0xffff) + (changes >> 16);
+ changes = (changes & 0xffff) + (changes >> 16);
+ cs->cs_ip.ip_sum = ~changes;
+
+ /* And copy the result into our buffer */
+ memcpy(*bufp, &cs->cs_ip, cs->cs_hlen);
+
+ return (len);
+bad:
+ comp->flags |= SLF_TOSS;
+ slstat->sls_errorin++;
+ return (0);
+}
+
+int
+sl_Show(struct cmdargs const *arg)
+{
+ prompt_Printf(arg->prompt, "VJ compression statistics:\n");
+ prompt_Printf(arg->prompt, " Out: %d (compress) / %d (total)",
+ arg->bundle->ncp.ipcp.vj.slstat.sls_compressed,
+ arg->bundle->ncp.ipcp.vj.slstat.sls_packets);
+ prompt_Printf(arg->prompt, " %d (miss) / %d (search)\n",
+ arg->bundle->ncp.ipcp.vj.slstat.sls_misses,
+ arg->bundle->ncp.ipcp.vj.slstat.sls_searches);
+ prompt_Printf(arg->prompt, " In: %d (compress), %d (uncompress)",
+ arg->bundle->ncp.ipcp.vj.slstat.sls_compressedin,
+ arg->bundle->ncp.ipcp.vj.slstat.sls_uncompressedin);
+ prompt_Printf(arg->prompt, " %d (error), %d (tossed)\n",
+ arg->bundle->ncp.ipcp.vj.slstat.sls_errorin,
+ arg->bundle->ncp.ipcp.vj.slstat.sls_tossed);
+ return 0;
+}
diff --git a/usr.sbin/ppp/slcompress.h b/usr.sbin/ppp/slcompress.h
new file mode 100644
index 0000000..9c415cb
--- /dev/null
+++ b/usr.sbin/ppp/slcompress.h
@@ -0,0 +1,161 @@
+/*
+ * Definitions for tcp compression routines.
+ *
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ * - Initial distribution.
+ *
+ * $FreeBSD$
+ */
+
+#define MIN_VJ_STATES 3
+#define MAX_VJ_STATES 255
+#define DEF_VJ_STATES 16 /* must be > 2 and < 256 */
+#define MAX_HDR 128
+
+/*
+ * Compressed packet format:
+ *
+ * The first octet contains the packet type (top 3 bits), TCP
+ * 'push' bit, and flags that indicate which of the 4 TCP sequence
+ * numbers have changed (bottom 5 bits). The next octet is a
+ * conversation number that associates a saved IP/TCP header with
+ * the compressed packet. The next two octets are the TCP checksum
+ * from the original datagram. The next 0 to 15 octets are
+ * sequence number changes, one change per bit set in the header
+ * (there may be no changes and there are two special cases where
+ * the receiver implicitly knows what changed -- see below).
+ *
+ * There are 5 numbers which can change (they are always inserted
+ * in the following order): TCP urgent pointer, window,
+ * acknowlegement, sequence number and IP ID. (The urgent pointer
+ * is different from the others in that its value is sent, not the
+ * change in value.) Since typical use of SLIP links is biased
+ * toward small packets (see comments on MTU/MSS below), changes
+ * use a variable length coding with one octet for numbers in the
+ * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the
+ * range 256 - 65535 or 0. (If the change in sequence number or
+ * ack is more than 65535, an uncompressed packet is sent.)
+ */
+
+/*
+ * Packet types (must not conflict with IP protocol version)
+ *
+ * The top nibble of the first octet is the packet type. There are
+ * three possible types: IP (not proto TCP or tcp with one of the
+ * control flags set); uncompressed TCP (a normal IP/TCP packet but
+ * with the 8-bit protocol field replaced by an 8-bit connection id --
+ * this type of packet syncs the sender & receiver); and compressed
+ * TCP (described above).
+ *
+ * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and
+ * is logically part of the 4-bit "changes" field that follows. Top
+ * three bits are actual packet type. For backward compatibility
+ * and in the interest of conserving bits, numbers are chosen so the
+ * IP protocol version number (4) which normally appears in this nibble
+ * means "IP packet".
+ */
+
+/* packet types */
+#define TYPE_IP 0x40
+#define TYPE_UNCOMPRESSED_TCP 0x70
+#define TYPE_COMPRESSED_TCP 0x80
+#define TYPE_ERROR 0x00
+
+/* Bits in first octet of compressed packet */
+#define NEW_C 0x40 /* flag bits for what changed in a packet */
+#define NEW_I 0x20
+#define NEW_S 0x08
+#define NEW_A 0x04
+#define NEW_W 0x02
+#define NEW_U 0x01
+
+/* reserved, special-case values of above */
+#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */
+#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */
+#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)
+
+#define TCP_PUSH_BIT 0x10
+
+/*
+ * "state" data for each active tcp conversation on the wire. This is
+ * basically a copy of the entire IP/TCP header from the last packet
+ * we saw from the conversation together with a small identifier
+ * the transmit & receive ends of the line use to locate saved header.
+ */
+struct cstate {
+ struct cstate *cs_next; /* next most recently used cstate (xmit only) */
+ u_short cs_hlen; /* size of hdr (receive only) */
+ u_char cs_id; /* connection # associated with this state */
+ u_char cs_filler;
+ union {
+ char csu_hdr[MAX_HDR];
+ struct ip csu_ip; /* ip/tcp hdr from most recent packet */
+ } slcs_u;
+};
+
+#define cs_ip slcs_u.csu_ip
+#define cs_hdr slcs_u.csu_hdr
+
+/*
+ * all the state data for one serial line (we need one of these
+ * per line).
+ */
+struct slcompress {
+ struct cstate *last_cs; /* most recently used tstate */
+ u_char last_recv; /* last rcvd conn. id */
+ u_char last_xmit; /* last sent conn. id */
+ u_short flags;
+ struct cstate tstate[MAX_VJ_STATES]; /* xmit connection states */
+ struct cstate rstate[MAX_VJ_STATES]; /* receive connection states */
+};
+
+struct slstat {
+ int sls_packets; /* outbound packets */
+ int sls_compressed; /* outbound compressed packets */
+ int sls_searches; /* searches for connection state */
+ int sls_misses; /* times couldn't find conn. state */
+ int sls_uncompressedin; /* inbound uncompressed packets */
+ int sls_compressedin; /* inbound compressed packets */
+ int sls_errorin; /* inbound unknown type packets */
+ int sls_tossed; /* inbound packets tossed because of error */
+};
+
+/* flag values */
+#define SLF_TOSS 1 /* tossing rcvd frames because of input err */
+
+struct mbuf;
+struct cmdargs;
+
+extern void sl_compress_init(struct slcompress *, int);
+extern u_char sl_compress_tcp(struct mbuf *, struct ip *, struct slcompress *,
+ struct slstat *, int);
+extern int sl_uncompress_tcp(u_char **, int, u_int, struct slcompress *,
+ struct slstat *, int);
+extern int sl_Show(struct cmdargs const *);
diff --git a/usr.sbin/ppp/sync.c b/usr.sbin/ppp/sync.c
new file mode 100644
index 0000000..712e036
--- /dev/null
+++ b/usr.sbin/ppp/sync.c
@@ -0,0 +1,84 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <termios.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "mbuf.h"
+#include "log.h"
+#include "sync.h"
+#include "timer.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "async.h"
+#include "descriptor.h"
+#include "physical.h"
+
+static struct mbuf *
+sync_LayerPush(struct bundle *bundle __unused, struct link *l __unused,
+ struct mbuf *bp, int pri __unused, u_short *proto __unused)
+{
+ log_DumpBp(LogSYNC, "Write", bp);
+ m_settype(bp, MB_SYNCOUT);
+ bp->priv = 0;
+ return bp;
+}
+
+static struct mbuf *
+sync_LayerPull(struct bundle *b __unused, struct link *l, struct mbuf *bp,
+ u_short *proto __unused)
+{
+ struct physical *p = link2physical(l);
+ int len;
+
+ if (!p)
+ log_Printf(LogERROR, "Can't Pull a sync packet from a logical link\n");
+ else {
+ log_DumpBp(LogSYNC, "Read", bp);
+
+ /* Either done here or by the HDLC layer */
+ len = m_length(bp);
+ p->hdlc.lqm.ifInOctets += len + 1; /* plus 1 flag octet! */
+ p->hdlc.lqm.lqr.InGoodOctets += len + 1; /* plus 1 flag octet! */
+ p->hdlc.lqm.ifInUniPackets++;
+ m_settype(bp, MB_SYNCIN);
+ }
+
+ return bp;
+}
+
+struct layer synclayer = { LAYER_SYNC, "sync", sync_LayerPush, sync_LayerPull };
diff --git a/usr.sbin/ppp/sync.h b/usr.sbin/ppp/sync.h
new file mode 100644
index 0000000..4f5bfee
--- /dev/null
+++ b/usr.sbin/ppp/sync.h
@@ -0,0 +1,29 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+extern struct layer synclayer;
diff --git a/usr.sbin/ppp/systems.c b/usr.sbin/ppp/systems.c
new file mode 100644
index 0000000..36cb4e9
--- /dev/null
+++ b/usr.sbin/ppp/systems.c
@@ -0,0 +1,483 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <ctype.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "defs.h"
+#include "command.h"
+#include "log.h"
+#include "id.h"
+#include "systems.h"
+
+#define issep(ch) ((ch) == ' ' || (ch) == '\t')
+
+FILE *
+OpenSecret(const char *file)
+{
+ FILE *fp;
+ char line[100];
+
+ snprintf(line, sizeof line, "%s/%s", PPP_CONFDIR, file);
+ fp = ID0fopen(line, "r");
+ if (fp == NULL)
+ log_Printf(LogWARN, "OpenSecret: Can't open %s.\n", line);
+ return (fp);
+}
+
+void
+CloseSecret(FILE *fp)
+{
+ fclose(fp);
+}
+
+/* Move string from ``from'' to ``to'', interpreting ``~'' and $.... */
+const char *
+InterpretArg(const char *from, char *to)
+{
+ char *ptr, *startto, *endto;
+ struct passwd *pwd;
+ int instring;
+ size_t len;
+ const char *env;
+
+ instring = 0;
+ startto = to;
+ endto = to + LINE_LEN - 1;
+
+ while(issep(*from))
+ from++;
+
+ while (*from != '\0') {
+ switch (*from) {
+ case '"':
+ instring = !instring;
+ *to++ = *from++;
+ break;
+ case '\\':
+ switch (*++from) {
+ case '$':
+ case '~':
+ break; /* Swallow the escapes */
+
+ default:
+ *to++ = '\\'; /* Pass the escapes on, maybe skipping \# */
+ break;
+ }
+ *to++ = *from++;
+ break;
+ case '$':
+ if (from[1] == '$') {
+ *to = '\0'; /* For an empty var name below */
+ from += 2;
+ } else if (from[1] == '{') {
+ ptr = strchr(from+2, '}');
+ if (ptr) {
+ len = ptr - from - 2;
+ if (endto - to < (int)len )
+ len = endto - to;
+ if (len) {
+ strncpy(to, from+2, len);
+ to[len] = '\0';
+ from = ptr+1;
+ } else {
+ *to++ = *from++;
+ continue;
+ }
+ } else {
+ *to++ = *from++;
+ continue;
+ }
+ } else {
+ ptr = to;
+ for (from++; (isalnum(*from) || *from == '_') && ptr < endto; from++)
+ *ptr++ = *from;
+ *ptr = '\0';
+ }
+ if (*to == '\0')
+ *to++ = '$';
+ else if ((env = getenv(to)) != NULL) {
+ strncpy(to, env, endto - to);
+ *endto = '\0';
+ to += strlen(to);
+ }
+ break;
+
+ case '~':
+ ptr = strchr(++from, '/');
+ len = ptr ? (size_t)(ptr - from) : strlen(from);
+ if (len == 0)
+ pwd = getpwuid(ID0realuid());
+ else {
+ strncpy(to, from, len);
+ to[len] = '\0';
+ pwd = getpwnam(to);
+ }
+ if (pwd == NULL)
+ *to++ = '~';
+ else {
+ strncpy(to, pwd->pw_dir, endto - to);
+ *endto = '\0';
+ to += strlen(to);
+ from += len;
+ }
+ endpwent();
+ break;
+
+ default:
+ *to++ = *from++;
+ break;
+ }
+ }
+
+ while (to > startto) {
+ to--;
+ if (!issep(*to)) {
+ to++;
+ break;
+ }
+ }
+ *to = '\0';
+
+ return from;
+}
+
+#define CTRL_UNKNOWN (0)
+#define CTRL_INCLUDE (1)
+
+static int
+DecodeCtrlCommand(char *line, char *arg)
+{
+ const char *end;
+
+ if (!strncasecmp(line, "include", 7) && issep(line[7])) {
+ end = InterpretArg(line+8, arg);
+ if (*end && *end != '#')
+ log_Printf(LogWARN, "usage: !include filename\n");
+ else
+ return CTRL_INCLUDE;
+ }
+ return CTRL_UNKNOWN;
+}
+
+/*
+ * Initialised in system_IsValid(), set in ReadSystem(),
+ * used by system_IsValid()
+ */
+static int modeok;
+static int userok;
+static int modereq;
+
+int
+AllowUsers(struct cmdargs const *arg)
+{
+ /* arg->bundle may be NULL (see system_IsValid()) ! */
+ int f;
+ struct passwd *pwd;
+
+ if (userok == -1)
+ userok = 0;
+
+ pwd = getpwuid(ID0realuid());
+ if (pwd != NULL)
+ for (f = arg->argn; f < arg->argc; f++)
+ if (!strcmp("*", arg->argv[f]) || !strcmp(pwd->pw_name, arg->argv[f])) {
+ userok = 1;
+ break;
+ }
+ endpwent();
+
+ return 0;
+}
+
+int
+AllowModes(struct cmdargs const *arg)
+{
+ /* arg->bundle may be NULL (see system_IsValid()) ! */
+ int f, mode, allowed;
+
+ allowed = 0;
+ for (f = arg->argn; f < arg->argc; f++) {
+ mode = Nam2mode(arg->argv[f]);
+ if (mode == PHYS_NONE || mode == PHYS_ALL)
+ log_Printf(LogWARN, "allow modes: %s: Invalid mode\n", arg->argv[f]);
+ else
+ allowed |= mode;
+ }
+
+ modeok = modereq & allowed ? 1 : 0;
+ return 0;
+}
+
+static char *
+strip(char *line)
+{
+ int len;
+
+ len = strlen(line);
+ while (len && (line[len-1] == '\n' || line[len-1] == '\r' ||
+ issep(line[len-1])))
+ line[--len] = '\0';
+
+ while (issep(*line))
+ line++;
+
+ if (*line == '#')
+ *line = '\0';
+
+ return line;
+}
+
+static int
+xgets(char *buf, int buflen, FILE *fp)
+{
+ int len, n;
+
+ n = 0;
+ while (fgets(buf, buflen-1, fp)) {
+ n++;
+ buf[buflen-1] = '\0';
+ len = strlen(buf);
+ while (len && (buf[len-1] == '\n' || buf[len-1] == '\r'))
+ buf[--len] = '\0';
+ if (len && buf[len-1] == '\\') {
+ buf += len - 1;
+ buflen -= len - 1;
+ if (!buflen) /* No buffer space */
+ break;
+ } else
+ break;
+ }
+ return n;
+}
+
+/* Values for ``how'' in ReadSystem */
+#define SYSTEM_EXISTS 1
+#define SYSTEM_VALIDATE 2
+#define SYSTEM_EXEC 3
+
+static char *
+GetLabel(char *line, const char *filename, int linenum)
+{
+ char *argv[MAXARGS];
+ int argc, len;
+
+ argc = MakeArgs(line, argv, MAXARGS, PARSE_REDUCE);
+
+ if (argc == 2 && !strcmp(argv[1], ":"))
+ return argv[0];
+
+ if (argc != 1 || (len = strlen(argv[0])) < 2 || argv[0][len-1] != ':') {
+ log_Printf(LogWARN, "Bad label in %s (line %d) - missing colon\n",
+ filename, linenum);
+ return NULL;
+ }
+ argv[0][len-1] = '\0'; /* Lose the ':' */
+
+ return argv[0];
+}
+
+/* Returns -2 for ``file not found'' and -1 for ``label not found'' */
+
+static int
+ReadSystem(struct bundle *bundle, const char *name, const char *file,
+ struct prompt *prompt, struct datalink *cx, int how)
+{
+ FILE *fp;
+ char *cp;
+ int n, len;
+ char line[LINE_LEN];
+ char filename[PATH_MAX];
+ int linenum;
+ int argc;
+ char *argv[MAXARGS];
+ int allowcmd;
+ int indent;
+ char arg[LINE_LEN];
+ struct prompt *op;
+
+ if (*file == '/')
+ snprintf(filename, sizeof filename, "%s", file);
+ else
+ snprintf(filename, sizeof filename, "%s/%s", PPP_CONFDIR, file);
+ fp = ID0fopen(filename, "r");
+ if (fp == NULL) {
+ log_Printf(LogDEBUG, "ReadSystem: Can't open %s.\n", filename);
+ return -2;
+ }
+ log_Printf(LogDEBUG, "ReadSystem: Checking %s (%s).\n", name, filename);
+
+ linenum = 0;
+ while ((n = xgets(line, sizeof line, fp))) {
+ linenum += n;
+ if (issep(*line))
+ continue;
+
+ cp = strip(line);
+
+ switch (*cp) {
+ case '\0': /* empty/comment */
+ break;
+
+ case '!':
+ switch (DecodeCtrlCommand(cp+1, arg)) {
+ case CTRL_INCLUDE:
+ log_Printf(LogCOMMAND, "%s: Including \"%s\"\n", filename, arg);
+ n = ReadSystem(bundle, name, arg, prompt, cx, how);
+ log_Printf(LogCOMMAND, "%s: Done include of \"%s\"\n", filename, arg);
+ if (!n) {
+ fclose(fp);
+ return 0; /* got it */
+ }
+ break;
+ default:
+ log_Printf(LogWARN, "%s: %s: Invalid command\n", filename, cp);
+ break;
+ }
+ break;
+
+ default:
+ if ((cp = GetLabel(cp, filename, linenum)) == NULL)
+ continue;
+
+ if (strcmp(cp, name) == 0) {
+ /* We're in business */
+ if (how == SYSTEM_EXISTS) {
+ fclose(fp);
+ return 0;
+ }
+ while ((n = xgets(line, sizeof line, fp))) {
+ linenum += n;
+ indent = issep(*line);
+ cp = strip(line);
+
+ if (*cp == '\0') /* empty / comment */
+ continue;
+
+ if (!indent) { /* start of next section */
+ if (*cp != '!' && how == SYSTEM_EXEC)
+ cp = GetLabel(cp, filename, linenum);
+ break;
+ }
+
+ len = strlen(cp);
+ if ((argc = command_Expand_Interpret(cp, len, argv, cp - line)) < 0)
+ log_Printf(LogWARN, "%s: %d: Syntax error\n", filename, linenum);
+ else {
+ allowcmd = argc > 0 && !strcasecmp(argv[0], "allow");
+ if ((how != SYSTEM_EXEC && allowcmd) ||
+ (how == SYSTEM_EXEC && !allowcmd)) {
+ /*
+ * Disable any context so that warnings are given to everyone,
+ * including syslog.
+ */
+ op = log_PromptContext;
+ log_PromptContext = NULL;
+ command_Run(bundle, argc, (char const *const *)argv, prompt,
+ name, cx);
+ log_PromptContext = op;
+ }
+ }
+ }
+
+ fclose(fp); /* everything read - get out */
+ return 0;
+ }
+ break;
+ }
+ }
+ fclose(fp);
+ return -1;
+}
+
+const char *
+system_IsValid(const char *name, struct prompt *prompt, int mode)
+{
+ /*
+ * Note: The ReadSystem() calls only result in calls to the Allow*
+ * functions. arg->bundle will be set to NULL for these commands !
+ */
+ int def, how, rs;
+ int defuserok;
+
+ def = !strcmp(name, "default");
+ how = ID0realuid() == 0 ? SYSTEM_EXISTS : SYSTEM_VALIDATE;
+ userok = -1;
+ modeok = 1;
+ modereq = mode;
+
+ rs = ReadSystem(NULL, "default", CONFFILE, prompt, NULL, how);
+
+ defuserok = userok;
+ userok = -1;
+
+ if (!def) {
+ if (rs == -1)
+ rs = 0; /* we don't care that ``default'' doesn't exist */
+
+ if (rs == 0)
+ rs = ReadSystem(NULL, name, CONFFILE, prompt, NULL, how);
+
+ if (rs == -1)
+ return "Configuration label not found";
+
+ if (rs == -2)
+ return PPP_CONFDIR "/" CONFFILE " : File not found";
+ }
+
+ if (userok == -1)
+ userok = defuserok;
+
+ if (how == SYSTEM_EXISTS)
+ userok = modeok = 1;
+
+ if (!userok)
+ return "User access denied";
+
+ if (!modeok)
+ return "Mode denied for this label";
+
+ return NULL;
+}
+
+int
+system_Select(struct bundle *bundle, const char *name, const char *file,
+ struct prompt *prompt, struct datalink *cx)
+{
+ userok = modeok = 1;
+ modereq = PHYS_ALL;
+ return ReadSystem(bundle, name, file, prompt, cx, SYSTEM_EXEC);
+}
diff --git a/usr.sbin/ppp/systems.h b/usr.sbin/ppp/systems.h
new file mode 100644
index 0000000..9091e73
--- /dev/null
+++ b/usr.sbin/ppp/systems.h
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+struct prompt;
+struct datalink;
+struct bundle;
+struct cmdargs;
+
+extern int system_Select(struct bundle *bundle, const char *, const char *,
+ struct prompt *, struct datalink *);
+extern const char *system_IsValid(const char *, struct prompt *, int);
+extern FILE *OpenSecret(const char *);
+extern void CloseSecret(FILE *);
+extern int AllowUsers(struct cmdargs const *);
+extern int AllowModes(struct cmdargs const *);
+extern const char *InterpretArg(const char *, char *);
diff --git a/usr.sbin/ppp/tcp.c b/usr.sbin/ppp/tcp.c
new file mode 100644
index 0000000..4ace882
--- /dev/null
+++ b/usr.sbin/ppp/tcp.c
@@ -0,0 +1,212 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "async.h"
+#include "descriptor.h"
+#include "physical.h"
+#include "tcp.h"
+
+static int
+tcp_OpenConnection(const char *name, char *host, char *port)
+{
+ struct sockaddr_in dest;
+ int sock;
+ struct servent *sp;
+
+ dest.sin_family = AF_INET;
+ dest.sin_addr = GetIpAddr(host);
+ if (dest.sin_addr.s_addr == INADDR_NONE) {
+ log_Printf(LogWARN, "%s: %s: unknown host\n", name, host);
+ return -2;
+ }
+ dest.sin_port = htons(atoi(port));
+ if (dest.sin_port == 0) {
+ sp = getservbyname(port, "tcp");
+ if (sp)
+ dest.sin_port = sp->s_port;
+ else {
+ log_Printf(LogWARN, "%s: %s: unknown service\n", name, port);
+ return -2;
+ }
+ }
+ log_Printf(LogPHASE, "%s: Connecting to %s:%s/tcp\n", name, host, port);
+
+ sock = socket(PF_INET, SOCK_STREAM, 0);
+ if (sock < 0)
+ return -2;
+
+ if (connect(sock, (struct sockaddr *)&dest, sizeof dest) < 0) {
+ log_Printf(LogWARN, "%s: connect: %s\n", name, strerror(errno));
+ close(sock);
+ return -2;
+ }
+
+ return sock;
+}
+
+static struct device tcpdevice = {
+ TCP_DEVICE,
+ "tcp",
+ 0,
+ { CD_NOTREQUIRED, 0 },
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+struct device *
+tcp_iov2device(int type, struct physical *p, struct iovec *iov,
+ int *niov, int maxiov __unused, int *auxfd __unused,
+ int *nauxfd __unused)
+{
+ if (type == TCP_DEVICE) {
+ free(iov[(*niov)++].iov_base);
+ physical_SetupStack(p, tcpdevice.name, PHYSICAL_FORCE_ASYNC);
+ return &tcpdevice;
+ }
+
+ return NULL;
+}
+
+struct device *
+tcp_Create(struct physical *p)
+{
+ char *cp, *host, *port, *svc;
+
+ if (p->fd < 0) {
+ if ((cp = strchr(p->name.full, ':')) != NULL && !strchr(cp + 1, ':')) {
+ *cp = '\0';
+ host = p->name.full;
+ port = cp + 1;
+ svc = strchr(port, '/');
+ if (svc && strcasecmp(svc, "/tcp")) {
+ *cp = ':';
+ return 0;
+ }
+ if (svc) {
+ p->fd--; /* We own the device but maybe can't use it - change fd */
+ *svc = '\0';
+ }
+ if (*host && *port) {
+ p->fd = tcp_OpenConnection(p->link.name, host, port);
+ *cp = ':';
+ if (svc)
+ *svc = '/';
+ if (p->fd >= 0)
+ log_Printf(LogDEBUG, "%s: Opened tcp socket %s\n", p->link.name,
+ p->name.full);
+ } else {
+ if (svc)
+ *svc = '/';
+ *cp = ':';
+ }
+ }
+ }
+
+ if (p->fd >= 0) {
+ /* See if we're a tcp socket */
+ struct stat st;
+
+ if (fstat(p->fd, &st) != -1 && (st.st_mode & S_IFSOCK)) {
+ int type, sz;
+
+ sz = sizeof type;
+ if (getsockopt(p->fd, SOL_SOCKET, SO_TYPE, &type, &sz) == -1) {
+ log_Printf(LogPHASE, "%s: Link is a closed socket !\n", p->link.name);
+ close(p->fd);
+ p->fd = -1;
+ return NULL;
+ }
+
+ if (sz == sizeof type && type == SOCK_STREAM) {
+ struct sockaddr_in sock;
+ struct sockaddr *sockp = (struct sockaddr *)&sock;
+
+ if (*p->name.full == '\0') {
+ sz = sizeof sock;
+ if (getpeername(p->fd, sockp, &sz) != 0 ||
+ sz != sizeof(struct sockaddr_in) || sock.sin_family != AF_INET) {
+ log_Printf(LogDEBUG, "%s: Link is SOCK_STREAM, but not inet\n",
+ p->link.name);
+ return NULL;
+ }
+
+ log_Printf(LogPHASE, "%s: Link is a tcp socket\n", p->link.name);
+
+ snprintf(p->name.full, sizeof p->name.full, "%s:%d/tcp",
+ inet_ntoa(sock.sin_addr), ntohs(sock.sin_port));
+ p->name.base = p->name.full;
+ }
+ physical_SetupStack(p, tcpdevice.name, PHYSICAL_FORCE_ASYNC);
+ if (p->cfg.cd.necessity != CD_DEFAULT)
+ log_Printf(LogWARN, "Carrier settings ignored\n");
+ return &tcpdevice;
+ }
+ }
+ }
+
+ return NULL;
+}
diff --git a/usr.sbin/ppp/tcp.h b/usr.sbin/ppp/tcp.h
new file mode 100644
index 0000000..f6a8235
--- /dev/null
+++ b/usr.sbin/ppp/tcp.h
@@ -0,0 +1,34 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct physical;
+
+extern struct device *tcp_Create(struct physical *);
+extern struct device *tcp_iov2device(int, struct physical *,
+ struct iovec *, int *, int, int *, int *);
+#define tcp_DeviceSize physical_DeviceSize
diff --git a/usr.sbin/ppp/tcpmss.c b/usr.sbin/ppp/tcpmss.c
new file mode 100644
index 0000000..38d2920
--- /dev/null
+++ b/usr.sbin/ppp/tcpmss.c
@@ -0,0 +1,185 @@
+/*-
+ * Copyright (c) 2000 Ruslan Ermilov and Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+
+#include <sys/socket.h>
+#include <net/route.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+#include <sys/un.h>
+
+#include <termios.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "mbuf.h"
+#include "throughput.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "iplist.h"
+#include "slcompress.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "mp.h"
+#include "iface.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+
+
+/*-
+ * We are in a liberal position about MSS
+ * (RFC 879, section 7).
+ */
+#define MAXMSS(mtu) ((mtu) - sizeof(struct ip) - sizeof(struct tcphdr) - 12)
+
+
+/*-
+ * The following macro is used to update an
+ * internet checksum. "acc" is a 32-bit
+ * accumulation of all the changes to the
+ * checksum (adding in old 16-bit words and
+ * subtracting out new words), and "cksum"
+ * is the checksum value to be updated.
+ */
+#define ADJUST_CHECKSUM(acc, cksum) { \
+ acc += cksum; \
+ if (acc < 0) { \
+ acc = -acc; \
+ acc = (acc >> 16) + (acc & 0xffff); \
+ acc += acc >> 16; \
+ cksum = (u_short) ~acc; \
+ } else { \
+ acc = (acc >> 16) + (acc & 0xffff); \
+ acc += acc >> 16; \
+ cksum = (u_short) acc; \
+ } \
+}
+
+static void
+MSSFixup(struct tcphdr *tc, size_t pktlen, u_int16_t maxmss)
+{
+ size_t hlen, olen, optlen;
+ u_char *opt;
+ u_int16_t *mss;
+ int accumulate;
+
+ hlen = tc->th_off << 2;
+
+ /* Invalid header length or header without options. */
+ if (hlen <= sizeof(struct tcphdr) || hlen > pktlen)
+ return;
+
+ /* MSS option only allowed within SYN packets. */
+ if (!(tc->th_flags & TH_SYN))
+ return;
+
+ for (olen = hlen - sizeof(struct tcphdr), opt = (u_char *)(tc + 1);
+ olen > 0; olen -= optlen, opt += optlen) {
+ if (*opt == TCPOPT_EOL)
+ break;
+ else if (*opt == TCPOPT_NOP)
+ optlen = 1;
+ else {
+ optlen = *(opt + 1);
+ if (optlen <= 0 || optlen > olen)
+ break;
+ if (*opt == TCPOPT_MAXSEG) {
+ if (optlen != TCPOLEN_MAXSEG)
+ continue;
+ mss = (u_int16_t *)(opt + 2);
+ if (ntohs(*mss) > maxmss) {
+ log_Printf(LogDEBUG, "MSS: %u -> %u\n",
+ ntohs(*mss), maxmss);
+ accumulate = *mss;
+ *mss = htons(maxmss);
+ accumulate -= *mss;
+ ADJUST_CHECKSUM(accumulate, tc->th_sum);
+ }
+ }
+ }
+ }
+}
+
+static struct mbuf *
+tcpmss_Check(struct bundle *bundle, struct mbuf *bp)
+{
+ struct ip *pip;
+ size_t hlen, plen;
+
+ if (!Enabled(bundle, OPT_TCPMSSFIXUP))
+ return bp;
+
+ bp = m_pullup(bp);
+ plen = m_length(bp);
+ pip = (struct ip *)MBUF_CTOP(bp);
+ hlen = pip->ip_hl << 2;
+
+ /*
+ * Check for MSS option only for TCP packets with zero fragment offsets
+ * and correct total and header lengths.
+ */
+ if (pip->ip_p == IPPROTO_TCP && (ntohs(pip->ip_off) & IP_OFFMASK) == 0 &&
+ ntohs(pip->ip_len) == plen && hlen <= plen &&
+ plen >= sizeof(struct tcphdr) + hlen)
+ MSSFixup((struct tcphdr *)(MBUF_CTOP(bp) + hlen), plen - hlen,
+ MAXMSS(bundle->iface->mtu));
+
+ return bp;
+}
+
+static struct mbuf *
+tcpmss_LayerPush(struct bundle *bundle, struct link *l __unused,
+ struct mbuf *bp, int pri __unused, u_short *proto __unused)
+{
+ return tcpmss_Check(bundle, bp);
+}
+
+static struct mbuf *
+tcpmss_LayerPull(struct bundle *bundle, struct link *l __unused,
+ struct mbuf *bp, u_short *proto __unused)
+{
+ return tcpmss_Check(bundle, bp);
+}
+
+struct layer tcpmsslayer =
+ { LAYER_PROTO, "tcpmss", tcpmss_LayerPush, tcpmss_LayerPull };
diff --git a/usr.sbin/ppp/tcpmss.h b/usr.sbin/ppp/tcpmss.h
new file mode 100644
index 0000000..ffd7b82
--- /dev/null
+++ b/usr.sbin/ppp/tcpmss.h
@@ -0,0 +1,29 @@
+/*-
+ * Copyright (c) 2000 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+extern struct layer tcpmsslayer;
diff --git a/usr.sbin/ppp/throughput.c b/usr.sbin/ppp/throughput.c
new file mode 100644
index 0000000..f6bc9b5
--- /dev/null
+++ b/usr.sbin/ppp/throughput.c
@@ -0,0 +1,302 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <time.h>
+
+#include "log.h"
+#include "timer.h"
+#include "throughput.h"
+#include "descriptor.h"
+#include "prompt.h"
+
+
+void
+throughput_init(struct pppThroughput *t, int period)
+{
+ t->OctetsIn = t->OctetsOut = t->PacketsIn = t->PacketsOut = 0;
+ t->SamplePeriod = period;
+ t->in.SampleOctets = (long long *)
+ calloc(period, sizeof *t->in.SampleOctets);
+ t->in.OctetsPerSecond = 0;
+ t->out.SampleOctets = (long long *)
+ calloc(period, sizeof *t->out.SampleOctets);
+ t->out.OctetsPerSecond = 0;
+ t->BestOctetsPerSecond = 0;
+ t->nSample = 0;
+ time(&t->BestOctetsPerSecondTime);
+ memset(&t->Timer, '\0', sizeof t->Timer);
+ t->Timer.name = "throughput";
+ t->uptime = 0;
+ t->downtime = 0;
+ t->rolling = 0;
+ t->callback.data = NULL;
+ t->callback.fn = NULL;
+ throughput_stop(t);
+}
+
+void
+throughput_destroy(struct pppThroughput *t)
+{
+ if (t && t->in.SampleOctets) {
+ throughput_stop(t);
+ free(t->in.SampleOctets);
+ free(t->out.SampleOctets);
+ t->in.SampleOctets = NULL;
+ t->out.SampleOctets = NULL;
+ }
+}
+
+int
+throughput_uptime(struct pppThroughput *t)
+{
+ time_t downat;
+
+ downat = t->downtime ? t->downtime : time(NULL);
+ if (t->uptime && downat < t->uptime) {
+ /* Euch ! The clock's gone back ! */
+ int i;
+
+ for (i = 0; i < t->SamplePeriod; i++)
+ t->in.SampleOctets[i] = t->out.SampleOctets[i] = 0;
+ t->nSample = 0;
+ t->uptime = downat;
+ }
+ return t->uptime ? downat - t->uptime : 0;
+}
+
+void
+throughput_disp(struct pppThroughput *t, struct prompt *prompt)
+{
+ int secs_up, divisor;
+
+ secs_up = throughput_uptime(t);
+ prompt_Printf(prompt, "Connect time: %d:%02d:%02d", secs_up / 3600,
+ (secs_up / 60) % 60, secs_up % 60);
+ if (t->downtime)
+ prompt_Printf(prompt, " - down at %s", ctime(&t->downtime));
+ else
+ prompt_Printf(prompt, "\n");
+
+ divisor = secs_up ? secs_up : 1;
+ prompt_Printf(prompt, "%llu octets in, %llu octets out\n",
+ t->OctetsIn, t->OctetsOut);
+ prompt_Printf(prompt, "%llu packets in, %llu packets out\n",
+ t->PacketsIn, t->PacketsOut);
+ if (t->rolling) {
+ prompt_Printf(prompt, " overall %6qu bytes/sec\n",
+ (t->OctetsIn + t->OctetsOut) / divisor);
+ prompt_Printf(prompt, " %s %6qu bytes/sec in, %6qu bytes/sec out "
+ "(over the last %d secs)\n",
+ t->downtime ? "average " : "currently",
+ t->in.OctetsPerSecond, t->out.OctetsPerSecond,
+ secs_up > t->SamplePeriod ? t->SamplePeriod : secs_up);
+ prompt_Printf(prompt, " peak %6qu bytes/sec on %s",
+ t->BestOctetsPerSecond, ctime(&t->BestOctetsPerSecondTime));
+ } else
+ prompt_Printf(prompt, "Overall %llu bytes/sec\n",
+ (t->OctetsIn + t->OctetsOut) / divisor);
+}
+
+
+void
+throughput_log(struct pppThroughput *t, int level, const char *title)
+{
+ if (t->uptime) {
+ int secs_up;
+
+ secs_up = throughput_uptime(t);
+ if (title == NULL)
+ title = "";
+ log_Printf(level, "%s%sConnect time: %d secs: %llu octets in, %llu octets"
+ " out\n", title, *title ? ": " : "", secs_up, t->OctetsIn,
+ t->OctetsOut);
+ log_Printf(level, "%s%s%llu packets in, %llu packets out\n",
+ title, *title ? ": " : "", t->PacketsIn, t->PacketsOut);
+ if (secs_up == 0)
+ secs_up = 1;
+ if (t->rolling)
+ log_Printf(level, " total %llu bytes/sec, peak %llu bytes/sec on %s",
+ (t->OctetsIn + t->OctetsOut) / secs_up, t->BestOctetsPerSecond,
+ ctime(&t->BestOctetsPerSecondTime));
+ else
+ log_Printf(level, " total %llu bytes/sec\n",
+ (t->OctetsIn + t->OctetsOut) / secs_up);
+ }
+}
+
+static void
+throughput_sampler(void *v)
+{
+ struct pppThroughput *t = (struct pppThroughput *)v;
+ unsigned long long old;
+ int uptime, divisor;
+ unsigned long long octets;
+
+ timer_Stop(&t->Timer);
+
+ uptime = throughput_uptime(t);
+ divisor = uptime < t->SamplePeriod ? uptime + 1 : t->SamplePeriod;
+
+ old = t->in.SampleOctets[t->nSample];
+ t->in.SampleOctets[t->nSample] = t->OctetsIn;
+ t->in.OctetsPerSecond = (t->in.SampleOctets[t->nSample] - old) / divisor;
+
+ old = t->out.SampleOctets[t->nSample];
+ t->out.SampleOctets[t->nSample] = t->OctetsOut;
+ t->out.OctetsPerSecond = (t->out.SampleOctets[t->nSample] - old) / divisor;
+
+ octets = t->in.OctetsPerSecond + t->out.OctetsPerSecond;
+ if (t->BestOctetsPerSecond < octets) {
+ t->BestOctetsPerSecond = octets;
+ time(&t->BestOctetsPerSecondTime);
+ }
+
+ if (++t->nSample == t->SamplePeriod)
+ t->nSample = 0;
+
+ if (t->callback.fn != NULL && uptime >= t->SamplePeriod)
+ (*t->callback.fn)(t->callback.data);
+
+ timer_Start(&t->Timer);
+}
+
+void
+throughput_start(struct pppThroughput *t, const char *name, int rolling)
+{
+ int i;
+ timer_Stop(&t->Timer);
+
+ for (i = 0; i < t->SamplePeriod; i++)
+ t->in.SampleOctets[i] = t->out.SampleOctets[i] = 0;
+ t->nSample = 0;
+ t->OctetsIn = t->OctetsOut = 0;
+ t->in.OctetsPerSecond = t->out.OctetsPerSecond = t->BestOctetsPerSecond = 0;
+ time(&t->BestOctetsPerSecondTime);
+ t->downtime = 0;
+ time(&t->uptime);
+ throughput_restart(t, name, rolling);
+}
+
+void
+throughput_restart(struct pppThroughput *t, const char *name, int rolling)
+{
+ timer_Stop(&t->Timer);
+ t->rolling = rolling ? 1 : 0;
+ if (t->rolling) {
+ t->Timer.load = SECTICKS;
+ t->Timer.func = throughput_sampler;
+ t->Timer.name = name;
+ t->Timer.arg = t;
+ timer_Start(&t->Timer);
+ } else {
+ t->Timer.load = 0;
+ t->Timer.func = NULL;
+ t->Timer.name = NULL;
+ t->Timer.arg = NULL;
+ }
+}
+
+void
+throughput_stop(struct pppThroughput *t)
+{
+ if (t->Timer.state != TIMER_STOPPED)
+ time(&t->downtime);
+ timer_Stop(&t->Timer);
+}
+
+void
+throughput_addin(struct pppThroughput *t, long long n)
+{
+ t->OctetsIn += n;
+ t->PacketsIn++;
+}
+
+void
+throughput_addout(struct pppThroughput *t, long long n)
+{
+ t->OctetsOut += n;
+ t->PacketsOut++;
+}
+
+void
+throughput_clear(struct pppThroughput *t, int clear_type, struct prompt *prompt)
+{
+ if (clear_type & (THROUGHPUT_OVERALL|THROUGHPUT_CURRENT)) {
+ int i;
+
+ for (i = 0; i < t->SamplePeriod; i++)
+ t->in.SampleOctets[i] = t->out.SampleOctets[i] = 0;
+ t->nSample = 0;
+ }
+
+ if (clear_type & THROUGHPUT_OVERALL) {
+ int divisor;
+
+ if ((divisor = throughput_uptime(t)) == 0)
+ divisor = 1;
+ prompt_Printf(prompt, "overall cleared (was %6qu bytes/sec)\n",
+ (t->OctetsIn + t->OctetsOut) / divisor);
+ t->OctetsIn = t->OctetsOut = 0;
+ t->downtime = 0;
+ time(&t->uptime);
+ }
+
+ if (clear_type & THROUGHPUT_CURRENT) {
+ prompt_Printf(prompt, "current cleared (was %6qu bytes/sec in,"
+ " %6qu bytes/sec out)\n",
+ t->in.OctetsPerSecond, t->out.OctetsPerSecond);
+ t->in.OctetsPerSecond = t->out.OctetsPerSecond = 0;
+ }
+
+ if (clear_type & THROUGHPUT_PEAK) {
+ char *time_buf, *last;
+
+ time_buf = ctime(&t->BestOctetsPerSecondTime);
+ last = time_buf + strlen(time_buf);
+ if (last > time_buf && *--last == '\n')
+ *last = '\0';
+ prompt_Printf(prompt, "peak cleared (was %6qu bytes/sec on %s)\n",
+ t->BestOctetsPerSecond, time_buf);
+ t->BestOctetsPerSecond = 0;
+ time(&t->BestOctetsPerSecondTime);
+ }
+}
+
+void
+throughput_callback(struct pppThroughput *t, void (*fn)(void *), void *data)
+{
+ t->callback.fn = fn;
+ t->callback.data = data;
+}
diff --git a/usr.sbin/ppp/throughput.h b/usr.sbin/ppp/throughput.h
new file mode 100644
index 0000000..21e5450
--- /dev/null
+++ b/usr.sbin/ppp/throughput.h
@@ -0,0 +1,70 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#define SAMPLE_PERIOD 5 /* Default sample period */
+
+#define THROUGHPUT_OVERALL 0x0001
+#define THROUGHPUT_CURRENT 0x0002
+#define THROUGHPUT_PEAK 0x0004
+#define THROUGHPUT_ALL 0x0007
+
+struct pppThroughput {
+ time_t uptime, downtime;
+ unsigned long long OctetsIn;
+ unsigned long long OctetsOut;
+ unsigned long long PacketsIn;
+ unsigned long long PacketsOut;
+ int SamplePeriod;
+ struct {
+ unsigned long long *SampleOctets;
+ unsigned long long OctetsPerSecond;
+ } in, out;
+ unsigned long long BestOctetsPerSecond;
+ time_t BestOctetsPerSecondTime;
+ int nSample;
+ unsigned rolling : 1;
+ struct pppTimer Timer;
+ struct {
+ void *data;
+ void (*fn)(void *v);
+ } callback;
+};
+
+extern void throughput_init(struct pppThroughput *, int);
+extern void throughput_destroy(struct pppThroughput *);
+extern void throughput_disp(struct pppThroughput *, struct prompt *);
+extern void throughput_log(struct pppThroughput *, int, const char *);
+extern void throughput_start(struct pppThroughput *, const char *, int);
+extern void throughput_restart(struct pppThroughput *, const char *, int);
+extern void throughput_stop(struct pppThroughput *);
+extern void throughput_addin(struct pppThroughput *, long long);
+extern void throughput_addout(struct pppThroughput *, long long);
+extern void throughput_clear(struct pppThroughput *, int, struct prompt *);
+extern void throughput_callback(struct pppThroughput *, void (*)(void *),
+ void *);
+extern int throughput_uptime(struct pppThroughput *);
diff --git a/usr.sbin/ppp/timer.c b/usr.sbin/ppp/timer.c
new file mode 100644
index 0000000..77b2af7
--- /dev/null
+++ b/usr.sbin/ppp/timer.c
@@ -0,0 +1,293 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <errno.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+#include <termios.h>
+
+#include "log.h"
+#include "sig.h"
+#include "timer.h"
+#include "descriptor.h"
+#include "prompt.h"
+
+
+#define RESTVAL(t) \
+ ((t).it_value.tv_sec * SECTICKS + (t).it_value.tv_usec / TICKUNIT + \
+ ((((t).it_value.tv_usec % TICKUNIT) >= (TICKUNIT >> 1)) ? 1 : 0))
+
+static struct pppTimer *TimerList = NULL, *ExpiredList = NULL;
+
+static void StopTimerNoBlock(struct pppTimer *);
+
+static const char *
+tState2Nam(u_int state)
+{
+ static const char * const StateNames[] = { "stopped", "running", "expired" };
+
+ if (state >= sizeof StateNames / sizeof StateNames[0])
+ return "unknown";
+ return StateNames[state];
+}
+
+void
+timer_Stop(struct pppTimer *tp)
+{
+ sigset_t mask, omask;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGALRM);
+ sigprocmask(SIG_BLOCK, &mask, &omask);
+ StopTimerNoBlock(tp);
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+}
+
+void
+timer_Start(struct pppTimer *tp)
+{
+ struct itimerval itimer;
+ struct pppTimer *t, *pt;
+ u_long ticks = 0;
+ sigset_t mask, omask;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGALRM);
+ sigprocmask(SIG_BLOCK, &mask, &omask);
+
+ if (tp->state != TIMER_STOPPED)
+ StopTimerNoBlock(tp);
+
+ if (tp->load == 0) {
+ log_Printf(LogTIMER, "%s timer[%p] has 0 load!\n", tp->name, tp);
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+ return;
+ }
+
+ /* Adjust our first delta so that it reflects what's really happening */
+ if (TimerList && getitimer(ITIMER_REAL, &itimer) == 0)
+ TimerList->rest = RESTVAL(itimer);
+
+ pt = NULL;
+ for (t = TimerList; t; t = t->next) {
+ if (ticks + t->rest >= tp->load)
+ break;
+ ticks += t->rest;
+ pt = t;
+ }
+
+ tp->state = TIMER_RUNNING;
+ tp->rest = tp->load - ticks;
+
+ if (t)
+ log_Printf(LogTIMER, "timer_Start: Inserting %s timer[%p] before %s "
+ "timer[%p], delta = %ld\n", tp->name, tp, t->name, t, tp->rest);
+ else
+ log_Printf(LogTIMER, "timer_Start: Inserting %s timer[%p]\n", tp->name, tp);
+
+ /* Insert given *tp just before *t */
+ tp->next = t;
+ if (pt) {
+ pt->next = tp;
+ } else {
+ TimerList = tp;
+ timer_InitService(t != NULL); /* [re]Start the Timer Service */
+ }
+ if (t)
+ t->rest -= tp->rest;
+
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+}
+
+static void
+StopTimerNoBlock(struct pppTimer *tp)
+{
+ struct pppTimer *t, *pt;
+
+ /*
+ * A RUNNING timer must be removed from TimerList (->next list).
+ * A STOPPED timer isn't in any list, but may have a bogus [e]next field.
+ * An EXPIRED timer is in the ->enext list.
+ */
+
+ if (tp->state == TIMER_STOPPED)
+ return;
+
+ pt = NULL;
+ for (t = TimerList; t != tp && t != NULL; t = t->next)
+ pt = t;
+
+ if (t) {
+ if (pt)
+ pt->next = t->next;
+ else {
+ TimerList = t->next;
+ if (TimerList == NULL) /* Last one ? */
+ timer_TermService(); /* Terminate Timer Service */
+ }
+ if (t->next) {
+ if (!pt) { /* t (tp) was the first in the list */
+ struct itimerval itimer;
+
+ if (getitimer(ITIMER_REAL, &itimer) == 0)
+ t->rest = RESTVAL(itimer);
+ }
+ t->next->rest += t->rest;
+ if (!pt) /* t->next is now the first in the list */
+ timer_InitService(1);
+ }
+ } else {
+ /* Search for any pending expired timers */
+ pt = NULL;
+ for (t = ExpiredList; t != tp && t != NULL; t = t->enext)
+ pt = t;
+
+ if (t) {
+ if (pt)
+ pt->enext = t->enext;
+ else
+ ExpiredList = t->enext;
+ } else if (tp->state == TIMER_RUNNING)
+ log_Printf(LogERROR, "Oops, %s timer not found!!\n", tp->name);
+ }
+
+ tp->next = tp->enext = NULL;
+ tp->state = TIMER_STOPPED;
+}
+
+static void
+TimerService(void)
+{
+ struct pppTimer *tp, *exp, *next;
+
+ if (log_IsKept(LogTIMER)) {
+ static time_t t; /* Only show timers globally every second */
+ time_t n = time(NULL);
+
+ if (n > t)
+ timer_Show(LogTIMER, NULL);
+ t = n;
+ }
+
+ tp = TimerList;
+ if (tp) {
+ tp->rest = 0;
+
+ /* Multiple timers might expire at once. Create a list of expired timers */
+ exp = NULL;
+ do {
+ tp->state = TIMER_EXPIRED;
+ next = tp->next;
+ tp->enext = exp;
+ exp = tp;
+ tp = next;
+ } while (tp && tp->rest == 0);
+
+ TimerList = tp;
+ if (TimerList != NULL) /* Any timers remaining ? */
+ timer_InitService(1); /* Restart the Timer Service */
+ else
+ timer_TermService(); /* Stop the Timer Service */
+
+ /* Process all expired timers */
+ while (exp) {
+ ExpiredList = exp->enext;
+ exp->enext = NULL;
+ if (exp->func)
+ (*exp->func)(exp->arg);
+ exp = ExpiredList;
+ }
+ }
+}
+
+void
+timer_Show(int LogLevel, struct prompt *prompt)
+{
+ struct itimerval itimer;
+ struct pppTimer *pt;
+ u_long rest = 0;
+
+ /* Adjust our first delta so that it reflects what's really happening */
+ if (TimerList && getitimer(ITIMER_REAL, &itimer) == 0)
+ TimerList->rest = RESTVAL(itimer);
+
+#define SECS(val) ((val) / SECTICKS)
+#define HSECS(val) (((val) % SECTICKS) * 100 / SECTICKS)
+#define DISP \
+ "%s timer[%p]: freq = %ld.%02lds, next = %lu.%02lus, state = %s\n", \
+ pt->name, pt, SECS(pt->load), HSECS(pt->load), SECS(rest), \
+ HSECS(rest), tState2Nam(pt->state)
+
+ if (!prompt)
+ log_Printf(LogLevel, "---- Begin of Timer Service List---\n");
+
+ for (pt = TimerList; pt; pt = pt->next) {
+ rest += pt->rest;
+ if (prompt)
+ prompt_Printf(prompt, DISP);
+ else
+ log_Printf(LogLevel, DISP);
+ }
+
+ if (!prompt)
+ log_Printf(LogLevel, "---- End of Timer Service List ---\n");
+}
+
+void
+timer_InitService(int restart)
+{
+ struct itimerval itimer;
+
+ if (TimerList) {
+ if (!restart)
+ sig_signal(SIGALRM, (void (*)(int))TimerService);
+ itimer.it_interval.tv_sec = 0;
+ itimer.it_interval.tv_usec = 0;
+ itimer.it_value.tv_sec = TimerList->rest / SECTICKS;
+ itimer.it_value.tv_usec = (TimerList->rest % SECTICKS) * TICKUNIT;
+ if (setitimer(ITIMER_REAL, &itimer, NULL) == -1)
+ log_Printf(LogERROR, "Unable to set itimer (%s)\n", strerror(errno));
+ }
+}
+
+void
+timer_TermService(void)
+{
+ struct itimerval itimer;
+
+ itimer.it_interval.tv_usec = itimer.it_interval.tv_sec = 0;
+ itimer.it_value.tv_usec = itimer.it_value.tv_sec = 0;
+ if (setitimer(ITIMER_REAL, &itimer, NULL) == -1)
+ log_Printf(LogERROR, "Unable to set itimer (%s)\n", strerror(errno));
+ sig_signal(SIGALRM, SIG_IGN);
+}
diff --git a/usr.sbin/ppp/timer.h b/usr.sbin/ppp/timer.h
new file mode 100644
index 0000000..02fcdd1
--- /dev/null
+++ b/usr.sbin/ppp/timer.h
@@ -0,0 +1,55 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+#define TICKUNIT 100000 /* usec's per Unit */
+#define SECTICKS (1000000/TICKUNIT) /* Units per second */
+
+struct pppTimer {
+ int state;
+ const char *name;
+ u_long rest; /* Ticks to expire */
+ u_long load; /* Initial load value */
+ void (*func)(void *); /* Function called when timer is expired */
+ void *arg; /* Argument passed to timeout function */
+ struct pppTimer *next; /* Link to next timer */
+ struct pppTimer *enext; /* Link to next expired timer */
+};
+
+#define TIMER_STOPPED 0
+#define TIMER_RUNNING 1
+#define TIMER_EXPIRED 2
+
+struct prompt;
+
+extern void timer_Start(struct pppTimer *);
+extern void timer_Stop(struct pppTimer *);
+extern void timer_InitService(int);
+extern void timer_TermService(void);
+extern void timer_Show(int LogLevel, struct prompt *);
diff --git a/usr.sbin/ppp/tty.c b/usr.sbin/ppp/tty.c
new file mode 100644
index 0000000..8d5d58b
--- /dev/null
+++ b/usr.sbin/ppp/tty.c
@@ -0,0 +1,769 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/un.h>
+#if defined(__OpenBSD__) || defined(__NetBSD__)
+#include <sys/ioctl.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <sys/uio.h>
+#include <termios.h>
+#include <ttyent.h>
+#include <unistd.h>
+#ifndef NONETGRAPH
+#include <netgraph.h>
+#include <netgraph/ng_async.h>
+#include <netgraph/ng_message.h>
+#include <netgraph/ng_ppp.h>
+#include <netgraph/ng_tty.h>
+#endif
+
+#include "layer.h"
+#include "defs.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "async.h"
+#include "descriptor.h"
+#include "physical.h"
+#include "mp.h"
+#include "chat.h"
+#include "auth.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+#include "main.h"
+#include "id.h"
+#include "tty.h"
+
+#if defined(__mac68k__) || defined(__macppc__)
+#undef CRTS_IFLOW
+#undef CCTS_OFLOW
+#define CRTS_IFLOW CDTRCTS
+#define CCTS_OFLOW CDTRCTS
+#endif
+
+#define Online(dev) ((dev)->mbits & TIOCM_CD)
+
+struct ttydevice {
+ struct device dev; /* What struct physical knows about */
+ struct pppTimer Timer; /* CD checks */
+ int mbits; /* Current DCD status */
+ int carrier_seconds; /* seconds before CD is *required* */
+#ifndef NONETGRAPH
+ struct {
+ unsigned speed; /* Pre-line-discipline speed */
+ int fd; /* Pre-line-discipline fd */
+ int disc; /* Old line-discipline */
+ } real;
+ char hook[sizeof NG_ASYNC_HOOK_SYNC]; /* our ng_socket hook */
+ int cs; /* A netgraph control socket (maybe) */
+#endif
+ struct termios ios; /* To be able to reset from raw mode */
+};
+
+#define device2tty(d) ((d)->type == TTY_DEVICE ? (struct ttydevice *)d : NULL)
+
+unsigned
+tty_DeviceSize(void)
+{
+ return sizeof(struct ttydevice);
+}
+
+/*
+ * tty_Timeout() watches the DCD signal and mentions it if it's status
+ * changes.
+ */
+static void
+tty_Timeout(void *data)
+{
+ struct physical *p = data;
+ struct ttydevice *dev = device2tty(p->handler);
+ int ombits, change;
+
+ timer_Stop(&dev->Timer);
+ dev->Timer.load = SECTICKS; /* Once a second please */
+ timer_Start(&dev->Timer);
+ ombits = dev->mbits;
+
+ if (p->fd >= 0) {
+ if (ioctl(p->fd, TIOCMGET, &dev->mbits) < 0) {
+ /* we must be a pty ? */
+ if (p->cfg.cd.necessity != CD_DEFAULT)
+ log_Printf(LogWARN, "%s: Carrier ioctl not supported, "
+ "using ``set cd off''\n", p->link.name);
+ timer_Stop(&dev->Timer);
+ dev->mbits = TIOCM_CD;
+ return;
+ }
+ } else
+ dev->mbits = 0;
+
+ if (ombits == -1) {
+ /* First time looking for carrier */
+ if (Online(dev))
+ log_Printf(LogPHASE, "%s: %s: CD detected\n", p->link.name, p->name.full);
+ else if (++dev->carrier_seconds >= dev->dev.cd.delay) {
+ if (dev->dev.cd.necessity == CD_REQUIRED)
+ log_Printf(LogPHASE, "%s: %s: Required CD not detected\n",
+ p->link.name, p->name.full);
+ else {
+ log_Printf(LogPHASE, "%s: %s doesn't support CD\n",
+ p->link.name, p->name.full);
+ dev->mbits = TIOCM_CD; /* Dodgy null-modem cable ? */
+ }
+ timer_Stop(&dev->Timer);
+ /* tty_AwaitCarrier() will notice */
+ } else {
+ /* Keep waiting */
+ log_Printf(LogDEBUG, "%s: %s: Still no carrier (%d/%d)\n",
+ p->link.name, p->name.full, dev->carrier_seconds,
+ dev->dev.cd.delay);
+ dev->mbits = -1;
+ }
+ } else {
+ change = ombits ^ dev->mbits;
+ if (change & TIOCM_CD) {
+ if (dev->mbits & TIOCM_CD)
+ log_Printf(LogDEBUG, "%s: offline -> online\n", p->link.name);
+ else {
+ log_Printf(LogDEBUG, "%s: online -> offline\n", p->link.name);
+ log_Printf(LogPHASE, "%s: Carrier lost\n", p->link.name);
+ datalink_Down(p->dl, CLOSE_NORMAL);
+ timer_Stop(&dev->Timer);
+ }
+ } else
+ log_Printf(LogDEBUG, "%s: Still %sline\n", p->link.name,
+ Online(dev) ? "on" : "off");
+ }
+}
+
+static void
+tty_StartTimer(struct physical *p)
+{
+ struct ttydevice *dev = device2tty(p->handler);
+
+ timer_Stop(&dev->Timer);
+ dev->Timer.load = SECTICKS;
+ dev->Timer.func = tty_Timeout;
+ dev->Timer.name = "tty CD";
+ dev->Timer.arg = p;
+ log_Printf(LogDEBUG, "%s: Using tty_Timeout [%p]\n",
+ p->link.name, tty_Timeout);
+ timer_Start(&dev->Timer);
+}
+
+static int
+tty_AwaitCarrier(struct physical *p)
+{
+ struct ttydevice *dev = device2tty(p->handler);
+
+ if (dev->dev.cd.necessity == CD_NOTREQUIRED || physical_IsSync(p))
+ return CARRIER_OK;
+
+ if (dev->mbits == -1) {
+ if (dev->Timer.state == TIMER_STOPPED) {
+ dev->carrier_seconds = 0;
+ tty_StartTimer(p);
+ }
+ return CARRIER_PENDING; /* Not yet ! */
+ }
+
+ return Online(dev) ? CARRIER_OK : CARRIER_LOST;
+}
+
+#ifdef NONETGRAPH
+#define tty_SetAsyncParams NULL
+#define tty_Write NULL
+#define tty_Read NULL
+#else
+
+static int
+isngtty(struct ttydevice *dev)
+{
+ return dev->real.fd != -1;
+}
+
+static void
+tty_SetAsyncParams(struct physical *p, u_int32_t mymap, u_int32_t hismap)
+{
+ struct ttydevice *dev = device2tty(p->handler);
+ char asyncpath[NG_PATHSIZ];
+ struct ng_async_cfg cfg;
+
+ if (isngtty(dev)) {
+ /* Configure the async converter node */
+
+ snprintf(asyncpath, sizeof asyncpath, ".:%s", dev->hook);
+ memset(&cfg, 0, sizeof cfg);
+ cfg.enabled = 1;
+ cfg.accm = mymap | hismap;
+ cfg.amru = MAX_MTU;
+ cfg.smru = MAX_MRU;
+ log_Printf(LogDEBUG, "Configure async node at %s\n", asyncpath);
+ if (NgSendMsg(dev->cs, asyncpath, NGM_ASYNC_COOKIE,
+ NGM_ASYNC_CMD_SET_CONFIG, &cfg, sizeof cfg) < 0)
+ log_Printf(LogWARN, "%s: Can't configure async node at %s\n",
+ p->link.name, asyncpath);
+ } else
+ /* No netgraph node, just config the async layer */
+ async_SetLinkParams(&p->async, mymap, hismap);
+}
+
+static int
+LoadLineDiscipline(struct physical *p)
+{
+ struct ttydevice *dev = device2tty(p->handler);
+ u_char rbuf[sizeof(struct ng_mesg) + sizeof(struct nodeinfo)];
+ struct ng_mesg *reply;
+ struct nodeinfo *info;
+ char ttypath[NG_NODESIZ];
+ struct ngm_mkpeer ngm;
+ struct ngm_connect ngc;
+ int ldisc, cs, ds, hot;
+ unsigned speed;
+
+ /*
+ * Don't use the netgraph line discipline for now. Using it works, but
+ * carrier cannot be detected via TIOCMGET and the device doesn't become
+ * selectable with 0 bytes to read when carrier is lost :(
+ */
+ return 0;
+
+ reply = (struct ng_mesg *)rbuf;
+ info = (struct nodeinfo *)reply->data;
+
+ loadmodules(LOAD_VERBOSLY, "netgraph", "ng_tty", "ng_async", "ng_socket",
+ NULL);
+
+ /* Get the speed before loading the line discipline */
+ speed = physical_GetSpeed(p);
+
+ if (ioctl(p->fd, TIOCGETD, &dev->real.disc) < 0) {
+ log_Printf(LogDEBUG, "%s: Couldn't get tty line discipline\n",
+ p->link.name);
+ return 0;
+ }
+ ldisc = NETGRAPHDISC;
+ if (ID0ioctl(p->fd, TIOCSETD, &ldisc) < 0) {
+ log_Printf(LogDEBUG, "%s: Couldn't set NETGRAPHDISC line discipline\n",
+ p->link.name);
+ return 0;
+ }
+
+ /* Get the name of the tty node */
+ if (ioctl(p->fd, NGIOCGINFO, info) < 0) {
+ log_Printf(LogWARN, "%s: ioctl(NGIOCGINFO): %s\n", p->link.name,
+ strerror(errno));
+ ID0ioctl(p->fd, TIOCSETD, &dev->real.disc);
+ return 0;
+ }
+ snprintf(ttypath, sizeof ttypath, "%s:", info->name);
+
+ /* Create a socket node for our endpoint (and to send messages via) */
+ if (ID0NgMkSockNode(NULL, &cs, &ds) == -1) {
+ log_Printf(LogWARN, "%s: NgMkSockNode: %s\n", p->link.name,
+ strerror(errno));
+ ID0ioctl(p->fd, TIOCSETD, &dev->real.disc);
+ return 0;
+ }
+
+ /* Set the ``hot char'' on the TTY node */
+ hot = HDLC_SYN;
+ log_Printf(LogDEBUG, "%s: Set tty hotchar to 0x%02x\n", p->link.name, hot);
+ if (NgSendMsg(cs, ttypath, NGM_TTY_COOKIE,
+ NGM_TTY_SET_HOTCHAR, &hot, sizeof hot) < 0) {
+ log_Printf(LogWARN, "%s: Can't set hot char\n", p->link.name);
+ goto failed;
+ }
+
+ /* Attach an async converter node */
+ snprintf(ngm.type, sizeof ngm.type, "%s", NG_ASYNC_NODE_TYPE);
+ snprintf(ngm.ourhook, sizeof ngm.ourhook, "%s", NG_TTY_HOOK);
+ snprintf(ngm.peerhook, sizeof ngm.peerhook, "%s", NG_ASYNC_HOOK_ASYNC);
+ log_Printf(LogDEBUG, "%s: Send mkpeer async:%s to %s:%s\n", p->link.name,
+ ngm.peerhook, ttypath, ngm.ourhook);
+ if (NgSendMsg(cs, ttypath, NGM_GENERIC_COOKIE,
+ NGM_MKPEER, &ngm, sizeof ngm) < 0) {
+ log_Printf(LogWARN, "%s: Can't create %s node\n", p->link.name,
+ NG_ASYNC_NODE_TYPE);
+ goto failed;
+ }
+
+ /* Connect the async node to our socket */
+ snprintf(ngc.path, sizeof ngc.path, "%s%s", ttypath, NG_TTY_HOOK);
+ snprintf(ngc.peerhook, sizeof ngc.peerhook, "%s", NG_ASYNC_HOOK_SYNC);
+ memcpy(ngc.ourhook, ngc.peerhook, sizeof ngc.ourhook);
+ log_Printf(LogDEBUG, "%s: Send connect %s:%s to .:%s\n", p->link.name,
+ ngc.path, ngc.peerhook, ngc.ourhook);
+ if (NgSendMsg(cs, ".:", NGM_GENERIC_COOKIE, NGM_CONNECT,
+ &ngc, sizeof ngc) < 0) {
+ log_Printf(LogWARN, "%s: Can't connect .:%s -> %s.%s: %s\n",
+ p->link.name, ngc.ourhook, ngc.path, ngc.peerhook,
+ strerror(errno));
+ goto failed;
+ }
+
+ /* Get the async node id */
+ if (NgSendMsg(cs, ngc.path, NGM_GENERIC_COOKIE, NGM_NODEINFO, NULL, 0) < 0) {
+ log_Printf(LogWARN, "%s: Can't request async node info at %s: %s\n",
+ p->link.name, ngc.path, strerror(errno));
+ goto failed;
+ }
+ if (NgRecvMsg(cs, reply, sizeof rbuf, NULL) < 0) {
+ log_Printf(LogWARN, "%s: Can't obtain async node info at %s: %s\n",
+ p->link.name, ngc.path, strerror(errno));
+ goto failed;
+ }
+
+ /* All done, set up our device state */
+ snprintf(dev->hook, sizeof dev->hook, "%s", ngc.ourhook);
+ dev->cs = cs;
+ dev->real.fd = p->fd;
+ p->fd = ds;
+ dev->real.speed = speed;
+ physical_SetSync(p);
+
+ tty_SetAsyncParams(p, 0xffffffff, 0xffffffff);
+ physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE);
+ log_Printf(LogPHASE, "%s: Loaded netgraph tty line discipline\n",
+ p->link.name);
+
+ return 1;
+
+failed:
+ ID0ioctl(p->fd, TIOCSETD, &dev->real.disc);
+ close(ds);
+ close(cs);
+
+ return 0;
+}
+
+static void
+UnloadLineDiscipline(struct physical *p)
+{
+ struct ttydevice *dev = device2tty(p->handler);
+
+ if (isngtty(dev)) {
+log_Printf(LogPHASE, "back to speed %d\n", dev->real.speed);
+ if (!physical_SetSpeed(p, dev->real.speed))
+ log_Printf(LogWARN, "Couldn't reset tty speed to %d\n", dev->real.speed);
+ dev->real.speed = 0;
+ close(p->fd);
+ p->fd = dev->real.fd;
+ dev->real.fd = -1;
+ close(dev->cs);
+ dev->cs = -1;
+ *dev->hook = '\0';
+ if (ID0ioctl(p->fd, TIOCSETD, &dev->real.disc) == 0) {
+ physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE);
+ log_Printf(LogPHASE, "%s: Unloaded netgraph tty line discipline\n",
+ p->link.name);
+ } else
+ log_Printf(LogWARN, "%s: Failed to unload netgraph tty line discipline\n",
+ p->link.name);
+ }
+}
+
+static ssize_t
+tty_Write(struct physical *p, const void *v, size_t n)
+{
+ struct ttydevice *dev = device2tty(p->handler);
+
+ if (isngtty(dev))
+ return NgSendData(p->fd, dev->hook, v, n) == -1 ? -1 : (ssize_t)n;
+ else
+ return write(p->fd, v, n);
+}
+
+static ssize_t
+tty_Read(struct physical *p, void *v, size_t n)
+{
+ struct ttydevice *dev = device2tty(p->handler);
+ char hook[sizeof NG_ASYNC_HOOK_SYNC];
+
+ if (isngtty(dev))
+ return NgRecvData(p->fd, v, n, hook);
+ else
+ return read(p->fd, v, n);
+}
+
+#endif /* NETGRAPH */
+
+static int
+tty_Raw(struct physical *p)
+{
+ struct ttydevice *dev = device2tty(p->handler);
+ struct termios ios;
+ int oldflag;
+
+ log_Printf(LogDEBUG, "%s: Entering tty_Raw\n", p->link.name);
+
+ if (p->type != PHYS_DIRECT && p->fd >= 0 && !Online(dev))
+ log_Printf(LogDEBUG, "%s: Raw: descriptor = %d, mbits = %x\n",
+ p->link.name, p->fd, dev->mbits);
+
+ if (!physical_IsSync(p)) {
+#ifndef NONETGRAPH
+ if (!LoadLineDiscipline(p))
+#endif
+ {
+ tcgetattr(p->fd, &ios);
+ cfmakeraw(&ios);
+ if (p->cfg.rts_cts)
+ ios.c_cflag |= CLOCAL | CCTS_OFLOW | CRTS_IFLOW;
+ else
+ ios.c_cflag |= CLOCAL;
+
+ if (p->type != PHYS_DEDICATED)
+ ios.c_cflag |= HUPCL;
+
+ if (tcsetattr(p->fd, TCSANOW, &ios) == -1)
+ log_Printf(LogWARN, "%s: tcsetattr: Failed configuring device\n",
+ p->link.name);
+ }
+ }
+
+ oldflag = fcntl(p->fd, F_GETFL, 0);
+ if (oldflag < 0)
+ return 0;
+ fcntl(p->fd, F_SETFL, oldflag | O_NONBLOCK);
+
+ return 1;
+}
+
+static void
+tty_Offline(struct physical *p)
+{
+ struct ttydevice *dev = device2tty(p->handler);
+
+ if (p->fd >= 0) {
+ timer_Stop(&dev->Timer);
+ dev->mbits &= ~TIOCM_DTR; /* XXX: Hmm, what's this supposed to do ? */
+ if (Online(dev)) {
+ struct termios tio;
+
+ tcgetattr(p->fd, &tio);
+ if (cfsetspeed(&tio, B0) == -1 || tcsetattr(p->fd, TCSANOW, &tio) == -1)
+ log_Printf(LogWARN, "%s: Unable to set physical to speed 0\n",
+ p->link.name);
+ }
+ }
+}
+
+static void
+tty_Cooked(struct physical *p)
+{
+ struct ttydevice *dev = device2tty(p->handler);
+ int oldflag;
+
+ tty_Offline(p); /* In case of emergency close()s */
+
+ tcflush(p->fd, TCIOFLUSH);
+
+ if (!physical_IsSync(p) && tcsetattr(p->fd, TCSAFLUSH, &dev->ios) == -1)
+ log_Printf(LogWARN, "%s: tcsetattr: Unable to restore device settings\n",
+ p->link.name);
+
+#ifndef NONETGRAPH
+ UnloadLineDiscipline(p);
+#endif
+
+ if ((oldflag = fcntl(p->fd, F_GETFL, 0)) != -1)
+ fcntl(p->fd, F_SETFL, oldflag & ~O_NONBLOCK);
+}
+
+static void
+tty_StopTimer(struct physical *p)
+{
+ struct ttydevice *dev = device2tty(p->handler);
+
+ timer_Stop(&dev->Timer);
+}
+
+static void
+tty_Free(struct physical *p)
+{
+ struct ttydevice *dev = device2tty(p->handler);
+
+ tty_Offline(p); /* In case of emergency close()s */
+ free(dev);
+}
+
+static unsigned
+tty_Speed(struct physical *p)
+{
+ struct termios ios;
+
+ if (tcgetattr(p->fd, &ios) == -1)
+ return 0;
+
+ return SpeedToUnsigned(cfgetispeed(&ios));
+}
+
+static const char *
+tty_OpenInfo(struct physical *p)
+{
+ struct ttydevice *dev = device2tty(p->handler);
+ static char buf[13];
+
+ if (Online(dev))
+ strcpy(buf, "with");
+ else
+ strcpy(buf, "no");
+ strcat(buf, " carrier");
+
+ return buf;
+}
+
+static int
+tty_Slot(struct physical *p)
+{
+ struct ttyent *ttyp;
+ int slot;
+
+ setttyent();
+ for (slot = 1; (ttyp = getttyent()); ++slot)
+ if (!strcmp(ttyp->ty_name, p->name.base)) {
+ endttyent();
+ return slot;
+ }
+
+ endttyent();
+ return -1;
+}
+
+static void
+tty_device2iov(struct device *d, struct iovec *iov, int *niov,
+ int maxiov __unused,
+#ifndef NONETGRAPH
+ int *auxfd, int *nauxfd
+#else
+ int *auxfd __unused, int *nauxfd __unused
+#endif
+ )
+{
+ struct ttydevice *dev = device2tty(d);
+ int sz = physical_MaxDeviceSize();
+
+ iov[*niov].iov_base = realloc(d, sz);
+ if (iov[*niov].iov_base == NULL) {
+ log_Printf(LogALERT, "Failed to allocate memory: %d\n", sz);
+ AbortProgram(EX_OSERR);
+ }
+ iov[*niov].iov_len = sz;
+ (*niov)++;
+
+#ifndef NONETGRAPH
+ if (dev->cs >= 0) {
+ *auxfd = dev->cs;
+ (*nauxfd)++;
+ }
+#endif
+
+ if (dev->Timer.state != TIMER_STOPPED) {
+ timer_Stop(&dev->Timer);
+ dev->Timer.state = TIMER_RUNNING;
+ }
+}
+
+static struct device basettydevice = {
+ TTY_DEVICE,
+ "tty",
+ 0,
+ { CD_VARIABLE, DEF_TTYCDDELAY },
+ tty_AwaitCarrier,
+ NULL,
+ tty_Raw,
+ tty_Offline,
+ tty_Cooked,
+ tty_SetAsyncParams,
+ tty_StopTimer,
+ tty_Free,
+ tty_Read,
+ tty_Write,
+ tty_device2iov,
+ tty_Speed,
+ tty_OpenInfo,
+ tty_Slot
+};
+
+struct device *
+tty_iov2device(int type, struct physical *p, struct iovec *iov, int *niov,
+ int maxiov __unused,
+#ifndef NONETGRAPH
+ int *auxfd, int *nauxfd
+#else
+ int *auxfd __unused, int *nauxfd __unused
+#endif
+ )
+{
+ if (type == TTY_DEVICE) {
+ struct ttydevice *dev = (struct ttydevice *)iov[(*niov)++].iov_base;
+
+ dev = realloc(dev, sizeof *dev); /* Reduce to the correct size */
+ if (dev == NULL) {
+ log_Printf(LogALERT, "Failed to allocate memory: %d\n",
+ (int)(sizeof *dev));
+ AbortProgram(EX_OSERR);
+ }
+
+#ifndef NONETGRAPH
+ if (*nauxfd) {
+ dev->cs = *auxfd;
+ (*nauxfd)--;
+ } else
+ dev->cs = -1;
+#endif
+
+ /* Refresh function pointers etc */
+ memcpy(&dev->dev, &basettydevice, sizeof dev->dev);
+
+ physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE);
+ if (dev->Timer.state != TIMER_STOPPED) {
+ dev->Timer.state = TIMER_STOPPED;
+ p->handler = &dev->dev; /* For the benefit of StartTimer */
+ tty_StartTimer(p);
+ }
+ return &dev->dev;
+ }
+
+ return NULL;
+}
+
+struct device *
+tty_Create(struct physical *p)
+{
+ struct ttydevice *dev;
+ struct termios ios;
+ int oldflag;
+
+ if (p->fd < 0 || !isatty(p->fd))
+ /* Don't want this */
+ return NULL;
+
+ if (*p->name.full == '\0') {
+ physical_SetDevice(p, ttyname(p->fd));
+ log_Printf(LogDEBUG, "%s: Input is a tty (%s)\n",
+ p->link.name, p->name.full);
+ } else
+ log_Printf(LogDEBUG, "%s: Opened %s\n", p->link.name, p->name.full);
+
+ /* We're gonna return a ttydevice (unless something goes horribly wrong) */
+
+ if ((dev = malloc(sizeof *dev)) == NULL) {
+ /* Complete failure - parent doesn't continue trying to ``create'' */
+ close(p->fd);
+ p->fd = -1;
+ return NULL;
+ }
+
+ memcpy(&dev->dev, &basettydevice, sizeof dev->dev);
+ memset(&dev->Timer, '\0', sizeof dev->Timer);
+ dev->mbits = -1;
+#ifndef NONETGRAPH
+ dev->real.speed = 0;
+ dev->real.fd = -1;
+ dev->real.disc = -1;
+ *dev->hook = '\0';
+#endif
+ tcgetattr(p->fd, &ios);
+ dev->ios = ios;
+
+ if (p->cfg.cd.necessity != CD_DEFAULT)
+ /* Any override is ok for the tty device */
+ dev->dev.cd = p->cfg.cd;
+
+ log_Printf(LogDEBUG, "%s: tty_Create: physical (get): fd = %d,"
+ " iflag = %lx, oflag = %lx, cflag = %lx\n", p->link.name, p->fd,
+ (u_long)ios.c_iflag, (u_long)ios.c_oflag, (u_long)ios.c_cflag);
+
+ cfmakeraw(&ios);
+ if (p->cfg.rts_cts)
+ ios.c_cflag |= CLOCAL | CCTS_OFLOW | CRTS_IFLOW;
+ else {
+ ios.c_cflag |= CLOCAL;
+ ios.c_iflag |= IXOFF;
+ }
+ ios.c_iflag |= IXON;
+ if (p->type != PHYS_DEDICATED)
+ ios.c_cflag |= HUPCL;
+
+ if (p->type != PHYS_DIRECT) {
+ /* Change tty speed when we're not in -direct mode */
+ ios.c_cflag &= ~(CSIZE | PARODD | PARENB);
+ ios.c_cflag |= p->cfg.parity;
+ if (cfsetspeed(&ios, UnsignedToSpeed(p->cfg.speed)) == -1)
+ log_Printf(LogWARN, "%s: %s: Unable to set speed to %d\n",
+ p->link.name, p->name.full, p->cfg.speed);
+ }
+
+ if (tcsetattr(p->fd, TCSADRAIN, &ios) == -1) {
+ log_Printf(LogWARN, "%s: tcsetattr: Failed configuring device\n",
+ p->link.name);
+ if (p->type != PHYS_DIRECT && p->cfg.speed > 115200)
+ log_Printf(LogWARN, "%.*s Perhaps the speed is unsupported\n",
+ (int)strlen(p->link.name), "");
+ }
+
+ log_Printf(LogDEBUG, "%s: physical (put): iflag = %lx, oflag = %lx, "
+ "cflag = %lx\n", p->link.name, (u_long)ios.c_iflag,
+ (u_long)ios.c_oflag, (u_long)ios.c_cflag);
+
+ oldflag = fcntl(p->fd, F_GETFL, 0);
+ if (oldflag < 0) {
+ /* Complete failure - parent doesn't continue trying to ``create'' */
+
+ log_Printf(LogWARN, "%s: Open: Cannot get physical flags: %s\n",
+ p->link.name, strerror(errno));
+ tty_Cooked(p);
+ close(p->fd);
+ p->fd = -1;
+ free(dev);
+ return NULL;
+ } else
+ fcntl(p->fd, F_SETFL, oldflag & ~O_NONBLOCK);
+
+ physical_SetupStack(p, dev->dev.name, PHYSICAL_NOFORCE);
+
+ return &dev->dev;
+}
diff --git a/usr.sbin/ppp/tty.h b/usr.sbin/ppp/tty.h
new file mode 100644
index 0000000..0991c81
--- /dev/null
+++ b/usr.sbin/ppp/tty.h
@@ -0,0 +1,37 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct physical;
+struct device;
+
+#define DEF_TTYCDDELAY 1 /* Default ``set cd'' value */
+
+extern struct device *tty_Create(struct physical *);
+extern struct device *tty_iov2device(int, struct physical *,
+ struct iovec *, int *, int, int *, int *);
+extern unsigned tty_DeviceSize(void);
diff --git a/usr.sbin/ppp/tun.c b/usr.sbin/ppp/tun.c
new file mode 100644
index 0000000..883cd5c
--- /dev/null
+++ b/usr.sbin/ppp/tun.c
@@ -0,0 +1,119 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+
+#include <sys/socket.h> /* For IFF_ defines */
+#ifndef __FreeBSD__
+#include <net/if.h> /* For IFF_ defines */
+#endif
+#include <net/route.h>
+#include <netinet/in.h>
+#include <net/if_types.h>
+#include <net/if_tun.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <string.h>
+#if defined(__OpenBSD__) || defined(__NetBSD__)
+#include <sys/ioctl.h>
+#endif
+#include <stdio.h>
+#include <termios.h>
+#ifdef __NetBSD__
+#include <unistd.h>
+#endif
+
+#include "layer.h"
+#include "mbuf.h"
+#include "log.h"
+#include "id.h"
+#include "timer.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "defs.h"
+#include "fsm.h"
+#include "throughput.h"
+#include "iplist.h"
+#include "slcompress.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "mp.h"
+#include "iface.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "tun.h"
+
+void
+tun_configure(struct bundle *bundle)
+{
+#ifdef __NetBSD__
+ struct ifreq ifr;
+ int s;
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+
+ if (s < 0) {
+ log_Printf(LogERROR, "tun_configure: socket(): %s\n", strerror(errno));
+ return;
+ }
+
+ sprintf(ifr.ifr_name, "tun%d", bundle->unit);
+ ifr.ifr_mtu = bundle->iface->mtu;
+ if (ioctl(s, SIOCSIFMTU, &ifr) < 0)
+ log_Printf(LogERROR, "tun_configure: ioctl(SIOCSIFMTU): %s\n",
+ strerror(errno));
+
+ close(s);
+#else
+ struct tuninfo info;
+
+ memset(&info, '\0', sizeof info);
+ info.type = IFT_PPP;
+ info.mtu = bundle->iface->mtu;
+
+ info.baudrate = bundle->bandwidth;
+#ifdef __OpenBSD__
+ info.flags = IFF_UP|IFF_POINTOPOINT|IFF_MULTICAST;
+#endif
+ if (ID0ioctl(bundle->dev.fd, TUNSIFINFO, &info) < 0)
+ log_Printf(LogERROR, "tun_configure: ioctl(TUNSIFINFO): %s\n",
+ strerror(errno));
+#endif
+}
diff --git a/usr.sbin/ppp/tun.h b/usr.sbin/ppp/tun.h
new file mode 100644
index 0000000..1bb0712
--- /dev/null
+++ b/usr.sbin/ppp/tun.h
@@ -0,0 +1,39 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct tun_data {
+ union {
+ u_int32_t family;
+ u_int32_t timeout;
+ } header;
+ u_char data[MAX_MRU];
+};
+
+struct bundle;
+
+extern void tun_configure(struct bundle *);
diff --git a/usr.sbin/ppp/ua.h b/usr.sbin/ppp/ua.h
new file mode 100644
index 0000000..dbd89e5
--- /dev/null
+++ b/usr.sbin/ppp/ua.h
@@ -0,0 +1,74 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifdef __i386__ /* Do any other archs not care about alignment ? */
+
+# define ua_htonl(src, tgt) (*(u_int32_t *)(tgt) = htonl(*(u_int32_t *)(src)))
+# define ua_ntohl(src, tgt) (*(u_int32_t *)(tgt) = ntohl(*(u_int32_t *)(src)))
+# define ua_htons(src, tgt) (*(u_int16_t *)(tgt) = htons(*(u_int16_t *)(src)))
+# define ua_ntohs(src, tgt) (*(u_int16_t *)(tgt) = ntohs(*(u_int16_t *)(src)))
+
+#else /* We care about alignment (or else drop a core !) */
+
+# define ua_htonl(src, tgt) \
+ do { \
+ u_int32_t __oh; \
+ memcpy(&__oh, (src), sizeof __oh); \
+ *(u_char *)(tgt) = __oh >> 24; \
+ *((u_char *)(tgt) + 1) = (__oh >> 16) & 0xff; \
+ *((u_char *)(tgt) + 2) = (__oh >> 8) & 0xff; \
+ *((u_char *)(tgt) + 3) = __oh & 0xff; \
+ } while (0)
+
+# define ua_ntohl(src, tgt) \
+ do { \
+ u_int32_t __nh; \
+ __nh = ((u_int32_t)*(u_char *)(src) << 24) | \
+ ((u_int32_t)*((u_char *)(src) + 1) << 16) | \
+ ((u_int32_t)*((u_char *)(src) + 2) << 8) | \
+ (u_int32_t)*((u_char *)(src) + 3); \
+ memcpy((tgt), &__nh, sizeof __nh); \
+ } while (0)
+
+# define ua_htons(src, tgt) \
+ do { \
+ u_int16_t __oh; \
+ memcpy(&__oh, (src), sizeof __oh); \
+ *(u_char *)(tgt) = __oh >> 8; \
+ *((u_char *)(tgt) + 1) = __oh & 0xff; \
+ } while (0)
+
+# define ua_ntohs(src, tgt) \
+ do { \
+ u_int16_t __nh; \
+ __nh = ((u_int16_t)*(u_char *)(src) << 8) | \
+ (u_int16_t)*((u_char *)(src) + 1); \
+ memcpy((tgt), &__nh, sizeof __nh); \
+ } while (0)
+
+#endif
diff --git a/usr.sbin/ppp/udp.c b/usr.sbin/ppp/udp.c
new file mode 100644
index 0000000..d9f3252
--- /dev/null
+++ b/usr.sbin/ppp/udp.c
@@ -0,0 +1,335 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "layer.h"
+#include "defs.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "async.h"
+#include "descriptor.h"
+#include "physical.h"
+#include "main.h"
+#include "udp.h"
+
+
+#define UDP_CONNECTED 1
+#define UDP_UNCONNECTED 2
+#define UDP_MAYBEUNCONNECTED 3
+
+struct udpdevice {
+ struct device dev; /* What struct physical knows about */
+ struct sockaddr_in sock; /* peer address */
+ unsigned connected : 2; /* Have we connect()d ? */
+};
+
+#define device2udp(d) ((d)->type == UDP_DEVICE ? (struct udpdevice *)d : NULL)
+
+unsigned
+udp_DeviceSize(void)
+{
+ return sizeof(struct udpdevice);
+}
+
+static ssize_t
+udp_Sendto(struct physical *p, const void *v, size_t n)
+{
+ struct udpdevice *dev = device2udp(p->handler);
+ int ret;
+
+ switch (dev->connected) {
+ case UDP_CONNECTED:
+ ret = write(p->fd, v, n);
+ break;
+
+ case UDP_UNCONNECTED:
+ default:
+ ret = sendto(p->fd, v, n, 0, (struct sockaddr *)&dev->sock,
+ sizeof dev->sock);
+ break;
+ }
+ if (dev->connected == UDP_MAYBEUNCONNECTED) {
+ if (ret == -1 && errno == EISCONN) {
+ dev->connected = UDP_CONNECTED;
+ ret = write(p->fd, v, n);
+ } else
+ dev->connected = UDP_UNCONNECTED;
+ }
+
+ return ret;
+}
+
+static ssize_t
+udp_Recvfrom(struct physical *p, void *v, size_t n)
+{
+ struct udpdevice *dev = device2udp(p->handler);
+ int sz, ret;
+
+ if (dev->connected == UDP_CONNECTED)
+ return read(p->fd, v, n);
+
+ sz = sizeof dev->sock;
+ ret = recvfrom(p->fd, v, n, 0, (struct sockaddr *)&dev->sock, &sz);
+
+ if (*p->name.full == '\0') {
+ snprintf(p->name.full, sizeof p->name.full, "%s:%d/udp",
+ inet_ntoa(dev->sock.sin_addr), ntohs(dev->sock.sin_port));
+ p->name.base = p->name.full;
+ }
+
+ return ret;
+}
+
+static void
+udp_Free(struct physical *p)
+{
+ struct udpdevice *dev = device2udp(p->handler);
+
+ free(dev);
+}
+
+static void
+udp_device2iov(struct device *d, struct iovec *iov, int *niov,
+ int maxiov __unused, int *auxfd __unused, int *nauxfd __unused)
+{
+ int sz = physical_MaxDeviceSize();
+
+ iov[*niov].iov_base = realloc(d, sz);
+ if (iov[*niov].iov_base == NULL) {
+ log_Printf(LogALERT, "Failed to allocate memory: %d\n", sz);
+ AbortProgram(EX_OSERR);
+ }
+ iov[*niov].iov_len = sz;
+ (*niov)++;
+}
+
+static const struct device baseudpdevice = {
+ UDP_DEVICE,
+ "udp",
+ 0,
+ { CD_NOTREQUIRED, 0 },
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ udp_Free,
+ udp_Recvfrom,
+ udp_Sendto,
+ udp_device2iov,
+ NULL,
+ NULL,
+ NULL
+};
+
+struct device *
+udp_iov2device(int type, struct physical *p, struct iovec *iov, int *niov,
+ int maxiov __unused, int *auxfd __unused, int *nauxfd __unused)
+{
+ if (type == UDP_DEVICE) {
+ struct udpdevice *dev = (struct udpdevice *)iov[(*niov)++].iov_base;
+
+ dev = realloc(dev, sizeof *dev); /* Reduce to the correct size */
+ if (dev == NULL) {
+ log_Printf(LogALERT, "Failed to allocate memory: %d\n",
+ (int)(sizeof *dev));
+ AbortProgram(EX_OSERR);
+ }
+
+ /* Refresh function pointers etc */
+ memcpy(&dev->dev, &baseudpdevice, sizeof dev->dev);
+
+ physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNC);
+ return &dev->dev;
+ }
+
+ return NULL;
+}
+
+static struct udpdevice *
+udp_CreateDevice(struct physical *p, char *host, char *port)
+{
+ struct udpdevice *dev;
+ struct servent *sp;
+
+ if ((dev = malloc(sizeof *dev)) == NULL) {
+ log_Printf(LogWARN, "%s: Cannot allocate a udp device: %s\n",
+ p->link.name, strerror(errno));
+ return NULL;
+ }
+
+ dev->sock.sin_family = AF_INET;
+ dev->sock.sin_addr = GetIpAddr(host);
+ if (dev->sock.sin_addr.s_addr == INADDR_NONE) {
+ log_Printf(LogWARN, "%s: %s: unknown host\n", p->link.name, host);
+ free(dev);
+ return NULL;
+ }
+ dev->sock.sin_port = htons(atoi(port));
+ if (dev->sock.sin_port == 0) {
+ sp = getservbyname(port, "udp");
+ if (sp)
+ dev->sock.sin_port = sp->s_port;
+ else {
+ log_Printf(LogWARN, "%s: %s: unknown service\n", p->link.name, port);
+ free(dev);
+ return NULL;
+ }
+ }
+
+ log_Printf(LogPHASE, "%s: Connecting to %s:%s/udp\n", p->link.name,
+ host, port);
+
+ p->fd = socket(PF_INET, SOCK_DGRAM, 0);
+ if (p->fd >= 0) {
+ log_Printf(LogDEBUG, "%s: Opened udp socket %s\n", p->link.name,
+ p->name.full);
+ if (connect(p->fd, (struct sockaddr *)&dev->sock, sizeof dev->sock) == 0) {
+ dev->connected = UDP_CONNECTED;
+ return dev;
+ } else
+ log_Printf(LogWARN, "%s: connect: %s\n", p->name.full, strerror(errno));
+ } else
+ log_Printf(LogWARN, "%s: socket: %s\n", p->name.full, strerror(errno));
+
+ close(p->fd);
+ p->fd = -1;
+ free(dev);
+
+ return NULL;
+}
+
+struct device *
+udp_Create(struct physical *p)
+{
+ char *cp, *host, *port, *svc;
+ struct udpdevice *dev;
+
+ dev = NULL;
+ if (p->fd < 0) {
+ if ((cp = strchr(p->name.full, ':')) != NULL && !strchr(cp + 1, ':')) {
+ *cp = '\0';
+ host = p->name.full;
+ port = cp + 1;
+ svc = strchr(port, '/');
+ if (svc && strcasecmp(svc, "/udp")) {
+ *cp = ':';
+ return NULL;
+ }
+ if (svc) {
+ p->fd--; /* We own the device but maybe can't use it - change fd */
+ *svc = '\0';
+ }
+
+ if (*host && *port)
+ dev = udp_CreateDevice(p, host, port);
+
+ *cp = ':';
+ if (svc)
+ *svc = '/';
+ }
+ } else {
+ /* See if we're a connected udp socket */
+ struct stat st;
+
+ if (fstat(p->fd, &st) != -1 && (st.st_mode & S_IFSOCK)) {
+ int type, sz;
+
+ sz = sizeof type;
+ if (getsockopt(p->fd, SOL_SOCKET, SO_TYPE, &type, &sz) == -1) {
+ log_Printf(LogPHASE, "%s: Link is a closed socket !\n", p->link.name);
+ close(p->fd);
+ p->fd = -1;
+ return NULL;
+ }
+
+ if (sz == sizeof type && type == SOCK_DGRAM) {
+ struct sockaddr_in sock;
+ struct sockaddr *sockp = (struct sockaddr *)&sock;
+
+ if ((dev = malloc(sizeof *dev)) == NULL) {
+ log_Printf(LogWARN, "%s: Cannot allocate a udp device: %s\n",
+ p->link.name, strerror(errno));
+ return NULL;
+ }
+
+ if (getpeername(p->fd, sockp, &sz) == 0) {
+ log_Printf(LogPHASE, "%s: Link is a connected udp socket\n",
+ p->link.name);
+ dev->connected = UDP_CONNECTED;
+ } else {
+ log_Printf(LogPHASE, "%s: Link is a disconnected udp socket\n",
+ p->link.name);
+
+ dev->connected = UDP_MAYBEUNCONNECTED;
+
+ if (p->link.lcp.cfg.openmode != OPEN_PASSIVE) {
+ log_Printf(LogPHASE, "%s: Changing openmode to PASSIVE\n",
+ p->link.name);
+ p->link.lcp.cfg.openmode = OPEN_PASSIVE;
+ }
+ }
+ }
+ }
+ }
+
+ if (dev) {
+ memcpy(&dev->dev, &baseudpdevice, sizeof dev->dev);
+ physical_SetupStack(p, dev->dev.name, PHYSICAL_FORCE_SYNC);
+ if (p->cfg.cd.necessity != CD_DEFAULT)
+ log_Printf(LogWARN, "Carrier settings ignored\n");
+ return &dev->dev;
+ }
+
+ return NULL;
+}
diff --git a/usr.sbin/ppp/udp.h b/usr.sbin/ppp/udp.h
new file mode 100644
index 0000000..c89e276
--- /dev/null
+++ b/usr.sbin/ppp/udp.h
@@ -0,0 +1,35 @@
+/*-
+ * Copyright (c) 1999 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct physical;
+struct device;
+
+extern struct device *udp_Create(struct physical *);
+extern struct device *udp_iov2device(int, struct physical *,
+ struct iovec *, int *, int, int *, int *);
+extern unsigned udp_DeviceSize(void);
diff --git a/usr.sbin/ppp/vjcomp.c b/usr.sbin/ppp/vjcomp.c
new file mode 100644
index 0000000..f3c6b71
--- /dev/null
+++ b/usr.sbin/ppp/vjcomp.c
@@ -0,0 +1,200 @@
+/*-
+ * Copyright (c) 1996 - 2001 Brian Somers <brian@Awfulhak.org>
+ * based on work by Toshiharu OHNO <tony-o@iij.ad.jp>
+ * Internet Initiative Japan, Inc (IIJ)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <stdio.h>
+#include <string.h> /* strlen/memcpy */
+#include <termios.h>
+
+#include "layer.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "proto.h"
+#include "slcompress.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "defs.h"
+#include "iplist.h"
+#include "throughput.h"
+#include "ncpaddr.h"
+#include "ipcp.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "ipv6cp.h"
+#include "ncp.h"
+#include "bundle.h"
+#include "vjcomp.h"
+
+#define MAX_VJHEADER 16 /* Maximum size of compressed header */
+
+static struct mbuf *
+vj_LayerPush(struct bundle *bundle, struct link *l __unused, struct mbuf *bp,
+ int pri __unused, u_short *proto)
+{
+ int type;
+ struct ip *pip;
+ u_short cproto = bundle->ncp.ipcp.peer_compproto >> 16;
+
+ bp = m_pullup(bp);
+ pip = (struct ip *)MBUF_CTOP(bp);
+ if (*proto == PROTO_IP && pip->ip_p == IPPROTO_TCP &&
+ cproto == PROTO_VJCOMP) {
+ type = sl_compress_tcp(bp, pip, &bundle->ncp.ipcp.vj.cslc,
+ &bundle->ncp.ipcp.vj.slstat,
+ bundle->ncp.ipcp.peer_compproto & 0xff);
+ log_Printf(LogDEBUG, "vj_LayerWrite: type = %x\n", type);
+ switch (type) {
+ case TYPE_IP:
+ break;
+
+ case TYPE_UNCOMPRESSED_TCP:
+ *proto = PROTO_VJUNCOMP;
+ log_Printf(LogDEBUG, "vj_LayerPush: PROTO_IP -> PROTO_VJUNCOMP\n");
+ m_settype(bp, MB_VJOUT);
+ break;
+
+ case TYPE_COMPRESSED_TCP:
+ *proto = PROTO_VJCOMP;
+ log_Printf(LogDEBUG, "vj_LayerPush: PROTO_IP -> PROTO_VJUNCOMP\n");
+ m_settype(bp, MB_VJOUT);
+ break;
+
+ default:
+ log_Printf(LogERROR, "vj_LayerPush: Unknown frame type %x\n", type);
+ m_freem(bp);
+ return NULL;
+ }
+ }
+
+ return bp;
+}
+
+static struct mbuf *
+VjUncompressTcp(struct ipcp *ipcp, struct mbuf *bp, u_char type)
+{
+ u_char *bufp;
+ int len, olen, rlen;
+ u_char work[MAX_HDR + MAX_VJHEADER]; /* enough to hold TCP/IP header */
+
+ bp = m_pullup(bp);
+ olen = len = m_length(bp);
+ if (type == TYPE_UNCOMPRESSED_TCP) {
+ /*
+ * Uncompressed packet does NOT change its size, so that we can use mbuf
+ * space for uncompression job.
+ */
+ bufp = MBUF_CTOP(bp);
+ len = sl_uncompress_tcp(&bufp, len, type, &ipcp->vj.cslc, &ipcp->vj.slstat,
+ (ipcp->my_compproto >> 8) & 255);
+ if (len <= 0) {
+ m_freem(bp);
+ bp = NULL;
+ } else
+ m_settype(bp, MB_VJIN);
+ return bp;
+ }
+
+ /*
+ * Handle compressed packet. 1) Read upto MAX_VJHEADER bytes into work
+ * space. 2) Try to uncompress it. 3) Compute amount of necessary space. 4)
+ * Copy unread data info there.
+ */
+ if (len > MAX_VJHEADER)
+ len = MAX_VJHEADER;
+ rlen = len;
+ bufp = work + MAX_HDR;
+ bp = mbuf_Read(bp, bufp, rlen);
+ len = sl_uncompress_tcp(&bufp, olen, type, &ipcp->vj.cslc, &ipcp->vj.slstat,
+ (ipcp->my_compproto >> 8) & 255);
+ if (len <= 0) {
+ m_freem(bp);
+ return NULL;
+ }
+ len -= olen;
+ len += rlen;
+
+ bp = m_prepend(bp, bufp, len, 0);
+ m_settype(bp, MB_VJIN);
+
+ return bp;
+}
+
+static struct mbuf *
+vj_LayerPull(struct bundle *bundle, struct link *l __unused, struct mbuf *bp,
+ u_short *proto)
+{
+ u_char type;
+
+ switch (*proto) {
+ case PROTO_VJCOMP:
+ type = TYPE_COMPRESSED_TCP;
+ log_Printf(LogDEBUG, "vj_LayerPull: PROTO_VJCOMP -> PROTO_IP\n");
+ break;
+ case PROTO_VJUNCOMP:
+ type = TYPE_UNCOMPRESSED_TCP;
+ log_Printf(LogDEBUG, "vj_LayerPull: PROTO_VJUNCOMP -> PROTO_IP\n");
+ break;
+ default:
+ return bp;
+ }
+
+ *proto = PROTO_IP;
+ return VjUncompressTcp(&bundle->ncp.ipcp, bp, type);
+}
+
+const char *
+vj2asc(u_int32_t val)
+{
+ static char asc[50]; /* The return value is used immediately */
+
+ if (val)
+ snprintf(asc, sizeof asc, "%d VJ slots with%s slot compression",
+ (int)((val>>8)&15)+1, val & 1 ? "" : "out");
+ else
+ strcpy(asc, "VJ disabled");
+ return asc;
+}
+
+struct layer vjlayer = { LAYER_VJ, "vj", vj_LayerPush, vj_LayerPull };
diff --git a/usr.sbin/ppp/vjcomp.h b/usr.sbin/ppp/vjcomp.h
new file mode 100644
index 0000000..2956122
--- /dev/null
+++ b/usr.sbin/ppp/vjcomp.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+struct mbuf;
+struct link;
+struct ipcp;
+struct bundle;
+
+extern const char *vj2asc(u_int32_t);
+
+extern struct layer vjlayer;
diff --git a/usr.sbin/pppctl/Makefile b/usr.sbin/pppctl/Makefile
new file mode 100644
index 0000000..6cdcc3d
--- /dev/null
+++ b/usr.sbin/pppctl/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= pppctl
+MAN= pppctl.8
+
+DPADD= ${LIBPTHREAD} ${LIBEDIT} ${LIBTERMCAP}
+LDADD= -lpthread -ledit -ltermcap
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pppctl/pppctl.8 b/usr.sbin/pppctl/pppctl.8
new file mode 100644
index 0000000..245690f
--- /dev/null
+++ b/usr.sbin/pppctl/pppctl.8
@@ -0,0 +1,232 @@
+.\" $FreeBSD$
+.Dd June 26, 1997
+.Os
+.Dt PPPCTL 8
+.Sh NAME
+.Nm pppctl
+.Nd PPP control program
+.Sh SYNOPSIS
+.Nm
+.Op Fl v
+.Op Fl t Ar n
+.Op Fl p Ar passwd
+.Xo Oo Ar host : Oc Ns
+.Ar Port | LocalSocket
+.Xc
+.Oo
+.Sm off
+.Ar command Oo ; Ar command Oc Ar ...
+.Sm on
+.Oc
+.Sh DESCRIPTION
+This utility provides command line control of the
+.Xr ppp 8
+daemon.
+Its primary use is to facilitate simple scripts that
+control a running daemon.
+.Pp
+The
+.Nm
+utility is passed at least one argument, specifying the socket on which
+.Nm ppp
+is listening.
+Refer to the
+.Sq set server
+command of
+.Nm ppp
+for details.
+If the socket contains a leading '/', it
+is taken as an
+.Dv AF_LOCAL
+socket.
+If it contains a colon, it is treated as a
+.Ar host : Ns Ar port
+pair, otherwise it is treated as a TCP port specification on the
+local machine (127.0.0.1).
+Both the
+.Ar host
+and
+.Ar port
+may be specified numerically if you wish to avoid a DNS lookup
+or do not have an entry for the given port in
+.Pa /etc/services .
+.Pp
+All remaining arguments are concatenated to form the
+.Ar command Ns (s)
+that will be sent to the
+.Nm ppp
+daemon.
+If any semi-colon characters are found, they are treated as
+.Ar command
+delimiters, allowing more than one
+.Ar command
+in a given
+.Sq session .
+For example:
+.Bd -literal -offset indent
+pppctl 3000 set timeout 300\\; show timeout
+.Ed
+.Pp
+Do not forget to escape or quote the ';' as it is a special character
+for most shells.
+.Pp
+If no
+.Ar command
+arguments are given,
+.Nm
+enters interactive mode, where commands are read from standard input.
+When reading commands, the
+.Xr editline 3
+library is used, allowing command-line editing (with
+.Xr editrc 5
+defining editing behaviour).
+The history size
+defaults to
+.Em 20 lines .
+.Pp
+The following command line options are available:
+.Bl -tag -width Ds
+.It Fl v
+Display all data sent to and received from the
+.Nm ppp
+daemon.
+Normally,
+.Nm
+displays only non-prompt lines received.
+This option is ignored in
+interactive mode.
+.It Fl t Ar n
+Use a timeout of
+.Ar n
+instead of the default 2 seconds when connecting.
+This may be required
+if you wish to control a daemon over a slow (or even a dialup) link.
+.It Fl p Ar passwd
+Specify the password required by the
+.Nm ppp
+daemon.
+If this switch is not used,
+.Nm
+will prompt for a password once it has successfully connected to
+.Nm ppp .
+.El
+.Sh ENVIRONMENT
+The following environment variables are understood by
+.Nm
+when in interactive mode:
+.Bl -tag -width XXXXXXXXXX
+.It Dv EL_SIZE
+The number of history lines.
+The default is 20.
+.It Dv EL_EDITOR
+The edit mode.
+Only values of "emacs" and "vi" are accepted.
+Other values
+are silently ignored.
+This environment variable will override the
+.Ar bind -v
+and
+.Ar bind -e
+commands in
+.Pa ~/.editrc .
+.El
+.Sh EXAMPLES
+If you run
+.Nm ppp
+in
+.Fl auto
+mode,
+.Nm
+can be used to automate many frequent tasks (you can actually control
+.Nm ppp
+in any mode except interactive mode).
+Use of the
+.Fl p
+option is discouraged (even in scripts that are not readable by others)
+as a
+.Xr ps 1
+listing may reveal your secret.
+.Pp
+The best way to allow easy, secure
+.Nm
+access is to create a local server socket in
+.Pa /etc/ppp/ppp.conf
+(in the correct section) like this:
+.Bd -literal -offset indent
+set server /var/run/internet "" 0177
+.Ed
+.Pp
+This will instruct
+.Nm ppp
+to create a local domain socket, with srw------- permissions and no
+password, allowing access only to the user that invoked
+.Nm ppp .
+Refer to the
+.Xr ppp 8
+man page for further details.
+.Pp
+You can now create some easy-access scripts.
+To connect to the internet:
+.Bd -literal -offset indent
+#! /bin/sh
+test $# -eq 0 && time=300 || time=$1
+exec pppctl /var/run/internet set timeout $time\\; dial
+.Ed
+.Pp
+To disconnect:
+.Bd -literal -offset indent
+#! /bin/sh
+exec pppctl /var/run/internet set timeout 300\\; close
+.Ed
+.Pp
+To check if the line is up:
+.Bd -literal -offset indent
+#! /bin/sh
+pppctl -p '' -v /var/run/internet quit | grep ^PPP >/dev/null
+if [ $? -eq 0 ]; then
+ echo Link is up
+else
+ echo Link is down
+fi
+.Ed
+.Pp
+You can even make a generic script:
+.Bd -literal -offset indent
+#! /bin/sh
+exec pppctl /var/run/internet "$@"
+.Ed
+.Pp
+You could also use
+.Nm
+to control when dial-on-demand works.
+Suppose you want
+.Nm ppp
+to run all the time, but you want to prevent dial-out between 8pm and 8am
+each day.
+However, any connections active at 8pm should continue to remain
+active until they are closed or naturally time out.
+.Pp
+A
+.Xr cron 8
+entry for 8pm which runs
+.Bd -literal -offset indent
+pppctl /var/run/internet set filter dial 0 deny 0 0
+.Ed
+.Pp
+will block all further dial requests, and the corresponding 8am entry
+.Bd -literal -offset indent
+pppctl /var/run/internet set filter dial -1
+.Ed
+.Pp
+will allow them again.
+.Sh SEE ALSO
+.Xr ps 1 ,
+.Xr editline 3 ,
+.Xr editrc 5 ,
+.Xr services 5 ,
+.Xr ppp 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 2.2.5 .
diff --git a/usr.sbin/pppctl/pppctl.c b/usr.sbin/pppctl/pppctl.c
new file mode 100644
index 0000000..d8e7b42
--- /dev/null
+++ b/usr.sbin/pppctl/pppctl.c
@@ -0,0 +1,680 @@
+/*-
+ * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
+
+#include <sys/time.h>
+#include <err.h>
+#include <errno.h>
+#include <histedit.h>
+#include <semaphore.h>
+#include <pthread.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#define LINELEN 2048
+
+/* Data passed to the threads we create */
+struct thread_data {
+ EditLine *edit; /* libedit stuff */
+ History *hist; /* libedit stuff */
+ pthread_t trm; /* Terminal thread (for pthread_kill()) */
+ int ppp; /* ppp descriptor */
+};
+
+/* Flags passed to Receive() */
+#define REC_PASSWD (1) /* Handle a password request from ppp */
+#define REC_SHOW (2) /* Show everything except prompts */
+#define REC_VERBOSE (4) /* Show everything */
+
+static char *passwd;
+static char *prompt; /* Tell libedit what the current prompt is */
+static int data = -1; /* setjmp() has been done when data != -1 */
+static jmp_buf pppdead; /* Jump the Terminal thread out of el_gets() */
+static int timetogo; /* Tell the Monitor thread to exit */
+static sem_t sem_select; /* select() co-ordination between threads */
+static int TimedOut; /* Set if our connect() timed out */
+static int want_sem_post; /* Need to let the Monitor thread in ? */
+
+/*
+ * How to use pppctl...
+ */
+static int
+usage()
+{
+ fprintf(stderr, "usage: pppctl [-v] [-t n] [-p passwd] "
+ "Port|LocalSock [command[;command]...]\n");
+ fprintf(stderr, " -v tells pppctl to output all"
+ " conversation\n");
+ fprintf(stderr, " -t n specifies a timeout of n"
+ " seconds when connecting (default 2)\n");
+ fprintf(stderr, " -p passwd specifies your password\n");
+ exit(1);
+}
+
+/*
+ * Handle the SIGALRM received due to a connect() timeout.
+ */
+static void
+Timeout(int Sig)
+{
+ TimedOut = 1;
+}
+
+/*
+ * A callback routine for libedit to find out what the current prompt is.
+ * All the work is done in Receive() below.
+ */
+static char *
+GetPrompt(EditLine *e)
+{
+ if (prompt == NULL)
+ prompt = "";
+ return prompt;
+}
+
+/*
+ * Receive data from the ppp descriptor.
+ * We also handle password prompts here (if asked via the `display' arg)
+ * and buffer what our prompt looks like (via the `prompt' global).
+ */
+static int
+Receive(int fd, int display)
+{
+ static char Buffer[LINELEN];
+ struct timeval t;
+ int Result;
+ char *last;
+ fd_set f;
+ int len;
+ int err;
+
+ FD_ZERO(&f);
+ FD_SET(fd, &f);
+ t.tv_sec = 0;
+ t.tv_usec = 100000;
+ prompt = Buffer;
+ len = 0;
+
+ while (Result = read(fd, Buffer+len, sizeof(Buffer)-len-1), Result != -1) {
+ if (Result == 0) {
+ Result = -1;
+ break;
+ }
+ len += Result;
+ Buffer[len] = '\0';
+ if (len > 2 && !strcmp(Buffer+len-2, "> ")) {
+ prompt = strrchr(Buffer, '\n');
+ if (display & (REC_SHOW|REC_VERBOSE)) {
+ if (display & REC_VERBOSE)
+ last = Buffer+len-1;
+ else
+ last = prompt;
+ if (last) {
+ last++;
+ write(STDOUT_FILENO, Buffer, last-Buffer);
+ }
+ }
+ prompt = prompt == NULL ? Buffer : prompt+1;
+ for (last = Buffer+len-2; last > Buffer && *last != ' '; last--)
+ ;
+ if (last > Buffer+3 && !strncmp(last-3, " on", 3)) {
+ /* a password is required ! */
+ if (display & REC_PASSWD) {
+ /* password time */
+ if (!passwd)
+ passwd = getpass("Password: ");
+ sprintf(Buffer, "passwd %s\n", passwd);
+ memset(passwd, '\0', strlen(passwd));
+ if (display & REC_VERBOSE)
+ write(STDOUT_FILENO, Buffer, strlen(Buffer));
+ write(fd, Buffer, strlen(Buffer));
+ memset(Buffer, '\0', strlen(Buffer));
+ return Receive(fd, display & ~REC_PASSWD);
+ }
+ Result = 1;
+ } else
+ Result = 0;
+ break;
+ } else
+ prompt = "";
+ if (len == sizeof Buffer - 1) {
+ int flush;
+ if ((last = strrchr(Buffer, '\n')) == NULL)
+ /* Yeuch - this is one mother of a line ! */
+ flush = sizeof Buffer / 2;
+ else
+ flush = last - Buffer + 1;
+ write(STDOUT_FILENO, Buffer, flush);
+ strcpy(Buffer, Buffer + flush);
+ len -= flush;
+ }
+ if ((Result = select(fd + 1, &f, NULL, NULL, &t)) <= 0) {
+ err = Result == -1 ? errno : 0;
+ if (len)
+ write(STDOUT_FILENO, Buffer, len);
+ if (err == EINTR)
+ continue;
+ break;
+ }
+ }
+
+ return Result;
+}
+
+/*
+ * Handle being told by the Monitor thread that there's data to be read
+ * on the ppp descriptor.
+ *
+ * Note, this is a signal handler - be careful of what we do !
+ */
+static void
+InputHandler(int sig)
+{
+ static char buf[LINELEN];
+ struct timeval t;
+ int len;
+ fd_set f;
+
+ if (data != -1) {
+ FD_ZERO(&f);
+ FD_SET(data, &f);
+ t.tv_sec = t.tv_usec = 0;
+
+ if (select(data + 1, &f, NULL, NULL, &t) > 0) {
+ len = read(data, buf, sizeof buf);
+
+ if (len > 0)
+ write(STDOUT_FILENO, buf, len);
+ else if (data != -1)
+ longjmp(pppdead, -1);
+ }
+
+ sem_post(&sem_select);
+ } else
+ /* Don't let the Monitor thread in 'till we've set ``data'' up again */
+ want_sem_post = 1;
+}
+
+/*
+ * This is a simple wrapper for el_gets(), allowing our SIGUSR1 signal
+ * handler (above) to take effect only after we've done a setjmp().
+ *
+ * We don't want it to do anything outside of here as we're going to
+ * service the ppp descriptor anyway.
+ */
+static const char *
+SmartGets(EditLine *e, int *count, int fd)
+{
+ const char *result;
+
+ if (setjmp(pppdead))
+ result = NULL;
+ else {
+ data = fd;
+ if (want_sem_post)
+ /* Let the Monitor thread in again */
+ sem_post(&sem_select);
+ result = el_gets(e, count);
+ }
+
+ data = -1;
+
+ return result;
+}
+
+/*
+ * The Terminal thread entry point.
+ *
+ * The bulk of the interactive work is done here. We read the terminal,
+ * write the results to our ppp descriptor and read the results back.
+ *
+ * While reading the terminal (using el_gets()), it's possible to take
+ * a SIGUSR1 from the Monitor thread, telling us that the ppp descriptor
+ * has some data. The data is read and displayed by the signal handler
+ * itself.
+ */
+static void *
+Terminal(void *v)
+{
+ struct sigaction act, oact;
+ struct thread_data *td;
+ const char *l;
+ int len;
+#ifndef __OpenBSD__
+ HistEvent hev = { 0, "" };
+#endif
+
+ act.sa_handler = InputHandler;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = SA_RESTART;
+ sigaction(SIGUSR1, &act, &oact);
+
+ td = (struct thread_data *)v;
+ want_sem_post = 1;
+
+ while ((l = SmartGets(td->edit, &len, td->ppp))) {
+ if (len > 1)
+#ifdef __OpenBSD__
+ history(td->hist, H_ENTER, l);
+#else
+ history(td->hist, &hev, H_ENTER, l);
+#endif
+ write(td->ppp, l, len);
+ if (Receive(td->ppp, REC_SHOW) != 0)
+ break;
+ }
+
+ return NULL;
+}
+
+/*
+ * The Monitor thread entry point.
+ *
+ * This thread simply monitors our ppp descriptor. When there's something
+ * to read, a SIGUSR1 is sent to the Terminal thread.
+ *
+ * sem_select() is used by the Terminal thread to keep us from sending
+ * flurries of SIGUSR1s, and is used from the main thread to wake us up
+ * when it's time to exit.
+ */
+static void *
+Monitor(void *v)
+{
+ struct thread_data *td;
+ fd_set f;
+ int ret;
+
+ td = (struct thread_data *)v;
+ FD_ZERO(&f);
+ FD_SET(td->ppp, &f);
+
+ sem_wait(&sem_select);
+ while (!timetogo)
+ if ((ret = select(td->ppp + 1, &f, NULL, NULL, NULL)) > 0) {
+ pthread_kill(td->trm, SIGUSR1);
+ sem_wait(&sem_select);
+ }
+
+ return NULL;
+}
+
+static const char *
+sockaddr_ntop(const struct sockaddr *sa)
+{
+ const void *addr;
+ static char addrbuf[INET6_ADDRSTRLEN];
+
+ switch (sa->sa_family) {
+ case AF_INET:
+ addr = &((const struct sockaddr_in *)sa)->sin_addr;
+ break;
+ case AF_UNIX:
+ addr = &((const struct sockaddr_un *)sa)->sun_path;
+ break;
+ case AF_INET6:
+ addr = &((const struct sockaddr_in6 *)sa)->sin6_addr;
+ break;
+ default:
+ return NULL;
+ }
+ inet_ntop(sa->sa_family, addr, addrbuf, sizeof(addrbuf));
+ return addrbuf;
+}
+
+/*
+ * Connect to ppp using either a local domain socket or a tcp socket.
+ *
+ * If we're given arguments, process them and quit, otherwise create two
+ * threads to handle interactive mode.
+ */
+int
+main(int argc, char **argv)
+{
+ struct sockaddr_un ifsun;
+ int n, arg, fd, len, verbose, save_errno, hide1, hide1off, hide2;
+ unsigned TimeoutVal;
+ char *DoneWord = "x", *next, *start;
+ struct sigaction act, oact;
+ void *thread_ret;
+ pthread_t mon;
+ char Command[LINELEN];
+ char Buffer[LINELEN];
+
+ verbose = 0;
+ TimeoutVal = 2;
+ hide1 = hide1off = hide2 = 0;
+
+ for (arg = 1; arg < argc; arg++)
+ if (*argv[arg] == '-') {
+ for (start = argv[arg] + 1; *start; start++)
+ switch (*start) {
+ case 't':
+ TimeoutVal = (unsigned)atoi
+ (start[1] ? start + 1 : argv[++arg]);
+ start = DoneWord;
+ break;
+
+ case 'v':
+ verbose = REC_VERBOSE;
+ break;
+
+ case 'p':
+ if (start[1]) {
+ hide1 = arg;
+ hide1off = start - argv[arg];
+ passwd = start + 1;
+ } else {
+ hide1 = arg;
+ hide1off = start - argv[arg];
+ passwd = argv[++arg];
+ hide2 = arg;
+ }
+ start = DoneWord;
+ break;
+
+ default:
+ usage();
+ }
+ }
+ else
+ break;
+
+
+ if (argc < arg + 1)
+ usage();
+
+ if (hide1) {
+ char title[1024];
+ int pos, harg;
+
+ for (harg = pos = 0; harg < argc; harg++)
+ if (harg == 0 || harg != hide2) {
+ if (harg == 0 || harg != hide1)
+ n = snprintf(title + pos, sizeof title - pos, "%s%s",
+ harg ? " " : "", argv[harg]);
+ else if (hide1off > 1)
+ n = snprintf(title + pos, sizeof title - pos, " %.*s",
+ hide1off, argv[harg]);
+ else
+ n = 0;
+ if (n < 0 || n >= sizeof title - pos)
+ break;
+ pos += n;
+ }
+#ifdef __FreeBSD__
+ setproctitle("-%s", title);
+#else
+ setproctitle("%s", title);
+#endif
+ }
+
+ if (*argv[arg] == '/') {
+ memset(&ifsun, '\0', sizeof ifsun);
+ ifsun.sun_len = strlen(argv[arg]);
+ if (ifsun.sun_len > sizeof ifsun.sun_path - 1) {
+ warnx("%s: path too long", argv[arg]);
+ return 1;
+ }
+ ifsun.sun_family = AF_LOCAL;
+ strcpy(ifsun.sun_path, argv[arg]);
+
+ if (fd = socket(AF_LOCAL, SOCK_STREAM, 0), fd < 0) {
+ warnx("cannot create local domain socket");
+ return 2;
+ }
+ if (connect(fd, (struct sockaddr *)&ifsun, sizeof(ifsun)) < 0) {
+ if (errno)
+ warn("cannot connect to socket %s", argv[arg]);
+ else
+ warnx("cannot connect to socket %s", argv[arg]);
+ close(fd);
+ return 3;
+ }
+ } else {
+ char *addr, *p, *port;
+ const char *caddr;
+ struct addrinfo hints, *res, *pai;
+ int gai;
+ char local[] = "localhost";
+
+ addr = argv[arg];
+ if (addr[strspn(addr, "0123456789")] == '\0') {
+ /* port on local machine */
+ port = addr;
+ addr = local;
+ } else if (*addr == '[') {
+ /* [addr]:port */
+ if ((p = strchr(addr, ']')) == NULL) {
+ warnx("%s: mismatched '['", addr);
+ return 1;
+ }
+ addr++;
+ *p++ = '\0';
+ if (*p != ':') {
+ warnx("%s: missing port", addr);
+ return 1;
+ }
+ port = ++p;
+ } else {
+ /* addr:port */
+ p = addr + strcspn(addr, ":");
+ if (*p != ':') {
+ warnx("%s: missing port", addr);
+ return 1;
+ }
+ *p++ = '\0';
+ port = p;
+ }
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_socktype = SOCK_STREAM;
+ gai = getaddrinfo(addr, port, &hints, &res);
+ if (gai != 0) {
+ warnx("%s: %s", addr, gai_strerror(gai));
+ return 1;
+ }
+ for (pai = res; pai != NULL; pai = pai->ai_next) {
+ if (fd = socket(pai->ai_family, pai->ai_socktype,
+ pai->ai_protocol), fd < 0) {
+ warnx("cannot create socket");
+ continue;
+ }
+ TimedOut = 0;
+ if (TimeoutVal) {
+ act.sa_handler = Timeout;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ sigaction(SIGALRM, &act, &oact);
+ alarm(TimeoutVal);
+ }
+ if (connect(fd, pai->ai_addr, pai->ai_addrlen) == 0)
+ break;
+ if (TimeoutVal) {
+ save_errno = errno;
+ alarm(0);
+ sigaction(SIGALRM, &oact, 0);
+ errno = save_errno;
+ }
+ caddr = sockaddr_ntop(pai->ai_addr);
+ if (caddr == NULL)
+ caddr = argv[arg];
+ if (TimedOut)
+ warnx("timeout: cannot connect to %s", caddr);
+ else {
+ if (errno)
+ warn("cannot connect to %s", caddr);
+ else
+ warnx("cannot connect to %s", caddr);
+ }
+ close(fd);
+ }
+ freeaddrinfo(res);
+ if (pai == NULL)
+ return 1;
+ if (TimeoutVal) {
+ alarm(0);
+ sigaction(SIGALRM, &oact, 0);
+ }
+ }
+
+ len = 0;
+ Command[sizeof(Command)-1] = '\0';
+ for (arg++; arg < argc; arg++) {
+ if (len && len < sizeof(Command)-1)
+ strcpy(Command+len++, " ");
+ strncpy(Command+len, argv[arg], sizeof(Command)-len-1);
+ len += strlen(Command+len);
+ }
+
+ switch (Receive(fd, verbose | REC_PASSWD)) {
+ case 1:
+ fprintf(stderr, "Password incorrect\n");
+ break;
+
+ case 0:
+ passwd = NULL;
+ if (len == 0) {
+ struct thread_data td;
+ const char *env;
+ int size;
+#ifndef __OpenBSD__
+ HistEvent hev = { 0, "" };
+#endif
+
+ td.hist = history_init();
+ if ((env = getenv("EL_SIZE"))) {
+ size = atoi(env);
+ if (size < 0)
+ size = 20;
+ } else
+ size = 20;
+#ifdef __OpenBSD__
+ history(td.hist, H_EVENT, size);
+ td.edit = el_init("pppctl", stdin, stdout);
+#else
+ history(td.hist, &hev, H_SETSIZE, size);
+ td.edit = el_init("pppctl", stdin, stdout, stderr);
+#endif
+ el_source(td.edit, NULL);
+ el_set(td.edit, EL_PROMPT, GetPrompt);
+ if ((env = getenv("EL_EDITOR"))) {
+ if (!strcmp(env, "vi"))
+ el_set(td.edit, EL_EDITOR, "vi");
+ else if (!strcmp(env, "emacs"))
+ el_set(td.edit, EL_EDITOR, "emacs");
+ }
+ el_set(td.edit, EL_SIGNAL, 1);
+ el_set(td.edit, EL_HIST, history, (const char *)td.hist);
+
+ td.ppp = fd;
+ td.trm = NULL;
+
+ /*
+ * We create two threads. The Terminal thread does all the
+ * work while the Monitor thread simply tells the Terminal
+ * thread when ``fd'' becomes readable. The telling is done
+ * by sending a SIGUSR1 to the Terminal thread. The
+ * sem_select semaphore is used to prevent the monitor
+ * thread from firing excessive signals at the Terminal
+ * thread (it's abused for exit handling too - see below).
+ *
+ * The Terminal thread never uses td.trm !
+ */
+ sem_init(&sem_select, 0, 0);
+
+ pthread_create(&td.trm, NULL, Terminal, &td);
+ pthread_create(&mon, NULL, Monitor, &td);
+
+ /* Wait for the terminal thread to finish */
+ pthread_join(td.trm, &thread_ret);
+ fprintf(stderr, "Connection closed\n");
+
+ /* Get rid of the monitor thread by abusing sem_select */
+ timetogo = 1;
+ close(fd);
+ fd = -1;
+ sem_post(&sem_select);
+ pthread_join(mon, &thread_ret);
+
+ /* Restore our terminal and release resources */
+ el_end(td.edit);
+ history_end(td.hist);
+ sem_destroy(&sem_select);
+ } else {
+ start = Command;
+ do {
+ next = strchr(start, ';');
+ while (*start == ' ' || *start == '\t')
+ start++;
+ if (next)
+ *next = '\0';
+ strcpy(Buffer, start);
+ Buffer[sizeof(Buffer)-2] = '\0';
+ strcat(Buffer, "\n");
+ if (verbose)
+ write(STDOUT_FILENO, Buffer, strlen(Buffer));
+ write(fd, Buffer, strlen(Buffer));
+ if (Receive(fd, verbose | REC_SHOW) != 0) {
+ fprintf(stderr, "Connection closed\n");
+ break;
+ }
+ if (next)
+ start = ++next;
+ } while (next && *next);
+ if (verbose)
+ write(STDOUT_FILENO, "quit\n", 5);
+ write(fd, "quit\n", 5);
+ while (Receive(fd, verbose | REC_SHOW) == 0)
+ ;
+ if (verbose)
+ puts("");
+ }
+ break;
+
+ default:
+ warnx("ppp is not responding");
+ break;
+ }
+
+ if (fd != -1)
+ close(fd);
+
+ return 0;
+}
diff --git a/usr.sbin/pppd/Makefile b/usr.sbin/pppd/Makefile
new file mode 100644
index 0000000..2df0330
--- /dev/null
+++ b/usr.sbin/pppd/Makefile
@@ -0,0 +1,56 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+# I once used this extensively, but no longer have a modem. Feel free
+# to ask me questions about it, but I disclaim ownership now. -Peter
+
+PROG= pppd
+MAN= pppd.8
+SRCS= main.c magic.c fsm.c lcp.c ipcp.c ipxcp.c upap.c chap.c ccp.c \
+ demand.c auth.c options.c sys-bsd.c
+BINMODE=4550
+BINOWN= root
+BINGRP= dialer
+
+CFLAGS+= -DHAVE_PATHS_H
+
+DPADD= ${LIBCRYPT} ${LIBUTIL} ${LIBMD}
+LDADD= -lcrypt -lutil -lmd
+
+# Support SPX/IPX - not quite ready
+#CFLAGS+= -DIPX_CHANGE
+#SRCS+= ipxcp.c
+
+# Callback Control Protocol
+CFLAGS+= -DCBCP_SUPPORT
+SRCS+= cbcp.c
+
+# Filter support
+CFLAGS+= -DPPP_FILTER
+DPADD+= ${LIBPCAP}
+LDADD+= -lpcap
+
+# MS-CHAP support. Requires the DES library.
+.if ${MK_OPENSSL} != "no" && !defined(RELEASE_CRUNCH)
+CFLAGS+= -DCHAPMS
+SRCS+= chap_ms.c
+LDADD+= -lcrypto
+DPADD+= ${LIBCRYPTO}
+.endif
+
+.if ${MK_INET6_SUPPORT} != "no"
+CFLAGS+=-DINET6
+SRCS+= eui64.c ipv6cp.c
+.endif
+
+.if defined(RELEASE_CRUNCH)
+# We must create these objects because crunchgen will link them,
+# and we don't want any unused symbols to spoil the final link.
+SRCS+= chap_ms.c
+chap_ms.o:
+ >null_${.PREFIX}.c
+ cc -c -o ${.TARGET} null_${.PREFIX}.c
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pppd/RELNOTES b/usr.sbin/pppd/RELNOTES
new file mode 100644
index 0000000..1c1e3922
--- /dev/null
+++ b/usr.sbin/pppd/RELNOTES
@@ -0,0 +1,726 @@
+# $FreeBSD$
+
+This is the README file for ppp-2.3, a package which implements the
+Point-to-Point Protocol (PPP) to provide Internet connections over
+serial lines.
+
+
+Introduction.
+*************
+
+The Point-to-Point Protocol (PPP) provides a standard way to establish
+a network connection over a serial link. At present, this package
+supports IP and the protocols layered above IP, such as TCP and UDP.
+The Linux port of this package also has support for IPX.
+
+This software consists of two parts:
+
+- Kernel code, which establishes a network interface and passes
+packets between the serial port, the kernel networking code and the
+PPP daemon (pppd). This code is implemented using STREAMS modules on
+SunOS 4.x, Solaris 2.x, System V Release 4, and OSF/1, and as a
+line discipline under Ultrix, NextStep, NetBSD, FreeBSD, and Linux.
+
+- The PPP daemon (pppd), which negotiates with the peer to establish
+the link and sets up the ppp network interface. Pppd includes support
+for authentication, so you can control which other systems may make a
+PPP connection and what IP addresses they may use.
+
+
+Installation.
+*************
+
+The file SETUP contains general information about setting up your
+system for using PPP. There is also a README file for each supported
+system, which contains more specific details for installing PPP on
+that system. The supported systems, and the corresponding README
+files, are:
+
+ Digital Unix (OSF/1) README.osf
+ Linux README.linux
+ NetBSD, FreeBSD README.bsd
+ NeXTStep README.next
+ Solaris 2 README.sol2
+ SunOS 4.x README.sunos4
+ System V Release 4 README.svr4
+ Ultrix 4.x README.ultrix
+
+Unfortunately, AIX 4 is no longer supported, since I don't have a
+maintainer for the AIX 4 port. If you want to volunteer, contact me.
+The Ultrix port is untested, as I no longer have access to an Ultrix
+box.
+
+In each case you start by running the ./configure script. This works
+out which operating system you are using and creates symbolic links to
+the appropriate makefiles. You then run `make' to compile the
+user-level code, and (as root) `make install' to install the
+user-level programs pppd, chat and pppstats.
+
+The procedures for installing the kernel code vary from system to
+system. On some systems, the kernel code can be loaded into a running
+kernel using a `modload' facility. On others, the kernel image has to
+be recompiled and the system rebooted. See the README.* files for
+details.
+
+N.B. Since 2.3.0, leaving the permitted IP addresses column of the
+pap-secrets or chap-secrets file empty means that no addresses are
+permitted. You need to put a "*" in that column to allow the peer to
+use any IP address. (This only applies where the peer is
+authenticating itself to you, of course.)
+
+
+What's new in ppp-2.3.5.
+************************
+
+* Minor corrections to the Digital UNIX and NetBSD ports.
+
+* A workaround to avoid tickling a bug in the `se' serial port driver
+on Sun PCI Ultra machines running Solaris.
+
+* Fixed a bug in the negotiation of the Microsoft WINS server address
+option.
+
+* Fixed a bug in the Linux port where it would fail for kernel
+versions above 2.1.99.
+
+
+What was new in ppp-2.3.4.
+**************************
+
+* The NeXT port has been updated, thanks to Steve Perkins.
+
+* ppp-2.3.4 compiles and works under Solaris 2.6, using either gcc or
+cc.
+
+* With the Solaris, SVR4 and SunOS ports, you can control the choice
+of C compiler, C compiler options, and installation directories by
+editing the svr4/Makedefs or sunos4/Makedefs file.
+
+* Until now, we have been using the number 24 to identify Deflate
+compression in the CCP negotiations, which was the number in the draft
+RFC describing Deflate. The number actually assigned to Deflate is
+26. The code has been changed to use 26, but to allow the use of 24
+for now for backwards compatibility. (This can be disabled with the
+`nodeflatedraft' option to pppd.)
+
+* Fixed some bugs in the linux driver and deflate compressor which
+were causing compression problems, including corrupting long
+incompressible packets sometimes.
+
+* Fixes to the PAM and shadow password support in pppd, from Al
+Longyear and others.
+
+* Pppd now sets some environment variables for scripts it invokes
+(ip-up/down, auth-ip/down), giving information about the connection.
+The variables it sets are PEERNAME, IPLOCAL, IPREMOTE, UID, DEVICE,
+SPEED, and IFNAME.
+
+* Pppd now has an `updetach' option, which will cause it to detach
+from its controlling terminal once the link has come up (i.e. once it
+is available for IP traffic).
+
+
+What was new in ppp-2.3.3.
+**************************
+
+* Fixed compilation problems under SunOS.
+
+* Fixed a bug introduced into chat in 2.3.2, and compilation problems
+introduced into the MS-CHAP implementation in 2.3.2.
+
+* The linux kernel driver has been updated for recent 2.1-series
+kernel changes, and it now will ask kerneld to load compression
+modules when required, if the kernel is configured to support kerneld.
+
+* Pppd should now compile correctly under linux on systems with glibc.
+
+
+What was new in ppp-2.3.2.
+**************************
+
+* In 2.3.1, I made a change which was intended to make pppd able to
+detect loss of CD during or immediately after the connection script
+runs. Unfortunately, this had the side-effect that the connection
+script wouldn't work at all on some systems. This change has been
+reversed.
+
+* Fix compilation problems in the Linux kernel driver.
+
+
+What was new in ppp-2.3.1.
+**************************
+
+* Enhancements to chat, thanks to Francis Demierre. Chat can now
+accept comments in the chat script file, and has new SAY, HANGUP,
+CLR_ABORT and CLR_REPORT keywords.
+
+* Fixed a bug which causes 2.3.0 to crash Solaris systems.
+
+* Bug-fixes and restructuring of the Linux kernel driver.
+
+* The holdoff behaviour of pppd has been changed slightly: now, if
+the link comes up for IP (or other network protocol) traffic, we
+consider that the link has been successfully established, and don't
+enforce the holdoff period after the link goes down.
+
+* Pppd should now correctly wait for CD (carrier detect) from the
+modem, even when the serial port initially had CLOCAL set, and it
+should also detect loss of CD during or immediately after the
+connection script runs.
+
+* Under linux, pppd will work with older 2.2.0* version kernel
+drivers, although demand-dialling is not supported with them.
+
+* Minor bugfixes for pppd.
+
+
+What was new in ppp-2.3.
+************************
+
+* Demand-dialling. Pppd now has a mode where it will establish the
+network interface immediately when it starts, but not actually bring
+the link up until it sees some data to be sent. Look for the demand
+option description in the pppd man page. Demand-dialling is not
+supported under Ultrix or NeXTStep.
+
+* Idle timeout. Pppd will optionally terminate the link if no data
+packets are sent or received within a certain time interval.
+
+* Pppd now runs the /etc/ppp/auth-up script, if it exists, when the
+peer successfully authenticates itself, and /etc/ppp/auth-down when
+the connection is subsequently terminated. This can be useful for
+accounting purposes.
+
+* A new packet compression scheme, Deflate, has been implemented.
+This uses the same compression method as `gzip'. This method is free
+of patent or copyright restrictions, and it achieves better
+compression than BSD-Compress. It does consume more CPU cycles for
+compression than BSD-Compress, but this shouldn't be a problem for
+links running at 100kbit/s or less.
+
+* There is no code in this distribution which is covered by Brad
+Clements' restrictive copyright notice. The STREAMS modules for SunOS
+and OSF/1 have been rewritten, based on the Solaris 2 modules, which
+were written from scratch without any Clements code.
+
+* Pppstats has been reworked to clean up the output format somewhat.
+It also has a new -d option which displays data rate in kbyte/s for
+those columns which would normally display bytes.
+
+* Pppd options beginning with - or + have been renamed, e.g. -ip
+became noip, +chap became require-chap, etc. The old options are
+still accepted for compatibility but may be removed in future.
+
+* Pppd now has some options (such as the new `noauth' option) which
+can only be specified if it is being run by root, or in an
+"privileged" options file: /etc/ppp/options or an options file in the
+/etc/ppp/peers directory. There is a new "call" option to read
+options from a file in /etc/ppp/peers, making it possible for non-root
+users to make unauthenticated connections, but only to certain trusted
+peers. My intention is to make the `auth' option the default in a
+future release.
+
+* Several minor new features have been added to pppd, including the
+maxconnect and welcome options. Pppd will now terminate the
+connection when there are no network control protocols running. The
+allowed IP address(es) field in the secrets files can now specify
+subnets (with a notation like 123.45.67.89/24) and addresses which are
+not acceptable (put a ! on the front).
+
+* Numerous bugs have been fixed (no doubt some have been introduced :-)
+Thanks to those who reported bugs in ppp-2.2.
+
+
+Patents.
+********
+
+The BSD-Compress algorithm used for packet compression is the same as
+that used in the Unix "compress" command. It is apparently covered by
+U.S. patents 4,814,746 (owned by IBM) and 4,558,302 (owned by Unisys),
+and corresponding patents in various other countries (but not
+Australia). If this is of concern, you can build the package without
+including BSD-Compress. To do this, edit net/ppp-comp.h to change the
+definition of DO_BSD_COMPRESS to 0. The bsd-comp.c files are then no
+longer needed, so the references to bsd-comp.o may optionally be
+removed from the Makefiles.
+
+
+Contacts.
+*********
+
+The comp.protocols.ppp newsgroup is a useful place to get help if you
+have trouble getting your ppp connections to work. Please do not send
+me questions of the form "please help me get connected to my ISP" -
+I'm sorry, but I simply do not have the time to answer all the
+questions like this that I get.
+
+If you find bugs in this package, please report them to the maintainer
+for the port for the operating system you are using:
+
+Digital Unix (OSF/1) Farrell Woods <ftw@zk3.dec.com>
+Linux Al Longyear <longyear@pobox.com>
+NetBSD Matthew Green <mrg@eterna.com.au
+FreeBSD Peter Wemm <peter@haywire.DIALix.COM>
+NeXTStep Steve Perkins <perkins@cps.msu.edu>
+Solaris 2 Paul Mackerras <Paul.Mackerras@cs.anu.edu.au>
+SunOS 4.x Paul Mackerras <Paul.Mackerras@cs.anu.edu.au>
+System V Release 4 Matthias Apitz <Matthias.Apitz@SOFTCON.de>
+Ultrix 4.x Paul Mackerras (for want of anybody better :-)
+
+
+Copyrights:
+***********
+
+All of the code can be freely used and redistributed.
+
+
+Distribution:
+*************
+
+The primary site for releases of this software is:
+
+ ftp://cs.anu.edu.au/pub/software/ppp/
+
+-------------------------
+This is the README file for ppp-2.2, a package which implements the
+Point-to-Point Protocol (PPP) to provide Internet connections over
+serial lines.
+
+
+Introduction.
+*************
+
+The Point-to-Point Protocol (PPP) provides a standard way to transmit
+datagrams over a serial link, as well as a standard way for the
+machines at either end of the link (the `peers') to negotiate various
+optional characteristics of the link. Using PPP, a serial link can be
+used to transmit Internet Protocol (IP) datagrams, allowing TCP/IP
+connections between the peers. PPP is defined in several RFC (Request
+For Comments) documents, in particular RFCs 1661, 1662, 1332 and 1334.
+Other RFCs describe standard ways to transmit datagrams from other
+network protocols (e.g., DECnet, OSI, Appletalk), but this package
+only supports IP.
+
+This software consists of two parts:
+
+- Kernel code, which establishes a network interface and passes
+packets between the serial port, the kernel networking code and the
+PPP daemon (pppd). This code is implemented using STREAMS modules on
+SunOS 4.x, AIX 4.1 and OSF/1, and as a line discipline under Ultrix,
+NextStep, NetBSD, FreeBSD, and Linux.
+
+- The PPP daemon (pppd), which negotiates with the peer to establish
+the link and sets up the ppp network interface. Pppd includes support
+for authentication, so you can control which other systems may make a
+PPP connection and what IP addresses they may use.
+
+
+What is new in ppp-2.2.
+***********************
+
+* More systems are now supported:
+
+ AIX 4, thanks to Charlie Wick,
+ OSF/1 on DEC Alpha, thanks to Steve Tate (srt@zaphod.csci.unt.edu),
+ NextStep 3.2 and 3.3, thanks to Philip-Andrew Prindeville
+ (philipp@res.enst.fr) and Steve Perkins (perkins@cps.msu.edu),
+ Solaris 2,
+
+in addition to NetBSD 1.0, SunOS 4.x, Ultrix 4.x, FreeBSD 2.0, and
+Linux.
+
+* Packet compression has been implemented. This version implements
+CCP (Compression Control Protocol) and the BSD-Compress compression
+scheme according to the current draft RFCs. This means that incoming
+and outgoing packets can be compressed with the LZW scheme (same as
+the `compress' command) using a code size of up to 15 bits.
+
+* Some bug fixes to the LCP protocol code. In particular, pppd now
+correctly replies with a Configure-NAK (instead of a Configure-Reject)
+if the peer asks for CHAP and pppd is willing to do PAP but not CHAP.
+
+* The ip-up and ip-down scripts are now run with the real user ID set
+to root, and with an empty environment. Clearing the environment
+fixes a security hole.
+
+* The kernel code on NetBSD, FreeBSD, NextStep and Ultrix has been
+restructured to make it easier to implement PPP over devices other
+than asynchronous tty ports (for example, synchronous serial ports).
+
+* pppd now looks at the list of interfaces in the system to determine
+what the netmask should be. In most cases, this should eliminate the
+need to use the `netmask' option.
+
+* There is a new `papcrypt' option to pppd, which specifies that
+secrets in /etc/ppp/pap-secrets used for authenticating the peer are
+encrypted, so pppd always encrypts the peer's password before
+comparing it with the secret from /etc/ppp/pap-secrets. This gives
+better security.
+
+
+Patents.
+********
+
+The BSD-Compress algorithm used for packet compression is the same as
+that used in the Unix "compress" command. It is apparently covered by
+U.S. patents 4,814,746 (owned by IBM) and 4,558,302 (owned by Unisys),
+and corresponding patents in various other countries (but not
+Australia). If this is of concern, you can build the package without
+including BSD-Compress. To do this, edit net/ppp-comp.h to change the
+definition of DO_BSD_COMPRESS to 0. The bsd-comp.c files are then no
+longer needed, so the references to bsd-comp.o may optionally be
+removed from the Makefiles.
+
+
+Contacts.
+*********
+
+Bugs in the the SunOS, NetBSD and Ultrix ports and bugs in pppd, chat
+or pppstats should be reported to:
+
+ paulus@cs.anu.edu.au
+ Paul Mackerras
+ Dept. of Computer Science
+ Australian National University
+ Canberra ACT 0200
+ AUSTRALIA
+
+Bugs in other ports should be reported to the maintainer for that port
+(see the appropriate README.* file) or to the above. Unfortunately,
+Charlie Wick is not in a position to provide support for the AIX 4
+port, so if you find bugs in it, send them to me.
+
+Thanks to:
+
+ Brad Parker (brad@fcr.com)
+ Greg Christy (gmc@quotron.com)
+ Drew D. Perkins (ddp@andrew.cmu.edu)
+ Rick Adams (rick@seismo.ARPA)
+ Chris Torek (chris@mimsy.umd.edu, umcp-cs!chris).
+
+
+Copyrights:
+
+Most of the code can be freely used and redistributed. The STREAMS
+code for SunOS 4.x, OSF/1 and AIX 4 is under a more restrictive
+copyright:
+
+ This code is Copyright (C) 1989, 1990 By Brad K. Clements,
+ All Rights Reserved.
+
+ You may use this code for your personal use, to provide a non-profit
+ service to others, or to use as a test platform for a commercial
+ implementation.
+
+ You may NOT use this code in a commercial product, nor to provide a
+ commercial service, nor may you sell this code without express
+ written permission of the author.
+
+ Otherwise, Enjoy!
+
+This copyright applies to (parts of) the following files:
+
+ sunos/ppp_async.c
+ sunos/ppp_if.c
+ aix4/ppp_async.c
+ aix4/ppp_if.c
+ net/ppp_str.h
+ pppd/sys-str.c
+ pppd/sys-osf.c
+ pppd/sys-aix4.c
+-------------------------
+ pppd-2.1.1 release notes
+ Paul Mackerras 27 May 1994
+
+This file details the new and changed features in pppd since version 1.3.
+Briefly:
+ - the protocol code has been updated to conform with
+ RFCs 1548, 1549, 1332 and 1334
+ - security has been improved
+ - functionality has been improved in various ways.
+
+
+NEW FEATURES
+
+* The option negotiation automaton has been updated to RFC1548. LCP
+now rejects the Quality Protocol option, since LQR is not implemented
+yet. IPCP now uses the IP-Address option, and falls back to the old
+IP-Addresses option if the IP-Address option is rejected. IPCP also
+uses the new form of the VJ-Compression option.
+
+RFC1548 defines the "passive" option to mean that the automaton
+outputs configure-request packets initially, but does not close down
+if no answer is received. A valid configure-request received will
+restart the negotiation. The "silent" option has been added with the
+old meaning of "passive", i.e. the automaton will not output
+configure-requests until it receives a valid one from the peer.
+
+* More systems are supported: in addition to SunOS 4.x and BSD/Net-2
+derived systems, Ultrix and Linux are supported, thanks to Robert
+Olsson, Per Sundstrom, Michael Callahan and Al Longyear.
+
+* Options can be taken from files as well as the command line. pppd
+reads options from the files /etc/ppp/options and ~/.ppprc before
+looking at the command line, and /etc/ppp/options.<ttyname> after
+interpreting the options on the command line. An options file is
+parsed into a series of words, delimited by whitespace. Whitespace
+can be included in a word by enclosing the word in quotes (").
+Backslash (\) quotes the following character. A hash (#) starts a
+comment, which continues until the end of the line. In addition, the
+`file' option causes pppd to read options from a file. pppd will
+report and error and exit if ~/.ppprc or the file given as the
+argument to the `file' option cannot be read by the user who invoked
+pppd.
+
+* On those systems, such as NetBSD, where the serial line speed is
+stored in the termios structure in bits per second (i.e. B9600 ==
+9600), it is possible to set any speed.
+
+* If desired, pppd will output LCP echo-request frames periodically
+while the link is up, and take the link down if no replies are
+received to a user-configurable number of echo-requests. This can be
+used to detect that the serial connection has been broken on those
+systems which don't have hardware modem control lines.
+
+AUTHENTICATION
+
+Previous versions of pppd have provided no control over which IP
+addresses the peer can use. Thus it is possible for the peer to
+impersonate another host on the local network, leading to various
+security holes. In addition, the authentication mechanisms were quite
+weak: if the peer refused to agree to authenticate, pppd would print a
+warning message but still allow the link to come up. The CHAP
+implementation also appeared to be quite broken (has anybody actually
+used it?).
+
+This new version of pppd addresses these problems. My aim has been to
+provide system administrators with sufficient access control that PPP
+access to a server machine can be provided to legitimate users without
+fear of compromising the security of the server or the network it's
+on. In part this is provided by the /etc/ppp/options file, where the
+administrator can place options to require authentication which cannot
+be disabled by users. Thus the new pppd can made setuid-root and run
+by users.
+
+The behaviour where pppd refuses to run unless the /etc/ppp/options
+file is present and readable by pppd is now the default behaviour. If
+you really want pppd to run without the presence of the
+/etc/ppp/options file, you will have to include -DREQ_SYSOPTIONS=0 on
+the compilation command line.
+
+The options related to authentication are:
+
+ auth Require authentication from the peer. If neither
+ +chap or +pap is also given, either CHAP or PAP
+ authentication will be accepted.
+ +chap Require CHAP authentication from the peer.
+ +pap Require PAP authentication from the peer.
+ -chap Don't agree to authenticate ourselves with the peer
+ using CHAP.
+ -pap Don't agree to authenticate ourselves using PAP.
+ +ua <f> Get username and password for authenticating ourselves
+ with the peer using PAP from file <f>.
+ name <n> Use <n> as the local name for authentication.
+ usehostname Use this machine's hostname as the local name for
+ authentication.
+ remotename <n> Use <n> as the name of the peer for authentication.
+ login If the peer authenticates using PAP, check the
+ supplied username and password against the system
+ password database, and make a wtmp entry.
+ user <n> Use <n> as the username for authenticating ourselves
+ using PAP.
+
+The defaults are to agree to authenticate if requested, and to not
+require authentication from the peer. However, pppd will not agree to
+authenticate itself with a particular protocol if it has no secrets
+which could be used to do so.
+
+Authentication is based on secrets, which are selected from secrets
+files (/etc/ppp/pap-secrets for PAP, /etc/ppp/chap-secrets for CHAP).
+Both secrets files have the same format, and both can store secrets
+for several combinations of server (authenticating peer) and client
+(peer being authenticated). Note that each end can be both a server
+and client, and that different protocols can be used in the two
+directions if desired.
+
+A secrets file is parsed into words as for an options file. A secret
+is specified by a line containing at least 3 words, in the order
+client, server, secret. Any following words on the same line are
+taken to be a list of acceptable IP addresses for that client. If
+there are only 3 words on the line, it is assumed that any IP address
+is OK; to disallow all IP addresses, use "-". If the secret starts
+with an `@', what follows is assumed to be the name of a file from
+which to read the secret. A "*" as the client or server name matches
+any name. When selecting a secret, pppd takes the best match, i.e.
+the match with the fewest wildcards.
+
+Thus a secrets file contains both secrets for use in authenticating
+other hosts, plus secrets which we use for authenticating ourselves to
+others. Which secret to use is chosen based on the names of the host
+(the `local name') and its peer (the `remote name'). The local name
+is set as follows:
+
+ if the `usehostname' option is given,
+ then the local name is the hostname of this machine
+ (with the domain appended, if given)
+
+ else if the `name' option is given,
+ then use the argument of the first `name' option seen
+
+ else if the local IP address is specified with a
+ host name (e.g. `sirius:')
+ then use that host name
+
+ else use the hostname of this machine
+ (with the domain appended, if given)
+
+When authenticating ourselves using PAP, there is also a `username'
+which is the local name by default, but can be set with the `user'
+option or the `+ua' option.
+
+The remote name is set as follows:
+
+ if the `remotename' option is given,
+ then use the argument of the last `remotename' option seen
+
+ else if the remote IP address is specified with a
+ host name (e.g. `avago:')
+ then use that host name
+
+ else the remote name is the null string "".
+
+Secrets are selected from the PAP secrets file as follows:
+
+- For authenticating the peer, look for a secret with client ==
+username specified in the PAP authenticate-request, and server ==
+local name.
+
+- For authenticating ourselves to the peer, look for a secret with
+client == our username, server == remote name.
+
+When authenticating the peer with PAP, a secret of "" matches any
+password supplied by the peer. If the password doesn't match the
+secret, the password is encrypted using crypt() and checked against
+the secret again; thus secrets for authenticating the peer can be
+stored in encrypted form. If the `login' option was specified, the
+username and password are also checked against the system password
+database. Thus, the system administrator can set up the pap-secrets
+file to allow PPP access only to certain users, and to restrict the
+set of IP addresses that each user can use.
+
+Secrets are selected from the CHAP secrets file as follows:
+
+- For authenticating the peer, look for a secret with client == name
+specified in the CHAP-Response message, and server == local name.
+
+- For authenticating ourselves to the peer, look for a secret with
+client == local name, and server == name specified in the
+CHAP-Challenge message.
+
+Authentication must be satisfactorily completed before IPCP (or any
+other Network Control Protocol) can be started. If authentication
+fails, pppd will terminated the link (by closing LCP). If IPCP
+negotiates an unacceptable IP address for the remote host, IPCP will
+be closed. IP packets cannot be sent or received until IPCP is
+successfully opened.
+
+(some examples needed here perhaps)
+
+
+ROUTING
+
+Setting the addresses on a ppp interface is sufficient to create a
+host route to the remote end of the link. Sometimes it is desirable
+to add a default route through the remote host, as in the case of a
+machine whose only connection to the Internet is through the ppp
+interface. The `defaultroute' option causes pppd to create such a
+default route when IPCP comes up, and delete it when the link is
+terminated.
+
+In some cases it is desirable to use proxy ARP, for example on a
+server machine connected to a LAN, in order to allow other hosts to
+communicate with the remote host. The `proxyarp' option causes pppd
+to look for a network interface (an interface supporting broadcast and
+ARP, which is up and not a point-to-point or loopback interface) on
+the same subnet as the remote host. If found, pppd creates a
+permanent, published ARP entry with the IP address of the remote host
+and the hardware address of the network interface found.
+
+
+OTHER NEW AND CHANGED OPTIONS
+
+ modem Use modem control lines (not fully implemented
+ yet)
+ local Don't use modem control lines
+ persist Keep reopening connection (not fully
+ implemented yet)
+
+ lcp-restart <n> Set timeout for LCP retransmissions to <n>
+ seconds (default 3 seconds)
+ lcp-max-terminate <n> Set maximum number of LCP terminate-request
+ transmissions (default 2)
+ lcp-max-configure <n> Set maximum number of LCP configure-request
+ transmissions (default 10)
+ lcp-max-failure <n> Set maximum number of LCP configure-Naks sent
+ before converting to configure-rejects
+ (default 10)
+
+ ipcp-restart <n> Set timeout for IPCP retransmissions to <n>
+ seconds (default 3 seconds)
+ ipcp-max-terminate <n> Set maximum number of IPCP
+ terminate-request transmissions (default 2)
+ ipcp-max-configure <n> Set maximum number of IPCP
+ configure-request transmissions (default 10)
+ ipcp-max-failure <n> Set maximum number of IPCP configure-Naks
+ sent before converting to configure-rejects
+ (default 10)
+
+ upap-restart <n> Set timeout for PAP retransmissions to
+ <n> seconds (default 3 seconds)
+ upap-max-authreq <n> Set maximum number of Authenticate-request
+ retransmissions (default 10)
+
+ chap-restart <n> Set timeout for CHAP retransmissions to
+ <n> seconds (default 3 seconds)
+ chap-max-challenge <n> Set maximum number of CHAP Challenge
+ retransmissions (default 10)
+ chap-interval <n> Set the interval between CHAP rechallenges
+ (default 0, meaning infinity)
+
+The -ua option no longer exists.
+
+
+SOFTWARE RESTRUCTURING
+
+Many of the source files for pppd have changed significantly from
+ppp-1.3, upon which it is based. In particular:
+
+- the macros for system-dependent operations in pppd.h have mostly
+been removed. Instead these operations are performed by procedures in
+sys-bsd.c (for BSD-4.4ish systems like NetBSD, 386BSD, etc.) or
+sys-str.c (for SunOS-based systems using STREAMS). (I got sick of
+having to recompile everything every time I wanted to change one of
+those horrible macros.)
+
+- most of the system-dependent code in main.c has also been removed to
+sys-bsd.c and sys-str.c.
+
+- the option processing code in main.c has been removed to options.c.
+
+- the authentication code in main.c has been removed to auth.c, which
+also contains substantial amounts of new code.
+
+- fsm.c has changed significantly, and lcp.c, ipcp.c, and upap.c have
+changed somewhat. chap.c has also changed significantly.
+
+
+STILL TO DO
+
+* sort out appropriate modem control and implement the persist option
+properly; add an `answer' option for auto-answering a modem.
+
+* add an inactivity timeout and demand dialing.
+
+* implement link quality monitoring.
+
+* implement other network control protocols.
diff --git a/usr.sbin/pppd/auth.c b/usr.sbin/pppd/auth.c
new file mode 100644
index 0000000..a0062da
--- /dev/null
+++ b/usr.sbin/pppd/auth.c
@@ -0,0 +1,1637 @@
+/*
+ * auth.c - PPP authentication and phase control.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 Australian National University. The name of the University
+ * 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 Carnegie Mellon University. The name of the
+ * University 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <paths.h>
+#include <pwd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <utmp.h>
+#include <fcntl.h>
+#if defined(_PATH_LASTLOG) && defined(_linux_)
+#include <lastlog.h>
+#endif
+
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+#include <utmp.h>
+
+#ifdef USE_PAM
+#include <security/pam_appl.h>
+#endif
+
+#ifdef HAS_SHADOW
+#include <shadow.h>
+#ifndef PW_PPP
+#define PW_PPP PW_LOGIN
+#endif
+#endif
+
+#include "pppd.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ipcp.h"
+#include "upap.h"
+#include "chap.h"
+#ifdef CBCP_SUPPORT
+#include "cbcp.h"
+#endif
+#include "pathnames.h"
+
+/* Used for storing a sequence of words. Usually malloced. */
+struct wordlist {
+ struct wordlist *next;
+ char word[1];
+};
+
+/* Bits in scan_authfile return value */
+#define NONWILD_SERVER 1
+#define NONWILD_CLIENT 2
+
+#define ISWILD(word) (word[0] == '*' && word[1] == 0)
+
+#define FALSE 0
+#define TRUE 1
+
+/* The name by which the peer authenticated itself to us. */
+char peer_authname[MAXNAMELEN];
+
+/* Records which authentication operations haven't completed yet. */
+static int auth_pending[NUM_PPP];
+
+/* Set if we have successfully called plogin() */
+static int logged_in;
+
+/* Set if not wild or blank */
+static int non_wildclient;
+
+/* Set if we have run the /etc/ppp/auth-up script. */
+static int did_authup;
+
+/* List of addresses which the peer may use. */
+static struct wordlist *addresses[NUM_PPP];
+
+/* Number of network protocols which we have opened. */
+static int num_np_open;
+
+/* Number of network protocols which have come up. */
+static int num_np_up;
+
+/* Set if we got the contents of passwd[] from the pap-secrets file. */
+static int passwd_from_file;
+
+/* Bits in auth_pending[] */
+#define PAP_WITHPEER 1
+#define PAP_PEER 2
+#define CHAP_WITHPEER 4
+#define CHAP_PEER 8
+
+extern char *crypt(const char *, const char *);
+
+/* Prototypes for procedures local to this file. */
+
+static void network_phase(int);
+static void check_idle(void *);
+static void connect_time_expired(void *);
+static int plogin(char *, char *, char **, int *);
+static void plogout(void);
+static int null_login(int);
+static int get_pap_passwd(char *);
+static int have_pap_secret(void);
+static int have_chap_secret(char *, char *, u_int32_t);
+static int ip_addr_check(u_int32_t, struct wordlist *);
+static int scan_authfile(FILE *, char *, char *, u_int32_t, char *,
+ struct wordlist **, char *);
+static void free_wordlist(struct wordlist *);
+static void auth_set_ip_addr(int);
+static void auth_script(char *);
+static void set_allowed_addrs(int, struct wordlist *);
+
+/*
+ * An Open on LCP has requested a change from Dead to Establish phase.
+ * Do what's necessary to bring the physical layer up.
+ */
+void
+link_required(unit)
+ int unit;
+{
+}
+
+/*
+ * LCP has terminated the link; go to the Dead phase and take the
+ * physical layer down.
+ */
+void
+link_terminated(unit)
+ int unit;
+{
+ extern time_t etime, stime;
+ extern int minutes;
+
+ if (phase == PHASE_DEAD)
+ return;
+ if (logged_in)
+ plogout();
+ phase = PHASE_DEAD;
+ etime = time((time_t *) NULL);
+ minutes = (etime-stime)/60;
+ syslog(LOG_NOTICE, "Connection terminated, connected for %d minutes\n",
+ minutes > 1 ? minutes : 1);
+}
+
+/*
+ * LCP has gone down; it will either die or try to re-establish.
+ */
+void
+link_down(unit)
+ int unit;
+{
+ int i;
+ struct protent *protp;
+
+ if (did_authup) {
+ auth_script(_PATH_AUTHDOWN);
+ did_authup = 0;
+ }
+ for (i = 0; (protp = protocols[i]) != NULL; ++i) {
+ if (!protp->enabled_flag)
+ continue;
+ if (protp->protocol != PPP_LCP && protp->lowerdown != NULL)
+ (*protp->lowerdown)(unit);
+ if (protp->protocol < 0xC000 && protp->close != NULL)
+ (*protp->close)(unit, "LCP down");
+ }
+ num_np_open = 0;
+ num_np_up = 0;
+ if (phase != PHASE_DEAD)
+ phase = PHASE_TERMINATE;
+}
+
+/*
+ * The link is established.
+ * Proceed to the Dead, Authenticate or Network phase as appropriate.
+ */
+void
+link_established(unit)
+ int unit;
+{
+ int auth;
+ lcp_options *wo = &lcp_wantoptions[unit];
+ lcp_options *go = &lcp_gotoptions[unit];
+ lcp_options *ho = &lcp_hisoptions[unit];
+ int i;
+ struct protent *protp;
+
+ /*
+ * Tell higher-level protocols that LCP is up.
+ */
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->protocol != PPP_LCP && protp->enabled_flag
+ && protp->lowerup != NULL)
+ (*protp->lowerup)(unit);
+
+ if (auth_required && !(go->neg_chap || go->neg_upap)) {
+ /*
+ * We wanted the peer to authenticate itself, and it refused:
+ * treat it as though it authenticated with PAP using a username
+ * of "" and a password of "". If that's not OK, boot it out.
+ */
+ if (!wo->neg_upap || !null_login(unit)) {
+ syslog(LOG_WARNING, "peer refused to authenticate");
+ lcp_close(unit, "peer refused to authenticate");
+ return;
+ }
+ }
+
+ phase = PHASE_AUTHENTICATE;
+ auth = 0;
+ if (go->neg_chap) {
+ ChapAuthPeer(unit, our_name, go->chap_mdtype);
+ auth |= CHAP_PEER;
+ } else if (go->neg_upap) {
+ upap_authpeer(unit);
+ auth |= PAP_PEER;
+ }
+ if (ho->neg_chap) {
+ ChapAuthWithPeer(unit, user, ho->chap_mdtype);
+ auth |= CHAP_WITHPEER;
+ } else if (ho->neg_upap) {
+ if (passwd[0] == 0) {
+ passwd_from_file = 1;
+ if (!get_pap_passwd(passwd))
+ syslog(LOG_ERR, "No secret found for PAP login");
+ }
+ upap_authwithpeer(unit, user, passwd);
+ auth |= PAP_WITHPEER;
+ }
+ auth_pending[unit] = auth;
+
+ if (!auth)
+ network_phase(unit);
+}
+
+/*
+ * Proceed to the network phase.
+ */
+static void
+network_phase(unit)
+ int unit;
+{
+ int i;
+ struct protent *protp;
+ lcp_options *go = &lcp_gotoptions[unit];
+
+ /*
+ * If the peer had to authenticate, run the auth-up script now.
+ */
+ if ((go->neg_chap || go->neg_upap) && !did_authup) {
+ auth_script(_PATH_AUTHUP);
+ did_authup = 1;
+ }
+
+#ifdef CBCP_SUPPORT
+ /*
+ * If we negotiated callback, do it now.
+ */
+ if (go->neg_cbcp) {
+ phase = PHASE_CALLBACK;
+ (*cbcp_protent.open)(unit);
+ return;
+ }
+#endif
+
+ phase = PHASE_NETWORK;
+#if 0
+ if (!demand)
+ set_filters(&pass_filter, &active_filter);
+#endif
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->protocol < 0xC000 && protp->enabled_flag
+ && protp->open != NULL) {
+ (*protp->open)(unit);
+ if (protp->protocol != PPP_CCP)
+ ++num_np_open;
+ }
+
+ if (num_np_open == 0)
+ /* nothing to do */
+ lcp_close(0, "No network protocols running");
+}
+
+/*
+ * The peer has failed to authenticate himself using `protocol'.
+ */
+void
+auth_peer_fail(unit, protocol)
+ int unit, protocol;
+{
+ /*
+ * Authentication failure: take the link down
+ */
+ lcp_close(unit, "Authentication failed");
+}
+
+/*
+ * The peer has been successfully authenticated using `protocol'.
+ */
+void
+auth_peer_success(unit, protocol, name, namelen)
+ int unit, protocol;
+ char *name;
+ int namelen;
+{
+ int bit;
+
+ switch (protocol) {
+ case PPP_CHAP:
+ bit = CHAP_PEER;
+ break;
+ case PPP_PAP:
+ bit = PAP_PEER;
+ break;
+ default:
+ syslog(LOG_WARNING, "auth_peer_success: unknown protocol %x",
+ protocol);
+ return;
+ }
+
+ /*
+ * Save the authenticated name of the peer for later.
+ */
+ if (namelen > sizeof(peer_authname) - 1)
+ namelen = sizeof(peer_authname) - 1;
+ BCOPY(name, peer_authname, namelen);
+ peer_authname[namelen] = 0;
+
+ /*
+ * If we have overridden addresses based on auth info
+ * then set that information now before continuing.
+ */
+ auth_set_ip_addr(unit);
+
+ script_setenv("PEERNAME", peer_authname);
+
+ /*
+ * If there is no more authentication still to be done,
+ * proceed to the network (or callback) phase.
+ */
+ if ((auth_pending[unit] &= ~bit) == 0)
+ network_phase(unit);
+}
+
+/*
+ * We have failed to authenticate ourselves to the peer using `protocol'.
+ */
+void
+auth_withpeer_fail(unit, protocol)
+ int unit, protocol;
+{
+ if (passwd_from_file)
+ BZERO(passwd, MAXSECRETLEN);
+ /*
+ * We've failed to authenticate ourselves to our peer.
+ * He'll probably take the link down, and there's not much
+ * we can do except wait for that.
+ */
+}
+
+/*
+ * We have successfully authenticated ourselves with the peer using `protocol'.
+ */
+void
+auth_withpeer_success(unit, protocol)
+ int unit, protocol;
+{
+ int bit;
+
+ switch (protocol) {
+ case PPP_CHAP:
+ bit = CHAP_WITHPEER;
+ break;
+ case PPP_PAP:
+ if (passwd_from_file)
+ BZERO(passwd, MAXSECRETLEN);
+ bit = PAP_WITHPEER;
+ break;
+ default:
+ syslog(LOG_WARNING, "auth_peer_success: unknown protocol %x",
+ protocol);
+ bit = 0;
+ }
+
+ /*
+ * If we have overridden addresses based on auth info
+ * then set that information now before continuing.
+ */
+ auth_set_ip_addr(unit);
+
+ /*
+ * If there is no more authentication still being done,
+ * proceed to the network (or callback) phase.
+ */
+ if ((auth_pending[unit] &= ~bit) == 0)
+ network_phase(unit);
+}
+
+
+/*
+ * np_up - a network protocol has come up.
+ */
+void
+np_up(unit, proto)
+ int unit, proto;
+{
+ if (num_np_up == 0) {
+ /*
+ * At this point we consider that the link has come up successfully.
+ */
+ need_holdoff = 0;
+
+ if (idle_time_limit > 0)
+ TIMEOUT(check_idle, NULL, idle_time_limit);
+
+ /*
+ * Set a timeout to close the connection once the maximum
+ * connect time has expired.
+ */
+ if (maxconnect > 0)
+ TIMEOUT(connect_time_expired, 0, maxconnect);
+
+ /*
+ * Detach now, if the updetach option was given.
+ */
+ if (nodetach == -1)
+ detach();
+ }
+ ++num_np_up;
+}
+
+/*
+ * np_down - a network protocol has gone down.
+ */
+void
+np_down(unit, proto)
+ int unit, proto;
+{
+ if (--num_np_up == 0 && idle_time_limit > 0) {
+ UNTIMEOUT(check_idle, NULL);
+ }
+}
+
+/*
+ * np_finished - a network protocol has finished using the link.
+ */
+void
+np_finished(unit, proto)
+ int unit, proto;
+{
+ if (--num_np_open <= 0) {
+ /* no further use for the link: shut up shop. */
+ lcp_close(0, "No network protocols running");
+ }
+}
+
+/*
+ * check_idle - check whether the link has been idle for long
+ * enough that we can shut it down.
+ */
+static void
+check_idle(arg)
+ void *arg;
+{
+ struct ppp_idle idle;
+ time_t itime;
+
+ if (!get_idle_time(0, &idle))
+ return;
+ itime = MIN(idle.xmit_idle, idle.recv_idle);
+ if (itime >= idle_time_limit) {
+ /* link is idle: shut it down. */
+ syslog(LOG_INFO, "Terminating connection due to lack of activity.");
+ lcp_close(0, "Link inactive");
+ } else {
+ TIMEOUT(check_idle, NULL, idle_time_limit - itime);
+ }
+}
+
+/*
+ * connect_time_expired - log a message and close the connection.
+ */
+static void
+connect_time_expired(arg)
+ void *arg;
+{
+ syslog(LOG_INFO, "Connect time expired");
+ lcp_close(0, "Connect time expired"); /* Close connection */
+}
+
+/*
+ * auth_check_options - called to check authentication options.
+ */
+void
+auth_check_options()
+{
+ lcp_options *wo = &lcp_wantoptions[0];
+ int can_auth;
+ ipcp_options *ipwo = &ipcp_wantoptions[0];
+ u_int32_t remote;
+
+ /* Default our_name to hostname, and user to our_name */
+ if (our_name[0] == 0 || usehostname)
+ strcpy(our_name, hostname);
+ if (user[0] == 0)
+ strcpy(user, our_name);
+
+ /* If authentication is required, ask peer for CHAP or PAP. */
+ if (auth_required && !wo->neg_chap && !wo->neg_upap) {
+ wo->neg_chap = 1;
+ wo->neg_upap = 1;
+ }
+
+ /*
+ * Check whether we have appropriate secrets to use
+ * to authenticate the peer.
+ */
+ can_auth = wo->neg_upap && (uselogin || have_pap_secret());
+ if (!can_auth && wo->neg_chap) {
+ remote = ipwo->accept_remote? 0: ipwo->hisaddr;
+ can_auth = have_chap_secret(remote_name, our_name, remote);
+ }
+
+ if (auth_required && !can_auth) {
+ option_error("peer authentication required but no suitable secret(s) found\n");
+ if (remote_name[0] == 0)
+ option_error("for authenticating any peer to us (%s)\n", our_name);
+ else
+ option_error("for authenticating peer %s to us (%s)\n",
+ remote_name, our_name);
+ exit(1);
+ }
+
+ /*
+ * Check whether the user tried to override certain values
+ * set by root.
+ */
+ if (!auth_required && auth_req_info.priv > 0) {
+ if (!default_device && devnam_info.priv == 0) {
+ option_error("can't override device name when noauth option used");
+ exit(1);
+ }
+ if ((connector != NULL && connector_info.priv == 0)
+ || (disconnector != NULL && disconnector_info.priv == 0)
+ || (welcomer != NULL && welcomer_info.priv == 0)) {
+ option_error("can't override connect, disconnect or welcome");
+ option_error("option values when noauth option used");
+ exit(1);
+ }
+ }
+}
+
+/*
+ * auth_reset - called when LCP is starting negotiations to recheck
+ * authentication options, i.e. whether we have appropriate secrets
+ * to use for authenticating ourselves and/or the peer.
+ */
+void
+auth_reset(unit)
+ int unit;
+{
+ lcp_options *go = &lcp_gotoptions[unit];
+ lcp_options *ao = &lcp_allowoptions[0];
+ ipcp_options *ipwo = &ipcp_wantoptions[0];
+ u_int32_t remote;
+
+ ao->neg_upap = !refuse_pap && (passwd[0] != 0 || get_pap_passwd(NULL));
+ ao->neg_chap = !refuse_chap
+ && have_chap_secret(user, remote_name, (u_int32_t)0);
+
+ if (go->neg_upap && !uselogin && !have_pap_secret())
+ go->neg_upap = 0;
+ if (go->neg_chap) {
+ remote = ipwo->accept_remote? 0: ipwo->hisaddr;
+ if (!have_chap_secret(remote_name, our_name, remote))
+ go->neg_chap = 0;
+ }
+}
+
+
+/*
+ * check_passwd - Check the user name and passwd against the PAP secrets
+ * file. If requested, also check against the system password database,
+ * and login the user if OK.
+ *
+ * returns:
+ * UPAP_AUTHNAK: Authentication failed.
+ * UPAP_AUTHACK: Authentication succeeded.
+ * In either case, msg points to an appropriate message.
+ */
+int
+check_passwd(unit, auser, userlen, apasswd, passwdlen, msg, msglen)
+ int unit;
+ char *auser;
+ int userlen;
+ char *apasswd;
+ int passwdlen;
+ char **msg;
+ int *msglen;
+{
+ int ret;
+ char *filename;
+ FILE *f;
+ struct wordlist *addrs;
+ u_int32_t remote;
+ ipcp_options *ipwo = &ipcp_wantoptions[unit];
+ char passwd[256], user[256];
+ char secret[MAXWORDLEN];
+ static int attempts = 0;
+ int len;
+
+ /*
+ * Make copies of apasswd and auser, then null-terminate them.
+ */
+ len = MIN(passwdlen, sizeof(passwd) - 1);
+ BCOPY(apasswd, passwd, len);
+ passwd[len] = '\0';
+ len = MIN(userlen, sizeof(user) - 1);
+ BCOPY(auser, user, len);
+ user[len] = '\0';
+ *msg = (char *) 0;
+
+ /*
+ * Open the file of pap secrets and scan for a suitable secret
+ * for authenticating this user.
+ */
+ filename = _PATH_UPAPFILE;
+ addrs = NULL;
+ ret = UPAP_AUTHACK;
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ syslog(LOG_ERR, "Can't open PAP password file %s: %m", filename);
+ ret = UPAP_AUTHNAK;
+
+ } else {
+ check_access(f, filename);
+ remote = ipwo->accept_remote? 0: ipwo->hisaddr;
+ if (scan_authfile(f, user, our_name, remote,
+ secret, &addrs, filename) < 0) {
+ warn("no PAP secret found for %s", user);
+ } else {
+ if (secret[0] != 0) {
+ /* password given in pap-secrets - must match */
+ if ((cryptpap || strcmp(passwd, secret) != 0)
+ && strcmp(crypt(passwd, secret), secret) != 0) {
+ ret = UPAP_AUTHNAK;
+ warn("PAP authentication failure for %s", user);
+ }
+ }
+ }
+ fclose(f);
+ }
+
+ if (uselogin && ret == UPAP_AUTHACK) {
+ ret = plogin(user, passwd, msg, msglen);
+ if (ret == UPAP_AUTHNAK) {
+ syslog(LOG_WARNING, "PAP login failure for %s", user);
+ }
+ }
+
+ if (ret == UPAP_AUTHNAK) {
+ if (*msg == (char *) 0)
+ *msg = "Login incorrect";
+ *msglen = strlen(*msg);
+ /*
+ * Frustrate passwd stealer programs.
+ * Allow 10 tries, but start backing off after 3 (stolen from login).
+ * On 10'th, drop the connection.
+ */
+ if (attempts++ >= 10) {
+ syslog(LOG_WARNING, "%d LOGIN FAILURES ON %s, %s",
+ attempts, devnam, user);
+ quit();
+ }
+ if (attempts > 3)
+ sleep((u_int) (attempts - 3) * 5);
+ if (addrs != NULL)
+ free_wordlist(addrs);
+
+ } else {
+ attempts = 0; /* Reset count */
+ if (*msg == (char *) 0)
+ *msg = "Login ok";
+ *msglen = strlen(*msg);
+ set_allowed_addrs(unit, addrs);
+ }
+
+ BZERO(passwd, sizeof(passwd));
+ BZERO(secret, sizeof(secret));
+
+ return ret;
+}
+
+/*
+ * Check if an "entry" is in the file "fname" - used by ppplogin.
+ * Taken from libexec/ftpd/ftpd.c
+ * Returns: 0 if not found, 1 if found, 2 if file can't be opened for reading.
+ */
+static int
+checkfile(fname, name)
+ char *fname;
+ char *name;
+{
+ FILE *fd;
+ int found = 0;
+ char *p, line[BUFSIZ];
+
+ if ((fd = fopen(fname, "r")) != NULL) {
+ while (fgets(line, sizeof(line), fd) != NULL)
+ if ((p = strchr(line, '\n')) != NULL) {
+ *p = '\0';
+ if (line[0] == '#')
+ continue;
+ if (strcmp(line, name) == 0) {
+ found = 1;
+ break;
+ }
+ }
+ (void) fclose(fd);
+ } else {
+ return(2);
+ }
+ return (found);
+}
+
+/*
+ * This function is needed for PAM.
+ */
+
+#ifdef USE_PAM
+static char *PAM_username = "";
+static char *PAM_password = "";
+
+#ifdef PAM_ESTABLISH_CRED /* new PAM defines :(^ */
+#define MY_PAM_STRERROR(err_code) (char *) pam_strerror(pamh,err_code)
+#else
+#define MY_PAM_STRERROR(err_code) (char *) pam_strerror(err_code)
+#endif
+
+static int pam_conv (int num_msg,
+ const struct pam_message **msg,
+ struct pam_response **resp,
+ void *appdata_ptr)
+{
+ int count = 0, replies = 0;
+ struct pam_response *reply = NULL;
+ int size = 0;
+
+ for (count = 0; count < num_msg; count++)
+ {
+ size += sizeof (struct pam_response);
+ reply = realloc (reply, size); /* ANSI: is malloc() if reply==NULL */
+ if (!reply)
+ return PAM_CONV_ERR;
+
+ switch (msg[count]->msg_style)
+ {
+ case PAM_PROMPT_ECHO_ON:
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies++].resp = strdup(PAM_username); /* never NULL */
+ break;
+
+ case PAM_PROMPT_ECHO_OFF:
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies++].resp = strdup(PAM_password); /* never NULL */
+ break;
+
+ case PAM_TEXT_INFO:
+ reply[replies].resp_retcode = PAM_SUCCESS;
+ reply[replies++].resp = NULL;
+ break;
+
+ case PAM_ERROR_MSG:
+ default:
+ free (reply);
+ return PAM_CONV_ERR;
+ }
+ }
+
+ if (resp)
+ *resp = reply;
+ else
+ free (reply);
+
+ return PAM_SUCCESS;
+}
+#endif
+
+/*
+ * plogin - Check the user name and password against the system
+ * password database, and login the user if OK.
+ *
+ * returns:
+ * UPAP_AUTHNAK: Login failed.
+ * UPAP_AUTHACK: Login succeeded.
+ * In either case, msg points to an appropriate message.
+ *
+ * UPAP_AUTHACK should only be returned *after* wtmp and utmp are updated.
+ */
+
+static int
+plogin(user, passwd, msg, msglen)
+ char *user;
+ char *passwd;
+ char **msg;
+ int *msglen;
+{
+
+#ifdef USE_PAM
+
+ struct pam_conv pam_conversation;
+ pam_handle_t *pamh;
+ int pam_error;
+/*
+ * Fill the pam_conversion structure
+ */
+ memset (&pam_conversation, '\0', sizeof (struct pam_conv));
+ pam_conversation.conv = &pam_conv;
+
+ pam_error = pam_start ("ppp", user, &pam_conversation, &pamh);
+
+ if (pam_error != PAM_SUCCESS) {
+ *msg = MY_PAM_STRERROR (pam_error);
+ return UPAP_AUTHNAK;
+ }
+/*
+ * Define the fields for the credintial validation
+ */
+ (void) pam_set_item (pamh, PAM_TTY, devnam);
+ PAM_username = user;
+ PAM_password = passwd;
+/*
+ * Validate the user
+ */
+ pam_error = pam_authenticate (pamh, PAM_SILENT);
+ if (pam_error == PAM_SUCCESS) {
+ pam_error = pam_acct_mgmt (pamh, PAM_SILENT);
+
+ /* start a session for this user. Session closed when link ends. */
+ if (pam_error == PAM_SUCCESS)
+ (void) pam_open_session (pamh, PAM_SILENT);
+ }
+
+ *msg = MY_PAM_STRERROR (pam_error);
+
+ PAM_username =
+ PAM_password = "";
+/*
+ * Clean up the mess
+ */
+ (void) pam_end (pamh, pam_error);
+
+ if (pam_error != PAM_SUCCESS)
+ return UPAP_AUTHNAK;
+/*
+ * Use the non-PAM methods directly
+ */
+#else /* #ifdef USE_PAM */
+
+ struct passwd *pw;
+ struct utmp utmp;
+ struct timeval tp;
+ char *tty;
+
+#ifdef HAS_SHADOW
+ struct spwd *spwd;
+ struct spwd *getspnam();
+#endif
+
+ pw = getpwnam(user);
+ endpwent();
+ if (pw == NULL) {
+ return (UPAP_AUTHNAK);
+ }
+/*
+ * Check that the user is not listed in /etc/ppp/ppp.deny
+ * and that the user's shell is listed in /etc/ppp/ppp.shells
+ * if /etc/ppp/ppp.shells exists.
+ */
+
+ if (checkfile(_PATH_PPPDENY, user) == 1) {
+ syslog(LOG_WARNING, "upap user %s: login denied in %s",
+ user, _PATH_PPPDENY);
+ return (UPAP_AUTHNAK);
+ }
+
+ if (checkfile(_PATH_PPPSHELLS, pw->pw_shell) == 0) {
+ syslog(LOG_WARNING, "upap user %s: shell %s not in %s",
+ user, pw->pw_shell, _PATH_PPPSHELLS);
+ return (UPAP_AUTHNAK);
+ }
+
+#ifdef HAS_SHADOW
+ spwd = getspnam(user);
+ endspent();
+ if (spwd) {
+ /* check the age of the password entry */
+ long now = time(NULL) / 86400L;
+
+ if ((spwd->sp_expire > 0 && now >= spwd->sp_expire)
+ || ((spwd->sp_max >= 0 && spwd->sp_max < 10000)
+ && spwd->sp_lstchg >= 0
+ && now >= spwd->sp_lstchg + spwd->sp_max)) {
+ syslog(LOG_WARNING, "Password for %s has expired", user);
+ return (UPAP_AUTHNAK);
+ }
+ pw->pw_passwd = spwd->sp_pwdp;
+ }
+#endif
+
+ /*
+ * If no passwd, don't let them login.
+ */
+ if (pw->pw_passwd == NULL || *pw->pw_passwd == '\0'
+ || strcmp(crypt(passwd, pw->pw_passwd), pw->pw_passwd) != 0)
+ return (UPAP_AUTHNAK);
+
+ if (pw->pw_expire) {
+ (void)gettimeofday(&tp, (struct timezone *)NULL);
+ if (tp.tv_sec >= pw->pw_expire) {
+ syslog(LOG_INFO, "pap user %s account expired", user);
+ return (UPAP_AUTHNAK);
+ }
+ }
+
+ /* These functions are not enabled for PAM. The reason for this is that */
+ /* there is not necessarily a "passwd" entry for this user. That is */
+ /* real purpose of 'PAM' -- to virtualize the account data from the */
+ /* application. If you want to do the same thing, write the entry in */
+ /* the 'session' hook. */
+
+ /* Log in wtmp and utmp using login() */
+
+ tty = devnam;
+ if (strncmp(tty, _PATH_DEV, sizeof _PATH_DEV - 1) == 0)
+ tty += 5;
+
+ if (logout(tty)) /* Already entered (by login?) */
+ logwtmp(tty, "", "");
+
+#if defined(_PATH_LASTLOG)
+ {
+ struct lastlog ll;
+ int fd;
+
+ if ((fd = open(_PATH_LASTLOG, O_RDWR, 0)) >= 0) {
+ (void)lseek(fd, (off_t)(pw->pw_uid * sizeof(ll)), SEEK_SET);
+ memset((void *)&ll, 0, sizeof(ll));
+ ll.ll_time = _time_to_time32(time(0));
+ (void)strncpy(ll.ll_line, tty, sizeof(ll.ll_line));
+ (void)write(fd, (char *)&ll, sizeof(ll));
+ (void)close(fd);
+ }
+ }
+#endif
+
+ memset((void *)&utmp, 0, sizeof(utmp));
+ utmp.ut_time = time(NULL);
+ (void)strncpy(utmp.ut_name, user, sizeof(utmp.ut_name));
+ (void)strncpy(utmp.ut_host, ":PPP", sizeof(utmp.ut_host));
+ (void)strncpy(utmp.ut_line, tty, sizeof(utmp.ut_line));
+ login(&utmp); /* This logs us in wtmp too */
+
+#endif /* #ifdef USE_PAM */
+
+ syslog(LOG_INFO, "user %s logged in", user);
+ logged_in = TRUE;
+
+ return (UPAP_AUTHACK);
+}
+
+/*
+ * plogout - Logout the user.
+ */
+static void
+plogout()
+{
+#ifdef USE_PAM
+ struct pam_conv pam_conversation;
+ pam_handle_t *pamh;
+ int pam_error;
+/*
+ * Fill the pam_conversion structure. The PAM specification states that the
+ * session must be able to be closed by a totally different handle from which
+ * it was created. Hold the PAM group to their own specification!
+ */
+ memset (&pam_conversation, '\0', sizeof (struct pam_conv));
+ pam_conversation.conv = &pam_conv;
+
+ pam_error = pam_start ("ppp", user, &pam_conversation, &pamh);
+ if (pam_error == PAM_SUCCESS) {
+ (void) pam_set_item (pamh, PAM_TTY, devnam);
+ (void) pam_close_session (pamh, PAM_SILENT);
+ (void) pam_end (pamh, PAM_SUCCESS);
+ }
+
+#else
+ char *tty;
+
+ tty = devnam;
+ if (strncmp(tty, _PATH_DEV, sizeof _PATH_DEV - 1) == 0)
+ tty += 5;
+ logwtmp(tty, "", ""); /* Wipe out wtmp logout entry */
+ logout(tty); /* Wipe out utmp */
+#endif
+
+ logged_in = FALSE;
+}
+
+
+/*
+ * null_login - Check if a username of "" and a password of "" are
+ * acceptable, and iff so, set the list of acceptable IP addresses
+ * and return 1.
+ */
+static int
+null_login(unit)
+ int unit;
+{
+ char *filename;
+ FILE *f;
+ int i, ret;
+ struct wordlist *addrs;
+ char secret[MAXWORDLEN];
+
+ /*
+ * Open the file of pap secrets and scan for a suitable secret.
+ * We don't accept a wildcard client.
+ */
+ filename = _PATH_UPAPFILE;
+ addrs = NULL;
+ f = fopen(filename, "r");
+ if (f == NULL)
+ return 0;
+ check_access(f, filename);
+
+ i = scan_authfile(f, "", our_name, (u_int32_t)0, secret, &addrs, filename);
+ ret = i >= 0 && (i & NONWILD_CLIENT) != 0 && secret[0] == 0;
+ BZERO(secret, sizeof(secret));
+
+ if (ret)
+ set_allowed_addrs(unit, addrs);
+ else
+ free_wordlist(addrs);
+
+ fclose(f);
+ return ret;
+}
+
+
+/*
+ * get_pap_passwd - get a password for authenticating ourselves with
+ * our peer using PAP. Returns 1 on success, 0 if no suitable password
+ * could be found.
+ */
+static int
+get_pap_passwd(passwd)
+ char *passwd;
+{
+ char *filename;
+ FILE *f;
+ int ret;
+ struct wordlist *addrs;
+ char secret[MAXWORDLEN];
+
+ filename = _PATH_UPAPFILE;
+ addrs = NULL;
+ f = fopen(filename, "r");
+ if (f == NULL)
+ return 0;
+ check_access(f, filename);
+ ret = scan_authfile(f, user,
+ remote_name[0]? remote_name: NULL,
+ (u_int32_t)0, secret, NULL, filename);
+ fclose(f);
+ if (ret < 0)
+ return 0;
+ if (passwd != NULL) {
+ strncpy(passwd, secret, MAXSECRETLEN);
+ passwd[MAXSECRETLEN-1] = 0;
+ }
+ BZERO(secret, sizeof(secret));
+ return 1;
+}
+
+
+/*
+ * have_pap_secret - check whether we have a PAP file with any
+ * secrets that we could possibly use for authenticating the peer.
+ */
+static int
+have_pap_secret()
+{
+ FILE *f;
+ int ret;
+ char *filename;
+ ipcp_options *ipwo = &ipcp_wantoptions[0];
+ u_int32_t remote;
+
+ filename = _PATH_UPAPFILE;
+ f = fopen(filename, "r");
+ if (f == NULL)
+ return 0;
+
+ remote = ipwo->accept_remote? 0: ipwo->hisaddr;
+ ret = scan_authfile(f, NULL, our_name, remote, NULL, NULL, filename);
+ fclose(f);
+ if (ret < 0)
+ return 0;
+
+ return 1;
+}
+
+
+/*
+ * have_chap_secret - check whether we have a CHAP file with a
+ * secret that we could possibly use for authenticating `client'
+ * on `server'. Either can be the null string, meaning we don't
+ * know the identity yet.
+ */
+static int
+have_chap_secret(client, server, remote)
+ char *client;
+ char *server;
+ u_int32_t remote;
+{
+ FILE *f;
+ int ret;
+ char *filename;
+
+ filename = _PATH_CHAPFILE;
+ f = fopen(filename, "r");
+ if (f == NULL)
+ return 0;
+
+ if (client[0] == 0)
+ client = NULL;
+ else if (server[0] == 0)
+ server = NULL;
+
+ ret = scan_authfile(f, client, server, remote, NULL, NULL, filename);
+ fclose(f);
+ if (ret < 0)
+ return 0;
+
+ return 1;
+}
+
+
+/*
+ * get_secret - open the CHAP secret file and return the secret
+ * for authenticating the given client on the given server.
+ * (We could be either client or server).
+ */
+int
+get_secret(unit, client, server, secret, secret_len, save_addrs)
+ int unit;
+ char *client;
+ char *server;
+ char *secret;
+ int *secret_len;
+ int save_addrs;
+{
+ FILE *f;
+ int ret, len;
+ char *filename;
+ struct wordlist *addrs;
+ char secbuf[MAXWORDLEN];
+
+ filename = _PATH_CHAPFILE;
+ addrs = NULL;
+ secbuf[0] = 0;
+
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ syslog(LOG_ERR, "Can't open chap secret file %s: %m", filename);
+ return 0;
+ }
+ check_access(f, filename);
+
+ ret = scan_authfile(f, client, server, (u_int32_t)0,
+ secbuf, &addrs, filename);
+ fclose(f);
+ if (ret < 0)
+ return 0;
+
+ if (save_addrs)
+ set_allowed_addrs(unit, addrs);
+
+ len = strlen(secbuf);
+ if (len > MAXSECRETLEN) {
+ syslog(LOG_ERR, "Secret for %s on %s is too long", client, server);
+ len = MAXSECRETLEN;
+ }
+ BCOPY(secbuf, secret, len);
+ BZERO(secbuf, sizeof(secbuf));
+ *secret_len = len;
+
+ return 1;
+}
+
+/*
+ * set_allowed_addrs() - set the list of allowed addresses.
+ */
+static void
+set_allowed_addrs(unit, addrs)
+ int unit;
+ struct wordlist *addrs;
+{
+ if (addresses[unit] != NULL)
+ free_wordlist(addresses[unit]);
+ addresses[unit] = addrs;
+
+ /*
+ * If there's only one authorized address we might as well
+ * ask our peer for that one right away
+ */
+ if (addrs != NULL && addrs->next == NULL) {
+ char *p = addrs->word;
+ struct ipcp_options *wo = &ipcp_wantoptions[unit];
+ u_int32_t a;
+ struct hostent *hp;
+
+ if (*p != '!' && *p != '-' && strchr(p, '/') == NULL) {
+ hp = gethostbyname(p);
+ if (hp != NULL && hp->h_addrtype == AF_INET)
+ a = *(u_int32_t *)hp->h_addr;
+ else
+ a = inet_addr(p);
+ if (a != (u_int32_t) -1)
+ wo->hisaddr = a;
+ }
+ }
+}
+
+static void
+auth_set_ip_addr(unit)
+ int unit;
+{
+ struct wordlist *addrs;
+
+ if (non_wildclient && (addrs = addresses[unit]) != NULL) {
+ for (; addrs != NULL; addrs = addrs->next) {
+ /* Look for address overrides, and set them if we have any */
+ if (strchr(addrs->word, ':') != NULL) {
+ if (setipaddr(addrs->word))
+ break;
+ }
+ }
+ }
+}
+
+/*
+ * auth_ip_addr - check whether the peer is authorized to use
+ * a given IP address. Returns 1 if authorized, 0 otherwise.
+ */
+int
+auth_ip_addr(unit, addr)
+ int unit;
+ u_int32_t addr;
+{
+ return ip_addr_check(addr, addresses[unit]);
+}
+
+static int
+ip_addr_check(addr, addrs)
+ u_int32_t addr;
+ struct wordlist *addrs;
+{
+ int x, y;
+ u_int32_t a, mask, ah;
+ int accept;
+ char *ptr_word, *ptr_mask;
+ struct hostent *hp;
+ struct netent *np;
+
+ /* don't allow loopback or multicast address */
+ if (bad_ip_adrs(addr))
+ return 0;
+
+ if (addrs == NULL)
+ return !auth_required; /* no addresses authorized */
+
+ x = y = 0;
+ for (; addrs != NULL; addrs = addrs->next) {
+ y++;
+ /* "-" means no addresses authorized, "*" means any address allowed */
+ ptr_word = addrs->word;
+ if (strcmp(ptr_word, "-") == 0)
+ break;
+ if (strcmp(ptr_word, "*") == 0)
+ return 1;
+
+ /*
+ * A colon in the string means that we wish to force a specific
+ * local:remote address, but we ignore these for now.
+ */
+ if (strchr(addrs->word, ':') != NULL)
+ x++;
+ else {
+
+ accept = 1;
+ if (*ptr_word == '!') {
+ accept = 0;
+ ++ptr_word;
+ }
+
+ mask = ~ (u_int32_t) 0;
+ ptr_mask = strchr (ptr_word, '/');
+ if (ptr_mask != NULL) {
+ int bit_count;
+
+ bit_count = (int) strtol (ptr_mask+1, (char **) 0, 10);
+ if (bit_count <= 0 || bit_count > 32) {
+ syslog (LOG_WARNING,
+ "invalid address length %s in auth. address list",
+ ptr_mask);
+ continue;
+ }
+ *ptr_mask = '\0';
+ mask <<= 32 - bit_count;
+ }
+
+ hp = gethostbyname(ptr_word);
+ if (hp != NULL && hp->h_addrtype == AF_INET) {
+ a = *(u_int32_t *)hp->h_addr;
+ } else {
+ np = getnetbyname (ptr_word);
+ if (np != NULL && np->n_addrtype == AF_INET) {
+ a = htonl (*(u_int32_t *)np->n_net);
+ if (ptr_mask == NULL) {
+ /* calculate appropriate mask for net */
+ ah = ntohl(a);
+ if (IN_CLASSA(ah))
+ mask = IN_CLASSA_NET;
+ else if (IN_CLASSB(ah))
+ mask = IN_CLASSB_NET;
+ else if (IN_CLASSC(ah))
+ mask = IN_CLASSC_NET;
+ }
+ } else {
+ a = inet_addr (ptr_word);
+ }
+ }
+
+ if (ptr_mask != NULL)
+ *ptr_mask = '/';
+
+ if (a == (u_int32_t)-1L)
+ syslog (LOG_WARNING,
+ "unknown host %s in auth. address list",
+ addrs->word);
+ else
+ /* Here a and addr are in network byte order,
+ and mask is in host order. */
+ if (((addr ^ a) & htonl(mask)) == 0)
+ return accept;
+ } /* else */
+ }
+ return x == y; /* not in list => can't have it */
+}
+
+/*
+ * bad_ip_adrs - return 1 if the IP address is one we don't want
+ * to use, such as an address in the loopback net or a multicast address.
+ * addr is in network byte order.
+ */
+int
+bad_ip_adrs(addr)
+ u_int32_t addr;
+{
+ addr = ntohl(addr);
+ return (addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET
+ || IN_MULTICAST(addr) || IN_BADCLASS(addr);
+}
+
+/*
+ * check_access - complain if a secret file has too-liberal permissions.
+ */
+void
+check_access(f, filename)
+ FILE *f;
+ char *filename;
+{
+ struct stat sbuf;
+
+ if (fstat(fileno(f), &sbuf) < 0) {
+ syslog(LOG_WARNING, "cannot stat secret file %s: %m", filename);
+ } else if ((sbuf.st_mode & (S_IRWXG | S_IRWXO)) != 0) {
+ syslog(LOG_WARNING, "Warning - secret file %s has world and/or group access", filename);
+ }
+}
+
+
+/*
+ * scan_authfile - Scan an authorization file for a secret suitable
+ * for authenticating `client' on `server'. The return value is -1
+ * if no secret is found, otherwise >= 0. The return value has
+ * NONWILD_CLIENT set if the secret didn't have "*" for the client, and
+ * NONWILD_SERVER set if the secret didn't have "*" for the server.
+ * Any following words on the line (i.e. address authorization
+ * info) are placed in a wordlist and returned in *addrs.
+ */
+static int
+scan_authfile(f, client, server, ipaddr, secret, addrs, filename)
+ FILE *f;
+ char *client;
+ char *server;
+ u_int32_t ipaddr;
+ char *secret;
+ struct wordlist **addrs;
+ char *filename;
+{
+ int newline, xxx;
+ int got_flag, best_flag;
+ FILE *sf;
+ struct wordlist *ap, *addr_list, *alist, *alast;
+ char word[MAXWORDLEN];
+ char atfile[MAXWORDLEN];
+ char lsecret[MAXWORDLEN];
+
+ if (addrs != NULL)
+ *addrs = NULL;
+ addr_list = NULL;
+ if (!getword(f, word, &newline, filename))
+ return -1; /* file is empty??? */
+ newline = 1;
+ best_flag = -1;
+ for (;;) {
+ /*
+ * Skip until we find a word at the start of a line.
+ */
+ while (!newline && getword(f, word, &newline, filename))
+ ;
+ if (!newline)
+ break; /* got to end of file */
+
+ /*
+ * Got a client - check if it's a match or a wildcard.
+ */
+ got_flag = 0;
+ if (client != NULL && strcmp(word, client) != 0 && !ISWILD(word)) {
+ newline = 0;
+ continue;
+ }
+ if (!ISWILD(word))
+ got_flag = NONWILD_CLIENT;
+
+ /*
+ * Now get a server and check if it matches.
+ */
+ if (!getword(f, word, &newline, filename))
+ break;
+ if (newline)
+ continue;
+ if (server != NULL && strcmp(word, server) != 0 && !ISWILD(word))
+ continue;
+ if (!ISWILD(word))
+ got_flag |= NONWILD_SERVER;
+
+ /*
+ * Got some sort of a match - see if it's better than what
+ * we have already.
+ */
+ if (got_flag <= best_flag)
+ continue;
+
+ /*
+ * Get the secret.
+ */
+ if (!getword(f, word, &newline, filename))
+ break;
+ if (newline)
+ continue;
+
+ /*
+ * Special syntax: @filename means read secret from file.
+ */
+ if (word[0] == '@') {
+ strcpy(atfile, word+1);
+ if ((sf = fopen(atfile, "r")) == NULL) {
+ syslog(LOG_WARNING, "can't open indirect secret file %s",
+ atfile);
+ continue;
+ }
+ check_access(sf, atfile);
+ if (!getword(sf, word, &xxx, atfile)) {
+ syslog(LOG_WARNING, "no secret in indirect secret file %s",
+ atfile);
+ fclose(sf);
+ continue;
+ }
+ fclose(sf);
+ }
+ if (secret != NULL)
+ strcpy(lsecret, word);
+
+ /*
+ * Now read address authorization info and make a wordlist.
+ */
+ alist = alast = NULL;
+ for (;;) {
+ if (!getword(f, word, &newline, filename) || newline)
+ break;
+ ap = (struct wordlist *) malloc(sizeof(struct wordlist)
+ + strlen(word));
+ if (ap == NULL)
+ novm("authorized addresses");
+ ap->next = NULL;
+ strcpy(ap->word, word);
+ if (alist == NULL)
+ alist = ap;
+ else
+ alast->next = ap;
+ alast = ap;
+ }
+
+ /*
+ * Check if the given IP address is allowed by the wordlist.
+ */
+ if (ipaddr != 0 && !ip_addr_check(ipaddr, alist)) {
+ free_wordlist(alist);
+ continue;
+ }
+
+ /*
+ * This is the best so far; remember it.
+ */
+ best_flag = got_flag;
+ if (addr_list)
+ free_wordlist(addr_list);
+ addr_list = alist;
+ if (secret != NULL)
+ strcpy(secret, lsecret);
+
+ if (!newline)
+ break;
+ }
+
+ if (addrs != NULL)
+ *addrs = addr_list;
+ else if (addr_list != NULL)
+ free_wordlist(addr_list);
+
+ non_wildclient = (best_flag & NONWILD_CLIENT) && client != NULL &&
+ *client != '\0';
+ return best_flag;
+}
+
+/*
+ * free_wordlist - release memory allocated for a wordlist.
+ */
+static void
+free_wordlist(wp)
+ struct wordlist *wp;
+{
+ struct wordlist *next;
+
+ while (wp != NULL) {
+ next = wp->next;
+ free(wp);
+ wp = next;
+ }
+}
+
+/*
+ * auth_script - execute a script with arguments
+ * interface-name peer-name real-user tty speed
+ */
+static void
+auth_script(script)
+ char *script;
+{
+ char strspeed[32];
+ struct passwd *pw;
+ char struid[32];
+ char *user_name;
+ char *argv[7];
+
+ if ((pw = getpwuid(getuid())) != NULL && pw->pw_name != NULL)
+ user_name = pw->pw_name;
+ else {
+ sprintf(struid, "%d", getuid());
+ user_name = struid;
+ }
+ sprintf(strspeed, "%d", baud_rate);
+
+ argv[0] = script;
+ argv[1] = ifname;
+ argv[2] = peer_authname;
+ argv[3] = user_name;
+ argv[4] = devnam;
+ argv[5] = strspeed;
+ argv[6] = NULL;
+
+ run_program(script, argv, 0);
+}
diff --git a/usr.sbin/pppd/cbcp.c b/usr.sbin/pppd/cbcp.c
new file mode 100644
index 0000000..7bcfcab
--- /dev/null
+++ b/usr.sbin/pppd/cbcp.c
@@ -0,0 +1,411 @@
+/*
+ * cbcp - Call Back Configuration Protocol.
+ *
+ * Copyright (c) 1995 Pedro Roque Marques
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 Pedro Roque Marques. 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 WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <syslog.h>
+
+#include "pppd.h"
+#include "cbcp.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ipcp.h"
+
+/*
+ * Protocol entry points.
+ */
+static void cbcp_init(int unit);
+static void cbcp_open(int unit);
+static void cbcp_lowerup(int unit);
+static void cbcp_input(int unit, u_char *pkt, int len);
+static void cbcp_protrej(int unit);
+static int cbcp_printpkt(u_char *pkt, int len,
+ void (*printer)(void *, char *, ...),
+ void *arg);
+
+struct protent cbcp_protent = {
+ PPP_CBCP,
+ cbcp_init,
+ cbcp_input,
+ cbcp_protrej,
+ cbcp_lowerup,
+ NULL,
+ cbcp_open,
+ NULL,
+ cbcp_printpkt,
+ NULL,
+ 0,
+ "CBCP",
+ NULL,
+ NULL,
+ NULL
+};
+
+cbcp_state cbcp[NUM_PPP];
+
+/* internal prototypes */
+
+static void cbcp_recvreq(cbcp_state *us, char *pckt, int len);
+static void cbcp_resp(cbcp_state *us);
+static void cbcp_up(cbcp_state *us);
+static void cbcp_recvack(cbcp_state *us, char *pckt, int len);
+static void cbcp_send(cbcp_state *us, u_char code, u_char *buf, int len);
+
+/* init state */
+static void
+cbcp_init(int iface)
+{
+ cbcp_state *us;
+
+ us = &cbcp[iface];
+ memset(us, 0, sizeof(cbcp_state));
+ us->us_unit = iface;
+ us->us_type |= (1 << CB_CONF_NO);
+}
+
+/* lower layer is up */
+static void
+cbcp_lowerup(int iface)
+{
+ cbcp_state *us = &cbcp[iface];
+
+ syslog(LOG_DEBUG, "cbcp_lowerup");
+ syslog(LOG_DEBUG, "want: %d", us->us_type);
+
+ if (us->us_type == CB_CONF_USER)
+ syslog(LOG_DEBUG, "phone no: %s", us->us_number);
+}
+
+static void
+cbcp_open(int unit)
+{
+ syslog(LOG_DEBUG, "cbcp_open");
+}
+
+/* process an incomming packet */
+static void
+cbcp_input(int unit, u_char *inpacket, int pktlen)
+{
+ u_char *inp;
+ u_char code, id;
+ u_short len;
+
+ cbcp_state *us = &cbcp[unit];
+
+ inp = inpacket;
+
+ if (pktlen < CBCP_MINLEN) {
+ syslog(LOG_ERR, "CBCP packet is too small");
+ return;
+ }
+
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+
+ if (len < CBCP_MINLEN || len > pktlen) {
+ syslog(LOG_ERR, "CBCP packet: invalid length");
+ return;
+ }
+
+ len -= CBCP_MINLEN;
+
+ switch(code) {
+ case CBCP_REQ:
+ us->us_id = id;
+ cbcp_recvreq(us, inp, len);
+ break;
+
+ case CBCP_RESP:
+ syslog(LOG_DEBUG, "CBCP_RESP received");
+ break;
+
+ case CBCP_ACK:
+ if (id != us->us_id)
+ syslog(LOG_DEBUG, "id doesn't match: expected %d recv %d",
+ us->us_id, id);
+
+ cbcp_recvack(us, inp, len);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* protocol was rejected by foe */
+void cbcp_protrej(int iface)
+{
+}
+
+char *cbcp_codenames[] = {
+ "Request", "Response", "Ack"
+};
+
+char *cbcp_optionnames[] = {
+ "NoCallback",
+ "UserDefined",
+ "AdminDefined",
+ "List"
+};
+
+/* pretty print a packet */
+static int
+cbcp_printpkt(u_char *p, int plen, void (*printer)(void *, char *, ...),
+ void *arg)
+{
+ int code, opt, id, len, olen, delay;
+ u_char *pstart;
+
+ if (plen < HEADERLEN)
+ return 0;
+ pstart = p;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < HEADERLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= sizeof(cbcp_codenames) / sizeof(char *))
+ printer(arg, " %s", cbcp_codenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+
+ printer(arg, " id=0x%x", id);
+ len -= HEADERLEN;
+
+ switch (code) {
+ case CBCP_REQ:
+ case CBCP_RESP:
+ case CBCP_ACK:
+ while(len >= 2) {
+ GETCHAR(opt, p);
+ GETCHAR(olen, p);
+
+ if (olen < 2 || olen > len) {
+ break;
+ }
+
+ printer(arg, " <");
+ len -= olen;
+
+ if (opt >= 1 && opt <= sizeof(cbcp_optionnames) / sizeof(char *))
+ printer(arg, " %s", cbcp_optionnames[opt-1]);
+ else
+ printer(arg, " option=0x%x", opt);
+
+ if (olen > 2) {
+ GETCHAR(delay, p);
+ printer(arg, " delay = %d", delay);
+ }
+
+ if (olen > 3) {
+ int addrt;
+ char str[256];
+
+ GETCHAR(addrt, p);
+ memcpy(str, p, olen - 4);
+ str[olen - 4] = 0;
+ printer(arg, " number = %s", str);
+ }
+ printer(arg, ">");
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ for (; len > 0; --len) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+
+ return p - pstart;
+}
+
+/* received CBCP request */
+static void
+cbcp_recvreq(cbcp_state *us, char *pckt, int pcktlen)
+{
+ u_char type, opt_len, delay, addr_type;
+ char address[256];
+ int len = pcktlen;
+
+ address[0] = 0;
+
+ while (len > 1) {
+ syslog(LOG_DEBUG, "length: %d", len);
+
+ GETCHAR(type, pckt);
+ GETCHAR(opt_len, pckt);
+
+ if (len < opt_len)
+ break;
+ len -= opt_len;
+
+ if (opt_len > 2)
+ GETCHAR(delay, pckt);
+
+ us->us_allowed |= (1 << type);
+
+ switch(type) {
+ case CB_CONF_NO:
+ syslog(LOG_DEBUG, "no callback allowed");
+ break;
+
+ case CB_CONF_USER:
+ syslog(LOG_DEBUG, "user callback allowed");
+ if (opt_len > 4) {
+ GETCHAR(addr_type, pckt);
+ memcpy(address, pckt, opt_len - 4);
+ address[opt_len - 4] = 0;
+ if (address[0])
+ syslog(LOG_DEBUG, "address: %s", address);
+ }
+ break;
+
+ case CB_CONF_ADMIN:
+ syslog(LOG_DEBUG, "user admin defined allowed");
+ break;
+
+ case CB_CONF_LIST:
+ break;
+ }
+ }
+
+ cbcp_resp(us);
+}
+
+static void
+cbcp_resp(cbcp_state *us)
+{
+ u_char cb_type;
+ u_char buf[256];
+ u_char *bufp = buf;
+ int len = 0;
+
+ cb_type = us->us_allowed & us->us_type;
+ syslog(LOG_DEBUG, "cbcp_resp cb_type=%d", cb_type);
+
+#if 0
+ if (!cb_type)
+ lcp_down(us->us_unit);
+#endif
+
+ if (cb_type & ( 1 << CB_CONF_USER ) ) {
+ syslog(LOG_DEBUG, "cbcp_resp CONF_USER");
+ PUTCHAR(CB_CONF_USER, bufp);
+ len = 3 + 1 + strlen(us->us_number) + 1;
+ PUTCHAR(len , bufp);
+ PUTCHAR(5, bufp); /* delay */
+ PUTCHAR(1, bufp);
+ BCOPY(us->us_number, bufp, strlen(us->us_number) + 1);
+ cbcp_send(us, CBCP_RESP, buf, len);
+ return;
+ }
+
+ if (cb_type & ( 1 << CB_CONF_ADMIN ) ) {
+ syslog(LOG_DEBUG, "cbcp_resp CONF_ADMIN");
+ PUTCHAR(CB_CONF_ADMIN, bufp);
+ len = 3;
+ PUTCHAR(len, bufp);
+ PUTCHAR(5, bufp); /* delay */
+ cbcp_send(us, CBCP_RESP, buf, len);
+ return;
+ }
+
+ if (cb_type & ( 1 << CB_CONF_NO ) ) {
+ syslog(LOG_DEBUG, "cbcp_resp CONF_NO");
+ PUTCHAR(CB_CONF_NO, bufp);
+ len = 2;
+ PUTCHAR(len , bufp);
+ cbcp_send(us, CBCP_RESP, buf, len);
+ (*ipcp_protent.open)(us->us_unit);
+ return;
+ }
+}
+
+static void
+cbcp_send(cbcp_state *us, u_char code, u_char *buf, int len)
+{
+ u_char *outp;
+ int outlen;
+
+ outp = outpacket_buf;
+
+ outlen = 4 + len;
+
+ MAKEHEADER(outp, PPP_CBCP);
+
+ PUTCHAR(code, outp);
+ PUTCHAR(us->us_id, outp);
+ PUTSHORT(outlen, outp);
+
+ if (len)
+ BCOPY(buf, outp, len);
+
+ output(us->us_unit, outpacket_buf, outlen + PPP_HDRLEN);
+}
+
+static void
+cbcp_recvack(cbcp_state *us, char *pckt, int len)
+{
+ u_char type, delay, addr_type;
+ int opt_len;
+ char address[256];
+
+ if (len > 1) {
+ GETCHAR(type, pckt);
+ GETCHAR(opt_len, pckt);
+
+ if (opt_len > len)
+ return;
+
+ if (opt_len > 2)
+ GETCHAR(delay, pckt);
+
+ if (opt_len > 4) {
+ GETCHAR(addr_type, pckt);
+ memcpy(address, pckt, opt_len - 4);
+ address[opt_len - 4] = 0;
+ if (address[0])
+ syslog(LOG_DEBUG, "peer will call: %s", address);
+ }
+ }
+
+ cbcp_up(us);
+}
+
+extern int persist;
+
+/* ok peer will do callback */
+static void
+cbcp_up(cbcp_state *us)
+{
+ persist = 0;
+ lcp_close(0, "Call me back, please");
+}
diff --git a/usr.sbin/pppd/cbcp.h b/usr.sbin/pppd/cbcp.h
new file mode 100644
index 0000000..c2ab3f6
--- /dev/null
+++ b/usr.sbin/pppd/cbcp.h
@@ -0,0 +1,26 @@
+#ifndef CBCP_H
+#define CBCP_H
+
+typedef struct cbcp_state {
+ int us_unit; /* Interface unit number */
+ u_char us_id; /* Current id */
+ u_char us_allowed;
+ int us_type;
+ char *us_number; /* Telefone Number */
+} cbcp_state;
+
+extern cbcp_state cbcp[];
+
+extern struct protent cbcp_protent;
+
+#define CBCP_MINLEN 4
+
+#define CBCP_REQ 1
+#define CBCP_RESP 2
+#define CBCP_ACK 3
+
+#define CB_CONF_NO 1
+#define CB_CONF_USER 2
+#define CB_CONF_ADMIN 3
+#define CB_CONF_LIST 4
+#endif
diff --git a/usr.sbin/pppd/ccp.c b/usr.sbin/pppd/ccp.c
new file mode 100644
index 0000000..ba88135
--- /dev/null
+++ b/usr.sbin/pppd/ccp.c
@@ -0,0 +1,1113 @@
+/*
+ * ccp.c - PPP Compression Control Protocol.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ */
+
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+#include <string.h>
+#include <syslog.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "ccp.h"
+#include <net/ppp_comp.h>
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ccp_init(int unit);
+static void ccp_open(int unit);
+static void ccp_close(int unit, char *);
+static void ccp_lowerup(int unit);
+static void ccp_lowerdown(int);
+static void ccp_input(int unit, u_char *pkt, int len);
+static void ccp_protrej(int unit);
+static int ccp_printpkt(u_char *pkt, int len,
+ void (*printer)(void *, char *, ...),
+ void *arg);
+static void ccp_datainput(int unit, u_char *pkt, int len);
+
+struct protent ccp_protent = {
+ PPP_CCP,
+ ccp_init,
+ ccp_input,
+ ccp_protrej,
+ ccp_lowerup,
+ ccp_lowerdown,
+ ccp_open,
+ ccp_close,
+ ccp_printpkt,
+ ccp_datainput,
+ 1,
+ "CCP",
+ NULL,
+ NULL,
+ NULL
+};
+
+fsm ccp_fsm[NUM_PPP];
+ccp_options ccp_wantoptions[NUM_PPP]; /* what to request the peer to use */
+ccp_options ccp_gotoptions[NUM_PPP]; /* what the peer agreed to do */
+ccp_options ccp_allowoptions[NUM_PPP]; /* what we'll agree to do */
+ccp_options ccp_hisoptions[NUM_PPP]; /* what we agreed to do */
+
+/*
+ * Callbacks for fsm code.
+ */
+static void ccp_resetci(fsm *);
+static int ccp_cilen(fsm *);
+static void ccp_addci(fsm *, u_char *, int *);
+static int ccp_ackci(fsm *, u_char *, int);
+static int ccp_nakci(fsm *, u_char *, int);
+static int ccp_rejci(fsm *, u_char *, int);
+static int ccp_reqci(fsm *, u_char *, int *, int);
+static void ccp_up(fsm *);
+static void ccp_down(fsm *);
+static int ccp_extcode(fsm *, int, int, u_char *, int);
+static void ccp_rack_timeout(void *);
+static char *method_name(ccp_options *, ccp_options *);
+
+static fsm_callbacks ccp_callbacks = {
+ ccp_resetci,
+ ccp_cilen,
+ ccp_addci,
+ ccp_ackci,
+ ccp_nakci,
+ ccp_rejci,
+ ccp_reqci,
+ ccp_up,
+ ccp_down,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ ccp_extcode,
+ "CCP"
+};
+
+/*
+ * Do we want / did we get any compression?
+ */
+#define ANY_COMPRESS(opt) ((opt).deflate || (opt).bsd_compress \
+ || (opt).predictor_1 || (opt).predictor_2)
+
+/*
+ * Local state (mainly for handling reset-reqs and reset-acks).
+ */
+static int ccp_localstate[NUM_PPP];
+#define RACK_PENDING 1 /* waiting for reset-ack */
+#define RREQ_REPEAT 2 /* send another reset-req if no reset-ack */
+
+#define RACKTIMEOUT 1 /* second */
+
+static int all_rejected[NUM_PPP]; /* we rejected all peer's options */
+
+/*
+ * ccp_init - initialize CCP.
+ */
+static void
+ccp_init(unit)
+ int unit;
+{
+ fsm *f = &ccp_fsm[unit];
+
+ f->unit = unit;
+ f->protocol = PPP_CCP;
+ f->callbacks = &ccp_callbacks;
+ fsm_init(f);
+
+ memset(&ccp_wantoptions[unit], 0, sizeof(ccp_options));
+ memset(&ccp_gotoptions[unit], 0, sizeof(ccp_options));
+ memset(&ccp_allowoptions[unit], 0, sizeof(ccp_options));
+ memset(&ccp_hisoptions[unit], 0, sizeof(ccp_options));
+
+ ccp_wantoptions[0].deflate = 1;
+ ccp_wantoptions[0].deflate_size = DEFLATE_MAX_SIZE;
+ ccp_wantoptions[0].deflate_correct = 1;
+ ccp_wantoptions[0].deflate_draft = 1;
+ ccp_allowoptions[0].deflate = 1;
+ ccp_allowoptions[0].deflate_size = DEFLATE_MAX_SIZE;
+ ccp_allowoptions[0].deflate_correct = 1;
+ ccp_allowoptions[0].deflate_draft = 1;
+
+ ccp_wantoptions[0].bsd_compress = 1;
+ ccp_wantoptions[0].bsd_bits = BSD_MAX_BITS;
+ ccp_allowoptions[0].bsd_compress = 1;
+ ccp_allowoptions[0].bsd_bits = BSD_MAX_BITS;
+
+ ccp_allowoptions[0].predictor_1 = 1;
+}
+
+/*
+ * ccp_open - CCP is allowed to come up.
+ */
+static void
+ccp_open(unit)
+ int unit;
+{
+ fsm *f = &ccp_fsm[unit];
+
+ if (f->state != OPENED)
+ ccp_flags_set(unit, 1, 0);
+
+ /*
+ * Find out which compressors the kernel supports before
+ * deciding whether to open in silent mode.
+ */
+ ccp_resetci(f);
+ if (!ANY_COMPRESS(ccp_gotoptions[unit]))
+ f->flags |= OPT_SILENT;
+
+ fsm_open(f);
+}
+
+/*
+ * ccp_close - Terminate CCP.
+ */
+static void
+ccp_close(unit, reason)
+ int unit;
+ char *reason;
+{
+ ccp_flags_set(unit, 0, 0);
+ fsm_close(&ccp_fsm[unit], reason);
+}
+
+/*
+ * ccp_lowerup - we may now transmit CCP packets.
+ */
+static void
+ccp_lowerup(unit)
+ int unit;
+{
+ fsm_lowerup(&ccp_fsm[unit]);
+}
+
+/*
+ * ccp_lowerdown - we may not transmit CCP packets.
+ */
+static void
+ccp_lowerdown(unit)
+ int unit;
+{
+ fsm_lowerdown(&ccp_fsm[unit]);
+}
+
+/*
+ * ccp_input - process a received CCP packet.
+ */
+static void
+ccp_input(unit, p, len)
+ int unit;
+ u_char *p;
+ int len;
+{
+ fsm *f = &ccp_fsm[unit];
+ int oldstate;
+
+ /*
+ * Check for a terminate-request so we can print a message.
+ */
+ oldstate = f->state;
+ fsm_input(f, p, len);
+ if (oldstate == OPENED && p[0] == TERMREQ && f->state != OPENED)
+ syslog(LOG_NOTICE, "Compression disabled by peer.");
+
+ /*
+ * If we get a terminate-ack and we're not asking for compression,
+ * close CCP.
+ */
+ if (oldstate == REQSENT && p[0] == TERMACK
+ && !ANY_COMPRESS(ccp_gotoptions[unit]))
+ ccp_close(unit, "No compression negotiated");
+}
+
+/*
+ * Handle a CCP-specific code.
+ */
+static int
+ccp_extcode(f, code, id, p, len)
+ fsm *f;
+ int code, id;
+ u_char *p;
+ int len;
+{
+ switch (code) {
+ case CCP_RESETREQ:
+ if (f->state != OPENED)
+ break;
+ /* send a reset-ack, which the transmitter will see and
+ reset its compression state. */
+ fsm_sdata(f, CCP_RESETACK, id, NULL, 0);
+ break;
+
+ case CCP_RESETACK:
+ if (ccp_localstate[f->unit] & RACK_PENDING && id == f->reqid) {
+ ccp_localstate[f->unit] &= ~(RACK_PENDING | RREQ_REPEAT);
+ UNTIMEOUT(ccp_rack_timeout, f);
+ }
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 1;
+}
+
+/*
+ * ccp_protrej - peer doesn't talk CCP.
+ */
+static void
+ccp_protrej(unit)
+ int unit;
+{
+ ccp_flags_set(unit, 0, 0);
+ fsm_lowerdown(&ccp_fsm[unit]);
+}
+
+/*
+ * ccp_resetci - initialize at start of negotiation.
+ */
+static void
+ccp_resetci(f)
+ fsm *f;
+{
+ ccp_options *go = &ccp_gotoptions[f->unit];
+ u_char opt_buf[16];
+
+ *go = ccp_wantoptions[f->unit];
+ all_rejected[f->unit] = 0;
+
+ /*
+ * Check whether the kernel knows about the various
+ * compression methods we might request.
+ */
+ if (go->bsd_compress) {
+ opt_buf[0] = CI_BSD_COMPRESS;
+ opt_buf[1] = CILEN_BSD_COMPRESS;
+ opt_buf[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, BSD_MIN_BITS);
+ if (ccp_test(f->unit, opt_buf, CILEN_BSD_COMPRESS, 0) <= 0)
+ go->bsd_compress = 0;
+ }
+ if (go->deflate) {
+ if (go->deflate_correct) {
+ opt_buf[0] = CI_DEFLATE;
+ opt_buf[1] = CILEN_DEFLATE;
+ opt_buf[2] = DEFLATE_MAKE_OPT(DEFLATE_MIN_SIZE);
+ opt_buf[3] = DEFLATE_CHK_SEQUENCE;
+ if (ccp_test(f->unit, opt_buf, CILEN_DEFLATE, 0) <= 0)
+ go->deflate_correct = 0;
+ }
+ if (go->deflate_draft) {
+ opt_buf[0] = CI_DEFLATE_DRAFT;
+ opt_buf[1] = CILEN_DEFLATE;
+ opt_buf[2] = DEFLATE_MAKE_OPT(DEFLATE_MIN_SIZE);
+ opt_buf[3] = DEFLATE_CHK_SEQUENCE;
+ if (ccp_test(f->unit, opt_buf, CILEN_DEFLATE, 0) <= 0)
+ go->deflate_draft = 0;
+ }
+ if (!go->deflate_correct && !go->deflate_draft)
+ go->deflate = 0;
+ }
+ if (go->predictor_1) {
+ opt_buf[0] = CI_PREDICTOR_1;
+ opt_buf[1] = CILEN_PREDICTOR_1;
+ if (ccp_test(f->unit, opt_buf, CILEN_PREDICTOR_1, 0) <= 0)
+ go->predictor_1 = 0;
+ }
+ if (go->predictor_2) {
+ opt_buf[0] = CI_PREDICTOR_2;
+ opt_buf[1] = CILEN_PREDICTOR_2;
+ if (ccp_test(f->unit, opt_buf, CILEN_PREDICTOR_2, 0) <= 0)
+ go->predictor_2 = 0;
+ }
+}
+
+/*
+ * ccp_cilen - Return total length of our configuration info.
+ */
+static int
+ccp_cilen(f)
+ fsm *f;
+{
+ ccp_options *go = &ccp_gotoptions[f->unit];
+
+ return (go->bsd_compress? CILEN_BSD_COMPRESS: 0)
+ + (go->deflate? CILEN_DEFLATE: 0)
+ + (go->predictor_1? CILEN_PREDICTOR_1: 0)
+ + (go->predictor_2? CILEN_PREDICTOR_2: 0);
+}
+
+/*
+ * ccp_addci - put our requests in a packet.
+ */
+static void
+ccp_addci(f, p, lenp)
+ fsm *f;
+ u_char *p;
+ int *lenp;
+{
+ int res;
+ ccp_options *go = &ccp_gotoptions[f->unit];
+ u_char *p0 = p;
+
+ /*
+ * Add the compression types that we can receive, in decreasing
+ * preference order. Get the kernel to allocate the first one
+ * in case it gets Acked.
+ */
+ if (go->deflate) {
+ p[0] = go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT;
+ p[1] = CILEN_DEFLATE;
+ p[2] = DEFLATE_MAKE_OPT(go->deflate_size);
+ p[3] = DEFLATE_CHK_SEQUENCE;
+ for (;;) {
+ res = ccp_test(f->unit, p, CILEN_DEFLATE, 0);
+ if (res > 0) {
+ p += CILEN_DEFLATE;
+ break;
+ }
+ if (res < 0 || go->deflate_size <= DEFLATE_MIN_SIZE) {
+ go->deflate = 0;
+ break;
+ }
+ --go->deflate_size;
+ p[2] = DEFLATE_MAKE_OPT(go->deflate_size);
+ }
+ if (p != p0 && go->deflate_correct && go->deflate_draft) {
+ p[0] = CI_DEFLATE_DRAFT;
+ p[1] = CILEN_DEFLATE;
+ p[2] = p[2 - CILEN_DEFLATE];
+ p[3] = DEFLATE_CHK_SEQUENCE;
+ p += CILEN_DEFLATE;
+ }
+ }
+ if (go->bsd_compress) {
+ p[0] = CI_BSD_COMPRESS;
+ p[1] = CILEN_BSD_COMPRESS;
+ p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits);
+ if (p != p0) {
+ p += CILEN_BSD_COMPRESS; /* not the first option */
+ } else {
+ for (;;) {
+ res = ccp_test(f->unit, p, CILEN_BSD_COMPRESS, 0);
+ if (res > 0) {
+ p += CILEN_BSD_COMPRESS;
+ break;
+ }
+ if (res < 0 || go->bsd_bits <= BSD_MIN_BITS) {
+ go->bsd_compress = 0;
+ break;
+ }
+ --go->bsd_bits;
+ p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits);
+ }
+ }
+ }
+ /* XXX Should Predictor 2 be preferable to Predictor 1? */
+ if (go->predictor_1) {
+ p[0] = CI_PREDICTOR_1;
+ p[1] = CILEN_PREDICTOR_1;
+ if (p == p0 && ccp_test(f->unit, p, CILEN_PREDICTOR_1, 0) <= 0) {
+ go->predictor_1 = 0;
+ } else {
+ p += CILEN_PREDICTOR_1;
+ }
+ }
+ if (go->predictor_2) {
+ p[0] = CI_PREDICTOR_2;
+ p[1] = CILEN_PREDICTOR_2;
+ if (p == p0 && ccp_test(f->unit, p, CILEN_PREDICTOR_2, 0) <= 0) {
+ go->predictor_2 = 0;
+ } else {
+ p += CILEN_PREDICTOR_2;
+ }
+ }
+
+ go->method = (p > p0)? p0[0]: -1;
+
+ *lenp = p - p0;
+}
+
+/*
+ * ccp_ackci - process a received configure-ack, and return
+ * 1 iff the packet was OK.
+ */
+static int
+ccp_ackci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ccp_options *go = &ccp_gotoptions[f->unit];
+ u_char *p0 = p;
+
+ if (go->deflate) {
+ if (len < CILEN_DEFLATE
+ || p[0] != (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT)
+ || p[1] != CILEN_DEFLATE
+ || p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+ || p[3] != DEFLATE_CHK_SEQUENCE)
+ return 0;
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ /* XXX Cope with first/fast ack */
+ if (len == 0)
+ return 1;
+ if (go->deflate_correct && go->deflate_draft) {
+ if (len < CILEN_DEFLATE
+ || p[0] != CI_DEFLATE_DRAFT
+ || p[1] != CILEN_DEFLATE
+ || p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+ || p[3] != DEFLATE_CHK_SEQUENCE)
+ return 0;
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ }
+ }
+ if (go->bsd_compress) {
+ if (len < CILEN_BSD_COMPRESS
+ || p[0] != CI_BSD_COMPRESS || p[1] != CILEN_BSD_COMPRESS
+ || p[2] != BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits))
+ return 0;
+ p += CILEN_BSD_COMPRESS;
+ len -= CILEN_BSD_COMPRESS;
+ /* XXX Cope with first/fast ack */
+ if (p == p0 && len == 0)
+ return 1;
+ }
+ if (go->predictor_1) {
+ if (len < CILEN_PREDICTOR_1
+ || p[0] != CI_PREDICTOR_1 || p[1] != CILEN_PREDICTOR_1)
+ return 0;
+ p += CILEN_PREDICTOR_1;
+ len -= CILEN_PREDICTOR_1;
+ /* XXX Cope with first/fast ack */
+ if (p == p0 && len == 0)
+ return 1;
+ }
+ if (go->predictor_2) {
+ if (len < CILEN_PREDICTOR_2
+ || p[0] != CI_PREDICTOR_2 || p[1] != CILEN_PREDICTOR_2)
+ return 0;
+ p += CILEN_PREDICTOR_2;
+ len -= CILEN_PREDICTOR_2;
+ /* XXX Cope with first/fast ack */
+ if (p == p0 && len == 0)
+ return 1;
+ }
+
+ if (len != 0)
+ return 0;
+ return 1;
+}
+
+/*
+ * ccp_nakci - process received configure-nak.
+ * Returns 1 iff the nak was OK.
+ */
+static int
+ccp_nakci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ccp_options *go = &ccp_gotoptions[f->unit];
+ ccp_options no; /* options we've seen already */
+ ccp_options try; /* options to ask for next time */
+
+ memset(&no, 0, sizeof(no));
+ try = *go;
+
+ if (go->deflate && len >= CILEN_DEFLATE
+ && p[0] == (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT)
+ && p[1] == CILEN_DEFLATE) {
+ no.deflate = 1;
+ /*
+ * Peer wants us to use a different code size or something.
+ * Stop asking for Deflate if we don't understand his suggestion.
+ */
+ if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL
+ || DEFLATE_SIZE(p[2]) < DEFLATE_MIN_SIZE
+ || p[3] != DEFLATE_CHK_SEQUENCE)
+ try.deflate = 0;
+ else if (DEFLATE_SIZE(p[2]) < go->deflate_size)
+ try.deflate_size = DEFLATE_SIZE(p[2]);
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ if (go->deflate_correct && go->deflate_draft
+ && len >= CILEN_DEFLATE && p[0] == CI_DEFLATE_DRAFT
+ && p[1] == CILEN_DEFLATE) {
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ }
+ }
+
+ if (go->bsd_compress && len >= CILEN_BSD_COMPRESS
+ && p[0] == CI_BSD_COMPRESS && p[1] == CILEN_BSD_COMPRESS) {
+ no.bsd_compress = 1;
+ /*
+ * Peer wants us to use a different number of bits
+ * or a different version.
+ */
+ if (BSD_VERSION(p[2]) != BSD_CURRENT_VERSION)
+ try.bsd_compress = 0;
+ else if (BSD_NBITS(p[2]) < go->bsd_bits)
+ try.bsd_bits = BSD_NBITS(p[2]);
+ p += CILEN_BSD_COMPRESS;
+ len -= CILEN_BSD_COMPRESS;
+ }
+
+ /*
+ * Predictor-1 and 2 have no options, so they can't be Naked.
+ *
+ * XXX What should we do with any remaining options?
+ */
+
+ if (len != 0)
+ return 0;
+
+ if (f->state != OPENED)
+ *go = try;
+ return 1;
+}
+
+/*
+ * ccp_rejci - reject some of our suggested compression methods.
+ */
+static int
+ccp_rejci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ccp_options *go = &ccp_gotoptions[f->unit];
+ ccp_options try; /* options to request next time */
+
+ try = *go;
+
+ /*
+ * Cope with empty configure-rejects by ceasing to send
+ * configure-requests.
+ */
+ if (len == 0 && all_rejected[f->unit])
+ return -1;
+
+ if (go->deflate && len >= CILEN_DEFLATE
+ && p[0] == (go->deflate_correct? CI_DEFLATE: CI_DEFLATE_DRAFT)
+ && p[1] == CILEN_DEFLATE) {
+ if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+ || p[3] != DEFLATE_CHK_SEQUENCE)
+ return 0; /* Rej is bad */
+ if (go->deflate_correct)
+ try.deflate_correct = 0;
+ else
+ try.deflate_draft = 0;
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ if (go->deflate_correct && go->deflate_draft
+ && len >= CILEN_DEFLATE && p[0] == CI_DEFLATE_DRAFT
+ && p[1] == CILEN_DEFLATE) {
+ if (p[2] != DEFLATE_MAKE_OPT(go->deflate_size)
+ || p[3] != DEFLATE_CHK_SEQUENCE)
+ return 0; /* Rej is bad */
+ try.deflate_draft = 0;
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ }
+ if (!try.deflate_correct && !try.deflate_draft)
+ try.deflate = 0;
+ }
+ if (go->bsd_compress && len >= CILEN_BSD_COMPRESS
+ && p[0] == CI_BSD_COMPRESS && p[1] == CILEN_BSD_COMPRESS) {
+ if (p[2] != BSD_MAKE_OPT(BSD_CURRENT_VERSION, go->bsd_bits))
+ return 0;
+ try.bsd_compress = 0;
+ p += CILEN_BSD_COMPRESS;
+ len -= CILEN_BSD_COMPRESS;
+ }
+ if (go->predictor_1 && len >= CILEN_PREDICTOR_1
+ && p[0] == CI_PREDICTOR_1 && p[1] == CILEN_PREDICTOR_1) {
+ try.predictor_1 = 0;
+ p += CILEN_PREDICTOR_1;
+ len -= CILEN_PREDICTOR_1;
+ }
+ if (go->predictor_2 && len >= CILEN_PREDICTOR_2
+ && p[0] == CI_PREDICTOR_2 && p[1] == CILEN_PREDICTOR_2) {
+ try.predictor_2 = 0;
+ p += CILEN_PREDICTOR_2;
+ len -= CILEN_PREDICTOR_2;
+ }
+
+ if (len != 0)
+ return 0;
+
+ if (f->state != OPENED)
+ *go = try;
+
+ return 1;
+}
+
+/*
+ * ccp_reqci - processed a received configure-request.
+ * Returns CONFACK, CONFNAK or CONFREJ and the packet modified
+ * appropriately.
+ */
+static int
+ccp_reqci(f, p, lenp, dont_nak)
+ fsm *f;
+ u_char *p;
+ int *lenp;
+ int dont_nak;
+{
+ int ret, newret, res;
+ u_char *p0, *retp;
+ int len, clen, type, nb;
+ ccp_options *ho = &ccp_hisoptions[f->unit];
+ ccp_options *ao = &ccp_allowoptions[f->unit];
+
+ ret = CONFACK;
+ retp = p0 = p;
+ len = *lenp;
+
+ memset(ho, 0, sizeof(ccp_options));
+ ho->method = (len > 0)? p[0]: -1;
+
+ while (len > 0) {
+ newret = CONFACK;
+ if (len < 2 || p[1] < 2 || p[1] > len) {
+ /* length is bad */
+ clen = len;
+ newret = CONFREJ;
+
+ } else {
+ type = p[0];
+ clen = p[1];
+
+ switch (type) {
+ case CI_DEFLATE:
+ case CI_DEFLATE_DRAFT:
+ if (!ao->deflate || clen != CILEN_DEFLATE
+ || (!ao->deflate_correct && type == CI_DEFLATE)
+ || (!ao->deflate_draft && type == CI_DEFLATE_DRAFT)) {
+ newret = CONFREJ;
+ break;
+ }
+
+ ho->deflate = 1;
+ ho->deflate_size = nb = DEFLATE_SIZE(p[2]);
+ if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL
+ || p[3] != DEFLATE_CHK_SEQUENCE
+ || nb > ao->deflate_size || nb < DEFLATE_MIN_SIZE) {
+ newret = CONFNAK;
+ if (!dont_nak) {
+ p[2] = DEFLATE_MAKE_OPT(ao->deflate_size);
+ p[3] = DEFLATE_CHK_SEQUENCE;
+ /* fall through to test this #bits below */
+ } else
+ break;
+ }
+
+ /*
+ * Check whether we can do Deflate with the window
+ * size they want. If the window is too big, reduce
+ * it until the kernel can cope and nak with that.
+ * We only check this for the first option.
+ */
+ if (p == p0) {
+ for (;;) {
+ res = ccp_test(f->unit, p, CILEN_DEFLATE, 1);
+ if (res > 0)
+ break; /* it's OK now */
+ if (res < 0 || nb == DEFLATE_MIN_SIZE || dont_nak) {
+ newret = CONFREJ;
+ p[2] = DEFLATE_MAKE_OPT(ho->deflate_size);
+ break;
+ }
+ newret = CONFNAK;
+ --nb;
+ p[2] = DEFLATE_MAKE_OPT(nb);
+ }
+ }
+ break;
+
+ case CI_BSD_COMPRESS:
+ if (!ao->bsd_compress || clen != CILEN_BSD_COMPRESS) {
+ newret = CONFREJ;
+ break;
+ }
+
+ ho->bsd_compress = 1;
+ ho->bsd_bits = nb = BSD_NBITS(p[2]);
+ if (BSD_VERSION(p[2]) != BSD_CURRENT_VERSION
+ || nb > ao->bsd_bits || nb < BSD_MIN_BITS) {
+ newret = CONFNAK;
+ if (!dont_nak) {
+ p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, ao->bsd_bits);
+ /* fall through to test this #bits below */
+ } else
+ break;
+ }
+
+ /*
+ * Check whether we can do BSD-Compress with the code
+ * size they want. If the code size is too big, reduce
+ * it until the kernel can cope and nak with that.
+ * We only check this for the first option.
+ */
+ if (p == p0) {
+ for (;;) {
+ res = ccp_test(f->unit, p, CILEN_BSD_COMPRESS, 1);
+ if (res > 0)
+ break;
+ if (res < 0 || nb == BSD_MIN_BITS || dont_nak) {
+ newret = CONFREJ;
+ p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION,
+ ho->bsd_bits);
+ break;
+ }
+ newret = CONFNAK;
+ --nb;
+ p[2] = BSD_MAKE_OPT(BSD_CURRENT_VERSION, nb);
+ }
+ }
+ break;
+
+ case CI_PREDICTOR_1:
+ if (!ao->predictor_1 || clen != CILEN_PREDICTOR_1) {
+ newret = CONFREJ;
+ break;
+ }
+
+ ho->predictor_1 = 1;
+ if (p == p0
+ && ccp_test(f->unit, p, CILEN_PREDICTOR_1, 1) <= 0) {
+ newret = CONFREJ;
+ }
+ break;
+
+ case CI_PREDICTOR_2:
+ if (!ao->predictor_2 || clen != CILEN_PREDICTOR_2) {
+ newret = CONFREJ;
+ break;
+ }
+
+ ho->predictor_2 = 1;
+ if (p == p0
+ && ccp_test(f->unit, p, CILEN_PREDICTOR_2, 1) <= 0) {
+ newret = CONFREJ;
+ }
+ break;
+
+ default:
+ newret = CONFREJ;
+ }
+ }
+
+ if (newret == CONFNAK && dont_nak)
+ newret = CONFREJ;
+ if (!(newret == CONFACK || (newret == CONFNAK && ret == CONFREJ))) {
+ /* we're returning this option */
+ if (newret == CONFREJ && ret == CONFNAK)
+ retp = p0;
+ ret = newret;
+ if (p != retp)
+ BCOPY(p, retp, clen);
+ retp += clen;
+ }
+
+ p += clen;
+ len -= clen;
+ }
+
+ if (ret != CONFACK) {
+ if (ret == CONFREJ && *lenp == retp - p0)
+ all_rejected[f->unit] = 1;
+ else
+ *lenp = retp - p0;
+ }
+ return ret;
+}
+
+/*
+ * Make a string name for a compression method (or 2).
+ */
+static char *
+method_name(opt, opt2)
+ ccp_options *opt, *opt2;
+{
+ static char result[64];
+
+ if (!ANY_COMPRESS(*opt))
+ return "(none)";
+ switch (opt->method) {
+ case CI_DEFLATE:
+ case CI_DEFLATE_DRAFT:
+ if (opt2 != NULL && opt2->deflate_size != opt->deflate_size)
+ sprintf(result, "Deflate%s (%d/%d)",
+ (opt->method == CI_DEFLATE_DRAFT? "(old#)": ""),
+ opt->deflate_size, opt2->deflate_size);
+ else
+ sprintf(result, "Deflate%s (%d)",
+ (opt->method == CI_DEFLATE_DRAFT? "(old#)": ""),
+ opt->deflate_size);
+ break;
+ case CI_BSD_COMPRESS:
+ if (opt2 != NULL && opt2->bsd_bits != opt->bsd_bits)
+ sprintf(result, "BSD-Compress (%d/%d)", opt->bsd_bits,
+ opt2->bsd_bits);
+ else
+ sprintf(result, "BSD-Compress (%d)", opt->bsd_bits);
+ break;
+ case CI_PREDICTOR_1:
+ return "Predictor 1";
+ case CI_PREDICTOR_2:
+ return "Predictor 2";
+ default:
+ sprintf(result, "Method %d", opt->method);
+ }
+ return result;
+}
+
+/*
+ * CCP has come up - inform the kernel driver and log a message.
+ */
+static void
+ccp_up(f)
+ fsm *f;
+{
+ ccp_options *go = &ccp_gotoptions[f->unit];
+ ccp_options *ho = &ccp_hisoptions[f->unit];
+ char method1[64];
+
+ ccp_flags_set(f->unit, 1, 1);
+ if (ANY_COMPRESS(*go)) {
+ if (ANY_COMPRESS(*ho)) {
+ if (go->method == ho->method) {
+ syslog(LOG_NOTICE, "%s compression enabled",
+ method_name(go, ho));
+ } else {
+ strcpy(method1, method_name(go, NULL));
+ syslog(LOG_NOTICE, "%s / %s compression enabled",
+ method1, method_name(ho, NULL));
+ }
+ } else
+ syslog(LOG_NOTICE, "%s receive compression enabled",
+ method_name(go, NULL));
+ } else if (ANY_COMPRESS(*ho))
+ syslog(LOG_NOTICE, "%s transmit compression enabled",
+ method_name(ho, NULL));
+}
+
+/*
+ * CCP has gone down - inform the kernel driver.
+ */
+static void
+ccp_down(f)
+ fsm *f;
+{
+ if (ccp_localstate[f->unit] & RACK_PENDING)
+ UNTIMEOUT(ccp_rack_timeout, f);
+ ccp_localstate[f->unit] = 0;
+ ccp_flags_set(f->unit, 1, 0);
+}
+
+/*
+ * Print the contents of a CCP packet.
+ */
+static char *ccp_codenames[] = {
+ "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+ "TermReq", "TermAck", "CodeRej",
+ NULL, NULL, NULL, NULL, NULL, NULL,
+ "ResetReq", "ResetAck",
+};
+
+static int
+ccp_printpkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer)(void *, char *, ...);
+ void *arg;
+{
+ u_char *p0, *optend;
+ int code, id, len;
+ int optlen;
+
+ p0 = p;
+ if (plen < HEADERLEN)
+ return 0;
+ code = p[0];
+ id = p[1];
+ len = (p[2] << 8) + p[3];
+ if (len < HEADERLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= sizeof(ccp_codenames) / sizeof(char *)
+ && ccp_codenames[code-1] != NULL)
+ printer(arg, " %s", ccp_codenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+ printer(arg, " id=0x%x", id);
+ len -= HEADERLEN;
+ p += HEADERLEN;
+
+ switch (code) {
+ case CONFREQ:
+ case CONFACK:
+ case CONFNAK:
+ case CONFREJ:
+ /* print list of possible compression methods */
+ while (len >= 2) {
+ code = p[0];
+ optlen = p[1];
+ if (optlen < 2 || optlen > len)
+ break;
+ printer(arg, " <");
+ len -= optlen;
+ optend = p + optlen;
+ switch (code) {
+ case CI_DEFLATE:
+ case CI_DEFLATE_DRAFT:
+ if (optlen >= CILEN_DEFLATE) {
+ printer(arg, "deflate%s %d",
+ (code == CI_DEFLATE_DRAFT? "(old#)": ""),
+ DEFLATE_SIZE(p[2]));
+ if (DEFLATE_METHOD(p[2]) != DEFLATE_METHOD_VAL)
+ printer(arg, " method %d", DEFLATE_METHOD(p[2]));
+ if (p[3] != DEFLATE_CHK_SEQUENCE)
+ printer(arg, " check %d", p[3]);
+ p += CILEN_DEFLATE;
+ }
+ break;
+ case CI_BSD_COMPRESS:
+ if (optlen >= CILEN_BSD_COMPRESS) {
+ printer(arg, "bsd v%d %d", BSD_VERSION(p[2]),
+ BSD_NBITS(p[2]));
+ p += CILEN_BSD_COMPRESS;
+ }
+ break;
+ case CI_PREDICTOR_1:
+ if (optlen >= CILEN_PREDICTOR_1) {
+ printer(arg, "predictor 1");
+ p += CILEN_PREDICTOR_1;
+ }
+ break;
+ case CI_PREDICTOR_2:
+ if (optlen >= CILEN_PREDICTOR_2) {
+ printer(arg, "predictor 2");
+ p += CILEN_PREDICTOR_2;
+ }
+ break;
+ }
+ while (p < optend)
+ printer(arg, " %.2x", *p++);
+ printer(arg, ">");
+ }
+ break;
+
+ case TERMACK:
+ case TERMREQ:
+ if (len > 0 && *p >= ' ' && *p < 0x7f) {
+ print_string(p, len, printer, arg);
+ p += len;
+ len = 0;
+ }
+ break;
+ }
+
+ /* dump out the rest of the packet in hex */
+ while (--len >= 0)
+ printer(arg, " %.2x", *p++);
+
+ return p - p0;
+}
+
+/*
+ * We have received a packet that the decompressor failed to
+ * decompress. Here we would expect to issue a reset-request, but
+ * Motorola has a patent on resetting the compressor as a result of
+ * detecting an error in the decompressed data after decompression.
+ * (See US patent 5,130,993; international patent publication number
+ * WO 91/10289; Australian patent 73296/91.)
+ *
+ * So we ask the kernel whether the error was detected after
+ * decompression; if it was, we take CCP down, thus disabling
+ * compression :-(, otherwise we issue the reset-request.
+ */
+static void
+ccp_datainput(unit, pkt, len)
+ int unit;
+ u_char *pkt;
+ int len;
+{
+ fsm *f;
+
+ f = &ccp_fsm[unit];
+ if (f->state == OPENED) {
+ if (ccp_fatal_error(unit)) {
+ /*
+ * Disable compression by taking CCP down.
+ */
+ syslog(LOG_ERR, "Lost compression sync: disabling compression");
+ ccp_close(unit, "Lost compression sync");
+ } else {
+ /*
+ * Send a reset-request to reset the peer's compressor.
+ * We don't do that if we are still waiting for an
+ * acknowledgement to a previous reset-request.
+ */
+ if (!(ccp_localstate[f->unit] & RACK_PENDING)) {
+ fsm_sdata(f, CCP_RESETREQ, f->reqid = ++f->id, NULL, 0);
+ TIMEOUT(ccp_rack_timeout, f, RACKTIMEOUT);
+ ccp_localstate[f->unit] |= RACK_PENDING;
+ } else
+ ccp_localstate[f->unit] |= RREQ_REPEAT;
+ }
+ }
+}
+
+/*
+ * Timeout waiting for reset-ack.
+ */
+static void
+ccp_rack_timeout(arg)
+ void *arg;
+{
+ fsm *f = arg;
+
+ if (f->state == OPENED && ccp_localstate[f->unit] & RREQ_REPEAT) {
+ fsm_sdata(f, CCP_RESETREQ, f->reqid, NULL, 0);
+ TIMEOUT(ccp_rack_timeout, f, RACKTIMEOUT);
+ ccp_localstate[f->unit] &= ~RREQ_REPEAT;
+ } else
+ ccp_localstate[f->unit] &= ~RACK_PENDING;
+}
+
diff --git a/usr.sbin/pppd/ccp.h b/usr.sbin/pppd/ccp.h
new file mode 100644
index 0000000..a03ac4d
--- /dev/null
+++ b/usr.sbin/pppd/ccp.h
@@ -0,0 +1,50 @@
+/*
+ * ccp.h - Definitions for PPP Compression Control Protocol.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * $FreeBSD$
+ */
+
+typedef struct ccp_options {
+ u_int bsd_compress: 1; /* do BSD Compress? */
+ u_int deflate: 1; /* do Deflate? (RFC code) */
+ u_int baddeflate: 1; /* do Deflate? (Magnalink!) */
+ u_int predictor_1: 1; /* do Predictor-1? */
+ u_int predictor_2: 1; /* do Predictor-2? */
+ u_int deflate_correct: 1; /* use correct code for deflate? */
+ u_int deflate_draft: 1; /* use draft RFC code for deflate? */
+ u_short bsd_bits; /* # bits/code for BSD Compress */
+ u_short deflate_size; /* lg(window size) for Deflate */
+ u_short baddeflate_size; /* lg(window size) for Deflate */
+ short method; /* code for chosen compression method */
+} ccp_options;
+
+extern fsm ccp_fsm[];
+extern ccp_options ccp_wantoptions[];
+extern ccp_options ccp_gotoptions[];
+extern ccp_options ccp_allowoptions[];
+extern ccp_options ccp_hisoptions[];
+
+extern struct protent ccp_protent;
diff --git a/usr.sbin/pppd/chap.c b/usr.sbin/pppd/chap.c
new file mode 100644
index 0000000..055dc7b
--- /dev/null
+++ b/usr.sbin/pppd/chap.c
@@ -0,0 +1,870 @@
+/*
+ * chap.c - Challenge Handshake Authentication Protocol.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 Australian National University. The name of the University
+ * 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Copyright (c) 1991 Gregory M. Christy.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 Gregory M. Christy. 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 WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <syslog.h>
+#include <md5.h>
+
+#include "pppd.h"
+#include "chap.h"
+#include "md5.h"
+#ifdef CHAPMS
+#include "chap_ms.h"
+#endif
+
+/*
+ * Protocol entry points.
+ */
+static void ChapInit(int);
+static void ChapLowerUp(int);
+static void ChapLowerDown(int);
+static void ChapInput(int, u_char *, int);
+static void ChapProtocolReject(int);
+static int ChapPrintPkt(u_char *, int,
+ void (*)(void *, char *, ...), void *);
+
+struct protent chap_protent = {
+ PPP_CHAP,
+ ChapInit,
+ ChapInput,
+ ChapProtocolReject,
+ ChapLowerUp,
+ ChapLowerDown,
+ NULL,
+ NULL,
+ ChapPrintPkt,
+ NULL,
+ 1,
+ "CHAP",
+ NULL,
+ NULL,
+ NULL
+};
+
+chap_state chap[NUM_PPP]; /* CHAP state; one for each unit */
+
+static void ChapChallengeTimeout(void *);
+static void ChapResponseTimeout(void *);
+static void ChapReceiveChallenge(chap_state *, u_char *, int, int);
+static void ChapRechallenge(void *);
+static void ChapReceiveResponse(chap_state *, u_char *, int, int);
+static void ChapReceiveSuccess(chap_state *, u_char *, int, int);
+static void ChapReceiveFailure(chap_state *, u_char *, int, int);
+static void ChapSendStatus(chap_state *, int);
+static void ChapSendChallenge(chap_state *);
+static void ChapSendResponse(chap_state *);
+static void ChapGenChallenge(chap_state *);
+
+extern double drand48(void);
+extern void srand48(long);
+
+/*
+ * ChapInit - Initialize a CHAP unit.
+ */
+static void
+ChapInit(unit)
+ int unit;
+{
+ chap_state *cstate = &chap[unit];
+
+ BZERO(cstate, sizeof(*cstate));
+ cstate->unit = unit;
+ cstate->clientstate = CHAPCS_INITIAL;
+ cstate->serverstate = CHAPSS_INITIAL;
+ cstate->timeouttime = CHAP_DEFTIMEOUT;
+ cstate->max_transmits = CHAP_DEFTRANSMITS;
+ /* random number generator is initialized in magic_init */
+}
+
+
+/*
+ * ChapAuthWithPeer - Authenticate us with our peer (start client).
+ *
+ */
+void
+ChapAuthWithPeer(unit, our_name, digest)
+ int unit;
+ char *our_name;
+ int digest;
+{
+ chap_state *cstate = &chap[unit];
+
+ cstate->resp_name = our_name;
+ cstate->resp_type = digest;
+
+ if (cstate->clientstate == CHAPCS_INITIAL ||
+ cstate->clientstate == CHAPCS_PENDING) {
+ /* lower layer isn't up - wait until later */
+ cstate->clientstate = CHAPCS_PENDING;
+ return;
+ }
+
+ /*
+ * We get here as a result of LCP coming up.
+ * So even if CHAP was open before, we will
+ * have to re-authenticate ourselves.
+ */
+ cstate->clientstate = CHAPCS_LISTEN;
+}
+
+
+/*
+ * ChapAuthPeer - Authenticate our peer (start server).
+ */
+void
+ChapAuthPeer(unit, our_name, digest)
+ int unit;
+ char *our_name;
+ int digest;
+{
+ chap_state *cstate = &chap[unit];
+
+ cstate->chal_name = our_name;
+ cstate->chal_type = digest;
+
+ if (cstate->serverstate == CHAPSS_INITIAL ||
+ cstate->serverstate == CHAPSS_PENDING) {
+ /* lower layer isn't up - wait until later */
+ cstate->serverstate = CHAPSS_PENDING;
+ return;
+ }
+
+ ChapGenChallenge(cstate);
+ ChapSendChallenge(cstate); /* crank it up dude! */
+ cstate->serverstate = CHAPSS_INITIAL_CHAL;
+}
+
+
+/*
+ * ChapChallengeTimeout - Timeout expired on sending challenge.
+ */
+static void
+ChapChallengeTimeout(arg)
+ void *arg;
+{
+ chap_state *cstate = (chap_state *) arg;
+
+ /* if we aren't sending challenges, don't worry. then again we */
+ /* probably shouldn't be here either */
+ if (cstate->serverstate != CHAPSS_INITIAL_CHAL &&
+ cstate->serverstate != CHAPSS_RECHALLENGE)
+ return;
+
+ if (cstate->chal_transmits >= cstate->max_transmits) {
+ /* give up on peer */
+ syslog(LOG_ERR, "Peer failed to respond to CHAP challenge");
+ cstate->serverstate = CHAPSS_BADAUTH;
+ auth_peer_fail(cstate->unit, PPP_CHAP);
+ return;
+ }
+
+ ChapSendChallenge(cstate); /* Re-send challenge */
+}
+
+
+/*
+ * ChapResponseTimeout - Timeout expired on sending response.
+ */
+static void
+ChapResponseTimeout(arg)
+ void *arg;
+{
+ chap_state *cstate = (chap_state *) arg;
+
+ /* if we aren't sending a response, don't worry. */
+ if (cstate->clientstate != CHAPCS_RESPONSE)
+ return;
+
+ ChapSendResponse(cstate); /* re-send response */
+}
+
+
+/*
+ * ChapRechallenge - Time to challenge the peer again.
+ */
+static void
+ChapRechallenge(arg)
+ void *arg;
+{
+ chap_state *cstate = (chap_state *) arg;
+
+ /* if we aren't sending a response, don't worry. */
+ if (cstate->serverstate != CHAPSS_OPEN)
+ return;
+
+ ChapGenChallenge(cstate);
+ ChapSendChallenge(cstate);
+ cstate->serverstate = CHAPSS_RECHALLENGE;
+}
+
+
+/*
+ * ChapLowerUp - The lower layer is up.
+ *
+ * Start up if we have pending requests.
+ */
+static void
+ChapLowerUp(unit)
+ int unit;
+{
+ chap_state *cstate = &chap[unit];
+
+ if (cstate->clientstate == CHAPCS_INITIAL)
+ cstate->clientstate = CHAPCS_CLOSED;
+ else if (cstate->clientstate == CHAPCS_PENDING)
+ cstate->clientstate = CHAPCS_LISTEN;
+
+ if (cstate->serverstate == CHAPSS_INITIAL)
+ cstate->serverstate = CHAPSS_CLOSED;
+ else if (cstate->serverstate == CHAPSS_PENDING) {
+ ChapGenChallenge(cstate);
+ ChapSendChallenge(cstate);
+ cstate->serverstate = CHAPSS_INITIAL_CHAL;
+ }
+}
+
+
+/*
+ * ChapLowerDown - The lower layer is down.
+ *
+ * Cancel all timeouts.
+ */
+static void
+ChapLowerDown(unit)
+ int unit;
+{
+ chap_state *cstate = &chap[unit];
+
+ /* Timeout(s) pending? Cancel if so. */
+ if (cstate->serverstate == CHAPSS_INITIAL_CHAL ||
+ cstate->serverstate == CHAPSS_RECHALLENGE)
+ UNTIMEOUT(ChapChallengeTimeout, cstate);
+ else if (cstate->serverstate == CHAPSS_OPEN
+ && cstate->chal_interval != 0)
+ UNTIMEOUT(ChapRechallenge, cstate);
+ if (cstate->clientstate == CHAPCS_RESPONSE)
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+
+ cstate->clientstate = CHAPCS_INITIAL;
+ cstate->serverstate = CHAPSS_INITIAL;
+}
+
+
+/*
+ * ChapProtocolReject - Peer doesn't grok CHAP.
+ */
+static void
+ChapProtocolReject(unit)
+ int unit;
+{
+ chap_state *cstate = &chap[unit];
+
+ if (cstate->serverstate != CHAPSS_INITIAL &&
+ cstate->serverstate != CHAPSS_CLOSED)
+ auth_peer_fail(unit, PPP_CHAP);
+ if (cstate->clientstate != CHAPCS_INITIAL &&
+ cstate->clientstate != CHAPCS_CLOSED)
+ auth_withpeer_fail(unit, PPP_CHAP);
+ ChapLowerDown(unit); /* shutdown chap */
+}
+
+
+/*
+ * ChapInput - Input CHAP packet.
+ */
+static void
+ChapInput(unit, inpacket, packet_len)
+ int unit;
+ u_char *inpacket;
+ int packet_len;
+{
+ chap_state *cstate = &chap[unit];
+ u_char *inp;
+ u_char code, id;
+ int len;
+
+ /*
+ * Parse header (code, id and length).
+ * If packet too short, drop it.
+ */
+ inp = inpacket;
+ if (packet_len < CHAP_HEADERLEN) {
+ CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short header."));
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+ if (len < CHAP_HEADERLEN) {
+ CHAPDEBUG((LOG_INFO, "ChapInput: rcvd illegal length."));
+ return;
+ }
+ if (len > packet_len) {
+ CHAPDEBUG((LOG_INFO, "ChapInput: rcvd short packet."));
+ return;
+ }
+ len -= CHAP_HEADERLEN;
+
+ /*
+ * Action depends on code (as in fact it usually does :-).
+ */
+ switch (code) {
+ case CHAP_CHALLENGE:
+ ChapReceiveChallenge(cstate, inp, id, len);
+ break;
+
+ case CHAP_RESPONSE:
+ ChapReceiveResponse(cstate, inp, id, len);
+ break;
+
+ case CHAP_FAILURE:
+ ChapReceiveFailure(cstate, inp, id, len);
+ break;
+
+ case CHAP_SUCCESS:
+ ChapReceiveSuccess(cstate, inp, id, len);
+ break;
+
+ default: /* Need code reject? */
+ syslog(LOG_WARNING, "Unknown CHAP code (%d) received.", code);
+ break;
+ }
+}
+
+
+/*
+ * ChapReceiveChallenge - Receive Challenge and send Response.
+ */
+static void
+ChapReceiveChallenge(cstate, inp, id, len)
+ chap_state *cstate;
+ u_char *inp;
+ int id;
+ int len;
+{
+ int rchallenge_len;
+ u_char *rchallenge;
+ int secret_len;
+ char secret[MAXSECRETLEN];
+ char rhostname[256];
+ MD5_CTX mdContext;
+ u_char hash[MD5_SIGNATURE_SIZE];
+
+ CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: Rcvd id %d.", id));
+ if (cstate->clientstate == CHAPCS_CLOSED ||
+ cstate->clientstate == CHAPCS_PENDING) {
+ CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: in state %d",
+ cstate->clientstate));
+ return;
+ }
+
+ if (len < 2) {
+ CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet."));
+ return;
+ }
+
+ GETCHAR(rchallenge_len, inp);
+ len -= sizeof (u_char) + rchallenge_len; /* now name field length */
+ if (len < 0) {
+ CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: rcvd short packet."));
+ return;
+ }
+ rchallenge = inp;
+ INCPTR(rchallenge_len, inp);
+
+ if (len >= sizeof(rhostname))
+ len = sizeof(rhostname) - 1;
+ BCOPY(inp, rhostname, len);
+ rhostname[len] = '\000';
+
+ CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: received name field '%s'",
+ rhostname));
+
+ /* Microsoft doesn't send their name back in the PPP packet */
+ if (remote_name[0] != 0 && (explicit_remote || rhostname[0] == 0)) {
+ strncpy(rhostname, remote_name, sizeof(rhostname));
+ rhostname[sizeof(rhostname) - 1] = 0;
+ CHAPDEBUG((LOG_INFO, "ChapReceiveChallenge: using '%s' as remote name",
+ rhostname));
+ }
+
+ /* get secret for authenticating ourselves with the specified host */
+ if (!get_secret(cstate->unit, cstate->resp_name, rhostname,
+ secret, &secret_len, 0)) {
+ secret_len = 0; /* assume null secret if can't find one */
+ syslog(LOG_WARNING, "No CHAP secret found for authenticating us to %s",
+ rhostname);
+ }
+
+ /* cancel response send timeout if necessary */
+ if (cstate->clientstate == CHAPCS_RESPONSE)
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+
+ cstate->resp_id = id;
+ cstate->resp_transmits = 0;
+
+ /* generate MD based on negotiated type */
+ switch (cstate->resp_type) {
+
+ case CHAP_DIGEST_MD5:
+ MD5Init(&mdContext);
+ MD5Update(&mdContext, &cstate->resp_id, 1);
+ MD5Update(&mdContext, secret, secret_len);
+ MD5Update(&mdContext, rchallenge, rchallenge_len);
+ MD5Final(hash, &mdContext);
+ BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE);
+ cstate->resp_length = MD5_SIGNATURE_SIZE;
+ break;
+
+#ifdef CHAPMS
+ case CHAP_MICROSOFT:
+ ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len);
+ break;
+#endif
+
+ default:
+ CHAPDEBUG((LOG_INFO, "unknown digest type %d", cstate->resp_type));
+ return;
+ }
+
+ BZERO(secret, sizeof(secret));
+ ChapSendResponse(cstate);
+}
+
+
+/*
+ * ChapReceiveResponse - Receive and process response.
+ */
+static void
+ChapReceiveResponse(cstate, inp, id, len)
+ chap_state *cstate;
+ u_char *inp;
+ int id;
+ int len;
+{
+ u_char *remmd, remmd_len;
+ int secret_len, old_state;
+ int code;
+ char rhostname[256];
+ MD5_CTX mdContext;
+ char secret[MAXSECRETLEN];
+ u_char hash[MD5_SIGNATURE_SIZE];
+
+ CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: Rcvd id %d.", id));
+
+ if (cstate->serverstate == CHAPSS_CLOSED ||
+ cstate->serverstate == CHAPSS_PENDING) {
+ CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: in state %d",
+ cstate->serverstate));
+ return;
+ }
+
+ if (id != cstate->chal_id)
+ return; /* doesn't match ID of last challenge */
+
+ /*
+ * If we have received a duplicate or bogus Response,
+ * we have to send the same answer (Success/Failure)
+ * as we did for the first Response we saw.
+ */
+ if (cstate->serverstate == CHAPSS_OPEN) {
+ ChapSendStatus(cstate, CHAP_SUCCESS);
+ return;
+ }
+ if (cstate->serverstate == CHAPSS_BADAUTH) {
+ ChapSendStatus(cstate, CHAP_FAILURE);
+ return;
+ }
+
+ if (len < 2) {
+ CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet."));
+ return;
+ }
+ GETCHAR(remmd_len, inp); /* get length of MD */
+ remmd = inp; /* get pointer to MD */
+ INCPTR(remmd_len, inp);
+
+ len -= sizeof (u_char) + remmd_len;
+ if (len < 0) {
+ CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: rcvd short packet."));
+ return;
+ }
+
+ UNTIMEOUT(ChapChallengeTimeout, cstate);
+
+ if (len >= sizeof(rhostname))
+ len = sizeof(rhostname) - 1;
+ BCOPY(inp, rhostname, len);
+ rhostname[len] = '\000';
+
+ CHAPDEBUG((LOG_INFO, "ChapReceiveResponse: received name field: %s",
+ rhostname));
+
+ /*
+ * Get secret for authenticating them with us,
+ * do the hash ourselves, and compare the result.
+ */
+ code = CHAP_FAILURE;
+ if (!get_secret(cstate->unit, rhostname, cstate->chal_name,
+ secret, &secret_len, 1)) {
+ syslog(LOG_WARNING, "No CHAP secret found for authenticating %s",
+ rhostname);
+ } else {
+
+ /* generate MD based on negotiated type */
+ switch (cstate->chal_type) {
+
+ case CHAP_DIGEST_MD5: /* only MD5 is defined for now */
+ if (remmd_len != MD5_SIGNATURE_SIZE)
+ break; /* it's not even the right length */
+ MD5Init(&mdContext);
+ MD5Update(&mdContext, &cstate->chal_id, 1);
+ MD5Update(&mdContext, secret, secret_len);
+ MD5Update(&mdContext, cstate->challenge, cstate->chal_len);
+ MD5Final(hash, &mdContext);
+
+ /* compare local and remote MDs and send the appropriate status */
+ if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0)
+ code = CHAP_SUCCESS; /* they are the same! */
+ break;
+
+ default:
+ CHAPDEBUG((LOG_INFO, "unknown digest type %d", cstate->chal_type));
+ }
+ }
+
+ BZERO(secret, sizeof(secret));
+ ChapSendStatus(cstate, code);
+
+ if (code == CHAP_SUCCESS) {
+ old_state = cstate->serverstate;
+ cstate->serverstate = CHAPSS_OPEN;
+ if (old_state == CHAPSS_INITIAL_CHAL) {
+ auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len);
+ }
+ if (cstate->chal_interval != 0)
+ TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval);
+ syslog(LOG_NOTICE, "CHAP peer authentication succeeded for %s",
+ rhostname);
+
+ } else {
+ syslog(LOG_ERR, "CHAP peer authentication failed for remote host %s",
+ rhostname);
+ cstate->serverstate = CHAPSS_BADAUTH;
+ auth_peer_fail(cstate->unit, PPP_CHAP);
+ }
+}
+
+/*
+ * ChapReceiveSuccess - Receive Success
+ */
+static void
+ChapReceiveSuccess(cstate, inp, id, len)
+ chap_state *cstate;
+ u_char *inp;
+ u_char id;
+ int len;
+{
+
+ CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: Rcvd id %d.", id));
+
+ if (cstate->clientstate == CHAPCS_OPEN)
+ /* presumably an answer to a duplicate response */
+ return;
+
+ if (cstate->clientstate != CHAPCS_RESPONSE) {
+ /* don't know what this is */
+ CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: in state %d\n",
+ cstate->clientstate));
+ return;
+ }
+
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+
+ /*
+ * Print message.
+ */
+ if (len > 0)
+ PRINTMSG(inp, len);
+
+ cstate->clientstate = CHAPCS_OPEN;
+
+ auth_withpeer_success(cstate->unit, PPP_CHAP);
+}
+
+
+/*
+ * ChapReceiveFailure - Receive failure.
+ */
+static void
+ChapReceiveFailure(cstate, inp, id, len)
+ chap_state *cstate;
+ u_char *inp;
+ u_char id;
+ int len;
+{
+ CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: Rcvd id %d.", id));
+
+ if (cstate->clientstate != CHAPCS_RESPONSE) {
+ /* don't know what this is */
+ CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: in state %d\n",
+ cstate->clientstate));
+ return;
+ }
+
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+
+ /*
+ * Print message.
+ */
+ if (len > 0)
+ PRINTMSG(inp, len);
+
+ syslog(LOG_ERR, "CHAP authentication failed");
+ auth_withpeer_fail(cstate->unit, PPP_CHAP);
+}
+
+
+/*
+ * ChapSendChallenge - Send an Authenticate challenge.
+ */
+static void
+ChapSendChallenge(cstate)
+ chap_state *cstate;
+{
+ u_char *outp;
+ int chal_len, name_len;
+ int outlen;
+
+ chal_len = cstate->chal_len;
+ name_len = strlen(cstate->chal_name);
+ outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len;
+ outp = outpacket_buf;
+
+ MAKEHEADER(outp, PPP_CHAP); /* paste in a CHAP header */
+
+ PUTCHAR(CHAP_CHALLENGE, outp);
+ PUTCHAR(cstate->chal_id, outp);
+ PUTSHORT(outlen, outp);
+
+ PUTCHAR(chal_len, outp); /* put length of challenge */
+ BCOPY(cstate->challenge, outp, chal_len);
+ INCPTR(chal_len, outp);
+
+ BCOPY(cstate->chal_name, outp, name_len); /* append hostname */
+
+ output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
+
+ CHAPDEBUG((LOG_INFO, "ChapSendChallenge: Sent id %d.", cstate->chal_id));
+
+ TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime);
+ ++cstate->chal_transmits;
+}
+
+
+/*
+ * ChapSendStatus - Send a status response (ack or nak).
+ */
+static void
+ChapSendStatus(cstate, code)
+ chap_state *cstate;
+ int code;
+{
+ u_char *outp;
+ int outlen, msglen;
+ char msg[256];
+
+ if (code == CHAP_SUCCESS)
+ sprintf(msg, "Welcome to %s.", hostname);
+ else
+ sprintf(msg, "I don't like you. Go 'way.");
+ msglen = strlen(msg);
+
+ outlen = CHAP_HEADERLEN + msglen;
+ outp = outpacket_buf;
+
+ MAKEHEADER(outp, PPP_CHAP); /* paste in a header */
+
+ PUTCHAR(code, outp);
+ PUTCHAR(cstate->chal_id, outp);
+ PUTSHORT(outlen, outp);
+ BCOPY(msg, outp, msglen);
+ output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
+
+ CHAPDEBUG((LOG_INFO, "ChapSendStatus: Sent code %d, id %d.", code,
+ cstate->chal_id));
+}
+
+/*
+ * ChapGenChallenge is used to generate a pseudo-random challenge string of
+ * a pseudo-random length between min_len and max_len. The challenge
+ * string and its length are stored in *cstate, and various other fields of
+ * *cstate are initialized.
+ */
+
+static void
+ChapGenChallenge(cstate)
+ chap_state *cstate;
+{
+ int chal_len;
+ u_char *ptr = cstate->challenge;
+ unsigned int i;
+
+ /* pick a random challenge length between MIN_CHALLENGE_LENGTH and
+ MAX_CHALLENGE_LENGTH */
+ chal_len = (unsigned) ((drand48() *
+ (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) +
+ MIN_CHALLENGE_LENGTH);
+ cstate->chal_len = chal_len;
+ cstate->chal_id = ++cstate->id;
+ cstate->chal_transmits = 0;
+
+ /* generate a random string */
+ for (i = 0; i < chal_len; i++ )
+ *ptr++ = (char) (drand48() * 0xff);
+}
+
+/*
+ * ChapSendResponse - send a response packet with values as specified
+ * in *cstate.
+ */
+/* ARGSUSED */
+static void
+ChapSendResponse(cstate)
+ chap_state *cstate;
+{
+ u_char *outp;
+ int outlen, md_len, name_len;
+
+ md_len = cstate->resp_length;
+ name_len = strlen(cstate->resp_name);
+ outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len;
+ outp = outpacket_buf;
+
+ MAKEHEADER(outp, PPP_CHAP);
+
+ PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */
+ PUTCHAR(cstate->resp_id, outp); /* copy id from challenge packet */
+ PUTSHORT(outlen, outp); /* packet length */
+
+ PUTCHAR(md_len, outp); /* length of MD */
+ BCOPY(cstate->response, outp, md_len); /* copy MD to buffer */
+ INCPTR(md_len, outp);
+
+ BCOPY(cstate->resp_name, outp, name_len); /* append our name */
+
+ /* send the packet */
+ output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
+
+ cstate->clientstate = CHAPCS_RESPONSE;
+ TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime);
+ ++cstate->resp_transmits;
+}
+
+/*
+ * ChapPrintPkt - print the contents of a CHAP packet.
+ */
+static char *ChapCodenames[] = {
+ "Challenge", "Response", "Success", "Failure"
+};
+
+static int
+ChapPrintPkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer)(void *, char *, ...);
+ void *arg;
+{
+ int code, id, len;
+ int clen, nlen;
+ u_char x;
+
+ if (plen < CHAP_HEADERLEN)
+ return 0;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < CHAP_HEADERLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *))
+ printer(arg, " %s", ChapCodenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+ printer(arg, " id=0x%x", id);
+ len -= CHAP_HEADERLEN;
+ switch (code) {
+ case CHAP_CHALLENGE:
+ case CHAP_RESPONSE:
+ if (len < 1)
+ break;
+ clen = p[0];
+ if (len < clen + 1)
+ break;
+ ++p;
+ nlen = len - clen - 1;
+ printer(arg, " <");
+ for (; clen > 0; --clen) {
+ GETCHAR(x, p);
+ printer(arg, "%.2x", x);
+ }
+ printer(arg, ">, name = ");
+ print_string((char *)p, nlen, printer, arg);
+ break;
+ case CHAP_FAILURE:
+ case CHAP_SUCCESS:
+ printer(arg, " ");
+ print_string((char *)p, len, printer, arg);
+ break;
+ default:
+ for (clen = len; clen > 0; --clen) {
+ GETCHAR(x, p);
+ printer(arg, " %.2x", x);
+ }
+ }
+
+ return len + CHAP_HEADERLEN;
+}
diff --git a/usr.sbin/pppd/chap.h b/usr.sbin/pppd/chap.h
new file mode 100644
index 0000000..be8c90d
--- /dev/null
+++ b/usr.sbin/pppd/chap.h
@@ -0,0 +1,124 @@
+/*
+ * chap.h - Challenge Handshake Authentication Protocol definitions.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 Australian National University. The name of the University
+ * 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Copyright (c) 1991 Gregory M. Christy
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 author.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CHAP_INCLUDE__
+
+/* Code + ID + length */
+#define CHAP_HEADERLEN 4
+
+/*
+ * CHAP codes.
+ */
+
+#define CHAP_DIGEST_MD5 5 /* use MD5 algorithm */
+#define MD5_SIGNATURE_SIZE 16 /* 16 bytes in a MD5 message digest */
+#define CHAP_MICROSOFT 0x80 /* use Microsoft-compatible alg. */
+#define MS_CHAP_RESPONSE_LEN 49 /* Response length for MS-CHAP */
+
+#define CHAP_CHALLENGE 1
+#define CHAP_RESPONSE 2
+#define CHAP_SUCCESS 3
+#define CHAP_FAILURE 4
+
+/*
+ * Challenge lengths (for challenges we send) and other limits.
+ */
+#define MIN_CHALLENGE_LENGTH 32
+#define MAX_CHALLENGE_LENGTH 64
+#define MAX_RESPONSE_LENGTH 64 /* sufficient for MD5 or MS-CHAP */
+
+/*
+ * Each interface is described by a chap structure.
+ */
+
+typedef struct chap_state {
+ int unit; /* Interface unit number */
+ int clientstate; /* Client state */
+ int serverstate; /* Server state */
+ u_char challenge[MAX_CHALLENGE_LENGTH]; /* last challenge string sent */
+ u_char chal_len; /* challenge length */
+ u_char chal_id; /* ID of last challenge */
+ u_char chal_type; /* hash algorithm for challenges */
+ u_char id; /* Current id */
+ char *chal_name; /* Our name to use with challenge */
+ int chal_interval; /* Time until we challenge peer again */
+ int timeouttime; /* Timeout time in seconds */
+ int max_transmits; /* Maximum # of challenge transmissions */
+ int chal_transmits; /* Number of transmissions of challenge */
+ int resp_transmits; /* Number of transmissions of response */
+ u_char response[MAX_RESPONSE_LENGTH]; /* Response to send */
+ u_char resp_length; /* length of response */
+ u_char resp_id; /* ID for response messages */
+ u_char resp_type; /* hash algorithm for responses */
+ char *resp_name; /* Our name to send with response */
+} chap_state;
+
+
+/*
+ * Client (peer) states.
+ */
+#define CHAPCS_INITIAL 0 /* Lower layer down, not opened */
+#define CHAPCS_CLOSED 1 /* Lower layer up, not opened */
+#define CHAPCS_PENDING 2 /* Auth us to peer when lower up */
+#define CHAPCS_LISTEN 3 /* Listening for a challenge */
+#define CHAPCS_RESPONSE 4 /* Sent response, waiting for status */
+#define CHAPCS_OPEN 5 /* We've received Success */
+
+/*
+ * Server (authenticator) states.
+ */
+#define CHAPSS_INITIAL 0 /* Lower layer down, not opened */
+#define CHAPSS_CLOSED 1 /* Lower layer up, not opened */
+#define CHAPSS_PENDING 2 /* Auth peer when lower up */
+#define CHAPSS_INITIAL_CHAL 3 /* We've sent the first challenge */
+#define CHAPSS_OPEN 4 /* We've sent a Success msg */
+#define CHAPSS_RECHALLENGE 5 /* We've sent another challenge */
+#define CHAPSS_BADAUTH 6 /* We've sent a Failure msg */
+
+/*
+ * Timeouts.
+ */
+#define CHAP_DEFTIMEOUT 3 /* Timeout time in seconds */
+#define CHAP_DEFTRANSMITS 10 /* max # times to send challenge */
+
+extern chap_state chap[];
+
+void ChapAuthWithPeer(int, char *, int);
+void ChapAuthPeer(int, char *, int);
+
+extern struct protent chap_protent;
+
+#define __CHAP_INCLUDE__
+#endif /* __CHAP_INCLUDE__ */
diff --git a/usr.sbin/pppd/chap_ms.c b/usr.sbin/pppd/chap_ms.c
new file mode 100644
index 0000000..5fce6e3
--- /dev/null
+++ b/usr.sbin/pppd/chap_ms.c
@@ -0,0 +1,335 @@
+/*
+ * chap_ms.c - Microsoft MS-CHAP compatible implementation.
+ *
+ * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited.
+ * http://www.strataware.com/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 Eric Rosenquist. 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 WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+ * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997
+ *
+ * Implemented LANManager type password response to MS-CHAP challenges.
+ * Now pppd provides both NT style and LANMan style blocks, and the
+ * prefered is set by option "ms-lanman". Default is to use NT.
+ * The hash text (StdText) was taken from Win95 RASAPI32.DLL.
+ *
+ * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80
+ */
+
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+#ifdef CHAPMS
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <syslog.h>
+#include <unistd.h>
+#ifdef HAVE_CRYPT_H
+#include <crypt.h>
+#endif
+
+#include "pppd.h"
+#include "chap.h"
+#include "chap_ms.h"
+#include "md4.h"
+
+#ifndef USE_CRYPT
+#include <openssl/des.h>
+#endif
+
+typedef struct {
+ u_char LANManResp[24];
+ u_char NTResp[24];
+ u_char UseNT; /* If 1, ignore the LANMan response field */
+} MS_ChapResponse;
+/* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse),
+ in case this struct gets padded. */
+
+
+static void ChallengeResponse(u_char *, u_char *, u_char *);
+static void DesEncrypt(u_char *, u_char *, u_char *);
+static void MakeKey(u_char *, u_char *);
+static u_char Get7Bits(u_char *, int);
+static void ChapMS_NT(char *, int, char *, int, MS_ChapResponse *);
+#ifdef MSLANMAN
+static void ChapMS_LANMan(char *, int, char *, int, MS_ChapResponse *);
+#endif
+
+#ifdef USE_CRYPT
+static void Expand(u_char *, u_char *);
+static void Collapse(u_char *, u_char *);
+#endif
+
+static void
+ChallengeResponse(challenge, pwHash, response)
+ u_char *challenge; /* IN 8 octets */
+ u_char *pwHash; /* IN 16 octets */
+ u_char *response; /* OUT 24 octets */
+{
+ char ZPasswordHash[21];
+
+ BZERO(ZPasswordHash, sizeof(ZPasswordHash));
+ BCOPY(pwHash, ZPasswordHash, MD4_SIGNATURE_SIZE);
+
+#if 0
+ log_packet(ZPasswordHash, sizeof(ZPasswordHash), "ChallengeResponse - ZPasswordHash", LOG_DEBUG);
+#endif
+
+ DesEncrypt(challenge, ZPasswordHash + 0, response + 0);
+ DesEncrypt(challenge, ZPasswordHash + 7, response + 8);
+ DesEncrypt(challenge, ZPasswordHash + 14, response + 16);
+
+#if 0
+ log_packet(response, 24, "ChallengeResponse - response", LOG_DEBUG);
+#endif
+}
+
+
+#ifdef USE_CRYPT
+static void
+DesEncrypt(clear, key, cipher)
+ u_char *clear; /* IN 8 octets */
+ u_char *key; /* IN 7 octets */
+ u_char *cipher; /* OUT 8 octets */
+{
+ u_char des_key[8];
+ u_char crypt_key[66];
+ u_char des_input[66];
+
+ MakeKey(key, des_key);
+
+ Expand(des_key, crypt_key);
+ setkey(crypt_key);
+
+#if 0
+ CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X",
+ clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7]));
+#endif
+
+ Expand(clear, des_input);
+ encrypt(des_input, 0);
+ Collapse(des_input, cipher);
+
+#if 0
+ CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X",
+ cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7]));
+#endif
+}
+
+#else /* USE_CRYPT */
+
+static void
+DesEncrypt(clear, key, cipher)
+ u_char *clear; /* IN 8 octets */
+ u_char *key; /* IN 7 octets */
+ u_char *cipher; /* OUT 8 octets */
+{
+ des_cblock des_key;
+ des_key_schedule key_schedule;
+
+ MakeKey(key, des_key);
+
+ des_set_key(&des_key, key_schedule);
+
+#if 0
+ CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X",
+ clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7]));
+#endif
+
+ des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1);
+
+#if 0
+ CHAPDEBUG((LOG_INFO, "DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X",
+ cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7]));
+#endif
+}
+
+#endif /* USE_CRYPT */
+
+
+static u_char Get7Bits(input, startBit)
+ u_char *input;
+ int startBit;
+{
+ register unsigned int word;
+
+ word = (unsigned)input[startBit / 8] << 8;
+ word |= (unsigned)input[startBit / 8 + 1];
+
+ word >>= 15 - (startBit % 8 + 7);
+
+ return word & 0xFE;
+}
+
+#ifdef USE_CRYPT
+
+/* in == 8-byte string (expanded version of the 56-bit key)
+ * out == 64-byte string where each byte is either 1 or 0
+ * Note that the low-order "bit" is always ignored by by setkey()
+ */
+static void Expand(in, out)
+ u_char *in;
+ u_char *out;
+{
+ int j, c;
+ int i;
+
+ for(i = 0; i < 64; in++){
+ c = *in;
+ for(j = 7; j >= 0; j--)
+ *out++ = (c >> j) & 01;
+ i += 8;
+ }
+}
+
+/* The inverse of Expand
+ */
+static void Collapse(in, out)
+ u_char *in;
+ u_char *out;
+{
+ int j;
+ int i;
+ unsigned int c;
+
+ for (i = 0; i < 64; i += 8, out++) {
+ c = 0;
+ for (j = 7; j >= 0; j--, in++)
+ c |= *in << j;
+ *out = c & 0xff;
+ }
+}
+#endif
+
+static void MakeKey(key, des_key)
+ u_char *key; /* IN 56 bit DES key missing parity bits */
+ u_char *des_key; /* OUT 64 bit DES key with parity bits added */
+{
+ des_key[0] = Get7Bits(key, 0);
+ des_key[1] = Get7Bits(key, 7);
+ des_key[2] = Get7Bits(key, 14);
+ des_key[3] = Get7Bits(key, 21);
+ des_key[4] = Get7Bits(key, 28);
+ des_key[5] = Get7Bits(key, 35);
+ des_key[6] = Get7Bits(key, 42);
+ des_key[7] = Get7Bits(key, 49);
+
+#ifndef USE_CRYPT
+ des_set_odd_parity((des_cblock *)des_key);
+#endif
+
+#if 0
+ CHAPDEBUG((LOG_INFO, "MakeKey: 56-bit input : %02X%02X%02X%02X%02X%02X%02X",
+ key[0], key[1], key[2], key[3], key[4], key[5], key[6]));
+ CHAPDEBUG((LOG_INFO, "MakeKey: 64-bit output: %02X%02X%02X%02X%02X%02X%02X%02X",
+ des_key[0], des_key[1], des_key[2], des_key[3], des_key[4], des_key[5], des_key[6], des_key[7]));
+#endif
+}
+
+static void
+ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, response)
+ char *rchallenge;
+ int rchallenge_len;
+ char *secret;
+ int secret_len;
+ MS_ChapResponse *response;
+{
+ int i;
+ MD4_CTX md4Context;
+ u_char hash[MD4_SIGNATURE_SIZE];
+ u_char unicodePassword[MAX_NT_PASSWORD * 2];
+
+ /* Initialize the Unicode version of the secret (== password). */
+ /* This implicitly supports 8-bit ISO8859/1 characters. */
+ BZERO(unicodePassword, sizeof(unicodePassword));
+ for (i = 0; i < secret_len; i++)
+ unicodePassword[i * 2] = (u_char)secret[i];
+
+ MD4Init(&md4Context);
+ MD4Update(&md4Context, unicodePassword, secret_len * 2); /* Unicode is 2 bytes/char */
+
+ MD4Final(hash, &md4Context); /* Tell MD4 we're done */
+
+ ChallengeResponse(rchallenge, hash, response->NTResp);
+}
+
+#ifdef MSLANMAN
+static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */
+
+static void
+ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, response)
+ char *rchallenge;
+ int rchallenge_len;
+ char *secret;
+ int secret_len;
+ MS_ChapResponse *response;
+{
+ int i;
+ u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */
+ u_char PasswordHash[MD4_SIGNATURE_SIZE];
+
+ /* LANMan password is case insensitive */
+ BZERO(UcasePassword, sizeof(UcasePassword));
+ for (i = 0; i < secret_len; i++)
+ UcasePassword[i] = (u_char)toupper(secret[i]);
+ DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 );
+ DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 );
+ ChallengeResponse(rchallenge, PasswordHash, response->LANManResp);
+}
+#endif
+
+void
+ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len)
+ chap_state *cstate;
+ char *rchallenge;
+ int rchallenge_len;
+ char *secret;
+ int secret_len;
+{
+ MS_ChapResponse response;
+#ifdef MSLANMAN
+ extern int ms_lanman;
+#endif
+
+#if 0
+ CHAPDEBUG((LOG_INFO, "ChapMS: secret is '%.*s'", secret_len, secret));
+#endif
+ BZERO(&response, sizeof(response));
+
+ /* Calculate both always */
+ ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, &response);
+
+#ifdef MSLANMAN
+ ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, &response);
+
+ /* prefered method is set by option */
+ response.UseNT = !ms_lanman;
+#else
+ response.UseNT = 1;
+#endif
+
+ BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN);
+ cstate->resp_length = MS_CHAP_RESPONSE_LEN;
+}
+
+#endif /* CHAPMS */
diff --git a/usr.sbin/pppd/chap_ms.h b/usr.sbin/pppd/chap_ms.h
new file mode 100644
index 0000000..6ef03c8
--- /dev/null
+++ b/usr.sbin/pppd/chap_ms.h
@@ -0,0 +1,33 @@
+/*
+ * chap.h - Challenge Handshake Authentication Protocol definitions.
+ *
+ * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited.
+ * http://www.strataware.com/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 Eric Rosenquist. 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 WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CHAPMS_INCLUDE__
+
+#define MD4_SIGNATURE_SIZE 16 /* 16 bytes in a MD4 message digest */
+#define MAX_NT_PASSWORD 256 /* Maximum number of (Unicode) chars in an NT password */
+
+void ChapMS(chap_state *, char *, int, char *, int);
+
+#define __CHAPMS_INCLUDE__
+#endif /* __CHAPMS_INCLUDE__ */
diff --git a/usr.sbin/pppd/demand.c b/usr.sbin/pppd/demand.c
new file mode 100644
index 0000000..b86c81f
--- /dev/null
+++ b/usr.sbin/pppd/demand.c
@@ -0,0 +1,348 @@
+/*
+ * demand.c - Support routines for demand-dialling.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 Australian National University. The name of the University
+ * 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#ifdef PPP_FILTER
+#include <net/if.h>
+#include <net/bpf.h>
+#include <pcap.h>
+#endif
+
+#include "pppd.h"
+#include "fsm.h"
+#include "ipcp.h"
+#include "lcp.h"
+
+char *frame;
+int framelen;
+int framemax;
+int escape_flag;
+int flush_flag;
+int fcs;
+
+struct packet {
+ int length;
+ struct packet *next;
+ unsigned char data[1];
+};
+
+struct packet *pend_q;
+struct packet *pend_qtail;
+
+static int active_packet(unsigned char *, int);
+
+/*
+ * demand_conf - configure the interface for doing dial-on-demand.
+ */
+void
+demand_conf()
+{
+ int i;
+ struct protent *protp;
+
+/* framemax = lcp_allowoptions[0].mru;
+ if (framemax < PPP_MRU) */
+ framemax = PPP_MRU;
+ framemax += PPP_HDRLEN + PPP_FCSLEN;
+ frame = malloc(framemax);
+ if (frame == NULL)
+ novm("demand frame");
+ framelen = 0;
+ pend_q = NULL;
+ escape_flag = 0;
+ flush_flag = 0;
+ fcs = PPP_INITFCS;
+
+ ppp_send_config(0, PPP_MRU, (u_int32_t) 0, 0, 0);
+ ppp_recv_config(0, PPP_MRU, (u_int32_t) 0, 0, 0);
+
+#ifdef PPP_FILTER
+ set_filters(&pass_filter, &active_filter);
+#endif
+
+ /*
+ * Call the demand_conf procedure for each protocol that's got one.
+ */
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->enabled_flag && protp->demand_conf != NULL)
+ if (!((*protp->demand_conf)(0)))
+ die(1);
+}
+
+
+/*
+ * demand_block - set each network protocol to block further packets.
+ */
+void
+demand_block()
+{
+ int i;
+ struct protent *protp;
+
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->enabled_flag && protp->demand_conf != NULL)
+ sifnpmode(0, protp->protocol & ~0x8000, NPMODE_QUEUE);
+ get_loop_output();
+}
+
+/*
+ * demand_discard - set each network protocol to discard packets
+ * with an error.
+ */
+void
+demand_discard()
+{
+ struct packet *pkt, *nextpkt;
+ int i;
+ struct protent *protp;
+
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->enabled_flag && protp->demand_conf != NULL)
+ sifnpmode(0, protp->protocol & ~0x8000, NPMODE_ERROR);
+ get_loop_output();
+
+ /* discard all saved packets */
+ for (pkt = pend_q; pkt != NULL; pkt = nextpkt) {
+ nextpkt = pkt->next;
+ free(pkt);
+ }
+ pend_q = NULL;
+ framelen = 0;
+ flush_flag = 0;
+ escape_flag = 0;
+ fcs = PPP_INITFCS;
+}
+
+/*
+ * demand_unblock - set each enabled network protocol to pass packets.
+ */
+void
+demand_unblock()
+{
+ int i;
+ struct protent *protp;
+
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->enabled_flag && protp->demand_conf != NULL)
+ sifnpmode(0, protp->protocol & ~0x8000, NPMODE_PASS);
+}
+
+/*
+ * FCS lookup table as calculated by genfcstab.
+ */
+static u_short fcstab[256] = {
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+/*
+ * loop_chars - process characters received from the loopback.
+ * Calls loop_frame when a complete frame has been accumulated.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ */
+int
+loop_chars(p, n)
+ unsigned char *p;
+ int n;
+{
+ int c, rv;
+
+ rv = 0;
+ for (; n > 0; --n) {
+ c = *p++;
+ if (c == PPP_FLAG) {
+ if (!escape_flag && !flush_flag
+ && framelen > 2 && fcs == PPP_GOODFCS) {
+ framelen -= 2;
+ if (loop_frame(frame, framelen))
+ rv = 1;
+ }
+ framelen = 0;
+ flush_flag = 0;
+ escape_flag = 0;
+ fcs = PPP_INITFCS;
+ continue;
+ }
+ if (flush_flag)
+ continue;
+ if (escape_flag) {
+ c ^= PPP_TRANS;
+ escape_flag = 0;
+ } else if (c == PPP_ESCAPE) {
+ escape_flag = 1;
+ continue;
+ }
+ if (framelen >= framemax) {
+ flush_flag = 1;
+ continue;
+ }
+ frame[framelen++] = c;
+ fcs = PPP_FCS(fcs, c);
+ }
+ return rv;
+}
+
+/*
+ * loop_frame - given a frame obtained from the loopback,
+ * decide whether to bring up the link or not, and, if we want
+ * to transmit this frame later, put it on the pending queue.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ * We assume that the kernel driver has already applied the
+ * pass_filter, so we won't get packets it rejected.
+ * We apply the active_filter to see if we want this packet to
+ * bring up the link.
+ */
+int
+loop_frame(frame, len)
+ unsigned char *frame;
+ int len;
+{
+ struct packet *pkt;
+
+ /* log_packet(frame, len, "from loop: ", LOG_DEBUG); */
+ if (len < PPP_HDRLEN)
+ return 0;
+ if ((PPP_PROTOCOL(frame) & 0x8000) != 0)
+ return 0; /* shouldn't get any of these anyway */
+ if (!active_packet(frame, len))
+ return 0;
+
+ pkt = (struct packet *) malloc(sizeof(struct packet) + len);
+ if (pkt != NULL) {
+ pkt->length = len;
+ pkt->next = NULL;
+ memcpy(pkt->data, frame, len);
+ if (pend_q == NULL)
+ pend_q = pkt;
+ else
+ pend_qtail->next = pkt;
+ pend_qtail = pkt;
+ }
+ return 1;
+}
+
+/*
+ * demand_rexmit - Resend all those frames which we got via the
+ * loopback, now that the real serial link is up.
+ */
+void
+demand_rexmit(proto)
+ int proto;
+{
+ struct packet *pkt, *prev, *nextpkt;
+
+ prev = NULL;
+ pkt = pend_q;
+ pend_q = NULL;
+ for (; pkt != NULL; pkt = nextpkt) {
+ nextpkt = pkt->next;
+ if (PPP_PROTOCOL(pkt->data) == proto) {
+ output(0, pkt->data, pkt->length);
+ free(pkt);
+ } else {
+ if (prev == NULL)
+ pend_q = pkt;
+ else
+ prev->next = pkt;
+ prev = pkt;
+ }
+ }
+ pend_qtail = prev;
+ if (prev != NULL)
+ prev->next = NULL;
+}
+
+/*
+ * Scan a packet to decide whether it is an "active" packet,
+ * that is, whether it is worth bringing up the link for.
+ */
+static int
+active_packet(p, len)
+ unsigned char *p;
+ int len;
+{
+ int proto, i;
+ struct protent *protp;
+
+ if (len < PPP_HDRLEN)
+ return 0;
+ proto = PPP_PROTOCOL(p);
+#ifdef PPP_FILTER
+ if (active_filter.bf_len != 0
+ && bpf_filter(active_filter.bf_insns, frame, len, len) == 0)
+ return 0;
+#endif
+ for (i = 0; (protp = protocols[i]) != NULL; ++i) {
+ if (protp->protocol < 0xC000 && (protp->protocol & ~0x8000) == proto) {
+ if (!protp->enabled_flag)
+ return 0;
+ if (protp->active_pkt == NULL)
+ return 1;
+ return (*protp->active_pkt)(p, len);
+ }
+ }
+ return 0; /* not a supported protocol !!?? */
+}
diff --git a/usr.sbin/pppd/eui64.c b/usr.sbin/pppd/eui64.c
new file mode 100644
index 0000000..28335a3
--- /dev/null
+++ b/usr.sbin/pppd/eui64.c
@@ -0,0 +1,46 @@
+/*
+ eui64.c - EUI64 routines for IPv6CP.
+ Copyright (C) 1999 Tommi Komulainen <Tommi.Komulainen@iki.fi>
+
+ Redistribution and use in source and binary forms 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 Tommi Komulainen. 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 WITHOUT ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+
+ $Id: eui64.c,v 1.3 1999/08/25 04:15:51 paulus Exp $
+*/
+
+#ifndef lint
+#define RCSID "$Id: eui64.c,v 1.3 1999/08/25 04:15:51 paulus Exp $"
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "pppd.h"
+
+#ifdef RCSID
+static const char rcsid[] = RCSID;
+#endif
+
+/*
+ * eui64_ntoa - Make an ascii representation of an interface identifier
+ */
+char *
+eui64_ntoa(e)
+ eui64_t e;
+{
+ static char buf[32];
+
+ snprintf(buf, 32, "%02x%02x:%02x%02x:%02x%02x:%02x%02x",
+ e.e8[0], e.e8[1], e.e8[2], e.e8[3],
+ e.e8[4], e.e8[5], e.e8[6], e.e8[7]);
+ return buf;
+}
diff --git a/usr.sbin/pppd/eui64.h b/usr.sbin/pppd/eui64.h
new file mode 100644
index 0000000..4b4b505
--- /dev/null
+++ b/usr.sbin/pppd/eui64.h
@@ -0,0 +1,98 @@
+/* $FreeBSD$ */
+/*
+ eui64.h - EUI64 routines for IPv6CP.
+ Copyright (C) 1999 Tommi Komulainen <Tommi.Komulainen@iki.fi>
+
+ Redistribution and use in source and binary forms 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 Tommi Komulainen. 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 WITHOUT ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+
+
+ $Id: eui64.h,v 1.3 1999/09/30 19:56:37 masputra Exp $
+*/
+
+#ifndef __EUI64_H__
+#define __EUI64_H__
+
+#if !defined(INET6)
+#error "this file should only be included when INET6 is defined"
+#endif /* not defined(INET6) */
+
+#if defined(SOL2)
+#include <netinet/in.h>
+
+typedef union {
+ uint8_t e8[8]; /* lower 64-bit IPv6 address */
+ uint32_t e32[2]; /* lower 64-bit IPv6 address */
+} eui64_t;
+
+/*
+ * Declare the two below, since in.h only defines them when _KERNEL
+ * is declared - which shouldn't be true when dealing with user-land programs
+ */
+#define s6_addr8 _S6_un._S6_u8
+#define s6_addr32 _S6_un._S6_u32
+
+#else /* else if not defined(SOL2) */
+
+/*
+ * TODO:
+ *
+ * Maybe this should be done by processing struct in6_addr directly...
+ */
+typedef union
+{
+ u_int8_t e8[8];
+ u_int16_t e16[4];
+ u_int32_t e32[2];
+} eui64_t;
+
+#endif /* defined(SOL2) */
+
+#define eui64_iszero(e) (((e).e32[0] | (e).e32[1]) == 0)
+#define eui64_equals(e, o) (((e).e32[0] == (o).e32[0]) && \
+ ((e).e32[1] == (o).e32[1]))
+#define eui64_zero(e) (e).e32[0] = (e).e32[1] = 0;
+
+#define eui64_copy(s, d) memcpy(&(d), &(s), sizeof(eui64_t))
+
+#define eui64_magic(e) do { \
+ (e).e32[0] = magic(); \
+ (e).e32[1] = magic(); \
+ (e).e8[0] &= ~2; \
+ } while (0)
+#define eui64_magic_nz(x) do { \
+ eui64_magic(x); \
+ } while (eui64_iszero(x))
+#define eui64_magic_ne(x, y) do { \
+ eui64_magic(x); \
+ } while (eui64_equals(x, y))
+
+#define eui64_get(ll, cp) do { \
+ eui64_copy((*cp), (ll)); \
+ (cp) += sizeof(eui64_t); \
+ } while (0)
+
+#define eui64_put(ll, cp) do { \
+ eui64_copy((ll), (*cp)); \
+ (cp) += sizeof(eui64_t); \
+ } while (0)
+
+#define eui64_set32(e, l) do { \
+ (e).e32[0] = 0; \
+ (e).e32[1] = htonl(l); \
+ } while (0)
+#define eui64_setlo32(e, l) eui64_set32(e, l)
+
+char *eui64_ntoa(eui64_t); /* Returns ascii representation of id */
+
+#endif /* __EUI64_H__ */
+
diff --git a/usr.sbin/pppd/fsm.c b/usr.sbin/pppd/fsm.c
new file mode 100644
index 0000000..0b586b7
--- /dev/null
+++ b/usr.sbin/pppd/fsm.c
@@ -0,0 +1,798 @@
+/*
+ * fsm.c - {Link, IP} Control Protocol Finite State Machine.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 Carnegie Mellon University. The name of the
+ * University 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+/*
+ * TODO:
+ * Randomize fsm id on link/init.
+ * Deal with variable outgoing MTU.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <syslog.h>
+
+#include "pppd.h"
+#include "fsm.h"
+
+static void fsm_timeout(void *);
+static void fsm_rconfreq(fsm *, int, u_char *, int);
+static void fsm_rconfack(fsm *, int, u_char *, int);
+static void fsm_rconfnakrej(fsm *, int, int, u_char *, int);
+static void fsm_rtermreq(fsm *, int, u_char *, int);
+static void fsm_rtermack(fsm *);
+static void fsm_rcoderej(fsm *, u_char *, int);
+static void fsm_sconfreq(fsm *, int);
+
+#define PROTO_NAME(f) ((f)->callbacks->proto_name)
+
+int peer_mru[NUM_PPP];
+
+
+/*
+ * fsm_init - Initialize fsm.
+ *
+ * Initialize fsm state.
+ */
+void
+fsm_init(f)
+ fsm *f;
+{
+ f->state = INITIAL;
+ f->flags = 0;
+ f->id = 0; /* XXX Start with random id? */
+ f->timeouttime = DEFTIMEOUT;
+ f->maxconfreqtransmits = DEFMAXCONFREQS;
+ f->maxtermtransmits = DEFMAXTERMREQS;
+ f->maxnakloops = DEFMAXNAKLOOPS;
+ f->term_reason_len = 0;
+}
+
+
+/*
+ * fsm_lowerup - The lower layer is up.
+ */
+void
+fsm_lowerup(f)
+ fsm *f;
+{
+ switch( f->state ){
+ case INITIAL:
+ f->state = CLOSED;
+ break;
+
+ case STARTING:
+ if( f->flags & OPT_SILENT )
+ f->state = STOPPED;
+ else {
+ /* Send an initial configure-request */
+ fsm_sconfreq(f, 0);
+ f->state = REQSENT;
+ }
+ break;
+
+ default:
+ FSMDEBUG((LOG_INFO, "%s: Up event in state %d!",
+ PROTO_NAME(f), f->state));
+ }
+}
+
+
+/*
+ * fsm_lowerdown - The lower layer is down.
+ *
+ * Cancel all timeouts and inform upper layers.
+ */
+void
+fsm_lowerdown(f)
+ fsm *f;
+{
+ switch( f->state ){
+ case CLOSED:
+ f->state = INITIAL;
+ break;
+
+ case STOPPED:
+ f->state = STARTING;
+ if( f->callbacks->starting )
+ (*f->callbacks->starting)(f);
+ break;
+
+ case CLOSING:
+ f->state = INITIAL;
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ break;
+
+ case STOPPING:
+ case REQSENT:
+ case ACKRCVD:
+ case ACKSENT:
+ f->state = STARTING;
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ break;
+
+ case OPENED:
+ if( f->callbacks->down )
+ (*f->callbacks->down)(f);
+ f->state = STARTING;
+ break;
+
+ default:
+ FSMDEBUG((LOG_INFO, "%s: Down event in state %d!",
+ PROTO_NAME(f), f->state));
+ }
+}
+
+
+/*
+ * fsm_open - Link is allowed to come up.
+ */
+void
+fsm_open(f)
+ fsm *f;
+{
+ switch( f->state ){
+ case INITIAL:
+ f->state = STARTING;
+ if( f->callbacks->starting )
+ (*f->callbacks->starting)(f);
+ break;
+
+ case CLOSED:
+ if( f->flags & OPT_SILENT )
+ f->state = STOPPED;
+ else {
+ /* Send an initial configure-request */
+ fsm_sconfreq(f, 0);
+ f->state = REQSENT;
+ }
+ break;
+
+ case CLOSING:
+ f->state = STOPPING;
+ /* FALLTHROUGH */
+ case STOPPED:
+ case OPENED:
+ if( f->flags & OPT_RESTART ){
+ fsm_lowerdown(f);
+ fsm_lowerup(f);
+ }
+ break;
+ }
+}
+
+
+/*
+ * fsm_close - Start closing connection.
+ *
+ * Cancel timeouts and either initiate close or possibly go directly to
+ * the CLOSED state.
+ */
+void
+fsm_close(f, reason)
+ fsm *f;
+ char *reason;
+{
+ f->term_reason = reason;
+ f->term_reason_len = (reason == NULL? 0: strlen(reason));
+ switch( f->state ){
+ case STARTING:
+ f->state = INITIAL;
+ break;
+ case STOPPED:
+ f->state = CLOSED;
+ break;
+ case STOPPING:
+ f->state = CLOSING;
+ break;
+
+ case REQSENT:
+ case ACKRCVD:
+ case ACKSENT:
+ case OPENED:
+ if( f->state != OPENED )
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ else if( f->callbacks->down )
+ (*f->callbacks->down)(f); /* Inform upper layers we're down */
+
+ /* Init restart counter, send Terminate-Request */
+ f->retransmits = f->maxtermtransmits;
+ fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
+ (u_char *) f->term_reason, f->term_reason_len);
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+ --f->retransmits;
+
+ f->state = CLOSING;
+ break;
+ }
+}
+
+
+/*
+ * fsm_timeout - Timeout expired.
+ */
+static void
+fsm_timeout(arg)
+ void *arg;
+{
+ fsm *f = (fsm *) arg;
+
+ switch (f->state) {
+ case CLOSING:
+ case STOPPING:
+ if( f->retransmits <= 0 ){
+ /*
+ * We've waited for an ack long enough. Peer probably heard us.
+ */
+ f->state = (f->state == CLOSING)? CLOSED: STOPPED;
+ if( f->callbacks->finished )
+ (*f->callbacks->finished)(f);
+ } else {
+ /* Send Terminate-Request */
+ fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
+ (u_char *) f->term_reason, f->term_reason_len);
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+ --f->retransmits;
+ }
+ break;
+
+ case REQSENT:
+ case ACKRCVD:
+ case ACKSENT:
+ if (f->retransmits <= 0) {
+ syslog(LOG_WARNING, "%s: timeout sending Config-Requests",
+ PROTO_NAME(f));
+ f->state = STOPPED;
+ if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished )
+ (*f->callbacks->finished)(f);
+
+ } else {
+ /* Retransmit the configure-request */
+ if (f->callbacks->retransmit)
+ (*f->callbacks->retransmit)(f);
+ fsm_sconfreq(f, 1); /* Re-send Configure-Request */
+ if( f->state == ACKRCVD )
+ f->state = REQSENT;
+ }
+ break;
+
+ default:
+ FSMDEBUG((LOG_INFO, "%s: Timeout event in state %d!",
+ PROTO_NAME(f), f->state));
+ }
+}
+
+
+/*
+ * fsm_input - Input packet.
+ */
+void
+fsm_input(f, inpacket, l)
+ fsm *f;
+ u_char *inpacket;
+ int l;
+{
+ u_char *inp;
+ u_char code, id;
+ int len;
+
+ /*
+ * Parse header (code, id and length).
+ * If packet too short, drop it.
+ */
+ inp = inpacket;
+ if (l < HEADERLEN) {
+ FSMDEBUG((LOG_WARNING, "fsm_input(%x): Rcvd short header.",
+ f->protocol));
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+ if (len < HEADERLEN) {
+ FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd illegal length.",
+ f->protocol));
+ return;
+ }
+ if (len > l) {
+ FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd short packet.",
+ f->protocol));
+ return;
+ }
+ len -= HEADERLEN; /* subtract header length */
+
+ if( f->state == INITIAL || f->state == STARTING ){
+ FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd packet in state %d.",
+ f->protocol, f->state));
+ return;
+ }
+
+ /*
+ * Action depends on code.
+ */
+ switch (code) {
+ case CONFREQ:
+ fsm_rconfreq(f, id, inp, len);
+ break;
+
+ case CONFACK:
+ fsm_rconfack(f, id, inp, len);
+ break;
+
+ case CONFNAK:
+ case CONFREJ:
+ fsm_rconfnakrej(f, code, id, inp, len);
+ break;
+
+ case TERMREQ:
+ fsm_rtermreq(f, id, inp, len);
+ break;
+
+ case TERMACK:
+ fsm_rtermack(f);
+ break;
+
+ case CODEREJ:
+ fsm_rcoderej(f, inp, len);
+ break;
+
+ default:
+ if( !f->callbacks->extcode
+ || !(*f->callbacks->extcode)(f, code, id, inp, len) )
+ fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN);
+ break;
+ }
+}
+
+
+/*
+ * fsm_rconfreq - Receive Configure-Request.
+ */
+static void
+fsm_rconfreq(f, id, inp, len)
+ fsm *f;
+ u_char id;
+ u_char *inp;
+ int len;
+{
+ int code, reject_if_disagree;
+
+ FSMDEBUG((LOG_INFO, "fsm_rconfreq(%s): Rcvd id %d.", PROTO_NAME(f), id));
+ switch( f->state ){
+ case CLOSED:
+ /* Go away, we're closed */
+ fsm_sdata(f, TERMACK, id, NULL, 0);
+ return;
+ case CLOSING:
+ case STOPPING:
+ return;
+
+ case OPENED:
+ /* Go down and restart negotiation */
+ if( f->callbacks->down )
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ fsm_sconfreq(f, 0); /* Send initial Configure-Request */
+ break;
+
+ case STOPPED:
+ /* Negotiation started by our peer */
+ fsm_sconfreq(f, 0); /* Send initial Configure-Request */
+ f->state = REQSENT;
+ break;
+ }
+
+ /*
+ * Pass the requested configuration options
+ * to protocol-specific code for checking.
+ */
+ if (f->callbacks->reqci){ /* Check CI */
+ reject_if_disagree = (f->nakloops >= f->maxnakloops);
+ code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree);
+ } else if (len)
+ code = CONFREJ; /* Reject all CI */
+ else
+ code = CONFACK;
+
+ /* send the Ack, Nak or Rej to the peer */
+ fsm_sdata(f, code, id, inp, len);
+
+ if (code == CONFACK) {
+ if (f->state == ACKRCVD) {
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ f->state = OPENED;
+ if (f->callbacks->up)
+ (*f->callbacks->up)(f); /* Inform upper layers */
+ } else
+ f->state = ACKSENT;
+ f->nakloops = 0;
+
+ } else {
+ /* we sent CONFACK or CONFREJ */
+ if (f->state != ACKRCVD)
+ f->state = REQSENT;
+ if( code == CONFNAK )
+ ++f->nakloops;
+ }
+}
+
+
+/*
+ * fsm_rconfack - Receive Configure-Ack.
+ */
+static void
+fsm_rconfack(f, id, inp, len)
+ fsm *f;
+ int id;
+ u_char *inp;
+ int len;
+{
+ FSMDEBUG((LOG_INFO, "fsm_rconfack(%s): Rcvd id %d.",
+ PROTO_NAME(f), id));
+
+ if (id != f->reqid || f->seen_ack) /* Expected id? */
+ return; /* Nope, toss... */
+ if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len):
+ (len == 0)) ){
+ /* Ack is bad - ignore it */
+ log_packet(inp, len, "Received bad configure-ack: ", LOG_ERR);
+ FSMDEBUG((LOG_INFO, "%s: received bad Ack (length %d)",
+ PROTO_NAME(f), len));
+ return;
+ }
+ f->seen_ack = 1;
+
+ switch (f->state) {
+ case CLOSED:
+ case STOPPED:
+ fsm_sdata(f, TERMACK, id, NULL, 0);
+ break;
+
+ case REQSENT:
+ f->state = ACKRCVD;
+ f->retransmits = f->maxconfreqtransmits;
+ break;
+
+ case ACKRCVD:
+ /* Huh? an extra valid Ack? oh well... */
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ fsm_sconfreq(f, 0);
+ f->state = REQSENT;
+ break;
+
+ case ACKSENT:
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ f->state = OPENED;
+ f->retransmits = f->maxconfreqtransmits;
+ if (f->callbacks->up)
+ (*f->callbacks->up)(f); /* Inform upper layers */
+ break;
+
+ case OPENED:
+ /* Go down and restart negotiation */
+ if (f->callbacks->down)
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ fsm_sconfreq(f, 0); /* Send initial Configure-Request */
+ f->state = REQSENT;
+ break;
+ }
+}
+
+
+/*
+ * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
+ */
+static void
+fsm_rconfnakrej(f, code, id, inp, len)
+ fsm *f;
+ int code, id;
+ u_char *inp;
+ int len;
+{
+ int (*proc)(fsm *, u_char *, int);
+ int ret;
+
+ FSMDEBUG((LOG_INFO, "fsm_rconfnakrej(%s): Rcvd id %d.",
+ PROTO_NAME(f), id));
+
+ if (id != f->reqid || f->seen_ack) /* Expected id? */
+ return; /* Nope, toss... */
+ proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci;
+ if (!proc || !(ret = proc(f, inp, len))) {
+ /* Nak/reject is bad - ignore it */
+ log_packet(inp, len, "Received bad configure-nak/rej: ", LOG_ERR);
+ FSMDEBUG((LOG_INFO, "%s: received bad %s (length %d)",
+ PROTO_NAME(f), (code==CONFNAK? "Nak": "reject"), len));
+ return;
+ }
+ f->seen_ack = 1;
+
+ switch (f->state) {
+ case CLOSED:
+ case STOPPED:
+ fsm_sdata(f, TERMACK, id, NULL, 0);
+ break;
+
+ case REQSENT:
+ case ACKSENT:
+ /* They didn't agree to what we wanted - try another request */
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ if (ret < 0)
+ f->state = STOPPED; /* kludge for stopping CCP */
+ else
+ fsm_sconfreq(f, 0); /* Send Configure-Request */
+ break;
+
+ case ACKRCVD:
+ /* Got a Nak/reject when we had already had an Ack?? oh well... */
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ fsm_sconfreq(f, 0);
+ f->state = REQSENT;
+ break;
+
+ case OPENED:
+ /* Go down and restart negotiation */
+ if (f->callbacks->down)
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ fsm_sconfreq(f, 0); /* Send initial Configure-Request */
+ f->state = REQSENT;
+ break;
+ }
+}
+
+
+/*
+ * fsm_rtermreq - Receive Terminate-Req.
+ */
+static void
+fsm_rtermreq(f, id, p, len)
+ fsm *f;
+ int id;
+ u_char *p;
+ int len;
+{
+ char str[80];
+
+ FSMDEBUG((LOG_INFO, "fsm_rtermreq(%s): Rcvd id %d.",
+ PROTO_NAME(f), id));
+
+ switch (f->state) {
+ case ACKRCVD:
+ case ACKSENT:
+ f->state = REQSENT; /* Start over but keep trying */
+ break;
+
+ case OPENED:
+ if (len > 0) {
+ fmtmsg(str, sizeof(str), "%0.*v", len, p);
+ syslog(LOG_INFO, "%s terminated by peer (%s)", PROTO_NAME(f), str);
+ } else
+ syslog(LOG_INFO, "%s terminated by peer", PROTO_NAME(f));
+ if (f->callbacks->down)
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ f->retransmits = 0;
+ f->state = STOPPING;
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+ break;
+ }
+
+ fsm_sdata(f, TERMACK, id, NULL, 0);
+}
+
+
+/*
+ * fsm_rtermack - Receive Terminate-Ack.
+ */
+static void
+fsm_rtermack(f)
+ fsm *f;
+{
+ FSMDEBUG((LOG_INFO, "fsm_rtermack(%s).", PROTO_NAME(f)));
+
+ switch (f->state) {
+ case CLOSING:
+ UNTIMEOUT(fsm_timeout, f);
+ f->state = CLOSED;
+ if( f->callbacks->finished )
+ (*f->callbacks->finished)(f);
+ break;
+ case STOPPING:
+ UNTIMEOUT(fsm_timeout, f);
+ f->state = STOPPED;
+ if( f->callbacks->finished )
+ (*f->callbacks->finished)(f);
+ break;
+
+ case ACKRCVD:
+ f->state = REQSENT;
+ break;
+
+ case OPENED:
+ if (f->callbacks->down)
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ fsm_sconfreq(f, 0);
+ break;
+ }
+}
+
+
+/*
+ * fsm_rcoderej - Receive a Code-Reject.
+ */
+static void
+fsm_rcoderej(f, inp, len)
+ fsm *f;
+ u_char *inp;
+ int len;
+{
+ u_char code, id;
+
+ FSMDEBUG((LOG_INFO, "fsm_rcoderej(%s).", PROTO_NAME(f)));
+
+ if (len < HEADERLEN) {
+ FSMDEBUG((LOG_INFO, "fsm_rcoderej: Rcvd short Code-Reject packet!"));
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ syslog(LOG_WARNING, "%s: Rcvd Code-Reject for code %d, id %d",
+ PROTO_NAME(f), code, id);
+
+ if( f->state == ACKRCVD )
+ f->state = REQSENT;
+}
+
+
+/*
+ * fsm_protreject - Peer doesn't speak this protocol.
+ *
+ * Treat this as a catastrophic error (RXJ-).
+ */
+void
+fsm_protreject(f)
+ fsm *f;
+{
+ switch( f->state ){
+ case CLOSING:
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ /* FALLTHROUGH */
+ case CLOSED:
+ f->state = CLOSED;
+ if( f->callbacks->finished )
+ (*f->callbacks->finished)(f);
+ break;
+
+ case STOPPING:
+ case REQSENT:
+ case ACKRCVD:
+ case ACKSENT:
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ /* FALLTHROUGH */
+ case STOPPED:
+ f->state = STOPPED;
+ if( f->callbacks->finished )
+ (*f->callbacks->finished)(f);
+ break;
+
+ case OPENED:
+ if( f->callbacks->down )
+ (*f->callbacks->down)(f);
+
+ /* Init restart counter, send Terminate-Request */
+ f->retransmits = f->maxtermtransmits;
+ fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
+ (u_char *) f->term_reason, f->term_reason_len);
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+ --f->retransmits;
+
+ f->state = STOPPING;
+ break;
+
+ default:
+ FSMDEBUG((LOG_INFO, "%s: Protocol-reject event in state %d!",
+ PROTO_NAME(f), f->state));
+ }
+}
+
+
+/*
+ * fsm_sconfreq - Send a Configure-Request.
+ */
+static void
+fsm_sconfreq(f, retransmit)
+ fsm *f;
+ int retransmit;
+{
+ u_char *outp;
+ int cilen;
+
+ if( f->state != REQSENT && f->state != ACKRCVD && f->state != ACKSENT ){
+ /* Not currently negotiating - reset options */
+ if( f->callbacks->resetci )
+ (*f->callbacks->resetci)(f);
+ f->nakloops = 0;
+ }
+
+ if( !retransmit ){
+ /* New request - reset retransmission counter, use new ID */
+ f->retransmits = f->maxconfreqtransmits;
+ f->reqid = ++f->id;
+ }
+
+ f->seen_ack = 0;
+
+ /*
+ * Make up the request packet
+ */
+ outp = outpacket_buf + PPP_HDRLEN + HEADERLEN;
+ if( f->callbacks->cilen && f->callbacks->addci ){
+ cilen = (*f->callbacks->cilen)(f);
+ if( cilen > peer_mru[f->unit] - HEADERLEN )
+ cilen = peer_mru[f->unit] - HEADERLEN;
+ if (f->callbacks->addci)
+ (*f->callbacks->addci)(f, outp, &cilen);
+ } else
+ cilen = 0;
+
+ /* send the request to our peer */
+ fsm_sdata(f, CONFREQ, f->reqid, outp, cilen);
+
+ /* start the retransmit timer */
+ --f->retransmits;
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+
+ FSMDEBUG((LOG_INFO, "%s: sending Configure-Request, id %d",
+ PROTO_NAME(f), f->reqid));
+}
+
+
+/*
+ * fsm_sdata - Send some data.
+ *
+ * Used for all packets sent to our peer by this module.
+ */
+void
+fsm_sdata(f, code, id, data, datalen)
+ fsm *f;
+ u_char code, id;
+ u_char *data;
+ int datalen;
+{
+ u_char *outp;
+ int outlen;
+
+ /* Adjust length to be smaller than MTU */
+ outp = outpacket_buf;
+ if (datalen > peer_mru[f->unit] - HEADERLEN)
+ datalen = peer_mru[f->unit] - HEADERLEN;
+ if (datalen && data != outp + PPP_HDRLEN + HEADERLEN)
+ BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen);
+ outlen = datalen + HEADERLEN;
+ MAKEHEADER(outp, f->protocol);
+ PUTCHAR(code, outp);
+ PUTCHAR(id, outp);
+ PUTSHORT(outlen, outp);
+ output(f->unit, outpacket_buf, outlen + PPP_HDRLEN);
+
+ FSMDEBUG((LOG_INFO, "fsm_sdata(%s): Sent code %d, id %d.",
+ PROTO_NAME(f), code, id));
+}
diff --git a/usr.sbin/pppd/fsm.h b/usr.sbin/pppd/fsm.h
new file mode 100644
index 0000000..053c833
--- /dev/null
+++ b/usr.sbin/pppd/fsm.h
@@ -0,0 +1,144 @@
+/*
+ * fsm.h - {Link, IP} Control Protocol Finite State Machine definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 Carnegie Mellon University. The name of the
+ * University 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Packet header = Code, id, length.
+ */
+#define HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short))
+
+
+/*
+ * CP (LCP, IPCP, etc.) codes.
+ */
+#define CONFREQ 1 /* Configuration Request */
+#define CONFACK 2 /* Configuration Ack */
+#define CONFNAK 3 /* Configuration Nak */
+#define CONFREJ 4 /* Configuration Reject */
+#define TERMREQ 5 /* Termination Request */
+#define TERMACK 6 /* Termination Ack */
+#define CODEREJ 7 /* Code Reject */
+
+
+/*
+ * Each FSM is described by an fsm structure and fsm callbacks.
+ */
+typedef struct fsm {
+ int unit; /* Interface unit number */
+ int protocol; /* Data Link Layer Protocol field value */
+ int state; /* State */
+ int flags; /* Contains option bits */
+ u_char id; /* Current id */
+ u_char reqid; /* Current request id */
+ u_char seen_ack; /* Have received valid Ack/Nak/Rej to Req */
+ int timeouttime; /* Timeout time in milliseconds */
+ int maxconfreqtransmits; /* Maximum Configure-Request transmissions */
+ int retransmits; /* Number of retransmissions left */
+ int maxtermtransmits; /* Maximum Terminate-Request transmissions */
+ int nakloops; /* Number of nak loops since last ack */
+ int maxnakloops; /* Maximum number of nak loops tolerated */
+ struct fsm_callbacks *callbacks; /* Callback routines */
+ char *term_reason; /* Reason for closing protocol */
+ int term_reason_len; /* Length of term_reason */
+} fsm;
+
+
+typedef struct fsm_callbacks {
+ void (*resetci) /* Reset our Configuration Information */
+ (fsm *);
+ int (*cilen) /* Length of our Configuration Information */
+ (fsm *);
+ void (*addci) /* Add our Configuration Information */
+ (fsm *, u_char *, int *);
+ int (*ackci) /* ACK our Configuration Information */
+ (fsm *, u_char *, int);
+ int (*nakci) /* NAK our Configuration Information */
+ (fsm *, u_char *, int);
+ int (*rejci) /* Reject our Configuration Information */
+ (fsm *, u_char *, int);
+ int (*reqci) /* Request peer's Configuration Information */
+ (fsm *, u_char *, int *, int);
+ void (*up) /* Called when fsm reaches OPENED state */
+ (fsm *);
+ void (*down) /* Called when fsm leaves OPENED state */
+ (fsm *);
+ void (*starting) /* Called when we want the lower layer */
+ (fsm *);
+ void (*finished) /* Called when we don't want the lower layer */
+ (fsm *);
+ void (*protreject) /* Called when Protocol-Reject received */
+ (int);
+ void (*retransmit) /* Retransmission is necessary */
+ (fsm *);
+ int (*extcode) /* Called when unknown code received */
+ (fsm *, int, int, u_char *, int);
+ char *proto_name; /* String name for protocol (for messages) */
+} fsm_callbacks;
+
+
+/*
+ * Link states.
+ */
+#define INITIAL 0 /* Down, hasn't been opened */
+#define STARTING 1 /* Down, been opened */
+#define CLOSED 2 /* Up, hasn't been opened */
+#define STOPPED 3 /* Open, waiting for down event */
+#define CLOSING 4 /* Terminating the connection, not open */
+#define STOPPING 5 /* Terminating, but open */
+#define REQSENT 6 /* We've sent a Config Request */
+#define ACKRCVD 7 /* We've received a Config Ack */
+#define ACKSENT 8 /* We've sent a Config Ack */
+#define OPENED 9 /* Connection available */
+
+
+/*
+ * Flags - indicate options controlling FSM operation
+ */
+#define OPT_PASSIVE 1 /* Don't die if we don't get a response */
+#define OPT_RESTART 2 /* Treat 2nd OPEN as DOWN, UP */
+#define OPT_SILENT 4 /* Wait for peer to speak first */
+
+
+/*
+ * Timeouts.
+ */
+#define DEFTIMEOUT 3 /* Timeout time in seconds */
+#define DEFMAXTERMREQS 2 /* Maximum Terminate-Request transmissions */
+#define DEFMAXCONFREQS 10 /* Maximum Configure-Request transmissions */
+#define DEFMAXNAKLOOPS 5 /* Maximum number of nak loops */
+
+
+/*
+ * Prototypes
+ */
+void fsm_init(fsm *);
+void fsm_lowerup(fsm *);
+void fsm_lowerdown(fsm *);
+void fsm_open(fsm *);
+void fsm_close(fsm *, char *);
+void fsm_input(fsm *, u_char *, int);
+void fsm_protreject(fsm *);
+void fsm_sdata(fsm *, int, int, u_char *, int);
+
+
+/*
+ * Variables
+ */
+extern int peer_mru[]; /* currently negotiated peer MRU (per unit) */
diff --git a/usr.sbin/pppd/ipcp.c b/usr.sbin/pppd/ipcp.c
new file mode 100644
index 0000000..3369757
--- /dev/null
+++ b/usr.sbin/pppd/ipcp.c
@@ -0,0 +1,1531 @@
+/*
+ * ipcp.c - PPP IP Control Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 Carnegie Mellon University. The name of the
+ * University 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "ipcp.h"
+#include "pathnames.h"
+
+/* global vars */
+ipcp_options ipcp_wantoptions[NUM_PPP]; /* Options that we want to request */
+ipcp_options ipcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */
+ipcp_options ipcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */
+ipcp_options ipcp_hisoptions[NUM_PPP]; /* Options that we ack'd */
+
+/* local vars */
+static int cis_received[NUM_PPP]; /* # Conf-Reqs received */
+static int default_route_set[NUM_PPP]; /* Have set up a default route */
+static int proxy_arp_set[NUM_PPP]; /* Have created proxy arp entry */
+
+/*
+ * Callbacks for fsm code. (CI = Configuration Information)
+ */
+static void ipcp_resetci(fsm *); /* Reset our CI */
+static int ipcp_cilen(fsm *); /* Return length of our CI */
+static void ipcp_addci(fsm *, u_char *, int *); /* Add our CI */
+static int ipcp_ackci(fsm *, u_char *, int); /* Peer ack'd our CI */
+static int ipcp_nakci(fsm *, u_char *, int); /* Peer nak'd our CI */
+static int ipcp_rejci(fsm *, u_char *, int); /* Peer rej'd our CI */
+static int ipcp_reqci(fsm *, u_char *, int *, int); /* Rcv CI */
+static void ipcp_up(fsm *); /* We're UP */
+static void ipcp_down(fsm *); /* We're DOWN */
+static void ipcp_script(fsm *, char *); /* Run an up/down script */
+static void ipcp_finished(fsm *); /* Don't need lower layer */
+
+fsm ipcp_fsm[NUM_PPP]; /* IPCP fsm structure */
+
+static fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */
+ ipcp_resetci, /* Reset our Configuration Information */
+ ipcp_cilen, /* Length of our Configuration Information */
+ ipcp_addci, /* Add our Configuration Information */
+ ipcp_ackci, /* ACK our Configuration Information */
+ ipcp_nakci, /* NAK our Configuration Information */
+ ipcp_rejci, /* Reject our Configuration Information */
+ ipcp_reqci, /* Request peer's Configuration Information */
+ ipcp_up, /* Called when fsm reaches OPENED state */
+ ipcp_down, /* Called when fsm leaves OPENED state */
+ NULL, /* Called when we want the lower layer up */
+ ipcp_finished, /* Called when we want the lower layer down */
+ NULL, /* Called when Protocol-Reject received */
+ NULL, /* Retransmission is necessary */
+ NULL, /* Called to handle protocol-specific codes */
+ "IPCP" /* String name of protocol */
+};
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ipcp_init(int);
+static void ipcp_open(int);
+static void ipcp_close(int, char *);
+static void ipcp_lowerup(int);
+static void ipcp_lowerdown(int);
+static void ipcp_input(int, u_char *, int);
+static void ipcp_protrej(int);
+static int ipcp_printpkt(u_char *, int,
+ void (*) (void *, char *, ...), void *);
+static void ip_check_options(void);
+static int ip_demand_conf(int);
+static int ip_active_pkt(u_char *, int);
+
+struct protent ipcp_protent = {
+ PPP_IPCP,
+ ipcp_init,
+ ipcp_input,
+ ipcp_protrej,
+ ipcp_lowerup,
+ ipcp_lowerdown,
+ ipcp_open,
+ ipcp_close,
+ ipcp_printpkt,
+ NULL,
+ 1,
+ "IPCP",
+ ip_check_options,
+ ip_demand_conf,
+ ip_active_pkt
+};
+
+static void ipcp_clear_addrs(int);
+
+/*
+ * Lengths of configuration options.
+ */
+#define CILEN_VOID 2
+#define CILEN_COMPRESS 4 /* min length for compression protocol opt. */
+#define CILEN_VJ 6 /* length for RFC1332 Van-Jacobson opt. */
+#define CILEN_ADDR 6 /* new-style single address option */
+#define CILEN_ADDRS 10 /* old-style dual address option */
+
+
+#define CODENAME(x) ((x) == CONFACK ? "ACK" : \
+ (x) == CONFNAK ? "NAK" : "REJ")
+
+
+/*
+ * Make a string representation of a network IP address.
+ */
+char *
+ip_ntoa(ipaddr)
+u_int32_t ipaddr;
+{
+ static char b[64];
+
+ ipaddr = ntohl(ipaddr);
+
+ sprintf(b, "%d.%d.%d.%d",
+ (u_char)(ipaddr >> 24),
+ (u_char)(ipaddr >> 16),
+ (u_char)(ipaddr >> 8),
+ (u_char)(ipaddr));
+ return b;
+}
+
+
+/*
+ * ipcp_init - Initialize IPCP.
+ */
+static void
+ipcp_init(unit)
+ int unit;
+{
+ fsm *f = &ipcp_fsm[unit];
+ ipcp_options *wo = &ipcp_wantoptions[unit];
+ ipcp_options *ao = &ipcp_allowoptions[unit];
+
+ f->unit = unit;
+ f->protocol = PPP_IPCP;
+ f->callbacks = &ipcp_callbacks;
+ fsm_init(&ipcp_fsm[unit]);
+
+ memset(wo, 0, sizeof(*wo));
+ memset(ao, 0, sizeof(*ao));
+
+ wo->neg_addr = 1;
+ wo->neg_vj = 1;
+ wo->vj_protocol = IPCP_VJ_COMP;
+ wo->maxslotindex = MAX_STATES - 1; /* really max index */
+ wo->cflag = 1;
+
+ /* max slots and slot-id compression are currently hardwired in */
+ /* ppp_if.c to 16 and 1, this needs to be changed (among other */
+ /* things) gmc */
+
+ ao->neg_addr = 1;
+ ao->neg_vj = 1;
+ ao->maxslotindex = MAX_STATES - 1;
+ ao->cflag = 1;
+
+ /*
+ * XXX These control whether the user may use the proxyarp
+ * and defaultroute options.
+ */
+ ao->proxy_arp = 1;
+ ao->default_route = 1;
+}
+
+
+/*
+ * ipcp_open - IPCP is allowed to come up.
+ */
+static void
+ipcp_open(unit)
+ int unit;
+{
+ fsm_open(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_close - Take IPCP down.
+ */
+static void
+ipcp_close(unit, reason)
+ int unit;
+ char *reason;
+{
+ fsm_close(&ipcp_fsm[unit], reason);
+}
+
+
+/*
+ * ipcp_lowerup - The lower layer is up.
+ */
+static void
+ipcp_lowerup(unit)
+ int unit;
+{
+ fsm_lowerup(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_lowerdown - The lower layer is down.
+ */
+static void
+ipcp_lowerdown(unit)
+ int unit;
+{
+ fsm_lowerdown(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_input - Input IPCP packet.
+ */
+static void
+ipcp_input(unit, p, len)
+ int unit;
+ u_char *p;
+ int len;
+{
+ fsm_input(&ipcp_fsm[unit], p, len);
+}
+
+
+/*
+ * ipcp_protrej - A Protocol-Reject was received for IPCP.
+ *
+ * Pretend the lower layer went down, so we shut up.
+ */
+static void
+ipcp_protrej(unit)
+ int unit;
+{
+ fsm_lowerdown(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_resetci - Reset our CI.
+ */
+static void
+ipcp_resetci(f)
+ fsm *f;
+{
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+
+ wo->req_addr = wo->neg_addr && ipcp_allowoptions[f->unit].neg_addr;
+ if (wo->ouraddr == 0)
+ wo->accept_local = 1;
+ if (wo->hisaddr == 0)
+ wo->accept_remote = 1;
+ ipcp_gotoptions[f->unit] = *wo;
+ cis_received[f->unit] = 0;
+}
+
+
+/*
+ * ipcp_cilen - Return length of our CI.
+ */
+static int
+ipcp_cilen(f)
+ fsm *f;
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+ ipcp_options *ho = &ipcp_hisoptions[f->unit];
+
+#define LENCIVJ(neg, old) (neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0)
+#define LENCIADDR(neg, old) (neg ? (old? CILEN_ADDRS : CILEN_ADDR) : 0)
+
+ /*
+ * First see if we want to change our options to the old
+ * forms because we have received old forms from the peer.
+ */
+ if (wo->neg_addr && !go->neg_addr && !go->old_addrs) {
+ /* use the old style of address negotiation */
+ go->neg_addr = 1;
+ go->old_addrs = 1;
+ }
+ if (wo->neg_vj && !go->neg_vj && !go->old_vj) {
+ /* try an older style of VJ negotiation */
+ if (cis_received[f->unit] == 0) {
+ /* keep trying the new style until we see some CI from the peer */
+ go->neg_vj = 1;
+ } else {
+ /* use the old style only if the peer did */
+ if (ho->neg_vj && ho->old_vj) {
+ go->neg_vj = 1;
+ go->old_vj = 1;
+ go->vj_protocol = ho->vj_protocol;
+ }
+ }
+ }
+
+ return (LENCIADDR(go->neg_addr, go->old_addrs) +
+ LENCIVJ(go->neg_vj, go->old_vj));
+}
+
+
+/*
+ * ipcp_addci - Add our desired CIs to a packet.
+ */
+static void
+ipcp_addci(f, ucp, lenp)
+ fsm *f;
+ u_char *ucp;
+ int *lenp;
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ int len = *lenp;
+
+#define ADDCIVJ(opt, neg, val, old, maxslotindex, cflag) \
+ if (neg) { \
+ int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \
+ if (len >= vjlen) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(vjlen, ucp); \
+ PUTSHORT(val, ucp); \
+ if (!old) { \
+ PUTCHAR(maxslotindex, ucp); \
+ PUTCHAR(cflag, ucp); \
+ } \
+ len -= vjlen; \
+ } else \
+ neg = 0; \
+ }
+
+#define ADDCIADDR(opt, neg, old, val1, val2) \
+ if (neg) { \
+ int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \
+ if (len >= addrlen) { \
+ u_int32_t l; \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(addrlen, ucp); \
+ l = ntohl(val1); \
+ PUTLONG(l, ucp); \
+ if (old) { \
+ l = ntohl(val2); \
+ PUTLONG(l, ucp); \
+ } \
+ len -= addrlen; \
+ } else \
+ neg = 0; \
+ }
+
+ ADDCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr,
+ go->old_addrs, go->ouraddr, go->hisaddr);
+
+ ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
+ go->maxslotindex, go->cflag);
+
+ *lenp -= len;
+}
+
+
+/*
+ * ipcp_ackci - Ack our CIs.
+ *
+ * Returns:
+ * 0 - Ack was bad.
+ * 1 - Ack was good.
+ */
+static int
+ipcp_ackci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ u_short cilen, citype, cishort;
+ u_int32_t cilong;
+ u_char cimaxslotindex, cicflag;
+
+ /*
+ * CIs must be in exactly the same order that we sent...
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+
+#define ACKCIVJ(opt, neg, val, old, maxslotindex, cflag) \
+ if (neg) { \
+ int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \
+ if ((len -= vjlen) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != vjlen || \
+ citype != opt) \
+ goto bad; \
+ GETSHORT(cishort, p); \
+ if (cishort != val) \
+ goto bad; \
+ if (!old) { \
+ GETCHAR(cimaxslotindex, p); \
+ if (cimaxslotindex != maxslotindex) \
+ goto bad; \
+ GETCHAR(cicflag, p); \
+ if (cicflag != cflag) \
+ goto bad; \
+ } \
+ }
+
+#define ACKCIADDR(opt, neg, old, val1, val2) \
+ if (neg) { \
+ int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \
+ u_int32_t l; \
+ if ((len -= addrlen) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != addrlen || \
+ citype != opt) \
+ goto bad; \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ if (val1 != cilong) \
+ goto bad; \
+ if (old) { \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ if (val2 != cilong) \
+ goto bad; \
+ } \
+ }
+
+ ACKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr,
+ go->old_addrs, go->ouraddr, go->hisaddr);
+
+ ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
+ go->maxslotindex, go->cflag);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ return (1);
+
+bad:
+ IPCPDEBUG((LOG_INFO, "ipcp_ackci: received bad Ack!"));
+ return (0);
+}
+
+/*
+ * ipcp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if IPCP is in the OPENED state.
+ *
+ * Returns:
+ * 0 - Nak was bad.
+ * 1 - Nak was good.
+ */
+static int
+ipcp_nakci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ u_char cimaxslotindex, cicflag;
+ u_char citype, cilen, *next;
+ u_short cishort;
+ u_int32_t ciaddr1, ciaddr2, l;
+ ipcp_options no; /* options we've seen Naks for */
+ ipcp_options try; /* options to request next time */
+
+ BZERO(&no, sizeof(no));
+ try = *go;
+
+ /*
+ * Any Nak'd CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define NAKCIADDR(opt, neg, old, code) \
+ if (go->neg && \
+ len >= (cilen = (old? CILEN_ADDRS: CILEN_ADDR)) && \
+ p[1] == cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETLONG(l, p); \
+ ciaddr1 = htonl(l); \
+ if (old) { \
+ GETLONG(l, p); \
+ ciaddr2 = htonl(l); \
+ no.old_addrs = 1; \
+ } else \
+ ciaddr2 = 0; \
+ no.neg = 1; \
+ code \
+ }
+
+#define NAKCIVJ(opt, neg, code) \
+ if (go->neg && \
+ ((cilen = p[1]) == CILEN_COMPRESS || cilen == CILEN_VJ) && \
+ len >= cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ no.neg = 1; \
+ code \
+ }
+
+ /*
+ * Accept the peer's idea of {our,his} address, if different
+ * from our idea, only if the accept_{local,remote} flag is set.
+ */
+ NAKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, go->old_addrs,
+ if (go->accept_local && ciaddr1) { /* Do we know our address? */
+ try.ouraddr = ciaddr1;
+ IPCPDEBUG((LOG_INFO, "local IP address %s",
+ ip_ntoa(ciaddr1)));
+ }
+ if (go->accept_remote && ciaddr2) { /* Does he know his? */
+ try.hisaddr = ciaddr2;
+ IPCPDEBUG((LOG_INFO, "remote IP address %s",
+ ip_ntoa(ciaddr2)));
+ }
+ );
+
+ /*
+ * Accept the peer's value of maxslotindex provided that it
+ * is less than what we asked for. Turn off slot-ID compression
+ * if the peer wants. Send old-style compress-type option if
+ * the peer wants.
+ */
+ NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
+ if (cilen == CILEN_VJ) {
+ GETCHAR(cimaxslotindex, p);
+ GETCHAR(cicflag, p);
+ if (cishort == IPCP_VJ_COMP) {
+ try.old_vj = 0;
+ if (cimaxslotindex < go->maxslotindex)
+ try.maxslotindex = cimaxslotindex;
+ if (!cicflag)
+ try.cflag = 0;
+ } else {
+ try.neg_vj = 0;
+ }
+ } else {
+ if (cishort == IPCP_VJ_COMP || cishort == IPCP_VJ_COMP_OLD) {
+ try.old_vj = 1;
+ try.vj_protocol = cishort;
+ } else {
+ try.neg_vj = 0;
+ }
+ }
+ );
+
+ /*
+ * There may be remaining CIs, if the peer is requesting negotiation
+ * on an option that we didn't include in our request packet.
+ * If they want to negotiate about IP addresses, we comply.
+ * If they want us to ask for compression, we refuse.
+ */
+ while (len > CILEN_VOID) {
+ GETCHAR(citype, p);
+ GETCHAR(cilen, p);
+ if( (len -= cilen) < 0 )
+ goto bad;
+ next = p + cilen - 2;
+
+ switch (citype) {
+ case CI_COMPRESSTYPE:
+ if (go->neg_vj || no.neg_vj ||
+ (cilen != CILEN_VJ && cilen != CILEN_COMPRESS))
+ goto bad;
+ no.neg_vj = 1;
+ break;
+ case CI_ADDRS:
+ if ((go->neg_addr && go->old_addrs) || no.old_addrs
+ || cilen != CILEN_ADDRS)
+ goto bad;
+ try.neg_addr = 1;
+ try.old_addrs = 1;
+ GETLONG(l, p);
+ ciaddr1 = htonl(l);
+ if (ciaddr1 && go->accept_local)
+ try.ouraddr = ciaddr1;
+ GETLONG(l, p);
+ ciaddr2 = htonl(l);
+ if (ciaddr2 && go->accept_remote)
+ try.hisaddr = ciaddr2;
+ no.old_addrs = 1;
+ break;
+ case CI_ADDR:
+ if (go->neg_addr || no.neg_addr || cilen != CILEN_ADDR)
+ goto bad;
+ try.old_addrs = 0;
+ GETLONG(l, p);
+ ciaddr1 = htonl(l);
+ if (ciaddr1 && go->accept_local)
+ try.ouraddr = ciaddr1;
+ if (try.ouraddr != 0)
+ try.neg_addr = 1;
+ no.neg_addr = 1;
+ break;
+ }
+ p = next;
+ }
+
+ /* If there is still anything left, this packet is bad. */
+ if (len != 0)
+ goto bad;
+
+ /*
+ * OK, the Nak is good. Now we can update state.
+ */
+ if (f->state != OPENED)
+ *go = try;
+
+ return 1;
+
+bad:
+ IPCPDEBUG((LOG_INFO, "ipcp_nakci: received bad Nak!"));
+ return 0;
+}
+
+
+/*
+ * ipcp_rejci - Reject some of our CIs.
+ */
+static int
+ipcp_rejci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ u_char cimaxslotindex, ciflag, cilen;
+ u_short cishort;
+ u_int32_t cilong;
+ ipcp_options try; /* options to request next time */
+
+ try = *go;
+ /*
+ * Any Rejected CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define REJCIADDR(opt, neg, old, val1, val2) \
+ if (go->neg && \
+ len >= (cilen = old? CILEN_ADDRS: CILEN_ADDR) && \
+ p[1] == cilen && \
+ p[0] == opt) { \
+ u_int32_t l; \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ /* Check rejected value. */ \
+ if (cilong != val1) \
+ goto bad; \
+ if (old) { \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ /* Check rejected value. */ \
+ if (cilong != val2) \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ }
+
+#define REJCIVJ(opt, neg, val, old, maxslot, cflag) \
+ if (go->neg && \
+ p[1] == (old? CILEN_COMPRESS : CILEN_VJ) && \
+ len >= p[1] && \
+ p[0] == opt) { \
+ len -= p[1]; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ /* Check rejected value. */ \
+ if (cishort != val) \
+ goto bad; \
+ if (!old) { \
+ GETCHAR(cimaxslotindex, p); \
+ if (cimaxslotindex != maxslot) \
+ goto bad; \
+ GETCHAR(ciflag, p); \
+ if (ciflag != cflag) \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ }
+
+ REJCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr,
+ go->old_addrs, go->ouraddr, go->hisaddr);
+
+ REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol, go->old_vj,
+ go->maxslotindex, go->cflag);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ /*
+ * Now we can update state.
+ */
+ if (f->state != OPENED)
+ *go = try;
+ return 1;
+
+bad:
+ IPCPDEBUG((LOG_INFO, "ipcp_rejci: received bad Reject!"));
+ return 0;
+}
+
+
+/*
+ * ipcp_reqci - Check the peer's requested CIs and send appropriate response.
+ *
+ * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
+ * appropriately. If reject_if_disagree is non-zero, doesn't return
+ * CONFNAK; returns CONFREJ if it can't return CONFACK.
+ */
+static int
+ipcp_reqci(f, inp, len, reject_if_disagree)
+ fsm *f;
+ u_char *inp; /* Requested CIs */
+ int *len; /* Length of requested CIs */
+ int reject_if_disagree;
+{
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+ ipcp_options *ho = &ipcp_hisoptions[f->unit];
+ ipcp_options *ao = &ipcp_allowoptions[f->unit];
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ u_char *cip, *next; /* Pointer to current and next CIs */
+ u_short cilen, citype; /* Parsed len, type */
+ u_short cishort; /* Parsed short value */
+ u_int32_t tl, ciaddr1, ciaddr2;/* Parsed address values */
+ int rc = CONFACK; /* Final packet return code */
+ int orc; /* Individual option return code */
+ u_char *p; /* Pointer to next char to parse */
+ u_char *ucp = inp; /* Pointer to current output char */
+ int l = *len; /* Length left */
+ u_char maxslotindex, cflag;
+ int d;
+
+ cis_received[f->unit] = 1;
+
+ /*
+ * Reset all his options.
+ */
+ BZERO(ho, sizeof(*ho));
+
+ /*
+ * Process all his options.
+ */
+ next = inp;
+ while (l) {
+ orc = CONFACK; /* Assume success */
+ cip = p = next; /* Remember begining of CI */
+ if (l < 2 || /* Not enough data for CI header or */
+ p[1] < 2 || /* CI length too small or */
+ p[1] > l) { /* CI length too big? */
+ IPCPDEBUG((LOG_INFO, "ipcp_reqci: bad CI length!"));
+ orc = CONFREJ; /* Reject bad CI */
+ cilen = l; /* Reject till end of packet */
+ l = 0; /* Don't loop again */
+ goto endswitch;
+ }
+ GETCHAR(citype, p); /* Parse CI type */
+ GETCHAR(cilen, p); /* Parse CI length */
+ l -= cilen; /* Adjust remaining length */
+ next += cilen; /* Step to next CI */
+
+ switch (citype) { /* Check CI type */
+ case CI_ADDRS:
+ IPCPDEBUG((LOG_INFO, "ipcp: received ADDRS "));
+ if (!ao->neg_addr ||
+ cilen != CILEN_ADDRS) { /* Check CI length */
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+
+ /*
+ * If he has no address, or if we both have his address but
+ * disagree about it, then NAK it with our idea.
+ * In particular, if we don't know his address, but he does,
+ * then accept it.
+ */
+ GETLONG(tl, p); /* Parse source address (his) */
+ ciaddr1 = htonl(tl);
+ IPCPDEBUG((LOG_INFO, "(%s:", ip_ntoa(ciaddr1)));
+ if (ciaddr1 != wo->hisaddr
+ && (ciaddr1 == 0 || !wo->accept_remote)) {
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(sizeof(u_int32_t), p);
+ tl = ntohl(wo->hisaddr);
+ PUTLONG(tl, p);
+ }
+ } else if (ciaddr1 == 0 && wo->hisaddr == 0) {
+ /*
+ * If neither we nor he knows his address, reject the option.
+ */
+ orc = CONFREJ;
+ wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */
+ break;
+ }
+
+ /*
+ * If he doesn't know our address, or if we both have our address
+ * but disagree about it, then NAK it with our idea.
+ */
+ GETLONG(tl, p); /* Parse desination address (ours) */
+ ciaddr2 = htonl(tl);
+ IPCPDEBUG((LOG_INFO, "%s)", ip_ntoa(ciaddr2)));
+ if (ciaddr2 != wo->ouraddr) {
+ if (ciaddr2 == 0 || !wo->accept_local) {
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(sizeof(u_int32_t), p);
+ tl = ntohl(wo->ouraddr);
+ PUTLONG(tl, p);
+ }
+ } else {
+ go->ouraddr = ciaddr2; /* accept peer's idea */
+ }
+ }
+
+ ho->neg_addr = 1;
+ ho->old_addrs = 1;
+ ho->hisaddr = ciaddr1;
+ ho->ouraddr = ciaddr2;
+ break;
+
+ case CI_ADDR:
+ IPCPDEBUG((LOG_INFO, "ipcp: received ADDR "));
+
+ if (!ao->neg_addr ||
+ cilen != CILEN_ADDR) { /* Check CI length */
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+
+ /*
+ * If he has no address, or if we both have his address but
+ * disagree about it, then NAK it with our idea.
+ * In particular, if we don't know his address, but he does,
+ * then accept it.
+ */
+ GETLONG(tl, p); /* Parse source address (his) */
+ ciaddr1 = htonl(tl);
+ IPCPDEBUG((LOG_INFO, "(%s)", ip_ntoa(ciaddr1)));
+ if (ciaddr1 != wo->hisaddr
+ && (ciaddr1 == 0 || !wo->accept_remote)) {
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(sizeof(u_int32_t), p);
+ tl = ntohl(wo->hisaddr);
+ PUTLONG(tl, p);
+ }
+ } else if (ciaddr1 == 0 && wo->hisaddr == 0) {
+ /*
+ * Don't ACK an address of 0.0.0.0 - reject it instead.
+ */
+ orc = CONFREJ;
+ wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */
+ break;
+ }
+
+ ho->neg_addr = 1;
+ ho->hisaddr = ciaddr1;
+ break;
+
+ case CI_MS_DNS1:
+ case CI_MS_DNS2:
+ /* Microsoft primary or secondary DNS request */
+ d = citype == CI_MS_DNS2;
+ IPCPDEBUG((LOG_INFO, "ipcp: received DNS%d Request ", d+1));
+
+ /* If we do not have a DNS address then we cannot send it */
+ if (ao->dnsaddr[d] == 0 ||
+ cilen != CILEN_ADDR) { /* Check CI length */
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+ GETLONG(tl, p);
+ if (htonl(tl) != ao->dnsaddr[d]) {
+ DECPTR(sizeof(u_int32_t), p);
+ tl = ntohl(ao->dnsaddr[d]);
+ PUTLONG(tl, p);
+ orc = CONFNAK;
+ }
+ break;
+
+ case CI_MS_WINS1:
+ case CI_MS_WINS2:
+ /* Microsoft primary or secondary WINS request */
+ d = citype == CI_MS_WINS2;
+ IPCPDEBUG((LOG_INFO, "ipcp: received WINS%d Request ", d+1));
+
+ /* If we do not have a DNS address then we cannot send it */
+ if (ao->winsaddr[d] == 0 ||
+ cilen != CILEN_ADDR) { /* Check CI length */
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+ GETLONG(tl, p);
+ if (htonl(tl) != ao->winsaddr[d]) {
+ DECPTR(sizeof(u_int32_t), p);
+ tl = ntohl(ao->winsaddr[d]);
+ PUTLONG(tl, p);
+ orc = CONFNAK;
+ }
+ break;
+
+ case CI_COMPRESSTYPE:
+ IPCPDEBUG((LOG_INFO, "ipcp: received COMPRESSTYPE "));
+ if (!ao->neg_vj ||
+ (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) {
+ orc = CONFREJ;
+ break;
+ }
+ GETSHORT(cishort, p);
+ IPCPDEBUG((LOG_INFO, "(%d)", cishort));
+
+ if (!(cishort == IPCP_VJ_COMP ||
+ (cishort == IPCP_VJ_COMP_OLD && cilen == CILEN_COMPRESS))) {
+ orc = CONFREJ;
+ break;
+ }
+
+ ho->neg_vj = 1;
+ ho->vj_protocol = cishort;
+ if (cilen == CILEN_VJ) {
+ GETCHAR(maxslotindex, p);
+ if (maxslotindex > ao->maxslotindex) {
+ orc = CONFNAK;
+ if (!reject_if_disagree){
+ DECPTR(1, p);
+ PUTCHAR(ao->maxslotindex, p);
+ }
+ }
+ GETCHAR(cflag, p);
+ if (cflag && !ao->cflag) {
+ orc = CONFNAK;
+ if (!reject_if_disagree){
+ DECPTR(1, p);
+ PUTCHAR(wo->cflag, p);
+ }
+ }
+ ho->maxslotindex = maxslotindex;
+ ho->cflag = cflag;
+ } else {
+ ho->old_vj = 1;
+ ho->maxslotindex = MAX_STATES - 1;
+ ho->cflag = 1;
+ }
+ break;
+
+ default:
+ orc = CONFREJ;
+ break;
+ }
+
+endswitch:
+ IPCPDEBUG((LOG_INFO, " (%s)\n", CODENAME(orc)));
+
+ if (orc == CONFACK && /* Good CI */
+ rc != CONFACK) /* but prior CI wasnt? */
+ continue; /* Don't send this one */
+
+ if (orc == CONFNAK) { /* Nak this CI? */
+ if (reject_if_disagree) /* Getting fed up with sending NAKs? */
+ orc = CONFREJ; /* Get tough if so */
+ else {
+ if (rc == CONFREJ) /* Rejecting prior CI? */
+ continue; /* Don't send this one */
+ if (rc == CONFACK) { /* Ack'd all prior CIs? */
+ rc = CONFNAK; /* Not anymore... */
+ ucp = inp; /* Backup */
+ }
+ }
+ }
+
+ if (orc == CONFREJ && /* Reject this CI */
+ rc != CONFREJ) { /* but no prior ones? */
+ rc = CONFREJ;
+ ucp = inp; /* Backup */
+ }
+
+ /* Need to move CI? */
+ if (ucp != cip)
+ BCOPY(cip, ucp, cilen); /* Move it */
+
+ /* Update output pointer */
+ INCPTR(cilen, ucp);
+ }
+
+ /*
+ * If we aren't rejecting this packet, and we want to negotiate
+ * their address, and they didn't send their address, then we
+ * send a NAK with a CI_ADDR option appended. We assume the
+ * input buffer is long enough that we can append the extra
+ * option safely.
+ */
+ if (rc != CONFREJ && !ho->neg_addr &&
+ wo->req_addr && !reject_if_disagree) {
+ if (rc == CONFACK) {
+ rc = CONFNAK;
+ ucp = inp; /* reset pointer */
+ wo->req_addr = 0; /* don't ask again */
+ }
+ PUTCHAR(CI_ADDR, ucp);
+ PUTCHAR(CILEN_ADDR, ucp);
+ tl = ntohl(wo->hisaddr);
+ PUTLONG(tl, ucp);
+ }
+
+ *len = ucp - inp; /* Compute output length */
+ IPCPDEBUG((LOG_INFO, "ipcp: returning Configure-%s", CODENAME(rc)));
+ return (rc); /* Return final code */
+}
+
+
+/*
+ * ip_check_options - check that any IP-related options are OK,
+ * and assign appropriate defaults.
+ */
+static void
+ip_check_options()
+{
+ struct hostent *hp;
+ u_int32_t local;
+ ipcp_options *wo = &ipcp_wantoptions[0];
+
+ /*
+ * Default our local IP address based on our hostname.
+ * If local IP address already given, don't bother.
+ */
+ if (wo->ouraddr == 0 && !disable_defaultip) {
+ /*
+ * Look up our hostname (possibly with domain name appended)
+ * and take the first IP address as our local IP address.
+ * If there isn't an IP address for our hostname, too bad.
+ */
+ wo->accept_local = 1; /* don't insist on this default value */
+ if ((hp = gethostbyname(hostname)) != NULL) {
+ local = *(u_int32_t *)hp->h_addr;
+ if (local != 0 && !bad_ip_adrs(local))
+ wo->ouraddr = local;
+ }
+ }
+
+ if (demand && wo->hisaddr == 0) {
+ option_error("remote IP address required for demand-dialling\n");
+ exit(1);
+ }
+#if 0
+ if (demand && wo->accept_remote) {
+ option_error("ipcp-accept-remote is incompatible with demand\n");
+ exit(1);
+ }
+#endif
+}
+
+
+/*
+ * ip_demand_conf - configure the interface as though
+ * IPCP were up, for use with dial-on-demand.
+ */
+static int
+ip_demand_conf(u)
+ int u;
+{
+ ipcp_options *wo = &ipcp_wantoptions[u];
+
+ if (!sifaddr(u, wo->ouraddr, wo->hisaddr, GetMask(wo->ouraddr)))
+ return 0;
+ if (!sifup(u))
+ return 0;
+ if (!sifnpmode(u, PPP_IP, NPMODE_QUEUE))
+ return 0;
+ if (wo->default_route)
+ if (sifdefaultroute(u, wo->ouraddr, wo->hisaddr))
+ default_route_set[u] = 1;
+ if (wo->proxy_arp)
+ if (sifproxyarp(u, wo->hisaddr))
+ proxy_arp_set[u] = 1;
+
+ syslog(LOG_NOTICE, "local IP address %s", ip_ntoa(wo->ouraddr));
+ syslog(LOG_NOTICE, "remote IP address %s", ip_ntoa(wo->hisaddr));
+
+ return 1;
+}
+
+
+/*
+ * ipcp_up - IPCP has come UP.
+ *
+ * Configure the IP network interface appropriately and bring it up.
+ */
+static void
+ipcp_up(f)
+ fsm *f;
+{
+ u_int32_t mask;
+ ipcp_options *ho = &ipcp_hisoptions[f->unit];
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+
+ np_up(f->unit, PPP_IP);
+ IPCPDEBUG((LOG_INFO, "ipcp: up"));
+
+ /*
+ * We must have a non-zero IP address for both ends of the link.
+ */
+ if (!ho->neg_addr)
+ ho->hisaddr = wo->hisaddr;
+
+ if (ho->hisaddr == 0) {
+ syslog(LOG_ERR, "Could not determine remote IP address");
+ ipcp_close(f->unit, "Could not determine remote IP address");
+ return;
+ }
+ if (go->ouraddr == 0) {
+ syslog(LOG_ERR, "Could not determine local IP address");
+ ipcp_close(f->unit, "Could not determine local IP address");
+ return;
+ }
+ script_setenv("IPLOCAL", ip_ntoa(go->ouraddr));
+ script_setenv("IPREMOTE", ip_ntoa(ho->hisaddr));
+
+ /*
+ * Check that the peer is allowed to use the IP address it wants.
+ */
+ if (!auth_ip_addr(f->unit, ho->hisaddr)) {
+ syslog(LOG_ERR, "Peer is not authorized to use remote address %s",
+ ip_ntoa(ho->hisaddr));
+ ipcp_close(f->unit, "Unauthorized remote IP address");
+ return;
+ }
+
+ /* set tcp compression */
+ sifvjcomp(f->unit, ho->neg_vj, ho->cflag, ho->maxslotindex);
+
+ /*
+ * If we are doing dial-on-demand, the interface is already
+ * configured, so we put out any saved-up packets, then set the
+ * interface to pass IP packets.
+ */
+ if (demand) {
+ if (go->ouraddr != wo->ouraddr || ho->hisaddr != wo->hisaddr) {
+ if (go->ouraddr != wo->ouraddr)
+ syslog(LOG_WARNING, "Local IP address changed to %s",
+ ip_ntoa(go->ouraddr));
+ if (ho->hisaddr != wo->hisaddr)
+ syslog(LOG_WARNING, "Remote IP address changed to %s",
+ ip_ntoa(ho->hisaddr));
+ ipcp_clear_addrs(f->unit);
+
+ /* Set the interface to the new addresses */
+ mask = GetMask(go->ouraddr);
+ if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) {
+ IPCPDEBUG((LOG_WARNING, "sifaddr failed"));
+ ipcp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+
+ /* assign a default route through the interface if required */
+ if (ipcp_wantoptions[f->unit].default_route)
+ if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr))
+ default_route_set[f->unit] = 1;
+
+ /* Make a proxy ARP entry if requested. */
+ if (ipcp_wantoptions[f->unit].proxy_arp)
+ if (sifproxyarp(f->unit, ho->hisaddr))
+ proxy_arp_set[f->unit] = 1;
+
+ }
+ demand_rexmit(PPP_IP);
+ sifnpmode(f->unit, PPP_IP, NPMODE_PASS);
+
+ } else {
+ /*
+ * Set IP addresses and (if specified) netmask.
+ */
+ mask = GetMask(go->ouraddr);
+
+#if !(defined(SVR4) && (defined(SNI) || defined(__USLC__)))
+ if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) {
+ IPCPDEBUG((LOG_WARNING, "sifaddr failed"));
+ ipcp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+#endif
+
+ /* bring the interface up for IP */
+ if (!sifup(f->unit)) {
+ IPCPDEBUG((LOG_WARNING, "sifup failed"));
+ ipcp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+
+#if (defined(SVR4) && (defined(SNI) || defined(__USLC__)))
+ if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask)) {
+ IPCPDEBUG((LOG_WARNING, "sifaddr failed"));
+ ipcp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+#endif
+ sifnpmode(f->unit, PPP_IP, NPMODE_PASS);
+
+ /* assign a default route through the interface if required */
+ if (ipcp_wantoptions[f->unit].default_route)
+ if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr))
+ default_route_set[f->unit] = 1;
+
+ /* Make a proxy ARP entry if requested. */
+ if (ipcp_wantoptions[f->unit].proxy_arp)
+ if (sifproxyarp(f->unit, ho->hisaddr))
+ proxy_arp_set[f->unit] = 1;
+
+ syslog(LOG_NOTICE, "local IP address %s", ip_ntoa(go->ouraddr));
+ syslog(LOG_NOTICE, "remote IP address %s", ip_ntoa(ho->hisaddr));
+ }
+
+ /*
+ * Execute the ip-up script, like this:
+ * /etc/ppp/ip-up interface tty speed local-IP remote-IP
+ */
+ ipcp_script(f, _PATH_IPUP);
+
+}
+
+
+/*
+ * ipcp_down - IPCP has gone DOWN.
+ *
+ * Take the IP network interface down, clear its addresses
+ * and delete routes through it.
+ */
+static void
+ipcp_down(f)
+ fsm *f;
+{
+ IPCPDEBUG((LOG_INFO, "ipcp: down"));
+ np_down(f->unit, PPP_IP);
+ sifvjcomp(f->unit, 0, 0, 0);
+
+ /*
+ * If we are doing dial-on-demand, set the interface
+ * to queue up outgoing packets (for now).
+ */
+ if (demand) {
+ sifnpmode(f->unit, PPP_IP, NPMODE_QUEUE);
+ } else {
+ sifdown(f->unit);
+ ipcp_clear_addrs(f->unit);
+ }
+
+ /* Execute the ip-down script */
+ ipcp_script(f, _PATH_IPDOWN);
+}
+
+
+/*
+ * ipcp_clear_addrs() - clear the interface addresses, routes,
+ * proxy arp entries, etc.
+ */
+static void
+ipcp_clear_addrs(unit)
+ int unit;
+{
+ u_int32_t ouraddr, hisaddr;
+
+ ouraddr = ipcp_gotoptions[unit].ouraddr;
+ hisaddr = ipcp_hisoptions[unit].hisaddr;
+ if (proxy_arp_set[unit]) {
+ cifproxyarp(unit, hisaddr);
+ proxy_arp_set[unit] = 0;
+ }
+ if (default_route_set[unit]) {
+ cifdefaultroute(unit, ouraddr, hisaddr);
+ default_route_set[unit] = 0;
+ }
+ cifaddr(unit, ouraddr, hisaddr);
+}
+
+
+/*
+ * ipcp_finished - possibly shut down the lower layers.
+ */
+static void
+ipcp_finished(f)
+ fsm *f;
+{
+ np_finished(f->unit, PPP_IP);
+}
+
+
+/*
+ * ipcp_script - Execute a script with arguments
+ * interface-name tty-name speed local-IP remote-IP.
+ */
+static void
+ipcp_script(f, script)
+ fsm *f;
+ char *script;
+{
+ char strspeed[32], strlocal[32], strremote[32];
+ char *argv[8];
+
+ sprintf(strspeed, "%d", baud_rate);
+ strcpy(strlocal, ip_ntoa(ipcp_gotoptions[f->unit].ouraddr));
+ strcpy(strremote, ip_ntoa(ipcp_hisoptions[f->unit].hisaddr));
+
+ argv[0] = script;
+ argv[1] = ifname;
+ argv[2] = devnam;
+ argv[3] = strspeed;
+ argv[4] = strlocal;
+ argv[5] = strremote;
+ argv[6] = ipparam;
+ argv[7] = NULL;
+ run_program(script, argv, 0);
+}
+
+/*
+ * ipcp_printpkt - print the contents of an IPCP packet.
+ */
+static char *ipcp_codenames[] = {
+ "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+ "TermReq", "TermAck", "CodeRej"
+};
+
+static int
+ipcp_printpkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer)(void *, char *, ...);
+ void *arg;
+{
+ int code, id, len, olen;
+ u_char *pstart, *optend;
+ u_short cishort;
+ u_int32_t cilong;
+
+ if (plen < HEADERLEN)
+ return 0;
+ pstart = p;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < HEADERLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= sizeof(ipcp_codenames) / sizeof(char *))
+ printer(arg, " %s", ipcp_codenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+ printer(arg, " id=0x%x", id);
+ len -= HEADERLEN;
+ switch (code) {
+ case CONFREQ:
+ case CONFACK:
+ case CONFNAK:
+ case CONFREJ:
+ /* print option list */
+ while (len >= 2) {
+ GETCHAR(code, p);
+ GETCHAR(olen, p);
+ p -= 2;
+ if (olen < 2 || olen > len) {
+ break;
+ }
+ printer(arg, " <");
+ len -= olen;
+ optend = p + olen;
+ switch (code) {
+ case CI_ADDRS:
+ if (olen == CILEN_ADDRS) {
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "addrs %I", htonl(cilong));
+ GETLONG(cilong, p);
+ printer(arg, " %I", htonl(cilong));
+ }
+ break;
+ case CI_COMPRESSTYPE:
+ if (olen >= CILEN_COMPRESS) {
+ p += 2;
+ GETSHORT(cishort, p);
+ printer(arg, "compress ");
+ switch (cishort) {
+ case IPCP_VJ_COMP:
+ printer(arg, "VJ");
+ break;
+ case IPCP_VJ_COMP_OLD:
+ printer(arg, "old-VJ");
+ break;
+ default:
+ printer(arg, "0x%x", cishort);
+ }
+ }
+ break;
+ case CI_ADDR:
+ if (olen == CILEN_ADDR) {
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "addr %I", htonl(cilong));
+ }
+ break;
+ case CI_MS_DNS1:
+ case CI_MS_DNS2:
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "ms-dns %I", htonl(cilong));
+ break;
+ case CI_MS_WINS1:
+ case CI_MS_WINS2:
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "ms-wins %I", htonl(cilong));
+ break;
+ }
+ while (p < optend) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+ printer(arg, ">");
+ }
+ break;
+
+ case TERMACK:
+ case TERMREQ:
+ if (len > 0 && *p >= ' ' && *p < 0x7f) {
+ printer(arg, " ");
+ print_string(p, len, printer, arg);
+ p += len;
+ len = 0;
+ }
+ break;
+ }
+
+ /* print the rest of the bytes in the packet */
+ for (; len > 0; --len) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+
+ return p - pstart;
+}
+
+/*
+ * ip_active_pkt - see if this IP packet is worth bringing the link up for.
+ * We don't bring the link up for IP fragments or for TCP FIN packets
+ * with no data.
+ */
+#define IP_HDRLEN 20 /* bytes */
+#define IP_OFFMASK 0x1fff
+#define IPPROTO_TCP 6
+#define TCP_HDRLEN 20
+#define TH_FIN 0x01
+
+/*
+ * We use these macros because the IP header may be at an odd address,
+ * and some compilers might use word loads to get th_off or ip_hl.
+ */
+
+#define net_short(x) (((x)[0] << 8) + (x)[1])
+#define get_iphl(x) (((unsigned char *)(x))[0] & 0xF)
+#define get_ipoff(x) net_short((unsigned char *)(x) + 6)
+#define get_ipproto(x) (((unsigned char *)(x))[9])
+#define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4)
+#define get_tcpflags(x) (((unsigned char *)(x))[13])
+
+static int
+ip_active_pkt(pkt, len)
+ u_char *pkt;
+ int len;
+{
+ u_char *tcp;
+ int hlen;
+
+ len -= PPP_HDRLEN;
+ pkt += PPP_HDRLEN;
+ if (len < IP_HDRLEN)
+ return 0;
+ if ((get_ipoff(pkt) & IP_OFFMASK) != 0)
+ return 0;
+ if (get_ipproto(pkt) != IPPROTO_TCP)
+ return 1;
+ hlen = get_iphl(pkt) * 4;
+ if (len < hlen + TCP_HDRLEN)
+ return 0;
+ tcp = pkt + hlen;
+ if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == hlen + get_tcpoff(tcp) * 4)
+ return 0;
+ return 1;
+}
diff --git a/usr.sbin/pppd/ipcp.h b/usr.sbin/pppd/ipcp.h
new file mode 100644
index 0000000..73f8b7e
--- /dev/null
+++ b/usr.sbin/pppd/ipcp.h
@@ -0,0 +1,70 @@
+/*
+ * ipcp.h - IP Control Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 Carnegie Mellon University. The name of the
+ * University 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Options.
+ */
+#define CI_ADDRS 1 /* IP Addresses */
+#define CI_COMPRESSTYPE 2 /* Compression Type */
+#define CI_ADDR 3
+
+#define CI_MS_DNS1 129 /* Primary DNS value */
+#define CI_MS_WINS1 130 /* Primary WINS value */
+#define CI_MS_DNS2 131 /* Secondary DNS value */
+#define CI_MS_WINS2 132 /* Secondary WINS value */
+
+#define MAX_STATES 16 /* from slcompress.h */
+
+#define IPCP_VJMODE_OLD 1 /* "old" mode (option # = 0x0037) */
+#define IPCP_VJMODE_RFC1172 2 /* "old-rfc"mode (option # = 0x002d) */
+#define IPCP_VJMODE_RFC1332 3 /* "new-rfc"mode (option # = 0x002d, */
+ /* maxslot and slot number compression) */
+
+#define IPCP_VJ_COMP 0x002d /* current value for VJ compression option*/
+#define IPCP_VJ_COMP_OLD 0x0037 /* "old" (i.e, broken) value for VJ */
+ /* compression option*/
+
+typedef struct ipcp_options {
+ int neg_addr : 1; /* Negotiate IP Address? */
+ int old_addrs : 1; /* Use old (IP-Addresses) option? */
+ int req_addr : 1; /* Ask peer to send IP address? */
+ int default_route : 1; /* Assign default route through interface? */
+ int proxy_arp : 1; /* Make proxy ARP entry for peer? */
+ int neg_vj : 1; /* Van Jacobson Compression? */
+ int old_vj : 1; /* use old (short) form of VJ option? */
+ int accept_local : 1; /* accept peer's value for ouraddr */
+ int accept_remote : 1; /* accept peer's value for hisaddr */
+ u_short vj_protocol; /* protocol value to use in VJ option */
+ u_char maxslotindex, cflag; /* values for RFC1332 VJ compression neg. */
+ u_int32_t ouraddr, hisaddr; /* Addresses in NETWORK BYTE ORDER */
+ u_int32_t dnsaddr[2]; /* Primary and secondary MS DNS entries */
+ u_int32_t winsaddr[2]; /* Primary and secondary MS WINS entries */
+} ipcp_options;
+
+extern fsm ipcp_fsm[];
+extern ipcp_options ipcp_wantoptions[];
+extern ipcp_options ipcp_gotoptions[];
+extern ipcp_options ipcp_allowoptions[];
+extern ipcp_options ipcp_hisoptions[];
+
+char *ip_ntoa(u_int32_t);
+
+extern struct protent ipcp_protent;
diff --git a/usr.sbin/pppd/ipv6cp.c b/usr.sbin/pppd/ipv6cp.c
new file mode 100644
index 0000000..27ba3a9
--- /dev/null
+++ b/usr.sbin/pppd/ipv6cp.c
@@ -0,0 +1,1422 @@
+/*
+ ipv6cp.c - PPP IPV6 Control Protocol.
+ Copyright (C) 1999 Tommi Komulainen <Tommi.Komulainen@iki.fi>
+
+ Redistribution and use in source and binary forms are permitted
+ provided that the above copyright notice and this paragraph are
+ duplicated in all such forms. 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 WITHOUT ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+*/
+
+/* Original version, based on RFC2023 :
+
+ Copyright (c) 1995, 1996, 1997 Francis.Dupont@inria.fr, INRIA Rocquencourt,
+ Alain.Durand@imag.fr, IMAG,
+ Jean-Luc.Richier@imag.fr, IMAG-LSR.
+
+ Copyright (c) 1998, 1999 Francis.Dupont@inria.fr, GIE DYADE,
+ Alain.Durand@imag.fr, IMAG,
+ Jean-Luc.Richier@imag.fr, IMAG-LSR.
+
+ Ce travail a été fait au sein du GIE DYADE (Groupement d'Intérêt
+ Économique ayant pour membres BULL S.A. et l'INRIA).
+
+ Ce logiciel informatique est disponible aux conditions
+ usuelles dans la recherche, c'est-à-dire qu'il peut
+ être utilisé, copié, modifié, distribué à l'unique
+ condition que ce texte soit conservé afin que
+ l'origine de ce logiciel soit reconnue.
+
+ Le nom de l'Institut National de Recherche en Informatique
+ et en Automatique (INRIA), de l'IMAG, ou d'une personne morale
+ ou physique ayant participé à l'élaboration de ce logiciel ne peut
+ être utilisé sans son accord préalable explicite.
+
+ Ce logiciel est fourni tel quel sans aucune garantie,
+ support ou responsabilité d'aucune sorte.
+ Ce logiciel est dérivé de sources d'origine
+ "University of California at Berkeley" et
+ "Digital Equipment Corporation" couvertes par des copyrights.
+
+ L'Institut d'Informatique et de Mathématiques Appliquées de Grenoble (IMAG)
+ est une fédération d'unités mixtes de recherche du CNRS, de l'Institut National
+ Polytechnique de Grenoble et de l'Université Joseph Fourier regroupant
+ sept laboratoires dont le laboratoire Logiciels, Systèmes, Réseaux (LSR).
+
+ This work has been done in the context of GIE DYADE (joint R & D venture
+ between BULL S.A. and INRIA).
+
+ This software is available with usual "research" terms
+ with the aim of retain credits of the software.
+ Permission to use, copy, modify and distribute this software for any
+ purpose and without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies,
+ and the name of INRIA, IMAG, or any contributor not be used in advertising
+ or publicity pertaining to this material without the prior explicit
+ permission. The software is provided "as is" without any
+ warranties, support or liabilities of any kind.
+ This software is derived from source code from
+ "University of California at Berkeley" and
+ "Digital Equipment Corporation" protected by copyrights.
+
+ Grenoble's Institute of Computer Science and Applied Mathematics (IMAG)
+ is a federation of seven research units funded by the CNRS, National
+ Polytechnic Institute of Grenoble and University Joseph Fourier.
+ The research unit in Software, Systems, Networks (LSR) is member of IMAG.
+*/
+
+/*
+ * Derived from :
+ *
+ *
+ * ipcp.c - PPP IP Control Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 Carnegie Mellon University. The name of the
+ * University 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: ipv6cp.c,v 1.7 1999/10/08 01:08:18 masputra Exp $
+ */
+
+#ifndef lint
+#define RCSID "$Id: ipv6cp.c,v 1.7 1999/10/08 01:08:18 masputra Exp $"
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * TODO:
+ *
+ * Proxy Neighbour Discovery.
+ *
+ * Better defines for selecting the ordering of
+ * interface up / set address. (currently checks for __linux__,
+ * since SVR4 && (SNI || __USLC__) didn't work properly)
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "ipcp.h"
+#include "ipv6cp.h"
+#include "magic.h"
+#include "pathnames.h"
+
+#define s6_addr32 __u6_addr.__u6_addr32
+
+#ifdef RCSID
+static const char rcsid[] = RCSID;
+#endif
+
+/* global vars */
+ipv6cp_options ipv6cp_wantoptions[NUM_PPP]; /* Options that we want to request */
+ipv6cp_options ipv6cp_gotoptions[NUM_PPP]; /* Options that peer ack'd */
+ipv6cp_options ipv6cp_allowoptions[NUM_PPP]; /* Options we allow peer to request */
+ipv6cp_options ipv6cp_hisoptions[NUM_PPP]; /* Options that we ack'd */
+int no_ifaceid_neg = 0;
+
+/* local vars */
+static int ipv6cp_is_up;
+
+/*
+ * Callbacks for fsm code. (CI = Configuration Information)
+ */
+static void ipv6cp_resetci(fsm *); /* Reset our CI */
+static int ipv6cp_cilen(fsm *); /* Return length of our CI */
+static void ipv6cp_addci(fsm *, u_char *, int *); /* Add our CI */
+static int ipv6cp_ackci(fsm *, u_char *, int); /* Peer ack'd our CI */
+static int ipv6cp_nakci(fsm *, u_char *, int); /* Peer nak'd our CI */
+static int ipv6cp_rejci(fsm *, u_char *, int); /* Peer rej'd our CI */
+static int ipv6cp_reqci(fsm *, u_char *, int *, int); /* Rcv CI */
+static void ipv6cp_up(fsm *); /* We're UP */
+static void ipv6cp_down(fsm *); /* We're DOWN */
+static void ipv6cp_finished(fsm *); /* Don't need lower layer */
+
+fsm ipv6cp_fsm[NUM_PPP]; /* IPV6CP fsm structure */
+
+static fsm_callbacks ipv6cp_callbacks = { /* IPV6CP callback routines */
+ ipv6cp_resetci, /* Reset our Configuration Information */
+ ipv6cp_cilen, /* Length of our Configuration Information */
+ ipv6cp_addci, /* Add our Configuration Information */
+ ipv6cp_ackci, /* ACK our Configuration Information */
+ ipv6cp_nakci, /* NAK our Configuration Information */
+ ipv6cp_rejci, /* Reject our Configuration Information */
+ ipv6cp_reqci, /* Request peer's Configuration Information */
+ ipv6cp_up, /* Called when fsm reaches OPENED state */
+ ipv6cp_down, /* Called when fsm leaves OPENED state */
+ NULL, /* Called when we want the lower layer up */
+ ipv6cp_finished, /* Called when we want the lower layer down */
+ NULL, /* Called when Protocol-Reject received */
+ NULL, /* Retransmission is necessary */
+ NULL, /* Called to handle protocol-specific codes */
+ "IPV6CP" /* String name of protocol */
+};
+
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ipv6cp_init(int);
+static void ipv6cp_open(int);
+static void ipv6cp_close(int, char *);
+static void ipv6cp_lowerup(int);
+static void ipv6cp_lowerdown(int);
+static void ipv6cp_input(int, u_char *, int);
+static void ipv6cp_protrej(int);
+static int ipv6cp_printpkt(u_char *, int,
+ void (*)(void *, char *, ...), void *);
+static void ipv6_check_options(void);
+static int ipv6_demand_conf(int);
+static int ipv6_active_pkt(u_char *, int);
+
+struct protent ipv6cp_protent = {
+ PPP_IPV6CP,
+ ipv6cp_init,
+ ipv6cp_input,
+ ipv6cp_protrej,
+ ipv6cp_lowerup,
+ ipv6cp_lowerdown,
+ ipv6cp_open,
+ ipv6cp_close,
+ ipv6cp_printpkt,
+ NULL,
+ 0,
+ "IPV6CP",
+ ipv6_check_options,
+ ipv6_demand_conf,
+ ipv6_active_pkt
+};
+
+static void ipv6cp_clear_addrs(int, eui64_t, eui64_t);
+static void ipv6cp_script(char *);
+
+/*
+ * Lengths of configuration options.
+ */
+#define CILEN_VOID 2
+#define CILEN_COMPRESS 4 /* length for RFC2023 compress opt. */
+#define CILEN_IFACEID 10 /* RFC2472, interface identifier */
+
+#define CODENAME(x) ((x) == CONFACK ? "ACK" : \
+ (x) == CONFNAK ? "NAK" : "REJ")
+
+/*
+ * This state variable is used to ensure that we don't
+ * run an ipcp-up/down script while one is already running.
+ */
+static enum script_state {
+ s_down,
+ s_up,
+} ipv6cp_script_state;
+
+/*
+ * setifaceid - set the interface identifiers manually
+ */
+int
+setifaceid(argv)
+ char **argv;
+{
+ char *comma, *arg;
+ ipv6cp_options *wo = &ipv6cp_wantoptions[0];
+ struct in6_addr addr;
+
+#define VALIDID(a) ( (((a).s6_addr32[0] == 0) && ((a).s6_addr32[1] == 0)) && \
+ (((a).s6_addr32[2] != 0) || ((a).s6_addr32[3] != 0)) )
+
+ arg = *argv;
+ if ((comma = strchr(arg, ',')) == NULL)
+ comma = arg + strlen(arg);
+
+ /*
+ * If comma first character, then no local identifier
+ */
+ if (comma != arg) {
+ *comma = '\0';
+
+ if (inet_pton(AF_INET6, arg, &addr) == 0 || !VALIDID(addr)) {
+ option_error("Illegal interface identifier (local): %s", arg);
+ return 0;
+ }
+
+ eui64_copy(addr.s6_addr32[2], wo->ourid);
+ wo->opt_local = 1;
+ *comma = ',';
+ }
+
+ /*
+ * If comma last character, the no remote identifier
+ */
+ if (*comma != 0 && *++comma != '\0') {
+ if (inet_pton(AF_INET6, comma, &addr) == 0 || !VALIDID(addr)) {
+ option_error("Illegal interface identifier (remote): %s", comma);
+ return 0;
+ }
+ eui64_copy(addr.s6_addr32[2], wo->hisid);
+ wo->opt_remote = 1;
+ }
+
+ ipv6cp_protent.enabled_flag = 1;
+ return 1;
+}
+
+/*
+ * Make a string representation of a network address.
+ */
+char *
+llv6_ntoa(ifaceid)
+ eui64_t ifaceid;
+{
+ static char b[64];
+
+ sprintf(b, "fe80::%s", eui64_ntoa(ifaceid));
+ return b;
+}
+
+
+/*
+ * ipv6cp_init - Initialize IPV6CP.
+ */
+static void
+ipv6cp_init(unit)
+ int unit;
+{
+ fsm *f = &ipv6cp_fsm[unit];
+ ipv6cp_options *wo = &ipv6cp_wantoptions[unit];
+ ipv6cp_options *ao = &ipv6cp_allowoptions[unit];
+
+ f->unit = unit;
+ f->protocol = PPP_IPV6CP;
+ f->callbacks = &ipv6cp_callbacks;
+ fsm_init(&ipv6cp_fsm[unit]);
+
+ memset(wo, 0, sizeof(*wo));
+ memset(ao, 0, sizeof(*ao));
+
+ wo->accept_local = 1;
+ wo->neg_ifaceid = 1;
+ ao->neg_ifaceid = 1;
+
+#ifdef IPV6CP_COMP
+ wo->neg_vj = 1;
+ ao->neg_vj = 1;
+ wo->vj_protocol = IPV6CP_COMP;
+#endif
+
+}
+
+
+/*
+ * ipv6cp_open - IPV6CP is allowed to come up.
+ */
+static void
+ipv6cp_open(unit)
+ int unit;
+{
+ fsm_open(&ipv6cp_fsm[unit]);
+}
+
+
+/*
+ * ipv6cp_close - Take IPV6CP down.
+ */
+static void
+ipv6cp_close(unit, reason)
+ int unit;
+ char *reason;
+{
+ fsm_close(&ipv6cp_fsm[unit], reason);
+}
+
+
+/*
+ * ipv6cp_lowerup - The lower layer is up.
+ */
+static void
+ipv6cp_lowerup(unit)
+ int unit;
+{
+ fsm_lowerup(&ipv6cp_fsm[unit]);
+}
+
+
+/*
+ * ipv6cp_lowerdown - The lower layer is down.
+ */
+static void
+ipv6cp_lowerdown(unit)
+ int unit;
+{
+ fsm_lowerdown(&ipv6cp_fsm[unit]);
+}
+
+
+/*
+ * ipv6cp_input - Input IPV6CP packet.
+ */
+static void
+ipv6cp_input(unit, p, len)
+ int unit;
+ u_char *p;
+ int len;
+{
+ fsm_input(&ipv6cp_fsm[unit], p, len);
+}
+
+
+/*
+ * ipv6cp_protrej - A Protocol-Reject was received for IPV6CP.
+ *
+ * Pretend the lower layer went down, so we shut up.
+ */
+static void
+ipv6cp_protrej(unit)
+ int unit;
+{
+ fsm_lowerdown(&ipv6cp_fsm[unit]);
+}
+
+
+/*
+ * ipv6cp_resetci - Reset our CI.
+ */
+static void
+ipv6cp_resetci(f)
+ fsm *f;
+{
+ ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit];
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+
+ wo->req_ifaceid = wo->neg_ifaceid && ipv6cp_allowoptions[f->unit].neg_ifaceid;
+
+ if (!wo->opt_local) {
+ eui64_magic_nz(wo->ourid);
+ }
+
+ *go = *wo;
+ eui64_zero(go->hisid); /* last proposed interface identifier */
+}
+
+
+/*
+ * ipv6cp_cilen - Return length of our CI.
+ */
+static int
+ipv6cp_cilen(f)
+ fsm *f;
+{
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+
+#define LENCIVJ(neg) (neg ? CILEN_COMPRESS : 0)
+#define LENCIIFACEID(neg) (neg ? CILEN_IFACEID : 0)
+
+ return (LENCIIFACEID(go->neg_ifaceid) +
+ LENCIVJ(go->neg_vj));
+}
+
+
+/*
+ * ipv6cp_addci - Add our desired CIs to a packet.
+ */
+static void
+ipv6cp_addci(f, ucp, lenp)
+ fsm *f;
+ u_char *ucp;
+ int *lenp;
+{
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+ int len = *lenp;
+
+#define ADDCIVJ(opt, neg, val) \
+ if (neg) { \
+ int vjlen = CILEN_COMPRESS; \
+ if (len >= vjlen) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(vjlen, ucp); \
+ PUTSHORT(val, ucp); \
+ len -= vjlen; \
+ } else \
+ neg = 0; \
+ }
+
+#define ADDCIIFACEID(opt, neg, val1) \
+ if (neg) { \
+ int idlen = CILEN_IFACEID; \
+ if (len >= idlen) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(idlen, ucp); \
+ eui64_put(val1, ucp); \
+ len -= idlen; \
+ } else \
+ neg = 0; \
+ }
+
+ ADDCIIFACEID(CI_IFACEID, go->neg_ifaceid, go->ourid);
+
+ ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol);
+
+ *lenp -= len;
+}
+
+
+/*
+ * ipv6cp_ackci - Ack our CIs.
+ *
+ * Returns:
+ * 0 - Ack was bad.
+ * 1 - Ack was good.
+ */
+static int
+ipv6cp_ackci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+ u_short cilen, citype, cishort;
+ eui64_t ifaceid;
+
+ /*
+ * CIs must be in exactly the same order that we sent...
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+
+#define ACKCIVJ(opt, neg, val) \
+ if (neg) { \
+ int vjlen = CILEN_COMPRESS; \
+ if ((len -= vjlen) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != vjlen || \
+ citype != opt) \
+ goto bad; \
+ GETSHORT(cishort, p); \
+ if (cishort != val) \
+ goto bad; \
+ }
+
+#define ACKCIIFACEID(opt, neg, val1) \
+ if (neg) { \
+ int idlen = CILEN_IFACEID; \
+ if ((len -= idlen) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != idlen || \
+ citype != opt) \
+ goto bad; \
+ eui64_get(ifaceid, p); \
+ if (! eui64_equals(val1, ifaceid)) \
+ goto bad; \
+ }
+
+ ACKCIIFACEID(CI_IFACEID, go->neg_ifaceid, go->ourid);
+
+ ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ return (1);
+
+bad:
+ IPV6CPDEBUG(("ipv6cp_ackci: received bad Ack!"));
+ return (0);
+}
+
+/*
+ * ipv6cp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if IPV6CP is in the OPENED state.
+ *
+ * Returns:
+ * 0 - Nak was bad.
+ * 1 - Nak was good.
+ */
+static int
+ipv6cp_nakci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+ u_char citype, cilen, *next;
+ u_short cishort;
+ eui64_t ifaceid;
+ ipv6cp_options no; /* options we've seen Naks for */
+ ipv6cp_options try; /* options to request next time */
+
+ BZERO(&no, sizeof(no));
+ try = *go;
+
+ /*
+ * Any Nak'd CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define NAKCIIFACEID(opt, neg, code) \
+ if (go->neg && \
+ len >= (cilen = CILEN_IFACEID) && \
+ p[1] == cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ eui64_get(ifaceid, p); \
+ no.neg = 1; \
+ code \
+ }
+
+#define NAKCIVJ(opt, neg, code) \
+ if (go->neg && \
+ ((cilen = p[1]) == CILEN_COMPRESS) && \
+ len >= cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ no.neg = 1; \
+ code \
+ }
+
+ /*
+ * Accept the peer's idea of {our,his} interface identifier, if different
+ * from our idea, only if the accept_{local,remote} flag is set.
+ */
+ NAKCIIFACEID(CI_IFACEID, neg_ifaceid,
+ if (go->accept_local) {
+ while (eui64_iszero(ifaceid) ||
+ eui64_equals(ifaceid, go->hisid)) /* bad luck */
+ eui64_magic(ifaceid);
+ try.ourid = ifaceid;
+ IPV6CPDEBUG(("local LL address %s", llv6_ntoa(ifaceid)));
+ }
+ );
+
+#ifdef IPV6CP_COMP
+ NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
+ {
+ if (cishort == IPV6CP_COMP) {
+ try.vj_protocol = cishort;
+ } else {
+ try.neg_vj = 0;
+ }
+ }
+ );
+#else
+ NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
+ {
+ try.neg_vj = 0;
+ }
+ );
+#endif
+
+ /*
+ * There may be remaining CIs, if the peer is requesting negotiation
+ * on an option that we didn't include in our request packet.
+ * If they want to negotiate about interface identifier, we comply.
+ * If they want us to ask for compression, we refuse.
+ */
+ while (len > CILEN_VOID) {
+ GETCHAR(citype, p);
+ GETCHAR(cilen, p);
+ if( (len -= cilen) < 0 )
+ goto bad;
+ next = p + cilen - 2;
+
+ switch (citype) {
+ case CI_COMPRESSTYPE:
+ if (go->neg_vj || no.neg_vj ||
+ (cilen != CILEN_COMPRESS))
+ goto bad;
+ no.neg_vj = 1;
+ break;
+ case CI_IFACEID:
+ if (go->neg_ifaceid || no.neg_ifaceid || cilen != CILEN_IFACEID)
+ goto bad;
+ try.neg_ifaceid = 1;
+ eui64_get(ifaceid, p);
+ if (go->accept_local) {
+ while (eui64_iszero(ifaceid) ||
+ eui64_equals(ifaceid, go->hisid)) /* bad luck */
+ eui64_magic(ifaceid);
+ try.ourid = ifaceid;
+ }
+ no.neg_ifaceid = 1;
+ break;
+ }
+ p = next;
+ }
+
+ /* If there is still anything left, this packet is bad. */
+ if (len != 0)
+ goto bad;
+
+ /*
+ * OK, the Nak is good. Now we can update state.
+ */
+ if (f->state != OPENED)
+ *go = try;
+
+ return 1;
+
+bad:
+ IPV6CPDEBUG(("ipv6cp_nakci: received bad Nak!"));
+ return 0;
+}
+
+
+/*
+ * ipv6cp_rejci - Reject some of our CIs.
+ */
+static int
+ipv6cp_rejci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+ u_char cilen;
+ u_short cishort;
+ eui64_t ifaceid;
+ ipv6cp_options try; /* options to request next time */
+
+ try = *go;
+ /*
+ * Any Rejected CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define REJCIIFACEID(opt, neg, val1) \
+ if (go->neg && \
+ len >= (cilen = CILEN_IFACEID) && \
+ p[1] == cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ eui64_get(ifaceid, p); \
+ /* Check rejected value. */ \
+ if (! eui64_equals(ifaceid, val1)) \
+ goto bad; \
+ try.neg = 0; \
+ }
+
+#define REJCIVJ(opt, neg, val) \
+ if (go->neg && \
+ p[1] == CILEN_COMPRESS && \
+ len >= p[1] && \
+ p[0] == opt) { \
+ len -= p[1]; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ /* Check rejected value. */ \
+ if (cishort != val) \
+ goto bad; \
+ try.neg = 0; \
+ }
+
+ REJCIIFACEID(CI_IFACEID, neg_ifaceid, go->ourid);
+
+ REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ /*
+ * Now we can update state.
+ */
+ if (f->state != OPENED)
+ *go = try;
+ return 1;
+
+bad:
+ IPV6CPDEBUG(("ipv6cp_rejci: received bad Reject!"));
+ return 0;
+}
+
+
+/*
+ * ipv6cp_reqci - Check the peer's requested CIs and send appropriate response.
+ *
+ * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
+ * appropriately. If reject_if_disagree is non-zero, doesn't return
+ * CONFNAK; returns CONFREJ if it can't return CONFACK.
+ */
+static int
+ipv6cp_reqci(f, inp, len, reject_if_disagree)
+ fsm *f;
+ u_char *inp; /* Requested CIs */
+ int *len; /* Length of requested CIs */
+ int reject_if_disagree;
+{
+ ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit];
+ ipv6cp_options *ho = &ipv6cp_hisoptions[f->unit];
+ ipv6cp_options *ao = &ipv6cp_allowoptions[f->unit];
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+ u_char *cip, *next; /* Pointer to current and next CIs */
+ u_short cilen, citype; /* Parsed len, type */
+ u_short cishort; /* Parsed short value */
+ eui64_t ifaceid; /* Parsed interface identifier */
+ int rc = CONFACK; /* Final packet return code */
+ int orc; /* Individual option return code */
+ u_char *p; /* Pointer to next char to parse */
+ u_char *ucp = inp; /* Pointer to current output char */
+ int l = *len; /* Length left */
+
+ /*
+ * Reset all his options.
+ */
+ BZERO(ho, sizeof(*ho));
+
+ /*
+ * Process all his options.
+ */
+ next = inp;
+ while (l) {
+ orc = CONFACK; /* Assume success */
+ cip = p = next; /* Remember begining of CI */
+ if (l < 2 || /* Not enough data for CI header or */
+ p[1] < 2 || /* CI length too small or */
+ p[1] > l) { /* CI length too big? */
+ IPV6CPDEBUG(("ipv6cp_reqci: bad CI length!"));
+ orc = CONFREJ; /* Reject bad CI */
+ cilen = l; /* Reject till end of packet */
+ l = 0; /* Don't loop again */
+ goto endswitch;
+ }
+ GETCHAR(citype, p); /* Parse CI type */
+ GETCHAR(cilen, p); /* Parse CI length */
+ l -= cilen; /* Adjust remaining length */
+ next += cilen; /* Step to next CI */
+
+ switch (citype) { /* Check CI type */
+ case CI_IFACEID:
+ IPV6CPDEBUG(("ipv6cp: received interface identifier "));
+
+ if (!ao->neg_ifaceid ||
+ cilen != CILEN_IFACEID) { /* Check CI length */
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+
+ /*
+ * If he has no interface identifier, or if we both have same
+ * identifier then NAK it with new idea.
+ * In particular, if we don't know his identifier, but he does,
+ * then accept it.
+ */
+ eui64_get(ifaceid, p);
+ IPV6CPDEBUG(("(%s)", llv6_ntoa(ifaceid)));
+ if (eui64_iszero(ifaceid) && eui64_iszero(go->ourid)) {
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+ if (!eui64_iszero(wo->hisid) &&
+ !eui64_equals(ifaceid, wo->hisid) &&
+ eui64_iszero(go->hisid)) {
+
+ orc = CONFNAK;
+ ifaceid = wo->hisid;
+ go->hisid = ifaceid;
+ DECPTR(sizeof(ifaceid), p);
+ eui64_put(ifaceid, p);
+ } else
+ if (eui64_iszero(ifaceid) || eui64_equals(ifaceid, go->ourid)) {
+ orc = CONFNAK;
+ if (eui64_iszero(go->hisid)) /* first time, try option */
+ ifaceid = wo->hisid;
+ while (eui64_iszero(ifaceid) ||
+ eui64_equals(ifaceid, go->ourid)) /* bad luck */
+ eui64_magic(ifaceid);
+ go->hisid = ifaceid;
+ DECPTR(sizeof(ifaceid), p);
+ eui64_put(ifaceid, p);
+ }
+
+ ho->neg_ifaceid = 1;
+ ho->hisid = ifaceid;
+ break;
+
+ case CI_COMPRESSTYPE:
+ IPV6CPDEBUG(("ipv6cp: received COMPRESSTYPE "));
+ if (!ao->neg_vj ||
+ (cilen != CILEN_COMPRESS)) {
+ orc = CONFREJ;
+ break;
+ }
+ GETSHORT(cishort, p);
+ IPV6CPDEBUG(("(%d)", cishort));
+
+#ifdef IPV6CP_COMP
+ if (!(cishort == IPV6CP_COMP)) {
+ orc = CONFREJ;
+ break;
+ }
+#else
+ orc = CONFREJ;
+ break;
+#endif
+
+ ho->neg_vj = 1;
+ ho->vj_protocol = cishort;
+ break;
+
+ default:
+ orc = CONFREJ;
+ break;
+ }
+
+endswitch:
+ IPV6CPDEBUG((" (%s)\n", CODENAME(orc)));
+
+ if (orc == CONFACK && /* Good CI */
+ rc != CONFACK) /* but prior CI wasnt? */
+ continue; /* Don't send this one */
+
+ if (orc == CONFNAK) { /* Nak this CI? */
+ if (reject_if_disagree) /* Getting fed up with sending NAKs? */
+ orc = CONFREJ; /* Get tough if so */
+ else {
+ if (rc == CONFREJ) /* Rejecting prior CI? */
+ continue; /* Don't send this one */
+ if (rc == CONFACK) { /* Ack'd all prior CIs? */
+ rc = CONFNAK; /* Not anymore... */
+ ucp = inp; /* Backup */
+ }
+ }
+ }
+
+ if (orc == CONFREJ && /* Reject this CI */
+ rc != CONFREJ) { /* but no prior ones? */
+ rc = CONFREJ;
+ ucp = inp; /* Backup */
+ }
+
+ /* Need to move CI? */
+ if (ucp != cip)
+ BCOPY(cip, ucp, cilen); /* Move it */
+
+ /* Update output pointer */
+ INCPTR(cilen, ucp);
+ }
+
+ /*
+ * If we aren't rejecting this packet, and we want to negotiate
+ * their identifier and they didn't send their identifier, then we
+ * send a NAK with a CI_IFACEID option appended. We assume the
+ * input buffer is long enough that we can append the extra
+ * option safely.
+ */
+ if (rc != CONFREJ && !ho->neg_ifaceid &&
+ wo->req_ifaceid && !reject_if_disagree) {
+ if (rc == CONFACK) {
+ rc = CONFNAK;
+ ucp = inp; /* reset pointer */
+ wo->req_ifaceid = 0; /* don't ask again */
+ }
+ PUTCHAR(CI_IFACEID, ucp);
+ PUTCHAR(CILEN_IFACEID, ucp);
+ eui64_put(wo->hisid, ucp);
+ }
+
+ *len = ucp - inp; /* Compute output length */
+ IPV6CPDEBUG(("ipv6cp: returning Configure-%s", CODENAME(rc)));
+ return (rc); /* Return final code */
+}
+
+
+/*
+ * ipv6_check_options - check that any IP-related options are OK,
+ * and assign appropriate defaults.
+ */
+static void
+ipv6_check_options()
+{
+ ipv6cp_options *wo = &ipv6cp_wantoptions[0];
+
+#if defined(SOL2)
+ /*
+ * Persistent link-local id is only used when user has not explicitly
+ * configure/hard-code the id
+ */
+ if ((wo->use_persistent) && (!wo->opt_local) && (!wo->opt_remote)) {
+
+ /*
+ * On systems where there are no Ethernet interfaces used, there
+ * may be other ways to obtain a persistent id. Right now, it
+ * will fall back to using magic [see eui64_magic] below when
+ * an EUI-48 from MAC address can't be obtained. Other possibilities
+ * include obtaining EEPROM serial numbers, or some other unique
+ * yet persistent number. On Sparc platforms, this is possible,
+ * but too bad there's no standards yet for x86 machines.
+ */
+ if (ether_to_eui64(&wo->ourid)) {
+ wo->opt_local = 1;
+ }
+ }
+#endif
+
+ if (!wo->opt_local) { /* init interface identifier */
+ if (wo->use_ip && eui64_iszero(wo->ourid)) {
+ eui64_setlo32(wo->ourid, ntohl(ipcp_wantoptions[0].ouraddr));
+ if (!eui64_iszero(wo->ourid))
+ wo->opt_local = 1;
+ }
+
+ while (eui64_iszero(wo->ourid))
+ eui64_magic(wo->ourid);
+ }
+
+ if (!wo->opt_remote) {
+ if (wo->use_ip && eui64_iszero(wo->hisid)) {
+ eui64_setlo32(wo->hisid, ntohl(ipcp_wantoptions[0].hisaddr));
+ if (!eui64_iszero(wo->hisid))
+ wo->opt_remote = 1;
+ }
+ }
+
+ if (demand && (eui64_iszero(wo->ourid) || eui64_iszero(wo->hisid))) {
+ option_error("local/remote LL address required for demand-dialling\n");
+ exit(1);
+ }
+}
+
+
+/*
+ * ipv6_demand_conf - configure the interface as though
+ * IPV6CP were up, for use with dial-on-demand.
+ */
+static int
+ipv6_demand_conf(u)
+ int u;
+{
+ ipv6cp_options *wo = &ipv6cp_wantoptions[u];
+
+#if defined(__linux__) || defined(SOL2) || (defined(SVR4) && (defined(SNI) || defined(__USLC__)))
+#if defined(SOL2)
+ if (!sif6up(u))
+ return 0;
+#else
+ if (!sifup(u))
+ return 0;
+#endif /* defined(SOL2) */
+#endif
+ if (!sif6addr(u, wo->ourid, wo->hisid))
+ return 0;
+#if !defined(__linux__) && !(defined(SVR4) && (defined(SNI) || defined(__USLC__)))
+ if (!sifup(u))
+ return 0;
+#endif
+ if (!sifnpmode(u, PPP_IPV6, NPMODE_QUEUE))
+ return 0;
+
+ syslog(LOG_NOTICE, "ipv6_demand_conf");
+ syslog(LOG_NOTICE, "local LL address %s", llv6_ntoa(wo->ourid));
+ syslog(LOG_NOTICE, "remote LL address %s", llv6_ntoa(wo->hisid));
+
+ return 1;
+}
+
+
+/*
+ * ipv6cp_up - IPV6CP has come UP.
+ *
+ * Configure the IPv6 network interface appropriately and bring it up.
+ */
+static void
+ipv6cp_up(f)
+ fsm *f;
+{
+ ipv6cp_options *ho = &ipv6cp_hisoptions[f->unit];
+ ipv6cp_options *go = &ipv6cp_gotoptions[f->unit];
+ ipv6cp_options *wo = &ipv6cp_wantoptions[f->unit];
+
+ IPV6CPDEBUG(("ipv6cp: up"));
+
+ /*
+ * We must have a non-zero LL address for both ends of the link.
+ */
+ if (!ho->neg_ifaceid)
+ ho->hisid = wo->hisid;
+
+ if(!no_ifaceid_neg) {
+ if (eui64_iszero(ho->hisid)) {
+ syslog(LOG_ERR, "Could not determine remote LL address");
+ ipv6cp_close(f->unit, "Could not determine remote LL address");
+ return;
+ }
+ if (eui64_iszero(go->ourid)) {
+ syslog(LOG_ERR, "Could not determine local LL address");
+ ipv6cp_close(f->unit, "Could not determine local LL address");
+ return;
+ }
+ if (eui64_equals(go->ourid, ho->hisid)) {
+ syslog(LOG_ERR, "local and remote LL addresses are equal");
+ ipv6cp_close(f->unit, "local and remote LL addresses are equal");
+ return;
+ }
+ }
+ script_setenv("LLLOCAL", llv6_ntoa(go->ourid));
+ script_setenv("LLREMOTE", llv6_ntoa(ho->hisid));
+
+#ifdef IPV6CP_COMP
+ /* set tcp compression */
+ sif6comp(f->unit, ho->neg_vj);
+#endif
+
+ /*
+ * If we are doing dial-on-demand, the interface is already
+ * configured, so we put out any saved-up packets, then set the
+ * interface to pass IPv6 packets.
+ */
+ if (demand) {
+ if (! eui64_equals(go->ourid, wo->ourid) ||
+ ! eui64_equals(ho->hisid, wo->hisid)) {
+ if (! eui64_equals(go->ourid, wo->ourid))
+ warn("Local LL address changed to %s",
+ llv6_ntoa(go->ourid));
+ if (! eui64_equals(ho->hisid, wo->hisid))
+ warn("Remote LL address changed to %s",
+ llv6_ntoa(ho->hisid));
+ ipv6cp_clear_addrs(f->unit, go->ourid, ho->hisid);
+
+ /* Set the interface to the new addresses */
+ if (!sif6addr(f->unit, go->ourid, ho->hisid)) {
+ if (debug)
+ warn("sif6addr failed");
+ ipv6cp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+
+ }
+ demand_rexmit(PPP_IPV6);
+ sifnpmode(f->unit, PPP_IPV6, NPMODE_PASS);
+
+ } else {
+ /*
+ * Set LL addresses
+ */
+#if !defined(__linux__) && !defined(SOL2) && !(defined(SVR4) && (defined(SNI) || defined(__USLC__)))
+ if (!sif6addr(f->unit, go->ourid, ho->hisid)) {
+ if (debug)
+ warn("sif6addr failed");
+ ipv6cp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+#endif
+
+ /* bring the interface up for IPv6 */
+#if defined(SOL2)
+ if (!sif6up(f->unit)) {
+ if (debug)
+ warn("sifup failed (IPV6)");
+ ipv6cp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+#else
+ if (!sifup(f->unit)) {
+ if (debug)
+ warn("sifup failed (IPV6)");
+ ipv6cp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+#endif /* defined(SOL2) */
+
+#if defined(__linux__) || defined(SOL2) || (defined(SVR4) && (defined(SNI) || defined(__USLC__)))
+ if (!sif6addr(f->unit, go->ourid, ho->hisid)) {
+ if (debug)
+ warn("sif6addr failed");
+ ipv6cp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+#endif
+ sifnpmode(f->unit, PPP_IPV6, NPMODE_PASS);
+
+ syslog(LOG_NOTICE, "local LL address %s", llv6_ntoa(go->ourid));
+ syslog(LOG_NOTICE, "remote LL address %s", llv6_ntoa(ho->hisid));
+ }
+
+ np_up(f->unit, PPP_IPV6);
+ ipv6cp_is_up = 1;
+
+ /*
+ * Execute the ipv6-up script, like this:
+ * /etc/ppp/ipv6-up interface tty speed local-LL remote-LL
+ */
+ if (ipv6cp_script_state == s_down) {
+ ipv6cp_script_state = s_up;
+ ipv6cp_script(_PATH_IPV6UP);
+ }
+}
+
+
+/*
+ * ipv6cp_down - IPV6CP has gone DOWN.
+ *
+ * Take the IPv6 network interface down, clear its addresses
+ * and delete routes through it.
+ */
+static void
+ipv6cp_down(f)
+ fsm *f;
+{
+ IPV6CPDEBUG(("ipv6cp: down"));
+ if (ipv6cp_is_up) {
+ ipv6cp_is_up = 0;
+ np_down(f->unit, PPP_IPV6);
+ }
+#ifdef IPV6CP_COMP
+ sif6comp(f->unit, 0);
+#endif
+
+ /*
+ * If we are doing dial-on-demand, set the interface
+ * to queue up outgoing packets (for now).
+ */
+ if (demand) {
+ sifnpmode(f->unit, PPP_IPV6, NPMODE_QUEUE);
+ } else {
+ sifnpmode(f->unit, PPP_IPV6, NPMODE_DROP);
+#if !defined(__linux__) && !(defined(SVR4) && (defined(SNI) || defined(__USLC)))
+#if defined(SOL2)
+ sif6down(f->unit);
+#else
+ sifdown(f->unit);
+#endif /* defined(SOL2) */
+#endif
+ ipv6cp_clear_addrs(f->unit,
+ ipv6cp_gotoptions[f->unit].ourid,
+ ipv6cp_hisoptions[f->unit].hisid);
+#if defined(__linux__) || (defined(SVR4) && (defined(SNI) || defined(__USLC)))
+ sifdown(f->unit);
+#endif
+ }
+
+ /* Execute the ipv6-down script */
+ if (ipv6cp_script_state == s_up) {
+ ipv6cp_script_state = s_down;
+ ipv6cp_script(_PATH_IPV6DOWN);
+ }
+}
+
+
+/*
+ * ipv6cp_clear_addrs() - clear the interface addresses, routes,
+ * proxy neighbour discovery entries, etc.
+ */
+static void
+ipv6cp_clear_addrs(unit, ourid, hisid)
+ int unit;
+ eui64_t ourid;
+ eui64_t hisid;
+{
+ cif6addr(unit, ourid, hisid);
+}
+
+
+/*
+ * ipv6cp_finished - possibly shut down the lower layers.
+ */
+static void
+ipv6cp_finished(f)
+ fsm *f;
+{
+ np_finished(f->unit, PPP_IPV6);
+}
+
+
+/*
+ * ipv6cp_script - Execute a script with arguments
+ * interface-name tty-name speed local-LL remote-LL.
+ */
+static void
+ipv6cp_script(script)
+ char *script;
+{
+ char strspeed[32], strlocal[32], strremote[32];
+ char *argv[8];
+
+ sprintf(strspeed, "%d", baud_rate);
+ strcpy(strlocal, llv6_ntoa(ipv6cp_gotoptions[0].ourid));
+ strcpy(strremote, llv6_ntoa(ipv6cp_hisoptions[0].hisid));
+
+ argv[0] = script;
+ argv[1] = ifname;
+ argv[2] = devnam;
+ argv[3] = strspeed;
+ argv[4] = strlocal;
+ argv[5] = strremote;
+ argv[6] = ipparam;
+ argv[7] = NULL;
+
+ run_program(script, argv, 0);
+}
+
+/*
+ * ipv6cp_printpkt - print the contents of an IPV6CP packet.
+ */
+static char *ipv6cp_codenames[] = {
+ "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+ "TermReq", "TermAck", "CodeRej"
+};
+
+static int
+ipv6cp_printpkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer)(void *, char *, ...);
+ void *arg;
+{
+ int code, id, len, olen;
+ u_char *pstart, *optend;
+ u_short cishort;
+ eui64_t ifaceid;
+
+ if (plen < HEADERLEN)
+ return 0;
+ pstart = p;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < HEADERLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= sizeof(ipv6cp_codenames) / sizeof(char *))
+ printer(arg, " %s", ipv6cp_codenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+ printer(arg, " id=0x%x", id);
+ len -= HEADERLEN;
+ switch (code) {
+ case CONFREQ:
+ case CONFACK:
+ case CONFNAK:
+ case CONFREJ:
+ /* print option list */
+ while (len >= 2) {
+ GETCHAR(code, p);
+ GETCHAR(olen, p);
+ p -= 2;
+ if (olen < 2 || olen > len) {
+ break;
+ }
+ printer(arg, " <");
+ len -= olen;
+ optend = p + olen;
+ switch (code) {
+ case CI_COMPRESSTYPE:
+ if (olen >= CILEN_COMPRESS) {
+ p += 2;
+ GETSHORT(cishort, p);
+ printer(arg, "compress ");
+ printer(arg, "0x%x", cishort);
+ }
+ break;
+ case CI_IFACEID:
+ if (olen == CILEN_IFACEID) {
+ p += 2;
+ eui64_get(ifaceid, p);
+ printer(arg, "addr %s", llv6_ntoa(ifaceid));
+ }
+ break;
+ }
+ while (p < optend) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+ printer(arg, ">");
+ }
+ break;
+
+ case TERMACK:
+ case TERMREQ:
+ if (len > 0 && *p >= ' ' && *p < 0x7f) {
+ printer(arg, " ");
+ print_string(p, len, printer, arg);
+ p += len;
+ len = 0;
+ }
+ break;
+ }
+
+ /* print the rest of the bytes in the packet */
+ for (; len > 0; --len) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+
+ return p - pstart;
+}
+
+/*
+ * ipv6_active_pkt - see if this IP packet is worth bringing the link up for.
+ * We don't bring the link up for IP fragments or for TCP FIN packets
+ * with no data.
+ */
+#define IP6_HDRLEN 40 /* bytes */
+#define IP6_NHDR_FRAG 44 /* fragment IPv6 header */
+#define IPPROTO_TCP 6
+#define TCP_HDRLEN 20
+#define TH_FIN 0x01
+
+/*
+ * We use these macros because the IP header may be at an odd address,
+ * and some compilers might use word loads to get th_off or ip_hl.
+ */
+
+#define get_ip6nh(x) (((unsigned char *)(x))[6])
+#define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4)
+#define get_tcpflags(x) (((unsigned char *)(x))[13])
+
+static int
+ipv6_active_pkt(pkt, len)
+ u_char *pkt;
+ int len;
+{
+ u_char *tcp;
+
+ len -= PPP_HDRLEN;
+ pkt += PPP_HDRLEN;
+ if (len < IP6_HDRLEN)
+ return 0;
+ if (get_ip6nh(pkt) == IP6_NHDR_FRAG)
+ return 0;
+ if (get_ip6nh(pkt) != IPPROTO_TCP)
+ return 1;
+ if (len < IP6_HDRLEN + TCP_HDRLEN)
+ return 0;
+ tcp = pkt + IP6_HDRLEN;
+ if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == IP6_HDRLEN + get_tcpoff(tcp) * 4)
+ return 0;
+ return 1;
+}
diff --git a/usr.sbin/pppd/ipv6cp.h b/usr.sbin/pppd/ipv6cp.h
new file mode 100644
index 0000000..39f9444
--- /dev/null
+++ b/usr.sbin/pppd/ipv6cp.h
@@ -0,0 +1,129 @@
+/*
+ ipv6cp.h - PPP IPV6 Control Protocol.
+ Copyright (C) 1999 Tommi Komulainen <Tommi.Komulainen@iki.fi>
+
+ Redistribution and use in source and binary forms are permitted
+ provided that the above copyright notice and this paragraph are
+ duplicated in all such forms. 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 WITHOUT ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+*/
+
+/* Original version, based on RFC2023 :
+
+ Copyright (c) 1995, 1996, 1997 Francis.Dupont@inria.fr, INRIA Rocquencourt,
+ Alain.Durand@imag.fr, IMAG,
+ Jean-Luc.Richier@imag.fr, IMAG-LSR.
+
+ Copyright (c) 1998, 1999 Francis.Dupont@inria.fr, GIE DYADE,
+ Alain.Durand@imag.fr, IMAG,
+ Jean-Luc.Richier@imag.fr, IMAG-LSR.
+
+ Ce travail a été fait au sein du GIE DYADE (Groupement d'Intérêt
+ Économique ayant pour membres BULL S.A. et l'INRIA).
+
+ Ce logiciel informatique est disponible aux conditions
+ usuelles dans la recherche, c'est-à-dire qu'il peut
+ être utilisé, copié, modifié, distribué à l'unique
+ condition que ce texte soit conservé afin que
+ l'origine de ce logiciel soit reconnue.
+
+ Le nom de l'Institut National de Recherche en Informatique
+ et en Automatique (INRIA), de l'IMAG, ou d'une personne morale
+ ou physique ayant participé à l'élaboration de ce logiciel ne peut
+ être utilisé sans son accord préalable explicite.
+
+ Ce logiciel est fourni tel quel sans aucune garantie,
+ support ou responsabilité d'aucune sorte.
+ Ce logiciel est dérivé de sources d'origine
+ "University of California at Berkeley" et
+ "Digital Equipment Corporation" couvertes par des copyrights.
+
+ L'Institut d'Informatique et de Mathématiques Appliquées de Grenoble (IMAG)
+ est une fédération d'unités mixtes de recherche du CNRS, de l'Institut National
+ Polytechnique de Grenoble et de l'Université Joseph Fourier regroupant
+ sept laboratoires dont le laboratoire Logiciels, Systèmes, Réseaux (LSR).
+
+ This work has been done in the context of GIE DYADE (joint R & D venture
+ between BULL S.A. and INRIA).
+
+ This software is available with usual "research" terms
+ with the aim of retain credits of the software.
+ Permission to use, copy, modify and distribute this software for any
+ purpose and without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies,
+ and the name of INRIA, IMAG, or any contributor not be used in advertising
+ or publicity pertaining to this material without the prior explicit
+ permission. The software is provided "as is" without any
+ warranties, support or liabilities of any kind.
+ This software is derived from source code from
+ "University of California at Berkeley" and
+ "Digital Equipment Corporation" protected by copyrights.
+
+ Grenoble's Institute of Computer Science and Applied Mathematics (IMAG)
+ is a federation of seven research units funded by the CNRS, National
+ Polytechnic Institute of Grenoble and University Joseph Fourier.
+ The research unit in Software, Systems, Networks (LSR) is member of IMAG.
+*/
+
+/*
+ * Derived from :
+ *
+ *
+ * ipcp.h - IP Control Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 Carnegie Mellon University. The name of the
+ * University 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: ipv6cp.h,v 1.3 1999/09/30 19:57:45 masputra Exp $
+ * $FreeBSD$
+ */
+
+/*
+ * Options.
+ */
+#define CI_IFACEID 1 /* Interface Identifier */
+#define CI_COMPRESSTYPE 2 /* Compression Type */
+
+/* No compression types yet defined.
+ *#define IPV6CP_COMP 0x004f
+ */
+typedef struct ipv6cp_options {
+ int neg_ifaceid; /* Negotiate interface identifier? */
+ int req_ifaceid; /* Ask peer to send interface identifier? */
+ int accept_local; /* accept peer's value for iface id? */
+ int opt_local; /* ourtoken set by option */
+ int opt_remote; /* histoken set by option */
+ int use_ip; /* use IP as interface identifier */
+#if defined(SOL2)
+ int use_persistent; /* use uniquely persistent value for address */
+#endif /* defined(SOL2) */
+ int neg_vj; /* Van Jacobson Compression? */
+ u_short vj_protocol; /* protocol value to use in VJ option */
+ eui64_t ourid, hisid; /* Interface identifiers */
+} ipv6cp_options;
+
+extern fsm ipv6cp_fsm[];
+extern ipv6cp_options ipv6cp_wantoptions[];
+extern ipv6cp_options ipv6cp_gotoptions[];
+extern ipv6cp_options ipv6cp_allowoptions[];
+extern ipv6cp_options ipv6cp_hisoptions[];
+
+extern struct protent ipv6cp_protent;
+
+extern int setifaceid(char **arg);
diff --git a/usr.sbin/pppd/ipxcp.c b/usr.sbin/pppd/ipxcp.c
new file mode 100644
index 0000000..ea7f127
--- /dev/null
+++ b/usr.sbin/pppd/ipxcp.c
@@ -0,0 +1,1399 @@
+/*
+ * ipxcp.c - PPP IPX Control Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 Carnegie Mellon University. The name of the
+ * University 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifdef IPX_CHANGE
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "ipxcp.h"
+#include "pathnames.h"
+
+/* global vars */
+ipxcp_options ipxcp_wantoptions[NUM_PPP]; /* Options that we want to request */
+ipxcp_options ipxcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */
+ipxcp_options ipxcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */
+ipxcp_options ipxcp_hisoptions[NUM_PPP]; /* Options that we ack'd */
+
+#define wo (&ipxcp_wantoptions[0])
+#define ao (&ipxcp_allowoptions[0])
+#define go (&ipxcp_gotoptions[0])
+#define ho (&ipxcp_hisoptions[0])
+
+/*
+ * Callbacks for fsm code. (CI = Configuration Information)
+ */
+static void ipxcp_resetci(fsm *); /* Reset our CI */
+static int ipxcp_cilen(fsm *); /* Return length of our CI */
+static void ipxcp_addci(fsm *, u_char *, int *); /* Add our CI */
+static int ipxcp_ackci(fsm *, u_char *, int); /* Peer ack'd our CI */
+static int ipxcp_nakci(fsm *, u_char *, int); /* Peer nak'd our CI */
+static int ipxcp_rejci(fsm *, u_char *, int); /* Peer rej'd our CI */
+static int ipxcp_reqci(fsm *, u_char *, int *, int); /* Rcv CI */
+static void ipxcp_up(fsm *); /* We're UP */
+static void ipxcp_down(fsm *); /* We're DOWN */
+static void ipxcp_script(fsm *, char *); /* Run an up/down script */
+
+fsm ipxcp_fsm[NUM_PPP]; /* IPXCP fsm structure */
+
+static fsm_callbacks ipxcp_callbacks = { /* IPXCP callback routines */
+ ipxcp_resetci, /* Reset our Configuration Information */
+ ipxcp_cilen, /* Length of our Configuration Information */
+ ipxcp_addci, /* Add our Configuration Information */
+ ipxcp_ackci, /* ACK our Configuration Information */
+ ipxcp_nakci, /* NAK our Configuration Information */
+ ipxcp_rejci, /* Reject our Configuration Information */
+ ipxcp_reqci, /* Request peer's Configuration Information */
+ ipxcp_up, /* Called when fsm reaches OPENED state */
+ ipxcp_down, /* Called when fsm leaves OPENED state */
+ NULL, /* Called when we want the lower layer up */
+ NULL, /* Called when we want the lower layer down */
+ NULL, /* Called when Protocol-Reject received */
+ NULL, /* Retransmission is necessary */
+ NULL, /* Called to handle protocol-specific codes */
+ "IPXCP" /* String name of protocol */
+};
+
+/*
+ * Protocol entry points.
+ */
+
+static void ipxcp_init(int);
+static void ipxcp_open(int);
+static void ipxcp_close(int, char *);
+static void ipxcp_lowerup(int);
+static void ipxcp_lowerdown(int);
+static void ipxcp_input(int, u_char *, int);
+static void ipxcp_protrej(int);
+static int ipxcp_printpkt(u_char *, int,
+ void (*)(void *, char *, ...), void *);
+
+struct protent ipxcp_protent = {
+ PPP_IPXCP,
+ ipxcp_init,
+ ipxcp_input,
+ ipxcp_protrej,
+ ipxcp_lowerup,
+ ipxcp_lowerdown,
+ ipxcp_open,
+ ipxcp_close,
+ ipxcp_printpkt,
+ NULL,
+ 0,
+ "IPXCP",
+ NULL,
+ NULL,
+ NULL
+};
+
+/*
+ * Lengths of configuration options.
+ */
+
+#define CILEN_VOID 2
+#define CILEN_COMPLETE 2 /* length of complete option */
+#define CILEN_NETN 6 /* network number length option */
+#define CILEN_NODEN 8 /* node number length option */
+#define CILEN_PROTOCOL 4 /* Minimum length of routing protocol */
+#define CILEN_NAME 3 /* Minimum length of router name */
+#define CILEN_COMPRESS 4 /* Minimum length of compression protocol */
+
+#define CODENAME(x) ((x) == CONFACK ? "ACK" : \
+ (x) == CONFNAK ? "NAK" : "REJ")
+
+/* Used in printing the node number */
+#define NODE(base) base[0], base[1], base[2], base[3], base[4], base[5]
+
+/* Used to generate the proper bit mask */
+#define BIT(num) (1 << (num))
+
+/*
+ * Convert from internal to external notation
+ */
+
+static short int
+to_external(internal)
+short int internal;
+{
+ short int external;
+
+ if (internal & IPX_NONE)
+ external = IPX_NONE;
+ else
+ external = RIP_SAP;
+
+ return external;
+}
+
+/*
+ * Make a string representation of a network IP address.
+ */
+
+char *
+ipx_ntoa(ipxaddr)
+u_int32_t ipxaddr;
+{
+ static char b[64];
+ sprintf(b, "%x", ipxaddr);
+ return b;
+}
+
+
+/*
+ * ipxcp_init - Initialize IPXCP.
+ */
+static void
+ipxcp_init(unit)
+ int unit;
+{
+ fsm *f = &ipxcp_fsm[unit];
+
+ f->unit = unit;
+ f->protocol = PPP_IPXCP;
+ f->callbacks = &ipxcp_callbacks;
+ fsm_init(&ipxcp_fsm[unit]);
+
+ memset (wo->name, 0, sizeof (wo->name));
+ memset (wo->our_node, 0, sizeof (wo->our_node));
+ memset (wo->his_node, 0, sizeof (wo->his_node));
+
+ wo->neg_nn = 1;
+ wo->neg_complete = 1;
+ wo->network = 0;
+
+ ao->neg_node = 1;
+ ao->neg_nn = 1;
+ ao->neg_name = 1;
+ ao->neg_complete = 1;
+ ao->neg_router = 1;
+
+ ao->accept_local = 0;
+ ao->accept_remote = 0;
+ ao->accept_network = 0;
+
+ wo->tried_rip = 0;
+ wo->tried_nlsp = 0;
+}
+
+/*
+ * Copy the node number
+ */
+
+static void
+copy_node (src, dst)
+u_char *src, *dst;
+{
+ memcpy (dst, src, sizeof (ipxcp_wantoptions[0].our_node));
+}
+
+/*
+ * Compare node numbers
+ */
+
+static int
+compare_node (src, dst)
+u_char *src, *dst;
+{
+ return memcmp (dst, src, sizeof (ipxcp_wantoptions[0].our_node)) == 0;
+}
+
+/*
+ * Is the node number zero?
+ */
+
+static int
+zero_node (node)
+u_char *node;
+{
+ int indx;
+ for (indx = 0; indx < sizeof (ipxcp_wantoptions[0].our_node); ++indx)
+ if (node [indx] != 0)
+ return 0;
+ return 1;
+}
+
+/*
+ * Increment the node number
+ */
+
+static void
+inc_node (node)
+u_char *node;
+{
+ u_char *outp;
+ u_int32_t magic_num;
+
+ outp = node;
+ magic_num = magic();
+ *outp++ = '\0';
+ *outp++ = '\0';
+ PUTLONG (magic_num, outp);
+}
+
+/*
+ * ipxcp_open - IPXCP is allowed to come up.
+ */
+static void
+ipxcp_open(unit)
+ int unit;
+{
+ fsm_open(&ipxcp_fsm[unit]);
+}
+
+/*
+ * ipxcp_close - Take IPXCP down.
+ */
+static void
+ipxcp_close(unit, reason)
+ int unit;
+ char *reason;
+{
+ fsm_close(&ipxcp_fsm[unit], reason);
+}
+
+
+/*
+ * ipxcp_lowerup - The lower layer is up.
+ */
+static void
+ipxcp_lowerup(unit)
+ int unit;
+{
+ fsm_lowerup(&ipxcp_fsm[unit]);
+}
+
+
+/*
+ * ipxcp_lowerdown - The lower layer is down.
+ */
+static void
+ipxcp_lowerdown(unit)
+ int unit;
+{
+ fsm_lowerdown(&ipxcp_fsm[unit]);
+}
+
+
+/*
+ * ipxcp_input - Input IPXCP packet.
+ */
+static void
+ipxcp_input(unit, p, len)
+ int unit;
+ u_char *p;
+ int len;
+{
+ fsm_input(&ipxcp_fsm[unit], p, len);
+}
+
+
+/*
+ * ipxcp_protrej - A Protocol-Reject was received for IPXCP.
+ *
+ * Pretend the lower layer went down, so we shut up.
+ */
+static void
+ipxcp_protrej(unit)
+ int unit;
+{
+ fsm_lowerdown(&ipxcp_fsm[unit]);
+}
+
+
+/*
+ * ipxcp_resetci - Reset our CI.
+ */
+static void
+ipxcp_resetci(f)
+ fsm *f;
+{
+ wo->req_node = wo->neg_node && ao->neg_node;
+ wo->req_nn = wo->neg_nn && ao->neg_nn;
+
+ if (wo->our_network == 0) {
+ wo->neg_node = 1;
+ ao->accept_network = 1;
+ }
+/*
+ * If our node number is zero then change it.
+ */
+ if (zero_node (wo->our_node)) {
+ inc_node (wo->our_node);
+ ao->accept_local = 1;
+ wo->neg_node = 1;
+ }
+/*
+ * If his node number is zero then change it.
+ */
+ if (zero_node (wo->his_node)) {
+ inc_node (wo->his_node);
+ ao->accept_remote = 1;
+ }
+/*
+ * If no routing agent was specified then we do RIP/SAP according to the
+ * RFC documents. If you have specified something then OK. Otherwise, we
+ * do RIP/SAP.
+ */
+ if (ao->router == 0) {
+ ao->router |= BIT(RIP_SAP);
+ wo->router |= BIT(RIP_SAP);
+ }
+
+ /* Always specify a routing protocol unless it was REJected. */
+ wo->neg_router = 1;
+/*
+ * Start with these default values
+ */
+ *go = *wo;
+}
+
+/*
+ * ipxcp_cilen - Return length of our CI.
+ */
+
+static int
+ipxcp_cilen(f)
+ fsm *f;
+{
+ int len;
+
+ len = go->neg_nn ? CILEN_NETN : 0;
+ len += go->neg_node ? CILEN_NODEN : 0;
+ len += go->neg_name ? CILEN_NAME + strlen (go->name) - 1 : 0;
+
+ /* RFC says that defaults should not be included. */
+ if (go->neg_router && to_external(go->router) != RIP_SAP)
+ len += CILEN_PROTOCOL;
+
+ return (len);
+}
+
+
+/*
+ * ipxcp_addci - Add our desired CIs to a packet.
+ */
+static void
+ipxcp_addci(f, ucp, lenp)
+ fsm *f;
+ u_char *ucp;
+ int *lenp;
+{
+/*
+ * Add the options to the record.
+ */
+ if (go->neg_nn) {
+ PUTCHAR (IPX_NETWORK_NUMBER, ucp);
+ PUTCHAR (CILEN_NETN, ucp);
+ PUTLONG (go->our_network, ucp);
+ }
+
+ if (go->neg_node) {
+ int indx;
+ PUTCHAR (IPX_NODE_NUMBER, ucp);
+ PUTCHAR (CILEN_NODEN, ucp);
+ for (indx = 0; indx < sizeof (go->our_node); ++indx)
+ PUTCHAR (go->our_node[indx], ucp);
+ }
+
+ if (go->neg_name) {
+ int cilen = strlen (go->name);
+ int indx;
+ PUTCHAR (IPX_ROUTER_NAME, ucp);
+ PUTCHAR (CILEN_NAME + cilen - 1, ucp);
+ for (indx = 0; indx < cilen; ++indx)
+ PUTCHAR (go->name [indx], ucp);
+ }
+
+ if (go->neg_router) {
+ short external = to_external (go->router);
+ if (external != RIP_SAP) {
+ PUTCHAR (IPX_ROUTER_PROTOCOL, ucp);
+ PUTCHAR (CILEN_PROTOCOL, ucp);
+ PUTSHORT (external, ucp);
+ }
+ }
+}
+
+/*
+ * ipxcp_ackci - Ack our CIs.
+ *
+ * Returns:
+ * 0 - Ack was bad.
+ * 1 - Ack was good.
+ */
+static int
+ipxcp_ackci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ u_short cilen, citype, cishort;
+ u_char cichar;
+ u_int32_t cilong;
+
+#define ACKCIVOID(opt, neg) \
+ if (neg) { \
+ if ((len -= CILEN_VOID) < 0) \
+ break; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_VOID || \
+ citype != opt) \
+ break; \
+ }
+
+#define ACKCICOMPLETE(opt,neg) ACKCIVOID(opt, neg)
+
+#define ACKCICHARS(opt, neg, val, cnt) \
+ if (neg) { \
+ int indx, count = cnt; \
+ len -= (count + 2); \
+ if (len < 0) \
+ break; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != (count + 2) || \
+ citype != opt) \
+ break; \
+ for (indx = 0; indx < count; ++indx) {\
+ GETCHAR(cichar, p); \
+ if (cichar != ((u_char *) &val)[indx]) \
+ break; \
+ }\
+ if (indx != count) \
+ break; \
+ }
+
+#define ACKCINODE(opt,neg,val) ACKCICHARS(opt,neg,val,sizeof(val))
+#define ACKCINAME(opt,neg,val) ACKCICHARS(opt,neg,val,strlen(val))
+
+#define ACKCINETWORK(opt, neg, val) \
+ if (neg) { \
+ if ((len -= CILEN_NETN) < 0) \
+ break; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_NETN || \
+ citype != opt) \
+ break; \
+ GETLONG(cilong, p); \
+ if (cilong != val) \
+ break; \
+ }
+
+#define ACKCIPROTO(opt, neg, val) \
+ if (neg) { \
+ if (len < 2) \
+ break; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_PROTOCOL || citype != opt) \
+ break; \
+ len -= cilen; \
+ if (len < 0) \
+ break; \
+ GETSHORT(cishort, p); \
+ if (cishort != to_external (val) || cishort == RIP_SAP) \
+ break; \
+ }
+/*
+ * Process the ACK frame in the order in which the frame was assembled
+ */
+ do {
+ ACKCINETWORK (IPX_NETWORK_NUMBER, go->neg_nn, go->our_network);
+ ACKCINODE (IPX_NODE_NUMBER, go->neg_node, go->our_node);
+ ACKCINAME (IPX_ROUTER_NAME, go->neg_name, go->name);
+ ACKCIPROTO (IPX_ROUTER_PROTOCOL, go->neg_router, go->router);
+ ACKCIPROTO (IPX_ROUTER_PROTOCOL, go->neg_router, go->router);
+ ACKCIPROTO (IPX_ROUTER_PROTOCOL, go->neg_router, go->router);
+/*
+ * This is the end of the record.
+ */
+ if (len == 0)
+ return (1);
+ } while (0);
+/*
+ * The frame is invalid
+ */
+ IPXCPDEBUG((LOG_INFO, "ipxcp_ackci: received bad Ack!"));
+ return (0);
+}
+
+/*
+ * ipxcp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if IPXCP is in the OPENED state.
+ *
+ * Returns:
+ * 0 - Nak was bad.
+ * 1 - Nak was good.
+ */
+
+static int
+ipxcp_nakci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ u_char citype, cilen, *next;
+ u_short s;
+ u_int32_t l;
+ ipxcp_options no; /* options we've seen Naks for */
+ ipxcp_options try; /* options to request next time */
+
+ BZERO(&no, sizeof(no));
+ try = *go;
+
+ while (len > CILEN_VOID) {
+ GETCHAR (citype, p);
+ GETCHAR (cilen, p);
+ len -= cilen;
+ if (len < 0)
+ goto bad;
+ next = &p [cilen - CILEN_VOID];
+
+ switch (citype) {
+ case IPX_NETWORK_NUMBER:
+ if (!go->neg_nn || no.neg_nn || (cilen != CILEN_NETN))
+ goto bad;
+ no.neg_nn = 1;
+
+ GETLONG(l, p);
+ IPXCPDEBUG((LOG_INFO, "local IP address %d", l));
+ if (l && ao->accept_network)
+ try.our_network = l;
+ break;
+
+ case IPX_NODE_NUMBER:
+ if (!go->neg_node || no.neg_node || (cilen != CILEN_NODEN))
+ goto bad;
+ no.neg_node = 1;
+
+ IPXCPDEBUG((LOG_INFO,
+ "local node number %02X%02X%02X%02X%02X%02X",
+ NODE(p)));
+
+ if (!zero_node (p) && ao->accept_local &&
+ ! compare_node (p, ho->his_node))
+ copy_node (p, try.our_node);
+ break;
+
+ /* This has never been sent. Ignore the NAK frame */
+ case IPX_COMPRESSION_PROTOCOL:
+ goto bad;
+
+ case IPX_ROUTER_PROTOCOL:
+ if (!go->neg_router || (cilen < CILEN_PROTOCOL))
+ goto bad;
+
+ GETSHORT (s, p);
+ if (s > 15) /* This is just bad, but ignore for now. */
+ break;
+
+ s = BIT(s);
+ if (no.router & s) /* duplicate NAKs are always bad */
+ goto bad;
+
+ if (no.router == 0) /* Reset on first NAK only */
+ try.router = 0;
+
+ no.router |= s;
+ try.router |= s;
+ try.neg_router = 1;
+
+ IPXCPDEBUG((LOG_INFO, "Router protocol number %d", s));
+ break;
+
+ /* These, according to the RFC, must never be NAKed. */
+ case IPX_ROUTER_NAME:
+ case IPX_COMPLETE:
+ goto bad;
+
+ /* These are for options which we have not seen. */
+ default:
+ break;
+ }
+ p = next;
+ }
+
+ /* If there is still anything left, this packet is bad. */
+ if (len != 0)
+ goto bad;
+
+ /*
+ * Do not permit the peer to force a router protocol which we do not
+ * support. However, default to the condition that will accept "NONE".
+ */
+ try.router &= (ao->router | BIT(IPX_NONE));
+ if (try.router == 0 && ao->router != 0)
+ try.router = BIT(IPX_NONE);
+
+ if (try.router != 0)
+ try.neg_router = 1;
+
+ /*
+ * OK, the Nak is good. Now we can update state.
+ */
+ if (f->state != OPENED)
+ *go = try;
+
+ return 1;
+
+bad:
+ IPXCPDEBUG((LOG_INFO, "ipxcp_nakci: received bad Nak!"));
+ return 0;
+}
+
+/*
+ * ipxcp_rejci - Reject some of our CIs.
+ */
+static int
+ipxcp_rejci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ u_short cilen, citype, cishort;
+ u_char cichar;
+ u_int32_t cilong;
+ ipxcp_options try; /* options to request next time */
+
+#define REJCINETWORK(opt, neg, val) \
+ if (neg && p[0] == opt) { \
+ if ((len -= CILEN_NETN) < 0) \
+ break; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_NETN || \
+ citype != opt) \
+ break; \
+ GETLONG(cilong, p); \
+ if (cilong != val) \
+ break; \
+ IPXCPDEBUG((LOG_INFO,"ipxcp_rejci rejected long opt %d", opt)); \
+ neg = 0; \
+ }
+
+#define REJCICHARS(opt, neg, val, cnt) \
+ if (neg && p[0] == opt) { \
+ int indx, count = cnt; \
+ len -= (count + 2); \
+ if (len < 0) \
+ break; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != (count + 2) || \
+ citype != opt) \
+ break; \
+ for (indx = 0; indx < count; ++indx) {\
+ GETCHAR(cichar, p); \
+ if (cichar != ((u_char *) &val)[indx]) \
+ break; \
+ }\
+ if (indx != count) \
+ break; \
+ IPXCPDEBUG((LOG_INFO,"ipxcp_rejci rejected opt %d", opt)); \
+ neg = 0; \
+ }
+
+#define REJCINODE(opt,neg,val) REJCICHARS(opt,neg,val,sizeof(val))
+#define REJCINAME(opt,neg,val) REJCICHARS(opt,neg,val,strlen(val))
+
+#define REJCIVOID(opt, neg) \
+ if (neg && p[0] == opt) { \
+ if ((len -= CILEN_VOID) < 0) \
+ break; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_VOID || citype != opt) \
+ break; \
+ IPXCPDEBUG((LOG_INFO, "ipxcp_rejci rejected void opt %d", opt)); \
+ neg = 0; \
+ }
+
+/* a reject for RIP/SAP is invalid since we don't send it and you can't
+ reject something which is not sent. (You can NAK, but you can't REJ.) */
+#define REJCIPROTO(opt, neg, val, bit) \
+ if (neg && p[0] == opt) { \
+ if ((len -= CILEN_PROTOCOL) < 0) \
+ break; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_PROTOCOL) \
+ break; \
+ GETSHORT(cishort, p); \
+ if (cishort != to_external (val) || cishort == RIP_SAP) \
+ break; \
+ IPXCPDEBUG((LOG_INFO, "ipxcp_rejci short opt %d", opt)); \
+ neg = 0; \
+ }
+/*
+ * Any Rejected CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+ try = *go;
+
+ do {
+ REJCINETWORK (IPX_NETWORK_NUMBER, try.neg_nn, try.our_network);
+ REJCINODE (IPX_NODE_NUMBER, try.neg_node, try.our_node);
+ REJCINAME (IPX_ROUTER_NAME, try.neg_name, try.name);
+ REJCIPROTO (IPX_ROUTER_PROTOCOL, try.neg_router, try.router, 0);
+/*
+ * This is the end of the record.
+ */
+ if (len == 0) {
+ if (f->state != OPENED)
+ *go = try;
+ return (1);
+ }
+ } while (0);
+/*
+ * The frame is invalid at this point.
+ */
+ IPXCPDEBUG((LOG_INFO, "ipxcp_rejci: received bad Reject!"));
+ return 0;
+}
+
+/*
+ * ipxcp_reqci - Check the peer's requested CIs and send appropriate response.
+ *
+ * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
+ * appropriately. If reject_if_disagree is non-zero, doesn't return
+ * CONFNAK; returns CONFREJ if it can't return CONFACK.
+ */
+static int
+ipxcp_reqci(f, inp, len, reject_if_disagree)
+ fsm *f;
+ u_char *inp; /* Requested CIs */
+ int *len; /* Length of requested CIs */
+ int reject_if_disagree;
+{
+ u_char *cip, *next; /* Pointer to current and next CIs */
+ u_short cilen, citype; /* Parsed len, type */
+ u_short cishort; /* Parsed short value */
+ u_int32_t cinetwork; /* Parsed address values */
+ int rc = CONFACK; /* Final packet return code */
+ int orc; /* Individual option return code */
+ u_char *p; /* Pointer to next char to parse */
+ u_char *ucp = inp; /* Pointer to current output char */
+ int l = *len; /* Length left */
+
+ /*
+ * Reset all his options.
+ */
+ BZERO(ho, sizeof(*ho));
+
+ /*
+ * Process all his options.
+ */
+ next = inp;
+ while (l) {
+ orc = CONFACK; /* Assume success */
+ cip = p = next; /* Remember begining of CI */
+ if (l < 2 || /* Not enough data for CI header or */
+ p[1] < 2 || /* CI length too small or */
+ p[1] > l) { /* CI length too big? */
+ IPXCPDEBUG((LOG_INFO, "ipxcp_reqci: bad CI length!"));
+ orc = CONFREJ; /* Reject bad CI */
+ cilen = l; /* Reject till end of packet */
+ l = 0; /* Don't loop again */
+ goto endswitch;
+ }
+ GETCHAR(citype, p); /* Parse CI type */
+ GETCHAR(cilen, p); /* Parse CI length */
+ l -= cilen; /* Adjust remaining length */
+ next += cilen; /* Step to next CI */
+
+ switch (citype) { /* Check CI type */
+/*
+ * The network number must match. Choose the larger of the two.
+ */
+ case IPX_NETWORK_NUMBER:
+ IPXCPDEBUG((LOG_INFO, "ipxcp: received Network Number request"));
+
+ /* if we wont negotiate the network number or the length is wrong
+ then reject the option */
+ if ( !ao->neg_nn || cilen != CILEN_NETN ) {
+ orc = CONFREJ;
+ break;
+ }
+ GETLONG(cinetwork, p);
+ IPXCPDEBUG((LOG_INFO,"Remote proposed IPX network number is %8Lx",tl));
+
+ /* If the network numbers match then acknowledge them. */
+ if (cinetwork != 0) {
+ ho->his_network = cinetwork;
+ ho->neg_nn = 1;
+ if (wo->our_network == cinetwork)
+ break;
+/*
+ * If the network number is not given or we don't accept their change or
+ * the network number is too small then NAK it.
+ */
+ if (! ao->accept_network || cinetwork < wo->our_network) {
+ DECPTR (sizeof (u_int32_t), p);
+ PUTLONG (wo->our_network, p);
+ orc = CONFNAK;
+ }
+ break;
+ }
+/*
+ * The peer sent '0' for the network. Give it ours if we have one.
+ */
+ if (go->our_network != 0) {
+ DECPTR (sizeof (u_int32_t), p);
+ PUTLONG (wo->our_network, p);
+ orc = CONFNAK;
+/*
+ * We don't have one. Reject the value.
+ */
+ } else
+ orc = CONFREJ;
+
+ break;
+/*
+ * The node number is required
+ */
+ case IPX_NODE_NUMBER:
+ IPXCPDEBUG((LOG_INFO, "ipxcp: received Node Number request"));
+
+ /* if we wont negotiate the node number or the length is wrong
+ then reject the option */
+ if ( cilen != CILEN_NODEN ) {
+ orc = CONFREJ;
+ break;
+ }
+
+ copy_node (p, ho->his_node);
+ ho->neg_node = 1;
+/*
+ * If the remote does not have a number and we do then NAK it with the value
+ * which we have for it. (We never have a default value of zero.)
+ */
+ if (zero_node (ho->his_node)) {
+ orc = CONFNAK;
+ copy_node (wo->his_node, p);
+ INCPTR (sizeof (wo->his_node), p);
+ break;
+ }
+/*
+ * If you have given me the expected network node number then I'll accept
+ * it now.
+ */
+ if (compare_node (wo->his_node, ho->his_node)) {
+ orc = CONFACK;
+ ho->neg_node = 1;
+ INCPTR (sizeof (wo->his_node), p);
+ break;
+ }
+/*
+ * If his node number is the same as ours then ask him to try the next
+ * value.
+ */
+ if (compare_node (ho->his_node, go->our_node)) {
+ inc_node (ho->his_node);
+ orc = CONFNAK;
+ copy_node (ho->his_node, p);
+ INCPTR (sizeof (wo->his_node), p);
+ break;
+ }
+/*
+ * If we don't accept a new value then NAK it.
+ */
+ if (! ao->accept_remote) {
+ copy_node (wo->his_node, p);
+ INCPTR (sizeof (wo->his_node), p);
+ orc = CONFNAK;
+ break;
+ }
+ orc = CONFACK;
+ ho->neg_node = 1;
+ INCPTR (sizeof (wo->his_node), p);
+ break;
+/*
+ * Compression is not desired at this time. It is always rejected.
+ */
+ case IPX_COMPRESSION_PROTOCOL:
+ IPXCPDEBUG((LOG_INFO, "ipxcp: received Compression Protocol request "));
+ orc = CONFREJ;
+ break;
+/*
+ * The routing protocol is a bitmask of various types. Any combination
+ * of the values RIP_SAP and NLSP are permissible. 'IPX_NONE' for no
+ * routing protocol must be specified only once.
+ */
+ case IPX_ROUTER_PROTOCOL:
+ if ( !ao->neg_router || cilen < CILEN_PROTOCOL ) {
+ orc = CONFREJ;
+ break;
+ }
+
+ GETSHORT (cishort, p);
+ IPXCPDEBUG((LOG_INFO,
+ "Remote router protocol number 0x%04x",
+ cishort));
+
+ if (wo->neg_router == 0) {
+ wo->neg_router = 1;
+ wo->router = BIT(IPX_NONE);
+ }
+
+ if ((cishort == IPX_NONE && ho->router != 0) ||
+ (ho->router & BIT(IPX_NONE))) {
+ orc = CONFREJ;
+ break;
+ }
+
+ cishort = BIT(cishort);
+ if (ho->router & cishort) {
+ orc = CONFREJ;
+ break;
+ }
+
+ ho->router |= cishort;
+ ho->neg_router = 1;
+
+ /* Finally do not allow a router protocol which we do not
+ support. */
+
+ if ((cishort & (ao->router | BIT(IPX_NONE))) == 0) {
+ int protocol;
+
+ if (cishort == BIT(NLSP) &&
+ (ao->router & BIT(RIP_SAP)) &&
+ !wo->tried_rip) {
+ protocol = RIP_SAP;
+ wo->tried_rip = 1;
+ } else
+ protocol = IPX_NONE;
+
+ DECPTR (sizeof (u_int16_t), p);
+ PUTSHORT (protocol, p);
+ orc = CONFNAK;
+ }
+ break;
+/*
+ * The router name is advisorary. Just accept it if it is not too large.
+ */
+ case IPX_ROUTER_NAME:
+ IPXCPDEBUG((LOG_INFO, "ipxcp: received Router Name request"));
+ if (cilen >= CILEN_NAME) {
+ int name_size = cilen - CILEN_NAME;
+ if (name_size > sizeof (ho->name))
+ name_size = sizeof (ho->name) - 1;
+ memset (ho->name, 0, sizeof (ho->name));
+ memcpy (ho->name, p, name_size);
+ ho->name [name_size] = '\0';
+ ho->neg_name = 1;
+ orc = CONFACK;
+ break;
+ }
+ orc = CONFREJ;
+ break;
+/*
+ * This is advisorary.
+ */
+ case IPX_COMPLETE:
+ IPXCPDEBUG((LOG_INFO, "ipxcp: received Complete request"));
+ if (cilen != CILEN_COMPLETE)
+ orc = CONFREJ;
+ else {
+ ho->neg_complete = 1;
+ orc = CONFACK;
+ }
+ break;
+/*
+ * All other entries are not known at this time.
+ */
+ default:
+ IPXCPDEBUG((LOG_INFO, "ipxcp: received Complete request"));
+ orc = CONFREJ;
+ break;
+ }
+
+endswitch:
+ IPXCPDEBUG((LOG_INFO, " (%s)\n", CODENAME(orc)));
+
+ if (orc == CONFACK && /* Good CI */
+ rc != CONFACK) /* but prior CI wasnt? */
+ continue; /* Don't send this one */
+
+ if (orc == CONFNAK) { /* Nak this CI? */
+ if (reject_if_disagree) /* Getting fed up with sending NAKs? */
+ orc = CONFREJ; /* Get tough if so */
+ if (rc == CONFREJ) /* Rejecting prior CI? */
+ continue; /* Don't send this one */
+ if (rc == CONFACK) { /* Ack'd all prior CIs? */
+ rc = CONFNAK; /* Not anymore... */
+ ucp = inp; /* Backup */
+ }
+ }
+
+ if (orc == CONFREJ && /* Reject this CI */
+ rc != CONFREJ) { /* but no prior ones? */
+ rc = CONFREJ;
+ ucp = inp; /* Backup */
+ }
+
+ /* Need to move CI? */
+ if (ucp != cip)
+ BCOPY(cip, ucp, cilen); /* Move it */
+
+ /* Update output pointer */
+ INCPTR(cilen, ucp);
+ }
+
+ /*
+ * If we aren't rejecting this packet, and we want to negotiate
+ * their address, and they didn't send their address, then we
+ * send a NAK with an IPX_NODE_NUMBER option appended. We assume the
+ * input buffer is long enough that we can append the extra
+ * option safely.
+ */
+
+ if (rc != CONFREJ && !ho->neg_node &&
+ wo->req_nn && !reject_if_disagree) {
+ if (rc == CONFACK) {
+ rc = CONFNAK;
+ wo->req_nn = 0; /* don't ask again */
+ ucp = inp; /* reset pointer */
+ }
+
+ if (zero_node (wo->his_node))
+ inc_node (wo->his_node);
+
+ PUTCHAR (IPX_NODE_NUMBER, ucp);
+ PUTCHAR (CILEN_NODEN, ucp);
+ copy_node (wo->his_node, ucp);
+ INCPTR (sizeof (wo->his_node), ucp);
+ }
+
+ *len = ucp - inp; /* Compute output length */
+ IPXCPDEBUG((LOG_INFO, "ipxcp: returning Configure-%s", CODENAME(rc)));
+ return (rc); /* Return final code */
+}
+
+/*
+ * ipxcp_up - IPXCP has come UP.
+ *
+ * Configure the IP network interface appropriately and bring it up.
+ */
+
+static void
+ipxcp_up(f)
+ fsm *f;
+{
+ int unit = f->unit;
+
+ IPXCPDEBUG((LOG_INFO, "ipxcp: up"));
+
+ /* The default router protocol is RIP/SAP. */
+ if (ho->router == 0)
+ ho->router = BIT(RIP_SAP);
+
+ if (go->router == 0)
+ go->router = BIT(RIP_SAP);
+
+ /* Fetch the network number */
+ if (!ho->neg_nn)
+ ho->his_network = wo->his_network;
+
+ if (!ho->neg_node)
+ copy_node (wo->his_node, ho->his_node);
+
+ if (!wo->neg_node && !go->neg_node)
+ copy_node (wo->our_node, go->our_node);
+
+ if (zero_node (go->our_node)) {
+ static char errmsg[] = "Could not determine local IPX node address";
+ IPXCPDEBUG((LOG_ERR, errmsg));
+ ipxcp_close(f->unit, errmsg);
+ return;
+ }
+
+ go->network = go->our_network;
+ if (ho->his_network != 0 && ho->his_network > go->network)
+ go->network = ho->his_network;
+
+ if (go->network == 0) {
+ static char errmsg[] = "Can not determine network number";
+ IPXCPDEBUG((LOG_ERR, errmsg));
+ ipxcp_close (unit, errmsg);
+ return;
+ }
+
+ /* bring the interface up */
+ if (!sifup(unit)) {
+ IPXCPDEBUG((LOG_WARNING, "sifup failed"));
+ ipxcp_close(unit, "Interface configuration failed");
+ return;
+ }
+
+ /* set the network number for IPX */
+ if (!sipxfaddr(unit, go->network, go->our_node)) {
+ IPXCPDEBUG((LOG_WARNING, "sipxfaddr failed"));
+ ipxcp_close(unit, "Interface configuration failed");
+ return;
+ }
+
+ /*
+ * Execute the ipx-up script, like this:
+ * /etc/ppp/ipx-up interface tty speed local-IPX remote-IPX
+ */
+
+ ipxcp_script (f, _PATH_IPXUP);
+}
+
+/*
+ * ipxcp_down - IPXCP has gone DOWN.
+ *
+ * Take the IP network interface down, clear its addresses
+ * and delete routes through it.
+ */
+
+static void
+ipxcp_down(f)
+ fsm *f;
+{
+ IPXCPDEBUG((LOG_INFO, "ipxcp: down"));
+
+ cipxfaddr (f->unit);
+ sifdown(f->unit);
+ ipxcp_script (f, _PATH_IPXDOWN);
+}
+
+
+/*
+ * ipxcp_script - Execute a script with arguments
+ * interface-name tty-name speed local-IPX remote-IPX networks.
+ */
+static void
+ipxcp_script(f, script)
+ fsm *f;
+ char *script;
+{
+ char strspeed[32], strlocal[32], strremote[32];
+ char strnetwork[32], strpid[32];
+ char *argv[14], strproto_lcl[32], strproto_rmt[32];
+
+ sprintf (strpid, "%d", getpid());
+ sprintf (strspeed, "%d", baud_rate);
+
+ strproto_lcl[0] = '\0';
+ if (go->neg_router && ((go->router & BIT(IPX_NONE)) == 0)) {
+ if (go->router & BIT(RIP_SAP))
+ strcpy (strproto_lcl, "RIP ");
+ if (go->router & BIT(NLSP))
+ strcat (strproto_lcl, "NLSP ");
+ }
+
+ if (strproto_lcl[0] == '\0')
+ strcpy (strproto_lcl, "NONE ");
+
+ strproto_lcl[strlen (strproto_lcl)-1] = '\0';
+
+ strproto_rmt[0] = '\0';
+ if (ho->neg_router && ((ho->router & BIT(IPX_NONE)) == 0)) {
+ if (ho->router & BIT(RIP_SAP))
+ strcpy (strproto_rmt, "RIP ");
+ if (ho->router & BIT(NLSP))
+ strcat (strproto_rmt, "NLSP ");
+ }
+
+ if (strproto_rmt[0] == '\0')
+ strcpy (strproto_rmt, "NONE ");
+
+ strproto_rmt[strlen (strproto_rmt)-1] = '\0';
+
+ strcpy (strnetwork, ipx_ntoa (go->network));
+
+ sprintf (strlocal,
+ "%02X%02X%02X%02X%02X%02X",
+ NODE(go->our_node));
+
+ sprintf (strremote,
+ "%02X%02X%02X%02X%02X%02X",
+ NODE(ho->his_node));
+
+ argv[0] = script;
+ argv[1] = ifname;
+ argv[2] = devnam;
+ argv[3] = strspeed;
+ argv[4] = strnetwork;
+ argv[5] = strlocal;
+ argv[6] = strremote;
+ argv[7] = strproto_lcl;
+ argv[8] = strproto_rmt;
+ argv[9] = go->name;
+ argv[10] = ho->name;
+ argv[11] = ipparam;
+ argv[12] = strpid;
+ argv[13] = NULL;
+ run_program(script, argv, 0);
+}
+
+/*
+ * ipxcp_printpkt - print the contents of an IPXCP packet.
+ */
+static char *ipxcp_codenames[] = {
+ "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+ "TermReq", "TermAck", "CodeRej"
+};
+
+static int
+ipxcp_printpkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer)(void *, char *, ...);
+ void *arg;
+{
+ int code, id, len, olen;
+ u_char *pstart, *optend;
+ u_short cishort;
+ u_int32_t cilong;
+
+ if (plen < HEADERLEN)
+ return 0;
+ pstart = p;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < HEADERLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= sizeof(ipxcp_codenames) / sizeof(char *))
+ printer(arg, " %s", ipxcp_codenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+ printer(arg, " id=0x%x", id);
+ len -= HEADERLEN;
+ switch (code) {
+ case CONFREQ:
+ case CONFACK:
+ case CONFNAK:
+ case CONFREJ:
+ /* print option list */
+ while (len >= 2) {
+ GETCHAR(code, p);
+ GETCHAR(olen, p);
+ p -= 2;
+ if (olen < CILEN_VOID || olen > len) {
+ break;
+ }
+ printer(arg, " <");
+ len -= olen;
+ optend = p + olen;
+ switch (code) {
+ case IPX_NETWORK_NUMBER:
+ if (olen == CILEN_NETN) {
+ p += 2;
+ GETLONG(cilong, p);
+ printer (arg, "network %s", ipx_ntoa (cilong));
+ }
+ break;
+ case IPX_NODE_NUMBER:
+ if (olen == CILEN_NODEN) {
+ p += 2;
+ printer (arg, "node ");
+ while (p < optend) {
+ GETCHAR(code, p);
+ printer(arg, "%.2x", (int) (unsigned int) (unsigned char) code);
+ }
+ }
+ break;
+ case IPX_COMPRESSION_PROTOCOL:
+ if (olen == CILEN_COMPRESS) {
+ p += 2;
+ GETSHORT (cishort, p);
+ printer (arg, "compression %d", (int) cishort);
+ }
+ break;
+ case IPX_ROUTER_PROTOCOL:
+ if (olen == CILEN_PROTOCOL) {
+ p += 2;
+ GETSHORT (cishort, p);
+ printer (arg, "router proto %d", (int) cishort);
+ }
+ break;
+ case IPX_ROUTER_NAME:
+ if (olen >= CILEN_NAME) {
+ p += 2;
+ printer (arg, "router name \"");
+ while (p < optend) {
+ GETCHAR(code, p);
+ if (code >= 0x20 && code <= 0x7E)
+ printer (arg, "%c", (int) (unsigned int) (unsigned char) code);
+ else
+ printer (arg, " \\%.2x", (int) (unsigned int) (unsigned char) code);
+ }
+ printer (arg, "\"");
+ }
+ break;
+ case IPX_COMPLETE:
+ if (olen == CILEN_COMPLETE) {
+ p += 2;
+ printer (arg, "complete");
+ }
+ break;
+ default:
+ break;
+ }
+
+ while (p < optend) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", (int) (unsigned int) (unsigned char) code);
+ }
+ printer(arg, ">");
+ }
+ break;
+
+ case TERMACK:
+ case TERMREQ:
+ if (len > 0 && *p >= ' ' && *p < 0x7f) {
+ printer(arg, " ");
+ print_string(p, len, printer, arg);
+ p += len;
+ len = 0;
+ }
+ break;
+ }
+
+ /* print the rest of the bytes in the packet */
+ for (; len > 0; --len) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", (int) (unsigned int) (unsigned char) code);
+ }
+
+ return p - pstart;
+}
+#endif /* ifdef IPX_CHANGE */
diff --git a/usr.sbin/pppd/ipxcp.h b/usr.sbin/pppd/ipxcp.h
new file mode 100644
index 0000000..0890181
--- /dev/null
+++ b/usr.sbin/pppd/ipxcp.h
@@ -0,0 +1,71 @@
+/*
+ * ipxcp.h - IPX Control Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 Carnegie Mellon University. The name of the
+ * University 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Options.
+ */
+#define IPX_NETWORK_NUMBER 1 /* IPX Network Number */
+#define IPX_NODE_NUMBER 2
+#define IPX_COMPRESSION_PROTOCOL 3
+#define IPX_ROUTER_PROTOCOL 4
+#define IPX_ROUTER_NAME 5
+#define IPX_COMPLETE 6
+
+/* Values for the router protocol */
+#define IPX_NONE 0
+#define RIP_SAP 2
+#define NLSP 4
+
+typedef struct ipxcp_options {
+ int neg_node : 1; /* Negotiate IPX node number? */
+ int req_node : 1; /* Ask peer to send IPX node number? */
+
+ int neg_nn : 1; /* Negotiate IPX network number? */
+ int req_nn : 1; /* Ask peer to send IPX network number */
+
+ int neg_name : 1; /* Negotiate IPX router name */
+ int neg_complete : 1; /* Negotiate completion */
+ int neg_router : 1; /* Negotiate IPX router number */
+
+ int accept_local : 1; /* accept peer's value for ournode */
+ int accept_remote : 1; /* accept peer's value for hisnode */
+ int accept_network : 1; /* accept network number */
+
+ int tried_nlsp : 1; /* I have suggested NLSP already */
+ int tried_rip : 1; /* I have suggested RIP/SAP already */
+
+ u_int32_t his_network; /* base network number */
+ u_int32_t our_network; /* our value for network number */
+ u_int32_t network; /* the final network number */
+
+ u_char his_node[6]; /* peer's node number */
+ u_char our_node[6]; /* our node number */
+ u_char name [48]; /* name of the router */
+ int router; /* routing protocol */
+} ipxcp_options;
+
+extern fsm ipxcp_fsm[];
+extern ipxcp_options ipxcp_wantoptions[];
+extern ipxcp_options ipxcp_gotoptions[];
+extern ipxcp_options ipxcp_allowoptions[];
+extern ipxcp_options ipxcp_hisoptions[];
+
+extern struct protent ipxcp_protent;
diff --git a/usr.sbin/pppd/lcp.c b/usr.sbin/pppd/lcp.c
new file mode 100644
index 0000000..a10a460
--- /dev/null
+++ b/usr.sbin/pppd/lcp.c
@@ -0,0 +1,1859 @@
+/*
+ * lcp.c - PPP Link Control Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 Carnegie Mellon University. The name of the
+ * University 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <assert.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "chap.h"
+#include "magic.h"
+
+/* global vars */
+fsm lcp_fsm[NUM_PPP]; /* LCP fsm structure (global)*/
+lcp_options lcp_wantoptions[NUM_PPP]; /* Options that we want to request */
+lcp_options lcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */
+lcp_options lcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */
+lcp_options lcp_hisoptions[NUM_PPP]; /* Options that we ack'd */
+u_int32_t xmit_accm[NUM_PPP][8]; /* extended transmit ACCM */
+
+static u_int32_t lcp_echos_pending = 0; /* Number of outstanding echo msgs */
+static u_int32_t lcp_echo_number = 0; /* ID number of next echo frame */
+static u_int32_t lcp_echo_timer_running = 0; /* TRUE if a timer is running */
+
+static u_char nak_buffer[PPP_MRU]; /* where we construct a nak packet */
+
+/*
+ * Callbacks for fsm code. (CI = Configuration Information)
+ */
+static void lcp_resetci(fsm *); /* Reset our CI */
+static int lcp_cilen(fsm *); /* Return length of our CI */
+static void lcp_addci(fsm *, u_char *, int *); /* Add our CI to pkt */
+static int lcp_ackci(fsm *, u_char *, int); /* Peer ack'd our CI */
+static int lcp_nakci(fsm *, u_char *, int); /* Peer nak'd our CI */
+static int lcp_rejci(fsm *, u_char *, int); /* Peer rej'd our CI */
+static int lcp_reqci(fsm *, u_char *, int *, int); /* Rcv peer CI */
+static void lcp_up(fsm *); /* We're UP */
+static void lcp_down(fsm *); /* We're DOWN */
+static void lcp_starting(fsm *); /* We need lower layer up */
+static void lcp_finished(fsm *); /* We need lower layer down */
+static int lcp_extcode(fsm *, int, int, u_char *, int);
+static void lcp_rprotrej(fsm *, u_char *, int);
+
+/*
+ * routines to send LCP echos to peer
+ */
+
+static void lcp_echo_lowerup(int);
+static void lcp_echo_lowerdown(int);
+static void LcpEchoTimeout(void *);
+static void lcp_received_echo_reply(fsm *, int, u_char *, int);
+static void LcpSendEchoRequest(fsm *);
+static void LcpLinkFailure(fsm *);
+static void LcpEchoCheck(fsm *);
+
+static fsm_callbacks lcp_callbacks = { /* LCP callback routines */
+ lcp_resetci, /* Reset our Configuration Information */
+ lcp_cilen, /* Length of our Configuration Information */
+ lcp_addci, /* Add our Configuration Information */
+ lcp_ackci, /* ACK our Configuration Information */
+ lcp_nakci, /* NAK our Configuration Information */
+ lcp_rejci, /* Reject our Configuration Information */
+ lcp_reqci, /* Request peer's Configuration Information */
+ lcp_up, /* Called when fsm reaches OPENED state */
+ lcp_down, /* Called when fsm leaves OPENED state */
+ lcp_starting, /* Called when we want the lower layer up */
+ lcp_finished, /* Called when we want the lower layer down */
+ NULL, /* Called when Protocol-Reject received */
+ NULL, /* Retransmission is necessary */
+ lcp_extcode, /* Called to handle LCP-specific codes */
+ "LCP" /* String name of protocol */
+};
+
+/*
+ * Protocol entry points.
+ * Some of these are called directly.
+ */
+
+static void lcp_init(int);
+static void lcp_input(int, u_char *, int);
+static void lcp_protrej(int);
+static int lcp_printpkt(u_char *, int,
+ void (*)(void *, char *, ...), void *);
+
+struct protent lcp_protent = {
+ PPP_LCP,
+ lcp_init,
+ lcp_input,
+ lcp_protrej,
+ lcp_lowerup,
+ lcp_lowerdown,
+ lcp_open,
+ lcp_close,
+ lcp_printpkt,
+ NULL,
+ 1,
+ "LCP",
+ NULL,
+ NULL,
+ NULL
+};
+
+int lcp_loopbackfail = DEFLOOPBACKFAIL;
+
+/*
+ * Length of each type of configuration option (in octets)
+ */
+#define CILEN_VOID 2
+#define CILEN_CHAR 3
+#define CILEN_SHORT 4 /* CILEN_VOID + sizeof(short) */
+#define CILEN_CHAP 5 /* CILEN_VOID + sizeof(short) + 1 */
+#define CILEN_LONG 6 /* CILEN_VOID + sizeof(long) */
+#define CILEN_LQR 8 /* CILEN_VOID + sizeof(short) + sizeof(long) */
+#define CILEN_CBCP 3
+
+#define CODENAME(x) ((x) == CONFACK ? "ACK" : \
+ (x) == CONFNAK ? "NAK" : "REJ")
+
+
+/*
+ * lcp_init - Initialize LCP.
+ */
+static void
+lcp_init(unit)
+ int unit;
+{
+ fsm *f = &lcp_fsm[unit];
+ lcp_options *wo = &lcp_wantoptions[unit];
+ lcp_options *ao = &lcp_allowoptions[unit];
+
+ f->unit = unit;
+ f->protocol = PPP_LCP;
+ f->callbacks = &lcp_callbacks;
+
+ fsm_init(f);
+
+ wo->passive = 0;
+ wo->silent = 0;
+ wo->restart = 0; /* Set to 1 in kernels or multi-line
+ implementations */
+ wo->neg_mru = 1;
+ wo->mru = DEFMRU;
+ wo->neg_asyncmap = 0;
+ wo->asyncmap = 0;
+ wo->neg_chap = 0; /* Set to 1 on server */
+ wo->neg_upap = 0; /* Set to 1 on server */
+ wo->chap_mdtype = CHAP_DIGEST_MD5;
+ wo->neg_magicnumber = 1;
+ wo->neg_pcompression = 1;
+ wo->neg_accompression = 1;
+ wo->neg_lqr = 0; /* no LQR implementation yet */
+ wo->neg_cbcp = 0;
+
+ ao->neg_mru = 1;
+ ao->mru = MAXMRU;
+ ao->neg_asyncmap = 1;
+ ao->asyncmap = 0;
+ ao->neg_chap = 1;
+ ao->chap_mdtype = CHAP_DIGEST_MD5;
+ ao->neg_upap = 1;
+ ao->neg_magicnumber = 1;
+ ao->neg_pcompression = 1;
+ ao->neg_accompression = 1;
+ ao->neg_lqr = 0; /* no LQR implementation yet */
+#ifdef CBCP_SUPPORT
+ ao->neg_cbcp = 1;
+#else
+ ao->neg_cbcp = 0;
+#endif
+
+ memset(xmit_accm[unit], 0, sizeof(xmit_accm[0]));
+ xmit_accm[unit][3] = 0x60000000;
+}
+
+
+/*
+ * lcp_open - LCP is allowed to come up.
+ */
+void
+lcp_open(unit)
+ int unit;
+{
+ fsm *f = &lcp_fsm[unit];
+ lcp_options *wo = &lcp_wantoptions[unit];
+
+ f->flags = 0;
+ if (wo->passive)
+ f->flags |= OPT_PASSIVE;
+ if (wo->silent)
+ f->flags |= OPT_SILENT;
+ fsm_open(f);
+}
+
+
+/*
+ * lcp_close - Take LCP down.
+ */
+void
+lcp_close(unit, reason)
+ int unit;
+ char *reason;
+{
+ fsm *f = &lcp_fsm[unit];
+
+ if (phase != PHASE_DEAD)
+ phase = PHASE_TERMINATE;
+ if (f->state == STOPPED && f->flags & (OPT_PASSIVE|OPT_SILENT)) {
+ /*
+ * This action is not strictly according to the FSM in RFC1548,
+ * but it does mean that the program terminates if you do a
+ * lcp_close() in passive/silent mode when a connection hasn't
+ * been established.
+ */
+ f->state = CLOSED;
+ lcp_finished(f);
+
+ } else
+ fsm_close(&lcp_fsm[unit], reason);
+}
+
+
+/*
+ * lcp_lowerup - The lower layer is up.
+ */
+void
+lcp_lowerup(unit)
+ int unit;
+{
+ lcp_options *wo = &lcp_wantoptions[unit];
+
+ /*
+ * Don't use A/C or protocol compression on transmission,
+ * but accept A/C and protocol compressed packets
+ * if we are going to ask for A/C and protocol compression.
+ */
+ ppp_set_xaccm(unit, xmit_accm[unit]);
+ ppp_send_config(unit, PPP_MRU, 0xffffffff, 0, 0);
+ ppp_recv_config(unit, PPP_MRU, 0xffffffff,
+ wo->neg_pcompression, wo->neg_accompression);
+ peer_mru[unit] = PPP_MRU;
+ lcp_allowoptions[unit].asyncmap = xmit_accm[unit][0];
+
+ fsm_lowerup(&lcp_fsm[unit]);
+}
+
+
+/*
+ * lcp_lowerdown - The lower layer is down.
+ */
+void
+lcp_lowerdown(unit)
+ int unit;
+{
+ fsm_lowerdown(&lcp_fsm[unit]);
+}
+
+
+/*
+ * lcp_input - Input LCP packet.
+ */
+static void
+lcp_input(unit, p, len)
+ int unit;
+ u_char *p;
+ int len;
+{
+ fsm *f = &lcp_fsm[unit];
+
+ fsm_input(f, p, len);
+}
+
+
+/*
+ * lcp_extcode - Handle a LCP-specific code.
+ */
+static int
+lcp_extcode(f, code, id, inp, len)
+ fsm *f;
+ int code, id;
+ u_char *inp;
+ int len;
+{
+ u_char *magp;
+
+ switch( code ){
+ case PROTREJ:
+ lcp_rprotrej(f, inp, len);
+ break;
+
+ case ECHOREQ:
+ if (f->state != OPENED)
+ break;
+ LCPDEBUG((LOG_INFO, "lcp: Echo-Request, Rcvd id %d", id));
+ magp = inp;
+ PUTLONG(lcp_gotoptions[f->unit].magicnumber, magp);
+ fsm_sdata(f, ECHOREP, id, inp, len);
+ break;
+
+ case ECHOREP:
+ lcp_received_echo_reply(f, id, inp, len);
+ break;
+
+ case DISCREQ:
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+
+/*
+ * lcp_rprotrej - Receive a Protocol-Reject.
+ *
+ * Figure out which protocol is rejected and inform it.
+ */
+static void
+lcp_rprotrej(f, inp, len)
+ fsm *f;
+ u_char *inp;
+ int len;
+{
+ int i;
+ struct protent *protp;
+ u_short prot;
+
+ LCPDEBUG((LOG_INFO, "lcp_rprotrej."));
+
+ if (len < sizeof (u_short)) {
+ LCPDEBUG((LOG_INFO,
+ "lcp_rprotrej: Rcvd short Protocol-Reject packet!"));
+ return;
+ }
+
+ GETSHORT(prot, inp);
+
+ LCPDEBUG((LOG_INFO,
+ "lcp_rprotrej: Rcvd Protocol-Reject packet for %x!",
+ prot));
+
+ /*
+ * Protocol-Reject packets received in any state other than the LCP
+ * OPENED state SHOULD be silently discarded.
+ */
+ if( f->state != OPENED ){
+ LCPDEBUG((LOG_INFO, "Protocol-Reject discarded: LCP in state %d",
+ f->state));
+ return;
+ }
+
+ /*
+ * Upcall the proper Protocol-Reject routine.
+ */
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->protocol == prot && protp->enabled_flag) {
+ (*protp->protrej)(f->unit);
+ return;
+ }
+
+ syslog(LOG_WARNING, "Protocol-Reject for unsupported protocol 0x%x",
+ prot);
+}
+
+
+/*
+ * lcp_protrej - A Protocol-Reject was received.
+ */
+/*ARGSUSED*/
+static void
+lcp_protrej(unit)
+ int unit;
+{
+ /*
+ * Can't reject LCP!
+ */
+ LCPDEBUG((LOG_WARNING,
+ "lcp_protrej: Received Protocol-Reject for LCP!"));
+ fsm_protreject(&lcp_fsm[unit]);
+}
+
+
+/*
+ * lcp_sprotrej - Send a Protocol-Reject for some protocol.
+ */
+void
+lcp_sprotrej(unit, p, len)
+ int unit;
+ u_char *p;
+ int len;
+{
+ /*
+ * Send back the protocol and the information field of the
+ * rejected packet. We only get here if LCP is in the OPENED state.
+ */
+ p += 2;
+ len -= 2;
+
+ fsm_sdata(&lcp_fsm[unit], PROTREJ, ++lcp_fsm[unit].id,
+ p, len);
+}
+
+
+/*
+ * lcp_resetci - Reset our CI.
+ */
+static void
+lcp_resetci(f)
+ fsm *f;
+{
+ lcp_wantoptions[f->unit].magicnumber = magic();
+ lcp_wantoptions[f->unit].numloops = 0;
+ lcp_gotoptions[f->unit] = lcp_wantoptions[f->unit];
+ peer_mru[f->unit] = PPP_MRU;
+ auth_reset(f->unit);
+}
+
+
+/*
+ * lcp_cilen - Return length of our CI.
+ */
+static int
+lcp_cilen(f)
+ fsm *f;
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+
+#define LENCIVOID(neg) ((neg) ? CILEN_VOID : 0)
+#define LENCICHAP(neg) ((neg) ? CILEN_CHAP : 0)
+#define LENCISHORT(neg) ((neg) ? CILEN_SHORT : 0)
+#define LENCILONG(neg) ((neg) ? CILEN_LONG : 0)
+#define LENCILQR(neg) ((neg) ? CILEN_LQR: 0)
+#define LENCICBCP(neg) ((neg) ? CILEN_CBCP: 0)
+ /*
+ * NB: we only ask for one of CHAP and UPAP, even if we will
+ * accept either.
+ */
+ return (LENCISHORT(go->neg_mru && go->mru != DEFMRU) +
+ LENCILONG(go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) +
+ LENCICHAP(go->neg_chap) +
+ LENCISHORT(!go->neg_chap && go->neg_upap) +
+ LENCILQR(go->neg_lqr) +
+ LENCICBCP(go->neg_cbcp) +
+ LENCILONG(go->neg_magicnumber) +
+ LENCIVOID(go->neg_pcompression) +
+ LENCIVOID(go->neg_accompression));
+}
+
+
+/*
+ * lcp_addci - Add our desired CIs to a packet.
+ */
+static void
+lcp_addci(f, ucp, lenp)
+ fsm *f;
+ u_char *ucp;
+ int *lenp;
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ u_char *start_ucp = ucp;
+
+#define ADDCIVOID(opt, neg) \
+ if (neg) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_VOID, ucp); \
+ }
+#define ADDCISHORT(opt, neg, val) \
+ if (neg) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_SHORT, ucp); \
+ PUTSHORT(val, ucp); \
+ }
+#define ADDCICHAP(opt, neg, val, digest) \
+ if (neg) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_CHAP, ucp); \
+ PUTSHORT(val, ucp); \
+ PUTCHAR(digest, ucp); \
+ }
+#define ADDCILONG(opt, neg, val) \
+ if (neg) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_LONG, ucp); \
+ PUTLONG(val, ucp); \
+ }
+#define ADDCILQR(opt, neg, val) \
+ if (neg) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_LQR, ucp); \
+ PUTSHORT(PPP_LQR, ucp); \
+ PUTLONG(val, ucp); \
+ }
+#define ADDCICHAR(opt, neg, val) \
+ if (neg) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_CHAR, ucp); \
+ PUTCHAR(val, ucp); \
+ }
+
+ ADDCISHORT(CI_MRU, go->neg_mru && go->mru != DEFMRU, go->mru);
+ ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF,
+ go->asyncmap);
+ ADDCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype);
+ ADDCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP);
+ ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period);
+ ADDCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT);
+ ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
+ ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression);
+ ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression);
+
+ if (ucp - start_ucp != *lenp) {
+ /* this should never happen, because peer_mtu should be 1500 */
+ syslog(LOG_ERR, "Bug in lcp_addci: wrong length");
+ }
+}
+
+
+/*
+ * lcp_ackci - Ack our CIs.
+ * This should not modify any state if the Ack is bad.
+ *
+ * Returns:
+ * 0 - Ack was bad.
+ * 1 - Ack was good.
+ */
+static int
+lcp_ackci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ u_char cilen, citype, cichar;
+ u_short cishort;
+ u_int32_t cilong;
+
+ /*
+ * CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define ACKCIVOID(opt, neg) \
+ if (neg) { \
+ if ((len -= CILEN_VOID) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_VOID || \
+ citype != opt) \
+ goto bad; \
+ }
+#define ACKCISHORT(opt, neg, val) \
+ if (neg) { \
+ if ((len -= CILEN_SHORT) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_SHORT || \
+ citype != opt) \
+ goto bad; \
+ GETSHORT(cishort, p); \
+ if (cishort != val) \
+ goto bad; \
+ }
+#define ACKCICHAR(opt, neg, val) \
+ if (neg) { \
+ if ((len -= CILEN_CHAR) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_CHAR || \
+ citype != opt) \
+ goto bad; \
+ GETCHAR(cichar, p); \
+ if (cichar != val) \
+ goto bad; \
+ }
+#define ACKCICHAP(opt, neg, val, digest) \
+ if (neg) { \
+ if ((len -= CILEN_CHAP) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_CHAP || \
+ citype != opt) \
+ goto bad; \
+ GETSHORT(cishort, p); \
+ if (cishort != val) \
+ goto bad; \
+ GETCHAR(cichar, p); \
+ if (cichar != digest) \
+ goto bad; \
+ }
+#define ACKCILONG(opt, neg, val) \
+ if (neg) { \
+ if ((len -= CILEN_LONG) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_LONG || \
+ citype != opt) \
+ goto bad; \
+ GETLONG(cilong, p); \
+ if (cilong != val) \
+ goto bad; \
+ }
+#define ACKCILQR(opt, neg, val) \
+ if (neg) { \
+ if ((len -= CILEN_LQR) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_LQR || \
+ citype != opt) \
+ goto bad; \
+ GETSHORT(cishort, p); \
+ if (cishort != PPP_LQR) \
+ goto bad; \
+ GETLONG(cilong, p); \
+ if (cilong != val) \
+ goto bad; \
+ }
+
+ ACKCISHORT(CI_MRU, go->neg_mru && go->mru != DEFMRU, go->mru);
+ ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF,
+ go->asyncmap);
+ ACKCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype);
+ ACKCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP);
+ ACKCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period);
+ ACKCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT);
+ ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
+ ACKCIVOID(CI_PCOMPRESSION, go->neg_pcompression);
+ ACKCIVOID(CI_ACCOMPRESSION, go->neg_accompression);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ return (1);
+bad:
+ LCPDEBUG((LOG_WARNING, "lcp_acki: received bad Ack!"));
+ return (0);
+}
+
+
+/*
+ * lcp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if LCP is in the OPENED state.
+ *
+ * Returns:
+ * 0 - Nak was bad.
+ * 1 - Nak was good.
+ */
+static int
+lcp_nakci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ lcp_options *wo = &lcp_wantoptions[f->unit];
+ u_char citype, cichar, *next;
+ u_short cishort;
+ u_int32_t cilong;
+ lcp_options no; /* options we've seen Naks for */
+ lcp_options try; /* options to request next time */
+ int looped_back = 0;
+ int cilen;
+
+ BZERO(&no, sizeof(no));
+ try = *go;
+
+ /*
+ * Any Nak'd CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define NAKCIVOID(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_VOID && \
+ p[1] == CILEN_VOID && \
+ p[0] == opt) { \
+ len -= CILEN_VOID; \
+ INCPTR(CILEN_VOID, p); \
+ no.neg = 1; \
+ code \
+ }
+#define NAKCICHAP(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_CHAP && \
+ p[1] == CILEN_CHAP && \
+ p[0] == opt) { \
+ len -= CILEN_CHAP; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ GETCHAR(cichar, p); \
+ no.neg = 1; \
+ code \
+ }
+#define NAKCICHAR(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_CHAR && \
+ p[1] == CILEN_CHAR && \
+ p[0] == opt) { \
+ len -= CILEN_CHAR; \
+ INCPTR(2, p); \
+ GETCHAR(cichar, p); \
+ no.neg = 1; \
+ code \
+ }
+#define NAKCISHORT(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_SHORT && \
+ p[1] == CILEN_SHORT && \
+ p[0] == opt) { \
+ len -= CILEN_SHORT; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ no.neg = 1; \
+ code \
+ }
+#define NAKCILONG(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_LONG && \
+ p[1] == CILEN_LONG && \
+ p[0] == opt) { \
+ len -= CILEN_LONG; \
+ INCPTR(2, p); \
+ GETLONG(cilong, p); \
+ no.neg = 1; \
+ code \
+ }
+#define NAKCILQR(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_LQR && \
+ p[1] == CILEN_LQR && \
+ p[0] == opt) { \
+ len -= CILEN_LQR; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ GETLONG(cilong, p); \
+ no.neg = 1; \
+ code \
+ }
+
+ /*
+ * We don't care if they want to send us smaller packets than
+ * we want. Therefore, accept any MRU less than what we asked for,
+ * but then ignore the new value when setting the MRU in the kernel.
+ * If they send us a bigger MRU than what we asked, accept it, up to
+ * the limit of the default MRU we'd get if we didn't negotiate.
+ */
+ if (go->neg_mru && go->mru != DEFMRU) {
+ NAKCISHORT(CI_MRU, neg_mru,
+ if (cishort <= wo->mru || cishort <= DEFMRU)
+ try.mru = cishort;
+ );
+ }
+
+ /*
+ * Add any characters they want to our (receive-side) asyncmap.
+ */
+ if (go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF) {
+ NAKCILONG(CI_ASYNCMAP, neg_asyncmap,
+ try.asyncmap = go->asyncmap | cilong;
+ );
+ }
+
+ /*
+ * If they've nak'd our authentication-protocol, check whether
+ * they are proposing a different protocol, or a different
+ * hash algorithm for CHAP.
+ */
+ if ((go->neg_chap || go->neg_upap)
+ && len >= CILEN_SHORT
+ && p[0] == CI_AUTHTYPE && p[1] >= CILEN_SHORT && p[1] <= len) {
+ cilen = p[1];
+ len -= cilen;
+ no.neg_chap = go->neg_chap;
+ no.neg_upap = go->neg_upap;
+ INCPTR(2, p);
+ GETSHORT(cishort, p);
+ if (cishort == PPP_PAP && cilen == CILEN_SHORT) {
+ /*
+ * If we were asking for CHAP, they obviously don't want to do it.
+ * If we weren't asking for CHAP, then we were asking for PAP,
+ * in which case this Nak is bad.
+ */
+ if (!go->neg_chap)
+ goto bad;
+ try.neg_chap = 0;
+
+ } else if (cishort == PPP_CHAP && cilen == CILEN_CHAP) {
+ GETCHAR(cichar, p);
+ if (go->neg_chap) {
+ /*
+ * We were asking for CHAP/MD5; they must want a different
+ * algorithm. If they can't do MD5, we'll have to stop
+ * asking for CHAP.
+ */
+ if (cichar != go->chap_mdtype)
+ try.neg_chap = 0;
+ } else {
+ /*
+ * Stop asking for PAP if we were asking for it.
+ */
+ try.neg_upap = 0;
+ }
+
+ } else {
+ /*
+ * We don't recognize what they're suggesting.
+ * Stop asking for what we were asking for.
+ */
+ if (go->neg_chap)
+ try.neg_chap = 0;
+ else
+ try.neg_upap = 0;
+ p += cilen - CILEN_SHORT;
+ }
+ }
+
+ /*
+ * If they can't cope with our link quality protocol, we'll have
+ * to stop asking for LQR. We haven't got any other protocol.
+ * If they Nak the reporting period, take their value XXX ?
+ */
+ NAKCILQR(CI_QUALITY, neg_lqr,
+ if (cishort != PPP_LQR)
+ try.neg_lqr = 0;
+ else
+ try.lqr_period = cilong;
+ );
+
+ /*
+ * Only implementing CBCP...not the rest of the callback options
+ */
+ NAKCICHAR(CI_CALLBACK, neg_cbcp,
+ try.neg_cbcp = 0;
+ );
+
+ /*
+ * Check for a looped-back line.
+ */
+ NAKCILONG(CI_MAGICNUMBER, neg_magicnumber,
+ try.magicnumber = magic();
+ looped_back = 1;
+ );
+
+ /*
+ * Peer shouldn't send Nak for protocol compression or
+ * address/control compression requests; they should send
+ * a Reject instead. If they send a Nak, treat it as a Reject.
+ */
+ NAKCIVOID(CI_PCOMPRESSION, neg_pcompression,
+ try.neg_pcompression = 0;
+ );
+ NAKCIVOID(CI_ACCOMPRESSION, neg_accompression,
+ try.neg_accompression = 0;
+ );
+
+ /*
+ * There may be remaining CIs, if the peer is requesting negotiation
+ * on an option that we didn't include in our request packet.
+ * If we see an option that we requested, or one we've already seen
+ * in this packet, then this packet is bad.
+ * If we wanted to respond by starting to negotiate on the requested
+ * option(s), we could, but we don't, because except for the
+ * authentication type and quality protocol, if we are not negotiating
+ * an option, it is because we were told not to.
+ * For the authentication type, the Nak from the peer means
+ * `let me authenticate myself with you' which is a bit pointless.
+ * For the quality protocol, the Nak means `ask me to send you quality
+ * reports', but if we didn't ask for them, we don't want them.
+ * An option we don't recognize represents the peer asking to
+ * negotiate some option we don't support, so ignore it.
+ */
+ while (len > CILEN_VOID) {
+ GETCHAR(citype, p);
+ GETCHAR(cilen, p);
+ if (cilen < CILEN_VOID || (len -= cilen) < 0)
+ goto bad;
+ next = p + cilen - 2;
+
+ switch (citype) {
+ case CI_MRU:
+ if ((go->neg_mru && go->mru != DEFMRU)
+ || no.neg_mru || cilen != CILEN_SHORT)
+ goto bad;
+ GETSHORT(cishort, p);
+ if (cishort < DEFMRU)
+ try.mru = cishort;
+ break;
+ case CI_ASYNCMAP:
+ if ((go->neg_asyncmap && go->asyncmap != 0xFFFFFFFF)
+ || no.neg_asyncmap || cilen != CILEN_LONG)
+ goto bad;
+ break;
+ case CI_AUTHTYPE:
+ if (go->neg_chap || no.neg_chap || go->neg_upap || no.neg_upap)
+ goto bad;
+ break;
+ case CI_MAGICNUMBER:
+ if (go->neg_magicnumber || no.neg_magicnumber ||
+ cilen != CILEN_LONG)
+ goto bad;
+ break;
+ case CI_PCOMPRESSION:
+ if (go->neg_pcompression || no.neg_pcompression
+ || cilen != CILEN_VOID)
+ goto bad;
+ break;
+ case CI_ACCOMPRESSION:
+ if (go->neg_accompression || no.neg_accompression
+ || cilen != CILEN_VOID)
+ goto bad;
+ break;
+ case CI_QUALITY:
+ if (go->neg_lqr || no.neg_lqr || cilen != CILEN_LQR)
+ goto bad;
+ break;
+ }
+ p = next;
+ }
+
+ /* If there is still anything left, this packet is bad. */
+ if (len != 0)
+ goto bad;
+
+ /*
+ * OK, the Nak is good. Now we can update state.
+ */
+ if (f->state != OPENED) {
+ if (looped_back) {
+ if (++try.numloops >= lcp_loopbackfail) {
+ syslog(LOG_NOTICE, "Serial line is looped back.");
+ lcp_close(f->unit, "Loopback detected");
+ }
+ } else
+ try.numloops = 0;
+ *go = try;
+ }
+
+ return 1;
+
+bad:
+ LCPDEBUG((LOG_WARNING, "lcp_nakci: received bad Nak!"));
+ return 0;
+}
+
+
+/*
+ * lcp_rejci - Peer has Rejected some of our CIs.
+ * This should not modify any state if the Reject is bad
+ * or if LCP is in the OPENED state.
+ *
+ * Returns:
+ * 0 - Reject was bad.
+ * 1 - Reject was good.
+ */
+static int
+lcp_rejci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ u_char cichar;
+ u_short cishort;
+ u_int32_t cilong;
+ lcp_options try; /* options to request next time */
+
+ try = *go;
+
+ /*
+ * Any Rejected CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define REJCIVOID(opt, neg) \
+ if (go->neg && \
+ len >= CILEN_VOID && \
+ p[1] == CILEN_VOID && \
+ p[0] == opt) { \
+ len -= CILEN_VOID; \
+ INCPTR(CILEN_VOID, p); \
+ try.neg = 0; \
+ LCPDEBUG((LOG_INFO, "lcp_rejci rejected void opt %d", opt)); \
+ }
+#define REJCISHORT(opt, neg, val) \
+ if (go->neg && \
+ len >= CILEN_SHORT && \
+ p[1] == CILEN_SHORT && \
+ p[0] == opt) { \
+ len -= CILEN_SHORT; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ /* Check rejected value. */ \
+ if (cishort != val) \
+ goto bad; \
+ try.neg = 0; \
+ LCPDEBUG((LOG_INFO,"lcp_rejci rejected short opt %d", opt)); \
+ }
+#define REJCICHAP(opt, neg, val, digest) \
+ if (go->neg && \
+ len >= CILEN_CHAP && \
+ p[1] == CILEN_CHAP && \
+ p[0] == opt) { \
+ len -= CILEN_CHAP; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ GETCHAR(cichar, p); \
+ /* Check rejected value. */ \
+ if (cishort != val || cichar != digest) \
+ goto bad; \
+ try.neg = 0; \
+ try.neg_upap = 0; \
+ LCPDEBUG((LOG_INFO,"lcp_rejci rejected chap opt %d", opt)); \
+ }
+#define REJCILONG(opt, neg, val) \
+ if (go->neg && \
+ len >= CILEN_LONG && \
+ p[1] == CILEN_LONG && \
+ p[0] == opt) { \
+ len -= CILEN_LONG; \
+ INCPTR(2, p); \
+ GETLONG(cilong, p); \
+ /* Check rejected value. */ \
+ if (cilong != val) \
+ goto bad; \
+ try.neg = 0; \
+ LCPDEBUG((LOG_INFO,"lcp_rejci rejected long opt %d", opt)); \
+ }
+#define REJCILQR(opt, neg, val) \
+ if (go->neg && \
+ len >= CILEN_LQR && \
+ p[1] == CILEN_LQR && \
+ p[0] == opt) { \
+ len -= CILEN_LQR; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ GETLONG(cilong, p); \
+ /* Check rejected value. */ \
+ if (cishort != PPP_LQR || cilong != val) \
+ goto bad; \
+ try.neg = 0; \
+ LCPDEBUG((LOG_INFO,"lcp_rejci rejected LQR opt %d", opt)); \
+ }
+#define REJCICBCP(opt, neg, val) \
+ if (go->neg && \
+ len >= CILEN_CBCP && \
+ p[1] == CILEN_CBCP && \
+ p[0] == opt) { \
+ len -= CILEN_CBCP; \
+ INCPTR(2, p); \
+ GETCHAR(cichar, p); \
+ /* Check rejected value. */ \
+ if (cichar != val) \
+ goto bad; \
+ try.neg = 0; \
+ LCPDEBUG((LOG_INFO,"lcp_rejci rejected Callback opt %d", opt)); \
+ }
+
+ REJCISHORT(CI_MRU, neg_mru, go->mru);
+ REJCILONG(CI_ASYNCMAP, neg_asyncmap, go->asyncmap);
+ REJCICHAP(CI_AUTHTYPE, neg_chap, PPP_CHAP, go->chap_mdtype);
+ if (!go->neg_chap) {
+ REJCISHORT(CI_AUTHTYPE, neg_upap, PPP_PAP);
+ }
+ REJCILQR(CI_QUALITY, neg_lqr, go->lqr_period);
+ REJCICBCP(CI_CALLBACK, neg_cbcp, CBCP_OPT);
+ REJCILONG(CI_MAGICNUMBER, neg_magicnumber, go->magicnumber);
+ REJCIVOID(CI_PCOMPRESSION, neg_pcompression);
+ REJCIVOID(CI_ACCOMPRESSION, neg_accompression);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ /*
+ * Now we can update state.
+ */
+ if (f->state != OPENED)
+ *go = try;
+ return 1;
+
+bad:
+ LCPDEBUG((LOG_WARNING, "lcp_rejci: received bad Reject!"));
+ return 0;
+}
+
+
+/*
+ * lcp_reqci - Check the peer's requested CIs and send appropriate response.
+ *
+ * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
+ * appropriately. If reject_if_disagree is non-zero, doesn't return
+ * CONFNAK; returns CONFREJ if it can't return CONFACK.
+ */
+static int
+lcp_reqci(f, inp, lenp, reject_if_disagree)
+ fsm *f;
+ u_char *inp; /* Requested CIs */
+ int *lenp; /* Length of requested CIs */
+ int reject_if_disagree;
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ lcp_options *ho = &lcp_hisoptions[f->unit];
+ lcp_options *ao = &lcp_allowoptions[f->unit];
+ u_char *cip, *next; /* Pointer to current and next CIs */
+ int cilen, citype, cichar; /* Parsed len, type, char value */
+ u_short cishort; /* Parsed short value */
+ u_int32_t cilong; /* Parse long value */
+ int rc = CONFACK; /* Final packet return code */
+ int orc; /* Individual option return code */
+ u_char *p; /* Pointer to next char to parse */
+ u_char *rejp; /* Pointer to next char in reject frame */
+ u_char *nakp; /* Pointer to next char in Nak frame */
+ int l = *lenp; /* Length left */
+
+ /*
+ * Reset all his options.
+ */
+ BZERO(ho, sizeof(*ho));
+
+ /*
+ * Process all his options.
+ */
+ next = inp;
+ nakp = nak_buffer;
+ rejp = inp;
+ while (l) {
+ orc = CONFACK; /* Assume success */
+ cip = p = next; /* Remember begining of CI */
+ if (l < 2 || /* Not enough data for CI header or */
+ p[1] < 2 || /* CI length too small or */
+ p[1] > l) { /* CI length too big? */
+ LCPDEBUG((LOG_WARNING, "lcp_reqci: bad CI length!"));
+ orc = CONFREJ; /* Reject bad CI */
+ cilen = l; /* Reject till end of packet */
+ l = 0; /* Don't loop again */
+ citype = 0;
+ goto endswitch;
+ }
+ GETCHAR(citype, p); /* Parse CI type */
+ GETCHAR(cilen, p); /* Parse CI length */
+ l -= cilen; /* Adjust remaining length */
+ next += cilen; /* Step to next CI */
+
+ switch (citype) { /* Check CI type */
+ case CI_MRU:
+ LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd MRU"));
+ if (!ao->neg_mru || /* Allow option? */
+ cilen != CILEN_SHORT) { /* Check CI length */
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+ GETSHORT(cishort, p); /* Parse MRU */
+ LCPDEBUG((LOG_INFO, "(%d)", cishort));
+
+ /*
+ * He must be able to receive at least our minimum.
+ * No need to check a maximum. If he sends a large number,
+ * we'll just ignore it.
+ */
+ if (cishort < MINMRU) {
+ orc = CONFNAK; /* Nak CI */
+ PUTCHAR(CI_MRU, nakp);
+ PUTCHAR(CILEN_SHORT, nakp);
+ PUTSHORT(MINMRU, nakp); /* Give him a hint */
+ break;
+ }
+ ho->neg_mru = 1; /* Remember he sent MRU */
+ ho->mru = cishort; /* And remember value */
+ break;
+
+ case CI_ASYNCMAP:
+ LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd ASYNCMAP"));
+ if (!ao->neg_asyncmap ||
+ cilen != CILEN_LONG) {
+ orc = CONFREJ;
+ break;
+ }
+ GETLONG(cilong, p);
+ LCPDEBUG((LOG_INFO, "(%x)", (unsigned int) cilong));
+
+ /*
+ * Asyncmap must have set at least the bits
+ * which are set in lcp_allowoptions[unit].asyncmap.
+ */
+ if ((ao->asyncmap & ~cilong) != 0) {
+ orc = CONFNAK;
+ PUTCHAR(CI_ASYNCMAP, nakp);
+ PUTCHAR(CILEN_LONG, nakp);
+ PUTLONG(ao->asyncmap | cilong, nakp);
+ break;
+ }
+ ho->neg_asyncmap = 1;
+ ho->asyncmap = cilong;
+ break;
+
+ case CI_AUTHTYPE:
+ LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd AUTHTYPE"));
+ if (cilen < CILEN_SHORT ||
+ !(ao->neg_upap || ao->neg_chap)) {
+ /*
+ * Reject the option if we're not willing to authenticate.
+ */
+ orc = CONFREJ;
+ break;
+ }
+ GETSHORT(cishort, p);
+ LCPDEBUG((LOG_INFO, "(%x)", cishort));
+
+ /*
+ * Authtype must be UPAP or CHAP.
+ *
+ * Note: if both ao->neg_upap and ao->neg_chap are set,
+ * and the peer sends a Configure-Request with two
+ * authenticate-protocol requests, one for CHAP and one
+ * for UPAP, then we will reject the second request.
+ * Whether we end up doing CHAP or UPAP depends then on
+ * the ordering of the CIs in the peer's Configure-Request.
+ */
+
+ if (cishort == PPP_PAP) {
+ if (ho->neg_chap || /* we've already accepted CHAP */
+ cilen != CILEN_SHORT) {
+ LCPDEBUG((LOG_WARNING,
+ "lcp_reqci: rcvd AUTHTYPE PAP, rejecting..."));
+ orc = CONFREJ;
+ break;
+ }
+ if (!ao->neg_upap) { /* we don't want to do PAP */
+ orc = CONFNAK; /* NAK it and suggest CHAP */
+ PUTCHAR(CI_AUTHTYPE, nakp);
+ PUTCHAR(CILEN_CHAP, nakp);
+ PUTSHORT(PPP_CHAP, nakp);
+ PUTCHAR(ao->chap_mdtype, nakp);
+ break;
+ }
+ ho->neg_upap = 1;
+ break;
+ }
+ if (cishort == PPP_CHAP) {
+ if (ho->neg_upap || /* we've already accepted PAP */
+ cilen != CILEN_CHAP) {
+ LCPDEBUG((LOG_INFO,
+ "lcp_reqci: rcvd AUTHTYPE CHAP, rejecting..."));
+ orc = CONFREJ;
+ break;
+ }
+ if (!ao->neg_chap) { /* we don't want to do CHAP */
+ orc = CONFNAK; /* NAK it and suggest PAP */
+ PUTCHAR(CI_AUTHTYPE, nakp);
+ PUTCHAR(CILEN_SHORT, nakp);
+ PUTSHORT(PPP_PAP, nakp);
+ break;
+ }
+ GETCHAR(cichar, p); /* get digest type*/
+ if (cichar != CHAP_DIGEST_MD5
+#ifdef CHAPMS
+ && cichar != CHAP_MICROSOFT
+#endif
+ ) {
+ orc = CONFNAK;
+ PUTCHAR(CI_AUTHTYPE, nakp);
+ PUTCHAR(CILEN_CHAP, nakp);
+ PUTSHORT(PPP_CHAP, nakp);
+ PUTCHAR(ao->chap_mdtype, nakp);
+ break;
+ }
+ ho->chap_mdtype = cichar; /* save md type */
+ ho->neg_chap = 1;
+ break;
+ }
+
+ /*
+ * We don't recognize the protocol they're asking for.
+ * Nak it with something we're willing to do.
+ * (At this point we know ao->neg_upap || ao->neg_chap.)
+ */
+ orc = CONFNAK;
+ PUTCHAR(CI_AUTHTYPE, nakp);
+ if (ao->neg_chap) {
+ PUTCHAR(CILEN_CHAP, nakp);
+ PUTSHORT(PPP_CHAP, nakp);
+ PUTCHAR(ao->chap_mdtype, nakp);
+ } else {
+ PUTCHAR(CILEN_SHORT, nakp);
+ PUTSHORT(PPP_PAP, nakp);
+ }
+ break;
+
+ case CI_QUALITY:
+ LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd QUALITY"));
+ if (!ao->neg_lqr ||
+ cilen != CILEN_LQR) {
+ orc = CONFREJ;
+ break;
+ }
+
+ GETSHORT(cishort, p);
+ GETLONG(cilong, p);
+ LCPDEBUG((LOG_INFO, "(%x %x)", cishort, (unsigned int) cilong));
+
+ /*
+ * Check the protocol and the reporting period.
+ * XXX When should we Nak this, and what with?
+ */
+ if (cishort != PPP_LQR) {
+ orc = CONFNAK;
+ PUTCHAR(CI_QUALITY, nakp);
+ PUTCHAR(CILEN_LQR, nakp);
+ PUTSHORT(PPP_LQR, nakp);
+ PUTLONG(ao->lqr_period, nakp);
+ break;
+ }
+ break;
+
+ case CI_MAGICNUMBER:
+ LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd MAGICNUMBER"));
+ if (!(ao->neg_magicnumber || go->neg_magicnumber) ||
+ cilen != CILEN_LONG) {
+ orc = CONFREJ;
+ break;
+ }
+ GETLONG(cilong, p);
+ LCPDEBUG((LOG_INFO, "(%x)", (unsigned int) cilong));
+
+ /*
+ * He must have a different magic number.
+ */
+ if (go->neg_magicnumber &&
+ cilong == go->magicnumber) {
+ cilong = magic(); /* Don't put magic() inside macro! */
+ orc = CONFNAK;
+ PUTCHAR(CI_MAGICNUMBER, nakp);
+ PUTCHAR(CILEN_LONG, nakp);
+ PUTLONG(cilong, nakp);
+ break;
+ }
+ ho->neg_magicnumber = 1;
+ ho->magicnumber = cilong;
+ break;
+
+
+ case CI_PCOMPRESSION:
+ LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd PCOMPRESSION"));
+ if (!ao->neg_pcompression ||
+ cilen != CILEN_VOID) {
+ orc = CONFREJ;
+ break;
+ }
+ ho->neg_pcompression = 1;
+ break;
+
+ case CI_ACCOMPRESSION:
+ LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd ACCOMPRESSION"));
+ if (!ao->neg_accompression ||
+ cilen != CILEN_VOID) {
+ orc = CONFREJ;
+ break;
+ }
+ ho->neg_accompression = 1;
+ break;
+
+ default:
+ LCPDEBUG((LOG_INFO, "lcp_reqci: rcvd unknown option %d",
+ citype));
+ orc = CONFREJ;
+ break;
+ }
+
+endswitch:
+ LCPDEBUG((LOG_INFO, " (%s)", CODENAME(orc)));
+ if (orc == CONFACK && /* Good CI */
+ rc != CONFACK) /* but prior CI wasnt? */
+ continue; /* Don't send this one */
+
+ if (orc == CONFNAK) { /* Nak this CI? */
+ if (reject_if_disagree /* Getting fed up with sending NAKs? */
+ && citype != CI_MAGICNUMBER) {
+ orc = CONFREJ; /* Get tough if so */
+ } else {
+ if (rc == CONFREJ) /* Rejecting prior CI? */
+ continue; /* Don't send this one */
+ rc = CONFNAK;
+ }
+ }
+ if (orc == CONFREJ) { /* Reject this CI */
+ rc = CONFREJ;
+ if (cip != rejp) /* Need to move rejected CI? */
+ BCOPY(cip, rejp, cilen); /* Move it */
+ INCPTR(cilen, rejp); /* Update output pointer */
+ }
+ }
+
+ /*
+ * If we wanted to send additional NAKs (for unsent CIs), the
+ * code would go here. The extra NAKs would go at *nakp.
+ * At present there are no cases where we want to ask the
+ * peer to negotiate an option.
+ */
+
+ switch (rc) {
+ case CONFACK:
+ *lenp = next - inp;
+ break;
+ case CONFNAK:
+ /*
+ * Copy the Nak'd options from the nak_buffer to the caller's buffer.
+ */
+ *lenp = nakp - nak_buffer;
+ BCOPY(nak_buffer, inp, *lenp);
+ break;
+ case CONFREJ:
+ *lenp = rejp - inp;
+ break;
+ }
+
+ LCPDEBUG((LOG_INFO, "lcp_reqci: returning CONF%s.", CODENAME(rc)));
+ return (rc); /* Return final code */
+}
+
+
+/*
+ * lcp_up - LCP has come UP.
+ */
+static void
+lcp_up(f)
+ fsm *f;
+{
+ lcp_options *wo = &lcp_wantoptions[f->unit];
+ lcp_options *ho = &lcp_hisoptions[f->unit];
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ lcp_options *ao = &lcp_allowoptions[f->unit];
+
+ if (!go->neg_magicnumber)
+ go->magicnumber = 0;
+ if (!ho->neg_magicnumber)
+ ho->magicnumber = 0;
+
+ /*
+ * Set our MTU to the smaller of the MTU we wanted and
+ * the MRU our peer wanted. If we negotiated an MRU,
+ * set our MRU to the larger of value we wanted and
+ * the value we got in the negotiation.
+ */
+ ppp_send_config(f->unit, MIN(ao->mru, (ho->neg_mru? ho->mru: PPP_MRU)),
+ (ho->neg_asyncmap? ho->asyncmap: 0xffffffff),
+ ho->neg_pcompression, ho->neg_accompression);
+ ppp_recv_config(f->unit, (go->neg_mru? MAX(wo->mru, go->mru): PPP_MRU),
+ (go->neg_asyncmap? go->asyncmap: 0xffffffff),
+ go->neg_pcompression, go->neg_accompression);
+
+ if (ho->neg_mru)
+ peer_mru[f->unit] = ho->mru;
+
+ lcp_echo_lowerup(f->unit); /* Enable echo messages */
+
+ link_established(f->unit);
+}
+
+
+/*
+ * lcp_down - LCP has gone DOWN.
+ *
+ * Alert other protocols.
+ */
+static void
+lcp_down(f)
+ fsm *f;
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+
+ lcp_echo_lowerdown(f->unit);
+
+ link_down(f->unit);
+
+ ppp_send_config(f->unit, PPP_MRU, 0xffffffff, 0, 0);
+ ppp_recv_config(f->unit, PPP_MRU,
+ (go->neg_asyncmap? go->asyncmap: 0xffffffff),
+ go->neg_pcompression, go->neg_accompression);
+ peer_mru[f->unit] = PPP_MRU;
+}
+
+
+/*
+ * lcp_starting - LCP needs the lower layer up.
+ */
+static void
+lcp_starting(f)
+ fsm *f;
+{
+ link_required(f->unit);
+}
+
+
+/*
+ * lcp_finished - LCP has finished with the lower layer.
+ */
+static void
+lcp_finished(f)
+ fsm *f;
+{
+ link_terminated(f->unit);
+}
+
+
+/*
+ * lcp_printpkt - print the contents of an LCP packet.
+ */
+static char *lcp_codenames[] = {
+ "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+ "TermReq", "TermAck", "CodeRej", "ProtRej",
+ "EchoReq", "EchoRep", "DiscReq"
+};
+
+static int
+lcp_printpkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer)(void *, char *, ...);
+ void *arg;
+{
+ int code, id, len, olen;
+ u_char *pstart, *optend;
+ u_short cishort;
+ u_int32_t cilong;
+
+ if (plen < HEADERLEN)
+ return 0;
+ pstart = p;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < HEADERLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= sizeof(lcp_codenames) / sizeof(char *))
+ printer(arg, " %s", lcp_codenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+ printer(arg, " id=0x%x", id);
+ len -= HEADERLEN;
+ switch (code) {
+ case CONFREQ:
+ case CONFACK:
+ case CONFNAK:
+ case CONFREJ:
+ /* print option list */
+ while (len >= 2) {
+ GETCHAR(code, p);
+ GETCHAR(olen, p);
+ p -= 2;
+ if (olen < 2 || olen > len) {
+ break;
+ }
+ printer(arg, " <");
+ len -= olen;
+ optend = p + olen;
+ switch (code) {
+ case CI_MRU:
+ if (olen == CILEN_SHORT) {
+ p += 2;
+ GETSHORT(cishort, p);
+ printer(arg, "mru %d", cishort);
+ }
+ break;
+ case CI_ASYNCMAP:
+ if (olen == CILEN_LONG) {
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "asyncmap 0x%x", cilong);
+ }
+ break;
+ case CI_AUTHTYPE:
+ if (olen >= CILEN_SHORT) {
+ p += 2;
+ printer(arg, "auth ");
+ GETSHORT(cishort, p);
+ switch (cishort) {
+ case PPP_PAP:
+ printer(arg, "pap");
+ break;
+ case PPP_CHAP:
+ printer(arg, "chap");
+ break;
+ default:
+ printer(arg, "0x%x", cishort);
+ }
+ }
+ break;
+ case CI_QUALITY:
+ if (olen >= CILEN_SHORT) {
+ p += 2;
+ printer(arg, "quality ");
+ GETSHORT(cishort, p);
+ switch (cishort) {
+ case PPP_LQR:
+ printer(arg, "lqr");
+ break;
+ default:
+ printer(arg, "0x%x", cishort);
+ }
+ }
+ break;
+ case CI_CALLBACK:
+ if (olen >= CILEN_CHAR) {
+ p += 2;
+ printer(arg, "callback ");
+ GETSHORT(cishort, p);
+ switch (cishort) {
+ case CBCP_OPT:
+ printer(arg, "CBCP");
+ break;
+ default:
+ printer(arg, "0x%x", cishort);
+ }
+ }
+ break;
+ case CI_MAGICNUMBER:
+ if (olen == CILEN_LONG) {
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "magic 0x%x", cilong);
+ }
+ break;
+ case CI_PCOMPRESSION:
+ if (olen == CILEN_VOID) {
+ p += 2;
+ printer(arg, "pcomp");
+ }
+ break;
+ case CI_ACCOMPRESSION:
+ if (olen == CILEN_VOID) {
+ p += 2;
+ printer(arg, "accomp");
+ }
+ break;
+ }
+ while (p < optend) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+ printer(arg, ">");
+ }
+ break;
+
+ case TERMACK:
+ case TERMREQ:
+ if (len > 0 && *p >= ' ' && *p < 0x7f) {
+ printer(arg, " ");
+ print_string(p, len, printer, arg);
+ p += len;
+ len = 0;
+ }
+ break;
+
+ case ECHOREQ:
+ case ECHOREP:
+ case DISCREQ:
+ if (len >= 4) {
+ GETLONG(cilong, p);
+ printer(arg, " magic=0x%x", cilong);
+ p += 4;
+ len -= 4;
+ }
+ break;
+ }
+
+ /* print the rest of the bytes in the packet */
+ for (; len > 0; --len) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+
+ return p - pstart;
+}
+
+/*
+ * Time to shut down the link because there is nothing out there.
+ */
+
+static
+void LcpLinkFailure (f)
+ fsm *f;
+{
+ if (f->state == OPENED) {
+ syslog(LOG_INFO, "No response to %d echo-requests", lcp_echos_pending);
+ syslog(LOG_NOTICE, "Serial link appears to be disconnected.");
+ lcp_close(f->unit, "Peer not responding");
+ }
+}
+
+/*
+ * Timer expired for the LCP echo requests from this process.
+ */
+
+static void
+LcpEchoCheck (f)
+ fsm *f;
+{
+ LcpSendEchoRequest (f);
+ if (f->state != OPENED)
+ return;
+
+ /*
+ * Start the timer for the next interval.
+ */
+ assert (lcp_echo_timer_running==0);
+ TIMEOUT (LcpEchoTimeout, f, lcp_echo_interval);
+ lcp_echo_timer_running = 1;
+}
+
+/*
+ * LcpEchoTimeout - Timer expired on the LCP echo
+ */
+
+static void
+LcpEchoTimeout (arg)
+ void *arg;
+{
+ if (lcp_echo_timer_running != 0) {
+ lcp_echo_timer_running = 0;
+ LcpEchoCheck ((fsm *) arg);
+ }
+}
+
+/*
+ * LcpEchoReply - LCP has received a reply to the echo
+ */
+
+static void
+lcp_received_echo_reply (f, id, inp, len)
+ fsm *f;
+ int id; u_char *inp; int len;
+{
+ u_int32_t magic;
+
+ /* Check the magic number - don't count replies from ourselves. */
+ if (len < 4) {
+ syslog(LOG_DEBUG, "lcp: received short Echo-Reply, length %d", len);
+ return;
+ }
+ GETLONG(magic, inp);
+ if (lcp_gotoptions[f->unit].neg_magicnumber
+ && magic == lcp_gotoptions[f->unit].magicnumber) {
+ syslog(LOG_WARNING, "appear to have received our own echo-reply!");
+ return;
+ }
+
+ /* Reset the number of outstanding echo frames */
+ lcp_echos_pending = 0;
+}
+
+/*
+ * LcpSendEchoRequest - Send an echo request frame to the peer
+ */
+
+static void
+LcpSendEchoRequest (f)
+ fsm *f;
+{
+ u_int32_t lcp_magic;
+ u_char pkt[4], *pktp;
+
+ /*
+ * Detect the failure of the peer at this point.
+ */
+ if (lcp_echo_fails != 0) {
+ if (lcp_echos_pending >= lcp_echo_fails) {
+ LcpLinkFailure(f);
+ lcp_echos_pending = 0;
+ }
+ }
+
+ /*
+ * Make and send the echo request frame.
+ */
+ if (f->state == OPENED) {
+ lcp_magic = lcp_gotoptions[f->unit].magicnumber;
+ pktp = pkt;
+ PUTLONG(lcp_magic, pktp);
+ fsm_sdata(f, ECHOREQ, lcp_echo_number++ & 0xFF, pkt, pktp - pkt);
+ ++lcp_echos_pending;
+ }
+}
+
+/*
+ * lcp_echo_lowerup - Start the timer for the LCP frame
+ */
+
+static void
+lcp_echo_lowerup (unit)
+ int unit;
+{
+ fsm *f = &lcp_fsm[unit];
+
+ /* Clear the parameters for generating echo frames */
+ lcp_echos_pending = 0;
+ lcp_echo_number = 0;
+ lcp_echo_timer_running = 0;
+
+ /* If a timeout interval is specified then start the timer */
+ if (lcp_echo_interval != 0)
+ LcpEchoCheck (f);
+}
+
+/*
+ * lcp_echo_lowerdown - Stop the timer for the LCP frame
+ */
+
+static void
+lcp_echo_lowerdown (unit)
+ int unit;
+{
+ fsm *f = &lcp_fsm[unit];
+
+ if (lcp_echo_timer_running != 0) {
+ UNTIMEOUT (LcpEchoTimeout, f);
+ lcp_echo_timer_running = 0;
+ }
+}
diff --git a/usr.sbin/pppd/lcp.h b/usr.sbin/pppd/lcp.h
new file mode 100644
index 0000000..603a32f
--- /dev/null
+++ b/usr.sbin/pppd/lcp.h
@@ -0,0 +1,88 @@
+/*
+ * lcp.h - Link Control Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 Carnegie Mellon University. The name of the
+ * University 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Options.
+ */
+#define CI_MRU 1 /* Maximum Receive Unit */
+#define CI_ASYNCMAP 2 /* Async Control Character Map */
+#define CI_AUTHTYPE 3 /* Authentication Type */
+#define CI_QUALITY 4 /* Quality Protocol */
+#define CI_MAGICNUMBER 5 /* Magic Number */
+#define CI_PCOMPRESSION 7 /* Protocol Field Compression */
+#define CI_ACCOMPRESSION 8 /* Address/Control Field Compression */
+#define CI_CALLBACK 13 /* callback */
+
+/*
+ * LCP-specific packet types.
+ */
+#define PROTREJ 8 /* Protocol Reject */
+#define ECHOREQ 9 /* Echo Request */
+#define ECHOREP 10 /* Echo Reply */
+#define DISCREQ 11 /* Discard Request */
+#define CBCP_OPT 6 /* Use callback control protocol */
+
+/*
+ * The state of options is described by an lcp_options structure.
+ */
+typedef struct lcp_options {
+ int passive : 1; /* Don't die if we don't get a response */
+ int silent : 1; /* Wait for the other end to start first */
+ int restart : 1; /* Restart vs. exit after close */
+ int neg_mru : 1; /* Negotiate the MRU? */
+ int neg_asyncmap : 1; /* Negotiate the async map? */
+ int neg_upap : 1; /* Ask for UPAP authentication? */
+ int neg_chap : 1; /* Ask for CHAP authentication? */
+ int neg_magicnumber : 1; /* Ask for magic number? */
+ int neg_pcompression : 1; /* HDLC Protocol Field Compression? */
+ int neg_accompression : 1; /* HDLC Address/Control Field Compression? */
+ int neg_lqr : 1; /* Negotiate use of Link Quality Reports */
+ int neg_cbcp : 1; /* Negotiate use of CBCP */
+ u_short mru; /* Value of MRU */
+ u_char chap_mdtype; /* which MD type (hashing algorithm) */
+ u_int32_t asyncmap; /* Value of async map */
+ u_int32_t magicnumber;
+ int numloops; /* Number of loops during magic number neg. */
+ u_int32_t lqr_period; /* Reporting period for LQR 1/100ths second */
+} lcp_options;
+
+extern fsm lcp_fsm[];
+extern lcp_options lcp_wantoptions[];
+extern lcp_options lcp_gotoptions[];
+extern lcp_options lcp_allowoptions[];
+extern lcp_options lcp_hisoptions[];
+extern u_int32_t xmit_accm[][8];
+
+#define DEFMRU 1500 /* Try for this */
+#define MINMRU 128 /* No MRUs below this */
+#define MAXMRU 16384 /* Normally limit MRU to this */
+
+void lcp_open(int);
+void lcp_close(int, char *);
+void lcp_lowerup(int);
+void lcp_lowerdown(int);
+void lcp_sprotrej(int, u_char *, int); /* send protocol reject */
+
+extern struct protent lcp_protent;
+
+/* Default number of times we receive our magic number from the peer
+ before deciding the link is looped-back. */
+#define DEFLOOPBACKFAIL 10
diff --git a/usr.sbin/pppd/magic.c b/usr.sbin/pppd/magic.c
new file mode 100644
index 0000000..b4627b1
--- /dev/null
+++ b/usr.sbin/pppd/magic.c
@@ -0,0 +1,87 @@
+/*
+ * magic.c - PPP Magic Number routines.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 Carnegie Mellon University. The name of the
+ * University 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include "pppd.h"
+#include "magic.h"
+
+extern long mrand48(void);
+extern void srand48(long);
+
+/*
+ * magic_init - Initialize the magic number generator.
+ *
+ * Attempts to compute a random number seed which will not repeat.
+ * The current method uses the current hostid, current process ID
+ * and current time, currently.
+ */
+void
+magic_init()
+{
+ long seed;
+ struct timeval t;
+
+ gettimeofday(&t, NULL);
+ seed = get_host_seed() ^ t.tv_sec ^ t.tv_usec ^ getpid();
+ srand48(seed);
+}
+
+/*
+ * magic - Returns the next magic number.
+ */
+u_int32_t
+magic()
+{
+ return (u_int32_t) mrand48();
+}
+
+#ifdef NO_DRAND48
+/*
+ * Substitute procedures for those systems which don't have
+ * drand48 et al.
+ */
+
+double
+drand48()
+{
+ return (double)random() / (double)0x7fffffffL; /* 2**31-1 */
+}
+
+long
+mrand48()
+{
+ return random();
+}
+
+void
+srand48(seedval)
+long seedval;
+{
+ srandom((int)seedval);
+}
+
+#endif
diff --git a/usr.sbin/pppd/magic.h b/usr.sbin/pppd/magic.h
new file mode 100644
index 0000000..c574131
--- /dev/null
+++ b/usr.sbin/pppd/magic.h
@@ -0,0 +1,23 @@
+/*
+ * magic.h - PPP Magic Number definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 Carnegie Mellon University. The name of the
+ * University 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $FreeBSD$
+ */
+
+void magic_init(void); /* Initialize the magic number generator */
+u_int32_t magic(void); /* Returns the next magic number */
diff --git a/usr.sbin/pppd/main.c b/usr.sbin/pppd/main.c
new file mode 100644
index 0000000..7850726
--- /dev/null
+++ b/usr.sbin/pppd/main.c
@@ -0,0 +1,1723 @@
+/*
+ * main.c - Point-to-Point Protocol main module
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 Carnegie Mellon University. The name of the
+ * University 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <utmp.h>
+#include <pwd.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+
+#include "pppd.h"
+#include "magic.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ipcp.h"
+#ifdef INET6
+#include "ipv6cp.h"
+#endif
+#include "upap.h"
+#include "chap.h"
+#include "ccp.h"
+#include "pathnames.h"
+#include "patchlevel.h"
+
+#ifdef CBCP_SUPPORT
+#include "cbcp.h"
+#endif
+
+#if defined(SUNOS4)
+extern char *strerror();
+#endif
+
+#ifdef IPX_CHANGE
+#include "ipxcp.h"
+#endif /* IPX_CHANGE */
+#ifdef AT_CHANGE
+#include "atcp.h"
+#endif
+
+/* interface vars */
+char ifname[32]; /* Interface name */
+int ifunit; /* Interface unit number */
+
+char *progname; /* Name of this program */
+char hostname[MAXNAMELEN]; /* Our hostname */
+static char pidfilename[MAXPATHLEN]; /* name of pid file */
+static char iffilename[MAXPATHLEN]; /* name of if file */
+static char default_devnam[MAXPATHLEN]; /* name of default device */
+static pid_t pid; /* Our pid */
+static uid_t uid; /* Our real user-id */
+time_t etime,stime; /* End and Start time */
+int minutes; /* connection duration */
+static int conn_running; /* we have a [dis]connector running */
+
+int ttyfd = -1; /* Serial port file descriptor */
+mode_t tty_mode = -1; /* Original access permissions to tty */
+int baud_rate; /* Actual bits/second for serial device */
+int hungup; /* terminal has been hung up */
+int privileged; /* we're running as real uid root */
+int need_holdoff; /* need holdoff period before restarting */
+int detached; /* have detached from terminal */
+
+int phase; /* where the link is at */
+int kill_link;
+int open_ccp_flag;
+
+char **script_env; /* Env. variable values for scripts */
+int s_env_nalloc; /* # words avail at script_env */
+
+u_char outpacket_buf[PPP_MRU+PPP_HDRLEN]; /* buffer for outgoing packet */
+u_char inpacket_buf[PPP_MRU+PPP_HDRLEN]; /* buffer for incoming packet */
+
+static int n_children; /* # child processes still running */
+
+static int locked; /* lock() has succeeded */
+
+char *no_ppp_msg = "Sorry - this system lacks PPP kernel support\n";
+
+/* Prototypes for procedures local to this file. */
+
+static void create_pidfile(void);
+static void cleanup(void);
+static void close_tty(void);
+static void get_input(void);
+static void calltimeout(void);
+static struct timeval *timeleft(struct timeval *);
+static void kill_my_pg(int);
+static void hup(int);
+static void term(int);
+static void chld(int);
+static void toggle_debug(int);
+static void open_ccp(int);
+static void bad_signal(int);
+static void holdoff_end(void *);
+static int device_script(char *, int, int);
+static void reap_kids(void);
+static void pr_log(void *, char *, ...);
+
+extern char *ttyname(int);
+extern char *getlogin(void);
+int main(int, char *[]);
+
+#ifdef ultrix
+#undef O_NONBLOCK
+#define O_NONBLOCK O_NDELAY
+#endif
+
+#ifdef ULTRIX
+#define setlogmask(x)
+#endif
+
+/*
+ * PPP Data Link Layer "protocol" table.
+ * One entry per supported protocol.
+ * The last entry must be NULL.
+ */
+struct protent *protocols[] = {
+ &lcp_protent,
+ &pap_protent,
+ &chap_protent,
+#ifdef CBCP_SUPPORT
+ &cbcp_protent,
+#endif
+ &ipcp_protent,
+#ifdef INET6
+ &ipv6cp_protent,
+#endif
+ &ccp_protent,
+#ifdef IPX_CHANGE
+ &ipxcp_protent,
+#endif
+#ifdef AT_CHANGE
+ &atcp_protent,
+#endif
+ NULL
+};
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int i, n, fdflags;
+ struct sigaction sa;
+ FILE *iffile;
+ char *p;
+ struct passwd *pw;
+ struct timeval timo;
+ sigset_t mask;
+ struct protent *protp;
+ struct stat statbuf;
+ int connect_attempts = 0;
+ char numbuf[16];
+
+ phase = PHASE_INITIALIZE;
+ p = ttyname(0);
+ if (p)
+ strcpy(devnam, p);
+ strcpy(default_devnam, devnam);
+
+ script_env = NULL;
+
+ /* Initialize syslog facilities */
+#ifdef ULTRIX
+ openlog("pppd", LOG_PID);
+#else
+ openlog("pppd", LOG_PID | LOG_NDELAY, LOG_PPP);
+ setlogmask(LOG_UPTO(LOG_INFO));
+#endif
+
+ if (gethostname(hostname, MAXNAMELEN) < 0 ) {
+ option_error("Couldn't get hostname: %m");
+ die(1);
+ }
+ hostname[MAXNAMELEN-1] = 0;
+
+ uid = getuid();
+ privileged = uid == 0;
+ sprintf(numbuf, "%d", uid);
+ script_setenv("UID", numbuf);
+
+ /*
+ * Initialize to the standard option set, then parse, in order,
+ * the system options file, the user's options file,
+ * the tty's options file, and the command line arguments.
+ */
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ (*protp->init)(0);
+
+ progname = *argv;
+
+ if (!options_from_file(_PATH_SYSOPTIONS, !privileged, 0, 1)
+ || !options_from_user())
+ exit(1);
+ scan_args(argc-1, argv+1); /* look for tty name on command line */
+ if (!options_for_tty()
+ || !parse_args(argc-1, argv+1))
+ exit(1);
+
+ /*
+ * Check that we are running as root.
+ */
+ if (geteuid() != 0) {
+ option_error("must be root to run %s, since it is not setuid-root",
+ argv[0]);
+ die(1);
+ }
+
+ if (!ppp_available()) {
+ option_error(no_ppp_msg);
+ exit(1);
+ }
+
+ /*
+ * Check that the options given are valid and consistent.
+ */
+ sys_check_options();
+ auth_check_options();
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->check_options != NULL)
+ (*protp->check_options)();
+ if (demand && connector == 0) {
+ option_error("connect script required for demand-dialling\n");
+ exit(1);
+ }
+
+ script_setenv("DEVICE", devnam);
+ sprintf(numbuf, "%d", baud_rate);
+ script_setenv("SPEED", numbuf);
+
+ /*
+ * If the user has specified the default device name explicitly,
+ * pretend they hadn't.
+ */
+ if (!default_device && strcmp(devnam, default_devnam) == 0)
+ default_device = 1;
+ if (default_device)
+ nodetach = 1;
+
+ /*
+ * Initialize system-dependent stuff and magic number package.
+ */
+ sys_init();
+ magic_init();
+ if (debug)
+ setlogmask(LOG_UPTO(LOG_DEBUG));
+
+ /*
+ * Detach ourselves from the terminal, if required,
+ * and identify who is running us.
+ */
+ if (nodetach == 0)
+ detach();
+ pid = getpid();
+ p = getlogin();
+ stime = time((time_t *) NULL);
+ if (p == NULL) {
+ pw = getpwuid(uid);
+ if (pw != NULL && pw->pw_name != NULL)
+ p = pw->pw_name;
+ else
+ p = "(unknown)";
+ }
+ syslog(LOG_NOTICE, "pppd %s.%d%s started by %s, uid %d",
+ VERSION, PATCHLEVEL, IMPLEMENTATION, p, uid);
+
+ /*
+ * Compute mask of all interesting signals and install signal handlers
+ * for each. Only one signal handler may be active at a time. Therefore,
+ * all other signals should be masked when any handler is executing.
+ */
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGHUP);
+ sigaddset(&mask, SIGINT);
+ sigaddset(&mask, SIGTERM);
+ sigaddset(&mask, SIGCHLD);
+
+#define SIGNAL(s, handler) { \
+ sa.sa_handler = handler; \
+ if (sigaction(s, &sa, NULL) < 0) { \
+ syslog(LOG_ERR, "Couldn't establish signal handler (%d): %m", s); \
+ die(1); \
+ } \
+ }
+
+ sa.sa_mask = mask;
+ sa.sa_flags = 0;
+ SIGNAL(SIGHUP, hup); /* Hangup */
+ SIGNAL(SIGINT, term); /* Interrupt */
+ SIGNAL(SIGTERM, term); /* Terminate */
+ SIGNAL(SIGCHLD, chld);
+
+ SIGNAL(SIGUSR1, toggle_debug); /* Toggle debug flag */
+ SIGNAL(SIGUSR2, open_ccp); /* Reopen CCP */
+
+ /*
+ * Install a handler for other signals which would otherwise
+ * cause pppd to exit without cleaning up.
+ */
+ SIGNAL(SIGABRT, bad_signal);
+ SIGNAL(SIGALRM, bad_signal);
+ SIGNAL(SIGFPE, bad_signal);
+ SIGNAL(SIGILL, bad_signal);
+ SIGNAL(SIGPIPE, bad_signal);
+ SIGNAL(SIGQUIT, bad_signal);
+ SIGNAL(SIGSEGV, bad_signal);
+#ifdef SIGBUS
+ SIGNAL(SIGBUS, bad_signal);
+#endif
+#ifdef SIGEMT
+ SIGNAL(SIGEMT, bad_signal);
+#endif
+#ifdef SIGPOLL
+ SIGNAL(SIGPOLL, bad_signal);
+#endif
+#ifdef SIGPROF
+ SIGNAL(SIGPROF, bad_signal);
+#endif
+#ifdef SIGSYS
+ SIGNAL(SIGSYS, bad_signal);
+#endif
+#ifdef SIGTRAP
+ SIGNAL(SIGTRAP, bad_signal);
+#endif
+#ifdef SIGVTALRM
+ SIGNAL(SIGVTALRM, bad_signal);
+#endif
+#ifdef SIGXCPU
+ SIGNAL(SIGXCPU, bad_signal);
+#endif
+#ifdef SIGXFSZ
+ SIGNAL(SIGXFSZ, bad_signal);
+#endif
+
+ /*
+ * Apparently we can get a SIGPIPE when we call syslog, if
+ * syslogd has died and been restarted. Ignoring it seems
+ * be sufficient.
+ */
+ signal(SIGPIPE, SIG_IGN);
+
+ /*
+ * If we're doing dial-on-demand, set up the interface now.
+ */
+ if (demand) {
+ /*
+ * Open the loopback channel and set it up to be the ppp interface.
+ */
+ open_ppp_loopback();
+
+ syslog(LOG_INFO, "Using interface ppp%d", ifunit);
+ (void) sprintf(ifname, "ppp%d", ifunit);
+ script_setenv("IFNAME", ifname);
+
+ create_pidfile(); /* write pid to file */
+
+ /*
+ * Configure the interface and mark it up, etc.
+ */
+ demand_conf();
+ }
+
+ for (;;) {
+
+ need_holdoff = 1;
+
+ if (demand) {
+ /*
+ * Don't do anything until we see some activity.
+ */
+ phase = PHASE_DORMANT;
+ kill_link = 0;
+ demand_unblock();
+ for (;;) {
+ wait_loop_output(timeleft(&timo));
+ calltimeout();
+ if (kill_link) {
+ if (!persist)
+ die(0);
+ kill_link = 0;
+ }
+ if (get_loop_output())
+ break;
+ reap_kids();
+ }
+
+ /*
+ * Now we want to bring up the link.
+ */
+ demand_block();
+ syslog(LOG_INFO, "Starting link");
+ }
+
+ /*
+ * Lock the device if we've been asked to.
+ */
+ if (lockflag && !default_device) {
+ if (lock(devnam) < 0)
+ goto fail;
+ locked = 1;
+ }
+
+ /*
+ * Open the serial device and set it up to be the ppp interface.
+ * First we open it in non-blocking mode so we can set the
+ * various termios flags appropriately. If we aren't dialling
+ * out and we want to use the modem lines, we reopen it later
+ * in order to wait for the carrier detect signal from the modem.
+ */
+ while ((ttyfd = open(devnam, O_NONBLOCK | O_RDWR, 0)) < 0) {
+ if (errno != EINTR)
+ syslog(LOG_ERR, "Failed to open %s: %m", devnam);
+ if (!persist || errno != EINTR)
+ goto fail;
+ }
+ if ((fdflags = fcntl(ttyfd, F_GETFL)) == -1
+ || fcntl(ttyfd, F_SETFL, fdflags & ~O_NONBLOCK) < 0)
+ syslog(LOG_WARNING,
+ "Couldn't reset non-blocking mode on device: %m");
+
+ hungup = 0;
+ kill_link = 0;
+
+ /*
+ * Do the equivalent of `mesg n' to stop broadcast messages.
+ */
+ if (fstat(ttyfd, &statbuf) < 0
+ || fchmod(ttyfd, statbuf.st_mode & ~(S_IWGRP | S_IWOTH)) < 0) {
+ syslog(LOG_WARNING,
+ "Couldn't restrict write permissions to %s: %m", devnam);
+ } else
+ tty_mode = statbuf.st_mode;
+
+ /* run connection script */
+ if (connector && connector[0]) {
+ MAINDEBUG((LOG_INFO, "Connecting with <%s>", connector));
+
+ /*
+ * Set line speed, flow control, etc.
+ * On most systems we set CLOCAL for now so that we can talk
+ * to the modem before carrier comes up. But this has the
+ * side effect that we might miss it if CD drops before we
+ * get to clear CLOCAL below. On systems where we can talk
+ * successfully to the modem with CLOCAL clear and CD down,
+ * we can clear CLOCAL at this point.
+ */
+ set_up_tty(ttyfd, 1);
+
+ /* drop dtr to hang up in case modem is off hook */
+ if (!default_device && modem) {
+ setdtr(ttyfd, FALSE);
+ sleep(1);
+ setdtr(ttyfd, TRUE);
+ }
+
+ if (device_script(connector, ttyfd, ttyfd) < 0) {
+ syslog(LOG_ERR, "Connect script failed");
+ setdtr(ttyfd, FALSE);
+ connect_attempts++;
+ goto fail;
+ }
+
+
+ syslog(LOG_INFO, "Serial connection established.");
+ sleep(1); /* give it time to set up its terminal */
+ }
+
+ connect_attempts = 0; /* we made it through ok */
+
+ /* set line speed, flow control, etc.; clear CLOCAL if modem option */
+ set_up_tty(ttyfd, 0);
+
+ /* reopen tty if necessary to wait for carrier */
+ if (connector == NULL && modem) {
+ while ((i = open(devnam, O_RDWR)) < 0) {
+ if (errno != EINTR)
+ syslog(LOG_ERR, "Failed to reopen %s: %m", devnam);
+ if (!persist || errno != EINTR ||
+ hungup || kill_link)
+ goto fail;
+ }
+ close(i);
+ }
+
+ /* run welcome script, if any */
+ if (welcomer && welcomer[0]) {
+ if (device_script(welcomer, ttyfd, ttyfd) < 0)
+ syslog(LOG_WARNING, "Welcome script failed");
+ }
+
+ /* set up the serial device as a ppp interface */
+ establish_ppp(ttyfd);
+
+ if (!demand) {
+
+ syslog(LOG_INFO, "Using interface ppp%d", ifunit);
+ (void) sprintf(ifname, "ppp%d", ifunit);
+
+ create_pidfile(); /* write pid to file */
+
+ /* write interface unit number to file */
+ for (n = strlen(devnam); n > 0 ; n--)
+ if (devnam[n] == '/') {
+ n++;
+ break;
+ }
+ (void) sprintf(iffilename, "%s%s.if", _PATH_VARRUN, &devnam[n]);
+ if ((iffile = fopen(iffilename, "w")) != NULL) {
+ fprintf(iffile, "ppp%d\n", ifunit);
+ (void) fclose(iffile);
+ } else {
+ syslog(LOG_ERR, "Failed to create if file %s: %m", iffilename);
+ iffilename[0] = 0;
+ }
+
+ script_setenv("IFNAME", ifname);
+ }
+
+ /*
+ * Start opening the connection and wait for
+ * incoming events (reply, timeout, etc.).
+ */
+ syslog(LOG_NOTICE, "Connect: %s <--> %s", ifname, devnam);
+ stime = time((time_t *) NULL);
+ lcp_lowerup(0);
+ lcp_open(0); /* Start protocol */
+ for (phase = PHASE_ESTABLISH; phase != PHASE_DEAD; ) {
+ wait_input(timeleft(&timo));
+ calltimeout();
+ get_input();
+ if (kill_link) {
+ lcp_close(0, "User request");
+ kill_link = 0;
+ }
+ if (open_ccp_flag) {
+ if (phase == PHASE_NETWORK) {
+ ccp_fsm[0].flags = OPT_RESTART; /* clears OPT_SILENT */
+ (*ccp_protent.open)(0);
+ }
+ open_ccp_flag = 0;
+ }
+ reap_kids(); /* Don't leave dead kids lying around */
+ }
+
+ /*
+ * If we may want to bring the link up again, transfer
+ * the ppp unit back to the loopback. Set the
+ * real serial device back to its normal mode of operation.
+ */
+ clean_check();
+ if (demand)
+ restore_loop();
+ disestablish_ppp(ttyfd);
+
+ /*
+ * Run disconnector script, if requested.
+ * XXX we may not be able to do this if the line has hung up!
+ */
+ if (disconnector && !hungup) {
+ set_up_tty(ttyfd, 1);
+ if (device_script(disconnector, ttyfd, ttyfd) < 0) {
+ syslog(LOG_WARNING, "disconnect script failed");
+ } else {
+ syslog(LOG_INFO, "Serial link disconnected.");
+ }
+ }
+
+ fail:
+ if (ttyfd >= 0)
+ close_tty();
+ if (locked) {
+ unlock();
+ locked = 0;
+ }
+
+ if (!demand) {
+ if (pidfilename[0] != 0
+ && unlink(pidfilename) < 0 && errno != ENOENT)
+ syslog(LOG_WARNING, "unable to delete pid file: %m");
+ pidfilename[0] = 0;
+
+ if (iffile)
+ if (unlink(iffilename) < 0 && errno != ENOENT)
+ syslog(LOG_WARNING, "unable to delete if file: %m");
+ iffilename[0] = 0;
+ }
+
+ /* limit to retries? */
+ if (max_con_attempts)
+ if (connect_attempts >= max_con_attempts)
+ break;
+
+ if (!persist)
+ die(1);
+
+ if (demand)
+ demand_discard();
+ if (holdoff > 0 && need_holdoff) {
+ phase = PHASE_HOLDOFF;
+ TIMEOUT(holdoff_end, NULL, holdoff);
+ do {
+ wait_time(timeleft(&timo));
+ calltimeout();
+ if (kill_link) {
+ if (!persist)
+ die(0);
+ kill_link = 0;
+ phase = PHASE_DORMANT; /* allow signal to end holdoff */
+ }
+ reap_kids();
+ } while (phase == PHASE_HOLDOFF);
+ }
+ }
+
+ die(0);
+ return 0;
+}
+
+/*
+ * detach - detach us from the controlling terminal.
+ */
+void
+detach()
+{
+ if (detached)
+ return;
+ if (daemon(0, 0) < 0) {
+ perror("Couldn't detach from controlling terminal");
+ die(1);
+ }
+ detached = 1;
+ pid = getpid();
+ /* update pid file if it has been written already */
+ if (pidfilename[0])
+ create_pidfile();
+}
+
+/*
+ * Create a file containing our process ID.
+ */
+static void
+create_pidfile()
+{
+ FILE *pidfile;
+
+ (void) sprintf(pidfilename, "%s%s.pid", _PATH_VARRUN, ifname);
+ if ((pidfile = fopen(pidfilename, "w")) != NULL) {
+ fprintf(pidfile, "%d\n", pid);
+ (void) fclose(pidfile);
+ } else {
+ syslog(LOG_ERR, "Failed to create pid file %s: %m", pidfilename);
+ pidfilename[0] = 0;
+ }
+}
+
+/*
+ * holdoff_end - called via a timeout when the holdoff period ends.
+ */
+static void
+holdoff_end(arg)
+ void *arg;
+{
+ phase = PHASE_DORMANT;
+}
+
+/*
+ * get_input - called when incoming data is available.
+ */
+static void
+get_input()
+{
+ int len, i;
+ u_char *p;
+ u_short protocol;
+ struct protent *protp;
+
+ p = inpacket_buf; /* point to beginning of packet buffer */
+
+ len = read_packet(inpacket_buf);
+ if (len < 0)
+ return;
+
+ if (len == 0) {
+ etime = time((time_t *) NULL);
+ minutes = (etime-stime)/60;
+ syslog(LOG_NOTICE, "Modem hangup, connected for %d minutes", (minutes >1) ? minutes : 1);
+ hungup = 1;
+ lcp_lowerdown(0); /* serial link is no longer available */
+ link_terminated(0);
+ return;
+ }
+
+ if (debug /*&& (debugflags & DBG_INPACKET)*/)
+ log_packet(p, len, "rcvd ", LOG_DEBUG);
+
+ if (len < PPP_HDRLEN) {
+ MAINDEBUG((LOG_INFO, "io(): Received short packet."));
+ return;
+ }
+
+ p += 2; /* Skip address and control */
+ GETSHORT(protocol, p);
+ len -= PPP_HDRLEN;
+
+ /*
+ * Toss all non-LCP packets unless LCP is OPEN.
+ */
+ if (protocol != PPP_LCP && lcp_fsm[0].state != OPENED) {
+ MAINDEBUG((LOG_INFO,
+ "get_input: Received non-LCP packet when LCP not open."));
+ return;
+ }
+
+ /*
+ * Until we get past the authentication phase, toss all packets
+ * except LCP, LQR and authentication packets.
+ */
+ if (phase <= PHASE_AUTHENTICATE
+ && !(protocol == PPP_LCP || protocol == PPP_LQR
+ || protocol == PPP_PAP || protocol == PPP_CHAP)) {
+ MAINDEBUG((LOG_INFO, "get_input: discarding proto 0x%x in phase %d",
+ protocol, phase));
+ return;
+ }
+
+ /*
+ * Upcall the proper protocol input routine.
+ */
+ for (i = 0; (protp = protocols[i]) != NULL; ++i) {
+ if (protp->protocol == protocol && protp->enabled_flag) {
+ (*protp->input)(0, p, len);
+ return;
+ }
+ if (protocol == (protp->protocol & ~0x8000) && protp->enabled_flag
+ && protp->datainput != NULL) {
+ (*protp->datainput)(0, p, len);
+ return;
+ }
+ }
+
+ if (debug)
+ syslog(LOG_WARNING, "Unsupported protocol (0x%x) received", protocol);
+ lcp_sprotrej(0, p - PPP_HDRLEN, len + PPP_HDRLEN);
+}
+
+
+/*
+ * quit - Clean up state and exit (with an error indication).
+ */
+void
+quit()
+{
+ die(1);
+}
+
+/*
+ * die - like quit, except we can specify an exit status.
+ */
+void
+die(status)
+ int status;
+{
+ cleanup();
+ syslog(LOG_INFO, "Exit.");
+ exit(status);
+}
+
+/*
+ * cleanup - restore anything which needs to be restored before we exit
+ */
+/* ARGSUSED */
+static void
+cleanup()
+{
+ sys_cleanup();
+
+ if (ttyfd >= 0)
+ close_tty();
+
+ if (pidfilename[0] != 0 && unlink(pidfilename) < 0 && errno != ENOENT)
+ syslog(LOG_WARNING, "unable to delete pid file: %m");
+ pidfilename[0] = 0;
+
+ if (locked)
+ unlock();
+}
+
+/*
+ * close_tty - restore the terminal device and close it.
+ */
+static void
+close_tty()
+{
+ disestablish_ppp(ttyfd);
+
+ /* drop dtr to hang up */
+ if (modem) {
+ setdtr(ttyfd, FALSE);
+ /*
+ * This sleep is in case the serial port has CLOCAL set by default,
+ * and consequently will reassert DTR when we close the device.
+ */
+ sleep(1);
+ }
+
+ restore_tty(ttyfd);
+
+ if (tty_mode != (mode_t) -1)
+ fchmod(ttyfd, tty_mode);
+
+ close(ttyfd);
+ ttyfd = -1;
+}
+
+
+struct callout {
+ struct timeval c_time; /* time at which to call routine */
+ void *c_arg; /* argument to routine */
+ void (*c_func)(void *); /* routine */
+ struct callout *c_next;
+};
+
+static struct callout *callout = NULL; /* Callout list */
+static struct timeval timenow; /* Current time */
+
+/*
+ * timeout - Schedule a timeout.
+ *
+ * Note that this timeout takes the number of seconds, NOT hz (as in
+ * the kernel).
+ */
+void
+timeout(func, arg, time)
+ void (*func)(void *);
+ void *arg;
+ int time;
+{
+ struct callout *newp, *p, **pp;
+
+ MAINDEBUG((LOG_DEBUG, "Timeout %lx:%lx in %d seconds.",
+ (long) func, (long) arg, time));
+
+ /*
+ * Allocate timeout.
+ */
+ if ((newp = (struct callout *) malloc(sizeof(struct callout))) == NULL) {
+ syslog(LOG_ERR, "Out of memory in timeout()!");
+ die(1);
+ }
+ newp->c_arg = arg;
+ newp->c_func = func;
+ gettimeofday(&timenow, NULL);
+ newp->c_time.tv_sec = timenow.tv_sec + time;
+ newp->c_time.tv_usec = timenow.tv_usec;
+
+ /*
+ * Find correct place and link it in.
+ */
+ for (pp = &callout; (p = *pp); pp = &p->c_next)
+ if (newp->c_time.tv_sec < p->c_time.tv_sec
+ || (newp->c_time.tv_sec == p->c_time.tv_sec
+ && newp->c_time.tv_usec < p->c_time.tv_sec))
+ break;
+ newp->c_next = p;
+ *pp = newp;
+}
+
+
+/*
+ * untimeout - Unschedule a timeout.
+ */
+void
+untimeout(func, arg)
+ void (*func)(void *);
+ void *arg;
+{
+ struct callout **copp, *freep;
+
+ MAINDEBUG((LOG_DEBUG, "Untimeout %lx:%lx.", (long) func, (long) arg));
+
+ /*
+ * Find first matching timeout and remove it from the list.
+ */
+ for (copp = &callout; (freep = *copp); copp = &freep->c_next)
+ if (freep->c_func == func && freep->c_arg == arg) {
+ *copp = freep->c_next;
+ (void) free((char *) freep);
+ break;
+ }
+}
+
+
+/*
+ * calltimeout - Call any timeout routines which are now due.
+ */
+static void
+calltimeout()
+{
+ struct callout *p;
+
+ while (callout != NULL) {
+ p = callout;
+
+ if (gettimeofday(&timenow, NULL) < 0) {
+ syslog(LOG_ERR, "Failed to get time of day: %m");
+ die(1);
+ }
+ if (!(p->c_time.tv_sec < timenow.tv_sec
+ || (p->c_time.tv_sec == timenow.tv_sec
+ && p->c_time.tv_usec <= timenow.tv_usec)))
+ break; /* no, it's not time yet */
+
+ callout = p->c_next;
+ (*p->c_func)(p->c_arg);
+
+ free((char *) p);
+ }
+}
+
+
+/*
+ * timeleft - return the length of time until the next timeout is due.
+ */
+static struct timeval *
+timeleft(tvp)
+ struct timeval *tvp;
+{
+ if (callout == NULL)
+ return NULL;
+
+ gettimeofday(&timenow, NULL);
+ tvp->tv_sec = callout->c_time.tv_sec - timenow.tv_sec;
+ tvp->tv_usec = callout->c_time.tv_usec - timenow.tv_usec;
+ if (tvp->tv_usec < 0) {
+ tvp->tv_usec += 1000000;
+ tvp->tv_sec -= 1;
+ }
+ if (tvp->tv_sec < 0)
+ tvp->tv_sec = tvp->tv_usec = 0;
+
+ return tvp;
+}
+
+
+/*
+ * kill_my_pg - send a signal to our process group, and ignore it ourselves.
+ */
+static void
+kill_my_pg(sig)
+ int sig;
+{
+ struct sigaction act, oldact;
+
+ act.sa_handler = SIG_IGN;
+ act.sa_flags = 0;
+ kill(0, sig);
+ sigaction(sig, &act, &oldact);
+ sigaction(sig, &oldact, NULL);
+}
+
+
+/*
+ * hup - Catch SIGHUP signal.
+ *
+ * Indicates that the physical layer has been disconnected.
+ * We don't rely on this indication; if the user has sent this
+ * signal, we just take the link down.
+ */
+static void
+hup(sig)
+ int sig;
+{
+ syslog(LOG_INFO, "Hangup (SIGHUP)");
+ kill_link = 1;
+ if (conn_running)
+ /* Send the signal to the [dis]connector process(es) also */
+ kill_my_pg(sig);
+}
+
+
+/*
+ * term - Catch SIGTERM signal and SIGINT signal (^C/del).
+ *
+ * Indicates that we should initiate a graceful disconnect and exit.
+ */
+/*ARGSUSED*/
+static void
+term(sig)
+ int sig;
+{
+ syslog(LOG_INFO, "Terminating on signal %d.", sig);
+ persist = 0; /* don't try to restart */
+ kill_link = 1;
+ if (conn_running)
+ /* Send the signal to the [dis]connector process(es) also */
+ kill_my_pg(sig);
+}
+
+
+/*
+ * chld - Catch SIGCHLD signal.
+ * Calls reap_kids to get status for any dead kids.
+ */
+static void
+chld(sig)
+ int sig;
+{
+ reap_kids();
+}
+
+
+/*
+ * toggle_debug - Catch SIGUSR1 signal.
+ *
+ * Toggle debug flag.
+ */
+/*ARGSUSED*/
+static void
+toggle_debug(sig)
+ int sig;
+{
+ debug = !debug;
+ if (debug) {
+ setlogmask(LOG_UPTO(LOG_DEBUG));
+ } else {
+ setlogmask(LOG_UPTO(LOG_WARNING));
+ }
+}
+
+
+/*
+ * open_ccp - Catch SIGUSR2 signal.
+ *
+ * Try to (re)negotiate compression.
+ */
+/*ARGSUSED*/
+static void
+open_ccp(sig)
+ int sig;
+{
+ open_ccp_flag = 1;
+}
+
+
+/*
+ * bad_signal - We've caught a fatal signal. Clean up state and exit.
+ */
+static void
+bad_signal(sig)
+ int sig;
+{
+ static int crashed = 0;
+
+ if (crashed)
+ _exit(127);
+ crashed = 1;
+ syslog(LOG_ERR, "Fatal signal %d", sig);
+ if (conn_running)
+ kill_my_pg(SIGTERM);
+ die(1);
+}
+
+
+/*
+ * device_script - run a program to connect or disconnect the
+ * serial device.
+ */
+static int
+device_script(program, in, out)
+ char *program;
+ int in, out;
+{
+ int pid;
+ int status;
+ int errfd;
+
+ conn_running = 1;
+ pid = fork();
+
+ if (pid < 0) {
+ conn_running = 0;
+ syslog(LOG_ERR, "Failed to create child process: %m");
+ die(1);
+ }
+
+ if (pid == 0) {
+ sys_close();
+ closelog();
+ if (in == out) {
+ if (in != 0) {
+ dup2(in, 0);
+ close(in);
+ }
+ dup2(0, 1);
+ } else {
+ if (out == 0)
+ out = dup(out);
+ if (in != 0) {
+ dup2(in, 0);
+ close(in);
+ }
+ if (out != 1) {
+ dup2(out, 1);
+ close(out);
+ }
+ }
+ if (nodetach == 0) {
+ close(2);
+ errfd = open(_PATH_CONNERRS, O_WRONLY | O_APPEND | O_CREAT, 0600);
+ if (errfd >= 0 && errfd != 2) {
+ dup2(errfd, 2);
+ close(errfd);
+ }
+ }
+ setuid(getuid());
+ setgid(getgid());
+ execl("/bin/sh", "sh", "-c", program, (char *)0);
+ syslog(LOG_ERR, "could not exec /bin/sh: %m");
+ _exit(99);
+ /* NOTREACHED */
+ }
+
+ while (waitpid(pid, &status, 0) < 0) {
+ if (errno == EINTR)
+ continue;
+ syslog(LOG_ERR, "error waiting for (dis)connection process: %m");
+ die(1);
+ }
+ conn_running = 0;
+
+ return (status == 0 ? 0 : -1);
+}
+
+
+/*
+ * run-program - execute a program with given arguments,
+ * but don't wait for it.
+ * If the program can't be executed, logs an error unless
+ * must_exist is 0 and the program file doesn't exist.
+ */
+int
+run_program(prog, args, must_exist)
+ char *prog;
+ char **args;
+ int must_exist;
+{
+ int pid;
+
+ pid = fork();
+ if (pid == -1) {
+ syslog(LOG_ERR, "Failed to create child process for %s: %m", prog);
+ return -1;
+ }
+ if (pid == 0) {
+ int new_fd;
+
+ /* Leave the current location */
+ (void) setsid(); /* No controlling tty. */
+ (void) umask (S_IRWXG|S_IRWXO);
+ (void) chdir ("/"); /* no current directory. */
+ setuid(geteuid());
+ setgid(getegid());
+
+ /* Ensure that nothing of our device environment is inherited. */
+ sys_close();
+ closelog();
+ close (0);
+ close (1);
+ close (2);
+ close (ttyfd); /* tty interface to the ppp device */
+
+ /* Don't pass handles to the PPP device, even by accident. */
+ new_fd = open (_PATH_DEVNULL, O_RDWR);
+ if (new_fd >= 0) {
+ if (new_fd != 0) {
+ dup2 (new_fd, 0); /* stdin <- /dev/null */
+ close (new_fd);
+ }
+ dup2 (0, 1); /* stdout -> /dev/null */
+ dup2 (0, 2); /* stderr -> /dev/null */
+ }
+
+#ifdef BSD
+ /* Force the priority back to zero if pppd is running higher. */
+ if (setpriority (PRIO_PROCESS, 0, 0) < 0)
+ syslog (LOG_WARNING, "can't reset priority to 0: %m");
+#endif
+
+ /* SysV recommends a second fork at this point. */
+
+ /* run the program; give it a null environment */
+ execve(prog, args, script_env);
+ if (must_exist || errno != ENOENT)
+ syslog(LOG_WARNING, "Can't execute %s: %m", prog);
+ _exit(-1);
+ }
+ MAINDEBUG((LOG_DEBUG, "Script %s started; pid = %d", prog, pid));
+ ++n_children;
+ return 0;
+}
+
+
+/*
+ * reap_kids - get status from any dead child processes,
+ * and log a message for abnormal terminations.
+ */
+static void
+reap_kids()
+{
+ int pid, status;
+
+ if (n_children == 0)
+ return;
+ if ((pid = waitpid(-1, &status, WNOHANG)) == -1) {
+ if (errno != ECHILD)
+ syslog(LOG_ERR, "Error waiting for child process: %m");
+ return;
+ }
+ if (pid > 0) {
+ --n_children;
+ if (WIFSIGNALED(status)) {
+ syslog(LOG_WARNING, "Child process %d terminated with signal %d",
+ pid, WTERMSIG(status));
+ }
+ }
+}
+
+
+/*
+ * log_packet - format a packet and log it.
+ */
+
+char line[256]; /* line to be logged accumulated here */
+char *linep;
+
+void
+log_packet(p, len, prefix, level)
+ u_char *p;
+ int len;
+ char *prefix;
+ int level;
+{
+ strcpy(line, prefix);
+ linep = line + strlen(line);
+ format_packet(p, len, pr_log, NULL);
+ if (linep != line)
+ syslog(level, "%s", line);
+}
+
+/*
+ * format_packet - make a readable representation of a packet,
+ * calling `printer(arg, format, ...)' to output it.
+ */
+void
+format_packet(p, len, printer, arg)
+ u_char *p;
+ int len;
+ void (*printer)(void *, char *, ...);
+ void *arg;
+{
+ int i, n;
+ u_short proto;
+ u_char x;
+ struct protent *protp;
+
+ if (len >= PPP_HDRLEN && p[0] == PPP_ALLSTATIONS && p[1] == PPP_UI) {
+ p += 2;
+ GETSHORT(proto, p);
+ len -= PPP_HDRLEN;
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (proto == protp->protocol)
+ break;
+ if (protp != NULL) {
+ printer(arg, "[%s", protp->name);
+ n = (*protp->printpkt)(p, len, printer, arg);
+ printer(arg, "]");
+ p += n;
+ len -= n;
+ } else {
+ printer(arg, "[proto=0x%x]", proto);
+ }
+ }
+
+ for (; len > 0; --len) {
+ GETCHAR(x, p);
+ printer(arg, " %.2x", x);
+ }
+}
+
+static void
+pr_log __V((void *arg, char *fmt, ...))
+{
+ int n;
+ va_list pvar;
+ char buf[256];
+
+#if __STDC__
+ va_start(pvar, fmt);
+#else
+ void *arg;
+ char *fmt;
+ va_start(pvar);
+ arg = va_arg(pvar, void *);
+ fmt = va_arg(pvar, char *);
+#endif
+
+ n = vfmtmsg(buf, sizeof(buf), fmt, pvar);
+ va_end(pvar);
+
+ if (linep + n + 1 > line + sizeof(line)) {
+ syslog(LOG_DEBUG, "%s", line);
+ linep = line;
+ }
+ strcpy(linep, buf);
+ linep += n;
+}
+
+/*
+ * print_string - print a readable representation of a string using
+ * printer.
+ */
+void
+print_string(p, len, printer, arg)
+ char *p;
+ int len;
+ void (*printer)(void *, char *, ...);
+ void *arg;
+{
+ int c;
+
+ printer(arg, "\"");
+ for (; len > 0; --len) {
+ c = *p++;
+ if (' ' <= c && c <= '~') {
+ if (c == '\\' || c == '"')
+ printer(arg, "\\");
+ printer(arg, "%c", c);
+ } else {
+ switch (c) {
+ case '\n':
+ printer(arg, "\\n");
+ break;
+ case '\r':
+ printer(arg, "\\r");
+ break;
+ case '\t':
+ printer(arg, "\\t");
+ break;
+ default:
+ printer(arg, "\\%.3o", c);
+ }
+ }
+ }
+ printer(arg, "\"");
+}
+
+/*
+ * novm - log an error message saying we ran out of memory, and die.
+ */
+void
+novm(msg)
+ char *msg;
+{
+ syslog(LOG_ERR, "Virtual memory exhausted allocating %s\n", msg);
+ die(1);
+}
+
+/*
+ * fmtmsg - format a message into a buffer. Like sprintf except we
+ * also specify the length of the output buffer, and we handle
+ * %r (recursive format), %m (error message) and %I (IP address) formats.
+ * Doesn't do floating-point formats.
+ * Returns the number of chars put into buf.
+ */
+int
+fmtmsg __V((char *buf, int buflen, char *fmt, ...))
+{
+ va_list args;
+ int n;
+
+#if __STDC__
+ va_start(args, fmt);
+#else
+ char *buf;
+ int buflen;
+ char *fmt;
+ va_start(args);
+ buf = va_arg(args, char *);
+ buflen = va_arg(args, int);
+ fmt = va_arg(args, char *);
+#endif
+ n = vfmtmsg(buf, buflen, fmt, args);
+ va_end(args);
+ return n;
+}
+
+/*
+ * vfmtmsg - like fmtmsg, takes a va_list instead of a list of args.
+ */
+#define OUTCHAR(c) (buflen > 0? (--buflen, *buf++ = (c)): 0)
+
+int
+vfmtmsg(buf, buflen, fmt, args)
+ char *buf;
+ int buflen;
+ char *fmt;
+ va_list args;
+{
+ int c, i, n;
+ int width, prec, fillch;
+ int base, len, neg, quoted;
+ unsigned long val = 0;
+ char *str, *f, *buf0;
+ unsigned char *p;
+ char num[32];
+ time_t t;
+ static char hexchars[] = "0123456789abcdef";
+
+ buf0 = buf;
+ --buflen;
+ while (buflen > 0) {
+ for (f = fmt; *f != '%' && *f != 0; ++f)
+ ;
+ if (f > fmt) {
+ len = f - fmt;
+ if (len > buflen)
+ len = buflen;
+ memcpy(buf, fmt, len);
+ buf += len;
+ buflen -= len;
+ fmt = f;
+ }
+ if (*fmt == 0)
+ break;
+ c = *++fmt;
+ width = prec = 0;
+ fillch = ' ';
+ if (c == '0') {
+ fillch = '0';
+ c = *++fmt;
+ }
+ if (c == '*') {
+ width = va_arg(args, int);
+ c = *++fmt;
+ } else {
+ while (isdigit(c)) {
+ width = width * 10 + c - '0';
+ c = *++fmt;
+ }
+ }
+ if (c == '.') {
+ c = *++fmt;
+ if (c == '*') {
+ prec = va_arg(args, int);
+ c = *++fmt;
+ } else {
+ while (isdigit(c)) {
+ prec = prec * 10 + c - '0';
+ c = *++fmt;
+ }
+ }
+ }
+ str = 0;
+ base = 0;
+ neg = 0;
+ ++fmt;
+ switch (c) {
+ case 'd':
+ i = va_arg(args, int);
+ if (i < 0) {
+ neg = 1;
+ val = -i;
+ } else
+ val = i;
+ base = 10;
+ break;
+ case 'o':
+ val = va_arg(args, unsigned int);
+ base = 8;
+ break;
+ case 'x':
+ val = va_arg(args, unsigned int);
+ base = 16;
+ break;
+ case 'p':
+ val = (unsigned long) va_arg(args, void *);
+ base = 16;
+ neg = 2;
+ break;
+ case 's':
+ str = va_arg(args, char *);
+ break;
+ case 'c':
+ num[0] = va_arg(args, int);
+ num[1] = 0;
+ str = num;
+ break;
+ case 'm':
+ str = strerror(errno);
+ break;
+ case 'I':
+ str = ip_ntoa(va_arg(args, u_int32_t));
+ break;
+ case 'r':
+ f = va_arg(args, char *);
+#if !defined(__powerpc__) && !defined(__amd64__)
+ n = vfmtmsg(buf, buflen + 1, f, va_arg(args, va_list));
+#else
+ /* On the powerpc, a va_list is an array of 1 structure */
+ n = vfmtmsg(buf, buflen + 1, f, va_arg(args, void *));
+#endif
+ buf += n;
+ buflen -= n;
+ continue;
+ case 't':
+ time(&t);
+ str = ctime(&t);
+ str += 4; /* chop off the day name */
+ str[15] = 0; /* chop off year and newline */
+ break;
+ case 'v': /* "visible" string */
+ case 'q': /* quoted string */
+ quoted = c == 'q';
+ p = va_arg(args, unsigned char *);
+ if (fillch == '0' && prec > 0) {
+ n = prec;
+ } else {
+ n = strlen((char *)p);
+ if (prec > 0 && prec < n)
+ n = prec;
+ }
+ while (n > 0 && buflen > 0) {
+ c = *p++;
+ --n;
+ if (!quoted && c >= 0x80) {
+ OUTCHAR('M');
+ OUTCHAR('-');
+ c -= 0x80;
+ }
+ if (quoted && (c == '"' || c == '\\'))
+ OUTCHAR('\\');
+ if (c < 0x20 || (0x7f <= c && c < 0xa0)) {
+ if (quoted) {
+ OUTCHAR('\\');
+ switch (c) {
+ case '\t': OUTCHAR('t'); break;
+ case '\n': OUTCHAR('n'); break;
+ case '\b': OUTCHAR('b'); break;
+ case '\f': OUTCHAR('f'); break;
+ default:
+ OUTCHAR('x');
+ OUTCHAR(hexchars[c >> 4]);
+ OUTCHAR(hexchars[c & 0xf]);
+ }
+ } else {
+ if (c == '\t')
+ OUTCHAR(c);
+ else {
+ OUTCHAR('^');
+ OUTCHAR(c ^ 0x40);
+ }
+ }
+ } else
+ OUTCHAR(c);
+ }
+ continue;
+ default:
+ *buf++ = '%';
+ if (c != '%')
+ --fmt; /* so %z outputs %z etc. */
+ --buflen;
+ continue;
+ }
+ if (base != 0) {
+ str = num + sizeof(num);
+ *--str = 0;
+ while (str > num + neg) {
+ *--str = hexchars[val % base];
+ val = val / base;
+ if (--prec <= 0 && val == 0)
+ break;
+ }
+ switch (neg) {
+ case 1:
+ *--str = '-';
+ break;
+ case 2:
+ *--str = 'x';
+ *--str = '0';
+ break;
+ }
+ len = num + sizeof(num) - 1 - str;
+ } else {
+ len = strlen(str);
+ if (prec > 0 && len > prec)
+ len = prec;
+ }
+ if (width > 0) {
+ if (width > buflen)
+ width = buflen;
+ if ((n = width - len) > 0) {
+ buflen -= n;
+ for (; n > 0; --n)
+ *buf++ = fillch;
+ }
+ }
+ if (len > buflen)
+ len = buflen;
+ memcpy(buf, str, len);
+ buf += len;
+ buflen -= len;
+ }
+ *buf = 0;
+ return buf - buf0;
+}
+
+/*
+ * script_setenv - set an environment variable value to be used
+ * for scripts that we run (e.g. ip-up, auth-up, etc.)
+ */
+void
+script_setenv(var, value)
+ char *var, *value;
+{
+ int vl = strlen(var);
+ int i;
+ char *p, *newstring;
+
+ newstring = (char *) malloc(vl + strlen(value) + 2);
+ if (newstring == 0)
+ return;
+ strcpy(newstring, var);
+ newstring[vl] = '=';
+ strcpy(newstring+vl+1, value);
+
+ /* check if this variable is already set */
+ if (script_env != 0) {
+ for (i = 0; (p = script_env[i]) != 0; ++i) {
+ if (strncmp(p, var, vl) == 0 && p[vl] == '=') {
+ free(p);
+ script_env[i] = newstring;
+ return;
+ }
+ }
+ } else {
+ i = 0;
+ script_env = (char **) malloc(16 * sizeof(char *));
+ if (script_env == 0)
+ return;
+ s_env_nalloc = 16;
+ }
+
+ /* reallocate script_env with more space if needed */
+ if (i + 1 >= s_env_nalloc) {
+ int new_n = i + 17;
+ char **newenv = (char **) realloc((void *)script_env,
+ new_n * sizeof(char *));
+ if (newenv == 0)
+ return;
+ script_env = newenv;
+ s_env_nalloc = new_n;
+ }
+
+ script_env[i] = newstring;
+ script_env[i+1] = 0;
+}
+
+/*
+ * script_unsetenv - remove a variable from the environment
+ * for scripts.
+ */
+void
+script_unsetenv(var)
+ char *var;
+{
+ int vl = strlen(var);
+ int i;
+ char *p;
+
+ if (script_env == 0)
+ return;
+ for (i = 0; (p = script_env[i]) != 0; ++i) {
+ if (strncmp(p, var, vl) == 0 && p[vl] == '=') {
+ free(p);
+ while ((script_env[i] = script_env[i+1]) != 0)
+ ++i;
+ break;
+ }
+ }
+}
diff --git a/usr.sbin/pppd/options.c b/usr.sbin/pppd/options.c
new file mode 100644
index 0000000..fa3cf93
--- /dev/null
+++ b/usr.sbin/pppd/options.c
@@ -0,0 +1,2683 @@
+/*
+ * options.c - handles option processing for PPP.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 Carnegie Mellon University. The name of the
+ * University 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <syslog.h>
+#include <string.h>
+#include <netdb.h>
+#include <paths.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#ifdef PPP_FILTER
+#include <pcap.h>
+#include <pcap-int.h> /* XXX: To get struct pcap */
+#endif
+
+#include "pppd.h"
+#include "pathnames.h"
+#include "patchlevel.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ipcp.h"
+#include "upap.h"
+#include "chap.h"
+#include "ccp.h"
+#ifdef CBCP_SUPPORT
+#include "cbcp.h"
+#endif
+
+#ifdef INET6
+#include "ipv6cp.h"
+#endif
+
+#ifdef IPX_CHANGE
+#include "ipxcp.h"
+#endif /* IPX_CHANGE */
+
+#include <net/ppp_comp.h>
+
+#define FALSE 0
+#define TRUE 1
+
+#if defined(ultrix) || defined(NeXT)
+char *strdup(char *);
+#endif
+
+#ifndef GIDSET_TYPE
+#define GIDSET_TYPE gid_t
+#endif
+
+/*
+ * Option variables and default values.
+ */
+#ifdef PPP_FILTER
+int dflag = 0; /* Tell libpcap we want debugging */
+#endif
+int debug = 0; /* Debug flag */
+int kdebugflag = 0; /* Tell kernel to print debug messages */
+int default_device = 1; /* Using /dev/tty or equivalent */
+char devnam[MAXPATHLEN] = _PATH_TTY; /* Device name */
+int crtscts = 0; /* Use hardware flow control */
+int modem = 1; /* Use modem control lines */
+int inspeed = 0; /* Input/Output speed requested */
+u_int32_t netmask = 0; /* IP netmask to set on interface */
+int lockflag = 0; /* Create lock file to lock the serial dev */
+int nodetach = 0; /* Don't detach from controlling tty */
+char *connector = NULL; /* Script to establish physical link */
+char *disconnector = NULL; /* Script to disestablish physical link */
+char *welcomer = NULL; /* Script to run after phys link estab. */
+int max_con_attempts = 0; /* Maximum connect tries in non-demand mode */
+int maxconnect = 0; /* Maximum connect time */
+char user[MAXNAMELEN]; /* Username for PAP */
+char passwd[MAXSECRETLEN]; /* Password for PAP */
+int auth_required = 0; /* Peer is required to authenticate */
+int defaultroute = 0; /* assign default route through interface */
+int proxyarp = 0; /* Set up proxy ARP entry for peer */
+int persist = 0; /* Reopen link after it goes down */
+int uselogin = 0; /* Use /etc/passwd for checking PAP */
+int lcp_echo_interval = 0; /* Interval between LCP echo-requests */
+int lcp_echo_fails = 0; /* Tolerance to unanswered echo-requests */
+char our_name[MAXNAMELEN]; /* Our name for authentication purposes */
+char remote_name[MAXNAMELEN]; /* Peer's name for authentication */
+int explicit_remote = 0; /* User specified explicit remote name */
+int usehostname = 0; /* Use hostname for our_name */
+int disable_defaultip = 0; /* Don't use hostname for default IP adrs */
+int demand = 0; /* do dial-on-demand */
+char *ipparam = NULL; /* Extra parameter for ip up/down scripts */
+int cryptpap; /* Passwords in pap-secrets are encrypted */
+int idle_time_limit = 0; /* Disconnect if idle for this many seconds */
+int holdoff = 30; /* # seconds to pause before reconnecting */
+int refuse_pap = 0; /* Set to say we won't do PAP */
+int refuse_chap = 0; /* Set to say we won't do CHAP */
+
+#ifdef MSLANMAN
+int ms_lanman = 0; /* Nonzero if use LanMan password instead of NT */
+ /* Has meaning only with MS-CHAP challenges */
+#endif
+
+struct option_info auth_req_info;
+struct option_info connector_info;
+struct option_info disconnector_info;
+struct option_info welcomer_info;
+struct option_info devnam_info;
+#ifdef PPP_FILTER
+struct bpf_program pass_filter;/* Filter program for packets to pass */
+struct bpf_program active_filter; /* Filter program for link-active pkts */
+pcap_t pc; /* Fake struct pcap so we can compile expr */
+#endif
+
+/*
+ * Prototypes
+ */
+static int setdevname(char *, int);
+static int setspeed(char *);
+static int setdebug(char **);
+static int setkdebug(char **);
+static int setpassive(char **);
+static int setsilent(char **);
+static int noopt(char **);
+static int setnovj(char **);
+static int setnovjccomp(char **);
+static int setvjslots(char **);
+static int reqpap(char **);
+static int nopap(char **);
+#ifdef OLD_OPTIONS
+static int setupapfile(char **);
+#endif
+static int nochap(char **);
+static int reqchap(char **);
+static int noaccomp(char **);
+static int noasyncmap(char **);
+static int noip(char **);
+static int nomagicnumber(char **);
+static int setasyncmap(char **);
+static int setescape(char **);
+static int setmru(char **);
+static int setmtu(char **);
+#ifdef CBCP_SUPPORT
+static int setcbcp(char **);
+#endif
+static int nomru(char **);
+static int nopcomp(char **);
+static int setconnector(char **);
+static int setdisconnector(char **);
+static int setwelcomer(char **);
+static int setmaxcon(char **);
+static int setmaxconnect(char **);
+static int setdomain(char **);
+static int setnetmask(char **);
+static int setcrtscts(char **);
+static int setnocrtscts(char **);
+static int setxonxoff(char **);
+static int setnodetach(char **);
+static int setupdetach(char **);
+static int setmodem(char **);
+static int setlocal(char **);
+static int setlock(char **);
+static int setname(char **);
+static int setuser(char **);
+static int setremote(char **);
+static int setauth(char **);
+static int setnoauth(char **);
+static int readfile(char **);
+static int callfile(char **);
+static int setdefaultroute(char **);
+static int setnodefaultroute(char **);
+static int setproxyarp(char **);
+static int setnoproxyarp(char **);
+static int setpersist(char **);
+static int setnopersist(char **);
+static int setdologin(char **);
+static int setusehostname(char **);
+static int setnoipdflt(char **);
+static int setlcptimeout(char **);
+static int setlcpterm(char **);
+static int setlcpconf(char **);
+static int setlcpfails(char **);
+static int setipcptimeout(char **);
+static int setipcpterm(char **);
+static int setipcpconf(char **);
+static int setipcpfails(char **);
+static int setpaptimeout(char **);
+static int setpapreqs(char **);
+static int setpapreqtime(char **);
+static int setchaptimeout(char **);
+static int setchapchal(char **);
+static int setchapintv(char **);
+static int setipcpaccl(char **);
+static int setipcpaccr(char **);
+static int setlcpechointv(char **);
+static int setlcpechofails(char **);
+static int noccp(char **);
+static int setbsdcomp(char **);
+static int setnobsdcomp(char **);
+static int setdeflate(char **);
+static int setnodeflate(char **);
+static int setnodeflatedraft(char **);
+static int setdemand(char **);
+static int setpred1comp(char **);
+static int setnopred1comp(char **);
+static int setipparam(char **);
+static int setpapcrypt(char **);
+static int setidle(char **);
+static int setholdoff(char **);
+static int setdnsaddr(char **);
+static int resetipv6proto(char **);
+static int resetipxproto(char **);
+static int setwinsaddr(char **);
+static int showversion(char **);
+static int showhelp(char **);
+
+#ifdef PPP_FILTER
+static int setpdebug(char **);
+static int setpassfilter(char **);
+static int setactivefilter(char **);
+#endif
+
+#ifdef INET6
+static int setipv6cp_accept_local(char **);
+static int setipv6cp_use_ip(char **);
+#if defined(SOL2)
+static int setipv6cp_use_persistent(char **);
+#endif
+static int setipv6cptimeout(char **);
+static int setipv6cpterm(char **);
+static int setipv6cpconf(char **);
+static int setipv6cpfails(char **);
+static int setipv6proto(char **);
+#endif /* INET6 */
+
+#ifdef IPX_CHANGE
+static int setipxproto(char **);
+static int setipxanet(char **);
+static int setipxalcl(char **);
+static int setipxarmt(char **);
+static int setipxnetwork(char **);
+static int setipxnode(char **);
+static int setipxrouter(char **);
+static int setipxname(char **);
+static int setipxcptimeout(char **);
+static int setipxcpterm(char **);
+static int setipxcpconf(char **);
+static int setipxcpfails(char **);
+#endif /* IPX_CHANGE */
+
+#ifdef MSLANMAN
+static int setmslanman(char **);
+#endif
+
+static int number_option(char *, u_int32_t *, int);
+static int int_option(char *, int *);
+static int readable(int fd);
+
+/*
+ * Valid arguments.
+ */
+static struct cmd {
+ char *cmd_name;
+ int num_args;
+ int (*cmd_func)(char **);
+} cmds[] = {
+ {"-all", 0, noopt}, /* Don't request/allow any options (useless) */
+ {"noaccomp", 0, noaccomp}, /* Disable Address/Control compression */
+ {"-ac", 0, noaccomp}, /* Disable Address/Control compress */
+ {"default-asyncmap", 0, noasyncmap}, /* Disable asyncmap negoatiation */
+ {"-am", 0, noasyncmap}, /* Disable asyncmap negotiation */
+ {"-as", 1, setasyncmap}, /* set the desired async map */
+ {"-d", 0, setdebug}, /* Increase debugging level */
+ {"nodetach", 0, setnodetach}, /* Don't detach from controlling tty */
+ {"-detach", 0, setnodetach}, /* don't fork */
+ {"updetach", 0, setupdetach}, /* Detach once an NP has come up */
+ {"noip", 0, noip}, /* Disable IP and IPCP */
+ {"-ip", 0, noip}, /* Disable IP and IPCP */
+ {"nomagic", 0, nomagicnumber}, /* Disable magic number negotiation */
+ {"-mn", 0, nomagicnumber}, /* Disable magic number negotiation */
+ {"default-mru", 0, nomru}, /* Disable MRU negotiation */
+ {"-mru", 0, nomru}, /* Disable mru negotiation */
+ {"-p", 0, setpassive}, /* Set passive mode */
+ {"nopcomp", 0, nopcomp}, /* Disable protocol field compression */
+ {"-pc", 0, nopcomp}, /* Disable protocol field compress */
+#if OLD_OPTIONS
+ {"+ua", 1, setupapfile}, /* Get PAP user and password from file */
+#endif
+ {"require-pap", 0, reqpap}, /* Require PAP authentication from peer */
+ {"+pap", 0, reqpap}, /* Require PAP auth from peer */
+ {"refuse-pap", 0, nopap}, /* Don't agree to auth to peer with PAP */
+ {"-pap", 0, nopap}, /* Don't allow UPAP authentication with peer */
+ {"require-chap", 0, reqchap}, /* Require CHAP authentication from peer */
+ {"+chap", 0, reqchap}, /* Require CHAP authentication from peer */
+ {"refuse-chap", 0, nochap}, /* Don't agree to auth to peer with CHAP */
+ {"-chap", 0, nochap}, /* Don't allow CHAP authentication with peer */
+ {"novj", 0, setnovj}, /* Disable VJ compression */
+ {"-vj", 0, setnovj}, /* disable VJ compression */
+ {"novjccomp", 0, setnovjccomp}, /* disable VJ connection-ID compression */
+ {"-vjccomp", 0, setnovjccomp}, /* disable VJ connection-ID compression */
+ {"vj-max-slots", 1, setvjslots}, /* Set maximum VJ header slots */
+ {"asyncmap", 1, setasyncmap}, /* set the desired async map */
+ {"escape", 1, setescape}, /* set chars to escape on transmission */
+ {"connect", 1, setconnector}, /* A program to set up a connection */
+ {"disconnect", 1, setdisconnector}, /* program to disconnect serial dev. */
+ {"welcome", 1, setwelcomer},/* Script to welcome client */
+ {"connect-max-attempts", 1, setmaxcon}, /* maximum # connect attempts */
+ {"maxconnect", 1, setmaxconnect}, /* specify a maximum connect time */
+ {"crtscts", 0, setcrtscts}, /* set h/w flow control */
+ {"nocrtscts", 0, setnocrtscts}, /* clear h/w flow control */
+ {"-crtscts", 0, setnocrtscts}, /* clear h/w flow control */
+ {"xonxoff", 0, setxonxoff}, /* set s/w flow control */
+ {"debug", 0, setdebug}, /* Increase debugging level */
+ {"kdebug", 1, setkdebug}, /* Enable kernel-level debugging */
+ {"domain", 1, setdomain}, /* Add given domain name to hostname*/
+ {"mru", 1, setmru}, /* Set MRU value for negotiation */
+ {"mtu", 1, setmtu}, /* Set our MTU */
+#ifdef CBCP_SUPPORT
+ {"callback", 1, setcbcp}, /* Ask for callback */
+#endif
+ {"netmask", 1, setnetmask}, /* set netmask */
+ {"passive", 0, setpassive}, /* Set passive mode */
+ {"silent", 0, setsilent}, /* Set silent mode */
+ {"modem", 0, setmodem}, /* Use modem control lines */
+ {"local", 0, setlocal}, /* Don't use modem control lines */
+ {"lock", 0, setlock}, /* Lock serial device (with lock file) */
+ {"name", 1, setname}, /* Set local name for authentication */
+ {"user", 1, setuser}, /* Set name for auth with peer */
+ {"usehostname", 0, setusehostname}, /* Must use hostname for auth. */
+ {"remotename", 1, setremote}, /* Set remote name for authentication */
+ {"auth", 0, setauth}, /* Require authentication from peer */
+ {"noauth", 0, setnoauth}, /* Don't require peer to authenticate */
+ {"file", 1, readfile}, /* Take options from a file */
+ {"call", 1, callfile}, /* Take options from a privileged file */
+ {"defaultroute", 0, setdefaultroute}, /* Add default route */
+ {"nodefaultroute", 0, setnodefaultroute}, /* disable defaultroute option */
+ {"-defaultroute", 0, setnodefaultroute}, /* disable defaultroute option */
+ {"proxyarp", 0, setproxyarp}, /* Add proxy ARP entry */
+ {"noproxyarp", 0, setnoproxyarp}, /* disable proxyarp option */
+ {"-proxyarp", 0, setnoproxyarp}, /* disable proxyarp option */
+ {"persist", 0, setpersist}, /* Keep on reopening connection after close */
+ {"nopersist", 0, setnopersist}, /* Turn off persist option */
+ {"demand", 0, setdemand}, /* Dial on demand */
+ {"login", 0, setdologin}, /* Use system password database for UPAP */
+ {"noipdefault", 0, setnoipdflt}, /* Don't use name for default IP adrs */
+ {"lcp-echo-failure", 1, setlcpechofails}, /* consecutive echo failures */
+ {"lcp-echo-interval", 1, setlcpechointv}, /* time for lcp echo events */
+ {"lcp-restart", 1, setlcptimeout}, /* Set timeout for LCP */
+ {"lcp-max-terminate", 1, setlcpterm}, /* Set max #xmits for term-reqs */
+ {"lcp-max-configure", 1, setlcpconf}, /* Set max #xmits for conf-reqs */
+ {"lcp-max-failure", 1, setlcpfails}, /* Set max #conf-naks for LCP */
+ {"ipcp-restart", 1, setipcptimeout}, /* Set timeout for IPCP */
+ {"ipcp-max-terminate", 1, setipcpterm}, /* Set max #xmits for term-reqs */
+ {"ipcp-max-configure", 1, setipcpconf}, /* Set max #xmits for conf-reqs */
+ {"ipcp-max-failure", 1, setipcpfails}, /* Set max #conf-naks for IPCP */
+ {"pap-restart", 1, setpaptimeout}, /* Set retransmit timeout for PAP */
+ {"pap-max-authreq", 1, setpapreqs}, /* Set max #xmits for auth-reqs */
+ {"pap-timeout", 1, setpapreqtime}, /* Set time limit for peer PAP auth. */
+ {"chap-restart", 1, setchaptimeout}, /* Set timeout for CHAP */
+ {"chap-max-challenge", 1, setchapchal}, /* Set max #xmits for challenge */
+ {"chap-interval", 1, setchapintv}, /* Set interval for rechallenge */
+ {"ipcp-accept-local", 0, setipcpaccl}, /* Accept peer's address for us */
+ {"ipcp-accept-remote", 0, setipcpaccr}, /* Accept peer's address for it */
+ {"noccp", 0, noccp}, /* Disable CCP negotiation */
+ {"-ccp", 0, noccp}, /* Disable CCP negotiation */
+ {"bsdcomp", 1, setbsdcomp}, /* request BSD-Compress */
+ {"nobsdcomp", 0, setnobsdcomp}, /* don't allow BSD-Compress */
+ {"-bsdcomp", 0, setnobsdcomp}, /* don't allow BSD-Compress */
+ {"deflate", 1, setdeflate}, /* request Deflate compression */
+ {"nodeflate", 0, setnodeflate}, /* don't allow Deflate compression */
+ {"-deflate", 0, setnodeflate}, /* don't allow Deflate compression */
+ {"nodeflatedraft", 0, setnodeflatedraft}, /* don't use draft deflate # */
+ {"predictor1", 0, setpred1comp}, /* request Predictor-1 */
+ {"nopredictor1", 0, setnopred1comp},/* don't allow Predictor-1 */
+ {"-predictor1", 0, setnopred1comp}, /* don't allow Predictor-1 */
+ {"ipparam", 1, setipparam}, /* set ip script parameter */
+ {"papcrypt", 0, setpapcrypt}, /* PAP passwords encrypted */
+ {"idle", 1, setidle}, /* idle time limit (seconds) */
+ {"holdoff", 1, setholdoff}, /* set holdoff time (seconds) */
+/* backwards compat hack */
+ {"dns1", 1, setdnsaddr}, /* DNS address for the peer's use */
+ {"dns2", 1, setdnsaddr}, /* DNS address for the peer's use */
+/* end compat hack */
+ {"ms-dns", 1, setdnsaddr}, /* DNS address for the peer's use */
+ {"ms-wins", 1, setwinsaddr}, /* Nameserver for SMB over TCP/IP for peer */
+ {"noipv6", 0, resetipv6proto}, /* Disable IPv6 and IPv6CP */
+ {"-ipv6", 0, resetipv6proto}, /* Disable IPv6 and IPv6CP */
+ {"noipx", 0, resetipxproto}, /* Disable IPXCP (and IPX) */
+ {"-ipx", 0, resetipxproto}, /* Disable IPXCP (and IPX) */
+ {"--version", 0, showversion}, /* Show version number */
+ {"--help", 0, showhelp}, /* Show brief listing of options */
+ {"-h", 0, showhelp}, /* ditto */
+
+#ifdef PPP_FILTER
+ {"pdebug", 1, setpdebug}, /* libpcap debugging */
+ {"pass-filter", 1, setpassfilter}, /* set filter for packets to pass */
+ {"active-filter", 1, setactivefilter}, /* set filter for active pkts */
+#endif
+
+#ifdef INET6
+ {"ipv6", 1, setifaceid}, /* Set interface id for IPV6" */
+ {"+ipv6", 0, setipv6proto}, /* Enable IPv6 and IPv6CP */
+ {"ipv6cp-accept-local", 0, setipv6cp_accept_local}, /* Accept peer's iface id for us */
+ {"ipv6cp-use-ipaddr", 0, setipv6cp_use_ip}, /* Use IPv4 addr as iface id */
+#if defined(SOL2)
+ {"ipv6cp-use-persistent", 0, setipv6cp_use_persistent}, /* Use uniquely-available persistent value for link local addr */
+#endif
+ {"ipv6cp-restart", 1, setipv6cptimeout}, /* Set timeout for IPv6CP */
+ {"ipv6cp-max-terminate", 1, setipv6cpterm}, /* max #xmits for term-reqs */
+ {"ipv6cp-max-configure", 1, setipv6cpconf}, /* max #xmits for conf-reqs */
+ {"ipv6cp-max-failure", 1, setipv6cpfails}, /* max #conf-naks for IPv6CP */
+#endif
+
+#ifdef IPX_CHANGE
+ {"ipx-network", 1, setipxnetwork}, /* IPX network number */
+ {"ipxcp-accept-network", 0, setipxanet}, /* Accept peer netowrk */
+ {"ipx-node", 1, setipxnode}, /* IPX node number */
+ {"ipxcp-accept-local", 0, setipxalcl}, /* Accept our address */
+ {"ipxcp-accept-remote", 0, setipxarmt}, /* Accept peer's address */
+ {"ipx-routing", 1, setipxrouter}, /* IPX routing proto number */
+ {"ipx-router-name", 1, setipxname}, /* IPX router name */
+ {"ipxcp-restart", 1, setipxcptimeout}, /* Set timeout for IPXCP */
+ {"ipxcp-max-terminate", 1, setipxcpterm}, /* max #xmits for term-reqs */
+ {"ipxcp-max-configure", 1, setipxcpconf}, /* max #xmits for conf-reqs */
+ {"ipxcp-max-failure", 1, setipxcpfails}, /* max #conf-naks for IPXCP */
+#if 0
+ {"ipx-compression", 1, setipxcompression}, /* IPX compression number */
+#endif
+ {"ipx", 0, setipxproto}, /* Enable IPXCP (and IPX) */
+ {"+ipx", 0, setipxproto}, /* Enable IPXCP (and IPX) */
+#endif /* IPX_CHANGE */
+
+#ifdef MSLANMAN
+ {"ms-lanman", 0, setmslanman}, /* Use LanMan psswd when using MS-CHAP */
+#endif
+
+ {NULL, 0, NULL}
+};
+
+
+#ifndef IMPLEMENTATION
+#define IMPLEMENTATION ""
+#endif
+
+static const char usage_string[] = "\
+pppd version %s patch level %d%s\n\
+Usage: %s [ options ], where options are:\n\
+ <device> Communicate over the named device\n\
+ <speed> Set the baud rate to <speed>\n\
+ <loc>:<rem> Set the local and/or remote interface IP\n\
+ addresses. Either one may be omitted.\n\
+ asyncmap <n> Set the desired async map to hex <n>\n\
+ auth Require authentication from peer\n\
+ connect <p> Invoke shell command <p> to set up the serial line\n\
+ crtscts Use hardware RTS/CTS flow control\n\
+ defaultroute Add default route through interface\n\
+ file <f> Take options from file <f>\n\
+ modem Use modem control lines\n\
+ mru <n> Set MRU value to <n> for negotiation\n\
+See pppd(8) for more options.\n\
+";
+
+static char *current_option; /* the name of the option being parsed */
+static int privileged_option; /* set iff the current option came from root */
+static char *option_source; /* string saying where the option came from */
+
+/*
+ * parse_args - parse a string of arguments from the command line.
+ */
+int
+parse_args(argc, argv)
+ int argc;
+ char **argv;
+{
+ char *arg;
+ struct cmd *cmdp;
+ int ret;
+
+ privileged_option = privileged;
+ option_source = "command line";
+ while (argc > 0) {
+ arg = *argv++;
+ --argc;
+
+ /*
+ * First see if it's a command.
+ */
+ for (cmdp = cmds; cmdp->cmd_name; cmdp++)
+ if (!strcmp(arg, cmdp->cmd_name))
+ break;
+
+ if (cmdp->cmd_name != NULL) {
+ if (argc < cmdp->num_args) {
+ option_error("too few parameters for option %s", arg);
+ return 0;
+ }
+ current_option = arg;
+ if (!(*cmdp->cmd_func)(argv))
+ return 0;
+ argc -= cmdp->num_args;
+ argv += cmdp->num_args;
+
+ } else {
+ /*
+ * Maybe a tty name, speed or IP address?
+ */
+ if ((ret = setdevname(arg, 0)) == 0
+ && (ret = setspeed(arg)) == 0
+ && (ret = setipaddr(arg)) == 0) {
+ option_error("unrecognized option '%s'", arg);
+ usage();
+ return 0;
+ }
+ if (ret < 0) /* error */
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/*
+ * scan_args - scan the command line arguments to get the tty name,
+ * if specified.
+ */
+void
+scan_args(argc, argv)
+ int argc;
+ char **argv;
+{
+ char *arg;
+ struct cmd *cmdp;
+
+ while (argc > 0) {
+ arg = *argv++;
+ --argc;
+
+ /* Skip options and their arguments */
+ for (cmdp = cmds; cmdp->cmd_name; cmdp++)
+ if (!strcmp(arg, cmdp->cmd_name))
+ break;
+
+ if (cmdp->cmd_name != NULL) {
+ argc -= cmdp->num_args;
+ argv += cmdp->num_args;
+ continue;
+ }
+
+ /* Check if it's a tty name and copy it if so */
+ (void) setdevname(arg, 1);
+ }
+}
+
+/*
+ * usage - print out a message telling how to use the program.
+ */
+void
+usage()
+{
+ if (phase == PHASE_INITIALIZE)
+ fprintf(stderr, usage_string, VERSION, PATCHLEVEL, IMPLEMENTATION,
+ progname);
+}
+
+/*
+ * showhelp - print out usage message and exit.
+ */
+static int
+showhelp(argv)
+ char **argv;
+{
+ if (phase == PHASE_INITIALIZE) {
+ usage();
+ exit(0);
+ }
+ return 0;
+}
+
+/*
+ * showversion - print out the version number and exit.
+ */
+static int
+showversion(argv)
+ char **argv;
+{
+ if (phase == PHASE_INITIALIZE) {
+ fprintf(stderr, "pppd version %s patch level %d%s\n",
+ VERSION, PATCHLEVEL, IMPLEMENTATION);
+ exit(0);
+ }
+ return 0;
+}
+
+/*
+ * options_from_file - Read a string of options from a file,
+ * and interpret them.
+ */
+int
+options_from_file(filename, must_exist, check_prot, priv)
+ char *filename;
+ int must_exist;
+ int check_prot;
+ int priv;
+{
+ FILE *f;
+ int i, newline, ret;
+ struct cmd *cmdp;
+ int oldpriv;
+ char *argv[MAXARGS];
+ char args[MAXARGS][MAXWORDLEN];
+ char cmd[MAXWORDLEN];
+
+ if ((f = fopen(filename, "r")) == NULL) {
+ if (!must_exist && errno == ENOENT)
+ return 1;
+ option_error("Can't open options file %s: %m", filename);
+ return 0;
+ }
+ if (check_prot && !readable(fileno(f))) {
+ option_error("Can't open options file %s: access denied", filename);
+ fclose(f);
+ return 0;
+ }
+
+ oldpriv = privileged_option;
+ privileged_option = priv;
+ ret = 0;
+ while (getword(f, cmd, &newline, filename)) {
+ /*
+ * First see if it's a command.
+ */
+ for (cmdp = cmds; cmdp->cmd_name; cmdp++)
+ if (!strcmp(cmd, cmdp->cmd_name))
+ break;
+
+ if (cmdp->cmd_name != NULL) {
+ for (i = 0; i < cmdp->num_args; ++i) {
+ if (!getword(f, args[i], &newline, filename)) {
+ option_error(
+ "In file %s: too few parameters for option '%s'",
+ filename, cmd);
+ goto err;
+ }
+ argv[i] = args[i];
+ }
+ current_option = cmd;
+ if (!(*cmdp->cmd_func)(argv))
+ goto err;
+
+ } else {
+ /*
+ * Maybe a tty name, speed or IP address?
+ */
+ if ((i = setdevname(cmd, 0)) == 0
+ && (i = setspeed(cmd)) == 0
+ && (i = setipaddr(cmd)) == 0) {
+ option_error("In file %s: unrecognized option '%s'",
+ filename, cmd);
+ goto err;
+ }
+ if (i < 0) /* error */
+ goto err;
+ }
+ }
+ ret = 1;
+
+err:
+ fclose(f);
+ privileged_option = oldpriv;
+ return ret;
+}
+
+/*
+ * options_from_user - See if the use has a ~/.ppprc file,
+ * and if so, interpret options from it.
+ */
+int
+options_from_user()
+{
+ char *user, *path, *file;
+ int ret;
+ struct passwd *pw;
+
+ pw = getpwuid(getuid());
+ if (pw == NULL || (user = pw->pw_dir) == NULL || user[0] == 0)
+ return 1;
+ file = _PATH_USEROPT;
+ path = malloc(strlen(user) + strlen(file) + 2);
+ if (path == NULL)
+ novm("init file name");
+ strcpy(path, user);
+ strcat(path, "/");
+ strcat(path, file);
+ ret = options_from_file(path, 0, 1, privileged);
+ free(path);
+ return ret;
+}
+
+/*
+ * options_for_tty - See if an options file exists for the serial
+ * device, and if so, interpret options from it.
+ */
+int
+options_for_tty()
+{
+ char *dev, *path, *p;
+ int ret;
+
+ dev = devnam;
+ if (strncmp(dev, _PATH_DEV, sizeof _PATH_DEV - 1) == 0)
+ dev += 5;
+ if (strcmp(dev, "tty") == 0)
+ return 1; /* don't look for /etc/ppp/options.tty */
+ path = malloc(strlen(_PATH_TTYOPT) + strlen(dev) + 1);
+ if (path == NULL)
+ novm("tty init file name");
+ strcpy(path, _PATH_TTYOPT);
+ /* Turn slashes into dots, for Solaris case (e.g. /dev/term/a) */
+ for (p = path + strlen(path); *dev != 0; ++dev)
+ *p++ = (*dev == '/'? '.': *dev);
+ *p = 0;
+ ret = options_from_file(path, 0, 0, 1);
+ free(path);
+ return ret;
+}
+
+/*
+ * option_error - print a message about an error in an option.
+ * The message is logged, and also sent to
+ * stderr if phase == PHASE_INITIALIZE.
+ */
+void
+option_error __V((char *fmt, ...))
+{
+ va_list args;
+ char buf[256];
+
+#if __STDC__
+ va_start(args, fmt);
+#else
+ char *fmt;
+ va_start(args);
+ fmt = va_arg(args, char *);
+#endif
+ vfmtmsg(buf, sizeof(buf), fmt, args);
+ va_end(args);
+ if (phase == PHASE_INITIALIZE)
+ fprintf(stderr, "%s: %s\n", progname, buf);
+ syslog(LOG_ERR, "%s", buf);
+}
+
+/*
+ * readable - check if a file is readable by the real user.
+ */
+static int
+readable(fd)
+ int fd;
+{
+ uid_t uid;
+ int ngroups, i;
+ struct stat sbuf;
+ GIDSET_TYPE groups[NGROUPS_MAX];
+
+ uid = getuid();
+ if (uid == 0)
+ return 1;
+ if (fstat(fd, &sbuf) != 0)
+ return 0;
+ if (sbuf.st_uid == uid)
+ return sbuf.st_mode & S_IRUSR;
+ if (sbuf.st_gid == getgid())
+ return sbuf.st_mode & S_IRGRP;
+ ngroups = getgroups(NGROUPS_MAX, groups);
+ for (i = 0; i < ngroups; ++i)
+ if (sbuf.st_gid == groups[i])
+ return sbuf.st_mode & S_IRGRP;
+ return sbuf.st_mode & S_IROTH;
+}
+
+/*
+ * Read a word from a file.
+ * Words are delimited by white-space or by quotes (" or ').
+ * Quotes, white-space and \ may be escaped with \.
+ * \<newline> is ignored.
+ */
+int
+getword(f, word, newlinep, filename)
+ FILE *f;
+ char *word;
+ int *newlinep;
+ char *filename;
+{
+ int c, len, escape;
+ int quoted, comment;
+ int value, digit, got, n;
+
+#define isoctal(c) ((c) >= '0' && (c) < '8')
+
+ *newlinep = 0;
+ len = 0;
+ escape = 0;
+ comment = 0;
+
+ /*
+ * First skip white-space and comments.
+ */
+ for (;;) {
+ c = getc(f);
+ if (c == EOF)
+ break;
+
+ /*
+ * A newline means the end of a comment; backslash-newline
+ * is ignored. Note that we cannot have escape && comment.
+ */
+ if (c == '\n') {
+ if (!escape) {
+ *newlinep = 1;
+ comment = 0;
+ } else
+ escape = 0;
+ continue;
+ }
+
+ /*
+ * Ignore characters other than newline in a comment.
+ */
+ if (comment)
+ continue;
+
+ /*
+ * If this character is escaped, we have a word start.
+ */
+ if (escape)
+ break;
+
+ /*
+ * If this is the escape character, look at the next character.
+ */
+ if (c == '\\') {
+ escape = 1;
+ continue;
+ }
+
+ /*
+ * If this is the start of a comment, ignore the rest of the line.
+ */
+ if (c == '#') {
+ comment = 1;
+ continue;
+ }
+
+ /*
+ * A non-whitespace character is the start of a word.
+ */
+ if (!isspace(c))
+ break;
+ }
+
+ /*
+ * Save the delimiter for quoted strings.
+ */
+ if (!escape && (c == '"' || c == '\'')) {
+ quoted = c;
+ c = getc(f);
+ } else
+ quoted = 0;
+
+ /*
+ * Process characters until the end of the word.
+ */
+ while (c != EOF) {
+ if (escape) {
+ /*
+ * This character is escaped: backslash-newline is ignored,
+ * various other characters indicate particular values
+ * as for C backslash-escapes.
+ */
+ escape = 0;
+ if (c == '\n') {
+ c = getc(f);
+ continue;
+ }
+
+ got = 0;
+ switch (c) {
+ case 'a':
+ value = '\a';
+ break;
+ case 'b':
+ value = '\b';
+ break;
+ case 'f':
+ value = '\f';
+ break;
+ case 'n':
+ value = '\n';
+ break;
+ case 'r':
+ value = '\r';
+ break;
+ case 's':
+ value = ' ';
+ break;
+ case 't':
+ value = '\t';
+ break;
+
+ default:
+ if (isoctal(c)) {
+ /*
+ * \ddd octal sequence
+ */
+ value = 0;
+ for (n = 0; n < 3 && isoctal(c); ++n) {
+ value = (value << 3) + (c & 07);
+ c = getc(f);
+ }
+ got = 1;
+ break;
+ }
+
+ if (c == 'x') {
+ /*
+ * \x<hex_string> sequence
+ */
+ value = 0;
+ c = getc(f);
+ for (n = 0; n < 2 && isxdigit(c); ++n) {
+ digit = toupper(c) - '0';
+ if (digit > 10)
+ digit += '0' + 10 - 'A';
+ value = (value << 4) + digit;
+ c = getc (f);
+ }
+ got = 1;
+ break;
+ }
+
+ /*
+ * Otherwise the character stands for itself.
+ */
+ value = c;
+ break;
+ }
+
+ /*
+ * Store the resulting character for the escape sequence.
+ */
+ if (len < MAXWORDLEN-1)
+ word[len] = value;
+ ++len;
+
+ if (!got)
+ c = getc(f);
+ continue;
+
+ }
+
+ /*
+ * Not escaped: see if we've reached the end of the word.
+ */
+ if (quoted) {
+ if (c == quoted)
+ break;
+ } else {
+ if (isspace(c) || c == '#') {
+ ungetc (c, f);
+ break;
+ }
+ }
+
+ /*
+ * Backslash starts an escape sequence.
+ */
+ if (c == '\\') {
+ escape = 1;
+ c = getc(f);
+ continue;
+ }
+
+ /*
+ * An ordinary character: store it in the word and get another.
+ */
+ if (len < MAXWORDLEN-1)
+ word[len] = c;
+ ++len;
+
+ c = getc(f);
+ }
+
+ /*
+ * End of the word: check for errors.
+ */
+ if (c == EOF) {
+ if (ferror(f)) {
+ if (errno == 0)
+ errno = EIO;
+ option_error("Error reading %s: %m", filename);
+ die(1);
+ }
+ /*
+ * If len is zero, then we didn't find a word before the
+ * end of the file.
+ */
+ if (len == 0)
+ return 0;
+ }
+
+ /*
+ * Warn if the word was too long, and append a terminating null.
+ */
+ if (len >= MAXWORDLEN) {
+ option_error("warning: word in file %s too long (%.20s...)",
+ filename, word);
+ len = MAXWORDLEN - 1;
+ }
+ word[len] = 0;
+
+ return 1;
+
+#undef isoctal
+
+}
+
+/*
+ * number_option - parse an unsigned numeric parameter for an option.
+ */
+static int
+number_option(str, valp, base)
+ char *str;
+ u_int32_t *valp;
+ int base;
+{
+ char *ptr;
+
+ *valp = strtoul(str, &ptr, base);
+ if (ptr == str) {
+ option_error("invalid numeric parameter '%s' for %s option",
+ str, current_option);
+ return 0;
+ }
+ return 1;
+}
+
+
+/*
+ * int_option - like number_option, but valp is int *,
+ * the base is assumed to be 0, and *valp is not changed
+ * if there is an error.
+ */
+static int
+int_option(str, valp)
+ char *str;
+ int *valp;
+{
+ u_int32_t v;
+
+ if (!number_option(str, &v, 0))
+ return 0;
+ *valp = (int) v;
+ return 1;
+}
+
+
+/*
+ * The following procedures parse options.
+ */
+
+/*
+ * readfile - take commands from a file.
+ */
+static int
+readfile(argv)
+ char **argv;
+{
+ return options_from_file(*argv, 1, 1, privileged_option);
+}
+
+/*
+ * callfile - take commands from /etc/ppp/peers/<name>.
+ * Name may not contain /../, start with / or ../, or end in /..
+ */
+static int
+callfile(argv)
+ char **argv;
+{
+ char *fname, *arg, *p;
+ int l, ok;
+
+ arg = *argv;
+ ok = 1;
+ if (arg[0] == '/' || arg[0] == 0)
+ ok = 0;
+ else {
+ for (p = arg; *p != 0; ) {
+ if (p[0] == '.' && p[1] == '.' && (p[2] == '/' || p[2] == 0)) {
+ ok = 0;
+ break;
+ }
+ while (*p != '/' && *p != 0)
+ ++p;
+ if (*p == '/')
+ ++p;
+ }
+ }
+ if (!ok) {
+ option_error("call option value may not contain .. or start with /");
+ return 0;
+ }
+
+ l = strlen(arg) + strlen(_PATH_PEERFILES) + 1;
+ if ((fname = (char *) malloc(l)) == NULL)
+ novm("call file name");
+ strcpy(fname, _PATH_PEERFILES);
+ strcat(fname, arg);
+
+ ok = options_from_file(fname, 1, 1, 1);
+
+ free(fname);
+ return ok;
+}
+
+
+/*
+ * setdebug - Set debug (command line argument).
+ */
+static int
+setdebug(argv)
+ char **argv;
+{
+ debug++;
+ return (1);
+}
+
+/*
+ * setkdebug - Set kernel debugging level.
+ */
+static int
+setkdebug(argv)
+ char **argv;
+{
+ return int_option(*argv, &kdebugflag);
+}
+
+#ifdef PPP_FILTER
+/*
+ * setpdebug - Set libpcap debugging level.
+ */
+static int
+setpdebug(argv)
+ char **argv;
+{
+ return int_option(*argv, &dflag);
+}
+
+/*
+ * setpassfilter - Set the pass filter for packets
+ */
+static int
+setpassfilter(argv)
+ char **argv;
+{
+ pc.linktype = DLT_PPP;
+ pc.snapshot = PPP_HDRLEN;
+
+ if (pcap_compile(&pc, &pass_filter, *argv, 1, netmask) == 0)
+ return 1;
+ option_error("error in pass-filter expression: %s\n", pcap_geterr(&pc));
+ return 0;
+}
+
+/*
+ * setactivefilter - Set the active filter for packets
+ */
+static int
+setactivefilter(argv)
+ char **argv;
+{
+ pc.linktype = DLT_PPP;
+ pc.snapshot = PPP_HDRLEN;
+
+ if (pcap_compile(&pc, &active_filter, *argv, 1, netmask) == 0)
+ return 1;
+ option_error("error in active-filter expression: %s\n", pcap_geterr(&pc));
+ return 0;
+}
+#endif
+
+/*
+ * noopt - Disable all options.
+ */
+static int
+noopt(argv)
+ char **argv;
+{
+ BZERO((char *) &lcp_wantoptions[0], sizeof (struct lcp_options));
+ BZERO((char *) &lcp_allowoptions[0], sizeof (struct lcp_options));
+ BZERO((char *) &ipcp_wantoptions[0], sizeof (struct ipcp_options));
+ BZERO((char *) &ipcp_allowoptions[0], sizeof (struct ipcp_options));
+
+#ifdef IPX_CHANGE
+ BZERO((char *) &ipxcp_wantoptions[0], sizeof (struct ipxcp_options));
+ BZERO((char *) &ipxcp_allowoptions[0], sizeof (struct ipxcp_options));
+#endif /* IPX_CHANGE */
+
+ return (1);
+}
+
+/*
+ * noaccomp - Disable Address/Control field compression negotiation.
+ */
+static int
+noaccomp(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].neg_accompression = 0;
+ lcp_allowoptions[0].neg_accompression = 0;
+ return (1);
+}
+
+
+/*
+ * noasyncmap - Disable async map negotiation.
+ */
+static int
+noasyncmap(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].neg_asyncmap = 0;
+ lcp_allowoptions[0].neg_asyncmap = 0;
+ return (1);
+}
+
+
+/*
+ * noip - Disable IP and IPCP.
+ */
+static int
+noip(argv)
+ char **argv;
+{
+ ipcp_protent.enabled_flag = 0;
+ return (1);
+}
+
+
+/*
+ * nomagicnumber - Disable magic number negotiation.
+ */
+static int
+nomagicnumber(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].neg_magicnumber = 0;
+ lcp_allowoptions[0].neg_magicnumber = 0;
+ return (1);
+}
+
+
+/*
+ * nomru - Disable mru negotiation.
+ */
+static int
+nomru(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].neg_mru = 0;
+ lcp_allowoptions[0].neg_mru = 0;
+ return (1);
+}
+
+
+/*
+ * setmru - Set MRU for negotiation.
+ */
+static int
+setmru(argv)
+ char **argv;
+{
+ u_int32_t mru;
+
+ if (!number_option(*argv, &mru, 0))
+ return 0;
+ lcp_wantoptions[0].mru = mru;
+ lcp_wantoptions[0].neg_mru = 1;
+ return (1);
+}
+
+
+/*
+ * setmru - Set the largest MTU we'll use.
+ */
+static int
+setmtu(argv)
+ char **argv;
+{
+ u_int32_t mtu;
+
+ if (!number_option(*argv, &mtu, 0))
+ return 0;
+ if (mtu < MINMRU || mtu > MAXMRU) {
+ option_error("mtu option value of %u is too %s", mtu,
+ (mtu < MINMRU? "small": "large"));
+ return 0;
+ }
+ lcp_allowoptions[0].mru = mtu;
+ return (1);
+}
+
+#ifdef CBCP_SUPPORT
+static int
+setcbcp(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].neg_cbcp = 1;
+ cbcp_protent.enabled_flag = 1;
+ cbcp[0].us_number = strdup(*argv);
+ if (cbcp[0].us_number == 0)
+ novm("callback number");
+ cbcp[0].us_type |= (1 << CB_CONF_USER);
+ cbcp[0].us_type |= (1 << CB_CONF_ADMIN);
+ return (1);
+}
+#endif
+
+/*
+ * nopcomp - Disable Protocol field compression negotiation.
+ */
+static int
+nopcomp(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].neg_pcompression = 0;
+ lcp_allowoptions[0].neg_pcompression = 0;
+ return (1);
+}
+
+
+/*
+ * setpassive - Set passive mode (don't give up if we time out sending
+ * LCP configure-requests).
+ */
+static int
+setpassive(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].passive = 1;
+ return (1);
+}
+
+
+/*
+ * setsilent - Set silent mode (don't start sending LCP configure-requests
+ * until we get one from the peer).
+ */
+static int
+setsilent(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].silent = 1;
+ return 1;
+}
+
+
+/*
+ * nopap - Disable PAP authentication with peer.
+ */
+static int
+nopap(argv)
+ char **argv;
+{
+ refuse_pap = 1;
+ return (1);
+}
+
+
+/*
+ * reqpap - Require PAP authentication from peer.
+ */
+static int
+reqpap(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].neg_upap = 1;
+ setauth(NULL);
+ return 1;
+}
+
+#if OLD_OPTIONS
+/*
+ * setupapfile - specifies UPAP info for authenticating with peer.
+ */
+static int
+setupapfile(argv)
+ char **argv;
+{
+ FILE * ufile;
+ int l;
+
+ lcp_allowoptions[0].neg_upap = 1;
+
+ /* open user info file */
+ if ((ufile = fopen(*argv, "r")) == NULL) {
+ option_error("unable to open user login data file %s", *argv);
+ return 0;
+ }
+ if (!readable(fileno(ufile))) {
+ option_error("%s: access denied", *argv);
+ return 0;
+ }
+ check_access(ufile, *argv);
+
+ /* get username */
+ if (fgets(user, MAXNAMELEN - 1, ufile) == NULL
+ || fgets(passwd, MAXSECRETLEN - 1, ufile) == NULL){
+ option_error("unable to read user login data file %s", *argv);
+ return 0;
+ }
+ fclose(ufile);
+
+ /* get rid of newlines */
+ l = strlen(user);
+ if (l > 0 && user[l-1] == '\n')
+ user[l-1] = 0;
+ l = strlen(passwd);
+ if (l > 0 && passwd[l-1] == '\n')
+ passwd[l-1] = 0;
+
+ return (1);
+}
+#endif
+
+/*
+ * nochap - Disable CHAP authentication with peer.
+ */
+static int
+nochap(argv)
+ char **argv;
+{
+ refuse_chap = 1;
+ return (1);
+}
+
+
+/*
+ * reqchap - Require CHAP authentication from peer.
+ */
+static int
+reqchap(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].neg_chap = 1;
+ setauth(NULL);
+ return (1);
+}
+
+
+/*
+ * setnovj - disable vj compression
+ */
+static int
+setnovj(argv)
+ char **argv;
+{
+ ipcp_wantoptions[0].neg_vj = 0;
+ ipcp_allowoptions[0].neg_vj = 0;
+ return (1);
+}
+
+
+/*
+ * setnovjccomp - disable VJ connection-ID compression
+ */
+static int
+setnovjccomp(argv)
+ char **argv;
+{
+ ipcp_wantoptions[0].cflag = 0;
+ ipcp_allowoptions[0].cflag = 0;
+ return 1;
+}
+
+
+/*
+ * setvjslots - set maximum number of connection slots for VJ compression
+ */
+static int
+setvjslots(argv)
+ char **argv;
+{
+ int value;
+
+ if (!int_option(*argv, &value))
+ return 0;
+ if (value < 2 || value > 16) {
+ option_error("vj-max-slots value must be between 2 and 16");
+ return 0;
+ }
+ ipcp_wantoptions [0].maxslotindex =
+ ipcp_allowoptions[0].maxslotindex = value - 1;
+ return 1;
+}
+
+
+/*
+ * setconnector - Set a program to connect to a serial line
+ */
+static int
+setconnector(argv)
+ char **argv;
+{
+ connector = strdup(*argv);
+ if (connector == NULL)
+ novm("connect script");
+ connector_info.priv = privileged_option;
+ connector_info.source = option_source;
+
+ return (1);
+}
+
+/*
+ * setdisconnector - Set a program to disconnect from the serial line
+ */
+static int
+setdisconnector(argv)
+ char **argv;
+{
+ disconnector = strdup(*argv);
+ if (disconnector == NULL)
+ novm("disconnect script");
+ disconnector_info.priv = privileged_option;
+ disconnector_info.source = option_source;
+
+ return (1);
+}
+
+/*
+ * setwelcomer - Set a program to welcome a client after connection
+ */
+static int
+setwelcomer(argv)
+ char **argv;
+{
+ welcomer = strdup(*argv);
+ if (welcomer == NULL)
+ novm("welcome script");
+ welcomer_info.priv = privileged_option;
+ welcomer_info.source = option_source;
+
+ return (1);
+}
+
+static int
+setmaxcon(argv)
+ char **argv;
+{
+ return int_option(*argv, &max_con_attempts);
+}
+
+/*
+ * setmaxconnect - Set the maximum connect time
+ */
+static int
+setmaxconnect(argv)
+ char **argv;
+{
+ int value;
+
+ if (!int_option(*argv, &value))
+ return 0;
+ if (value < 0) {
+ option_error("maxconnect time must be positive");
+ return 0;
+ }
+ if (maxconnect > 0 && (value == 0 || value > maxconnect)) {
+ option_error("maxconnect time cannot be increased");
+ return 0;
+ }
+ maxconnect = value;
+ return 1;
+}
+
+/*
+ * setdomain - Set domain name to append to hostname
+ */
+static int
+setdomain(argv)
+ char **argv;
+{
+ if (!privileged_option) {
+ option_error("using the domain option requires root privilege");
+ return 0;
+ }
+ gethostname(hostname, MAXNAMELEN);
+ if (**argv != 0) {
+ if (**argv != '.')
+ strncat(hostname, ".", MAXNAMELEN - strlen(hostname));
+ strncat(hostname, *argv, MAXNAMELEN - strlen(hostname));
+ }
+ hostname[MAXNAMELEN-1] = 0;
+ return (1);
+}
+
+
+/*
+ * setasyncmap - add bits to asyncmap (what we request peer to escape).
+ */
+static int
+setasyncmap(argv)
+ char **argv;
+{
+ u_int32_t asyncmap;
+
+ if (!number_option(*argv, &asyncmap, 16))
+ return 0;
+ lcp_wantoptions[0].asyncmap |= asyncmap;
+ lcp_wantoptions[0].neg_asyncmap = 1;
+ return(1);
+}
+
+
+/*
+ * setescape - add chars to the set we escape on transmission.
+ */
+static int
+setescape(argv)
+ char **argv;
+{
+ int n, ret;
+ char *p, *endp;
+
+ p = *argv;
+ ret = 1;
+ while (*p) {
+ n = strtol(p, &endp, 16);
+ if (p == endp) {
+ option_error("escape parameter contains invalid hex number '%s'",
+ p);
+ return 0;
+ }
+ p = endp;
+ if (n < 0 || (0x20 <= n && n <= 0x3F) || n == 0x5E || n > 0xFF) {
+ option_error("can't escape character 0x%x", n);
+ ret = 0;
+ } else
+ xmit_accm[0][n >> 5] |= 1 << (n & 0x1F);
+ while (*p == ',' || *p == ' ')
+ ++p;
+ }
+ return ret;
+}
+
+
+/*
+ * setspeed - Set the speed.
+ */
+static int
+setspeed(arg)
+ char *arg;
+{
+ char *ptr;
+ int spd;
+
+ spd = strtol(arg, &ptr, 0);
+ if (ptr == arg || *ptr != 0 || spd == 0)
+ return 0;
+ inspeed = spd;
+ return 1;
+}
+
+
+/*
+ * setdevname - Set the device name.
+ */
+static int
+setdevname(cp, quiet)
+ char *cp;
+ int quiet;
+{
+ struct stat statbuf;
+ char dev[MAXPATHLEN];
+
+ if (*cp == 0)
+ return 0;
+
+ if (strncmp(_PATH_DEV, cp, sizeof _PATH_DEV - 1) != 0) {
+ strcpy(dev, _PATH_DEV);
+ strncat(dev, cp, MAXPATHLEN - sizeof _PATH_DEV - 1);
+ dev[MAXPATHLEN-1] = 0;
+ cp = dev;
+ }
+
+ /*
+ * Check if there is a device by this name.
+ */
+ if (stat(cp, &statbuf) < 0) {
+ if (errno == ENOENT || quiet)
+ return 0;
+ option_error("Couldn't stat %s: %m", cp);
+ return -1;
+ }
+
+ (void) strncpy(devnam, cp, MAXPATHLEN);
+ devnam[MAXPATHLEN-1] = 0;
+ default_device = FALSE;
+ devnam_info.priv = privileged_option;
+ devnam_info.source = option_source;
+
+ return 1;
+}
+
+
+/*
+ * setipaddr - Set the IP address
+ */
+int
+setipaddr(arg)
+ char *arg;
+{
+ struct hostent *hp;
+ char *colon;
+ u_int32_t local, remote;
+ ipcp_options *wo = &ipcp_wantoptions[0];
+
+ /*
+ * IP address pair separated by ":".
+ */
+ if ((colon = strchr(arg, ':')) == NULL)
+ return 0;
+
+ /*
+ * If colon first character, then no local addr.
+ */
+ if (colon != arg) {
+ *colon = '\0';
+ if ((local = inet_addr(arg)) == -1) {
+ if ((hp = gethostbyname(arg)) == NULL) {
+ option_error("unknown host: %s", arg);
+ return -1;
+ } else {
+ local = *(u_int32_t *)hp->h_addr;
+ }
+ }
+ if (bad_ip_adrs(local)) {
+ option_error("bad local IP address %s", ip_ntoa(local));
+ return -1;
+ }
+ if (local != 0)
+ wo->ouraddr = local;
+ *colon = ':';
+ }
+
+ /*
+ * If colon last character, then no remote addr.
+ */
+ if (*++colon != '\0') {
+ if ((remote = inet_addr(colon)) == -1) {
+ if ((hp = gethostbyname(colon)) == NULL) {
+ option_error("unknown host: %s", colon);
+ return -1;
+ } else {
+ remote = *(u_int32_t *)hp->h_addr;
+ if (remote_name[0] == 0) {
+ strncpy(remote_name, colon, MAXNAMELEN);
+ remote_name[MAXNAMELEN-1] = 0;
+ }
+ }
+ }
+ if (bad_ip_adrs(remote)) {
+ option_error("bad remote IP address %s", ip_ntoa(remote));
+ return -1;
+ }
+ if (remote != 0)
+ wo->hisaddr = remote;
+ }
+
+ return 1;
+}
+
+
+/*
+ * setnoipdflt - disable setipdefault()
+ */
+static int
+setnoipdflt(argv)
+ char **argv;
+{
+ disable_defaultip = 1;
+ return 1;
+}
+
+
+/*
+ * setipcpaccl - accept peer's idea of our address
+ */
+static int
+setipcpaccl(argv)
+ char **argv;
+{
+ ipcp_wantoptions[0].accept_local = 1;
+ return 1;
+}
+
+
+/*
+ * setipcpaccr - accept peer's idea of its address
+ */
+static int
+setipcpaccr(argv)
+ char **argv;
+{
+ ipcp_wantoptions[0].accept_remote = 1;
+ return 1;
+}
+
+
+/*
+ * setnetmask - set the netmask to be used on the interface.
+ */
+static int
+setnetmask(argv)
+ char **argv;
+{
+ struct in_addr mask;
+
+ if (!inet_aton(*argv, &mask) || (netmask & ~mask.s_addr)) {
+ fprintf(stderr, "Invalid netmask %s\n", *argv);
+ return (0);
+ }
+
+ netmask = mask.s_addr;
+ return (1);
+}
+
+static int
+setcrtscts(argv)
+ char **argv;
+{
+ crtscts = 1;
+ return (1);
+}
+
+static int
+setnocrtscts(argv)
+ char **argv;
+{
+ crtscts = -1;
+ return (1);
+}
+
+static int
+setxonxoff(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].asyncmap |= 0x000A0000; /* escape ^S and ^Q */
+ lcp_wantoptions[0].neg_asyncmap = 1;
+
+ crtscts = -2;
+ return (1);
+}
+
+static int
+setnodetach(argv)
+ char **argv;
+{
+ nodetach = 1;
+ return (1);
+}
+
+static int
+setupdetach(argv)
+ char **argv;
+{
+ nodetach = -1;
+ return (1);
+}
+
+static int
+setdemand(argv)
+ char **argv;
+{
+ demand = 1;
+ persist = 1;
+ return 1;
+}
+
+static int
+setmodem(argv)
+ char **argv;
+{
+ modem = 1;
+ return 1;
+}
+
+static int
+setlocal(argv)
+ char **argv;
+{
+ modem = 0;
+ return 1;
+}
+
+static int
+setlock(argv)
+ char **argv;
+{
+ lockflag = 1;
+ return 1;
+}
+
+static int
+setusehostname(argv)
+ char **argv;
+{
+ usehostname = 1;
+ return 1;
+}
+
+static int
+setname(argv)
+ char **argv;
+{
+ if (!privileged_option) {
+ option_error("using the name option requires root privilege");
+ return 0;
+ }
+ strncpy(our_name, argv[0], MAXNAMELEN);
+ our_name[MAXNAMELEN-1] = 0;
+ return 1;
+}
+
+static int
+setuser(argv)
+ char **argv;
+{
+ strncpy(user, argv[0], MAXNAMELEN);
+ user[MAXNAMELEN-1] = 0;
+ return 1;
+}
+
+static int
+setremote(argv)
+ char **argv;
+{
+ strncpy(remote_name, argv[0], MAXNAMELEN);
+ remote_name[MAXNAMELEN-1] = 0;
+ return 1;
+}
+
+static int
+setauth(argv)
+ char **argv;
+{
+ auth_required = 1;
+ if (privileged_option > auth_req_info.priv) {
+ auth_req_info.priv = privileged_option;
+ auth_req_info.source = option_source;
+ }
+ return 1;
+}
+
+static int
+setnoauth(argv)
+ char **argv;
+{
+ if (auth_required && privileged_option < auth_req_info.priv) {
+ option_error("cannot override auth option set by %s",
+ auth_req_info.source);
+ return 0;
+ }
+ auth_required = 0;
+ return 1;
+}
+
+static int
+setdefaultroute(argv)
+ char **argv;
+{
+ if (!ipcp_allowoptions[0].default_route) {
+ option_error("defaultroute option is disabled");
+ return 0;
+ }
+ ipcp_wantoptions[0].default_route = 1;
+ return 1;
+}
+
+static int
+setnodefaultroute(argv)
+ char **argv;
+{
+ ipcp_allowoptions[0].default_route = 0;
+ ipcp_wantoptions[0].default_route = 0;
+ return 1;
+}
+
+static int
+setproxyarp(argv)
+ char **argv;
+{
+ if (!ipcp_allowoptions[0].proxy_arp) {
+ option_error("proxyarp option is disabled");
+ return 0;
+ }
+ ipcp_wantoptions[0].proxy_arp = 1;
+ return 1;
+}
+
+static int
+setnoproxyarp(argv)
+ char **argv;
+{
+ ipcp_wantoptions[0].proxy_arp = 0;
+ ipcp_allowoptions[0].proxy_arp = 0;
+ return 1;
+}
+
+static int
+setpersist(argv)
+ char **argv;
+{
+ persist = 1;
+ return 1;
+}
+
+static int
+setnopersist(argv)
+ char **argv;
+{
+ persist = 0;
+ return 1;
+}
+
+static int
+setdologin(argv)
+ char **argv;
+{
+ uselogin = 1;
+ return 1;
+}
+
+/*
+ * Functions to set the echo interval for modem-less monitors
+ */
+
+static int
+setlcpechointv(argv)
+ char **argv;
+{
+ return int_option(*argv, &lcp_echo_interval);
+}
+
+static int
+setlcpechofails(argv)
+ char **argv;
+{
+ return int_option(*argv, &lcp_echo_fails);
+}
+
+/*
+ * Functions to set timeouts, max transmits, etc.
+ */
+static int
+setlcptimeout(argv)
+ char **argv;
+{
+ return int_option(*argv, &lcp_fsm[0].timeouttime);
+}
+
+static int
+setlcpterm(argv)
+ char **argv;
+{
+ return int_option(*argv, &lcp_fsm[0].maxtermtransmits);
+}
+
+static int
+setlcpconf(argv)
+ char **argv;
+{
+ return int_option(*argv, &lcp_fsm[0].maxconfreqtransmits);
+}
+
+static int
+setlcpfails(argv)
+ char **argv;
+{
+ return int_option(*argv, &lcp_fsm[0].maxnakloops);
+}
+
+static int
+setipcptimeout(argv)
+ char **argv;
+{
+ return int_option(*argv, &ipcp_fsm[0].timeouttime);
+}
+
+static int
+setipcpterm(argv)
+ char **argv;
+{
+ return int_option(*argv, &ipcp_fsm[0].maxtermtransmits);
+}
+
+static int
+setipcpconf(argv)
+ char **argv;
+{
+ return int_option(*argv, &ipcp_fsm[0].maxconfreqtransmits);
+}
+
+static int
+setipcpfails(argv)
+ char **argv;
+{
+ return int_option(*argv, &lcp_fsm[0].maxnakloops);
+}
+
+static int
+setpaptimeout(argv)
+ char **argv;
+{
+ return int_option(*argv, &upap[0].us_timeouttime);
+}
+
+static int
+setpapreqtime(argv)
+ char **argv;
+{
+ return int_option(*argv, &upap[0].us_reqtimeout);
+}
+
+static int
+setpapreqs(argv)
+ char **argv;
+{
+ return int_option(*argv, &upap[0].us_maxtransmits);
+}
+
+static int
+setchaptimeout(argv)
+ char **argv;
+{
+ return int_option(*argv, &chap[0].timeouttime);
+}
+
+static int
+setchapchal(argv)
+ char **argv;
+{
+ return int_option(*argv, &chap[0].max_transmits);
+}
+
+static int
+setchapintv(argv)
+ char **argv;
+{
+ return int_option(*argv, &chap[0].chal_interval);
+}
+
+static int
+noccp(argv)
+ char **argv;
+{
+ ccp_protent.enabled_flag = 0;
+ return 1;
+}
+
+static int
+setbsdcomp(argv)
+ char **argv;
+{
+ int rbits, abits;
+ char *str, *endp;
+
+ str = *argv;
+ abits = rbits = strtol(str, &endp, 0);
+ if (endp != str && *endp == ',') {
+ str = endp + 1;
+ abits = strtol(str, &endp, 0);
+ }
+ if (*endp != 0 || endp == str) {
+ option_error("invalid parameter '%s' for bsdcomp option", *argv);
+ return 0;
+ }
+ if ((rbits != 0 && (rbits < BSD_MIN_BITS || rbits > BSD_MAX_BITS))
+ || (abits != 0 && (abits < BSD_MIN_BITS || abits > BSD_MAX_BITS))) {
+ option_error("bsdcomp option values must be 0 or %d .. %d",
+ BSD_MIN_BITS, BSD_MAX_BITS);
+ return 0;
+ }
+ if (rbits > 0) {
+ ccp_wantoptions[0].bsd_compress = 1;
+ ccp_wantoptions[0].bsd_bits = rbits;
+ } else
+ ccp_wantoptions[0].bsd_compress = 0;
+ if (abits > 0) {
+ ccp_allowoptions[0].bsd_compress = 1;
+ ccp_allowoptions[0].bsd_bits = abits;
+ } else
+ ccp_allowoptions[0].bsd_compress = 0;
+ return 1;
+}
+
+static int
+setnobsdcomp(argv)
+ char **argv;
+{
+ ccp_wantoptions[0].bsd_compress = 0;
+ ccp_allowoptions[0].bsd_compress = 0;
+ return 1;
+}
+
+static int
+setdeflate(argv)
+ char **argv;
+{
+ int rbits, abits;
+ char *str, *endp;
+
+ str = *argv;
+ abits = rbits = strtol(str, &endp, 0);
+ if (endp != str && *endp == ',') {
+ str = endp + 1;
+ abits = strtol(str, &endp, 0);
+ }
+ if (*endp != 0 || endp == str) {
+ option_error("invalid parameter '%s' for deflate option", *argv);
+ return 0;
+ }
+ if ((rbits != 0 && (rbits < DEFLATE_MIN_SIZE || rbits > DEFLATE_MAX_SIZE))
+ || (abits != 0 && (abits < DEFLATE_MIN_SIZE
+ || abits > DEFLATE_MAX_SIZE))) {
+ option_error("deflate option values must be 0 or %d .. %d",
+ DEFLATE_MIN_SIZE, DEFLATE_MAX_SIZE);
+ return 0;
+ }
+ if (rbits > 0) {
+ ccp_wantoptions[0].deflate = 1;
+ ccp_wantoptions[0].deflate_size = rbits;
+ } else
+ ccp_wantoptions[0].deflate = 0;
+ if (abits > 0) {
+ ccp_allowoptions[0].deflate = 1;
+ ccp_allowoptions[0].deflate_size = abits;
+ } else
+ ccp_allowoptions[0].deflate = 0;
+
+ /* XXX copy over settings for switch compatibility */
+ ccp_wantoptions[0].baddeflate = ccp_wantoptions[0].deflate;
+ ccp_wantoptions[0].baddeflate_size = ccp_wantoptions[0].deflate_size;
+ ccp_allowoptions[0].baddeflate = ccp_allowoptions[0].deflate;
+ ccp_allowoptions[0].baddeflate_size = ccp_allowoptions[0].deflate_size;
+
+ return 1;
+}
+
+static int
+setnodeflate(argv)
+ char **argv;
+{
+ ccp_wantoptions[0].deflate = 0;
+ ccp_allowoptions[0].deflate = 0;
+ return 1;
+}
+
+static int
+setnodeflatedraft(argv)
+ char **argv;
+{
+ ccp_wantoptions[0].deflate_draft = 0;
+ ccp_allowoptions[0].deflate_draft = 0;
+ return 1;
+}
+
+static int
+setpred1comp(argv)
+ char **argv;
+{
+ ccp_wantoptions[0].predictor_1 = 1;
+ ccp_allowoptions[0].predictor_1 = 1;
+ return 1;
+}
+
+static int
+setnopred1comp(argv)
+ char **argv;
+{
+ ccp_wantoptions[0].predictor_1 = 0;
+ ccp_allowoptions[0].predictor_1 = 0;
+ return 1;
+}
+
+static int
+setipparam(argv)
+ char **argv;
+{
+ ipparam = strdup(*argv);
+ if (ipparam == NULL)
+ novm("ipparam string");
+
+ return 1;
+}
+
+static int
+setpapcrypt(argv)
+ char **argv;
+{
+ cryptpap = 1;
+ return 1;
+}
+
+static int
+setidle(argv)
+ char **argv;
+{
+ return int_option(*argv, &idle_time_limit);
+}
+
+static int
+setholdoff(argv)
+ char **argv;
+{
+ return int_option(*argv, &holdoff);
+}
+
+/*
+ * setdnsaddr - set the dns address(es)
+ */
+static int
+setdnsaddr(argv)
+ char **argv;
+{
+ u_int32_t dns;
+ struct hostent *hp;
+
+ dns = inet_addr(*argv);
+ if (dns == -1) {
+ if ((hp = gethostbyname(*argv)) == NULL) {
+ option_error("invalid address parameter '%s' for ms-dns option",
+ *argv);
+ return 0;
+ }
+ dns = *(u_int32_t *)hp->h_addr;
+ }
+
+ /* if there is no primary then update it. */
+ if (ipcp_allowoptions[0].dnsaddr[0] == 0)
+ ipcp_allowoptions[0].dnsaddr[0] = dns;
+
+ /* always set the secondary address value to the same value. */
+ ipcp_allowoptions[0].dnsaddr[1] = dns;
+
+ return (1);
+}
+
+/*
+ * setwinsaddr - set the wins address(es)
+ * This is primrarly used with the Samba package under UNIX or for pointing
+ * the caller to the existing WINS server on a Windows NT platform.
+ */
+static int
+setwinsaddr(argv)
+ char **argv;
+{
+ u_int32_t wins;
+ struct hostent *hp;
+
+ wins = inet_addr(*argv);
+ if (wins == -1) {
+ if ((hp = gethostbyname(*argv)) == NULL) {
+ option_error("invalid address parameter '%s' for ms-wins option",
+ *argv);
+ return 0;
+ }
+ wins = *(u_int32_t *)hp->h_addr;
+ }
+
+ /* if there is no primary then update it. */
+ if (ipcp_allowoptions[0].winsaddr[0] == 0)
+ ipcp_allowoptions[0].winsaddr[0] = wins;
+
+ /* always set the secondary address value to the same value. */
+ ipcp_allowoptions[0].winsaddr[1] = wins;
+
+ return (1);
+}
+
+#ifdef INET6
+static int
+setipv6cp_accept_local(argv)
+ char **argv;
+{
+ ipv6cp_allowoptions[0].accept_local = 1;
+ return 1;
+}
+
+static int
+setipv6cp_use_ip(argv)
+ char **argv;
+{
+ ipv6cp_allowoptions[0].use_ip = 1;
+ return 1;
+}
+
+#if defined(SOL2)
+static int
+setipv6cp_use_persistent(argv)
+ char **argv;
+{
+ ipv6cp_wantoptions[0].use_persistent = 1;
+ return 1;
+}
+#endif
+
+static int
+setipv6cptimeout(argv)
+ char **argv;
+{
+ return int_option(*argv, &ipv6cp_fsm[0].timeouttime);
+}
+
+static int
+setipv6cpterm(argv)
+ char **argv;
+{
+ return int_option(*argv, &ipv6cp_fsm[0].maxtermtransmits);
+}
+
+static int
+setipv6cpconf(argv)
+ char **argv;
+{
+ return int_option(*argv, &ipv6cp_fsm[0].maxconfreqtransmits);
+}
+
+static int
+setipv6cpfails(argv)
+ char **argv;
+{
+ return int_option(*argv, &ipv6cp_fsm[0].maxnakloops);
+}
+
+static int
+setipv6proto(argv)
+ char **argv;
+{
+ ipv6cp_protent.enabled_flag = 1;
+ return 1;
+}
+
+static int
+resetipv6proto(argv)
+ char **argv;
+{
+ ipv6cp_protent.enabled_flag = 0;
+ return 1;
+}
+#else
+static int
+resetipv6proto(argv)
+ char **argv;
+{
+ return 1;
+}
+#endif /* INET6 */
+
+#ifdef IPX_CHANGE
+static int
+setipxrouter (argv)
+ char **argv;
+{
+ ipxcp_wantoptions[0].neg_router = 1;
+ ipxcp_allowoptions[0].neg_router = 1;
+ return int_option(*argv, &ipxcp_wantoptions[0].router);
+}
+
+static int
+setipxname (argv)
+ char **argv;
+{
+ char *dest = ipxcp_wantoptions[0].name;
+ char *src = *argv;
+ int count;
+ char ch;
+
+ ipxcp_wantoptions[0].neg_name = 1;
+ ipxcp_allowoptions[0].neg_name = 1;
+ memset (dest, '\0', sizeof (ipxcp_wantoptions[0].name));
+
+ count = 0;
+ while (*src) {
+ ch = *src++;
+ if (! isalnum (ch) && ch != '_') {
+ option_error("IPX router name must be alphanumeric or _");
+ return 0;
+ }
+
+ if (count >= sizeof (ipxcp_wantoptions[0].name)) {
+ option_error("IPX router name is limited to %d characters",
+ sizeof (ipxcp_wantoptions[0].name) - 1);
+ return 0;
+ }
+
+ dest[count++] = toupper (ch);
+ }
+
+ return 1;
+}
+
+static int
+setipxcptimeout (argv)
+ char **argv;
+{
+ return int_option(*argv, &ipxcp_fsm[0].timeouttime);
+}
+
+static int
+setipxcpterm (argv)
+ char **argv;
+{
+ return int_option(*argv, &ipxcp_fsm[0].maxtermtransmits);
+}
+
+static int
+setipxcpconf (argv)
+ char **argv;
+{
+ return int_option(*argv, &ipxcp_fsm[0].maxconfreqtransmits);
+}
+
+static int
+setipxcpfails (argv)
+ char **argv;
+{
+ return int_option(*argv, &ipxcp_fsm[0].maxnakloops);
+}
+
+static int
+setipxnetwork(argv)
+ char **argv;
+{
+ u_int32_t v;
+
+ if (!number_option(*argv, &v, 16))
+ return 0;
+
+ ipxcp_wantoptions[0].our_network = (int) v;
+ ipxcp_wantoptions[0].neg_nn = 1;
+ return 1;
+}
+
+static int
+setipxanet(argv)
+ char **argv;
+{
+ ipxcp_wantoptions[0].accept_network = 1;
+ ipxcp_allowoptions[0].accept_network = 1;
+ return 1;
+}
+
+static int
+setipxalcl(argv)
+ char **argv;
+{
+ ipxcp_wantoptions[0].accept_local = 1;
+ ipxcp_allowoptions[0].accept_local = 1;
+ return 1;
+}
+
+static int
+setipxarmt(argv)
+ char **argv;
+{
+ ipxcp_wantoptions[0].accept_remote = 1;
+ ipxcp_allowoptions[0].accept_remote = 1;
+ return 1;
+}
+
+static u_char *
+setipxnodevalue(src,dst)
+u_char *src, *dst;
+{
+ int indx;
+ int item;
+
+ for (;;) {
+ if (!isxdigit (*src))
+ break;
+
+ for (indx = 0; indx < 5; ++indx) {
+ dst[indx] <<= 4;
+ dst[indx] |= (dst[indx + 1] >> 4) & 0x0F;
+ }
+
+ item = toupper (*src) - '0';
+ if (item > 9)
+ item -= 7;
+
+ dst[5] = (dst[5] << 4) | item;
+ ++src;
+ }
+ return src;
+}
+
+static int
+setipxnode(argv)
+ char **argv;
+{
+ char *end;
+
+ memset (&ipxcp_wantoptions[0].our_node[0], 0, 6);
+ memset (&ipxcp_wantoptions[0].his_node[0], 0, 6);
+
+ end = setipxnodevalue (*argv, &ipxcp_wantoptions[0].our_node[0]);
+ if (*end == ':')
+ end = setipxnodevalue (++end, &ipxcp_wantoptions[0].his_node[0]);
+
+ if (*end == '\0') {
+ ipxcp_wantoptions[0].neg_node = 1;
+ return 1;
+ }
+
+ option_error("invalid parameter '%s' for ipx-node option", *argv);
+ return 0;
+}
+
+static int
+setipxproto(argv)
+ char **argv;
+{
+ ipxcp_protent.enabled_flag = 1;
+ return 1;
+}
+
+static int
+resetipxproto(argv)
+ char **argv;
+{
+ ipxcp_protent.enabled_flag = 0;
+ return 1;
+}
+#else
+
+static int
+resetipxproto(argv)
+ char **argv;
+{
+ return 1;
+}
+#endif /* IPX_CHANGE */
+
+#ifdef MSLANMAN
+static int
+setmslanman(argv)
+ char **argv;
+{
+ ms_lanman = 1;
+ return (1);
+}
+#endif
diff --git a/usr.sbin/pppd/patchlevel.h b/usr.sbin/pppd/patchlevel.h
new file mode 100644
index 0000000..bc49c58
--- /dev/null
+++ b/usr.sbin/pppd/patchlevel.h
@@ -0,0 +1,6 @@
+/* $FreeBSD$ */
+#define PATCHLEVEL 5
+
+#define VERSION "2.3"
+#define IMPLEMENTATION ""
+#define DATE "4 May 1998"
diff --git a/usr.sbin/pppd/pathnames.h b/usr.sbin/pppd/pathnames.h
new file mode 100644
index 0000000..5d09b0b
--- /dev/null
+++ b/usr.sbin/pppd/pathnames.h
@@ -0,0 +1,37 @@
+/*
+ * define path names
+ *
+ * $FreeBSD$
+ */
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+
+#else
+#define _PATH_VARRUN "/etc/ppp/"
+#define _PATH_DEVNULL "/dev/null"
+#endif
+
+#define _PATH_UPAPFILE "/etc/ppp/pap-secrets"
+#define _PATH_CHAPFILE "/etc/ppp/chap-secrets"
+#define _PATH_SYSOPTIONS "/etc/ppp/options"
+#define _PATH_IPUP "/etc/ppp/ip-up"
+#define _PATH_IPDOWN "/etc/ppp/ip-down"
+#define _PATH_AUTHUP "/etc/ppp/auth-up"
+#define _PATH_AUTHDOWN "/etc/ppp/auth-down"
+#define _PATH_TTYOPT "/etc/ppp/options."
+#define _PATH_CONNERRS "/var/log/connect-errors"
+#define _PATH_USEROPT ".ppprc"
+#define _PATH_PEERFILES "/etc/ppp/peers/"
+#define _PATH_PPPDENY "/etc/ppp/ppp.deny"
+#define _PATH_PPPSHELLS "/etc/ppp/ppp.shells"
+
+#ifdef INET6
+#define _PATH_IPV6UP "/etc/ppp/ipv6-up"
+#define _PATH_IPV6DOWN "/etc/ppp/ipv6-down"
+#endif
+
+#ifdef IPX_CHANGE
+#define _PATH_IPXUP "/etc/ppp/ipx-up"
+#define _PATH_IPXDOWN "/etc/ppp/ipx-down"
+#endif /* IPX_CHANGE */
diff --git a/usr.sbin/pppd/pppd.8 b/usr.sbin/pppd/pppd.8
new file mode 100644
index 0000000..ba55f21
--- /dev/null
+++ b/usr.sbin/pppd/pppd.8
@@ -0,0 +1,1247 @@
+.\" manual page [] for pppd 2.3
+.\" $FreeBSD$
+.\" SH section heading
+.\" SS subsection heading
+.\" LP paragraph
+.\" IP indented paragraph
+.\" TP hanging label
+.TH PPPD 8
+.SH NAME
+pppd \- Point to Point Protocol daemon
+.SH SYNOPSIS
+.B pppd
+[
+.I tty_name
+] [
+.I speed
+] [
+.I options
+]
+.SH DESCRIPTION
+.LP
+The Point-to-Point Protocol (PPP) provides a method for transmitting
+datagrams over serial point-to-point links. PPP
+is composed of three parts: a method for encapsulating datagrams over
+serial links, an extensible Link Control Protocol (LCP), and
+a family of Network Control Protocols (NCP) for establishing
+and configuring different network-layer protocols.
+.LP
+The encapsulation scheme is provided by driver code in the kernel.
+Pppd provides the basic LCP, authentication support, and an NCP for
+establishing and configuring the Internet Protocol (IP) (called the IP
+Control Protocol, IPCP).
+.SH FREQUENTLY USED OPTIONS
+.TP
+.I <tty_name>
+Communicate over the named device. The string "/dev/" is prepended if
+necessary. If no device name is given, or if the name of the terminal
+connected to the standard input is given, pppd
+will use that terminal, and will not fork to put itself in the
+background. This option is privileged if the \fInoauth\fR option is
+used.
+.TP
+.I <speed>
+Set the baud rate to <speed> (a decimal number). On systems such as
+4.4BSD and NetBSD, any speed can be specified, providing that it is
+supported by the serial device driver. Other systems
+(e.g. SunOS, Linux) allow only a limited set of speeds.
+.TP
+.B active-filter \fIfilter-expression
+Specifies a packet filter to be applied to data packets to determine
+which packets are to be regarded as link activity, and therefore reset
+the idle timer, or cause the link to be brought up in demand-dialling
+mode. This option is useful in conjunction with the
+\fBidle\fR option if there are packets being sent or received
+regularly over the link (for example, routing information packets)
+which would otherwise prevent the link from ever appearing to be idle.
+The \fIfilter-expression\fR syntax is as described for tcpdump(1),
+except that qualifiers which are inappropriate for a PPP link, such as
+\fBether\fR and \fBarp\fR, are not permitted. Generally the filter
+expression should be enclosed in single-quotes to prevent whitespace
+in the expression from being interpreted by the shell.
+This option
+only available
+if both the kernel and pppd were compiled with PPP_FILTER defined.
+.TP
+.B asyncmap \fI<map>
+Set the async character map to <map>. This map describes which
+control characters cannot be successfully received over the serial
+line. Pppd will ask the peer to send these characters as a 2-byte
+escape sequence. The argument is a 32 bit hex number with each bit
+representing a character to escape. Bit 0 (00000001) represents the
+character 0x00; bit 31 (80000000) represents the character 0x1f or ^_.
+If multiple \fIasyncmap\fR options are given, the values are ORed
+together. If no \fIasyncmap\fR option is given, no async character
+map will be negotiated for the receive direction; the peer should then
+escape \fIall\fR control characters. To escape transmitted
+characters, use the \fIescape\fR option.
+.TP
+.B auth
+Require the peer to authenticate itself before allowing network
+packets to be sent or received.
+.TP
+.B call \fIname
+Read options from the file /etc/ppp/peers/\fIname\fR. This file may
+contain privileged options, such as \fInoauth\fR, even if pppd
+is not being run by root. The \fIname\fR string may not begin with /
+or include .. as a pathname component. The format of the options file
+is described below.
+.TP
+.B connect \fIscript
+Use the executable or shell command specified by \fIscript\fR to set
+up the serial line. This script would typically use the chat(8)
+program to dial the modem and start the remote ppp session. This
+option is privileged if the \fInoauth\fR option is used.
+.TP
+.B connect-max-attempts \fI<n>
+Attempt dial-out connection to remote system no more than specified number
+of times (default = 1). If the connection is not made, pppd will exit.
+Requires that \fBpersist\fR has been specified.
+.TP
+.B crtscts
+Use hardware flow control (i.e. RTS/CTS) to control the flow of data
+on the serial port. If neither the \fIcrtscts\fR nor the
+\fInocrtscts\fR option is given, the hardware flow control setting
+for the serial port is left unchanged.
+.TP
+.B defaultroute
+Add a default route to the system routing tables, using the peer as
+the gateway, when IPCP negotiation is successfully completed.
+This entry is removed when the PPP connection is broken. This option
+is privileged if the \fInodefaultroute\fR option has been specified.
+.TP
+.B disconnect \fIscript
+Run the executable or shell command specified by \fIscript\fR after
+pppd has terminated the link. This script could, for example, issue
+commands to the modem to cause it to hang up if hardware modem control
+signals were not available. The disconnect script is not run if the
+modem has already hung up. This option is privileged if the
+\fInoauth\fR option is used.
+.TP
+.B escape \fIxx,yy,...
+Specifies that certain characters should be escaped on transmission
+(regardless of whether the peer requests them to be escaped with its
+async control character map). The characters to be escaped are
+specified as a list of hex numbers separated by commas. Note that
+almost any character can be specified for the \fIescape\fR option,
+unlike the \fIasyncmap\fR option which only allows control characters
+to be specified. The characters which may not be escaped are those
+with hex values 0x20 - 0x3f or 0x5e.
+.TP
+.B file \fIname
+Read options from file \fIname\fR (the format is described below).
+The file must be readable by the user who has invoked pppd.
+.TP
+.B lock
+Specifies that pppd should create a UUCP-style lock file for the
+serial device to ensure exclusive access to the device.
+.TP
+.B mru \fIn
+Set the MRU [Maximum Receive Unit] value to \fIn\fR.
+Pppd
+will ask the peer to send packets of no more than \fIn\fR bytes. The
+minimum MRU value is 128. The default MRU value is 1500. A value of
+296 is recommended for slow links (40 bytes for TCP/IP header + 256
+bytes of data). (Note that for IPv6 MRU must be at least 1280)
+.TP
+.B mtu \fIn
+Set the MTU [Maximum Transmit Unit] value to \fIn\fR. Unless the
+peer requests a smaller value via MRU negotiation, pppd will
+request that the kernel networking code send data packets of no more
+than \fIn\fR bytes through the PPP network interface. (Note that for
+IPv6 MTU must be at least 1280)
+.TP
+.B passive
+Enables the "passive" option in the LCP. With this option, pppd will
+attempt to initiate a connection; if no reply is received from the
+peer, pppd will then just wait passively for a valid LCP packet from
+the peer, instead of exiting, as it would without this option.
+.SH OPTIONS
+.TP
+.I <local_IP_address>\fB:\fI<remote_IP_address>
+Set the local and/or remote interface IP addresses. Either one may be
+omitted. The IP addresses can be specified with a host name or in
+decimal dot notation (e.g. 150.234.56.78). The default local
+address is the (first) IP address of the system (unless the
+\fInoipdefault\fR
+option is given). The remote address will be obtained from the peer
+if not specified in any option. Thus, in simple cases, this option is
+not required. If a local and/or remote IP address is specified with
+this option, pppd
+will not accept a different value from the peer in the IPCP
+negotiation, unless the \fIipcp-accept-local\fR and/or
+\fIipcp-accept-remote\fR options are given, respectively.
+.TP
+.B ipv6 \fI<local_interface_identifier>\fR,\fI<remote_interface_identifier>
+Set the local and/or remote 64-bit interface identifier. Either one may be
+omitted. The identifier must be specified in standard ascii notation of
+IPv6 addresses (e.g. ::dead:beef). If the
+\fIipv6cp-use-ipaddr\fR
+option is given, the local identifier is the local IPv4 address (see above).
+On systems which supports a unique persistent id, such as EUI-48 derived
+from the Ethernet MAC address, \fIipv6cp-use-persistent\fR option can be
+used to replace the \fIipv6 <local>,<remote>\fR option. Otherwise the
+identifier is randomized.
+.TP
+.B bsdcomp \fInr,nt
+Request that the peer compress packets that it sends, using the
+BSD-Compress scheme, with a maximum code size of \fInr\fR bits, and
+agree to compress packets sent to the peer with a maximum code size of
+\fInt\fR bits. If \fInt\fR is not specified, it defaults to the value
+given for \fInr\fR. Values in the range 9 to 15 may be used for
+\fInr\fR and \fInt\fR; larger values give better compression but
+consume more kernel memory for compression dictionaries.
+Alternatively, a value of 0 for \fInr\fR or \fInt\fR disables
+compression in the corresponding direction. Use \fInobsdcomp\fR or
+\fIbsdcomp 0\fR to disable BSD-Compress compression entirely.
+.TP
+.B callback \fIphone_number
+Request a call-back to the \fIphone_number\fR. This only works if the peer
+is speaking the Call Back Configuration Protocol. Do not put this into the
+main options file if you sometimes connect to servers that don't support
+it.
+.TP
+.B chap-interval \fIn
+If this option is given, pppd will rechallenge the peer every \fIn\fR
+seconds.
+.TP
+.B chap-max-challenge \fIn
+Set the maximum number of CHAP challenge transmissions to \fIn\fR
+(default 10).
+.TP
+.B chap-restart \fIn
+Set the CHAP restart interval (retransmission timeout for challenges)
+to \fIn\fR seconds (default 3).
+.TP
+.B debug
+Enables connection debugging facilities.
+If this option is given, pppd will log the contents of all
+control packets sent or received in a readable form. The packets are
+logged through syslog with facility \fIdaemon\fR and level
+\fIdebug\fR. This information can be directed to a file by setting up
+/etc/syslog.conf appropriately (see syslog.conf(5)).
+.TP
+.B default-asyncmap
+Disable asyncmap negotiation, forcing all control characters to be
+escaped for both the transmit and the receive direction.
+.TP
+.B default-mru
+Disable MRU [Maximum Receive Unit] negotiation. With this option,
+pppd will use the default MRU value of 1500 bytes for both the
+transmit and receive direction.
+.TP
+.B deflate \fInr,nt
+Request that the peer compress packets that it sends, using the
+Deflate scheme, with a maximum window size of \fI2**nr\fR bytes, and
+agree to compress packets sent to the peer with a maximum window size
+of \fI2**nt\fR bytes. If \fInt\fR is not specified, it defaults to
+the value given for \fInr\fR. Values in the range 8 to 15 may be used
+for \fInr\fR and \fInt\fR; larger values give better compression but
+consume more kernel memory for compression dictionaries.
+Alternatively, a value of 0 for \fInr\fR or \fInt\fR disables
+compression in the corresponding direction. Use \fInodeflate\fR or
+\fIdeflate 0\fR to disable Deflate compression entirely. (Note: pppd
+requests Deflate compression in preference to BSD-Compress if the peer
+can do either.)
+.TP
+.B demand
+Initiate the link only on demand, i.e. when data traffic is present.
+With this option, the remote IP address must be specified by the user
+on the command line or in an options file. Pppd will initially
+configure the interface and enable it for IP traffic without
+connecting to the peer. When traffic is available, pppd will
+connect to the peer and perform negotiation, authentication, etc.
+When this is completed, pppd will commence passing data packets
+(i.e., IP packets) across the link.
+
+The \fIdemand\fR option implies the \fIpersist\fR option. If this
+behaviour is not desired, use the \fInopersist\fR option after the
+\fIdemand\fR option. The \fIidle\fR and \fIholdoff\fR
+options are also useful in conjunction with the \fIdemand\fR option.
+.TP
+.B domain \fId
+Append the domain name \fId\fR to the local host name for authentication
+purposes. For example, if gethostname() returns the name porsche, but
+the fully qualified domain name is porsche.Quotron.COM, you could
+specify \fIdomain Quotron.COM\fR. Pppd would then use the name
+\fIporsche.Quotron.COM\fR for looking up secrets in the secrets file,
+and as the default name to send to the peer when authenticating itself
+to the peer. This option is privileged.
+.TP
+.B holdoff \fIn
+Specifies how many seconds to wait before re-initiating the link after
+it terminates. This option only has any effect if the \fIpersist\fR
+or \fIdemand\fR option is used. The holdoff period is not applied if
+the link was terminated because it was idle.
+.TP
+.B idle \fIn
+Specifies that pppd should disconnect if the link is idle for \fIn\fR
+seconds. The link is idle when no data packets (i.e. IP packets) are
+being sent or received. Note: it is not advisable to use this option
+with the \fIpersist\fR option without the \fIdemand\fR option.
+If the \fBactive-filter\fR
+option is given, data packets which are rejected by the specified
+activity filter also count as the link being idle.
+.TP
+.B ipcp-accept-local
+With this option, pppd will accept the peer's idea of our local IP
+address, even if the local IP address was specified in an option.
+.TP
+.B ipcp-accept-remote
+With this option, pppd will accept the peer's idea of its (remote) IP
+address, even if the remote IP address was specified in an option.
+.TP
+.B ipcp-max-configure \fIn
+Set the maximum number of IPCP configure-request transmissions to
+\fIn\fR (default 10).
+.TP
+.B ipcp-max-failure \fIn
+Set the maximum number of IPCP configure-NAKs returned before starting
+to send configure-Rejects instead to \fIn\fR (default 10).
+.TP
+.B ipcp-max-terminate \fIn
+Set the maximum number of IPCP terminate-request transmissions to
+\fIn\fR (default 3).
+.TP
+.B ipcp-restart \fIn
+Set the IPCP restart interval (retransmission timeout) to \fIn\fR
+seconds (default 3).
+.TP
+.B ipparam \fIstring
+Provides an extra parameter to the ip-up and ip-down scripts. If this
+option is given, the \fIstring\fR supplied is given as the 6th
+parameter to those scripts.
+.TP
+.B ipv6cp-max-configure \fIn
+Set the maximum number of IPv6CP configure-request transmissions to
+\fIn\fR (default 10).
+.TP
+.B ipv6cp-max-failure \fIn
+Set the maximum number of IPv6CP configure-NAKs returned before starting
+to send configure-Rejects instead to \fIn\fR (default 10).
+.TP
+.B ipv6cp-max-terminate \fIn
+Set the maximum number of IPv6CP terminate-request transmissions to
+\fIn\fR (default 3).
+.TP
+.B ipv6cp-restart \fIn
+Set the IPv6CP restart interval (retransmission timeout) to \fIn\fR
+seconds (default 3).
+.TP
+.B ipx
+Enable the IPXCP and IPX protocols. This option is presently only
+supported under Linux, and only if your kernel has been configured to
+include IPX support.
+.TP
+.B ipx-network \fIn
+Set the IPX network number in the IPXCP configure request frame to
+\fIn\fR, a hexadecimal number (without a leading 0x). There is no
+valid default. If this option is not specified, the network number is
+obtained from the peer. If the peer does not have the network number,
+the IPX protocol will not be started.
+.TP
+.B ipx-node \fIn\fB:\fIm
+Set the IPX node numbers.
+The two node numbers are separated from each
+other with a colon character.
+The first number \fIn\fR is the local
+node number.
+The second number \fIm\fR is the peer's node number.
+Each
+node number is a hexadecimal number, at most 10 digits long.
+The node
+numbers on the ipx-network must be unique.
+There is no valid
+default.
+If this option is not specified then the node numbers are
+obtained from the peer.
+.TP
+.B ipx-router-name \fI<string>
+Set the name of the router.
+This is a string and is sent to the peer
+as information data.
+.TP
+.B ipx-routing \fIn
+Set the routing protocol to be received by this option.
+More than one
+instance of \fIipx-routing\fR may be specified.
+The '\fInone\fR'
+option (0) may be specified as the only instance of ipx-routing.
+The
+values may be \fI0\fR for \fINONE\fR, \fI2\fR for \fIRIP/SAP\fR, and
+\fI4\fR for \fINLSP\fR.
+.TP
+.B ipxcp-accept-local
+Accept the peer's NAK for the node number specified in the ipx-node
+option.
+If a node number was specified, and non-zero, the default is
+to insist that the value be used.
+If you include this option then you
+will permit the peer to override the entry of the node number.
+.TP
+.B ipxcp-accept-network
+Accept the peer's NAK for the network number specified in the
+ipx-network option.
+If a network number was specified, and non-zero, the
+default is to insist that the value be used.
+If you include this
+option then you will permit the peer to override the entry of the node
+number.
+.TP
+.B ipxcp-accept-remote
+Use the peer's network number specified in the configure request
+frame.
+If a node number was specified for the peer and this option was
+not specified, the peer will be forced to use the value which you have
+specified.
+.TP
+.B ipxcp-max-configure \fIn
+Set the maximum number of IPXCP configure request frames which the
+system will send to \fIn\fR.
+The default is 10.
+.TP
+.B ipxcp-max-failure \fIn
+Set the maximum number of IPXCP NAK frames which the local system will
+send before it rejects the options.
+The default value is 3.
+.TP
+.B ipxcp-max-terminate \fIn
+Set the maximum number of IPXCP terminate request frames before the
+local system considers that the peer is not listening to them.
+The
+default value is 3.
+.TP
+.B kdebug \fIn
+Enable debugging code in the kernel-level PPP driver. The argument
+\fIn\fR is a number which is the sum of the following values: 1 to
+enable general debug messages, 2 to request that the contents of
+received packets be printed, and 4 to request that the contents of
+transmitted packets be printed. On most systems, messages printed by
+the kernel are logged by syslog(1) to a file as directed in the
+/etc/syslog.conf configuration file.
+.TP
+.B lcp-echo-failure \fIn
+If this option is given, pppd will presume the peer to be dead
+if \fIn\fR LCP echo-requests are sent without receiving a valid LCP
+echo-reply. If this happens, pppd will terminate the
+connection. Use of this option requires a non-zero value for the
+\fIlcp-echo-interval\fR parameter. This option can be used to enable
+pppd to terminate after the physical connection has been broken
+(e.g., the modem has hung up) in situations where no hardware modem
+control lines are available.
+.TP
+.B lcp-echo-interval \fIn
+If this option is given, pppd will send an LCP echo-request frame to
+the peer every \fIn\fR seconds. Normally the peer should respond to
+the echo-request by sending an echo-reply. This option can be used
+with the \fIlcp-echo-failure\fR option to detect that the peer is no
+longer connected.
+.TP
+.B lcp-max-configure \fIn
+Set the maximum number of LCP configure-request transmissions to
+\fIn\fR (default 10).
+.TP
+.B lcp-max-failure \fIn
+Set the maximum number of LCP configure-NAKs returned before starting
+to send configure-Rejects instead to \fIn\fR (default 10).
+.TP
+.B lcp-max-terminate \fIn
+Set the maximum number of LCP terminate-request transmissions to
+\fIn\fR (default 3).
+.TP
+.B lcp-restart \fIn
+Set the LCP restart interval (retransmission timeout) to \fIn\fR
+seconds (default 3).
+.TP
+.B local
+Don't use the modem control lines. With this option, pppd will ignore
+the state of the CD (Carrier Detect) signal from the modem and will
+not change the state of the DTR (Data Terminal Ready) signal.
+.TP
+.B login
+Use the system password database for authenticating the peer using
+PAP, and record the user in the system wtmp file. Note that the peer
+must have an entry in the /etc/ppp/pap-secrets file as well as the
+system password database to be allowed access.
+.TP
+.B maxconnect \fIn
+Terminate the connection when it has been available for network
+traffic for \fIn\fR seconds (i.e. \fIn\fR seconds after the first
+network control protocol comes up).
+.TP
+.B modem
+Use the modem control lines. This option is the default. With this
+option, pppd will wait for the CD (Carrier Detect) signal from the
+modem to be asserted when opening the serial device (unless a connect
+script is specified), and it will drop the DTR (Data Terminal Ready)
+signal briefly when the connection is terminated and before executing
+the connect script. On Ultrix, this option implies hardware flow
+control, as for the \fIcrtscts\fR option.
+.TP
+.B ms-dns \fI<addr>
+If pppd is acting as a server for Microsoft Windows clients, this
+option allows pppd to supply one or two DNS (Domain Name Server)
+addresses to the clients. The first instance of this option specifies
+the primary DNS address; the second instance (if given) specifies the
+secondary DNS address. (This option was present in some older
+versions of pppd under the name \fBdns-addr\fR.)
+.TP
+.B ms-wins \fI<addr>
+If pppd is acting as a server for Microsoft Windows or "Samba"
+clients, this option allows pppd to supply one or two WINS (Windows
+Internet Name Services) server addresses to the clients. The first
+instance of this option specifies the primary WINS address; the second
+instance (if given) specifies the secondary WINS address.
+.TP
+.B name \fIname
+Set the name of the local system for authentication purposes to
+\fIname\fR. This is a privileged option. With this option, pppd will
+use lines in the secrets files which have \fIname\fR as the second
+field when looking for a secret to use in authenticating the peer. In
+addition, unless overridden with the \fIuser\fR option, \fIname\fR
+will be used as the name to send to the peer when authenticating the
+local system to the peer. (Note that pppd does not append the domain
+name to \fIname\fR.)
+.TP
+.B netmask \fIn
+Set the interface netmask to \fIn\fR, a 32 bit netmask in "decimal dot"
+notation (e.g. 255.255.255.0). If this option is given, the value
+specified is ORed with the default netmask. The default netmask is
+chosen based on the negotiated remote IP address; it is the
+appropriate network mask for the class of the remote IP address, ORed
+with the netmasks for any non point-to-point network interfaces in the
+system which are on the same network.
+.TP
+.B noaccomp
+Disable Address/Control compression in both directions (send and
+receive).
+.TP
+.B noauth
+Do not require the peer to authenticate itself. This option is
+privileged if the \fIauth\fR option is specified in /etc/ppp/options.
+.TP
+.B nobsdcomp
+Disables BSD-Compress compression; \fBpppd\fR will not request or
+agree to compress packets using the BSD-Compress scheme.
+.TP
+.B noccp
+Disable CCP (Compression Control Protocol) negotiation. This option
+should only be required if the peer is buggy and gets confused by
+requests from pppd for CCP negotiation.
+.TP
+.B nocrtscts
+Disable hardware flow control (i.e. RTS/CTS) on the serial port. If
+neither the \fIcrtscts\fR nor the \fInocrtscts\fR option is given,
+the hardware flow control setting for the serial port is left
+unchanged.
+.TP
+.B nodefaultroute
+Disable the \fIdefaultroute\fR option. The system administrator who
+wishes to prevent users from creating default routes with pppd
+can do so by placing this option in the /etc/ppp/options file.
+.TP
+.B nodeflate
+Disables Deflate compression; pppd will not request or agree to
+compress packets using the Deflate scheme.
+.TP
+.B nodetach
+Don't detach from the controlling terminal. Without this option, if a
+serial device other than the terminal on the standard input is
+specified, pppd will fork to become a background process.
+.TP
+.B noip
+Disable IPCP negotiation and IP communication. This option should
+only be required if the peer is buggy and gets confused by requests
+from pppd for IPCP negotiation.
+.TP
+.B noipv6
+Disable IPv6CP negotiation and IPv6 communication. This option should
+only be required if the peer is buggy and gets confused by requests
+from pppd for IPv6CP negotiation.
+.TP
+.B noipdefault
+Disables the default behaviour when no local IP address is specified,
+which is to determine (if possible) the local IP address from the
+hostname. With this option, the peer will have to supply the local IP
+address during IPCP negotiation (unless it specified explicitly on the
+command line or in an options file).
+.TP
+.B noipx
+Disable the IPXCP and IPX protocols. This option should only be
+required if the peer is buggy and gets confused by requests from pppd
+for IPXCP negotiation.
+.TP
+.B nomagic
+Disable magic number negotiation. With this option, pppd cannot
+detect a looped-back line. This option should only be needed if the
+peer is buggy.
+.TP
+.B nopcomp
+Disable protocol field compression negotiation in both the receive and
+the transmit direction.
+.TP
+.B nopersist
+Exit once a connection has been made and terminated. This is the
+default unless the \fIpersist\fR or \fIdemand\fR option has been
+specified.
+.TP
+.B nopredictor1
+Do not accept or agree to Predictor-1 compression.
+.TP
+.B noproxyarp
+Disable the \fIproxyarp\fR option. The system administrator who
+wishes to prevent users from creating proxy ARP entries with pppd can
+do so by placing this option in the /etc/ppp/options file.
+.TP
+.B novj
+Disable Van Jacobson style TCP/IP header compression in both the
+transmit and the receive direction.
+.TP
+.B novjccomp
+Disable the connection-ID compression option in Van Jacobson style
+TCP/IP header compression. With this option, pppd will not omit the
+connection-ID byte from Van Jacobson compressed TCP/IP headers, nor
+ask the peer to do so.
+.TP
+.B papcrypt
+Indicates that all secrets in the /etc/ppp/pap-secrets file which are
+used for checking the identity of the peer are encrypted, and thus
+pppd should not accept a password which, before encryption, is
+identical to the secret from the /etc/ppp/pap-secrets file.
+.TP
+.B pap-max-authreq \fIn
+Set the maximum number of PAP authenticate-request transmissions to
+\fIn\fR (default 10).
+.TP
+.B pap-restart \fIn
+Set the PAP restart interval (retransmission timeout) to \fIn\fR
+seconds (default 3).
+.TP
+.B pap-timeout \fIn
+Set the maximum time that pppd will wait for the peer to authenticate
+itself with PAP to \fIn\fR seconds (0 means no limit).
+.TP
+.B pass-filter \fIfilter-expression
+Specifies a packet filter to applied to data packets being sent or
+received to determine which packets should be allowed to pass.
+Packets which are rejected by the filter are silently discarded. This
+option can be used to prevent specific network daemons (such as
+routed) using up link bandwidth, or to provide a basic firewall
+capability.
+The \fIfilter-expression\fR syntax is as described for tcpdump(1),
+except that qualifiers which are inappropriate for a PPP link, such as
+\fBether\fR and \fBarp\fR, are not permitted. Generally the filter
+expression should be enclosed in single-quotes to prevent whitespace
+in the expression from being interpreted by the shell. Note that it
+is possible to apply different constraints to incoming and outgoing
+packets using the \fBinbound\fR and \fBoutbound\fR qualifiers.
+This
+option is currently only available under NetBSD, and then only if both
+the kernel and pppd were compiled with PPP_FILTER defined.
+.TP
+.B persist
+Do not exit after a connection is terminated; instead try to reopen
+the connection.
+.TP
+.B predictor1
+Request that the peer compress frames that it sends using Predictor-1
+compression, and agree to compress transmitted frames with Predictor-1
+if requested. This option has no effect unless the kernel driver
+supports Predictor-1 compression.
+.TP
+.B proxyarp
+Add an entry to this system's ARP [Address Resolution Protocol] table
+with the IP address of the peer and the Ethernet address of this
+system. This will have the effect of making the peer appear to other
+systems to be on the local ethernet.
+.TP
+.B remotename \fIname
+Set the assumed name of the remote system for authentication purposes
+to \fIname\fR.
+.TP
+.B refuse-chap
+With this option, pppd will not agree to authenticate itself to the
+peer using CHAP.
+.TP
+.B refuse-pap
+With this option, pppd will not agree to authenticate itself to the
+peer using PAP.
+.TP
+.B require-chap
+Require the peer to authenticate itself using CHAP [Challenge
+Handshake Authentication Protocol] authentication.
+.TP
+.B require-pap
+Require the peer to authenticate itself using PAP [Password
+Authentication Protocol] authentication.
+.TP
+.B silent
+With this option, pppd will not transmit LCP packets to initiate a
+connection until a valid LCP packet is received from the peer (as for
+the `passive' option with ancient versions of pppd).
+.TP
+.B usehostname
+Enforce the use of the hostname (with domain name appended, if given)
+as the name of the local system for authentication purposes (overrides
+the \fIname\fR option).
+.TP
+.B user \fIname
+Sets the name used for authenticating the local system to the peer to
+\fIname\fR.
+.TP
+.B vj-max-slots \fIn
+Sets the number of connection slots to be used by the Van Jacobson
+TCP/IP header compression and decompression code to \fIn\fR, which
+must be between 2 and 16 (inclusive).
+.TP
+.B welcome \fIscript
+Run the executable or shell command specified by \fIscript\fR before
+initiating PPP negotiation, after the connect script (if any) has
+completed. This option is privileged if the \fInoauth\fR option is
+used.
+.TP
+.B xonxoff
+Use software flow control (i.e. XON/XOFF) to control the flow of data on
+the serial port.
+.SH OPTIONS FILES
+Options can be taken from files as well as the command line. Pppd
+reads options from the files /etc/ppp/options, ~/.ppprc and
+/etc/ppp/options.\fIttyname\fR (in that order) before processing the
+options on the command line. (In fact, the command-line options are
+scanned to find the terminal name before the options.\fIttyname\fR
+file is read.) In forming the name of the options.\fIttyname\fR file,
+the initial /dev/ is removed from the terminal name, and any remaining
+/ characters are replaced with dots.
+.PP
+An options file is parsed into a series of words, delimited by
+whitespace. Whitespace can be included in a word by enclosing the
+word in double-quotes ("). A backslash (\\) quotes the following character.
+A hash (#) starts a comment, which continues until the end of the
+line. There is no restriction on using the \fIfile\fR or \fIcall\fR
+options within an options file.
+.SH SECURITY
+.I pppd
+provides system administrators with sufficient access control that PPP
+access to a server machine can be provided to legitimate users without
+fear of compromising the security of the server or the network it's
+on. In part this is provided by the /etc/ppp/options file, where the
+administrator can place options to restrict the ways in which pppd can
+be used, and in part by the PAP and CHAP secrets files, where the
+administrator can restrict the set of IP addresses which individual
+users may use.
+.PP
+The normal way that pppd should be set up is to have the \fIauth\fR
+option in the /etc/ppp/options file. (This may become the default in
+later releases.) If users wish to use pppd to dial out to a peer
+which will refuse to authenticate itself (such as an internet service
+provider), the system administrator should create an options file
+under /etc/ppp/peers containing the \fInoauth\fR option, the name of
+the serial port to use, and the \fIconnect\fR option (if required),
+plus any other appropriate options. In this way, pppd can be set up
+to allow non-privileged users to make unauthenticated connections only
+to trusted peers.
+.PP
+As indicated above, some security-sensitive options are privileged,
+which means that they may not be used by an ordinary non-privileged
+user running a setuid-root pppd, either on the command line, in the
+user's ~/.ppprc file, or in an options file read using the \fIfile\fR
+option. Privileged options may be used in /etc/ppp/options file or in
+an options file read using the \fIcall\fR option. If pppd is being
+run by the root user, privileged options can be used without
+restriction.
+.SH AUTHENTICATION
+Authentication is the process whereby one peer convinces the other of
+its identity. This involves the first peer sending its name to the
+other, together with some kind of secret information which could only
+come from the genuine authorized user of that name. In such an
+exchange, we will call the first peer the "client" and the other the
+"server". The client has a name by which it identifies itself to the
+server, and the server also has a name by which it identifies itself
+to the client. Generally the genuine client shares some secret (or
+password) with the server, and authenticates itself by proving that it
+knows that secret. Very often, the names used for authentication
+correspond to the internet hostnames of the peers, but this is not
+essential.
+.LP
+At present, pppd supports two authentication protocols: the Password
+Authentication Protocol (PAP) and the Challenge Handshake
+Authentication Protocol (CHAP). PAP involves the client sending its
+name and a cleartext password to the server to authenticate itself.
+In contrast, the server initiates the CHAP authentication exchange by
+sending a challenge to the client (the challenge packet includes the
+server's name). The client must respond with a response which
+includes its name plus a hash value derived from the shared secret and
+the challenge, in order to prove that it knows the secret.
+.LP
+The PPP protocol, being symmetrical, allows both peers to require the
+other to authenticate itself. In that case, two separate and
+independent authentication exchanges will occur. The two exchanges
+could use different authentication protocols, and in principle,
+different names could be used in the two exchanges.
+.LP
+The default behaviour of pppd is to agree to authenticate if
+requested, and to not require authentication from the peer. However,
+pppd will not agree to authenticate itself with a particular protocol
+if it has no secrets which could be used to do so.
+.LP
+Pppd stores secrets for use in authentication in secrets
+files (/etc/ppp/pap-secrets for PAP, /etc/ppp/chap-secrets for CHAP).
+Both secrets files have the same format. The secrets files can
+contain secrets for pppd to use in authenticating itself to other
+systems, as well as secrets for pppd to use when authenticating other
+systems to itself.
+.LP
+Each line in a secrets file contains one secret. A given secret is
+specific to a particular combination of client and server - it can
+only be used by that client to authenticate itself to that server.
+Thus each line in a secrets file has at least 3 fields: the name of
+the client, the name of the server, and the secret. These fields may
+be followed by a list of the IP addresses that the specified client
+may use when connecting to the specified server.
+.LP
+A secrets file is parsed into words as for an options file, so the
+client name, server name and secrets fields must each be one word,
+with any embedded spaces or other special characters quoted or
+escaped. Any following words on the same line are taken to be a list
+of acceptable IP addresses for that client, or an
+override for "local:remote" addresses (the same format used on the
+command line or in the options file) when on a line that contains a
+specific client name (not a wildcard nor empty).
+If there are only 3 words
+on the line, or if the first word is "-", then all IP addresses are
+disallowed. To allow any address, use "*".
+A word starting with "!" indicates that the
+specified address is \fInot\fR acceptable. An address may be followed
+by "/" and a number \fIn\fR, to indicate a whole subnet, i.e. all
+addresses which have the same value in the most significant \fIn\fR
+bits. Note that case is significant in the client and server names
+and in the secret.
+.LP
+If the secret starts with an `@', what follows is assumed to be the
+name of a file from which to read the secret. A "*" as the client or
+server name matches any name. When selecting a secret, pppd takes the
+best match, i.e. the match with the fewest wildcards.
+.LP
+Thus a secrets file contains both secrets for use in authenticating
+other hosts, plus secrets which we use for authenticating ourselves to
+others. When pppd is authenticating the peer (checking the peer's
+identity), it chooses a secret with the peer's name in the first
+field and the name of the local system in the second field. The
+name of the local system defaults to the hostname, with the domain
+name appended if the \fIdomain\fR option is used. This default can be
+overridden with the \fIname\fR option, except when the
+\fIusehostname\fR option is used.
+.LP
+When pppd is choosing a secret to use in authenticating itself to the
+peer, it first determines what name it is going to use to identify
+itself to the peer. This name can be specified by the user with the
+\fIuser\fR option. If this option is not used, the name defaults to
+the name of the local system, determined as described in the previous
+paragraph. Then pppd looks for a secret with this name in the first
+field and the peer's name in the second field. Pppd will know the
+name of the peer if CHAP authentication is being used, because the
+peer will have sent it in the challenge packet. However, if PAP is being
+used, pppd will have to determine the peer's name from the options
+specified by the user. The user can specify the peer's name directly
+with the \fIremotename\fR option. Otherwise, if the remote IP address
+was specified by a name (rather than in numeric form), that name will
+be used as the peer's name. Failing that, pppd will use the null
+string as the peer's name.
+.LP
+When authenticating the peer with PAP, the supplied password is first
+compared with the secret from the secrets file. If the password
+doesn't match the secret, the password is encrypted using crypt() and
+checked against the secret again. Thus secrets for authenticating the
+peer can be stored in encrypted form if desired. If the
+\fIpapcrypt\fR option is given, the first (unencrypted) comparison is
+omitted, for better security.
+.LP
+Furthermore, if the \fIlogin\fR option was specified, the username and
+password are also checked against the system password database. Thus,
+the system administrator can set up the pap-secrets file to allow PPP
+access only to certain users, and to restrict the set of IP addresses
+that each user can use. Typically, when using the \fIlogin\fR option,
+the secret in /etc/ppp/pap-secrets would be "", which will match any
+password supplied by the peer. This avoids the need to have the same
+secret in two places.
+.LP
+Additional checks are performed when the \fBlogin\fR option is used.
+If the file /etc/ppp/ppp.deny exists, and the user is listed in it,
+the authentication fails. If the file /etc/ppp/ppp.shells exists and
+the user's normal login shell is not listed, the authentication fails.
+.LP
+Authentication must be satisfactorily completed before IPCP (or any
+other Network Control Protocol) can be started. If the peer is
+required to authenticate itself, and fails to do so, pppd will
+terminated the link (by closing LCP). If IPCP negotiates an
+unacceptable IP address for the remote host, IPCP will be closed. IP
+packets can only be sent or received when IPCP is open.
+.LP
+In some cases it is desirable to allow some hosts which can't
+authenticate themselves to connect and use one of a restricted set of
+IP addresses, even when the local host generally requires
+authentication. If the peer refuses to authenticate itself when
+requested, pppd takes that as equivalent to authenticating with PAP
+using the empty string for the username and password. Thus, by adding
+a line to the pap-secrets file which specifies the empty string for
+the client and password, it is possible to allow restricted access to
+hosts which refuse to authenticate themselves.
+.SH ROUTING
+.LP
+When IPCP negotiation is completed successfully, pppd will inform the
+kernel of the local and remote IP addresses for the ppp interface.
+This is sufficient to create a host route to the remote end of the
+link, which will enable the peers to exchange IP packets.
+Communication with other machines generally requires further
+modification to routing tables and/or ARP (Address Resolution
+Protocol) tables. In most cases the \fIdefaultroute\fR and/or
+\fIproxyarp\fR options are sufficient for this, but in some cases
+further intervention is required. The /etc/ppp/ip-up script can be
+used for this.
+.LP
+Sometimes it is desirable to add a default route through the remote
+host, as in the case of a machine whose only connection to the
+Internet is through the ppp interface. The \fIdefaultroute\fR option
+causes pppd to create such a default route when IPCP comes up, and
+delete it when the link is terminated.
+.LP
+In some cases it is desirable to use proxy ARP, for example on a
+server machine connected to a LAN, in order to allow other hosts to
+communicate with the remote host. The \fIproxyarp\fR option causes
+pppd to look for a network interface on the same subnet as the remote
+host (an interface supporting broadcast and ARP, which is up and not a
+point-to-point or loopback interface). If found, pppd creates a
+permanent, published ARP entry with the IP address of the remote host
+and the hardware address of the network interface found.
+.LP
+When the \fIdemand\fR option is used, the interface IP addresses have
+already been set at the point when IPCP comes up. If pppd has not
+been able to negotiate the same addresses that it used to configure
+the interface (for example when the peer is an ISP that uses dynamic
+IP address assignment), pppd has to change the interface IP addresses
+to the negotiated addresses. This may disrupt existing connections,
+and the use of demand dialling with peers that do dynamic IP address
+assignment is not recommended.
+.SH EXAMPLES
+.LP
+The following examples assume that the /etc/ppp/options file contains
+the \fIauth\fR option (as in the default /etc/ppp/options file in the
+ppp distribution).
+.LP
+Probably the most common use of pppd is to dial out to an ISP. This
+can be done with a command such as
+.IP
+pppd call isp
+.LP
+where the /etc/ppp/peers/isp file is set up by the system
+administrator to contain something like this:
+.IP
+ttyS0 19200 crtscts
+.br
+connect '/usr/sbin/chat -v -f /etc/ppp/chat-isp'
+.br
+noauth
+.LP
+In this example, we are using chat to dial the ISP's modem and go
+through any logon sequence required. The /etc/ppp/chat-isp file
+contains the script used by chat; it could for example contain
+something like this:
+.IP
+ABORT "NO CARRIER"
+.br
+ABORT "NO DIALTONE"
+.br
+ABORT "ERROR"
+.br
+ABORT "NO ANSWER"
+.br
+ABORT "BUSY"
+.br
+ABORT "Username/Password Incorrect"
+.br
+"" "at"
+.br
+OK "at&d0&c1"
+.br
+OK "atdt2468135"
+.br
+"name:" "^Umyuserid"
+.br
+"word:" "\\qmypassword"
+.br
+"ispts" "\\q^Uppp"
+.br
+"~-^Uppp-~"
+.LP
+See the chat(8) man page for details of chat scripts.
+.LP
+Pppd can also be used to provide a dial-in ppp service for users. If
+the users already have login accounts, the simplest way to set up the
+ppp service is to let the users log in to their accounts and run pppd
+(installed setuid-root) with a command such as
+.IP
+pppd proxyarp
+.LP
+To allow a user to use the PPP facilities, you need to allocate an IP
+address for that user's machine and create an entry in
+/etc/ppp/pap-secrets or /etc/ppp/chap-secrets (depending on which
+authentication method the PPP implementation on the user's machine
+supports), so that the user's
+machine can authenticate itself. For example, if Joe has a machine
+called "joespc" which is to be allowed to dial in to the machine
+called "server" and use the IP address joespc.my.net, you would add an
+entry like this to /etc/ppp/pap-secrets or /etc/ppp/chap-secrets:
+.IP
+joespc server "joe's secret" joespc.my.net
+.LP
+Alternatively, you can create a username called (for example) "ppp",
+whose login shell is pppd and whose home directory is /etc/ppp.
+Options to be used when pppd is run this way can be put in
+/etc/ppp/.ppprc.
+.LP
+If your serial connection is any more complicated than a piece of
+wire, you may need to arrange for some control characters to be
+escaped. In particular, it is often useful to escape XON (^Q) and
+XOFF (^S), using \fIasyncmap a0000\fR. If the path includes a telnet,
+you probably should escape ^] as well (\fIasyncmap 200a0000\fR). If
+the path includes an rlogin, you will need to use the \fIescape ff\fR
+option on the end which is running the rlogin client, since many
+rlogin implementations are not transparent; they will remove the
+sequence [0xff, 0xff, 0x73, 0x73, followed by any 8 bytes] from the
+stream.
+.SH DIAGNOSTICS
+.LP
+Messages are sent to the syslog daemon using facility LOG_DAEMON.
+(This can be overriden by recompiling pppd with the macro
+LOG_PPP defined as the desired facility.) In order to see the error
+and debug messages, you will need to edit your /etc/syslog.conf file
+to direct the messages to the desired output device or file.
+.LP
+The \fIdebug\fR option causes the contents of all control packets sent
+or received to be logged, that is, all LCP, PAP, CHAP or IPCP packets.
+This can be useful if the PPP negotiation does not succeed or if
+authentication fails.
+If debugging is enabled at compile time, the \fIdebug\fR option also
+causes other debugging messages to be logged.
+.LP
+Debugging can also be enabled or disabled by sending a SIGUSR1 signal
+to the pppd process. This signal acts as a toggle.
+.SH SCRIPTS
+Pppd invokes scripts at various stages in its processing which can be
+used to perform site-specific ancillary processing. These scripts are
+usually shell scripts, but could be executable code files instead.
+Pppd does not wait for the scripts to finish. The scripts are
+executed as root (with the real and effective user-id set to 0), so
+that they can do things such as update routing tables or run
+privileged daemons. Be careful that the contents of these scripts do
+not compromise your system's security. Pppd runs the scripts with
+standard input, output and error redirected to /dev/null, and with an
+environment that is empty except for some environment variables that
+give information about the link. The environment variables that pppd
+sets are:
+.TP
+.B DEVICE
+The name of the serial tty device being used.
+.TP
+.B IFNAME
+The name of the network interface being used.
+.TP
+.B IPLOCAL
+The IP address for the local end of the link. This is only set when
+IPCP has come up.
+.TP
+.B IPREMOTE
+The IP address for the remote end of the link. This is only set when
+IPCP has come up.
+.TP
+.B PEERNAME
+The authenticated name of the peer. This is only set if the peer
+authenticates itself.
+.TP
+.B SPEED
+The baud rate of the tty device.
+.TP
+.B UID
+The real user-id of the user who invoked pppd.
+.P
+Pppd invokes the following scripts, if they exist. It is not an error
+if they don't exist.
+.TP
+.B /etc/ppp/auth-up
+A program or script which is executed after the remote system
+successfully authenticates itself. It is executed with the parameters
+.IP
+\fIinterface-name peer-name user-name tty-device speed\fR
+.IP
+Note that this script is not executed if the peer doesn't authenticate
+itself, for example when the \fInoauth\fR option is used.
+.TP
+.B /etc/ppp/auth-down
+A program or script which is executed when the link goes down, if
+/etc/ppp/auth-up was previously executed. It is executed in the same
+manner with the same parameters as /etc/ppp/auth-up.
+.TP
+.B /etc/ppp/ip-up
+A program or script which is executed when the link is available for
+sending and receiving IP packets (that is, IPCP has come up). It is
+executed with the parameters
+.IP
+\fIinterface-name tty-device speed local-IP-address
+remote-IP-address ipparam\fR
+.TP
+.B /etc/ppp/ip-down
+A program or script which is executed when the link is no longer
+available for sending and receiving IP packets. This script can be
+used for undoing the effects of the /etc/ppp/ip-up script. It is
+invoked in the same manner and with the same parameters as the ip-up
+script.
+.TP
+.B /etc/ppp/ipv6-up
+Like /etc/ppp/ip-up, except that it is executed when the link is available
+for sending and receiving IPv6 packets. It is executed with the parameters
+.IP
+\fIinterface-name tty-device speed local-link-local-address
+remote-link-local-address ipparam\fR
+.TP
+.B /etc/ppp/ipv6-down
+Similar to /etc/ppp/ip-down, but it is executed when IPv6 packets can no
+longer be transmitted on the link. It is executed with the same parameters
+as the ipv6-up script.
+.TP
+.B /etc/ppp/ipx-up
+A program or script which is executed when the link is available for
+sending and receiving IPX packets (that is, IPXCP has come up). It is
+executed with the parameters
+.IP
+\fIinterface-name tty-device speed network-number local-IPX-node-address
+remote-IPX-node-address local-IPX-routing-protocol remote-IPX-routing-protocol
+local-IPX-router-name remote-IPX-router-name ipparam pppd-pid\fR
+.IP
+The local-IPX-routing-protocol and remote-IPX-routing-protocol field
+may be one of the following:
+.IP
+NONE to indicate that there is no routing protocol
+.br
+RIP to indicate that RIP/SAP should be used
+.br
+NLSP to indicate that Novell NLSP should be used
+.br
+RIP NLSP to indicate that both RIP/SAP and NLSP should be used
+.TP
+.B /etc/ppp/ipx-down
+A program or script which is executed when the link is no longer
+available for sending and receiving IPX packets. This script can be
+used for undoing the effects of the /etc/ppp/ipx-up script. It is
+invoked in the same manner and with the same parameters as the ipx-up
+script.
+.SH FILES
+.TP
+.B /var/run/ppp\fIn\fB.pid \fR(BSD or Linux), \fB/etc/ppp/ppp\fIn\fB.pid \fR(others)
+Process-ID for pppd process on ppp interface unit \fIn\fR.
+.TP
+.B /etc/ppp/pap-secrets
+Usernames, passwords and IP addresses for PAP authentication. This
+file should be owned by root and not readable or writable by any other
+user. Pppd will log a warning if this is not the case.
+.TP
+.B /etc/ppp/chap-secrets
+Names, secrets and IP addresses for CHAP authentication. As for
+/etc/ppp/pap-secrets, this file should be owned by root and not
+readable or writable by any other user. Pppd will log a warning if
+this is not the case.
+.TP
+.B /etc/ppp/options
+System default options for pppd, read before user default options or
+command-line options.
+.TP
+.B ~/.ppprc
+User default options, read before /etc/ppp/options.\fIttyname\fR.
+.TP
+.B /etc/ppp/options.\fIttyname
+System default options for the serial port being used, read after
+~/.ppprc. In forming the \fIttyname\fR part of this
+filename, an initial /dev/ is stripped from the port name (if
+present), and any slashes in the remaining part are converted to
+dots.
+.TP
+.B /etc/ppp/peers
+A directory containing options files which may contain privileged
+options, even if pppd was invoked by a user other than root. The
+system administrator can create options files in this directory to
+permit non-privileged users to dial out without requiring the peer to
+authenticate, but only to certain trusted peers.
+.TP
+.B /etc/ppp/ppp.deny
+Lists users who may not use the system password PAP authentication.
+.TP
+.B /etc/ppp/ppp.shells
+Lists user shells which are approved for system password PAP authentication
+logins.
+.TP
+.B /usr/share/examples/pppd/
+Sample pppd configuration files.
+.SH SEE ALSO
+.IR chat(8),
+.IR ppp(8)
+.TP
+.B RFC1144
+Jacobson, V.
+\fICompressing TCP/IP headers for low-speed serial links.\fR
+February 1990.
+.TP
+.B RFC1321
+Rivest, R.
+.I The MD5 Message-Digest Algorithm.
+April 1992.
+.TP
+.B RFC1332
+McGregor, G.
+.I PPP Internet Protocol Control Protocol (IPCP).
+May 1992.
+.TP
+.B RFC1334
+Lloyd, B.; Simpson, W.A.
+.I PPP authentication protocols.
+October 1992.
+.TP
+.B RFC1661
+Simpson, W.A.
+.I The Point\-to\-Point Protocol (PPP).
+July 1994.
+.TP
+.B RFC1662
+Simpson, W.A.
+.I PPP in HDLC-like Framing.
+July 1994.
+.SH NOTES
+The following signals have the specified effect when sent to pppd.
+.TP
+.B SIGINT, SIGTERM
+These signals cause pppd to terminate the link (by closing LCP),
+restore the serial device settings, and exit.
+.TP
+.B SIGHUP
+This signal causes pppd to terminate the link, restore the serial
+device settings, and close the serial device. If the \fIpersist\fR or
+\fIdemand\fR option has been specified, pppd will try to reopen the
+serial device and start another connection (after the holdoff period).
+Otherwise pppd will exit. If this signal is received during the
+holdoff period, it causes pppd to end the holdoff period immediately.
+.TP
+.B SIGUSR1
+This signal toggles the state of the \fIdebug\fR option.
+.TP
+.B SIGUSR2
+This signal causes pppd to renegotiate compression. This can be
+useful to re-enable compression after it has been disabled as a result
+of a fatal decompression error. (Fatal decompression errors generally
+indicate a bug in one or other implementation.)
+
+.SH AUTHORS
+Paul Mackerras (Paul.Mackerras@cs.anu.edu.au), based on earlier work by
+Drew Perkins,
+Brad Clements,
+Karl Fox,
+Greg Christy,
+and
+Brad Parker.
diff --git a/usr.sbin/pppd/pppd.h b/usr.sbin/pppd/pppd.h
new file mode 100644
index 0000000..a547fb7
--- /dev/null
+++ b/usr.sbin/pppd/pppd.h
@@ -0,0 +1,509 @@
+/*
+ * pppd.h - PPP daemon global declarations.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 Carnegie Mellon University. The name of the
+ * University 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * TODO:
+ */
+
+#ifndef __PPPD_H__
+#define __PPPD_H__
+
+#include <stdio.h> /* for FILE */
+#include <sys/param.h> /* for MAXPATHLEN and BSD4_4, if defined */
+#include <sys/types.h> /* for u_int32_t, if defined */
+#include <sys/time.h> /* for struct timeval */
+#include <net/ppp_defs.h>
+
+#if __STDC__
+#include <stdarg.h>
+#define __V(x) x
+#else
+#include <varargs.h>
+#define __V(x) (va_alist) va_dcl
+#define const
+#endif
+
+#ifdef INET6
+#include "eui64.h"
+#endif
+
+/*
+ * Limits.
+ */
+
+#define NUM_PPP 1 /* One PPP interface supported (per process) */
+#define MAXWORDLEN 1024 /* max length of word in file (incl null) */
+#define MAXARGS 1 /* max # args to a command */
+#define MAXNAMELEN 256 /* max length of hostname or name for auth */
+#define MAXSECRETLEN 256 /* max length of password or secret */
+
+/*
+ * Global variables.
+ */
+
+extern int hungup; /* Physical layer has disconnected */
+extern int ifunit; /* Interface unit number */
+extern char ifname[]; /* Interface name */
+extern int ttyfd; /* Serial device file descriptor */
+extern char hostname[]; /* Our hostname */
+extern u_char outpacket_buf[]; /* Buffer for outgoing packets */
+extern int phase; /* Current state of link - see values below */
+extern int baud_rate; /* Current link speed in bits/sec */
+extern char *progname; /* Name of this program */
+extern int redirect_stderr;/* Connector's stderr should go to file */
+extern char peer_authname[];/* Authenticated name of peer */
+extern int privileged; /* We were run by real-uid root */
+extern int need_holdoff; /* Need holdoff period after link terminates */
+extern char **script_env; /* Environment variables for scripts */
+extern int detached; /* Have detached from controlling tty */
+
+/*
+ * Variables set by command-line options.
+ */
+
+extern int debug; /* Debug flag */
+extern int kdebugflag; /* Tell kernel to print debug messages */
+extern int default_device; /* Using /dev/tty or equivalent */
+extern char devnam[]; /* Device name */
+extern int crtscts; /* Use hardware flow control */
+extern int modem; /* Use modem control lines */
+extern int inspeed; /* Input/Output speed requested */
+extern u_int32_t netmask; /* IP netmask to set on interface */
+extern int lockflag; /* Create lock file to lock the serial dev */
+extern int nodetach; /* Don't detach from controlling tty */
+extern char *connector; /* Script to establish physical link */
+extern char *disconnector; /* Script to disestablish physical link */
+extern char *welcomer; /* Script to welcome client after connection */
+extern int max_con_attempts;/* Maximum number of times to try dialing */
+extern int maxconnect; /* Maximum connect time (seconds) */
+extern char user[]; /* Our name for authenticating ourselves */
+extern char passwd[]; /* Password for PAP */
+extern int auth_required; /* Peer is required to authenticate */
+extern int proxyarp; /* Set up proxy ARP entry for peer */
+extern int persist; /* Reopen link after it goes down */
+extern int uselogin; /* Use /etc/passwd for checking PAP */
+extern int lcp_echo_interval; /* Interval between LCP echo-requests */
+extern int lcp_echo_fails; /* Tolerance to unanswered echo-requests */
+extern char our_name[]; /* Our name for authentication purposes */
+extern char remote_name[]; /* Peer's name for authentication */
+extern int explicit_remote;/* remote_name specified with remotename opt */
+extern int usehostname; /* Use hostname for our_name */
+extern int disable_defaultip; /* Don't use hostname for default IP adrs */
+extern int demand; /* Do dial-on-demand */
+extern char *ipparam; /* Extra parameter for ip up/down scripts */
+extern int cryptpap; /* Others' PAP passwords are encrypted */
+extern int idle_time_limit;/* Shut down link if idle for this long */
+extern int holdoff; /* Dead time before restarting */
+extern int refuse_pap; /* Don't wanna auth. ourselves with PAP */
+extern int refuse_chap; /* Don't wanna auth. ourselves with CHAP */
+#ifdef PPP_FILTER
+extern struct bpf_program pass_filter; /* Filter for pkts to pass */
+extern struct bpf_program active_filter; /* Filter for link-active pkts */
+#endif
+
+
+#ifdef MSLANMAN
+extern int ms_lanman; /* Nonzero if use LanMan password instead of NT */
+ /* Has meaning only with MS-CHAP challenges */
+#endif
+
+/*
+ * Values for phase.
+ */
+#define PHASE_DEAD 0
+#define PHASE_INITIALIZE 1
+#define PHASE_DORMANT 2
+#define PHASE_ESTABLISH 3
+#define PHASE_AUTHENTICATE 4
+#define PHASE_CALLBACK 5
+#define PHASE_NETWORK 6
+#define PHASE_TERMINATE 7
+#define PHASE_HOLDOFF 8
+
+/*
+ * The following struct gives the addresses of procedures to call
+ * for a particular protocol.
+ */
+struct protent {
+ u_short protocol; /* PPP protocol number */
+ /* Initialization procedure */
+ void (*init)(int unit);
+ /* Process a received packet */
+ void (*input)(int unit, u_char *pkt, int len);
+ /* Process a received protocol-reject */
+ void (*protrej)(int unit);
+ /* Lower layer has come up */
+ void (*lowerup)(int unit);
+ /* Lower layer has gone down */
+ void (*lowerdown)(int unit);
+ /* Open the protocol */
+ void (*open)(int unit);
+ /* Close the protocol */
+ void (*close)(int unit, char *reason);
+ /* Print a packet in readable form */
+ int (*printpkt)(u_char *pkt, int len,
+ void (*printer)(void *, char *, ...),
+ void *arg);
+ /* Process a received data packet */
+ void (*datainput)(int unit, u_char *pkt, int len);
+ int enabled_flag; /* 0 iff protocol is disabled */
+ char *name; /* Text name of protocol */
+ /* Check requested options, assign defaults */
+ void (*check_options)(void);
+ /* Configure interface for demand-dial */
+ int (*demand_conf)(int unit);
+ /* Say whether to bring up link for this pkt */
+ int (*active_pkt)(u_char *pkt, int len);
+};
+
+/* Table of pointers to supported protocols */
+extern struct protent *protocols[];
+
+/*
+ * Prototypes.
+ */
+
+/* Procedures exported from main.c. */
+void detach(void); /* Detach from controlling tty */
+void die(int); /* Cleanup and exit */
+void quit(void); /* like die(1) */
+void novm(char *); /* Say we ran out of memory, and die */
+void timeout(void (*func)(void *), void *arg, int t);
+ /* Call func(arg) after t seconds */
+void untimeout(void (*func)(void *), void *arg);
+ /* Cancel call to func(arg) */
+int run_program(char *prog, char **args, int must_exist);
+ /* Run program prog with args in child */
+void demuxprotrej(int, int);
+ /* Demultiplex a Protocol-Reject */
+void format_packet(u_char *, int, void (*) (void *, char *, ...),
+ void *); /* Format a packet in human-readable form */
+void log_packet(u_char *, int, char *, int);
+ /* Format a packet and log it with syslog */
+void print_string(char *, int, void (*) (void *, char *, ...),
+ void *); /* Format a string for output */
+int fmtmsg(char *, int, char *, ...); /* sprintf++ */
+int vfmtmsg(char *, int, char *, va_list); /* vsprintf++ */
+void script_setenv(char *, char *); /* set script env var */
+void script_unsetenv(char *); /* unset script env var */
+
+/* Procedures exported from auth.c */
+void link_required(int); /* we are starting to use the link */
+void link_terminated(int); /* we are finished with the link */
+void link_down(int); /* the LCP layer has left the Opened state */
+void link_established(int); /* the link is up; authenticate now */
+void np_up(int, int); /* a network protocol has come up */
+void np_down(int, int); /* a network protocol has gone down */
+void np_finished(int, int); /* a network protocol no longer needs link */
+void auth_peer_fail(int, int);
+ /* peer failed to authenticate itself */
+void auth_peer_success(int, int, char *, int);
+ /* peer successfully authenticated itself */
+void auth_withpeer_fail(int, int);
+ /* we failed to authenticate ourselves */
+void auth_withpeer_success(int, int);
+ /* we successfully authenticated ourselves */
+void auth_check_options(void);
+ /* check authentication options supplied */
+void auth_reset(int); /* check what secrets we have */
+int check_passwd(int, char *, int, char *, int, char **, int *);
+ /* Check peer-supplied username/password */
+int get_secret(int, char *, char *, char *, int *, int);
+ /* get "secret" for chap */
+int auth_ip_addr(int, u_int32_t);
+ /* check if IP address is authorized */
+int bad_ip_adrs(u_int32_t);
+ /* check if IP address is unreasonable */
+void check_access(FILE *, char *);
+ /* check permissions on secrets file */
+
+/* Procedures exported from demand.c */
+void demand_conf(void); /* config interface(s) for demand-dial */
+void demand_block(void); /* set all NPs to queue up packets */
+void demand_unblock(void); /* set all NPs to pass packets */
+void demand_discard(void); /* set all NPs to discard packets */
+void demand_rexmit(int); /* retransmit saved frames for an NP */
+int loop_chars(unsigned char *, int); /* process chars from loopback */
+int loop_frame(unsigned char *, int); /* process frame from loopback */
+
+/* Procedures exported from sys-*.c */
+void sys_init(void); /* Do system-dependent initialization */
+void sys_cleanup(void); /* Restore system state before exiting */
+void sys_check_options(void); /* Check options specified */
+void sys_close(void); /* Clean up in a child before execing */
+int ppp_available(void); /* Test whether ppp kernel support exists */
+void open_ppp_loopback(void); /* Open loopback for demand-dialling */
+void establish_ppp(int); /* Turn serial port into a ppp interface */
+void restore_loop(void); /* Transfer ppp unit back to loopback */
+void disestablish_ppp(int); /* Restore port to normal operation */
+void clean_check(void); /* Check if line was 8-bit clean */
+void set_up_tty(int, int); /* Set up port's speed, parameters, etc. */
+void restore_tty(int); /* Restore port's original parameters */
+void setdtr(int, int); /* Raise or lower port's DTR line */
+void output(int, u_char *, int); /* Output a PPP packet */
+void wait_input(struct timeval *);
+ /* Wait for input, with timeout */
+void wait_loop_output(struct timeval *);
+ /* Wait for pkt from loopback, with timeout */
+void wait_time(struct timeval *); /* Wait for given length of time */
+int read_packet(u_char *); /* Read PPP packet */
+int get_loop_output(void); /* Read pkts from loopback */
+void ppp_send_config(int, int, u_int32_t, int, int);
+ /* Configure i/f transmit parameters */
+void ppp_set_xaccm(int, ext_accm);
+ /* Set extended transmit ACCM */
+void ppp_recv_config(int, int, u_int32_t, int, int);
+ /* Configure i/f receive parameters */
+int ccp_test(int, u_char *, int, int);
+ /* Test support for compression scheme */
+void ccp_flags_set(int, int, int);
+ /* Set kernel CCP state */
+int ccp_fatal_error(int); /* Test for fatal decomp error in kernel */
+int get_idle_time(int, struct ppp_idle *);
+ /* Find out how long link has been idle */
+int sifvjcomp(int, int, int, int);
+ /* Configure VJ TCP header compression */
+int sifup(int); /* Configure i/f up (for IP) */
+int sifnpmode(int u, int proto, enum NPmode mode);
+ /* Set mode for handling packets for proto */
+int sifdown(int); /* Configure i/f down (for IP) */
+int sifaddr(int, u_int32_t, u_int32_t, u_int32_t);
+ /* Configure IP addresses for i/f */
+int cifaddr(int, u_int32_t, u_int32_t);
+ /* Reset i/f IP addresses */
+#ifdef INET6
+int sif6addr(int, eui64_t, eui64_t);
+ /* Configure IPv6 addresses for i/f */
+int cif6addr(int, eui64_t, eui64_t);
+ /* Remove an IPv6 address from i/f */
+#endif
+int sifdefaultroute(int, u_int32_t, u_int32_t);
+ /* Create default route through i/f */
+int cifdefaultroute(int, u_int32_t, u_int32_t);
+ /* Delete default route through i/f */
+int sifproxyarp(int, u_int32_t);
+ /* Add proxy ARP entry for peer */
+int cifproxyarp(int, u_int32_t);
+ /* Delete proxy ARP entry for peer */
+u_int32_t GetMask(u_int32_t); /* Get appropriate netmask for address */
+int lock(char *); /* Create lock file for device */
+void unlock(void); /* Delete previously-created lock file */
+int daemon(int, int); /* Detach us from terminal session */
+void logwtmp(const char *, const char *, const char *);
+ /* Write entry to wtmp file */
+int get_host_seed(void); /* Get host-dependent random number seed */
+#ifdef PPP_FILTER
+int set_filters(struct bpf_program *pass, struct bpf_program *active);
+ /* Set filter programs in kernel */
+#endif
+
+/* Procedures exported from options.c */
+int parse_args(int argc, char **argv);
+ /* Parse options from arguments given */
+void usage(void); /* Print a usage message */
+int options_from_file(char *filename, int must_exist, int check_prot,
+ int privileged);
+ /* Parse options from an options file */
+int options_from_user(void); /* Parse options from user's .ppprc */
+int options_for_tty(void); /* Parse options from /etc/ppp/options.tty */
+void scan_args(int argc, char **argv);
+ /* Look for tty name in command-line args */
+int getword(FILE *f, char *word, int *newlinep, char *filename);
+ /* Read a word from a file */
+void option_error(char *fmt, ...);
+ /* Print an error message about an option */
+int setipaddr(char *); /* set IP addresses */
+
+
+/*
+ * This structure is used to store information about certain
+ * options, such as where the option value came from (/etc/ppp/options,
+ * command line, etc.) and whether it came from a privileged source.
+ */
+
+struct option_info {
+ int priv; /* was value set by sysadmin? */
+ char *source; /* where option came from */
+};
+
+extern struct option_info auth_req_info;
+extern struct option_info connector_info;
+extern struct option_info disconnector_info;
+extern struct option_info welcomer_info;
+extern struct option_info devnam_info;
+
+/*
+ * Inline versions of get/put char/short/long.
+ * Pointer is advanced; we assume that both arguments
+ * are lvalues and will already be in registers.
+ * cp MUST be u_char *.
+ */
+#define GETCHAR(c, cp) { \
+ (c) = *(cp)++; \
+}
+#define PUTCHAR(c, cp) { \
+ *(cp)++ = (u_char) (c); \
+}
+
+
+#define GETSHORT(s, cp) { \
+ (s) = *(cp)++ << 8; \
+ (s) |= *(cp)++; \
+}
+#define PUTSHORT(s, cp) { \
+ *(cp)++ = (u_char) ((s) >> 8); \
+ *(cp)++ = (u_char) (s); \
+}
+
+#define GETLONG(l, cp) { \
+ (l) = *(cp)++ << 8; \
+ (l) |= *(cp)++; (l) <<= 8; \
+ (l) |= *(cp)++; (l) <<= 8; \
+ (l) |= *(cp)++; \
+}
+#define PUTLONG(l, cp) { \
+ *(cp)++ = (u_char) ((l) >> 24); \
+ *(cp)++ = (u_char) ((l) >> 16); \
+ *(cp)++ = (u_char) ((l) >> 8); \
+ *(cp)++ = (u_char) (l); \
+}
+
+#define INCPTR(n, cp) ((cp) += (n))
+#define DECPTR(n, cp) ((cp) -= (n))
+
+#undef FALSE
+#define FALSE 0
+#undef TRUE
+#define TRUE 1
+
+/*
+ * System dependent definitions for user-level 4.3BSD UNIX implementation.
+ */
+
+#define DEMUXPROTREJ(u, p) demuxprotrej(u, p)
+
+#define TIMEOUT(r, f, t) timeout((r), (f), (t))
+#define UNTIMEOUT(r, f) untimeout((r), (f))
+
+#define BCOPY(s, d, l) memcpy(d, s, l)
+#define BZERO(s, n) memset(s, 0, n)
+#define EXIT(u) quit()
+
+#define PRINTMSG(m, l) { m[l] = '\0'; syslog(LOG_INFO, "Remote message: %s", m); }
+
+/*
+ * MAKEHEADER - Add Header fields to a packet.
+ */
+#define MAKEHEADER(p, t) { \
+ PUTCHAR(PPP_ALLSTATIONS, p); \
+ PUTCHAR(PPP_UI, p); \
+ PUTSHORT(t, p); }
+
+
+#ifdef DEBUGALL
+#define DEBUGMAIN 1
+#define DEBUGFSM 1
+#define DEBUGLCP 1
+#define DEBUGIPCP 1
+#define DEBUGUPAP 1
+#define DEBUGCHAP 1
+#endif
+
+#ifndef LOG_PPP /* we use LOG_LOCAL2 for syslog by default */
+#if defined(DEBUGMAIN) || defined(DEBUGFSM) || defined(DEBUGSYS) \
+ || defined(DEBUGLCP) || defined(DEBUGIPCP) || defined(DEBUGUPAP) \
+ || defined(DEBUGCHAP) || defined(DEBUG)
+#define LOG_PPP LOG_LOCAL2
+#else
+#define LOG_PPP LOG_DAEMON
+#endif
+#endif /* LOG_PPP */
+
+#ifdef DEBUGMAIN
+#define MAINDEBUG(x) if (debug) syslog x
+#else
+#define MAINDEBUG(x)
+#endif
+
+#ifdef DEBUGSYS
+#define SYSDEBUG(x) if (debug) syslog x
+#else
+#define SYSDEBUG(x)
+#endif
+
+#ifdef DEBUGFSM
+#define FSMDEBUG(x) if (debug) syslog x
+#else
+#define FSMDEBUG(x)
+#endif
+
+#ifdef DEBUGLCP
+#define LCPDEBUG(x) if (debug) syslog x
+#else
+#define LCPDEBUG(x)
+#endif
+
+#ifdef DEBUGIPCP
+#define IPCPDEBUG(x) if (debug) syslog x
+#else
+#define IPCPDEBUG(x)
+#endif
+
+#ifdef DEBUGIPV6CP
+#define IPV6CPDEBUG(x) if (debug) syslog x
+#else
+#define IPV6CPDEBUG(x)
+#endif
+
+#ifdef DEBUGUPAP
+#define UPAPDEBUG(x) if (debug) syslog x
+#else
+#define UPAPDEBUG(x)
+#endif
+
+#ifdef DEBUGCHAP
+#define CHAPDEBUG(x) if (debug) syslog x
+#else
+#define CHAPDEBUG(x)
+#endif
+
+#ifdef DEBUGIPXCP
+#define IPXCPDEBUG(x) if (debug) syslog x
+#else
+#define IPXCPDEBUG(x)
+#endif
+
+#ifndef SIGTYPE
+#if defined(sun) || defined(SYSV) || defined(POSIX_SOURCE)
+#define SIGTYPE void
+#else
+#define SIGTYPE int
+#endif /* defined(sun) || defined(SYSV) || defined(POSIX_SOURCE) */
+#endif /* SIGTYPE */
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b)? (a): (b))
+#endif
+#ifndef MAX
+#define MAX(a, b) ((a) > (b)? (a): (b))
+#endif
+
+#endif /* __PPP_H__ */
diff --git a/usr.sbin/pppd/sys-bsd.c b/usr.sbin/pppd/sys-bsd.c
new file mode 100644
index 0000000..cbde7a4
--- /dev/null
+++ b/usr.sbin/pppd/sys-bsd.c
@@ -0,0 +1,1697 @@
+/*
+ * sys-bsd.c - System-dependent procedures for setting up
+ * PPP interfaces on bsd-4.4-ish systems (including 386BSD, NetBSD, etc.)
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * Copyright (c) 1995 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 Carnegie Mellon University and The Australian National University.
+ * The names of the Universities 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+/* $NetBSD: sys-bsd.c,v 1.1.1.3 1997/09/26 18:53:04 christos Exp $ */
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/module.h>
+#ifdef NetBSD1_2
+#include <util.h>
+#endif
+#ifdef PPP_FILTER
+#include <net/bpf.h>
+#endif
+
+#include <net/if.h>
+#include <net/ppp_defs.h>
+#include <net/if_ppp.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#include <net/if_var.h>
+#include <netinet6/in6_var.h>
+#include <netinet6/nd6.h>
+#include <ifaddrs.h>
+
+#ifdef IPX_CHANGE
+#include <netipx/ipx.h>
+#endif
+
+#if RTM_VERSION >= 3
+#include <sys/param.h>
+#if defined(NetBSD) && (NetBSD >= 199703)
+#include <netinet/if_inarp.h>
+#else /* NetBSD 1.2D or later */
+#ifdef __FreeBSD__
+#include <netinet/if_ether.h>
+#else
+#include <net/if_ether.h>
+#endif
+#endif
+#endif
+
+#include <ifaddrs.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "ipcp.h"
+
+static int initdisc = -1; /* Initial TTY discipline for ppp_fd */
+static int initfdflags = -1; /* Initial file descriptor flags for ppp_fd */
+static int ppp_fd = -1; /* fd which is set to PPP discipline */
+static int rtm_seq;
+
+static int restore_term; /* 1 => we've munged the terminal */
+static struct termios inittermios; /* Initial TTY termios */
+static struct winsize wsinfo; /* Initial window size info */
+
+static char *lock_file; /* name of lock file created */
+
+static int loop_slave = -1;
+static int loop_master;
+static char loop_name[20];
+
+static unsigned char inbuf[512]; /* buffer for chars read from loopback */
+
+static int sockfd; /* socket for doing interface ioctls */
+#ifdef INET6
+static int sock6_fd = -1; /* socket for doing ipv6 interface ioctls */
+#endif /* INET6 */
+
+static int if_is_up; /* the interface is currently up */
+static u_int32_t ifaddrs[2]; /* local and remote addresses we set */
+static u_int32_t default_route_gateway; /* gateway addr for default route */
+static u_int32_t proxy_arp_addr; /* remote addr for proxy arp */
+
+/* Prototypes for procedures local to this file. */
+static int dodefaultroute(u_int32_t, int);
+static int get_ether_addr(u_int32_t, struct sockaddr_dl *);
+
+
+/*
+ * sys_init - System-dependent initialization.
+ */
+void
+sys_init()
+{
+ /* Get an internet socket for doing socket ioctl's on. */
+ if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "Couldn't create IP socket: %m");
+ die(1);
+ }
+
+#ifdef INET6
+ if ((sock6_fd = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ /* check it at runtime */
+ sock6_fd = -1;
+ }
+#endif
+}
+
+/*
+ * sys_cleanup - restore any system state we modified before exiting:
+ * mark the interface down, delete default route and/or proxy arp entry.
+ * This should call die() because it's called from die().
+ */
+void
+sys_cleanup()
+{
+ struct ifreq ifr;
+
+ if (if_is_up) {
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) >= 0
+ && ((ifr.ifr_flags & IFF_UP) != 0)) {
+ ifr.ifr_flags &= ~IFF_UP;
+ ioctl(sockfd, SIOCSIFFLAGS, &ifr);
+ }
+ }
+ if (ifaddrs[0] != 0)
+ cifaddr(0, ifaddrs[0], ifaddrs[1]);
+ if (default_route_gateway)
+ cifdefaultroute(0, 0, default_route_gateway);
+ if (proxy_arp_addr)
+ cifproxyarp(0, proxy_arp_addr);
+}
+
+/*
+ * sys_close - Clean up in a child process before execing.
+ */
+void
+sys_close()
+{
+ if (sockfd >= 0)
+ close(sockfd);
+#ifdef INET6
+ if (sock6_fd >= 0)
+ close(sock6_fd);
+#endif
+ if (loop_slave >= 0) {
+ close(loop_slave);
+ close(loop_master);
+ }
+}
+
+/*
+ * sys_check_options - check the options that the user specified
+ */
+void
+sys_check_options()
+{
+}
+
+/*
+ * ppp_available - check whether the system has the ppp module loaded
+ * or compiled in. If it doesn't, and we're actually root (not just SUID
+ * root) try loading it before giving up.
+ */
+int
+ppp_available()
+{
+ const char *modname = "if_ppp";
+ extern char *no_ppp_msg;
+
+ if (modfind(modname) != -1) {
+ return 1;
+ }
+
+ if (getuid() == 0 && kldload(modname) != -1)
+ return 1;
+
+ no_ppp_msg = "\
+This system lacks kernel support for PPP. To include PPP support\n\
+in the kernel, please add \"device ppp\" to your kernel config or \n\
+load the if_ppp module.\n";
+
+ return 0;
+}
+
+/*
+ * establish_ppp - Turn the serial port into a ppp interface.
+ */
+void
+establish_ppp(fd)
+ int fd;
+{
+ int pppdisc = PPPDISC;
+ int x;
+
+ if (demand) {
+ /*
+ * Demand mode - prime the old ppp device to relinquish the unit.
+ */
+ if (ioctl(ppp_fd, PPPIOCXFERUNIT, 0) < 0) {
+ syslog(LOG_ERR, "ioctl(transfer ppp unit): %m");
+ die(1);
+ }
+ }
+
+ /*
+ * Save the old line discipline of fd, and set it to PPP.
+ */
+ if (ioctl(fd, TIOCGETD, &initdisc) < 0) {
+ syslog(LOG_ERR, "ioctl(TIOCGETD): %m");
+ die(1);
+ }
+ if (ioctl(fd, TIOCSETD, &pppdisc) < 0) {
+ syslog(LOG_ERR, "ioctl(TIOCSETD): %m");
+ die(1);
+ }
+
+ if (!demand) {
+ /*
+ * Find out which interface we were given.
+ */
+ if (ioctl(fd, PPPIOCGUNIT, &ifunit) < 0) {
+ syslog(LOG_ERR, "ioctl(PPPIOCGUNIT): %m");
+ die(1);
+ }
+ } else {
+ /*
+ * Check that we got the same unit again.
+ */
+ if (ioctl(fd, PPPIOCGUNIT, &x) < 0) {
+ syslog(LOG_ERR, "ioctl(PPPIOCGUNIT): %m");
+ die(1);
+ }
+ if (x != ifunit) {
+ syslog(LOG_ERR, "transfer_ppp failed: wanted unit %d, got %d",
+ ifunit, x);
+ die(1);
+ }
+ x = TTYDISC;
+ ioctl(loop_slave, TIOCSETD, &x);
+ }
+
+ ppp_fd = fd;
+
+ /*
+ * Enable debug in the driver if requested.
+ */
+ if (kdebugflag) {
+ if (ioctl(fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
+ syslog(LOG_WARNING, "ioctl (PPPIOCGFLAGS): %m");
+ } else {
+ x |= (kdebugflag & 0xFF) * SC_DEBUG;
+ if (ioctl(fd, PPPIOCSFLAGS, (caddr_t) &x) < 0)
+ syslog(LOG_WARNING, "ioctl(PPPIOCSFLAGS): %m");
+ }
+ }
+
+ /*
+ * Set device for non-blocking reads.
+ */
+ if ((initfdflags = fcntl(fd, F_GETFL)) == -1
+ || fcntl(fd, F_SETFL, initfdflags | O_NONBLOCK) == -1) {
+ syslog(LOG_WARNING, "Couldn't set device to non-blocking mode: %m");
+ }
+}
+
+/*
+ * restore_loop - reattach the ppp unit to the loopback.
+ */
+void
+restore_loop()
+{
+ int x;
+
+ /*
+ * Transfer the ppp interface back to the loopback.
+ */
+ if (ioctl(ppp_fd, PPPIOCXFERUNIT, 0) < 0) {
+ syslog(LOG_ERR, "ioctl(transfer ppp unit): %m");
+ die(1);
+ }
+ x = PPPDISC;
+ if (ioctl(loop_slave, TIOCSETD, &x) < 0) {
+ syslog(LOG_ERR, "ioctl(TIOCSETD): %m");
+ die(1);
+ }
+
+ /*
+ * Check that we got the same unit again.
+ */
+ if (ioctl(loop_slave, PPPIOCGUNIT, &x) < 0) {
+ syslog(LOG_ERR, "ioctl(PPPIOCGUNIT): %m");
+ die(1);
+ }
+ if (x != ifunit) {
+ syslog(LOG_ERR, "transfer_ppp failed: wanted unit %d, got %d",
+ ifunit, x);
+ die(1);
+ }
+ ppp_fd = loop_slave;
+}
+
+
+/*
+ * disestablish_ppp - Restore the serial port to normal operation.
+ * This shouldn't call die() because it's called from die().
+ */
+void
+disestablish_ppp(fd)
+ int fd;
+{
+ /* Reset non-blocking mode on fd. */
+ if (initfdflags != -1 && fcntl(fd, F_SETFL, initfdflags) < 0)
+ syslog(LOG_WARNING, "Couldn't restore device fd flags: %m");
+ initfdflags = -1;
+
+ /* Restore old line discipline. */
+ if (initdisc >= 0 && ioctl(fd, TIOCSETD, &initdisc) < 0)
+ syslog(LOG_ERR, "ioctl(TIOCSETD): %m");
+ initdisc = -1;
+
+ if (fd == ppp_fd)
+ ppp_fd = -1;
+}
+
+/*
+ * Check whether the link seems not to be 8-bit clean.
+ */
+void
+clean_check()
+{
+ int x;
+ char *s;
+
+ if (ioctl(ppp_fd, PPPIOCGFLAGS, (caddr_t) &x) == 0) {
+ s = NULL;
+ switch (~x & (SC_RCV_B7_0|SC_RCV_B7_1|SC_RCV_EVNP|SC_RCV_ODDP)) {
+ case SC_RCV_B7_0:
+ s = "bit 7 set to 1";
+ break;
+ case SC_RCV_B7_1:
+ s = "bit 7 set to 0";
+ break;
+ case SC_RCV_EVNP:
+ s = "odd parity";
+ break;
+ case SC_RCV_ODDP:
+ s = "even parity";
+ break;
+ }
+ if (s != NULL) {
+ syslog(LOG_WARNING, "Serial link is not 8-bit clean:");
+ syslog(LOG_WARNING, "All received characters had %s", s);
+ }
+ }
+}
+
+/*
+ * set_up_tty: Set up the serial port on `fd' for 8 bits, no parity,
+ * at the requested speed, etc. If `local' is true, set CLOCAL
+ * regardless of whether the modem option was specified.
+ *
+ * For *BSD, we assume that speed_t values numerically equal bits/second.
+ */
+void
+set_up_tty(fd, local)
+ int fd, local;
+{
+ struct termios tios;
+
+ if (tcgetattr(fd, &tios) < 0) {
+ syslog(LOG_ERR, "tcgetattr: %m");
+ die(1);
+ }
+
+ if (!restore_term) {
+ inittermios = tios;
+ ioctl(fd, TIOCGWINSZ, &wsinfo);
+ }
+
+ tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CLOCAL);
+ if (crtscts > 0 && !local)
+ tios.c_cflag |= CRTSCTS;
+ else if (crtscts < 0)
+ tios.c_cflag &= ~CRTSCTS;
+
+ tios.c_cflag |= CS8 | CREAD | HUPCL;
+ if (local || !modem)
+ tios.c_cflag |= CLOCAL;
+ tios.c_iflag = IGNBRK | IGNPAR;
+ tios.c_oflag = 0;
+ tios.c_lflag = 0;
+ tios.c_cc[VMIN] = 1;
+ tios.c_cc[VTIME] = 0;
+
+ if (crtscts == -2) {
+ tios.c_iflag |= IXON | IXOFF;
+ tios.c_cc[VSTOP] = 0x13; /* DC3 = XOFF = ^S */
+ tios.c_cc[VSTART] = 0x11; /* DC1 = XON = ^Q */
+ }
+
+ if (inspeed) {
+ cfsetospeed(&tios, inspeed);
+ cfsetispeed(&tios, inspeed);
+ } else {
+ inspeed = cfgetospeed(&tios);
+ /*
+ * We can't proceed if the serial port speed is 0,
+ * since that implies that the serial port is disabled.
+ */
+ if (inspeed == 0) {
+ syslog(LOG_ERR, "Baud rate for %s is 0; need explicit baud rate",
+ devnam);
+ die(1);
+ }
+ }
+ baud_rate = inspeed;
+
+ if (tcsetattr(fd, TCSAFLUSH, &tios) < 0) {
+ syslog(LOG_ERR, "tcsetattr: %m");
+ die(1);
+ }
+
+ restore_term = 1;
+}
+
+/*
+ * restore_tty - restore the terminal to the saved settings.
+ */
+void
+restore_tty(fd)
+ int fd;
+{
+ if (restore_term) {
+ if (!default_device) {
+ /*
+ * Turn off echoing, because otherwise we can get into
+ * a loop with the tty and the modem echoing to each other.
+ * We presume we are the sole user of this tty device, so
+ * when we close it, it will revert to its defaults anyway.
+ */
+ inittermios.c_lflag &= ~(ECHO | ECHONL);
+ }
+ if (tcsetattr(fd, TCSAFLUSH, &inittermios) < 0)
+ if (errno != ENXIO)
+ syslog(LOG_WARNING, "tcsetattr: %m");
+ ioctl(fd, TIOCSWINSZ, &wsinfo);
+ restore_term = 0;
+ }
+}
+
+/*
+ * setdtr - control the DTR line on the serial port.
+ * This is called from die(), so it shouldn't call die().
+ */
+void
+setdtr(fd, on)
+int fd, on;
+{
+ int modembits = TIOCM_DTR;
+
+ ioctl(fd, (on? TIOCMBIS: TIOCMBIC), &modembits);
+}
+
+#ifdef INET6
+/*
+ * sif6addr - Config the interface with an IPv6 link-local address
+ */
+int
+sif6addr(int unit, eui64_t our_eui64, eui64_t his_eui64)
+{
+ int ifindex;
+ struct in6_aliasreq addreq6;
+
+ if (sock6_fd < 0) {
+ syslog(LOG_ERR, "No IPv6 socket available");
+ die(1);
+ /*NOTREACHED*/
+ }
+
+ /* actually, this part is not kame local - RFC2553 conformant */
+ ifindex = if_nametoindex(ifname);
+ if (ifindex == 0) {
+ syslog(LOG_ERR, "sifaddr6: no interface %s", ifname);
+ return 0;
+ }
+
+ memset(&addreq6, 0, sizeof(addreq6));
+ strlcpy(addreq6.ifra_name, ifname, sizeof(addreq6.ifra_name));
+
+ /* my addr */
+ addreq6.ifra_addr.sin6_family = AF_INET6;
+ addreq6.ifra_addr.sin6_len = sizeof(struct sockaddr_in6);
+ addreq6.ifra_addr.sin6_addr.s6_addr[0] = 0xfe;
+ addreq6.ifra_addr.sin6_addr.s6_addr[1] = 0x80;
+ memcpy(&addreq6.ifra_addr.sin6_addr.s6_addr[8], &our_eui64,
+ sizeof(our_eui64));
+ /* KAME ifindex hack */
+ *(u_int16_t *)&addreq6.ifra_addr.sin6_addr.s6_addr[2] = htons(ifindex);
+
+ /* his addr */
+ addreq6.ifra_dstaddr.sin6_family = AF_INET6;
+ addreq6.ifra_dstaddr.sin6_len = sizeof(struct sockaddr_in6);
+ addreq6.ifra_dstaddr.sin6_addr.s6_addr[0] = 0xfe;
+ addreq6.ifra_dstaddr.sin6_addr.s6_addr[1] = 0x80;
+ memcpy(&addreq6.ifra_dstaddr.sin6_addr.s6_addr[8], &his_eui64,
+ sizeof(our_eui64));
+ /* KAME ifindex hack */
+ *(u_int16_t *)&addreq6.ifra_dstaddr.sin6_addr.s6_addr[2] = htons(ifindex);
+
+ /* prefix mask: 128bit */
+ addreq6.ifra_prefixmask.sin6_family = AF_INET6;
+ addreq6.ifra_prefixmask.sin6_len = sizeof(struct sockaddr_in6);
+ memset(&addreq6.ifra_prefixmask.sin6_addr, 0xff,
+ sizeof(addreq6.ifra_prefixmask.sin6_addr));
+
+ /* address lifetime (infty) */
+ addreq6.ifra_lifetime.ia6t_pltime = ND6_INFINITE_LIFETIME;
+ addreq6.ifra_lifetime.ia6t_vltime = ND6_INFINITE_LIFETIME;
+
+ if (ioctl(sock6_fd, SIOCAIFADDR_IN6, &addreq6) < 0) {
+ syslog(LOG_ERR, "sif6addr: ioctl(SIOCAIFADDR_IN6): %m");
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/*
+ * cif6addr - Remove IPv6 address from interface
+ */
+int
+cif6addr(int unit, eui64_t our_eui64, eui64_t his_eui64)
+{
+ int ifindex;
+ struct in6_ifreq delreq6;
+
+ if (sock6_fd < 0) {
+ syslog(LOG_ERR, "No IPv6 socket available");
+ die(1);
+ /*NOTREACHED*/
+ }
+
+ /* actually, this part is not kame local - RFC2553 conformant */
+ ifindex = if_nametoindex(ifname);
+ if (ifindex == 0) {
+ syslog(LOG_ERR, "cifaddr6: no interface %s", ifname);
+ return 0;
+ }
+
+ memset(&delreq6, 0, sizeof(delreq6));
+ strlcpy(delreq6.ifr_name, ifname, sizeof(delreq6.ifr_name));
+
+ /* my addr */
+ delreq6.ifr_ifru.ifru_addr.sin6_family = AF_INET6;
+ delreq6.ifr_ifru.ifru_addr.sin6_len = sizeof(struct sockaddr_in6);
+ delreq6.ifr_ifru.ifru_addr.sin6_addr.s6_addr[0] = 0xfe;
+ delreq6.ifr_ifru.ifru_addr.sin6_addr.s6_addr[1] = 0x80;
+ memcpy(&delreq6.ifr_ifru.ifru_addr.sin6_addr.s6_addr[8], &our_eui64,
+ sizeof(our_eui64));
+ /* KAME ifindex hack */
+ *(u_int16_t *)&delreq6.ifr_ifru.ifru_addr.sin6_addr.s6_addr[2] =
+ htons(ifindex);
+
+ if (ioctl(sock6_fd, SIOCDIFADDR_IN6, &delreq6) < 0) {
+ syslog(LOG_ERR, "cif6addr: ioctl(SIOCDIFADDR_IN6): %m");
+ return 0;
+ }
+
+ return 1;
+}
+#endif /* INET6 */
+
+/*
+ * open_ppp_loopback - open the device we use for getting
+ * packets in demand mode, and connect it to a ppp interface.
+ * Here we use a pty.
+ */
+void
+open_ppp_loopback()
+{
+ int flags;
+ struct termios tios;
+ int pppdisc = PPPDISC;
+
+ if (openpty(&loop_master, &loop_slave, loop_name, NULL, NULL) < 0) {
+ syslog(LOG_ERR, "No free pty for loopback");
+ die(1);
+ }
+ SYSDEBUG((LOG_DEBUG, "using %s for loopback", loop_name));
+
+ if (tcgetattr(loop_slave, &tios) == 0) {
+ tios.c_cflag &= ~(CSIZE | CSTOPB | PARENB);
+ tios.c_cflag |= CS8 | CREAD;
+ tios.c_iflag = IGNPAR;
+ tios.c_oflag = 0;
+ tios.c_lflag = 0;
+ if (tcsetattr(loop_slave, TCSAFLUSH, &tios) < 0)
+ syslog(LOG_WARNING, "couldn't set attributes on loopback: %m");
+ }
+
+ if ((flags = fcntl(loop_master, F_GETFL)) != -1)
+ if (fcntl(loop_master, F_SETFL, flags | O_NONBLOCK) == -1)
+ syslog(LOG_WARNING, "couldn't set loopback to nonblock: %m");
+
+ ppp_fd = loop_slave;
+ if (ioctl(ppp_fd, TIOCSETD, &pppdisc) < 0) {
+ syslog(LOG_ERR, "ioctl(TIOCSETD): %m");
+ die(1);
+ }
+
+ /*
+ * Find out which interface we were given.
+ */
+ if (ioctl(ppp_fd, PPPIOCGUNIT, &ifunit) < 0) {
+ syslog(LOG_ERR, "ioctl(PPPIOCGUNIT): %m");
+ die(1);
+ }
+
+ /*
+ * Enable debug in the driver if requested.
+ */
+ if (kdebugflag) {
+ if (ioctl(ppp_fd, PPPIOCGFLAGS, (caddr_t) &flags) < 0) {
+ syslog(LOG_WARNING, "ioctl (PPPIOCGFLAGS): %m");
+ } else {
+ flags |= (kdebugflag & 0xFF) * SC_DEBUG;
+ if (ioctl(ppp_fd, PPPIOCSFLAGS, (caddr_t) &flags) < 0)
+ syslog(LOG_WARNING, "ioctl(PPPIOCSFLAGS): %m");
+ }
+ }
+
+}
+
+
+/*
+ * output - Output PPP packet.
+ */
+void
+output(unit, p, len)
+ int unit;
+ u_char *p;
+ int len;
+{
+ if (debug)
+ log_packet(p, len, "sent ", LOG_DEBUG);
+
+ if (write(ttyfd, p, len) < 0) {
+ if (errno != EIO)
+ syslog(LOG_ERR, "write: %m");
+ }
+}
+
+
+/*
+ * wait_input - wait until there is data available on ttyfd,
+ * for the length of time specified by *timo (indefinite
+ * if timo is NULL).
+ */
+void
+wait_input(timo)
+ struct timeval *timo;
+{
+ fd_set ready;
+ int n;
+
+ if (ttyfd >= FD_SETSIZE) {
+ syslog(LOG_ERR, "descriptor too big");
+ die(1);
+ }
+ FD_ZERO(&ready);
+ FD_SET(ttyfd, &ready);
+ n = select(ttyfd+1, &ready, NULL, &ready, timo);
+ if (n < 0 && errno != EINTR) {
+ syslog(LOG_ERR, "select: %m");
+ die(1);
+ }
+}
+
+
+/*
+ * wait_loop_output - wait until there is data available on the
+ * loopback, for the length of time specified by *timo (indefinite
+ * if timo is NULL).
+ */
+void
+wait_loop_output(timo)
+ struct timeval *timo;
+{
+ fd_set ready;
+ int n;
+
+ if (loop_master >= FD_SETSIZE) {
+ syslog(LOG_ERR, "descriptor too big");
+ die(1);
+ }
+ FD_ZERO(&ready);
+ FD_SET(loop_master, &ready);
+ n = select(loop_master + 1, &ready, NULL, &ready, timo);
+ if (n < 0 && errno != EINTR) {
+ syslog(LOG_ERR, "select: %m");
+ die(1);
+ }
+}
+
+
+/*
+ * wait_time - wait for a given length of time or until a
+ * signal is received.
+ */
+void
+wait_time(timo)
+ struct timeval *timo;
+{
+ int n;
+
+ n = select(0, NULL, NULL, NULL, timo);
+ if (n < 0 && errno != EINTR) {
+ syslog(LOG_ERR, "select: %m");
+ die(1);
+ }
+}
+
+
+/*
+ * read_packet - get a PPP packet from the serial device.
+ */
+int
+read_packet(buf)
+ u_char *buf;
+{
+ int len;
+
+ if ((len = read(ttyfd, buf, PPP_MTU + PPP_HDRLEN)) < 0) {
+ if (errno == EWOULDBLOCK || errno == EINTR)
+ return -1;
+ syslog(LOG_ERR, "read: %m");
+ die(1);
+ }
+ return len;
+}
+
+
+/*
+ * get_loop_output - read characters from the loopback, form them
+ * into frames, and detect when we want to bring the real link up.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ */
+int
+get_loop_output()
+{
+ int rv = 0;
+ int n;
+
+ while ((n = read(loop_master, inbuf, sizeof(inbuf))) >= 0) {
+ if (loop_chars(inbuf, n))
+ rv = 1;
+ }
+
+ if (n == 0) {
+ syslog(LOG_ERR, "eof on loopback");
+ die(1);
+ } else if (errno != EWOULDBLOCK){
+ syslog(LOG_ERR, "read from loopback: %m");
+ die(1);
+ }
+
+ return rv;
+}
+
+
+/*
+ * ppp_send_config - configure the transmit characteristics of
+ * the ppp interface.
+ */
+void
+ppp_send_config(unit, mtu, asyncmap, pcomp, accomp)
+ int unit, mtu;
+ u_int32_t asyncmap;
+ int pcomp, accomp;
+{
+ u_int x;
+ struct ifreq ifr;
+
+ strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+ ifr.ifr_mtu = mtu;
+ if (ioctl(sockfd, SIOCSIFMTU, (caddr_t) &ifr) < 0) {
+ syslog(LOG_ERR, "ioctl(SIOCSIFMTU): %m");
+ quit();
+ }
+
+ if (ioctl(ppp_fd, PPPIOCSASYNCMAP, (caddr_t) &asyncmap) < 0) {
+ syslog(LOG_ERR, "ioctl(PPPIOCSASYNCMAP): %m");
+ quit();
+ }
+
+ if (ioctl(ppp_fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
+ syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m");
+ quit();
+ }
+ x = pcomp? x | SC_COMP_PROT: x &~ SC_COMP_PROT;
+ x = accomp? x | SC_COMP_AC: x &~ SC_COMP_AC;
+ if (ioctl(ppp_fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) {
+ syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m");
+ quit();
+ }
+}
+
+
+/*
+ * ppp_set_xaccm - set the extended transmit ACCM for the interface.
+ */
+void
+ppp_set_xaccm(unit, accm)
+ int unit;
+ ext_accm accm;
+{
+ if (ioctl(ppp_fd, PPPIOCSXASYNCMAP, accm) < 0 && errno != ENOTTY)
+ syslog(LOG_WARNING, "ioctl(set extended ACCM): %m");
+}
+
+
+/*
+ * ppp_recv_config - configure the receive-side characteristics of
+ * the ppp interface.
+ */
+void
+ppp_recv_config(unit, mru, asyncmap, pcomp, accomp)
+ int unit, mru;
+ u_int32_t asyncmap;
+ int pcomp, accomp;
+{
+ int x;
+
+ if (ioctl(ppp_fd, PPPIOCSMRU, (caddr_t) &mru) < 0) {
+ syslog(LOG_ERR, "ioctl(PPPIOCSMRU): %m");
+ quit();
+ }
+ if (ioctl(ppp_fd, PPPIOCSRASYNCMAP, (caddr_t) &asyncmap) < 0) {
+ syslog(LOG_ERR, "ioctl(PPPIOCSRASYNCMAP): %m");
+ quit();
+ }
+ if (ioctl(ppp_fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
+ syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m");
+ quit();
+ }
+ x = !accomp? x | SC_REJ_COMP_AC: x &~ SC_REJ_COMP_AC;
+ if (ioctl(ppp_fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) {
+ syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m");
+ quit();
+ }
+}
+
+/*
+ * ccp_test - ask kernel whether a given compression method
+ * is acceptable for use. Returns 1 if the method and parameters
+ * are OK, 0 if the method is known but the parameters are not OK
+ * (e.g. code size should be reduced), or -1 if the method is unknown.
+ */
+int
+ccp_test(unit, opt_ptr, opt_len, for_transmit)
+ int unit, opt_len, for_transmit;
+ u_char *opt_ptr;
+{
+ struct ppp_option_data data;
+
+ data.ptr = opt_ptr;
+ data.length = opt_len;
+ data.transmit = for_transmit;
+ if (ioctl(ttyfd, PPPIOCSCOMPRESS, (caddr_t) &data) >= 0)
+ return 1;
+ return (errno == ENOBUFS)? 0: -1;
+}
+
+/*
+ * ccp_flags_set - inform kernel about the current state of CCP.
+ */
+void
+ccp_flags_set(unit, isopen, isup)
+ int unit, isopen, isup;
+{
+ int x;
+
+ if (ioctl(ppp_fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
+ syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m");
+ return;
+ }
+ x = isopen? x | SC_CCP_OPEN: x &~ SC_CCP_OPEN;
+ x = isup? x | SC_CCP_UP: x &~ SC_CCP_UP;
+ if (ioctl(ppp_fd, PPPIOCSFLAGS, (caddr_t) &x) < 0)
+ syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m");
+}
+
+/*
+ * ccp_fatal_error - returns 1 if decompression was disabled as a
+ * result of an error detected after decompression of a packet,
+ * 0 otherwise. This is necessary because of patent nonsense.
+ */
+int
+ccp_fatal_error(unit)
+ int unit;
+{
+ int x;
+
+ if (ioctl(ppp_fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
+ syslog(LOG_ERR, "ioctl(PPPIOCGFLAGS): %m");
+ return 0;
+ }
+ return x & SC_DC_FERROR;
+}
+
+/*
+ * get_idle_time - return how long the link has been idle.
+ */
+int
+get_idle_time(u, ip)
+ int u;
+ struct ppp_idle *ip;
+{
+ return ioctl(ppp_fd, PPPIOCGIDLE, ip) >= 0;
+}
+
+
+#ifdef PPP_FILTER
+/*
+ * set_filters - transfer the pass and active filters to the kernel.
+ */
+int
+set_filters(pass, active)
+ struct bpf_program *pass, *active;
+{
+ int ret = 1;
+
+ if (pass->bf_len > 0) {
+ if (ioctl(ppp_fd, PPPIOCSPASS, pass) < 0) {
+ syslog(LOG_ERR, "Couldn't set pass-filter in kernel: %m");
+ ret = 0;
+ }
+ }
+ if (active->bf_len > 0) {
+ if (ioctl(ppp_fd, PPPIOCSACTIVE, active) < 0) {
+ syslog(LOG_ERR, "Couldn't set active-filter in kernel: %m");
+ ret = 0;
+ }
+ }
+ return ret;
+}
+#endif
+
+/*
+ * sifvjcomp - config tcp header compression
+ */
+int
+sifvjcomp(u, vjcomp, cidcomp, maxcid)
+ int u, vjcomp, cidcomp, maxcid;
+{
+ u_int x;
+
+ if (ioctl(ppp_fd, PPPIOCGFLAGS, (caddr_t) &x) < 0) {
+ syslog(LOG_ERR, "ioctl (PPPIOCGFLAGS): %m");
+ return 0;
+ }
+ x = vjcomp ? x | SC_COMP_TCP: x &~ SC_COMP_TCP;
+ x = cidcomp? x & ~SC_NO_TCP_CCID: x | SC_NO_TCP_CCID;
+ if (ioctl(ppp_fd, PPPIOCSFLAGS, (caddr_t) &x) < 0) {
+ syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m");
+ return 0;
+ }
+ if (vjcomp && ioctl(ppp_fd, PPPIOCSMAXCID, (caddr_t) &maxcid) < 0) {
+ syslog(LOG_ERR, "ioctl(PPPIOCSFLAGS): %m");
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * sifup - Config the interface up and enable IP packets to pass.
+ */
+int
+sifup(u)
+ int u;
+{
+ struct ifreq ifr;
+
+ strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+ if (ioctl(sockfd, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
+ syslog(LOG_ERR, "ioctl (SIOCGIFFLAGS): %m");
+ return 0;
+ }
+ ifr.ifr_flags |= IFF_UP;
+ if (ioctl(sockfd, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
+ syslog(LOG_ERR, "ioctl(SIOCSIFFLAGS): %m");
+ return 0;
+ }
+ if_is_up = 1;
+ return 1;
+}
+
+/*
+ * sifnpmode - Set the mode for handling packets for a given NP.
+ */
+int
+sifnpmode(u, proto, mode)
+ int u;
+ int proto;
+ enum NPmode mode;
+{
+ struct npioctl npi;
+
+ npi.protocol = proto;
+ npi.mode = mode;
+ if (ioctl(ppp_fd, PPPIOCSNPMODE, &npi) < 0) {
+ syslog(LOG_ERR, "ioctl(set NP %d mode to %d): %m", proto, mode);
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * sifdown - Config the interface down and disable IP.
+ */
+int
+sifdown(u)
+ int u;
+{
+ struct ifreq ifr;
+ int rv;
+ struct npioctl npi;
+
+ rv = 1;
+ npi.protocol = PPP_IP;
+ npi.mode = NPMODE_ERROR;
+ ioctl(ppp_fd, PPPIOCSNPMODE, (caddr_t) &npi);
+ /* ignore errors, because ppp_fd might have been closed by now. */
+
+ strncpy(ifr.ifr_name, ifname, sizeof (ifr.ifr_name));
+ if (ioctl(sockfd, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
+ syslog(LOG_ERR, "ioctl (SIOCGIFFLAGS): %m");
+ rv = 0;
+ } else {
+ ifr.ifr_flags &= ~IFF_UP;
+ if (ioctl(sockfd, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
+ syslog(LOG_ERR, "ioctl(SIOCSIFFLAGS): %m");
+ rv = 0;
+ } else
+ if_is_up = 0;
+ }
+ return rv;
+}
+
+/*
+ * SET_SA_FAMILY - set the sa_family field of a struct sockaddr,
+ * if it exists.
+ */
+#define SET_SA_FAMILY(addr, family) \
+ BZERO((char *) &(addr), sizeof(addr)); \
+ addr.sa_family = (family); \
+ addr.sa_len = sizeof(addr);
+
+/*
+ * sifaddr - Config the interface IP addresses and netmask.
+ */
+int
+sifaddr(u, o, h, m)
+ int u;
+ u_int32_t o, h, m;
+{
+ struct ifaliasreq ifra;
+ struct ifreq ifr;
+
+ strncpy(ifra.ifra_name, ifname, sizeof(ifra.ifra_name));
+ SET_SA_FAMILY(ifra.ifra_addr, AF_INET);
+ ((struct sockaddr_in *) &ifra.ifra_addr)->sin_addr.s_addr = o;
+ SET_SA_FAMILY(ifra.ifra_broadaddr, AF_INET);
+ ((struct sockaddr_in *) &ifra.ifra_broadaddr)->sin_addr.s_addr = h;
+ if (m != 0) {
+ SET_SA_FAMILY(ifra.ifra_mask, AF_INET);
+ ((struct sockaddr_in *) &ifra.ifra_mask)->sin_addr.s_addr = m;
+ } else
+ BZERO(&ifra.ifra_mask, sizeof(ifra.ifra_mask));
+ BZERO(&ifr, sizeof(ifr));
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(sockfd, SIOCDIFADDR, (caddr_t) &ifr) < 0) {
+ if (errno != EADDRNOTAVAIL)
+ syslog(LOG_WARNING, "Couldn't remove interface address: %m");
+ }
+ if (ioctl(sockfd, SIOCAIFADDR, (caddr_t) &ifra) < 0) {
+ if (errno != EEXIST) {
+ syslog(LOG_ERR, "Couldn't set interface address: %m");
+ return 0;
+ }
+ syslog(LOG_WARNING,
+ "Couldn't set interface address: Address %s already exists",
+ ip_ntoa(o));
+ }
+ ifaddrs[0] = o;
+ ifaddrs[1] = h;
+ return 1;
+}
+
+/*
+ * cifaddr - Clear the interface IP addresses, and delete routes
+ * through the interface if possible.
+ */
+int
+cifaddr(u, o, h)
+ int u;
+ u_int32_t o, h;
+{
+ struct ifaliasreq ifra;
+
+ ifaddrs[0] = 0;
+ strncpy(ifra.ifra_name, ifname, sizeof(ifra.ifra_name));
+ SET_SA_FAMILY(ifra.ifra_addr, AF_INET);
+ ((struct sockaddr_in *) &ifra.ifra_addr)->sin_addr.s_addr = o;
+ SET_SA_FAMILY(ifra.ifra_broadaddr, AF_INET);
+ ((struct sockaddr_in *) &ifra.ifra_broadaddr)->sin_addr.s_addr = h;
+ BZERO(&ifra.ifra_mask, sizeof(ifra.ifra_mask));
+ if (ioctl(sockfd, SIOCDIFADDR, (caddr_t) &ifra) < 0) {
+ if (errno != EADDRNOTAVAIL)
+ syslog(LOG_WARNING, "Couldn't delete interface address: %m");
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * sifdefaultroute - assign a default route through the address given.
+ */
+int
+sifdefaultroute(u, l, g)
+ int u;
+ u_int32_t l, g;
+{
+ return dodefaultroute(g, 's');
+}
+
+/*
+ * cifdefaultroute - delete a default route through the address given.
+ */
+int
+cifdefaultroute(u, l, g)
+ int u;
+ u_int32_t l, g;
+{
+ return dodefaultroute(g, 'c');
+}
+
+/*
+ * dodefaultroute - talk to a routing socket to add/delete a default route.
+ */
+static int
+dodefaultroute(g, cmd)
+ u_int32_t g;
+ int cmd;
+{
+ int routes;
+ struct {
+ struct rt_msghdr hdr;
+ struct sockaddr_in dst;
+ struct sockaddr_in gway;
+ struct sockaddr_in mask;
+ } rtmsg;
+
+ if ((routes = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) {
+ syslog(LOG_ERR, "Couldn't %s default route: socket: %m",
+ cmd=='s'? "add": "delete");
+ return 0;
+ }
+
+ memset(&rtmsg, 0, sizeof(rtmsg));
+ rtmsg.hdr.rtm_type = cmd == 's'? RTM_ADD: RTM_DELETE;
+ rtmsg.hdr.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;
+ rtmsg.hdr.rtm_version = RTM_VERSION;
+ rtmsg.hdr.rtm_seq = ++rtm_seq;
+ rtmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
+ rtmsg.dst.sin_len = sizeof(rtmsg.dst);
+ rtmsg.dst.sin_family = AF_INET;
+ rtmsg.gway.sin_len = sizeof(rtmsg.gway);
+ rtmsg.gway.sin_family = AF_INET;
+ rtmsg.gway.sin_addr.s_addr = g;
+ rtmsg.mask.sin_len = sizeof(rtmsg.dst);
+ rtmsg.mask.sin_family = AF_INET;
+
+ rtmsg.hdr.rtm_msglen = sizeof(rtmsg);
+ if (write(routes, &rtmsg, sizeof(rtmsg)) < 0) {
+ syslog(LOG_ERR, "Couldn't %s default route: %m",
+ cmd=='s'? "add": "delete");
+ close(routes);
+ return 0;
+ }
+
+ close(routes);
+ default_route_gateway = (cmd == 's')? g: 0;
+ return 1;
+}
+
+#if RTM_VERSION >= 3
+
+/*
+ * sifproxyarp - Make a proxy ARP entry for the peer.
+ */
+static struct {
+ struct rt_msghdr hdr;
+ struct sockaddr_inarp dst;
+ struct sockaddr_dl hwa;
+ char extra[128];
+} arpmsg;
+
+static int arpmsg_valid;
+
+int
+sifproxyarp(unit, hisaddr)
+ int unit;
+ u_int32_t hisaddr;
+{
+ int routes;
+
+ /*
+ * Get the hardware address of an interface on the same subnet
+ * as our local address.
+ */
+ memset(&arpmsg, 0, sizeof(arpmsg));
+ if (!get_ether_addr(hisaddr, &arpmsg.hwa)) {
+ syslog(LOG_ERR, "Cannot determine ethernet address for proxy ARP");
+ return 0;
+ }
+
+ if ((routes = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) {
+ syslog(LOG_ERR, "Couldn't add proxy arp entry: socket: %m");
+ return 0;
+ }
+
+ arpmsg.hdr.rtm_type = RTM_ADD;
+ arpmsg.hdr.rtm_flags = RTF_ANNOUNCE | RTF_HOST | RTF_STATIC;
+ arpmsg.hdr.rtm_version = RTM_VERSION;
+ arpmsg.hdr.rtm_seq = ++rtm_seq;
+ arpmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY;
+ arpmsg.hdr.rtm_inits = RTV_EXPIRE;
+ arpmsg.dst.sin_len = sizeof(struct sockaddr_inarp);
+ arpmsg.dst.sin_family = AF_INET;
+ arpmsg.dst.sin_addr.s_addr = hisaddr;
+ arpmsg.dst.sin_other = SIN_PROXY;
+
+ arpmsg.hdr.rtm_msglen = (char *) &arpmsg.hwa - (char *) &arpmsg
+ + arpmsg.hwa.sdl_len;
+ if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) {
+ syslog(LOG_ERR, "Couldn't add proxy arp entry: %m");
+ close(routes);
+ return 0;
+ }
+
+ close(routes);
+ arpmsg_valid = 1;
+ proxy_arp_addr = hisaddr;
+ return 1;
+}
+
+/*
+ * cifproxyarp - Delete the proxy ARP entry for the peer.
+ */
+int
+cifproxyarp(unit, hisaddr)
+ int unit;
+ u_int32_t hisaddr;
+{
+ int routes;
+
+ if (!arpmsg_valid)
+ return 0;
+ arpmsg_valid = 0;
+
+ arpmsg.hdr.rtm_type = RTM_DELETE;
+ arpmsg.hdr.rtm_seq = ++rtm_seq;
+
+ if ((routes = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) {
+ syslog(LOG_ERR, "Couldn't delete proxy arp entry: socket: %m");
+ return 0;
+ }
+
+ if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) {
+ syslog(LOG_ERR, "Couldn't delete proxy arp entry: %m");
+ close(routes);
+ return 0;
+ }
+
+ close(routes);
+ proxy_arp_addr = 0;
+ return 1;
+}
+
+#else /* RTM_VERSION */
+
+/*
+ * sifproxyarp - Make a proxy ARP entry for the peer.
+ */
+int
+sifproxyarp(unit, hisaddr)
+ int unit;
+ u_int32_t hisaddr;
+{
+ struct arpreq arpreq;
+ struct {
+ struct sockaddr_dl sdl;
+ char space[128];
+ } dls;
+
+ BZERO(&arpreq, sizeof(arpreq));
+
+ /*
+ * Get the hardware address of an interface on the same subnet
+ * as our local address.
+ */
+ if (!get_ether_addr(hisaddr, &dls.sdl)) {
+ syslog(LOG_ERR, "Cannot determine ethernet address for proxy ARP");
+ return 0;
+ }
+
+ arpreq.arp_ha.sa_len = sizeof(struct sockaddr);
+ arpreq.arp_ha.sa_family = AF_UNSPEC;
+ BCOPY(LLADDR(&dls.sdl), arpreq.arp_ha.sa_data, dls.sdl.sdl_alen);
+ SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
+ ((struct sockaddr_in *) &arpreq.arp_pa)->sin_addr.s_addr = hisaddr;
+ arpreq.arp_flags = ATF_PERM | ATF_PUBL;
+ if (ioctl(sockfd, SIOCSARP, (caddr_t)&arpreq) < 0) {
+ syslog(LOG_ERR, "Couldn't add proxy arp entry: %m");
+ return 0;
+ }
+
+ proxy_arp_addr = hisaddr;
+ return 1;
+}
+
+/*
+ * cifproxyarp - Delete the proxy ARP entry for the peer.
+ */
+int
+cifproxyarp(unit, hisaddr)
+ int unit;
+ u_int32_t hisaddr;
+{
+ struct arpreq arpreq;
+
+ BZERO(&arpreq, sizeof(arpreq));
+ SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
+ ((struct sockaddr_in *) &arpreq.arp_pa)->sin_addr.s_addr = hisaddr;
+ if (ioctl(sockfd, SIOCDARP, (caddr_t)&arpreq) < 0) {
+ syslog(LOG_WARNING, "Couldn't delete proxy arp entry: %m");
+ return 0;
+ }
+ proxy_arp_addr = 0;
+ return 1;
+}
+#endif /* RTM_VERSION */
+
+#ifdef IPX_CHANGE
+/********************************************************************
+ *
+ * sipxfaddr - Config the interface IPX networknumber
+ */
+
+int
+sipxfaddr (int unit, unsigned long int network, unsigned char * node )
+ {
+ int result = 1;
+
+ int skfd;
+ struct sockaddr_ipx ipx_addr;
+ struct ifreq ifr;
+ struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) &ifr.ifr_addr;
+ union ipx_net_u net;
+
+ skfd = socket (AF_IPX, SOCK_DGRAM, 0);
+ if (skfd < 0)
+ {
+ syslog (LOG_DEBUG, "socket(AF_IPX): %m(%d)", errno);
+ result = 0;
+ }
+ else
+ {
+ memset (&ifr, '\0', sizeof (ifr));
+ strcpy (ifr.ifr_name, ifname);
+
+ memcpy (sipx->sipx_addr.x_host.c_host, node, 6);
+ sipx->sipx_len = sizeof(sipx);
+ sipx->sipx_family = AF_IPX;
+ sipx->sipx_port = 0;
+ memset(&net, 0, sizeof(net));
+ net.long_e = htonl (network);
+ sipx->sipx_addr.x_net = net.net_e;
+/*
+ * Set the IPX device
+ */
+ if (ioctl(skfd, SIOCSIFADDR, (caddr_t) &ifr) < 0)
+ {
+ result = 0;
+ if (errno != EEXIST)
+ {
+ syslog (LOG_DEBUG,
+ "ioctl(SIOCAIFADDR, CRTITF): %m(%d)", errno);
+ }
+ else
+ {
+ syslog (LOG_WARNING,
+ "ioctl(SIOCAIFADDR, CRTITF): Address already exists");
+ }
+ }
+ close (skfd);
+ }
+ return result;
+ }
+
+/********************************************************************
+ *
+ * cipxfaddr - Clear the information for the IPX network. The IPX routes
+ * are removed and the device is no longer able to pass IPX
+ * frames.
+ */
+
+int cipxfaddr (int unit)
+ {
+ int result = 1;
+
+ int skfd;
+ struct sockaddr_ipx ipx_addr;
+ struct ifreq ifr;
+ struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) &ifr.ifr_addr;
+
+ skfd = socket (AF_IPX, SOCK_DGRAM, 0);
+ if (skfd < 0)
+ {
+ syslog (LOG_DEBUG, "socket(AF_IPX): %m(%d)", errno);
+ result = 0;
+ }
+ else
+ {
+ memset (&ifr, '\0', sizeof (ifr));
+ strcpy (ifr.ifr_name, ifname);
+
+ sipx->sipx_len = sizeof(sipx);
+ sipx->sipx_family = AF_IPX;
+/*
+ * Set the IPX device
+ */
+ if (ioctl(skfd, SIOCSIFADDR, (caddr_t) &ifr) < 0)
+ {
+ syslog (LOG_INFO,
+ "ioctl(SIOCAIFADDR, IPX_DLTITF): %m(%d)", errno);
+ result = 0;
+ }
+ close (skfd);
+ }
+ return result;
+ }
+#endif
+
+/*
+ * get_ether_addr - get the hardware address of an interface on the
+ * the same subnet as ipaddr.
+ */
+static int
+get_ether_addr(ipaddr, hwaddr)
+ u_int32_t ipaddr;
+ struct sockaddr_dl *hwaddr;
+{
+ u_int32_t ina, mask;
+ struct sockaddr_dl *dla;
+ struct ifaddrs *ifap, *ifa, *ifp;
+
+ /*
+ * Scan through looking for an interface with an Internet
+ * address on the same subnet as `ipaddr'.
+ */
+ if (getifaddrs(&ifap) != 0) {
+ syslog(LOG_ERR, "getifaddrs: %m");
+ return 0;
+ }
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr->sa_family != AF_INET)
+ continue;
+ ina = ((struct sockaddr_in *) ifa->ifa_addr)->sin_addr.s_addr;
+ /*
+ * Check that the interface is up, and not point-to-point
+ * or loopback.
+ */
+ if ((ifa->ifa_flags &
+ (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK|IFF_NOARP))
+ != (IFF_UP|IFF_BROADCAST))
+ continue;
+ /*
+ * Get its netmask and check that it's on the right subnet.
+ */
+ mask = ((struct sockaddr_in *) ifa->ifa_netmask)->sin_addr.s_addr;
+ if ((ipaddr & mask) != (ina & mask))
+ continue;
+ break;
+ }
+ if (!ifa) {
+ freeifaddrs(ifap);
+ return 0;
+ }
+ syslog(LOG_INFO, "found interface %s for proxy arp", ifa->ifa_name);
+
+ /*
+ * Now scan through again looking for a link-level address
+ * for this interface.
+ */
+ ifp = ifa;
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ if (strcmp(ifp->ifa_name, ifa->ifa_name) != 0)
+ continue;
+ if (ifa->ifa_addr->sa_family != AF_LINK)
+ continue;
+ /*
+ * Found the link-level address - copy it out
+ */
+ dla = (struct sockaddr_dl *) ifa->ifa_addr;
+ BCOPY(dla, hwaddr, dla->sdl_len);
+ freeifaddrs(ifap);
+ return 1;
+ }
+
+ freeifaddrs(ifap);
+ return 0;
+}
+
+/*
+ * Return user specified netmask, modified by any mask we might determine
+ * for address `addr' (in network byte order).
+ * Here we scan through the system's list of interfaces, looking for
+ * any non-point-to-point interfaces which might appear to be on the same
+ * network as `addr'. If we find any, we OR in their netmask to the
+ * user-specified netmask.
+ */
+u_int32_t
+GetMask(addr)
+ u_int32_t addr;
+{
+ u_int32_t mask, nmask, ina;
+ struct ifaddrs *ifap, *ifa;
+
+ addr = ntohl(addr);
+ if (IN_CLASSA(addr)) /* determine network mask for address class */
+ nmask = IN_CLASSA_NET;
+ else if (IN_CLASSB(addr))
+ nmask = IN_CLASSB_NET;
+ else
+ nmask = IN_CLASSC_NET;
+ /* class D nets are disallowed by bad_ip_adrs */
+ mask = netmask | htonl(nmask);
+
+ /*
+ * Scan through the system's network interfaces.
+ */
+ if (getifaddrs(&ifap) != 0) {
+ syslog(LOG_WARNING, "getifaddrs: %m");
+ return mask;
+ }
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ /*
+ * Check the interface's internet address.
+ */
+ if (ifa->ifa_addr->sa_family != AF_INET)
+ continue;
+ ina = ((struct sockaddr_in *)&ifa->ifa_addr)->sin_addr.s_addr;
+ if ((ntohl(ina) & nmask) != (addr & nmask))
+ continue;
+ /*
+ * Check that the interface is up, and not point-to-point or loopback.
+ */
+ if ((ifa->ifa_flags & (IFF_UP|IFF_POINTOPOINT|IFF_LOOPBACK)) != IFF_UP)
+ continue;
+ /*
+ * Get its netmask and OR it into our mask.
+ */
+ mask |= ((struct sockaddr_in *)&ifa->ifa_netmask)->sin_addr.s_addr;
+ }
+
+ freeifaddrs(ifap);
+ return mask;
+}
+
+/*
+ * Use the hostid as part of the random number seed.
+ */
+int
+get_host_seed()
+{
+ return gethostid();
+}
+
+/*
+ * lock - create a lock file for the named lock device
+ */
+#define LOCK_PREFIX "/var/spool/lock/LCK.."
+
+int
+lock(dev)
+ char *dev;
+{
+ char hdb_lock_buffer[12];
+ int fd, pid, n;
+ char *p;
+
+ if ((p = strrchr(dev, '/')) != NULL)
+ dev = p + 1;
+ lock_file = malloc(strlen(LOCK_PREFIX) + strlen(dev) + 1);
+ if (lock_file == NULL)
+ novm("lock file name");
+ strcat(strcpy(lock_file, LOCK_PREFIX), dev);
+
+ while ((fd = open(lock_file, O_EXCL | O_CREAT | O_RDWR, 0644)) < 0) {
+ if (errno == EEXIST
+ && (fd = open(lock_file, O_RDONLY, 0)) >= 0) {
+ /* Read the lock file to find out who has the device locked */
+ n = read(fd, hdb_lock_buffer, 11);
+ if (n <= 0) {
+ syslog(LOG_ERR, "Can't read pid from lock file %s", lock_file);
+ close(fd);
+ } else {
+ hdb_lock_buffer[n] = 0;
+ pid = atoi(hdb_lock_buffer);
+ if (kill(pid, 0) == -1 && errno == ESRCH) {
+ /* pid no longer exists - remove the lock file */
+ if (unlink(lock_file) == 0) {
+ close(fd);
+ syslog(LOG_NOTICE, "Removed stale lock on %s (pid %d)",
+ dev, pid);
+ continue;
+ } else
+ syslog(LOG_WARNING, "Couldn't remove stale lock on %s",
+ dev);
+ } else
+ syslog(LOG_NOTICE, "Device %s is locked by pid %d",
+ dev, pid);
+ }
+ close(fd);
+ } else
+ syslog(LOG_ERR, "Can't create lock file %s: %m", lock_file);
+ free(lock_file);
+ lock_file = NULL;
+ return -1;
+ }
+
+ sprintf(hdb_lock_buffer, "%10d\n", getpid());
+ write(fd, hdb_lock_buffer, 11);
+
+ close(fd);
+ return 0;
+}
+
+/*
+ * unlock - remove our lockfile
+ */
+void
+unlock()
+{
+ if (lock_file) {
+ unlink(lock_file);
+ free(lock_file);
+ lock_file = NULL;
+ }
+}
diff --git a/usr.sbin/pppd/upap.c b/usr.sbin/pppd/upap.c
new file mode 100644
index 0000000..af25a58
--- /dev/null
+++ b/usr.sbin/pppd/upap.c
@@ -0,0 +1,618 @@
+/*
+ * upap.c - User/Password Authentication Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 Carnegie Mellon University. The name of the
+ * University 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <syslog.h>
+
+#include "pppd.h"
+#include "upap.h"
+
+/*
+ * Protocol entry points.
+ */
+static void upap_init(int);
+static void upap_lowerup(int);
+static void upap_lowerdown(int);
+static void upap_input(int, u_char *, int);
+static void upap_protrej(int);
+static int upap_printpkt(u_char *, int,
+ void (*)(void *, char *, ...), void *);
+
+struct protent pap_protent = {
+ PPP_PAP,
+ upap_init,
+ upap_input,
+ upap_protrej,
+ upap_lowerup,
+ upap_lowerdown,
+ NULL,
+ NULL,
+ upap_printpkt,
+ NULL,
+ 1,
+ "PAP",
+ NULL,
+ NULL,
+ NULL
+};
+
+upap_state upap[NUM_PPP]; /* UPAP state; one for each unit */
+
+static void upap_timeout(void *);
+static void upap_reqtimeout(void *);
+static void upap_rauthreq(upap_state *, u_char *, int, int);
+static void upap_rauthack(upap_state *, u_char *, int, int);
+static void upap_rauthnak(upap_state *, u_char *, int, int);
+static void upap_sauthreq(upap_state *);
+static void upap_sresp(upap_state *, int, int, char *, int);
+
+
+/*
+ * upap_init - Initialize a UPAP unit.
+ */
+static void
+upap_init(unit)
+ int unit;
+{
+ upap_state *u = &upap[unit];
+
+ u->us_unit = unit;
+ u->us_user = NULL;
+ u->us_userlen = 0;
+ u->us_passwd = NULL;
+ u->us_passwdlen = 0;
+ u->us_clientstate = UPAPCS_INITIAL;
+ u->us_serverstate = UPAPSS_INITIAL;
+ u->us_id = 0;
+ u->us_timeouttime = UPAP_DEFTIMEOUT;
+ u->us_maxtransmits = 10;
+ u->us_reqtimeout = UPAP_DEFREQTIME;
+}
+
+
+/*
+ * upap_authwithpeer - Authenticate us with our peer (start client).
+ *
+ * Set new state and send authenticate's.
+ */
+void
+upap_authwithpeer(unit, user, password)
+ int unit;
+ char *user, *password;
+{
+ upap_state *u = &upap[unit];
+
+ /* Save the username and password we're given */
+ u->us_user = user;
+ u->us_userlen = strlen(user);
+ u->us_passwd = password;
+ u->us_passwdlen = strlen(password);
+ u->us_transmits = 0;
+
+ /* Lower layer up yet? */
+ if (u->us_clientstate == UPAPCS_INITIAL ||
+ u->us_clientstate == UPAPCS_PENDING) {
+ u->us_clientstate = UPAPCS_PENDING;
+ return;
+ }
+
+ upap_sauthreq(u); /* Start protocol */
+}
+
+
+/*
+ * upap_authpeer - Authenticate our peer (start server).
+ *
+ * Set new state.
+ */
+void
+upap_authpeer(unit)
+ int unit;
+{
+ upap_state *u = &upap[unit];
+
+ /* Lower layer up yet? */
+ if (u->us_serverstate == UPAPSS_INITIAL ||
+ u->us_serverstate == UPAPSS_PENDING) {
+ u->us_serverstate = UPAPSS_PENDING;
+ return;
+ }
+
+ u->us_serverstate = UPAPSS_LISTEN;
+ if (u->us_reqtimeout > 0)
+ TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout);
+}
+
+
+/*
+ * upap_timeout - Retransmission timer for sending auth-reqs expired.
+ */
+static void
+upap_timeout(arg)
+ void *arg;
+{
+ upap_state *u = (upap_state *) arg;
+
+ if (u->us_clientstate != UPAPCS_AUTHREQ)
+ return;
+
+ if (u->us_transmits >= u->us_maxtransmits) {
+ /* give up in disgust */
+ syslog(LOG_ERR, "No response to PAP authenticate-requests");
+ u->us_clientstate = UPAPCS_BADAUTH;
+ auth_withpeer_fail(u->us_unit, PPP_PAP);
+ return;
+ }
+
+ upap_sauthreq(u); /* Send Authenticate-Request */
+}
+
+
+/*
+ * upap_reqtimeout - Give up waiting for the peer to send an auth-req.
+ */
+static void
+upap_reqtimeout(arg)
+ void *arg;
+{
+ upap_state *u = (upap_state *) arg;
+
+ if (u->us_serverstate != UPAPSS_LISTEN)
+ return; /* huh?? */
+
+ auth_peer_fail(u->us_unit, PPP_PAP);
+ u->us_serverstate = UPAPSS_BADAUTH;
+}
+
+
+/*
+ * upap_lowerup - The lower layer is up.
+ *
+ * Start authenticating if pending.
+ */
+static void
+upap_lowerup(unit)
+ int unit;
+{
+ upap_state *u = &upap[unit];
+
+ if (u->us_clientstate == UPAPCS_INITIAL)
+ u->us_clientstate = UPAPCS_CLOSED;
+ else if (u->us_clientstate == UPAPCS_PENDING) {
+ upap_sauthreq(u); /* send an auth-request */
+ }
+
+ if (u->us_serverstate == UPAPSS_INITIAL)
+ u->us_serverstate = UPAPSS_CLOSED;
+ else if (u->us_serverstate == UPAPSS_PENDING) {
+ u->us_serverstate = UPAPSS_LISTEN;
+ if (u->us_reqtimeout > 0)
+ TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout);
+ }
+}
+
+
+/*
+ * upap_lowerdown - The lower layer is down.
+ *
+ * Cancel all timeouts.
+ */
+static void
+upap_lowerdown(unit)
+ int unit;
+{
+ upap_state *u = &upap[unit];
+
+ if (u->us_clientstate == UPAPCS_AUTHREQ) /* Timeout pending? */
+ UNTIMEOUT(upap_timeout, u); /* Cancel timeout */
+ if (u->us_serverstate == UPAPSS_LISTEN && u->us_reqtimeout > 0)
+ UNTIMEOUT(upap_reqtimeout, u);
+
+ u->us_clientstate = UPAPCS_INITIAL;
+ u->us_serverstate = UPAPSS_INITIAL;
+}
+
+
+/*
+ * upap_protrej - Peer doesn't speak this protocol.
+ *
+ * This shouldn't happen. In any case, pretend lower layer went down.
+ */
+static void
+upap_protrej(unit)
+ int unit;
+{
+ upap_state *u = &upap[unit];
+
+ if (u->us_clientstate == UPAPCS_AUTHREQ) {
+ syslog(LOG_ERR, "PAP authentication failed due to protocol-reject");
+ auth_withpeer_fail(unit, PPP_PAP);
+ }
+ if (u->us_serverstate == UPAPSS_LISTEN) {
+ syslog(LOG_ERR, "PAP authentication of peer failed (protocol-reject)");
+ auth_peer_fail(unit, PPP_PAP);
+ }
+ upap_lowerdown(unit);
+}
+
+
+/*
+ * upap_input - Input UPAP packet.
+ */
+static void
+upap_input(unit, inpacket, l)
+ int unit;
+ u_char *inpacket;
+ int l;
+{
+ upap_state *u = &upap[unit];
+ u_char *inp;
+ u_char code, id;
+ int len;
+
+ /*
+ * Parse header (code, id and length).
+ * If packet too short, drop it.
+ */
+ inp = inpacket;
+ if (l < UPAP_HEADERLEN) {
+ UPAPDEBUG((LOG_INFO, "pap_input: rcvd short header."));
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+ if (len < UPAP_HEADERLEN) {
+ UPAPDEBUG((LOG_INFO, "pap_input: rcvd illegal length."));
+ return;
+ }
+ if (len > l) {
+ UPAPDEBUG((LOG_INFO, "pap_input: rcvd short packet."));
+ return;
+ }
+ len -= UPAP_HEADERLEN;
+
+ /*
+ * Action depends on code.
+ */
+ switch (code) {
+ case UPAP_AUTHREQ:
+ upap_rauthreq(u, inp, id, len);
+ break;
+
+ case UPAP_AUTHACK:
+ upap_rauthack(u, inp, id, len);
+ break;
+
+ case UPAP_AUTHNAK:
+ upap_rauthnak(u, inp, id, len);
+ break;
+
+ default: /* XXX Need code reject */
+ break;
+ }
+}
+
+
+/*
+ * upap_rauth - Receive Authenticate.
+ */
+static void
+upap_rauthreq(u, inp, id, len)
+ upap_state *u;
+ u_char *inp;
+ int id;
+ int len;
+{
+ u_char ruserlen, rpasswdlen;
+ char *ruser, *rpasswd;
+ int retcode;
+ char *msg;
+ int msglen;
+
+ UPAPDEBUG((LOG_INFO, "pap_rauth: Rcvd id %d.", id));
+
+ if (u->us_serverstate < UPAPSS_LISTEN)
+ return;
+
+ /*
+ * If we receive a duplicate authenticate-request, we are
+ * supposed to return the same status as for the first request.
+ */
+ if (u->us_serverstate == UPAPSS_OPEN) {
+ upap_sresp(u, UPAP_AUTHACK, id, "", 0); /* return auth-ack */
+ return;
+ }
+ if (u->us_serverstate == UPAPSS_BADAUTH) {
+ upap_sresp(u, UPAP_AUTHNAK, id, "", 0); /* return auth-nak */
+ return;
+ }
+
+ /*
+ * Parse user/passwd.
+ */
+ if (len < sizeof (u_char)) {
+ UPAPDEBUG((LOG_INFO, "pap_rauth: rcvd short packet."));
+ return;
+ }
+ GETCHAR(ruserlen, inp);
+ len -= sizeof (u_char) + ruserlen + sizeof (u_char);
+ if (len < 0) {
+ UPAPDEBUG((LOG_INFO, "pap_rauth: rcvd short packet."));
+ return;
+ }
+ ruser = (char *) inp;
+ INCPTR(ruserlen, inp);
+ GETCHAR(rpasswdlen, inp);
+ if (len < rpasswdlen) {
+ UPAPDEBUG((LOG_INFO, "pap_rauth: rcvd short packet."));
+ return;
+ }
+ rpasswd = (char *) inp;
+
+ /*
+ * Check the username and password given.
+ */
+ retcode = check_passwd(u->us_unit, ruser, ruserlen, rpasswd,
+ rpasswdlen, &msg, &msglen);
+ BZERO(rpasswd, rpasswdlen);
+
+ upap_sresp(u, retcode, id, msg, msglen);
+
+ if (retcode == UPAP_AUTHACK) {
+ u->us_serverstate = UPAPSS_OPEN;
+ auth_peer_success(u->us_unit, PPP_PAP, ruser, ruserlen);
+ } else {
+ u->us_serverstate = UPAPSS_BADAUTH;
+ auth_peer_fail(u->us_unit, PPP_PAP);
+ }
+
+ if (u->us_reqtimeout > 0)
+ UNTIMEOUT(upap_reqtimeout, u);
+}
+
+
+/*
+ * upap_rauthack - Receive Authenticate-Ack.
+ */
+static void
+upap_rauthack(u, inp, id, len)
+ upap_state *u;
+ u_char *inp;
+ int id;
+ int len;
+{
+ u_char msglen;
+ char *msg;
+
+ UPAPDEBUG((LOG_INFO, "pap_rauthack: Rcvd id %d.", id));
+ if (u->us_clientstate != UPAPCS_AUTHREQ) /* XXX */
+ return;
+
+ /*
+ * Parse message.
+ */
+ if (len < sizeof (u_char)) {
+ UPAPDEBUG((LOG_INFO, "pap_rauthack: rcvd short packet."));
+ return;
+ }
+ GETCHAR(msglen, inp);
+ len -= sizeof (u_char);
+ if (len < msglen) {
+ UPAPDEBUG((LOG_INFO, "pap_rauthack: rcvd short packet."));
+ return;
+ }
+ msg = (char *) inp;
+ PRINTMSG(msg, msglen);
+
+ u->us_clientstate = UPAPCS_OPEN;
+
+ auth_withpeer_success(u->us_unit, PPP_PAP);
+}
+
+
+/*
+ * upap_rauthnak - Receive Authenticate-Nakk.
+ */
+static void
+upap_rauthnak(u, inp, id, len)
+ upap_state *u;
+ u_char *inp;
+ int id;
+ int len;
+{
+ u_char msglen;
+ char *msg;
+
+ UPAPDEBUG((LOG_INFO, "pap_rauthnak: Rcvd id %d.", id));
+ if (u->us_clientstate != UPAPCS_AUTHREQ) /* XXX */
+ return;
+
+ /*
+ * Parse message.
+ */
+ if (len < sizeof (u_char)) {
+ UPAPDEBUG((LOG_INFO, "pap_rauthnak: rcvd short packet."));
+ return;
+ }
+ GETCHAR(msglen, inp);
+ len -= sizeof (u_char);
+ if (len < msglen) {
+ UPAPDEBUG((LOG_INFO, "pap_rauthnak: rcvd short packet."));
+ return;
+ }
+ msg = (char *) inp;
+ PRINTMSG(msg, msglen);
+
+ u->us_clientstate = UPAPCS_BADAUTH;
+
+ syslog(LOG_ERR, "PAP authentication failed");
+ auth_withpeer_fail(u->us_unit, PPP_PAP);
+}
+
+
+/*
+ * upap_sauthreq - Send an Authenticate-Request.
+ */
+static void
+upap_sauthreq(u)
+ upap_state *u;
+{
+ u_char *outp;
+ int outlen;
+
+ outlen = UPAP_HEADERLEN + 2 * sizeof (u_char) +
+ u->us_userlen + u->us_passwdlen;
+ outp = outpacket_buf;
+
+ MAKEHEADER(outp, PPP_PAP);
+
+ PUTCHAR(UPAP_AUTHREQ, outp);
+ PUTCHAR(++u->us_id, outp);
+ PUTSHORT(outlen, outp);
+ PUTCHAR(u->us_userlen, outp);
+ BCOPY(u->us_user, outp, u->us_userlen);
+ INCPTR(u->us_userlen, outp);
+ PUTCHAR(u->us_passwdlen, outp);
+ BCOPY(u->us_passwd, outp, u->us_passwdlen);
+
+ output(u->us_unit, outpacket_buf, outlen + PPP_HDRLEN);
+
+ UPAPDEBUG((LOG_INFO, "pap_sauth: Sent id %d.", u->us_id));
+
+ TIMEOUT(upap_timeout, u, u->us_timeouttime);
+ ++u->us_transmits;
+ u->us_clientstate = UPAPCS_AUTHREQ;
+}
+
+
+/*
+ * upap_sresp - Send a response (ack or nak).
+ */
+static void
+upap_sresp(u, code, id, msg, msglen)
+ upap_state *u;
+ u_char code, id;
+ char *msg;
+ int msglen;
+{
+ u_char *outp;
+ int outlen;
+
+ outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen;
+ outp = outpacket_buf;
+ MAKEHEADER(outp, PPP_PAP);
+
+ PUTCHAR(code, outp);
+ PUTCHAR(id, outp);
+ PUTSHORT(outlen, outp);
+ PUTCHAR(msglen, outp);
+ BCOPY(msg, outp, msglen);
+ output(u->us_unit, outpacket_buf, outlen + PPP_HDRLEN);
+
+ UPAPDEBUG((LOG_INFO, "pap_sresp: Sent code %d, id %d.", code, id));
+}
+
+/*
+ * upap_printpkt - print the contents of a PAP packet.
+ */
+static char *upap_codenames[] = {
+ "AuthReq", "AuthAck", "AuthNak"
+};
+
+static int
+upap_printpkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer)(void *, char *, ...);
+ void *arg;
+{
+ int code, id, len;
+ int mlen, ulen, wlen;
+ char *user, *pwd, *msg;
+ u_char *pstart;
+
+ if (plen < UPAP_HEADERLEN)
+ return 0;
+ pstart = p;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < UPAP_HEADERLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= sizeof(upap_codenames) / sizeof(char *))
+ printer(arg, " %s", upap_codenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+ printer(arg, " id=0x%x", id);
+ len -= UPAP_HEADERLEN;
+ switch (code) {
+ case UPAP_AUTHREQ:
+ if (len < 1)
+ break;
+ ulen = p[0];
+ if (len < ulen + 2)
+ break;
+ wlen = p[ulen + 1];
+ if (len < ulen + wlen + 2)
+ break;
+ user = (char *) (p + 1);
+ pwd = (char *) (p + ulen + 2);
+ p += ulen + wlen + 2;
+ len -= ulen + wlen + 2;
+ printer(arg, " user=");
+ print_string(user, ulen, printer, arg);
+ printer(arg, " password=");
+ print_string(pwd, wlen, printer, arg);
+ break;
+ case UPAP_AUTHACK:
+ case UPAP_AUTHNAK:
+ if (len < 1)
+ break;
+ mlen = p[0];
+ if (len < mlen + 1)
+ break;
+ msg = (char *) (p + 1);
+ p += mlen + 1;
+ len -= mlen + 1;
+ printer(arg, " ");
+ print_string(msg, mlen, printer, arg);
+ break;
+ }
+
+ /* print the rest of the bytes in the packet */
+ for (; len > 0; --len) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+
+ return p - pstart;
+}
diff --git a/usr.sbin/pppd/upap.h b/usr.sbin/pppd/upap.h
new file mode 100644
index 0000000..82ce604
--- /dev/null
+++ b/usr.sbin/pppd/upap.h
@@ -0,0 +1,87 @@
+/*
+ * upap.h - User/Password Authentication Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 Carnegie Mellon University. The name of the
+ * University 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Packet header = Code, id, length.
+ */
+#define UPAP_HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short))
+
+
+/*
+ * UPAP codes.
+ */
+#define UPAP_AUTHREQ 1 /* Authenticate-Request */
+#define UPAP_AUTHACK 2 /* Authenticate-Ack */
+#define UPAP_AUTHNAK 3 /* Authenticate-Nak */
+
+
+/*
+ * Each interface is described by upap structure.
+ */
+typedef struct upap_state {
+ int us_unit; /* Interface unit number */
+ char *us_user; /* User */
+ int us_userlen; /* User length */
+ char *us_passwd; /* Password */
+ int us_passwdlen; /* Password length */
+ int us_clientstate; /* Client state */
+ int us_serverstate; /* Server state */
+ u_char us_id; /* Current id */
+ int us_timeouttime; /* Timeout (seconds) for auth-req retrans. */
+ int us_transmits; /* Number of auth-reqs sent */
+ int us_maxtransmits; /* Maximum number of auth-reqs to send */
+ int us_reqtimeout; /* Time to wait for auth-req from peer */
+} upap_state;
+
+
+/*
+ * Client states.
+ */
+#define UPAPCS_INITIAL 0 /* Connection down */
+#define UPAPCS_CLOSED 1 /* Connection up, haven't requested auth */
+#define UPAPCS_PENDING 2 /* Connection down, have requested auth */
+#define UPAPCS_AUTHREQ 3 /* We've sent an Authenticate-Request */
+#define UPAPCS_OPEN 4 /* We've received an Ack */
+#define UPAPCS_BADAUTH 5 /* We've received a Nak */
+
+/*
+ * Server states.
+ */
+#define UPAPSS_INITIAL 0 /* Connection down */
+#define UPAPSS_CLOSED 1 /* Connection up, haven't requested auth */
+#define UPAPSS_PENDING 2 /* Connection down, have requested auth */
+#define UPAPSS_LISTEN 3 /* Listening for an Authenticate */
+#define UPAPSS_OPEN 4 /* We've sent an Ack */
+#define UPAPSS_BADAUTH 5 /* We've sent a Nak */
+
+
+/*
+ * Timeouts.
+ */
+#define UPAP_DEFTIMEOUT 3 /* Timeout (seconds) for retransmitting req */
+#define UPAP_DEFREQTIME 30 /* Time to wait for auth-req from peer */
+
+extern upap_state upap[];
+
+void upap_authwithpeer(int, char *, char *);
+void upap_authpeer(int);
+
+extern struct protent pap_protent;
diff --git a/usr.sbin/pppstats/Makefile b/usr.sbin/pppstats/Makefile
new file mode 100644
index 0000000..da34dc9
--- /dev/null
+++ b/usr.sbin/pppstats/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+PROG= pppstats
+MAN= pppstats.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pppstats/pppstats.8 b/usr.sbin/pppstats/pppstats.8
new file mode 100644
index 0000000..5b28ea0
--- /dev/null
+++ b/usr.sbin/pppstats/pppstats.8
@@ -0,0 +1,218 @@
+.\" @(#) $FreeBSD$
+.TH PPPSTATS 8 "26 June 1995"
+.SH NAME
+pppstats \- print PPP statistics
+.SH SYNOPSIS
+.B pppstats
+[
+.B -a
+] [
+.B -v
+] [
+.B -r
+] [
+.B -z
+] [
+.B -c
+.I <count>
+] [
+.B -w
+.I <secs>
+] [
+.I interface
+]
+.ti 12
+.SH DESCRIPTION
+The
+.B pppstats
+utility reports PPP-related statistics at regular intervals for the
+specified PPP interface. If the interface is unspecified, it will
+default to ppp0.
+The display is split horizontally
+into input and output sections containing columns of statistics
+describing the properties and volume of packets received and
+transmitted by the interface.
+.PP
+The options are as follows:
+.TP
+.B -a
+Display absolute values rather than deltas. With this option, all
+reports show statistics for the time since the link was initiated.
+Without this option, the second and subsequent reports show statistics
+for the time since the last report.
+.TP
+.B -c \fIcount
+Repeat the display
+.I count
+times. If this option is not specified, the default repeat count is 1
+if the
+.B -w
+option is not specified, otherwise infinity.
+.TP
+.B -r
+Display additional statistics summarizing the compression ratio
+achieved by the packet compression algorithm in use.
+.TP
+.B -v
+Display additional statistics relating to the performance of the Van
+Jacobson TCP header compression algorithm.
+.TP
+.B -w \fIwait
+Pause
+.I wait
+seconds between each display. If this option is not specified, the
+default interval is 5 seconds.
+.TP
+.B -z
+Instead of the standard display, show statistics indicating the
+performance of the packet compression algorithm in use.
+.PP
+The following fields are printed on the input side when the
+.B -z
+option is not used:
+.TP
+.B IN
+The total number of bytes received by this interface.
+.TP
+.B PACK
+The total number of packets received by this interface.
+.TP
+.B VJCOMP
+The number of header-compressed TCP packets received by this interface.
+.TP
+.B VJUNC
+The number of header-uncompressed TCP packets received by this
+interface. Not reported when the
+.B -r
+option is specified.
+.TP
+.B VJERR
+The number of corrupted or bogus header-compressed TCP packets
+received by this interface. Not reported when the
+.B -r
+option is specified.
+.TP
+.B VJTOSS
+The number of VJ header-compressed TCP packets dropped on reception by
+this interface because of preceding errors. Only reported when the
+.B -v
+option is specified.
+.TP
+.B NON-VJ
+The total number of non-TCP packets received by this interface.
+Only
+reported when the
+.B -v
+option is specified.
+.TP
+.B RATIO
+The compression ratio achieved for received packets by the
+packet compression scheme in use, defined as the uncompressed size
+divided by the compressed size.
+Only reported when the
+.B -r
+option is specified.
+.TP
+.B UBYTE
+The total number of bytes received, after decompression of compressed
+packets. Only reported when the
+.B -r
+option is specified.
+.PP
+The following fields are printed on the output side:
+.TP
+.B OUT
+The total number of bytes transmitted from this interface.
+.TP
+.B PACK
+The total number of packets transmitted from this interface.
+.TP
+.B VJCOMP
+The number of TCP packets transmitted from this interface with
+VJ-compressed TCP headers.
+.TP
+.B VJUNC
+The number of TCP packets transmitted from this interface with
+VJ-uncompressed TCP headers.
+Not reported when the
+.B -r
+option is specified.
+.TP
+.B NON-VJ
+The total number of non-TCP packets transmitted from this interface.
+Not reported when the
+.B -r
+option is specified.
+.TP
+.B VJSRCH
+The number of searches for the cached header entry for a VJ header
+compressed TCP packet. Only reported when the
+.B -v
+option is specified.
+.TP
+.B VJMISS
+The number of failed searches for the cached header entry for a
+VJ header compressed TCP packet. Only reported when the
+.B -v
+option is specified.
+.TP
+.B RATIO
+The compression ratio achieved for transmitted packets by the
+packet compression scheme in use, defined as the size
+before compression divided by the compressed size.
+Only reported when the
+.B -r
+option is specified.
+.TP
+.B UBYTE
+The total number of bytes to be transmitted, before packet compression
+is applied. Only reported when the
+.B -r
+option is specified.
+.PP
+When the
+.B -z
+option is specified,
+.B pppstats
+instead displays the following fields, relating to the packet
+compression algorithm currently in use. If packet compression is not
+in use, these fields will all display zeroes. The fields displayed on
+the input side are:
+.TP
+.B COMPRESSED BYTE
+The number of bytes of compressed packets received.
+.TP
+.B COMPRESSED PACK
+The number of compressed packets received.
+.TP
+.B INCOMPRESSIBLE BYTE
+The number of bytes of incompressible packets (that is, those which
+were transmitted in uncompressed form) received.
+.TP
+.B INCOMPRESSIBLE PACK
+The number of incompressible packets received.
+.TP
+.B COMP RATIO
+The recent compression ratio for incoming packets, defined as the
+uncompressed size divided by the compressed size (including both
+compressible and incompressible packets).
+.PP
+The fields displayed on the output side are:
+.TP
+.B COMPRESSED BYTE
+The number of bytes of compressed packets transmitted.
+.TP
+.B COMPRESSED PACK
+The number of compressed packets transmitted.
+.TP
+.B INCOMPRESSIBLE BYTE
+The number of bytes of incompressible packets transmitted (that is,
+those which were transmitted in uncompressed form).
+.TP
+.B INCOMPRESSIBLE PACK
+The number of incompressible packets transmitted.
+.TP
+.B COMP RATIO
+The recent compression ratio for outgoing packets.
+.SH SEE ALSO
+pppd(8)
diff --git a/usr.sbin/pppstats/pppstats.c b/usr.sbin/pppstats/pppstats.c
new file mode 100644
index 0000000..b9d5a09
--- /dev/null
+++ b/usr.sbin/pppstats/pppstats.c
@@ -0,0 +1,519 @@
+/*
+ * print PPP statistics:
+ * pppstats [-a|-d] [-v|-r|-z] [-c count] [-w wait] [interface]
+ *
+ * -a Show absolute values rather than deltas
+ * -d Show data rate (kB/s) rather than bytes
+ * -v Show more stats for VJ TCP header compression
+ * -r Show compression ratio
+ * -z Show compression statistics instead of default display
+ *
+ * History:
+ * perkins@cps.msu.edu: Added compression statistics and alternate
+ * display. 11/94
+ * Brad Parker (brad@cayman.com) 6/92
+ *
+ * from the original "slstats" by Van Jacobson
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 University of California, Berkeley. The name of the
+ * University 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$FreeBSD$";
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <net/ppp_defs.h>
+
+#ifndef STREAMS
+#include <sys/socket.h> /* *BSD, Linux, NeXT, Ultrix etc. */
+#include <net/if.h>
+#include <net/if_ppp.h>
+
+#else /* STREAMS */
+#include <sys/stropts.h> /* SVR4, Solaris 2, SunOS 4, OSF/1, etc. */
+#include <net/pppio.h>
+
+#endif /* STREAMS */
+
+int vflag, rflag, zflag; /* select type of display */
+int aflag; /* print absolute values, not deltas */
+int dflag; /* print data rates, not bytes */
+int interval, count;
+int infinite;
+int unit;
+int s; /* socket or /dev/ppp file descriptor */
+int signalled; /* set if alarm goes off "early" */
+char *progname;
+char *interface;
+
+#if defined(SUNOS4) || defined(ULTRIX) || defined(NeXT)
+extern int optind;
+extern char *optarg;
+#endif
+
+static void usage(void);
+static void catchalarm(int);
+static void get_ppp_stats(struct ppp_stats *);
+static void get_ppp_cstats(struct ppp_comp_stats *);
+static void intpr(void);
+
+int main(int, char *argv[]);
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: %s [-a|-d] [-v|-r|-z] [-c count] [-w wait] [interface]\n",
+ progname);
+ exit(1);
+}
+
+/*
+ * Called if an interval expires before intpr has completed a loop.
+ * Sets a flag to not wait for the alarm.
+ */
+static void
+catchalarm(arg)
+ int arg;
+{
+ signalled = 1;
+}
+
+
+#ifndef STREAMS
+static void
+get_ppp_stats(curp)
+ struct ppp_stats *curp;
+{
+ struct ifpppstatsreq req;
+
+ memset (&req, 0, sizeof (req));
+
+#ifdef _linux_
+ req.stats_ptr = (caddr_t) &req.stats;
+#undef ifr_name
+#define ifr_name ifr__name
+#endif
+
+ strncpy(req.ifr_name, interface, sizeof(req.ifr_name));
+ if (ioctl(s, SIOCGPPPSTATS, &req) < 0) {
+ fprintf(stderr, "%s: ", progname);
+ if (errno == ENOTTY)
+ fprintf(stderr, "kernel support missing\n");
+ else
+ perror("couldn't get PPP statistics");
+ exit(1);
+ }
+ *curp = req.stats;
+}
+
+static void
+get_ppp_cstats(csp)
+ struct ppp_comp_stats *csp;
+{
+ struct ifpppcstatsreq creq;
+
+ memset (&creq, 0, sizeof (creq));
+
+#ifdef _linux_
+ creq.stats_ptr = (caddr_t) &creq.stats;
+#undef ifr_name
+#define ifr_name ifr__name
+#endif
+
+ strncpy(creq.ifr_name, interface, sizeof(creq.ifr_name));
+ if (ioctl(s, SIOCGPPPCSTATS, &creq) < 0) {
+ fprintf(stderr, "%s: ", progname);
+ if (errno == ENOTTY) {
+ fprintf(stderr, "no kernel compression support\n");
+ if (zflag)
+ exit(1);
+ rflag = 0;
+ } else {
+ perror("couldn't get PPP compression stats");
+ exit(1);
+ }
+ }
+
+#ifdef _linux_
+ if (creq.stats.c.bytes_out == 0)
+ creq.stats.c.ratio = 0.0;
+ else
+ creq.stats.c.ratio = (double) creq.stats.c.in_count /
+ (double) creq.stats.c.bytes_out;
+
+ if (creq.stats.d.bytes_out == 0)
+ creq.stats.d.ratio = 0.0;
+ else
+ creq.stats.d.ratio = (double) creq.stats.d.in_count /
+ (double) creq.stats.d.bytes_out;
+#endif
+
+ *csp = creq.stats;
+}
+
+#else /* STREAMS */
+
+int
+strioctl(fd, cmd, ptr, ilen, olen)
+ int fd, cmd, ilen, olen;
+ char *ptr;
+{
+ struct strioctl str;
+
+ str.ic_cmd = cmd;
+ str.ic_timout = 0;
+ str.ic_len = ilen;
+ str.ic_dp = ptr;
+ if (ioctl(fd, I_STR, &str) == -1)
+ return -1;
+ if (str.ic_len != olen)
+ fprintf(stderr, "strioctl: expected %d bytes, got %d for cmd %x\n",
+ olen, str.ic_len, cmd);
+ return 0;
+}
+
+static void
+get_ppp_stats(curp)
+ struct ppp_stats *curp;
+{
+ if (strioctl(s, PPPIO_GETSTAT, curp, 0, sizeof(*curp)) < 0) {
+ fprintf(stderr, "%s: ", progname);
+ if (errno == EINVAL)
+ fprintf(stderr, "kernel support missing\n");
+ else
+ perror("couldn't get PPP statistics");
+ exit(1);
+ }
+}
+
+static void
+get_ppp_cstats(csp)
+ struct ppp_comp_stats *csp;
+{
+ if (strioctl(s, PPPIO_GETCSTAT, csp, 0, sizeof(*csp)) < 0) {
+ fprintf(stderr, "%s: ", progname);
+ if (errno == ENOTTY) {
+ fprintf(stderr, "no kernel compression support\n");
+ if (zflag)
+ exit(1);
+ rflag = 0;
+ } else {
+ perror("couldn't get PPP compression statistics");
+ exit(1);
+ }
+ }
+}
+
+#endif /* STREAMS */
+
+#define MAX0(a) ((int)(a) > 0? (a): 0)
+#define V(offset) MAX0(cur.offset - old.offset)
+#define W(offset) MAX0(ccs.offset - ocs.offset)
+
+#define RATIO(c, i, u) ((c) == 0? 1.0: (u) / ((double)(c) + (i)))
+#define CRATE(x) RATIO(W(x.comp_bytes), W(x.inc_bytes), W(x.unc_bytes))
+
+#define KBPS(n) ((n) / (interval * 1000.0))
+
+/*
+ * Print a running summary of interface statistics.
+ * Repeat display every interval seconds, showing statistics
+ * collected over that interval. Assumes that interval is non-zero.
+ * First line printed is cumulative.
+ */
+static void
+intpr()
+{
+ register int line = 0;
+ sigset_t oldmask, mask;
+ char *bunit;
+ int ratef = 0;
+ struct ppp_stats cur, old;
+ struct ppp_comp_stats ccs, ocs;
+
+ memset(&old, 0, sizeof(old));
+ memset(&ocs, 0, sizeof(ocs));
+
+ while (1) {
+ get_ppp_stats(&cur);
+ if (zflag || rflag)
+ get_ppp_cstats(&ccs);
+
+ (void)signal(SIGALRM, catchalarm);
+ signalled = 0;
+ (void)alarm(interval);
+
+ if ((line % 20) == 0) {
+ if (zflag) {
+ printf("IN: COMPRESSED INCOMPRESSIBLE COMP | ");
+ printf("OUT: COMPRESSED INCOMPRESSIBLE COMP\n");
+ bunit = dflag? "KB/S": "BYTE";
+ printf(" %s PACK %s PACK RATIO | ", bunit, bunit);
+ printf(" %s PACK %s PACK RATIO", bunit, bunit);
+ } else {
+ printf("%8.8s %6.6s %6.6s",
+ "IN", "PACK", "VJCOMP");
+
+ if (!rflag)
+ printf(" %6.6s %6.6s", "VJUNC", "VJERR");
+ if (vflag)
+ printf(" %6.6s %6.6s", "VJTOSS", "NON-VJ");
+ if (rflag)
+ printf(" %6.6s %6.6s", "RATIO", "UBYTE");
+ printf(" | %8.8s %6.6s %6.6s",
+ "OUT", "PACK", "VJCOMP");
+
+ if (!rflag)
+ printf(" %6.6s %6.6s", "VJUNC", "NON-VJ");
+ if (vflag)
+ printf(" %6.6s %6.6s", "VJSRCH", "VJMISS");
+ if (rflag)
+ printf(" %6.6s %6.6s", "RATIO", "UBYTE");
+ }
+ putchar('\n');
+ }
+
+ if (zflag) {
+ if (ratef) {
+ printf("%8.3f %6u %8.3f %6u %6.2f",
+ KBPS(W(d.comp_bytes)),
+ W(d.comp_packets),
+ KBPS(W(d.inc_bytes)),
+ W(d.inc_packets),
+ ccs.d.ratio / 256.0);
+ printf(" | %8.3f %6u %8.3f %6u %6.2f",
+ KBPS(W(c.comp_bytes)),
+ W(c.comp_packets),
+ KBPS(W(c.inc_bytes)),
+ W(c.inc_packets),
+ ccs.c.ratio / 256.0);
+ } else {
+ printf("%8u %6u %8u %6u %6.2f",
+ W(d.comp_bytes),
+ W(d.comp_packets),
+ W(d.inc_bytes),
+ W(d.inc_packets),
+ ccs.d.ratio / 256.0);
+ printf(" | %8u %6u %8u %6u %6.2f",
+ W(c.comp_bytes),
+ W(c.comp_packets),
+ W(c.inc_bytes),
+ W(c.inc_packets),
+ ccs.c.ratio / 256.0);
+ }
+
+ } else {
+ if (ratef)
+ printf("%8.3f", KBPS(V(p.ppp_ibytes)));
+ else
+ printf("%8u", V(p.ppp_ibytes));
+ printf(" %6u %6u",
+ V(p.ppp_ipackets),
+ V(vj.vjs_compressedin));
+ if (!rflag)
+ printf(" %6u %6u",
+ V(vj.vjs_uncompressedin),
+ V(vj.vjs_errorin));
+ if (vflag)
+ printf(" %6u %6u",
+ V(vj.vjs_tossed),
+ V(p.ppp_ipackets) - V(vj.vjs_compressedin)
+ - V(vj.vjs_uncompressedin) - V(vj.vjs_errorin));
+ if (rflag) {
+ printf(" %6.2f ", CRATE(d));
+ if (ratef)
+ printf("%6.2f", KBPS(W(d.unc_bytes)));
+ else
+ printf("%6u", W(d.unc_bytes));
+ }
+ if (ratef)
+ printf(" | %8.3f", KBPS(V(p.ppp_obytes)));
+ else
+ printf(" | %8u", V(p.ppp_obytes));
+ printf(" %6u %6u",
+ V(p.ppp_opackets),
+ V(vj.vjs_compressed));
+ if (!rflag)
+ printf(" %6u %6u",
+ V(vj.vjs_packets) - V(vj.vjs_compressed),
+ V(p.ppp_opackets) - V(vj.vjs_packets));
+ if (vflag)
+ printf(" %6u %6u",
+ V(vj.vjs_searches),
+ V(vj.vjs_misses));
+ if (rflag) {
+ printf(" %6.2f ", CRATE(c));
+ if (ratef)
+ printf("%6.2f", KBPS(W(c.unc_bytes)));
+ else
+ printf("%6u", W(c.unc_bytes));
+ }
+
+ }
+
+ putchar('\n');
+ fflush(stdout);
+ line++;
+
+ count--;
+ if (!infinite && !count)
+ break;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGALRM);
+ sigprocmask(SIG_BLOCK, &mask, &oldmask);
+ if (!signalled) {
+ sigemptyset(&mask);
+ sigsuspend(&mask);
+ }
+ sigprocmask(SIG_SETMASK, &oldmask, NULL);
+ signalled = 0;
+ (void)alarm(interval);
+
+ if (!aflag) {
+ old = cur;
+ ocs = ccs;
+ ratef = dflag;
+ }
+ }
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c;
+#ifdef STREAMS
+ char *dev;
+#endif
+
+ interface = "ppp0";
+ if ((progname = strrchr(argv[0], '/')) == NULL)
+ progname = argv[0];
+ else
+ ++progname;
+
+ while ((c = getopt(argc, argv, "advrzc:w:")) != -1) {
+ switch (c) {
+ case 'a':
+ ++aflag;
+ break;
+ case 'd':
+ ++dflag;
+ break;
+ case 'v':
+ ++vflag;
+ break;
+ case 'r':
+ ++rflag;
+ break;
+ case 'z':
+ ++zflag;
+ break;
+ case 'c':
+ count = atoi(optarg);
+ if (count <= 0)
+ usage();
+ break;
+ case 'w':
+ interval = atoi(optarg);
+ if (interval <= 0)
+ usage();
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!interval && count)
+ interval = 5;
+ if (interval && !count)
+ infinite = 1;
+ if (!interval && !count)
+ count = 1;
+ if (aflag)
+ dflag = 0;
+
+ if (argc > 1)
+ usage();
+ if (argc > 0)
+ interface = argv[0];
+
+ if (sscanf(interface, "ppp%d", &unit) != 1) {
+ fprintf(stderr, "%s: invalid interface '%s' specified\n",
+ progname, interface);
+ }
+
+#ifndef STREAMS
+ {
+ struct ifreq ifr;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ fprintf(stderr, "%s: ", progname);
+ perror("couldn't create IP socket");
+ exit(1);
+ }
+
+#ifdef _linux_
+#undef ifr_name
+#define ifr_name ifr_ifrn.ifrn_name
+#endif
+ strncpy(ifr.ifr_name, interface, sizeof(ifr.ifr_name));
+ if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
+ fprintf(stderr, "%s: nonexistent interface '%s' specified\n",
+ progname, interface);
+ exit(1);
+ }
+ }
+
+#else /* STREAMS */
+#ifdef __osf__
+ dev = "/dev/streams/ppp";
+#else
+ dev = "/dev/ppp";
+#endif
+ if ((s = open(dev, O_RDONLY)) < 0) {
+ fprintf(stderr, "%s: couldn't open ", progname);
+ perror(dev);
+ exit(1);
+ }
+ if (strioctl(s, PPPIO_ATTACH, &unit, sizeof(int), 0) < 0) {
+ fprintf(stderr, "%s: ppp%d is not available\n", progname, unit);
+ exit(1);
+ }
+
+#endif /* STREAMS */
+
+ intpr();
+ exit(0);
+}
diff --git a/usr.sbin/praliases/Makefile b/usr.sbin/praliases/Makefile
new file mode 100644
index 0000000..81fd4d1
--- /dev/null
+++ b/usr.sbin/praliases/Makefile
@@ -0,0 +1,38 @@
+# @(#)Makefile 8.2 (Berkeley) 9/21/96
+# $FreeBSD$
+
+SENDMAIL_DIR= ${.CURDIR}/../../contrib/sendmail
+.PATH: ${SENDMAIL_DIR}/praliases
+
+PROG= praliases
+SRCS= praliases.c
+MAN= praliases.8
+
+CFLAGS+= -I${SENDMAIL_DIR}/src -I${SENDMAIL_DIR}/include -I.
+CFLAGS+= -DNEWDB -DNOT_SENDMAIL
+
+LIBSMDIR= ${.OBJDIR}/../../lib/libsm
+LIBSM= ${LIBSMDIR}/libsm.a
+
+LIBSMDBDIR= ${.OBJDIR}/../../lib/libsmdb
+LIBSMDB= ${LIBSMDBDIR}/libsmdb.a
+
+LIBSMUTILDIR= ${.OBJDIR}/../../lib/libsmutil
+LIBSMUTIL= ${LIBSMUTILDIR}/libsmutil.a
+
+DPADD= ${LIBSMDB} ${LIBSMUTIL} ${LIBSM}
+LDADD= ${LIBSMDB} ${LIBSMUTIL} ${LIBSM}
+
+SRCS+= sm_os.h
+CLEANFILES+=sm_os.h
+
+# User customizations to the sendmail build environment
+CFLAGS+= ${SENDMAIL_CFLAGS}
+LDFLAGS+=${SENDMAIL_LDFLAGS}
+DPADD+= ${SENDMAIL_DPADD}
+LDADD+= ${SENDMAIL_LDADD}
+
+sm_os.h:
+ ln -sf ${SENDMAIL_DIR}/include/sm/os/sm_os_freebsd.h sm_os.h
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/praudit/Makefile b/usr.sbin/praudit/Makefile
new file mode 100644
index 0000000..4ff08fd
--- /dev/null
+++ b/usr.sbin/praudit/Makefile
@@ -0,0 +1,14 @@
+#
+# $FreeBSD$
+#
+
+OPENBSMDIR=${.CURDIR}/../../contrib/openbsm
+.PATH: ${OPENBSMDIR}/bin/praudit
+
+PROG= praudit
+MAN= praudit.1
+
+DPADD= ${LIBBSM}
+LDADD= -lbsm
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/procctl/Makefile b/usr.sbin/procctl/Makefile
new file mode 100644
index 0000000..5cb35e7
--- /dev/null
+++ b/usr.sbin/procctl/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= procctl
+MAN= procctl.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/procctl/procctl.8 b/usr.sbin/procctl/procctl.8
new file mode 100644
index 0000000..1add286
--- /dev/null
+++ b/usr.sbin/procctl/procctl.8
@@ -0,0 +1,34 @@
+.\" $FreeBSD$
+.Dd November 23, 1997
+.Dt PROCCTL 8
+.Os
+.Sh NAME
+.Nm procctl
+.Nd clear procfs event flags
+.Sh SYNOPSIS
+.Nm
+.Ar pid ...
+.Sh DESCRIPTION
+The
+.Nm
+utility clears the
+.Xr procfs 5
+event mask used by
+.Xr truss 1 .
+This can be used in the event that a process is left stranded, since
+the
+.Xr procfs 5
+events result in a non-killable process.
+The arguments are a list of process IDs;
+.Nm
+goes through the list and clears the event masks for each specified process.
+.Sh SEE ALSO
+.Xr truss 1 ,
+.Xr procfs 5
+.Sh HISTORY
+The
+.Nm
+utility was written by
+.An Sean Eric Fagan
+for
+.Fx .
diff --git a/usr.sbin/procctl/procctl.c b/usr.sbin/procctl/procctl.c
new file mode 100644
index 0000000..5bd1f21
--- /dev/null
+++ b/usr.sbin/procctl/procctl.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright 1997 Sean Eric Fagan
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Sean Eric Fagan
+ * 4. Neither the name of the author may 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$");
+
+/*
+ * procctl -- clear the event mask, and continue, any specified processes.
+ * This is largely an example of how to use the procfs interface; however,
+ * for now, it is also sometimes necessary, as a stopped process will not
+ * otherwise continue. (This will be fixed in a later version of the
+ * procfs code, almost certainly; however, this program will still be useful
+ * for some annoying circumstances.)
+ */
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/pioctl.h>
+
+int
+main(int ac, char **av) {
+ int fd;
+ int i;
+
+ for (i = 1; i < ac; i++) {
+ char buf[32];
+
+ snprintf(buf, sizeof(buf), "/proc/%s/mem", av[i]);
+ fd = open(buf, O_RDWR);
+ if (fd == -1) {
+ if (errno == ENOENT)
+ continue;
+ warn("cannot open pid %s", av[i]);
+ continue;
+ }
+ if (ioctl(fd, PIOCBIC, ~0) == -1)
+ warn("cannot clear process %s's event mask", av[i]);
+ if (ioctl(fd, PIOCCONT, 0) == -1 && errno != EINVAL)
+ warn("cannot continue process %s", av[i]);
+ close(fd);
+ }
+ return 0;
+}
diff --git a/usr.sbin/pstat/Makefile b/usr.sbin/pstat/Makefile
new file mode 100644
index 0000000..8fd2ced
--- /dev/null
+++ b/usr.sbin/pstat/Makefile
@@ -0,0 +1,14 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= pstat
+LINKS= ${BINDIR}/pstat ${BINDIR}/swapinfo
+MAN= pstat.8
+MLINKS= pstat.8 swapinfo.8
+
+WARNS?= 3
+
+DPADD= ${LIBKVM} ${LIBUTIL}
+LDADD= -lkvm -lutil
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pstat/pstat.8 b/usr.sbin/pstat/pstat.8
new file mode 100644
index 0000000..5e3760c
--- /dev/null
+++ b/usr.sbin/pstat/pstat.8
@@ -0,0 +1,251 @@
+.\" Copyright (c) 1980, 1991, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\" Copyright (c) 2002 Networks Associates Technology, Inc.
+.\" All rights reserved.
+.\"
+.\" Portions of this software was developed for the FreeBSD Project by
+.\" ThinkSec AS 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.
+.\" 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.
+.\"
+.\" @(#)pstat.8 8.5 (Berkeley) 5/13/94
+.\" $FreeBSD$
+.\"
+.Dd August 20, 2008
+.Dt PSTAT 8
+.Os
+.Sh NAME
+.Nm pstat ,
+.Nm swapinfo
+.Nd display system data structures
+.Sh SYNOPSIS
+.Nm
+.Op Fl Tfghkmnst
+.Op Fl M Ar core Op Fl N Ar system
+.Nm swapinfo
+.Op Fl ghkm
+.Op Fl M Ar core Op Fl N Ar system
+.Sh DESCRIPTION
+The
+.Nm
+utility displays open file entry, swap space utilization,
+terminal state, and vnode data structures.
+.Pp
+If invoked as
+.Nm swapinfo
+the
+.Fl s
+option is implied, and only the
+.Fl k , m , g ,
+and
+.Fl h
+options are legal.
+.Pp
+If the
+.Fl M
+option is not specified, information is obtained from
+the currently running kernel via the
+.Xr sysctl 3
+interface.
+Otherwise, information is read from the specified core file,
+using the name list from the specified kernel image (or from
+the default image).
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl n
+Print devices out by major/minor instead of name.
+.It Fl h
+.Dq Human-readable
+output.
+Use unit suffixes when printing swap partition sizes:
+Byte, Kilobyte, Megabyte, Gigabyte, Terabyte and Petabyte.
+.It Fl k
+Print sizes in kilobytes, regardless of the setting of the
+.Ev BLOCKSIZE
+environment variable.
+.It Fl m
+Print sizes in megabytes, regardless of the setting of the
+.Ev BLOCKSIZE
+environment variable.
+.It Fl g
+Print sizes in gigabytes, regardless of the setting of the
+.Ev BLOCKSIZE
+environment variable.
+.It Fl T
+Print the number of used and free slots in several system tables.
+This is useful for checking to see how large system tables have become
+if the system is under heavy load.
+.It Fl f
+Print the open file table with these headings:
+.Bl -tag -width indent
+.It LOC
+The core location of this table entry.
+.It TYPE
+The type of object the file table entry points to.
+.It FLG
+Miscellaneous state variables encoded thus:
+.Pp
+.Bl -tag -width indent -compact
+.It R
+open for reading
+.It W
+open for writing
+.It A
+open for appending
+.It I
+signal pgrp when data ready
+.El
+.It CNT
+Number of processes that know this open file.
+.It MSG
+Number of messages outstanding for this file.
+.It DATA
+The location of the vnode table entry or socket structure for this file.
+.It OFFSET
+The file offset (see
+.Xr lseek 2 ) .
+.El
+.It Fl s
+Print information about swap space usage on all the
+swap areas compiled into the kernel.
+The first column is the device name of the partition.
+The next column is
+the total space available in the partition.
+The
+.Ar Used
+column indicates the total blocks used so far; the
+.Ar Available
+column indicates how much space is remaining on each partition.
+The
+.Ar Capacity
+reports the percentage of space used.
+.Pp
+If more than one partition is configured into the system, totals for all
+of the statistics will be reported in the final line of the report.
+.It Fl t
+Print table for terminals
+with these headings:
+.Bl -tag -width indent
+.It LINE
+Device name.
+.It INQ
+Number of characters that can be stored in the input queue.
+.It CAN
+Number of characters in the input queue which can be read.
+.It LIN
+Number of characters in the input queue which cannot be read yet.
+.It LOW
+Low water mark for input.
+.It OUTQ
+Number of characters that can be stored in the output queue.
+.It USE
+Number of bytes in the output queue.
+.It LOW
+Low water mark for output.
+.It COL
+Calculated column position of terminal.
+.It SESS
+Kernel address of the session structure.
+.It PGID
+Process group for which this is the controlling terminal.
+.It STATE
+Miscellaneous state variables encoded thus:
+.Pp
+.Bl -tag -width indent -compact
+.It I
+init/lock-state device nodes present
+.It C
+callout device nodes present
+.It O
+opened
+.It c
+console in use
+.It G
+gone
+.It B
+busy in
+.Xr open 2
+.It Y
+send SIGIO for input events
+.It L
+next character is literal
+.It H
+high watermark reached
+.It X
+open for exclusive use
+.It S
+output stopped (ixon flow control)
+.It l
+block mode input routine in use
+.It Z
+connection lost
+.It s
+i/o being snooped
+.It b
+busy in
+.Xr read 2
+or
+.Xr write 2
+.El
+.Pp
+The
+.Ql i
+and
+.Ql o
+characters refer to the previous character, to differentiate between
+input and output.
+.El
+.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 SEE ALSO
+.Xr ps 1 ,
+.Xr systat 1 ,
+.Xr stat 2 ,
+.Xr fs 5 ,
+.Xr iostat 8 ,
+.Xr vmstat 8
+.Rs
+.%T UNIX Implementation
+.%A K. Thompson
+.Re
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.0 .
+.Sh BUGS
+Does not understand
+.Tn NFS
+swap servers.
diff --git a/usr.sbin/pstat/pstat.c b/usr.sbin/pstat/pstat.c
new file mode 100644
index 0000000..8272eee
--- /dev/null
+++ b/usr.sbin/pstat/pstat.c
@@ -0,0 +1,584 @@
+/*-
+ * Copyright (c) 1980, 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 2002 Networks Associates Technologies, Inc.
+ * All rights reserved.
+ *
+ * Portions of this software were developed for the FreeBSD Project by
+ * ThinkSec AS 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.
+ * 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, 1991, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)pstat.c 8.16 (Berkeley) 5/9/95";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/stdint.h>
+#include <sys/ioctl.h>
+#include <sys/tty.h>
+#include <sys/blist.h>
+
+#include <sys/sysctl.h>
+#include <vm/vm_param.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <libutil.h>
+#include <limits.h>
+#include <nlist.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+enum {
+ NL_CONSTTY,
+ NL_MAXFILES,
+ NL_NFILES,
+ NL_TTY_LIST
+};
+
+static struct nlist nl[] = {
+ { .n_name = "_constty" },
+ { .n_name = "_maxfiles" },
+ { .n_name = "_openfiles" },
+ { .n_name = "_tty_list" },
+ { .n_name = "" }
+};
+
+static int humanflag;
+static int usenumflag;
+static int totalflag;
+static int swapflag;
+static char *nlistf;
+static char *memf;
+static kvm_t *kd;
+
+static char *usagestr;
+
+static void filemode(void);
+static int getfiles(char **, size_t *);
+static void swapmode(void);
+static void ttymode(void);
+static void ttyprt(struct xtty *);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ int ch, i, quit, ret;
+ int fileflag, ttyflag;
+ char buf[_POSIX2_LINE_MAX],*opts;
+
+ fileflag = swapflag = ttyflag = 0;
+
+ /* We will behave like good old swapinfo if thus invoked */
+ opts = strrchr(argv[0], '/');
+ if (opts)
+ opts++;
+ else
+ opts = argv[0];
+ if (!strcmp(opts, "swapinfo")) {
+ swapflag = 1;
+ opts = "ghkmM:N:";
+ usagestr = "swapinfo [-ghkm] [-M core [-N system]]";
+ } else {
+ opts = "TM:N:fghkmnst";
+ usagestr = "pstat [-Tfghkmnst] [-M core [-N system]]";
+ }
+
+ while ((ch = getopt(argc, argv, opts)) != -1)
+ switch (ch) {
+ case 'f':
+ fileflag = 1;
+ break;
+ case 'g':
+ setenv("BLOCKSIZE", "1G", 1);
+ break;
+ case 'h':
+ humanflag = 1;
+ break;
+ case 'k':
+ setenv("BLOCKSIZE", "1K", 1);
+ break;
+ case 'm':
+ setenv("BLOCKSIZE", "1M", 1);
+ break;
+ case 'M':
+ memf = optarg;
+ break;
+ case 'N':
+ nlistf = optarg;
+ break;
+ case 'n':
+ usenumflag = 1;
+ break;
+ case 's':
+ ++swapflag;
+ break;
+ case 'T':
+ totalflag = 1;
+ break;
+ case 't':
+ ttyflag = 1;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (memf != NULL) {
+ kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, buf);
+ if (kd == NULL)
+ errx(1, "kvm_openfiles: %s", buf);
+ if ((ret = kvm_nlist(kd, nl)) != 0) {
+ if (ret == -1)
+ errx(1, "kvm_nlist: %s", kvm_geterr(kd));
+ quit = 0;
+ for (i = 0; nl[i].n_name[0] != '\0'; ++i)
+ if (nl[i].n_value == 0) {
+ quit = 1;
+ warnx("undefined symbol: %s",
+ nl[i].n_name);
+ }
+ if (quit)
+ exit(1);
+ }
+ }
+ if (!(fileflag | ttyflag | swapflag | totalflag))
+ usage();
+ if (fileflag || totalflag)
+ filemode();
+ if (ttyflag)
+ ttymode();
+ if (swapflag || totalflag)
+ swapmode();
+ exit (0);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: %s\n", usagestr);
+ exit (1);
+}
+
+static const char fhdr32[] =
+ " LOC TYPE FLG CNT MSG DATA OFFSET\n";
+/* c0000000 ------ RWAI 123 123 c0000000 1000000000000000 */
+
+static const char fhdr64[] =
+ " LOC TYPE FLG CNT MSG DATA OFFSET\n";
+/* c000000000000000 ------ RWAI 123 123 c000000000000000 1000000000000000 */
+
+static const char hdr[] =
+" LINE INQ CAN LIN LOW OUTQ USE LOW COL SESS PGID STATE\n";
+
+static void
+ttymode_kvm(void)
+{
+ TAILQ_HEAD(, tty) tl;
+ struct tty *tp, tty;
+ struct xtty xt;
+
+ (void)printf("%s", hdr);
+ bzero(&xt, sizeof xt);
+ xt.xt_size = sizeof xt;
+ if (kvm_read(kd, nl[NL_TTY_LIST].n_value, &tl, sizeof tl) != sizeof tl)
+ errx(1, "kvm_read(): %s", kvm_geterr(kd));
+ tp = TAILQ_FIRST(&tl);
+ while (tp != NULL) {
+ if (kvm_read(kd, (u_long)tp, &tty, sizeof tty) != sizeof tty)
+ errx(1, "kvm_read(): %s", kvm_geterr(kd));
+ xt.xt_insize = tty.t_inq.ti_nblocks * TTYINQ_DATASIZE;
+ xt.xt_incc = tty.t_inq.ti_linestart - tty.t_inq.ti_begin;
+ xt.xt_inlc = tty.t_inq.ti_end - tty.t_inq.ti_linestart;
+ xt.xt_inlow = tty.t_inlow;
+ xt.xt_outsize = tty.t_outq.to_nblocks * TTYOUTQ_DATASIZE;
+ xt.xt_outcc = tty.t_outq.to_end - tty.t_outq.to_begin;
+ xt.xt_outlow = tty.t_outlow;
+ xt.xt_column = tty.t_column;
+ /* xt.xt_pgid = ... */
+ /* xt.xt_sid = ... */
+ xt.xt_flags = tty.t_flags;
+ xt.xt_dev = NODEV;
+ ttyprt(&xt);
+ tp = TAILQ_NEXT(&tty, t_list);
+ }
+}
+
+static void
+ttymode_sysctl(void)
+{
+ struct xtty *xt, *end;
+ void *xttys;
+ size_t len;
+
+ (void)printf("%s", hdr);
+ if ((xttys = malloc(len = sizeof *xt)) == NULL)
+ err(1, "malloc()");
+ while (sysctlbyname("kern.ttys", xttys, &len, 0, 0) == -1) {
+ if (errno != ENOMEM)
+ err(1, "sysctlbyname()");
+ len *= 2;
+ if ((xttys = realloc(xttys, len)) == NULL)
+ err(1, "realloc()");
+ }
+ if (len > 0) {
+ end = (struct xtty *)((char *)xttys + len);
+ for (xt = xttys; xt < end; xt++)
+ ttyprt(xt);
+ }
+}
+
+static void
+ttymode(void)
+{
+
+ if (kd != NULL)
+ ttymode_kvm();
+ else
+ ttymode_sysctl();
+}
+
+static struct {
+ int flag;
+ char val;
+} ttystates[] = {
+#if 0
+ { TF_NOPREFIX, 'N' },
+#endif
+ { TF_INITLOCK, 'I' },
+ { TF_CALLOUT, 'C' },
+
+ /* Keep these together -> 'Oi' and 'Oo'. */
+ { TF_OPENED, 'O' },
+ { TF_OPENED_IN, 'i' },
+ { TF_OPENED_OUT, 'o' },
+ { TF_OPENED_CONS, 'c' },
+
+ { TF_GONE, 'G' },
+ { TF_OPENCLOSE, 'B' },
+ { TF_ASYNC, 'Y' },
+ { TF_LITERAL, 'L' },
+
+ /* Keep these together -> 'Hi' and 'Ho'. */
+ { TF_HIWAT, 'H' },
+ { TF_HIWAT_IN, 'i' },
+ { TF_HIWAT_OUT, 'o' },
+
+ { TF_STOPPED, 'S' },
+ { TF_EXCLUDE, 'X' },
+ { TF_BYPASS, 'l' },
+ { TF_ZOMBIE, 'Z' },
+ { TF_HOOK, 's' },
+
+ /* Keep these together -> 'bi' and 'bo'. */
+ { TF_BUSY, 'b' },
+ { TF_BUSY_IN, 'i' },
+ { TF_BUSY_OUT, 'o' },
+
+ { 0, '\0'},
+};
+
+static void
+ttyprt(struct xtty *xt)
+{
+ int i, j;
+ char *name;
+
+ if (xt->xt_size != sizeof *xt)
+ errx(1, "struct xtty size mismatch");
+ if (usenumflag || xt->xt_dev == 0 ||
+ (name = devname(xt->xt_dev, S_IFCHR)) == NULL)
+ printf("%5d,%4d ", major(xt->xt_dev), minor(xt->xt_dev));
+ else
+ printf("%10s ", name);
+ printf("%5zu %4zu %4zu %4zu %5zu %4zu %4zu %5u %5d %5d ",
+ xt->xt_insize, xt->xt_incc, xt->xt_inlc,
+ (xt->xt_insize - xt->xt_inlow), xt->xt_outsize,
+ xt->xt_outcc, (xt->xt_outsize - xt->xt_outlow),
+ MIN(xt->xt_column, 99999), xt->xt_sid, xt->xt_pgid);
+ for (i = j = 0; ttystates[i].flag; i++)
+ if (xt->xt_flags & ttystates[i].flag) {
+ putchar(ttystates[i].val);
+ j++;
+ }
+ if (j == 0)
+ putchar('-');
+ putchar('\n');
+}
+
+static void
+filemode(void)
+{
+ struct xfile *fp;
+ char *buf, flagbuf[16], *fbp;
+ int maxf, openf;
+ size_t len;
+ static char *dtypes[] = { "???", "inode", "socket", "pipe",
+ "fifo", "kqueue", "crypto" };
+ int i;
+ int wid;
+
+ if (kd != NULL) {
+ if (kvm_read(kd, nl[NL_MAXFILES].n_value,
+ &maxf, sizeof maxf) != sizeof maxf ||
+ kvm_read(kd, nl[NL_NFILES].n_value,
+ &openf, sizeof openf) != sizeof openf)
+ errx(1, "kvm_read(): %s", kvm_geterr(kd));
+ } else {
+ len = sizeof(int);
+ if (sysctlbyname("kern.maxfiles", &maxf, &len, 0, 0) == -1 ||
+ sysctlbyname("kern.openfiles", &openf, &len, 0, 0) == -1)
+ err(1, "sysctlbyname()");
+ }
+
+ if (totalflag) {
+ (void)printf("%3d/%3d files\n", openf, maxf);
+ return;
+ }
+ if (getfiles(&buf, &len) == -1)
+ return;
+ openf = len / sizeof *fp;
+
+ (void)printf("%d/%d open files\n", openf, maxf);
+ printf(sizeof(uintptr_t) == 4 ? fhdr32 : fhdr64);
+ wid = (int)sizeof(uintptr_t) * 2;
+ for (fp = (struct xfile *)buf, i = 0; i < openf; ++fp, ++i) {
+ if ((size_t)fp->xf_type >= sizeof(dtypes) / sizeof(dtypes[0]))
+ continue;
+ (void)printf("%*jx", wid, (uintmax_t)(uintptr_t)fp->xf_file);
+ (void)printf(" %-6.6s", dtypes[fp->xf_type]);
+ fbp = flagbuf;
+ if (fp->xf_flag & FREAD)
+ *fbp++ = 'R';
+ if (fp->xf_flag & FWRITE)
+ *fbp++ = 'W';
+ if (fp->xf_flag & FAPPEND)
+ *fbp++ = 'A';
+ if (fp->xf_flag & FASYNC)
+ *fbp++ = 'I';
+ *fbp = '\0';
+ (void)printf(" %4s %3d", flagbuf, fp->xf_count);
+ (void)printf(" %3d", fp->xf_msgcount);
+ (void)printf(" %*jx", wid, (uintmax_t)(uintptr_t)fp->xf_data);
+ (void)printf(" %*jx\n", (int)sizeof(fp->xf_offset) * 2,
+ (uintmax_t)fp->xf_offset);
+ }
+ free(buf);
+}
+
+static int
+getfiles(char **abuf, size_t *alen)
+{
+ size_t len;
+ int mib[2];
+ char *buf;
+
+ /*
+ * XXX
+ * Add emulation of KINFO_FILE here.
+ */
+ if (kd != NULL)
+ errx(1, "files on dead kernel, not implemented");
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_FILE;
+ if (sysctl(mib, 2, NULL, &len, NULL, 0) == -1) {
+ warn("sysctl: KERN_FILE");
+ return (-1);
+ }
+ if ((buf = malloc(len)) == NULL)
+ errx(1, "malloc");
+ if (sysctl(mib, 2, buf, &len, NULL, 0) == -1) {
+ warn("sysctl: KERN_FILE");
+ return (-1);
+ }
+ *abuf = buf;
+ *alen = len;
+ return (0);
+}
+
+/*
+ * swapmode is based on a program called swapinfo written
+ * by Kevin Lahey <kml@rokkaku.atl.ga.us>.
+ */
+
+#define CONVERT(v) ((int64_t)(v) * pagesize / blocksize)
+static struct kvm_swap swtot;
+static int nswdev;
+
+static void
+print_swap_header(void)
+{
+ int hlen;
+ long blocksize;
+ const char *header;
+
+ header = getbsize(&hlen, &blocksize);
+ if (totalflag == 0)
+ (void)printf("%-15s %*s %8s %8s %8s\n",
+ "Device", hlen, header,
+ "Used", "Avail", "Capacity");
+}
+
+static void
+print_swap_line(const char *devname, intmax_t nblks, intmax_t bused,
+ intmax_t bavail, float bpercent)
+{
+ char usedbuf[5];
+ char availbuf[5];
+ int hlen, pagesize;
+ long blocksize;
+
+ pagesize = getpagesize();
+ getbsize(&hlen, &blocksize);
+
+ printf("%-15s %*jd ", devname, hlen, CONVERT(nblks));
+ if (humanflag) {
+ humanize_number(usedbuf, sizeof(usedbuf),
+ CONVERT(blocksize * bused), "",
+ HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
+ humanize_number(availbuf, sizeof(availbuf),
+ CONVERT(blocksize * bavail), "",
+ HN_AUTOSCALE, HN_B | HN_NOSPACE | HN_DECIMAL);
+ printf("%8s %8s %5.0f%%\n", usedbuf, availbuf, bpercent);
+ } else {
+ printf("%8jd %8jd %5.0f%%\n", (intmax_t)CONVERT(bused),
+ (intmax_t)CONVERT(bavail), bpercent);
+ }
+}
+
+static void
+print_swap(struct kvm_swap *ksw)
+{
+
+ swtot.ksw_total += ksw->ksw_total;
+ swtot.ksw_used += ksw->ksw_used;
+ ++nswdev;
+ if (totalflag == 0)
+ print_swap_line(ksw->ksw_devname, ksw->ksw_total,
+ ksw->ksw_used, ksw->ksw_total - ksw->ksw_used,
+ (ksw->ksw_used * 100.0) / ksw->ksw_total);
+}
+
+static void
+print_swap_total(void)
+{
+ int hlen, pagesize;
+ long blocksize;
+
+ pagesize = getpagesize();
+ getbsize(&hlen, &blocksize);
+ if (totalflag) {
+ blocksize = 1024 * 1024;
+ (void)printf("%jdM/%jdM swap space\n",
+ CONVERT(swtot.ksw_used), CONVERT(swtot.ksw_total));
+ } else if (nswdev > 1) {
+ print_swap_line("Total", swtot.ksw_total, swtot.ksw_used,
+ swtot.ksw_total - swtot.ksw_used,
+ (swtot.ksw_used * 100.0) / swtot.ksw_total);
+ }
+}
+
+static void
+swapmode_kvm(void)
+{
+ struct kvm_swap kswap[16];
+ int i, n;
+
+ n = kvm_getswapinfo(kd, kswap, sizeof kswap / sizeof kswap[0],
+ SWIF_DEV_PREFIX);
+
+ print_swap_header();
+ for (i = 0; i < n; ++i)
+ print_swap(&kswap[i]);
+ print_swap_total();
+}
+
+static void
+swapmode_sysctl(void)
+{
+ struct kvm_swap ksw;
+ struct xswdev xsw;
+ size_t mibsize, size;
+ int mib[16], n;
+
+ print_swap_header();
+ mibsize = sizeof mib / sizeof mib[0];
+ if (sysctlnametomib("vm.swap_info", mib, &mibsize) == -1)
+ err(1, "sysctlnametomib()");
+ 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");
+ if (xsw.xsw_dev == NODEV)
+ snprintf(ksw.ksw_devname, sizeof ksw.ksw_devname,
+ "<NFSfile>");
+ else
+ snprintf(ksw.ksw_devname, sizeof ksw.ksw_devname,
+ "/dev/%s", devname(xsw.xsw_dev, S_IFCHR));
+ ksw.ksw_used = xsw.xsw_used;
+ ksw.ksw_total = xsw.xsw_nblks;
+ ksw.ksw_flags = xsw.xsw_flags;
+ print_swap(&ksw);
+ }
+ if (errno != ENOENT)
+ err(1, "sysctl()");
+ print_swap_total();
+}
+
+static void
+swapmode(void)
+{
+ if (kd != NULL)
+ swapmode_kvm();
+ else
+ swapmode_sysctl();
+}
diff --git a/usr.sbin/pw/Makefile b/usr.sbin/pw/Makefile
new file mode 100644
index 0000000..8937124
--- /dev/null
+++ b/usr.sbin/pw/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+PROG= pw
+MAN= pw.conf.5 pw.8
+SRCS= pw.c pw_conf.c pw_user.c pw_group.c pw_log.c pw_nis.c pw_vpw.c \
+ grupd.c pwupd.c fileupd.c edgroup.c psdate.c \
+ bitmap.c cpdir.c rm_r.c
+
+DPADD= ${LIBCRYPT} ${LIBUTIL}
+LDADD= -lcrypt -lutil
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pw/README b/usr.sbin/pw/README
new file mode 100644
index 0000000..bbb1539
--- /dev/null
+++ b/usr.sbin/pw/README
@@ -0,0 +1,22 @@
+
+pw is a command-line driven passwd/group editor utility that provides
+an easy and safe means of modifying of any/all fields in the system
+password files, and has an add, modify and delete mode for user and
+group records. Command line options have been fashioned to be similar
+to those used by the Sun/shadow commands: useradd, usermod, userdel,
+groupadd, groupmod, groupdel, but combines all operations within the
+single command `pw'.
+
+User add mode also provides a means of easily setting system useradd
+defaults (see pw.conf.5), so that adding a user is as easy as issuing
+the command "pw useradd <loginid>". Creation of a unique primary
+group for each user and automatic membership in secondary groups
+is fully supported.
+
+This program may be FreeBSD specific, but should be trivial to port to
+other bsd4.4 variants.
+
+Author and maintainer: David L. Nugent, <davidn@blaze.net.au>
+
+$FreeBSD$
+
diff --git a/usr.sbin/pw/bitmap.c b/usr.sbin/pw/bitmap.c
new file mode 100644
index 0000000..bcfea7e
--- /dev/null
+++ b/usr.sbin/pw/bitmap.c
@@ -0,0 +1,132 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 <stdlib.h>
+#include <string.h>
+
+#include "bitmap.h"
+
+struct bitmap
+bm_alloc(int size)
+{
+ struct bitmap bm;
+ int szmap = (size / 8) + !!(size % 8);
+
+ bm.size = size;
+ bm.map = malloc(szmap);
+ if (bm.map)
+ memset(bm.map, 0, szmap);
+ return bm;
+}
+
+void
+bm_dealloc(struct bitmap * bm)
+{
+ if (bm->map)
+ free(bm->map);
+}
+
+static void
+bm_getmask(int *pos, unsigned char *bmask)
+{
+ *bmask = (unsigned char) (1 << (*pos % 8));
+ *pos /= 8;
+}
+
+void
+bm_setbit(struct bitmap * bm, int pos)
+{
+ unsigned char bmask;
+
+ bm_getmask(&pos, &bmask);
+ bm->map[pos] |= bmask;
+}
+
+void
+bm_clrbit(struct bitmap * bm, int pos)
+{
+ unsigned char bmask;
+
+ bm_getmask(&pos, &bmask);
+ bm->map[pos] &= ~bmask;
+}
+
+int
+bm_isset(struct bitmap * bm, int pos)
+{
+ unsigned char bmask;
+
+ bm_getmask(&pos, &bmask);
+ return !!(bm->map[pos] & bmask);
+}
+
+int
+bm_firstunset(struct bitmap * bm)
+{
+ int szmap = (bm->size / 8) + !!(bm->size % 8);
+ int at = 0;
+ int pos = 0;
+
+ while (pos < szmap) {
+ unsigned char bmv = bm->map[pos++];
+ unsigned char bmask = 1;
+
+ while (bmask & 0xff) {
+ if ((bmv & bmask) == 0)
+ return at;
+ bmask <<= 1;
+ ++at;
+ }
+ }
+ return at;
+}
+
+int
+bm_lastset(struct bitmap * bm)
+{
+ int szmap = (bm->size / 8) + !!(bm->size % 8);
+ int at = 0;
+ int pos = 0;
+ int ofs = 0;
+
+ while (pos < szmap) {
+ unsigned char bmv = bm->map[pos++];
+ unsigned char bmask = 1;
+
+ while (bmask & 0xff) {
+ if ((bmv & bmask) != 0)
+ ofs = at;
+ bmask <<= 1;
+ ++at;
+ }
+ }
+ return ofs;
+}
diff --git a/usr.sbin/pw/bitmap.h b/usr.sbin/pw/bitmap.h
new file mode 100644
index 0000000..4d6cfe4
--- /dev/null
+++ b/usr.sbin/pw/bitmap.h
@@ -0,0 +1,50 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 _BITMAP_H_
+#define _BITMAP_H_
+
+#include <sys/cdefs.h>
+
+struct bitmap
+{
+ int size;
+ unsigned char *map;
+};
+
+__BEGIN_DECLS
+struct bitmap bm_alloc(int size);
+void bm_dealloc(struct bitmap * bm);
+void bm_setbit(struct bitmap * bm, int pos);
+void bm_clrbit(struct bitmap * bm, int pos);
+int bm_isset(struct bitmap * bm, int pos);
+int bm_firstunset(struct bitmap * bm);
+int bm_lastset(struct bitmap * bm);
+__END_DECLS
+
+#endif /* !_BITMAP_H */
diff --git a/usr.sbin/pw/cpdir.c b/usr.sbin/pw/cpdir.c
new file mode 100644
index 0000000..f370421
--- /dev/null
+++ b/usr.sbin/pw/cpdir.c
@@ -0,0 +1,130 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <dirent.h>
+
+#include "pwupd.h"
+
+void
+copymkdir(char const * dir, char const * skel, mode_t mode, uid_t uid, gid_t gid)
+{
+ char src[MAXPATHLEN];
+ char dst[MAXPATHLEN];
+ char lnk[MAXPATHLEN];
+ int len;
+
+ if (mkdir(dir, mode) != 0 && errno != EEXIST) {
+ warn("mkdir(%s)", dir);
+ } else {
+ int infd, outfd;
+ struct stat st;
+
+ static char counter = 0;
+ static char *copybuf = NULL;
+
+ ++counter;
+ chown(dir, uid, gid);
+ if (skel != NULL && *skel != '\0') {
+ DIR *d = opendir(skel);
+
+ if (d != NULL) {
+ struct dirent *e;
+
+ while ((e = readdir(d)) != NULL) {
+ char *p = e->d_name;
+
+ if (snprintf(src, sizeof(src), "%s/%s", skel, p) >= (int)sizeof(src))
+ warn("warning: pathname too long '%s/%s' (skel not copied)", skel, p);
+ else if (lstat(src, &st) == 0) {
+ if (strncmp(p, "dot.", 4) == 0) /* Conversion */
+ p += 3;
+ if (snprintf(dst, sizeof(dst), "%s/%s", dir, p) >= (int)sizeof(dst))
+ warn("warning: path too long '%s/%s' (skel file skipped)", dir, p);
+ else {
+ if (S_ISDIR(st.st_mode)) { /* Recurse for this */
+ if (strcmp(e->d_name, ".") != 0 && strcmp(e->d_name, "..") != 0)
+ copymkdir(dst, src, (st.st_mode & 0777), uid, gid);
+ chflags(dst, st.st_flags); /* propogate flags */
+ } else if (S_ISLNK(st.st_mode) && (len = readlink(src, lnk, sizeof(lnk))) != -1) {
+ lnk[len] = '\0';
+ symlink(lnk, dst);
+ lchown(dst, uid, gid);
+ /*
+ * Note: don't propogate special attributes
+ * but do propogate file flags
+ */
+ } else if (S_ISREG(st.st_mode) && (outfd = open(dst, O_RDWR | O_CREAT | O_EXCL, st.st_mode)) != -1) {
+ if ((infd = open(src, O_RDONLY)) == -1) {
+ close(outfd);
+ remove(dst);
+ } else {
+ int b;
+
+ /*
+ * Allocate our copy buffer if we need to
+ */
+ if (copybuf == NULL)
+ copybuf = malloc(4096);
+ while ((b = read(infd, copybuf, 4096)) > 0)
+ write(outfd, copybuf, b);
+ close(infd);
+ /*
+ * Propogate special filesystem flags
+ */
+ fchown(outfd, uid, gid);
+ fchflags(outfd, st.st_flags);
+ close(outfd);
+ chown(dst, uid, gid);
+ }
+ }
+ }
+ }
+ }
+ closedir(d);
+ }
+ }
+ if (--counter == 0 && copybuf != NULL) {
+ free(copybuf);
+ copybuf = NULL;
+ }
+ }
+}
+
diff --git a/usr.sbin/pw/edgroup.c b/usr.sbin/pw/edgroup.c
new file mode 100644
index 0000000..1cc46b4
--- /dev/null
+++ b/usr.sbin/pw/edgroup.c
@@ -0,0 +1,229 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <grp.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <ctype.h>
+
+#include "pwupd.h"
+
+static int
+isingroup(char const * name, char **mem)
+{
+ int i;
+
+ for (i = 0; mem[i] != NULL; i++)
+ if (strcmp(name, mem[i]) == 0)
+ return i;
+ return -1;
+}
+
+int
+editgroups(char *name, char **groups)
+{
+ int rc = 0;
+ int infd;
+ char groupfile[MAXPATHLEN];
+ char grouptmp[MAXPATHLEN];
+
+ strncpy(groupfile, getgrpath(_GROUP), MAXPATHLEN - 5);
+ groupfile[MAXPATHLEN - 5] = '\0';
+ strcpy(grouptmp, groupfile);
+ strcat(grouptmp, ".new");
+
+ if ((infd = open(groupfile, O_RDWR | O_CREAT | O_EXLOCK, 0644)) != -1) {
+ FILE *infp;
+
+ if ((infp = fdopen(infd, "r+")) == NULL)
+ close(infd);
+ else {
+ int outfd;
+
+ if ((outfd = open(grouptmp, O_RDWR | O_CREAT | O_TRUNC, 0644)) != -1) {
+ FILE *outfp;
+
+ if ((outfp = fdopen(outfd, "w+")) == NULL)
+ close(outfd);
+ else {
+ int linelen = PWBUFSZ;
+ int outlen = PWBUFSZ;
+ int memlen = 200; /* Arbitrary */
+ char *line = malloc(linelen);
+ char *outl = malloc(outlen);
+ char **mems = malloc(memlen * sizeof(char *));
+ int namlen = strlen(name);
+
+ if (line == NULL || outl == NULL || mems == NULL) {
+ mem_abort:
+ rc = 0;
+ } else {
+ while (fgets(line, linelen, infp) != NULL) {
+ char *p;
+ int l;
+
+ while ((p = strchr(line, '\n')) == NULL)
+ {
+ if (extendline(&line, &linelen, linelen + PWBUFSZ) == -1) {
+ goto mem_abort;
+ }
+ l = strlen(line);
+ if (fgets(line + l, linelen - l, infp) == NULL)
+ break; /* No newline terminator on last line */
+ }
+ l = strlen(line) + namlen + 1;
+ if (extendline(&outl, &outlen, l) == -1) {
+ goto mem_abort;
+ }
+ if (*line == '#')
+ strcpy(outl, line);
+ else if (*line == '\n')
+ *outl = '\0';
+ else {
+ int i,
+ mno = 0;
+ char *cp = line;
+ char const *sep = ":\n";
+ struct group grp;
+
+ memset(&grp, 0, sizeof grp);
+ for (i = 0; (p = strsep(&cp, sep)) != NULL; i++) {
+ switch (i) {
+ case 0: /* Group name */
+ grp.gr_name = p;
+ break;
+ case 1: /* Group password */
+ grp.gr_passwd = p;
+ break;
+ case 2: /* Group id */
+ grp.gr_gid = atoi(p);
+ break;
+ case 3: /* Member list */
+ cp = p;
+ sep = ",\n";
+ break;
+ default: /* Individual members */
+ if (*p) {
+ if (extendarray(&mems, &memlen, mno + 2) == -1) {
+ goto mem_abort;
+ }
+ mems[mno++] = p;
+ }
+ break;
+ }
+ }
+ if (i < 2) /* Bail out - insufficient fields */
+ continue;
+
+ grp.gr_mem = mems;
+ for (i = mno; i < memlen; i++)
+ mems[i] = NULL;
+
+ /*
+ * Delete from group, or add to group?
+ */
+ if (groups == NULL || isingroup(grp.gr_name, groups) == -1) { /* Delete */
+ int idx;
+
+ while ((idx = isingroup(name, mems)) != -1) {
+ for (i = idx; i < (memlen - 1); i++)
+ mems[i] = mems[i + 1];
+ mems[i] = NULL;
+ --mno;
+ }
+ /*
+ * Special case - deleting user and group may be user's own
+ */
+ if (groups == NULL && mems[0] == NULL && strcmp(name, grp.gr_name) == 0) {
+ /*
+ * First, make _sure_ we don't have other members
+ */
+ struct passwd *pwd;
+
+ SETPWENT();
+ while ((pwd = GETPWENT()) != NULL && (gid_t)pwd->pw_gid != (gid_t)grp.gr_gid);
+ ENDPWENT();
+ if (pwd == NULL) /* No members at all */
+ continue; /* Drop the group */
+ }
+ } else if (isingroup(name, mems) == -1) {
+ if (extendarray(&mems, &memlen, mno + 2) == -1) {
+ goto mem_abort;
+ }
+ grp.gr_mem = mems; /* May have realloced() */
+ mems[mno++] = name;
+ mems[mno ] = NULL;
+ }
+ fmtgrentry(&outl, &outlen, &grp, PWF_GROUP);
+ }
+ fputs(outl, outfp);
+ }
+ if (fflush(outfp) != EOF) {
+ rc = 1;
+
+ /*
+ * Copy data back into the original file and truncate
+ */
+ rewind(infp);
+ rewind(outfp);
+ while (fgets(outl, outlen, outfp) != NULL)
+ fputs(outl, infp);
+
+ /*
+ * This is a gross hack, but we may have corrupted the
+ * original file.
+ */
+ if (fflush(infp) == EOF || ferror(infp))
+ rc = rename(grouptmp, groupfile) == 0;
+ else
+ ftruncate(infd, ftell(infp));
+ }
+ }
+ free(mems);
+ free(outl);
+ free(line);
+ fclose(outfp);
+ }
+ remove(grouptmp);
+ }
+ fclose(infp);
+ }
+ }
+ return rc;
+}
diff --git a/usr.sbin/pw/fileupd.c b/usr.sbin/pw/fileupd.c
new file mode 100644
index 0000000..b88f4fa
--- /dev/null
+++ b/usr.sbin/pw/fileupd.c
@@ -0,0 +1,203 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "pwupd.h"
+
+int
+extendline(char **buf, int * buflen, int needed)
+{
+ if (needed > *buflen) {
+ char *tmp = realloc(*buf, needed);
+ if (tmp == NULL)
+ return -1;
+ *buf = tmp;
+ *buflen = needed;
+ }
+ return *buflen;
+}
+
+int
+extendarray(char ***buf, int * buflen, int needed)
+{
+ if (needed > *buflen) {
+ char **tmp = realloc(*buf, needed * sizeof(char *));
+ if (tmp == NULL)
+ return -1;
+ *buf = tmp;
+ *buflen = needed;
+ }
+ return *buflen;
+}
+
+
+int
+fileupdate(char const * filename, mode_t fmode, char const * newline, char const * prefix, int pfxlen, int updmode)
+{
+ int rc = 0;
+
+ if (pfxlen <= 1)
+ rc = EINVAL;
+ else {
+ int infd = open(filename, O_RDWR | O_CREAT | O_EXLOCK, fmode);
+
+ if (infd == -1)
+ rc = errno;
+ else {
+ FILE *infp = fdopen(infd, "r+");
+
+ if (infp == NULL) {
+ rc = errno; /* Assumes fopen(3) sets errno from open(2) */
+ close(infd);
+ } else {
+ int outfd;
+ char file[MAXPATHLEN];
+
+ strcpy(file, filename);
+ strcat(file, ".new");
+ outfd = open(file, O_RDWR | O_CREAT | O_TRUNC, fmode);
+ if (outfd == -1)
+ rc = errno;
+ else {
+ FILE *outfp = fdopen(outfd, "w+");
+
+ if (outfp == NULL) {
+ rc = errno;
+ close(outfd);
+ } else {
+ int updated = UPD_CREATE;
+ int linesize = PWBUFSZ;
+ char *line = malloc(linesize);
+
+ nextline:
+ while (fgets(line, linesize, infp) != NULL) {
+ char *p = strchr(line, '\n');
+
+ while ((p = strchr(line, '\n')) == NULL) {
+ int l;
+ if (extendline(&line, &linesize, linesize + PWBUFSZ) == -1) {
+ int ch;
+ fputs(line, outfp);
+ while ((ch = fgetc(infp)) != EOF) {
+ fputc(ch, outfp);
+ if (ch == '\n')
+ break;
+ }
+ goto nextline;
+ }
+ l = strlen(line);
+ if (fgets(line + l, linesize - l, infp) == NULL)
+ break;
+ }
+ if (*line != '#' && *line != '\n') {
+ if (!updated && strncmp(line, prefix, pfxlen) == 0) {
+ updated = updmode == UPD_REPLACE ? UPD_REPLACE : UPD_DELETE;
+
+ /*
+ * Only actually write changes if updating
+ */
+ if (updmode == UPD_REPLACE)
+ strcpy(line, newline);
+ else if (updmode == UPD_DELETE)
+ continue;
+ }
+ }
+ fputs(line, outfp);
+ }
+
+ /*
+ * Now, we need to decide what to do: If we are in
+ * update mode, and no record was updated, then error If
+ * we are in insert mode, and record already exists,
+ * then error
+ */
+ if (updmode != updated)
+ /* -1 return means:
+ * update,delete=no user entry
+ * create=entry exists
+ */
+ rc = -1;
+ else {
+
+ /*
+ * If adding a new record, append it to the end
+ */
+ if (updmode == UPD_CREATE)
+ fputs(newline, outfp);
+
+ /*
+ * Flush the file and check for the result
+ */
+ if (fflush(outfp) == EOF)
+ rc = errno; /* Failed to update */
+ else {
+ /*
+ * Copy data back into the
+ * original file and truncate
+ */
+ rewind(infp);
+ rewind(outfp);
+ while (fgets(line, linesize, outfp) != NULL)
+ fputs(line, infp);
+
+ /*
+ * If there was a problem with copying
+ * we will just rename 'file.new'
+ * to 'file'.
+ * This is a gross hack, but we may have
+ * corrupted the original file
+ */
+ if (fflush(infp) == EOF || ferror(infp))
+ rename(file, filename);
+ else
+ ftruncate(infd, ftell(infp));
+ }
+ }
+ free(line);
+ fclose(outfp);
+ }
+ remove(file);
+ }
+ fclose(infp);
+ }
+ }
+ }
+ return rc;
+}
diff --git a/usr.sbin/pw/grupd.c b/usr.sbin/pw/grupd.c
new file mode 100644
index 0000000..edff76d
--- /dev/null
+++ b/usr.sbin/pw/grupd.c
@@ -0,0 +1,171 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+
+#include "pwupd.h"
+
+static char * grpath = _PATH_PWD;
+
+int
+setgrdir(const char * dir)
+{
+ if (dir == NULL)
+ return -1;
+ else {
+ char * d = malloc(strlen(dir)+1);
+ if (d == NULL)
+ return -1;
+ grpath = strcpy(d, dir);
+ }
+ return 0;
+}
+
+char *
+getgrpath(const char * file)
+{
+ static char pathbuf[MAXPATHLEN];
+
+ snprintf(pathbuf, sizeof pathbuf, "%s/%s", grpath, file);
+ return pathbuf;
+}
+
+int
+grdb(char *arg,...)
+{
+ /*
+ * This is a stub for now, but maybe eventually be functional
+ * if ever an indexed version of /etc/groups is implemented.
+ */
+ arg=arg;
+ return 0;
+}
+
+int
+fmtgrentry(char **buf, int * buflen, struct group * grp, int type)
+{
+ int i, l;
+
+ /*
+ * Since a group line is of arbitrary length,
+ * we need to calculate up-front just how long
+ * it will need to be...
+ */
+ /* groupname : password : gid : */
+ l = strlen(grp->gr_name) + 1 + strlen(grp->gr_passwd) + 1 + 5 + 1;
+ /* group members + comma separator */
+ for (i = 0; grp->gr_mem[i] != NULL; i++) {
+ l += strlen(grp->gr_mem[i]) + 1;
+ }
+ l += 2; /* For newline & NUL */
+ if (extendline(buf, buflen, l) == -1)
+ l = -1;
+ else{
+ /*
+ * Now we can safely format
+ */
+ if (type == PWF_STANDARD)
+ l = sprintf(*buf, "%s:*:%ld:", grp->gr_name, (long) grp->gr_gid);
+ else
+ l = sprintf(*buf, "%s:%s:%ld:", grp->gr_name, grp->gr_passwd, (long) grp->gr_gid);
+
+ /*
+ * List members
+ */
+ for (i = 0; grp->gr_mem[i] != NULL; i++) {
+ l += sprintf(*buf + l, "%s%s", i ? "," : "", grp->gr_mem[i]);
+ }
+
+ (*buf)[l++] = '\n';
+ (*buf)[l] = '\0';
+ }
+ return l;
+}
+
+
+int
+fmtgrent(char **buf, int * buflen, struct group * grp)
+{
+ return fmtgrentry(buf, buflen, grp, PWF_STANDARD);
+}
+
+
+static int
+gr_update(struct group * grp, char const * group, int mode)
+{
+ int l;
+ char pfx[64];
+ int grbuflen = 0;
+ char *grbuf = NULL;
+
+ ENDGRENT();
+ l = snprintf(pfx, sizeof pfx, "%s:", group);
+
+ /*
+ * Update the group file
+ */
+ if (grp != NULL && fmtgrentry(&grbuf, &grbuflen, grp, PWF_PASSWD) == -1)
+ l = -1;
+ else {
+ l = fileupdate(getgrpath(_GROUP), 0644, grbuf, pfx, l, mode);
+ if (l == 0)
+ l = grdb(NULL);
+ }
+ if (grbuf != NULL)
+ free(grbuf);
+ return l;
+}
+
+
+int
+addgrent(struct group * grp)
+{
+ return gr_update(grp, grp->gr_name, UPD_CREATE);
+}
+
+int
+chggrent(char const * login, struct group * grp)
+{
+ return gr_update(grp, login, UPD_REPLACE);
+}
+
+int
+delgrent(struct group * grp)
+{
+ return gr_update(NULL, grp->gr_name, UPD_DELETE);
+}
diff --git a/usr.sbin/pw/psdate.c b/usr.sbin/pw/psdate.c
new file mode 100644
index 0000000..3f4c010
--- /dev/null
+++ b/usr.sbin/pw/psdate.c
@@ -0,0 +1,295 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "psdate.h"
+
+
+static int
+a2i(char const ** str)
+{
+ int i = 0;
+ char const *s = *str;
+
+ if (isdigit((unsigned char)*s)) {
+ i = atoi(s);
+ while (isdigit((unsigned char)*s))
+ ++s;
+ *str = s;
+ }
+ return i;
+}
+
+static int
+numerics(char const * str)
+{
+ int rc = isdigit((unsigned char)*str);
+
+ if (rc)
+ while (isdigit((unsigned char)*str) || *str == 'x')
+ ++str;
+ return rc && !*str;
+}
+
+static int
+aindex(char const * arr[], char const ** str, int len)
+{
+ int l, i;
+ char mystr[32];
+
+ mystr[len] = '\0';
+ l = strlen(strncpy(mystr, *str, len));
+ for (i = 0; i < l; i++)
+ mystr[i] = (char) tolower((unsigned char)mystr[i]);
+ for (i = 0; arr[i] && strcmp(mystr, arr[i]) != 0; i++);
+ if (arr[i] == NULL)
+ i = -1;
+ else { /* Skip past it */
+ while (**str && isalpha((unsigned char)**str))
+ ++(*str);
+ /* And any following whitespace */
+ while (**str && (**str == ',' || isspace((unsigned char)**str)))
+ ++(*str);
+ } /* Return index */
+ return i;
+}
+
+static int
+weekday(char const ** str)
+{
+ static char const *days[] =
+ {"sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL};
+
+ return aindex(days, str, 3);
+}
+
+static int
+month(char const ** str)
+{
+ static char const *months[] =
+ {"jan", "feb", "mar", "apr", "may", "jun", "jul",
+ "aug", "sep", "oct", "nov", "dec", NULL};
+
+ return aindex(months, str, 3);
+}
+
+static void
+parse_time(char const * str, int *hour, int *min, int *sec)
+{
+ *hour = a2i(&str);
+ if ((str = strchr(str, ':')) == NULL)
+ *min = *sec = 0;
+ else {
+ ++str;
+ *min = a2i(&str);
+ *sec = ((str = strchr(str, ':')) == NULL) ? 0 : atoi(++str);
+ }
+}
+
+
+static void
+parse_datesub(char const * str, int *day, int *mon, int *year)
+{
+ int i;
+
+ static char const nchrs[] = "0123456789 \t,/-.";
+
+ if ((i = month(&str)) != -1) {
+ *mon = i;
+ if ((i = a2i(&str)) != 0)
+ *day = i;
+ } else if ((i = a2i(&str)) != 0) {
+ *day = i;
+ while (*str && strchr(nchrs + 10, *str) != NULL)
+ ++str;
+ if ((i = month(&str)) != -1)
+ *mon = i;
+ else if ((i = a2i(&str)) != 0)
+ *mon = i - 1;
+ } else
+ return;
+
+ while (*str && strchr(nchrs + 10, *str) != NULL)
+ ++str;
+ if (isdigit((unsigned char)*str)) {
+ *year = atoi(str);
+ if (*year > 1900)
+ *year -= 1900;
+ else if (*year < 32)
+ *year += 100;
+ }
+}
+
+
+/*-
+ * Parse time must be flexible, it handles the following formats:
+ * nnnnnnnnnnn UNIX timestamp (all numeric), 0 = now
+ * 0xnnnnnnnn UNIX timestamp in hexadecimal
+ * 0nnnnnnnnn UNIX timestamp in octal
+ * 0 Given time
+ * +nnnn[smhdwoy] Given time + nnnn hours, mins, days, weeks, months or years
+ * -nnnn[smhdwoy] Given time - nnnn hours, mins, days, weeks, months or years
+ * dd[ ./-]mmm[ ./-]yy Date }
+ * hh:mm:ss Time } May be combined
+ */
+
+time_t
+parse_date(time_t dt, char const * str)
+{
+ char *p;
+ int i;
+ long val;
+ struct tm *T;
+
+ if (dt == 0)
+ dt = time(NULL);
+
+ while (*str && isspace((unsigned char)*str))
+ ++str;
+
+ if (numerics(str)) {
+ dt = strtol(str, &p, 0);
+ } else if (*str == '+' || *str == '-') {
+ val = strtol(str, &p, 0);
+ switch (*p) {
+ case 'h':
+ case 'H': /* hours */
+ dt += (val * 3600L);
+ break;
+ case '\0':
+ case 'm':
+ case 'M': /* minutes */
+ dt += (val * 60L);
+ break;
+ case 's':
+ case 'S': /* seconds */
+ dt += val;
+ break;
+ case 'd':
+ case 'D': /* days */
+ dt += (val * 86400L);
+ break;
+ case 'w':
+ case 'W': /* weeks */
+ dt += (val * 604800L);
+ break;
+ case 'o':
+ case 'O': /* months */
+ T = localtime(&dt);
+ T->tm_mon += (int) val;
+ i = T->tm_mday;
+ goto fixday;
+ case 'y':
+ case 'Y': /* years */
+ T = localtime(&dt);
+ T->tm_year += (int) val;
+ i = T->tm_mday;
+ fixday:
+ dt = mktime(T);
+ T = localtime(&dt);
+ if (T->tm_mday != i) {
+ T->tm_mday = 1;
+ dt = mktime(T);
+ dt -= (time_t) 86400L;
+ }
+ default: /* unknown */
+ break; /* leave untouched */
+ }
+ } else {
+ char *q, tmp[64];
+
+ /*
+ * Skip past any weekday prefix
+ */
+ weekday(&str);
+ strlcpy(tmp, str, sizeof(tmp));
+ str = tmp;
+ T = localtime(&dt);
+
+ /*
+ * See if we can break off any timezone
+ */
+ while ((q = strrchr(tmp, ' ')) != NULL) {
+ if (strchr("(+-", q[1]) != NULL)
+ *q = '\0';
+ else {
+ int j = 1;
+
+ while (q[j] && isupper((unsigned char)q[j]))
+ ++j;
+ if (q[j] == '\0')
+ *q = '\0';
+ else
+ break;
+ }
+ }
+
+ /*
+ * See if there is a time hh:mm[:ss]
+ */
+ if ((p = strchr(tmp, ':')) == NULL) {
+
+ /*
+ * No time string involved
+ */
+ T->tm_hour = T->tm_min = T->tm_sec = 0;
+ parse_datesub(tmp, &T->tm_mday, &T->tm_mon, &T->tm_year);
+ } else {
+ char datestr[64], timestr[64];
+
+ /*
+ * Let's chip off the time string
+ */
+ if ((q = strpbrk(p, " \t")) != NULL) { /* Time first? */
+ int l = q - str;
+
+ strlcpy(timestr, str, l + 1);
+ strlcpy(datestr, q + 1, sizeof(datestr));
+ parse_time(timestr, &T->tm_hour, &T->tm_min, &T->tm_sec);
+ parse_datesub(datestr, &T->tm_mday, &T->tm_mon, &T->tm_year);
+ } else if ((q = strrchr(tmp, ' ')) != NULL) { /* Time last */
+ int l = q - tmp;
+
+ strlcpy(timestr, q + 1, sizeof(timestr));
+ strlcpy(datestr, tmp, l + 1);
+ } else /* Bail out */
+ return dt;
+ parse_time(timestr, &T->tm_hour, &T->tm_min, &T->tm_sec);
+ parse_datesub(datestr, &T->tm_mday, &T->tm_mon, &T->tm_year);
+ }
+ dt = mktime(T);
+ }
+ return dt;
+}
diff --git a/usr.sbin/pw/psdate.h b/usr.sbin/pw/psdate.h
new file mode 100644
index 0000000..a1e99d4
--- /dev/null
+++ b/usr.sbin/pw/psdate.h
@@ -0,0 +1,40 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 _PSDATE_H_
+#define _PSDATE_H_
+
+#include <time.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+time_t parse_date(time_t dt, char const * str);
+void print_date(char *buf, time_t t, int dotime);
+__END_DECLS
+
+#endif /* !_PSDATE_H_ */
diff --git a/usr.sbin/pw/pw.8 b/usr.sbin/pw/pw.8
new file mode 100644
index 0000000..4287055
--- /dev/null
+++ b/usr.sbin/pw/pw.8
@@ -0,0 +1,1001 @@
+.\" Copyright (C) 1996
+.\" David L. Nugent. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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, 2007
+.Dt PW 8
+.Os
+.Sh NAME
+.Nm pw
+.Nd create, remove, modify & display system users and groups
+.Sh SYNOPSIS
+.Nm
+.Op Fl V Ar etcdir
+.Ar useradd
+.Op name|uid
+.Op Fl C Ar config
+.Op Fl q
+.Op Fl n Ar name
+.Op Fl u Ar uid
+.Op Fl c Ar comment
+.Op Fl d Ar dir
+.Op Fl e Ar date
+.Op Fl p Ar date
+.Op Fl g Ar group
+.Op Fl G Ar grouplist
+.Op Fl m
+.Op Fl M Ar mode
+.Op Fl k Ar dir
+.Op Fl w Ar method
+.Op Fl s Ar shell
+.Op Fl o
+.Op Fl L Ar class
+.Op Fl h Ar fd | Fl H Ar fd
+.Op Fl N
+.Op Fl P
+.Op Fl Y
+.Nm
+.Op Fl V Ar etcdir
+.Ar useradd
+.Op name|uid
+.Fl D
+.Op Fl C Ar config
+.Op Fl q
+.Op Fl b Ar dir
+.Op Fl e Ar days
+.Op Fl p Ar days
+.Op Fl g Ar group
+.Op Fl G Ar grouplist
+.Op Fl k Ar dir
+.Op Fl M Ar mode
+.Op Fl u Ar min , Ns Ar max
+.Op Fl i Ar min , Ns Ar max
+.Op Fl w Ar method
+.Op Fl s Ar shell
+.Op Fl y Ar path
+.Nm
+.Op Fl V Ar etcdir
+.Ar userdel
+.Op name|uid
+.Op Fl n Ar name
+.Op Fl u Ar uid
+.Op Fl r
+.Op Fl Y
+.Nm
+.Op Fl V Ar etcdir
+.Ar usermod
+.Op name|uid
+.Op Fl C Ar config
+.Op Fl q
+.Op Fl n Ar name
+.Op Fl u Ar uid
+.Op Fl c Ar comment
+.Op Fl d Ar dir
+.Op Fl e Ar date
+.Op Fl p Ar date
+.Op Fl g Ar group
+.Op Fl G Ar grouplist
+.Op Fl l Ar name
+.Op Fl m
+.Op Fl M Ar mode
+.Op Fl k Ar dir
+.Op Fl w Ar method
+.Op Fl s Ar shell
+.Op Fl L Ar class
+.Op Fl h Ar fd | Fl H Ar fd
+.Op Fl N
+.Op Fl P
+.Op Fl Y
+.Nm
+.Op Fl V Ar etcdir
+.Ar usershow
+.Op name|uid
+.Op Fl n Ar name
+.Op Fl u Ar uid
+.Op Fl F
+.Op Fl P
+.Op Fl 7
+.Op Fl a
+.Nm
+.Op Fl V Ar etcdir
+.Ar usernext
+.Op Fl C Ar config
+.Op Fl q
+.Nm
+.Op Fl V Ar etcdir
+.Ar groupadd
+.Op group|gid
+.Op Fl C Ar config
+.Op Fl q
+.Op Fl n Ar group
+.Op Fl g Ar gid
+.Op Fl M Ar members
+.Op Fl o
+.Op Fl h Ar fd | Fl H Ar fd
+.Op Fl N
+.Op Fl P
+.Op Fl Y
+.Nm
+.Op Fl V Ar etcdir
+.Ar groupdel
+.Op group|gid
+.Op Fl n Ar name
+.Op Fl g Ar gid
+.Op Fl Y
+.Nm
+.Op Fl V Ar etcdir
+.Ar groupmod
+.Op group|gid
+.Op Fl C Ar config
+.Op Fl q
+.Op Fl n Ar name
+.Op Fl g Ar gid
+.Op Fl l Ar name
+.Op Fl M Ar members
+.Op Fl m Ar newmembers
+.Op Fl d Ar oldmembers
+.Op Fl h Ar fd | Fl H Ar fd
+.Op Fl N
+.Op Fl P
+.Op Fl Y
+.Nm
+.Op Fl V Ar etcdir
+.Ar groupshow
+.Op group|gid
+.Op Fl n Ar name
+.Op Fl g Ar gid
+.Op Fl F
+.Op Fl P
+.Op Fl a
+.Nm
+.Op Fl V Ar etcdir
+.Ar groupnext
+.Op Fl C Ar config
+.Op Fl q
+.Nm
+.Op Fl V Ar etcdir
+.Ar lock
+.Op name|uid
+.Op Fl C Ar config
+.Op Fl q
+.Nm
+.Op Fl V Ar etcdir
+.Ar unlock
+.Op name|uid
+.Op Fl C Ar config
+.Op Fl q
+.Sh DESCRIPTION
+The
+.Nm
+utility is a command-line based editor for the system
+.Ar user
+and
+.Ar group
+files, allowing the superuser an easy to use and standardized way of adding,
+modifying and removing users and groups.
+Note that
+.Nm
+only operates on the local user and group files.
+.Tn NIS
+users and groups must be
+maintained on the
+.Tn NIS
+server.
+The
+.Nm
+utility handles updating the
+.Pa passwd ,
+.Pa master.passwd ,
+.Pa group
+and the secure and insecure
+password database files, and must be run as root.
+.Pp
+The first one or two keywords provided to
+.Nm
+on the command line provide the context for the remainder of the arguments.
+The keywords
+.Ar user
+and
+.Ar group
+may be combined with
+.Ar add ,
+.Ar del ,
+.Ar mod ,
+.Ar show ,
+or
+.Ar next
+in any order.
+(For example,
+.Ar showuser ,
+.Ar usershow ,
+.Ar show user ,
+and
+.Ar user show
+all mean the same thing.)
+This flexibility is useful for interactive scripts calling
+.Nm
+for user and group database manipulation.
+Following these keywords, you may optionally specify the user or group name or numeric
+id as an alternative to using the
+.Fl n Ar name ,
+.Fl u Ar uid ,
+.Fl g Ar gid
+options.
+.Pp
+The following flags are common to most or all modes of operation:
+.Pp
+.Bl -tag -width "-G grouplist"
+.It Fl V Ar etcdir
+This flag sets an alternate location for the password, group and configuration files,
+and may be used to maintain a user/group database in an alternate location.
+If this switch is specified, the system
+.Pa /etc/pw.conf
+will not be sourced for default configuration data, but the file pw.conf in the
+specified directory will be used instead (or none, if it does not exist).
+The
+.Fl C
+flag may be used to override this behaviour.
+As an exception to the general rule where options must follow the operation
+type, the
+.Fl V
+flag may be used on the command line before the operation keyword.
+.It Fl C Ar config
+By default,
+.Nm
+reads the file
+.Pa /etc/pw.conf
+to obtain policy information on how new user accounts and groups are to be created.
+The
+.Fl C
+option specifies a different configuration file.
+While most of the contents of the configuration file may be overridden via
+command-line options, it may be more convenient to keep standard information in a
+configuration file.
+.It Fl q
+Use of this option causes
+.Nm
+to suppress error messages, which may be useful in interactive environments where it
+is preferable to interpret status codes returned by
+.Nm
+rather than messing up a carefully formatted display.
+.It Fl N
+This option is available in
+.Ar add
+and
+.Ar modify
+operations, and tells
+.Nm
+to output the result of the operation without updating the user or group
+databases.
+You may use the
+.Fl P
+option to switch between standard passwd and readable formats.
+.It Fl Y
+Using this option with any of the update modes causes
+.Nm
+to run
+.Xr make 1
+after changing to the directory
+.Pa /var/yp .
+This is intended to allow automatic updating of
+.Tn NIS
+database files.
+If separate passwd and group files are being used by
+.Tn NIS ,
+then use the
+.Fl y Ar path
+option to specify the location of the
+.Tn NIS
+passwd database so that
+.Nm
+will concurrently update it with the system password
+databases.
+.El
+.Sh USER OPTIONS
+The following options apply to the
+.Ar useradd
+and
+.Ar usermod
+commands:
+.Pp
+.Bl -tag -width "-G grouplist"
+.It Fl n Ar name
+Specify the user/account name.
+.It Fl u Ar uid
+Specify the user/account numeric id.
+.Pp
+Usually, you only need to provide one or the other of these options, as the account
+name will imply the uid, or vice versa.
+However, there are times when you need to provide both.
+For example, when changing the uid of an existing user with
+.Ar usermod ,
+or overriding the default uid when creating a new account.
+If you wish
+.Nm
+to automatically allocate the uid to a new user with
+.Ar useradd ,
+then you should
+.Em not
+use the
+.Fl u
+option.
+You may also provide either the account or userid immediately after the
+.Ar useradd ,
+.Ar userdel ,
+.Ar usermod
+or
+.Ar usershow
+keywords on the command line without using the
+.Fl n
+or
+.Fl u
+options.
+.El
+.Pp
+.Bl -tag -width "-G grouplist"
+.It Fl c Ar comment
+This field sets the contents of the passwd GECOS field, which normally contains up
+to four comma-separated fields containing the user's full name, office or location,
+and work and home phone numbers.
+These sub-fields are used by convention only, however, and are optional.
+If this field is to contain spaces, you need to quote the comment itself with double
+quotes
+.Ql \&" .
+Avoid using commas in this field as these are used as sub-field separators, and the
+colon
+.Ql \&:
+character also cannot be used as this is the field separator for the passwd
+file itself.
+.It Fl d Ar dir
+This option sets the account's home directory.
+Normally, you will only use this if the home directory is to be different from the
+default determined from
+.Pa /etc/pw.conf
+- normally
+.Pa /home
+with the account name as a subdirectory.
+.It Fl e Ar date
+Set the account's expiration date.
+Format of the date is either a UNIX time in decimal, or a date in
+.Ql dd-mmm-yy[yy]
+format, where dd is the day, mmm is the month, either in numeric or alphabetic format
+('Jan', 'Feb', etc) and year is either a two or four digit year.
+This option also accepts a relative date in the form
+.Ql \&+n[mhdwoy]
+where
+.Ql \&n
+is a decimal, octal (leading 0) or hexadecimal (leading 0x) digit followed by the
+number of Minutes, Hours, Days, Weeks, Months or Years from the current date at
+which the expiration date is to be set.
+.It Fl p Ar date
+Set the account's password expiration date.
+This field is similar to the account expiration date option, except that it
+applies to forced password changes.
+This is set in the same manner as the
+.Fl e
+option.
+.It Fl g Ar group
+Set the account's primary group to the given group.
+.Ar group
+may be defined by either its name or group number.
+.It Fl G Ar grouplist
+Set additional group memberships for an account.
+.Ar grouplist
+is a comma, space or tab-separated list of group names or group numbers.
+The user's name is added to the group lists in
+.Pa /etc/group ,
+and
+removed from any groups not specified in
+.Ar grouplist .
+Note: a user should not be added to their primary group with
+.Ar grouplist .
+Also, group membership changes do not take effect for current user login
+sessions, requiring the user to reconnect to be affected by the changes.
+.It Fl L Ar class
+This option sets the login class for the user being created.
+See
+.Xr login.conf 5
+and
+.Xr passwd 5
+for more information on user login classes.
+.It Fl m
+This option instructs
+.Nm
+to attempt to create the user's home directory.
+While primarily useful when adding a new account with
+.Ar useradd ,
+this may also be of use when moving an existing user's home directory elsewhere on
+the file system.
+The new home directory is populated with the contents of the
+.Ar skeleton
+directory, which typically contains a set of shell configuration files that the
+user may personalize to taste.
+Files in this directory are usually named
+.Pa dot . Ns Aq Ar config
+where the
+.Pa dot
+prefix will be stripped.
+When
+.Fl m
+is used on an account with
+.Ar usermod ,
+existing configuration files in the user's home directory are
+.Em not
+overwritten from the skeleton files.
+.Pp
+When a user's home directory is created, it will by default be a subdirectory of the
+.Ar basehome
+directory as specified by the
+.Fl b
+option (see below), bearing the name of the new account.
+This can be overridden by the
+.Fl d
+option on the command line, if desired.
+.It Fl M Ar mode
+Create the user's home directory with the specified
+.Ar mode ,
+modified by the current
+.Xr umask 2 .
+If omitted, it is derived from the parent process'
+.Xr umask 2 .
+This option is only useful in combination with the
+.Fl m
+flag.
+.It Fl k Ar dir
+Set the
+.Ar skeleton
+directory, from which basic startup and configuration files are copied when
+the user's home directory is created.
+This option only has meaning when used with the
+.Fl d
+or
+.Fl m
+flags.
+.It Fl s Ar shell
+Set or changes the user's login shell to
+.Ar shell .
+If the path to the shell program is omitted,
+.Nm
+searches the
+.Ar shellpath
+specified in
+.Pa /etc/pw.conf
+and fills it in as appropriate.
+Note that unless you have a specific reason to do so, you should avoid
+specifying the path - this will allow
+.Nm
+to validate that the program exists and is executable.
+Specifying a full path (or supplying a blank "" shell) avoids this check
+and allows for such entries as
+.Pa /nonexistent
+that should be set for accounts not intended for interactive login.
+.It Fl h Ar fd
+This option provides a special interface by which interactive scripts can
+set an account password using
+.Nm .
+Because the command line and environment are fundamentally insecure mechanisms
+by which programs can accept information,
+.Nm
+will only allow setting of account and group passwords via a file descriptor
+(usually a pipe between an interactive script and the program).
+.Ar sh ,
+.Ar bash ,
+.Ar ksh
+and
+.Ar perl
+all possess mechanisms by which this can be done.
+Alternatively,
+.Nm
+will prompt for the user's password if
+.Fl h Ar 0
+is given, nominating
+.Em stdin
+as the file descriptor on which to read the password.
+Note that this password will be read only once and is intended
+for use by a script rather than for interactive use.
+If you wish to have new password confirmation along the lines of
+.Xr passwd 1 ,
+this must be implemented as part of an interactive script that calls
+.Nm .
+.Pp
+If a value of
+.Ql \&-
+is given as the argument
+.Ar fd ,
+then the password will be set to
+.Ql \&* ,
+rendering the account inaccessible via password-based login.
+.It Fl H Ar fd
+Read an encrypted password string from the specified file descriptor.
+This is like
+.Fl h ,
+but the password should be supplied already encrypted in a form
+suitable for writing directly to the password database.
+.El
+.Pp
+It is possible to use
+.Ar useradd
+to create a new account that duplicates an existing user id.
+While this is normally considered an error and will be rejected, the
+.Fl o
+option overrides the check for duplicates and allows the duplication of
+the user id.
+This may be useful if you allow the same user to login under
+different contexts (different group allocations, different home
+directory, different shell) while providing basically the same
+permissions for access to the user's files in each account.
+.Pp
+The
+.Ar useradd
+command also has the ability to set new user and group defaults by using the
+.Fl D
+option.
+Instead of adding a new user,
+.Nm
+writes a new set of defaults to its configuration file,
+.Pa /etc/pw.conf .
+When using the
+.Fl D
+option, you must not use either
+.Fl n Ar name
+or
+.Fl u Ar uid
+or an error will result.
+Use of
+.Fl D
+changes the meaning of several command line switches in the
+.Ar useradd
+command.
+These are:
+.Bl -tag -width "-G grouplist"
+.It Fl D
+Set default values in
+.Pa /etc/pw.conf
+configuration file, or a different named configuration file if the
+.Fl C Ar config
+option is used.
+.It Fl b Ar dir
+Set the root directory in which user home directories are created.
+The default value for this is
+.Pa /home ,
+but it may be set elsewhere as desired.
+.It Fl e Ar days
+Set the default account expiration period in days.
+Unlike use without
+.Fl D ,
+the argument must be numeric, which specifies the number of days after creation when
+the account is to expire.
+A value of 0 suppresses automatic calculation of the expiry date.
+.It Fl p Ar days
+Set the default password expiration period in days.
+.It Fl g Ar group
+Set the default group for new users.
+If a blank group is specified using
+.Fl g Ar \&"" ,
+then new users will be allocated their own private primary group
+with the same name as their login name.
+If a group is supplied, either its name or uid may be given as an argument.
+.It Fl G Ar grouplist
+Set the default groups in which new users are granted membership.
+This is a separate set of groups from the primary group, and you should avoid
+nominating the same group as both primary and extra groups.
+In other words, these extra groups determine membership in groups
+.Em other than
+the primary group.
+.Ar grouplist
+is a comma-separated list of group names or ids, and are always
+stored in
+.Pa /etc/pw.conf
+by their symbolic names.
+.It Fl L Ar class
+This option sets the default login class for new users.
+.It Fl k Ar dir
+Set the default
+.Em skeleton
+directory, from which prototype shell and other initialization files are copied when
+.Nm
+creates a user's home directory.
+See description of
+.Fl k
+for naming conventions of these files.
+.It Xo
+.Fl u Ar min , Ns Ar max ,
+.Fl i Ar min , Ns Ar max
+.Xc
+These options set the minimum and maximum user and group ids allocated for new accounts
+and groups created by
+.Nm .
+The default values for each is 1000 minimum and 32000 maximum.
+.Ar min
+and
+.Ar max
+are both numbers, where max must be greater than min, and both must be between 0
+and 32767.
+In general, user and group ids less than 100 are reserved for use by the system,
+and numbers greater than 32000 may also be reserved for special purposes (used by
+some system daemons).
+.It Fl w Ar method
+The
+.Fl w
+option sets the default method used to set passwords for newly created user accounts.
+.Ar method
+is one of:
+.Pp
+.Bl -tag -width random -offset indent -compact
+.It no
+disable login on newly created accounts
+.It yes
+force the password to be the account name
+.It none
+force a blank password
+.It random
+generate a random password
+.El
+.Pp
+The
+.Ql \&random
+or
+.Ql \&no
+methods are the most secure; in the former case,
+.Nm
+generates a password and prints it to stdout, which is suitable where you issue
+users with passwords to access their accounts rather than having the user nominate
+their own (possibly poorly chosen) password.
+The
+.Ql \&no
+method requires that the superuser use
+.Xr passwd 1
+to render the account accessible with a password.
+.It Fl y Ar path
+This sets the pathname of the database used by
+.Tn NIS
+if you are not sharing
+the information from
+.Pa /etc/master.passwd
+directly with
+.Tn NIS .
+You should only set this option for
+.Tn NIS
+servers.
+.El
+.Pp
+The
+.Ar userdel
+command has only three valid options.
+The
+.Fl n Ar name
+and
+.Fl u Ar uid
+options have already been covered above.
+The additional option is:
+.Bl -tag -width "-G grouplist"
+.It Fl r
+This tells
+.Nm
+to remove the user's home directory and all of its contents.
+The
+.Nm
+utility errs on the side of caution when removing files from the system.
+Firstly, it will not do so if the uid of the account being removed is also used by
+another account on the system, and the 'home' directory in the password file is
+a valid path that commences with the character
+.Ql \&/ .
+Secondly, it will only remove files and directories that are actually owned by
+the user, or symbolic links owned by anyone under the user's home directory.
+Finally, after deleting all contents owned by the user only empty directories
+will be removed.
+If any additional cleanup work is required, this is left to the administrator.
+.El
+.Pp
+Mail spool files and crontabs are always removed when an account is deleted as these
+are unconditionally attached to the user name.
+Jobs queued for processing by
+.Ar at
+are also removed if the user's uid is unique and not also used by another account on the
+system.
+.Pp
+The
+.Ar usershow
+command allows viewing of an account in one of two formats.
+By default, the format is identical to the format used in
+.Pa /etc/master.passwd
+with the password field replaced with a
+.Ql \&* .
+If the
+.Fl P
+option is used, then
+.Nm
+outputs the account details in a more human readable form.
+If the
+.Fl 7
+option is used, the account details are shown in v7 format.
+The
+.Fl a
+option lists all users currently on file.
+Using
+.Fl F
+forces
+.Nm
+to print the details of an account even if it does not exist.
+.Pp
+The command
+.Ar usernext
+returns the next available user and group ids separated by a colon.
+This is normally of interest only to interactive scripts or front-ends
+that use
+.Nm .
+.Sh GROUP OPTIONS
+The
+.Fl C
+and
+.Fl q
+options (explained at the start of the previous section) are available
+with the group manipulation commands.
+Other common options to all group-related commands are:
+.Bl -tag -width "-m newmembers"
+.It Fl n Ar name
+Specify the group name.
+.It Fl g Ar gid
+Specify the group numeric id.
+.Pp
+As with the account name and id fields, you will usually only need
+to supply one of these, as the group name implies the uid and vice
+versa.
+You will only need to use both when setting a specific group id
+against a new group or when changing the uid of an existing group.
+.It Fl M Ar memberlist
+This option provides an alternative way to add existing users to a
+new group (in groupadd) or replace an existing membership list (in
+groupmod).
+.Ar memberlist
+is a comma separated list of valid and existing user names or uids.
+.It Fl m Ar newmembers
+Similar to
+.Fl M ,
+this option allows the
+.Em addition
+of existing users to a group without replacing the existing list of
+members.
+Login names or user ids may be used, and duplicate users are
+silently eliminated.
+.It Fl d Ar oldmembers
+Similar to
+.Fl M ,
+this option allows the
+.Em deletion
+of existing users from a group without replacing the existing list of
+members.
+Login names or user ids may be used, and duplicate users are
+silently eliminated.
+.El
+.Pp
+.Ar groupadd
+also has a
+.Fl o
+option that allows allocation of an existing group id to a new group.
+The default action is to reject an attempt to add a group, and this option overrides
+the check for duplicate group ids.
+There is rarely any need to duplicate a group id.
+.Pp
+The
+.Ar groupmod
+command adds one additional option:
+.Pp
+.Bl -tag -width "-m newmembers"
+.It Fl l Ar name
+This option allows changing of an existing group name to
+.Ql \&name .
+The new name must not already exist, and any attempt to duplicate an existing group
+name will be rejected.
+.El
+.Pp
+Options for
+.Ar groupshow
+are the same as for
+.Ar usershow ,
+with the
+.Fl g Ar gid
+replacing
+.Fl u Ar uid
+to specify the group id.
+The
+.Fl 7
+option does not apply to the
+.Ar groupshow
+command.
+.Pp
+The command
+.Ar groupnext
+returns the next available group id on standard output.
+.Sh USER LOCKING
+The
+.Nm
+utility
+supports a simple password locking mechanism for users; it works by
+prepending the string
+.Ql *LOCKED*
+to the beginning of the password field in
+.Pa master.passwd
+to prevent successful authentication.
+.Pp
+The
+.Ar lock
+and
+.Ar unlock
+commands take a user name or uid of the account to lock or unlock,
+respectively.
+The
+.Fl V ,
+.Fl C ,
+and
+.Fl q
+options as described above are accepted by these commands.
+.Sh NOTES
+For a summary of options available with each command, you can use
+.Dl pw [command] help
+For example,
+.Dl pw useradd help
+lists all available options for the useradd operation.
+.Pp
+The
+.Nm
+utility allows 8-bit characters in the passwd GECOS field (user's full name,
+office, work and home phone number subfields), but disallows them in
+user login and group names.
+Use 8-bit characters with caution, as connection to the Internet will
+require that your mail transport program supports 8BITMIME, and will
+convert headers containing 8-bit characters to 7-bit quoted-printable
+format.
+.Xr sendmail 8
+does support this.
+Use of 8-bit characters in the GECOS field should be used in
+conjunction with the user's default locale and character set
+and should not be implemented without their use.
+Using 8-bit characters may also affect other
+programs that transmit the contents of the GECOS field over the
+Internet, such as
+.Xr fingerd 8 ,
+and a small number of TCP/IP clients, such as IRC, where full names
+specified in the passwd file may be used by default.
+.Pp
+The
+.Nm
+utility writes a log to the
+.Pa /var/log/userlog
+file when actions such as user or group additions or deletions occur.
+The location of this logfile can be changed in
+.Xr pw.conf 5 .
+.Sh FILES
+.Bl -tag -width /etc/master.passwd.new -compact
+.It Pa /etc/master.passwd
+The user database
+.It Pa /etc/passwd
+A Version 7 format password file
+.It Pa /etc/login.conf
+The user capabilities database
+.It Pa /etc/group
+The group database
+.It Pa /etc/master.passwd.new
+Temporary copy of the master password file
+.It Pa /etc/passwd.new
+Temporary copy of the Version 7 password file
+.It Pa /etc/group.new
+Temporary copy of the group file
+.It Pa /etc/pw.conf
+Pw default options file
+.It Pa /var/log/userlog
+User/group modification logfile
+.El
+.Sh EXIT STATUS
+The
+.Nm
+utility returns EXIT_SUCCESS on successful operation, otherwise
+.Nm
+returns one of the
+following exit codes defined by
+.Xr sysexits 3
+as follows:
+.Bl -tag -width xxxx
+.It EX_USAGE
+.Bl -bullet -compact
+.It
+Command line syntax errors (invalid keyword, unknown option).
+.El
+.It EX_NOPERM
+.Bl -bullet -compact
+.It
+Attempting to run one of the update modes as non-root.
+.El
+.It EX_OSERR
+.Bl -bullet -compact
+.It
+Memory allocation error.
+.It
+Read error from password file descriptor.
+.El
+.It EX_DATAERR
+.Bl -bullet -compact
+.It
+Bad or invalid data provided or missing on the command line or
+via the password file descriptor.
+.It
+Attempted to remove, rename root account or change its uid.
+.El
+.It EX_OSFILE
+.Bl -bullet -compact
+.It
+Skeleton directory is invalid or does not exist.
+.It
+Base home directory is invalid or does not exist.
+.It
+Invalid or non-existent shell specified.
+.El
+.It EX_NOUSER
+.Bl -bullet -compact
+.It
+User, user id, group or group id specified does not exist.
+.It
+User or group recorded, added, or modified unexpectedly disappeared.
+.El
+.It EX_SOFTWARE
+.Bl -bullet -compact
+.It
+No more group or user ids available within specified range.
+.El
+.It EX_IOERR
+.Bl -bullet -compact
+.It
+Unable to rewrite configuration file.
+.It
+Error updating group or user database files.
+.It
+Update error for passwd or group database files.
+.El
+.It EX_CONFIG
+.Bl -bullet -compact
+.It
+No base home directory configured.
+.El
+.El
+.Sh SEE ALSO
+.Xr chpass 1 ,
+.Xr passwd 1 ,
+.Xr umask 2 ,
+.Xr group 5 ,
+.Xr login.conf 5 ,
+.Xr passwd 5 ,
+.Xr pw.conf 5 ,
+.Xr pwd_mkdb 8 ,
+.Xr vipw 8
+.Sh HISTORY
+The
+.Nm
+utility was written to mimic many of the options used in the SYSV
+.Em shadow
+support suite, but is modified for passwd and group fields specific to
+the
+.Bx 4.4
+operating system, and combines all of the major elements
+into a single command.
diff --git a/usr.sbin/pw/pw.c b/usr.sbin/pw/pw.c
new file mode 100644
index 0000000..e9d9363
--- /dev/null
+++ b/usr.sbin/pw/pw.c
@@ -0,0 +1,456 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 <err.h>
+#include <fcntl.h>
+#include <locale.h>
+#include <paths.h>
+#include <sys/wait.h>
+#include "pw.h"
+
+#if !defined(_PATH_YP)
+#define _PATH_YP "/var/yp/"
+#endif
+const char *Modes[] = {
+ "add", "del", "mod", "show", "next",
+ NULL};
+const char *Which[] = {"user", "group", NULL};
+static const char *Combo1[] = {
+ "useradd", "userdel", "usermod", "usershow", "usernext",
+ "lock", "unlock",
+ "groupadd", "groupdel", "groupmod", "groupshow", "groupnext",
+ NULL};
+static const char *Combo2[] = {
+ "adduser", "deluser", "moduser", "showuser", "nextuser",
+ "lock", "unlock",
+ "addgroup", "delgroup", "modgroup", "showgroup", "nextgroup",
+ NULL};
+
+struct pwf PWF =
+{
+ 0,
+ setpwent,
+ endpwent,
+ getpwent,
+ getpwuid,
+ getpwnam,
+ pwdb,
+ setgrent,
+ endgrent,
+ getgrent,
+ getgrgid,
+ getgrnam,
+ grdb
+
+};
+struct pwf VPWF =
+{
+ 1,
+ vsetpwent,
+ vendpwent,
+ vgetpwent,
+ vgetpwuid,
+ vgetpwnam,
+ vpwdb,
+ vsetgrent,
+ vendgrent,
+ vgetgrent,
+ vgetgrgid,
+ vgetgrnam,
+ vgrdb
+};
+
+static struct cargs arglist;
+
+static int getindex(const char *words[], const char *word);
+static void cmdhelp(int mode, int which);
+
+
+int
+main(int argc, char *argv[])
+{
+ int ch;
+ int mode = -1;
+ int which = -1;
+ char *config = NULL;
+ struct userconf *cnf;
+
+ static const char *opts[W_NUM][M_NUM] =
+ {
+ { /* user */
+ "V:C:qn:u:c:d:e:p:g:G:mM:k:s:oL:i:w:h:H:Db:NPy:Y",
+ "V:C:qn:u:rY",
+ "V:C:qn:u:c:d:e:p:g:G:mM:l:k:s:w:L:h:H:FNPY",
+ "V:C:qn:u:FPa7",
+ "V:C:q",
+ "V:C:q",
+ "V:C:q"
+ },
+ { /* grp */
+ "V:C:qn:g:h:H:M:opNPY",
+ "V:C:qn:g:Y",
+ "V:C:qn:d:g:l:h:H:FM:m:NPY",
+ "V:C:qn:g:FPa",
+ "V:C:q"
+ }
+ };
+
+ static int (*funcs[W_NUM]) (struct userconf * _cnf, int _mode, struct cargs * _args) =
+ { /* Request handlers */
+ pw_user,
+ pw_group
+ };
+
+ LIST_INIT(&arglist);
+
+ (void)setlocale(LC_ALL, "");
+
+ /*
+ * Break off the first couple of words to determine what exactly
+ * we're being asked to do
+ */
+ while (argc > 1) {
+ int tmp;
+
+ if (*argv[1] == '-') {
+ /*
+ * Special case, allow pw -V<dir> <operation> [args] for scripts etc.
+ */
+ if (argv[1][1] == 'V') {
+ optarg = &argv[1][2];
+ if (*optarg == '\0') {
+ optarg = argv[2];
+ ++argv;
+ --argc;
+ }
+ addarg(&arglist, 'V', optarg);
+ } else
+ break;
+ }
+ else if (mode == -1 && (tmp = getindex(Modes, argv[1])) != -1)
+ mode = tmp;
+ else if (which == -1 && (tmp = getindex(Which, argv[1])) != -1)
+ which = tmp;
+ else if ((mode == -1 && which == -1) &&
+ ((tmp = getindex(Combo1, argv[1])) != -1 ||
+ (tmp = getindex(Combo2, argv[1])) != -1)) {
+ which = tmp / M_NUM;
+ mode = tmp % M_NUM;
+ } else if (strcmp(argv[1], "help") == 0 && argv[2] == NULL)
+ cmdhelp(mode, which);
+ else if (which != -1 && mode != -1)
+ addarg(&arglist, 'n', argv[1]);
+ else
+ errx(EX_USAGE, "unknown keyword `%s'", argv[1]);
+ ++argv;
+ --argc;
+ }
+
+ /*
+ * Bail out unless the user is specific!
+ */
+ if (mode == -1 || which == -1)
+ cmdhelp(mode, which);
+
+ /*
+ * We know which mode we're in and what we're about to do, so now
+ * let's dispatch the remaining command line args in a genric way.
+ */
+ optarg = NULL;
+
+ while ((ch = getopt(argc, argv, opts[which][mode])) != -1) {
+ if (ch == '?')
+ errx(EX_USAGE, "unknown switch");
+ else
+ addarg(&arglist, ch, optarg);
+ optarg = NULL;
+ }
+
+ /*
+ * Must be root to attempt an update
+ */
+ if (geteuid() != 0 && mode != M_PRINT && mode != M_NEXT && getarg(&arglist, 'N')==NULL)
+ errx(EX_NOPERM, "you must be root to run this program");
+
+ /*
+ * We should immediately look for the -q 'quiet' switch so that we
+ * don't bother with extraneous errors
+ */
+ if (getarg(&arglist, 'q') != NULL)
+ freopen(_PATH_DEVNULL, "w", stderr);
+
+ /*
+ * Set our base working path if not overridden
+ */
+
+ config = getarg(&arglist, 'C') ? getarg(&arglist, 'C')->val : NULL;
+
+ if (getarg(&arglist, 'V') != NULL) {
+ char * etcpath = getarg(&arglist, 'V')->val;
+ if (*etcpath) {
+ if (config == NULL) { /* Only override config location if -C not specified */
+ config = malloc(MAXPATHLEN);
+ snprintf(config, MAXPATHLEN, "%s/pw.conf", etcpath);
+ }
+ memcpy(&PWF, &VPWF, sizeof PWF);
+ setpwdir(etcpath);
+ setgrdir(etcpath);
+ }
+ }
+
+ /*
+ * Now, let's do the common initialisation
+ */
+ cnf = read_userconfig(config);
+
+ ch = funcs[which] (cnf, mode, &arglist);
+
+ /*
+ * If everything went ok, and we've been asked to update
+ * the NIS maps, then do it now
+ */
+ if (ch == EXIT_SUCCESS && getarg(&arglist, 'Y') != NULL) {
+ pid_t pid;
+
+ fflush(NULL);
+ if (chdir(_PATH_YP) == -1)
+ warn("chdir(" _PATH_YP ")");
+ else if ((pid = fork()) == -1)
+ warn("fork()");
+ else if (pid == 0) {
+ /* Is make anywhere else? */
+ execlp("/usr/bin/make", "make", (char *)NULL);
+ _exit(1);
+ } else {
+ int i;
+ waitpid(pid, &i, 0);
+ if ((i = WEXITSTATUS(i)) != 0)
+ errx(ch, "make exited with status %d", i);
+ else
+ pw_log(cnf, mode, which, "NIS maps updated");
+ }
+ }
+ return ch;
+}
+
+
+static int
+getindex(const char *words[], const char *word)
+{
+ int i = 0;
+
+ while (words[i]) {
+ if (strcmp(words[i], word) == 0)
+ return i;
+ i++;
+ }
+ return -1;
+}
+
+
+/*
+ * This is probably an overkill for a cmdline help system, but it reflects
+ * the complexity of the command line.
+ */
+
+static void
+cmdhelp(int mode, int which)
+{
+ if (which == -1)
+ fprintf(stderr, "usage:\n pw [user|group|lock|unlock] [add|del|mod|show|next] [help|switches/values]\n");
+ else if (mode == -1)
+ fprintf(stderr, "usage:\n pw %s [add|del|mod|show|next] [help|switches/values]\n", Which[which]);
+ else {
+
+ /*
+ * We need to give mode specific help
+ */
+ static const char *help[W_NUM][M_NUM] =
+ {
+ {
+ "usage: pw useradd [name] [switches]\n"
+ "\t-V etcdir alternate /etc location\n"
+ "\t-C config configuration file\n"
+ "\t-q quiet operation\n"
+ " Adding users:\n"
+ "\t-n name login name\n"
+ "\t-u uid user id\n"
+ "\t-c comment user name/comment\n"
+ "\t-d directory home directory\n"
+ "\t-e date account expiry date\n"
+ "\t-p date password expiry date\n"
+ "\t-g grp initial group\n"
+ "\t-G grp1,grp2 additional groups\n"
+ "\t-m [ -k dir ] create and set up home\n"
+ "\t-M mode home directory permissions\n"
+ "\t-s shell name of login shell\n"
+ "\t-o duplicate uid ok\n"
+ "\t-L class user class\n"
+ "\t-h fd read password on fd\n"
+ "\t-H fd read encrypted password on fd\n"
+ "\t-Y update NIS maps\n"
+ "\t-N no update\n"
+ " Setting defaults:\n"
+ "\t-V etcdir alternate /etc location\n"
+ "\t-D set user defaults\n"
+ "\t-b dir default home root dir\n"
+ "\t-e period default expiry period\n"
+ "\t-p period default password change period\n"
+ "\t-g group default group\n"
+ "\t-G grp1,grp2 additional groups\n"
+ "\t-L class default user class\n"
+ "\t-k dir default home skeleton\n"
+ "\t-M mode home directory permissions\n"
+ "\t-u min,max set min,max uids\n"
+ "\t-i min,max set min,max gids\n"
+ "\t-w method set default password method\n"
+ "\t-s shell default shell\n"
+ "\t-y path set NIS passwd file path\n",
+ "usage: pw userdel [uid|name] [switches]\n"
+ "\t-V etcdir alternate /etc location\n"
+ "\t-n name login name\n"
+ "\t-u uid user id\n"
+ "\t-Y update NIS maps\n"
+ "\t-r remove home & contents\n",
+ "usage: pw usermod [uid|name] [switches]\n"
+ "\t-V etcdir alternate /etc location\n"
+ "\t-C config configuration file\n"
+ "\t-q quiet operation\n"
+ "\t-F force add if no user\n"
+ "\t-n name login name\n"
+ "\t-u uid user id\n"
+ "\t-c comment user name/comment\n"
+ "\t-d directory home directory\n"
+ "\t-e date account expiry date\n"
+ "\t-p date password expiry date\n"
+ "\t-g grp initial group\n"
+ "\t-G grp1,grp2 additional groups\n"
+ "\t-l name new login name\n"
+ "\t-L class user class\n"
+ "\t-m [ -k dir ] create and set up home\n"
+ "\t-M mode home directory permissions\n"
+ "\t-s shell name of login shell\n"
+ "\t-w method set new password using method\n"
+ "\t-h fd read password on fd\n"
+ "\t-H fd read encrypted password on fd\n"
+ "\t-Y update NIS maps\n"
+ "\t-N no update\n",
+ "usage: pw usershow [uid|name] [switches]\n"
+ "\t-V etcdir alternate /etc location\n"
+ "\t-n name login name\n"
+ "\t-u uid user id\n"
+ "\t-F force print\n"
+ "\t-P prettier format\n"
+ "\t-a print all users\n"
+ "\t-7 print in v7 format\n",
+ "usage: pw usernext [switches]\n"
+ "\t-V etcdir alternate /etc location\n"
+ "\t-C config configuration file\n"
+ "\t-q quiet operation\n",
+ "usage pw: lock [switches]\n"
+ "\t-V etcdir alternate /etc locations\n"
+ "\t-C config configuration file\n"
+ "\t-q quiet operation\n",
+ "usage pw: unlock [switches]\n"
+ "\t-V etcdir alternate /etc locations\n"
+ "\t-C config configuration file\n"
+ "\t-q quiet operation\n"
+ },
+ {
+ "usage: pw groupadd [group|gid] [switches]\n"
+ "\t-V etcdir alternate /etc location\n"
+ "\t-C config configuration file\n"
+ "\t-q quiet operation\n"
+ "\t-n group group name\n"
+ "\t-g gid group id\n"
+ "\t-M usr1,usr2 add users as group members\n"
+ "\t-o duplicate gid ok\n"
+ "\t-Y update NIS maps\n"
+ "\t-N no update\n",
+ "usage: pw groupdel [group|gid] [switches]\n"
+ "\t-V etcdir alternate /etc location\n"
+ "\t-n name group name\n"
+ "\t-g gid group id\n"
+ "\t-Y update NIS maps\n",
+ "usage: pw groupmod [group|gid] [switches]\n"
+ "\t-V etcdir alternate /etc location\n"
+ "\t-C config configuration file\n"
+ "\t-q quiet operation\n"
+ "\t-F force add if not exists\n"
+ "\t-n name group name\n"
+ "\t-g gid group id\n"
+ "\t-M usr1,usr2 replaces users as group members\n"
+ "\t-m usr1,usr2 add users as group members\n"
+ "\t-d usr1,usr2 delete users as group members\n"
+ "\t-l name new group name\n"
+ "\t-Y update NIS maps\n"
+ "\t-N no update\n",
+ "usage: pw groupshow [group|gid] [switches]\n"
+ "\t-V etcdir alternate /etc location\n"
+ "\t-n name group name\n"
+ "\t-g gid group id\n"
+ "\t-F force print\n"
+ "\t-P prettier format\n"
+ "\t-a print all accounting groups\n",
+ "usage: pw groupnext [switches]\n"
+ "\t-V etcdir alternate /etc location\n"
+ "\t-C config configuration file\n"
+ "\t-q quiet operation\n"
+ }
+ };
+
+ fprintf(stderr, "%s", help[which][mode]);
+ }
+ exit(EXIT_FAILURE);
+}
+
+struct carg *
+getarg(struct cargs * _args, int ch)
+{
+ struct carg *c = LIST_FIRST(_args);
+
+ while (c != NULL && c->ch != ch)
+ c = LIST_NEXT(c, list);
+ return c;
+}
+
+struct carg *
+addarg(struct cargs * _args, int ch, char *argstr)
+{
+ struct carg *ca = malloc(sizeof(struct carg));
+
+ if (ca == NULL)
+ errx(EX_OSERR, "out of memory");
+ ca->ch = ch;
+ ca->val = argstr;
+ LIST_INSERT_HEAD(_args, ca, list);
+ return ca;
+}
diff --git a/usr.sbin/pw/pw.conf.5 b/usr.sbin/pw/pw.conf.5
new file mode 100644
index 0000000..3f023aa
--- /dev/null
+++ b/usr.sbin/pw/pw.conf.5
@@ -0,0 +1,318 @@
+.\" Copyright (C) 1996
+.\" David L. Nugent. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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, 2007
+.Dt PW.CONF 5
+.Os
+.Sh NAME
+.Nm pw.conf
+.Nd format of the pw.conf configuration file
+.Sh DESCRIPTION
+The file
+.In /etc/pw.conf
+contains configuration data for the
+.Xr pw 8
+utility.
+The
+.Xr pw 8
+utility is used for maintenance of the system password and group
+files, allowing users and groups to be added, deleted and changed.
+This file may be modified via the
+.Xr pw 8
+command using the
+.Ar useradd
+command and the
+.Fl D
+option, or by editing it directly with a text editor.
+.Pp
+Each line in
+.Pa /etc/pw.conf
+is treated either a comment or as configuration data;
+blank lines and lines commencing with a
+.Ql \&#
+character are considered comments, and any remaining lines are
+examined for a leading keyword, followed by corresponding data.
+.Pp
+Keywords recognized by
+.Xr pw 8
+are:
+.Bl -tag -width password_days -offset indent -compact
+.It defaultpasswd
+affect passwords generated for new users
+.It reuseuids
+reuse gaps in uid sequences
+.It reusegids
+reuse gaps in gid sequences
+.It nispasswd
+path to the
+.Tn NIS
+passwd database
+.It skeleton
+where to obtain default home contents
+.It newmail
+mail to send to new users
+.It logfile
+log user/group modifications to this file
+.It home
+root directory for home directories
+.It homemode
+permissions for home directory
+.It shellpath
+paths in which to locate shell programs
+.It shells
+list of valid shells (without path)
+.It defaultshell
+default shell (without path)
+.It defaultgroup
+default group
+.It extragroups
+add new users to this groups
+.It defaultclass
+place new users in this login class
+.It minuid
+.It maxuid
+range of valid default user ids
+.It mingid
+.It maxgid
+range of valid default group ids
+.It expire_days
+days after which account expires
+.It password_days
+days after which password expires
+.El
+.Pp
+Valid values for
+.Ar defaultpasswd
+are:
+.Bl -tag -width password_days -offset indent -compact
+.It no
+disable login on newly created accounts
+.It yes
+force the password to be the account name
+.It none
+force a blank password
+.It random
+generate a random password
+.El
+.Pp
+The second and third options are insecure and should be avoided if
+possible on a publicly accessible system.
+The first option requires that the superuser run
+.Xr passwd 1
+to set a password before the account may be used.
+This may also be useful for creating administrative accounts.
+The final option causes
+.Xr pw 8
+to respond by printing a randomly generated password on stdout.
+This is the preferred and most secure option.
+The
+.Xr pw 8
+utility also provides a method of setting a specific password for the new
+user via a filehandle (command lines are not secure).
+.Pp
+Both
+.Ar reuseuids
+and
+.Ar reusegids
+determine the method by which new user and group id numbers are
+generated.
+A
+.Ql \&yes
+in this field will cause
+.Xr pw 8
+to search for the first unused user or group id within the allowed
+range, whereas a
+.Ql \&no
+will ensure that no other existing user or group id within the range
+is numerically lower than the new one generated, and therefore avoids
+reusing gaps in the user or group id sequence that are caused by
+previous user or group deletions.
+Note that if the default group is not specified using the
+.Ar defaultgroup
+keyword,
+.Xr pw 8
+will create a new group for the user and attempt to keep the new
+user's uid and gid the same.
+If the new user's uid is currently in use as a group id, then the next
+available group id is chosen instead.
+.Pp
+On
+.Tn NIS
+servers which maintain a separate passwd database to
+.Pa /etc/master.passwd ,
+this option allows the additional file to be concurrently updated
+as user records are added, modified or removed.
+If blank or set to 'no', no additional database is updated.
+An absolute pathname must be used.
+.Pp
+The
+.Ar skeleton
+keyword nominates a directory from which the contents of a user's
+new home directory is constructed.
+This is
+.Pa /usr/share/skel
+by default.
+The
+.Xr pw 8 Ns 's
+.Fl m
+option causes the user's home directory to be created and populated
+using the files contained in the
+.Ar skeleton
+directory.
+.Pp
+To send an initial email to new users, the
+.Ar newmail
+keyword may be used to specify a path name to a file containing
+the message body of the message to be sent.
+To avoid sending mail when accounts are created, leave this entry
+blank or specify
+.Ql \&no .
+.Pp
+The
+.Ar logfile
+option allows logging of password file modifications into the
+nominated log file.
+To avoid creating or adding to such a logfile, then leave this
+field blank or specify
+.Ql \&no .
+.Pp
+The
+.Ar home
+keyword is mandatory.
+This specifies the location of the directory in which all new user
+home directories are created.
+.Pp
+The
+.Ar homemode
+keyword is optional.
+It specifies the creation mask of the user's home directory and is modified by
+.Xr umask 2 .
+.Pp
+The
+.Ar shellpath
+keyword specifies a list of directories - separated by colons
+.Ql \&:
+- which contain the programs used by the login shells.
+.Pp
+The
+.Ar shells
+keyword specifies a list of programs available for use as login
+shells.
+This list is a comma-separated list of shell names which should
+not contain a path.
+These shells must exist in one of the directories nominated by
+.Ar shellpath .
+.Pp
+The
+.Ar defaultshell
+keyword nominates which shell program to use for new users when
+none is specified on the
+.Xr pw 8
+command line.
+.Pp
+The
+.Ar defaultgroup
+keyword defines the primary group (the group id number in the
+password file) used for new accounts.
+If left blank, or the word
+.Ql \&no
+is used, then each new user will have a corresponding group of
+their own created automatically.
+This is the recommended procedure for new users as it best secures each
+user's files against interference by other users of the system
+irrespective of the
+.Em umask
+normally used by the user.
+.Pp
+The
+.Ar extragroups
+keyword provides an automatic means of placing new users into groups within
+the
+.Pa /etc/groups
+file.
+This is useful where all users share some resources, and is preferable
+to placing users into the same primary group.
+The effect of this keyword can be overridden using the
+.Fl G
+option on the
+.Xr pw 8
+command line.
+.Pp
+The
+.Ar defaultclass
+field determines the login class (See
+.Xr login.conf 5 )
+that new users will be allocated unless overwritten by
+.Xr pw 8 .
+.Pp
+The
+.Ar minuid ,
+.Ar maxuid ,
+.Ar mingid ,
+.Ar maxgid
+keywords determine the allowed ranges of automatically allocated user
+and group id numbers.
+The default values for both user and group ids are 1000 and 32000 as
+minimum and maximum respectively.
+The user and group id's actually used when creating an account with
+.Xr pw 8
+may be overridden using the
+.Fl u
+and
+.Fl g
+command line options.
+.Pp
+The
+.Ar expire_days
+and
+.Ar password_days
+are used to automatically calculate the number of days from the date
+on which an account is created when the account will expire or the
+user will be forced to change the account's password.
+A value of
+.Ql \&0
+in either field will disable the corresponding (account or password)
+expiration date.
+.Sh LIMITS
+The maximum line length of
+.Pa /etc/pw.conf
+is 1024 characters.
+Longer lines will be skipped and treated
+as comments.
+.Sh FILES
+.Bl -tag -width /etc/master.passwd -compact
+.It Pa /etc/pw.conf
+.It Pa /etc/passwd
+.It Pa /etc/master.passwd
+.It Pa /etc/group
+.El
+.Sh SEE ALSO
+.Xr passwd 1 ,
+.Xr umask 2 ,
+.Xr group 5 ,
+.Xr login.conf 5 ,
+.Xr passwd 5 ,
+.Xr pw 8
diff --git a/usr.sbin/pw/pw.h b/usr.sbin/pw/pw.h
new file mode 100644
index 0000000..7568c22
--- /dev/null
+++ b/usr.sbin/pw/pw.h
@@ -0,0 +1,132 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/queue.h>
+#include <sysexits.h>
+
+#include "psdate.h"
+#include "pwupd.h"
+
+enum _mode
+{
+ M_ADD,
+ M_DELETE,
+ M_UPDATE,
+ M_PRINT,
+ M_NEXT,
+ M_LOCK,
+ M_UNLOCK,
+ M_NUM
+};
+
+enum _which
+{
+ W_USER,
+ W_GROUP,
+ W_NUM
+};
+
+struct carg
+{
+ int ch;
+ char *val;
+ LIST_ENTRY(carg) list;
+};
+
+LIST_HEAD(cargs, carg);
+
+struct userconf
+{
+ int default_password; /* Default password for new users? */
+ int reuse_uids; /* Reuse uids? */
+ int reuse_gids; /* Reuse gids? */
+ char *nispasswd; /* Path to NIS version of the passwd file */
+ char *dotdir; /* Where to obtain skeleton files */
+ char *newmail; /* Mail to send to new accounts */
+ char *logfile; /* Where to log changes */
+ char *home; /* Where to create home directory */
+ mode_t homemode; /* Home directory permissions */
+ char *shelldir; /* Where shells are located */
+ char **shells; /* List of shells */
+ char *shell_default; /* Default shell */
+ char *default_group; /* Default group number */
+ char **groups; /* Default (additional) groups */
+ char *default_class; /* Default user class */
+ uid_t min_uid, max_uid; /* Allowed range of uids */
+ gid_t min_gid, max_gid; /* Allowed range of gids */
+ int expire_days; /* Days to expiry */
+ int password_days; /* Days to password expiry */
+ int numgroups; /* (internal) size of default_group array */
+};
+
+#define _PATH_PW_CONF "/etc/pw.conf"
+#define _UC_MAXLINE 1024
+#define _UC_MAXSHELLS 32
+
+struct userconf *read_userconfig(char const * file);
+int write_userconfig(char const * file);
+struct carg *addarg(struct cargs * _args, int ch, char *argstr);
+struct carg *getarg(struct cargs * _args, int ch);
+
+int pw_user(struct userconf * cnf, int mode, struct cargs * _args);
+int pw_group(struct userconf * cnf, int mode, struct cargs * _args);
+char *pw_checkname(u_char *name, int gecos);
+
+int addpwent(struct passwd * pwd);
+int delpwent(struct passwd * pwd);
+int chgpwent(char const * login, struct passwd * pwd);
+int fmtpwent(char *buf, struct passwd * pwd);
+
+int addnispwent(const char *path, struct passwd *pwd);
+int delnispwent(const char *path, const char *login);
+int chgnispwent(const char *path, const char *login, struct passwd *pwd);
+
+int addgrent(struct group * grp);
+int delgrent(struct group * grp);
+int chggrent(char const * login, struct group * grp);
+
+int boolean_val(char const * str, int dflt);
+char const *boolean_str(int val);
+char *newstr(char const * p);
+
+void pw_log(struct userconf * cnf, int mode, int which, char const * fmt,...) __printflike(4, 5);
+char *pw_pwcrypt(char *password);
+
+extern const char *Modes[];
+extern const char *Which[];
diff --git a/usr.sbin/pw/pw_conf.c b/usr.sbin/pw/pw_conf.c
new file mode 100644
index 0000000..51672b9
--- /dev/null
+++ b/usr.sbin/pw/pw_conf.c
@@ -0,0 +1,516 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+
+#include "pw.h"
+
+#define debugging 0
+
+enum {
+ _UC_NONE,
+ _UC_DEFAULTPWD,
+ _UC_REUSEUID,
+ _UC_REUSEGID,
+ _UC_NISPASSWD,
+ _UC_DOTDIR,
+ _UC_NEWMAIL,
+ _UC_LOGFILE,
+ _UC_HOMEROOT,
+ _UC_HOMEMODE,
+ _UC_SHELLPATH,
+ _UC_SHELLS,
+ _UC_DEFAULTSHELL,
+ _UC_DEFAULTGROUP,
+ _UC_EXTRAGROUPS,
+ _UC_DEFAULTCLASS,
+ _UC_MINUID,
+ _UC_MAXUID,
+ _UC_MINGID,
+ _UC_MAXGID,
+ _UC_EXPIRE,
+ _UC_PASSWORD,
+ _UC_FIELDS
+};
+
+static char bourne_shell[] = "sh";
+
+static char *system_shells[_UC_MAXSHELLS] =
+{
+ bourne_shell,
+ "csh",
+ "tcsh"
+};
+
+static char const *booltrue[] =
+{
+ "yes", "true", "1", "on", NULL
+};
+static char const *boolfalse[] =
+{
+ "no", "false", "0", "off", NULL
+};
+
+static struct userconf config =
+{
+ 0, /* Default password for new users? (nologin) */
+ 0, /* Reuse uids? */
+ 0, /* Reuse gids? */
+ NULL, /* NIS version of the passwd file */
+ "/usr/share/skel", /* Where to obtain skeleton files */
+ NULL, /* Mail to send to new accounts */
+ "/var/log/userlog", /* Where to log changes */
+ "/home", /* Where to create home directory */
+ 0777, /* Home directory perms, modified by umask */
+ "/bin", /* Where shells are located */
+ system_shells, /* List of shells (first is default) */
+ bourne_shell, /* Default shell */
+ NULL, /* Default group name */
+ NULL, /* Default (additional) groups */
+ NULL, /* Default login class */
+ 1000, 32000, /* Allowed range of uids */
+ 1000, 32000, /* Allowed range of gids */
+ 0, /* Days until account expires */
+ 0, /* Days until password expires */
+ 0 /* size of default_group array */
+};
+
+static char const *comments[_UC_FIELDS] =
+{
+ "#\n# pw.conf - user/group configuration defaults\n#\n",
+ "\n# Password for new users? no=nologin yes=loginid none=blank random=random\n",
+ "\n# Reuse gaps in uid sequence? (yes or no)\n",
+ "\n# Reuse gaps in gid sequence? (yes or no)\n",
+ "\n# Path to the NIS passwd file (blank or 'no' for none)\n",
+ "\n# Obtain default dotfiles from this directory\n",
+ "\n# Mail this file to new user (/etc/newuser.msg or no)\n",
+ "\n# Log add/change/remove information in this file\n",
+ "\n# Root directory in which $HOME directory is created\n",
+ "\n# Mode for the new $HOME directory, will be modified by umask\n",
+ "\n# Colon separated list of directories containing valid shells\n",
+ "\n# Comma separated list of available shells (without paths)\n",
+ "\n# Default shell (without path)\n",
+ "\n# Default group (leave blank for new group per user)\n",
+ "\n# Extra groups for new users\n",
+ "\n# Default login class for new users\n",
+ "\n# Range of valid default user ids\n",
+ NULL,
+ "\n# Range of valid default group ids\n",
+ NULL,
+ "\n# Days after which account expires (0=disabled)\n",
+ "\n# Days after which password expires (0=disabled)\n"
+};
+
+static char const *kwds[] =
+{
+ "",
+ "defaultpasswd",
+ "reuseuids",
+ "reusegids",
+ "nispasswd",
+ "skeleton",
+ "newmail",
+ "logfile",
+ "home",
+ "homemode",
+ "shellpath",
+ "shells",
+ "defaultshell",
+ "defaultgroup",
+ "extragroups",
+ "defaultclass",
+ "minuid",
+ "maxuid",
+ "mingid",
+ "maxgid",
+ "expire_days",
+ "password_days",
+ NULL
+};
+
+static char *
+unquote(char const * str)
+{
+ if (str && (*str == '"' || *str == '\'')) {
+ char *p = strchr(str + 1, *str);
+
+ if (p != NULL)
+ *p = '\0';
+ return (char *) (*++str ? str : NULL);
+ }
+ return (char *) str;
+}
+
+int
+boolean_val(char const * str, int dflt)
+{
+ if ((str = unquote(str)) != NULL) {
+ int i;
+
+ for (i = 0; booltrue[i]; i++)
+ if (strcmp(str, booltrue[i]) == 0)
+ return 1;
+ for (i = 0; boolfalse[i]; i++)
+ if (strcmp(str, boolfalse[i]) == 0)
+ return 0;
+
+ /*
+ * Special cases for defaultpassword
+ */
+ if (strcmp(str, "random") == 0)
+ return -1;
+ if (strcmp(str, "none") == 0)
+ return -2;
+ }
+ return dflt;
+}
+
+char const *
+boolean_str(int val)
+{
+ if (val == -1)
+ return "random";
+ else if (val == -2)
+ return "none";
+ else
+ return val ? booltrue[0] : boolfalse[0];
+}
+
+char *
+newstr(char const * p)
+{
+ char *q = NULL;
+
+ if ((p = unquote(p)) != NULL) {
+ int l = strlen(p) + 1;
+
+ if ((q = malloc(l)) != NULL)
+ memcpy(q, p, l);
+ }
+ return q;
+}
+
+#define LNBUFSZ 1024
+
+
+struct userconf *
+read_userconfig(char const * file)
+{
+ FILE *fp;
+
+ extendarray(&config.groups, &config.numgroups, 200);
+ memset(config.groups, 0, config.numgroups * sizeof(char *));
+ if (file == NULL)
+ file = _PATH_PW_CONF;
+ if ((fp = fopen(file, "r")) != NULL) {
+ int buflen = LNBUFSZ;
+ char *buf = malloc(buflen);
+
+ nextline:
+ while (fgets(buf, buflen, fp) != NULL) {
+ char *p;
+
+ while ((p = strchr(buf, '\n')) == NULL) {
+ int l;
+ if (extendline(&buf, &buflen, buflen + LNBUFSZ) == -1) {
+ int ch;
+ while ((ch = fgetc(fp)) != '\n' && ch != EOF);
+ goto nextline; /* Ignore it */
+ }
+ l = strlen(buf);
+ if (fgets(buf + l, buflen - l, fp) == NULL)
+ break; /* Unterminated last line */
+ }
+
+ if (p != NULL)
+ *p = '\0';
+
+ if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
+ static char const toks[] = " \t\r\n,=";
+ char *q = strtok(NULL, toks);
+ int i = 0;
+ mode_t *modeset;
+
+ while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
+ ++i;
+#if debugging
+ if (i == _UC_FIELDS)
+ printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
+ else
+ printf("Got kwd[%s]=%s\n", p, q);
+#endif
+ switch (i) {
+ case _UC_DEFAULTPWD:
+ config.default_password = boolean_val(q, 1);
+ break;
+ case _UC_REUSEUID:
+ config.reuse_uids = boolean_val(q, 0);
+ break;
+ case _UC_REUSEGID:
+ config.reuse_gids = boolean_val(q, 0);
+ break;
+ case _UC_NISPASSWD:
+ config.nispasswd = (q == NULL || !boolean_val(q, 1))
+ ? NULL : newstr(q);
+ break;
+ case _UC_DOTDIR:
+ config.dotdir = (q == NULL || !boolean_val(q, 1))
+ ? NULL : newstr(q);
+ break;
+ case _UC_NEWMAIL:
+ config.newmail = (q == NULL || !boolean_val(q, 1))
+ ? NULL : newstr(q);
+ break;
+ case _UC_LOGFILE:
+ config.logfile = (q == NULL || !boolean_val(q, 1))
+ ? NULL : newstr(q);
+ break;
+ case _UC_HOMEROOT:
+ config.home = (q == NULL || !boolean_val(q, 1))
+ ? "/home" : newstr(q);
+ break;
+ case _UC_HOMEMODE:
+ modeset = setmode(q);
+ config.homemode = (q == NULL || !boolean_val(q, 1))
+ ? 0777 : getmode(modeset, 0777);
+ free(modeset);
+ break;
+ case _UC_SHELLPATH:
+ config.shelldir = (q == NULL || !boolean_val(q, 1))
+ ? "/bin" : newstr(q);
+ break;
+ case _UC_SHELLS:
+ for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks))
+ system_shells[i] = newstr(q);
+ if (i > 0)
+ while (i < _UC_MAXSHELLS)
+ system_shells[i++] = NULL;
+ break;
+ case _UC_DEFAULTSHELL:
+ config.shell_default = (q == NULL || !boolean_val(q, 1))
+ ? (char *) bourne_shell : newstr(q);
+ break;
+ case _UC_DEFAULTGROUP:
+ q = unquote(q);
+ config.default_group = (q == NULL || !boolean_val(q, 1) || GETGRNAM(q) == NULL)
+ ? NULL : newstr(q);
+ break;
+ case _UC_EXTRAGROUPS:
+ for (i = 0; q != NULL; q = strtok(NULL, toks)) {
+ if (extendarray(&config.groups, &config.numgroups, i + 2) != -1)
+ config.groups[i++] = newstr(q);
+ }
+ if (i > 0)
+ while (i < config.numgroups)
+ config.groups[i++] = NULL;
+ break;
+ case _UC_DEFAULTCLASS:
+ config.default_class = (q == NULL || !boolean_val(q, 1))
+ ? NULL : newstr(q);
+ break;
+ case _UC_MINUID:
+ if ((q = unquote(q)) != NULL && isdigit(*q))
+ config.min_uid = (uid_t) atol(q);
+ break;
+ case _UC_MAXUID:
+ if ((q = unquote(q)) != NULL && isdigit(*q))
+ config.max_uid = (uid_t) atol(q);
+ break;
+ case _UC_MINGID:
+ if ((q = unquote(q)) != NULL && isdigit(*q))
+ config.min_gid = (gid_t) atol(q);
+ break;
+ case _UC_MAXGID:
+ if ((q = unquote(q)) != NULL && isdigit(*q))
+ config.max_gid = (gid_t) atol(q);
+ break;
+ case _UC_EXPIRE:
+ if ((q = unquote(q)) != NULL && isdigit(*q))
+ config.expire_days = atoi(q);
+ break;
+ case _UC_PASSWORD:
+ if ((q = unquote(q)) != NULL && isdigit(*q))
+ config.password_days = atoi(q);
+ break;
+ case _UC_FIELDS:
+ case _UC_NONE:
+ break;
+ }
+ }
+ }
+ free(buf);
+ fclose(fp);
+ }
+ return &config;
+}
+
+
+int
+write_userconfig(char const * file)
+{
+ int fd;
+
+ if (file == NULL)
+ file = _PATH_PW_CONF;
+
+ if ((fd = open(file, O_CREAT | O_RDWR | O_TRUNC | O_EXLOCK, 0644)) != -1) {
+ FILE *fp;
+
+ if ((fp = fdopen(fd, "w")) == NULL)
+ close(fd);
+ else {
+ int i, j, k;
+ int len = LNBUFSZ;
+ char *buf = malloc(len);
+
+ for (i = _UC_NONE; i < _UC_FIELDS; i++) {
+ int quote = 1;
+ char const *val = buf;
+
+ *buf = '\0';
+ switch (i) {
+ case _UC_DEFAULTPWD:
+ val = boolean_str(config.default_password);
+ break;
+ case _UC_REUSEUID:
+ val = boolean_str(config.reuse_uids);
+ break;
+ case _UC_REUSEGID:
+ val = boolean_str(config.reuse_gids);
+ break;
+ case _UC_NISPASSWD:
+ val = config.nispasswd ? config.nispasswd : "";
+ quote = 0;
+ break;
+ case _UC_DOTDIR:
+ val = config.dotdir ? config.dotdir : boolean_str(0);
+ break;
+ case _UC_NEWMAIL:
+ val = config.newmail ? config.newmail : boolean_str(0);
+ break;
+ case _UC_LOGFILE:
+ val = config.logfile ? config.logfile : boolean_str(0);
+ break;
+ case _UC_HOMEROOT:
+ val = config.home;
+ break;
+ case _UC_HOMEMODE:
+ sprintf(buf, "%04o", config.homemode);
+ quote = 0;
+ break;
+ case _UC_SHELLPATH:
+ val = config.shelldir;
+ break;
+ case _UC_SHELLS:
+ for (j = k = 0; j < _UC_MAXSHELLS && system_shells[j] != NULL; j++) {
+ char lbuf[64];
+ int l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", system_shells[j]);
+ if (l < 0)
+ l = 0;
+ if (l + k + 1 < len || extendline(&buf, &len, len + LNBUFSZ) != -1) {
+ strcpy(buf + k, lbuf);
+ k += l;
+ }
+ }
+ quote = 0;
+ break;
+ case _UC_DEFAULTSHELL:
+ val = config.shell_default ? config.shell_default : bourne_shell;
+ break;
+ case _UC_DEFAULTGROUP:
+ val = config.default_group ? config.default_group : "";
+ break;
+ case _UC_EXTRAGROUPS:
+ extendarray(&config.groups, &config.numgroups, 200);
+ for (j = k = 0; j < config.numgroups && config.groups[j] != NULL; j++) {
+ char lbuf[64];
+ int l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", config.groups[j]);
+ if (l < 0)
+ l = 0;
+ if (l + k + 1 < len || extendline(&buf, &len, len + 1024) != -1) {
+ strcpy(buf + k, lbuf);
+ k += l;
+ }
+ }
+ quote = 0;
+ break;
+ case _UC_DEFAULTCLASS:
+ val = config.default_class ? config.default_class : "";
+ break;
+ case _UC_MINUID:
+ sprintf(buf, "%lu", (unsigned long) config.min_uid);
+ quote = 0;
+ break;
+ case _UC_MAXUID:
+ sprintf(buf, "%lu", (unsigned long) config.max_uid);
+ quote = 0;
+ break;
+ case _UC_MINGID:
+ sprintf(buf, "%lu", (unsigned long) config.min_gid);
+ quote = 0;
+ break;
+ case _UC_MAXGID:
+ sprintf(buf, "%lu", (unsigned long) config.max_gid);
+ quote = 0;
+ break;
+ case _UC_EXPIRE:
+ sprintf(buf, "%d", config.expire_days);
+ quote = 0;
+ break;
+ case _UC_PASSWORD:
+ sprintf(buf, "%d", config.password_days);
+ quote = 0;
+ break;
+ case _UC_NONE:
+ break;
+ }
+
+ if (comments[i])
+ fputs(comments[i], fp);
+
+ if (*kwds[i]) {
+ if (quote)
+ fprintf(fp, "%s = \"%s\"\n", kwds[i], val);
+ else
+ fprintf(fp, "%s = %s\n", kwds[i], val);
+#if debugging
+ printf("WROTE: %s = %s\n", kwds[i], val);
+#endif
+ }
+ }
+ free(buf);
+ return fclose(fp) != EOF;
+ }
+ }
+ return 0;
+}
diff --git a/usr.sbin/pw/pw_group.c b/usr.sbin/pw/pw_group.c
new file mode 100644
index 0000000..a8f182c
--- /dev/null
+++ b/usr.sbin/pw/pw_group.c
@@ -0,0 +1,423 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 <ctype.h>
+#include <err.h>
+#include <termios.h>
+#include <stdbool.h>
+#include <unistd.h>
+
+#include "pw.h"
+#include "bitmap.h"
+
+
+static struct passwd *lookup_pwent(const char *user);
+static void delete_members(char ***members, int *grmembers, int *i,
+ struct carg *arg, struct group *grp);
+static int print_group(struct group * grp, int pretty);
+static gid_t gr_gidpolicy(struct userconf * cnf, struct cargs * args);
+
+int
+pw_group(struct userconf * cnf, int mode, struct cargs * args)
+{
+ int rc;
+ struct carg *a_name = getarg(args, 'n');
+ struct carg *a_gid = getarg(args, 'g');
+ struct carg *arg;
+ struct group *grp = NULL;
+ int grmembers = 0;
+ char **members = NULL;
+
+ static struct group fakegroup =
+ {
+ "nogroup",
+ "*",
+ -1,
+ NULL
+ };
+
+ if (mode == M_LOCK || mode == M_UNLOCK)
+ errx(EX_USAGE, "'lock' command is not available for groups");
+
+ /*
+ * With M_NEXT, we only need to return the
+ * next gid to stdout
+ */
+ if (mode == M_NEXT) {
+ gid_t next = gr_gidpolicy(cnf, args);
+ if (getarg(args, 'q'))
+ return next;
+ printf("%ld\n", (long)next);
+ return EXIT_SUCCESS;
+ }
+
+ if (mode == M_PRINT && getarg(args, 'a')) {
+ int pretty = getarg(args, 'P') != NULL;
+
+ SETGRENT();
+ while ((grp = GETGRENT()) != NULL)
+ print_group(grp, pretty);
+ ENDGRENT();
+ return EXIT_SUCCESS;
+ }
+ if (a_gid == NULL) {
+ if (a_name == NULL)
+ errx(EX_DATAERR, "group name or id required");
+
+ if (mode != M_ADD && grp == NULL && isdigit((unsigned char)*a_name->val)) {
+ (a_gid = a_name)->ch = 'g';
+ a_name = NULL;
+ }
+ }
+ grp = (a_name != NULL) ? GETGRNAM(a_name->val) : GETGRGID((gid_t) atoi(a_gid->val));
+
+ if (mode == M_UPDATE || mode == M_DELETE || mode == M_PRINT) {
+ if (a_name == NULL && grp == NULL) /* Try harder */
+ grp = GETGRGID(atoi(a_gid->val));
+
+ if (grp == NULL) {
+ if (mode == M_PRINT && getarg(args, 'F')) {
+ char *fmems[1];
+ fmems[0] = NULL;
+ fakegroup.gr_name = a_name ? a_name->val : "nogroup";
+ fakegroup.gr_gid = a_gid ? (gid_t) atol(a_gid->val) : -1;
+ fakegroup.gr_mem = fmems;
+ return print_group(&fakegroup, getarg(args, 'P') != NULL);
+ }
+ errx(EX_DATAERR, "unknown group `%s'", a_name ? a_name->val : a_gid->val);
+ }
+ if (a_name == NULL) /* Needed later */
+ a_name = addarg(args, 'n', grp->gr_name);
+
+ /*
+ * Handle deletions now
+ */
+ if (mode == M_DELETE) {
+ gid_t gid = grp->gr_gid;
+
+ rc = delgrent(grp);
+ if (rc == -1)
+ err(EX_IOERR, "group '%s' not available (NIS?)", grp->gr_name);
+ else if (rc != 0) {
+ warn("group update");
+ return EX_IOERR;
+ }
+ pw_log(cnf, mode, W_GROUP, "%s(%ld) removed", a_name->val, (long) gid);
+ return EXIT_SUCCESS;
+ } else if (mode == M_PRINT)
+ return print_group(grp, getarg(args, 'P') != NULL);
+
+ if (a_gid)
+ grp->gr_gid = (gid_t) atoi(a_gid->val);
+
+ if ((arg = getarg(args, 'l')) != NULL)
+ grp->gr_name = pw_checkname((u_char *)arg->val, 0);
+ } else {
+ if (a_name == NULL) /* Required */
+ errx(EX_DATAERR, "group name required");
+ else if (grp != NULL) /* Exists */
+ errx(EX_DATAERR, "group name `%s' already exists", a_name->val);
+
+ extendarray(&members, &grmembers, 200);
+ members[0] = NULL;
+ grp = &fakegroup;
+ grp->gr_name = pw_checkname((u_char *)a_name->val, 0);
+ grp->gr_passwd = "*";
+ grp->gr_gid = gr_gidpolicy(cnf, args);
+ grp->gr_mem = members;
+ }
+
+ /*
+ * This allows us to set a group password Group passwords is an
+ * antique idea, rarely used and insecure (no secure database) Should
+ * be discouraged, but it is apparently still supported by some
+ * software.
+ */
+
+ if ((arg = getarg(args, 'h')) != NULL ||
+ (arg = getarg(args, 'H')) != NULL) {
+ if (strcmp(arg->val, "-") == 0)
+ grp->gr_passwd = "*"; /* No access */
+ else {
+ int fd = atoi(arg->val);
+ int precrypt = (arg->ch == 'H');
+ int b;
+ int istty = isatty(fd);
+ struct termios t;
+ char *p, line[256];
+
+ if (istty) {
+ if (tcgetattr(fd, &t) == -1)
+ istty = 0;
+ else {
+ struct termios n = t;
+
+ /* Disable echo */
+ n.c_lflag &= ~(ECHO);
+ tcsetattr(fd, TCSANOW, &n);
+ printf("%sassword for group %s:", (mode == M_UPDATE) ? "New p" : "P", grp->gr_name);
+ fflush(stdout);
+ }
+ }
+ b = read(fd, line, sizeof(line) - 1);
+ if (istty) { /* Restore state */
+ tcsetattr(fd, TCSANOW, &t);
+ fputc('\n', stdout);
+ fflush(stdout);
+ }
+ if (b < 0) {
+ warn("-h file descriptor");
+ return EX_OSERR;
+ }
+ line[b] = '\0';
+ if ((p = strpbrk(line, " \t\r\n")) != NULL)
+ *p = '\0';
+ if (!*line)
+ errx(EX_DATAERR, "empty password read on file descriptor %d", fd);
+ if (precrypt) {
+ if (strchr(line, ':') != NULL)
+ return EX_DATAERR;
+ grp->gr_passwd = line;
+ } else
+ grp->gr_passwd = pw_pwcrypt(line);
+ }
+ }
+
+ if (((arg = getarg(args, 'M')) != NULL ||
+ (arg = getarg(args, 'd')) != NULL ||
+ (arg = getarg(args, 'm')) != NULL) && arg->val) {
+ int i = 0;
+ char *p;
+ struct passwd *pwd;
+
+ /* Make sure this is not stay NULL with -M "" */
+ extendarray(&members, &grmembers, 200);
+ if (arg->ch == 'd')
+ delete_members(&members, &grmembers, &i, arg, grp);
+ else if (arg->ch == 'm') {
+ int k = 0;
+
+ while (grp->gr_mem[k] != NULL) {
+ if (extendarray(&members, &grmembers, i + 2) != -1)
+ members[i++] = grp->gr_mem[k];
+ k++;
+ }
+ }
+
+ if (arg->ch != 'd')
+ for (p = strtok(arg->val, ", \t"); p != NULL; p = strtok(NULL, ", \t")) {
+ int j;
+
+ /*
+ * Check for duplicates
+ */
+ pwd = lookup_pwent(p);
+ for (j = 0; j < i && strcmp(members[j], pwd->pw_name) != 0; j++)
+ ;
+ if (j == i && extendarray(&members, &grmembers, i + 2) != -1)
+ members[i++] = newstr(pwd->pw_name);
+ }
+ while (i < grmembers)
+ members[i++] = NULL;
+ grp->gr_mem = members;
+ }
+
+ if (getarg(args, 'N') != NULL)
+ return print_group(grp, getarg(args, 'P') != NULL);
+
+ if (mode == M_ADD && (rc = addgrent(grp)) != 0) {
+ if (rc == -1)
+ warnx("group '%s' already exists", grp->gr_name);
+ else
+ warn("group update");
+ return EX_IOERR;
+ } else if (mode == M_UPDATE && (rc = chggrent(a_name->val, grp)) != 0) {
+ if (rc == -1)
+ warnx("group '%s' not available (NIS?)", grp->gr_name);
+ else
+ warn("group update");
+ return EX_IOERR;
+ }
+ /* grp may have been invalidated */
+ if ((grp = GETGRNAM(a_name->val)) == NULL)
+ errx(EX_SOFTWARE, "group disappeared during update");
+
+ pw_log(cnf, mode, W_GROUP, "%s(%ld)", grp->gr_name, (long) grp->gr_gid);
+
+ if (members)
+ free(members);
+
+ return EXIT_SUCCESS;
+}
+
+
+/*
+ * Lookup a passwd entry using a name or UID.
+ */
+static struct passwd *
+lookup_pwent(const char *user)
+{
+ struct passwd *pwd;
+
+ if ((pwd = GETPWNAM(user)) == NULL &&
+ (!isdigit((unsigned char)*user) ||
+ (pwd = getpwuid((uid_t) atoi(user))) == NULL))
+ errx(EX_NOUSER, "user `%s' does not exist", user);
+
+ return (pwd);
+}
+
+
+/*
+ * Delete requested members from a group.
+ */
+static void
+delete_members(char ***members, int *grmembers, int *i, struct carg *arg,
+ struct group *grp)
+{
+ bool matchFound;
+ char *user;
+ char *valueCopy;
+ char *valuePtr;
+ int k;
+ struct passwd *pwd;
+
+ k = 0;
+ while (grp->gr_mem[k] != NULL) {
+ matchFound = false;
+ if ((valueCopy = strdup(arg->val)) == NULL)
+ errx(EX_UNAVAILABLE, "out of memory");
+ valuePtr = valueCopy;
+ while ((user = strsep(&valuePtr, ", \t")) != NULL) {
+ pwd = lookup_pwent(user);
+ if (strcmp(grp->gr_mem[k], pwd->pw_name) == 0) {
+ matchFound = true;
+ break;
+ }
+ }
+ free(valueCopy);
+
+ if (!matchFound &&
+ extendarray(members, grmembers, *i + 2) != -1)
+ (*members)[(*i)++] = grp->gr_mem[k];
+
+ k++;
+ }
+
+ return;
+}
+
+
+static gid_t
+gr_gidpolicy(struct userconf * cnf, struct cargs * args)
+{
+ struct group *grp;
+ gid_t gid = (gid_t) - 1;
+ struct carg *a_gid = getarg(args, 'g');
+
+ /*
+ * Check the given gid, if any
+ */
+ if (a_gid != NULL) {
+ gid = (gid_t) atol(a_gid->val);
+
+ if ((grp = GETGRGID(gid)) != NULL && getarg(args, 'o') == NULL)
+ errx(EX_DATAERR, "gid `%ld' has already been allocated", (long) grp->gr_gid);
+ } else {
+ struct bitmap bm;
+
+ /*
+ * We need to allocate the next available gid under one of
+ * two policies a) Grab the first unused gid b) Grab the
+ * highest possible unused gid
+ */
+ if (cnf->min_gid >= cnf->max_gid) { /* Sanity claus^H^H^H^Hheck */
+ cnf->min_gid = 1000;
+ cnf->max_gid = 32000;
+ }
+ bm = bm_alloc(cnf->max_gid - cnf->min_gid + 1);
+
+ /*
+ * Now, let's fill the bitmap from the password file
+ */
+ SETGRENT();
+ while ((grp = GETGRENT()) != NULL)
+ if ((gid_t)grp->gr_gid >= (gid_t)cnf->min_gid &&
+ (gid_t)grp->gr_gid <= (gid_t)cnf->max_gid)
+ bm_setbit(&bm, grp->gr_gid - cnf->min_gid);
+ ENDGRENT();
+
+ /*
+ * Then apply the policy, with fallback to reuse if necessary
+ */
+ if (cnf->reuse_gids)
+ gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid);
+ else {
+ gid = (gid_t) (bm_lastset(&bm) + 1);
+ if (!bm_isset(&bm, gid))
+ gid += cnf->min_gid;
+ else
+ gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid);
+ }
+
+ /*
+ * Another sanity check
+ */
+ if (gid < cnf->min_gid || gid > cnf->max_gid)
+ errx(EX_SOFTWARE, "unable to allocate a new gid - range fully used");
+ bm_dealloc(&bm);
+ }
+ return gid;
+}
+
+
+static int
+print_group(struct group * grp, int pretty)
+{
+ if (!pretty) {
+ int buflen = 0;
+ char *buf = NULL;
+
+ fmtgrent(&buf, &buflen, grp);
+ fputs(buf, stdout);
+ free(buf);
+ } else {
+ int i;
+
+ printf("Group Name: %-15s #%lu\n"
+ " Members: ",
+ grp->gr_name, (long) grp->gr_gid);
+ for (i = 0; grp->gr_mem[i]; i++)
+ printf("%s%s", i ? "," : "", grp->gr_mem[i]);
+ fputs("\n\n", stdout);
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/usr.sbin/pw/pw_log.c b/usr.sbin/pw/pw_log.c
new file mode 100644
index 0000000..fc85828
--- /dev/null
+++ b/usr.sbin/pw/pw_log.c
@@ -0,0 +1,68 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 <fcntl.h>
+
+#include "pw.h"
+
+static FILE *logfile = NULL;
+
+void
+pw_log(struct userconf * cnf, int mode, int which, char const * fmt,...)
+{
+ if (cnf->logfile && *cnf->logfile) {
+ if (logfile == NULL) { /* With umask==0 we need to control file access modes on create */
+ int fd = open(cnf->logfile, O_WRONLY | O_CREAT | O_APPEND, 0600);
+
+ if (fd != -1)
+ logfile = fdopen(fd, "a");
+ }
+ if (logfile != NULL) {
+ va_list argp;
+ int l;
+ time_t now = time(NULL);
+ struct tm *t = localtime(&now);
+ char nfmt[256];
+ char *name;
+
+ if ((name = getenv("LOGNAME")) == NULL && (name = getenv("USER")) == NULL)
+ name = "unknown";
+ /* ISO 8601 International Standard Date format */
+ strftime(nfmt, sizeof nfmt, "%Y-%m-%d %T ", t);
+ l = strlen(nfmt);
+ sprintf(nfmt + strlen(nfmt), "[%s:%s%s] %s\n", name, Which[which], Modes[mode], fmt);
+ va_start(argp, fmt);
+ vfprintf(logfile, nfmt, argp);
+ va_end(argp);
+ fflush(logfile);
+ }
+ }
+}
diff --git a/usr.sbin/pw/pw_nis.c b/usr.sbin/pw/pw_nis.c
new file mode 100644
index 0000000..74a3ed0
--- /dev/null
+++ b/usr.sbin/pw/pw_nis.c
@@ -0,0 +1,72 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "pw.h"
+
+static int
+pw_nisupdate(const char * path, struct passwd * pwd, char const * user, int mode)
+{
+ char pfx[32];
+ char pwbuf[PWBUFSZ];
+ int l = sprintf(pfx, "%s:", user);
+
+ /*
+ * Update the passwd file first
+ */
+ if (pwd == NULL)
+ *pwbuf = '\0';
+ else
+ fmtpwentry(pwbuf, pwd, PWF_MASTER);
+ return fileupdate(path, 0600, pwbuf, pfx, l, mode) != 0;
+}
+
+int
+addnispwent(const char *path, struct passwd * pwd)
+{
+ return pw_nisupdate(path, pwd, pwd->pw_name, UPD_CREATE);
+}
+
+int
+chgnispwent(const char *path, char const * login, struct passwd * pwd)
+{
+ return pw_nisupdate(path, pwd, login, UPD_REPLACE);
+}
+
+int
+delnispwent(const char *path, const char *login)
+{
+ return pw_nisupdate(path, NULL, login, UPD_DELETE);
+}
diff --git a/usr.sbin/pw/pw_user.c b/usr.sbin/pw/pw_user.c
new file mode 100644
index 0000000..4c62fe8
--- /dev/null
+++ b/usr.sbin/pw/pw_user.c
@@ -0,0 +1,1279 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <dirent.h>
+#include <paths.h>
+#include <termios.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <unistd.h>
+#include <utmp.h>
+#include <login_cap.h>
+#include "pw.h"
+#include "bitmap.h"
+
+#if (MAXLOGNAME-1) > UT_NAMESIZE
+#define LOGNAMESIZE UT_NAMESIZE
+#else
+#define LOGNAMESIZE (MAXLOGNAME-1)
+#endif
+
+static char locked_str[] = "*LOCKED*";
+
+static int print_user(struct passwd * pwd, int pretty, int v7);
+static uid_t pw_uidpolicy(struct userconf * cnf, struct cargs * args);
+static uid_t pw_gidpolicy(struct userconf * cnf, struct cargs * args, char *nam, gid_t prefer);
+static time_t pw_pwdpolicy(struct userconf * cnf, struct cargs * args);
+static time_t pw_exppolicy(struct userconf * cnf, struct cargs * args);
+static char *pw_homepolicy(struct userconf * cnf, struct cargs * args, char const * user);
+static char *pw_shellpolicy(struct userconf * cnf, struct cargs * args, char *newshell);
+static char *pw_password(struct userconf * cnf, struct cargs * args, char const * user);
+static char *shell_path(char const * path, char *shells[], char *sh);
+static void rmat(uid_t uid);
+static void rmopie(char const * name);
+
+/*-
+ * -C config configuration file
+ * -q quiet operation
+ * -n name login name
+ * -u uid user id
+ * -c comment user name/comment
+ * -d directory home directory
+ * -e date account expiry date
+ * -p date password expiry date
+ * -g grp primary group
+ * -G grp1,grp2 additional groups
+ * -m [ -k dir ] create and set up home
+ * -s shell name of login shell
+ * -o duplicate uid ok
+ * -L class user class
+ * -l name new login name
+ * -h fd password filehandle
+ * -H fd encrypted password filehandle
+ * -F force print or add
+ * Setting defaults:
+ * -D set user defaults
+ * -b dir default home root dir
+ * -e period default expiry period
+ * -p period default password change period
+ * -g group default group
+ * -G grp1,grp2.. default additional groups
+ * -L class default login class
+ * -k dir default home skeleton
+ * -s shell default shell
+ * -w method default password method
+ */
+
+int
+pw_user(struct userconf * cnf, int mode, struct cargs * args)
+{
+ int rc, edited = 0;
+ char *p = NULL;
+ char *passtmp;
+ struct carg *a_name;
+ struct carg *a_uid;
+ struct carg *arg;
+ struct passwd *pwd = NULL;
+ struct group *grp;
+ struct stat st;
+ char line[_PASSWORD_LEN+1];
+ FILE *fp;
+ mode_t dmode;
+ char *dmode_c;
+ void *set = NULL;
+
+ static struct passwd fakeuser =
+ {
+ NULL,
+ "*",
+ -1,
+ -1,
+ 0,
+ "",
+ "User &",
+ "/nonexistent",
+ "/bin/sh",
+ 0
+#if defined(__FreeBSD__)
+ ,0
+#endif
+ };
+
+
+ /*
+ * With M_NEXT, we only need to return the
+ * next uid to stdout
+ */
+ if (mode == M_NEXT)
+ {
+ uid_t next = pw_uidpolicy(cnf, args);
+ if (getarg(args, 'q'))
+ return next;
+ printf("%ld:", (long)next);
+ pw_group(cnf, mode, args);
+ return EXIT_SUCCESS;
+ }
+
+ /*
+ * We can do all of the common legwork here
+ */
+
+ if ((arg = getarg(args, 'b')) != NULL) {
+ cnf->home = arg->val;
+ }
+
+ if ((arg = getarg(args, 'M')) != NULL) {
+ dmode_c = arg->val;
+ if ((set = setmode(dmode_c)) == NULL)
+ errx(EX_DATAERR, "invalid directory creation mode '%s'",
+ dmode_c);
+ dmode = getmode(set, S_IRWXU | S_IRWXG | S_IRWXO);
+ free(set);
+ cnf->homemode = dmode;
+ }
+
+ /*
+ * If we'll need to use it or we're updating it,
+ * then create the base home directory if necessary
+ */
+ if (arg != NULL || getarg(args, 'm') != NULL) {
+ int l = strlen(cnf->home);
+
+ if (l > 1 && cnf->home[l-1] == '/') /* Shave off any trailing path delimiter */
+ cnf->home[--l] = '\0';
+
+ if (l < 2 || *cnf->home != '/') /* Check for absolute path name */
+ errx(EX_DATAERR, "invalid base directory for home '%s'", cnf->home);
+
+ if (stat(cnf->home, &st) == -1) {
+ char dbuf[MAXPATHLEN];
+
+ /*
+ * This is a kludge especially for Joerg :)
+ * If the home directory would be created in the root partition, then
+ * we really create it under /usr which is likely to have more space.
+ * But we create a symlink from cnf->home -> "/usr" -> cnf->home
+ */
+ if (strchr(cnf->home+1, '/') == NULL) {
+ strcpy(dbuf, "/usr");
+ strncat(dbuf, cnf->home, MAXPATHLEN-5);
+ if (mkdir(dbuf, cnf->homemode) != -1 || errno == EEXIST) {
+ chown(dbuf, 0, 0);
+ /*
+ * Skip first "/" and create symlink:
+ * /home -> usr/home
+ */
+ symlink(dbuf+1, cnf->home);
+ }
+ /* If this falls, fall back to old method */
+ }
+ strlcpy(dbuf, cnf->home, sizeof(dbuf));
+ p = dbuf;
+ if (stat(dbuf, &st) == -1) {
+ while ((p = strchr(++p, '/')) != NULL) {
+ *p = '\0';
+ if (stat(dbuf, &st) == -1) {
+ if (mkdir(dbuf, cnf->homemode) == -1)
+ goto direrr;
+ chown(dbuf, 0, 0);
+ } else if (!S_ISDIR(st.st_mode))
+ errx(EX_OSFILE, "'%s' (root home parent) is not a directory", dbuf);
+ *p = '/';
+ }
+ }
+ if (stat(dbuf, &st) == -1) {
+ if (mkdir(dbuf, cnf->homemode) == -1) {
+ direrr: err(EX_OSFILE, "mkdir '%s'", dbuf);
+ }
+ chown(dbuf, 0, 0);
+ }
+ } else if (!S_ISDIR(st.st_mode))
+ errx(EX_OSFILE, "root home `%s' is not a directory", cnf->home);
+ }
+
+ if ((arg = getarg(args, 'e')) != NULL)
+ cnf->expire_days = atoi(arg->val);
+
+ if ((arg = getarg(args, 'y')) != NULL)
+ cnf->nispasswd = arg->val;
+
+ if ((arg = getarg(args, 'p')) != NULL && arg->val)
+ cnf->password_days = atoi(arg->val);
+
+ if ((arg = getarg(args, 'g')) != NULL) {
+ if (!*(p = arg->val)) /* Handle empty group list specially */
+ cnf->default_group = "";
+ else {
+ if ((grp = GETGRNAM(p)) == NULL) {
+ if (!isdigit((unsigned char)*p) || (grp = GETGRGID((gid_t) atoi(p))) == NULL)
+ errx(EX_NOUSER, "group `%s' does not exist", p);
+ }
+ cnf->default_group = newstr(grp->gr_name);
+ }
+ }
+ if ((arg = getarg(args, 'L')) != NULL)
+ cnf->default_class = pw_checkname((u_char *)arg->val, 0);
+
+ if ((arg = getarg(args, 'G')) != NULL && arg->val) {
+ int i = 0;
+
+ for (p = strtok(arg->val, ", \t"); p != NULL; p = strtok(NULL, ", \t")) {
+ if ((grp = GETGRNAM(p)) == NULL) {
+ if (!isdigit((unsigned char)*p) || (grp = GETGRGID((gid_t) atoi(p))) == NULL)
+ errx(EX_NOUSER, "group `%s' does not exist", p);
+ }
+ if (extendarray(&cnf->groups, &cnf->numgroups, i + 2) != -1)
+ cnf->groups[i++] = newstr(grp->gr_name);
+ }
+ while (i < cnf->numgroups)
+ cnf->groups[i++] = NULL;
+ }
+
+ if ((arg = getarg(args, 'k')) != NULL) {
+ if (stat(cnf->dotdir = arg->val, &st) == -1 || !S_ISDIR(st.st_mode))
+ errx(EX_OSFILE, "skeleton `%s' is not a directory or does not exist", cnf->dotdir);
+ }
+
+ if ((arg = getarg(args, 's')) != NULL)
+ cnf->shell_default = arg->val;
+
+ if ((arg = getarg(args, 'w')) != NULL)
+ cnf->default_password = boolean_val(arg->val, cnf->default_password);
+ if (mode == M_ADD && getarg(args, 'D')) {
+ if (getarg(args, 'n') != NULL)
+ errx(EX_DATAERR, "can't combine `-D' with `-n name'");
+ if ((arg = getarg(args, 'u')) != NULL && (p = strtok(arg->val, ", \t")) != NULL) {
+ if ((cnf->min_uid = (uid_t) atoi(p)) == 0)
+ cnf->min_uid = 1000;
+ if ((p = strtok(NULL, " ,\t")) == NULL || (cnf->max_uid = (uid_t) atoi(p)) < cnf->min_uid)
+ cnf->max_uid = 32000;
+ }
+ if ((arg = getarg(args, 'i')) != NULL && (p = strtok(arg->val, ", \t")) != NULL) {
+ if ((cnf->min_gid = (gid_t) atoi(p)) == 0)
+ cnf->min_gid = 1000;
+ if ((p = strtok(NULL, " ,\t")) == NULL || (cnf->max_gid = (gid_t) atoi(p)) < cnf->min_gid)
+ cnf->max_gid = 32000;
+ }
+
+ arg = getarg(args, 'C');
+ if (write_userconfig(arg ? arg->val : NULL))
+ return EXIT_SUCCESS;
+ warn("config update");
+ return EX_IOERR;
+ }
+
+ if (mode == M_PRINT && getarg(args, 'a')) {
+ int pretty = getarg(args, 'P') != NULL;
+ int v7 = getarg(args, '7') != NULL;
+
+ SETPWENT();
+ while ((pwd = GETPWENT()) != NULL)
+ print_user(pwd, pretty, v7);
+ ENDPWENT();
+ return EXIT_SUCCESS;
+ }
+
+ if ((a_name = getarg(args, 'n')) != NULL)
+ pwd = GETPWNAM(pw_checkname((u_char *)a_name->val, 0));
+ a_uid = getarg(args, 'u');
+
+ if (a_uid == NULL) {
+ if (a_name == NULL)
+ errx(EX_DATAERR, "user name or id required");
+
+ /*
+ * Determine whether 'n' switch is name or uid - we don't
+ * really don't really care which we have, but we need to
+ * know.
+ */
+ if (mode != M_ADD && pwd == NULL
+ && strspn(a_name->val, "0123456789") == strlen(a_name->val)
+ && atoi(a_name->val) > 0) { /* Assume uid */
+ (a_uid = a_name)->ch = 'u';
+ a_name = NULL;
+ }
+ }
+
+ /*
+ * Update, delete & print require that the user exists
+ */
+ if (mode == M_UPDATE || mode == M_DELETE ||
+ mode == M_PRINT || mode == M_LOCK || mode == M_UNLOCK) {
+
+ if (a_name == NULL && pwd == NULL) /* Try harder */
+ pwd = GETPWUID(atoi(a_uid->val));
+
+ if (pwd == NULL) {
+ if (mode == M_PRINT && getarg(args, 'F')) {
+ fakeuser.pw_name = a_name ? a_name->val : "nouser";
+ fakeuser.pw_uid = a_uid ? (uid_t) atol(a_uid->val) : -1;
+ return print_user(&fakeuser,
+ getarg(args, 'P') != NULL,
+ getarg(args, '7') != NULL);
+ }
+ if (a_name == NULL)
+ errx(EX_NOUSER, "no such uid `%s'", a_uid->val);
+ errx(EX_NOUSER, "no such user `%s'", a_name->val);
+ }
+
+ if (a_name == NULL) /* May be needed later */
+ a_name = addarg(args, 'n', newstr(pwd->pw_name));
+
+ /*
+ * The M_LOCK and M_UNLOCK functions simply add or remove
+ * a "*LOCKED*" prefix from in front of the password to
+ * prevent it decoding correctly, and therefore prevents
+ * access. Of course, this only prevents access via
+ * password authentication (not ssh, kerberos or any
+ * other method that does not use the UNIX password) but
+ * that is a known limitation.
+ */
+
+ if (mode == M_LOCK) {
+ if (strncmp(pwd->pw_passwd, locked_str, sizeof(locked_str)-1) == 0)
+ errx(EX_DATAERR, "user '%s' is already locked", pwd->pw_name);
+ passtmp = malloc(strlen(pwd->pw_passwd) + sizeof(locked_str));
+ if (passtmp == NULL) /* disaster */
+ errx(EX_UNAVAILABLE, "out of memory");
+ strcpy(passtmp, locked_str);
+ strcat(passtmp, pwd->pw_passwd);
+ pwd->pw_passwd = passtmp;
+ edited = 1;
+ } else if (mode == M_UNLOCK) {
+ if (strncmp(pwd->pw_passwd, locked_str, sizeof(locked_str)-1) != 0)
+ errx(EX_DATAERR, "user '%s' is not locked", pwd->pw_name);
+ pwd->pw_passwd += sizeof(locked_str)-1;
+ edited = 1;
+ } else if (mode == M_DELETE) {
+ /*
+ * Handle deletions now
+ */
+ char file[MAXPATHLEN];
+ char home[MAXPATHLEN];
+ uid_t uid = pwd->pw_uid;
+
+ if (strcmp(pwd->pw_name, "root") == 0)
+ errx(EX_DATAERR, "cannot remove user 'root'");
+
+ if (!PWALTDIR()) {
+ /*
+ * Remove opie record from /etc/opiekeys
+ */
+
+ rmopie(pwd->pw_name);
+
+ /*
+ * Remove crontabs
+ */
+ sprintf(file, "/var/cron/tabs/%s", pwd->pw_name);
+ if (access(file, F_OK) == 0) {
+ sprintf(file, "crontab -u %s -r", pwd->pw_name);
+ system(file);
+ }
+ }
+ /*
+ * Save these for later, since contents of pwd may be
+ * invalidated by deletion
+ */
+ sprintf(file, "%s/%s", _PATH_MAILDIR, pwd->pw_name);
+ strlcpy(home, pwd->pw_dir, sizeof(home));
+
+ rc = delpwent(pwd);
+ if (rc == -1)
+ err(EX_IOERR, "user '%s' does not exist", pwd->pw_name);
+ else if (rc != 0) {
+ warn("passwd update");
+ return EX_IOERR;
+ }
+
+ if (cnf->nispasswd && *cnf->nispasswd=='/') {
+ rc = delnispwent(cnf->nispasswd, a_name->val);
+ if (rc == -1)
+ warnx("WARNING: user '%s' does not exist in NIS passwd", pwd->pw_name);
+ else if (rc != 0)
+ warn("WARNING: NIS passwd update");
+ /* non-fatal */
+ }
+
+ editgroups(a_name->val, NULL);
+
+ pw_log(cnf, mode, W_USER, "%s(%ld) account removed", a_name->val, (long) uid);
+
+ if (!PWALTDIR()) {
+ /*
+ * Remove mail file
+ */
+ remove(file);
+
+ /*
+ * Remove at jobs
+ */
+ if (getpwuid(uid) == NULL)
+ rmat(uid);
+
+ /*
+ * Remove home directory and contents
+ */
+ if (getarg(args, 'r') != NULL && *home == '/' && getpwuid(uid) == NULL) {
+ if (stat(home, &st) != -1) {
+ rm_r(home, uid);
+ pw_log(cnf, mode, W_USER, "%s(%ld) home '%s' %sremoved",
+ a_name->val, (long) uid, home,
+ stat(home, &st) == -1 ? "" : "not completely ");
+ }
+ }
+ }
+ return EXIT_SUCCESS;
+ } else if (mode == M_PRINT)
+ return print_user(pwd,
+ getarg(args, 'P') != NULL,
+ getarg(args, '7') != NULL);
+
+ /*
+ * The rest is edit code
+ */
+ if ((arg = getarg(args, 'l')) != NULL) {
+ if (strcmp(pwd->pw_name, "root") == 0)
+ errx(EX_DATAERR, "can't rename `root' account");
+ pwd->pw_name = pw_checkname((u_char *)arg->val, 0);
+ edited = 1;
+ }
+
+ if ((arg = getarg(args, 'u')) != NULL && isdigit((unsigned char)*arg->val)) {
+ pwd->pw_uid = (uid_t) atol(arg->val);
+ edited = 1;
+ if (pwd->pw_uid != 0 && strcmp(pwd->pw_name, "root") == 0)
+ errx(EX_DATAERR, "can't change uid of `root' account");
+ if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0)
+ warnx("WARNING: account `%s' will have a uid of 0 (superuser access!)", pwd->pw_name);
+ }
+
+ if ((arg = getarg(args, 'g')) != NULL && pwd->pw_uid != 0) { /* Already checked this */
+ gid_t newgid = (gid_t) GETGRNAM(cnf->default_group)->gr_gid;
+ if (newgid != pwd->pw_gid) {
+ edited = 1;
+ pwd->pw_gid = newgid;
+ }
+ }
+
+ if ((arg = getarg(args, 'p')) != NULL) {
+ if (*arg->val == '\0' || strcmp(arg->val, "0") == 0) {
+ if (pwd->pw_change != 0) {
+ pwd->pw_change = 0;
+ edited = 1;
+ }
+ }
+ else {
+ time_t now = time(NULL);
+ time_t expire = parse_date(now, arg->val);
+
+ if (now == expire)
+ errx(EX_DATAERR, "invalid password change date `%s'", arg->val);
+ if (pwd->pw_change != expire) {
+ pwd->pw_change = expire;
+ edited = 1;
+ }
+ }
+ }
+
+ if ((arg = getarg(args, 'e')) != NULL) {
+ if (*arg->val == '\0' || strcmp(arg->val, "0") == 0) {
+ if (pwd->pw_expire != 0) {
+ pwd->pw_expire = 0;
+ edited = 1;
+ }
+ }
+ else {
+ time_t now = time(NULL);
+ time_t expire = parse_date(now, arg->val);
+
+ if (now == expire)
+ errx(EX_DATAERR, "invalid account expiry date `%s'", arg->val);
+ if (pwd->pw_expire != expire) {
+ pwd->pw_expire = expire;
+ edited = 1;
+ }
+ }
+ }
+
+ if ((arg = getarg(args, 's')) != NULL) {
+ char *shell = shell_path(cnf->shelldir, cnf->shells, arg->val);
+ if (shell == NULL)
+ shell = "";
+ if (strcmp(shell, pwd->pw_shell) != 0) {
+ pwd->pw_shell = shell;
+ edited = 1;
+ }
+ }
+
+ if (getarg(args, 'L')) {
+ if (cnf->default_class == NULL)
+ cnf->default_class = "";
+ if (strcmp(pwd->pw_class, cnf->default_class) != 0) {
+ pwd->pw_class = cnf->default_class;
+ edited = 1;
+ }
+ }
+
+ if ((arg = getarg(args, 'd')) != NULL) {
+ if (strcmp(pwd->pw_dir, arg->val))
+ edited = 1;
+ if (stat(pwd->pw_dir = arg->val, &st) == -1) {
+ if (getarg(args, 'm') == NULL && strcmp(pwd->pw_dir, "/nonexistent") != 0)
+ warnx("WARNING: home `%s' does not exist", pwd->pw_dir);
+ } else if (!S_ISDIR(st.st_mode))
+ warnx("WARNING: home `%s' is not a directory", pwd->pw_dir);
+ }
+
+ if ((arg = getarg(args, 'w')) != NULL &&
+ getarg(args, 'h') == NULL && getarg(args, 'H') == NULL) {
+ login_cap_t *lc;
+
+ lc = login_getpwclass(pwd);
+ if (lc == NULL ||
+ login_setcryptfmt(lc, "md5", NULL) == NULL)
+ warn("setting crypt(3) format");
+ login_close(lc);
+ pwd->pw_passwd = pw_password(cnf, args, pwd->pw_name);
+ edited = 1;
+ }
+
+ } else {
+ login_cap_t *lc;
+
+ /*
+ * Add code
+ */
+
+ if (a_name == NULL) /* Required */
+ errx(EX_DATAERR, "login name required");
+ else if ((pwd = GETPWNAM(a_name->val)) != NULL) /* Exists */
+ errx(EX_DATAERR, "login name `%s' already exists", a_name->val);
+
+ /*
+ * Now, set up defaults for a new user
+ */
+ pwd = &fakeuser;
+ pwd->pw_name = a_name->val;
+ pwd->pw_class = cnf->default_class ? cnf->default_class : "";
+ pwd->pw_uid = pw_uidpolicy(cnf, args);
+ pwd->pw_gid = pw_gidpolicy(cnf, args, pwd->pw_name, (gid_t) pwd->pw_uid);
+ pwd->pw_change = pw_pwdpolicy(cnf, args);
+ pwd->pw_expire = pw_exppolicy(cnf, args);
+ pwd->pw_dir = pw_homepolicy(cnf, args, pwd->pw_name);
+ pwd->pw_shell = pw_shellpolicy(cnf, args, NULL);
+ lc = login_getpwclass(pwd);
+ if (lc == NULL || login_setcryptfmt(lc, "md5", NULL) == NULL)
+ warn("setting crypt(3) format");
+ login_close(lc);
+ pwd->pw_passwd = pw_password(cnf, args, pwd->pw_name);
+ edited = 1;
+
+ if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0)
+ warnx("WARNING: new account `%s' has a uid of 0 (superuser access!)", pwd->pw_name);
+ }
+
+ /*
+ * Shared add/edit code
+ */
+ if ((arg = getarg(args, 'c')) != NULL) {
+ char *gecos = pw_checkname((u_char *)arg->val, 1);
+ if (strcmp(pwd->pw_gecos, gecos) != 0) {
+ pwd->pw_gecos = gecos;
+ edited = 1;
+ }
+ }
+
+ if ((arg = getarg(args, 'h')) != NULL ||
+ (arg = getarg(args, 'H')) != NULL) {
+ if (strcmp(arg->val, "-") == 0) {
+ if (!pwd->pw_passwd || *pwd->pw_passwd != '*') {
+ pwd->pw_passwd = "*"; /* No access */
+ edited = 1;
+ }
+ } else {
+ int fd = atoi(arg->val);
+ int precrypt = (arg->ch == 'H');
+ int b;
+ int istty = isatty(fd);
+ struct termios t;
+ login_cap_t *lc;
+
+ if (istty) {
+ if (tcgetattr(fd, &t) == -1)
+ istty = 0;
+ else {
+ struct termios n = t;
+
+ /* Disable echo */
+ n.c_lflag &= ~(ECHO);
+ tcsetattr(fd, TCSANOW, &n);
+ printf("%s%spassword for user %s:",
+ (mode == M_UPDATE) ? "new " : "",
+ precrypt ? "encrypted " : "",
+ pwd->pw_name);
+ fflush(stdout);
+ }
+ }
+ b = read(fd, line, sizeof(line) - 1);
+ if (istty) { /* Restore state */
+ tcsetattr(fd, TCSANOW, &t);
+ fputc('\n', stdout);
+ fflush(stdout);
+ }
+ if (b < 0) {
+ warn("-%c file descriptor", precrypt ? 'H' :
+ 'h');
+ return EX_IOERR;
+ }
+ line[b] = '\0';
+ if ((p = strpbrk(line, "\r\n")) != NULL)
+ *p = '\0';
+ if (!*line)
+ errx(EX_DATAERR, "empty password read on file descriptor %d", fd);
+ if (precrypt) {
+ if (strchr(line, ':') != NULL)
+ return EX_DATAERR;
+ pwd->pw_passwd = line;
+ } else {
+ lc = login_getpwclass(pwd);
+ if (lc == NULL ||
+ login_setcryptfmt(lc, "md5", NULL) == NULL)
+ warn("setting crypt(3) format");
+ login_close(lc);
+ pwd->pw_passwd = pw_pwcrypt(line);
+ }
+ edited = 1;
+ }
+ }
+
+ /*
+ * Special case: -N only displays & exits
+ */
+ if (getarg(args, 'N') != NULL)
+ return print_user(pwd,
+ getarg(args, 'P') != NULL,
+ getarg(args, '7') != NULL);
+
+ if (mode == M_ADD) {
+ edited = 1; /* Always */
+ rc = addpwent(pwd);
+ if (rc == -1) {
+ warnx("user '%s' already exists", pwd->pw_name);
+ return EX_IOERR;
+ } else if (rc != 0) {
+ warn("passwd file update");
+ return EX_IOERR;
+ }
+ if (cnf->nispasswd && *cnf->nispasswd=='/') {
+ rc = addnispwent(cnf->nispasswd, pwd);
+ if (rc == -1)
+ warnx("User '%s' already exists in NIS passwd", pwd->pw_name);
+ else
+ warn("NIS passwd update");
+ /* NOTE: we treat NIS-only update errors as non-fatal */
+ }
+ } else if (mode == M_UPDATE || mode == M_LOCK || mode == M_UNLOCK) {
+ if (edited) { /* Only updated this if required */
+ rc = chgpwent(a_name->val, pwd);
+ if (rc == -1) {
+ warnx("user '%s' does not exist (NIS?)", pwd->pw_name);
+ return EX_IOERR;
+ } else if (rc != 0) {
+ warn("passwd file update");
+ return EX_IOERR;
+ }
+ if ( cnf->nispasswd && *cnf->nispasswd=='/') {
+ rc = chgnispwent(cnf->nispasswd, a_name->val, pwd);
+ if (rc == -1)
+ warn("User '%s' not found in NIS passwd", pwd->pw_name);
+ else
+ warn("NIS passwd update");
+ /* NOTE: NIS-only update errors are not fatal */
+ }
+ }
+ }
+
+ /*
+ * Ok, user is created or changed - now edit group file
+ */
+
+ if (mode == M_ADD || getarg(args, 'G') != NULL)
+ editgroups(pwd->pw_name, cnf->groups);
+
+ /* go get a current version of pwd */
+ pwd = GETPWNAM(a_name->val);
+ if (pwd == NULL) {
+ /* This will fail when we rename, so special case that */
+ if (mode == M_UPDATE && (arg = getarg(args, 'l')) != NULL) {
+ a_name->val = arg->val; /* update new name */
+ pwd = GETPWNAM(a_name->val); /* refetch renamed rec */
+ }
+ }
+ if (pwd == NULL) /* can't go on without this */
+ errx(EX_NOUSER, "user '%s' disappeared during update", a_name->val);
+
+ grp = GETGRGID(pwd->pw_gid);
+ pw_log(cnf, mode, W_USER, "%s(%ld):%s(%ld):%s:%s:%s",
+ pwd->pw_name, (long) pwd->pw_uid,
+ grp ? grp->gr_name : "unknown", (long) (grp ? grp->gr_gid : -1),
+ pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell);
+
+ /*
+ * If adding, let's touch and chown the user's mail file. This is not
+ * strictly necessary under BSD with a 0755 maildir but it also
+ * doesn't hurt anything to create the empty mailfile
+ */
+ if (mode == M_ADD) {
+ if (!PWALTDIR()) {
+ sprintf(line, "%s/%s", _PATH_MAILDIR, pwd->pw_name);
+ close(open(line, O_RDWR | O_CREAT, 0600)); /* Preserve contents &
+ * mtime */
+ chown(line, pwd->pw_uid, pwd->pw_gid);
+ }
+ }
+
+ /*
+ * Let's create and populate the user's home directory. Note
+ * that this also `works' for editing users if -m is used, but
+ * existing files will *not* be overwritten.
+ */
+ if (!PWALTDIR() && getarg(args, 'm') != NULL && pwd->pw_dir && *pwd->pw_dir == '/' && pwd->pw_dir[1]) {
+ copymkdir(pwd->pw_dir, cnf->dotdir, cnf->homemode, pwd->pw_uid, pwd->pw_gid);
+ pw_log(cnf, mode, W_USER, "%s(%ld) home %s made",
+ pwd->pw_name, (long) pwd->pw_uid, pwd->pw_dir);
+ }
+
+
+ /*
+ * Finally, send mail to the new user as well, if we are asked to
+ */
+ if (mode == M_ADD && !PWALTDIR() && cnf->newmail && *cnf->newmail && (fp = fopen(cnf->newmail, "r")) != NULL) {
+ FILE *pfp = popen(_PATH_SENDMAIL " -t", "w");
+
+ if (pfp == NULL)
+ warn("sendmail");
+ else {
+ fprintf(pfp, "From: root\n" "To: %s\n" "Subject: Welcome!\n\n", pwd->pw_name);
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ /* Do substitutions? */
+ fputs(line, pfp);
+ }
+ pclose(pfp);
+ pw_log(cnf, mode, W_USER, "%s(%ld) new user mail sent",
+ pwd->pw_name, (long) pwd->pw_uid);
+ }
+ fclose(fp);
+ }
+
+ return EXIT_SUCCESS;
+}
+
+
+static uid_t
+pw_uidpolicy(struct userconf * cnf, struct cargs * args)
+{
+ struct passwd *pwd;
+ uid_t uid = (uid_t) - 1;
+ struct carg *a_uid = getarg(args, 'u');
+
+ /*
+ * Check the given uid, if any
+ */
+ if (a_uid != NULL) {
+ uid = (uid_t) atol(a_uid->val);
+
+ if ((pwd = GETPWUID(uid)) != NULL && getarg(args, 'o') == NULL)
+ errx(EX_DATAERR, "uid `%ld' has already been allocated", (long) pwd->pw_uid);
+ } else {
+ struct bitmap bm;
+
+ /*
+ * We need to allocate the next available uid under one of
+ * two policies a) Grab the first unused uid b) Grab the
+ * highest possible unused uid
+ */
+ if (cnf->min_uid >= cnf->max_uid) { /* Sanity
+ * claus^H^H^H^Hheck */
+ cnf->min_uid = 1000;
+ cnf->max_uid = 32000;
+ }
+ bm = bm_alloc(cnf->max_uid - cnf->min_uid + 1);
+
+ /*
+ * Now, let's fill the bitmap from the password file
+ */
+ SETPWENT();
+ while ((pwd = GETPWENT()) != NULL)
+ if (pwd->pw_uid >= (uid_t) cnf->min_uid && pwd->pw_uid <= (uid_t) cnf->max_uid)
+ bm_setbit(&bm, pwd->pw_uid - cnf->min_uid);
+ ENDPWENT();
+
+ /*
+ * Then apply the policy, with fallback to reuse if necessary
+ */
+ if (cnf->reuse_uids || (uid = (uid_t) (bm_lastset(&bm) + cnf->min_uid + 1)) > cnf->max_uid)
+ uid = (uid_t) (bm_firstunset(&bm) + cnf->min_uid);
+
+ /*
+ * Another sanity check
+ */
+ if (uid < cnf->min_uid || uid > cnf->max_uid)
+ errx(EX_SOFTWARE, "unable to allocate a new uid - range fully used");
+ bm_dealloc(&bm);
+ }
+ return uid;
+}
+
+
+static uid_t
+pw_gidpolicy(struct userconf * cnf, struct cargs * args, char *nam, gid_t prefer)
+{
+ struct group *grp;
+ gid_t gid = (uid_t) - 1;
+ struct carg *a_gid = getarg(args, 'g');
+
+ /*
+ * If no arg given, see if default can help out
+ */
+ if (a_gid == NULL && cnf->default_group && *cnf->default_group)
+ a_gid = addarg(args, 'g', cnf->default_group);
+
+ /*
+ * Check the given gid, if any
+ */
+ SETGRENT();
+ if (a_gid != NULL) {
+ if ((grp = GETGRNAM(a_gid->val)) == NULL) {
+ gid = (gid_t) atol(a_gid->val);
+ if ((gid == 0 && !isdigit((unsigned char)*a_gid->val)) || (grp = GETGRGID(gid)) == NULL)
+ errx(EX_NOUSER, "group `%s' is not defined", a_gid->val);
+ }
+ gid = grp->gr_gid;
+ } else if ((grp = GETGRNAM(nam)) != NULL && grp->gr_mem[0] == NULL) {
+ gid = grp->gr_gid; /* Already created? Use it anyway... */
+ } else {
+ struct cargs grpargs;
+ char tmp[32];
+
+ LIST_INIT(&grpargs);
+ addarg(&grpargs, 'n', nam);
+
+ /*
+ * We need to auto-create a group with the user's name. We
+ * can send all the appropriate output to our sister routine
+ * bit first see if we can create a group with gid==uid so we
+ * can keep the user and group ids in sync. We purposely do
+ * NOT check the gid range if we can force the sync. If the
+ * user's name dups an existing group, then the group add
+ * function will happily handle that case for us and exit.
+ */
+ if (GETGRGID(prefer) == NULL) {
+ sprintf(tmp, "%lu", (unsigned long) prefer);
+ addarg(&grpargs, 'g', tmp);
+ }
+ if (getarg(args, 'N'))
+ {
+ addarg(&grpargs, 'N', NULL);
+ addarg(&grpargs, 'q', NULL);
+ gid = pw_group(cnf, M_NEXT, &grpargs);
+ }
+ else
+ {
+ pw_group(cnf, M_ADD, &grpargs);
+ if ((grp = GETGRNAM(nam)) != NULL)
+ gid = grp->gr_gid;
+ }
+ a_gid = LIST_FIRST(&grpargs);
+ while (a_gid != NULL) {
+ struct carg *t = LIST_NEXT(a_gid, list);
+ LIST_REMOVE(a_gid, list);
+ a_gid = t;
+ }
+ }
+ ENDGRENT();
+ return gid;
+}
+
+
+static time_t
+pw_pwdpolicy(struct userconf * cnf, struct cargs * args)
+{
+ time_t result = 0;
+ time_t now = time(NULL);
+ struct carg *arg = getarg(args, 'p');
+
+ if (arg != NULL) {
+ if ((result = parse_date(now, arg->val)) == now)
+ errx(EX_DATAERR, "invalid date/time `%s'", arg->val);
+ } else if (cnf->password_days > 0)
+ result = now + ((long) cnf->password_days * 86400L);
+ return result;
+}
+
+
+static time_t
+pw_exppolicy(struct userconf * cnf, struct cargs * args)
+{
+ time_t result = 0;
+ time_t now = time(NULL);
+ struct carg *arg = getarg(args, 'e');
+
+ if (arg != NULL) {
+ if ((result = parse_date(now, arg->val)) == now)
+ errx(EX_DATAERR, "invalid date/time `%s'", arg->val);
+ } else if (cnf->expire_days > 0)
+ result = now + ((long) cnf->expire_days * 86400L);
+ return result;
+}
+
+
+static char *
+pw_homepolicy(struct userconf * cnf, struct cargs * args, char const * user)
+{
+ struct carg *arg = getarg(args, 'd');
+
+ if (arg)
+ return arg->val;
+ else {
+ static char home[128];
+
+ if (cnf->home == NULL || *cnf->home == '\0')
+ errx(EX_CONFIG, "no base home directory set");
+ sprintf(home, "%s/%s", cnf->home, user);
+ return home;
+ }
+}
+
+static char *
+shell_path(char const * path, char *shells[], char *sh)
+{
+ if (sh != NULL && (*sh == '/' || *sh == '\0'))
+ return sh; /* specified full path or forced none */
+ else {
+ char *p;
+ char paths[_UC_MAXLINE];
+
+ /*
+ * We need to search paths
+ */
+ strlcpy(paths, path, sizeof(paths));
+ for (p = strtok(paths, ": \t\r\n"); p != NULL; p = strtok(NULL, ": \t\r\n")) {
+ int i;
+ static char shellpath[256];
+
+ if (sh != NULL) {
+ sprintf(shellpath, "%s/%s", p, sh);
+ if (access(shellpath, X_OK) == 0)
+ return shellpath;
+ } else
+ for (i = 0; i < _UC_MAXSHELLS && shells[i] != NULL; i++) {
+ sprintf(shellpath, "%s/%s", p, shells[i]);
+ if (access(shellpath, X_OK) == 0)
+ return shellpath;
+ }
+ }
+ if (sh == NULL)
+ errx(EX_OSFILE, "can't find shell `%s' in shell paths", sh);
+ errx(EX_CONFIG, "no default shell available or defined");
+ return NULL;
+ }
+}
+
+
+static char *
+pw_shellpolicy(struct userconf * cnf, struct cargs * args, char *newshell)
+{
+ char *sh = newshell;
+ struct carg *arg = getarg(args, 's');
+
+ if (newshell == NULL && arg != NULL)
+ sh = arg->val;
+ return shell_path(cnf->shelldir, cnf->shells, sh ? sh : cnf->shell_default);
+}
+
+#define SALTSIZE 32
+
+static char const chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./";
+
+char *
+pw_pwcrypt(char *password)
+{
+ int i;
+ char salt[SALTSIZE + 1];
+
+ static char buf[256];
+
+ /*
+ * Calculate a salt value
+ */
+ for (i = 0; i < SALTSIZE; i++)
+ salt[i] = chars[arc4random_uniform(sizeof(chars) - 1)];
+ salt[SALTSIZE] = '\0';
+
+ return strcpy(buf, crypt(password, salt));
+}
+
+
+static char *
+pw_password(struct userconf * cnf, struct cargs * args, char const * user)
+{
+ int i, l;
+ char pwbuf[32];
+
+ switch (cnf->default_password) {
+ case -1: /* Random password */
+ l = (arc4random() % 8 + 8); /* 8 - 16 chars */
+ for (i = 0; i < l; i++)
+ pwbuf[i] = chars[arc4random_uniform(sizeof(chars)-1)];
+ pwbuf[i] = '\0';
+
+ /*
+ * We give this information back to the user
+ */
+ if (getarg(args, 'h') == NULL && getarg(args, 'H') == NULL &&
+ getarg(args, 'N') == NULL) {
+ if (isatty(STDOUT_FILENO))
+ printf("Password for '%s' is: ", user);
+ printf("%s\n", pwbuf);
+ fflush(stdout);
+ }
+ break;
+
+ case -2: /* No password at all! */
+ return "";
+
+ case 0: /* No login - default */
+ default:
+ return "*";
+
+ case 1: /* user's name */
+ strlcpy(pwbuf, user, sizeof(pwbuf));
+ break;
+ }
+ return pw_pwcrypt(pwbuf);
+}
+
+
+static int
+print_user(struct passwd * pwd, int pretty, int v7)
+{
+ if (!pretty) {
+ char buf[_UC_MAXLINE];
+
+ fmtpwentry(buf, pwd, v7 ? PWF_PASSWD : PWF_STANDARD);
+ fputs(buf, stdout);
+ } else {
+ int j;
+ char *p;
+ struct group *grp = GETGRGID(pwd->pw_gid);
+ char uname[60] = "User &", office[60] = "[None]",
+ wphone[60] = "[None]", hphone[60] = "[None]";
+ char acexpire[32] = "[None]", pwexpire[32] = "[None]";
+ struct tm * tptr;
+
+ if ((p = strtok(pwd->pw_gecos, ",")) != NULL) {
+ strlcpy(uname, p, sizeof(uname));
+ if ((p = strtok(NULL, ",")) != NULL) {
+ strlcpy(office, p, sizeof(office));
+ if ((p = strtok(NULL, ",")) != NULL) {
+ strlcpy(wphone, p, sizeof(wphone));
+ if ((p = strtok(NULL, "")) != NULL) {
+ strlcpy(hphone, p,
+ sizeof(hphone));
+ }
+ }
+ }
+ }
+ /*
+ * Handle '&' in gecos field
+ */
+ if ((p = strchr(uname, '&')) != NULL) {
+ int l = strlen(pwd->pw_name);
+ int m = strlen(p);
+
+ memmove(p + l, p + 1, m);
+ memmove(p, pwd->pw_name, l);
+ *p = (char) toupper((unsigned char)*p);
+ }
+ if (pwd->pw_expire > (time_t)0 && (tptr = localtime(&pwd->pw_expire)) != NULL)
+ strftime(acexpire, sizeof acexpire, "%c", tptr);
+ if (pwd->pw_change > (time_t)0 && (tptr = localtime(&pwd->pw_change)) != NULL)
+ strftime(pwexpire, sizeof pwexpire, "%c", tptr);
+ printf("Login Name: %-15s #%-12ld Group: %-15s #%ld\n"
+ " Full Name: %s\n"
+ " Home: %-26.26s Class: %s\n"
+ " Shell: %-26.26s Office: %s\n"
+ "Work Phone: %-26.26s Home Phone: %s\n"
+ "Acc Expire: %-26.26s Pwd Expire: %s\n",
+ pwd->pw_name, (long) pwd->pw_uid,
+ grp ? grp->gr_name : "(invalid)", (long) pwd->pw_gid,
+ uname, pwd->pw_dir, pwd->pw_class,
+ pwd->pw_shell, office, wphone, hphone,
+ acexpire, pwexpire);
+ SETGRENT();
+ j = 0;
+ while ((grp=GETGRENT()) != NULL)
+ {
+ int i = 0;
+ while (grp->gr_mem[i] != NULL)
+ {
+ if (strcmp(grp->gr_mem[i], pwd->pw_name)==0)
+ {
+ printf(j++ == 0 ? " Groups: %s" : ",%s", grp->gr_name);
+ break;
+ }
+ ++i;
+ }
+ }
+ ENDGRENT();
+ printf("%s", j ? "\n" : "");
+ }
+ return EXIT_SUCCESS;
+}
+
+char *
+pw_checkname(u_char *name, int gecos)
+{
+ char showch[8];
+ u_char const *badchars, *ch, *showtype;
+ int reject;
+
+ ch = name;
+ reject = 0;
+ if (gecos) {
+ /* See if the name is valid as a gecos (comment) field. */
+ badchars = ":!@";
+ showtype = "gecos field";
+ } else {
+ /* See if the name is valid as a userid or group. */
+ badchars = " ,\t:+&#%$^()!@~*?<>=|\\/\"";
+ showtype = "userid/group name";
+ /* Userids and groups can not have a leading '-'. */
+ if (*ch == '-')
+ reject = 1;
+ }
+ if (!reject) {
+ while (*ch) {
+ if (strchr(badchars, *ch) != NULL || *ch < ' ' ||
+ *ch == 127) {
+ reject = 1;
+ break;
+ }
+ /* 8-bit characters are only allowed in GECOS fields */
+ if (!gecos && (*ch & 0x80)) {
+ reject = 1;
+ break;
+ }
+ ch++;
+ }
+ }
+ /*
+ * A `$' is allowed as the final character for userids and groups,
+ * mainly for the benefit of samba.
+ */
+ if (reject && !gecos) {
+ if (*ch == '$' && *(ch + 1) == '\0') {
+ reject = 0;
+ ch++;
+ }
+ }
+ if (reject) {
+ snprintf(showch, sizeof(showch), (*ch >= ' ' && *ch < 127)
+ ? "`%c'" : "0x%02x", *ch);
+ errx(EX_DATAERR, "invalid character %s at position %d in %s",
+ showch, (ch - name), showtype);
+ }
+ if (!gecos && (ch - name) > LOGNAMESIZE)
+ errx(EX_DATAERR, "name too long `%s' (max is %d)", name,
+ LOGNAMESIZE);
+ return (char *)name;
+}
+
+
+static void
+rmat(uid_t uid)
+{
+ DIR *d = opendir("/var/at/jobs");
+
+ if (d != NULL) {
+ struct dirent *e;
+
+ while ((e = readdir(d)) != NULL) {
+ struct stat st;
+
+ if (strncmp(e->d_name, ".lock", 5) != 0 &&
+ stat(e->d_name, &st) == 0 &&
+ !S_ISDIR(st.st_mode) &&
+ st.st_uid == uid) {
+ char tmp[MAXPATHLEN];
+
+ sprintf(tmp, "/usr/bin/atrm %s", e->d_name);
+ system(tmp);
+ }
+ }
+ closedir(d);
+ }
+}
+
+static void
+rmopie(char const * name)
+{
+ static const char etcopie[] = "/etc/opiekeys";
+ FILE *fp = fopen(etcopie, "r+");
+
+ if (fp != NULL) {
+ char tmp[1024];
+ off_t atofs = 0;
+ int length = strlen(name);
+
+ while (fgets(tmp, sizeof tmp, fp) != NULL) {
+ if (strncmp(name, tmp, length) == 0 && tmp[length]==' ') {
+ if (fseek(fp, atofs, SEEK_SET) == 0) {
+ fwrite("#", 1, 1, fp); /* Comment username out */
+ }
+ break;
+ }
+ atofs = ftell(fp);
+ }
+ /*
+ * If we got an error of any sort, don't update!
+ */
+ fclose(fp);
+ }
+}
+
diff --git a/usr.sbin/pw/pw_vpw.c b/usr.sbin/pw/pw_vpw.c
new file mode 100644
index 0000000..473cbb6
--- /dev/null
+++ b/usr.sbin/pw/pw_vpw.c
@@ -0,0 +1,316 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/param.h>
+
+#include "pwupd.h"
+
+static FILE * pwd_fp = NULL;
+
+void
+vendpwent(void)
+{
+ if (pwd_fp != NULL) {
+ fclose(pwd_fp);
+ pwd_fp = NULL;
+ }
+}
+
+void
+vsetpwent(void)
+{
+ vendpwent();
+}
+
+static struct passwd *
+vnextpwent(char const * nam, uid_t uid, int doclose)
+{
+ struct passwd * pw = NULL;
+ static char pwtmp[1024];
+
+ strlcpy(pwtmp, getpwpath(_MASTERPASSWD), sizeof(pwtmp));
+
+ if (pwd_fp != NULL || (pwd_fp = fopen(pwtmp, "r")) != NULL) {
+ int done = 0;
+
+ static struct passwd pwd;
+
+ while (!done && fgets(pwtmp, sizeof pwtmp, pwd_fp) != NULL)
+ {
+ int i, quickout = 0;
+ char * q;
+ char * p = strchr(pwtmp, '\n');
+
+ if (p == NULL) {
+ while (fgets(pwtmp, sizeof pwtmp, pwd_fp) != NULL && strchr(pwtmp, '\n')==NULL)
+ ; /* Skip long lines */
+ continue;
+ }
+
+ /* skip comments & empty lines */
+ if (*pwtmp =='\n' || *pwtmp == '#')
+ continue;
+
+ i = 0;
+ q = p = pwtmp;
+ bzero(&pwd, sizeof pwd);
+ while (!quickout && (p = strsep(&q, ":\n")) != NULL) {
+ switch (i++)
+ {
+ case 0: /* username */
+ pwd.pw_name = p;
+ if (nam) {
+ if (strcmp(nam, p) == 0)
+ done = 1;
+ else
+ quickout = 1;
+ }
+ break;
+ case 1: /* password */
+ pwd.pw_passwd = p;
+ break;
+ case 2: /* uid */
+ pwd.pw_uid = atoi(p);
+ if (uid != (uid_t)-1) {
+ if (uid == pwd.pw_uid)
+ done = 1;
+ else
+ quickout = 1;
+ }
+ break;
+ case 3: /* gid */
+ pwd.pw_gid = atoi(p);
+ break;
+ case 4: /* class */
+ if (nam == NULL && uid == (uid_t)-1)
+ done = 1;
+ pwd.pw_class = p;
+ break;
+ case 5: /* change */
+ pwd.pw_change = (time_t)atol(p);
+ break;
+ case 6: /* expire */
+ pwd.pw_expire = (time_t)atol(p);
+ break;
+ case 7: /* gecos */
+ pwd.pw_gecos = p;
+ break;
+ case 8: /* directory */
+ pwd.pw_dir = p;
+ break;
+ case 9: /* shell */
+ pwd.pw_shell = p;
+ break;
+ }
+ }
+ }
+ if (doclose)
+ vendpwent();
+ if (done && pwd.pw_name) {
+ pw = &pwd;
+
+ #define CKNULL(s) s = s ? s : ""
+ CKNULL(pwd.pw_passwd);
+ CKNULL(pwd.pw_class);
+ CKNULL(pwd.pw_gecos);
+ CKNULL(pwd.pw_dir);
+ CKNULL(pwd.pw_shell);
+ }
+ }
+ return pw;
+}
+
+struct passwd *
+vgetpwent(void)
+{
+ return vnextpwent(NULL, -1, 0);
+}
+
+struct passwd *
+vgetpwuid(uid_t uid)
+{
+ return vnextpwent(NULL, uid, 1);
+}
+
+struct passwd *
+vgetpwnam(const char * nam)
+{
+ return vnextpwent(nam, -1, 1);
+}
+
+int vpwdb(char *arg, ...)
+{
+ arg=arg;
+ return 0;
+}
+
+
+
+static FILE * grp_fp = NULL;
+
+void
+vendgrent(void)
+{
+ if (grp_fp != NULL) {
+ fclose(grp_fp);
+ grp_fp = NULL;
+ }
+}
+
+RET_SETGRENT
+vsetgrent(void)
+{
+ vendgrent();
+#if defined(__FreeBSD__)
+ return 0;
+#endif
+}
+
+static struct group *
+vnextgrent(char const * nam, gid_t gid, int doclose)
+{
+ struct group * gr = NULL;
+
+ static char * grtmp = NULL;
+ static int grlen = 0;
+ static char ** mems = NULL;
+ static int memlen = 0;
+
+ extendline(&grtmp, &grlen, MAXPATHLEN);
+ strlcpy(grtmp, getgrpath(_GROUP), MAXPATHLEN);
+
+ if (grp_fp != NULL || (grp_fp = fopen(grtmp, "r")) != NULL) {
+ int done = 0;
+
+ static struct group grp;
+
+ while (!done && fgets(grtmp, grlen, grp_fp) != NULL)
+ {
+ int i, quickout = 0;
+ int mno = 0;
+ char * q, * p;
+ char * sep = ":\n";
+
+ if ((p = strchr(grtmp, '\n')) == NULL) {
+ int l;
+ extendline(&grtmp, &grlen, grlen + PWBUFSZ);
+ l = strlen(grtmp);
+ if (fgets(grtmp + l, grlen - l, grp_fp) == NULL)
+ break; /* No newline terminator on last line */
+ }
+ /* Skip comments and empty lines */
+ if (*grtmp == '\n' || *grtmp == '#')
+ continue;
+ i = 0;
+ q = p = grtmp;
+ bzero(&grp, sizeof grp);
+ extendarray(&mems, &memlen, 200);
+ while (!quickout && (p = strsep(&q, sep)) != NULL) {
+ switch (i++)
+ {
+ case 0: /* groupname */
+ grp.gr_name = p;
+ if (nam) {
+ if (strcmp(nam, p) == 0)
+ done = 1;
+ else
+ quickout = 1;
+ }
+ break;
+ case 1: /* password */
+ grp.gr_passwd = p;
+ break;
+ case 2: /* gid */
+ grp.gr_gid = atoi(p);
+ if (gid != (gid_t)-1) {
+ if (gid == (gid_t)grp.gr_gid)
+ done = 1;
+ else
+ quickout = 1;
+ } else if (nam == NULL)
+ done = 1;
+ break;
+ case 3:
+ q = p;
+ sep = ",\n";
+ break;
+ default:
+ if (*p) {
+ extendarray(&mems, &memlen, mno + 2);
+ mems[mno++] = p;
+ }
+ break;
+ }
+ }
+ grp.gr_mem = mems;
+ mems[mno] = NULL;
+ }
+ if (doclose)
+ vendgrent();
+ if (done && grp.gr_name) {
+ gr = &grp;
+
+ CKNULL(grp.gr_passwd);
+ }
+ }
+ return gr;
+}
+
+struct group *
+vgetgrent(void)
+{
+ return vnextgrent(NULL, -1, 0);
+}
+
+
+struct group *
+vgetgrgid(gid_t gid)
+{
+ return vnextgrent(NULL, gid, 1);
+}
+
+struct group *
+vgetgrnam(const char * nam)
+{
+ return vnextgrent(nam, -1, 1);
+}
+
+int
+vgrdb(char *arg, ...)
+{
+ arg=arg;
+ return 0;
+}
+
diff --git a/usr.sbin/pw/pwupd.c b/usr.sbin/pw/pwupd.c
new file mode 100644
index 0000000..cb8456d
--- /dev/null
+++ b/usr.sbin/pw/pwupd.c
@@ -0,0 +1,213 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/wait.h>
+
+#include "pwupd.h"
+
+#define HAVE_PWDB_C 1
+#define HAVE_PWDB_U 1
+
+static char pathpwd[] = _PATH_PWD;
+static char * pwpath = pathpwd;
+
+int
+setpwdir(const char * dir)
+{
+ if (dir == NULL)
+ return -1;
+ else {
+ char * d = malloc(strlen(dir)+1);
+ if (d == NULL)
+ return -1;
+ pwpath = strcpy(d, dir);
+ }
+ return 0;
+}
+
+char *
+getpwpath(char const * file)
+{
+ static char pathbuf[MAXPATHLEN];
+
+ snprintf(pathbuf, sizeof pathbuf, "%s/%s", pwpath, file);
+ return pathbuf;
+}
+
+int
+pwdb(char *arg,...)
+{
+ int i = 0;
+ pid_t pid;
+ va_list ap;
+ char *args[10];
+
+ args[i++] = _PATH_PWD_MKDB;
+ va_start(ap, arg);
+ while (i < 6 && arg != NULL) {
+ args[i++] = arg;
+ arg = va_arg(ap, char *);
+ }
+ if (pwpath != pathpwd) {
+ args[i++] = "-d";
+ args[i++] = pwpath;
+ }
+ args[i++] = getpwpath(_MASTERPASSWD);
+ args[i] = NULL;
+
+ if ((pid = fork()) == -1) /* Error (errno set) */
+ i = errno;
+ else if (pid == 0) { /* Child */
+ execv(args[0], args);
+ _exit(1);
+ } else { /* Parent */
+ waitpid(pid, &i, 0);
+ if (WEXITSTATUS(i))
+ i = EIO;
+ }
+ return i;
+}
+
+int
+fmtpwentry(char *buf, struct passwd * pwd, int type)
+{
+ int l;
+ char *pw;
+
+ pw = (type == PWF_MASTER) ?
+ ((pwd->pw_passwd == NULL) ? "" : pwd->pw_passwd) : "*";
+
+ if (type == PWF_PASSWD)
+ l = sprintf(buf, "%s:*:%ld:%ld:%s:%s:%s\n",
+ pwd->pw_name, (long) pwd->pw_uid, (long) pwd->pw_gid,
+ pwd->pw_gecos ? pwd->pw_gecos : "User &",
+ pwd->pw_dir, pwd->pw_shell);
+ else
+ l = sprintf(buf, "%s:%s:%ld:%ld:%s:%lu:%lu:%s:%s:%s\n",
+ pwd->pw_name, pw, (long) pwd->pw_uid, (long) pwd->pw_gid,
+ pwd->pw_class ? pwd->pw_class : "",
+ (unsigned long) pwd->pw_change,
+ (unsigned long) pwd->pw_expire,
+ pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell);
+ return l;
+}
+
+
+int
+fmtpwent(char *buf, struct passwd * pwd)
+{
+ return fmtpwentry(buf, pwd, PWF_STANDARD);
+}
+
+static int
+pw_update(struct passwd * pwd, char const * user, int mode)
+{
+ int rc = 0;
+
+ ENDPWENT();
+
+ /*
+ * First, let's check the see if the database is alright
+ * Note: -C is only available in FreeBSD 2.2 and above
+ */
+#ifdef HAVE_PWDB_C
+ rc = pwdb("-C", (char *)NULL); /* Check only */
+ if (rc == 0) {
+#else
+ { /* No -C */
+#endif
+ char pfx[PWBUFSZ];
+ char pwbuf[PWBUFSZ];
+ int l = snprintf(pfx, PWBUFSZ, "%s:", user);
+#ifdef HAVE_PWDB_U
+ int isrename = pwd!=NULL && strcmp(user, pwd->pw_name);
+#endif
+
+ /*
+ * Update the passwd file first
+ */
+ if (pwd == NULL)
+ *pwbuf = '\0';
+ else
+ fmtpwentry(pwbuf, pwd, PWF_PASSWD);
+
+ if (l < 0)
+ l = 0;
+ rc = fileupdate(getpwpath(_PASSWD), 0644, pwbuf, pfx, l, mode);
+ if (rc == 0) {
+
+ /*
+ * Then the master.passwd file
+ */
+ if (pwd != NULL)
+ fmtpwentry(pwbuf, pwd, PWF_MASTER);
+ rc = fileupdate(getpwpath(_MASTERPASSWD), 0600, pwbuf, pfx, l, mode);
+ if (rc == 0) {
+#ifdef HAVE_PWDB_U
+ if (mode == UPD_DELETE || isrename)
+#endif
+ rc = pwdb(NULL);
+#ifdef HAVE_PWDB_U
+ else
+ rc = pwdb("-u", user, (char *)NULL);
+#endif
+ }
+ }
+ }
+ return rc;
+}
+
+int
+addpwent(struct passwd * pwd)
+{
+ return pw_update(pwd, pwd->pw_name, UPD_CREATE);
+}
+
+int
+chgpwent(char const * login, struct passwd * pwd)
+{
+ return pw_update(pwd, login, UPD_REPLACE);
+}
+
+int
+delpwent(struct passwd * pwd)
+{
+ return pw_update(NULL, pwd->pw_name, UPD_DELETE);
+}
diff --git a/usr.sbin/pw/pwupd.h b/usr.sbin/pw/pwupd.h
new file mode 100644
index 0000000..7289065
--- /dev/null
+++ b/usr.sbin/pw/pwupd.h
@@ -0,0 +1,160 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 _PWUPD_H_
+#define _PWUPD_H_
+
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <sys/cdefs.h>
+
+#if defined(__FreeBSD__)
+#define RET_SETGRENT int
+#else
+#define RET_SETGRENT void
+#endif
+
+enum updtype
+{
+ UPD_DELETE = -1,
+ UPD_CREATE = 0,
+ UPD_REPLACE = 1
+};
+
+__BEGIN_DECLS
+int fileupdate(char const * fname, mode_t fm, char const * nline, char const * pfx, int pfxlen, int updmode);
+__END_DECLS
+
+enum pwdfmttype
+{
+ PWF_STANDARD, /* MASTER format but with '*' as password */
+ PWF_PASSWD, /* V7 format */
+ PWF_GROUP = PWF_PASSWD,
+ PWF_MASTER /* MASTER format with password */
+};
+
+struct pwf
+{
+ int _altdir;
+ void (*_setpwent)(void);
+ void (*_endpwent)(void);
+ struct passwd * (*_getpwent)(void);
+ struct passwd * (*_getpwuid)(uid_t uid);
+ struct passwd * (*_getpwnam)(const char * nam);
+ int (*_pwdb)(char *arg, ...);
+ RET_SETGRENT (*_setgrent)(void);
+ void (*_endgrent)(void);
+ struct group * (*_getgrent)(void);
+ struct group * (*_getgrgid)(gid_t gid);
+ struct group * (*_getgrnam)(const char * nam);
+ int (*_grdb)(char *arg, ...);
+};
+
+extern struct pwf PWF;
+extern struct pwf VPWF;
+
+#define SETPWENT() PWF._setpwent()
+#define ENDPWENT() PWF._endpwent()
+#define GETPWENT() PWF._getpwent()
+#define GETPWUID(uid) PWF._getpwuid(uid)
+#define GETPWNAM(nam) PWF._getpwnam(nam)
+#define PWDB(args) PWF._pwdb(args)
+
+#define SETGRENT() PWF._setgrent()
+#define ENDGRENT() PWF._endgrent()
+#define GETGRENT() PWF._getgrent()
+#define GETGRGID(gid) PWF._getgrgid(gid)
+#define GETGRNAM(nam) PWF._getgrnam(nam)
+#define GRDB(args) PWF._grdb(args)
+
+#define PWALTDIR() PWF._altdir
+#ifndef _PATH_PWD
+#define _PATH_PWD "/etc"
+#endif
+#ifndef _GROUP
+#define _GROUP "group"
+#endif
+#ifndef _PASSWD
+#define _PASSWD "passwd"
+#endif
+#ifndef _MASTERPASSWD
+#define _MASTERPASSWD "master.passwd"
+#endif
+#ifndef _GROUP
+#define _GROUP "group"
+#endif
+
+__BEGIN_DECLS
+int addpwent(struct passwd * pwd);
+int delpwent(struct passwd * pwd);
+int chgpwent(char const * login, struct passwd * pwd);
+int fmtpwent(char *buf, struct passwd * pwd);
+int fmtpwentry(char *buf, struct passwd * pwd, int type);
+
+int setpwdir(const char * dir);
+char * getpwpath(char const * file);
+int pwdb(char *arg, ...);
+
+int addgrent(struct group * grp);
+int delgrent(struct group * grp);
+int chggrent(char const * name, struct group * grp);
+int fmtgrent(char **buf, int * buflen, struct group * grp);
+int fmtgrentry(char **buf, int * buflen, struct group * grp, int type);
+int editgroups(char *name, char **groups);
+
+int setgrdir(const char * dir);
+char * getgrpath(const char *file);
+int grdb(char *arg, ...);
+
+void vsetpwent(void);
+void vendpwent(void);
+struct passwd * vgetpwent(void);
+struct passwd * vgetpwuid(uid_t uid);
+struct passwd * vgetpwnam(const char * nam);
+struct passwd * vgetpwent(void);
+int vpwdb(char *arg, ...);
+
+struct group * vgetgrent(void);
+struct group * vgetgrgid(gid_t gid);
+struct group * vgetgrnam(const char * nam);
+struct group * vgetgrent(void);
+int vgrdb(char *arg, ...);
+RET_SETGRENT vsetgrent(void);
+void vendgrent(void);
+
+void copymkdir(char const * dir, char const * skel, mode_t mode, uid_t uid, gid_t gid);
+void rm_r(char const * dir, uid_t uid);
+int extendline(char **buf, int *buflen, int needed);
+int extendarray(char ***buf, int *buflen, int needed);
+__END_DECLS
+
+#define PWBUFSZ 1024
+
+#endif /* !_PWUPD_H */
diff --git a/usr.sbin/pw/rm_r.c b/usr.sbin/pw/rm_r.c
new file mode 100644
index 0000000..4ad590b
--- /dev/null
+++ b/usr.sbin/pw/rm_r.c
@@ -0,0 +1,75 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "pwupd.h"
+
+void
+rm_r(char const * dir, uid_t uid)
+{
+ DIR *d = opendir(dir);
+
+ if (d != NULL) {
+ struct dirent *e;
+ struct stat st;
+ char file[MAXPATHLEN];
+
+ while ((e = readdir(d)) != NULL) {
+ if (strcmp(e->d_name, ".") != 0 && strcmp(e->d_name, "..") != 0) {
+ sprintf(file, "%s/%s", dir, e->d_name);
+ if (lstat(file, &st) == 0) { /* Need symlinks, not
+ * linked file */
+ if (S_ISDIR(st.st_mode)) /* Directory - recurse */
+ rm_r(file, uid);
+ else {
+ if (S_ISLNK(st.st_mode) || st.st_uid == uid)
+ remove(file);
+ }
+ }
+ }
+ }
+ closedir(d);
+ if (lstat(dir, &st) == 0) {
+ if (S_ISLNK(st.st_mode))
+ remove(dir);
+ else if (st.st_uid == uid)
+ rmdir(dir);
+ }
+ }
+}
diff --git a/usr.sbin/pwd_mkdb/Makefile b/usr.sbin/pwd_mkdb/Makefile
new file mode 100644
index 0000000..c3aaeb3
--- /dev/null
+++ b/usr.sbin/pwd_mkdb/Makefile
@@ -0,0 +1,13 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../lib/libc/gen # for pw_scan.c
+
+PROG= pwd_mkdb
+MAN= pwd_mkdb.8
+SRCS= pw_scan.c pwd_mkdb.c
+
+WARNS?= 4
+CFLAGS+= -I${.CURDIR}/../../lib/libc/gen # for pw_scan.h
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pwd_mkdb/pwd_mkdb.8 b/usr.sbin/pwd_mkdb/pwd_mkdb.8
new file mode 100644
index 0000000..1265c59
--- /dev/null
+++ b/usr.sbin/pwd_mkdb/pwd_mkdb.8
@@ -0,0 +1,183 @@
+.\" 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.
+.\"
+.\" @(#)pwd_mkdb.8 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\"
+.Dd February 28, 2005
+.Dt PWD_MKDB 8
+.Os
+.Sh NAME
+.Nm pwd_mkdb
+.Nd "generate the password databases"
+.Sh SYNOPSIS
+.Nm
+.Op Fl BCiLNp
+.Op Fl d Ar directory
+.Op Fl s Ar cachesize
+.Op Fl u Ar username
+.Ar file
+.Sh DESCRIPTION
+The
+.Nm
+utility creates
+.Xr db 3
+style secure and insecure databases for the specified file.
+These databases are then installed into
+.Pa /etc/spwd.db
+and
+.Pa /etc/pwd.db
+respectively.
+The file is installed into
+.Pa /etc/master.passwd .
+The file must be in the correct format (see
+.Xr passwd 5 ) .
+It is important to note that the format used in this system is
+different from the historic Version 7 style format.
+.Pp
+The options are as follows:
+.Bl -tag -width flag
+.It Fl B
+Store data in big-endian format.
+.It Fl C
+Check if the password file is in the correct format.
+Do not
+change, add, or remove any files.
+.It Fl L
+Store data in little-endian format.
+.It Fl N
+Tell
+.Nm
+to exit with an error if it cannot obtain a lock on the file.
+By default,
+we block waiting for a lock on the source file.
+The lock is held through
+the rebuilding of the database.
+.It Fl p
+Create a Version 7 style password file and install it into
+.Pa /etc/passwd .
+.It Fl i
+Ignore locking failure of the
+.Pa master.passwd
+file.
+This option is intended to be used to build password files in
+the release process over NFS where no contention can happen.
+A non-default directory must also be specified with the
+.Fl d
+option for locking to be ignored.
+Other use of this option is strongly discouraged.
+.It Fl d Ar directory
+Store databases into specified destination directory instead of
+.Pa /etc .
+.It Fl u Ar username
+Only update the record for the specified user.
+Utilities that
+operate on a single user can use this option to avoid the
+overhead of rebuilding the entire database.
+.It Fl s Ar cachesize
+Specify in megabytes the size of the memory cache used by the
+hashing library.
+On systems with a large user base, a small cache
+size can lead to prohibitively long database file rebuild times.
+As a rough guide, the memory usage of
+.Nm
+in megabytes will be a little bit more than twice the figure
+specified here.
+The default is 2 megabytes.
+.El
+.Pp
+The two databases differ in that the secure version contains the user's
+encrypted password and the insecure version has an asterisk (``*'')
+.Pp
+The databases are used by the C library password routines (see
+.Xr getpwent 3 ) .
+.Pp
+The
+.Nm
+utility exits zero on success, non-zero on failure.
+.Sh ENVIRONMENT
+If the
+.Ev PW_SCAN_BIG_IDS
+environment variable is set,
+.Nm
+will suppress the warning messages that are
+normally generated for large user and group IDs.
+Such IDs can cause serious problems with software
+that makes assumptions about the values of IDs.
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /etc/pwd.db
+The insecure password database file.
+.It Pa /etc/pwd.db.tmp
+A temporary file.
+.It Pa /etc/spwd.db
+The secure password database file.
+.It Pa /etc/spwd.db.tmp
+A temporary file.
+.It Pa /etc/master.passwd
+The current password file.
+.It Pa /etc/passwd
+A Version 7 format password file.
+.El
+.Sh COMPATIBILITY
+Previous versions of the system had a program similar to
+.Nm ,
+.Xr mkpasswd 8 ,
+which built
+.Xr dbm 3
+style databases for the password file but depended on the calling programs
+to install them.
+The program was renamed in order that previous users of the program
+not be surprised by the changes in functionality.
+.Sh SEE ALSO
+.Xr chpass 1 ,
+.Xr passwd 1 ,
+.Xr db 3 ,
+.Xr getpwent 3 ,
+.Xr passwd 5 ,
+.Xr vipw 8
+.Sh BUGS
+Because of the necessity for atomic update of the password files,
+.Nm
+uses
+.Xr rename 2
+to install them.
+This, however, requires that the file specified on the command line live
+on the same file system as the
+.Pa /etc
+directory.
+.Pp
+There are the obvious races with multiple people running
+.Nm
+on different password files at the same time.
+The front-ends to
+.Nm ,
+.Xr chpass 1 ,
+.Xr passwd 1
+and
+.Xr vipw 8 ,
+handle the locking necessary to avoid this problem.
diff --git a/usr.sbin/pwd_mkdb/pwd_mkdb.c b/usr.sbin/pwd_mkdb/pwd_mkdb.c
new file mode 100644
index 0000000..3c8ca25
--- /dev/null
+++ b/usr.sbin/pwd_mkdb/pwd_mkdb.c
@@ -0,0 +1,758 @@
+/*-
+ * Copyright (c) 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.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1991, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)pwd_mkdb.c 8.5 (Berkeley) 4/20/94";
+#endif /* not lint */
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/endian.h>
+#include <sys/stat.h>
+#include <arpa/inet.h>
+
+#include <db.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pw_scan.h"
+
+#define INSECURE 1
+#define SECURE 2
+#define PERM_INSECURE (S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)
+#define PERM_SECURE (S_IRUSR|S_IWUSR)
+#define LEGACY_VERSION(x) _PW_VERSIONED(x, 3)
+#define CURRENT_VERSION(x) _PW_VERSIONED(x, 4)
+
+HASHINFO openinfo = {
+ 4096, /* bsize */
+ 32, /* ffactor */
+ 256, /* nelem */
+ 2048 * 1024, /* cachesize */
+ NULL, /* hash() */
+ BYTE_ORDER /* lorder */
+};
+
+static enum state { FILE_INSECURE, FILE_SECURE, FILE_ORIG } clean;
+static struct passwd pwd; /* password structure */
+static char *pname; /* password file name */
+static char prefix[MAXPATHLEN];
+
+static int is_comment; /* flag for comments */
+static char line[LINE_MAX];
+
+void cleanup(void);
+void error(const char *);
+void cp(char *, char *, mode_t mode);
+void mv(char *, char *);
+int scan(FILE *, struct passwd *);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ static char verskey[] = _PWD_VERSION_KEY;
+ char version = _PWD_CURRENT_VERSION;
+ DB *dp, *sdp, *pw_db;
+ DBT data, sdata, key;
+ FILE *fp, *oldfp;
+ sigset_t set;
+ int ch, cnt, ypcnt, makeold, tfd, yp_enabled = 0;
+ unsigned int len;
+ uint32_t store;
+ const char *t;
+ char *p;
+ char buf[MAX(MAXPATHLEN, LINE_MAX * 2)], tbuf[1024];
+ char sbuf[MAX(MAXPATHLEN, LINE_MAX * 2)];
+ char buf2[MAXPATHLEN];
+ char sbuf2[MAXPATHLEN];
+ char *username;
+ u_int method, methoduid;
+ int Cflag, dflag, iflag;
+ int nblock = 0;
+
+ iflag = dflag = Cflag = 0;
+ strcpy(prefix, _PATH_PWD);
+ makeold = 0;
+ username = NULL;
+ oldfp = NULL;
+ while ((ch = getopt(argc, argv, "BCLNd:ips:u:v")) != -1)
+ switch(ch) {
+ case 'B': /* big-endian output */
+ openinfo.lorder = BIG_ENDIAN;
+ break;
+ case 'C': /* verify only */
+ Cflag = 1;
+ break;
+ case 'L': /* little-endian output */
+ openinfo.lorder = LITTLE_ENDIAN;
+ break;
+ case 'N': /* do not wait for lock */
+ nblock = LOCK_NB; /* will fail if locked */
+ break;
+ case 'd':
+ dflag++;
+ strlcpy(prefix, optarg, sizeof(prefix));
+ break;
+ case 'i':
+ iflag++;
+ break;
+ case 'p': /* create V7 "file.orig" */
+ makeold = 1;
+ break;
+ case 's': /* change default cachesize */
+ openinfo.cachesize = atoi(optarg) * 1024 * 1024;
+ break;
+ case 'u': /* only update this record */
+ username = optarg;
+ break;
+ case 'v': /* backward compatible */
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1 || (username && (*username == '+' || *username == '-')))
+ usage();
+
+ /*
+ * This could be changed to allow the user to interrupt.
+ * Probably not worth the effort.
+ */
+ sigemptyset(&set);
+ sigaddset(&set, SIGTSTP);
+ sigaddset(&set, SIGHUP);
+ sigaddset(&set, SIGINT);
+ sigaddset(&set, SIGQUIT);
+ sigaddset(&set, SIGTERM);
+ (void)sigprocmask(SIG_BLOCK, &set, (sigset_t *)NULL);
+
+ /* We don't care what the user wants. */
+ (void)umask(0);
+
+ pname = *argv;
+
+ /*
+ * Open and lock the original password file. We have to check
+ * the hardlink count after we get the lock to handle any potential
+ * unlink/rename race.
+ *
+ * This lock is necessary when someone runs pwd_mkdb manually, directly
+ * on master.passwd, to handle the case where a user might try to
+ * change his password while pwd_mkdb is running.
+ */
+ for (;;) {
+ struct stat st;
+
+ if (!(fp = fopen(pname, "r")))
+ error(pname);
+ if (flock(fileno(fp), LOCK_EX|nblock) < 0 && !(dflag && iflag))
+ error("flock");
+ if (fstat(fileno(fp), &st) < 0)
+ error(pname);
+ if (st.st_nlink != 0)
+ break;
+ fclose(fp);
+ fp = NULL;
+ }
+
+ /* check only if password database is valid */
+ if (Cflag) {
+ for (cnt = 1; scan(fp, &pwd); ++cnt);
+ exit(0);
+ }
+
+ /* Open the temporary insecure password database. */
+ (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
+ (void)snprintf(sbuf, sizeof(sbuf), "%s/%s.tmp", prefix, _SMP_DB);
+ if (username) {
+ int use_version;
+
+ (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _MP_DB);
+ (void)snprintf(sbuf2, sizeof(sbuf2), "%s/%s", prefix, _SMP_DB);
+
+ clean = FILE_INSECURE;
+ cp(buf2, buf, PERM_INSECURE);
+ dp = dbopen(buf,
+ O_RDWR|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo);
+ if (dp == NULL)
+ error(buf);
+
+ clean = FILE_SECURE;
+ cp(sbuf2, sbuf, PERM_SECURE);
+ sdp = dbopen(sbuf,
+ O_RDWR|O_EXCL, PERM_SECURE, DB_HASH, &openinfo);
+ if (sdp == NULL)
+ error(sbuf);
+
+ /*
+ * Do some trouble to check if we should store this users
+ * uid. Don't use getpwnam/getpwuid as that interferes
+ * with NIS.
+ */
+ pw_db = dbopen(_PATH_MP_DB, O_RDONLY, 0, DB_HASH, NULL);
+ if (!pw_db)
+ error(_MP_DB);
+
+ key.data = verskey;
+ key.size = sizeof(verskey)-1;
+ if ((pw_db->get)(pw_db, &key, &data, 0) == 0)
+ use_version = *(unsigned char *)data.data;
+ else
+ use_version = 3;
+ buf[0] = _PW_VERSIONED(_PW_KEYBYNAME, use_version);
+ len = strlen(username);
+
+ /* Only check that username fits in buffer */
+ memmove(buf + 1, username, MIN(len, sizeof(buf) - 1));
+ key.data = (u_char *)buf;
+ key.size = len + 1;
+ if ((pw_db->get)(pw_db, &key, &data, 0) == 0) {
+ p = (char *)data.data;
+
+ /* jump over pw_name and pw_passwd, to get to pw_uid */
+ while (*p++)
+ ;
+ while (*p++)
+ ;
+
+ buf[0] = _PW_VERSIONED(_PW_KEYBYUID, use_version);
+ memmove(buf + 1, p, sizeof(store));
+ key.data = (u_char *)buf;
+ key.size = sizeof(store) + 1;
+
+ if ((pw_db->get)(pw_db, &key, &data, 0) == 0) {
+ /* First field of data.data holds pw_pwname */
+ if (!strcmp(data.data, username))
+ methoduid = 0;
+ else
+ methoduid = R_NOOVERWRITE;
+ } else {
+ methoduid = R_NOOVERWRITE;
+ }
+ } else {
+ methoduid = R_NOOVERWRITE;
+ }
+ if ((pw_db->close)(pw_db))
+ error("close pw_db");
+ method = 0;
+ } else {
+ dp = dbopen(buf,
+ O_RDWR|O_CREAT|O_EXCL, PERM_INSECURE, DB_HASH, &openinfo);
+ if (dp == NULL)
+ error(buf);
+ clean = FILE_INSECURE;
+
+ sdp = dbopen(sbuf,
+ O_RDWR|O_CREAT|O_EXCL, PERM_SECURE, DB_HASH, &openinfo);
+ if (sdp == NULL)
+ error(sbuf);
+ clean = FILE_SECURE;
+
+ method = R_NOOVERWRITE;
+ methoduid = R_NOOVERWRITE;
+ }
+
+ /*
+ * Open file for old password file. Minor trickiness -- don't want to
+ * chance the file already existing, since someone (stupidly) might
+ * still be using this for permission checking. So, open it first and
+ * fdopen the resulting fd. The resulting file should be readable by
+ * everyone.
+ */
+ if (makeold) {
+ (void)snprintf(buf, sizeof(buf), "%s.orig", pname);
+ if ((tfd = open(buf,
+ O_WRONLY|O_CREAT|O_EXCL, PERM_INSECURE)) < 0)
+ error(buf);
+ if ((oldfp = fdopen(tfd, "w")) == NULL)
+ error(buf);
+ clean = FILE_ORIG;
+ }
+
+ /*
+ * The databases actually contain three copies of the original data.
+ * Each password file entry is converted into a rough approximation
+ * of a ``struct passwd'', with the strings placed inline. This
+ * object is then stored as the data for three separate keys. The
+ * first key * is the pw_name field prepended by the _PW_KEYBYNAME
+ * character. The second key is the pw_uid field prepended by the
+ * _PW_KEYBYUID character. The third key is the line number in the
+ * original file prepended by the _PW_KEYBYNUM character. (The special
+ * characters are prepended to ensure that the keys do not collide.)
+ */
+ /* In order to transition this file into a machine-independent
+ * form, we have to change the format of entries. However, since
+ * older binaries will still expect the old MD format entries, we
+ * create those as usual and use versioned tags for the new entries.
+ */
+ if (username == NULL) {
+ /* Do not add the VERSION tag when updating a single
+ * user. When operating on `old format' databases, this
+ * would result in applications `seeing' only the updated
+ * entries.
+ */
+ key.data = verskey;
+ key.size = sizeof(verskey)-1;
+ data.data = &version;
+ data.size = 1;
+ if ((dp->put)(dp, &key, &data, 0) == -1)
+ error("put");
+ if ((dp->put)(sdp, &key, &data, 0) == -1)
+ error("put");
+ }
+ ypcnt = 1;
+ data.data = (u_char *)buf;
+ sdata.data = (u_char *)sbuf;
+ key.data = (u_char *)tbuf;
+ for (cnt = 1; scan(fp, &pwd); ++cnt) {
+ if (!is_comment &&
+ (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-'))
+ yp_enabled = 1;
+ if (is_comment)
+ --cnt;
+#define COMPACT(e) t = e; while ((*p++ = *t++));
+#define SCALAR(e) store = htonl((uint32_t)(e)); \
+ memmove(p, &store, sizeof(store)); \
+ p += sizeof(store);
+#define LSCALAR(e) store = HTOL((uint32_t)(e)); \
+ memmove(p, &store, sizeof(store)); \
+ p += sizeof(store);
+#define HTOL(e) (openinfo.lorder == BYTE_ORDER ? \
+ (uint32_t)(e) : \
+ bswap32((uint32_t)(e)))
+ if (!is_comment &&
+ (!username || (strcmp(username, pwd.pw_name) == 0))) {
+ /* Create insecure data. */
+ p = buf;
+ COMPACT(pwd.pw_name);
+ COMPACT("*");
+ SCALAR(pwd.pw_uid);
+ SCALAR(pwd.pw_gid);
+ SCALAR(pwd.pw_change);
+ COMPACT(pwd.pw_class);
+ COMPACT(pwd.pw_gecos);
+ COMPACT(pwd.pw_dir);
+ COMPACT(pwd.pw_shell);
+ SCALAR(pwd.pw_expire);
+ SCALAR(pwd.pw_fields);
+ data.size = p - buf;
+
+ /* Create secure data. */
+ p = sbuf;
+ COMPACT(pwd.pw_name);
+ COMPACT(pwd.pw_passwd);
+ SCALAR(pwd.pw_uid);
+ SCALAR(pwd.pw_gid);
+ SCALAR(pwd.pw_change);
+ COMPACT(pwd.pw_class);
+ COMPACT(pwd.pw_gecos);
+ COMPACT(pwd.pw_dir);
+ COMPACT(pwd.pw_shell);
+ SCALAR(pwd.pw_expire);
+ SCALAR(pwd.pw_fields);
+ sdata.size = p - sbuf;
+
+ /* Store insecure by name. */
+ tbuf[0] = CURRENT_VERSION(_PW_KEYBYNAME);
+ len = strlen(pwd.pw_name);
+ memmove(tbuf + 1, pwd.pw_name, len);
+ key.size = len + 1;
+ if ((dp->put)(dp, &key, &data, method) == -1)
+ error("put");
+
+ /* Store insecure by number. */
+ tbuf[0] = CURRENT_VERSION(_PW_KEYBYNUM);
+ store = htonl(cnt);
+ memmove(tbuf + 1, &store, sizeof(store));
+ key.size = sizeof(store) + 1;
+ if ((dp->put)(dp, &key, &data, method) == -1)
+ error("put");
+
+ /* Store insecure by uid. */
+ tbuf[0] = CURRENT_VERSION(_PW_KEYBYUID);
+ store = htonl(pwd.pw_uid);
+ memmove(tbuf + 1, &store, sizeof(store));
+ key.size = sizeof(store) + 1;
+ if ((dp->put)(dp, &key, &data, methoduid) == -1)
+ error("put");
+
+ /* Store secure by name. */
+ tbuf[0] = CURRENT_VERSION(_PW_KEYBYNAME);
+ len = strlen(pwd.pw_name);
+ memmove(tbuf + 1, pwd.pw_name, len);
+ key.size = len + 1;
+ if ((sdp->put)(sdp, &key, &sdata, method) == -1)
+ error("put");
+
+ /* Store secure by number. */
+ tbuf[0] = CURRENT_VERSION(_PW_KEYBYNUM);
+ store = htonl(cnt);
+ memmove(tbuf + 1, &store, sizeof(store));
+ key.size = sizeof(store) + 1;
+ if ((sdp->put)(sdp, &key, &sdata, method) == -1)
+ error("put");
+
+ /* Store secure by uid. */
+ tbuf[0] = CURRENT_VERSION(_PW_KEYBYUID);
+ store = htonl(pwd.pw_uid);
+ memmove(tbuf + 1, &store, sizeof(store));
+ key.size = sizeof(store) + 1;
+ if ((sdp->put)(sdp, &key, &sdata, methoduid) == -1)
+ error("put");
+
+ /* Store insecure and secure special plus and special minus */
+ if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-') {
+ tbuf[0] = CURRENT_VERSION(_PW_KEYYPBYNUM);
+ store = htonl(ypcnt);
+ memmove(tbuf + 1, &store, sizeof(store));
+ ypcnt++;
+ key.size = sizeof(store) + 1;
+ if ((dp->put)(dp, &key, &data, method) == -1)
+ error("put");
+ if ((sdp->put)(sdp, &key, &sdata, method) == -1)
+ error("put");
+ }
+
+ /* Create insecure data. (legacy version) */
+ p = buf;
+ COMPACT(pwd.pw_name);
+ COMPACT("*");
+ LSCALAR(pwd.pw_uid);
+ LSCALAR(pwd.pw_gid);
+ LSCALAR(pwd.pw_change);
+ COMPACT(pwd.pw_class);
+ COMPACT(pwd.pw_gecos);
+ COMPACT(pwd.pw_dir);
+ COMPACT(pwd.pw_shell);
+ LSCALAR(pwd.pw_expire);
+ LSCALAR(pwd.pw_fields);
+ data.size = p - buf;
+
+ /* Create secure data. (legacy version) */
+ p = sbuf;
+ COMPACT(pwd.pw_name);
+ COMPACT(pwd.pw_passwd);
+ LSCALAR(pwd.pw_uid);
+ LSCALAR(pwd.pw_gid);
+ LSCALAR(pwd.pw_change);
+ COMPACT(pwd.pw_class);
+ COMPACT(pwd.pw_gecos);
+ COMPACT(pwd.pw_dir);
+ COMPACT(pwd.pw_shell);
+ LSCALAR(pwd.pw_expire);
+ LSCALAR(pwd.pw_fields);
+ sdata.size = p - sbuf;
+
+ /* Store insecure by name. */
+ tbuf[0] = LEGACY_VERSION(_PW_KEYBYNAME);
+ len = strlen(pwd.pw_name);
+ memmove(tbuf + 1, pwd.pw_name, len);
+ key.size = len + 1;
+ if ((dp->put)(dp, &key, &data, method) == -1)
+ error("put");
+
+ /* Store insecure by number. */
+ tbuf[0] = LEGACY_VERSION(_PW_KEYBYNUM);
+ store = HTOL(cnt);
+ memmove(tbuf + 1, &store, sizeof(store));
+ key.size = sizeof(store) + 1;
+ if ((dp->put)(dp, &key, &data, method) == -1)
+ error("put");
+
+ /* Store insecure by uid. */
+ tbuf[0] = LEGACY_VERSION(_PW_KEYBYUID);
+ store = HTOL(pwd.pw_uid);
+ memmove(tbuf + 1, &store, sizeof(store));
+ key.size = sizeof(store) + 1;
+ if ((dp->put)(dp, &key, &data, methoduid) == -1)
+ error("put");
+
+ /* Store secure by name. */
+ tbuf[0] = LEGACY_VERSION(_PW_KEYBYNAME);
+ len = strlen(pwd.pw_name);
+ memmove(tbuf + 1, pwd.pw_name, len);
+ key.size = len + 1;
+ if ((sdp->put)(sdp, &key, &sdata, method) == -1)
+ error("put");
+
+ /* Store secure by number. */
+ tbuf[0] = LEGACY_VERSION(_PW_KEYBYNUM);
+ store = HTOL(cnt);
+ memmove(tbuf + 1, &store, sizeof(store));
+ key.size = sizeof(store) + 1;
+ if ((sdp->put)(sdp, &key, &sdata, method) == -1)
+ error("put");
+
+ /* Store secure by uid. */
+ tbuf[0] = LEGACY_VERSION(_PW_KEYBYUID);
+ store = HTOL(pwd.pw_uid);
+ memmove(tbuf + 1, &store, sizeof(store));
+ key.size = sizeof(store) + 1;
+ if ((sdp->put)(sdp, &key, &sdata, methoduid) == -1)
+ error("put");
+
+ /* Store insecure and secure special plus and special minus */
+ if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-') {
+ tbuf[0] = LEGACY_VERSION(_PW_KEYYPBYNUM);
+ store = HTOL(ypcnt);
+ memmove(tbuf + 1, &store, sizeof(store));
+ ypcnt++;
+ key.size = sizeof(store) + 1;
+ if ((dp->put)(dp, &key, &data, method) == -1)
+ error("put");
+ if ((sdp->put)(sdp, &key, &sdata, method) == -1)
+ error("put");
+ }
+ }
+ /* Create original format password file entry */
+ if (is_comment && makeold){ /* copy comments */
+ if (fprintf(oldfp, "%s\n", line) < 0)
+ error("write old");
+ } else if (makeold) {
+ char uidstr[20];
+ char gidstr[20];
+
+ snprintf(uidstr, sizeof(uidstr), "%u", pwd.pw_uid);
+ snprintf(gidstr, sizeof(gidstr), "%u", pwd.pw_gid);
+
+ if (fprintf(oldfp, "%s:*:%s:%s:%s:%s:%s\n",
+ pwd.pw_name, pwd.pw_fields & _PWF_UID ? uidstr : "",
+ pwd.pw_fields & _PWF_GID ? gidstr : "",
+ pwd.pw_gecos, pwd.pw_dir, pwd.pw_shell) < 0)
+ error("write old");
+ }
+ }
+ /* If YP enabled, set flag. */
+ if (yp_enabled) {
+ buf[0] = yp_enabled + 2;
+ data.size = 1;
+ key.size = 1;
+ tbuf[0] = CURRENT_VERSION(_PW_KEYYPENABLED);
+ if ((dp->put)(dp, &key, &data, method) == -1)
+ error("put");
+ if ((sdp->put)(sdp, &key, &data, method) == -1)
+ error("put");
+ tbuf[0] = LEGACY_VERSION(_PW_KEYYPENABLED);
+ key.size = 1;
+ if ((dp->put)(dp, &key, &data, method) == -1)
+ error("put");
+ if ((sdp->put)(sdp, &key, &data, method) == -1)
+ error("put");
+ }
+
+ if ((dp->close)(dp) == -1)
+ error("close");
+ if ((sdp->close)(sdp) == -1)
+ error("close");
+ if (makeold) {
+ (void)fflush(oldfp);
+ if (fclose(oldfp) == EOF)
+ error("close old");
+ }
+
+ /* Set master.passwd permissions, in case caller forgot. */
+ (void)fchmod(fileno(fp), S_IRUSR|S_IWUSR);
+
+ /* Install as the real password files. */
+ (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
+ (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _MP_DB);
+ mv(buf, buf2);
+ (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB);
+ (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _SMP_DB);
+ mv(buf, buf2);
+ if (makeold) {
+ (void)snprintf(buf2, sizeof(buf2), "%s/%s", prefix, _PASSWD);
+ (void)snprintf(buf, sizeof(buf), "%s.orig", pname);
+ mv(buf, buf2);
+ }
+ /*
+ * Move the master password LAST -- chpass(1), passwd(1) and vipw(8)
+ * all use flock(2) on it to block other incarnations of themselves.
+ * The rename means that everything is unlocked, as the original file
+ * can no longer be accessed.
+ */
+ (void)snprintf(buf, sizeof(buf), "%s/%s", prefix, _MASTERPASSWD);
+ mv(pname, buf);
+
+ /*
+ * Close locked password file after rename()
+ */
+ if (fclose(fp) == EOF)
+ error("close fp");
+
+ exit(0);
+}
+
+int
+scan(FILE *fp, struct passwd *pw)
+{
+ static int lcnt;
+ size_t len;
+ char *p;
+
+ p = fgetln(fp, &len);
+ if (p == NULL)
+ return (0);
+ ++lcnt;
+ /*
+ * ``... if I swallow anything evil, put your fingers down my
+ * throat...''
+ * -- The Who
+ */
+ if (len > 0 && p[len - 1] == '\n')
+ len--;
+ if (len >= sizeof(line) - 1) {
+ warnx("line #%d too long", lcnt);
+ goto fmt;
+ }
+ memcpy(line, p, len);
+ line[len] = '\0';
+
+ /*
+ * Ignore comments: ^[ \t]*#
+ */
+ for (p = line; *p != '\0'; p++)
+ if (*p != ' ' && *p != '\t')
+ break;
+ if (*p == '#' || *p == '\0') {
+ is_comment = 1;
+ return(1);
+ } else
+ is_comment = 0;
+
+ if (!__pw_scan(line, pw, _PWSCAN_WARN|_PWSCAN_MASTER)) {
+ warnx("at line #%d", lcnt);
+fmt: errno = EFTYPE; /* XXX */
+ error(pname);
+ }
+
+ return (1);
+}
+
+void
+cp(char *from, char *to, mode_t mode)
+{
+ static char buf[MAXBSIZE];
+ int from_fd, rcount, to_fd, wcount;
+
+ if ((from_fd = open(from, O_RDONLY, 0)) < 0)
+ error(from);
+ if ((to_fd = open(to, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0)
+ error(to);
+ while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
+ wcount = write(to_fd, buf, rcount);
+ if (rcount != wcount || wcount == -1) {
+ int sverrno = errno;
+
+ (void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
+ errno = sverrno;
+ error(buf);
+ }
+ }
+ if (rcount < 0) {
+ int sverrno = errno;
+
+ (void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
+ errno = sverrno;
+ error(buf);
+ }
+}
+
+
+void
+mv(char *from, char *to)
+{
+ char buf[MAXPATHLEN];
+
+ if (rename(from, to)) {
+ int sverrno = errno;
+ (void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
+ errno = sverrno;
+ error(buf);
+ }
+}
+
+void
+error(const char *name)
+{
+
+ warn("%s", name);
+ cleanup();
+ exit(1);
+}
+
+void
+cleanup(void)
+{
+ char buf[MAXPATHLEN];
+
+ switch(clean) {
+ case FILE_ORIG:
+ (void)snprintf(buf, sizeof(buf), "%s.orig", pname);
+ (void)unlink(buf);
+ /* FALLTHROUGH */
+ case FILE_SECURE:
+ (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB);
+ (void)unlink(buf);
+ /* FALLTHROUGH */
+ case FILE_INSECURE:
+ (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
+ (void)unlink(buf);
+ }
+}
+
+static void
+usage(void)
+{
+
+ (void)fprintf(stderr,
+"usage: pwd_mkdb [-BCiLNp] [-d directory] [-s cachesize] [-u username] file\n");
+ exit(1);
+}
diff --git a/usr.sbin/quot/Makefile b/usr.sbin/quot/Makefile
new file mode 100644
index 0000000..07bcc7e
--- /dev/null
+++ b/usr.sbin/quot/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= quot
+MAN= quot.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/quot/quot.8 b/usr.sbin/quot/quot.8
new file mode 100644
index 0000000..5451f63
--- /dev/null
+++ b/usr.sbin/quot/quot.8
@@ -0,0 +1,110 @@
+.\" Copyright (C) 1994 Wolfgang Solfrank.
+.\" Copyright (C) 1994 TooLs GmbH.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 TooLs GmbH.
+.\" 4. The name of TooLs GmbH may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+.\" PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+.\" OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 8, 1994
+.Dt QUOT 8
+.Os
+.Sh NAME
+.Nm quot
+.Nd display disk space occupied by each user
+.Sh SYNOPSIS
+.Nm
+.Op Fl acfhknv
+.Op Ar filesystem ...
+.Sh DESCRIPTION
+The
+.Nm
+utility
+is used to gather statistics about the disk usage for each local user.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl a
+Include statistics for all mounted file systems.
+.It Fl c
+Display three columns containing number of blocks per file,
+number of files in this category, and aggregate total of
+blocks in files with this or lower size.
+.It Fl f
+For each user, display count of files and space occupied.
+.It Fl h
+Estimate the number of blocks in each file based on its size.
+Despite that this does not give the correct results (it does not
+account for the holes in files), this option is not any faster
+and thus is discouraged.
+.It Fl k
+Force the numbers to be reported in kilobyte counts.
+By default, all sizes are reported in 512-byte block counts.
+.It Fl n
+Given a list of inodes (plus some optional data on each line)
+in the standard input, for each file print out the owner (plus
+the remainder of the input line).
+This is traditionally used
+in the pipe:
+.Bd -literal -offset indent
+.\" ncheck filesystem | sort +0n | quot -n filesystem
+ls -i | sed -e 's,^ *,,' | sort -k 1n | quot -n filesystem
+.Ed
+.Pp
+to get a report of files and their owners.
+.It Fl v
+In addition to the default output, display the number of files
+not accessed within 30, 60 and 90 days.
+.El
+.Sh ENVIRONMENT
+.Bl -tag -width BLOCKSIZE
+.It Ev BLOCKSIZE
+If the environment variable
+.Ev BLOCKSIZE
+is set, and the
+.Fl k
+option is not specified, the block counts will be displayed in units of that
+size block.
+.El
+.Sh SEE ALSO
+.Xr df 1 ,
+.Xr quota 1 ,
+.Xr getmntinfo 3 ,
+.Xr fstab 5 ,
+.Xr mount 8
+.Sh HISTORY
+This implementation of
+.Nm
+is by
+.An Wolfgang Solfrank
+/ TooLs GmbH.
+.Sh BUGS
+.Xr ncheck
+(which would be a lot more useful than
+.Nm ls Fl i
+in the example above)
+does not exist in
+.Fx .
diff --git a/usr.sbin/quot/quot.c b/usr.sbin/quot/quot.c
new file mode 100644
index 0000000..4da58ba
--- /dev/null
+++ b/usr.sbin/quot/quot.c
@@ -0,0 +1,644 @@
+/*
+ * Copyright (C) 1991, 1994 Wolfgang Solfrank.
+ * Copyright (C) 1991, 1994 TooLs GmbH.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 TooLs GmbH.
+ * 4. The name of TooLs GmbH may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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/stdint.h>
+#include <sys/mount.h>
+#include <sys/disklabel.h>
+#include <sys/time.h>
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <fstab.h>
+#include <errno.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* some flags of what to do: */
+static char estimate;
+static char count;
+static char unused;
+static void (*func)(int, struct fs *, char *);
+static long blocksize;
+static char *header;
+static int headerlen;
+
+static union dinode *get_inode(int, struct fs *, ino_t);
+static int virtualblocks(struct fs *, union dinode *);
+static int isfree(struct fs *, union dinode *);
+static void inituser(void);
+static void usrrehash(void);
+static struct user *user(uid_t);
+static int cmpusers(const void *, const void *);
+static void uses(uid_t, daddr_t, time_t);
+static void initfsizes(void);
+static void dofsizes(int, struct fs *, char *);
+static void douser(int, struct fs *, char *);
+static void donames(int, struct fs *, char *);
+static void usage(void);
+static void quot(char *, char *);
+
+/*
+ * Original BSD quot doesn't round to number of frags/blocks,
+ * doesn't account for indirection blocks and gets it totally
+ * wrong if the size is a multiple of the blocksize.
+ * The new code always counts the number of 512 byte blocks
+ * instead of the number of kilobytes and converts them to
+ * kByte when done (on request).
+ *
+ * Due to the size of modern disks, we must cast intermediate
+ * values to 64 bits to prevent potential overflows.
+ */
+#ifdef COMPAT
+#define SIZE(n) (n)
+#else
+#define SIZE(n) ((int)(((quad_t)(n) * 512 + blocksize - 1)/blocksize))
+#endif
+
+#define INOCNT(fs) ((fs)->fs_ipg)
+#define INOSZ(fs) \
+ (((fs)->fs_magic == FS_UFS1_MAGIC ? sizeof(struct ufs1_dinode) : \
+ sizeof(struct ufs2_dinode)) * INOCNT(fs))
+
+union dinode {
+ struct ufs1_dinode dp1;
+ struct ufs2_dinode dp2;
+};
+#define DIP(fs, dp, field) \
+ (((fs)->fs_magic == FS_UFS1_MAGIC) ? \
+ (dp)->dp1.field : (dp)->dp2.field)
+
+static union dinode *
+get_inode(int fd, struct fs *super, ino_t ino)
+{
+ static caddr_t ipbuf;
+ static struct cg *cgp;
+ static ino_t last;
+ static int cg;
+ struct ufs2_dinode *di2;
+
+ if (fd < 0) { /* flush cache */
+ if (ipbuf) {
+ free(ipbuf);
+ ipbuf = 0;
+ if (super != NULL && super->fs_magic == FS_UFS2_MAGIC) {
+ free(cgp);
+ cgp = 0;
+ }
+ }
+ return 0;
+ }
+
+ if (!ipbuf || ino < last || ino >= last + INOCNT(super)) {
+ if (super->fs_magic == FS_UFS2_MAGIC &&
+ (!cgp || cg != ino_to_cg(super, ino))) {
+ cg = ino_to_cg(super, ino);
+ if (!cgp && !(cgp = malloc(super->fs_cgsize)))
+ errx(1, "allocate cg");
+ if (lseek(fd, (off_t)cgtod(super, cg) << super->fs_fshift, 0) < 0)
+ err(1, "lseek cg");
+ if (read(fd, cgp, super->fs_cgsize) != super->fs_cgsize)
+ err(1, "read cg");
+ if (!cg_chkmagic(cgp))
+ errx(1, "cg has bad magic");
+ }
+ if (!ipbuf
+ && !(ipbuf = malloc(INOSZ(super))))
+ errx(1, "allocate inodes");
+ last = (ino / INOCNT(super)) * INOCNT(super);
+ if (lseek(fd, (off_t)ino_to_fsba(super, last) << super->fs_fshift, 0) < (off_t)0
+ || read(fd, ipbuf, INOSZ(super)) != (ssize_t)INOSZ(super))
+ err(1, "read inodes");
+ }
+
+ if (super->fs_magic == FS_UFS1_MAGIC)
+ return ((union dinode *)
+ &((struct ufs1_dinode *)ipbuf)[ino % INOCNT(super)]);
+ di2 = &((struct ufs2_dinode *)ipbuf)[ino % INOCNT(super)];
+ /* If the inode is unused, it might be unallocated too, so zero it. */
+ if (isclr(cg_inosused(cgp), ino % super->fs_ipg))
+ bzero(di2, sizeof (*di2));
+ return ((union dinode *)di2);
+}
+
+#ifdef COMPAT
+#define actualblocks(fs, dp) (DIP(fs, dp, di_blocks) / 2)
+#else
+#define actualblocks(fs, dp) DIP(fs, dp, di_blocks)
+#endif
+
+static int virtualblocks(struct fs *super, union dinode *dp)
+{
+ off_t nblk, sz;
+
+ sz = DIP(super, dp, di_size);
+#ifdef COMPAT
+ if (lblkno(super,sz) >= NDADDR) {
+ nblk = blkroundup(super,sz);
+ if (sz == nblk)
+ nblk += super->fs_bsize;
+ }
+
+ return sz / 1024;
+
+#else /* COMPAT */
+
+ if (lblkno(super,sz) >= NDADDR) {
+ nblk = blkroundup(super,sz);
+ sz = lblkno(super,nblk);
+ sz = (sz - NDADDR + NINDIR(super) - 1) / NINDIR(super);
+ while (sz > 0) {
+ nblk += sz * super->fs_bsize;
+ /* sz - 1 rounded up */
+ sz = (sz - 1 + NINDIR(super) - 1) / NINDIR(super);
+ }
+ } else
+ nblk = fragroundup(super,sz);
+
+ return nblk / 512;
+#endif /* COMPAT */
+}
+
+static int
+isfree(struct fs *super, union dinode *dp)
+{
+#ifdef COMPAT
+ return (DIP(super, dp, di_mode) & IFMT) == 0;
+#else /* COMPAT */
+
+ switch (DIP(super, dp, di_mode) & IFMT) {
+ case IFIFO:
+ case IFLNK: /* should check FASTSYMLINK? */
+ case IFDIR:
+ case IFREG:
+ return 0;
+ case IFCHR:
+ case IFBLK:
+ case IFSOCK:
+ case IFWHT:
+ case 0:
+ return 1;
+ default:
+ errx(1, "unknown IFMT 0%o", DIP(super, dp, di_mode) & IFMT);
+ }
+#endif
+}
+
+static struct user {
+ uid_t uid;
+ char *name;
+ daddr_t space;
+ long count;
+ daddr_t spc30;
+ daddr_t spc60;
+ daddr_t spc90;
+} *users;
+static int nusers;
+
+static void
+inituser(void)
+{
+ int i;
+ struct user *usr;
+
+ if (!nusers) {
+ nusers = 8;
+ if (!(users =
+ (struct user *)calloc(nusers,sizeof(struct user))))
+ errx(1, "allocate users");
+ } else {
+ for (usr = users, i = nusers; --i >= 0; usr++) {
+ usr->space = usr->spc30 = usr->spc60 = usr->spc90 = 0;
+ usr->count = 0;
+ }
+ }
+}
+
+static void
+usrrehash(void)
+{
+ int i;
+ struct user *usr, *usrn;
+ struct user *svusr;
+
+ svusr = users;
+ nusers <<= 1;
+ if (!(users = (struct user *)calloc(nusers,sizeof(struct user))))
+ errx(1, "allocate users");
+ for (usr = svusr, i = nusers >> 1; --i >= 0; usr++) {
+ for (usrn = users + (usr->uid&(nusers - 1)); usrn->name;
+ usrn--) {
+ if (usrn <= users)
+ usrn = users + nusers;
+ }
+ *usrn = *usr;
+ }
+}
+
+static struct user *
+user(uid_t uid)
+{
+ struct user *usr;
+ int i;
+ struct passwd *pwd;
+
+ while (1) {
+ for (usr = users + (uid&(nusers - 1)), i = nusers; --i >= 0;
+ usr--) {
+ if (!usr->name) {
+ usr->uid = uid;
+
+ if (!(pwd = getpwuid(uid))) {
+ if ((usr->name = (char *)malloc(7)))
+ sprintf(usr->name,"#%d",uid);
+ } else {
+ if ((usr->name = (char *)
+ malloc(strlen(pwd->pw_name) + 1)))
+ strcpy(usr->name,pwd->pw_name);
+ }
+ if (!usr->name)
+ errx(1, "allocate users");
+
+ return usr;
+
+ } else if (usr->uid == uid)
+ return usr;
+
+ if (usr <= users)
+ usr = users + nusers;
+ }
+ usrrehash();
+ }
+}
+
+static int
+cmpusers(const void *v1, const void *v2)
+{
+ const struct user *u1, *u2;
+ u1 = (const struct user *)v1;
+ u2 = (const struct user *)v2;
+
+ return u2->space - u1->space;
+}
+
+#define sortusers(users) (qsort((users),nusers,sizeof(struct user), \
+ cmpusers))
+
+static void
+uses(uid_t uid, daddr_t blks, time_t act)
+{
+ static time_t today;
+ struct user *usr;
+
+ if (!today)
+ time(&today);
+
+ usr = user(uid);
+ usr->count++;
+ usr->space += blks;
+
+ if (today - act > 90L * 24L * 60L * 60L)
+ usr->spc90 += blks;
+ if (today - act > 60L * 24L * 60L * 60L)
+ usr->spc60 += blks;
+ if (today - act > 30L * 24L * 60L * 60L)
+ usr->spc30 += blks;
+}
+
+#ifdef COMPAT
+#define FSZCNT 500
+#else
+#define FSZCNT 512
+#endif
+struct fsizes {
+ struct fsizes *fsz_next;
+ daddr_t fsz_first, fsz_last;
+ ino_t fsz_count[FSZCNT];
+ daddr_t fsz_sz[FSZCNT];
+} *fsizes;
+
+static void
+initfsizes(void)
+{
+ struct fsizes *fp;
+ int i;
+
+ for (fp = fsizes; fp; fp = fp->fsz_next) {
+ for (i = FSZCNT; --i >= 0;) {
+ fp->fsz_count[i] = 0;
+ fp->fsz_sz[i] = 0;
+ }
+ }
+}
+
+static void
+dofsizes(int fd, struct fs *super, char *name)
+{
+ ino_t inode, maxino;
+ union dinode *dp;
+ daddr_t sz, ksz;
+ struct fsizes *fp, **fsp;
+ int i;
+
+ maxino = super->fs_ncg * super->fs_ipg - 1;
+#ifdef COMPAT
+ if (!(fsizes = (struct fsizes *)malloc(sizeof(struct fsizes))))
+ errx(1, "allocate fsize structure");
+#endif /* COMPAT */
+ for (inode = 0; inode < maxino; inode++) {
+ errno = 0;
+ if ((dp = get_inode(fd,super,inode))
+#ifdef COMPAT
+ && ((DIP(super, dp, di_mode) & IFMT) == IFREG
+ || (DIP(super, dp, di_mode) & IFMT) == IFDIR)
+#else /* COMPAT */
+ && !isfree(super, dp)
+#endif /* COMPAT */
+ ) {
+ sz = estimate ? virtualblocks(super, dp) :
+ actualblocks(super, dp);
+#ifdef COMPAT
+ if (sz >= FSZCNT) {
+ fsizes->fsz_count[FSZCNT-1]++;
+ fsizes->fsz_sz[FSZCNT-1] += sz;
+ } else {
+ fsizes->fsz_count[sz]++;
+ fsizes->fsz_sz[sz] += sz;
+ }
+#else /* COMPAT */
+ ksz = SIZE(sz);
+ for (fsp = &fsizes; (fp = *fsp); fsp = &fp->fsz_next) {
+ if (ksz < fp->fsz_last)
+ break;
+ }
+ if (!fp || ksz < fp->fsz_first) {
+ if (!(fp = (struct fsizes *)
+ malloc(sizeof(struct fsizes))))
+ errx(1, "allocate fsize structure");
+ fp->fsz_next = *fsp;
+ *fsp = fp;
+ fp->fsz_first = (ksz / FSZCNT) * FSZCNT;
+ fp->fsz_last = fp->fsz_first + FSZCNT;
+ for (i = FSZCNT; --i >= 0;) {
+ fp->fsz_count[i] = 0;
+ fp->fsz_sz[i] = 0;
+ }
+ }
+ fp->fsz_count[ksz % FSZCNT]++;
+ fp->fsz_sz[ksz % FSZCNT] += sz;
+#endif /* COMPAT */
+ } else if (errno) {
+ err(1, "%s", name);
+ }
+ }
+ sz = 0;
+ for (fp = fsizes; fp; fp = fp->fsz_next) {
+ for (i = 0; i < FSZCNT; i++) {
+ if (fp->fsz_count[i])
+ printf("%jd\t%jd\t%d\n",
+ (intmax_t)(fp->fsz_first + i),
+ (intmax_t)fp->fsz_count[i],
+ SIZE(sz += fp->fsz_sz[i]));
+ }
+ }
+}
+
+static void
+douser(int fd, struct fs *super, char *name)
+{
+ ino_t inode, maxino;
+ struct user *usr, *usrs;
+ union dinode *dp;
+ int n;
+
+ maxino = super->fs_ncg * super->fs_ipg - 1;
+ for (inode = 0; inode < maxino; inode++) {
+ errno = 0;
+ if ((dp = get_inode(fd,super,inode))
+ && !isfree(super, dp))
+ uses(DIP(super, dp, di_uid),
+ estimate ? virtualblocks(super, dp) :
+ actualblocks(super, dp),
+ DIP(super, dp, di_atime));
+ else if (errno) {
+ err(1, "%s", name);
+ }
+ }
+ if (!(usrs = (struct user *)malloc(nusers * sizeof(struct user))))
+ errx(1, "allocate users");
+ bcopy(users,usrs,nusers * sizeof(struct user));
+ sortusers(usrs);
+ for (usr = usrs, n = nusers; --n >= 0 && usr->count; usr++) {
+ printf("%5d",SIZE(usr->space));
+ if (count)
+ printf("\t%5ld",usr->count);
+ printf("\t%-8s",usr->name);
+ if (unused)
+ printf("\t%5d\t%5d\t%5d",
+ SIZE(usr->spc30),
+ SIZE(usr->spc60),
+ SIZE(usr->spc90));
+ printf("\n");
+ }
+ free(usrs);
+}
+
+static void
+donames(int fd, struct fs *super, char *name)
+{
+ int c;
+ ino_t inode;
+ ino_t maxino;
+ union dinode *dp;
+
+ maxino = super->fs_ncg * super->fs_ipg - 1;
+ /* first skip the name of the filesystem */
+ while ((c = getchar()) != EOF && (c < '0' || c > '9'))
+ while ((c = getchar()) != EOF && c != '\n');
+ ungetc(c,stdin);
+ while (scanf("%u",&inode) == 1) {
+ if (inode > maxino) {
+ warnx("illegal inode %d",inode);
+ return;
+ }
+ errno = 0;
+ if ((dp = get_inode(fd,super,inode))
+ && !isfree(super, dp)) {
+ printf("%s\t",user(DIP(super, dp, di_uid))->name);
+ /* now skip whitespace */
+ while ((c = getchar()) == ' ' || c == '\t');
+ /* and print out the remainder of the input line */
+ while (c != EOF && c != '\n') {
+ putchar(c);
+ c = getchar();
+ }
+ putchar('\n');
+ } else {
+ if (errno) {
+ err(1, "%s", name);
+ }
+ /* skip this line */
+ while ((c = getchar()) != EOF && c != '\n');
+ }
+ if (c == EOF)
+ break;
+ }
+}
+
+static void
+usage(void)
+{
+#ifdef COMPAT
+ fprintf(stderr,"usage: quot [-nfcvha] [filesystem ...]\n");
+#else /* COMPAT */
+ fprintf(stderr,"usage: quot [-acfhknv] [filesystem ...]\n");
+#endif /* COMPAT */
+ exit(1);
+}
+
+/*
+ * Possible superblock locations ordered from most to least likely.
+ */
+static int sblock_try[] = SBLOCKSEARCH;
+static char superblock[SBLOCKSIZE];
+
+void
+quot(char *name, char *mp)
+{
+ int i, fd;
+ struct fs *fs;
+
+ get_inode(-1, NULL, 0); /* flush cache */
+ inituser();
+ initfsizes();
+ if ((fd = open(name,0)) < 0) {
+ warn("%s", name);
+ close(fd);
+ return;
+ }
+ for (i = 0; sblock_try[i] != -1; i++) {
+ if (lseek(fd, sblock_try[i], 0) != sblock_try[i]) {
+ close(fd);
+ return;
+ }
+ if (read(fd, superblock, SBLOCKSIZE) != SBLOCKSIZE) {
+ close(fd);
+ return;
+ }
+ fs = (struct fs *)superblock;
+ if ((fs->fs_magic == FS_UFS1_MAGIC ||
+ (fs->fs_magic == FS_UFS2_MAGIC &&
+ fs->fs_sblockloc == sblock_try[i])) &&
+ fs->fs_bsize <= MAXBSIZE &&
+ fs->fs_bsize >= sizeof(struct fs))
+ break;
+ }
+ if (sblock_try[i] == -1) {
+ warnx("%s: not a BSD filesystem",name);
+ close(fd);
+ return;
+ }
+ printf("%s:",name);
+ if (mp)
+ printf(" (%s)",mp);
+ putchar('\n');
+ (*func)(fd, fs, name);
+ close(fd);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char all = 0;
+ struct statfs *mp;
+ struct fstab *fs;
+ int cnt;
+
+ func = douser;
+#ifndef COMPAT
+ header = getbsize(&headerlen,&blocksize);
+#endif
+ while (--argc > 0 && **++argv == '-') {
+ while (*++*argv) {
+ switch (**argv) {
+ case 'n':
+ func = donames;
+ break;
+ case 'c':
+ func = dofsizes;
+ break;
+ case 'a':
+ all = 1;
+ break;
+ case 'f':
+ count = 1;
+ break;
+ case 'h':
+ estimate = 1;
+ break;
+#ifndef COMPAT
+ case 'k':
+ blocksize = 1024;
+ break;
+#endif /* COMPAT */
+ case 'v':
+ unused = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+ }
+ if (all) {
+ cnt = getmntinfo(&mp,MNT_NOWAIT);
+ for (; --cnt >= 0; mp++) {
+ if (!strncmp(mp->f_fstypename, "ufs", MFSNAMELEN))
+ quot(mp->f_mntfromname, mp->f_mntonname);
+ }
+ }
+ while (--argc >= 0) {
+ if ((fs = getfsfile(*argv)) != NULL)
+ quot(fs->fs_spec, 0);
+ else
+ quot(*argv,0);
+ argv++;
+ }
+ return 0;
+}
diff --git a/usr.sbin/quotaon/Makefile b/usr.sbin/quotaon/Makefile
new file mode 100644
index 0000000..3f88bd9
--- /dev/null
+++ b/usr.sbin/quotaon/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= quotaon
+LINKS= ${BINDIR}/quotaon ${BINDIR}/quotaoff
+MAN= quotaon.8
+MLINKS= quotaon.8 quotaoff.8
+
+WARNS?= 4
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/quotaon/quotaon.8 b/usr.sbin/quotaon/quotaon.8
new file mode 100644
index 0000000..08e8d11
--- /dev/null
+++ b/usr.sbin/quotaon/quotaon.8
@@ -0,0 +1,137 @@
+.\" 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.
+.\"
+.\" @(#)quotaon.8 8.2 (Berkeley) 12/11/93
+.\" $FreeBSD$
+.\"
+.Dd December 11, 1993
+.Dt QUOTAON 8
+.Os
+.Sh NAME
+.Nm quotaon ,
+.Nm quotaoff
+.Nd turn file system quotas on and off
+.Sh SYNOPSIS
+.Nm
+.Op Fl g
+.Op Fl u
+.Op Fl v
+.Ar filesystem Ar ...
+.Nm
+.Op Fl g
+.Op Fl u
+.Op Fl v
+.Fl a
+.Nm quotaoff
+.Op Fl g
+.Op Fl u
+.Op Fl v
+.Ar filesystem Ar ...
+.Nm quotaoff
+.Op Fl g
+.Op Fl u
+.Op Fl v
+.Fl a
+.Sh DESCRIPTION
+The
+.Nm
+utility
+announces to the system that disk quotas should be enabled on one or more
+file systems.
+The
+.Nm quotaoff
+utility announces to the system that the specified
+file systems should have any disk quotas turned off.
+The file systems specified must have entries in
+.Pa /etc/fstab
+and be mounted.
+The
+.Nm
+utility expects each file system to have 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 .
+By default both user and group quotas are enabled.
+.Pp
+Available options:
+.Bl -tag -width indent
+.It Fl a
+If supplied in place of any file system names,
+.Nm Ns / Ns Nm quotaoff
+will enable/disable 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 enabled.
+.It Fl g
+Only group quotas listed in
+.Pa /etc/fstab
+should be enabled/disabled.
+.It Fl u
+Only user quotas listed in
+.Pa /etc/fstab
+should be enabled/disabled.
+.It Fl v
+Cause
+.Nm
+and
+.Nm quotaoff
+to print a message for each file system where quotas are turned on or off.
+.El
+.Pp
+Specifying both
+.Fl g
+and
+.Fl u
+is equivalent to the default.
+.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
+file system table
+.El
+.Sh SEE ALSO
+.Xr quota 1 ,
+.Xr quotactl 2 ,
+.Xr fstab 5 ,
+.Xr edquota 8 ,
+.Xr quotacheck 8 ,
+.Xr repquota 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.2 .
diff --git a/usr.sbin/quotaon/quotaon.c b/usr.sbin/quotaon/quotaon.c
new file mode 100644
index 0000000..314f596
--- /dev/null
+++ b/usr.sbin/quotaon/quotaon.c
@@ -0,0 +1,267 @@
+/*
+ * 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[] = "@(#)quotaon.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Turn quota on/off for a filesystem.
+ */
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/mount.h>
+#include <ufs/ufs/quota.h>
+#include <err.h>
+#include <fstab.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+const char *qfname = QUOTAFILENAME;
+const char *qfextension[] = INITQFNAMES;
+
+int aflag; /* all filesystems */
+int gflag; /* operate on group quotas */
+int uflag; /* operate on user quotas */
+int vflag; /* verbose */
+
+int hasquota(struct fstab *, int, char **);
+int oneof(char *, char *[], int);
+int quotaonoff(struct fstab *fs, int, int, char *);
+int readonly(struct fstab *);
+static void usage(void);
+
+int
+main(int argc, char **argv)
+{
+ struct fstab *fs;
+ char *qfnp, *whoami;
+ long argnum, done = 0;
+ int ch, i, offmode = 0, errs = 0;
+
+ whoami = rindex(*argv, '/') + 1;
+ if (whoami == (char *)1)
+ whoami = *argv;
+ if (strcmp(whoami, "quotaoff") == 0)
+ offmode++;
+ else if (strcmp(whoami, "quotaon") != 0)
+ errx(1, "name must be quotaon or quotaoff");
+ while ((ch = getopt(argc, argv, "avug")) != -1) {
+ switch(ch) {
+ case 'a':
+ aflag++;
+ break;
+ case 'g':
+ gflag++;
+ break;
+ case 'u':
+ uflag++;
+ break;
+ case 'v':
+ vflag++;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc <= 0 && !aflag)
+ usage();
+ if (!gflag && !uflag) {
+ gflag++;
+ uflag++;
+ }
+ setfsent();
+ while ((fs = getfsent()) != NULL) {
+ if (strcmp(fs->fs_vfstype, "ufs") ||
+ strcmp(fs->fs_type, FSTAB_RW))
+ continue;
+ if (aflag) {
+ if (gflag && hasquota(fs, GRPQUOTA, &qfnp))
+ errs += quotaonoff(fs, offmode, GRPQUOTA, qfnp);
+ if (uflag && hasquota(fs, USRQUOTA, &qfnp))
+ errs += quotaonoff(fs, offmode, USRQUOTA, qfnp);
+ continue;
+ }
+ if ((argnum = oneof(fs->fs_file, argv, argc)) >= 0 ||
+ (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) {
+ done |= 1 << argnum;
+ if (gflag && hasquota(fs, GRPQUOTA, &qfnp))
+ errs += quotaonoff(fs, offmode, GRPQUOTA, qfnp);
+ if (uflag && hasquota(fs, USRQUOTA, &qfnp))
+ errs += quotaonoff(fs, offmode, USRQUOTA, qfnp);
+ }
+ }
+ endfsent();
+ for (i = 0; i < argc; i++)
+ if ((done & (1 << i)) == 0)
+ warnx("%s not found in fstab", argv[i]);
+ exit(errs);
+}
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "%s\n%s\n%s\n%s\n",
+ "usage: quotaon [-g] [-u] [-v] -a",
+ " quotaon [-g] [-u] [-v] filesystem ...",
+ " quotaoff [-g] [-u] [-v] -a",
+ " quotaoff [-g] [-u] [-v] filesystem ...");
+ exit(1);
+}
+
+int
+quotaonoff(fs, offmode, type, qfpathname)
+ register struct fstab *fs;
+ int offmode, type;
+ char *qfpathname;
+{
+
+ if (strcmp(fs->fs_file, "/") && readonly(fs))
+ return (1);
+ if (offmode) {
+ if (quotactl(fs->fs_file, QCMD(Q_QUOTAOFF, type), 0, 0) < 0) {
+ warn("%s", fs->fs_file);
+ return (1);
+ }
+ if (vflag)
+ printf("%s: quotas turned off\n", fs->fs_file);
+ return (0);
+ }
+ if (quotactl(fs->fs_file, QCMD(Q_QUOTAON, type), 0, qfpathname) < 0) {
+ warnx("using %s on", qfpathname);
+ warn("%s", fs->fs_file);
+ return (1);
+ }
+ if (vflag)
+ printf("%s: %s quotas turned on with data file %s\n",
+ fs->fs_file, qfextension[type], qfpathname);
+ return (0);
+}
+
+/*
+ * Check to see if target appears in list of size cnt.
+ */
+int
+oneof(char *target, char *list[], int cnt)
+{
+ int i;
+
+ for (i = 0; i < cnt; i++)
+ if (strcmp(target, list[i]) == 0)
+ return (i);
+ return (-1);
+}
+
+/*
+ * Check to see if a particular quota is to be enabled.
+ */
+int
+hasquota(struct fstab *fs, int type, char **qfnamep)
+{
+ char *opt;
+ char *cp;
+ struct statfs sfb;
+ 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, '=')))
+ *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;
+ }
+ if (statfs(fs->fs_file, &sfb) != 0) {
+ warn("cannot statfs mount point %s", fs->fs_file);
+ return (0);
+ }
+ if (strcmp(fs->fs_file, sfb.f_mntonname)) {
+ warnx("%s not mounted for %s quotas", fs->fs_file,
+ type == USRQUOTA ? "user" : "group");
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * Verify filesystem is mounted and not readonly.
+ */
+int
+readonly(struct fstab *fs)
+{
+ struct statfs fsbuf;
+
+ if (statfs(fs->fs_file, &fsbuf) < 0 ||
+ strcmp(fsbuf.f_mntonname, fs->fs_file) ||
+ strcmp(fsbuf.f_mntfromname, fs->fs_spec)) {
+ printf("%s: not mounted\n", fs->fs_file);
+ return (1);
+ }
+ if (fsbuf.f_flags & MNT_RDONLY) {
+ printf("%s: mounted read-only\n", fs->fs_file);
+ return (1);
+ }
+ return (0);
+}
diff --git a/usr.sbin/rarpd/Makefile b/usr.sbin/rarpd/Makefile
new file mode 100644
index 0000000..7f34457
--- /dev/null
+++ b/usr.sbin/rarpd/Makefile
@@ -0,0 +1,11 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $FreeBSD$
+
+PROG= rarpd
+MAN= rarpd.8
+
+WARNS?= 3
+# This breaks with format strings returned by expand_syslog_m().. argh!
+#FORMAT_AUDIT?= 1
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rarpd/rarpd.8 b/usr.sbin/rarpd/rarpd.8
new file mode 100644
index 0000000..8552b69
--- /dev/null
+++ b/usr.sbin/rarpd/rarpd.8
@@ -0,0 +1,148 @@
+.\" 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: (1) source code distributions
+.\" retain the above copyright notice and this paragraph in its entirety, (2)
+.\" distributions including binary code include the above copyright notice and
+.\" this paragraph in its entirety in the documentation or other materials
+.\" provided with the distribution. Neither the name of
+.\" the University nor the names of its contributors may be used to endorse
+.\" or promote products derived from this software without specific prior
+.\" written permission.
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+.\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd November 16, 2001
+.Dt RARPD 8
+.Os
+.Sh NAME
+.Nm rarpd
+.Nd reverse ARP daemon
+.Sh SYNOPSIS
+.Nm
+.Fl a
+.Op Fl dfsv
+.Op Fl t Ar directory
+.Nm
+.Op Fl dfsv
+.Op Fl t Ar directory
+.Ar interface
+.Sh DESCRIPTION
+The
+.Nm
+utility services Reverse ARP requests on the Ethernet connected to
+.Ar interface .
+Upon receiving a request,
+.Nm
+maps the target hardware address to an IP address via its name, which
+must be present in both the
+.Xr ethers 5
+and
+.Xr hosts 5
+databases.
+If a host does not exist in both databases, the translation cannot
+proceed and a reply will not be sent.
+.Pp
+By default, a request is honored only if the server
+(i.e., the host that
+.Nm
+is running on)
+can "boot" the target; that is, a file or directory matching the glob
+.Pa /tftpboot/\fIipaddr\fP*
+exists, where
+.Em ipaddr
+is the target IP address in hex.
+For example, the IP address 204.216.27.18 will be replied to if any of
+.Pa /tftpboot/CCD81B12 ,
+.Pa /tftpboot/CCD81B12.SUN3 ,
+or
+.Pa /tftpboot/CCD81B12-boot
+exist.
+This requirement can be overridden with the
+.Fl s
+flag (see below).
+.Pp
+In normal operation,
+.Nm
+forks a copy of itself and runs in the background.
+Anomalies and errors are reported via
+.Xr syslog 3 .
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl a
+Listen on all the Ethernets attached to the system.
+If
+.Fl a
+is omitted, an interface must be specified.
+.It Fl d
+If
+.Fl f
+is also specified,
+.Nm
+logs messages to
+.Em stdout
+and
+.Em stderr
+instead of via
+.Xr syslog 3 .
+.It Fl f
+Run in the foreground.
+.It Fl s
+Supply a response to any RARP request for which an ethernet to IP address
+mapping exists; do not depend on the existence of
+.Pa /tftpboot/\fIipaddr\fP* .
+.It Fl t
+Supply an alternate tftp root directory to
+.Pa /tftpboot ,
+similar to the
+.Fl s
+option of
+.Xr tftpd 8 .
+This permits
+.Nm
+to selectively respond to RARP requests, but use an alternate directory
+for IP checking.
+.It Fl v
+Enable verbose syslogging.
+.El
+.Sh FILES
+.Bl -tag -width /etc/ethers -compact
+.It Pa /etc/ethers
+.It Pa /etc/hosts
+.It Pa /tftpboot
+.El
+.Sh SEE ALSO
+.Xr bpf 4
+.Rs
+.%A "Finlayson, R."
+.%A "Mann, T."
+.%A "Mogul, J.C."
+.%A "Theimer, M."
+.%T "RFC 903: Reverse Address Resolution Protocol"
+.%D "June 1984"
+.%O "4 p"
+.Re
+.Sh AUTHORS
+.An -nosplit
+.An Craig Leres Aq leres@ee.lbl.gov
+and
+.An Steven McCanne Aq mccanne@ee.lbl.gov .
+Lawrence Berkeley Laboratory, University of California, Berkeley, CA.
+.Sh BUGS
+The
+.Nm
+utility can depend on the DNS to resolve the name discovered from
+.Pa /etc/ethers .
+If this name is not in the DNS but is in
+.Pa /etc/hosts ,
+the DNS lookup can cause a delayed RARP response, so in this situation
+it is recommended to configure
+.Xr nsswitch.conf 5
+to read
+.Pa /etc/hosts
+first.
diff --git a/usr.sbin/rarpd/rarpd.c b/usr.sbin/rarpd/rarpd.c
new file mode 100644
index 0000000..a500dd5
--- /dev/null
+++ b/usr.sbin/rarpd/rarpd.c
@@ -0,0 +1,951 @@
+/*
+ * Copyright (c) 1990, 1991, 1992, 1993, 1996
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution
+ *
+ * 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.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1990, 1991, 1992, 1993, 1996\n\
+The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * rarpd - Reverse ARP Daemon
+ *
+ * Usage: rarpd -a [-dfsv] [-t directory] [hostname]
+ * rarpd [-dfsv] [-t directory] interface [hostname]
+ *
+ * 'hostname' is optional solely for backwards compatibility with Sun's rarpd.
+ * Currently, the argument is ignored.
+ */
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <net/bpf.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <arpa/inet.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <ifaddrs.h>
+#include <netdb.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+/* Cast a struct sockaddr to a struct sockaddr_in */
+#define SATOSIN(sa) ((struct sockaddr_in *)(sa))
+
+#ifndef TFTP_DIR
+#define TFTP_DIR "/tftpboot"
+#endif
+
+#define ARPSECS (20 * 60) /* as per code in netinet/if_ether.c */
+#define REVARP_REQUEST ARPOP_REVREQUEST
+#define REVARP_REPLY ARPOP_REVREPLY
+
+/*
+ * The structure for each interface.
+ */
+struct if_info {
+ struct if_info *ii_next;
+ int ii_fd; /* BPF file descriptor */
+ in_addr_t ii_ipaddr; /* IP address */
+ in_addr_t ii_netmask; /* subnet or net mask */
+ u_char ii_eaddr[ETHER_ADDR_LEN]; /* ethernet address */
+ char ii_ifname[IF_NAMESIZE];
+};
+
+/*
+ * The list of all interfaces that are being listened to. rarp_loop()
+ * "selects" on the descriptors in this list.
+ */
+struct if_info *iflist;
+
+int verbose; /* verbose messages */
+const char *tftp_dir = TFTP_DIR; /* tftp directory */
+
+int dflag; /* messages to stdout/stderr, not syslog(3) */
+int sflag; /* ignore /tftpboot */
+
+static u_char zero[6];
+
+static int bpf_open(void);
+static in_addr_t choose_ipaddr(in_addr_t **, in_addr_t, in_addr_t);
+static char *eatoa(u_char *);
+static int expand_syslog_m(const char *fmt, char **newfmt);
+static void init(char *);
+static void init_one(struct ifaddrs *, char *, int);
+static char *intoa(in_addr_t);
+static in_addr_t ipaddrtonetmask(in_addr_t);
+static void logmsg(int, const char *, ...) __printflike(2, 3);
+static int rarp_bootable(in_addr_t);
+static int rarp_check(u_char *, u_int);
+static void rarp_loop(void);
+static int rarp_open(char *);
+static void rarp_process(struct if_info *, u_char *, u_int);
+static void rarp_reply(struct if_info *, struct ether_header *,
+ in_addr_t, u_int);
+static void update_arptab(u_char *, in_addr_t);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ int op;
+ char *ifname, *hostname, *name;
+
+ int aflag = 0; /* listen on "all" interfaces */
+ int fflag = 0; /* don't fork */
+
+ if ((name = strrchr(argv[0], '/')) != NULL)
+ ++name;
+ else
+ name = argv[0];
+ if (*name == '-')
+ ++name;
+
+ /*
+ * All error reporting is done through syslog, unless -d is specified
+ */
+ openlog(name, LOG_PID | LOG_CONS, LOG_DAEMON);
+
+ opterr = 0;
+ while ((op = getopt(argc, argv, "adfst:v")) != -1)
+ switch (op) {
+ case 'a':
+ ++aflag;
+ break;
+
+ case 'd':
+ ++dflag;
+ break;
+
+ case 'f':
+ ++fflag;
+ break;
+
+ case 's':
+ ++sflag;
+ break;
+
+ case 't':
+ tftp_dir = optarg;
+ break;
+
+ case 'v':
+ ++verbose;
+ break;
+
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ argc -= optind;
+ argv += optind;
+
+ ifname = (aflag == 0) ? argv[0] : NULL;
+ hostname = ifname ? argv[1] : argv[0];
+
+ if ((aflag && ifname) || (!aflag && ifname == NULL))
+ usage();
+
+ init(ifname);
+
+ if (!fflag) {
+ if (daemon(0,0)) {
+ logmsg(LOG_ERR, "cannot fork");
+ exit(1);
+ }
+ }
+ rarp_loop();
+ return(0);
+}
+
+/*
+ * Add to the interface list.
+ */
+static void
+init_one(struct ifaddrs *ifa, char *target, int pass1)
+{
+ struct if_info *ii, *ii2;
+ struct sockaddr_dl *ll;
+ int family;
+
+ family = ifa->ifa_addr->sa_family;
+ switch (family) {
+ case AF_INET:
+ if (pass1)
+ /* Consider only AF_LINK during pass1. */
+ return;
+ /* FALLTHROUGH */
+ case AF_LINK:
+ if (!(ifa->ifa_flags & IFF_UP) ||
+ (ifa->ifa_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)))
+ return;
+ break;
+ default:
+ return;
+ }
+
+ /* Don't bother going any further if not the target interface */
+ if (target != NULL && strcmp(ifa->ifa_name, target) != 0)
+ return;
+
+ /* Look for interface in list */
+ for (ii = iflist; ii != NULL; ii = ii->ii_next)
+ if (strcmp(ifa->ifa_name, ii->ii_ifname) == 0)
+ break;
+
+ if (pass1 && ii != NULL)
+ /* We've already seen that interface once. */
+ return;
+
+ /* Allocate a new one if not found */
+ if (ii == NULL) {
+ ii = (struct if_info *)malloc(sizeof(*ii));
+ if (ii == NULL) {
+ logmsg(LOG_ERR, "malloc: %m");
+ exit(1);
+ }
+ bzero(ii, sizeof(*ii));
+ ii->ii_fd = -1;
+ strlcpy(ii->ii_ifname, ifa->ifa_name, sizeof(ii->ii_ifname));
+ ii->ii_next = iflist;
+ iflist = ii;
+ } else if (!pass1 && ii->ii_ipaddr != 0) {
+ /*
+ * Second AF_INET definition for that interface: clone
+ * the existing one, and work on that cloned one.
+ * This must be another IP address for this interface,
+ * so avoid killing the previous configuration.
+ */
+ ii2 = (struct if_info *)malloc(sizeof(*ii2));
+ if (ii2 == NULL) {
+ logmsg(LOG_ERR, "malloc: %m");
+ exit(1);
+ }
+ memcpy(ii2, ii, sizeof(*ii2));
+ ii2->ii_fd = -1;
+ ii2->ii_next = iflist;
+ iflist = ii2;
+
+ ii = ii2;
+ }
+
+ switch (family) {
+ case AF_INET:
+ ii->ii_ipaddr = SATOSIN(ifa->ifa_addr)->sin_addr.s_addr;
+ ii->ii_netmask = SATOSIN(ifa->ifa_netmask)->sin_addr.s_addr;
+ if (ii->ii_netmask == 0)
+ ii->ii_netmask = ipaddrtonetmask(ii->ii_ipaddr);
+ if (ii->ii_fd < 0)
+ ii->ii_fd = rarp_open(ii->ii_ifname);
+ break;
+
+ case AF_LINK:
+ ll = (struct sockaddr_dl *)ifa->ifa_addr;
+ if (ll->sdl_type == IFT_ETHER)
+ bcopy(LLADDR(ll), ii->ii_eaddr, 6);
+ break;
+ }
+}
+/*
+ * Initialize all "candidate" interfaces that are in the system
+ * configuration list. A "candidate" is up, not loopback and not
+ * point to point.
+ */
+static void
+init(char *target)
+{
+ struct if_info *ii, *nii, *lii;
+ struct ifaddrs *ifhead, *ifa;
+ int error;
+
+ error = getifaddrs(&ifhead);
+ if (error) {
+ logmsg(LOG_ERR, "getifaddrs: %m");
+ exit(1);
+ }
+ /*
+ * We make two passes over the list we have got. In the first
+ * one, we only collect AF_LINK interfaces, and initialize our
+ * list of interfaces from them. In the second pass, we
+ * collect the actual IP addresses from the AF_INET
+ * interfaces, and allow for the same interface name to appear
+ * multiple times (in case of more than one IP address).
+ */
+ for (ifa = ifhead; ifa != NULL; ifa = ifa->ifa_next)
+ init_one(ifa, target, 1);
+ for (ifa = ifhead; ifa != NULL; ifa = ifa->ifa_next)
+ init_one(ifa, target, 0);
+ freeifaddrs(ifhead);
+
+ /* Throw away incomplete interfaces */
+ lii = NULL;
+ for (ii = iflist; ii != NULL; ii = nii) {
+ nii = ii->ii_next;
+ if (ii->ii_ipaddr == 0 ||
+ bcmp(ii->ii_eaddr, zero, 6) == 0) {
+ if (lii == NULL)
+ iflist = nii;
+ else
+ lii->ii_next = nii;
+ if (ii->ii_fd >= 0)
+ close(ii->ii_fd);
+ free(ii);
+ continue;
+ }
+ lii = ii;
+ }
+
+ /* Verbose stuff */
+ if (verbose)
+ for (ii = iflist; ii != NULL; ii = ii->ii_next)
+ logmsg(LOG_DEBUG, "%s %s 0x%08x %s",
+ ii->ii_ifname, intoa(ntohl(ii->ii_ipaddr)),
+ (in_addr_t)ntohl(ii->ii_netmask), eatoa(ii->ii_eaddr));
+}
+
+static void
+usage(void)
+{
+ (void)fprintf(stderr, "%s\n%s\n",
+ "usage: rarpd -a [-dfsv] [-t directory]",
+ " rarpd [-dfsv] [-t directory] interface");
+ exit(1);
+}
+
+static int
+bpf_open(void)
+{
+ int fd;
+ int n = 0;
+ char device[sizeof "/dev/bpf000"];
+
+ /*
+ * Go through all the minors and find one that isn't in use.
+ */
+ do {
+ (void)sprintf(device, "/dev/bpf%d", n++);
+ fd = open(device, O_RDWR);
+ } while ((fd == -1) && (errno == EBUSY));
+
+ if (fd == -1) {
+ logmsg(LOG_ERR, "%s: %m", device);
+ exit(1);
+ }
+ return fd;
+}
+
+/*
+ * Open a BPF file and attach it to the interface named 'device'.
+ * Set immediate mode, and set a filter that accepts only RARP requests.
+ */
+static int
+rarp_open(char *device)
+{
+ int fd;
+ struct ifreq ifr;
+ u_int dlt;
+ int immediate;
+
+ static struct bpf_insn insns[] = {
+ BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 12),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ETHERTYPE_REVARP, 0, 3),
+ BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 20),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, REVARP_REQUEST, 0, 1),
+ BPF_STMT(BPF_RET|BPF_K, sizeof(struct ether_arp) +
+ sizeof(struct ether_header)),
+ BPF_STMT(BPF_RET|BPF_K, 0),
+ };
+ static struct bpf_program filter = {
+ sizeof insns / sizeof(insns[0]),
+ insns
+ };
+
+ fd = bpf_open();
+ /*
+ * Set immediate mode so packets are processed as they arrive.
+ */
+ immediate = 1;
+ if (ioctl(fd, BIOCIMMEDIATE, &immediate) == -1) {
+ logmsg(LOG_ERR, "BIOCIMMEDIATE: %m");
+ exit(1);
+ }
+ strlcpy(ifr.ifr_name, device, sizeof(ifr.ifr_name));
+ if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) == -1) {
+ logmsg(LOG_ERR, "BIOCSETIF: %m");
+ exit(1);
+ }
+ /*
+ * Check that the data link layer is an Ethernet; this code won't
+ * work with anything else.
+ */
+ if (ioctl(fd, BIOCGDLT, (caddr_t)&dlt) == -1) {
+ logmsg(LOG_ERR, "BIOCGDLT: %m");
+ exit(1);
+ }
+ if (dlt != DLT_EN10MB) {
+ logmsg(LOG_ERR, "%s is not an ethernet", device);
+ exit(1);
+ }
+ /*
+ * Set filter program.
+ */
+ if (ioctl(fd, BIOCSETF, (caddr_t)&filter) == -1) {
+ logmsg(LOG_ERR, "BIOCSETF: %m");
+ exit(1);
+ }
+ return fd;
+}
+
+/*
+ * Perform various sanity checks on the RARP request packet. Return
+ * false on failure and log the reason.
+ */
+static int
+rarp_check(u_char *p, u_int len)
+{
+ struct ether_header *ep = (struct ether_header *)p;
+ struct ether_arp *ap = (struct ether_arp *)(p + sizeof(*ep));
+
+ if (len < sizeof(*ep) + sizeof(*ap)) {
+ logmsg(LOG_ERR, "truncated request, got %u, expected %lu",
+ len, (u_long)(sizeof(*ep) + sizeof(*ap)));
+ return 0;
+ }
+ /*
+ * XXX This test might be better off broken out...
+ */
+ if (ntohs(ep->ether_type) != ETHERTYPE_REVARP ||
+ ntohs(ap->arp_hrd) != ARPHRD_ETHER ||
+ ntohs(ap->arp_op) != REVARP_REQUEST ||
+ ntohs(ap->arp_pro) != ETHERTYPE_IP ||
+ ap->arp_hln != 6 || ap->arp_pln != 4) {
+ logmsg(LOG_DEBUG, "request fails sanity check");
+ return 0;
+ }
+ if (bcmp((char *)&ep->ether_shost, (char *)&ap->arp_sha, 6) != 0) {
+ logmsg(LOG_DEBUG, "ether/arp sender address mismatch");
+ return 0;
+ }
+ if (bcmp((char *)&ap->arp_sha, (char *)&ap->arp_tha, 6) != 0) {
+ logmsg(LOG_DEBUG, "ether/arp target address mismatch");
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * Loop indefinitely listening for RARP requests on the
+ * interfaces in 'iflist'.
+ */
+static void
+rarp_loop(void)
+{
+ u_char *buf, *bp, *ep;
+ int cc, fd;
+ fd_set fds, listeners;
+ int bufsize, maxfd = 0;
+ struct if_info *ii;
+
+ if (iflist == NULL) {
+ logmsg(LOG_ERR, "no interfaces");
+ exit(1);
+ }
+ if (ioctl(iflist->ii_fd, BIOCGBLEN, (caddr_t)&bufsize) == -1) {
+ logmsg(LOG_ERR, "BIOCGBLEN: %m");
+ exit(1);
+ }
+ buf = malloc(bufsize);
+ if (buf == NULL) {
+ logmsg(LOG_ERR, "malloc: %m");
+ exit(1);
+ }
+
+ while (1) {
+ /*
+ * Find the highest numbered file descriptor for select().
+ * Initialize the set of descriptors to listen to.
+ */
+ FD_ZERO(&fds);
+ for (ii = iflist; ii != NULL; ii = ii->ii_next) {
+ FD_SET(ii->ii_fd, &fds);
+ if (ii->ii_fd > maxfd)
+ maxfd = ii->ii_fd;
+ }
+ listeners = fds;
+ if (select(maxfd + 1, &listeners, NULL, NULL, NULL) == -1) {
+ /* Don't choke when we get ptraced */
+ if (errno == EINTR)
+ continue;
+ logmsg(LOG_ERR, "select: %m");
+ exit(1);
+ }
+ for (ii = iflist; ii != NULL; ii = ii->ii_next) {
+ fd = ii->ii_fd;
+ if (!FD_ISSET(fd, &listeners))
+ continue;
+ again:
+ cc = read(fd, (char *)buf, bufsize);
+ /* Don't choke when we get ptraced */
+ if ((cc == -1) && (errno == EINTR))
+ goto again;
+
+ /* Loop through the packet(s) */
+#define bhp ((struct bpf_hdr *)bp)
+ bp = buf;
+ ep = bp + cc;
+ while (bp < ep) {
+ u_int caplen, hdrlen;
+
+ caplen = bhp->bh_caplen;
+ hdrlen = bhp->bh_hdrlen;
+ if (rarp_check(bp + hdrlen, caplen))
+ rarp_process(ii, bp + hdrlen, caplen);
+ bp += BPF_WORDALIGN(hdrlen + caplen);
+ }
+ }
+ }
+#undef bhp
+}
+
+/*
+ * True if this server can boot the host whose IP address is 'addr'.
+ * This check is made by looking in the tftp directory for the
+ * configuration file.
+ */
+static int
+rarp_bootable(in_addr_t addr)
+{
+ struct dirent *dent;
+ DIR *d;
+ char ipname[9];
+ static DIR *dd = NULL;
+
+ (void)sprintf(ipname, "%08X", (in_addr_t)ntohl(addr));
+
+ /*
+ * If directory is already open, rewind it. Otherwise, open it.
+ */
+ if ((d = dd) != NULL)
+ rewinddir(d);
+ else {
+ if (chdir(tftp_dir) == -1) {
+ logmsg(LOG_ERR, "chdir: %s: %m", tftp_dir);
+ exit(1);
+ }
+ d = opendir(".");
+ if (d == NULL) {
+ logmsg(LOG_ERR, "opendir: %m");
+ exit(1);
+ }
+ dd = d;
+ }
+ while ((dent = readdir(d)) != NULL)
+ if (strncmp(dent->d_name, ipname, 8) == 0)
+ return 1;
+ return 0;
+}
+
+/*
+ * Given a list of IP addresses, 'alist', return the first address that
+ * is on network 'net'; 'netmask' is a mask indicating the network portion
+ * of the address.
+ */
+static in_addr_t
+choose_ipaddr(in_addr_t **alist, in_addr_t net, in_addr_t netmask)
+{
+ for (; *alist; ++alist)
+ if ((**alist & netmask) == net)
+ return **alist;
+ return 0;
+}
+
+/*
+ * Answer the RARP request in 'pkt', on the interface 'ii'. 'pkt' has
+ * already been checked for validity. The reply is overlaid on the request.
+ */
+static void
+rarp_process(struct if_info *ii, u_char *pkt, u_int len)
+{
+ struct ether_header *ep;
+ struct hostent *hp;
+ in_addr_t target_ipaddr;
+ char ename[256];
+
+ ep = (struct ether_header *)pkt;
+ /* should this be arp_tha? */
+ if (ether_ntohost(ename, (struct ether_addr *)&ep->ether_shost) != 0) {
+ logmsg(LOG_ERR, "cannot map %s to name",
+ eatoa(ep->ether_shost));
+ return;
+ }
+
+ if ((hp = gethostbyname(ename)) == NULL) {
+ logmsg(LOG_ERR, "cannot map %s to IP address", ename);
+ return;
+ }
+
+ /*
+ * Choose correct address from list.
+ */
+ if (hp->h_addrtype != AF_INET) {
+ logmsg(LOG_ERR, "cannot handle non IP addresses for %s",
+ ename);
+ return;
+ }
+ target_ipaddr = choose_ipaddr((in_addr_t **)hp->h_addr_list,
+ ii->ii_ipaddr & ii->ii_netmask,
+ ii->ii_netmask);
+ if (target_ipaddr == 0) {
+ logmsg(LOG_ERR, "cannot find %s on net %s",
+ ename, intoa(ntohl(ii->ii_ipaddr & ii->ii_netmask)));
+ return;
+ }
+ if (sflag || rarp_bootable(target_ipaddr))
+ rarp_reply(ii, ep, target_ipaddr, len);
+ else if (verbose > 1)
+ logmsg(LOG_INFO, "%s %s at %s DENIED (not bootable)",
+ ii->ii_ifname,
+ eatoa(ep->ether_shost),
+ intoa(ntohl(target_ipaddr)));
+}
+
+/*
+ * Poke the kernel arp tables with the ethernet/ip address combinataion
+ * given. When processing a reply, we must do this so that the booting
+ * host (i.e. the guy running rarpd), won't try to ARP for the hardware
+ * address of the guy being booted (he cannot answer the ARP).
+ */
+struct sockaddr_inarp sin_inarp = {
+ sizeof(struct sockaddr_inarp), AF_INET, 0,
+ {0},
+ {0},
+ 0, 0
+};
+struct sockaddr_dl sin_dl = {
+ sizeof(struct sockaddr_dl), AF_LINK, 0, IFT_ETHER, 0, 6,
+ 0, ""
+};
+struct {
+ struct rt_msghdr rthdr;
+ char rtspace[512];
+} rtmsg;
+
+static void
+update_arptab(u_char *ep, in_addr_t ipaddr)
+{
+ int cc;
+ struct sockaddr_inarp *ar, *ar2;
+ struct sockaddr_dl *ll, *ll2;
+ struct rt_msghdr *rt;
+ int xtype, xindex;
+ static pid_t pid;
+ int r;
+ static int seq;
+
+ r = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (r == -1) {
+ logmsg(LOG_ERR, "raw route socket: %m");
+ exit(1);
+ }
+ pid = getpid();
+
+ ar = &sin_inarp;
+ ar->sin_addr.s_addr = ipaddr;
+ ll = &sin_dl;
+ bcopy(ep, LLADDR(ll), 6);
+
+ /* Get the type and interface index */
+ rt = &rtmsg.rthdr;
+ bzero(rt, sizeof(rtmsg));
+ rt->rtm_version = RTM_VERSION;
+ rt->rtm_addrs = RTA_DST;
+ rt->rtm_type = RTM_GET;
+ rt->rtm_seq = ++seq;
+ ar2 = (struct sockaddr_inarp *)rtmsg.rtspace;
+ bcopy(ar, ar2, sizeof(*ar));
+ rt->rtm_msglen = sizeof(*rt) + sizeof(*ar);
+ errno = 0;
+ if ((write(r, rt, rt->rtm_msglen) == -1) && (errno != ESRCH)) {
+ logmsg(LOG_ERR, "rtmsg get write: %m");
+ close(r);
+ return;
+ }
+ do {
+ cc = read(r, rt, sizeof(rtmsg));
+ } while (cc > 0 && (rt->rtm_seq != seq || rt->rtm_pid != pid));
+ if (cc == -1) {
+ logmsg(LOG_ERR, "rtmsg get read: %m");
+ close(r);
+ return;
+ }
+ ll2 = (struct sockaddr_dl *)((u_char *)ar2 + ar2->sin_len);
+ if (ll2->sdl_family != AF_LINK) {
+ /*
+ * XXX I think this means the ip address is not on a
+ * directly connected network (the family is AF_INET in
+ * this case).
+ */
+ logmsg(LOG_ERR, "bogus link family (%d) wrong net for %08X?\n",
+ ll2->sdl_family, ipaddr);
+ close(r);
+ return;
+ }
+ xtype = ll2->sdl_type;
+ xindex = ll2->sdl_index;
+
+ /* Set the new arp entry */
+ bzero(rt, sizeof(rtmsg));
+ rt->rtm_version = RTM_VERSION;
+ rt->rtm_addrs = RTA_DST | RTA_GATEWAY;
+ rt->rtm_inits = RTV_EXPIRE;
+ rt->rtm_rmx.rmx_expire = time(0) + ARPSECS;
+ rt->rtm_flags = RTF_HOST | RTF_STATIC;
+ rt->rtm_type = RTM_ADD;
+ rt->rtm_seq = ++seq;
+
+ bcopy(ar, ar2, sizeof(*ar));
+
+ ll2 = (struct sockaddr_dl *)((u_char *)ar2 + sizeof(*ar2));
+ bcopy(ll, ll2, sizeof(*ll));
+ ll2->sdl_type = xtype;
+ ll2->sdl_index = xindex;
+
+ rt->rtm_msglen = sizeof(*rt) + sizeof(*ar2) + sizeof(*ll2);
+ errno = 0;
+ if ((write(r, rt, rt->rtm_msglen) == -1) && (errno != EEXIST)) {
+ logmsg(LOG_ERR, "rtmsg add write: %m");
+ close(r);
+ return;
+ }
+ do {
+ cc = read(r, rt, sizeof(rtmsg));
+ } while (cc > 0 && (rt->rtm_seq != seq || rt->rtm_pid != pid));
+ close(r);
+ if (cc == -1) {
+ logmsg(LOG_ERR, "rtmsg add read: %m");
+ return;
+ }
+}
+
+/*
+ * Build a reverse ARP packet and sent it out on the interface.
+ * 'ep' points to a valid REVARP_REQUEST. The REVARP_REPLY is built
+ * on top of the request, then written to the network.
+ *
+ * RFC 903 defines the ether_arp fields as follows. The following comments
+ * are taken (more or less) straight from this document.
+ *
+ * REVARP_REQUEST
+ *
+ * arp_sha is the hardware address of the sender of the packet.
+ * arp_spa is undefined.
+ * arp_tha is the 'target' hardware address.
+ * In the case where the sender wishes to determine his own
+ * protocol address, this, like arp_sha, will be the hardware
+ * address of the sender.
+ * arp_tpa is undefined.
+ *
+ * REVARP_REPLY
+ *
+ * arp_sha is the hardware address of the responder (the sender of the
+ * reply packet).
+ * arp_spa is the protocol address of the responder (see the note below).
+ * arp_tha is the hardware address of the target, and should be the same as
+ * that which was given in the request.
+ * arp_tpa is the protocol address of the target, that is, the desired address.
+ *
+ * Note that the requirement that arp_spa be filled in with the responder's
+ * protocol is purely for convenience. For instance, if a system were to use
+ * both ARP and RARP, then the inclusion of the valid protocol-hardware
+ * address pair (arp_spa, arp_sha) may eliminate the need for a subsequent
+ * ARP request.
+ */
+static void
+rarp_reply(struct if_info *ii, struct ether_header *ep, in_addr_t ipaddr,
+ u_int len)
+{
+ u_int n;
+ struct ether_arp *ap = (struct ether_arp *)(ep + 1);
+
+ update_arptab((u_char *)&ap->arp_sha, ipaddr);
+
+ /*
+ * Build the rarp reply by modifying the rarp request in place.
+ */
+ ap->arp_op = htons(REVARP_REPLY);
+
+#ifdef BROKEN_BPF
+ ep->ether_type = ETHERTYPE_REVARP;
+#endif
+ bcopy((char *)&ap->arp_sha, (char *)&ep->ether_dhost, 6);
+ bcopy((char *)ii->ii_eaddr, (char *)&ep->ether_shost, 6);
+ bcopy((char *)ii->ii_eaddr, (char *)&ap->arp_sha, 6);
+
+ bcopy((char *)&ipaddr, (char *)ap->arp_tpa, 4);
+ /* Target hardware is unchanged. */
+ bcopy((char *)&ii->ii_ipaddr, (char *)ap->arp_spa, 4);
+
+ /* Zero possible garbage after packet. */
+ bzero((char *)ep + (sizeof(*ep) + sizeof(*ap)),
+ len - (sizeof(*ep) + sizeof(*ap)));
+ n = write(ii->ii_fd, (char *)ep, len);
+ if (n != len)
+ logmsg(LOG_ERR, "write: only %d of %d bytes written", n, len);
+ if (verbose)
+ logmsg(LOG_INFO, "%s %s at %s REPLIED", ii->ii_ifname,
+ eatoa(ap->arp_tha),
+ intoa(ntohl(ipaddr)));
+}
+
+/*
+ * Get the netmask of an IP address. This routine is used if
+ * SIOCGIFNETMASK doesn't work.
+ */
+static in_addr_t
+ipaddrtonetmask(in_addr_t addr)
+{
+ addr = ntohl(addr);
+ if (IN_CLASSA(addr))
+ return htonl(IN_CLASSA_NET);
+ if (IN_CLASSB(addr))
+ return htonl(IN_CLASSB_NET);
+ if (IN_CLASSC(addr))
+ return htonl(IN_CLASSC_NET);
+ logmsg(LOG_DEBUG, "unknown IP address class: %08X", addr);
+ return htonl(0xffffffff);
+}
+
+/*
+ * A faster replacement for inet_ntoa().
+ */
+static char *
+intoa(in_addr_t addr)
+{
+ char *cp;
+ u_int byte;
+ int n;
+ static char buf[sizeof(".xxx.xxx.xxx.xxx")];
+
+ cp = &buf[sizeof buf];
+ *--cp = '\0';
+
+ n = 4;
+ do {
+ byte = addr & 0xff;
+ *--cp = byte % 10 + '0';
+ byte /= 10;
+ if (byte > 0) {
+ *--cp = byte % 10 + '0';
+ byte /= 10;
+ if (byte > 0)
+ *--cp = byte + '0';
+ }
+ *--cp = '.';
+ addr >>= 8;
+ } while (--n > 0);
+
+ return cp + 1;
+}
+
+static char *
+eatoa(u_char *ea)
+{
+ static char buf[sizeof("xx:xx:xx:xx:xx:xx")];
+
+ (void)sprintf(buf, "%x:%x:%x:%x:%x:%x",
+ ea[0], ea[1], ea[2], ea[3], ea[4], ea[5]);
+ return (buf);
+}
+
+static void
+logmsg(int pri, const char *fmt, ...)
+{
+ va_list v;
+ FILE *fp;
+ char *newfmt;
+
+ va_start(v, fmt);
+ if (dflag) {
+ if (pri == LOG_ERR)
+ fp = stderr;
+ else
+ fp = stdout;
+ if (expand_syslog_m(fmt, &newfmt) == -1) {
+ vfprintf(fp, fmt, v);
+ } else {
+ vfprintf(fp, newfmt, v);
+ free(newfmt);
+ }
+ fputs("\n", fp);
+ fflush(fp);
+ } else {
+ vsyslog(pri, fmt, v);
+ }
+ va_end(v);
+}
+
+static int
+expand_syslog_m(const char *fmt, char **newfmt) {
+ const char *str, *m;
+ char *p, *np;
+
+ p = strdup("");
+ str = fmt;
+ while ((m = strstr(str, "%m")) != NULL) {
+ asprintf(&np, "%s%.*s%s", p, (int)(m - str),
+ str, strerror(errno));
+ free(p);
+ if (np == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ p = np;
+ str = m + 2;
+ }
+
+ if (*str != '\0') {
+ asprintf(&np, "%s%s", p, str);
+ free(p);
+ if (np == NULL) {
+ errno = ENOMEM;
+ return (-1);
+ }
+ p = np;
+ }
+
+ *newfmt = p;
+ return (0);
+}
diff --git a/usr.sbin/raycontrol/Makefile b/usr.sbin/raycontrol/Makefile
new file mode 100644
index 0000000..50e7472
--- /dev/null
+++ b/usr.sbin/raycontrol/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= raycontrol
+MAN= raycontrol.8
+
+CFLAGS+= -I${.CURDIR}/../../sys
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/raycontrol/raycontrol.8 b/usr.sbin/raycontrol/raycontrol.8
new file mode 100644
index 0000000..26a910a
--- /dev/null
+++ b/usr.sbin/raycontrol/raycontrol.8
@@ -0,0 +1,307 @@
+.\"
+.\" Copyright (C) 2000
+.\" Dr. Duncan McLennan Barclay, dmlb@ragnet.demon.co.uk.
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 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 DUNCAN BARCLAY AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL DUNCAN BARCLAY OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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) 1997, 1998, 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 21, 2000
+.Dt RAYCONTROL 8
+.Os
+.Sh NAME
+.Nm raycontrol
+.Nd configure Raytheon Raylink/Webgear Aviator devices
+.Sh SYNOPSIS
+.Nm
+.Fl i Ar iface Op Fl o
+.Nm
+.Fl i Ar iface Fl t Ar tx rate
+.Nm
+.Fl i Ar iface Fl n Ar network name
+.Nm
+.Fl i Ar iface Fl c Ar ap status
+.Nm
+.Fl i Ar iface Fl p Ar port type
+.Nm
+.Fl i Ar iface Fl m Ar mac address
+.Nm
+.Fl i Ar iface Fl d Ar max data length
+.Nm
+.Fl i Ar iface Fl r Ar RTS threshold
+.Nm
+.Fl i Ar iface Fl f Ar hopset
+.Nm
+.Fl i Ar iface Fl P Ar 0|1
+.Nm
+.Fl i Ar iface Fl S Ar max_sleep_duration
+.Nm
+.Fl i Ar iface Fl Z Ar zero signal cache
+.Nm
+.Fl i Ar iface Fl C Ar display signal cache
+.Sh DESCRIPTION
+The
+.Nm
+utility controls the operation of Raylink/Webgear wireless networking
+devices via the
+.Xr ray 4
+driver.
+Most of the parameters that can be changed relate to the
+IEEE 802.11 protocol which the card implements.
+This includes
+the station name, whether the station is operating in ad-hoc
+or infrastructure mode, and the network name of a service
+set to join - the BSS in ad-hoc mode or ESS if infrastructure mode is enabled.
+The
+.Nm
+utility can also be used to view the current settings of these parameters
+and to dump out the values of the card's statistics counters.
+.Pp
+The
+.Ar iface
+argument given to
+.Nm
+should be the logical interface name associated with the Raylink/Webgear
+device (ray0, ray1, ...).
+.Sh OPTIONS
+The options are as follows:
+.Bl -tag -width Fl
+.It Fl i Ar iface Op Fl o
+Display the current settings of the specified Raylink/Webgear interface.
+This retrieves the current card settings from the driver and prints them
+out.
+The results from this command are a snapshot of the card settings.
+Using the additional
+.Fl o
+flag will cause
+.Nm
+to print out the statistics counters instead of the card settings.
+.It Fl i Ar iface Fl t Ar tx rate
+Set the transmit rate of the specified interface.
+The
+NICs support a maximum transmit rate of 2Mbps.
+The following table shows the
+legal transmit rate settings and the corresponding transmit speeds:
+.Bl -column "TX rate " "NIC speed " -offset indent
+.Em "TX rate NIC speed"
+1 Very Low (0.5Mbps)
+2 Low (1Mbps)
+3 Medium (1.5Mbps)
+4 High (2Mbps)
+.El
+.Pp
+The version 4 firmware may ignore this setting.
+Note, that the IEEE 802.11
+standard
+only allows 1Mbps or 2Mbps operation, and that the generally accepted
+reading of the IEEE 802.11 standard is that 2Mbps is only allowed in
+infrastructure mode.
+.It Fl i Ar iface Fl n Ar network name
+Set the name of the service set that this station wishes to
+join.
+The
+.Ar network name
+can be any text string up to 32 characters in length.
+The default name
+is the string "NETWORK_NAME" which should allow the station to connect to
+the default Webgear ad-hoc network.
+.It Fl i Ar iface Fl p Ar port type
+Set the
+.Ar port type
+for a specified interface.
+The legal values for
+.Ar port type
+are 0 (ad-hoc mode) and 1 (infrastructure mode).
+In ad-hoc mode, the station can
+communicate directly with any other stations within direct radio range
+(provided that they are also operating in ad-hoc mode).
+In infrastructure mode,
+hosts must associate with a service set controlled by an access point,
+that relays traffic between end stations.
+The default setting is 0
+(ad-hoc mode).
+.Pp
+When in ad-hoc mode the station will create a BSS with the network name
+specified by the
+.Fl n
+option if it cannot find an existing network of that name on the
+currently configured hopset (see the
+.Fl f
+option).
+.It Fl i Ar iface Fl m Ar mac address
+Set the station address for the specified interface.
+The
+.Ar mac address
+is specified as a series of six hexadecimal values separated by colons,
+e.g.: 00:60:1d:12:34:56.
+This programs the new address into the card
+and updates the interface as well.
+.It Fl i Ar iface Fl d Ar max_data_length
+Set the maximum transmit frame size for a specified interface.
+The
+.Ar max data length
+can be any number from 350 to 2304 or -1 to disable fragmentation.
+The default is -1.
+.It Fl i Ar iface Fl r Ar RTS threshold
+Set the RTS/CTS threshold for a given interface.
+This controls the
+number of bytes used for the RTS/CTS handshake boundary.
+The
+.Ar RTS threshold
+can be any value between -1 and 2047.
+The default is -1 (disable).
+.It Fl i Ar iface Fl f Ar hopset
+Set the radio hopset of a given interface.
+The
+.Ar hopset
+should be specified as a country code as shown in the table below.
+The
+hopset varies both the number of RF channels and their frequencies
+and is dependent on radio regulations specified
+by regional authorities.
+.Bl -column "Hopset ID " "Country " -offset indent
+.Em "Hopset ID Country"
+1 USA
+2 Europe
+3 Japan
+4 Korea
+5 Spain
+6 France
+7 Israel
+8 Australia
+9 Japan Test
+.El
+.Pp
+Whilst the card can be programmed to work with any hopset it makes
+sense to use the hopset for your own region to avoid interference from
+and interfering with other users of the RF spectrum (in places like
+France this is the military).
+.Pp
+Note that all stations must be set to the same hopset in order to
+communicate.
+.It Fl i Ar iface Fl P Ar 0|1
+Enable or disable power management on a given interface.
+Enabling
+power management uses an alternating sleep/wake protocol to help
+conserve power on mobile stations, at the cost of some increased
+receive latency.
+Power management is off by default.
+.Pp
+Note that power
+management requires the cooperation of an access point in order to
+function; it is not functional in ad-hoc mode.
+Legal
+values for this parameter are 0 (off) and 1 (on).
+.It Fl i Ar iface Fl S Ar max_sleep_interval
+Specify the sleep interval to use when power management is enabled.
+The
+.Ar max_sleep_interval
+is specified in milliseconds.
+The default is 100.
+.It Fl i Ar iface Fl Z
+Clear the signal strength cache maintained internally by the
+.Xr ray 4
+driver.
+.It Fl i Ar iface Fl C
+Display the cached signal strength information maintained by the
+.Xr ray 4
+driver.
+The driver retains information about signal strength and
+noise level for packets received from different hosts.
+For
+infrastructure networks the cache stores the signal strength of the
+access point.
+.Pp
+The driver also uses the cache to pick the best antenna when
+transmitting.
+.El
+.Sh SEE ALSO
+.Xr ray 4 ,
+.Xr ifconfig 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 4.0 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility was written by
+.An Duncan Barclay Aq dmlb@ragnet.demon.co.uk
+and based on the
+.Nm wicontrol
+utility by
+.An Bill Paul Aq wpaul@ctr.columbia.edu .
+.Sh BUGS
+The
+.Fl m ,
+.Fl P ,
+.Fl S
+and
+.Fl Z
+options are not implemented yet.
+No access point was available for testing
+against.
+.Pp
+Not tested with Version 5 firmware.
+.Pp
+Hopset changing may not work with version 4 firmware.
+.Pp
+The
+.Fl W
+option is un-documented on purpose.
diff --git a/usr.sbin/raycontrol/raycontrol.c b/usr.sbin/raycontrol/raycontrol.c
new file mode 100644
index 0000000..006feda
--- /dev/null
+++ b/usr.sbin/raycontrol/raycontrol.c
@@ -0,0 +1,489 @@
+/*
+ * Copyright (c) 1999, 2000
+ * Dr. Duncan McLennan Barclay, dmlb@ragnet.demon.co.uk. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 DUNCAN BARCLAY AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DUNCAN BARCLAY OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <net/ethernet.h>
+#include <net80211/ieee80211.h>
+#include <net80211/ieee80211_ioctl.h>
+
+#include <dev/ray/if_rayreg.h>
+#include <dev/ray/if_raymib.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <err.h>
+
+static char * ray_printhex (u_int8_t *d, char *s, int len);
+static void ray_getval (char *iface, struct ray_param_req *rreq);
+static void ray_getstats (char *iface, struct ray_stats_req *sreq);
+static int ray_version (char *iface);
+static void ray_dumpstats (char *iface);
+static void ray_dumpinfo (char *iface);
+static void ray_setstr (char *iface, u_int8_t mib, char *s);
+static void ray_setword (char *iface, u_int8_t mib, u_int16_t v);
+static void ray_setval (char *iface, struct ray_param_req *rreq);
+static void usage (char *p);
+
+static char *mib_strings[] = RAY_MIB_STRINGS;
+static char *mib_help_strings[] = RAY_MIB_HELP_STRINGS;
+static int mib_info[RAY_MIB_MAX+1][3] = RAY_MIB_INFO;
+
+static char *
+ray_printhex(u_int8_t *d, char *s, int len)
+{
+ static char buf[3*256];
+ char *p;
+ int i;
+
+ if (2 * len + strlen(s) * (len - 1) > sizeof(buf) - 1)
+ errx(1, "byte string too long");
+
+ sprintf(buf, "%02x", *d);
+ for (p = buf + 2, i = 1; i < len; i++)
+ p += sprintf(p, "%s%02x", s, *(d+i));
+
+ return(buf);
+}
+
+static void
+ray_getval(char *iface, struct ray_param_req *rreq)
+{
+ struct ifreq ifr;
+ int s;
+
+ bzero((char *)&ifr, sizeof(ifr));
+
+ strlcpy(ifr.ifr_name, iface, IFNAMSIZ);
+ ifr.ifr_data = (caddr_t)rreq;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (s == -1)
+ err(1, "socket");
+
+ if (ioctl(s, SIOCGRAYPARAM, &ifr) == -1)
+ warn("SIOCGRAYPARAM failed with failcode 0x%02x",
+ rreq->r_failcause);
+
+ close(s);
+}
+
+static void
+ray_getsiglev(char *iface, struct ray_siglev *siglev)
+{
+ struct ifreq ifr;
+ int s;
+
+ bzero((char *)&ifr, sizeof(ifr));
+
+ strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (caddr_t)siglev;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (s == -1)
+ err(1, "socket");
+
+ if (ioctl(s, SIOCGRAYSIGLEV, &ifr) == -1)
+ err(1, "SIOCGRAYSIGLEV failed");
+
+ close(s);
+}
+
+static void
+ray_getstats(char *iface, struct ray_stats_req *sreq)
+{
+ struct ifreq ifr;
+ int s;
+
+ bzero((char *)&ifr, sizeof(ifr));
+
+ strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (caddr_t)sreq;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (s == -1)
+ err(1, "socket");
+
+ if (ioctl(s, SIOCGRAYSTATS, &ifr) == -1)
+ err(1, "SIOCGRAYSTATS failed");
+
+ close(s);
+}
+
+static int
+ray_version(char *iface)
+{
+ struct ray_param_req rreq;
+
+ if (iface == NULL)
+ errx(1, "must specify interface name");
+
+ bzero((char *)&rreq, sizeof(rreq));
+ rreq.r_paramid = RAY_MIB_VERSION;
+ ray_getval(iface, &rreq);
+ return(*rreq.r_data);
+}
+
+static void
+ray_dumpinfo(char *iface)
+{
+ struct ray_param_req rreq;
+ u_int8_t mib, version;
+
+ if (iface == NULL)
+ errx(1, "must specify interface name");
+
+ bzero((char *)&rreq, sizeof(rreq));
+
+ version = ray_version(iface);
+ printf("%-26s\t", mib_strings[RAY_MIB_VERSION]);
+ printf("%d\n", 3+version);
+
+ for (mib = RAY_MIB_NET_TYPE; mib <= RAY_MIB_MAX; mib++) {
+
+ if ((mib_info[mib][0] & version) == 0)
+ continue;
+ if (mib == RAY_MIB_VERSION)
+ continue;
+
+ rreq.r_paramid = mib;
+ ray_getval(iface, &rreq);
+ printf("%-26s\t", mib_strings[mib]);
+ switch (rreq.r_len) {
+
+ case 2:
+ printf("0x%02x%02x", *rreq.r_data, *(rreq.r_data+1));
+ break;
+
+ case ETHER_ADDR_LEN:
+ printf("%s",
+ ray_printhex(rreq.r_data, ":", rreq.r_len));
+ break;
+
+ case IEEE80211_NWID_LEN:
+ printf("%-32s", (char *)rreq.r_data);
+ break;
+
+
+ case 1:
+ default:
+ printf("0x%02x", *rreq.r_data);
+ break;
+ }
+ printf("\t%s\n", mib_help_strings[mib]);
+ }
+}
+
+static void
+ray_dumpsiglev(char *iface)
+{
+ struct ray_siglev siglevs[RAY_NSIGLEVRECS];
+ int i;
+
+ if (iface == NULL)
+ errx(1, "must specify interface name");
+
+ bzero((char *)siglevs, sizeof(siglevs));
+
+ ray_getsiglev(iface, siglevs);
+
+ for (i = 0; i < RAY_NSIGLEVRECS; i++) {
+ printf("Slot %d: %s", i,
+ ray_printhex(siglevs[i].rsl_host, ":", ETHER_ADDR_LEN));
+ printf(" %s",
+ ray_printhex(siglevs[i].rsl_siglevs, ",", RAY_NSIGLEV));
+ printf(" %s\n",
+ ray_printhex(siglevs[i].rsl_antennas, "", RAY_NANTENNA));
+ }
+
+}
+
+static void
+ray_dumpstats(char *iface)
+{
+ struct ray_stats_req sreq;
+
+ if (iface == NULL)
+ errx(1, "must specify interface name");
+
+ bzero((char *)&sreq, sizeof(sreq));
+
+ ray_getstats(iface, &sreq);
+
+ printf("Receiver overflows %lu\n",
+ (unsigned long int)sreq.rxoverflow);
+ printf("Receiver checksum errors %lu\n",
+ (unsigned long int)sreq.rxcksum);
+ printf("Header checksum errors %lu\n",
+ (unsigned long int)sreq.rxhcksum);
+ printf("Clear channel noise level %u\n", sreq.rxnoise);
+}
+
+static void
+ray_setval(char *iface, struct ray_param_req *rreq)
+{
+ struct ifreq ifr;
+ int s;
+
+ bzero((char *)&ifr, sizeof(ifr));
+
+ strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
+ ifr.ifr_data = (caddr_t)rreq;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+
+ if (s == -1)
+ err(1, "socket");
+
+ if (ioctl(s, SIOCSRAYPARAM, &ifr) == -1) {
+ err(1, "SIOCSRAYPARAM failed with failcode 0x%02x",
+ rreq->r_failcause);
+ }
+
+ close(s);
+}
+
+static void
+ray_setword(char *iface, u_int8_t mib, u_int16_t v)
+{
+ struct ray_param_req rreq;
+
+ if (iface == NULL)
+ errx(1, "must specify interface name");
+
+ bzero((char *)&rreq, sizeof(rreq));
+
+ rreq.r_paramid = mib;
+ rreq.r_len = RAY_MIB_SIZE(mib_info, mib, ray_version(iface));
+ switch (rreq.r_len) {
+
+ case 1:
+ *rreq.r_data = (u_int8_t)(v & 0xff);
+ break;
+
+ case 2:
+ *rreq.r_data = (u_int8_t)((v & 0xff00) >> 8);
+ *(rreq.r_data+1) = (u_int8_t)(v & 0xff);
+ break;
+
+ default:
+ break;
+ }
+
+ ray_setval(iface, &rreq);
+}
+
+static void
+ray_setstr(char *iface, u_int8_t mib, char *s)
+{
+ struct ray_param_req rreq;
+
+ if (iface == NULL)
+ errx(1, "must specify interface name");
+ if (s == NULL)
+ errx(1, "must specify string");
+ if (strlen(s) > RAY_MIB_SIZE(mib_info, mib, ray_version(iface)))
+ errx(1, "string too long");
+
+ bzero((char *)&rreq, sizeof(rreq));
+
+ rreq.r_paramid = mib;
+ rreq.r_len = RAY_MIB_SIZE(mib_info, mib, ray_version(iface));
+ bcopy(s, (char *)rreq.r_data, strlen(s));
+
+ ray_setval(iface, &rreq);
+}
+
+static void
+usage(char *p)
+{
+ fprintf(stderr, "usage: %s -i iface\n", p);
+ fprintf(stderr, "\t%s -i iface -o\n", p);
+ fprintf(stderr, "\t%s -i iface -t tx rate\n", p);
+ fprintf(stderr, "\t%s -i iface -n network name\n", p);
+ fprintf(stderr, "\t%s -i iface -p port type\n", p);
+ fprintf(stderr, "\t%s -i iface -m mac address\n", p);
+ fprintf(stderr, "\t%s -i iface -d max data length\n", p);
+ fprintf(stderr, "\t%s -i iface -r RTS threshold\n", p);
+ fprintf(stderr, "\t%s -i iface -f hopset\n", p);
+ fprintf(stderr, "\t%s -i iface -P 0|1\n", p);
+ fprintf(stderr, "\t%s -i iface -S max sleep duration\n", p);
+ fprintf(stderr, "\t%s -i iface -C print signal cache\n", p);
+
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+
+ char *iface, *p;
+ int ch, val;
+
+ iface = NULL;
+ p = argv[0];
+
+ /* Get the interface name */
+ opterr = 0;
+ ch = getopt(argc, argv, "i:");
+ if (ch == 'i') {
+ iface = optarg;
+ } else {
+ if (argc > 1 && *argv[1] != '-') {
+ iface = argv[1];
+ optind = 2;
+ } else {
+ iface = "ray0";
+ optind = 1;
+ }
+ optreset = 1;
+ }
+ opterr = 1;
+
+ while ((ch = getopt(argc, argv, "hoCi:d:f:n:p:r:t:W:")) != -1) {
+ switch (ch) {
+
+ case 'i':
+ iface = optarg;
+ break;
+
+ case 'd':
+ val = atoi(optarg);
+ if (((val < 350) &&
+ (val != -1)) || (val > RAY_MIB_FRAG_THRESH_MAXIMUM))
+ usage(p);
+ if (val == -1)
+ val = 0x7fff;
+ ray_setword(iface, RAY_MIB_FRAG_THRESH, val);
+ exit(0);
+ break;
+
+ case 'f':
+ val = atoi(optarg);
+ if ((val < RAY_MIB_COUNTRY_CODE_MIMIMUM) ||
+ (val > RAY_MIB_COUNTRY_CODE_MAXIMUM))
+ usage(p);
+ ray_setword(iface, RAY_MIB_COUNTRY_CODE, val);
+ exit(0);
+ break;
+
+ case 'n':
+ ray_setstr(iface, RAY_MIB_SSID, optarg);
+ exit(0);
+ break;
+
+ case 'o':
+ ray_dumpstats(iface);
+ exit(0);
+ break;
+
+ case 'p':
+ val = atoi(optarg);
+ if ((val < 0) || (val > 1))
+ usage(p);
+ ray_setword(iface, RAY_MIB_NET_TYPE, val);
+ exit(0);
+ break;
+
+
+ case 'r':
+ val = atoi(optarg);
+ if ((val < -1) || (val > RAY_MIB_RTS_THRESH_MAXIMUM))
+ usage(p);
+ if (val == -1)
+ val = 0x7fff;
+ ray_setword(iface, RAY_MIB_RTS_THRESH, val);
+ exit(0);
+ break;
+
+ case 't':
+ val = atoi(optarg);
+ if ((val < RAY_MIB_BASIC_RATE_SET_MINIMUM) ||
+ (val > RAY_MIB_BASIC_RATE_SET_MAXIMUM))
+ usage(p);
+ ray_setword(iface, RAY_MIB_BASIC_RATE_SET, val);
+ exit(0);
+ break;
+
+ case 'C':
+ ray_dumpsiglev(iface);
+ exit(0);
+ break;
+
+ case 'W':
+ {
+ char *stringp, **ap, *av[5];
+ u_int8_t mib;
+
+ stringp = optarg;
+ ap = av;
+ *ap = strsep(&stringp, ":");
+ if (stringp == NULL)
+ usage(p);
+ ap++;
+ *ap = strsep(&stringp, ":");
+ mib = atoi(av[0]);
+ sscanf(av[1], "%x", &val);
+ printf("mib %d, val 0x%02x\n", mib, val);
+ ray_setword(iface, mib, val);
+ }
+ exit(0);
+ break;
+
+ case 'h':
+ default:
+ usage(p);
+
+ }
+ }
+
+ if (iface == NULL)
+ usage(p);
+
+ ray_dumpinfo(iface);
+
+ exit(0);
+}
diff --git a/usr.sbin/repquota/Makefile b/usr.sbin/repquota/Makefile
new file mode 100644
index 0000000..087e890
--- /dev/null
+++ b/usr.sbin/repquota/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= repquota
+MAN= repquota.8
+
+WARNS?= 4
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/repquota/repquota.8 b/usr.sbin/repquota/repquota.8
new file mode 100644
index 0000000..b669fdf
--- /dev/null
+++ b/usr.sbin/repquota/repquota.8
@@ -0,0 +1,106 @@
+.\" 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.
+.\"
+.\" @(#)repquota.8 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\"
+.Dd June 6, 1993
+.Dt REPQUOTA 8
+.Os
+.Sh NAME
+.Nm repquota
+.Nd summarize quotas for a file system
+.Sh SYNOPSIS
+.Nm
+.Op Fl g
+.Op Fl n
+.Op Fl u
+.Op Fl v
+.Ar filesystem Ar ...
+.Nm
+.Op Fl g
+.Op Fl n
+.Op Fl u
+.Op Fl v
+.Fl a
+.Sh DESCRIPTION
+The
+.Nm
+utility prints a summary of the disk usage and quotas for the
+specified file systems.
+.Pp
+Available options:
+.Bl -tag -width indent
+.It Fl a
+Print the quotas of all the file systems listed in
+.Pa /etc/fstab .
+.It Fl g
+Print only group quotas (the default is to print both
+group and user quotas if they exist).
+.It Fl n
+Display user and group IDs numerically rather than converting to
+a user or group name.
+.It Fl u
+Print only user quotas (the default is to print both
+group and user quotas if they exist).
+.It Fl v
+Print a header line before printing each file system quotas.
+.El
+.Pp
+For each user or group, the current
+number files and amount of space (in kilobytes) is
+printed, along with any quotas created with
+.Xr edquota 8 .
+.Pp
+Only members of the operator group or the super-user may
+use this command.
+.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
+for file system names and locations
+.El
+.Sh DIAGNOSTICS
+Various messages about inaccessible files; self-explanatory.
+.Sh SEE ALSO
+.Xr quota 1 ,
+.Xr quotactl 2 ,
+.Xr fstab 5 ,
+.Xr edquota 8 ,
+.Xr quotacheck 8 ,
+.Xr quotaon 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.2 .
diff --git a/usr.sbin/repquota/repquota.c b/usr.sbin/repquota/repquota.c
new file mode 100644
index 0000000..19c1858
--- /dev/null
+++ b/usr.sbin/repquota/repquota.c
@@ -0,0 +1,408 @@
+/*
+ * 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[] = "@(#)repquota.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Quota report
+ */
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <ufs/ufs/quota.h>
+#include <err.h>
+#include <errno.h>
+#include <fstab.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <utmp.h>
+
+/* Let's be paranoid about block size */
+#if 10 > DEV_BSHIFT
+#define dbtokb(db) \
+ ((off_t)(db) >> (10-DEV_BSHIFT))
+#elif 10 < DEV_BSHIFT
+#define dbtokb(db) \
+ ((off_t)(db) << (DEV_BSHIFT-10))
+#else
+#define dbtokb(db) (db)
+#endif
+
+#define max(a,b) ((a) >= (b) ? (a) : (b))
+
+const char *qfname = QUOTAFILENAME;
+const char *qfextension[] = INITQFNAMES;
+
+struct fileusage {
+ struct fileusage *fu_next;
+ struct dqblk fu_dqblk;
+ u_long fu_id;
+ char fu_name[1];
+ /* actually bigger */
+};
+#define FUHASH 1024 /* must be power of two */
+struct fileusage *fuhead[MAXQUOTAS][FUHASH];
+struct fileusage *lookup(u_long, int);
+struct fileusage *addid(u_long, int, char *);
+u_long highid[MAXQUOTAS]; /* highest addid()'ed identifier per type */
+
+int vflag; /* verbose */
+int aflag; /* all filesystems */
+int nflag; /* display user/group by id */
+
+int hasquota(struct fstab *, int, char **);
+int oneof(char *, char *[], int);
+int repquota(struct fstab *, int, char *);
+char *timeprt(time_t);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ struct fstab *fs;
+ struct passwd *pw;
+ struct group *gr;
+ int ch, gflag = 0, uflag = 0, errs = 0;
+ long i, argnum, done = 0;
+ char *qfnp;
+
+ while ((ch = getopt(argc, argv, "agnuv")) != -1) {
+ switch(ch) {
+ case 'a':
+ aflag++;
+ break;
+ case 'g':
+ gflag++;
+ break;
+ case 'n':
+ nflag++;
+ break;
+ case 'u':
+ uflag++;
+ break;
+ case 'v':
+ vflag++;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc == 0 && !aflag)
+ usage();
+ if (!gflag && !uflag) {
+ if (aflag)
+ gflag++;
+ uflag++;
+ }
+ if (gflag && !nflag) {
+ setgrent();
+ while ((gr = getgrent()) != 0)
+ (void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name);
+ endgrent();
+ }
+ if (uflag && !nflag) {
+ setpwent();
+ while ((pw = getpwent()) != 0)
+ (void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name);
+ endpwent();
+ }
+ setfsent();
+ while ((fs = getfsent()) != NULL) {
+ if (strcmp(fs->fs_vfstype, "ufs"))
+ continue;
+ if (aflag) {
+ if (gflag && hasquota(fs, GRPQUOTA, &qfnp))
+ errs += repquota(fs, GRPQUOTA, qfnp);
+ if (uflag && hasquota(fs, USRQUOTA, &qfnp))
+ errs += repquota(fs, USRQUOTA, qfnp);
+ continue;
+ }
+ if ((argnum = oneof(fs->fs_file, argv, argc)) >= 0 ||
+ (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) {
+ done |= 1 << argnum;
+ if (gflag && hasquota(fs, GRPQUOTA, &qfnp))
+ errs += repquota(fs, GRPQUOTA, qfnp);
+ if (uflag && hasquota(fs, USRQUOTA, &qfnp))
+ errs += repquota(fs, USRQUOTA, qfnp);
+ }
+ }
+ endfsent();
+ for (i = 0; i < argc; i++)
+ if ((done & (1 << i)) == 0)
+ warnx("%s not found in fstab", argv[i]);
+ exit(errs);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: repquota [-v] [-g] [-n] [-u] -a",
+ " repquota [-v] [-g] [-n] [-u] filesystem ...");
+ exit(1);
+}
+
+int
+repquota(struct fstab *fs, int type, char *qfpathname)
+{
+ struct fileusage *fup;
+ FILE *qf;
+ u_long id;
+ struct dqblk dqbuf;
+ static struct dqblk zerodqblk;
+ static int warned = 0;
+ static int multiple = 0;
+
+ if (quotactl(fs->fs_file, QCMD(Q_SYNC, type), 0, 0) < 0 &&
+ errno == EOPNOTSUPP && !warned && vflag) {
+ warned++;
+ fprintf(stdout,
+ "*** Warning: Quotas are not compiled into this kernel\n");
+ }
+ if (multiple++)
+ printf("\n");
+ if (vflag)
+ fprintf(stdout, "*** Report for %s quotas on %s (%s)\n",
+ qfextension[type], fs->fs_file, fs->fs_spec);
+ if ((qf = fopen(qfpathname, "r")) == NULL) {
+ warn("%s", qfpathname);
+ return (1);
+ }
+ for (id = 0; ; id++) {
+ fread(&dqbuf, sizeof(struct dqblk), 1, qf);
+ if (feof(qf))
+ break;
+ if (dqbuf.dqb_curinodes == 0 && dqbuf.dqb_curblocks == 0)
+ continue;
+ if ((fup = lookup(id, type)) == 0)
+ fup = addid(id, type, (char *)0);
+ fup->fu_dqblk = dqbuf;
+ }
+ fclose(qf);
+ printf("%*s Block limits File limits\n",
+ max(UT_NAMESIZE,10), " ");
+ printf("%s%*s used soft hard grace used soft hard grace\n",
+ type == USRQUOTA ? "User " : "Group", max(UT_NAMESIZE,10), " ");
+ for (id = 0; id <= highid[type]; id++) {
+ fup = lookup(id, type);
+ if (fup == 0)
+ continue;
+ if (fup->fu_dqblk.dqb_curinodes == 0 &&
+ fup->fu_dqblk.dqb_curblocks == 0)
+ continue;
+ printf("%-*s ", max(UT_NAMESIZE,10), fup->fu_name);
+ printf("%c%c %8lu %8lu %8lu %6s",
+ fup->fu_dqblk.dqb_bsoftlimit &&
+ fup->fu_dqblk.dqb_curblocks >=
+ fup->fu_dqblk.dqb_bsoftlimit ? '+' : '-',
+ fup->fu_dqblk.dqb_isoftlimit &&
+ fup->fu_dqblk.dqb_curinodes >=
+ fup->fu_dqblk.dqb_isoftlimit ? '+' : '-',
+ (u_long)(dbtokb(fup->fu_dqblk.dqb_curblocks)),
+ (u_long)(dbtokb(fup->fu_dqblk.dqb_bsoftlimit)),
+ (u_long)(dbtokb(fup->fu_dqblk.dqb_bhardlimit)),
+ fup->fu_dqblk.dqb_bsoftlimit &&
+ fup->fu_dqblk.dqb_curblocks >=
+ fup->fu_dqblk.dqb_bsoftlimit ?
+ timeprt(fup->fu_dqblk.dqb_btime) : "-");
+ printf(" %7lu %7lu %7lu %6s\n",
+ (u_long)fup->fu_dqblk.dqb_curinodes,
+ (u_long)fup->fu_dqblk.dqb_isoftlimit,
+ (u_long)fup->fu_dqblk.dqb_ihardlimit,
+ fup->fu_dqblk.dqb_isoftlimit &&
+ fup->fu_dqblk.dqb_curinodes >=
+ fup->fu_dqblk.dqb_isoftlimit ?
+ timeprt(fup->fu_dqblk.dqb_itime) : "-");
+ fup->fu_dqblk = zerodqblk;
+ }
+ return (0);
+}
+
+/*
+ * Check to see if target appears in list of size cnt.
+ */
+int
+oneof(char *target, char *list[], int cnt)
+{
+ int i;
+
+ for (i = 0; i < cnt; i++)
+ if (strcmp(target, list[i]) == 0)
+ return (i);
+ return (-1);
+}
+
+/*
+ * Check to see if a particular quota is to be enabled.
+ */
+int
+hasquota(struct fstab *fs, int type, char **qfnamep)
+{
+ char *opt;
+ char *cp;
+ struct statfs sfb;
+ 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, '=')))
+ *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;
+ }
+ if (statfs(fs->fs_file, &sfb) != 0) {
+ warn("cannot statfs mount point %s", fs->fs_file);
+ return (0);
+ }
+ if (strcmp(fs->fs_file, sfb.f_mntonname)) {
+ warnx("%s not mounted for %s quotas", fs->fs_file,
+ type == USRQUOTA ? "user" : "group");
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * Routines to manage the file usage table.
+ *
+ * Lookup an id of a specific type.
+ */
+struct fileusage *
+lookup(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 ((struct fileusage *)0);
+}
+
+/*
+ * Add a new file usage id if it does not already exist.
+ */
+struct fileusage *
+addid(u_long id, int type, char *name)
+{
+ struct fileusage *fup, **fhp;
+ int len;
+
+ if ((fup = lookup(id, type)))
+ return (fup);
+ if (name)
+ len = strlen(name);
+ else
+ len = 10;
+ if ((fup = (struct fileusage *)calloc(1, sizeof(*fup) + len)) == NULL)
+ errx(1, "out of memory for fileusage structures");
+ 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 {
+ sprintf(fup->fu_name, "%lu", id);
+ }
+ return (fup);
+}
+
+/*
+ * Calculate the grace period and return a printable string for it.
+ */
+char *
+timeprt(time_t seconds)
+{
+ time_t hours, minutes;
+ static char buf[20];
+ static time_t now;
+
+ if (now == 0)
+ time(&now);
+ if (now > seconds) {
+ strlcpy(buf, "none", sizeof (buf));
+ return (buf);
+ }
+ seconds -= now;
+ minutes = (seconds + 30) / 60;
+ hours = (minutes + 30) / 60;
+ if (hours >= 36) {
+ sprintf(buf, "%lddays", (long)(hours + 12) / 24);
+ return (buf);
+ }
+ if (minutes >= 60) {
+ sprintf(buf, "%2ld:%ld", (long)minutes / 60,
+ (long)minutes % 60);
+ return (buf);
+ }
+ sprintf(buf, "%2ld", (long)minutes);
+ return (buf);
+}
diff --git a/usr.sbin/rip6query/Makefile b/usr.sbin/rip6query/Makefile
new file mode 100644
index 0000000..e0ea621
--- /dev/null
+++ b/usr.sbin/rip6query/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+PROG= rip6query
+MAN= rip6query.8
+
+WARNS?= 2
+CFLAGS+= -I${.CURDIR}/../route6d
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rip6query/rip6query.8 b/usr.sbin/rip6query/rip6query.8
new file mode 100644
index 0000000..9bcd4a5
--- /dev/null
+++ b/usr.sbin/rip6query/rip6query.8
@@ -0,0 +1,64 @@
+.\" $KAME: rip6query.8,v 1.4 2001/03/15 12:35:24 itojun Exp $
+.\"
+.\" Copyright (C) 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.
+.\"
+.\" $Id: rip6query.8,v 1.2 2000/01/19 06:24:55 itojun Exp $
+.\" $FreeBSD$
+.\"
+.Dd October 7, 1999
+.Dt RIP6QUERY 8
+.Os
+.Sh NAME
+.Nm rip6query
+.Nd RIPng debugging tool
+.\"
+.Sh SYNOPSIS
+.Nm
+.Op Fl I Ar interface
+.Ar destination
+.\"
+.Sh DESCRIPTION
+The
+.Nm
+utility requests remote RIPng daemon on
+.Ar destination
+to dump RIPng routing information.
+.Fl I
+lets you specify outgoing
+.Ar interface
+for the query packet,
+and is useful when link-local address is specified for
+.Ar destination .
+.\"
+.Sh SEE ALSO
+.Xr route6d 8
+.\"
+.Sh HISTORY
+The
+.Nm
+utility first appeared in WIDE Hydrangea IPv6 protocol stack kit.
diff --git a/usr.sbin/rip6query/rip6query.c b/usr.sbin/rip6query/rip6query.c
new file mode 100644
index 0000000..0bf56fd
--- /dev/null
+++ b/usr.sbin/rip6query/rip6query.c
@@ -0,0 +1,204 @@
+/* $KAME: rip6query.c,v 1.11 2001/05/08 04:36:37 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$
+ */
+
+#include <stdio.h>
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <signal.h>
+#include <errno.h>
+#include <err.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <net/if_var.h>
+#endif /* __FreeBSD__ >= 3 */
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include "route6d.h"
+
+int s;
+struct sockaddr_in6 sin6;
+struct rip6 *ripbuf;
+
+#define RIPSIZE(n) (sizeof(struct rip6) + (n-1) * sizeof(struct netinfo6))
+
+int main(int, char **);
+static void usage(void);
+static const char *sa_n2a(struct sockaddr *);
+static const char *inet6_n2a(struct in6_addr *);
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ struct netinfo6 *np;
+ struct sockaddr_in6 fsock;
+ int i, n, len;
+ int c;
+ int ifidx = -1;
+ int error;
+ socklen_t flen;
+ char pbuf[10];
+ struct addrinfo hints, *res;
+
+ while ((c = getopt(argc, argv, "I:")) != -1) {
+ switch (c) {
+ case 'I':
+ ifidx = if_nametoindex(optarg);
+ if (ifidx == 0) {
+ errx(1, "invalid interface %s", optarg);
+ /*NOTREACHED*/
+ }
+ break;
+ default:
+ usage();
+ exit(1);
+ /*NOTREACHED*/
+ }
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (argc != 1) {
+ usage();
+ exit(1);
+ }
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ err(1, "socket");
+ /*NOTREACHED*/
+ }
+
+ /* getaddrinfo is preferred for addr@ifname syntax */
+ snprintf(pbuf, sizeof(pbuf), "%d", RIP6_PORT);
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_DGRAM;
+ error = getaddrinfo(argv[0], pbuf, &hints, &res);
+ if (error) {
+ errx(1, "%s: %s", argv[0], gai_strerror(error));
+ /*NOTREACHED*/
+ }
+ if (res->ai_next) {
+ errx(1, "%s: %s", argv[0], "resolved to multiple addrs");
+ /*NOTREACHED*/
+ }
+ if (sizeof(sin6) != res->ai_addrlen) {
+ errx(1, "%s: %s", argv[0], "invalid addrlen");
+ /*NOTREACHED*/
+ }
+ memcpy(&sin6, res->ai_addr, res->ai_addrlen);
+ if (ifidx >= 0)
+ sin6.sin6_scope_id = ifidx;
+
+ if ((ripbuf = (struct rip6 *)malloc(BUFSIZ)) == NULL) {
+ err(1, "malloc");
+ /*NOTREACHED*/
+ }
+ ripbuf->rip6_cmd = RIP6_REQUEST;
+ ripbuf->rip6_vers = RIP6_VERSION;
+ ripbuf->rip6_res1[0] = 0;
+ ripbuf->rip6_res1[1] = 0;
+ np = ripbuf->rip6_nets;
+ bzero(&np->rip6_dest, sizeof(struct in6_addr));
+ np->rip6_tag = 0;
+ np->rip6_plen = 0;
+ np->rip6_metric = HOPCNT_INFINITY6;
+ if (sendto(s, ripbuf, RIPSIZE(1), 0, (struct sockaddr *)&sin6,
+ sizeof(struct sockaddr_in6)) < 0) {
+ err(1, "send");
+ /*NOTREACHED*/
+ }
+ do {
+ flen = sizeof(fsock);
+ if ((len = recvfrom(s, ripbuf, BUFSIZ, 0,
+ (struct sockaddr *)&fsock, &flen)) < 0) {
+ err(1, "recvfrom");
+ /*NOTREACHED*/
+ }
+ printf("Response from %s len %d\n",
+ sa_n2a((struct sockaddr *)&fsock), len);
+ n = (len - sizeof(struct rip6) + sizeof(struct netinfo6)) /
+ sizeof(struct netinfo6);
+ np = ripbuf->rip6_nets;
+ for (i = 0; i < n; i++, np++) {
+ printf("\t%s/%d [%d]", inet6_n2a(&np->rip6_dest),
+ np->rip6_plen, np->rip6_metric);
+ if (np->rip6_tag)
+ printf(" tag=0x%x", ntohs(np->rip6_tag));
+ printf("\n");
+ }
+ } while (len == RIPSIZE(24));
+
+ exit(0);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: rip6query [-I iface] address\n");
+}
+
+/* getnameinfo() is preferred as we may be able to show ifindex as ifname */
+static const char *
+sa_n2a(sa)
+ struct sockaddr *sa;
+{
+ static char buf[NI_MAXHOST];
+
+ if (getnameinfo(sa, sa->sa_len, buf, sizeof(buf),
+ NULL, 0, NI_NUMERICHOST) != 0) {
+ snprintf(buf, sizeof(buf), "%s", "(invalid)");
+ }
+ return buf;
+}
+
+static const char *
+inet6_n2a(addr)
+ struct in6_addr *addr;
+{
+ static char buf[NI_MAXHOST];
+
+ return inet_ntop(AF_INET6, addr, buf, sizeof(buf));
+}
diff --git a/usr.sbin/rmt/Makefile b/usr.sbin/rmt/Makefile
new file mode 100644
index 0000000..579a02b
--- /dev/null
+++ b/usr.sbin/rmt/Makefile
@@ -0,0 +1,14 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= rmt
+MAN= rmt.8
+
+WARNS?= 4
+
+# called from /usr/src/etc/Makefile
+etc-rmt:
+ rm -f ${DESTDIR}/etc/rmt
+ ln -s ${BINDIR}/rmt ${DESTDIR}/etc/rmt
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rmt/rmt.8 b/usr.sbin/rmt/rmt.8
new file mode 100644
index 0000000..8dfe245
--- /dev/null
+++ b/usr.sbin/rmt/rmt.8
@@ -0,0 +1,221 @@
+.\" 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.
+.\"
+.\" @(#)rmt.8 8.3 (Berkeley) 6/1/94
+.\" $FreeBSD$
+.\"
+.Dd June 1, 1994
+.Dt RMT 8
+.Os
+.Sh NAME
+.Nm rmt
+.Nd remote magtape protocol module
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+utility is used by the remote dump and restore programs
+in manipulating a magnetic tape drive through an interprocess
+communication connection.
+It is normally started up with an
+.Xr rcmd 3
+call.
+.Pp
+The
+.Nm
+utility accepts requests specific to the manipulation of
+magnetic tapes, performs the commands, then responds with
+a status indication.
+All responses are in
+.Tn ASCII
+and in
+one of two forms.
+Successful commands have responses of:
+.Bd -ragged -offset indent
+.Sm off
+.Sy A Ar number No \en
+.Sm on
+.Ed
+.Pp
+.Ar Number
+is an
+.Tn ASCII
+representation of a decimal number.
+Unsuccessful commands are responded to with:
+.Bd -ragged -offset indent
+.Sm off
+.Xo Sy E Ar error-number
+.No \en Ar error-message
+.No \en
+.Xc
+.Sm on
+.Ed
+.Pp
+.Ar Error-number
+is one of the possible error
+numbers described in
+.Xr intro 2
+and
+.Ar error-message
+is the corresponding error string as printed
+from a call to
+.Xr perror 3 .
+The protocol is comprised of the
+following commands, which are sent as indicated - no spaces are supplied
+between the command and its arguments, or between its arguments, and
+.Ql \en
+indicates that a newline should be supplied:
+.Bl -tag -width Ds
+.Sm off
+.It Xo Sy \&O Ar device
+.No \en Ar mode No \en
+.Xc
+.Sm on
+Open the specified
+.Ar device
+using the indicated
+.Ar mode .
+.Ar Device
+is a full pathname and
+.Ar mode
+is an
+.Tn ASCII
+representation of a decimal
+number suitable for passing to
+.Xr open 2 .
+If a device had already been opened, it is
+closed before a new open is performed.
+.Sm off
+.It Xo Sy C Ar device No \en
+.Xc
+.Sm on
+Close the currently open device.
+The
+.Ar device
+specified is ignored.
+.Sm off
+.It Xo Sy L
+.Ar whence No \en
+.Ar offset No \en
+.Xc
+.Sm on
+Perform an
+.Xr lseek 2
+operation using the specified parameters.
+The response value is that returned from the
+.Xr lseek 2
+call.
+.Sm off
+.It Sy W Ar count No \en
+.Sm on
+Write data onto the open device.
+The
+.Nm
+utility reads
+.Ar count
+bytes from the connection, aborting if
+a premature end-of-file is encountered.
+The response value is that returned from
+the
+.Xr write 2
+call.
+.Sm off
+.It Sy R Ar count No \en
+.Sm on
+Read
+.Ar count
+bytes of data from the open device.
+If
+.Ar count
+exceeds the size of the data buffer (10 kilobytes), it is
+truncated to the data buffer size.
+The
+.Nm
+utility then performs the requested
+.Xr read 2
+and responds with
+.Sm off
+.Sy A Ar count-read No \en
+.Sm on
+if the read was
+successful; otherwise an error in the
+standard format is returned.
+If the read
+was successful, the data read is then sent.
+.Sm off
+.It Xo Sy I Ar operation
+.No \en Ar count No \en
+.Xc
+.Sm on
+Perform a
+.Dv MTIOCOP
+.Xr ioctl 2
+command using the specified parameters.
+The parameters are interpreted as the
+.Tn ASCII
+representations of the decimal values
+to place in the
+.Ar mt_op
+and
+.Ar mt_count
+fields of the structure used in the
+.Xr ioctl 2
+call.
+The return value is the
+.Ar count
+parameter when the operation is successful.
+.It Sy S
+Return the status of the open device, as
+obtained with a
+.Dv MTIOCGET
+.Xr ioctl 2
+call.
+If the operation was successful,
+an ``ack'' is sent with the size of the
+status buffer, then the status buffer is
+sent (in binary).
+.El
+.Pp
+Any other command causes
+.Nm
+to exit.
+.Sh DIAGNOSTICS
+All responses are of the form described above.
+.Sh SEE ALSO
+.Xr rcmd 3 ,
+.Xr mtio 4 ,
+.Xr rdump 8 ,
+.Xr rrestore 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.2 .
+.Sh BUGS
+People should be discouraged from using this for a remote
+file access protocol.
diff --git a/usr.sbin/rmt/rmt.c b/usr.sbin/rmt/rmt.c
new file mode 100644
index 0000000..af4f954
--- /dev/null
+++ b/usr.sbin/rmt/rmt.c
@@ -0,0 +1,254 @@
+/*
+ * 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[] = "@(#)rmt.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * rmt
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/mtio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int tape = -1;
+
+char *record;
+int maxrecsize = -1;
+
+#define SSIZE 64
+char device[SSIZE];
+char count[SSIZE], mode[SSIZE], pos[SSIZE], op[SSIZE];
+
+char resp[BUFSIZ];
+
+FILE *debug;
+#define DEBUG(f) if (debug) fprintf(debug, f)
+#define DEBUG1(f,a) if (debug) fprintf(debug, f, a)
+#define DEBUG2(f,a1,a2) if (debug) fprintf(debug, f, a1, a2)
+
+char *checkbuf(char *, int);
+void error(int);
+void getstring(char *);
+
+int
+main(int argc, char **argv)
+{
+ int rval;
+ char c;
+ int n, i, cc;
+
+ argc--, argv++;
+ if (argc > 0) {
+ debug = fopen(*argv, "w");
+ if (debug == 0)
+ exit(1);
+ (void)setbuf(debug, (char *)0);
+ }
+top:
+ errno = 0;
+ rval = 0;
+ if (read(STDIN_FILENO, &c, 1) != 1)
+ exit(0);
+ switch (c) {
+
+ case 'O':
+ if (tape >= 0)
+ (void) close(tape);
+ getstring(device);
+ getstring(mode);
+ DEBUG2("rmtd: O %s %s\n", device, mode);
+ /*
+ * XXX the rmt protocol does not provide a means to
+ * specify the permission bits; allow rw for everyone,
+ * as modified by the users umask
+ */
+ tape = open(device, atoi(mode), 0666);
+ if (tape < 0)
+ goto ioerror;
+ goto respond;
+
+ case 'C':
+ DEBUG("rmtd: C\n");
+ getstring(device); /* discard */
+ if (close(tape) < 0)
+ goto ioerror;
+ tape = -1;
+ goto respond;
+
+ case 'L':
+ getstring(count);
+ getstring(pos);
+ DEBUG2("rmtd: L %s %s\n", count, pos);
+ rval = lseek(tape, (off_t)strtoll(count, NULL, 10), atoi(pos));
+ if (rval < 0)
+ goto ioerror;
+ goto respond;
+
+ case 'W':
+ getstring(count);
+ n = atoi(count);
+ DEBUG1("rmtd: W %s\n", count);
+ record = checkbuf(record, n);
+ for (i = 0; i < n; i += cc) {
+ cc = read(STDIN_FILENO, &record[i], n - i);
+ if (cc <= 0) {
+ DEBUG("rmtd: premature eof\n");
+ exit(2);
+ }
+ }
+ rval = write(tape, record, n);
+ if (rval < 0)
+ goto ioerror;
+ goto respond;
+
+ case 'R':
+ getstring(count);
+ DEBUG1("rmtd: R %s\n", count);
+ n = atoi(count);
+ record = checkbuf(record, n);
+ rval = read(tape, record, n);
+ if (rval < 0)
+ goto ioerror;
+ (void)sprintf(resp, "A%d\n", rval);
+ (void)write(STDOUT_FILENO, resp, strlen(resp));
+ (void)write(STDOUT_FILENO, record, rval);
+ goto top;
+
+ case 'I':
+ getstring(op);
+ getstring(count);
+ DEBUG2("rmtd: I %s %s\n", op, count);
+ { struct mtop mtop;
+ mtop.mt_op = atoi(op);
+ mtop.mt_count = atoi(count);
+ if (ioctl(tape, MTIOCTOP, (char *)&mtop) < 0)
+ goto ioerror;
+ rval = mtop.mt_count;
+ }
+ goto respond;
+
+ case 'S': /* status */
+ DEBUG("rmtd: S\n");
+ { struct mtget mtget;
+ if (ioctl(tape, MTIOCGET, (char *)&mtget) < 0)
+ goto ioerror;
+ rval = sizeof (mtget);
+ if (rval > 24) /* original mtget structure size */
+ rval = 24;
+ (void)sprintf(resp, "A%d\n", rval);
+ (void)write(STDOUT_FILENO, resp, strlen(resp));
+ (void)write(STDOUT_FILENO, (char *)&mtget, rval);
+ goto top;
+ }
+
+ case 'V': /* version */
+ getstring(op);
+ DEBUG1("rmtd: V %s\n", op);
+ rval = 2;
+ goto respond;
+
+ default:
+ DEBUG1("rmtd: garbage command %c\n", c);
+ exit(3);
+ }
+respond:
+ DEBUG1("rmtd: A %d\n", rval);
+ (void)sprintf(resp, "A%d\n", rval);
+ (void)write(STDOUT_FILENO, resp, strlen(resp));
+ goto top;
+ioerror:
+ error(errno);
+ goto top;
+}
+
+void
+getstring(bp)
+ char *bp;
+{
+ int i;
+ char *cp = bp;
+
+ for (i = 0; i < SSIZE; i++) {
+ if (read(STDIN_FILENO, cp+i, 1) != 1)
+ exit(0);
+ if (cp[i] == '\n')
+ break;
+ }
+ cp[i] = '\0';
+}
+
+char *
+checkbuf(rec, size)
+ char *rec;
+ int size;
+{
+
+ if (size <= maxrecsize)
+ return (rec);
+ if (rec != 0)
+ free(rec);
+ rec = malloc(size);
+ if (rec == 0) {
+ DEBUG("rmtd: cannot allocate buffer space\n");
+ exit(4);
+ }
+ maxrecsize = size;
+ while (size > 1024 &&
+ setsockopt(0, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size)) < 0)
+ size -= 1024;
+ return (rec);
+}
+
+void
+error(num)
+ int num;
+{
+
+ DEBUG2("rmtd: E %d (%s)\n", num, strerror(num));
+ (void)snprintf(resp, sizeof(resp), "E%d\n%s\n", num, strerror(num));
+ (void)write(STDOUT_FILENO, resp, strlen(resp));
+}
diff --git a/usr.sbin/rndc-confgen/Makefile b/usr.sbin/rndc-confgen/Makefile
new file mode 100644
index 0000000..b8a52dd
--- /dev/null
+++ b/usr.sbin/rndc-confgen/Makefile
@@ -0,0 +1,29 @@
+# $FreeBSD$
+
+BIND_DIR= ${.CURDIR}/../../contrib/bind9
+LIB_BIND_REL= ../../lib/bind
+LIB_BIND_DIR= ${.CURDIR}/${LIB_BIND_REL}
+SRCDIR= ${BIND_DIR}/bin/rndc
+
+.include "${LIB_BIND_DIR}/config.mk"
+
+PROG= rndc-confgen
+
+.PATH: ${SRCDIR}/unix
+SRCS+= os.c
+
+.PATH: ${SRCDIR}
+SRCS+= rndc-confgen.c util.c
+
+CFLAGS+= -I${SRCDIR}/unix/include -I${SRCDIR}/include -I${LIB_BIND_DIR}
+
+DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD}
+LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD}
+
+MAN= rndc-confgen.8
+
+MANFILTER= sed -e 's@fI/etc\\fR.*@fI/etc/namedb\\fR@' \
+ -e '/^sysconfdir$$/d' \
+ -e '/was specified as when BIND was built)/d'
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rndc/Makefile b/usr.sbin/rndc/Makefile
new file mode 100644
index 0000000..2ca7697
--- /dev/null
+++ b/usr.sbin/rndc/Makefile
@@ -0,0 +1,28 @@
+# $FreeBSD$
+
+BIND_DIR= ${.CURDIR}/../../contrib/bind9
+LIB_BIND_REL= ../../lib/bind
+LIB_BIND_DIR= ${.CURDIR}/${LIB_BIND_REL}
+SRCDIR= ${BIND_DIR}/bin/rndc
+
+.include "${LIB_BIND_DIR}/config.mk"
+
+PROG= rndc
+
+.PATH: ${SRCDIR}/unix
+SRCS+= os.c
+
+.PATH: ${SRCDIR}
+SRCS+= rndc.c util.c
+
+CFLAGS+= -I${SRCDIR}/unix/include -I${SRCDIR}/include -I${LIB_BIND_DIR}
+
+DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD}
+LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD}
+
+MAN= rndc.8 rndc.conf.5
+
+MANFILTER= sed -e "s@/etc/rndc\.conf@/etc/namedb/rndc.conf@g" \
+ -e "s@/etc/rndc\.key@/etc/namedb/rndc.key@g"
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/route6d/Makefile b/usr.sbin/route6d/Makefile
new file mode 100644
index 0000000..308aad7
--- /dev/null
+++ b/usr.sbin/route6d/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $FreeBSD$
+
+PROG= route6d
+MAN= route6d.8
+
+CFLAGS+= -DHAVE_POLL_H
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/route6d/misc/chkrt b/usr.sbin/route6d/misc/chkrt
new file mode 100755
index 0000000..6ae01f3
--- /dev/null
+++ b/usr.sbin/route6d/misc/chkrt
@@ -0,0 +1,64 @@
+#!/usr/bin/perl
+#
+# $FreeBSD$
+#
+$dump="/var/tmp/route6d_dump";
+$pidfile="/var/run/route6d.pid";
+
+system("rm -f $dump");
+
+open(FD, "< $pidfile") || die "Can not open $pidfile";
+$_ = <FD>;
+chop;
+close(FD);
+system("kill -INT $_");
+
+open(NS, "/usr/bin/netstat -r -n|") || die "Can not open netstat";
+while (<NS>) {
+ chop;
+ next unless (/^3f/ || /^5f/);
+ @f = split(/\s+/);
+ $gw{$f[0]} = $f[1];
+ $int{$f[0]} = $f[3];
+}
+close(NS);
+
+$err=0;
+sleep(2);
+open(FD, "< $dump") || die "Can not open $dump";
+while (<FD>) {
+ chop;
+ next unless (/^ 3f/ || /^ 5f/);
+ @f = split(/\s+/);
+ $dst = $f[1];
+ $f[2] =~ /if\(\d:([a-z0-9]+)\)/;
+ $intf = $1;
+ $f[3] =~ /gw\(([a-z0-9:]+)\)/;
+ $gateway = $1;
+ $f[4] =~ /\[(\d+)\]/;
+ $metric = $1;
+ $f[5] =~ /age\((\d+)\)/;
+ $age = $1;
+ unless (defined($gw{$dst})) {
+ print "NOT FOUND: $dst $intf $gateway $metric $age\n";
+ $err++;
+ next;
+ }
+ if ($gw{$dst} ne $gateway && $gw{$dst} !~ /link#\d+/) {
+ print "WRONG GW: $dst $intf $gateway $metric $age\n";
+ print "kernel gw: $gw{$dst}\n";
+ $err++;
+ next;
+ }
+ if ($int{$dst} ne $intf) {
+ print "WRONG IF: $dst $intf $gateway $metric $age\n";
+ print "kernel if: $int{$dst}\n";
+ $err++;
+ next;
+ }
+}
+close(FD);
+
+if ($err == 0) {
+ print "No error found\n";
+}
diff --git a/usr.sbin/route6d/misc/cksum.c b/usr.sbin/route6d/misc/cksum.c
new file mode 100644
index 0000000..bff7e2d
--- /dev/null
+++ b/usr.sbin/route6d/misc/cksum.c
@@ -0,0 +1,52 @@
+/*
+ * 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$
+ */
+
+#include <stdio.h>
+
+unsigned short buf[BUFSIZ];
+
+main()
+{
+ int i;
+ unsigned short *p = buf, *q = &buf[4];
+ unsigned long sum, sum2;
+
+ while (scanf("%x", &i) != EOF) {
+ *p++ = i; printf("%d ", i);
+ }
+ printf("\n");
+
+ sum = buf[2] + (buf[3] >> 8) & 0xff;
+ while (q != p)
+ sum += (*q++ & 0xffff);
+ sum2 = (sum & 0xffff) + (sum >> 16) & 0xffff;
+ printf("%x, %x\n", sum, sum2);
+}
diff --git a/usr.sbin/route6d/route6d.8 b/usr.sbin/route6d/route6d.8
new file mode 100644
index 0000000..121f5f2
--- /dev/null
+++ b/usr.sbin/route6d/route6d.8
@@ -0,0 +1,255 @@
+.\" $KAME: route6d.8,v 1.10 2000/11/24 11:57:18 itojun Exp $
+.\"
+.\" 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$
+.\"
+.Dd January 31, 1997
+.Dt ROUTE6D 8
+.Os
+.Sh NAME
+.Nm route6d
+.Nd RIP6 Routing Daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl adDhlnqsS
+.Bk -words
+.Op Fl R Ar routelog
+.Ek
+.Bk -words
+.Op Fl A Ar prefix/preflen,if1[,if2...\&]
+.Ek
+.Bk -words
+.Op Fl L Ar prefix/preflen,if1[,if2...\&]
+.Ek
+.Bk -words
+.Op Fl N Ar if1[,if2...\&]
+.Ek
+.Bk -words
+.Op Fl O Ar prefix/preflen,if1[,if2...\&]
+.Ek
+.Bk -words
+.Op Fl T Ar if1[,if2...\&]
+.Ek
+.Bk -words
+.Op Fl t Ar tag
+.Ek
+.\"
+.Sh DESCRIPTION
+The
+.Nm
+utility is a routing daemon which supports RIP over IPv6.
+.Pp
+Options are:
+.Bl -tag -width indent
+.\"
+.It Fl a
+Enables aging of the statically defined routes.
+With this option, any
+statically defined routes will be removed unless corresponding updates
+arrive as if the routes are received at the startup of
+.Nm .
+.\"
+.It Fl R Ar routelog
+This option makes the
+.Nm
+to log the route change (add/delete) to the file
+.Ar routelog .
+.\"
+.It Fl A Ar prefix/preflen,if1[,if2...]
+This option is used for aggregating routes.
+.Ar prefix/preflen
+specifies the prefix and the prefix length of the
+aggregated route.
+When advertising routes,
+.Nm
+filters specific routes covered by the aggregate,
+and advertises the aggregated route
+.Ar prefix/preflen ,
+to the interfaces specified in the comma-separated interface list,
+.Ar if1[,if2...] .
+The
+.Nm
+utility creates a static route to
+.Ar prefix/preflen
+with
+.Dv RTF_REJECT
+flag, into the kernel routing table.
+.\"
+.It Fl d
+Enables output of debugging message.
+This option also instructs
+.Nm
+to run in foreground mode
+(does not become daemon).
+.\"
+.It Fl D
+Enables extensive output of debugging message.
+This option also instructs
+.Nm
+to run in foreground mode
+(does not become daemon).
+.\"
+.It Fl h
+Disables the split horizon processing.
+.\"
+.It Fl l
+By default,
+.Nm
+will not exchange site local routes for safety reasons.
+This is because semantics of site local address space is rather vague
+(specification is still in being worked),
+and there is no good way to define site local boundary.
+With
+.Fl l
+option,
+.Nm
+will exchange site local routes as well.
+It must not be used on site boundary routers,
+since
+.Fl l
+option assumes that all interfaces are in the same site.
+.\"
+.It Fl L Ar prefix/preflen,if1[,if2...]
+Filter incoming routes from interfaces
+.Ar if1,[if2...] .
+The
+.Nm
+utility will accept incoming routes that are in
+.Ar prefix/preflen .
+If multiple
+.Fl L
+options are specified, any routes that match one of the options is accepted.
+.Li ::/0
+is treated specially as default route, not
+.Do
+any route that has longer prefix length than, or equal to 0
+.Dc .
+If you would like to accept any route, specify no
+.Fl L
+option.
+For example, with
+.Do
+.Fl L
+.Li 3ffe::/16,if1
+.Fl L
+.Li ::/0,if1
+.Dc
+.Nm
+will accept default route and routes in 6bone test address, but no others.
+.\"
+.It Fl n
+Do not update the kernel routing table.
+.\"
+.It Fl N Ar if1[,if2...]
+Do not listen to, or advertise, route from/to interfaces specified by
+.Ar if1,[if2...] .
+.\"
+.It Fl O Ar prefix/preflen,if1[,if2...]
+Restrict route advertisement toward interfaces specified by
+.Ar if1,[if2...] .
+With this option
+.Nm
+will only advertise routes that matches
+.Ar prefix/preflen .
+.\"
+.It Fl q
+Makes
+.Nm
+in listen-only mode.
+No advertisement is sent.
+.\"
+.It Fl s
+Makes
+.Nm
+to advertise the statically defined routes which exist in the kernel routing
+table when
+.Nm
+invoked.
+Announcements obey the regular split horizon rule.
+.\"
+.It Fl S
+This option is the same as
+.Fl s
+option except that no split horizon rule does apply.
+.\"
+.It Fl T Ar if1[,if2...]
+Advertise only default route, toward
+.Ar if1,[if2...] .
+.\"
+.It Fl t Ar tag
+Attach route tag
+.Ar tag
+to originated route entries.
+.Ar tag
+can be decimal, octal prefixed by
+.Li 0 ,
+or hexadecimal prefixed by
+.Li 0x .
+.\"
+.El
+.Pp
+Upon receipt of signal
+.Dv SIGINT
+or
+.Dv SIGUSR1 ,
+.Nm
+will dump the current internal state into
+.Pa /var/run/route6d_dump .
+.\"
+.Sh FILES
+.Bl -tag -width /var/run/route6d_dump -compact
+.It Pa /var/run/route6d_dump
+dumps internal state on
+.Dv SIGINT
+or
+.Dv SIGUSR1
+.El
+.\"
+.Sh SEE ALSO
+.Rs
+.%A G. Malkin
+.%A R. Minnear
+.%T RIPng for IPv6
+.%R RFC2080
+.%D January 1997
+.Re
+.\"
+.Sh NOTE
+The
+.Nm
+utility uses IPv6 advanced API,
+defined in RFC2292,
+for communicating with peers using link-local addresses.
+.Pp
+Internally
+.Nm
+embeds interface identifier into bit 32 to 63 of link-local addresses
+.Li ( fe80::xx
+and
+.Li ff02::xx )
+so they will be visible on internal state dump file
+.Pq Pa /var/run/route6d_dump .
+.Pp
+Routing table manipulation differs from IPv6 implementation to implementation.
+Currently
+.Nm
+obeys WIDE Hydrangea/KAME IPv6 kernel,
+and will not be able to run on other platforms.
+.Pp
+Current
+.Nm
+does not reduce the rate of the triggered updates when consecutive updates
+arrive.
diff --git a/usr.sbin/route6d/route6d.c b/usr.sbin/route6d/route6d.c
new file mode 100644
index 0000000..0205a03
--- /dev/null
+++ b/usr.sbin/route6d/route6d.c
@@ -0,0 +1,3631 @@
+/* $FreeBSD$ */
+/* $KAME: route6d.c,v 1.104 2003/10/31 00:30:20 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.
+ */
+
+#ifndef lint
+static char _rcsid[] = "$KAME: route6d.c,v 1.104 2003/10/31 00:30:20 itojun Exp $";
+#endif
+
+#include <stdio.h>
+
+#include <time.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include <syslog.h>
+#include <stddef.h>
+#include <errno.h>
+#include <err.h>
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+#include <sys/uio.h>
+#include <net/if.h>
+#include <net/if_var.h>
+#define _KERNEL 1
+#include <net/route.h>
+#undef _KERNEL
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/ip6.h>
+#include <netinet/udp.h>
+#include <netdb.h>
+#include <ifaddrs.h>
+
+#include <arpa/inet.h>
+
+#include "route6d.h"
+
+#define MAXFILTER 40
+
+#ifdef DEBUG
+#define INIT_INTERVAL6 6
+#else
+#define INIT_INTERVAL6 10 /* Wait to submit an initial riprequest */
+#endif
+
+/* alignment constraint for routing socket */
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+
+/*
+ * Following two macros are highly depending on KAME Release
+ */
+#define IN6_LINKLOCAL_IFINDEX(addr) \
+ ((addr).s6_addr[2] << 8 | (addr).s6_addr[3])
+
+#define SET_IN6_LINKLOCAL_IFINDEX(addr, index) \
+ do { \
+ (addr).s6_addr[2] = ((index) >> 8) & 0xff; \
+ (addr).s6_addr[3] = (index) & 0xff; \
+ } while (0)
+
+struct ifc { /* Configuration of an interface */
+ char *ifc_name; /* if name */
+ struct ifc *ifc_next;
+ int ifc_index; /* if index */
+ int ifc_mtu; /* if mtu */
+ int ifc_metric; /* if metric */
+ u_int ifc_flags; /* flags */
+ short ifc_cflags; /* IFC_XXX */
+ struct in6_addr ifc_mylladdr; /* my link-local address */
+ struct sockaddr_in6 ifc_ripsin; /* rip multicast address */
+ struct iff *ifc_filter; /* filter structure */
+ struct ifac *ifc_addr; /* list of AF_INET6 addresses */
+ int ifc_joined; /* joined to ff02::9 */
+};
+
+struct ifac { /* Adddress associated to an interface */
+ struct ifc *ifa_conf; /* back pointer */
+ struct ifac *ifa_next;
+ struct in6_addr ifa_addr; /* address */
+ struct in6_addr ifa_raddr; /* remote address, valid in p2p */
+ int ifa_plen; /* prefix length */
+};
+
+struct iff {
+ int iff_type;
+ struct in6_addr iff_addr;
+ int iff_plen;
+ struct iff *iff_next;
+};
+
+struct ifc *ifc;
+int nifc; /* number of valid ifc's */
+struct ifc **index2ifc;
+int nindex2ifc;
+struct ifc *loopifcp = NULL; /* pointing to loopback */
+#ifdef HAVE_POLL_H
+struct pollfd set[2];
+#else
+fd_set *sockvecp; /* vector to select() for receiving */
+fd_set *recvecp;
+int fdmasks;
+int maxfd; /* maximum fd for select() */
+#endif
+int rtsock; /* the routing socket */
+int ripsock; /* socket to send/receive RIP datagram */
+
+struct rip6 *ripbuf; /* packet buffer for sending */
+
+/*
+ * Maintain the routes in a linked list. When the number of the routes
+ * grows, somebody would like to introduce a hash based or a radix tree
+ * based structure. I believe the number of routes handled by RIP is
+ * limited and I don't have to manage a complex data structure, however.
+ *
+ * One of the major drawbacks of the linear linked list is the difficulty
+ * of representing the relationship between a couple of routes. This may
+ * be a significant problem when we have to support route aggregation with
+ * supressing the specifices covered by the aggregate.
+ */
+
+struct riprt {
+ struct riprt *rrt_next; /* next destination */
+ struct riprt *rrt_same; /* same destination - future use */
+ struct netinfo6 rrt_info; /* network info */
+ struct in6_addr rrt_gw; /* gateway */
+ u_long rrt_flags; /* kernel routing table flags */
+ u_long rrt_rflags; /* route6d routing table flags */
+ time_t rrt_t; /* when the route validated */
+ int rrt_index; /* ifindex from which this route got */
+};
+
+struct riprt *riprt = 0;
+
+int dflag = 0; /* debug flag */
+int qflag = 0; /* quiet flag */
+int nflag = 0; /* don't update kernel routing table */
+int aflag = 0; /* age out even the statically defined routes */
+int hflag = 0; /* don't split horizon */
+int lflag = 0; /* exchange site local routes */
+int sflag = 0; /* announce static routes w/ split horizon */
+int Sflag = 0; /* announce static routes to every interface */
+unsigned long routetag = 0; /* route tag attached on originating case */
+
+char *filter[MAXFILTER];
+int filtertype[MAXFILTER];
+int nfilter = 0;
+
+pid_t pid;
+
+struct sockaddr_storage ripsin;
+
+int interval = 1;
+time_t nextalarm = 0;
+time_t sup_trig_update = 0;
+
+FILE *rtlog = NULL;
+
+int logopened = 0;
+
+static int seq = 0;
+
+volatile sig_atomic_t seenalrm;
+volatile sig_atomic_t seenquit;
+volatile sig_atomic_t seenusr1;
+
+#define RRTF_AGGREGATE 0x08000000
+#define RRTF_NOADVERTISE 0x10000000
+#define RRTF_NH_NOT_LLADDR 0x20000000
+#define RRTF_SENDANYWAY 0x40000000
+#define RRTF_CHANGED 0x80000000
+
+int main(int, char **);
+void sighandler(int);
+void ripalarm(void);
+void riprecv(void);
+void ripsend(struct ifc *, struct sockaddr_in6 *, int);
+int out_filter(struct riprt *, struct ifc *);
+void init(void);
+void sockopt(struct ifc *);
+void ifconfig(void);
+void ifconfig1(const char *, const struct sockaddr *, struct ifc *, int);
+void rtrecv(void);
+int rt_del(const struct sockaddr_in6 *, const struct sockaddr_in6 *,
+ const struct sockaddr_in6 *);
+int rt_deladdr(struct ifc *, const struct sockaddr_in6 *,
+ const struct sockaddr_in6 *);
+void filterconfig(void);
+int getifmtu(int);
+const char *rttypes(struct rt_msghdr *);
+const char *rtflags(struct rt_msghdr *);
+const char *ifflags(int);
+int ifrt(struct ifc *, int);
+void ifrt_p2p(struct ifc *, int);
+void applymask(struct in6_addr *, struct in6_addr *);
+void applyplen(struct in6_addr *, int);
+void ifrtdump(int);
+void ifdump(int);
+void ifdump0(FILE *, const struct ifc *);
+void rtdump(int);
+void rt_entry(struct rt_msghdr *, int);
+void rtdexit(void);
+void riprequest(struct ifc *, struct netinfo6 *, int,
+ struct sockaddr_in6 *);
+void ripflush(struct ifc *, struct sockaddr_in6 *);
+void sendrequest(struct ifc *);
+int sin6mask2len(const struct sockaddr_in6 *);
+int mask2len(const struct in6_addr *, int);
+int sendpacket(struct sockaddr_in6 *, int);
+int addroute(struct riprt *, const struct in6_addr *, struct ifc *);
+int delroute(struct netinfo6 *, struct in6_addr *);
+struct in6_addr *getroute(struct netinfo6 *, struct in6_addr *);
+void krtread(int);
+int tobeadv(struct riprt *, struct ifc *);
+char *allocopy(char *);
+char *hms(void);
+const char *inet6_n2p(const struct in6_addr *);
+struct ifac *ifa_match(const struct ifc *, const struct in6_addr *, int);
+struct in6_addr *plen2mask(int);
+struct riprt *rtsearch(struct netinfo6 *, struct riprt **);
+int ripinterval(int);
+time_t ripsuptrig(void);
+void fatal(const char *, ...)
+ __attribute__((__format__(__printf__, 1, 2)));
+void trace(int, const char *, ...)
+ __attribute__((__format__(__printf__, 2, 3)));
+void tracet(int, const char *, ...)
+ __attribute__((__format__(__printf__, 2, 3)));
+unsigned int if_maxindex(void);
+struct ifc *ifc_find(char *);
+struct iff *iff_find(struct ifc *, int);
+void setindex2ifc(int, struct ifc *);
+
+#define MALLOC(type) ((type *)malloc(sizeof(type)))
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int ch;
+ int error = 0;
+ struct ifc *ifcp;
+ sigset_t mask, omask;
+ FILE *pidfile;
+ char *progname;
+ char *ep;
+
+ progname = strrchr(*argv, '/');
+ if (progname)
+ progname++;
+ else
+ progname = *argv;
+
+ pid = getpid();
+ while ((ch = getopt(argc, argv, "A:N:O:R:T:L:t:adDhlnqsS")) != -1) {
+ switch (ch) {
+ case 'A':
+ case 'N':
+ case 'O':
+ case 'T':
+ case 'L':
+ if (nfilter >= MAXFILTER) {
+ fatal("Exceeds MAXFILTER");
+ /*NOTREACHED*/
+ }
+ filtertype[nfilter] = ch;
+ filter[nfilter++] = allocopy(optarg);
+ break;
+ case 't':
+ ep = NULL;
+ routetag = strtoul(optarg, &ep, 0);
+ if (!ep || *ep != '\0' || (routetag & ~0xffff) != 0) {
+ fatal("invalid route tag");
+ /*NOTREACHED*/
+ }
+ break;
+ case 'R':
+ if ((rtlog = fopen(optarg, "w")) == NULL) {
+ fatal("Can not write to routelog");
+ /*NOTREACHED*/
+ }
+ break;
+#define FLAG(c, flag, n) case c: do { flag = n; break; } while(0)
+ FLAG('a', aflag, 1); break;
+ FLAG('d', dflag, 1); break;
+ FLAG('D', dflag, 2); break;
+ FLAG('h', hflag, 1); break;
+ FLAG('l', lflag, 1); break;
+ FLAG('n', nflag, 1); break;
+ FLAG('q', qflag, 1); break;
+ FLAG('s', sflag, 1); break;
+ FLAG('S', Sflag, 1); break;
+#undef FLAG
+ default:
+ fatal("Invalid option specified, terminating");
+ /*NOTREACHED*/
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc > 0) {
+ fatal("bogus extra arguments");
+ /*NOTREACHED*/
+ }
+
+ if (geteuid()) {
+ nflag = 1;
+ fprintf(stderr, "No kernel update is allowed\n");
+ }
+
+ if (dflag == 0) {
+ if (daemon(0, 0) < 0) {
+ fatal("daemon");
+ /*NOTREACHED*/
+ }
+ }
+
+ openlog(progname, LOG_NDELAY|LOG_PID, LOG_DAEMON);
+ logopened++;
+
+ if ((ripbuf = (struct rip6 *)malloc(RIP6_MAXMTU)) == NULL)
+ fatal("malloc");
+ memset(ripbuf, 0, RIP6_MAXMTU);
+ ripbuf->rip6_cmd = RIP6_RESPONSE;
+ ripbuf->rip6_vers = RIP6_VERSION;
+ ripbuf->rip6_res1[0] = 0;
+ ripbuf->rip6_res1[1] = 0;
+
+ init();
+ ifconfig();
+ for (ifcp = ifc; ifcp; ifcp = ifcp->ifc_next) {
+ if (ifcp->ifc_index < 0) {
+ fprintf(stderr,
+"No ifindex found at %s (no link-local address?)\n",
+ ifcp->ifc_name);
+ error++;
+ }
+ }
+ if (error)
+ exit(1);
+ if (loopifcp == NULL) {
+ fatal("No loopback found");
+ /*NOTREACHED*/
+ }
+ for (ifcp = ifc; ifcp; ifcp = ifcp->ifc_next)
+ ifrt(ifcp, 0);
+ filterconfig();
+ krtread(0);
+ if (dflag)
+ ifrtdump(0);
+
+#if 1
+ pid = getpid();
+ if ((pidfile = fopen(ROUTE6D_PID, "w")) != NULL) {
+ fprintf(pidfile, "%d\n", pid);
+ fclose(pidfile);
+ }
+#endif
+
+ if ((ripbuf = (struct rip6 *)malloc(RIP6_MAXMTU)) == NULL) {
+ fatal("malloc");
+ /*NOTREACHED*/
+ }
+ memset(ripbuf, 0, RIP6_MAXMTU);
+ ripbuf->rip6_cmd = RIP6_RESPONSE;
+ ripbuf->rip6_vers = RIP6_VERSION;
+ ripbuf->rip6_res1[0] = 0;
+ ripbuf->rip6_res1[1] = 0;
+
+ if (signal(SIGALRM, sighandler) == SIG_ERR ||
+ signal(SIGQUIT, sighandler) == SIG_ERR ||
+ signal(SIGTERM, sighandler) == SIG_ERR ||
+ signal(SIGUSR1, sighandler) == SIG_ERR ||
+ signal(SIGHUP, sighandler) == SIG_ERR ||
+ signal(SIGINT, sighandler) == SIG_ERR) {
+ fatal("signal");
+ /*NOTREACHED*/
+ }
+ /*
+ * To avoid rip packet congestion (not on a cable but in this
+ * process), wait for a moment to send the first RIP6_RESPONSE
+ * packets.
+ */
+ alarm(ripinterval(INIT_INTERVAL6));
+
+ for (ifcp = ifc; ifcp; ifcp = ifcp->ifc_next) {
+ if (iff_find(ifcp, 'N'))
+ continue;
+ if (ifcp->ifc_index > 0 && (ifcp->ifc_flags & IFF_UP))
+ sendrequest(ifcp);
+ }
+
+ syslog(LOG_INFO, "**** Started ****");
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGALRM);
+ while (1) {
+ if (seenalrm) {
+ ripalarm();
+ seenalrm = 0;
+ continue;
+ }
+ if (seenquit) {
+ rtdexit();
+ seenquit = 0;
+ continue;
+ }
+ if (seenusr1) {
+ ifrtdump(SIGUSR1);
+ seenusr1 = 0;
+ continue;
+ }
+
+#ifdef HAVE_POLL_H
+ switch (poll(set, 2, INFTIM))
+#else
+ memcpy(recvecp, sockvecp, fdmasks);
+ switch (select(maxfd + 1, recvecp, 0, 0, 0))
+#endif
+ {
+ case -1:
+ if (errno != EINTR) {
+ fatal("select");
+ /*NOTREACHED*/
+ }
+ continue;
+ case 0:
+ continue;
+ default:
+#ifdef HAVE_POLL_H
+ if (set[0].revents & POLLIN)
+#else
+ if (FD_ISSET(ripsock, recvecp))
+#endif
+ {
+ sigprocmask(SIG_BLOCK, &mask, &omask);
+ riprecv();
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+ }
+#ifdef HAVE_POLL_H
+ if (set[1].revents & POLLIN)
+#else
+ if (FD_ISSET(rtsock, recvecp))
+#endif
+ {
+ sigprocmask(SIG_BLOCK, &mask, &omask);
+ rtrecv();
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+ }
+ }
+ }
+}
+
+void
+sighandler(signo)
+ int signo;
+{
+
+ switch (signo) {
+ case SIGALRM:
+ seenalrm++;
+ break;
+ case SIGQUIT:
+ case SIGTERM:
+ seenquit++;
+ break;
+ case SIGUSR1:
+ case SIGHUP:
+ case SIGINT:
+ seenusr1++;
+ break;
+ }
+}
+
+/*
+ * gracefully exits after resetting sockopts.
+ */
+/* ARGSUSED */
+void
+rtdexit()
+{
+ struct riprt *rrt;
+
+ alarm(0);
+ for (rrt = riprt; rrt; rrt = rrt->rrt_next) {
+ if (rrt->rrt_rflags & RRTF_AGGREGATE) {
+ delroute(&rrt->rrt_info, &rrt->rrt_gw);
+ }
+ }
+ close(ripsock);
+ close(rtsock);
+ syslog(LOG_INFO, "**** Terminated ****");
+ closelog();
+ exit(1);
+}
+
+/*
+ * Called periodically:
+ * 1. age out the learned route. remove it if necessary.
+ * 2. submit RIP6_RESPONSE packets.
+ * Invoked in every SUPPLY_INTERVAL6 (30) seconds. I believe we don't have
+ * to invoke this function in every 1 or 5 or 10 seconds only to age the
+ * routes more precisely.
+ */
+/* ARGSUSED */
+void
+ripalarm()
+{
+ struct ifc *ifcp;
+ struct riprt *rrt, *rrt_prev, *rrt_next;
+ time_t t_lifetime, t_holddown;
+
+ /* age the RIP routes */
+ rrt_prev = 0;
+ t_lifetime = time(NULL) - RIP_LIFETIME;
+ t_holddown = t_lifetime - RIP_HOLDDOWN;
+ for (rrt = riprt; rrt; rrt = rrt_next) {
+ rrt_next = rrt->rrt_next;
+
+ if (rrt->rrt_t == 0) {
+ rrt_prev = rrt;
+ continue;
+ }
+ if (rrt->rrt_t < t_holddown) {
+ if (rrt_prev) {
+ rrt_prev->rrt_next = rrt->rrt_next;
+ } else {
+ riprt = rrt->rrt_next;
+ }
+ delroute(&rrt->rrt_info, &rrt->rrt_gw);
+ free(rrt);
+ continue;
+ }
+ if (rrt->rrt_t < t_lifetime)
+ rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6;
+ rrt_prev = rrt;
+ }
+ /* Supply updates */
+ for (ifcp = ifc; ifcp; ifcp = ifcp->ifc_next) {
+ if (ifcp->ifc_index > 0 && (ifcp->ifc_flags & IFF_UP))
+ ripsend(ifcp, &ifcp->ifc_ripsin, 0);
+ }
+ alarm(ripinterval(SUPPLY_INTERVAL6));
+}
+
+void
+init()
+{
+ int error;
+ const int int0 = 0, int1 = 1, int255 = 255;
+ struct addrinfo hints, *res;
+ char port[NI_MAXSERV];
+
+ ifc = (struct ifc *)NULL;
+ nifc = 0;
+ nindex2ifc = 0; /*initial guess*/
+ index2ifc = NULL;
+ snprintf(port, sizeof(port), "%u", RIP6_PORT);
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_INET6;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ hints.ai_flags = AI_PASSIVE;
+ error = getaddrinfo(NULL, port, &hints, &res);
+ if (error) {
+ fatal("%s", gai_strerror(error));
+ /*NOTREACHED*/
+ }
+ if (res->ai_next) {
+ fatal(":: resolved to multiple address");
+ /*NOTREACHED*/
+ }
+
+ ripsock = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (ripsock < 0) {
+ fatal("rip socket");
+ /*NOTREACHED*/
+ }
+#ifdef IPV6_V6ONLY
+ if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_V6ONLY,
+ &int1, sizeof(int1)) < 0) {
+ fatal("rip IPV6_V6ONLY");
+ /*NOTREACHED*/
+ }
+#endif
+ if (bind(ripsock, res->ai_addr, res->ai_addrlen) < 0) {
+ fatal("rip bind");
+ /*NOTREACHED*/
+ }
+ if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS,
+ &int255, sizeof(int255)) < 0) {
+ fatal("rip IPV6_MULTICAST_HOPS");
+ /*NOTREACHED*/
+ }
+ if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP,
+ &int0, sizeof(int0)) < 0) {
+ fatal("rip IPV6_MULTICAST_LOOP");
+ /*NOTREACHED*/
+ }
+
+#ifdef IPV6_RECVPKTINFO
+ if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_RECVPKTINFO,
+ &int1, sizeof(int1)) < 0) {
+ fatal("rip IPV6_RECVPKTINFO");
+ /*NOTREACHED*/
+ }
+#else /* old adv. API */
+ if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_PKTINFO,
+ &int1, sizeof(int1)) < 0) {
+ fatal("rip IPV6_PKTINFO");
+ /*NOTREACHED*/
+ }
+#endif
+
+#ifdef IPV6_RECVPKTINFO
+ if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT,
+ &int1, sizeof(int1)) < 0) {
+ fatal("rip IPV6_RECVHOPLIMIT");
+ /*NOTREACHED*/
+ }
+#else /* old adv. API */
+ if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_HOPLIMIT,
+ &int1, sizeof(int1)) < 0) {
+ fatal("rip IPV6_HOPLIMIT");
+ /*NOTREACHED*/
+ }
+#endif
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_INET6;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_protocol = IPPROTO_UDP;
+ error = getaddrinfo(RIP6_DEST, port, &hints, &res);
+ if (error) {
+ fatal("%s", gai_strerror(error));
+ /*NOTREACHED*/
+ }
+ if (res->ai_next) {
+ fatal("%s resolved to multiple address", RIP6_DEST);
+ /*NOTREACHED*/
+ }
+ memcpy(&ripsin, res->ai_addr, res->ai_addrlen);
+
+#ifdef HAVE_POLL_H
+ set[0].fd = ripsock;
+ set[0].events = POLLIN;
+#else
+ maxfd = ripsock;
+#endif
+
+ if (nflag == 0) {
+ if ((rtsock = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) {
+ fatal("route socket");
+ /*NOTREACHED*/
+ }
+#ifdef HAVE_POLL_H
+ set[1].fd = rtsock;
+ set[1].events = POLLIN;
+#else
+ if (rtsock > maxfd)
+ maxfd = rtsock;
+#endif
+ } else {
+#ifdef HAVE_POLL_H
+ set[1].fd = -1;
+#else
+ rtsock = -1; /*just for safety */
+#endif
+ }
+
+#ifndef HAVE_POLL_H
+ fdmasks = howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask);
+ if ((sockvecp = malloc(fdmasks)) == NULL) {
+ fatal("malloc");
+ /*NOTREACHED*/
+ }
+ if ((recvecp = malloc(fdmasks)) == NULL) {
+ fatal("malloc");
+ /*NOTREACHED*/
+ }
+ memset(sockvecp, 0, fdmasks);
+ FD_SET(ripsock, sockvecp);
+ if (rtsock >= 0)
+ FD_SET(rtsock, sockvecp);
+#endif
+}
+
+#define RIPSIZE(n) \
+ (sizeof(struct rip6) + ((n)-1) * sizeof(struct netinfo6))
+
+/*
+ * ripflush flushes the rip datagram stored in the rip buffer
+ */
+static int nrt;
+static struct netinfo6 *np;
+
+void
+ripflush(ifcp, sin6)
+ struct ifc *ifcp;
+ struct sockaddr_in6 *sin6;
+{
+ int i;
+ int error;
+
+ if (ifcp)
+ tracet(1, "Send(%s): info(%d) to %s.%d\n",
+ ifcp->ifc_name, nrt,
+ inet6_n2p(&sin6->sin6_addr), ntohs(sin6->sin6_port));
+ else
+ tracet(1, "Send: info(%d) to %s.%d\n",
+ nrt, inet6_n2p(&sin6->sin6_addr), ntohs(sin6->sin6_port));
+ if (dflag >= 2) {
+ np = ripbuf->rip6_nets;
+ for (i = 0; i < nrt; i++, np++) {
+ if (np->rip6_metric == NEXTHOP_METRIC) {
+ if (IN6_IS_ADDR_UNSPECIFIED(&np->rip6_dest))
+ trace(2, " NextHop reset");
+ else {
+ trace(2, " NextHop %s",
+ inet6_n2p(&np->rip6_dest));
+ }
+ } else {
+ trace(2, " %s/%d[%d]",
+ inet6_n2p(&np->rip6_dest),
+ np->rip6_plen, np->rip6_metric);
+ }
+ if (np->rip6_tag) {
+ trace(2, " tag=0x%04x",
+ ntohs(np->rip6_tag) & 0xffff);
+ }
+ trace(2, "\n");
+ }
+ }
+ error = sendpacket(sin6, RIPSIZE(nrt));
+ if (error == EAFNOSUPPORT) {
+ /* Protocol not supported */
+ tracet(1, "Could not send info to %s (%s): "
+ "set IFF_UP to 0\n",
+ ifcp->ifc_name, inet6_n2p(&ifcp->ifc_ripsin.sin6_addr));
+ ifcp->ifc_flags &= ~IFF_UP; /* As if down for AF_INET6 */
+ }
+ nrt = 0; np = ripbuf->rip6_nets;
+}
+
+/*
+ * Generate RIP6_RESPONSE packets and send them.
+ */
+void
+ripsend(ifcp, sin6, flag)
+ struct ifc *ifcp;
+ struct sockaddr_in6 *sin6;
+ int flag;
+{
+ struct riprt *rrt;
+ struct in6_addr *nh; /* next hop */
+ int maxrte;
+
+ if (qflag)
+ return;
+
+ if (ifcp == NULL) {
+ /*
+ * Request from non-link local address is not
+ * a regular route6d update.
+ */
+ maxrte = (IFMINMTU - sizeof(struct ip6_hdr) -
+ sizeof(struct udphdr) -
+ sizeof(struct rip6) + sizeof(struct netinfo6)) /
+ sizeof(struct netinfo6);
+ nrt = 0; np = ripbuf->rip6_nets; nh = NULL;
+ for (rrt = riprt; rrt; rrt = rrt->rrt_next) {
+ if (rrt->rrt_rflags & RRTF_NOADVERTISE)
+ continue;
+ /* Put the route to the buffer */
+ *np = rrt->rrt_info;
+ np++; nrt++;
+ if (nrt == maxrte) {
+ ripflush(NULL, sin6);
+ nh = NULL;
+ }
+ }
+ if (nrt) /* Send last packet */
+ ripflush(NULL, sin6);
+ return;
+ }
+
+ if ((flag & RRTF_SENDANYWAY) == 0 &&
+ (qflag || (ifcp->ifc_flags & IFF_LOOPBACK)))
+ return;
+
+ /* -N: no use */
+ if (iff_find(ifcp, 'N') != NULL)
+ return;
+
+ /* -T: generate default route only */
+ if (iff_find(ifcp, 'T') != NULL) {
+ struct netinfo6 rrt_info;
+ memset(&rrt_info, 0, sizeof(struct netinfo6));
+ rrt_info.rip6_dest = in6addr_any;
+ rrt_info.rip6_plen = 0;
+ rrt_info.rip6_metric = 1;
+ rrt_info.rip6_metric += ifcp->ifc_metric;
+ rrt_info.rip6_tag = htons(routetag & 0xffff);
+ np = ripbuf->rip6_nets;
+ *np = rrt_info;
+ nrt = 1;
+ ripflush(ifcp, sin6);
+ return;
+ }
+
+ maxrte = (ifcp->ifc_mtu - sizeof(struct ip6_hdr) -
+ sizeof(struct udphdr) -
+ sizeof(struct rip6) + sizeof(struct netinfo6)) /
+ sizeof(struct netinfo6);
+
+ nrt = 0; np = ripbuf->rip6_nets; nh = NULL;
+ for (rrt = riprt; rrt; rrt = rrt->rrt_next) {
+ if (rrt->rrt_rflags & RRTF_NOADVERTISE)
+ continue;
+
+ /* Need to check filter here */
+ if (out_filter(rrt, ifcp) == 0)
+ continue;
+
+ /* Check split horizon and other conditions */
+ if (tobeadv(rrt, ifcp) == 0)
+ continue;
+
+ /* Only considers the routes with flag if specified */
+ if ((flag & RRTF_CHANGED) &&
+ (rrt->rrt_rflags & RRTF_CHANGED) == 0)
+ continue;
+
+ /* Check nexthop */
+ if (rrt->rrt_index == ifcp->ifc_index &&
+ !IN6_IS_ADDR_UNSPECIFIED(&rrt->rrt_gw) &&
+ (rrt->rrt_rflags & RRTF_NH_NOT_LLADDR) == 0) {
+ if (nh == NULL || !IN6_ARE_ADDR_EQUAL(nh, &rrt->rrt_gw)) {
+ if (nrt == maxrte - 2)
+ ripflush(ifcp, sin6);
+ np->rip6_dest = rrt->rrt_gw;
+ if (IN6_IS_ADDR_LINKLOCAL(&np->rip6_dest))
+ SET_IN6_LINKLOCAL_IFINDEX(np->rip6_dest, 0);
+ np->rip6_plen = 0;
+ np->rip6_tag = 0;
+ np->rip6_metric = NEXTHOP_METRIC;
+ nh = &rrt->rrt_gw;
+ np++; nrt++;
+ }
+ } else if (nh && (rrt->rrt_index != ifcp->ifc_index ||
+ !IN6_ARE_ADDR_EQUAL(nh, &rrt->rrt_gw) ||
+ rrt->rrt_rflags & RRTF_NH_NOT_LLADDR)) {
+ /* Reset nexthop */
+ if (nrt == maxrte - 2)
+ ripflush(ifcp, sin6);
+ memset(np, 0, sizeof(struct netinfo6));
+ np->rip6_metric = NEXTHOP_METRIC;
+ nh = NULL;
+ np++; nrt++;
+ }
+
+ /* Put the route to the buffer */
+ *np = rrt->rrt_info;
+ np++; nrt++;
+ if (nrt == maxrte) {
+ ripflush(ifcp, sin6);
+ nh = NULL;
+ }
+ }
+ if (nrt) /* Send last packet */
+ ripflush(ifcp, sin6);
+}
+
+/*
+ * outbound filter logic, per-route/interface.
+ */
+int
+out_filter(rrt, ifcp)
+ struct riprt *rrt;
+ struct ifc *ifcp;
+{
+ struct iff *iffp;
+ struct in6_addr ia;
+ int ok;
+
+ /*
+ * -A: filter out less specific routes, if we have aggregated
+ * route configured.
+ */
+ for (iffp = ifcp->ifc_filter; iffp; iffp = iffp->iff_next) {
+ if (iffp->iff_type != 'A')
+ continue;
+ if (rrt->rrt_info.rip6_plen <= iffp->iff_plen)
+ continue;
+ ia = rrt->rrt_info.rip6_dest;
+ applyplen(&ia, iffp->iff_plen);
+ if (IN6_ARE_ADDR_EQUAL(&ia, &iffp->iff_addr))
+ return 0;
+ }
+
+ /*
+ * if it is an aggregated route, advertise it only to the
+ * interfaces specified on -A.
+ */
+ if ((rrt->rrt_rflags & RRTF_AGGREGATE) != 0) {
+ ok = 0;
+ for (iffp = ifcp->ifc_filter; iffp; iffp = iffp->iff_next) {
+ if (iffp->iff_type != 'A')
+ continue;
+ if (rrt->rrt_info.rip6_plen == iffp->iff_plen &&
+ IN6_ARE_ADDR_EQUAL(&rrt->rrt_info.rip6_dest,
+ &iffp->iff_addr)) {
+ ok = 1;
+ break;
+ }
+ }
+ if (!ok)
+ return 0;
+ }
+
+ /*
+ * -O: advertise only if prefix matches the configured prefix.
+ */
+ if (iff_find(ifcp, 'O')) {
+ ok = 0;
+ for (iffp = ifcp->ifc_filter; iffp; iffp = iffp->iff_next) {
+ if (iffp->iff_type != 'O')
+ continue;
+ if (rrt->rrt_info.rip6_plen < iffp->iff_plen)
+ continue;
+ ia = rrt->rrt_info.rip6_dest;
+ applyplen(&ia, iffp->iff_plen);
+ if (IN6_ARE_ADDR_EQUAL(&ia, &iffp->iff_addr)) {
+ ok = 1;
+ break;
+ }
+ }
+ if (!ok)
+ return 0;
+ }
+
+ /* the prefix should be advertised */
+ return 1;
+}
+
+/*
+ * Determine if the route is to be advertised on the specified interface.
+ * It checks options specified in the arguments and the split horizon rule.
+ */
+int
+tobeadv(rrt, ifcp)
+ struct riprt *rrt;
+ struct ifc *ifcp;
+{
+
+ /* Special care for static routes */
+ if (rrt->rrt_flags & RTF_STATIC) {
+ /* XXX don't advertise reject/blackhole routes */
+ if (rrt->rrt_flags & (RTF_REJECT | RTF_BLACKHOLE))
+ return 0;
+
+ if (Sflag) /* Yes, advertise it anyway */
+ return 1;
+ if (sflag && rrt->rrt_index != ifcp->ifc_index)
+ return 1;
+ return 0;
+ }
+ /* Regular split horizon */
+ if (hflag == 0 && rrt->rrt_index == ifcp->ifc_index)
+ return 0;
+ return 1;
+}
+
+/*
+ * Send a rip packet actually.
+ */
+int
+sendpacket(sin6, len)
+ struct sockaddr_in6 *sin6;
+ int len;
+{
+ struct msghdr m;
+ struct cmsghdr *cm;
+ struct iovec iov[2];
+ u_char cmsgbuf[256];
+ struct in6_pktinfo *pi;
+ int idx;
+ struct sockaddr_in6 sincopy;
+
+ /* do not overwrite the given sin */
+ sincopy = *sin6;
+ sin6 = &sincopy;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr) ||
+ IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
+ /* XXX: do not mix the interface index and link index */
+ idx = IN6_LINKLOCAL_IFINDEX(sin6->sin6_addr);
+ SET_IN6_LINKLOCAL_IFINDEX(sin6->sin6_addr, 0);
+ sin6->sin6_scope_id = idx;
+ } else
+ idx = 0;
+
+ m.msg_name = (caddr_t)sin6;
+ m.msg_namelen = sizeof(*sin6);
+ iov[0].iov_base = (caddr_t)ripbuf;
+ iov[0].iov_len = len;
+ m.msg_iov = iov;
+ m.msg_iovlen = 1;
+ if (!idx) {
+ m.msg_control = NULL;
+ m.msg_controllen = 0;
+ } else {
+ memset(cmsgbuf, 0, sizeof(cmsgbuf));
+ cm = (struct cmsghdr *)cmsgbuf;
+ m.msg_control = (caddr_t)cm;
+ m.msg_controllen = CMSG_SPACE(sizeof(struct in6_pktinfo));
+
+ cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+ cm->cmsg_level = IPPROTO_IPV6;
+ cm->cmsg_type = IPV6_PKTINFO;
+ pi = (struct in6_pktinfo *)CMSG_DATA(cm);
+ memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*::*/
+ pi->ipi6_ifindex = idx;
+ }
+
+ if (sendmsg(ripsock, &m, 0 /*MSG_DONTROUTE*/) < 0) {
+ trace(1, "sendmsg: %s\n", strerror(errno));
+ return errno;
+ }
+
+ return 0;
+}
+
+/*
+ * Receive and process RIP packets. Update the routes/kernel forwarding
+ * table if necessary.
+ */
+void
+riprecv()
+{
+ struct ifc *ifcp, *ic;
+ struct sockaddr_in6 fsock;
+ struct in6_addr nh; /* next hop */
+ struct rip6 *rp;
+ struct netinfo6 *np, *nq;
+ struct riprt *rrt;
+ ssize_t len, nn;
+ unsigned int need_trigger, idx;
+ char buf[4 * RIP6_MAXMTU];
+ time_t t;
+ struct msghdr m;
+ struct cmsghdr *cm;
+ struct iovec iov[2];
+ u_char cmsgbuf[256];
+ struct in6_pktinfo *pi = NULL;
+ int *hlimp = NULL;
+ struct iff *iffp;
+ struct in6_addr ia;
+ int ok;
+ time_t t_half_lifetime;
+
+ need_trigger = 0;
+
+ m.msg_name = (caddr_t)&fsock;
+ m.msg_namelen = sizeof(fsock);
+ iov[0].iov_base = (caddr_t)buf;
+ iov[0].iov_len = sizeof(buf);
+ m.msg_iov = iov;
+ m.msg_iovlen = 1;
+ cm = (struct cmsghdr *)cmsgbuf;
+ m.msg_control = (caddr_t)cm;
+ m.msg_controllen = sizeof(cmsgbuf);
+ if ((len = recvmsg(ripsock, &m, 0)) < 0) {
+ fatal("recvmsg");
+ /*NOTREACHED*/
+ }
+ idx = 0;
+ for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&m);
+ cm;
+ cm = (struct cmsghdr *)CMSG_NXTHDR(&m, cm)) {
+ if (cm->cmsg_level != IPPROTO_IPV6)
+ continue;
+ switch (cm->cmsg_type) {
+ case IPV6_PKTINFO:
+ if (cm->cmsg_len != CMSG_LEN(sizeof(*pi))) {
+ trace(1,
+ "invalid cmsg length for IPV6_PKTINFO\n");
+ return;
+ }
+ pi = (struct in6_pktinfo *)(CMSG_DATA(cm));
+ idx = pi->ipi6_ifindex;
+ break;
+ case IPV6_HOPLIMIT:
+ if (cm->cmsg_len != CMSG_LEN(sizeof(int))) {
+ trace(1,
+ "invalid cmsg length for IPV6_HOPLIMIT\n");
+ return;
+ }
+ hlimp = (int *)CMSG_DATA(cm);
+ break;
+ }
+ }
+ if (idx && IN6_IS_ADDR_LINKLOCAL(&fsock.sin6_addr))
+ SET_IN6_LINKLOCAL_IFINDEX(fsock.sin6_addr, idx);
+
+ if (len < sizeof(struct rip6)) {
+ trace(1, "Packet too short\n");
+ return;
+ }
+
+ if (pi == NULL || hlimp == NULL) {
+ /*
+ * This can happen when the kernel failed to allocate memory
+ * for the ancillary data. Although we might be able to handle
+ * some cases without this info, those are minor and not so
+ * important, so it's better to discard the packet for safer
+ * operation.
+ */
+ trace(1, "IPv6 packet information cannot be retrieved\n");
+ return;
+ }
+
+ nh = fsock.sin6_addr;
+ nn = (len - sizeof(struct rip6) + sizeof(struct netinfo6)) /
+ sizeof(struct netinfo6);
+ rp = (struct rip6 *)buf;
+ np = rp->rip6_nets;
+
+ if (rp->rip6_vers != RIP6_VERSION) {
+ trace(1, "Incorrect RIP version %d\n", rp->rip6_vers);
+ return;
+ }
+ if (rp->rip6_cmd == RIP6_REQUEST) {
+ if (idx && idx < nindex2ifc) {
+ ifcp = index2ifc[idx];
+ riprequest(ifcp, np, nn, &fsock);
+ } else {
+ riprequest(NULL, np, nn, &fsock);
+ }
+ return;
+ }
+
+ if (!IN6_IS_ADDR_LINKLOCAL(&fsock.sin6_addr)) {
+ trace(1, "Response from non-ll addr: %s\n",
+ inet6_n2p(&fsock.sin6_addr));
+ return; /* Ignore packets from non-link-local addr */
+ }
+ if (ntohs(fsock.sin6_port) != RIP6_PORT) {
+ trace(1, "Response from non-rip port from %s\n",
+ inet6_n2p(&fsock.sin6_addr));
+ return;
+ }
+ if (IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr) && *hlimp != 255) {
+ trace(1,
+ "Response packet with a smaller hop limit (%d) from %s\n",
+ *hlimp, inet6_n2p(&fsock.sin6_addr));
+ return;
+ }
+ /*
+ * Further validation: since this program does not send off-link
+ * requests, an incoming response must always come from an on-link
+ * node. Although this is normally ensured by the source address
+ * check above, it may not 100% be safe because there are router
+ * implementations that (invalidly) allow a packet with a link-local
+ * source address to be forwarded to a different link.
+ * So we also check whether the destination address is a link-local
+ * address or the hop limit is 255. Note that RFC2080 does not require
+ * the specific hop limit for a unicast response, so we cannot assume
+ * the limitation.
+ */
+ if (!IN6_IS_ADDR_LINKLOCAL(&pi->ipi6_addr) && *hlimp != 255) {
+ trace(1,
+ "Response packet possibly from an off-link node: "
+ "from %s to %s hlim=%d\n",
+ inet6_n2p(&fsock.sin6_addr),
+ inet6_n2p(&pi->ipi6_addr), *hlimp);
+ return;
+ }
+
+ idx = IN6_LINKLOCAL_IFINDEX(fsock.sin6_addr);
+ ifcp = (idx < nindex2ifc) ? index2ifc[idx] : NULL;
+ if (!ifcp) {
+ trace(1, "Packets to unknown interface index %d\n", idx);
+ return; /* Ignore it */
+ }
+ if (IN6_ARE_ADDR_EQUAL(&ifcp->ifc_mylladdr, &fsock.sin6_addr))
+ return; /* The packet is from me; ignore */
+ if (rp->rip6_cmd != RIP6_RESPONSE) {
+ trace(1, "Invalid command %d\n", rp->rip6_cmd);
+ return;
+ }
+
+ /* -N: no use */
+ if (iff_find(ifcp, 'N') != NULL)
+ return;
+
+ tracet(1, "Recv(%s): from %s.%d info(%d)\n",
+ ifcp->ifc_name, inet6_n2p(&nh), ntohs(fsock.sin6_port), nn);
+
+ t = time(NULL);
+ t_half_lifetime = t - (RIP_LIFETIME/2);
+ for (; nn; nn--, np++) {
+ if (np->rip6_metric == NEXTHOP_METRIC) {
+ /* modify neighbor address */
+ if (IN6_IS_ADDR_LINKLOCAL(&np->rip6_dest)) {
+ nh = np->rip6_dest;
+ SET_IN6_LINKLOCAL_IFINDEX(nh, idx);
+ trace(1, "\tNexthop: %s\n", inet6_n2p(&nh));
+ } else if (IN6_IS_ADDR_UNSPECIFIED(&np->rip6_dest)) {
+ nh = fsock.sin6_addr;
+ trace(1, "\tNexthop: %s\n", inet6_n2p(&nh));
+ } else {
+ nh = fsock.sin6_addr;
+ trace(1, "\tInvalid Nexthop: %s\n",
+ inet6_n2p(&np->rip6_dest));
+ }
+ continue;
+ }
+ if (IN6_IS_ADDR_MULTICAST(&np->rip6_dest)) {
+ trace(1, "\tMulticast netinfo6: %s/%d [%d]\n",
+ inet6_n2p(&np->rip6_dest),
+ np->rip6_plen, np->rip6_metric);
+ continue;
+ }
+ if (IN6_IS_ADDR_LOOPBACK(&np->rip6_dest)) {
+ trace(1, "\tLoopback netinfo6: %s/%d [%d]\n",
+ inet6_n2p(&np->rip6_dest),
+ np->rip6_plen, np->rip6_metric);
+ continue;
+ }
+ if (IN6_IS_ADDR_LINKLOCAL(&np->rip6_dest)) {
+ trace(1, "\tLink Local netinfo6: %s/%d [%d]\n",
+ inet6_n2p(&np->rip6_dest),
+ np->rip6_plen, np->rip6_metric);
+ continue;
+ }
+ /* may need to pass sitelocal prefix in some case, however*/
+ if (IN6_IS_ADDR_SITELOCAL(&np->rip6_dest) && !lflag) {
+ trace(1, "\tSite Local netinfo6: %s/%d [%d]\n",
+ inet6_n2p(&np->rip6_dest),
+ np->rip6_plen, np->rip6_metric);
+ continue;
+ }
+ trace(2, "\tnetinfo6: %s/%d [%d]",
+ inet6_n2p(&np->rip6_dest),
+ np->rip6_plen, np->rip6_metric);
+ if (np->rip6_tag)
+ trace(2, " tag=0x%04x", ntohs(np->rip6_tag) & 0xffff);
+ if (dflag >= 2) {
+ ia = np->rip6_dest;
+ applyplen(&ia, np->rip6_plen);
+ if (!IN6_ARE_ADDR_EQUAL(&ia, &np->rip6_dest))
+ trace(2, " [junk outside prefix]");
+ }
+
+ /*
+ * -L: listen only if the prefix matches the configuration
+ */
+ ok = 1; /* if there's no L filter, it is ok */
+ for (iffp = ifcp->ifc_filter; iffp; iffp = iffp->iff_next) {
+ if (iffp->iff_type != 'L')
+ continue;
+ ok = 0;
+ if (np->rip6_plen < iffp->iff_plen)
+ continue;
+ /* special rule: ::/0 means default, not "in /0" */
+ if (iffp->iff_plen == 0 && np->rip6_plen > 0)
+ continue;
+ ia = np->rip6_dest;
+ applyplen(&ia, iffp->iff_plen);
+ if (IN6_ARE_ADDR_EQUAL(&ia, &iffp->iff_addr)) {
+ ok = 1;
+ break;
+ }
+ }
+ if (!ok) {
+ trace(2, " (filtered)\n");
+ continue;
+ }
+
+ trace(2, "\n");
+ np->rip6_metric++;
+ np->rip6_metric += ifcp->ifc_metric;
+ if (np->rip6_metric > HOPCNT_INFINITY6)
+ np->rip6_metric = HOPCNT_INFINITY6;
+
+ applyplen(&np->rip6_dest, np->rip6_plen);
+ if ((rrt = rtsearch(np, NULL)) != NULL) {
+ if (rrt->rrt_t == 0)
+ continue; /* Intf route has priority */
+ nq = &rrt->rrt_info;
+ if (nq->rip6_metric > np->rip6_metric) {
+ if (rrt->rrt_index == ifcp->ifc_index &&
+ IN6_ARE_ADDR_EQUAL(&nh, &rrt->rrt_gw)) {
+ /* Small metric from the same gateway */
+ nq->rip6_metric = np->rip6_metric;
+ } else {
+ /* Better route found */
+ rrt->rrt_index = ifcp->ifc_index;
+ /* Update routing table */
+ delroute(nq, &rrt->rrt_gw);
+ rrt->rrt_gw = nh;
+ *nq = *np;
+ addroute(rrt, &nh, ifcp);
+ }
+ rrt->rrt_rflags |= RRTF_CHANGED;
+ rrt->rrt_t = t;
+ need_trigger = 1;
+ } else if (nq->rip6_metric < np->rip6_metric &&
+ rrt->rrt_index == ifcp->ifc_index &&
+ IN6_ARE_ADDR_EQUAL(&nh, &rrt->rrt_gw)) {
+ /* Got worse route from same gw */
+ nq->rip6_metric = np->rip6_metric;
+ rrt->rrt_t = t;
+ rrt->rrt_rflags |= RRTF_CHANGED;
+ need_trigger = 1;
+ } else if (nq->rip6_metric == np->rip6_metric &&
+ np->rip6_metric < HOPCNT_INFINITY6) {
+ if (rrt->rrt_index == ifcp->ifc_index &&
+ IN6_ARE_ADDR_EQUAL(&nh, &rrt->rrt_gw)) {
+ /* same metric, same route from same gw */
+ rrt->rrt_t = t;
+ } else if (rrt->rrt_t < t_half_lifetime) {
+ /* Better route found */
+ rrt->rrt_index = ifcp->ifc_index;
+ /* Update routing table */
+ delroute(nq, &rrt->rrt_gw);
+ rrt->rrt_gw = nh;
+ *nq = *np;
+ addroute(rrt, &nh, ifcp);
+ rrt->rrt_rflags |= RRTF_CHANGED;
+ rrt->rrt_t = t;
+ }
+ }
+ /*
+ * if nq->rip6_metric == HOPCNT_INFINITY6 then
+ * do not update age value. Do nothing.
+ */
+ } else if (np->rip6_metric < HOPCNT_INFINITY6) {
+ /* Got a new valid route */
+ if ((rrt = MALLOC(struct riprt)) == NULL) {
+ fatal("malloc: struct riprt");
+ /*NOTREACHED*/
+ }
+ memset(rrt, 0, sizeof(*rrt));
+ nq = &rrt->rrt_info;
+
+ rrt->rrt_same = NULL;
+ rrt->rrt_index = ifcp->ifc_index;
+ rrt->rrt_flags = RTF_UP|RTF_GATEWAY;
+ rrt->rrt_gw = nh;
+ *nq = *np;
+ applyplen(&nq->rip6_dest, nq->rip6_plen);
+ if (nq->rip6_plen == sizeof(struct in6_addr) * 8)
+ rrt->rrt_flags |= RTF_HOST;
+
+ /* Put the route to the list */
+ rrt->rrt_next = riprt;
+ riprt = rrt;
+ /* Update routing table */
+ addroute(rrt, &nh, ifcp);
+ rrt->rrt_rflags |= RRTF_CHANGED;
+ need_trigger = 1;
+ rrt->rrt_t = t;
+ }
+ }
+ /* XXX need to care the interval between triggered updates */
+ if (need_trigger) {
+ if (nextalarm > time(NULL) + RIP_TRIG_INT6_MAX) {
+ for (ic = ifc; ic; ic = ic->ifc_next) {
+ if (ifcp->ifc_index == ic->ifc_index)
+ continue;
+ if (ic->ifc_flags & IFF_UP)
+ ripsend(ic, &ic->ifc_ripsin,
+ RRTF_CHANGED);
+ }
+ }
+ /* Reset the flag */
+ for (rrt = riprt; rrt; rrt = rrt->rrt_next)
+ rrt->rrt_rflags &= ~RRTF_CHANGED;
+ }
+}
+
+/*
+ * Send all routes request packet to the specified interface.
+ */
+void
+sendrequest(ifcp)
+ struct ifc *ifcp;
+{
+ struct netinfo6 *np;
+ int error;
+
+ if (ifcp->ifc_flags & IFF_LOOPBACK)
+ return;
+ ripbuf->rip6_cmd = RIP6_REQUEST;
+ np = ripbuf->rip6_nets;
+ memset(np, 0, sizeof(struct netinfo6));
+ np->rip6_metric = HOPCNT_INFINITY6;
+ tracet(1, "Send rtdump Request to %s (%s)\n",
+ ifcp->ifc_name, inet6_n2p(&ifcp->ifc_ripsin.sin6_addr));
+ error = sendpacket(&ifcp->ifc_ripsin, RIPSIZE(1));
+ if (error == EAFNOSUPPORT) {
+ /* Protocol not supported */
+ tracet(1, "Could not send rtdump Request to %s (%s): "
+ "set IFF_UP to 0\n",
+ ifcp->ifc_name, inet6_n2p(&ifcp->ifc_ripsin.sin6_addr));
+ ifcp->ifc_flags &= ~IFF_UP; /* As if down for AF_INET6 */
+ }
+ ripbuf->rip6_cmd = RIP6_RESPONSE;
+}
+
+/*
+ * Process a RIP6_REQUEST packet.
+ */
+void
+riprequest(ifcp, np, nn, sin6)
+ struct ifc *ifcp;
+ struct netinfo6 *np;
+ int nn;
+ struct sockaddr_in6 *sin6;
+{
+ int i;
+ struct riprt *rrt;
+
+ if (!(nn == 1 && IN6_IS_ADDR_UNSPECIFIED(&np->rip6_dest) &&
+ np->rip6_plen == 0 && np->rip6_metric == HOPCNT_INFINITY6)) {
+ /* Specific response, don't split-horizon */
+ trace(1, "\tRIP Request\n");
+ for (i = 0; i < nn; i++, np++) {
+ rrt = rtsearch(np, NULL);
+ if (rrt)
+ np->rip6_metric = rrt->rrt_info.rip6_metric;
+ else
+ np->rip6_metric = HOPCNT_INFINITY6;
+ }
+ (void)sendpacket(sin6, RIPSIZE(nn));
+ return;
+ }
+ /* Whole routing table dump */
+ trace(1, "\tRIP Request -- whole routing table\n");
+ ripsend(ifcp, sin6, RRTF_SENDANYWAY);
+}
+
+/*
+ * Get information of each interface.
+ */
+void
+ifconfig()
+{
+ struct ifaddrs *ifap, *ifa;
+ struct ifc *ifcp;
+ struct ipv6_mreq mreq;
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ fatal("socket");
+ /*NOTREACHED*/
+ }
+
+ if (getifaddrs(&ifap) != 0) {
+ fatal("getifaddrs");
+ /*NOTREACHED*/
+ }
+
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ ifcp = ifc_find(ifa->ifa_name);
+ /* we are interested in multicast-capable interfaces */
+ if ((ifa->ifa_flags & IFF_MULTICAST) == 0)
+ continue;
+ if (!ifcp) {
+ /* new interface */
+ if ((ifcp = MALLOC(struct ifc)) == NULL) {
+ fatal("malloc: struct ifc");
+ /*NOTREACHED*/
+ }
+ memset(ifcp, 0, sizeof(*ifcp));
+ ifcp->ifc_index = -1;
+ ifcp->ifc_next = ifc;
+ ifc = ifcp;
+ nifc++;
+ ifcp->ifc_name = allocopy(ifa->ifa_name);
+ ifcp->ifc_addr = 0;
+ ifcp->ifc_filter = 0;
+ ifcp->ifc_flags = ifa->ifa_flags;
+ trace(1, "newif %s <%s>\n", ifcp->ifc_name,
+ ifflags(ifcp->ifc_flags));
+ if (!strcmp(ifcp->ifc_name, LOOPBACK_IF))
+ loopifcp = ifcp;
+ } else {
+ /* update flag, this may be up again */
+ if (ifcp->ifc_flags != ifa->ifa_flags) {
+ trace(1, "%s: <%s> -> ", ifcp->ifc_name,
+ ifflags(ifcp->ifc_flags));
+ trace(1, "<%s>\n", ifflags(ifa->ifa_flags));
+ ifcp->ifc_cflags |= IFC_CHANGED;
+ }
+ ifcp->ifc_flags = ifa->ifa_flags;
+ }
+ ifconfig1(ifa->ifa_name, ifa->ifa_addr, ifcp, s);
+ if ((ifcp->ifc_flags & (IFF_LOOPBACK | IFF_UP)) == IFF_UP
+ && 0 < ifcp->ifc_index && !ifcp->ifc_joined) {
+ mreq.ipv6mr_multiaddr = ifcp->ifc_ripsin.sin6_addr;
+ mreq.ipv6mr_interface = ifcp->ifc_index;
+ if (setsockopt(ripsock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ &mreq, sizeof(mreq)) < 0) {
+ fatal("IPV6_JOIN_GROUP");
+ /*NOTREACHED*/
+ }
+ trace(1, "join %s %s\n", ifcp->ifc_name, RIP6_DEST);
+ ifcp->ifc_joined++;
+ }
+ }
+ close(s);
+ freeifaddrs(ifap);
+}
+
+void
+ifconfig1(name, sa, ifcp, s)
+ const char *name;
+ const struct sockaddr *sa;
+ struct ifc *ifcp;
+ int s;
+{
+ struct in6_ifreq ifr;
+ const struct sockaddr_in6 *sin6;
+ struct ifac *ifa;
+ int plen;
+ char buf[BUFSIZ];
+
+ sin6 = (const struct sockaddr_in6 *)sa;
+ if (IN6_IS_ADDR_SITELOCAL(&sin6->sin6_addr) && !lflag)
+ return;
+ ifr.ifr_addr = *sin6;
+ strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+ if (ioctl(s, SIOCGIFNETMASK_IN6, (char *)&ifr) < 0) {
+ fatal("ioctl: SIOCGIFNETMASK_IN6");
+ /*NOTREACHED*/
+ }
+ plen = sin6mask2len(&ifr.ifr_addr);
+ if ((ifa = ifa_match(ifcp, &sin6->sin6_addr, plen)) != NULL) {
+ /* same interface found */
+ /* need check if something changed */
+ /* XXX not yet implemented */
+ return;
+ }
+ /*
+ * New address is found
+ */
+ if ((ifa = MALLOC(struct ifac)) == NULL) {
+ fatal("malloc: struct ifac");
+ /*NOTREACHED*/
+ }
+ memset(ifa, 0, sizeof(*ifa));
+ ifa->ifa_conf = ifcp;
+ ifa->ifa_next = ifcp->ifc_addr;
+ ifcp->ifc_addr = ifa;
+ ifa->ifa_addr = sin6->sin6_addr;
+ ifa->ifa_plen = plen;
+ if (ifcp->ifc_flags & IFF_POINTOPOINT) {
+ ifr.ifr_addr = *sin6;
+ if (ioctl(s, SIOCGIFDSTADDR_IN6, (char *)&ifr) < 0) {
+ fatal("ioctl: SIOCGIFDSTADDR_IN6");
+ /*NOTREACHED*/
+ }
+ ifa->ifa_raddr = ifr.ifr_dstaddr.sin6_addr;
+ inet_ntop(AF_INET6, (void *)&ifa->ifa_raddr, buf, sizeof(buf));
+ trace(1, "found address %s/%d -- %s\n",
+ inet6_n2p(&ifa->ifa_addr), ifa->ifa_plen, buf);
+ } else {
+ trace(1, "found address %s/%d\n",
+ inet6_n2p(&ifa->ifa_addr), ifa->ifa_plen);
+ }
+ if (ifcp->ifc_index < 0 && IN6_IS_ADDR_LINKLOCAL(&ifa->ifa_addr)) {
+ ifcp->ifc_mylladdr = ifa->ifa_addr;
+ ifcp->ifc_index = IN6_LINKLOCAL_IFINDEX(ifa->ifa_addr);
+ memcpy(&ifcp->ifc_ripsin, &ripsin, ripsin.ss_len);
+ SET_IN6_LINKLOCAL_IFINDEX(ifcp->ifc_ripsin.sin6_addr,
+ ifcp->ifc_index);
+ setindex2ifc(ifcp->ifc_index, ifcp);
+ ifcp->ifc_mtu = getifmtu(ifcp->ifc_index);
+ if (ifcp->ifc_mtu > RIP6_MAXMTU)
+ ifcp->ifc_mtu = RIP6_MAXMTU;
+ if (ioctl(s, SIOCGIFMETRIC, (char *)&ifr) < 0) {
+ fatal("ioctl: SIOCGIFMETRIC");
+ /*NOTREACHED*/
+ }
+ ifcp->ifc_metric = ifr.ifr_metric;
+ trace(1, "\tindex: %d, mtu: %d, metric: %d\n",
+ ifcp->ifc_index, ifcp->ifc_mtu, ifcp->ifc_metric);
+ } else
+ ifcp->ifc_cflags |= IFC_CHANGED;
+}
+
+/*
+ * Receive and process routing messages.
+ * Update interface information as necesssary.
+ */
+void
+rtrecv()
+{
+ char buf[BUFSIZ];
+ char *p, *q;
+ struct rt_msghdr *rtm;
+ struct ifa_msghdr *ifam;
+ struct if_msghdr *ifm;
+ int len;
+ struct ifc *ifcp, *ic;
+ int iface = 0, rtable = 0;
+ struct sockaddr_in6 *rta[RTAX_MAX];
+ struct sockaddr_in6 mask;
+ int i, addrs;
+ struct riprt *rrt;
+
+ if ((len = read(rtsock, buf, sizeof(buf))) < 0) {
+ perror("read from rtsock");
+ exit(1);
+ }
+ if (len < sizeof(*rtm)) {
+ trace(1, "short read from rtsock: %d (should be > %lu)\n",
+ len, (u_long)sizeof(*rtm));
+ return;
+ }
+ if (dflag >= 2) {
+ fprintf(stderr, "rtmsg:\n");
+ for (i = 0; i < len; i++) {
+ fprintf(stderr, "%02x ", buf[i] & 0xff);
+ if (i % 16 == 15) fprintf(stderr, "\n");
+ }
+ fprintf(stderr, "\n");
+ }
+
+ for (p = buf; p - buf < len; p += ((struct rt_msghdr *)p)->rtm_msglen) {
+ /* safety against bogus message */
+ if (((struct rt_msghdr *)p)->rtm_msglen <= 0) {
+ trace(1, "bogus rtmsg: length=%d\n",
+ ((struct rt_msghdr *)p)->rtm_msglen);
+ break;
+ }
+ rtm = NULL;
+ ifam = NULL;
+ ifm = NULL;
+ switch (((struct rt_msghdr *)p)->rtm_type) {
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ ifam = (struct ifa_msghdr *)p;
+ addrs = ifam->ifam_addrs;
+ q = (char *)(ifam + 1);
+ break;
+ case RTM_IFINFO:
+ ifm = (struct if_msghdr *)p;
+ addrs = ifm->ifm_addrs;
+ q = (char *)(ifm + 1);
+ break;
+ default:
+ rtm = (struct rt_msghdr *)p;
+ addrs = rtm->rtm_addrs;
+ q = (char *)(rtm + 1);
+ if (rtm->rtm_version != RTM_VERSION) {
+ trace(1, "unexpected rtmsg version %d "
+ "(should be %d)\n",
+ rtm->rtm_version, RTM_VERSION);
+ continue;
+ }
+ if (rtm->rtm_pid == pid) {
+#if 0
+ trace(1, "rtmsg looped back to me, ignored\n");
+#endif
+ continue;
+ }
+ break;
+ }
+ memset(&rta, 0, sizeof(rta));
+ for (i = 0; i < RTAX_MAX; i++) {
+ if (addrs & (1 << i)) {
+ rta[i] = (struct sockaddr_in6 *)q;
+ q += ROUNDUP(rta[i]->sin6_len);
+ }
+ }
+
+ trace(1, "rtsock: %s (addrs=%x)\n",
+ rttypes((struct rt_msghdr *)p), addrs);
+ if (dflag >= 2) {
+ for (i = 0;
+ i < ((struct rt_msghdr *)p)->rtm_msglen;
+ i++) {
+ fprintf(stderr, "%02x ", p[i] & 0xff);
+ if (i % 16 == 15) fprintf(stderr, "\n");
+ }
+ fprintf(stderr, "\n");
+ }
+
+ /*
+ * Easy ones first.
+ *
+ * We may be able to optimize by using ifm->ifm_index or
+ * ifam->ifam_index. For simplicity we don't do that here.
+ */
+ switch (((struct rt_msghdr *)p)->rtm_type) {
+ case RTM_NEWADDR:
+ case RTM_IFINFO:
+ iface++;
+ continue;
+ case RTM_ADD:
+ rtable++;
+ continue;
+ case RTM_LOSING:
+ case RTM_MISS:
+ case RTM_GET:
+ case RTM_LOCK:
+ /* nothing to be done here */
+ trace(1, "\tnothing to be done, ignored\n");
+ continue;
+ }
+
+#if 0
+ if (rta[RTAX_DST] == NULL) {
+ trace(1, "\tno destination, ignored\n");
+ continue;
+ }
+ if (rta[RTAX_DST]->sin6_family != AF_INET6) {
+ trace(1, "\taf mismatch, ignored\n");
+ continue;
+ }
+ if (IN6_IS_ADDR_LINKLOCAL(&rta[RTAX_DST]->sin6_addr)) {
+ trace(1, "\tlinklocal destination, ignored\n");
+ continue;
+ }
+ if (IN6_ARE_ADDR_EQUAL(&rta[RTAX_DST]->sin6_addr, &in6addr_loopback)) {
+ trace(1, "\tloopback destination, ignored\n");
+ continue; /* Loopback */
+ }
+ if (IN6_IS_ADDR_MULTICAST(&rta[RTAX_DST]->sin6_addr)) {
+ trace(1, "\tmulticast destination, ignored\n");
+ continue;
+ }
+#endif
+
+ /* hard ones */
+ switch (((struct rt_msghdr *)p)->rtm_type) {
+ case RTM_NEWADDR:
+ case RTM_IFINFO:
+ case RTM_ADD:
+ case RTM_LOSING:
+ case RTM_MISS:
+ case RTM_GET:
+ case RTM_LOCK:
+ /* should already be handled */
+ fatal("rtrecv: never reach here");
+ /*NOTREACHED*/
+ case RTM_DELETE:
+ if (!rta[RTAX_DST] || !rta[RTAX_GATEWAY]) {
+ trace(1, "\tsome of dst/gw/netamsk are "
+ "unavailable, ignored\n");
+ break;
+ }
+ if ((rtm->rtm_flags & RTF_HOST) != 0) {
+ mask.sin6_len = sizeof(mask);
+ memset(&mask.sin6_addr, 0xff,
+ sizeof(mask.sin6_addr));
+ rta[RTAX_NETMASK] = &mask;
+ } else if (!rta[RTAX_NETMASK]) {
+ trace(1, "\tsome of dst/gw/netamsk are "
+ "unavailable, ignored\n");
+ break;
+ }
+ if (rt_del(rta[RTAX_DST], rta[RTAX_GATEWAY],
+ rta[RTAX_NETMASK]) == 0) {
+ rtable++; /*just to be sure*/
+ }
+ break;
+ case RTM_CHANGE:
+ case RTM_REDIRECT:
+ trace(1, "\tnot supported yet, ignored\n");
+ break;
+ case RTM_DELADDR:
+ if (!rta[RTAX_NETMASK] || !rta[RTAX_IFA]) {
+ trace(1, "\tno netmask or ifa given, ignored\n");
+ break;
+ }
+ if (ifam->ifam_index < nindex2ifc)
+ ifcp = index2ifc[ifam->ifam_index];
+ else
+ ifcp = NULL;
+ if (!ifcp) {
+ trace(1, "\tinvalid ifam_index %d, ignored\n",
+ ifam->ifam_index);
+ break;
+ }
+ if (!rt_deladdr(ifcp, rta[RTAX_IFA], rta[RTAX_NETMASK]))
+ iface++;
+ break;
+ case RTM_OLDADD:
+ case RTM_OLDDEL:
+ trace(1, "\tnot supported yet, ignored\n");
+ break;
+ }
+
+ }
+
+ if (iface) {
+ trace(1, "rtsock: reconfigure interfaces, refresh interface routes\n");
+ ifconfig();
+ for (ifcp = ifc; ifcp; ifcp = ifcp->ifc_next)
+ if (ifcp->ifc_cflags & IFC_CHANGED) {
+ if (ifrt(ifcp, 1)) {
+ for (ic = ifc; ic; ic = ic->ifc_next) {
+ if (ifcp->ifc_index == ic->ifc_index)
+ continue;
+ if (ic->ifc_flags & IFF_UP)
+ ripsend(ic, &ic->ifc_ripsin,
+ RRTF_CHANGED);
+ }
+ /* Reset the flag */
+ for (rrt = riprt; rrt; rrt = rrt->rrt_next)
+ rrt->rrt_rflags &= ~RRTF_CHANGED;
+ }
+ ifcp->ifc_cflags &= ~IFC_CHANGED;
+ }
+ }
+ if (rtable) {
+ trace(1, "rtsock: read routing table again\n");
+ krtread(1);
+ }
+}
+
+/*
+ * remove specified route from the internal routing table.
+ */
+int
+rt_del(sdst, sgw, smask)
+ const struct sockaddr_in6 *sdst;
+ const struct sockaddr_in6 *sgw;
+ const struct sockaddr_in6 *smask;
+{
+ const struct in6_addr *dst = NULL;
+ const struct in6_addr *gw = NULL;
+ int prefix;
+ struct netinfo6 ni6;
+ struct riprt *rrt = NULL;
+ time_t t_lifetime;
+
+ if (sdst->sin6_family != AF_INET6) {
+ trace(1, "\tother AF, ignored\n");
+ return -1;
+ }
+ if (IN6_IS_ADDR_LINKLOCAL(&sdst->sin6_addr)
+ || IN6_ARE_ADDR_EQUAL(&sdst->sin6_addr, &in6addr_loopback)
+ || IN6_IS_ADDR_MULTICAST(&sdst->sin6_addr)) {
+ trace(1, "\taddress %s not interesting, ignored\n",
+ inet6_n2p(&sdst->sin6_addr));
+ return -1;
+ }
+ dst = &sdst->sin6_addr;
+ if (sgw->sin6_family == AF_INET6) {
+ /* easy case */
+ gw = &sgw->sin6_addr;
+ prefix = sin6mask2len(smask);
+ } else if (sgw->sin6_family == AF_LINK) {
+ /*
+ * Interface route... a hard case. We need to get the prefix
+ * length from the kernel, but we now are parsing rtmsg.
+ * We'll purge matching routes from my list, then get the
+ * fresh list.
+ */
+ struct riprt *longest;
+ trace(1, "\t%s is an interface route, guessing prefixlen\n",
+ inet6_n2p(dst));
+ longest = NULL;
+ for (rrt = riprt; rrt; rrt = rrt->rrt_next) {
+ if (IN6_ARE_ADDR_EQUAL(&rrt->rrt_info.rip6_dest,
+ &sdst->sin6_addr)
+ && IN6_IS_ADDR_LOOPBACK(&rrt->rrt_gw)) {
+ if (!longest
+ || longest->rrt_info.rip6_plen <
+ rrt->rrt_info.rip6_plen) {
+ longest = rrt;
+ }
+ }
+ }
+ rrt = longest;
+ if (!rrt) {
+ trace(1, "\tno matching interface route found\n");
+ return -1;
+ }
+ gw = &in6addr_loopback;
+ prefix = rrt->rrt_info.rip6_plen;
+ } else {
+ trace(1, "\tunsupported af: (gw=%d)\n", sgw->sin6_family);
+ return -1;
+ }
+
+ trace(1, "\tdeleting %s/%d ", inet6_n2p(dst), prefix);
+ trace(1, "gw %s\n", inet6_n2p(gw));
+ t_lifetime = time(NULL) - RIP_LIFETIME;
+ /* age route for interface address */
+ memset(&ni6, 0, sizeof(ni6));
+ ni6.rip6_dest = *dst;
+ ni6.rip6_plen = prefix;
+ applyplen(&ni6.rip6_dest, ni6.rip6_plen); /*to be sure*/
+ trace(1, "\tfind route %s/%d\n", inet6_n2p(&ni6.rip6_dest),
+ ni6.rip6_plen);
+ if (!rrt && (rrt = rtsearch(&ni6, NULL)) == NULL) {
+ trace(1, "\tno route found\n");
+ return -1;
+ }
+#if 0
+ if ((rrt->rrt_flags & RTF_STATIC) == 0) {
+ trace(1, "\tyou can delete static routes only\n");
+ } else
+#endif
+ if (!IN6_ARE_ADDR_EQUAL(&rrt->rrt_gw, gw)) {
+ trace(1, "\tgw mismatch: %s <-> ",
+ inet6_n2p(&rrt->rrt_gw));
+ trace(1, "%s\n", inet6_n2p(gw));
+ } else {
+ trace(1, "\troute found, age it\n");
+ if (rrt->rrt_t == 0 || rrt->rrt_t > t_lifetime) {
+ rrt->rrt_t = t_lifetime;
+ rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6;
+ }
+ }
+ return 0;
+}
+
+/*
+ * remove specified address from internal interface/routing table.
+ */
+int
+rt_deladdr(ifcp, sifa, smask)
+ struct ifc *ifcp;
+ const struct sockaddr_in6 *sifa;
+ const struct sockaddr_in6 *smask;
+{
+ const struct in6_addr *addr = NULL;
+ int prefix;
+ struct ifac *ifa = NULL;
+ struct netinfo6 ni6;
+ struct riprt *rrt = NULL;
+ time_t t_lifetime;
+ int updated = 0;
+
+ if (sifa->sin6_family != AF_INET6) {
+ trace(1, "\tother AF, ignored\n");
+ return -1;
+ }
+ addr = &sifa->sin6_addr;
+ prefix = sin6mask2len(smask);
+
+ trace(1, "\tdeleting %s/%d from %s\n",
+ inet6_n2p(addr), prefix, ifcp->ifc_name);
+ ifa = ifa_match(ifcp, addr, prefix);
+ if (!ifa) {
+ trace(1, "\tno matching ifa found for %s/%d on %s\n",
+ inet6_n2p(addr), prefix, ifcp->ifc_name);
+ return -1;
+ }
+ if (ifa->ifa_conf != ifcp) {
+ trace(1, "\taddress table corrupt: back pointer does not match "
+ "(%s != %s)\n",
+ ifcp->ifc_name, ifa->ifa_conf->ifc_name);
+ return -1;
+ }
+ /* remove ifa from interface */
+ if (ifcp->ifc_addr == ifa)
+ ifcp->ifc_addr = ifa->ifa_next;
+ else {
+ struct ifac *p;
+ for (p = ifcp->ifc_addr; p; p = p->ifa_next) {
+ if (p->ifa_next == ifa) {
+ p->ifa_next = ifa->ifa_next;
+ break;
+ }
+ }
+ }
+ ifa->ifa_next = NULL;
+ ifa->ifa_conf = NULL;
+ t_lifetime = time(NULL) - RIP_LIFETIME;
+ /* age route for interface address */
+ memset(&ni6, 0, sizeof(ni6));
+ ni6.rip6_dest = ifa->ifa_addr;
+ ni6.rip6_plen = ifa->ifa_plen;
+ applyplen(&ni6.rip6_dest, ni6.rip6_plen);
+ trace(1, "\tfind interface route %s/%d on %d\n",
+ inet6_n2p(&ni6.rip6_dest), ni6.rip6_plen, ifcp->ifc_index);
+ if ((rrt = rtsearch(&ni6, NULL)) != NULL) {
+ struct in6_addr none;
+ memset(&none, 0, sizeof(none));
+ if (rrt->rrt_index == ifcp->ifc_index &&
+ (IN6_ARE_ADDR_EQUAL(&rrt->rrt_gw, &none) ||
+ IN6_IS_ADDR_LOOPBACK(&rrt->rrt_gw))) {
+ trace(1, "\troute found, age it\n");
+ if (rrt->rrt_t == 0 || rrt->rrt_t > t_lifetime) {
+ rrt->rrt_t = t_lifetime;
+ rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6;
+ }
+ updated++;
+ } else {
+ trace(1, "\tnon-interface route found: %s/%d on %d\n",
+ inet6_n2p(&rrt->rrt_info.rip6_dest),
+ rrt->rrt_info.rip6_plen,
+ rrt->rrt_index);
+ }
+ } else
+ trace(1, "\tno interface route found\n");
+ /* age route for p2p destination */
+ if (ifcp->ifc_flags & IFF_POINTOPOINT) {
+ memset(&ni6, 0, sizeof(ni6));
+ ni6.rip6_dest = ifa->ifa_raddr;
+ ni6.rip6_plen = 128;
+ applyplen(&ni6.rip6_dest, ni6.rip6_plen); /*to be sure*/
+ trace(1, "\tfind p2p route %s/%d on %d\n",
+ inet6_n2p(&ni6.rip6_dest), ni6.rip6_plen,
+ ifcp->ifc_index);
+ if ((rrt = rtsearch(&ni6, NULL)) != NULL) {
+ if (rrt->rrt_index == ifcp->ifc_index &&
+ IN6_ARE_ADDR_EQUAL(&rrt->rrt_gw, &ifa->ifa_addr)) {
+ trace(1, "\troute found, age it\n");
+ if (rrt->rrt_t == 0 || rrt->rrt_t > t_lifetime) {
+ rrt->rrt_t = t_lifetime;
+ rrt->rrt_info.rip6_metric =
+ HOPCNT_INFINITY6;
+ updated++;
+ }
+ } else {
+ trace(1, "\tnon-p2p route found: %s/%d on %d\n",
+ inet6_n2p(&rrt->rrt_info.rip6_dest),
+ rrt->rrt_info.rip6_plen,
+ rrt->rrt_index);
+ }
+ } else
+ trace(1, "\tno p2p route found\n");
+ }
+ return updated ? 0 : -1;
+}
+
+/*
+ * Get each interface address and put those interface routes to the route
+ * list.
+ */
+int
+ifrt(ifcp, again)
+ struct ifc *ifcp;
+ int again;
+{
+ struct ifac *ifa;
+ struct riprt *rrt = NULL, *search_rrt, *prev_rrt, *loop_rrt;
+ struct netinfo6 *np;
+ time_t t_lifetime;
+ int need_trigger = 0;
+
+#if 0
+ if (ifcp->ifc_flags & IFF_LOOPBACK)
+ return 0; /* ignore loopback */
+#endif
+
+ if (ifcp->ifc_flags & IFF_POINTOPOINT) {
+ ifrt_p2p(ifcp, again);
+ return 0;
+ }
+
+ for (ifa = ifcp->ifc_addr; ifa; ifa = ifa->ifa_next) {
+ if (IN6_IS_ADDR_LINKLOCAL(&ifa->ifa_addr)) {
+#if 0
+ trace(1, "route: %s on %s: "
+ "skip linklocal interface address\n",
+ inet6_n2p(&ifa->ifa_addr), ifcp->ifc_name);
+#endif
+ continue;
+ }
+ if (IN6_IS_ADDR_UNSPECIFIED(&ifa->ifa_addr)) {
+#if 0
+ trace(1, "route: %s: skip unspec interface address\n",
+ ifcp->ifc_name);
+#endif
+ continue;
+ }
+ if (IN6_IS_ADDR_LOOPBACK(&ifa->ifa_addr)) {
+#if 0
+ trace(1, "route: %s: skip loopback address\n",
+ ifcp->ifc_name);
+#endif
+ continue;
+ }
+ if (ifcp->ifc_flags & IFF_UP) {
+ if ((rrt = MALLOC(struct riprt)) == NULL)
+ fatal("malloc: struct riprt");
+ memset(rrt, 0, sizeof(*rrt));
+ rrt->rrt_same = NULL;
+ rrt->rrt_index = ifcp->ifc_index;
+ rrt->rrt_t = 0; /* don't age */
+ rrt->rrt_info.rip6_dest = ifa->ifa_addr;
+ rrt->rrt_info.rip6_tag = htons(routetag & 0xffff);
+ rrt->rrt_info.rip6_metric = 1 + ifcp->ifc_metric;
+ rrt->rrt_info.rip6_plen = ifa->ifa_plen;
+ rrt->rrt_flags = RTF_HOST;
+ rrt->rrt_rflags |= RRTF_CHANGED;
+ applyplen(&rrt->rrt_info.rip6_dest, ifa->ifa_plen);
+ memset(&rrt->rrt_gw, 0, sizeof(struct in6_addr));
+ rrt->rrt_gw = ifa->ifa_addr;
+ np = &rrt->rrt_info;
+ search_rrt = rtsearch(np, &prev_rrt);
+ if (search_rrt != NULL) {
+ if (search_rrt->rrt_info.rip6_metric <=
+ rrt->rrt_info.rip6_metric) {
+ /* Already have better route */
+ if (!again) {
+ trace(1, "route: %s/%d: "
+ "already registered (%s)\n",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen,
+ ifcp->ifc_name);
+ }
+ goto next;
+ }
+
+ if (prev_rrt)
+ prev_rrt->rrt_next = rrt->rrt_next;
+ else
+ riprt = rrt->rrt_next;
+ delroute(&rrt->rrt_info, &rrt->rrt_gw);
+ }
+ /* Attach the route to the list */
+ trace(1, "route: %s/%d: register route (%s)\n",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen,
+ ifcp->ifc_name);
+ rrt->rrt_next = riprt;
+ riprt = rrt;
+ addroute(rrt, &rrt->rrt_gw, ifcp);
+ rrt = NULL;
+ sendrequest(ifcp);
+ ripsend(ifcp, &ifcp->ifc_ripsin, 0);
+ need_trigger = 1;
+ } else {
+ for (loop_rrt = riprt; loop_rrt; loop_rrt = loop_rrt->rrt_next) {
+ if (loop_rrt->rrt_index == ifcp->ifc_index) {
+ t_lifetime = time(NULL) - RIP_LIFETIME;
+ if (loop_rrt->rrt_t == 0 || loop_rrt->rrt_t > t_lifetime) {
+ loop_rrt->rrt_t = t_lifetime;
+ loop_rrt->rrt_info.rip6_metric = HOPCNT_INFINITY6;
+ loop_rrt->rrt_rflags |= RRTF_CHANGED;
+ need_trigger = 1;
+ }
+ }
+ }
+ }
+ next:
+ if (rrt)
+ free(rrt);
+ }
+ return need_trigger;
+}
+
+/*
+ * there are couple of p2p interface routing models. "behavior" lets
+ * you pick one. it looks that gated behavior fits best with BSDs,
+ * since BSD kernels do not look at prefix length on p2p interfaces.
+ */
+void
+ifrt_p2p(ifcp, again)
+ struct ifc *ifcp;
+ int again;
+{
+ struct ifac *ifa;
+ struct riprt *rrt, *orrt, *prevrrt;
+ struct netinfo6 *np;
+ struct in6_addr addr, dest;
+ int advert, ignore, i;
+#define P2PADVERT_NETWORK 1
+#define P2PADVERT_ADDR 2
+#define P2PADVERT_DEST 4
+#define P2PADVERT_MAX 4
+ const enum { CISCO, GATED, ROUTE6D } behavior = GATED;
+ const char *category = "";
+ const char *noadv;
+
+ for (ifa = ifcp->ifc_addr; ifa; ifa = ifa->ifa_next) {
+ addr = ifa->ifa_addr;
+ dest = ifa->ifa_raddr;
+ applyplen(&addr, ifa->ifa_plen);
+ applyplen(&dest, ifa->ifa_plen);
+ advert = ignore = 0;
+ switch (behavior) {
+ case CISCO:
+ /*
+ * honor addr/plen, just like normal shared medium
+ * interface. this may cause trouble if you reuse
+ * addr/plen on other interfaces.
+ *
+ * advertise addr/plen.
+ */
+ advert |= P2PADVERT_NETWORK;
+ break;
+ case GATED:
+ /*
+ * prefixlen on p2p interface is meaningless.
+ * advertise addr/128 and dest/128.
+ *
+ * do not install network route to route6d routing
+ * table (if we do, it would prevent route installation
+ * for other p2p interface that shares addr/plen).
+ *
+ * XXX what should we do if dest is ::? it will not
+ * get announced anyways (see following filter),
+ * but we need to think.
+ */
+ advert |= P2PADVERT_ADDR;
+ advert |= P2PADVERT_DEST;
+ ignore |= P2PADVERT_NETWORK;
+ break;
+ case ROUTE6D:
+ /*
+ * just for testing. actually the code is redundant
+ * given the current p2p interface address assignment
+ * rule for kame kernel.
+ *
+ * intent:
+ * A/n -> announce A/n
+ * A B/n, A and B share prefix -> A/n (= B/n)
+ * A B/n, do not share prefix -> A/128 and B/128
+ * actually, A/64 and A B/128 are the only cases
+ * permitted by the kernel:
+ * A/64 -> A/64
+ * A B/128 -> A/128 and B/128
+ */
+ if (!IN6_IS_ADDR_UNSPECIFIED(&ifa->ifa_raddr)) {
+ if (IN6_ARE_ADDR_EQUAL(&addr, &dest))
+ advert |= P2PADVERT_NETWORK;
+ else {
+ advert |= P2PADVERT_ADDR;
+ advert |= P2PADVERT_DEST;
+ ignore |= P2PADVERT_NETWORK;
+ }
+ } else
+ advert |= P2PADVERT_NETWORK;
+ break;
+ }
+
+ for (i = 1; i <= P2PADVERT_MAX; i *= 2) {
+ if ((ignore & i) != 0)
+ continue;
+ if ((rrt = MALLOC(struct riprt)) == NULL) {
+ fatal("malloc: struct riprt");
+ /*NOTREACHED*/
+ }
+ memset(rrt, 0, sizeof(*rrt));
+ rrt->rrt_same = NULL;
+ rrt->rrt_index = ifcp->ifc_index;
+ rrt->rrt_t = 0; /* don't age */
+ switch (i) {
+ case P2PADVERT_NETWORK:
+ rrt->rrt_info.rip6_dest = ifa->ifa_addr;
+ rrt->rrt_info.rip6_plen = ifa->ifa_plen;
+ applyplen(&rrt->rrt_info.rip6_dest,
+ ifa->ifa_plen);
+ category = "network";
+ break;
+ case P2PADVERT_ADDR:
+ rrt->rrt_info.rip6_dest = ifa->ifa_addr;
+ rrt->rrt_info.rip6_plen = 128;
+ rrt->rrt_gw = in6addr_loopback;
+ category = "addr";
+ break;
+ case P2PADVERT_DEST:
+ rrt->rrt_info.rip6_dest = ifa->ifa_raddr;
+ rrt->rrt_info.rip6_plen = 128;
+ rrt->rrt_gw = ifa->ifa_addr;
+ category = "dest";
+ break;
+ }
+ if (IN6_IS_ADDR_UNSPECIFIED(&rrt->rrt_info.rip6_dest) ||
+ IN6_IS_ADDR_LINKLOCAL(&rrt->rrt_info.rip6_dest)) {
+#if 0
+ trace(1, "route: %s: skip unspec/linklocal "
+ "(%s on %s)\n", category, ifcp->ifc_name);
+#endif
+ free(rrt);
+ continue;
+ }
+ if ((advert & i) == 0) {
+ rrt->rrt_rflags |= RRTF_NOADVERTISE;
+ noadv = ", NO-ADV";
+ } else
+ noadv = "";
+ rrt->rrt_info.rip6_tag = htons(routetag & 0xffff);
+ rrt->rrt_info.rip6_metric = 1 + ifcp->ifc_metric;
+ np = &rrt->rrt_info;
+ orrt = rtsearch(np, &prevrrt);
+ if (!orrt) {
+ /* Attach the route to the list */
+ trace(1, "route: %s/%d: register route "
+ "(%s on %s%s)\n",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen,
+ category, ifcp->ifc_name, noadv);
+ rrt->rrt_next = riprt;
+ riprt = rrt;
+ } else if (rrt->rrt_index != orrt->rrt_index ||
+ rrt->rrt_info.rip6_metric != orrt->rrt_info.rip6_metric) {
+ /* swap route */
+ rrt->rrt_next = orrt->rrt_next;
+ if (prevrrt)
+ prevrrt->rrt_next = rrt;
+ else
+ riprt = rrt;
+ free(orrt);
+
+ trace(1, "route: %s/%d: update (%s on %s%s)\n",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen,
+ category, ifcp->ifc_name, noadv);
+ } else {
+ /* Already found */
+ if (!again) {
+ trace(1, "route: %s/%d: "
+ "already registered (%s on %s%s)\n",
+ inet6_n2p(&np->rip6_dest),
+ np->rip6_plen, category,
+ ifcp->ifc_name, noadv);
+ }
+ free(rrt);
+ }
+ }
+ }
+#undef P2PADVERT_NETWORK
+#undef P2PADVERT_ADDR
+#undef P2PADVERT_DEST
+#undef P2PADVERT_MAX
+}
+
+int
+getifmtu(ifindex)
+ int ifindex;
+{
+ int mib[6];
+ char *buf;
+ size_t msize;
+ struct if_msghdr *ifm;
+ int mtu;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET6;
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = ifindex;
+ if (sysctl(mib, 6, NULL, &msize, NULL, 0) < 0) {
+ fatal("sysctl estimate NET_RT_IFLIST");
+ /*NOTREACHED*/
+ }
+ if ((buf = malloc(msize)) == NULL) {
+ fatal("malloc");
+ /*NOTREACHED*/
+ }
+ if (sysctl(mib, 6, buf, &msize, NULL, 0) < 0) {
+ fatal("sysctl NET_RT_IFLIST");
+ /*NOTREACHED*/
+ }
+ ifm = (struct if_msghdr *)buf;
+ mtu = ifm->ifm_data.ifi_mtu;
+ if (ifindex != ifm->ifm_index) {
+ fatal("ifindex does not match with ifm_index");
+ /*NOTREACHED*/
+ }
+ free(buf);
+ return mtu;
+}
+
+const char *
+rttypes(rtm)
+ struct rt_msghdr *rtm;
+{
+#define RTTYPE(s, f) \
+do { \
+ if (rtm->rtm_type == (f)) \
+ return (s); \
+} while (0)
+ RTTYPE("ADD", RTM_ADD);
+ RTTYPE("DELETE", RTM_DELETE);
+ RTTYPE("CHANGE", RTM_CHANGE);
+ RTTYPE("GET", RTM_GET);
+ RTTYPE("LOSING", RTM_LOSING);
+ RTTYPE("REDIRECT", RTM_REDIRECT);
+ RTTYPE("MISS", RTM_MISS);
+ RTTYPE("LOCK", RTM_LOCK);
+ RTTYPE("OLDADD", RTM_OLDADD);
+ RTTYPE("OLDDEL", RTM_OLDDEL);
+ RTTYPE("NEWADDR", RTM_NEWADDR);
+ RTTYPE("DELADDR", RTM_DELADDR);
+ RTTYPE("IFINFO", RTM_IFINFO);
+#ifdef RTM_OLDADD
+ RTTYPE("OLDADD", RTM_OLDADD);
+#endif
+#ifdef RTM_OLDDEL
+ RTTYPE("OLDDEL", RTM_OLDDEL);
+#endif
+#ifdef RTM_OIFINFO
+ RTTYPE("OIFINFO", RTM_OIFINFO);
+#endif
+#ifdef RTM_IFANNOUNCE
+ RTTYPE("IFANNOUNCE", RTM_IFANNOUNCE);
+#endif
+#ifdef RTM_NEWMADDR
+ RTTYPE("NEWMADDR", RTM_NEWMADDR);
+#endif
+#ifdef RTM_DELMADDR
+ RTTYPE("DELMADDR", RTM_DELMADDR);
+#endif
+#undef RTTYPE
+ return NULL;
+}
+
+const char *
+rtflags(rtm)
+ struct rt_msghdr *rtm;
+{
+ static char buf[BUFSIZ];
+
+ /*
+ * letter conflict should be okay. painful when *BSD diverges...
+ */
+ strlcpy(buf, "", sizeof(buf));
+#define RTFLAG(s, f) \
+do { \
+ if (rtm->rtm_flags & (f)) \
+ strlcat(buf, (s), sizeof(buf)); \
+} while (0)
+ RTFLAG("U", RTF_UP);
+ RTFLAG("G", RTF_GATEWAY);
+ RTFLAG("H", RTF_HOST);
+ RTFLAG("R", RTF_REJECT);
+ RTFLAG("D", RTF_DYNAMIC);
+ RTFLAG("M", RTF_MODIFIED);
+ RTFLAG("d", RTF_DONE);
+#ifdef RTF_MASK
+ RTFLAG("m", RTF_MASK);
+#endif
+#ifdef RTF_CLONING
+ RTFLAG("C", RTF_CLONING);
+#endif
+#ifdef RTF_CLONED
+ RTFLAG("c", RTF_CLONED);
+#endif
+#ifdef RTF_PRCLONING
+ RTFLAG("c", RTF_PRCLONING);
+#endif
+#ifdef RTF_WASCLONED
+ RTFLAG("W", RTF_WASCLONED);
+#endif
+ RTFLAG("X", RTF_XRESOLVE);
+#ifdef RTF_LLINFO
+ RTFLAG("L", RTF_LLINFO);
+#endif
+ RTFLAG("S", RTF_STATIC);
+ RTFLAG("B", RTF_BLACKHOLE);
+#ifdef RTF_PROTO3
+ RTFLAG("3", RTF_PROTO3);
+#endif
+ RTFLAG("2", RTF_PROTO2);
+ RTFLAG("1", RTF_PROTO1);
+#ifdef RTF_BROADCAST
+ RTFLAG("b", RTF_BROADCAST);
+#endif
+#ifdef RTF_DEFAULT
+ RTFLAG("d", RTF_DEFAULT);
+#endif
+#ifdef RTF_ISAROUTER
+ RTFLAG("r", RTF_ISAROUTER);
+#endif
+#ifdef RTF_TUNNEL
+ RTFLAG("T", RTF_TUNNEL);
+#endif
+#ifdef RTF_AUTH
+ RTFLAG("A", RTF_AUTH);
+#endif
+#ifdef RTF_CRYPT
+ RTFLAG("E", RTF_CRYPT);
+#endif
+#undef RTFLAG
+ return buf;
+}
+
+const char *
+ifflags(flags)
+ int flags;
+{
+ static char buf[BUFSIZ];
+
+ strlcpy(buf, "", sizeof(buf));
+#define IFFLAG(s, f) \
+do { \
+ if (flags & (f)) { \
+ if (buf[0]) \
+ strlcat(buf, ",", sizeof(buf)); \
+ strlcat(buf, (s), sizeof(buf)); \
+ } \
+} while (0)
+ IFFLAG("UP", IFF_UP);
+ IFFLAG("BROADCAST", IFF_BROADCAST);
+ IFFLAG("DEBUG", IFF_DEBUG);
+ IFFLAG("LOOPBACK", IFF_LOOPBACK);
+ IFFLAG("POINTOPOINT", IFF_POINTOPOINT);
+#ifdef IFF_NOTRAILERS
+ IFFLAG("NOTRAILERS", IFF_NOTRAILERS);
+#endif
+#ifdef IFF_SMART
+ IFFLAG("SMART", IFF_SMART);
+#endif
+ IFFLAG("RUNNING", IFF_RUNNING);
+ IFFLAG("NOARP", IFF_NOARP);
+ IFFLAG("PROMISC", IFF_PROMISC);
+ IFFLAG("ALLMULTI", IFF_ALLMULTI);
+ IFFLAG("OACTIVE", IFF_OACTIVE);
+ IFFLAG("SIMPLEX", IFF_SIMPLEX);
+ IFFLAG("LINK0", IFF_LINK0);
+ IFFLAG("LINK1", IFF_LINK1);
+ IFFLAG("LINK2", IFF_LINK2);
+ IFFLAG("MULTICAST", IFF_MULTICAST);
+#undef IFFLAG
+ return buf;
+}
+
+void
+krtread(again)
+ int again;
+{
+ int mib[6];
+ size_t msize;
+ char *buf, *p, *lim;
+ struct rt_msghdr *rtm;
+ int retry;
+ const char *errmsg;
+
+ retry = 0;
+ buf = NULL;
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET6; /* Address family */
+ mib[4] = NET_RT_DUMP; /* Dump the kernel routing table */
+ mib[5] = 0; /* No flags */
+ do {
+ retry++;
+ errmsg = NULL;
+ if (buf)
+ free(buf);
+ if (sysctl(mib, 6, NULL, &msize, NULL, 0) < 0) {
+ errmsg = "sysctl estimate";
+ continue;
+ }
+ if ((buf = malloc(msize)) == NULL) {
+ errmsg = "malloc";
+ continue;
+ }
+ if (sysctl(mib, 6, buf, &msize, NULL, 0) < 0) {
+ errmsg = "sysctl NET_RT_DUMP";
+ continue;
+ }
+ } while (retry < 5 && errmsg != NULL);
+ if (errmsg) {
+ fatal("%s (with %d retries, msize=%lu)", errmsg, retry,
+ (u_long)msize);
+ /*NOTREACHED*/
+ } else if (1 < retry)
+ syslog(LOG_INFO, "NET_RT_DUMP %d retires", retry);
+
+ lim = buf + msize;
+ for (p = buf; p < lim; p += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)p;
+ rt_entry(rtm, again);
+ }
+ free(buf);
+}
+
+void
+rt_entry(rtm, again)
+ struct rt_msghdr *rtm;
+ int again;
+{
+ struct sockaddr_in6 *sin6_dst, *sin6_gw, *sin6_mask;
+ struct sockaddr_in6 *sin6_genmask, *sin6_ifp;
+ char *rtmp, *ifname = NULL;
+ struct riprt *rrt, *orrt;
+ struct netinfo6 *np;
+ int s;
+
+ sin6_dst = sin6_gw = sin6_mask = sin6_genmask = sin6_ifp = 0;
+ if ((rtm->rtm_flags & RTF_UP) == 0 || rtm->rtm_flags &
+ (RTF_XRESOLVE|RTF_BLACKHOLE)) {
+ return; /* not interested in the link route */
+ }
+ /* do not look at cloned routes */
+#ifdef RTF_WASCLONED
+ if (rtm->rtm_flags & RTF_WASCLONED)
+ return;
+#endif
+#ifdef RTF_CLONED
+ if (rtm->rtm_flags & RTF_CLONED)
+ return;
+#endif
+ /*
+ * do not look at dynamic routes.
+ * netbsd/openbsd cloned routes have UGHD.
+ */
+ if (rtm->rtm_flags & RTF_DYNAMIC)
+ return;
+ rtmp = (char *)(rtm + 1);
+ /* Destination */
+ if ((rtm->rtm_addrs & RTA_DST) == 0)
+ return; /* ignore routes without destination address */
+ sin6_dst = (struct sockaddr_in6 *)rtmp;
+ rtmp += ROUNDUP(sin6_dst->sin6_len);
+ if (rtm->rtm_addrs & RTA_GATEWAY) {
+ sin6_gw = (struct sockaddr_in6 *)rtmp;
+ rtmp += ROUNDUP(sin6_gw->sin6_len);
+ }
+ if (rtm->rtm_addrs & RTA_NETMASK) {
+ sin6_mask = (struct sockaddr_in6 *)rtmp;
+ rtmp += ROUNDUP(sin6_mask->sin6_len);
+ }
+ if (rtm->rtm_addrs & RTA_GENMASK) {
+ sin6_genmask = (struct sockaddr_in6 *)rtmp;
+ rtmp += ROUNDUP(sin6_genmask->sin6_len);
+ }
+ if (rtm->rtm_addrs & RTA_IFP) {
+ sin6_ifp = (struct sockaddr_in6 *)rtmp;
+ rtmp += ROUNDUP(sin6_ifp->sin6_len);
+ }
+
+ /* Destination */
+ if (sin6_dst->sin6_family != AF_INET6)
+ return;
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6_dst->sin6_addr))
+ return; /* Link-local */
+ if (IN6_ARE_ADDR_EQUAL(&sin6_dst->sin6_addr, &in6addr_loopback))
+ return; /* Loopback */
+ if (IN6_IS_ADDR_MULTICAST(&sin6_dst->sin6_addr))
+ return;
+
+ if ((rrt = MALLOC(struct riprt)) == NULL) {
+ fatal("malloc: struct riprt");
+ /*NOTREACHED*/
+ }
+ memset(rrt, 0, sizeof(*rrt));
+ np = &rrt->rrt_info;
+ rrt->rrt_same = NULL;
+ rrt->rrt_t = time(NULL);
+ if (aflag == 0 && (rtm->rtm_flags & RTF_STATIC))
+ rrt->rrt_t = 0; /* Don't age static routes */
+ if ((rtm->rtm_flags & (RTF_HOST|RTF_GATEWAY)) == RTF_HOST)
+ rrt->rrt_t = 0; /* Don't age non-gateway host routes */
+ np->rip6_tag = 0;
+ np->rip6_metric = rtm->rtm_rmx.rmx_hopcount;
+ if (np->rip6_metric < 1)
+ np->rip6_metric = 1;
+ rrt->rrt_flags = rtm->rtm_flags;
+ np->rip6_dest = sin6_dst->sin6_addr;
+
+ /* Mask or plen */
+ if (rtm->rtm_flags & RTF_HOST)
+ np->rip6_plen = 128; /* Host route */
+ else if (sin6_mask)
+ np->rip6_plen = sin6mask2len(sin6_mask);
+ else
+ np->rip6_plen = 0;
+
+ orrt = rtsearch(np, NULL);
+ if (orrt && orrt->rrt_info.rip6_metric != HOPCNT_INFINITY6) {
+ /* Already found */
+ if (!again) {
+ trace(1, "route: %s/%d flags %s: already registered\n",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen,
+ rtflags(rtm));
+ }
+ free(rrt);
+ return;
+ }
+ /* Gateway */
+ if (!sin6_gw)
+ memset(&rrt->rrt_gw, 0, sizeof(struct in6_addr));
+ else {
+ if (sin6_gw->sin6_family == AF_INET6)
+ rrt->rrt_gw = sin6_gw->sin6_addr;
+ else if (sin6_gw->sin6_family == AF_LINK) {
+ /* XXX in case ppp link? */
+ rrt->rrt_gw = in6addr_loopback;
+ } else
+ memset(&rrt->rrt_gw, 0, sizeof(struct in6_addr));
+ }
+ trace(1, "route: %s/%d flags %s",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen, rtflags(rtm));
+ trace(1, " gw %s", inet6_n2p(&rrt->rrt_gw));
+
+ /* Interface */
+ s = rtm->rtm_index;
+ if (s < nindex2ifc && index2ifc[s])
+ ifname = index2ifc[s]->ifc_name;
+ else {
+ trace(1, " not configured\n");
+ free(rrt);
+ return;
+ }
+ trace(1, " if %s sock %d", ifname, s);
+ rrt->rrt_index = s;
+
+ trace(1, "\n");
+
+ /* Check gateway */
+ if (!IN6_IS_ADDR_LINKLOCAL(&rrt->rrt_gw) &&
+ !IN6_IS_ADDR_LOOPBACK(&rrt->rrt_gw) &&
+ (rrt->rrt_flags & RTF_LOCAL) == 0) {
+ trace(0, "***** Gateway %s is not a link-local address.\n",
+ inet6_n2p(&rrt->rrt_gw));
+ trace(0, "***** dest(%s) if(%s) -- Not optimized.\n",
+ inet6_n2p(&rrt->rrt_info.rip6_dest), ifname);
+ rrt->rrt_rflags |= RRTF_NH_NOT_LLADDR;
+ }
+
+ /* Put it to the route list */
+ if (orrt && orrt->rrt_info.rip6_metric == HOPCNT_INFINITY6) {
+ /* replace route list */
+ rrt->rrt_next = orrt->rrt_next;
+ *orrt = *rrt;
+ trace(1, "route: %s/%d flags %s: replace new route\n",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen,
+ rtflags(rtm));
+ free(rrt);
+ } else {
+ rrt->rrt_next = riprt;
+ riprt = rrt;
+ }
+}
+
+int
+addroute(rrt, gw, ifcp)
+ struct riprt *rrt;
+ const struct in6_addr *gw;
+ struct ifc *ifcp;
+{
+ struct netinfo6 *np;
+ u_char buf[BUFSIZ], buf1[BUFSIZ], buf2[BUFSIZ];
+ struct rt_msghdr *rtm;
+ struct sockaddr_in6 *sin6;
+ int len;
+
+ np = &rrt->rrt_info;
+ inet_ntop(AF_INET6, (const void *)gw, (char *)buf1, sizeof(buf1));
+ inet_ntop(AF_INET6, (void *)&ifcp->ifc_mylladdr, (char *)buf2, sizeof(buf2));
+ tracet(1, "ADD: %s/%d gw %s [%d] ifa %s\n",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen, buf1,
+ np->rip6_metric - 1, buf2);
+ if (rtlog)
+ fprintf(rtlog, "%s: ADD: %s/%d gw %s [%d] ifa %s\n", hms(),
+ inet6_n2p(&np->rip6_dest), np->rip6_plen, buf1,
+ np->rip6_metric - 1, buf2);
+ if (nflag)
+ return 0;
+
+ memset(buf, 0, sizeof(buf));
+ rtm = (struct rt_msghdr *)buf;
+ rtm->rtm_type = RTM_ADD;
+ rtm->rtm_version = RTM_VERSION;
+ rtm->rtm_seq = ++seq;
+ rtm->rtm_pid = pid;
+ rtm->rtm_flags = rrt->rrt_flags;
+ rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
+ rtm->rtm_rmx.rmx_hopcount = np->rip6_metric - 1;
+ rtm->rtm_inits = RTV_HOPCOUNT;
+ sin6 = (struct sockaddr_in6 *)&buf[sizeof(struct rt_msghdr)];
+ /* Destination */
+ sin6->sin6_len = sizeof(struct sockaddr_in6);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_addr = np->rip6_dest;
+ sin6 = (struct sockaddr_in6 *)((char *)sin6 + ROUNDUP(sin6->sin6_len));
+ /* Gateway */
+ sin6->sin6_len = sizeof(struct sockaddr_in6);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_addr = *gw;
+ sin6 = (struct sockaddr_in6 *)((char *)sin6 + ROUNDUP(sin6->sin6_len));
+ /* Netmask */
+ sin6->sin6_len = sizeof(struct sockaddr_in6);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_addr = *(plen2mask(np->rip6_plen));
+ sin6 = (struct sockaddr_in6 *)((char *)sin6 + ROUNDUP(sin6->sin6_len));
+
+ len = (char *)sin6 - (char *)buf;
+ rtm->rtm_msglen = len;
+ if (write(rtsock, buf, len) > 0)
+ return 0;
+
+ if (errno == EEXIST) {
+ trace(0, "ADD: Route already exists %s/%d gw %s\n",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen, buf1);
+ if (rtlog)
+ fprintf(rtlog, "ADD: Route already exists %s/%d gw %s\n",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen, buf1);
+ } else {
+ trace(0, "Can not write to rtsock (addroute): %s\n",
+ strerror(errno));
+ if (rtlog)
+ fprintf(rtlog, "\tCan not write to rtsock: %s\n",
+ strerror(errno));
+ }
+ return -1;
+}
+
+int
+delroute(np, gw)
+ struct netinfo6 *np;
+ struct in6_addr *gw;
+{
+ u_char buf[BUFSIZ], buf2[BUFSIZ];
+ struct rt_msghdr *rtm;
+ struct sockaddr_in6 *sin6;
+ int len;
+
+ inet_ntop(AF_INET6, (void *)gw, (char *)buf2, sizeof(buf2));
+ tracet(1, "DEL: %s/%d gw %s\n", inet6_n2p(&np->rip6_dest),
+ np->rip6_plen, buf2);
+ if (rtlog)
+ fprintf(rtlog, "%s: DEL: %s/%d gw %s\n",
+ hms(), inet6_n2p(&np->rip6_dest), np->rip6_plen, buf2);
+ if (nflag)
+ return 0;
+
+ memset(buf, 0, sizeof(buf));
+ rtm = (struct rt_msghdr *)buf;
+ rtm->rtm_type = RTM_DELETE;
+ rtm->rtm_version = RTM_VERSION;
+ rtm->rtm_seq = ++seq;
+ rtm->rtm_pid = pid;
+ rtm->rtm_flags = RTF_UP | RTF_GATEWAY;
+ if (np->rip6_plen == sizeof(struct in6_addr) * 8)
+ rtm->rtm_flags |= RTF_HOST;
+ rtm->rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
+ sin6 = (struct sockaddr_in6 *)&buf[sizeof(struct rt_msghdr)];
+ /* Destination */
+ sin6->sin6_len = sizeof(struct sockaddr_in6);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_addr = np->rip6_dest;
+ sin6 = (struct sockaddr_in6 *)((char *)sin6 + ROUNDUP(sin6->sin6_len));
+ /* Gateway */
+ sin6->sin6_len = sizeof(struct sockaddr_in6);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_addr = *gw;
+ sin6 = (struct sockaddr_in6 *)((char *)sin6 + ROUNDUP(sin6->sin6_len));
+ /* Netmask */
+ sin6->sin6_len = sizeof(struct sockaddr_in6);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_addr = *(plen2mask(np->rip6_plen));
+ sin6 = (struct sockaddr_in6 *)((char *)sin6 + ROUNDUP(sin6->sin6_len));
+
+ len = (char *)sin6 - (char *)buf;
+ rtm->rtm_msglen = len;
+ if (write(rtsock, buf, len) >= 0)
+ return 0;
+
+ if (errno == ESRCH) {
+ trace(0, "RTDEL: Route does not exist: %s/%d gw %s\n",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen, buf2);
+ if (rtlog)
+ fprintf(rtlog, "RTDEL: Route does not exist: %s/%d gw %s\n",
+ inet6_n2p(&np->rip6_dest), np->rip6_plen, buf2);
+ } else {
+ trace(0, "Can not write to rtsock (delroute): %s\n",
+ strerror(errno));
+ if (rtlog)
+ fprintf(rtlog, "\tCan not write to rtsock: %s\n",
+ strerror(errno));
+ }
+ return -1;
+}
+
+struct in6_addr *
+getroute(np, gw)
+ struct netinfo6 *np;
+ struct in6_addr *gw;
+{
+ u_char buf[BUFSIZ];
+ int myseq;
+ int len;
+ struct rt_msghdr *rtm;
+ struct sockaddr_in6 *sin6;
+
+ rtm = (struct rt_msghdr *)buf;
+ len = sizeof(struct rt_msghdr) + sizeof(struct sockaddr_in6);
+ memset(rtm, 0, len);
+ rtm->rtm_type = RTM_GET;
+ rtm->rtm_version = RTM_VERSION;
+ myseq = ++seq;
+ rtm->rtm_seq = myseq;
+ rtm->rtm_addrs = RTA_DST;
+ rtm->rtm_msglen = len;
+ sin6 = (struct sockaddr_in6 *)&buf[sizeof(struct rt_msghdr)];
+ sin6->sin6_len = sizeof(struct sockaddr_in6);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_addr = np->rip6_dest;
+ if (write(rtsock, buf, len) < 0) {
+ if (errno == ESRCH) /* No such route found */
+ return NULL;
+ perror("write to rtsock");
+ exit(1);
+ }
+ do {
+ if ((len = read(rtsock, buf, sizeof(buf))) < 0) {
+ perror("read from rtsock");
+ exit(1);
+ }
+ rtm = (struct rt_msghdr *)buf;
+ } while (rtm->rtm_seq != myseq || rtm->rtm_pid != pid);
+ sin6 = (struct sockaddr_in6 *)&buf[sizeof(struct rt_msghdr)];
+ if (rtm->rtm_addrs & RTA_DST) {
+ sin6 = (struct sockaddr_in6 *)
+ ((char *)sin6 + ROUNDUP(sin6->sin6_len));
+ }
+ if (rtm->rtm_addrs & RTA_GATEWAY) {
+ *gw = sin6->sin6_addr;
+ return gw;
+ }
+ return NULL;
+}
+
+const char *
+inet6_n2p(p)
+ const struct in6_addr *p;
+{
+ static char buf[BUFSIZ];
+
+ return inet_ntop(AF_INET6, (const void *)p, buf, sizeof(buf));
+}
+
+void
+ifrtdump(sig)
+ int sig;
+{
+
+ ifdump(sig);
+ rtdump(sig);
+}
+
+void
+ifdump(sig)
+ int sig;
+{
+ struct ifc *ifcp;
+ FILE *dump;
+ int i;
+
+ if (sig == 0)
+ dump = stderr;
+ else
+ if ((dump = fopen(ROUTE6D_DUMP, "a")) == NULL)
+ dump = stderr;
+
+ fprintf(dump, "%s: Interface Table Dump\n", hms());
+ fprintf(dump, " Number of interfaces: %d\n", nifc);
+ for (i = 0; i < 2; i++) {
+ fprintf(dump, " %sadvertising interfaces:\n", i ? "non-" : "");
+ for (ifcp = ifc; ifcp; ifcp = ifcp->ifc_next) {
+ if (i == 0) {
+ if ((ifcp->ifc_flags & IFF_UP) == 0)
+ continue;
+ if (iff_find(ifcp, 'N') != NULL)
+ continue;
+ } else {
+ if (ifcp->ifc_flags & IFF_UP)
+ continue;
+ }
+ ifdump0(dump, ifcp);
+ }
+ }
+ fprintf(dump, "\n");
+ if (dump != stderr)
+ fclose(dump);
+}
+
+void
+ifdump0(dump, ifcp)
+ FILE *dump;
+ const struct ifc *ifcp;
+{
+ struct ifac *ifa;
+ struct iff *iffp;
+ char buf[BUFSIZ];
+ const char *ft;
+ int addr;
+
+ fprintf(dump, " %s: index(%d) flags(%s) addr(%s) mtu(%d) metric(%d)\n",
+ ifcp->ifc_name, ifcp->ifc_index, ifflags(ifcp->ifc_flags),
+ inet6_n2p(&ifcp->ifc_mylladdr),
+ ifcp->ifc_mtu, ifcp->ifc_metric);
+ for (ifa = ifcp->ifc_addr; ifa; ifa = ifa->ifa_next) {
+ if (ifcp->ifc_flags & IFF_POINTOPOINT) {
+ inet_ntop(AF_INET6, (void *)&ifa->ifa_raddr,
+ buf, sizeof(buf));
+ fprintf(dump, "\t%s/%d -- %s\n",
+ inet6_n2p(&ifa->ifa_addr),
+ ifa->ifa_plen, buf);
+ } else {
+ fprintf(dump, "\t%s/%d\n",
+ inet6_n2p(&ifa->ifa_addr),
+ ifa->ifa_plen);
+ }
+ }
+ if (ifcp->ifc_filter) {
+ fprintf(dump, "\tFilter:");
+ for (iffp = ifcp->ifc_filter; iffp; iffp = iffp->iff_next) {
+ addr = 0;
+ switch (iffp->iff_type) {
+ case 'A':
+ ft = "Aggregate"; addr++; break;
+ case 'N':
+ ft = "No-use"; break;
+ case 'O':
+ ft = "Advertise-only"; addr++; break;
+ case 'T':
+ ft = "Default-only"; break;
+ case 'L':
+ ft = "Listen-only"; addr++; break;
+ default:
+ snprintf(buf, sizeof(buf), "Unknown-%c", iffp->iff_type);
+ ft = buf;
+ addr++;
+ break;
+ }
+ fprintf(dump, " %s", ft);
+ if (addr) {
+ fprintf(dump, "(%s/%d)", inet6_n2p(&iffp->iff_addr),
+ iffp->iff_plen);
+ }
+ }
+ fprintf(dump, "\n");
+ }
+}
+
+void
+rtdump(sig)
+ int sig;
+{
+ struct riprt *rrt;
+ char buf[BUFSIZ];
+ FILE *dump;
+ time_t t, age;
+
+ if (sig == 0)
+ dump = stderr;
+ else
+ if ((dump = fopen(ROUTE6D_DUMP, "a")) == NULL)
+ dump = stderr;
+
+ t = time(NULL);
+ fprintf(dump, "\n%s: Routing Table Dump\n", hms());
+ for (rrt = riprt; rrt; rrt = rrt->rrt_next) {
+ if (rrt->rrt_t == 0)
+ age = 0;
+ else
+ age = t - rrt->rrt_t;
+ inet_ntop(AF_INET6, (void *)&rrt->rrt_info.rip6_dest,
+ buf, sizeof(buf));
+ fprintf(dump, " %s/%d if(%d:%s) gw(%s) [%d] age(%ld)",
+ buf, rrt->rrt_info.rip6_plen, rrt->rrt_index,
+ index2ifc[rrt->rrt_index]->ifc_name,
+ inet6_n2p(&rrt->rrt_gw),
+ rrt->rrt_info.rip6_metric, (long)age);
+ if (rrt->rrt_info.rip6_tag) {
+ fprintf(dump, " tag(0x%04x)",
+ ntohs(rrt->rrt_info.rip6_tag) & 0xffff);
+ }
+ if (rrt->rrt_rflags & RRTF_NH_NOT_LLADDR)
+ fprintf(dump, " NOT-LL");
+ if (rrt->rrt_rflags & RRTF_NOADVERTISE)
+ fprintf(dump, " NO-ADV");
+ fprintf(dump, "\n");
+ }
+ fprintf(dump, "\n");
+ if (dump != stderr)
+ fclose(dump);
+}
+
+/*
+ * Parse the -A (and -O) options and put corresponding filter object to the
+ * specified interface structures. Each of the -A/O option has the following
+ * syntax: -A 5f09:c400::/32,ef0,ef1 (aggregate)
+ * -O 5f09:c400::/32,ef0,ef1 (only when match)
+ */
+void
+filterconfig()
+{
+ int i;
+ char *p, *ap, *iflp, *ifname, *ep;
+ struct iff ftmp, *iff_obj;
+ struct ifc *ifcp;
+ struct riprt *rrt;
+#if 0
+ struct in6_addr gw;
+#endif
+ u_long plen;
+
+ for (i = 0; i < nfilter; i++) {
+ ap = filter[i];
+ iflp = NULL;
+ ifcp = NULL;
+ if (filtertype[i] == 'N' || filtertype[i] == 'T') {
+ iflp = ap;
+ goto ifonly;
+ }
+ if ((p = strchr(ap, ',')) != NULL) {
+ *p++ = '\0';
+ iflp = p;
+ }
+ if ((p = strchr(ap, '/')) == NULL) {
+ fatal("no prefixlen specified for '%s'", ap);
+ /*NOTREACHED*/
+ }
+ *p++ = '\0';
+ if (inet_pton(AF_INET6, ap, &ftmp.iff_addr) != 1) {
+ fatal("invalid prefix specified for '%s'", ap);
+ /*NOTREACHED*/
+ }
+ errno = 0;
+ ep = NULL;
+ plen = strtoul(p, &ep, 10);
+ if (errno || !*p || *ep || plen > sizeof(ftmp.iff_addr) * 8) {
+ fatal("invalid prefix length specified for '%s'", ap);
+ /*NOTREACHED*/
+ }
+ ftmp.iff_plen = plen;
+ ftmp.iff_next = NULL;
+ applyplen(&ftmp.iff_addr, ftmp.iff_plen);
+ifonly:
+ ftmp.iff_type = filtertype[i];
+ if (iflp == NULL || *iflp == '\0') {
+ fatal("no interface specified for '%s'", ap);
+ /*NOTREACHED*/
+ }
+ /* parse the interface listing portion */
+ while (iflp) {
+ ifname = iflp;
+ if ((iflp = strchr(iflp, ',')) != NULL)
+ *iflp++ = '\0';
+ ifcp = ifc_find(ifname);
+ if (ifcp == NULL) {
+ fatal("no interface %s exists", ifname);
+ /*NOTREACHED*/
+ }
+ iff_obj = (struct iff *)malloc(sizeof(struct iff));
+ if (iff_obj == NULL) {
+ fatal("malloc of iff_obj");
+ /*NOTREACHED*/
+ }
+ memcpy((void *)iff_obj, (void *)&ftmp,
+ sizeof(struct iff));
+ /* link it to the interface filter */
+ iff_obj->iff_next = ifcp->ifc_filter;
+ ifcp->ifc_filter = iff_obj;
+ }
+
+ /*
+ * -A: aggregate configuration.
+ */
+ if (filtertype[i] != 'A')
+ continue;
+ /* put the aggregate to the kernel routing table */
+ rrt = (struct riprt *)malloc(sizeof(struct riprt));
+ if (rrt == NULL) {
+ fatal("malloc: rrt");
+ /*NOTREACHED*/
+ }
+ memset(rrt, 0, sizeof(struct riprt));
+ rrt->rrt_info.rip6_dest = ftmp.iff_addr;
+ rrt->rrt_info.rip6_plen = ftmp.iff_plen;
+ rrt->rrt_info.rip6_metric = 1;
+ rrt->rrt_info.rip6_tag = htons(routetag & 0xffff);
+ rrt->rrt_gw = in6addr_loopback;
+ rrt->rrt_flags = RTF_UP | RTF_REJECT;
+ rrt->rrt_rflags = RRTF_AGGREGATE;
+ rrt->rrt_t = 0;
+ rrt->rrt_index = loopifcp->ifc_index;
+#if 0
+ if (getroute(&rrt->rrt_info, &gw)) {
+#if 0
+ /*
+ * When the address has already been registered in the
+ * kernel routing table, it should be removed
+ */
+ delroute(&rrt->rrt_info, &gw);
+#else
+ /* it is safer behavior */
+ errno = EINVAL;
+ fatal("%s/%u already in routing table, "
+ "cannot aggregate",
+ inet6_n2p(&rrt->rrt_info.rip6_dest),
+ rrt->rrt_info.rip6_plen);
+ /*NOTREACHED*/
+#endif
+ }
+#endif
+ /* Put the route to the list */
+ rrt->rrt_next = riprt;
+ riprt = rrt;
+ trace(1, "Aggregate: %s/%d for %s\n",
+ inet6_n2p(&ftmp.iff_addr), ftmp.iff_plen,
+ ifcp->ifc_name);
+ /* Add this route to the kernel */
+ if (nflag) /* do not modify kernel routing table */
+ continue;
+ addroute(rrt, &in6addr_loopback, loopifcp);
+ }
+}
+
+/***************** utility functions *****************/
+
+/*
+ * Returns a pointer to ifac whose address and prefix length matches
+ * with the address and prefix length specified in the arguments.
+ */
+struct ifac *
+ifa_match(ifcp, ia, plen)
+ const struct ifc *ifcp;
+ const struct in6_addr *ia;
+ int plen;
+{
+ struct ifac *ifa;
+
+ for (ifa = ifcp->ifc_addr; ifa; ifa = ifa->ifa_next) {
+ if (IN6_ARE_ADDR_EQUAL(&ifa->ifa_addr, ia) &&
+ ifa->ifa_plen == plen)
+ break;
+ }
+ return ifa;
+}
+
+/*
+ * Return a pointer to riprt structure whose address and prefix length
+ * matches with the address and prefix length found in the argument.
+ * Note: This is not a rtalloc(). Therefore exact match is necessary.
+ */
+struct riprt *
+rtsearch(np, prev_rrt)
+ struct netinfo6 *np;
+ struct riprt **prev_rrt;
+{
+ struct riprt *rrt;
+
+ if (prev_rrt)
+ *prev_rrt = NULL;
+ for (rrt = riprt; rrt; rrt = rrt->rrt_next) {
+ if (rrt->rrt_info.rip6_plen == np->rip6_plen &&
+ IN6_ARE_ADDR_EQUAL(&rrt->rrt_info.rip6_dest,
+ &np->rip6_dest))
+ return rrt;
+ if (prev_rrt)
+ *prev_rrt = rrt;
+ }
+ if (prev_rrt)
+ *prev_rrt = NULL;
+ return 0;
+}
+
+int
+sin6mask2len(sin6)
+ const struct sockaddr_in6 *sin6;
+{
+
+ return mask2len(&sin6->sin6_addr,
+ sin6->sin6_len - offsetof(struct sockaddr_in6, sin6_addr));
+}
+
+int
+mask2len(addr, lenlim)
+ const struct in6_addr *addr;
+ int lenlim;
+{
+ int i = 0, j;
+ const u_char *p = (const u_char *)addr;
+
+ for (j = 0; j < lenlim; j++, p++) {
+ if (*p != 0xff)
+ break;
+ i += 8;
+ }
+ if (j < lenlim) {
+ switch (*p) {
+#define MASKLEN(m, l) case m: do { i += l; break; } while (0)
+ MASKLEN(0xfe, 7); break;
+ MASKLEN(0xfc, 6); break;
+ MASKLEN(0xf8, 5); break;
+ MASKLEN(0xf0, 4); break;
+ MASKLEN(0xe0, 3); break;
+ MASKLEN(0xc0, 2); break;
+ MASKLEN(0x80, 1); break;
+#undef MASKLEN
+ }
+ }
+ return i;
+}
+
+void
+applymask(addr, mask)
+ struct in6_addr *addr, *mask;
+{
+ int i;
+ u_long *p, *q;
+
+ p = (u_long *)addr; q = (u_long *)mask;
+ for (i = 0; i < 4; i++)
+ *p++ &= *q++;
+}
+
+static const u_char plent[8] = {
+ 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe
+};
+
+void
+applyplen(ia, plen)
+ struct in6_addr *ia;
+ int plen;
+{
+ u_char *p;
+ int i;
+
+ p = ia->s6_addr;
+ for (i = 0; i < 16; i++) {
+ if (plen <= 0)
+ *p = 0;
+ else if (plen < 8)
+ *p &= plent[plen];
+ p++, plen -= 8;
+ }
+}
+
+static const int pl2m[9] = {
+ 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff
+};
+
+struct in6_addr *
+plen2mask(n)
+ 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;
+}
+
+char *
+allocopy(p)
+ char *p;
+{
+ int len = strlen(p) + 1;
+ char *q = (char *)malloc(len);
+
+ if (!q) {
+ fatal("malloc");
+ /*NOTREACHED*/
+ }
+
+ strlcpy(q, p, len);
+ return q;
+}
+
+char *
+hms()
+{
+ static char buf[BUFSIZ];
+ time_t t;
+ struct tm *tm;
+
+ t = time(NULL);
+ if ((tm = localtime(&t)) == 0) {
+ fatal("localtime");
+ /*NOTREACHED*/
+ }
+ snprintf(buf, sizeof(buf), "%02d:%02d:%02d", tm->tm_hour, tm->tm_min,
+ tm->tm_sec);
+ return buf;
+}
+
+#define RIPRANDDEV 1.0 /* 30 +- 15, max - min = 30 */
+
+int
+ripinterval(timer)
+ int timer;
+{
+ double r = rand();
+
+ interval = (int)(timer + timer * RIPRANDDEV * (r / RAND_MAX - 0.5));
+ nextalarm = time(NULL) + interval;
+ return interval;
+}
+
+time_t
+ripsuptrig()
+{
+ time_t t;
+
+ double r = rand();
+ t = (int)(RIP_TRIG_INT6_MIN +
+ (RIP_TRIG_INT6_MAX - RIP_TRIG_INT6_MIN) * (r / RAND_MAX));
+ sup_trig_update = time(NULL) + t;
+ return t;
+}
+
+void
+#ifdef __STDC__
+fatal(const char *fmt, ...)
+#else
+fatal(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+ char buf[1024];
+
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+ perror(buf);
+ if (errno)
+ syslog(LOG_ERR, "%s: %s", buf, strerror(errno));
+ else
+ syslog(LOG_ERR, "%s", buf);
+ rtdexit();
+}
+
+void
+#ifdef __STDC__
+tracet(int level, const char *fmt, ...)
+#else
+tracet(level, fmt, va_alist)
+ int level;
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+ if (level <= dflag) {
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ fprintf(stderr, "%s: ", hms());
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+ if (dflag) {
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ if (level > 0)
+ vsyslog(LOG_DEBUG, fmt, ap);
+ else
+ vsyslog(LOG_WARNING, fmt, ap);
+ va_end(ap);
+ }
+}
+
+void
+#ifdef __STDC__
+trace(int level, const char *fmt, ...)
+#else
+trace(level, fmt, va_alist)
+ int level;
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+ if (level <= dflag) {
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ }
+ if (dflag) {
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ if (level > 0)
+ vsyslog(LOG_DEBUG, fmt, ap);
+ else
+ vsyslog(LOG_WARNING, fmt, ap);
+ va_end(ap);
+ }
+}
+
+unsigned int
+if_maxindex()
+{
+ struct if_nameindex *p, *p0;
+ unsigned int max = 0;
+
+ p0 = if_nameindex();
+ for (p = p0; p && p->if_index && p->if_name; p++) {
+ if (max < p->if_index)
+ max = p->if_index;
+ }
+ if_freenameindex(p0);
+ return max;
+}
+
+struct ifc *
+ifc_find(name)
+ char *name;
+{
+ struct ifc *ifcp;
+
+ for (ifcp = ifc; ifcp; ifcp = ifcp->ifc_next) {
+ if (strcmp(name, ifcp->ifc_name) == 0)
+ return ifcp;
+ }
+ return (struct ifc *)NULL;
+}
+
+struct iff *
+iff_find(ifcp, type)
+ struct ifc *ifcp;
+ int type;
+{
+ struct iff *iffp;
+
+ for (iffp = ifcp->ifc_filter; iffp; iffp = iffp->iff_next) {
+ if (iffp->iff_type == type)
+ return iffp;
+ }
+ return NULL;
+}
+
+void
+setindex2ifc(idx, ifcp)
+ int idx;
+ struct ifc *ifcp;
+{
+ int n, nsize;
+ struct ifc **p;
+
+ if (!index2ifc) {
+ nindex2ifc = 5; /*initial guess*/
+ index2ifc = (struct ifc **)
+ malloc(sizeof(*index2ifc) * nindex2ifc);
+ if (index2ifc == NULL) {
+ fatal("malloc");
+ /*NOTREACHED*/
+ }
+ memset(index2ifc, 0, sizeof(*index2ifc) * nindex2ifc);
+ }
+ n = nindex2ifc;
+ for (nsize = nindex2ifc; nsize <= idx; nsize *= 2)
+ ;
+ if (n != nsize) {
+ p = (struct ifc **)realloc(index2ifc,
+ sizeof(*index2ifc) * nsize);
+ if (p == NULL) {
+ fatal("realloc");
+ /*NOTREACHED*/
+ }
+ memset(p + n, 0, sizeof(*index2ifc) * (nindex2ifc - n));
+ index2ifc = p;
+ nindex2ifc = nsize;
+ }
+ index2ifc[idx] = ifcp;
+}
diff --git a/usr.sbin/route6d/route6d.h b/usr.sbin/route6d/route6d.h
new file mode 100644
index 0000000..49a7d5a
--- /dev/null
+++ b/usr.sbin/route6d/route6d.h
@@ -0,0 +1,81 @@
+/* $FreeBSD$ */
+/* $KAME: route6d.h,v 1.8 2003/05/28 09:11:13 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.
+ */
+
+#define ROUTE6D_DUMP "/var/run/route6d_dump"
+#define ROUTE6D_PID "/var/run/route6d.pid"
+
+#define RIP6_VERSION 1
+
+#define RIP6_REQUEST 1
+#define RIP6_RESPONSE 2
+
+#define IFC_CHANGED 1
+
+struct netinfo6 {
+ struct in6_addr rip6_dest;
+ u_short rip6_tag;
+ u_char rip6_plen;
+ u_char rip6_metric;
+};
+
+struct rip6 {
+ u_char rip6_cmd;
+ u_char rip6_vers;
+ u_char rip6_res1[2];
+ struct netinfo6 rip6_nets[1];
+};
+
+#define HOPCNT_INFINITY6 16
+#define NEXTHOP_METRIC 0xff
+#define RIP6_MAXMTU 1500
+
+#define IFMINMTU 1280
+
+#ifndef DEBUG
+#define SUPPLY_INTERVAL6 30
+#define RIP_LIFETIME 180
+#define RIP_HOLDDOWN 120
+#define RIP_TRIG_INT6_MAX 5
+#define RIP_TRIG_INT6_MIN 1
+#else
+/* only for debugging; can not wait for 30sec to appear a bug */
+#define SUPPLY_INTERVAL6 10
+#define RIP_LIFETIME 60
+#define RIP_HOLDDOWN 40
+#define RIP_TRIG_INT6_MAX 5
+#define RIP_TRIG_INT6_MIN 1
+#endif
+
+#define RIP6_PORT 521
+#define RIP6_DEST "ff02::9"
+
+#define LOOPBACK_IF "lo0"
diff --git a/usr.sbin/rpc.lockd/Makefile b/usr.sbin/rpc.lockd/Makefile
new file mode 100644
index 0000000..961ad17
--- /dev/null
+++ b/usr.sbin/rpc.lockd/Makefile
@@ -0,0 +1,29 @@
+# $NetBSD: Makefile,v 1.12 2000/08/07 16:23:31 thorpej Exp $
+# $FreeBSD$
+
+PROG= rpc.lockd
+MAN= rpc.lockd.8
+MLINKS= rpc.lockd.8 lockd.8
+SRCS= kern.c nlm_prot_svc.c lockd.c lock_proc.c lockd_lock.c
+
+CFLAGS+= -I. -I${DESTDIR}/usr/include/rpcsvc
+#WARNS?= 2
+
+DPADD= ${LIBRPCSVC} ${LIBUTIL}
+LDADD= -lrpcsvc -lutil
+
+CLEANFILES= nlm_prot_svc.c nlm_prot.h test
+
+RPCSRC= ${DESTDIR}/usr/include/rpcsvc/nlm_prot.x
+RPCGEN= rpcgen -L -C
+
+nlm_prot_svc.c: ${RPCSRC}
+ ${RPCGEN} -m -o ${.TARGET} ${RPCSRC}
+
+nlm_prot.h: ${RPCSRC}
+ ${RPCGEN} -h -o ${.TARGET} ${RPCSRC}
+
+test: ${.CURDIR}/test.c
+ cc -o test ${.CURDIR}/test.c -lrpcsvc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rpc.lockd/kern.c b/usr.sbin/rpc.lockd/kern.c
new file mode 100644
index 0000000..bf0fc92
--- /dev/null
+++ b/usr.sbin/rpc.lockd/kern.c
@@ -0,0 +1,606 @@
+/*-
+ * Copyright (c) 1997 Berkeley Software Design, 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.
+ * 3. Berkeley Software Design Inc's name may not be used to endorse or
+ * promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN 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 BERKELEY SOFTWARE DESIGN 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.
+ *
+ * from BSDI kern.c,v 1.2 1998/11/25 22:38:27 don Exp
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <netdb.h>
+
+#include "nlm_prot.h"
+#include <nfs/rpcv2.h>
+#include <nfs/nfsproto.h>
+#include <nfsclient/nfs_lock.h>
+
+#include "lockd.h"
+#include "lockd_lock.h"
+#include <nfsclient/nfs.h>
+
+#define DAEMON_USERNAME "daemon"
+
+/* Lock request owner. */
+typedef struct __owner {
+ pid_t pid; /* Process ID. */
+ time_t tod; /* Time-of-day. */
+} OWNER;
+static OWNER owner;
+
+static char hostname[MAXHOSTNAMELEN + 1]; /* Hostname. */
+static int devfd;
+
+static void client_cleanup(void);
+static const char *from_addr(struct sockaddr *);
+int lock_request(LOCKD_MSG *);
+static void set_auth(CLIENT *cl, struct xucred *ucred);
+void show(LOCKD_MSG *);
+int test_request(LOCKD_MSG *);
+int unlock_request(LOCKD_MSG *);
+
+static int
+nfslockdans(int vers, struct lockd_ans *ansp)
+{
+
+ ansp->la_vers = vers;
+ return (write(devfd, ansp, sizeof *ansp) <= 0);
+}
+
+/*
+ * will break because fifo needs to be repopened when EOF'd
+ */
+#define lockd_seteuid(uid) seteuid(uid)
+
+#define d_calls (debug_level > 1)
+#define d_args (debug_level > 2)
+
+static const char *
+from_addr(saddr)
+ struct sockaddr *saddr;
+{
+ static char inet_buf[INET6_ADDRSTRLEN];
+
+ if (getnameinfo(saddr, saddr->sa_len, inet_buf, sizeof(inet_buf),
+ NULL, 0, NI_NUMERICHOST) == 0)
+ return inet_buf;
+ return "???";
+}
+
+void
+client_cleanup(void)
+{
+ (void)lockd_seteuid(0);
+ exit(-1);
+}
+
+/*
+ * client_request --
+ * Loop around messages from the kernel, forwarding them off to
+ * NLM servers.
+ */
+pid_t
+client_request(void)
+{
+ LOCKD_MSG msg;
+ int nr, ret;
+ pid_t child;
+ uid_t daemon_uid;
+ struct passwd *pw;
+
+ /* Open the dev . */
+ devfd = open(_PATH_DEV _PATH_NFSLCKDEV, O_RDWR | O_NONBLOCK);
+ if (devfd < 0) {
+ syslog(LOG_ERR, "open: %s: %m", _PATH_NFSLCKDEV);
+ goto err;
+ }
+
+ signal(SIGPIPE, SIG_IGN);
+
+ /*
+ * Create a separate process, the client code is really a separate
+ * daemon that shares a lot of code.
+ */
+ switch (child = fork()) {
+ case -1:
+ err(1, "fork");
+ case 0:
+ setproctitle("client");
+ break;
+ default:
+ setproctitle("server");
+ return (child);
+ }
+
+ signal(SIGHUP, (sig_t)client_cleanup);
+ signal(SIGTERM, (sig_t)client_cleanup);
+
+ /* Setup. */
+ (void)time(&owner.tod);
+ owner.pid = getpid();
+ (void)gethostname(hostname, sizeof(hostname) - 1);
+
+ pw = getpwnam(DAEMON_USERNAME);
+ if (pw == NULL) {
+ syslog(LOG_ERR, "getpwnam: %s: %m", DAEMON_USERNAME);
+ goto err;
+ }
+ daemon_uid = pw->pw_uid;
+ /* drop our root priviledges */
+ (void)lockd_seteuid(daemon_uid);
+
+ for (;;) {
+ /* Read the fixed length message. */
+ if ((nr = read(devfd, &msg, sizeof(msg))) == sizeof(msg)) {
+ if (d_args)
+ show(&msg);
+
+ if (msg.lm_version != LOCKD_MSG_VERSION) {
+ syslog(LOG_ERR,
+ "unknown msg type: %d", msg.lm_version);
+ }
+ /*
+ * Send it to the NLM server and don't grant the lock
+ * if we fail for any reason.
+ */
+ switch (msg.lm_fl.l_type) {
+ case F_RDLCK:
+ case F_WRLCK:
+ if (msg.lm_getlk)
+ ret = test_request(&msg);
+ else
+ ret = lock_request(&msg);
+ break;
+ case F_UNLCK:
+ ret = unlock_request(&msg);
+ break;
+ default:
+ ret = 1;
+ syslog(LOG_ERR,
+ "unknown lock type: %d", msg.lm_fl.l_type);
+ break;
+ }
+ if (ret) {
+ struct lockd_ans ans;
+
+ ans.la_msg_ident = msg.lm_msg_ident;
+ ans.la_errno = EHOSTUNREACH;
+
+ if (nfslockdans(LOCKD_ANS_VERSION, &ans)) {
+ syslog((errno == EPIPE ? LOG_INFO :
+ LOG_ERR), "process %lu: %m",
+ (u_long)msg.lm_msg_ident.pid);
+ }
+ }
+ } else if (nr == -1) {
+ if (errno != EAGAIN) {
+ syslog(LOG_ERR, "read: %s: %m", _PATH_NFSLCKDEV);
+ goto err;
+ }
+ } else if (nr != 0) {
+ syslog(LOG_ERR,
+ "%s: discard %d bytes", _PATH_NFSLCKDEV, nr);
+ }
+ }
+
+ /* Reached only on error. */
+err:
+ (void)lockd_seteuid(0);
+ _exit (1);
+}
+
+void
+set_auth(cl, xucred)
+ CLIENT *cl;
+ struct xucred *xucred;
+{
+ int ngroups;
+
+ ngroups = xucred->cr_ngroups - 1;
+ if (ngroups > NGRPS)
+ ngroups = NGRPS;
+ if (cl->cl_auth != NULL)
+ cl->cl_auth->ah_ops->ah_destroy(cl->cl_auth);
+ cl->cl_auth = authunix_create(hostname,
+ xucred->cr_uid,
+ xucred->cr_groups[0],
+ ngroups,
+ &xucred->cr_groups[1]);
+}
+
+
+/*
+ * test_request --
+ * Convert a lock LOCKD_MSG into an NLM request, and send it off.
+ */
+int
+test_request(LOCKD_MSG *msg)
+{
+ CLIENT *cli;
+ struct timeval timeout = {0, 0}; /* No timeout, no response. */
+ char dummy;
+
+ if (d_calls)
+ syslog(LOG_DEBUG, "test request: %s: %s to %s",
+ msg->lm_nfsv3 ? "V4" : "V1/3",
+ msg->lm_fl.l_type == F_WRLCK ? "write" : "read",
+ from_addr((struct sockaddr *)&msg->lm_addr));
+
+ if (msg->lm_nfsv3) {
+ struct nlm4_testargs arg4;
+
+ arg4.cookie.n_bytes = (char *)&msg->lm_msg_ident;
+ arg4.cookie.n_len = sizeof(msg->lm_msg_ident);
+ arg4.exclusive = msg->lm_fl.l_type == F_WRLCK ? 1 : 0;
+ arg4.alock.caller_name = hostname;
+ arg4.alock.fh.n_bytes = (char *)&msg->lm_fh;
+ arg4.alock.fh.n_len = msg->lm_fh_len;
+ arg4.alock.oh.n_bytes = (char *)&owner;
+ arg4.alock.oh.n_len = sizeof(owner);
+ arg4.alock.svid = msg->lm_msg_ident.pid;
+ arg4.alock.l_offset = msg->lm_fl.l_start;
+ arg4.alock.l_len = msg->lm_fl.l_len;
+
+ if ((cli = get_client(
+ (struct sockaddr *)&msg->lm_addr,
+ NLM_VERS4)) == NULL)
+ return (1);
+
+ set_auth(cli, &msg->lm_cred);
+ (void)clnt_call(cli, NLM_TEST_MSG,
+ (xdrproc_t)xdr_nlm4_testargs, &arg4,
+ (xdrproc_t)xdr_void, &dummy, timeout);
+ } else {
+ struct nlm_testargs arg;
+
+ arg.cookie.n_bytes = (char *)&msg->lm_msg_ident;
+ arg.cookie.n_len = sizeof(msg->lm_msg_ident);
+ arg.exclusive = msg->lm_fl.l_type == F_WRLCK ? 1 : 0;
+ arg.alock.caller_name = hostname;
+ arg.alock.fh.n_bytes = (char *)&msg->lm_fh;
+ arg.alock.fh.n_len = msg->lm_fh_len;
+ arg.alock.oh.n_bytes = (char *)&owner;
+ arg.alock.oh.n_len = sizeof(owner);
+ arg.alock.svid = msg->lm_msg_ident.pid;
+ arg.alock.l_offset = msg->lm_fl.l_start;
+ arg.alock.l_len = msg->lm_fl.l_len;
+
+ if ((cli = get_client(
+ (struct sockaddr *)&msg->lm_addr,
+ NLM_VERS)) == NULL)
+ return (1);
+
+ set_auth(cli, &msg->lm_cred);
+ (void)clnt_call(cli, NLM_TEST_MSG,
+ (xdrproc_t)xdr_nlm_testargs, &arg,
+ (xdrproc_t)xdr_void, &dummy, timeout);
+ }
+ return (0);
+}
+
+/*
+ * lock_request --
+ * Convert a lock LOCKD_MSG into an NLM request, and send it off.
+ */
+int
+lock_request(LOCKD_MSG *msg)
+{
+ CLIENT *cli;
+ struct nlm4_lockargs arg4;
+ struct nlm_lockargs arg;
+ struct timeval timeout = {0, 0}; /* No timeout, no response. */
+ char dummy;
+
+ if (d_calls)
+ syslog(LOG_DEBUG, "lock request: %s: %s to %s",
+ msg->lm_nfsv3 ? "V4" : "V1/3",
+ msg->lm_fl.l_type == F_WRLCK ? "write" : "read",
+ from_addr((struct sockaddr *)&msg->lm_addr));
+
+ if (msg->lm_nfsv3) {
+ arg4.cookie.n_bytes = (char *)&msg->lm_msg_ident;
+ arg4.cookie.n_len = sizeof(msg->lm_msg_ident);
+ arg4.block = msg->lm_wait ? 1 : 0;
+ arg4.exclusive = msg->lm_fl.l_type == F_WRLCK ? 1 : 0;
+ arg4.alock.caller_name = hostname;
+ arg4.alock.fh.n_bytes = (char *)&msg->lm_fh;
+ arg4.alock.fh.n_len = msg->lm_fh_len;
+ arg4.alock.oh.n_bytes = (char *)&owner;
+ arg4.alock.oh.n_len = sizeof(owner);
+ arg4.alock.svid = msg->lm_msg_ident.pid;
+ arg4.alock.l_offset = msg->lm_fl.l_start;
+ arg4.alock.l_len = msg->lm_fl.l_len;
+ arg4.reclaim = 0;
+ arg4.state = nsm_state;
+
+ if ((cli = get_client(
+ (struct sockaddr *)&msg->lm_addr,
+ NLM_VERS4)) == NULL)
+ return (1);
+
+ set_auth(cli, &msg->lm_cred);
+ (void)clnt_call(cli, NLM_LOCK_MSG,
+ (xdrproc_t)xdr_nlm4_lockargs, &arg4,
+ (xdrproc_t)xdr_void, &dummy, timeout);
+ } else {
+ arg.cookie.n_bytes = (char *)&msg->lm_msg_ident;
+ arg.cookie.n_len = sizeof(msg->lm_msg_ident);
+ arg.block = msg->lm_wait ? 1 : 0;
+ arg.exclusive = msg->lm_fl.l_type == F_WRLCK ? 1 : 0;
+ arg.alock.caller_name = hostname;
+ arg.alock.fh.n_bytes = (char *)&msg->lm_fh;
+ arg.alock.fh.n_len = msg->lm_fh_len;
+ arg.alock.oh.n_bytes = (char *)&owner;
+ arg.alock.oh.n_len = sizeof(owner);
+ arg.alock.svid = msg->lm_msg_ident.pid;
+ arg.alock.l_offset = msg->lm_fl.l_start;
+ arg.alock.l_len = msg->lm_fl.l_len;
+ arg.reclaim = 0;
+ arg.state = nsm_state;
+
+ if ((cli = get_client(
+ (struct sockaddr *)&msg->lm_addr,
+ NLM_VERS)) == NULL)
+ return (1);
+
+ set_auth(cli, &msg->lm_cred);
+ (void)clnt_call(cli, NLM_LOCK_MSG,
+ (xdrproc_t)xdr_nlm_lockargs, &arg,
+ (xdrproc_t)xdr_void, &dummy, timeout);
+ }
+ return (0);
+}
+
+/*
+ * unlock_request --
+ * Convert an unlock LOCKD_MSG into an NLM request, and send it off.
+ */
+int
+unlock_request(LOCKD_MSG *msg)
+{
+ CLIENT *cli;
+ struct nlm4_unlockargs arg4;
+ struct nlm_unlockargs arg;
+ struct timeval timeout = {0, 0}; /* No timeout, no response. */
+ char dummy;
+
+ if (d_calls)
+ syslog(LOG_DEBUG, "unlock request: %s: to %s",
+ msg->lm_nfsv3 ? "V4" : "V1/3",
+ from_addr((struct sockaddr *)&msg->lm_addr));
+
+ if (msg->lm_nfsv3) {
+ arg4.cookie.n_bytes = (char *)&msg->lm_msg_ident;
+ arg4.cookie.n_len = sizeof(msg->lm_msg_ident);
+ arg4.alock.caller_name = hostname;
+ arg4.alock.fh.n_bytes = (char *)&msg->lm_fh;
+ arg4.alock.fh.n_len = msg->lm_fh_len;
+ arg4.alock.oh.n_bytes = (char *)&owner;
+ arg4.alock.oh.n_len = sizeof(owner);
+ arg4.alock.svid = msg->lm_msg_ident.pid;
+ arg4.alock.l_offset = msg->lm_fl.l_start;
+ arg4.alock.l_len = msg->lm_fl.l_len;
+
+ if ((cli = get_client(
+ (struct sockaddr *)&msg->lm_addr,
+ NLM_VERS4)) == NULL)
+ return (1);
+
+ set_auth(cli, &msg->lm_cred);
+ (void)clnt_call(cli, NLM_UNLOCK_MSG,
+ (xdrproc_t)xdr_nlm4_unlockargs, &arg4,
+ (xdrproc_t)xdr_void, &dummy, timeout);
+ } else {
+ arg.cookie.n_bytes = (char *)&msg->lm_msg_ident;
+ arg.cookie.n_len = sizeof(msg->lm_msg_ident);
+ arg.alock.caller_name = hostname;
+ arg.alock.fh.n_bytes = (char *)&msg->lm_fh;
+ arg.alock.fh.n_len = msg->lm_fh_len;
+ arg.alock.oh.n_bytes = (char *)&owner;
+ arg.alock.oh.n_len = sizeof(owner);
+ arg.alock.svid = msg->lm_msg_ident.pid;
+ arg.alock.l_offset = msg->lm_fl.l_start;
+ arg.alock.l_len = msg->lm_fl.l_len;
+
+ if ((cli = get_client(
+ (struct sockaddr *)&msg->lm_addr,
+ NLM_VERS)) == NULL)
+ return (1);
+
+ set_auth(cli, &msg->lm_cred);
+ (void)clnt_call(cli, NLM_UNLOCK_MSG,
+ (xdrproc_t)xdr_nlm_unlockargs, &arg,
+ (xdrproc_t)xdr_void, &dummy, timeout);
+ }
+
+ return (0);
+}
+
+int
+lock_answer(int pid, netobj *netcookie, int result, int *pid_p, int version)
+{
+ struct lockd_ans ans;
+
+ if (netcookie->n_len != sizeof(ans.la_msg_ident)) {
+ if (pid == -1) { /* we're screwed */
+ syslog(LOG_ERR, "inedible nlm cookie");
+ return -1;
+ }
+ ans.la_msg_ident.pid = pid;
+ ans.la_msg_ident.msg_seq = -1;
+ } else {
+ memcpy(&ans.la_msg_ident, netcookie->n_bytes,
+ sizeof(ans.la_msg_ident));
+ }
+
+ if (d_calls)
+ syslog(LOG_DEBUG, "lock answer: pid %lu: %s %d",
+ (unsigned long)ans.la_msg_ident.pid,
+ version == NLM_VERS4 ? "nlmv4" : "nlmv3",
+ result);
+
+ ans.la_set_getlk_pid = 0;
+ if (version == NLM_VERS4)
+ switch (result) {
+ case nlm4_granted:
+ ans.la_errno = 0;
+ break;
+ default:
+ ans.la_errno = EACCES;
+ break;
+ case nlm4_denied:
+ if (pid_p == NULL)
+ ans.la_errno = EAGAIN;
+ else {
+ /* this is an answer to a nlm_test msg */
+ ans.la_set_getlk_pid = 1;
+ ans.la_getlk_pid = *pid_p;
+ ans.la_errno = 0;
+ }
+ break;
+ case nlm4_denied_nolocks:
+ ans.la_errno = EAGAIN;
+ break;
+ case nlm4_blocked:
+ return -1;
+ /* NOTREACHED */
+ case nlm4_denied_grace_period:
+ ans.la_errno = EAGAIN;
+ break;
+ case nlm4_deadlck:
+ ans.la_errno = EDEADLK;
+ break;
+ case nlm4_rofs:
+ ans.la_errno = EROFS;
+ break;
+ case nlm4_stale_fh:
+ ans.la_errno = ESTALE;
+ break;
+ case nlm4_fbig:
+ ans.la_errno = EFBIG;
+ break;
+ case nlm4_failed:
+ ans.la_errno = EACCES;
+ break;
+ }
+ else
+ switch (result) {
+ case nlm_granted:
+ ans.la_errno = 0;
+ break;
+ default:
+ ans.la_errno = EACCES;
+ break;
+ case nlm_denied:
+ if (pid_p == NULL)
+ ans.la_errno = EAGAIN;
+ else {
+ /* this is an answer to a nlm_test msg */
+ ans.la_set_getlk_pid = 1;
+ ans.la_getlk_pid = *pid_p;
+ ans.la_errno = 0;
+ }
+ break;
+ case nlm_denied_nolocks:
+ ans.la_errno = EAGAIN;
+ break;
+ case nlm_blocked:
+ return -1;
+ /* NOTREACHED */
+ case nlm_denied_grace_period:
+ ans.la_errno = EAGAIN;
+ break;
+ case nlm_deadlck:
+ ans.la_errno = EDEADLK;
+ break;
+ }
+
+ if (nfslockdans(LOCKD_ANS_VERSION, &ans)) {
+ syslog(((errno == EPIPE || errno == ESRCH) ?
+ LOG_INFO : LOG_ERR),
+ "process %lu: %m", (u_long)ans.la_msg_ident.pid);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * show --
+ * Display the contents of a kernel LOCKD_MSG structure.
+ */
+void
+show(LOCKD_MSG *mp)
+{
+ static char hex[] = "0123456789abcdef";
+ struct fid *fidp;
+ fsid_t *fsidp;
+ size_t len;
+ u_int8_t *p, *t, buf[NFS_SMALLFH*3+1];
+
+ syslog(LOG_DEBUG, "process ID: %lu\n", (long)mp->lm_msg_ident.pid);
+
+ fsidp = (fsid_t *)&mp->lm_fh;
+ fidp = (struct fid *)((u_int8_t *)&mp->lm_fh + sizeof(fsid_t));
+
+ for (t = buf, p = (u_int8_t *)mp->lm_fh,
+ len = mp->lm_fh_len;
+ len > 0; ++p, --len) {
+ *t++ = '\\';
+ *t++ = hex[(*p & 0xf0) >> 4];
+ *t++ = hex[*p & 0x0f];
+ }
+ *t = '\0';
+
+ syslog(LOG_DEBUG, "fh_len %d, fh %s\n", (int)mp->lm_fh_len, buf);
+
+ /* Show flock structure. */
+ syslog(LOG_DEBUG, "start %qu; len %qu; pid %lu; type %d; whence %d\n",
+ (unsigned long long)mp->lm_fl.l_start,
+ (unsigned long long)mp->lm_fl.l_len, (u_long)mp->lm_fl.l_pid,
+ mp->lm_fl.l_type, mp->lm_fl.l_whence);
+
+ /* Show wait flag. */
+ syslog(LOG_DEBUG, "wait was %s\n", mp->lm_wait ? "set" : "not set");
+}
diff --git a/usr.sbin/rpc.lockd/lock_proc.c b/usr.sbin/rpc.lockd/lock_proc.c
new file mode 100644
index 0000000..0449664
--- /dev/null
+++ b/usr.sbin/rpc.lockd/lock_proc.c
@@ -0,0 +1,1415 @@
+/* $NetBSD: lock_proc.c,v 1.7 2000/10/11 20:23:56 is Exp $ */
+/* $FreeBSD$ */
+/*
+ * Copyright (c) 1995
+ * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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
+ * 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 ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED 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>
+#ifndef lint
+__RCSID("$NetBSD: lock_proc.c,v 1.7 2000/10/11 20:23:56 is Exp $");
+#endif
+
+#include <sys/param.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <netdb.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <netconfig.h>
+
+#include <rpc/rpc.h>
+#include <rpcsvc/sm_inter.h>
+
+#include "lockd.h"
+#include <rpcsvc/nlm_prot.h>
+#include "lockd_lock.h"
+
+
+#define CLIENT_CACHE_SIZE 64 /* No. of client sockets cached */
+#define CLIENT_CACHE_LIFETIME 120 /* In seconds */
+
+#define getrpcaddr(rqstp) (struct sockaddr *)(svc_getrpccaller((rqstp)->rq_xprt)->buf)
+
+static void log_from_addr(const char *, struct svc_req *);
+static void log_netobj(netobj *obj);
+static int addrcmp(struct sockaddr *, struct sockaddr *);
+
+/* log_from_addr ----------------------------------------------------------- */
+/*
+ * Purpose: Log name of function called and source address
+ * Returns: Nothing
+ * Notes: Extracts the source address from the transport handle
+ * passed in as part of the called procedure specification
+ */
+static void
+log_from_addr(fun_name, req)
+ const char *fun_name;
+ struct svc_req *req;
+{
+ struct sockaddr *addr;
+ char hostname_buf[NI_MAXHOST];
+
+ addr = svc_getrpccaller(req->rq_xprt)->buf;
+ if (getnameinfo(addr , addr->sa_len, hostname_buf, sizeof hostname_buf,
+ NULL, 0, 0) != 0)
+ return;
+
+ syslog(LOG_DEBUG, "%s from %s", fun_name, hostname_buf);
+}
+
+/* log_netobj ----------------------------------------------------------- */
+/*
+ * Purpose: Log a netobj
+ * Returns: Nothing
+ * Notes: This function should only really be called as part of
+ * a debug subsystem.
+*/
+static void
+log_netobj(obj)
+ netobj *obj;
+{
+ char objvalbuffer[(sizeof(char)*2)*MAX_NETOBJ_SZ+2];
+ char objascbuffer[sizeof(char)*MAX_NETOBJ_SZ+1];
+ unsigned int i, maxlen;
+ char *tmp1, *tmp2;
+
+ /* Notify of potential security attacks */
+ if (obj->n_len > MAX_NETOBJ_SZ) {
+ syslog(LOG_DEBUG, "SOMEONE IS TRYING TO DO SOMETHING NASTY!\n");
+ syslog(LOG_DEBUG, "netobj too large! Should be %d was %d\n",
+ MAX_NETOBJ_SZ, obj->n_len);
+ }
+ /* Prevent the security hazard from the buffer overflow */
+ maxlen = (obj->n_len < MAX_NETOBJ_SZ ? obj->n_len : MAX_NETOBJ_SZ);
+ for (i=0, tmp1 = objvalbuffer, tmp2 = objascbuffer; i < obj->n_len;
+ i++, tmp1 +=2, tmp2 +=1) {
+ sprintf(tmp1,"%02X",*(obj->n_bytes+i));
+ sprintf(tmp2,"%c",*(obj->n_bytes+i));
+ }
+ *tmp1 = '\0';
+ *tmp2 = '\0';
+ syslog(LOG_DEBUG,"netobjvals: %s\n",objvalbuffer);
+ syslog(LOG_DEBUG,"netobjascs: %s\n",objascbuffer);
+}
+/* get_client -------------------------------------------------------------- */
+/*
+ * Purpose: Get a CLIENT* for making RPC calls to lockd on given host
+ * Returns: CLIENT* pointer, from clnt_udp_create, or NULL if error
+ * Notes: Creating a CLIENT* is quite expensive, involving a
+ * conversation with the remote portmapper to get the
+ * port number. Since a given client is quite likely
+ * to make several locking requests in succession, it is
+ * desirable to cache the created CLIENT*.
+ *
+ * Since we are using UDP rather than TCP, there is no cost
+ * to the remote system in keeping these cached indefinitely.
+ * Unfortunately there is a snag: if the remote system
+ * reboots, the cached portmapper results will be invalid,
+ * and we will never detect this since all of the xxx_msg()
+ * calls return no result - we just fire off a udp packet
+ * and hope for the best.
+ *
+ * We solve this by discarding cached values after two
+ * minutes, regardless of whether they have been used
+ * in the meanwhile (since a bad one might have been used
+ * plenty of times, as the host keeps retrying the request
+ * and we keep sending the reply back to the wrong port).
+ *
+ * Given that the entries will always expire in the order
+ * that they were created, there is no point in a LRU
+ * algorithm for when the cache gets full - entries are
+ * always re-used in sequence.
+ */
+static CLIENT *clnt_cache_ptr[CLIENT_CACHE_SIZE];
+static long clnt_cache_time[CLIENT_CACHE_SIZE]; /* time entry created */
+static struct sockaddr_storage clnt_cache_addr[CLIENT_CACHE_SIZE];
+static rpcvers_t clnt_cache_vers[CLIENT_CACHE_SIZE];
+static int clnt_cache_next_to_use = 0;
+
+static int
+addrcmp(sa1, sa2)
+ struct sockaddr *sa1;
+ struct sockaddr *sa2;
+{
+ int len;
+ void *p1, *p2;
+
+ 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;
+ break;
+ default:
+ return -1;
+ }
+
+ return memcmp(p1, p2, len);
+}
+
+CLIENT *
+get_client(host_addr, vers)
+ struct sockaddr *host_addr;
+ rpcvers_t vers;
+{
+ CLIENT *client;
+ struct timeval retry_time, time_now;
+ int error, i;
+ const char *netid;
+ struct netconfig *nconf;
+ char host[NI_MAXHOST];
+ uid_t old_euid;
+ int clnt_fd;
+
+ gettimeofday(&time_now, NULL);
+
+ /*
+ * Search for the given client in the cache, zapping any expired
+ * entries that we happen to notice in passing.
+ */
+ for (i = 0; i < CLIENT_CACHE_SIZE; i++) {
+ client = clnt_cache_ptr[i];
+ if (client && ((clnt_cache_time[i] + CLIENT_CACHE_LIFETIME)
+ < time_now.tv_sec)) {
+ /* Cache entry has expired. */
+ if (debug_level > 3)
+ syslog(LOG_DEBUG, "Expired CLIENT* in cache");
+ clnt_cache_time[i] = 0L;
+ clnt_destroy(client);
+ clnt_cache_ptr[i] = NULL;
+ client = NULL;
+ }
+ if (client && !addrcmp((struct sockaddr *)&clnt_cache_addr[i],
+ host_addr) && clnt_cache_vers[i] == vers) {
+ /* Found it! */
+ if (debug_level > 3)
+ syslog(LOG_DEBUG, "Found CLIENT* in cache");
+ return (client);
+ }
+ }
+
+ if (debug_level > 3)
+ syslog(LOG_DEBUG, "CLIENT* not found in cache, creating");
+
+ /* Not found in cache. Free the next entry if it is in use. */
+ if (clnt_cache_ptr[clnt_cache_next_to_use]) {
+ clnt_destroy(clnt_cache_ptr[clnt_cache_next_to_use]);
+ clnt_cache_ptr[clnt_cache_next_to_use] = NULL;
+ }
+
+ /*
+ * Need a host string for clnt_tp_create. Use NI_NUMERICHOST
+ * to avoid DNS lookups.
+ */
+ error = getnameinfo(host_addr, host_addr->sa_len, host, sizeof host,
+ NULL, 0, NI_NUMERICHOST);
+ if (error != 0) {
+ syslog(LOG_ERR, "unable to get name string for caller: %s",
+ gai_strerror(error));
+ return NULL;
+ }
+
+#if 1
+ if (host_addr->sa_family == AF_INET6)
+ netid = "udp6";
+ else
+ netid = "udp";
+#else
+ if (host_addr->sa_family == AF_INET6)
+ netid = "tcp6";
+ else
+ netid = "tcp";
+#endif
+ nconf = getnetconfigent(netid);
+ if (nconf == NULL) {
+ syslog(LOG_ERR, "could not get netconfig info for '%s': "
+ "no /etc/netconfig file?", netid);
+ return NULL;
+ }
+
+ client = clnt_tp_create(host, NLM_PROG, vers, nconf);
+ freenetconfigent(nconf);
+
+ if (!client) {
+ syslog(LOG_ERR, "%s", clnt_spcreateerror("clntudp_create"));
+ syslog(LOG_ERR, "Unable to return result to %s", host);
+ return NULL;
+ }
+
+ /* Get the FD of the client, for bindresvport. */
+ clnt_control(client, CLGET_FD, &clnt_fd);
+
+ /* Regain root privileges, for bindresvport. */
+ old_euid = geteuid();
+ seteuid(0);
+
+ /*
+ * Bind the client FD to a reserved port.
+ * Some NFS servers reject any NLM request from a non-reserved port.
+ */
+ bindresvport(clnt_fd, NULL);
+
+ /* Drop root privileges again. */
+ seteuid(old_euid);
+
+ /* Success - update the cache entry */
+ clnt_cache_ptr[clnt_cache_next_to_use] = client;
+ memcpy(&clnt_cache_addr[clnt_cache_next_to_use], host_addr,
+ host_addr->sa_len);
+ clnt_cache_vers[clnt_cache_next_to_use] = vers;
+ clnt_cache_time[clnt_cache_next_to_use] = time_now.tv_sec;
+ if (++clnt_cache_next_to_use >= CLIENT_CACHE_SIZE)
+ clnt_cache_next_to_use = 0;
+
+ /*
+ * Disable the default timeout, so we can specify our own in calls
+ * to clnt_call(). (Note that the timeout is a different concept
+ * from the retry period set in clnt_udp_create() above.)
+ */
+ retry_time.tv_sec = -1;
+ retry_time.tv_usec = -1;
+ clnt_control(client, CLSET_TIMEOUT, (char *)&retry_time);
+
+ if (debug_level > 3)
+ syslog(LOG_DEBUG, "Created CLIENT* for %s", host);
+ return client;
+}
+
+
+/* transmit_result --------------------------------------------------------- */
+/*
+ * Purpose: Transmit result for nlm_xxx_msg pseudo-RPCs
+ * Returns: Nothing - we have no idea if the datagram got there
+ * Notes: clnt_call() will always fail (with timeout) as we are
+ * calling it with timeout 0 as a hack to just issue a datagram
+ * without expecting a result
+ */
+void
+transmit_result(opcode, result, addr)
+ int opcode;
+ nlm_res *result;
+ struct sockaddr *addr;
+{
+ static char dummy;
+ CLIENT *cli;
+ struct timeval timeo;
+ int success;
+
+ if ((cli = get_client(addr, NLM_VERS)) != NULL) {
+ timeo.tv_sec = 0; /* No timeout - not expecting response */
+ timeo.tv_usec = 0;
+
+ success = clnt_call(cli, opcode, (xdrproc_t)xdr_nlm_res, result,
+ (xdrproc_t)xdr_void, &dummy, timeo);
+
+ if (debug_level > 2)
+ syslog(LOG_DEBUG, "clnt_call returns %d(%s)",
+ success, clnt_sperrno(success));
+ }
+}
+/* transmit4_result --------------------------------------------------------- */
+/*
+ * Purpose: Transmit result for nlm4_xxx_msg pseudo-RPCs
+ * Returns: Nothing - we have no idea if the datagram got there
+ * Notes: clnt_call() will always fail (with timeout) as we are
+ * calling it with timeout 0 as a hack to just issue a datagram
+ * without expecting a result
+ */
+void
+transmit4_result(opcode, result, addr)
+ int opcode;
+ nlm4_res *result;
+ struct sockaddr *addr;
+{
+ static char dummy;
+ CLIENT *cli;
+ struct timeval timeo;
+ int success;
+
+ if ((cli = get_client(addr, NLM_VERS4)) != NULL) {
+ timeo.tv_sec = 0; /* No timeout - not expecting response */
+ timeo.tv_usec = 0;
+
+ success = clnt_call(cli, opcode,
+ (xdrproc_t)xdr_nlm4_res, result,
+ (xdrproc_t)xdr_void, &dummy, timeo);
+
+ if (debug_level > 2)
+ syslog(LOG_DEBUG, "clnt_call returns %d(%s)",
+ success, clnt_sperrno(success));
+ }
+}
+
+/*
+ * converts a struct nlm_lock to struct nlm4_lock
+ */
+static void nlmtonlm4(struct nlm_lock *, struct nlm4_lock *);
+static void
+nlmtonlm4(arg, arg4)
+ struct nlm_lock *arg;
+ struct nlm4_lock *arg4;
+{
+ arg4->caller_name = arg->caller_name;
+ arg4->fh = arg->fh;
+ arg4->oh = arg->oh;
+ arg4->svid = arg->svid;
+ arg4->l_offset = arg->l_offset;
+ arg4->l_len = arg->l_len;
+}
+/* ------------------------------------------------------------------------- */
+/*
+ * Functions for Unix<->Unix locking (ie. monitored locking, with rpc.statd
+ * involved to ensure reclaim of locks after a crash of the "stateless"
+ * server.
+ *
+ * These all come in two flavours - nlm_xxx() and nlm_xxx_msg().
+ * The first are standard RPCs with argument and result.
+ * The nlm_xxx_msg() calls implement exactly the same functions, but
+ * use two pseudo-RPCs (one in each direction). These calls are NOT
+ * standard use of the RPC protocol in that they do not return a result
+ * at all (NB. this is quite different from returning a void result).
+ * The effect of this is to make the nlm_xxx_msg() calls simple unacknowledged
+ * datagrams, requiring higher-level code to perform retries.
+ *
+ * Despite the disadvantages of the nlm_xxx_msg() approach (some of which
+ * are documented in the comments to get_client() above), this is the
+ * interface used by all current commercial NFS implementations
+ * [Solaris, SCO, AIX etc.]. This is presumed to be because these allow
+ * implementations to continue using the standard RPC libraries, while
+ * avoiding the block-until-result nature of the library interface.
+ *
+ * No client implementations have been identified so far that make use
+ * of the true RPC version (early SunOS releases would be a likely candidate
+ * for testing).
+ */
+
+/* nlm_test ---------------------------------------------------------------- */
+/*
+ * Purpose: Test whether a specified lock would be granted if requested
+ * Returns: nlm_granted (or error code)
+ * Notes:
+ */
+nlm_testres *
+nlm_test_1_svc(arg, rqstp)
+ nlm_testargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_testres res;
+ struct nlm4_lock arg4;
+ struct nlm4_holder *holder;
+ nlmtonlm4(&arg->alock, &arg4);
+
+ if (debug_level)
+ log_from_addr("nlm_test", rqstp);
+
+ holder = testlock(&arg4, arg->exclusive, 0);
+ /*
+ * Copy the cookie from the argument into the result. Note that this
+ * is slightly hazardous, as the structure contains a pointer to a
+ * malloc()ed buffer that will get freed by the caller. However, the
+ * main function transmits the result before freeing the argument
+ * so it is in fact safe.
+ */
+ res.cookie = arg->cookie;
+ if (holder == NULL) {
+ res.stat.stat = nlm_granted;
+ } else {
+ res.stat.stat = nlm_denied;
+ memcpy(&res.stat.nlm_testrply_u.holder, holder,
+ sizeof(struct nlm_holder));
+ res.stat.nlm_testrply_u.holder.l_offset = holder->l_offset;
+ res.stat.nlm_testrply_u.holder.l_len = holder->l_len;
+ }
+ return (&res);
+}
+
+void *
+nlm_test_msg_1_svc(arg, rqstp)
+ nlm_testargs *arg;
+ struct svc_req *rqstp;
+{
+ nlm_testres res;
+ static char dummy;
+ struct sockaddr *addr;
+ CLIENT *cli;
+ int success;
+ struct timeval timeo;
+ struct nlm4_lock arg4;
+ struct nlm4_holder *holder;
+
+ nlmtonlm4(&arg->alock, &arg4);
+
+ if (debug_level)
+ log_from_addr("nlm_test_msg", rqstp);
+
+ holder = testlock(&arg4, arg->exclusive, 0);
+
+ res.cookie = arg->cookie;
+ if (holder == NULL) {
+ res.stat.stat = nlm_granted;
+ } else {
+ res.stat.stat = nlm_denied;
+ memcpy(&res.stat.nlm_testrply_u.holder, holder,
+ sizeof(struct nlm_holder));
+ res.stat.nlm_testrply_u.holder.l_offset = holder->l_offset;
+ res.stat.nlm_testrply_u.holder.l_len = holder->l_len;
+ }
+
+ /*
+ * nlm_test has different result type to the other operations, so
+ * can't use transmit_result() in this case
+ */
+ addr = svc_getrpccaller(rqstp->rq_xprt)->buf;
+ if ((cli = get_client(addr, NLM_VERS)) != NULL) {
+ timeo.tv_sec = 0; /* No timeout - not expecting response */
+ timeo.tv_usec = 0;
+
+ success = clnt_call(cli, NLM_TEST_RES,
+ (xdrproc_t)xdr_nlm_testres, &res,
+ (xdrproc_t)xdr_void, &dummy, timeo);
+
+ if (debug_level > 2)
+ syslog(LOG_DEBUG, "clnt_call returns %d", success);
+ }
+ return (NULL);
+}
+
+/* nlm_lock ---------------------------------------------------------------- */
+/*
+ * Purposes: Establish a lock
+ * Returns: granted, denied or blocked
+ * Notes: *** grace period support missing
+ */
+nlm_res *
+nlm_lock_1_svc(arg, rqstp)
+ nlm_lockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_res res;
+ struct nlm4_lockargs arg4;
+ nlmtonlm4(&arg->alock, &arg4.alock);
+ arg4.cookie = arg->cookie;
+ arg4.block = arg->block;
+ arg4.exclusive = arg->exclusive;
+ arg4.reclaim = arg->reclaim;
+ arg4.state = arg->state;
+
+ if (debug_level)
+ log_from_addr("nlm_lock", rqstp);
+
+ /* copy cookie from arg to result. See comment in nlm_test_1() */
+ res.cookie = arg->cookie;
+
+ res.stat.stat = getlock(&arg4, rqstp, LOCK_MON);
+ return (&res);
+}
+
+void *
+nlm_lock_msg_1_svc(arg, rqstp)
+ nlm_lockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_res res;
+ struct nlm4_lockargs arg4;
+
+ nlmtonlm4(&arg->alock, &arg4.alock);
+ arg4.cookie = arg->cookie;
+ arg4.block = arg->block;
+ arg4.exclusive = arg->exclusive;
+ arg4.reclaim = arg->reclaim;
+ arg4.state = arg->state;
+
+ if (debug_level)
+ log_from_addr("nlm_lock_msg", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat.stat = getlock(&arg4, rqstp, LOCK_ASYNC | LOCK_MON);
+ transmit_result(NLM_LOCK_RES, &res, getrpcaddr(rqstp));
+
+ return (NULL);
+}
+
+/* nlm_cancel -------------------------------------------------------------- */
+/*
+ * Purpose: Cancel a blocked lock request
+ * Returns: granted or denied
+ * Notes:
+ */
+nlm_res *
+nlm_cancel_1_svc(arg, rqstp)
+ nlm_cancargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_res res;
+ struct nlm4_lock arg4;
+
+ nlmtonlm4(&arg->alock, &arg4);
+
+ if (debug_level)
+ log_from_addr("nlm_cancel", rqstp);
+
+ /* copy cookie from arg to result. See comment in nlm_test_1() */
+ res.cookie = arg->cookie;
+
+ /*
+ * Since at present we never return 'nlm_blocked', there can never be
+ * a lock to cancel, so this call always fails.
+ */
+ res.stat.stat = unlock(&arg4, LOCK_CANCEL);
+ return (&res);
+}
+
+void *
+nlm_cancel_msg_1_svc(arg, rqstp)
+ nlm_cancargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_res res;
+ struct nlm4_lock arg4;
+
+ nlmtonlm4(&arg->alock, &arg4);
+
+ if (debug_level)
+ log_from_addr("nlm_cancel_msg", rqstp);
+
+ res.cookie = arg->cookie;
+ /*
+ * Since at present we never return 'nlm_blocked', there can never be
+ * a lock to cancel, so this call always fails.
+ */
+ res.stat.stat = unlock(&arg4, LOCK_CANCEL);
+ transmit_result(NLM_CANCEL_RES, &res, getrpcaddr(rqstp));
+ return (NULL);
+}
+
+/* nlm_unlock -------------------------------------------------------------- */
+/*
+ * Purpose: Release an existing lock
+ * Returns: Always granted, unless during grace period
+ * Notes: "no such lock" error condition is ignored, as the
+ * protocol uses unreliable UDP datagrams, and may well
+ * re-try an unlock that has already succeeded.
+ */
+nlm_res *
+nlm_unlock_1_svc(arg, rqstp)
+ nlm_unlockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_res res;
+ struct nlm4_lock arg4;
+
+ nlmtonlm4(&arg->alock, &arg4);
+
+ if (debug_level)
+ log_from_addr("nlm_unlock", rqstp);
+
+ res.stat.stat = unlock(&arg4, 0);
+ res.cookie = arg->cookie;
+
+ return (&res);
+}
+
+void *
+nlm_unlock_msg_1_svc(arg, rqstp)
+ nlm_unlockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_res res;
+ struct nlm4_lock arg4;
+
+ nlmtonlm4(&arg->alock, &arg4);
+
+ if (debug_level)
+ log_from_addr("nlm_unlock_msg", rqstp);
+
+ res.stat.stat = unlock(&arg4, 0);
+ res.cookie = arg->cookie;
+
+ transmit_result(NLM_UNLOCK_RES, &res, getrpcaddr(rqstp));
+ return (NULL);
+}
+
+/* ------------------------------------------------------------------------- */
+/*
+ * Client-side pseudo-RPCs for results. Note that for the client there
+ * are only nlm_xxx_msg() versions of each call, since the 'real RPC'
+ * version returns the results in the RPC result, and so the client
+ * does not normally receive incoming RPCs.
+ *
+ * The exception to this is nlm_granted(), which is genuinely an RPC
+ * call from the server to the client - a 'call-back' in normal procedure
+ * call terms.
+ */
+
+/* nlm_granted ------------------------------------------------------------- */
+/*
+ * Purpose: Receive notification that formerly blocked lock now granted
+ * Returns: always success ('granted')
+ * Notes:
+ */
+nlm_res *
+nlm_granted_1_svc(arg, rqstp)
+ nlm_testargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_res res;
+
+ if (debug_level)
+ log_from_addr("nlm_granted", rqstp);
+
+ res.stat.stat = lock_answer(arg->alock.svid, &arg->cookie,
+ nlm_granted, NULL, NLM_VERS) == 0 ?
+ nlm_granted : nlm_denied;
+
+ /* copy cookie from arg to result. See comment in nlm_test_1() */
+ res.cookie = arg->cookie;
+
+ return (&res);
+}
+
+void *
+nlm_granted_msg_1_svc(arg, rqstp)
+ nlm_testargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_res res;
+
+ if (debug_level)
+ log_from_addr("nlm_granted_msg", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat.stat = lock_answer(arg->alock.svid, &arg->cookie,
+ nlm_granted, NULL, NLM_VERS) == 0 ?
+ nlm_granted : nlm_denied;
+
+ transmit_result(NLM_GRANTED_RES, &res, getrpcaddr(rqstp));
+ return (NULL);
+}
+
+/* nlm_test_res ------------------------------------------------------------ */
+/*
+ * Purpose: Accept result from earlier nlm_test_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm_test_res_1_svc(arg, rqstp)
+ nlm_testres *arg;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm_test_res", rqstp);
+ (void)lock_answer(-1, &arg->cookie, arg->stat.stat,
+ &arg->stat.nlm_testrply_u.holder.svid, NLM_VERS);
+ return (NULL);
+}
+
+/* nlm_lock_res ------------------------------------------------------------ */
+/*
+ * Purpose: Accept result from earlier nlm_lock_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm_lock_res_1_svc(arg, rqstp)
+ nlm_res *arg;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm_lock_res", rqstp);
+
+ (void)lock_answer(-1, &arg->cookie, arg->stat.stat, NULL, NLM_VERS);
+
+ return (NULL);
+}
+
+/* nlm_cancel_res ---------------------------------------------------------- */
+/*
+ * Purpose: Accept result from earlier nlm_cancel_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm_cancel_res_1_svc(arg, rqstp)
+ nlm_res *arg __unused;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm_cancel_res", rqstp);
+ return (NULL);
+}
+
+/* nlm_unlock_res ---------------------------------------------------------- */
+/*
+ * Purpose: Accept result from earlier nlm_unlock_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm_unlock_res_1_svc(arg, rqstp)
+ nlm_res *arg;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm_unlock_res", rqstp);
+
+ lock_answer(-1, &arg->cookie, arg->stat.stat, NULL, NLM_VERS);
+
+ return (NULL);
+}
+
+/* nlm_granted_res --------------------------------------------------------- */
+/*
+ * Purpose: Accept result from earlier nlm_granted_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm_granted_res_1_svc(arg, rqstp)
+ nlm_res *arg __unused;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm_granted_res", rqstp);
+ return (NULL);
+}
+
+/* ------------------------------------------------------------------------- */
+/*
+ * Calls for PCNFS locking (aka non-monitored locking, no involvement
+ * of rpc.statd).
+ *
+ * These are all genuine RPCs - no nlm_xxx_msg() nonsense here.
+ */
+
+/* nlm_share --------------------------------------------------------------- */
+/*
+ * Purpose: Establish a DOS-style lock
+ * Returns: success or failure
+ * Notes: Blocking locks are not supported - client is expected
+ * to retry if required.
+ */
+nlm_shareres *
+nlm_share_3_svc(arg, rqstp)
+ nlm_shareargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_shareres res;
+
+ if (debug_level)
+ log_from_addr("nlm_share", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat = nlm_granted;
+ res.sequence = 1234356; /* X/Open says this field is ignored? */
+ return (&res);
+}
+
+/* nlm_unshare ------------------------------------------------------------ */
+/*
+ * Purpose: Release a DOS-style lock
+ * Returns: nlm_granted, unless in grace period
+ * Notes:
+ */
+nlm_shareres *
+nlm_unshare_3_svc(arg, rqstp)
+ nlm_shareargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_shareres res;
+
+ if (debug_level)
+ log_from_addr("nlm_unshare", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat = nlm_granted;
+ res.sequence = 1234356; /* X/Open says this field is ignored? */
+ return (&res);
+}
+
+/* nlm_nm_lock ------------------------------------------------------------ */
+/*
+ * Purpose: non-monitored version of nlm_lock()
+ * Returns: as for nlm_lock()
+ * Notes: These locks are in the same style as the standard nlm_lock,
+ * but the rpc.statd should not be called to establish a
+ * monitor for the client machine, since that machine is
+ * declared not to be running a rpc.statd, and so would not
+ * respond to the statd protocol.
+ */
+nlm_res *
+nlm_nm_lock_3_svc(arg, rqstp)
+ nlm_lockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm_res res;
+
+ if (debug_level)
+ log_from_addr("nlm_nm_lock", rqstp);
+
+ /* copy cookie from arg to result. See comment in nlm_test_1() */
+ res.cookie = arg->cookie;
+ res.stat.stat = nlm_granted;
+ return (&res);
+}
+
+/* nlm_free_all ------------------------------------------------------------ */
+/*
+ * Purpose: Release all locks held by a named client
+ * Returns: Nothing
+ * Notes: Potential denial of service security problem here - the
+ * locks to be released are specified by a host name, independent
+ * of the address from which the request has arrived.
+ * Should probably be rejected if the named host has been
+ * using monitored locks.
+ */
+void *
+nlm_free_all_3_svc(arg, rqstp)
+ nlm_notify *arg __unused;
+ struct svc_req *rqstp;
+{
+ static char dummy;
+
+ if (debug_level)
+ log_from_addr("nlm_free_all", rqstp);
+ return (&dummy);
+}
+
+/* calls for nlm version 4 (NFSv3) */
+/* nlm_test ---------------------------------------------------------------- */
+/*
+ * Purpose: Test whether a specified lock would be granted if requested
+ * Returns: nlm_granted (or error code)
+ * Notes:
+ */
+nlm4_testres *
+nlm4_test_4_svc(arg, rqstp)
+ nlm4_testargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_testres res;
+ struct nlm4_holder *holder;
+
+ if (debug_level)
+ log_from_addr("nlm4_test", rqstp);
+ if (debug_level > 5) {
+ syslog(LOG_DEBUG, "Locking arguments:\n");
+ log_netobj(&(arg->cookie));
+ syslog(LOG_DEBUG, "Alock arguments:\n");
+ syslog(LOG_DEBUG, "Caller Name: %s\n",arg->alock.caller_name);
+ syslog(LOG_DEBUG, "File Handle:\n");
+ log_netobj(&(arg->alock.fh));
+ syslog(LOG_DEBUG, "Owner Handle:\n");
+ log_netobj(&(arg->alock.oh));
+ syslog(LOG_DEBUG, "SVID: %d\n", arg->alock.svid);
+ syslog(LOG_DEBUG, "Lock Offset: %llu\n",
+ (unsigned long long)arg->alock.l_offset);
+ syslog(LOG_DEBUG, "Lock Length: %llu\n",
+ (unsigned long long)arg->alock.l_len);
+ syslog(LOG_DEBUG, "Exclusive: %s\n",
+ (arg->exclusive ? "true" : "false"));
+ }
+
+ holder = testlock(&arg->alock, arg->exclusive, LOCK_V4);
+
+ /*
+ * Copy the cookie from the argument into the result. Note that this
+ * is slightly hazardous, as the structure contains a pointer to a
+ * malloc()ed buffer that will get freed by the caller. However, the
+ * main function transmits the result before freeing the argument
+ * so it is in fact safe.
+ */
+ res.cookie = arg->cookie;
+ if (holder == NULL) {
+ res.stat.stat = nlm4_granted;
+ } else {
+ res.stat.stat = nlm4_denied;
+ memcpy(&res.stat.nlm4_testrply_u.holder, holder,
+ sizeof(struct nlm4_holder));
+ }
+ return (&res);
+}
+
+void *
+nlm4_test_msg_4_svc(arg, rqstp)
+ nlm4_testargs *arg;
+ struct svc_req *rqstp;
+{
+ nlm4_testres res;
+ static char dummy;
+ struct sockaddr *addr;
+ CLIENT *cli;
+ int success;
+ struct timeval timeo;
+ struct nlm4_holder *holder;
+
+ if (debug_level)
+ log_from_addr("nlm4_test_msg", rqstp);
+
+ holder = testlock(&arg->alock, arg->exclusive, LOCK_V4);
+
+ res.cookie = arg->cookie;
+ if (holder == NULL) {
+ res.stat.stat = nlm4_granted;
+ } else {
+ res.stat.stat = nlm4_denied;
+ memcpy(&res.stat.nlm4_testrply_u.holder, holder,
+ sizeof(struct nlm4_holder));
+ }
+
+ /*
+ * nlm_test has different result type to the other operations, so
+ * can't use transmit4_result() in this case
+ */
+ addr = svc_getrpccaller(rqstp->rq_xprt)->buf;
+ if ((cli = get_client(addr, NLM_VERS4)) != NULL) {
+ timeo.tv_sec = 0; /* No timeout - not expecting response */
+ timeo.tv_usec = 0;
+
+ success = clnt_call(cli, NLM4_TEST_RES,
+ (xdrproc_t)xdr_nlm4_testres, &res,
+ (xdrproc_t)xdr_void, &dummy, timeo);
+
+ if (debug_level > 2)
+ syslog(LOG_DEBUG, "clnt_call returns %d", success);
+ }
+ return (NULL);
+}
+
+/* nlm_lock ---------------------------------------------------------------- */
+/*
+ * Purposes: Establish a lock
+ * Returns: granted, denied or blocked
+ * Notes: *** grace period support missing
+ */
+nlm4_res *
+nlm4_lock_4_svc(arg, rqstp)
+ nlm4_lockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_res res;
+
+ if (debug_level)
+ log_from_addr("nlm4_lock", rqstp);
+ if (debug_level > 5) {
+ syslog(LOG_DEBUG, "Locking arguments:\n");
+ log_netobj(&(arg->cookie));
+ syslog(LOG_DEBUG, "Alock arguments:\n");
+ syslog(LOG_DEBUG, "Caller Name: %s\n",arg->alock.caller_name);
+ syslog(LOG_DEBUG, "File Handle:\n");
+ log_netobj(&(arg->alock.fh));
+ syslog(LOG_DEBUG, "Owner Handle:\n");
+ log_netobj(&(arg->alock.oh));
+ syslog(LOG_DEBUG, "SVID: %d\n", arg->alock.svid);
+ syslog(LOG_DEBUG, "Lock Offset: %llu\n",
+ (unsigned long long)arg->alock.l_offset);
+ syslog(LOG_DEBUG, "Lock Length: %llu\n",
+ (unsigned long long)arg->alock.l_len);
+ syslog(LOG_DEBUG, "Block: %s\n", (arg->block ? "true" : "false"));
+ syslog(LOG_DEBUG, "Exclusive: %s\n", (arg->exclusive ? "true" : "false"));
+ syslog(LOG_DEBUG, "Reclaim: %s\n", (arg->reclaim ? "true" : "false"));
+ syslog(LOG_DEBUG, "State num: %d\n", arg->state);
+ }
+
+ /* copy cookie from arg to result. See comment in nlm_test_4() */
+ res.cookie = arg->cookie;
+
+ res.stat.stat = getlock(arg, rqstp, LOCK_MON | LOCK_V4);
+ return (&res);
+}
+
+void *
+nlm4_lock_msg_4_svc(arg, rqstp)
+ nlm4_lockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_res res;
+
+ if (debug_level)
+ log_from_addr("nlm4_lock_msg", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat.stat = getlock(arg, rqstp, LOCK_MON | LOCK_ASYNC | LOCK_V4);
+ transmit4_result(NLM4_LOCK_RES, &res, getrpcaddr(rqstp));
+
+ return (NULL);
+}
+
+/* nlm_cancel -------------------------------------------------------------- */
+/*
+ * Purpose: Cancel a blocked lock request
+ * Returns: granted or denied
+ * Notes:
+ */
+nlm4_res *
+nlm4_cancel_4_svc(arg, rqstp)
+ nlm4_cancargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_res res;
+
+ if (debug_level)
+ log_from_addr("nlm4_cancel", rqstp);
+
+ /* copy cookie from arg to result. See comment in nlm_test_1() */
+ res.cookie = arg->cookie;
+
+ /*
+ * Since at present we never return 'nlm_blocked', there can never be
+ * a lock to cancel, so this call always fails.
+ */
+ res.stat.stat = unlock(&arg->alock, LOCK_CANCEL);
+ return (&res);
+}
+
+void *
+nlm4_cancel_msg_4_svc(arg, rqstp)
+ nlm4_cancargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_res res;
+
+ if (debug_level)
+ log_from_addr("nlm4_cancel_msg", rqstp);
+
+ res.cookie = arg->cookie;
+ /*
+ * Since at present we never return 'nlm_blocked', there can never be
+ * a lock to cancel, so this call always fails.
+ */
+ res.stat.stat = unlock(&arg->alock, LOCK_CANCEL | LOCK_V4);
+ transmit4_result(NLM4_CANCEL_RES, &res, getrpcaddr(rqstp));
+ return (NULL);
+}
+
+/* nlm_unlock -------------------------------------------------------------- */
+/*
+ * Purpose: Release an existing lock
+ * Returns: Always granted, unless during grace period
+ * Notes: "no such lock" error condition is ignored, as the
+ * protocol uses unreliable UDP datagrams, and may well
+ * re-try an unlock that has already succeeded.
+ */
+nlm4_res *
+nlm4_unlock_4_svc(arg, rqstp)
+ nlm4_unlockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_res res;
+
+ if (debug_level)
+ log_from_addr("nlm4_unlock", rqstp);
+
+ res.stat.stat = unlock(&arg->alock, LOCK_V4);
+ res.cookie = arg->cookie;
+
+ return (&res);
+}
+
+void *
+nlm4_unlock_msg_4_svc(arg, rqstp)
+ nlm4_unlockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_res res;
+
+ if (debug_level)
+ log_from_addr("nlm4_unlock_msg", rqstp);
+
+ res.stat.stat = unlock(&arg->alock, LOCK_V4);
+ res.cookie = arg->cookie;
+
+ transmit4_result(NLM4_UNLOCK_RES, &res, getrpcaddr(rqstp));
+ return (NULL);
+}
+
+/* ------------------------------------------------------------------------- */
+/*
+ * Client-side pseudo-RPCs for results. Note that for the client there
+ * are only nlm_xxx_msg() versions of each call, since the 'real RPC'
+ * version returns the results in the RPC result, and so the client
+ * does not normally receive incoming RPCs.
+ *
+ * The exception to this is nlm_granted(), which is genuinely an RPC
+ * call from the server to the client - a 'call-back' in normal procedure
+ * call terms.
+ */
+
+/* nlm_granted ------------------------------------------------------------- */
+/*
+ * Purpose: Receive notification that formerly blocked lock now granted
+ * Returns: always success ('granted')
+ * Notes:
+ */
+nlm4_res *
+nlm4_granted_4_svc(arg, rqstp)
+ nlm4_testargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_res res;
+
+ if (debug_level)
+ log_from_addr("nlm4_granted", rqstp);
+
+ res.stat.stat = lock_answer(arg->alock.svid, &arg->cookie,
+ nlm4_granted, NULL, NLM_VERS4) == 0 ?
+ nlm4_granted : nlm4_denied;
+
+ /* copy cookie from arg to result. See comment in nlm_test_1() */
+ res.cookie = arg->cookie;
+
+ return (&res);
+}
+
+void *
+nlm4_granted_msg_4_svc(arg, rqstp)
+ nlm4_testargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_res res;
+
+ if (debug_level)
+ log_from_addr("nlm4_granted_msg", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat.stat = lock_answer(arg->alock.svid, &arg->cookie,
+ nlm4_granted, NULL, NLM_VERS4) == 0 ?
+ nlm4_granted : nlm4_denied;
+ transmit4_result(NLM4_GRANTED_RES, &res, getrpcaddr(rqstp));
+ return (NULL);
+}
+
+/* nlm_test_res ------------------------------------------------------------ */
+/*
+ * Purpose: Accept result from earlier nlm_test_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm4_test_res_4_svc(arg, rqstp)
+ nlm4_testres *arg;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm4_test_res", rqstp);
+
+ (void)lock_answer(-1, &arg->cookie, arg->stat.stat,
+ (int *)&arg->stat.nlm4_testrply_u.holder.svid,
+ NLM_VERS4);
+ return (NULL);
+}
+
+/* nlm_lock_res ------------------------------------------------------------ */
+/*
+ * Purpose: Accept result from earlier nlm_lock_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm4_lock_res_4_svc(arg, rqstp)
+ nlm4_res *arg;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm4_lock_res", rqstp);
+
+ (void)lock_answer(-1, &arg->cookie, arg->stat.stat, NULL, NLM_VERS4);
+
+ return (NULL);
+}
+
+/* nlm_cancel_res ---------------------------------------------------------- */
+/*
+ * Purpose: Accept result from earlier nlm_cancel_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm4_cancel_res_4_svc(arg, rqstp)
+ nlm4_res *arg __unused;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm4_cancel_res", rqstp);
+ return (NULL);
+}
+
+/* nlm_unlock_res ---------------------------------------------------------- */
+/*
+ * Purpose: Accept result from earlier nlm_unlock_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm4_unlock_res_4_svc(arg, rqstp)
+ nlm4_res *arg __unused;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm4_unlock_res", rqstp);
+ return (NULL);
+}
+
+/* nlm_granted_res --------------------------------------------------------- */
+/*
+ * Purpose: Accept result from earlier nlm_granted_msg() call
+ * Returns: Nothing
+ */
+void *
+nlm4_granted_res_4_svc(arg, rqstp)
+ nlm4_res *arg __unused;
+ struct svc_req *rqstp;
+{
+ if (debug_level)
+ log_from_addr("nlm4_granted_res", rqstp);
+ return (NULL);
+}
+
+/* ------------------------------------------------------------------------- */
+/*
+ * Calls for PCNFS locking (aka non-monitored locking, no involvement
+ * of rpc.statd).
+ *
+ * These are all genuine RPCs - no nlm_xxx_msg() nonsense here.
+ */
+
+/* nlm_share --------------------------------------------------------------- */
+/*
+ * Purpose: Establish a DOS-style lock
+ * Returns: success or failure
+ * Notes: Blocking locks are not supported - client is expected
+ * to retry if required.
+ */
+nlm4_shareres *
+nlm4_share_4_svc(arg, rqstp)
+ nlm4_shareargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_shareres res;
+
+ if (debug_level)
+ log_from_addr("nlm4_share", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat = nlm4_granted;
+ res.sequence = 1234356; /* X/Open says this field is ignored? */
+ return (&res);
+}
+
+/* nlm4_unshare ------------------------------------------------------------ */
+/*
+ * Purpose: Release a DOS-style lock
+ * Returns: nlm_granted, unless in grace period
+ * Notes:
+ */
+nlm4_shareres *
+nlm4_unshare_4_svc(arg, rqstp)
+ nlm4_shareargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_shareres res;
+
+ if (debug_level)
+ log_from_addr("nlm_unshare", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat = nlm4_granted;
+ res.sequence = 1234356; /* X/Open says this field is ignored? */
+ return (&res);
+}
+
+/* nlm4_nm_lock ------------------------------------------------------------ */
+/*
+ * Purpose: non-monitored version of nlm4_lock()
+ * Returns: as for nlm4_lock()
+ * Notes: These locks are in the same style as the standard nlm4_lock,
+ * but the rpc.statd should not be called to establish a
+ * monitor for the client machine, since that machine is
+ * declared not to be running a rpc.statd, and so would not
+ * respond to the statd protocol.
+ */
+nlm4_res *
+nlm4_nm_lock_4_svc(arg, rqstp)
+ nlm4_lockargs *arg;
+ struct svc_req *rqstp;
+{
+ static nlm4_res res;
+
+ if (debug_level)
+ log_from_addr("nlm4_nm_lock", rqstp);
+
+ /* copy cookie from arg to result. See comment in nlm4_test_1() */
+ res.cookie = arg->cookie;
+ res.stat.stat = nlm4_granted;
+ return (&res);
+}
+
+/* nlm4_free_all ------------------------------------------------------------ */
+/*
+ * Purpose: Release all locks held by a named client
+ * Returns: Nothing
+ * Notes: Potential denial of service security problem here - the
+ * locks to be released are specified by a host name, independent
+ * of the address from which the request has arrived.
+ * Should probably be rejected if the named host has been
+ * using monitored locks.
+ */
+void *
+nlm4_free_all_4_svc(arg, rqstp)
+ struct nlm4_notify *arg __unused;
+ struct svc_req *rqstp;
+{
+ static char dummy;
+
+ if (debug_level)
+ log_from_addr("nlm4_free_all", rqstp);
+ return (&dummy);
+}
+
+/* nlm_sm_notify --------------------------------------------------------- */
+/*
+ * Purpose: called by rpc.statd when a monitored host state changes.
+ * Returns: Nothing
+ */
+void *
+nlm_sm_notify_0_svc(arg, rqstp)
+ struct nlm_sm_status *arg;
+ struct svc_req *rqstp __unused;
+{
+ static char dummy;
+ notify(arg->mon_name, arg->state);
+ return (&dummy);
+}
diff --git a/usr.sbin/rpc.lockd/lockd.c b/usr.sbin/rpc.lockd/lockd.c
new file mode 100644
index 0000000..c111b58
--- /dev/null
+++ b/usr.sbin/rpc.lockd/lockd.c
@@ -0,0 +1,839 @@
+/* $NetBSD: lockd.c,v 1.7 2000/08/12 18:08:44 thorpej Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 1995
+ * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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
+ * 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 ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED 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>
+#ifndef lint
+__RCSID("$NetBSD: lockd.c,v 1.7 2000/08/12 18:08:44 thorpej Exp $");
+#endif
+
+/*
+ * main() function for NFS lock daemon. Most of the code in this
+ * file was generated by running rpcgen /usr/include/rpcsvc/nlm_prot.x.
+ *
+ * The actual program logic is in the file lock_proc.c
+ */
+
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <syslog.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <libutil.h>
+#include <netconfig.h>
+#include <netdb.h>
+
+#include <rpc/rpc.h>
+#include <rpc/rpc_com.h>
+#include <rpcsvc/sm_inter.h>
+
+#include "lockd.h"
+#include <rpcsvc/nlm_prot.h>
+
+int debug_level = 0; /* 0 = no debugging syslog() calls */
+int _rpcsvcdirty = 0;
+
+int grace_expired;
+int nsm_state;
+int kernel_lockd;
+int kernel_lockd_client;
+pid_t client_pid;
+struct mon mon_host;
+char **hosts, *svcport_str = NULL;
+int nhosts = 0;
+int xcreated = 0;
+char **addrs; /* actually (netid, uaddr) pairs */
+int naddrs; /* count of how many (netid, uaddr) pairs */
+
+void create_service(struct netconfig *nconf);
+void lookup_addresses(struct netconfig *nconf);
+void init_nsm(void);
+void nlm_prog_0(struct svc_req *, SVCXPRT *);
+void nlm_prog_1(struct svc_req *, SVCXPRT *);
+void nlm_prog_3(struct svc_req *, SVCXPRT *);
+void nlm_prog_4(struct svc_req *, SVCXPRT *);
+void out_of_mem(void);
+void usage(void);
+
+void sigalarm_handler(void);
+
+/*
+ * XXX move to some header file.
+ */
+#define _PATH_RPCLOCKDSOCK "/var/run/rpclockd.sock"
+
+int
+main(int argc, char **argv)
+{
+ int ch, i, s;
+ void *nc_handle;
+ char *endptr, **hosts_bak;
+ struct sigaction sigalarm;
+ int grace_period = 30;
+ struct netconfig *nconf;
+ int have_v6 = 1;
+ int maxrec = RPC_MAXDATASIZE;
+ in_port_t svcport = 0;
+
+ while ((ch = getopt(argc, argv, "d:g:h:p:")) != (-1)) {
+ switch (ch) {
+ case 'd':
+ debug_level = atoi(optarg);
+ if (!debug_level) {
+ usage();
+ /* NOTREACHED */
+ }
+ break;
+ case 'g':
+ grace_period = atoi(optarg);
+ if (!grace_period) {
+ usage();
+ /* NOTREACHED */
+ }
+ break;
+ case 'h':
+ ++nhosts;
+ hosts_bak = hosts;
+ hosts_bak = realloc(hosts, nhosts * sizeof(char *));
+ if (hosts_bak == NULL) {
+ if (hosts != NULL) {
+ for (i = 0; i < nhosts; i++)
+ free(hosts[i]);
+ free(hosts);
+ out_of_mem();
+ }
+ }
+ hosts = hosts_bak;
+ hosts[nhosts - 1] = strdup(optarg);
+ if (hosts[nhosts - 1] == NULL) {
+ for (i = 0; i < (nhosts - 1); i++)
+ free(hosts[i]);
+ free(hosts);
+ out_of_mem();
+ }
+ break;
+ case 'p':
+ endptr = NULL;
+ svcport = (in_port_t)strtoul(optarg, &endptr, 10);
+ if (endptr == NULL || *endptr != '\0' ||
+ svcport == 0 || svcport >= IPPORT_MAX)
+ usage();
+ svcport_str = strdup(optarg);
+ break;
+ default:
+ case '?':
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ if (geteuid()) { /* This command allowed only to root */
+ fprintf(stderr, "Sorry. You are not superuser\n");
+ exit(1);
+ }
+
+ kernel_lockd = FALSE;
+ kernel_lockd_client = FALSE;
+ if (modfind("nfslockd") < 0) {
+ if (kldload("nfslockd") < 0) {
+ fprintf(stderr, "Can't find or load kernel support for rpc.lockd - using non-kernel implementation\n");
+ } else {
+ kernel_lockd = TRUE;
+ }
+ } else {
+ kernel_lockd = TRUE;
+ }
+ if (kernel_lockd) {
+ if (getosreldate() >= 800040)
+ kernel_lockd_client = TRUE;
+ }
+
+ (void)rpcb_unset(NLM_PROG, NLM_SM, NULL);
+ (void)rpcb_unset(NLM_PROG, NLM_VERS, NULL);
+ (void)rpcb_unset(NLM_PROG, NLM_VERSX, NULL);
+ (void)rpcb_unset(NLM_PROG, NLM_VERS4, NULL);
+
+ /*
+ * Check if IPv6 support is present.
+ */
+ s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ if (s < 0)
+ have_v6 = 0;
+ else
+ close(s);
+
+ rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
+
+ /*
+ * If no hosts were specified, add a wildcard entry to bind to
+ * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the
+ * list.
+ */
+ if (nhosts == 0) {
+ hosts = malloc(sizeof(char**));
+ if (hosts == NULL)
+ out_of_mem();
+
+ hosts[0] = "*";
+ nhosts = 1;
+ } else {
+ hosts_bak = hosts;
+ if (have_v6) {
+ hosts_bak = realloc(hosts, (nhosts + 2) *
+ sizeof(char *));
+ if (hosts_bak == NULL) {
+ for (i = 0; i < nhosts; i++)
+ free(hosts[i]);
+ free(hosts);
+ out_of_mem();
+ } else
+ hosts = hosts_bak;
+
+ nhosts += 2;
+ hosts[nhosts - 2] = "::1";
+ } else {
+ hosts_bak = realloc(hosts, (nhosts + 1) * sizeof(char *));
+ if (hosts_bak == NULL) {
+ for (i = 0; i < nhosts; i++)
+ free(hosts[i]);
+
+ free(hosts);
+ out_of_mem();
+ } else {
+ nhosts += 1;
+ hosts = hosts_bak;
+ }
+ }
+ hosts[nhosts - 1] = "127.0.0.1";
+ }
+
+ if (kernel_lockd) {
+ if (!kernel_lockd_client) {
+ /*
+ * For the case where we have a kernel lockd but it
+ * doesn't provide client locking, we run a cut-down
+ * RPC service on a local-domain socket. The kernel's
+ * RPC server will pass what it can't handle (mainly
+ * client replies) down to us.
+ */
+ struct sockaddr_un sun;
+ int fd, oldmask;
+ SVCXPRT *xprt;
+
+ memset(&sun, 0, sizeof sun);
+ sun.sun_family = AF_LOCAL;
+ unlink(_PATH_RPCLOCKDSOCK);
+ strcpy(sun.sun_path, _PATH_RPCLOCKDSOCK);
+ sun.sun_len = SUN_LEN(&sun);
+ fd = socket(AF_LOCAL, SOCK_STREAM, 0);
+ if (!fd) {
+ err(1, "Can't create local lockd socket");
+ }
+ oldmask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
+ if (bind(fd, (struct sockaddr *) &sun, sun.sun_len) < 0) {
+ err(1, "Can't bind local lockd socket");
+ }
+ umask(oldmask);
+ if (listen(fd, SOMAXCONN) < 0) {
+ err(1, "Can't listen on local lockd socket");
+ }
+ xprt = svc_vc_create(fd, RPC_MAXDATASIZE, RPC_MAXDATASIZE);
+ if (!xprt) {
+ err(1, "Can't create transport for local lockd socket");
+ }
+ if (!svc_reg(xprt, NLM_PROG, NLM_VERS4, nlm_prog_4, NULL)) {
+ err(1, "Can't register service for local lockd socket");
+ }
+ }
+
+ /*
+ * We need to look up the addresses so that we can
+ * hand uaddrs (ascii encoded address+port strings) to
+ * the kernel.
+ */
+ nc_handle = setnetconfig();
+ while ((nconf = getnetconfig(nc_handle))) {
+ /* We want to listen only on udp6, tcp6, udp, tcp transports */
+ if (nconf->nc_flag & NC_VISIBLE) {
+ /* Skip if there's no IPv6 support */
+ if (have_v6 == 0 && strcmp(nconf->nc_protofmly, "inet6") == 0) {
+ /* DO NOTHING */
+ } else {
+ lookup_addresses(nconf);
+ }
+ }
+ }
+ endnetconfig(nc_handle);
+ } else {
+ nc_handle = setnetconfig();
+ while ((nconf = getnetconfig(nc_handle))) {
+ /* We want to listen only on udp6, tcp6, udp, tcp transports */
+ if (nconf->nc_flag & NC_VISIBLE) {
+ /* Skip if there's no IPv6 support */
+ if (have_v6 == 0 && strcmp(nconf->nc_protofmly, "inet6") == 0) {
+ /* DO NOTHING */
+ } else {
+ create_service(nconf);
+ }
+ }
+ }
+ endnetconfig(nc_handle);
+ }
+
+ /*
+ * Note that it is NOT sensible to run this program from inetd - the
+ * protocol assumes that it will run immediately at boot time.
+ */
+ if (daemon(0, debug_level > 0)) {
+ err(1, "cannot fork");
+ /* NOTREACHED */
+ }
+
+ openlog("rpc.lockd", 0, LOG_DAEMON);
+ if (debug_level)
+ syslog(LOG_INFO, "Starting, debug level %d", debug_level);
+ else
+ syslog(LOG_INFO, "Starting");
+
+ sigalarm.sa_handler = (sig_t) sigalarm_handler;
+ sigemptyset(&sigalarm.sa_mask);
+ sigalarm.sa_flags = SA_RESETHAND; /* should only happen once */
+ sigalarm.sa_flags |= SA_RESTART;
+ if (sigaction(SIGALRM, &sigalarm, NULL) != 0) {
+ syslog(LOG_WARNING, "sigaction(SIGALRM) failed: %s",
+ strerror(errno));
+ exit(1);
+ }
+
+ if (kernel_lockd) {
+ if (!kernel_lockd_client) {
+ init_nsm();
+ client_pid = client_request();
+
+ /*
+ * Create a child process to enter the kernel and then
+ * wait for RPCs on our local domain socket.
+ */
+ if (!fork())
+ nlm_syscall(debug_level, grace_period,
+ naddrs, addrs);
+ else
+ svc_run();
+ } else {
+ /*
+ * The kernel lockd implementation provides
+ * both client and server so we don't need to
+ * do anything else.
+ */
+ nlm_syscall(debug_level, grace_period, naddrs, addrs);
+ }
+ } else {
+ grace_expired = 0;
+ alarm(grace_period);
+
+ init_nsm();
+
+ client_pid = client_request();
+
+ svc_run(); /* Should never return */
+ }
+ exit(1);
+}
+
+/*
+ * This routine creates and binds sockets on the appropriate
+ * addresses. It gets called one time for each transport and
+ * registrates the service with rpcbind on that trasport.
+ */
+void
+create_service(struct netconfig *nconf)
+{
+ struct addrinfo hints, *res = NULL;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ struct __rpc_sockinfo si;
+ struct netbuf servaddr;
+ SVCXPRT *transp = NULL;
+ int aicode;
+ int fd;
+ int nhostsbak;
+ int r;
+ int registered = 0;
+ u_int32_t host_addr[4]; /* IPv4 or IPv6 */
+
+ if ((nconf->nc_semantics != NC_TPI_CLTS) &&
+ (nconf->nc_semantics != NC_TPI_COTS) &&
+ (nconf->nc_semantics != NC_TPI_COTS_ORD))
+ return; /* not my type */
+
+ /*
+ * XXX - using RPC library internal functions.
+ */
+ if (!__rpc_nconf2sockinfo(nconf, &si)) {
+ syslog(LOG_ERR, "cannot get information for %s",
+ nconf->nc_netid);
+ return;
+ }
+
+ /* Get rpc.statd's address on this transport */
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = si.si_af;
+ hints.ai_socktype = si.si_socktype;
+ hints.ai_protocol = si.si_proto;
+
+ /*
+ * Bind to specific IPs if asked to
+ */
+ nhostsbak = nhosts;
+ while (nhostsbak > 0) {
+ --nhostsbak;
+
+ /*
+ * XXX - using RPC library internal functions.
+ */
+ if ((fd = __rpc_nconf2fd(nconf)) < 0) {
+ syslog(LOG_ERR, "cannot create socket for %s",
+ nconf->nc_netid);
+ continue;
+ }
+
+ switch (hints.ai_family) {
+ case AF_INET:
+ if (inet_pton(AF_INET, hosts[nhostsbak],
+ host_addr) == 1) {
+ hints.ai_flags &= AI_NUMERICHOST;
+ } else {
+ /*
+ * Skip if we have an AF_INET6 address.
+ */
+ if (inet_pton(AF_INET6, hosts[nhostsbak],
+ host_addr) == 1) {
+ close(fd);
+ continue;
+ }
+ }
+ break;
+ case AF_INET6:
+ if (inet_pton(AF_INET6, hosts[nhostsbak],
+ host_addr) == 1) {
+ hints.ai_flags &= AI_NUMERICHOST;
+ } else {
+ /*
+ * Skip if we have an AF_INET address.
+ */
+ if (inet_pton(AF_INET, hosts[nhostsbak],
+ host_addr) == 1) {
+ close(fd);
+ continue;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * If no hosts were specified, just bind to INADDR_ANY
+ */
+ if (strcmp("*", hosts[nhostsbak]) == 0) {
+ if (svcport_str == NULL) {
+ res = malloc(sizeof(struct addrinfo));
+ if (res == NULL)
+ out_of_mem();
+ res->ai_flags = hints.ai_flags;
+ res->ai_family = hints.ai_family;
+ res->ai_protocol = hints.ai_protocol;
+ switch (res->ai_family) {
+ case AF_INET:
+ sin = malloc(sizeof(struct sockaddr_in));
+ if (sin == NULL)
+ out_of_mem();
+ sin->sin_family = AF_INET;
+ sin->sin_port = htons(0);
+ sin->sin_addr.s_addr = htonl(INADDR_ANY);
+ res->ai_addr = (struct sockaddr*) sin;
+ res->ai_addrlen = (socklen_t)
+ sizeof(res->ai_addr);
+ break;
+ case AF_INET6:
+ sin6 = malloc(sizeof(struct sockaddr_in6));
+ if (sin6 == NULL)
+ out_of_mem();
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = htons(0);
+ sin6->sin6_addr = in6addr_any;
+ res->ai_addr = (struct sockaddr*) sin6;
+ res->ai_addrlen = (socklen_t) sizeof(res->ai_addr);
+ break;
+ default:
+ break;
+ }
+ } else {
+ if ((aicode = getaddrinfo(NULL, svcport_str,
+ &hints, &res)) != 0) {
+ syslog(LOG_ERR,
+ "cannot get local address for %s: %s",
+ nconf->nc_netid,
+ gai_strerror(aicode));
+ continue;
+ }
+ }
+ } else {
+ if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str,
+ &hints, &res)) != 0) {
+ syslog(LOG_ERR,
+ "cannot get local address for %s: %s",
+ nconf->nc_netid, gai_strerror(aicode));
+ continue;
+ }
+ }
+
+ r = bindresvport_sa(fd, res->ai_addr);
+ if (r != 0) {
+ syslog(LOG_ERR, "bindresvport_sa: %m");
+ exit(1);
+ }
+
+ if (nconf->nc_semantics != NC_TPI_CLTS)
+ listen(fd, SOMAXCONN);
+
+ transp = svc_tli_create(fd, nconf, NULL,
+ RPC_MAXDATASIZE, RPC_MAXDATASIZE);
+
+ if (transp != (SVCXPRT *) NULL) {
+ if (!svc_reg(transp, NLM_PROG, NLM_SM, nlm_prog_0,
+ NULL))
+ syslog(LOG_ERR,
+ "can't register %s NLM_PROG, NLM_SM service",
+ nconf->nc_netid);
+
+ if (!svc_reg(transp, NLM_PROG, NLM_VERS, nlm_prog_1,
+ NULL))
+ syslog(LOG_ERR,
+ "can't register %s NLM_PROG, NLM_VERS service",
+ nconf->nc_netid);
+
+ if (!svc_reg(transp, NLM_PROG, NLM_VERSX, nlm_prog_3,
+ NULL))
+ syslog(LOG_ERR,
+ "can't register %s NLM_PROG, NLM_VERSX service",
+ nconf->nc_netid);
+
+ if (!svc_reg(transp, NLM_PROG, NLM_VERS4, nlm_prog_4,
+ NULL))
+ syslog(LOG_ERR,
+ "can't register %s NLM_PROG, NLM_VERS4 service",
+ nconf->nc_netid);
+
+ } else
+ syslog(LOG_WARNING, "can't create %s services",
+ nconf->nc_netid);
+
+ if (registered == 0) {
+ registered = 1;
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = si.si_af;
+ hints.ai_socktype = si.si_socktype;
+ hints.ai_protocol = si.si_proto;
+
+ if (svcport_str == NULL) {
+ svcport_str = malloc(NI_MAXSERV * sizeof(char));
+ if (svcport_str == NULL)
+ out_of_mem();
+
+ if (getnameinfo(res->ai_addr,
+ res->ai_addr->sa_len, NULL, NI_MAXHOST,
+ svcport_str, NI_MAXSERV * sizeof(char),
+ NI_NUMERICHOST | NI_NUMERICSERV))
+ errx(1, "Cannot get port number");
+ }
+
+ if((aicode = getaddrinfo(NULL, svcport_str, &hints,
+ &res)) != 0) {
+ syslog(LOG_ERR, "cannot get local address: %s",
+ gai_strerror(aicode));
+ exit(1);
+ }
+
+ servaddr.buf = malloc(res->ai_addrlen);
+ memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen);
+ servaddr.len = res->ai_addrlen;
+
+ rpcb_set(NLM_PROG, NLM_SM, nconf, &servaddr);
+ rpcb_set(NLM_PROG, NLM_VERS, nconf, &servaddr);
+ rpcb_set(NLM_PROG, NLM_VERSX, nconf, &servaddr);
+ rpcb_set(NLM_PROG, NLM_VERS4, nconf, &servaddr);
+
+ xcreated++;
+ freeaddrinfo(res);
+ }
+ } /* end while */
+}
+
+/*
+ * Look up addresses for the kernel to create transports for.
+ */
+void
+lookup_addresses(struct netconfig *nconf)
+{
+ struct addrinfo hints, *res = NULL;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ struct __rpc_sockinfo si;
+ struct netbuf servaddr;
+ SVCXPRT *transp = NULL;
+ int aicode;
+ int nhostsbak;
+ int r;
+ int registered = 0;
+ u_int32_t host_addr[4]; /* IPv4 or IPv6 */
+ char *uaddr;
+
+ if ((nconf->nc_semantics != NC_TPI_CLTS) &&
+ (nconf->nc_semantics != NC_TPI_COTS) &&
+ (nconf->nc_semantics != NC_TPI_COTS_ORD))
+ return; /* not my type */
+
+ /*
+ * XXX - using RPC library internal functions.
+ */
+ if (!__rpc_nconf2sockinfo(nconf, &si)) {
+ syslog(LOG_ERR, "cannot get information for %s",
+ nconf->nc_netid);
+ return;
+ }
+
+ /* Get rpc.statd's address on this transport */
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = si.si_af;
+ hints.ai_socktype = si.si_socktype;
+ hints.ai_protocol = si.si_proto;
+
+ /*
+ * Bind to specific IPs if asked to
+ */
+ nhostsbak = nhosts;
+ while (nhostsbak > 0) {
+ --nhostsbak;
+
+ switch (hints.ai_family) {
+ case AF_INET:
+ if (inet_pton(AF_INET, hosts[nhostsbak],
+ host_addr) == 1) {
+ hints.ai_flags &= AI_NUMERICHOST;
+ } else {
+ /*
+ * Skip if we have an AF_INET6 address.
+ */
+ if (inet_pton(AF_INET6, hosts[nhostsbak],
+ host_addr) == 1) {
+ continue;
+ }
+ }
+ break;
+ case AF_INET6:
+ if (inet_pton(AF_INET6, hosts[nhostsbak],
+ host_addr) == 1) {
+ hints.ai_flags &= AI_NUMERICHOST;
+ } else {
+ /*
+ * Skip if we have an AF_INET address.
+ */
+ if (inet_pton(AF_INET, hosts[nhostsbak],
+ host_addr) == 1) {
+ continue;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * If no hosts were specified, just bind to INADDR_ANY
+ */
+ if (strcmp("*", hosts[nhostsbak]) == 0) {
+ if (svcport_str == NULL) {
+ res = malloc(sizeof(struct addrinfo));
+ if (res == NULL)
+ out_of_mem();
+ res->ai_flags = hints.ai_flags;
+ res->ai_family = hints.ai_family;
+ res->ai_protocol = hints.ai_protocol;
+ switch (res->ai_family) {
+ case AF_INET:
+ sin = malloc(sizeof(struct sockaddr_in));
+ if (sin == NULL)
+ out_of_mem();
+ sin->sin_family = AF_INET;
+ sin->sin_port = htons(0);
+ sin->sin_addr.s_addr = htonl(INADDR_ANY);
+ res->ai_addr = (struct sockaddr*) sin;
+ res->ai_addrlen = (socklen_t)
+ sizeof(res->ai_addr);
+ break;
+ case AF_INET6:
+ sin6 = malloc(sizeof(struct sockaddr_in6));
+ if (sin6 == NULL)
+ out_of_mem();
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = htons(0);
+ sin6->sin6_addr = in6addr_any;
+ res->ai_addr = (struct sockaddr*) sin6;
+ res->ai_addrlen = (socklen_t) sizeof(res->ai_addr);
+ break;
+ default:
+ break;
+ }
+ } else {
+ if ((aicode = getaddrinfo(NULL, svcport_str,
+ &hints, &res)) != 0) {
+ syslog(LOG_ERR,
+ "cannot get local address for %s: %s",
+ nconf->nc_netid,
+ gai_strerror(aicode));
+ continue;
+ }
+ }
+ } else {
+ if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str,
+ &hints, &res)) != 0) {
+ syslog(LOG_ERR,
+ "cannot get local address for %s: %s",
+ nconf->nc_netid, gai_strerror(aicode));
+ continue;
+ }
+ }
+
+ servaddr.len = servaddr.maxlen = res->ai_addr->sa_len;
+ servaddr.buf = res->ai_addr;
+ uaddr = taddr2uaddr(nconf, &servaddr);
+
+ addrs = realloc(addrs, 2 * (naddrs + 1) * sizeof(char *));
+ if (!addrs)
+ out_of_mem();
+ addrs[2 * naddrs] = strdup(nconf->nc_netid);
+ addrs[2 * naddrs + 1] = uaddr;
+ naddrs++;
+ } /* end while */
+}
+
+void
+sigalarm_handler(void)
+{
+
+ grace_expired = 1;
+}
+
+void
+usage()
+{
+ errx(1, "usage: rpc.lockd [-d <debuglevel>]"
+ " [-g <grace period>] [-h <bindip>] [-p <port>]");
+}
+
+/*
+ * init_nsm --
+ * Reset the NSM state-of-the-world and acquire its state.
+ */
+void
+init_nsm(void)
+{
+ enum clnt_stat ret;
+ my_id id;
+ sm_stat stat;
+ char name[] = "NFS NLM";
+ char localhost[] = "localhost";
+
+ /*
+ * !!!
+ * The my_id structure isn't used by the SM_UNMON_ALL call, as far
+ * as I know. Leave it empty for now.
+ */
+ memset(&id, 0, sizeof(id));
+ id.my_name = name;
+
+ /*
+ * !!!
+ * The statd program must already be registered when lockd runs.
+ */
+ do {
+ ret = callrpc("localhost", SM_PROG, SM_VERS, SM_UNMON_ALL,
+ (xdrproc_t)xdr_my_id, &id, (xdrproc_t)xdr_sm_stat, &stat);
+ if (ret == RPC_PROGUNAVAIL) {
+ syslog(LOG_WARNING, "%lu %s", SM_PROG,
+ clnt_sperrno(ret));
+ sleep(2);
+ continue;
+ }
+ break;
+ } while (0);
+
+ if (ret != 0) {
+ syslog(LOG_ERR, "%lu %s", SM_PROG, clnt_sperrno(ret));
+ exit(1);
+ }
+
+ nsm_state = stat.state;
+
+ /* setup constant data for SM_MON calls */
+ mon_host.mon_id.my_id.my_name = localhost;
+ mon_host.mon_id.my_id.my_prog = NLM_PROG;
+ mon_host.mon_id.my_id.my_vers = NLM_SM;
+ mon_host.mon_id.my_id.my_proc = NLM_SM_NOTIFY; /* bsdi addition */
+}
+
+/*
+ * Out of memory, fatal
+ */
+void out_of_mem()
+{
+ syslog(LOG_ERR, "out of memory");
+ exit(2);
+}
diff --git a/usr.sbin/rpc.lockd/lockd.h b/usr.sbin/rpc.lockd/lockd.h
new file mode 100644
index 0000000..0a8e2e6
--- /dev/null
+++ b/usr.sbin/rpc.lockd/lockd.h
@@ -0,0 +1,41 @@
+/* $NetBSD: lockd.h,v 1.2 2000/06/07 14:34:40 bouyer Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 1995
+ * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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
+ * 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 ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED 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.
+ *
+ */
+
+extern int debug_level;
+extern int grace_expired;
+pid_t client_request(void);
+extern int nsm_state;
+extern pid_t client_pid;
diff --git a/usr.sbin/rpc.lockd/lockd_lock.c b/usr.sbin/rpc.lockd/lockd_lock.c
new file mode 100644
index 0000000..5d37c32
--- /dev/null
+++ b/usr.sbin/rpc.lockd/lockd_lock.c
@@ -0,0 +1,2303 @@
+/* $NetBSD: lockd_lock.c,v 1.5 2000/11/21 03:47:41 enami Exp $ */
+
+/*
+ * Copyright (c) 2001 Andrew P. Lentvorski, Jr.
+ * Copyright (c) 2000 Manuel Bouyer.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$");
+
+#define LOCKD_DEBUG
+
+#include <stdio.h>
+#ifdef LOCKD_DEBUG
+#include <stdarg.h>
+#endif
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <errno.h>
+#include <string.h>
+#include <signal.h>
+#include <rpc/rpc.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/wait.h>
+#include <rpcsvc/sm_inter.h>
+#include <rpcsvc/nlm_prot.h>
+#include "lockd_lock.h"
+#include "lockd.h"
+
+#define MAXOBJECTSIZE 64
+#define MAXBUFFERSIZE 1024
+
+/*
+ * A set of utilities for managing file locking
+ *
+ * XXX: All locks are in a linked list, a better structure should be used
+ * to improve search/access effeciency.
+ */
+
+/* struct describing a lock */
+struct file_lock {
+ LIST_ENTRY(file_lock) nfslocklist;
+ fhandle_t filehandle; /* NFS filehandle */
+ struct sockaddr *addr;
+ struct nlm4_holder client; /* lock holder */
+ /* XXX: client_cookie used *only* in send_granted */
+ netobj client_cookie; /* cookie sent by the client */
+ int nsm_status; /* status from the remote lock manager */
+ int status; /* lock status, see below */
+ int flags; /* lock flags, see lockd_lock.h */
+ int blocking; /* blocking lock or not */
+ char client_name[SM_MAXSTRLEN]; /* client_name is really variable
+ length and must be last! */
+};
+
+LIST_HEAD(nfslocklist_head, file_lock);
+struct nfslocklist_head nfslocklist_head = LIST_HEAD_INITIALIZER(nfslocklist_head);
+
+LIST_HEAD(blockedlocklist_head, file_lock);
+struct blockedlocklist_head blockedlocklist_head = LIST_HEAD_INITIALIZER(blockedlocklist_head);
+
+/* lock status */
+#define LKST_LOCKED 1 /* lock is locked */
+/* XXX: Is this flag file specific or lock specific? */
+#define LKST_WAITING 2 /* file is already locked by another host */
+#define LKST_PROCESSING 3 /* child is trying to aquire the lock */
+#define LKST_DYING 4 /* must dies when we get news from the child */
+
+/* struct describing a monitored host */
+struct host {
+ LIST_ENTRY(host) hostlst;
+ int refcnt;
+ char name[SM_MAXSTRLEN]; /* name is really variable length and
+ must be last! */
+};
+/* list of hosts we monitor */
+LIST_HEAD(hostlst_head, host);
+struct hostlst_head hostlst_head = LIST_HEAD_INITIALIZER(hostlst_head);
+
+/*
+ * File monitoring handlers
+ * XXX: These might be able to be removed when kevent support
+ * is placed into the hardware lock/unlock routines. (ie.
+ * let the kernel do all the file monitoring)
+ */
+
+/* Struct describing a monitored file */
+struct monfile {
+ LIST_ENTRY(monfile) monfilelist;
+ fhandle_t filehandle; /* Local access filehandle */
+ int fd; /* file descriptor: remains open until unlock! */
+ int refcount;
+ int exclusive;
+};
+
+/* List of files we monitor */
+LIST_HEAD(monfilelist_head, monfile);
+struct monfilelist_head monfilelist_head = LIST_HEAD_INITIALIZER(monfilelist_head);
+
+static int debugdelay = 0;
+
+enum nfslock_status { NFS_GRANTED = 0, NFS_GRANTED_DUPLICATE,
+ NFS_DENIED, NFS_DENIED_NOLOCK,
+ NFS_RESERR };
+
+enum hwlock_status { HW_GRANTED = 0, HW_GRANTED_DUPLICATE,
+ HW_DENIED, HW_DENIED_NOLOCK,
+ HW_STALEFH, HW_READONLY, HW_RESERR };
+
+enum partialfilelock_status { PFL_GRANTED=0, PFL_GRANTED_DUPLICATE, PFL_DENIED,
+ PFL_NFSDENIED, PFL_NFSBLOCKED, PFL_NFSDENIED_NOLOCK, PFL_NFSRESERR,
+ PFL_HWDENIED, PFL_HWBLOCKED, PFL_HWDENIED_NOLOCK, PFL_HWRESERR};
+
+enum LFLAGS {LEDGE_LEFT, LEDGE_LBOUNDARY, LEDGE_INSIDE, LEDGE_RBOUNDARY, LEDGE_RIGHT};
+enum RFLAGS {REDGE_LEFT, REDGE_LBOUNDARY, REDGE_INSIDE, REDGE_RBOUNDARY, REDGE_RIGHT};
+/* XXX: WARNING! I HAVE OVERLOADED THIS STATUS ENUM! SPLIT IT APART INTO TWO */
+enum split_status {SPL_DISJOINT=0, SPL_LOCK1=1, SPL_LOCK2=2, SPL_CONTAINED=4, SPL_RESERR=8};
+
+enum partialfilelock_status lock_partialfilelock(struct file_lock *fl);
+
+void send_granted(struct file_lock *fl, int opcode);
+void siglock(void);
+void sigunlock(void);
+void monitor_lock_host(const char *hostname);
+void unmonitor_lock_host(char *hostname);
+
+void copy_nlm4_lock_to_nlm4_holder(const struct nlm4_lock *src,
+ const bool_t exclusive, struct nlm4_holder *dest);
+struct file_lock * allocate_file_lock(const netobj *lockowner,
+ const netobj *matchcookie,
+ const struct sockaddr *addr,
+ const char *caller_name);
+void deallocate_file_lock(struct file_lock *fl);
+void fill_file_lock(struct file_lock *fl, const fhandle_t *fh,
+ const bool_t exclusive, const int32_t svid,
+ const u_int64_t offset, const u_int64_t len,
+ const int state, const int status, const int flags, const int blocking);
+int regions_overlap(const u_int64_t start1, const u_int64_t len1,
+ const u_int64_t start2, const u_int64_t len2);
+enum split_status region_compare(const u_int64_t starte, const u_int64_t lene,
+ const u_int64_t startu, const u_int64_t lenu,
+ u_int64_t *start1, u_int64_t *len1, u_int64_t *start2, u_int64_t *len2);
+int same_netobj(const netobj *n0, const netobj *n1);
+int same_filelock_identity(const struct file_lock *fl0,
+ const struct file_lock *fl2);
+
+static void debuglog(char const *fmt, ...);
+void dump_static_object(const unsigned char* object, const int sizeof_object,
+ unsigned char* hbuff, const int sizeof_hbuff,
+ unsigned char* cbuff, const int sizeof_cbuff);
+void dump_netobj(const struct netobj *nobj);
+void dump_filelock(const struct file_lock *fl);
+struct file_lock * get_lock_matching_unlock(const struct file_lock *fl);
+enum nfslock_status test_nfslock(const struct file_lock *fl,
+ struct file_lock **conflicting_fl);
+enum nfslock_status lock_nfslock(struct file_lock *fl);
+enum nfslock_status delete_nfslock(struct file_lock *fl);
+enum nfslock_status unlock_nfslock(const struct file_lock *fl,
+ struct file_lock **released_lock, struct file_lock **left_lock,
+ struct file_lock **right_lock);
+enum hwlock_status lock_hwlock(struct file_lock *fl);
+enum split_status split_nfslock(const struct file_lock *exist_lock,
+ const struct file_lock *unlock_lock, struct file_lock **left_lock,
+ struct file_lock **right_lock);
+void add_blockingfilelock(struct file_lock *fl);
+enum hwlock_status unlock_hwlock(const struct file_lock *fl);
+enum hwlock_status test_hwlock(const struct file_lock *fl,
+ struct file_lock **conflicting_fl);
+void remove_blockingfilelock(struct file_lock *fl);
+void clear_blockingfilelock(const char *hostname);
+void retry_blockingfilelocklist(void);
+enum partialfilelock_status unlock_partialfilelock(
+ const struct file_lock *fl);
+void clear_partialfilelock(const char *hostname);
+enum partialfilelock_status test_partialfilelock(
+ const struct file_lock *fl, struct file_lock **conflicting_fl);
+enum nlm_stats do_test(struct file_lock *fl,
+ struct file_lock **conflicting_fl);
+enum nlm_stats do_unlock(struct file_lock *fl);
+enum nlm_stats do_lock(struct file_lock *fl);
+void do_clear(const char *hostname);
+size_t strnlen(const char *, size_t);
+
+void
+debuglog(char const *fmt, ...)
+{
+ va_list ap;
+
+ if (debug_level < 1) {
+ return;
+ }
+
+ sleep(debugdelay);
+
+ va_start(ap, fmt);
+ vsyslog(LOG_DEBUG, fmt, ap);
+ va_end(ap);
+}
+
+void
+dump_static_object(object, size_object, hbuff, size_hbuff, cbuff, size_cbuff)
+ const unsigned char *object;
+ const int size_object;
+ unsigned char *hbuff;
+ const int size_hbuff;
+ unsigned char *cbuff;
+ const int size_cbuff;
+{
+ int i, objectsize;
+
+ if (debug_level < 2) {
+ return;
+ }
+
+ objectsize = size_object;
+
+ if (objectsize == 0) {
+ debuglog("object is size 0\n");
+ } else {
+ if (objectsize > MAXOBJECTSIZE) {
+ debuglog("Object of size %d being clamped"
+ "to size %d\n", objectsize, MAXOBJECTSIZE);
+ objectsize = MAXOBJECTSIZE;
+ }
+
+ if (hbuff != NULL) {
+ if (size_hbuff < objectsize*2+1) {
+ debuglog("Hbuff not large enough."
+ " Increase size\n");
+ } else {
+ for(i=0;i<objectsize;i++) {
+ sprintf(hbuff+i*2,"%02x",*(object+i));
+ }
+ *(hbuff+i*2) = '\0';
+ }
+ }
+
+ if (cbuff != NULL) {
+ if (size_cbuff < objectsize+1) {
+ debuglog("Cbuff not large enough."
+ " Increase Size\n");
+ }
+
+ for(i=0;i<objectsize;i++) {
+ if (*(object+i) >= 32 && *(object+i) <= 127) {
+ *(cbuff+i) = *(object+i);
+ } else {
+ *(cbuff+i) = '.';
+ }
+ }
+ *(cbuff+i) = '\0';
+ }
+ }
+}
+
+void
+dump_netobj(const struct netobj *nobj)
+{
+ char hbuff[MAXBUFFERSIZE*2];
+ char cbuff[MAXBUFFERSIZE];
+
+ if (debug_level < 2) {
+ return;
+ }
+
+ if (nobj == NULL) {
+ debuglog("Null netobj pointer\n");
+ }
+ else if (nobj->n_len == 0) {
+ debuglog("Size zero netobj\n");
+ } else {
+ dump_static_object(nobj->n_bytes, nobj->n_len,
+ hbuff, sizeof(hbuff), cbuff, sizeof(cbuff));
+ debuglog("netobj: len: %d data: %s ::: %s\n",
+ nobj->n_len, hbuff, cbuff);
+ }
+}
+
+/* #define DUMP_FILELOCK_VERBOSE */
+void
+dump_filelock(const struct file_lock *fl)
+{
+#ifdef DUMP_FILELOCK_VERBOSE
+ char hbuff[MAXBUFFERSIZE*2];
+ char cbuff[MAXBUFFERSIZE];
+#endif
+
+ if (debug_level < 2) {
+ return;
+ }
+
+ if (fl != NULL) {
+ debuglog("Dumping file lock structure @ %p\n", fl);
+
+#ifdef DUMP_FILELOCK_VERBOSE
+ dump_static_object((unsigned char *)&fl->filehandle,
+ sizeof(fl->filehandle), hbuff, sizeof(hbuff),
+ cbuff, sizeof(cbuff));
+ debuglog("Filehandle: %8s ::: %8s\n", hbuff, cbuff);
+#endif
+
+ debuglog("Dumping nlm4_holder:\n"
+ "exc: %x svid: %x offset:len %llx:%llx\n",
+ fl->client.exclusive, fl->client.svid,
+ fl->client.l_offset, fl->client.l_len);
+
+#ifdef DUMP_FILELOCK_VERBOSE
+ debuglog("Dumping client identity:\n");
+ dump_netobj(&fl->client.oh);
+
+ debuglog("Dumping client cookie:\n");
+ dump_netobj(&fl->client_cookie);
+
+ debuglog("nsm: %d status: %d flags: %d svid: %x"
+ " client_name: %s\n", fl->nsm_status, fl->status,
+ fl->flags, fl->client.svid, fl->client_name);
+#endif
+ } else {
+ debuglog("NULL file lock structure\n");
+ }
+}
+
+void
+copy_nlm4_lock_to_nlm4_holder(src, exclusive, dest)
+ const struct nlm4_lock *src;
+ const bool_t exclusive;
+ struct nlm4_holder *dest;
+{
+
+ dest->exclusive = exclusive;
+ dest->oh.n_len = src->oh.n_len;
+ dest->oh.n_bytes = src->oh.n_bytes;
+ dest->svid = src->svid;
+ dest->l_offset = src->l_offset;
+ dest->l_len = src->l_len;
+}
+
+
+size_t
+strnlen(const char *s, size_t len)
+{
+ size_t n;
+
+ for (n = 0; s[n] != 0 && n < len; n++)
+ ;
+ return n;
+}
+
+/*
+ * allocate_file_lock: Create a lock with the given parameters
+ */
+
+struct file_lock *
+allocate_file_lock(const netobj *lockowner, const netobj *matchcookie,
+ const struct sockaddr *addr, const char *caller_name)
+{
+ struct file_lock *newfl;
+ size_t n;
+
+ /* Beware of rubbish input! */
+ n = strnlen(caller_name, SM_MAXSTRLEN);
+ if (n == SM_MAXSTRLEN) {
+ return NULL;
+ }
+
+ newfl = malloc(sizeof(*newfl) - sizeof(newfl->client_name) + n + 1);
+ if (newfl == NULL) {
+ return NULL;
+ }
+ bzero(newfl, sizeof(*newfl) - sizeof(newfl->client_name));
+ memcpy(newfl->client_name, caller_name, n);
+ newfl->client_name[n] = 0;
+
+ newfl->client.oh.n_bytes = malloc(lockowner->n_len);
+ if (newfl->client.oh.n_bytes == NULL) {
+ free(newfl);
+ return NULL;
+ }
+ newfl->client.oh.n_len = lockowner->n_len;
+ bcopy(lockowner->n_bytes, newfl->client.oh.n_bytes, lockowner->n_len);
+
+ newfl->client_cookie.n_bytes = malloc(matchcookie->n_len);
+ if (newfl->client_cookie.n_bytes == NULL) {
+ free(newfl->client.oh.n_bytes);
+ free(newfl);
+ return NULL;
+ }
+ newfl->client_cookie.n_len = matchcookie->n_len;
+ bcopy(matchcookie->n_bytes, newfl->client_cookie.n_bytes, matchcookie->n_len);
+
+ newfl->addr = malloc(addr->sa_len);
+ if (newfl->addr == NULL) {
+ free(newfl->client_cookie.n_bytes);
+ free(newfl->client.oh.n_bytes);
+ free(newfl);
+ return NULL;
+ }
+ memcpy(newfl->addr, addr, addr->sa_len);
+
+ return newfl;
+}
+
+/*
+ * file_file_lock: Force creation of a valid file lock
+ */
+void
+fill_file_lock(struct file_lock *fl, const fhandle_t *fh,
+ const bool_t exclusive, const int32_t svid,
+ const u_int64_t offset, const u_int64_t len,
+ const int state, const int status, const int flags, const int blocking)
+{
+ bcopy(fh, &fl->filehandle, sizeof(fhandle_t));
+
+ fl->client.exclusive = exclusive;
+ fl->client.svid = svid;
+ fl->client.l_offset = offset;
+ fl->client.l_len = len;
+
+ fl->nsm_status = state;
+ fl->status = status;
+ fl->flags = flags;
+ fl->blocking = blocking;
+}
+
+/*
+ * deallocate_file_lock: Free all storage associated with a file lock
+ */
+void
+deallocate_file_lock(struct file_lock *fl)
+{
+ free(fl->addr);
+ free(fl->client.oh.n_bytes);
+ free(fl->client_cookie.n_bytes);
+ free(fl);
+}
+
+/*
+ * regions_overlap(): This function examines the two provided regions for
+ * overlap.
+ */
+int
+regions_overlap(start1, len1, start2, len2)
+ const u_int64_t start1, len1, start2, len2;
+{
+ u_int64_t d1,d2,d3,d4;
+ enum split_status result;
+
+ debuglog("Entering region overlap with vals: %llu:%llu--%llu:%llu\n",
+ start1, len1, start2, len2);
+
+ result = region_compare(start1, len1, start2, len2,
+ &d1, &d2, &d3, &d4);
+
+ debuglog("Exiting region overlap with val: %d\n",result);
+
+ if (result == SPL_DISJOINT) {
+ return 0;
+ } else {
+ return 1;
+ }
+
+ return (result);
+}
+
+/*
+ * region_compare(): Examine lock regions and split appropriately
+ *
+ * XXX: Fix 64 bit overflow problems
+ * XXX: Check to make sure I got *ALL* the cases.
+ * XXX: This DESPERATELY needs a regression test.
+ */
+enum split_status
+region_compare(starte, lene, startu, lenu,
+ start1, len1, start2, len2)
+ const u_int64_t starte, lene, startu, lenu;
+ u_int64_t *start1, *len1, *start2, *len2;
+{
+ /*
+ * Please pay attention to the sequential exclusions
+ * of the if statements!!!
+ */
+ enum LFLAGS lflags;
+ enum RFLAGS rflags;
+ enum split_status retval;
+
+ retval = SPL_DISJOINT;
+
+ if (lene == 0 && lenu == 0) {
+ /* Examine left edge of locker */
+ lflags = LEDGE_INSIDE;
+ if (startu < starte) {
+ lflags = LEDGE_LEFT;
+ } else if (startu == starte) {
+ lflags = LEDGE_LBOUNDARY;
+ }
+
+ rflags = REDGE_RBOUNDARY; /* Both are infiinite */
+
+ if (lflags == LEDGE_INSIDE) {
+ *start1 = starte;
+ *len1 = startu - starte;
+ }
+
+ if (lflags == LEDGE_LEFT || lflags == LEDGE_LBOUNDARY) {
+ retval = SPL_CONTAINED;
+ } else {
+ retval = SPL_LOCK1;
+ }
+ } else if (lene == 0 && lenu != 0) {
+ /* Established lock is infinite */
+ /* Examine left edge of unlocker */
+ lflags = LEDGE_INSIDE;
+ if (startu < starte) {
+ lflags = LEDGE_LEFT;
+ } else if (startu == starte) {
+ lflags = LEDGE_LBOUNDARY;
+ }
+
+ /* Examine right edge of unlocker */
+ if (startu + lenu < starte) {
+ /* Right edge of unlocker left of established lock */
+ rflags = REDGE_LEFT;
+ return SPL_DISJOINT;
+ } else if (startu + lenu == starte) {
+ /* Right edge of unlocker on start of established lock */
+ rflags = REDGE_LBOUNDARY;
+ return SPL_DISJOINT;
+ } else { /* Infinifty is right of finity */
+ /* Right edge of unlocker inside established lock */
+ rflags = REDGE_INSIDE;
+ }
+
+ if (lflags == LEDGE_INSIDE) {
+ *start1 = starte;
+ *len1 = startu - starte;
+ retval |= SPL_LOCK1;
+ }
+
+ if (rflags == REDGE_INSIDE) {
+ /* Create right lock */
+ *start2 = startu+lenu;
+ *len2 = 0;
+ retval |= SPL_LOCK2;
+ }
+ } else if (lene != 0 && lenu == 0) {
+ /* Unlocker is infinite */
+ /* Examine left edge of unlocker */
+ lflags = LEDGE_RIGHT;
+ if (startu < starte) {
+ lflags = LEDGE_LEFT;
+ retval = SPL_CONTAINED;
+ return retval;
+ } else if (startu == starte) {
+ lflags = LEDGE_LBOUNDARY;
+ retval = SPL_CONTAINED;
+ return retval;
+ } else if ((startu > starte) && (startu < starte + lene - 1)) {
+ lflags = LEDGE_INSIDE;
+ } else if (startu == starte + lene - 1) {
+ lflags = LEDGE_RBOUNDARY;
+ } else { /* startu > starte + lene -1 */
+ lflags = LEDGE_RIGHT;
+ return SPL_DISJOINT;
+ }
+
+ rflags = REDGE_RIGHT; /* Infinity is right of finity */
+
+ if (lflags == LEDGE_INSIDE || lflags == LEDGE_RBOUNDARY) {
+ *start1 = starte;
+ *len1 = startu - starte;
+ retval |= SPL_LOCK1;
+ return retval;
+ }
+ } else {
+ /* Both locks are finite */
+
+ /* Examine left edge of unlocker */
+ lflags = LEDGE_RIGHT;
+ if (startu < starte) {
+ lflags = LEDGE_LEFT;
+ } else if (startu == starte) {
+ lflags = LEDGE_LBOUNDARY;
+ } else if ((startu > starte) && (startu < starte + lene - 1)) {
+ lflags = LEDGE_INSIDE;
+ } else if (startu == starte + lene - 1) {
+ lflags = LEDGE_RBOUNDARY;
+ } else { /* startu > starte + lene -1 */
+ lflags = LEDGE_RIGHT;
+ return SPL_DISJOINT;
+ }
+
+ /* Examine right edge of unlocker */
+ if (startu + lenu < starte) {
+ /* Right edge of unlocker left of established lock */
+ rflags = REDGE_LEFT;
+ return SPL_DISJOINT;
+ } else if (startu + lenu == starte) {
+ /* Right edge of unlocker on start of established lock */
+ rflags = REDGE_LBOUNDARY;
+ return SPL_DISJOINT;
+ } else if (startu + lenu < starte + lene) {
+ /* Right edge of unlocker inside established lock */
+ rflags = REDGE_INSIDE;
+ } else if (startu + lenu == starte + lene) {
+ /* Right edge of unlocker on right edge of established lock */
+ rflags = REDGE_RBOUNDARY;
+ } else { /* startu + lenu > starte + lene */
+ /* Right edge of unlocker is right of established lock */
+ rflags = REDGE_RIGHT;
+ }
+
+ if (lflags == LEDGE_INSIDE || lflags == LEDGE_RBOUNDARY) {
+ /* Create left lock */
+ *start1 = starte;
+ *len1 = (startu - starte);
+ retval |= SPL_LOCK1;
+ }
+
+ if (rflags == REDGE_INSIDE) {
+ /* Create right lock */
+ *start2 = startu+lenu;
+ *len2 = starte+lene-(startu+lenu);
+ retval |= SPL_LOCK2;
+ }
+
+ if ((lflags == LEDGE_LEFT || lflags == LEDGE_LBOUNDARY) &&
+ (rflags == REDGE_RBOUNDARY || rflags == REDGE_RIGHT)) {
+ retval = SPL_CONTAINED;
+ }
+ }
+ return retval;
+}
+
+/*
+ * same_netobj: Compares the apprpriate bits of a netobj for identity
+ */
+int
+same_netobj(const netobj *n0, const netobj *n1)
+{
+ int retval;
+
+ retval = 0;
+
+ debuglog("Entering netobj identity check\n");
+
+ if (n0->n_len == n1->n_len) {
+ debuglog("Preliminary length check passed\n");
+ retval = !bcmp(n0->n_bytes, n1->n_bytes, n0->n_len);
+ debuglog("netobj %smatch\n", retval ? "" : "mis");
+ }
+
+ return (retval);
+}
+
+/*
+ * same_filelock_identity: Compares the appropriate bits of a file_lock
+ */
+int
+same_filelock_identity(fl0, fl1)
+ const struct file_lock *fl0, *fl1;
+{
+ int retval;
+
+ retval = 0;
+
+ debuglog("Checking filelock identity\n");
+
+ /*
+ * Check process ids and host information.
+ */
+ retval = (fl0->client.svid == fl1->client.svid &&
+ same_netobj(&(fl0->client.oh), &(fl1->client.oh)));
+
+ debuglog("Exiting checking filelock identity: retval: %d\n",retval);
+
+ return (retval);
+}
+
+/*
+ * Below here are routines associated with manipulating the NFS
+ * lock list.
+ */
+
+/*
+ * get_lock_matching_unlock: Return a lock which matches the given unlock lock
+ * or NULL otehrwise
+ * XXX: It is a shame that this duplicates so much code from test_nfslock.
+ */
+struct file_lock *
+get_lock_matching_unlock(const struct file_lock *fl)
+{
+ struct file_lock *ifl; /* Iterator */
+
+ debuglog("Entering get_lock_matching_unlock\n");
+ debuglog("********Dump of fl*****************\n");
+ dump_filelock(fl);
+
+ LIST_FOREACH(ifl, &nfslocklist_head, nfslocklist) {
+ debuglog("Pointer to file lock: %p\n",ifl);
+
+ debuglog("****Dump of ifl****\n");
+ dump_filelock(ifl);
+ debuglog("*******************\n");
+
+ /*
+ * XXX: It is conceivable that someone could use the NLM RPC
+ * system to directly access filehandles. This may be a
+ * security hazard as the filehandle code may bypass normal
+ * file access controls
+ */
+ if (bcmp(&fl->filehandle, &ifl->filehandle, sizeof(fhandle_t)))
+ continue;
+
+ debuglog("get_lock_matching_unlock: Filehandles match, "
+ "checking regions\n");
+
+ /* Filehandles match, check for region overlap */
+ if (!regions_overlap(fl->client.l_offset, fl->client.l_len,
+ ifl->client.l_offset, ifl->client.l_len))
+ continue;
+
+ debuglog("get_lock_matching_unlock: Region overlap"
+ " found %llu : %llu -- %llu : %llu\n",
+ fl->client.l_offset,fl->client.l_len,
+ ifl->client.l_offset,ifl->client.l_len);
+
+ /* Regions overlap, check the identity */
+ if (!same_filelock_identity(fl,ifl))
+ continue;
+
+ debuglog("get_lock_matching_unlock: Duplicate lock id. Granting\n");
+ return (ifl);
+ }
+
+ debuglog("Exiting bet_lock_matching_unlock\n");
+
+ return (NULL);
+}
+
+/*
+ * test_nfslock: check for NFS lock in lock list
+ *
+ * This routine makes the following assumptions:
+ * 1) Nothing will adjust the lock list during a lookup
+ *
+ * This routine has an intersting quirk which bit me hard.
+ * The conflicting_fl is the pointer to the conflicting lock.
+ * However, to modify the "*pointer* to the conflicting lock" rather
+ * that the "conflicting lock itself" one must pass in a "pointer to
+ * the pointer of the conflicting lock". Gross.
+ */
+
+enum nfslock_status
+test_nfslock(const struct file_lock *fl, struct file_lock **conflicting_fl)
+{
+ struct file_lock *ifl; /* Iterator */
+ enum nfslock_status retval;
+
+ debuglog("Entering test_nfslock\n");
+
+ retval = NFS_GRANTED;
+ (*conflicting_fl) = NULL;
+
+ debuglog("Entering lock search loop\n");
+
+ debuglog("***********************************\n");
+ debuglog("Dumping match filelock\n");
+ debuglog("***********************************\n");
+ dump_filelock(fl);
+ debuglog("***********************************\n");
+
+ LIST_FOREACH(ifl, &nfslocklist_head, nfslocklist) {
+ if (retval == NFS_DENIED)
+ break;
+
+ debuglog("Top of lock loop\n");
+ debuglog("Pointer to file lock: %p\n",ifl);
+
+ debuglog("***********************************\n");
+ debuglog("Dumping test filelock\n");
+ debuglog("***********************************\n");
+ dump_filelock(ifl);
+ debuglog("***********************************\n");
+
+ /*
+ * XXX: It is conceivable that someone could use the NLM RPC
+ * system to directly access filehandles. This may be a
+ * security hazard as the filehandle code may bypass normal
+ * file access controls
+ */
+ if (bcmp(&fl->filehandle, &ifl->filehandle, sizeof(fhandle_t)))
+ continue;
+
+ debuglog("test_nfslock: filehandle match found\n");
+
+ /* Filehandles match, check for region overlap */
+ if (!regions_overlap(fl->client.l_offset, fl->client.l_len,
+ ifl->client.l_offset, ifl->client.l_len))
+ continue;
+
+ debuglog("test_nfslock: Region overlap found"
+ " %llu : %llu -- %llu : %llu\n",
+ fl->client.l_offset,fl->client.l_len,
+ ifl->client.l_offset,ifl->client.l_len);
+
+ /* Regions overlap, check the exclusivity */
+ if (!(fl->client.exclusive || ifl->client.exclusive))
+ continue;
+
+ debuglog("test_nfslock: Exclusivity failure: %d %d\n",
+ fl->client.exclusive,
+ ifl->client.exclusive);
+
+ if (same_filelock_identity(fl,ifl)) {
+ debuglog("test_nfslock: Duplicate id. Granting\n");
+ (*conflicting_fl) = ifl;
+ retval = NFS_GRANTED_DUPLICATE;
+ } else {
+ /* locking attempt fails */
+ debuglog("test_nfslock: Lock attempt failed\n");
+ debuglog("Desired lock\n");
+ dump_filelock(fl);
+ debuglog("Conflicting lock\n");
+ dump_filelock(ifl);
+ (*conflicting_fl) = ifl;
+ retval = NFS_DENIED;
+ }
+ }
+
+ debuglog("Dumping file locks\n");
+ debuglog("Exiting test_nfslock\n");
+
+ return (retval);
+}
+
+/*
+ * lock_nfslock: attempt to create a lock in the NFS lock list
+ *
+ * This routine tests whether the lock will be granted and then adds
+ * the entry to the lock list if so.
+ *
+ * Argument fl gets modified as its list housekeeping entries get modified
+ * upon insertion into the NFS lock list
+ *
+ * This routine makes several assumptions:
+ * 1) It is perfectly happy to grant a duplicate lock from the same pid.
+ * While this seems to be intuitively wrong, it is required for proper
+ * Posix semantics during unlock. It is absolutely imperative to not
+ * unlock the main lock before the two child locks are established. Thus,
+ * one has be be able to create duplicate locks over an existing lock
+ * 2) It currently accepts duplicate locks from the same id,pid
+ */
+
+enum nfslock_status
+lock_nfslock(struct file_lock *fl)
+{
+ enum nfslock_status retval;
+ struct file_lock *dummy_fl;
+
+ dummy_fl = NULL;
+
+ debuglog("Entering lock_nfslock...\n");
+
+ retval = test_nfslock(fl,&dummy_fl);
+
+ if (retval == NFS_GRANTED || retval == NFS_GRANTED_DUPLICATE) {
+ debuglog("Inserting lock...\n");
+ dump_filelock(fl);
+ LIST_INSERT_HEAD(&nfslocklist_head, fl, nfslocklist);
+ }
+
+ debuglog("Exiting lock_nfslock...\n");
+
+ return (retval);
+}
+
+/*
+ * delete_nfslock: delete an NFS lock list entry
+ *
+ * This routine is used to delete a lock out of the NFS lock list
+ * without regard to status, underlying locks, regions or anything else
+ *
+ * Note that this routine *does not deallocate memory* of the lock.
+ * It just disconnects it from the list. The lock can then be used
+ * by other routines without fear of trashing the list.
+ */
+
+enum nfslock_status
+delete_nfslock(struct file_lock *fl)
+{
+
+ LIST_REMOVE(fl, nfslocklist);
+
+ return (NFS_GRANTED);
+}
+
+enum split_status
+split_nfslock(exist_lock, unlock_lock, left_lock, right_lock)
+ const struct file_lock *exist_lock, *unlock_lock;
+ struct file_lock **left_lock, **right_lock;
+{
+ u_int64_t start1, len1, start2, len2;
+ enum split_status spstatus;
+
+ spstatus = region_compare(exist_lock->client.l_offset, exist_lock->client.l_len,
+ unlock_lock->client.l_offset, unlock_lock->client.l_len,
+ &start1, &len1, &start2, &len2);
+
+ if ((spstatus & SPL_LOCK1) != 0) {
+ *left_lock = allocate_file_lock(&exist_lock->client.oh, &exist_lock->client_cookie, exist_lock->addr, exist_lock->client_name);
+ if (*left_lock == NULL) {
+ debuglog("Unable to allocate resource for split 1\n");
+ return SPL_RESERR;
+ }
+
+ fill_file_lock(*left_lock, &exist_lock->filehandle,
+ exist_lock->client.exclusive, exist_lock->client.svid,
+ start1, len1,
+ exist_lock->nsm_status,
+ exist_lock->status, exist_lock->flags, exist_lock->blocking);
+ }
+
+ if ((spstatus & SPL_LOCK2) != 0) {
+ *right_lock = allocate_file_lock(&exist_lock->client.oh, &exist_lock->client_cookie, exist_lock->addr, exist_lock->client_name);
+ if (*right_lock == NULL) {
+ debuglog("Unable to allocate resource for split 1\n");
+ if (*left_lock != NULL) {
+ deallocate_file_lock(*left_lock);
+ }
+ return SPL_RESERR;
+ }
+
+ fill_file_lock(*right_lock, &exist_lock->filehandle,
+ exist_lock->client.exclusive, exist_lock->client.svid,
+ start2, len2,
+ exist_lock->nsm_status,
+ exist_lock->status, exist_lock->flags, exist_lock->blocking);
+ }
+
+ return spstatus;
+}
+
+enum nfslock_status
+unlock_nfslock(fl, released_lock, left_lock, right_lock)
+ const struct file_lock *fl;
+ struct file_lock **released_lock;
+ struct file_lock **left_lock;
+ struct file_lock **right_lock;
+{
+ struct file_lock *mfl; /* Matching file lock */
+ enum nfslock_status retval;
+ enum split_status spstatus;
+
+ debuglog("Entering unlock_nfslock\n");
+
+ *released_lock = NULL;
+ *left_lock = NULL;
+ *right_lock = NULL;
+
+ retval = NFS_DENIED_NOLOCK;
+
+ debuglog("Attempting to match lock...\n");
+ mfl = get_lock_matching_unlock(fl);
+
+ if (mfl != NULL) {
+ debuglog("Unlock matched. Querying for split\n");
+
+ spstatus = split_nfslock(mfl, fl, left_lock, right_lock);
+
+ debuglog("Split returned %d %p %p %p %p\n",spstatus,mfl,fl,*left_lock,*right_lock);
+ debuglog("********Split dumps********");
+ dump_filelock(mfl);
+ dump_filelock(fl);
+ dump_filelock(*left_lock);
+ dump_filelock(*right_lock);
+ debuglog("********End Split dumps********");
+
+ if (spstatus == SPL_RESERR) {
+ if (*left_lock != NULL) {
+ deallocate_file_lock(*left_lock);
+ *left_lock = NULL;
+ }
+
+ if (*right_lock != NULL) {
+ deallocate_file_lock(*right_lock);
+ *right_lock = NULL;
+ }
+
+ return NFS_RESERR;
+ }
+
+ /* Insert new locks from split if required */
+ if (*left_lock != NULL) {
+ debuglog("Split left activated\n");
+ LIST_INSERT_HEAD(&nfslocklist_head, *left_lock, nfslocklist);
+ }
+
+ if (*right_lock != NULL) {
+ debuglog("Split right activated\n");
+ LIST_INSERT_HEAD(&nfslocklist_head, *right_lock, nfslocklist);
+ }
+
+ /* Unlock the lock since it matches identity */
+ LIST_REMOVE(mfl, nfslocklist);
+ *released_lock = mfl;
+ retval = NFS_GRANTED;
+ }
+
+ debuglog("Exiting unlock_nfslock\n");
+
+ return retval;
+}
+
+/*
+ * Below here are the routines for manipulating the file lock directly
+ * on the disk hardware itself
+ */
+enum hwlock_status
+lock_hwlock(struct file_lock *fl)
+{
+ struct monfile *imf,*nmf;
+ int lflags, flerror;
+
+ /* Scan to see if filehandle already present */
+ LIST_FOREACH(imf, &monfilelist_head, monfilelist) {
+ if (bcmp(&fl->filehandle, &imf->filehandle,
+ sizeof(fl->filehandle)) == 0) {
+ /* imf is the correct filehandle */
+ break;
+ }
+ }
+
+ /*
+ * Filehandle already exists (we control the file)
+ * *AND* NFS has already cleared the lock for availability
+ * Grant it and bump the refcount.
+ */
+ if (imf != NULL) {
+ ++(imf->refcount);
+ return (HW_GRANTED);
+ }
+
+ /* No filehandle found, create and go */
+ nmf = malloc(sizeof(struct monfile));
+ if (nmf == NULL) {
+ debuglog("hwlock resource allocation failure\n");
+ return (HW_RESERR);
+ }
+
+ /* XXX: Is O_RDWR always the correct mode? */
+ nmf->fd = fhopen(&fl->filehandle, O_RDWR);
+ if (nmf->fd < 0) {
+ debuglog("fhopen failed (from %16s): %32s\n",
+ fl->client_name, strerror(errno));
+ free(nmf);
+ switch (errno) {
+ case ESTALE:
+ return (HW_STALEFH);
+ case EROFS:
+ return (HW_READONLY);
+ default:
+ return (HW_RESERR);
+ }
+ }
+
+ /* File opened correctly, fill the monitor struct */
+ bcopy(&fl->filehandle, &nmf->filehandle, sizeof(fl->filehandle));
+ nmf->refcount = 1;
+ nmf->exclusive = fl->client.exclusive;
+
+ lflags = (nmf->exclusive == 1) ?
+ (LOCK_EX | LOCK_NB) : (LOCK_SH | LOCK_NB);
+
+ flerror = flock(nmf->fd, lflags);
+
+ if (flerror != 0) {
+ debuglog("flock failed (from %16s): %32s\n",
+ fl->client_name, strerror(errno));
+ close(nmf->fd);
+ free(nmf);
+ switch (errno) {
+ case EAGAIN:
+ return (HW_DENIED);
+ case ESTALE:
+ return (HW_STALEFH);
+ case EROFS:
+ return (HW_READONLY);
+ default:
+ return (HW_RESERR);
+ break;
+ }
+ }
+
+ /* File opened and locked */
+ LIST_INSERT_HEAD(&monfilelist_head, nmf, monfilelist);
+
+ debuglog("flock succeeded (from %16s)\n", fl->client_name);
+ return (HW_GRANTED);
+}
+
+enum hwlock_status
+unlock_hwlock(const struct file_lock *fl)
+{
+ struct monfile *imf;
+
+ debuglog("Entering unlock_hwlock\n");
+ debuglog("Entering loop interation\n");
+
+ /* Scan to see if filehandle already present */
+ LIST_FOREACH(imf, &monfilelist_head, monfilelist) {
+ if (bcmp(&fl->filehandle, &imf->filehandle,
+ sizeof(fl->filehandle)) == 0) {
+ /* imf is the correct filehandle */
+ break;
+ }
+ }
+
+ debuglog("Completed iteration. Proceeding\n");
+
+ if (imf == NULL) {
+ /* No lock found */
+ debuglog("Exiting unlock_hwlock (HW_DENIED_NOLOCK)\n");
+ return (HW_DENIED_NOLOCK);
+ }
+
+ /* Lock found */
+ --imf->refcount;
+
+ if (imf->refcount < 0) {
+ debuglog("Negative hardware reference count\n");
+ }
+
+ if (imf->refcount <= 0) {
+ close(imf->fd);
+ LIST_REMOVE(imf, monfilelist);
+ free(imf);
+ }
+ debuglog("Exiting unlock_hwlock (HW_GRANTED)\n");
+ return (HW_GRANTED);
+}
+
+enum hwlock_status
+test_hwlock(fl, conflicting_fl)
+ const struct file_lock *fl __unused;
+ struct file_lock **conflicting_fl __unused;
+{
+
+ /*
+ * XXX: lock tests on hardware are not required until
+ * true partial file testing is done on the underlying file
+ */
+ return (HW_RESERR);
+}
+
+
+
+/*
+ * Below here are routines for manipulating blocked lock requests
+ * They should only be called from the XXX_partialfilelock routines
+ * if at all possible
+ */
+
+int
+duplicate_block(struct file_lock *fl)
+{
+ struct file_lock *ifl,*nfl;
+ int retval = 0;
+
+ debuglog("Entering duplicate_block");
+
+ /*
+ * Is this lock request already on the blocking list?
+ * Consider it a dupe if the file handles, offset, length,
+ * exclusivity and client match.
+ */
+ LIST_FOREACH(ifl, &blockedlocklist_head, nfslocklist) {
+ if (!bcmp(&fl->filehandle, &ifl->filehandle,
+ sizeof(fhandle_t)) &&
+ fl->client.exclusive == ifl->client.exclusive &&
+ fl->client.l_offset == ifl->client.l_offset &&
+ fl->client.l_len == ifl->client.l_len &&
+ same_filelock_identity(fl, ifl)) {
+ retval = 1;
+ break;
+ }
+ }
+
+ debuglog("Exiting duplicate_block: %s\n", retval ? "already blocked"
+ : "not already blocked");
+ return retval;
+}
+
+void
+add_blockingfilelock(struct file_lock *fl)
+{
+ debuglog("Entering add_blockingfilelock\n");
+
+ /*
+ * A blocking lock request _should_ never be duplicated as a client
+ * that is already blocked shouldn't be able to request another
+ * lock. Alas, there are some buggy clients that do request the same
+ * lock repeatedly. Make sure only unique locks are on the blocked
+ * lock list.
+ */
+ if (duplicate_block(fl)) {
+ debuglog("Exiting add_blockingfilelock: already blocked\n");
+ return;
+ }
+
+ /*
+ * Clear the blocking flag so that it can be reused without
+ * adding it to the blocking queue a second time
+ */
+
+ fl->blocking = 0;
+ LIST_INSERT_HEAD(&blockedlocklist_head, fl, nfslocklist);
+
+ debuglog("Exiting add_blockingfilelock: added blocked lock\n");
+}
+
+void
+remove_blockingfilelock(struct file_lock *fl)
+{
+
+ debuglog("Entering remove_blockingfilelock\n");
+
+ LIST_REMOVE(fl, nfslocklist);
+
+ debuglog("Exiting remove_blockingfilelock\n");
+}
+
+void
+clear_blockingfilelock(const char *hostname)
+{
+ struct file_lock *ifl,*nfl;
+
+ /*
+ * Normally, LIST_FOREACH is called for, but since
+ * the current element *is* the iterator, deleting it
+ * would mess up the iteration. Thus, a next element
+ * must be used explicitly
+ */
+
+ ifl = LIST_FIRST(&blockedlocklist_head);
+
+ while (ifl != NULL) {
+ nfl = LIST_NEXT(ifl, nfslocklist);
+
+ if (strncmp(hostname, ifl->client_name, SM_MAXSTRLEN) == 0) {
+ remove_blockingfilelock(ifl);
+ deallocate_file_lock(ifl);
+ }
+
+ ifl = nfl;
+ }
+}
+
+void
+retry_blockingfilelocklist(void)
+{
+ /* Retry all locks in the blocked list */
+ struct file_lock *ifl, *nfl; /* Iterator */
+ enum partialfilelock_status pflstatus;
+
+ debuglog("Entering retry_blockingfilelocklist\n");
+
+ LIST_FOREACH_SAFE(ifl, &blockedlocklist_head, nfslocklist, nfl) {
+ debuglog("Iterator choice %p\n",ifl);
+ debuglog("Next iterator choice %p\n",nfl);
+
+ /*
+ * SUBTLE BUG: The file_lock must be removed from the
+ * old list so that it's list pointers get disconnected
+ * before being allowed to participate in the new list
+ * which will automatically add it in if necessary.
+ */
+
+ LIST_REMOVE(ifl, nfslocklist);
+ pflstatus = lock_partialfilelock(ifl);
+
+ if (pflstatus == PFL_GRANTED || pflstatus == PFL_GRANTED_DUPLICATE) {
+ debuglog("Granted blocked lock\n");
+ /* lock granted and is now being used */
+ send_granted(ifl,0);
+ } else {
+ /* Reinsert lock back into blocked list */
+ debuglog("Replacing blocked lock\n");
+ LIST_INSERT_HEAD(&blockedlocklist_head, ifl, nfslocklist);
+ }
+ }
+
+ debuglog("Exiting retry_blockingfilelocklist\n");
+}
+
+/*
+ * Below here are routines associated with manipulating all
+ * aspects of the partial file locking system (list, hardware, etc.)
+ */
+
+/*
+ * Please note that lock monitoring must be done at this level which
+ * keeps track of *individual* lock requests on lock and unlock
+ *
+ * XXX: Split unlocking is going to make the unlock code miserable
+ */
+
+/*
+ * lock_partialfilelock:
+ *
+ * Argument fl gets modified as its list housekeeping entries get modified
+ * upon insertion into the NFS lock list
+ *
+ * This routine makes several assumptions:
+ * 1) It (will) pass locks through to flock to lock the entire underlying file
+ * and then parcel out NFS locks if it gets control of the file.
+ * This matches the old rpc.lockd file semantics (except where it
+ * is now more correct). It is the safe solution, but will cause
+ * overly restrictive blocking if someone is trying to use the
+ * underlying files without using NFS. This appears to be an
+ * acceptable tradeoff since most people use standalone NFS servers.
+ * XXX: The right solution is probably kevent combined with fcntl
+ *
+ * 2) Nothing modifies the lock lists between testing and granting
+ * I have no idea whether this is a useful assumption or not
+ */
+
+enum partialfilelock_status
+lock_partialfilelock(struct file_lock *fl)
+{
+ enum partialfilelock_status retval;
+ enum nfslock_status lnlstatus;
+ enum hwlock_status hwstatus;
+
+ debuglog("Entering lock_partialfilelock\n");
+
+ retval = PFL_DENIED;
+
+ /*
+ * Execute the NFS lock first, if possible, as it is significantly
+ * easier and less expensive to undo than the filesystem lock
+ */
+
+ lnlstatus = lock_nfslock(fl);
+
+ switch (lnlstatus) {
+ case NFS_GRANTED:
+ case NFS_GRANTED_DUPLICATE:
+ /*
+ * At this point, the NFS lock is allocated and active.
+ * Remember to clean it up if the hardware lock fails
+ */
+ hwstatus = lock_hwlock(fl);
+
+ switch (hwstatus) {
+ case HW_GRANTED:
+ case HW_GRANTED_DUPLICATE:
+ debuglog("HW GRANTED\n");
+ /*
+ * XXX: Fixme: Check hwstatus for duplicate when
+ * true partial file locking and accounting is
+ * done on the hardware.
+ */
+ if (lnlstatus == NFS_GRANTED_DUPLICATE) {
+ retval = PFL_GRANTED_DUPLICATE;
+ } else {
+ retval = PFL_GRANTED;
+ }
+ monitor_lock_host(fl->client_name);
+ break;
+ case HW_RESERR:
+ debuglog("HW RESERR\n");
+ retval = PFL_HWRESERR;
+ break;
+ case HW_DENIED:
+ debuglog("HW DENIED\n");
+ retval = PFL_HWDENIED;
+ break;
+ default:
+ debuglog("Unmatched hwstatus %d\n",hwstatus);
+ break;
+ }
+
+ if (retval != PFL_GRANTED &&
+ retval != PFL_GRANTED_DUPLICATE) {
+ /* Clean up the NFS lock */
+ debuglog("Deleting trial NFS lock\n");
+ delete_nfslock(fl);
+ }
+ break;
+ case NFS_DENIED:
+ retval = PFL_NFSDENIED;
+ break;
+ case NFS_RESERR:
+ retval = PFL_NFSRESERR;
+ default:
+ debuglog("Unmatched lnlstatus %d\n");
+ retval = PFL_NFSDENIED_NOLOCK;
+ break;
+ }
+
+ /*
+ * By the time fl reaches here, it is completely free again on
+ * failure. The NFS lock done before attempting the
+ * hardware lock has been backed out
+ */
+
+ if (retval == PFL_NFSDENIED || retval == PFL_HWDENIED) {
+ /* Once last chance to check the lock */
+ if (fl->blocking == 1) {
+ if (retval == PFL_NFSDENIED) {
+ /* Queue the lock */
+ debuglog("BLOCKING LOCK RECEIVED\n");
+ retval = PFL_NFSBLOCKED;
+ add_blockingfilelock(fl);
+ dump_filelock(fl);
+ } else {
+ /* retval is okay as PFL_HWDENIED */
+ debuglog("BLOCKING LOCK DENIED IN HARDWARE\n");
+ dump_filelock(fl);
+ }
+ } else {
+ /* Leave retval alone, it's already correct */
+ debuglog("Lock denied. Non-blocking failure\n");
+ dump_filelock(fl);
+ }
+ }
+
+ debuglog("Exiting lock_partialfilelock\n");
+
+ return retval;
+}
+
+/*
+ * unlock_partialfilelock:
+ *
+ * Given a file_lock, unlock all locks which match.
+ *
+ * Note that a given lock might have to unlock ITSELF! See
+ * clear_partialfilelock for example.
+ */
+
+enum partialfilelock_status
+unlock_partialfilelock(const struct file_lock *fl)
+{
+ struct file_lock *lfl,*rfl,*releasedfl,*selffl;
+ enum partialfilelock_status retval;
+ enum nfslock_status unlstatus;
+ enum hwlock_status unlhwstatus, lhwstatus;
+
+ debuglog("Entering unlock_partialfilelock\n");
+
+ selffl = NULL;
+ lfl = NULL;
+ rfl = NULL;
+ releasedfl = NULL;
+ retval = PFL_DENIED;
+
+ /*
+ * There are significant overlap and atomicity issues
+ * with partially releasing a lock. For example, releasing
+ * part of an NFS shared lock does *not* always release the
+ * corresponding part of the file since there is only one
+ * rpc.lockd UID but multiple users could be requesting it
+ * from NFS. Also, an unlock request should never allow
+ * another process to gain a lock on the remaining parts.
+ * ie. Always apply the new locks before releasing the
+ * old one
+ */
+
+ /*
+ * Loop is required since multiple little locks
+ * can be allocated and then deallocated with one
+ * big unlock.
+ *
+ * The loop is required to be here so that the nfs &
+ * hw subsystems do not need to communicate with one
+ * one another
+ */
+
+ do {
+ debuglog("Value of releasedfl: %p\n",releasedfl);
+ /* lfl&rfl are created *AND* placed into the NFS lock list if required */
+ unlstatus = unlock_nfslock(fl, &releasedfl, &lfl, &rfl);
+ debuglog("Value of releasedfl: %p\n",releasedfl);
+
+
+ /* XXX: This is grungy. It should be refactored to be cleaner */
+ if (lfl != NULL) {
+ lhwstatus = lock_hwlock(lfl);
+ if (lhwstatus != HW_GRANTED &&
+ lhwstatus != HW_GRANTED_DUPLICATE) {
+ debuglog("HW duplicate lock failure for left split\n");
+ }
+ monitor_lock_host(lfl->client_name);
+ }
+
+ if (rfl != NULL) {
+ lhwstatus = lock_hwlock(rfl);
+ if (lhwstatus != HW_GRANTED &&
+ lhwstatus != HW_GRANTED_DUPLICATE) {
+ debuglog("HW duplicate lock failure for right split\n");
+ }
+ monitor_lock_host(rfl->client_name);
+ }
+
+ switch (unlstatus) {
+ case NFS_GRANTED:
+ /* Attempt to unlock on the hardware */
+ debuglog("NFS unlock granted. Attempting hardware unlock\n");
+
+ /* This call *MUST NOT* unlock the two newly allocated locks */
+ unlhwstatus = unlock_hwlock(fl);
+ debuglog("HW unlock returned with code %d\n",unlhwstatus);
+
+ switch (unlhwstatus) {
+ case HW_GRANTED:
+ debuglog("HW unlock granted\n");
+ unmonitor_lock_host(releasedfl->client_name);
+ retval = PFL_GRANTED;
+ break;
+ case HW_DENIED_NOLOCK:
+ /* Huh?!?! This shouldn't happen */
+ debuglog("HW unlock denied no lock\n");
+ retval = PFL_HWRESERR;
+ /* Break out of do-while */
+ unlstatus = NFS_RESERR;
+ break;
+ default:
+ debuglog("HW unlock failed\n");
+ retval = PFL_HWRESERR;
+ /* Break out of do-while */
+ unlstatus = NFS_RESERR;
+ break;
+ }
+
+ debuglog("Exiting with status retval: %d\n",retval);
+
+ retry_blockingfilelocklist();
+ break;
+ case NFS_DENIED_NOLOCK:
+ retval = PFL_GRANTED;
+ debuglog("All locks cleaned out\n");
+ break;
+ default:
+ retval = PFL_NFSRESERR;
+ debuglog("NFS unlock failure\n");
+ dump_filelock(fl);
+ break;
+ }
+
+ if (releasedfl != NULL) {
+ if (fl == releasedfl) {
+ /*
+ * XXX: YECHHH!!! Attempt to unlock self succeeded
+ * but we can't deallocate the space yet. This is what
+ * happens when you don't write malloc and free together
+ */
+ debuglog("Attempt to unlock self\n");
+ selffl = releasedfl;
+ } else {
+ /*
+ * XXX: this deallocation *still* needs to migrate closer
+ * to the allocation code way up in get_lock or the allocation
+ * code needs to migrate down (violation of "When you write
+ * malloc you must write free")
+ */
+
+ deallocate_file_lock(releasedfl);
+ }
+ }
+
+ } while (unlstatus == NFS_GRANTED);
+
+ if (selffl != NULL) {
+ /*
+ * This statement wipes out the incoming file lock (fl)
+ * in spite of the fact that it is declared const
+ */
+ debuglog("WARNING! Destroying incoming lock pointer\n");
+ deallocate_file_lock(selffl);
+ }
+
+ debuglog("Exiting unlock_partialfilelock\n");
+
+ return retval;
+}
+
+/*
+ * clear_partialfilelock
+ *
+ * Normally called in response to statd state number change.
+ * Wipe out all locks held by a host. As a bonus, the act of
+ * doing so should automatically clear their statd entries and
+ * unmonitor the host.
+ */
+
+void
+clear_partialfilelock(const char *hostname)
+{
+ struct file_lock *ifl, *nfl;
+
+ /* Clear blocking file lock list */
+ clear_blockingfilelock(hostname);
+
+ /* do all required unlocks */
+ /* Note that unlock can smash the current pointer to a lock */
+
+ /*
+ * Normally, LIST_FOREACH is called for, but since
+ * the current element *is* the iterator, deleting it
+ * would mess up the iteration. Thus, a next element
+ * must be used explicitly
+ */
+
+ ifl = LIST_FIRST(&nfslocklist_head);
+
+ while (ifl != NULL) {
+ nfl = LIST_NEXT(ifl, nfslocklist);
+
+ if (strncmp(hostname, ifl->client_name, SM_MAXSTRLEN) == 0) {
+ /* Unlock destroys ifl out from underneath */
+ unlock_partialfilelock(ifl);
+ /* ifl is NO LONGER VALID AT THIS POINT */
+ }
+ ifl = nfl;
+ }
+}
+
+/*
+ * test_partialfilelock:
+ */
+enum partialfilelock_status
+test_partialfilelock(const struct file_lock *fl,
+ struct file_lock **conflicting_fl)
+{
+ enum partialfilelock_status retval;
+ enum nfslock_status teststatus;
+
+ debuglog("Entering testpartialfilelock...\n");
+
+ retval = PFL_DENIED;
+
+ teststatus = test_nfslock(fl, conflicting_fl);
+ debuglog("test_partialfilelock: teststatus %d\n",teststatus);
+
+ if (teststatus == NFS_GRANTED || teststatus == NFS_GRANTED_DUPLICATE) {
+ /* XXX: Add the underlying filesystem locking code */
+ retval = (teststatus == NFS_GRANTED) ?
+ PFL_GRANTED : PFL_GRANTED_DUPLICATE;
+ debuglog("Dumping locks...\n");
+ dump_filelock(fl);
+ dump_filelock(*conflicting_fl);
+ debuglog("Done dumping locks...\n");
+ } else {
+ retval = PFL_NFSDENIED;
+ debuglog("NFS test denied.\n");
+ dump_filelock(fl);
+ debuglog("Conflicting.\n");
+ dump_filelock(*conflicting_fl);
+ }
+
+ debuglog("Exiting testpartialfilelock...\n");
+
+ return retval;
+}
+
+/*
+ * Below here are routines associated with translating the partial file locking
+ * codes into useful codes to send back to the NFS RPC messaging system
+ */
+
+/*
+ * These routines translate the (relatively) useful return codes back onto
+ * the few return codes which the nlm subsystems wishes to trasmit
+ */
+
+enum nlm_stats
+do_test(struct file_lock *fl, struct file_lock **conflicting_fl)
+{
+ enum partialfilelock_status pfsret;
+ enum nlm_stats retval;
+
+ debuglog("Entering do_test...\n");
+
+ pfsret = test_partialfilelock(fl,conflicting_fl);
+
+ switch (pfsret) {
+ case PFL_GRANTED:
+ debuglog("PFL test lock granted\n");
+ dump_filelock(fl);
+ dump_filelock(*conflicting_fl);
+ retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted;
+ break;
+ case PFL_GRANTED_DUPLICATE:
+ debuglog("PFL test lock granted--duplicate id detected\n");
+ dump_filelock(fl);
+ dump_filelock(*conflicting_fl);
+ debuglog("Clearing conflicting_fl for call semantics\n");
+ *conflicting_fl = NULL;
+ retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted;
+ break;
+ case PFL_NFSDENIED:
+ case PFL_HWDENIED:
+ debuglog("PFL test lock denied\n");
+ dump_filelock(fl);
+ dump_filelock(*conflicting_fl);
+ retval = (fl->flags & LOCK_V4) ? nlm4_denied : nlm_denied;
+ break;
+ case PFL_NFSRESERR:
+ case PFL_HWRESERR:
+ debuglog("PFL test lock resource fail\n");
+ dump_filelock(fl);
+ dump_filelock(*conflicting_fl);
+ retval = (fl->flags & LOCK_V4) ? nlm4_denied_nolocks : nlm_denied_nolocks;
+ break;
+ default:
+ debuglog("PFL test lock *FAILED*\n");
+ dump_filelock(fl);
+ dump_filelock(*conflicting_fl);
+ retval = (fl->flags & LOCK_V4) ? nlm4_failed : nlm_denied;
+ break;
+ }
+
+ debuglog("Exiting do_test...\n");
+
+ return retval;
+}
+
+/*
+ * do_lock: Try to acquire a lock
+ *
+ * This routine makes a distinction between NLM versions. I am pretty
+ * convinced that this should be abstracted out and bounced up a level
+ */
+
+enum nlm_stats
+do_lock(struct file_lock *fl)
+{
+ enum partialfilelock_status pfsret;
+ enum nlm_stats retval;
+
+ debuglog("Entering do_lock...\n");
+
+ pfsret = lock_partialfilelock(fl);
+
+ switch (pfsret) {
+ case PFL_GRANTED:
+ debuglog("PFL lock granted");
+ dump_filelock(fl);
+ retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted;
+ break;
+ case PFL_GRANTED_DUPLICATE:
+ debuglog("PFL lock granted--duplicate id detected");
+ dump_filelock(fl);
+ retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted;
+ break;
+ case PFL_NFSDENIED:
+ case PFL_HWDENIED:
+ debuglog("PFL_NFS lock denied");
+ dump_filelock(fl);
+ retval = (fl->flags & LOCK_V4) ? nlm4_denied : nlm_denied;
+ break;
+ case PFL_NFSBLOCKED:
+ case PFL_HWBLOCKED:
+ debuglog("PFL_NFS blocking lock denied. Queued.\n");
+ dump_filelock(fl);
+ retval = (fl->flags & LOCK_V4) ? nlm4_blocked : nlm_blocked;
+ break;
+ case PFL_NFSRESERR:
+ case PFL_HWRESERR:
+ debuglog("PFL lock resource alocation fail\n");
+ dump_filelock(fl);
+ retval = (fl->flags & LOCK_V4) ? nlm4_denied_nolocks : nlm_denied_nolocks;
+ break;
+ default:
+ debuglog("PFL lock *FAILED*");
+ dump_filelock(fl);
+ retval = (fl->flags & LOCK_V4) ? nlm4_failed : nlm_denied;
+ break;
+ }
+
+ debuglog("Exiting do_lock...\n");
+
+ return retval;
+}
+
+enum nlm_stats
+do_unlock(struct file_lock *fl)
+{
+ enum partialfilelock_status pfsret;
+ enum nlm_stats retval;
+
+ debuglog("Entering do_unlock...\n");
+ pfsret = unlock_partialfilelock(fl);
+
+ switch (pfsret) {
+ case PFL_GRANTED:
+ debuglog("PFL unlock granted");
+ dump_filelock(fl);
+ retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted;
+ break;
+ case PFL_NFSDENIED:
+ case PFL_HWDENIED:
+ debuglog("PFL_NFS unlock denied");
+ dump_filelock(fl);
+ retval = (fl->flags & LOCK_V4) ? nlm4_denied : nlm_denied;
+ break;
+ case PFL_NFSDENIED_NOLOCK:
+ case PFL_HWDENIED_NOLOCK:
+ debuglog("PFL_NFS no lock found\n");
+ retval = (fl->flags & LOCK_V4) ? nlm4_granted : nlm_granted;
+ break;
+ case PFL_NFSRESERR:
+ case PFL_HWRESERR:
+ debuglog("PFL unlock resource failure");
+ dump_filelock(fl);
+ retval = (fl->flags & LOCK_V4) ? nlm4_denied_nolocks : nlm_denied_nolocks;
+ break;
+ default:
+ debuglog("PFL unlock *FAILED*");
+ dump_filelock(fl);
+ retval = (fl->flags & LOCK_V4) ? nlm4_failed : nlm_denied;
+ break;
+ }
+
+ debuglog("Exiting do_unlock...\n");
+
+ return retval;
+}
+
+/*
+ * do_clear
+ *
+ * This routine is non-existent because it doesn't have a return code.
+ * It is here for completeness in case someone *does* need to do return
+ * codes later. A decent compiler should optimize this away.
+ */
+
+void
+do_clear(const char *hostname)
+{
+
+ clear_partialfilelock(hostname);
+}
+
+/*
+ * The following routines are all called from the code which the
+ * RPC layer invokes
+ */
+
+/*
+ * testlock(): inform the caller if the requested lock would be granted
+ *
+ * returns NULL if lock would granted
+ * returns pointer to a conflicting nlm4_holder if not
+ */
+
+struct nlm4_holder *
+testlock(struct nlm4_lock *lock, bool_t exclusive, int flags __unused)
+{
+ struct file_lock test_fl, *conflicting_fl;
+
+ bzero(&test_fl, sizeof(test_fl));
+
+ bcopy(lock->fh.n_bytes, &(test_fl.filehandle), sizeof(fhandle_t));
+ copy_nlm4_lock_to_nlm4_holder(lock, exclusive, &test_fl.client);
+
+ siglock();
+ do_test(&test_fl, &conflicting_fl);
+
+ if (conflicting_fl == NULL) {
+ debuglog("No conflicting lock found\n");
+ sigunlock();
+ return NULL;
+ } else {
+ debuglog("Found conflicting lock\n");
+ dump_filelock(conflicting_fl);
+ sigunlock();
+ return (&conflicting_fl->client);
+ }
+}
+
+/*
+ * getlock: try to aquire the lock.
+ * If file is already locked and we can sleep, put the lock in the list with
+ * status LKST_WAITING; it'll be processed later.
+ * Otherwise try to lock. If we're allowed to block, fork a child which
+ * will do the blocking lock.
+ */
+
+enum nlm_stats
+getlock(nlm4_lockargs *lckarg, struct svc_req *rqstp, const int flags)
+{
+ struct file_lock *newfl;
+ enum nlm_stats retval;
+
+ debuglog("Entering getlock...\n");
+
+ if (grace_expired == 0 && lckarg->reclaim == 0)
+ return (flags & LOCK_V4) ?
+ nlm4_denied_grace_period : nlm_denied_grace_period;
+
+ /* allocate new file_lock for this request */
+ newfl = allocate_file_lock(&lckarg->alock.oh, &lckarg->cookie,
+ (struct sockaddr *)svc_getrpccaller(rqstp->rq_xprt)->buf, lckarg->alock.caller_name);
+ if (newfl == NULL) {
+ syslog(LOG_NOTICE, "lock allocate failed: %s", strerror(errno));
+ /* failed */
+ return (flags & LOCK_V4) ?
+ nlm4_denied_nolocks : nlm_denied_nolocks;
+ }
+
+ if (lckarg->alock.fh.n_len != sizeof(fhandle_t)) {
+ debuglog("recieved fhandle size %d, local size %d",
+ lckarg->alock.fh.n_len, (int)sizeof(fhandle_t));
+ }
+
+ fill_file_lock(newfl, (fhandle_t *)lckarg->alock.fh.n_bytes,
+ lckarg->exclusive, lckarg->alock.svid, lckarg->alock.l_offset,
+ lckarg->alock.l_len,
+ lckarg->state, 0, flags, lckarg->block);
+
+ /*
+ * newfl is now fully constructed and deallocate_file_lock
+ * can now be used to delete it
+ */
+
+ siglock();
+ debuglog("Pointer to new lock is %p\n",newfl);
+
+ retval = do_lock(newfl);
+
+ debuglog("Pointer to new lock is %p\n",newfl);
+ sigunlock();
+
+ switch (retval)
+ {
+ case nlm4_granted:
+ /* case nlm_granted: is the same as nlm4_granted */
+ /* do_mon(lckarg->alock.caller_name); */
+ break;
+ case nlm4_blocked:
+ /* case nlm_blocked: is the same as nlm4_blocked */
+ /* do_mon(lckarg->alock.caller_name); */
+ break;
+ default:
+ deallocate_file_lock(newfl);
+ break;
+ }
+
+ debuglog("Exiting getlock...\n");
+
+ return retval;
+}
+
+
+/* unlock a filehandle */
+enum nlm_stats
+unlock(nlm4_lock *lock, const int flags __unused)
+{
+ struct file_lock fl;
+ enum nlm_stats err;
+
+ siglock();
+
+ debuglog("Entering unlock...\n");
+
+ bzero(&fl,sizeof(struct file_lock));
+ bcopy(lock->fh.n_bytes, &fl.filehandle, sizeof(fhandle_t));
+
+ copy_nlm4_lock_to_nlm4_holder(lock, 0, &fl.client);
+
+ err = do_unlock(&fl);
+
+ sigunlock();
+
+ debuglog("Exiting unlock...\n");
+
+ return err;
+}
+
+/*
+ * XXX: The following monitor/unmonitor routines
+ * have not been extensively tested (ie. no regression
+ * script exists like for the locking sections
+ */
+
+/*
+ * monitor_lock_host: monitor lock hosts locally with a ref count and
+ * inform statd
+ */
+void
+monitor_lock_host(const char *hostname)
+{
+ struct host *ihp, *nhp;
+ struct mon smon;
+ struct sm_stat_res sres;
+ int rpcret, statflag;
+ size_t n;
+
+ rpcret = 0;
+ statflag = 0;
+
+ LIST_FOREACH(ihp, &hostlst_head, hostlst) {
+ if (strncmp(hostname, ihp->name, SM_MAXSTRLEN) == 0) {
+ /* Host is already monitored, bump refcount */
+ ++ihp->refcnt;
+ /* Host should only be in the monitor list once */
+ return;
+ }
+ }
+
+ /* Host is not yet monitored, add it */
+ n = strnlen(hostname, SM_MAXSTRLEN);
+ if (n == SM_MAXSTRLEN) {
+ return;
+ }
+ nhp = malloc(sizeof(*nhp) - sizeof(nhp->name) + n + 1);
+ if (nhp == NULL) {
+ debuglog("Unable to allocate entry for statd mon\n");
+ return;
+ }
+
+ /* Allocated new host entry, now fill the fields */
+ memcpy(nhp->name, hostname, n);
+ nhp->name[n] = 0;
+ nhp->refcnt = 1;
+ debuglog("Locally Monitoring host %16s\n",hostname);
+
+ debuglog("Attempting to tell statd\n");
+
+ bzero(&smon,sizeof(smon));
+
+ smon.mon_id.mon_name = nhp->name;
+ smon.mon_id.my_id.my_name = "localhost";
+ smon.mon_id.my_id.my_prog = NLM_PROG;
+ smon.mon_id.my_id.my_vers = NLM_SM;
+ smon.mon_id.my_id.my_proc = NLM_SM_NOTIFY;
+
+ rpcret = callrpc("localhost", SM_PROG, SM_VERS, SM_MON,
+ (xdrproc_t)xdr_mon, &smon,
+ (xdrproc_t)xdr_sm_stat_res, &sres);
+
+ if (rpcret == 0) {
+ if (sres.res_stat == stat_fail) {
+ debuglog("Statd call failed\n");
+ statflag = 0;
+ } else {
+ statflag = 1;
+ }
+ } else {
+ debuglog("Rpc call to statd failed with return value: %d\n",
+ rpcret);
+ statflag = 0;
+ }
+
+ if (statflag == 1) {
+ LIST_INSERT_HEAD(&hostlst_head, nhp, hostlst);
+ } else {
+ free(nhp);
+ }
+
+}
+
+/*
+ * unmonitor_lock_host: clear monitor ref counts and inform statd when gone
+ */
+void
+unmonitor_lock_host(char *hostname)
+{
+ struct host *ihp;
+ struct mon_id smon_id;
+ struct sm_stat smstat;
+ int rpcret;
+
+ rpcret = 0;
+
+ for( ihp=LIST_FIRST(&hostlst_head); ihp != NULL;
+ ihp=LIST_NEXT(ihp, hostlst)) {
+ if (strncmp(hostname, ihp->name, SM_MAXSTRLEN) == 0) {
+ /* Host is monitored, bump refcount */
+ --ihp->refcnt;
+ /* Host should only be in the monitor list once */
+ break;
+ }
+ }
+
+ if (ihp == NULL) {
+ debuglog("Could not find host %16s in mon list\n", hostname);
+ return;
+ }
+
+ if (ihp->refcnt > 0)
+ return;
+
+ if (ihp->refcnt < 0) {
+ debuglog("Negative refcount!: %d\n",
+ ihp->refcnt);
+ }
+
+ debuglog("Attempting to unmonitor host %16s\n", hostname);
+
+ bzero(&smon_id,sizeof(smon_id));
+
+ smon_id.mon_name = hostname;
+ smon_id.my_id.my_name = "localhost";
+ smon_id.my_id.my_prog = NLM_PROG;
+ smon_id.my_id.my_vers = NLM_SM;
+ smon_id.my_id.my_proc = NLM_SM_NOTIFY;
+
+ rpcret = callrpc("localhost", SM_PROG, SM_VERS, SM_UNMON,
+ (xdrproc_t)xdr_mon_id, &smon_id,
+ (xdrproc_t)xdr_sm_stat, &smstat);
+
+ if (rpcret != 0) {
+ debuglog("Rpc call to unmonitor statd failed with "
+ " return value: %d\n", rpcret);
+ }
+
+ LIST_REMOVE(ihp, hostlst);
+ free(ihp);
+}
+
+/*
+ * notify: Clear all locks from a host if statd complains
+ *
+ * XXX: This routine has not been thoroughly tested. However, neither
+ * had the old one been. It used to compare the statd crash state counter
+ * to the current lock state. The upshot of this was that it basically
+ * cleared all locks from the specified host 99% of the time (with the
+ * other 1% being a bug). Consequently, the assumption is that clearing
+ * all locks from a host when notified by statd is acceptable.
+ *
+ * Please note that this routine skips the usual level of redirection
+ * through a do_* type routine. This introduces a possible level of
+ * error and might better be written as do_notify and take this one out.
+
+ */
+
+void
+notify(const char *hostname, const int state)
+{
+ debuglog("notify from %s, new state %d", hostname, state);
+
+ siglock();
+ do_clear(hostname);
+ sigunlock();
+
+ debuglog("Leaving notify\n");
+}
+
+void
+send_granted(fl, opcode)
+ struct file_lock *fl;
+ int opcode __unused;
+{
+ CLIENT *cli;
+ static char dummy;
+ struct timeval timeo;
+ int success;
+ static struct nlm_res retval;
+ static struct nlm4_res retval4;
+
+ debuglog("About to send granted on blocked lock\n");
+
+ cli = get_client(fl->addr,
+ (fl->flags & LOCK_V4) ? NLM_VERS4 : NLM_VERS);
+ if (cli == NULL) {
+ syslog(LOG_NOTICE, "failed to get CLIENT for %s",
+ fl->client_name);
+ /*
+ * We fail to notify remote that the lock has been granted.
+ * The client will timeout and retry, the lock will be
+ * granted at this time.
+ */
+ return;
+ }
+ timeo.tv_sec = 0;
+ timeo.tv_usec = (fl->flags & LOCK_ASYNC) ? 0 : 500000; /* 0.5s */
+
+ if (fl->flags & LOCK_V4) {
+ static nlm4_testargs res;
+ res.cookie = fl->client_cookie;
+ res.exclusive = fl->client.exclusive;
+ res.alock.caller_name = fl->client_name;
+ res.alock.fh.n_len = sizeof(fhandle_t);
+ res.alock.fh.n_bytes = (char*)&fl->filehandle;
+ res.alock.oh = fl->client.oh;
+ res.alock.svid = fl->client.svid;
+ res.alock.l_offset = fl->client.l_offset;
+ res.alock.l_len = fl->client.l_len;
+ debuglog("sending v4 reply%s",
+ (fl->flags & LOCK_ASYNC) ? " (async)":"");
+ if (fl->flags & LOCK_ASYNC) {
+ success = clnt_call(cli, NLM4_GRANTED_MSG,
+ (xdrproc_t)xdr_nlm4_testargs, &res,
+ (xdrproc_t)xdr_void, &dummy, timeo);
+ } else {
+ success = clnt_call(cli, NLM4_GRANTED,
+ (xdrproc_t)xdr_nlm4_testargs, &res,
+ (xdrproc_t)xdr_nlm4_res, &retval4, timeo);
+ }
+ } else {
+ static nlm_testargs res;
+
+ res.cookie = fl->client_cookie;
+ res.exclusive = fl->client.exclusive;
+ res.alock.caller_name = fl->client_name;
+ res.alock.fh.n_len = sizeof(fhandle_t);
+ res.alock.fh.n_bytes = (char*)&fl->filehandle;
+ res.alock.oh = fl->client.oh;
+ res.alock.svid = fl->client.svid;
+ res.alock.l_offset = fl->client.l_offset;
+ res.alock.l_len = fl->client.l_len;
+ debuglog("sending v1 reply%s",
+ (fl->flags & LOCK_ASYNC) ? " (async)":"");
+ if (fl->flags & LOCK_ASYNC) {
+ success = clnt_call(cli, NLM_GRANTED_MSG,
+ (xdrproc_t)xdr_nlm_testargs, &res,
+ (xdrproc_t)xdr_void, &dummy, timeo);
+ } else {
+ success = clnt_call(cli, NLM_GRANTED,
+ (xdrproc_t)xdr_nlm_testargs, &res,
+ (xdrproc_t)xdr_nlm_res, &retval, timeo);
+ }
+ }
+ if (debug_level > 2)
+ debuglog("clnt_call returns %d(%s) for granted",
+ success, clnt_sperrno(success));
+
+}
+
+/*
+ * Routines below here have not been modified in the overhaul
+ */
+
+/*
+ * Are these two routines still required since lockd is not spawning off
+ * children to service locks anymore? Presumably they were originally
+ * put in place to prevent a one child from changing the lock list out
+ * from under another one.
+ */
+
+void
+siglock(void)
+{
+ sigset_t block;
+
+ sigemptyset(&block);
+ sigaddset(&block, SIGCHLD);
+
+ if (sigprocmask(SIG_BLOCK, &block, NULL) < 0) {
+ syslog(LOG_WARNING, "siglock failed: %s", strerror(errno));
+ }
+}
+
+void
+sigunlock(void)
+{
+ sigset_t block;
+
+ sigemptyset(&block);
+ sigaddset(&block, SIGCHLD);
+
+ if (sigprocmask(SIG_UNBLOCK, &block, NULL) < 0) {
+ syslog(LOG_WARNING, "sigunlock failed: %s", strerror(errno));
+ }
+}
diff --git a/usr.sbin/rpc.lockd/lockd_lock.h b/usr.sbin/rpc.lockd/lockd_lock.h
new file mode 100644
index 0000000..62f6981
--- /dev/null
+++ b/usr.sbin/rpc.lockd/lockd_lock.h
@@ -0,0 +1,25 @@
+/* $NetBSD: lockd_lock.h,v 1.2 2000/06/09 14:00:54 fvdl Exp $ */
+/* $FreeBSD$ */
+
+/* Headers and function declarations for file-locking utilities */
+
+struct nlm4_holder * testlock(struct nlm4_lock *lock, bool_t exclusive,
+ int flags);
+enum nlm_stats getlock(nlm4_lockargs *lckarg, struct svc_req *rqstp,
+ const int flags);
+enum nlm_stats unlock(nlm4_lock *lock, const int flags);
+int lock_answer(int pid, netobj *netcookie, int result, int *pid_p,
+ int version);
+
+void notify(const char *hostname, const int state);
+
+/* flags for testlock, getlock & unlock */
+#define LOCK_ASYNC 0x01 /* async version (getlock only) */
+#define LOCK_V4 0x02 /* v4 version */
+#define LOCK_MON 0x04 /* monitored lock (getlock only) */
+#define LOCK_CANCEL 0x08 /* cancel, not unlock request (unlock only) */
+
+/* callbacks from lock_proc.c */
+void transmit_result(int, nlm_res *, struct sockaddr *);
+void transmit4_result(int, nlm4_res *, struct sockaddr *);
+CLIENT *get_client(struct sockaddr *, rpcvers_t);
diff --git a/usr.sbin/rpc.lockd/rpc.lockd.8 b/usr.sbin/rpc.lockd/rpc.lockd.8
new file mode 100644
index 0000000..4fabe5d
--- /dev/null
+++ b/usr.sbin/rpc.lockd/rpc.lockd.8
@@ -0,0 +1,150 @@
+.\" $NetBSD: rpc.lockd.8,v 1.5 2000/06/09 18:51:47 cgd Exp $
+.\"
+.\" Copyright (c) 1995 A.R.Gordon, andrew.gordon@net-tel.co.uk
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 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 2, 2007
+.Dt RPC.LOCKD 8
+.Os
+.Sh NAME
+.Nm rpc.lockd
+.Nd NFS file locking daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl d Ar debug_level
+.Op Fl g Ar grace period
+.Op Fl h Ar bindip
+.Op Fl p Ar port
+.Sh DESCRIPTION
+The
+.Nm
+utility provides monitored and unmonitored file and record locking services
+in an NFS environment.
+To monitor the status of hosts requesting locks,
+the locking daemon typically operates in conjunction
+with
+.Xr rpc.statd 8 .
+.Pp
+Options and operands available for
+.Nm :
+.Bl -tag -width indent
+.It Fl d
+The
+.Fl d
+option causes debugging information to be written to syslog, recording
+all RPC transactions to the daemon.
+These messages are logged with level
+.Dv LOG_DEBUG
+and facility
+.Dv LOG_DAEMON .
+Specifying a
+.Ar debug_level
+of 1 results
+in the generation of one log line per protocol operation.
+Higher
+debug levels can be specified, causing display of operation arguments
+and internal operations of the daemon.
+.It Fl g
+The
+.Fl g
+option allow to specify the
+.Ar grace period ,
+in seconds.
+During the grace period
+.Nm
+only accepts requests from hosts which are reinitialising locks which
+existed before the server restart.
+Default is 30 seconds.
+.It Fl h Ar bindip
+Specify specific IP addresses to bind to.
+This option may be specified multiple times.
+If no
+.Fl h
+option is specified,
+.Nm
+will bind to
+.Dv INADDR_ANY .
+Note that when specifying IP addresses with
+.Fl h ,
+.Nm
+will automatically add
+.Li 127.0.0.1
+and if IPv6 is enabled,
+.Li ::1
+to the list.
+.It Fl p
+The
+.Fl p
+option allow to force the daemon to bind to the specified
+.Ar port ,
+for both AF_INET and AF_INET6 address families.
+.El
+.Pp
+Error conditions are logged to syslog, irrespective of the debug level,
+using log level
+.Dv LOG_ERR
+and facility
+.Dv LOG_DAEMON .
+.Pp
+The
+.Nm
+utility must NOT be invoked by
+.Xr inetd 8
+because the protocol assumes that the daemon will run from system start time.
+Instead, it should be configured in
+.Xr rc.conf 5
+to run at system startup.
+.Sh FILES
+.Bl -tag -width /usr/include/rpcsvc/nlm_prot.x -compact
+.It Pa /usr/include/rpcsvc/nlm_prot.x
+RPC protocol specification for the network lock manager protocol.
+.El
+.Sh SEE ALSO
+.Xr syslog 3 ,
+.Xr rc.conf 5 ,
+.Xr rpc.statd 8
+.Sh STANDARDS
+The implementation is based on the specification in
+.Rs
+.%B "X/Open CAE Specification C218"
+.%T "Protocols for X/Open PC Interworking: XNFS, Issue 4"
+.%O ISBN 1 872630 66 9
+.Re
+.Sh HISTORY
+A version of
+.Nm
+appeared in
+.Tn SunOS
+4.
+.Sh BUGS
+The current implementation serialises locks requests that could be shared.
diff --git a/usr.sbin/rpc.lockd/test.c b/usr.sbin/rpc.lockd/test.c
new file mode 100644
index 0000000..a751e5c
--- /dev/null
+++ b/usr.sbin/rpc.lockd/test.c
@@ -0,0 +1,365 @@
+/* $NetBSD: test.c,v 1.2 1997/10/18 04:01:21 lukem Exp $ */
+
+#include <sys/cdefs.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/nlm_prot.h>
+#ifndef lint
+#if 0
+static char sccsid[] = "from: @(#)nlm_prot.x 1.8 87/09/21 Copyr 1987 Sun Micro";
+static char sccsid[] = "from: * @(#)nlm_prot.x 2.1 88/08/01 4.0 RPCSRC";
+#else
+__RCSID("$NetBSD: test.c,v 1.2 1997/10/18 04:01:21 lukem Exp $");
+static const char rcsid[] = "$FreeBSD$";
+#endif
+#endif /* not lint */
+
+/* Default timeout can be changed using clnt_control() */
+static struct timeval TIMEOUT = { 0, 0 };
+
+nlm_testres *
+nlm_test_1(argp, clnt)
+ struct nlm_testargs *argp;
+ CLIENT *clnt;
+{
+ static nlm_testres res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_TEST, xdr_nlm_testargs, argp, xdr_nlm_testres, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+nlm_res *
+nlm_lock_1(argp, clnt)
+ struct nlm_lockargs *argp;
+ CLIENT *clnt;
+{
+ enum clnt_stat st;
+ static nlm_res res;
+
+ bzero((char *)&res, sizeof(res));
+ if (st = clnt_call(clnt, NLM_LOCK, xdr_nlm_lockargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) {
+ printf("clnt_call returns %d\n", st);
+ clnt_perror(clnt, "humbug");
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+nlm_res *
+nlm_cancel_1(argp, clnt)
+ struct nlm_cancargs *argp;
+ CLIENT *clnt;
+{
+ static nlm_res res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_CANCEL, xdr_nlm_cancargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+nlm_res *
+nlm_unlock_1(argp, clnt)
+ struct nlm_unlockargs *argp;
+ CLIENT *clnt;
+{
+ static nlm_res res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_UNLOCK, xdr_nlm_unlockargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+nlm_res *
+nlm_granted_1(argp, clnt)
+ struct nlm_testargs *argp;
+ CLIENT *clnt;
+{
+ static nlm_res res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_GRANTED, xdr_nlm_testargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+void *
+nlm_test_msg_1(argp, clnt)
+ struct nlm_testargs *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_TEST_MSG, xdr_nlm_testargs, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+void *
+nlm_lock_msg_1(argp, clnt)
+ struct nlm_lockargs *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_LOCK_MSG, xdr_nlm_lockargs, argp, xdr_void, NULL, TIMEOUT) != RPC_SUCCESS) {
+ clnt_perror(clnt, "nlm_lock_msg_1");
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+void *
+nlm_cancel_msg_1(argp, clnt)
+ struct nlm_cancargs *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_CANCEL_MSG, xdr_nlm_cancargs, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+void *
+nlm_unlock_msg_1(argp, clnt)
+ struct nlm_unlockargs *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_UNLOCK_MSG, xdr_nlm_unlockargs, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+void *
+nlm_granted_msg_1(argp, clnt)
+ struct nlm_testargs *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_GRANTED_MSG, xdr_nlm_testargs, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+void *
+nlm_test_res_1(argp, clnt)
+ nlm_testres *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_TEST_RES, xdr_nlm_testres, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+void *
+nlm_lock_res_1(argp, clnt)
+ nlm_res *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_LOCK_RES, xdr_nlm_res, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+void *
+nlm_cancel_res_1(argp, clnt)
+ nlm_res *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_CANCEL_RES, xdr_nlm_res, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+void *
+nlm_unlock_res_1(argp, clnt)
+ nlm_res *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_UNLOCK_RES, xdr_nlm_res, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+void *
+nlm_granted_res_1(argp, clnt)
+ nlm_res *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_GRANTED_RES, xdr_nlm_res, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+nlm_shareres *
+nlm_share_3(argp, clnt)
+ nlm_shareargs *argp;
+ CLIENT *clnt;
+{
+ static nlm_shareres res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_SHARE, xdr_nlm_shareargs, argp, xdr_nlm_shareres, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+nlm_shareres *
+nlm_unshare_3(argp, clnt)
+ nlm_shareargs *argp;
+ CLIENT *clnt;
+{
+ static nlm_shareres res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_UNSHARE, xdr_nlm_shareargs, argp, xdr_nlm_shareres, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+nlm_res *
+nlm_nm_lock_3(argp, clnt)
+ nlm_lockargs *argp;
+ CLIENT *clnt;
+{
+ static nlm_res res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_NM_LOCK, xdr_nlm_lockargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+void *
+nlm_free_all_3(argp, clnt)
+ nlm_notify *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_FREE_ALL, xdr_nlm_notify, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+int main(int argc, char **argv)
+{
+ CLIENT *cli;
+ nlm_res res_block;
+ nlm_res *out;
+ nlm_lockargs arg;
+ struct timeval tim;
+
+ printf("Creating client for host %s\n", argv[1]);
+ cli = clnt_create(argv[1], NLM_PROG, NLM_VERS, "udp");
+ if (!cli) {
+ errx(1, "Failed to create client\n");
+ /* NOTREACHED */
+ }
+ clnt_control(cli, CLGET_TIMEOUT, &tim);
+ printf("Default timeout was %d.%d\n", tim.tv_sec, tim.tv_usec);
+ tim.tv_usec = -1;
+ tim.tv_sec = -1;
+ clnt_control(cli, CLSET_TIMEOUT, &tim);
+ clnt_control(cli, CLGET_TIMEOUT, &tim);
+ printf("timeout now %d.%d\n", tim.tv_sec, tim.tv_usec);
+
+
+ arg.cookie.n_len = 4;
+ arg.cookie.n_bytes = "hello";
+ arg.block = 0;
+ arg.exclusive = 0;
+ arg.reclaim = 0;
+ arg.state = 0x1234;
+ arg.alock.caller_name = "localhost";
+ arg.alock.fh.n_len = 32;
+ arg.alock.fh.n_bytes = "\x04\x04\x02\x00\x01\x00\x00\x00\x0c\x00\x00\x00\xff\xff\xff\xd0\x16\x00\x00\x5b\x7c\xff\xff\xff\xec\x2f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x19\x54\xef\xbf\xd7\x94";
+ arg.alock.oh.n_len = 8;
+ arg.alock.oh.n_bytes = "\x00\x00\x02\xff\xff\xff\xd3";
+ arg.alock.svid = 0x5678;
+ arg.alock.l_offset = 0;
+ arg.alock.l_len = 100;
+
+ res_block.stat.stat = nlm_granted;
+ res_block.cookie.n_bytes = "hello";
+ res_block.cookie.n_len = 5;
+
+#if 0
+ if (nlm_lock_res_1(&res_block, cli))
+ printf("Success!\n");
+ else
+ printf("Fail\n");
+#else
+ if (out = nlm_lock_msg_1(&arg, cli)) {
+ printf("Success!\n");
+ printf("out->stat = %d", out->stat);
+ } else {
+ printf("Fail\n");
+ }
+#endif
+
+ return 0;
+}
diff --git a/usr.sbin/rpc.statd/Makefile b/usr.sbin/rpc.statd/Makefile
new file mode 100644
index 0000000..6d8053c
--- /dev/null
+++ b/usr.sbin/rpc.statd/Makefile
@@ -0,0 +1,27 @@
+# $FreeBSD$
+
+PROG= rpc.statd
+MAN= rpc.statd.8
+SRCS= file.c sm_inter_svc.c sm_inter.h statd.c procs.c
+
+CFLAGS+= -I.
+#WARNS?= 2
+
+DPADD= ${LIBRPCSVC}
+LDADD= -lrpcsvc
+
+CLEANFILES= sm_inter_svc.c sm_inter.h
+
+RPCSRC= ${DESTDIR}/usr/include/rpcsvc/sm_inter.x
+RPCGEN= rpcgen -L -C
+
+sm_inter_svc.c: ${RPCSRC}
+ ${RPCGEN} -m -o ${.TARGET} ${RPCSRC}
+
+sm_inter.h: ${RPCSRC}
+ ${RPCGEN} -h -o ${.TARGET} ${RPCSRC}
+
+test: test.c
+ cc -o test test.c -lrpcsvc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rpc.statd/file.c b/usr.sbin/rpc.statd/file.c
new file mode 100644
index 0000000..0625e30
--- /dev/null
+++ b/usr.sbin/rpc.statd/file.c
@@ -0,0 +1,361 @@
+/*
+ * Copyright (c) 1995
+ * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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
+ * 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 ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED 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 <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/mman.h> /* For mmap() */
+#include <rpc/rpc.h>
+#include <syslog.h>
+#include <stdlib.h>
+
+#include "statd.h"
+
+FileLayout *status_info; /* Pointer to the mmap()ed status file */
+static int status_fd; /* File descriptor for the open file */
+static off_t status_file_len; /* Current on-disc length of file */
+
+/* sync_file --------------------------------------------------------------- */
+/*
+ Purpose: Packaged call of msync() to flush changes to mmap()ed file
+ Returns: Nothing. Errors to syslog.
+*/
+
+void sync_file(void)
+{
+ if (msync((void *)status_info, 0, 0) < 0)
+ {
+ syslog(LOG_ERR, "msync() failed: %s", strerror(errno));
+ }
+}
+
+/* find_host -------------------------------------------------------------- */
+/*
+ Purpose: Find the entry in the status file for a given host
+ Returns: Pointer to that entry in the mmap() region, or NULL.
+ Notes: Also creates entries if requested.
+ Failure to create also returns NULL.
+*/
+
+HostInfo *find_host(char *hostname, int create)
+{
+ HostInfo *hp;
+ HostInfo *spare_slot = NULL;
+ HostInfo *result = NULL;
+ struct addrinfo *ai1, *ai2;
+ int i;
+
+ if (getaddrinfo(hostname, NULL, NULL, &ai1) != 0)
+ ai1 = NULL;
+ for (i = 0, hp = status_info->hosts; i < status_info->noOfHosts; i++, hp++)
+ {
+ if (!strncasecmp(hostname, hp->hostname, SM_MAXSTRLEN))
+ {
+ result = hp;
+ break;
+ }
+ if (hp->hostname[0] &&
+ getaddrinfo(hp->hostname, NULL, NULL, &ai2) != 0)
+ ai2 = NULL;
+ if (ai1 && ai2)
+ {
+ struct addrinfo *p1, *p2;
+ for (p1 = ai1; !result && p1; p1 = p1->ai_next)
+ {
+ for (p2 = ai2; !result && p2; p2 = p2->ai_next)
+ {
+ if (p1->ai_family == p2->ai_family
+ && p1->ai_addrlen == p2->ai_addrlen
+ && !memcmp(p1->ai_addr, p2->ai_addr, p1->ai_addrlen))
+ {
+ result = hp;
+ break;
+ }
+ }
+ }
+ if (result)
+ break;
+ }
+ if (ai2)
+ freeaddrinfo(ai2);
+ if (!spare_slot && !hp->monList && !hp->notifyReqd)
+ spare_slot = hp;
+ }
+ if (ai1)
+ freeaddrinfo(ai1);
+
+ /* Return if entry found, or if not asked to create one. */
+ if (result || !create) return (result);
+
+ /* Now create an entry, using the spare slot if one was found or */
+ /* adding to the end of the list otherwise, extending file if reqd */
+ if (!spare_slot)
+ {
+ off_t desired_size;
+ spare_slot = &status_info->hosts[status_info->noOfHosts];
+ desired_size = ((char*)spare_slot - (char*)status_info) + sizeof(HostInfo);
+ if (desired_size > status_file_len)
+ {
+ /* Extend file by writing 1 byte of junk at the desired end pos */
+ lseek(status_fd, desired_size - 1, SEEK_SET);
+ i = write(status_fd, &i, 1);
+ if (i < 1)
+ {
+ syslog(LOG_ERR, "Unable to extend status file");
+ return (NULL);
+ }
+ status_file_len = desired_size;
+ }
+ status_info->noOfHosts++;
+ }
+
+ /* Initialise the spare slot that has been found/created */
+ /* Note that we do not msync(), since the caller is presumed to be */
+ /* about to modify the entry further */
+ memset(spare_slot, 0, sizeof(HostInfo));
+ strncpy(spare_slot->hostname, hostname, SM_MAXSTRLEN);
+ return (spare_slot);
+}
+
+/* init_file -------------------------------------------------------------- */
+/*
+ Purpose: Open file, create if necessary, initialise it.
+ Returns: Nothing - exits on error
+ Notes: Called before process becomes daemon, hence logs to
+ stderr rather than syslog.
+ Opens the file, then mmap()s it for ease of access.
+ Also performs initial clean-up of the file, zeroing
+ monitor list pointers, setting the notifyReqd flag in
+ all hosts that had a monitor list, and incrementing
+ the state number to the next even value.
+*/
+
+void init_file(const char *filename)
+{
+ int new_file = FALSE;
+ char buf[HEADER_LEN];
+ int i;
+
+ /* try to open existing file - if not present, create one */
+ status_fd = open(filename, O_RDWR);
+ if ((status_fd < 0) && (errno == ENOENT))
+ {
+ status_fd = open(filename, O_RDWR | O_CREAT, 0644);
+ new_file = TRUE;
+ }
+ if (status_fd < 0)
+ errx(1, "unable to open status file %s", filename);
+
+ /* File now open. mmap() it, with a generous size to allow for */
+ /* later growth, where we will extend the file but not re-map it. */
+ status_info = (FileLayout *)
+ mmap(NULL, 0x10000000, PROT_READ | PROT_WRITE, MAP_SHARED, status_fd, 0);
+
+ if (status_info == (FileLayout *) MAP_FAILED)
+ err(1, "unable to mmap() status file");
+
+ status_file_len = lseek(status_fd, 0L, SEEK_END);
+
+ /* If the file was not newly created, validate the contents, and if */
+ /* defective, re-create from scratch. */
+ if (!new_file)
+ {
+ if ((status_file_len < HEADER_LEN) || (status_file_len
+ < (HEADER_LEN + sizeof(HostInfo) * status_info->noOfHosts)) )
+ {
+ warnx("status file is corrupt");
+ new_file = TRUE;
+ }
+ }
+
+ /* Initialisation of a new, empty file. */
+ if (new_file)
+ {
+ memset(buf, 0, sizeof(buf));
+ lseek(status_fd, 0L, SEEK_SET);
+ write(status_fd, buf, HEADER_LEN);
+ status_file_len = HEADER_LEN;
+ }
+ else
+ {
+ /* Clean-up of existing file - monitored hosts will have a pointer */
+ /* to a list of clients, which refers to memory in the previous */
+ /* incarnation of the program and so are meaningless now. These */
+ /* pointers are zeroed and the fact that the host was previously */
+ /* monitored is recorded by setting the notifyReqd flag, which will */
+ /* in due course cause a SM_NOTIFY to be sent. */
+ /* Note that if we crash twice in quick succession, some hosts may */
+ /* already have notifyReqd set, where we didn't manage to notify */
+ /* them before the second crash occurred. */
+ for (i = 0; i < status_info->noOfHosts; i++)
+ {
+ HostInfo *this_host = &status_info->hosts[i];
+
+ if (this_host->monList)
+ {
+ this_host->notifyReqd = TRUE;
+ this_host->monList = NULL;
+ }
+ }
+ /* Select the next higher even number for the state counter */
+ status_info->ourState = (status_info->ourState + 2) & 0xfffffffe;
+/*???????******/ status_info->ourState++;
+ }
+}
+
+/* notify_one_host --------------------------------------------------------- */
+/*
+ Purpose: Perform SM_NOTIFY procedure at specified host
+ Returns: TRUE if success, FALSE if failed.
+*/
+
+static int notify_one_host(char *hostname)
+{
+ struct timeval timeout = { 20, 0 }; /* 20 secs timeout */
+ CLIENT *cli;
+ char dummy;
+ stat_chge arg;
+ char our_hostname[SM_MAXSTRLEN+1];
+
+ gethostname(our_hostname, sizeof(our_hostname));
+ our_hostname[SM_MAXSTRLEN] = '\0';
+ arg.mon_name = our_hostname;
+ arg.state = status_info->ourState;
+
+ if (debug) syslog (LOG_DEBUG, "Sending SM_NOTIFY to host %s from %s", hostname, our_hostname);
+
+ cli = clnt_create(hostname, SM_PROG, SM_VERS, "udp");
+ if (!cli)
+ {
+ syslog(LOG_ERR, "Failed to contact host %s%s", hostname,
+ clnt_spcreateerror(""));
+ return (FALSE);
+ }
+
+ if (clnt_call(cli, SM_NOTIFY, (xdrproc_t)xdr_stat_chge, &arg,
+ (xdrproc_t)xdr_void, &dummy, timeout)
+ != RPC_SUCCESS)
+ {
+ syslog(LOG_ERR, "Failed to contact rpc.statd at host %s", hostname);
+ clnt_destroy(cli);
+ return (FALSE);
+ }
+
+ clnt_destroy(cli);
+ return (TRUE);
+}
+
+/* notify_hosts ------------------------------------------------------------ */
+/*
+ Purpose: Send SM_NOTIFY to all hosts marked as requiring it
+ Returns: Nothing, immediately - forks a process to do the work.
+ Notes: Does nothing if there are no monitored hosts.
+ Called after all the initialisation has been done -
+ logs to syslog.
+*/
+
+void notify_hosts(void)
+{
+ int i;
+ int attempts;
+ int work_to_do = FALSE;
+ HostInfo *hp;
+ pid_t pid;
+
+ /* First check if there is in fact any work to do. */
+ for (i = status_info->noOfHosts, hp = status_info->hosts; i ; i--, hp++)
+ {
+ if (hp->notifyReqd)
+ {
+ work_to_do = TRUE;
+ break;
+ }
+ }
+
+ if (!work_to_do) return; /* No work found */
+
+ pid = fork();
+ if (pid == -1)
+ {
+ syslog(LOG_ERR, "Unable to fork notify process - %s", strerror(errno));
+ return;
+ }
+ if (pid) return;
+
+ /* Here in the child process. We continue until all the hosts marked */
+ /* as requiring notification have been duly notified. */
+ /* If one of the initial attempts fails, we sleep for a while and */
+ /* have another go. This is necessary because when we have crashed, */
+ /* (eg. a power outage) it is quite possible that we won't be able to */
+ /* contact all monitored hosts immediately on restart, either because */
+ /* they crashed too and take longer to come up (in which case the */
+ /* notification isn't really required), or more importantly if some */
+ /* router etc. needed to reach the monitored host has not come back */
+ /* up yet. In this case, we will be a bit late in re-establishing */
+ /* locks (after the grace period) but that is the best we can do. */
+ /* We try 10 times at 5 sec intervals, 10 more times at 1 minute */
+ /* intervals, then 24 more times at hourly intervals, finally */
+ /* giving up altogether if the host hasn't come back to life after */
+ /* 24 hours. */
+
+ for (attempts = 0; attempts < 44; attempts++)
+ {
+ work_to_do = FALSE; /* Unless anything fails */
+ for (i = status_info->noOfHosts, hp = status_info->hosts; i ; i--, hp++)
+ {
+ if (hp->notifyReqd)
+ {
+ if (notify_one_host(hp->hostname))
+ {
+ hp->notifyReqd = FALSE;
+ sync_file();
+ }
+ else work_to_do = TRUE;
+ }
+ }
+ if (!work_to_do) break;
+ if (attempts < 10) sleep(5);
+ else if (attempts < 20) sleep(60);
+ else sleep(60*60);
+ }
+ exit(0);
+}
+
+
diff --git a/usr.sbin/rpc.statd/procs.c b/usr.sbin/rpc.statd/procs.c
new file mode 100644
index 0000000..f6067e7
--- /dev/null
+++ b/usr.sbin/rpc.statd/procs.c
@@ -0,0 +1,436 @@
+/*
+ * Copyright (c) 1995
+ * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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
+ * 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 ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED 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 <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <rpc/rpc.h>
+#include <syslog.h>
+#include <vis.h>
+#include <netdb.h> /* for getaddrinfo() */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "statd.h"
+
+static const char *
+from_addr(saddr)
+ struct sockaddr *saddr;
+{
+ static char inet_buf[INET6_ADDRSTRLEN];
+
+ if (getnameinfo(saddr, saddr->sa_len, inet_buf, sizeof(inet_buf),
+ NULL, 0, NI_NUMERICHOST) == 0)
+ return inet_buf;
+ return "???";
+}
+
+/* sm_check_hostname -------------------------------------------------------- */
+/*
+ * Purpose: Check `mon_name' member of sm_name struct to ensure that the array
+ * consists only of printable characters.
+ *
+ * Returns: TRUE if hostname is good. FALSE if hostname contains binary or
+ * otherwise non-printable characters.
+ *
+ * Notes: Will syslog(3) to warn of corrupt hostname.
+ */
+
+int sm_check_hostname(struct svc_req *req, char *arg)
+{
+ int len, dstlen, ret;
+ struct sockaddr *claddr;
+ char *dst;
+
+ len = strlen(arg);
+ dstlen = (4 * len) + 1;
+ dst = malloc(dstlen);
+ claddr = (struct sockaddr *) (svc_getrpccaller(req->rq_xprt)->buf) ;
+ ret = 1;
+
+ if (claddr == NULL || dst == NULL)
+ {
+ ret = 0;
+ }
+ else if (strvis(dst, arg, VIS_WHITE) != len)
+ {
+ syslog(LOG_ERR,
+ "sm_stat: client %s hostname %s contained invalid characters.",
+ from_addr(claddr),
+ dst);
+ ret = 0;
+ }
+ free(dst);
+ return (ret);
+}
+
+/* sm_stat_1 --------------------------------------------------------------- */
+/*
+ Purpose: RPC call to enquire if a host can be monitored
+ Returns: TRUE for any hostname that can be looked up to give
+ an address.
+*/
+
+struct sm_stat_res *sm_stat_1_svc(sm_name *arg, struct svc_req *req)
+{
+ static sm_stat_res res;
+ struct addrinfo *ai;
+ struct sockaddr *claddr;
+ static int err;
+
+ err = 1;
+ if ((err = sm_check_hostname(req, arg->mon_name)) == 0)
+ {
+ res.res_stat = stat_fail;
+ }
+ if (err != 0)
+ {
+ if (debug)
+ syslog(LOG_DEBUG, "stat called for host %s", arg->mon_name);
+ if (getaddrinfo(arg->mon_name, NULL, NULL, &ai) == 0) {
+ res.res_stat = stat_succ;
+ freeaddrinfo(ai);
+ }
+ else
+ {
+ claddr = (struct sockaddr *) (svc_getrpccaller(req->rq_xprt)->buf) ;
+ syslog(LOG_ERR, "invalid hostname to sm_stat from %s: %s",
+ from_addr(claddr), arg->mon_name);
+ res.res_stat = stat_fail;
+ }
+ }
+ res.state = status_info->ourState;
+ return (&res);
+}
+
+/* sm_mon_1 ---------------------------------------------------------------- */
+/*
+ Purpose: RPC procedure to establish a monitor request
+ Returns: Success, unless lack of resources prevents
+ the necessary structures from being set up
+ to record the request, or if the hostname is not
+ valid (as judged by getaddrinfo())
+*/
+
+struct sm_stat_res *sm_mon_1_svc(mon *arg, struct svc_req *req)
+{
+ static sm_stat_res res;
+ HostInfo *hp;
+ static int err;
+ MonList *lp;
+ struct addrinfo *ai;
+
+ if ((err = sm_check_hostname(req, arg->mon_id.mon_name)) == 0)
+ {
+ res.res_stat = stat_fail;
+ }
+
+ if (err != 0)
+ {
+ if (debug)
+ {
+ syslog(LOG_DEBUG, "monitor request for host %s", arg->mon_id.mon_name);
+ syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d",
+ arg->mon_id.my_id.my_name,
+ arg->mon_id.my_id.my_prog,
+ arg->mon_id.my_id.my_vers,
+ arg->mon_id.my_id.my_proc);
+ }
+ res.res_stat = stat_fail; /* Assume fail until set otherwise */
+ res.state = status_info->ourState;
+
+ /* Find existing host entry, or create one if not found */
+ /* If find_host() fails, it will have logged the error already. */
+ if (getaddrinfo(arg->mon_id.mon_name, NULL, NULL, &ai) != 0)
+ {
+ syslog(LOG_ERR, "Invalid hostname to sm_mon: %s", arg->mon_id.mon_name);
+ return (&res);
+ }
+ freeaddrinfo(ai);
+ if ((hp = find_host(arg->mon_id.mon_name, TRUE)))
+ {
+ lp = (MonList *)malloc(sizeof(MonList));
+ if (!lp)
+ {
+ syslog(LOG_ERR, "Out of memory");
+ }
+ else
+ {
+ strncpy(lp->notifyHost, arg->mon_id.my_id.my_name, SM_MAXSTRLEN);
+ lp->notifyProg = arg->mon_id.my_id.my_prog;
+ lp->notifyVers = arg->mon_id.my_id.my_vers;
+ lp->notifyProc = arg->mon_id.my_id.my_proc;
+ memcpy(lp->notifyData, arg->priv, sizeof(lp->notifyData));
+
+ lp->next = hp->monList;
+ hp->monList = lp;
+ sync_file();
+
+ res.res_stat = stat_succ; /* Report success */
+ }
+ }
+ }
+ return (&res);
+}
+
+/* do_unmon ---------------------------------------------------------------- */
+/*
+ Purpose: Remove a monitor request from a host
+ Returns: TRUE if found, FALSE if not found.
+ Notes: Common code from sm_unmon_1_svc and sm_unmon_all_1_svc
+ In the unlikely event of more than one identical monitor
+ request, all are removed.
+*/
+
+static int do_unmon(HostInfo *hp, my_id *idp)
+{
+ MonList *lp, *next;
+ MonList *last = NULL;
+ int result = FALSE;
+
+ lp = hp->monList;
+ while (lp)
+ {
+ if (!strncasecmp(idp->my_name, lp->notifyHost, SM_MAXSTRLEN)
+ && (idp->my_prog == lp->notifyProg) && (idp->my_proc == lp->notifyProc)
+ && (idp->my_vers == lp->notifyVers))
+ {
+ /* found one. Unhook from chain and free. */
+ next = lp->next;
+ if (last) last->next = next;
+ else hp->monList = next;
+ free(lp);
+ lp = next;
+ result = TRUE;
+ }
+ else
+ {
+ last = lp;
+ lp = lp->next;
+ }
+ }
+ return (result);
+}
+
+/* sm_unmon_1 -------------------------------------------------------------- */
+/*
+ Purpose: RPC procedure to release a monitor request.
+ Returns: Local machine's status number
+ Notes: The supplied mon_id should match the value passed in an
+ earlier call to sm_mon_1
+*/
+
+struct sm_stat *sm_unmon_1_svc(mon_id *arg, struct svc_req *req __unused)
+{
+ static sm_stat res;
+ HostInfo *hp;
+
+ if (debug)
+ {
+ syslog(LOG_DEBUG, "un-monitor request for host %s", arg->mon_name);
+ syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d",
+ arg->mon_name,
+ arg->my_id.my_prog, arg->my_id.my_vers, arg->my_id.my_proc);
+ }
+
+ if ((hp = find_host(arg->mon_name, FALSE)))
+ {
+ if (do_unmon(hp, &arg->my_id)) sync_file();
+ else
+ {
+ syslog(LOG_ERR, "unmon request from %s, no matching monitor",
+ arg->my_id.my_name);
+ }
+ }
+ else syslog(LOG_ERR, "unmon request from %s for unknown host %s",
+ arg->my_id.my_name, arg->mon_name);
+
+ res.state = status_info->ourState;
+
+ return (&res);
+}
+
+/* sm_unmon_all_1 ---------------------------------------------------------- */
+/*
+ Purpose: RPC procedure to release monitor requests.
+ Returns: Local machine's status number
+ Notes: Releases all monitor requests (if any) from the specified
+ host and program number.
+*/
+
+struct sm_stat *sm_unmon_all_1_svc(my_id *arg, struct svc_req *req __unused)
+{
+ static sm_stat res;
+ HostInfo *hp;
+ int i;
+
+ if (debug)
+ {
+ syslog(LOG_DEBUG, "unmon_all for host: %s prog: %d ver: %d proc: %d",
+ arg->my_name, arg->my_prog, arg->my_vers, arg->my_proc);
+ }
+
+ for (i = status_info->noOfHosts, hp = status_info->hosts; i; i--, hp++)
+ {
+ do_unmon(hp, arg);
+ }
+ sync_file();
+
+ res.state = status_info->ourState;
+
+ return (&res);
+}
+
+/* sm_simu_crash_1 --------------------------------------------------------- */
+/*
+ Purpose: RPC procedure to simulate a crash
+ Returns: Nothing
+ Notes: Standardised mechanism for debug purposes
+ The specification says that we should drop all of our
+ status information (apart from the list of monitored hosts
+ on disc). However, this would confuse the rpc.lockd
+ which would be unaware that all of its monitor requests
+ had been silently junked. Hence we in fact retain all
+ current requests and simply increment the status counter
+ and inform all hosts on the monitor list.
+*/
+
+void *sm_simu_crash_1_svc(void *v __unused, struct svc_req *req __unused)
+{
+ static char dummy;
+ int work_to_do;
+ HostInfo *hp;
+ int i;
+
+ work_to_do = FALSE;
+ if (debug) syslog(LOG_DEBUG, "simu_crash called!!");
+
+ /* Simulate crash by setting notify-required flag on all monitored */
+ /* hosts, and incrementing our status number. notify_hosts() is */
+ /* then called to fork a process to do the notifications. */
+
+ for (i = status_info->noOfHosts, hp = status_info->hosts; i ; i--, hp++)
+ {
+ if (hp->monList)
+ {
+ work_to_do = TRUE;
+ hp->notifyReqd = TRUE;
+ }
+ }
+ status_info->ourState += 2; /* always even numbers if not crashed */
+
+ if (work_to_do) notify_hosts();
+
+ return (&dummy);
+}
+
+/* sm_notify_1 ------------------------------------------------------------- */
+/*
+ Purpose: RPC procedure notifying local statd of the crash of another
+ Returns: Nothing
+ Notes: There is danger of deadlock, since it is quite likely that
+ the client procedure that we call will in turn call us
+ to remove or adjust the monitor request.
+ We therefore fork() a process to do the notifications.
+ Note that the main HostInfo structure is in a mmap()
+ region and so will be shared with the child, but the
+ monList pointed to by the HostInfo is in normal memory.
+ Hence if we read the monList before forking, we are
+ protected from the parent servicing other requests
+ that modify the list.
+*/
+
+void *sm_notify_1_svc(stat_chge *arg, struct svc_req *req __unused)
+{
+ struct timeval timeout = { 20, 0 }; /* 20 secs timeout */
+ CLIENT *cli;
+ static char dummy;
+ sm_status tx_arg; /* arg sent to callback procedure */
+ MonList *lp;
+ HostInfo *hp;
+ pid_t pid;
+
+ if (debug) syslog(LOG_DEBUG, "notify from host %s, new state %d",
+ arg->mon_name, arg->state);
+
+ hp = find_host(arg->mon_name, FALSE);
+ if (!hp)
+ {
+ /* Never heard of this host - why is it notifying us? */
+ syslog(LOG_ERR, "Unsolicited notification from host %s", arg->mon_name);
+ return (&dummy);
+ }
+ lp = hp->monList;
+ if (!lp) return (&dummy); /* We know this host, but have no */
+ /* outstanding requests. */
+ pid = fork();
+ if (pid == -1)
+ {
+ syslog(LOG_ERR, "Unable to fork notify process - %s", strerror(errno));
+ return (NULL); /* no answer, the client will retry */
+ }
+ if (pid) return (&dummy); /* Parent returns */
+
+ while (lp)
+ {
+ tx_arg.mon_name = arg->mon_name;
+ tx_arg.state = arg->state;
+ memcpy(tx_arg.priv, lp->notifyData, sizeof(tx_arg.priv));
+ cli = clnt_create(lp->notifyHost, lp->notifyProg, lp->notifyVers, "udp");
+ if (!cli)
+ {
+ syslog(LOG_ERR, "Failed to contact host %s%s", lp->notifyHost,
+ clnt_spcreateerror(""));
+ }
+ else
+ {
+ if (clnt_call(cli, lp->notifyProc, (xdrproc_t)xdr_sm_status, &tx_arg,
+ (xdrproc_t)xdr_void, &dummy, timeout) != RPC_SUCCESS)
+ {
+ syslog(LOG_ERR, "Failed to call rpc.statd client at host %s",
+ lp->notifyHost);
+ }
+ clnt_destroy(cli);
+ }
+ lp = lp->next;
+ }
+
+ exit (0); /* Child quits */
+}
diff --git a/usr.sbin/rpc.statd/rpc.statd.8 b/usr.sbin/rpc.statd/rpc.statd.8
new file mode 100644
index 0000000..5b53ff1
--- /dev/null
+++ b/usr.sbin/rpc.statd/rpc.statd.8
@@ -0,0 +1,141 @@
+.\" -*- nroff -*-
+.\"
+.\" Copyright (c) 1995 A.R.Gordon, andrew.gordon@net-tel.co.uk
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 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 1, 2007
+.Dt RPC.STATD 8
+.Os
+.Sh NAME
+.Nm rpc.statd
+.Nd host status monitoring daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl h Ar bindip
+.Op Fl p Ar port
+.Sh DESCRIPTION
+The
+.Nm
+utility
+is a daemon which co-operates with
+.Nm
+daemons on other hosts to provide
+a status monitoring service.
+The daemon accepts requests from
+programs running on the local host (typically,
+.Xr rpc.lockd 8 ,
+the NFS file locking daemon) to monitor the status of specified
+hosts.
+If a monitored host crashes and restarts, the remote daemon will
+notify the local daemon, which in turn will notify the local program(s)
+which requested the monitoring service.
+Conversely, if this host crashes
+and re-starts, when the
+.Nm
+re-starts, it will notify all of the hosts which were being monitored
+at the time of the crash.
+.Pp
+The following option is available:
+.Bl -tag -width indent
+.It Fl d
+Cause debugging information to be written to syslog, recording
+all RPC transactions to the daemon.
+These messages are logged with level
+LOG_DEBUG and facility LOG_DAEMON.
+Error conditions are logged irrespective
+of this option, using level LOG_ERR.
+.It Fl h Ar bindip
+Specify specific IP addresses to bind to.
+This option may be specified multiple times.
+If no
+.Fl h
+option is specified,
+.Nm
+will bind to
+.Dv INADDR_ANY .
+Note that when specifying IP addresses with
+.Fl h ,
+.Nm
+will automatically add
+.Li 127.0.0.1
+and if IPv6 is enabled,
+.Li ::1
+to the list.
+.It Fl p
+The
+.Fl p
+option allow to force the daemon to bind to the specified
+.Ar port ,
+for both AF_INET and AF_INET6 address families.
+.El
+.Pp
+The
+.Nm
+utility must NOT be invoked by
+.Xr inetd 8
+because the protocol assumes that the daemon will run from system start time.
+Instead, it should be run from
+.Xr rc 8
+after the network has been started.
+.Sh FILES
+.Bl -tag -width /usr/include/rpcsvc/sm_inter.x -compact
+.It Pa /var/db/statd.status
+non-volatile record of currently monitored hosts.
+.It Pa /usr/include/rpcsvc/sm_inter.x
+RPC protocol specification used by local applications to register monitoring requests.
+.El
+.Sh SEE ALSO
+.Xr syslog 3 ,
+.Xr rc 8 ,
+.Xr rpc.lockd 8
+.Sh STANDARDS
+The implementation is based on the specification in X/Open CAE Specification
+C218, "Protocols for X/Open PC Interworking: XNFS, Issue 4", ISBN 1 872630 66 9
+.Sh BUGS
+There is no means for the daemon to tell when a monitored host has
+disappeared permanently (e.g.\& catastrophic hardware failure), as opposed
+to transient failure of the host or an intermediate router.
+At present,
+it will re-try notification attempts at frequent intervals for 10 minutes,
+then hourly, and finally gives up after 24 hours.
+.Pp
+The protocol requires that symmetric monitor requests are made to both
+the local and remote daemon in order to establish a monitored relationship.
+This is convenient for the NFS locking protocol, but probably reduces the
+usefulness of the monitoring system for other applications.
+.Pp
+The current implementation uses more than 1Kbyte per monitored host in
+the status file (and also in VM).
+This may be inefficient for NFS servers
+with large numbers of clients.
diff --git a/usr.sbin/rpc.statd/statd.c b/usr.sbin/rpc.statd/statd.c
new file mode 100644
index 0000000..b8b4311
--- /dev/null
+++ b/usr.sbin/rpc.statd/statd.c
@@ -0,0 +1,471 @@
+/*
+ * Copyright (c) 1995
+ * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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
+ * 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 ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED 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.
+ *
+ */
+
+/* main() function for status monitor daemon. Some of the code in this */
+/* file was generated by running rpcgen /usr/include/rpcsvc/sm_inter.x */
+/* The actual program logic is in the file procs.c */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <rpc/rpc.h>
+#include <rpc/rpc_com.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <signal.h>
+#include <unistd.h>
+#include "statd.h"
+
+int debug = 0; /* Controls syslog() calls for debug messages */
+
+char **hosts, *svcport_str = NULL;
+int nhosts = 0;
+int xcreated = 0;
+
+void create_service(struct netconfig *nconf);
+static void handle_sigchld(int sig);
+void out_of_mem(void);
+
+static void usage(void);
+
+int
+main(int argc, char **argv)
+{
+ struct sigaction sa;
+ struct netconfig *nconf;
+ void *nc_handle;
+ in_port_t svcport;
+ int ch, i, s;
+ char *endptr, **hosts_bak;
+ int have_v6 = 1;
+ int maxrec = RPC_MAXDATASIZE;
+
+ while ((ch = getopt(argc, argv, "dh:p:")) != -1)
+ switch (ch) {
+ case 'd':
+ debug = 1;
+ break;
+ case 'h':
+ ++nhosts;
+ hosts_bak = hosts;
+ hosts_bak = realloc(hosts, nhosts * sizeof(char *));
+ if (hosts_bak == NULL) {
+ if (hosts != NULL) {
+ for (i = 0; i < nhosts; i++)
+ free(hosts[i]);
+ free(hosts);
+ out_of_mem();
+ }
+ }
+ hosts = hosts_bak;
+ hosts[nhosts - 1] = strdup(optarg);
+ if (hosts[nhosts - 1] == NULL) {
+ for (i = 0; i < (nhosts - 1); i++)
+ free(hosts[i]);
+ free(hosts);
+ out_of_mem();
+ }
+ break;
+ case 'p':
+ endptr = NULL;
+ svcport = (in_port_t)strtoul(optarg, &endptr, 10);
+ if (endptr == NULL || *endptr != '\0' || svcport == 0 ||
+ svcport >= IPPORT_MAX)
+ usage();
+
+ svcport_str = strdup(optarg);
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ (void)rpcb_unset(SM_PROG, SM_VERS, NULL);
+
+ /*
+ * Check if IPv6 support is present.
+ */
+ s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+ if (s < 0)
+ have_v6 = 0;
+ else
+ close(s);
+
+ rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
+
+ /*
+ * If no hosts were specified, add a wildcard entry to bind to
+ * INADDR_ANY. Otherwise make sure 127.0.0.1 and ::1 are added to the
+ * list.
+ */
+ if (nhosts == 0) {
+ hosts = malloc(sizeof(char**));
+ if (hosts == NULL)
+ out_of_mem();
+
+ hosts[0] = "*";
+ nhosts = 1;
+ } else {
+ hosts_bak = hosts;
+ if (have_v6) {
+ hosts_bak = realloc(hosts, (nhosts + 2) *
+ sizeof(char *));
+ if (hosts_bak == NULL) {
+ for (i = 0; i < nhosts; i++)
+ free(hosts[i]);
+ free(hosts);
+ out_of_mem();
+ } else
+ hosts = hosts_bak;
+
+ nhosts += 2;
+ hosts[nhosts - 2] = "::1";
+ } else {
+ hosts_bak = realloc(hosts, (nhosts + 1) * sizeof(char *));
+ if (hosts_bak == NULL) {
+ for (i = 0; i < nhosts; i++)
+ free(hosts[i]);
+
+ free(hosts);
+ out_of_mem();
+ } else {
+ nhosts += 1;
+ hosts = hosts_bak;
+ }
+ }
+ hosts[nhosts - 1] = "127.0.0.1";
+ }
+
+ nc_handle = setnetconfig();
+ while ((nconf = getnetconfig(nc_handle))) {
+ /* We want to listen only on udp6, tcp6, udp, tcp transports */
+ if (nconf->nc_flag & NC_VISIBLE) {
+ /* Skip if there's no IPv6 support */
+ if (have_v6 == 0 && strcmp(nconf->nc_protofmly, "inet6") == 0) {
+ /* DO NOTHING */
+ } else {
+ create_service(nconf);
+ }
+ }
+ }
+ endnetconfig(nc_handle);
+ init_file("/var/db/statd.status");
+
+ /* Note that it is NOT sensible to run this program from inetd - the */
+ /* protocol assumes that it will run immediately at boot time. */
+ daemon(0, 0);
+ openlog("rpc.statd", 0, LOG_DAEMON);
+ if (debug) syslog(LOG_INFO, "Starting - debug enabled");
+ else syslog(LOG_INFO, "Starting");
+
+ /* Install signal handler to collect exit status of child processes */
+ sa.sa_handler = handle_sigchld;
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, SIGCHLD);
+ sa.sa_flags = SA_RESTART;
+ sigaction(SIGCHLD, &sa, NULL);
+
+ /* Initialisation now complete - start operating */
+ notify_hosts(); /* Forks a process (if necessary) to do the */
+ /* SM_NOTIFY calls, which may be slow. */
+
+ svc_run(); /* Should never return */
+ exit(1);
+}
+
+/*
+ * This routine creates and binds sockets on the appropriate
+ * addresses. It gets called one time for each transport and
+ * registrates the service with rpcbind on that trasport.
+ */
+void
+create_service(struct netconfig *nconf)
+{
+ struct addrinfo hints, *res = NULL;
+ struct sockaddr_in *sin;
+ struct sockaddr_in6 *sin6;
+ struct __rpc_sockinfo si;
+ struct netbuf servaddr;
+ SVCXPRT *transp = NULL;
+ int aicode;
+ int fd;
+ int nhostsbak;
+ int r;
+ int registered = 0;
+ u_int32_t host_addr[4]; /* IPv4 or IPv6 */
+
+ if ((nconf->nc_semantics != NC_TPI_CLTS) &&
+ (nconf->nc_semantics != NC_TPI_COTS) &&
+ (nconf->nc_semantics != NC_TPI_COTS_ORD))
+ return; /* not my type */
+
+ /*
+ * XXX - using RPC library internal functions.
+ */
+ if (!__rpc_nconf2sockinfo(nconf, &si)) {
+ syslog(LOG_ERR, "cannot get information for %s",
+ nconf->nc_netid);
+ return;
+ }
+
+ /* Get rpc.statd's address on this transport */
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = si.si_af;
+ hints.ai_socktype = si.si_socktype;
+ hints.ai_protocol = si.si_proto;
+
+ /*
+ * Bind to specific IPs if asked to
+ */
+ nhostsbak = nhosts;
+ while (nhostsbak > 0) {
+ --nhostsbak;
+
+ /*
+ * XXX - using RPC library internal functions.
+ */
+ if ((fd = __rpc_nconf2fd(nconf)) < 0) {
+ syslog(LOG_ERR, "cannot create socket for %s",
+ nconf->nc_netid);
+ continue;
+ }
+ switch (hints.ai_family) {
+ case AF_INET:
+ if (inet_pton(AF_INET, hosts[nhostsbak],
+ host_addr) == 1) {
+ hints.ai_flags &= AI_NUMERICHOST;
+ } else {
+ /*
+ * Skip if we have an AF_INET6 address.
+ */
+ if (inet_pton(AF_INET6, hosts[nhostsbak],
+ host_addr) == 1) {
+ close(fd);
+ continue;
+ }
+ }
+ break;
+ case AF_INET6:
+ if (inet_pton(AF_INET6, hosts[nhostsbak],
+ host_addr) == 1) {
+ hints.ai_flags &= AI_NUMERICHOST;
+ } else {
+ /*
+ * Skip if we have an AF_INET address.
+ */
+ if (inet_pton(AF_INET, hosts[nhostsbak],
+ host_addr) == 1) {
+ close(fd);
+ continue;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * If no hosts were specified, just bind to INADDR_ANY
+ */
+ if (strcmp("*", hosts[nhostsbak]) == 0) {
+ if (svcport_str == NULL) {
+ res = malloc(sizeof(struct addrinfo));
+ if (res == NULL)
+ out_of_mem();
+ res->ai_flags = hints.ai_flags;
+ res->ai_family = hints.ai_family;
+ res->ai_protocol = hints.ai_protocol;
+ switch (res->ai_family) {
+ case AF_INET:
+ sin = malloc(sizeof(struct sockaddr_in));
+ if (sin == NULL)
+ out_of_mem();
+ sin->sin_family = AF_INET;
+ sin->sin_port = htons(0);
+ sin->sin_addr.s_addr = htonl(INADDR_ANY);
+ res->ai_addr = (struct sockaddr*) sin;
+ res->ai_addrlen = (socklen_t)
+ sizeof(res->ai_addr);
+ break;
+ case AF_INET6:
+ sin6 = malloc(sizeof(struct sockaddr_in6));
+ if (sin6 == NULL)
+ out_of_mem();
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_port = htons(0);
+ sin6->sin6_addr = in6addr_any;
+ res->ai_addr = (struct sockaddr*) sin6;
+ res->ai_addrlen = (socklen_t) sizeof(res->ai_addr);
+ break;
+ default:
+ break;
+ }
+ } else {
+ if ((aicode = getaddrinfo(NULL, svcport_str,
+ &hints, &res)) != 0) {
+ syslog(LOG_ERR,
+ "cannot get local address for %s: %s",
+ nconf->nc_netid,
+ gai_strerror(aicode));
+ continue;
+ }
+ }
+ } else {
+ if ((aicode = getaddrinfo(hosts[nhostsbak], svcport_str,
+ &hints, &res)) != 0) {
+ syslog(LOG_ERR,
+ "cannot get local address for %s: %s",
+ nconf->nc_netid, gai_strerror(aicode));
+ continue;
+ }
+ }
+
+ r = bindresvport_sa(fd, res->ai_addr);
+ if (r != 0) {
+ syslog(LOG_ERR, "bindresvport_sa: %m");
+ exit(1);
+ }
+
+ if (nconf->nc_semantics != NC_TPI_CLTS)
+ listen(fd, SOMAXCONN);
+
+ transp = svc_tli_create(fd, nconf, NULL,
+ RPC_MAXDATASIZE, RPC_MAXDATASIZE);
+
+ if (transp != (SVCXPRT *) NULL) {
+ if (!svc_register(transp, SM_PROG, SM_VERS,
+ sm_prog_1, 0)) {
+ syslog(LOG_ERR, "can't register on %s",
+ nconf->nc_netid);
+ } else {
+ if (!svc_reg(transp, SM_PROG, SM_VERS,
+ sm_prog_1, NULL))
+ syslog(LOG_ERR,
+ "can't register %s SM_PROG service",
+ nconf->nc_netid);
+ }
+ } else
+ syslog(LOG_WARNING, "can't create %s services",
+ nconf->nc_netid);
+
+ if (registered == 0) {
+ registered = 1;
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = si.si_af;
+ hints.ai_socktype = si.si_socktype;
+ hints.ai_protocol = si.si_proto;
+
+ if (svcport_str == NULL) {
+ svcport_str = malloc(NI_MAXSERV * sizeof(char));
+ if (svcport_str == NULL)
+ out_of_mem();
+
+ if (getnameinfo(res->ai_addr,
+ res->ai_addr->sa_len, NULL, NI_MAXHOST,
+ svcport_str, NI_MAXSERV * sizeof(char),
+ NI_NUMERICHOST | NI_NUMERICSERV))
+ errx(1, "Cannot get port number");
+ }
+
+ if((aicode = getaddrinfo(NULL, svcport_str, &hints,
+ &res)) != 0) {
+ syslog(LOG_ERR, "cannot get local address: %s",
+ gai_strerror(aicode));
+ exit(1);
+ }
+
+ servaddr.buf = malloc(res->ai_addrlen);
+ memcpy(servaddr.buf, res->ai_addr, res->ai_addrlen);
+ servaddr.len = res->ai_addrlen;
+
+ rpcb_set(SM_PROG, SM_VERS, nconf, &servaddr);
+
+ xcreated++;
+ freeaddrinfo(res);
+ }
+ } /* end while */
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: rpc.statd [-d] [-h <bindip>] [-p <port>]\n");
+ exit(1);
+}
+
+/* handle_sigchld ---------------------------------------------------------- */
+/*
+ Purpose: Catch SIGCHLD and collect process status
+ Retruns: Nothing.
+ Notes: No special action required, other than to collect the
+ process status and hence allow the child to die:
+ we only use child processes for asynchronous transmission
+ of SM_NOTIFY to other systems, so it is normal for the
+ children to exit when they have done their work.
+*/
+
+static void handle_sigchld(int sig __unused)
+{
+ int pid, status;
+ pid = wait4(-1, &status, WNOHANG, (struct rusage*)0);
+ if (!pid) syslog(LOG_ERR, "Phantom SIGCHLD??");
+ else if (status == 0)
+ {
+ if (debug) syslog(LOG_DEBUG, "Child %d exited OK", pid);
+ }
+ else syslog(LOG_ERR, "Child %d failed with status %d", pid,
+ WEXITSTATUS(status));
+}
+
+/*
+ * Out of memory, fatal
+ */
+void
+out_of_mem()
+{
+
+ syslog(LOG_ERR, "out of memory");
+ exit(2);
+}
diff --git a/usr.sbin/rpc.statd/statd.h b/usr.sbin/rpc.statd/statd.h
new file mode 100644
index 0000000..a82d760
--- /dev/null
+++ b/usr.sbin/rpc.statd/statd.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 1995
+ * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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
+ * 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 ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED 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 "sm_inter.h"
+
+/* ------------------------------------------------------------------------- */
+/*
+ Data structures for recording monitored hosts
+
+ The information held by the status monitor comprises a list of hosts
+ that we have been asked to monitor, and, associated with each monitored
+ host, one or more clients to be called back if the monitored host crashes.
+
+ The list of monitored hosts must be retained over a crash, so that upon
+ re-boot we can call the SM_NOTIFY procedure in all those hosts so as to
+ cause them to start recovery processing. On the other hand, the client
+ call-backs are not required to be preserved: they are assumed (in the
+ protocol design) to be local processes which will have crashed when
+ we did, and so are discarded on restart.
+
+ We handle this by keeping the list of monitored hosts in a file
+ (/var/statd.state) which is mmap()ed and whose format is described
+ by the typedef FileLayout. The lists of client callbacks are chained
+ off this structure, but are held in normal memory and so will be
+ lost after a re-boot. Hence the actual values of MonList * pointers
+ in the copy on disc have no significance, but their NULL/non-NULL
+ status indicates whether this host is actually being monitored or if it
+ is an empty slot in the file.
+*/
+
+typedef struct MonList_s
+{
+ struct MonList_s *next; /* Next in list or NULL */
+ char notifyHost[SM_MAXSTRLEN + 1]; /* Host to notify */
+ int notifyProg; /* RPC program number to call */
+ int notifyVers; /* version number */
+ int notifyProc; /* procedure number */
+ unsigned char notifyData[16]; /* Opaque data from caller */
+} MonList;
+
+typedef struct
+{
+ char hostname[SM_MAXSTRLEN + 1]; /* Name of monitored host */
+ int notifyReqd; /* TRUE if we've crashed and not yet */
+ /* informed the monitored host */
+ MonList *monList; /* List of clients to inform if we */
+ /* hear that the monitored host has */
+ /* crashed, NULL if no longer monitored */
+} HostInfo;
+
+
+/* Overall file layout. */
+
+typedef struct
+{
+ int ourState; /* State number as defined in statd protocol */
+ int noOfHosts; /* Number of elements in hosts[] */
+ char reserved[248]; /* Reserved for future use */
+ HostInfo hosts[1]; /* vector of monitored hosts */
+} FileLayout;
+
+#define HEADER_LEN (sizeof(FileLayout) - sizeof(HostInfo))
+
+/* ------------------------------------------------------------------------- */
+
+/* Global variables */
+
+extern FileLayout *status_info; /* The mmap()ed status file */
+
+extern int debug; /* =1 to enable diagnostics to syslog */
+
+/* Function prototypes */
+
+extern HostInfo *find_host(char * /*hostname*/, int /*create*/);
+extern void init_file(const char * /*filename*/);
+extern void notify_hosts(void);
+extern void sync_file(void);
+extern int sm_check_hostname(struct svc_req *req, char *arg);
diff --git a/usr.sbin/rpc.statd/test.c b/usr.sbin/rpc.statd/test.c
new file mode 100644
index 0000000..6df1501
--- /dev/null
+++ b/usr.sbin/rpc.statd/test.c
@@ -0,0 +1,144 @@
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/sm_inter.h>
+
+
+/* Default timeout can be changed using clnt_control() */
+static struct timeval TIMEOUT = { 25, 0 };
+
+struct sm_stat_res *
+sm_stat_1(argp, clnt)
+ struct sm_name *argp;
+ CLIENT *clnt;
+{
+ static struct sm_stat_res res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, SM_STAT, xdr_sm_name, argp, xdr_sm_stat_res, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+struct sm_stat_res *
+sm_mon_1(argp, clnt)
+ struct mon *argp;
+ CLIENT *clnt;
+{
+ static struct sm_stat_res res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, SM_MON, xdr_mon, argp, xdr_sm_stat_res, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+struct sm_stat *
+sm_unmon_1(argp, clnt)
+ struct mon_id *argp;
+ CLIENT *clnt;
+{
+ static struct sm_stat res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, SM_UNMON, xdr_mon_id, argp, xdr_sm_stat, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+struct sm_stat *
+sm_unmon_all_1(argp, clnt)
+ struct my_id *argp;
+ CLIENT *clnt;
+{
+ static struct sm_stat res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, SM_UNMON_ALL, xdr_my_id, argp, xdr_sm_stat, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+void *
+sm_simu_crash_1(argp, clnt)
+ void *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, SM_SIMU_CRASH, xdr_void, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+int main(int argc, char **argv)
+{
+ CLIENT *cli;
+ char dummy;
+ void *out;
+ struct mon mon;
+
+ if (argc < 2)
+ {
+ fprintf(stderr, "usage: test <hostname> | crash\n");
+ fprintf(stderr, "always talks to statd at localhost\n");
+ exit(1);
+ }
+
+ printf("Creating client for localhost\n" );
+ cli = clnt_create("localhost", SM_PROG, SM_VERS, "udp");
+ if (!cli)
+ {
+ printf("Failed to create client\n");
+ exit(1);
+ }
+
+ mon.mon_id.mon_name = argv[1];
+ mon.mon_id.my_id.my_name = argv[1];
+ mon.mon_id.my_id.my_prog = SM_PROG;
+ mon.mon_id.my_id.my_vers = SM_VERS;
+ mon.mon_id.my_id.my_proc = 1; /* have it call sm_stat() !!! */
+
+ if (strcmp(argv[1], "crash"))
+ {
+ /* Hostname given */
+ struct sm_stat_res *res;
+ if (res = sm_mon_1(&mon, cli))
+ {
+ printf("Success!\n");
+ }
+ else
+ {
+ printf("Fail\n");
+ }
+ }
+ else
+ {
+ if (out = sm_simu_crash_1(&dummy, cli))
+ {
+ printf("Success!\n");
+ }
+ else
+ {
+ printf("Fail\n");
+ }
+ }
+
+ return 0;
+}
diff --git a/usr.sbin/rpc.umntall/Makefile b/usr.sbin/rpc.umntall/Makefile
new file mode 100644
index 0000000..0a829c3
--- /dev/null
+++ b/usr.sbin/rpc.umntall/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.4 (Berkeley) 6/22/95
+# $FreeBSD$
+
+PROG= rpc.umntall
+MAN= rpc.umntall.8
+SRCS= rpc.umntall.c mounttab.c
+
+#WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rpc.umntall/mounttab.c b/usr.sbin/rpc.umntall/mounttab.c
new file mode 100644
index 0000000..b81334e
--- /dev/null
+++ b/usr.sbin/rpc.umntall/mounttab.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 1999 Martin Blapp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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/syslog.h>
+
+#include <rpc/rpc.h>
+#include <nfs/rpcv2.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mounttab.h"
+
+struct mtablist *mtabhead;
+
+static void badline(char *field, char *bad);
+
+/*
+ * Add an entry to PATH_MOUNTTAB for each mounted NFS filesystem,
+ * so the client can notify the NFS server even after reboot.
+ */
+int
+add_mtab(char *hostp, char *dirp) {
+ FILE *mtabfile;
+
+ if ((mtabfile = fopen(PATH_MOUNTTAB, "a")) == NULL)
+ return (0);
+ else {
+ fprintf(mtabfile, "%ld\t%s\t%s\n",
+ (long)time(NULL), hostp, dirp);
+ fclose(mtabfile);
+ return (1);
+ }
+}
+
+/*
+ * Read mounttab line for line and return struct mtablist.
+ */
+int
+read_mtab() {
+ struct mtablist **mtabpp, *mtabp;
+ char *hostp, *dirp, *cp;
+ char str[STRSIZ];
+ char *timep, *endp;
+ time_t time;
+ u_long ultmp;
+ FILE *mtabfile;
+
+ if ((mtabfile = fopen(PATH_MOUNTTAB, "r")) == NULL) {
+ if (errno == ENOENT)
+ return (0);
+ else {
+ syslog(LOG_ERR, "can't open %s", PATH_MOUNTTAB);
+ return (0);
+ }
+ }
+ time = 0;
+ mtabpp = &mtabhead;
+ while (fgets(str, STRSIZ, mtabfile) != NULL) {
+ cp = str;
+ errno = 0;
+ if (*cp == '#' || *cp == ' ' || *cp == '\n')
+ continue;
+ timep = strsep(&cp, " \t\n");
+ if (timep == NULL || *timep == '\0') {
+ badline("time", timep);
+ continue;
+ }
+ hostp = strsep(&cp, " \t\n");
+ if (hostp == NULL || *hostp == '\0') {
+ badline("host", hostp);
+ continue;
+ }
+ dirp = strsep(&cp, " \t\n");
+ if (dirp == NULL || *dirp == '\0') {
+ badline("dir", dirp);
+ continue;
+ }
+ ultmp = strtoul(timep, &endp, 10);
+ if (ultmp == ULONG_MAX || *endp != '\0') {
+ badline("time", timep);
+ continue;
+ }
+ time = ultmp;
+ if ((mtabp = malloc(sizeof (struct mtablist))) == NULL) {
+ syslog(LOG_ERR, "malloc");
+ fclose(mtabfile);
+ return (0);
+ }
+ mtabp->mtab_time = time;
+ memmove(mtabp->mtab_host, hostp, RPCMNT_NAMELEN);
+ mtabp->mtab_host[RPCMNT_NAMELEN - 1] = '\0';
+ memmove(mtabp->mtab_dirp, dirp, RPCMNT_PATHLEN);
+ mtabp->mtab_dirp[RPCMNT_PATHLEN - 1] = '\0';
+ mtabp->mtab_next = (struct mtablist *)NULL;
+ *mtabpp = mtabp;
+ mtabpp = &mtabp->mtab_next;
+ }
+ fclose(mtabfile);
+ return (1);
+}
+
+/*
+ * Rewrite PATH_MOUNTTAB from scratch and skip bad entries.
+ * Unlink PATH_MOUNTAB if no entry is left.
+ */
+int
+write_mtab(int verbose) {
+ struct mtablist *mtabp, *mp;
+ FILE *mtabfile;
+ int line;
+
+ if ((mtabfile = fopen(PATH_MOUNTTAB, "w")) == NULL) {
+ syslog(LOG_ERR, "can't write to %s", PATH_MOUNTTAB);
+ return (0);
+ }
+ line = 0;
+ for (mtabp = mtabhead; mtabp != NULL; mtabp = mtabp->mtab_next) {
+ if (mtabp->mtab_host[0] == '\0')
+ continue;
+ /* Skip if a later (hence more recent) entry is identical. */
+ for (mp = mtabp->mtab_next; mp != NULL; mp = mp->mtab_next)
+ if (strcmp(mtabp->mtab_host, mp->mtab_host) == 0 &&
+ strcmp(mtabp->mtab_dirp, mp->mtab_dirp) == 0)
+ break;
+ if (mp != NULL)
+ continue;
+
+ fprintf(mtabfile, "%ld\t%s\t%s\n",
+ (long)mtabp->mtab_time, mtabp->mtab_host,
+ mtabp->mtab_dirp);
+ if (verbose)
+ warnx("write mounttab entry %s:%s",
+ mtabp->mtab_host, mtabp->mtab_dirp);
+ line++;
+ }
+ fclose(mtabfile);
+ if (line == 0) {
+ if (unlink(PATH_MOUNTTAB) == -1) {
+ syslog(LOG_ERR, "can't remove %s", PATH_MOUNTTAB);
+ return (0);
+ }
+ }
+ return (1);
+}
+
+/*
+ * Mark the entries as clean where RPC calls have been done successfully.
+ */
+void
+clean_mtab(char *hostp, char *dirp, int verbose) {
+ struct mtablist *mtabp;
+ char *host;
+
+ /* Copy hostp in case it points to an entry that we are zeroing out. */
+ host = strdup(hostp);
+ for (mtabp = mtabhead; mtabp != NULL; mtabp = mtabp->mtab_next) {
+ if (strcmp(mtabp->mtab_host, host) != 0)
+ continue;
+ if (dirp != NULL && strcmp(mtabp->mtab_dirp, dirp) != 0)
+ continue;
+
+ if (verbose)
+ warnx("delete mounttab entry%s %s:%s",
+ (dirp == NULL) ? " by host" : "",
+ mtabp->mtab_host, mtabp->mtab_dirp);
+ bzero(mtabp->mtab_host, RPCMNT_NAMELEN);
+ }
+ free(host);
+}
+
+/*
+ * Free struct mtablist mtab.
+ */
+void
+free_mtab() {
+ struct mtablist *mtabp;
+
+ while ((mtabp = mtabhead) != NULL) {
+ mtabhead = mtabhead->mtab_next;
+ free(mtabp);
+ }
+}
+
+/*
+ * Print bad lines to syslog.
+ */
+static void
+badline(char *field, char *bad) {
+ syslog(LOG_ERR, "bad mounttab %s field '%s'", field,
+ (bad == NULL) ? "<null>" : bad);
+}
diff --git a/usr.sbin/rpc.umntall/mounttab.h b/usr.sbin/rpc.umntall/mounttab.h
new file mode 100644
index 0000000..acf6c79
--- /dev/null
+++ b/usr.sbin/rpc.umntall/mounttab.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 1999 Martin Blapp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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$
+ */
+
+#define STRSIZ (RPCMNT_NAMELEN+RPCMNT_PATHLEN+100)
+#define PATH_MOUNTTAB "/var/db/mounttab"
+
+/* Structure for /var/db/mounttab */
+struct mtablist {
+ time_t mtab_time;
+ char mtab_host[RPCMNT_NAMELEN];
+ char mtab_dirp[RPCMNT_PATHLEN];
+ struct mtablist *mtab_next;
+};
+
+extern struct mtablist *mtabhead;
+
+int add_mtab(char *, char *);
+void clean_mtab(char *, char *, int);
+int read_mtab(void);
+int write_mtab(int);
+void free_mtab(void);
diff --git a/usr.sbin/rpc.umntall/rpc.umntall.8 b/usr.sbin/rpc.umntall/rpc.umntall.8
new file mode 100644
index 0000000..6236635
--- /dev/null
+++ b/usr.sbin/rpc.umntall/rpc.umntall.8
@@ -0,0 +1,126 @@
+.\"
+.\" Copyright (c) 1999 Martin Blapp
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 17, 1999
+.Dt RPC.UMNTALL 8
+.Os
+.Sh NAME
+.Nm rpc.umntall
+.Nd notify NFS servers about unmounted NFS file systems
+.Sh SYNOPSIS
+.Nm
+.Op Fl e Ar expire
+.Op Fl h Ar host
+.Op Fl k
+.Op Fl p Ar remotepath
+.Op Fl v
+.Sh DESCRIPTION
+The
+.Nm
+utility is proposed in the
+.Tn NFS
+RPC specification; see
+.Rs
+.%T "NFS Version 3 Protocol Specification"
+.%O "RFC 1813, Appendix I"
+.Re
+It uses remote procedure calls to remove mount entries from
+.Pa /var/db/mountdtab
+on the remote NFS server.
+It is called automatically
+without any parameters during startup and shutdown of
+the system.
+This ensures that
+.Xr showmount 8
+does not display old and expired entries.
+The
+.Nm
+utility
+is only needed on client side, where
+.Xr mount_nfs 8
+adds a mount entry with the current date to
+.Pa /var/db/mounttab ,
+and
+.Xr umount 8
+removes the entry again.
+The
+.Nm
+utility
+cares about all remaining entries in this table which result from crashes
+or unproper shutdowns.
+.Pp
+The options are as follows:
+.Bl -tag -width indentxxx
+.It Fl e Ar expire
+All entries which are not actually mounted or older than
+.Ar expire
+(seconds) are removed from
+.Pa /var/db/mounttab .
+This may be the case
+for DNS changes or long out of service periods.
+Default expire time
+is 86400 seconds (one day).
+.It Fl h Ar host
+Only remove the specific hostname.
+Send a UMNTALL RPC to the NFS server.
+.It Fl k
+Keep entries for existing NFS file systems.
+Compare the NFS file systems from
+the mounttab against the kernel mountlist and do not send the RPC to
+existing mount entries.
+Useful during startup of the system.
+It may be
+possible that there are already mounted NFS file systems, so calling
+RPC UMOUNT is not a good idea.
+This is the case if the user has rebooted
+to 'single user mode' and starts up the system again.
+.It Fl p Ar path
+Only remove the specific mount-path.
+Send a UMOUNT RPC to the NFS server.
+This option implies the
+.Fl host
+option.
+.It Fl v
+Verbose, additional information is printed for each processed mounttab
+entry.
+.El
+.Sh FILES
+.Bl -tag -width /var/db/mounttab -compact
+.It Pa /var/db/mounttab
+mounted nfs-file systems
+.El
+.Sh SEE ALSO
+.Xr mountd 8 ,
+.Xr mount_nfs 8 ,
+.Xr umount 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 4.0 .
+.Sh AUTHORS
+.An Martin Blapp Aq mb@imp.ch
diff --git a/usr.sbin/rpc.umntall/rpc.umntall.c b/usr.sbin/rpc.umntall/rpc.umntall.c
new file mode 100644
index 0000000..b79e478
--- /dev/null
+++ b/usr.sbin/rpc.umntall/rpc.umntall.c
@@ -0,0 +1,266 @@
+/*
+ * Copyright (c) 1999 Martin Blapp
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/ucred.h>
+#include <sys/mount.h>
+
+#include <rpc/rpc.h>
+#include <nfs/rpcv2.h>
+
+#include <err.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mounttab.h"
+
+int verbose;
+
+static int do_umount (char *, char *);
+static int do_umntall (char *);
+static int is_mounted (char *, char *);
+static void usage (void);
+int xdr_dir (XDR *, char *);
+
+int
+main(int argc, char **argv) {
+ int ch, keep, success, pathlen;
+ time_t expire, now;
+ char *host, *path;
+ struct mtablist *mtab;
+
+ expire = 0;
+ host = path = NULL;
+ success = keep = verbose = 0;
+ while ((ch = getopt(argc, argv, "h:kp:ve:")) != -1)
+ switch (ch) {
+ case 'h':
+ host = optarg;
+ break;
+ case 'e':
+ expire = atoi(optarg);
+ break;
+ case 'k':
+ keep = 1;
+ break;
+ case 'p':
+ path = optarg;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case '?':
+ usage();
+ default:
+ break;
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* Default expiretime is one day */
+ if (expire == 0)
+ expire = 86400;
+ time(&now);
+
+ /* Read PATH_MOUNTTAB. */
+ if (!read_mtab()) {
+ if (verbose)
+ warnx("no mounttab entries (%s does not exist)",
+ PATH_MOUNTTAB);
+ mtabhead = NULL;
+ }
+
+ if (host == NULL && path == NULL) {
+ /* Check each entry and do any necessary unmount RPCs. */
+ for (mtab = mtabhead; mtab != NULL; mtab = mtab->mtab_next) {
+ if (*mtab->mtab_host == '\0')
+ continue;
+ if (mtab->mtab_time + expire < now) {
+ /* Clear expired entry. */
+ if (verbose)
+ warnx("remove expired entry %s:%s",
+ mtab->mtab_host, mtab->mtab_dirp);
+ bzero(mtab->mtab_host,
+ sizeof(mtab->mtab_host));
+ continue;
+ }
+ if (keep && is_mounted(mtab->mtab_host,
+ mtab->mtab_dirp)) {
+ if (verbose)
+ warnx("skip entry %s:%s",
+ mtab->mtab_host, mtab->mtab_dirp);
+ continue;
+ }
+ if (do_umount(mtab->mtab_host, mtab->mtab_dirp)) {
+ if (verbose)
+ warnx("umount RPC for %s:%s succeeded",
+ mtab->mtab_host, mtab->mtab_dirp);
+ /* Remove all entries for this host + path. */
+ clean_mtab(mtab->mtab_host, mtab->mtab_dirp,
+ verbose);
+ }
+ }
+ success = 1;
+ } else {
+ if (host == NULL && path != NULL)
+ /* Missing hostname. */
+ usage();
+ if (path == NULL) {
+ /* Do a RPC UMNTALL for this specific host */
+ success = do_umntall(host);
+ if (verbose && success)
+ warnx("umntall RPC for %s succeeded", host);
+ } else {
+ /* Do a RPC UMNTALL for this specific mount */
+ for (pathlen = strlen(path);
+ pathlen > 1 && path[pathlen - 1] == '/'; pathlen--)
+ path[pathlen - 1] = '\0';
+ success = do_umount(host, path);
+ if (verbose && success)
+ warnx("umount RPC for %s:%s succeeded", host,
+ path);
+ }
+ /* If successful, remove any corresponding mounttab entries. */
+ if (success)
+ clean_mtab(host, path, verbose);
+ }
+ /* Write and unlink PATH_MOUNTTAB if necessary */
+ if (success)
+ success = write_mtab(verbose);
+ free_mtab();
+ exit (success ? 0 : 1);
+}
+
+/*
+ * Send a RPC_MNT UMNTALL request to hostname.
+ * XXX This works for all mountd implementations,
+ * but produces a RPC IOERR on non FreeBSD systems.
+ */
+int
+do_umntall(char *hostname) {
+ enum clnt_stat clnt_stat;
+ struct timeval try;
+ CLIENT *clp;
+
+ try.tv_sec = 3;
+ try.tv_usec = 0;
+ clp = clnt_create_timed(hostname, RPCPROG_MNT, RPCMNT_VER1, "udp",
+ &try);
+ if (clp == NULL) {
+ warnx("%s: %s", hostname, clnt_spcreateerror("RPCPROG_MNT"));
+ return (0);
+ }
+ clp->cl_auth = authunix_create_default();
+ clnt_stat = clnt_call(clp, RPCMNT_UMNTALL,
+ (xdrproc_t)xdr_void, (caddr_t)0,
+ (xdrproc_t)xdr_void, (caddr_t)0, try);
+ if (clnt_stat != RPC_SUCCESS)
+ warnx("%s: %s", hostname, clnt_sperror(clp, "RPCMNT_UMNTALL"));
+ auth_destroy(clp->cl_auth);
+ clnt_destroy(clp);
+ return (clnt_stat == RPC_SUCCESS);
+}
+
+/*
+ * Send a RPC_MNT UMOUNT request for dirp to hostname.
+ */
+int
+do_umount(char *hostname, char *dirp) {
+ enum clnt_stat clnt_stat;
+ struct timeval try;
+ CLIENT *clp;
+
+ try.tv_sec = 3;
+ try.tv_usec = 0;
+ clp = clnt_create_timed(hostname, RPCPROG_MNT, RPCMNT_VER1, "udp",
+ &try);
+ if (clp == NULL) {
+ warnx("%s: %s", hostname, clnt_spcreateerror("RPCPROG_MNT"));
+ return (0);
+ }
+ clp->cl_auth = authsys_create_default();
+ clnt_stat = clnt_call(clp, RPCMNT_UMOUNT, (xdrproc_t)xdr_dir, dirp,
+ (xdrproc_t)xdr_void, (caddr_t)0, try);
+ if (clnt_stat != RPC_SUCCESS)
+ warnx("%s: %s", hostname, clnt_sperror(clp, "RPCMNT_UMOUNT"));
+ auth_destroy(clp->cl_auth);
+ clnt_destroy(clp);
+ return (clnt_stat == RPC_SUCCESS);
+}
+
+/*
+ * Check if the entry is still/already mounted.
+ */
+int
+is_mounted(char *hostname, char *dirp) {
+ struct statfs *mntbuf;
+ char name[MNAMELEN + 1];
+ size_t bufsize;
+ int mntsize, i;
+
+ if (strlen(hostname) + strlen(dirp) >= MNAMELEN)
+ return (0);
+ snprintf(name, sizeof(name), "%s:%s", hostname, dirp);
+ mntsize = getfsstat(NULL, 0, MNT_NOWAIT);
+ if (mntsize <= 0)
+ return (0);
+ bufsize = (mntsize + 1) * sizeof(struct statfs);
+ if ((mntbuf = malloc(bufsize)) == NULL)
+ err(1, "malloc");
+ mntsize = getfsstat(mntbuf, (long)bufsize, MNT_NOWAIT);
+ for (i = mntsize - 1; i >= 0; i--) {
+ if (strcmp(mntbuf[i].f_mntfromname, name) == 0) {
+ free(mntbuf);
+ return (1);
+ }
+ }
+ free(mntbuf);
+ return (0);
+}
+
+/*
+ * xdr routines for mount rpc's
+ */
+int
+xdr_dir(XDR *xdrsp, char *dirp) {
+ return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
+}
+
+static void
+usage() {
+ (void)fprintf(stderr, "%s\n",
+ "usage: rpc.umntall [-kv] [-e expire] [-h host] [-p path]");
+ exit(1);
+}
diff --git a/usr.sbin/rpc.yppasswdd/Makefile b/usr.sbin/rpc.yppasswdd/Makefile
new file mode 100644
index 0000000..bbd2658
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/Makefile
@@ -0,0 +1,62 @@
+# $FreeBSD$
+
+RPCDIR= ${DESTDIR}/usr/include/rpcsvc
+
+.PATH: ${.CURDIR}/../../usr.sbin/ypserv ${.CURDIR}/../../usr.bin/chpass \
+ ${.CURDIR}/../../libexec/ypxfr ${RPCDIR}
+
+PROG= rpc.yppasswdd
+SCRIPTS=yppwupdate
+SCRIPTSDIR= /usr/libexec
+MAN= rpc.yppasswdd.8
+SRCS= util.c yp_access.c yp_dblookup.c yp_dbwrite.c \
+ yp_error.c yppasswdd_main.c yppasswdd_server.c ypxfr_misc.c ${GENSRCS}
+GENSRCS=yp.h yp_clnt.c yppasswd.h yppasswd_private.h yppasswd_private_svc.c \
+ yppasswd_private_xdr.c yppasswd_svc.c
+
+WARNS?= 4
+CFLAGS+= -fno-strict-aliasing
+CFLAGS+= -I${.CURDIR}/../../usr.sbin/vipw \
+ -I${.CURDIR}/../../usr.sbin/ypserv \
+ -I${.CURDIR}/../../libexec/ypxfr \
+ -I${.CURDIR} -I.
+DPADD= ${LIBRPCSVC} ${LIBCRYPT} ${LIBUTIL}
+LDADD= -lrpcsvc -lcrypt -lutil
+
+CLEANFILES= ${GENSRCS}
+
+RPCGEN= rpcgen -I -C
+
+# We need to remove the 'static' keyword from _rpcsvcstate so that
+# yppasswdd_main.c can see it.
+yppasswd_svc.c: yppasswd.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -m ${RPCDIR}/yppasswd.x | \
+ sed s/"static int _rpcsvcstate"/"int _rpcsvcstate"/g > ${.TARGET}
+
+yppasswd.h: yppasswd.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -h -o ${.TARGET} ${RPCDIR}/yppasswd.x
+
+yp.h: yp.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -h -o ${.TARGET} ${RPCDIR}/yp.x
+
+yp_clnt.c: yp.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -DYPSERV_ONLY -l -o ${.TARGET} ${RPCDIR}/yp.x
+
+yppasswd_private.h: yppasswd_private.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -h -o ${.TARGET} ${.CURDIR}/yppasswd_private.x
+
+yppasswd_private_xdr.c: yppasswd_private.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -c -o ${.TARGET} ${.CURDIR}/yppasswd_private.x
+
+yppasswd_private_svc.c: yppasswd_private.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -m ${.CURDIR}/yppasswd_private.x | \
+ sed s/"static int _rpcsvcstate = _IDLE"/"extern int _rpcsvcstate"/g > ${.TARGET}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rpc.yppasswdd/rpc.yppasswdd.8 b/usr.sbin/rpc.yppasswdd/rpc.yppasswdd.8
new file mode 100644
index 0000000..3763908
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/rpc.yppasswdd.8
@@ -0,0 +1,359 @@
+.\" Copyright (c) 1995, 1996
+.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Bill Paul.
+.\" 4. Neither the name of the author nor the names of contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd February 8, 1996
+.Dt RPC.YPPASSWDD 8
+.Os
+.Sh NAME
+.Nm rpc.yppasswdd
+.Nd "server for updating NIS passwords"
+.Sh SYNOPSIS
+.Nm
+.Op Fl t Ar master.passwd template file
+.Op Fl d Ar default domain
+.Op Fl p Ar path
+.Op Fl s
+.Op Fl f
+.Op Fl a
+.Op Fl m
+.Op Fl i
+.Op Fl v
+.Op Fl u
+.Op Fl h
+.Sh DESCRIPTION
+The
+.Nm
+utility allows users to change their NIS passwords and certain
+other information using the
+.Xr yppasswd 1
+and
+.Xr ypchpass 1
+commands.
+The
+.Nm
+utility
+is an RPC-based server that accepts incoming password change requests,
+authenticates them, places the updated information in the
+.Pa /var/yp/master.passwd
+template file and then updates the NIS
+.Pa master.passwd
+and
+.Pa passwd
+maps.
+.Pp
+The
+.Nm
+utility allows a normal NIS user to change
+his or her NIS password, full name (also
+known as 'GECOS' field) or shell.
+These updates are typically done using
+the
+.Xr yppasswd 1 ,
+.Xr ypchfn 1 ,
+.Xr ypchsh 1 ,
+or
+.Xr ypchpass 1
+commands.
+(Some administrators do not want users to be able to change their
+full name information or shells; the server can be invoked with option flags
+that disallow such changes.)
+When the server receives an update request,
+it compares the address of the client making the request against the
+.Pa securenets
+rules outlined in
+.Pa /var/yp/securenets .
+(See the
+.Xr ypserv 8
+manual page for more information on securenets; the
+.Nm
+utility uses the same access control mechanism as
+.Xr ypserv 8 . )
+.Pp
+The server then
+checks the 'old' password supplied by the user to make sure it is
+valid, then performs some sanity checks on the updated information (these
+include checking for embedded control characters, colons or invalid shells).
+Once it is satisfied that the update request is valid, the server modifies
+the template password file (the default is
+.Pa /var/yp/master.passwd )
+and then runs the
+.Pa /usr/libexec/yppwupdate
+script to rebuild the NIS maps.
+(This script has two arguments passed
+to it: the absolute pathname of the password template that was modified
+and the name of the domain that is to be updated.
+These in turn are
+passed to
+.Pa /var/yp/Makefile ) .
+.Pp
+The
+.Fx
+version of
+.Nm
+also allows the super-user on the NIS master server to perform more
+sophisticated updates on the NIS passwd maps.
+The super-user can modify
+any field in any user's master.passwd entry in any domain, and can
+do so without knowing the user's existing NIS password (when the server
+receives a request from the super-user, the password authentication
+check is bypassed).
+Furthermore, if the server is invoked with the
+.Fl a
+flag, the super-user can even add new entries to the maps using
+.Xr ypchpass 1 .
+Again, this only applies to the super-user on the NIS
+master server: none of these special functions can be performed over
+the network.
+.Pp
+The
+.Nm
+utility can only be run on a machine that is an NIS master server.
+.Sh OPTIONS
+The following options are available:
+.Bl -tag -width indent
+.It Fl t Ar master.passwd template file
+By default,
+.Nm
+assumes that the template file used to generates the
+.Pa master.passwd
+and
+.Pa passwd
+maps for the default domain is called
+.Pa /var/yp/master.passwd .
+This default can be overridden by specifying an alternate file name
+with the
+.Fl t
+flag.
+.Pp
+Note: if the template file specified with this flag is
+.Pa /etc/master.passwd ,
+.Nm
+will also automatically invoke
+.Xr pwd_mkdb 8
+to rebuild the local password databases in addition to the NIS
+maps.
+.It Fl d Ar domain
+The
+.Nm
+utility can support multiple domains, however it must
+choose one domain as a default.
+It will try to use the system default domain name as set by the
+.Xr domainname 1
+command for this default.
+However,
+if the system domain name is not
+set, a default domain must be specified on
+the command line.
+If the system default domain is set,
+then this option can be used to override it.
+.It Fl p Ar path
+This option can be used to override the default path to
+the location of the NIS
+map databases.
+The compiled-in default path is
+.Pa /var/yp .
+.It Fl s
+Disallow changing of shell information.
+.It Fl f
+Disallow changing of full name ('GECOS') information.
+.It Fl a
+Allow additions to be made to the NIS passwd databases.
+The super-user on the
+NIS master server is permitted to use the
+.Xr ypchpass 1
+command to perform unrestricted modifications to any field in a user's
+.Pa master.passwd
+map entry.
+When
+.Nm
+is started with this flag, it will also allow the super-user to add new
+records to the NIS passwd maps, just as is possible when using
+.Xr chpass 1
+to modify the local password database.
+.It Fl m
+Turn on multi-domain mode.
+Even though
+.Xr ypserv 8
+can handle several simultaneous domains, most implementations of
+.Nm
+can only operate on a single NIS domain, which is generally the same as
+the system default domain of the NIS master server.
+The
+.Fx
+.Nm
+attempts to overcome this problem in spite of the inherent limitations
+of the
+.Pa yppasswd
+protocol, which does not allow for a
+.Pa domain
+argument in client requests.
+In multi-domain mode,
+.Nm
+will search through all the passwd maps of all the domains it
+can find under
+.Pa /var/yp
+until it finds an entry that matches the user information specified in
+a given update request.
+(Matches are determined by checking the username,
+UID and GID fields.)
+The matched entry and corresponding domain are then
+used for the update.
+.Pp
+Note that in order for multi-domain mode to work, there have to be
+separate template files for each domain.
+For example, if a server
+supports three domains,
+.Pa foo ,
+.Pa bar ,
+and
+.Pa baz ,
+there should be three separate master.passwd template files called
+.Pa /var/yp/foo/master.passwd ,
+.Pa /var/yp/bar/master.passwd ,
+and
+.Pa /var/yp/baz/master.passwd .
+If
+.Pa foo
+happens to be the system default domain, then its template file can
+be either
+.Pa /var/yp/foo/master.passwd
+or
+.Pa /var/yp/master.passwd .
+The server will check for the latter file first and then use the former
+if it cannot find it.
+.Pp
+Multi-domain mode is off by default since it can fail if there are
+duplicate or near-duplicate user entries in different domains.
+The server
+will abort an update request if it finds more than one user entry that
+matches its search criteria.
+Even so, paranoid administrators
+may wish to leave multi-domain mode disabled.
+.It Fl i
+If
+.Nm
+is invoked with this flag, it will perform map updates in place.
+This
+means that instead of just modifying the password template file and
+starting a map update, the server will modify the map databases
+directly.
+This is useful when the password maps are large: if, for
+example, the password database has tens of thousands of entries, it
+can take several minutes for a map update to complete.
+Updating the
+maps in place reduces this time to a few seconds.
+.It Fl v
+Turn on verbose logging mode.
+The server normally only logs messages
+using the
+.Xr syslog 3
+facility when it encounters an error condition, or when processing
+updates for the super-user on the NIS master server.
+Running the server
+with the
+.Fl v
+flag will cause it to log informational messages for all updates.
+.It Fl u
+Many commercial
+.Xr yppasswd 1
+clients do not use a reserved port when sending requests to
+.Nm .
+This is either because the
+.Xr yppasswd 1
+program is not installed set-uid root, or because the RPC
+implementation does not place any emphasis on binding to reserved
+ports when establishing client connections for the super-user.
+By default,
+.Nm
+expects to receive requests from clients using reserved ports; requests
+received from non-privileged ports are rejected.
+Unfortunately, this
+behavior prevents any client systems that to not use privileged
+ports from successfully submitting password updates.
+Specifying
+the
+.Fl u
+flag to
+.Nm
+disables the privileged port check so that it will work with
+.Xr yppasswd 1
+clients that do not use privileged ports.
+This reduces security to
+a certain small degree, but it might be necessary in cases where it
+is not possible to change the client behavior.
+.It Fl h
+Display the list of flags and options understood by
+.Nm .
+.El
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /usr/libexec/yppwupdate
+The script invoked by
+.Nm
+to update and push the NIS maps after
+an update.
+.It Pa /var/yp/master.passwd
+The template password file for the default domain.
+.It Pa /var/yp/[domainname]/[maps]
+The NIS maps for a particular NIS domain.
+.It Pa /var/yp/[domainname]/master.passwd
+The template password file(s) for non-default domains
+(used only in multi-domain mode).
+.El
+.Sh SEE ALSO
+.Xr yp 8 ,
+.Xr yppush 8 ,
+.Xr ypserv 8 ,
+.Xr ypxfr 8
+.Sh AUTHORS
+.An Bill Paul Aq wpaul@ctr.columbia.edu
+.Sh BUGS
+As listed in the yppasswd.x protocol definition, the YPPASSWDPROC_UPDATE
+procedure takes two arguments: a V7-style passwd structure containing
+updated user information and the user's existing unencrypted (cleartext)
+password.
+Since
+.Nm
+is supposed to handle update requests from remote NIS client machines,
+this means that
+.Xr yppasswd 1
+and similar client programs will in fact be transmitting users' cleartext
+passwords over the network.
+.Pp
+This is not a problem for password updates since the plaintext password
+sent with the update will no longer be valid once the new encrypted password
+is put into place, but if the user is only updating his or her 'GECOS'
+information or shell, then the cleartext password sent with the update
+will still be valid once the update is completed.
+If the network is
+insecure, this cleartext password could be intercepted and used to
+gain unauthorized access to the user's account.
diff --git a/usr.sbin/rpc.yppasswdd/yppasswd_private.x b/usr.sbin/rpc.yppasswdd/yppasswd_private.x
new file mode 100644
index 0000000..a1d6be5
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/yppasswd_private.x
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef RPC_HDR
+%#include <sys/cdefs.h>
+%__FBSDID("$FreeBSD$");
+#endif
+
+#ifdef RPC_HDR
+%#define YP_SOCKNAME "/var/run/yppasswdsock"
+#endif
+
+struct x_master_passwd {
+ string pw_name<>; /* username */
+ string pw_passwd<>; /* encrypted password */
+ int pw_uid; /* user id */
+ int pw_gid; /* group id */
+ unsigned long pw_change;/* password change time */
+ string pw_class<>; /* user access class */
+ string pw_gecos<>; /* in real life name */
+ string pw_dir<>; /* home directory */
+ string pw_shell<>; /* default shell */
+ unsigned long pw_expire;/* account expiration */
+ unsigned long pw_fields;/* internal: fields filled in */
+};
+
+const _YPMAXDOMAIN = 64;
+
+struct master_yppasswd {
+ string oldpass<>; /* unencrypted old password */
+ string domain<_YPMAXDOMAIN>; /* domain we want to operate on */
+ x_master_passwd newpw; /* new passwd entry */
+};
+
+
+program MASTER_YPPASSWDPROG {
+ version MASTER_YPPASSWDVERS {
+ int
+ YPPASSWDPROC_UPDATE_MASTER(master_yppasswd) = 1;
+ } = 1;
+} = 600100009;
diff --git a/usr.sbin/rpc.yppasswdd/yppasswdd_extern.h b/usr.sbin/rpc.yppasswdd/yppasswdd_extern.h
new file mode 100644
index 0000000..db15be2
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/yppasswdd_extern.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _YPPASSWDD_EXTERN_H
+#define _YPPASSWDD_EXTERN_H
+
+#ifndef YPLIBDIR
+#define YPLIBDIR "/usr/libexec/"
+#endif
+
+#ifndef _PATH_YP
+#define _PATH_YP "/var/yp/"
+#endif
+
+#define MAP_UPDATE "yppwupdate"
+#define MAP_UPDATE_PATH YPLIBDIR "yppwupdate"
+
+extern char *yp_dir;
+extern char *progname;
+extern void do_master(void);
+extern void yppasswdprog_1(struct svc_req *, register SVCXPRT *);
+extern void master_yppasswdprog_1(struct svc_req *, register SVCXPRT *);
+extern void reaper(int);
+extern void install_reaper(int);
+extern char *ok_shell(char *);
+extern char *passfile;
+extern char *passfile_default;
+extern char *yppasswd_domain;
+extern int no_chsh;
+extern int no_chfn;
+extern int allow_additions;
+extern int multidomain;
+extern int resvport;
+extern int inplace;
+extern int verbose;
+extern int _rpc_dtablesize(void);
+
+#endif
diff --git a/usr.sbin/rpc.yppasswdd/yppasswdd_main.c b/usr.sbin/rpc.yppasswdd/yppasswdd_main.c
new file mode 100644
index 0000000..8c89691
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/yppasswdd_main.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <netinet/in.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <memory.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h> /* getenv, exit */
+#include <string.h>
+#include <string.h> /* strcmp */
+#include <syslog.h>
+#include <unistd.h>
+
+#include <rpc/rpc.h>
+#include <rpc/rpc_com.h>
+#include <rpc/pmap_clnt.h> /* for pmap_unset */
+#include <rpcsvc/yp.h>
+#include <rpcsvc/ypclnt.h>
+
+#include "yppasswd.h"
+#include "yppasswdd_extern.h"
+#include "yppasswd_private.h"
+#include "ypxfr_extern.h"
+#include "yp_extern.h"
+
+#ifndef SIG_PF
+#define SIG_PF void(*)(int)
+#endif
+
+#ifdef DEBUG
+#define RPC_SVC_FG
+#endif
+
+#define _RPCSVC_CLOSEDOWN 120
+int _rpcpmstart = 0; /* Started by a port monitor ? */
+static int _rpcfdtype;
+ /* Whether Stream or Datagram ? */
+ /* States a server can be in wrt request */
+
+#define _IDLE 0
+#define _SERVED 1
+#define _SERVING 2
+
+static char _localhost[] = "localhost";
+static char _passwd_byname[] = "passwd.byname";
+extern int _rpcsvcstate; /* Set when a request is serviced */
+static char _progname[] = "rpc.yppasswdd";
+char *progname = _progname;
+static char _yp_dir[] = _PATH_YP;
+char *yp_dir = _yp_dir;
+static char _passfile_default[] = _PATH_YP "master.passwd";
+char *passfile_default = _passfile_default;
+char *passfile;
+char *yppasswd_domain = NULL;
+int no_chsh = 0;
+int no_chfn = 0;
+int allow_additions = 0;
+int multidomain = 0;
+int verbose = 0;
+int resvport = 1;
+int inplace = 0;
+char sockname[] = YP_SOCKNAME;
+
+static void
+terminate(int sig __unused)
+{
+ rpcb_unset(YPPASSWDPROG, YPPASSWDVERS, NULL);
+ rpcb_unset(MASTER_YPPASSWDPROG, MASTER_YPPASSWDVERS, NULL);
+ unlink(sockname);
+ exit(0);
+}
+
+static void
+reload(int sig __unused)
+{
+ load_securenets();
+}
+
+static void
+closedown(int sig __unused)
+{
+ if (_rpcsvcstate == _IDLE) {
+ extern fd_set svc_fdset;
+ static int size;
+ int i, openfd;
+
+ if (_rpcfdtype == SOCK_DGRAM) {
+ unlink(sockname);
+ exit(0);
+ }
+ if (size == 0) {
+ size = getdtablesize();
+ }
+ for (i = 0, openfd = 0; i < size && openfd < 2; i++)
+ if (FD_ISSET(i, &svc_fdset))
+ openfd++;
+ if (openfd <= 1) {
+ unlink(sockname);
+ exit(0);
+ }
+ }
+ if (_rpcsvcstate == _SERVED)
+ _rpcsvcstate = _IDLE;
+
+ (void) signal(SIGALRM, (SIG_PF) closedown);
+ (void) alarm(_RPCSVC_CLOSEDOWN/2);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "%s\n%s\n",
+"usage: rpc.yppasswdd [-t master.passwd file] [-d domain] [-p path] [-s]",
+" [-f] [-m] [-i] [-a] [-v] [-u] [-h]");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct rlimit rlim;
+ SVCXPRT *transp = NULL;
+ struct sockaddr_in saddr;
+ socklen_t asize = sizeof (saddr);
+ struct netconfig *nconf;
+ struct sigaction sa;
+ void *localhandle;
+ int ch;
+ char *mastername;
+ char myname[MAXHOSTNAMELEN + 2];
+ int maxrec = RPC_MAXDATASIZE;
+
+ extern int debug;
+
+ debug = 1;
+
+ while ((ch = getopt(argc, argv, "t:d:p:sfamuivh")) != -1) {
+ switch (ch) {
+ case 't':
+ passfile_default = optarg;
+ break;
+ case 'd':
+ yppasswd_domain = optarg;
+ break;
+ case 's':
+ no_chsh++;
+ break;
+ case 'f':
+ no_chfn++;
+ break;
+ case 'p':
+ yp_dir = optarg;
+ break;
+ case 'a':
+ allow_additions++;
+ break;
+ case 'm':
+ multidomain++;
+ break;
+ case 'i':
+ inplace++;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'u':
+ resvport = 0;
+ break;
+ default:
+ case 'h':
+ usage();
+ break;
+ }
+ }
+
+ if (yppasswd_domain == NULL) {
+ if (yp_get_default_domain(&yppasswd_domain)) {
+ yp_error("no domain specified and system domain \
+name isn't set -- aborting");
+ usage();
+ }
+ }
+
+ load_securenets();
+
+ if (getrpcport(_localhost, YPPROG, YPVERS, IPPROTO_UDP) <= 0) {
+ yp_error("no ypserv processes registered with local portmap");
+ yp_error("this host is not an NIS server -- aborting");
+ exit(1);
+ }
+
+ if ((mastername = ypxfr_get_master(yppasswd_domain,
+ _passwd_byname, _localhost, 0)) == NULL) {
+ yp_error("can't get name of NIS master server for domain %s",
+ yppasswd_domain);
+ exit(1);
+ }
+
+ if (gethostname((char *)&myname, sizeof(myname)) == -1) {
+ yp_error("can't get local hostname: %s", strerror(errno));
+ exit(1);
+ }
+
+ if (strncasecmp(mastername, (char *)&myname, sizeof(myname))) {
+ yp_error("master of %s is %s, but we are %s",
+ "passwd.byname", mastername, myname);
+ yp_error("this host is not the NIS master server for \
+the %s domain -- aborting", yppasswd_domain);
+ exit(1);
+ }
+
+ debug = 0;
+
+ if (getsockname(0, (struct sockaddr *)&saddr, &asize) == 0) {
+ socklen_t ssize = sizeof (int);
+ if (saddr.sin_family != AF_INET)
+ exit(1);
+ if (getsockopt(0, SOL_SOCKET, SO_TYPE,
+ (char *)&_rpcfdtype, &ssize) == -1)
+ exit(1);
+ _rpcpmstart = 1;
+ }
+
+ if (!debug && _rpcpmstart == 0) {
+ if (daemon(0,0)) {
+ err(1,"cannot fork");
+ }
+ }
+ openlog("rpc.yppasswdd", LOG_PID, LOG_DAEMON);
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_flags = SA_NOCLDWAIT;
+ sigaction(SIGCHLD, &sa, NULL);
+
+ rpcb_unset(YPPASSWDPROG, YPPASSWDVERS, NULL);
+ rpcb_unset(MASTER_YPPASSWDPROG, MASTER_YPPASSWDVERS, NULL);
+
+ rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
+
+ if (svc_create(yppasswdprog_1, YPPASSWDPROG, YPPASSWDVERS, "netpath") == 0) {
+ yp_error("cannot create yppasswd service.");
+ exit(1);
+ }
+ if (svc_create(master_yppasswdprog_1, MASTER_YPPASSWDPROG,
+ MASTER_YPPASSWDVERS, "netpath") == 0) {
+ yp_error("cannot create master_yppasswd service.");
+ exit(1);
+ }
+
+ nconf = NULL;
+ localhandle = setnetconfig();
+ while ((nconf = getnetconfig(localhandle)) != NULL) {
+ if (nconf->nc_protofmly != NULL &&
+ strcmp(nconf->nc_protofmly, NC_LOOPBACK) == 0)
+ break;
+ }
+ if (nconf == NULL) {
+ yp_error("getnetconfigent unix: %s", nc_sperror());
+ exit(1);
+ }
+ unlink(sockname);
+ transp = svcunix_create(RPC_ANYSOCK, 0, 0, sockname);
+ if (transp == NULL) {
+ yp_error("cannot create AF_LOCAL service.");
+ exit(1);
+ }
+ if (!svc_reg(transp, MASTER_YPPASSWDPROG, MASTER_YPPASSWDVERS,
+ master_yppasswdprog_1, nconf)) {
+ yp_error("unable to register (MASTER_YPPASSWDPROG, \
+ MASTER_YPPASSWDVERS, unix).");
+ exit(1);
+ }
+ endnetconfig(localhandle);
+
+ /* Only root may connect() to the AF_UNIX link. */
+ if (chmod(sockname, 0))
+ err(1, "chmod of %s failed", sockname);
+
+ if (transp == (SVCXPRT *)NULL) {
+ yp_error("could not create a handle");
+ exit(1);
+ }
+ if (_rpcpmstart) {
+ (void) signal(SIGALRM, (SIG_PF) closedown);
+ (void) alarm(_RPCSVC_CLOSEDOWN/2);
+ }
+
+ /* Unlimited resource limits. */
+ rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
+ (void)setrlimit(RLIMIT_CPU, &rlim);
+ (void)setrlimit(RLIMIT_FSIZE, &rlim);
+ (void)setrlimit(RLIMIT_STACK, &rlim);
+ (void)setrlimit(RLIMIT_DATA, &rlim);
+ (void)setrlimit(RLIMIT_RSS, &rlim);
+
+ /* Don't drop core (not really necessary, but GP's). */
+ rlim.rlim_cur = rlim.rlim_max = 0;
+ (void)setrlimit(RLIMIT_CORE, &rlim);
+
+ /* Turn off signals. */
+ (void)signal(SIGALRM, SIG_IGN);
+ (void)signal(SIGHUP, (SIG_PF) reload);
+ (void)signal(SIGINT, SIG_IGN);
+ (void)signal(SIGPIPE, SIG_IGN);
+ (void)signal(SIGQUIT, SIG_IGN);
+ (void)signal(SIGTERM, (SIG_PF) terminate);
+
+ svc_run();
+ yp_error("svc_run returned");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.sbin/rpc.yppasswdd/yppasswdd_server.c b/usr.sbin/rpc.yppasswdd/yppasswdd_server.c
new file mode 100644
index 0000000..446b130
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/yppasswdd_server.c
@@ -0,0 +1,918 @@
+/*
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/fcntl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <arpa/inet.h>
+#include <netinet/in.h>
+
+#include <ctype.h>
+#include <db.h>
+#include <dirent.h>
+#include <errno.h>
+#include <limits.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libgen.h>
+#include <libutil.h>
+
+#include <rpc/rpc.h>
+#include <rpcsvc/yp.h>
+struct dom_binding;
+#include <rpcsvc/ypclnt.h>
+#include "yppasswdd_extern.h"
+#include "yppasswd.h"
+#include "yppasswd_private.h"
+#include "ypxfr_extern.h"
+#include "yp_extern.h"
+
+static struct passwd yp_password;
+
+static void
+xlate_passwd(struct x_master_passwd *xpwd, struct passwd *pwd)
+{
+ pwd->pw_name = xpwd->pw_name;
+ pwd->pw_passwd = xpwd->pw_passwd;
+ pwd->pw_uid = xpwd->pw_uid;
+ pwd->pw_gid = xpwd->pw_gid;
+ pwd->pw_change = xpwd->pw_change;
+ pwd->pw_class = xpwd->pw_class;
+ pwd->pw_gecos = xpwd->pw_gecos;
+ pwd->pw_dir = xpwd->pw_dir;
+ pwd->pw_shell = xpwd->pw_shell;
+ pwd->pw_expire = xpwd->pw_expire;
+ pwd->pw_fields = xpwd->pw_fields;
+}
+
+static void
+copy_yp_pass(char *p, int x, int m)
+{
+ char *t, *s = p;
+ static char *buf;
+
+ yp_password.pw_fields = 0;
+
+ buf = realloc(buf, m + 10);
+ bzero(buf, m + 10);
+
+ /* Turn all colons into NULLs */
+ while (strchr(s, ':')) {
+ s = (strchr(s, ':') + 1);
+ *(s - 1)= '\0';
+ }
+
+ t = buf;
+#define EXPAND(e) e = t; while ((*t++ = *p++));
+ EXPAND(yp_password.pw_name);
+ yp_password.pw_fields |= _PWF_NAME;
+ EXPAND(yp_password.pw_passwd);
+ yp_password.pw_fields |= _PWF_PASSWD;
+ yp_password.pw_uid = atoi(p);
+ p += (strlen(p) + 1);
+ yp_password.pw_fields |= _PWF_UID;
+ yp_password.pw_gid = atoi(p);
+ p += (strlen(p) + 1);
+ yp_password.pw_fields |= _PWF_GID;
+ if (x) {
+ EXPAND(yp_password.pw_class);
+ yp_password.pw_fields |= _PWF_CLASS;
+ yp_password.pw_change = atol(p);
+ p += (strlen(p) + 1);
+ yp_password.pw_fields |= _PWF_CHANGE;
+ yp_password.pw_expire = atol(p);
+ p += (strlen(p) + 1);
+ yp_password.pw_fields |= _PWF_EXPIRE;
+ }
+ EXPAND(yp_password.pw_gecos);
+ yp_password.pw_fields |= _PWF_GECOS;
+ EXPAND(yp_password.pw_dir);
+ yp_password.pw_fields |= _PWF_DIR;
+ EXPAND(yp_password.pw_shell);
+ yp_password.pw_fields |= _PWF_SHELL;
+
+ return;
+}
+
+static int
+validchars(char *arg)
+{
+ size_t i;
+
+ for (i = 0; i < strlen(arg); i++) {
+ if (iscntrl(arg[i])) {
+ yp_error("string contains a control character");
+ return(1);
+ }
+ if (arg[i] == ':') {
+ yp_error("string contains a colon");
+ return(1);
+ }
+ /* Be evil: truncate strings with \n in them silently. */
+ if (arg[i] == '\n') {
+ arg[i] = '\0';
+ return(0);
+ }
+ }
+ return(0);
+}
+
+static int
+validate_master(struct passwd *opw __unused, struct x_master_passwd *npw)
+{
+
+ if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') {
+ yp_error("client tried to modify an NIS entry");
+ return(1);
+ }
+
+ if (validchars(npw->pw_shell)) {
+ yp_error("specified shell contains invalid characters");
+ return(1);
+ }
+
+ if (validchars(npw->pw_gecos)) {
+ yp_error("specified gecos field contains invalid characters");
+ return(1);
+ }
+
+ if (validchars(npw->pw_passwd)) {
+ yp_error("specified password contains invalid characters");
+ return(1);
+ }
+ return(0);
+}
+
+static int
+validate(struct passwd *opw, struct x_passwd *npw)
+{
+
+ if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') {
+ yp_error("client tried to modify an NIS entry");
+ return(1);
+ }
+
+ if ((uid_t)npw->pw_uid != opw->pw_uid) {
+ yp_error("UID mismatch: client says user %s has UID %d",
+ npw->pw_name, npw->pw_uid);
+ yp_error("database says user %s has UID %d", opw->pw_name,
+ opw->pw_uid);
+ return(1);
+ }
+
+ if ((gid_t)npw->pw_gid != opw->pw_gid) {
+ yp_error("GID mismatch: client says user %s has GID %d",
+ npw->pw_name, npw->pw_gid);
+ yp_error("database says user %s has GID %d", opw->pw_name,
+ opw->pw_gid);
+ return(1);
+ }
+
+ /*
+ * Don't allow the user to shoot himself in the foot,
+ * even on purpose.
+ */
+ if (!ok_shell(npw->pw_shell)) {
+ yp_error("%s is not a valid shell", npw->pw_shell);
+ return(1);
+ }
+
+ if (validchars(npw->pw_shell)) {
+ yp_error("specified shell contains invalid characters");
+ return(1);
+ }
+
+ if (validchars(npw->pw_gecos)) {
+ yp_error("specified gecos field contains invalid characters");
+ return(1);
+ }
+
+ if (validchars(npw->pw_passwd)) {
+ yp_error("specified password contains invalid characters");
+ return(1);
+ }
+ return(0);
+}
+
+/*
+ * Kludge alert:
+ * In order to have one rpc.yppasswdd support multiple domains,
+ * we have to cheat: we search each directory under /var/yp
+ * and try to match the user in each master.passwd.byname
+ * map that we find. If the user matches (username, uid and gid
+ * all agree), then we use that domain. If we match the user in
+ * more than one database, we must abort.
+ */
+static char *
+find_domain(struct x_passwd *pw)
+{
+ struct stat statbuf;
+ struct dirent *dirp;
+ DIR *dird;
+ char yp_mapdir[MAXPATHLEN + 2];
+ static char domain[YPMAXDOMAIN];
+ char *tmp = NULL;
+ DBT key, data;
+ int hit = 0;
+
+ yp_error("performing multidomain lookup");
+
+ if ((dird = opendir(yp_dir)) == NULL) {
+ yp_error("opendir(%s) failed: %s", yp_dir, strerror(errno));
+ return(NULL);
+ }
+
+ while ((dirp = readdir(dird)) != NULL) {
+ snprintf(yp_mapdir, sizeof yp_mapdir, "%s/%s",
+ yp_dir, dirp->d_name);
+ if (stat(yp_mapdir, &statbuf) < 0) {
+ yp_error("stat(%s) failed: %s", yp_mapdir,
+ strerror(errno));
+ closedir(dird);
+ return(NULL);
+ }
+ if (S_ISDIR(statbuf.st_mode)) {
+ tmp = (char *)dirp->d_name;
+ key.data = pw->pw_name;
+ key.size = strlen(pw->pw_name);
+
+ if (yp_get_record(tmp,"master.passwd.byname",
+ &key, &data, 0) != YP_TRUE) {
+ continue;
+ }
+ *((char *)data.data + data.size) = '\0';
+ copy_yp_pass(data.data, 1, data.size);
+ if (yp_password.pw_uid == (uid_t)pw->pw_uid &&
+ yp_password.pw_gid == (gid_t)pw->pw_gid) {
+ hit++;
+ snprintf(domain, YPMAXDOMAIN, "%s", tmp);
+ }
+ }
+ }
+
+ closedir(dird);
+ if (hit > 1) {
+ yp_error("found same user in two different domains");
+ return(NULL);
+ } else
+ return((char *)&domain);
+}
+
+static const char *maps[] = {
+ "master.passwd.byname",
+ "master.passwd.byuid",
+ "passwd.byname",
+ "passwd.byuid"
+};
+
+static const char *formats[] = {
+ "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s",
+ "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s",
+ "%s:%s:%d:%d:%s:%s:%s",
+ "%s:%s:%d:%d:%s:%s:%s"
+};
+
+static int
+update_inplace(struct passwd *pw, char *domain)
+{
+ DB *dbp = NULL;
+ DBT key = { NULL, 0 };
+ DBT data = { NULL, 0 };
+ char pwbuf[YPMAXRECORD];
+ char keybuf[20];
+ int i;
+ char *ptr = NULL;
+ static char yp_last[] = "YP_LAST_MODIFIED";
+ char yplastbuf[YPMAXRECORD];
+
+ snprintf(yplastbuf, sizeof yplastbuf, "%llu",
+ (unsigned long long)time(NULL));
+
+ for (i = 0; i < 4; i++) {
+
+ if (i % 2) {
+ snprintf(keybuf, sizeof keybuf,
+ "%llu", (unsigned long long)pw->pw_uid);
+ key.data = &keybuf;
+ key.size = strlen(keybuf);
+ } else {
+ key.data = pw->pw_name;
+ key.size = strlen(pw->pw_name);
+ }
+
+ /*
+ * XXX The passwd.byname and passwd.byuid maps come in
+ * two flavors: secure and insecure. The secure version
+ * has a '*' in the password field whereas the insecure one
+ * has a real crypted password. The maps will be insecure
+ * if they were built with 'unsecure = TRUE' enabled in
+ * /var/yp/Makefile, but we'd have no way of knowing if
+ * this has been done unless we were to try parsing the
+ * Makefile, which is a disgusting thought. Instead, we
+ * read the records from the maps, skip to the first ':'
+ * in them, and then look at the character immediately
+ * following it. If it's an '*' then the map is 'secure'
+ * and we must not insert a real password into the pw_passwd
+ * field. If it's not an '*', then we put the real crypted
+ * password in.
+ */
+ if (yp_get_record(domain,maps[i],&key,&data,1) != YP_TRUE) {
+ yp_error("couldn't read %s/%s: %s", domain,
+ maps[i], strerror(errno));
+ return(1);
+ }
+
+ if ((ptr = strchr(data.data, ':')) == NULL) {
+ yp_error("no colon in passwd record?!");
+ return(1);
+ }
+
+ /*
+ * XXX Supposing we have more than one user with the same
+ * UID? (Or more than one user with the same name?) We could
+ * end up modifying the wrong record if were not careful.
+ */
+ if (i % 2) {
+ if (strncmp(data.data, pw->pw_name,
+ strlen(pw->pw_name))) {
+ yp_error("warning: found entry for UID %d \
+in map %s@%s with wrong name (%.*s)", pw->pw_uid, maps[i], domain,
+ (int)(ptr - (char *)data.data),
+ (char *)data.data);
+ yp_error("there may be more than one user \
+with the same UID - continuing");
+ continue;
+ }
+ } else {
+ /*
+ * We're really being ultra-paranoid here.
+ * This is generally a 'can't happen' condition.
+ */
+ snprintf(pwbuf, sizeof pwbuf, ":%d:%d:", pw->pw_uid,
+ pw->pw_gid);
+ if (!strstr(data.data, pwbuf)) {
+ yp_error("warning: found entry for user %s \
+in map %s@%s with wrong UID", pw->pw_name, maps[i], domain);
+ yp_error("there may be more than one user \
+with the same name - continuing");
+ continue;
+ }
+ }
+
+ if (i < 2) {
+ snprintf(pwbuf, sizeof pwbuf, formats[i],
+ pw->pw_name, pw->pw_passwd, pw->pw_uid,
+ pw->pw_gid, pw->pw_class, pw->pw_change,
+ pw->pw_expire, pw->pw_gecos, pw->pw_dir,
+ pw->pw_shell);
+ } else {
+ snprintf(pwbuf, sizeof pwbuf, formats[i],
+ pw->pw_name, *(ptr+1) == '*' ? "*" : pw->pw_passwd,
+ pw->pw_uid, pw->pw_gid, pw->pw_gecos, pw->pw_dir,
+ pw->pw_shell);
+ }
+
+#define FLAGS O_RDWR|O_CREAT
+
+ if ((dbp = yp_open_db_rw(domain, maps[i], FLAGS)) == NULL) {
+ yp_error("couldn't open %s/%s r/w: %s",domain,
+ maps[i],strerror(errno));
+ return(1);
+ }
+
+ data.data = pwbuf;
+ data.size = strlen(pwbuf);
+
+ if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) {
+ yp_error("failed to update record in %s/%s", domain,
+ maps[i]);
+ (void)(dbp->close)(dbp);
+ return(1);
+ }
+
+ key.data = yp_last;
+ key.size = strlen(yp_last);
+ data.data = (char *)&yplastbuf;
+ data.size = strlen(yplastbuf);
+
+ if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) {
+ yp_error("failed to update timestamp in %s/%s", domain,
+ maps[i]);
+ (void)(dbp->close)(dbp);
+ return(1);
+ }
+
+ (void)(dbp->close)(dbp);
+ }
+
+ return(0);
+}
+
+int *
+yppasswdproc_update_1_svc(yppasswd *argp, struct svc_req *rqstp)
+{
+ static int result;
+ struct sockaddr_in *rqhost;
+ DBT key, data;
+ int rval = 0;
+ int pfd, tfd;
+ int pid;
+ int passwd_changed = 0;
+ int shell_changed = 0;
+ int gecos_changed = 0;
+ char *oldshell = NULL;
+ char *oldgecos = NULL;
+ char *passfile_hold;
+ char passfile_buf[MAXPATHLEN + 2];
+ char passfile_hold_buf[MAXPATHLEN + 2];
+ char *domain = yppasswd_domain;
+ static struct sockaddr_in clntaddr;
+ static struct timeval t_saved, t_test;
+
+ /*
+ * Normal user updates always use the 'default' master.passwd file.
+ */
+
+ passfile = passfile_default;
+ result = 1;
+
+ rqhost = svc_getcaller(rqstp->rq_xprt);
+
+ gettimeofday(&t_test, NULL);
+ if (!bcmp(rqhost, &clntaddr, sizeof *rqhost) &&
+ t_test.tv_sec > t_saved.tv_sec &&
+ t_test.tv_sec - t_saved.tv_sec < 300) {
+
+ bzero(&clntaddr, sizeof clntaddr);
+ bzero(&t_saved, sizeof t_saved);
+ return(NULL);
+ }
+
+ bcopy(rqhost, &clntaddr, sizeof clntaddr);
+ gettimeofday(&t_saved, NULL);
+
+ if (yp_access(resvport ? "master.passwd.byname" : NULL, rqstp)) {
+ yp_error("rejected update request from unauthorized host");
+ svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
+ return(&result);
+ }
+
+ /*
+ * Step one: find the user. (It's kinda pointless to
+ * proceed if the user doesn't exist.) We look for the
+ * user in the master.passwd.byname database, _NOT_ by
+ * using getpwent() and friends! We can't use getpwent()
+ * since the NIS master server is not guaranteed to be
+ * configured as an NIS client.
+ */
+
+ if (multidomain) {
+ if ((domain = find_domain(&argp->newpw)) == NULL) {
+ yp_error("multidomain lookup failed - aborting update");
+ return(&result);
+ } else
+ yp_error("updating user %s in domain %s",
+ argp->newpw.pw_name, domain);
+ }
+
+ key.data = argp->newpw.pw_name;
+ key.size = strlen(argp->newpw.pw_name);
+
+ if ((rval = yp_get_record(domain,"master.passwd.byname",
+ &key, &data, 0)) != YP_TRUE) {
+ if (rval == YP_NOKEY) {
+ yp_error("user %s not found in passwd database",
+ argp->newpw.pw_name);
+ } else {
+ yp_error("database access error: %s",
+ yperr_string(rval));
+ }
+ return(&result);
+ }
+
+ /* Nul terminate, please. */
+ *((char *)data.data + data.size) = '\0';
+
+ copy_yp_pass(data.data, 1, data.size);
+
+ /* Step 2: check that the supplied oldpass is valid. */
+
+ if (strcmp(crypt(argp->oldpass, yp_password.pw_passwd),
+ yp_password.pw_passwd)) {
+ yp_error("rejected change attempt -- bad password");
+ yp_error("client address: %s username: %s",
+ inet_ntoa(rqhost->sin_addr),
+ argp->newpw.pw_name);
+ return(&result);
+ }
+
+ /* Step 3: validate the arguments passed to us by the client. */
+
+ if (validate(&yp_password, &argp->newpw)) {
+ yp_error("rejecting change attempt: bad arguments");
+ yp_error("client address: %s username: %s",
+ inet_ntoa(rqhost->sin_addr),
+ argp->newpw.pw_name);
+ svcerr_decode(rqstp->rq_xprt);
+ return(&result);
+ }
+
+ /* Step 4: update the user's passwd structure. */
+
+ if (!no_chsh && strcmp(argp->newpw.pw_shell, yp_password.pw_shell)) {
+ oldshell = yp_password.pw_shell;
+ yp_password.pw_shell = argp->newpw.pw_shell;
+ shell_changed++;
+ }
+
+
+ if (!no_chfn && strcmp(argp->newpw.pw_gecos, yp_password.pw_gecos)) {
+ oldgecos = yp_password.pw_gecos;
+ yp_password.pw_gecos = argp->newpw.pw_gecos;
+ gecos_changed++;
+ }
+
+ if (strcmp(argp->newpw.pw_passwd, yp_password.pw_passwd)) {
+ yp_password.pw_passwd = argp->newpw.pw_passwd;
+ yp_password.pw_change = 0;
+ passwd_changed++;
+ }
+
+ /*
+ * If the caller specified a domain other than our 'default'
+ * domain, change the path to master.passwd accordingly.
+ */
+
+ if (strcmp(domain, yppasswd_domain)) {
+ snprintf(passfile_buf, sizeof(passfile_buf),
+ "%s/%s/master.passwd", yp_dir, domain);
+ passfile = (char *)&passfile_buf;
+ }
+
+ /*
+ * Create a filename to hold the original master.passwd
+ * so if our call to yppwupdate fails we can roll back
+ */
+ snprintf(passfile_hold_buf, sizeof(passfile_hold_buf),
+ "%s.hold", passfile);
+ passfile_hold = (char *)&passfile_hold_buf;
+
+
+ /* Step 5: make a new password file with the updated info. */
+
+ if (pw_init(dirname(passfile), passfile)) {
+ yp_error("pw_init() failed");
+ return &result;
+ }
+ if ((pfd = pw_lock()) == -1) {
+ pw_fini();
+ yp_error("pw_lock() failed");
+ return &result;
+ }
+ if ((tfd = pw_tmp(-1)) == -1) {
+ pw_fini();
+ yp_error("pw_tmp() failed");
+ return &result;
+ }
+ if (pw_copy(pfd, tfd, &yp_password, NULL) == -1) {
+ pw_fini();
+ yp_error("pw_copy() failed");
+ return &result;
+ }
+ if (rename(passfile, passfile_hold) == -1) {
+ pw_fini();
+ yp_error("rename of %s to %s failed", passfile,
+ passfile_hold);
+ return &result;
+ }
+
+ if (strcmp(passfile, _PATH_MASTERPASSWD) == 0) {
+ /*
+ * NIS server is exporting the system's master.passwd.
+ * Call pw_mkdb to rebuild passwd and the .db files
+ */
+ if (pw_mkdb(yp_password.pw_name) == -1) {
+ pw_fini();
+ yp_error("pw_mkdb() failed");
+ rename(passfile_hold, passfile);
+ return &result;
+ }
+ } else {
+ /*
+ * NIS server is exporting a private master.passwd.
+ * Rename tempfile into final location
+ */
+ if (rename(pw_tempname(), passfile) == -1) {
+ pw_fini();
+ yp_error("rename of %s to %s failed",
+ pw_tempname(), passfile);
+ rename(passfile_hold, passfile);
+ return &result;
+ }
+ }
+
+ pw_fini();
+
+ if (inplace) {
+ if ((rval = update_inplace(&yp_password, domain))) {
+ yp_error("inplace update failed -- rebuilding maps");
+ }
+ }
+
+ switch ((pid = fork())) {
+ case 0:
+ if (inplace && !rval) {
+ execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
+ yppasswd_domain, "pushpw", (char *)NULL);
+ } else {
+ execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
+ yppasswd_domain, (char *)NULL);
+ }
+ yp_error("couldn't exec map update process: %s",
+ strerror(errno));
+ unlink(passfile);
+ rename(passfile_hold, passfile);
+ exit(1);
+ break;
+ case -1:
+ yp_error("fork() failed: %s", strerror(errno));
+ unlink(passfile);
+ rename(passfile_hold, passfile);
+ return(&result);
+ break;
+ default:
+ unlink(passfile_hold);
+ break;
+ }
+
+ if (verbose) {
+ yp_error("update completed for user %s (uid %d) in %s:",
+ argp->newpw.pw_name, argp->newpw.pw_uid, passfile);
+
+ if (passwd_changed)
+ yp_error("password changed");
+
+ if (gecos_changed)
+ yp_error("gecos changed ('%s' -> '%s')",
+ oldgecos, argp->newpw.pw_gecos);
+
+ if (shell_changed)
+ yp_error("shell changed ('%s' -> '%s')",
+ oldshell, argp->newpw.pw_shell);
+ }
+
+ result = 0;
+ return (&result);
+}
+
+/*
+ * Note that this function performs a little less sanity checking
+ * than the last one. Since only the superuser is allowed to use it,
+ * it is assumed that the caller knows what he's doing.
+ */
+int *
+yppasswdproc_update_master_1_svc(master_yppasswd *argp,
+ struct svc_req *rqstp)
+{
+ static int result;
+ int pfd, tfd;
+ int pid;
+ uid_t uid;
+ int rval = 0;
+ DBT key, data;
+ char *passfile_hold;
+ char passfile_buf[MAXPATHLEN + 2];
+ char passfile_hold_buf[MAXPATHLEN + 2];
+ struct sockaddr_in *rqhost;
+ SVCXPRT *transp;
+ struct passwd newpasswd;
+
+ result = 1;
+ transp = rqstp->rq_xprt;
+
+ /*
+ * NO AF_INET CONNETCIONS ALLOWED!
+ */
+ rqhost = svc_getcaller(transp);
+ if (rqhost->sin_family != AF_UNIX) {
+ yp_error("Alert! %s/%d attempted to use superuser-only \
+procedure!\n", inet_ntoa(rqhost->sin_addr), rqhost->sin_port);
+ svcerr_auth(transp, AUTH_BADCRED);
+ return(&result);
+ }
+
+ if (rqstp->rq_cred.oa_flavor != AUTH_SYS) {
+ yp_error("caller didn't send proper credentials");
+ svcerr_auth(transp, AUTH_BADCRED);
+ return(&result);
+ }
+
+ if (__rpc_get_local_uid(transp, &uid) < 0) {
+ yp_error("caller didn't send proper credentials");
+ svcerr_auth(transp, AUTH_BADCRED);
+ return(&result);
+ }
+
+ if (uid) {
+ yp_error("caller euid is %d, expecting 0 -- rejecting request",
+ uid);
+ svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
+ return(&result);
+ }
+
+ passfile = passfile_default;
+
+ key.data = argp->newpw.pw_name;
+ key.size = strlen(argp->newpw.pw_name);
+
+ /*
+ * The superuser may add entries to the passwd maps if
+ * rpc.yppasswdd is started with the -a flag. Paranoia
+ * prevents me from allowing additions by default.
+ */
+ if ((rval = yp_get_record(argp->domain, "master.passwd.byname",
+ &key, &data, 0)) != YP_TRUE) {
+ if (rval == YP_NOKEY) {
+ yp_error("user %s not found in passwd database",
+ argp->newpw.pw_name);
+ if (allow_additions)
+ yp_error("notice: adding user %s to \
+master.passwd database for domain %s", argp->newpw.pw_name, argp->domain);
+ else
+ yp_error("restart rpc.yppasswdd with the -a flag to \
+allow additions to be made to the password database");
+ } else {
+ yp_error("database access error: %s",
+ yperr_string(rval));
+ }
+ if (!allow_additions)
+ return(&result);
+ } else {
+
+ /* Nul terminate, please. */
+ *((char *)data.data + data.size) = '\0';
+
+ copy_yp_pass(data.data, 1, data.size);
+ }
+
+ /*
+ * Perform a small bit of sanity checking.
+ */
+ if (validate_master(rval == YP_TRUE ? &yp_password:NULL,&argp->newpw)){
+ yp_error("rejecting update attempt for %s: bad arguments",
+ argp->newpw.pw_name);
+ return(&result);
+ }
+
+ /*
+ * If the caller specified a domain other than our 'default'
+ * domain, change the path to master.passwd accordingly.
+ */
+
+ if (strcmp(argp->domain, yppasswd_domain)) {
+ snprintf(passfile_buf, sizeof(passfile_buf),
+ "%s/%s/master.passwd", yp_dir, argp->domain);
+ passfile = (char *)&passfile_buf;
+ }
+
+ /*
+ * Create a filename to hold the original master.passwd
+ * so if our call to yppwupdate fails we can roll back
+ */
+ snprintf(passfile_hold_buf, sizeof(passfile_hold_buf),
+ "%s.hold", passfile);
+ passfile_hold = (char *)&passfile_hold_buf;
+
+ if (pw_init(dirname(passfile), passfile)) {
+ yp_error("pw_init() failed");
+ return &result;
+ }
+ if ((pfd = pw_lock()) == -1) {
+ pw_fini();
+ yp_error("pw_lock() failed");
+ return &result;
+ }
+ if ((tfd = pw_tmp(-1)) == -1) {
+ pw_fini();
+ yp_error("pw_tmp() failed");
+ return &result;
+ }
+ xlate_passwd(&argp->newpw, &newpasswd);
+ if (pw_copy(pfd, tfd, &newpasswd, NULL) == -1) {
+ pw_fini();
+ yp_error("pw_copy() failed");
+ return &result;
+ }
+ if (rename(passfile, passfile_hold) == -1) {
+ pw_fini();
+ yp_error("rename of %s to %s failed", passfile,
+ passfile_hold);
+ return &result;
+ }
+ if (strcmp(passfile, _PATH_MASTERPASSWD) == 0) {
+ /*
+ * NIS server is exporting the system's master.passwd.
+ * Call pw_mkdb to rebuild passwd and the .db files
+ */
+ if (pw_mkdb(argp->newpw.pw_name) == -1) {
+ pw_fini();
+ yp_error("pw_mkdb() failed");
+ rename(passfile_hold, passfile);
+ return &result;
+ }
+ } else {
+ /*
+ * NIS server is exporting a private master.passwd.
+ * Rename tempfile into final location
+ */
+ if (rename(pw_tempname(), passfile) == -1) {
+ pw_fini();
+ yp_error("rename of %s to %s failed",
+ pw_tempname(), passfile);
+ rename(passfile_hold, passfile);
+ return &result;
+ }
+ }
+ pw_fini();
+
+ if (inplace) {
+ xlate_passwd(&argp->newpw, &newpasswd);
+ if ((rval = update_inplace(&newpasswd, argp->domain))) {
+ yp_error("inplace update failed -- rebuilding maps");
+ }
+ }
+
+ switch ((pid = fork())) {
+ case 0:
+ if (inplace && !rval) {
+ execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
+ argp->domain, "pushpw", (char *)NULL);
+ } else {
+ execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
+ argp->domain, (char *)NULL);
+ }
+ yp_error("couldn't exec map update process: %s",
+ strerror(errno));
+ unlink(passfile);
+ rename(passfile_hold, passfile);
+ exit(1);
+ break;
+ case -1:
+ yp_error("fork() failed: %s", strerror(errno));
+ unlink(passfile);
+ rename(passfile_hold, passfile);
+ return(&result);
+ break;
+ default:
+ unlink(passfile_hold);
+ break;
+ }
+
+ yp_error("performed update of user %s (uid %d) domain %s",
+ argp->newpw.pw_name,
+ argp->newpw.pw_uid,
+ argp->domain);
+
+ result = 0;
+ return(&result);
+}
diff --git a/usr.sbin/rpc.yppasswdd/yppwupdate b/usr.sbin/rpc.yppasswdd/yppwupdate
new file mode 100644
index 0000000..a9db8f7
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/yppwupdate
@@ -0,0 +1,34 @@
+#!/bin/sh
+#
+# This script is invoked by rpc.yppasswdd to update the password
+# maps after the master password file has been modified. It expects
+# to be passed two arguments: the name of the master.passwd template
+# file that was modified by the server, and the name of the domain to
+# update. These are passed to /var/yp/Makefile.
+#
+# Comment out the LOG=yes line to disable logging.
+#
+# $FreeBSD$
+#
+
+PATH=/bin:/usr/bin; export PATH
+LOG=yes
+LOGFILE=/var/yp/ypupdate.log
+
+umask 077
+
+if [ ! -f $LOGFILE ];
+then
+ touch $LOGFILE
+ echo "# Edit /usr/libexec/yppwupdate to disable" >> $LOGFILE
+ echo "# logging to this file from yppasswdd." >> $LOGFILE
+ echo -n "# Log started on: " >> $LOGFILE
+ date >> $LOGFILE
+fi
+
+if [ ! $LOG ];
+then
+ cd /var/yp && make MASTER_PASSWD=$1 UPDATE_DOMAIN=$2 $3 2>&1
+else
+ cd /var/yp && make MASTER_PASSWD=$1 UPDATE_DOMAIN=$2 $3 >> $LOGFILE 2>&1
+fi
diff --git a/usr.sbin/rpc.ypupdated/Makefile b/usr.sbin/rpc.ypupdated/Makefile
new file mode 100644
index 0000000..4fe19c9
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/Makefile
@@ -0,0 +1,32 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../ypserv ${.CURDIR}/../../libexec/ypxfr
+
+PROG= rpc.ypupdated
+NO_MAN=
+SRCS= ypupdate_prot_svc.c ypupdate_prot.h ypupdated_main.c \
+ yp_error.c update.c ypupdated_server.c \
+ yp_dblookup.c yp_dbwrite.c yp_dbdelete.c yp_dbupdate.c
+
+#CFLAGS+= -DYP
+CFLAGS+= -I${.CURDIR}/../ypserv -I. -I${.CURDIR}/../../libexec/ypxfr
+
+DPADD= ${LIBRPCSVC}
+LDADD= -lrpcsvc
+
+CLEANFILES= ypupdate_prot_svc.c ypupdate_prot.h
+
+RPCDIR= ${DESTDIR}/usr/include/rpcsvc
+RPCGEN= rpcgen -I -C
+
+# We need to remove the 'static' keyword from _rpcsvcstate so that
+# ypupdated_main.c can see it.
+ypupdate_prot_svc.c: ${RPCDIR}/ypupdate_prot.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -m ${RPCDIR}/ypupdate_prot.x | \
+ sed s/"static int _rpcsvcstate"/"int _rpcsvcstate"/g > ${.TARGET}
+
+ypupdate_prot.h: ${RPCDIR}/ypupdate_prot.x
+ ${RPCGEN} -h -o ${.TARGET} ${RPCDIR}/ypupdate_prot.x
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rpc.ypupdated/update.c b/usr.sbin/rpc.ypupdated/update.c
new file mode 100644
index 0000000..72a7128
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/update.c
@@ -0,0 +1,330 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user or with the express written consent of
+ * Sun Microsystems, Inc.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)update.c 1.2 91/03/11 Copyr 1986 Sun Micro";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * Copyright (C) 1986, 1989, Sun Microsystems, Inc.
+ */
+
+/*
+ * Administrative tool to add a new user to the publickey database
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <rpc/rpc.h>
+#include <rpc/key_prot.h>
+#ifdef YP
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+#include <sys/wait.h>
+#include <netdb.h>
+#endif /* YP */
+#include <pwd.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <stdlib.h>
+#include "ypupdated_extern.h"
+
+#ifdef YP
+#define MAXMAPNAMELEN 256
+#else
+#define YPOP_CHANGE 1 /* change, do not add */
+#define YPOP_INSERT 2 /* add, do not change */
+#define YPOP_DELETE 3 /* delete this entry */
+#define YPOP_STORE 4 /* add, or change */
+#endif
+
+#ifdef YP
+static char SHELL[] = "/bin/sh";
+static char YPDBPATH[]="/var/yp"; /* This is defined but not used! */
+static char PKMAP[] = "publickey.byname";
+static char UPDATEFILE[] = "updaters";
+static char PKFILE[] = "/etc/publickey";
+#endif /* YP */
+
+#ifdef YP
+static int _openchild(char *, FILE **, FILE **);
+
+/*
+ * Determine if requester is allowed to update the given map,
+ * and update it if so. Returns the yp status, which is zero
+ * if there is no access violation.
+ */
+int
+mapupdate(char *requester, char *mapname, u_int op, u_int keylen, char *key,
+ u_int datalen, char *data)
+{
+ char updater[MAXMAPNAMELEN + 40];
+ FILE *childargs;
+ FILE *childrslt;
+#ifdef WEXITSTATUS
+ int status;
+#else
+ union wait status;
+#endif
+ pid_t pid;
+ u_int yperrno;
+
+
+#ifdef DEBUG
+ printf("%s %s\n", key, data);
+#endif
+ (void)sprintf(updater, "make -s -f %s/%s %s", YPDBPATH, /* !!! */
+ UPDATEFILE, mapname);
+ pid = _openchild(updater, &childargs, &childrslt);
+ if (pid < 0) {
+ return (YPERR_YPERR);
+ }
+
+ /*
+ * Write to child
+ */
+ (void)fprintf(childargs, "%s\n", requester);
+ (void)fprintf(childargs, "%u\n", op);
+ (void)fprintf(childargs, "%u\n", keylen);
+ (void)fwrite(key, (int)keylen, 1, childargs);
+ (void)fprintf(childargs, "\n");
+ (void)fprintf(childargs, "%u\n", datalen);
+ (void)fwrite(data, (int)datalen, 1, childargs);
+ (void)fprintf(childargs, "\n");
+ (void)fclose(childargs);
+
+ /*
+ * Read from child
+ */
+ (void)fscanf(childrslt, "%d", &yperrno);
+ (void)fclose(childrslt);
+
+ (void)wait(&status);
+#ifdef WEXITSTATUS
+ if (WEXITSTATUS(status) != 0)
+#else
+ if (status.w_retcode != 0)
+#endif
+ return (YPERR_YPERR);
+ return (yperrno);
+}
+
+/*
+ * returns pid, or -1 for failure
+ */
+static int
+_openchild(char *command, FILE **fto, FILE **ffrom)
+{
+ int i;
+ pid_t pid;
+ int pdto[2];
+ int pdfrom[2];
+ char *com;
+ struct rlimit rl;
+
+ if (pipe(pdto) < 0) {
+ goto error1;
+ }
+ if (pipe(pdfrom) < 0) {
+ goto error2;
+ }
+ switch (pid = fork()) {
+ case -1:
+ goto error3;
+
+ case 0:
+ /*
+ * child: read from pdto[0], write into pdfrom[1]
+ */
+ (void)close(0);
+ (void)dup(pdto[0]);
+ (void)close(1);
+ (void)dup(pdfrom[1]);
+ getrlimit(RLIMIT_NOFILE, &rl);
+ for (i = rl.rlim_max - 1; i >= 3; i--) {
+ (void) close(i);
+ }
+ com = malloc((unsigned) strlen(command) + 6);
+ if (com == NULL) {
+ _exit(~0);
+ }
+ (void)sprintf(com, "exec %s", command);
+ execl(SHELL, basename(SHELL), "-c", com, (char *)NULL);
+ _exit(~0);
+
+ default:
+ /*
+ * parent: write into pdto[1], read from pdfrom[0]
+ */
+ *fto = fdopen(pdto[1], "w");
+ (void)close(pdto[0]);
+ *ffrom = fdopen(pdfrom[0], "r");
+ (void)close(pdfrom[1]);
+ break;
+ }
+ return (pid);
+
+ /*
+ * error cleanup and return
+ */
+error3:
+ (void)close(pdfrom[0]);
+ (void)close(pdfrom[1]);
+error2:
+ (void)close(pdto[0]);
+ (void)close(pdto[1]);
+error1:
+ return (-1);
+}
+
+static char *
+basename(char *path)
+{
+ char *p;
+
+ p = strrchr(path, '/');
+ if (p == NULL) {
+ return (path);
+ } else {
+ return (p + 1);
+ }
+}
+
+#else /* YP */
+
+static int match(char *, char *);
+
+/*
+ * Determine if requester is allowed to update the given map,
+ * and update it if so. Returns the status, which is zero
+ * if there is no access violation. This function updates
+ * the local file and then shuts up.
+ */
+int
+localupdate(char *name, char *filename, u_int op, u_int keylen __unused,
+ char *key, u_int datalen __unused, char *data)
+{
+ char line[256];
+ FILE *rf;
+ FILE *wf;
+ char *tmpname;
+ int err;
+
+ /*
+ * Check permission
+ */
+ if (strcmp(name, key) != 0) {
+ return (ERR_ACCESS);
+ }
+ if (strcmp(name, "nobody") == 0) {
+ /*
+ * Can't change "nobody"s key.
+ */
+ return (ERR_ACCESS);
+ }
+
+ /*
+ * Open files
+ */
+ tmpname = malloc(strlen(filename) + 4);
+ if (tmpname == NULL) {
+ return (ERR_MALLOC);
+ }
+ sprintf(tmpname, "%s.tmp", filename);
+ rf = fopen(filename, "r");
+ if (rf == NULL) {
+ return (ERR_READ);
+ }
+ wf = fopen(tmpname, "w");
+ if (wf == NULL) {
+ return (ERR_WRITE);
+ }
+ err = -1;
+ while (fgets(line, sizeof (line), rf)) {
+ if (err < 0 && match(line, name)) {
+ switch (op) {
+ case YPOP_INSERT:
+ err = ERR_KEY;
+ break;
+ case YPOP_STORE:
+ case YPOP_CHANGE:
+ fprintf(wf, "%s %s\n", key, data);
+ err = 0;
+ break;
+ case YPOP_DELETE:
+ /* do nothing */
+ err = 0;
+ break;
+ }
+ } else {
+ fputs(line, wf);
+ }
+ }
+ if (err < 0) {
+ switch (op) {
+ case YPOP_CHANGE:
+ case YPOP_DELETE:
+ err = ERR_KEY;
+ break;
+ case YPOP_INSERT:
+ case YPOP_STORE:
+ err = 0;
+ fprintf(wf, "%s %s\n", key, data);
+ break;
+ }
+ }
+ fclose(wf);
+ fclose(rf);
+ if (err == 0) {
+ if (rename(tmpname, filename) < 0) {
+ return (ERR_DBASE);
+ }
+ } else {
+ if (unlink(tmpname) < 0) {
+ return (ERR_DBASE);
+ }
+ }
+ return (err);
+}
+
+static int
+match(char *line, char *name)
+{
+ int len;
+
+ len = strlen(name);
+ return (strncmp(line, name, len) == 0 &&
+ (line[len] == ' ' || line[len] == '\t'));
+}
+#endif /* !YP */
diff --git a/usr.sbin/rpc.ypupdated/yp_dbdelete.c b/usr.sbin/rpc.ypupdated/yp_dbdelete.c
new file mode 100644
index 0000000..0552aab
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/yp_dbdelete.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <limits.h>
+#include <unistd.h>
+#include <db.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <paths.h>
+#include <rpcsvc/yp.h>
+#include "ypxfr_extern.h"
+
+int
+yp_del_record(DB *dbp, DBT *key)
+{
+ int rval;
+
+ if ((rval = (dbp->del)(dbp,key,0))) {
+ switch (rval) {
+ case 1:
+ return(YP_FALSE);
+ break;
+ case -1:
+ default:
+ (void)(dbp->close)(dbp);
+ return(YP_BADDB);
+ break;
+ }
+ }
+
+ return(YP_TRUE);
+}
diff --git a/usr.sbin/rpc.ypupdated/yp_dbupdate.c b/usr.sbin/rpc.ypupdated/yp_dbupdate.c
new file mode 100644
index 0000000..453ed2b
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/yp_dbupdate.c
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/fcntl.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <db.h>
+#include <unistd.h>
+#include <rpcsvc/ypclnt.h>
+#include <rpcsvc/ypupdate_prot.h>
+#include "ypxfr_extern.h"
+#include "ypupdated_extern.h"
+
+static int
+yp_domake(char *map, char *domain)
+{
+ int pid;
+
+ switch ((pid = fork())) {
+ case 0:
+ execlp(MAP_UPDATE_PATH, MAP_UPDATE, map, domain, (char *)NULL);
+ yp_error("couldn't exec map update process: %s",
+ strerror(errno));
+ exit(1);
+ break;
+ case -1:
+ yp_error("fork() failed: %s", strerror(errno));
+ return(YPERR_YPERR);
+ break;
+ default:
+ children++;
+ break;
+ }
+
+ return(0);
+}
+
+int
+ypmap_update(char *netname, char *map, unsigned int op, unsigned int keylen,
+ char *keyval, unsigned int datlen, char *datval)
+{
+ DB *dbp;
+ DBT key = { NULL, 0 }, data = { NULL, 0 };
+ char *yp_last = "YP_LAST_MODIFIED";
+ char yplastbuf[YPMAXRECORD];
+ char *domptr;
+ int rval = 0;
+
+ if ((domptr = strchr(netname, '@')) == NULL)
+ return(ERR_ACCESS);
+ domptr++;
+
+
+ dbp = yp_open_db_rw(domptr, map, O_RDWR);
+ if (dbp == NULL)
+ return(ERR_DBASE);
+
+ key.data = keyval;
+ key.size = keylen;
+ data.data = datval;
+ data.size = datlen;
+
+ switch (op) {
+ case YPOP_DELETE: /* delete this entry */
+ rval = yp_del_record(dbp, &key);
+ if (rval == YP_TRUE)
+ rval = 0;
+ break;
+ case YPOP_INSERT: /* add, do not change */
+ rval = yp_put_record(dbp, &key, &data, 0);
+ if (rval == YP_TRUE)
+ rval = 0;
+ break;
+ case YPOP_STORE: /* add, or change */
+ rval = yp_put_record(dbp, &key, &data, 1);
+ if (rval == YP_TRUE)
+ rval = 0;
+ break;
+ case YPOP_CHANGE: /* change, do not add */
+ if (yp_get_record(domptr, map, &key, &data, 0) != YP_TRUE) {
+ rval = ERR_KEY;
+ break;
+ }
+ rval = yp_put_record(dbp, &key, &data, 1);
+ if (rval == YP_TRUE)
+ rval = 0;
+ break;
+ default:
+ yp_error("unknown update command: (%d)", op);
+ }
+
+ if (rval) {
+ (void)(dbp->close)(dbp);
+ return(rval);
+ }
+
+ snprintf(yplastbuf, sizeof(yplastbuf), "%lu", time(NULL));
+ key.data = yp_last;
+ key.size = strlen(yp_last);
+ data.data = (char *)&yplastbuf;
+ data.size = strlen(yplastbuf);
+ if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) {
+ yp_error("failed to update timestamp in %s/%s", domptr, map);
+ (void)(dbp->close)(dbp);
+ return(ERR_DBASE);
+ }
+
+ (void)(dbp->close)(dbp);
+ return(yp_domake(map, domptr));
+}
diff --git a/usr.sbin/rpc.ypupdated/ypupdate b/usr.sbin/rpc.ypupdated/ypupdate
new file mode 100755
index 0000000..4a26f4b
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/ypupdate
@@ -0,0 +1,33 @@
+#!/bin/sh
+#
+# This script is invoked by rpc.ypupdatedd to propagate NIS maps
+# after the master map databases have been modified. It expects
+# to be passed two arguments: the name of the map that was updated
+# and the name of the domain where the map resides.
+# These are passed to /var/yp/Makefile.
+#
+# Comment out the LOG=yes line to disable logging.
+#
+# $FreeBSD$
+#
+
+LOG=yes
+LOGFILE=/var/yp/ypupdate.log
+
+umask 077
+
+if [ ! -f $LOGFILE ];
+then
+ /usr/bin/touch $LOGFILE
+ echo "# Edit /usr/libexec/yppwupdate to disable" >> $LOGFILE
+ echo "# logging to this file from yppasswdd." >> $LOGFILE
+ echo -n "# Log started on: " >> $LOGFILE
+ /bin/date >> $LOGFILE
+fi
+
+if [ ! $LOG ];
+then
+ cd /var/yp/$2; /usr/bin/make -f ../Makefile $1 2>&1
+else
+ cd /var/yp/$2; /usr/bin/make -f ../Makefile $1 >> $LOGFILE
+fi
diff --git a/usr.sbin/rpc.ypupdated/ypupdated_extern.h b/usr.sbin/rpc.ypupdated/ypupdated_extern.h
new file mode 100644
index 0000000..2cacb95
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/ypupdated_extern.h
@@ -0,0 +1,33 @@
+/*
+ * $FreeBSD$
+ */
+
+#include <db.h>
+
+#define YPOP_CHANGE 1 /* change, do not add */
+#define YPOP_INSERT 2 /* add, do not change */
+#define YPOP_DELETE 3 /* delete this entry */
+#define YPOP_STORE 4 /* add, or change */
+
+#define ERR_ACCESS 1
+#define ERR_MALLOC 2
+#define ERR_READ 3
+#define ERR_WRITE 4
+#define ERR_DBASE 5
+#define ERR_KEY 6
+
+#ifndef YPLIBDIR
+#define YPLIBDIR "/usr/libexec/"
+#endif
+
+#ifndef MAP_UPPATE
+#define MAP_UPDATE "ypupdate"
+#endif
+
+#define MAP_UPDATE_PATH YPLIBDIR MAP_UPDATE
+
+extern int children;
+extern void ypu_prog_1(struct svc_req *, register SVCXPRT *);
+extern int localupdate(char *, char *, u_int, u_int, char *, u_int, char *);
+extern int ypmap_update(char *, char *, u_int, u_int, char *, u_int, char *);
+extern int yp_del_record(DB *, DBT *);
diff --git a/usr.sbin/rpc.ypupdated/ypupdated_main.c b/usr.sbin/rpc.ypupdated/ypupdated_main.c
new file mode 100644
index 0000000..c13af7d
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/ypupdated_main.c
@@ -0,0 +1,286 @@
+/*
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "ypupdate_prot.h"
+#include <stdio.h>
+#include <stdlib.h> /* getenv, exit */
+#include <rpc/pmap_clnt.h> /* for pmap_unset */
+#include <rpc/rpc_com.h>
+#include <string.h> /* strcmp */
+#include <signal.h>
+#ifdef __cplusplus
+#include <sysent.h> /* getdtablesize, open */
+#endif /* __cplusplus */
+#include <memory.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <syslog.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <err.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "ypupdated_extern.h"
+#include "yp_extern.h"
+
+#ifndef SIG_PF
+#define SIG_PF void(*)(int)
+#endif
+
+#ifdef DEBUG
+#define RPC_SVC_FG
+#endif
+
+#define _RPCSVC_CLOSEDOWN 120
+int _rpcpmstart; /* Started by a port monitor ? */
+static int _rpcfdtype;
+ /* Whether Stream or Datagram ? */
+ /* States a server can be in wrt request */
+
+#define _IDLE 0
+#define _SERVED 1
+#define _SERVING 2
+
+extern int _rpcsvcstate; /* Set when a request is serviced */
+
+char *progname = "rpc.ypupdated";
+char *yp_dir = "/var/yp/";
+
+static void
+_msgout(char* msg)
+{
+#ifdef RPC_SVC_FG
+ if (_rpcpmstart)
+ syslog(LOG_ERR, "%s", msg);
+ else
+ warnx("%s", msg);
+#else
+ syslog(LOG_ERR, "%s", msg);
+#endif
+}
+
+static void
+closedown(int sig)
+{
+ if (_rpcsvcstate == _IDLE) {
+ extern fd_set svc_fdset;
+ static int size;
+ int i, openfd;
+
+ if (_rpcfdtype == SOCK_DGRAM)
+ exit(0);
+ if (size == 0) {
+ size = getdtablesize();
+ }
+ for (i = 0, openfd = 0; i < size && openfd < 2; i++)
+ if (FD_ISSET(i, &svc_fdset))
+ openfd++;
+ if (openfd <= 1)
+ exit(0);
+ }
+ if (_rpcsvcstate == _SERVED)
+ _rpcsvcstate = _IDLE;
+
+ (void) signal(SIGALRM, (SIG_PF) closedown);
+ (void) alarm(_RPCSVC_CLOSEDOWN/2);
+}
+
+static void
+ypupdated_svc_run(void)
+{
+#ifdef FD_SETSIZE
+ fd_set readfds;
+#else
+ int readfds;
+#endif /* def FD_SETSIZE */
+ extern int forked;
+ int pid;
+ int fd_setsize = _rpc_dtablesize();
+
+ /* Establish the identity of the parent ypupdated process. */
+ pid = getpid();
+
+ for (;;) {
+#ifdef FD_SETSIZE
+ readfds = svc_fdset;
+#else
+ readfds = svc_fds;
+#endif /* def FD_SETSIZE */
+ switch (select(fd_setsize, &readfds, NULL, NULL,
+ (struct timeval *)0)) {
+ case -1:
+ if (errno == EINTR) {
+ continue;
+ }
+ warn("svc_run: - select failed");
+ return;
+ case 0:
+ continue;
+ default:
+ svc_getreqset(&readfds);
+ if (forked && pid != getpid())
+ exit(0);
+ }
+ }
+}
+
+static void
+reaper(int sig)
+{
+ int status;
+
+ if (sig == SIGHUP) {
+#ifdef foo
+ load_securenets();
+#endif
+ return;
+ }
+
+ if (sig == SIGCHLD) {
+ while (wait3(&status, WNOHANG, NULL) > 0)
+ children--;
+ } else {
+ (void) pmap_unset(YPU_PROG, YPU_VERS);
+ exit(0);
+ }
+}
+
+void
+usage(void)
+{
+ fprintf(stderr, "rpc.ypupdatedd [-p path]\n");
+ exit(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ register SVCXPRT *transp = NULL;
+ int sock;
+ int proto = 0;
+ struct sockaddr_in saddr;
+ int asize = sizeof (saddr);
+ int ch;
+
+ while ((ch = getopt(argc, argv, "p:h")) != -1) {
+ switch (ch) {
+ case 'p':
+ yp_dir = optarg;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+#ifdef foo
+ load_securenets();
+#endif
+
+ if (svc_auth_reg(AUTH_DES, _svcauth_des) == -1) {
+ yp_error("failed to register AUTH_DES flavor");
+ exit(1);
+ }
+
+ if (getsockname(0, (struct sockaddr *)&saddr, &asize) == 0) {
+ int ssize = sizeof (int);
+
+ if (saddr.sin_family != AF_INET)
+ exit(1);
+ if (getsockopt(0, SOL_SOCKET, SO_TYPE,
+ (char *)&_rpcfdtype, &ssize) == -1)
+ exit(1);
+ sock = 0;
+ _rpcpmstart = 1;
+ proto = 0;
+ openlog("rpc.ypupdatedd", LOG_PID, LOG_DAEMON);
+ } else {
+#ifndef RPC_SVC_FG
+ if (daemon(0,0)) {
+ err(1, "cannot fork");
+ }
+ openlog("rpc.ypupdated", LOG_PID, LOG_DAEMON);
+#endif
+ sock = RPC_ANYSOCK;
+ (void) pmap_unset(YPU_PROG, YPU_VERS);
+ }
+
+ if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_DGRAM)) {
+ transp = svcudp_create(sock);
+ if (transp == NULL) {
+ _msgout("cannot create udp service.");
+ exit(1);
+ }
+ if (!_rpcpmstart)
+ proto = IPPROTO_UDP;
+ if (!svc_register(transp, YPU_PROG, YPU_VERS, ypu_prog_1, proto)) {
+ _msgout("unable to register (YPU_PROG, YPU_VERS, udp).");
+ exit(1);
+ }
+ }
+
+ if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_STREAM)) {
+ transp = svctcp_create(sock, 0, 0);
+ if (transp == NULL) {
+ _msgout("cannot create tcp service.");
+ exit(1);
+ }
+ if (!_rpcpmstart)
+ proto = IPPROTO_TCP;
+ if (!svc_register(transp, YPU_PROG, YPU_VERS, ypu_prog_1, proto)) {
+ _msgout("unable to register (YPU_PROG, YPU_VERS, tcp).");
+ exit(1);
+ }
+ }
+
+ if (transp == (SVCXPRT *)NULL) {
+ _msgout("could not create a handle");
+ exit(1);
+ }
+ if (_rpcpmstart) {
+ (void) signal(SIGALRM, (SIG_PF) closedown);
+ (void) alarm(_RPCSVC_CLOSEDOWN/2);
+ }
+
+ (void) signal(SIGPIPE, SIG_IGN);
+ (void) signal(SIGCHLD, (SIG_PF) reaper);
+ (void) signal(SIGTERM, (SIG_PF) reaper);
+ (void) signal(SIGINT, (SIG_PF) reaper);
+ (void) signal(SIGHUP, (SIG_PF) reaper);
+
+ ypupdated_svc_run();
+ _msgout("svc_run returned");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.sbin/rpc.ypupdated/ypupdated_server.c b/usr.sbin/rpc.ypupdated/ypupdated_server.c
new file mode 100644
index 0000000..c9a50dd
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/ypupdated_server.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * ypupdate server implementation
+ *
+ * Written by Bill Paul <wpaul@ctr.columbia.edu>
+ * Center for Telecommunications Research
+ * Columbia University, New York City
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <rpc/key_prot.h>
+#include <sys/param.h>
+#include <sys/cdefs.h>
+#include <rpcsvc/yp.h>
+#include "ypupdate_prot.h"
+#include "ypupdated_extern.h"
+#include "yp_extern.h"
+#include "ypxfr_extern.h"
+
+int children = 0;
+int forked = 0;
+
+/*
+ * Try to avoid spoofing: if a client chooses to use a very large
+ * window and then tries a bunch of randomly chosen encrypted timestamps,
+ * there's a chance he might stumble onto a valid combination.
+ * We therefore reject any RPCs with a window size larger than a preset
+ * value.
+ */
+#ifndef WINDOW
+#define WINDOW (60*60)
+#endif
+
+static enum auth_stat
+yp_checkauth(struct svc_req *svcreq)
+{
+ struct authdes_cred *des_cred;
+
+ switch (svcreq->rq_cred.oa_flavor) {
+ case AUTH_DES:
+ des_cred = (struct authdes_cred *) svcreq->rq_clntcred;
+ if (des_cred->adc_fullname.window > WINDOW) {
+ yp_error("warning: client-specified window size \
+was too large -- possible spoof attempt");
+ return(AUTH_BADCRED);
+ }
+ return(AUTH_OK);
+ break;
+ case AUTH_UNIX:
+ case AUTH_NONE:
+ yp_error("warning: client didn't use DES authentication");
+ return(AUTH_TOOWEAK);
+ break;
+ default:
+ yp_error("client used unknown auth flavor");
+ return(AUTH_REJECTEDCRED);
+ break;
+ }
+}
+
+unsigned int *
+ypu_change_1_svc(struct ypupdate_args *args, struct svc_req *svcreq)
+{
+ struct authdes_cred *des_cred;
+ static int res;
+ char *netname;
+ enum auth_stat astat;
+
+ res = 0;
+
+ astat = yp_checkauth(svcreq);
+
+ if (astat != AUTH_OK) {
+ svcerr_auth(svcreq->rq_xprt, astat);
+ return(&res);
+ }
+
+ des_cred = (struct authdes_cred *) svcreq->rq_clntcred;
+ netname = des_cred->adc_fullname.name;
+
+ res = localupdate(netname, "/etc/publickey", YPOP_CHANGE,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ args->datum.yp_buf_len, args->datum.yp_buf_val);
+
+ if (res)
+ return (&res);
+
+ res = ypmap_update(netname, args->mapname, YPOP_CHANGE,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ args->datum.yp_buf_len, args->datum.yp_buf_val);
+
+ return (&res);
+}
+
+unsigned int *
+ypu_insert_1_svc(struct ypupdate_args *args, struct svc_req *svcreq)
+{
+ struct authdes_cred *des_cred;
+ static int res;
+ char *netname;
+ enum auth_stat astat;
+
+ res = 0;
+
+ astat = yp_checkauth(svcreq);
+
+ if (astat != AUTH_OK) {
+ svcerr_auth(svcreq->rq_xprt, astat);
+ return(&res);
+ }
+
+ des_cred = (struct authdes_cred *) svcreq->rq_clntcred;
+ netname = des_cred->adc_fullname.name;
+
+ res = localupdate(netname, "/etc/publickey", YPOP_INSERT,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ args->datum.yp_buf_len, args->datum.yp_buf_val);
+
+ if (res)
+ return (&res);
+
+ res = ypmap_update(netname, args->mapname, YPOP_INSERT,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ args->datum.yp_buf_len, args->datum.yp_buf_val);
+
+ return (&res);
+}
+
+unsigned int *
+ypu_delete_1_svc(struct ypdelete_args *args, struct svc_req *svcreq)
+{
+ struct authdes_cred *des_cred;
+ static int res;
+ char *netname;
+ enum auth_stat astat;
+
+ res = 0;
+
+ astat = yp_checkauth(svcreq);
+
+ if (astat != AUTH_OK) {
+ svcerr_auth(svcreq->rq_xprt, astat);
+ return(&res);
+ }
+
+ des_cred = (struct authdes_cred *) svcreq->rq_clntcred;
+ netname = des_cred->adc_fullname.name;
+
+ res = localupdate(netname, "/etc/publickey", YPOP_DELETE,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ 0, NULL);
+
+ if (res)
+ return (&res);
+
+ res = ypmap_update(netname, args->mapname, YPOP_DELETE,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ 0, NULL);
+
+ return (&res);
+}
+
+unsigned int *
+ypu_store_1_svc(struct ypupdate_args *args, struct svc_req *svcreq)
+{
+ struct authdes_cred *des_cred;
+ static int res;
+ char *netname;
+ enum auth_stat astat;
+
+ res = 0;
+
+ astat = yp_checkauth(svcreq);
+
+ if (astat != AUTH_OK) {
+ svcerr_auth(svcreq->rq_xprt, astat);
+ return(&res);
+ }
+
+ des_cred = (struct authdes_cred *) svcreq->rq_clntcred;
+ netname = des_cred->adc_fullname.name;
+
+ res = localupdate(netname, "/etc/publickey", YPOP_STORE,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ args->datum.yp_buf_len, args->datum.yp_buf_val);
+
+ if (res)
+ return (&res);
+
+ res = ypmap_update(netname, args->mapname, YPOP_STORE,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ args->datum.yp_buf_len, args->datum.yp_buf_val);
+
+ return (&res);
+}
diff --git a/usr.sbin/rpc.ypxfrd/Makefile b/usr.sbin/rpc.ypxfrd/Makefile
new file mode 100644
index 0000000..70729b1
--- /dev/null
+++ b/usr.sbin/rpc.ypxfrd/Makefile
@@ -0,0 +1,35 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../usr.sbin/ypserv
+
+PROG= rpc.ypxfrd
+MAN= rpc.ypxfrd.8
+SRCS= ypxfrd_svc.c ypxfrd.h ypxfrd_server.c yp_error.c \
+ yp_access.c ypxfrd_main.c
+
+CFLAGS+= -I. -DXFRBLOCKSIZE=65535
+
+DPADD= ${LIBRPCSVC}
+LDADD= -lrpcsvc
+
+CLEANFILES= ypxfrd_svc.c ypxfrd.h
+
+RPCDIR= ${.CURDIR}/../../include/rpcsvc
+RPCGEN= rpcgen -I -C
+
+# We need to remove the 'static' keyword from _rpcsvcstate so that
+# ypxfrd_main.c can see it.
+ypxfrd_svc.c: ${RPCDIR}/ypxfrd.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -m ${RPCDIR}/ypxfrd.x | \
+ sed s/"static int _rpcsvcstate"/"int _rpcsvcstate"/g > ${.TARGET}
+
+# ypxfrd_xdr.c: ${RPCDIR}/ypxfrd.x
+# rm -f ${.TARGET}
+# ${RPCGEN} -c -o ${.TARGET} ${RPCDIR}/ypxfrd.x
+
+ypxfrd.h: ${RPCDIR}/ypxfrd.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -h -o ${.TARGET} ${RPCDIR}/ypxfrd.x
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rpc.ypxfrd/rpc.ypxfrd.8 b/usr.sbin/rpc.ypxfrd/rpc.ypxfrd.8
new file mode 100644
index 0000000..b1bcbcc
--- /dev/null
+++ b/usr.sbin/rpc.ypxfrd/rpc.ypxfrd.8
@@ -0,0 +1,153 @@
+.\" Copyright (c) 1995, 1996
+.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Bill Paul.
+.\" 4. Neither the name of the author nor the names of contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd June 2, 1996
+.Dt RPC.YPXFRD 8
+.Os
+.Sh NAME
+.Nm rpc.ypxfrd
+.Nd "NIS map transfer server"
+.Sh SYNOPSIS
+.Nm
+.Op Fl p Ar path
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to speed up the distribution of very large NIS maps
+from NIS master to NIS slave servers.
+The normal method for transfering
+maps involves several steps:
+.Bl -bullet -offset indent
+.It
+The master server calls
+.Xr yppush 8
+to inform the slave servers to start a transfer.
+.It
+The slave servers invoke
+.Xr ypxfr 8 ,
+which reads the entire contents of a map from the master server
+using the yp_all() function.
+.It
+The
+.Xr ypxfr 8
+program then creates a new map database file by using the
+.Xr db 3
+library hash method to store the data that it receives from the server.
+.It
+When all the data has been retrieved,
+.Xr ypxfr 8
+moves the new file into place and sends
+.Xr ypserv 8
+on the local machine a YPPROC_CLEAR to tell it to refresh its
+database handles.
+.El
+.Pp
+This process can take several minutes when there are very large
+maps involved.
+For example: a passwd database with several tens of
+thousands of entries can consume several megabytes of disk space,
+and it can take the
+.Xr db 3
+library package a long time to sort and store all the records
+in a hash database.
+Consider also that there are two sets of map
+files:
+.Pa master.passwd.by{name,uid}
+and
+.Pa passwd.by{name,uid} .
+.Pp
+The
+.Nm
+utility speeds up the transfer process by allowing NIS slave servers to
+simply copy the master server's map files rather than building their
+own from scratch.
+Simply put,
+.Nm
+implements an RPC-based file transfer protocol.
+Transfering even
+a multi-megabyte file in this fashion takes only a few seconds compared
+to the several minutes it would take even a reasonably fast slave server
+to build a new map from scratch.
+.Pp
+The
+.Nm
+utility uses the same access restriction mechanism as
+.Xr ypserv 8 .
+This means that slave servers will only be permitted to transfer
+files if the rules in the
+.Pa securenets
+database permit it (see
+.Xr ypserv 8
+for more information on
+.Pa securenets ) .
+Furthermore, only slave servers using reserved
+ports will be allowed to transfer the
+.Pa master.passwd
+maps.
+.Sh OPTIONS
+The following option is available:
+.Bl -tag -width indent
+.It Fl p Ar path
+This option can be used to override the default path to
+the location of the NIS
+map databases.
+The compiled-in default path is
+.Pa /var/yp .
+.El
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /var/yp/[domainname]/[maps]
+The NIS maps for a particular NIS domain.
+.El
+.Sh SEE ALSO
+.Xr yp 8 ,
+.Xr yppush 8 ,
+.Xr ypserv 8 ,
+.Xr ypxfr 8
+.Sh AUTHORS
+.An Bill Paul Aq wpaul@ctr.columbia.edu
+.Sh BUGS
+The
+.Fx
+.Nm ypxfrd
+protocol is not compatible with that used by SunOS.
+This is unfortunate
+but unavoidable: Sun's protocol is not freely available, and even if it
+were it would probably not be useful since the SunOS NIS v2 implementation
+uses the original ndbm package for its map databases whereas the
+.Fx
+implementation uses Berkeley DB.
+These two packages use vastly different
+file formats.
+Furthermore, ndbm is byte-order sensitive and not very
+smart about it, meaning that am ndbm database created on a big endian
+system cannot be read on a little endian system.
diff --git a/usr.sbin/rpc.ypxfrd/ypxfrd_extern.h b/usr.sbin/rpc.ypxfrd/ypxfrd_extern.h
new file mode 100644
index 0000000..5aba934
--- /dev/null
+++ b/usr.sbin/rpc.ypxfrd/ypxfrd_extern.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef MAX_CHILDREN
+#define MAX_CHILDREN 20
+#endif
+
+#ifndef XFRBLOCKSIZE
+#define XFRBLOCKSIZE YPXFRBLOCK
+#endif
+
+extern int forked;
+extern int children;
+extern void load_securenets(void);
+extern void yp_error(const char *, ...);
+extern int yp_access(const char *, const struct svc_req *);
+extern int yp_validdomain(const char *);
+extern char *yp_dir;
+extern void ypxfrd_freebsd_prog_1(struct svc_req *, register SVCXPRT *);
diff --git a/usr.sbin/rpc.ypxfrd/ypxfrd_main.c b/usr.sbin/rpc.ypxfrd/ypxfrd_main.c
new file mode 100644
index 0000000..8fa3e22
--- /dev/null
+++ b/usr.sbin/rpc.ypxfrd/ypxfrd_main.c
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "ypxfrd.h"
+#include <err.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h> /* getenv, exit */
+#include <unistd.h>
+#include <rpc/pmap_clnt.h> /* for pmap_unset */
+#include <rpc/rpc_com.h>
+#include <string.h> /* strcmp */
+#include <signal.h>
+#include <sys/ttycom.h> /* TIOCNOTTY */
+#ifdef __cplusplus
+#include <sysent.h> /* getdtablesize, open */
+#endif /* __cplusplus */
+#include <memory.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <syslog.h>
+#include "ypxfrd_extern.h"
+#include <sys/wait.h>
+#include <errno.h>
+
+#ifndef SIG_PF
+#define SIG_PF void(*)(int)
+#endif
+
+#ifdef DEBUG
+#define RPC_SVC_FG
+#endif
+
+#define _RPCSVC_CLOSEDOWN 120
+int _rpcpmstart; /* Started by a port monitor ? */
+static int _rpcfdtype;
+ /* Whether Stream or Datagram ? */
+ /* States a server can be in wrt request */
+
+#define _IDLE 0
+#define _SERVED 1
+#define _SERVING 2
+
+extern int _rpcsvcstate; /* Set when a request is serviced */
+
+char *progname = "rpc.ypxfrd";
+char *yp_dir = "/var/yp/";
+
+static void
+_msgout(char *msg)
+{
+#ifdef RPC_SVC_FG
+ if (_rpcpmstart)
+ syslog(LOG_ERR, "%s", msg);
+ else
+ warnx("%s", msg);
+#else
+ syslog(LOG_ERR, "%s", msg);
+#endif
+}
+
+static void
+closedown(int sig)
+{
+ if (_rpcsvcstate == _IDLE) {
+ extern fd_set svc_fdset;
+ static int size;
+ int i, openfd;
+
+ if (_rpcfdtype == SOCK_DGRAM)
+ exit(0);
+ if (size == 0) {
+ size = getdtablesize();
+ }
+ for (i = 0, openfd = 0; i < size && openfd < 2; i++)
+ if (FD_ISSET(i, &svc_fdset))
+ openfd++;
+ if (openfd <= 1)
+ exit(0);
+ }
+ if (_rpcsvcstate == _SERVED)
+ _rpcsvcstate = _IDLE;
+
+ (void) signal(SIGALRM, (SIG_PF) closedown);
+ (void) alarm(_RPCSVC_CLOSEDOWN/2);
+}
+
+
+static void
+ypxfrd_svc_run(void)
+{
+#ifdef FD_SETSIZE
+ fd_set readfds;
+#else
+ int readfds;
+#endif /* def FD_SETSIZE */
+ extern int forked;
+ int pid;
+ int fd_setsize = _rpc_dtablesize();
+
+ /* Establish the identity of the parent ypserv process. */
+ pid = getpid();
+
+ for (;;) {
+#ifdef FD_SETSIZE
+ readfds = svc_fdset;
+#else
+ readfds = svc_fds;
+#endif /* def FD_SETSIZE */
+ switch (select(fd_setsize, &readfds, NULL, NULL,
+ (struct timeval *)0)) {
+ case -1:
+ if (errno == EINTR) {
+ continue;
+ }
+ warn("svc_run: - select failed");
+ return;
+ case 0:
+ continue;
+ default:
+ svc_getreqset(&readfds);
+ if (forked && pid != getpid())
+ exit(0);
+ }
+ }
+}
+
+static void reaper(int sig)
+{
+ int status;
+ int saved_errno;
+
+ saved_errno = errno;
+
+ if (sig == SIGHUP) {
+ load_securenets();
+ errno = saved_errno;
+ return;
+ }
+
+ if (sig == SIGCHLD) {
+ while (wait3(&status, WNOHANG, NULL) > 0)
+ children--;
+ } else {
+ (void) pmap_unset(YPXFRD_FREEBSD_PROG, YPXFRD_FREEBSD_VERS);
+ exit(0);
+ }
+
+ errno = saved_errno;
+ return;
+}
+
+void
+usage(void)
+{
+ fprintf(stderr, "usage: rpc.ypxfrd [-p path]\n");
+ exit(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ register SVCXPRT *transp = NULL;
+ int sock;
+ int proto = 0;
+ struct sockaddr_in saddr;
+ int asize = sizeof (saddr);
+ int ch;
+
+ while ((ch = getopt(argc, argv, "p:h")) != -1) {
+ switch (ch) {
+ case 'p':
+ yp_dir = optarg;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ load_securenets();
+
+ if (getsockname(0, (struct sockaddr *)&saddr, &asize) == 0) {
+ int ssize = sizeof (int);
+
+ if (saddr.sin_family != AF_INET)
+ exit(1);
+ if (getsockopt(0, SOL_SOCKET, SO_TYPE,
+ (char *)&_rpcfdtype, &ssize) == -1)
+ exit(1);
+ sock = 0;
+ _rpcpmstart = 1;
+ proto = 0;
+ openlog("rpc.ypxfrd", LOG_PID, LOG_DAEMON);
+ } else {
+#ifndef RPC_SVC_FG
+ int size;
+ int pid, i;
+
+ pid = fork();
+ if (pid < 0)
+ err(1, "fork");
+ if (pid)
+ exit(0);
+ size = getdtablesize();
+ for (i = 0; i < size; i++)
+ (void) close(i);
+ i = open(_PATH_CONSOLE, 2);
+ (void) dup2(i, 1);
+ (void) dup2(i, 2);
+ i = open(_PATH_TTY, 2);
+ if (i >= 0) {
+ (void) ioctl(i, TIOCNOTTY, (char *)NULL);
+ (void) close(i);
+ }
+ openlog("rpc.ypxfrd", LOG_PID, LOG_DAEMON);
+#endif
+ sock = RPC_ANYSOCK;
+ (void) pmap_unset(YPXFRD_FREEBSD_PROG, YPXFRD_FREEBSD_VERS);
+ }
+
+ if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_DGRAM)) {
+ transp = svcudp_create(sock);
+ if (transp == NULL) {
+ _msgout("cannot create udp service.");
+ exit(1);
+ }
+ if (!_rpcpmstart)
+ proto = IPPROTO_UDP;
+ if (!svc_register(transp, YPXFRD_FREEBSD_PROG, YPXFRD_FREEBSD_VERS, ypxfrd_freebsd_prog_1, proto)) {
+ _msgout("unable to register (YPXFRD_FREEBSD_PROG, YPXFRD_FREEBSD_VERS, udp).");
+ exit(1);
+ }
+ }
+
+ if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_STREAM)) {
+ transp = svctcp_create(sock, 0, 0);
+ if (transp == NULL) {
+ _msgout("cannot create tcp service.");
+ exit(1);
+ }
+ if (!_rpcpmstart)
+ proto = IPPROTO_TCP;
+ if (!svc_register(transp, YPXFRD_FREEBSD_PROG, YPXFRD_FREEBSD_VERS, ypxfrd_freebsd_prog_1, proto)) {
+ _msgout("unable to register (YPXFRD_FREEBSD_PROG, YPXFRD_FREEBSD_VERS, tcp).");
+ exit(1);
+ }
+ }
+
+ if (transp == (SVCXPRT *)NULL) {
+ _msgout("could not create a handle");
+ exit(1);
+ }
+ if (_rpcpmstart) {
+ (void) signal(SIGALRM, (SIG_PF) closedown);
+ (void) alarm(_RPCSVC_CLOSEDOWN/2);
+ }
+
+ (void) signal(SIGPIPE, SIG_IGN);
+ (void) signal(SIGCHLD, (SIG_PF) reaper);
+ (void) signal(SIGTERM, (SIG_PF) reaper);
+ (void) signal(SIGINT, (SIG_PF) reaper);
+ (void) signal(SIGHUP, (SIG_PF) reaper);
+
+ ypxfrd_svc_run();
+ _msgout("svc_run returned");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.sbin/rpc.ypxfrd/ypxfrd_server.c b/usr.sbin/rpc.ypxfrd/ypxfrd_server.c
new file mode 100644
index 0000000..5f60712
--- /dev/null
+++ b/usr.sbin/rpc.ypxfrd/ypxfrd_server.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "ypxfrd.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/uio.h>
+#include <sys/fcntl.h>
+#include <machine/endian.h>
+#include "ypxfrd_extern.h"
+
+int forked = 0;
+int children = 0;
+int fp = 0;
+
+static bool_t
+xdr_my_xfr(register XDR *xdrs, xfr *objp)
+{
+ unsigned char buf[XFRBLOCKSIZE];
+
+ while (1) {
+ if ((objp->xfr_u.xfrblock_buf.xfrblock_buf_len =
+ read(fp, &buf, XFRBLOCKSIZE)) != -1) {
+ objp->ok = TRUE;
+ objp->xfr_u.xfrblock_buf.xfrblock_buf_val = (char *)&buf;
+ } else {
+ objp->ok = FALSE;
+ objp->xfr_u.xfrstat = XFR_READ_ERR;
+ yp_error("read error: %s", strerror(errno));
+ }
+
+ /* Serialize */
+ if (!xdr_xfr(xdrs, objp))
+ return(FALSE);
+ if (objp->ok == FALSE)
+ return(TRUE);
+ if (objp->xfr_u.xfrblock_buf.xfrblock_buf_len < XFRBLOCKSIZE) {
+ objp->ok = FALSE;
+ objp->xfr_u.xfrstat = XFR_DONE;
+ if (!xdr_xfr(xdrs, objp))
+ return(FALSE);
+ return(TRUE);
+ }
+ }
+}
+
+struct xfr *
+ypxfrd_getmap_1_svc(ypxfr_mapname *argp, struct svc_req *rqstp)
+{
+ static struct xfr result;
+ char buf[MAXPATHLEN];
+
+ result.ok = FALSE;
+ result.xfr_u.xfrstat = XFR_DENIED;
+
+ if (yp_validdomain(argp->xfrdomain)) {
+ return(&result);
+ }
+
+ if (yp_access(argp->xfrmap, (struct svc_req *)rqstp)) {
+ return(&result);
+ }
+
+ snprintf (buf, sizeof(buf), "%s/%s/%s", yp_dir, argp->xfrdomain,
+ argp->xfrmap);
+ if (access(buf, R_OK) == -1) {
+ result.xfr_u.xfrstat = XFR_ACCESS;
+ return(&result);
+ }
+
+ if (argp->xfr_db_type != XFR_DB_BSD_HASH &&
+ argp->xfr_db_type != XFR_DB_ANY) {
+ result.xfr_u.xfrstat = XFR_DB_TYPE_MISMATCH;
+ return(&result);
+ }
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+ if (argp->xfr_byte_order == XFR_ENDIAN_BIG) {
+#else
+ if (argp->xfr_byte_order == XFR_ENDIAN_LITTLE) {
+#endif
+ result.xfr_u.xfrstat = XFR_DB_ENDIAN_MISMATCH;
+ return(&result);
+ }
+
+#ifndef DEBUG
+ if (children < MAX_CHILDREN && fork()) {
+ children++;
+ forked = 0;
+ return (NULL);
+ } else {
+ forked++;
+ }
+#endif
+ if ((fp = open(buf, O_RDONLY)) == -1) {
+ result.xfr_u.xfrstat = XFR_READ_ERR;
+ return(&result);
+ }
+
+ /* Start sending the file. */
+
+ svc_sendreply(rqstp->rq_xprt, (xdrproc_t)xdr_my_xfr, &result);
+
+ close(fp);
+
+ return (NULL);
+}
diff --git a/usr.sbin/rpcbind/Makefile b/usr.sbin/rpcbind/Makefile
new file mode 100644
index 0000000..3251378
--- /dev/null
+++ b/usr.sbin/rpcbind/Makefile
@@ -0,0 +1,20 @@
+# $NetBSD: Makefile,v 1.3 2000/06/20 13:56:43 fvdl Exp $
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+PROG= rpcbind
+MAN= rpcbind.8
+SRCS= check_bound.c rpcb_stat.c rpcb_svc_4.c rpcbind.c pmap_svc.c \
+ rpcb_svc.c rpcb_svc_com.c security.c warmstart.c util.c
+
+CFLAGS+= -DPORTMAP -DLIBWRAP
+
+.if ${MK_INET6_SUPPORT} != "no"
+CFLAGS+= -DINET6
+.endif
+
+DPADD= ${LIBWRAP} ${LIBUTIL}
+LDADD= -lwrap -lutil
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rpcbind/check_bound.c b/usr.sbin/rpcbind/check_bound.c
new file mode 100644
index 0000000..03f70b5
--- /dev/null
+++ b/usr.sbin/rpcbind/check_bound.c
@@ -0,0 +1,231 @@
+/* $NetBSD: check_bound.c,v 1.2 2000/06/22 08:09:26 fvdl Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/*
+ * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc.
+ */
+
+/* #ident "@(#)check_bound.c 1.15 93/07/05 SMI" */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)check_bound.c 1.11 89/04/21 Copyr 1989 Sun Micro";
+#endif
+#endif
+
+/*
+ * check_bound.c
+ * Checks to see whether the program is still bound to the
+ * claimed address and returns the univeral merged address
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <rpc/rpc.h>
+#include <stdio.h>
+#include <netconfig.h>
+#include <syslog.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "rpcbind.h"
+
+struct fdlist {
+ int fd;
+ struct netconfig *nconf;
+ struct fdlist *next;
+ int check_binding;
+};
+
+static struct fdlist *fdhead; /* Link list of the check fd's */
+static struct fdlist *fdtail;
+static char *nullstring = "";
+
+static bool_t check_bound(struct fdlist *, char *uaddr);
+
+/*
+ * Returns 1 if the given address is bound for the given addr & transport
+ * For all error cases, we assume that the address is bound
+ * Returns 0 for success.
+ */
+static bool_t
+check_bound(struct fdlist *fdl, char *uaddr)
+{
+ int fd;
+ struct netbuf *na;
+ int ans;
+
+ if (fdl->check_binding == FALSE)
+ return (TRUE);
+
+ na = uaddr2taddr(fdl->nconf, uaddr);
+ if (!na)
+ return (TRUE); /* punt, should never happen */
+
+ fd = __rpc_nconf2fd(fdl->nconf);
+ if (fd < 0) {
+ free(na->buf);
+ free(na);
+ return (TRUE);
+ }
+
+ ans = bind(fd, (struct sockaddr *)na->buf, na->len);
+
+ close(fd);
+ free(na->buf);
+ free(na);
+
+ return (ans == 0 ? FALSE : TRUE);
+}
+
+int
+add_bndlist(struct netconfig *nconf, struct netbuf *baddr __unused)
+{
+ struct fdlist *fdl;
+ struct netconfig *newnconf;
+
+ newnconf = getnetconfigent(nconf->nc_netid);
+ if (newnconf == NULL)
+ return (-1);
+ fdl = malloc(sizeof (struct fdlist));
+ if (fdl == NULL) {
+ freenetconfigent(newnconf);
+ syslog(LOG_ERR, "no memory!");
+ return (-1);
+ }
+ fdl->nconf = newnconf;
+ fdl->next = NULL;
+ if (fdhead == NULL) {
+ fdhead = fdl;
+ fdtail = fdl;
+ } else {
+ fdtail->next = fdl;
+ fdtail = fdl;
+ }
+ /* XXX no bound checking for now */
+ fdl->check_binding = FALSE;
+
+ return 0;
+}
+
+bool_t
+is_bound(char *netid, char *uaddr)
+{
+ struct fdlist *fdl;
+
+ for (fdl = fdhead; fdl; fdl = fdl->next)
+ if (strcmp(fdl->nconf->nc_netid, netid) == 0)
+ break;
+ if (fdl == NULL)
+ return (TRUE);
+ return (check_bound(fdl, uaddr));
+}
+
+/*
+ * Returns NULL if there was some system error.
+ * Returns "" if the address was not bound, i.e the server crashed.
+ * Returns the merged address otherwise.
+ */
+char *
+mergeaddr(SVCXPRT *xprt, char *netid, char *uaddr, char *saddr)
+{
+ struct fdlist *fdl;
+ char *c_uaddr, *s_uaddr, *m_uaddr, *allocated_uaddr = NULL;
+
+ for (fdl = fdhead; fdl; fdl = fdl->next)
+ if (strcmp(fdl->nconf->nc_netid, netid) == 0)
+ break;
+ if (fdl == NULL)
+ return (NULL);
+ if (check_bound(fdl, uaddr) == FALSE)
+ /* that server died */
+ return (nullstring);
+ /*
+ * If saddr is not NULL, the remote client may have included the
+ * address by which it contacted us. Use that for the "client" uaddr,
+ * otherwise use the info from the SVCXPRT.
+ */
+ if (saddr != NULL) {
+ c_uaddr = saddr;
+ } else {
+ c_uaddr = taddr2uaddr(fdl->nconf, svc_getrpccaller(xprt));
+ if (c_uaddr == NULL) {
+ syslog(LOG_ERR, "taddr2uaddr failed for %s",
+ fdl->nconf->nc_netid);
+ return (NULL);
+ }
+ allocated_uaddr = c_uaddr;
+ }
+
+#ifdef ND_DEBUG
+ if (debugging) {
+ if (saddr == NULL) {
+ fprintf(stderr, "mergeaddr: client uaddr = %s\n",
+ c_uaddr);
+ } else {
+ fprintf(stderr, "mergeaddr: contact uaddr = %s\n",
+ c_uaddr);
+ }
+ }
+#endif
+ s_uaddr = uaddr;
+ /*
+ * This is all we should need for IP 4 and 6
+ */
+ m_uaddr = addrmerge(svc_getrpccaller(xprt), s_uaddr, c_uaddr, netid);
+#ifdef ND_DEBUG
+ if (debugging)
+ fprintf(stderr, "mergeaddr: uaddr = %s, merged uaddr = %s\n",
+ uaddr, m_uaddr);
+#endif
+ if (allocated_uaddr != NULL)
+ free(allocated_uaddr);
+ return (m_uaddr);
+}
+
+/*
+ * Returns a netconf structure from its internal list. This
+ * structure should not be freed.
+ */
+struct netconfig *
+rpcbind_get_conf(char *netid)
+{
+ struct fdlist *fdl;
+
+ for (fdl = fdhead; fdl; fdl = fdl->next)
+ if (strcmp(fdl->nconf->nc_netid, netid) == 0)
+ break;
+ if (fdl == NULL)
+ return (NULL);
+ return (fdl->nconf);
+}
diff --git a/usr.sbin/rpcbind/pmap_svc.c b/usr.sbin/rpcbind/pmap_svc.c
new file mode 100644
index 0000000..5df3097
--- /dev/null
+++ b/usr.sbin/rpcbind/pmap_svc.c
@@ -0,0 +1,368 @@
+/* $NetBSD: pmap_svc.c,v 1.2 2000/10/20 11:49:40 fvdl Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/*
+ * Copyright (c) 1984 - 1991 by Sun Microsystems, Inc.
+ */
+
+/* #ident "@(#)pmap_svc.c 1.14 93/07/05 SMI" */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)pmap_svc.c 1.23 89/04/05 Copyr 1984 Sun Micro";
+#endif
+#endif
+
+/*
+ * pmap_svc.c
+ * The server procedure for the version 2 portmaper.
+ * All the portmapper related interface from the portmap side.
+ */
+
+#ifdef PORTMAP
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <rpc/rpcb_prot.h>
+#ifdef RPCBIND_DEBUG
+#include <stdlib.h>
+#endif
+#include "rpcbind.h"
+
+static struct pmaplist *find_service_pmap(rpcprog_t, rpcvers_t,
+ rpcprot_t);
+static bool_t pmapproc_change(struct svc_req *, SVCXPRT *, u_long);
+static bool_t pmapproc_getport(struct svc_req *, SVCXPRT *);
+static bool_t pmapproc_dump(struct svc_req *, SVCXPRT *);
+
+/*
+ * Called for all the version 2 inquiries.
+ */
+void
+pmap_service(struct svc_req *rqstp, SVCXPRT *xprt)
+{
+ rpcbs_procinfo(RPCBVERS_2_STAT, rqstp->rq_proc);
+ switch (rqstp->rq_proc) {
+ case PMAPPROC_NULL:
+ /*
+ * Null proc call
+ */
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "PMAPPROC_NULL\n");
+#endif
+ check_access(xprt, rqstp->rq_proc, NULL, PMAPVERS);
+ if ((!svc_sendreply(xprt, (xdrproc_t) xdr_void, NULL)) &&
+ debugging) {
+ if (doabort) {
+ rpcbind_abort();
+ }
+ }
+ break;
+
+ case PMAPPROC_SET:
+ /*
+ * Set a program, version to port mapping
+ */
+ pmapproc_change(rqstp, xprt, rqstp->rq_proc);
+ break;
+
+ case PMAPPROC_UNSET:
+ /*
+ * Remove a program, version to port mapping.
+ */
+ pmapproc_change(rqstp, xprt, rqstp->rq_proc);
+ break;
+
+ case PMAPPROC_GETPORT:
+ /*
+ * Lookup the mapping for a program, version and return its
+ * port number.
+ */
+ pmapproc_getport(rqstp, xprt);
+ break;
+
+ case PMAPPROC_DUMP:
+ /*
+ * Return the current set of mapped program, version
+ */
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "PMAPPROC_DUMP\n");
+#endif
+ pmapproc_dump(rqstp, xprt);
+ break;
+
+ case PMAPPROC_CALLIT:
+ /*
+ * Calls a procedure on the local machine. If the requested
+ * procedure is not registered this procedure does not return
+ * error information!!
+ * This procedure is only supported on rpc/udp and calls via
+ * rpc/udp. It passes null authentication parameters.
+ */
+ rpcbproc_callit_com(rqstp, xprt, PMAPPROC_CALLIT, PMAPVERS);
+ break;
+
+ default:
+ svcerr_noproc(xprt);
+ break;
+ }
+}
+
+/*
+ * returns the item with the given program, version number. If that version
+ * number is not found, it returns the item with that program number, so that
+ * the port number is now returned to the caller. The caller when makes a
+ * call to this program, version number, the call will fail and it will
+ * return with PROGVERS_MISMATCH. The user can then determine the highest
+ * and the lowest version number for this program using clnt_geterr() and
+ * use those program version numbers.
+ */
+static struct pmaplist *
+find_service_pmap(rpcprog_t prog, rpcvers_t vers, rpcprot_t prot)
+{
+ register struct pmaplist *hit = NULL;
+ register struct pmaplist *pml;
+
+ for (pml = list_pml; pml != NULL; pml = pml->pml_next) {
+ if ((pml->pml_map.pm_prog != prog) ||
+ (pml->pml_map.pm_prot != prot))
+ continue;
+ hit = pml;
+ if (pml->pml_map.pm_vers == vers)
+ break;
+ }
+ return (hit);
+}
+
+static bool_t
+pmapproc_change(struct svc_req *rqstp __unused, SVCXPRT *xprt, unsigned long op)
+{
+ struct pmap reg;
+ RPCB rpcbreg;
+ long ans;
+ struct sockaddr_in *who;
+ uid_t uid;
+ char uidbuf[32];
+
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "%s request for (%lu, %lu) : ",
+ op == PMAPPROC_SET ? "PMAP_SET" : "PMAP_UNSET",
+ reg.pm_prog, reg.pm_vers);
+#endif
+
+ if (!svc_getargs(xprt, (xdrproc_t) xdr_pmap, (char *)&reg)) {
+ svcerr_decode(xprt);
+ return (FALSE);
+ }
+
+ if (!check_access(xprt, op, &reg, PMAPVERS)) {
+ svcerr_weakauth(xprt);
+ return FALSE;
+ }
+
+ who = svc_getcaller(xprt);
+
+ /*
+ * Can't use getpwnam here. We might end up calling ourselves
+ * and looping.
+ */
+ if (__rpc_get_local_uid(xprt, &uid) < 0)
+ rpcbreg.r_owner = "unknown";
+ else if (uid == 0)
+ rpcbreg.r_owner = "superuser";
+ else {
+ /* r_owner will be strdup-ed later */
+ snprintf(uidbuf, sizeof uidbuf, "%d", uid);
+ rpcbreg.r_owner = uidbuf;
+ }
+
+ rpcbreg.r_prog = reg.pm_prog;
+ rpcbreg.r_vers = reg.pm_vers;
+
+ if (op == PMAPPROC_SET) {
+ char buf[32];
+
+ snprintf(buf, sizeof buf, "0.0.0.0.%d.%d",
+ (int)((reg.pm_port >> 8) & 0xff),
+ (int)(reg.pm_port & 0xff));
+ rpcbreg.r_addr = buf;
+ if (reg.pm_prot == IPPROTO_UDP) {
+ rpcbreg.r_netid = udptrans;
+ } else if (reg.pm_prot == IPPROTO_TCP) {
+ rpcbreg.r_netid = tcptrans;
+ } else {
+ ans = FALSE;
+ goto done_change;
+ }
+ ans = map_set(&rpcbreg, rpcbreg.r_owner);
+ } else if (op == PMAPPROC_UNSET) {
+ bool_t ans1, ans2;
+
+ rpcbreg.r_addr = NULL;
+ rpcbreg.r_netid = tcptrans;
+ ans1 = map_unset(&rpcbreg, rpcbreg.r_owner);
+ rpcbreg.r_netid = udptrans;
+ ans2 = map_unset(&rpcbreg, rpcbreg.r_owner);
+ ans = ans1 || ans2;
+ } else {
+ ans = FALSE;
+ }
+done_change:
+ if ((!svc_sendreply(xprt, (xdrproc_t) xdr_long, (caddr_t) &ans)) &&
+ debugging) {
+ fprintf(stderr, "portmap: svc_sendreply\n");
+ if (doabort) {
+ rpcbind_abort();
+ }
+ }
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "%s\n", ans == TRUE ? "succeeded" : "failed");
+#endif
+ if (op == PMAPPROC_SET)
+ rpcbs_set(RPCBVERS_2_STAT, ans);
+ else
+ rpcbs_unset(RPCBVERS_2_STAT, ans);
+ return (TRUE);
+}
+
+/* ARGSUSED */
+static bool_t
+pmapproc_getport(struct svc_req *rqstp __unused, SVCXPRT *xprt)
+{
+ struct pmap reg;
+ long lport;
+ int port = 0;
+ struct pmaplist *fnd;
+#ifdef RPCBIND_DEBUG
+ char *uaddr;
+#endif
+
+ if (!svc_getargs(xprt, (xdrproc_t) xdr_pmap, (char *)&reg)) {
+ svcerr_decode(xprt);
+ return (FALSE);
+ }
+
+ if (!check_access(xprt, PMAPPROC_GETPORT, &reg, PMAPVERS)) {
+ svcerr_weakauth(xprt);
+ return FALSE;
+ }
+
+#ifdef RPCBIND_DEBUG
+ if (debugging) {
+ uaddr = taddr2uaddr(rpcbind_get_conf(xprt->xp_netid),
+ svc_getrpccaller(xprt));
+ fprintf(stderr, "PMAP_GETPORT req for (%lu, %lu, %s) from %s :",
+ reg.pm_prog, reg.pm_vers,
+ reg.pm_prot == IPPROTO_UDP ? "udp" : "tcp", uaddr);
+ free(uaddr);
+ }
+#endif
+ fnd = find_service_pmap(reg.pm_prog, reg.pm_vers, reg.pm_prot);
+ if (fnd) {
+ char serveuaddr[32], *ua;
+ int h1, h2, h3, h4, p1, p2;
+ char *netid;
+
+ if (reg.pm_prot == IPPROTO_UDP) {
+ ua = udp_uaddr;
+ netid = udptrans;
+ } else {
+ ua = tcp_uaddr; /* To get the len */
+ netid = tcptrans;
+ }
+ if (ua == NULL) {
+ goto sendreply;
+ }
+ if (sscanf(ua, "%d.%d.%d.%d.%d.%d", &h1, &h2, &h3,
+ &h4, &p1, &p2) == 6) {
+ p1 = (fnd->pml_map.pm_port >> 8) & 0xff;
+ p2 = (fnd->pml_map.pm_port) & 0xff;
+ snprintf(serveuaddr, sizeof serveuaddr,
+ "%d.%d.%d.%d.%d.%d", h1, h2, h3, h4, p1, p2);
+ if (is_bound(netid, serveuaddr)) {
+ port = fnd->pml_map.pm_port;
+ } else { /* this service is dead; delete it */
+ delete_prog(reg.pm_prog);
+ }
+ }
+ }
+sendreply:
+ lport = port;
+ if ((!svc_sendreply(xprt, (xdrproc_t) xdr_long, (caddr_t)&lport)) &&
+ debugging) {
+ (void) fprintf(stderr, "portmap: svc_sendreply\n");
+ if (doabort) {
+ rpcbind_abort();
+ }
+ }
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "port = %d\n", port);
+#endif
+ rpcbs_getaddr(RPCBVERS_2_STAT, reg.pm_prog, reg.pm_vers,
+ reg.pm_prot == IPPROTO_UDP ? udptrans : tcptrans,
+ port ? udptrans : "");
+
+ return (TRUE);
+}
+
+/* ARGSUSED */
+static bool_t
+pmapproc_dump(struct svc_req *rqstp __unused, SVCXPRT *xprt)
+{
+ if (!svc_getargs(xprt, (xdrproc_t)xdr_void, NULL)) {
+ svcerr_decode(xprt);
+ return (FALSE);
+ }
+
+ if (!check_access(xprt, PMAPPROC_DUMP, NULL, PMAPVERS)) {
+ svcerr_weakauth(xprt);
+ return FALSE;
+ }
+
+ if ((!svc_sendreply(xprt, (xdrproc_t) xdr_pmaplist_ptr,
+ (caddr_t)&list_pml)) && debugging) {
+ if (debugging)
+ (void) fprintf(stderr, "portmap: svc_sendreply\n");
+ if (doabort) {
+ rpcbind_abort();
+ }
+ }
+ return (TRUE);
+}
+
+#endif /* PORTMAP */
diff --git a/usr.sbin/rpcbind/rpcb_stat.c b/usr.sbin/rpcbind/rpcb_stat.c
new file mode 100644
index 0000000..b10ed50
--- /dev/null
+++ b/usr.sbin/rpcbind/rpcb_stat.c
@@ -0,0 +1,206 @@
+/*
+ * $NetBSD: rpcb_stat.c,v 1.2 2000/07/04 20:27:40 matt Exp $
+ * $FreeBSD$
+ */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/* #pragma ident "@(#)rpcb_stat.c 1.7 94/04/25 SMI" */
+
+/*
+ * rpcb_stat.c
+ * Allows for gathering of statistics
+ *
+ * Copyright (c) 1990 by Sun Microsystems, Inc.
+ */
+
+#include <stdio.h>
+#include <netconfig.h>
+#include <rpc/rpc.h>
+#include <rpc/rpcb_prot.h>
+#include <sys/stat.h>
+#ifdef PORTMAP
+#include <rpc/pmap_prot.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include "rpcbind.h"
+
+static rpcb_stat_byvers inf;
+
+void
+rpcbs_init()
+{
+
+}
+
+void
+rpcbs_procinfo(rpcvers_t rtype, rpcproc_t proc)
+{
+ switch (rtype + 2) {
+#ifdef PORTMAP
+ case PMAPVERS: /* version 2 */
+ if (proc > rpcb_highproc_2)
+ return;
+ break;
+#endif
+ case RPCBVERS: /* version 3 */
+ if (proc > rpcb_highproc_3)
+ return;
+ break;
+ case RPCBVERS4: /* version 4 */
+ if (proc > rpcb_highproc_4)
+ return;
+ break;
+ default: return;
+ }
+ inf[rtype].info[proc]++;
+ return;
+}
+
+void
+rpcbs_set(rpcvers_t rtype, bool_t success)
+{
+ if ((rtype >= RPCBVERS_STAT) || (success == FALSE))
+ return;
+ inf[rtype].setinfo++;
+ return;
+}
+
+void
+rpcbs_unset(rpcvers_t rtype, bool_t success)
+{
+ if ((rtype >= RPCBVERS_STAT) || (success == FALSE))
+ return;
+ inf[rtype].unsetinfo++;
+ return;
+}
+
+void
+rpcbs_getaddr(rpcvers_t rtype, rpcprog_t prog, rpcvers_t vers, char *netid,
+ char *uaddr)
+{
+ rpcbs_addrlist *al;
+ struct netconfig *nconf;
+
+ if (rtype >= RPCBVERS_STAT)
+ return;
+ for (al = inf[rtype].addrinfo; al; al = al->next) {
+
+ if(al->netid == NULL)
+ return;
+ if ((al->prog == prog) && (al->vers == vers) &&
+ (strcmp(al->netid, netid) == 0)) {
+ if ((uaddr == NULL) || (uaddr[0] == 0))
+ al->failure++;
+ else
+ al->success++;
+ return;
+ }
+ }
+ nconf = rpcbind_get_conf(netid);
+ if (nconf == NULL) {
+ return;
+ }
+ al = (rpcbs_addrlist *) malloc(sizeof (rpcbs_addrlist));
+ if (al == NULL) {
+ return;
+ }
+ al->prog = prog;
+ al->vers = vers;
+ al->netid = nconf->nc_netid;
+ if ((uaddr == NULL) || (uaddr[0] == 0)) {
+ al->failure = 1;
+ al->success = 0;
+ } else {
+ al->failure = 0;
+ al->success = 1;
+ }
+ al->next = inf[rtype].addrinfo;
+ inf[rtype].addrinfo = al;
+}
+
+void
+rpcbs_rmtcall(rpcvers_t rtype, rpcproc_t rpcbproc, rpcprog_t prog,
+ rpcvers_t vers, rpcproc_t proc, char *netid, rpcblist_ptr rbl)
+{
+ rpcbs_rmtcalllist *rl;
+ struct netconfig *nconf;
+
+ if (rtype > RPCBVERS_STAT)
+ return;
+ for (rl = inf[rtype].rmtinfo; rl; rl = rl->next) {
+
+ if(rl->netid == NULL)
+ return;
+
+ if ((rl->prog == prog) && (rl->vers == vers) &&
+ (rl->proc == proc) &&
+ (strcmp(rl->netid, netid) == 0)) {
+ if ((rbl == NULL) ||
+ (rbl->rpcb_map.r_vers != vers))
+ rl->failure++;
+ else
+ rl->success++;
+ if (rpcbproc == RPCBPROC_INDIRECT)
+ rl->indirect++;
+ return;
+ }
+ }
+ nconf = rpcbind_get_conf(netid);
+ if (nconf == NULL) {
+ return;
+ }
+ rl = (rpcbs_rmtcalllist *) malloc(sizeof (rpcbs_rmtcalllist));
+ if (rl == NULL) {
+ return;
+ }
+ rl->prog = prog;
+ rl->vers = vers;
+ rl->proc = proc;
+ rl->netid = nconf->nc_netid;
+ if ((rbl == NULL) ||
+ (rbl->rpcb_map.r_vers != vers)) {
+ rl->failure = 1;
+ rl->success = 0;
+ } else {
+ rl->failure = 0;
+ rl->success = 1;
+ }
+ rl->indirect = 1;
+ rl->next = inf[rtype].rmtinfo;
+ inf[rtype].rmtinfo = rl;
+ return;
+}
+
+void *
+rpcbproc_getstat(void *arg __unused, struct svc_req *req __unused,
+ SVCXPRT *xprt __unused, rpcvers_t versnum __unused)
+{
+ return (void *)&inf;
+}
diff --git a/usr.sbin/rpcbind/rpcb_svc.c b/usr.sbin/rpcbind/rpcb_svc.c
new file mode 100644
index 0000000..9ed05347
--- /dev/null
+++ b/usr.sbin/rpcbind/rpcb_svc.c
@@ -0,0 +1,233 @@
+/* $NetBSD: rpcb_svc.c,v 1.1 2000/06/02 23:15:41 fvdl Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/*
+ * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc.
+ */
+
+/* #ident "@(#)rpcb_svc.c 1.16 93/07/05 SMI" */
+
+/*
+ * rpcb_svc.c
+ * The server procedure for the version 3 rpcbind (TLI).
+ *
+ * It maintains a separate list of all the registered services with the
+ * version 3 of rpcbind.
+ */
+#include <sys/types.h>
+#include <rpc/rpc.h>
+#include <rpc/rpcb_prot.h>
+#include <netconfig.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "rpcbind.h"
+
+static void *rpcbproc_getaddr_3_local(void *, struct svc_req *, SVCXPRT *,
+ rpcvers_t);
+static void *rpcbproc_dump_3_local(void *, struct svc_req *, SVCXPRT *,
+ rpcvers_t);
+
+/*
+ * Called by svc_getreqset. There is a separate server handle for
+ * every transport that it waits on.
+ */
+void
+rpcb_service_3(struct svc_req *rqstp, SVCXPRT *transp)
+{
+ union {
+ RPCB rpcbproc_set_3_arg;
+ RPCB rpcbproc_unset_3_arg;
+ RPCB rpcbproc_getaddr_3_local_arg;
+ struct rpcb_rmtcallargs rpcbproc_callit_3_arg;
+ char *rpcbproc_uaddr2taddr_3_arg;
+ struct netbuf rpcbproc_taddr2uaddr_3_arg;
+ } argument;
+ char *result;
+ xdrproc_t xdr_argument, xdr_result;
+ void *(*local)(void *, struct svc_req *, SVCXPRT *, rpcvers_t);
+
+ rpcbs_procinfo(RPCBVERS_3_STAT, rqstp->rq_proc);
+
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ /*
+ * Null proc call
+ */
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_NULL\n");
+#endif
+ /* This call just logs, no actual checks */
+ check_access(transp, rqstp->rq_proc, NULL, RPCBVERS);
+ (void) svc_sendreply(transp, (xdrproc_t)xdr_void, (char *)NULL);
+ return;
+
+ case RPCBPROC_SET:
+ xdr_argument = (xdrproc_t )xdr_rpcb;
+ xdr_result = (xdrproc_t )xdr_bool;
+ local = rpcbproc_set_com;
+ break;
+
+ case RPCBPROC_UNSET:
+ xdr_argument = (xdrproc_t)xdr_rpcb;
+ xdr_result = (xdrproc_t)xdr_bool;
+ local = rpcbproc_unset_com;
+ break;
+
+ case RPCBPROC_GETADDR:
+ xdr_argument = (xdrproc_t)xdr_rpcb;
+ xdr_result = (xdrproc_t)xdr_wrapstring;
+ local = rpcbproc_getaddr_3_local;
+ break;
+
+ case RPCBPROC_DUMP:
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_DUMP\n");
+#endif
+ xdr_argument = (xdrproc_t)xdr_void;
+ xdr_result = (xdrproc_t)xdr_rpcblist_ptr;
+ local = rpcbproc_dump_3_local;
+ break;
+
+ case RPCBPROC_CALLIT:
+ rpcbproc_callit_com(rqstp, transp, rqstp->rq_proc, RPCBVERS);
+ return;
+
+ case RPCBPROC_GETTIME:
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_GETTIME\n");
+#endif
+ xdr_argument = (xdrproc_t)xdr_void;
+ xdr_result = (xdrproc_t)xdr_u_long;
+ local = rpcbproc_gettime_com;
+ break;
+
+ case RPCBPROC_UADDR2TADDR:
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_UADDR2TADDR\n");
+#endif
+ xdr_argument = (xdrproc_t)xdr_wrapstring;
+ xdr_result = (xdrproc_t)xdr_netbuf;
+ local = rpcbproc_uaddr2taddr_com;
+ break;
+
+ case RPCBPROC_TADDR2UADDR:
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_TADDR2UADDR\n");
+#endif
+ xdr_argument = (xdrproc_t)xdr_netbuf;
+ xdr_result = (xdrproc_t)xdr_wrapstring;
+ local = rpcbproc_taddr2uaddr_com;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+ (void) memset((char *)&argument, 0, sizeof (argument));
+ if (!svc_getargs(transp, (xdrproc_t) xdr_argument,
+ (char *) &argument)) {
+ svcerr_decode(transp);
+ if (debugging)
+ (void) fprintf(stderr, "rpcbind: could not decode\n");
+ return;
+ }
+ if (!check_access(transp, rqstp->rq_proc, &argument, RPCBVERS)) {
+ svcerr_weakauth(transp);
+ goto done;
+ }
+ result = (*local)(&argument, rqstp, transp, RPCBVERS);
+ if (result != NULL && !svc_sendreply(transp, (xdrproc_t)xdr_result,
+ result)) {
+ svcerr_systemerr(transp);
+ if (debugging) {
+ (void) fprintf(stderr, "rpcbind: svc_sendreply\n");
+ if (doabort) {
+ rpcbind_abort();
+ }
+ }
+ }
+done:
+ if (!svc_freeargs(transp, (xdrproc_t)xdr_argument, (char *)
+ &argument)) {
+ if (debugging) {
+ (void) fprintf(stderr, "unable to free arguments\n");
+ if (doabort) {
+ rpcbind_abort();
+ }
+ }
+ }
+}
+
+/*
+ * Lookup the mapping for a program, version and return its
+ * address. Assuming that the caller wants the address of the
+ * server running on the transport on which the request came.
+ *
+ * We also try to resolve the universal address in terms of
+ * address of the caller.
+ */
+/* ARGSUSED */
+static void *
+rpcbproc_getaddr_3_local(void *arg, struct svc_req *rqstp __unused,
+ SVCXPRT *transp __unused, rpcvers_t versnum __unused)
+{
+ RPCB *regp = (RPCB *)arg;
+#ifdef RPCBIND_DEBUG
+ if (debugging) {
+ char *uaddr;
+
+ uaddr = taddr2uaddr(rpcbind_get_conf(transp->xp_netid),
+ svc_getrpccaller(transp));
+ fprintf(stderr, "RPCB_GETADDR req for (%lu, %lu, %s) from %s: ",
+ (unsigned long)regp->r_prog, (unsigned long)regp->r_vers,
+ regp->r_netid, uaddr);
+ free(uaddr);
+ }
+#endif
+ return (rpcbproc_getaddr_com(regp, rqstp, transp, RPCBVERS,
+ RPCB_ALLVERS));
+}
+
+/* ARGSUSED */
+static void *
+rpcbproc_dump_3_local(void *arg __unused, struct svc_req *rqstp __unused,
+ SVCXPRT *transp __unused, rpcvers_t versnum __unused)
+{
+ return ((void *)&list_rbl);
+}
diff --git a/usr.sbin/rpcbind/rpcb_svc_4.c b/usr.sbin/rpcbind/rpcb_svc_4.c
new file mode 100644
index 0000000..86bfc08
--- /dev/null
+++ b/usr.sbin/rpcbind/rpcb_svc_4.c
@@ -0,0 +1,455 @@
+/*
+ * $NetBSD: rpcb_svc_4.c,v 1.1 2000/06/02 23:15:41 fvdl Exp $
+ * $FreeBSD$
+ */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/*
+ * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc.
+ */
+
+/* #ident "@(#)rpcb_svc_4.c 1.8 93/07/05 SMI" */
+
+/*
+ * rpcb_svc_4.c
+ * The server procedure for the version 4 rpcbind.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <rpc/rpc.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <netconfig.h>
+#include <syslog.h>
+#include <string.h>
+#include <stdlib.h>
+#include "rpcbind.h"
+
+static void *rpcbproc_getaddr_4_local(void *, struct svc_req *, SVCXPRT *,
+ rpcvers_t);
+static void *rpcbproc_getversaddr_4_local(void *, struct svc_req *, SVCXPRT *, rpcvers_t);
+static void *rpcbproc_getaddrlist_4_local
+ (void *, struct svc_req *, SVCXPRT *, rpcvers_t);
+static void free_rpcb_entry_list(rpcb_entry_list_ptr *);
+static void *rpcbproc_dump_4_local(void *, struct svc_req *, SVCXPRT *, rpcvers_t);
+
+/*
+ * Called by svc_getreqset. There is a separate server handle for
+ * every transport that it waits on.
+ */
+void
+rpcb_service_4(struct svc_req *rqstp, SVCXPRT *transp)
+{
+ union {
+ rpcb rpcbproc_set_4_arg;
+ rpcb rpcbproc_unset_4_arg;
+ rpcb rpcbproc_getaddr_4_local_arg;
+ char *rpcbproc_uaddr2taddr_4_arg;
+ struct netbuf rpcbproc_taddr2uaddr_4_arg;
+ } argument;
+ char *result;
+ xdrproc_t xdr_argument, xdr_result;
+ void *(*local)(void *, struct svc_req *, SVCXPRT *, rpcvers_t);
+
+ rpcbs_procinfo(RPCBVERS_4_STAT, rqstp->rq_proc);
+
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ /*
+ * Null proc call
+ */
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_NULL\n");
+#endif
+ check_access(transp, rqstp->rq_proc, NULL, RPCBVERS4);
+ (void) svc_sendreply(transp, (xdrproc_t) xdr_void,
+ (char *)NULL);
+ return;
+
+ case RPCBPROC_SET:
+ /*
+ * Check to see whether the message came from
+ * loopback transports (for security reasons)
+ */
+ xdr_argument = (xdrproc_t)xdr_rpcb;
+ xdr_result = (xdrproc_t)xdr_bool;
+ local = rpcbproc_set_com;
+ break;
+
+ case RPCBPROC_UNSET:
+ /*
+ * Check to see whether the message came from
+ * loopback transports (for security reasons)
+ */
+ xdr_argument = (xdrproc_t)xdr_rpcb;
+ xdr_result = (xdrproc_t)xdr_bool;
+ local = rpcbproc_unset_com;
+ break;
+
+ case RPCBPROC_GETADDR:
+ xdr_argument = (xdrproc_t)xdr_rpcb;
+ xdr_result = (xdrproc_t)xdr_wrapstring;
+ local = rpcbproc_getaddr_4_local;
+ break;
+
+ case RPCBPROC_GETVERSADDR:
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_GETVERSADDR\n");
+#endif
+ xdr_argument = (xdrproc_t)xdr_rpcb;
+ xdr_result = (xdrproc_t)xdr_wrapstring;
+ local = rpcbproc_getversaddr_4_local;
+ break;
+
+ case RPCBPROC_DUMP:
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_DUMP\n");
+#endif
+ xdr_argument = (xdrproc_t)xdr_void;
+ xdr_result = (xdrproc_t)xdr_rpcblist_ptr;
+ local = rpcbproc_dump_4_local;
+ break;
+
+ case RPCBPROC_INDIRECT:
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_INDIRECT\n");
+#endif
+ rpcbproc_callit_com(rqstp, transp, rqstp->rq_proc, RPCBVERS4);
+ return;
+
+/* case RPCBPROC_CALLIT: */
+ case RPCBPROC_BCAST:
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_BCAST\n");
+#endif
+ rpcbproc_callit_com(rqstp, transp, rqstp->rq_proc, RPCBVERS4);
+ return;
+
+ case RPCBPROC_GETTIME:
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_GETTIME\n");
+#endif
+ xdr_argument = (xdrproc_t)xdr_void;
+ xdr_result = (xdrproc_t)xdr_u_long;
+ local = rpcbproc_gettime_com;
+ break;
+
+ case RPCBPROC_UADDR2TADDR:
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_UADDR2TADDR\n");
+#endif
+ xdr_argument = (xdrproc_t)xdr_wrapstring;
+ xdr_result = (xdrproc_t)xdr_netbuf;
+ local = rpcbproc_uaddr2taddr_com;
+ break;
+
+ case RPCBPROC_TADDR2UADDR:
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_TADDR2UADDR\n");
+#endif
+ xdr_argument = (xdrproc_t)xdr_netbuf;
+ xdr_result = (xdrproc_t)xdr_wrapstring;
+ local = rpcbproc_taddr2uaddr_com;
+ break;
+
+ case RPCBPROC_GETADDRLIST:
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_GETADDRLIST\n");
+#endif
+ xdr_argument = (xdrproc_t)xdr_rpcb;
+ xdr_result = (xdrproc_t)xdr_rpcb_entry_list_ptr;
+ local = rpcbproc_getaddrlist_4_local;
+ break;
+
+ case RPCBPROC_GETSTAT:
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCBPROC_GETSTAT\n");
+#endif
+ xdr_argument = (xdrproc_t)xdr_void;
+ xdr_result = (xdrproc_t)xdr_rpcb_stat_byvers;
+ local = rpcbproc_getstat;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+ memset((char *)&argument, 0, sizeof (argument));
+ if (!svc_getargs(transp, (xdrproc_t) xdr_argument,
+ (char *)&argument)) {
+ svcerr_decode(transp);
+ if (debugging)
+ (void) fprintf(stderr, "rpcbind: could not decode\n");
+ return;
+ }
+ if (!check_access(transp, rqstp->rq_proc, &argument, RPCBVERS4)) {
+ svcerr_weakauth(transp);
+ goto done;
+ }
+ result = (*local)(&argument, rqstp, transp, RPCBVERS4);
+ if (result != NULL && !svc_sendreply(transp, (xdrproc_t) xdr_result,
+ result)) {
+ svcerr_systemerr(transp);
+ if (debugging) {
+ (void) fprintf(stderr, "rpcbind: svc_sendreply\n");
+ if (doabort) {
+ rpcbind_abort();
+ }
+ }
+ }
+done:
+ if (!svc_freeargs(transp, (xdrproc_t) xdr_argument,
+ (char *)&argument)) {
+ if (debugging) {
+ (void) fprintf(stderr, "unable to free arguments\n");
+ if (doabort) {
+ rpcbind_abort();
+ }
+ }
+ }
+ return;
+}
+
+/*
+ * Lookup the mapping for a program, version and return its
+ * address. Assuming that the caller wants the address of the
+ * server running on the transport on which the request came.
+ * Even if a service with a different version number is available,
+ * it will return that address. The client should check with an
+ * clnt_call to verify whether the service is the one that is desired.
+ * We also try to resolve the universal address in terms of
+ * address of the caller.
+ */
+/* ARGSUSED */
+static void *
+rpcbproc_getaddr_4_local(void *arg, struct svc_req *rqstp, SVCXPRT *transp,
+ rpcvers_t rpcbversnum __unused)
+{
+ RPCB *regp = (RPCB *)arg;
+#ifdef RPCBIND_DEBUG
+ if (debugging) {
+ char *uaddr;
+
+ uaddr = taddr2uaddr(rpcbind_get_conf(transp->xp_netid),
+ svc_getrpccaller(transp));
+ fprintf(stderr, "RPCB_GETADDR req for (%lu, %lu, %s) from %s: ",
+ (unsigned long)regp->r_prog, (unsigned long)regp->r_vers,
+ regp->r_netid, uaddr);
+ free(uaddr);
+ }
+#endif
+ return (rpcbproc_getaddr_com(regp, rqstp, transp, RPCBVERS4,
+ RPCB_ALLVERS));
+}
+
+/*
+ * Lookup the mapping for a program, version and return its
+ * address. Assuming that the caller wants the address of the
+ * server running on the transport on which the request came.
+ *
+ * We also try to resolve the universal address in terms of
+ * address of the caller.
+ */
+/* ARGSUSED */
+static void *
+rpcbproc_getversaddr_4_local(void *arg, struct svc_req *rqstp, SVCXPRT *transp,
+ rpcvers_t versnum __unused)
+{
+ RPCB *regp = (RPCB *)arg;
+#ifdef RPCBIND_DEBUG
+ if (debugging) {
+ char *uaddr;
+
+ uaddr = taddr2uaddr(rpcbind_get_conf(transp->xp_netid),
+ svc_getrpccaller(transp));
+ fprintf(stderr, "RPCB_GETVERSADDR rqst for (%lu, %lu, %s)"
+ " from %s : ",
+ (unsigned long)regp->r_prog, (unsigned long)regp->r_vers,
+ regp->r_netid, uaddr);
+ free(uaddr);
+ }
+#endif
+ return (rpcbproc_getaddr_com(regp, rqstp, transp, RPCBVERS4,
+ RPCB_ONEVERS));
+}
+
+/*
+ * Lookup the mapping for a program, version and return the
+ * addresses for all transports in the current transport family.
+ * We return a merged address.
+ */
+/* ARGSUSED */
+static void *
+rpcbproc_getaddrlist_4_local(void *arg, struct svc_req *rqstp __unused,
+ SVCXPRT *transp, rpcvers_t versnum __unused)
+{
+ RPCB *regp = (RPCB *)arg;
+ static rpcb_entry_list_ptr rlist;
+ register rpcblist_ptr rbl;
+ rpcb_entry_list_ptr rp, tail;
+ rpcprog_t prog;
+ rpcvers_t vers;
+ rpcb_entry *a;
+ struct netconfig *nconf;
+ struct netconfig *reg_nconf;
+ char *saddr, *maddr = NULL;
+
+ free_rpcb_entry_list(&rlist);
+ tail = NULL;
+ prog = regp->r_prog;
+ vers = regp->r_vers;
+ reg_nconf = rpcbind_get_conf(transp->xp_netid);
+ if (reg_nconf == NULL)
+ return (NULL);
+ if (*(regp->r_addr) != '\0') {
+ saddr = regp->r_addr;
+ } else {
+ saddr = NULL;
+ }
+#ifdef RPCBIND_DEBUG
+ if (debugging) {
+ fprintf(stderr, "r_addr: %s r_netid: %s nc_protofmly: %s\n",
+ regp->r_addr, regp->r_netid, reg_nconf->nc_protofmly);
+ }
+#endif
+ for (rbl = list_rbl; rbl != NULL; rbl = rbl->rpcb_next) {
+ if ((rbl->rpcb_map.r_prog == prog) &&
+ (rbl->rpcb_map.r_vers == vers)) {
+ nconf = rpcbind_get_conf(rbl->rpcb_map.r_netid);
+ if (nconf == NULL)
+ goto fail;
+ if (strcmp(nconf->nc_protofmly, reg_nconf->nc_protofmly)
+ != 0) {
+ continue; /* not same proto family */
+ }
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "\tmerge with: %s\n",
+ rbl->rpcb_map.r_addr);
+#endif
+ if ((maddr = mergeaddr(transp, rbl->rpcb_map.r_netid,
+ rbl->rpcb_map.r_addr, saddr)) == NULL) {
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, " FAILED\n");
+#endif
+ continue;
+ } else if (!maddr[0]) {
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, " SUCCEEDED, but port died - maddr: nullstring\n");
+#endif
+ /* The server died. Unset this combination */
+ delete_prog(regp->r_prog);
+ continue;
+ }
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, " SUCCEEDED maddr: %s\n", maddr);
+#endif
+ /*
+ * Add it to rlist.
+ */
+ rp = malloc(sizeof (rpcb_entry_list));
+ if (rp == NULL)
+ goto fail;
+ a = &rp->rpcb_entry_map;
+ a->r_maddr = maddr;
+ a->r_nc_netid = nconf->nc_netid;
+ a->r_nc_semantics = nconf->nc_semantics;
+ a->r_nc_protofmly = nconf->nc_protofmly;
+ a->r_nc_proto = nconf->nc_proto;
+ rp->rpcb_entry_next = NULL;
+ if (rlist == NULL) {
+ rlist = rp;
+ tail = rp;
+ } else {
+ tail->rpcb_entry_next = rp;
+ tail = rp;
+ }
+ rp = NULL;
+ }
+ }
+#ifdef RPCBIND_DEBUG
+ if (debugging) {
+ for (rp = rlist; rp; rp = rp->rpcb_entry_next) {
+ fprintf(stderr, "\t%s %s\n", rp->rpcb_entry_map.r_maddr,
+ rp->rpcb_entry_map.r_nc_proto);
+ }
+ }
+#endif
+ /*
+ * XXX: getaddrlist info is also being stuffed into getaddr.
+ * Perhaps wrong, but better than it not getting counted at all.
+ */
+ rpcbs_getaddr(RPCBVERS4 - 2, prog, vers, transp->xp_netid, maddr);
+ return (void *)&rlist;
+
+fail: free_rpcb_entry_list(&rlist);
+ return (NULL);
+}
+
+/*
+ * Free only the allocated structure, rest is all a pointer to some
+ * other data somewhere else.
+ */
+static void
+free_rpcb_entry_list(rpcb_entry_list_ptr *rlistp)
+{
+ register rpcb_entry_list_ptr rbl, tmp;
+
+ for (rbl = *rlistp; rbl != NULL; ) {
+ tmp = rbl;
+ rbl = rbl->rpcb_entry_next;
+ free((char *)tmp->rpcb_entry_map.r_maddr);
+ free((char *)tmp);
+ }
+ *rlistp = NULL;
+}
+
+/* ARGSUSED */
+static void *
+rpcbproc_dump_4_local(void *arg __unused, struct svc_req *req __unused,
+ SVCXPRT *xprt __unused, rpcvers_t versnum __unused)
+{
+ return ((void *)&list_rbl);
+}
diff --git a/usr.sbin/rpcbind/rpcb_svc_com.c b/usr.sbin/rpcbind/rpcb_svc_com.c
new file mode 100644
index 0000000..ab7c8ff
--- /dev/null
+++ b/usr.sbin/rpcbind/rpcb_svc_com.c
@@ -0,0 +1,1467 @@
+/* $NetBSD: rpcb_svc_com.c,v 1.9 2002/11/08 00:16:39 fvdl Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/*
+ * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc.
+ */
+
+/* #ident "@(#)rpcb_svc_com.c 1.18 94/05/02 SMI" */
+
+/*
+ * rpcb_svc_com.c
+ * The commom server procedure for the rpcbind.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <rpc/rpc.h>
+#include <rpc/rpcb_prot.h>
+#include <rpc/svc_dg.h>
+#include <netconfig.h>
+#include <errno.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <stdio.h>
+#ifdef PORTMAP
+#include <netinet/in.h>
+#include <rpc/pmap_prot.h>
+#endif /* PORTMAP */
+#include <string.h>
+#include <stdlib.h>
+
+#include "rpcbind.h"
+
+#define RPC_BUF_MAX 65536 /* can be raised if required */
+
+static char *nullstring = "";
+static int rpcb_rmtcalls;
+
+struct rmtcallfd_list {
+ int fd;
+ SVCXPRT *xprt;
+ char *netid;
+ struct rmtcallfd_list *next;
+};
+
+#define NFORWARD 64
+#define MAXTIME_OFF 300 /* 5 minutes */
+
+struct finfo {
+ int flag;
+#define FINFO_ACTIVE 0x1
+ u_int32_t caller_xid;
+ struct netbuf *caller_addr;
+ u_int32_t forward_xid;
+ int forward_fd;
+ char *uaddr;
+ rpcproc_t reply_type;
+ rpcvers_t versnum;
+ time_t time;
+};
+static struct finfo FINFO[NFORWARD];
+
+
+static bool_t xdr_encap_parms(XDR *, struct encap_parms *);
+static bool_t xdr_rmtcall_args(XDR *, struct r_rmtcall_args *);
+static bool_t xdr_rmtcall_result(XDR *, struct r_rmtcall_args *);
+static bool_t xdr_opaque_parms(XDR *, struct r_rmtcall_args *);
+static int find_rmtcallfd_by_netid(char *);
+static SVCXPRT *find_rmtcallxprt_by_fd(int);
+static int forward_register(u_int32_t, struct netbuf *, int, char *,
+ rpcproc_t, rpcvers_t, u_int32_t *);
+static struct finfo *forward_find(u_int32_t);
+static int free_slot_by_xid(u_int32_t);
+static int free_slot_by_index(int);
+static int netbufcmp(struct netbuf *, struct netbuf *);
+static struct netbuf *netbufdup(struct netbuf *);
+static void netbuffree(struct netbuf *);
+static int check_rmtcalls(struct pollfd *, int);
+static void xprt_set_caller(SVCXPRT *, struct finfo *);
+static void send_svcsyserr(SVCXPRT *, struct finfo *);
+static void handle_reply(int, SVCXPRT *);
+static void find_versions(rpcprog_t, char *, rpcvers_t *, rpcvers_t *);
+static rpcblist_ptr find_service(rpcprog_t, rpcvers_t, char *);
+static char *getowner(SVCXPRT *, char *, size_t);
+static int add_pmaplist(RPCB *);
+static int del_pmaplist(RPCB *);
+
+/*
+ * Set a mapping of program, version, netid
+ */
+/* ARGSUSED */
+void *
+rpcbproc_set_com(void *arg, struct svc_req *rqstp __unused, SVCXPRT *transp,
+ rpcvers_t rpcbversnum)
+{
+ RPCB *regp = (RPCB *)arg;
+ static bool_t ans;
+ char owner[64];
+
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCB_SET request for (%lu, %lu, %s, %s) : ",
+ (unsigned long)regp->r_prog, (unsigned long)regp->r_vers,
+ regp->r_netid, regp->r_addr);
+#endif
+ ans = map_set(regp, getowner(transp, owner, sizeof owner));
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "%s\n", ans == TRUE ? "succeeded" : "failed");
+#endif
+ /* XXX: should have used some defined constant here */
+ rpcbs_set(rpcbversnum - 2, ans);
+ return (void *)&ans;
+}
+
+bool_t
+map_set(RPCB *regp, char *owner)
+{
+ RPCB reg, *a;
+ rpcblist_ptr rbl, fnd;
+
+ reg = *regp;
+ /*
+ * check to see if already used
+ * find_service returns a hit even if
+ * the versions don't match, so check for it
+ */
+ fnd = find_service(reg.r_prog, reg.r_vers, reg.r_netid);
+ if (fnd && (fnd->rpcb_map.r_vers == reg.r_vers)) {
+ if (!strcmp(fnd->rpcb_map.r_addr, reg.r_addr))
+ /*
+ * if these match then it is already
+ * registered so just say "OK".
+ */
+ return (TRUE);
+ else
+ return (FALSE);
+ }
+ /*
+ * add to the end of the list
+ */
+ rbl = malloc(sizeof (RPCBLIST));
+ if (rbl == NULL)
+ return (FALSE);
+ a = &(rbl->rpcb_map);
+ a->r_prog = reg.r_prog;
+ a->r_vers = reg.r_vers;
+ a->r_netid = strdup(reg.r_netid);
+ a->r_addr = strdup(reg.r_addr);
+ a->r_owner = strdup(owner);
+ if (!a->r_addr || !a->r_netid || !a->r_owner) {
+ if (a->r_netid)
+ free(a->r_netid);
+ if (a->r_addr)
+ free(a->r_addr);
+ if (a->r_owner)
+ free(a->r_owner);
+ free(rbl);
+ return (FALSE);
+ }
+ rbl->rpcb_next = (rpcblist_ptr)NULL;
+ if (list_rbl == NULL) {
+ list_rbl = rbl;
+ } else {
+ for (fnd = list_rbl; fnd->rpcb_next;
+ fnd = fnd->rpcb_next)
+ ;
+ fnd->rpcb_next = rbl;
+ }
+#ifdef PORTMAP
+ (void) add_pmaplist(regp);
+#endif
+ return (TRUE);
+}
+
+/*
+ * Unset a mapping of program, version, netid
+ */
+/* ARGSUSED */
+void *
+rpcbproc_unset_com(void *arg, struct svc_req *rqstp __unused, SVCXPRT *transp,
+ rpcvers_t rpcbversnum)
+{
+ RPCB *regp = (RPCB *)arg;
+ static bool_t ans;
+ char owner[64];
+
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "RPCB_UNSET request for (%lu, %lu, %s) : ",
+ (unsigned long)regp->r_prog, (unsigned long)regp->r_vers,
+ regp->r_netid);
+#endif
+ ans = map_unset(regp, getowner(transp, owner, sizeof owner));
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "%s\n", ans == TRUE ? "succeeded" : "failed");
+#endif
+ /* XXX: should have used some defined constant here */
+ rpcbs_unset(rpcbversnum - 2, ans);
+ return (void *)&ans;
+}
+
+bool_t
+map_unset(RPCB *regp, char *owner)
+{
+ int ans = 0;
+ rpcblist_ptr rbl, prev, tmp;
+
+ if (owner == NULL)
+ return (0);
+
+ for (prev = NULL, rbl = list_rbl; rbl; /* cstyle */) {
+ if ((rbl->rpcb_map.r_prog != regp->r_prog) ||
+ (rbl->rpcb_map.r_vers != regp->r_vers) ||
+ (regp->r_netid[0] && strcasecmp(regp->r_netid,
+ rbl->rpcb_map.r_netid))) {
+ /* both rbl & prev move forwards */
+ prev = rbl;
+ rbl = rbl->rpcb_next;
+ continue;
+ }
+ /*
+ * Check whether appropriate uid. Unset only
+ * if superuser or the owner itself.
+ */
+ if (strcmp(owner, "superuser") &&
+ strcmp(rbl->rpcb_map.r_owner, owner))
+ return (0);
+ /* found it; rbl moves forward, prev stays */
+ ans = 1;
+ tmp = rbl;
+ rbl = rbl->rpcb_next;
+ if (prev == NULL)
+ list_rbl = rbl;
+ else
+ prev->rpcb_next = rbl;
+ free(tmp->rpcb_map.r_addr);
+ free(tmp->rpcb_map.r_netid);
+ free(tmp->rpcb_map.r_owner);
+ free(tmp);
+ }
+#ifdef PORTMAP
+ if (ans)
+ (void) del_pmaplist(regp);
+#endif
+ /*
+ * We return 1 either when the entry was not there or it
+ * was able to unset it. It can come to this point only if
+ * atleast one of the conditions is true.
+ */
+ return (1);
+}
+
+void
+delete_prog(unsigned int prog)
+{
+ RPCB reg;
+ register rpcblist_ptr rbl;
+
+ for (rbl = list_rbl; rbl != NULL; rbl = rbl->rpcb_next) {
+ if ((rbl->rpcb_map.r_prog != prog))
+ continue;
+ if (is_bound(rbl->rpcb_map.r_netid, rbl->rpcb_map.r_addr))
+ continue;
+ reg.r_prog = rbl->rpcb_map.r_prog;
+ reg.r_vers = rbl->rpcb_map.r_vers;
+ reg.r_netid = strdup(rbl->rpcb_map.r_netid);
+ (void) map_unset(&reg, "superuser");
+ free(reg.r_netid);
+ }
+}
+
+void *
+rpcbproc_getaddr_com(RPCB *regp, struct svc_req *rqstp __unused,
+ SVCXPRT *transp, rpcvers_t rpcbversnum, rpcvers_t verstype)
+{
+ static char *uaddr;
+ char *saddr = NULL;
+ rpcblist_ptr fnd;
+
+ if (uaddr != NULL && uaddr != nullstring) {
+ free(uaddr);
+ uaddr = NULL;
+ }
+ fnd = find_service(regp->r_prog, regp->r_vers, transp->xp_netid);
+ if (fnd && ((verstype == RPCB_ALLVERS) ||
+ (regp->r_vers == fnd->rpcb_map.r_vers))) {
+ if (*(regp->r_addr) != '\0') { /* may contain a hint about */
+ saddr = regp->r_addr; /* the interface that we */
+ } /* should use */
+ if (!(uaddr = mergeaddr(transp, transp->xp_netid,
+ fnd->rpcb_map.r_addr, saddr))) {
+ /* Try whatever we have */
+ uaddr = strdup(fnd->rpcb_map.r_addr);
+ } else if (!uaddr[0]) {
+ /*
+ * The server died. Unset all versions of this prog.
+ */
+ delete_prog(regp->r_prog);
+ uaddr = nullstring;
+ }
+ } else {
+ uaddr = nullstring;
+ }
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "getaddr: %s\n", uaddr);
+#endif
+ /* XXX: should have used some defined constant here */
+ rpcbs_getaddr(rpcbversnum - 2, regp->r_prog, regp->r_vers,
+ transp->xp_netid, uaddr);
+ return (void *)&uaddr;
+}
+
+/* ARGSUSED */
+void *
+rpcbproc_gettime_com(void *arg __unused, struct svc_req *rqstp __unused,
+ SVCXPRT *transp __unused, rpcvers_t rpcbversnum __unused)
+{
+ static time_t curtime;
+
+ (void) time(&curtime);
+ return (void *)&curtime;
+}
+
+/*
+ * Convert uaddr to taddr. Should be used only by
+ * local servers/clients. (kernel level stuff only)
+ */
+/* ARGSUSED */
+void *
+rpcbproc_uaddr2taddr_com(void *arg, struct svc_req *rqstp __unused,
+ SVCXPRT *transp, rpcvers_t rpcbversnum __unused)
+{
+ char **uaddrp = (char **)arg;
+ struct netconfig *nconf;
+ static struct netbuf nbuf;
+ static struct netbuf *taddr;
+
+ if (taddr) {
+ free(taddr->buf);
+ free(taddr);
+ taddr = NULL;
+ }
+ if (((nconf = rpcbind_get_conf(transp->xp_netid)) == NULL) ||
+ ((taddr = uaddr2taddr(nconf, *uaddrp)) == NULL)) {
+ (void) memset((char *)&nbuf, 0, sizeof (struct netbuf));
+ return (void *)&nbuf;
+ }
+ return (void *)taddr;
+}
+
+/*
+ * Convert taddr to uaddr. Should be used only by
+ * local servers/clients. (kernel level stuff only)
+ */
+/* ARGSUSED */
+void *
+rpcbproc_taddr2uaddr_com(void *arg, struct svc_req *rqstp __unused,
+ SVCXPRT *transp, rpcvers_t rpcbversnum __unused)
+{
+ struct netbuf *taddr = (struct netbuf *)arg;
+ static char *uaddr;
+ struct netconfig *nconf;
+
+#ifdef CHEW_FDS
+ int fd;
+
+ if ((fd = open("/dev/null", O_RDONLY)) == -1) {
+ uaddr = (char *)strerror(errno);
+ return (&uaddr);
+ }
+#endif /* CHEW_FDS */
+ if (uaddr != NULL && uaddr != nullstring) {
+ free(uaddr);
+ uaddr = NULL;
+ }
+ if (((nconf = rpcbind_get_conf(transp->xp_netid)) == NULL) ||
+ ((uaddr = taddr2uaddr(nconf, taddr)) == NULL)) {
+ uaddr = nullstring;
+ }
+ return (void *)&uaddr;
+}
+
+
+static bool_t
+xdr_encap_parms(XDR *xdrs, struct encap_parms *epp)
+{
+ return (xdr_bytes(xdrs, &(epp->args), (u_int *) &(epp->arglen), ~0));
+}
+
+/*
+ * XDR remote call arguments. It ignores the address part.
+ * written for XDR_DECODE direction only
+ */
+static bool_t
+xdr_rmtcall_args(XDR *xdrs, struct r_rmtcall_args *cap)
+{
+ /* does not get the address or the arguments */
+ if (xdr_u_int32_t(xdrs, &(cap->rmt_prog)) &&
+ xdr_u_int32_t(xdrs, &(cap->rmt_vers)) &&
+ xdr_u_int32_t(xdrs, &(cap->rmt_proc))) {
+ return (xdr_encap_parms(xdrs, &(cap->rmt_args)));
+ }
+ return (FALSE);
+}
+
+/*
+ * XDR remote call results along with the address. Ignore
+ * program number, version number and proc number.
+ * Written for XDR_ENCODE direction only.
+ */
+static bool_t
+xdr_rmtcall_result(XDR *xdrs, struct r_rmtcall_args *cap)
+{
+ bool_t result;
+
+#ifdef PORTMAP
+ if (cap->rmt_localvers == PMAPVERS) {
+ int h1, h2, h3, h4, p1, p2;
+ u_long port;
+
+ /* interpret the universal address for TCP/IP */
+ if (sscanf(cap->rmt_uaddr, "%d.%d.%d.%d.%d.%d",
+ &h1, &h2, &h3, &h4, &p1, &p2) != 6)
+ return (FALSE);
+ port = ((p1 & 0xff) << 8) + (p2 & 0xff);
+ result = xdr_u_long(xdrs, &port);
+ } else
+#endif
+ if ((cap->rmt_localvers == RPCBVERS) ||
+ (cap->rmt_localvers == RPCBVERS4)) {
+ result = xdr_wrapstring(xdrs, &(cap->rmt_uaddr));
+ } else {
+ return (FALSE);
+ }
+ if (result == TRUE)
+ return (xdr_encap_parms(xdrs, &(cap->rmt_args)));
+ return (FALSE);
+}
+
+/*
+ * only worries about the struct encap_parms part of struct r_rmtcall_args.
+ * The arglen must already be set!!
+ */
+static bool_t
+xdr_opaque_parms(XDR *xdrs, struct r_rmtcall_args *cap)
+{
+ return (xdr_opaque(xdrs, cap->rmt_args.args, cap->rmt_args.arglen));
+}
+
+static struct rmtcallfd_list *rmthead;
+static struct rmtcallfd_list *rmttail;
+
+int
+create_rmtcall_fd(struct netconfig *nconf)
+{
+ int fd;
+ struct rmtcallfd_list *rmt;
+ SVCXPRT *xprt;
+
+ if ((fd = __rpc_nconf2fd(nconf)) == -1) {
+ if (debugging)
+ fprintf(stderr,
+ "create_rmtcall_fd: couldn't open \"%s\" (errno %d)\n",
+ nconf->nc_device, errno);
+ return (-1);
+ }
+ xprt = svc_tli_create(fd, 0, (struct t_bind *) 0, 0, 0);
+ if (xprt == NULL) {
+ if (debugging)
+ fprintf(stderr,
+ "create_rmtcall_fd: svc_tli_create failed\n");
+ return (-1);
+ }
+ rmt = malloc(sizeof (struct rmtcallfd_list));
+ if (rmt == NULL) {
+ syslog(LOG_ERR, "create_rmtcall_fd: no memory!");
+ return (-1);
+ }
+ rmt->xprt = xprt;
+ rmt->netid = strdup(nconf->nc_netid);
+ xprt->xp_netid = rmt->netid;
+ rmt->fd = fd;
+ rmt->next = NULL;
+ if (rmthead == NULL) {
+ rmthead = rmt;
+ rmttail = rmt;
+ } else {
+ rmttail->next = rmt;
+ rmttail = rmt;
+ }
+ /* XXX not threadsafe */
+ if (fd > svc_maxfd)
+ svc_maxfd = fd;
+ FD_SET(fd, &svc_fdset);
+ return (fd);
+}
+
+static int
+find_rmtcallfd_by_netid(char *netid)
+{
+ struct rmtcallfd_list *rmt;
+
+ for (rmt = rmthead; rmt != NULL; rmt = rmt->next) {
+ if (strcmp(netid, rmt->netid) == 0) {
+ return (rmt->fd);
+ }
+ }
+ return (-1);
+}
+
+static SVCXPRT *
+find_rmtcallxprt_by_fd(int fd)
+{
+ struct rmtcallfd_list *rmt;
+
+ for (rmt = rmthead; rmt != NULL; rmt = rmt->next) {
+ if (fd == rmt->fd) {
+ return (rmt->xprt);
+ }
+ }
+ return (NULL);
+}
+
+
+/*
+ * Call a remote procedure service. This procedure is very quiet when things
+ * go wrong. The proc is written to support broadcast rpc. In the broadcast
+ * case, a machine should shut-up instead of complain, lest the requestor be
+ * overrun with complaints at the expense of not hearing a valid reply.
+ * When receiving a request and verifying that the service exists, we
+ *
+ * receive the request
+ *
+ * open a new TLI endpoint on the same transport on which we received
+ * the original request
+ *
+ * remember the original request's XID (which requires knowing the format
+ * of the svc_dg_data structure)
+ *
+ * forward the request, with a new XID, to the requested service,
+ * remembering the XID used to send this request (for later use in
+ * reassociating the answer with the original request), the requestor's
+ * address, the file descriptor on which the forwarded request is
+ * made and the service's address.
+ *
+ * mark the file descriptor on which we anticipate receiving a reply from
+ * the service and one to select for in our private svc_run procedure
+ *
+ * At some time in the future, a reply will be received from the service to
+ * which we forwarded the request. At that time, we detect that the socket
+ * used was for forwarding (by looking through the finfo structures to see
+ * whether the fd corresponds to one of those) and call handle_reply() to
+ *
+ * receive the reply
+ *
+ * bundle the reply, along with the service's universal address
+ *
+ * create a SVCXPRT structure and use a version of svc_sendreply
+ * that allows us to specify the reply XID and destination, send the reply
+ * to the original requestor.
+ */
+
+void
+rpcbproc_callit_com(struct svc_req *rqstp, SVCXPRT *transp,
+ rpcproc_t reply_type, rpcvers_t versnum)
+{
+ register rpcblist_ptr rbl;
+ struct netconfig *nconf;
+ struct netbuf *caller;
+ struct r_rmtcall_args a;
+ char *buf_alloc = NULL, *outbufp;
+ char *outbuf_alloc = NULL;
+ char buf[RPC_BUF_MAX], outbuf[RPC_BUF_MAX];
+ struct netbuf *na = (struct netbuf *) NULL;
+ struct rpc_msg call_msg;
+ int outlen;
+ u_int sendsz;
+ XDR outxdr;
+ AUTH *auth;
+ int fd = -1;
+ char *uaddr, *m_uaddr = NULL, *local_uaddr = NULL;
+ u_int32_t *xidp;
+ struct __rpc_sockinfo si;
+ struct sockaddr *localsa;
+ struct netbuf tbuf;
+
+ if (!__rpc_fd2sockinfo(transp->xp_fd, &si)) {
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_systemerr(transp);
+ return;
+ }
+ if (si.si_socktype != SOCK_DGRAM)
+ return; /* Only datagram type accepted */
+ sendsz = __rpc_get_t_size(si.si_af, si.si_proto, UDPMSGSIZE);
+ if (sendsz == 0) { /* data transfer not supported */
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_systemerr(transp);
+ return;
+ }
+ /*
+ * Should be multiple of 4 for XDR.
+ */
+ sendsz = ((sendsz + 3) / 4) * 4;
+ if (sendsz > RPC_BUF_MAX) {
+#ifdef notyet
+ buf_alloc = alloca(sendsz); /* not in IDR2? */
+#else
+ buf_alloc = malloc(sendsz);
+#endif /* notyet */
+ if (buf_alloc == NULL) {
+ if (debugging)
+ fprintf(stderr,
+ "rpcbproc_callit_com: No Memory!\n");
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_systemerr(transp);
+ return;
+ }
+ a.rmt_args.args = buf_alloc;
+ } else {
+ a.rmt_args.args = buf;
+ }
+
+ call_msg.rm_xid = 0; /* For error checking purposes */
+ if (!svc_getargs(transp, (xdrproc_t) xdr_rmtcall_args, (char *) &a)) {
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_decode(transp);
+ if (debugging)
+ fprintf(stderr,
+ "rpcbproc_callit_com: svc_getargs failed\n");
+ goto error;
+ }
+
+ if (!check_callit(transp, &a, versnum)) {
+ svcerr_weakauth(transp);
+ goto error;
+ }
+
+ caller = svc_getrpccaller(transp);
+#ifdef RPCBIND_DEBUG
+ if (debugging) {
+ uaddr = taddr2uaddr(rpcbind_get_conf(transp->xp_netid), caller);
+ fprintf(stderr, "%s %s req for (%lu, %lu, %lu, %s) from %s : ",
+ versnum == PMAPVERS ? "pmap_rmtcall" :
+ versnum == RPCBVERS ? "rpcb_rmtcall" :
+ versnum == RPCBVERS4 ? "rpcb_indirect" : "unknown",
+ reply_type == RPCBPROC_INDIRECT ? "indirect" : "callit",
+ (unsigned long)a.rmt_prog, (unsigned long)a.rmt_vers,
+ (unsigned long)a.rmt_proc, transp->xp_netid,
+ uaddr ? uaddr : "unknown");
+ if (uaddr)
+ free(uaddr);
+ }
+#endif
+
+ rbl = find_service(a.rmt_prog, a.rmt_vers, transp->xp_netid);
+
+ rpcbs_rmtcall(versnum - 2, reply_type, a.rmt_prog, a.rmt_vers,
+ a.rmt_proc, transp->xp_netid, rbl);
+
+ if (rbl == (rpcblist_ptr)NULL) {
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "not found\n");
+#endif
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_noprog(transp);
+ goto error;
+ }
+ if (rbl->rpcb_map.r_vers != a.rmt_vers) {
+ if (reply_type == RPCBPROC_INDIRECT) {
+ rpcvers_t vers_low, vers_high;
+
+ find_versions(a.rmt_prog, transp->xp_netid,
+ &vers_low, &vers_high);
+ svcerr_progvers(transp, vers_low, vers_high);
+ }
+ goto error;
+ }
+
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "found at uaddr %s\n", rbl->rpcb_map.r_addr);
+#endif
+ /*
+ * Check whether this entry is valid and a server is present
+ * Mergeaddr() returns NULL if no such entry is present, and
+ * returns "" if the entry was present but the server is not
+ * present (i.e., it crashed).
+ */
+ if (reply_type == RPCBPROC_INDIRECT) {
+ uaddr = mergeaddr(transp, transp->xp_netid,
+ rbl->rpcb_map.r_addr, NULL);
+ if (uaddr == NULL || uaddr[0] == '\0') {
+ svcerr_noprog(transp);
+ if (uaddr != NULL)
+ free(uaddr);
+ goto error;
+ }
+ free(uaddr);
+ }
+ nconf = rpcbind_get_conf(transp->xp_netid);
+ if (nconf == (struct netconfig *)NULL) {
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_systemerr(transp);
+ if (debugging)
+ fprintf(stderr,
+ "rpcbproc_callit_com: rpcbind_get_conf failed\n");
+ goto error;
+ }
+ localsa = local_sa(((struct sockaddr *)caller->buf)->sa_family);
+ if (localsa == NULL) {
+ if (debugging)
+ fprintf(stderr,
+ "rpcbproc_callit_com: no local address\n");
+ goto error;
+ }
+ tbuf.len = tbuf.maxlen = localsa->sa_len;
+ tbuf.buf = localsa;
+ local_uaddr =
+ addrmerge(&tbuf, rbl->rpcb_map.r_addr, NULL, nconf->nc_netid);
+ m_uaddr = addrmerge(caller, rbl->rpcb_map.r_addr, NULL,
+ nconf->nc_netid);
+#ifdef RPCBIND_DEBUG
+ if (debugging)
+ fprintf(stderr, "merged uaddr %s\n", m_uaddr);
+#endif
+ if ((fd = find_rmtcallfd_by_netid(nconf->nc_netid)) == -1) {
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_systemerr(transp);
+ goto error;
+ }
+ xidp = __rpcb_get_dg_xidp(transp);
+ switch (forward_register(*xidp, caller, fd, m_uaddr, reply_type,
+ versnum, &call_msg.rm_xid)) {
+ case 1:
+ /* Success; forward_register() will free m_uaddr for us. */
+ m_uaddr = NULL;
+ break;
+ case 0:
+ /*
+ * A duplicate request for the slow server. Let's not
+ * beat on it any more.
+ */
+ if (debugging)
+ fprintf(stderr,
+ "rpcbproc_callit_com: duplicate request\n");
+ goto error;
+ case -1:
+ /* forward_register failed. Perhaps no memory. */
+ if (debugging)
+ fprintf(stderr,
+ "rpcbproc_callit_com: forward_register failed\n");
+ goto error;
+ }
+
+#ifdef DEBUG_RMTCALL
+ if (debugging)
+ fprintf(stderr,
+ "rpcbproc_callit_com: original XID %x, new XID %x\n",
+ *xidp, call_msg.rm_xid);
+#endif
+ call_msg.rm_direction = CALL;
+ call_msg.rm_call.cb_rpcvers = RPC_MSG_VERSION;
+ call_msg.rm_call.cb_prog = a.rmt_prog;
+ call_msg.rm_call.cb_vers = a.rmt_vers;
+ if (sendsz > RPC_BUF_MAX) {
+#ifdef notyet
+ outbuf_alloc = alloca(sendsz); /* not in IDR2? */
+#else
+ outbuf_alloc = malloc(sendsz);
+#endif /* notyet */
+ if (outbuf_alloc == NULL) {
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_systemerr(transp);
+ if (debugging)
+ fprintf(stderr,
+ "rpcbproc_callit_com: No memory!\n");
+ goto error;
+ }
+ xdrmem_create(&outxdr, outbuf_alloc, sendsz, XDR_ENCODE);
+ } else {
+ xdrmem_create(&outxdr, outbuf, sendsz, XDR_ENCODE);
+ }
+ if (!xdr_callhdr(&outxdr, &call_msg)) {
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_systemerr(transp);
+ if (debugging)
+ fprintf(stderr,
+ "rpcbproc_callit_com: xdr_callhdr failed\n");
+ goto error;
+ }
+ if (!xdr_u_int32_t(&outxdr, &(a.rmt_proc))) {
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_systemerr(transp);
+ if (debugging)
+ fprintf(stderr,
+ "rpcbproc_callit_com: xdr_u_long failed\n");
+ goto error;
+ }
+
+ if (rqstp->rq_cred.oa_flavor == AUTH_NULL) {
+ auth = authnone_create();
+ } else if (rqstp->rq_cred.oa_flavor == AUTH_SYS) {
+ struct authunix_parms *au;
+
+ au = (struct authunix_parms *)rqstp->rq_clntcred;
+ auth = authunix_create(au->aup_machname,
+ au->aup_uid, au->aup_gid,
+ au->aup_len, au->aup_gids);
+ if (auth == NULL) /* fall back */
+ auth = authnone_create();
+ } else {
+ /* we do not support any other authentication scheme */
+ if (debugging)
+ fprintf(stderr,
+"rpcbproc_callit_com: oa_flavor != AUTH_NONE and oa_flavor != AUTH_SYS\n");
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_weakauth(transp); /* XXX too strong.. */
+ goto error;
+ }
+ if (auth == NULL) {
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_systemerr(transp);
+ if (debugging)
+ fprintf(stderr,
+ "rpcbproc_callit_com: authwhatever_create returned NULL\n");
+ goto error;
+ }
+ if (!AUTH_MARSHALL(auth, &outxdr)) {
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_systemerr(transp);
+ AUTH_DESTROY(auth);
+ if (debugging)
+ fprintf(stderr,
+ "rpcbproc_callit_com: AUTH_MARSHALL failed\n");
+ goto error;
+ }
+ AUTH_DESTROY(auth);
+ if (!xdr_opaque_parms(&outxdr, &a)) {
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_systemerr(transp);
+ if (debugging)
+ fprintf(stderr,
+ "rpcbproc_callit_com: xdr_opaque_parms failed\n");
+ goto error;
+ }
+ outlen = (int) XDR_GETPOS(&outxdr);
+ if (outbuf_alloc)
+ outbufp = outbuf_alloc;
+ else
+ outbufp = outbuf;
+
+ na = uaddr2taddr(nconf, local_uaddr);
+ if (!na) {
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_systemerr(transp);
+ goto error;
+ }
+
+ if (sendto(fd, outbufp, outlen, 0, (struct sockaddr *)na->buf, na->len)
+ != outlen) {
+ if (debugging)
+ fprintf(stderr,
+ "rpcbproc_callit_com: sendto failed: errno %d\n", errno);
+ if (reply_type == RPCBPROC_INDIRECT)
+ svcerr_systemerr(transp);
+ goto error;
+ }
+ goto out;
+
+error:
+ if (call_msg.rm_xid != 0)
+ (void) free_slot_by_xid(call_msg.rm_xid);
+out:
+ if (local_uaddr)
+ free(local_uaddr);
+ if (buf_alloc)
+ free(buf_alloc);
+ if (outbuf_alloc)
+ free(outbuf_alloc);
+ if (na) {
+ free(na->buf);
+ free(na);
+ }
+ if (m_uaddr != NULL)
+ free(m_uaddr);
+}
+
+/*
+ * Makes an entry into the FIFO for the given request.
+ * Returns 1 on success, 0 if this is a duplicate request, or -1 on error.
+ * *callxidp is set to the xid of the call.
+ */
+static int
+forward_register(u_int32_t caller_xid, struct netbuf *caller_addr,
+ int forward_fd, char *uaddr, rpcproc_t reply_type,
+ rpcvers_t versnum, u_int32_t *callxidp)
+{
+ int i;
+ int j = 0;
+ time_t min_time, time_now;
+ static u_int32_t lastxid;
+ int entry = -1;
+
+ min_time = FINFO[0].time;
+ time_now = time((time_t *)0);
+ /* initialization */
+ if (lastxid == 0)
+ lastxid = time_now * NFORWARD;
+
+ /*
+ * Check if it is a duplicate entry. Then,
+ * try to find an empty slot. If not available, then
+ * use the slot with the earliest time.
+ */
+ for (i = 0; i < NFORWARD; i++) {
+ if (FINFO[i].flag & FINFO_ACTIVE) {
+ if ((FINFO[i].caller_xid == caller_xid) &&
+ (FINFO[i].reply_type == reply_type) &&
+ (FINFO[i].versnum == versnum) &&
+ (!netbufcmp(FINFO[i].caller_addr,
+ caller_addr))) {
+ FINFO[i].time = time((time_t *)0);
+ return (0); /* Duplicate entry */
+ } else {
+ /* Should we wait any longer */
+ if ((time_now - FINFO[i].time) > MAXTIME_OFF)
+ (void) free_slot_by_index(i);
+ }
+ }
+ if (entry == -1) {
+ if ((FINFO[i].flag & FINFO_ACTIVE) == 0) {
+ entry = i;
+ } else if (FINFO[i].time < min_time) {
+ j = i;
+ min_time = FINFO[i].time;
+ }
+ }
+ }
+ if (entry != -1) {
+ /* use this empty slot */
+ j = entry;
+ } else {
+ (void) free_slot_by_index(j);
+ }
+ if ((FINFO[j].caller_addr = netbufdup(caller_addr)) == NULL) {
+ return (-1);
+ }
+ rpcb_rmtcalls++; /* no of pending calls */
+ FINFO[j].flag = FINFO_ACTIVE;
+ FINFO[j].reply_type = reply_type;
+ FINFO[j].versnum = versnum;
+ FINFO[j].time = time_now;
+ FINFO[j].caller_xid = caller_xid;
+ FINFO[j].forward_fd = forward_fd;
+ /*
+ * Though uaddr is not allocated here, it will still be freed
+ * from free_slot_*().
+ */
+ FINFO[j].uaddr = uaddr;
+ lastxid = lastxid + NFORWARD;
+ /* Don't allow a zero xid below. */
+ if ((u_int32_t)(lastxid + NFORWARD) <= NFORWARD)
+ lastxid = NFORWARD;
+ FINFO[j].forward_xid = lastxid + j; /* encode slot */
+ *callxidp = FINFO[j].forward_xid; /* forward on this xid */
+ return (1);
+}
+
+static struct finfo *
+forward_find(u_int32_t reply_xid)
+{
+ int i;
+
+ i = reply_xid % (u_int32_t)NFORWARD;
+ if ((FINFO[i].flag & FINFO_ACTIVE) &&
+ (FINFO[i].forward_xid == reply_xid)) {
+ return (&FINFO[i]);
+ }
+ return (NULL);
+}
+
+static int
+free_slot_by_xid(u_int32_t xid)
+{
+ int entry;
+
+ entry = xid % (u_int32_t)NFORWARD;
+ return (free_slot_by_index(entry));
+}
+
+static int
+free_slot_by_index(int index)
+{
+ struct finfo *fi;
+
+ fi = &FINFO[index];
+ if (fi->flag & FINFO_ACTIVE) {
+ netbuffree(fi->caller_addr);
+ /* XXX may be too big, but can't access xprt array here */
+ if (fi->forward_fd >= svc_maxfd)
+ svc_maxfd--;
+ free(fi->uaddr);
+ fi->flag &= ~FINFO_ACTIVE;
+ rpcb_rmtcalls--;
+ return (1);
+ }
+ return (0);
+}
+
+static int
+netbufcmp(struct netbuf *n1, struct netbuf *n2)
+{
+ return ((n1->len != n2->len) || memcmp(n1->buf, n2->buf, n1->len));
+}
+
+static struct netbuf *
+netbufdup(struct netbuf *ap)
+{
+ struct netbuf *np;
+
+ if ((np = malloc(sizeof(struct netbuf))) == NULL)
+ return (NULL);
+ if ((np->buf = malloc(ap->len)) == NULL) {
+ free(np);
+ return (NULL);
+ }
+ np->maxlen = np->len = ap->len;
+ memcpy(np->buf, ap->buf, ap->len);
+ return (np);
+}
+
+static void
+netbuffree(struct netbuf *ap)
+{
+ free(ap->buf);
+ free(ap);
+}
+
+
+#define MASKVAL (POLLIN | POLLPRI | POLLRDNORM | POLLRDBAND)
+extern bool_t __svc_clean_idle(fd_set *, int, bool_t);
+
+void
+my_svc_run()
+{
+ size_t nfds;
+ struct pollfd pollfds[FD_SETSIZE];
+ int poll_ret, check_ret;
+ int n;
+#ifdef SVC_RUN_DEBUG
+ int i;
+#endif
+ register struct pollfd *p;
+ fd_set cleanfds;
+
+ for (;;) {
+ p = pollfds;
+ for (n = 0; n <= svc_maxfd; n++) {
+ if (FD_ISSET(n, &svc_fdset)) {
+ p->fd = n;
+ p->events = MASKVAL;
+ p++;
+ }
+ }
+ nfds = p - pollfds;
+ poll_ret = 0;
+#ifdef SVC_RUN_DEBUG
+ if (debugging) {
+ fprintf(stderr, "polling for read on fd < ");
+ for (i = 0, p = pollfds; i < nfds; i++, p++)
+ if (p->events)
+ fprintf(stderr, "%d ", p->fd);
+ fprintf(stderr, ">\n");
+ }
+#endif
+ switch (poll_ret = poll(pollfds, nfds, 30 * 1000)) {
+ case -1:
+ /*
+ * We ignore all errors, continuing with the assumption
+ * that it was set by the signal handlers (or any
+ * other outside event) and not caused by poll().
+ */
+ case 0:
+ cleanfds = svc_fdset;
+ __svc_clean_idle(&cleanfds, 30, FALSE);
+ continue;
+ default:
+#ifdef SVC_RUN_DEBUG
+ if (debugging) {
+ fprintf(stderr, "poll returned read fds < ");
+ for (i = 0, p = pollfds; i < nfds; i++, p++)
+ if (p->revents)
+ fprintf(stderr, "%d ", p->fd);
+ fprintf(stderr, ">\n");
+ }
+#endif
+ /*
+ * If we found as many replies on callback fds
+ * as the number of descriptors selectable which
+ * poll() returned, there can be no more so we
+ * don't call svc_getreq_poll. Otherwise, there
+ * must be another so we must call svc_getreq_poll.
+ */
+ if ((check_ret = check_rmtcalls(pollfds, nfds)) ==
+ poll_ret)
+ continue;
+ svc_getreq_poll(pollfds, poll_ret-check_ret);
+ }
+#ifdef SVC_RUN_DEBUG
+ if (debugging) {
+ fprintf(stderr, "svc_maxfd now %u\n", svc_maxfd);
+ }
+#endif
+ }
+}
+
+static int
+check_rmtcalls(struct pollfd *pfds, int nfds)
+{
+ int j, ncallbacks_found = 0, rmtcalls_pending;
+ SVCXPRT *xprt;
+
+ if (rpcb_rmtcalls == 0)
+ return (0);
+
+ rmtcalls_pending = rpcb_rmtcalls;
+ for (j = 0; j < nfds; j++) {
+ if ((xprt = find_rmtcallxprt_by_fd(pfds[j].fd)) != NULL) {
+ if (pfds[j].revents) {
+ ncallbacks_found++;
+#ifdef DEBUG_RMTCALL
+ if (debugging)
+ fprintf(stderr,
+"my_svc_run: polled on forwarding fd %d, netid %s - calling handle_reply\n",
+ pfds[j].fd, xprt->xp_netid);
+#endif
+ handle_reply(pfds[j].fd, xprt);
+ pfds[j].revents = 0;
+ if (ncallbacks_found >= rmtcalls_pending) {
+ break;
+ }
+ }
+ }
+ }
+ return (ncallbacks_found);
+}
+
+static void
+xprt_set_caller(SVCXPRT *xprt, struct finfo *fi)
+{
+ u_int32_t *xidp;
+
+ *(svc_getrpccaller(xprt)) = *(fi->caller_addr);
+ xidp = __rpcb_get_dg_xidp(xprt);
+ *xidp = fi->caller_xid;
+}
+
+/*
+ * Call svcerr_systemerr() only if RPCBVERS4
+ */
+static void
+send_svcsyserr(SVCXPRT *xprt, struct finfo *fi)
+{
+ if (fi->reply_type == RPCBPROC_INDIRECT) {
+ xprt_set_caller(xprt, fi);
+ svcerr_systemerr(xprt);
+ }
+ return;
+}
+
+static void
+handle_reply(int fd, SVCXPRT *xprt)
+{
+ XDR reply_xdrs;
+ struct rpc_msg reply_msg;
+ struct rpc_err reply_error;
+ char *buffer;
+ struct finfo *fi;
+ int inlen, pos, len;
+ struct r_rmtcall_args a;
+ struct sockaddr_storage ss;
+ socklen_t fromlen;
+#ifdef SVC_RUN_DEBUG
+ char *uaddr;
+#endif
+
+ buffer = malloc(RPC_BUF_MAX);
+ if (buffer == NULL)
+ goto done;
+
+ do {
+ inlen = recvfrom(fd, buffer, RPC_BUF_MAX, 0,
+ (struct sockaddr *)&ss, &fromlen);
+ } while (inlen < 0 && errno == EINTR);
+ if (inlen < 0) {
+ if (debugging)
+ fprintf(stderr,
+ "handle_reply: recvfrom returned %d, errno %d\n", inlen, errno);
+ goto done;
+ }
+
+ reply_msg.acpted_rply.ar_verf = _null_auth;
+ reply_msg.acpted_rply.ar_results.where = 0;
+ reply_msg.acpted_rply.ar_results.proc = (xdrproc_t) xdr_void;
+
+ xdrmem_create(&reply_xdrs, buffer, (u_int)inlen, XDR_DECODE);
+ if (!xdr_replymsg(&reply_xdrs, &reply_msg)) {
+ if (debugging)
+ (void) fprintf(stderr,
+ "handle_reply: xdr_replymsg failed\n");
+ goto done;
+ }
+ fi = forward_find(reply_msg.rm_xid);
+#ifdef SVC_RUN_DEBUG
+ if (debugging) {
+ fprintf(stderr, "handle_reply: reply xid: %d fi addr: %p\n",
+ reply_msg.rm_xid, fi);
+ }
+#endif
+ if (fi == NULL) {
+ goto done;
+ }
+ _seterr_reply(&reply_msg, &reply_error);
+ if (reply_error.re_status != RPC_SUCCESS) {
+ if (debugging)
+ (void) fprintf(stderr, "handle_reply: %s\n",
+ clnt_sperrno(reply_error.re_status));
+ send_svcsyserr(xprt, fi);
+ goto done;
+ }
+ pos = XDR_GETPOS(&reply_xdrs);
+ len = inlen - pos;
+ a.rmt_args.args = &buffer[pos];
+ a.rmt_args.arglen = len;
+ a.rmt_uaddr = fi->uaddr;
+ a.rmt_localvers = fi->versnum;
+
+ xprt_set_caller(xprt, fi);
+#ifdef SVC_RUN_DEBUG
+ uaddr = taddr2uaddr(rpcbind_get_conf("udp"),
+ svc_getrpccaller(xprt));
+ if (debugging) {
+ fprintf(stderr, "handle_reply: forwarding address %s to %s\n",
+ a.rmt_uaddr, uaddr ? uaddr : "unknown");
+ }
+ if (uaddr)
+ free(uaddr);
+#endif
+ svc_sendreply(xprt, (xdrproc_t) xdr_rmtcall_result, (char *) &a);
+done:
+ if (buffer)
+ free(buffer);
+
+ if (reply_msg.rm_xid == 0) {
+#ifdef SVC_RUN_DEBUG
+ if (debugging) {
+ fprintf(stderr, "handle_reply: NULL xid on exit!\n");
+ }
+#endif
+ } else
+ (void) free_slot_by_xid(reply_msg.rm_xid);
+ return;
+}
+
+static void
+find_versions(rpcprog_t prog, char *netid, rpcvers_t *lowvp, rpcvers_t *highvp)
+{
+ register rpcblist_ptr rbl;
+ unsigned int lowv = 0;
+ unsigned int highv = 0;
+
+ for (rbl = list_rbl; rbl != NULL; rbl = rbl->rpcb_next) {
+ if ((rbl->rpcb_map.r_prog != prog) ||
+ ((rbl->rpcb_map.r_netid != NULL) &&
+ (strcasecmp(rbl->rpcb_map.r_netid, netid) != 0)))
+ continue;
+ if (lowv == 0) {
+ highv = rbl->rpcb_map.r_vers;
+ lowv = highv;
+ } else if (rbl->rpcb_map.r_vers < lowv) {
+ lowv = rbl->rpcb_map.r_vers;
+ } else if (rbl->rpcb_map.r_vers > highv) {
+ highv = rbl->rpcb_map.r_vers;
+ }
+ }
+ *lowvp = lowv;
+ *highvp = highv;
+ return;
+}
+
+/*
+ * returns the item with the given program, version number and netid.
+ * If that version number is not found, it returns the item with that
+ * program number, so that address is now returned to the caller. The
+ * caller when makes a call to this program, version number, the call
+ * will fail and it will return with PROGVERS_MISMATCH. The user can
+ * then determine the highest and the lowest version number for this
+ * program using clnt_geterr() and use those program version numbers.
+ *
+ * Returns the RPCBLIST for the given prog, vers and netid
+ */
+static rpcblist_ptr
+find_service(rpcprog_t prog, rpcvers_t vers, char *netid)
+{
+ register rpcblist_ptr hit = NULL;
+ register rpcblist_ptr rbl;
+
+ for (rbl = list_rbl; rbl != NULL; rbl = rbl->rpcb_next) {
+ if ((rbl->rpcb_map.r_prog != prog) ||
+ ((rbl->rpcb_map.r_netid != NULL) &&
+ (strcasecmp(rbl->rpcb_map.r_netid, netid) != 0)))
+ continue;
+ hit = rbl;
+ if (rbl->rpcb_map.r_vers == vers)
+ break;
+ }
+ return (hit);
+}
+
+/*
+ * Copies the name associated with the uid of the caller and returns
+ * a pointer to it. Similar to getwd().
+ */
+static char *
+getowner(SVCXPRT *transp, char *owner, size_t ownersize)
+{
+ uid_t uid;
+
+ if (__rpc_get_local_uid(transp, &uid) < 0)
+ strlcpy(owner, "unknown", ownersize);
+ else if (uid == 0)
+ strlcpy(owner, "superuser", ownersize);
+ else
+ snprintf(owner, ownersize, "%d", uid);
+
+ return owner;
+}
+
+#ifdef PORTMAP
+/*
+ * Add this to the pmap list only if it is UDP or TCP.
+ */
+static int
+add_pmaplist(RPCB *arg)
+{
+ struct pmap pmap;
+ struct pmaplist *pml;
+ int h1, h2, h3, h4, p1, p2;
+
+ if (strcmp(arg->r_netid, udptrans) == 0) {
+ /* It is UDP! */
+ pmap.pm_prot = IPPROTO_UDP;
+ } else if (strcmp(arg->r_netid, tcptrans) == 0) {
+ /* It is TCP */
+ pmap.pm_prot = IPPROTO_TCP;
+ } else
+ /* Not an IP protocol */
+ return (0);
+
+ /* interpret the universal address for TCP/IP */
+ if (sscanf(arg->r_addr, "%d.%d.%d.%d.%d.%d",
+ &h1, &h2, &h3, &h4, &p1, &p2) != 6)
+ return (0);
+ pmap.pm_port = ((p1 & 0xff) << 8) + (p2 & 0xff);
+ pmap.pm_prog = arg->r_prog;
+ pmap.pm_vers = arg->r_vers;
+ /*
+ * add to END of list
+ */
+ pml = malloc(sizeof (struct pmaplist));
+ if (pml == NULL) {
+ (void) syslog(LOG_ERR, "rpcbind: no memory!\n");
+ return (1);
+ }
+ pml->pml_map = pmap;
+ pml->pml_next = NULL;
+ if (list_pml == NULL) {
+ list_pml = pml;
+ } else {
+ struct pmaplist *fnd;
+
+ /* Attach to the end of the list */
+ for (fnd = list_pml; fnd->pml_next; fnd = fnd->pml_next)
+ ;
+ fnd->pml_next = pml;
+ }
+ return (0);
+}
+
+/*
+ * Delete this from the pmap list only if it is UDP or TCP.
+ */
+static int
+del_pmaplist(RPCB *arg)
+{
+ struct pmaplist *pml;
+ struct pmaplist *prevpml, *fnd;
+ unsigned long prot;
+
+ if (strcmp(arg->r_netid, udptrans) == 0) {
+ /* It is UDP! */
+ prot = IPPROTO_UDP;
+ } else if (strcmp(arg->r_netid, tcptrans) == 0) {
+ /* It is TCP */
+ prot = IPPROTO_TCP;
+ } else if (arg->r_netid[0] == 0) {
+ prot = 0; /* Remove all occurrences */
+ } else {
+ /* Not an IP protocol */
+ return (0);
+ }
+ for (prevpml = NULL, pml = list_pml; pml; /* cstyle */) {
+ if ((pml->pml_map.pm_prog != arg->r_prog) ||
+ (pml->pml_map.pm_vers != arg->r_vers) ||
+ (prot && (pml->pml_map.pm_prot != prot))) {
+ /* both pml & prevpml move forwards */
+ prevpml = pml;
+ pml = pml->pml_next;
+ continue;
+ }
+ /* found it; pml moves forward, prevpml stays */
+ fnd = pml;
+ pml = pml->pml_next;
+ if (prevpml == NULL)
+ list_pml = pml;
+ else
+ prevpml->pml_next = pml;
+ free(fnd);
+ }
+ return (0);
+}
+#endif /* PORTMAP */
diff --git a/usr.sbin/rpcbind/rpcbind.8 b/usr.sbin/rpcbind/rpcbind.8
new file mode 100644
index 0000000..0ecf895
--- /dev/null
+++ b/usr.sbin/rpcbind/rpcbind.8
@@ -0,0 +1,148 @@
+.\" @(#)rpcbind.1m 1.19 92/09/14 SMI; from SVr4
+.\" Copyright 1989 AT&T
+.\" Copyright 1991 Sun Microsystems, Inc.
+.\" $FreeBSD$
+.Dd April 23, 2007
+.Dt RPCBIND 8
+.Os
+.Sh NAME
+.Nm rpcbind
+.Nd universal addresses to RPC program number mapper
+.Sh SYNOPSIS
+.Nm
+.Op Fl 6adiLls
+.Op Fl h Ar bindip
+.Sh DESCRIPTION
+The
+.Nm
+utility is a server that converts
+.Tn RPC
+program numbers into
+universal addresses.
+It must be running on the host to be able to make
+.Tn RPC
+calls
+on a server on that machine.
+.Pp
+When an
+.Tn RPC
+service is started,
+it tells
+.Nm
+the address at which it is listening,
+and the
+.Tn RPC
+program numbers it is prepared to serve.
+When a client wishes to make an
+.Tn RPC
+call to a given program number,
+it first contacts
+.Nm
+on the server machine to determine
+the address where
+.Tn RPC
+requests should be sent.
+.Pp
+The
+.Nm
+utility should be started before any other RPC service.
+Normally, standard
+.Tn RPC
+servers are started by port monitors, so
+.Nm
+must be started before port monitors are invoked.
+.Pp
+When
+.Nm
+is started, it checks that certain name-to-address
+translation-calls function correctly.
+If they fail, the network configuration databases may be corrupt.
+Since
+.Tn RPC
+services cannot function correctly in this situation,
+.Nm
+reports the condition and terminates.
+.Pp
+The
+.Nm
+utility can only be started by the super-user.
+.Sh OPTIONS
+.Bl -tag -width indent
+.It Fl 6
+Bind to AF_INET6 (IPv6) addresses only.
+.It Fl a
+When debugging
+.Pq Fl d ,
+do an abort on errors.
+.It Fl d
+Run in debug mode.
+In this mode,
+.Nm
+will not fork when it starts, will print additional information
+during operation, and will abort on certain errors if
+.Fl a
+is also specified.
+With this option, the name-to-address translation consistency
+checks are shown in detail.
+.It Fl h Ar bindip
+Specify specific IP addresses to bind to for TCP and UDP requests.
+This option
+may be specified multiple times and is typically necessary when running
+on a multi-homed host.
+If no
+.Fl h
+option is specified,
+.Nm
+will bind to
+.Dv INADDR_ANY ,
+which could lead to problems on a multi-homed host due to
+.Nm
+returning a UDP packet from a different IP address than it was
+sent to.
+Note that when specifying IP addresses with
+.Fl h ,
+.Nm
+will automatically add
+.Li 127.0.0.1
+and if IPv6 is enabled,
+.Li ::1
+to the list.
+.It Fl i
+.Dq Insecure
+mode.
+Allow calls to SET and UNSET from any host.
+Normally
+.Nm
+accepts these requests only from the loopback interface for security reasons.
+This change is necessary for programs that were compiled with earlier
+versions of the rpc library and do not make those requests using the
+loopback interface.
+.It Fl L
+Allow old-style local connections over the loopback interface.
+Without this flag, local connections are only allowed over a local socket,
+.Pa /var/run/rpcbind.sock .
+.It Fl l
+Turn on libwrap connection logging.
+.It Fl s
+Cause
+.Nm
+to change to the user daemon as soon as possible.
+This causes
+.Nm
+to use non-privileged ports for outgoing connections, preventing non-privileged
+clients from using
+.Nm
+to connect to services from a privileged port.
+.El
+.Sh NOTES
+All RPC servers must be restarted if
+.Nm
+is restarted.
+.Sh FILES
+.Bl -tag -width /var/run/rpcbind.sock -compact
+.It Pa /var/run/rpcbind.sock
+.El
+.Sh SEE ALSO
+.Xr rpcbind 3 ,
+.Xr netconfig 5 ,
+.Xr rpcinfo 8
diff --git a/usr.sbin/rpcbind/rpcbind.c b/usr.sbin/rpcbind/rpcbind.c
new file mode 100644
index 0000000..b601da5
--- /dev/null
+++ b/usr.sbin/rpcbind/rpcbind.c
@@ -0,0 +1,783 @@
+/* $NetBSD: rpcbind.c,v 1.3 2002/11/08 00:16:40 fvdl Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/*
+ * Copyright (c) 1984 - 1991 by Sun Microsystems, Inc.
+ */
+
+/* #ident "@(#)rpcbind.c 1.19 94/04/25 SMI" */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)rpcbind.c 1.35 89/04/21 Copyr 1984 Sun Micro";
+#endif
+#endif
+
+/*
+ * rpcbind.c
+ * Implements the program, version to address mapping for rpc.
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/wait.h>
+#include <sys/signal.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <rpc/rpc.h>
+#include <rpc/rpc_com.h>
+#ifdef PORTMAP
+#include <netinet/in.h>
+#endif
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <netconfig.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <err.h>
+#include <libutil.h>
+#include <pwd.h>
+#include <string.h>
+#include <errno.h>
+#include "rpcbind.h"
+
+/* Global variables */
+int debugging = 0; /* Tell me what's going on */
+int doabort = 0; /* When debugging, do an abort on errors */
+rpcblist_ptr list_rbl; /* A list of version 3/4 rpcbind services */
+
+/* who to suid to if -s is given */
+#define RUN_AS "daemon"
+
+#define RPCBINDDLOCK "/var/run/rpcbind.lock"
+
+int runasdaemon = 0;
+int insecure = 0;
+int oldstyle_local = 0;
+int verboselog = 0;
+
+char **hosts = NULL;
+int ipv6_only = 0;
+int nhosts = 0;
+int on = 1;
+int rpcbindlockfd;
+
+#ifdef WARMSTART
+/* Local Variable */
+static int warmstart = 0; /* Grab an old copy of registrations. */
+#endif
+
+#ifdef PORTMAP
+struct pmaplist *list_pml; /* A list of version 2 rpcbind services */
+char *udptrans; /* Name of UDP transport */
+char *tcptrans; /* Name of TCP transport */
+char *udp_uaddr; /* Universal UDP address */
+char *tcp_uaddr; /* Universal TCP address */
+#endif
+static char servname[] = "rpcbind";
+static char superuser[] = "superuser";
+
+int main(int, char *[]);
+
+static int init_transport(struct netconfig *);
+static void rbllist_add(rpcprog_t, rpcvers_t, struct netconfig *,
+ struct netbuf *);
+static void terminate(int);
+static void parseargs(int, char *[]);
+
+int
+main(int argc, char *argv[])
+{
+ struct netconfig *nconf;
+ void *nc_handle; /* Net config handle */
+ struct rlimit rl;
+ int maxrec = RPC_MAXDATASIZE;
+
+ parseargs(argc, argv);
+
+ /* Check that another rpcbind isn't already running. */
+ if ((rpcbindlockfd = (open(RPCBINDDLOCK,
+ O_RDONLY|O_CREAT, 0444))) == -1)
+ err(1, "%s", RPCBINDDLOCK);
+
+ if(flock(rpcbindlockfd, LOCK_EX|LOCK_NB) == -1 && errno == EWOULDBLOCK)
+ errx(1, "another rpcbind is already running. Aborting");
+
+ getrlimit(RLIMIT_NOFILE, &rl);
+ if (rl.rlim_cur < 128) {
+ if (rl.rlim_max <= 128)
+ rl.rlim_cur = rl.rlim_max;
+ else
+ rl.rlim_cur = 128;
+ setrlimit(RLIMIT_NOFILE, &rl);
+ }
+ openlog("rpcbind", LOG_CONS, LOG_DAEMON);
+ if (geteuid()) { /* This command allowed only to root */
+ fprintf(stderr, "Sorry. You are not superuser\n");
+ exit(1);
+ }
+ nc_handle = setnetconfig(); /* open netconfig file */
+ if (nc_handle == NULL) {
+ syslog(LOG_ERR, "could not read /etc/netconfig");
+ exit(1);
+ }
+#ifdef PORTMAP
+ udptrans = "";
+ tcptrans = "";
+#endif
+
+ nconf = getnetconfigent("local");
+ if (nconf == NULL)
+ nconf = getnetconfigent("unix");
+ if (nconf == NULL) {
+ syslog(LOG_ERR, "%s: can't find local transport\n", argv[0]);
+ exit(1);
+ }
+
+ rpc_control(RPC_SVC_CONNMAXREC_SET, &maxrec);
+
+ init_transport(nconf);
+
+ while ((nconf = getnetconfig(nc_handle))) {
+ if (nconf->nc_flag & NC_VISIBLE)
+ if (ipv6_only == 1 && strcmp(nconf->nc_protofmly,
+ "inet") == 0) {
+ /* DO NOTHING */
+ } else
+ init_transport(nconf);
+ }
+ endnetconfig(nc_handle);
+
+ /* catch the usual termination signals for graceful exit */
+ (void) signal(SIGCHLD, reap);
+ (void) signal(SIGINT, terminate);
+ (void) signal(SIGTERM, terminate);
+ (void) signal(SIGQUIT, terminate);
+ /* ignore others that could get sent */
+ (void) signal(SIGPIPE, SIG_IGN);
+ (void) signal(SIGHUP, SIG_IGN);
+ (void) signal(SIGUSR1, SIG_IGN);
+ (void) signal(SIGUSR2, SIG_IGN);
+#ifdef WARMSTART
+ if (warmstart) {
+ read_warmstart();
+ }
+#endif
+ if (debugging) {
+ printf("rpcbind debugging enabled.");
+ if (doabort) {
+ printf(" Will abort on errors!\n");
+ } else {
+ printf("\n");
+ }
+ } else {
+ if (daemon(0, 0))
+ err(1, "fork failed");
+ }
+
+ if (runasdaemon) {
+ struct passwd *p;
+
+ if((p = getpwnam(RUN_AS)) == NULL) {
+ syslog(LOG_ERR, "cannot get uid of daemon: %m");
+ exit(1);
+ }
+ if (setuid(p->pw_uid) == -1) {
+ syslog(LOG_ERR, "setuid to daemon failed: %m");
+ exit(1);
+ }
+ }
+
+ network_init();
+
+ my_svc_run();
+ syslog(LOG_ERR, "svc_run returned unexpectedly");
+ rpcbind_abort();
+ /* NOTREACHED */
+
+ return 0;
+}
+
+/*
+ * Adds the entry into the rpcbind database.
+ * If PORTMAP, then for UDP and TCP, it adds the entries for version 2 also
+ * Returns 0 if succeeds, else fails
+ */
+static int
+init_transport(struct netconfig *nconf)
+{
+ int fd;
+ struct t_bind taddr;
+ struct addrinfo hints, *res = NULL;
+ struct __rpc_sockinfo si;
+ SVCXPRT *my_xprt;
+ int status; /* bound checking ? */
+ int aicode;
+ int addrlen;
+ int nhostsbak;
+ int bound;
+ struct sockaddr *sa;
+ u_int32_t host_addr[4]; /* IPv4 or IPv6 */
+ struct sockaddr_un sun;
+ mode_t oldmask;
+
+ if ((nconf->nc_semantics != NC_TPI_CLTS) &&
+ (nconf->nc_semantics != NC_TPI_COTS) &&
+ (nconf->nc_semantics != NC_TPI_COTS_ORD))
+ return (1); /* not my type */
+#ifdef ND_DEBUG
+ if (debugging) {
+ int i;
+ char **s;
+
+ (void)fprintf(stderr, "%s: %ld lookup routines :\n",
+ nconf->nc_netid, nconf->nc_nlookups);
+ for (i = 0, s = nconf->nc_lookups; i < nconf->nc_nlookups;
+ i++, s++)
+ fprintf(stderr, "[%d] - %s\n", i, *s);
+ }
+#endif
+
+ /*
+ * XXX - using RPC library internal functions.
+ */
+ if ((strcmp(nconf->nc_netid, "local") == 0) ||
+ (strcmp(nconf->nc_netid, "unix") == 0)) {
+ /*
+ * For other transports we call this later, for each socket we
+ * like to bind.
+ */
+ if ((fd = __rpc_nconf2fd(nconf)) < 0) {
+ int non_fatal = 0;
+ if (errno == EPROTONOSUPPORT)
+ non_fatal = 1;
+ syslog(non_fatal?LOG_DEBUG:LOG_ERR, "cannot create socket for %s",
+ nconf->nc_netid);
+ return (1);
+ }
+ }
+
+ if (!__rpc_nconf2sockinfo(nconf, &si)) {
+ syslog(LOG_ERR, "cannot get information for %s",
+ nconf->nc_netid);
+ return (1);
+ }
+
+ if ((strcmp(nconf->nc_netid, "local") == 0) ||
+ (strcmp(nconf->nc_netid, "unix") == 0)) {
+ memset(&sun, 0, sizeof sun);
+ sun.sun_family = AF_LOCAL;
+ unlink(_PATH_RPCBINDSOCK);
+ strcpy(sun.sun_path, _PATH_RPCBINDSOCK);
+ sun.sun_len = SUN_LEN(&sun);
+ addrlen = sizeof (struct sockaddr_un);
+ sa = (struct sockaddr *)&sun;
+ } else {
+ /* Get rpcbind's address on this transport */
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = si.si_af;
+ hints.ai_socktype = si.si_socktype;
+ hints.ai_protocol = si.si_proto;
+ }
+
+ if ((strcmp(nconf->nc_netid, "local") != 0) &&
+ (strcmp(nconf->nc_netid, "unix") != 0)) {
+ /*
+ * If no hosts were specified, just bind to INADDR_ANY.
+ * Otherwise make sure 127.0.0.1 is added to the list.
+ */
+ nhostsbak = nhosts;
+ nhostsbak++;
+ hosts = realloc(hosts, nhostsbak * sizeof(char *));
+ if (nhostsbak == 1)
+ hosts[0] = "*";
+ else {
+ if (hints.ai_family == AF_INET) {
+ hosts[nhostsbak - 1] = "127.0.0.1";
+ } else if (hints.ai_family == AF_INET6) {
+ hosts[nhostsbak - 1] = "::1";
+ } else
+ return 1;
+ }
+
+ /*
+ * Bind to specific IPs if asked to
+ */
+ bound = 0;
+ while (nhostsbak > 0) {
+ --nhostsbak;
+ /*
+ * XXX - using RPC library internal functions.
+ */
+ if ((fd = __rpc_nconf2fd(nconf)) < 0) {
+ int non_fatal = 0;
+ if (errno == EPROTONOSUPPORT &&
+ nconf->nc_semantics != NC_TPI_CLTS)
+ non_fatal = 1;
+ syslog(non_fatal ? LOG_DEBUG : LOG_ERR,
+ "cannot create socket for %s", nconf->nc_netid);
+ return (1);
+ }
+ switch (hints.ai_family) {
+ case AF_INET:
+ if (inet_pton(AF_INET, hosts[nhostsbak],
+ host_addr) == 1) {
+ hints.ai_flags &= AI_NUMERICHOST;
+ } else {
+ /*
+ * Skip if we have an AF_INET6 adress.
+ */
+ if (inet_pton(AF_INET6,
+ hosts[nhostsbak], host_addr) == 1) {
+ close(fd);
+ continue;
+ }
+ }
+ break;
+ case AF_INET6:
+ if (inet_pton(AF_INET6, hosts[nhostsbak],
+ host_addr) == 1) {
+ hints.ai_flags &= AI_NUMERICHOST;
+ } else {
+ /*
+ * Skip if we have an AF_INET adress.
+ */
+ if (inet_pton(AF_INET, hosts[nhostsbak],
+ host_addr) == 1) {
+ close(fd);
+ continue;
+ }
+ }
+ if (setsockopt(fd, IPPROTO_IPV6,
+ IPV6_V6ONLY, &on, sizeof on) < 0) {
+ syslog(LOG_ERR,
+ "can't set v6-only binding for "
+ "ipv6 socket: %m");
+ continue;
+ }
+ break;
+ default:
+ break;
+ }
+
+ /*
+ * If no hosts were specified, just bind to INADDR_ANY
+ */
+ if (strcmp("*", hosts[nhostsbak]) == 0)
+ hosts[nhostsbak] = NULL;
+ if ((strcmp(nconf->nc_netid, "local") != 0) &&
+ (strcmp(nconf->nc_netid, "unix") != 0)) {
+ if ((aicode = getaddrinfo(hosts[nhostsbak],
+ servname, &hints, &res)) != 0) {
+ syslog(LOG_ERR,
+ "cannot get local address for %s: %s",
+ nconf->nc_netid, gai_strerror(aicode));
+ continue;
+ }
+ addrlen = res->ai_addrlen;
+ sa = (struct sockaddr *)res->ai_addr;
+ }
+ oldmask = umask(S_IXUSR|S_IXGRP|S_IXOTH);
+ if (bind(fd, sa, addrlen) != 0) {
+ syslog(LOG_ERR, "cannot bind %s on %s: %m",
+ (hosts[nhostsbak] == NULL) ? "*" :
+ hosts[nhostsbak], nconf->nc_netid);
+ if (res != NULL)
+ freeaddrinfo(res);
+ continue;
+ } else
+ bound = 1;
+ (void)umask(oldmask);
+
+ /* Copy the address */
+ taddr.addr.len = taddr.addr.maxlen = addrlen;
+ taddr.addr.buf = malloc(addrlen);
+ if (taddr.addr.buf == NULL) {
+ syslog(LOG_ERR,
+ "cannot allocate memory for %s address",
+ nconf->nc_netid);
+ if (res != NULL)
+ freeaddrinfo(res);
+ return 1;
+ }
+ memcpy(taddr.addr.buf, sa, addrlen);
+#ifdef ND_DEBUG
+ if (debugging) {
+ /*
+ * for debugging print out our universal
+ * address
+ */
+ char *uaddr;
+ struct netbuf nb;
+
+ nb.buf = sa;
+ nb.len = nb.maxlen = sa->sa_len;
+ uaddr = taddr2uaddr(nconf, &nb);
+ (void)fprintf(stderr,
+ "rpcbind : my address is %s\n", uaddr);
+ (void)free(uaddr);
+ }
+#endif
+
+ if (nconf->nc_semantics != NC_TPI_CLTS)
+ listen(fd, SOMAXCONN);
+
+ my_xprt = (SVCXPRT *)svc_tli_create(fd, nconf, &taddr,
+ RPC_MAXDATASIZE, RPC_MAXDATASIZE);
+ if (my_xprt == (SVCXPRT *)NULL) {
+ syslog(LOG_ERR, "%s: could not create service",
+ nconf->nc_netid);
+ goto error;
+ }
+ }
+ if (!bound)
+ return 1;
+ } else {
+ oldmask = umask(S_IXUSR|S_IXGRP|S_IXOTH);
+ if (bind(fd, sa, addrlen) < 0) {
+ syslog(LOG_ERR, "cannot bind %s: %m", nconf->nc_netid);
+ if (res != NULL)
+ freeaddrinfo(res);
+ return 1;
+ }
+ (void) umask(oldmask);
+
+ /* Copy the address */
+ taddr.addr.len = taddr.addr.maxlen = addrlen;
+ taddr.addr.buf = malloc(addrlen);
+ if (taddr.addr.buf == NULL) {
+ syslog(LOG_ERR, "cannot allocate memory for %s address",
+ nconf->nc_netid);
+ if (res != NULL)
+ freeaddrinfo(res);
+ return 1;
+ }
+ memcpy(taddr.addr.buf, sa, addrlen);
+#ifdef ND_DEBUG
+ if (debugging) {
+ /* for debugging print out our universal address */
+ char *uaddr;
+ struct netbuf nb;
+
+ nb.buf = sa;
+ nb.len = nb.maxlen = sa->sa_len;
+ uaddr = taddr2uaddr(nconf, &nb);
+ (void) fprintf(stderr, "rpcbind : my address is %s\n",
+ uaddr);
+ (void) free(uaddr);
+ }
+#endif
+
+ if (nconf->nc_semantics != NC_TPI_CLTS)
+ listen(fd, SOMAXCONN);
+
+ my_xprt = (SVCXPRT *)svc_tli_create(fd, nconf, &taddr,
+ RPC_MAXDATASIZE, RPC_MAXDATASIZE);
+ if (my_xprt == (SVCXPRT *)NULL) {
+ syslog(LOG_ERR, "%s: could not create service",
+ nconf->nc_netid);
+ goto error;
+ }
+ }
+
+#ifdef PORTMAP
+ /*
+ * Register both the versions for tcp/ip, udp/ip and local.
+ */
+ if ((strcmp(nconf->nc_protofmly, NC_INET) == 0 &&
+ (strcmp(nconf->nc_proto, NC_TCP) == 0 ||
+ strcmp(nconf->nc_proto, NC_UDP) == 0)) ||
+ (strcmp(nconf->nc_netid, "unix") == 0) ||
+ (strcmp(nconf->nc_netid, "local") == 0)) {
+ struct pmaplist *pml;
+
+ if (!svc_register(my_xprt, PMAPPROG, PMAPVERS,
+ pmap_service, 0)) {
+ syslog(LOG_ERR, "could not register on %s",
+ nconf->nc_netid);
+ goto error;
+ }
+ pml = malloc(sizeof (struct pmaplist));
+ if (pml == NULL) {
+ syslog(LOG_ERR, "no memory!");
+ exit(1);
+ }
+ pml->pml_map.pm_prog = PMAPPROG;
+ pml->pml_map.pm_vers = PMAPVERS;
+ pml->pml_map.pm_port = PMAPPORT;
+ if (strcmp(nconf->nc_proto, NC_TCP) == 0) {
+ if (tcptrans[0]) {
+ syslog(LOG_ERR,
+ "cannot have more than one TCP transport");
+ goto error;
+ }
+ tcptrans = strdup(nconf->nc_netid);
+ pml->pml_map.pm_prot = IPPROTO_TCP;
+
+ /* Let's snarf the universal address */
+ /* "h1.h2.h3.h4.p1.p2" */
+ tcp_uaddr = taddr2uaddr(nconf, &taddr.addr);
+ } else if (strcmp(nconf->nc_proto, NC_UDP) == 0) {
+ if (udptrans[0]) {
+ syslog(LOG_ERR,
+ "cannot have more than one UDP transport");
+ goto error;
+ }
+ udptrans = strdup(nconf->nc_netid);
+ pml->pml_map.pm_prot = IPPROTO_UDP;
+
+ /* Let's snarf the universal address */
+ /* "h1.h2.h3.h4.p1.p2" */
+ udp_uaddr = taddr2uaddr(nconf, &taddr.addr);
+ } else if (strcmp(nconf->nc_netid, "local") == 0)
+ pml->pml_map.pm_prot = IPPROTO_ST;
+ else if (strcmp(nconf->nc_netid, "unix") == 0)
+ pml->pml_map.pm_prot = IPPROTO_ST;
+ pml->pml_next = list_pml;
+ list_pml = pml;
+
+ /* Add version 3 information */
+ pml = malloc(sizeof (struct pmaplist));
+ if (pml == NULL) {
+ syslog(LOG_ERR, "no memory!");
+ exit(1);
+ }
+ pml->pml_map = list_pml->pml_map;
+ pml->pml_map.pm_vers = RPCBVERS;
+ pml->pml_next = list_pml;
+ list_pml = pml;
+
+ /* Add version 4 information */
+ pml = malloc (sizeof (struct pmaplist));
+ if (pml == NULL) {
+ syslog(LOG_ERR, "no memory!");
+ exit(1);
+ }
+ pml->pml_map = list_pml->pml_map;
+ pml->pml_map.pm_vers = RPCBVERS4;
+ pml->pml_next = list_pml;
+ list_pml = pml;
+
+ /* Also add version 2 stuff to rpcbind list */
+ rbllist_add(PMAPPROG, PMAPVERS, nconf, &taddr.addr);
+ }
+#endif
+
+ /* version 3 registration */
+ if (!svc_reg(my_xprt, RPCBPROG, RPCBVERS, rpcb_service_3, NULL)) {
+ syslog(LOG_ERR, "could not register %s version 3",
+ nconf->nc_netid);
+ goto error;
+ }
+ rbllist_add(RPCBPROG, RPCBVERS, nconf, &taddr.addr);
+
+ /* version 4 registration */
+ if (!svc_reg(my_xprt, RPCBPROG, RPCBVERS4, rpcb_service_4, NULL)) {
+ syslog(LOG_ERR, "could not register %s version 4",
+ nconf->nc_netid);
+ goto error;
+ }
+ rbllist_add(RPCBPROG, RPCBVERS4, nconf, &taddr.addr);
+
+ /* decide if bound checking works for this transport */
+ status = add_bndlist(nconf, &taddr.addr);
+#ifdef BIND_DEBUG
+ if (debugging) {
+ if (status < 0) {
+ fprintf(stderr, "Error in finding bind status for %s\n",
+ nconf->nc_netid);
+ } else if (status == 0) {
+ fprintf(stderr, "check binding for %s\n",
+ nconf->nc_netid);
+ } else if (status > 0) {
+ fprintf(stderr, "No check binding for %s\n",
+ nconf->nc_netid);
+ }
+ }
+#endif
+ /*
+ * rmtcall only supported on CLTS transports for now.
+ */
+ if (nconf->nc_semantics == NC_TPI_CLTS) {
+ status = create_rmtcall_fd(nconf);
+
+#ifdef BIND_DEBUG
+ if (debugging) {
+ if (status < 0) {
+ fprintf(stderr,
+ "Could not create rmtcall fd for %s\n",
+ nconf->nc_netid);
+ } else {
+ fprintf(stderr, "rmtcall fd for %s is %d\n",
+ nconf->nc_netid, status);
+ }
+ }
+#endif
+ }
+ return (0);
+error:
+ close(fd);
+ return (1);
+}
+
+static void
+rbllist_add(rpcprog_t prog, rpcvers_t vers, struct netconfig *nconf,
+ struct netbuf *addr)
+{
+ rpcblist_ptr rbl;
+
+ rbl = malloc(sizeof (rpcblist));
+ if (rbl == NULL) {
+ syslog(LOG_ERR, "no memory!");
+ exit(1);
+ }
+
+ rbl->rpcb_map.r_prog = prog;
+ rbl->rpcb_map.r_vers = vers;
+ rbl->rpcb_map.r_netid = strdup(nconf->nc_netid);
+ rbl->rpcb_map.r_addr = taddr2uaddr(nconf, addr);
+ rbl->rpcb_map.r_owner = strdup(superuser);
+ rbl->rpcb_next = list_rbl; /* Attach to global list */
+ list_rbl = rbl;
+}
+
+/*
+ * Catch the signal and die
+ */
+static void
+terminate(int dummy __unused)
+{
+ close(rpcbindlockfd);
+#ifdef WARMSTART
+ syslog(LOG_ERR,
+ "rpcbind terminating on signal. Restart with \"rpcbind -w\"");
+ write_warmstart(); /* Dump yourself */
+#endif
+ exit(2);
+}
+
+void
+rpcbind_abort()
+{
+#ifdef WARMSTART
+ write_warmstart(); /* Dump yourself */
+#endif
+ abort();
+}
+
+/* get command line options */
+static void
+parseargs(int argc, char *argv[])
+{
+ int c;
+
+#ifdef WARMSTART
+#define WSOP "w"
+#else
+#define WSOP ""
+#endif
+ while ((c = getopt(argc, argv, "6adh:iLls" WSOP)) != -1) {
+ switch (c) {
+ case '6':
+ ipv6_only = 1;
+ break;
+ case 'a':
+ doabort = 1; /* when debugging, do an abort on */
+ break; /* errors; for rpcbind developers */
+ /* only! */
+ case 'd':
+ debugging = 1;
+ break;
+ case 'h':
+ ++nhosts;
+ hosts = realloc(hosts, nhosts * sizeof(char *));
+ if (hosts == NULL)
+ errx(1, "Out of memory");
+ hosts[nhosts - 1] = strdup(optarg);
+ if (hosts[nhosts - 1] == NULL)
+ errx(1, "Out of memory");
+ break;
+ case 'i':
+ insecure = 1;
+ break;
+ case 'L':
+ oldstyle_local = 1;
+ break;
+ case 'l':
+ verboselog = 1;
+ break;
+ case 's':
+ runasdaemon = 1;
+ break;
+#ifdef WARMSTART
+ case 'w':
+ warmstart = 1;
+ break;
+#endif
+ default: /* error */
+ fprintf(stderr,
+ "usage: rpcbind [-6adiLls%s] [-h bindip]\n",
+ WSOP);
+ exit (1);
+ }
+ }
+ if (doabort && !debugging) {
+ fprintf(stderr,
+ "-a (abort) specified without -d (debugging) -- ignored.\n");
+ doabort = 0;
+ }
+#undef WSOP
+}
+
+void
+reap(int dummy __unused)
+{
+ int save_errno = errno;
+
+ while (wait3(NULL, WNOHANG, NULL) > 0)
+ ;
+ errno = save_errno;
+}
+
+void
+toggle_verboselog(int dummy __unused)
+{
+ verboselog = !verboselog;
+}
diff --git a/usr.sbin/rpcbind/rpcbind.h b/usr.sbin/rpcbind/rpcbind.h
new file mode 100644
index 0000000..5537ce4
--- /dev/null
+++ b/usr.sbin/rpcbind/rpcbind.h
@@ -0,0 +1,144 @@
+/* $NetBSD: rpcbind.h,v 1.1 2000/06/03 00:47:21 fvdl Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/*
+ * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc.
+ */
+
+/* #ident "@(#)rpcbind.h 1.4 90/04/12 SMI" */
+
+/*
+ * rpcbind.h
+ * The common header declarations
+ */
+
+#ifndef rpcbind_h
+#define rpcbind_h
+
+#ifdef PORTMAP
+#include <rpc/pmap_prot.h>
+#endif
+#include <rpc/rpcb_prot.h>
+
+/*
+ * Stuff for the rmtcall service
+ */
+struct encap_parms {
+ u_int32_t arglen;
+ char *args;
+};
+
+struct r_rmtcall_args {
+ u_int32_t rmt_prog;
+ u_int32_t rmt_vers;
+ u_int32_t rmt_proc;
+ int rmt_localvers; /* whether to send port # or uaddr */
+ char *rmt_uaddr;
+ struct encap_parms rmt_args;
+};
+
+extern int debugging;
+extern int doabort;
+extern int verboselog;
+extern int insecure;
+extern int oldstyle_local;
+extern rpcblist_ptr list_rbl; /* A list of version 3 & 4 rpcbind services */
+
+#ifdef PORTMAP
+extern struct pmaplist *list_pml; /* A list of version 2 rpcbind services */
+extern char *udptrans; /* Name of UDP transport */
+extern char *tcptrans; /* Name of TCP transport */
+extern char *udp_uaddr; /* Universal UDP address */
+extern char *tcp_uaddr; /* Universal TCP address */
+#endif
+
+int add_bndlist(struct netconfig *, struct netbuf *);
+bool_t is_bound(char *, char *);
+char *mergeaddr(SVCXPRT *, char *, char *, char *);
+struct netconfig *rpcbind_get_conf(char *);
+
+void rpcbs_init(void);
+void rpcbs_procinfo(rpcvers_t, rpcproc_t);
+void rpcbs_set(rpcvers_t, bool_t);
+void rpcbs_unset(rpcvers_t, bool_t);
+void rpcbs_getaddr(rpcvers_t, rpcprog_t, rpcvers_t, char *, char *);
+void rpcbs_rmtcall(rpcvers_t, rpcproc_t, rpcprog_t, rpcvers_t, rpcproc_t,
+ char *, rpcblist_ptr);
+void *rpcbproc_getstat(void *, struct svc_req *, SVCXPRT *, rpcvers_t);
+
+void rpcb_service_3(struct svc_req *, SVCXPRT *);
+void rpcb_service_4(struct svc_req *, SVCXPRT *);
+
+/* Common functions shared between versions */
+void *rpcbproc_set_com(void *, struct svc_req *, SVCXPRT *, rpcvers_t);
+void *rpcbproc_unset_com(void *, struct svc_req *, SVCXPRT *, rpcvers_t);
+bool_t map_set(RPCB *, char *);
+bool_t map_unset(RPCB *, char *);
+void delete_prog(unsigned int);
+void *rpcbproc_getaddr_com(RPCB *, struct svc_req *, SVCXPRT *, rpcvers_t,
+ rpcvers_t);
+void *rpcbproc_gettime_com(void *, struct svc_req *, SVCXPRT *,
+ rpcvers_t);
+void *rpcbproc_uaddr2taddr_com(void *, struct svc_req *,
+ SVCXPRT *, rpcvers_t);
+void *rpcbproc_taddr2uaddr_com(void *, struct svc_req *, SVCXPRT *,
+ rpcvers_t);
+int create_rmtcall_fd(struct netconfig *);
+void rpcbproc_callit_com(struct svc_req *, SVCXPRT *, rpcvers_t,
+ rpcvers_t);
+void my_svc_run(void);
+
+void rpcbind_abort(void);
+void reap(int);
+void toggle_verboselog(int);
+
+int check_access(SVCXPRT *, rpcproc_t, void *, unsigned int);
+int check_callit(SVCXPRT *, struct r_rmtcall_args *, int);
+void logit(int, struct sockaddr *, rpcproc_t, rpcprog_t, const char *);
+int is_loopback(struct netbuf *);
+
+#ifdef PORTMAP
+extern void pmap_service(struct svc_req *, SVCXPRT *);
+#endif
+
+void write_warmstart(void);
+void read_warmstart(void);
+
+char *addrmerge(struct netbuf *caller, char *serv_uaddr, char *clnt_uaddr,
+ char *netid);
+void network_init(void);
+struct sockaddr *local_sa(int);
+
+/* For different getaddr semantics */
+#define RPCB_ALLVERS 0
+#define RPCB_ONEVERS 1
+
+#endif /* rpcbind_h */
diff --git a/usr.sbin/rpcbind/security.c b/usr.sbin/rpcbind/security.c
new file mode 100644
index 0000000..8657247
--- /dev/null
+++ b/usr.sbin/rpcbind/security.c
@@ -0,0 +1,288 @@
+/* $NetBSD: security.c,v 1.5 2000/06/08 09:01:05 fvdl Exp $ */
+/* $FreeBSD$ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <rpc/rpc.h>
+#include <rpc/rpcb_prot.h>
+#include <rpc/pmap_prot.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <libutil.h>
+#include <syslog.h>
+#include <netdb.h>
+
+/*
+ * XXX for special case checks in check_callit.
+ */
+#include <rpcsvc/mount.h>
+#include <rpcsvc/rquota.h>
+#include <rpcsvc/nfs_prot.h>
+#include <rpcsvc/yp.h>
+#include <rpcsvc/ypclnt.h>
+#include <rpcsvc/yppasswd.h>
+
+#include "rpcbind.h"
+
+#ifdef LIBWRAP
+# include <tcpd.h>
+#ifndef LIBWRAP_ALLOW_FACILITY
+# define LIBWRAP_ALLOW_FACILITY LOG_AUTH
+#endif
+#ifndef LIBWRAP_ALLOW_SEVERITY
+# define LIBWRAP_ALLOW_SEVERITY LOG_INFO
+#endif
+#ifndef LIBWRAP_DENY_FACILITY
+# define LIBWRAP_DENY_FACILITY LOG_AUTH
+#endif
+#ifndef LIBWRAP_DENY_SEVERITY
+# define LIBWRAP_DENY_SEVERITY LOG_WARNING
+#endif
+int allow_severity = LIBWRAP_ALLOW_FACILITY|LIBWRAP_ALLOW_SEVERITY;
+int deny_severity = LIBWRAP_DENY_FACILITY|LIBWRAP_DENY_SEVERITY;
+#endif
+
+#ifndef PORTMAP_LOG_FACILITY
+# define PORTMAP_LOG_FACILITY LOG_AUTH
+#endif
+#ifndef PORTMAP_LOG_SEVERITY
+# define PORTMAP_LOG_SEVERITY LOG_INFO
+#endif
+int log_severity = PORTMAP_LOG_FACILITY|PORTMAP_LOG_SEVERITY;
+
+extern int verboselog;
+
+int
+check_access(SVCXPRT *xprt, rpcproc_t proc, void *args, unsigned int rpcbvers)
+{
+ struct netbuf *caller = svc_getrpccaller(xprt);
+ struct sockaddr *addr = (struct sockaddr *)caller->buf;
+#ifdef LIBWRAP
+ struct request_info req;
+#endif
+ rpcprog_t prog = 0;
+ rpcb *rpcbp;
+ struct pmap *pmap;
+
+ /*
+ * The older PMAP_* equivalents have the same numbers, so
+ * they are accounted for here as well.
+ */
+ switch (proc) {
+ case RPCBPROC_GETADDR:
+ case RPCBPROC_SET:
+ case RPCBPROC_UNSET:
+ if (rpcbvers > PMAPVERS) {
+ rpcbp = (rpcb *)args;
+ prog = rpcbp->r_prog;
+ } else {
+ pmap = (struct pmap *)args;
+ prog = pmap->pm_prog;
+ }
+ if (proc == RPCBPROC_GETADDR)
+ break;
+ if (!insecure && !is_loopback(caller)) {
+ if (verboselog)
+ logit(log_severity, addr, proc, prog,
+ " declined (non-loopback sender)");
+ return 0;
+ }
+ break;
+ case RPCBPROC_CALLIT:
+ case RPCBPROC_INDIRECT:
+ case RPCBPROC_DUMP:
+ case RPCBPROC_GETTIME:
+ case RPCBPROC_UADDR2TADDR:
+ case RPCBPROC_TADDR2UADDR:
+ case RPCBPROC_GETVERSADDR:
+ case RPCBPROC_GETADDRLIST:
+ case RPCBPROC_GETSTAT:
+ default:
+ break;
+ }
+
+#ifdef LIBWRAP
+ if (addr->sa_family == AF_LOCAL)
+ return 1;
+ request_init(&req, RQ_DAEMON, "rpcbind", RQ_CLIENT_SIN, addr, 0);
+ sock_methods(&req);
+ if(!hosts_access(&req)) {
+ logit(deny_severity, addr, proc, prog, ": request from unauthorized host");
+ return 0;
+ }
+#endif
+ if (verboselog)
+ logit(log_severity, addr, proc, prog, "");
+ return 1;
+}
+
+int
+is_loopback(struct netbuf *nbuf)
+{
+ struct sockaddr *addr = (struct sockaddr *)nbuf->buf;
+ struct sockaddr_in *sin;
+#ifdef INET6
+ struct sockaddr_in6 *sin6;
+#endif
+
+ switch (addr->sa_family) {
+ case AF_INET:
+ if (!oldstyle_local)
+ return 0;
+ sin = (struct sockaddr_in *)addr;
+ return ((sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) &&
+ (ntohs(sin->sin_port) < IPPORT_RESERVED));
+#ifdef INET6
+ case AF_INET6:
+ if (!oldstyle_local)
+ return 0;
+ sin6 = (struct sockaddr_in6 *)addr;
+ return (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr) &&
+ (ntohs(sin6->sin6_port) < IPV6PORT_RESERVED));
+#endif
+ case AF_LOCAL:
+ return 1;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+
+/* logit - report events of interest via the syslog daemon */
+void
+logit(int severity, struct sockaddr *addr, rpcproc_t procnum, rpcprog_t prognum,
+ const char *text)
+{
+ const char *procname;
+ char procbuf[32];
+ char *progname;
+ char progbuf[32];
+ char fromname[NI_MAXHOST];
+ struct rpcent *rpc;
+ static const char *procmap[] = {
+ /* RPCBPROC_NULL */ "null",
+ /* RPCBPROC_SET */ "set",
+ /* RPCBPROC_UNSET */ "unset",
+ /* RPCBPROC_GETADDR */ "getport/addr",
+ /* RPCBPROC_DUMP */ "dump",
+ /* RPCBPROC_CALLIT */ "callit",
+ /* RPCBPROC_GETTIME */ "gettime",
+ /* RPCBPROC_UADDR2TADDR */ "uaddr2taddr",
+ /* RPCBPROC_TADDR2UADDR */ "taddr2uaddr",
+ /* RPCBPROC_GETVERSADDR */ "getversaddr",
+ /* RPCBPROC_INDIRECT */ "indirect",
+ /* RPCBPROC_GETADDRLIST */ "getaddrlist",
+ /* RPCBPROC_GETSTAT */ "getstat"
+ };
+
+ /*
+ * Fork off a process or the portmap daemon might hang while
+ * getrpcbynumber() or syslog() does its thing.
+ */
+
+ if (fork() == 0) {
+ setproctitle("logit");
+
+ /* Try to map program number to name. */
+
+ if (prognum == 0) {
+ progname = "";
+ } else if ((rpc = getrpcbynumber((int) prognum))) {
+ progname = rpc->r_name;
+ } else {
+ snprintf(progname = progbuf, sizeof(progbuf), "%u",
+ (unsigned)prognum);
+ }
+
+ /* Try to map procedure number to name. */
+
+ if (procnum >= (sizeof procmap / sizeof (char *))) {
+ snprintf(procbuf, sizeof procbuf, "%u",
+ (unsigned)procnum);
+ procname = procbuf;
+ } else
+ procname = procmap[procnum];
+
+ /* Write syslog record. */
+
+ if (addr->sa_family == AF_LOCAL)
+ strcpy(fromname, "local");
+ else
+ getnameinfo(addr, addr->sa_len, fromname,
+ sizeof fromname, NULL, 0, NI_NUMERICHOST);
+
+ syslog(severity, "connect from %s to %s(%s)%s",
+ fromname, procname, progname, text);
+ _exit(0);
+ }
+}
+
+int
+check_callit(SVCXPRT *xprt, struct r_rmtcall_args *args, int versnum __unused)
+{
+ struct sockaddr *sa = (struct sockaddr *)svc_getrpccaller(xprt)->buf;
+
+ /*
+ * Always allow calling NULLPROC
+ */
+ if (args->rmt_proc == 0)
+ return 1;
+
+ /*
+ * XXX - this special casing sucks.
+ */
+ switch (args->rmt_prog) {
+ case RPCBPROG:
+ /*
+ * Allow indirect calls to ourselves in insecure mode.
+ * The is_loopback checks aren't useful then anyway.
+ */
+ if (!insecure)
+ goto deny;
+ break;
+ case MOUNTPROG:
+ if (args->rmt_proc != MOUNTPROC_MNT &&
+ args->rmt_proc != MOUNTPROC_UMNT)
+ break;
+ goto deny;
+ case YPBINDPROG:
+ if (args->rmt_proc != YPBINDPROC_SETDOM)
+ break;
+ /* FALLTHROUGH */
+ case YPPASSWDPROG:
+ case NFS_PROGRAM:
+ case RQUOTAPROG:
+ goto deny;
+ case YPPROG:
+ switch (args->rmt_proc) {
+ case YPPROC_ALL:
+ case YPPROC_MATCH:
+ case YPPROC_FIRST:
+ case YPPROC_NEXT:
+ goto deny;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+
+ return 1;
+deny:
+#ifdef LIBWRAP
+ logit(deny_severity, sa, args->rmt_proc, args->rmt_prog,
+ ": indirect call not allowed");
+#else
+ logit(0, sa, args->rmt_proc, args->rmt_prog,
+ ": indirect call not allowed");
+#endif
+ return 0;
+}
diff --git a/usr.sbin/rpcbind/util.c b/usr.sbin/rpcbind/util.c
new file mode 100644
index 0000000..66797a7
--- /dev/null
+++ b/usr.sbin/rpcbind/util.c
@@ -0,0 +1,383 @@
+/*
+ * $NetBSD: util.c,v 1.4 2000/08/03 00:04:30 fvdl Exp $
+ * $FreeBSD$
+ */
+
+/*-
+ * Copyright (c) 2000 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Frank van der Linden.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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/types.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <ifaddrs.h>
+#include <sys/poll.h>
+#include <rpc/rpc.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <netconfig.h>
+#include <stdio.h>
+#include <arpa/inet.h>
+
+#include "rpcbind.h"
+
+#define SA2SIN(sa) ((struct sockaddr_in *)(sa))
+#define SA2SINADDR(sa) (SA2SIN(sa)->sin_addr)
+#ifdef INET6
+#define SA2SIN6(sa) ((struct sockaddr_in6 *)(sa))
+#define SA2SIN6ADDR(sa) (SA2SIN6(sa)->sin6_addr)
+#endif
+
+static struct sockaddr_in *local_in4;
+#ifdef INET6
+static struct sockaddr_in6 *local_in6;
+#endif
+
+static int bitmaskcmp(void *, void *, void *, int);
+#ifdef INET6
+static void in6_fillscopeid(struct sockaddr_in6 *);
+#endif
+
+/*
+ * For all bits set in "mask", compare the corresponding bits in
+ * "dst" and "src", and see if they match. Returns 0 if the addresses
+ * match.
+ */
+static int
+bitmaskcmp(void *dst, void *src, void *mask, int bytelen)
+{
+ int i;
+ u_int8_t *p1 = dst, *p2 = src, *netmask = mask;
+
+ for (i = 0; i < bytelen; i++)
+ if ((p1[i] & netmask[i]) != (p2[i] & netmask[i]))
+ return (1);
+ return (0);
+}
+
+/*
+ * Similar to code in ifconfig.c. Fill in the scope ID for link-local
+ * addresses returned by getifaddrs().
+ */
+#ifdef INET6
+static void
+in6_fillscopeid(struct sockaddr_in6 *sin6)
+{
+ u_int16_t ifindex;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr)) {
+ ifindex = ntohs(*(u_int16_t *)&sin6->sin6_addr.s6_addr[2]);
+ if (sin6->sin6_scope_id == 0 && ifindex != 0) {
+ sin6->sin6_scope_id = ifindex;
+ *(u_int16_t *)&sin6->sin6_addr.s6_addr[2] = 0;
+ }
+ }
+}
+#endif
+
+/*
+ * Find a server address that can be used by `caller' to contact
+ * the local service specified by `serv_uaddr'. If `clnt_uaddr' is
+ * non-NULL, it is used instead of `caller' as a hint suggesting
+ * the best address (e.g. the `r_addr' field of an rpc, which
+ * contains the rpcbind server address that the caller used).
+ *
+ * Returns the best server address as a malloc'd "universal address"
+ * string which should be freed by the caller. On error, returns NULL.
+ */
+char *
+addrmerge(struct netbuf *caller, char *serv_uaddr, char *clnt_uaddr,
+ char *netid)
+{
+ struct ifaddrs *ifap, *ifp = NULL, *bestif;
+ struct netbuf *serv_nbp = NULL, *hint_nbp = NULL, tbuf;
+ struct sockaddr *caller_sa, *hint_sa, *ifsa, *ifmasksa, *serv_sa;
+ struct sockaddr_storage ss;
+ struct netconfig *nconf;
+ char *caller_uaddr = NULL, *hint_uaddr = NULL;
+ char *ret = NULL;
+
+#ifdef ND_DEBUG
+ if (debugging)
+ fprintf(stderr, "addrmerge(caller, %s, %s, %s\n", serv_uaddr,
+ clnt_uaddr == NULL ? "NULL" : clnt_uaddr, netid);
+#endif
+ caller_sa = caller->buf;
+ if ((nconf = rpcbind_get_conf(netid)) == NULL)
+ goto freeit;
+ if ((caller_uaddr = taddr2uaddr(nconf, caller)) == NULL)
+ goto freeit;
+
+ /*
+ * Use `clnt_uaddr' as the hint if non-NULL, but ignore it if its
+ * address family is different from that of the caller.
+ */
+ hint_sa = NULL;
+ if (clnt_uaddr != NULL) {
+ hint_uaddr = clnt_uaddr;
+ if ((hint_nbp = uaddr2taddr(nconf, clnt_uaddr)) == NULL)
+ goto freeit;
+ hint_sa = hint_nbp->buf;
+ }
+ if (hint_sa == NULL || hint_sa->sa_family != caller_sa->sa_family) {
+ hint_uaddr = caller_uaddr;
+ hint_sa = caller->buf;
+ }
+
+#ifdef ND_DEBUG
+ if (debugging)
+ fprintf(stderr, "addrmerge: hint %s\n", hint_uaddr);
+#endif
+ /* Local caller, just return the server address. */
+ if (strncmp(caller_uaddr, "0.0.0.0.", 8) == 0 ||
+ strncmp(caller_uaddr, "::.", 3) == 0 || caller_uaddr[0] == '/') {
+ ret = strdup(serv_uaddr);
+ goto freeit;
+ }
+
+ if (getifaddrs(&ifp) < 0)
+ goto freeit;
+
+ /*
+ * Loop through all interfaces. For each interface, see if the
+ * network portion of its address is equal to that of the client.
+ * If so, we have found the interface that we want to use.
+ */
+ bestif = NULL;
+ for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) {
+ ifsa = ifap->ifa_addr;
+ ifmasksa = ifap->ifa_netmask;
+
+ if (ifsa == NULL || ifsa->sa_family != hint_sa->sa_family ||
+ !(ifap->ifa_flags & IFF_UP))
+ continue;
+
+ switch (hint_sa->sa_family) {
+ case AF_INET:
+ /*
+ * If the hint address matches this interface
+ * address/netmask, then we're done.
+ */
+ if (!bitmaskcmp(&SA2SINADDR(ifsa),
+ &SA2SINADDR(hint_sa), &SA2SINADDR(ifmasksa),
+ sizeof(struct in_addr))) {
+ bestif = ifap;
+ goto found;
+ }
+ break;
+#ifdef INET6
+ case AF_INET6:
+ /*
+ * For v6 link local addresses, if the caller is on
+ * a link-local address then use the scope id to see
+ * which one.
+ */
+ in6_fillscopeid(SA2SIN6(ifsa));
+ if (IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(ifsa)) &&
+ IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(caller_sa)) &&
+ IN6_IS_ADDR_LINKLOCAL(&SA2SIN6ADDR(hint_sa))) {
+ if (SA2SIN6(ifsa)->sin6_scope_id ==
+ SA2SIN6(caller_sa)->sin6_scope_id) {
+ bestif = ifap;
+ goto found;
+ }
+ } else if (!bitmaskcmp(&SA2SIN6ADDR(ifsa),
+ &SA2SIN6ADDR(hint_sa), &SA2SIN6ADDR(ifmasksa),
+ sizeof(struct in6_addr))) {
+ bestif = ifap;
+ goto found;
+ }
+ break;
+#endif
+ default:
+ continue;
+ }
+
+ /*
+ * Remember the first possibly useful interface, preferring
+ * "normal" to point-to-point and loopback ones.
+ */
+ if (bestif == NULL ||
+ (!(ifap->ifa_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) &&
+ (bestif->ifa_flags & (IFF_LOOPBACK | IFF_POINTOPOINT))))
+ bestif = ifap;
+ }
+ if (bestif == NULL)
+ goto freeit;
+
+found:
+ /*
+ * Construct the new address using the the address from
+ * `bestif', and the port number from `serv_uaddr'.
+ */
+ serv_nbp = uaddr2taddr(nconf, serv_uaddr);
+ if (serv_nbp == NULL)
+ goto freeit;
+ serv_sa = serv_nbp->buf;
+
+ memcpy(&ss, bestif->ifa_addr, bestif->ifa_addr->sa_len);
+ switch (ss.ss_family) {
+ case AF_INET:
+ SA2SIN(&ss)->sin_port = SA2SIN(serv_sa)->sin_port;
+ break;
+#ifdef INET6
+ case AF_INET6:
+ SA2SIN6(&ss)->sin6_port = SA2SIN6(serv_sa)->sin6_port;
+ break;
+#endif
+ }
+ tbuf.len = ss.ss_len;
+ tbuf.maxlen = sizeof(ss);
+ tbuf.buf = &ss;
+ ret = taddr2uaddr(nconf, &tbuf);
+
+freeit:
+ if (caller_uaddr != NULL)
+ free(caller_uaddr);
+ if (hint_nbp != NULL) {
+ free(hint_nbp->buf);
+ free(hint_nbp);
+ }
+ if (serv_nbp != NULL) {
+ free(serv_nbp->buf);
+ free(serv_nbp);
+ }
+ if (ifp != NULL)
+ freeifaddrs(ifp);
+
+#ifdef ND_DEBUG
+ if (debugging)
+ fprintf(stderr, "addrmerge: returning %s\n", ret);
+#endif
+ return ret;
+}
+
+void
+network_init()
+{
+#ifdef INET6
+ struct ifaddrs *ifap, *ifp;
+ struct ipv6_mreq mreq6;
+ unsigned int ifindex;
+ int s;
+#endif
+ int ecode;
+ struct addrinfo hints, *res;
+
+ memset(&hints, 0, sizeof hints);
+ hints.ai_family = AF_INET;
+ if ((ecode = getaddrinfo(NULL, "sunrpc", &hints, &res))) {
+ if (debugging)
+ fprintf(stderr, "can't get local ip4 address: %s\n",
+ gai_strerror(ecode));
+ } else {
+ local_in4 = (struct sockaddr_in *)malloc(sizeof *local_in4);
+ if (local_in4 == NULL) {
+ if (debugging)
+ fprintf(stderr, "can't alloc local ip4 addr\n");
+ }
+ memcpy(local_in4, res->ai_addr, sizeof *local_in4);
+ }
+
+#ifdef INET6
+ hints.ai_family = AF_INET6;
+ if ((ecode = getaddrinfo(NULL, "sunrpc", &hints, &res))) {
+ if (debugging)
+ fprintf(stderr, "can't get local ip6 address: %s\n",
+ gai_strerror(ecode));
+ } else {
+ local_in6 = (struct sockaddr_in6 *)malloc(sizeof *local_in6);
+ if (local_in6 == NULL) {
+ if (debugging)
+ fprintf(stderr, "can't alloc local ip6 addr\n");
+ }
+ memcpy(local_in6, res->ai_addr, sizeof *local_in6);
+ }
+
+ /*
+ * Now join the RPC ipv6 multicast group on all interfaces.
+ */
+ if (getifaddrs(&ifp) < 0)
+ return;
+
+ mreq6.ipv6mr_interface = 0;
+ inet_pton(AF_INET6, RPCB_MULTICAST_ADDR, &mreq6.ipv6mr_multiaddr);
+
+ s = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+
+ /*
+ * Loop through all interfaces. For each IPv6 multicast-capable
+ * interface, join the RPC multicast group on that interface.
+ */
+ for (ifap = ifp; ifap != NULL; ifap = ifap->ifa_next) {
+ if (ifap->ifa_addr->sa_family != AF_INET6 ||
+ !(ifap->ifa_flags & IFF_MULTICAST))
+ continue;
+ ifindex = if_nametoindex(ifap->ifa_name);
+ if (ifindex == mreq6.ipv6mr_interface)
+ /*
+ * Already did this one.
+ */
+ continue;
+ mreq6.ipv6mr_interface = ifindex;
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq6,
+ sizeof mreq6) < 0)
+ if (debugging)
+ perror("setsockopt v6 multicast");
+ }
+#endif
+
+ /* close(s); */
+}
+
+struct sockaddr *
+local_sa(int af)
+{
+ switch (af) {
+ case AF_INET:
+ return (struct sockaddr *)local_in4;
+#ifdef INET6
+ case AF_INET6:
+ return (struct sockaddr *)local_in6;
+#endif
+ default:
+ return NULL;
+ }
+}
diff --git a/usr.sbin/rpcbind/warmstart.c b/usr.sbin/rpcbind/warmstart.c
new file mode 100644
index 0000000..410f270
--- /dev/null
+++ b/usr.sbin/rpcbind/warmstart.c
@@ -0,0 +1,179 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+/*
+ * warmstart.c
+ * Allows for gathering of registrations from an earlier dumped file.
+ *
+ * Copyright (c) 1990 by Sun Microsystems, Inc.
+ */
+
+/*
+ * #ident "@(#)warmstart.c 1.7 93/07/05 SMI"
+ * $FreeBSD$/
+ */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <rpc/rpcb_prot.h>
+#include <rpc/xdr.h>
+#ifdef PORTMAP
+#include <netinet/in.h>
+#include <rpc/pmap_prot.h>
+#endif
+#include <syslog.h>
+#include <unistd.h>
+
+#include "rpcbind.h"
+
+/*
+ * XXX this code is unsafe and is not used. It should be made safe.
+ */
+
+
+/* These files keep the pmap_list and rpcb_list in XDR format */
+#define RPCBFILE "/tmp/rpcbind.file"
+#ifdef PORTMAP
+#define PMAPFILE "/tmp/portmap.file"
+#endif
+
+static bool_t write_struct(char *, xdrproc_t, void *);
+static bool_t read_struct(char *, xdrproc_t, void *);
+
+static bool_t
+write_struct(char *filename, xdrproc_t structproc, void *list)
+{
+ FILE *fp;
+ XDR xdrs;
+ mode_t omask;
+
+ omask = umask(077);
+ fp = fopen(filename, "w");
+ if (fp == NULL) {
+ int i;
+
+ for (i = 0; i < 10; i++)
+ close(i);
+ fp = fopen(filename, "w");
+ if (fp == NULL) {
+ syslog(LOG_ERR,
+ "cannot open file = %s for writing", filename);
+ syslog(LOG_ERR, "cannot save any registration");
+ return (FALSE);
+ }
+ }
+ (void) umask(omask);
+ xdrstdio_create(&xdrs, fp, XDR_ENCODE);
+
+ if (structproc(&xdrs, list) == FALSE) {
+ syslog(LOG_ERR, "rpcbind: xdr_%s: failed", filename);
+ fclose(fp);
+ return (FALSE);
+ }
+ XDR_DESTROY(&xdrs);
+ fclose(fp);
+ return (TRUE);
+}
+
+static bool_t
+read_struct(char *filename, xdrproc_t structproc, void *list)
+{
+ FILE *fp;
+ XDR xdrs;
+ struct stat sbuf;
+
+ if (stat(filename, &sbuf) != 0) {
+ fprintf(stderr,
+ "rpcbind: cannot stat file = %s for reading\n", filename);
+ goto error;
+ }
+ if ((sbuf.st_uid != 0) || (sbuf.st_mode & S_IRWXG) ||
+ (sbuf.st_mode & S_IRWXO)) {
+ fprintf(stderr,
+ "rpcbind: invalid permissions on file = %s for reading\n",
+ filename);
+ goto error;
+ }
+ fp = fopen(filename, "r");
+ if (fp == NULL) {
+ fprintf(stderr,
+ "rpcbind: cannot open file = %s for reading\n", filename);
+ goto error;
+ }
+ xdrstdio_create(&xdrs, fp, XDR_DECODE);
+
+ if (structproc(&xdrs, list) == FALSE) {
+ fprintf(stderr, "rpcbind: xdr_%s: failed\n", filename);
+ fclose(fp);
+ goto error;
+ }
+ XDR_DESTROY(&xdrs);
+ fclose(fp);
+ return (TRUE);
+
+error: fprintf(stderr, "rpcbind: will start from scratch\n");
+ return (FALSE);
+}
+
+void
+write_warmstart()
+{
+ (void) write_struct(RPCBFILE, (xdrproc_t)xdr_rpcblist_ptr, &list_rbl);
+#ifdef PORTMAP
+ (void) write_struct(PMAPFILE, (xdrproc_t)xdr_pmaplist_ptr, &list_pml);
+#endif
+
+}
+
+void
+read_warmstart()
+{
+ rpcblist_ptr tmp_rpcbl = NULL;
+#ifdef PORTMAP
+ struct pmaplist *tmp_pmapl = NULL;
+#endif
+ int ok1, ok2 = TRUE;
+
+ ok1 = read_struct(RPCBFILE, (xdrproc_t)xdr_rpcblist_ptr, &tmp_rpcbl);
+ if (ok1 == FALSE)
+ return;
+#ifdef PORTMAP
+ ok2 = read_struct(PMAPFILE, (xdrproc_t)xdr_pmaplist_ptr, &tmp_pmapl);
+#endif
+ if (ok2 == FALSE) {
+ xdr_free((xdrproc_t) xdr_rpcblist_ptr, (char *)&tmp_rpcbl);
+ return;
+ }
+ xdr_free((xdrproc_t) xdr_rpcblist_ptr, (char *)&list_rbl);
+ list_rbl = tmp_rpcbl;
+#ifdef PORTMAP
+ xdr_free((xdrproc_t) xdr_pmaplist_ptr, (char *)&list_pml);
+ list_pml = tmp_pmapl;
+#endif
+}
diff --git a/usr.sbin/rrenumd/Makefile b/usr.sbin/rrenumd/Makefile
new file mode 100644
index 0000000..c31a7c7
--- /dev/null
+++ b/usr.sbin/rrenumd/Makefile
@@ -0,0 +1,36 @@
+# 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$
+
+PROG= rrenumd
+MAN= rrenumd.conf.5 rrenumd.8
+SRCS= rrenumd.c parser.y lexer.l
+
+CFLAGS+= -DIPSEC -I. -I${.CURDIR}
+YFLAGS= -d
+
+LDADD= -lipsec -ll -ly
+DPADD= ${LIBIPSEC} ${LIBL} ${LIBY}
+
+CLEANFILES= y.tab.h
+SRCS+= y.tab.h
+y.tab.h: parser.y
+
+.if defined(YACCDEBUG)
+CFLAGS+= -DYYDEBUG
+YFLAGS+= -t -v
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rrenumd/lexer.l b/usr.sbin/rrenumd/lexer.l
new file mode 100644
index 0000000..bd0d3c0
--- /dev/null
+++ b/usr.sbin/rrenumd/lexer.l
@@ -0,0 +1,273 @@
+/* $KAME: lexer.l,v 1.7 2000/11/08 02:40:53 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$
+ */
+
+%{
+#define YY_NO_UNPUT
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+
+#include <string.h>
+
+#include <net/if.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <net/if_var.h>
+#endif /* __FreeBSD__ >= 3 */
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/icmp6.h>
+
+#include <arpa/inet.h>
+
+#include "y.tab.h"
+
+int lineno = 1;
+
+#define LINEBUF_SIZE 1000
+char linebuf[LINEBUF_SIZE];
+
+int parse(FILE **);
+void yyerror(const char *);
+int yylex(void);
+%}
+
+/* common section */
+nl \n
+ws [ \t]+
+digit [0-9]
+letter [0-9A-Za-z]
+hexdigit [0-9A-Fa-f]
+special [()+\|\?\*,]
+dot \.
+hyphen \-
+colon \:
+slash \/
+bcl \{
+ecl \}
+semi \;
+usec {dot}{digit}{1,6}
+comment \#.*
+qstring \"[^"]*\"
+decstring {digit}+
+hexpair {hexdigit}{hexdigit}
+hexstring 0[xX]{hexdigit}+
+octetstring {octet}({dot}{octet})+
+ipv4addr {digit}{1,3}({dot}{digit}{1,3}){0,3}
+ipv6addr {hexdigit}{0,4}({colon}{hexdigit}{0,4}){2,7}
+ipaddrmask {slash}{digit}{1,3}
+keyword {letter}{letter}+
+name {letter}(({letter}|{digit}|{hyphen})*({letter}|{digit}))*
+hostname {name}(({dot}{name})+{dot}?)?
+
+timeval {digit}{0,2}
+days d{timeval}
+hours h{timeval}
+minutes m{timeval}
+seconds s{timeval}
+
+mprefix match_prefix|match-prefix
+uprefix use_prefix|use-prefix
+
+%%
+ /* rrenumd keywords */
+debug {
+ return(DEBUG_CMD);
+ }
+dest {
+ return(DEST_CMD);
+ }
+retry {
+ return(RETRY_CMD);
+ }
+seqnum {
+ return(SEQNUM_CMD);
+ }
+add {
+ yylval.num = RPM_PCO_ADD;
+ return(ADD);
+ }
+change {
+ yylval.num = RPM_PCO_CHANGE;
+ return(CHANGE);
+ }
+setglobal {
+ yylval.num = RPM_PCO_SETGLOBAL;
+ return(SETGLOBAL);
+ }
+{mprefix} {
+ return(MATCH_PREFIX_CMD);
+ }
+maxlen {
+ return(MAXLEN_CMD);
+ }
+minlen {
+ return(MINLEN_CMD);
+ }
+{uprefix} {
+ return(USE_PREFIX_CMD);
+ }
+keeplen {
+ return(KEEPLEN_CMD);
+ }
+
+vltime {
+ return(VLTIME_CMD);
+ }
+pltime {
+ return(PLTIME_CMD);
+ }
+raf_onlink {
+ return(RAF_ONLINK_CMD);
+ }
+raf_auto {
+ return(RAF_AUTO_CMD);
+ }
+rrf_decrvalid {
+ return(RAF_DECRVALID_CMD);
+ }
+rrf_decrprefd {
+ return(RAF_DECRPREFD_CMD);
+ }
+{days} {
+ yytext++;
+ yylval.num = atoi(yytext);
+ return(DAYS);
+ }
+{hours} {
+ yytext++;
+ yylval.num = atoi(yytext);
+ return(HOURS);
+ }
+{minutes} {
+ yytext++;
+ yylval.num = atoi(yytext);
+ return(MINUTES);
+ }
+{seconds} {
+ yytext++;
+ yylval.num = atoi(yytext);
+ return(SECONDS);
+ }
+infinity {
+ return(INFINITY);
+ }
+
+on {
+ yylval.num = 1;
+ return(ON);
+ }
+off {
+ yylval.num = 0;
+ return(OFF);
+ }
+
+ /* basic rules */
+{ws} ;
+{nl} {
+ lineno++;
+ }
+{semi} {
+ return EOS;
+ }
+{bcl} {
+ return BCL;
+ }
+{ecl} {
+ return ECL;
+ }
+{qstring} {
+ yylval.cs.cp = yytext;
+ yylval.cs.len = yyleng;
+ return QSTRING;
+ }
+{decstring} {
+ yylval.cs.cp = yytext;
+ yylval.cs.len = yyleng;
+ return DECSTRING;
+ }
+{name} {
+ yylval.cs.cp = yytext;
+ yylval.cs.len = yyleng;
+ return NAME;
+ }
+{ipv4addr} {
+ memset(&yylval.addr4, 0, sizeof(struct in_addr));
+ if (inet_pton(AF_INET, yytext,
+ &yylval.addr4) == 1) {
+ return IPV4ADDR;
+ } else {
+ return ERROR;
+ }
+ }
+{ipv6addr} {
+ memset(&yylval.addr6, 0, sizeof(struct in6_addr));
+ if (inet_pton(AF_INET6, yytext,
+ &yylval.addr6) == 1) {
+ return IPV6ADDR;
+ } else {
+ return ERROR;
+ }
+ }
+{ipaddrmask} {
+ yytext++;
+ yylval.num = atoi(yytext);
+ return(PREFIXLEN);
+ }
+{hostname} {
+ yylval.cs.cp = yytext;
+ yylval.cs.len = yyleng;
+ return HOSTNAME;
+ }
+%%
+
+int parse(FILE **fp)
+{
+ extern int yyparse(void);
+
+ yyin = *fp;
+
+ if (yyparse())
+ return(-1);
+
+ return(0);
+
+}
+
+void
+yyerror(const char *s)
+{
+ printf("%s: at %s in line %d\n", s, yytext, lineno);
+}
diff --git a/usr.sbin/rrenumd/parser.y b/usr.sbin/rrenumd/parser.y
new file mode 100644
index 0000000..65dfd46
--- /dev/null
+++ b/usr.sbin/rrenumd/parser.y
@@ -0,0 +1,676 @@
+/* $KAME: parser.y,v 1.8 2000/11/08 03:03:34 jinmei 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$
+ */
+
+%{
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#if defined(__FreeBSD__) && __FreeBSD__ >= 3
+#include <net/if_var.h>
+#endif /* __FreeBSD__ >= 3 */
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/icmp6.h>
+
+#include <limits.h>
+#include <netdb.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "rrenumd.h"
+
+struct config_is_set {
+ u_short cis_dest : 1;
+} cis;
+
+struct dst_list *dl_head;
+struct payload_list *pl_head, ple_cur;
+u_int retry;
+char errbuf[LINE_MAX];
+
+extern int lineno;
+extern void yyerror(const char *s);
+extern int yylex(void);
+static struct payload_list * pllist_lookup(int seqnum);
+static void pllist_enqueue(struct payload_list *pl_entry);
+
+#define MAX_RETRYNUM 10 /* upper limit of retry in this rrenumd program */
+#define MAX_SEQNUM 256 /* upper limit of seqnum in this rrenumd program */
+#define NOSPEC -1
+
+%}
+
+%union {
+ u_long num;
+ struct {
+ char *cp;
+ int len;
+ } cs;
+ struct in_addr addr4;
+ struct in6_addr addr6;
+ struct {
+ struct in6_addr addr;
+ u_char plen;
+ } prefix;
+ struct dst_list *dl;
+ struct payload_list *pl;
+ struct sockaddr *sa;
+}
+
+%token <num> ADD CHANGE SETGLOBAL
+%token DEBUG_CMD DEST_CMD RETRY_CMD SEQNUM_CMD
+%token MATCH_PREFIX_CMD MAXLEN_CMD MINLEN_CMD
+%token USE_PREFIX_CMD KEEPLEN_CMD
+%token VLTIME_CMD PLTIME_CMD
+%token RAF_ONLINK_CMD RAF_AUTO_CMD RAF_DECRVALID_CMD RAF_DECRPREFD_CMD
+%token <num> DAYS HOURS MINUTES SECONDS INFINITY
+%token <num> ON OFF
+%token BCL ECL EOS ERROR
+%token <cs> NAME HOSTNAME QSTRING DECSTRING
+%token <addr4> IPV4ADDR
+%token <addr6> IPV6ADDR
+%token <num> PREFIXLEN
+
+%type <num> retrynum seqnum rrenum_cmd
+%type <num> prefixlen maxlen minlen keeplen vltime pltime
+%type <num> lifetime days hours minutes seconds
+%type <num> decstring
+%type <num> raf_onlink raf_auto raf_decrvalid raf_decrprefd flag
+%type <dl> dest_addrs dest_addr sin sin6
+%type <pl> rrenum_statement
+%type <cs> ifname
+%type <prefix> prefixval
+
+%%
+config:
+ /* empty */
+ | statements
+ ;
+
+statements:
+ statement
+ | statements statement
+ ;
+
+statement:
+ debug_statement
+ | destination_statement
+ | rrenum_statement_without_seqnum
+ | rrenum_statement_with_seqnum
+ | error EOS
+ {
+ yyerrok;
+ }
+ | EOS
+ ;
+
+debug_statement:
+ DEBUG_CMD flag EOS
+ {
+#ifdef YYDEBUG
+ yydebug = $2;
+#endif /* YYDEBUG */
+ }
+ ;
+
+destination_statement:
+ DEST_CMD dest_addrs retrynum EOS
+ {
+ dl_head = $2;
+ retry = $3;
+ }
+ ;
+
+dest_addrs:
+ dest_addr
+ | dest_addrs dest_addr
+ {
+ $2->dl_next = $1;
+ $$ = $2;
+ }
+ ;
+
+dest_addr :
+ sin
+ {
+ with_v4dest = 1;
+ }
+ | sin6
+ {
+ with_v6dest = 1;
+ }
+ | sin6 ifname
+ {
+ struct sockaddr_in6 *sin6;
+
+ sin6 = (struct sockaddr_in6 *)$1->dl_dst;
+ sin6->sin6_scope_id = if_nametoindex($2.cp);
+ with_v6dest = 1;
+ $$ = $1;
+ }
+ | HOSTNAME
+ {
+ struct sockaddr_storage *ss;
+ struct addrinfo hints, *res;
+ int error;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_CANONNAME;
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_RAW;
+ hints.ai_protocol = 0;
+ error = getaddrinfo($1.cp, 0, &hints, &res);
+ if (error) {
+ snprintf(errbuf, sizeof(errbuf),
+ "name resolution failed for %s:%s",
+ $1.cp, gai_strerror(error));
+ yyerror(errbuf);
+ }
+ ss = (struct sockaddr_storage *)malloc(sizeof(*ss));
+ memset(ss, 0, sizeof(*ss));
+ memcpy(ss, res->ai_addr, res->ai_addr->sa_len);
+ freeaddrinfo(res);
+
+ $$ = (struct dst_list *)
+ malloc(sizeof(struct dst_list));
+ memset($$, 0, sizeof(struct dst_list));
+ $$->dl_dst = (struct sockaddr *)ss;
+ }
+ ;
+
+sin:
+ IPV4ADDR
+ {
+ struct sockaddr_in *sin;
+
+ sin = (struct sockaddr_in *)malloc(sizeof(*sin));
+ memset(sin, 0, sizeof(*sin));
+ sin->sin_len = sizeof(*sin);
+ sin->sin_family = AF_INET;
+ sin->sin_addr = $1;
+
+ $$ = (struct dst_list *)
+ malloc(sizeof(struct dst_list));
+ memset($$, 0, sizeof(struct dst_list));
+ $$->dl_dst = (struct sockaddr *)sin;
+ }
+ ;
+
+sin6:
+ IPV6ADDR
+ {
+ struct sockaddr_in6 *sin6;
+
+ sin6 = (struct sockaddr_in6 *)malloc(sizeof(*sin6));
+ memset(sin6, 0, sizeof(*sin6));
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_family = AF_INET6;
+ sin6->sin6_addr = $1;
+
+ $$ = (struct dst_list *)
+ malloc(sizeof(struct dst_list));
+ memset($$, 0, sizeof(struct dst_list));
+ $$->dl_dst = (struct sockaddr *)sin6;
+ }
+
+ifname:
+ NAME
+ {
+ $$.cp = strdup($1.cp);
+ $$.len = $1.len;
+ }
+ | QSTRING
+ {
+ $1.cp[$1.len - 1] = 0;
+ $$.cp = strdup(&$1.cp[1]);
+ $$.len = $1.len - 2;
+ }
+ ;
+
+retrynum:
+ /* empty */
+ {
+ $$ = 2;
+ }
+ | RETRY_CMD decstring
+ {
+ if ($2 > MAX_RETRYNUM)
+ $2 = MAX_RETRYNUM;
+ $$ = $2;
+ }
+ ;
+
+rrenum_statement_with_seqnum:
+ SEQNUM_CMD seqnum
+ {
+ if (pllist_lookup($2)) {
+ snprintf(errbuf, sizeof(errbuf),
+ "duplicate seqnum %ld specified at %d",
+ $2, lineno);
+ yyerror(errbuf);
+ }
+ }
+ BCL rrenum_statement EOS ECL EOS
+ {
+ $5->pl_irr.rr_seqnum = $2;
+ pllist_enqueue($5);
+ }
+ ;
+
+seqnum:
+ /* empty */
+ {
+ $$ = 0;
+ }
+ | decstring
+ {
+ if ($1 > MAX_SEQNUM) {
+ snprintf(errbuf, sizeof(errbuf),
+ "seqnum %ld is illegal for this program. "
+ "should be between 0 and %d",
+ $1, MAX_SEQNUM);
+ yyerror(errbuf);
+ }
+ $$ = $1;
+ }
+ ;
+
+rrenum_statement_without_seqnum:
+ rrenum_statement EOS
+ {
+ if (pllist_lookup(0)) {
+ snprintf(errbuf, sizeof(errbuf),
+ "duplicate seqnum %d specified at %d",
+ 0, lineno);
+ yyerror(errbuf);
+ }
+ $1->pl_irr.rr_seqnum = 0;
+ pllist_enqueue($1);
+ }
+ ;
+
+rrenum_statement:
+ match_prefix_definition use_prefix_definition
+ {
+ $$ = (struct payload_list *)
+ malloc(sizeof(struct payload_list));
+ memcpy($$, &ple_cur, sizeof(ple_cur));
+ }
+ ;
+
+match_prefix_definition:
+ rrenum_cmd MATCH_PREFIX_CMD prefixval maxlen minlen
+ {
+ struct icmp6_router_renum *irr;
+ struct rr_pco_match *rpm;
+
+ irr = (struct icmp6_router_renum *)&ple_cur.pl_irr;
+ rpm = (struct rr_pco_match *)(irr + 1);
+ memset(rpm, 0, sizeof(*rpm));
+
+ rpm->rpm_code = $1;
+ rpm->rpm_prefix = $3.addr;
+ rpm->rpm_matchlen = $3.plen;
+ rpm->rpm_maxlen = $4;
+ rpm->rpm_minlen = $5;
+ }
+ ;
+
+rrenum_cmd:
+ /* empty */
+ {
+ $$ = RPM_PCO_ADD;
+ }
+ | ADD
+ | CHANGE
+ | SETGLOBAL
+ ;
+
+prefixval:
+ IPV6ADDR prefixlen
+ {
+ $$.addr = $1;
+ $$.plen = $2;
+ }
+ ;
+
+prefixlen:
+ /* empty */
+ {
+ $$ = 64;
+ }
+ | PREFIXLEN
+ ;
+
+maxlen:
+ /* empty */
+ {
+ $$ = 128;
+ }
+ | MAXLEN_CMD decstring
+ {
+ if ($2 > 128)
+ $2 = 128;
+ $$ = $2;
+ }
+ ;
+
+minlen:
+ /* empty */
+ {
+ $$ = 0;
+ }
+ | MINLEN_CMD decstring
+ {
+ if ($2 > 128)
+ $2 = 128;
+ $$ = $2;
+ }
+ ;
+
+use_prefix_definition:
+ /* empty */
+ {
+ struct icmp6_router_renum *irr;
+ struct rr_pco_match *rpm;
+ struct rr_pco_use *rpu;
+
+ irr = (struct icmp6_router_renum *)&ple_cur.pl_irr;
+ rpm = (struct rr_pco_match *)(irr + 1);
+ rpu = (struct rr_pco_use *)(rpm + 1);
+ memset(rpu, 0, sizeof(*rpu));
+ }
+ | USE_PREFIX_CMD prefixval keeplen use_prefix_values
+ {
+ struct icmp6_router_renum *irr;
+ struct rr_pco_match *rpm;
+ struct rr_pco_use *rpu;
+
+ irr = (struct icmp6_router_renum *)&ple_cur.pl_irr;
+ rpm = (struct rr_pco_match *)(irr + 1);
+ rpu = (struct rr_pco_use *)(rpm + 1);
+
+ rpu->rpu_prefix = $2.addr;
+ rpu->rpu_uselen = $2.plen;
+ rpu->rpu_keeplen = $3;
+ }
+ ;
+
+use_prefix_values:
+ /* empty */
+ {
+ struct icmp6_router_renum *irr;
+ struct rr_pco_match *rpm;
+ struct rr_pco_use *rpu;
+
+ irr = (struct icmp6_router_renum *)&ple_cur.pl_irr;
+ rpm = (struct rr_pco_match *)(irr + 1);
+ rpu = (struct rr_pco_use *)(rpm + 1);
+ memset(rpu, 0, sizeof(*rpu));
+
+ rpu->rpu_vltime = htonl(DEF_VLTIME);
+ rpu->rpu_pltime = htonl(DEF_PLTIME);
+ rpu->rpu_ramask = 0;
+ rpu->rpu_flags = 0;
+ }
+ | BCL vltime pltime raf_onlink raf_auto raf_decrvalid raf_decrprefd ECL
+ {
+ struct icmp6_router_renum *irr;
+ struct rr_pco_match *rpm;
+ struct rr_pco_use *rpu;
+
+ irr = (struct icmp6_router_renum *)&ple_cur.pl_irr;
+ rpm = (struct rr_pco_match *)(irr + 1);
+ rpu = (struct rr_pco_use *)(rpm + 1);
+ memset(rpu, 0, sizeof(*rpu));
+
+ rpu->rpu_vltime = $2;
+ rpu->rpu_pltime = $3;
+ if ($4 == NOSPEC) {
+ rpu->rpu_ramask &=
+ ~ICMP6_RR_PCOUSE_RAFLAGS_ONLINK;
+ } else {
+ rpu->rpu_ramask |=
+ ICMP6_RR_PCOUSE_RAFLAGS_ONLINK;
+ if ($4 == ON) {
+ rpu->rpu_raflags |=
+ ICMP6_RR_PCOUSE_RAFLAGS_ONLINK;
+ } else {
+ rpu->rpu_raflags &=
+ ~ICMP6_RR_PCOUSE_RAFLAGS_ONLINK;
+ }
+ }
+ if ($5 == NOSPEC) {
+ rpu->rpu_ramask &=
+ ICMP6_RR_PCOUSE_RAFLAGS_AUTO;
+ } else {
+ rpu->rpu_ramask |=
+ ICMP6_RR_PCOUSE_RAFLAGS_AUTO;
+ if ($5 == ON) {
+ rpu->rpu_raflags |=
+ ICMP6_RR_PCOUSE_RAFLAGS_AUTO;
+ } else {
+ rpu->rpu_raflags &=
+ ~ICMP6_RR_PCOUSE_RAFLAGS_AUTO;
+ }
+ }
+ rpu->rpu_flags = 0;
+ if ($6 == ON) {
+ rpu->rpu_flags |=
+ ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME;
+ }
+ if ($7 == ON) {
+ rpu->rpu_flags |=
+ ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME;
+ }
+ }
+ ;
+
+keeplen:
+ /* empty */
+ {
+ $$ = 0;
+ }
+ | KEEPLEN_CMD decstring
+ {
+ if ($2 > 128)
+ $2 = 128;
+ $$ = $2;
+ }
+ ;
+
+
+vltime:
+ /* empty */
+ {
+ $$ = htonl(DEF_VLTIME);
+ }
+ | VLTIME_CMD lifetime
+ {
+ $$ = htonl($2);
+ }
+ ;
+
+pltime:
+ /* empty */
+ {
+ $$ = htonl(DEF_PLTIME);
+ }
+ | PLTIME_CMD lifetime
+ {
+ $$ = htonl($2);
+ }
+
+raf_onlink:
+ /* empty */
+ {
+ $$ = NOSPEC;
+ }
+ | RAF_ONLINK_CMD flag
+ {
+ $$ = $2;
+ }
+ ;
+
+raf_auto:
+ /* empty */
+ {
+ $$ = NOSPEC;
+ }
+ | RAF_AUTO_CMD flag
+ {
+ $$ = $2;
+ }
+ ;
+
+raf_decrvalid:
+ /* empty */
+ {
+ $$ = NOSPEC;
+ }
+ | RAF_DECRVALID_CMD flag
+ {
+ $$ = $2;
+ }
+ ;
+
+raf_decrprefd:
+ /* empty */
+ {
+ $$ = NOSPEC;
+ }
+ | RAF_DECRPREFD_CMD flag
+ {
+ $$ = $2;
+ }
+ ;
+
+flag:
+ ON { $$ = ON; }
+ | OFF { $$ = OFF; }
+ ;
+
+lifetime:
+ decstring
+ | INFINITY
+ {
+ $$ = 0xffffffff;
+ }
+ | days hours minutes seconds
+ {
+ int d, h, m, s;
+
+ d = $1 * 24 * 60 * 60;
+ h = $2 * 60 * 60;
+ m = $3 * 60;
+ s = $4;
+ $$ = d + h + m + s;
+ }
+ ;
+
+days:
+ /* empty */
+ {
+ $$ = 0;
+ }
+ | DAYS
+ ;
+
+hours:
+ /* empty */
+ {
+ $$ = 0;
+ }
+ | HOURS
+ ;
+
+minutes:
+ /* empty */
+ {
+ $$ = 0;
+ }
+ | MINUTES
+ ;
+
+seconds:
+ /* empty */
+ {
+ $$ = 0;
+ }
+ | SECONDS
+ ;
+
+decstring:
+ DECSTRING
+ {
+ int dval;
+
+ dval = atoi($1.cp);
+ $$ = dval;
+ }
+ ;
+
+%%
+
+static struct payload_list *
+pllist_lookup(int seqnum)
+{
+ struct payload_list *pl;
+ for (pl = pl_head; pl && pl->pl_irr.rr_seqnum != seqnum;
+ pl = pl->pl_next)
+ continue;
+ return (pl);
+}
+
+static void
+pllist_enqueue(struct payload_list *pl_entry)
+{
+ struct payload_list *pl, *pl_last;
+
+ pl_last = NULL;
+ for (pl = pl_head;
+ pl && pl->pl_irr.rr_seqnum < pl_entry->pl_irr.rr_seqnum;
+ pl_last = pl, pl = pl->pl_next)
+ continue;
+ if (pl_last)
+ pl_last->pl_next = pl_entry;
+ else
+ pl_head = pl_entry;
+
+ return;
+}
diff --git a/usr.sbin/rrenumd/rrenumd.8 b/usr.sbin/rrenumd/rrenumd.8
new file mode 100644
index 0000000..cc13a01
--- /dev/null
+++ b/usr.sbin/rrenumd/rrenumd.8
@@ -0,0 +1,103 @@
+.\" $KAME: rrenumd.8,v 1.6 2001/01/22 02:06:24 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 September 7, 1998
+.Dt RRENUMD 8
+.Os
+.Sh NAME
+.Nm rrenumd
+.Nd router renumbering daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl df
+.Oo
+.Fl c Ar conf_file | Fl s
+.Oc
+.Sh DESCRIPTION
+The
+.Nm
+utility transmits router renumbering request packets,
+to renumber the routers in the site network.
+.Pp
+On KAME-based systems,
+router renumbering requests are received and processed by
+.Xr rtadvd 8 .
+For other systems, refer to relevant documents.
+.Pp
+The program will daemonize itself on invocation.
+It reads configuration information from standard input if
+.Fl s
+is specified, or from
+.Ar conf_file
+if
+.Fl c Ar conf_file
+is specified.
+.Pp
+The contents of configuration information are described in
+.Xr rrenumd.conf 5 .
+.Pp
+After successful configuration,
+.Nm
+sends router renumbering
+messages periodically to configured destinations.
+Messages contain prefixes configured to be renumbered.
+.Bl -tag -width indent
+.\"
+.It Fl d
+Debug mode.
+.It Fl f
+Foreground mode.
+Do not become daemon.
+.It Fl s
+Script mode.
+Configuration information is obtained from standard input.
+.It Fl c Ar conf_file
+Specify a configuration file where configuration information is kept.
+.El
+.Sh EXIT STATUS
+The program exits with 0 on success, and non-zero on failures.
+.Sh SEE ALSO
+.Xr rrenumd.conf 5 ,
+.Xr rtadvd 8
+.Sh STANDARDS
+.Rs
+.%A Matt Crawford
+.%R RFC
+.%N 2894
+.%D August 2000
+.%T "Router Renumbering for IPv6"
+.Re
+.Sh HISTORY
+The
+.Nm
+utility first appeared in KAME IPv6 protocol stack kit.
+.\" .Sh BUGS
+.\" (to be written)
diff --git a/usr.sbin/rrenumd/rrenumd.c b/usr.sbin/rrenumd/rrenumd.c
new file mode 100644
index 0000000..68f6847
--- /dev/null
+++ b/usr.sbin/rrenumd/rrenumd.c
@@ -0,0 +1,663 @@
+/* $KAME: rrenumd.c,v 1.20 2000/11/08 02:40:53 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$
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+
+#include <string.h>
+
+#include <net/route.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+
+#include <arpa/inet.h>
+
+#ifdef IPSEC
+#include <netipsec/ipsec.h>
+#endif
+
+#include <stdio.h>
+#include <err.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+
+#include "rrenumd.h"
+
+#define LL_ALLROUTERS "ff02::2"
+#define SL_ALLROUTERS "ff05::2"
+
+#define RR_MCHLIM_DEFAULT 64
+
+#ifndef IN6_IS_SCOPE_LINKLOCAL
+#define IN6_IS_SCOPE_LINKLOCAL(a) \
+ ((IN6_IS_ADDR_LINKLOCAL(a)) || \
+ (IN6_IS_ADDR_MC_LINKLOCAL(a)))
+#endif /* IN6_IS_SCOPE_LINKLOCAL */
+
+struct flags {
+ u_long debug : 1;
+ u_long fg : 1;
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+ u_long policy : 1;
+#else /* IPSEC_POLICY_IPSEC */
+ u_long auth : 1;
+ u_long encrypt : 1;
+#endif /* IPSEC_POLICY_IPSEC */
+#endif /*IPSEC*/
+};
+
+struct msghdr sndmhdr;
+struct msghdr rcvmhdr;
+struct sockaddr_in6 from;
+struct sockaddr_in6 sin6_ll_allrouters;
+
+int s4, s6;
+int with_v4dest, with_v6dest;
+struct in6_addr prefix; /* ADHOC */
+int prefixlen = 64; /* ADHOC */
+
+extern int parse(FILE **);
+
+static void show_usage(void);
+static void init_sin6(struct sockaddr_in6 *, const char *);
+#if 0
+static void join_multi(const char *);
+#endif
+static void init_globals(void);
+static void config(FILE **);
+#ifdef IPSEC_POLICY_IPSEC
+static void sock6_open(struct flags *, char *);
+static void sock4_open(struct flags *, char *);
+#else
+static void sock6_open(struct flags *);
+static void sock4_open(struct flags *);
+#endif
+static void rrenum_output(struct payload_list *, struct dst_list *);
+static void rrenum_snd_eachdst(struct payload_list *);
+#if 0
+static void rrenum_snd_fullsequence(void);
+#endif
+static void rrenum_input(int);
+int main(int, char *[]);
+
+
+/* Print usage. Don't call this after daemonized. */
+static void
+show_usage()
+{
+ fprintf(stderr, "usage: rrenumd [-c conf_file|-s] [-df"
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+ "] [-P policy"
+#else /* IPSEC_POLICY_IPSEC */
+ "AE"
+#endif /* IPSEC_POLICY_IPSEC */
+#endif /* IPSEC */
+ "]\n");
+ exit(1);
+}
+
+static void
+init_sin6(struct sockaddr_in6 *sin6, const char *addr_ascii)
+{
+ memset(sin6, 0, sizeof(*sin6));
+ sin6->sin6_len = sizeof(*sin6);
+ sin6->sin6_family = AF_INET6;
+ if (inet_pton(AF_INET6, addr_ascii, &sin6->sin6_addr) != 1)
+ ; /* XXX do something */
+}
+
+#if 0 /* XXX: not necessary ?? */
+static void
+join_multi(const char *addrname)
+{
+ struct ipv6_mreq mreq;
+
+ if (inet_pton(AF_INET6, addrname, &mreq.ipv6mr_multiaddr.s6_addr)
+ != 1) {
+ syslog(LOG_ERR, "<%s> inet_pton failed(library bug?)",
+ __func__);
+ exit(1);
+ }
+ /* ADHOC: currently join only one */
+ {
+ if ((mreq.ipv6mr_interface = if_nametoindex(ifname)) == 0) {
+ syslog(LOG_ERR, "<%s> ifname %s should be invalid: %s",
+ __func__, ifname, strerror(errno));
+ exit(1);
+ }
+ if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ &mreq,
+ sizeof(mreq)) < 0) {
+ syslog(LOG_ERR, "<%s> IPV6_JOIN_GROUP on %s: %s",
+ __func__, ifname, strerror(errno));
+ exit(1);
+ }
+ }
+}
+#endif
+
+static void
+init_globals()
+{
+ static struct iovec rcviov;
+ static u_char rprdata[4500]; /* maximal MTU of connected links */
+ static u_char *rcvcmsgbuf = NULL;
+ static u_char *sndcmsgbuf = NULL;
+ int sndcmsglen, rcvcmsglen;
+
+ /* init ll_allrouters */
+ init_sin6(&sin6_ll_allrouters, LL_ALLROUTERS);
+
+ /* initialize msghdr for receiving packets */
+ rcviov.iov_base = (caddr_t)rprdata;
+ rcviov.iov_len = sizeof(rprdata);
+ rcvmhdr.msg_namelen = sizeof(struct sockaddr_in6);
+ rcvmhdr.msg_iov = &rcviov;
+ rcvmhdr.msg_iovlen = 1;
+ rcvcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+ CMSG_SPACE(sizeof(int));
+ if (rcvcmsgbuf == NULL &&
+ (rcvcmsgbuf = (u_char *)malloc(rcvcmsglen)) == NULL) {
+ syslog(LOG_ERR, "<%s>: malloc failed", __func__);
+ exit(1);
+ }
+ rcvmhdr.msg_control = (caddr_t)rcvcmsgbuf;
+ rcvmhdr.msg_controllen = rcvcmsglen;
+
+ /* initialize msghdr for sending packets */
+ sndmhdr.msg_namelen = sizeof(struct sockaddr_in6);
+ sndmhdr.msg_iovlen = 1;
+ sndcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+ CMSG_SPACE(sizeof(int));
+ if (sndcmsgbuf == NULL &&
+ (sndcmsgbuf = (u_char *)malloc(sndcmsglen)) == NULL) {
+ syslog(LOG_ERR, "<%s>: malloc failed", __func__);
+ exit(1);
+ }
+ sndmhdr.msg_control = (caddr_t)sndcmsgbuf;
+ sndmhdr.msg_controllen = sndcmsglen;
+}
+
+static void
+config(FILE **fpp)
+{
+ struct payload_list *pl;
+ struct iovec *iov;
+ struct icmp6_router_renum *irr;
+ struct rr_pco_match *rpm;
+
+ if (parse(fpp) < 0) {
+ syslog(LOG_ERR, "<%s> parse failed", __func__);
+ exit(1);
+ }
+
+ /* initialize fields not configured by parser */
+ for (pl = pl_head; pl; pl = pl->pl_next) {
+ iov = (struct iovec *)&pl->pl_sndiov;
+ irr = (struct icmp6_router_renum *)&pl->pl_irr;
+ rpm = (struct rr_pco_match *)&pl->pl_rpm;
+
+ irr->rr_type = ICMP6_ROUTER_RENUMBERING;
+ irr->rr_code = 0;
+ /*
+ * now we don't support multiple PCOs in a rr message.
+ * so segment number is not supported.
+ */
+ /* TODO: rr flags config in parser */
+ irr->rr_flags |= ICMP6_RR_FLAGS_SPECSITE;
+ /* TODO: max delay config in parser */
+
+ /*
+ * means only 1 use_prefix is contained as router-renum-05.txt.
+ * now we don't support multiple PCOs in a rr message,
+ * nor multiple use_prefix in one PCO.
+ */
+ rpm->rpm_len = 4*1 +3;
+ rpm->rpm_ordinal = 0;
+ iov->iov_base = (caddr_t)irr;
+ iov->iov_len = sizeof(struct icmp6_router_renum)
+ + sizeof(struct rr_pco_match)
+ + sizeof(struct rr_pco_use);
+ }
+}
+
+static void
+sock6_open(struct flags *flags
+#ifdef IPSEC_POLICY_IPSEC
+ , char *policy
+#endif /* IPSEC_POLICY_IPSEC */
+ )
+{
+ struct icmp6_filter filt;
+ int on;
+#ifdef IPSEC
+#ifndef IPSEC_POLICY_IPSEC
+ int optval;
+#endif
+#endif
+
+ if (with_v6dest == 0)
+ return;
+ if (with_v6dest &&
+ (s6 = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) {
+ syslog(LOG_ERR, "<%s> socket(v6): %s", __func__,
+ strerror(errno));
+ exit(1);
+ }
+
+ /*
+ * join all routers multicast addresses.
+ */
+#if 0 /* XXX: not necessary ?? */
+ join_multi(LL_ALLROUTERS);
+ join_multi(SL_ALLROUTERS);
+#endif
+
+ /* set icmpv6 filter */
+ ICMP6_FILTER_SETBLOCKALL(&filt);
+ ICMP6_FILTER_SETPASS(ICMP6_ROUTER_RENUMBERING, &filt);
+ if (setsockopt(s6, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
+ sizeof(filt)) < 0) {
+ syslog(LOG_ERR, "<%s> IICMP6_FILTER: %s",
+ __func__, strerror(errno));
+ exit(1);
+ }
+
+ /* specify to tell receiving interface */
+ on = 1;
+ if (setsockopt(s6, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
+ sizeof(on)) < 0) {
+ syslog(LOG_ERR, "<%s> IPV6_RECVPKTINFO: %s",
+ __func__, strerror(errno));
+ exit(1);
+ }
+
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+ if (flags->policy) {
+ char *buf;
+ buf = ipsec_set_policy(policy, strlen(policy));
+ if (buf == NULL)
+ errx(1, "%s", ipsec_strerror());
+ /* XXX should handle in/out bound policy. */
+ if (setsockopt(s6, IPPROTO_IPV6, IPV6_IPSEC_POLICY,
+ buf, ipsec_get_policylen(buf)) < 0)
+ err(1, "setsockopt(IPV6_IPSEC_POLICY)");
+ free(buf);
+ }
+#else /* IPSEC_POLICY_IPSEC */
+ if (flags->auth) {
+ optval = IPSEC_LEVEL_REQUIRE;
+ if (setsockopt(s6, IPPROTO_IPV6, IPV6_AUTH_TRANS_LEVEL,
+ &optval, sizeof(optval)) == -1) {
+ syslog(LOG_ERR, "<%s> IPV6_AUTH_TRANS_LEVEL: %s",
+ __func__, strerror(errno));
+ exit(1);
+ }
+ }
+ if (flags->encrypt) {
+ optval = IPSEC_LEVEL_REQUIRE;
+ if (setsockopt(s6, IPPROTO_IPV6, IPV6_ESP_TRANS_LEVEL,
+ &optval, sizeof(optval)) == -1) {
+ syslog(LOG_ERR, "<%s> IPV6_ESP_TRANS_LEVEL: %s",
+ __func__, strerror(errno));
+ exit(1);
+ }
+ }
+#endif /* IPSEC_POLICY_IPSEC */
+#endif /* IPSEC */
+
+ return;
+}
+
+static void
+sock4_open(struct flags *flags
+#ifdef IPSEC_POLICY_IPSEC
+ , char *policy
+#endif /* IPSEC_POLICY_IPSEC */
+ )
+{
+#ifdef IPSEC
+#ifndef IPSEC_POLICY_IPSEC
+ int optval;
+#endif
+#endif
+
+ if (with_v4dest == 0)
+ return;
+ if ((s4 = socket(AF_INET, SOCK_RAW, IPPROTO_ICMPV6)) < 0) {
+ syslog(LOG_ERR, "<%s> socket(v4): %s", __func__,
+ strerror(errno));
+ exit(1);
+ }
+
+#if 0 /* XXX: not necessary ?? */
+ /*
+ * join all routers multicast addresses.
+ */
+ some_join_function();
+#endif
+
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+ if (flags->policy) {
+ char *buf;
+ buf = ipsec_set_policy(policy, strlen(policy));
+ if (buf == NULL)
+ errx(1, "%s", ipsec_strerror());
+ /* XXX should handle in/out bound policy. */
+ if (setsockopt(s4, IPPROTO_IP, IP_IPSEC_POLICY,
+ buf, ipsec_get_policylen(buf)) < 0)
+ err(1, "setsockopt(IP_IPSEC_POLICY)");
+ free(buf);
+ }
+#else /* IPSEC_POLICY_IPSEC */
+ if (flags->auth) {
+ optval = IPSEC_LEVEL_REQUIRE;
+ if (setsockopt(s4, IPPROTO_IP, IP_AUTH_TRANS_LEVEL,
+ &optval, sizeof(optval)) == -1) {
+ syslog(LOG_ERR, "<%s> IP_AUTH_TRANS_LEVEL: %s",
+ __func__, strerror(errno));
+ exit(1);
+ }
+ }
+ if (flags->encrypt) {
+ optval = IPSEC_LEVEL_REQUIRE;
+ if (setsockopt(s4, IPPROTO_IP, IP_ESP_TRANS_LEVEL,
+ &optval, sizeof(optval)) == -1) {
+ syslog(LOG_ERR, "<%s> IP_ESP_TRANS_LEVEL: %s",
+ __func__, strerror(errno));
+ exit(1);
+ }
+ }
+#endif /* IPSEC_POLICY_IPSEC */
+#endif /* IPSEC */
+
+ return;
+}
+
+static void
+rrenum_output(struct payload_list *pl, struct dst_list *dl)
+{
+ int i, msglen = 0;
+ struct cmsghdr *cm;
+ struct in6_pktinfo *pi;
+ struct sockaddr_in6 *sin6 = NULL;
+
+ sndmhdr.msg_name = (caddr_t)dl->dl_dst;
+ if (dl->dl_dst->sa_family == AF_INET6)
+ sin6 = (struct sockaddr_in6 *)dl->dl_dst;
+
+ if (sin6 != NULL &&
+ IN6_IS_ADDR_MULTICAST(&sin6->sin6_addr)) {
+ int hoplimit = RR_MCHLIM_DEFAULT;
+
+ cm = CMSG_FIRSTHDR(&sndmhdr);
+ /* specify the outgoing interface */
+ cm->cmsg_level = IPPROTO_IPV6;
+ cm->cmsg_type = IPV6_PKTINFO;
+ cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+ pi = (struct in6_pktinfo *)CMSG_DATA(cm);
+ memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/
+ pi->ipi6_ifindex = sin6->sin6_scope_id;
+ msglen += CMSG_LEN(sizeof(struct in6_pktinfo));
+
+ /* specify the hop limit of the packet if dest is link local */
+ /* not defined by router-renum-05.txt, but maybe its OK */
+ cm = CMSG_NXTHDR(&sndmhdr, cm);
+ cm->cmsg_level = IPPROTO_IPV6;
+ cm->cmsg_type = IPV6_HOPLIMIT;
+ cm->cmsg_len = CMSG_LEN(sizeof(int));
+ memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int));
+ msglen += CMSG_LEN(sizeof(int));
+ }
+ sndmhdr.msg_controllen = msglen;
+ if (sndmhdr.msg_controllen == 0)
+ sndmhdr.msg_control = 0;
+
+ sndmhdr.msg_iov = &pl->pl_sndiov;
+ i = sendmsg(dl->dl_dst->sa_family == AF_INET ? s4 : s6, &sndmhdr, 0);
+
+ if (i < 0 || i != sndmhdr.msg_iov->iov_len)
+ syslog(LOG_ERR, "<%s> sendmsg: %s", __func__,
+ strerror(errno));
+}
+
+static void
+rrenum_snd_eachdst(struct payload_list *pl)
+{
+ struct dst_list *dl;
+
+ for (dl = dl_head; dl; dl = dl->dl_next) {
+ rrenum_output(pl, dl);
+ }
+}
+
+#if 0
+static void
+rrenum_snd_fullsequence()
+{
+ struct payload_list *pl;
+
+ for (pl = pl_head; pl; pl = pl->pl_next) {
+ rrenum_snd_eachdst(pl);
+ }
+}
+#endif
+
+static void
+rrenum_input(int s)
+{
+ int i;
+ struct icmp6_router_renum *rr;
+
+ /* get message */
+ if ((i = recvmsg(s, &rcvmhdr, 0)) < 0) {
+ syslog(LOG_ERR, "<%s> recvmsg: %s", __func__,
+ strerror(errno));
+ return;
+ }
+ if (s == s4)
+ i -= sizeof(struct ip);
+ if (i < sizeof(struct icmp6_router_renum)) {
+ syslog(LOG_ERR, "<%s> packet size(%d) is too short",
+ __func__, i);
+ return;
+ }
+ if (s == s4) {
+ struct ip *ip = (struct ip *)rcvmhdr.msg_iov->iov_base;
+
+ rr = (struct icmp6_router_renum *)(ip + 1);
+ } else /* s == s6 */
+ rr = (struct icmp6_router_renum *)rcvmhdr.msg_iov->iov_base;
+
+ switch(rr->rr_code) {
+ case ICMP6_ROUTER_RENUMBERING_COMMAND:
+ /* COMMAND will be processed by rtadvd */
+ break;
+ case ICMP6_ROUTER_RENUMBERING_RESULT:
+ /* TODO: receiving result message */
+ break;
+ default:
+ syslog(LOG_ERR, "<%s> received unknown code %d",
+ __func__, rr->rr_code);
+ break;
+ }
+}
+
+int
+main(int argc, char *argv[])
+{
+ FILE *fp = stdin;
+ fd_set fdset;
+ struct timeval timeout;
+ int ch, i, maxfd = 0, send_counter = 0;
+ struct flags flags;
+ struct payload_list *pl;
+#ifdef IPSEC_POLICY_IPSEC
+ char *policy = NULL;
+#endif
+
+ memset(&flags, 0, sizeof(flags));
+ openlog("rrenumd", LOG_PID, LOG_DAEMON);
+
+ /* get options */
+ while ((ch = getopt(argc, argv, "c:sdf"
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+ "P:"
+#else /* IPSEC_POLICY_IPSEC */
+ "AE"
+#endif /* IPSEC_POLICY_IPSEC */
+#endif /* IPSEC */
+ )) != -1){
+ switch (ch) {
+ case 'c':
+ if((fp = fopen(optarg, "r")) == NULL) {
+ syslog(LOG_ERR,
+ "<%s> config file %s open failed",
+ __func__, optarg);
+ exit(1);
+ }
+ break;
+ case 's':
+ fp = stdin;
+ break;
+ case 'd':
+ flags.debug = 1;
+ break;
+ case 'f':
+ flags.fg = 1;
+ break;
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+ case 'P':
+ flags.policy = 1;
+ policy = strdup(optarg);
+ break;
+#else /* IPSEC_POLICY_IPSEC */
+ case 'A':
+ flags.auth = 1;
+ break;
+ case 'E':
+ flags.encrypt = 1;
+ break;
+#endif /* IPSEC_POLICY_IPSEC */
+#endif /*IPSEC*/
+ default:
+ show_usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* set log level */
+ if (flags.debug == 0)
+ (void)setlogmask(LOG_UPTO(LOG_ERR));
+ if (flags.debug == 1)
+ (void)setlogmask(LOG_UPTO(LOG_INFO));
+
+ /* init global variables */
+ init_globals();
+
+ config(&fp);
+
+ sock6_open(&flags
+#ifdef IPSEC_POLICY_IPSEC
+ , policy
+#endif /* IPSEC_POLICY_IPSEC */
+ );
+ sock4_open(&flags
+#ifdef IPSEC_POLICY_IPSEC
+ , policy
+#endif /* IPSEC_POLICY_IPSEC */
+ );
+
+ if (!flags.fg)
+ daemon(0, 0);
+
+ FD_ZERO(&fdset);
+ if (with_v6dest) {
+ FD_SET(s6, &fdset);
+ if (s6 > maxfd)
+ maxfd = s6;
+ }
+ if (with_v4dest) {
+ FD_SET(s4, &fdset);
+ if (s4 > maxfd)
+ maxfd = s4;
+ }
+
+ /* ADHOC: timeout each 30seconds */
+ memset(&timeout, 0, sizeof(timeout));
+
+ /* init temporary payload_list and send_counter*/
+ pl = pl_head;
+ send_counter = retry + 1;
+ while (1) {
+ struct fd_set select_fd = fdset; /* reinitialize */
+
+ if ((i = select(maxfd + 1, &select_fd, NULL, NULL,
+ &timeout)) < 0){
+ syslog(LOG_ERR, "<%s> select: %s",
+ __func__, strerror(errno));
+ continue;
+ }
+ if (i == 0) { /* timeout */
+ if (pl == NULL)
+ exit(0);
+ rrenum_snd_eachdst(pl);
+ send_counter--;
+ timeout.tv_sec = 30;
+ if (send_counter == 0) {
+ timeout.tv_sec = 0;
+ pl = pl->pl_next;
+ send_counter = retry + 1;
+ }
+ }
+ if (FD_ISSET(s4, &select_fd))
+ rrenum_input(s4);
+ if (FD_ISSET(s6, &select_fd))
+ rrenum_input(s6);
+ }
+}
diff --git a/usr.sbin/rrenumd/rrenumd.conf.5 b/usr.sbin/rrenumd/rrenumd.conf.5
new file mode 100644
index 0000000..9ce2306
--- /dev/null
+++ b/usr.sbin/rrenumd/rrenumd.conf.5
@@ -0,0 +1,369 @@
+.\" $KAME: rrenumd.conf.5,v 1.8 2001/02/06 02:17:23 jinmei 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 November 5, 1998
+.Dt RRENUMD.CONF 5
+.Os
+.Sh NAME
+.\"
+.Nm rrenumd.conf
+.Nd configuration file for router renumbering daemon
+.\"
+.Sh DESCRIPTION
+The rrenumd config file describes how the router renumbering packet
+must be constructed and to which destinations it should be sent.
+This file consists of a sequence of statements terminated by a semi-colon (`;').
+Statements are composed of tokens
+separated by white space, which can be any combination of blanks, tabs
+and newlines.
+This structure simplifies identification of
+the parts of the configuration associated with each other.
+Lines beginning with
+.Ql #
+are comments.
+.\"
+.Sh Meta Syntax
+Keywords and special characters that the parser expects exactly are
+displayed using the
+.Ic bold
+font.
+Parameters are specifying with
+.Ar underline .
+Parameters shown in
+square brackets (`[' and `]') are used to show optional
+keywords and parameters.
+The vertical bar (`|') is used to indicate
+between a choice of optional parameters.
+Curly braces (`{' and
+`}') are used to group keywords and parameters when necessary.
+.\"
+.Sh Interface specification
+There are some statements that may or have to specify interface.
+Interfaces are specified in the form of "name unit", such as
+.Ar lo0
+and
+.Ar ep1 .
+.\"
+.Sh Configuration Statements
+.Bl -tag -width Ds
+.\"
+.It Ic debug on|off ;
+Enables configuration file parser debugging.
+If
+.Ic on
+is specified,
+then debugging is enabled,
+If
+.Ic off
+is specified,
+then debugging is disabled.
+It is disabled by default.
+.\"
+.It Ic dest Ar dest-list Op Ar retrycmd ;
+Specifies destinations to which router renumbering messages should be
+sent.
+.Ar dest-list
+can be any combination of single or multiple numerical IPv6 addrs,
+or Full Qualified Domain Names.
+.Ar retrycmd
+has following syntax.
+.\"
+.Bl -tag -width Ds
+.It Ic retry Ar retry-num
+.Ar retry-num
+specifies how many router renumbering messages are sent repeatedly.
+.El
+.It Op Ic add|change|setglobal
+.Cm match-prefix Ar match-prefix-val
+.Bk -words
+.Op /match-prefix-len
+.Ek
+.Bk -words
+.Op Cm maxlen Ar maxlen-val
+.Ek
+.Bk -words
+.Op Cm minlen Ar minlen-val
+.Ek
+.Bk -words
+.Op Cm use-prefix Ar use-prefix-val
+.Ek
+.Bk -words
+.Op /use-prefix-len
+.Ek
+.Bk -words
+.Op Cm keeplen Ar keeplen-val
+.Ek
+.Bk -words
+.Op Ar use-prefix-values ;
+.Ek
+.Pp
+Specifies contents of sending router renumbering message with seqnum 0.
+If
+.Cm add|change|setglobal
+is not specified, then
+.Cm add
+is assumed.
+.Ar use-prefix-values
+has following syntax.
+.Pp
+{
+.Op Cm vltime Ar vltime-val
+.Bk -words
+.Op Cm pltime Ar pltime-val
+.Ek
+.Bk -words
+.Op Cm raf_onlink Cm on|off
+.Ek
+.Bk -words
+.Op Cm raf_auto Cm on|off
+.Ek
+.Bk -words
+.Op Cm rrf_decrprefd Cm on|off
+.Ek
+.Bk -words
+.Op Cm rrf_decrvalid Cm on|off
+.Ek
+}
+.Pp
+Each value has following meaning.
+.Pp
+.Bl -tag -width Ds -compact
+.It Cm match-prefix Ar match-prefix-val Op /match-prefix-len
+Specify
+.Ar match-prefix-val
+that is used for matching with preassigned prefixes to which
+.Cm add|change|setglobal
+command should be applied.
+.Ar /match-prefix-len
+Specify the starting part of
+.Ar match-prefix-val
+to be used for matching with preassigned prefixes, as decimal bit number.
+.It Cm maxlen Ar maxlen-val
+Specify the maximum length of prefixes which is allowed to be
+matched to
+.Ar match-prefix-val ,
+as decimal bit number.
+.It Cm minlen Ar minlen-val
+Specify the minimum length of prefixes which is allowed to be matched to
+.Ar match-prefix-val ,
+as decimal bit number.
+.It Cm use-prefix Ar use-prefix-val Op /usr-prefix-len
+Specify
+.Ar use-prefix-val
+that is used for prefixes to be added on
+.Cm add|change|setglobal
+command.
+.Ar /use-prefix-len
+Specify the starting part of
+.Ar use-prefix-val
+copied to the starting part of prefixes to be added on
+.Cm add|change|setglobal
+command, as decimal bit number.
+.It Cm keeplen Ar keeplen-val
+Specify the medium part of
+.Ar use-prefix-val
+just next to the starting part specified by
+.Ar use-prefix-len ,
+as decimal bit number.
+Contiguous bits part in the same bit position of an existent prefix
+matched with
+.Ar match-prefix-val
+is copied to the same bit position of prefixes to be added.
+.It Cm vltime Ar vmtime-val
+Assign an
+.Ar time
+as prefix valid life time for a prefix to be added.
+Valid value for
+.Ar time
+is decimal seconds number or special format as "d00h00m00s00",
+where 00 can take any decimal number, and "d" means days, "h" means hours,
+"m" means minutes, "s" means seconds.
+And alternatively, special keyword
+"infinity" can be also be specified.
+.It Cm pltime Ar pltime-val
+Assign an
+.Ar time
+as prefix preferred life time for a prefix to be added.
+Valid value for
+.Ar time
+is same as for
+.Ar vltime-val .
+.It Cm raf_onlink Cm on|off
+Let the prefix to be added to have on-link or off-link nature
+for the assigned interface.
+If
+.Cm on
+is specified, the prefix have on-link nature
+(e.g.\& the prefix
+belong to the link).
+If
+.Cm off
+is specified, the prefix have off-link nature
+(e.g.\& the
+prefix does not belong to the link).
+.It Cm raf_auto Cm on|off
+Enable or disable the autonomous address auto configuration
+for the prefix to be added.
+If
+.Cm on
+is specified, autonomous address auto configuration is
+enabled.
+If
+.Cm off
+is specified, it is disabled.
+.It Cm rrf_decrprefd Cm on|off
+Enable or disable the decrementation of the pltime.
+If
+.Cm on
+is specified, decrementation of the pltime is enabled.
+If
+.Cm off
+is specified, decrementation of the pltime is disabled.
+.It Cm rrf_decrvalid Cm on|off
+Enable or disable the decrementation of the vltime.
+If
+.Cm on
+is specified, decrementation of the vltime is enabled.
+If
+.Cm off
+is specified, decrementation of the vltime is disabled.
+.El
+.\"
+.It seqnum Ar seqnum-val { Ar rrenum-cmd } ;
+Specifies contents of sending router renumbering message with some
+specific seqnum.
+Multiple of this statement can be specified if they
+have different
+.Ar seqnum-val
+each other.
+.Ar rrenum-cmd
+has just same syntax with above add|change|setglobal statement.
+.El
+.\"
+.Sh EXAMPLES
+For each configuration file example shown below, we suppose
+every IPv6 subnet has its own prefix beginning with
+fec0:0:0::/48 and with its own subnet number
+(in this case,
+subnet number is 7th and 8th octet value of the prefix).
+.Pp
+If you want to assign prefixes beginning with 3ffe:501:ffff::/48
+to each subnet, then following configuration will be enough,
+if each of your routers supports IPv6 multicast forwarding.
+The subnet number of the existing fec0:0:0::/48 prefix and the
+newly assigned 3ffe:501:ffff::/48 prefix will be same.
+.\"
+.Bd -literal -offset indent
+dest ff05::2;
+
+add match-prefix fec0:0:0:: /48 use-prefix 3ffe:501:ffff:: /48 keeplen 16;
+.Ed
+.Pp
+.\"
+If your routers do not support IPv6 multicast forwarding,
+you will need to specify each destination at
+.Cm dest
+command.
+.\"
+.Bd -literal -offset indent
+dest fec0:0:0:1:260:8ff:fe24:fb3a fec0:0:0:2:200:eff:fe2e:dfe1 fec0:0:0:3:5254:ff:fedc:5217;
+
+add match-prefix fec0:0:0:: /48 use-prefix 3ffe:501:ffff:: /48 keeplen 16;
+.Ed
+.Pp
+.\"
+If you are going to do renumbering, then following procedure will be natural.
+.Bl -enum -offset indent
+.It
+Assign a new prefix.
+.It
+Set old prefix lifetimes to some appropriate transition
+period.
+In the following example we use 1 week for valid
+lifetime, and 0 for preferred lifetime.
+Also, enable old prefix lifetime expiration
+(By default, it is static and does not expire).
+.It
+After the transition period, old prefixes should become
+invalid, and may have been deleted.
+To make sure that they are deleted, send new router
+renumbering message, which specifies old prefixes as match
+prefix, and no use prefix.
+.El
+.Pp
+.\"
+The following configuration file will do 1 and 2.
+.\"
+.Bd -literal -offset indent
+dest ff05::2;
+
+seqnum 0 {
+ add match-prefix fec0:0:0:: /48 use-prefix 3ffe:501:fffe:: /48 keeplen 16;
+ };
+
+seqnum 1 {
+ change match-prefix 3ffe:501:ffff:: /48 use-prefix 3ffe:501:ffff:: /48 keeplen 16 vltime d7 pltime 0 rrf_decrvalid on rrf_decrprefd on;
+ };
+.Ed
+.Pp
+.\"
+And the following configuration file will do 3
+(should be
+used for the router renumbering message to be sent 1 week
+afterward).
+.\"
+.Bd -literal -offset indent
+dest ff05::2;
+
+change match-prefix 3ffe:501:ffff:: /48;
+.Ed
+.Pp
+.\"
+In the above example, only
+.Cm add
+and
+.Cm change
+commands are used, and there is no example for
+.Cm setglobal
+command.
+.Cm setglobal
+command is almost same with
+.Cm change
+command except that it deletes all pre-defined IPv6 global address.
+.Sh SEE ALSO
+.Xr prefix 8 ,
+.Xr rrenumd 8
+.Sh HISTORY
+The
+.Nm
+configuration file was first appeared in KAME IPv6 protocol stack kit.
+.\" .Sh BUGS
+.\" (to be written)
diff --git a/usr.sbin/rrenumd/rrenumd.h b/usr.sbin/rrenumd/rrenumd.h
new file mode 100644
index 0000000..df0280b
--- /dev/null
+++ b/usr.sbin/rrenumd/rrenumd.h
@@ -0,0 +1,60 @@
+/* $KAME: rrenumd.h,v 1.2 2000/07/03 02:54:09 itojun Exp $ */
+
+/*
+ * Copyright (C) 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by WIDE Project and
+ * its contributors.
+ * 4. 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$
+ */
+
+struct dst_list {
+ struct dst_list * dl_next;
+ struct sockaddr * dl_dst;
+};
+
+extern struct dst_list *dl_head;
+
+struct payload_list {
+ struct payload_list * pl_next;
+ struct iovec pl_sndiov;
+ struct icmp6_router_renum
+ pl_irr;
+ struct rr_pco_match pl_rpm;
+ /* currently, support only 1 rr_pco_use field per packet */
+ struct rr_pco_use pl_rpu;
+};
+
+extern struct payload_list *pl_head;
+extern u_int retry;
+extern int with_v4dest, with_v6dest;
+
+#define DEF_VLTIME 2592000
+#define DEF_PLTIME 604800
diff --git a/usr.sbin/rtadvd/Makefile b/usr.sbin/rtadvd/Makefile
new file mode 100644
index 0000000..f7e2021
--- /dev/null
+++ b/usr.sbin/rtadvd/Makefile
@@ -0,0 +1,23 @@
+# 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$
+
+PROG= rtadvd
+MAN= rtadvd.conf.5 rtadvd.8
+SRCS= rtadvd.c rrenum.c advcap.c if.c config.c timer.c dump.c
+
+CFLAGS+= -DHAVE_ARC4RANDOM -DHAVE_POLL_H -DROUTEINFO
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rtadvd/advcap.c b/usr.sbin/rtadvd/advcap.c
new file mode 100644
index 0000000..792ea27
--- /dev/null
+++ b/usr.sbin/rtadvd/advcap.c
@@ -0,0 +1,451 @@
+/* $FreeBSD$ */
+/* $KAME: advcap.c,v 1.11 2003/05/19 09:46:50 keiichi Exp $ */
+
+/*
+ * Copyright (c) 1983 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.
+ */
+
+/*
+ * remcap - routines for dealing with the remote host data base
+ *
+ * derived from termcap
+ */
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <errno.h>
+#include <string.h>
+#include "pathnames.h"
+
+#ifndef BUFSIZ
+#define BUFSIZ 1024
+#endif
+#define MAXHOP 32 /* max number of tc= indirections */
+
+#define tgetent agetent
+#define tnchktc anchktc
+#define tnamatch anamatch
+#define tgetnum agetnum
+#define tgetflag agetflag
+#define tgetstr agetstr
+
+#if 0
+#define V_TERMCAP "REMOTE"
+#define V_TERM "HOST"
+#endif
+
+char *RM;
+
+/*
+ * termcap - routines for dealing with the terminal capability data base
+ *
+ * BUG: Should use a "last" pointer in tbuf, so that searching
+ * for capabilities alphabetically would not be a n**2/2
+ * process when large numbers of capabilities are given.
+ * Note: If we add a last pointer now we will screw up the
+ * tc capability. We really should compile termcap.
+ *
+ * Essentially all the work here is scanning and decoding escapes
+ * in string capabilities. We don't use stdio because the editor
+ * doesn't, and because living w/o it is not hard.
+ */
+
+static char *tbuf;
+static int hopcount; /* detect infinite loops in termcap, init 0 */
+
+static char *remotefile;
+
+extern char *conffile;
+
+int tgetent(char *, char *);
+int getent(char *, char *, char *);
+int tnchktc(void);
+int tnamatch(char *);
+static char *tskip(char *);
+int64_t tgetnum(char *);
+int tgetflag(char *);
+char *tgetstr(char *, char **);
+static char *tdecode(char *, char **);
+
+/*
+ * Get an entry for terminal name in buffer bp,
+ * from the termcap file. Parse is very rudimentary;
+ * we just notice escaped newlines.
+ */
+int
+tgetent(bp, name)
+ char *bp, *name;
+{
+ char *cp;
+
+ remotefile = cp = conffile ? conffile : _PATH_RTADVDCONF;
+ return (getent(bp, name, cp));
+}
+
+int
+getent(bp, name, cp)
+ char *bp, *name, *cp;
+{
+ int c;
+ int i = 0, cnt = 0;
+ char ibuf[BUFSIZ];
+ int tf;
+
+ tbuf = bp;
+ tf = 0;
+ /*
+ * TERMCAP can have one of two things in it. It can be the
+ * name of a file to use instead of /etc/termcap. In this
+ * case it better start with a "/". Or it can be an entry to
+ * use so we don't have to read the file. In this case it
+ * has to already have the newlines crunched out.
+ */
+ if (cp && *cp) {
+ tf = open(RM = cp, O_RDONLY);
+ }
+ if (tf < 0) {
+ syslog(LOG_INFO,
+ "<%s> open: %s", __func__, strerror(errno));
+ return (-2);
+ }
+ for (;;) {
+ cp = bp;
+ for (;;) {
+ if (i == cnt) {
+ cnt = read(tf, ibuf, BUFSIZ);
+ if (cnt <= 0) {
+ close(tf);
+ return (0);
+ }
+ i = 0;
+ }
+ c = ibuf[i++];
+ if (c == '\n') {
+ if (cp > bp && cp[-1] == '\\') {
+ cp--;
+ continue;
+ }
+ break;
+ }
+ if (cp >= bp + BUFSIZ) {
+ write(STDERR_FILENO, "Remcap entry too long\n",
+ 23);
+ break;
+ } else
+ *cp++ = c;
+ }
+ *cp = 0;
+
+ /*
+ * The real work for the match.
+ */
+ if (tnamatch(name)) {
+ close(tf);
+ return (tnchktc());
+ }
+ }
+}
+
+/*
+ * tnchktc: check the last entry, see if it's tc=xxx. If so,
+ * recursively find xxx and append that entry (minus the names)
+ * to take the place of the tc=xxx entry. This allows termcap
+ * entries to say "like an HP2621 but doesn't turn on the labels".
+ * Note that this works because of the left to right scan.
+ */
+int
+tnchktc()
+{
+ char *p, *q;
+ char tcname[16]; /* name of similar terminal */
+ char tcbuf[BUFSIZ];
+ char *holdtbuf = tbuf;
+ int l;
+
+ p = tbuf + strlen(tbuf) - 2; /* before the last colon */
+ while (*--p != ':')
+ if (p < tbuf) {
+ write(STDERR_FILENO, "Bad remcap entry\n", 18);
+ return (0);
+ }
+ p++;
+ /* p now points to beginning of last field */
+ if (p[0] != 't' || p[1] != 'c')
+ return (1);
+ strlcpy(tcname, p + 3, sizeof tcname);
+ q = tcname;
+ while (*q && *q != ':')
+ q++;
+ *q = 0;
+ if (++hopcount > MAXHOP) {
+ write(STDERR_FILENO, "Infinite tc= loop\n", 18);
+ return (0);
+ }
+ if (getent(tcbuf, tcname, remotefile) != 1) {
+ return (0);
+ }
+ for (q = tcbuf; *q++ != ':'; )
+ ;
+ l = p - holdtbuf + strlen(q);
+ if (l > BUFSIZ) {
+ write(STDERR_FILENO, "Remcap entry too long\n", 23);
+ q[BUFSIZ - (p-holdtbuf)] = 0;
+ }
+ strcpy(p, q);
+ tbuf = holdtbuf;
+ return (1);
+}
+
+/*
+ * Tnamatch deals with name matching. The first field of the termcap
+ * entry is a sequence of names separated by |'s, so we compare
+ * against each such name. The normal : terminator after the last
+ * name (before the first field) stops us.
+ */
+int
+tnamatch(np)
+ char *np;
+{
+ char *Np, *Bp;
+
+ Bp = tbuf;
+ if (*Bp == '#')
+ return (0);
+ for (;;) {
+ for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
+ continue;
+ if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
+ return (1);
+ while (*Bp && *Bp != ':' && *Bp != '|')
+ Bp++;
+ if (*Bp == 0 || *Bp == ':')
+ return (0);
+ Bp++;
+ }
+}
+
+/*
+ * Skip to the next field. Notice that this is very dumb, not
+ * knowing about \: escapes or any such. If necessary, :'s can be put
+ * into the termcap file in octal.
+ */
+static char *
+tskip(bp)
+ char *bp;
+{
+ int dquote;
+
+ dquote = 0;
+ while (*bp) {
+ switch (*bp) {
+ case ':':
+ if (!dquote)
+ goto breakbreak;
+ else
+ bp++;
+ break;
+ case '\\':
+ bp++;
+ if (isdigit(*bp)) {
+ while (isdigit(*bp++))
+ ;
+ } else
+ bp++;
+ case '"':
+ dquote = (dquote ? 1 : 0);
+ bp++;
+ break;
+ default:
+ bp++;
+ break;
+ }
+ }
+breakbreak:
+ if (*bp == ':')
+ bp++;
+ return (bp);
+}
+
+/*
+ * Return the (numeric) option id.
+ * Numeric options look like
+ * li#80
+ * i.e. the option string is separated from the numeric value by
+ * a # character. If the option is not found we return -1.
+ * Note that we handle octal numbers beginning with 0.
+ */
+int64_t
+tgetnum(id)
+ char *id;
+{
+ int64_t i;
+ int base;
+ char *bp = tbuf;
+
+ for (;;) {
+ bp = tskip(bp);
+ if (*bp == 0)
+ return (-1);
+ if (strncmp(bp, id, strlen(id)) != 0)
+ continue;
+ bp += strlen(id);
+ if (*bp == '@')
+ return (-1);
+ if (*bp != '#')
+ continue;
+ bp++;
+ base = 10;
+ if (*bp == '0')
+ base = 8;
+ i = 0;
+ while (isdigit(*bp))
+ i *= base, i += *bp++ - '0';
+ return (i);
+ }
+}
+
+/*
+ * Handle a flag option.
+ * Flag options are given "naked", i.e. followed by a : or the end
+ * of the buffer. Return 1 if we find the option, or 0 if it is
+ * not given.
+ */
+int
+tgetflag(id)
+ char *id;
+{
+ char *bp = tbuf;
+
+ for (;;) {
+ bp = tskip(bp);
+ if (!*bp)
+ return (0);
+ if (strncmp(bp, id, strlen(id)) == 0) {
+ bp += strlen(id);
+ if (!*bp || *bp == ':')
+ return (1);
+ else if (*bp == '@')
+ return (0);
+ }
+ }
+}
+
+/*
+ * Get a string valued option.
+ * These are given as
+ * cl=^Z
+ * Much decoding is done on the strings, and the strings are
+ * placed in area, which is a ref parameter which is updated.
+ * No checking on area overflow.
+ */
+char *
+tgetstr(id, area)
+ char *id, **area;
+{
+ char *bp = tbuf;
+
+ for (;;) {
+ bp = tskip(bp);
+ if (!*bp)
+ return (0);
+ if (strncmp(bp, id, strlen(id)) != 0)
+ continue;
+ bp += strlen(id);
+ if (*bp == '@')
+ return (0);
+ if (*bp != '=')
+ continue;
+ bp++;
+ return (tdecode(bp, area));
+ }
+}
+
+/*
+ * Tdecode does the grung work to decode the
+ * string capability escapes.
+ */
+static char *
+tdecode(str, area)
+ char *str;
+ char **area;
+{
+ char *cp;
+ int c;
+ char *dp;
+ int i;
+ char term;
+
+ term = ':';
+ cp = *area;
+again:
+ if (*str == '"') {
+ term = '"';
+ str++;
+ }
+ while ((c = *str++) && c != term) {
+ switch (c) {
+
+ case '^':
+ c = *str++ & 037;
+ break;
+
+ case '\\':
+ dp = "E\033^^\\\\::n\nr\rt\tb\bf\f\"\"";
+ c = *str++;
+nextc:
+ if (*dp++ == c) {
+ c = *dp++;
+ break;
+ }
+ dp++;
+ if (*dp)
+ goto nextc;
+ if (isdigit(c)) {
+ c -= '0', i = 2;
+ do
+ c <<= 3, c |= *str++ - '0';
+ while (--i && isdigit(*str));
+ }
+ break;
+ }
+ *cp++ = c;
+ }
+ if (c == term && term != ':') {
+ term = ':';
+ goto again;
+ }
+ *cp++ = 0;
+ str = *area;
+ *area = cp;
+ return (str);
+}
diff --git a/usr.sbin/rtadvd/advcap.h b/usr.sbin/rtadvd/advcap.h
new file mode 100644
index 0000000..3cc124a
--- /dev/null
+++ b/usr.sbin/rtadvd/advcap.h
@@ -0,0 +1,46 @@
+/* $FreeBSD$ */
+/* $KAME: advcap.h,v 1.5 2003/06/09 05:40:54 t-momose Exp $ */
+
+/*
+ * Copyright (C) 1994,1995 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 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.
+ */
+
+/* Based on Id: termcap.h,v 1.8 1996/09/10 12:42:10 peter Exp */
+
+#ifndef _ADVCAP_H_
+#define _ADVCAP_H_
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+extern int agetent(char *, const char *);
+extern int agetflag(const char *);
+extern int64_t agetnum(const char *);
+extern char *agetstr(const char *, char **);
+
+__END_DECLS
+
+#endif /* _ADVCAP_H_ */
diff --git a/usr.sbin/rtadvd/config.c b/usr.sbin/rtadvd/config.c
new file mode 100644
index 0000000..5eadcc5
--- /dev/null
+++ b/usr.sbin/rtadvd/config.c
@@ -0,0 +1,1080 @@
+/* $FreeBSD$ */
+/* $KAME: config.c,v 1.84 2003/08/05 12:34:23 itojun Exp $ */
+
+/*
+ * Copyright (C) 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.
+ */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/icmp6.h>
+#include <netinet6/nd6.h>
+
+#include <arpa/inet.h>
+
+#include <stdio.h>
+#include <syslog.h>
+#include <errno.h>
+#include <string.h>
+#include <search.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ifaddrs.h>
+
+#include "rtadvd.h"
+#include "advcap.h"
+#include "timer.h"
+#include "if.h"
+#include "config.h"
+
+static time_t prefix_timo = (60 * 120); /* 2 hours.
+ * XXX: should be configurable. */
+extern struct rainfo *ralist;
+
+static struct rtadvd_timer *prefix_timeout(void *);
+static void makeentry(char *, size_t, int, char *);
+static int getinet6sysctl(int);
+
+void
+getconfig(intface)
+ char *intface;
+{
+ int stat, i;
+ char tbuf[BUFSIZ];
+ struct rainfo *tmp;
+ long val;
+ int64_t val64;
+ char buf[BUFSIZ];
+ char *bp = buf;
+ char *addr, *flagstr;
+ static int forwarding = -1;
+
+#define MUSTHAVE(var, cap) \
+ do { \
+ int64_t t; \
+ if ((t = agetnum(cap)) < 0) { \
+ fprintf(stderr, "rtadvd: need %s for interface %s\n", \
+ cap, intface); \
+ exit(1); \
+ } \
+ var = t; \
+ } while (0)
+#define MAYHAVE(var, cap, def) \
+ do { \
+ if ((var = agetnum(cap)) < 0) \
+ var = def; \
+ } while (0)
+
+ if ((stat = agetent(tbuf, intface)) <= 0) {
+ memset(tbuf, 0, sizeof(tbuf));
+ syslog(LOG_INFO,
+ "<%s> %s isn't defined in the configuration file"
+ " or the configuration file doesn't exist."
+ " Treat it as default",
+ __func__, intface);
+ }
+
+ tmp = (struct rainfo *)malloc(sizeof(*ralist));
+ if (tmp == NULL) {
+ syslog(LOG_INFO, "<%s> %s: can't allocate enough memory",
+ __func__, intface);
+ exit(1);
+ }
+ memset(tmp, 0, sizeof(*tmp));
+ tmp->prefix.next = tmp->prefix.prev = &tmp->prefix;
+#ifdef ROUTEINFO
+ tmp->route.next = tmp->route.prev = &tmp->route;
+#endif
+
+ /* check if we are allowed to forward packets (if not determined) */
+ if (forwarding < 0) {
+ if ((forwarding = getinet6sysctl(IPV6CTL_FORWARDING)) < 0)
+ exit(1);
+ }
+
+ /* get interface information */
+ if (agetflag("nolladdr"))
+ tmp->advlinkopt = 0;
+ else
+ tmp->advlinkopt = 1;
+ if (tmp->advlinkopt) {
+ if ((tmp->sdl = if_nametosdl(intface)) == NULL) {
+ syslog(LOG_ERR,
+ "<%s> can't get information of %s",
+ __func__, intface);
+ exit(1);
+ }
+ tmp->ifindex = tmp->sdl->sdl_index;
+ } else
+ tmp->ifindex = if_nametoindex(intface);
+ strncpy(tmp->ifname, intface, sizeof(tmp->ifname));
+ if ((tmp->phymtu = if_getmtu(intface)) == 0) {
+ tmp->phymtu = IPV6_MMTU;
+ syslog(LOG_WARNING,
+ "<%s> can't get interface mtu of %s. Treat as %d",
+ __func__, intface, IPV6_MMTU);
+ }
+
+ /*
+ * set router configuration variables.
+ */
+ MAYHAVE(val, "maxinterval", DEF_MAXRTRADVINTERVAL);
+ if (val < MIN_MAXINTERVAL || val > MAX_MAXINTERVAL) {
+ syslog(LOG_ERR,
+ "<%s> maxinterval (%ld) on %s is invalid "
+ "(must be between %u and %u)", __func__, val,
+ intface, MIN_MAXINTERVAL, MAX_MAXINTERVAL);
+ exit(1);
+ }
+ tmp->maxinterval = (u_int)val;
+ MAYHAVE(val, "mininterval", tmp->maxinterval/3);
+ if (val < MIN_MININTERVAL || val > (tmp->maxinterval * 3) / 4) {
+ syslog(LOG_ERR,
+ "<%s> mininterval (%ld) on %s is invalid "
+ "(must be between %d and %d)",
+ __func__, val, intface, MIN_MININTERVAL,
+ (tmp->maxinterval * 3) / 4);
+ exit(1);
+ }
+ tmp->mininterval = (u_int)val;
+
+ MAYHAVE(val, "chlim", DEF_ADVCURHOPLIMIT);
+ tmp->hoplimit = val & 0xff;
+
+ if ((flagstr = (char *)agetstr("raflags", &bp))) {
+ val = 0;
+ if (strchr(flagstr, 'm'))
+ val |= ND_RA_FLAG_MANAGED;
+ if (strchr(flagstr, 'o'))
+ val |= ND_RA_FLAG_OTHER;
+ if (strchr(flagstr, 'h'))
+ val |= ND_RA_FLAG_RTPREF_HIGH;
+ if (strchr(flagstr, 'l')) {
+ if ((val & ND_RA_FLAG_RTPREF_HIGH)) {
+ syslog(LOG_ERR, "<%s> the \'h\' and \'l\'"
+ " router flags are exclusive", __func__);
+ exit(1);
+ }
+ val |= ND_RA_FLAG_RTPREF_LOW;
+ }
+ } else {
+ MAYHAVE(val, "raflags", 0);
+ }
+ tmp->managedflg = val & ND_RA_FLAG_MANAGED;
+ tmp->otherflg = val & ND_RA_FLAG_OTHER;
+#ifndef ND_RA_FLAG_RTPREF_MASK
+#define ND_RA_FLAG_RTPREF_MASK 0x18 /* 00011000 */
+#define ND_RA_FLAG_RTPREF_RSV 0x10 /* 00010000 */
+#endif
+ tmp->rtpref = val & ND_RA_FLAG_RTPREF_MASK;
+ if (tmp->rtpref == ND_RA_FLAG_RTPREF_RSV) {
+ syslog(LOG_ERR, "<%s> invalid router preference (%02x) on %s",
+ __func__, tmp->rtpref, intface);
+ exit(1);
+ }
+
+ MAYHAVE(val, "rltime", tmp->maxinterval * 3);
+ if (val && (val < tmp->maxinterval || val > MAXROUTERLIFETIME)) {
+ syslog(LOG_ERR,
+ "<%s> router lifetime (%ld) on %s is invalid "
+ "(must be 0 or between %d and %d)",
+ __func__, val, intface,
+ tmp->maxinterval,
+ MAXROUTERLIFETIME);
+ exit(1);
+ }
+ /*
+ * Basically, hosts MUST NOT send Router Advertisement messages at any
+ * time (RFC 2461, Section 6.2.3). However, it would sometimes be
+ * useful to allow hosts to advertise some parameters such as prefix
+ * information and link MTU. Thus, we allow hosts to invoke rtadvd
+ * only when router lifetime (on every advertising interface) is
+ * explicitly set zero. (see also the above section)
+ */
+ if (val && forwarding == 0) {
+ syslog(LOG_ERR,
+ "<%s> non zero router lifetime is specified for %s, "
+ "which must not be allowed for hosts. you must "
+ "change router lifetime or enable IPv6 forwarding.",
+ __func__, intface);
+ exit(1);
+ }
+ tmp->lifetime = val & 0xffff;
+
+ MAYHAVE(val, "rtime", DEF_ADVREACHABLETIME);
+ if (val < 0 || val > MAXREACHABLETIME) {
+ syslog(LOG_ERR,
+ "<%s> reachable time (%ld) on %s is invalid "
+ "(must be no greater than %d)",
+ __func__, val, intface, MAXREACHABLETIME);
+ exit(1);
+ }
+ tmp->reachabletime = (u_int32_t)val;
+
+ MAYHAVE(val64, "retrans", DEF_ADVRETRANSTIMER);
+ if (val64 < 0 || val64 > 0xffffffff) {
+ syslog(LOG_ERR, "<%s> retrans time (%lld) on %s out of range",
+ __func__, (long long)val64, intface);
+ exit(1);
+ }
+ tmp->retranstimer = (u_int32_t)val64;
+
+ if (agetnum("hapref") != -1 || agetnum("hatime") != -1) {
+ syslog(LOG_ERR,
+ "<%s> mobile-ip6 configuration not supported",
+ __func__);
+ exit(1);
+ }
+ /* prefix information */
+
+ /*
+ * This is an implementation specific parameter to consider
+ * link propagation delays and poorly synchronized clocks when
+ * checking consistency of advertised lifetimes.
+ */
+ MAYHAVE(val, "clockskew", 0);
+ tmp->clockskew = val;
+
+ tmp->pfxs = 0;
+ for (i = -1; i < MAXPREFIX; i++) {
+ struct prefix *pfx;
+ char entbuf[256];
+
+ makeentry(entbuf, sizeof(entbuf), i, "addr");
+ addr = (char *)agetstr(entbuf, &bp);
+ if (addr == NULL)
+ continue;
+
+ /* allocate memory to store prefix information */
+ if ((pfx = malloc(sizeof(struct prefix))) == NULL) {
+ syslog(LOG_ERR,
+ "<%s> can't allocate enough memory",
+ __func__);
+ exit(1);
+ }
+ memset(pfx, 0, sizeof(*pfx));
+
+ /* link into chain */
+ insque(pfx, &tmp->prefix);
+ tmp->pfxs++;
+ pfx->rainfo = tmp;
+
+ pfx->origin = PREFIX_FROM_CONFIG;
+
+ if (inet_pton(AF_INET6, addr, &pfx->prefix) != 1) {
+ syslog(LOG_ERR,
+ "<%s> inet_pton failed for %s",
+ __func__, addr);
+ exit(1);
+ }
+ if (IN6_IS_ADDR_MULTICAST(&pfx->prefix)) {
+ syslog(LOG_ERR,
+ "<%s> multicast prefix (%s) must "
+ "not be advertised on %s",
+ __func__, addr, intface);
+ exit(1);
+ }
+ if (IN6_IS_ADDR_LINKLOCAL(&pfx->prefix))
+ syslog(LOG_NOTICE,
+ "<%s> link-local prefix (%s) will be"
+ " advertised on %s",
+ __func__, addr, intface);
+
+ makeentry(entbuf, sizeof(entbuf), i, "prefixlen");
+ MAYHAVE(val, entbuf, 64);
+ if (val < 0 || val > 128) {
+ syslog(LOG_ERR, "<%s> prefixlen (%ld) for %s "
+ "on %s out of range",
+ __func__, val, addr, intface);
+ exit(1);
+ }
+ pfx->prefixlen = (int)val;
+
+ makeentry(entbuf, sizeof(entbuf), i, "pinfoflags");
+ if ((flagstr = (char *)agetstr(entbuf, &bp))) {
+ val = 0;
+ if (strchr(flagstr, 'l'))
+ val |= ND_OPT_PI_FLAG_ONLINK;
+ if (strchr(flagstr, 'a'))
+ val |= ND_OPT_PI_FLAG_AUTO;
+ } else {
+ MAYHAVE(val, entbuf,
+ (ND_OPT_PI_FLAG_ONLINK|ND_OPT_PI_FLAG_AUTO));
+ }
+ pfx->onlinkflg = val & ND_OPT_PI_FLAG_ONLINK;
+ pfx->autoconfflg = val & ND_OPT_PI_FLAG_AUTO;
+
+ makeentry(entbuf, sizeof(entbuf), i, "vltime");
+ MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
+ if (val64 < 0 || val64 > 0xffffffff) {
+ syslog(LOG_ERR, "<%s> vltime (%lld) for "
+ "%s/%d on %s is out of range",
+ __func__, (long long)val64,
+ addr, pfx->prefixlen, intface);
+ exit(1);
+ }
+ pfx->validlifetime = (u_int32_t)val64;
+
+ makeentry(entbuf, sizeof(entbuf), i, "vltimedecr");
+ if (agetflag(entbuf)) {
+ struct timeval now;
+ gettimeofday(&now, 0);
+ pfx->vltimeexpire =
+ now.tv_sec + pfx->validlifetime;
+ }
+
+ makeentry(entbuf, sizeof(entbuf), i, "pltime");
+ MAYHAVE(val64, entbuf, DEF_ADVPREFERREDLIFETIME);
+ if (val64 < 0 || val64 > 0xffffffff) {
+ syslog(LOG_ERR,
+ "<%s> pltime (%lld) for %s/%d on %s "
+ "is out of range",
+ __func__, (long long)val64,
+ addr, pfx->prefixlen, intface);
+ exit(1);
+ }
+ pfx->preflifetime = (u_int32_t)val64;
+
+ makeentry(entbuf, sizeof(entbuf), i, "pltimedecr");
+ if (agetflag(entbuf)) {
+ struct timeval now;
+ gettimeofday(&now, 0);
+ pfx->pltimeexpire =
+ now.tv_sec + pfx->preflifetime;
+ }
+ }
+ if (tmp->pfxs == 0)
+ get_prefix(tmp);
+
+ MAYHAVE(val, "mtu", 0);
+ if (val < 0 || val > 0xffffffff) {
+ syslog(LOG_ERR,
+ "<%s> mtu (%ld) on %s out of range",
+ __func__, val, intface);
+ exit(1);
+ }
+ tmp->linkmtu = (u_int32_t)val;
+ if (tmp->linkmtu == 0) {
+ char *mtustr;
+
+ if ((mtustr = (char *)agetstr("mtu", &bp)) &&
+ strcmp(mtustr, "auto") == 0)
+ tmp->linkmtu = tmp->phymtu;
+ }
+ else if (tmp->linkmtu < IPV6_MMTU || tmp->linkmtu > tmp->phymtu) {
+ syslog(LOG_ERR,
+ "<%s> advertised link mtu (%lu) on %s is invalid (must "
+ "be between least MTU (%d) and physical link MTU (%d)",
+ __func__, (unsigned long)tmp->linkmtu, intface,
+ IPV6_MMTU, tmp->phymtu);
+ exit(1);
+ }
+
+#ifdef SIOCSIFINFO_IN6
+ {
+ struct in6_ndireq ndi;
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "<%s> socket: %s", __func__,
+ strerror(errno));
+ exit(1);
+ }
+ memset(&ndi, 0, sizeof(ndi));
+ strncpy(ndi.ifname, intface, IFNAMSIZ);
+ if (ioctl(s, SIOCGIFINFO_IN6, (caddr_t)&ndi) < 0) {
+ syslog(LOG_INFO, "<%s> ioctl:SIOCGIFINFO_IN6 at %s: %s",
+ __func__, intface, strerror(errno));
+ }
+
+ /* reflect the RA info to the host variables in kernel */
+ ndi.ndi.chlim = tmp->hoplimit;
+ ndi.ndi.retrans = tmp->retranstimer;
+ ndi.ndi.basereachable = tmp->reachabletime;
+ if (ioctl(s, SIOCSIFINFO_IN6, (caddr_t)&ndi) < 0) {
+ syslog(LOG_INFO, "<%s> ioctl:SIOCSIFINFO_IN6 at %s: %s",
+ __func__, intface, strerror(errno));
+ }
+ close(s);
+ }
+#endif
+
+ /* route information */
+#ifdef ROUTEINFO
+ tmp->routes = 0;
+ for (i = -1; i < MAXROUTE; i++) {
+ struct rtinfo *rti;
+ char entbuf[256], oentbuf[256];
+
+ makeentry(entbuf, sizeof(entbuf), i, "rtprefix");
+ addr = (char *)agetstr(entbuf, &bp);
+ if (addr == NULL) {
+ makeentry(oentbuf, sizeof(oentbuf), i, "rtrprefix");
+ addr = (char *)agetstr(oentbuf, &bp);
+ if (addr) {
+ fprintf(stderr, "%s was obsoleted. Use %s.\n",
+ oentbuf, entbuf);
+ }
+ }
+ if (addr == NULL)
+ continue;
+
+ /* allocate memory to store prefix information */
+ if ((rti = malloc(sizeof(struct rtinfo))) == NULL) {
+ syslog(LOG_ERR,
+ "<%s> can't allocate enough memory",
+ __func__);
+ exit(1);
+ }
+ memset(rti, 0, sizeof(*rti));
+
+ /* link into chain */
+ insque(rti, &tmp->route);
+ tmp->routes++;
+
+ if (inet_pton(AF_INET6, addr, &rti->prefix) != 1) {
+ syslog(LOG_ERR, "<%s> inet_pton failed for %s",
+ __func__, addr);
+ exit(1);
+ }
+#if 0
+ /*
+ * XXX: currently there's no restriction in route information
+ * prefix according to
+ * draft-ietf-ipngwg-router-selection-00.txt.
+ * However, I think the similar restriction be necessary.
+ */
+ MAYHAVE(val64, entbuf, DEF_ADVVALIDLIFETIME);
+ if (IN6_IS_ADDR_MULTICAST(&rti->prefix)) {
+ syslog(LOG_ERR,
+ "<%s> multicast route (%s) must "
+ "not be advertised on %s",
+ __func__, addr, intface);
+ exit(1);
+ }
+ if (IN6_IS_ADDR_LINKLOCAL(&rti->prefix)) {
+ syslog(LOG_NOTICE,
+ "<%s> link-local route (%s) will "
+ "be advertised on %s",
+ __func__, addr, intface);
+ exit(1);
+ }
+#endif
+
+ makeentry(entbuf, sizeof(entbuf), i, "rtplen");
+ /* XXX: 256 is a magic number for compatibility check. */
+ MAYHAVE(val, entbuf, 256);
+ if (val == 256) {
+ makeentry(oentbuf, sizeof(oentbuf), i, "rtrplen");
+ MAYHAVE(val, oentbuf, 256);
+ if (val != 256) {
+ fprintf(stderr, "%s was obsoleted. Use %s.\n",
+ oentbuf, entbuf);
+ } else
+ val = 64;
+ }
+ if (val < 0 || val > 128) {
+ syslog(LOG_ERR, "<%s> prefixlen (%ld) for %s on %s "
+ "out of range",
+ __func__, val, addr, intface);
+ exit(1);
+ }
+ rti->prefixlen = (int)val;
+
+ makeentry(entbuf, sizeof(entbuf), i, "rtflags");
+ if ((flagstr = (char *)agetstr(entbuf, &bp))) {
+ val = 0;
+ if (strchr(flagstr, 'h'))
+ val |= ND_RA_FLAG_RTPREF_HIGH;
+ if (strchr(flagstr, 'l')) {
+ if ((val & ND_RA_FLAG_RTPREF_HIGH)) {
+ syslog(LOG_ERR,
+ "<%s> the \'h\' and \'l\' route"
+ " preferences are exclusive",
+ __func__);
+ exit(1);
+ }
+ val |= ND_RA_FLAG_RTPREF_LOW;
+ }
+ } else
+ MAYHAVE(val, entbuf, 256); /* XXX */
+ if (val == 256) {
+ makeentry(oentbuf, sizeof(oentbuf), i, "rtrflags");
+ MAYHAVE(val, oentbuf, 256);
+ if (val != 256) {
+ fprintf(stderr, "%s was obsoleted. Use %s.\n",
+ oentbuf, entbuf);
+ } else
+ val = 0;
+ }
+ rti->rtpref = val & ND_RA_FLAG_RTPREF_MASK;
+ if (rti->rtpref == ND_RA_FLAG_RTPREF_RSV) {
+ syslog(LOG_ERR, "<%s> invalid route preference (%02x) "
+ "for %s/%d on %s",
+ __func__, rti->rtpref, addr,
+ rti->prefixlen, intface);
+ exit(1);
+ }
+
+ /*
+ * Since the spec does not a default value, we should make
+ * this entry mandatory. However, FreeBSD 4.4 has shipped
+ * with this field being optional, we use the router lifetime
+ * as an ad-hoc default value with a warning message.
+ */
+ makeentry(entbuf, sizeof(entbuf), i, "rtltime");
+ MAYHAVE(val64, entbuf, -1);
+ if (val64 == -1) {
+ makeentry(oentbuf, sizeof(oentbuf), i, "rtrltime");
+ MAYHAVE(val64, oentbuf, -1);
+ if (val64 != -1) {
+ fprintf(stderr, "%s was obsoleted. Use %s.\n",
+ oentbuf, entbuf);
+ } else {
+ fprintf(stderr, "%s should be specified "
+ "for interface %s.\n",
+ entbuf, intface);
+ val64 = tmp->lifetime;
+ }
+ }
+ if (val64 < 0 || val64 > 0xffffffff) {
+ syslog(LOG_ERR, "<%s> route lifetime (%lld) for "
+ "%s/%d on %s out of range", __func__,
+ (long long)val64, addr, rti->prefixlen, intface);
+ exit(1);
+ }
+ rti->ltime = (u_int32_t)val64;
+ }
+#endif
+
+ /* okey */
+ tmp->next = ralist;
+ ralist = tmp;
+
+ /* construct the sending packet */
+ make_packet(tmp);
+
+ /* set timer */
+ tmp->timer = rtadvd_add_timer(ra_timeout, ra_timer_update,
+ tmp, tmp);
+ ra_timer_update((void *)tmp, &tmp->timer->tm);
+ rtadvd_set_timer(&tmp->timer->tm, tmp->timer);
+}
+
+void
+get_prefix(struct rainfo *rai)
+{
+ struct ifaddrs *ifap, *ifa;
+ struct prefix *pp;
+ struct in6_addr *a;
+ u_char *p, *ep, *m, *lim;
+ u_char ntopbuf[INET6_ADDRSTRLEN];
+
+ if (getifaddrs(&ifap) < 0) {
+ syslog(LOG_ERR,
+ "<%s> can't get interface addresses",
+ __func__);
+ exit(1);
+ }
+
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ int plen;
+
+ if (strcmp(ifa->ifa_name, rai->ifname) != 0)
+ continue;
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ a = &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr;
+ if (IN6_IS_ADDR_LINKLOCAL(a))
+ continue;
+ /* get prefix length */
+ m = (u_char *)&((struct sockaddr_in6 *)ifa->ifa_netmask)->sin6_addr;
+ lim = (u_char *)(ifa->ifa_netmask) + ifa->ifa_netmask->sa_len;
+ plen = prefixlen(m, lim);
+ if (plen <= 0 || plen > 128) {
+ syslog(LOG_ERR, "<%s> failed to get prefixlen "
+ "or prefix is invalid",
+ __func__);
+ exit(1);
+ }
+ if (plen == 128) /* XXX */
+ continue;
+ if (find_prefix(rai, a, plen)) {
+ /* ignore a duplicated prefix. */
+ continue;
+ }
+
+ /* allocate memory to store prefix info. */
+ if ((pp = malloc(sizeof(*pp))) == NULL) {
+ syslog(LOG_ERR,
+ "<%s> can't get allocate buffer for prefix",
+ __func__);
+ exit(1);
+ }
+ memset(pp, 0, sizeof(*pp));
+
+ /* set prefix, sweep bits outside of prefixlen */
+ pp->prefixlen = plen;
+ memcpy(&pp->prefix, a, sizeof(*a));
+ p = (u_char *)&pp->prefix;
+ ep = (u_char *)(&pp->prefix + 1);
+ while (m < lim && p < ep)
+ *p++ &= *m++;
+ while (p < ep)
+ *p++ = 0x00;
+ if (!inet_ntop(AF_INET6, &pp->prefix, ntopbuf,
+ sizeof(ntopbuf))) {
+ syslog(LOG_ERR, "<%s> inet_ntop failed", __func__);
+ exit(1);
+ }
+ syslog(LOG_DEBUG,
+ "<%s> add %s/%d to prefix list on %s",
+ __func__, ntopbuf, pp->prefixlen, rai->ifname);
+
+ /* set other fields with protocol defaults */
+ pp->validlifetime = DEF_ADVVALIDLIFETIME;
+ pp->preflifetime = DEF_ADVPREFERREDLIFETIME;
+ pp->onlinkflg = 1;
+ pp->autoconfflg = 1;
+ pp->origin = PREFIX_FROM_KERNEL;
+ pp->rainfo = rai;
+
+ /* link into chain */
+ insque(pp, &rai->prefix);
+
+ /* counter increment */
+ rai->pfxs++;
+ }
+
+ freeifaddrs(ifap);
+}
+
+static void
+makeentry(buf, len, id, string)
+ char *buf;
+ size_t len;
+ int id;
+ char *string;
+{
+
+ if (id < 0)
+ strlcpy(buf, string, len);
+ else
+ snprintf(buf, len, "%s%d", string, id);
+}
+
+/*
+ * Add a prefix to the list of specified interface and reconstruct
+ * the outgoing packet.
+ * The prefix must not be in the list.
+ * XXX: other parameters of the prefix (e.g. lifetime) should be
+ * able to be specified.
+ */
+static void
+add_prefix(struct rainfo *rai, struct in6_prefixreq *ipr)
+{
+ struct prefix *prefix;
+ u_char ntopbuf[INET6_ADDRSTRLEN];
+
+ if ((prefix = malloc(sizeof(*prefix))) == NULL) {
+ syslog(LOG_ERR, "<%s> memory allocation failed",
+ __func__);
+ return; /* XXX: error or exit? */
+ }
+ memset(prefix, 0, sizeof(*prefix));
+ prefix->prefix = ipr->ipr_prefix.sin6_addr;
+ prefix->prefixlen = ipr->ipr_plen;
+ prefix->validlifetime = ipr->ipr_vltime;
+ prefix->preflifetime = ipr->ipr_pltime;
+ prefix->onlinkflg = ipr->ipr_raf_onlink;
+ prefix->autoconfflg = ipr->ipr_raf_auto;
+ prefix->origin = PREFIX_FROM_DYNAMIC;
+
+ insque(prefix, &rai->prefix);
+ prefix->rainfo = rai;
+
+ syslog(LOG_DEBUG, "<%s> new prefix %s/%d was added on %s",
+ __func__, inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ ipr->ipr_plen, rai->ifname);
+
+ /* free the previous packet */
+ free(rai->ra_data);
+ rai->ra_data = NULL;
+
+ /* reconstruct the packet */
+ rai->pfxs++;
+ make_packet(rai);
+}
+
+/*
+ * Delete a prefix to the list of specified interface and reconstruct
+ * the outgoing packet.
+ * The prefix must be in the list.
+ */
+void
+delete_prefix(struct prefix *prefix)
+{
+ u_char ntopbuf[INET6_ADDRSTRLEN];
+ struct rainfo *rai = prefix->rainfo;
+
+ remque(prefix);
+ syslog(LOG_DEBUG, "<%s> prefix %s/%d was deleted on %s",
+ __func__, inet_ntop(AF_INET6, &prefix->prefix,
+ ntopbuf, INET6_ADDRSTRLEN),
+ prefix->prefixlen, rai->ifname);
+ if (prefix->timer)
+ rtadvd_remove_timer(&prefix->timer);
+ free(prefix);
+ rai->pfxs--;
+}
+
+void
+invalidate_prefix(struct prefix *prefix)
+{
+ u_char ntopbuf[INET6_ADDRSTRLEN];
+ struct timeval timo;
+ struct rainfo *rai = prefix->rainfo;
+
+ if (prefix->timer) { /* sanity check */
+ syslog(LOG_ERR,
+ "<%s> assumption failure: timer already exists",
+ __func__);
+ exit(1);
+ }
+
+ syslog(LOG_DEBUG, "<%s> prefix %s/%d was invalidated on %s, "
+ "will expire in %ld seconds", __func__,
+ inet_ntop(AF_INET6, &prefix->prefix, ntopbuf, INET6_ADDRSTRLEN),
+ prefix->prefixlen, rai->ifname, (long)prefix_timo);
+
+ /* set the expiration timer */
+ prefix->timer = rtadvd_add_timer(prefix_timeout, NULL, prefix, NULL);
+ if (prefix->timer == NULL) {
+ syslog(LOG_ERR, "<%s> failed to add a timer for a prefix. "
+ "remove the prefix", __func__);
+ delete_prefix(prefix);
+ }
+ timo.tv_sec = prefix_timo;
+ timo.tv_usec = 0;
+ rtadvd_set_timer(&timo, prefix->timer);
+}
+
+static struct rtadvd_timer *
+prefix_timeout(void *arg)
+{
+ struct prefix *prefix = (struct prefix *)arg;
+
+ delete_prefix(prefix);
+
+ return(NULL);
+}
+
+void
+update_prefix(struct prefix * prefix)
+{
+ u_char ntopbuf[INET6_ADDRSTRLEN];
+ struct rainfo *rai = prefix->rainfo;
+
+ if (prefix->timer == NULL) { /* sanity check */
+ syslog(LOG_ERR,
+ "<%s> assumption failure: timer does not exist",
+ __func__);
+ exit(1);
+ }
+
+ syslog(LOG_DEBUG, "<%s> prefix %s/%d was re-enabled on %s",
+ __func__, inet_ntop(AF_INET6, &prefix->prefix, ntopbuf,
+ INET6_ADDRSTRLEN), prefix->prefixlen, rai->ifname);
+
+ /* stop the expiration timer */
+ rtadvd_remove_timer(&prefix->timer);
+}
+
+/*
+ * Try to get an in6_prefixreq contents for a prefix which matches
+ * ipr->ipr_prefix and ipr->ipr_plen and belongs to
+ * the interface whose name is ipr->ipr_name[].
+ */
+static int
+init_prefix(struct in6_prefixreq *ipr)
+{
+#if 0
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "<%s> socket: %s", __func__,
+ strerror(errno));
+ exit(1);
+ }
+
+ if (ioctl(s, SIOCGIFPREFIX_IN6, (caddr_t)ipr) < 0) {
+ syslog(LOG_INFO, "<%s> ioctl:SIOCGIFPREFIX %s", __func__,
+ strerror(errno));
+
+ ipr->ipr_vltime = DEF_ADVVALIDLIFETIME;
+ ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME;
+ ipr->ipr_raf_onlink = 1;
+ ipr->ipr_raf_auto = 1;
+ /* omit other field initialization */
+ }
+ else if (ipr->ipr_origin < PR_ORIG_RR) {
+ u_char ntopbuf[INET6_ADDRSTRLEN];
+
+ syslog(LOG_WARNING, "<%s> Added prefix(%s)'s origin %d is"
+ "lower than PR_ORIG_RR(router renumbering)."
+ "This should not happen if I am router", __func__,
+ inet_ntop(AF_INET6, &ipr->ipr_prefix.sin6_addr, ntopbuf,
+ sizeof(ntopbuf)), ipr->ipr_origin);
+ close(s);
+ return 1;
+ }
+
+ close(s);
+ return 0;
+#else
+ ipr->ipr_vltime = DEF_ADVVALIDLIFETIME;
+ ipr->ipr_pltime = DEF_ADVPREFERREDLIFETIME;
+ ipr->ipr_raf_onlink = 1;
+ ipr->ipr_raf_auto = 1;
+ return 0;
+#endif
+}
+
+void
+make_prefix(struct rainfo *rai, int ifindex, struct in6_addr *addr, int plen)
+{
+ struct in6_prefixreq ipr;
+
+ memset(&ipr, 0, sizeof(ipr));
+ if (if_indextoname(ifindex, ipr.ipr_name) == NULL) {
+ syslog(LOG_ERR, "<%s> Prefix added interface No.%d doesn't"
+ "exist. This should not happen! %s", __func__,
+ ifindex, strerror(errno));
+ exit(1);
+ }
+ ipr.ipr_prefix.sin6_len = sizeof(ipr.ipr_prefix);
+ ipr.ipr_prefix.sin6_family = AF_INET6;
+ ipr.ipr_prefix.sin6_addr = *addr;
+ ipr.ipr_plen = plen;
+
+ if (init_prefix(&ipr))
+ return; /* init failed by some error */
+ add_prefix(rai, &ipr);
+}
+
+void
+make_packet(struct rainfo *rainfo)
+{
+ size_t packlen, lladdroptlen = 0;
+ char *buf;
+ struct nd_router_advert *ra;
+ struct nd_opt_prefix_info *ndopt_pi;
+ struct nd_opt_mtu *ndopt_mtu;
+#ifdef ROUTEINFO
+ struct nd_opt_route_info *ndopt_rti;
+ struct rtinfo *rti;
+#endif
+ struct prefix *pfx;
+
+ /* calculate total length */
+ packlen = sizeof(struct nd_router_advert);
+ if (rainfo->advlinkopt) {
+ if ((lladdroptlen = lladdropt_length(rainfo->sdl)) == 0) {
+ syslog(LOG_INFO,
+ "<%s> link-layer address option has"
+ " null length on %s. Treat as not included.",
+ __func__, rainfo->ifname);
+ rainfo->advlinkopt = 0;
+ }
+ packlen += lladdroptlen;
+ }
+ if (rainfo->pfxs)
+ packlen += sizeof(struct nd_opt_prefix_info) * rainfo->pfxs;
+ if (rainfo->linkmtu)
+ packlen += sizeof(struct nd_opt_mtu);
+#ifdef ROUTEINFO
+ for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next)
+ packlen += sizeof(struct nd_opt_route_info) +
+ ((rti->prefixlen + 0x3f) >> 6) * 8;
+#endif
+
+ /* allocate memory for the packet */
+ if ((buf = malloc(packlen)) == NULL) {
+ syslog(LOG_ERR,
+ "<%s> can't get enough memory for an RA packet",
+ __func__);
+ exit(1);
+ }
+ if (rainfo->ra_data) {
+ /* free the previous packet */
+ free(rainfo->ra_data);
+ rainfo->ra_data = NULL;
+ }
+ rainfo->ra_data = buf;
+ /* XXX: what if packlen > 576? */
+ rainfo->ra_datalen = packlen;
+
+ /*
+ * construct the packet
+ */
+ ra = (struct nd_router_advert *)buf;
+ ra->nd_ra_type = ND_ROUTER_ADVERT;
+ ra->nd_ra_code = 0;
+ ra->nd_ra_cksum = 0;
+ ra->nd_ra_curhoplimit = (u_int8_t)(0xff & rainfo->hoplimit);
+ ra->nd_ra_flags_reserved = 0; /* just in case */
+ /*
+ * XXX: the router preference field, which is a 2-bit field, should be
+ * initialized before other fields.
+ */
+ ra->nd_ra_flags_reserved = 0xff & rainfo->rtpref;
+ ra->nd_ra_flags_reserved |=
+ rainfo->managedflg ? ND_RA_FLAG_MANAGED : 0;
+ ra->nd_ra_flags_reserved |=
+ rainfo->otherflg ? ND_RA_FLAG_OTHER : 0;
+ ra->nd_ra_router_lifetime = htons(rainfo->lifetime);
+ ra->nd_ra_reachable = htonl(rainfo->reachabletime);
+ ra->nd_ra_retransmit = htonl(rainfo->retranstimer);
+ buf += sizeof(*ra);
+
+ if (rainfo->advlinkopt) {
+ lladdropt_fill(rainfo->sdl, (struct nd_opt_hdr *)buf);
+ buf += lladdroptlen;
+ }
+
+ if (rainfo->linkmtu) {
+ ndopt_mtu = (struct nd_opt_mtu *)buf;
+ ndopt_mtu->nd_opt_mtu_type = ND_OPT_MTU;
+ ndopt_mtu->nd_opt_mtu_len = 1;
+ ndopt_mtu->nd_opt_mtu_reserved = 0;
+ ndopt_mtu->nd_opt_mtu_mtu = htonl(rainfo->linkmtu);
+ buf += sizeof(struct nd_opt_mtu);
+ }
+
+ for (pfx = rainfo->prefix.next;
+ pfx != &rainfo->prefix; pfx = pfx->next) {
+ u_int32_t vltime, pltime;
+ struct timeval now;
+
+ ndopt_pi = (struct nd_opt_prefix_info *)buf;
+ ndopt_pi->nd_opt_pi_type = ND_OPT_PREFIX_INFORMATION;
+ ndopt_pi->nd_opt_pi_len = 4;
+ ndopt_pi->nd_opt_pi_prefix_len = pfx->prefixlen;
+ ndopt_pi->nd_opt_pi_flags_reserved = 0;
+ if (pfx->onlinkflg)
+ ndopt_pi->nd_opt_pi_flags_reserved |=
+ ND_OPT_PI_FLAG_ONLINK;
+ if (pfx->autoconfflg)
+ ndopt_pi->nd_opt_pi_flags_reserved |=
+ ND_OPT_PI_FLAG_AUTO;
+ if (pfx->timer)
+ vltime = 0;
+ else {
+ if (pfx->vltimeexpire || pfx->pltimeexpire)
+ gettimeofday(&now, NULL);
+ if (pfx->vltimeexpire == 0)
+ vltime = pfx->validlifetime;
+ else
+ vltime = (pfx->vltimeexpire > now.tv_sec) ?
+ pfx->vltimeexpire - now.tv_sec : 0;
+ }
+ if (pfx->timer)
+ pltime = 0;
+ else {
+ if (pfx->pltimeexpire == 0)
+ pltime = pfx->preflifetime;
+ else
+ pltime = (pfx->pltimeexpire > now.tv_sec) ?
+ pfx->pltimeexpire - now.tv_sec : 0;
+ }
+ if (vltime < pltime) {
+ /*
+ * this can happen if vltime is decrement but pltime
+ * is not.
+ */
+ pltime = vltime;
+ }
+ ndopt_pi->nd_opt_pi_valid_time = htonl(vltime);
+ ndopt_pi->nd_opt_pi_preferred_time = htonl(pltime);
+ ndopt_pi->nd_opt_pi_reserved2 = 0;
+ ndopt_pi->nd_opt_pi_prefix = pfx->prefix;
+
+ buf += sizeof(struct nd_opt_prefix_info);
+ }
+
+#ifdef ROUTEINFO
+ for (rti = rainfo->route.next; rti != &rainfo->route; rti = rti->next) {
+ u_int8_t psize = (rti->prefixlen + 0x3f) >> 6;
+
+ ndopt_rti = (struct nd_opt_route_info *)buf;
+ ndopt_rti->nd_opt_rti_type = ND_OPT_ROUTE_INFO;
+ ndopt_rti->nd_opt_rti_len = 1 + psize;
+ ndopt_rti->nd_opt_rti_prefixlen = rti->prefixlen;
+ ndopt_rti->nd_opt_rti_flags = 0xff & rti->rtpref;
+ ndopt_rti->nd_opt_rti_lifetime = htonl(rti->ltime);
+ memcpy(ndopt_rti + 1, &rti->prefix, psize * 8);
+ buf += sizeof(struct nd_opt_route_info) + psize * 8;
+ }
+#endif
+
+ return;
+}
+
+static int
+getinet6sysctl(int code)
+{
+ int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
+ int value;
+ size_t size;
+
+ mib[3] = code;
+ size = sizeof(value);
+ if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0)
+ < 0) {
+ syslog(LOG_ERR, "<%s>: failed to get ip6 sysctl(%d): %s",
+ __func__, code,
+ strerror(errno));
+ return(-1);
+ }
+ else
+ return(value);
+}
diff --git a/usr.sbin/rtadvd/config.h b/usr.sbin/rtadvd/config.h
new file mode 100644
index 0000000..2d02b8a
--- /dev/null
+++ b/usr.sbin/rtadvd/config.h
@@ -0,0 +1,47 @@
+/* $FreeBSD$ */
+/* $KAME: config.h,v 1.8 2003/06/17 08:26:22 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.
+ */
+
+extern void getconfig(char *);
+extern void delete_prefix(struct prefix *);
+extern void invalidate_prefix(struct prefix *);
+extern void update_prefix(struct prefix *);
+extern void make_prefix(struct rainfo *, int, struct in6_addr *, int);
+extern void make_packet(struct rainfo *);
+extern void get_prefix(struct rainfo *);
+
+
+/*
+ * it is highly unlikely to have 100 prefix information options,
+ * so it should be okay to limit it
+ */
+#define MAXPREFIX 100
+#define MAXROUTE 100
diff --git a/usr.sbin/rtadvd/dump.c b/usr.sbin/rtadvd/dump.c
new file mode 100644
index 0000000..d37f5db
--- /dev/null
+++ b/usr.sbin/rtadvd/dump.c
@@ -0,0 +1,252 @@
+/* $FreeBSD$ */
+/* $KAME: dump.c,v 1.32 2003/05/19 09:46:50 keiichi Exp $ */
+
+/*
+ * Copyright (C) 2000 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/socket.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_dl.h>
+
+#include <netinet/in.h>
+
+/* XXX: the following two are non-standard include files */
+#include <netinet6/in6_var.h>
+#include <netinet6/nd6.h>
+
+#include <arpa/inet.h>
+
+#include <time.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <string.h>
+#include <errno.h>
+
+#include "rtadvd.h"
+#include "timer.h"
+#include "if.h"
+#include "dump.h"
+
+static FILE *fp;
+
+extern struct rainfo *ralist;
+
+static char *ether_str(struct sockaddr_dl *);
+static void if_dump(void);
+
+static char *rtpref_str[] = {
+ "medium", /* 00 */
+ "high", /* 01 */
+ "rsv", /* 10 */
+ "low" /* 11 */
+};
+
+static char *
+ether_str(sdl)
+ struct sockaddr_dl *sdl;
+{
+ static char hbuf[32];
+ u_char *cp;
+
+ if (sdl->sdl_alen && sdl->sdl_alen > 5) {
+ cp = (u_char *)LLADDR(sdl);
+ snprintf(hbuf, sizeof(hbuf), "%x:%x:%x:%x:%x:%x",
+ cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]);
+ } else
+ snprintf(hbuf, sizeof(hbuf), "NONE");
+
+ return(hbuf);
+}
+
+static void
+if_dump()
+{
+ struct rainfo *rai;
+ struct prefix *pfx;
+#ifdef ROUTEINFO
+ struct rtinfo *rti;
+#endif
+ char prefixbuf[INET6_ADDRSTRLEN];
+ int first;
+ struct timeval now;
+
+ gettimeofday(&now, NULL); /* XXX: unused in most cases */
+ for (rai = ralist; rai; rai = rai->next) {
+ fprintf(fp, "%s:\n", rai->ifname);
+
+ fprintf(fp, " Status: %s\n",
+ (iflist[rai->ifindex]->ifm_flags & IFF_UP) ? "UP" :
+ "DOWN");
+
+ /* control information */
+ if (rai->lastsent.tv_sec) {
+ /* note that ctime() appends CR by itself */
+ fprintf(fp, " Last RA sent: %s",
+ ctime((time_t *)&rai->lastsent.tv_sec));
+ }
+ if (rai->timer) {
+ fprintf(fp, " Next RA will be sent: %s",
+ ctime((time_t *)&rai->timer->tm.tv_sec));
+ }
+ else
+ fprintf(fp, " RA timer is stopped");
+ fprintf(fp, " waits: %d, initcount: %d\n",
+ rai->waiting, rai->initcounter);
+
+ /* statistics */
+ fprintf(fp, " statistics: RA(out/in/inconsistent): "
+ "%llu/%llu/%llu, ",
+ (unsigned long long)rai->raoutput,
+ (unsigned long long)rai->rainput,
+ (unsigned long long)rai->rainconsistent);
+ fprintf(fp, "RS(input): %llu\n",
+ (unsigned long long)rai->rsinput);
+
+ /* interface information */
+ if (rai->advlinkopt)
+ fprintf(fp, " Link-layer address: %s\n",
+ ether_str(rai->sdl));
+ fprintf(fp, " MTU: %d\n", rai->phymtu);
+
+ /* Router configuration variables */
+ fprintf(fp, " DefaultLifetime: %d, MaxAdvInterval: %d, "
+ "MinAdvInterval: %d\n", rai->lifetime, rai->maxinterval,
+ rai->mininterval);
+ fprintf(fp, " Flags: %s%s%s, ",
+ rai->managedflg ? "M" : "", rai->otherflg ? "O" : "", "");
+ fprintf(fp, "Preference: %s, ",
+ rtpref_str[(rai->rtpref >> 3) & 0xff]);
+ fprintf(fp, "MTU: %d\n", rai->linkmtu);
+ fprintf(fp, " ReachableTime: %d, RetransTimer: %d, "
+ "CurHopLimit: %d\n", rai->reachabletime,
+ rai->retranstimer, rai->hoplimit);
+ if (rai->clockskew)
+ fprintf(fp, " Clock skew: %ldsec\n",
+ rai->clockskew);
+ for (first = 1, pfx = rai->prefix.next; pfx != &rai->prefix;
+ pfx = pfx->next) {
+ if (first) {
+ fprintf(fp, " Prefixes:\n");
+ first = 0;
+ }
+ fprintf(fp, " %s/%d(",
+ inet_ntop(AF_INET6, &pfx->prefix, prefixbuf,
+ sizeof(prefixbuf)), pfx->prefixlen);
+ switch (pfx->origin) {
+ case PREFIX_FROM_KERNEL:
+ fprintf(fp, "KERNEL, ");
+ break;
+ case PREFIX_FROM_CONFIG:
+ fprintf(fp, "CONFIG, ");
+ break;
+ case PREFIX_FROM_DYNAMIC:
+ fprintf(fp, "DYNAMIC, ");
+ break;
+ }
+ if (pfx->validlifetime == ND6_INFINITE_LIFETIME)
+ fprintf(fp, "vltime: infinity");
+ else
+ fprintf(fp, "vltime: %ld",
+ (long)pfx->validlifetime);
+ if (pfx->vltimeexpire != 0)
+ fprintf(fp, "(decr,expire %ld), ", (long)
+ pfx->vltimeexpire > now.tv_sec ?
+ pfx->vltimeexpire - now.tv_sec : 0);
+ else
+ fprintf(fp, ", ");
+ if (pfx->preflifetime == ND6_INFINITE_LIFETIME)
+ fprintf(fp, "pltime: infinity");
+ else
+ fprintf(fp, "pltime: %ld",
+ (long)pfx->preflifetime);
+ if (pfx->pltimeexpire != 0)
+ fprintf(fp, "(decr,expire %ld), ", (long)
+ pfx->pltimeexpire > now.tv_sec ?
+ pfx->pltimeexpire - now.tv_sec : 0);
+ else
+ fprintf(fp, ", ");
+ fprintf(fp, "flags: %s%s%s",
+ pfx->onlinkflg ? "L" : "",
+ pfx->autoconfflg ? "A" : "",
+ "");
+ if (pfx->timer) {
+ struct timeval *rest;
+
+ rest = rtadvd_timer_rest(pfx->timer);
+ if (rest) { /* XXX: what if not? */
+ fprintf(fp, ", expire in: %ld",
+ (long)rest->tv_sec);
+ }
+ }
+ fprintf(fp, ")\n");
+ }
+#ifdef ROUTEINFO
+ for (first = 1, rti = rai->route.next; rti != &rai->route;
+ rti = rti->next) {
+ if (first) {
+ fprintf(fp, " Route Information:\n");
+ first = 0;
+ }
+ fprintf(fp, " %s/%d (",
+ inet_ntop(AF_INET6, &rti->prefix,
+ prefixbuf, sizeof(prefixbuf)),
+ rti->prefixlen);
+ fprintf(fp, "preference: %s, ",
+ rtpref_str[0xff & (rti->rtpref >> 3)]);
+ if (rti->ltime == ND6_INFINITE_LIFETIME)
+ fprintf(fp, "lifetime: infinity");
+ else
+ fprintf(fp, "lifetime: %ld", (long)rti->ltime);
+ fprintf(fp, ")\n");
+ }
+#endif
+ }
+}
+
+void
+rtadvd_dump_file(dumpfile)
+ char *dumpfile;
+{
+ syslog(LOG_DEBUG, "<%s> dump current status to %s", __func__,
+ dumpfile);
+
+ if ((fp = fopen(dumpfile, "w")) == NULL) {
+ syslog(LOG_WARNING, "<%s> open a dump file(%s)",
+ __func__, dumpfile);
+ return;
+ }
+
+ if_dump();
+
+ fclose(fp);
+}
diff --git a/usr.sbin/rtadvd/dump.h b/usr.sbin/rtadvd/dump.h
new file mode 100644
index 0000000..c8a6b22
--- /dev/null
+++ b/usr.sbin/rtadvd/dump.h
@@ -0,0 +1,33 @@
+/* $FreeBSD$ */
+/* $KAME: dump.h,v 1.1 2000/05/23 11:31:26 itojun Exp $ */
+
+/*
+ * Copyright (C) 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.
+ */
+
+extern void rtadvd_dump_file(char *);
diff --git a/usr.sbin/rtadvd/if.c b/usr.sbin/rtadvd/if.c
new file mode 100644
index 0000000..d8ed088
--- /dev/null
+++ b/usr.sbin/rtadvd/if.c
@@ -0,0 +1,580 @@
+/* $FreeBSD$ */
+/* $KAME: if.c,v 1.17 2001/01/21 15:27:30 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.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/ethernet.h>
+#include <ifaddrs.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#include <netinet/icmp6.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include "rtadvd.h"
+#include "if.h"
+
+#define ROUNDUP(a, size) \
+ (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
+
+#define NEXT_SA(ap) (ap) = (struct sockaddr *) \
+ ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\
+ sizeof(u_long)) :\
+ sizeof(u_long)))
+
+struct if_msghdr **iflist;
+int iflist_init_ok;
+size_t ifblock_size;
+char *ifblock;
+
+static void get_iflist(char **buf, size_t *size);
+static void parse_iflist(struct if_msghdr ***ifmlist_p, char *buf,
+ size_t bufsize);
+
+static void
+get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
+{
+ int i;
+
+ for (i = 0; i < RTAX_MAX; i++) {
+ if (addrs & (1 << i)) {
+ rti_info[i] = sa;
+ NEXT_SA(sa);
+ }
+ else
+ rti_info[i] = NULL;
+ }
+}
+
+struct sockaddr_dl *
+if_nametosdl(char *name)
+{
+ int mib[6] = {CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0};
+ char *buf, *next, *lim;
+ size_t len;
+ struct if_msghdr *ifm;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+ struct sockaddr_dl *sdl = NULL, *ret_sdl;
+
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
+ return(NULL);
+ if ((buf = malloc(len)) == NULL)
+ return(NULL);
+ if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
+ free(buf);
+ return(NULL);
+ }
+
+ lim = buf + len;
+ for (next = buf; next < lim; next += ifm->ifm_msglen) {
+ ifm = (struct if_msghdr *)next;
+ if (ifm->ifm_type == RTM_IFINFO) {
+ sa = (struct sockaddr *)(ifm + 1);
+ get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
+ if ((sa = rti_info[RTAX_IFP]) != NULL) {
+ if (sa->sa_family == AF_LINK) {
+ sdl = (struct sockaddr_dl *)sa;
+ if (strlen(name) != sdl->sdl_nlen)
+ continue; /* not same len */
+ if (strncmp(&sdl->sdl_data[0],
+ name,
+ sdl->sdl_nlen) == 0) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (next == lim) {
+ /* search failed */
+ free(buf);
+ return(NULL);
+ }
+
+ if ((ret_sdl = malloc(sdl->sdl_len)) == NULL)
+ goto end;
+ memcpy((caddr_t)ret_sdl, (caddr_t)sdl, sdl->sdl_len);
+
+end:
+ free(buf);
+ return(ret_sdl);
+}
+
+int
+if_getmtu(char *name)
+{
+ struct ifaddrs *ifap, *ifa;
+ struct if_data *ifd;
+ u_long mtu = 0;
+
+ if (getifaddrs(&ifap) < 0)
+ return(0);
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ if (strcmp(ifa->ifa_name, name) == 0) {
+ ifd = ifa->ifa_data;
+ if (ifd)
+ mtu = ifd->ifi_mtu;
+ break;
+ }
+ }
+ freeifaddrs(ifap);
+
+#ifdef SIOCGIFMTU /* XXX: this ifdef may not be necessary */
+ if (mtu == 0) {
+ struct ifreq ifr;
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0)
+ return(0);
+
+ ifr.ifr_addr.sa_family = AF_INET6;
+ strncpy(ifr.ifr_name, name,
+ sizeof(ifr.ifr_name));
+ if (ioctl(s, SIOCGIFMTU, (caddr_t)&ifr) < 0) {
+ close(s);
+ return(0);
+ }
+ close(s);
+
+ mtu = ifr.ifr_mtu;
+ }
+#endif
+
+ return(mtu);
+}
+
+/* give interface index and its old flags, then new flags returned */
+int
+if_getflags(int ifindex, int oifflags)
+{
+ struct ifreq ifr;
+ int s;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "<%s> socket: %s", __func__,
+ strerror(errno));
+ return (oifflags & ~IFF_UP);
+ }
+
+ if_indextoname(ifindex, ifr.ifr_name);
+ if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
+ syslog(LOG_ERR, "<%s> ioctl:SIOCGIFFLAGS: failed for %s",
+ __func__, ifr.ifr_name);
+ close(s);
+ return (oifflags & ~IFF_UP);
+ }
+ close(s);
+ return (ifr.ifr_flags);
+}
+
+#define ROUNDUP8(a) (1 + (((a) - 1) | 7))
+int
+lladdropt_length(struct sockaddr_dl *sdl)
+{
+ switch (sdl->sdl_type) {
+ case IFT_ETHER:
+ return(ROUNDUP8(ETHER_ADDR_LEN + 2));
+ default:
+ return(0);
+ }
+}
+
+void
+lladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt)
+{
+ char *addr;
+
+ ndopt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; /* fixed */
+
+ switch (sdl->sdl_type) {
+ case IFT_ETHER:
+ ndopt->nd_opt_len = (ROUNDUP8(ETHER_ADDR_LEN + 2)) >> 3;
+ addr = (char *)(ndopt + 1);
+ memcpy(addr, LLADDR(sdl), ETHER_ADDR_LEN);
+ break;
+ default:
+ syslog(LOG_ERR, "<%s> unsupported link type(%d)",
+ __func__, sdl->sdl_type);
+ exit(1);
+ }
+
+ return;
+}
+
+int
+rtbuf_len()
+{
+ size_t len;
+
+ int mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET6, NET_RT_DUMP, 0};
+
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
+ return(-1);
+
+ return(len);
+}
+
+#define FILTER_MATCH(type, filter) ((0x1 << type) & filter)
+#define SIN6(s) ((struct sockaddr_in6 *)(s))
+#define SDL(s) ((struct sockaddr_dl *)(s))
+char *
+get_next_msg(char *buf, char *lim, int ifindex, size_t *lenp, int filter)
+{
+ struct rt_msghdr *rtm;
+ struct ifa_msghdr *ifam;
+ struct sockaddr *sa, *dst, *gw, *ifa, *rti_info[RTAX_MAX];
+
+ *lenp = 0;
+ for (rtm = (struct rt_msghdr *)buf;
+ rtm < (struct rt_msghdr *)lim;
+ rtm = (struct rt_msghdr *)(((char *)rtm) + rtm->rtm_msglen)) {
+ /* just for safety */
+ if (!rtm->rtm_msglen) {
+ syslog(LOG_WARNING, "<%s> rtm_msglen is 0 "
+ "(buf=%p lim=%p rtm=%p)", __func__,
+ buf, lim, rtm);
+ break;
+ }
+ if (FILTER_MATCH(rtm->rtm_type, filter) == 0) {
+ continue;
+ }
+
+ switch (rtm->rtm_type) {
+ case RTM_GET:
+ case RTM_ADD:
+ case RTM_DELETE:
+ /* address related checks */
+ sa = (struct sockaddr *)(rtm + 1);
+ get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
+ if ((dst = rti_info[RTAX_DST]) == NULL ||
+ dst->sa_family != AF_INET6)
+ continue;
+
+ if (IN6_IS_ADDR_LINKLOCAL(&SIN6(dst)->sin6_addr) ||
+ IN6_IS_ADDR_MULTICAST(&SIN6(dst)->sin6_addr))
+ continue;
+
+ if ((gw = rti_info[RTAX_GATEWAY]) == NULL ||
+ gw->sa_family != AF_LINK)
+ continue;
+ if (ifindex && SDL(gw)->sdl_index != ifindex)
+ continue;
+
+ if (rti_info[RTAX_NETMASK] == NULL)
+ continue;
+
+ /* found */
+ *lenp = rtm->rtm_msglen;
+ return (char *)rtm;
+ /* NOTREACHED */
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ ifam = (struct ifa_msghdr *)rtm;
+
+ /* address related checks */
+ sa = (struct sockaddr *)(ifam + 1);
+ get_rtaddrs(ifam->ifam_addrs, sa, rti_info);
+ if ((ifa = rti_info[RTAX_IFA]) == NULL ||
+ (ifa->sa_family != AF_INET &&
+ ifa->sa_family != AF_INET6))
+ continue;
+
+ if (ifa->sa_family == AF_INET6 &&
+ (IN6_IS_ADDR_LINKLOCAL(&SIN6(ifa)->sin6_addr) ||
+ IN6_IS_ADDR_MULTICAST(&SIN6(ifa)->sin6_addr)))
+ continue;
+
+ if (ifindex && ifam->ifam_index != ifindex)
+ continue;
+
+ /* found */
+ *lenp = ifam->ifam_msglen;
+ return (char *)rtm;
+ /* NOTREACHED */
+ case RTM_IFINFO:
+ /* found */
+ *lenp = rtm->rtm_msglen;
+ return (char *)rtm;
+ /* NOTREACHED */
+ }
+ }
+
+ return (char *)rtm;
+}
+#undef FILTER_MATCH
+
+struct in6_addr *
+get_addr(char *buf)
+{
+ struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+
+ sa = (struct sockaddr *)(rtm + 1);
+ get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
+
+ return(&SIN6(rti_info[RTAX_DST])->sin6_addr);
+}
+
+int
+get_rtm_ifindex(char *buf)
+{
+ struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+
+ sa = (struct sockaddr *)(rtm + 1);
+ get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
+
+ return(((struct sockaddr_dl *)rti_info[RTAX_GATEWAY])->sdl_index);
+}
+
+int
+get_ifm_ifindex(char *buf)
+{
+ struct if_msghdr *ifm = (struct if_msghdr *)buf;
+
+ return ((int)ifm->ifm_index);
+}
+
+int
+get_ifam_ifindex(char *buf)
+{
+ struct ifa_msghdr *ifam = (struct ifa_msghdr *)buf;
+
+ return ((int)ifam->ifam_index);
+}
+
+int
+get_ifm_flags(char *buf)
+{
+ struct if_msghdr *ifm = (struct if_msghdr *)buf;
+
+ return (ifm->ifm_flags);
+}
+
+int
+get_prefixlen(char *buf)
+{
+ struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+ u_char *p, *lim;
+
+ sa = (struct sockaddr *)(rtm + 1);
+ get_rtaddrs(rtm->rtm_addrs, sa, rti_info);
+ sa = rti_info[RTAX_NETMASK];
+
+ p = (u_char *)(&SIN6(sa)->sin6_addr);
+ lim = (u_char *)sa + sa->sa_len;
+ return prefixlen(p, lim);
+}
+
+int
+prefixlen(u_char *p, u_char *lim)
+{
+ int masklen;
+
+ for (masklen = 0; p < lim; p++) {
+ switch (*p) {
+ case 0xff:
+ masklen += 8;
+ break;
+ case 0xfe:
+ masklen += 7;
+ break;
+ case 0xfc:
+ masklen += 6;
+ break;
+ case 0xf8:
+ masklen += 5;
+ break;
+ case 0xf0:
+ masklen += 4;
+ break;
+ case 0xe0:
+ masklen += 3;
+ break;
+ case 0xc0:
+ masklen += 2;
+ break;
+ case 0x80:
+ masklen += 1;
+ break;
+ case 0x00:
+ break;
+ default:
+ return(-1);
+ }
+ }
+
+ return(masklen);
+}
+
+int
+rtmsg_type(char *buf)
+{
+ struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
+
+ return(rtm->rtm_type);
+}
+
+int
+rtmsg_len(char *buf)
+{
+ struct rt_msghdr *rtm = (struct rt_msghdr *)buf;
+
+ return(rtm->rtm_msglen);
+}
+
+int
+ifmsg_len(char *buf)
+{
+ struct if_msghdr *ifm = (struct if_msghdr *)buf;
+
+ return(ifm->ifm_msglen);
+}
+
+/*
+ * alloc buffer and get if_msghdrs block from kernel,
+ * and put them into the buffer
+ */
+static void
+get_iflist(char **buf, size_t *size)
+{
+ int mib[6];
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET6;
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0;
+
+ if (sysctl(mib, 6, NULL, size, NULL, 0) < 0) {
+ syslog(LOG_ERR, "<%s> sysctl: iflist size get failed",
+ __func__);
+ exit(1);
+ }
+ if ((*buf = malloc(*size)) == NULL) {
+ syslog(LOG_ERR, "<%s> malloc failed", __func__);
+ exit(1);
+ }
+ if (sysctl(mib, 6, *buf, size, NULL, 0) < 0) {
+ syslog(LOG_ERR, "<%s> sysctl: iflist get failed",
+ __func__);
+ exit(1);
+ }
+ return;
+}
+
+/*
+ * alloc buffer and parse if_msghdrs block passed as arg,
+ * and init the buffer as list of pointers ot each of the if_msghdr.
+ */
+static void
+parse_iflist(struct if_msghdr ***ifmlist_p, char *buf, size_t bufsize)
+{
+ int iflentry_size, malloc_size;
+ struct if_msghdr *ifm;
+ struct ifa_msghdr *ifam;
+ char *lim;
+
+ /*
+ * Estimate least size of an iflist entry, to be obtained from kernel.
+ * Should add sizeof(sockaddr) ??
+ */
+ iflentry_size = sizeof(struct if_msghdr);
+ /* roughly estimate max list size of pointers to each if_msghdr */
+ malloc_size = (bufsize/iflentry_size) * sizeof(size_t);
+ if ((*ifmlist_p = (struct if_msghdr **)malloc(malloc_size)) == NULL) {
+ syslog(LOG_ERR, "<%s> malloc failed", __func__);
+ exit(1);
+ }
+
+ lim = buf + bufsize;
+ for (ifm = (struct if_msghdr *)buf; ifm < (struct if_msghdr *)lim;) {
+ if (ifm->ifm_msglen == 0) {
+ syslog(LOG_WARNING, "<%s> ifm_msglen is 0 "
+ "(buf=%p lim=%p ifm=%p)", __func__,
+ buf, lim, ifm);
+ return;
+ }
+
+ if (ifm->ifm_type == RTM_IFINFO) {
+ (*ifmlist_p)[ifm->ifm_index] = ifm;
+ } else {
+ syslog(LOG_ERR, "out of sync parsing NET_RT_IFLIST\n"
+ "expected %d, got %d\n msglen = %d\n"
+ "buf:%p, ifm:%p, lim:%p\n",
+ RTM_IFINFO, ifm->ifm_type, ifm->ifm_msglen,
+ buf, ifm, lim);
+ exit (1);
+ }
+ for (ifam = (struct ifa_msghdr *)
+ ((char *)ifm + ifm->ifm_msglen);
+ ifam < (struct ifa_msghdr *)lim;
+ ifam = (struct ifa_msghdr *)
+ ((char *)ifam + ifam->ifam_msglen)) {
+ /* just for safety */
+ if (!ifam->ifam_msglen) {
+ syslog(LOG_WARNING, "<%s> ifa_msglen is 0 "
+ "(buf=%p lim=%p ifam=%p)", __func__,
+ buf, lim, ifam);
+ return;
+ }
+ if (ifam->ifam_type != RTM_NEWADDR)
+ break;
+ }
+ ifm = (struct if_msghdr *)ifam;
+ }
+}
+
+void
+init_iflist()
+{
+ if (ifblock) {
+ free(ifblock);
+ ifblock_size = 0;
+ }
+ if (iflist)
+ free(iflist);
+ /* get iflist block from kernel */
+ get_iflist(&ifblock, &ifblock_size);
+
+ /* make list of pointers to each if_msghdr */
+ parse_iflist(&iflist, ifblock, ifblock_size);
+}
diff --git a/usr.sbin/rtadvd/if.h b/usr.sbin/rtadvd/if.h
new file mode 100644
index 0000000..216eaa0
--- /dev/null
+++ b/usr.sbin/rtadvd/if.h
@@ -0,0 +1,58 @@
+/* $FreeBSD$ */
+/* $KAME: if.h,v 1.10 2003/02/24 11:29:10 ono 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.
+ */
+
+#define RTADV_TYPE2BITMASK(type) (0x1 << type)
+
+extern struct if_msghdr **iflist;
+extern size_t ifblock_size;
+extern char *ifblock;
+
+struct nd_opt_hdr;
+struct sockaddr_dl *if_nametosdl(char *);
+int if_getmtu(char *);
+int if_getflags(int, int);
+int lladdropt_length(struct sockaddr_dl *);
+void lladdropt_fill(struct sockaddr_dl *, struct nd_opt_hdr *);
+int rtbuf_len(void);
+char *get_next_msg(char *, char *, int, size_t *, int);
+struct in6_addr *get_addr(char *);
+int get_rtm_ifindex(char *);
+int get_ifm_ifindex(char *);
+int get_ifam_ifindex(char *);
+int get_ifm_flags(char *);
+int get_prefixlen(char *);
+int prefixlen(u_char *, u_char *);
+int rtmsg_type(char *);
+int ifmsg_type(char *);
+int rtmsg_len(char *);
+int ifmsg_len(char *);
+void init_iflist(void);
diff --git a/usr.sbin/rtadvd/pathnames.h b/usr.sbin/rtadvd/pathnames.h
new file mode 100644
index 0000000..3afee55
--- /dev/null
+++ b/usr.sbin/rtadvd/pathnames.h
@@ -0,0 +1,4 @@
+/* $KAME: pathnames.h,v 1.2 2000/05/16 13:34:13 itojun Exp $ */
+/* $FreeBSD$ */
+
+#define _PATH_RTADVDCONF "/etc/rtadvd.conf"
diff --git a/usr.sbin/rtadvd/rrenum.c b/usr.sbin/rtadvd/rrenum.c
new file mode 100644
index 0000000..aafa0f9
--- /dev/null
+++ b/usr.sbin/rtadvd/rrenum.c
@@ -0,0 +1,486 @@
+/* $FreeBSD$ */
+/* $KAME: rrenum.c,v 1.12 2002/06/10 19:59: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.
+ */
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <netinet/icmp6.h>
+
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include "rtadvd.h"
+#include "rrenum.h"
+#include "if.h"
+
+#define RR_ISSET_SEGNUM(segnum_bits, segnum) \
+ ((((segnum_bits)[(segnum) >> 5]) & (1 << ((segnum) & 31))) != 0)
+#define RR_SET_SEGNUM(segnum_bits, segnum) \
+ (((segnum_bits)[(segnum) >> 5]) |= (1 << ((segnum) & 31)))
+
+struct rr_operation {
+ u_long rro_seqnum;
+ u_long rro_segnum_bits[8];
+};
+
+static struct rr_operation rro;
+static int rr_rcvifindex;
+static int rrcmd2pco[RPM_PCO_MAX] = {
+ 0,
+ SIOCAIFPREFIX_IN6,
+ SIOCCIFPREFIX_IN6,
+ SIOCSGIFPREFIX_IN6
+};
+static int s = -1;
+
+/*
+ * Check validity of a Prefix Control Operation(PCO).
+ * Return 0 on success, 1 on failure.
+ */
+static int
+rr_pco_check(int len, struct rr_pco_match *rpm)
+{
+ struct rr_pco_use *rpu, *rpulim;
+ int checklen;
+
+ /* rpm->rpm_len must be (4N * 3) as router-renum-05.txt */
+ if ((rpm->rpm_len - 3) < 0 || /* must be at least 3 */
+ (rpm->rpm_len - 3) & 0x3) { /* must be multiple of 4 */
+ syslog(LOG_WARNING, "<%s> rpm_len %d is not 4N * 3",
+ __func__, rpm->rpm_len);
+ return 1;
+ }
+ /* rpm->rpm_code must be valid value */
+ switch (rpm->rpm_code) {
+ case RPM_PCO_ADD:
+ case RPM_PCO_CHANGE:
+ case RPM_PCO_SETGLOBAL:
+ break;
+ default:
+ syslog(LOG_WARNING, "<%s> unknown rpm_code %d", __func__,
+ rpm->rpm_code);
+ return 1;
+ }
+ /* rpm->rpm_matchlen must be 0 to 128 inclusive */
+ if (rpm->rpm_matchlen > 128) {
+ syslog(LOG_WARNING, "<%s> rpm_matchlen %d is over 128",
+ __func__, rpm->rpm_matchlen);
+ return 1;
+ }
+
+ /*
+ * rpu->rpu_uselen, rpu->rpu_keeplen, and sum of them must be
+ * between 0 and 128 inclusive
+ */
+ for (rpu = (struct rr_pco_use *)(rpm + 1),
+ rpulim = (struct rr_pco_use *)((char *)rpm + len);
+ rpu < rpulim;
+ rpu += 1) {
+ checklen = rpu->rpu_uselen;
+ checklen += rpu->rpu_keeplen;
+ /*
+ * omit these check, because either of rpu_uselen
+ * and rpu_keeplen is unsigned char
+ * (128 > rpu_uselen > 0)
+ * (128 > rpu_keeplen > 0)
+ * (rpu_uselen + rpu_keeplen > 0)
+ */
+ if (checklen > 128) {
+ syslog(LOG_WARNING, "<%s> sum of rpu_uselen %d and"
+ " rpu_keeplen %d is %d(over 128)",
+ __func__, rpu->rpu_uselen,
+ rpu->rpu_keeplen,
+ rpu->rpu_uselen + rpu->rpu_keeplen);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void
+do_use_prefix(int len, struct rr_pco_match *rpm,
+ struct in6_rrenumreq *irr, int ifindex)
+{
+ struct rr_pco_use *rpu, *rpulim;
+ struct rainfo *rai;
+ struct prefix *pp;
+
+ rpu = (struct rr_pco_use *)(rpm + 1);
+ rpulim = (struct rr_pco_use *)((char *)rpm + len);
+
+ if (rpu == rpulim) { /* no use prefix */
+ if (rpm->rpm_code == RPM_PCO_ADD)
+ return;
+
+ irr->irr_u_uselen = 0;
+ irr->irr_u_keeplen = 0;
+ irr->irr_raf_mask_onlink = 0;
+ irr->irr_raf_mask_auto = 0;
+ irr->irr_vltime = 0;
+ irr->irr_pltime = 0;
+ memset(&irr->irr_flags, 0, sizeof(irr->irr_flags));
+ irr->irr_useprefix.sin6_len = 0; /* let it mean, no addition */
+ irr->irr_useprefix.sin6_family = 0;
+ irr->irr_useprefix.sin6_addr = in6addr_any;
+ if (ioctl(s, rrcmd2pco[rpm->rpm_code], (caddr_t)irr) < 0 &&
+ errno != EADDRNOTAVAIL)
+ syslog(LOG_ERR, "<%s> ioctl: %s", __func__,
+ strerror(errno));
+ return;
+ }
+
+ for (rpu = (struct rr_pco_use *)(rpm + 1),
+ rpulim = (struct rr_pco_use *)((char *)rpm + len);
+ rpu < rpulim;
+ rpu += 1) {
+ /* init in6_rrenumreq fields */
+ irr->irr_u_uselen = rpu->rpu_uselen;
+ irr->irr_u_keeplen = rpu->rpu_keeplen;
+ irr->irr_raf_mask_onlink =
+ !!(rpu->rpu_ramask & ICMP6_RR_PCOUSE_RAFLAGS_ONLINK);
+ irr->irr_raf_mask_auto =
+ !!(rpu->rpu_ramask & ICMP6_RR_PCOUSE_RAFLAGS_AUTO);
+ irr->irr_vltime = ntohl(rpu->rpu_vltime);
+ irr->irr_pltime = ntohl(rpu->rpu_pltime);
+ irr->irr_raf_onlink =
+ (rpu->rpu_raflags & ICMP6_RR_PCOUSE_RAFLAGS_ONLINK) == 0 ? 0 : 1;
+ irr->irr_raf_auto =
+ (rpu->rpu_raflags & ICMP6_RR_PCOUSE_RAFLAGS_AUTO) == 0 ? 0 : 1;
+ irr->irr_rrf_decrvalid =
+ (rpu->rpu_flags & ICMP6_RR_PCOUSE_FLAGS_DECRVLTIME) == 0 ? 0 : 1;
+ irr->irr_rrf_decrprefd =
+ (rpu->rpu_flags & ICMP6_RR_PCOUSE_FLAGS_DECRPLTIME) == 0 ? 0 : 1;
+ irr->irr_useprefix.sin6_len = sizeof(irr->irr_useprefix);
+ irr->irr_useprefix.sin6_family = AF_INET6;
+ irr->irr_useprefix.sin6_addr = rpu->rpu_prefix;
+
+ if (ioctl(s, rrcmd2pco[rpm->rpm_code], (caddr_t)irr) < 0 &&
+ errno != EADDRNOTAVAIL)
+ syslog(LOG_ERR, "<%s> ioctl: %s", __func__,
+ strerror(errno));
+
+ /* very adhoc: should be rewritten */
+ if (rpm->rpm_code == RPM_PCO_CHANGE &&
+ IN6_ARE_ADDR_EQUAL(&rpm->rpm_prefix, &rpu->rpu_prefix) &&
+ rpm->rpm_matchlen == rpu->rpu_uselen &&
+ rpu->rpu_uselen == rpu->rpu_keeplen) {
+ if ((rai = if_indextorainfo(ifindex)) == NULL)
+ continue; /* non-advertising IF */
+
+ for (pp = rai->prefix.next; pp != &rai->prefix;
+ pp = pp->next) {
+ struct timeval now;
+
+ if (prefix_match(&pp->prefix, pp->prefixlen,
+ &rpm->rpm_prefix,
+ rpm->rpm_matchlen)) {
+ /* change parameters */
+ pp->validlifetime = ntohl(rpu->rpu_vltime);
+ pp->preflifetime = ntohl(rpu->rpu_pltime);
+ if (irr->irr_rrf_decrvalid) {
+ gettimeofday(&now, 0);
+ pp->vltimeexpire =
+ now.tv_sec + pp->validlifetime;
+ } else
+ pp->vltimeexpire = 0;
+ if (irr->irr_rrf_decrprefd) {
+ gettimeofday(&now, 0);
+ pp->pltimeexpire =
+ now.tv_sec + pp->preflifetime;
+ } else
+ pp->pltimeexpire = 0;
+ }
+ }
+ }
+ }
+}
+
+/*
+ * process a Prefix Control Operation(PCO).
+ * return 0 on success, 1 on failure
+ */
+static int
+do_pco(struct icmp6_router_renum *rr, int len, struct rr_pco_match *rpm)
+{
+ int ifindex = 0;
+ struct in6_rrenumreq irr;
+
+ if ((rr_pco_check(len, rpm) != 0))
+ return 1;
+
+ if (s == -1 && (s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "<%s> socket: %s", __func__,
+ strerror(errno));
+ exit(1);
+ }
+
+ memset(&irr, 0, sizeof(irr));
+ irr.irr_origin = PR_ORIG_RR;
+ irr.irr_m_len = rpm->rpm_matchlen;
+ irr.irr_m_minlen = rpm->rpm_minlen;
+ irr.irr_m_maxlen = rpm->rpm_maxlen;
+ irr.irr_matchprefix.sin6_len = sizeof(irr.irr_matchprefix);
+ irr.irr_matchprefix.sin6_family = AF_INET6;
+ irr.irr_matchprefix.sin6_addr = rpm->rpm_prefix;
+
+ while (if_indextoname(++ifindex, irr.irr_name)) {
+ /*
+ * if ICMP6_RR_FLAGS_FORCEAPPLY(A flag) is 0 and IFF_UP is off,
+ * the interface is not applied
+ */
+ if ((rr->rr_flags & ICMP6_RR_FLAGS_FORCEAPPLY) == 0 &&
+ (iflist[ifindex]->ifm_flags & IFF_UP) == 0)
+ continue;
+ /* TODO: interface scope check */
+ do_use_prefix(len, rpm, &irr, ifindex);
+ }
+ if (errno == ENXIO)
+ return 0;
+ else if (errno) {
+ syslog(LOG_ERR, "<%s> if_indextoname: %s", __func__,
+ strerror(errno));
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * call do_pco() for each Prefix Control Operations(PCOs) in a received
+ * Router Renumbering Command packet.
+ * return 0 on success, 1 on failure
+ */
+static int
+do_rr(int len, struct icmp6_router_renum *rr)
+{
+ struct rr_pco_match *rpm;
+ char *cp, *lim;
+
+ lim = (char *)rr + len;
+ cp = (char *)(rr + 1);
+ len -= sizeof(struct icmp6_router_renum);
+
+ /* get iflist block from kernel again, to get up-to-date information */
+ init_iflist();
+
+ while (cp < lim) {
+ int rpmlen;
+
+ rpm = (struct rr_pco_match *)cp;
+ if (len < sizeof(struct rr_pco_match)) {
+ tooshort:
+ syslog(LOG_ERR, "<%s> pkt too short. left len = %d. "
+ "gabage at end of pkt?", __func__, len);
+ return 1;
+ }
+ rpmlen = rpm->rpm_len << 3;
+ if (len < rpmlen)
+ goto tooshort;
+
+ if (do_pco(rr, rpmlen, rpm)) {
+ syslog(LOG_WARNING, "<%s> invalid PCO", __func__);
+ goto next;
+ }
+
+ next:
+ cp += rpmlen;
+ len -= rpmlen;
+ }
+
+ return 0;
+}
+
+/*
+ * check validity of a router renumbering command packet
+ * return 0 on success, 1 on failure
+ */
+static int
+rr_command_check(int len, struct icmp6_router_renum *rr, struct in6_addr *from,
+ struct in6_addr *dst)
+{
+ u_char ntopbuf[INET6_ADDRSTRLEN];
+
+ /* omit rr minimal length check. hope kernel have done it. */
+ /* rr_command length check */
+ if (len < (sizeof(struct icmp6_router_renum) +
+ sizeof(struct rr_pco_match))) {
+ syslog(LOG_ERR, "<%s> rr_command len %d is too short",
+ __func__, len);
+ return 1;
+ }
+
+ /* destination check. only for multicast. omit unicast check. */
+ if (IN6_IS_ADDR_MULTICAST(dst) && !IN6_IS_ADDR_MC_LINKLOCAL(dst) &&
+ !IN6_IS_ADDR_MC_SITELOCAL(dst)) {
+ syslog(LOG_ERR, "<%s> dst mcast addr %s is illegal",
+ __func__,
+ inet_ntop(AF_INET6, dst, ntopbuf, INET6_ADDRSTRLEN));
+ return 1;
+ }
+
+ /* seqnum and segnum check */
+ if (rro.rro_seqnum > rr->rr_seqnum) {
+ syslog(LOG_WARNING,
+ "<%s> rcvd old seqnum %d from %s",
+ __func__, (u_int32_t)ntohl(rr->rr_seqnum),
+ inet_ntop(AF_INET6, from, ntopbuf, INET6_ADDRSTRLEN));
+ return 1;
+ }
+ if (rro.rro_seqnum == rr->rr_seqnum &&
+ (rr->rr_flags & ICMP6_RR_FLAGS_TEST) == 0 &&
+ RR_ISSET_SEGNUM(rro.rro_segnum_bits, rr->rr_segnum)) {
+ if ((rr->rr_flags & ICMP6_RR_FLAGS_REQRESULT) != 0)
+ syslog(LOG_WARNING,
+ "<%s> rcvd duped segnum %d from %s",
+ __func__, rr->rr_segnum,
+ inet_ntop(AF_INET6, from, ntopbuf,
+ INET6_ADDRSTRLEN));
+ return 0;
+ }
+
+ /* update seqnum */
+ if (rro.rro_seqnum != rr->rr_seqnum) {
+ /* then must be "<" */
+
+ /* init rro_segnum_bits */
+ memset(rro.rro_segnum_bits, 0,
+ sizeof(rro.rro_segnum_bits));
+ }
+ rro.rro_seqnum = rr->rr_seqnum;
+
+ return 0;
+}
+
+static void
+rr_command_input(int len, struct icmp6_router_renum *rr,
+ struct in6_addr *from, struct in6_addr *dst)
+{
+ /* rr_command validity check */
+ if (rr_command_check(len, rr, from, dst))
+ goto failed;
+ if ((rr->rr_flags & (ICMP6_RR_FLAGS_TEST|ICMP6_RR_FLAGS_REQRESULT)) ==
+ ICMP6_RR_FLAGS_TEST)
+ return;
+
+ /* do router renumbering */
+ if (do_rr(len, rr)) {
+ goto failed;
+ }
+
+ /* update segnum */
+ RR_SET_SEGNUM(rro.rro_segnum_bits, rr->rr_segnum);
+
+ return;
+
+ failed:
+ syslog(LOG_ERR, "<%s> received RR was invalid", __func__);
+ return;
+}
+
+void
+rr_input(int len, struct icmp6_router_renum *rr, struct in6_pktinfo *pi,
+ struct sockaddr_in6 *from, struct in6_addr *dst)
+{
+ u_char ntopbuf[2][INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
+
+ syslog(LOG_DEBUG,
+ "<%s> RR received from %s to %s on %s",
+ __func__,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf[0], INET6_ADDRSTRLEN),
+ inet_ntop(AF_INET6, &dst, ntopbuf[1], INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+
+ /* packet validation based on Section 4.1 of RFC2894 */
+ if (len < sizeof(struct icmp6_router_renum)) {
+ syslog(LOG_NOTICE,
+ "<%s>: RR short message (size %d) from %s to %s on %s",
+ __func__, len,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf[0], INET6_ADDRSTRLEN),
+ inet_ntop(AF_INET6, &dst, ntopbuf[1], INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ /*
+ * If the IPv6 destination address is neither an All Routers multicast
+ * address [AARCH] nor one of the receiving router's unicast addresses,
+ * the message MUST be discarded and SHOULD be logged to network
+ * management.
+ * We rely on the kernel input routine for unicast addresses, and thus
+ * check multicast destinations only.
+ */
+ if (IN6_IS_ADDR_MULTICAST(&pi->ipi6_addr) &&
+ !IN6_ARE_ADDR_EQUAL(&in6a_site_allrouters, &pi->ipi6_addr)) {
+ syslog(LOG_NOTICE,
+ "<%s>: RR message with invalid destination (%s) "
+ "from %s on %s",
+ __func__,
+ inet_ntop(AF_INET6, &dst, ntopbuf[0], INET6_ADDRSTRLEN),
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf[1], INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ rr_rcvifindex = pi->ipi6_ifindex;
+
+ switch (rr->rr_code) {
+ case ICMP6_ROUTER_RENUMBERING_COMMAND:
+ rr_command_input(len, rr, &from->sin6_addr, dst);
+ /* TODO: send reply msg */
+ break;
+ case ICMP6_ROUTER_RENUMBERING_RESULT:
+ /* RESULT will be processed by rrenumd */
+ break;
+ case ICMP6_ROUTER_RENUMBERING_SEQNUM_RESET:
+ /* TODO: sequence number reset */
+ break;
+ default:
+ syslog(LOG_ERR, "<%s> received unknown code %d",
+ __func__, rr->rr_code);
+ break;
+
+ }
+
+ return;
+}
diff --git a/usr.sbin/rtadvd/rrenum.h b/usr.sbin/rtadvd/rrenum.h
new file mode 100644
index 0000000..c358a2b
--- /dev/null
+++ b/usr.sbin/rtadvd/rrenum.h
@@ -0,0 +1,34 @@
+/* $FreeBSD$ */
+/* $KAME: rrenum.h,v 1.3 2001/01/21 15:37:14 itojun Exp $ */
+
+/*
+ * Copyright (C) 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.
+ */
+
+void rr_input(int, struct icmp6_router_renum *, struct in6_pktinfo *,
+ struct sockaddr_in6 *, struct in6_addr *);
diff --git a/usr.sbin/rtadvd/rtadvd.8 b/usr.sbin/rtadvd/rtadvd.8
new file mode 100644
index 0000000..b2c0aad
--- /dev/null
+++ b/usr.sbin/rtadvd/rtadvd.8
@@ -0,0 +1,195 @@
+.\" $KAME: rtadvd.8,v 1.24 2002/05/31 16:16:08 jinmei 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 RTADVD 8
+.Os
+.Sh NAME
+.Nm rtadvd
+.Nd router advertisement daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl dDfMRs
+.Op Fl c Ar configfile
+.Ar interface ...
+.Sh DESCRIPTION
+.Nm
+sends router advertisement packets to the specified
+.Ar interfaces .
+.Pp
+The program will daemonize itself on invocation.
+It will then send router advertisement packets periodically, as well
+as in response to router solicitation messages sent by end hosts.
+.Pp
+Router advertisements can be configured on a per-interface basis, as
+described in
+.Xr rtadvd.conf 5 .
+.Pp
+If there is no configuration file entry for an interface,
+or if the configuration file does not exist altogether,
+.Nm
+sets all the parameters to their default values.
+In particular,
+.Nm
+reads all the interface routes from the routing table and advertises
+them as on-link prefixes.
+.Pp
+.Nm
+also watches the routing table.
+If an interface direct route is
+added on an advertising interface and no static prefixes are
+specified by the configuration file,
+.Nm
+adds the corresponding prefix to its advertising list.
+.Pp
+Similarly, when an interface direct route is deleted,
+.Nm
+will start advertising the prefixes with zero valid and preferred
+lifetimes to help the receiving hosts switch to a new prefix when
+renumbering.
+Note, however, that the zero valid lifetime cannot invalidate the
+autoconfigured addresses at a receiving host immediately.
+According to the specification, the host will retain the address
+for a certain period, which will typically be two hours.
+The zero lifetimes rather intend to make the address deprecated,
+indicating that a new non-deprecated address should be used as the
+source address of a new connection.
+This behavior will last for two hours.
+Then
+.Nm
+will completely remove the prefix from the advertising list,
+and succeeding advertisements will not contain the prefix information.
+.Pp
+Moreover, if the status of an advertising interface changes,
+.Nm
+will start or stop sending router advertisements according
+to the latest status.
+.Pp
+The
+.Fl s
+option may be used to disable this behavior;
+.Nm
+will not watch the routing table and the whole functionality described
+above will be suppressed.
+.Pp
+Basically, hosts MUST NOT send Router Advertisement messages at any
+time (RFC 2461, Section 6.2.3).
+However, it would sometimes be useful to allow hosts to advertise some
+parameters such as prefix information and link MTU.
+Thus,
+.Nm
+can be invoked if router lifetime is explicitly set zero on every
+advertising interface.
+.Pp
+The command line options are:
+.Bl -tag -width indent
+.\"
+.It Fl c
+Specify an alternate location,
+.Ar configfile ,
+for the configuration file.
+By default,
+.Pa /etc/rtadvd.conf
+is used.
+.It Fl d
+Print debugging information.
+.It Fl D
+Even more debugging information is printed.
+.It Fl f
+Foreground mode (useful when debugging).
+Log messages will be dumped to stderr when this option is specified.
+.It Fl M
+Specify an interface to join the all-routers site-local multicast group.
+By default,
+.Nm
+tries to join the first advertising interface appearing on the command
+line.
+This option has meaning only with the
+.Fl R
+option, which enables routing renumbering protocol support.
+.It Fl R
+Accept router renumbering requests.
+If you enable it, certain IPsec setup is suggested for security reasons.
+This option is currently disabled, and is ignored by
+.Nm
+with a warning message.
+.It Fl s
+Do not add or delete prefixes dynamically.
+Only statically configured prefixes, if any, will be advertised.
+.El
+.Pp
+Upon receipt of signal
+.Dv SIGUSR1 ,
+.Nm
+will dump the current internal state into
+.Pa /var/run/rtadvd.dump .
+.Pp
+Use
+.Dv SIGTERM
+to kill
+.Nm
+gracefully.
+In this case,
+.Nm
+will transmit router advertisement with router lifetime 0
+to all the interfaces
+.Pq in accordance with RFC2461 6.2.5 .
+.Sh EXIT STATUS
+.Ex -std
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /etc/rtadvd.conf
+The default configuration file.
+.It Pa /var/run/rtadvd.pid
+contains the pid of the currently running
+.Nm .
+.It Pa /var/run/rtadvd.dump
+The file in which
+.Nm
+dumps its internal state.
+.El
+.Sh SEE ALSO
+.Xr rtadvd.conf 5 ,
+.Xr rtsol 8
+.Sh HISTORY
+The
+.Nm
+command first appeared in the WIDE Hydrangea IPv6 protocol stack kit.
+.Sh BUGS
+There used to be some text that recommended users not to let
+.Nm
+advertise Router Advertisement messages on an upstream link to avoid
+undesirable
+.Xr icmp6 4
+redirect messages.
+However, based on the later discussion in the IETF ipng working group,
+all routers should rather advertise the messages regardless of
+the network topology, in order to ensure reachability.
diff --git a/usr.sbin/rtadvd/rtadvd.c b/usr.sbin/rtadvd/rtadvd.c
new file mode 100644
index 0000000..af72bb9
--- /dev/null
+++ b/usr.sbin/rtadvd/rtadvd.c
@@ -0,0 +1,1689 @@
+/* $FreeBSD$ */
+/* $KAME: rtadvd.c,v 1.82 2003/08/05 12:34:23 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.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/icmp6.h>
+
+#include <arpa/inet.h>
+
+#include <time.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <err.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#endif
+
+#include "rtadvd.h"
+#include "rrenum.h"
+#include "advcap.h"
+#include "timer.h"
+#include "if.h"
+#include "config.h"
+#include "dump.h"
+
+struct msghdr rcvmhdr;
+static u_char *rcvcmsgbuf;
+static size_t rcvcmsgbuflen;
+static u_char *sndcmsgbuf = NULL;
+static size_t sndcmsgbuflen;
+volatile sig_atomic_t do_dump;
+volatile sig_atomic_t do_die;
+struct msghdr sndmhdr;
+struct iovec rcviov[2];
+struct iovec sndiov[2];
+struct sockaddr_in6 rcvfrom;
+struct sockaddr_in6 sin6_allnodes = {sizeof(sin6_allnodes), AF_INET6};
+struct in6_addr in6a_site_allrouters;
+static char *dumpfilename = "/var/run/rtadvd.dump"; /* XXX: should be configurable */
+static char *pidfilename = "/var/run/rtadvd.pid"; /* should be configurable */
+static char *mcastif;
+int sock;
+int rtsock = -1;
+int accept_rr = 0;
+int dflag = 0, sflag = 0;
+
+u_char *conffile = NULL;
+
+struct rainfo *ralist = NULL;
+struct nd_optlist {
+ struct nd_optlist *next;
+ struct nd_opt_hdr *opt;
+};
+union nd_opts {
+ struct nd_opt_hdr *nd_opt_array[9];
+ struct {
+ struct nd_opt_hdr *zero;
+ struct nd_opt_hdr *src_lladdr;
+ struct nd_opt_hdr *tgt_lladdr;
+ struct nd_opt_prefix_info *pi;
+ struct nd_opt_rd_hdr *rh;
+ struct nd_opt_mtu *mtu;
+ struct nd_optlist *list;
+ } nd_opt_each;
+};
+#define nd_opts_src_lladdr nd_opt_each.src_lladdr
+#define nd_opts_tgt_lladdr nd_opt_each.tgt_lladdr
+#define nd_opts_pi nd_opt_each.pi
+#define nd_opts_rh nd_opt_each.rh
+#define nd_opts_mtu nd_opt_each.mtu
+#define nd_opts_list nd_opt_each.list
+
+#define NDOPT_FLAG_SRCLINKADDR 0x1
+#define NDOPT_FLAG_TGTLINKADDR 0x2
+#define NDOPT_FLAG_PREFIXINFO 0x4
+#define NDOPT_FLAG_RDHDR 0x8
+#define NDOPT_FLAG_MTU 0x10
+
+u_int32_t ndopt_flags[] = {
+ 0, NDOPT_FLAG_SRCLINKADDR, NDOPT_FLAG_TGTLINKADDR,
+ NDOPT_FLAG_PREFIXINFO, NDOPT_FLAG_RDHDR, NDOPT_FLAG_MTU,
+};
+
+int main(int, char *[]);
+static void set_die(int);
+static void die(void);
+static void sock_open(void);
+static void rtsock_open(void);
+static void rtadvd_input(void);
+static void rs_input(int, struct nd_router_solicit *,
+ struct in6_pktinfo *, struct sockaddr_in6 *);
+static void ra_input(int, struct nd_router_advert *,
+ struct in6_pktinfo *, struct sockaddr_in6 *);
+static int prefix_check(struct nd_opt_prefix_info *, struct rainfo *,
+ struct sockaddr_in6 *);
+static int nd6_options(struct nd_opt_hdr *, int,
+ union nd_opts *, u_int32_t);
+static void free_ndopts(union nd_opts *);
+static void ra_output(struct rainfo *);
+static void rtmsg_input(void);
+static void rtadvd_set_dump_file(int);
+static void set_short_delay(struct rainfo *);
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+#ifdef HAVE_POLL_H
+ struct pollfd set[2];
+#else
+ fd_set *fdsetp, *selectfdp;
+ int fdmasks;
+ int maxfd = 0;
+#endif
+ struct timeval *timeout;
+ int i, ch;
+ int fflag = 0, logopt;
+ FILE *pidfp;
+ pid_t pid;
+
+ /* get command line options and arguments */
+ while ((ch = getopt(argc, argv, "c:dDfM:Rs")) != -1) {
+ switch (ch) {
+ case 'c':
+ conffile = optarg;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'D':
+ dflag = 2;
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 'M':
+ mcastif = optarg;
+ break;
+ case 'R':
+ fprintf(stderr, "rtadvd: "
+ "the -R option is currently ignored.\n");
+ /* accept_rr = 1; */
+ /* run anyway... */
+ break;
+ case 's':
+ sflag = 1;
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc == 0) {
+ fprintf(stderr,
+ "usage: rtadvd [-dDfMRs] [-c conffile] "
+ "interfaces...\n");
+ exit(1);
+ }
+
+ logopt = LOG_NDELAY | LOG_PID;
+ if (fflag)
+ logopt |= LOG_PERROR;
+ openlog("rtadvd", logopt, LOG_DAEMON);
+
+ /* set log level */
+ if (dflag == 0)
+ (void)setlogmask(LOG_UPTO(LOG_ERR));
+ if (dflag == 1)
+ (void)setlogmask(LOG_UPTO(LOG_INFO));
+
+ /* timer initialization */
+ rtadvd_timer_init();
+
+#ifndef HAVE_ARC4RANDOM
+ /* random value initialization */
+#ifdef __FreeBSD__
+ srandomdev();
+#else
+ srandom((u_long)time(NULL));
+#endif
+#endif
+
+ /* get iflist block from kernel */
+ init_iflist();
+
+ while (argc--)
+ getconfig(*argv++);
+
+ if (inet_pton(AF_INET6, ALLNODES, &sin6_allnodes.sin6_addr) != 1) {
+ fprintf(stderr, "fatal: inet_pton failed\n");
+ exit(1);
+ }
+
+ if (!fflag)
+ daemon(1, 0);
+
+ sock_open();
+
+ /* record the current PID */
+ pid = getpid();
+ if ((pidfp = fopen(pidfilename, "w")) == NULL) {
+ syslog(LOG_ERR,
+ "<%s> failed to open the pid log file, run anyway.",
+ __func__);
+ } else {
+ fprintf(pidfp, "%d\n", pid);
+ fclose(pidfp);
+ }
+
+#ifdef HAVE_POLL_H
+ set[0].fd = sock;
+ set[0].events = POLLIN;
+ if (sflag == 0) {
+ rtsock_open();
+ set[1].fd = rtsock;
+ set[1].events = POLLIN;
+ } else
+ set[1].fd = -1;
+#else
+ maxfd = sock;
+ if (sflag == 0) {
+ rtsock_open();
+ if (rtsock > sock)
+ maxfd = rtsock;
+ } else
+ rtsock = -1;
+
+ fdmasks = howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask);
+ if ((fdsetp = malloc(fdmasks)) == NULL) {
+ err(1, "malloc");
+ /*NOTREACHED*/
+ }
+ if ((selectfdp = malloc(fdmasks)) == NULL) {
+ err(1, "malloc");
+ /*NOTREACHED*/
+ }
+ memset(fdsetp, 0, fdmasks);
+ FD_SET(sock, fdsetp);
+ if (rtsock >= 0)
+ FD_SET(rtsock, fdsetp);
+#endif
+
+ signal(SIGTERM, set_die);
+ signal(SIGUSR1, rtadvd_set_dump_file);
+
+ while (1) {
+#ifndef HAVE_POLL_H
+ memcpy(selectfdp, fdsetp, fdmasks); /* reinitialize */
+#endif
+
+ if (do_dump) { /* SIGUSR1 */
+ do_dump = 0;
+ rtadvd_dump_file(dumpfilename);
+ }
+
+ if (do_die) {
+ die();
+ /*NOTREACHED*/
+ }
+
+ /* timer expiration check and reset the timer */
+ timeout = rtadvd_check_timer();
+
+ if (timeout != NULL) {
+ syslog(LOG_DEBUG,
+ "<%s> set timer to %ld:%ld. waiting for "
+ "inputs or timeout", __func__,
+ (long int)timeout->tv_sec,
+ (long int)timeout->tv_usec);
+ } else {
+ syslog(LOG_DEBUG,
+ "<%s> there's no timer. waiting for inputs",
+ __func__);
+ }
+
+#ifdef HAVE_POLL_H
+ if ((i = poll(set, 2, timeout ? (timeout->tv_sec * 1000 +
+ timeout->tv_usec / 1000) : INFTIM)) < 0)
+#else
+ if ((i = select(maxfd + 1, selectfdp, NULL, NULL,
+ timeout)) < 0)
+#endif
+ {
+ /* EINTR would occur upon SIGUSR1 for status dump */
+ if (errno != EINTR)
+ syslog(LOG_ERR, "<%s> select: %s",
+ __func__, strerror(errno));
+ continue;
+ }
+ if (i == 0) /* timeout */
+ continue;
+#ifdef HAVE_POLL_H
+ if (rtsock != -1 && set[1].revents & POLLIN)
+#else
+ if (rtsock != -1 && FD_ISSET(rtsock, selectfdp))
+#endif
+ rtmsg_input();
+#ifdef HAVE_POLL_H
+ if (set[0].revents & POLLIN)
+#else
+ if (FD_ISSET(sock, selectfdp))
+#endif
+ rtadvd_input();
+ }
+ exit(0); /* NOTREACHED */
+}
+
+static void
+rtadvd_set_dump_file(sig)
+ int sig;
+{
+ do_dump = 1;
+}
+
+static void
+set_die(sig)
+ int sig;
+{
+ do_die = 1;
+}
+
+static void
+die()
+{
+ struct rainfo *ra;
+ int i;
+ const int retrans = MAX_FINAL_RTR_ADVERTISEMENTS;
+
+ if (dflag > 1) {
+ syslog(LOG_DEBUG, "<%s> cease to be an advertising router\n",
+ __func__);
+ }
+
+ for (ra = ralist; ra; ra = ra->next) {
+ ra->lifetime = 0;
+ make_packet(ra);
+ }
+ for (i = 0; i < retrans; i++) {
+ for (ra = ralist; ra; ra = ra->next)
+ ra_output(ra);
+ sleep(MIN_DELAY_BETWEEN_RAS);
+ }
+ exit(0);
+ /*NOTREACHED*/
+}
+
+static void
+rtmsg_input()
+{
+ int n, type, ifindex = 0, plen;
+ size_t len;
+ char msg[2048], *next, *lim;
+ u_char ifname[IF_NAMESIZE];
+ struct prefix *prefix;
+ struct rainfo *rai;
+ struct in6_addr *addr;
+ char addrbuf[INET6_ADDRSTRLEN];
+ int prefixchange = 0;
+
+ n = read(rtsock, msg, sizeof(msg));
+ if (dflag > 1) {
+ syslog(LOG_DEBUG, "<%s> received a routing message "
+ "(type = %d, len = %d)", __func__, rtmsg_type(msg), n);
+ }
+ if (n > rtmsg_len(msg)) {
+ /*
+ * This usually won't happen for messages received on
+ * a routing socket.
+ */
+ if (dflag > 1)
+ syslog(LOG_DEBUG,
+ "<%s> received data length is larger than "
+ "1st routing message len. multiple messages? "
+ "read %d bytes, but 1st msg len = %d",
+ __func__, n, rtmsg_len(msg));
+#if 0
+ /* adjust length */
+ n = rtmsg_len(msg);
+#endif
+ }
+
+ lim = msg + n;
+ for (next = msg; next < lim; next += len) {
+ int oldifflags;
+
+ next = get_next_msg(next, lim, 0, &len,
+ RTADV_TYPE2BITMASK(RTM_ADD) |
+ RTADV_TYPE2BITMASK(RTM_DELETE) |
+ RTADV_TYPE2BITMASK(RTM_NEWADDR) |
+ RTADV_TYPE2BITMASK(RTM_DELADDR) |
+ RTADV_TYPE2BITMASK(RTM_IFINFO));
+ if (len == 0)
+ break;
+ type = rtmsg_type(next);
+ switch (type) {
+ case RTM_ADD:
+ case RTM_DELETE:
+ ifindex = get_rtm_ifindex(next);
+ break;
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ ifindex = get_ifam_ifindex(next);
+ break;
+ case RTM_IFINFO:
+ ifindex = get_ifm_ifindex(next);
+ break;
+ default:
+ /* should not reach here */
+ if (dflag > 1) {
+ syslog(LOG_DEBUG,
+ "<%s:%d> unknown rtmsg %d on %s",
+ __func__, __LINE__, type,
+ if_indextoname(ifindex, ifname));
+ }
+ continue;
+ }
+
+ if ((rai = if_indextorainfo(ifindex)) == NULL) {
+ if (dflag > 1) {
+ syslog(LOG_DEBUG,
+ "<%s> route changed on "
+ "non advertising interface(%s)",
+ __func__,
+ if_indextoname(ifindex, ifname));
+ }
+ continue;
+ }
+ oldifflags = iflist[ifindex]->ifm_flags;
+
+ switch (type) {
+ case RTM_ADD:
+ /* init ifflags because it may have changed */
+ iflist[ifindex]->ifm_flags =
+ if_getflags(ifindex, iflist[ifindex]->ifm_flags);
+
+ if (sflag)
+ break; /* we aren't interested in prefixes */
+
+ addr = get_addr(msg);
+ plen = get_prefixlen(msg);
+ /* sanity check for plen */
+ /* as RFC2373, prefixlen is at least 4 */
+ if (plen < 4 || plen > 127) {
+ syslog(LOG_INFO, "<%s> new interface route's"
+ "plen %d is invalid for a prefix",
+ __func__, plen);
+ break;
+ }
+ prefix = find_prefix(rai, addr, plen);
+ if (prefix) {
+ if (prefix->timer) {
+ /*
+ * If the prefix has been invalidated,
+ * make it available again.
+ */
+ update_prefix(prefix);
+ prefixchange = 1;
+ } else if (dflag > 1) {
+ syslog(LOG_DEBUG,
+ "<%s> new prefix(%s/%d) "
+ "added on %s, "
+ "but it was already in list",
+ __func__,
+ inet_ntop(AF_INET6, addr,
+ (char *)addrbuf, INET6_ADDRSTRLEN),
+ plen, rai->ifname);
+ }
+ break;
+ }
+ make_prefix(rai, ifindex, addr, plen);
+ prefixchange = 1;
+ break;
+ case RTM_DELETE:
+ /* init ifflags because it may have changed */
+ iflist[ifindex]->ifm_flags =
+ if_getflags(ifindex, iflist[ifindex]->ifm_flags);
+
+ if (sflag)
+ break;
+
+ addr = get_addr(msg);
+ plen = get_prefixlen(msg);
+ /* sanity check for plen */
+ /* as RFC2373, prefixlen is at least 4 */
+ if (plen < 4 || plen > 127) {
+ syslog(LOG_INFO,
+ "<%s> deleted interface route's "
+ "plen %d is invalid for a prefix",
+ __func__, plen);
+ break;
+ }
+ prefix = find_prefix(rai, addr, plen);
+ if (prefix == NULL) {
+ if (dflag > 1) {
+ syslog(LOG_DEBUG,
+ "<%s> prefix(%s/%d) was "
+ "deleted on %s, "
+ "but it was not in list",
+ __func__,
+ inet_ntop(AF_INET6, addr,
+ (char *)addrbuf, INET6_ADDRSTRLEN),
+ plen, rai->ifname);
+ }
+ break;
+ }
+ invalidate_prefix(prefix);
+ prefixchange = 1;
+ break;
+ case RTM_NEWADDR:
+ case RTM_DELADDR:
+ /* init ifflags because it may have changed */
+ iflist[ifindex]->ifm_flags =
+ if_getflags(ifindex, iflist[ifindex]->ifm_flags);
+ break;
+ case RTM_IFINFO:
+ iflist[ifindex]->ifm_flags = get_ifm_flags(next);
+ break;
+ default:
+ /* should not reach here */
+ if (dflag > 1) {
+ syslog(LOG_DEBUG,
+ "<%s:%d> unknown rtmsg %d on %s",
+ __func__, __LINE__, type,
+ if_indextoname(ifindex, ifname));
+ }
+ return;
+ }
+
+ /* check if an interface flag is changed */
+ if ((oldifflags & IFF_UP) && /* UP to DOWN */
+ !(iflist[ifindex]->ifm_flags & IFF_UP)) {
+ syslog(LOG_INFO,
+ "<%s> interface %s becomes down. stop timer.",
+ __func__, rai->ifname);
+ rtadvd_remove_timer(&rai->timer);
+ } else if (!(oldifflags & IFF_UP) && /* DOWN to UP */
+ (iflist[ifindex]->ifm_flags & IFF_UP)) {
+ syslog(LOG_INFO,
+ "<%s> interface %s becomes up. restart timer.",
+ __func__, rai->ifname);
+
+ rai->initcounter = 0; /* reset the counter */
+ rai->waiting = 0; /* XXX */
+ rai->timer = rtadvd_add_timer(ra_timeout,
+ ra_timer_update, rai, rai);
+ ra_timer_update((void *)rai, &rai->timer->tm);
+ rtadvd_set_timer(&rai->timer->tm, rai->timer);
+ } else if (prefixchange &&
+ (iflist[ifindex]->ifm_flags & IFF_UP)) {
+ /*
+ * An advertised prefix has been added or invalidated.
+ * Will notice the change in a short delay.
+ */
+ rai->initcounter = 0;
+ set_short_delay(rai);
+ }
+ }
+
+ return;
+}
+
+void
+rtadvd_input()
+{
+ int i;
+ int *hlimp = NULL;
+#ifdef OLDRAWSOCKET
+ struct ip6_hdr *ip;
+#endif
+ struct icmp6_hdr *icp;
+ int ifindex = 0;
+ struct cmsghdr *cm;
+ struct in6_pktinfo *pi = NULL;
+ u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
+ struct in6_addr dst = in6addr_any;
+
+ /*
+ * Get message. We reset msg_controllen since the field could
+ * be modified if we had received a message before setting
+ * receive options.
+ */
+ rcvmhdr.msg_controllen = rcvcmsgbuflen;
+ if ((i = recvmsg(sock, &rcvmhdr, 0)) < 0)
+ return;
+
+ /* extract optional information via Advanced API */
+ for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhdr);
+ cm;
+ cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvmhdr, cm)) {
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_PKTINFO &&
+ cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
+ pi = (struct in6_pktinfo *)(CMSG_DATA(cm));
+ ifindex = pi->ipi6_ifindex;
+ dst = pi->ipi6_addr;
+ }
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_HOPLIMIT &&
+ cm->cmsg_len == CMSG_LEN(sizeof(int)))
+ hlimp = (int *)CMSG_DATA(cm);
+ }
+ if (ifindex == 0) {
+ syslog(LOG_ERR,
+ "<%s> failed to get receiving interface",
+ __func__);
+ return;
+ }
+ if (hlimp == NULL) {
+ syslog(LOG_ERR,
+ "<%s> failed to get receiving hop limit",
+ __func__);
+ return;
+ }
+
+ /*
+ * If we happen to receive data on an interface which is now down,
+ * just discard the data.
+ */
+ if ((iflist[pi->ipi6_ifindex]->ifm_flags & IFF_UP) == 0) {
+ syslog(LOG_INFO,
+ "<%s> received data on a disabled interface (%s)",
+ __func__,
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+#ifdef OLDRAWSOCKET
+ if (i < sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr)) {
+ syslog(LOG_ERR,
+ "<%s> packet size(%d) is too short",
+ __func__, i);
+ return;
+ }
+
+ ip = (struct ip6_hdr *)rcvmhdr.msg_iov[0].iov_base;
+ icp = (struct icmp6_hdr *)(ip + 1); /* XXX: ext. hdr? */
+#else
+ if (i < sizeof(struct icmp6_hdr)) {
+ syslog(LOG_ERR,
+ "<%s> packet size(%d) is too short",
+ __func__, i);
+ return;
+ }
+
+ icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base;
+#endif
+
+ switch (icp->icmp6_type) {
+ case ND_ROUTER_SOLICIT:
+ /*
+ * Message verification - RFC-2461 6.1.1
+ * XXX: these checks must be done in the kernel as well,
+ * but we can't completely rely on them.
+ */
+ if (*hlimp != 255) {
+ syslog(LOG_NOTICE,
+ "<%s> RS with invalid hop limit(%d) "
+ "received from %s on %s",
+ __func__, *hlimp,
+ inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+ if (icp->icmp6_code) {
+ syslog(LOG_NOTICE,
+ "<%s> RS with invalid ICMP6 code(%d) "
+ "received from %s on %s",
+ __func__, icp->icmp6_code,
+ inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+ if (i < sizeof(struct nd_router_solicit)) {
+ syslog(LOG_NOTICE,
+ "<%s> RS from %s on %s does not have enough "
+ "length (len = %d)",
+ __func__,
+ inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf), i);
+ return;
+ }
+ rs_input(i, (struct nd_router_solicit *)icp, pi, &rcvfrom);
+ break;
+ case ND_ROUTER_ADVERT:
+ /*
+ * Message verification - RFC-2461 6.1.2
+ * XXX: there's a same dilemma as above...
+ */
+ if (*hlimp != 255) {
+ syslog(LOG_NOTICE,
+ "<%s> RA with invalid hop limit(%d) "
+ "received from %s on %s",
+ __func__, *hlimp,
+ inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+ if (icp->icmp6_code) {
+ syslog(LOG_NOTICE,
+ "<%s> RA with invalid ICMP6 code(%d) "
+ "received from %s on %s",
+ __func__, icp->icmp6_code,
+ inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+ if (i < sizeof(struct nd_router_advert)) {
+ syslog(LOG_NOTICE,
+ "<%s> RA from %s on %s does not have enough "
+ "length (len = %d)",
+ __func__,
+ inet_ntop(AF_INET6, &rcvfrom.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf), i);
+ return;
+ }
+ ra_input(i, (struct nd_router_advert *)icp, pi, &rcvfrom);
+ break;
+ case ICMP6_ROUTER_RENUMBERING:
+ if (accept_rr == 0) {
+ syslog(LOG_ERR, "<%s> received a router renumbering "
+ "message, but not allowed to be accepted",
+ __func__);
+ break;
+ }
+ rr_input(i, (struct icmp6_router_renum *)icp, pi, &rcvfrom,
+ &dst);
+ break;
+ default:
+ /*
+ * Note that this case is POSSIBLE, especially just
+ * after invocation of the daemon. This is because we
+ * could receive message after opening the socket and
+ * before setting ICMP6 type filter(see sock_open()).
+ */
+ syslog(LOG_ERR, "<%s> invalid icmp type(%d)",
+ __func__, icp->icmp6_type);
+ return;
+ }
+
+ return;
+}
+
+static void
+rs_input(int len, struct nd_router_solicit *rs,
+ struct in6_pktinfo *pi, struct sockaddr_in6 *from)
+{
+ u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
+ union nd_opts ndopts;
+ struct rainfo *ra;
+ struct soliciter *sol;
+
+ syslog(LOG_DEBUG,
+ "<%s> RS received from %s on %s",
+ __func__,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+
+ /* ND option check */
+ memset(&ndopts, 0, sizeof(ndopts));
+ if (nd6_options((struct nd_opt_hdr *)(rs + 1),
+ len - sizeof(struct nd_router_solicit),
+ &ndopts, NDOPT_FLAG_SRCLINKADDR)) {
+ syslog(LOG_INFO,
+ "<%s> ND option check failed for an RS from %s on %s",
+ __func__,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ /*
+ * If the IP source address is the unspecified address, there
+ * must be no source link-layer address option in the message.
+ * (RFC-2461 6.1.1)
+ */
+ if (IN6_IS_ADDR_UNSPECIFIED(&from->sin6_addr) &&
+ ndopts.nd_opts_src_lladdr) {
+ syslog(LOG_INFO,
+ "<%s> RS from unspecified src on %s has a link-layer"
+ " address option",
+ __func__,
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ goto done;
+ }
+
+ ra = ralist;
+ while (ra != NULL) {
+ if (pi->ipi6_ifindex == ra->ifindex)
+ break;
+ ra = ra->next;
+ }
+ if (ra == NULL) {
+ syslog(LOG_INFO,
+ "<%s> RS received on non advertising interface(%s)",
+ __func__,
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ goto done;
+ }
+
+ ra->rsinput++; /* increment statistics */
+
+ /*
+ * Decide whether to send RA according to the rate-limit
+ * consideration.
+ */
+
+ /* record sockaddr waiting for RA, if possible */
+ sol = (struct soliciter *)malloc(sizeof(*sol));
+ if (sol) {
+ sol->addr = *from;
+ /* XXX RFC2553 need clarification on flowinfo */
+ sol->addr.sin6_flowinfo = 0;
+ sol->next = ra->soliciter;
+ ra->soliciter = sol;
+ }
+
+ /*
+ * If there is already a waiting RS packet, don't
+ * update the timer.
+ */
+ if (ra->waiting++)
+ goto done;
+
+ set_short_delay(ra);
+
+ done:
+ free_ndopts(&ndopts);
+ return;
+}
+
+static void
+set_short_delay(rai)
+ struct rainfo *rai;
+{
+ long delay; /* must not be greater than 1000000 */
+ struct timeval interval, now, min_delay, tm_tmp, *rest;
+
+ /*
+ * Compute a random delay. If the computed value
+ * corresponds to a time later than the time the next
+ * multicast RA is scheduled to be sent, ignore the random
+ * delay and send the advertisement at the
+ * already-scheduled time. RFC-2461 6.2.6
+ */
+#ifdef HAVE_ARC4RANDOM
+ delay = arc4random_uniform(MAX_RA_DELAY_TIME);
+#else
+ delay = random() % MAX_RA_DELAY_TIME;
+#endif
+ interval.tv_sec = 0;
+ interval.tv_usec = delay;
+ rest = rtadvd_timer_rest(rai->timer);
+ if (TIMEVAL_LT(*rest, interval)) {
+ syslog(LOG_DEBUG, "<%s> random delay is larger than "
+ "the rest of the current timer", __func__);
+ interval = *rest;
+ }
+
+ /*
+ * If we sent a multicast Router Advertisement within
+ * the last MIN_DELAY_BETWEEN_RAS seconds, schedule
+ * the advertisement to be sent at a time corresponding to
+ * MIN_DELAY_BETWEEN_RAS plus the random value after the
+ * previous advertisement was sent.
+ */
+ gettimeofday(&now, NULL);
+ TIMEVAL_SUB(&now, &rai->lastsent, &tm_tmp);
+ min_delay.tv_sec = MIN_DELAY_BETWEEN_RAS;
+ min_delay.tv_usec = 0;
+ if (TIMEVAL_LT(tm_tmp, min_delay)) {
+ TIMEVAL_SUB(&min_delay, &tm_tmp, &min_delay);
+ TIMEVAL_ADD(&min_delay, &interval, &interval);
+ }
+ rtadvd_set_timer(&interval, rai->timer);
+}
+
+static void
+ra_input(int len, struct nd_router_advert *ra,
+ struct in6_pktinfo *pi, struct sockaddr_in6 *from)
+{
+ struct rainfo *rai;
+ u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
+ union nd_opts ndopts;
+ char *on_off[] = {"OFF", "ON"};
+ u_int32_t reachabletime, retranstimer, mtu;
+ int inconsistent = 0;
+
+ syslog(LOG_DEBUG,
+ "<%s> RA received from %s on %s",
+ __func__,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+
+ /* ND option check */
+ memset(&ndopts, 0, sizeof(ndopts));
+ if (nd6_options((struct nd_opt_hdr *)(ra + 1),
+ len - sizeof(struct nd_router_advert),
+ &ndopts, NDOPT_FLAG_SRCLINKADDR |
+ NDOPT_FLAG_PREFIXINFO | NDOPT_FLAG_MTU)) {
+ syslog(LOG_INFO,
+ "<%s> ND option check failed for an RA from %s on %s",
+ __func__,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ /*
+ * RA consistency check according to RFC-2461 6.2.7
+ */
+ if ((rai = if_indextorainfo(pi->ipi6_ifindex)) == 0) {
+ syslog(LOG_INFO,
+ "<%s> received RA from %s on non-advertising"
+ " interface(%s)",
+ __func__,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ goto done;
+ }
+ rai->rainput++; /* increment statistics */
+
+ /* Cur Hop Limit value */
+ if (ra->nd_ra_curhoplimit && rai->hoplimit &&
+ ra->nd_ra_curhoplimit != rai->hoplimit) {
+ syslog(LOG_INFO,
+ "<%s> CurHopLimit inconsistent on %s:"
+ " %d from %s, %d from us",
+ __func__,
+ rai->ifname,
+ ra->nd_ra_curhoplimit,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ rai->hoplimit);
+ inconsistent++;
+ }
+ /* M flag */
+ if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_MANAGED) !=
+ rai->managedflg) {
+ syslog(LOG_INFO,
+ "<%s> M flag inconsistent on %s:"
+ " %s from %s, %s from us",
+ __func__,
+ rai->ifname,
+ on_off[!rai->managedflg],
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ on_off[rai->managedflg]);
+ inconsistent++;
+ }
+ /* O flag */
+ if ((ra->nd_ra_flags_reserved & ND_RA_FLAG_OTHER) !=
+ rai->otherflg) {
+ syslog(LOG_INFO,
+ "<%s> O flag inconsistent on %s:"
+ " %s from %s, %s from us",
+ __func__,
+ rai->ifname,
+ on_off[!rai->otherflg],
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ on_off[rai->otherflg]);
+ inconsistent++;
+ }
+ /* Reachable Time */
+ reachabletime = ntohl(ra->nd_ra_reachable);
+ if (reachabletime && rai->reachabletime &&
+ reachabletime != rai->reachabletime) {
+ syslog(LOG_INFO,
+ "<%s> ReachableTime inconsistent on %s:"
+ " %d from %s, %d from us",
+ __func__,
+ rai->ifname,
+ reachabletime,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ rai->reachabletime);
+ inconsistent++;
+ }
+ /* Retrans Timer */
+ retranstimer = ntohl(ra->nd_ra_retransmit);
+ if (retranstimer && rai->retranstimer &&
+ retranstimer != rai->retranstimer) {
+ syslog(LOG_INFO,
+ "<%s> RetranceTimer inconsistent on %s:"
+ " %d from %s, %d from us",
+ __func__,
+ rai->ifname,
+ retranstimer,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ rai->retranstimer);
+ inconsistent++;
+ }
+ /* Values in the MTU options */
+ if (ndopts.nd_opts_mtu) {
+ mtu = ntohl(ndopts.nd_opts_mtu->nd_opt_mtu_mtu);
+ if (mtu && rai->linkmtu && mtu != rai->linkmtu) {
+ syslog(LOG_INFO,
+ "<%s> MTU option value inconsistent on %s:"
+ " %d from %s, %d from us",
+ __func__,
+ rai->ifname, mtu,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ rai->linkmtu);
+ inconsistent++;
+ }
+ }
+ /* Preferred and Valid Lifetimes for prefixes */
+ {
+ struct nd_optlist *optp = ndopts.nd_opts_list;
+
+ if (ndopts.nd_opts_pi) {
+ if (prefix_check(ndopts.nd_opts_pi, rai, from))
+ inconsistent++;
+ }
+ while (optp) {
+ if (prefix_check((struct nd_opt_prefix_info *)optp->opt,
+ rai, from))
+ inconsistent++;
+ optp = optp->next;
+ }
+ }
+
+ if (inconsistent)
+ rai->rainconsistent++;
+
+ done:
+ free_ndopts(&ndopts);
+ return;
+}
+
+/* return a non-zero value if the received prefix is inconsitent with ours */
+static int
+prefix_check(struct nd_opt_prefix_info *pinfo,
+ struct rainfo *rai, struct sockaddr_in6 *from)
+{
+ u_int32_t preferred_time, valid_time;
+ struct prefix *pp;
+ int inconsistent = 0;
+ u_char ntopbuf[INET6_ADDRSTRLEN], prefixbuf[INET6_ADDRSTRLEN];
+ struct timeval now;
+
+#if 0 /* impossible */
+ if (pinfo->nd_opt_pi_type != ND_OPT_PREFIX_INFORMATION)
+ return(0);
+#endif
+
+ /*
+ * log if the adveritsed prefix has link-local scope(sanity check?)
+ */
+ if (IN6_IS_ADDR_LINKLOCAL(&pinfo->nd_opt_pi_prefix)) {
+ syslog(LOG_INFO,
+ "<%s> link-local prefix %s/%d is advertised "
+ "from %s on %s",
+ __func__,
+ inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
+ prefixbuf, INET6_ADDRSTRLEN),
+ pinfo->nd_opt_pi_prefix_len,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ rai->ifname);
+ }
+
+ if ((pp = find_prefix(rai, &pinfo->nd_opt_pi_prefix,
+ pinfo->nd_opt_pi_prefix_len)) == NULL) {
+ syslog(LOG_INFO,
+ "<%s> prefix %s/%d from %s on %s is not in our list",
+ __func__,
+ inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
+ prefixbuf, INET6_ADDRSTRLEN),
+ pinfo->nd_opt_pi_prefix_len,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ rai->ifname);
+ return(0);
+ }
+
+ preferred_time = ntohl(pinfo->nd_opt_pi_preferred_time);
+ if (pp->pltimeexpire) {
+ /*
+ * The lifetime is decremented in real time, so we should
+ * compare the expiration time.
+ * (RFC 2461 Section 6.2.7.)
+ * XXX: can we really expect that all routers on the link
+ * have synchronized clocks?
+ */
+ gettimeofday(&now, NULL);
+ preferred_time += now.tv_sec;
+
+ if (!pp->timer && rai->clockskew &&
+ abs(preferred_time - pp->pltimeexpire) > rai->clockskew) {
+ syslog(LOG_INFO,
+ "<%s> preferred lifetime for %s/%d"
+ " (decr. in real time) inconsistent on %s:"
+ " %d from %s, %ld from us",
+ __func__,
+ inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
+ prefixbuf, INET6_ADDRSTRLEN),
+ pinfo->nd_opt_pi_prefix_len,
+ rai->ifname, preferred_time,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ pp->pltimeexpire);
+ inconsistent++;
+ }
+ } else if (!pp->timer && preferred_time != pp->preflifetime) {
+ syslog(LOG_INFO,
+ "<%s> preferred lifetime for %s/%d"
+ " inconsistent on %s:"
+ " %d from %s, %d from us",
+ __func__,
+ inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
+ prefixbuf, INET6_ADDRSTRLEN),
+ pinfo->nd_opt_pi_prefix_len,
+ rai->ifname, preferred_time,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ pp->preflifetime);
+ }
+
+ valid_time = ntohl(pinfo->nd_opt_pi_valid_time);
+ if (pp->vltimeexpire) {
+ gettimeofday(&now, NULL);
+ valid_time += now.tv_sec;
+
+ if (!pp->timer && rai->clockskew &&
+ abs(valid_time - pp->vltimeexpire) > rai->clockskew) {
+ syslog(LOG_INFO,
+ "<%s> valid lifetime for %s/%d"
+ " (decr. in real time) inconsistent on %s:"
+ " %d from %s, %ld from us",
+ __func__,
+ inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
+ prefixbuf, INET6_ADDRSTRLEN),
+ pinfo->nd_opt_pi_prefix_len,
+ rai->ifname, preferred_time,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ pp->vltimeexpire);
+ inconsistent++;
+ }
+ } else if (!pp->timer && valid_time != pp->validlifetime) {
+ syslog(LOG_INFO,
+ "<%s> valid lifetime for %s/%d"
+ " inconsistent on %s:"
+ " %d from %s, %d from us",
+ __func__,
+ inet_ntop(AF_INET6, &pinfo->nd_opt_pi_prefix,
+ prefixbuf, INET6_ADDRSTRLEN),
+ pinfo->nd_opt_pi_prefix_len,
+ rai->ifname, valid_time,
+ inet_ntop(AF_INET6, &from->sin6_addr,
+ ntopbuf, INET6_ADDRSTRLEN),
+ pp->validlifetime);
+ inconsistent++;
+ }
+
+ return(inconsistent);
+}
+
+struct prefix *
+find_prefix(struct rainfo *rai, struct in6_addr *prefix, int plen)
+{
+ struct prefix *pp;
+ int bytelen, bitlen;
+ u_char bitmask;
+
+ for (pp = rai->prefix.next; pp != &rai->prefix; pp = pp->next) {
+ if (plen != pp->prefixlen)
+ continue;
+ bytelen = plen / 8;
+ bitlen = plen % 8;
+ bitmask = 0xff << (8 - bitlen);
+ if (memcmp((void *)prefix, (void *)&pp->prefix, bytelen))
+ continue;
+ if (bitlen == 0 ||
+ ((prefix->s6_addr[bytelen] & bitmask) ==
+ (pp->prefix.s6_addr[bytelen] & bitmask))) {
+ return(pp);
+ }
+ }
+
+ return(NULL);
+}
+
+/* check if p0/plen0 matches p1/plen1; return 1 if matches, otherwise 0. */
+int
+prefix_match(struct in6_addr *p0, int plen0,
+ struct in6_addr *p1, int plen1)
+{
+ int bytelen, bitlen;
+ u_char bitmask;
+
+ if (plen0 < plen1)
+ return(0);
+ bytelen = plen1 / 8;
+ bitlen = plen1 % 8;
+ bitmask = 0xff << (8 - bitlen);
+ if (memcmp((void *)p0, (void *)p1, bytelen))
+ return(0);
+ if (bitlen == 0 ||
+ ((p0->s6_addr[bytelen] & bitmask) ==
+ (p1->s6_addr[bytelen] & bitmask))) {
+ return(1);
+ }
+
+ return(0);
+}
+
+static int
+nd6_options(struct nd_opt_hdr *hdr, int limit,
+ union nd_opts *ndopts, u_int32_t optflags)
+{
+ int optlen = 0;
+
+ for (; limit > 0; limit -= optlen) {
+ if (limit < sizeof(struct nd_opt_hdr)) {
+ syslog(LOG_INFO, "<%s> short option header", __func__);
+ goto bad;
+ }
+
+ hdr = (struct nd_opt_hdr *)((caddr_t)hdr + optlen);
+ if (hdr->nd_opt_len == 0) {
+ syslog(LOG_INFO,
+ "<%s> bad ND option length(0) (type = %d)",
+ __func__, hdr->nd_opt_type);
+ goto bad;
+ }
+ optlen = hdr->nd_opt_len << 3;
+ if (optlen > limit) {
+ syslog(LOG_INFO, "<%s> short option", __func__);
+ goto bad;
+ }
+
+ if (hdr->nd_opt_type > ND_OPT_MTU) {
+ syslog(LOG_INFO, "<%s> unknown ND option(type %d)",
+ __func__, hdr->nd_opt_type);
+ continue;
+ }
+
+ if ((ndopt_flags[hdr->nd_opt_type] & optflags) == 0) {
+ syslog(LOG_INFO, "<%s> unexpected ND option(type %d)",
+ __func__, hdr->nd_opt_type);
+ continue;
+ }
+
+ /*
+ * Option length check. Do it here for all fixed-length
+ * options.
+ */
+ if ((hdr->nd_opt_type == ND_OPT_MTU &&
+ (optlen != sizeof(struct nd_opt_mtu))) ||
+ ((hdr->nd_opt_type == ND_OPT_PREFIX_INFORMATION &&
+ optlen != sizeof(struct nd_opt_prefix_info)))) {
+ syslog(LOG_INFO, "<%s> invalid option length",
+ __func__);
+ continue;
+ }
+
+ switch (hdr->nd_opt_type) {
+ case ND_OPT_TARGET_LINKADDR:
+ case ND_OPT_REDIRECTED_HEADER:
+ break; /* we don't care about these options */
+ case ND_OPT_SOURCE_LINKADDR:
+ case ND_OPT_MTU:
+ if (ndopts->nd_opt_array[hdr->nd_opt_type]) {
+ syslog(LOG_INFO,
+ "<%s> duplicated ND option (type = %d)",
+ __func__, hdr->nd_opt_type);
+ }
+ ndopts->nd_opt_array[hdr->nd_opt_type] = hdr;
+ break;
+ case ND_OPT_PREFIX_INFORMATION:
+ {
+ struct nd_optlist *pfxlist;
+
+ if (ndopts->nd_opts_pi == 0) {
+ ndopts->nd_opts_pi =
+ (struct nd_opt_prefix_info *)hdr;
+ continue;
+ }
+ if ((pfxlist = malloc(sizeof(*pfxlist))) == NULL) {
+ syslog(LOG_ERR, "<%s> can't allocate memory",
+ __func__);
+ goto bad;
+ }
+ pfxlist->next = ndopts->nd_opts_list;
+ pfxlist->opt = hdr;
+ ndopts->nd_opts_list = pfxlist;
+
+ break;
+ }
+ default: /* impossible */
+ break;
+ }
+ }
+
+ return(0);
+
+ bad:
+ free_ndopts(ndopts);
+
+ return(-1);
+}
+
+static void
+free_ndopts(union nd_opts *ndopts)
+{
+ struct nd_optlist *opt = ndopts->nd_opts_list, *next;
+
+ while (opt) {
+ next = opt->next;
+ free(opt);
+ opt = next;
+ }
+}
+
+void
+sock_open()
+{
+ struct icmp6_filter filt;
+ struct ipv6_mreq mreq;
+ struct rainfo *ra = ralist;
+ int on;
+ /* XXX: should be max MTU attached to the node */
+ static u_char answer[1500];
+
+ rcvcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+ CMSG_SPACE(sizeof(int));
+ rcvcmsgbuf = (u_char *)malloc(rcvcmsgbuflen);
+ if (rcvcmsgbuf == NULL) {
+ syslog(LOG_ERR, "<%s> not enough core", __func__);
+ exit(1);
+ }
+
+ sndcmsgbuflen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+ CMSG_SPACE(sizeof(int));
+ sndcmsgbuf = (u_char *)malloc(sndcmsgbuflen);
+ if (sndcmsgbuf == NULL) {
+ syslog(LOG_ERR, "<%s> not enough core", __func__);
+ exit(1);
+ }
+
+ if ((sock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) {
+ syslog(LOG_ERR, "<%s> socket: %s", __func__,
+ strerror(errno));
+ exit(1);
+ }
+
+ /* specify to tell receiving interface */
+ on = 1;
+#ifdef IPV6_RECVPKTINFO
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
+ sizeof(on)) < 0) {
+ syslog(LOG_ERR, "<%s> IPV6_RECVPKTINFO: %s",
+ __func__, strerror(errno));
+ exit(1);
+ }
+#else /* old adv. API */
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_PKTINFO, &on,
+ sizeof(on)) < 0) {
+ syslog(LOG_ERR, "<%s> IPV6_PKTINFO: %s",
+ __func__, strerror(errno));
+ exit(1);
+ }
+#endif
+
+ on = 1;
+ /* specify to tell value of hoplimit field of received IP6 hdr */
+#ifdef IPV6_RECVHOPLIMIT
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
+ sizeof(on)) < 0) {
+ syslog(LOG_ERR, "<%s> IPV6_RECVHOPLIMIT: %s",
+ __func__, strerror(errno));
+ exit(1);
+ }
+#else /* old adv. API */
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on,
+ sizeof(on)) < 0) {
+ syslog(LOG_ERR, "<%s> IPV6_HOPLIMIT: %s",
+ __func__, strerror(errno));
+ exit(1);
+ }
+#endif
+
+ ICMP6_FILTER_SETBLOCKALL(&filt);
+ ICMP6_FILTER_SETPASS(ND_ROUTER_SOLICIT, &filt);
+ ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
+ if (accept_rr)
+ ICMP6_FILTER_SETPASS(ICMP6_ROUTER_RENUMBERING, &filt);
+ if (setsockopt(sock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
+ sizeof(filt)) < 0) {
+ syslog(LOG_ERR, "<%s> IICMP6_FILTER: %s",
+ __func__, strerror(errno));
+ exit(1);
+ }
+
+ /*
+ * join all routers multicast address on each advertising interface.
+ */
+ if (inet_pton(AF_INET6, ALLROUTERS_LINK,
+ &mreq.ipv6mr_multiaddr.s6_addr)
+ != 1) {
+ syslog(LOG_ERR, "<%s> inet_pton failed(library bug?)",
+ __func__);
+ exit(1);
+ }
+ while (ra) {
+ mreq.ipv6mr_interface = ra->ifindex;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, &mreq,
+ sizeof(mreq)) < 0) {
+ syslog(LOG_ERR, "<%s> IPV6_JOIN_GROUP(link) on %s: %s",
+ __func__, ra->ifname, strerror(errno));
+ exit(1);
+ }
+ ra = ra->next;
+ }
+
+ /*
+ * When attending router renumbering, join all-routers site-local
+ * multicast group.
+ */
+ if (accept_rr) {
+ if (inet_pton(AF_INET6, ALLROUTERS_SITE,
+ &in6a_site_allrouters) != 1) {
+ syslog(LOG_ERR, "<%s> inet_pton failed(library bug?)",
+ __func__);
+ exit(1);
+ }
+ mreq.ipv6mr_multiaddr = in6a_site_allrouters;
+ if (mcastif) {
+ if ((mreq.ipv6mr_interface = if_nametoindex(mcastif))
+ == 0) {
+ syslog(LOG_ERR,
+ "<%s> invalid interface: %s",
+ __func__, mcastif);
+ exit(1);
+ }
+ } else
+ mreq.ipv6mr_interface = ralist->ifindex;
+ if (setsockopt(sock, IPPROTO_IPV6, IPV6_JOIN_GROUP,
+ &mreq, sizeof(mreq)) < 0) {
+ syslog(LOG_ERR,
+ "<%s> IPV6_JOIN_GROUP(site) on %s: %s",
+ __func__,
+ mcastif ? mcastif : ralist->ifname,
+ strerror(errno));
+ exit(1);
+ }
+ }
+
+ /* initialize msghdr for receiving packets */
+ rcviov[0].iov_base = (caddr_t)answer;
+ rcviov[0].iov_len = sizeof(answer);
+ rcvmhdr.msg_name = (caddr_t)&rcvfrom;
+ rcvmhdr.msg_namelen = sizeof(rcvfrom);
+ rcvmhdr.msg_iov = rcviov;
+ rcvmhdr.msg_iovlen = 1;
+ rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf;
+ rcvmhdr.msg_controllen = rcvcmsgbuflen;
+
+ /* initialize msghdr for sending packets */
+ sndmhdr.msg_namelen = sizeof(struct sockaddr_in6);
+ sndmhdr.msg_iov = sndiov;
+ sndmhdr.msg_iovlen = 1;
+ sndmhdr.msg_control = (caddr_t)sndcmsgbuf;
+ sndmhdr.msg_controllen = sndcmsgbuflen;
+
+ return;
+}
+
+/* open a routing socket to watch the routing table */
+static void
+rtsock_open()
+{
+ if ((rtsock = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) {
+ syslog(LOG_ERR,
+ "<%s> socket: %s", __func__, strerror(errno));
+ exit(1);
+ }
+}
+
+struct rainfo *
+if_indextorainfo(int idx)
+{
+ struct rainfo *rai = ralist;
+
+ for (rai = ralist; rai; rai = rai->next) {
+ if (rai->ifindex == idx)
+ return(rai);
+ }
+
+ return(NULL); /* search failed */
+}
+
+static void
+ra_output(rainfo)
+struct rainfo *rainfo;
+{
+ int i;
+ struct cmsghdr *cm;
+ struct in6_pktinfo *pi;
+ struct soliciter *sol, *nextsol;
+
+ if ((iflist[rainfo->ifindex]->ifm_flags & IFF_UP) == 0) {
+ syslog(LOG_DEBUG, "<%s> %s is not up, skip sending RA",
+ __func__, rainfo->ifname);
+ return;
+ }
+
+ make_packet(rainfo); /* XXX: inefficient */
+
+ sndmhdr.msg_name = (caddr_t)&sin6_allnodes;
+ sndmhdr.msg_iov[0].iov_base = (caddr_t)rainfo->ra_data;
+ sndmhdr.msg_iov[0].iov_len = rainfo->ra_datalen;
+
+ cm = CMSG_FIRSTHDR(&sndmhdr);
+ /* specify the outgoing interface */
+ cm->cmsg_level = IPPROTO_IPV6;
+ cm->cmsg_type = IPV6_PKTINFO;
+ cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+ pi = (struct in6_pktinfo *)CMSG_DATA(cm);
+ memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/
+ pi->ipi6_ifindex = rainfo->ifindex;
+
+ /* specify the hop limit of the packet */
+ {
+ int hoplimit = 255;
+
+ cm = CMSG_NXTHDR(&sndmhdr, cm);
+ cm->cmsg_level = IPPROTO_IPV6;
+ cm->cmsg_type = IPV6_HOPLIMIT;
+ cm->cmsg_len = CMSG_LEN(sizeof(int));
+ memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int));
+ }
+
+ syslog(LOG_DEBUG,
+ "<%s> send RA on %s, # of waitings = %d",
+ __func__, rainfo->ifname, rainfo->waiting);
+
+ i = sendmsg(sock, &sndmhdr, 0);
+
+ if (i < 0 || i != rainfo->ra_datalen) {
+ if (i < 0) {
+ syslog(LOG_ERR, "<%s> sendmsg on %s: %s",
+ __func__, rainfo->ifname,
+ strerror(errno));
+ }
+ }
+ /* update counter */
+ if (rainfo->initcounter < MAX_INITIAL_RTR_ADVERTISEMENTS)
+ rainfo->initcounter++;
+ rainfo->raoutput++;
+
+ /*
+ * unicast advertisements
+ * XXX commented out. reason: though spec does not forbit it, unicast
+ * advert does not really help
+ */
+ for (sol = rainfo->soliciter; sol; sol = nextsol) {
+ nextsol = sol->next;
+
+ sol->next = NULL;
+ free(sol);
+ }
+ rainfo->soliciter = NULL;
+
+ /* update timestamp */
+ gettimeofday(&rainfo->lastsent, NULL);
+
+ /* reset waiting conter */
+ rainfo->waiting = 0;
+}
+
+/* process RA timer */
+struct rtadvd_timer *
+ra_timeout(void *data)
+{
+ struct rainfo *rai = (struct rainfo *)data;
+
+#ifdef notyet
+ /* if necessary, reconstruct the packet. */
+#endif
+
+ syslog(LOG_DEBUG,
+ "<%s> RA timer on %s is expired",
+ __func__, rai->ifname);
+
+ ra_output(rai);
+
+ return(rai->timer);
+}
+
+/* update RA timer */
+void
+ra_timer_update(void *data, struct timeval *tm)
+{
+ struct rainfo *rai = (struct rainfo *)data;
+ long interval;
+
+ /*
+ * Whenever a multicast advertisement is sent from an interface,
+ * the timer is reset to a uniformly-distributed random value
+ * between the interface's configured MinRtrAdvInterval and
+ * MaxRtrAdvInterval (RFC2461 6.2.4).
+ */
+ interval = rai->mininterval;
+#ifdef HAVE_ARC4RANDOM
+ interval += arc4random_uniform(rai->maxinterval - rai->mininterval);
+#else
+ interval += random() % (rai->maxinterval - rai->mininterval);
+#endif
+
+ /*
+ * For the first few advertisements (up to
+ * MAX_INITIAL_RTR_ADVERTISEMENTS), if the randomly chosen interval
+ * is greater than MAX_INITIAL_RTR_ADVERT_INTERVAL, the timer
+ * SHOULD be set to MAX_INITIAL_RTR_ADVERT_INTERVAL instead.
+ * (RFC-2461 6.2.4)
+ */
+ if (rai->initcounter < MAX_INITIAL_RTR_ADVERTISEMENTS &&
+ interval > MAX_INITIAL_RTR_ADVERT_INTERVAL)
+ interval = MAX_INITIAL_RTR_ADVERT_INTERVAL;
+
+ tm->tv_sec = interval;
+ tm->tv_usec = 0;
+
+ syslog(LOG_DEBUG,
+ "<%s> RA timer on %s is set to %ld:%ld",
+ __func__, rai->ifname,
+ (long int)tm->tv_sec, (long int)tm->tv_usec);
+
+ return;
+}
diff --git a/usr.sbin/rtadvd/rtadvd.conf b/usr.sbin/rtadvd/rtadvd.conf
new file mode 100644
index 0000000..33ab7f3
--- /dev/null
+++ b/usr.sbin/rtadvd/rtadvd.conf
@@ -0,0 +1,21 @@
+# $FreeBSD$
+# $KAME: rtadvd.conf,v 1.13 2003/06/25 03:45:21 itojun Exp $
+#
+# Note: All of the following parameters have default values defined
+# in specifications, and hence you usually do not have to set them
+# by hand unless you need special non-default values.
+#
+# You even do not need to create the configuration file. rtadvd
+# would usually work well without a configuration file.
+# See also: rtadvd(8)
+
+# per-interface definitions.
+# Mainly IPv6 prefixes are configured in this part. However, rtadvd
+# automatically learns appropriate prefixes from the kernel's routing
+# table, and advertises the prefixes, so you don't have to configure
+# this part, either.
+# If you don't want the automatic advertisement, (uncomment and) configure
+# this part by hand, and then invoke rtadvd with the -s option.
+
+#ef0:\
+# :addr="3ffe:501:ffff:1000::":prefixlen#64:
diff --git a/usr.sbin/rtadvd/rtadvd.conf.5 b/usr.sbin/rtadvd/rtadvd.conf.5
new file mode 100644
index 0000000..dcc73cc
--- /dev/null
+++ b/usr.sbin/rtadvd/rtadvd.conf.5
@@ -0,0 +1,427 @@
+.\" $KAME: rtadvd.conf.5,v 1.50 2005/01/14 05:30:59 jinmei 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 RTADVD.CONF 5
+.Os
+.Sh NAME
+.Nm rtadvd.conf
+.Nd config file for router advertisement daemon
+.Sh DESCRIPTION
+This file describes how the router advertisement packets must be constructed
+for each of the interfaces.
+.Pp
+As described in
+.Xr rtadvd 8 ,
+you do not have to set this configuration file up at all,
+unless you need some special configurations.
+You may even omit the file as a whole.
+In such cases, the
+.Nm rtadvd
+daemon will automatically configure itself using default values
+specified in the specification.
+.Pp
+It obeys the famous
+.Xr termcap 5
+file format.
+Each line in the file describes a network interface.
+Fields are separated by a colon
+.Pq Sq \&: ,
+and each field contains one capability description.
+Lines may be concatenated by the
+.Sq \e
+character.
+The comment marker is the
+.Sq \&#
+character.
+.Sh CAPABILITIES
+Capabilities describe the value to be filled into ICMPv6 router
+advertisement messages and to control
+.Xr rtadvd 8
+behavior.
+Therefore, you are encouraged to read IETF neighbor discovery documents
+if you would like to modify the sample configuration file.
+.Pp
+Note that almost all items have default values.
+If you omit an item, the default value of the item will be used.
+.Pp
+There are two items which control the interval of sending router advertisements.
+These items can be omitted, then
+.Nm rtadvd
+will use the default values.
+.Bl -tag -width indent
+.It Cm \&maxinterval
+(num) The maximum time allowed between sending unsolicited
+multicast router advertisements
+.Pq unit: seconds .
+The default value is 600.
+Its value must be no less than 4 seconds
+and no greater than 1800 seconds.
+.It Cm \&mininterval
+(num) The minimum time allowed between sending unsolicited multicast
+router advertisements
+.Pq unit: seconds .
+The default value is the one third of value of
+.Cm maxinterval .
+Its value must be no less than 3 seconds and no greater than .75 *
+the value of
+.Cm maxinterval .
+.El
+.Pp
+The following items are for ICMPv6 router advertisement message
+header.
+These items can be omitted, then
+.Nm rtadvd
+will use the default values.
+.Bl -tag -width indent
+.It Cm \&chlim
+(num) The value for Cur Hop Limit field.
+The default value is 64.
+.It Cm \&raflags
+(str or num) A 8-bit flags field in router advertisement message header.
+This field can be specified either as a case-sensitive string or as an
+integer.
+A sting consists of characters each of which corresponds to a
+particular flag bit(s).
+An integer should be the logical OR of all enabled bits.
+Bit 7
+.Po
+.Li 'm' or 0x80
+.Pc
+means Managed address configuration flag bit,
+and Bit 6
+.Po
+.Li 'o' or 0x40
+.Pc
+means Other stateful configuration flag bit.
+Bit 4
+.Po
+.Li 0x10
+.Pc
+and Bit 3
+.Po
+.Li 0x08
+.Pc
+are used to encode router preference.
+Bits 01
+.Po
+or 'h'
+.Pc
+means high, 00 means medium, and 11
+.Po
+or 'l'
+.Pc
+means low.
+Bits 10 is reserved, and must not be specified.
+There is no character to specify the medium preference explicitly.
+The default value of the entire flag is 0
+.Po
+or a null string,
+.Pc
+which means no additional
+configuration methods, and the medium router preference.
+.It Cm \&rltime
+(num) Router lifetime field
+.Pq unit: seconds .
+The value must be either zero or between
+the value of
+.Cm maxinterval
+and 9000.
+When
+.Nm rtadvd
+runs on a host, this value must explicitly set 0 on all the
+advertising interfaces as described in
+.Xr rtadvd 8 .
+The default value is 1800.
+.It Cm \&rtime
+(num) Reachable time field
+.Pq unit: milliseconds .
+The default value is 0, which means unspecified by this router.
+.It Cm \&retrans
+(num) Retrans Timer field
+.Pq unit: milliseconds .
+The default value is 0, which means unspecified by this router.
+.El
+.Pp
+The following items are for ICMPv6 prefix information option,
+which will be attached to router advertisement header.
+These items can be omitted, then
+.Nm rtadvd
+will automatically get appropriate prefixes from the kernel's routing table,
+and advertise the prefixes with the default parameters.
+Keywords other than
+.Cm clockskew
+can be augmented with a number, like
+.Dq Li prefix2 ,
+to specify multiple prefixes.
+.Bl -tag -width indent
+.It Cm \&clockskew
+(num) Time skew to adjust link propagation delays and clock skews
+between routers on the link
+.Pq unit: seconds .
+This value is used in consistency check for locally-configured and
+advertised prefix lifetimes, and has its meaning when the local router
+configures a prefix on the link with a lifetime that decrements in
+real time.
+If the value is 0, it means the consistency check will be skipped
+for such prefixes.
+The default value is 0.
+.It Cm \&prefixlen
+(num) Prefix length field.
+The default value is 64.
+.It Cm \&pinfoflags
+(str or num) A 8-bit flags field in prefix information option.
+This field can be specified either as a case-sensitive string or as an
+integer.
+A sting consists of characters each of which corresponds to a
+particular flag bit(s).
+An integer should be the logical OR of all enabled bits.
+Bit 7
+.Po
+.Li 'l' or 0x80
+.Pc
+means On-link flag bit,
+and Bit 6
+.Po
+.Li 'a' or 0x40
+.Pc
+means Autonomous address-configuration flag bit.
+The default value is "la" or 0xc0, i.e., both bits are set.
+.It Cm \&addr
+(str) The address filled into Prefix field.
+Since
+.Dq \&:
+is used for
+.Xr termcap 5
+file format as well as IPv6 numeric address, the field MUST be quoted by
+doublequote character.
+.It Cm \&vltime
+(num) Valid lifetime field
+.Pq unit: seconds .
+The default value is 2592000 (30 days).
+.It Cm \&vltimedecr
+(bool) This item means the advertised valid lifetime will decrement
+in real time, which is disabled by default.
+.It Cm \&pltime
+(num) Preferred lifetime field
+.Pq unit: seconds .
+The default value is 604800 (7 days).
+.It Cm \&pltimedecr
+(bool) This item means the advertised preferred lifetime will decrement
+in real time, which is disabled by default.
+.El
+.Pp
+The following item is for ICMPv6 MTU option,
+which will be attached to router advertisement header.
+This item can be omitted, then
+.Nm rtadvd
+will use the default value.
+.Bl -tag -width indent
+.It Cm \&mtu
+(num or str) MTU (maximum transmission unit) field.
+If 0 is specified, it means that the option will not be included.
+The default value is 0.
+If the special string
+.Dq auto
+is specified for this item, MTU option will be included and its value
+will be set to the interface MTU automatically.
+.El
+.Pp
+The following item controls ICMPv6 source link-layer address option,
+which will be attached to router advertisement header.
+As noted above, you can just omit the item, then
+.Nm rtadvd
+will use the default value.
+.Bl -tag -width indent
+.It Cm \&nolladdr
+(bool) By default
+.Po
+if
+.Cm \&nolladdr
+is not specified
+.Pc ,
+.Xr rtadvd 8
+will try to get link-layer address for the interface from the kernel,
+and attach that in source link-layer address option.
+If this capability exists,
+.Xr rtadvd 8
+will not attach source link-layer address option to
+router advertisement packets.
+.El
+.Pp
+The following item controls ICMPv6 home agent information option,
+which was defined with mobile IPv6 support.
+It will be attached to router advertisement header just like other options do.
+.Bl -tag -width indent
+.It Cm \&hapref
+(num) Specifies home agent preference.
+If set to non-zero,
+.Cm \&hatime
+must be present as well.
+.It Cm \&hatime
+(num) Specifies home agent lifetime.
+.El
+.Pp
+When mobile IPv6 support is turned on for
+.Xr rtadvd 8 ,
+advertisement interval option will be attached to router advertisement
+packet, by configuring
+.Cm \&maxinterval
+explicitly.
+.Pp
+The following items are for ICMPv6 route information option,
+which will be attached to router advertisement header.
+These items are optional.
+Each items can be augmented with number, like
+.Dq Li rtplen2 ,
+to specify multiple routes.
+.Bl -tag -width indent
+.It Cm \&rtprefix
+(str) The prefix filled into the Prefix field of route information option.
+Since
+.Dq \&:
+is used for
+.Xr termcap 5
+file format as well as IPv6 numeric address, the field MUST be quoted by
+doublequote character.
+.It Cm \&rtplen
+(num) Prefix length field in route information option.
+The default value is 64.
+.It Cm \&rtflags
+(str or num) A 8-bit flags field in route information option.
+Currently only the preference values are defined.
+The notation is same as that of the raflags field.
+Bit 4
+.Po
+.Li 0x10
+.Pc
+and
+Bit 3
+.Po
+.Li 0x08
+.Pc
+are used to encode the route preference for the route.
+The default value is 0x00, i.e., medium preference.
+.It Cm \&rtltime
+(num) route lifetime field in route information option.
+.Pq unit: seconds .
+Since the specification does not define the default value of this
+item, the value for this item should be specified by hand.
+However,
+.Nm rtadvd
+allows this item to be unspecified, and uses the router lifetime
+as the default value in such a case, just for compatibility with an
+old version of the program.
+.El
+.Pp
+In the above list, each keyword beginning with
+.Dq Li rt
+could be replaced with the one beginning with
+.Dq Li rtr
+for backward compatibility reason.
+For example,
+.Cm rtrplen
+is accepted instead of
+.Cm rtplen .
+However, keywords that start with
+.Dq Li rtr
+have basically been obsoleted, and should not be used any more.
+.Pp
+You can also refer one line from another by using
+.Cm tc
+capability.
+See
+.Xr termcap 5
+for details on the capability.
+.Sh EXAMPLES
+As presented above, all of the advertised parameters have default values
+defined in specifications, and hence you usually do not have to set them
+by hand, unless you need special non-default values.
+It can cause interoperability problem if you use an ill-configured
+parameter.
+.Pp
+To override a configuration parameter, you can specify the parameter alone.
+With the following configuration,
+.Xr rtadvd 8
+overrides the router lifetime parameter for the
+.Li ne0
+interface.
+.Bd -literal -offset
+ne0:\\
+ :rltime#0:
+.Ed
+.Pp
+The following example manually configures prefixes advertised from the
+.Li ef0
+interface.
+The configuration must be used with the
+.Fl s
+option to
+.Xr rtadvd 8 .
+.Bd -literal -offset
+ef0:\\
+ :addr="3ffe:501:ffff:1000::":prefixlen#64:
+.Ed
+.Pp
+The following example presents the default values in an explicit manner.
+The configuration is provided just for reference purposes;
+YOU DO NOT NEED TO HAVE IT AT ALL.
+.Bd -literal -offset
+default:\\
+ :chlim#64:raflags#0:rltime#1800:rtime#0:retrans#0:\\
+ :pinfoflags="la":vltime#2592000:pltime#604800:mtu#0:
+ef0:\\
+ :addr="3ffe:501:ffff:1000::":prefixlen#64:tc=default:
+.Ed
+.Sh SEE ALSO
+.Xr termcap 5 ,
+.Xr rtadvd 8 ,
+.Xr rtsol 8
+.Rs
+.%A Thomas Narten
+.%A Erik Nordmark
+.%A W. A. Simpson
+.%T Neighbor Discovery for IP version 6 (IPv6)
+.%R RFC 2461
+.Re
+.Rs
+.%A Richard Draves
+.%T Default Router Preferences and More-Specific Routes
+.%R draft-ietf-ipngwg-router-selection-xx.txt
+.Re
+.Sh HISTORY
+The
+.Xr rtadvd 8
+and the configuration file
+.Nm
+first appeared in WIDE Hydrangea IPv6 protocol stack kit.
+.\" .Sh BUGS
+.\" (to be written)
diff --git a/usr.sbin/rtadvd/rtadvd.h b/usr.sbin/rtadvd/rtadvd.h
new file mode 100644
index 0000000..828fec6
--- /dev/null
+++ b/usr.sbin/rtadvd/rtadvd.h
@@ -0,0 +1,161 @@
+/* $FreeBSD$ */
+/* $KAME: rtadvd.h,v 1.26 2003/08/05 12:34:23 itojun Exp $ */
+
+/*
+ * Copyright (C) 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.
+ */
+
+#define ALLNODES "ff02::1"
+#define ALLROUTERS_LINK "ff02::2"
+#define ALLROUTERS_SITE "ff05::2"
+#define ANY "::"
+#define RTSOLLEN 8
+
+/* protocol constants and default values */
+#define DEF_MAXRTRADVINTERVAL 600
+#define DEF_ADVLINKMTU 0
+#define DEF_ADVREACHABLETIME 0
+#define DEF_ADVRETRANSTIMER 0
+#define DEF_ADVCURHOPLIMIT 64
+#define DEF_ADVVALIDLIFETIME 2592000
+#define DEF_ADVPREFERREDLIFETIME 604800
+
+#define MAXROUTERLIFETIME 9000
+#define MIN_MAXINTERVAL 4
+#define MAX_MAXINTERVAL 1800
+#define MIN_MININTERVAL 3
+#define MAXREACHABLETIME 3600000
+
+#define MAX_INITIAL_RTR_ADVERT_INTERVAL 16
+#define MAX_INITIAL_RTR_ADVERTISEMENTS 3
+#define MAX_FINAL_RTR_ADVERTISEMENTS 3
+#define MIN_DELAY_BETWEEN_RAS 3
+#define MAX_RA_DELAY_TIME 500000 /* usec */
+
+#define PREFIX_FROM_KERNEL 1
+#define PREFIX_FROM_CONFIG 2
+#define PREFIX_FROM_DYNAMIC 3
+
+struct prefix {
+ struct prefix *next; /* forward link */
+ struct prefix *prev; /* previous link */
+
+ struct rainfo *rainfo; /* back pointer to the interface */
+
+ struct rtadvd_timer *timer; /* expiration timer. used when a prefix
+ * derived from the kernel is deleted.
+ */
+
+ u_int32_t validlifetime; /* AdvValidLifetime */
+ long vltimeexpire; /* expiration of vltime; decrement case only */
+ u_int32_t preflifetime; /* AdvPreferredLifetime */
+ long pltimeexpire; /* expiration of pltime; decrement case only */
+ u_int onlinkflg; /* bool: AdvOnLinkFlag */
+ u_int autoconfflg; /* bool: AdvAutonomousFlag */
+ int prefixlen;
+ int origin; /* from kernel or config */
+ struct in6_addr prefix;
+};
+
+#ifdef ROUTEINFO
+struct rtinfo {
+ struct rtinfo *prev; /* previous link */
+ struct rtinfo *next; /* forward link */
+
+ u_int32_t ltime; /* route lifetime */
+ u_int rtpref; /* route preference */
+ int prefixlen;
+ struct in6_addr prefix;
+};
+#endif
+
+struct soliciter {
+ struct soliciter *next;
+ struct sockaddr_in6 addr;
+};
+
+struct rainfo {
+ /* pointer for list */
+ struct rainfo *next;
+
+ /* timer related parameters */
+ struct rtadvd_timer *timer;
+ int initcounter; /* counter for the first few advertisements */
+ struct timeval lastsent; /* timestamp when the latest RA was sent */
+ int waiting; /* number of RS waiting for RA */
+
+ /* interface information */
+ int ifindex;
+ int advlinkopt; /* bool: whether include link-layer addr opt */
+ struct sockaddr_dl *sdl;
+ char ifname[16];
+ int phymtu; /* mtu of the physical interface */
+
+ /* Router configuration variables */
+ u_short lifetime; /* AdvDefaultLifetime */
+ u_int maxinterval; /* MaxRtrAdvInterval */
+ u_int mininterval; /* MinRtrAdvInterval */
+ int managedflg; /* AdvManagedFlag */
+ int otherflg; /* AdvOtherConfigFlag */
+
+ int rtpref; /* router preference */
+ u_int32_t linkmtu; /* AdvLinkMTU */
+ u_int32_t reachabletime; /* AdvReachableTime */
+ u_int32_t retranstimer; /* AdvRetransTimer */
+ u_int hoplimit; /* AdvCurHopLimit */
+ struct prefix prefix; /* AdvPrefixList(link head) */
+ int pfxs; /* number of prefixes */
+ long clockskew; /* used for consisitency check of lifetimes */
+
+#ifdef ROUTEINFO
+ struct rtinfo route; /* route information option (link head) */
+ int routes; /* number of route information options */
+#endif
+
+ /* actual RA packet data and its length */
+ size_t ra_datalen;
+ u_char *ra_data;
+
+ /* statistics */
+ u_quad_t raoutput; /* number of RAs sent */
+ u_quad_t rainput; /* number of RAs received */
+ u_quad_t rainconsistent; /* number of RAs inconsistent with ours */
+ u_quad_t rsinput; /* number of RSs received */
+
+ /* info about soliciter */
+ struct soliciter *soliciter; /* recent solication source */
+};
+
+struct rtadvd_timer *ra_timeout(void *);
+void ra_timer_update(void *, struct timeval *);
+
+int prefix_match(struct in6_addr *, int, struct in6_addr *, int);
+struct rainfo *if_indextorainfo(int);
+struct prefix *find_prefix(struct rainfo *, struct in6_addr *, int);
+
+extern struct in6_addr in6a_site_allrouters;
diff --git a/usr.sbin/rtadvd/timer.c b/usr.sbin/rtadvd/timer.c
new file mode 100644
index 0000000..7fb0902
--- /dev/null
+++ b/usr.sbin/rtadvd/timer.c
@@ -0,0 +1,209 @@
+/* $FreeBSD$ */
+/* $KAME: timer.c,v 1.9 2002/06/10 19:59:47 itojun Exp $ */
+
+/*
+ * Copyright (C) 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.
+ */
+
+#include <sys/time.h>
+
+#include <unistd.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <string.h>
+#include <search.h>
+#include "timer.h"
+
+static struct rtadvd_timer timer_head;
+
+#define MILLION 1000000
+#define TIMEVAL_EQUAL(t1,t2) ((t1)->tv_sec == (t2)->tv_sec &&\
+ (t1)->tv_usec == (t2)->tv_usec)
+
+static struct timeval tm_max = {0x7fffffff, 0x7fffffff};
+
+void
+rtadvd_timer_init()
+{
+ memset(&timer_head, 0, sizeof(timer_head));
+
+ timer_head.next = timer_head.prev = &timer_head;
+ timer_head.tm = tm_max;
+}
+
+struct rtadvd_timer *
+rtadvd_add_timer(struct rtadvd_timer *(*timeout)(void *),
+ void (*update)(void *, struct timeval *),
+ void *timeodata, void *updatedata)
+{
+ struct rtadvd_timer *newtimer;
+
+ if ((newtimer = malloc(sizeof(*newtimer))) == NULL) {
+ syslog(LOG_ERR,
+ "<%s> can't allocate memory", __func__);
+ exit(1);
+ }
+
+ memset(newtimer, 0, sizeof(*newtimer));
+
+ if (timeout == NULL) {
+ syslog(LOG_ERR,
+ "<%s> timeout function unspecified", __func__);
+ exit(1);
+ }
+ newtimer->expire = timeout;
+ newtimer->update = update;
+ newtimer->expire_data = timeodata;
+ newtimer->update_data = updatedata;
+ newtimer->tm = tm_max;
+
+ /* link into chain */
+ insque(newtimer, &timer_head);
+
+ return(newtimer);
+}
+
+void
+rtadvd_remove_timer(struct rtadvd_timer **timer)
+{
+ remque(*timer);
+ free(*timer);
+ *timer = NULL;
+}
+
+void
+rtadvd_set_timer(struct timeval *tm, struct rtadvd_timer *timer)
+{
+ struct timeval now;
+
+ /* reset the timer */
+ gettimeofday(&now, NULL);
+
+ TIMEVAL_ADD(&now, tm, &timer->tm);
+
+ /* update the next expiration time */
+ if (TIMEVAL_LT(timer->tm, timer_head.tm))
+ timer_head.tm = timer->tm;
+
+ return;
+}
+
+/*
+ * Check expiration for each timer. If a timer expires,
+ * call the expire function for the timer and update the timer.
+ * Return the next interval for select() call.
+ */
+struct timeval *
+rtadvd_check_timer()
+{
+ static struct timeval returnval;
+ struct timeval now;
+ struct rtadvd_timer *tm = timer_head.next, *tm_next;
+
+ gettimeofday(&now, NULL);
+
+ timer_head.tm = tm_max;
+
+ for (tm = timer_head.next; tm != &timer_head; tm = tm_next) {
+ tm_next = tm->next;
+
+ if (TIMEVAL_LEQ(tm->tm, now)) {
+ if (((*tm->expire)(tm->expire_data) == NULL))
+ continue; /* the timer was removed */
+ if (tm->update)
+ (*tm->update)(tm->update_data, &tm->tm);
+ TIMEVAL_ADD(&tm->tm, &now, &tm->tm);
+ }
+
+ if (TIMEVAL_LT(tm->tm, timer_head.tm))
+ timer_head.tm = tm->tm;
+ }
+
+ if (TIMEVAL_EQUAL(&tm_max, &timer_head.tm)) {
+ /* no need to timeout */
+ return(NULL);
+ } else if (TIMEVAL_LT(timer_head.tm, now)) {
+ /* this may occur when the interval is too small */
+ returnval.tv_sec = returnval.tv_usec = 0;
+ } else
+ TIMEVAL_SUB(&timer_head.tm, &now, &returnval);
+ return(&returnval);
+}
+
+struct timeval *
+rtadvd_timer_rest(struct rtadvd_timer *timer)
+{
+ static struct timeval returnval, now;
+
+ gettimeofday(&now, NULL);
+ if (TIMEVAL_LEQ(timer->tm, now)) {
+ syslog(LOG_DEBUG,
+ "<%s> a timer must be expired, but not yet",
+ __func__);
+ returnval.tv_sec = returnval.tv_usec = 0;
+ }
+ else
+ TIMEVAL_SUB(&timer->tm, &now, &returnval);
+
+ return(&returnval);
+}
+
+/* result = a + b */
+void
+TIMEVAL_ADD(struct timeval *a, struct timeval *b, struct timeval *result)
+{
+ long l;
+
+ if ((l = a->tv_usec + b->tv_usec) < MILLION) {
+ result->tv_usec = l;
+ result->tv_sec = a->tv_sec + b->tv_sec;
+ }
+ else {
+ result->tv_usec = l - MILLION;
+ result->tv_sec = a->tv_sec + b->tv_sec + 1;
+ }
+}
+
+/*
+ * result = a - b
+ * XXX: this function assumes that a >= b.
+ */
+void
+TIMEVAL_SUB(struct timeval *a, struct timeval *b, struct timeval *result)
+{
+ long l;
+
+ if ((l = a->tv_usec - b->tv_usec) >= 0) {
+ result->tv_usec = l;
+ result->tv_sec = a->tv_sec - b->tv_sec;
+ }
+ else {
+ result->tv_usec = MILLION + l;
+ result->tv_sec = a->tv_sec - b->tv_sec - 1;
+ }
+}
diff --git a/usr.sbin/rtadvd/timer.h b/usr.sbin/rtadvd/timer.h
new file mode 100644
index 0000000..4526103
--- /dev/null
+++ b/usr.sbin/rtadvd/timer.h
@@ -0,0 +1,65 @@
+/* $FreeBSD$ */
+/* $KAME: timer.h,v 1.5 2002/05/31 13:30:38 jinmei Exp $ */
+
+/*
+ * Copyright (C) 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.
+ */
+
+/* a < b */
+#define TIMEVAL_LT(a, b) (((a).tv_sec < (b).tv_sec) ||\
+ (((a).tv_sec == (b).tv_sec) && \
+ ((a).tv_usec < (b).tv_usec)))
+
+/* a <= b */
+#define TIMEVAL_LEQ(a, b) (((a).tv_sec < (b).tv_sec) ||\
+ (((a).tv_sec == (b).tv_sec) &&\
+ ((a).tv_usec <= (b).tv_usec)))
+
+struct rtadvd_timer {
+ struct rtadvd_timer *next;
+ struct rtadvd_timer *prev;
+ struct rainfo *rai;
+ struct timeval tm;
+
+ struct rtadvd_timer *(*expire)(void *); /* expiration function */
+ void *expire_data;
+ void (*update)(void *, struct timeval *); /* update function */
+ void *update_data;
+};
+
+void rtadvd_timer_init(void);
+struct rtadvd_timer *rtadvd_add_timer(struct rtadvd_timer *(*)(void *),
+ void (*)(void *, struct timeval *), void *, void *);
+void rtadvd_set_timer(struct timeval *, struct rtadvd_timer *);
+void rtadvd_remove_timer(struct rtadvd_timer **);
+struct timeval * rtadvd_check_timer(void);
+struct timeval * rtadvd_timer_rest(struct rtadvd_timer *);
+void TIMEVAL_ADD(struct timeval *, struct timeval *,
+ struct timeval *);
+void TIMEVAL_SUB(struct timeval *, struct timeval *,
+ struct timeval *);
diff --git a/usr.sbin/rtprio/Makefile b/usr.sbin/rtprio/Makefile
new file mode 100644
index 0000000..ebc7bd1
--- /dev/null
+++ b/usr.sbin/rtprio/Makefile
@@ -0,0 +1,8 @@
+# from: @(#)Makefile 5.5 (Berkeley) 5/11/90
+# $FreeBSD$
+
+PROG= rtprio
+LINKS= ${BINDIR}/rtprio ${BINDIR}/idprio
+MLINKS= rtprio.1 idprio.1
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rtprio/rtprio.1 b/usr.sbin/rtprio/rtprio.1
new file mode 100644
index 0000000..3141fba
--- /dev/null
+++ b/usr.sbin/rtprio/rtprio.1
@@ -0,0 +1,192 @@
+.\"
+.\" Copyright (c) 1994, Henrik Vestergaard Draboel
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 Henrik Vestergaard Draboel.
+.\" 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 AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED 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 23, 1994
+.Dt RTPRIO 1
+.Os
+.Sh NAME
+.Nm rtprio ,
+.Nm idprio
+.Nd execute, examine or modify a utility's or process's realtime
+or idletime scheduling priority
+.Sh SYNOPSIS
+.Nm [id|rt]prio
+.Nm [id|rt]prio
+.Oo Fl Oc Ns Ar pid
+.Nm [id|rt]prio
+.Ar priority
+.Ar command
+.Op args
+.Nm [id|rt]prio
+.Ar priority
+.Fl Ar pid
+.Nm [id|rt]prio
+.Fl t
+.Ar command
+.Op args
+.Nm [id|rt]prio
+.Fl t
+.Fl Ar pid
+.Sh DESCRIPTION
+The
+.Nm
+utility is used for controlling realtime process scheduling.
+.Pp
+The
+.Nm idprio
+utility is used for controlling idletime process scheduling, and can be called
+with the same options as
+.Nm .
+.Pp
+A process with a realtime priority is not subject to priority
+degradation, and will only be preempted by another process of equal or
+higher realtime priority.
+.Pp
+A process with an idle priority will run only when no other
+process is runnable and then only if its idle priority is equal or
+greater than all other runnable idle priority processes.
+.Pp
+Both
+.Nm
+or
+.Nm idprio
+when called without arguments will return the realtime priority
+of the current process.
+.Pp
+If
+.Nm
+is called with 1 argument, it will return the realtime priority
+of the process with the specified
+.Ar pid .
+.Pp
+If
+.Ar priority
+is specified, the process or program is run at that realtime priority.
+If
+.Fl t
+is specified, the process or program is run as a normal (non-realtime)
+process.
+.Pp
+If
+.Ar -pid
+is specified, the process with the process identifier
+.Ar pid
+will be modified, else if
+.Ar command
+is specified, that program is run with its arguments.
+.Pp
+.Ar Priority
+is an integer between 0 and RTP_PRIO_MAX (usually 31).
+0 is the
+highest priority
+.Pp
+.Ar Pid
+of 0 means "the current process".
+.Pp
+Only root is allowed to set realtime or idle priority for a process.
+.Sh EXIT STATUS
+If
+.Nm
+execute a command, the exit value is that of the command executed.
+In all other cases,
+.Nm
+exits 0 on success, and 1 for all other errors.
+.Sh EXAMPLES
+To see which realtime priority the current process is at:
+.Dl rtprio
+.Pp
+To see which realtime priority of process 1423:
+.Dl "rtprio 1423"
+.Pp
+To run
+.Xr cron 8
+at the lowest realtime priority:
+.Dl "rtprio 31 cron"
+.Pp
+To change the realtime priority of process 1423 to 16:
+.Dl "rtprio 16 -1423"
+.Pp
+To run
+.Xr tcpdump 1
+without realtime priority:
+.Dl "rtprio -t tcpdump"
+.Pp
+To change the realtime priority of process 1423
+to
+.Dv RTP_PRIO_NORMAL
+(non-realtime/normal priority):
+.Dl "rtprio -t -1423"
+.Pp
+To make depend while not disturbing other machine usage:
+.Dl "idprio 31 make depend"
+.Sh SEE ALSO
+.Xr nice 1 ,
+.Xr ps 1 ,
+.Xr rtprio 2 ,
+.Xr setpriority 2 ,
+.Xr nice 3 ,
+.Xr renice 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 2.0 ,
+but is similar to the HP-UX version.
+.Sh CAVEATS
+You can lock yourself out of the system by placing a cpu-heavy
+process in a realtime priority.
+.Sh AUTHORS
+.An -nosplit
+.An Henrik Vestergaard Draboel Aq hvd@terry.ping.dk
+is the original author.
+This
+implementation in
+.Fx
+was substantially rewritten by
+.An David Greenman .
+.Sh BUGS
+There is no way to set/view the realtime priority of process 0
+(swapper) (see
+.Xr ps 1 ) .
+.Pp
+There is in
+.Fx
+no way to ensure that a process page is present in memory therefore
+the process may be stopped for pagein (see
+.Xr mprotect 2 ,
+.Xr madvise 2 ) .
+.Pp
+Under
+.Fx
+system calls are currently never preempted, therefore non-realtime
+processes can starve realtime processes, or idletime processes can
+starve normal priority processes.
diff --git a/usr.sbin/rtprio/rtprio.c b/usr.sbin/rtprio/rtprio.c
new file mode 100644
index 0000000..245f714
--- /dev/null
+++ b/usr.sbin/rtprio/rtprio.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 1994 David Greenman
+ * Copyright (c) 1994 Henrik Vestergaard Draboel (hvd@terry.ping.dk)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Henrik Vestergaard Draboel.
+ * This product includes software developed by David Greenman.
+ * 4. Neither the names of the authors nor the names of 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 AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED 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/rtprio.h>
+#include <sys/errno.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static void usage();
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char *p;
+ int proc = 0;
+ struct rtprio rtp;
+
+ /* find basename */
+ if ((p = rindex(argv[0], '/')) == NULL)
+ p = argv[0];
+ else
+ ++p;
+
+ if (!strcmp(p, "rtprio"))
+ rtp.type = RTP_PRIO_REALTIME;
+ else if (!strcmp(p, "idprio"))
+ rtp.type = RTP_PRIO_IDLE;
+
+ switch (argc) {
+ case 2:
+ proc = abs(atoi(argv[1])); /* Should check if numeric
+ * arg! */
+ /* FALLTHROUGH */
+ case 1:
+ if (rtprio(RTP_LOOKUP, proc, &rtp) != 0)
+ err(1, "%s", argv[0]);
+ printf("%s: ", p);
+ switch (rtp.type) {
+ case RTP_PRIO_REALTIME:
+ case RTP_PRIO_FIFO:
+ printf("realtime priority %d\n", rtp.prio);
+ break;
+ case RTP_PRIO_NORMAL:
+ printf("normal priority\n");
+ break;
+ case RTP_PRIO_IDLE:
+ printf("idle priority %d\n", rtp.prio);
+ break;
+ default:
+ printf("invalid priority type %d\n", rtp.type);
+ break;
+ }
+ exit(0);
+ default:
+ if (argv[1][0] == '-' || isdigit(argv[1][0])) {
+ if (argv[1][0] == '-') {
+ if (strcmp(argv[1], "-t") == 0) {
+ rtp.type = RTP_PRIO_NORMAL;
+ rtp.prio = 0;
+ } else {
+ usage();
+ break;
+ }
+ } else {
+ rtp.prio = atoi(argv[1]);
+ }
+ } else {
+ usage();
+ break;
+ }
+
+ if (argv[2][0] == '-')
+ proc = -atoi(argv[2]);
+
+ if (rtprio(RTP_SET, proc, &rtp) != 0)
+ err(1, "%s", argv[0]);
+
+ if (proc == 0) {
+ execvp(argv[2], &argv[2]);
+ err(1, "%s", argv[2]);
+ }
+ exit(0);
+ }
+ exit (1);
+}
+
+static void
+usage()
+{
+ (void) fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n",
+ "usage: [id|rt]prio",
+ " [id|rt]prio [-]pid",
+ " [id|rt]prio priority command [args]",
+ " [id|rt]prio priority -pid",
+ " [id|rt]prio -t command [args]",
+ " [id|rt]prio -t -pid");
+ exit(1);
+}
diff --git a/usr.sbin/rtsold/Makefile b/usr.sbin/rtsold/Makefile
new file mode 100644
index 0000000..1afa0f6
--- /dev/null
+++ b/usr.sbin/rtsold/Makefile
@@ -0,0 +1,27 @@
+# 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$
+
+PROG= rtsold
+MAN= rtsold.8
+MLINKS= rtsold.8 rtsol.8
+SRCS= rtsold.c rtsol.c if.c probe.c dump.c rtsock.c
+
+CFLAGS+= -DHAVE_ARC4RANDOM -DHAVE_POLL_H
+
+DPADD= ${LIBKVM}
+LDADD= -lkvm
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rtsold/dump.c b/usr.sbin/rtsold/dump.c
new file mode 100644
index 0000000..62056ae
--- /dev/null
+++ b/usr.sbin/rtsold/dump.c
@@ -0,0 +1,149 @@
+/* $KAME: dump.c,v 1.13 2003/10/05 00:09:36 itojun Exp $ */
+
+/*
+ * Copyright (C) 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/time.h>
+#include <sys/socket.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/icmp6.h>
+
+#include <syslog.h>
+#include <time.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+
+#include "rtsold.h"
+
+static FILE *fp;
+
+extern struct ifinfo *iflist;
+
+static void dump_interface_status(void);
+static char *sec2str(time_t);
+char *ifstatstr[] = {"IDLE", "DELAY", "PROBE", "DOWN", "TENTATIVE"};
+
+static void
+dump_interface_status(void)
+{
+ struct ifinfo *ifinfo;
+ struct timeval now;
+
+ gettimeofday(&now, NULL);
+
+ for (ifinfo = iflist; ifinfo; ifinfo = ifinfo->next) {
+ fprintf(fp, "Interface %s\n", ifinfo->ifname);
+ fprintf(fp, " probe interval: ");
+ if (ifinfo->probeinterval) {
+ fprintf(fp, "%d\n", ifinfo->probeinterval);
+ fprintf(fp, " probe timer: %d\n", ifinfo->probetimer);
+ } else {
+ fprintf(fp, "infinity\n");
+ fprintf(fp, " no probe timer\n");
+ }
+ fprintf(fp, " interface status: %s\n",
+ ifinfo->active > 0 ? "active" : "inactive");
+ fprintf(fp, " other config: %s\n",
+ ifinfo->otherconfig ? "on" : "off");
+ fprintf(fp, " rtsold status: %s\n", ifstatstr[ifinfo->state]);
+ fprintf(fp, " carrier detection: %s\n",
+ ifinfo->mediareqok ? "available" : "unavailable");
+ fprintf(fp, " probes: %d, dadcount = %d\n",
+ ifinfo->probes, ifinfo->dadcount);
+ if (ifinfo->timer.tv_sec == tm_max.tv_sec &&
+ ifinfo->timer.tv_usec == tm_max.tv_usec)
+ fprintf(fp, " no timer\n");
+ else {
+ fprintf(fp, " timer: interval=%d:%d, expire=%s\n",
+ (int)ifinfo->timer.tv_sec,
+ (int)ifinfo->timer.tv_usec,
+ (ifinfo->expire.tv_sec < now.tv_sec) ? "expired"
+ : sec2str(ifinfo->expire.tv_sec - now.tv_sec));
+ }
+ fprintf(fp, " number of valid RAs: %d\n", ifinfo->racnt);
+ }
+}
+
+void
+rtsold_dump_file(char *dumpfile)
+{
+ if ((fp = fopen(dumpfile, "w")) == NULL) {
+ warnmsg(LOG_WARNING, __func__, "open a dump file(%s): %s",
+ dumpfile, strerror(errno));
+ return;
+ }
+ dump_interface_status();
+ fclose(fp);
+}
+
+static char *
+sec2str(time_t total)
+{
+ static char result[256];
+ int days, hours, mins, secs;
+ int first = 1;
+ char *p = result;
+ char *ep = &result[sizeof(result)];
+ int n;
+
+ days = total / 3600 / 24;
+ hours = (total / 3600) % 24;
+ mins = (total / 60) % 60;
+ secs = total % 60;
+
+ if (days) {
+ first = 0;
+ n = snprintf(p, ep - p, "%dd", days);
+ if (n < 0 || n >= ep - p)
+ return "?";
+ p += n;
+ }
+ if (!first || hours) {
+ first = 0;
+ n = snprintf(p, ep - p, "%dh", hours);
+ if (n < 0 || n >= ep - p)
+ return "?";
+ p += n;
+ }
+ if (!first || mins) {
+ first = 0;
+ n = snprintf(p, ep - p, "%dm", mins);
+ if (n < 0 || n >= ep - p)
+ return "?";
+ p += n;
+ }
+ snprintf(p, ep - p, "%ds", secs);
+ return(result);
+}
diff --git a/usr.sbin/rtsold/if.c b/usr.sbin/rtsold/if.c
new file mode 100644
index 0000000..4b238a1
--- /dev/null
+++ b/usr.sbin/rtsold/if.c
@@ -0,0 +1,385 @@
+/* $KAME: if.c,v 1.27 2003/10/05 00:09:36 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$
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/ioctl.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_types.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#include <net/ethernet.h>
+#include <netinet/in.h>
+#include <netinet/icmp6.h>
+
+#include <netinet6/in6_var.h>
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <string.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <limits.h>
+#include <ifaddrs.h>
+#include "rtsold.h"
+
+extern int rssock;
+static int ifsock;
+
+static int get_llflag(const char *);
+static void get_rtaddrs(int, struct sockaddr *, struct sockaddr **);
+
+int
+ifinit(void)
+{
+ ifsock = rssock;
+
+ return(0);
+}
+
+int
+interface_up(char *name)
+{
+ struct ifreq ifr;
+ int llflag;
+
+ strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name));
+
+ if (ioctl(ifsock, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
+ warnmsg(LOG_WARNING, __func__, "ioctl(SIOCGIFFLAGS): %s",
+ strerror(errno));
+ return(-1);
+ }
+ if (!(ifr.ifr_flags & IFF_UP)) {
+ ifr.ifr_flags |= IFF_UP;
+ if (ioctl(ifsock, SIOCSIFFLAGS, (caddr_t)&ifr) < 0)
+ warnmsg(LOG_ERR, __func__,
+ "ioctl(SIOCSIFFLAGS): %s", strerror(errno));
+ return(-1);
+ }
+
+ warnmsg(LOG_DEBUG, __func__, "checking if %s is ready...", name);
+
+ llflag = get_llflag(name);
+ if (llflag < 0) {
+ warnmsg(LOG_WARNING, __func__,
+ "get_llflag() failed, anyway I'll try");
+ return 0;
+ }
+
+ if (!(llflag & IN6_IFF_NOTREADY)) {
+ warnmsg(LOG_DEBUG, __func__, "%s is ready", name);
+ return(0);
+ } else {
+ if (llflag & IN6_IFF_TENTATIVE) {
+ warnmsg(LOG_DEBUG, __func__, "%s is tentative",
+ name);
+ return IFS_TENTATIVE;
+ }
+ if (llflag & IN6_IFF_DUPLICATED)
+ warnmsg(LOG_DEBUG, __func__, "%s is duplicated",
+ name);
+ return -1;
+ }
+}
+
+int
+interface_status(struct ifinfo *ifinfo)
+{
+ char *ifname = ifinfo->ifname;
+ struct ifreq ifr;
+ struct ifmediareq ifmr;
+
+ /* get interface flags */
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ if (ioctl(ifsock, SIOCGIFFLAGS, &ifr) < 0) {
+ warnmsg(LOG_ERR, __func__, "ioctl(SIOCGIFFLAGS) on %s: %s",
+ ifname, strerror(errno));
+ return(-1);
+ }
+ /*
+ * 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->mediareqok)
+ goto active;
+ memset(&ifmr, 0, sizeof(ifmr));
+ strncpy(ifmr.ifm_name, ifname, sizeof(ifmr.ifm_name));
+
+ if (ioctl(ifsock, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) {
+ if (errno != EINVAL) {
+ warnmsg(LOG_DEBUG, __func__,
+ "ioctl(SIOCGIFMEDIA) on %s: %s",
+ ifname, strerror(errno));
+ return(-1);
+ }
+ /*
+ * EINVAL simply means that the interface does not support
+ * the SIOCGIFMEDIA ioctl. We regard it alive.
+ */
+ ifinfo->mediareqok = 0;
+ goto active;
+ }
+
+ if (ifmr.ifm_status & IFM_AVALID) {
+ switch (ifmr.ifm_active & IFM_NMASK) {
+ case IFM_ETHER:
+ case IFM_IEEE80211:
+ if (ifmr.ifm_status & IFM_ACTIVE)
+ goto active;
+ else
+ goto inactive;
+ break;
+ default:
+ goto inactive;
+ }
+ }
+
+ inactive:
+ return(0);
+
+ active:
+ return(1);
+}
+
+#define ROUNDUP(a, size) \
+ (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
+
+#define NEXT_SA(ap) (ap) = (struct sockaddr *) \
+ ((caddr_t)(ap) + ((ap)->sa_len ? ROUNDUP((ap)->sa_len,\
+ sizeof(u_long)) : sizeof(u_long)))
+#define ROUNDUP8(a) (1 + (((a) - 1) | 7))
+
+int
+lladdropt_length(struct sockaddr_dl *sdl)
+{
+ switch (sdl->sdl_type) {
+ case IFT_ETHER:
+#ifdef IFT_IEEE80211
+ case IFT_IEEE80211:
+#endif
+ return(ROUNDUP8(ETHER_ADDR_LEN + 2));
+ default:
+ return(0);
+ }
+}
+
+void
+lladdropt_fill(struct sockaddr_dl *sdl, struct nd_opt_hdr *ndopt)
+{
+ char *addr;
+
+ ndopt->nd_opt_type = ND_OPT_SOURCE_LINKADDR; /* fixed */
+
+ switch (sdl->sdl_type) {
+ case IFT_ETHER:
+#ifdef IFT_IEEE80211
+ case IFT_IEEE80211:
+#endif
+ ndopt->nd_opt_len = (ROUNDUP8(ETHER_ADDR_LEN + 2)) >> 3;
+ addr = (char *)(ndopt + 1);
+ memcpy(addr, LLADDR(sdl), ETHER_ADDR_LEN);
+ break;
+ default:
+ warnmsg(LOG_ERR, __func__,
+ "unsupported link type(%d)", sdl->sdl_type);
+ exit(1);
+ }
+
+ return;
+}
+
+struct sockaddr_dl *
+if_nametosdl(char *name)
+{
+ int mib[6] = {CTL_NET, AF_ROUTE, 0, 0, NET_RT_IFLIST, 0};
+ char *buf, *next, *lim;
+ size_t len;
+ struct if_msghdr *ifm;
+ struct sockaddr *sa, *rti_info[RTAX_MAX];
+ struct sockaddr_dl *sdl = NULL, *ret_sdl;
+
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
+ return(NULL);
+ if ((buf = malloc(len)) == NULL)
+ return(NULL);
+ if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
+ free(buf);
+ return(NULL);
+ }
+
+ lim = buf + len;
+ for (next = buf; next < lim; next += ifm->ifm_msglen) {
+ ifm = (struct if_msghdr *)next;
+ if (ifm->ifm_type == RTM_IFINFO) {
+ sa = (struct sockaddr *)(ifm + 1);
+ get_rtaddrs(ifm->ifm_addrs, sa, rti_info);
+ if ((sa = rti_info[RTAX_IFP]) != NULL) {
+ if (sa->sa_family == AF_LINK) {
+ sdl = (struct sockaddr_dl *)sa;
+ if (strlen(name) != sdl->sdl_nlen)
+ continue; /* not same len */
+ if (strncmp(&sdl->sdl_data[0],
+ name,
+ sdl->sdl_nlen) == 0) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (next == lim) {
+ /* search failed */
+ free(buf);
+ return(NULL);
+ }
+
+ if ((ret_sdl = malloc(sdl->sdl_len)) == NULL) {
+ free(buf);
+ return(NULL);
+ }
+ memcpy((caddr_t)ret_sdl, (caddr_t)sdl, sdl->sdl_len);
+
+ free(buf);
+ return(ret_sdl);
+}
+
+int
+getinet6sysctl(int code)
+{
+ int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
+ int value;
+ size_t size;
+
+ mib[3] = code;
+ size = sizeof(value);
+ if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size, NULL, 0) < 0)
+ return -1;
+ else
+ return value;
+}
+
+int
+setinet6sysctl(int code, int newval)
+{
+ int mib[] = { CTL_NET, PF_INET6, IPPROTO_IPV6, 0 };
+ int value;
+ size_t size;
+
+ mib[3] = code;
+ size = sizeof(value);
+ if (sysctl(mib, sizeof(mib)/sizeof(mib[0]), &value, &size,
+ &newval, sizeof(newval)) < 0)
+ return -1;
+ else
+ return value;
+}
+
+/*------------------------------------------------------------*/
+
+/* get ia6_flags for link-local addr on if. returns -1 on error. */
+static int
+get_llflag(const char *name)
+{
+ struct ifaddrs *ifap, *ifa;
+ struct in6_ifreq ifr6;
+ struct sockaddr_in6 *sin6;
+ int s;
+
+ if ((s = socket(PF_INET6, SOCK_DGRAM, 0)) < 0) {
+ warnmsg(LOG_ERR, __func__, "socket(SOCK_DGRAM): %s",
+ strerror(errno));
+ exit(1);
+ }
+ if (getifaddrs(&ifap) != 0) {
+ warnmsg(LOG_ERR, __func__, "getifaddrs: %s",
+ strerror(errno));
+ exit(1);
+ }
+
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ if (strlen(ifa->ifa_name) != strlen(name) ||
+ strncmp(ifa->ifa_name, name, strlen(name)) != 0)
+ continue;
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+ sin6 = (struct sockaddr_in6 *)ifa->ifa_addr;
+ if (!IN6_IS_ADDR_LINKLOCAL(&sin6->sin6_addr))
+ continue;
+
+ memset(&ifr6, 0, sizeof(ifr6));
+ strncpy(ifr6.ifr_name, name, sizeof(ifr6.ifr_name));
+ memcpy(&ifr6.ifr_ifru.ifru_addr, sin6, sin6->sin6_len);
+ if (ioctl(s, SIOCGIFAFLAG_IN6, &ifr6) < 0) {
+ warnmsg(LOG_ERR, __func__,
+ "ioctl(SIOCGIFAFLAG_IN6): %s", strerror(errno));
+ exit(1);
+ }
+
+ freeifaddrs(ifap);
+ close(s);
+ return ifr6.ifr_ifru.ifru_flags6;
+ }
+
+ freeifaddrs(ifap);
+ close(s);
+ return -1;
+}
+
+
+static void
+get_rtaddrs(int addrs, struct sockaddr *sa, struct sockaddr **rti_info)
+{
+ int i;
+
+ for (i = 0; i < RTAX_MAX; i++) {
+ if (addrs & (1 << i)) {
+ rti_info[i] = sa;
+ NEXT_SA(sa);
+ } else
+ rti_info[i] = NULL;
+ }
+}
diff --git a/usr.sbin/rtsold/probe.c b/usr.sbin/rtsold/probe.c
new file mode 100644
index 0000000..61c47bc
--- /dev/null
+++ b/usr.sbin/rtsold/probe.c
@@ -0,0 +1,184 @@
+/* $KAME: probe.c,v 1.17 2003/10/05 00:09:36 itojun Exp $ */
+
+/*
+ * Copyright (C) 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$
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_dl.h>
+
+#include <netinet/in.h>
+#include <netinet6/in6_var.h>
+#include <netinet/icmp6.h>
+#include <netinet6/nd6.h>
+
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdlib.h>
+
+#include "rtsold.h"
+
+static struct msghdr sndmhdr;
+static struct iovec sndiov[2];
+static int probesock;
+static void sendprobe(struct in6_addr *, struct ifinfo *);
+
+int
+probe_init(void)
+{
+ int scmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+ CMSG_SPACE(sizeof(int));
+ static u_char *sndcmsgbuf = NULL;
+
+ if (sndcmsgbuf == NULL &&
+ (sndcmsgbuf = (u_char *)malloc(scmsglen)) == NULL) {
+ warnmsg(LOG_ERR, __func__, "malloc failed");
+ return(-1);
+ }
+
+ if ((probesock = socket(AF_INET6, SOCK_RAW, IPPROTO_NONE)) < 0) {
+ warnmsg(LOG_ERR, __func__, "socket: %s", strerror(errno));
+ return(-1);
+ }
+
+ /* make the socket send-only */
+ if (shutdown(probesock, 0)) {
+ warnmsg(LOG_ERR, __func__, "shutdown: %s", strerror(errno));
+ return(-1);
+ }
+
+ /* initialize msghdr for sending packets */
+ sndmhdr.msg_namelen = sizeof(struct sockaddr_in6);
+ sndmhdr.msg_iov = sndiov;
+ sndmhdr.msg_iovlen = 1;
+ sndmhdr.msg_control = (caddr_t)sndcmsgbuf;
+ sndmhdr.msg_controllen = scmsglen;
+ return(0);
+}
+
+/*
+ * Probe if each router in the default router list is still alive.
+ */
+void
+defrouter_probe(struct ifinfo *ifinfo)
+{
+ u_char ntopbuf[INET6_ADDRSTRLEN];
+ struct in6_drlist dr;
+ int s, i;
+ int ifindex = ifinfo->sdl->sdl_index;
+
+ if ((s = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ warnmsg(LOG_ERR, __func__, "socket: %s", strerror(errno));
+ return;
+ }
+ memset(&dr, 0, sizeof(dr));
+ strlcpy(dr.ifname, "lo0", sizeof dr.ifname); /* dummy interface */
+ if (ioctl(s, SIOCGDRLST_IN6, (caddr_t)&dr) < 0) {
+ warnmsg(LOG_ERR, __func__, "ioctl(SIOCGDRLST_IN6): %s",
+ strerror(errno));
+ goto closeandend;
+ }
+
+ for (i = 0; dr.defrouter[i].if_index && i < PRLSTSIZ; i++) {
+ if (ifindex && dr.defrouter[i].if_index == ifindex) {
+ /* sanity check */
+ if (!IN6_IS_ADDR_LINKLOCAL(&dr.defrouter[i].rtaddr)) {
+ warnmsg(LOG_ERR, __func__,
+ "default router list contains a "
+ "non-link-local address(%s)",
+ inet_ntop(AF_INET6,
+ &dr.defrouter[i].rtaddr,
+ ntopbuf, INET6_ADDRSTRLEN));
+ continue; /* ignore the address */
+ }
+ sendprobe(&dr.defrouter[i].rtaddr, ifinfo);
+ }
+ }
+
+closeandend:
+ close(s);
+}
+
+static void
+sendprobe(struct in6_addr *addr, struct ifinfo *ifinfo)
+{
+ u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
+ struct sockaddr_in6 sa6_probe;
+ struct in6_pktinfo *pi;
+ struct cmsghdr *cm;
+ u_int32_t ifindex = ifinfo->sdl->sdl_index;
+ int hoplimit = 1;
+
+ memset(&sa6_probe, 0, sizeof(sa6_probe));
+ sa6_probe.sin6_family = AF_INET6;
+ sa6_probe.sin6_len = sizeof(sa6_probe);
+ sa6_probe.sin6_addr = *addr;
+ sa6_probe.sin6_scope_id = ifinfo->linkid;
+
+ sndmhdr.msg_name = (caddr_t)&sa6_probe;
+ sndmhdr.msg_iov[0].iov_base = NULL;
+ sndmhdr.msg_iov[0].iov_len = 0;
+
+ cm = CMSG_FIRSTHDR(&sndmhdr);
+ /* specify the outgoing interface */
+ cm->cmsg_level = IPPROTO_IPV6;
+ cm->cmsg_type = IPV6_PKTINFO;
+ cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+ pi = (struct in6_pktinfo *)CMSG_DATA(cm);
+ memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/
+ pi->ipi6_ifindex = ifindex;
+
+ /* specify the hop limit of the packet for safety */
+ cm = CMSG_NXTHDR(&sndmhdr, cm);
+ cm->cmsg_level = IPPROTO_IPV6;
+ cm->cmsg_type = IPV6_HOPLIMIT;
+ cm->cmsg_len = CMSG_LEN(sizeof(int));
+ memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int));
+
+ warnmsg(LOG_DEBUG, __func__, "probe a router %s on %s",
+ inet_ntop(AF_INET6, addr, ntopbuf, INET6_ADDRSTRLEN),
+ if_indextoname(ifindex, ifnamebuf));
+
+ if (sendmsg(probesock, &sndmhdr, 0))
+ warnmsg(LOG_ERR, __func__, "sendmsg on %s: %s",
+ if_indextoname(ifindex, ifnamebuf), strerror(errno));
+}
diff --git a/usr.sbin/rtsold/rtsock.c b/usr.sbin/rtsold/rtsock.c
new file mode 100644
index 0000000..0de6e85
--- /dev/null
+++ b/usr.sbin/rtsold/rtsock.c
@@ -0,0 +1,175 @@
+/* $KAME: rtsock.c,v 1.3 2000/10/10 08:46:45 itojun Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (C) 2000 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/param.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+
+#include <time.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <err.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include "rtsold.h"
+
+#define ROUNDUP(a, size) \
+ (((a) & ((size)-1)) ? (1 + ((a) | ((size)-1))) : (a))
+
+#define NEXT_SA(ap) (ap) = (struct sockaddr *) \
+ ((caddr_t)(ap) + \
+ ((ap)->sa_len ? ROUNDUP((ap)->sa_len, sizeof(u_long)) \
+ : sizeof(u_long)))
+
+#ifdef RTM_IFANNOUNCE /*NetBSD 1.5 or later*/
+static int rtsock_input_ifannounce(int, struct rt_msghdr *, char *);
+#endif
+
+static struct {
+ u_char type;
+ size_t minlen;
+ int (*func)(int, struct rt_msghdr *, char *);
+} rtsock_dispatch[] = {
+#ifdef RTM_IFANNOUNCE /*NetBSD 1.5 or later*/
+ { RTM_IFANNOUNCE, sizeof(struct if_announcemsghdr),
+ rtsock_input_ifannounce },
+#endif
+ { 0, 0, NULL },
+};
+
+int
+rtsock_open(void)
+{
+
+ return socket(PF_ROUTE, SOCK_RAW, 0);
+}
+
+int
+rtsock_input(int s)
+{
+ ssize_t n;
+ char msg[2048];
+ char *lim, *next;
+ struct rt_msghdr *rtm;
+ int idx;
+ size_t len;
+ int ret = 0;
+ const size_t lenlim =
+ offsetof(struct rt_msghdr, rtm_msglen) + sizeof(rtm->rtm_msglen);
+
+ n = read(s, msg, sizeof(msg));
+
+ lim = msg + n;
+ for (next = msg; next < lim; next += len) {
+ rtm = (struct rt_msghdr *)next;
+ if (lim - next < lenlim)
+ break;
+ len = rtm->rtm_msglen;
+ if (len < lenlim)
+ break;
+
+ if (dflag > 1) {
+ warnmsg(LOG_INFO, __func__,
+ "rtmsg type %d, len=%lu", rtm->rtm_type,
+ (u_long)len);
+ }
+
+ for (idx = 0; rtsock_dispatch[idx].func; idx++) {
+ if (rtm->rtm_type != rtsock_dispatch[idx].type)
+ continue;
+ if (rtm->rtm_msglen < rtsock_dispatch[idx].minlen) {
+ warnmsg(LOG_INFO, __func__,
+ "rtmsg type %d too short!", rtm->rtm_type);
+ continue;
+ }
+
+ ret = (*rtsock_dispatch[idx].func)(s, rtm, lim);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+#ifdef RTM_IFANNOUNCE /*NetBSD 1.5 or later*/
+static int
+rtsock_input_ifannounce(int s, struct rt_msghdr *rtm, char *lim)
+{
+ struct if_announcemsghdr *ifan;
+ struct ifinfo *ifinfo;
+
+ ifan = (struct if_announcemsghdr *)rtm;
+ if ((char *)(ifan + 1) > lim)
+ return -1;
+
+ switch (ifan->ifan_what) {
+ case IFAN_ARRIVAL:
+ /*
+ * XXX for NetBSD 1.5, interface index will monotonically be
+ * increased as new pcmcia card gets inserted.
+ * we may be able to do a name-based interface match,
+ * and call ifreconfig() to enable the interface again.
+ */
+ warnmsg(LOG_INFO, __func__,
+ "interface %s inserted", ifan->ifan_name);
+ break;
+ case IFAN_DEPARTURE:
+ warnmsg(LOG_WARNING, __func__,
+ "interface %s removed", ifan->ifan_name);
+ ifinfo = find_ifinfo(ifan->ifan_index);
+ if (ifinfo) {
+ if (dflag > 1) {
+ warnmsg(LOG_INFO, __func__,
+ "bring interface %s to DOWN state",
+ ifan->ifan_name);
+ }
+ ifinfo->state = IFS_DOWN;
+ }
+ break;
+ }
+
+ return 0;
+}
+#endif
diff --git a/usr.sbin/rtsold/rtsol.c b/usr.sbin/rtsold/rtsol.c
new file mode 100644
index 0000000..3420f2f
--- /dev/null
+++ b/usr.sbin/rtsold/rtsol.c
@@ -0,0 +1,470 @@
+/* $KAME: rtsol.c,v 1.27 2003/10/05 00:09:36 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$
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/time.h>
+#include <sys/queue.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+
+#include <net/if.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <netinet6/ip6_var.h>
+#include <netinet/icmp6.h>
+
+#include <arpa/inet.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <err.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include "rtsold.h"
+
+#define ALLROUTER "ff02::2"
+
+static struct msghdr rcvmhdr;
+static struct msghdr sndmhdr;
+static struct iovec rcviov[2];
+static struct iovec sndiov[2];
+static struct sockaddr_in6 from;
+static int rcvcmsglen;
+
+int rssock;
+
+static struct sockaddr_in6 sin6_allrouters =
+{sizeof(sin6_allrouters), AF_INET6};
+
+static void call_script(char *, char *);
+static int safefile(const char *);
+
+int
+sockopen(void)
+{
+ static u_char *rcvcmsgbuf = NULL, *sndcmsgbuf = NULL;
+ int sndcmsglen, on;
+ static u_char answer[1500];
+ struct icmp6_filter filt;
+
+ sndcmsglen = rcvcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+ CMSG_SPACE(sizeof(int));
+ if (rcvcmsgbuf == NULL && (rcvcmsgbuf = malloc(rcvcmsglen)) == NULL) {
+ warnmsg(LOG_ERR, __func__,
+ "malloc for receive msghdr failed");
+ return(-1);
+ }
+ if (sndcmsgbuf == NULL && (sndcmsgbuf = malloc(sndcmsglen)) == NULL) {
+ warnmsg(LOG_ERR, __func__,
+ "malloc for send msghdr failed");
+ return(-1);
+ }
+ memset(&sin6_allrouters, 0, sizeof(struct sockaddr_in6));
+ sin6_allrouters.sin6_family = AF_INET6;
+ sin6_allrouters.sin6_len = sizeof(sin6_allrouters);
+ if (inet_pton(AF_INET6, ALLROUTER,
+ &sin6_allrouters.sin6_addr.s6_addr) != 1) {
+ warnmsg(LOG_ERR, __func__, "inet_pton failed for %s",
+ ALLROUTER);
+ return(-1);
+ }
+
+ if ((rssock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) {
+ warnmsg(LOG_ERR, __func__, "socket: %s", strerror(errno));
+ return(-1);
+ }
+
+ /* specify to tell receiving interface */
+ on = 1;
+#ifdef IPV6_RECVPKTINFO
+ if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
+ sizeof(on)) < 0) {
+ warnmsg(LOG_ERR, __func__, "IPV6_RECVPKTINFO: %s",
+ strerror(errno));
+ exit(1);
+ }
+#else /* old adv. API */
+ if (setsockopt(rssock, IPPROTO_IPV6, IPV6_PKTINFO, &on,
+ sizeof(on)) < 0) {
+ warnmsg(LOG_ERR, __func__, "IPV6_PKTINFO: %s",
+ strerror(errno));
+ exit(1);
+ }
+#endif
+
+ on = 1;
+ /* specify to tell value of hoplimit field of received IP6 hdr */
+#ifdef IPV6_RECVHOPLIMIT
+ if (setsockopt(rssock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
+ sizeof(on)) < 0) {
+ warnmsg(LOG_ERR, __func__, "IPV6_RECVHOPLIMIT: %s",
+ strerror(errno));
+ exit(1);
+ }
+#else /* old adv. API */
+ if (setsockopt(rssock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on,
+ sizeof(on)) < 0) {
+ warnmsg(LOG_ERR, __func__, "IPV6_HOPLIMIT: %s",
+ strerror(errno));
+ exit(1);
+ }
+#endif
+
+ /* specfiy to accept only router advertisements on the socket */
+ ICMP6_FILTER_SETBLOCKALL(&filt);
+ ICMP6_FILTER_SETPASS(ND_ROUTER_ADVERT, &filt);
+ if (setsockopt(rssock, IPPROTO_ICMPV6, ICMP6_FILTER, &filt,
+ sizeof(filt)) == -1) {
+ warnmsg(LOG_ERR, __func__, "setsockopt(ICMP6_FILTER): %s",
+ strerror(errno));
+ return(-1);
+ }
+
+ /* initialize msghdr for receiving packets */
+ rcviov[0].iov_base = (caddr_t)answer;
+ rcviov[0].iov_len = sizeof(answer);
+ rcvmhdr.msg_name = (caddr_t)&from;
+ rcvmhdr.msg_iov = rcviov;
+ rcvmhdr.msg_iovlen = 1;
+ rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf;
+
+ /* initialize msghdr for sending packets */
+ sndmhdr.msg_namelen = sizeof(struct sockaddr_in6);
+ sndmhdr.msg_iov = sndiov;
+ sndmhdr.msg_iovlen = 1;
+ sndmhdr.msg_control = (caddr_t)sndcmsgbuf;
+ sndmhdr.msg_controllen = sndcmsglen;
+
+ return(rssock);
+}
+
+void
+sendpacket(struct ifinfo *ifinfo)
+{
+ struct in6_pktinfo *pi;
+ struct cmsghdr *cm;
+ int hoplimit = 255;
+ int i;
+ struct sockaddr_in6 dst;
+
+ dst = sin6_allrouters;
+ dst.sin6_scope_id = ifinfo->linkid;
+
+ sndmhdr.msg_name = (caddr_t)&dst;
+ sndmhdr.msg_iov[0].iov_base = (caddr_t)ifinfo->rs_data;
+ sndmhdr.msg_iov[0].iov_len = ifinfo->rs_datalen;
+
+ cm = CMSG_FIRSTHDR(&sndmhdr);
+ /* specify the outgoing interface */
+ cm->cmsg_level = IPPROTO_IPV6;
+ cm->cmsg_type = IPV6_PKTINFO;
+ cm->cmsg_len = CMSG_LEN(sizeof(struct in6_pktinfo));
+ pi = (struct in6_pktinfo *)CMSG_DATA(cm);
+ memset(&pi->ipi6_addr, 0, sizeof(pi->ipi6_addr)); /*XXX*/
+ pi->ipi6_ifindex = ifinfo->sdl->sdl_index;
+
+ /* specify the hop limit of the packet */
+ cm = CMSG_NXTHDR(&sndmhdr, cm);
+ cm->cmsg_level = IPPROTO_IPV6;
+ cm->cmsg_type = IPV6_HOPLIMIT;
+ cm->cmsg_len = CMSG_LEN(sizeof(int));
+ memcpy(CMSG_DATA(cm), &hoplimit, sizeof(int));
+
+ warnmsg(LOG_DEBUG, __func__,
+ "send RS on %s, whose state is %d",
+ ifinfo->ifname, ifinfo->state);
+ i = sendmsg(rssock, &sndmhdr, 0);
+ if (i < 0 || i != ifinfo->rs_datalen) {
+ /*
+ * ENETDOWN is not so serious, especially when using several
+ * network cards on a mobile node. We ignore it.
+ */
+ if (errno != ENETDOWN || dflag > 0)
+ warnmsg(LOG_ERR, __func__, "sendmsg on %s: %s",
+ ifinfo->ifname, strerror(errno));
+ }
+
+ /* update counter */
+ ifinfo->probes++;
+}
+
+void
+rtsol_input(int s)
+{
+ u_char ntopbuf[INET6_ADDRSTRLEN], ifnamebuf[IFNAMSIZ];
+ int ifindex = 0, i, *hlimp = NULL;
+ struct in6_pktinfo *pi = NULL;
+ struct ifinfo *ifi = NULL;
+ struct icmp6_hdr *icp;
+ struct nd_router_advert *nd_ra;
+ struct cmsghdr *cm;
+
+ /* get message. namelen and controllen must always be initialized. */
+ rcvmhdr.msg_namelen = sizeof(from);
+ rcvmhdr.msg_controllen = rcvcmsglen;
+ if ((i = recvmsg(s, &rcvmhdr, 0)) < 0) {
+ warnmsg(LOG_ERR, __func__, "recvmsg: %s", strerror(errno));
+ return;
+ }
+
+ /* extract optional information via Advanced API */
+ for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(&rcvmhdr); cm;
+ cm = (struct cmsghdr *)CMSG_NXTHDR(&rcvmhdr, cm)) {
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_PKTINFO &&
+ cm->cmsg_len == CMSG_LEN(sizeof(struct in6_pktinfo))) {
+ pi = (struct in6_pktinfo *)(CMSG_DATA(cm));
+ ifindex = pi->ipi6_ifindex;
+ }
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_HOPLIMIT &&
+ cm->cmsg_len == CMSG_LEN(sizeof(int)))
+ hlimp = (int *)CMSG_DATA(cm);
+ }
+
+ if (ifindex == 0) {
+ warnmsg(LOG_ERR, __func__,
+ "failed to get receiving interface");
+ return;
+ }
+ if (hlimp == NULL) {
+ warnmsg(LOG_ERR, __func__,
+ "failed to get receiving hop limit");
+ return;
+ }
+
+ if (i < sizeof(struct nd_router_advert)) {
+ warnmsg(LOG_INFO, __func__,
+ "packet size(%d) is too short", i);
+ return;
+ }
+
+ icp = (struct icmp6_hdr *)rcvmhdr.msg_iov[0].iov_base;
+
+ if (icp->icmp6_type != ND_ROUTER_ADVERT) {
+ /*
+ * this should not happen because we configured a filter
+ * that only passes RAs on the receiving socket.
+ */
+ warnmsg(LOG_ERR, __func__,
+ "invalid icmp type(%d) from %s on %s", icp->icmp6_type,
+ inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ if (icp->icmp6_code != 0) {
+ warnmsg(LOG_INFO, __func__,
+ "invalid icmp code(%d) from %s on %s", icp->icmp6_code,
+ inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ if (*hlimp != 255) {
+ warnmsg(LOG_INFO, __func__,
+ "invalid RA with hop limit(%d) from %s on %s",
+ *hlimp,
+ inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ if (pi && !IN6_IS_ADDR_LINKLOCAL(&from.sin6_addr)) {
+ warnmsg(LOG_INFO, __func__,
+ "invalid RA with non link-local source from %s on %s",
+ inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ /* xxx: more validation? */
+
+ if ((ifi = find_ifinfo(pi->ipi6_ifindex)) == NULL) {
+ warnmsg(LOG_INFO, __func__,
+ "received RA from %s on an unexpected IF(%s)",
+ inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf,
+ INET6_ADDRSTRLEN),
+ if_indextoname(pi->ipi6_ifindex, ifnamebuf));
+ return;
+ }
+
+ warnmsg(LOG_DEBUG, __func__,
+ "received RA from %s on %s, state is %d",
+ inet_ntop(AF_INET6, &from.sin6_addr, ntopbuf, INET6_ADDRSTRLEN),
+ ifi->ifname, ifi->state);
+
+ nd_ra = (struct nd_router_advert *)icp;
+
+ /*
+ * Process the "O bit."
+ * If the value of OtherConfigFlag changes from FALSE to TRUE, the
+ * host should invoke the stateful autoconfiguration protocol,
+ * requesting information.
+ * [RFC 2462 Section 5.5.3]
+ */
+ if (((nd_ra->nd_ra_flags_reserved) & ND_RA_FLAG_OTHER) &&
+ !ifi->otherconfig) {
+ warnmsg(LOG_DEBUG, __func__,
+ "OtherConfigFlag on %s is turned on", ifi->ifname);
+ ifi->otherconfig = 1;
+ call_script(otherconf_script, ifi->ifname);
+ }
+
+ ifi->racnt++;
+
+ switch (ifi->state) {
+ case IFS_IDLE: /* should be ignored */
+ case IFS_DELAY: /* right? */
+ break;
+ case IFS_PROBE:
+ ifi->state = IFS_IDLE;
+ ifi->probes = 0;
+ rtsol_timer_update(ifi);
+ break;
+ }
+}
+
+static void
+call_script(char *scriptpath, char *ifname)
+{
+ pid_t pid, wpid;
+
+ if (scriptpath == NULL)
+ return;
+
+ /* launch the script */
+ pid = fork();
+ if (pid < 0) {
+ warnmsg(LOG_ERR, __func__,
+ "failed to fork: %s", strerror(errno));
+ return;
+ } else if (pid) {
+ int wstatus;
+
+ do {
+ wpid = wait(&wstatus);
+ } while (wpid != pid && wpid > 0);
+
+ if (wpid < 0)
+ warnmsg(LOG_ERR, __func__,
+ "wait: %s", strerror(errno));
+ else {
+ warnmsg(LOG_DEBUG, __func__,
+ "script \"%s\" terminated", scriptpath);
+ }
+ } else {
+ char *argv[3];
+ int fd;
+
+ argv[0] = scriptpath;
+ argv[1] = ifname;
+ argv[2] = NULL;
+
+ if (safefile(scriptpath)) {
+ warnmsg(LOG_ERR, __func__,
+ "script \"%s\" cannot be executed safely",
+ scriptpath);
+ exit(1);
+ }
+
+ if ((fd = open("/dev/null", O_RDWR)) != -1) {
+ dup2(fd, STDIN_FILENO);
+ dup2(fd, STDOUT_FILENO);
+ dup2(fd, STDERR_FILENO);
+ if (fd > STDERR_FILENO)
+ close(fd);
+ }
+
+ execv(scriptpath, argv);
+
+ warnmsg(LOG_ERR, __func__, "child: exec failed: %s",
+ strerror(errno));
+ exit(0);
+ }
+
+ return;
+}
+
+static int
+safefile(const char *path)
+{
+ struct stat s;
+ uid_t myuid;
+
+ /* no setuid */
+ if (getuid() != geteuid()) {
+ warnmsg(LOG_NOTICE, __func__,
+ "setuid'ed execution not allowed\n");
+ return (-1);
+ }
+
+ if (lstat(path, &s) != 0) {
+ warnmsg(LOG_NOTICE, __func__, "lstat failed: %s",
+ strerror(errno));
+ return (-1);
+ }
+
+ /* the file must be owned by the running uid */
+ myuid = getuid();
+ if (s.st_uid != myuid) {
+ warnmsg(LOG_NOTICE, __func__,
+ "%s has invalid owner uid\n", path);
+ return (-1);
+ }
+
+ switch (s.st_mode & S_IFMT) {
+ case S_IFREG:
+ break;
+ default:
+ warnmsg(LOG_NOTICE, __func__,
+ "%s is an invalid file type 0x%o\n",
+ path, (s.st_mode & S_IFMT));
+ return (-1);
+ }
+
+ return (0);
+}
diff --git a/usr.sbin/rtsold/rtsold.8 b/usr.sbin/rtsold/rtsold.8
new file mode 100644
index 0000000..1ee932d
--- /dev/null
+++ b/usr.sbin/rtsold/rtsold.8
@@ -0,0 +1,270 @@
+.\" $KAME: rtsold.8,v 1.20 2003/04/11 12:46:12 jinmei 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 RTSOLD 8
+.Os
+.\"
+.Sh NAME
+.Nm rtsold , rtsol
+.Nd router solicitation daemon
+.\"
+.Sh SYNOPSIS
+.Nm
+.Op Fl dDfFm1
+.Op Fl O Ar script-name
+.Ar interface ...
+.Nm
+.Op Fl dDfFm1
+.Op Fl O Ar script-name
+.Fl a
+.Nm rtsol
+.Op Fl dDF
+.Op Fl O Ar script-name
+.Ar interface ...
+.Nm rtsol
+.Op Fl dD
+.Op Fl O Ar script-name
+.Fl a
+.\"
+.Sh DESCRIPTION
+.Nm
+is the daemon program to send ICMPv6 Router Solicitation messages
+on the specified interfaces.
+If a node (re)attaches to a link,
+.Nm
+sends some Router Solicitations on the link destined to the link-local scope
+all-routers multicast address to discover new routers
+and to get non link-local addresses.
+.Pp
+.Nm
+should be used on IPv6 hosts
+.Pq non-router nodes
+only.
+.Pp
+If you invoke the program as
+.Nm rtsol ,
+it will transmit probes from the specified
+.Ar interface ,
+without becoming a daemon.
+In other words,
+.Nm rtsol
+behaves as
+.Do
+.Nm
+.Fl f1
+.Ar interfaces
+.Dc .
+.Pp
+Specifically,
+.Nm
+sends at most 3 Router Solicitations on an interface
+after one of the following events:
+.Pp
+.Bl -bullet -compact
+.It
+Just after invocation of
+.Nm
+daemon.
+.It
+The interface is up after a temporary interface failure.
+.Nm
+detects such failures by periodically probing to see if the status
+of the interface is active or not.
+Note that some network cards and drivers do not allow the extraction
+of link state.
+In such cases,
+.Nm
+cannot detect the change of the interface status.
+.It
+Every 60 seconds if the
+.Fl m
+option is specified and the
+.Nm
+daemon cannot get the interface status.
+This feature does not conform to the IPv6 neighbor discovery
+specification, but is provided for mobile stations.
+The default interval for router advertisements, which is on the order of 10
+minutes, is slightly long for mobile stations.
+This feature is provided
+for such stations so that they can find new routers as soon as possible
+when they attach to another link.
+.El
+.Lp
+Once
+.Nm
+has sent a Router Solicitation, and has received a valid Router Advertisement,
+it refrains from sending additional solicitations on that interface, until
+the next time one of the above events occurs.
+.Lp
+When sending a Router Solicitation on an interface,
+.Nm
+includes a Source Link-layer address option if the interface
+has a link-layer address.
+.Lp
+.Nm
+manages a per-interface parameter to detect if a separate protocol is
+needed for configuration parameters other than host's addresses.
+At the invocation time, the flag is FALSE, and becomes TRUE when
+the daemon receives a router advertisement with the OtherConfig flag
+being set.
+A script file can be specified to deal with the case
+.Pq see below .
+When
+.Nm
+start resending router solicitation messages by one of the conditions
+events,
+the daemon resets the parameter because the event may indicate a
+change on the attached link.
+.Pp
+Upon receipt of signal
+.Dv SIGUSR1 ,
+.Nm
+will dump the current internal state into
+.Pa /var/run/rtsold.dump .
+.\"
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl a
+Autoprobe outgoing interface.
+.Nm
+will try to find a non-loopback, non-point-to-point, IPv6-capable interface.
+If
+.Nm
+finds multiple interfaces,
+.Nm
+will exit with error.
+.\"
+.It Fl d
+Enable debugging.
+.It Fl D
+Enable more debugging including the printing of internal timer information.
+.It Fl f
+Prevent
+.Nm
+from becoming a daemon (foreground mode).
+Warning messages are generated to standard error
+instead of
+.Xr syslog 3 .
+.It Fl F
+Explicitly configure the kernel to accept Router Advertisements and
+disable IPv6 forwarding.
+These settings are required for proper
+.Nm
+operation.
+Without this option, the current settings will be obeyed;
+if they are incompatible with proper operation,
+warning messages will be generated,
+but Router Solicitations will still be sent.
+The settings may be changed manually with
+.Xr sysctl 8 .
+.It Fl m
+Enable mobility support.
+If this option is specified,
+.Nm
+sends probing packets to default routers that have advertised Router
+Advertisements
+when the node (re)attaches to an interface.
+Moreover, if the option is specified,
+.Nm
+periodically sends Router Solicitation on an interface that does not support
+.Dv SIOCGIFMEDIA
+ioctl.
+.It Fl 1
+Perform only one probe.
+Transmit Router Solicitation packets until at least one valid Router
+Advertisement packet has arrived on each
+.Ar interface ,
+then exit.
+.It Fl O Ar script-name
+Specifies a supplement script file to handle the Other Configuration
+flag of the router advertisement.
+When the flag changes from FALSE to TRUE,
+.Nm
+will invoke
+.Ar script-name
+with a single argument of the receiving interface name,
+expecting the script will then start a protocol for the other
+configuration.
+.Ar script-name
+must be the absolute path from root to the script file, be a regular
+file, and be created by the same owner who runs
+.Nm .
+.El
+.Sh EXIT STATUS
+.Ex -std
+.\"
+.Sh FILES
+.Bl -tag -width /var/run/rtsold.dump -compact
+.It Pa /var/run/rtsold.pid
+the pid of the currently running
+.Nm .
+.It Pa /var/run/rtsold.dump
+dumps internal state on.
+.El
+.\"
+.Sh SEE ALSO
+.Xr rtadvd 8 ,
+.Xr sysctl 8
+.\"
+.Sh HISTORY
+The
+.Nm
+command is based on the
+.Nm rtsol
+command, which first appeared in WIDE/KAME IPv6 protocol stack kit.
+.Nm rtsol
+is now integrated into
+.Xr rtsold 8 .
+.\"
+.Sh BUGS
+In some operating systems, when a PCMCIA network card is removed
+and reinserted, the corresponding interface index is changed.
+However,
+.Nm
+assumes such changes will not occur, and always uses the index that
+it got at invocation.
+As a result,
+.Nm
+may not work if you reinsert a network card.
+In such a case,
+.Nm
+should be killed and restarted.
+.Pp
+The IPv6 autoconfiguration specification assumes a single-interface host.
+You may see kernel error messages if you try to autoconfigure a host with
+multiple interfaces.
+Also, it seems contradictory for
+.Nm
+to accept multiple
+.Ar interface
+arguments.
diff --git a/usr.sbin/rtsold/rtsold.c b/usr.sbin/rtsold/rtsold.c
new file mode 100644
index 0000000..f0493fbd
--- /dev/null
+++ b/usr.sbin/rtsold/rtsold.c
@@ -0,0 +1,861 @@
+/* $KAME: rtsold.c,v 1.67 2003/05/17 18:16:15 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$
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+
+#include <netinet/in.h>
+#include <netinet/icmp6.h>
+
+#include <signal.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <err.h>
+#include <stdarg.h>
+#include <ifaddrs.h>
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#endif
+
+#include "rtsold.h"
+
+struct ifinfo *iflist;
+struct timeval tm_max = {0x7fffffff, 0x7fffffff};
+static int log_upto = 999;
+static int fflag = 0;
+static int Fflag = 0; /* force setting sysctl parameters */
+
+int aflag = 0;
+int dflag = 0;
+
+char *otherconf_script;
+
+/* protocol constants */
+#define MAX_RTR_SOLICITATION_DELAY 1 /* second */
+#define RTR_SOLICITATION_INTERVAL 4 /* seconds */
+#define MAX_RTR_SOLICITATIONS 3 /* times */
+
+/*
+ * implementation dependent constants in seconds
+ * XXX: should be configurable
+ */
+#define PROBE_INTERVAL 60
+
+int main(int, char **);
+
+/* static variables and functions */
+static int mobile_node = 0;
+#ifndef SMALL
+static int do_dump;
+static char *dumpfilename = "/var/run/rtsold.dump"; /* XXX: should be configurable */
+#endif
+#if 1
+static char *pidfilename = "/var/run/rtsold.pid"; /* should be configurable */
+#endif
+
+#if 0
+static int ifreconfig(char *);
+#endif
+static int make_packet(struct ifinfo *);
+static struct timeval *rtsol_check_timer(void);
+
+#ifndef SMALL
+static void rtsold_set_dump_file(int);
+#endif
+static void usage(char *);
+
+int
+main(int argc, char **argv)
+{
+ int s, ch, once = 0;
+ struct timeval *timeout;
+ char *argv0, *opts;
+#ifdef HAVE_POLL_H
+ struct pollfd set[2];
+#else
+ fd_set *fdsetp, *selectfdp;
+ int fdmasks;
+ int maxfd;
+#endif
+ int rtsock;
+
+ /*
+ * Initialization
+ */
+ argv0 = argv[0];
+
+ /* get option */
+ if (argv0 && argv0[strlen(argv0) - 1] != 'd') {
+ fflag = 1;
+ once = 1;
+ opts = "adDFO:";
+ } else
+ opts = "adDfFm1O:";
+
+ while ((ch = getopt(argc, argv, opts)) != -1) {
+ switch (ch) {
+ case 'a':
+ aflag = 1;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'D':
+ dflag = 2;
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 'F':
+ Fflag = 1;
+ break;
+ case 'm':
+ mobile_node = 1;
+ break;
+ case '1':
+ once = 1;
+ break;
+ case 'O':
+ otherconf_script = optarg;
+ break;
+ default:
+ usage(argv0);
+ /*NOTREACHED*/
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if ((!aflag && argc == 0) || (aflag && argc != 0)) {
+ usage(argv0);
+ /*NOTREACHED*/
+ }
+
+ /* set log level */
+ if (dflag == 0)
+ log_upto = LOG_NOTICE;
+ if (!fflag) {
+ char *ident;
+
+ ident = strrchr(argv0, '/');
+ if (!ident)
+ ident = argv0;
+ else
+ ident++;
+ openlog(ident, LOG_NDELAY|LOG_PID, LOG_DAEMON);
+ if (log_upto >= 0)
+ setlogmask(LOG_UPTO(log_upto));
+ }
+
+ if (otherconf_script && *otherconf_script != '/') {
+ errx(1, "configuration script (%s) must be an absolute path",
+ otherconf_script);
+ }
+
+#ifndef HAVE_ARC4RANDOM
+ /* random value initialization */
+ srandom((u_long)time(NULL));
+#endif
+
+ if (Fflag) {
+ setinet6sysctl(IPV6CTL_ACCEPT_RTADV, 1);
+ setinet6sysctl(IPV6CTL_FORWARDING, 0);
+ } else {
+ /* warn if accept_rtadv is down */
+ if (!getinet6sysctl(IPV6CTL_ACCEPT_RTADV))
+ warnx("kernel is configured not to accept RAs");
+ /* warn if forwarding is up */
+ if (getinet6sysctl(IPV6CTL_FORWARDING))
+ warnx("kernel is configured as a router, not a host");
+ }
+
+#ifndef SMALL
+ /* initialization to dump internal status to a file */
+ signal(SIGUSR1, rtsold_set_dump_file);
+#endif
+
+ if (!fflag)
+ daemon(0, 0); /* act as a daemon */
+
+ /*
+ * Open a socket for sending RS and receiving RA.
+ * This should be done before calling ifinit(), since the function
+ * uses the socket.
+ */
+ if ((s = sockopen()) < 0) {
+ warnmsg(LOG_ERR, __func__, "failed to open a socket");
+ exit(1);
+ /*NOTREACHED*/
+ }
+#ifdef HAVE_POLL_H
+ set[0].fd = s;
+ set[0].events = POLLIN;
+#else
+ maxfd = s;
+#endif
+
+#ifdef HAVE_POLL_H
+ set[1].fd = -1;
+#endif
+
+ if ((rtsock = rtsock_open()) < 0) {
+ warnmsg(LOG_ERR, __func__, "failed to open a socket");
+ exit(1);
+ /*NOTREACHED*/
+ }
+#ifdef HAVE_POLL_H
+ set[1].fd = rtsock;
+ set[1].events = POLLIN;
+#else
+ if (rtsock > maxfd)
+ maxfd = rtsock;
+#endif
+
+#ifndef HAVE_POLL_H
+ fdmasks = howmany(maxfd + 1, NFDBITS) * sizeof(fd_mask);
+ if ((fdsetp = malloc(fdmasks)) == NULL) {
+ err(1, "malloc");
+ /*NOTREACHED*/
+ }
+ if ((selectfdp = malloc(fdmasks)) == NULL) {
+ err(1, "malloc");
+ /*NOTREACHED*/
+ }
+#endif
+
+ /* configuration per interface */
+ if (ifinit()) {
+ warnmsg(LOG_ERR, __func__,
+ "failed to initialize interfaces");
+ exit(1);
+ /*NOTREACHED*/
+ }
+ if (aflag)
+ argv = autoifprobe();
+ while (argv && *argv) {
+ if (ifconfig(*argv)) {
+ warnmsg(LOG_ERR, __func__,
+ "failed to initialize %s", *argv);
+ exit(1);
+ /*NOTREACHED*/
+ }
+ argv++;
+ }
+
+ /* setup for probing default routers */
+ if (probe_init()) {
+ warnmsg(LOG_ERR, __func__,
+ "failed to setup for probing routers");
+ exit(1);
+ /*NOTREACHED*/
+ }
+
+#if 1
+ /* dump the current pid */
+ if (!once) {
+ pid_t pid = getpid();
+ FILE *fp;
+
+ if ((fp = fopen(pidfilename, "w")) == NULL)
+ warnmsg(LOG_ERR, __func__,
+ "failed to open a pid log file(%s): %s",
+ pidfilename, strerror(errno));
+ else {
+ fprintf(fp, "%d\n", pid);
+ fclose(fp);
+ }
+ }
+#endif
+
+#ifndef HAVE_POLL_H
+ memset(fdsetp, 0, fdmasks);
+ FD_SET(s, fdsetp);
+ FD_SET(rtsock, fdsetp);
+#endif
+ while (1) { /* main loop */
+ int e;
+
+#ifndef HAVE_POLL_H
+ memcpy(selectfdp, fdsetp, fdmasks);
+#endif
+
+#ifndef SMALL
+ if (do_dump) { /* SIGUSR1 */
+ do_dump = 0;
+ rtsold_dump_file(dumpfilename);
+ }
+#endif
+
+ timeout = rtsol_check_timer();
+
+ if (once) {
+ struct ifinfo *ifi;
+
+ /* if we have no timeout, we are done (or failed) */
+ if (timeout == NULL)
+ break;
+
+ /* if all interfaces have got RA packet, we are done */
+ for (ifi = iflist; ifi; ifi = ifi->next) {
+ if (ifi->state != IFS_DOWN && ifi->racnt == 0)
+ break;
+ }
+ if (ifi == NULL)
+ break;
+ }
+#ifdef HAVE_POLL_H
+ e = poll(set, 2, timeout ? (timeout->tv_sec * 1000 + timeout->tv_usec / 1000) : INFTIM);
+#else
+ e = select(maxfd + 1, selectfdp, NULL, NULL, timeout);
+#endif
+ if (e < 1) {
+ if (e < 0 && errno != EINTR) {
+ warnmsg(LOG_ERR, __func__, "select: %s",
+ strerror(errno));
+ }
+ continue;
+ }
+
+ /* packet reception */
+#ifdef HAVE_POLL_H
+ if (set[1].revents & POLLIN)
+#else
+ if (FD_ISSET(rtsock, selectfdp))
+#endif
+ rtsock_input(rtsock);
+#ifdef HAVE_POLL_H
+ if (set[0].revents & POLLIN)
+#else
+ if (FD_ISSET(s, selectfdp))
+#endif
+ rtsol_input(s);
+ }
+ /* NOTREACHED */
+
+ return 0;
+}
+
+int
+ifconfig(char *ifname)
+{
+ struct ifinfo *ifinfo;
+ struct sockaddr_dl *sdl;
+ int flags;
+
+ if ((sdl = if_nametosdl(ifname)) == NULL) {
+ warnmsg(LOG_ERR, __func__,
+ "failed to get link layer information for %s", ifname);
+ return(-1);
+ }
+ if (find_ifinfo(sdl->sdl_index)) {
+ warnmsg(LOG_ERR, __func__,
+ "interface %s was already configured", ifname);
+ free(sdl);
+ return(-1);
+ }
+
+ if ((ifinfo = malloc(sizeof(*ifinfo))) == NULL) {
+ warnmsg(LOG_ERR, __func__, "memory allocation failed");
+ free(sdl);
+ return(-1);
+ }
+ memset(ifinfo, 0, sizeof(*ifinfo));
+ ifinfo->sdl = sdl;
+
+ strlcpy(ifinfo->ifname, ifname, sizeof(ifinfo->ifname));
+
+ /* construct a router solicitation message */
+ if (make_packet(ifinfo))
+ goto bad;
+
+ /* set link ID of this interface. */
+#ifdef HAVE_SCOPELIB
+ if (inet_zoneid(AF_INET6, 2, ifname, &ifinfo->linkid))
+ goto bad;
+#else
+ /* XXX: assume interface IDs as link IDs */
+ ifinfo->linkid = ifinfo->sdl->sdl_index;
+#endif
+
+ /*
+ * check if the interface is available.
+ * also check if SIOCGIFMEDIA ioctl is OK on the interface.
+ */
+ ifinfo->mediareqok = 1;
+ ifinfo->active = interface_status(ifinfo);
+ if (!ifinfo->mediareqok) {
+ /*
+ * probe routers periodically even if the link status
+ * does not change.
+ */
+ ifinfo->probeinterval = PROBE_INTERVAL;
+ }
+
+ /* activate interface: interface_up returns 0 on success */
+ flags = interface_up(ifinfo->ifname);
+ if (flags == 0)
+ ifinfo->state = IFS_DELAY;
+ else if (flags == IFS_TENTATIVE)
+ ifinfo->state = IFS_TENTATIVE;
+ else
+ ifinfo->state = IFS_DOWN;
+
+ rtsol_timer_update(ifinfo);
+
+ /* link into chain */
+ if (iflist)
+ ifinfo->next = iflist;
+ iflist = ifinfo;
+
+ return(0);
+
+bad:
+ free(ifinfo->sdl);
+ free(ifinfo);
+ return(-1);
+}
+
+void
+iflist_init(void)
+{
+ struct ifinfo *ifi, *next;
+
+ for (ifi = iflist; ifi; ifi = next) {
+ next = ifi->next;
+ if (ifi->sdl)
+ free(ifi->sdl);
+ if (ifi->rs_data)
+ free(ifi->rs_data);
+ free(ifi);
+ iflist = NULL;
+ }
+}
+
+#if 0
+static int
+ifreconfig(char *ifname)
+{
+ struct ifinfo *ifi, *prev;
+ int rv;
+
+ prev = NULL;
+ for (ifi = iflist; ifi; ifi = ifi->next) {
+ if (strncmp(ifi->ifname, ifname, sizeof(ifi->ifname)) == 0)
+ break;
+ prev = ifi;
+ }
+ prev->next = ifi->next;
+
+ rv = ifconfig(ifname);
+
+ /* reclaim it after ifconfig() in case ifname is pointer inside ifi */
+ if (ifi->rs_data)
+ free(ifi->rs_data);
+ free(ifi->sdl);
+ free(ifi);
+ return rv;
+}
+#endif
+
+struct ifinfo *
+find_ifinfo(int ifindex)
+{
+ struct ifinfo *ifi;
+
+ for (ifi = iflist; ifi; ifi = ifi->next)
+ if (ifi->sdl->sdl_index == ifindex)
+ return(ifi);
+ return(NULL);
+}
+
+static int
+make_packet(struct ifinfo *ifinfo)
+{
+ size_t packlen = sizeof(struct nd_router_solicit), lladdroptlen = 0;
+ struct nd_router_solicit *rs;
+ char *buf;
+
+ if ((lladdroptlen = lladdropt_length(ifinfo->sdl)) == 0) {
+ warnmsg(LOG_INFO, __func__,
+ "link-layer address option has null length"
+ " on %s. Treat as not included.", ifinfo->ifname);
+ }
+ packlen += lladdroptlen;
+ ifinfo->rs_datalen = packlen;
+
+ /* allocate buffer */
+ if ((buf = malloc(packlen)) == NULL) {
+ warnmsg(LOG_ERR, __func__,
+ "memory allocation failed for %s", ifinfo->ifname);
+ return(-1);
+ }
+ ifinfo->rs_data = buf;
+
+ /* fill in the message */
+ rs = (struct nd_router_solicit *)buf;
+ rs->nd_rs_type = ND_ROUTER_SOLICIT;
+ rs->nd_rs_code = 0;
+ rs->nd_rs_cksum = 0;
+ rs->nd_rs_reserved = 0;
+ buf += sizeof(*rs);
+
+ /* fill in source link-layer address option */
+ if (lladdroptlen)
+ lladdropt_fill(ifinfo->sdl, (struct nd_opt_hdr *)buf);
+
+ return(0);
+}
+
+static struct timeval *
+rtsol_check_timer(void)
+{
+ static struct timeval returnval;
+ struct timeval now, rtsol_timer;
+ struct ifinfo *ifinfo;
+ int flags;
+
+ gettimeofday(&now, NULL);
+
+ rtsol_timer = tm_max;
+
+ for (ifinfo = iflist; ifinfo; ifinfo = ifinfo->next) {
+ if (timercmp(&ifinfo->expire, &now, <=)) {
+ if (dflag > 1)
+ warnmsg(LOG_DEBUG, __func__,
+ "timer expiration on %s, "
+ "state = %d", ifinfo->ifname,
+ ifinfo->state);
+
+ switch (ifinfo->state) {
+ case IFS_DOWN:
+ case IFS_TENTATIVE:
+ /* interface_up returns 0 on success */
+ flags = interface_up(ifinfo->ifname);
+ if (flags == 0)
+ ifinfo->state = IFS_DELAY;
+ else if (flags == IFS_TENTATIVE)
+ ifinfo->state = IFS_TENTATIVE;
+ else
+ ifinfo->state = IFS_DOWN;
+ break;
+ case IFS_IDLE:
+ {
+ int oldstatus = ifinfo->active;
+ int probe = 0;
+
+ ifinfo->active = interface_status(ifinfo);
+
+ if (oldstatus != ifinfo->active) {
+ warnmsg(LOG_DEBUG, __func__,
+ "%s status is changed"
+ " from %d to %d",
+ ifinfo->ifname,
+ oldstatus, ifinfo->active);
+ probe = 1;
+ ifinfo->state = IFS_DELAY;
+ } else if (ifinfo->probeinterval &&
+ (ifinfo->probetimer -=
+ ifinfo->timer.tv_sec) <= 0) {
+ /* probe timer expired */
+ ifinfo->probetimer =
+ ifinfo->probeinterval;
+ probe = 1;
+ ifinfo->state = IFS_PROBE;
+ }
+
+ /*
+ * If we need a probe, clear the previous
+ * status wrt the "other" configuration.
+ */
+ if (probe)
+ ifinfo->otherconfig = 0;
+
+ if (probe && mobile_node)
+ defrouter_probe(ifinfo);
+ break;
+ }
+ case IFS_DELAY:
+ ifinfo->state = IFS_PROBE;
+ sendpacket(ifinfo);
+ break;
+ case IFS_PROBE:
+ if (ifinfo->probes < MAX_RTR_SOLICITATIONS)
+ sendpacket(ifinfo);
+ else {
+ warnmsg(LOG_INFO, __func__,
+ "No answer after sending %d RSs",
+ ifinfo->probes);
+ ifinfo->probes = 0;
+ ifinfo->state = IFS_IDLE;
+ }
+ break;
+ }
+ rtsol_timer_update(ifinfo);
+ }
+
+ if (timercmp(&ifinfo->expire, &rtsol_timer, <))
+ rtsol_timer = ifinfo->expire;
+ }
+
+ if (timercmp(&rtsol_timer, &tm_max, ==)) {
+ warnmsg(LOG_DEBUG, __func__, "there is no timer");
+ return(NULL);
+ } else if (timercmp(&rtsol_timer, &now, <))
+ /* this may occur when the interval is too small */
+ returnval.tv_sec = returnval.tv_usec = 0;
+ else
+ timersub(&rtsol_timer, &now, &returnval);
+
+ if (dflag > 1)
+ warnmsg(LOG_DEBUG, __func__, "New timer is %ld:%08ld",
+ (long)returnval.tv_sec, (long)returnval.tv_usec);
+
+ return(&returnval);
+}
+
+void
+rtsol_timer_update(struct ifinfo *ifinfo)
+{
+#define MILLION 1000000
+#define DADRETRY 10 /* XXX: adhoc */
+ long interval;
+ struct timeval now;
+
+ bzero(&ifinfo->timer, sizeof(ifinfo->timer));
+
+ switch (ifinfo->state) {
+ case IFS_DOWN:
+ case IFS_TENTATIVE:
+ if (++ifinfo->dadcount > DADRETRY) {
+ ifinfo->dadcount = 0;
+ ifinfo->timer.tv_sec = PROBE_INTERVAL;
+ } else
+ ifinfo->timer.tv_sec = 1;
+ break;
+ case IFS_IDLE:
+ if (mobile_node) {
+ /* XXX should be configurable */
+ ifinfo->timer.tv_sec = 3;
+ }
+ else
+ ifinfo->timer = tm_max; /* stop timer(valid?) */
+ break;
+ case IFS_DELAY:
+#ifndef HAVE_ARC4RANDOM
+ interval = random() % (MAX_RTR_SOLICITATION_DELAY * MILLION);
+#else
+ interval = arc4random_uniform(MAX_RTR_SOLICITATION_DELAY * MILLION);
+#endif
+ ifinfo->timer.tv_sec = interval / MILLION;
+ ifinfo->timer.tv_usec = interval % MILLION;
+ break;
+ case IFS_PROBE:
+ if (ifinfo->probes < MAX_RTR_SOLICITATIONS)
+ ifinfo->timer.tv_sec = RTR_SOLICITATION_INTERVAL;
+ else {
+ /*
+ * After sending MAX_RTR_SOLICITATIONS solicitations,
+ * we're just waiting for possible replies; there
+ * will be no more solicitation. Thus, we change
+ * the timer value to MAX_RTR_SOLICITATION_DELAY based
+ * on RFC 2461, Section 6.3.7.
+ */
+ ifinfo->timer.tv_sec = MAX_RTR_SOLICITATION_DELAY;
+ }
+ break;
+ default:
+ warnmsg(LOG_ERR, __func__,
+ "illegal interface state(%d) on %s",
+ ifinfo->state, ifinfo->ifname);
+ return;
+ }
+
+ /* reset the timer */
+ if (timercmp(&ifinfo->timer, &tm_max, ==)) {
+ ifinfo->expire = tm_max;
+ warnmsg(LOG_DEBUG, __func__,
+ "stop timer for %s", ifinfo->ifname);
+ } else {
+ gettimeofday(&now, NULL);
+ timeradd(&now, &ifinfo->timer, &ifinfo->expire);
+
+ if (dflag > 1)
+ warnmsg(LOG_DEBUG, __func__,
+ "set timer for %s to %d:%d", ifinfo->ifname,
+ (int)ifinfo->timer.tv_sec,
+ (int)ifinfo->timer.tv_usec);
+ }
+
+#undef MILLION
+}
+
+/* timer related utility functions */
+#define MILLION 1000000
+
+#ifndef SMALL
+static void
+rtsold_set_dump_file(int sig)
+{
+ do_dump = 1;
+}
+#endif
+
+static void
+usage(char *progname)
+{
+ if (progname && progname[strlen(progname) - 1] != 'd') {
+ fprintf(stderr, "usage: rtsol [-dDF] interfaces...\n");
+ fprintf(stderr, "usage: rtsol [-dDF] -a\n");
+ } else {
+ fprintf(stderr, "usage: rtsold [-adDfFm1] interfaces...\n");
+ fprintf(stderr, "usage: rtsold [-dDfFm1] -a\n");
+ }
+ exit(1);
+}
+
+void
+#if __STDC__
+warnmsg(int priority, const char *func, const char *msg, ...)
+#else
+warnmsg(priority, func, msg, va_alist)
+ int priority;
+ const char *func;
+ const char *msg;
+ va_dcl
+#endif
+{
+ va_list ap;
+ char buf[BUFSIZ];
+
+ va_start(ap, msg);
+ if (fflag) {
+ if (priority <= log_upto) {
+ (void)vfprintf(stderr, msg, ap);
+ (void)fprintf(stderr, "\n");
+ }
+ } else {
+ snprintf(buf, sizeof(buf), "<%s> %s", func, msg);
+ msg = buf;
+ vsyslog(priority, msg, ap);
+ }
+ va_end(ap);
+}
+
+/*
+ * return a list of interfaces which is suitable to sending an RS.
+ */
+char **
+autoifprobe(void)
+{
+ static char **argv = NULL;
+ static int n = 0;
+ char **a;
+ int i, found;
+ struct ifaddrs *ifap, *ifa, *target;
+
+ /* initialize */
+ while (n--)
+ free(argv[n]);
+ if (argv) {
+ free(argv);
+ argv = NULL;
+ }
+ n = 0;
+
+ if (getifaddrs(&ifap) != 0)
+ return NULL;
+
+ target = NULL;
+ /* find an ethernet */
+ for (ifa = ifap; ifa; ifa = ifa->ifa_next) {
+ if ((ifa->ifa_flags & IFF_UP) == 0)
+ continue;
+ if ((ifa->ifa_flags & IFF_POINTOPOINT) != 0)
+ continue;
+ if ((ifa->ifa_flags & IFF_LOOPBACK) != 0)
+ continue;
+ if ((ifa->ifa_flags & IFF_MULTICAST) == 0)
+ continue;
+
+ if (ifa->ifa_addr->sa_family != AF_INET6)
+ continue;
+
+ found = 0;
+ for (i = 0; i < n; i++) {
+ if (strcmp(argv[i], ifa->ifa_name) == 0) {
+ found++;
+ break;
+ }
+ }
+ if (found)
+ continue;
+
+ /* if we find multiple candidates, just warn. */
+ if (n != 0 && dflag > 1)
+ warnx("multiple interfaces found");
+
+ a = (char **)realloc(argv, (n + 1) * sizeof(char **));
+ if (a == NULL)
+ err(1, "realloc");
+ argv = a;
+ argv[n] = strdup(ifa->ifa_name);
+ if (!argv[n])
+ err(1, "malloc");
+ n++;
+ argv[n] = NULL;
+ }
+
+ if (n) {
+ a = (char **)realloc(argv, (n + 1) * sizeof(char **));
+ if (a == NULL)
+ err(1, "realloc");
+ argv = a;
+ argv[n] = NULL;
+
+ if (dflag > 0) {
+ for (i = 0; i < n; i++)
+ warnx("probing %s", argv[i]);
+ }
+ }
+ freeifaddrs(ifap);
+ return argv;
+}
diff --git a/usr.sbin/rtsold/rtsold.h b/usr.sbin/rtsold/rtsold.h
new file mode 100644
index 0000000..88e6311
--- /dev/null
+++ b/usr.sbin/rtsold/rtsold.h
@@ -0,0 +1,103 @@
+/* $KAME: rtsold.h,v 1.19 2003/04/16 09:48:15 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$
+ */
+
+struct ifinfo {
+ struct ifinfo *next; /* pointer to the next interface */
+
+ struct sockaddr_dl *sdl; /* link-layer address */
+ char ifname[IF_NAMESIZE]; /* interface name */
+ u_int32_t linkid; /* link ID of this interface */
+ int active; /* interface status */
+ int probeinterval; /* interval of probe timer (if necessary) */
+ int probetimer; /* rest of probe timer */
+ int mediareqok; /* wheter the IF supports SIOCGIFMEDIA */
+ int otherconfig; /* need a separate protocol for the "other"
+ * configuration */
+ int state;
+ int probes;
+ int dadcount;
+ struct timeval timer;
+ struct timeval expire;
+ int errors; /* # of errors we've got - detect wedge */
+
+ int racnt; /* total # of valid RAs it have got */
+
+ size_t rs_datalen;
+ u_char *rs_data;
+};
+
+/* per interface status */
+#define IFS_IDLE 0
+#define IFS_DELAY 1
+#define IFS_PROBE 2
+#define IFS_DOWN 3
+#define IFS_TENTATIVE 4
+
+/* rtsold.c */
+extern struct timeval tm_max;
+extern int dflag;
+extern int aflag;
+extern char *otherconf_script;
+extern int ifconfig(char *);
+extern void iflist_init(void);
+struct ifinfo *find_ifinfo(int);
+void rtsol_timer_update(struct ifinfo *);
+extern void warnmsg(int, const char *, const char *, ...)
+ __attribute__((__format__(__printf__, 3, 4)));
+extern char **autoifprobe(void);
+
+/* if.c */
+extern int ifinit(void);
+extern int interface_up(char *);
+extern int interface_status(struct ifinfo *);
+extern int lladdropt_length(struct sockaddr_dl *);
+extern void lladdropt_fill(struct sockaddr_dl *, struct nd_opt_hdr *);
+extern struct sockaddr_dl *if_nametosdl(char *);
+extern int getinet6sysctl(int);
+extern int setinet6sysctl(int, int);
+
+/* rtsol.c */
+extern int sockopen(void);
+extern void sendpacket(struct ifinfo *);
+extern void rtsol_input(int);
+
+/* probe.c */
+extern int probe_init(void);
+extern void defrouter_probe(struct ifinfo *);
+
+/* dump.c */
+extern void rtsold_dump_file(char *);
+
+/* rtsock.c */
+extern int rtsock_open(void);
+extern int rtsock_input(int);
diff --git a/usr.sbin/rwhod/Makefile b/usr.sbin/rwhod/Makefile
new file mode 100644
index 0000000..def0e6e
--- /dev/null
+++ b/usr.sbin/rwhod/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= rwhod
+MAN= rwhod.8
+
+# XXX breaks on Alpha due to alignment constraints
+#WARNS?= 4
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rwhod/rwhod.8 b/usr.sbin/rwhod/rwhod.8
new file mode 100644
index 0000000..642eeeb
--- /dev/null
+++ b/usr.sbin/rwhod/rwhod.8
@@ -0,0 +1,240 @@
+.\" 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.
+.\"
+.\" @(#)rwhod.8 8.2 (Berkeley) 12/11/93
+.\" $FreeBSD$
+.\"
+.Dd December 11, 1993
+.Dt RWHOD 8
+.Os
+.Sh NAME
+.Nm rwhod
+.Nd system status server
+.Sh SYNOPSIS
+.Nm
+.Op Fl i
+.Op Fl p
+.Op Fl l
+.Op Fl m Op Ar ttl
+.Sh DESCRIPTION
+The
+.Nm
+utility is the server which maintains the database used by the
+.Xr rwho 1
+and
+.Xr ruptime 1
+programs.
+Its operation is predicated on the ability to
+.Em broadcast
+or
+.Em multicast
+messages on a network.
+.Pp
+The
+.Nm
+utility operates as both a producer and consumer of status information,
+unless the
+.Fl l
+(listen mode) option is specified, in which case
+it acts as a consumer only.
+As a producer of information it periodically
+queries the state of the system and constructs
+status messages which are broadcasted or multicasted on a network.
+As a consumer of information, it listens for other
+.Nm
+servers' status messages, validating them, then recording
+them in a collection of files located in the directory
+.Pa /var/rwho .
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl i
+Enable insecure mode, which causes
+.Nm
+to ignore the source port on incoming packets.
+.It Fl p
+Ignore all
+.Dv POINTOPOINT
+interfaces.
+This is useful if you do not wish to keep dial on demand
+interfaces permanently active.
+.It Fl l
+Enable listen mode, which causes
+.Nm
+to not broadcast any information.
+This allows you to monitor other machines'
+.Nm
+information, without broadcasting your own.
+.It Fl m Op Ar ttl
+Cause
+.Nm
+to use IP multicast (instead of
+broadcast) on all interfaces that have
+the IFF_MULTICAST flag set in their "ifnet" structs
+(excluding the loopback interface).
+The multicast
+reports are sent with a time-to-live of 1, to prevent
+forwarding beyond the directly-connected subnet(s).
+.Pp
+If the optional
+.Ar ttl
+argument is supplied with the
+.Fl m
+flag,
+.Nm
+will send IP multicast datagrams with a
+time-to-live of
+.Ar ttl ,
+via a SINGLE interface rather
+than all interfaces.
+.Ar ttl
+must be between 0 and
+32 (or MAX_MULTICAST_SCOPE).
+Note that
+.Fl m Ar 1
+is different from
+.Fl m ,
+in that
+.Fl m Ar 1
+specifies transmission on one interface only.
+.Pp
+When
+.Fl m
+is used without a
+.Ar ttl
+argument, the program accepts multicast
+.Nm
+reports from all multicast-capable interfaces.
+If a
+.Ar ttl
+argument is given, it accepts multicast reports from only one interface, the
+one on which reports are sent (which may be controlled via the host's routing
+table).
+Regardless of the
+.Fl m
+option, the program accepts broadcast or
+unicast reports from all interfaces.
+Thus, this program will hear the
+reports of old, non-multicasting
+.Nm Ns s ,
+but, if multicasting is used,
+those old
+.Nm Ns s
+will not hear the reports generated by this program.
+.El
+.Pp
+The server transmits and receives messages at the port indicated
+in the ``who'' service specification; see
+.Xr services 5 .
+The messages sent and received, are of the form:
+.Bd -literal -offset indent
+struct outmp {
+ char out_line[8]; /* tty name */
+ char out_name[8]; /* user id */
+ long out_time; /* time on */
+};
+
+struct whod {
+ char wd_vers;
+ char wd_type;
+ char wd_fill[2];
+ int wd_sendtime;
+ int wd_recvtime;
+ char wd_hostname[32];
+ int wd_loadav[3];
+ int wd_boottime;
+ struct whoent {
+ struct outmp we_utmp;
+ int we_idle;
+ } wd_we[1024 / sizeof (struct whoent)];
+};
+.Ed
+.Pp
+All fields are converted to network byte order prior to
+transmission.
+The load averages are as calculated by the
+.Xr w 1
+program, and represent load averages over the 5, 10, and 15 minute
+intervals prior to a server's transmission; they are multiplied by 100
+for representation in an integer.
+The host name
+included is that returned by the
+.Xr gethostname 3
+system call, with any trailing domain name omitted.
+The array at the end of the message contains information about
+the users logged in to the sending machine.
+This information
+includes the contents of the
+.Xr utmp 5
+entry for each non-idle terminal line and a value indicating the
+time in seconds since a character was last received on the terminal line.
+.Pp
+Messages received by the
+.Nm rwho
+server are discarded unless they originated at an
+.Nm rwho
+server's port or the
+.Fl i
+option was specified.
+In addition, if the host's name, as specified
+in the message, contains any unprintable
+.Tn ASCII
+characters, the
+message is discarded.
+Valid messages received by
+.Nm
+are placed in files named
+.Pa whod.hostname
+in the directory
+.Pa /var/rwho .
+These files contain only the most recent message, in the
+format described above.
+.Pp
+Status messages are generated approximately once every
+3 minutes.
+The
+.Nm
+utility performs an
+.Xr nlist 3
+on
+.Pa /boot/kernel/kernel
+every 30 minutes to guard against
+the possibility that this file is not the system
+image currently operating.
+.Sh SEE ALSO
+.Xr ruptime 1 ,
+.Xr rwho 1
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.2 .
+.Sh BUGS
+Status information should be sent only upon request rather than continuously.
+People often interpret the server dying
+or network communication failures
+as a machine going down.
diff --git a/usr.sbin/rwhod/rwhod.c b/usr.sbin/rwhod/rwhod.c
new file mode 100644
index 0000000..96fd9ec
--- /dev/null
+++ b/usr.sbin/rwhod/rwhod.c
@@ -0,0 +1,737 @@
+/*
+ * 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 */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)rwhod.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/signal.h>
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <protocols/rwhod.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <timeconv.h>
+#include <unistd.h>
+#include <utmp.h>
+#include <pwd.h>
+#include <grp.h>
+
+/*
+ * This version of Berkeley's rwhod has been modified to use IP multicast
+ * datagrams, under control of a new command-line option:
+ *
+ * rwhod -m causes rwhod to use IP multicast (instead of
+ * broadcast or unicast) on all interfaces that have
+ * the IFF_MULTICAST flag set in their "ifnet" structs
+ * (excluding the loopback interface). The multicast
+ * reports are sent with a time-to-live of 1, to prevent
+ * forwarding beyond the directly-connected subnet(s).
+ *
+ * rwhod -m <ttl> causes rwhod to send IP multicast datagrams with a
+ * time-to-live of <ttl>, via a SINGLE interface rather
+ * than all interfaces. <ttl> must be between 0 and
+ * MAX_MULTICAST_SCOPE, defined below. Note that "-m 1"
+ * is different than "-m", in that "-m 1" specifies
+ * transmission on one interface only.
+ *
+ * When "-m" is used without a <ttl> argument, the program accepts multicast
+ * rwhod reports from all multicast-capable interfaces. If a <ttl> argument
+ * is given, it accepts multicast reports from only one interface, the one
+ * on which reports are sent (which may be controlled via the host's routing
+ * table). Regardless of the "-m" option, the program accepts broadcast or
+ * unicast reports from all interfaces. Thus, this program will hear the
+ * reports of old, non-multicasting rwhods, but, if multicasting is used,
+ * those old rwhods won't hear the reports generated by this program.
+ *
+ * -- Steve Deering, Stanford University, February 1989
+ */
+
+#define UNPRIV_USER "daemon"
+#define UNPRIV_GROUP "daemon"
+
+#define NO_MULTICAST 0 /* multicast modes */
+#define PER_INTERFACE_MULTICAST 1
+#define SCOPED_MULTICAST 2
+
+#define MAX_MULTICAST_SCOPE 32 /* "site-wide", by convention */
+
+#define INADDR_WHOD_GROUP (u_long)0xe0000103 /* 224.0.1.3 */
+ /* (belongs in protocols/rwhod.h) */
+
+int insecure_mode;
+int quiet_mode;
+int iff_flag = IFF_POINTOPOINT;
+int multicast_mode = NO_MULTICAST;
+int multicast_scope;
+struct sockaddr_in multicast_addr =
+ { sizeof multicast_addr, AF_INET, 0, { 0 }, { 0 } };
+
+/*
+ * Alarm interval. Don't forget to change the down time check in ruptime
+ * if this is changed.
+ */
+#define AL_INTERVAL (3 * 60)
+
+char myname[MAXHOSTNAMELEN];
+
+/*
+ * We communicate with each neighbor in a list constructed at the time we're
+ * started up. Neighbors are currently directly connected via a hardware
+ * interface.
+ */
+struct neighbor {
+ struct neighbor *n_next;
+ char *n_name; /* interface name */
+ struct sockaddr *n_addr; /* who to send to */
+ int n_addrlen; /* size of address */
+ int n_flags; /* should forward?, interface flags */
+};
+
+struct neighbor *neighbors;
+struct whod mywd;
+struct servent *sp;
+int s, utmpf;
+
+#define WHDRSIZE (int)(sizeof(mywd) - sizeof(mywd.wd_we))
+
+void run_as(uid_t *, gid_t *);
+int configure(int);
+void getboottime(int);
+void onalrm(int);
+void quit(const char *);
+void rt_xaddrs(caddr_t, caddr_t, struct rt_addrinfo *);
+int verify(char *, int);
+static void usage(void);
+#ifdef DEBUG
+char *interval(int, char *);
+void Sendto(int, const void *, size_t, int, const struct sockaddr *, int);
+#define sendto Sendto
+#endif
+
+int
+main(int argc, char *argv[])
+{
+ struct sockaddr_in from;
+ struct stat st;
+ char path[64];
+ int on = 1;
+ char *cp;
+ struct sockaddr_in soin;
+ uid_t unpriv_uid;
+ gid_t unpriv_gid;
+
+ if (getuid())
+ errx(1, "not super user");
+
+ run_as(&unpriv_uid, &unpriv_gid);
+
+ argv++; argc--;
+ while (argc > 0 && *argv[0] == '-') {
+ if (strcmp(*argv, "-m") == 0) {
+ if (argc > 1 && isdigit(*(argv + 1)[0])) {
+ argv++, argc--;
+ multicast_mode = SCOPED_MULTICAST;
+ multicast_scope = atoi(*argv);
+ if (multicast_scope > MAX_MULTICAST_SCOPE)
+ errx(1, "ttl must not exceed %u",
+ MAX_MULTICAST_SCOPE);
+ }
+ else multicast_mode = PER_INTERFACE_MULTICAST;
+ }
+ else if (strcmp(*argv, "-i") == 0)
+ insecure_mode = 1;
+ else if (strcmp(*argv, "-l") == 0)
+ quiet_mode = 1;
+ else if (strcmp(*argv, "-p") == 0)
+ iff_flag = 0;
+ else
+ usage();
+ argv++, argc--;
+ }
+ if (argc > 0)
+ usage();
+#ifndef DEBUG
+ daemon(1, 0);
+#endif
+ (void) signal(SIGHUP, getboottime);
+ openlog("rwhod", LOG_PID, LOG_DAEMON);
+ sp = getservbyname("who", "udp");
+ if (sp == NULL) {
+ syslog(LOG_ERR, "who/udp: unknown service");
+ exit(1);
+ }
+ if (chdir(_PATH_RWHODIR) < 0) {
+ syslog(LOG_ERR, "%s: %m", _PATH_RWHODIR);
+ exit(1);
+ }
+ /*
+ * Establish host name as returned by system.
+ */
+ if (gethostname(myname, sizeof(myname) - 1) < 0) {
+ syslog(LOG_ERR, "gethostname: %m");
+ exit(1);
+ }
+ if ((cp = index(myname, '.')) != NULL)
+ *cp = '\0';
+ strncpy(mywd.wd_hostname, myname, sizeof(mywd.wd_hostname) - 1);
+ mywd.wd_hostname[sizeof(mywd.wd_hostname) - 1] = '\0';
+ utmpf = open(_PATH_UTMP, O_RDONLY|O_CREAT, 0644);
+ if (utmpf < 0) {
+ syslog(LOG_ERR, "%s: %m", _PATH_UTMP);
+ exit(1);
+ }
+ getboottime(0);
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "socket: %m");
+ exit(1);
+ }
+ if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
+ syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m");
+ exit(1);
+ }
+ memset(&soin, 0, sizeof(soin));
+ soin.sin_len = sizeof(soin);
+ soin.sin_family = AF_INET;
+ soin.sin_port = sp->s_port;
+ if (bind(s, (struct sockaddr *)&soin, sizeof(soin)) < 0) {
+ syslog(LOG_ERR, "bind: %m");
+ exit(1);
+ }
+ setgid(unpriv_gid);
+ setgroups(1, &unpriv_gid); /* XXX BOGUS groups[0] = egid */
+ setuid(unpriv_uid);
+ if (!configure(s))
+ exit(1);
+ if (!quiet_mode) {
+ signal(SIGALRM, onalrm);
+ onalrm(0);
+ }
+ for (;;) {
+ struct whod wd;
+ socklen_t len = sizeof(from);
+ int cc, whod;
+ time_t t;
+
+ cc = recvfrom(s, (char *)&wd, sizeof(struct whod), 0,
+ (struct sockaddr *)&from, &len);
+ if (cc <= 0) {
+ if (cc < 0 && errno != EINTR)
+ syslog(LOG_WARNING, "recv: %m");
+ continue;
+ }
+ if (from.sin_port != sp->s_port && !insecure_mode) {
+ syslog(LOG_WARNING, "%d: bad source port from %s",
+ ntohs(from.sin_port), inet_ntoa(from.sin_addr));
+ continue;
+ }
+ if (cc < WHDRSIZE) {
+ syslog(LOG_WARNING, "short packet from %s",
+ inet_ntoa(from.sin_addr));
+ continue;
+ }
+ if (wd.wd_vers != WHODVERSION)
+ continue;
+ if (wd.wd_type != WHODTYPE_STATUS)
+ continue;
+ if (!verify(wd.wd_hostname, sizeof wd.wd_hostname)) {
+ syslog(LOG_WARNING, "malformed host name from %s",
+ inet_ntoa(from.sin_addr));
+ continue;
+ }
+ (void) snprintf(path, sizeof path, "whod.%s", wd.wd_hostname);
+ /*
+ * Rather than truncating and growing the file each time,
+ * use ftruncate if size is less than previous size.
+ */
+ whod = open(path, O_WRONLY | O_CREAT, 0644);
+ if (whod < 0) {
+ syslog(LOG_WARNING, "%s: %m", path);
+ continue;
+ }
+#if ENDIAN != BIG_ENDIAN
+ {
+ int i, n = (cc - WHDRSIZE)/sizeof(struct whoent);
+ struct whoent *we;
+
+ /* undo header byte swapping before writing to file */
+ wd.wd_sendtime = ntohl(wd.wd_sendtime);
+ for (i = 0; i < 3; i++)
+ wd.wd_loadav[i] = ntohl(wd.wd_loadav[i]);
+ wd.wd_boottime = ntohl(wd.wd_boottime);
+ we = wd.wd_we;
+ for (i = 0; i < n; i++) {
+ we->we_idle = ntohl(we->we_idle);
+ we->we_utmp.out_time =
+ ntohl(we->we_utmp.out_time);
+ we++;
+ }
+ }
+#endif
+ (void) time(&t);
+ wd.wd_recvtime = _time_to_int(t);
+ (void) write(whod, (char *)&wd, cc);
+ if (fstat(whod, &st) < 0 || st.st_size > cc)
+ ftruncate(whod, cc);
+ (void) close(whod);
+ }
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: rwhod [-i] [-p] [-l] [-m [ttl]]\n");
+ exit(1);
+}
+
+void
+run_as(uid, gid)
+ uid_t *uid;
+ gid_t *gid;
+{
+ struct passwd *pw;
+ struct group *gr;
+
+ pw = getpwnam(UNPRIV_USER);
+ if (!pw) {
+ syslog(LOG_ERR, "getpwnam(%s): %m", UNPRIV_USER);
+ exit(1);
+ }
+ *uid = pw->pw_uid;
+
+ gr = getgrnam(UNPRIV_GROUP);
+ if (!gr) {
+ syslog(LOG_ERR, "getgrnam(%s): %m", UNPRIV_GROUP);
+ exit(1);
+ }
+ *gid = gr->gr_gid;
+}
+
+/*
+ * Check out host name for unprintables
+ * and other funnies before allowing a file
+ * to be created. Sorry, but blanks aren't allowed.
+ */
+int
+verify(name, maxlen)
+ register char *name;
+ register int maxlen;
+{
+ register int size = 0;
+
+ while (*name && size < maxlen - 1) {
+ if (!isascii(*name) || !(isalnum(*name) || ispunct(*name)))
+ return (0);
+ name++, size++;
+ }
+ *name = '\0';
+ return (size > 0);
+}
+
+int utmptime;
+int utmpent;
+int utmpsize = 0;
+struct utmp *utmp;
+int alarmcount;
+
+void
+onalrm(signo)
+ int signo __unused;
+{
+ register struct neighbor *np;
+ register struct whoent *we = mywd.wd_we, *wlast;
+ register int i;
+ struct stat stb;
+ double avenrun[3];
+ time_t now;
+ int cc;
+
+ now = time(NULL);
+ if (alarmcount % 10 == 0)
+ getboottime(0);
+ alarmcount++;
+ (void) fstat(utmpf, &stb);
+ if ((stb.st_mtime != utmptime) || (stb.st_size > utmpsize)) {
+ utmptime = stb.st_mtime;
+ if (stb.st_size > utmpsize) {
+ utmpsize = stb.st_size + 10 * sizeof(struct utmp);
+ utmp = (struct utmp *)reallocf(utmp, utmpsize);
+ if (utmp == NULL) {
+ syslog(LOG_WARNING, "malloc failed");
+ utmpsize = 0;
+ goto done;
+ }
+ }
+ (void) lseek(utmpf, (off_t)0, L_SET);
+ cc = read(utmpf, (char *)utmp, stb.st_size);
+ if (cc < 0) {
+ syslog(LOG_ERR, "read(%s): %m", _PATH_UTMP);
+ goto done;
+ }
+ wlast = &mywd.wd_we[1024 / sizeof(struct whoent) - 1];
+ utmpent = cc / sizeof(struct utmp);
+ for (i = 0; i < utmpent; i++)
+ if (utmp[i].ut_name[0]) {
+ memcpy(we->we_utmp.out_line, utmp[i].ut_line,
+ sizeof(utmp[i].ut_line));
+ memcpy(we->we_utmp.out_name, utmp[i].ut_name,
+ sizeof(utmp[i].ut_name));
+ we->we_utmp.out_time = htonl(utmp[i].ut_time);
+ if (we >= wlast)
+ break;
+ we++;
+ }
+ utmpent = we - mywd.wd_we;
+ }
+
+ /*
+ * The test on utmpent looks silly---after all, if no one is
+ * logged on, why worry about efficiency?---but is useful on
+ * (e.g.) compute servers.
+ */
+ if (utmpent && chdir(_PATH_DEV)) {
+ syslog(LOG_ERR, "chdir(%s): %m", _PATH_DEV);
+ exit(1);
+ }
+ we = mywd.wd_we;
+ for (i = 0; i < utmpent; i++) {
+ if (stat(we->we_utmp.out_line, &stb) >= 0)
+ we->we_idle = htonl(now - stb.st_atime);
+ we++;
+ }
+ (void)getloadavg(avenrun, sizeof(avenrun)/sizeof(avenrun[0]));
+ for (i = 0; i < 3; i++)
+ mywd.wd_loadav[i] = htonl((u_long)(avenrun[i] * 100));
+ cc = (char *)we - (char *)&mywd;
+ mywd.wd_sendtime = htonl(_time_to_time32(time(NULL)));
+ mywd.wd_vers = WHODVERSION;
+ mywd.wd_type = WHODTYPE_STATUS;
+ if (multicast_mode == SCOPED_MULTICAST) {
+ (void) sendto(s, (char *)&mywd, cc, 0,
+ (struct sockaddr *)&multicast_addr,
+ sizeof(multicast_addr));
+ }
+ else for (np = neighbors; np != NULL; np = np->n_next) {
+ if (multicast_mode == PER_INTERFACE_MULTICAST &&
+ np->n_flags & IFF_MULTICAST) {
+ /*
+ * Select the outgoing interface for the multicast.
+ */
+ if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF,
+ &(((struct sockaddr_in *)np->n_addr)->sin_addr),
+ sizeof(struct in_addr)) < 0) {
+ syslog(LOG_ERR,
+ "setsockopt IP_MULTICAST_IF: %m");
+ exit(1);
+ }
+ (void) sendto(s, (char *)&mywd, cc, 0,
+ (struct sockaddr *)&multicast_addr,
+ sizeof(multicast_addr));
+ } else (void) sendto(s, (char *)&mywd, cc, 0,
+ np->n_addr, np->n_addrlen);
+ }
+ if (utmpent && chdir(_PATH_RWHODIR)) {
+ syslog(LOG_ERR, "chdir(%s): %m", _PATH_RWHODIR);
+ exit(1);
+ }
+done:
+ (void) alarm(AL_INTERVAL);
+}
+
+void
+getboottime(signo)
+ int signo __unused;
+{
+ int mib[2];
+ size_t size;
+ struct timeval tm;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_BOOTTIME;
+ size = sizeof(tm);
+ if (sysctl(mib, 2, &tm, &size, NULL, 0) == -1) {
+ syslog(LOG_ERR, "cannot get boottime: %m");
+ exit(1);
+ }
+ mywd.wd_boottime = htonl(_time_to_time32(tm.tv_sec));
+}
+
+void
+quit(msg)
+ const char *msg;
+{
+ syslog(LOG_ERR, "%s", msg);
+ exit(1);
+}
+
+void
+rt_xaddrs(cp, cplim, rtinfo)
+ register caddr_t cp, cplim;
+ register struct rt_addrinfo *rtinfo;
+{
+ register struct sockaddr *sa;
+ register 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);
+ }
+}
+
+/*
+ * Figure out device configuration and select
+ * networks which deserve status information.
+ */
+int
+configure(so)
+ int so;
+{
+ register struct neighbor *np;
+ register struct if_msghdr *ifm;
+ register struct ifa_msghdr *ifam;
+ struct sockaddr_dl *sdl;
+ size_t needed;
+ int mib[6], flags = 0, len;
+ char *buf, *lim, *next;
+ struct rt_addrinfo info;
+
+ if (multicast_mode != NO_MULTICAST) {
+ multicast_addr.sin_addr.s_addr = htonl(INADDR_WHOD_GROUP);
+ multicast_addr.sin_port = sp->s_port;
+ }
+
+ if (multicast_mode == SCOPED_MULTICAST) {
+ struct ip_mreq mreq;
+ unsigned char ttl;
+
+ mreq.imr_multiaddr.s_addr = htonl(INADDR_WHOD_GROUP);
+ mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+ if (setsockopt(so, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &mreq, sizeof(mreq)) < 0) {
+ syslog(LOG_ERR,
+ "setsockopt IP_ADD_MEMBERSHIP: %m");
+ return(0);
+ }
+ ttl = multicast_scope;
+ if (setsockopt(so, IPPROTO_IP, IP_MULTICAST_TTL,
+ &ttl, sizeof(ttl)) < 0) {
+ syslog(LOG_ERR,
+ "setsockopt IP_MULTICAST_TTL: %m");
+ return(0);
+ }
+ return(1);
+ }
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET;
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0;
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
+ quit("route-sysctl-estimate");
+ if ((buf = malloc(needed)) == NULL)
+ quit("malloc");
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
+ quit("actual retrieval of interface table");
+ lim = buf + needed;
+
+ sdl = NULL; /* XXX just to keep gcc -Wall happy */
+ for (next = buf; next < lim; next += ifm->ifm_msglen) {
+ ifm = (struct if_msghdr *)next;
+ if (ifm->ifm_type == RTM_IFINFO) {
+ sdl = (struct sockaddr_dl *)(ifm + 1);
+ flags = ifm->ifm_flags;
+ continue;
+ }
+ if ((flags & IFF_UP) == 0 ||
+ (flags & (((multicast_mode == PER_INTERFACE_MULTICAST) ?
+ IFF_MULTICAST : 0) |
+ IFF_BROADCAST|iff_flag)) == 0)
+ continue;
+ if (ifm->ifm_type != RTM_NEWADDR)
+ quit("out of sync parsing NET_RT_IFLIST");
+ ifam = (struct ifa_msghdr *)ifm;
+ info.rti_addrs = ifam->ifam_addrs;
+ rt_xaddrs((char *)(ifam + 1), ifam->ifam_msglen + (char *)ifam,
+ &info);
+ /* gag, wish we could get rid of Internet dependencies */
+#define dstaddr info.rti_info[RTAX_BRD]
+#define ifaddr info.rti_info[RTAX_IFA]
+#define IPADDR_SA(x) ((struct sockaddr_in *)(x))->sin_addr.s_addr
+#define PORT_SA(x) ((struct sockaddr_in *)(x))->sin_port
+ if (dstaddr == 0 || dstaddr->sa_family != AF_INET)
+ continue;
+ PORT_SA(dstaddr) = sp->s_port;
+ for (np = neighbors; np != NULL; np = np->n_next)
+ if (memcmp(sdl->sdl_data, np->n_name,
+ sdl->sdl_nlen) == 0 &&
+ IPADDR_SA(np->n_addr) == IPADDR_SA(dstaddr))
+ break;
+ if (np != NULL)
+ continue;
+ len = sizeof(*np) + dstaddr->sa_len + sdl->sdl_nlen + 1;
+ np = (struct neighbor *)malloc(len);
+ if (np == NULL)
+ quit("malloc of neighbor structure");
+ memset(np, 0, len);
+ np->n_flags = flags;
+ np->n_addr = (struct sockaddr *)(np + 1);
+ np->n_addrlen = dstaddr->sa_len;
+ np->n_name = np->n_addrlen + (char *)np->n_addr;
+ memcpy((char *)np->n_addr, (char *)dstaddr, np->n_addrlen);
+ memcpy(np->n_name, sdl->sdl_data, sdl->sdl_nlen);
+ if (multicast_mode == PER_INTERFACE_MULTICAST &&
+ (flags & IFF_MULTICAST) &&
+ !(flags & IFF_LOOPBACK)) {
+ struct ip_mreq mreq;
+
+ memcpy((char *)np->n_addr, (char *)ifaddr,
+ np->n_addrlen);
+ mreq.imr_multiaddr.s_addr = htonl(INADDR_WHOD_GROUP);
+ mreq.imr_interface.s_addr =
+ ((struct sockaddr_in *)np->n_addr)->sin_addr.s_addr;
+ if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &mreq, sizeof(mreq)) < 0) {
+ syslog(LOG_ERR,
+ "setsockopt IP_ADD_MEMBERSHIP: %m");
+#if 0
+ /* Fall back to broadcast on this if. */
+ np->n_flags &= ~IFF_MULTICAST;
+#else
+ free((char *)np);
+ continue;
+#endif
+ }
+ }
+ np->n_next = neighbors;
+ neighbors = np;
+ }
+ free(buf);
+ return (1);
+}
+
+#ifdef DEBUG
+void
+Sendto(s, buf, cc, flags, to, tolen)
+ int s;
+ const void *buf;
+ size_t cc;
+ int flags;
+ const struct sockaddr *to;
+ int tolen;
+{
+ register struct whod *w = (struct whod *)buf;
+ register struct whoent *we;
+ struct sockaddr_in *sin = (struct sockaddr_in *)to;
+
+ printf("sendto %x.%d\n", ntohl(sin->sin_addr.s_addr),
+ ntohs(sin->sin_port));
+ printf("hostname %s %s\n", w->wd_hostname,
+ interval(ntohl(w->wd_sendtime) - ntohl(w->wd_boottime), " up"));
+ printf("load %4.2f, %4.2f, %4.2f\n",
+ ntohl(w->wd_loadav[0]) / 100.0, ntohl(w->wd_loadav[1]) / 100.0,
+ ntohl(w->wd_loadav[2]) / 100.0);
+ cc -= WHDRSIZE;
+ for (we = w->wd_we, cc /= sizeof(struct whoent); cc > 0; cc--, we++) {
+ time_t t = _time32_to_time(ntohl(we->we_utmp.out_time));
+ printf("%-8.8s %s:%s %.12s",
+ we->we_utmp.out_name,
+ w->wd_hostname, we->we_utmp.out_line,
+ ctime(&t)+4);
+ we->we_idle = ntohl(we->we_idle) / 60;
+ if (we->we_idle) {
+ if (we->we_idle >= 100*60)
+ we->we_idle = 100*60 - 1;
+ if (we->we_idle >= 60)
+ printf(" %2d", we->we_idle / 60);
+ else
+ printf(" ");
+ printf(":%02d", we->we_idle % 60);
+ }
+ printf("\n");
+ }
+}
+
+char *
+interval(time, updown)
+ int time;
+ char *updown;
+{
+ static char resbuf[32];
+ int days, hours, minutes;
+
+ if (time < 0 || time > 3*30*24*60*60) {
+ (void) sprintf(resbuf, " %s ??:??", updown);
+ return (resbuf);
+ }
+ minutes = (time + 59) / 60; /* round to minutes */
+ hours = minutes / 60; minutes %= 60;
+ days = hours / 24; hours %= 24;
+ if (days)
+ (void) sprintf(resbuf, "%s %2d+%02d:%02d",
+ updown, days, hours, minutes);
+ else
+ (void) sprintf(resbuf, "%s %2d:%02d",
+ updown, hours, minutes);
+ return (resbuf);
+}
+#endif
diff --git a/usr.sbin/sa/Makefile b/usr.sbin/sa/Makefile
new file mode 100644
index 0000000..718cbc8
--- /dev/null
+++ b/usr.sbin/sa/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../usr.bin/lastcomm
+
+PROG= sa
+MAN= sa.8
+SRCS= main.c db.c pdb.c usrdb.c readrec.c
+
+WARNS?= 6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/sa/db.c b/usr.sbin/sa/db.c
new file mode 100644
index 0000000..c6b25f3
--- /dev/null
+++ b/usr.sbin/sa/db.c
@@ -0,0 +1,207 @@
+/*-
+ * Copyright (c) 2007 Diomidis Spinellis
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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/acct.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <db.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+
+#include "extern.h"
+
+/* Key used to store the version of the database data elements. */
+#define VERSION_KEY "\0VERSION"
+
+/*
+ * Create the in-memory database, *mdb.
+ * If iflag is not set, fill-in mdb with the records of the disk-based
+ * database dbname.
+ * Upgrade old-version records by calling v1_to_v2.
+ * Return 0 if OK, -1 on error.
+ */
+int
+db_copy_in(DB **mdb, const char *dbname, const char *uname, BTREEINFO *bti,
+ int (*v1_to_v2)(DBT *key, DBT *data))
+{
+ DBT key, data;
+ DB *ddb;
+ int error, rv, version;
+
+ if ((*mdb = dbopen(NULL, O_RDWR, 0, DB_BTREE, bti)) == NULL)
+ return (-1);
+
+ if (iflag)
+ return (0);
+
+ if ((ddb = dbopen(dbname, O_RDONLY, 0, DB_BTREE, bti)) == NULL) {
+ if (errno == ENOENT)
+ return (0);
+ warn("retrieving %s summary", uname);
+ db_destroy(*mdb, uname);
+ return (-1);
+ }
+
+ error = 0;
+
+ /* Obtain/set version. */
+ version = 1;
+ key.data = &VERSION_KEY;
+ key.size = sizeof(VERSION_KEY);
+
+ rv = DB_GET(ddb, &key, &data, 0);
+ if (rv < 0) {
+ warn("get version key from %s stats", uname);
+ error = -1;
+ goto closeout;
+ } else if (rv == 0) { /* It's there; verify version. */
+ if (data.size != sizeof(version)) {
+ warnx("invalid version size %zd in %s",
+ data.size, uname);
+ error = -1;
+ goto closeout;
+ }
+ memcpy(&version, data.data, data.size);
+ if (version != 2) {
+ warnx("unsupported version %d in %s",
+ version, uname);
+ error = -1;
+ goto closeout;
+ }
+ }
+
+ for (rv = DB_SEQ(ddb, &key, &data, R_FIRST); rv == 0;
+ rv = DB_SEQ(ddb, &key, &data, R_NEXT)) {
+
+ /* See if this is a version record. */
+ if (key.size == sizeof(VERSION_KEY) &&
+ memcmp(key.data, VERSION_KEY, sizeof(VERSION_KEY)) == 0)
+ continue;
+
+ /* Convert record from v1, if needed. */
+ if (version == 1 && v1_to_v2(&key, &data) < 0) {
+ warn("converting %s stats", uname);
+ error = -1;
+ goto closeout;
+ }
+
+ /* Copy record to the in-memory database. */
+ if ((rv = DB_PUT(*mdb, &key, &data, 0)) < 0) {
+ warn("initializing %s stats", uname);
+ error = -1;
+ goto closeout;
+ }
+ }
+ if (rv < 0) {
+ warn("retrieving %s summary", uname);
+ error = -1;
+ }
+
+closeout:
+ if (DB_CLOSE(ddb) < 0) {
+ warn("closing %s summary", uname);
+ error = -1;
+ }
+
+ if (error)
+ db_destroy(*mdb, uname);
+ return (error);
+}
+
+/*
+ * Save the in-memory database mdb to the disk database dbname.
+ * Return 0 if OK, -1 on error.
+ */
+int
+db_copy_out(DB *mdb, const char *dbname, const char *uname, BTREEINFO *bti)
+{
+ DB *ddb;
+ DBT key, data;
+ int error, rv, version;
+
+ if ((ddb = dbopen(dbname, O_RDWR|O_CREAT|O_TRUNC, 0644,
+ DB_BTREE, bti)) == NULL) {
+ warn("creating %s summary", uname);
+ return (-1);
+ }
+
+ error = 0;
+
+ for (rv = DB_SEQ(mdb, &key, &data, R_FIRST);
+ rv == 0; rv = DB_SEQ(mdb, &key, &data, R_NEXT)) {
+ if ((rv = DB_PUT(ddb, &key, &data, 0)) < 0) {
+ warn("saving %s summary", uname);
+ error = -1;
+ goto out;
+ }
+ }
+ if (rv < 0) {
+ warn("retrieving %s stats", uname);
+ error = -1;
+ }
+
+out:
+ /* Add a version record. */
+ key.data = &VERSION_KEY;
+ key.size = sizeof(VERSION_KEY);
+ version = 2;
+ data.data = &version;
+ data.size = sizeof(version);
+ if ((rv = DB_PUT(ddb, &key, &data, 0)) < 0) {
+ warn("add version record to %s stats", uname);
+ error = -1;
+ } else if (rv == 1) {
+ warnx("duplicate version record in %s stats", uname);
+ error = -1;
+ }
+
+ if (DB_SYNC(ddb, 0) < 0) {
+ warn("syncing %s summary", uname);
+ error = -1;
+ }
+ if (DB_CLOSE(ddb) < 0) {
+ warn("closing %s summary", uname);
+ error = -1;
+ }
+ return error;
+}
+
+void
+db_destroy(DB *db, const char *uname)
+{
+ if (DB_CLOSE(db) < 0)
+ warn("destroying %s stats", uname);
+}
diff --git a/usr.sbin/sa/extern.h b/usr.sbin/sa/extern.h
new file mode 100644
index 0000000..70f97a7
--- /dev/null
+++ b/usr.sbin/sa/extern.h
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <db.h>
+
+/* structures */
+
+/* All times are stored in 1e-6s units. */
+
+struct cmdinfo {
+ char ci_comm[MAXCOMLEN+2]; /* command name (+ '*') */
+ uid_t ci_uid; /* user id */
+ u_quad_t ci_calls; /* number of calls */
+ double ci_etime; /* elapsed time */
+ double ci_utime; /* user time */
+ double ci_stime; /* system time */
+ double ci_mem; /* memory use */
+ double ci_io; /* number of disk i/o ops */
+ u_int ci_flags; /* flags; see below */
+};
+#define CI_UNPRINTABLE 0x0001 /* unprintable chars in name */
+
+struct userinfo {
+ uid_t ui_uid; /* user id; for consistency */
+ u_quad_t ui_calls; /* number of invocations */
+ double ui_utime; /* user time */
+ double ui_stime; /* system time */
+ double ui_mem; /* memory use */
+ double ui_io; /* number of disk i/o ops */
+};
+
+/* typedefs */
+
+typedef int (*cmpf_t)(const DBT *, const DBT *);
+
+/* external functions in db.c */
+int db_copy_in(DB **mdb, const char *dbname, const char *name,
+ BTREEINFO *bti, int (*v1_to_v2)(DBT *key, DBT *data));
+int db_copy_out(DB *mdb, const char *dbname, const char *name,
+ BTREEINFO *bti);
+void db_destroy(DB *db, const char *uname);
+
+/* external functions in pdb.c */
+int pacct_init(void);
+void pacct_destroy(void);
+int pacct_add(const struct cmdinfo *);
+int pacct_update(void);
+void pacct_print(void);
+
+/* external functions in readrec.c */
+int readrec_forward(FILE *f, struct acctv2 *av2);
+
+/* external functions in usrdb.c */
+int usracct_init(void);
+void usracct_destroy(void);
+int usracct_add(const struct cmdinfo *);
+int usracct_update(void);
+void usracct_print(void);
+
+/* variables */
+
+extern int aflag, bflag, cflag, dflag, Dflag, fflag, iflag, jflag, kflag;
+extern int Kflag, lflag, mflag, qflag, rflag, sflag, tflag, uflag, vflag;
+extern u_quad_t cutoff;
+extern cmpf_t sa_cmp;
+extern const char *pdb_file, *usrdb_file;
+
+/* some #defines to help with db's stupidity */
+
+#define DB_CLOSE(db) \
+ ((*(db)->close)(db))
+#define DB_GET(db, key, data, flags) \
+ ((*(db)->get)((db), (key), (data), (flags)))
+#define DB_PUT(db, key, data, flags) \
+ ((*(db)->put)((db), (key), (data), (flags)))
+#define DB_SYNC(db, flags) \
+ ((*(db)->sync)((db), (flags)))
+#define DB_SEQ(db, key, data, flags) \
+ ((*(db)->seq)((db), (key), (data), (flags)))
diff --git a/usr.sbin/sa/main.c b/usr.sbin/sa/main.c
new file mode 100644
index 0000000..7be13e5
--- /dev/null
+++ b/usr.sbin/sa/main.c
@@ -0,0 +1,533 @@
+/*
+ * 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.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1994 Christopher G. Demetriou\n\
+ All rights reserved.\n";
+#endif
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * sa: system accounting
+ */
+
+#include <sys/types.h>
+#include <sys/acct.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "extern.h"
+#include "pathnames.h"
+
+static FILE *acct_load(const char *, int);
+static int cmp_comm(const char *, const char *);
+static int cmp_usrsys(const DBT *, const DBT *);
+static int cmp_avgusrsys(const DBT *, const DBT *);
+static int cmp_dkio(const DBT *, const DBT *);
+static int cmp_avgdkio(const DBT *, const DBT *);
+static int cmp_cpumem(const DBT *, const DBT *);
+static int cmp_avgcpumem(const DBT *, const DBT *);
+static int cmp_calls(const DBT *, const DBT *);
+static void usage(void);
+
+int aflag, bflag, cflag, dflag, Dflag, fflag, iflag, jflag, kflag;
+int Kflag, lflag, mflag, qflag, rflag, sflag, tflag, uflag, vflag;
+u_quad_t cutoff = 1;
+const char *pdb_file = _PATH_SAVACCT;
+const char *usrdb_file = _PATH_USRACCT;
+
+static char *dfltargv[] = { NULL };
+static int dfltargc = (sizeof dfltargv/sizeof(char *));
+
+/* default to comparing by sum of user + system time */
+cmpf_t sa_cmp = cmp_usrsys;
+
+int
+main(int argc, char **argv)
+{
+ FILE *f;
+ char pathacct[] = _PATH_ACCT;
+ int ch, error = 0;
+
+ dfltargv[0] = pathacct;
+
+ while ((ch = getopt(argc, argv, "abcdDfijkKlmnP:qrstuU:v:")) != -1)
+ switch (ch) {
+ case 'a':
+ /* print all commands */
+ aflag = 1;
+ break;
+ case 'b':
+ /* sort by per-call user/system time average */
+ bflag = 1;
+ sa_cmp = cmp_avgusrsys;
+ break;
+ case 'c':
+ /* print percentage total time */
+ cflag = 1;
+ break;
+ case 'd':
+ /* sort by averge number of disk I/O ops */
+ dflag = 1;
+ sa_cmp = cmp_avgdkio;
+ break;
+ case 'D':
+ /* print and sort by total disk I/O ops */
+ Dflag = 1;
+ sa_cmp = cmp_dkio;
+ break;
+ case 'f':
+ /* force no interactive threshold comprison */
+ fflag = 1;
+ break;
+ case 'i':
+ /* do not read in summary file */
+ iflag = 1;
+ break;
+ case 'j':
+ /* instead of total minutes, give sec/call */
+ jflag = 1;
+ break;
+ case 'k':
+ /* sort by cpu-time average memory usage */
+ kflag = 1;
+ sa_cmp = cmp_avgcpumem;
+ break;
+ case 'K':
+ /* print and sort by cpu-storage integral */
+ sa_cmp = cmp_cpumem;
+ Kflag = 1;
+ break;
+ case 'l':
+ /* separate system and user time */
+ lflag = 1;
+ break;
+ case 'm':
+ /* print procs and time per-user */
+ mflag = 1;
+ break;
+ case 'n':
+ /* sort by number of calls */
+ sa_cmp = cmp_calls;
+ break;
+ case 'P':
+ /* specify program database summary file */
+ pdb_file = optarg;
+ break;
+ case 'q':
+ /* quiet; error messages only */
+ qflag = 1;
+ break;
+ case 'r':
+ /* reverse order of sort */
+ rflag = 1;
+ break;
+ case 's':
+ /* merge accounting file into summaries */
+ sflag = 1;
+ break;
+ case 't':
+ /* report ratio of user and system times */
+ tflag = 1;
+ break;
+ case 'u':
+ /* first, print uid and command name */
+ uflag = 1;
+ break;
+ case 'U':
+ /* specify user database summary file */
+ usrdb_file = optarg;
+ break;
+ case 'v':
+ /* cull junk */
+ vflag = 1;
+ cutoff = atoi(optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* various argument checking */
+ if (fflag && !vflag)
+ errx(1, "only one of -f requires -v");
+ if (fflag && aflag)
+ errx(1, "only one of -a and -v may be specified");
+ /* XXX need more argument checking */
+
+ if (!uflag) {
+ /* initialize tables */
+ if ((sflag || (!mflag && !qflag)) && pacct_init() != 0)
+ errx(1, "process accounting initialization failed");
+ if ((sflag || (mflag && !qflag)) && usracct_init() != 0)
+ errx(1, "user accounting initialization failed");
+ }
+
+ if (argc == 0) {
+ argc = dfltargc;
+ argv = dfltargv;
+ }
+
+ /* for each file specified */
+ for (; argc > 0; argc--, argv++) {
+ /*
+ * load the accounting data from the file.
+ * if it fails, go on to the next file.
+ */
+ f = acct_load(argv[0], sflag);
+ if (f == NULL)
+ continue;
+
+ if (!uflag && sflag) {
+#ifndef DEBUG
+ sigset_t nmask, omask;
+ int unmask = 1;
+
+ /*
+ * block most signals so we aren't interrupted during
+ * the update.
+ */
+ if (sigfillset(&nmask) == -1) {
+ warn("sigfillset");
+ unmask = 0;
+ error = 1;
+ }
+ if (unmask &&
+ (sigprocmask(SIG_BLOCK, &nmask, &omask) == -1)) {
+ warn("couldn't set signal mask");
+ unmask = 0;
+ error = 1;
+ }
+#endif /* DEBUG */
+
+ /*
+ * truncate the accounting data file ASAP, to avoid
+ * losing data. don't worry about errors in updating
+ * the saved stats; better to underbill than overbill,
+ * but we want every accounting record intact.
+ */
+ if (ftruncate(fileno(f), 0) == -1) {
+ warn("couldn't truncate %s", *argv);
+ error = 1;
+ }
+
+ /*
+ * update saved user and process accounting data.
+ * note errors for later.
+ */
+ if (pacct_update() != 0 || usracct_update() != 0)
+ error = 1;
+
+#ifndef DEBUG
+ /*
+ * restore signals
+ */
+ if (unmask &&
+ (sigprocmask(SIG_SETMASK, &omask, NULL) == -1)) {
+ warn("couldn't restore signal mask");
+ error = 1;
+ }
+#endif /* DEBUG */
+ }
+
+ /*
+ * close the opened accounting file
+ */
+ if (fclose(f) == EOF) {
+ warn("fclose %s", *argv);
+ error = 1;
+ }
+ }
+
+ if (!uflag && !qflag) {
+ /* print any results we may have obtained. */
+ if (!mflag)
+ pacct_print();
+ else
+ usracct_print();
+ }
+
+ if (!uflag) {
+ /* finally, deallocate databases */
+ if (sflag || (!mflag && !qflag))
+ pacct_destroy();
+ if (sflag || (mflag && !qflag))
+ usracct_destroy();
+ }
+
+ exit(error);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: sa [-abcdDfijkKlmnqrstu] [-P file] [-U file] [-v cutoff] [file ...]\n");
+ exit(1);
+}
+
+static FILE *
+acct_load(const char *pn, int wr)
+{
+ struct acctv2 ac;
+ struct cmdinfo ci;
+ ssize_t rv;
+ FILE *f;
+ int i;
+
+ /*
+ * open the file
+ */
+ f = fopen(pn, wr ? "r+" : "r");
+ if (f == NULL) {
+ warn("open %s %s", pn, wr ? "for read/write" : "read-only");
+ return (NULL);
+ }
+
+ /*
+ * read all we can; don't stat and open because more processes
+ * could exit, and we'd miss them
+ */
+ while (1) {
+ /* get one accounting entry and punt if there's an error */
+ rv = readrec_forward(f, &ac);
+ if (rv != 1) {
+ if (rv == EOF)
+ warn("error reading %s", pn);
+ break;
+ }
+
+ /* decode it */
+ ci.ci_calls = 1;
+ for (i = 0; i < (int)sizeof ac.ac_comm && ac.ac_comm[i] != '\0';
+ i++) {
+ char c = ac.ac_comm[i];
+
+ if (!isascii(c) || iscntrl(c)) {
+ ci.ci_comm[i] = '?';
+ ci.ci_flags |= CI_UNPRINTABLE;
+ } else
+ ci.ci_comm[i] = c;
+ }
+ if (ac.ac_flagx & AFORK)
+ ci.ci_comm[i++] = '*';
+ ci.ci_comm[i++] = '\0';
+ ci.ci_etime = ac.ac_etime;
+ ci.ci_utime = ac.ac_utime;
+ ci.ci_stime = ac.ac_stime;
+ ci.ci_uid = ac.ac_uid;
+ ci.ci_mem = ac.ac_mem;
+ ci.ci_io = ac.ac_io;
+
+ if (!uflag) {
+ /* and enter it into the usracct and pacct databases */
+ if (sflag || (!mflag && !qflag))
+ pacct_add(&ci);
+ if (sflag || (mflag && !qflag))
+ usracct_add(&ci);
+ } else if (!qflag)
+ printf("%6u %12.3lf cpu %12.0lfk mem %12.0lf io %s\n",
+ ci.ci_uid,
+ (ci.ci_utime + ci.ci_stime) / 1000000,
+ ci.ci_mem, ci.ci_io,
+ ci.ci_comm);
+ }
+
+ /* Finally, return the file stream for possible truncation. */
+ return (f);
+}
+
+/* sort commands, doing the right thing in terms of reversals */
+static int
+cmp_comm(const char *s1, const char *s2)
+{
+ int rv;
+
+ rv = strcmp(s1, s2);
+ if (rv == 0)
+ rv = -1;
+ return (rflag ? rv : -rv);
+}
+
+/* sort by total user and system time */
+static int
+cmp_usrsys(const DBT *d1, const DBT *d2)
+{
+ struct cmdinfo c1, c2;
+ double t1, t2;
+
+ memcpy(&c1, d1->data, sizeof(c1));
+ memcpy(&c2, d2->data, sizeof(c2));
+
+ t1 = c1.ci_utime + c1.ci_stime;
+ t2 = c2.ci_utime + c2.ci_stime;
+
+ if (t1 < t2)
+ return -1;
+ else if (t1 == t2)
+ return (cmp_comm(c1.ci_comm, c2.ci_comm));
+ else
+ return 1;
+}
+
+/* sort by average user and system time */
+static int
+cmp_avgusrsys(const DBT *d1, const DBT *d2)
+{
+ struct cmdinfo c1, c2;
+ double t1, t2;
+
+ memcpy(&c1, d1->data, sizeof(c1));
+ memcpy(&c2, d2->data, sizeof(c2));
+
+ t1 = c1.ci_utime + c1.ci_stime;
+ t1 /= (double) (c1.ci_calls ? c1.ci_calls : 1);
+
+ t2 = c2.ci_utime + c2.ci_stime;
+ t2 /= (double) (c2.ci_calls ? c2.ci_calls : 1);
+
+ if (t1 < t2)
+ return -1;
+ else if (t1 == t2)
+ return (cmp_comm(c1.ci_comm, c2.ci_comm));
+ else
+ return 1;
+}
+
+/* sort by total number of disk I/O operations */
+static int
+cmp_dkio(const DBT *d1, const DBT *d2)
+{
+ struct cmdinfo c1, c2;
+
+ memcpy(&c1, d1->data, sizeof(c1));
+ memcpy(&c2, d2->data, sizeof(c2));
+
+ if (c1.ci_io < c2.ci_io)
+ return -1;
+ else if (c1.ci_io == c2.ci_io)
+ return (cmp_comm(c1.ci_comm, c2.ci_comm));
+ else
+ return 1;
+}
+
+/* sort by average number of disk I/O operations */
+static int
+cmp_avgdkio(const DBT *d1, const DBT *d2)
+{
+ struct cmdinfo c1, c2;
+ double n1, n2;
+
+ memcpy(&c1, d1->data, sizeof(c1));
+ memcpy(&c2, d2->data, sizeof(c2));
+
+ n1 = c1.ci_io / (double) (c1.ci_calls ? c1.ci_calls : 1);
+ n2 = c2.ci_io / (double) (c2.ci_calls ? c2.ci_calls : 1);
+
+ if (n1 < n2)
+ return -1;
+ else if (n1 == n2)
+ return (cmp_comm(c1.ci_comm, c2.ci_comm));
+ else
+ return 1;
+}
+
+/* sort by the cpu-storage integral */
+static int
+cmp_cpumem(const DBT *d1, const DBT *d2)
+{
+ struct cmdinfo c1, c2;
+
+ memcpy(&c1, d1->data, sizeof(c1));
+ memcpy(&c2, d2->data, sizeof(c2));
+
+ if (c1.ci_mem < c2.ci_mem)
+ return -1;
+ else if (c1.ci_mem == c2.ci_mem)
+ return (cmp_comm(c1.ci_comm, c2.ci_comm));
+ else
+ return 1;
+}
+
+/* sort by the cpu-time average memory usage */
+static int
+cmp_avgcpumem(const DBT *d1, const DBT *d2)
+{
+ struct cmdinfo c1, c2;
+ double t1, t2;
+ double n1, n2;
+
+ memcpy(&c1, d1->data, sizeof(c1));
+ memcpy(&c2, d2->data, sizeof(c2));
+
+ t1 = c1.ci_utime + c1.ci_stime;
+ t2 = c2.ci_utime + c2.ci_stime;
+
+ n1 = c1.ci_mem / (t1 ? t1 : 1);
+ n2 = c2.ci_mem / (t2 ? t2 : 1);
+
+ if (n1 < n2)
+ return -1;
+ else if (n1 == n2)
+ return (cmp_comm(c1.ci_comm, c2.ci_comm));
+ else
+ return 1;
+}
+
+/* sort by the number of invocations */
+static int
+cmp_calls(const DBT *d1, const DBT *d2)
+{
+ struct cmdinfo c1, c2;
+
+ memcpy(&c1, d1->data, sizeof(c1));
+ memcpy(&c2, d2->data, sizeof(c2));
+
+ if (c1.ci_calls < c2.ci_calls)
+ return -1;
+ else if (c1.ci_calls == c2.ci_calls)
+ return (cmp_comm(c1.ci_comm, c2.ci_comm));
+ else
+ return 1;
+}
diff --git a/usr.sbin/sa/pathnames.h b/usr.sbin/sa/pathnames.h
new file mode 100644
index 0000000..7a7edf6
--- /dev/null
+++ b/usr.sbin/sa/pathnames.h
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#define _PATH_ACCT "/var/account/acct"
+#define _PATH_SAVACCT "/var/account/savacct"
+#define _PATH_USRACCT "/var/account/usracct"
diff --git a/usr.sbin/sa/pdb.c b/usr.sbin/sa/pdb.c
new file mode 100644
index 0000000..38beb55
--- /dev/null
+++ b/usr.sbin/sa/pdb.c
@@ -0,0 +1,374 @@
+/*
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/acct.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include "extern.h"
+#include "pathnames.h"
+
+static int check_junk(const struct cmdinfo *);
+static void add_ci(const struct cmdinfo *, struct cmdinfo *);
+static void print_ci(const struct cmdinfo *, const struct cmdinfo *);
+
+static DB *pacct_db;
+
+/* Legacy format in AHZV1 units. */
+struct cmdinfov1 {
+ char ci_comm[MAXCOMLEN+2]; /* command name (+ '*') */
+ uid_t ci_uid; /* user id */
+ u_quad_t ci_calls; /* number of calls */
+ u_quad_t ci_etime; /* elapsed time */
+ u_quad_t ci_utime; /* user time */
+ u_quad_t ci_stime; /* system time */
+ u_quad_t ci_mem; /* memory use */
+ u_quad_t ci_io; /* number of disk i/o ops */
+ u_int ci_flags; /* flags; see below */
+};
+
+/*
+ * Convert a v1 data record into the current version.
+ * Return 0 if OK, -1 on error, setting errno.
+ */
+static int
+v1_to_v2(DBT *key __unused, DBT *data)
+{
+ struct cmdinfov1 civ1;
+ static struct cmdinfo civ2;
+
+ if (data->size != sizeof(civ1)) {
+ errno = EFTYPE;
+ return (-1);
+ }
+ memcpy(&civ1, data->data, data->size);
+ memset(&civ2, 0, sizeof(civ2));
+ memcpy(civ2.ci_comm, civ1.ci_comm, sizeof(civ2.ci_comm));
+ civ2.ci_uid = civ1.ci_uid;
+ civ2.ci_calls = civ1.ci_calls;
+ civ2.ci_etime = ((double)civ1.ci_etime / AHZV1) * 1000000;
+ civ2.ci_utime = ((double)civ1.ci_utime / AHZV1) * 1000000;
+ civ2.ci_stime = ((double)civ1.ci_stime / AHZV1) * 1000000;
+ civ2.ci_mem = civ1.ci_mem;
+ civ2.ci_io = civ1.ci_io;
+ civ2.ci_flags = civ1.ci_flags;
+ data->size = sizeof(civ2);
+ data->data = &civ2;
+ return (0);
+}
+
+/* Copy pdb_file to in-memory pacct_db. */
+int
+pacct_init()
+{
+ return (db_copy_in(&pacct_db, pdb_file, "process accounting",
+ NULL, v1_to_v2));
+}
+
+void
+pacct_destroy()
+{
+ db_destroy(pacct_db, "process accounting");
+}
+
+int
+pacct_add(const struct cmdinfo *ci)
+{
+ DBT key, data;
+ struct cmdinfo newci;
+ char keydata[sizeof ci->ci_comm];
+ int rv;
+
+ bcopy(ci->ci_comm, &keydata, sizeof keydata);
+ key.data = &keydata;
+ key.size = strlen(keydata);
+
+ rv = DB_GET(pacct_db, &key, &data, 0);
+ if (rv < 0) {
+ warn("get key %s from process accounting stats", ci->ci_comm);
+ return (-1);
+ } else if (rv == 0) { /* it's there; copy whole thing */
+ /* XXX compare size if paranoid */
+ /* add the old data to the new data */
+ bcopy(data.data, &newci, data.size);
+ } else { /* it's not there; zero it and copy the key */
+ bzero(&newci, sizeof newci);
+ bcopy(key.data, newci.ci_comm, key.size);
+ }
+
+ add_ci(ci, &newci);
+
+ data.data = &newci;
+ data.size = sizeof newci;
+ rv = DB_PUT(pacct_db, &key, &data, 0);
+ if (rv < 0) {
+ warn("add key %s to process accounting stats", ci->ci_comm);
+ return (-1);
+ } else if (rv == 1) {
+ warnx("duplicate key %s in process accounting stats",
+ ci->ci_comm);
+ return (-1);
+ }
+
+ return (0);
+}
+
+/* Copy in-memory pacct_db to pdb_file. */
+int
+pacct_update()
+{
+ return (db_copy_out(pacct_db, pdb_file, "process accounting",
+ NULL));
+}
+
+void
+pacct_print()
+{
+ BTREEINFO bti;
+ DBT key, data, ndata;
+ DB *output_pacct_db;
+ struct cmdinfo *cip, ci, ci_total, ci_other, ci_junk;
+ int rv;
+
+ bzero(&ci_total, sizeof ci_total);
+ strcpy(ci_total.ci_comm, "");
+ bzero(&ci_other, sizeof ci_other);
+ strcpy(ci_other.ci_comm, "***other");
+ bzero(&ci_junk, sizeof ci_junk);
+ strcpy(ci_junk.ci_comm, "**junk**");
+
+ /*
+ * Retrieve them into new DB, sorted by appropriate key.
+ * At the same time, cull 'other' and 'junk'
+ */
+ bzero(&bti, sizeof bti);
+ bti.compare = sa_cmp;
+ output_pacct_db = dbopen(NULL, O_RDWR, 0, DB_BTREE, &bti);
+ if (output_pacct_db == NULL) {
+ warn("couldn't sort process accounting stats");
+ return;
+ }
+
+ ndata.data = NULL;
+ ndata.size = 0;
+ rv = DB_SEQ(pacct_db, &key, &data, R_FIRST);
+ if (rv < 0)
+ warn("retrieving process accounting stats");
+ while (rv == 0) {
+ cip = (struct cmdinfo *) data.data;
+ bcopy(cip, &ci, sizeof ci);
+
+ /* add to total */
+ add_ci(&ci, &ci_total);
+
+ if (vflag && ci.ci_calls <= cutoff &&
+ (fflag || check_junk(&ci))) {
+ /* put it into **junk** */
+ add_ci(&ci, &ci_junk);
+ goto next;
+ }
+ if (!aflag &&
+ ((ci.ci_flags & CI_UNPRINTABLE) != 0 || ci.ci_calls <= 1)) {
+ /* put into ***other */
+ add_ci(&ci, &ci_other);
+ goto next;
+ }
+ rv = DB_PUT(output_pacct_db, &data, &ndata, 0);
+ if (rv < 0)
+ warn("sorting process accounting stats");
+
+next: rv = DB_SEQ(pacct_db, &key, &data, R_NEXT);
+ if (rv < 0)
+ warn("retrieving process accounting stats");
+ }
+
+ /* insert **junk** and ***other */
+ if (ci_junk.ci_calls != 0) {
+ data.data = &ci_junk;
+ data.size = sizeof ci_junk;
+ rv = DB_PUT(output_pacct_db, &data, &ndata, 0);
+ if (rv < 0)
+ warn("sorting process accounting stats");
+ }
+ if (ci_other.ci_calls != 0) {
+ data.data = &ci_other;
+ data.size = sizeof ci_other;
+ rv = DB_PUT(output_pacct_db, &data, &ndata, 0);
+ if (rv < 0)
+ warn("sorting process accounting stats");
+ }
+
+ /* print out the total */
+ print_ci(&ci_total, &ci_total);
+
+ /* print out; if reversed, print first (smallest) first */
+ rv = DB_SEQ(output_pacct_db, &data, &ndata, rflag ? R_FIRST : R_LAST);
+ if (rv < 0)
+ warn("retrieving process accounting report");
+ while (rv == 0) {
+ cip = (struct cmdinfo *) data.data;
+ bcopy(cip, &ci, sizeof ci);
+
+ print_ci(&ci, &ci_total);
+
+ rv = DB_SEQ(output_pacct_db, &data, &ndata,
+ rflag ? R_NEXT : R_PREV);
+ if (rv < 0)
+ warn("retrieving process accounting report");
+ }
+ DB_CLOSE(output_pacct_db);
+}
+
+static int
+check_junk(const struct cmdinfo *cip)
+{
+ char *cp;
+ size_t len;
+
+ fprintf(stderr, "%s (%ju) -- ", cip->ci_comm, (uintmax_t)cip->ci_calls);
+ cp = fgetln(stdin, &len);
+
+ return (cp && (cp[0] == 'y' || cp[0] == 'Y')) ? 1 : 0;
+}
+
+static void
+add_ci(const struct cmdinfo *fromcip, struct cmdinfo *tocip)
+{
+ tocip->ci_calls += fromcip->ci_calls;
+ tocip->ci_etime += fromcip->ci_etime;
+ tocip->ci_utime += fromcip->ci_utime;
+ tocip->ci_stime += fromcip->ci_stime;
+ tocip->ci_mem += fromcip->ci_mem;
+ tocip->ci_io += fromcip->ci_io;
+}
+
+static void
+print_ci(const struct cmdinfo *cip, const struct cmdinfo *totalcip)
+{
+ double t, c;
+ int uflow;
+
+ c = cip->ci_calls ? cip->ci_calls : 1;
+ t = (cip->ci_utime + cip->ci_stime) / 1000000;
+ if (t < 0.01) {
+ t = 0.01;
+ uflow = 1;
+ } else
+ uflow = 0;
+
+ printf("%8ju ", (uintmax_t)cip->ci_calls);
+ if (cflag) {
+ if (cip != totalcip)
+ printf(" %4.1f%% ", cip->ci_calls /
+ (double)totalcip->ci_calls * 100);
+ else
+ printf(" %4s ", "");
+ }
+
+ if (jflag)
+ printf("%11.3fre ", cip->ci_etime / (1000000 * c));
+ else
+ printf("%11.3fre ", cip->ci_etime / (60.0 * 1000000));
+ if (cflag) {
+ if (cip != totalcip)
+ printf(" %4.1f%% ", cip->ci_etime /
+ totalcip->ci_etime * 100);
+ else
+ printf(" %4s ", "");
+ }
+
+ if (!lflag) {
+ if (jflag)
+ printf("%11.3fcp ", t / (double) cip->ci_calls);
+ else
+ printf("%11.2fcp ", t / 60.0);
+ if (cflag) {
+ if (cip != totalcip)
+ printf(" %4.1f%% ",
+ (cip->ci_utime + cip->ci_stime) /
+ (totalcip->ci_utime + totalcip->ci_stime) *
+ 100);
+ else
+ printf(" %4s ", "");
+ }
+ } else {
+ if (jflag)
+ printf("%11.3fu ", cip->ci_utime / (1000000 * c));
+ else
+ printf("%11.2fu ", cip->ci_utime / (60.0 * 1000000));
+ if (cflag) {
+ if (cip != totalcip)
+ printf(" %4.1f%% ", cip->ci_utime /
+ (double)totalcip->ci_utime * 100);
+ else
+ printf(" %4s ", "");
+ }
+ if (jflag)
+ printf("%11.3fs ", cip->ci_stime / (1000000 * c));
+ else
+ printf("%11.2fs ", cip->ci_stime / (60.0 * 1000000));
+ if (cflag) {
+ if (cip != totalcip)
+ printf(" %4.1f%% ", cip->ci_stime /
+ (double)totalcip->ci_stime * 100);
+ else
+ printf(" %4s ", "");
+ }
+ }
+
+ if (tflag) {
+ if (!uflow)
+ printf("%8.2fre/cp ",
+ cip->ci_etime /
+ (cip->ci_utime + cip->ci_stime));
+ else
+ printf("*ignore* ");
+ }
+
+ if (Dflag)
+ printf("%10.0fio ", cip->ci_io);
+ else
+ printf("%8.0favio ", cip->ci_io / c);
+
+ if (Kflag)
+ printf("%10.0fk*sec ", cip->ci_mem);
+ else
+ printf("%8.0fk ", cip->ci_mem / t);
+
+ printf(" %s\n", cip->ci_comm);
+}
diff --git a/usr.sbin/sa/sa.8 b/usr.sbin/sa/sa.8
new file mode 100644
index 0000000..e763064
--- /dev/null
+++ b/usr.sbin/sa/sa.8
@@ -0,0 +1,262 @@
+.\"
+.\" 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.
+.\" 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 18, 2007
+.Dt SA 8
+.Os
+.Sh NAME
+.Nm sa
+.Nd print system accounting statistics
+.Sh SYNOPSIS
+.Nm
+.Op Fl abcdDfijkKlmnqrstu
+.Op Fl P Ar file
+.Op Fl U Ar file
+.Op Fl v Ar cutoff
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility reports on, cleans up,
+and generally maintains system
+accounting files.
+.Pp
+The
+.Nm
+utility is able to condense the information in
+.Pa /var/account/acct
+into the summary files
+.Pa /var/account/savacct
+and
+.Pa /var/account/usracct ,
+which contain system statistics according
+to command name and login id, respectively.
+This condensation is desirable because on a
+large system,
+.Pa /var/account/acct
+can grow by hundreds of blocks per day.
+The summary files are normally read before
+the accounting file, so that reports include
+all available information.
+.Pp
+If file names are supplied, they are read instead of
+.Pa /var/account/acct .
+After each file is read, if the summary
+files are being updated, an updated summary will
+be saved to disk.
+Only one report is printed,
+after the last file is processed.
+.Pp
+The labels used in the output indicate the following, except
+where otherwise specified by individual options:
+.Bl -tag -width k*sec
+.It Dv avio
+Average number of I/O operations per execution
+.It Dv cp
+Sum of user and system time, in minutes
+.It Dv cpu
+Same as
+.Dv cp
+.It Dv k
+CPU-time averaged core usage, in 1k units
+.It Dv k*sec
+CPU storage integral, in 1k-core seconds
+.It Dv re
+Real time, in minutes
+.It Dv s
+System time, in minutes
+.It Dv tio
+Total number of I/O operations
+.It Dv u
+User time, in minutes
+.El
+.Pp
+The options to
+.Nm
+are:
+.Bl -tag -width Ds
+.It Fl a
+List all command names, including those containing unprintable
+characters and those used only once.
+By default,
+.Nm
+places all names containing unprintable characters and
+those used only once under the name ``***other''.
+.It Fl b
+If printing command statistics, sort output by the sum of user and system
+time divided by number of calls.
+.It Fl c
+In addition to the number of calls and the user, system and real times
+for each command, print their percentage of the total over all commands.
+.It Fl d
+If printing command statistics, sort by the average number of disk
+I/O operations.
+If printing user statistics, print the average number of
+disk I/O operations per user.
+.It Fl D
+If printing command statistics, sort and print by the total number
+of disk I/O operations.
+.It Fl f
+Force no interactive threshold comparison with the
+.Fl v
+option.
+.It Fl i
+Do not read in the summary files.
+.It Fl j
+Instead of the total minutes per category, give seconds per call.
+.It Fl k
+If printing command statistics, sort by the cpu-time average memory
+usage.
+If printing user statistics, print the cpu-time average
+memory usage.
+.It Fl K
+If printing command statistics, print and sort by the cpu-storage integral.
+.It Fl l
+Separate system and user time; normally they are combined.
+.It Fl m
+Print per-user statistics rather than per-command statistics.
+.It Fl n
+Sort by number of calls.
+.It Fl P Ar file
+Use the specified
+.Ar file
+for accessing the per-command accounting summary database,
+instead of the default
+.Pa /var/account/savacct .
+.It Fl q
+Create no output other than error messages.
+.It Fl r
+Reverse order of sort.
+.It Fl s
+Truncate the accounting files when done and merge their data
+into the summary files.
+.It Fl t
+For each command, report the ratio of real time to the sum
+of user and system cpu times.
+If the cpu time is too small to report, ``*ignore*'' appears in
+this field.
+.It Fl U Ar file
+Use the specified
+.Ar file
+for accessing the per-user accounting summary database,
+instead of the default
+.Pa /var/account/usracct .
+.It Fl u
+Superseding all other flags, for each entry
+in the accounting file, print the user ID, total seconds of cpu usage,
+total memory usage, number of I/O operations performed, and
+command name.
+.It Fl v Ar cutoff
+For each command used
+.Ar cutoff
+times or fewer, print the command name and await a reply
+from the terminal.
+If the reply begins with ``y'', add
+the command to the category ``**junk**''.
+This flag is
+used to strip garbage from the report.
+.El
+.Pp
+By default, per-command statistics will be printed.
+The number of
+calls, the total elapsed time in minutes, total cpu and user time
+in minutes, average number of I/O operations, and CPU-time
+averaged core usage will be printed.
+If the
+.Fl m
+option is specified, per-user statistics will be printed, including
+the user name, the number of commands invoked, total cpu time used
+(in minutes), total number of I/O operations, and CPU storage integral
+for each user.
+If the
+.Fl u
+option is specified, the uid, user and system time (in seconds),
+CPU storage integral, I/O usage, and command name will be printed
+for each entry in the accounting data file.
+.Pp
+If the
+.Fl u
+flag is specified, all flags other than
+.Fl q
+are ignored.
+If the
+.Fl m
+flag is specified, only the
+.Fl b ,
+.Fl d ,
+.Fl i ,
+.Fl k ,
+.Fl q ,
+and
+.Fl s
+flags are honored.
+.Sh FILES
+.Bl -tag -width /var/account/usracct -compact
+.It Pa /var/account/acct
+raw accounting data file
+.It Pa /var/account/savacct
+per-command accounting summary database
+.It Pa /var/account/usracct
+per-user accounting summary database
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr lastcomm 1 ,
+.Xr acct 5 ,
+.Xr ac 8 ,
+.Xr accton 8
+.Sh CAVEATS
+While the behavior of the options in this version of
+.Nm
+was modeled after the original version, there are some intentional
+differences and undoubtedly some unintentional ones as well.
+In
+particular, the
+.Fl q
+option has been added, and the
+.Fl m
+option now understands more options than it used to.
+.Pp
+The formats of the summary files created by this version of
+.Nm
+are very different from the those used by the original version.
+This is not considered a problem, however, because the accounting record
+format has changed as well (since user ids are now 32 bits).
+.Sh AUTHORS
+.An Chris G. Demetriou Aq cgd@postgres.berkeley.edu
+.Sh BUGS
+The number of options to this program is absurd, especially considering
+that there is not much logic behind their lettering.
+.Pp
+The field labels should be more consistent.
+.Pp
+The VM system does not record the CPU storage integral.
diff --git a/usr.sbin/sa/usrdb.c b/usr.sbin/sa/usrdb.c
new file mode 100644
index 0000000..07a5033
--- /dev/null
+++ b/usr.sbin/sa/usrdb.c
@@ -0,0 +1,239 @@
+/*
+ * 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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/acct.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "extern.h"
+#include "pathnames.h"
+
+static int uid_compare(const DBT *, const DBT *);
+
+static DB *usracct_db;
+
+/* Legacy format in AHZV1 units. */
+struct userinfov1 {
+ uid_t ui_uid; /* user id; for consistency */
+ u_quad_t ui_calls; /* number of invocations */
+ u_quad_t ui_utime; /* user time */
+ u_quad_t ui_stime; /* system time */
+ u_quad_t ui_mem; /* memory use */
+ u_quad_t ui_io; /* number of disk i/o ops */
+};
+
+/*
+ * Convert a v1 data record into the current version.
+ * Return 0 if OK, -1 on error, setting errno.
+ */
+static int
+v1_to_v2(DBT *key, DBT *data)
+{
+ struct userinfov1 uiv1;
+ static struct userinfo uiv2;
+ static uid_t uid;
+
+ if (key->size != sizeof(u_long) || data->size != sizeof(uiv1)) {
+ errno = EFTYPE;
+ return (-1);
+ }
+
+ /* Convert key. */
+ key->size = sizeof(uid_t);
+ uid = (uid_t)*(u_long *)(key->data);
+ key->data = &uid;
+
+ /* Convert data. */
+ memcpy(&uiv1, data->data, data->size);
+ memset(&uiv2, 0, sizeof(uiv2));
+ uiv2.ui_uid = uiv1.ui_uid;
+ uiv2.ui_calls = uiv1.ui_calls;
+ uiv2.ui_utime = ((double)uiv1.ui_utime / AHZV1) * 1000000;
+ uiv2.ui_stime = ((double)uiv1.ui_stime / AHZV1) * 1000000;
+ uiv2.ui_mem = uiv1.ui_mem;
+ uiv2.ui_io = uiv1.ui_io;
+ data->size = sizeof(uiv2);
+ data->data = &uiv2;
+
+ return (0);
+}
+
+/* Copy usrdb_file to in-memory usracct_db. */
+int
+usracct_init()
+{
+ BTREEINFO bti;
+
+ bzero(&bti, sizeof bti);
+ bti.compare = uid_compare;
+
+ return (db_copy_in(&usracct_db, usrdb_file, "user accounting",
+ &bti, v1_to_v2));
+}
+
+void
+usracct_destroy()
+{
+ db_destroy(usracct_db, "user accounting");
+}
+
+int
+usracct_add(const struct cmdinfo *ci)
+{
+ DBT key, data;
+ struct userinfo newui;
+ uid_t uid;
+ int rv;
+
+ uid = ci->ci_uid;
+ key.data = &uid;
+ key.size = sizeof uid;
+
+ rv = DB_GET(usracct_db, &key, &data, 0);
+ if (rv < 0) {
+ warn("get key %u from user accounting stats", uid);
+ return (-1);
+ } else if (rv == 0) { /* it's there; copy whole thing */
+ /* add the old data to the new data */
+ bcopy(data.data, &newui, data.size);
+ if (newui.ui_uid != uid) {
+ warnx("key %u != expected record number %u",
+ newui.ui_uid, uid);
+ warnx("inconsistent user accounting stats");
+ return (-1);
+ }
+ } else { /* it's not there; zero it and copy the key */
+ bzero(&newui, sizeof newui);
+ newui.ui_uid = ci->ci_uid;
+ }
+
+ newui.ui_calls += ci->ci_calls;
+ newui.ui_utime += ci->ci_utime;
+ newui.ui_stime += ci->ci_stime;
+ newui.ui_mem += ci->ci_mem;
+ newui.ui_io += ci->ci_io;
+
+ data.data = &newui;
+ data.size = sizeof newui;
+ rv = DB_PUT(usracct_db, &key, &data, 0);
+ if (rv < 0) {
+ warn("add key %u to user accounting stats", uid);
+ return (-1);
+ } else if (rv != 0) {
+ warnx("DB_PUT returned 1");
+ return (-1);
+ }
+
+ return (0);
+}
+
+/* Copy in-memory usracct_db to usrdb_file. */
+int
+usracct_update()
+{
+ BTREEINFO bti;
+
+ bzero(&bti, sizeof bti);
+ bti.compare = uid_compare;
+
+ return (db_copy_out(usracct_db, usrdb_file, "user accounting",
+ &bti));
+}
+
+void
+usracct_print()
+{
+ DBT key, data;
+ struct userinfo uistore, *ui = &uistore;
+ double t;
+ int rv;
+
+ rv = DB_SEQ(usracct_db, &key, &data, R_FIRST);
+ if (rv < 0)
+ warn("retrieving user accounting stats");
+
+ while (rv == 0) {
+ memcpy(ui, data.data, sizeof(struct userinfo));
+
+ printf("%-*s %9ju ", MAXLOGNAME - 1,
+ user_from_uid(ui->ui_uid, 0), (uintmax_t)ui->ui_calls);
+
+ t = (ui->ui_utime + ui->ui_stime) / 1000000;
+ if (t < 0.000001) /* kill divide by zero */
+ t = 0.000001;
+
+ printf("%12.2f%s ", t / 60.0, "cpu");
+
+ /* ui->ui_calls is always != 0 */
+ if (dflag)
+ printf("%12.0f%s",
+ ui->ui_io / ui->ui_calls, "avio");
+ else
+ printf("%12.0f%s", ui->ui_io, "tio");
+
+ /* t is always >= 0.000001; see above. */
+ if (kflag)
+ printf("%12.0f%s", ui->ui_mem / t, "k");
+ else
+ printf("%12.0f%s", ui->ui_mem, "k*sec");
+
+ printf("\n");
+
+ rv = DB_SEQ(usracct_db, &key, &data, R_NEXT);
+ if (rv < 0)
+ warn("retrieving user accounting stats");
+ }
+}
+
+static int
+uid_compare(const DBT *k1, const DBT *k2)
+{
+ uid_t d1, d2;
+
+ bcopy(k1->data, &d1, sizeof d1);
+ bcopy(k2->data, &d2, sizeof d2);
+
+ if (d1 < d2)
+ return -1;
+ else if (d1 == d2)
+ return 0;
+ else
+ return 1;
+}
diff --git a/usr.sbin/sade/Makefile b/usr.sbin/sade/Makefile
new file mode 100644
index 0000000..bd94155
--- /dev/null
+++ b/usr.sbin/sade/Makefile
@@ -0,0 +1,27 @@
+# $FreeBSD$
+
+.if ${MACHINE_ARCH} != "ia64"
+_wizard= wizard.c
+.endif
+
+PROG= sade
+MAN= sade.8
+SRCS= command.c config.c devices.c \
+ disks.c dispatch.c dmenu.c \
+ globals.c install.c \
+ label.c main.c menus.c misc.c \
+ msg.c system.c termcap.c \
+ variable.c ${_wizard}
+WARNS?= 3
+
+# command.c
+
+.if ${MACHINE} == "pc98"
+CFLAGS+= -DPC98
+.endif
+CFLAGS+= -I${.CURDIR}/../../gnu/lib/libdialog -I.
+
+DPADD= ${LIBDIALOG} ${LIBNCURSES} ${LIBUTIL} ${LIBDISK}
+LDADD= -ldialog -lncurses -lutil -ldisk
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/sade/command.c b/usr.sbin/sade/command.c
new file mode 100644
index 0000000..b3968df
--- /dev/null
+++ b/usr.sbin/sade/command.c
@@ -0,0 +1,179 @@
+/*
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sade.h"
+
+#define MAX_NUM_COMMANDS 10
+
+typedef struct {
+ char key[FILENAME_MAX];
+ struct {
+ enum { CMD_SHELL, CMD_FUNCTION } type;
+ void *ptr, *data;
+ } cmds[MAX_NUM_COMMANDS];
+ int ncmds;
+} Command;
+
+#define MAX_CMDS 200
+static Command *commandStack[MAX_CMDS];
+int numCommands;
+
+/* Nuke the command stack */
+void
+command_clear(void)
+{
+ int i, j;
+
+ for (i = 0; i < numCommands; i++)
+ for (j = 0; j < commandStack[i]->ncmds; j++)
+ if (commandStack[i]->cmds[j].type == CMD_SHELL)
+ free(commandStack[i]->cmds[j].ptr);
+ free(commandStack[i]);
+ numCommands = 0;
+}
+
+static void
+addit(char *key, int type, void *cmd, void *data)
+{
+ int i;
+
+ /* First, look for the key already present and add a command to it if found */
+ for (i = 0; i < numCommands; i++) {
+ if (!strcmp(commandStack[i]->key, key)) {
+ if (commandStack[i]->ncmds == MAX_NUM_COMMANDS)
+ msgFatal("More than %d commands stacked up behind %s??", MAX_NUM_COMMANDS, key);
+ commandStack[i]->cmds[commandStack[i]->ncmds].type = type;
+ commandStack[i]->cmds[commandStack[i]->ncmds].ptr = cmd;
+ commandStack[i]->cmds[commandStack[i]->ncmds].data = data;
+ ++(commandStack[i]->ncmds);
+ return;
+ }
+ }
+ if (numCommands == MAX_CMDS)
+ msgFatal("More than %d commands accumulated??", MAX_CMDS);
+
+ /* If we fell to here, it's a new key */
+ commandStack[numCommands] = safe_malloc(sizeof(Command));
+ strcpy(commandStack[numCommands]->key, key);
+ commandStack[numCommands]->ncmds = 1;
+ commandStack[numCommands]->cmds[0].type = type;
+ commandStack[numCommands]->cmds[0].ptr = cmd;
+ commandStack[numCommands]->cmds[0].data = data;
+ ++numCommands;
+}
+
+/* Add a shell command under a given key */
+void
+command_shell_add(char *key, const char *fmt, ...)
+{
+ va_list args;
+ char *cmd;
+
+ cmd = (char *)safe_malloc(256);
+ va_start(args, fmt);
+ vsnprintf(cmd, 256, fmt, args);
+ va_end(args);
+
+ addit(key, CMD_SHELL, cmd, NULL);
+}
+
+/* Add a shell command under a given key */
+void
+command_func_add(char *key, commandFunc func, void *data)
+{
+ addit(key, CMD_FUNCTION, func, data);
+}
+
+static int
+sort_compare(Command *p1, Command *p2)
+{
+ if (!p1 && !p2)
+ return 0;
+ else if (!p1 && p2) /* NULL has a "greater" value for commands */
+ return 1;
+ else if (p1 && !p2)
+ return -1;
+ else
+ return strcmp(p1->key, p2->key);
+}
+
+void
+command_sort(void)
+{
+ int i, j;
+
+ commandStack[numCommands] = NULL;
+ /* Just do a crude bubble sort since the list is small */
+ for (i = 0; i < numCommands; i++) {
+ for (j = 0; j < numCommands; j++) {
+ if (sort_compare(commandStack[j], commandStack[j + 1]) > 0) {
+ Command *tmp = commandStack[j];
+
+ commandStack[j] = commandStack[j + 1];
+ commandStack[j + 1] = tmp;
+ }
+ }
+ }
+}
+
+/* Run all accumulated commands in sorted order */
+void
+command_execute(void)
+{
+ int i, j, ret;
+ commandFunc func;
+
+ for (i = 0; i < numCommands; i++) {
+ for (j = 0; j < commandStack[i]->ncmds; j++) {
+ /* If it's a shell command, run system on it */
+ if (commandStack[i]->cmds[j].type == CMD_SHELL) {
+ msgNotify("Doing %s", (char *)commandStack[i]->cmds[j].ptr);
+ ret = vsystem("%s", (char *)commandStack[i]->cmds[j].ptr);
+ if (isDebug())
+ msgDebug("Command `%s' returns status %d\n",
+ (char *)commandStack[i]->cmds[j].ptr, ret);
+ }
+ else {
+ /* It's a function pointer - call it with the key and
+ the data */
+ func = (commandFunc)commandStack[i]->cmds[j].ptr;
+ if (isDebug())
+ msgDebug("%p: Execute(%s, %s)\n",
+ func, commandStack[i]->key,
+ (char *)commandStack[i]->cmds[j].data);
+ ret = (*func)(commandStack[i]->key, commandStack[i]->cmds[j].data);
+ if (isDebug())
+ msgDebug("Function @ %p returns status %d\n",
+ commandStack[i]->cmds[j].ptr, ret);
+ }
+ }
+ }
+}
diff --git a/usr.sbin/sade/config.c b/usr.sbin/sade/config.c
new file mode 100644
index 0000000..c8b0fc8
--- /dev/null
+++ b/usr.sbin/sade/config.c
@@ -0,0 +1,333 @@
+/*
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sade.h"
+#include <sys/disklabel.h>
+#include <sys/wait.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/mount.h>
+#include <libdisk.h>
+#include <time.h>
+#include <kenv.h>
+
+static Chunk *chunk_list[MAX_CHUNKS];
+static int nchunks;
+static int rootdev_is_od;
+
+/* arg to sort */
+static int
+chunk_compare(Chunk *c1, Chunk *c2)
+{
+ if (!c1 && !c2)
+ return 0;
+ else if (!c1 && c2)
+ return 1;
+ else if (c1 && !c2)
+ return -1;
+ else if (!c1->private_data && !c2->private_data)
+ return 0;
+ else if (c1->private_data && !c2->private_data)
+ return 1;
+ else if (!c1->private_data && c2->private_data)
+ return -1;
+ else
+ return strcmp(((PartInfo *)(c1->private_data))->mountpoint, ((PartInfo *)(c2->private_data))->mountpoint);
+}
+
+static void
+chunk_sort(void)
+{
+ int i, j;
+
+ for (i = 0; i < nchunks; i++) {
+ for (j = 0; j < nchunks; j++) {
+ if (chunk_compare(chunk_list[j], chunk_list[j + 1]) > 0) {
+ Chunk *tmp = chunk_list[j];
+
+ chunk_list[j] = chunk_list[j + 1];
+ chunk_list[j + 1] = tmp;
+ }
+ }
+ }
+}
+
+static void
+check_rootdev(Chunk **list, int n)
+{
+ int i;
+ Chunk *c;
+
+ rootdev_is_od = 0;
+ for (i = 0; i < n; i++) {
+ c = *list++;
+ if (c->type == part && (c->flags & CHUNK_IS_ROOT)
+ && strncmp(c->disk->name, "od", 2) == 0)
+ rootdev_is_od = 1;
+ }
+}
+
+static char *
+name_of(Chunk *c1)
+{
+ return c1->name;
+}
+
+static char *
+mount_point(Chunk *c1)
+{
+ if (c1->type == part && c1->subtype == FS_SWAP)
+ return "none";
+ else if (c1->type == part || c1->type == fat || c1->type == efi)
+ return ((PartInfo *)c1->private_data)->mountpoint;
+ return "/bogus";
+}
+
+static char *
+fstype(Chunk *c1)
+{
+ if (c1->type == fat || c1->type == efi)
+ return "msdosfs";
+ else if (c1->type == part) {
+ if (c1->subtype != FS_SWAP)
+ return "ufs";
+ else
+ return "swap";
+ }
+ return "bogus";
+}
+
+static char *
+fstype_short(Chunk *c1)
+{
+ if (c1->type == part) {
+ if (c1->subtype != FS_SWAP) {
+ if (rootdev_is_od == 0 && strncmp(c1->name, "od", 2) == 0)
+ return "rw,noauto";
+ else
+ return "rw";
+ }
+ else
+ return "sw";
+ }
+ else if (c1->type == fat) {
+ if (strncmp(c1->name, "od", 2) == 0)
+ return "ro,noauto";
+ else
+ return "ro";
+ }
+ else if (c1->type == efi)
+ return "rw";
+
+ return "bog";
+}
+
+static int
+seq_num(Chunk *c1)
+{
+ if (c1->type == part && c1->subtype != FS_SWAP) {
+ if (rootdev_is_od == 0 && strncmp(c1->name, "od", 2) == 0)
+ return 0;
+ else if (c1->flags & CHUNK_IS_ROOT)
+ return 1;
+ else
+ return 2;
+ }
+ return 0;
+}
+
+int
+configFstab(dialogMenuItem *self)
+{
+ Device **devs;
+ Disk *disk;
+ FILE *fstab;
+ int i;
+ Chunk *c1, *c2;
+
+ if (file_readable("/etc/fstab"))
+ return DITEM_SUCCESS;
+ else {
+ msgConfirm("Attempting to rebuild your /etc/fstab file. Warning: If you had\n"
+ "any CD devices in use before running %s then they may NOT\n"
+ "be found by this run!", ProgName);
+ }
+
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("No disks found!");
+ return DITEM_FAILURE;
+ }
+
+ /* Record all the chunks */
+ nchunks = 0;
+ for (i = 0; devs[i]; i++) {
+ if (!devs[i]->enabled)
+ continue;
+ disk = (Disk *)devs[i]->private;
+ if (!disk->chunks)
+ msgFatal("No chunk list found for %s!", disk->name);
+ for (c1 = disk->chunks->part; c1; c1 = c1->next) {
+#ifdef __powerpc__
+ if (c1->type == apple) {
+#else
+ if (c1->type == freebsd) {
+#endif
+ for (c2 = c1->part; c2; c2 = c2->next) {
+ if (c2->type == part && (c2->subtype == FS_SWAP || c2->private_data))
+ chunk_list[nchunks++] = c2;
+ }
+ }
+ else if (((c1->type == fat || c1->type == efi || c1->type == part) &&
+ c1->private_data) || (c1->type == part && c1->subtype == FS_SWAP))
+ chunk_list[nchunks++] = c1;
+ }
+ }
+ chunk_list[nchunks] = 0;
+ chunk_sort();
+
+ fstab = fopen("/etc/fstab", "w");
+ if (!fstab) {
+ msgConfirm("Unable to create a new /etc/fstab file! Manual intervention\n"
+ "will be required.");
+ return DITEM_FAILURE;
+ }
+
+ check_rootdev(chunk_list, nchunks);
+
+ /* Go for the burn */
+ msgDebug("Generating /etc/fstab file\n");
+ fprintf(fstab, "# Device\t\tMountpoint\tFStype\tOptions\t\tDump\tPass#\n");
+ for (i = 0; i < nchunks; i++)
+ fprintf(fstab, "/dev/%s\t\t%s\t\t%s\t%s\t\t%d\t%d\n", name_of(chunk_list[i]), mount_point(chunk_list[i]),
+ fstype(chunk_list[i]), fstype_short(chunk_list[i]), seq_num(chunk_list[i]), seq_num(chunk_list[i]));
+
+
+ fclose(fstab);
+ if (isDebug())
+ msgDebug("Wrote out /etc/fstab file\n");
+ return DITEM_SUCCESS;
+}
+
+#if 0
+/* Do the work of sucking in a config file.
+ * config is the filename to read in.
+ * lines is a fixed (max) sized array of char*
+ * returns number of lines read. line contents
+ * are malloc'd and must be freed by the caller.
+ */
+static int
+readConfig(char *config, char **lines, int max)
+{
+ FILE *fp;
+ char line[256];
+ int i, nlines;
+
+ fp = fopen(config, "r");
+ if (!fp)
+ return -1;
+
+ nlines = 0;
+ /* Read in the entire file */
+ for (i = 0; i < max; i++) {
+ if (!fgets(line, sizeof line, fp))
+ break;
+ lines[nlines++] = strdup(line);
+ }
+ fclose(fp);
+ if (isDebug())
+ msgDebug("readConfig: Read %d lines from %s.\n", nlines, config);
+ return nlines;
+}
+#endif
+
+#define MAX_LINES 2000 /* Some big number we're not likely to ever reach - I'm being really lazy here, I know */
+
+#if 0
+static void
+readConfigFile(char *config, int marked)
+{
+ char *lines[MAX_LINES], *cp, *cp2;
+ int i, nlines;
+
+ nlines = readConfig(config, lines, MAX_LINES);
+ if (nlines == -1)
+ return;
+
+ for (i = 0; i < nlines; i++) {
+ /* Skip the comments & non-variable settings */
+ if (lines[i][0] == '#' || !(cp = index(lines[i], '='))) {
+ free(lines[i]);
+ continue;
+ }
+ *cp++ = '\0';
+ /* Find quotes */
+ if ((cp2 = index(cp, '"')) || (cp2 = index(cp, '\047'))) {
+ cp = cp2 + 1;
+ cp2 = index(cp, *cp2);
+ }
+ /* If valid quotes, use it */
+ if (cp2) {
+ *cp2 = '\0';
+ /* If we have a legit value, set it */
+ if (strlen(cp))
+ variable_set2(lines[i], cp, marked);
+ }
+ free(lines[i]);
+ }
+}
+
+/* Load the environment from rc.conf file(s) */
+void
+configEnvironmentRC_conf(void)
+{
+ static struct {
+ char *fname;
+ int marked;
+ } configs[] = {
+ { "/etc/defaults/rc.conf", 0 },
+ { "/etc/rc.conf", 0 },
+ { "/etc/rc.conf.local", 0 },
+ { NULL, 0 },
+ };
+ int i;
+
+ for (i = 0; configs[i].fname; i++) {
+ if (file_readable(configs[i].fname))
+ readConfigFile(configs[i].fname, configs[i].marked);
+ }
+}
+#endif
+
diff --git a/usr.sbin/sade/devices.c b/usr.sbin/sade/devices.c
new file mode 100644
index 0000000..fec9bba
--- /dev/null
+++ b/usr.sbin/sade/devices.c
@@ -0,0 +1,345 @@
+/*
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sade.h"
+#include <sys/fcntl.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <ctype.h>
+#include <libdisk.h>
+
+/* how much to bias minor number for a given /dev/<ct#><un#>s<s#> slice */
+#define SLICE_DELTA (0x10000)
+
+static Device *Devices[DEV_MAX];
+static int numDevs;
+
+#define DEVICE_ENTRY(type, name, descr, max) { type, name, descr, max }
+
+#define DISK(name, descr, max) \
+ DEVICE_ENTRY(DEVICE_TYPE_DISK, name, descr, max)
+
+static struct _devname {
+ DeviceType type;
+ char *name;
+ char *description;
+ int max;
+} device_names[] = {
+ DISK("da%d", "SCSI disk device", 16),
+ DISK("ad%d", "ATA/IDE disk device", 16),
+ DISK("ar%d", "ATA/IDE RAID device", 16),
+ DISK("afd%d", "ATAPI/IDE floppy device", 4),
+ DISK("mlxd%d", "Mylex RAID disk", 4),
+ DISK("amrd%d", "AMI MegaRAID drive", 4),
+ DISK("idad%d", "Compaq RAID array", 4),
+ DISK("twed%d", "3ware ATA RAID array", 4),
+ DISK("aacd%d", "Adaptec FSA RAID array", 4),
+ DISK("ipsd%d", "IBM ServeRAID RAID array", 4),
+ DISK("mfid%d", "LSI MegaRAID SAS array", 4),
+ { 0, NULL, NULL, 0 },
+};
+
+Device *
+new_device(char *name)
+{
+ Device *dev;
+
+ dev = safe_malloc(sizeof(Device));
+ bzero(dev, sizeof(Device));
+ if (name)
+ SAFE_STRCPY(dev->name, name);
+ return dev;
+}
+
+/* Stubs for unimplemented strategy routines */
+Boolean
+dummyInit(Device *dev)
+{
+ return TRUE;
+}
+
+FILE *
+dummyGet(Device *dev, char *dist, Boolean probe)
+{
+ return NULL;
+}
+
+void
+dummyShutdown(Device *dev)
+{
+ return;
+}
+
+static int
+deviceTry(struct _devname dev, char *try, int i)
+{
+ int fd;
+ char unit[80];
+
+ snprintf(unit, sizeof unit, dev.name, i);
+ snprintf(try, FILENAME_MAX, "/dev/%s", unit);
+ if (isDebug())
+ msgDebug("deviceTry: attempting to open %s\n", try);
+ fd = open(try, O_RDONLY);
+ if (fd >= 0) {
+ if (isDebug())
+ msgDebug("deviceTry: open of %s succeeded.\n", try);
+ }
+ return fd;
+}
+
+/* Register a new device in the devices array */
+Device *
+deviceRegister(char *name, char *desc, char *devname, DeviceType type, Boolean enabled,
+ Boolean (*init)(Device *), FILE * (*get)(Device *, char *, Boolean),
+ void (*shutdown)(Device *), void *private)
+{
+ Device *newdev = NULL;
+
+ if (numDevs == DEV_MAX)
+ msgFatal("Too many devices found!");
+ else {
+ newdev = new_device(name);
+ newdev->description = desc;
+ newdev->devname = devname;
+ newdev->type = type;
+ newdev->enabled = enabled;
+ newdev->init = init ? init : dummyInit;
+ newdev->get = get ? get : dummyGet;
+ newdev->shutdown = shutdown ? shutdown : dummyShutdown;
+ newdev->private = private;
+ Devices[numDevs] = newdev;
+ Devices[++numDevs] = NULL;
+ }
+ return newdev;
+}
+
+/* Reset the registered device chain */
+void
+deviceReset(void)
+{
+ int i;
+
+ for (i = 0; i < numDevs; i++) {
+ DEVICE_SHUTDOWN(Devices[i]);
+
+ /* XXX this potentially leaks Devices[i]->private if it's being
+ * used to point to something dynamic, but you're not supposed
+ * to call this routine at such times that some open instance
+ * has its private ptr pointing somewhere anyway. XXX
+ */
+ free(Devices[i]);
+ }
+ Devices[numDevs = 0] = NULL;
+}
+
+/* Get all device information for devices we have attached */
+void
+deviceGetAll(void)
+{
+ int i, j, fd;
+ char **names;
+
+ msgNotify("Probing devices, please wait (this can take a while)...");
+
+ /* Next, try to find all the types of devices one might need
+ * during the second stage of the installation.
+ */
+ for (i = 0; device_names[i].name; i++) {
+ for (j = 0; j < device_names[i].max; j++) {
+ char try[FILENAME_MAX];
+
+ switch(device_names[i].type) {
+ case DEVICE_TYPE_DISK:
+ fd = deviceTry(device_names[i], try, j);
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ /* Finally, go get the disks and look for DOS partitions to register */
+ if ((names = Disk_Names()) != NULL) {
+ int i;
+
+ for (i = 0; names[i]; i++) {
+ Disk *d;
+
+ /* Ignore memory disks */
+ if (!strncmp(names[i], "md", 2))
+ continue;
+
+ /*
+ * XXX
+ * Due to unknown reasons, Disk_Names() returns SCSI CDROM as a
+ * valid disk. This is main reason why sysinstall presents SCSI
+ * CDROM to available disks in Fdisk/Label menu. In addition,
+ * adding a blank SCSI CDROM to the menu generates floating point
+ * exception in sparc64. Disk_Names() just extracts sysctl
+ * "kern.disks". Why GEOM treats SCSI CDROM as a disk is beyond
+ * me and that should be investigated.
+ * For temporary workaround, ignore SCSI CDROM device.
+ */
+ if (!strncmp(names[i], "cd", 2))
+ continue;
+
+ d = Open_Disk(names[i]);
+ if (!d) {
+ msgDebug("Unable to open disk %s\n", names[i]);
+ continue;
+ }
+
+ deviceRegister(names[i], names[i], d->name, DEVICE_TYPE_DISK, FALSE,
+ dummyInit, dummyGet, dummyShutdown, d);
+ if (isDebug())
+ msgDebug("Found a disk device named %s\n", names[i]);
+
+#if 0
+ /* Look for existing DOS partitions to register as "DOS media devices" */
+ for (c1 = d->chunks->part; c1; c1 = c1->next) {
+ if (c1->type == fat || c1->type == efi || c1->type == extended) {
+ Device *dev;
+ char devname[80];
+
+ /* Got one! */
+ snprintf(devname, sizeof devname, "/dev/%s", c1->name);
+ dev = deviceRegister(c1->name, c1->name, strdup(devname), DEVICE_TYPE_DOS, TRUE,
+ mediaInitDOS, mediaGetDOS, mediaShutdownDOS, NULL);
+ dev->private = c1;
+ if (isDebug())
+ msgDebug("Found a DOS partition %s on drive %s\n", c1->name, d->name);
+ }
+ }
+#endif
+ }
+ free(names);
+ }
+ dialog_clear_norefresh();
+}
+
+/* Rescan all devices, after closing previous set - convenience function */
+void
+deviceRescan(void)
+{
+ deviceReset();
+ deviceGetAll();
+}
+
+/*
+ * Find all devices that match the criteria, allowing "wildcarding" as well
+ * by allowing NULL or ANY values to match all. The array returned is static
+ * and may be used until the next invocation of deviceFind().
+ */
+Device **
+deviceFind(char *name, DeviceType class)
+{
+ static Device *found[DEV_MAX];
+ int i, j;
+
+ j = 0;
+ for (i = 0; i < numDevs; i++) {
+ if ((!name || !strcmp(Devices[i]->name, name))
+ && (class == DEVICE_TYPE_ANY || class == Devices[i]->type))
+ found[j++] = Devices[i];
+ }
+ found[j] = NULL;
+ return j ? found : NULL;
+}
+
+Device **
+deviceFindDescr(char *name, char *desc, DeviceType class)
+{
+ static Device *found[DEV_MAX];
+ int i, j;
+
+ j = 0;
+ for (i = 0; i < numDevs; i++) {
+ if ((!name || !strcmp(Devices[i]->name, name)) &&
+ (!desc || !strcmp(Devices[i]->description, desc)) &&
+ (class == DEVICE_TYPE_ANY || class == Devices[i]->type))
+ found[j++] = Devices[i];
+ }
+ found[j] = NULL;
+ return j ? found : NULL;
+}
+
+int
+deviceCount(Device **devs)
+{
+ int i;
+
+ if (!devs)
+ return 0;
+ for (i = 0; devs[i]; i++);
+ return i;
+}
+
+/*
+ * Create a menu listing all the devices of a certain type in the system.
+ * The passed-in menu is expected to be a "prototype" from which the new
+ * menu is cloned.
+ */
+DMenu *
+deviceCreateMenu(DMenu *menu, DeviceType type, int (*hook)(dialogMenuItem *d), int (*check)(dialogMenuItem *d))
+{
+ Device **devs;
+ int numdevs;
+ DMenu *tmp = NULL;
+ int i, j;
+
+ devs = deviceFind(NULL, type);
+ numdevs = deviceCount(devs);
+ if (!numdevs)
+ return NULL;
+ tmp = (DMenu *)safe_malloc(sizeof(DMenu) + (sizeof(dialogMenuItem) * (numdevs + 1)));
+ bcopy(menu, tmp, sizeof(DMenu));
+ for (i = 0; devs[i]; i++) {
+ tmp->items[i].prompt = devs[i]->name;
+ for (j = 0; j < numDevs; j++) {
+ if (devs[i] == Devices[j]) {
+ tmp->items[i].title = Devices[j]->description;
+ break;
+ }
+ }
+ if (j == numDevs)
+ tmp->items[i].title = "<unknown device type>";
+ tmp->items[i].fire = hook;
+ tmp->items[i].checked = check;
+ }
+ tmp->items[i].title = NULL;
+ return tmp;
+}
diff --git a/usr.sbin/sade/disks.c b/usr.sbin/sade/disks.c
new file mode 100644
index 0000000..b802e12
--- /dev/null
+++ b/usr.sbin/sade/disks.c
@@ -0,0 +1,1038 @@
+/*
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sade.h"
+#include <ctype.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libdisk.h>
+#include <sys/stat.h>
+#include <sys/disklabel.h>
+
+#ifdef WITH_SLICES
+enum size_units_t { UNIT_BLOCKS, UNIT_KILO, UNIT_MEG, UNIT_GIG, UNIT_SIZE };
+
+#ifdef PC98
+#define SUBTYPE_FREEBSD 50324
+#define SUBTYPE_FAT 37218
+#else
+#define SUBTYPE_FREEBSD 165
+#define SUBTYPE_FAT 6
+#endif
+#define SUBTYPE_EFI 239
+
+#ifdef PC98
+#define OTHER_SLICE_VALUES \
+ "Other popular values are 37218 for a\n" \
+ "DOS FAT partition.\n\n"
+#else
+#define OTHER_SLICE_VALUES \
+ "Other popular values are 6 for a\n" \
+ "DOS FAT partition, 131 for a Linux ext2fs partition, or\n" \
+ "130 for a Linux swap partition.\n\n"
+#endif
+#define NON_FREEBSD_NOTE \
+ "Note: If you choose a non-FreeBSD partition type, it will not\n" \
+ "be formatted or otherwise prepared, it will simply reserve space\n" \
+ "for you to use another tool, such as DOS format, to later format\n" \
+ "and actually use the partition."
+
+/* Where we start displaying chunk information on the screen */
+#define CHUNK_START_ROW 5
+
+/* Where we keep track of MBR chunks */
+#define CHUNK_INFO_ENTRIES 16
+static struct chunk *chunk_info[CHUNK_INFO_ENTRIES];
+static int current_chunk;
+
+static void diskPartitionNonInteractive(Device *dev);
+#if !defined(__ia64__)
+static u_char * bootalloc(char *name, size_t *size);
+#endif
+
+static void
+record_chunks(Disk *d)
+{
+ struct chunk *c1 = NULL;
+ int i = 0;
+ daddr_t last_free = 0;
+
+ if (!d->chunks)
+ msgFatal("No chunk list found for %s!", d->name);
+
+ for (c1 = d->chunks->part; c1; c1 = c1->next) {
+ if (c1->type == unused && c1->size > last_free) {
+ last_free = c1->size;
+ current_chunk = i;
+ }
+ chunk_info[i++] = c1;
+ }
+ chunk_info[i] = NULL;
+ if (current_chunk >= i)
+ current_chunk = i - 1;
+}
+
+static daddr_t Total;
+
+static void
+check_geometry(Disk *d)
+{
+ int sg;
+
+#ifdef PC98
+ if (d->bios_cyl >= 65536 || d->bios_hd > 256 || d->bios_sect >= 256)
+#else
+ if (d->bios_cyl > 65536 || d->bios_hd > 256 || d->bios_sect >= 64)
+#endif
+ {
+ dialog_clear_norefresh();
+ sg = msgYesNo("WARNING: It is safe to use a geometry of %lu/%lu/%lu for %s on\n"
+ "computers with modern BIOS versions. If this disk is to be used\n"
+ "on an old machine it is recommended that it does not have more\n"
+ "than 65535 cylinders, more than 255 heads, or more than\n"
+#ifdef PC98
+ "255"
+#else
+ "63"
+#endif
+ " sectors per track.\n"
+ "\n"
+ "Would you like to keep using the current geometry?\n",
+ d->bios_cyl, d->bios_hd, d->bios_sect, d->name);
+ if (sg == 1) {
+ Sanitize_Bios_Geom(d);
+ msgConfirm("A geometry of %lu/%lu/%lu was calculated for %s.\n"
+ "\n"
+ "If you are not sure about this, please consult the Hardware Guide\n"
+ "in the Documentation submenu or use the (G)eometry command to\n"
+ "change it. Remember: you need to enter whatever your BIOS thinks\n"
+ "the geometry is! For IDE, it's what you were told in the BIOS\n"
+ "setup. For SCSI, it's the translation mode your controller is\n"
+ "using. Do NOT use a ``physical geometry''.\n",
+ d->bios_cyl, d->bios_hd, d->bios_sect, d->name);
+ }
+ }
+}
+
+static void
+print_chunks(Disk *d, int u)
+{
+ int row;
+ int i;
+ daddr_t sz;
+ char *szstr;
+
+ szstr = (u == UNIT_GIG ? "GB" : (u == UNIT_MEG ? "MB" :
+ (u == UNIT_KILO ? "KB" : "ST")));
+
+ Total = 0;
+ for (i = 0; chunk_info[i]; i++)
+ Total += chunk_info[i]->size;
+ attrset(A_NORMAL);
+ mvaddstr(0, 0, "Disk name:\t");
+ clrtobot();
+ attrset(A_REVERSE); addstr(d->name); attrset(A_NORMAL);
+ attrset(A_REVERSE); mvaddstr(0, 55, "FDISK Partition Editor"); attrset(A_NORMAL);
+ mvprintw(1, 0,
+ "DISK Geometry:\t%lu cyls/%lu heads/%lu sectors = %jd sectors (%jdMB)",
+ d->bios_cyl, d->bios_hd, d->bios_sect,
+ (intmax_t)d->bios_cyl * d->bios_hd * d->bios_sect,
+ (intmax_t)d->bios_cyl * d->bios_hd * d->bios_sect / (1024/512) / 1024);
+ mvprintw(3, 0, "%6s %10s(%s) %10s %8s %6s %10s %8s %8s",
+ "Offset", "Size", szstr, "End", "Name", "PType", "Desc",
+ "Subtype", "Flags");
+ for (i = 0, row = CHUNK_START_ROW; chunk_info[i]; i++, row++) {
+ switch(u) {
+ default: /* fall thru */
+ case UNIT_BLOCKS:
+ sz = chunk_info[i]->size;
+ break;
+ case UNIT_KILO:
+ sz = chunk_info[i]->size / (1024/512);
+ break;
+ case UNIT_MEG:
+ sz = chunk_info[i]->size / (1024/512) / 1024;
+ break;
+ case UNIT_GIG:
+ sz = chunk_info[i]->size / (1024/512) / 1024 / 1024;
+ break;
+ }
+ if (i == current_chunk)
+ attrset(ATTR_SELECTED);
+ mvprintw(row, 0, "%10jd %10jd %10jd %8s %6d %10s %8d\t%-6s",
+ (intmax_t)chunk_info[i]->offset, (intmax_t)sz,
+ (intmax_t)chunk_info[i]->end, chunk_info[i]->name,
+ chunk_info[i]->type,
+ slice_type_name(chunk_info[i]->type, chunk_info[i]->subtype),
+ chunk_info[i]->subtype, ShowChunkFlags(chunk_info[i]));
+ if (i == current_chunk)
+ attrset(A_NORMAL);
+ }
+}
+
+static void
+print_command_summary(void)
+{
+ mvprintw(14, 0, "The following commands are supported (in upper or lower case):");
+ mvprintw(16, 0, "A = Use Entire Disk G = set Drive Geometry C = Create Slice F = `DD' mode");
+ mvprintw(17, 0, "D = Delete Slice Z = Toggle Size Units S = Set Bootable | = Wizard m.");
+ mvprintw(18, 0, "T = Change Type U = Undo All Changes Q = Finish");
+ mvprintw(18, 47, "W = Write Changes");
+ mvprintw(21, 0, "Use F1 or ? to get more help, arrow keys to select.");
+ move(0, 0);
+}
+
+#ifdef PC98
+static void
+getBootMgr(char *dname, u_char **bootipl, size_t *bootipl_size,
+ u_char **bootmenu, size_t *bootmenu_size)
+{
+ static u_char *boot0;
+ static size_t boot0_size;
+ static u_char *boot05;
+ static size_t boot05_size;
+
+ char str[80];
+ char *cp;
+ int i = 0;
+
+ cp = variable_get(VAR_BOOTMGR);
+ if (!cp) {
+ /* Figure out what kind of IPL the user wants */
+ sprintf(str, "Install Boot Manager for drive %s?", dname);
+ MenuIPLType.title = str;
+ i = dmenuOpenSimple(&MenuIPLType, FALSE);
+ } else {
+ if (!strncmp(cp, "boot", 4))
+ BootMgr = 0;
+ else
+ BootMgr = 1;
+ }
+ if (cp || i) {
+ switch (BootMgr) {
+ case 0:
+ if (!boot0) boot0 = bootalloc("boot0", &boot0_size);
+ *bootipl = boot0;
+ *bootipl_size = boot0_size;
+ if (!boot05) boot05 = bootalloc("boot0.5", &boot05_size);
+ *bootmenu = boot05;
+ *bootmenu_size = boot05_size;
+ return;
+ case 1:
+ default:
+ break;
+ }
+ }
+ *bootipl = NULL;
+ *bootipl_size = 0;
+ *bootmenu = NULL;
+ *bootmenu_size = 0;
+}
+#else
+static void
+getBootMgr(char *dname, u_char **bootCode, size_t *bootCodeSize)
+{
+#if defined(__i386__) || defined(__amd64__) /* only meaningful on x86 */
+ static u_char *mbr, *boot0;
+ static size_t mbr_size, boot0_size;
+ char str[80];
+ char *cp;
+ int i = 0;
+
+ cp = variable_get(VAR_BOOTMGR);
+ if (!cp) {
+ /* Figure out what kind of MBR the user wants */
+ sprintf(str, "Install Boot Manager for drive %s?", dname);
+ MenuMBRType.title = str;
+ i = dmenuOpenSimple(&MenuMBRType, FALSE);
+ }
+ else {
+ if (!strncmp(cp, "boot", 4))
+ BootMgr = 0;
+ else if (!strcmp(cp, "standard"))
+ BootMgr = 1;
+ else
+ BootMgr = 2;
+ }
+ if (cp || i) {
+ switch (BootMgr) {
+ case 0:
+ if (!boot0) boot0 = bootalloc("boot0", &boot0_size);
+ *bootCode = boot0;
+ *bootCodeSize = boot0_size;
+ return;
+ case 1:
+ if (!mbr) mbr = bootalloc("mbr", &mbr_size);
+ *bootCode = mbr;
+ *bootCodeSize = mbr_size;
+ return;
+ case 2:
+ default:
+ break;
+ }
+ }
+#endif
+ *bootCode = NULL;
+ *bootCodeSize = 0;
+}
+#endif
+#endif /* WITH_SLICES */
+
+int
+diskGetSelectCount(Device ***devs)
+{
+ int i, cnt, enabled;
+ char *cp;
+ Device **dp;
+
+ cp = variable_get(VAR_DISK);
+ dp = *devs = deviceFind(cp, DEVICE_TYPE_DISK);
+ cnt = deviceCount(dp);
+ if (!cnt)
+ return -1;
+ for (i = 0, enabled = 0; i < cnt; i++) {
+ if (dp[i]->enabled)
+ ++enabled;
+ }
+ return enabled;
+}
+
+#ifdef WITH_SLICES
+void
+diskPartition(Device *dev)
+{
+ char *cp, *p;
+ int rv, key = 0;
+ int i;
+ Boolean chunking;
+ char *msg = NULL;
+#ifdef PC98
+ u_char *bootipl;
+ size_t bootipl_size;
+ u_char *bootmenu;
+ size_t bootmenu_size;
+#else
+ u_char *mbrContents;
+ size_t mbrSize;
+#endif
+ WINDOW *w = savescr();
+ Disk *d = (Disk *)dev->private;
+ int size_unit;
+
+ size_unit = UNIT_BLOCKS;
+ chunking = TRUE;
+ keypad(stdscr, TRUE);
+
+ /* Flush both the dialog and curses library views of the screen
+ since we don't always know who called us */
+ dialog_clear_norefresh(), clear();
+ current_chunk = 0;
+
+ /* Set up the chunk array */
+ record_chunks(d);
+
+ /* Give the user a chance to sanitize the disk geometry, if necessary */
+ check_geometry(d);
+
+ while (chunking) {
+ char *val, geometry[80];
+
+ /* Now print our overall state */
+ if (d)
+ print_chunks(d, size_unit);
+ print_command_summary();
+ if (msg) {
+ attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
+ beep();
+ msg = NULL;
+ }
+ else {
+ move(23, 0);
+ clrtoeol();
+ }
+
+ /* Get command character */
+ key = getch();
+ switch (toupper(key)) {
+ case '\014': /* ^L (redraw) */
+ clear();
+ msg = NULL;
+ break;
+
+ case '\020': /* ^P */
+ case KEY_UP:
+ case '-':
+ if (current_chunk != 0)
+ --current_chunk;
+ break;
+
+ case '\016': /* ^N */
+ case KEY_DOWN:
+ case '+':
+ case '\r':
+ case '\n':
+ if (chunk_info[current_chunk + 1])
+ ++current_chunk;
+ break;
+
+ case KEY_HOME:
+ current_chunk = 0;
+ break;
+
+ case KEY_END:
+ while (chunk_info[current_chunk + 1])
+ ++current_chunk;
+ break;
+
+ case KEY_F(1):
+ case '?':
+ systemDisplayHelp("slice");
+ clear();
+ break;
+
+ case 'A':
+ case 'F': /* Undocumented magic Dangerously Dedicated mode */
+#if !defined(__i386__) && !defined(__amd64__)
+ rv = 1;
+#else /* The rest is only relevant on x86 */
+ cp = variable_get(VAR_DEDICATE_DISK);
+ if (cp && !strcasecmp(cp, "always"))
+ rv = 1;
+ else if (toupper(key) == 'A')
+ rv = 0;
+ else {
+ rv = msgYesNo("Do you want to do this with a true partition entry\n"
+ "so as to remain cooperative with any future possible\n"
+ "operating systems on the drive(s)?\n"
+ "(See also the section about ``dangerously dedicated''\n"
+ "disks in the FreeBSD FAQ.)");
+ if (rv == -1)
+ rv = 0;
+ }
+#endif
+ All_FreeBSD(d, rv);
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ record_chunks(d);
+ clear();
+ break;
+
+ case 'C':
+ if (chunk_info[current_chunk]->type != unused)
+ msg = "Slice in use, delete it first or move to an unused one.";
+ else {
+ char *val, tmp[20], name[16], *cp;
+ daddr_t size;
+ int subtype;
+ chunk_e partitiontype;
+#ifdef PC98
+ snprintf(name, sizeof (name), "%s", "FreeBSD");
+ val = msgGetInput(name,
+ "Please specify the name for new FreeBSD slice.");
+ if (val)
+ strncpy(name, val, sizeof (name));
+#else
+ name[0] = '\0';
+#endif
+ snprintf(tmp, 20, "%jd", (intmax_t)chunk_info[current_chunk]->size);
+ val = msgGetInput(tmp, "Please specify the size for new FreeBSD slice in blocks\n"
+ "or append a trailing `M' for megabytes (e.g. 20M).");
+ if (val && (size = strtoimax(val, &cp, 0)) > 0) {
+ if (*cp && toupper(*cp) == 'M')
+ size *= ONE_MEG;
+ else if (*cp && toupper(*cp) == 'G')
+ size *= ONE_GIG;
+ sprintf(tmp, "%d", SUBTYPE_FREEBSD);
+ val = msgGetInput(tmp, "Enter type of partition to create:\n\n"
+ "Pressing Enter will choose the default, a native FreeBSD\n"
+ "slice (type %u). "
+ OTHER_SLICE_VALUES
+ NON_FREEBSD_NOTE, SUBTYPE_FREEBSD);
+ if (val && (subtype = strtol(val, NULL, 0)) > 0) {
+ if (subtype == SUBTYPE_FREEBSD)
+ partitiontype = freebsd;
+ else if (subtype == SUBTYPE_FAT)
+ partitiontype = fat;
+ else if (subtype == SUBTYPE_EFI)
+ partitiontype = efi;
+ else
+#ifdef PC98
+ partitiontype = pc98;
+#else
+ partitiontype = mbr;
+#endif
+ Create_Chunk(d, chunk_info[current_chunk]->offset, size, partitiontype, subtype,
+ (chunk_info[current_chunk]->flags & CHUNK_ALIGN), name);
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ record_chunks(d);
+ }
+ }
+ clear();
+ }
+ break;
+
+ case KEY_DC:
+ case 'D':
+ if (chunk_info[current_chunk]->type == unused)
+ msg = "Slice is already unused!";
+ else {
+ Delete_Chunk(d, chunk_info[current_chunk]);
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ record_chunks(d);
+ }
+ break;
+
+ case 'T':
+ if (chunk_info[current_chunk]->type == unused)
+ msg = "Slice is currently unused (use create instead)";
+ else {
+ char *val, tmp[20];
+ int subtype;
+ chunk_e partitiontype;
+
+ sprintf(tmp, "%d", chunk_info[current_chunk]->subtype);
+ val = msgGetInput(tmp, "New partition type:\n\n"
+ "Pressing Enter will use the current type. To choose a native\n"
+ "FreeBSD slice enter %u. "
+ OTHER_SLICE_VALUES
+ NON_FREEBSD_NOTE, SUBTYPE_FREEBSD);
+ if (val && (subtype = strtol(val, NULL, 0)) > 0) {
+ if (subtype == SUBTYPE_FREEBSD)
+ partitiontype = freebsd;
+ else if (subtype == SUBTYPE_FAT)
+ partitiontype = fat;
+ else if (subtype == SUBTYPE_EFI)
+ partitiontype = efi;
+ else
+#ifdef PC98
+ partitiontype = pc98;
+#else
+ partitiontype = mbr;
+#endif
+ chunk_info[current_chunk]->type = partitiontype;
+ chunk_info[current_chunk]->subtype = subtype;
+ }
+ }
+ break;
+
+ case 'G':
+ snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect);
+ val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n"
+ "Don't forget to use the two slash (/) separator characters!\n"
+ "It's not possible to parse the field without them.");
+ if (val) {
+ long nc, nh, ns;
+ nc = strtol(val, &val, 0);
+ nh = strtol(val + 1, &val, 0);
+ ns = strtol(val + 1, 0, 0);
+ Set_Bios_Geom(d, nc, nh, ns);
+ }
+ clear();
+ break;
+
+ case 'S':
+ /* Clear active states so we won't have two */
+ for (i = 0; (chunk_info[i] != NULL) && (i < CHUNK_INFO_ENTRIES); i++)
+ chunk_info[i]->flags &= !CHUNK_ACTIVE;
+
+ /* Set Bootable */
+ chunk_info[current_chunk]->flags |= CHUNK_ACTIVE;
+ break;
+
+ case 'U':
+ if (!variable_cmp(DISK_LABELLED, "written")) {
+ msgConfirm("You've already written this information out - you\n"
+ "can't undo it.");
+ }
+ else if (!msgNoYes("Are you SURE you want to Undo everything?")) {
+ char cp[BUFSIZ];
+
+ sstrncpy(cp, d->name, sizeof cp);
+ Free_Disk(dev->private);
+ d = Open_Disk(cp);
+ if (!d)
+ msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", cp);
+ dev->private = d;
+ variable_unset(DISK_PARTITIONED);
+ variable_unset(DISK_LABELLED);
+ if (d)
+ record_chunks(d);
+ }
+ clear();
+ break;
+
+ case 'W':
+ if (!msgNoYes("WARNING: You are about to modify an EXISTING installation.\n"
+ "You should simply type Q when you are finished\n"
+ "here and write to the disk from the label editor.\n\n"
+ "Are you absolutely sure you want to continue?")) {
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+
+#ifdef PC98
+ /*
+ * Don't trash the IPL if the first (and therefore only) chunk
+ * is marked for a truly dedicated disk (i.e., the disklabel
+ * starts at sector 0), even in cases where the user has
+ * requested a FreeBSD Boot Manager -- both would be fatal in
+ * this case.
+ */
+ /*
+ * Don't offer to update the IPL on this disk if the first
+ * "real" chunk looks like a FreeBSD "all disk" partition,
+ * or the disk is entirely FreeBSD.
+ */
+ if ((d->chunks->part->type != freebsd) ||
+ (d->chunks->part->offset > 1))
+ getBootMgr(d->name, &bootipl, &bootipl_size,
+ &bootmenu, &bootmenu_size);
+ else {
+ bootipl = NULL;
+ bootipl_size = 0;
+ bootmenu = NULL;
+ bootmenu_size = 0;
+ }
+ Set_Boot_Mgr(d, bootipl, bootipl_size, bootmenu, bootmenu_size);
+#else
+ /*
+ * Don't trash the MBR if the first (and therefore only) chunk
+ * is marked for a truly dedicated disk (i.e., the disklabel
+ * starts at sector 0), even in cases where the user has
+ * requested booteasy or a "standard" MBR -- both would be
+ * fatal in this case.
+ */
+ /*
+ * Don't offer to update the MBR on this disk if the first
+ * "real" chunk looks like a FreeBSD "all disk" partition,
+ * or the disk is entirely FreeBSD.
+ */
+ if ((d->chunks->part->type != freebsd) ||
+ (d->chunks->part->offset > 1))
+ getBootMgr(d->name, &mbrContents, &mbrSize);
+ else {
+ mbrContents = NULL;
+ mbrSize = 0;
+ }
+ Set_Boot_Mgr(d, mbrContents, mbrSize);
+#endif
+
+ if (DITEM_STATUS(diskPartitionWrite(NULL)) != DITEM_SUCCESS)
+ msgConfirm("Disk partition write returned an error status!");
+ else
+ msgConfirm("Wrote FDISK partition information out successfully.");
+ }
+ clear();
+ break;
+
+ case '|':
+ if (!msgNoYes("Are you SURE you want to go into Wizard mode?\n"
+ "No seat belts whatsoever are provided!")) {
+ clear();
+ refresh();
+ slice_wizard(d);
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ record_chunks(d);
+ }
+ else
+ msg = "Wise choice!";
+ clear();
+ break;
+
+ case '\033': /* ESC */
+ case 'Q':
+ chunking = FALSE;
+#ifdef PC98
+ /*
+ * Don't trash the IPL if the first (and therefore only) chunk
+ * is marked for a truly dedicated disk (i.e., the disklabel
+ * starts at sector 0), even in cases where the user has requested
+ * a FreeBSD Boot Manager -- both would be fatal in this case.
+ */
+ /*
+ * Don't offer to update the IPL on this disk if the first "real"
+ * chunk looks like a FreeBSD "all disk" partition, or the disk is
+ * entirely FreeBSD.
+ */
+ if ((d->chunks->part->type != freebsd) ||
+ (d->chunks->part->offset > 1)) {
+ if (variable_cmp(DISK_PARTITIONED, "written")) {
+ getBootMgr(d->name, &bootipl, &bootipl_size,
+ &bootmenu, &bootmenu_size);
+ if (bootipl != NULL && bootmenu != NULL)
+ Set_Boot_Mgr(d, bootipl, bootipl_size,
+ bootmenu, bootmenu_size);
+ }
+ }
+#else
+ /*
+ * Don't trash the MBR if the first (and therefore only) chunk
+ * is marked for a truly dedicated disk (i.e., the disklabel
+ * starts at sector 0), even in cases where the user has requested
+ * booteasy or a "standard" MBR -- both would be fatal in this case.
+ */
+ /*
+ * Don't offer to update the MBR on this disk if the first "real"
+ * chunk looks like a FreeBSD "all disk" partition, or the disk is
+ * entirely FreeBSD.
+ */
+ if ((d->chunks->part->type != freebsd) ||
+ (d->chunks->part->offset > 1)) {
+ if (variable_cmp(DISK_PARTITIONED, "written")) {
+ getBootMgr(d->name, &mbrContents, &mbrSize);
+ if (mbrContents != NULL)
+ Set_Boot_Mgr(d, mbrContents, mbrSize);
+ }
+ }
+#endif
+ break;
+
+ case 'Z':
+ size_unit = (size_unit + 1) % UNIT_SIZE;
+ break;
+
+ default:
+ beep();
+ msg = "Type F1 or ? for help";
+ break;
+ }
+ }
+ p = CheckRules(d);
+ if (p) {
+ char buf[FILENAME_MAX];
+
+ use_helpline("Press F1 to read more about disk slices.");
+ use_helpfile(systemHelpFile("partition", buf));
+ if (!variable_get(VAR_NO_WARN))
+ dialog_mesgbox("Disk slicing warning:", p, -1, -1);
+ free(p);
+ }
+ restorescr(w);
+}
+#endif /* WITH_SLICES */
+
+#if !defined(__ia64__)
+static u_char *
+bootalloc(char *name, size_t *size)
+{
+ char buf[FILENAME_MAX];
+ struct stat sb;
+
+ snprintf(buf, sizeof buf, "/boot/%s", name);
+ if (stat(buf, &sb) != -1) {
+ int fd;
+
+ fd = open(buf, O_RDONLY);
+ if (fd != -1) {
+ u_char *cp;
+
+ cp = malloc(sb.st_size);
+ if (read(fd, cp, sb.st_size) != sb.st_size) {
+ free(cp);
+ close(fd);
+ msgDebug("bootalloc: couldn't read %ld bytes from %s\n", (long)sb.st_size, buf);
+ return NULL;
+ }
+ close(fd);
+ if (size != NULL)
+ *size = sb.st_size;
+ return cp;
+ }
+ msgDebug("bootalloc: couldn't open %s\n", buf);
+ }
+ else
+ msgDebug("bootalloc: can't stat %s\n", buf);
+ return NULL;
+}
+#endif /* !__ia64__ */
+
+#ifdef WITH_SLICES
+static int
+partitionHook(dialogMenuItem *selected)
+{
+ Device **devs = NULL;
+
+ devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("Unable to find disk %s!", selected->prompt);
+ return DITEM_FAILURE;
+ }
+ /* Toggle enabled status? */
+ if (!devs[0]->enabled) {
+ devs[0]->enabled = TRUE;
+ diskPartition(devs[0]);
+ }
+ else
+ devs[0]->enabled = FALSE;
+ return DITEM_SUCCESS;
+}
+
+static int
+partitionCheck(dialogMenuItem *selected)
+{
+ Device **devs = NULL;
+
+ devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
+ if (!devs || devs[0]->enabled == FALSE)
+ return FALSE;
+ return TRUE;
+}
+
+int
+diskPartitionEditor(dialogMenuItem *self)
+{
+ DMenu *menu;
+ Device **devs;
+ int i, cnt, devcnt;
+
+ cnt = diskGetSelectCount(&devs);
+ devcnt = deviceCount(devs);
+ if (cnt == -1) {
+ msgConfirm("No disks found! Please verify that your disk controller is being\n"
+ "properly probed at boot time. See the Hardware Guide on the\n"
+ "Documentation menu for clues on diagnosing this type of problem.");
+ return DITEM_FAILURE;
+ }
+ else if (cnt) {
+ /* Some are already selected */
+ for (i = 0; i < devcnt; i++) {
+ if (devs[i]->enabled) {
+ if (variable_get(VAR_NONINTERACTIVE) &&
+ !variable_get(VAR_DISKINTERACTIVE))
+ diskPartitionNonInteractive(devs[i]);
+ else
+ diskPartition(devs[i]);
+ }
+ }
+ }
+ else {
+ /* No disks are selected, fall-back case now */
+ if (devcnt == 1) {
+ devs[0]->enabled = TRUE;
+ if (variable_get(VAR_NONINTERACTIVE) &&
+ !variable_get(VAR_DISKINTERACTIVE))
+ diskPartitionNonInteractive(devs[0]);
+ else
+ diskPartition(devs[0]);
+ return DITEM_SUCCESS;
+ }
+ else {
+ menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook, partitionCheck);
+ if (!menu) {
+ msgConfirm("No devices suitable for installation found!\n\n"
+ "Please verify that your disk controller (and attached drives)\n"
+ "were detected properly. This can be done by pressing the\n"
+ "[Scroll Lock] key and using the Arrow keys to move back to\n"
+ "the boot messages. Press [Scroll Lock] again to return.");
+ return DITEM_FAILURE;
+ }
+ else {
+ i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE;
+ free(menu);
+ }
+ return i;
+ }
+ }
+ return DITEM_SUCCESS;
+}
+#endif /* WITH_SLICES */
+
+int
+diskPartitionWrite(dialogMenuItem *self)
+{
+ Device **devs;
+ int i;
+
+ if (!variable_cmp(DISK_PARTITIONED, "written"))
+ return DITEM_SUCCESS;
+
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("Unable to find any disks to write to??");
+ return DITEM_FAILURE;
+ }
+ if (isDebug())
+ msgDebug("diskPartitionWrite: Examining %d devices\n", deviceCount(devs));
+ for (i = 0; devs[i]; i++) {
+ Disk *d = (Disk *)devs[i]->private;
+#if !defined(__ia64__)
+ static u_char *boot1;
+#endif
+#if defined(__i386__) || defined(__amd64__)
+ static u_char *boot2;
+#endif
+
+ if (!devs[i]->enabled)
+ continue;
+
+#if defined(__i386__) || defined(__amd64__)
+ if (!boot1) boot1 = bootalloc("boot1", NULL);
+ if (!boot2) boot2 = bootalloc("boot2", NULL);
+ Set_Boot_Blocks(d, boot1, boot2);
+#elif !defined(__ia64__)
+ if (!boot1) boot1 = bootalloc("boot1", NULL);
+ Set_Boot_Blocks(d, boot1, NULL);
+#endif
+
+ msgNotify("Writing partition information to drive %s", d->name);
+ if (!Fake && Write_Disk(d)) {
+ msgConfirm("ERROR: Unable to write data to disk %s!", d->name);
+ return DITEM_FAILURE;
+ }
+ }
+ /* Now it's not "yes", but "written" */
+ variable_set2(DISK_PARTITIONED, "written", 0);
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+#ifdef WITH_SLICES
+/* Partition a disk based wholly on which variables are set */
+static void
+diskPartitionNonInteractive(Device *dev)
+{
+ char *cp;
+ int i, all_disk = 0;
+ daddr_t sz;
+#ifdef PC98
+ u_char *bootipl;
+ size_t bootipl_size;
+ u_char *bootmenu;
+ size_t bootmenu_size;
+#else
+ u_char *mbrContents;
+ size_t mbrSize;
+#endif
+ Disk *d = (Disk *)dev->private;
+
+ record_chunks(d);
+ cp = variable_get(VAR_GEOMETRY);
+ if (cp) {
+ if (!strcasecmp(cp, "sane")) {
+#ifdef PC98
+ if (d->bios_cyl >= 65536 || d->bios_hd > 256 || d->bios_sect >= 256)
+#else
+ if (d->bios_cyl > 65536 || d->bios_hd > 256 || d->bios_sect >= 64)
+#endif
+ {
+ msgDebug("Warning: A geometry of %lu/%lu/%lu for %s is incorrect.\n",
+ d->bios_cyl, d->bios_hd, d->bios_sect, d->name);
+ Sanitize_Bios_Geom(d);
+ msgDebug("Sanitized geometry for %s is %lu/%lu/%lu.\n",
+ d->name, d->bios_cyl, d->bios_hd, d->bios_sect);
+ }
+ } else {
+ msgDebug("Setting geometry from script to: %s\n", cp);
+ d->bios_cyl = strtol(cp, &cp, 0);
+ d->bios_hd = strtol(cp + 1, &cp, 0);
+ d->bios_sect = strtol(cp + 1, 0, 0);
+ }
+ }
+
+ cp = variable_get(VAR_PARTITION);
+ if (cp) {
+ if (!strcmp(cp, "free")) {
+ /* Do free disk space case */
+ for (i = 0; chunk_info[i]; i++) {
+ /* If a chunk is at least 10MB in size, use it. */
+ if (chunk_info[i]->type == unused && chunk_info[i]->size > (10 * ONE_MEG)) {
+ Create_Chunk(d, chunk_info[i]->offset, chunk_info[i]->size,
+ freebsd, 3,
+ (chunk_info[i]->flags & CHUNK_ALIGN),
+ "FreeBSD");
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ break;
+ }
+ }
+ if (!chunk_info[i]) {
+ msgConfirm("Unable to find any free space on this disk!");
+ return;
+ }
+ }
+ else if (!strcmp(cp, "all")) {
+ /* Do all disk space case */
+ msgDebug("Warning: Devoting all of disk %s to FreeBSD.\n", d->name);
+
+ All_FreeBSD(d, FALSE);
+ }
+ else if (!strcmp(cp, "exclusive")) {
+ /* Do really-all-the-disk-space case */
+ msgDebug("Warning: Devoting all of disk %s to FreeBSD.\n", d->name);
+
+ All_FreeBSD(d, all_disk = TRUE);
+ }
+ else if ((sz = strtoimax(cp, &cp, 0))) {
+ /* Look for sz bytes free */
+ if (*cp && toupper(*cp) == 'M')
+ sz *= ONE_MEG;
+ else if (*cp && toupper(*cp) == 'G')
+ sz *= ONE_GIG;
+ for (i = 0; chunk_info[i]; i++) {
+ /* If a chunk is at least sz MB, use it. */
+ if (chunk_info[i]->type == unused && chunk_info[i]->size >= sz) {
+ Create_Chunk(d, chunk_info[i]->offset, sz, freebsd, 3,
+ (chunk_info[i]->flags & CHUNK_ALIGN),
+ "FreeBSD");
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ break;
+ }
+ }
+ if (!chunk_info[i]) {
+ msgConfirm("Unable to find %jd free blocks on this disk!",
+ (intmax_t)sz);
+ return;
+ }
+ }
+ else if (!strcmp(cp, "existing")) {
+ /* Do existing FreeBSD case */
+ for (i = 0; chunk_info[i]; i++) {
+ if (chunk_info[i]->type == freebsd)
+ break;
+ }
+ if (!chunk_info[i]) {
+ msgConfirm("Unable to find any existing FreeBSD partitions on this disk!");
+ return;
+ }
+ }
+ else {
+ msgConfirm("`%s' is an invalid value for %s - is config file valid?", cp, VAR_PARTITION);
+ return;
+ }
+ if (!all_disk) {
+#ifdef PC98
+ getBootMgr(d->name, &bootipl, &bootipl_size,
+ &bootmenu, &bootmenu_size);
+ Set_Boot_Mgr(d, bootipl, bootipl_size, bootmenu, bootmenu_size);
+#else
+ getBootMgr(d->name, &mbrContents, &mbrSize);
+ Set_Boot_Mgr(d, mbrContents, mbrSize);
+#endif
+ }
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ }
+}
+#endif /* WITH_SLICES */
diff --git a/usr.sbin/sade/dispatch.c b/usr.sbin/sade/dispatch.c
new file mode 100644
index 0000000..5b10b73
--- /dev/null
+++ b/usr.sbin/sade/dispatch.c
@@ -0,0 +1,161 @@
+/*
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sade.h"
+#include <ctype.h>
+#include <errno.h>
+#include <sys/signal.h>
+#include <sys/fcntl.h>
+
+#include "list.h"
+
+static int dispatch_systemExecute(dialogMenuItem *unused);
+static int dispatch_msgConfirm(dialogMenuItem *unused);
+
+static struct _word {
+ char *name;
+ int (*handler)(dialogMenuItem *self);
+} resWords[] = {
+#ifdef WITH_SLICES
+ { "diskPartitionEditor", diskPartitionEditor },
+#endif
+ { "diskPartitionWrite", diskPartitionWrite },
+ { "diskLabelEditor", diskLabelEditor },
+ { "diskLabelCommit", diskLabelCommit },
+ { "msgConfirm", dispatch_msgConfirm },
+ { "system", dispatch_systemExecute },
+ { "dumpVariables", dump_variables },
+ { NULL, NULL },
+};
+
+/*
+ * Helper routines for buffering data.
+ *
+ * We read an entire configuration into memory before executing it
+ * so that we are truely standalone and can do things like nuke the
+ * file or disk we're working on.
+ */
+
+typedef struct command_buffer_ {
+ qelement queue;
+ char * string;
+} command_buffer;
+
+/*
+ * Command processing
+ */
+
+static int
+dispatch_systemExecute(dialogMenuItem *unused)
+{
+ char *cmd = variable_get(VAR_COMMAND);
+
+ if (cmd)
+ return systemExecute(cmd) ? DITEM_FAILURE : DITEM_SUCCESS;
+ else
+ msgDebug("_systemExecute: No command passed in `command' variable.\n");
+ return DITEM_FAILURE;
+}
+
+static int
+dispatch_msgConfirm(dialogMenuItem *unused)
+{
+ char *msg = variable_get(VAR_COMMAND);
+
+ if (msg) {
+ msgConfirm("%s", msg);
+ return DITEM_SUCCESS;
+ }
+
+ msgDebug("_msgConfirm: No message passed in `command' variable.\n");
+ return DITEM_FAILURE;
+}
+
+static int
+call_possible_resword(char *name, dialogMenuItem *value, int *status)
+{
+ int i, rval;
+
+ rval = 0;
+ for (i = 0; resWords[i].name; i++) {
+ if (!strcmp(name, resWords[i].name)) {
+ *status = resWords[i].handler(value);
+ rval = 1;
+ break;
+ }
+ }
+ return rval;
+}
+
+/* For a given string, call it or spit out an undefined command diagnostic */
+int
+dispatchCommand(char *str)
+{
+ int i;
+ char *cp;
+
+ if (!str || !*str) {
+ msgConfirm("Null or zero-length string passed to dispatchCommand");
+ return DITEM_FAILURE;
+ }
+ /* If it's got a newline, trim it */
+ if ((cp = index(str, '\n')) != NULL)
+ *cp = '\0';
+
+ /* If it's got a `=' sign in there, assume it's a variable setting */
+ if (index(str, '=')) {
+ if (isDebug())
+ msgDebug("dispatch: setting variable `%s'\n", str);
+ variable_set(str, 0);
+ i = DITEM_SUCCESS;
+ }
+ else {
+ /* A command might be a pathname if it's encoded in argv[0], which
+ we also support */
+ if ((cp = rindex(str, '/')) != NULL)
+ str = cp + 1;
+ if (isDebug())
+ msgDebug("dispatch: calling resword `%s'\n", str);
+ if (!call_possible_resword(str, NULL, &i)) {
+ msgNotify("Warning: No such command ``%s''", str);
+ i = DITEM_FAILURE;
+ }
+ /*
+ * Allow a user to prefix a command with "noError" to cause
+ * us to ignore any errors for that one command.
+ */
+ if (i != DITEM_SUCCESS && variable_get(VAR_NO_ERROR))
+ i = DITEM_SUCCESS;
+ variable_unset(VAR_NO_ERROR);
+ }
+ return i;
+}
+
diff --git a/usr.sbin/sade/dmenu.c b/usr.sbin/sade/dmenu.c
new file mode 100644
index 0000000..139f999
--- /dev/null
+++ b/usr.sbin/sade/dmenu.c
@@ -0,0 +1,295 @@
+/*
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sade.h"
+#include <errno.h>
+
+#define MAX_MENU 15
+
+static Boolean exited;
+
+int
+dmenuDisplayFile(dialogMenuItem *tmp)
+{
+ systemDisplayHelp((char *)tmp->data);
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuSubmenu(dialogMenuItem *tmp)
+{
+ return (dmenuOpenSimple((DMenu *)(tmp->data), FALSE) ? DITEM_SUCCESS : DITEM_FAILURE);
+}
+
+int
+dmenuSystemCommand(dialogMenuItem *self)
+{
+ WINDOW *w = NULL; /* Keep lint happy */
+
+ /* If aux is set, the command is known not to produce any screen-spoiling output */
+ if (!self->aux)
+ w = savescr();
+ systemExecute((char *)self->data);
+ if (!self->aux)
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuSystemCommandBox(dialogMenuItem *tmp)
+{
+ WINDOW *w = savescr();
+
+ use_helpfile(NULL);
+ use_helpline("Select OK to dismiss this dialog");
+ dialog_prgbox(tmp->title, (char *)tmp->data, 22, 76, 1, 1);
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuExit(dialogMenuItem *tmp)
+{
+ exited = TRUE;
+ return DITEM_LEAVE_MENU;
+}
+
+int
+dmenuSetVariable(dialogMenuItem *tmp)
+{
+ variable_set((char *)tmp->data, *((char *)tmp->data) != '_');
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuSetVariables(dialogMenuItem *tmp)
+{
+ char *cp1, *cp2;
+ char *copy = strdup((char *)tmp->data);
+
+ for (cp1 = copy; cp1 != NULL;) {
+ cp2 = index(cp1, ',');
+ if (cp2 != NULL) *cp2++ = '\0';
+ variable_set(cp1, *cp1 != '_');
+ cp1 = cp2;
+ }
+ free(copy);
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuToggleVariable(dialogMenuItem *tmp)
+{
+ char *var, *cp;
+ int status;
+
+ if (!(var = strdup((char *)tmp->data))) {
+ msgConfirm("Incorrect data field for `%s'!", tmp->title);
+ return DITEM_FAILURE;
+ }
+ if (!(cp = index(var, '='))) {
+ msgConfirm("Data field for %s is not in var=value format!", tmp->title);
+ return DITEM_FAILURE;
+ }
+ status = variable_check(var);
+ *cp = '\0';
+ variable_set2(var, status ? "NO" : "YES", *var != '_');
+ free(var);
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuISetVariable(dialogMenuItem *tmp)
+{
+ char *ans, *var;
+
+ if (!(var = (char *)tmp->data)) {
+ msgConfirm("Incorrect data field for `%s'!", tmp->title);
+ return DITEM_FAILURE;
+ }
+ ans = msgGetInput(variable_get(var), tmp->title, 1);
+ if (!ans)
+ return DITEM_FAILURE;
+ else if (!*ans)
+ variable_unset(var);
+ else
+ variable_set2(var, ans, *var != '_');
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuSetFlag(dialogMenuItem *tmp)
+{
+ if (*((unsigned int *)tmp->data) & tmp->aux)
+ *((unsigned int *)tmp->data) &= ~tmp->aux;
+ else
+ *((unsigned int *)tmp->data) |= tmp->aux;
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuSetValue(dialogMenuItem *tmp)
+{
+ *((unsigned int *)tmp->data) = tmp->aux;
+ return DITEM_SUCCESS;
+}
+
+/* Traverse menu but give user no control over positioning */
+Boolean
+dmenuOpenSimple(DMenu *menu, Boolean buttons)
+{
+ int choice, scroll, curr, max;
+
+ choice = scroll = curr = max = 0;
+ return dmenuOpen(menu, &choice, &scroll, &curr, &max, buttons);
+}
+
+/* Work functions for the state hook */
+int
+dmenuFlagCheck(dialogMenuItem *item)
+{
+ return (*((unsigned int *)item->data) & item->aux);
+}
+
+int
+dmenuVarCheck(dialogMenuItem *item)
+{
+ char *w;
+
+ w = (char *)item->aux;
+ if (!w)
+ w = (char *)item->data;
+ return variable_check(w);
+}
+
+int
+dmenuVarsCheck(dialogMenuItem *item)
+{
+ int res, init;
+ char *w, *cp1, *cp2;
+ char *copy;
+
+ w = (char *)item->aux;
+ if (!w)
+ w = (char *)item->data;
+ if (!w)
+ return FALSE;
+
+ copy = strdup(w);
+ res = TRUE;
+ init = FALSE;
+ for (cp1 = copy; cp1 != NULL;) {
+ init = TRUE;
+ cp2 = index(cp1, ',');
+ if (cp2 != NULL)
+ *cp2++ = '\0';
+ res = res && variable_check(cp1);
+ cp1 = cp2;
+ }
+ free(copy);
+ return res && init;
+}
+
+int
+dmenuRadioCheck(dialogMenuItem *item)
+{
+ return (*((long *)item->data) == item->aux);
+}
+
+static int
+menu_height(DMenu *menu, int n)
+{
+ int max;
+ char *t;
+
+ max = MAX_MENU;
+ if (StatusLine > 24)
+ max += StatusLine - 24;
+ for (t = menu->prompt; *t; t++) {
+ if (*t == '\n')
+ --max;
+ }
+ return n > max ? max : n;
+}
+
+/* Traverse over an internal menu */
+Boolean
+dmenuOpen(DMenu *menu, int *choice, int *scroll, int *curr, int *max, Boolean buttons)
+{
+ int n, rval = 0;
+ dialogMenuItem *items;
+
+ items = menu->items;
+ if (buttons)
+ items += 2;
+ /* Count up all the items */
+ for (n = 0; items[n].title; n++);
+
+ while (1) {
+ char buf[FILENAME_MAX];
+ WINDOW *w = savescr();
+
+ /* Any helpful hints, put 'em up! */
+ use_helpline(menu->helpline);
+ use_helpfile(systemHelpFile(menu->helpfile, buf));
+ dialog_clear_norefresh();
+ /* Pop up that dialog! */
+ if (menu->type & DMENU_NORMAL_TYPE)
+ rval = dialog_menu((u_char *)menu->title, (u_char *)menu->prompt,
+ -1, -1, menu_height(menu, n), -n, items,
+ (char *)(uintptr_t)buttons, choice, scroll);
+
+ else if (menu->type & DMENU_RADIO_TYPE)
+ rval = dialog_radiolist((u_char *)menu->title,
+ (u_char *)menu->prompt, -1, -1, menu_height(menu, n), -n,
+ items, (char *)(uintptr_t)buttons);
+
+ else if (menu->type & DMENU_CHECKLIST_TYPE)
+ rval = dialog_checklist((u_char *)menu->title,
+ (u_char *)menu->prompt, -1, -1, menu_height(menu, n), -n,
+ items, (char *)(uintptr_t)buttons);
+ else
+ msgFatal("Menu: `%s' is of an unknown type\n", menu->title);
+ if (exited) {
+ exited = FALSE;
+ restorescr(w);
+ return TRUE;
+ }
+ else if (rval) {
+ restorescr(w);
+ return FALSE;
+ }
+ else if (menu->type & DMENU_SELECTION_RETURNS) {
+ restorescr(w);
+ return TRUE;
+ }
+ }
+}
diff --git a/usr.sbin/sade/globals.c b/usr.sbin/sade/globals.c
new file mode 100644
index 0000000..0b08da3
--- /dev/null
+++ b/usr.sbin/sade/globals.c
@@ -0,0 +1,84 @@
+/*
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sade.h"
+
+/*
+ * Various global variables and an initialization hook to set them to
+ * whatever values we feel are appropriate.
+ */
+
+int DebugFD; /* Where diagnostic output goes */
+Boolean Fake; /* Only pretend to be useful */
+Boolean DialogActive; /* Is libdialog initialized? */
+Boolean ColorDisplay; /* Are we on a color display? */
+Boolean OnVTY; /* Are we on a VTY? */
+Boolean Restarting; /* Are we restarting sysinstall? */
+Variable *VarHead; /* The head of the variable chain */
+int BootMgr; /* Which boot manager we're using */
+int StatusLine; /* Where to stick our status messages */
+jmp_buf BailOut; /* Beam me up, scotty! The natives are pissed! */
+
+Chunk *HomeChunk;
+Chunk *RootChunk;
+Chunk *SwapChunk;
+Chunk *TmpChunk;
+Chunk *UsrChunk;
+Chunk *VarChunk;
+#ifdef __ia64__
+Chunk *EfiChunk;
+#endif
+
+/*
+ * Yes, I know some of these are already automatically initialized as
+ * globals. I simply find it clearer to set everything explicitly.
+ */
+void
+globalsInit(void)
+{
+ DebugFD = -1;
+ ColorDisplay = FALSE;
+ Fake = FALSE;
+ Restarting = FALSE;
+ OnVTY = FALSE;
+ DialogActive = FALSE;
+ VarHead = NULL;
+
+ HomeChunk = NULL;
+ RootChunk = NULL;
+ SwapChunk = NULL;
+ TmpChunk = NULL;
+ UsrChunk = NULL;
+ VarChunk = NULL;
+#ifdef __ia64__
+ EfiChunk = NULL;
+#endif
+}
diff --git a/usr.sbin/sade/help/partition.hlp b/usr.sbin/sade/help/partition.hlp
new file mode 100644
index 0000000..3d13b34
--- /dev/null
+++ b/usr.sbin/sade/help/partition.hlp
@@ -0,0 +1,169 @@
+This is the FreeBSD DiskLabel Editor.
+
+NOTE: If you're entering this editor from the update procedure then
+you probably shouldn't (C)reate anything at all but rather use only
+the (M)ount command to check and mount existing partitions for
+upgrading.
+
+If you would like the label editor to do most of the following for
+you, simply type `A' for automatic partitioning of the disk.
+
+If you wish to create partitions manually you may do so by moving the
+highlighted selection bar with the arrow keys over the FreeBSD
+partition(s) displayed at the top of the screen. Typing (C)reate
+while a partition with available free space is selected will allow you
+to create a BSD partition inside of it using some or all of its
+available space.
+
+Typing (M)ount over an existing partition entry (displayed in the
+middle of the screen) will allow you to set a mount point for it
+without initializing it. If you want it initialized, use the (T)oggle
+command to flip the Newfs flag. When Newfs is set to "Y", the
+filesystem in question will be ERASED and rebuilt from scratch!
+
+
+You should use this editor to create at least the following
+filesystems:
+
+ Name Purpose Min Size? Optional?
+ ---- ------- --------- ---------
+ / Root filesystem 118MB No
+ swap Swap space 2 * MEM No
+ /usr System & user files 128MB or more Yes
+
+Note: If you do not create a /usr filesystem then your / filesystem
+will need to be bigger - at least 240MB. This is not recommended as
+any media errors that may occur during disk I/O to user files will
+corrupt the filesystem containing vital system files as well. It is
+for this reason that / is generally kept on its own filesystem, where
+it should be considered essentially "read only" in your administration
+of it.
+
+Swap space is a little tricker, and the rule of "2 * MEM" is simply a
+best-guess approximation and not necessarily accurate for your
+intended usage of the system. If you intend to use the system heavily
+in a server or multi-user application, you may be well advised to
+increase this size. You may also create swap space on multiple drives
+for a larger "total" swap and this is, in fact, recommended if you
+have multiple, fast drives for which such load-balancing can only help
+overall I/O performance.
+
+The /usr filesystem should be sized according to what kind of
+distributions you're trying to load and how many packages you intend
+to install in locations like /usr/local. You can also make /usr/local
+a separate filesystem if you don't want to risk filling up your /usr
+by mistake.
+
+Another useful filesystem to create is /var, which contains mail, news
+printer spool files and other temporary items. It is a popular
+candidate for a separate partition and should be sized according to
+your estimates of the amount of mail, news or spooled print jobs that
+may be stored there.
+
+WARNING: If you do not create a separate filesystem for /var, space
+for such files will be allocated out of the root (/) filesystem
+instead. You may therefore wish to make the / partition bigger if you
+expect a lot of mail or news and do not want to make /var its own
+partition.
+
+If you're new to this installation, you might also want to read the
+following explanation of how FreeBSD's new "slice" paradigm for
+looking at disk storage works:
+
+
+In FreeBSD's new system, a device name can be broken up into up to 3
+parts. Take a typical name like ``/dev/da0s1a'':
+
+ The first three characters represent the drive name. If we had
+ a system with two SCSI drives on it then we'd see /dev/da0 and
+ /dev/da1 as the device entries representing the entire drives.
+
+ Next you have the "slice" (or "FDISK Partition") number,
+ as seen in the Partition Editor. Assuming that our da0
+ contained two slices, a FreeBSD slice and a DOS slice, that
+ would give us /dev/da0s1 and /dev/da0s2 as device entries pointing
+ to the entire slices.
+
+ Next, if a slice is a FreeBSD slice, you can have a number of
+ (confusingly named) "partitions" inside of it.
+
+ These partitions are where various filesystems or swap areas live,
+ and using our hypothetical two-SCSI-disk machine again, we might
+ have something like the following layout on da0:
+
+ Name Mountpoint
+ ---- ----------
+ da0s1a /
+ da0s1b <swap space>
+ da0s1e /usr
+
+Once you understand all this, then the purpose of the label editor
+becomes fairly clear: You're carving up the FreeBSD slices displayed
+at the top of the screen into smaller pieces, which are displayed in
+the middle of the screen, and then assigning FreeBSD file system names
+(mount points) to them.
+
+You can also use the label editor to mount existing partitions/slices
+into your filesystem hierarchy, as is frequently done for DOS FAT
+slices. For FreeBSD partitions, you can also toggle the "newfs" state
+so that the partitions are either (re)created from scratch or simply
+checked and mounted (the contents are preserved).
+
+If you set (S)oftUpdates on a filesystem, it will cause the
+"Soft Updates" policy to be in effect for it. This basically causes
+both metadata and data blocks to be written asynchronously to disk,
+but with extra state information which causes the metadata and any
+related data blocks to be committed in a single transaction. This
+results in async metadata update speeds (which are considerably
+faster than the default sync) without the potential for data loss
+which could occur if you simply mounted the filesystem with purely
+"async" update policy and then had a power failure. If you wish
+to later turn the softupdates policy back off, use the command
+"tunefs -n disable devicename". NOTE: It is probably not wise
+to use this on your root filesystem unless you have a large
+(e.g. non-standard size) root. The reason is that smaller filesystems
+with significant activity can temporarily overflow if the soft updates
+policy results in free'd blocks not being "garbage collected" as fast
+as they're being requested.
+
+The UNIX File System (UFS) on FreeBSD supports two different on-disk
+layouts: UFS1 and UFS2. UFS1 was the default file system in use
+through FreeBSD 5.0-RELEASE; as of FreeBSD 5.1-RELEASE, the default
+is now UFS2, with the exception of the PC98 platform. UFS2 provides
+sparse inode allocation (faster fsck), 64-bit storage pointers (larger
+maximum size), and native extended attributes (required for ACLs, MAC,
+and other advanced security and file system services). The selection
+of UFS1 or UFS2 must be made when the file system is created--later
+conversion is not currently possible. UFS2 is the recommended file
+system, but if disks are to be used on older FreeBSD systems, UFS1
+improves portability. When dual-booting between FreeBSD 4.x or
+earlier and FreeBSD 5.x, UFS1 file systems will be accessible from
+both. To toggle a file system to UFS1, press '1'. To restore it to
+UFS2, press '2'.
+
+WARNING: FreeBSD on i386 is currently unable to boot from root file
+systems larger than 1.5TB.
+
+To add additional flags to the newfs command line for UFS file
+systems, press 'N'. These options will be specified before the
+device argument of the command line, but after any other options
+placed there by sysinstall, such as the UFS version and soft
+updates flag; as such, arguments provided may override existing
+settings. To completely replace the newfs command used by
+sysinstall, press 'Z' to convert a partition to a Custom
+partition type. Sysinstall will prompt you with the newfs
+command line that it would have used based on existing settings
+prior to the change, but allow you to modify any aspect of the
+command line. Once a partition has been converted to a custom
+partition in the label editor, you will need to restart the
+labeling process or delete and recreate the partition to restore
+it to a non-custom state. Custom partitions are represented by
+the letters "CST" instead of "UFS" or "FAT.
+
+When you're done, type `Q' to exit.
+
+No actual changes will be made to the disk until you (C)ommit from the
+Install menu or (W)rite directly from this one. You're working with
+what is essentially a copy of the disk label(s), both here and in the
+FDISK Partition Editor, and the actual on-disk labels won't be
+affected by any changes you make until you explicitly say so.
diff --git a/usr.sbin/sade/help/slice.hlp b/usr.sbin/sade/help/slice.hlp
new file mode 100644
index 0000000..e9f3abb
--- /dev/null
+++ b/usr.sbin/sade/help/slice.hlp
@@ -0,0 +1,57 @@
+This is the Main Slice (``FDISK'' or PC-style Partition) Editor.
+
+Possible commands are printed at the bottom and the Master Boot Record
+contents are shown at the top. You can move up and down with the
+arrow keys and (C)reate a new slice whenever the highlighted
+selection bar is over a slice whose type is marked as "unused."
+
+You are expected to leave this screen with at least one slice
+marked "FreeBSD." Note that unlike Linux, you don't need to create
+multiple FreeBSD FDISK partition entries for different things like
+swap, file systems, etc. The usual convention is to create ONE
+FreeBSD slice (FDISK partition) per drive and then subsection this slice
+into swap and file systems with the Label editor.
+
+No actual changes will be made to the disk until you (C)ommit from the
+Install menu or use the (W)rite option here! You're working with what
+is essentially a copy of the disk label(s), both here and in the Label
+Editor.
+
+If you want to use the entire disk for FreeBSD, type `A'. Slices will
+be aligned to fictitious cylinder boundaries and space will be reserved
+in front of the FreeBSD slice for a [future] possible boot manager.
+
+For the truly dedicated disk case, type `F'. You'll be asked whether or
+not you wish to keep the disk (potentially) compatible with other
+operating systems, i.e. the information in the FDISK table should be
+kept valid. A truly dedicated disk can be achieved by selecting `No'.
+In that case, all BIOS geometry considerations will no longer be in
+effect and you can safely ignore any ``The detected geometry is
+invalid'' warning messages you may later see. It is also not necessary
+in this case to set a slice bootable or install an MBR boot manager as
+both things are then irrelevant. The FreeBSD slice will start at
+absolute sector 0 of the disk (so that FreeBSD's disk label is identical
+to the Master Boot Record) and extend to the very last sector of the
+disk medium. Needless to say, such a disk cannot have any sort of a
+boot manager, `disk manager', or anything else that has to interact with
+the BIOS. This option is therefore only considered safe for SCSI disks
+and most IDE disks and is primarily intended for people who are going to
+set up a dedicated FreeBSD server or workstation, not a typical `home PC'.
+
+If you select the default of `Yes' at the compatibility, slices will be
+aligned to fictitious cylinder boundaries and space will be reserved
+in front of the FreeBSD slice for a [future] possible boot manager.
+This is pretty much equivalent to having chosen `A' originally.
+
+The flags field has the following legend:
+
+ '=' -- This slice is properly aligned.
+ 'A' -- This slice is marked active.
+ 'R' -- This slice contains the root (/) filesystem
+
+If no slice is marked Active, you will need to either install
+a Boot Manager (the option for which will be presented later in the
+installation) or set one Active before leaving this screen.
+
+To leave the slice editor, type `Q'.
+
diff --git a/usr.sbin/sade/install.c b/usr.sbin/sade/install.c
new file mode 100644
index 0000000..952ae93
--- /dev/null
+++ b/usr.sbin/sade/install.c
@@ -0,0 +1,257 @@
+/*
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sade.h"
+#include <ctype.h>
+#include <sys/consio.h>
+#include <sys/disklabel.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <sys/wait.h>
+#include <sys/uio.h>
+#include <sys/param.h>
+#define MSDOSFS
+#include <sys/mount.h>
+#include <ufs/ufs/ufsmount.h>
+#include <fs/msdosfs/msdosfsmount.h>
+#undef MSDOSFS
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <libdisk.h>
+#include <limits.h>
+#include <unistd.h>
+#include <termios.h>
+
+#define TERMCAP_FILE "/usr/share/misc/termcap"
+
+Boolean
+checkLabels(Boolean whinge)
+{
+ Boolean status;
+
+ /* Don't allow whinging if noWarn is set */
+ if (variable_get(VAR_NO_WARN))
+ whinge = FALSE;
+
+ status = TRUE;
+ HomeChunk = RootChunk = SwapChunk = NULL;
+ TmpChunk = UsrChunk = VarChunk = NULL;
+#ifdef __ia64__
+ EfiChunk = NULL;
+#endif
+
+ /* We don't need to worry about root/usr/swap if we're already multiuser */
+ return status;
+}
+
+#define QUEUE_YES 1
+#define QUEUE_NO 0
+static int
+performNewfs(PartInfo *pi, char *dname, int queue)
+{
+ char buffer[LINE_MAX];
+
+ if (pi->do_newfs) {
+ switch(pi->newfs_type) {
+ case NEWFS_UFS:
+ snprintf(buffer, LINE_MAX, "%s %s %s %s %s",
+ NEWFS_UFS_CMD,
+ pi->newfs_data.newfs_ufs.softupdates ? "-U" : "",
+ pi->newfs_data.newfs_ufs.ufs1 ? "-O1" : "-O2",
+ pi->newfs_data.newfs_ufs.user_options,
+ dname);
+ break;
+
+ case NEWFS_MSDOS:
+ snprintf(buffer, LINE_MAX, "%s %s", NEWFS_MSDOS_CMD,
+ dname);
+ break;
+
+ case NEWFS_CUSTOM:
+ snprintf(buffer, LINE_MAX, "%s %s",
+ pi->newfs_data.newfs_custom.command, dname);
+ break;
+ }
+
+ if (queue == QUEUE_YES) {
+ command_shell_add(pi->mountpoint, buffer);
+ return (0);
+ } else
+ return (vsystem(buffer));
+ }
+ return (0);
+}
+
+/* Go newfs and/or mount all the filesystems we've been asked to */
+int
+installFilesystems(dialogMenuItem *self)
+{
+ int i;
+ Disk *disk;
+ Chunk *c1, *c2;
+ Device **devs;
+ PartInfo *root;
+ char dname[80];
+ Boolean upgrade = FALSE;
+
+ /* If we've already done this, bail out */
+ if (!variable_cmp(DISK_LABELLED, "written"))
+ return DITEM_SUCCESS;
+
+ upgrade = !variable_cmp(SYSTEM_STATE, "upgrade");
+ if (!checkLabels(TRUE))
+ return DITEM_FAILURE;
+
+ root = (RootChunk != NULL) ? (PartInfo *)RootChunk->private_data : NULL;
+
+ command_clear();
+
+ /* Now buzz through the rest of the partitions and mount them too */
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ for (i = 0; devs[i]; i++) {
+ if (!devs[i]->enabled)
+ continue;
+
+ disk = (Disk *)devs[i]->private;
+ if (!disk->chunks) {
+ msgConfirm("No chunk list found for %s!", disk->name);
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+ for (c1 = disk->chunks->part; c1; c1 = c1->next) {
+#ifdef __ia64__
+ if (c1->type == part) {
+ c2 = c1;
+ {
+#elif defined(__powerpc__)
+ if (c1->type == apple) {
+ for (c2 = c1->part; c2; c2 = c2->next) {
+#else
+ if (c1->type == freebsd) {
+ for (c2 = c1->part; c2; c2 = c2->next) {
+#endif
+ if (c2->type == part && c2->subtype != FS_SWAP && c2->private_data) {
+ PartInfo *tmp = (PartInfo *)c2->private_data;
+
+ /* Already did root */
+ if (c2 == RootChunk)
+ continue;
+
+ sprintf(dname, "/dev/%s", c2->name);
+
+ if (tmp->do_newfs && (!upgrade ||
+ !msgNoYes("You are upgrading - are you SURE you"
+ " want to newfs /dev/%s?", c2->name)))
+ performNewfs(tmp, dname, QUEUE_YES);
+ else
+ command_shell_add(tmp->mountpoint,
+ "fsck_ffs -y /dev/%s", c2->name);
+ command_func_add(tmp->mountpoint, Mount, c2->name);
+ }
+ else if (c2->type == part && c2->subtype == FS_SWAP) {
+ char fname[80];
+ int i;
+
+ if (c2 == SwapChunk)
+ continue;
+ sprintf(fname, "/dev/%s", c2->name);
+ i = (Fake || swapon(fname));
+ if (!i) {
+ dialog_clear_norefresh();
+ msgNotify("Added %s as an additional swap device", fname);
+ }
+ else {
+ msgConfirm("Unable to add %s as a swap device: %s", fname, strerror(errno));
+ }
+ }
+ }
+ }
+ else if (c1->type == fat && c1->private_data &&
+ (root->do_newfs || upgrade)) {
+ char name[FILENAME_MAX];
+
+ sprintf(name, "/%s", ((PartInfo *)c1->private_data)->mountpoint);
+ Mkdir(name);
+ }
+#if defined(__ia64__)
+ else if (c1->type == efi && c1->private_data) {
+ PartInfo *pi = (PartInfo *)c1->private_data;
+
+ sprintf(dname, "/dev/%s", c1->name);
+
+ if (pi->do_newfs && (!upgrade ||
+ !msgNoYes("You are upgrading - are you SURE you want to "
+ "newfs /dev/%s?", c1->name)))
+ performNewfs(pi, dname, QUEUE_YES);
+ }
+#endif
+ }
+ }
+
+ command_sort();
+ command_execute();
+ dialog_clear_norefresh();
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+static char *
+getRelname(void)
+{
+ static char buf[64];
+ size_t sz = (sizeof buf) - 1;
+
+ if (sysctlbyname("kern.osrelease", buf, &sz, NULL, 0) != -1) {
+ buf[sz] = '\0';
+ return buf;
+ }
+ else
+ return "<unknown>";
+}
+
+/* Initialize various user-settable values to their defaults */
+int
+installVarDefaults(dialogMenuItem *self)
+{
+
+ /* Set default startup options */
+ variable_set2(VAR_RELNAME, getRelname(), 0);
+ variable_set2(SYSTEM_STATE, "update", 0);
+ variable_set2(VAR_NEWFS_ARGS, "-b 16384 -f 2048", 0);
+ variable_set2(VAR_CONSTERM, "NO", 0);
+ return DITEM_SUCCESS;
+}
+
+/* Load the environment up from various system configuration files */
+void
+installEnvironment(void)
+{
+}
+
diff --git a/usr.sbin/sade/label.c b/usr.sbin/sade/label.c
new file mode 100644
index 0000000..3f20450
--- /dev/null
+++ b/usr.sbin/sade/label.c
@@ -0,0 +1,1703 @@
+/*
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sade.h"
+#include <ctype.h>
+#include <inttypes.h>
+#include <libdisk.h>
+#include <sys/disklabel.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+
+#define AUTO_HOME 0 /* do not create /home automatically */
+
+/*
+ * Everything to do with editing the contents of disk labels.
+ */
+
+/* A nice message we use a lot in the disklabel editor */
+#define MSG_NOT_APPLICABLE "That option is not applicable here"
+
+/* Where to start printing the freebsd slices */
+#define CHUNK_SLICE_START_ROW 2
+#define CHUNK_PART_START_ROW 11
+
+/* The smallest filesystem we're willing to create */
+#define FS_MIN_SIZE ONE_MEG
+
+/*
+ * Minimum partition sizes
+ */
+#if defined(__alpha__) || defined(__ia64__) || defined(__sparc64__) || defined(__amd64__)
+#define ROOT_MIN_SIZE 128
+#else
+#define ROOT_MIN_SIZE 118
+#endif
+#define SWAP_MIN_SIZE 32
+#define USR_MIN_SIZE 128
+#define VAR_MIN_SIZE 20
+#define TMP_MIN_SIZE 20
+#define HOME_MIN_SIZE 20
+
+/*
+ * Swap size limit for auto-partitioning (4G).
+ */
+#define SWAP_AUTO_LIMIT_SIZE 4096
+
+/*
+ * Default partition sizes. If we do not have sufficient disk space
+ * for this configuration we scale things relative to the NOM vs DEFAULT
+ * sizes. If the disk is larger then /home will get any remaining space.
+ */
+#define ROOT_DEFAULT_SIZE 512
+#define USR_DEFAULT_SIZE 8192
+#define VAR_DEFAULT_SIZE 1024
+#define TMP_DEFAULT_SIZE 512
+#define HOME_DEFAULT_SIZE USR_DEFAULT_SIZE
+
+/*
+ * Nominal partition sizes. These are used to scale the default sizes down
+ * when we have insufficient disk space. If this isn't sufficient we scale
+ * down using the MIN sizes instead.
+ */
+#define ROOT_NOMINAL_SIZE 256
+#define USR_NOMINAL_SIZE 1536
+#define VAR_NOMINAL_SIZE 128
+#define TMP_NOMINAL_SIZE 128
+#define HOME_NOMINAL_SIZE USR_NOMINAL_SIZE
+
+/* The bottom-most row we're allowed to scribble on */
+#define CHUNK_ROW_MAX 16
+
+
+/* All the chunks currently displayed on the screen */
+static struct {
+ struct chunk *c;
+ PartType type;
+} label_chunk_info[MAX_CHUNKS + 1];
+static int here;
+
+/*** with this value we try to track the most recently added label ***/
+static int label_focus = 0, pslice_focus = 0;
+
+static int diskLabel(Device *dev);
+static int diskLabelNonInteractive(Device *dev);
+static char *try_auto_label(Device **devs, Device *dev, int perc, int *req);
+
+static int
+labelHook(dialogMenuItem *selected)
+{
+ Device **devs = NULL;
+
+ devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("Unable to find disk %s!", selected->prompt);
+ return DITEM_FAILURE;
+ }
+ /* Toggle enabled status? */
+ if (!devs[0]->enabled) {
+ devs[0]->enabled = TRUE;
+ diskLabel(devs[0]);
+ }
+ else
+ devs[0]->enabled = FALSE;
+ return DITEM_SUCCESS;
+}
+
+static int
+labelCheck(dialogMenuItem *selected)
+{
+ Device **devs = NULL;
+
+ devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
+ if (!devs || devs[0]->enabled == FALSE)
+ return FALSE;
+ return TRUE;
+}
+
+int
+diskLabelEditor(dialogMenuItem *self)
+{
+ DMenu *menu;
+ Device **devs;
+ int i, cnt;
+
+ i = 0;
+ cnt = diskGetSelectCount(&devs);
+ if (cnt == -1) {
+ msgConfirm("No disks found! Please verify that your disk controller is being\n"
+ "properly probed at boot time. See the Hardware Guide on the\n"
+ "Documentation menu for clues on diagnosing this type of problem.");
+ return DITEM_FAILURE;
+ }
+ else if (cnt) {
+ /* Some are already selected */
+ if (variable_get(VAR_NONINTERACTIVE) &&
+ !variable_get(VAR_DISKINTERACTIVE))
+ i = diskLabelNonInteractive(NULL);
+ else
+ i = diskLabel(NULL);
+ }
+ else {
+ /* No disks are selected, fall-back case now */
+ cnt = deviceCount(devs);
+ if (cnt == 1) {
+ devs[0]->enabled = TRUE;
+ if (variable_get(VAR_NONINTERACTIVE) &&
+ !variable_get(VAR_DISKINTERACTIVE))
+ i = diskLabelNonInteractive(devs[0]);
+ else
+ i = diskLabel(devs[0]);
+ }
+ else {
+ menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, labelHook, labelCheck);
+ if (!menu) {
+ msgConfirm("No devices suitable for installation found!\n\n"
+ "Please verify that your disk controller (and attached drives)\n"
+ "were detected properly. This can be done by pressing the\n"
+ "[Scroll Lock] key and using the Arrow keys to move back to\n"
+ "the boot messages. Press [Scroll Lock] again to return.");
+ i = DITEM_FAILURE;
+ }
+ else {
+ i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE;
+ free(menu);
+ }
+ }
+ }
+ if (DITEM_STATUS(i) != DITEM_FAILURE) {
+ if (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ }
+ return i;
+}
+
+int
+diskLabelCommit(dialogMenuItem *self)
+{
+ char *cp;
+ int i;
+
+ /* Already done? */
+ if ((cp = variable_get(DISK_LABELLED)) && strcmp(cp, "yes"))
+ i = DITEM_SUCCESS;
+ else if (!cp) {
+ msgConfirm("You must assign disk labels before this option can be used.");
+ i = DITEM_FAILURE;
+ }
+ /* The routine will guard against redundant writes, just as this one does */
+ else if (DITEM_STATUS(diskPartitionWrite(self)) != DITEM_SUCCESS)
+ i = DITEM_FAILURE;
+ else if (DITEM_STATUS(installFilesystems(self)) != DITEM_SUCCESS)
+ i = DITEM_FAILURE;
+ else {
+ msgInfo("All filesystem information written successfully.");
+ variable_set2(DISK_LABELLED, "written", 0);
+ i = DITEM_SUCCESS;
+ }
+ return i;
+}
+
+/* See if we're already using a desired partition name */
+static Boolean
+check_conflict(char *name)
+{
+ int i;
+
+ for (i = 0; label_chunk_info[i].c; i++)
+ if ((label_chunk_info[i].type == PART_FILESYSTEM || label_chunk_info[i].type == PART_FAT
+ || label_chunk_info[i].type == PART_EFI) && label_chunk_info[i].c->private_data
+ && !strcmp(((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint, name))
+ return TRUE;
+ return FALSE;
+}
+
+/* How much space is in this FreeBSD slice? */
+static daddr_t
+space_free(struct chunk *c)
+{
+ struct chunk *c1;
+ daddr_t sz = c->size;
+
+ for (c1 = c->part; c1; c1 = c1->next) {
+ if (c1->type != unused)
+ sz -= c1->size;
+ }
+ if (sz < 0)
+ msgFatal("Partitions are larger than actual chunk??");
+ return sz;
+}
+
+/* Snapshot the current situation into the displayed chunks structure */
+static void
+record_label_chunks(Device **devs, Device *dev)
+{
+ int i, j, p;
+ struct chunk *c1, *c2;
+ Disk *d;
+
+ j = p = 0;
+ /* First buzz through and pick up the FreeBSD slices */
+ for (i = 0; devs[i]; i++) {
+ if ((dev && devs[i] != dev) || !devs[i]->enabled)
+ continue;
+ d = (Disk *)devs[i]->private;
+ if (!d->chunks)
+ msgFatal("No chunk list found for %s!", d->name);
+
+#ifdef __ia64__
+ label_chunk_info[j].type = PART_SLICE;
+ label_chunk_info[j].c = d->chunks;
+ j++;
+#endif
+
+ /* Put the slice entries first */
+ for (c1 = d->chunks->part; c1; c1 = c1->next) {
+ if (c1->type == freebsd) {
+ label_chunk_info[j].type = PART_SLICE;
+ label_chunk_info[j].c = c1;
+ ++j;
+ }
+#ifdef __powerpc__
+ if (c1->type == apple) {
+ label_chunk_info[j].type = PART_SLICE;
+ label_chunk_info[j].c = c1;
+ ++j;
+ }
+#endif
+ }
+ }
+
+ /* Now run through again and get the FreeBSD partition entries */
+ for (i = 0; devs[i]; i++) {
+ if (!devs[i]->enabled)
+ continue;
+ d = (Disk *)devs[i]->private;
+ /* Then buzz through and pick up the partitions */
+ for (c1 = d->chunks->part; c1; c1 = c1->next) {
+ if (c1->type == freebsd) {
+ for (c2 = c1->part; c2; c2 = c2->next) {
+ if (c2->type == part) {
+ if (c2->subtype == FS_SWAP)
+ label_chunk_info[j].type = PART_SWAP;
+ else
+ label_chunk_info[j].type = PART_FILESYSTEM;
+ label_chunk_info[j].c = c2;
+ ++j;
+ }
+ }
+ }
+ else if (c1->type == fat) {
+ label_chunk_info[j].type = PART_FAT;
+ label_chunk_info[j].c = c1;
+ ++j;
+ }
+#ifdef __ia64__
+ else if (c1->type == efi) {
+ label_chunk_info[j].type = PART_EFI;
+ label_chunk_info[j].c = c1;
+ ++j;
+ }
+ else if (c1->type == part) {
+ if (c1->subtype == FS_SWAP)
+ label_chunk_info[j].type = PART_SWAP;
+ else
+ label_chunk_info[j].type = PART_FILESYSTEM;
+ label_chunk_info[j].c = c1;
+ ++j;
+ }
+#endif
+#ifdef __powerpc__
+ else if (c1->type == apple) {
+ for (c2 = c1->part; c2; c2 = c2->next) {
+ if (c2->type == part) {
+ if (c2->subtype == FS_SWAP)
+ label_chunk_info[j].type = PART_SWAP;
+ else
+ label_chunk_info[j].type = PART_FILESYSTEM;
+ label_chunk_info[j].c = c2;
+ ++j;
+ }
+ }
+ }
+#endif
+ }
+ }
+ label_chunk_info[j].c = NULL;
+ if (here >= j) {
+ here = j ? j - 1 : 0;
+ }
+}
+
+/* A new partition entry */
+static PartInfo *
+new_part(PartType type, char *mpoint, Boolean newfs)
+{
+ PartInfo *pi;
+
+ if (!mpoint)
+ mpoint = (type == PART_EFI) ? "/efi" : "/change_me";
+
+ pi = (PartInfo *)safe_malloc(sizeof(PartInfo));
+ sstrncpy(pi->mountpoint, mpoint, FILENAME_MAX);
+
+ pi->do_newfs = newfs;
+
+ if (type == PART_EFI) {
+ pi->newfs_type = NEWFS_MSDOS;
+ } else {
+ pi->newfs_type = NEWFS_UFS;
+ strcpy(pi->newfs_data.newfs_ufs.user_options, "");
+ pi->newfs_data.newfs_ufs.acls = FALSE;
+ pi->newfs_data.newfs_ufs.multilabel = FALSE;
+ pi->newfs_data.newfs_ufs.softupdates = strcmp(mpoint, "/");
+#ifdef PC98
+ pi->newfs_data.newfs_ufs.ufs1 = TRUE;
+#else
+ pi->newfs_data.newfs_ufs.ufs1 = FALSE;
+#endif
+ }
+
+ return pi;
+}
+
+/* Get the mountpoint for a partition and save it away */
+static PartInfo *
+get_mountpoint(PartType type, struct chunk *old)
+{
+ char *val;
+ PartInfo *tmp;
+ Boolean newfs;
+
+ if (old && old->private_data)
+ tmp = old->private_data;
+ else
+ tmp = NULL;
+ val = (tmp != NULL) ? tmp->mountpoint : (type == PART_EFI) ? "/efi" : NULL;
+ val = msgGetInput(val, "Please specify a mount point for the partition");
+ if (!val || !*val) {
+ if (!old)
+ return NULL;
+ else {
+ free(old->private_data);
+ old->private_data = NULL;
+ }
+ return NULL;
+ }
+
+ /* Is it just the same value? */
+ if (tmp && !strcmp(tmp->mountpoint, val))
+ return NULL;
+
+ /* Did we use it already? */
+ if (check_conflict(val)) {
+ msgConfirm("You already have a mount point for %s assigned!", val);
+ return NULL;
+ }
+
+ /* Is it bogus? */
+ if (*val != '/') {
+ msgConfirm("Mount point must start with a / character");
+ return NULL;
+ }
+
+ /* Is it going to be mounted on root? */
+ if (!strcmp(val, "/")) {
+ if (old)
+ old->flags |= CHUNK_IS_ROOT;
+ }
+ else if (old)
+ old->flags &= ~CHUNK_IS_ROOT;
+
+ newfs = TRUE;
+ if (tmp) {
+ newfs = tmp->do_newfs;
+ safe_free(tmp);
+ }
+ val = string_skipwhite(string_prune(val));
+ tmp = new_part(type, val, newfs);
+ if (old) {
+ old->private_data = tmp;
+ old->private_free = safe_free;
+ }
+ return tmp;
+}
+
+/* Get the type of the new partiton */
+static PartType
+get_partition_type(void)
+{
+ char selection[20];
+ int i;
+ static unsigned char *fs_types[] = {
+#ifdef __ia64__
+ "EFI", "An EFI system partition",
+#endif
+ "FS", "A file system",
+ "Swap", "A swap partition.",
+ };
+ WINDOW *w = savescr();
+
+ i = dialog_menu("Please choose a partition type",
+ "If you want to use this partition for swap space, select Swap.\n"
+ "If you want to put a filesystem on it, choose FS.",
+ -1, -1,
+#ifdef __ia64__
+ 3, 3,
+#else
+ 2, 2,
+#endif
+ fs_types, selection, NULL, NULL);
+ restorescr(w);
+ if (!i) {
+#ifdef __ia64__
+ if (!strcmp(selection, "EFI"))
+ return PART_EFI;
+#endif
+ if (!strcmp(selection, "FS"))
+ return PART_FILESYSTEM;
+ else if (!strcmp(selection, "Swap"))
+ return PART_SWAP;
+ }
+ return PART_NONE;
+}
+
+/* If the user wants a special newfs command for this, set it */
+static void
+getNewfsCmd(PartInfo *p)
+{
+ char buffer[NEWFS_CMD_ARGS_MAX];
+ char *val;
+
+ switch (p->newfs_type) {
+ case NEWFS_UFS:
+ snprintf(buffer, NEWFS_CMD_ARGS_MAX, "%s %s %s %s",
+ NEWFS_UFS_CMD, p->newfs_data.newfs_ufs.softupdates ? "-U" : "",
+ p->newfs_data.newfs_ufs.ufs1 ? "-O1" : "-O2",
+ p->newfs_data.newfs_ufs.user_options);
+ break;
+ case NEWFS_MSDOS:
+ snprintf(buffer, NEWFS_CMD_ARGS_MAX, "%s", NEWFS_MSDOS_CMD);
+ break;
+ case NEWFS_CUSTOM:
+ strcpy(buffer, p->newfs_data.newfs_custom.command);
+ break;
+ }
+
+ val = msgGetInput(buffer,
+ "Please enter the newfs command and options you'd like to use in\n"
+ "creating this file system.");
+ if (val != NULL) {
+ p->newfs_type = NEWFS_CUSTOM;
+ strlcpy(p->newfs_data.newfs_custom.command, val, NEWFS_CMD_ARGS_MAX);
+ }
+}
+
+static void
+getNewfsOptionalArguments(PartInfo *p)
+{
+ char buffer[NEWFS_CMD_ARGS_MAX];
+ char *val;
+
+ /* Must be UFS, per argument checking in I/O routines. */
+
+ strlcpy(buffer, p->newfs_data.newfs_ufs.user_options,
+ NEWFS_CMD_ARGS_MAX);
+ val = msgGetInput(buffer,
+ "Please enter any additional UFS newfs options you'd like to\n"
+ "use in creating this file system.");
+ if (val != NULL)
+ strlcpy(p->newfs_data.newfs_ufs.user_options, val,
+ NEWFS_CMD_ARGS_MAX);
+}
+
+#define MAX_MOUNT_NAME 9
+
+#define PART_PART_COL 0
+#define PART_MOUNT_COL 10
+#define PART_SIZE_COL (PART_MOUNT_COL + MAX_MOUNT_NAME + 3)
+#define PART_NEWFS_COL (PART_SIZE_COL + 8)
+#define PART_OFF 38
+
+#define TOTAL_AVAIL_LINES (10)
+#define PSLICE_SHOWABLE (4)
+
+
+/* stick this all up on the screen */
+static void
+print_label_chunks(void)
+{
+ int i, j, srow, prow, pcol;
+ daddr_t sz;
+ char clrmsg[80];
+ int ChunkPartStartRow;
+ WINDOW *ChunkWin;
+
+ /********************************************************/
+ /*** These values are for controling screen resources ***/
+ /*** Each label line holds up to 2 labels, so beware! ***/
+ /*** strategy will be to try to always make sure the ***/
+ /*** highlighted label is in the active display area. ***/
+ /********************************************************/
+ int pslice_max, label_max;
+ int pslice_count, label_count, label_focus_found, pslice_focus_found;
+
+ attrset(A_REVERSE);
+ mvaddstr(0, 25, "FreeBSD Disklabel Editor");
+ attrset(A_NORMAL);
+
+ /*** Count the number of parition slices ***/
+ pslice_count = 0;
+ for (i = 0; label_chunk_info[i].c ; i++) {
+ if (label_chunk_info[i].type == PART_SLICE)
+ ++pslice_count;
+ }
+ pslice_max = pslice_count;
+
+ /*** 4 line max for partition slices ***/
+ if (pslice_max > PSLICE_SHOWABLE) {
+ pslice_max = PSLICE_SHOWABLE;
+ }
+ ChunkPartStartRow = CHUNK_SLICE_START_ROW + 3 + pslice_max;
+
+ /*** View partition slices modulo pslice_max ***/
+ label_max = TOTAL_AVAIL_LINES - pslice_max;
+
+ for (i = 0; i < 2; i++) {
+ mvaddstr(ChunkPartStartRow - 2, PART_PART_COL + (i * PART_OFF), "Part");
+ mvaddstr(ChunkPartStartRow - 1, PART_PART_COL + (i * PART_OFF), "----");
+
+ mvaddstr(ChunkPartStartRow - 2, PART_MOUNT_COL + (i * PART_OFF), "Mount");
+ mvaddstr(ChunkPartStartRow - 1, PART_MOUNT_COL + (i * PART_OFF), "-----");
+
+ mvaddstr(ChunkPartStartRow - 2, PART_SIZE_COL + (i * PART_OFF) + 3, "Size");
+ mvaddstr(ChunkPartStartRow - 1, PART_SIZE_COL + (i * PART_OFF) + 3, "----");
+
+ mvaddstr(ChunkPartStartRow - 2, PART_NEWFS_COL + (i * PART_OFF), "Newfs");
+ mvaddstr(ChunkPartStartRow - 1, PART_NEWFS_COL + (i * PART_OFF), "-----");
+ }
+ srow = CHUNK_SLICE_START_ROW;
+ prow = 0;
+ pcol = 0;
+
+ /*** these variables indicate that the focused item is shown currently ***/
+ label_focus_found = 0;
+ pslice_focus_found = 0;
+
+ label_count = 0;
+ pslice_count = 0;
+ mvprintw(CHUNK_SLICE_START_ROW - 1, 0, " ");
+ mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, " ");
+
+ ChunkWin = newwin(CHUNK_ROW_MAX - ChunkPartStartRow, 76, ChunkPartStartRow, 0);
+
+ wclear(ChunkWin);
+ /*** wrefresh(ChunkWin); ***/
+
+ for (i = 0; label_chunk_info[i].c; i++) {
+ /* Is it a slice entry displayed at the top? */
+ if (label_chunk_info[i].type == PART_SLICE) {
+ /*** This causes the new pslice to replace the previous display ***/
+ /*** focus must remain on the most recently active pslice ***/
+ if (pslice_count == pslice_max) {
+ if (pslice_focus_found) {
+ /*** This is where we can mark the more following ***/
+ attrset(A_BOLD);
+ mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, "***MORE***");
+ attrset(A_NORMAL);
+ continue;
+ }
+ else {
+ /*** this is where we set the more previous ***/
+ attrset(A_BOLD);
+ mvprintw(CHUNK_SLICE_START_ROW - 1, 0, "***MORE***");
+ attrset(A_NORMAL);
+ pslice_count = 0;
+ srow = CHUNK_SLICE_START_ROW;
+ }
+ }
+
+ sz = space_free(label_chunk_info[i].c);
+ if (i == here)
+ attrset(ATTR_SELECTED);
+ if (i == pslice_focus)
+ pslice_focus_found = -1;
+
+ if (label_chunk_info[i].c->type == whole) {
+ if (sz >= 100 * ONE_GIG)
+ mvprintw(srow++, 0,
+ "Disk: %s\t\tFree: %jd blocks (%jdGB)",
+ label_chunk_info[i].c->disk->name, (intmax_t)sz,
+ (intmax_t)(sz / ONE_GIG));
+ else
+ mvprintw(srow++, 0,
+ "Disk: %s\t\tFree: %jd blocks (%jdMB)",
+ label_chunk_info[i].c->disk->name, (intmax_t)sz,
+ (intmax_t)(sz / ONE_MEG));
+ } else {
+ if (sz >= 100 * ONE_GIG)
+ mvprintw(srow++, 0,
+ "Disk: %s\tPartition name: %s\tFree: %jd blocks (%jdGB)",
+ label_chunk_info[i].c->disk->name,
+ label_chunk_info[i].c->name,
+ (intmax_t)sz, (intmax_t)(sz / ONE_GIG));
+ else
+ mvprintw(srow++, 0,
+ "Disk: %s\tPartition name: %s\tFree: %jd blocks (%jdMB)",
+ label_chunk_info[i].c->disk->name,
+ label_chunk_info[i].c->name,
+ (intmax_t)sz, (intmax_t)(sz / ONE_MEG));
+ }
+ attrset(A_NORMAL);
+ clrtoeol();
+ move(0, 0);
+ /*** refresh(); ***/
+ ++pslice_count;
+ }
+ /* Otherwise it's a DOS, swap or filesystem entry in the Chunk window */
+ else {
+ char onestr[PART_OFF], num[10], *mountpoint, newfs[12];
+
+ /*
+ * We copy this into a blank-padded string so that it looks like
+ * a solid bar in reverse-video
+ */
+ memset(onestr, ' ', PART_OFF - 1);
+ onestr[PART_OFF - 1] = '\0';
+
+ /*** Track how many labels have been displayed ***/
+ if (label_count == ((label_max - 1 ) * 2)) {
+ if (label_focus_found) {
+ continue;
+ }
+ else {
+ label_count = 0;
+ prow = 0;
+ pcol = 0;
+ }
+ }
+
+ /* Go for two columns if we've written one full columns worth */
+ /*** if (prow == (CHUNK_ROW_MAX - ChunkPartStartRow)) ***/
+ if (label_count == label_max - 1) {
+ pcol = PART_OFF;
+ prow = 0;
+ }
+ memcpy(onestr + PART_PART_COL, label_chunk_info[i].c->name, strlen(label_chunk_info[i].c->name));
+ /* If it's a filesystem, display the mountpoint */
+ if (label_chunk_info[i].c->private_data && (label_chunk_info[i].type == PART_FILESYSTEM
+ || label_chunk_info[i].type == PART_FAT || label_chunk_info[i].type == PART_EFI))
+ mountpoint = ((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint;
+ else if (label_chunk_info[i].type == PART_SWAP)
+ mountpoint = "swap";
+ else
+ mountpoint = "<none>";
+
+ /* Now display the newfs field */
+ if (label_chunk_info[i].type == PART_FAT)
+ strcpy(newfs, "DOS");
+#if defined(__ia64__)
+ else if (label_chunk_info[i].type == PART_EFI) {
+ strcpy(newfs, "EFI");
+ if (label_chunk_info[i].c->private_data) {
+ strcat(newfs, " ");
+ PartInfo *pi = (PartInfo *)label_chunk_info[i].c->private_data;
+ strcat(newfs, pi->do_newfs ? " Y" : " N");
+ }
+ }
+#endif
+ else if (label_chunk_info[i].c->private_data && label_chunk_info[i].type == PART_FILESYSTEM) {
+ PartInfo *pi = (PartInfo *)label_chunk_info[i].c->private_data;
+
+ switch (pi->newfs_type) {
+ case NEWFS_UFS:
+ strcpy(newfs, NEWFS_UFS_STRING);
+ if (pi->newfs_data.newfs_ufs.ufs1)
+ strcat(newfs, "1");
+ else
+ strcat(newfs, "2");
+ if (pi->newfs_data.newfs_ufs.softupdates)
+ strcat(newfs, "+S");
+ else
+ strcat(newfs, " ");
+
+ break;
+ case NEWFS_MSDOS:
+ strcpy(newfs, "FAT");
+ break;
+ case NEWFS_CUSTOM:
+ strcpy(newfs, "CUST");
+ break;
+ }
+ strcat(newfs, pi->do_newfs ? " Y" : " N ");
+ }
+ else if (label_chunk_info[i].type == PART_SWAP)
+ strcpy(newfs, "SWAP");
+ else
+ strcpy(newfs, "*");
+ for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++)
+ onestr[PART_MOUNT_COL + j] = mountpoint[j];
+ if (label_chunk_info[i].c->size == 0)
+ snprintf(num, 10, "%5dMB", 0);
+ else if (label_chunk_info[i].c->size < (100 * ONE_GIG))
+ snprintf(num, 10, "%5jdMB",
+ (intmax_t)label_chunk_info[i].c->size / ONE_MEG);
+ else
+ snprintf(num, 10, "%5jdGB",
+ (intmax_t)label_chunk_info[i].c->size / ONE_GIG);
+ memcpy(onestr + PART_SIZE_COL, num, strlen(num));
+ memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs));
+ onestr[PART_NEWFS_COL + strlen(newfs)] = '\0';
+ if (i == label_focus) {
+ label_focus_found = -1;
+ wattrset(ChunkWin, A_BOLD);
+ }
+ if (i == here)
+ wattrset(ChunkWin, ATTR_SELECTED);
+
+ /*** lazy man's way of expensively padding this string ***/
+ while (strlen(onestr) < 37)
+ strcat(onestr, " ");
+
+ mvwaddstr(ChunkWin, prow, pcol, onestr);
+ wattrset(ChunkWin, A_NORMAL);
+ move(0, 0);
+ ++prow;
+ ++label_count;
+ }
+ }
+
+ /*** this will erase all the extra stuff ***/
+ memset(clrmsg, ' ', 37);
+ clrmsg[37] = '\0';
+
+ while (pslice_count < pslice_max) {
+ mvprintw(srow++, 0, clrmsg);
+ clrtoeol();
+ ++pslice_count;
+ }
+ while (label_count < (2 * (label_max - 1))) {
+ mvwaddstr(ChunkWin, prow++, pcol, clrmsg);
+ ++label_count;
+ if (prow == (label_max - 1)) {
+ prow = 0;
+ pcol = PART_OFF;
+ }
+ }
+ refresh();
+ wrefresh(ChunkWin);
+}
+
+static void
+print_command_summary(void)
+{
+ mvprintw(17, 0, "The following commands are valid here (upper or lower case):");
+ mvprintw(18, 0, "C = Create D = Delete M = Mount pt.");
+ mvprintw(18, 56, "W = Write");
+ mvprintw(19, 0, "N = Newfs Opts Q = Finish S = Toggle SoftUpdates Z = Custom Newfs");
+ mvprintw(20, 0, "T = Toggle Newfs U = Undo A = Auto Defaults R = Delete+Merge");
+ mvprintw(22, 0, "Use F1 or ? to get more help, arrow keys to select.");
+ move(0, 0);
+}
+
+static void
+clear_wins(void)
+{
+
+ clear();
+ print_label_chunks();
+}
+
+static int
+diskLabel(Device *dev)
+{
+ daddr_t sz;
+ int key = 0;
+ Boolean labeling;
+ char *msg = NULL;
+ PartInfo *p, *oldp;
+ PartType type;
+ Device **devs;
+ WINDOW *w = savescr();
+
+ label_focus = 0;
+ pslice_focus = 0;
+ here = 0;
+
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("No disks found!");
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+ labeling = TRUE;
+ keypad(stdscr, TRUE);
+ record_label_chunks(devs, dev);
+
+ clear();
+ while (labeling) {
+ char *cp;
+ int rflags = DELCHUNK_NORMAL;
+
+ print_label_chunks();
+ print_command_summary();
+ if (msg) {
+ attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
+ clrtoeol();
+ beep();
+ msg = NULL;
+ }
+ else {
+ move(23, 0);
+ clrtoeol();
+ }
+
+ refresh();
+ key = getch();
+ switch (toupper(key)) {
+ int i;
+ static char _msg[40];
+
+ case '\014': /* ^L */
+ clear_wins();
+ break;
+
+ case '\020': /* ^P */
+ case KEY_UP:
+ case '-':
+ if (here != 0)
+ --here;
+ else
+ while (label_chunk_info[here + 1].c)
+ ++here;
+ break;
+
+ case '\016': /* ^N */
+ case KEY_DOWN:
+ case '+':
+ case '\r':
+ case '\n':
+ if (label_chunk_info[here + 1].c)
+ ++here;
+ else
+ here = 0;
+ break;
+
+ case KEY_HOME:
+ here = 0;
+ break;
+
+ case KEY_END:
+ while (label_chunk_info[here + 1].c)
+ ++here;
+ break;
+
+ case KEY_F(1):
+ case '?':
+ systemDisplayHelp("partition");
+ clear_wins();
+ break;
+
+ case '1':
+ if (label_chunk_info[here].type == PART_FILESYSTEM) {
+ PartInfo *pi =
+ ((PartInfo *)label_chunk_info[here].c->private_data);
+
+ if ((pi != NULL) &&
+ (pi->newfs_type == NEWFS_UFS)) {
+ pi->newfs_data.newfs_ufs.ufs1 = true;
+ } else
+ msg = MSG_NOT_APPLICABLE;
+ } else
+ msg = MSG_NOT_APPLICABLE;
+ break;
+ break;
+
+ case '2':
+ if (label_chunk_info[here].type == PART_FILESYSTEM) {
+ PartInfo *pi =
+ ((PartInfo *)label_chunk_info[here].c->private_data);
+
+ if ((pi != NULL) &&
+ (pi->newfs_type == NEWFS_UFS)) {
+ pi->newfs_data.newfs_ufs.ufs1 = false;
+ } else
+ msg = MSG_NOT_APPLICABLE;
+ } else
+ msg = MSG_NOT_APPLICABLE;
+ break;
+ break;
+
+ case 'A':
+ if (label_chunk_info[here].type != PART_SLICE) {
+ msg = "You can only do this in a disk slice (at top of screen)";
+ break;
+ }
+ /*
+ * Generate standard partitions automatically. If we do not
+ * have sufficient space we attempt to scale-down the size
+ * of the partitions within certain bounds.
+ */
+ {
+ int perc;
+ int req = 0;
+
+ for (perc = 100; perc > 0; perc -= 5) {
+ req = 0; /* reset for each loop */
+ if ((msg = try_auto_label(devs, dev, perc, &req)) == NULL)
+ break;
+ }
+ if (msg) {
+ if (req) {
+ msgConfirm(msg);
+ clear_wins();
+ msg = NULL;
+ }
+ }
+ }
+ break;
+
+ case 'C':
+ if (label_chunk_info[here].type != PART_SLICE) {
+ msg = "You can only do this in a master partition (see top of screen)";
+ break;
+ }
+ sz = space_free(label_chunk_info[here].c);
+ if (sz <= FS_MIN_SIZE) {
+ msg = "Not enough space to create an additional FreeBSD partition";
+ break;
+ }
+ else {
+ char *val;
+ daddr_t size;
+ struct chunk *tmp;
+ char osize[80];
+ u_long flags = 0;
+
+#ifdef __powerpc__
+ /* Always use the maximum size for apple partitions */
+ if (label_chunk_info[here].c->type == apple)
+ size = sz;
+ else {
+#endif
+ sprintf(osize, "%jd", (intmax_t)sz);
+ val = msgGetInput(osize,
+ "Please specify the partition size in blocks or append a trailing G for\n"
+#ifdef __ia64__
+ "gigabytes, M for megabytes.\n"
+#else
+ "gigabytes, M for megabytes, or C for cylinders.\n"
+#endif
+ "%jd blocks (%jdMB) are free.",
+ (intmax_t)sz, (intmax_t)sz / ONE_MEG);
+ if (!val || (size = strtoimax(val, &cp, 0)) <= 0) {
+ clear_wins();
+ break;
+ }
+
+ if (*cp) {
+ if (toupper(*cp) == 'M')
+ size *= ONE_MEG;
+ else if (toupper(*cp) == 'G')
+ size *= ONE_GIG;
+#ifndef __ia64__
+ else if (toupper(*cp) == 'C')
+ size *= (label_chunk_info[here].c->disk->bios_hd * label_chunk_info[here].c->disk->bios_sect);
+#endif
+ }
+ if (size <= FS_MIN_SIZE) {
+ msgConfirm("The minimum filesystem size is %dMB", FS_MIN_SIZE / ONE_MEG);
+ clear_wins();
+ break;
+ }
+#ifdef __powerpc__
+ }
+#endif
+ type = get_partition_type();
+ if (type == PART_NONE) {
+ clear_wins();
+ beep();
+ break;
+ }
+
+ if (type == PART_FILESYSTEM || type == PART_EFI) {
+ if ((p = get_mountpoint(type, NULL)) == NULL) {
+ clear_wins();
+ beep();
+ break;
+ }
+ else if (!strcmp(p->mountpoint, "/")) {
+ if (type != PART_FILESYSTEM) {
+ clear_wins();
+ beep();
+ break;
+ }
+ else
+ flags |= CHUNK_IS_ROOT;
+ }
+ else
+ flags &= ~CHUNK_IS_ROOT;
+ }
+ else
+ p = NULL;
+
+ if ((flags & CHUNK_IS_ROOT) && (size < (ROOT_MIN_SIZE * ONE_MEG))) {
+ msgConfirm("Warning: This is smaller than the recommended size for a\n"
+ "root partition. For a variety of reasons, root\n"
+ "partitions should usually be at least %dMB in size", ROOT_MIN_SIZE);
+ }
+ tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, size,
+#ifdef __ia64__
+ (type == PART_EFI) ? efi : part,
+ (type == PART_EFI) ? 0 : (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
+#else
+ part, (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
+#endif
+ flags);
+ if (!tmp) {
+ msgConfirm("Unable to create the partition. Too big?");
+ clear_wins();
+ break;
+ }
+
+#ifdef __alpha__
+ /*
+ * SRM requires that the root partition is at the
+ * begining of the disk and cannot boot otherwise.
+ * Warn Alpha users if they are about to shoot themselves in
+ * the foot in this way.
+ *
+ * Since partitions may not start precisely at offset 0 we
+ * check for a "close to 0" instead. :-(
+ */
+ if ((flags & CHUNK_IS_ROOT) && (tmp->offset > 1024)) {
+ msgConfirm("Your root partition `a' does not seem to be the first\n"
+ "partition. The Alpha's firmware can only boot from the\n"
+ "first partition. So it is unlikely that your current\n"
+ "disk layout will be bootable boot after installation.\n"
+ "\n"
+ "Please allocate the root partition before allocating\n"
+ "any others.\n");
+ }
+#endif /* alpha */
+
+ tmp->private_data = p;
+ tmp->private_free = safe_free;
+ if (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ record_label_chunks(devs, dev);
+ clear_wins();
+ /* This is where we assign focus to new label so it shows. */
+ {
+ int i;
+ label_focus = -1;
+ for (i = 0; label_chunk_info[i].c; ++i) {
+ if (label_chunk_info[i].c == tmp) {
+ label_focus = i;
+ break;
+ }
+ }
+ if (label_focus == -1)
+ label_focus = i - 1;
+ }
+ }
+ break;
+
+ case KEY_DC:
+ case 'R': /* recover space (delete w/ recover) */
+ /*
+ * Delete the partition w/ space recovery.
+ */
+ rflags = DELCHUNK_RECOVER;
+ /* fall through */
+ case 'D': /* delete */
+ if (label_chunk_info[here].type == PART_SLICE) {
+ msg = MSG_NOT_APPLICABLE;
+ break;
+ }
+ else if (label_chunk_info[here].type == PART_FAT) {
+ msg = "Use the Disk Partition Editor to delete DOS partitions";
+ break;
+ }
+ Delete_Chunk2(label_chunk_info[here].c->disk, label_chunk_info[here].c, rflags);
+ if (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ record_label_chunks(devs, dev);
+ break;
+
+ case 'M': /* mount */
+ switch(label_chunk_info[here].type) {
+ case PART_SLICE:
+ msg = MSG_NOT_APPLICABLE;
+ break;
+
+ case PART_SWAP:
+ msg = "You don't need to specify a mountpoint for a swap partition.";
+ break;
+
+ case PART_FAT:
+ case PART_EFI:
+ case PART_FILESYSTEM:
+ oldp = label_chunk_info[here].c->private_data;
+ p = get_mountpoint(label_chunk_info[here].type, label_chunk_info[here].c);
+ if (p) {
+ if (!oldp)
+ p->do_newfs = FALSE;
+ if ((label_chunk_info[here].type == PART_FAT ||
+ label_chunk_info[here].type == PART_EFI) &&
+ (!strcmp(p->mountpoint, "/") ||
+ !strcmp(p->mountpoint, "/usr") ||
+ !strcmp(p->mountpoint, "/var"))) {
+ msgConfirm("%s is an invalid mount point for a DOS partition!", p->mountpoint);
+ strcpy(p->mountpoint, "/bogus");
+ }
+ }
+ if (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ record_label_chunks(devs, dev);
+ clear_wins();
+ break;
+
+ default:
+ msgFatal("Bogus partition under cursor???");
+ break;
+ }
+ break;
+
+ case 'N': /* Set newfs options */
+ if (label_chunk_info[here].c->private_data &&
+ ((PartInfo *)label_chunk_info[here].c->private_data)->do_newfs)
+ getNewfsOptionalArguments(
+ label_chunk_info[here].c->private_data);
+ else
+ msg = MSG_NOT_APPLICABLE;
+ clear_wins();
+ break;
+
+ case 'S': /* Toggle soft updates flag */
+ if (label_chunk_info[here].type == PART_FILESYSTEM) {
+ PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
+ if (pi != NULL &&
+ pi->newfs_type == NEWFS_UFS)
+ pi->newfs_data.newfs_ufs.softupdates =
+ !pi->newfs_data.newfs_ufs.softupdates;
+ else
+ msg = MSG_NOT_APPLICABLE;
+ }
+ else
+ msg = MSG_NOT_APPLICABLE;
+ break;
+
+ case 'T': /* Toggle newfs state */
+ if ((label_chunk_info[here].type == PART_FILESYSTEM ||
+ label_chunk_info[here].type == PART_EFI) &&
+ (label_chunk_info[here].c->private_data)) {
+ PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
+ if (!pi->do_newfs)
+ label_chunk_info[here].c->flags |= CHUNK_NEWFS;
+ else
+ label_chunk_info[here].c->flags &= ~CHUNK_NEWFS;
+
+ label_chunk_info[here].c->private_data =
+ new_part(label_chunk_info[here].type, pi ? pi->mountpoint : NULL, pi ? !pi->do_newfs
+ : TRUE);
+ if (pi != NULL &&
+ pi->newfs_type == NEWFS_UFS) {
+ PartInfo *pi_new = label_chunk_info[here].c->private_data;
+
+ pi_new->newfs_data.newfs_ufs = pi->newfs_data.newfs_ufs;
+ }
+ safe_free(pi);
+ label_chunk_info[here].c->private_free = safe_free;
+ if (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ }
+ else
+ msg = MSG_NOT_APPLICABLE;
+ break;
+
+ case 'U':
+ clear();
+ if (!variable_cmp(DISK_LABELLED, "written")) {
+ msgConfirm("You've already written out your changes -\n"
+ "it's too late to undo!");
+ }
+ else if (!msgNoYes("Are you SURE you want to Undo everything?")) {
+ variable_unset(DISK_PARTITIONED);
+ variable_unset(DISK_LABELLED);
+ for (i = 0; devs[i]; i++) {
+ Disk *d;
+
+ if (!devs[i]->enabled)
+ continue;
+ else if ((d = Open_Disk(devs[i]->name)) != NULL) {
+ Free_Disk(devs[i]->private);
+ devs[i]->private = d;
+#ifdef WITH_SLICES
+ diskPartition(devs[i]);
+#endif
+ }
+ }
+ record_label_chunks(devs, dev);
+ }
+ clear_wins();
+ break;
+
+ case 'W':
+ if (!variable_cmp(DISK_LABELLED, "written")) {
+ msgConfirm("You've already written out your changes - if you\n"
+ "wish to overwrite them, you'll have to restart\n"
+ "%s first.", ProgName);
+ }
+ else if (!msgNoYes("WARNING: You are about to modify an EXISTING\n"
+ "installation.\n\n"
+ "Are you absolutely sure you want to continue?")) {
+ variable_set2(DISK_LABELLED, "yes", 0);
+ diskLabelCommit(NULL);
+ }
+ clear_wins();
+ break;
+
+ case 'Z': /* Set newfs command line */
+ if (label_chunk_info[here].c->private_data &&
+ ((PartInfo *)label_chunk_info[here].c->private_data)->do_newfs)
+ getNewfsCmd(label_chunk_info[here].c->private_data);
+ else
+ msg = MSG_NOT_APPLICABLE;
+ clear_wins();
+ break;
+
+#ifndef __ia64__
+ case '|':
+ if (!msgNoYes("Are you sure you want to go into Wizard mode?\n\n"
+ "This is an entirely undocumented feature which you are not\n"
+ "expected to understand!")) {
+ int i;
+ Device **devs;
+
+ dialog_clear();
+ end_dialog();
+ DialogActive = FALSE;
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("Can't find any disk devices!");
+ break;
+ }
+ for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) {
+ if (devs[i]->enabled)
+ slice_wizard(((Disk *)devs[i]->private));
+ }
+ if (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ DialogActive = TRUE;
+ record_label_chunks(devs, dev);
+ clear_wins();
+ }
+ else
+ msg = "A most prudent choice!";
+ break;
+#endif
+
+ case '\033': /* ESC */
+ case 'Q':
+ labeling = FALSE;
+ break;
+
+ default:
+ beep();
+ sprintf(_msg, "Invalid key %d - Type F1 or ? for help", key);
+ msg = _msg;
+ break;
+ }
+ if (label_chunk_info[here].type == PART_SLICE)
+ pslice_focus = here;
+ else
+ label_focus = here;
+ }
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+static __inline daddr_t
+requested_part_size(char *varName, daddr_t nom, int def, int perc)
+{
+ char *cp;
+ daddr_t sz;
+
+ if ((cp = variable_get(varName)) != NULL)
+ sz = strtoimax(cp, NULL, 0);
+ else
+ sz = nom + (def - nom) * perc / 100;
+ return(sz * ONE_MEG);
+}
+
+/*
+ * Attempt to auto-label the disk. 'perc' (0-100) scales
+ * the size of the various partitions within appropriate
+ * bounds (NOMINAL through DEFAULT sizes). The procedure
+ * succeeds of NULL is returned. A non-null return message
+ * is either a failure-status message (*req == 0), or
+ * a confirmation requestor (*req == 1). *req is 0 on
+ * entry to this call.
+ *
+ * As a special exception to the usual sizing rules, /var is given
+ * additional space equal to the amount of physical memory present
+ * if perc == 100 in order to ensure that users with large hard drives
+ * will have enough space to store a crashdump in /var/crash.
+ *
+ * We autolabel the following partitions: /, swap, /var, /tmp, /usr,
+ * and /home. /home receives any extra left over disk space.
+ */
+static char *
+try_auto_label(Device **devs, Device *dev, int perc, int *req)
+{
+ daddr_t sz;
+ Chunk *AutoHome, *AutoRoot, *AutoSwap;
+ Chunk *AutoTmp, *AutoUsr, *AutoVar;
+#ifdef __ia64__
+ Chunk *AutoEfi;
+#endif
+ int mib[2];
+ unsigned long physmem;
+ size_t size;
+ char *msg = NULL;
+
+ sz = space_free(label_chunk_info[here].c);
+ if (sz <= FS_MIN_SIZE)
+ return("Not enough free space to create a new partition in the slice");
+
+ (void)checkLabels(FALSE);
+ AutoHome = AutoRoot = AutoSwap = NULL;
+ AutoTmp = AutoUsr = AutoVar = NULL;
+
+#ifdef __ia64__
+ AutoEfi = NULL;
+ if (EfiChunk == NULL) {
+ sz = 100 * ONE_MEG;
+ AutoEfi = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, efi, 0, 0);
+ if (AutoEfi == NULL) {
+ *req = 1;
+ msg = "Unable to create the EFI system partition. Too big?";
+ goto done;
+ }
+ AutoEfi->private_data = new_part(PART_EFI, "/efi", TRUE);
+ AutoEfi->private_free = safe_free;
+ AutoEfi->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+#endif
+
+ if (RootChunk == NULL) {
+ sz = requested_part_size(VAR_ROOT_SIZE, ROOT_NOMINAL_SIZE, ROOT_DEFAULT_SIZE, perc);
+
+ AutoRoot = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_BSDFFS, CHUNK_IS_ROOT | CHUNK_AUTO_SIZE);
+ if (!AutoRoot) {
+ *req = 1;
+ msg = "Unable to create the root partition. Too big?";
+ goto done;
+ }
+ AutoRoot->private_data = new_part(PART_FILESYSTEM, "/", TRUE);
+ AutoRoot->private_free = safe_free;
+ AutoRoot->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+ if (SwapChunk == NULL) {
+ sz = requested_part_size(VAR_SWAP_SIZE, 0, 0, perc);
+ if (sz == 0) {
+ daddr_t nom;
+ daddr_t def;
+
+ mib[0] = CTL_HW;
+ mib[1] = HW_PHYSMEM;
+ size = sizeof physmem;
+ sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
+ def = 2 * (int)(physmem / 512);
+ if (def < SWAP_MIN_SIZE * ONE_MEG)
+ def = SWAP_MIN_SIZE * ONE_MEG;
+ if (def > SWAP_AUTO_LIMIT_SIZE * ONE_MEG)
+ def = SWAP_AUTO_LIMIT_SIZE * ONE_MEG;
+ nom = (int)(physmem / 512) / 8;
+ sz = nom + (def - nom) * perc / 100;
+ }
+ AutoSwap = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_SWAP, CHUNK_AUTO_SIZE);
+ if (!AutoSwap) {
+ *req = 1;
+ msg = "Unable to create the swap partition. Too big?";
+ goto done;
+ }
+ AutoSwap->private_data = 0;
+ AutoSwap->private_free = safe_free;
+ record_label_chunks(devs, dev);
+ }
+ if (VarChunk == NULL) {
+ /* Work out how much extra space we want for a crash dump */
+ unsigned long crashdumpsz;
+
+ mib[0] = CTL_HW;
+ mib[1] = HW_PHYSMEM;
+ size = sizeof(physmem);
+ sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
+
+ if (perc == 100)
+ crashdumpsz = physmem / 1048576;
+ else
+ crashdumpsz = 0;
+
+ sz = requested_part_size(VAR_VAR_SIZE, VAR_NOMINAL_SIZE, \
+ VAR_DEFAULT_SIZE + crashdumpsz, perc);
+
+ AutoVar = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_BSDFFS, CHUNK_AUTO_SIZE);
+ if (!AutoVar) {
+ *req = 1;
+ msg = "Not enough free space for /var - you will need to\n"
+ "partition your disk manually with a custom install!";
+ goto done;
+ }
+ AutoVar->private_data = new_part(PART_FILESYSTEM, "/var", TRUE);
+ AutoVar->private_free = safe_free;
+ AutoVar->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+ if (TmpChunk == NULL && !variable_get(VAR_NO_TMP)) {
+ sz = requested_part_size(VAR_TMP_SIZE, TMP_NOMINAL_SIZE, TMP_DEFAULT_SIZE, perc);
+
+ AutoTmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_BSDFFS, CHUNK_AUTO_SIZE);
+ if (!AutoTmp) {
+ *req = 1;
+ msg = "Not enough free space for /tmp - you will need to\n"
+ "partition your disk manually with a custom install!";
+ goto done;
+ }
+ AutoTmp->private_data = new_part(PART_FILESYSTEM, "/tmp", TRUE);
+ AutoTmp->private_free = safe_free;
+ AutoTmp->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+ if (UsrChunk == NULL && !variable_get(VAR_NO_USR)) {
+ sz = requested_part_size(VAR_USR_SIZE, USR_NOMINAL_SIZE, USR_DEFAULT_SIZE, perc);
+#if AUTO_HOME == 0
+ if (sz < space_free(label_chunk_info[here].c))
+ sz = space_free(label_chunk_info[here].c);
+#endif
+ if (sz) {
+ if (sz < (USR_MIN_SIZE * ONE_MEG)) {
+ *req = 1;
+ msg = "Not enough free space for /usr - you will need to\n"
+ "partition your disk manually with a custom install!";
+ }
+
+ AutoUsr = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_BSDFFS, CHUNK_AUTO_SIZE);
+ if (!AutoUsr) {
+ msg = "Unable to create the /usr partition. Not enough space?\n"
+ "You will need to partition your disk manually with a custom install!";
+ goto done;
+ }
+ AutoUsr->private_data = new_part(PART_FILESYSTEM, "/usr", TRUE);
+ AutoUsr->private_free = safe_free;
+ AutoUsr->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+ }
+#if AUTO_HOME == 1
+ if (HomeChunk == NULL && !variable_get(VAR_NO_HOME)) {
+ sz = requested_part_size(VAR_HOME_SIZE, HOME_NOMINAL_SIZE, HOME_DEFAULT_SIZE, perc);
+ if (sz < space_free(label_chunk_info[here].c))
+ sz = space_free(label_chunk_info[here].c);
+ if (sz) {
+ if (sz < (HOME_MIN_SIZE * ONE_MEG)) {
+ *req = 1;
+ msg = "Not enough free space for /home - you will need to\n"
+ "partition your disk manually with a custom install!";
+ goto done;
+ }
+
+ AutoHome = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_BSDFFS, CHUNK_AUTO_SIZE);
+ if (!AutoHome) {
+ msg = "Unable to create the /home partition. Not enough space?\n"
+ "You will need to partition your disk manually with a custom install!";
+ goto done;
+ }
+ AutoHome->private_data = new_part(PART_FILESYSTEM, "/home", TRUE);
+ AutoHome->private_free = safe_free;
+ AutoHome->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+ }
+#endif
+
+ /* At this point, we're reasonably "labelled" */
+ if (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+
+done:
+ if (msg) {
+ if (AutoRoot != NULL)
+ Delete_Chunk(AutoRoot->disk, AutoRoot);
+ if (AutoSwap != NULL)
+ Delete_Chunk(AutoSwap->disk, AutoSwap);
+ if (AutoVar != NULL)
+ Delete_Chunk(AutoVar->disk, AutoVar);
+ if (AutoTmp != NULL)
+ Delete_Chunk(AutoTmp->disk, AutoTmp);
+ if (AutoUsr != NULL)
+ Delete_Chunk(AutoUsr->disk, AutoUsr);
+ if (AutoHome != NULL)
+ Delete_Chunk(AutoHome->disk, AutoHome);
+ record_label_chunks(devs, dev);
+ }
+ return(msg);
+}
+
+static int
+diskLabelNonInteractive(Device *dev)
+{
+ char *cp;
+ PartType type;
+ PartInfo *p;
+ u_long flags;
+ int i, status;
+ Device **devs;
+ Disk *d;
+
+ status = DITEM_SUCCESS;
+ cp = variable_get(VAR_DISK);
+ if (!cp) {
+ msgConfirm("diskLabel: No disk selected - can't label automatically.");
+ return DITEM_FAILURE;
+ }
+ devs = deviceFind(cp, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("diskLabel: No disk device %s found!", cp);
+ return DITEM_FAILURE;
+ }
+ if (dev)
+ d = dev->private;
+ else
+ d = devs[0]->private;
+ record_label_chunks(devs, dev);
+ for (i = 0; label_chunk_info[i].c; i++) {
+ Chunk *c1 = label_chunk_info[i].c;
+
+ if (label_chunk_info[i].type == PART_SLICE) {
+ char name[512];
+ char typ[10], mpoint[50];
+ int entries;
+
+ for (entries = 1;; entries++) {
+ intmax_t sz;
+ int soft = 0;
+ snprintf(name, sizeof name, "%s-%d", c1->name, entries);
+ if ((cp = variable_get(name)) == NULL)
+ break;
+ if (sscanf(cp, "%s %jd %s %d", typ, &sz, mpoint, &soft) < 3) {
+ msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
+ status = DITEM_FAILURE;
+ break;
+ } else {
+ Chunk *tmp;
+
+ flags = 0;
+ if (!strcmp(typ, "swap")) {
+ type = PART_SWAP;
+ strcpy(mpoint, "SWAP");
+ } else {
+ type = PART_FILESYSTEM;
+ if (!strcmp(mpoint, "/"))
+ flags |= CHUNK_IS_ROOT;
+ }
+ if (!sz)
+ sz = space_free(c1);
+ if (sz > space_free(c1)) {
+ msgConfirm("Not enough free space to create partition: %s", mpoint);
+ status = DITEM_FAILURE;
+ break;
+ }
+ if (!(tmp = Create_Chunk_DWIM(d, c1, sz, part,
+ (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS, flags))) {
+ msgConfirm("Unable to create from partition spec: %s. Too big?", cp);
+ status = DITEM_FAILURE;
+ break;
+ } else {
+ PartInfo *pi;
+ pi = tmp->private_data = new_part(PART_FILESYSTEM, mpoint, TRUE);
+ tmp->private_free = safe_free;
+ pi->newfs_data.newfs_ufs.softupdates = soft;
+ }
+ }
+ }
+ } else {
+ /* Must be something we can set a mountpoint for */
+ cp = variable_get(c1->name);
+ if (cp) {
+ char mpoint[50], do_newfs[8];
+ Boolean newfs = FALSE;
+
+ do_newfs[0] = '\0';
+ if (sscanf(cp, "%s %s", mpoint, do_newfs) != 2) {
+ msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
+ status = DITEM_FAILURE;
+ break;
+ }
+ newfs = toupper(do_newfs[0]) == 'Y' ? TRUE : FALSE;
+ if (c1->private_data) {
+ p = c1->private_data;
+ p->do_newfs = newfs;
+ strcpy(p->mountpoint, mpoint);
+ }
+ else {
+ c1->private_data = new_part(PART_FILESYSTEM, mpoint, newfs);
+ c1->private_free = safe_free;
+ }
+ if (!strcmp(mpoint, "/"))
+ c1->flags |= CHUNK_IS_ROOT;
+ else
+ c1->flags &= ~CHUNK_IS_ROOT;
+ }
+ }
+ }
+ if (status == DITEM_SUCCESS)
+ variable_set2(DISK_LABELLED, "yes", 0);
+ return status;
+}
diff --git a/usr.sbin/sade/list.h b/usr.sbin/sade/list.h
new file mode 100644
index 0000000..8c5cf2a
--- /dev/null
+++ b/usr.sbin/sade/list.h
@@ -0,0 +1,55 @@
+/*
+ * $FreeBSD$
+ *
+ * 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,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 PAUL TRAINA ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PAUL TRAINA OR HIS KILLER RATS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/* The structure */
+typedef struct _qelement {
+ struct _qelement *q_forw;
+ struct _qelement *q_back;
+} qelement;
+
+#define INITQUE(Xhead) { \
+ (Xhead).q_forw = &(Xhead); \
+ (Xhead).q_back = &(Xhead); \
+}
+
+#define EMPTYQUE(Xhead) \
+ ((Xhead).q_forw == &(Xhead))
+
+#define INSQUEUE(elem, pred) { \
+ register qelement *Xe = (qelement *) (elem); \
+ register qelement *Xp = (qelement *) (pred); \
+ Xp->q_forw = (Xe->q_forw = (Xe->q_back = Xp)->q_forw)->q_back = Xe; \
+}
+
+#define REMQUE(elem) { \
+ register qelement *Xe = (qelement *) (elem); \
+ (Xe->q_back->q_forw = Xe->q_forw)->q_back = Xe->q_back; \
+}
diff --git a/usr.sbin/sade/main.c b/usr.sbin/sade/main.c
new file mode 100644
index 0000000..22d9401
--- /dev/null
+++ b/usr.sbin/sade/main.c
@@ -0,0 +1,122 @@
+/*
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sade.h"
+#include <sys/signal.h>
+#include <sys/fcntl.h>
+
+const char *StartName; /* Initial contents of argv[0] */
+const char *ProgName = "sade";
+
+int
+main(int argc, char **argv)
+{
+ int choice, scroll, curr, max, status;
+
+ /* Record name to be able to restart */
+ StartName = argv[0];
+
+ signal(SIGPIPE, SIG_IGN);
+
+ /* We don't work too well when running as non-root anymore */
+ if (geteuid() != 0) {
+ fprintf(stderr, "Error: This utility should only be run as root.\n");
+ return 1;
+ }
+
+#ifdef PC98
+ {
+ /* XXX */
+ char *p = getenv("TERM");
+ if (p && strcmp(p, "cons25") == 0)
+ setenv("TERM", "cons25w", 1);
+ }
+#endif
+
+ /* Set up whatever things need setting up */
+ systemInitialize(argc, argv);
+
+ /* Set default flag and variable values */
+ installVarDefaults(NULL);
+
+ if (argc > 1 && !strcmp(argv[1], "-fake")) {
+ variable_set2(VAR_DEBUG, "YES", 0);
+ Fake = TRUE;
+ msgConfirm("I'll be just faking it from here on out, OK?");
+ }
+ if (argc > 1 && !strcmp(argv[1], "-restart"))
+ Restarting = TRUE;
+
+ /* Try to preserve our scroll-back buffer */
+ if (OnVTY) {
+ for (curr = 0; curr < 25; curr++)
+ putchar('\n');
+ }
+ /* Move stderr aside */
+ if (DebugFD)
+ dup2(DebugFD, 2);
+
+ /* Initialize driver modules, if we haven't already done so (ie,
+ the user hit Ctrl-C -> Restart. */
+ if (!pvariable_get("modulesInitialize")) {
+ pvariable_set("modulesInitialize=1");
+ }
+
+ /* Probe for all relevant devices on the system */
+ deviceGetAll();
+
+ /* First, see if we have any arguments to process (and argv[0] counts if it's not "sysinstall") */
+
+ status = setjmp(BailOut);
+ if (status) {
+ msgConfirm("A signal %d was caught - I'm saving what I can and shutting\n"
+ "down. If you can reproduce the problem, please turn Debug on\n"
+ "in the Options menu for the extra information it provides\n"
+ "in debugging problems like this.", status);
+ ;
+ }
+
+ /* Begin user dialog at outer menu */
+ dialog_clear();
+ while (1) {
+ choice = scroll = curr = max = 0;
+ dmenuOpen(&MenuMain, &choice, &scroll, &curr, &max, FALSE);
+ if (getpid() != 1
+ || !msgNoYes("Are you sure you wish to exit?")
+ )
+ break;
+ }
+
+ /* Shut down curses */
+ endwin();
+
+ return 0;
+}
diff --git a/usr.sbin/sade/menus.c b/usr.sbin/sade/menus.c
new file mode 100644
index 0000000..f48c772
--- /dev/null
+++ b/usr.sbin/sade/menus.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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
+
+#include "sade.h"
+
+/* All the system menus go here.
+ *
+ * Hardcoded things like version number strings will disappear from
+ * these menus just as soon as I add the code for doing inline variable
+ * expansion.
+ */
+
+DMenu MenuDiskDevices = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Select Drive(s)",
+ "Please select the drive, or drives, on which you wish to perform\n"
+ "this operation. If you are attempting to install a boot partition\n"
+ "on a drive other than the first one or have multiple operating\n"
+ "systems on your machine, you will have the option to install a boot\n"
+ "manager later. To select a drive, use the arrow keys to move to it\n"
+ "and press [SPACE] or [ENTER]. To de-select it, press it again.\n\n"
+ "Use [TAB] to get to the buttons and leave this menu.",
+ "Press F1 for important information regarding disk geometry!",
+ "drives",
+ { { NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0 } },
+};
+
+DMenu MenuMain = {
+ DMENU_NORMAL_TYPE,
+ "Disklabel and partitioning utility",
+ "This is a utility for partitioning and/or labelling your disks.",
+ "DISKUTIL",
+ "main",
+ {
+#ifdef WITH_SLICES
+ { "1 Partition", "Managing disk partitions", NULL, diskPartitionEditor, NULL, NULL, 0, 0, 0, 0 },
+#endif
+ { "2 Label", "Label allocated disk partitions", NULL, diskLabelEditor, NULL, NULL, 0, 0, 0, 0 },
+ { NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0 }
+ },
+};
+
+#if defined(__i386__) || defined(__amd64__)
+#ifdef PC98
+/* IPL type menu */
+DMenu MenuIPLType = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "overwrite me", /* will be disk specific label */
+ "If you want a FreeBSD Boot Manager, select \"BootMgr\". If you would\n"
+ "prefer your Boot Manager to remain untouched then select \"None\".\n\n",
+ "Press F1 to read about drive setup",
+ "drives",
+ { { "BootMgr", "Install the FreeBSD Boot Manager",
+ dmenuRadioCheck, dmenuSetValue, NULL, &BootMgr, 0, 0, 0, 0 },
+ { "None", "Leave the IPL untouched",
+ dmenuRadioCheck, dmenuSetValue, NULL, &BootMgr, '(', '*', ')', 1 },
+ { NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0 } },
+};
+#else
+/* MBR type menu */
+DMenu MenuMBRType = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "overwrite me", /* will be disk specific label */
+ "FreeBSD comes with a boot selector that allows you to easily\n"
+ "select between FreeBSD and any other operating systems on your machine\n"
+ "at boot time. If you have more than one drive and want to boot\n"
+ "from the second one, the boot selector will also make it possible\n"
+ "to do so (limitations in the PC BIOS usually prevent this otherwise).\n"
+ "If you do not want a boot selector, or wish to replace an existing\n"
+ "one, select \"standard\". If you would prefer your Master Boot\n"
+ "Record to remain untouched then select \"None\".\n\n"
+ " NOTE: PC-DOS users will almost certainly require \"None\"!",
+ "Press F1 to read about drive setup",
+ "drives",
+ { { "BootMgr", "Install the FreeBSD Boot Manager",
+ dmenuRadioCheck, dmenuSetValue, NULL, &BootMgr, 0, 0, 0, 0 },
+ { "Standard", "Install a standard MBR (no boot manager)",
+ dmenuRadioCheck, dmenuSetValue, NULL, &BootMgr, '(', '*', ')', 1 },
+ { "None", "Leave the Master Boot Record untouched",
+ dmenuRadioCheck, dmenuSetValue, NULL, &BootMgr, '(', '*', ')', 2 },
+ { NULL, NULL, NULL, NULL, NULL, NULL, 0, 0, 0, 0 } },
+};
+#endif /* PC98 */
+#endif /* __i386__ */
+
+
diff --git a/usr.sbin/sade/misc.c b/usr.sbin/sade/misc.c
new file mode 100644
index 0000000..4730c7f
--- /dev/null
+++ b/usr.sbin/sade/misc.c
@@ -0,0 +1,491 @@
+/*
+ * Miscellaneous support routines..
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sade.h"
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <ufs/ufs/ufsmount.h>
+#include <sys/reboot.h>
+#include <sys/disklabel.h>
+#include <fs/msdosfs/msdosfsmount.h>
+
+/* Quick check to see if a file is readable */
+Boolean
+file_readable(char *fname)
+{
+ if (!access(fname, F_OK))
+ return TRUE;
+ return FALSE;
+}
+
+/* Quick check to see if a file is executable */
+Boolean
+file_executable(char *fname)
+{
+ if (!access(fname, X_OK))
+ return TRUE;
+ return FALSE;
+}
+
+/* Concatenate two strings into static storage */
+char *
+string_concat(char *one, char *two)
+{
+ static char tmp[FILENAME_MAX];
+
+ /* Yes, we're deliberately cavalier about not checking for overflow */
+ strcpy(tmp, one);
+ strcat(tmp, two);
+ return tmp;
+}
+
+/* sane strncpy() function */
+char *
+sstrncpy(char *dst, const char *src, int size)
+{
+ dst[size] = '\0';
+ return strncpy(dst, src, size);
+}
+
+/* Concatenate three strings into static storage */
+char *
+string_concat3(char *one, char *two, char *three)
+{
+ static char tmp[FILENAME_MAX];
+
+ /* Yes, we're deliberately cavalier about not checking for overflow */
+ strcpy(tmp, one);
+ strcat(tmp, two);
+ strcat(tmp, three);
+ return tmp;
+}
+
+/* Clip the whitespace off the end of a string */
+char *
+string_prune(char *str)
+{
+ int len = str ? strlen(str) : 0;
+
+ while (len && isspace(str[len - 1]))
+ str[--len] = '\0';
+ return str;
+}
+
+/* run the whitespace off the front of a string */
+char *
+string_skipwhite(char *str)
+{
+ while (*str && isspace(*str))
+ ++str;
+ return str;
+}
+
+/* copy optionally and allow second arg to be null */
+char *
+string_copy(char *s1, char *s2)
+{
+ if (!s1)
+ return NULL;
+ if (!s2)
+ s1[0] = '\0';
+ else
+ strcpy(s1, s2);
+ return s1;
+}
+
+/* convert an integer to a string, using a static buffer */
+char *
+itoa(int value)
+{
+ static char buf[13];
+
+ snprintf(buf, 12, "%d", value);
+ return buf;
+}
+
+Boolean
+directory_exists(const char *dirname)
+{
+ DIR *tptr;
+
+ if (!dirname)
+ return FALSE;
+ if (!strlen(dirname))
+ return FALSE;
+
+ tptr = opendir(dirname);
+ if (!tptr)
+ return (FALSE);
+
+ closedir(tptr);
+ return (TRUE);
+}
+
+char *
+pathBaseName(const char *path)
+{
+ char *pt;
+ char *ret = (char *)path;
+
+ pt = strrchr(path,(int)'/');
+
+ if (pt != 0) /* if there is a slash */
+ {
+ ret = ++pt; /* start the file after it */
+ }
+
+ return(ret);
+}
+
+/* A free guaranteed to take NULL ptrs */
+void
+safe_free(void *ptr)
+{
+ if (ptr)
+ free(ptr);
+}
+
+/* A malloc that checks errors */
+void *
+safe_malloc(size_t size)
+{
+ void *ptr;
+
+ if (size <= 0)
+ msgFatal("Invalid malloc size of %ld!", (long)size);
+ ptr = malloc(size);
+ if (!ptr)
+ msgFatal("Out of memory!");
+ bzero(ptr, size);
+ return ptr;
+}
+
+/* A realloc that checks errors */
+void *
+safe_realloc(void *orig, size_t size)
+{
+ void *ptr;
+
+ if (size <= 0)
+ msgFatal("Invalid realloc size of %ld!", (long)size);
+ ptr = reallocf(orig, size);
+ if (!ptr)
+ msgFatal("Out of memory!");
+ return ptr;
+}
+
+/* Create a path biased from the VAR_INSTALL_ROOT variable (if not /) */
+char *
+root_bias(char *path)
+{
+ static char tmp[FILENAME_MAX];
+ char *cp = variable_get(VAR_INSTALL_ROOT);
+
+ if (!strcmp(cp, "/"))
+ return path;
+ strcpy(tmp, variable_get(VAR_INSTALL_ROOT));
+ strcat(tmp, path);
+ return tmp;
+}
+
+/*
+ * These next routines are kind of specialized just for building item lists
+ * for dialog_menu().
+ */
+
+/* Add an item to an item list */
+dialogMenuItem *
+item_add(dialogMenuItem *list, char *prompt, char *title,
+ int (*checked)(dialogMenuItem *self),
+ int (*fire)(dialogMenuItem *self),
+ void (*selected)(dialogMenuItem *self, int is_selected),
+ void *data, int *aux, int *curr, int *max)
+{
+ dialogMenuItem *d;
+
+ if (*curr == *max) {
+ *max += 20;
+ list = (dialogMenuItem *)safe_realloc(list, sizeof(dialogMenuItem) * *max);
+ }
+ d = &list[(*curr)++];
+ bzero(d, sizeof(*d));
+ d->prompt = prompt ? strdup(prompt) : NULL;
+ d->title = title ? strdup(title) : NULL;
+ d->checked = checked;
+ d->fire = fire;
+ d->selected = selected;
+ d->data = data;
+ d->aux = (long)aux;
+ return list;
+}
+
+/* Toss the items out */
+void
+items_free(dialogMenuItem *list, int *curr, int *max)
+{
+ int i;
+
+ for (i = 0; list[i].prompt; i++) {
+ safe_free(list[i].prompt);
+ safe_free(list[i].title);
+ }
+ safe_free(list);
+ *curr = *max = 0;
+}
+
+int
+Mkdir(char *ipath)
+{
+ struct stat sb;
+ int final;
+ char *p, *path;
+
+ if (file_readable(ipath) || Fake)
+ return DITEM_SUCCESS;
+
+ path = strcpy(alloca(strlen(ipath) + 1), ipath);
+ if (isDebug())
+ msgDebug("mkdir(%s)\n", path);
+ p = path;
+ if (p[0] == '/') /* Skip leading '/'. */
+ ++p;
+ for (final = FALSE; !final; ++p) {
+ if (p[0] == '\0' || (p[0] == '/' && p[1] == '\0'))
+ final = TRUE;
+ else if (p[0] != '/')
+ continue;
+ *p = '\0';
+ if (stat(path, &sb)) {
+ if (errno != ENOENT) {
+ msgConfirm("Couldn't stat directory %s: %s", path, strerror(errno));
+ return DITEM_FAILURE;
+ }
+ if (isDebug())
+ msgDebug("mkdir(%s..)\n", path);
+ if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
+ msgConfirm("Couldn't create directory %s: %s", path,strerror(errno));
+ return DITEM_FAILURE;
+ }
+ }
+ *p = '/';
+ }
+ return DITEM_SUCCESS;
+}
+
+int
+Mkdir_command(char *key, void *dir)
+{
+ return (Mkdir((char*)dir));
+}
+
+int
+Mount(char *mountp, void *dev)
+{
+ struct ufs_args ufsargs;
+ char device[80];
+ char mountpoint[FILENAME_MAX];
+
+ if (Fake)
+ return DITEM_SUCCESS;
+
+ if (*((char *)dev) != '/') {
+ sprintf(device, "/dev/%s", (char *)dev);
+ sprintf(mountpoint, "%s", mountp);
+ }
+ else {
+ strcpy(device, dev);
+ strcpy(mountpoint, mountp);
+ }
+ memset(&ufsargs,0,sizeof ufsargs);
+
+ if (Mkdir(mountpoint)) {
+ msgConfirm("Unable to make directory mountpoint for %s!", mountpoint);
+ return DITEM_FAILURE;
+ }
+ if (isDebug())
+ msgDebug("mount %s %s\n", device, mountpoint);
+
+ ufsargs.fspec = device;
+ if (mount("ufs", mountpoint, 0,
+ (caddr_t)&ufsargs) == -1) {
+ msgConfirm("Error mounting %s on %s : %s", device, mountpoint, strerror(errno));
+ return DITEM_FAILURE;
+ }
+ return DITEM_SUCCESS;
+}
+
+WINDOW *
+openLayoutDialog(char *helpfile, char *title, int x, int y, int width, int height)
+{
+ WINDOW *win;
+ static char help[FILENAME_MAX];
+
+ /* We need a curses window */
+ win = newwin(LINES, COLS, 0, 0);
+ if (win) {
+ /* Say where our help comes from */
+ if (helpfile) {
+ use_helpline("Press F1 for more information on this screen.");
+ use_helpfile(systemHelpFile(helpfile, help));
+ }
+ /* Setup a nice screen for us to splat stuff onto */
+ draw_box(win, y, x, height, width, dialog_attr, border_attr);
+ wattrset(win, dialog_attr);
+ mvwaddstr(win, y, x + (COLS - strlen(title)) / 2, title);
+ }
+ return win;
+}
+
+ComposeObj *
+initLayoutDialog(WINDOW *win, Layout *layout, int x, int y, int *max)
+{
+ ComposeObj *obj = NULL, *first;
+ int n;
+
+ /* Loop over the layout list, create the objects, and add them
+ onto the chain of objects that dialog uses for traversal*/
+
+ n = 0;
+ while (layout[n].help != NULL) {
+ int t = TYPE_OF_OBJ(layout[n].type);
+
+ switch (t) {
+ case STRINGOBJ:
+ layout[n].obj = NewStringObj(win, layout[n].prompt, layout[n].var,
+ layout[n].y + y, layout[n].x + x, layout[n].len, layout[n].maxlen);
+ ((StringObj *)layout[n].obj)->attr_mask = ATTR_OF_OBJ(layout[n].type);
+ break;
+
+ case BUTTONOBJ:
+ layout[n].obj = NewButtonObj(win, layout[n].prompt, layout[n].var, layout[n].y + y, layout[n].x + x);
+ break;
+
+ default:
+ msgFatal("Don't support this object yet!");
+ }
+ AddObj(&obj, t, (void *) layout[n].obj);
+ n++;
+ }
+ *max = n - 1;
+ /* Find the first object in the list */
+ for (first = obj; first->prev; first = first->prev);
+ return first;
+}
+
+int
+layoutDialogLoop(WINDOW *win, Layout *layout, ComposeObj **obj, int *n, int max, int *cbutton, int *cancel)
+{
+ char help_line[80];
+ int ret, i, len = strlen(layout[*n].help);
+
+ /* Display the help line at the bottom of the screen */
+ for (i = 0; i < 79; i++)
+ help_line[i] = (i < len) ? layout[*n].help[i] : ' ';
+ help_line[i] = '\0';
+ use_helpline(help_line);
+ display_helpline(win, LINES - 1, COLS - 1);
+ wrefresh(win);
+
+ /* Ask for libdialog to do its stuff */
+ ret = PollObj(obj);
+ /* Handle special case stuff that libdialog misses. Sigh */
+ switch (ret) {
+ case SEL_ESC: /* Bail out */
+ *cancel = TRUE;
+ return FALSE;
+
+ /* This doesn't work for list dialogs. Oh well. Perhaps
+ should special case the move from the OK button ``up''
+ to make it go to the interface list, but then it gets
+ awkward for the user to go back and correct screw up's
+ in the per-interface section */
+ case KEY_DOWN:
+ case SEL_CR:
+ case SEL_TAB:
+ if (*n < max)
+ ++*n;
+ else
+ *n = 0;
+ break;
+
+ /* The user has pressed enter over a button object */
+ case SEL_BUTTON:
+ if (cbutton && *cbutton)
+ *cancel = TRUE;
+ else
+ *cancel = FALSE;
+ return FALSE;
+
+ case KEY_UP:
+ case SEL_BACKTAB:
+ if (*n)
+ --*n;
+ else
+ *n = max;
+ break;
+
+ case KEY_F(1):
+ display_helpfile();
+
+ /* They tried some key combination we don't support - tootle them forcefully! */
+ default:
+ beep();
+ }
+ return TRUE;
+}
+
+WINDOW *
+savescr(void)
+{
+ WINDOW *w;
+
+ w = dupwin(newscr);
+ return w;
+}
+
+void
+restorescr(WINDOW *w)
+{
+ touchwin(w);
+ wrefresh(w);
+ delwin(w);
+}
+
diff --git a/usr.sbin/sade/msg.c b/usr.sbin/sade/msg.c
new file mode 100644
index 0000000..4003b20
--- /dev/null
+++ b/usr.sbin/sade/msg.c
@@ -0,0 +1,350 @@
+/*
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sade.h"
+#include <stdarg.h>
+#include <sys/ioctl.h>
+#include <sys/consio.h>
+
+Boolean
+isDebug(void)
+{
+ char *cp;
+
+ return (cp = variable_get(VAR_DEBUG)) && strcmp(cp, "no");
+}
+
+/* Whack up an informational message on the status line, in stand-out */
+void
+msgYap(const char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int attrs;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ attrs = getattrs(stdscr);
+ attrset(A_REVERSE);
+ mvaddstr(StatusLine, 0, errstr);
+ attrset(attrs);
+ refresh();
+}
+
+/* Whack up an informational message on the status line */
+void
+msgInfo(const char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int i, attrs;
+ char line[81];
+
+ attrs = getattrs(stdscr);
+ /* NULL is a special convention meaning "erase the old stuff" */
+ if (!fmt) {
+ move(StatusLine, 0);
+ clrtoeol();
+ return;
+ }
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ memset(line, ' ', 80);
+ for (i = 0; i < 80; i++) {
+ if (errstr[i])
+ line[i] = errstr[i];
+ else
+ break;
+ }
+ line[80] = '\0';
+ attrset(ATTR_TITLE);
+ mvaddstr(StatusLine, 0, line);
+ attrset(attrs);
+ move(StatusLine, 79);
+ refresh();
+}
+
+/* Whack up a warning on the status line */
+void
+msgWarn(const char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int attrs;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ strcpy(errstr, "Warning: ");
+ va_start(args, fmt);
+ vsnprintf((char *)(errstr + strlen(errstr)), FILENAME_MAX, fmt, args);
+ va_end(args);
+ attrs = getattrs(stdscr);
+ beep();
+ attrset(ATTR_TITLE);
+ mvaddstr(StatusLine, 0, errstr);
+ attrset(attrs);
+ refresh();
+ if (OnVTY && isDebug())
+ msgDebug("Warning message `%s'\n", errstr);
+}
+
+/* Whack up an error on the status line */
+void
+msgError(const char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int attrs;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ strcpy(errstr, "Error: ");
+ va_start(args, fmt);
+ vsnprintf((char *)(errstr + strlen(errstr)), FILENAME_MAX, fmt, args);
+ va_end(args);
+ beep();
+ attrs = getattrs(stdscr);
+ attrset(ATTR_TITLE);
+ mvaddstr(StatusLine, 0, errstr);
+ attrset(attrs);
+ refresh();
+ if (OnVTY && isDebug())
+ msgDebug("Error message `%s'\n", errstr);
+}
+
+/* Whack up a fatal error on the status line */
+void
+msgFatal(const char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int attrs;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ strcpy(errstr, "Fatal Error: ");
+ va_start(args, fmt);
+ vsnprintf((char *)(errstr + strlen(errstr)), FILENAME_MAX, fmt, args);
+ va_end(args);
+ beep();
+ attrs = getattrs(stdscr);
+ attrset(ATTR_TITLE);
+ mvaddstr(StatusLine, 0, errstr);
+ addstr(" - ");
+ addstr("PRESS ANY KEY TO ");
+ if (getpid() == 1)
+ addstr("REBOOT");
+ else
+ addstr("QUIT");
+ attrset(attrs);
+ refresh();
+ if (OnVTY)
+ msgDebug("Fatal error `%s'!\n", errstr);
+ getch();
+}
+
+/* Put up a message in a popup confirmation box */
+void
+msgConfirm(const char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ WINDOW *w = savescr();
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (OnVTY) {
+ ioctl(0, VT_ACTIVATE, 1);
+ msgInfo(NULL);
+ }
+ dialog_notify(errstr);
+ restorescr(w);
+}
+
+/* Put up a message in a popup information box */
+void
+msgNotify(const char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (isDebug())
+ msgDebug("Notify: %s\n", errstr);
+ dialog_msgbox(NULL, errstr, -1, -1, 0);
+}
+
+/* Put up a message in a popup yes/no box and return 0 for YES, 1 for NO */
+int
+msgYesNo(const char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int ret;
+ WINDOW *w = savescr();
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (OnVTY) {
+ ioctl(0, VT_ACTIVATE, 1); /* Switch back */
+ msgInfo(NULL);
+ }
+ if (variable_get(VAR_NONINTERACTIVE))
+ return 0; /* If non-interactive, return YES all the time */
+ ret = dialog_yesno("User Confirmation Requested", errstr, -1, -1);
+ restorescr(w);
+ return ret;
+}
+
+/* Put up a message in a popup no/yes box and return 0 for YES, 1 for NO */
+int
+msgNoYes(const char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int ret;
+ WINDOW *w = savescr();
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (OnVTY) {
+ ioctl(0, VT_ACTIVATE, 1); /* Switch back */
+ msgInfo(NULL);
+ }
+ if (variable_get(VAR_NONINTERACTIVE))
+ return 1; /* If non-interactive, return NO all the time */
+ ret = dialog_noyes("User Confirmation Requested", errstr, -1, -1);
+ restorescr(w);
+ return ret;
+}
+
+/* Put up a message in an input box and return the value */
+char *
+msgGetInput(char *buf, const char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ static char input_buffer[256];
+ int rval;
+ WINDOW *w = savescr();
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (buf)
+ SAFE_STRCPY(input_buffer, buf);
+ else
+ input_buffer[0] = '\0';
+ if (OnVTY) {
+ ioctl(0, VT_ACTIVATE, 1); /* Switch back */
+ msgInfo(NULL);
+ }
+ rval = dialog_inputbox("Value Required", errstr, -1, -1, input_buffer);
+ restorescr(w);
+ if (!rval)
+ return input_buffer;
+ else
+ return NULL;
+}
+
+/* Write something to the debugging port */
+void
+msgDebug(const char *fmt, ...)
+{
+ va_list args;
+ char *dbg;
+
+ if (DebugFD == -1)
+ return;
+ dbg = (char *)alloca(FILENAME_MAX);
+ strcpy(dbg, "DEBUG: ");
+ va_start(args, fmt);
+ vsnprintf((char *)(dbg + strlen(dbg)), FILENAME_MAX, fmt, args);
+ va_end(args);
+ write(DebugFD, dbg, strlen(dbg));
+}
+
+/* Tell the user there's some output to go look at */
+void
+msgWeHaveOutput(const char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ WINDOW *w = savescr();
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ msgDebug("Notify: %s\n", errstr);
+ dialog_clear_norefresh();
+ sleep(2);
+ dialog_msgbox(NULL, errstr, -1, -1, 0);
+ restorescr(w);
+}
+
+/* Simple versions of msgConfirm() and msgNotify() for calling from scripts */
+int
+msgSimpleConfirm(const char *str)
+{
+ msgConfirm("%s", str);
+ return DITEM_SUCCESS;
+}
+
+int
+msgSimpleNotify(const char *str)
+{
+ msgNotify("%s", str);
+ return DITEM_SUCCESS;
+}
diff --git a/usr.sbin/sade/sade.8 b/usr.sbin/sade/sade.8
new file mode 100644
index 0000000..8acda90
--- /dev/null
+++ b/usr.sbin/sade/sade.8
@@ -0,0 +1,73 @@
+.\" Copyright (c) 1997
+.\" Jordan Hubbard <jkh@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 Jordan Hubbard AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL Jordan Hubbard OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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 8, 2006
+.Dt SADE 8
+.Os
+.Sh NAME
+.Nm sade
+.Nd sysadmins disk editor
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+utility is used for various disk administration tasks on
+.Fx
+systems.
+.Pp
+It is generally invoked without arguments for the default
+behavior, where the main menu is presented.
+.Sh NOTES
+The
+.Nm
+utility aims to provide a handy tool for disk management
+tasks on an already installed system.
+The goal is to save
+some of the useful functionality of the old
+.Xr sysinstall 8
+which
+will be removed from the system in favor of the new installer.
+.Sh SEE ALSO
+.Xr sysinstall 8
+.Sh HISTORY
+This version of
+.Nm
+first appeared in
+.Fx 6.3 .
+The code is extracted from the
+.Xr sysinstall 8
+utility.
+.Sh AUTHORS
+.An Jordan K. Hubbard Aq jkh@FreeBSD.org
+.Sh BUGS
+The utility misses a lot of nice features, such as tools for
+manipulating
+.Xr gmirror 8
+or
+.Xr gvinum 8
+stuff.
+These will be added later.
diff --git a/usr.sbin/sade/sade.h b/usr.sbin/sade/sade.h
new file mode 100644
index 0000000..4bf7e1b
--- /dev/null
+++ b/usr.sbin/sade/sade.h
@@ -0,0 +1,487 @@
+/*
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 _SADE_H_INCLUDE
+#define _SADE_H_INCLUDE
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <dialog.h>
+#include "ui_objects.h"
+#include "dir.h"
+#include "colors.h"
+
+/*** Defines ***/
+
+#if defined(__i386__) || defined(__amd64__)
+#define WITH_SYSCONS
+#define WITH_MICE
+#endif
+
+#if defined(__i386__) || defined(__amd64__)
+#define WITH_SLICES
+#endif
+
+#if defined(__i386__)
+#define WITH_LINUX
+#endif
+
+/* device limits */
+#define DEV_NAME_MAX 128 /* The maximum length of a device name */
+#define DEV_MAX 100 /* The maximum number of devices we'll deal with */
+#define IO_ERROR -2 /* Status code for I/O error rather than normal EOF */
+
+/*
+ * I make some pretty gross assumptions about having a max of 50 chunks
+ * total - 8 slices and 42 partitions. I can't easily display many more
+ * than that on the screen at once!
+ *
+ * For 2.1 I'll revisit this and try to make it more dynamic, but since
+ * this will catch 99.99% of all possible cases, I'm not too worried.
+ */
+#define MAX_CHUNKS 40
+
+/* Internal environment variable names */
+#define DISK_PARTITIONED "_diskPartitioned"
+#define DISK_LABELLED "_diskLabelled"
+#define DISK_SELECTED "_diskSelected"
+#define SYSTEM_STATE "_systemState"
+#define RUNNING_ON_ROOT "_runningOnRoot"
+
+/* Ones that can be tweaked from config files */
+#define VAR_BLANKTIME "blanktime"
+#define VAR_BOOTMGR "bootManager"
+#define VAR_DEBUG "debug"
+#define VAR_DISK "disk"
+#define VAR_DISKINTERACTIVE "diskInteractive"
+#define VAR_DEDICATE_DISK "dedicateDisk"
+#define VAR_COMMAND "command"
+#define VAR_CONFIG_FILE "configFile"
+#define VAR_GEOMETRY "geometry"
+#define VAR_INSTALL_CFG "installConfig"
+#define VAR_INSTALL_ROOT "installRoot"
+#define VAR_LABEL "label"
+#define VAR_LABEL_COUNT "labelCount"
+#define VAR_NEWFS_ARGS "newfsArgs"
+#define VAR_NO_CONFIRM "noConfirm"
+#define VAR_NO_ERROR "noError"
+#define VAR_NO_WARN "noWarn"
+#define VAR_NO_USR "noUsr"
+#define VAR_NO_TMP "noTmp"
+#define VAR_NO_HOME "noHome"
+#define VAR_NONINTERACTIVE "nonInteractive"
+#define VAR_PARTITION "partition"
+#define VAR_RELNAME "releaseName"
+#define VAR_ROOT_SIZE "rootSize"
+#define VAR_SWAP_SIZE "swapSize"
+#define VAR_TAPE_BLOCKSIZE "tapeBlocksize"
+#define VAR_UFS_PATH "ufs"
+#define VAR_USR_SIZE "usrSize"
+#define VAR_VAR_SIZE "varSize"
+#define VAR_TMP_SIZE "tmpSize"
+#define VAR_TERM "TERM"
+#define VAR_CONSTERM "_consterm"
+
+#define DEFAULT_TAPE_BLOCKSIZE "20"
+
+/* One MB worth of blocks */
+#define ONE_MEG 2048
+#define ONE_GIG (ONE_MEG * 1024)
+
+/* Which selection attributes to use */
+#define ATTR_SELECTED (ColorDisplay ? item_selected_attr : item_attr)
+#define ATTR_TITLE button_active_attr
+
+/* Handy strncpy() macro */
+#define SAFE_STRCPY(to, from) sstrncpy((to), (from), sizeof (to) - 1)
+
+/*** Types ***/
+typedef int Boolean;
+typedef struct disk Disk;
+typedef struct chunk Chunk;
+
+/* Bitfields for menu options */
+#define DMENU_NORMAL_TYPE 0x1 /* Normal dialog menu */
+#define DMENU_RADIO_TYPE 0x2 /* Radio dialog menu */
+#define DMENU_CHECKLIST_TYPE 0x4 /* Multiple choice menu */
+#define DMENU_SELECTION_RETURNS 0x8 /* Immediate return on item selection */
+
+typedef struct _dmenu {
+ int type; /* What sort of menu we are */
+ char *title; /* Our title */
+ char *prompt; /* Our prompt */
+ char *helpline; /* Line of help at bottom */
+ char *helpfile; /* Help file for "F1" */
+ dialogMenuItem items[]; /* Array of menu items */
+} DMenu;
+
+/* An rc.conf variable */
+typedef struct _variable {
+ struct _variable *next;
+ char *name;
+ char *value;
+ int dirty;
+} Variable;
+
+#define NO_ECHO_OBJ(type) ((type) | (DITEM_NO_ECHO << 16))
+#define TYPE_OF_OBJ(type) ((type) & 0xff)
+#define ATTR_OF_OBJ(type) ((type) >> 16)
+
+/* A screen layout structure */
+typedef struct _layout {
+ int y; /* x & Y co-ordinates */
+ int x;
+ int len; /* The size of the dialog on the screen */
+ int maxlen; /* How much the user can type in ... */
+ char *prompt; /* The string for the prompt */
+ char *help; /* The display for the help line */
+ void *var; /* The var to set when this changes */
+ int type; /* The type of the dialog to create */
+ void *obj; /* The obj pointer returned by libdialog */
+} Layout;
+
+typedef enum {
+ DEVICE_TYPE_NONE,
+ DEVICE_TYPE_DISK,
+ DEVICE_TYPE_DOS,
+ DEVICE_TYPE_UFS,
+ DEVICE_TYPE_ANY,
+} DeviceType;
+
+/* A "device" from sade's point of view */
+typedef struct _device {
+ char name[DEV_NAME_MAX];
+ char *description;
+ char *devname;
+ DeviceType type;
+ Boolean enabled;
+ Boolean (*init)(struct _device *dev);
+ FILE * (*get)(struct _device *dev, char *file, Boolean probe);
+ void (*shutdown)(struct _device *dev);
+ void *private;
+ unsigned int flags;
+ unsigned int volume;
+} Device;
+
+/* Some internal representations of partitions */
+typedef enum {
+ PART_NONE,
+ PART_SLICE,
+ PART_SWAP,
+ PART_FILESYSTEM,
+ PART_FAT,
+ PART_EFI
+} PartType;
+
+#define NEWFS_UFS_CMD "newfs"
+#define NEWFS_MSDOS_CMD "newfs_msdos"
+
+enum newfs_type { NEWFS_UFS, NEWFS_MSDOS, NEWFS_CUSTOM };
+#define NEWFS_UFS_STRING "UFS"
+#define NEWFS_MSDOS_STRING "FAT"
+#define NEWFS_CUSTOM_STRING "CST"
+
+/* The longest set of custom command line arguments we'll pass. */
+#define NEWFS_CMD_ARGS_MAX 256
+
+typedef struct _part_info {
+ char mountpoint[FILENAME_MAX];
+
+ /* Is invocation of newfs desired? */
+ Boolean do_newfs;
+
+ enum newfs_type newfs_type;
+ union {
+ struct {
+ char user_options[NEWFS_CMD_ARGS_MAX];
+ Boolean acls; /* unused */
+ Boolean multilabel; /* unused */
+ Boolean softupdates;
+ Boolean ufs1;
+ } newfs_ufs;
+ struct {
+ /* unused */
+ } newfs_msdos;
+ struct {
+ char command[NEWFS_CMD_ARGS_MAX];
+ } newfs_custom;
+ } newfs_data;
+} PartInfo;
+
+/* An option */
+typedef struct _opt {
+ char *name;
+ char *desc;
+ enum { OPT_IS_STRING, OPT_IS_INT, OPT_IS_FUNC, OPT_IS_VAR } type;
+ void *data;
+ void *aux;
+ char *(*check)(void);
+} Option;
+
+typedef int (*commandFunc)(char *key, void *data);
+
+#define EXTRAS_FIELD_LEN 128
+
+/*** Externs ***/
+extern jmp_buf BailOut; /* Used to get the heck out */
+extern int DebugFD; /* Where diagnostic output goes */
+extern Boolean Fake; /* Don't actually modify anything - testing */
+extern Boolean Restarting; /* Are we restarting sysinstall? */
+extern Boolean SystemWasInstalled; /* Did we install it? */
+extern Boolean RunningAsInit; /* Are we running stand-alone? */
+extern Boolean DialogActive; /* Is the dialog() stuff up? */
+extern Boolean ColorDisplay; /* Are we on a color display? */
+extern Boolean OnVTY; /* On a syscons VTY? */
+extern Variable *VarHead; /* The head of the variable chain */
+extern int BootMgr; /* Which boot manager to use */
+extern int StatusLine; /* Where to print our status messages */
+#if defined(__i386__) || defined(__amd64__)
+#ifdef PC98
+extern DMenu MenuIPLType; /* Type of IPL to write on the disk */
+#else
+extern DMenu MenuMBRType; /* Type of MBR to write on the disk */
+#endif
+#endif
+extern DMenu MenuMain; /* New main menu */
+extern DMenu MenuDiskDevices; /* Disk type devices */
+extern const char * StartName; /* Which name we were started as */
+extern const char * ProgName; /* Program's proper name */
+
+/* Important chunks. */
+extern Chunk *HomeChunk;
+extern Chunk *RootChunk;
+extern Chunk *SwapChunk;
+extern Chunk *TmpChunk;
+extern Chunk *UsrChunk;
+extern Chunk *VarChunk;
+#ifdef __ia64__
+extern Chunk *EfiChunk;
+#endif
+
+/* Stuff from libdialog which isn't properly declared outside */
+extern void display_helpfile(void);
+extern void display_helpline(WINDOW *w, int y, int width);
+
+/*** Prototypes ***/
+
+/* command.c */
+extern void command_clear(void);
+extern void command_sort(void);
+extern void command_execute(void);
+extern void command_shell_add(char *key, const char *fmt, ...) __printflike(2, 3);
+extern void command_func_add(char *key, commandFunc func, void *data);
+
+/* config.c */
+extern void configEnvironmentRC_conf(void);
+extern void configRC_conf(void);
+extern int configFstab(dialogMenuItem *self);
+extern int configRC(dialogMenuItem *self);
+extern int configWriteRC_conf(dialogMenuItem *self);
+
+/* devices.c */
+extern DMenu *deviceCreateMenu(DMenu *menu, DeviceType type, int (*hook)(dialogMenuItem *d),
+ int (*check)(dialogMenuItem *d));
+extern void deviceGetAll(void);
+extern void deviceReset(void);
+extern void deviceRescan(void);
+extern Device **deviceFind(char *name, DeviceType type);
+extern Device **deviceFindDescr(char *name, char *desc, DeviceType class);
+extern int deviceCount(Device **devs);
+extern Device *new_device(char *name);
+extern Device *deviceRegister(char *name, char *desc, char *devicename, DeviceType type, Boolean enabled,
+ Boolean (*init)(Device *mediadev),
+ FILE * (*get)(Device *dev, char *file, Boolean probe),
+ void (*shutDown)(Device *mediadev),
+ void *private);
+extern Boolean dummyInit(Device *dev);
+extern FILE *dummyGet(Device *dev, char *dist, Boolean probe);
+extern void dummyShutdown(Device *dev);
+
+/* disks.c */
+#ifdef WITH_SLICES
+extern void diskPartition(Device *dev);
+extern int diskPartitionEditor(dialogMenuItem *self);
+#endif
+extern int diskPartitionWrite(dialogMenuItem *self);
+extern int diskGetSelectCount(Device ***devs);
+
+/* dispatch.c */
+extern int dispatchCommand(char *command);
+extern int dispatch_load_floppy(dialogMenuItem *self);
+extern int dispatch_load_file_int(int);
+extern int dispatch_load_file(dialogMenuItem *self);
+
+/* dmenu.c */
+extern int dmenuDisplayFile(dialogMenuItem *tmp);
+extern int dmenuSubmenu(dialogMenuItem *tmp);
+extern int dmenuSystemCommand(dialogMenuItem *tmp);
+extern int dmenuSystemCommandBox(dialogMenuItem *tmp);
+extern int dmenuExit(dialogMenuItem *tmp);
+extern int dmenuISetVariable(dialogMenuItem *tmp);
+extern int dmenuSetVariable(dialogMenuItem *tmp);
+extern int dmenuSetVariables(dialogMenuItem *tmp);
+extern int dmenuToggleVariable(dialogMenuItem *tmp);
+extern int dmenuSetFlag(dialogMenuItem *tmp);
+extern int dmenuSetValue(dialogMenuItem *tmp);
+extern Boolean dmenuOpen(DMenu *menu, int *choice, int *bscroll, int *curr, int *max, Boolean buttons);
+extern Boolean dmenuOpenSimple(DMenu *menu, Boolean buttons);
+extern int dmenuVarCheck(dialogMenuItem *item);
+extern int dmenuVarsCheck(dialogMenuItem *item);
+extern int dmenuFlagCheck(dialogMenuItem *item);
+extern int dmenuRadioCheck(dialogMenuItem *item);
+
+/* dos.c */
+extern Boolean mediaCloseDOS(Device *dev, FILE *fp);
+extern Boolean mediaInitDOS(Device *dev);
+extern FILE *mediaGetDOS(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownDOS(Device *dev);
+
+/* globals.c */
+extern void globalsInit(void);
+
+/* install.c */
+extern Boolean checkLabels(Boolean whinge);
+extern int installCommit(dialogMenuItem *self);
+extern int installCustomCommit(dialogMenuItem *self);
+extern int installFilesystems(dialogMenuItem *self);
+extern int installVarDefaults(dialogMenuItem *self);
+extern void installEnvironment(void);
+extern Boolean copySelf(void);
+
+/* kget.c */
+extern int kget(char *out);
+
+/* label.c */
+extern int diskLabelEditor(dialogMenuItem *self);
+extern int diskLabelCommit(dialogMenuItem *self);
+
+/* misc.c */
+extern Boolean file_readable(char *fname);
+extern Boolean file_executable(char *fname);
+extern Boolean directory_exists(const char *dirname);
+extern char *root_bias(char *path);
+extern char *itoa(int value);
+extern char *string_concat(char *p1, char *p2);
+extern char *string_concat3(char *p1, char *p2, char *p3);
+extern char *string_prune(char *str);
+extern char *string_skipwhite(char *str);
+extern char *string_copy(char *s1, char *s2);
+extern char *pathBaseName(const char *path);
+extern void safe_free(void *ptr);
+extern void *safe_malloc(size_t size);
+extern void *safe_realloc(void *orig, size_t size);
+extern dialogMenuItem *item_add(dialogMenuItem *list, char *prompt, char *title,
+ int (*checked)(dialogMenuItem *self),
+ int (*fire)(dialogMenuItem *self),
+ void (*selected)(dialogMenuItem *self, int is_selected),
+ void *data, int *aux, int *curr, int *max);
+extern void items_free(dialogMenuItem *list, int *curr, int *max);
+extern int Mkdir(char *);
+extern int Mkdir_command(char *key, void *data);
+extern int Mount(char *, void *data);
+extern WINDOW *openLayoutDialog(char *helpfile, char *title, int x, int y, int width, int height);
+extern ComposeObj *initLayoutDialog(WINDOW *win, Layout *layout, int x, int y, int *max);
+extern int layoutDialogLoop(WINDOW *win, Layout *layout, ComposeObj **obj,
+ int *n, int max, int *cbutton, int *cancel);
+
+extern WINDOW *savescr(void);
+extern void restorescr(WINDOW *w);
+extern char *sstrncpy(char *dst, const char *src, int size);
+
+/* msg.c */
+extern Boolean isDebug(void);
+extern void msgInfo(const char *fmt, ...) __printf0like(1, 2);
+extern void msgYap(const char *fmt, ...) __printflike(1, 2);
+extern void msgWarn(const char *fmt, ...) __printflike(1, 2);
+extern void msgDebug(const char *fmt, ...) __printflike(1, 2);
+extern void msgError(const char *fmt, ...) __printflike(1, 2);
+extern void msgFatal(const char *fmt, ...) __printflike(1, 2);
+extern void msgConfirm(const char *fmt, ...) __printflike(1, 2);
+extern void msgNotify(const char *fmt, ...) __printflike(1, 2);
+extern void msgWeHaveOutput(const char *fmt, ...) __printflike(1, 2);
+extern int msgYesNo(const char *fmt, ...) __printflike(1, 2);
+extern int msgNoYes(const char *fmt, ...) __printflike(1, 2);
+extern char *msgGetInput(char *buf, const char *fmt, ...) __printflike(2, 3);
+extern int msgSimpleConfirm(const char *);
+extern int msgSimpleNotify(const char *);
+
+/* pccard.c */
+extern void pccardInitialize(void);
+
+/* system.c */
+extern void systemInitialize(int argc, char **argv);
+extern void systemShutdown(int status);
+extern int execExecute(char *cmd, char *name);
+extern int systemExecute(char *cmd);
+extern void systemSuspendDialog(void);
+extern void systemResumeDialog(void);
+extern int systemDisplayHelp(char *file);
+extern char *systemHelpFile(char *file, char *buf);
+extern void systemChangeFont(const u_char font[]);
+extern void systemChangeLang(char *lang);
+extern void systemChangeTerminal(char *color, const u_char c_termcap[], char *mono, const u_char m_termcap[]);
+extern void systemChangeScreenmap(const u_char newmap[]);
+extern int vsystem(const char *fmt, ...) __printflike(1, 2);
+
+/* termcap.c */
+extern int set_termcap(void);
+
+/* variable.c */
+extern void variable_set(char *var, int dirty);
+extern void variable_set2(char *name, char *value, int dirty);
+extern char *variable_get(char *var);
+extern int variable_cmp(char *var, char *value);
+extern void variable_unset(char *var);
+extern char *variable_get_value(char *var, char *prompt, int dirty);
+extern int variable_check(char *data);
+extern int variable_check2(char *data);
+extern int dump_variables(dialogMenuItem *self);
+extern void free_variables(void);
+extern void pvariable_set(char *var);
+extern char *pvariable_get(char *var);
+
+/* wizard.c */
+extern void slice_wizard(Disk *d);
+
+/*
+ * Macros. Please find a better place for us!
+ */
+#define DEVICE_INIT(d) ((d) != NULL ? (d)->init((d)) : (Boolean)0)
+#define DEVICE_GET(d, b, f) ((d) != NULL ? (d)->get((d), (b), (f)) : NULL)
+#define DEVICE_SHUTDOWN(d) ((d) != NULL ? (d)->shutdown((d)) : (void)0)
+
+#endif
+/* _SYSINSTALL_H_INCLUDE */
diff --git a/usr.sbin/sade/system.c b/usr.sbin/sade/system.c
new file mode 100644
index 0000000..149bbeb
--- /dev/null
+++ b/usr.sbin/sade/system.c
@@ -0,0 +1,307 @@
+/*
+ * $FreeBSD$
+ *
+ * Jordan Hubbard
+ *
+ * My contributions are in the public domain.
+ *
+ * Parts of this file are also blatently stolen from Poul-Henning Kamp's
+ * previous version of sysinstall, and as such fall under his "BEERWARE license"
+ * so buy him a beer if you like it! Buy him a beer for me, too!
+ * Heck, get him completely drunk and send me pictures! :-)
+ */
+
+#include "sade.h"
+#include <signal.h>
+#include <termios.h>
+#include <sys/param.h>
+#include <sys/reboot.h>
+#include <sys/consio.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <ufs/ufs/ufsmount.h>
+
+
+/* Where we stick our temporary expanded doc file */
+#define DOC_TMP_DIR "/tmp/.doc"
+#define DOC_TMP_FILE "/tmp/.doc/doc.tmp"
+
+/*
+ * Handle interrupt signals - this probably won't work in all cases
+ * due to our having bogotified the internal state of dialog or curses,
+ * but we'll give it a try.
+ */
+static int
+intr_continue(dialogMenuItem *self)
+{
+ return DITEM_LEAVE_MENU;
+}
+
+static int
+intr_restart(dialogMenuItem *self)
+{
+ int ret, fd, fdmax;
+
+ free_variables();
+ fdmax = getdtablesize();
+ for (fd = 3; fd < fdmax; fd++)
+ close(fd);
+ ret = execl(StartName, StartName, "-restart", (char *)NULL);
+ msgDebug("execl failed (%s)\n", strerror(errno));
+ /* NOTREACHED */
+ return -1;
+}
+
+static dialogMenuItem intrmenu[] = {
+ { "Restart", "Restart the program", NULL, intr_restart, NULL, NULL, 0, 0, 0, 0 },
+ { "Continue", "Continue without restarting", NULL, intr_continue, NULL, NULL, 0, 0, 0, 0 },
+};
+
+
+static void
+handle_intr(int sig)
+{
+ WINDOW *save = savescr();
+
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (OnVTY) {
+ ioctl(0, VT_ACTIVATE, 1); /* Switch back */
+ msgInfo(NULL);
+ }
+ (void)dialog_menu("Installation interrupt",
+ "Do you want to abort the installation?",
+ -1, -1, 2, -2, intrmenu, NULL, NULL, NULL);
+ restorescr(save);
+}
+
+/* Expand a file into a convenient location, nuking it each time */
+static char *
+expand(char *fname)
+{
+ char *gunzip = "/usr/bin/gunzip";
+
+ if (!directory_exists(DOC_TMP_DIR)) {
+ Mkdir(DOC_TMP_DIR);
+ if (chown(DOC_TMP_DIR, 0, 0) < 0)
+ return NULL;
+ if (chmod(DOC_TMP_DIR, S_IRWXU) < 0)
+ return NULL;
+ }
+ else
+ unlink(DOC_TMP_FILE);
+ if (!file_readable(fname) || vsystem("%s < %s > %s", gunzip, fname, DOC_TMP_FILE))
+ return NULL;
+ return DOC_TMP_FILE;
+}
+
+/* Initialize system defaults */
+void
+systemInitialize(int argc, char **argv)
+{
+ size_t i;
+ int boothowto;
+ sigset_t signalset;
+
+ signal(SIGINT, SIG_IGN);
+ globalsInit();
+
+ i = sizeof(boothowto);
+ if (!sysctlbyname("debug.boothowto", &boothowto, &i, NULL, 0) &&
+ (i == sizeof(boothowto)) && (boothowto & RB_VERBOSE))
+ variable_set2(VAR_DEBUG, "YES", 0);
+
+ if (set_termcap() == -1) {
+ printf("Can't find terminal entry\n");
+ exit(-1);
+ }
+
+ /* XXX - libdialog has particularly bad return value checking */
+ init_dialog();
+
+ /* If we haven't crashed I guess dialog is running ! */
+ DialogActive = TRUE;
+
+ /* Make sure HOME is set for those utilities that need it */
+ signal(SIGINT, handle_intr);
+ /*
+ * Make sure we can be interrupted even if we were re-executed
+ * from an interrupt.
+ */
+ sigemptyset(&signalset);
+ sigaddset(&signalset, SIGINT);
+ sigprocmask(SIG_UNBLOCK, &signalset, NULL);
+
+ (void)vsystem("rm -rf %s", DOC_TMP_DIR);
+}
+
+/* Run some general command */
+int
+systemExecute(char *command)
+{
+ int status;
+ struct termios foo;
+ WINDOW *w = savescr();
+
+ dialog_clear();
+ dialog_update();
+ end_dialog();
+ DialogActive = FALSE;
+ if (tcgetattr(0, &foo) != -1) {
+ foo.c_cc[VERASE] = '\010';
+ tcsetattr(0, TCSANOW, &foo);
+ }
+ if (!Fake)
+ status = system(command);
+ else {
+ status = 0;
+ msgDebug("systemExecute: Faked execution of `%s'\n", command);
+ }
+ DialogActive = TRUE;
+ restorescr(w);
+ return status;
+}
+
+/* suspend/resume libdialog/curses screen */
+static WINDOW *oldW;
+
+void
+systemSuspendDialog(void)
+{
+
+ oldW = savescr();
+ dialog_clear();
+ dialog_update();
+ end_dialog();
+ DialogActive = FALSE;
+}
+
+void
+systemResumeDialog(void)
+{
+
+ DialogActive = TRUE;
+ restorescr(oldW);
+}
+
+/* Display a help file in a filebox */
+int
+systemDisplayHelp(char *file)
+{
+ char *fname = NULL;
+ char buf[FILENAME_MAX];
+ int ret = 0;
+ WINDOW *w = savescr();
+
+ printf("zzz");
+ fname = systemHelpFile(file, buf);
+ if (!fname) {
+ snprintf(buf, FILENAME_MAX, "The %s file is not provided on this particular floppy image.", file);
+ use_helpfile(NULL);
+ use_helpline(NULL);
+ dialog_mesgbox("Sorry!", buf, -1, -1);
+ ret = 1;
+ }
+ else {
+ use_helpfile(NULL);
+ use_helpline(NULL);
+ dialog_textbox(file, fname, LINES, COLS);
+ }
+ restorescr(w);
+ return ret;
+}
+
+char *
+systemHelpFile(char *file, char *buf)
+{
+ if (!file)
+ return NULL;
+ if (file[0] == '/')
+ return file;
+ snprintf(buf, FILENAME_MAX, "/stand/help/%s.hlp.gz", file);
+ if (file_readable(buf))
+ return expand(buf);
+ snprintf(buf, FILENAME_MAX, "/stand/help/%s.hlp", file);
+ if (file_readable(buf))
+ return expand(buf);
+ snprintf(buf, FILENAME_MAX, "/stand/help/%s.TXT.gz", file);
+ if (file_readable(buf))
+ return expand(buf);
+ snprintf(buf, FILENAME_MAX, "/stand/help/%s.TXT", file);
+ if (file_readable(buf))
+ return expand(buf);
+ snprintf(buf, FILENAME_MAX, "/usr/src/usr.sbin/%s/help/%s.hlp", ProgName,
+ file);
+ if (file_readable(buf))
+ return buf;
+ snprintf(buf, FILENAME_MAX, "/usr/src/usr.sbin/%s/help/%s.TXT", ProgName,
+ file);
+ if (file_readable(buf))
+ return buf;
+ return NULL;
+}
+
+int
+vsystem(const char *fmt, ...)
+{
+ va_list args;
+ int pstat;
+ pid_t pid;
+ int omask;
+ sig_t intsave, quitsave;
+ char *cmd;
+ int i;
+ struct stat sb;
+
+ cmd = (char *)alloca(FILENAME_MAX);
+ cmd[0] = '\0';
+ va_start(args, fmt);
+ vsnprintf(cmd, FILENAME_MAX, fmt, args);
+ va_end(args);
+
+ omask = sigblock(sigmask(SIGCHLD));
+ if (Fake) {
+ msgDebug("vsystem: Faked execution of `%s'\n", cmd);
+ return 0;
+ }
+ if (isDebug())
+ msgDebug("Executing command `%s'\n", cmd);
+ pid = fork();
+ if (pid == -1) {
+ (void)sigsetmask(omask);
+ i = 127;
+ }
+ else if (!pid) { /* Junior */
+ (void)sigsetmask(omask);
+ if (DebugFD != -1) {
+ dup2(DebugFD, 0);
+ dup2(DebugFD, 1);
+ dup2(DebugFD, 2);
+ }
+ else {
+ close(1); open("/dev/null", O_WRONLY);
+ dup2(1, 2);
+ }
+ if (stat("/stand/sh", &sb) == 0)
+ execl("/stand/sh", "/stand/sh", "-c", cmd, (char *)NULL);
+ else
+ execl("/bin/sh", "/bin/sh", "-c", cmd, (char *)NULL);
+ exit(1);
+ }
+ else {
+ intsave = signal(SIGINT, SIG_IGN);
+ quitsave = signal(SIGQUIT, SIG_IGN);
+ pid = waitpid(pid, &pstat, 0);
+ (void)sigsetmask(omask);
+ (void)signal(SIGINT, intsave);
+ (void)signal(SIGQUIT, quitsave);
+ i = (pid == -1) ? -1 : WEXITSTATUS(pstat);
+ if (isDebug())
+ msgDebug("Command `%s' returns status of %d\n", cmd, i);
+ }
+ return i;
+}
+
diff --git a/usr.sbin/sade/termcap.c b/usr.sbin/sade/termcap.c
new file mode 100644
index 0000000..3e80a41
--- /dev/null
+++ b/usr.sbin/sade/termcap.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 1994, Paul Richards.
+ *
+ * All rights reserved.
+ *
+ * This software may be used, modified, copied, distributed, and sold, in both
+ * source and binary form provided that the above copyright and these terms
+ * are retained, verbatim, as the first lines of this file. Under no
+ * circumstances is the author responsible for the proper functioning of this
+ * software, nor does the author assume any responsibility for damages
+ * incurred with its use.
+ *
+ * $FreeBSD$
+ */
+
+#include "sade.h"
+#include <stdarg.h>
+#include <fcntl.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/consio.h>
+
+#define VTY_STATUS_LINE 24
+#define TTY_STATUS_LINE 23
+
+static void
+prompt_term(char **termp)
+{
+ char str[80];
+
+ printf("\nPlease set your TERM variable before running this program.\n");
+ printf("Defaulting to an ANSI compatible terminal - please press RETURN\n");
+ fgets(str, sizeof(str), stdin); /* Just to make it interactive */
+ *termp = (char *)"ansi";
+}
+
+int
+set_termcap(void)
+{
+ char *term;
+ int stat;
+ struct winsize ws;
+
+ term = getenv("TERM");
+ stat = ioctl(STDERR_FILENO, GIO_COLOR, &ColorDisplay);
+
+ if (isDebug())
+ DebugFD = open("sade.debug", O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ else
+ DebugFD = -1;
+ if (DebugFD < 0)
+ DebugFD = open("/dev/null", O_RDWR, 0);
+
+ if (!OnVTY || (stat < 0)) {
+ if (!term) {
+ char *term;
+
+ prompt_term(&term);
+ if (setenv("TERM", term, 1) < 0)
+ return -1;
+ }
+ if (DebugFD < 0)
+ DebugFD = open("/dev/null", O_RDWR, 0);
+ }
+ else {
+ int i, on;
+
+ if (getpid() == 1) {
+ DebugFD = open("/dev/ttyv1", O_WRONLY);
+ if (DebugFD != -1) {
+ on = 1;
+ i = ioctl(DebugFD, TIOCCONS, (char *)&on);
+ msgDebug("ioctl(%d, TIOCCONS, NULL) = %d (%s)\n",
+ DebugFD, i, !i ? "success" : strerror(errno));
+ }
+ }
+
+#ifdef PC98
+ if (!term) {
+ if (setenv("TERM", "cons25w", 1) < 0)
+ return -1;
+ }
+#else
+ if (ColorDisplay) {
+ if (!term) {
+ if (setenv("TERM", "cons25", 1) < 0)
+ return -1;
+ }
+ }
+ else {
+ if (!term) {
+ if (setenv("TERM", "cons25-m", 1) < 0)
+ return -1;
+ }
+ }
+#endif
+ }
+ if (ioctl(0, TIOCGWINSZ, &ws) == -1) {
+ msgDebug("Unable to get terminal size - errno %d\n", errno);
+ ws.ws_row = 0;
+ }
+ StatusLine = ws.ws_row ? ws.ws_row - 1: (OnVTY ? VTY_STATUS_LINE : TTY_STATUS_LINE);
+ return 0;
+}
diff --git a/usr.sbin/sade/variable.c b/usr.sbin/sade/variable.c
new file mode 100644
index 0000000..911eb02
--- /dev/null
+++ b/usr.sbin/sade/variable.c
@@ -0,0 +1,324 @@
+/*
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ * Copyright (c) 2001
+ * Murray Stokely. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sade.h"
+
+/* Routines for dealing with variable lists */
+
+static void
+make_variable(char *var, char *value, int dirty)
+{
+ Variable *vp;
+
+ /* Trim leading and trailing whitespace */
+ var = string_skipwhite(string_prune(var));
+
+ if (!var || !*var)
+ return;
+
+
+ /* Now search to see if it's already in the list */
+ for (vp = VarHead; vp; vp = vp->next) {
+ if (!strcmp(vp->name, var)) {
+ if (vp->dirty && !dirty)
+ return;
+ setenv(var, value, 1);
+ free(vp->value);
+ vp->value = strdup(value);
+ if (dirty != -1)
+ vp->dirty = dirty;
+ return;
+ }
+ }
+
+ setenv(var, value, 1);
+ /* No? Create a new one */
+ vp = (Variable *)safe_malloc(sizeof(Variable));
+ vp->name = strdup(var);
+ vp->value = strdup(value);
+ if (dirty == -1)
+ dirty = 0;
+ vp->dirty = dirty;
+ vp->next = VarHead;
+ VarHead = vp;
+}
+
+void
+variable_set(char *var, int dirty)
+{
+ char tmp[1024], *cp;
+
+ if (!var)
+ msgFatal("NULL variable name & value passed.");
+ else if (!*var)
+ msgDebug("Warning: Zero length name & value passed to variable_set()\n");
+ SAFE_STRCPY(tmp, var);
+ if ((cp = index(tmp, '=')) == NULL)
+ msgFatal("Invalid variable format: %s", var);
+ *(cp++) = '\0';
+ make_variable(tmp, string_skipwhite(cp), dirty);
+}
+
+void
+variable_set2(char *var, char *value, int dirty)
+{
+ if (!var || !value)
+ msgFatal("Null name or value passed to set_variable2(%s) = %s!",
+ var ? var : "", value ? value : "");
+ else if (!*var || !*value)
+ msgDebug("Warning: Zero length name or value passed to variable_set2(%s) = %s\n",
+ var, value);
+ make_variable(var, value, dirty);
+}
+
+char *
+variable_get(char *var)
+{
+ return getenv(var);
+}
+
+int
+variable_cmp(char *var, char *value)
+{
+ char *val;
+
+ if ((val = variable_get(var)))
+ return strcmp(val, value);
+ return -1;
+}
+
+void
+variable_unset(char *var)
+{
+ Variable *vp;
+ char name[512], *cp;
+
+ if ((cp = index(var, '=')) != NULL)
+ sstrncpy(name, var, cp - var);
+ else
+ SAFE_STRCPY(name, var);
+ unsetenv(name);
+ /* Now search to see if it's in our list, if we have one.. */
+ if (!VarHead)
+ return;
+ else if (!VarHead->next && !strcmp(VarHead->name, name)) {
+ safe_free(VarHead->name);
+ safe_free(VarHead->value);
+ free(VarHead);
+ VarHead = NULL;
+ }
+ else {
+ for (vp = VarHead; vp; vp = vp->next) {
+ if (!strcmp(vp->name, name)) {
+ Variable *save = vp->next;
+
+ safe_free(vp->name);
+ safe_free(vp->value);
+ *vp = *save;
+ safe_free(save);
+ break;
+ }
+ }
+ }
+}
+
+/* Prompt user for the name of a variable */
+char *
+variable_get_value(char *var, char *prompt, int dirty)
+{
+ char *cp;
+
+ cp = variable_get(var);
+ if (cp && variable_get(VAR_NONINTERACTIVE))
+ return cp;
+ else if ((cp = msgGetInput(cp, "%s", prompt)) != NULL)
+ variable_set2(var, cp, dirty);
+ else
+ cp = NULL;
+ return cp;
+}
+
+/* Check if value passed in data (in the form "variable=value") is
+ * valid, and it's status compared to the value of variable stored in
+ * env
+ *
+ * Possible return values :
+ * -3: Invalid line, the data string is NOT set as an env variable
+ * -2: Invalid line, the data string is set as an env variable
+ * -1: Invalid line
+ * 0: Valid line, is NOT equal to env version
+ * 1: Valid line, is equal to env version
+ * 2: Valid line, value empty - e.g. foo=""
+ * 3: Valid line, does not exist in env
+*/
+int
+variable_check2(char *data)
+{
+ char *cp, *cp2, *cp3, tmp[256];
+
+ if (data == NULL)
+ return -1;
+ SAFE_STRCPY(tmp, data);
+ if ((cp = index(tmp, '=')) != NULL) {
+ *(cp++) = '\0';
+ if (*cp == '"') { /* smash quotes if present */
+ ++cp;
+ if ((cp3 = index(cp, '"')) != NULL)
+ *cp3 = '\0';
+ }
+ else if ((cp3 = index(cp, ',')) != NULL)
+ *cp3 = '\0';
+ cp2 = variable_get(tmp);
+ if (cp2 != NULL) {
+ if (*cp == '\0')
+ return 2;
+ else
+ return strcmp(cp, cp2) == 0 ? 1 : 0;
+ }
+ else
+ return 3;
+ }
+ else
+ return variable_get(tmp) != NULL ? -2 : -3;
+}
+
+/* Check if the value passed in data (in the form "variable=value") is
+ equal to the value of variable stored in env */
+int
+variable_check(char *data)
+{
+ int ret;
+ ret = variable_check2(data);
+
+ switch(ret) {
+ case -2:
+ case 1:
+ case 2:
+ return TRUE;
+ /* NOT REACHED */
+ default:
+ return FALSE;
+ }
+}
+
+int
+dump_variables(dialogMenuItem *unused)
+{
+ FILE *fp;
+ Variable *vp;
+
+ if (isDebug())
+ msgDebug("Writing %s variables to file..\n", ProgName);
+
+ fp = fopen("/etc/sade.vars", "w");
+ if (!fp) {
+ msgConfirm("Unable to write to /etc/%s.vars: %s",
+ ProgName, strerror(errno));
+ return DITEM_FAILURE;
+ }
+
+ for (vp = VarHead; vp; vp = vp->next)
+ fprintf(fp, "%s=\"%s\" (%d)\n", vp->name, vp->value, vp->dirty);
+
+ fclose(fp);
+
+ return DITEM_SUCCESS;
+}
+
+/* Free all of the variables, useful to really start over as when the
+ user selects "restart" from the interrupt menu. */
+void
+free_variables(void)
+{
+ Variable *vp;
+
+ /* Free the variables from our list, if we have one.. */
+ if (!VarHead)
+ return;
+ else if (!VarHead->next) {
+ unsetenv(VarHead->name);
+ safe_free(VarHead->name);
+ safe_free(VarHead->value);
+ free(VarHead);
+ VarHead = NULL;
+ }
+ else {
+ for (vp = VarHead; vp; ) {
+ Variable *save = vp;
+ unsetenv(vp->name);
+ safe_free(vp->name);
+ safe_free(vp->value);
+ vp = vp->next;
+ safe_free(save);
+ }
+ VarHead = NULL;
+ }
+}
+
+/*
+ * Persistent variables. The variables modified by these functions
+ * are not cleared between invocations of sysinstall. This is useful
+ * to allow the user to completely restart sysinstall, without having
+ * it load all of the modules again from the installation media which
+ * are still in memory.
+ */
+
+void
+pvariable_set(char *var)
+{
+ char *p;
+ char tmp[1024];
+
+ if (!var)
+ msgFatal("NULL variable name & value passed.");
+ else if (!*var)
+ msgDebug("Warning: Zero length name & value passed to variable_set()\n");
+ /* Add a trivial namespace to whatever name the caller chooses. */
+ SAFE_STRCPY(tmp, "SYSINSTALL_PVAR");
+ if (index(var, '=') == NULL)
+ msgFatal("Invalid variable format: %s", var);
+ strlcat(tmp, var, 1024);
+ p = strchr(tmp, '=');
+ *p = '\0';
+ setenv(tmp, p + 1, 1);
+}
+
+char *
+pvariable_get(char *var)
+{
+ char tmp[1024];
+
+ SAFE_STRCPY(tmp, "SYSINSTALL_PVAR");
+ strlcat(tmp, var, 1024);
+ return getenv(tmp);
+}
diff --git a/usr.sbin/sade/wizard.c b/usr.sbin/sade/wizard.c
new file mode 100644
index 0000000..5edd872
--- /dev/null
+++ b/usr.sbin/sade/wizard.c
@@ -0,0 +1,200 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "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 "sade.h"
+#include <fcntl.h>
+#include <err.h>
+#include <libdisk.h>
+
+static int
+scan_block(int fd, daddr_t block)
+{
+ u_char foo[512];
+
+ if (-1 == lseek(fd,block * 512,SEEK_SET))
+ err(1,"lseek");
+ if (512 != read(fd,foo, 512))
+ return 1;
+ return 0;
+}
+
+static void
+Scan_Disk(Disk *d)
+{
+ char device[64];
+ u_long l;
+ int i,j,fd;
+
+ strcpy(device,"/dev/");
+ strcat(device,d->name);
+
+ fd = open(device,O_RDWR);
+ if (fd < 0) {
+ msgWarn("open(%s) failed", device);
+ return;
+ }
+ for(i=-1,l=0;;l++) {
+ j = scan_block(fd,l);
+ if (j != i) {
+ if (i == -1) {
+ printf("%c: %lu.",j ? 'B' : 'G', l);
+ fflush(stdout);
+ } else if (i == 0) {
+ printf(".%lu\nB: %lu.",l-1,l);
+ fflush(stdout);
+ } else {
+ printf(".%lu\nG: %lu.",l-1,l);
+ fflush(stdout);
+ }
+ i = j;
+ }
+ }
+ close(fd);
+}
+
+void
+slice_wizard(Disk *d)
+{
+ Disk *db;
+ char myprompt[BUFSIZ];
+ char input[BUFSIZ];
+ char *p,*q=0;
+ char **cp,*cmds[200];
+ int ncmd,i;
+
+ systemSuspendDialog();
+ sprintf(myprompt,"%s> ", d->name);
+ while(1) {
+ printf("--==##==--\n");
+ Debug_Disk(d);
+ p = CheckRules(d);
+ if (p) {
+ printf("%s",p);
+ free(p);
+ }
+ printf("%s", myprompt);
+ fflush(stdout);
+ q = p = fgets(input,sizeof(input),stdin);
+ if(!p)
+ break;
+ for(cp = cmds; (*cp = strsep(&p, " \t\n")) != NULL;)
+ if (**cp != '\0')
+ cp++;
+ ncmd = cp - cmds;
+ if(!ncmd)
+ continue;
+ if (!strcasecmp(*cmds,"quit")) { break; }
+ if (!strcasecmp(*cmds,"exit")) { break; }
+ if (!strcasecmp(*cmds,"q")) { break; }
+ if (!strcasecmp(*cmds,"x")) { break; }
+ if (!strcasecmp(*cmds,"delete") && ncmd == 2) {
+ printf("delete = %d\n",
+ Delete_Chunk(d,
+ (struct chunk *)strtol(cmds[1],0,0)));
+ continue;
+ }
+ if (!strcasecmp(*cmds,"allfreebsd")) {
+ All_FreeBSD(d, 0);
+ continue;
+ }
+ if (!strcasecmp(*cmds,"dedicate")) {
+ All_FreeBSD(d, 1);
+ continue;
+ }
+ if (!strcasecmp(*cmds,"bios") && ncmd == 4) {
+ Set_Bios_Geom(d,
+ strtol(cmds[1],0,0),
+ strtol(cmds[2],0,0),
+ strtol(cmds[3],0,0));
+ continue;
+ }
+ if (!strcasecmp(*cmds,"list")) {
+ cp = Disk_Names();
+ printf("Disks:");
+ for(i=0;cp[i];i++) {
+ printf(" %s",cp[i]);
+ free(cp[i]);
+ }
+ free(cp);
+ continue;
+ }
+#ifdef PC98
+ if (!strcasecmp(*cmds,"create") && ncmd == 7) {
+ printf("Create=%d\n",
+ Create_Chunk(d,
+ strtol(cmds[1],0,0),
+ strtol(cmds[2],0,0),
+ strtol(cmds[3],0,0),
+ strtol(cmds[4],0,0),
+ strtol(cmds[5],0,0),
+ cmds[6]));
+ continue;
+ }
+#else
+ if (!strcasecmp(*cmds,"create") && ncmd == 6) {
+ printf("Create=%d\n",
+ Create_Chunk(d,
+ strtol(cmds[1],0,0),
+ strtol(cmds[2],0,0),
+ strtol(cmds[3],0,0),
+ strtol(cmds[4],0,0),
+ strtol(cmds[5],0,0), ""));
+ continue;
+ }
+#endif
+ if (!strcasecmp(*cmds,"read")) {
+ db = d;
+ if (ncmd > 1)
+ d = Open_Disk(cmds[1]);
+ else
+ d = Open_Disk(d->name);
+ if (d)
+ Free_Disk(db);
+ else
+ d = db;
+ continue;
+ }
+ if (!strcasecmp(*cmds,"scan")) {
+ Scan_Disk(d);
+ continue;
+ }
+ if (!strcasecmp(*cmds,"write")) {
+ printf("Write=%d\n",
+ Fake ? 0 : Write_Disk(d));
+ q = strdup(d->name);
+ Free_Disk(d);
+ d = Open_Disk(q);
+ continue;
+ }
+ if (strcasecmp(*cmds,"help"))
+ printf("\007ERROR\n");
+ printf("CMDS:\n");
+ printf("allfreebsd\t\t");
+ printf("dedicate\t\t");
+ printf("bios cyl hd sect\n");
+ printf("collapse [pointer]\t\t");
+#ifdef PC98
+ printf("create offset size enum subtype flags name\n");
+#else
+ printf("create offset size enum subtype flags\n");
+#endif
+ printf("subtype(part): swap=1, ffs=7\t\t");
+ printf("delete pointer\n");
+ printf("list\t\t");
+ printf("quit\n");
+ printf("read [disk]\t\t");
+ printf("scan\n");
+ printf("write\t\t");
+ printf("\n");
+
+ }
+ systemResumeDialog();
+}
diff --git a/usr.sbin/sendmail/Makefile b/usr.sbin/sendmail/Makefile
new file mode 100644
index 0000000..0b89297
--- /dev/null
+++ b/usr.sbin/sendmail/Makefile
@@ -0,0 +1,77 @@
+# @(#)Makefile 8.8 (Berkeley) 3/28/97
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+SENDMAIL_DIR=${.CURDIR}/../../contrib/sendmail
+SMDIR= ${SENDMAIL_DIR}/src
+.PATH: ${SMDIR}
+
+BINDIR= /usr/libexec/sendmail
+
+PROG= sendmail
+MAN= mailq.1 newaliases.1 aliases.5 sendmail.8
+MLINKS+=sendmail.8 hoststat.8
+MLINKS+=sendmail.8 purgestat.8
+SRCS= alias.c arpadate.c bf.c collect.c conf.c control.c \
+ convtime.c daemon.c deliver.c domain.c envelope.c err.c headers.c \
+ macro.c main.c map.c mci.c milter.c mime.c parseaddr.c queue.c \
+ ratectrl.c readcf.c recipient.c savemail.c sasl.c sfsasl.c \
+ shmticklib.c sm_resolve.c srvrsmtp.c stab.c stats.c sysexits.c \
+ timers.c tls.c trace.c udb.c usersmtp.c util.c version.c
+BINOWN= root
+BINGRP= smmsp
+.ifdef SENDMAIL_SET_USER_ID
+BINMODE=4555
+.else
+BINMODE=2555
+.endif
+
+# Define the database format to use for aliases et al.
+DBMDEF= -DNEWDB
+# If you don't want NIS alias/map support, comment out this line
+.if ${MK_NIS} != "no"
+NIS= -DNIS
+.endif
+# Map extensions
+MAPS= -DMAP_REGEX -DDNSMAP
+
+CFLAGS+= -I${SMDIR} -I${SENDMAIL_DIR}/include -I.
+CFLAGS+= ${DBMDEF} ${NIS} -DTCPWRAPPERS ${MAPS}
+
+.if ${MK_INET6_SUPPORT} != "no"
+CFLAGS+= -DNETINET6
+.endif
+
+DPADD= ${LIBUTIL} ${LIBWRAP}
+LDADD= -lutil -lwrap
+
+LIBSMDIR= ${.OBJDIR}/../../lib/libsm
+LIBSM= ${LIBSMDIR}/libsm.a
+
+LIBSMUTILDIR= ${.OBJDIR}/../../lib/libsmutil
+LIBSMUTIL= ${LIBSMUTILDIR}/libsmutil.a
+
+DPADD+= ${LIBSMUTIL} ${LIBSM}
+LDADD+= ${LIBSMUTIL} ${LIBSM}
+
+SRCS+= sm_os.h
+CLEANFILES+=sm_os.h
+
+.if ${MK_OPENSSL} != "no" && !defined(RELEASE_CRUNCH)
+# STARTTLS support
+CFLAGS+= -DSTARTTLS -D_FFR_TLS_1
+DPADD+= ${LIBSSL} ${LIBCRYPTO}
+LDADD+= -lssl -lcrypto
+.endif
+
+# User customizations to the sendmail build environment
+CFLAGS+=${SENDMAIL_CFLAGS}
+DPADD+=${SENDMAIL_DPADD}
+LDADD+=${SENDMAIL_LDADD}
+LDFLAGS+=${SENDMAIL_LDFLAGS}
+
+sm_os.h:
+ ln -sf ${SENDMAIL_DIR}/include/sm/os/sm_os_freebsd.h sm_os.h
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/setfib/Makefile b/usr.sbin/setfib/Makefile
new file mode 100644
index 0000000..8508ed1
--- /dev/null
+++ b/usr.sbin/setfib/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= setfib
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/setfib/setfib.1 b/usr.sbin/setfib/setfib.1
new file mode 100644
index 0000000..fd0516b
--- /dev/null
+++ b/usr.sbin/setfib/setfib.1
@@ -0,0 +1,97 @@
+.\" Copyright (c) 2008 Cisco systems
+.\" Author Julian Elischer. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. 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 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 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 9, 2008
+.Dt SETFIB 1
+.Os
+.Sh NAME
+.Nm setfib
+.Nd execute a utility with an altered default network view
+.Sh SYNOPSIS
+.Nm
+.Op Fl F
+.Ar fib
+.Ar utility
+.Op Ar argument ...
+.Sh DESCRIPTION
+The
+.Nm
+utility runs
+.Ar utility
+with an different routing table.
+The table number
+.Dq Ar fib
+will be used by default for all sockets started by this
+process or descendents.
+.Sh ENVIRONMENT
+The
+.Ev PATH
+environment variable is used to locate the requested
+.Ar utility
+if the name contains no
+.Ql /
+characters.
+.Sh EXIT STATUS
+If
+.Ar utility
+is invoked, the exit status of
+.Nm
+is the exit status of
+.Ar utility .
+.Pp
+An exit status of 126 indicates
+.Ar utility
+was found, but could not be executed.
+An exit status of 127 indicates
+.Ar utility
+could not be found.
+.Sh EXAMPLES
+Execute utility
+.Sq netstat
+to view the second routing table.
+.Pp
+.Dl "setfib -F 1 netstat -rn"
+or
+.Dl "setfib 1 netstat -rn"
+or
+.Dl "setfib -1 netstat -rn"
+.Sh SEE ALSO
+.Xr setfib 2 ,
+.Xr setsockopt 2
+.Sh STANDARDS
+The
+.Nm
+utility is a
+.Fx
+specific extension, however many
+.Ux
+like systems
+have an equivalent function.
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 8.0 .
diff --git a/usr.sbin/setfib/setfib.c b/usr.sbin/setfib/setfib.c
new file mode 100644
index 0000000..f4b6c3d
--- /dev/null
+++ b/usr.sbin/setfib/setfib.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 2008 Cisco Systems, All rights reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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.
+ *
+ * setfib file skelaton taken from nice.c
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ long fib = 0;
+ int ch;
+ char *ep;
+ int numfibs;
+ size_t intsize = sizeof(int);
+
+ if (sysctlbyname("net.fibs", &numfibs, &intsize, NULL, 0) == -1)
+ errx(1, "Multiple FIBS not supported");
+ if (argc < 2)
+ usage();
+ ep = argv[1];
+ /*
+ * convert -N or N to -FN. (N is a number)
+ */
+ if (ep[0]== '-' && isdigit((unsigned char)ep[1]))
+ ep++;
+ if (isdigit((unsigned char)*ep))
+ if (asprintf(&argv[1], "-F%s", ep) < 0)
+ err(1, "asprintf");
+
+ while ((ch = getopt(argc, argv, "F:")) != -1) {
+ switch (ch) {
+ case 'F':
+ errno = 0;
+ fib = strtol(optarg, &ep, 10);
+ if (ep == optarg || *ep != '\0' || errno ||
+ fib < 0 || fib >= numfibs)
+ errx(1, "%s: invalid FIB (max %d)",
+ optarg, numfibs - 1);
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0)
+ usage();
+
+ errno = 0;
+ if (setfib((int)fib))
+ warn("setfib");
+ execvp(*argv, argv);
+ err(errno == ENOENT ? 127 : 126, "%s", *argv);
+}
+
+static void
+usage(void)
+{
+
+ (void)fprintf(stderr,
+ "usage: setfib [-[F]]value command\n");
+ exit(1);
+}
diff --git a/usr.sbin/setfmac/Makefile b/usr.sbin/setfmac/Makefile
new file mode 100644
index 0000000..34b979b
--- /dev/null
+++ b/usr.sbin/setfmac/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= setfmac
+LINKS= ${BINDIR}/setfmac ${BINDIR}/setfsmac
+MAN= setfmac.8 setfsmac.8
+
+WARNS?= 6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/setfmac/setfmac.8 b/usr.sbin/setfmac/setfmac.8
new file mode 100644
index 0000000..b541a4a
--- /dev/null
+++ b/usr.sbin/setfmac/setfmac.8
@@ -0,0 +1,66 @@
+.\" Copyright (c) 2002, 2004 Networks Associates Technology, Inc.
+.\" All rights reserved.
+.\"
+.\" This software was developed for the FreeBSD Project by Chris
+.\" Costello at Safeport Network Services 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 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 February 17, 2004
+.Dt SETFMAC 8
+.Os
+.Sh NAME
+.Nm setfmac
+.Nd set MAC label for a file system object
+.Sh SYNOPSIS
+.Nm setfmac
+.Op Fl hqR
+.Ar label
+.Ar
+.Sh DESCRIPTION
+The
+.Nm setfmac
+utility assigns the specified MAC label to the specified files.
+The following options are available:
+.Bl -tag -width indent
+.It Fl R
+Set the labels on the file hierarchies rooted in the files instead of
+just the files themselves.
+.It Fl h
+If the file is a symbolic link, change the label of the link rather
+than the file that the link points to.
+.It Fl q
+Do not print non-fatal warnings during execution.
+.El
+.Sh SEE ALSO
+.Xr mac 3 ,
+.Xr mac_set_file 3 ,
+.Xr mac_set_link 3 ,
+.Xr mac 4 ,
+.Xr re_format 7 ,
+.Xr getfmac 8 ,
+.Xr setfsmac 8 ,
+.Xr mac 9
diff --git a/usr.sbin/setfmac/setfmac.c b/usr.sbin/setfmac/setfmac.c
new file mode 100644
index 0000000..e3da25d
--- /dev/null
+++ b/usr.sbin/setfmac/setfmac.c
@@ -0,0 +1,498 @@
+/*-
+ * Copyright (c) 2002, 2004 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.
+ *
+ * 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/mac.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <libgen.h>
+#include <regex.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+struct label_spec {
+ struct label_spec_entry {
+ regex_t regex; /* compiled regular expression to match */
+ char *regexstr; /* uncompiled regular expression */
+ mode_t mode; /* mode to possibly match */
+ const char *modestr; /* print-worthy ",-?" mode string */
+ char *mactext; /* MAC label to apply */
+ int flags; /* miscellaneous flags */
+#define F_DONTLABEL 0x01
+#define F_ALWAYSMATCH 0x02
+ } *entries, /* entries[0..nentries] */
+ *match; /* cached decision for MAC label to apply */
+ size_t nentries; /* size of entries list */
+ STAILQ_ENTRY(label_spec) link;
+};
+
+struct label_specs {
+ STAILQ_HEAD(label_specs_head, label_spec) head;
+};
+
+void usage(int) __dead2;
+struct label_specs *new_specs(void);
+void add_specs(struct label_specs *, const char *, int);
+void add_setfmac_specs(struct label_specs *, char *);
+void add_spec_line(const char *, int, struct label_spec_entry *, char *);
+int apply_specs(struct label_specs *, FTSENT *, int, int);
+int specs_empty(struct label_specs *);
+
+static int qflag;
+
+int
+main(int argc, char **argv)
+{
+ FTSENT *ftsent;
+ FTS *fts;
+ struct label_specs *specs;
+ int eflag = 0, xflag = 0, vflag = 0, Rflag = 0, hflag;
+ int ch, is_setfmac;
+ char *bn;
+
+ bn = basename(argv[0]);
+ if (bn == NULL)
+ err(1, "basename");
+ is_setfmac = strcmp(bn, "setfmac") == 0;
+ hflag = is_setfmac ? FTS_LOGICAL : FTS_PHYSICAL;
+ specs = new_specs();
+ while ((ch = getopt(argc, argv, is_setfmac ? "Rhq" : "ef:qs:vx")) !=
+ -1) {
+ switch (ch) {
+ case 'R':
+ Rflag = 1;
+ break;
+ case 'e':
+ eflag = 1;
+ break;
+ case 'f':
+ add_specs(specs, optarg, 0);
+ break;
+ case 'h':
+ hflag = FTS_PHYSICAL;
+ break;
+ case 'q':
+ qflag = 1;
+ break;
+ case 's':
+ add_specs(specs, optarg, 1);
+ break;
+ case 'v':
+ vflag++;
+ break;
+ case 'x':
+ xflag = FTS_XDEV;
+ break;
+ default:
+ usage(is_setfmac);
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (is_setfmac) {
+ if (argc <= 1)
+ usage(is_setfmac);
+ add_setfmac_specs(specs, *argv);
+ argc--;
+ argv++;
+ } else {
+ if (argc == 0 || specs_empty(specs))
+ usage(is_setfmac);
+ }
+ fts = fts_open(argv, hflag | xflag, NULL);
+ if (fts == NULL)
+ err(1, "cannot traverse filesystem%s", argc ? "s" : "");
+ while ((ftsent = fts_read(fts)) != NULL) {
+ switch (ftsent->fts_info) {
+ case FTS_DP: /* skip post-order */
+ break;
+ case FTS_D: /* do pre-order */
+ case FTS_DC: /* do cyclic? */
+ /* don't ever recurse directories as setfmac(8) */
+ if (is_setfmac && !Rflag)
+ fts_set(fts, ftsent, FTS_SKIP);
+ case FTS_DEFAULT: /* do default */
+ case FTS_F: /* do regular */
+ case FTS_SL: /* do symlink */
+ case FTS_SLNONE: /* do symlink */
+ case FTS_W: /* do whiteout */
+ if (apply_specs(specs, ftsent, hflag, vflag)) {
+ if (eflag) {
+ errx(1, "labeling not supported in %s",
+ ftsent->fts_path);
+ }
+ if (!qflag)
+ warnx("labeling not supported in %s",
+ ftsent->fts_path);
+ fts_set(fts, ftsent, FTS_SKIP);
+ }
+ break;
+ case FTS_DNR: /* die on all errors */
+ case FTS_ERR:
+ case FTS_NS:
+ err(1, "traversing %s", ftsent->fts_path);
+ default:
+ errx(1, "CANNOT HAPPEN (%d) traversing %s",
+ ftsent->fts_info, ftsent->fts_path);
+ }
+ }
+ fts_close(fts);
+ exit(0);
+}
+
+void
+usage(int is_setfmac)
+{
+
+ if (is_setfmac)
+ fprintf(stderr, "usage: setfmac [-Rhq] label file ...\n");
+ else
+ fprintf(stderr, "usage: setfsmac [-ehqvx] [-f specfile [...]] [-s specfile [...]] file ...\n");
+ exit(1);
+}
+
+static int
+chomp_line(char **line, size_t *linesize)
+{
+ char *s;
+ int freeme = 0;
+
+ for (s = *line; (unsigned)(s - *line) < *linesize; s++) {
+ if (!isspace(*s))
+ break;
+ }
+ if (*s == '#') {
+ **line = '\0';
+ *linesize = 0;
+ return (freeme);
+ }
+ memmove(*line, s, *linesize - (s - *line));
+ *linesize -= s - *line;
+ for (s = &(*line)[*linesize - 1]; s >= *line; s--) {
+ if (!isspace(*s))
+ break;
+ }
+ if (s != &(*line)[*linesize - 1]) {
+ *linesize = s - *line + 1;
+ } else {
+ s = malloc(*linesize + 1);
+ if (s == NULL)
+ err(1, "malloc");
+ strncpy(s, *line, *linesize);
+ *line = s;
+ freeme = 1;
+ }
+ (*line)[*linesize] = '\0';
+ return (freeme);
+}
+
+void
+add_specs(struct label_specs *specs, const char *file, int is_sebsd)
+{
+ struct label_spec *spec;
+ FILE *fp;
+ char *line;
+ size_t nlines = 0, linesize;
+ int freeline;
+
+ spec = malloc(sizeof(*spec));
+ if (spec == NULL)
+ err(1, "malloc");
+ fp = fopen(file, "r");
+ if (fp == NULL)
+ err(1, "opening %s", file);
+ while ((line = fgetln(fp, &linesize)) != NULL) {
+ freeline = chomp_line(&line, &linesize);
+ if (linesize > 0) /* only allocate space for non-comments */
+ nlines++;
+ if (freeline)
+ free(line);
+ }
+ if (ferror(fp))
+ err(1, "fgetln on %s", file);
+ rewind(fp);
+ spec->entries = calloc(nlines, sizeof(*spec->entries));
+ if (spec->entries == NULL)
+ err(1, "malloc");
+ spec->nentries = nlines;
+ while (nlines > 0) {
+ line = fgetln(fp, &linesize);
+ if (line == NULL) {
+ if (feof(fp))
+ errx(1, "%s ended prematurely", file);
+ else
+ err(1, "failure reading %s", file);
+ }
+ freeline = chomp_line(&line, &linesize);
+ if (linesize == 0) {
+ if (freeline)
+ free(line);
+ continue;
+ }
+ add_spec_line(file, is_sebsd, &spec->entries[--nlines], line);
+ if (freeline)
+ free(line);
+ }
+ fclose(fp);
+ if (!qflag)
+ warnx("%s: read %lu specifications", file,
+ (long)spec->nentries);
+ STAILQ_INSERT_TAIL(&specs->head, spec, link);
+}
+
+void
+add_setfmac_specs(struct label_specs *specs, char *label)
+{
+ struct label_spec *spec;
+
+ spec = malloc(sizeof(*spec));
+ if (spec == NULL)
+ err(1, "malloc");
+ spec->nentries = 1;
+ spec->entries = calloc(spec->nentries, sizeof(*spec->entries));
+ if (spec->entries == NULL)
+ err(1, "malloc");
+ /* The _only_ thing specified here is the mactext! */
+ spec->entries->mactext = label;
+ spec->entries->flags |= F_ALWAYSMATCH;
+ STAILQ_INSERT_TAIL(&specs->head, spec, link);
+}
+
+void
+add_spec_line(const char *file, int is_sebsd, struct label_spec_entry *entry,
+ char *line)
+{
+ char *regexstr, *modestr, *macstr, *regerrorstr;
+ size_t size;
+ int error;
+
+ regexstr = strtok(line, " \t");
+ if (regexstr == NULL)
+ errx(1, "%s: need regular expression", file);
+ modestr = strtok(NULL, " \t");
+ if (modestr == NULL)
+ errx(1, "%s: need a label", file);
+ macstr = strtok(NULL, " \t");
+ if (macstr == NULL) { /* the mode is just optional */
+ macstr = modestr;
+ modestr = NULL;
+ }
+ if (strtok(NULL, " \t") != NULL)
+ errx(1, "%s: extraneous fields at end of line", file);
+ /* assume we need to anchor this regex */
+ if (asprintf(&regexstr, "^%s$", regexstr) == -1)
+ err(1, "%s: processing regular expression", file);
+ entry->regexstr = regexstr;
+ error = regcomp(&entry->regex, regexstr, REG_EXTENDED | REG_NOSUB);
+ if (error) {
+ size = regerror(error, &entry->regex, NULL, 0);
+ regerrorstr = malloc(size);
+ if (regerrorstr == NULL)
+ err(1, "malloc");
+ (void)regerror(error, &entry->regex, regerrorstr, size);
+ errx(1, "%s: %s: %s", file, entry->regexstr, regerrorstr);
+ }
+ if (!is_sebsd) {
+ entry->mactext = strdup(macstr);
+ if (entry->mactext == NULL)
+ err(1, "strdup");
+ } else {
+ if (asprintf(&entry->mactext, "sebsd/%s", macstr) == -1)
+ err(1, "asprintf");
+ if (strcmp(macstr, "<<none>>") == 0)
+ entry->flags |= F_DONTLABEL;
+ }
+ if (modestr != NULL) {
+ if (strlen(modestr) != 2 || modestr[0] != '-')
+ errx(1, "%s: invalid mode string: %s", file, modestr);
+ switch (modestr[1]) {
+ case 'b':
+ entry->mode = S_IFBLK;
+ entry->modestr = ",-b";
+ break;
+ case 'c':
+ entry->mode = S_IFCHR;
+ entry->modestr = ",-c";
+ break;
+ case 'd':
+ entry->mode = S_IFDIR;
+ entry->modestr = ",-d";
+ break;
+ case 'p':
+ entry->mode = S_IFIFO;
+ entry->modestr = ",-p";
+ break;
+ case 'l':
+ entry->mode = S_IFLNK;
+ entry->modestr = ",-l";
+ break;
+ case 's':
+ entry->mode = S_IFSOCK;
+ entry->modestr = ",-s";
+ break;
+ case '-':
+ entry->mode = S_IFREG;
+ entry->modestr = ",--";
+ break;
+ default:
+ errx(1, "%s: invalid mode string: %s", file, modestr);
+ }
+ } else {
+ entry->modestr = "";
+ }
+}
+
+int
+specs_empty(struct label_specs *specs)
+{
+
+ return (STAILQ_EMPTY(&specs->head));
+}
+
+int
+apply_specs(struct label_specs *specs, FTSENT *ftsent, int hflag, int vflag)
+{
+ regmatch_t pmatch;
+ struct label_spec *ls;
+ struct label_spec_entry *ent;
+ char *regerrorstr, *macstr;
+ size_t size;
+ mac_t mac;
+ int error, matchedby;
+
+ /*
+ * Work through file context sources in order of specification
+ * on the command line, and through their entries in reverse
+ * order to find the "last" (hopefully "best") match.
+ */
+ matchedby = 0;
+ STAILQ_FOREACH(ls, &specs->head, link) {
+ for (ls->match = NULL, ent = ls->entries;
+ ent < &ls->entries[ls->nentries]; ent++) {
+ if (ent->flags & F_ALWAYSMATCH)
+ goto matched;
+ if (ent->mode != 0 &&
+ (ftsent->fts_statp->st_mode & S_IFMT) != ent->mode)
+ continue;
+ pmatch.rm_so = 0;
+ pmatch.rm_eo = ftsent->fts_pathlen;
+ error = regexec(&ent->regex, ftsent->fts_path, 1,
+ &pmatch, REG_STARTEND);
+ switch (error) {
+ case REG_NOMATCH:
+ continue;
+ case 0:
+ break;
+ default:
+ size = regerror(error, &ent->regex, NULL, 0);
+ regerrorstr = malloc(size);
+ if (regerrorstr == NULL)
+ err(1, "malloc");
+ (void)regerror(error, &ent->regex, regerrorstr,
+ size);
+ errx(1, "%s: %s", ent->regexstr, regerrorstr);
+ }
+ matched:
+ ls->match = ent;
+ if (vflag) {
+ if (matchedby == 0) {
+ printf("%s matched by ",
+ ftsent->fts_path);
+ matchedby = 1;
+ }
+ printf("%s(%s%s,%s)", matchedby == 2 ? "," : "",
+ ent->regexstr, ent->modestr, ent->mactext);
+ if (matchedby == 1)
+ matchedby = 2;
+ }
+ break;
+ }
+ }
+ if (vflag && matchedby)
+ printf("\n");
+ size = 0;
+ STAILQ_FOREACH(ls, &specs->head, link) {
+ /* cached match decision */
+ if (ls->match && (ls->match->flags & F_DONTLABEL) == 0)
+ /* add length of "x\0"/"y," */
+ size += strlen(ls->match->mactext) + 1;
+ }
+ if (size == 0)
+ return (0);
+ macstr = malloc(size);
+ if (macstr == NULL)
+ err(1, "malloc");
+ *macstr = '\0';
+ STAILQ_FOREACH(ls, &specs->head, link) {
+ /* cached match decision */
+ if (ls->match && (ls->match->flags & F_DONTLABEL) == 0) {
+ if (*macstr != '\0')
+ strcat(macstr, ",");
+ strcat(macstr, ls->match->mactext);
+ }
+ }
+ if (mac_from_text(&mac, macstr))
+ err(1, "mac_from_text(%s)", macstr);
+ if ((hflag == FTS_PHYSICAL ? mac_set_link(ftsent->fts_accpath, mac) :
+ mac_set_file(ftsent->fts_accpath, mac)) != 0) {
+ if (errno == EOPNOTSUPP) {
+ mac_free(mac);
+ free(macstr);
+ return (1);
+ }
+ err(1, "mac_set_link(%s, %s)", ftsent->fts_path, macstr);
+ }
+ mac_free(mac);
+ free(macstr);
+ return (0);
+}
+
+struct label_specs *
+new_specs(void)
+{
+ struct label_specs *specs;
+
+ specs = malloc(sizeof(*specs));
+ if (specs == NULL)
+ err(1, "malloc");
+ STAILQ_INIT(&specs->head);
+ return (specs);
+}
diff --git a/usr.sbin/setfmac/setfsmac.8 b/usr.sbin/setfmac/setfsmac.8
new file mode 100644
index 0000000..7d4150f
--- /dev/null
+++ b/usr.sbin/setfmac/setfsmac.8
@@ -0,0 +1,129 @@
+.\" Copyright (c) 2003, 2004 Networks Associates Technology, Inc.
+.\" All rights reserved.
+.\"
+.\" This software was developed for the FreeBSD Project by Chris Costello
+.\" at Safeport Network Services and Network Associates 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 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 February 17, 2004
+.Os
+.Dt SETFSMAC 8
+.Sh NAME
+.Nm setfsmac
+.Nd set MAC label for a file hierarchy
+.Sh SYNOPSIS
+.Nm
+.Op Fl ehqvx
+.Oo Fl f Ar specfile Oc ...
+.Oo Fl s Ar specfile Oc ...
+.Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility accepts a list of specification files as input and sets the MAC
+labels on the specified file system hierarchies.
+Path names specified will be visited in order as given on the command
+line, and each tree will be traversed in pre-order.
+(Generally, it will not be very useful to use relative paths instead of
+absolute paths.)
+Multiple entries matching a single file will be combined and applied in
+a single transaction.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl e
+Treat any file systems encountered which do not support MAC labelling as
+errors, instead of warning and skipping them.
+.It Fl f Ar specfile
+Apply the specifications in
+.Ar specfile
+to the specified paths.
+.\" XXX
+.Bf -emphasis
+NOTE: Only the first entry for each file is applied;
+all others are disregarded and silently dropped.
+.Ef
+Multiple
+.Fl f
+arguments may be specified to include multiple
+specification files.
+.It Fl h
+When a symbolic link is encountered, change the label of the link rather
+than the file the link points to.
+.It Fl q
+Do not print non-fatal warnings during execution.
+.It Fl s Ar specfile
+Apply the specifications in
+.Ar specfile ,
+but assume the specification format is compatible with the SELinux
+.Ar specfile
+format.
+.\" XXX
+.Bf -emphasis
+NOTE: Only the first entry for each file is applied;
+all others are disregarded and silently dropped.
+.Ef
+The prefix
+.Dq Li sebsd/
+will be automatically prepended to the labels in
+.Ar specfile .
+Labels matching
+.Dq Li <<none>>
+will be explicitly not relabeled.
+This permits SEBSD to reuse existing SELinux policy specification files.
+.It Fl v
+Increase the degree of verbosity.
+.It Fl x
+Do not recurse into new file systems when traversing them.
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /usr/share/security/lomac-policy.contexts" -compact
+.It Pa /usr/share/security/lomac-policy.contexts
+Sample specfile containing LOMAC policy entries.
+.El
+.Sh EXAMPLES
+See
+.Sx FILES .
+.Sh SEE ALSO
+.Xr mac 3 ,
+.Xr mac_set_file 3 ,
+.Xr mac_set_link 3 ,
+.Xr mac 4 ,
+.Xr re_format 7 ,
+.Xr getfmac 8 ,
+.Xr setfmac 8 ,
+.Xr mac 9
+.Sh AUTHORS
+This software was contributed to the
+.Fx
+Project by Network Associates 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.
diff --git a/usr.sbin/setpmac/Makefile b/usr.sbin/setpmac/Makefile
new file mode 100644
index 0000000..ae7ee02
--- /dev/null
+++ b/usr.sbin/setpmac/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= setpmac
+MAN= setpmac.8
+
+WARNS?= 6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/setpmac/setpmac.8 b/usr.sbin/setpmac/setpmac.8
new file mode 100644
index 0000000..e84a8bc6
--- /dev/null
+++ b/usr.sbin/setpmac/setpmac.8
@@ -0,0 +1,65 @@
+.\" Copyright (c) 2003 Networks Associates Technology, Inc.
+.\" All rights reserved.
+.\"
+.\" This software was developed for the FreeBSD Project by Chris Costello
+.\" at Safeport Network Services and Network Associates 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 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 14, 2003
+.Os
+.Dt SETPMAC 8
+.Sh NAME
+.Nm setpmac
+.Nd "run a command with a different MAC process label"
+.Sh SYNOPSIS
+.Nm
+.Ar label
+.Ar command
+.Op Ar arg ...
+.Sh DESCRIPTION
+The
+.Nm
+utility forks a new process, attempts to set the label to
+.Ar label
+and if successful, runs
+.Ar command .
+.Sh SEE ALSO
+.Xr mac 4 ,
+.Xr maclabel 7 ,
+.Xr getfmac 8 ,
+.Xr getpmac 8 ,
+.Xr setfmac 8
+.Sh AUTHORS
+This software was contributed to the
+.Fx
+Project by Network Associates 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.
diff --git a/usr.sbin/setpmac/setpmac.c b/usr.sbin/setpmac/setpmac.c
new file mode 100644
index 0000000..bc096bb
--- /dev/null
+++ b/usr.sbin/setpmac/setpmac.c
@@ -0,0 +1,92 @@
+/*-
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by 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/types.h>
+#include <sys/mac.h>
+
+#include <err.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#define MAXELEMENTS 32
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "setpmac [label] [command] [args ...]\n");
+ exit (EX_USAGE);
+}
+
+int
+main(int argc, char *argv[])
+{
+ const char *shell;
+ mac_t label;
+ int error;
+
+
+ if (argc < 3)
+ usage();
+
+ error = mac_from_text(&label, argv[1]);
+ if (error != 0) {
+ perror("mac_from_text");
+ return (-1);
+ }
+
+ error = mac_set_proc(label);
+ if (error != 0) {
+ perror(argv[1]);
+ return (-1);
+ }
+
+ mac_free(label);
+
+ if (argc >= 3) {
+ execvp(argv[2], argv + 2);
+ err(1, "%s", argv[2]);
+ } else {
+ if (!(shell = getenv("SHELL")))
+ shell = _PATH_BSHELL;
+ execlp(shell, shell, "-i", (char *)NULL);
+ err(1, "%s", shell);
+ }
+ /* NOTREACHED */
+}
diff --git a/usr.sbin/sicontrol/Makefile b/usr.sbin/sicontrol/Makefile
new file mode 100644
index 0000000..eb31205
--- /dev/null
+++ b/usr.sbin/sicontrol/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= sicontrol
+MAN= sicontrol.8
+
+CFLAGS+= -I${.CURDIR}/../../sys
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/sicontrol/sicontrol.8 b/usr.sbin/sicontrol/sicontrol.8
new file mode 100644
index 0000000..a3aa87a
--- /dev/null
+++ b/usr.sbin/sicontrol/sicontrol.8
@@ -0,0 +1,109 @@
+.\" $FreeBSD$
+.\"
+.Dd September 26, 1995
+.Dt SICONTROL 8
+.Os
+.Sh NAME
+.Nm sicontrol
+.Nd Specialix SI/XIO driver configuration and debugging
+.Sh SYNOPSIS
+.Nm
+device
+.Ar command Op Ar param ...
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to configure and monitor the SI/XIO device driver.
+.Pp
+The
+.Nm
+utility operates on the specified
+.Ar device
+to indicate which port is to be used.
+.Pp
+The special
+.Ar device
+string `-' is used to indicate the global driver settings instead.
+.Pp
+A
+.Pa /dev/
+is included if necessary.
+.Pp
+The following commands are used for the global settings and should be
+specified with the '-' device name:
+.Bl -tag -width 4n
+.It Cm int_throttle Op Cm value
+Configure the `aggregate interrupt throttle value'.
+The maximum number of host adapter interrupts per second is determined by:
+.Pp
+.Ar "controller CPU clock / (8 * int_throttle)"
+.Pp
+The default value at boot time is 25000.
+The host adapter cpu clock is
+25MHz.
+This gives a maximum interrupt rate of about 125 interrupts per
+second.
+.Pp
+Lowering this value will increase the rate in which the host adapter can
+interrupt the operating system for attention.
+.\"
+.It Cm rxint_throttle Op Cm value
+Configure the receiver interrupt throttle value.
+The default value of 4 at boot time allows an interrupt rate of
+approximately 25.
+.Pp
+Lowering this value will increase the rate in which the host adapter can
+interrupt the operating system to empty the receiver fifos.
+.\"
+.It Cm nport
+Return the number of ports under the control of the device driver.
+.El
+.Pp
+The following commands are used for the individual ports and should be
+specified with a device name from
+.Pa /dev :
+.Bl -tag -width 4n
+.It Cm mstate
+Show the current incoming modem control signals.
+.It Cm ccbstat
+Show the current "ccb" structure for the specified port.
+This is not of
+much use outside of debugging the driver and determining why a port is
+wedged.
+.El
+.Sh FILES
+.Bl -tag -width /dev/si_control -compact
+.It Pa /dev/si_control
+global driver control file for use by
+.Nm
+.It Pa /dev/ttyA*
+terminal control ports
+.It Pa /dev/ttyiA*
+initial termios state devices, for use by
+.Xr stty 1
+.It Pa /dev/ttylA*
+locked termios state devices, for use by
+.Xr stty 1
+.El
+.Sh DIAGNOSTICS
+Generally self explanatory.....
+.Sh SEE ALSO
+.Xr stty 1 ,
+.Xr si 4 ,
+.Xr termios 4 ,
+.Xr tty 4 ,
+.Xr comcontrol 8
+.Sh HISTORY
+The
+.Nm
+utility is loosely based on a utility called
+.Nm siconfig
+which was written by
+.An Andy Rutter Aq andy@acronym.co.uk
+.Pp
+Specialix International do not support this device driver in any way.
+.Sh AUTHORS
+.An Peter Wemm Aq peter@FreeBSD.org
+.Sh BUGS
+Bound to be many...
+:-)
diff --git a/usr.sbin/sicontrol/sicontrol.c b/usr.sbin/sicontrol/sicontrol.c
new file mode 100644
index 0000000..061807d
--- /dev/null
+++ b/usr.sbin/sicontrol/sicontrol.c
@@ -0,0 +1,726 @@
+/*
+ * Device driver for Specialix range (SLXOS) of serial line multiplexors.
+ * SLXOS configuration and debug interface
+ *
+ * Copyright (C) 1990, 1992 Specialix International,
+ * Copyright (C) 1993, Andy Rutter <andy@acronym.co.uk>
+ * Copyright (C) 1995, Peter Wemm
+ *
+ * Derived from: SunOS 4.x version
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notices, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notices, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of Advanced Methods and Tools, nor Specialix
+ * International may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/tty.h>
+
+#define SI_DEBUG
+#include <dev/si/si.h>
+#include <dev/si/sivar.h>
+
+struct lv {
+ char *lv_name;
+ int lv_bit;
+} lv[] = {
+ {"entry", DBG_ENTRY},
+ {"open", DBG_OPEN},
+ {"close", DBG_CLOSE},
+ {"read", DBG_READ},
+ {"write", DBG_WRITE},
+ {"param", DBG_PARAM},
+ {"modem", DBG_MODEM},
+ {"select", DBG_SELECT},
+ {"optim", DBG_OPTIM},
+ {"intr", DBG_INTR},
+ {"start", DBG_START},
+ {"lstart", DBG_LSTART},
+ {"ioctl", DBG_IOCTL},
+ {"fail", DBG_FAIL},
+ {"autoboot", DBG_AUTOBOOT},
+ {"download", DBG_DOWNLOAD},
+ {"drain", DBG_DRAIN},
+ {"poll", DBG_POLL},
+ {0, 0}
+};
+
+static int alldev = 0;
+
+void ccb_stat(int, char **);
+void port_stat(int, char **);
+void debug(int, char **);
+void dostat(void);
+int getnum(char *);
+int islevel(char *);
+int lvls2bits(char *);
+void mstate(int, char **);
+void nport(int, char **);
+void onoff(int, char **, int, char *, char *, int);
+int opencontrol(void);
+void prlevels(int);
+void prusage(int, int);
+void rxint(int, char **);
+void txint(int, char **);
+
+struct opt {
+ char *o_name;
+ void (*o_func)(int, char **);
+} opt[] = {
+ {"debug", debug},
+ {"rxint_throttle", rxint},
+ {"int_throttle", txint},
+ {"nport", nport},
+ {"mstate", mstate},
+ {"ccbstat", ccb_stat},
+ {"portstat", port_stat},
+ {0, 0}
+};
+
+struct stat_list {
+ void (*st_func)(int, char **);
+} stat_list[] = {
+ {mstate},
+ {0}
+};
+
+#define U_DEBUG 0
+#define U_TXINT 1
+#define U_RXINT 2
+#define U_NPORT 3
+#define U_MSTATE 4
+#define U_STAT_CCB 5
+#define U_STAT_PORT 6
+
+#define U_MAX 7
+#define U_ALL -1
+char *usage[] = {
+ "debug [[add|del|set debug_levels] | [off]]\n",
+ "int_throttle [newvalue]\n",
+ "rxint_throttle [newvalue]\n",
+ "nport\n",
+ "mstate\n",
+ "ccbstat\n",
+ "portstat\n",
+ 0
+};
+
+int ctlfd;
+char *Devname;
+struct si_tcsi tc;
+
+int
+main(int argc, char **argv)
+{
+ struct opt *op;
+ void (*func)(int, char **) = NULL;
+
+ if (argc < 2)
+ prusage(U_ALL, 1);
+ Devname = argv[1];
+ if (strcmp(Devname, "-") == 0) {
+ alldev = 1;
+ } else {
+ sidev_t dev;
+ int n;
+ int card, port;
+
+ n = sscanf(Devname, "%d:%d", &card, &port);
+ if (n != 2)
+ errx(1, "Devname must be in form card:port. eg: 0:7");
+ dev.sid_card = card;
+ dev.sid_port = port;
+ tc.tc_dev = dev;
+ }
+ ctlfd = opencontrol();
+ if (argc == 2) {
+ dostat();
+ exit(0);
+ }
+
+ argc--; argv++;
+ for (op = opt; op->o_name; op++) {
+ if (strcmp(argv[1], op->o_name) == 0) {
+ func = op->o_func;
+ break;
+ }
+ }
+ if (func == NULL)
+ prusage(U_ALL, 1);
+
+ argc -= 2;
+ argv += 2;
+ (*func)(argc, argv);
+ exit(0);
+}
+
+int
+opencontrol(void)
+{
+ int fd;
+
+ fd = open(CONTROLDEV, O_RDWR|O_NDELAY);
+ if (fd < 0)
+ err(1, "open on %s", CONTROLDEV);
+ return(fd);
+}
+
+/*
+ * Print a usage message - this relies on U_DEBUG==0 and U_BOOT==1.
+ * Don't print the DEBUG usage string unless explicity requested.
+ */
+void
+prusage(int strn, int eflag)
+{
+ char **cp;
+
+ if (strn == U_ALL) {
+ fprintf(stderr, "usage: sicontrol %s", usage[1]);
+ fprintf(stderr, " sicontrol %s", usage[2]);
+ fprintf(stderr, " sicontrol %s", usage[3]);
+ fprintf(stderr, " sicontrol devname %s", usage[4]);
+ for (cp = &usage[5]; *cp; cp++)
+ fprintf(stderr, " sicontrol devname %s", *cp);
+ }
+ else if (strn >= 0 && strn <= U_MAX)
+ fprintf(stderr, "usage: sicontrol devname %s", usage[strn]);
+ else
+ fprintf(stderr, "sicontrol: usage ???\n");
+ exit(eflag);
+}
+
+/* print port status */
+void
+dostat(void)
+{
+ char *av[1], *acp;
+ struct stat_list *stp;
+ struct si_tcsi stc;
+ int donefirst = 0;
+
+ printf("%s: ", alldev ? "ALL" : Devname);
+ acp = malloc(strlen(Devname) + 3);
+ memset(acp, ' ', strlen(Devname));
+ strcat(acp, " ");
+ stc = tc;
+ for (stp = stat_list; stp->st_func != NULL; stp++) {
+ if (donefirst)
+ fputs(acp, stdout);
+ else
+ donefirst++;
+ av[0] = NULL;
+ tc = stc;
+ (*stp->st_func)(-1, av);
+ }
+}
+
+/*
+ * debug
+ * debug [[set|add|del debug_lvls] | [off]]
+ */
+void
+debug(int ac, char **av)
+{
+ int level;
+
+ if (ac > 2)
+ prusage(U_DEBUG, 1);
+ if (alldev) {
+ if (ioctl(ctlfd, TCSIGDBG_ALL, &tc.tc_dbglvl) < 0)
+ err(1, "TCSIGDBG_ALL on %s", Devname);
+ } else {
+ if (ioctl(ctlfd, TCSIGDBG_LEVEL, &tc) < 0)
+ err(1, "TCSIGDBG_LEVEL on %s", Devname);
+ }
+
+ switch (ac) {
+ case 0:
+ printf("%s: debug levels - ", Devname);
+ prlevels(tc.tc_dbglvl);
+ return;
+ case 1:
+ if (strcmp(av[0], "off") == 0) {
+ tc.tc_dbglvl = 0;
+ break;
+ }
+ prusage(U_DEBUG, 1);
+ /* no return */
+ case 2:
+ level = lvls2bits(av[1]);
+ if (strcmp(av[0], "add") == 0)
+ tc.tc_dbglvl |= level;
+ else if (strcmp(av[0], "del") == 0)
+ tc.tc_dbglvl &= ~level;
+ else if (strcmp(av[0], "set") == 0)
+ tc.tc_dbglvl = level;
+ else
+ prusage(U_DEBUG, 1);
+ }
+ if (alldev) {
+ if (ioctl(ctlfd, TCSISDBG_ALL, &tc.tc_dbglvl) < 0)
+ err(1, "TCSISDBG_ALL on %s", Devname);
+ } else {
+ if (ioctl(ctlfd, TCSISDBG_LEVEL, &tc) < 0)
+ err(1, "TCSISDBG_LEVEL on %s", Devname);
+ }
+}
+
+void
+rxint(int ac, char **av)
+{
+ tc.tc_port = 0;
+ switch (ac) {
+ case 0:
+ printf("%s: ", Devname);
+ case -1:
+ if (ioctl(ctlfd, TCSIGRXIT, &tc) < 0)
+ err(1, "TCSIGRXIT");
+ printf("RX interrupt throttle: %d msec\n", tc.tc_int*10);
+ break;
+ case 1:
+ tc.tc_int = getnum(av[0]) / 10;
+ if (tc.tc_int == 0)
+ tc.tc_int = 1;
+ if (ioctl(ctlfd, TCSIRXIT, &tc) < 0)
+ err(1, "TCSIRXIT on %s at %d msec",
+ Devname, tc.tc_int*10);
+ break;
+ default:
+ prusage(U_RXINT, 1);
+ }
+}
+
+void
+txint(int ac, char **av)
+{
+
+ tc.tc_port = 0;
+ switch (ac) {
+ case 0:
+ printf("%s: ", Devname);
+ case -1:
+ if (ioctl(ctlfd, TCSIGIT, &tc) < 0)
+ err(1, "TCSIGIT");
+ printf("aggregate interrupt throttle: %d\n", tc.tc_int);
+ break;
+ case 1:
+ tc.tc_int = getnum(av[0]);
+ if (ioctl(ctlfd, TCSIIT, &tc) < 0)
+ err(1, "TCSIIT on %s at %d", Devname, tc.tc_int);
+ break;
+ default:
+ prusage(U_TXINT, 1);
+ }
+}
+
+void
+onoff(int ac, char **av, int cmd, char *cmdstr, char *prstr, int usage)
+{
+ if (ac > 1)
+ prusage(usage, 1);
+ if (ac == 1) {
+ if (strcmp(av[0], "on") == 0)
+ tc.tc_int = 1;
+ else if (strcmp(av[0], "off") == 0)
+ tc.tc_int = 0;
+ else
+ prusage(usage, 1);
+ } else
+ tc.tc_int = -1;
+ if (ioctl(ctlfd, cmd, &tc) < 0)
+ err(1, "%s on %s", cmdstr, Devname);
+ switch (ac) {
+ case 0:
+ printf("%s: ", Devname);
+ case -1:
+ printf("%s ", prstr);
+ if (tc.tc_int)
+ printf("on\n");
+ else
+ printf("off\n");
+ }
+}
+
+void
+mstate(int ac, char **av)
+{
+ switch (ac) {
+ case 0:
+ printf("%s: ", Devname);
+ case -1:
+ break;
+ default:
+ prusage(U_MSTATE, 1);
+ }
+ if (ioctl(ctlfd, TCSISTATE, &tc) < 0)
+ err(1, "TCSISTATE on %s", Devname);
+ printf("modem bits state - (0x%x)", tc.tc_int);
+ if (tc.tc_int & IP_DCD) printf(" DCD");
+ if (tc.tc_int & IP_DTR) printf(" DTR");
+ if (tc.tc_int & IP_RTS) printf(" RTS");
+ printf("\n");
+}
+
+void
+nport(int ac, char **av)
+{
+ int ports;
+
+ if (ac != 0)
+ prusage(U_NPORT, 1);
+ if (ioctl(ctlfd, TCSIPORTS, &ports) < 0)
+ err(1, "TCSIPORTS on %s", Devname);
+ printf("SLXOS: total of %d ports\n", ports);
+}
+
+const char *s_stat(int stat)
+{
+ switch (stat) {
+ case IDLE_OPEN: return "IDLE_OPEN";
+ case LOPEN: return "LOPEN";
+ case MOPEN: return "MOPEN";
+ case MPEND: return "MPEND";
+ case CONFIG: return "CONFIG";
+ case CLOSE: return "CLOSE";
+ case SBREAK: return "SBREAK";
+ case EBREAK: return "EBREAK";
+ case IDLE_CLOSE:return "IDLE_CLOSE";
+ case IDLE_BREAK:return "IDLE_BREAK";
+ case FCLOSE: return "FCLOSE";
+ case RESUME: return "RESUME";
+ case WFLUSH: return "WFLUSH";
+ case RFLUSH: return "RFLUSH";
+ default: return "??";
+ }
+}
+const char *s_mr1(int mr1)
+{
+ static char msg[200];
+
+ sprintf(msg, "%dbit, %s, parity:[", 5 + (mr1 & MR1_8_BITS), mr1 & MR1_ODD ? "odd" : "even");
+ if (mr1 & MR1_WITH)
+ strcat(msg, "with;");
+ if (mr1 & MR1_FORCE)
+ strcat(msg, "force;");
+ if (mr1 & MR1_NONE)
+ strcat(msg, "none;");
+ if (mr1 & MR1_SPECIAL)
+ strcat(msg, "special;");
+ strcpy(msg + strlen(msg) - 1, "]");
+ sprintf(msg + strlen(msg), ", err: %s", mr1 & MR1_BLOCK ? "block" : "none");
+ sprintf(msg + strlen(msg), ", cts: %s", mr1 & MR1_CTSCONT ? "auto" : "none");
+ return (msg);
+}
+const char *s_mr2(int mr2)
+{
+ static char msg[200];
+
+ switch (mr2 & 0xf) {
+ case MR2_1_STOP: strcpy(msg, "1stop"); break;
+ case MR2_2_STOP: strcpy(msg, "2stop"); break;
+ default: sprintf(msg, "??stop (0x%x)", mr2 & 0xf); break;
+ }
+ if (mr2 & MR2_RTSCONT) strcat(msg, ", rtscont");
+ if (mr2 & MR2_CTSCONT) strcat(msg, ", ctscont");
+ switch (mr2 & 0xc0) {
+ case MR2_NORMAL: strcat(msg, ", mode:normal"); break;
+ case MR2_AUTO: strcat(msg, ", mode:auto"); break;
+ case MR2_LOCAL: strcat(msg, ", mode:local"); break;
+ case MR2_REMOTE: strcat(msg, ", mode:remote"); break;
+ }
+ return (msg);
+}
+const char *s_clk(int clk)
+{
+ switch (clk & 0xf) {
+ case 0x0: return "75";
+ case 0x1: return "110/115200";
+ case 0x2: return "38400";
+ case 0x3: return "150";
+ case 0x4: return "300";
+ case 0x5: return "600";
+ case 0x6: return "1200";
+ case 0x7: return "2000";
+ case 0x8: return "2400";
+ case 0x9: return "4800";
+ case 0xa: return "7200";
+ case 0xb: return "9600";
+ case 0xc: return "19200";
+ case 0xd: return "57600";
+ case 0xe: return "?0xe";
+ case 0xf: return "?0xf";
+ }
+ return ("gcc sucks");
+}
+const char *s_op(int op)
+{
+ static char msg[200];
+
+ sprintf(msg, "cts:%s", (op & OP_CTS) ? "on" : "off");
+ sprintf(msg + strlen(msg), ", dsr:%s", (op & OP_DSR) ? "on" : "off");
+ return (msg);
+}
+
+const char *s_ip(int ip)
+{
+ static char msg[200];
+
+ sprintf(msg, "rts:%s", (ip & IP_RTS) ? "on" : "off");
+ sprintf(msg + strlen(msg), ", dcd:%s", (ip & IP_DCD) ? "on" : "off");
+ sprintf(msg + strlen(msg), ", dtr:%s", (ip & IP_DTR) ? "on" : "off");
+ sprintf(msg + strlen(msg), ", ri:%s", (ip & IP_RI) ? "on" : "off");
+ return (msg);
+}
+
+const char *s_state(int state)
+{
+ return (state & ST_BREAK ? "break:on" : "break:off");
+}
+
+const char *s_prtcl(int pr)
+{
+ static char msg[200];
+
+ sprintf(msg, "tx xon any:%s", (pr & SP_TANY) ? "on" : "off");
+ sprintf(msg + strlen(msg), ", tx xon/xoff:%s", (pr & SP_TXEN) ? "on" : "off");
+ sprintf(msg + strlen(msg), ", cooking:%s", (pr & SP_CEN) ? "on" : "off");
+ sprintf(msg + strlen(msg), ", rx xon/xoff:%s", (pr & SP_RXEN) ? "on" : "off");
+ sprintf(msg + strlen(msg), ", dcd/dsr check:%s", (pr & SP_DCEN) ? "on" : "off");
+ sprintf(msg + strlen(msg), ", parity check:%s", (pr & SP_PAEN) ? "on" : "off");
+ return (msg);
+}
+const char *s_break(int br)
+{
+ static char msg[200];
+
+ sprintf(msg, "ignore rx brk:%s", (br & BR_IGN) ? "on" : "off");
+ sprintf(msg + strlen(msg), ", brk interrupt:%s", (br & BR_INT) ? "on" : "off");
+ sprintf(msg + strlen(msg), ", parmrking:%s", (br & BR_PARMRK) ? "on" : "off");
+ sprintf(msg + strlen(msg), ", parign:%s", (br & BR_PARIGN) ? "on" : "off");
+ return (msg);
+}
+
+const char *
+s_xstat(int xs)
+{
+ static char msg[200];
+
+ msg[0] = 0;
+ /* MTA definitions, not TA */
+ if (xs & 0x01) strcat(msg, "TION "); /* Tx interrupts on (MTA only) */
+ if (xs & 0x02) strcat(msg, "RTSEN "); /* RTS FLOW enabled (MTA only) */
+ if (xs & 0x04) strcat(msg, "RTSLOW "); /* XOFF received (TA only) */
+ if (xs & 0x08) strcat(msg, "RXEN "); /* Rx XON/XOFF enabled */
+ if (xs & 0x10) strcat(msg, "ANYXO "); /* XOFF pending/sent or RTS dropped */
+ if (xs & 0x20) strcat(msg, "RXSE "); /* Rx XOFF sent */
+ if (xs & 0x40) strcat(msg, "NPEND "); /* Rx XON pending or XOFF pending */
+ if (xs & 0x40) strcat(msg, "FPEND "); /* Rx XOFF pending */
+ return (msg);
+}
+
+const char *
+s_cstat(int cs)
+{
+ static char msg[200];
+
+ msg[0] = 0;
+ /* MTA definitions, not TA */
+ if (cs & 0x01) strcat(msg, "TEMR "); /* Tx empty requested (MTA only) */
+ if (cs & 0x02) strcat(msg, "TEMA "); /* Tx empty acked (MTA only) */
+ if (cs & 0x04) strcat(msg, "EN "); /* Cooking enabled (on MTA means port is also || */
+ if (cs & 0x08) strcat(msg, "HIGH "); /* Buffer previously hit high water */
+ if (cs & 0x10) strcat(msg, "CTSEN "); /* CTS automatic flow-control enabled */
+ if (cs & 0x20) strcat(msg, "DCDEN "); /* DCD/DTR checking enabled */
+ if (cs & 0x40) strcat(msg, "BREAK "); /* Break detected */
+ if (cs & 0x80) strcat(msg, "RTSEN "); /* RTS automatic flow control enabled (MTA only) */
+ return (msg);
+}
+
+void
+ccb_stat(int ac, char **av)
+{
+ struct si_pstat sip;
+#define CCB sip.tc_ccb
+
+ if (ac != 0)
+ prusage(U_STAT_CCB, 1);
+ sip.tc_dev = tc.tc_dev;
+ if (ioctl(ctlfd, TCSI_CCB, &sip) < 0)
+ err(1, "TCSI_CCB on %s", Devname);
+ printf("%s: ", Devname);
+
+ /* WORD next - Next Channel */
+ /* WORD addr_uart - Uart address */
+ /* WORD module - address of module struct */
+ printf("\tuart_type 0x%x\n", CCB.type); /* BYTE type - Uart type */
+ /* BYTE fill - */
+ printf("\tx_status 0x%x %s\n", CCB.x_status, s_xstat(CCB.x_status)); /* BYTE x_status - XON / XOFF status */
+ printf("\tc_status 0x%x %s\n", CCB.c_status, s_cstat(CCB.c_status)); /* BYTE c_status - cooking status */
+ printf("\thi_rxipos 0x%x\n", CCB.hi_rxipos); /* BYTE hi_rxipos - stuff into rx buff */
+ printf("\thi_rxopos 0x%x\n", CCB.hi_rxopos); /* BYTE hi_rxopos - stuff out of rx buffer */
+ printf("\thi_txopos 0x%x\n", CCB.hi_txopos); /* BYTE hi_txopos - Stuff into tx ptr */
+ printf("\thi_txipos 0x%x\n", CCB.hi_txipos); /* BYTE hi_txipos - ditto out */
+ printf("\thi_stat 0x%x %s\n", CCB.hi_stat, s_stat(CCB.hi_stat));/* BYTE hi_stat - Command register */
+ printf("\tdsr_bit 0x%x\n", CCB.dsr_bit); /* BYTE dsr_bit - Magic bit for DSR */
+ printf("\ttxon 0x%x\n", CCB.txon); /* BYTE txon - TX XON char */
+ printf("\ttxoff 0x%x\n", CCB.txoff); /* BYTE txoff - ditto XOFF */
+ printf("\trxon 0x%x\n", CCB.rxon); /* BYTE rxon - RX XON char */
+ printf("\trxoff 0x%x\n", CCB.rxoff); /* BYTE rxoff - ditto XOFF */
+ printf("\thi_mr1 0x%x %s\n", CCB.hi_mr1, s_mr1(CCB.hi_mr1)); /* BYTE hi_mr1 - mode 1 image */
+ printf("\thi_mr2 0x%x %s\n", CCB.hi_mr2, s_mr2(CCB.hi_mr2)); /* BYTE hi_mr2 - mode 2 image */
+ printf("\thi_csr 0x%x in:%s out:%s\n", CCB.hi_csr, s_clk(CCB.hi_csr >> 4), s_clk(CCB.hi_csr)); /* BYTE hi_csr - clock register */
+ printf("\thi_op 0x%x %s\n", CCB.hi_op, s_op(CCB.hi_op)); /* BYTE hi_op - Op control */
+ printf("\thi_ip 0x%x %s\n", CCB.hi_ip, s_ip(CCB.hi_ip)); /* BYTE hi_ip - Input pins */
+ printf("\thi_state 0x%x %s\n", CCB.hi_state, s_state(CCB.hi_state)); /* BYTE hi_state - status */
+ printf("\thi_prtcl 0x%x %s\n", CCB.hi_prtcl, s_prtcl(CCB.hi_prtcl)); /* BYTE hi_prtcl - Protocol */
+ printf("\thi_txon 0x%x\n", CCB.hi_txon); /* BYTE hi_txon - host copy tx xon stuff */
+ printf("\thi_txoff 0x%x\n", CCB.hi_txoff); /* BYTE hi_txoff - */
+ printf("\thi_rxon 0x%x\n", CCB.hi_rxon); /* BYTE hi_rxon - */
+ printf("\thi_rxoff 0x%x\n", CCB.hi_rxoff); /* BYTE hi_rxoff - */
+ printf("\tclose_prev 0x%x\n", CCB.close_prev); /* BYTE close_prev - Was channel previously closed */
+ printf("\thi_break 0x%x %s\n", CCB.hi_break, s_break(CCB.hi_break)); /* BYTE hi_break - host copy break process */
+ printf("\tbreak_state 0x%x\n", CCB.break_state); /* BYTE break_state - local copy ditto */
+ printf("\thi_mask 0x%x\n", CCB.hi_mask); /* BYTE hi_mask - Mask for CS7 etc. */
+ printf("\tmask_z280 0x%x\n", CCB.mask_z280); /* BYTE mask_z280 - Z280's copy */
+ /* BYTE res[0x60 - 36] - */
+ /* BYTE hi_txbuf[SLXOS_BUFFERSIZE] - */
+ /* BYTE hi_rxbuf[SLXOS_BUFFERSIZE] - */
+ /* BYTE res1[0xA0] - */
+}
+
+const char *sp_state(int st)
+{
+
+ if (st & SS_LSTART)
+ return("lstart ");
+ else
+ return("");
+}
+
+void
+port_stat(int ac, char **av)
+{
+ struct si_pstat sip;
+#define PRT sip.tc_siport
+
+ if (ac != 0)
+ prusage(U_STAT_PORT, 1);
+ sip.tc_dev = tc.tc_dev;
+ if (ioctl(ctlfd, TCSI_PORT, &sip) < 0)
+ err(1, "TCSI_PORT on %s", Devname);
+ printf("%s: ", Devname);
+
+ printf("\tsp_pend 0x%x %s\n", PRT.sp_pend, s_stat(PRT.sp_pend));
+ printf("\tsp_last_hi_ip 0x%x %s\n", PRT.sp_last_hi_ip, s_ip(PRT.sp_last_hi_ip));
+ printf("\tsp_state 0x%x %s\n", PRT.sp_state, sp_state(PRT.sp_state));
+ printf("\tsp_delta_overflows 0x%d\n", PRT.sp_delta_overflows);
+}
+
+int
+islevel(char *tk)
+{
+ struct lv *lvp;
+ char *acp;
+
+ for (acp = tk; *acp; acp++)
+ if (isupper(*acp))
+ *acp = tolower(*acp);
+ for (lvp = lv; lvp->lv_name; lvp++)
+ if (strcmp(lvp->lv_name, tk) == 0)
+ return(lvp->lv_bit);
+ return(0);
+}
+
+/*
+ * Convert a string consisting of tokens separated by white space, commas
+ * or `|' into a bitfield - flag any unrecognised tokens.
+ */
+int
+lvls2bits(char *str)
+{
+ int i, bits = 0;
+ int errflag = 0;
+ char token[20];
+
+ while (sscanf(str, "%[^,| \t]", token) == 1) {
+ str += strlen(token);
+ while (isspace(*str) || *str==',' || *str=='|')
+ str++;
+ if (strcmp(token, "all") == 0)
+ return(0xffffffff);
+ if ((i = islevel(token)) == 0) {
+ warnx("unknown token '%s'", token);
+ errflag++;
+ } else
+ bits |= i;
+ }
+ if (errflag)
+ exit(1);
+
+ return(bits);
+}
+
+int
+getnum(char *str)
+{
+ int x;
+ char *acp = str;
+
+ x = 0;
+ while (*acp) {
+ if (!isdigit(*acp))
+ errx(1, "%s is not a number", str);
+ x *= 10;
+ x += (*acp - '0');
+ acp++;
+ }
+ return(x);
+}
+
+void
+prlevels(int x)
+{
+ struct lv *lvp;
+
+ switch (x) {
+ case 0:
+ printf("(none)\n");
+ break;
+ case 0xffffffff:
+ printf("all\n");
+ break;
+ default:
+ for (lvp = lv; lvp->lv_name; lvp++)
+ if (x & lvp->lv_bit)
+ printf(" %s", lvp->lv_name);
+ printf("\n");
+ }
+}
diff --git a/usr.sbin/sliplogin/Makefile b/usr.sbin/sliplogin/Makefile
new file mode 100644
index 0000000..0236f1e
--- /dev/null
+++ b/usr.sbin/sliplogin/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 7/19/93
+# $FreeBSD$
+
+PROG= sliplogin
+MAN= sliplogin.8
+BINOWN= root
+BINGRP= network
+BINMODE=4550
+PRECIOUSPROG=
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/sliplogin/pathnames.h b/usr.sbin/sliplogin/pathnames.h
new file mode 100644
index 0000000..e318f92
--- /dev/null
+++ b/usr.sbin/sliplogin/pathnames.h
@@ -0,0 +1,44 @@
+/*-
+ * 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.
+ *
+ * From: @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ * $FreeBSD$
+ */
+
+#ifndef COMPAT
+#include <paths.h>
+#else
+#define _PATH_DEVNULL "/dev/null"
+#endif
+
+#define _PATH_ACCESS "/etc/sliphome/slip.hosts"
+#define _PATH_SLPARMS "/etc/sliphome/slip.slparms"
+#define _PATH_SLIP_LOGIN "/etc/sliphome/slip.login"
+#define _PATH_SLIP_LOGOUT "/etc/sliphome/slip.logout"
+#define _PATH_DEBUG "/tmp/sliplogin.XXXXXX"
+
diff --git a/usr.sbin/sliplogin/sliplogin.8 b/usr.sbin/sliplogin/sliplogin.8
new file mode 100644
index 0000000..1f6960b
--- /dev/null
+++ b/usr.sbin/sliplogin/sliplogin.8
@@ -0,0 +1,317 @@
+.\" 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.
+.\"
+.\" @(#)sliplogin.8 8.2 (Berkeley) 1/5/94
+.\" $FreeBSD$
+.\"
+.Dd January 5, 1994
+.Dt SLIPLOGIN 8
+.Os
+.Sh NAME
+.Nm sliplogin
+.Nd attach a serial line network interface
+.Sh SYNOPSIS
+.Nm
+.Op Ar loginname Op Ar device
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to turn the terminal line on standard input (or
+.Ar device )
+into a Serial Line IP
+.Pq Tn SLIP
+link to a remote host.
+To do this, the program
+searches the file
+.Pa /etc/sliphome/slip.hosts
+for an entry matching
+.Ar loginname
+(which defaults to the current login name if omitted).
+If a matching entry is found, the line is configured appropriately
+for slip (8-bit transparent i/o) and converted to
+.Tn SLIP
+line discipline using the optional line discipline parameters.
+.Pp
+The optional line discipline parameters consist of one or more of
+the following;
+.Sq normal ,
+.Sq compress ,
+.Sq noicmp ,
+or
+.Sq autocomp
+which correspond respectively to
+.Sq use normal line discipline
+(no header compression),
+.Sq enable VJ header compression ,
+.Sq throw away ICMP packets ,
+and
+.Sq auto enable VJ header compression
+(only if the remote end of the link also supports it).
+.Pp
+Then a shell script is invoked to initialize the slip
+interface with the appropriate local and remote
+.Tn IP
+address,
+netmask, etc.
+.Pp
+The usual initialization script is
+.Pa /etc/sliphome/slip.login
+but, if particular hosts need special initialization, the file
+.Pa /etc/sliphome/slip.login. Ns Ar loginname
+will be executed instead if it exists.
+The script is invoked with the parameters
+.Bl -tag -width slipunit
+.It Em slipunit
+The unit number of the slip interface assigned to this line.
+E.g.,
+.Sy 0
+for
+.Sy sl0 .
+.It Em speed
+The speed of the line.
+.It Em args
+The arguments from the
+.Pa /etc/sliphome/slip.hosts
+entry, in order starting with
+.Ar loginname .
+.El
+.Pp
+Only the super-user may attach a network interface.
+The interface is
+automatically detached when the other end hangs up or the
+.Nm
+process dies.
+If the kernel slip
+module has been configured for it, all routes through that interface will
+also disappear at the same time.
+If there is other processing a site
+would like done on hangup, the file
+.Pa /etc/sliphome/slip.logout
+or
+.Pa /etc/sliphome/slip.logout. Ns Ar loginname
+is executed if it exists.
+It is given the same arguments as the login script.
+.Ss Format of /etc/sliphome/slip.hosts
+Comments (lines starting with a `#') and blank lines (or started
+with space) are ignored.
+Other lines must start with a
+.Ar loginname
+but the remaining arguments can be whatever is appropriate for the
+.Pa slip.login
+file that will be executed for that name.
+Arguments are separated by white space and follow normal
+.Xr sh 1
+quoting conventions (however,
+.Ar loginname
+cannot be quoted).
+Usually, lines have the form
+.Bd -literal -offset indent
+loginname local-address remote-address netmask opt-args
+.Ed
+.Pp
+where
+.Em local-address
+and
+.Em remote-address
+are the IP host names or addresses of the local and remote ends of the
+slip line and
+.Em netmask
+is the appropriate IP netmask.
+These arguments are passed
+directly to
+.Xr ifconfig 8 .
+.Em Opt-args
+are optional arguments used to configure the line.
+.Sh FreeBSD Additions
+An additional SLIP configuration file (if present) is
+.Pa /etc/sliphome/slip.slparms .
+If particular hosts need different configurations, the file
+.Pa /etc/sliphome/slip.slparms. Ns Ar loginname
+will be parsed instead if it exists.
+.Ss Format of /etc/sliphome/slip.slparms*
+Comments (lines starting with a `#') and blank lines (or started with
+space) are ignored.
+This file contains from one to three numeric parameters separated with spaces,
+in order:
+.Ar keepalive ,
+.Ar outfill
+and
+.Ar slunit .
+.Bl -tag -width keepalive
+.It Ar keepalive
+Set SLIP "keep alive" timeout in seconds.
+If FRAME_END is not received in
+this amount of time,
+.Nm
+closes the line and exits.
+The default value is no timeout (zero).
+.It 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 (zero).
+.It Ar slunit
+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.
+.El
+.Pp
+If latter two parameters are omitted, they will not affect the
+corresponding SLIP configuration.
+If any of first two parameters is equal to zero, it will not affect
+the corresponding SLIP configuration.
+.Sh FILES
+.Bl -tag -width indent
+.It Pa /etc/sliphome/slip.hosts
+list of host login names and parameters.
+.It Pa /etc/sliphome/slip.login
+script executed when a connection is made.
+.It Pa /etc/sliphome/slip.login. Ns Ar loginname
+script executed when a connection is made by
+.Ar loginname .
+.It Pa /etc/sliphome/slip.logout
+script executed when a connection is lost.
+.It Pa /etc/sliphome/slip.logout. Ns Ar loginname
+script executed when a connection is lost by
+.Ar loginname .
+.It Pa /etc/sliphome/slip.slparms
+extra parameters file.
+.It Pa /etc/sliphome/slip.slparms. Ns Ar loginname
+extra parameters file for
+.Ar loginname .
+.It Pa /var/run/ Ns Ar ttyXn Ns Pa .if
+contains the name of the network interface used by the sliplogin process on
+.Ar ttyXn .
+.It Pa /var/run/ Ns Ar slX Ns Pa .pid
+contains the PID of the sliplogin process which is using interface
+.Ar slX .
+.El
+.Sh EXAMPLES
+The normal use of
+.Nm
+is to create a
+.Pa /etc/passwd
+entry for each legal, remote slip site with
+.Nm
+as the shell for that entry.
+E.g.,
+.Bd -literal
+Sfoo:ikhuy6:2010:1:slip line to foo:/tmp:/usr/sbin/sliplogin
+.Ed
+.Pp
+(Our convention is to name the account used by remote host
+.Ar hostname
+as
+.Em Shostname . )
+Then an entry is added to
+.Pa slip.hosts
+that looks like:
+.Pp
+.Bd -literal -offset indent -compact
+Sfoo `hostname` foo netmask
+.Ed
+.Pp
+where
+.Em `hostname`
+will be evaluated by
+.Xr sh 1
+to the local host name and
+.Em netmask
+is the local host IP netmask.
+.Pp
+Note that
+.Nm
+must be setuid to root and, while not a security hole, moral defectives
+can use it to place terminal lines in an unusable state and/or deny
+access to legitimate users of a remote slip line.
+To prevent this,
+.Nm
+is installed as user
+.Em root ,
+group
+.Em network
+and mode 4550 so that only members of group
+.Em network
+may run
+.Nm .
+The system administrator should make sure that all legitimate users
+are a member of the correct group.
+.Sh DIAGNOSTICS
+The
+.Nm
+utility logs various information to the system log daemon,
+.Xr syslogd 8 ,
+with a facility code of
+.Em daemon .
+The messages are listed here, grouped by severity level.
+.Pp
+.Sy Error Severity
+.Bl -tag -width Ds -compact
+.It Sy ioctl (TCGETS): Em reason
+A
+.Dv TCGETS
+.Fn ioctl
+to get the line parameters failed.
+.Pp
+.It Sy ioctl (TCSETS): Em reason
+A
+.Dv TCSETS
+.Fn ioctl
+to set the line parameters failed.
+.Pp
+.It Sy /etc/sliphome/slip.hosts: Em reason
+The
+.Pa /etc/sliphome/slip.hosts
+file could not be opened.
+.Pp
+.It Sy access denied for Em user
+No entry for
+.Em user
+was found in
+.Pa /etc/sliphome/slip.hosts .
+.El
+.Pp
+.Sy Notice Severity
+.Bl -tag -width Ds -compact
+.It Sy "attaching slip unit" Em unit Sy for Ar loginname
+.Tn SLIP
+unit
+.Em unit
+was successfully attached.
+.El
+.Sh SEE ALSO
+.Xr slattach 8 ,
+.Xr syslogd 8
+.Pp
+.Pa /usr/share/examples/sliplogin
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.3 Reno .
diff --git a/usr.sbin/sliplogin/sliplogin.c b/usr.sbin/sliplogin/sliplogin.c
new file mode 100644
index 0000000..dafdfd4
--- /dev/null
+++ b/usr.sbin/sliplogin/sliplogin.c
@@ -0,0 +1,544 @@
+/*-
+ * 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.
+ */
+
+#ifndef lint
+static 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[] = "@(#)sliplogin.c 8.2 (Berkeley) 2/1/94";
+static char rscid[] = "@(#)$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * sliplogin.c
+ * [MUST BE RUN SUID, SLOPEN DOES A SUSER()!]
+ *
+ * This program initializes its own tty port to be an async TCP/IP interface.
+ * It sets the line discipline to slip, invokes a shell script to initialize
+ * the network interface, then pauses forever waiting for hangup.
+ *
+ * It is a remote descendant of several similar programs with incestuous ties:
+ * - Kirk Smith's slipconf, modified by Richard Johnsson @ DEC WRL.
+ * - slattach, probably by Rick Adams but touched by countless hordes.
+ * - the original sliplogin for 4.2bsd, Doug Kingston the mover behind it.
+ *
+ * There are two forms of usage:
+ *
+ * "sliplogin"
+ * Invoked simply as "sliplogin", the program looks up the username
+ * in the file /etc/slip.hosts.
+ * If an entry is found, the line on fd0 is configured for SLIP operation
+ * as specified in the file.
+ *
+ * "sliplogin IPhostlogin </dev/ttyb"
+ * Invoked by root with a username, the name is looked up in the
+ * /etc/slip.hosts file and if found fd0 is configured as in case 1.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <syslog.h>
+#include <netdb.h>
+
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <net/slip.h>
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <paths.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include "pathnames.h"
+
+extern char **environ;
+
+static char *restricted_environ[] = {
+ "PATH=" _PATH_STDPATH,
+ NULL
+};
+
+int unit;
+int slip_mode;
+speed_t speed;
+int uid;
+int keepal;
+int outfill;
+int slunit;
+char loginargs[BUFSIZ];
+char loginfile[MAXPATHLEN];
+char loginname[BUFSIZ];
+static char raddr[32]; /* remote address */
+char ifname[IFNAMSIZ]; /* interface name */
+static char pidfilename[MAXPATHLEN]; /* name of pid file */
+static char iffilename[MAXPATHLEN]; /* name of if file */
+static pid_t pid; /* our pid */
+
+char *
+make_ipaddr(void)
+{
+static char address[20] ="";
+struct hostent *he;
+unsigned long ipaddr;
+
+address[0] = '\0';
+if ((he = gethostbyname(raddr)) != NULL) {
+ ipaddr = ntohl(*(long *)he->h_addr_list[0]);
+ sprintf(address, "%lu.%lu.%lu.%lu",
+ ipaddr >> 24,
+ (ipaddr & 0x00ff0000) >> 16,
+ (ipaddr & 0x0000ff00) >> 8,
+ (ipaddr & 0x000000ff));
+ }
+
+return address;
+}
+
+struct slip_modes {
+ char *sm_name;
+ int sm_or_flag;
+ int sm_and_flag;
+} modes[] = {
+ "normal", 0 , 0 ,
+ "compress", IFF_LINK0, IFF_LINK2,
+ "noicmp", IFF_LINK1, 0 ,
+ "autocomp", IFF_LINK2, IFF_LINK0,
+};
+
+void
+findid(name)
+ char *name;
+{
+ FILE *fp;
+ static char slopt[5][16];
+ static char laddr[16];
+ static char mask[16];
+ char slparmsfile[MAXPATHLEN];
+ char user[16];
+ char buf[128];
+ int i, j, n;
+
+ environ = restricted_environ; /* minimal protection for system() */
+
+ (void)strncpy(loginname, name, sizeof(loginname)-1);
+ loginname[sizeof(loginname)-1] = '\0';
+
+ if ((fp = fopen(_PATH_ACCESS, "r")) == NULL) {
+ accfile_err:
+ syslog(LOG_ERR, "%s: %m\n", _PATH_ACCESS);
+ exit(1);
+ }
+ while (fgets(loginargs, sizeof(loginargs) - 1, fp)) {
+ if (ferror(fp))
+ goto accfile_err;
+ if (loginargs[0] == '#' || isspace(loginargs[0]))
+ continue;
+ n = sscanf(loginargs, "%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s\n",
+ user, laddr, raddr, mask, slopt[0], slopt[1],
+ slopt[2], slopt[3], slopt[4]);
+ if (n < 4) {
+ syslog(LOG_ERR, "%s: wrong format\n", _PATH_ACCESS);
+ exit(1);
+ }
+ if (strcmp(user, name) != 0)
+ continue;
+
+ (void) fclose(fp);
+
+ slip_mode = 0;
+ for (i = 0; i < n - 4; i++) {
+ for (j = 0; j < sizeof(modes)/sizeof(struct slip_modes);
+ j++) {
+ if (strcmp(modes[j].sm_name, slopt[i]) == 0) {
+ slip_mode |= (modes[j].sm_or_flag);
+ slip_mode &= ~(modes[j].sm_and_flag);
+ break;
+ }
+ }
+ }
+
+ /*
+ * we've found the guy we're looking for -- see if
+ * there's a login file we can use. First check for
+ * one specific to this host. If none found, try for
+ * a generic one.
+ */
+ (void)snprintf(loginfile, sizeof(loginfile), "%s.%s",
+ _PATH_SLIP_LOGIN, name);
+ if (access(loginfile, R_OK|X_OK) != 0) {
+ (void)strncpy(loginfile, _PATH_SLIP_LOGIN,
+ sizeof(loginfile) - 1);
+ loginfile[sizeof(loginfile) - 1] = '\0';
+ if (access(loginfile, R_OK|X_OK)) {
+ syslog(LOG_ERR,
+ "access denied for %s - no %s\n",
+ name, _PATH_SLIP_LOGIN);
+ exit(5);
+ }
+ }
+ (void)snprintf(slparmsfile, sizeof(slparmsfile), "%s.%s", _PATH_SLPARMS, name);
+ if (access(slparmsfile, R_OK|X_OK) != 0) {
+ (void)strncpy(slparmsfile, _PATH_SLPARMS, sizeof(slparmsfile)-1);
+ slparmsfile[sizeof(slparmsfile)-1] = '\0';
+ if (access(slparmsfile, R_OK|X_OK))
+ *slparmsfile = '\0';
+ }
+ keepal = outfill = 0;
+ slunit = -1;
+ if (*slparmsfile) {
+ if ((fp = fopen(slparmsfile, "r")) == NULL) {
+ slfile_err:
+ syslog(LOG_ERR, "%s: %m\n", slparmsfile);
+ exit(1);
+ }
+ n = 0;
+ while (fgets(buf, sizeof(buf) - 1, fp) != NULL) {
+ if (ferror(fp))
+ goto slfile_err;
+ if (buf[0] == '#' || isspace(buf[0]))
+ continue;
+ n = sscanf(buf, "%d %d %d", &keepal, &outfill, &slunit);
+ if (n < 1) {
+ slwrong_fmt:
+ syslog(LOG_ERR, "%s: wrong format\n", slparmsfile);
+ exit(1);
+ }
+ (void) fclose(fp);
+ break;
+ }
+ if (n == 0)
+ goto slwrong_fmt;
+ }
+
+ return;
+ }
+ syslog(LOG_ERR, "SLIP access denied for %s\n", name);
+ exit(4);
+ /* NOTREACHED */
+}
+
+char *
+sigstr(s)
+ int s;
+{
+ static char buf[32];
+
+ switch (s) {
+ case SIGHUP: return("HUP");
+ case SIGINT: return("INT");
+ case SIGQUIT: return("QUIT");
+ case SIGILL: return("ILL");
+ case SIGTRAP: return("TRAP");
+ case SIGIOT: return("IOT");
+ case SIGEMT: return("EMT");
+ case SIGFPE: return("FPE");
+ case SIGKILL: return("KILL");
+ case SIGBUS: return("BUS");
+ case SIGSEGV: return("SEGV");
+ case SIGSYS: return("SYS");
+ case SIGPIPE: return("PIPE");
+ case SIGALRM: return("ALRM");
+ case SIGTERM: return("TERM");
+ case SIGURG: return("URG");
+ case SIGSTOP: return("STOP");
+ case SIGTSTP: return("TSTP");
+ case SIGCONT: return("CONT");
+ case SIGCHLD: return("CHLD");
+ case SIGTTIN: return("TTIN");
+ case SIGTTOU: return("TTOU");
+ case SIGIO: return("IO");
+ case SIGXCPU: return("XCPU");
+ case SIGXFSZ: return("XFSZ");
+ case SIGVTALRM: return("VTALRM");
+ case SIGPROF: return("PROF");
+ case SIGWINCH: return("WINCH");
+#ifdef SIGLOST
+ case SIGLOST: return("LOST");
+#endif
+ case SIGUSR1: return("USR1");
+ case SIGUSR2: return("USR2");
+ }
+ (void)snprintf(buf, sizeof(buf), "sig %d", s);
+ return(buf);
+}
+
+void
+hup_handler(s)
+ int s;
+{
+ char logoutfile[MAXPATHLEN];
+
+ (void) close(0);
+ seteuid(0);
+ (void)snprintf(logoutfile, sizeof(logoutfile), "%s.%s",
+ _PATH_SLIP_LOGOUT, loginname);
+ if (access(logoutfile, R_OK|X_OK) != 0) {
+ (void)strncpy(logoutfile, _PATH_SLIP_LOGOUT,
+ sizeof(logoutfile) - 1);
+ logoutfile[sizeof(logoutfile) - 1] = '\0';
+ }
+ if (access(logoutfile, R_OK|X_OK) == 0) {
+ char logincmd[2*MAXPATHLEN+32];
+
+ (void) snprintf(logincmd, sizeof(logincmd), "%s %d %ld %s", logoutfile, unit, speed, loginargs);
+ (void) system(logincmd);
+ }
+ syslog(LOG_INFO, "closed %s slip unit %d (%s)\n", loginname, unit,
+ sigstr(s));
+ if (unlink(pidfilename) < 0 && errno != ENOENT)
+ syslog(LOG_WARNING, "unable to delete pid file: %m");
+ if (unlink(iffilename) < 0 && errno != ENOENT)
+ syslog(LOG_WARNING, "unable to delete if file: %m");
+ exit(1);
+ /* NOTREACHED */
+}
+
+
+/* Modify the slip line mode and add any compression or no-icmp flags. */
+void line_flags(unit)
+ int unit;
+{
+ struct ifreq ifr;
+ int s;
+
+ /* 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(1);
+ }
+ sprintf(ifr.ifr_name, "sl%d", unit);
+
+ /* get the flags for the interface */
+ if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
+ syslog(LOG_ERR, "ioctl (SIOCGIFFLAGS): %m");
+ exit(1);
+ }
+
+ /* Assert any compression or no-icmp flags. */
+#define SLMASK (~(IFF_LINK0 | IFF_LINK1 | IFF_LINK2))
+ ifr.ifr_flags &= SLMASK;
+ ifr.ifr_flags |= slip_mode;
+ if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) {
+ syslog(LOG_ERR, "ioctl (SIOCSIFFLAGS): %m");
+ exit(1);
+ }
+ close(s);
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int fd, s, ldisc;
+ char *name;
+ struct termios tios;
+ char logincmd[2*BUFSIZ+32];
+
+ FILE *pidfile; /* pid file */
+ FILE *iffile; /* interfaces file */
+ char *p;
+ int n;
+ char devnam[MAXPATHLEN] = _PATH_TTY; /* Device name */
+
+ if ((name = strrchr(argv[0], '/')) == NULL)
+ name = argv[0];
+ s = getdtablesize();
+ for (fd = 3 ; fd < s ; fd++)
+ (void) close(fd);
+ openlog(name, LOG_PID|LOG_PERROR, LOG_DAEMON);
+ uid = getuid();
+ if (argc > 1) {
+ findid(argv[1]);
+
+ /*
+ * Disassociate from current controlling terminal, if any,
+ * and ensure that the slip line is our controlling terminal.
+ */
+ if (daemon(1, 1)) {
+ syslog(LOG_ERR, "daemon(1, 1): %m");
+ exit(1);
+ }
+ if (argc > 2) {
+ if ((fd = open(argv[2], O_RDWR)) == -1) {
+ syslog(LOG_ERR, "open %s: %m", argv[2]);
+ exit(2);
+ }
+ (void) dup2(fd, 0);
+ if (fd > 2)
+ close(fd);
+ }
+ if (ioctl(0, TIOCSCTTY, 0) == -1) {
+ syslog(LOG_ERR, "ioctl (TIOCSCTTY): %m");
+ exit(1);
+ }
+ if (tcsetpgrp(0, getpid()) < 0) {
+ syslog(LOG_ERR, "tcsetpgrp failed: %m");
+ exit(1);
+ }
+ } else {
+ if ((name = getlogin()) == NULL) {
+ syslog(LOG_ERR, "access denied - login name not found\n");
+ exit(1);
+ }
+ findid(name);
+ }
+ (void) fchmod(0, 0600);
+ (void) fprintf(stderr, "starting slip login for %s\n", loginname);
+ (void) fprintf(stderr, "your address is %s\n\n", make_ipaddr());
+
+ (void) fflush(stderr);
+ sleep(1);
+
+ /* set up the line parameters */
+ if (tcgetattr(0, &tios) < 0) {
+ syslog(LOG_ERR, "tcgetattr: %m");
+ exit(1);
+ }
+ cfmakeraw(&tios);
+ if (tcsetattr(0, TCSAFLUSH, &tios) < 0) {
+ syslog(LOG_ERR, "tcsetattr: %m");
+ exit(1);
+ }
+ speed = cfgetispeed(&tios);
+
+ ldisc = SLIPDISC;
+ if (ioctl(0, TIOCSETD, &ldisc) < 0) {
+ syslog(LOG_ERR, "ioctl(TIOCSETD): %m");
+ exit(1);
+ }
+ if (slunit >= 0 && ioctl(0, SLIOCSUNIT, &slunit) < 0) {
+ syslog(LOG_ERR, "ioctl (SLIOCSUNIT): %m");
+ exit(1);
+ }
+ /* find out what unit number we were assigned */
+ if (ioctl(0, SLIOCGUNIT, &unit) < 0) {
+ syslog(LOG_ERR, "ioctl (SLIOCGUNIT): %m");
+ exit(1);
+ }
+ (void) signal(SIGHUP, hup_handler);
+ (void) signal(SIGTERM, hup_handler);
+
+ if (keepal > 0) {
+ (void) signal(SIGURG, hup_handler);
+ if (ioctl(0, SLIOCSKEEPAL, &keepal) < 0) {
+ syslog(LOG_ERR, "ioctl(SLIOCSKEEPAL): %m");
+ exit(1);
+ }
+ }
+ if (outfill > 0 && ioctl(0, SLIOCSOUTFILL, &outfill) < 0) {
+ syslog(LOG_ERR, "ioctl(SLIOCSOUTFILL): %m");
+ exit(1);
+ }
+
+ /* write pid to file */
+ pid = getpid();
+ (void) sprintf(ifname, "sl%d", unit);
+ (void) sprintf(pidfilename, "%s%s.pid", _PATH_VARRUN, ifname);
+ if ((pidfile = fopen(pidfilename, "w")) != NULL) {
+ fprintf(pidfile, "%d\n", pid);
+ (void) fclose(pidfile);
+ } else {
+ syslog(LOG_ERR, "Failed to create pid file %s: %m",
+ pidfilename);
+ pidfilename[0] = 0;
+ }
+
+ /* write interface unit number to file */
+ p = ttyname(0);
+ if (p)
+ strcpy(devnam, p);
+ for (n = strlen(devnam); n > 0; n--)
+ if (devnam[n] == '/') {
+ n++;
+ break;
+ }
+ (void) sprintf(iffilename, "%s%s.if", _PATH_VARRUN, &devnam[n]);
+ if ((iffile = fopen(iffilename, "w")) != NULL) {
+ fprintf(iffile, "sl%d\n", unit);
+ (void) fclose(iffile);
+ } else {
+ syslog(LOG_ERR, "Failed to create if file %s: %m", iffilename);
+ iffilename[0] = 0;
+ }
+
+
+ syslog(LOG_INFO, "attaching slip unit %d for %s\n", unit, loginname);
+ (void)snprintf(logincmd, sizeof(logincmd), "%s %d %ld %s", loginfile, unit, speed,
+ loginargs);
+ /*
+ * aim stdout and errout at /dev/null so logincmd output won't
+ * babble into the slip tty line.
+ */
+ (void) close(1);
+ if ((fd = open(_PATH_DEVNULL, O_WRONLY)) != 1) {
+ if (fd < 0) {
+ syslog(LOG_ERR, "open %s: %m", _PATH_DEVNULL);
+ exit(1);
+ }
+ (void) dup2(fd, 1);
+ (void) close(fd);
+ }
+ (void) dup2(1, 2);
+
+ /*
+ * Run login and logout scripts as root (real and effective);
+ * current route(8) is setuid root, and checks the real uid
+ * to see whether changes are allowed (or just "route get").
+ */
+ (void) setuid(0);
+ if (s = system(logincmd)) {
+ syslog(LOG_ERR, "%s login failed: exit status %d from %s",
+ loginname, s, loginfile);
+ exit(6);
+ }
+
+ /* Handle any compression or no-icmp flags. */
+ line_flags(unit);
+
+ /* reset uid to users' to allow the user to give a signal. */
+ seteuid(uid);
+ /* twiddle thumbs until we get a signal */
+ while (1)
+ sigpause(0);
+
+ /* NOTREACHED */
+}
diff --git a/usr.sbin/slstat/Makefile b/usr.sbin/slstat/Makefile
new file mode 100644
index 0000000..650e221
--- /dev/null
+++ b/usr.sbin/slstat/Makefile
@@ -0,0 +1,9 @@
+# from: @(#)Makefile 5.6 (Berkeley) 4/23/91
+# $FreeBSD$
+
+PROG= slstat
+MAN= slstat.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/slstat/slstat.8 b/usr.sbin/slstat/slstat.8
new file mode 100644
index 0000000..e85e2a8
--- /dev/null
+++ b/usr.sbin/slstat/slstat.8
@@ -0,0 +1,125 @@
+.\" Copyright (c) 1986 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: @(#)slstat.8 6.8 (Berkeley) 6/20/91
+.\" $FreeBSD$
+.\"
+.Dd October 11, 1996
+.Dt SLSTAT 8
+.Os
+.Sh NAME
+.Nm slstat
+.Nd report serial line IP statistics
+.Sh SYNOPSIS
+.Nm
+.Op Fl i Ar interval
+.Op Fl vr
+.Op Ar unit
+.Sh DESCRIPTION
+The
+.Nm
+utility
+reports certain kernel statistics kept about serial line internet
+protocol traffic.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl i
+Repeat the display indefinitely every
+.Ar interval
+seconds.
+If no
+.Ar interval
+is specified, the default is 5 seconds.
+.It Fl v
+Verbose display of extra fields of information.
+.It Fl r
+Display all values in rate per second rather than amount per interval.
+.It Ar unit
+Is number specifying the
+.Tn SLIP
+interface, or a
+.Tn SLIP
+interface name.
+The default unit is
+.Sy 0
+for interface
+.Sy sl0 .
+.El
+.Pp
+By default,
+.Nm
+displays the following information:
+.Pp
+.Bl -tag -width indent
+.It in
+bytes received
+.It out
+bytes sent
+.It pack
+packets received or sent
+.It comp
+compressed packets received or sent
+.It uncomp
+uncompressed packets received or sent
+.It unknwn
+inbound packets of unknown type
+.It toss
+inbound packets tossed because of error
+.It other
+all other inbound or outbound ip packets
+.It err
+input or output errors
+.It search
+searches for connection state
+.It miss
+times we could not find a connection state
+.It coll
+collisions with end of clists.
+If you get many collisions (more than one or two
+a day) you probably do not have enough clists
+and you should increase
+.Dv nclist
+in param.c.
+.El
+.Sh EXAMPLES
+The command:
+.Dl slstat -i 5
+will print what the system is doing every five
+seconds.
+.Sh SEE ALSO
+.Xr fstat 1 ,
+.Xr netstat 1 ,
+.Xr nfsstat 1 ,
+.Xr ps 1 ,
+.Xr systat 1 ,
+.Xr iostat 8 ,
+.Xr pppstats 8 ,
+.Xr pstat 8
+.Pp
+The sections starting with ``Interpreting system activity'' in
+.%T "Installing and Operating 4.3BSD" .
diff --git a/usr.sbin/slstat/slstat.c b/usr.sbin/slstat/slstat.c
new file mode 100644
index 0000000..ff017f4
--- /dev/null
+++ b/usr.sbin/slstat/slstat.c
@@ -0,0 +1,243 @@
+/*
+ * print serial line IP statistics:
+ * slstat [-i interval] [-v] [interface]
+ *
+ * Copyright (c) 1989, 1990, 1991, 1992 Regents of the University of
+ * California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms 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 University of California, Berkeley. The name of the
+ * University 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 MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Van Jacobson (van@ee.lbl.gov), Dec 31, 1989:
+ * - Initial distribution.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_mib.h>
+#include <net/if_types.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <net/slcompress.h>
+#include <net/if_slvar.h>
+
+static void usage(void);
+static void intpr(void);
+static void catchalarm(int);
+
+#define INTERFACE_PREFIX "sl%d"
+char interface[IFNAMSIZ];
+
+int rflag;
+int vflag;
+unsigned interval = 5;
+int unit;
+int name[6];
+
+int
+main(int argc, char *argv[])
+{
+ int c, i;
+ size_t len;
+ int maxifno;
+ int indx;
+ struct ifmibdata ifmd;
+
+ while ((c = getopt(argc, argv, "vri:")) != -1) {
+ switch(c) {
+ case 'v':
+ ++vflag;
+ break;
+ case 'r':
+ ++rflag;
+ break;
+ case 'i':
+ interval = atoi(optarg);
+ if (interval <= 0)
+ usage();
+ break;
+ default:
+ usage();
+ }
+ }
+ if (optind >= argc)
+ sprintf(interface, INTERFACE_PREFIX, unit);
+ else if (isdigit(argv[optind][0])) {
+ unit = atoi(argv[optind]);
+ if (unit < 0)
+ usage();
+ sprintf(interface, INTERFACE_PREFIX, unit);
+ } else if (strncmp(argv[optind], "sl", 2) == 0
+ && isdigit(argv[optind][2])
+ && sscanf(argv[optind], "sl%d", &unit) == 1) {
+ strncpy(interface, argv[optind], IFNAMSIZ);
+ } else
+ usage();
+
+ name[0] = CTL_NET;
+ name[1] = PF_LINK;
+ name[2] = NETLINK_GENERIC;
+ name[3] = IFMIB_SYSTEM;
+ name[4] = IFMIB_IFCOUNT;
+ len = sizeof maxifno;
+ if (sysctl(name, 5, &maxifno, &len, 0, 0) < 0)
+ err(1, "sysctl net.link.generic.system.ifcount");
+
+ name[3] = IFMIB_IFDATA;
+ name[5] = IFDATA_GENERAL;
+ len = sizeof ifmd;
+ for (i = 1; ; i++) {
+ name[4] = i;
+
+ if (sysctl(name, 6, &ifmd, &len, 0, 0) < 0) {
+ if (errno == ENOENT)
+ continue;
+
+ err(1, "sysctl");
+ }
+ if (strncmp(interface, ifmd.ifmd_name, IFNAMSIZ) == 0
+ && ifmd.ifmd_data.ifi_type == IFT_SLIP) {
+ indx = i;
+ break;
+ }
+ if (i >= maxifno)
+ errx(1, "interface %s does not exist", interface);
+ }
+
+ name[4] = indx;
+ name[5] = IFDATA_LINKSPECIFIC;
+ intpr();
+ exit(0);
+}
+
+#define V(offset) ((line % 20)? ((sc->offset - osc->offset) / \
+ (rflag ? interval : 1)) : sc->offset)
+#define AMT (sizeof(*sc) - 2 * sizeof(sc->sc_comp.tstate))
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: slstat [-i interval] [-vr] [unit]\n");
+ exit(1);
+}
+
+u_char signalled; /* set if alarm goes off "early" */
+
+/*
+ * Print a running summary of interface statistics.
+ * Repeat display every interval seconds, showing statistics
+ * collected over that interval. Assumes that interval is non-zero.
+ * First line printed at top of screen is always cumulative.
+ */
+static void
+intpr()
+{
+ register int line = 0;
+ int oldmask;
+ struct sl_softc *sc, *osc;
+ size_t len;
+
+ sc = (struct sl_softc *)malloc(AMT);
+ osc = (struct sl_softc *)malloc(AMT);
+ bzero((char *)osc, AMT);
+ len = AMT;
+
+ while (1) {
+ if (sysctl(name, 6, sc, &len, 0, 0) < 0 &&
+ (errno != ENOMEM || len != AMT))
+ err(1, "sysctl linkspecific");
+
+ (void)signal(SIGALRM, catchalarm);
+ signalled = 0;
+ (void)alarm(interval);
+
+ if ((line % 20) == 0) {
+ printf("%8.8s %6.6s %6.6s %6.6s %6.6s",
+ "in", "pack", "comp", "uncomp", "unknwn");
+ if (vflag)
+ printf(" %6.6s %6.6s %6.6s",
+ "toss", "other", "err");
+ printf(" | %8.8s %6.6s %6.6s %6.6s %6.6s",
+ "out", "pack", "comp", "uncomp", "other");
+ if (vflag)
+ printf(" %6.6s %6.6s %6.6s %6.6s",
+ "search", "miss", "err", "coll");
+ putchar('\n');
+ }
+ printf("%8lu %6ld %6u %6u %6u",
+ V(sc_ifp->if_ibytes),
+ V(sc_ifp->if_ipackets),
+ V(sc_comp.sls_compressedin),
+ V(sc_comp.sls_uncompressedin),
+ V(sc_comp.sls_errorin));
+ if (vflag)
+ printf(" %6u %6lu %6lu",
+ V(sc_comp.sls_tossed),
+ V(sc_ifp->if_ipackets) -
+ V(sc_comp.sls_compressedin) -
+ V(sc_comp.sls_uncompressedin) -
+ V(sc_comp.sls_errorin),
+ V(sc_ifp->if_ierrors));
+ printf(" | %8lu %6ld %6u %6u %6lu",
+ V(sc_ifp->if_obytes) / (rflag ? interval : 1),
+ V(sc_ifp->if_opackets),
+ V(sc_comp.sls_compressed),
+ V(sc_comp.sls_packets) - V(sc_comp.sls_compressed),
+ V(sc_ifp->if_opackets) - V(sc_comp.sls_packets));
+ if (vflag)
+ printf(" %6u %6u %6lu %6lu",
+ V(sc_comp.sls_searches),
+ V(sc_comp.sls_misses),
+ V(sc_ifp->if_oerrors),
+ V(sc_ifp->if_collisions));
+ putchar('\n');
+ fflush(stdout);
+ line++;
+ oldmask = sigblock(sigmask(SIGALRM));
+ if (! signalled) {
+ sigpause(0);
+ }
+ sigsetmask(oldmask);
+ signalled = 0;
+ (void)alarm(interval);
+ bcopy((char *)sc, (char *)osc, AMT);
+ }
+}
+
+/*
+ * Called if an interval expires before sidewaysintpr has completed a loop.
+ * Sets a flag to not wait for the alarm.
+ */
+static void
+catchalarm(sig)
+ int sig __unused;
+{
+ signalled = 1;
+}
diff --git a/usr.sbin/smbmsg/Makefile b/usr.sbin/smbmsg/Makefile
new file mode 100644
index 0000000..a581b2c
--- /dev/null
+++ b/usr.sbin/smbmsg/Makefile
@@ -0,0 +1,8 @@
+#
+# $FreeBSD$
+
+PROG= smbmsg
+MAN= smbmsg.8
+WARNS= 6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/smbmsg/pathnames.h b/usr.sbin/smbmsg/pathnames.h
new file mode 100644
index 0000000..aff7335
--- /dev/null
+++ b/usr.sbin/smbmsg/pathnames.h
@@ -0,0 +1,29 @@
+/*-
+ * Copyright (C) 2004 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 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$
+ */
+
+#define PATH_DEFAULTSMBDEV "/dev/smb0"
diff --git a/usr.sbin/smbmsg/smbmsg.8 b/usr.sbin/smbmsg/smbmsg.8
new file mode 100644
index 0000000..efc7451
--- /dev/null
+++ b/usr.sbin/smbmsg/smbmsg.8
@@ -0,0 +1,287 @@
+'\" t
+.\" Copyright (c) 2004 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 May 16, 2004
+.Dt SMBMSG 8
+.Os
+.Sh NAME
+.Nm smbmsg
+.Nd "send or receive messages over an SMBus"
+.Sh SYNOPSIS
+.Nm
+.Op Fl f Ar dev
+.Fl p
+.Pp
+.Nm
+.Op Fl f Ar dev
+.Fl s Ar slave
+.Op Fl F Ar fmt
+.Op Fl c Ar cmd
+.Op Fl w
+.Op Fl i Ar incnt
+.Op Fl o Ar outcnt
+.Op Ar outdata ...
+.Sh DESCRIPTION
+The
+.Nm
+utility can be used to send or receive messages over an
+SMBus, see
+.Xr smbus 4 .
+.Pp
+The
+.Nm
+utility has two different modi of operation.
+The first form shown in the synopsis can be used to
+.Dq probe
+the devices on the SMBus.
+This is done by sending each valid device address one
+receive byte, and one quick read message, respectively.
+Devices that respond to these requests will by displayed
+by their device address, followed by the strings
+.Ql r ,
+.Ql w ,
+or
+.Ql rw ,
+for devices that are readable, writeable, or both, readable
+and writeable, respectively.
+The only valid additional option for this modus of operation (besides
+the
+.Fl p
+option that choses the modus) is
+.Fl f Ar dev .
+See below for a description.
+.Pp
+Note that probing the bus is risky, since individual devices could
+perform unwanted actions upon receiving one of the mentioned messages.
+For example, if a particular SMBus device considers
+.Em any
+write operation issued to it as a request to power off the system,
+the probing would trigger this action.
+.Pp
+The second form shown in the synopsis can be used to send or receive
+arbitrary messages to or from individual devices.
+This might be useful to explore individual devices on the SMBus, or
+maybe even to write short shell scripts performing maintenance
+operations on the bus.
+.Pp
+Any data values on the command-line are integer values in the
+range 0 through 255 for byte values, or 0 through 65535 for
+word values.
+They can be specified using standard
+.Ql C
+notation (prefix 0 for octal interpretation, or 0x for
+hexadecimal interpretation).
+.Pp
+Since the low-order bit of the device address of SMBus devices
+selects between read and write operations, only even-numbered
+slave addresses can exist on the bus.
+.Pp
+The options are as follows:
+.Bl -tag -width ".Fl o Ar outcnt"
+.It Fl F Ar fmt
+Specify the
+.Xr printf 3
+format to be used for displaying input data.
+This option is ignored in messages that do not read any input
+from the SMBus device.
+The format defaults to
+.Ql 0x%02x
+for byte input operations, and to
+.Ql 0x%04x
+for word input operations.
+For multi-byte input (block read), the same format is used for
+each individual byte read from the SMBus.
+.It Fl c Ar cmd
+This is the value of the
+.Em command
+byte to be issued as part of the SMBus message.
+.It Fl f Ar dev
+This specifies that
+.Ar dev
+should be used as the connection to the SMBus, rather than the
+default of
+.Pa /dev/smb0 .
+.It Fl i Ar incnt
+An SMBus message should be generated to read
+.Ar incnt
+bytes from the device.
+.It Fl o Ar outcnt
+An SMBus message should be generated to write
+.Ar outcnt
+bytes to the device.
+The data values to write are expected to follow all of the options
+(and their arguments) on the command-line, where the number of data
+bytes must match the
+.Ar outcnt
+value.
+.It Fl p
+This selects the
+.Em probe bus
+modus of operation.
+.It Fl s Ar slave
+The
+.Ar slave
+parameter specifies which SMBus device to connect to.
+This option also selects the
+.Em transfer messages from/to device
+modus of operation, where a slave address is mandatory.
+.It Fl w
+This option specifies that IO operations are word operations,
+rather than byte operations.
+Either
+.Ar incnt ,
+or
+.Ar outcnt
+(or both) must be equal 2 in this case.
+Note that the SMBus byte order is defined to be little-endian
+(low byte first, high byte follows).
+.El
+.Pp
+Not all argument combinations make sense in order to form valid SMBus
+messages.
+If no
+.Fl c Ar cmd
+option has been provided, the following messages can be
+issued:
+.Bd -unfilled -offset indent
+.TS
+l r r.
+\fBmessage incnt outcnt\fR
+quick read 0 \&-
+quick write \&- 0
+receive byte 1 \&-
+send byte \&- 1
+.TE
+.Ed
+.Pp
+Note in particular that specifying 0 as a count value
+has a different meaning than omitting the respective
+option entirely.
+.Pp
+If a command value has been given using the
+.Fl c Ar cmd
+option, the following messages can be generated:
+.Bd -unfilled -offset indent
+.TS
+l l r r.
+\fBmessage \&-w incnt outcnt\fR
+read byte no 1 \&-
+write byte no \&- 1
+read word yes 2 \&-
+write word yes \&- 2
+process call yes 2 2
+block read no \*(Ge 2 \&-
+block write no \&- \*(Ge 2
+.TE
+.Ed
+.Sh FILES
+.Bl -tag -width ".Pa /dev/smb0" -compact
+.It Pa /dev/smb0
+The default device to connect to, unless
+.Fl f Ar dev
+has been provided.
+.El
+.Sh EXIT STATUS
+Exit status is 0 on success, or according to
+.Xr sysexits 3
+in case of failure.
+.Sh EXAMPLES
+Typical usage examples of the
+.Nm
+command include:
+.Pp
+.Dl "smbmsg -f /dev/smb1 -p"
+.Pp
+Probe all devices on the SMBus attached to
+.Pa /dev/smb1 .
+.Pp
+.Dl "smbmsg -s 0x70 -i 1"
+.Pp
+Issue a
+.Em receive byte
+message to the device at address 0x70, and display
+the received byte using the default format.
+.Pp
+.Dl "smbmsg -s 0x70 -c 0xff -i 1 -F %d"
+.Pp
+Issue a
+.Em read byte
+message to the device at slave address 0x70, using
+255 (0xff) as the command-byte to send to the device,
+and display the result using the custom format
+.Ql %d .
+.Pp
+.Dl "smbmsg -s 0xa0 -c 0 -o 1 0x80"
+.Pp
+Send a
+.Em write byte
+message to the slave device at address 0xa0, using
+0 as the command-byte value, and 0x80 as the byte to
+send (after the command).
+Assuming this might be a Philips PCF8583 real-time clock,
+this would stop the clock.
+.Pp
+.Dl "smbmsg -s 0xa0 -c 1 -i 6 -F %02x"
+.Pp
+Send a
+.Em block read
+command to device at address 0xa0, and read 6 bytes from
+it, using hexadecimal display.
+Again, assuming a PCF8583 RTC, this would display the
+fractions of second, seconds, minutes, hours, year/date,
+and weekday/month values.
+Since this RTC uses BCD notation, the actual values displayed
+were decimal then.
+.Pp
+.Dl "smbmsg -s 0xa0 -c 2 -o 5 0x00 0x07 0x22 0x16 0x05"
+.Pp
+Send a
+.Em block write
+command to device at address 0xa0.
+For the PCF8583 RTC, this would set the clock to Sunday (2004%4)-05-16
+22:07:00.
+.Sh DIAGNOSTICS
+Diagnostic messages issued are supposed to be self-explanatory.
+.Sh SEE ALSO
+.Xr printf 3 ,
+.Xr sysexits 3 ,
+.Xr smb 4 ,
+.Xr smbus 4
+.Rs
+.%T "The SMBus specification"
+.%O http://www.smbus.org/specs/
+.Re
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 5.3 .
+.Sh AUTHORS
+The
+.Nm
+utility and this manual page were written by
+.An J\(:org Wunsch .
diff --git a/usr.sbin/smbmsg/smbmsg.c b/usr.sbin/smbmsg/smbmsg.c
new file mode 100644
index 0000000..425b782
--- /dev/null
+++ b/usr.sbin/smbmsg/smbmsg.c
@@ -0,0 +1,344 @@
+/*-
+ * Copyright (C) 2004 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 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$
+ */
+
+/*
+ * Send or receive messages over an SMBus.
+ */
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <dev/smbus/smb.h>
+
+#include "pathnames.h"
+
+static const char *dev = PATH_DEFAULTSMBDEV;
+static const char *bytefmt = "0x%02x";
+static const char *wordfmt = "0x%04x";
+static const char *fmt;
+
+static int fd; /* file descriptor for /dev/smbX */
+static int cflag = -1; /* SMBus cmd */
+static int iflag = -1; /* input data */
+static int oflag = -1; /* output data */
+static int pflag; /* probe bus */
+static int slave = -1; /* slave address */
+static int wflag; /* word IO */
+
+static unsigned char ibuf[SMB_MAXBLOCKSIZE];
+static unsigned char obuf[SMB_MAXBLOCKSIZE];
+static unsigned short oword, iword;
+
+/*
+ * The I2C specs say that all addresses below 16 and above or equal
+ * 240 are reserved. Address 0 is the global address, but we do not
+ * care for this detail.
+ */
+#define MIN_I2C_ADDR 16
+#define MAX_I2C_ADDR 240
+
+static int do_io(void);
+static int getnum(const char *s);
+static void probe_i2c(void);
+static void usage(void);
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: smbmsg [-f dev] -p\n"
+ " smbmsg [-f dev] -s slave [-F fmt] [-c cmd] [-w] "
+ "[-i incnt] [-o outcnt] [outdata ...]\n");
+ exit(EX_USAGE);
+}
+
+static int
+getnum(const char *s)
+{
+ char *endp;
+ unsigned long l;
+
+ l = strtoul(s, &endp, 0);
+ if (*s != '\0' && *endp == '\0')
+ return (int)l;
+ return (-1);
+}
+
+static void
+probe_i2c(void)
+{
+ unsigned char addr;
+ int flags;
+#define IS_READABLE 1
+#define IS_WRITEABLE 2
+ struct smbcmd c;
+
+ printf("Probing for devices on %s:\n", dev);
+
+ for (addr = MIN_I2C_ADDR; addr < MAX_I2C_ADDR; addr += 2) {
+ c.slave = addr;
+ flags = 0;
+ if (ioctl(fd, SMB_RECVB, &c) != -1)
+ flags = IS_READABLE;
+ if (ioctl(fd, SMB_QUICK_WRITE, &c) != -1)
+ flags |= IS_WRITEABLE;
+ if (flags != 0) {
+ printf("Device @0x%02x: ", addr);
+ if (flags & IS_READABLE)
+ putchar('r');
+ if (flags & IS_WRITEABLE)
+ putchar('w');
+ putchar('\n');
+ }
+ }
+}
+
+static int
+do_io(void)
+{
+ struct smbcmd c;
+ int i;
+
+ c.slave = slave;
+ c.cmd = cflag;
+
+ if (fmt == NULL && iflag > 0)
+ fmt = wflag? wordfmt: bytefmt;
+
+ if (cflag == -1) {
+ /* operations that do not require a command byte */
+ if (iflag == -1 && oflag == 0)
+ /* 0 bytes output: quick write operation */
+ return (ioctl(fd, SMB_QUICK_WRITE, &c));
+ else if (iflag == 0 && oflag == -1)
+ /* 0 bytes input: quick read operation */
+ return (ioctl(fd, SMB_QUICK_READ, &c));
+ else if (iflag == 1 && oflag == -1) {
+ /* no command, 1 byte input: receive byte op. */
+ if (ioctl(fd, SMB_RECVB, &c) == -1)
+ return (-1);
+ printf(fmt, (unsigned char)c.cmd);
+ putchar('\n');
+ return (0);
+ } else if (iflag == -1 && oflag == 1) {
+ /* no command, 1 byte output: send byte op. */
+ c.cmd = obuf[0];
+ return (ioctl(fd, SMB_SENDB, &c));
+ } else
+ return (-2);
+ }
+ if (iflag == 1 && oflag == -1) {
+ /* command + 1 byte input: read byte op. */
+ c.data.byte_ptr = ibuf;
+ if (ioctl(fd, SMB_READB, &c) == -1)
+ return (-1);
+ printf(fmt, (int)(unsigned char)ibuf[0]);
+ putchar('\n');
+ return (0);
+ } else if (iflag == -1 && oflag == 1) {
+ /* command + 1 byte output: write byte op. */
+ c.data.byte = obuf[0];
+ return (ioctl(fd, SMB_WRITEB, &c));
+ } else if (wflag && iflag == 2 && oflag == -1) {
+ /* command + 2 bytes input: read word op. */
+ c.data.word_ptr = &iword;
+ if (ioctl(fd, SMB_READW, &c) == -1)
+ return (-1);
+ printf(fmt, (int)(unsigned short)iword);
+ putchar('\n');
+ return (0);
+ } else if (wflag && iflag == -1 && oflag == 2) {
+ /* command + 2 bytes output: write word op. */
+ c.data.word = oword;
+ return (ioctl(fd, SMB_WRITEW, &c));
+ } else if (wflag && iflag == 2 && oflag == 2) {
+ /*
+ * command + 2 bytes output + 2 bytes input:
+ * "process call" op.
+ */
+ c.data.process.sdata = oword;
+ c.data.process.rdata = &iword;
+ if (ioctl(fd, SMB_PCALL, &c) == -1)
+ return (-1);
+ printf(fmt, (int)(unsigned short)iword);
+ putchar('\n');
+ return (0);
+ } else if (iflag > 1 && oflag == -1) {
+ /* command + > 1 bytes of input: block read */
+ c.data.byte_ptr = ibuf;
+ c.count = iflag;
+ if (ioctl(fd, SMB_BREAD, &c) == -1)
+ return (-1);
+ for (i = 0; i < iflag; i++) {
+ if (i != 0)
+ putchar(' ');
+ printf(fmt, ibuf[i]);
+ }
+ putchar('\n');
+ return (0);
+ } else if (iflag == -1 && oflag > 1) {
+ /* command + > 1 bytes of output: block write */
+ c.data.byte_ptr = obuf;
+ c.count = oflag;
+ return (ioctl(fd, SMB_BWRITE, &c));
+ }
+
+ return (-2);
+}
+
+
+int
+main(int argc, char **argv)
+{
+ int i, n, errs = 0;
+ int savederrno;
+
+ while ((i = getopt(argc, argv, "F:c:f:i:o:ps:w")) != -1)
+ switch (i) {
+ case 'F':
+ fmt = optarg;
+ break;
+
+ case 'c':
+ if ((cflag = getnum(optarg)) == -1)
+ errx(EX_USAGE, "Invalid number: %s", optarg);
+ if (cflag < 0 || cflag >= 256)
+ errx(EX_USAGE,
+ "CMD out of range: %d",
+ cflag);
+ break;
+
+ case 'f':
+ dev = optarg;
+ break;
+
+ case 'i':
+ if ((iflag = getnum(optarg)) == -1)
+ errx(EX_USAGE, "Invalid number: %s", optarg);
+ if (iflag < 0 || iflag > SMB_MAXBLOCKSIZE)
+ errx(EX_USAGE,
+ "# input bytes out of range: %d",
+ iflag);
+ break;
+
+ case 'o':
+ if ((oflag = getnum(optarg)) == -1)
+ errx(EX_USAGE, "Invalid number: %s", optarg);
+ if (oflag < 0 || oflag > SMB_MAXBLOCKSIZE)
+ errx(EX_USAGE,
+ "# output bytes out of range: %d",
+ oflag);
+ break;
+
+ case 'p':
+ pflag = 1;
+ break;
+
+ case 's':
+ if ((slave = getnum(optarg)) == -1)
+ errx(EX_USAGE, "Invalid number: %s", optarg);
+
+ if (slave < MIN_I2C_ADDR || slave >= MAX_I2C_ADDR)
+ errx(EX_USAGE,
+ "Slave address out of range: %d",
+ slave);
+ break;
+
+ case 'w':
+ wflag = 1;
+ break;
+
+ default:
+ errs++;
+ }
+ argc -= optind;
+ argv += optind;
+ if (errs || (slave != -1 && pflag) || (slave == -1 && !pflag))
+ usage();
+ if (wflag &&
+ !((iflag == 2 && oflag == -1) ||
+ (iflag == -1 && oflag == 2) ||
+ (iflag == 2 && oflag == 2)))
+ errx(EX_USAGE, "Illegal # IO bytes for word IO");
+ if (!pflag && iflag == -1 && oflag == -1)
+ errx(EX_USAGE, "Nothing to do");
+ if (pflag && (cflag != -1 || iflag != -1 || oflag != -1 || wflag != 0))
+ usage();
+ if (oflag > 0) {
+ if (oflag == 2 && wflag) {
+ if (argc == 0)
+ errx(EX_USAGE, "Too few arguments for -o count");
+ if ((n = getnum(*argv)) == -1)
+ errx(EX_USAGE, "Invalid number: %s", *argv);
+ if (n < 0 || n >= 65535)
+ errx(EX_USAGE, "Value out of range: %d", n);
+ oword = n;
+ argc--;
+ argv++;
+ } else for (i = 0; i < oflag; i++, argv++, argc--) {
+ if (argc == 0)
+ errx(EX_USAGE, "Too few arguments for -o count");
+ if ((n = getnum(*argv)) == -1)
+ errx(EX_USAGE, "Invalid number: %s", *argv);
+ if (n < 0 || n >= 256)
+ errx(EX_USAGE, "Value out of range: %d", n);
+ obuf[i] = n;
+ }
+ }
+ if (argc != 0)
+ usage();
+
+ if ((fd = open(dev, O_RDWR)) == -1)
+ err(EX_UNAVAILABLE, "Cannot open %s", dev);
+
+ i = 0;
+ if (pflag)
+ probe_i2c();
+ else
+ i = do_io();
+
+ savederrno = errno;
+ close(fd);
+ errno = savederrno;
+
+ if (i == -1)
+ err(EX_UNAVAILABLE, "Error performing SMBus IO");
+ else if (i == -2)
+ errx(EX_USAGE, "Invalid option combination");
+
+ return (0);
+}
diff --git a/usr.sbin/snapinfo/Makefile b/usr.sbin/snapinfo/Makefile
new file mode 100644
index 0000000..c5175f1
--- /dev/null
+++ b/usr.sbin/snapinfo/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+#
+
+PROG= snapinfo
+MAN= snapinfo.8
+
+DPADD= ${LIBUFS}
+LDADD= -lufs
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/snapinfo/snapinfo.8 b/usr.sbin/snapinfo/snapinfo.8
new file mode 100644
index 0000000..9d402ea
--- /dev/null
+++ b/usr.sbin/snapinfo/snapinfo.8
@@ -0,0 +1,66 @@
+.\"
+.\" Copyright (c) 2005 Mark Santcroos <marks@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 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd July 20, 2005
+.Dt SNAPINFO 8
+.Os
+.Sh NAME
+.Nm snapinfo
+.Nd "show snapshot location on UFS file systems"
+.Sh SYNOPSIS
+.Nm
+.Op Fl v
+.Fl a
+.Nm
+.Op Fl v
+.Ar mountpoint
+.Sh DESCRIPTION
+The
+.Nm
+utility searches and displays the location of snapshots on UFS file systems.
+.Pp
+Currently it works only on mounted file systems.
+It can either search one file system or all of them.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl a
+Search for snapshots on all mounted UFS file systems.
+.It Fl v
+Verbose mode.
+.It Ar mountpoint
+Search the file system mounted on this mountpoint.
+.El
+.Sh SEE ALSO
+.Xr ffs 7 ,
+.Xr mksnap_ffs 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 6.1 .
+.Sh AUTHORS
+.An Mark Santcroos Aq marks@FreeBSD.org
diff --git a/usr.sbin/snapinfo/snapinfo.c b/usr.sbin/snapinfo/snapinfo.c
new file mode 100644
index 0000000..55b54a5
--- /dev/null
+++ b/usr.sbin/snapinfo/snapinfo.c
@@ -0,0 +1,179 @@
+/*-
+ * Copyright (c) 2005 Mark Santcroos <marks@freebsd.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <ufs/ufs/dinode.h>
+#include <ufs/ffs/fs.h>
+
+#include <errno.h>
+#include <ftw.h>
+#include <libufs.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void find_inum(char *path);
+void usage(void);
+int compare_function(const char *, const struct stat *, int, struct FTW *);
+int find_snapshot(struct statfs *sfs);
+
+int verbose;
+int cont_search;
+uint32_t inode;
+
+int
+main(int argc, char **argv)
+{
+ char *path;
+ struct stat st;
+ struct statfs *mntbuf;
+ int all = 0, ch, done = 0, fscount, n;
+
+ while ((ch = getopt(argc, argv, "adv")) != -1) {
+ switch (ch) {
+ case 'a':
+ all++;
+ break;
+ case 'd':
+ /* continue to search when matching inode is found
+ * this feature is not documented */
+ cont_search++;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if ((all == 0 && argc != 1) || (all == 1 && argc > 0))
+ usage();
+
+ if (!all) {
+ char resolved[PATH_MAX];
+
+ path = *argv;
+ /*
+ * mount(8) use realpath(3) before mounting file system,
+ * so let's do the same with the given path.
+ */
+ if (realpath(path, resolved) == NULL || /* can create full path */
+ stat(resolved, &st) == -1 || /* is it stat'able */
+ !S_ISDIR(st.st_mode)) { /* is it a directory */
+ usage();
+ }
+ path = resolved;
+ }
+
+ fscount = getmntinfo(&mntbuf, MNT_WAIT);
+ for (n = 0; n < fscount; n++) {
+ if (!strncmp(mntbuf[n].f_fstypename, "ufs", 3)) {
+ if (all || strcmp(path, mntbuf[n].f_mntonname) == 0) {
+ find_snapshot(&mntbuf[n]);
+ done++;
+ }
+ }
+ }
+
+ if (done == 0)
+ usage();
+
+ return (0);
+}
+
+int
+find_snapshot(struct statfs *sfs)
+{
+ struct uufsd disk;
+ int j, snapcount = 0;
+
+ if (ufs_disk_fillout(&disk, sfs->f_mntfromname) == -1)
+ perror("ufs_disk_fillout");
+
+ if (verbose)
+ printf("%s mounted on %s\n", disk.d_name, disk.d_fs.fs_fsmnt);
+
+ for (j = 0; j < FSMAXSNAP; j++) {
+ if (disk.d_fs.fs_snapinum[j]) {
+ inode = disk.d_fs.fs_snapinum[j];
+ find_inum(sfs->f_mntonname);
+ snapcount++;
+ }
+ }
+
+ if (!snapcount && verbose)
+ printf("\tno snapshots found\n");
+
+ return 0;
+}
+
+int
+compare_function(const char *path, const struct stat *st, int flags,
+struct FTW * ftwv)
+{
+
+ if (flags == FTW_F && st->st_ino == inode) {
+ if (verbose)
+ printf("\tsnapshot ");
+ printf("%s", path);
+ if (verbose)
+ printf(" (inode %d)", st->st_ino);
+ printf("\n");
+ if (!cont_search)
+ return (EEXIST);
+ }
+
+ return (0);
+}
+
+void
+find_inum(char *path)
+{
+ int ret;
+
+ ret = nftw(path, compare_function, 1, FTW_PHYS|FTW_MOUNT);
+ if (ret != EEXIST && ret != 0) {
+ perror("ftw");
+ exit(ret);
+ }
+}
+
+void
+usage(void)
+{
+
+ printf("usage: snapinfo [-v] -a\n");
+ printf(" snapinfo [-v] mountpoint\n");
+ exit(1);
+}
diff --git a/usr.sbin/spkrtest/Makefile b/usr.sbin/spkrtest/Makefile
new file mode 100644
index 0000000..8e9245a
--- /dev/null
+++ b/usr.sbin/spkrtest/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+SCRIPTS=spkrtest.sh
+MAN= spkrtest.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/spkrtest/spkrtest.8 b/usr.sbin/spkrtest/spkrtest.8
new file mode 100644
index 0000000..58eced5
--- /dev/null
+++ b/usr.sbin/spkrtest/spkrtest.8
@@ -0,0 +1,48 @@
+.\" Copyright (c) May 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.Dd July 23, 1995
+.Dt SPKRTEST 8
+.Os
+.Sh NAME
+.Nm spkrtest
+.Nd test script for the speaker driver
+.Sh DESCRIPTION
+The
+.Nm
+utility is an easy to use test script for the speaker driver.
+.Sh FILES
+.Bl -tag -width /dev/speakerxx
+.It Pa /dev/speaker
+speaker device file
+.El
+.Sh SEE ALSO
+.Xr dialog 1 ,
+.Xr spkr 4
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 1.0
diff --git a/usr.sbin/spkrtest/spkrtest.sh b/usr.sbin/spkrtest/spkrtest.sh
new file mode 100644
index 0000000..c682b03
--- /dev/null
+++ b/usr.sbin/spkrtest/spkrtest.sh
@@ -0,0 +1,113 @@
+#!/bin/sh
+
+#
+# Copyright (c) 2002 The FreeBSD Project
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+
+#
+# Inspired on spkrtest.pl, rewritten from scratch to remove perl dependency
+# $VER: spkrtest 0.3 (9.5.2002) Riccardo "VIC" Torrini <riccardo@torrini.org>
+# $FreeBSD$
+#
+
+cleanExit() {
+ rm -f ${choices}
+ exit ${1:-0}
+}
+
+trap 'cleanExit 1' 1 2 3 5 15 # HUP, INT, QUIT, TRAP, TERM
+
+choices=${TMP:-/tmp}/_spkrtest_choices.$$
+speaker=/dev/speaker
+
+test -w ${speaker}
+if [ $? -ne 0 ]
+then
+ echo "You have no write access to $speaker or the speaker device is"
+ echo "not enabled in kernel. Cannot play any melody! See spkr(4)."
+ sleep 2
+ cleanExit 1
+fi
+
+/usr/bin/dialog --title "Speaker test" --checklist \
+ "Please select the melodies you wish to play (space for select)" \
+ -1 -1 10 \
+ reveille "Reveille" OFF \
+ contact "Contact theme from Close Encounters" OFF \
+ dance "Lord of the Dance (aka Simple Gifts)" OFF \
+ loony "Loony Toons theme" OFF \
+ sinister "Standard villain's entrance music" OFF \
+ rightstuff "A trope from 'The Right Stuff' score by Bill Conti" OFF \
+ toccata "Opening bars of Bach's Toccata and Fugue in D Minor" OFF \
+ startrek "Opening bars of the theme from Star Trek Classic" OFF \
+ 2> ${choices} || cleanExit 0
+
+echo ""
+tunes="`cat ${choices} | tr -d '\"'`"
+for tune in ${tunes:-DEFAULT}
+do
+ case ${tune:-NULL} in
+ DEFAULT)
+ title="(default melody)"
+ music="ec"
+ ;;
+ reveille)
+ title="Reveille"
+ music="t255l8c.f.afc~c.f.afc~c.f.afc.f.a..f.~c.f.afc~c.f.afc~c.f.afc~c.f.."
+ ;;
+ contact)
+ title="Contact theme from Close Encounters"
+ music="<cd<a#~<a#>f"
+ ;;
+ dance)
+ title="Lord of the Dance (aka Simple Gifts)"
+ music="t240<cfcfgagaa#b#>dc<a#a.~fg.gaa#.agagegc.~cfcfgagaa#b#>dc<a#a.~fg.gga.agfgfgf."
+ ;;
+ loony)
+ title="Loony Toons theme"
+ music="t255cf8f8edc<a>~cf8f8edd#e~ce8cdce8cd.<a>c8c8c#def8af8"
+ ;;
+ sinister)
+ title="standard villain's entrance music"
+ music="mst200o2ola.l8bc.~a.~>l2d#"
+ ;;
+ rightstuff)
+ title="a trope from 'The Right Stuff' score by Bill Conti"
+ music="olcega.a8f>cd2bgc.c8dee2"
+ ;;
+ toccata)
+ title="opening bars of Bach's Toccata and Fugue in D Minor"
+ music="msl16oldcd4mll8pcb-agf+4.g4p4<msl16dcd4mll8pa.a+f+4p16g4"
+ ;;
+ startrek)
+ title="opening bars of the theme from Star Trek Classic"
+ music="l2b.f+.p16a.c+.p l4mn<b.>e8a2mspg+e8c+f+8b2"
+ ;;
+ esac
+ echo "Title: ${title}"
+ echo ${music} > ${speaker}
+ sleep 1
+done
+cleanExit 0
diff --git a/usr.sbin/spray/Makefile b/usr.sbin/spray/Makefile
new file mode 100644
index 0000000..c2515e0
--- /dev/null
+++ b/usr.sbin/spray/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= spray
+MAN= spray.8
+
+DPADD= ${LIBRPCSVC}
+LDADD= -lrpcsvc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/spray/spray.8 b/usr.sbin/spray/spray.8
new file mode 100644
index 0000000..e107048
--- /dev/null
+++ b/usr.sbin/spray/spray.8
@@ -0,0 +1,76 @@
+.\"
+.\" Copyright (c) 1994 James A. Jegers
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the 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 ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED 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 10, 1995
+.Dt SPRAY 8
+.Os
+.Sh NAME
+.Nm spray
+.Nd send many packets to host
+.Sh SYNOPSIS
+.Nm
+.Op Fl c Ar count
+.Op Fl d Ar delay
+.Op Fl l Ar length
+.Ar host
+.Sh DESCRIPTION
+The
+.Nm
+utility sends multiple RPC packets to
+.Ar host
+and records how many of them were correctly received and how long it took.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl c Ar count
+Send
+.Ar count
+packets.
+.It Fl d Ar delay
+Pause
+.Ar delay
+microseconds between sending each packet.
+.It Fl l Ar length
+Set the length of the packet that holds the RPC call message to
+.Ar length
+bytes.
+Not all values of
+.Ar length
+are possible because RPC data is encoded using XDR.
+The
+.Nm
+utility rounds up to the nearest possible value.
+.El
+.Pp
+The
+.Nm
+utility is intended for use in network testing, measurement, and management.
+This command
+.Em "can be very hard on a network and should be used with caution" .
+.Sh SEE ALSO
+.Xr netstat 1 ,
+.Xr ifconfig 8 ,
+.Xr ping 8 ,
+.Xr rpc.sprayd 8
diff --git a/usr.sbin/spray/spray.c b/usr.sbin/spray/spray.c
new file mode 100644
index 0000000..0e072e2
--- /dev/null
+++ b/usr.sbin/spray/spray.c
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 1993 Winning Strategies, 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.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Winning Strategies, Inc.
+ * 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 <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <rpc/rpc.h>
+#include <rpcsvc/spray.h>
+
+#ifndef SPRAYOVERHEAD
+#define SPRAYOVERHEAD 86
+#endif
+
+static void usage(void);
+static void print_xferstats(unsigned int, int, double);
+
+/* spray buffer */
+char spray_buffer[SPRAYMAX];
+
+/* RPC timeouts */
+struct timeval NO_DEFAULT = { -1, -1 };
+struct timeval ONE_WAY = { 0, 0 };
+struct timeval TIMEOUT = { 25, 0 };
+
+int
+main(int argc, char *argv[])
+{
+ spraycumul host_stats;
+ sprayarr host_array;
+ CLIENT *cl;
+ int c;
+ u_int i;
+ u_int count = 0;
+ int delay = 0;
+ int length = 0;
+ double xmit_time; /* time to receive data */
+
+ while ((c = getopt(argc, argv, "c:d:l:")) != -1) {
+ switch (c) {
+ case 'c':
+ count = atoi(optarg);
+ break;
+ case 'd':
+ delay = atoi(optarg);
+ break;
+ case 'l':
+ length = atoi(optarg);
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1) {
+ usage();
+ /* NOTREACHED */
+ }
+
+
+ /* Correct packet length. */
+ if (length > SPRAYMAX) {
+ length = SPRAYMAX;
+ } else if (length < SPRAYOVERHEAD) {
+ length = SPRAYOVERHEAD;
+ } else {
+ /* The RPC portion of the packet is a multiple of 32 bits. */
+ length -= SPRAYOVERHEAD - 3;
+ length &= ~3;
+ length += SPRAYOVERHEAD;
+ }
+
+
+ /*
+ * The default value of count is the number of packets required
+ * to make the total stream size 100000 bytes.
+ */
+ if (!count) {
+ count = 100000 / length;
+ }
+
+ /* Initialize spray argument */
+ host_array.sprayarr_len = length - SPRAYOVERHEAD;
+ host_array.sprayarr_val = spray_buffer;
+
+
+ /* create connection with server */
+ cl = clnt_create(*argv, SPRAYPROG, SPRAYVERS, "udp");
+ if (cl == NULL)
+ errx(1, "%s", clnt_spcreateerror(""));
+
+
+ /*
+ * For some strange reason, RPC 4.0 sets the default timeout,
+ * thus timeouts specified in clnt_call() are always ignored.
+ *
+ * The following (undocumented) hack resets the internal state
+ * of the client handle.
+ */
+ clnt_control(cl, CLSET_TIMEOUT, &NO_DEFAULT);
+
+
+ /* Clear server statistics */
+ if (clnt_call(cl, SPRAYPROC_CLEAR, (xdrproc_t)xdr_void, NULL,
+ (xdrproc_t)xdr_void, NULL, TIMEOUT) != RPC_SUCCESS)
+ errx(1, "%s", clnt_sperror(cl, ""));
+
+
+ /* Spray server with packets */
+ printf ("sending %u packets of lnth %d to %s ...", count, length,
+ *argv);
+ fflush (stdout);
+
+ for (i = 0; i < count; i++) {
+ clnt_call(cl, SPRAYPROC_SPRAY, (xdrproc_t)xdr_sprayarr,
+ &host_array, (xdrproc_t)xdr_void, NULL, ONE_WAY);
+
+ if (delay) {
+ usleep(delay);
+ }
+ }
+
+
+ /* Collect statistics from server */
+ if (clnt_call(cl, SPRAYPROC_GET, (xdrproc_t)xdr_void, NULL,
+ (xdrproc_t)xdr_spraycumul, &host_stats, TIMEOUT) != RPC_SUCCESS)
+ errx(1, "%s", clnt_sperror(cl, ""));
+
+ xmit_time = host_stats.clock.sec +
+ (host_stats.clock.usec / 1000000.0);
+
+ printf ("\n\tin %.2f seconds elapsed time\n", xmit_time);
+
+
+ /* report dropped packets */
+ if (host_stats.counter != count) {
+ int packets_dropped = count - host_stats.counter;
+
+ printf("\t%d packets (%.2f%%) dropped\n",
+ packets_dropped,
+ 100.0 * packets_dropped / count );
+ } else {
+ printf("\tno packets dropped\n");
+ }
+
+ printf("Sent:");
+ print_xferstats(count, length, xmit_time);
+
+ printf("Rcvd:");
+ print_xferstats(host_stats.counter, length, xmit_time);
+
+ exit (0);
+}
+
+
+static void
+print_xferstats(u_int packets, int packetlen, double xfertime)
+{
+ int datalen;
+ double pps; /* packets per second */
+ double bps; /* bytes per second */
+
+ datalen = packets * packetlen;
+ pps = packets / xfertime;
+ bps = datalen / xfertime;
+
+ printf("\t%.0f packets/sec, ", pps);
+
+ if (bps >= 1024)
+ printf ("%.1fK ", bps / 1024);
+ else
+ printf ("%.0f ", bps);
+
+ printf("bytes/sec\n");
+}
+
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: spray [-c count] [-l length] [-d delay] host\n");
+ exit(1);
+}
diff --git a/usr.sbin/sysinstall/Makefile b/usr.sbin/sysinstall/Makefile
new file mode 100644
index 0000000..5cbd2e5
--- /dev/null
+++ b/usr.sbin/sysinstall/Makefile
@@ -0,0 +1,154 @@
+# $FreeBSD$
+
+.if ${MACHINE_ARCH} != "ia64"
+_wizard= wizard.c
+.endif
+
+PROG= sysinstall
+MAN= sysinstall.8
+SRCS= anonFTP.c cdrom.c command.c config.c devices.c dhcp.c \
+ disks.c dispatch.c dist.c dmenu.c doc.c dos.c floppy.c \
+ ftp.c globals.c http.c index.c install.c installUpgrade.c keymap.c \
+ label.c main.c makedevs.c media.c menus.c misc.c modules.c \
+ mouse.c msg.c network.c nfs.c options.c package.c \
+ system.c tcpip.c termcap.c ttys.c ufs.c user.c \
+ variable.c ${_wizard} keymap.h countries.h
+
+CFLAGS+= -DUSE_GZIP=1
+.if ${MACHINE} == "pc98"
+CFLAGS+= -DPC98
+.endif
+CFLAGS+= -I${.CURDIR}/../../gnu/lib/libdialog -I.
+
+DPADD= ${LIBDIALOG} ${LIBNCURSES} ${LIBUTIL} ${LIBDISK} ${LIBFTPIO}
+LDADD= -ldialog -lncurses -lutil -ldisk -lftpio
+
+#
+# When distributions have both UP and SMP kernels sysinstall
+# will probe for the number of cpus on the target machine and
+# automatically select which is appropriate. This can be overridden
+# through the menus or both kernels can be installed (with the
+# most "appropriate" one setup as /boot/kernel). For now this
+# is done for i386 and amd64; for other systems support must be
+# added to identify the cpu count if acpi and MPTable probing
+# is insufficient.
+#
+# The unmber of cpus probed is passed through the environment in
+# VAR_NCPUS ("ncpus") to scripts.
+#
+# Note that WITH_SMP is a compile time option that enables the
+# builtin menus for the SMP kernel configuration. If this kernel
+# is not built (see release/Makefile) then this should not be
+# enabled as sysinstall may try to select an SMP kernel config
+# where none is available. This option should not be needed--we
+# should probe for an SMP kernel in the distribution but doing
+# that is painful because of media changes and the structure of
+# sysinstall so for now it's a priori.
+#
+.if ${MACHINE} == "i386" || ${MACHINE_ARCH} == "amd64"
+SRCS+= acpi.c biosmptable.c
+.if exists(${.CURDIR}/../../sys/${MACHINE}/conf/SMP)
+CFLAGS+=-DWITH_SMP
+.endif
+DPADD+= ${LIBDEVINFO}
+LDADD+= -ldevinfo
+.endif
+
+CLEANFILES= makedevs.c rtermcap
+CLEANFILES+= keymap.tmp keymap.h countries.tmp countries.h
+
+.if exists(${.CURDIR}/../../share/termcap/termcap.src)
+RTERMCAP= TERMCAP=${.CURDIR}/../../share/termcap/termcap.src ./rtermcap
+.else
+RTERMCAP= ./rtermcap
+.endif
+
+makedevs.c: Makefile rtermcap
+ echo '#include <sys/types.h>' > makedevs.c
+ ${RTERMCAP} ansi | \
+ file2c 'const char termcap_ansi[] = {' ',0};' \
+ >> makedevs.c
+ ${RTERMCAP} cons25w | \
+ file2c 'const char termcap_cons25w[] = {' ',0};' \
+ >> makedevs.c
+ ${RTERMCAP} cons25 | \
+ file2c 'const char termcap_cons25[] = {' ',0};' \
+ >> makedevs.c
+ ${RTERMCAP} cons25-m | \
+ file2c 'const char termcap_cons25_m[] = {' ',0};' \
+ >> makedevs.c
+ ${RTERMCAP} cons25r | \
+ file2c 'const char termcap_cons25r[] = {' ',0};' \
+ >> makedevs.c
+ ${RTERMCAP} cons25r-m | \
+ file2c 'const char termcap_cons25r_m[] = {' ',0};' \
+ >> makedevs.c
+ ${RTERMCAP} cons25l1 | \
+ file2c 'const char termcap_cons25l1[] = {' ',0};' \
+ >> makedevs.c
+ ${RTERMCAP} cons25l1-m | \
+ file2c 'const char termcap_cons25l1_m[] = {' ',0};' \
+ >> makedevs.c
+ ${RTERMCAP} vt100 | \
+ file2c 'const char termcap_vt100[] = {' ',0};' \
+ >> makedevs.c
+ ${RTERMCAP} xterm | \
+ file2c 'const char termcap_xterm[] = {' ',0};' \
+ >> makedevs.c
+
+build-tools: rtermcap
+
+rtermcap: rtermcap.c
+ ${CC} -o ${.TARGET} ${.ALLSRC} -ltermcap
+
+.if ${MACHINE} == "pc98"
+KEYMAPS= jp.pc98 jp.pc98.iso
+.else
+KEYMAPS= be.iso bg.bds.ctrlcaps bg.phonetic.ctrlcaps br275.iso \
+ ce.iso2 cs.latin2.qwertz danish.iso el.iso07 \
+ estonian.cp850 estonian.iso estonian.iso15 finnish.iso fr.iso \
+ german.iso gr.elot.acc gr.us101.acc hr.iso hu.iso2.101keys \
+ it.iso icelandic.iso jp.106 norwegian.iso pl_PL.ISO8859-2 \
+ pt.iso ru.koi8-r si.iso sk.iso2 spanish.iso swedish.iso \
+ swissfrench.iso \
+ swissgerman.iso ua.koi8-u ua.koi8-u.shift.alt uk.iso us.dvorak \
+ us.iso us.pc-ctrl us.unix
+.endif
+
+keymap.h:
+ rm -f keymap.tmp
+ for map in ${KEYMAPS} ; do \
+ KEYMAP_PATH=${.CURDIR}/../../share/syscons/keymaps \
+ kbdcontrol -L $$map | \
+ sed -e '/^static accentmap_t/,$$d' >> keymap.tmp ; \
+ done
+ echo "static struct keymapInfo keymapInfos[] = {" >> keymap.tmp
+ for map in ${KEYMAPS} ; do \
+ echo -n ' { "'$$map'", ' >> keymap.tmp ; \
+ echo "&keymap_$$map }," | tr '[-.]' '_' >> keymap.tmp ; \
+ done
+ ( echo " { NULL, NULL }"; echo "};" ; echo "" ) >> keymap.tmp
+ mv keymap.tmp keymap.h
+
+countries.h: ${.CURDIR}/../../share/misc/iso3166
+ rm -f countries.tmp
+ awk 'BEGIN { \
+ FS = "\t"; \
+ num = 1; \
+ print "DMenu MenuCountry = {"; \
+ print " DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,"; \
+ print " \"Country Selection\","; \
+ print " \"Please choose a country, region, or group.\\n\""; \
+ print " \"Select an item using [SPACE] or [ENTER].\","; \
+ printf " NULL,\n NULL,\n { "; \
+ } \
+ /^[[:space:]]*#/ {next;} \
+ {if (num > 1) {printf " ";} \
+ print "{ \"" num "\", \"" $$4 "\"" \
+ ", dmenuVarCheck, dmenuSetCountryVariable" \
+ ", NULL, VAR_COUNTRY \"=" tolower($$1) "\" },"; \
+ ++num;} \
+ END {print " { NULL } }\n};\n";}' < ${.ALLSRC} > countries.tmp
+ mv countries.tmp ${.TARGET}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/sysinstall/acpi.c b/usr.sbin/sysinstall/acpi.c
new file mode 100644
index 0000000..4a375a1
--- /dev/null
+++ b/usr.sbin/sysinstall/acpi.c
@@ -0,0 +1,356 @@
+/*-
+ * Copyright (c) 1998 Doug Rabson
+ * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@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/param.h>
+#include <sys/endian.h>
+#include <sys/mman.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/wait.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <paths.h>
+#include <devinfo.h>
+
+#include "acpidump.h"
+#include "sysinstall.h"
+
+static void acpi_handle_apic(struct ACPIsdt *sdp);
+static struct ACPIsdt *acpi_map_sdt(vm_offset_t pa);
+static void acpi_handle_rsdt(struct ACPIsdt *rsdp);
+static struct acpi_user_mapping *acpi_user_find_mapping(vm_offset_t, size_t);
+static void * acpi_map_physical(vm_offset_t, size_t);
+
+/* Size of an address. 32-bit for ACPI 1.0, 64-bit for ACPI 2.0 and up. */
+static int addr_size;
+
+static int ncpu;
+
+static void
+acpi_handle_apic(struct ACPIsdt *sdp)
+{
+ struct MADTbody *madtp;
+ struct MADT_APIC *mp;
+ struct MADT_local_apic *apic;
+ struct MADT_local_sapic *sapic;
+
+ madtp = (struct MADTbody *) sdp->body;
+ mp = (struct MADT_APIC *)madtp->body;
+ while (((uintptr_t)mp) - ((uintptr_t)sdp) < sdp->len) {
+ switch (mp->type) {
+ case ACPI_MADT_APIC_TYPE_LOCAL_APIC:
+ apic = &mp->body.local_apic;
+ msgDebug("MADT: Found CPU APIC ID %d %s\n",
+ apic->cpu_id,
+ apic->flags & ACPI_MADT_APIC_LOCAL_FLAG_ENABLED ?
+ "enabled" : "disabled");
+ if (apic->flags & ACPI_MADT_APIC_LOCAL_FLAG_ENABLED)
+ ncpu++;
+ break;
+ case ACPI_MADT_APIC_TYPE_LOCAL_SAPIC:
+ sapic = &mp->body.local_sapic;
+ msgDebug("MADT: Found CPU SAPIC ID %d %s\n",
+ sapic->cpu_id,
+ sapic->flags & ACPI_MADT_APIC_LOCAL_FLAG_ENABLED ?
+ "enabled" : "disabled");
+ /* XXX is enable flag the same? */
+ if (sapic->flags & ACPI_MADT_APIC_LOCAL_FLAG_ENABLED)
+ ncpu++;
+ break;
+ default:
+ break;
+ }
+ mp = (struct MADT_APIC *) ((char *)mp + mp->len);
+ }
+}
+
+static int
+acpi_checksum(void *p, size_t length)
+{
+ u_int8_t *bp;
+ u_int8_t sum;
+
+ bp = p;
+ sum = 0;
+ while (length--)
+ sum += *bp++;
+
+ return (sum);
+}
+
+static struct ACPIsdt *
+acpi_map_sdt(vm_offset_t pa)
+{
+ struct ACPIsdt *sp;
+
+ sp = acpi_map_physical(pa, sizeof(struct ACPIsdt));
+ if (sp != NULL)
+ sp = acpi_map_physical(pa, sp->len);
+ return (sp);
+}
+
+static void
+acpi_handle_rsdt(struct ACPIsdt *rsdp)
+{
+ struct ACPIsdt *sdp;
+ vm_offset_t addr;
+ int entries, i;
+
+ entries = (rsdp->len - SIZEOF_SDT_HDR) / addr_size;
+ for (i = 0; i < entries; i++) {
+ switch (addr_size) {
+ case 4:
+ addr = le32dec((char*)rsdp->body + i * addr_size);
+ break;
+ case 8:
+ addr = le64dec((char*)rsdp->body + i * addr_size);
+ break;
+ default:
+ assert((addr = 0));
+ }
+
+ sdp = (struct ACPIsdt *)acpi_map_sdt(addr);
+ if (sdp == NULL) {
+ msgDebug("%s: unable to map sdt\n", __func__);
+ continue;
+ }
+ if (acpi_checksum(sdp, sdp->len)) {
+#if 0
+ msgDebug("RSDT entry %d (sig %.4s) has bad checksum\n",
+ i, sdp->signature);
+#endif
+ continue;
+ }
+ if (!memcmp(sdp->signature, "APIC", 4))
+ acpi_handle_apic(sdp);
+ }
+}
+
+static char machdep_acpi_root[] = "machdep.acpi_root";
+static int acpi_mem_fd = -1;
+
+struct acpi_user_mapping {
+ LIST_ENTRY(acpi_user_mapping) link;
+ vm_offset_t pa;
+ caddr_t va;
+ size_t size;
+};
+
+LIST_HEAD(acpi_user_mapping_list, acpi_user_mapping) maplist;
+
+static int
+acpi_user_init(void)
+{
+
+ if (acpi_mem_fd == -1) {
+ acpi_mem_fd = open(_PATH_MEM, O_RDONLY);
+ if (acpi_mem_fd == -1) {
+ msgDebug("%s: error opening %s: %s\n", __func__,
+ _PATH_MEM, strerror(errno));
+ return 0;
+ }
+ LIST_INIT(&maplist);
+ }
+ return 1;
+}
+
+static struct acpi_user_mapping *
+acpi_user_find_mapping(vm_offset_t pa, size_t size)
+{
+ struct acpi_user_mapping *map;
+
+ /* First search for an existing mapping */
+ for (map = LIST_FIRST(&maplist); map; map = LIST_NEXT(map, link)) {
+ if (map->pa <= pa && map->size >= pa + size - map->pa)
+ return (map);
+ }
+
+ /* Then create a new one */
+ size = round_page(pa + size) - trunc_page(pa);
+ pa = trunc_page(pa);
+ map = malloc(sizeof(struct acpi_user_mapping));
+ if (!map) {
+ msgDebug("%s: out of memory: %s\n", __func__, strerror(errno));
+ return (map);
+ }
+ map->pa = pa;
+ map->va = mmap(0, size, PROT_READ, MAP_SHARED, acpi_mem_fd, pa);
+ map->size = size;
+ if ((intptr_t) map->va == -1) {
+ msgDebug("%s: can't mmap address %lu size %lu: %s\n", __func__,
+ (unsigned long) pa, (unsigned long) size, strerror(errno));
+ free(map);
+ return (NULL);
+ }
+ LIST_INSERT_HEAD(&maplist, map, link);
+
+ return (map);
+}
+
+static void *
+acpi_map_physical(vm_offset_t pa, size_t size)
+{
+ struct acpi_user_mapping *map;
+
+ map = acpi_user_find_mapping(pa, size);
+ return (map == NULL ? NULL : map->va + (pa - map->pa));
+}
+
+static struct ACPIrsdp *
+acpi_get_rsdp(u_long addr)
+{
+ struct ACPIrsdp rsdp;
+ size_t len;
+
+ /* Read in the table signature and check it. */
+ pread(acpi_mem_fd, &rsdp, 8, addr);
+ if (memcmp(rsdp.signature, "RSD PTR ", 8))
+ return (NULL);
+
+ /* Read the entire table. */
+ pread(acpi_mem_fd, &rsdp, sizeof(rsdp), addr);
+
+ /* Run the checksum only over the version 1 header. */
+ if (acpi_checksum(&rsdp, 20))
+ return (NULL);
+
+ /* If the revision is 0, assume a version 1 length. */
+ if (rsdp.revision == 0)
+ len = 20;
+ else
+ len = rsdp.length;
+
+ /* XXX Should handle ACPI 2.0 RSDP extended checksum here. */
+
+ return (acpi_map_physical(addr, len));
+}
+
+static const char *
+devstate(devinfo_state_t state)
+{
+ switch (state) {
+ case DIS_NOTPRESENT:
+ return "not-present";
+ case DIS_ALIVE:
+ return "alive";
+ case DIS_ATTACHED:
+ return "attached";
+ case DIS_BUSY:
+ return "busy";
+ default:
+ return "unknown-state";
+ }
+}
+
+static int
+acpi0_check(struct devinfo_dev *dd, void *arg)
+{
+ printf("%s: %s %s\n", __func__, dd->dd_name, devstate(dd->dd_state));
+ /* NB: device must be present AND attached */
+ if (strcmp(dd->dd_name, "acpi0") == 0)
+ return (dd->dd_state == DIS_ATTACHED ||
+ dd->dd_state == DIS_BUSY);
+ return devinfo_foreach_device_child(dd, acpi0_check, arg);
+}
+
+static int
+acpi0_present(void)
+{
+ struct devinfo_dev *root;
+ int found;
+
+ found = 0;
+ devinfo_init();
+ root = devinfo_handle_to_device(DEVINFO_ROOT_DEVICE);
+ if (root != NULL)
+ found = devinfo_foreach_device_child(root, acpi0_check, NULL);
+ devinfo_free();
+ return found;
+}
+
+int
+acpi_detect(void)
+{
+ struct ACPIrsdp *rp;
+ struct ACPIsdt *rsdp;
+ u_long addr;
+ size_t len;
+
+ if (!acpi0_present()) {
+ msgDebug("%s: no acpi0 device located\n", __func__);
+ return -1;
+ }
+
+ if (!acpi_user_init())
+ return -1;
+
+ /* Attempt to use sysctl to find RSD PTR record. */
+ len = sizeof(addr);
+ if (sysctlbyname(machdep_acpi_root, &addr, &len, NULL, 0) != 0) {
+ msgDebug("%s: cannot find ACPI information\n", __func__);
+ return -1;
+ }
+ rp = acpi_get_rsdp(addr);
+ if (rp == NULL) {
+ msgDebug("%s: cannot find ACPI information: "
+ "sysctl %s does not point to RSDP\n", __func__,
+ machdep_acpi_root);
+ return -1;
+ }
+ if (rp->revision < 2) {
+ rsdp = (struct ACPIsdt *)acpi_map_sdt(rp->rsdt_addr);
+ if (rsdp == NULL)
+ return -1;
+ if (memcmp(rsdp->signature, "RSDT", 4) != 0 ||
+ acpi_checksum(rsdp, rsdp->len) != 0) {
+ msgDebug("%s: RSDT is corrupted\n", __func__);
+ return -1;
+ }
+ addr_size = sizeof(uint32_t);
+ } else {
+ rsdp = (struct ACPIsdt *)acpi_map_sdt(rp->xsdt_addr);
+ if (rsdp == NULL)
+ return -1;
+ if (memcmp(rsdp->signature, "XSDT", 4) != 0 ||
+ acpi_checksum(rsdp, rsdp->len) != 0) {
+ msgDebug("%s: XSDT is corrupted\n", __func__);
+ return -1;
+ }
+ addr_size = sizeof(uint64_t);
+ }
+ ncpu = 0;
+ acpi_handle_rsdt(rsdp);
+ return (ncpu == 0 ? 1 : ncpu);
+}
diff --git a/usr.sbin/sysinstall/acpidump.h b/usr.sbin/sysinstall/acpidump.h
new file mode 100644
index 0000000..9c2b5b6
--- /dev/null
+++ b/usr.sbin/sysinstall/acpidump.h
@@ -0,0 +1,177 @@
+/*-
+ * Copyright (c) 1999 Doug Rabson
+ * Copyright (c) 2000 Mitsuru IWASAKI <iwasaki@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 _ACPIDUMP_H_
+#define _ACPIDUMP_H_
+
+/* Root System Description Pointer */
+struct ACPIrsdp {
+ u_char signature[8];
+ u_char sum;
+ u_char oem[6];
+ u_char revision;
+ u_int32_t rsdt_addr;
+ u_int32_t length;
+ u_int64_t xsdt_addr;
+ u_char xsum;
+ u_char _reserved_[3];
+} __packed;
+
+/* System Description Table */
+struct ACPIsdt {
+ u_char signature[4];
+ u_int32_t len;
+ u_char rev;
+ u_char check;
+ u_char oemid[6];
+ u_char oemtblid[8];
+ u_int32_t oemrev;
+ u_char creator[4];
+ u_int32_t crerev;
+#define SIZEOF_SDT_HDR 36 /* struct size except body */
+ u_int32_t body[1];/* This member should be casted */
+} __packed;
+
+struct MADT_local_apic {
+ u_char cpu_id;
+ u_char apic_id;
+ u_int32_t flags;
+#define ACPI_MADT_APIC_LOCAL_FLAG_ENABLED 1
+} __packed;
+
+struct MADT_io_apic {
+ u_char apic_id;
+ u_char reserved;
+ u_int32_t apic_addr;
+ u_int32_t int_base;
+} __packed;
+
+struct MADT_int_override {
+ u_char bus;
+ u_char source;
+ u_int32_t intr;
+ u_int16_t mps_flags;
+#define MPS_INT_FLAG_POLARITY_MASK 0x3
+#define MPS_INT_FLAG_POLARITY_CONFORM 0x0
+#define MPS_INT_FLAG_POLARITY_HIGH 0x1
+#define MPS_INT_FLAG_POLARITY_LOW 0x3
+#define MPS_INT_FLAG_TRIGGER_MASK 0xc
+#define MPS_INT_FLAG_TRIGGER_CONFORM 0x0
+#define MPS_INT_FLAG_TRIGGER_EDGE 0x4
+#define MPS_INT_FLAG_TRIGGER_LEVEL 0xc
+} __packed;
+
+struct MADT_nmi {
+ u_int16_t mps_flags;
+ u_int32_t intr;
+} __packed;
+
+struct MADT_local_nmi {
+ u_char cpu_id;
+ u_int16_t mps_flags;
+ u_char lintpin;
+} __packed;
+
+struct MADT_local_apic_override {
+ u_char reserved[2];
+ u_int64_t apic_addr;
+} __packed;
+
+struct MADT_io_sapic {
+ u_char apic_id;
+ u_char reserved;
+ u_int32_t int_base;
+ u_int64_t apic_addr;
+} __packed;
+
+struct MADT_local_sapic {
+ u_char cpu_id;
+ u_char apic_id;
+ u_char apic_eid;
+ u_char reserved[3];
+ u_int32_t flags;
+} __packed;
+
+struct MADT_int_src {
+ u_int16_t mps_flags;
+ u_char type;
+#define ACPI_MADT_APIC_INT_SOURCE_PMI 1
+#define ACPI_MADT_APIC_INT_SOURCE_INIT 2
+#define ACPI_MADT_APIC_INT_SOURCE_CPEI 3 /* Corrected Platform Error */
+ u_char cpu_id;
+ u_char cpu_eid;
+ u_char sapic_vector;
+ u_int32_t intr;
+ u_char reserved[4];
+} __packed;
+
+struct MADT_APIC {
+ u_char type;
+#define ACPI_MADT_APIC_TYPE_LOCAL_APIC 0
+#define ACPI_MADT_APIC_TYPE_IO_APIC 1
+#define ACPI_MADT_APIC_TYPE_INT_OVERRIDE 2
+#define ACPI_MADT_APIC_TYPE_NMI 3
+#define ACPI_MADT_APIC_TYPE_LOCAL_NMI 4
+#define ACPI_MADT_APIC_TYPE_LOCAL_OVERRIDE 5
+#define ACPI_MADT_APIC_TYPE_IO_SAPIC 6
+#define ACPI_MADT_APIC_TYPE_LOCAL_SAPIC 7
+#define ACPI_MADT_APIC_TYPE_INT_SRC 8
+ u_char len;
+ union {
+ struct MADT_local_apic local_apic;
+ struct MADT_io_apic io_apic;
+ struct MADT_int_override int_override;
+ struct MADT_nmi nmi;
+ struct MADT_local_nmi local_nmi;
+ struct MADT_local_apic_override local_apic_override;
+ struct MADT_io_sapic io_sapic;
+ struct MADT_local_sapic local_sapic;
+ struct MADT_int_src int_src;
+ } body;
+} __packed;
+
+struct MADTbody {
+ u_int32_t lapic_addr;
+ u_int32_t flags;
+#define ACPI_APIC_FLAG_PCAT_COMPAT 1 /* System has dual-8259 setup. */
+ u_char body[1];
+} __packed;
+
+/*
+ * Addresses to scan on ia32 for the RSD PTR. According to section 5.2.2
+ * of the ACPI spec, we only consider two regions for the base address:
+ * 1. EBDA (1 KB area addressed to by 16 bit pointer at 0x40E)
+ * 2. High memory (0xE0000 - 0xFFFFF)
+ */
+#define RSDP_EBDA_PTR 0x40E
+#define RSDP_EBDA_SIZE 0x400
+#define RSDP_HI_START 0xE0000
+#define RSDP_HI_SIZE 0x20000
+
+#endif /* !_ACPIDUMP_H_ */
diff --git a/usr.sbin/sysinstall/anonFTP.c b/usr.sbin/sysinstall/anonFTP.c
new file mode 100644
index 0000000..2eaf636
--- /dev/null
+++ b/usr.sbin/sysinstall/anonFTP.c
@@ -0,0 +1,328 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Coranth Gryphon. All rights reserved.
+ * Copyright (c) 1996
+ * Jordan K. Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 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 OR THEIR PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sysinstall.h"
+#include <sys/param.h>
+#include <pwd.h>
+#include <grp.h>
+
+/* This doesn't change until FTP itself changes */
+
+#define FTP_NAME "ftp"
+#define MOTD_FILE "ftpmotd"
+
+/* These change if we want to use different defaults */
+
+#define FTP_UID 14
+#define FTP_GID 14
+#define FTP_GROUP "ftp"
+#define FTP_UPLOAD "incoming"
+#define FTP_COMMENT "Anonymous FTP Admin"
+#define FTP_HOMEDIR "/var/ftp"
+
+#define ANONFTP_HELPFILE "anonftp"
+
+/* Set up the structure to hold configuration information */
+/* Note that this is only what we could fit onto the one screen */
+
+typedef struct
+{
+ char homedir[64]; /* Home Dir for Anon FTP */
+ char group[32]; /* Group */
+ char uid[8]; /* UID */
+ char comment[64]; /* PWD Comment */
+ char upload[32]; /* Upload Dir */
+} FTPConf;
+
+static FTPConf tconf;
+
+#define ANONFTP_HOMEDIR_LEN 64
+#define ANONFTP_COMMENT_LEN 64
+#define ANONFTP_UPLOAD_LEN 32
+#define ANONFTP_GROUP_LEN 32
+#define ANONFTP_UID_LEN 8
+
+static int okbutton, cancelbutton;
+
+/* What the screen size is meant to be */
+#define ANONFTP_DIALOG_Y 0
+#define ANONFTP_DIALOG_X 8
+#define ANONFTP_DIALOG_WIDTH COLS - 16
+#define ANONFTP_DIALOG_HEIGHT LINES - 2
+
+static Layout layout[] = {
+#define LAYOUT_UID 0
+ { 2, 3, 8, ANONFTP_UID_LEN - 1,
+ "UID:", "What user ID to assign to FTP Admin",
+ tconf.uid, STRINGOBJ, NULL },
+#define LAYOUT_GROUP 1
+ { 2, 15, 15, ANONFTP_GROUP_LEN - 1,
+ "Group:", "Group name that ftp process belongs to",
+ tconf.group, STRINGOBJ, NULL },
+#define LAYOUT_COMMENT 2
+ { 2, 35, 24, ANONFTP_COMMENT_LEN - 1,
+ "Comment:", "Password file comment for FTP Admin",
+ tconf.comment, STRINGOBJ, NULL },
+#define LAYOUT_HOMEDIR 3
+ { 9, 10, 43, ANONFTP_HOMEDIR_LEN - 1,
+ "FTP Root Directory:",
+ "The top directory to chroot to when doing anonymous ftp",
+ tconf.homedir, STRINGOBJ, NULL },
+#define LAYOUT_UPLOAD 4
+ { 14, 20, 22, ANONFTP_UPLOAD_LEN - 1,
+ "Upload Subdirectory:", "Designated sub-directory that holds uploads (leave empty for none)",
+ tconf.upload, STRINGOBJ, NULL },
+#define LAYOUT_OKBUTTON 5
+ { 19, 15, 0, 0,
+ "OK", "Select this if you are happy with these settings",
+ &okbutton, BUTTONOBJ, NULL },
+#define LAYOUT_CANCELBUTTON 6
+ { 19, 35, 0, 0,
+ "CANCEL", "Select this if you wish to cancel this screen",
+ &cancelbutton, BUTTONOBJ, NULL },
+ LAYOUT_END,
+};
+
+static int
+createFtpUser(void)
+{
+ struct passwd *tpw;
+ struct group *tgrp;
+ char pwline[256];
+ char *tptr;
+ int gid;
+ FILE *fptr;
+
+ if ((gid = atoi(tconf.group)) <= 0) {
+ if (!(tgrp = getgrnam(tconf.group))) {
+ /* group does not exist, create it by name */
+
+ tptr = msgGetInput("14", "What group ID to use for group %s ?", tconf.group);
+ if (tptr && *tptr && ((gid = atoi(tptr)) > 0)) {
+ if ((fptr = fopen(_PATH_GROUP,"a"))) {
+ fprintf(fptr,"%s:*:%d:%s\n",tconf.group,gid,FTP_NAME);
+ fclose(fptr);
+ }
+ }
+ else
+ gid = FTP_GID;
+ }
+ else
+ gid = tgrp->gr_gid;
+ }
+ else if (!getgrgid(gid)) {
+ /* group does not exist, create it by number */
+
+ tptr = msgGetInput("ftp", "What group name to use for gid %d ?", gid);
+ if (tptr && *tptr) {
+ SAFE_STRCPY(tconf.group, tptr);
+ if ((tgrp = getgrnam(tconf.group))) {
+ gid = tgrp->gr_gid;
+ }
+ else if ((fptr = fopen(_PATH_GROUP,"a"))) {
+ fprintf(fptr,"%s:*:%d:%s\n",tconf.group,gid,FTP_NAME);
+ fclose(fptr);
+ }
+ }
+ }
+
+ if ((tpw = getpwnam(FTP_NAME))) {
+ if (tpw->pw_uid != FTP_UID)
+ msgConfirm("FTP user already exists with a different uid.");
+
+ return DITEM_SUCCESS; /* succeeds if already exists */
+ }
+
+ sprintf(pwline, "%s:*:%s:%d::0:0:%s:%s:/nonexistent\n", FTP_NAME, tconf.uid, gid, tconf.comment, tconf.homedir);
+
+ fptr = fopen(_PATH_MASTERPASSWD,"a");
+ if (! fptr) {
+ msgConfirm("Could not open master password file.");
+ return DITEM_FAILURE;
+ }
+ fprintf(fptr, "%s", pwline);
+ fclose(fptr);
+ msgNotify("Remaking password file: %s", _PATH_MASTERPASSWD);
+ vsystem("pwd_mkdb -p %s", _PATH_MASTERPASSWD);
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+/* This is it - how to get the setup values */
+static int
+anonftpOpenDialog(void)
+{
+ WINDOW *ds_win;
+ ComposeObj *obj = NULL;
+ int n = 0, cancel = FALSE;
+ int max;
+ char title[80];
+ WINDOW *w = savescr();
+
+ /* We need a curses window */
+ if (!(ds_win = openLayoutDialog(ANONFTP_HELPFILE, " Anonymous FTP Configuration ",
+ ANONFTP_DIALOG_X, ANONFTP_DIALOG_Y, ANONFTP_DIALOG_WIDTH, ANONFTP_DIALOG_HEIGHT))) {
+ beep();
+ msgConfirm("Cannot open anonymous ftp dialog window!!");
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+
+ /* Draw a sub-box for the path configuration */
+ draw_box(ds_win, ANONFTP_DIALOG_Y + 7, ANONFTP_DIALOG_X + 8,
+ ANONFTP_DIALOG_HEIGHT - 11, ANONFTP_DIALOG_WIDTH - 17,
+ dialog_attr, border_attr);
+ wattrset(ds_win, dialog_attr);
+ sprintf(title, " Path Configuration ");
+ mvwaddstr(ds_win, ANONFTP_DIALOG_Y + 7, ANONFTP_DIALOG_X + 22, title);
+
+ /** Initialize the config Data Structure **/
+ bzero(&tconf, sizeof(tconf));
+
+ SAFE_STRCPY(tconf.group, FTP_GROUP);
+ SAFE_STRCPY(tconf.upload, FTP_UPLOAD);
+ SAFE_STRCPY(tconf.comment, FTP_COMMENT);
+ SAFE_STRCPY(tconf.homedir, FTP_HOMEDIR);
+ sprintf(tconf.uid, "%d", FTP_UID);
+
+ /* Some more initialisation before we go into the main input loop */
+ obj = initLayoutDialog(ds_win, layout, ANONFTP_DIALOG_X, ANONFTP_DIALOG_Y, &max);
+
+ cancelbutton = okbutton = 0;
+ while (layoutDialogLoop(ds_win, layout, &obj, &n, max, &cancelbutton, &cancel));
+
+ /* Clear this crap off the screen */
+ delwin(ds_win);
+ use_helpfile(NULL);
+ restorescr(w);
+ if (cancel)
+ return DITEM_FAILURE;
+ return DITEM_SUCCESS;
+}
+
+int
+configAnonFTP(dialogMenuItem *self __unused)
+{
+ int i;
+
+
+ if (msgYesNo("Anonymous FTP permits un-authenticated users to connect to the system\n"
+ "FTP server, if FTP service is enabled. Anonymous users are\n"
+ "restricted to a specific subset of the file system, and the default\n"
+ "configuration provides a drop-box incoming directory to which uploads\n"
+ "are permitted. You must separately enable both inetd(8), and enable\n"
+ "ftpd(8) in inetd.conf(5) for FTP services to be available. If you\n"
+ "did not do so earlier, you will have the opportunity to enable inetd(8)\n"
+ "again later.\n\n"
+ "If you want the server to be read-only you should leave the upload\n"
+ "directory option empty and add the -r command-line option to ftpd(8)\n"
+ "in inetd.conf(5)\n\n"
+ "Do you wish to continue configuring anonymous FTP?")) {
+ return DITEM_FAILURE;
+ }
+
+ /* Be optimistic */
+ i = DITEM_SUCCESS;
+
+ i = anonftpOpenDialog();
+ if (DITEM_STATUS(i) != DITEM_SUCCESS) {
+ msgConfirm("Configuration of Anonymous FTP cancelled per user request.");
+ return i;
+ }
+
+ /*** Use defaults for any invalid values ***/
+ if (atoi(tconf.uid) <= 0)
+ sprintf(tconf.uid, "%d", FTP_UID);
+
+ if (!tconf.group[0])
+ SAFE_STRCPY(tconf.group, FTP_GROUP);
+
+ /*** If the user did not specify a directory, use default ***/
+
+ if (tconf.homedir[strlen(tconf.homedir) - 1] == '/')
+ tconf.homedir[strlen(tconf.homedir) - 1] = '\0';
+
+ if (!tconf.homedir[0])
+ SAFE_STRCPY(tconf.homedir, FTP_HOMEDIR);
+
+ /*** If HomeDir does not exist, create it ***/
+
+ if (!directory_exists(tconf.homedir))
+ vsystem("mkdir -p %s", tconf.homedir);
+
+ if (directory_exists(tconf.homedir)) {
+ msgNotify("Configuring %s for use by anon FTP.", tconf.homedir);
+ vsystem("chmod 555 %s && chown root:%s %s", tconf.homedir, tconf.group, tconf.homedir);
+ vsystem("mkdir %s/etc && chmod 555 %s/etc", tconf.homedir, tconf.homedir);
+ vsystem("mkdir -p %s/pub", tconf.homedir);
+ if (tconf.upload[0]) {
+ vsystem("mkdir -p %s/%s", tconf.homedir, tconf.upload);
+ vsystem("chmod 1777 %s/%s", tconf.homedir, tconf.upload);
+ }
+
+ if (DITEM_STATUS(createFtpUser()) == DITEM_SUCCESS) {
+ msgNotify("Copying password information for anon FTP.");
+ vsystem("awk -F: '{if ((substr($1, 1, 1) != \"+\") && (substr($1, 1, 1) != \"-\") && ($3 < 10 || $1 == \"ftp\")) print $0}' /etc/master.passwd > %s/etc/master.passwd", tconf.homedir);
+ vsystem("/usr/sbin/pwd_mkdb -d %s/etc %s/etc/master.passwd && chmod 444 %s/etc/pwd.db", tconf.homedir, tconf.homedir, tconf.homedir);
+ vsystem("rm -f %s/etc/master.passwd %s/etc/spwd.db", tconf.homedir, tconf.homedir);
+ vsystem("awk -F: '!/^#/ {if ((substr($1, 1, 1) != \"+\") && (substr($1, 1, 1) != \"-\") && ($3 < 100)) printf \"%%s:*:%%s:\\n\", $1, $3}' /etc/group > %s/etc/group && chmod 444 %s/etc/group", tconf.homedir, tconf.homedir);
+ vsystem("chown -R root:%s %s/pub", tconf.group, tconf.homedir);
+ }
+ else {
+ msgConfirm("Unable to create FTP user! Anonymous FTP setup failed.");
+ i = DITEM_FAILURE;
+ }
+
+ if (!msgYesNo("Create a welcome message file for anonymous FTP users?")) {
+ char cmd[256];
+ vsystem("echo Your welcome message here. > %s/etc/%s", tconf.homedir, MOTD_FILE);
+ sprintf(cmd, "%s %s/etc/%s", variable_get(VAR_EDITOR), tconf.homedir, MOTD_FILE);
+ if (!systemExecute(cmd))
+ i = DITEM_SUCCESS;
+ else
+ i = DITEM_FAILURE;
+ }
+ }
+ else {
+ msgConfirm("Invalid Directory: %s\n"
+ "Anonymous FTP will not be set up.", tconf.homedir);
+ i = DITEM_FAILURE;
+ }
+ if (DITEM_STATUS(i) == DITEM_SUCCESS)
+ variable_set2("anon_ftp", "YES", 0);
+ return i | DITEM_RESTORE;
+}
diff --git a/usr.sbin/sysinstall/biosmptable.c b/usr.sbin/sysinstall/biosmptable.c
new file mode 100644
index 0000000..5a507bd
--- /dev/null
+++ b/usr.sbin/sysinstall/biosmptable.c
@@ -0,0 +1,275 @@
+/*-
+ * Copyright (c) 2005 Sandvine Incorporated. All righs reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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: Ed Maste <emaste@phaedrus.sandvine.ca>
+ */
+
+/*
+ * This module detects Intel Multiprocessor spec info (mptable) and returns
+ * the number of cpu's identified.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <machine/mptable.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <paths.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "sysinstall.h"
+
+#define MPFPS_SIG "_MP_"
+#define MPCTH_SIG "PCMP"
+
+#define PTOV(pa) ((off_t)(pa))
+
+static mpfps_t biosmptable_find_mpfps(void);
+static mpfps_t biosmptable_search_mpfps(off_t base, int length);
+static mpcth_t biosmptable_check_mpcth(off_t addr);
+
+static int memopen(void);
+static void memclose(void);
+
+int
+biosmptable_detect(void)
+{
+ mpfps_t mpfps;
+ mpcth_t mpcth;
+ char *entry_type_p;
+ proc_entry_ptr proc;
+ int ncpu, i;
+
+ if (!memopen())
+ return -1; /* XXX 0? */
+ /* locate and validate the mpfps */
+ mpfps = biosmptable_find_mpfps();
+ mpcth = NULL;
+ if (mpfps == NULL) {
+ ncpu = 0;
+ } else if (mpfps->config_type != 0) {
+ /*
+ * If thie config_type is nonzero then this is a default configuration
+ * from Chapter 5 in the MP spec. Report 2 cpus and 1 I/O APIC.
+ */
+ ncpu = 2;
+ } else {
+ ncpu = 0;
+ mpcth = biosmptable_check_mpcth(PTOV(mpfps->pap));
+ if (mpcth != NULL) {
+ entry_type_p = (char *)(mpcth + 1);
+ for (i = 0; i < mpcth->entry_count; i++) {
+ switch (*entry_type_p) {
+ case 0:
+ entry_type_p += sizeof(struct PROCENTRY);
+ proc = (proc_entry_ptr) entry_type_p;
+ msgDebug("MPTable: Found CPU APIC ID %d %s\n",
+ proc->apic_id,
+ proc->cpu_flags & PROCENTRY_FLAG_EN ?
+ "enabled" : "disabled");
+ if (proc->cpu_flags & PROCENTRY_FLAG_EN)
+ ncpu++;
+ break;
+ case 1:
+ entry_type_p += sizeof(struct BUSENTRY);
+ break;
+ case 2:
+ entry_type_p += sizeof(struct IOAPICENTRY);
+ break;
+ case 3:
+ case 4:
+ entry_type_p += sizeof(struct INTENTRY);
+ break;
+ default:
+ msgDebug("%s: unknown mptable entry type (%d)\n",
+ __func__, *entry_type_p);
+ goto done; /* XXX error return? */
+ }
+ }
+ done:
+ ;
+ }
+ }
+ memclose();
+ if (mpcth != NULL)
+ free(mpcth);
+ if (mpfps != NULL)
+ free(mpfps);
+
+ return ncpu;
+}
+
+static int pfd = -1;
+
+static int
+memopen(void)
+{
+ if (pfd < 0) {
+ pfd = open(_PATH_MEM, O_RDONLY);
+ if (pfd < 0)
+ warn("%s: cannot open", _PATH_MEM);
+ }
+ return pfd >= 0;
+}
+
+static void
+memclose(void)
+{
+ if (pfd >= 0) {
+ close(pfd);
+ pfd = -1;
+ }
+}
+
+static int
+memread(off_t addr, void* entry, size_t size)
+{
+ if ((size_t)pread(pfd, entry, size, addr) != size) {
+ warn("pread (%zu @ 0x%llx)", size, addr);
+ return 0;
+ }
+ return 1;
+}
+
+
+/*
+ * Find the MP Floating Pointer Structure. See the MP spec section 4.1.
+ */
+static mpfps_t
+biosmptable_find_mpfps(void)
+{
+ mpfps_t mpfps;
+ uint16_t addr;
+
+ /* EBDA is the 1 KB addressed by the 16 bit pointer at 0x40E. */
+ if (!memread(PTOV(0x40E), &addr, sizeof(addr)))
+ return (NULL);
+ mpfps = biosmptable_search_mpfps(PTOV(addr << 4), 0x400);
+ if (mpfps != NULL)
+ return (mpfps);
+
+ /* Check the BIOS. */
+ mpfps = biosmptable_search_mpfps(PTOV(0xf0000), 0x10000);
+ if (mpfps != NULL)
+ return (mpfps);
+
+ return (NULL);
+}
+
+static mpfps_t
+biosmptable_search_mpfps(off_t base, int length)
+{
+ mpfps_t mpfps;
+ u_int8_t *cp, sum;
+ int ofs, idx;
+
+ mpfps = malloc(sizeof(*mpfps));
+ if (mpfps == NULL) {
+ msgDebug("%s: unable to malloc space for "
+ "MP Floating Pointer Structure\n", __func__);
+ return (NULL);
+ }
+ /* search on 16-byte boundaries */
+ for (ofs = 0; ofs < length; ofs += 16) {
+ if (!memread(base + ofs, mpfps, sizeof(*mpfps)))
+ break;
+
+ /* compare signature, validate checksum */
+ if (!strncmp(mpfps->signature, MPFPS_SIG, strlen(MPFPS_SIG))) {
+ cp = (u_int8_t *)mpfps;
+ sum = 0;
+ /* mpfps is 16 bytes, or one "paragraph" */
+ if (mpfps->length != 1) {
+ msgDebug("%s: bad mpfps length (%d)\n",
+ __func__, mpfps->length);
+ continue;
+ }
+ for (idx = 0; idx < mpfps->length * 16; idx++)
+ sum += *(cp + idx);
+ if (sum != 0) {
+ msgDebug("%s: bad mpfps checksum (%d)\n", __func__, sum);
+ continue;
+ }
+ return (mpfps);
+ }
+ }
+ free(mpfps);
+ return (NULL);
+}
+
+static mpcth_t
+biosmptable_check_mpcth(off_t addr)
+{
+ mpcth_t mpcth;
+ u_int8_t *cp, sum;
+ int idx, table_length;
+
+ /* mpcth must be in the first 1MB */
+ if ((u_int32_t)addr >= 1024 * 1024) {
+ msgDebug("%s: bad mpcth address (0x%llx)\n", __func__, addr);
+ return (NULL);
+ }
+
+ mpcth = malloc(sizeof(*mpcth));
+ if (mpcth == NULL) {
+ msgDebug("%s: unable to malloc space for "
+ "MP Configuration Table Header\n", __func__);
+ return (NULL);
+ }
+ if (!memread(addr, mpcth, sizeof(*mpcth)))
+ goto bad;
+ /* Compare signature and validate checksum. */
+ if (strncmp(mpcth->signature, MPCTH_SIG, strlen(MPCTH_SIG)) != 0) {
+ msgDebug("%s: bad mpcth signature\n", __func__);
+ goto bad;
+ }
+ table_length = mpcth->base_table_length;
+ mpcth = realloc(mpcth, table_length);
+ if (mpcth == NULL) {
+ msgDebug("%s: unable to realloc space for mpcth (len %u)\n",
+ __func__, table_length);
+ return (NULL);
+ }
+ if (!memread(addr, mpcth, table_length))
+ goto bad;
+ cp = (u_int8_t *)mpcth;
+ sum = 0;
+ for (idx = 0; idx < mpcth->base_table_length; idx++)
+ sum += *(cp + idx);
+ if (sum != 0) {
+ msgDebug("%s: bad mpcth checksum (%d)\n", __func__, sum);
+ goto bad;
+ }
+
+ return mpcth;
+bad:
+ free(mpcth);
+ return (NULL);
+}
diff --git a/usr.sbin/sysinstall/cdrom.c b/usr.sbin/sysinstall/cdrom.c
new file mode 100644
index 0000000..a5029ec
--- /dev/null
+++ b/usr.sbin/sysinstall/cdrom.c
@@ -0,0 +1,221 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ * Copyright (c) 1995
+ * Gary J Palmer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/* These routines deal with getting things off of CDROM media */
+
+#include "sysinstall.h"
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <grp.h>
+#include <fcntl.h>
+#include <libutil.h>
+
+#define CD9660
+#include <sys/mount.h>
+#include <isofs/cd9660/cd9660_mount.h>
+#undef CD9660
+
+static Boolean cdromMounted;
+static Boolean previouslyMounted; /* Was the disc already mounted? */
+static char mountpoint[MAXPATHLEN] = "/dist";
+int CDROMInitQuiet;
+
+static properties
+read_props(char *name)
+{
+ int fd;
+ properties n;
+
+ fd = open(name, O_RDONLY);
+ if (fd == -1)
+ return NULL;
+ n = properties_read(fd);
+ close(fd);
+ return n;
+}
+
+Boolean
+mediaInitCDROM(Device *dev)
+{
+ struct iso_args args;
+ properties cd_attr = NULL;
+ char *cp = NULL;
+ Boolean readInfo = TRUE;
+ static Boolean bogusCDOK = FALSE;
+ int err;
+
+ if (cdromMounted)
+ return TRUE;
+
+ Mkdir(mountpoint);
+ bzero(&args, sizeof(args));
+ args.fspec = dev->devname;
+ args.flags = 0;
+ err = mount("cd9660", mountpoint, MNT_RDONLY, (caddr_t) &args);
+ /* If disc inserted too recently first access generates EIO, try again */
+ if (err == -1 && errno == EIO)
+ err = mount("cd9660", mountpoint, MNT_RDONLY, (caddr_t) &args);
+ if (err == -1) {
+ if (errno == EINVAL) {
+ msgConfirm("The disc in your drive looks more like an Audio disc than a FreeBSD release.");
+ return FALSE;
+ }
+ if (errno == EBUSY) {
+ /* Perhaps the CDROM drive is already mounted as /cdrom */
+ if (file_readable("/cdrom/cdrom.inf")) {
+ previouslyMounted = TRUE;
+ strlcpy(mountpoint, "/cdrom", 7);
+ errno = 0;
+ }
+ }
+ if (errno) {
+ if (!CDROMInitQuiet)
+ msgConfirm("Error mounting %s on %s: %s (%u)", dev->devname,
+ mountpoint, strerror(errno), errno);
+ return FALSE;
+ }
+ }
+ cdromMounted = TRUE;
+
+ if (!file_readable(string_concat(mountpoint, "/cdrom.inf")) && !bogusCDOK) {
+ if (msgYesNo("Warning: The disc currently in the drive is either not a FreeBSD\n"
+ "disc or it is an older (pre 2.1.5) FreeBSD CD which does not\n"
+ "have a version number on it. Do you wish to use this disc anyway?") != 0) {
+ if (!previouslyMounted)
+ unmount(mountpoint, MNT_FORCE);
+ cdromMounted = FALSE;
+ return FALSE;
+ }
+ else {
+ readInfo = FALSE;
+ bogusCDOK = TRUE;
+ }
+ }
+
+ if (readInfo) {
+ if (!(cd_attr = read_props(string_concat(mountpoint, "/cdrom.inf")))
+ || !(cp = property_find(cd_attr, "CD_VERSION"))) {
+ msgConfirm("Unable to find a %s/cdrom.inf file.\n"
+ "Either this is not a FreeBSD disc, there is a problem with\n"
+ "the CDROM driver or something is wrong with your hardware.\n"
+ "Please fix this problem (check the console logs on VTY2) and\n"
+ "try again.", mountpoint);
+ }
+ else {
+ if (variable_cmp(VAR_RELNAME, cp) &&
+ variable_cmp(VAR_RELNAME, "any") &&
+ variable_cmp(cp, "any") &&
+ !bogusCDOK) {
+ msgConfirm("Warning: The version of the FreeBSD disc currently in the drive\n"
+ "(%s) does not match the version of the boot floppy\n"
+ "(%s).\n\n"
+ "If this is intentional, to avoid this message in the future\n"
+ "please visit the Options editor to set the boot floppy version\n"
+ "string to match that of the disc before selecting it as your\n"
+ "installation media.", cp, variable_get(VAR_RELNAME));
+
+ if (msgYesNo("Would you like to try and use this disc anyway?") != 0) {
+ if (!previouslyMounted)
+ unmount(mountpoint, MNT_FORCE);
+ cdromMounted = FALSE;
+ properties_free(cd_attr);
+ return FALSE;
+ }
+ else
+ bogusCDOK = TRUE;
+ }
+ if ((cp = property_find(cd_attr, "CD_MACHINE_ARCH")) != NULL) {
+ if (strcmp(cp, "any") &&
+#if defined(PC98)
+ strcmp(cp, "pc98")) {
+#elif defined(__sparc64__)
+ strcmp(cp, "sparc64")) {
+#else
+ strcmp(cp, "x86")) {
+#endif
+ msgConfirm("Fatal: The FreeBSD install CD/DVD currently in the drive\n"
+ "is for the %s architecture, not the machine you're using.\n\n"
+
+ "Please use the correct installation CD/DVD for your machine type.", cp);
+
+ if (!previouslyMounted)
+ unmount(mountpoint, MNT_FORCE);
+ cdromMounted = FALSE;
+ properties_free(cd_attr);
+ return FALSE;
+ }
+ }
+ if ((cp = property_find(cd_attr, "CD_VOLUME")) != NULL) {
+ dev->volume = atoi(cp);
+ /* XXX - Sanity check the volume here? */
+ msgDebug("CD Volume %d initialized!\n", dev->volume);
+ } else {
+ dev->volume = 0;
+ }
+ }
+ }
+ if (cd_attr)
+ properties_free(cd_attr);
+ return TRUE;
+}
+
+FILE *
+mediaGetCDROM(Device *dev, char *file, Boolean probe)
+{
+ return mediaGenericGet(mountpoint, file);
+}
+
+void
+mediaShutdownCDROM(Device *dev)
+{
+ if (!cdromMounted)
+ return;
+
+ if (previouslyMounted) {
+ cdromMounted = FALSE;
+ return;
+ }
+
+ if (unmount(mountpoint, MNT_FORCE) != 0)
+ msgConfirm("Could not unmount the CDROM/DVD from %s: %s", mountpoint, strerror(errno));
+ else
+ cdromMounted = FALSE;
+}
diff --git a/usr.sbin/sysinstall/command.c b/usr.sbin/sysinstall/command.c
new file mode 100644
index 0000000..33ebc35
--- /dev/null
+++ b/usr.sbin/sysinstall/command.c
@@ -0,0 +1,184 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sysinstall.h"
+
+#define MAX_NUM_COMMANDS 10
+
+typedef struct {
+ char key[FILENAME_MAX];
+ struct {
+ enum { CMD_SHELL, CMD_FUNCTION } type;
+ void *ptr, *data;
+ } cmds[MAX_NUM_COMMANDS];
+ int ncmds;
+} Command;
+
+#define MAX_CMDS 200
+static Command *commandStack[MAX_CMDS];
+int numCommands;
+
+/* Nuke the command stack */
+void
+command_clear(void)
+{
+ int i, j;
+
+ for (i = 0; i < numCommands; i++)
+ for (j = 0; j < commandStack[i]->ncmds; j++)
+ if (commandStack[i]->cmds[j].type == CMD_SHELL)
+ free(commandStack[i]->cmds[j].ptr);
+ free(commandStack[i]);
+ numCommands = 0;
+}
+
+static void
+addit(char *key, int type, void *cmd, void *data)
+{
+ int i;
+
+ /* First, look for the key already present and add a command to it if found */
+ for (i = 0; i < numCommands; i++) {
+ if (!strcmp(commandStack[i]->key, key)) {
+ if (commandStack[i]->ncmds == MAX_NUM_COMMANDS)
+ msgFatal("More than %d commands stacked up behind %s??", MAX_NUM_COMMANDS, key);
+ commandStack[i]->cmds[commandStack[i]->ncmds].type = type;
+ commandStack[i]->cmds[commandStack[i]->ncmds].ptr = cmd;
+ commandStack[i]->cmds[commandStack[i]->ncmds].data = data;
+ ++(commandStack[i]->ncmds);
+ return;
+ }
+ }
+ if (numCommands == MAX_CMDS)
+ msgFatal("More than %d commands accumulated??", MAX_CMDS);
+
+ /* If we fell to here, it's a new key */
+ commandStack[numCommands] = safe_malloc(sizeof(Command));
+ strcpy(commandStack[numCommands]->key, key);
+ commandStack[numCommands]->ncmds = 1;
+ commandStack[numCommands]->cmds[0].type = type;
+ commandStack[numCommands]->cmds[0].ptr = cmd;
+ commandStack[numCommands]->cmds[0].data = data;
+ ++numCommands;
+}
+
+/* Add a shell command under a given key */
+void
+command_shell_add(char *key, char *fmt, ...)
+{
+ va_list args;
+ char *cmd;
+
+ cmd = (char *)safe_malloc(256);
+ va_start(args, fmt);
+ vsnprintf(cmd, 256, fmt, args);
+ va_end(args);
+
+ addit(key, CMD_SHELL, cmd, NULL);
+}
+
+/* Add a shell command under a given key */
+void
+command_func_add(char *key, commandFunc func, void *data)
+{
+ addit(key, CMD_FUNCTION, func, data);
+}
+
+static int
+sort_compare(Command *p1, Command *p2)
+{
+ if (!p1 && !p2)
+ return 0;
+ else if (!p1 && p2) /* NULL has a "greater" value for commands */
+ return 1;
+ else if (p1 && !p2)
+ return -1;
+ else
+ return strcmp(p1->key, p2->key);
+}
+
+void
+command_sort(void)
+{
+ int i, j;
+
+ commandStack[numCommands] = NULL;
+ /* Just do a crude bubble sort since the list is small */
+ for (i = 0; i < numCommands; i++) {
+ for (j = 0; j < numCommands; j++) {
+ if (sort_compare(commandStack[j], commandStack[j + 1]) > 0) {
+ Command *tmp = commandStack[j];
+
+ commandStack[j] = commandStack[j + 1];
+ commandStack[j + 1] = tmp;
+ }
+ }
+ }
+}
+
+/* Run all accumulated commands in sorted order */
+void
+command_execute(void)
+{
+ int i, j, ret;
+ commandFunc func;
+
+ for (i = 0; i < numCommands; i++) {
+ for (j = 0; j < commandStack[i]->ncmds; j++) {
+ /* If it's a shell command, run system on it */
+ if (commandStack[i]->cmds[j].type == CMD_SHELL) {
+ msgNotify("Doing %s", (char *)commandStack[i]->cmds[j].ptr);
+ ret = vsystem("%s", (char *)commandStack[i]->cmds[j].ptr);
+ if (isDebug())
+ msgDebug("Command `%s' returns status %d\n",
+ (char *)commandStack[i]->cmds[j].ptr, ret);
+ }
+ else {
+ /* It's a function pointer - call it with the key and
+ the data */
+ func = (commandFunc)commandStack[i]->cmds[j].ptr;
+ if (isDebug())
+ msgDebug("%p: Execute(%s, %s)\n",
+ func, commandStack[i]->key,
+ (char *)commandStack[i]->cmds[j].data);
+ ret = (*func)(commandStack[i]->key, commandStack[i]->cmds[j].data);
+ if (isDebug())
+ msgDebug("Function @ %p returns status %d\n",
+ commandStack[i]->cmds[j].ptr, ret);
+ }
+ }
+ }
+}
diff --git a/usr.sbin/sysinstall/config.c b/usr.sbin/sysinstall/config.c
new file mode 100644
index 0000000..ff4c1f9
--- /dev/null
+++ b/usr.sbin/sysinstall/config.c
@@ -0,0 +1,1074 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sysinstall.h"
+#include <sys/disklabel.h>
+#include <sys/wait.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/mount.h>
+#include <libdisk.h>
+#include <time.h>
+#include <kenv.h>
+
+static Chunk *chunk_list[MAX_CHUNKS];
+static int nchunks;
+static int rootdev_is_od;
+
+/* arg to sort */
+static int
+chunk_compare(Chunk *c1, Chunk *c2)
+{
+ if (!c1 && !c2)
+ return 0;
+ else if (!c1 && c2)
+ return 1;
+ else if (c1 && !c2)
+ return -1;
+ else if (!c1->private_data && !c2->private_data)
+ return 0;
+ else if (c1->private_data && !c2->private_data)
+ return 1;
+ else if (!c1->private_data && c2->private_data)
+ return -1;
+ else
+ return strcmp(((PartInfo *)(c1->private_data))->mountpoint, ((PartInfo *)(c2->private_data))->mountpoint);
+}
+
+static void
+chunk_sort(void)
+{
+ int i, j;
+
+ for (i = 0; i < nchunks; i++) {
+ for (j = 0; j < nchunks; j++) {
+ if (chunk_compare(chunk_list[j], chunk_list[j + 1]) > 0) {
+ Chunk *tmp = chunk_list[j];
+
+ chunk_list[j] = chunk_list[j + 1];
+ chunk_list[j + 1] = tmp;
+ }
+ }
+ }
+}
+
+static void
+check_rootdev(Chunk **list, int n)
+{
+ int i;
+ Chunk *c;
+
+ rootdev_is_od = 0;
+ for (i = 0; i < n; i++) {
+ c = *list++;
+ if (c->type == part && (c->flags & CHUNK_IS_ROOT)
+ && strncmp(c->disk->name, "od", 2) == 0)
+ rootdev_is_od = 1;
+ }
+}
+
+static char *
+name_of(Chunk *c1)
+{
+ return c1->name;
+}
+
+static char *
+mount_point(Chunk *c1)
+{
+ if (c1->type == part && c1->subtype == FS_SWAP)
+ return "none";
+ else if (c1->type == part || c1->type == fat || c1->type == efi)
+ return ((PartInfo *)c1->private_data)->mountpoint;
+ return "/bogus";
+}
+
+static char *
+fstype(Chunk *c1)
+{
+ if (c1->type == fat || c1->type == efi)
+ return "msdosfs";
+ else if (c1->type == part) {
+ if (c1->subtype != FS_SWAP)
+ return "ufs";
+ else
+ return "swap";
+ }
+ return "bogus";
+}
+
+static char *
+fstype_short(Chunk *c1)
+{
+ if (c1->type == part) {
+ if (c1->subtype != FS_SWAP) {
+ if (rootdev_is_od == 0 && strncmp(c1->name, "od", 2) == 0)
+ return "rw,noauto";
+ else
+ return "rw";
+ }
+ else
+ return "sw";
+ }
+ else if (c1->type == fat) {
+ if (strncmp(c1->name, "od", 2) == 0)
+ return "ro,noauto";
+ else
+ return "ro";
+ }
+ else if (c1->type == efi)
+ return "rw";
+
+ return "bog";
+}
+
+static int
+seq_num(Chunk *c1)
+{
+ if (c1->type == part && c1->subtype != FS_SWAP) {
+ if (rootdev_is_od == 0 && strncmp(c1->name, "od", 2) == 0)
+ return 0;
+ else if (c1->flags & CHUNK_IS_ROOT)
+ return 1;
+ else
+ return 2;
+ }
+ return 0;
+}
+
+int
+configFstab(dialogMenuItem *self)
+{
+ Device **devs;
+ Disk *disk;
+ FILE *fstab;
+ int i, cnt;
+ Chunk *c1, *c2;
+
+ if (!RunningAsInit) {
+ if (file_readable("/etc/fstab"))
+ return DITEM_SUCCESS;
+ else {
+ msgConfirm("Attempting to rebuild your /etc/fstab file. Warning: If you had\n"
+ "any CD devices in use before running %s then they may NOT\n"
+ "be found by this run!", ProgName);
+ }
+ }
+
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("No disks found!");
+ return DITEM_FAILURE;
+ }
+
+ /* Record all the chunks */
+ nchunks = 0;
+ for (i = 0; devs[i]; i++) {
+ if (!devs[i]->enabled)
+ continue;
+ disk = (Disk *)devs[i]->private;
+ if (!disk->chunks)
+ msgFatal("No chunk list found for %s!", disk->name);
+ for (c1 = disk->chunks->part; c1; c1 = c1->next) {
+#ifdef __powerpc__
+ if (c1->type == apple) {
+#else
+ if (c1->type == freebsd) {
+#endif
+ for (c2 = c1->part; c2; c2 = c2->next) {
+ if (c2->type == part && (c2->subtype == FS_SWAP || c2->private_data))
+ chunk_list[nchunks++] = c2;
+ }
+ }
+ else if (((c1->type == fat || c1->type == efi || c1->type == part) &&
+ c1->private_data) || (c1->type == part && c1->subtype == FS_SWAP))
+ chunk_list[nchunks++] = c1;
+ }
+ }
+ chunk_list[nchunks] = 0;
+ chunk_sort();
+
+ fstab = fopen("/etc/fstab", "w");
+ if (!fstab) {
+ msgConfirm("Unable to create a new /etc/fstab file! Manual intervention\n"
+ "will be required.");
+ return DITEM_FAILURE;
+ }
+
+ check_rootdev(chunk_list, nchunks);
+
+ /* Go for the burn */
+ msgDebug("Generating /etc/fstab file\n");
+ fprintf(fstab, "# Device\t\tMountpoint\tFStype\tOptions\t\tDump\tPass#\n");
+ for (i = 0; i < nchunks; i++)
+ fprintf(fstab, "/dev/%s\t\t%s\t\t%s\t%s\t\t%d\t%d\n", name_of(chunk_list[i]), mount_point(chunk_list[i]),
+ fstype(chunk_list[i]), fstype_short(chunk_list[i]), seq_num(chunk_list[i]), seq_num(chunk_list[i]));
+
+ /* Now look for the CDROMs */
+ devs = deviceFind(NULL, DEVICE_TYPE_CDROM);
+ cnt = deviceCount(devs);
+
+ /* Write out the CDROM entries */
+ for (i = 0; i < cnt; i++) {
+ char cdname[10];
+
+ sprintf(cdname, "/cdrom%s", i ? itoa(i) : "");
+ if (Mkdir(cdname))
+ msgConfirm("Unable to make mount point for: %s", cdname);
+ else
+ fprintf(fstab, "/dev/%s\t\t%s\t\tcd9660\tro,noauto\t0\t0\n", devs[i]->name, cdname);
+ }
+
+ fclose(fstab);
+ if (isDebug())
+ msgDebug("Wrote out /etc/fstab file\n");
+ return DITEM_SUCCESS;
+}
+
+/* Do the work of sucking in a config file.
+ * config is the filename to read in.
+ * lines is a fixed (max) sized array of char*
+ * returns number of lines read. line contents
+ * are malloc'd and must be freed by the caller.
+ */
+static int
+readConfig(char *config, char **lines, int max)
+{
+ FILE *fp;
+ char line[256];
+ int i, nlines;
+
+ fp = fopen(config, "r");
+ if (!fp)
+ return -1;
+
+ nlines = 0;
+ /* Read in the entire file */
+ for (i = 0; i < max; i++) {
+ if (!fgets(line, sizeof line, fp))
+ break;
+ lines[nlines++] = strdup(line);
+ }
+ fclose(fp);
+ if (isDebug())
+ msgDebug("readConfig: Read %d lines from %s.\n", nlines, config);
+ return nlines;
+}
+
+#define MAX_LINES 2000 /* Some big number we're not likely to ever reach - I'm being really lazy here, I know */
+
+static void
+readConfigFile(char *config, int marked)
+{
+ char *lines[MAX_LINES], *cp, *cp2;
+ int i, nlines;
+
+ nlines = readConfig(config, lines, MAX_LINES);
+ if (nlines == -1)
+ return;
+
+ for (i = 0; i < nlines; i++) {
+ /* Skip the comments & non-variable settings */
+ if (lines[i][0] == '#' || !(cp = index(lines[i], '='))) {
+ free(lines[i]);
+ continue;
+ }
+ *cp++ = '\0';
+ /* Find quotes */
+ if ((cp2 = index(cp, '"')) || (cp2 = index(cp, '\047'))) {
+ cp = cp2 + 1;
+ cp2 = index(cp, *cp2);
+ }
+ /* If valid quotes, use it */
+ if (cp2) {
+ *cp2 = '\0';
+ /* If we have a legit value, set it */
+ if (strlen(cp))
+ variable_set2(lines[i], cp, marked);
+ }
+ free(lines[i]);
+ }
+}
+
+/* Load the environment from rc.conf file(s) */
+void
+configEnvironmentRC_conf(void)
+{
+ static struct {
+ char *fname;
+ int marked;
+ } configs[] = {
+ { "/etc/defaults/rc.conf", 0 },
+ { "/etc/rc.conf", 0 },
+ { "/etc/rc.conf.local", 0 },
+ { NULL, 0 },
+ };
+ int i;
+
+ for (i = 0; configs[i].fname; i++) {
+ if (file_readable(configs[i].fname))
+ readConfigFile(configs[i].fname, configs[i].marked);
+ }
+}
+
+/* Load the environment from a resolv.conf file */
+void
+configEnvironmentResolv(char *config)
+{
+ char *lines[MAX_LINES];
+ int i, nlines;
+
+ nlines = readConfig(config, lines, MAX_LINES);
+ if (nlines == -1)
+ return;
+ for (i = 0; i < nlines; i++) {
+ Boolean name_set = variable_get(VAR_NAMESERVER) ? 1 : 0;
+
+ if (!strncmp(lines[i], "domain", 6) && !variable_get(VAR_DOMAINNAME))
+ variable_set2(VAR_DOMAINNAME, string_skipwhite(string_prune(lines[i] + 6)), 0);
+ else if (!name_set && !strncmp(lines[i], "nameserver", 10)) {
+ /* Only take the first nameserver setting - we're lame */
+ variable_set2(VAR_NAMESERVER, string_skipwhite(string_prune(lines[i] + 10)), 0);
+ }
+ free(lines[i]);
+ }
+}
+
+/* Version of below for dispatch routines */
+int
+configRC(dialogMenuItem *unused)
+{
+ configRC_conf();
+ return DITEM_SUCCESS;
+}
+
+/*
+ * Write out rc.conf
+ *
+ * rc.conf is sorted if running as init and the needed utilities are
+ * present
+ *
+ * If rc.conf is sorted, all variables in rc.conf which conflict with
+ * the variables in the environment are removed from the original
+ * rc.conf
+ */
+void
+configRC_conf(void)
+{
+ char line[256];
+ FILE *rcSite, *rcOld;
+ Variable *v;
+ int write_header;
+ time_t t_loc;
+ char *cp;
+ static int did_marker = 0;
+ int do_sort;
+ int do_merge;
+ time_t tp;
+
+ configTtys();
+ write_header = !file_readable("/etc/rc.conf");
+ do_sort = RunningAsInit && file_readable("/usr/bin/sort") &&
+ file_readable("/usr/bin/uniq");
+ do_merge = do_sort && file_readable("/etc/rc.conf");
+
+ if(do_merge) {
+ rcSite = fopen("/etc/rc.conf.new", "w");
+ } else
+ rcSite = fopen("/etc/rc.conf", "a");
+ if (rcSite == NULL) {
+ msgError("Error opening new rc.conf for writing: %s (%u)", strerror(errno), errno);
+ return;
+ }
+
+ if (do_merge) {
+ /* "Copy" the old rc.conf */
+ rcOld = fopen("/etc/rc.conf", "r");
+ if(!rcOld) {
+ msgError("Error opening rc.conf for reading: %s (%u)", strerror(errno), errno);
+ return;
+ }
+ while(fgets(line, sizeof(line), rcOld)) {
+ if(line[0] == '#' || variable_check2(line) != 0)
+ fprintf(rcSite, "%s", line);
+ else {
+ if (variable_get(VAR_KEEPRCCONF) != NULL)
+ fprintf(rcSite, "%s", line);
+ else
+ fprintf(rcSite, "#REMOVED: %s", line);
+ }
+ }
+ fclose(rcOld);
+ } else if (write_header) {
+ fprintf(rcSite, "# This file now contains just the overrides from /etc/defaults/rc.conf.\n");
+ fprintf(rcSite, "# Please make all changes to this file, not to /etc/defaults/rc.conf.\n\n");
+ fprintf(rcSite, "# Enable network daemons for user convenience.\n");
+ if ((t_loc = time(NULL)) != -1 && (cp = ctime(&t_loc)))
+ fprintf(rcSite, "# Created: %s", cp);
+ }
+
+ /* Now do variable substitutions */
+ for (v = VarHead; v; v = v->next) {
+ if (v->dirty) {
+ if (!did_marker) {
+ time(&tp);
+ fprintf(rcSite, "# -- sysinstall generated deltas -- # "
+ "%s", ctime(&tp));
+ did_marker = 1;
+ }
+ fprintf(rcSite, "%s=\"%s\"\n", v->name, v->value);
+ v->dirty = 0;
+ }
+ }
+ fclose(rcSite);
+
+ if(do_merge) {
+ if(rename("/etc/rc.conf.new", "/etc/rc.conf") != 0) {
+ msgError("Error renaming temporary rc.conf: %s (%u)", strerror(errno), errno);
+ return;
+ }
+ }
+
+ /* Tidy up the resulting file if it's late enough in the installation
+ for sort and uniq to be available */
+ if (do_sort) {
+ (void)vsystem("sort /etc/rc.conf | uniq > /etc/rc.conf.new && mv /etc/rc.conf.new /etc/rc.conf");
+ }
+}
+
+int
+configSaver(dialogMenuItem *self)
+{
+ variable_set((char *)self->data, 1);
+ if (!variable_get(VAR_BLANKTIME))
+ variable_set2(VAR_BLANKTIME, "300", 1);
+ return DITEM_SUCCESS;
+}
+
+int
+configSaverTimeout(dialogMenuItem *self)
+{
+ return (variable_get_value(VAR_BLANKTIME,
+ "Enter time-out period in seconds for screen saver", 1) ?
+ DITEM_SUCCESS : DITEM_FAILURE);
+}
+
+int
+configNTP(dialogMenuItem *self)
+{
+ int status;
+
+ status = variable_get_value(VAR_NTPDATE_FLAGS,
+ "Enter the name of an NTP server", 1)
+ ? DITEM_SUCCESS : DITEM_FAILURE;
+ if (status == DITEM_SUCCESS) {
+ static char tmp[255];
+
+ snprintf(tmp, sizeof(tmp), "ntpdate_enable=YES,ntpdate_flags=%s",
+ variable_get(VAR_NTPDATE_FLAGS));
+ self->data = tmp;
+ dmenuSetVariables(self);
+ }
+ return status;
+}
+
+int
+configCountry(dialogMenuItem *self)
+{
+ int choice, scroll, curr, max;
+
+ WINDOW *w = savescr();
+
+ dialog_clear_norefresh();
+ dmenuSetDefaultItem(&MenuCountry, NULL, NULL,
+ VAR_COUNTRY "=" DEFAULT_COUNTRY, &choice, &scroll, &curr, &max);
+ dmenuOpen(&MenuCountry, &choice, &scroll, &curr, &max, FALSE);
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+int
+configUsers(dialogMenuItem *self)
+{
+ WINDOW *w = savescr();
+
+ dialog_clear_norefresh();
+ dmenuOpenSimple(&MenuUsermgmt, FALSE);
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+int
+configSecurelevel(dialogMenuItem *self)
+{
+ WINDOW *w = savescr();
+
+ dialog_clear_norefresh();
+ dmenuOpenSimple(&MenuSecurelevel, FALSE);
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+int
+configSecurelevelDisabled(dialogMenuItem *self)
+{
+
+ variable_set2("kern_securelevel_enable", "NO", 1);
+ return DITEM_SUCCESS;
+}
+
+int
+configSecurelevelSecure(dialogMenuItem *self)
+{
+
+ variable_set2("kern_securelevel_enable", "YES", 1);
+ variable_set2("kern_securelevel", "1", 1);
+ return DITEM_SUCCESS;
+}
+
+int
+configSecurelevelHighlySecure(dialogMenuItem *self)
+{
+
+ variable_set2("kern_securelevel_enable", "YES", 1);
+ variable_set2("kern_securelevel", "2", 1);
+ return DITEM_SUCCESS;
+}
+
+int
+configSecurelevelNetworkSecure(dialogMenuItem *self)
+{
+
+ variable_set2("kern_securelevel_enable", "YES", 1);
+ variable_set2("kern_securelevel", "3", 1);
+ return DITEM_SUCCESS;
+}
+
+int
+configResolv(dialogMenuItem *ditem)
+{
+ FILE *fp;
+ char *cp, *c6p, *dp, *hp;
+
+ cp = variable_get(VAR_NAMESERVER);
+ if (!cp || !*cp)
+ goto skip;
+ Mkdir("/etc");
+ fp = fopen("/etc/resolv.conf", "w");
+ if (!fp)
+ return DITEM_FAILURE;
+ if (variable_get(VAR_DOMAINNAME))
+ fprintf(fp, "domain\t%s\n", variable_get(VAR_DOMAINNAME));
+ fprintf(fp, "nameserver\t%s\n", cp);
+ fclose(fp);
+ if (isDebug())
+ msgDebug("Wrote out /etc/resolv.conf\n");
+
+skip:
+ dp = variable_get(VAR_DOMAINNAME);
+ cp = variable_get(VAR_IPADDR);
+ c6p = variable_get(VAR_IPV6ADDR);
+ hp = variable_get(VAR_HOSTNAME);
+ /* Tack ourselves into /etc/hosts */
+ fp = fopen("/etc/hosts", "w");
+ if (!fp)
+ return DITEM_FAILURE;
+ /* Add an entry for localhost */
+ if (dp) {
+ fprintf(fp, "::1\t\t\tlocalhost localhost.%s\n", dp);
+ fprintf(fp, "127.0.0.1\t\tlocalhost localhost.%s\n", dp);
+ } else {
+ fprintf(fp, "::1\t\t\tlocalhost\n");
+ fprintf(fp, "127.0.0.1\t\tlocalhost\n");
+ }
+ /* Now the host entries, if applicable */
+ if (((cp && cp[0] != '0') || (c6p && c6p[0] != '0')) && hp) {
+ char cp2[255];
+
+ if (!index(hp, '.'))
+ cp2[0] = '\0';
+ else {
+ SAFE_STRCPY(cp2, hp);
+ *(index(cp2, '.')) = '\0';
+ }
+ if (c6p && c6p[0] != '0') {
+ fprintf(fp, "%s\t%s %s\n", c6p, hp, cp2);
+ fprintf(fp, "%s\t%s.\n", c6p, hp);
+ }
+ if (cp && cp[0] != '0') {
+ fprintf(fp, "%s\t\t%s %s\n", cp, hp, cp2);
+ fprintf(fp, "%s\t\t%s.\n", cp, hp);
+ }
+ }
+ fclose(fp);
+ if (isDebug())
+ msgDebug("Wrote out /etc/hosts\n");
+ return DITEM_SUCCESS;
+}
+
+int
+configRouter(dialogMenuItem *self)
+{
+ int ret;
+
+ ret = variable_get_value(VAR_ROUTER,
+ "Please specify the router you wish to use. Routed is\n"
+ "provided with the stock system and gated is provided\n"
+ "as an optional package which this installation system\n"
+ "will attempt to load if you select gated. Any other\n"
+ "choice of routing daemon will be assumed to be something\n"
+ "the user intends to install themselves before rebooting\n"
+ "the system. If you don't want any routing daemon, choose NO", 1)
+ ? DITEM_SUCCESS : DITEM_FAILURE;
+
+ if (ret == DITEM_SUCCESS) {
+ char *cp = variable_get(VAR_ROUTER);
+
+ if (cp && strcmp(cp, "NO")) {
+ variable_set2(VAR_ROUTER_ENABLE, "YES", 1);
+ if (!strcmp(cp, "gated")) {
+ if (package_add("gated") != DITEM_SUCCESS) {
+ msgConfirm("Unable to load gated package. Falling back to no router.");
+ variable_unset(VAR_ROUTER);
+ variable_unset(VAR_ROUTERFLAGS);
+ variable_set2(VAR_ROUTER_ENABLE, "NO", 1);
+ cp = NULL;
+ }
+ }
+ if (cp) {
+ /* Now get the flags, if they chose a router */
+ ret = variable_get_value(VAR_ROUTERFLAGS,
+ "Please Specify the routing daemon flags; if you're running routed\n"
+ "then -q is the right choice for nodes and -s for gateway hosts.\n", 1)
+ ? DITEM_SUCCESS : DITEM_FAILURE;
+ if (ret != DITEM_SUCCESS)
+ variable_unset(VAR_ROUTERFLAGS);
+ }
+ }
+ else {
+ /* No router case */
+ variable_set2(VAR_ROUTER_ENABLE, "NO", 1);
+ variable_unset(VAR_ROUTERFLAGS);
+ variable_unset(VAR_ROUTER);
+ }
+ }
+ else {
+ variable_set2(VAR_ROUTER_ENABLE, "NO", 1);
+ variable_unset(VAR_ROUTERFLAGS);
+ variable_unset(VAR_ROUTER);
+ }
+ return ret;
+}
+
+/* Shared between us and index_initialize() */
+extern PkgNode Top, Plist;
+
+int
+configPackages(dialogMenuItem *self)
+{
+ int i, restoreflag = 0;
+ PkgNodePtr tmp;
+
+ /* Did we get an INDEX? */
+ i = index_initialize("packages/INDEX");
+ if (DITEM_STATUS(i) == DITEM_FAILURE)
+ return i;
+
+ while (1) {
+ int ret, pos, scroll;
+ int current, low, high;
+
+ /* Bring up the packages menu */
+ pos = scroll = 0;
+ index_menu(&Top, &Top, &Plist, &pos, &scroll);
+
+ if (Plist.kids && Plist.kids->name) {
+ /* Now show the packing list menu */
+ pos = scroll = 0;
+ ret = index_menu(&Plist, &Plist, NULL, &pos, &scroll);
+ if (ret & DITEM_LEAVE_MENU)
+ break;
+ else if (DITEM_STATUS(ret) != DITEM_FAILURE) {
+ dialog_clear();
+ restoreflag = 1;
+ if (have_volumes) {
+ low = low_volume;
+ high = high_volume;
+ } else
+ low = high = 0;
+ for (current = low; current <= high; current++)
+ for (tmp = Plist.kids; tmp && tmp->name; tmp = tmp->next)
+ (void)index_extract(mediaDevice, &Top, tmp, FALSE, current);
+ break;
+ }
+ }
+ else {
+ msgConfirm("No packages were selected for extraction.");
+ break;
+ }
+ }
+ tmp = Plist.kids;
+ while (tmp) {
+ PkgNodePtr tmp2 = tmp->next;
+
+ safe_free(tmp);
+ tmp = tmp2;
+ }
+ index_init(NULL, &Plist);
+ return DITEM_SUCCESS | (restoreflag ? DITEM_RESTORE : 0);
+}
+
+/* Load pcnfsd package */
+int
+configPCNFSD(dialogMenuItem *self)
+{
+ int ret;
+
+ ret = package_add("pcnfsd");
+ if (DITEM_STATUS(ret) == DITEM_SUCCESS) {
+ variable_set2(VAR_PCNFSD, "YES", 0);
+ variable_set2("mountd_flags", "-n", 1);
+ }
+ return ret;
+}
+
+int
+configInetd(dialogMenuItem *self)
+{
+ char cmd[256];
+
+ WINDOW *w = savescr();
+
+ if (msgYesNo("The Internet Super Server (inetd) allows a number of simple Internet\n"
+ "services to be enabled, including finger, ftp, and telnetd. Enabling\n"
+ "these services may increase risk of security problems by increasing\n"
+ "the exposure of your system.\n\n"
+ "With this in mind, do you wish to enable inetd?\n")) {
+ variable_set2("inetd_enable", "NO", 1);
+ } else {
+ /* If inetd is enabled, we'll need an inetd.conf */
+ variable_set2("inetd_enable", "YES", 1);
+ if (!msgYesNo("inetd(8) relies on its configuration file, /etc/inetd.conf, to determine\n"
+ "which of its Internet services will be available. The default FreeBSD\n"
+ "inetd.conf(5) leaves all services disabled by default, so they must be\n"
+ "specifically enabled in the configuration file before they will\n"
+ "function, even once inetd(8) is enabled. Note that services for\n"
+ "IPv6 must be separately enabled from IPv4 services.\n\n"
+ "Select [Yes] now to invoke an editor on /etc/inetd.conf, or [No] to\n"
+ "use the current settings.\n")) {
+ sprintf(cmd, "%s /etc/inetd.conf", variable_get(VAR_EDITOR));
+ dialog_clear();
+ systemExecute(cmd);
+ }
+ }
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+int
+configNFSServer(dialogMenuItem *self)
+{
+ char cmd[256];
+ int retval = 0;
+
+ /* If we're an NFS server, we need an exports file */
+ if (!file_readable("/etc/exports")) {
+ WINDOW *w = savescr();
+
+ if (file_readable("/etc/exports.disabled"))
+ vsystem("mv /etc/exports.disabled /etc/exports");
+ else {
+ dialog_clear_norefresh();
+ msgConfirm("Operating as an NFS server means that you must first configure\n"
+ "an /etc/exports file to indicate which hosts are allowed certain\n"
+ "kinds of access to your local file systems.\n"
+ "Press [ENTER] now to invoke an editor on /etc/exports\n");
+ vsystem("echo '#The following examples export /usr to 3 machines named after ducks,' > /etc/exports");
+ vsystem("echo '#/usr/src and /usr/obj read-only to machines named after trouble makers,' >> /etc/exports");
+ vsystem("echo '#/home and all directories under it to machines named after dead rock stars' >> /etc/exports");
+ vsystem("echo '#and, /a to a network of privileged machines allowed to write on it as root.' >> /etc/exports");
+ vsystem("echo '#/usr huey louie dewie' >> /etc/exports");
+ vsystem("echo '#/usr/src /usr/obj -ro calvin hobbes' >> /etc/exports");
+ vsystem("echo '#/home -alldirs janice jimmy frank' >> /etc/exports");
+ vsystem("echo '#/a -maproot=0 -network 10.0.1.0 -mask 255.255.248.0' >> /etc/exports");
+ vsystem("echo '#' >> /etc/exports");
+ vsystem("echo '# You should replace these lines with your actual exported filesystems.' >> /etc/exports");
+ vsystem("echo \"# Note that BSD's export syntax is 'host-centric' vs. Sun's 'FS-centric' one.\" >> /etc/exports");
+ vsystem("echo >> /etc/exports");
+ sprintf(cmd, "%s /etc/exports", variable_get(VAR_EDITOR));
+ dialog_clear();
+ systemExecute(cmd);
+ }
+ variable_set2(VAR_NFS_SERVER, "YES", 1);
+ retval = configRpcBind(NULL);
+ restorescr(w);
+ }
+ else if (variable_get(VAR_NFS_SERVER)) { /* We want to turn it off again? */
+ vsystem("mv -f /etc/exports /etc/exports.disabled");
+ variable_unset(VAR_NFS_SERVER);
+ }
+ return DITEM_SUCCESS | retval;
+}
+
+/*
+ * Extend the standard dmenuToggleVariable() method to also check and set
+ * the rpcbind variable if needed.
+ */
+int
+configRpcBind(dialogMenuItem *self)
+{
+ char *tmp, *tmp2;
+ int retval = 0;
+ int doupdate = 1;
+
+ if (self != NULL) {
+ retval = dmenuToggleVariable(self);
+ tmp = strdup(self->data);
+ if ((tmp2 = index(tmp, '=')) != NULL)
+ *tmp2 = '\0';
+ if (strcmp(variable_get(tmp), "YES") != 0)
+ doupdate = 0;
+ free(tmp);
+ }
+
+ if (doupdate && strcmp(variable_get(VAR_RPCBIND_ENABLE), "YES") != 0) {
+ variable_set2(VAR_RPCBIND_ENABLE, "YES", 1);
+ retval |= DITEM_REDRAW;
+ }
+
+ return retval;
+}
+
+int
+configEtcTtys(dialogMenuItem *self)
+{
+ char cmd[256];
+
+ WINDOW *w = savescr();
+
+ /* Simply prompt for confirmation, then edit away. */
+ if (msgYesNo("Configuration of system TTYs requires editing the /etc/ttys file.\n"
+ "Typical configuration activities might include enabling getty(8)\n"
+ "on the first serial port to allow login via serial console after\n"
+ "reboot, or to enable xdm. The default ttys file enables normal\n"
+ "virtual consoles, and most sites will not need to perform manual\n"
+ "configuration.\n\n"
+ "To load /etc/ttys in the editor, select [Yes], otherwise, [No].")) {
+ } else {
+ configTtys();
+ sprintf(cmd, "%s /etc/ttys", variable_get(VAR_EDITOR));
+ dialog_clear();
+ systemExecute(cmd);
+ }
+
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+#ifdef __i386__
+int
+checkLoaderACPI(void)
+{
+ char val[4];
+
+ if (kenv(KENV_GET, "loader.acpi_disabled_by_user", &val[0], 4) <= 0) {
+ return (0);
+ }
+
+ if (strtol(&val[0], NULL, 10) <= 0) {
+ return (0);
+ }
+
+ return (1);
+}
+
+int
+configLoaderACPI(int disable)
+{
+ FILE *ldconf;
+
+ ldconf = fopen("/boot/loader.conf", "a");
+ if (ldconf == NULL) {
+ msgConfirm("Unable to open /boot/loader.conf. Please consult the\n"
+ "FreeBSD Handbook for instructions on disabling ACPI");
+ return DITEM_FAILURE;
+ }
+
+ fprintf(ldconf, "# --- Generated by sysinstall ---\n");
+ fprintf(ldconf, "hint.acpi.0.disabled=%d\n", disable);
+ fclose(ldconf);
+
+ return DITEM_SUCCESS;
+}
+#endif
+
+int
+configMTAPostfix(dialogMenuItem *self)
+{
+ int ret;
+ FILE *perconf;
+
+ if(setenv("POSTFIX_DEFAULT_MTA", "YES", 1) != 0)
+ msgError("Error setting the enviroment variable POSTFIX_DEFAULT_MTA: %s (%u)",
+ strerror(errno), errno);
+
+ ret = package_add("postfix-2.4");
+ unsetenv("POSTFIX_DEFAULT_MTA");
+
+ if(DITEM_STATUS(ret) == DITEM_FAILURE) {
+ msgConfirm("An error occurred while adding the postfix package\n"
+ "Please change installation media and try again.");
+ return ret;
+ }
+
+ variable_set2(VAR_SENDMAIL_ENABLE, "YES", 1);
+ variable_set2("sendmail_flags", "-bd", 1);
+ variable_set2("sendmail_outbound_enable", "NO", 1);
+ variable_set2("sendmail_submit_enable", "NO", 1);
+ variable_set2("sendmail_msp_queue_enable", "NO", 1);
+
+ perconf = fopen("/etc/periodic.conf", "a");
+ if (perconf == NULL) {
+ msgConfirm("Unable to open /etc/periodic.conf.\n"
+ "The daily cleanup scripts might generate errors when\n"
+ "trying to run some sendmail only cleanup scripts.\n"
+ "Please consult the documentation for the postfix port on how to\n"
+ "fix this.");
+
+ /* Not really a serious problem, so we return success */
+ return DITEM_SUCCESS;
+ }
+
+ fprintf(perconf, "# --- Generated by sysinstall ---\n");
+ fprintf(perconf, "daily_clean_hoststat_enable=\"NO\"\n");
+ fprintf(perconf, "daily_status_mail_rejects_enable=\"NO\"\n");
+ fprintf(perconf, "daily_status_include_submit_mailq=\"NO\"\n");
+ fprintf(perconf, "daily_submit_queuerun=\"NO\"\n");
+ fclose(perconf);
+
+ msgConfirm("Postfix is now installed and enabled as the default MTA.\n"
+ "Please check that the configuration works as expected.\n"
+ "See the Postfix documentation for more information.\n"
+ "The documentation can be found in /usr/local/share/doc/postfix/\n"
+ "or on the Postfix website at http://www.postfix.org/.");
+
+ return DITEM_SUCCESS;
+}
+
+int
+configMTAExim(dialogMenuItem *self)
+{
+ int ret;
+ FILE *perconf, *mailerconf, *newsyslogconf;
+
+ ret = package_add("exim");
+
+ if(DITEM_STATUS(ret) == DITEM_FAILURE) {
+ msgConfirm("An error occurred while adding the exim package\n"
+ "Please change installation media and try again.");
+ return ret;
+ }
+
+ variable_set2(VAR_SENDMAIL_ENABLE, "NONE", 1);
+ variable_set2("exim_enable", "YES", 1);
+
+ /* Update periodic.conf */
+ perconf = fopen("/etc/periodic.conf", "a");
+ if (perconf == NULL) {
+ /* Not really a serious problem, so we do not abort */
+ msgConfirm("Unable to open /etc/periodic.conf.\n"
+ "The daily cleanup scripts might generate errors when\n"
+ "trying to run some sendmail only cleanup scripts.\n"
+ "Please consult the documentation for the exim port on how to\n"
+ "fix this.");
+ } else {
+ fprintf(perconf, "# --- Generated by sysinstall ---\n");
+ fprintf(perconf, "daily_clean_hoststat_enable=\"NO\"\n");
+ fprintf(perconf, "daily_status_include_submit_mailq=\"NO\"\n");
+ fprintf(perconf, "daily_status_mail_rejects_enable=\"NO\"\n");
+ fprintf(perconf, "daily_submit_queuerun=\"NO\"\n");
+ fclose(perconf);
+ }
+
+ /* Update mailer.conf */
+ vsystem("mv -f /etc/mail/mailer.conf /etc/mail/mailer.conf.old");
+ mailerconf = fopen("/etc/mail/mailer.conf", "w");
+ if (mailerconf == NULL) {
+ /* Not really a serious problem, so we do not abort */
+ msgConfirm("Unable to open /etc/mailer.conf.\n"
+ "Some programs which use the sendmail wrappers may not work.\n"
+ "Please consult the documentation for the exim port on how\n"
+ "to correct this.");
+ } else {
+ fprintf(mailerconf, "# --- Generated by sysinstall ---\n");
+ fprintf(mailerconf, "# Execute exim instead of sendmail\n");
+ fprintf(mailerconf, "#\n");
+ fprintf(mailerconf, "sendmail /usr/local/sbin/exim\n");
+ fprintf(mailerconf, "send-mail /usr/local/sbin/exim\n");
+ fprintf(mailerconf, "mailq /usr/local/sbin/exim\n");
+ fprintf(mailerconf, "newaliases /usr/local/sbin/exim\n");
+ fprintf(mailerconf, "hoststat /usr/bin/true\n");
+ fprintf(mailerconf, "purgestat /usr/bin/true\n");
+ fclose(mailerconf);
+ }
+
+ /* Make newsyslog rotate exim logfiles */
+ newsyslogconf = fopen("/etc/newsyslog.conf", "a");
+ if (newsyslogconf == NULL) {
+ /* Not really a serious problem, so we do not abort */
+ msgConfirm("Unable to open /etc/newsyslog.conf.\n"
+ "The exim logfiles will not be rotated.\n"
+ "Please consult the documentation for the exim port on how to\n"
+ "rotate the logfiles.");
+ } else {
+ fprintf(newsyslogconf, "# --- Generated by sysinstall ---\n");
+ fprintf(newsyslogconf, "/var/log/exim/mainlog mailnull:mail 640 7 * @T00 ZN\n");
+ fprintf(newsyslogconf, "/var/log/exim/rejectlog mailnull:mail 640 7 * @T00 ZN\n");
+ fclose(newsyslogconf);
+ }
+
+ msgConfirm("Exim is now installed and enabled as the default MTA.\n"
+ "Please check that the configuration works as expected.\n"
+ "See the Exim documentation for more information.\n"
+ "The documentation can be found in /usr/local/share/doc/exim/\n"
+ "or on the Exim website at http://www.exim.org/.");
+
+ return DITEM_SUCCESS;
+}
diff --git a/usr.sbin/sysinstall/devices.c b/usr.sbin/sysinstall/devices.c
new file mode 100644
index 0000000..c4438e1
--- /dev/null
+++ b/usr.sbin/sysinstall/devices.c
@@ -0,0 +1,569 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sysinstall.h"
+#include <sys/fcntl.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#include <netinet/in_var.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include <libdisk.h>
+
+static Device *Devices[DEV_MAX];
+static int numDevs;
+
+#define DEVICE_ENTRY(type, name, descr, max) { type, name, descr, max }
+
+#define CDROM(name, descr, max) \
+ DEVICE_ENTRY(DEVICE_TYPE_CDROM, name, descr, max)
+#define DISK(name, descr, max) \
+ DEVICE_ENTRY(DEVICE_TYPE_DISK, name, descr, max)
+#define FLOPPY(name, descr, max) \
+ DEVICE_ENTRY(DEVICE_TYPE_FLOPPY, name, descr, max)
+#define NETWORK(name, descr) \
+ DEVICE_ENTRY(DEVICE_TYPE_NETWORK, name, descr, 0)
+#define SERIAL(name, descr, max) \
+ DEVICE_ENTRY(DEVICE_TYPE_NETWORK, name, descr, max)
+
+static struct _devname {
+ DeviceType type;
+ char *name;
+ char *description;
+ int max;
+} device_names[] = {
+ CDROM("cd%d", "SCSI CDROM drive", 4),
+ CDROM("mcd%d", "Mitsumi (old model) CDROM drive", 4),
+ CDROM("scd%d", "Sony CDROM drive - CDU31/33A type", 4),
+ CDROM("acd%d", "ATAPI/IDE CDROM", 4),
+ DISK("da%d", "SCSI disk device", 16),
+ DISK("ad%d", "ATA/IDE disk device", 16),
+ DISK("ar%d", "ATA/IDE RAID device", 16),
+ DISK("afd%d", "ATAPI/IDE floppy device", 4),
+ DISK("mlxd%d", "Mylex RAID disk", 4),
+ DISK("amrd%d", "AMI MegaRAID drive", 4),
+ DISK("idad%d", "Compaq RAID array", 4),
+ DISK("twed%d", "3ware ATA RAID array", 4),
+ DISK("aacd%d", "Adaptec FSA RAID array", 4),
+ DISK("ipsd%d", "IBM ServeRAID RAID array", 4),
+ DISK("mfid%d", "LSI MegaRAID SAS array", 4),
+ FLOPPY("fd%d", "floppy drive unit A", 4),
+ SERIAL("cuad%d", "%s on device %s (COM%d)", 16),
+ NETWORK("ae", "Attansic/Atheros L2 Fast Ethernet"),
+ NETWORK("age", "Attansic/Atheros L1 Gigabit Ethernet"),
+ NETWORK("ale", "Atheros AR8121/AR8113/AR8114 PCIe Ethernet"),
+ NETWORK("an", "Aironet 4500/4800 802.11 wireless adapter"),
+ NETWORK("ath", "Atheros IEEE 802.11 wireless adapter"),
+ NETWORK("aue", "ADMtek USB Ethernet adapter"),
+ NETWORK("axe", "ASIX Electronics USB Ethernet adapter"),
+ NETWORK("bce", "Broadcom NetXtreme II Gigabit Ethernet card"),
+ NETWORK("bfe", "Broadcom BCM440x PCI Ethernet card"),
+ NETWORK("bge", "Broadcom BCM570x PCI Gigabit Ethernet card"),
+ NETWORK("cue", "CATC USB Ethernet adapter"),
+ NETWORK("cxgb", "Chelsio T3 10Gb Ethernet card"),
+ NETWORK("fpa", "DEC DEFPA PCI FDDI card"),
+ NETWORK("sr", "SDL T1/E1 sync serial PCI card"),
+ NETWORK("cc3i", "SDL HSSI sync serial PCI card"),
+ NETWORK("en", "Efficient Networks ATM PCI card"),
+ NETWORK("dc", "DEC/Intel 21143 (and clones) PCI Fast Ethernet card"),
+ NETWORK("de", "DEC DE435 PCI NIC or other DC21040-AA based card"),
+ NETWORK("fxp", "Intel EtherExpress Pro/100B PCI Fast Ethernet card"),
+ NETWORK("ed", "Novell NE1000/2000; 3C503; NE2000-compatible PCMCIA"),
+ NETWORK("ep", "3Com 3C509 Ethernet card/3C589 PCMCIA"),
+ NETWORK("em", "Intel(R) PRO/1000 Ethernet card"),
+ NETWORK("et", "Agere ET1310 based PCI Express Gigabit Ethernet card"),
+ NETWORK("ex", "Intel EtherExpress Pro/10 Ethernet card"),
+ NETWORK("fe", "Fujitsu MB86960A/MB86965A Ethernet card"),
+ NETWORK("gem", "Apple GMAC or Sun ERI/GEM Ethernet adapter"),
+ NETWORK("hme", "Sun HME (Happy Meal Ethernet) Ethernet adapter"),
+ NETWORK("ie", "AT&T StarLAN 10 and EN100; 3Com 3C507; NI5210"),
+ NETWORK("igb", "Intel(R) PRO/1000 PCI Express Gigabit Ethernet card"),
+ NETWORK("ipw", "Intel PRO/Wireless 2100 IEEE 802.11 adapter"),
+ NETWORK("iwi", "Intel PRO/Wireless 2200BG/2225BG/2915ABG adapter"),
+ NETWORK("iwn", "Intel Wireless WiFi Link 4965AGN IEEE 802.11n adapter"),
+ NETWORK("ixgb", "Intel(R) PRO/10Gb Ethernet card"),
+ NETWORK("ixgbe", "Intel(R) PRO/10Gb Ethernet card"),
+ NETWORK("jme", "JMicron JMC250 Gigabit/JMC260 Fast Ethernet"),
+ NETWORK("kue", "Kawasaki LSI USB Ethernet adapter"),
+ NETWORK("le", "AMD Am7900 LANCE or Am79C9xx PCnet Ethernet adapter"),
+ NETWORK("lge", "Level 1 LXT1001 Gigabit Ethernet card"),
+ NETWORK("malo", "Marvell Libertas 88W8335 802.11 wireless adapter"),
+ NETWORK("msk", "Marvell/SysKonnect Yukon II Gigabit Ethernet"),
+ NETWORK("mxge", "Myricom Myri10GE 10Gb Ethernet card"),
+ NETWORK("nfe", "NVIDIA nForce MCP Ethernet"),
+ NETWORK("nge", "NatSemi PCI Gigabit Ethernet card"),
+ NETWORK("nve", "NVIDIA nForce MCP Ethernet"),
+ NETWORK("nxge", "Neterion Xframe 10GbE Server/Storage adapter"),
+ NETWORK("pcn", "AMD Am79c79x PCI Ethernet card"),
+ NETWORK("ral", "Ralink Technology IEEE 802.11 wireless adapter"),
+ NETWORK("ray", "Raytheon Raylink 802.11 wireless adapter"),
+ NETWORK("re", "RealTek 8139C+/8169/8169S/8110S PCI Ethernet card"),
+ NETWORK("rl", "RealTek 8129/8139 PCI Ethernet card"),
+ NETWORK("rue", "RealTek USB Ethernet card"),
+ NETWORK("rum", "Ralink Technology USB IEEE 802.11 wireless adapter"),
+ NETWORK("sf", "Adaptec AIC-6915 PCI Ethernet card"),
+ NETWORK("sis", "SiS 900/SiS 7016 PCI Ethernet card"),
+#ifdef PC98
+ NETWORK("snc", "SONIC Ethernet card"),
+#endif
+ NETWORK("sn", "SMC/Megahertz Ethernet card"),
+ NETWORK("ste", "Sundance ST201 PCI Ethernet card"),
+ NETWORK("stge", "Sundance/Tamarack TC9021 Gigabit Ethernet"),
+ NETWORK("sk", "SysKonnect PCI Gigabit Ethernet card"),
+ NETWORK("tx", "SMC 9432TX Ethernet card"),
+ NETWORK("txp", "3Com 3cR990 Ethernet card"),
+ NETWORK("ti", "Alteon Networks PCI Gigabit Ethernet card"),
+ NETWORK("tl", "Texas Instruments ThunderLAN PCI Ethernet card"),
+ NETWORK("upgt", "Conexant/Intersil PrismGT USB wireless adapter"),
+ NETWORK("ural", "Ralink Technology RT2500USB 802.11 wireless adapter"),
+ NETWORK("urtw", "Realtek 8187L USB wireless adapter"),
+ NETWORK("vge", "VIA VT612x PCI Gigabit Ethernet card"),
+ NETWORK("vr", "VIA VT3043/VT86C100A Rhine PCI Ethernet card"),
+ NETWORK("vlan", "IEEE 802.1Q VLAN network interface"),
+ NETWORK("vx", "3COM 3c590 / 3c595 Ethernet card"),
+ NETWORK("wb", "Winbond W89C840F PCI Ethernet card"),
+ NETWORK("wi", "Lucent WaveLAN/IEEE 802.11 wireless adapter"),
+ NETWORK("wpi", "Intel 3945ABG IEEE 802.11 wireless adapter"),
+ NETWORK("xe", "Xircom/Intel EtherExpress Pro100/16 Ethernet card"),
+ NETWORK("xl", "3COM 3c90x / 3c90xB PCI Ethernet card"),
+ NETWORK("zyd", "ZyDAS ZD1211/ZD1211B USB 802.11 wireless adapter"),
+ NETWORK("fwe", "FireWire Ethernet emulation"),
+ NETWORK("fwip", "IP over FireWire"),
+ NETWORK("plip", "Parallel Port IP (PLIP) peer connection"),
+ NETWORK("lo", "Loop-back (local) network interface"),
+ NETWORK("disc", "Software discard network interface"),
+ { 0, NULL, NULL, 0 }
+};
+
+Device *
+new_device(char *name)
+{
+ Device *dev;
+
+ dev = safe_malloc(sizeof(Device));
+ bzero(dev, sizeof(Device));
+ if (name)
+ SAFE_STRCPY(dev->name, name);
+ return dev;
+}
+
+/* Stubs for unimplemented strategy routines */
+Boolean
+dummyInit(Device *dev)
+{
+ return TRUE;
+}
+
+FILE *
+dummyGet(Device *dev, char *dist, Boolean probe)
+{
+ return NULL;
+}
+
+void
+dummyShutdown(Device *dev)
+{
+ return;
+}
+
+static int
+deviceTry(struct _devname dev, char *try, int i)
+{
+ int fd;
+ char unit[80];
+
+ snprintf(unit, sizeof unit, dev.name, i);
+ snprintf(try, FILENAME_MAX, "/dev/%s", unit);
+ if (isDebug())
+ msgDebug("deviceTry: attempting to open %s\n", try);
+ fd = open(try, O_RDONLY);
+ if (fd >= 0) {
+ if (isDebug())
+ msgDebug("deviceTry: open of %s succeeded on first try.\n", try);
+ } else {
+ if (isDebug())
+ msgDebug("deviceTry: open of %s failed.\n", try);
+ }
+ return fd;
+}
+
+/* Register a new device in the devices array */
+Device *
+deviceRegister(char *name, char *desc, char *devname, DeviceType type, Boolean enabled,
+ Boolean (*init)(Device *), FILE * (*get)(Device *, char *, Boolean),
+ void (*shutdown)(Device *), void *private)
+{
+ Device *newdev = NULL;
+
+ if (numDevs == DEV_MAX)
+ msgFatal("Too many devices found!");
+ else {
+ newdev = new_device(name);
+ newdev->description = desc;
+ newdev->devname = devname;
+ newdev->type = type;
+ newdev->enabled = enabled;
+ newdev->init = init ? init : dummyInit;
+ newdev->get = get ? get : dummyGet;
+ newdev->shutdown = shutdown ? shutdown : dummyShutdown;
+ newdev->private = private;
+ Devices[numDevs] = newdev;
+ Devices[++numDevs] = NULL;
+ }
+ return newdev;
+}
+
+/* Reset the registered device chain */
+void
+deviceReset(void)
+{
+ int i;
+
+ for (i = 0; i < numDevs; i++) {
+ DEVICE_SHUTDOWN(Devices[i]);
+
+ /* XXX this potentially leaks Devices[i]->private if it's being
+ * used to point to something dynamic, but you're not supposed
+ * to call this routine at such times that some open instance
+ * has its private ptr pointing somewhere anyway. XXX
+ */
+ free(Devices[i]);
+ }
+ Devices[numDevs = 0] = NULL;
+}
+
+/* Get all device information for devices we have attached */
+void
+deviceGetAll(void)
+{
+ int i, j, fd, s;
+ struct ifconf ifc;
+ struct ifreq *ifptr, *end;
+ int ifflags;
+ char buffer[INTERFACE_MAX * sizeof(struct ifreq)];
+ char **names;
+
+ msgNotify("Probing devices, please wait (this can take a while)...");
+ /* First go for the network interfaces. Stolen shamelessly from ifconfig! */
+ ifc.ifc_len = sizeof(buffer);
+ ifc.ifc_buf = buffer;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0)
+ goto skipif; /* Jump over network iface probing */
+
+ if (ioctl(s, SIOCGIFCONF, (char *) &ifc) < 0)
+ goto skipif; /* Jump over network iface probing */
+
+ close(s);
+ ifflags = ifc.ifc_req->ifr_flags;
+ end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+ for (ifptr = ifc.ifc_req; ifptr < end; ifptr++) {
+ char *descr;
+
+ /* If it's not a link entry, forget it */
+ if (ifptr->ifr_ifru.ifru_addr.sa_family != AF_LINK)
+ goto loopend;
+
+ /* Eliminate network devices that don't make sense */
+ if (!strncmp(ifptr->ifr_name, "lo", 2))
+ goto loopend;
+
+ /* If we have a slip device, don't register it */
+ if (!strncmp(ifptr->ifr_name, "sl", 2)) {
+ goto loopend;
+ }
+ /* And the same for ppp */
+ if (!strncmp(ifptr->ifr_name, "tun", 3) || !strncmp(ifptr->ifr_name, "ppp", 3)) {
+ goto loopend;
+ }
+ /* Try and find its description */
+ for (i = 0, descr = NULL; device_names[i].name; i++) {
+ int len = strlen(device_names[i].name);
+
+ if (!ifptr->ifr_name || !ifptr->ifr_name[0])
+ continue;
+ else if (!strncmp(ifptr->ifr_name, device_names[i].name, len)) {
+ descr = device_names[i].description;
+ break;
+ }
+ }
+ if (!descr)
+ descr = "<unknown network interface type>";
+
+ deviceRegister(ifptr->ifr_name, descr, strdup(ifptr->ifr_name), DEVICE_TYPE_NETWORK, TRUE,
+ mediaInitNetwork, NULL, mediaShutdownNetwork, NULL);
+ if (isDebug())
+ msgDebug("Found a network device named %s\n", ifptr->ifr_name);
+ close(s);
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ continue;
+
+loopend:
+ if (ifptr->ifr_addr.sa_len) /* I'm not sure why this is here - it's inherited */
+ ifptr = (struct ifreq *)((caddr_t)ifptr + ifptr->ifr_addr.sa_len - sizeof(struct sockaddr));
+ close(s);
+ }
+
+skipif:
+ /* Next, try to find all the types of devices one might need
+ * during the second stage of the installation.
+ */
+ for (i = 0; device_names[i].name; i++) {
+ for (j = 0; j < device_names[i].max; j++) {
+ char try[FILENAME_MAX];
+
+ switch(device_names[i].type) {
+ case DEVICE_TYPE_CDROM:
+ fd = deviceTry(device_names[i], try, j);
+ if (fd >= 0 || errno == EBUSY) { /* EBUSY if already mounted */
+ char n[BUFSIZ];
+
+ if (fd >= 0) close(fd);
+ snprintf(n, sizeof n, device_names[i].name, j);
+ deviceRegister(strdup(n), device_names[i].description, strdup(try),
+ DEVICE_TYPE_CDROM, TRUE, mediaInitCDROM, mediaGetCDROM,
+ mediaShutdownCDROM, NULL);
+ if (isDebug())
+ msgDebug("Found a CDROM device for %s\n", try);
+ }
+ break;
+
+ case DEVICE_TYPE_DISK:
+ /* nothing to do */
+ break;
+
+ case DEVICE_TYPE_FLOPPY:
+ fd = deviceTry(device_names[i], try, j);
+ if (fd >= 0) {
+ char n[BUFSIZ];
+
+ close(fd);
+ snprintf(n, sizeof n, device_names[i].name, j);
+ deviceRegister(strdup(n), device_names[i].description, strdup(try),
+ DEVICE_TYPE_FLOPPY, TRUE, mediaInitFloppy, mediaGetFloppy,
+ mediaShutdownFloppy, NULL);
+ if (isDebug())
+ msgDebug("Found a floppy device for %s\n", try);
+ }
+ break;
+
+ case DEVICE_TYPE_NETWORK:
+ fd = deviceTry(device_names[i], try, j);
+ /* The only network devices that you can open this way are serial ones */
+ if (fd >= 0) {
+ char *newdesc, *cp;
+
+ close(fd);
+ cp = device_names[i].description;
+ /* Serial devices get a slip and ppp device each, if supported */
+ newdesc = safe_malloc(strlen(cp) + 40);
+ sprintf(newdesc, cp, "SLIP interface", try, j + 1);
+ deviceRegister("sl0", newdesc, strdup(try), DEVICE_TYPE_NETWORK, TRUE, mediaInitNetwork,
+ NULL, mediaShutdownNetwork, NULL);
+ msgDebug("Add mapping for %s to sl0\n", try);
+ newdesc = safe_malloc(strlen(cp) + 50);
+ sprintf(newdesc, cp, "PPP interface", try, j + 1);
+ deviceRegister("ppp0", newdesc, strdup(try), DEVICE_TYPE_NETWORK, TRUE, mediaInitNetwork,
+ NULL, mediaShutdownNetwork, NULL);
+ if (isDebug())
+ msgDebug("Add mapping for %s to ppp0\n", try);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ /* Finally, go get the disks and look for DOS partitions to register */
+ if ((names = Disk_Names()) != NULL) {
+ int i;
+
+ for (i = 0; names[i]; i++) {
+ Chunk *c1;
+ Disk *d;
+
+ /* Ignore memory disks */
+ if (!strncmp(names[i], "md", 2))
+ continue;
+
+ /*
+ * XXX
+ * Due to unknown reasons, Disk_Names() returns SCSI CDROM as a
+ * valid disk. This is main reason why sysinstall presents SCSI
+ * CDROM to available disks in Fdisk/Label menu. In addition,
+ * adding a blank SCSI CDROM to the menu generates floating point
+ * exception in sparc64. Disk_Names() just extracts sysctl
+ * "kern.disks". Why GEOM treats SCSI CDROM as a disk is beyond
+ * me and that should be investigated.
+ * For temporary workaround, ignore SCSI CDROM device.
+ */
+ if (!strncmp(names[i], "cd", 2))
+ continue;
+
+ d = Open_Disk(names[i]);
+ if (!d) {
+ msgDebug("Unable to open disk %s\n", names[i]);
+ continue;
+ }
+
+ deviceRegister(names[i], names[i], d->name, DEVICE_TYPE_DISK, FALSE,
+ dummyInit, dummyGet, dummyShutdown, d);
+ if (isDebug())
+ msgDebug("Found a disk device named %s\n", names[i]);
+
+ /* Look for existing DOS partitions to register as "DOS media devices" */
+ for (c1 = d->chunks->part; c1; c1 = c1->next) {
+ if (c1->type == fat || c1->type == efi || c1->type == extended) {
+ Device *dev;
+ char devname[80];
+
+ /* Got one! */
+ snprintf(devname, sizeof devname, "/dev/%s", c1->name);
+ dev = deviceRegister(c1->name, c1->name, strdup(devname), DEVICE_TYPE_DOS, TRUE,
+ mediaInitDOS, mediaGetDOS, mediaShutdownDOS, NULL);
+ dev->private = c1;
+ if (isDebug())
+ msgDebug("Found a DOS partition %s on drive %s\n", c1->name, d->name);
+ }
+ }
+ }
+ free(names);
+ }
+ dialog_clear_norefresh();
+}
+
+/* Rescan all devices, after closing previous set - convenience function */
+void
+deviceRescan(void)
+{
+ deviceReset();
+ deviceGetAll();
+}
+
+/*
+ * Find all devices that match the criteria, allowing "wildcarding" as well
+ * by allowing NULL or ANY values to match all. The array returned is static
+ * and may be used until the next invocation of deviceFind().
+ */
+Device **
+deviceFind(char *name, DeviceType class)
+{
+ static Device *found[DEV_MAX];
+ int i, j;
+
+ j = 0;
+ for (i = 0; i < numDevs; i++) {
+ if ((!name || !strcmp(Devices[i]->name, name))
+ && (class == DEVICE_TYPE_ANY || class == Devices[i]->type))
+ found[j++] = Devices[i];
+ }
+ found[j] = NULL;
+ return j ? found : NULL;
+}
+
+Device **
+deviceFindDescr(char *name, char *desc, DeviceType class)
+{
+ static Device *found[DEV_MAX];
+ int i, j;
+
+ j = 0;
+ for (i = 0; i < numDevs; i++) {
+ if ((!name || !strcmp(Devices[i]->name, name)) &&
+ (!desc || !strcmp(Devices[i]->description, desc)) &&
+ (class == DEVICE_TYPE_ANY || class == Devices[i]->type))
+ found[j++] = Devices[i];
+ }
+ found[j] = NULL;
+ return j ? found : NULL;
+}
+
+int
+deviceCount(Device **devs)
+{
+ int i;
+
+ if (!devs)
+ return 0;
+ for (i = 0; devs[i]; i++);
+ return i;
+}
+
+/*
+ * Create a menu listing all the devices of a certain type in the system.
+ * The passed-in menu is expected to be a "prototype" from which the new
+ * menu is cloned.
+ */
+DMenu *
+deviceCreateMenu(DMenu *menu, DeviceType type, int (*hook)(dialogMenuItem *d), int (*check)(dialogMenuItem *d))
+{
+ Device **devs;
+ int numdevs;
+ DMenu *tmp = NULL;
+ int i, j;
+
+ devs = deviceFind(NULL, type);
+ numdevs = deviceCount(devs);
+ if (!numdevs)
+ return NULL;
+ tmp = (DMenu *)safe_malloc(sizeof(DMenu) + (sizeof(dialogMenuItem) * (numdevs + 1)));
+ bcopy(menu, tmp, sizeof(DMenu));
+ for (i = 0; devs[i]; i++) {
+ tmp->items[i].prompt = devs[i]->name;
+ for (j = 0; j < numDevs; j++) {
+ if (devs[i] == Devices[j]) {
+ tmp->items[i].title = Devices[j]->description;
+ break;
+ }
+ }
+ if (j == numDevs)
+ tmp->items[i].title = "<unknown device type>";
+ tmp->items[i].fire = hook;
+ tmp->items[i].checked = check;
+ }
+ tmp->items[i].title = NULL;
+ return tmp;
+}
diff --git a/usr.sbin/sysinstall/dhcp.c b/usr.sbin/sysinstall/dhcp.c
new file mode 100644
index 0000000..fa35580
--- /dev/null
+++ b/usr.sbin/sysinstall/dhcp.c
@@ -0,0 +1,158 @@
+/*
+ * $FreeBSD$
+ *
+ * Copyright (c) 1999
+ * C. Stone. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 C. STONE ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL C STONE OR HIS BODILY PARASITES BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE BY THE VOICES IN YOUR HEAD BEFOREHAND.
+ *
+ */
+
+#include "sysinstall.h"
+
+#include <ctype.h>
+
+int
+dhcpParseLeases(char *file, char *hostname, char *domain, char *nameserver,
+ char *ipaddr, char *gateway, char *netmask)
+{
+ char tempbuf[1024];
+ char optbuf[1024], *optname = NULL;
+ char *tptr;
+ int endedflag = 0;
+ int leaseflag = 0;
+ FILE *fp;
+ enum { P_NOSTMT, P_NOSTMT1, P_STMT, P_STMTLINE } state;
+
+ if ((fp = fopen(file, "r")) == NULL) {
+ msgDebug("error opening file %s: %s\n", file, strerror(errno));
+ return -1;
+ }
+
+ state = P_NOSTMT;
+ while (fscanf(fp, "%1023s", tempbuf) > 0) {
+ switch (state) {
+ case P_NOSTMT:
+ state = P_NOSTMT1;
+ if (!strncasecmp(tempbuf, "lease", 5)) {
+ if (!leaseflag)
+ leaseflag = 1;
+ else {
+ fclose(fp);
+ return 0;
+ }
+ }
+ break;
+
+ case P_NOSTMT1:
+ if (tempbuf[0] != '{') {
+ msgWarn("dhcpParseLeases: '{' expected");
+ fclose(fp);
+ return -1;
+ }
+ state = P_STMT;
+ break;
+
+ case P_STMT:
+ if (!strncasecmp("option", tempbuf, 6))
+ continue;
+ if (tempbuf[0] == '}') {
+ state = P_NOSTMT;
+ leaseflag = 0;
+ continue;
+ }
+ if (!leaseflag)
+ break;
+ if (tempbuf[0] == ';') { /* play it safe */
+ state = P_STMT;
+ continue;
+ }
+ if ((tptr = (char *)strchr(tempbuf, ';')) && (*(tptr + 1) == 0)) {
+ *tptr = '\0';
+ endedflag = 1;
+ }
+ if (!isalnum(tempbuf[0])) {
+ msgWarn("dhcpParseLeases: bad option");
+ fclose(fp);
+ return -1;
+ }
+ if (optname)
+ free(optname);
+ optname = strdup(tempbuf);
+ if (endedflag) {
+ state = P_STMT;
+ endedflag = 0;
+ continue;
+ }
+ state = P_STMTLINE;
+ break;
+
+ case P_STMTLINE:
+ if (tempbuf[0] == ';') {
+ state = P_STMT;
+ continue;
+ }
+ if ((tptr = (char *)strchr(tempbuf, ';')) && (*(tptr + 1) == 0)) {
+ *tptr = '\0';
+ endedflag = 1;
+ }
+ if (tempbuf[0] == '"') {
+ if (sscanf(tempbuf, "\"%[^\" ]\"", optbuf) < 1) {
+ msgWarn("dhcpParseLeases: bad option value");
+ fclose(fp);
+ return -1;
+ }
+ }
+ else
+ strcpy(optbuf, tempbuf);
+
+ if (!strcasecmp("host-name", optname)) {
+ strcpy(hostname, optbuf);
+ } else if (!strcasecmp("domain-name", optname)) {
+ strcpy(domain, optbuf);
+ } else if (!strcasecmp("fixed-address", optname)) {
+ strcpy(ipaddr, optbuf);
+ } else if (!strcasecmp("routers", optname)) {
+ if((tptr = (char *)strchr(optbuf, ',')))
+ *tptr = '\0';
+ strcpy(gateway, optbuf);
+ } else if (!strcasecmp("subnet-mask", optname)) {
+ strcpy(netmask, optbuf);
+ } else if (!strcasecmp("domain-name-servers", optname)) {
+ /* <jkh> ...one value per property */
+ if((tptr = (char *)strchr(optbuf, ',')))
+ *tptr = '\0';
+ strcpy(nameserver, optbuf);
+ }
+ if (endedflag) {
+ state = P_STMT;
+ endedflag = 0;
+ continue;
+ }
+ break;
+ }
+ }
+ fclose(fp);
+ return 0;
+}
diff --git a/usr.sbin/sysinstall/disks.c b/usr.sbin/sysinstall/disks.c
new file mode 100644
index 0000000..ba7bbff
--- /dev/null
+++ b/usr.sbin/sysinstall/disks.c
@@ -0,0 +1,1047 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sysinstall.h"
+#include <ctype.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <libdisk.h>
+#include <sys/stat.h>
+#include <sys/disklabel.h>
+
+#ifdef WITH_SLICES
+enum size_units_t { UNIT_BLOCKS, UNIT_KILO, UNIT_MEG, UNIT_GIG, UNIT_SIZE };
+
+#ifdef PC98
+#define SUBTYPE_FREEBSD 50324
+#define SUBTYPE_FAT 37218
+#else
+#define SUBTYPE_FREEBSD 165
+#define SUBTYPE_FAT 6
+#endif
+#define SUBTYPE_EFI 239
+
+#ifdef PC98
+#define OTHER_SLICE_VALUES \
+ "Other popular values are 37218 for a\n" \
+ "DOS FAT partition.\n\n"
+#else
+#define OTHER_SLICE_VALUES \
+ "Other popular values are 6 for a\n" \
+ "DOS FAT partition, 131 for a Linux ext2fs partition, or\n" \
+ "130 for a Linux swap partition.\n\n"
+#endif
+#define NON_FREEBSD_NOTE \
+ "Note: If you choose a non-FreeBSD partition type, it will not\n" \
+ "be formatted or otherwise prepared, it will simply reserve space\n" \
+ "for you to use another tool, such as DOS format, to later format\n" \
+ "and actually use the partition."
+
+/* Where we start displaying chunk information on the screen */
+#define CHUNK_START_ROW 5
+
+/* Where we keep track of MBR chunks */
+#define CHUNK_INFO_ENTRIES 16
+static struct chunk *chunk_info[CHUNK_INFO_ENTRIES];
+static int current_chunk;
+
+static void diskPartitionNonInteractive(Device *dev);
+static u_char * bootalloc(char *name, size_t *size);
+
+static void
+record_chunks(Disk *d)
+{
+ struct chunk *c1 = NULL;
+ int i = 0;
+ daddr_t last_free = 0;
+
+ if (!d->chunks)
+ msgFatal("No chunk list found for %s!", d->name);
+
+ for (c1 = d->chunks->part; c1; c1 = c1->next) {
+ if (c1->type == unused && c1->size > last_free) {
+ last_free = c1->size;
+ current_chunk = i;
+ }
+ chunk_info[i++] = c1;
+ }
+ chunk_info[i] = NULL;
+ if (current_chunk >= i)
+ current_chunk = i - 1;
+}
+
+static daddr_t Total;
+
+static void
+check_geometry(Disk *d)
+{
+ int sg;
+
+#ifdef PC98
+ if (d->bios_cyl >= 65536 || d->bios_hd > 256 || d->bios_sect >= 256)
+#else
+ if (d->bios_cyl > 65536 || d->bios_hd > 256 || d->bios_sect >= 64)
+#endif
+ {
+ dialog_clear_norefresh();
+ sg = msgYesNo("WARNING: It is safe to use a geometry of %lu/%lu/%lu for %s on\n"
+ "computers with modern BIOS versions. If this disk is to be used\n"
+ "on an old machine it is recommended that it does not have more\n"
+ "than 65535 cylinders, more than 255 heads, or more than\n"
+#ifdef PC98
+ "255"
+#else
+ "63"
+#endif
+ " sectors per track.\n"
+ "\n"
+ "Would you like to keep using the current geometry?\n",
+ d->bios_cyl, d->bios_hd, d->bios_sect, d->name);
+ if (sg == 1) {
+ Sanitize_Bios_Geom(d);
+ msgConfirm("A geometry of %lu/%lu/%lu was calculated for %s.\n"
+ "\n"
+ "If you are not sure about this, please consult the Hardware Guide\n"
+ "in the Documentation submenu or use the (G)eometry command to\n"
+ "change it. Remember: you need to enter whatever your BIOS thinks\n"
+ "the geometry is! For IDE, it's what you were told in the BIOS\n"
+ "setup. For SCSI, it's the translation mode your controller is\n"
+ "using. Do NOT use a ``physical geometry''.\n",
+ d->bios_cyl, d->bios_hd, d->bios_sect, d->name);
+ }
+ }
+}
+
+static void
+print_chunks(Disk *d, int u)
+{
+ int row;
+ int i;
+ daddr_t sz;
+ char *szstr;
+
+ szstr = (u == UNIT_GIG ? "GB" : (u == UNIT_MEG ? "MB" :
+ (u == UNIT_KILO ? "KB" : "ST")));
+
+ Total = 0;
+ for (i = 0; chunk_info[i]; i++)
+ Total += chunk_info[i]->size;
+ attrset(A_NORMAL);
+ mvaddstr(0, 0, "Disk name:\t");
+ clrtobot();
+ attrset(A_REVERSE); addstr(d->name); attrset(A_NORMAL);
+ attrset(A_REVERSE); mvaddstr(0, 55, "FDISK Partition Editor"); attrset(A_NORMAL);
+ mvprintw(1, 0,
+ "DISK Geometry:\t%lu cyls/%lu heads/%lu sectors = %jd sectors (%jdMB)",
+ d->bios_cyl, d->bios_hd, d->bios_sect,
+ (intmax_t)d->bios_cyl * d->bios_hd * d->bios_sect,
+ (intmax_t)d->bios_cyl * d->bios_hd * d->bios_sect / (1024/512) / 1024);
+ mvprintw(3, 0, "%6s %10s(%s) %10s %8s %6s %10s %8s %8s",
+ "Offset", "Size", szstr, "End", "Name", "PType", "Desc",
+ "Subtype", "Flags");
+ for (i = 0, row = CHUNK_START_ROW; chunk_info[i]; i++, row++) {
+ switch(u) {
+ default: /* fall thru */
+ case UNIT_BLOCKS:
+ sz = chunk_info[i]->size;
+ break;
+ case UNIT_KILO:
+ sz = chunk_info[i]->size / (1024/512);
+ break;
+ case UNIT_MEG:
+ sz = chunk_info[i]->size / (1024/512) / 1024;
+ break;
+ case UNIT_GIG:
+ sz = chunk_info[i]->size / (1024/512) / 1024 / 1024;
+ break;
+ }
+ if (i == current_chunk)
+ attrset(ATTR_SELECTED);
+ mvprintw(row, 0, "%10jd %10jd %10jd %8s %6d %10s %8d\t%-6s",
+ (intmax_t)chunk_info[i]->offset, (intmax_t)sz,
+ (intmax_t)chunk_info[i]->end, chunk_info[i]->name,
+ chunk_info[i]->type,
+ slice_type_name(chunk_info[i]->type, chunk_info[i]->subtype),
+ chunk_info[i]->subtype, ShowChunkFlags(chunk_info[i]));
+ if (i == current_chunk)
+ attrset(A_NORMAL);
+ }
+}
+
+static void
+print_command_summary(void)
+{
+ mvprintw(14, 0, "The following commands are supported (in upper or lower case):");
+ mvprintw(16, 0, "A = Use Entire Disk G = set Drive Geometry C = Create Slice F = `DD' mode");
+ mvprintw(17, 0, "D = Delete Slice Z = Toggle Size Units S = Set Bootable | = Wizard m.");
+ mvprintw(18, 0, "T = Change Type U = Undo All Changes Q = Finish");
+ if (!RunningAsInit)
+ mvprintw(18, 47, "W = Write Changes");
+ mvprintw(21, 0, "Use F1 or ? to get more help, arrow keys to select.");
+ move(0, 0);
+}
+
+#ifdef PC98
+static void
+getBootMgr(char *dname, u_char **bootipl, size_t *bootipl_size,
+ u_char **bootmenu, size_t *bootmenu_size)
+{
+ static u_char *boot0;
+ static size_t boot0_size;
+ static u_char *boot05;
+ static size_t boot05_size;
+
+ char str[80];
+ char *cp;
+ int i = 0;
+
+ cp = variable_get(VAR_BOOTMGR);
+ if (!cp) {
+ /* Figure out what kind of IPL the user wants */
+ sprintf(str, "Install Boot Manager for drive %s?", dname);
+ MenuIPLType.title = str;
+ i = dmenuOpenSimple(&MenuIPLType, FALSE);
+ } else {
+ if (!strncmp(cp, "boot", 4))
+ BootMgr = 0;
+ else
+ BootMgr = 1;
+ }
+ if (cp || i) {
+ switch (BootMgr) {
+ case 0:
+ if (!boot0) boot0 = bootalloc("boot0", &boot0_size);
+ *bootipl = boot0;
+ *bootipl_size = boot0_size;
+ if (!boot05) boot05 = bootalloc("boot0.5", &boot05_size);
+ *bootmenu = boot05;
+ *bootmenu_size = boot05_size;
+ return;
+ case 1:
+ default:
+ break;
+ }
+ }
+ *bootipl = NULL;
+ *bootipl_size = 0;
+ *bootmenu = NULL;
+ *bootmenu_size = 0;
+}
+#else
+static void
+getBootMgr(char *dname, u_char **bootCode, size_t *bootCodeSize)
+{
+#if defined(__i386__) || defined(__amd64__) /* only meaningful on x86 */
+ static u_char *mbr, *boot0;
+ static size_t mbr_size, boot0_size;
+ char str[80];
+ char *cp;
+ int i = 0;
+
+ cp = variable_get(VAR_BOOTMGR);
+ if (!cp) {
+ /* Figure out what kind of MBR the user wants */
+ sprintf(str, "Install Boot Manager for drive %s?", dname);
+ MenuMBRType.title = str;
+ i = dmenuOpenSimple(&MenuMBRType, FALSE);
+ }
+ else {
+ if (!strncmp(cp, "boot", 4))
+ BootMgr = 0;
+ else if (!strcmp(cp, "standard"))
+ BootMgr = 1;
+ else
+ BootMgr = 2;
+ }
+ if (cp || i) {
+ switch (BootMgr) {
+ case 0:
+ if (!boot0) boot0 = bootalloc("boot0", &boot0_size);
+ *bootCode = boot0;
+ *bootCodeSize = boot0_size;
+ return;
+ case 1:
+ if (!mbr) mbr = bootalloc("mbr", &mbr_size);
+ *bootCode = mbr;
+ *bootCodeSize = mbr_size;
+ return;
+ case 2:
+ default:
+ break;
+ }
+ }
+#endif
+ *bootCode = NULL;
+ *bootCodeSize = 0;
+}
+#endif
+#endif /* WITH_SLICES */
+
+int
+diskGetSelectCount(Device ***devs)
+{
+ int i, cnt, enabled;
+ char *cp;
+ Device **dp;
+
+ cp = variable_get(VAR_DISK);
+ dp = *devs = deviceFind(cp, DEVICE_TYPE_DISK);
+ cnt = deviceCount(dp);
+ if (!cnt)
+ return -1;
+ for (i = 0, enabled = 0; i < cnt; i++) {
+ if (dp[i]->enabled)
+ ++enabled;
+ }
+ return enabled;
+}
+
+#ifdef WITH_SLICES
+void
+diskPartition(Device *dev)
+{
+ char *cp, *p;
+ int rv, key = 0;
+ int i;
+ Boolean chunking;
+ char *msg = NULL;
+#ifdef PC98
+ u_char *bootipl;
+ size_t bootipl_size;
+ u_char *bootmenu;
+ size_t bootmenu_size;
+#else
+ u_char *mbrContents;
+ size_t mbrSize;
+#endif
+ WINDOW *w = savescr();
+ Disk *d = (Disk *)dev->private;
+ int size_unit;
+
+ size_unit = UNIT_BLOCKS;
+ chunking = TRUE;
+ keypad(stdscr, TRUE);
+
+ /* Flush both the dialog and curses library views of the screen
+ since we don't always know who called us */
+ dialog_clear_norefresh(), clear();
+ current_chunk = 0;
+
+ /* Set up the chunk array */
+ record_chunks(d);
+
+ /* Give the user a chance to sanitize the disk geometry, if necessary */
+ check_geometry(d);
+
+ while (chunking) {
+ char *val, geometry[80];
+
+ /* Now print our overall state */
+ if (d)
+ print_chunks(d, size_unit);
+ print_command_summary();
+ if (msg) {
+ attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
+ beep();
+ msg = NULL;
+ }
+ else {
+ move(23, 0);
+ clrtoeol();
+ }
+
+ /* Get command character */
+ key = getch();
+ switch (toupper(key)) {
+ case '\014': /* ^L (redraw) */
+ clear();
+ msg = NULL;
+ break;
+
+ case '\020': /* ^P */
+ case KEY_UP:
+ case '-':
+ if (current_chunk != 0)
+ --current_chunk;
+ break;
+
+ case '\016': /* ^N */
+ case KEY_DOWN:
+ case '+':
+ case '\r':
+ case '\n':
+ if (chunk_info[current_chunk + 1])
+ ++current_chunk;
+ break;
+
+ case KEY_HOME:
+ current_chunk = 0;
+ break;
+
+ case KEY_END:
+ while (chunk_info[current_chunk + 1])
+ ++current_chunk;
+ break;
+
+ case KEY_F(1):
+ case '?':
+ systemDisplayHelp("slice");
+ clear();
+ break;
+
+ case 'A':
+ case 'F': /* Undocumented magic Dangerously Dedicated mode */
+#if !defined(__i386__) && !defined(__amd64__)
+ rv = 1;
+#else /* The rest is only relevant on x86 */
+ cp = variable_get(VAR_DEDICATE_DISK);
+ if (cp && !strcasecmp(cp, "always"))
+ rv = 1;
+ else if (toupper(key) == 'A')
+ rv = 0;
+ else {
+ rv = msgYesNo("Do you want to do this with a true partition entry\n"
+ "so as to remain cooperative with any future possible\n"
+ "operating systems on the drive(s)?\n"
+ "(See also the section about ``dangerously dedicated''\n"
+ "disks in the FreeBSD FAQ.)");
+ if (rv == -1)
+ rv = 0;
+ }
+#endif
+ All_FreeBSD(d, rv);
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ record_chunks(d);
+ clear();
+ break;
+
+ case 'C':
+ if (chunk_info[current_chunk]->type != unused)
+ msg = "Slice in use, delete it first or move to an unused one.";
+ else {
+ char *val, tmp[20], name[16], *cp;
+ daddr_t size;
+ int subtype;
+ chunk_e partitiontype;
+#ifdef PC98
+ snprintf(name, sizeof (name), "%s", "FreeBSD");
+ val = msgGetInput(name,
+ "Please specify the name for new FreeBSD slice.");
+ if (val)
+ strncpy(name, val, sizeof (name));
+#else
+ name[0] = '\0';
+#endif
+ snprintf(tmp, 20, "%jd", (intmax_t)chunk_info[current_chunk]->size);
+ val = msgGetInput(tmp, "Please specify the size for new FreeBSD slice in blocks\n"
+ "or append a trailing `M' for megabytes (e.g. 20M).");
+ if (val && (size = strtoimax(val, &cp, 0)) > 0) {
+ if (*cp && toupper(*cp) == 'M')
+ size *= ONE_MEG;
+ else if (*cp && toupper(*cp) == 'G')
+ size *= ONE_GIG;
+ sprintf(tmp, "%d", SUBTYPE_FREEBSD);
+ val = msgGetInput(tmp, "Enter type of partition to create:\n\n"
+ "Pressing Enter will choose the default, a native FreeBSD\n"
+ "slice (type %u). "
+ OTHER_SLICE_VALUES
+ NON_FREEBSD_NOTE, SUBTYPE_FREEBSD);
+ if (val && (subtype = strtol(val, NULL, 0)) > 0) {
+ if (subtype == SUBTYPE_FREEBSD)
+ partitiontype = freebsd;
+ else if (subtype == SUBTYPE_FAT)
+ partitiontype = fat;
+ else if (subtype == SUBTYPE_EFI)
+ partitiontype = efi;
+ else
+#ifdef PC98
+ partitiontype = pc98;
+#else
+ partitiontype = mbr;
+#endif
+ Create_Chunk(d, chunk_info[current_chunk]->offset, size, partitiontype, subtype,
+ (chunk_info[current_chunk]->flags & CHUNK_ALIGN), name);
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ record_chunks(d);
+ }
+ }
+ clear();
+ }
+ break;
+
+ case KEY_DC:
+ case 'D':
+ if (chunk_info[current_chunk]->type == unused)
+ msg = "Slice is already unused!";
+ else {
+ Delete_Chunk(d, chunk_info[current_chunk]);
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ record_chunks(d);
+ }
+ break;
+
+ case 'T':
+ if (chunk_info[current_chunk]->type == unused)
+ msg = "Slice is currently unused (use create instead)";
+ else {
+ char *val, tmp[20];
+ int subtype;
+ chunk_e partitiontype;
+
+ sprintf(tmp, "%d", chunk_info[current_chunk]->subtype);
+ val = msgGetInput(tmp, "New partition type:\n\n"
+ "Pressing Enter will use the current type. To choose a native\n"
+ "FreeBSD slice enter %u. "
+ OTHER_SLICE_VALUES
+ NON_FREEBSD_NOTE, SUBTYPE_FREEBSD);
+ if (val && (subtype = strtol(val, NULL, 0)) > 0) {
+ if (subtype == SUBTYPE_FREEBSD)
+ partitiontype = freebsd;
+ else if (subtype == SUBTYPE_FAT)
+ partitiontype = fat;
+ else if (subtype == SUBTYPE_EFI)
+ partitiontype = efi;
+ else
+#ifdef PC98
+ partitiontype = pc98;
+#else
+ partitiontype = mbr;
+#endif
+ chunk_info[current_chunk]->type = partitiontype;
+ chunk_info[current_chunk]->subtype = subtype;
+ }
+ }
+ break;
+
+ case 'G':
+ snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect);
+ val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n"
+ "Don't forget to use the two slash (/) separator characters!\n"
+ "It's not possible to parse the field without them.");
+ if (val) {
+ long nc, nh, ns;
+ nc = strtol(val, &val, 0);
+ nh = strtol(val + 1, &val, 0);
+ ns = strtol(val + 1, 0, 0);
+ Set_Bios_Geom(d, nc, nh, ns);
+ }
+ clear();
+ break;
+
+ case 'S':
+ /* Clear active states so we won't have two */
+ for (i = 0; (chunk_info[i] != NULL) && (i < CHUNK_INFO_ENTRIES); i++)
+ chunk_info[i]->flags &= !CHUNK_ACTIVE;
+
+ /* Set Bootable */
+ chunk_info[current_chunk]->flags |= CHUNK_ACTIVE;
+ break;
+
+ case 'U':
+ if (!variable_cmp(DISK_LABELLED, "written")) {
+ msgConfirm("You've already written this information out - you\n"
+ "can't undo it.");
+ }
+ else if (!msgNoYes("Are you SURE you want to Undo everything?")) {
+ char cp[BUFSIZ];
+
+ sstrncpy(cp, d->name, sizeof cp);
+ Free_Disk(dev->private);
+ d = Open_Disk(cp);
+ if (!d)
+ msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", cp);
+ dev->private = d;
+ variable_unset(DISK_PARTITIONED);
+ variable_unset(DISK_LABELLED);
+ if (d)
+ record_chunks(d);
+ }
+ clear();
+ break;
+
+ case 'W':
+ if (!msgNoYes("WARNING: This should only be used when modifying an EXISTING\n"
+ "installation. If you are installing FreeBSD for the first time\n"
+ "then you should simply type Q when you're finished here and your\n"
+ "changes will be committed in one batch automatically at the end of\n"
+ "these questions. If you're adding a disk, you should NOT write\n"
+ "from this screen, you should do it from the label editor.\n\n"
+ "Are you absolutely sure you want to do this now?")) {
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+
+#ifdef PC98
+ /*
+ * Don't trash the IPL if the first (and therefore only) chunk
+ * is marked for a truly dedicated disk (i.e., the disklabel
+ * starts at sector 0), even in cases where the user has
+ * requested a FreeBSD Boot Manager -- both would be fatal in
+ * this case.
+ */
+ /*
+ * Don't offer to update the IPL on this disk if the first
+ * "real" chunk looks like a FreeBSD "all disk" partition,
+ * or the disk is entirely FreeBSD.
+ */
+ if ((d->chunks->part->type != freebsd) ||
+ (d->chunks->part->offset > 1))
+ getBootMgr(d->name, &bootipl, &bootipl_size,
+ &bootmenu, &bootmenu_size);
+ else {
+ bootipl = NULL;
+ bootipl_size = 0;
+ bootmenu = NULL;
+ bootmenu_size = 0;
+ }
+ Set_Boot_Mgr(d, bootipl, bootipl_size, bootmenu, bootmenu_size);
+#else
+ /*
+ * Don't trash the MBR if the first (and therefore only) chunk
+ * is marked for a truly dedicated disk (i.e., the disklabel
+ * starts at sector 0), even in cases where the user has
+ * requested booteasy or a "standard" MBR -- both would be
+ * fatal in this case.
+ */
+ /*
+ * Don't offer to update the MBR on this disk if the first
+ * "real" chunk looks like a FreeBSD "all disk" partition,
+ * or the disk is entirely FreeBSD.
+ */
+ if ((d->chunks->part->type != freebsd) ||
+ (d->chunks->part->offset > 1))
+ getBootMgr(d->name, &mbrContents, &mbrSize);
+ else {
+ mbrContents = NULL;
+ mbrSize = 0;
+ }
+ Set_Boot_Mgr(d, mbrContents, mbrSize);
+#endif
+
+ if (DITEM_STATUS(diskPartitionWrite(NULL)) != DITEM_SUCCESS)
+ msgConfirm("Disk partition write returned an error status!");
+ else
+ msgConfirm("Wrote FDISK partition information out successfully.");
+ }
+ clear();
+ break;
+
+ case '|':
+ if (!msgNoYes("Are you SURE you want to go into Wizard mode?\n"
+ "No seat belts whatsoever are provided!")) {
+ clear();
+ refresh();
+ slice_wizard(d);
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ record_chunks(d);
+ }
+ else
+ msg = "Wise choice!";
+ clear();
+ break;
+
+ case '\033': /* ESC */
+ case 'Q':
+ chunking = FALSE;
+#ifdef PC98
+ /*
+ * Don't trash the IPL if the first (and therefore only) chunk
+ * is marked for a truly dedicated disk (i.e., the disklabel
+ * starts at sector 0), even in cases where the user has requested
+ * a FreeBSD Boot Manager -- both would be fatal in this case.
+ */
+ /*
+ * Don't offer to update the IPL on this disk if the first "real"
+ * chunk looks like a FreeBSD "all disk" partition, or the disk is
+ * entirely FreeBSD.
+ */
+ if ((d->chunks->part->type != freebsd) ||
+ (d->chunks->part->offset > 1)) {
+ if (variable_cmp(DISK_PARTITIONED, "written")) {
+ getBootMgr(d->name, &bootipl, &bootipl_size,
+ &bootmenu, &bootmenu_size);
+ if (bootipl != NULL && bootmenu != NULL)
+ Set_Boot_Mgr(d, bootipl, bootipl_size,
+ bootmenu, bootmenu_size);
+ }
+ }
+#else
+ /*
+ * Don't trash the MBR if the first (and therefore only) chunk
+ * is marked for a truly dedicated disk (i.e., the disklabel
+ * starts at sector 0), even in cases where the user has requested
+ * booteasy or a "standard" MBR -- both would be fatal in this case.
+ */
+ /*
+ * Don't offer to update the MBR on this disk if the first "real"
+ * chunk looks like a FreeBSD "all disk" partition, or the disk is
+ * entirely FreeBSD.
+ */
+ if ((d->chunks->part->type != freebsd) ||
+ (d->chunks->part->offset > 1)) {
+ if (variable_cmp(DISK_PARTITIONED, "written")) {
+ getBootMgr(d->name, &mbrContents, &mbrSize);
+ if (mbrContents != NULL)
+ Set_Boot_Mgr(d, mbrContents, mbrSize);
+ }
+ }
+#endif
+ break;
+
+ case 'Z':
+ size_unit = (size_unit + 1) % UNIT_SIZE;
+ break;
+
+ default:
+ beep();
+ msg = "Type F1 or ? for help";
+ break;
+ }
+ }
+ p = CheckRules(d);
+ if (p) {
+ char buf[FILENAME_MAX];
+
+ use_helpline("Press F1 to read more about disk slices.");
+ use_helpfile(systemHelpFile("partition", buf));
+ if (!variable_get(VAR_NO_WARN))
+ dialog_mesgbox("Disk slicing warning:", p, -1, -1);
+ free(p);
+ }
+ restorescr(w);
+}
+#endif /* WITH_SLICES */
+
+static u_char *
+bootalloc(char *name, size_t *size)
+{
+ char buf[FILENAME_MAX];
+ struct stat sb;
+
+ snprintf(buf, sizeof buf, "/boot/%s", name);
+ if (stat(buf, &sb) != -1) {
+ int fd;
+
+ fd = open(buf, O_RDONLY);
+ if (fd != -1) {
+ u_char *cp;
+
+ cp = malloc(sb.st_size);
+ if (read(fd, cp, sb.st_size) != sb.st_size) {
+ free(cp);
+ close(fd);
+ msgDebug("bootalloc: couldn't read %ld bytes from %s\n", (long)sb.st_size, buf);
+ return NULL;
+ }
+ close(fd);
+ if (size != NULL)
+ *size = sb.st_size;
+ return cp;
+ }
+ msgDebug("bootalloc: couldn't open %s\n", buf);
+ }
+ else
+ msgDebug("bootalloc: can't stat %s\n", buf);
+ return NULL;
+}
+
+#ifdef WITH_SLICES
+static int
+partitionHook(dialogMenuItem *selected)
+{
+ Device **devs = NULL;
+
+ devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("Unable to find disk %s!", selected->prompt);
+ return DITEM_FAILURE;
+ }
+ /* Toggle enabled status? */
+ if (!devs[0]->enabled) {
+ devs[0]->enabled = TRUE;
+ diskPartition(devs[0]);
+ }
+ else
+ devs[0]->enabled = FALSE;
+ return DITEM_SUCCESS;
+}
+
+static int
+partitionCheck(dialogMenuItem *selected)
+{
+ Device **devs = NULL;
+
+ devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
+ if (!devs || devs[0]->enabled == FALSE)
+ return FALSE;
+ return TRUE;
+}
+
+int
+diskPartitionEditor(dialogMenuItem *self)
+{
+ DMenu *menu;
+ Device **devs;
+ int i, cnt, devcnt;
+
+ cnt = diskGetSelectCount(&devs);
+ devcnt = deviceCount(devs);
+ if (cnt == -1) {
+ msgConfirm("No disks found! Please verify that your disk controller is being\n"
+ "properly probed at boot time. See the Hardware Guide on the\n"
+ "Documentation menu for clues on diagnosing this type of problem.");
+ return DITEM_FAILURE;
+ }
+ else if (cnt) {
+ /* Some are already selected */
+ for (i = 0; i < devcnt; i++) {
+ if (devs[i]->enabled) {
+ if (variable_get(VAR_NONINTERACTIVE) &&
+ !variable_get(VAR_DISKINTERACTIVE))
+ diskPartitionNonInteractive(devs[i]);
+ else
+ diskPartition(devs[i]);
+ }
+ }
+ }
+ else {
+ /* No disks are selected, fall-back case now */
+ if (devcnt == 1) {
+ devs[0]->enabled = TRUE;
+ if (variable_get(VAR_NONINTERACTIVE) &&
+ !variable_get(VAR_DISKINTERACTIVE))
+ diskPartitionNonInteractive(devs[0]);
+ else
+ diskPartition(devs[0]);
+ return DITEM_SUCCESS;
+ }
+ else {
+ menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook, partitionCheck);
+ if (!menu) {
+ msgConfirm("No devices suitable for installation found!\n\n"
+ "Please verify that your disk controller (and attached drives)\n"
+ "were detected properly. This can be done by pressing the\n"
+ "[Scroll Lock] key and using the Arrow keys to move back to\n"
+ "the boot messages. Press [Scroll Lock] again to return.");
+ return DITEM_FAILURE;
+ }
+ else {
+ i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE;
+ free(menu);
+ }
+ return i;
+ }
+ }
+ return DITEM_SUCCESS;
+}
+#endif /* WITH_SLICES */
+
+int
+diskPartitionWrite(dialogMenuItem *self)
+{
+ Device **devs;
+ int i;
+
+ if (!variable_cmp(DISK_PARTITIONED, "written"))
+ return DITEM_SUCCESS;
+
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("Unable to find any disks to write to??");
+ return DITEM_FAILURE;
+ }
+ if (isDebug())
+ msgDebug("diskPartitionWrite: Examining %d devices\n", deviceCount(devs));
+ for (i = 0; devs[i]; i++) {
+ Disk *d = (Disk *)devs[i]->private;
+ static u_char *boot1;
+#if defined(__i386__) || defined(__amd64__)
+ static u_char *boot2;
+#endif
+
+ if (!devs[i]->enabled)
+ continue;
+
+#if defined(__i386__) || defined(__amd64__)
+ if (!boot1) boot1 = bootalloc("boot1", NULL);
+ if (!boot2) boot2 = bootalloc("boot2", NULL);
+ Set_Boot_Blocks(d, boot1, boot2);
+#elif !defined(__ia64__)
+ if (!boot1) boot1 = bootalloc("boot1", NULL);
+ Set_Boot_Blocks(d, boot1, NULL);
+#endif
+
+ msgNotify("Writing partition information to drive %s", d->name);
+ if (!Fake && Write_Disk(d)) {
+ if (RunningAsInit) {
+ msgConfirm("ERROR: Unable to write data to disk %s!", d->name);
+ } else {
+ msgConfirm("ERROR: Unable to write data to disk %s!\n\n"
+ "To edit the labels on a running system set\n"
+ "sysctl kern.geom.debugflags=16 and try again.", d->name);
+ }
+ return DITEM_FAILURE;
+ }
+ }
+ /* Now it's not "yes", but "written" */
+ variable_set2(DISK_PARTITIONED, "written", 0);
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+#ifdef WITH_SLICES
+/* Partition a disk based wholly on which variables are set */
+static void
+diskPartitionNonInteractive(Device *dev)
+{
+ char *cp;
+ int i, all_disk = 0;
+ daddr_t sz;
+#ifdef PC98
+ u_char *bootipl;
+ size_t bootipl_size;
+ u_char *bootmenu;
+ size_t bootmenu_size;
+#else
+ u_char *mbrContents;
+ size_t mbrSize;
+#endif
+ Disk *d = (Disk *)dev->private;
+
+ record_chunks(d);
+ cp = variable_get(VAR_GEOMETRY);
+ if (cp) {
+ if (!strcasecmp(cp, "sane")) {
+#ifdef PC98
+ if (d->bios_cyl >= 65536 || d->bios_hd > 256 || d->bios_sect >= 256)
+#else
+ if (d->bios_cyl > 65536 || d->bios_hd > 256 || d->bios_sect >= 64)
+#endif
+ {
+ msgDebug("Warning: A geometry of %lu/%lu/%lu for %s is incorrect.\n",
+ d->bios_cyl, d->bios_hd, d->bios_sect, d->name);
+ Sanitize_Bios_Geom(d);
+ msgDebug("Sanitized geometry for %s is %lu/%lu/%lu.\n",
+ d->name, d->bios_cyl, d->bios_hd, d->bios_sect);
+ }
+ } else {
+ msgDebug("Setting geometry from script to: %s\n", cp);
+ d->bios_cyl = strtol(cp, &cp, 0);
+ d->bios_hd = strtol(cp + 1, &cp, 0);
+ d->bios_sect = strtol(cp + 1, 0, 0);
+ }
+ }
+
+ cp = variable_get(VAR_PARTITION);
+ if (cp) {
+ if (!strcmp(cp, "free")) {
+ /* Do free disk space case */
+ for (i = 0; chunk_info[i]; i++) {
+ /* If a chunk is at least 10MB in size, use it. */
+ if (chunk_info[i]->type == unused && chunk_info[i]->size > (10 * ONE_MEG)) {
+ Create_Chunk(d, chunk_info[i]->offset, chunk_info[i]->size,
+ freebsd, 3,
+ (chunk_info[i]->flags & CHUNK_ALIGN),
+ "FreeBSD");
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ break;
+ }
+ }
+ if (!chunk_info[i]) {
+ msgConfirm("Unable to find any free space on this disk!");
+ return;
+ }
+ }
+ else if (!strcmp(cp, "all")) {
+ /* Do all disk space case */
+ msgDebug("Warning: Devoting all of disk %s to FreeBSD.\n", d->name);
+
+ All_FreeBSD(d, FALSE);
+ }
+ else if (!strcmp(cp, "exclusive")) {
+ /* Do really-all-the-disk-space case */
+ msgDebug("Warning: Devoting all of disk %s to FreeBSD.\n", d->name);
+
+ All_FreeBSD(d, all_disk = TRUE);
+ }
+ else if ((sz = strtoimax(cp, &cp, 0))) {
+ /* Look for sz bytes free */
+ if (*cp && toupper(*cp) == 'M')
+ sz *= ONE_MEG;
+ else if (*cp && toupper(*cp) == 'G')
+ sz *= ONE_GIG;
+ for (i = 0; chunk_info[i]; i++) {
+ /* If a chunk is at least sz MB, use it. */
+ if (chunk_info[i]->type == unused && chunk_info[i]->size >= sz) {
+ Create_Chunk(d, chunk_info[i]->offset, sz, freebsd, 3,
+ (chunk_info[i]->flags & CHUNK_ALIGN),
+ "FreeBSD");
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ break;
+ }
+ }
+ if (!chunk_info[i]) {
+ msgConfirm("Unable to find %jd free blocks on this disk!",
+ (intmax_t)sz);
+ return;
+ }
+ }
+ else if (!strcmp(cp, "existing")) {
+ /* Do existing FreeBSD case */
+ for (i = 0; chunk_info[i]; i++) {
+ if (chunk_info[i]->type == freebsd)
+ break;
+ }
+ if (!chunk_info[i]) {
+ msgConfirm("Unable to find any existing FreeBSD partitions on this disk!");
+ return;
+ }
+ }
+ else {
+ msgConfirm("`%s' is an invalid value for %s - is config file valid?", cp, VAR_PARTITION);
+ return;
+ }
+ if (!all_disk) {
+#ifdef PC98
+ getBootMgr(d->name, &bootipl, &bootipl_size,
+ &bootmenu, &bootmenu_size);
+ Set_Boot_Mgr(d, bootipl, bootipl_size, bootmenu, bootmenu_size);
+#else
+ getBootMgr(d->name, &mbrContents, &mbrSize);
+ Set_Boot_Mgr(d, mbrContents, mbrSize);
+#endif
+ }
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ }
+}
+#endif /* WITH_SLICES */
diff --git a/usr.sbin/sysinstall/dispatch.c b/usr.sbin/sysinstall/dispatch.c
new file mode 100644
index 0000000..79d9cf3
--- /dev/null
+++ b/usr.sbin/sysinstall/dispatch.c
@@ -0,0 +1,636 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sysinstall.h"
+#include <ctype.h>
+#include <errno.h>
+#include <sys/signal.h>
+#include <sys/fcntl.h>
+
+#include "list.h"
+
+static int dispatch_shutdown(dialogMenuItem *unused);
+static int dispatch_systemExecute(dialogMenuItem *unused);
+static int dispatch_msgConfirm(dialogMenuItem *unused);
+static int dispatch_mediaOpen(dialogMenuItem *unused);
+static int dispatch_mediaClose(dialogMenuItem *unused);
+static int cfgModuleFire(dialogMenuItem *self);
+
+static struct _word {
+ char *name;
+ int (*handler)(dialogMenuItem *self);
+} resWords[] = {
+ { "configAnonFTP", configAnonFTP },
+ { "configRouter", configRouter },
+ { "configInetd", configInetd },
+ { "configNFSServer", configNFSServer },
+ { "configNTP", configNTP },
+ { "configPCNFSD", configPCNFSD },
+ { "configPackages", configPackages },
+ { "configUsers", configUsers },
+#ifdef WITH_SLICES
+ { "diskPartitionEditor", diskPartitionEditor },
+#endif
+ { "diskPartitionWrite", diskPartitionWrite },
+ { "diskLabelEditor", diskLabelEditor },
+ { "diskLabelCommit", diskLabelCommit },
+ { "distReset", distReset },
+ { "distSetCustom", distSetCustom },
+ { "distUnsetCustom", distUnsetCustom },
+ { "distSetDeveloper", distSetDeveloper },
+ { "distSetKernDeveloper", distSetKernDeveloper },
+ { "distSetUser", distSetUser },
+ { "distSetMinimum", distSetMinimum },
+ { "distSetEverything", distSetEverything },
+ { "distSetSrc", distSetSrc },
+ { "distExtractAll", distExtractAll },
+ { "docBrowser", docBrowser },
+ { "docShowDocument", docShowDocument },
+ { "installCommit", installCommit },
+ { "installExpress", installExpress },
+ { "installStandard", installStandard },
+ { "installUpgrade", installUpgrade },
+ { "installFixupBase", installFixupBase },
+ { "installFixitHoloShell", installFixitHoloShell },
+ { "installFixitCDROM", installFixitCDROM },
+ { "installFixitFloppy", installFixitFloppy },
+ { "installFilesystems", installFilesystems },
+ { "installVarDefaults", installVarDefaults },
+ { "loadConfig", dispatch_load_file },
+ { "loadFloppyConfig", dispatch_load_floppy },
+ { "loadCDROMConfig", dispatch_load_cdrom },
+ { "mediaOpen", dispatch_mediaOpen },
+ { "mediaClose", dispatch_mediaClose },
+ { "mediaSetCDROM", mediaSetCDROM },
+ { "mediaSetFloppy", mediaSetFloppy },
+ { "mediaSetDOS", mediaSetDOS },
+ { "mediaSetFTP", mediaSetFTP },
+ { "mediaSetFTPActive", mediaSetFTPActive },
+ { "mediaSetFTPPassive", mediaSetFTPPassive },
+ { "mediaSetHTTP", mediaSetHTTP },
+ { "mediaSetUFS", mediaSetUFS },
+ { "mediaSetNFS", mediaSetNFS },
+ { "mediaSetFTPUserPass", mediaSetFTPUserPass },
+ { "mediaSetCPIOVerbosity", mediaSetCPIOVerbosity },
+ { "mediaGetType", mediaGetType },
+ { "msgConfirm", dispatch_msgConfirm },
+ { "optionsEditor", optionsEditor },
+ { "packageAdd", packageAdd },
+ { "addGroup", userAddGroup },
+ { "addUser", userAddUser },
+ { "shutdown", dispatch_shutdown },
+ { "system", dispatch_systemExecute },
+ { "dumpVariables", dump_variables },
+ { "tcpMenuSelect", tcpMenuSelect },
+ { NULL, NULL },
+};
+
+/*
+ * Helper routines for buffering data.
+ *
+ * We read an entire configuration into memory before executing it
+ * so that we are truely standalone and can do things like nuke the
+ * file or disk we're working on.
+ */
+
+typedef struct command_buffer_ {
+ qelement queue;
+ char * string;
+} command_buffer;
+
+static void
+dispatch_free_command(command_buffer *item)
+{
+ REMQUE(item);
+ free(item->string);
+ free(item);
+}
+
+static void
+dispatch_free_all(qelement *head)
+{
+ command_buffer *item;
+
+ while (!EMPTYQUE(*head)) {
+ item = (command_buffer *) head->q_forw;
+ dispatch_free_command(item);
+ }
+}
+
+static command_buffer *
+dispatch_add_command(qelement *head, char *string)
+{
+ command_buffer *new;
+
+ new = malloc(sizeof(command_buffer));
+
+ if (!new)
+ return NULL;
+
+ new->string = strdup(string);
+ INSQUEUE(new, head->q_back);
+
+ return new;
+}
+
+/*
+ * Command processing
+ */
+
+/* Just convenience */
+static int
+dispatch_shutdown(dialogMenuItem *unused)
+{
+ systemShutdown(0);
+ return DITEM_FAILURE;
+}
+
+static int
+dispatch_systemExecute(dialogMenuItem *unused)
+{
+ char *cmd = variable_get(VAR_COMMAND);
+
+ if (cmd)
+ return systemExecute(cmd) ? DITEM_FAILURE : DITEM_SUCCESS;
+ else
+ msgDebug("_systemExecute: No command passed in `command' variable.\n");
+ return DITEM_FAILURE;
+}
+
+static int
+dispatch_msgConfirm(dialogMenuItem *unused)
+{
+ char *msg = variable_get(VAR_COMMAND);
+
+ if (msg) {
+ msgConfirm("%s", msg);
+ return DITEM_SUCCESS;
+ }
+
+ msgDebug("_msgConfirm: No message passed in `command' variable.\n");
+ return DITEM_FAILURE;
+}
+
+static int
+dispatch_mediaOpen(dialogMenuItem *unused)
+{
+ return mediaOpen();
+}
+
+static int
+dispatch_mediaClose(dialogMenuItem *unused)
+{
+ mediaClose();
+ return DITEM_SUCCESS;
+}
+
+static int
+call_possible_resword(char *name, dialogMenuItem *value, int *status)
+{
+ int i, rval;
+
+ rval = 0;
+ for (i = 0; resWords[i].name; i++) {
+ if (!strcmp(name, resWords[i].name)) {
+ *status = resWords[i].handler(value);
+ rval = 1;
+ break;
+ }
+ }
+ return rval;
+}
+
+/* For a given string, call it or spit out an undefined command diagnostic */
+int
+dispatchCommand(char *str)
+{
+ int i;
+ char *cp;
+
+ if (!str || !*str) {
+ msgConfirm("Null or zero-length string passed to dispatchCommand");
+ return DITEM_FAILURE;
+ }
+
+ /* Fixup DOS abuse */
+ if ((cp = index(str, '\r')) != NULL)
+ *cp = '\0';
+
+ /* If it's got a `=' sign in there, assume it's a variable setting */
+ if (index(str, '=')) {
+ if (isDebug())
+ msgDebug("dispatch: setting variable `%s'\n", str);
+ variable_set(str, 0);
+ i = DITEM_SUCCESS;
+ }
+ else {
+ /* A command might be a pathname if it's encoded in argv[0], which
+ we also support */
+ if ((cp = rindex(str, '/')) != NULL)
+ str = cp + 1;
+ if (isDebug())
+ msgDebug("dispatch: calling resword `%s'\n", str);
+ if (!call_possible_resword(str, NULL, &i)) {
+ msgNotify("Warning: No such command ``%s''", str);
+ i = DITEM_FAILURE;
+ }
+ /*
+ * Allow a user to prefix a command with "noError" to cause
+ * us to ignore any errors for that one command.
+ */
+ if (i != DITEM_SUCCESS && variable_get(VAR_NO_ERROR))
+ i = DITEM_SUCCESS;
+ variable_unset(VAR_NO_ERROR);
+ }
+ return i;
+}
+
+
+/*
+ * File processing
+ */
+
+static qelement *
+dispatch_load_fp(FILE *fp)
+{
+ qelement *head;
+ char buf[BUFSIZ], *cp;
+
+ head = malloc(sizeof(qelement));
+
+ if (!head)
+ return NULL;
+
+ INITQUE(*head);
+
+ while (fgets(buf, sizeof buf, fp)) {
+ /* Fix up DOS abuse */
+ if ((cp = index(buf, '\r')) != NULL)
+ *cp = '\0';
+ /* If it's got a new line, trim it */
+ if ((cp = index(buf, '\n')) != NULL)
+ *cp = '\0';
+ if (*buf == '\0' || *buf == '#')
+ continue;
+
+ if (!dispatch_add_command(head, buf))
+ return NULL;
+ }
+
+ return head;
+}
+
+static int
+dispatch_execute(qelement *head)
+{
+ int result = DITEM_SUCCESS;
+ command_buffer *item;
+ char *old_interactive;
+
+ if (!head)
+ return result | DITEM_FAILURE;
+
+ old_interactive = variable_get(VAR_NONINTERACTIVE);
+ if (old_interactive)
+ old_interactive = strdup(old_interactive); /* save copy */
+
+ /* Hint to others that we're running from a script, should they care */
+ variable_set2(VAR_NONINTERACTIVE, "yes", 0);
+
+ while (!EMPTYQUE(*head)) {
+ item = (command_buffer *) head->q_forw;
+
+ if (DITEM_STATUS(dispatchCommand(item->string)) != DITEM_SUCCESS) {
+ msgConfirm("Command `%s' failed - rest of script aborted.\n",
+ item->string);
+ result |= DITEM_FAILURE;
+ break;
+ }
+ dispatch_free_command(item);
+ }
+
+ dispatch_free_all(head);
+
+ if (!old_interactive)
+ variable_unset(VAR_NONINTERACTIVE);
+ else {
+ variable_set2(VAR_NONINTERACTIVE, old_interactive, 0);
+ free(old_interactive);
+ }
+
+ return result;
+}
+
+int
+dispatch_load_file_int(int quiet)
+{
+ FILE *fp;
+ char *cp;
+ int i;
+ qelement *list;
+
+ static const char *names[] = {
+ "install.cfg",
+ "/stand/install.cfg",
+ "/tmp/install.cfg",
+ NULL
+ };
+
+ fp = NULL;
+ cp = variable_get(VAR_CONFIG_FILE);
+ if (!cp) {
+ for (i = 0; names[i]; i++)
+ if ((fp = fopen(names[i], "r")) != NULL)
+ break;
+ } else
+ fp = fopen(cp, "r");
+
+ if (!fp) {
+ if (!quiet)
+ msgConfirm("Unable to open %s: %s", cp, strerror(errno));
+ return DITEM_FAILURE;
+ }
+
+ list = dispatch_load_fp(fp);
+ fclose(fp);
+
+ return dispatch_execute(list);
+}
+
+int
+dispatch_load_file(dialogMenuItem *self)
+{
+ return dispatch_load_file_int(FALSE);
+}
+
+int
+dispatch_load_floppy(dialogMenuItem *self)
+{
+ int what = DITEM_SUCCESS;
+ extern char *distWanted;
+ char *cp;
+ FILE *fp;
+ qelement *list;
+
+ mediaClose();
+ cp = variable_get_value(VAR_INSTALL_CFG,
+ "Specify the name of a configuration file", 0);
+ if (!cp || !*cp) {
+ variable_unset(VAR_INSTALL_CFG);
+ what |= DITEM_FAILURE;
+ return what;
+ }
+
+ distWanted = cp;
+ /* Try to open the floppy drive */
+ if (DITEM_STATUS(mediaSetFloppy(NULL)) == DITEM_FAILURE) {
+ msgConfirm("Unable to set media device to floppy.");
+ what |= DITEM_FAILURE;
+ mediaClose();
+ return what;
+ }
+
+ if (!DEVICE_INIT(mediaDevice)) {
+ msgConfirm("Unable to mount floppy filesystem.");
+ what |= DITEM_FAILURE;
+ mediaClose();
+ return what;
+ }
+
+ fp = DEVICE_GET(mediaDevice, cp, TRUE);
+ if (fp) {
+ list = dispatch_load_fp(fp);
+ fclose(fp);
+ mediaClose();
+
+ what |= dispatch_execute(list);
+ }
+ else {
+ if (!variable_get(VAR_NO_ERROR))
+ msgConfirm("Configuration file '%s' not found.", cp);
+ variable_unset(VAR_INSTALL_CFG);
+ what |= DITEM_FAILURE;
+ mediaClose();
+ }
+ return what;
+}
+
+int
+dispatch_load_cdrom(dialogMenuItem *self)
+{
+ int what = DITEM_SUCCESS;
+ extern char *distWanted;
+ char *cp;
+ FILE *fp;
+ qelement *list;
+
+ mediaClose();
+ cp = variable_get_value(VAR_INSTALL_CFG,
+ "Specify the name of a configuration file\n"
+ "residing on the CDROM.", 0);
+ if (!cp || !*cp) {
+ variable_unset(VAR_INSTALL_CFG);
+ what |= DITEM_FAILURE;
+ return what;
+ }
+
+ distWanted = cp;
+ /* Try to open the floppy drive */
+ if (DITEM_STATUS(mediaSetCDROM(NULL)) == DITEM_FAILURE) {
+ msgConfirm("Unable to set media device to CDROM.");
+ what |= DITEM_FAILURE;
+ mediaClose();
+ return what;
+ }
+
+ if (!DEVICE_INIT(mediaDevice)) {
+ msgConfirm("Unable to CDROM filesystem.");
+ what |= DITEM_FAILURE;
+ mediaClose();
+ return what;
+ }
+
+ fp = DEVICE_GET(mediaDevice, cp, TRUE);
+ if (fp) {
+ list = dispatch_load_fp(fp);
+ fclose(fp);
+ mediaClose();
+
+ what |= dispatch_execute(list);
+ }
+ else {
+ if (!variable_get(VAR_NO_ERROR))
+ msgConfirm("Configuration file '%s' not found.", cp);
+ variable_unset(VAR_INSTALL_CFG);
+ what |= DITEM_FAILURE;
+ mediaClose();
+ }
+ return what;
+}
+
+/*
+ * Create a menu based on available disk devices
+ */
+int
+dispatch_load_menu(dialogMenuItem *self)
+{
+ DMenu *menu;
+ Device **devlist;
+ char *err;
+ int what, i, j, msize, count;
+ DeviceType dtypes[] = {DEVICE_TYPE_FLOPPY, DEVICE_TYPE_CDROM, DEVICE_TYPE_DOS, DEVICE_TYPE_UFS};
+
+ fprintf(stderr, "dispatch_load_menu called\n");
+
+ msize = sizeof(DMenu) + (sizeof(dialogMenuItem) * 2);
+ count = 0;
+ err = NULL;
+ what = DITEM_SUCCESS;
+
+ if ((menu = malloc(msize)) == NULL) {
+ err = "Failed to allocate memory for menu";
+ goto errout;
+ }
+
+ bcopy(&MenuConfig, menu, sizeof(DMenu));
+
+ bzero(&menu->items[count], sizeof(menu->items[0]));
+ menu->items[count].prompt = strdup("X Exit");
+ menu->items[count].title = strdup("Exit this menu (returning to previous)");
+ menu->items[count].fire = dmenuExit;
+ count++;
+
+ for (i = 0; i < sizeof(dtypes) / sizeof(dtypes[0]); i++) {
+ if ((devlist = deviceFind(NULL, dtypes[i])) == NULL) {
+ fprintf(stderr, "No devices found for type %d\n", dtypes[i]);
+ continue;
+ }
+
+ for (j = 0; devlist[j] != NULL; j++) {
+ fprintf(stderr, "device type %d device name %s\n", dtypes[i], devlist[j]->name);
+ msize += sizeof(dialogMenuItem);
+ if ((menu = realloc(menu, msize)) == NULL) {
+ err = "Failed to allocate memory for menu item";
+ goto errout;
+ }
+
+ bzero(&menu->items[count], sizeof(menu->items[0]));
+ menu->items[count].fire = cfgModuleFire;
+
+ menu->items[count].prompt = strdup(devlist[j]->name);
+ menu->items[count].title = strdup(devlist[j]->description);
+ /* XXX: dialog(3) sucks */
+ menu->items[count].aux = (long)devlist[j];
+ count++;
+ }
+ }
+
+ menu->items[count].prompt = NULL;
+ menu->items[count].title = NULL;
+
+ dmenuOpenSimple(menu, FALSE);
+
+ errout:
+ for (i = 0; i < count; i++) {
+ free(menu->items[i].prompt);
+ free(menu->items[i].title);
+ }
+
+ free(menu);
+
+ if (err != NULL) {
+ what |= DITEM_FAILURE;
+ if (!variable_get(VAR_NO_ERROR))
+ msgConfirm(err);
+ }
+
+ return (what);
+
+}
+
+static int
+cfgModuleFire(dialogMenuItem *self) {
+ Device *d;
+ FILE *fp;
+ int what = DITEM_SUCCESS;
+ extern char *distWanted;
+ qelement *list;
+ char *cp;
+
+ d = (Device *)self->aux;
+
+ msgDebug("cfgModuleFire: User selected %s (%s)\n", self->prompt, d->devname);
+
+ mediaClose();
+
+ cp = variable_get_value(VAR_INSTALL_CFG,
+ "Specify the name of a configuration file", 0);
+ if (!cp || !*cp) {
+ variable_unset(VAR_INSTALL_CFG);
+ what |= DITEM_FAILURE;
+ return what;
+ }
+
+ distWanted = cp;
+
+ mediaDevice = d;
+ if (!DEVICE_INIT(mediaDevice)) {
+ msgConfirm("Unable to mount filesystem.");
+ what |= DITEM_FAILURE;
+ mediaClose();
+ return what;
+ }
+ msgDebug("getting fp for %s\n", cp);
+
+ fp = DEVICE_GET(mediaDevice, cp, TRUE);
+ if (fp) {
+ msgDebug("opened OK, processing..\n");
+
+ list = dispatch_load_fp(fp);
+ fclose(fp);
+ mediaClose();
+
+ what |= dispatch_execute(list);
+ } else {
+ if (!variable_get(VAR_NO_ERROR))
+ msgConfirm("Configuration file '%s' not found.", cp);
+ variable_unset(VAR_INSTALL_CFG);
+ what |= DITEM_FAILURE;
+ mediaClose();
+ }
+
+ return(what);
+ }
diff --git a/usr.sbin/sysinstall/dist.c b/usr.sbin/sysinstall/dist.c
new file mode 100644
index 0000000..c3ec4c1
--- /dev/null
+++ b/usr.sbin/sysinstall/dist.c
@@ -0,0 +1,823 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sysinstall.h"
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <ctype.h>
+#include <signal.h>
+#include <libutil.h>
+
+unsigned int Dists;
+unsigned int SrcDists;
+unsigned int KernelDists;
+
+enum _disttype { DT_TARBALL, DT_SUBDIST, DT_PACKAGE };
+
+typedef struct _dist {
+ char *my_name;
+ unsigned int *my_mask;
+ unsigned int my_bit;
+ enum _disttype my_type;
+ union {
+ char *my_string; /* DT_TARBALL & DT_PACKAGE */
+ struct _dist *my_dist; /* DT_SUBDIST */
+ } my_data;
+} Distribution;
+
+static Distribution KernelDistTable[];
+static Distribution SrcDistTable[];
+
+#define DTE_TARBALL(name, mask, flag, directory) \
+ { name, mask, DIST_ ## flag, DT_TARBALL, { directory } }
+#define DTE_PACKAGE(name, mask, flag, package) \
+ { name, mask, DIST_ ## flag, DT_PACKAGE, { package } }
+#define DTE_SUBDIST(name, mask, flag, subdist) \
+ { name, mask, DIST_ ## flag, DT_SUBDIST, { .my_dist = subdist } }
+#define DTE_END { NULL, NULL, 0, 0, { NULL } }
+
+#define BASE_DIST (&DistTable[0])
+
+/* The top-level distribution categories */
+static Distribution DistTable[] = {
+ DTE_TARBALL("base", &Dists, BASE, "/"),
+ DTE_SUBDIST("kernels", &Dists, KERNEL, KernelDistTable),
+ DTE_TARBALL("doc", &Dists, DOC, "/"),
+ DTE_TARBALL("games", &Dists, GAMES, "/"),
+ DTE_TARBALL("manpages", &Dists, MANPAGES, "/"),
+ DTE_TARBALL("catpages", &Dists, CATPAGES, "/"),
+ DTE_TARBALL("proflibs", &Dists, PROFLIBS, "/"),
+ DTE_TARBALL("dict", &Dists, DICT, "/"),
+ DTE_TARBALL("info", &Dists, INFO, "/"),
+#ifdef __amd64__
+ DTE_TARBALL("lib32", &Dists, LIB32, "/"),
+#endif
+ DTE_SUBDIST("src", &Dists, SRC, SrcDistTable),
+ DTE_TARBALL("ports", &Dists, PORTS, "/usr"),
+ DTE_TARBALL("local", &Dists, LOCAL, "/"),
+ DTE_END,
+};
+
+/* The kernel distributions */
+static Distribution KernelDistTable[] = {
+ DTE_TARBALL("GENERIC", &KernelDists, KERNEL_GENERIC, "/boot"),
+#ifdef WITH_SMP
+ DTE_TARBALL("SMP", &KernelDists, KERNEL_SMP, "/boot"),
+#endif
+ DTE_END,
+};
+
+/* The /usr/src distribution */
+static Distribution SrcDistTable[] = {
+ DTE_TARBALL("sbase", &SrcDists, SRC_BASE, "/usr/src"),
+ DTE_TARBALL("scddl", &SrcDists, SRC_CDDL, "/usr/src"),
+ DTE_TARBALL("scontrib", &SrcDists, SRC_CONTRIB, "/usr/src"),
+ DTE_TARBALL("scrypto", &SrcDists, SRC_SCRYPTO, "/usr/src"),
+ DTE_TARBALL("sgnu", &SrcDists, SRC_GNU, "/usr/src"),
+ DTE_TARBALL("setc", &SrcDists, SRC_ETC, "/usr/src"),
+ DTE_TARBALL("sgames", &SrcDists, SRC_GAMES, "/usr/src"),
+ DTE_TARBALL("sinclude", &SrcDists, SRC_INCLUDE, "/usr/src"),
+ DTE_TARBALL("skrb5", &SrcDists, SRC_SKERBEROS5, "/usr/src"),
+ DTE_TARBALL("slib", &SrcDists, SRC_LIB, "/usr/src"),
+ DTE_TARBALL("slibexec", &SrcDists, SRC_LIBEXEC, "/usr/src"),
+ DTE_TARBALL("srelease", &SrcDists, SRC_RELEASE, "/usr/src"),
+ DTE_TARBALL("sbin", &SrcDists, SRC_BIN, "/usr/src"),
+ DTE_TARBALL("ssecure", &SrcDists, SRC_SSECURE, "/usr/src"),
+ DTE_TARBALL("ssbin", &SrcDists, SRC_SBIN, "/usr/src"),
+ DTE_TARBALL("sshare", &SrcDists, SRC_SHARE, "/usr/src"),
+ DTE_TARBALL("ssys", &SrcDists, SRC_SYS, "/usr/src"),
+ DTE_TARBALL("subin", &SrcDists, SRC_UBIN, "/usr/src"),
+ DTE_TARBALL("susbin", &SrcDists, SRC_USBIN, "/usr/src"),
+ DTE_TARBALL("stools", &SrcDists, SRC_TOOLS, "/usr/src"),
+ DTE_TARBALL("srescue", &SrcDists, SRC_RESCUE, "/usr/src"),
+ DTE_END,
+};
+
+static int distMaybeSetPorts(dialogMenuItem *self);
+
+static void
+distVerifyFlags(void)
+{
+ if (SrcDists)
+ Dists |= DIST_SRC;
+ if (KernelDists)
+ Dists |= DIST_KERNEL;
+ if (isDebug())
+ msgDebug("Dist Masks: Dists: %0x, Srcs: %0x Kernels: %0x\n", Dists,
+ SrcDists, KernelDists);
+}
+
+int
+distReset(dialogMenuItem *self)
+{
+ Dists = 0;
+ SrcDists = 0;
+ KernelDists = 0;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+int
+distConfig(dialogMenuItem *self)
+{
+ char *cp;
+
+ distReset(NULL);
+
+ if ((cp = variable_get(VAR_DIST_MAIN)) != NULL)
+ Dists = atoi(cp);
+
+ if ((cp = variable_get(VAR_DIST_SRC)) != NULL)
+ SrcDists = atoi(cp);
+
+ if ((cp = variable_get(VAR_DIST_KERNEL)) != NULL)
+ KernelDists = atoi(cp);
+
+ distVerifyFlags();
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+int
+selectKernel(void)
+{
+#ifdef WITH_SMP
+ /* select default kernel based on deduced cpu count */
+ return NCpus > 1 ? DIST_KERNEL_SMP : DIST_KERNEL_GENERIC;
+#else
+ return DIST_KERNEL_GENERIC;
+#endif
+}
+
+int
+distSetDeveloper(dialogMenuItem *self)
+{
+ int i;
+
+ distReset(NULL);
+ Dists = _DIST_DEVELOPER;
+ SrcDists = DIST_SRC_ALL;
+ KernelDists = selectKernel();
+ i = distMaybeSetPorts(self);
+ distVerifyFlags();
+ return i;
+}
+
+int
+distSetKernDeveloper(dialogMenuItem *self)
+{
+ int i;
+
+ distReset(NULL);
+ Dists = _DIST_DEVELOPER;
+ SrcDists = DIST_SRC_SYS | DIST_SRC_BASE;
+ KernelDists = selectKernel();
+ i = distMaybeSetPorts(self);
+ distVerifyFlags();
+ return i;
+}
+
+int
+distSetUser(dialogMenuItem *self)
+{
+ int i;
+
+ distReset(NULL);
+ Dists = _DIST_USER;
+ KernelDists = selectKernel();
+ i = distMaybeSetPorts(self);
+ distVerifyFlags();
+ return i;
+}
+
+int
+distSetMinimum(dialogMenuItem *self)
+{
+ distReset(NULL);
+ Dists = DIST_BASE | DIST_KERNEL;
+ KernelDists = selectKernel();
+ distVerifyFlags();
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+int
+distSetEverything(dialogMenuItem *self)
+{
+ int i;
+
+ Dists = DIST_ALL;
+ SrcDists = DIST_SRC_ALL;
+ KernelDists = DIST_KERNEL_ALL;
+ i = distMaybeSetPorts(self);
+ distVerifyFlags();
+ return i | DITEM_REDRAW;
+}
+
+static int
+distMaybeSetPorts(dialogMenuItem *self)
+{
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to install the FreeBSD ports collection?\n\n"
+ "This will give you ready access to over 19,000 ported software packages,\n"
+ "at a cost of around 445MB of disk space when \"clean\" and possibly\n"
+ "much more than that when a lot of the distribution tarballs are loaded\n"
+ "(unless you have the extra discs available from a FreeBSD CD/DVD distribution\n"
+ "and can mount them on /cdrom, in which case this is far less of a problem).\n\n"
+ "The ports collection is a very valuable resource and well worth having\n"
+ "on your /usr partition, so it is advisable to say Yes to this option.\n\n"
+ "For more information on the ports collection & the latest ports, visit:\n"
+ " http://www.freebsd.org/ports\n"))
+ Dists |= DIST_PORTS;
+ else
+ Dists &= ~DIST_PORTS;
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+static Boolean
+distSetByName(Distribution *dist, char *name)
+{
+ int i, status = FALSE;
+
+ /* Loop through current set */
+ for (i = 0; dist[i].my_name; i++) {
+ switch (dist[i].my_type) {
+ case DT_TARBALL:
+ case DT_PACKAGE:
+ if (!strcmp(dist[i].my_name, name)) {
+ *(dist[i].my_mask) |= dist[i].my_bit;
+ status = TRUE;
+ }
+ break;
+ case DT_SUBDIST:
+ if (distSetByName(dist[i].my_data.my_dist, name)) {
+ status = TRUE;
+ }
+ break;
+ }
+ }
+ distVerifyFlags();
+ return status;
+}
+
+static Boolean
+distUnsetByName(Distribution *dist, char *name)
+{
+ int i, status = FALSE;
+
+ /* Loop through current set */
+ for (i = 0; dist[i].my_name; i++) {
+ switch (dist[i].my_type) {
+ case DT_TARBALL:
+ case DT_PACKAGE:
+ if (!strcmp(dist[i].my_name, name)) {
+ *(dist[i].my_mask) &= ~(dist[i].my_bit);
+ status = TRUE;
+ }
+ break;
+ case DT_SUBDIST:
+ if (distUnsetByName(dist[i].my_data.my_dist, name)) {
+ status = TRUE;
+ }
+ break;
+ }
+ }
+ return status;
+}
+
+/* Just for the dispatch stuff */
+int
+distSetCustom(dialogMenuItem *self)
+{
+ char *cp, *cp2, *tmp;
+
+ if (!(tmp = variable_get(VAR_DISTS))) {
+ msgDebug("distSetCustom() called without %s variable set.\n", VAR_DISTS);
+ return DITEM_FAILURE;
+ }
+
+ cp = alloca(strlen(tmp) + 1);
+ if (!cp)
+ msgFatal("Couldn't alloca() %d bytes!\n", (int)(strlen(tmp) + 1));
+ strcpy(cp, tmp);
+ while (cp) {
+ if ((cp2 = index(cp, ' ')) != NULL)
+ *(cp2++) = '\0';
+ if (!distSetByName(DistTable, cp))
+ msgDebug("distSetCustom: Warning, no such release \"%s\"\n", cp);
+ cp = cp2;
+ }
+ distVerifyFlags();
+ return DITEM_SUCCESS;
+}
+
+/* Just for the dispatch stuff */
+int
+distUnsetCustom(dialogMenuItem *self)
+{
+ char *cp, *cp2, *tmp;
+
+ if (!(tmp = variable_get(VAR_DISTS))) {
+ msgDebug("distUnsetCustom() called without %s variable set.\n", VAR_DISTS);
+ return DITEM_FAILURE;
+ }
+
+ cp = alloca(strlen(tmp) + 1);
+ if (!cp)
+ msgFatal("Couldn't alloca() %d bytes!\n", (int)(strlen(tmp) + 1));
+ strcpy(cp, tmp);
+ while (cp) {
+ if ((cp2 = index(cp, ' ')) != NULL)
+ *(cp2++) = '\0';
+ if (!distUnsetByName(DistTable, cp))
+ msgDebug("distUnsetCustom: Warning, no such release \"%s\"\n", cp);
+ cp = cp2;
+ }
+ return DITEM_SUCCESS;
+}
+
+int
+distSetSrc(dialogMenuItem *self)
+{
+ int i;
+
+ dialog_clear_norefresh();
+ if (!dmenuOpenSimple(&MenuSrcDistributions, FALSE))
+ i = DITEM_FAILURE;
+ else
+ i = DITEM_SUCCESS;
+ distVerifyFlags();
+ return i | DITEM_RESTORE;
+}
+
+int
+distSetKernel(dialogMenuItem *self)
+{
+ int i;
+
+ dialog_clear_norefresh();
+ if (!dmenuOpenSimple(&MenuKernelDistributions, FALSE))
+ i = DITEM_FAILURE;
+ else
+ i = DITEM_SUCCESS;
+ distVerifyFlags();
+ return i | DITEM_RESTORE;
+}
+
+static Boolean got_intr = FALSE;
+
+/* timeout handler */
+static void
+handle_intr(int sig)
+{
+ msgDebug("User generated interrupt.\n");
+ got_intr = TRUE;
+}
+
+static int
+check_for_interrupt(void)
+{
+ if (got_intr) {
+ got_intr = FALSE;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/*
+ * translate distribution filename to lower case
+ * as doTARBALL does in release/Makefile
+ */
+static void
+translateDist(char trdist[PATH_MAX], const char *dist)
+{
+ int j;
+
+ /*
+ * translate distribution filename to lower case
+ * as doTARBALL does in release/Makefile
+ */
+ for (j = 0; j < PATH_MAX-1 && dist[j] != '\0'; j++)
+ trdist[j] = tolower(dist[j]);
+ trdist[j] = '\0';
+}
+
+/*
+ * Try to get distribution as multiple pieces, locating and parsing an
+ * info file which tells us how many we need for this distribution.
+ */
+static Boolean
+distExtractTarball(char *path, char *dist, char *my_dir, int is_base)
+{
+ char *buf = NULL, trdist[PATH_MAX], fname[PATH_MAX];
+ struct timeval start, stop;
+ int j, status, total, intr;
+ int cpid, zpid, fd2, chunk, numchunks;
+ properties dist_attr = NULL;
+ const char *tmp;
+ FILE *fp;
+
+ translateDist(trdist, dist);
+ if (isDebug())
+ msgDebug("%s: path \"%s\" dist \"%s\" trdist \"%s\" "
+ "my_dir \"%s\" %sis_base\n",
+ __func__, path, dist, trdist, my_dir, is_base ? "" : "!");
+
+ status = TRUE;
+ numchunks = 0;
+ snprintf(fname, sizeof (fname), "%s/%s.inf", path, trdist);
+
+getinfo:
+ fp = DEVICE_GET(mediaDevice, fname, TRUE);
+ intr = check_for_interrupt();
+ if (fp == (FILE *)IO_ERROR || intr || !mediaDevice) {
+ if (isDebug())
+ msgDebug("%s: fname %s fp: %p, intr: %d mediaDevice: %p\n",
+ __func__, fname, fp, intr, mediaDevice);
+ /* Hard error, can't continue */
+ if (!msgYesNo("Unable to open %s: %s.\nReinitialize media?",
+ fname, !intr ? "I/O error." : "User interrupt.")) {
+ DEVICE_SHUTDOWN(mediaDevice);
+ if (!DEVICE_INIT(mediaDevice))
+ return (FALSE);
+ goto getinfo;
+ } else
+ return (FALSE);
+ } else if (fp == NULL) {
+ /* No attributes file, so try as a single file. */
+ snprintf(fname, sizeof(fname), "%s/%s.%s", path, trdist,
+ USE_GZIP ? "tgz" : "tbz");
+ if (isDebug())
+ msgDebug("%s: fp is NULL (1) fname: %s\n", __func__, fname);
+ /*
+ * Passing TRUE as 3rd parm to get routine makes this a "probing"
+ * get, for which errors are not considered too significant.
+ */
+ getsingle:
+ fp = DEVICE_GET(mediaDevice, fname, TRUE);
+ intr = check_for_interrupt();
+ if (fp == (FILE *)IO_ERROR || intr || !mediaDevice) {
+ if (isDebug())
+ msgDebug("%s: fname %s fp: %p, intr: %d mediaDevice: %p\n",
+ __func__, fname, fp, intr, mediaDevice);
+ /* Hard error, can't continue */
+ msgConfirm("Unable to open %s: %s", fname,
+ !intr ? "I/O error" : "User interrupt");
+ DEVICE_SHUTDOWN(mediaDevice);
+ if (!DEVICE_INIT(mediaDevice))
+ return (FALSE);
+ goto getsingle;
+ } else if (fp != NULL) {
+ char *dir = root_bias(my_dir);
+
+ dialog_clear_norefresh();
+ msgNotify("Extracting %s into %s directory...", dist, dir);
+ status = mediaExtractDist(dir, dist, fp);
+ fclose(fp);
+ return (status);
+ } else {
+ if (isDebug())
+ msgDebug("%s: fp is NULL (2) fname %s\n", __func__, fname);
+ return (FALSE);
+ }
+ }
+
+ if (isDebug())
+ msgDebug("Parsing attributes file for distribution %s\n", dist);
+
+ dist_attr = properties_read(fileno(fp));
+ intr = check_for_interrupt();
+ if (intr || !dist_attr) {
+ if (isDebug())
+ msgDebug("%s: intr %d dist_attr %p\n", __func__, intr, dist_attr);
+ msgConfirm("Cannot parse information file for the %s distribution: %s\n"
+ "Please verify that your media is valid and try again.",
+ dist, !intr ? "I/O error" : "User interrupt");
+ } else {
+ tmp = property_find(dist_attr, "Pieces");
+ if (tmp)
+ numchunks = strtol(tmp, 0, 0);
+ }
+ fclose(fp);
+ if (!numchunks) {
+ if (isDebug())
+ msgDebug("%s: numchunks is zero\n", __func__);
+ return (TRUE);
+ }
+
+ if (isDebug())
+ msgDebug("Attempting to extract distribution from %u chunks.\n",
+ numchunks);
+
+ total = 0;
+ (void)gettimeofday(&start, (struct timezone *)NULL);
+
+ /* We have one or more chunks, initialize unpackers... */
+ mediaExtractDistBegin(root_bias(my_dir), &fd2, &zpid, &cpid);
+
+ /* And go for all the chunks */
+ dialog_clear_norefresh();
+ for (chunk = 0; chunk < numchunks; chunk++) {
+ int n, retval, last_msg, chunksize, realsize;
+ char prompt[80];
+
+ last_msg = 0;
+
+ getchunk:
+ snprintf(fname, sizeof(fname), "cksum.%c%c", (chunk / 26) + 'a',
+ (chunk % 26) + 'a');
+ tmp = property_find(dist_attr, fname);
+ chunksize = 0;
+ if (tmp) {
+ tmp = index(tmp, ' ');
+ chunksize = strtol(tmp, 0, 0);
+ }
+ snprintf(fname, sizeof(fname), "%s/%s.%c%c", path, trdist, (chunk / 26) + 'a',
+ (chunk % 26) + 'a');
+ if (isDebug())
+ msgDebug("trying for piece %d of %d: %s\n", chunk + 1, numchunks,
+ fname);
+ fp = DEVICE_GET(mediaDevice, fname, FALSE);
+ intr = check_for_interrupt();
+ /* XXX: this can't work if we get an I/O error */
+ if (fp <= (FILE *)NULL || intr) {
+ if (fp == NULL)
+ msgConfirm("Failed to find %s on this media. Reinitializing media.", fname);
+ else
+ msgConfirm("Failed to retreive piece file %s.\n"
+ "%s: Reinitializing media.",
+ fname, !intr ? "I/O error" : "User interrupt");
+ DEVICE_SHUTDOWN(mediaDevice);
+ if (!DEVICE_INIT(mediaDevice))
+ goto punt;
+ else
+ goto getchunk;
+ }
+
+ snprintf(prompt, sizeof(prompt), "Extracting %s into %s directory...",
+ dist, root_bias(my_dir));
+ dialog_gauge("Progress", prompt, 8, 15, 6, 50,
+ (chunk + 1) * 100 / numchunks);
+
+ buf = safe_realloc(buf, chunksize);
+ realsize = 0;
+ while (1) {
+ int seconds;
+
+ n = fread(buf + realsize, 1, BUFSIZ, fp);
+ if (check_for_interrupt()) {
+ msgConfirm("Media read error: User interrupt.");
+ fclose(fp);
+ goto punt;
+ } else if (n <= 0)
+ break;
+ total += n;
+ realsize += n;
+
+ /* Print statistics about how we're doing */
+ (void) gettimeofday(&stop, (struct timezone *)0);
+ stop.tv_sec = stop.tv_sec - start.tv_sec;
+ stop.tv_usec = stop.tv_usec - start.tv_usec;
+ if (stop.tv_usec < 0)
+ stop.tv_sec--, stop.tv_usec += 1000000;
+ seconds = stop.tv_sec + (stop.tv_usec / 1000000.0);
+ if (!seconds)
+ seconds = 1;
+
+ if (seconds != last_msg) {
+ last_msg = seconds;
+ msgInfo("%10d bytes read from %s dist, chunk %2d of %2d @ %.1f KBytes/sec.",
+ total, dist, chunk + 1, numchunks,
+ (total / seconds) / 1000.0);
+ }
+ }
+ fclose(fp);
+
+ if (!chunksize || (realsize == chunksize)) {
+ /* No substitution necessary */
+ retval = write(fd2, buf, realsize);
+ if (retval != realsize) {
+ fclose(fp);
+ dialog_clear_norefresh();
+ msgConfirm("Write failure on transfer! (wrote %d bytes of %d bytes)", retval, realsize);
+ goto punt;
+ }
+ } else {
+ for (j = 0; j < realsize; j++) {
+ /* On finding CRLF, skip the CR; don't exceed end of buffer. */
+ if ((buf[j] != 0x0d) || (j == total - 1) || (buf[j + 1] != 0x0a)) {
+ retval = write(fd2, buf + j, 1);
+ if (retval != 1) {
+ fclose(fp);
+ dialog_clear_norefresh();
+ msgConfirm("Write failure on transfer! (wrote %d bytes of %d bytes)", j, chunksize);
+ goto punt;
+ }
+ }
+ }
+ }
+ }
+ goto done;
+
+punt:
+ status = FALSE;
+done:
+ properties_free(dist_attr);
+ close(fd2);
+ if (status != FALSE)
+ status = mediaExtractDistEnd(zpid, cpid);
+ else
+ (void)mediaExtractDistEnd(zpid, cpid);
+
+ safe_free(buf);
+ return (status);
+}
+
+static Boolean
+distExtract(char *parent, Distribution *me)
+{
+ int i, status;
+ char *path, *dist;
+ WINDOW *w = savescr();
+ struct sigaction old, new;
+
+ status = TRUE;
+ if (isDebug())
+ msgDebug("distExtract: parent: %s, me: %s\n", parent ? parent : "(none)", me->my_name);
+
+ /* Make ^C fake a sudden timeout */
+ new.sa_handler = handle_intr;
+ new.sa_flags = 0;
+ (void)sigemptyset(&new.sa_mask);
+ dialog_clear_norefresh();
+ dialog_msgbox("Please Wait", "Extracting all requested distributions...", -1, -1, 0);
+ sigaction(SIGINT, &new, &old);
+
+ /* Loop through to see if we're in our parent's plans */
+ for (i = 0; me[i].my_name; i++) {
+ dist = me[i].my_name;
+ path = parent ? parent : dist;
+
+ /* If our bit isn't set, go to the next */
+ if (!(me[i].my_bit & *(me[i].my_mask)))
+ continue;
+
+ switch (me[i].my_type) {
+ case DT_SUBDIST:
+ /* Recurse if we actually have a sub-distribution */
+ status = distExtract(dist, me[i].my_data.my_dist);
+ if (!status) {
+ dialog_clear_norefresh();
+ msgConfirm("Unable to transfer all components of the %s distribution.\n"
+ "You may wish to switch media types and try again.\n",
+ me[i].my_name);
+ }
+ break;
+ case DT_PACKAGE:
+ dialog_clear_norefresh();
+ msgNotify("Installing %s distribution...", dist);
+ status = (package_add(me[i].my_data.my_string) == DITEM_SUCCESS);
+ if (!status)
+ dialog_clear_norefresh();
+ break;
+ case DT_TARBALL:
+ status = distExtractTarball(path, dist, me[i].my_data.my_string,
+ &me[i] == BASE_DIST);
+ if (!status) {
+ dialog_clear_norefresh();
+ if (me[i].my_bit != DIST_LOCAL) {
+ status = msgYesNo("Unable to transfer the %s distribution from\n%s.\n\n"
+ "Do you want to try to retrieve it again?",
+ me[i].my_name, mediaDevice->name);
+ if (!status)
+ --i;
+ status = FALSE;
+ }
+ }
+ break;
+ }
+
+ /*
+ * If extract was successful, remove ourselves from further
+ * consideration.
+ */
+ if (status)
+ *(me[i].my_mask) &= ~(me[i].my_bit);
+ }
+ sigaction(SIGINT, &old, NULL); /* Restore signal handler */
+ restorescr(w);
+ return status;
+}
+
+static void
+printSelected(char *buf, int selected, Distribution *me, int *col)
+{
+ int i;
+
+ /* Loop through to see if we're in our parent's plans */
+ for (i = 0; me[i].my_name; i++) {
+
+ /* If our bit isn't set, go to the next */
+ if (!(me[i].my_bit & selected))
+ continue;
+
+ *col += strlen(me[i].my_name);
+ if (*col > 50) {
+ *col = 0;
+ strcat(buf, "\n");
+ }
+ sprintf(&buf[strlen(buf)], " %s", me[i].my_name);
+
+ /* Recurse if have a sub-distribution */
+ if (me[i].my_type == DT_SUBDIST)
+ printSelected(buf, *(me[i].my_mask), me[i].my_data.my_dist, col);
+ }
+}
+
+int
+distExtractAll(dialogMenuItem *self)
+{
+ int old_dists, old_kernel, retries = 0, status = DITEM_SUCCESS;
+ char buf[512];
+ WINDOW *w;
+
+ /* paranoia */
+ if (!Dists) {
+ if (!dmenuOpenSimple(&MenuSubDistributions, FALSE) || !Dists)
+ return DITEM_FAILURE;
+ }
+
+ if (!mediaVerify() || !DEVICE_INIT(mediaDevice))
+ return DITEM_FAILURE;
+
+ old_dists = Dists;
+ old_kernel = KernelDists;
+ distVerifyFlags();
+
+ dialog_clear_norefresh();
+ w = savescr();
+ msgNotify("Attempting to install all selected distributions..");
+
+ /* Try for 3 times around the loop, then give up. */
+ while (Dists && ++retries < 3)
+ distExtract(NULL, DistTable);
+
+ dialog_clear_norefresh();
+ /* Only do base fixup if base dist was successfully extracted */
+ if ((old_dists & DIST_BASE) && !(Dists & DIST_BASE))
+ status |= installFixupBase(self);
+ /* Only do kernel fixup if kernel dist was successfully extracted */
+ if ((old_dists & DIST_KERNEL) && !(Dists & DIST_KERNEL))
+ status |= installFixupKernel(self, old_kernel);
+
+ /* Clear any local dist flags now */
+ Dists &= ~DIST_LOCAL;
+
+ if (Dists) {
+ int col = 0;
+
+ buf[0] = '\0';
+ dialog_clear_norefresh();
+ printSelected(buf, Dists, DistTable, &col);
+ dialog_clear_norefresh();
+ if (col) {
+ msgConfirm("Couldn't extract the following distributions. This may\n"
+ "be because they were not available on the installation\n"
+ "media you've chosen:\n\n\t%s", buf);
+ }
+ }
+ restorescr(w);
+ return status;
+}
diff --git a/usr.sbin/sysinstall/dist.h b/usr.sbin/sysinstall/dist.h
new file mode 100644
index 0000000..0eb42e1
--- /dev/null
+++ b/usr.sbin/sysinstall/dist.h
@@ -0,0 +1,61 @@
+/* $FreeBSD$ */
+
+#ifndef _DIST_H_INCLUDE
+#define _DIST_H_INCLUDE
+
+/* Bitfields for distributions - hope we never have more than 32! :-) */
+#define DIST_BASE 0x00001
+#define DIST_GAMES 0x00002
+#define DIST_MANPAGES 0x00004
+#define DIST_PROFLIBS 0x00008
+#define DIST_DICT 0x00010
+#define DIST_SRC 0x00020
+#define DIST_DOC 0x00040
+#define DIST_INFO 0x00080
+#define DIST_CATPAGES 0x00200
+#define DIST_PORTS 0x00400
+#define DIST_LOCAL 0x00800
+#ifdef __amd64__
+#define DIST_LIB32 0x01000
+#endif
+#define DIST_KERNEL 0x02000
+#define DIST_ALL 0xFFFFF
+
+/* Subtypes for SRC distribution */
+#define DIST_SRC_BASE 0x00001
+#define DIST_SRC_CONTRIB 0x00002
+#define DIST_SRC_GNU 0x00004
+#define DIST_SRC_ETC 0x00008
+#define DIST_SRC_GAMES 0x00010
+#define DIST_SRC_INCLUDE 0x00020
+#define DIST_SRC_LIB 0x00040
+#define DIST_SRC_LIBEXEC 0x00080
+#define DIST_SRC_TOOLS 0x00100
+#define DIST_SRC_RELEASE 0x00200
+#define DIST_SRC_SBIN 0x00400
+#define DIST_SRC_SHARE 0x00800
+#define DIST_SRC_SYS 0x01000
+#define DIST_SRC_UBIN 0x02000
+#define DIST_SRC_USBIN 0x04000
+#define DIST_SRC_BIN 0x08000
+#define DIST_SRC_SCRYPTO 0x10000
+#define DIST_SRC_SSECURE 0x20000
+#define DIST_SRC_SKERBEROS5 0x40000
+#define DIST_SRC_RESCUE 0x80000
+#define DIST_SRC_CDDL 0x100000
+#define DIST_SRC_ALL 0x3FFFFF
+
+/* Subtypes for KERNEL distribution */
+#define DIST_KERNEL_GENERIC 0x00001
+#define DIST_KERNEL_SMP 0x00002
+#define DIST_KERNEL_ALL 0xFFFFF
+
+/* Canned distribution sets */
+
+#define _DIST_USER \
+ ( DIST_BASE | DIST_KERNEL | DIST_DOC | DIST_MANPAGES | DIST_DICT )
+
+#define _DIST_DEVELOPER \
+ ( _DIST_USER | DIST_PROFLIBS | DIST_INFO | DIST_SRC )
+
+#endif /* _DIST_H_INCLUDE */
diff --git a/usr.sbin/sysinstall/dmenu.c b/usr.sbin/sysinstall/dmenu.c
new file mode 100644
index 0000000..1aef667
--- /dev/null
+++ b/usr.sbin/sysinstall/dmenu.c
@@ -0,0 +1,393 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated for what's essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sysinstall.h"
+#include <sys/param.h>
+#include <errno.h>
+
+#define MAX_MENU 15
+
+static Boolean exited;
+
+int
+dmenuDisplayFile(dialogMenuItem *tmp)
+{
+ systemDisplayHelp((char *)tmp->data);
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuSubmenu(dialogMenuItem *tmp)
+{
+ return (dmenuOpenSimple((DMenu *)(tmp->data), FALSE) ? DITEM_SUCCESS : DITEM_FAILURE);
+}
+
+int
+dmenuSystemCommand(dialogMenuItem *self)
+{
+ WINDOW *w = NULL; /* Keep lint happy */
+
+ /* If aux is set, the command is known not to produce any screen-spoiling output */
+ if (!self->aux)
+ w = savescr();
+ systemExecute((char *)self->data);
+ if (!self->aux)
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuSystemCommandBox(dialogMenuItem *tmp)
+{
+ WINDOW *w = savescr();
+
+ use_helpfile(NULL);
+ use_helpline("Select OK to dismiss this dialog");
+ dialog_prgbox(tmp->title, (char *)tmp->data, 22, 76, 1, 1);
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuExit(dialogMenuItem *tmp)
+{
+ exited = TRUE;
+ return DITEM_LEAVE_MENU;
+}
+
+int
+dmenuSetVariable(dialogMenuItem *tmp)
+{
+ variable_set((char *)tmp->data, *((char *)tmp->data) != '_');
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuSetVariables(dialogMenuItem *tmp)
+{
+ char *cp1, *cp2;
+ char *copy = strdup((char *)tmp->data);
+
+ for (cp1 = copy; cp1 != NULL;) {
+ cp2 = index(cp1, ',');
+ if (cp2 != NULL) *cp2++ = '\0';
+ variable_set(cp1, *cp1 != '_');
+ cp1 = cp2;
+ }
+ free(copy);
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuSetCountryVariable(dialogMenuItem *tmp)
+{
+ variable_set((char *)tmp->data, FALSE);
+#ifdef WITH_SYSCONS
+ /* Don't prompt the user for a keymap if they're using the default locale. */
+ if (!strcmp(variable_get(VAR_COUNTRY), DEFAULT_COUNTRY))
+ return DITEM_SUCCESS;
+
+ return keymapMenuSelect(tmp);
+#else
+ return DITEM_SUCCESS;
+#endif
+}
+
+int
+dmenuSetKmapVariable(dialogMenuItem *tmp)
+{
+ char *lang;
+ int err;
+
+ variable_set((char *)tmp->data, TRUE);
+ lang = variable_get(VAR_KEYMAP);
+ if (lang != NULL)
+ {
+ err = loadKeymap(lang);
+ if (err == -1)
+ msgConfirm("No appropriate keyboard map found, sorry.");
+ else if (err == -2)
+ msgConfirm("Error installing keyboard map, errno = %d.", errno);
+ }
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuToggleVariable(dialogMenuItem *tmp)
+{
+ char *var, *cp;
+ int status;
+
+ if (!(var = strdup((char *)tmp->data))) {
+ msgConfirm("Incorrect data field for `%s'!", tmp->title);
+ return DITEM_FAILURE;
+ }
+ if (!(cp = index(var, '='))) {
+ msgConfirm("Data field for %s is not in var=value format!", tmp->title);
+ return DITEM_FAILURE;
+ }
+ status = variable_check(var);
+ *cp = '\0';
+ variable_set2(var, status ? "NO" : "YES", *var != '_');
+ free(var);
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuISetVariable(dialogMenuItem *tmp)
+{
+ char *ans, *p, *var;
+
+ if (!(var = strdup((char *)tmp->data))) {
+ msgConfirm("Incorrect data field for `%s'!", tmp->title);
+ return DITEM_FAILURE;
+ }
+ if ((p = index(var, '=')) != NULL)
+ *p = '\0';
+ ans = msgGetInput(variable_get(var), tmp->title, 1);
+ if (!ans) {
+ free(var);
+ return DITEM_FAILURE;
+ } else if (!*ans)
+ variable_unset(var);
+ else
+ variable_set2(var, ans, *var != '_');
+ free(var);
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuSetFlag(dialogMenuItem *tmp)
+{
+ if (*((unsigned int *)tmp->data) & tmp->aux)
+ *((unsigned int *)tmp->data) &= ~tmp->aux;
+ else
+ *((unsigned int *)tmp->data) |= tmp->aux;
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuSetValue(dialogMenuItem *tmp)
+{
+ *((unsigned int *)tmp->data) = tmp->aux;
+ return DITEM_SUCCESS;
+}
+
+/* Traverse menu but give user no control over positioning */
+Boolean
+dmenuOpenSimple(DMenu *menu, Boolean buttons)
+{
+ int choice, scroll, curr, max;
+
+ choice = scroll = curr = max = 0;
+ return dmenuOpen(menu, &choice, &scroll, &curr, &max, buttons);
+}
+
+/* Work functions for the state hook */
+int
+dmenuFlagCheck(dialogMenuItem *item)
+{
+ return (*((unsigned int *)item->data) & item->aux);
+}
+
+int
+dmenuVarCheck(dialogMenuItem *item)
+{
+ char *w;
+
+ w = (char *)item->aux;
+ if (!w)
+ w = (char *)item->data;
+ return variable_check(w);
+}
+
+int
+dmenuVarsCheck(dialogMenuItem *item)
+{
+ int res, init;
+ char *w, *cp1, *cp2;
+ char *copy;
+
+ w = (char *)item->aux;
+ if (!w)
+ w = (char *)item->data;
+ if (!w)
+ return FALSE;
+
+ copy = strdup(w);
+ res = TRUE;
+ init = FALSE;
+ for (cp1 = copy; cp1 != NULL;) {
+ init = TRUE;
+ cp2 = index(cp1, ',');
+ if (cp2 != NULL)
+ *cp2++ = '\0';
+ res = res && variable_check(cp1);
+ cp1 = cp2;
+ }
+ free(copy);
+ return res && init;
+}
+
+int
+dmenuRadioCheck(dialogMenuItem *item)
+{
+ return (*((int *)item->data) == item->aux);
+}
+
+static int
+menu_height(DMenu *menu, int n)
+{
+ int max;
+ char *t;
+
+ max = MAX_MENU;
+ if (StatusLine > 24)
+ max += StatusLine - 24;
+ for (t = menu->prompt; *t; t++) {
+ if (*t == '\n')
+ --max;
+ }
+ return n > max ? max : n;
+}
+
+/* Find a menu item that matches any field. */
+int
+dmenuFindItem(DMenu *menu, const char *prompt, const char *title, void *data)
+{
+ dialogMenuItem *items = menu->items;
+ int i;
+
+ for (i = 0; items[i].prompt; ++i)
+ if ((prompt && !strcmp(items[i].prompt, prompt)) ||
+ (title && !strcmp(items[i].title, title)) ||
+ (data && items[i].data == data))
+ return i;
+
+ return -1;
+}
+
+/* Set the default item for a menu by index and scroll to it. */
+void
+dmenuSetDefaultIndex(DMenu *menu, int *choice, int *scroll, int *curr, int *max)
+{
+ int nitem;
+ int height;
+
+ *curr = *max = 0;
+
+ for (nitem = 0; menu->items[nitem].prompt; ++nitem);
+
+ height = menu_height(menu, nitem);
+ if (*choice > height)
+ {
+ *scroll = MIN(nitem - height, *choice);
+ *choice = *choice - *scroll;
+ }
+ else
+ *scroll = 0;
+}
+
+/* Set the default menu item that matches any field and scroll to it. */
+Boolean
+dmenuSetDefaultItem(DMenu *menu, const char *prompt, const char *title, void *data,
+ int *choice, int *scroll, int *curr, int *max)
+{
+ if ((*choice = dmenuFindItem(menu, prompt, title, data)) != -1)
+ {
+ dmenuSetDefaultIndex(menu, choice, scroll, curr, max);
+ return TRUE;
+ }
+ else
+ {
+ *choice = *scroll = *curr = *max = 0;
+ return FALSE;
+ }
+}
+
+/* Traverse over an internal menu */
+Boolean
+dmenuOpen(DMenu *menu, int *choice, int *scroll, int *curr, int *max, Boolean buttons)
+{
+ int n, rval = 0;
+ dialogMenuItem *items;
+
+ items = menu->items;
+ if (buttons)
+ items += 2;
+ /* Count up all the items */
+ for (n = 0; items[n].title; n++);
+
+ while (1) {
+ char buf[FILENAME_MAX];
+ WINDOW *w = savescr();
+
+ /* Any helpful hints, put 'em up! */
+ use_helpline(menu->helpline);
+ use_helpfile(systemHelpFile(menu->helpfile, buf));
+ dialog_clear_norefresh();
+ /* Pop up that dialog! */
+ if (menu->type & DMENU_NORMAL_TYPE)
+ rval = dialog_menu((u_char *)menu->title, (u_char *)menu->prompt,
+ -1, -1, menu_height(menu, n), -n, items,
+ (char *)(uintptr_t)buttons, choice, scroll);
+
+ else if (menu->type & DMENU_RADIO_TYPE)
+ rval = dialog_radiolist((u_char *)menu->title,
+ (u_char *)menu->prompt, -1, -1, menu_height(menu, n), -n,
+ items, (char *)(uintptr_t)buttons);
+
+ else if (menu->type & DMENU_CHECKLIST_TYPE)
+ rval = dialog_checklist((u_char *)menu->title,
+ (u_char *)menu->prompt, -1, -1, menu_height(menu, n), -n,
+ items, (char *)(uintptr_t)buttons);
+ else
+ msgFatal("Menu: `%s' is of an unknown type\n", menu->title);
+ if (exited) {
+ exited = FALSE;
+ restorescr(w);
+ return TRUE;
+ }
+ else if (rval) {
+ restorescr(w);
+ return FALSE;
+ }
+ else if (menu->type & DMENU_SELECTION_RETURNS) {
+ restorescr(w);
+ return TRUE;
+ }
+ }
+}
diff --git a/usr.sbin/sysinstall/doc.c b/usr.sbin/sysinstall/doc.c
new file mode 100644
index 0000000..5294972
--- /dev/null
+++ b/usr.sbin/sysinstall/doc.c
@@ -0,0 +1,123 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated for what's essentially a complete rewrite.
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sysinstall.h"
+
+/*
+ * This is called from the main menu. Try to find a copy of Lynx from somewhere
+ * and fire it up on the first copy of the handbook we can find.
+ */
+int
+docBrowser(dialogMenuItem *self)
+{
+ int ret;
+ char *browser = variable_get(VAR_BROWSER_PACKAGE);
+
+ if (RunningAsInit && !strstr(variable_get(SYSTEM_STATE), "install")) {
+ msgConfirm("This option may only be used after the system is installed, sorry!");
+ return DITEM_FAILURE;
+ }
+
+ /* First, make sure we have whatever browser we've chosen is here */
+ if (!package_installed(browser)) {
+ ret = package_add(browser);
+ if (DITEM_STATUS(ret) != DITEM_SUCCESS) {
+ msgConfirm("Unable to install the %s HTML browser package. You may\n"
+ "wish to verify that your media is configured correctly and\n"
+ "try again.", browser);
+ return ret;
+ }
+ }
+
+ if (!file_executable(variable_get(VAR_BROWSER_BINARY))) {
+ if (!msgYesNo("Hmmm. The %s package claims to have installed, but I can't\n"
+ "find its binary in %s! You may wish to try a different\n"
+ "location to load the package from (go to Media menu) and see if that\n"
+ "makes a difference.\n\n"
+ "I suggest that we remove the version that was extracted since it does\n"
+ "not appear to be correct. Would you like me to do that now?",
+ browser, variable_get(VAR_BROWSER_BINARY)))
+ vsystem("pkg_delete %s %s", !strcmp(variable_get(VAR_CPIO_VERBOSITY), "high") ? "-v" : "", browser);
+ return DITEM_FAILURE;
+ }
+
+ /* Run browser on the appropriate doc */
+ if (dmenuOpenSimple(&MenuHTMLDoc, FALSE))
+ return DITEM_SUCCESS;
+ else
+ return DITEM_FAILURE;
+}
+
+/* Try to show one of the documents requested from the HTML doc menu */
+int
+docShowDocument(dialogMenuItem *self)
+{
+ char tmp[512], target[512];
+ char *where, *browser = variable_get(VAR_BROWSER_BINARY);
+ char *str = self->prompt;
+
+ if (!file_executable(browser)) {
+ msgConfirm("Can't find the browser in %s! Please ensure that it's\n"
+ "properly set in the Options editor.", browser);
+ return DITEM_FAILURE;
+ }
+ /* Default to Home */
+ where = strcpy(target, "http://www.freebsd.org");
+ if (strstr(str, "Other")) {
+ where = msgGetInput("http://www.freebsd.org", "Please enter the URL of the location you wish to visit.");
+ if (where)
+ strcpy(target, where);
+ }
+ else if (strstr(str, "FAQ")) {
+ where = strcpy(target, "/usr/share/doc/faq/index.html");
+ if (!file_readable(target))
+ where = strcpy(target, "http://www.freebsd.org/doc/en_US.ISO8859-1/books/faq");
+ }
+ else if (strstr(str, "Handbook")) {
+ where = strcpy(target, "/usr/share/doc/handbook/index.html");
+ if (!file_readable(target))
+ where = strcpy(target, "http://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook");
+ }
+ if (where) {
+ sprintf(tmp, "%s %s", browser, target);
+ systemExecute(tmp);
+ return DITEM_SUCCESS;
+ }
+ else {
+ msgConfirm("Hmmmmm! I can't seem to access the documentation you selected!\n"
+ "Have you loaded the base distribution? Is your network connected?");
+ return DITEM_FAILURE;
+ }
+}
diff --git a/usr.sbin/sysinstall/dos.c b/usr.sbin/sysinstall/dos.c
new file mode 100644
index 0000000..564e33f
--- /dev/null
+++ b/usr.sbin/sysinstall/dos.c
@@ -0,0 +1,94 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ * Copyright (c) 1995
+ * Gary J Palmer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sysinstall.h"
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <grp.h>
+#define MSDOSFS
+#include <sys/mount.h>
+#include <fs/msdosfs/msdosfsmount.h>
+#undef MSDOSFS
+
+static Boolean DOSMounted;
+static char mountpoint[] = "/dist";
+
+Boolean
+mediaInitDOS(Device *dev)
+{
+ struct msdosfs_args args;
+
+ if (DOSMounted)
+ return TRUE;
+
+ Mkdir(mountpoint);
+ memset(&args, 0, sizeof(args));
+ args.fspec = dev->devname;
+ args.uid = args.gid = 0;
+ args.mask = 0777;
+
+ if (mount("msdosfs", mountpoint, MNT_RDONLY, (caddr_t)&args) == -1) {
+ msgConfirm("Error mounting %s on %s: %s (%u)", args.fspec, mountpoint, strerror(errno), errno);
+ return FALSE;
+ }
+ DOSMounted = TRUE;
+ return TRUE;
+}
+
+FILE *
+mediaGetDOS(Device *dev, char *file, Boolean probe)
+{
+ return mediaGenericGet(mountpoint, file);
+}
+
+void
+mediaShutdownDOS(Device *dev)
+{
+ if (!DOSMounted)
+ return;
+ if (unmount(mountpoint, MNT_FORCE) != 0)
+ msgConfirm("Could not unmount the DOS partition from %s: %s",
+ mountpoint, strerror(errno));
+ else
+ DOSMounted = FALSE;
+ return;
+}
diff --git a/usr.sbin/sysinstall/floppy.c b/usr.sbin/sysinstall/floppy.c
new file mode 100644
index 0000000..0101bde
--- /dev/null
+++ b/usr.sbin/sysinstall/floppy.c
@@ -0,0 +1,158 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ * Copyright (c) 1995
+ * Gary J Palmer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/* These routines deal with getting things off of floppy media */
+
+#include "sysinstall.h"
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <grp.h>
+
+#define MSDOSFS
+#include <sys/mount.h>
+#include <fs/msdosfs/msdosfsmount.h>
+#undef MSDOSFS
+
+#include <ufs/ufs/ufsmount.h>
+static Boolean floppyMounted;
+
+char *distWanted;
+static char mountpoint[] = "/dist";
+
+Boolean
+mediaInitFloppy(Device *dev)
+{
+ struct msdosfs_args dosargs;
+ struct ufs_args u_args;
+ char *mp;
+
+ if (floppyMounted)
+ return TRUE;
+
+ mp = dev->private ? (char *)dev->private : mountpoint;
+ if (Mkdir(mp)) {
+ msgConfirm("Unable to make %s directory mountpoint for %s!", mp, dev->devname);
+ return FALSE;
+ }
+
+ msgDebug("Init floppy called for %s distribution.\n", distWanted ? distWanted : "some");
+
+ if (!variable_get(VAR_NONINTERACTIVE)) {
+ if (!distWanted)
+ msgConfirm("Please insert floppy in %s", dev->description);
+ else
+ msgConfirm("Please insert floppy containing %s in %s",
+ distWanted, dev->description);
+ }
+
+ memset(&dosargs, 0, sizeof dosargs);
+ dosargs.fspec = dev->devname;
+ dosargs.uid = dosargs.gid = 0;
+ dosargs.mask = 0777;
+
+ memset(&u_args, 0, sizeof(u_args));
+ u_args.fspec = dev->devname;
+
+ if (mount("msdosfs", mp, MNT_RDONLY, (caddr_t)&dosargs) != -1)
+ goto success;
+ if (mount("ufs", mp, MNT_RDONLY, (caddr_t)&u_args) != -1)
+ goto success;
+
+ msgConfirm("Error mounting floppy %s (%s) on %s : %s",
+ dev->name, dev->devname, mp, strerror(errno));
+ return FALSE;
+
+success:
+ floppyMounted = TRUE;
+ distWanted = NULL;
+ return TRUE;
+}
+
+FILE *
+mediaGetFloppy(Device *dev, char *file, Boolean probe)
+{
+ char buf[PATH_MAX], *mp;
+ FILE *fp;
+ int nretries = 5;
+
+ /*
+ * floppies don't use mediaGenericGet() because it's too expensive
+ * to speculatively open files on a floppy disk. Make user get it
+ * right or give up with floppies.
+ */
+ mp = dev->private ? (char *)dev->private : mountpoint;
+ snprintf(buf, PATH_MAX, "%s/%s", mp, file);
+ if (!file_readable(buf)) {
+ if (probe)
+ return NULL;
+ else {
+ while (!file_readable(buf)) {
+ if (!--nretries) {
+ msgConfirm("GetFloppy: Failed to get %s after retries;\ngiving up.", buf);
+ return NULL;
+ }
+ distWanted = buf;
+ mediaShutdownFloppy(dev);
+ if (!mediaInitFloppy(dev))
+ return NULL;
+ }
+ }
+ }
+ fp = fopen(buf, "r");
+ return fp;
+}
+
+void
+mediaShutdownFloppy(Device *dev)
+{
+ if (floppyMounted) {
+ char *mp = dev->private ? (char *)dev->private : mountpoint;
+
+ if (unmount(mp, MNT_FORCE) != 0)
+ msgDebug("Umount of floppy on %s failed: %s (%d)\n", mp, strerror(errno), errno);
+ else {
+ floppyMounted = FALSE;
+ if (!variable_get(VAR_NONINTERACTIVE) && variable_cmp(SYSTEM_STATE, "fixit"))
+ msgConfirm("You may remove the floppy from %s", dev->description);
+ }
+ }
+}
diff --git a/usr.sbin/sysinstall/ftp.c b/usr.sbin/sysinstall/ftp.c
new file mode 100644
index 0000000..14fd74b
--- /dev/null
+++ b/usr.sbin/sysinstall/ftp.c
@@ -0,0 +1,282 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sysinstall.h"
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <ftpio.h>
+
+Boolean ftpInitted = FALSE;
+static FILE *OpenConn;
+int FtpPort;
+
+/* List of sub directories to look for under a given FTP server. */
+const char *ftp_dirs[] = { ".", "releases/"MACHINE, "snapshots/"MACHINE,
+ "pub/FreeBSD", "pub/FreeBSD/releases/"MACHINE,
+ "pub/FreeBSD/snapshots/"MACHINE, NULL };
+
+/* Brings up attached network device, if any - takes FTP device as arg */
+static Boolean
+netUp(Device *dev)
+{
+ Device *netdev = (Device *)dev->private;
+
+ if (netdev)
+ return DEVICE_INIT(netdev);
+ else
+ return TRUE; /* No net == happy net */
+}
+
+/* Brings down attached network device, if any - takes FTP device as arg */
+static void
+netDown(Device *dev)
+{
+ Device *netdev = (Device *)dev->private;
+
+ if (netdev)
+ DEVICE_SHUTDOWN(netdev);
+}
+
+Boolean
+mediaInitFTP(Device *dev)
+{
+ int i, code, af, fdir;
+ char *cp, *rel, *hostname, *dir;
+ char *user, *login_name, password[80];
+
+ if (ftpInitted)
+ return TRUE;
+
+ if (OpenConn) {
+ fclose(OpenConn);
+ OpenConn = NULL;
+ }
+
+ /* If we can't initialize the network, bag it! */
+ if (!netUp(dev))
+ return FALSE;
+
+try:
+ cp = variable_get(VAR_FTP_PATH);
+ if (!cp) {
+ if (DITEM_STATUS(mediaSetFTP(NULL)) == DITEM_FAILURE || (cp = variable_get(VAR_FTP_PATH)) == NULL) {
+ msgConfirm("Unable to get proper FTP path. FTP media not initialized.");
+ netDown(dev);
+ return FALSE;
+ }
+ }
+
+ hostname = variable_get(VAR_FTP_HOST);
+ dir = variable_get(VAR_FTP_DIR);
+ if (!hostname || !dir) {
+ msgConfirm("Missing FTP host or directory specification. FTP media not initialized,");
+ netDown(dev);
+ return FALSE;
+ }
+ user = variable_get(VAR_FTP_USER);
+ login_name = (!user || !*user) ? "anonymous" : user;
+
+ if (variable_get(VAR_FTP_PASS))
+ SAFE_STRCPY(password, variable_get(VAR_FTP_PASS));
+ else if (RunningAsInit)
+ sprintf(password, "installer@%s", variable_get(VAR_HOSTNAME));
+ else {
+ struct passwd *pw;
+ char *user;
+
+ pw = getpwuid(getuid());
+ user = pw ? pw->pw_name : "ftp";
+ sprintf(password, "%s@%s", user, variable_get(VAR_HOSTNAME));
+ }
+ af = variable_cmp(VAR_IPV6_ENABLE, "YES") ? AF_INET : AF_UNSPEC;
+ msgNotify("Logging in to %s@%s..", login_name, hostname);
+ if ((OpenConn = ftpLoginAf(hostname, af, login_name, password, FtpPort, isDebug(), &code)) == NULL) {
+ msgConfirm("Couldn't open FTP connection to %s:\n %s.", hostname, ftpErrString(code));
+ goto punt;
+ }
+
+ ftpPassive(OpenConn, !strcmp(variable_get(VAR_FTP_STATE), "passive"));
+ ftpBinary(OpenConn);
+ if (dir && *dir != '\0') {
+ if ((i = ftpChdir(OpenConn, dir)) != 0) {
+ if (i == 550)
+ msgConfirm("No such directory ftp://%s/%s\n"
+ "please check your URL and try again.", hostname, dir);
+ else
+ msgConfirm("FTP chdir to ftp://%s/%s returned error status:\n %s.", hostname, dir, ftpErrString(i));
+ goto punt;
+ }
+ }
+
+ /*
+ * Now that we've verified that the path we're given is ok, let's try to
+ * be a bit intelligent in locating the release we are looking for. First
+ * off, if the release is specified as "__RELEASE" or "any", then just
+ * assume that the current directory is the one we want and give up.
+ */
+ rel = variable_get(VAR_RELNAME);
+ if (strcmp(rel, "__RELEASE") && strcmp(rel, "any")) {
+ /*
+ * Ok, since we have a release variable, let's walk through the list
+ * of directories looking for a release directory. The first one to
+ * match wins. For each case, we chdir to ftp_dirs[fdir] first. If
+ * that fails, we skip to the next one. Otherwise, we try to chdir to
+ * rel. If it succeeds we break out. If it fails, then we go back to
+ * the base directory and try again. Lots of chdirs, but oh well. :)
+ */
+ for (fdir = 0; ftp_dirs[fdir]; fdir++) {
+ /* Avoid sending CWD . commands which confuse some ftp servers */
+ if (strcmp(ftp_dirs[fdir], ".") &&
+ (ftpChdir(OpenConn, (char *)ftp_dirs[fdir]) != 0))
+ continue;
+ if (ftpChdir(OpenConn, rel) == 0) {
+ ftpInitted = TRUE;
+ return TRUE;
+ }
+ else /* reset to "root" dir for a fresh try */
+ ftpChdir(OpenConn, "/");
+ }
+
+ /*
+ * If we get here, then all of the directories we tried failed, so
+ * print out the error message and ask the user if they want to try
+ * again.
+ */
+ if (!msgYesNo("Warning: Can't find the `%s' distribution on this\n"
+ "FTP server. You may need to visit a different server for\n"
+ "the release you are trying to fetch or go to the Options\n"
+ "menu and to set the release name to explicitly match what's\n"
+ "available on %s (or set to \"any\").\n\n"
+ "Would you like to select another FTP server?",
+ rel, hostname)) {
+ variable_unset(VAR_FTP_PATH);
+ if (DITEM_STATUS(mediaSetFTP(NULL)) != DITEM_FAILURE)
+ goto try;
+ }
+ } else {
+ ftpInitted = TRUE;
+ return TRUE;
+ }
+
+punt:
+ ftpInitted = FALSE;
+ if (OpenConn != NULL) {
+ fclose(OpenConn);
+ OpenConn = NULL;
+ }
+ netDown(dev);
+ variable_unset(VAR_FTP_PATH);
+ return FALSE;
+}
+
+FILE *
+mediaGetFTP(Device *dev, char *file, Boolean probe)
+{
+ int nretries = 1;
+ FILE *fp;
+ char *try, buf[PATH_MAX];
+
+ if (!OpenConn) {
+ msgDebug("No FTP connection open, can't get file %s\n", file);
+ return NULL;
+ }
+
+ try = file;
+ while ((fp = ftpGet(OpenConn, try, 0)) == NULL) {
+ int ftperr = ftpErrno(OpenConn);
+
+ /* If a hard fail, try to "bounce" the ftp server to clear it */
+ if (ftperr != 550) {
+ if (ftperr != 421) /* Timeout? */
+ variable_unset(VAR_FTP_PATH);
+ /* If we can't re-initialize, just forget it */
+ DEVICE_SHUTDOWN(dev);
+ if (!DEVICE_INIT(dev)) {
+ netDown(dev);
+ if (OpenConn) {
+ fclose(OpenConn);
+ OpenConn = NULL;
+ }
+ variable_unset(VAR_FTP_PATH);
+ return NULL;
+ }
+ }
+ else if (probe)
+ return NULL;
+ else {
+ /* Try some alternatives */
+ switch (nretries++) {
+ case 1:
+ sprintf(buf, "releases/%s", file);
+ try = buf;
+ break;
+
+ case 2:
+ sprintf(buf, "%s/%s", variable_get(VAR_RELNAME), file);
+ try = buf;
+ break;
+
+ case 3:
+ sprintf(buf, "%s/releases/%s", variable_get(VAR_RELNAME), file);
+ try = buf;
+ break;
+
+ case 4:
+ try = file;
+ break;
+ }
+ }
+ }
+ return fp;
+}
+
+void
+mediaShutdownFTP(Device *dev)
+{
+ if (!ftpInitted)
+ return;
+
+ if (OpenConn != NULL) {
+ fclose(OpenConn);
+ OpenConn = NULL;
+ }
+ ftpInitted = FALSE;
+}
diff --git a/usr.sbin/sysinstall/globals.c b/usr.sbin/sysinstall/globals.c
new file mode 100644
index 0000000..9119c15
--- /dev/null
+++ b/usr.sbin/sysinstall/globals.c
@@ -0,0 +1,96 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sysinstall.h"
+
+/*
+ * Various global variables and an initialization hook to set them to
+ * whatever values we feel are appropriate.
+ */
+
+int DebugFD; /* Where diagnostic output goes */
+Boolean Fake; /* Only pretend to be useful */
+Boolean RunningAsInit; /* Are we running as init? */
+Boolean DialogActive; /* Is libdialog initialized? */
+Boolean ColorDisplay; /* Are we on a color display? */
+Boolean OnVTY; /* Are we on a VTY? */
+Boolean Restarting; /* Are we restarting sysinstall? */
+Boolean have_volumes; /* Media has more than one volume. */
+Variable *VarHead; /* The head of the variable chain */
+Device *mediaDevice; /* Where we're installing from */
+int BootMgr; /* Which boot manager we're using */
+int StatusLine; /* Where to stick our status messages */
+int low_volume; /* Lowest volume number */
+int high_volume; /* Highest volume number */
+jmp_buf BailOut; /* Beam me up, scotty! The natives are pissed! */
+
+Chunk *HomeChunk;
+Chunk *RootChunk;
+Chunk *SwapChunk;
+Chunk *TmpChunk;
+Chunk *UsrChunk;
+Chunk *VarChunk;
+#ifdef __ia64__
+Chunk *EfiChunk;
+#endif
+
+/*
+ * Yes, I know some of these are already automatically initialized as
+ * globals. I simply find it clearer to set everything explicitly.
+ */
+void
+globalsInit(void)
+{
+ DebugFD = -1;
+ ColorDisplay = FALSE;
+ Fake = FALSE;
+ Restarting = FALSE;
+ OnVTY = FALSE;
+ DialogActive = FALSE;
+ VarHead = NULL;
+ mediaDevice = NULL;
+ RunningAsInit = FALSE;
+
+ HomeChunk = NULL;
+ RootChunk = NULL;
+ SwapChunk = NULL;
+ TmpChunk = NULL;
+ UsrChunk = NULL;
+ VarChunk = NULL;
+#ifdef __ia64__
+ EfiChunk = NULL;
+#endif
+}
diff --git a/usr.sbin/sysinstall/help/anonftp.hlp b/usr.sbin/sysinstall/help/anonftp.hlp
new file mode 100644
index 0000000..a85b8aa
--- /dev/null
+++ b/usr.sbin/sysinstall/help/anonftp.hlp
@@ -0,0 +1,21 @@
+This screen allows you to configure the anonymous FTP user.
+
+The following configuration values are editable:
+
+UID: The user ID you wish to assign to the anonymous FTP user.
+ All files uploaded will be owned by this ID.
+
+Group: Which group you wish the anonymous FTP user to be in.
+
+Comment: String describing this user in /etc/passwd
+
+
+FTP Root Directory:
+
+ Where files available for anonymous FTP will be kept.
+
+Upload subdirectory:
+
+ Where files uploaded by anonymous FTP users will go.
+ If you do not wish to allow anonymous uploads, then this
+ field should be blank - note that this is not the default!
diff --git a/usr.sbin/sysinstall/help/configure.hlp b/usr.sbin/sysinstall/help/configure.hlp
new file mode 100644
index 0000000..65be877
--- /dev/null
+++ b/usr.sbin/sysinstall/help/configure.hlp
@@ -0,0 +1,10 @@
+This menu allows you to configure your system after the installation
+process is complete. At the minimum, you should probably set the
+system manager's password and the system time zone.
+
+For extra goodies like bash, emacs, Pascal, etc., you should look at
+the Packages item in this menu.
+
+For setting the timezone after the system is installed, type
+``tzsetup''. For more information on the overall general system
+configuration, see the ``/etc/defaults/rc.conf'' file.
diff --git a/usr.sbin/sysinstall/help/distributions.hlp b/usr.sbin/sysinstall/help/distributions.hlp
new file mode 100644
index 0000000..d6fefa6
--- /dev/null
+++ b/usr.sbin/sysinstall/help/distributions.hlp
@@ -0,0 +1,34 @@
+DISTRIBUTION INFORMATION
+------------------------
+
+An ``X-'' prefixed before a distribution set means that the Xorg
+base distribution, libraries, manual pages, servers and a set
+of default fonts will be selected in addition to the set itself.
+If you select such a set, you will also be presented with a set of
+menus for customizing the selections to your desired X Window System
+setup.
+
+Any distribution may be further customized by selecting the `Custom'
+item before leaving the menu.
+
+The current "canned" installations are provided:
+
+All: The base distribution, man pages, dictionary files,
+ profiling libraries, the FreeBSD compatibility libraries,
+ the complete source tree, games and your choice of Xorg
+ distribution components.
+
+Developer: Base distribution, man pages, dictionary files,
+ profiling libraries and the complete source tree.
+
+Kern-Developer: As Developer, but with only kernel sources instead of
+ the complete source tree.
+
+User: The base distribution, man pages and dictionary files.
+
+Minimal: Only the base distribution.
+
+Custom: Allows you to create or modify your distribution set on
+ a piece-by-piece basis.
+
+Reset: Clear all currently selected distributions.
diff --git a/usr.sbin/sysinstall/help/drives.hlp b/usr.sbin/sysinstall/help/drives.hlp
new file mode 100644
index 0000000..946a1b2
--- /dev/null
+++ b/usr.sbin/sysinstall/help/drives.hlp
@@ -0,0 +1,92 @@
+Boot Manager Selection:
+-----------------------
+
+If you wish to switch between multiple operating systems on your
+machine, or if you are trying to install FreeBSD on a drive other than
+your 1st drive, then you must install a boot manager. In the case
+where you wish to boot off an alternate drive, it should also be noted
+that you still need to install a boot manager on the FIRST drive!
+Even if you do not intend to create a FreeBSD partition on that drive
+(e.g. it's being wholly used by something else), the boot manager
+still needs to reside on the first disk in order to function as a
+"redirector" for the boot process.
+
+To do this, simply select your 1st drive in the drive selection menu
+and when the partition editor comes up, don't make any changes - just
+(Q)uit. At the boot manager menu which follows, select the first
+option (install a boot manager) and then proceed to setup the other
+drive(s) for FreeBSD as normal.
+
+It should also be noted that "operating systems" such as Windows 95
+will completely overwrite your boot manager without so much as a
+polite "may I please destroy your boot manager?" prompt if you make
+the mistake of installing them second. If this happens to you after
+FreeBSD is already installed, all is not lost! Simply revisit your
+FreeBSD distribution directory and look for a tools/ subdirectory, in
+which you'll find "bootinst.exe" and "boot.bin". To reinstall, simply
+say "bootinst boot.bin" while in the tools/ subdirectory.
+
+
+If you see the boot manager displaying ``F?'' when you try to come up
+for the first time and it refuses to change, no matter how often you
+whap on the function key assigned to FreeBSD, then you have a geometry
+mismatch problem and you should read the next section for important
+information on how to prevent that exact problem from happening!
+
+
+Geometry Translation / Sharing the disk(s) with another OS:
+----------------------------------------------------------
+
+If you are going to actually install some portion of FreeBSD on a
+drive then PLEASE BE VERY CERTAIN that the Geometry reported in the
+Partition Editor is the correct one for your drive and controller
+combination!
+
+IDE drives often have a certain geometry set during the PC BIOS setup,
+or (in the case of larger IDE drives) have their geometry "translated"
+by either the IDE controller or a special boot-sector translation
+utility such as that by OnTrack Systems. In these cases, knowing the
+correct geometry gets even more complicated as it's not something you
+can easily tell by looking at the drive or the PC BIOS setup. The
+best way of verifying that your geometry is being correctly calculated
+in such situations is to boot DOS (from the hard disk, not a floppy!)
+and run the ``pfdisk'' utility provided in the tools/ subdirectory of
+the FreeBSD CDROM or FTP site. It will report the geometry that DOS
+sees, which is generally the correct one.
+
+If you have no DOS partition sharing the disk at all, then you may
+find that you have better luck with Geometry detection if you create a
+very small DOS partition first, before installing FreeBSD. Once
+FreeBSD is installed you can always delete it again if you need the
+space.
+
+It's actually not a bad idea (believe it or not) to have a small
+bootable DOS partition on your FreeBSD machine anyway: Should the
+machine become unstable or exhibit strange behavior at some point in
+the future (which is not uncommon behavior for PC hardware!) you can
+then at least use DOS for installing and running one of the
+commercially available system diagnostic utilities.
+
+IMPORTANT NOTE:
+
+Any root partition you try to boot from must also reside below the
+1024th cylinder. If you're using a translated geometry then this is
+probably not a problem, but if you are using a native disk geometry
+which exceeds 1024 cylinders then you could have a failure to boot if
+you end up installing a root partition (or even just the kernel file
+in a root partition) out past cylinder 1024. If you are trying to
+share your first disk with FreeBSD and another OS which was installed
+previously, you are particularly susceptible to this problem and should
+check your disk addresses very carefully.
+
+If you find that you have insufficient space below cylinder 1024 to
+make a root partition for FreeBSD (and again, this ONLY applies to the
+root partition - once FreeBSD's kernel is loaded, it doesn't care
+about the geometry issues) then you will probably need to install on a
+completely different disk (see the boot manager section above) or
+resize your existing partitions so that both operating systems can
+have boot partitions below cylinder 1024.
+
+You may blame IBM for the limitations of a 10 bit cylinder address.
+"No one will have a disk with more than 1024 cylinders." I'm sure
+someone said.
diff --git a/usr.sbin/sysinstall/help/fixit.hlp b/usr.sbin/sysinstall/help/fixit.hlp
new file mode 100644
index 0000000..801f258
--- /dev/null
+++ b/usr.sbin/sysinstall/help/fixit.hlp
@@ -0,0 +1,3 @@
+A special shell will be launched by this option with a fixit floppy
+or a FreeBSD CD-ROM (or DVD-ROM) mounted as /mnt2. This provides
+access to extra commands under /mnt2.
diff --git a/usr.sbin/sysinstall/help/html.hlp b/usr.sbin/sysinstall/help/html.hlp
new file mode 100644
index 0000000..c12f82c
--- /dev/null
+++ b/usr.sbin/sysinstall/help/html.hlp
@@ -0,0 +1,20 @@
+In this screen, you can jump to remote or local HTML
+resources such as the FreeBSD Handbook & FreeBSD FAQ
+(Frequently Asked Questions) documents located in:
+
+ file:/usr/share/doc/
+
+if you've loaded the doc distribution.
+
+The default browser package used is links (a text based
+browser which can render tables), which will be
+automatically loaded from the installation media if it
+is available. You may change the selection of browser
+& browser package to auto-load by visiting the Options
+editor.
+
+In order to visit remote URLs, you naturally must have
+some sort of working Internet connection. If you have not
+yet brought up any network interfaces, please visit
+the ``Networking'' item in the Configuration menu
+before attempting to reference any http://.. style URLs.
diff --git a/usr.sbin/sysinstall/help/media.hlp b/usr.sbin/sysinstall/help/media.hlp
new file mode 100644
index 0000000..414a2aa
--- /dev/null
+++ b/usr.sbin/sysinstall/help/media.hlp
@@ -0,0 +1,54 @@
+You can install from the following types of media:
+
+ CDROM requires one of the following supported CDROM drives:
+ ATAPI - Any standard ATAPI CDROM drive hooked to
+ a supported controller (see Hardware Guide).
+ SCSI - Any standard SCSI CDROM drive hooked to
+ a supported controller (see Hardware Guide).
+
+
+ DOS A DOS primary partition with the required FreeBSD
+ distribution files copied onto it (e.g. C:\FREEBSD\)
+
+
+ FS Assuming a disk or partition with an existing
+ FreeBSD file system and distribution set on it,
+ get the distribution files from there.
+
+
+ Floppy Get distribution files from one or more DOS or UFS
+ formatted floppies. Such floppies are assumed to
+ contain the appropriate distribution pieces - see
+ ABOUT.TXT for more information about making floppy
+ distribution media.
+
+
+ FTP Get the distribution files from an anonymous ftp server
+ (you will be presented with a list). Please note that
+ you may invoke FTP in "Active" mode, "Passive" mode, or
+ via an HTTP proxy.
+
+ Active mode is the standard way of fetching files and
+ Passive mode is for use when you're behind a firewall or
+ some other security mechanism that blocks active FTP
+ connections. Using an HTTP proxy is sometimes necessary
+ for firewalls which block all FTP connections.
+
+ If you chose to enter your own URL in the FTP menu, please
+ note that all paths are *relative* to the home directory
+ of the user being logged in as. By default, this is the
+ user "ftp" (anonymous ftp) but you may change this in the
+ Options screen.
+
+
+ NFS Get the distribution files from an NFS server somewhere
+ (make sure that permissions on the server allow this!).
+ If this install method hangs on you or refuses to work
+ properly, you may need to set some special options for
+ your NFS server. See the Options screen for more details.
+
+
+ Tape Extract distribution files from tape into a temporary
+ directory and install from there. If the tape was created
+ with blocksize other than 20, you may wish to change this
+ in the Options screen.
diff --git a/usr.sbin/sysinstall/help/network_device.hlp b/usr.sbin/sysinstall/help/network_device.hlp
new file mode 100644
index 0000000..affa86a
--- /dev/null
+++ b/usr.sbin/sysinstall/help/network_device.hlp
@@ -0,0 +1,58 @@
+You can do network installations over 3 types of communications links:
+
+ Serial port: SLIP / PPP
+ Parallel port: PLIP (laplink cable)
+ Ethernet: A standard Ethernet controller (includes some
+ PCMCIA networking cards).
+
+SLIP support is rather primitive and limited primarily to directly
+connected links, such as a serial cable running between a laptop
+computer and another PC. The link must be hard-wired as the SLIP
+installation doesn't currently offer a dialing capability (that
+facility is offered by the PPP utility, which should be used in
+preference to SLIP whenever possible). When you choose the SLIP
+option, you'll be given the option of later editing the slattach
+command before it's run on the serial line. It is expected that
+you'll run slattach (or some equivalent command) on the other end of
+the link at that time and bring up the line. FreeBSD will then
+install itself at serial speeds of up to 115.2K/baud (the recommended
+speed for a hardwired cable).
+
+If you're using a modem then PPP is almost certainly your only choice.
+Make sure that you have your service provider's information handy as
+you'll need to know it fairly early in the installation process. You
+will need to know your service provider's IP address, the IP address
+of your provider's DNS server, and possibly your own IP address unless
+your ISP supports dynamic negotiation, most do. If you do not choose
+a PAP or CHAP login you will also need to know how to use the various
+"AT commands" to dial the ISP with your particular brand of modem as
+the PPP dialer provides only a very simple terminal emulator and has no
+"modem capabilities database". If you choose a PAP or CHAP login you
+can simply enter `dial' (without the quotes) at the ppp prompt if your
+modem uses the Hayes compatible AT command set.
+
+If a hard-wired connection to another FreeBSD (2.0R or later) machine
+is available, you might also consider installing over a "laplink"
+parallel port cable. The data rate over the parallel port is much
+higher than what is typically possible over a serial line, and speeds
+of over 50KB/sec are not uncommon.
+
+Finally, for the fastest possible network installation, an Ethernet
+adaptor is always a good choice! FreeBSD supports most common PC
+Ethernet cards, a table of which is provided in the FreeBSD Hardware
+Guide (see the `Documentation' entry in the main menu). If you are
+using one of the supported PCMCIA Ethernet cards, also be sure that
+it's plugged in BEFORE the laptop is powered on! Sysinstall does not,
+unfortunately, currently support "hot insertion" of PCMCIA cards.
+
+You will also need to know your IP address on the network, the
+"netmask" value for your address class, and the name of your machine.
+Your system administrator can tell you which values to use for your
+particular network setup. If you will be referring to other hosts by
+name rather than IP address, you'll also need a name server and
+possibly the address of a gateway (if you're using PPP, it's your
+provider's IP address) to use in talking to it. If you do not know
+the answers to all or most of these questions then you should really
+probably talk to your system administrator FIRST before trying this
+type of installation! Choosing the wrong IP address on a busy network
+will NOT make you popular with your systems administrator! :-)
diff --git a/usr.sbin/sysinstall/help/options.hlp b/usr.sbin/sysinstall/help/options.hlp
new file mode 100644
index 0000000..f115577
--- /dev/null
+++ b/usr.sbin/sysinstall/help/options.hlp
@@ -0,0 +1,181 @@
+The following options may be set from this screen. Use the SPACE key
+to toggle an option's value, Q to leave when you're done.
+
+NFS Secure: NFS server talks only on a secure port
+
+ This is most commonly used when talking to Sun workstations, which
+ will not talk NFS over "non privileged" ports.
+
+
+NFS Slow: User is using a slow PC or Ethernet card
+
+ Use this option if you have a slow PC (386) or an Ethernet card
+ with poor performance being "fed" by NFS on a higher-performance
+ workstation. This will throttle the workstation back to prevent
+ the PC from becoming swamped with data.
+
+
+NFS TCP: Use TCP for the NFS mount
+
+ This option can be used if your NFS server supports TCP
+ connections; not all do! This may be useful if your NFS server
+ is at a remote site in which case it may offer some additional
+ stability.
+
+
+NFS version 3: Use NFS version 3
+
+ This option forces the use of NFS version 3 and is on by default.
+ If your NFS server only supports NFS version 2, disable this option.
+
+
+Debugging: Turn on the extra debugging flag
+
+ This turns on a lot of extra noise over on the second screen
+ (ALT-F2 to see it, ALT-F1 to switch back). If your installation
+ should fail for any reason, PLEASE turn this flag on when
+ attempting to reproduce the problem. It will provide a lot of
+ extra debugging at the failure point and may be very helpful to
+ the developers in tracking such problems down!
+
+
+No Warnings: Disable some warnings
+
+ This flag tells sysinstall, and particularly the disk editing
+ routines, that you consider yourself to know what you are
+ doing and disables various warning. It is not recommended that
+ you enable this option.
+
+
+Yes To All: Assume "Yes" answers to all non-critical dialogs
+
+ This flag should be used with caution. It will essentially
+ decide NOT to ask the user about any "boundary" conditions that
+ might not constitute actual errors but may be warnings indicative
+ of other problems. It's most useful to those who are doing unattended
+ installs.
+
+
+DHCP: Enable DHCP configuration of interfaces
+
+ This option specifies whether DHCP configuration of interfaces
+ may be attempted. The default setting is to interactively ask
+ the user.
+
+
+IPv6: Enable IPv6 router solicitation configuration
+
+ This option specifies whether automatic configuration of IPv6
+ interfaces may be attempted. This uses the router solicitation
+ method of automatic configuration. The default setting is to
+ interactively ask the user.
+
+
+FTP username: Specify username and password instead of anonymous.
+
+ By default, the installation attempts to log in as the
+ anonymous user. If you wish to log in as someone else,
+ specify the username and password with this option.
+
+
+Editor: Specify which screen editor to use.
+
+ At various points during the installation it may be necessary
+ to customize some text file, at which point the user will be
+ thrown unceremoniously into a screen editor. A relatively
+ simplistic editor which shows its command set on-screen is
+ selected by default, but UNIX purists may wish to change this
+ setting to `/usr/bin/vi'.
+
+
+Tape Blocksize: Specify block size in 512 byte blocks of tape.
+
+ This defaults to 20 blocks, which should work with most
+ tape drive + tar combinations. It may not allow your particular
+ drive to win any records for speed, however, and the more
+ adventurous among you might try experimenting with larger sizes.
+
+
+Extract Detail: How to show filenames on debug screen as they're extracted.
+
+ While a distribution is being extracted, the default detail level
+ of "high" will show the full file names as they're extracted.
+ If you would prefer a more terse form for this, namely dots, select
+ the "medium" detail level. If you want nothing to be printed
+ on the debugging screen during extraction, select "low".
+
+
+Release Name: Which release to attempt to load from installation media.
+
+ You should only change this option if you're really sure you know
+ what you are doing! This will change the release name used by
+ sysinstall when fetching components of any distributions, and
+ is a useful way of using a more recent installation boot floppy
+ with an older release (say, on CDROM).
+
+
+Install Root: Specify some directory other than / as your "root".
+
+ This should be left as / unless you have a really good reason to
+ change it. One good reason might be if you were installing to a
+ disk other than your own, as might happen if you needed to prepare a
+ disk for another machine which couldn't load FreeBSD directly
+ for some reason.
+
+ Note: If you set this option, you will only be able to install
+ packages if the base distribution is also installed (usually
+ the case anyway) since /usr/sbin/pkg_add will otherwise not be
+ found after the chroot() call.
+
+
+Browser Package: Which package to load for an HTML browser.
+
+ By default, this is set to links but may also be set to any other
+ text capable HTML browser for which a package exists. If you set this
+ to an X based browser, you will not be able to use it if you're running
+ in text mode! :)
+
+
+Browser Exec: Which binary to run for the HTML browser.
+
+ The full pathname to the main executable in Browser Package.
+
+
+Media Type: Which media type is being used.
+
+ This is mostly informational and indicates which media type (if any)
+ was last selected in the Media menu. It's also a convenient short-cut
+ to the media menu itself.
+
+
+Package Temp: Where package temporary files should go
+
+ Some packages, like emacs, can use a LOT of temporary space - up to
+ 20 or 30MB. If you are going to configure a small / directory and no
+ seperate /var (and hence a small /var/tmp), then you may wish to set
+ this to point at another location (say, /usr/tmp).
+
+
+Newfs Args: Specify default arguments to newfs(8)
+
+ The default parameters used to build new filesystems.
+ If you will be running a service that creates millions of small
+ files or need to specify different default parameters for any
+ other reason, you may do so here.
+
+
+Fixit Console: The location of the fixit console
+
+ Specifies where sysinstall should start the fixit shell for
+ interactive repair. Valid arguments are "serial" for a serial
+ port, or "standard" for VTY4.
+
+
+Re-scan Devices:
+
+ Reprobe the system for devices.
+
+
+Use Defaults: Use default values.
+
+ Reset all options back to their default values.
diff --git a/usr.sbin/sysinstall/help/partition.hlp b/usr.sbin/sysinstall/help/partition.hlp
new file mode 100644
index 0000000..3d13b34
--- /dev/null
+++ b/usr.sbin/sysinstall/help/partition.hlp
@@ -0,0 +1,169 @@
+This is the FreeBSD DiskLabel Editor.
+
+NOTE: If you're entering this editor from the update procedure then
+you probably shouldn't (C)reate anything at all but rather use only
+the (M)ount command to check and mount existing partitions for
+upgrading.
+
+If you would like the label editor to do most of the following for
+you, simply type `A' for automatic partitioning of the disk.
+
+If you wish to create partitions manually you may do so by moving the
+highlighted selection bar with the arrow keys over the FreeBSD
+partition(s) displayed at the top of the screen. Typing (C)reate
+while a partition with available free space is selected will allow you
+to create a BSD partition inside of it using some or all of its
+available space.
+
+Typing (M)ount over an existing partition entry (displayed in the
+middle of the screen) will allow you to set a mount point for it
+without initializing it. If you want it initialized, use the (T)oggle
+command to flip the Newfs flag. When Newfs is set to "Y", the
+filesystem in question will be ERASED and rebuilt from scratch!
+
+
+You should use this editor to create at least the following
+filesystems:
+
+ Name Purpose Min Size? Optional?
+ ---- ------- --------- ---------
+ / Root filesystem 118MB No
+ swap Swap space 2 * MEM No
+ /usr System & user files 128MB or more Yes
+
+Note: If you do not create a /usr filesystem then your / filesystem
+will need to be bigger - at least 240MB. This is not recommended as
+any media errors that may occur during disk I/O to user files will
+corrupt the filesystem containing vital system files as well. It is
+for this reason that / is generally kept on its own filesystem, where
+it should be considered essentially "read only" in your administration
+of it.
+
+Swap space is a little tricker, and the rule of "2 * MEM" is simply a
+best-guess approximation and not necessarily accurate for your
+intended usage of the system. If you intend to use the system heavily
+in a server or multi-user application, you may be well advised to
+increase this size. You may also create swap space on multiple drives
+for a larger "total" swap and this is, in fact, recommended if you
+have multiple, fast drives for which such load-balancing can only help
+overall I/O performance.
+
+The /usr filesystem should be sized according to what kind of
+distributions you're trying to load and how many packages you intend
+to install in locations like /usr/local. You can also make /usr/local
+a separate filesystem if you don't want to risk filling up your /usr
+by mistake.
+
+Another useful filesystem to create is /var, which contains mail, news
+printer spool files and other temporary items. It is a popular
+candidate for a separate partition and should be sized according to
+your estimates of the amount of mail, news or spooled print jobs that
+may be stored there.
+
+WARNING: If you do not create a separate filesystem for /var, space
+for such files will be allocated out of the root (/) filesystem
+instead. You may therefore wish to make the / partition bigger if you
+expect a lot of mail or news and do not want to make /var its own
+partition.
+
+If you're new to this installation, you might also want to read the
+following explanation of how FreeBSD's new "slice" paradigm for
+looking at disk storage works:
+
+
+In FreeBSD's new system, a device name can be broken up into up to 3
+parts. Take a typical name like ``/dev/da0s1a'':
+
+ The first three characters represent the drive name. If we had
+ a system with two SCSI drives on it then we'd see /dev/da0 and
+ /dev/da1 as the device entries representing the entire drives.
+
+ Next you have the "slice" (or "FDISK Partition") number,
+ as seen in the Partition Editor. Assuming that our da0
+ contained two slices, a FreeBSD slice and a DOS slice, that
+ would give us /dev/da0s1 and /dev/da0s2 as device entries pointing
+ to the entire slices.
+
+ Next, if a slice is a FreeBSD slice, you can have a number of
+ (confusingly named) "partitions" inside of it.
+
+ These partitions are where various filesystems or swap areas live,
+ and using our hypothetical two-SCSI-disk machine again, we might
+ have something like the following layout on da0:
+
+ Name Mountpoint
+ ---- ----------
+ da0s1a /
+ da0s1b <swap space>
+ da0s1e /usr
+
+Once you understand all this, then the purpose of the label editor
+becomes fairly clear: You're carving up the FreeBSD slices displayed
+at the top of the screen into smaller pieces, which are displayed in
+the middle of the screen, and then assigning FreeBSD file system names
+(mount points) to them.
+
+You can also use the label editor to mount existing partitions/slices
+into your filesystem hierarchy, as is frequently done for DOS FAT
+slices. For FreeBSD partitions, you can also toggle the "newfs" state
+so that the partitions are either (re)created from scratch or simply
+checked and mounted (the contents are preserved).
+
+If you set (S)oftUpdates on a filesystem, it will cause the
+"Soft Updates" policy to be in effect for it. This basically causes
+both metadata and data blocks to be written asynchronously to disk,
+but with extra state information which causes the metadata and any
+related data blocks to be committed in a single transaction. This
+results in async metadata update speeds (which are considerably
+faster than the default sync) without the potential for data loss
+which could occur if you simply mounted the filesystem with purely
+"async" update policy and then had a power failure. If you wish
+to later turn the softupdates policy back off, use the command
+"tunefs -n disable devicename". NOTE: It is probably not wise
+to use this on your root filesystem unless you have a large
+(e.g. non-standard size) root. The reason is that smaller filesystems
+with significant activity can temporarily overflow if the soft updates
+policy results in free'd blocks not being "garbage collected" as fast
+as they're being requested.
+
+The UNIX File System (UFS) on FreeBSD supports two different on-disk
+layouts: UFS1 and UFS2. UFS1 was the default file system in use
+through FreeBSD 5.0-RELEASE; as of FreeBSD 5.1-RELEASE, the default
+is now UFS2, with the exception of the PC98 platform. UFS2 provides
+sparse inode allocation (faster fsck), 64-bit storage pointers (larger
+maximum size), and native extended attributes (required for ACLs, MAC,
+and other advanced security and file system services). The selection
+of UFS1 or UFS2 must be made when the file system is created--later
+conversion is not currently possible. UFS2 is the recommended file
+system, but if disks are to be used on older FreeBSD systems, UFS1
+improves portability. When dual-booting between FreeBSD 4.x or
+earlier and FreeBSD 5.x, UFS1 file systems will be accessible from
+both. To toggle a file system to UFS1, press '1'. To restore it to
+UFS2, press '2'.
+
+WARNING: FreeBSD on i386 is currently unable to boot from root file
+systems larger than 1.5TB.
+
+To add additional flags to the newfs command line for UFS file
+systems, press 'N'. These options will be specified before the
+device argument of the command line, but after any other options
+placed there by sysinstall, such as the UFS version and soft
+updates flag; as such, arguments provided may override existing
+settings. To completely replace the newfs command used by
+sysinstall, press 'Z' to convert a partition to a Custom
+partition type. Sysinstall will prompt you with the newfs
+command line that it would have used based on existing settings
+prior to the change, but allow you to modify any aspect of the
+command line. Once a partition has been converted to a custom
+partition in the label editor, you will need to restart the
+labeling process or delete and recreate the partition to restore
+it to a non-custom state. Custom partitions are represented by
+the letters "CST" instead of "UFS" or "FAT.
+
+When you're done, type `Q' to exit.
+
+No actual changes will be made to the disk until you (C)ommit from the
+Install menu or (W)rite directly from this one. You're working with
+what is essentially a copy of the disk label(s), both here and in the
+FDISK Partition Editor, and the actual on-disk labels won't be
+affected by any changes you make until you explicitly say so.
diff --git a/usr.sbin/sysinstall/help/securelevel.hlp b/usr.sbin/sysinstall/help/securelevel.hlp
new file mode 100644
index 0000000..27eb1ec
--- /dev/null
+++ b/usr.sbin/sysinstall/help/securelevel.hlp
@@ -0,0 +1,40 @@
+This menu allows you to configure the Securelevel mechanism in FreeBSD.
+
+Securelevels may be used to limit the privileges assigned to the
+root user in multi-user mode, which in turn may limit the effects of
+a root compromise, at the cost of reducing administrative functions.
+Refer to the security(7) and init(8) manual pages for complete details.
+
+ -1 Permanently insecure mode - always run the system in level 0
+ mode. This is the default initial value.
+
+ 0 Insecure mode - immutable and append-only flags may be turned
+ off. All devices may be read or written subject to their
+ permissions.
+
+ 1 Secure mode - the system immutable and system append-only
+ flags may not be turned off; disks for mounted file systems,
+ /dev/mem, /dev/kmem and /dev/io (if your platform has it)
+ may not be opened for writing; kernel modules (see kld(4))
+ may not be loaded or unloaded.
+
+ 2 Highly secure mode - same as secure mode, plus disks may not
+ be opened for writing (except by mount(2)) whether mounted or
+ not. This level precludes tampering with file systems by
+ unmounting them, but also inhibits running newfs(8) while the
+ system is multi-user.
+
+ In addition, kernel time changes are restricted to less than
+ or equal to one second. Attempts to change the time by more
+ than this will log the message ``Time adjustment clamped to +1
+ second''.
+
+ 3 Network secure mode - same as highly secure mode, plus IP
+ packet filter rules (see ipfw(8), ipfirewall(4) and pfctl(8))
+ cannot be changed and dummynet(4) or pf(4) configuration
+ cannot be adjusted.
+
+Securelevels must be used in combination with careful system design and
+application of protective mechanisms to prevent system configuration
+files from being modified in a way that compromises the protections of
+the securelevel variable upon reboot.
diff --git a/usr.sbin/sysinstall/help/shortcuts.hlp b/usr.sbin/sysinstall/help/shortcuts.hlp
new file mode 100644
index 0000000..553e96f
--- /dev/null
+++ b/usr.sbin/sysinstall/help/shortcuts.hlp
@@ -0,0 +1,117 @@
+sysinstall now supports command-line "shortcuts" which can
+often replace outdated commands, like pkg_manage. Multiple commands
+can be invoked in sequence, and variables may be set on-the-fly to
+customize the installation program's behavior in various ways.
+
+Syntax:
+
+/usr/sbin/sysinstall [var=value ..] [command ..]
+
+Where "var" can be one or more of:
+
+blanktime Screen blank time setting in seconds
+bootManager Select boot manager: booteasy, standard or none
+browserBinary Which doc browser to use (default: links)
+browserPackage Which package to get browser from (default: links)
+cpioVerbose How verbose to be with cpio: high, medium or low
+debug Extra debugging?
+defaultrouter IP address of default route
+disk Which disk to operate on (ad0, da0, etc).
+domainname Domain name
+editor Which screen editor to use
+ftp Which FTP site/dir to use (URL ftp://site/dir/..)
+ftpDirectory Root of the FreeBSD distribution tree on FTP server
+ftpHost Which FTP hostname to use (overrides ftp variable)
+ftpOnError Set to retry or abort
+ftpPass Which password to use when logging into FTP server
+ftpPort Which FTP port to use (default: 21)
+ftpRetryCount How many times to retry a fetch operation
+ftpUser Which username to use when logging into FTP server
+gated Use gated instead of routed
+geometry Geometry to use for selected disk ("cyl/hd/sec")
+hostname Fully qualified domain name for host.
+ifconfig_<iface> For each <iface> in network_devices
+ipaddr IP address for this host's primary interface
+nameserver IP address of name server
+netmask Netmask for this host's primary interface
+network_interfaces Which network interfaces to configure
+nfs Full host:/path/ specification to NFS media
+nfsHost Host portion of nfs path
+nfsSecure Use NFS secure mount (-P flag)
+nfs_server Configure this machine as an NFS server
+noConfirm Don't ask for confirmation on non-fatal errors
+ntpDate Which ntp clock synchronization server to use
+pcnfsd Install the PCNFSD package
+ports Path to the ports collection
+releaseName Which FreeBSD release to install
+rootSize Size of the root partition to create for Auto
+routedflags Which flags to pass to routed, if enabled
+serialSpeed How fast to run a SLIP/PPP connection
+slowEthernetCard PC ethernet card is uncommonly slow
+swapSize Size of the swap partition to create for Auto
+tapeBlocksize Tape size in blocks
+ufs Full path to UFS media directory
+usrSize Size of the /usr partition to create for Auto
+varSize Size of the /var partition to create for Auto
+
+And "command" can be one or more of:
+
+addGroup Add a new group to the system
+addUser Add a new user to the system
+configAnonFTP Configure system for anonymous FTP
+configInetd Configure the inetd super-server
+configNFSServer Configure host as an NFS server
+configNTP Configure host as an NTP client
+configPackages Browse / install packages
+configRouter Configure a routing daemon
+configUsers Add users and/or groups to the system
+diskLabelCommit Write out any changed label information
+diskLabelEditor Label/Newfs/Mount new or existing filesystems
+diskPartitionEditor Partition a new or existing disk
+diskPartitionWrite Write out any changed partition information
+distExtractAll Extract all selected distributions
+distReset Reset distribution information
+distSetDeveloper Select developer distribution
+distSetEverything Select all distributions
+distSetKernDeveloper Select kernel developer distribution
+distSetMinimum Select minimal distribution
+distSetSrc Select source sub-distributions
+distSetUser Select user distribution
+distSetXDeveloper Select Xorg developer distribution
+distSetXOrg Select Xorg sub-distributions
+distSetXUser Select Xorg user distribution
+docBrowser Browse documentation
+installCommit Commit any pending installation operations
+installExpress Express installation
+installStandard Standard installation
+installUpgrade Upgrade installation
+mediaGetType Prompt for media type
+mediaSetCDROM Select CDROM media
+mediaSetCPIOVerbosity Prompt for CPIO verbosity
+mediaSetDOS Select DOS media
+mediaSetFTP Select FTP media
+mediaSetFTPPassive Select FTP media in passive mode
+mediaSetFTPUserPass Prompt for FTP username and password
+mediaSetFloppy Select floppy media
+mediaSetHTTP Select FTP media via HTTP proxy
+mediaSetNFS Select NFS media
+mediaSetTape Select tape media
+mediaSetUFS Select UFS media
+optionsEditor Go to options editor
+tcpMenuSelect Configure TCP/IP networking
+
+Examples:
+
+/usr/sbin/sysinstall mediaSetFTP configPackages
+
+Selects an FTP site and then goes to the package configuration menu.
+
+
+/usr/sbin/sysinstall disk=da0 diskPartitionEditor
+
+Invokes the disk partition editor on disk da0.
+
+
+If /usr/sbin/sysinstall is linked to another filename, say
+`/usr/local/bin/configPackages', then the basename will be used
+as an implicit command name.
diff --git a/usr.sbin/sysinstall/help/slice.hlp b/usr.sbin/sysinstall/help/slice.hlp
new file mode 100644
index 0000000..e9f3abb
--- /dev/null
+++ b/usr.sbin/sysinstall/help/slice.hlp
@@ -0,0 +1,57 @@
+This is the Main Slice (``FDISK'' or PC-style Partition) Editor.
+
+Possible commands are printed at the bottom and the Master Boot Record
+contents are shown at the top. You can move up and down with the
+arrow keys and (C)reate a new slice whenever the highlighted
+selection bar is over a slice whose type is marked as "unused."
+
+You are expected to leave this screen with at least one slice
+marked "FreeBSD." Note that unlike Linux, you don't need to create
+multiple FreeBSD FDISK partition entries for different things like
+swap, file systems, etc. The usual convention is to create ONE
+FreeBSD slice (FDISK partition) per drive and then subsection this slice
+into swap and file systems with the Label editor.
+
+No actual changes will be made to the disk until you (C)ommit from the
+Install menu or use the (W)rite option here! You're working with what
+is essentially a copy of the disk label(s), both here and in the Label
+Editor.
+
+If you want to use the entire disk for FreeBSD, type `A'. Slices will
+be aligned to fictitious cylinder boundaries and space will be reserved
+in front of the FreeBSD slice for a [future] possible boot manager.
+
+For the truly dedicated disk case, type `F'. You'll be asked whether or
+not you wish to keep the disk (potentially) compatible with other
+operating systems, i.e. the information in the FDISK table should be
+kept valid. A truly dedicated disk can be achieved by selecting `No'.
+In that case, all BIOS geometry considerations will no longer be in
+effect and you can safely ignore any ``The detected geometry is
+invalid'' warning messages you may later see. It is also not necessary
+in this case to set a slice bootable or install an MBR boot manager as
+both things are then irrelevant. The FreeBSD slice will start at
+absolute sector 0 of the disk (so that FreeBSD's disk label is identical
+to the Master Boot Record) and extend to the very last sector of the
+disk medium. Needless to say, such a disk cannot have any sort of a
+boot manager, `disk manager', or anything else that has to interact with
+the BIOS. This option is therefore only considered safe for SCSI disks
+and most IDE disks and is primarily intended for people who are going to
+set up a dedicated FreeBSD server or workstation, not a typical `home PC'.
+
+If you select the default of `Yes' at the compatibility, slices will be
+aligned to fictitious cylinder boundaries and space will be reserved
+in front of the FreeBSD slice for a [future] possible boot manager.
+This is pretty much equivalent to having chosen `A' originally.
+
+The flags field has the following legend:
+
+ '=' -- This slice is properly aligned.
+ 'A' -- This slice is marked active.
+ 'R' -- This slice contains the root (/) filesystem
+
+If no slice is marked Active, you will need to either install
+a Boot Manager (the option for which will be presented later in the
+installation) or set one Active before leaving this screen.
+
+To leave the slice editor, type `Q'.
+
diff --git a/usr.sbin/sysinstall/help/tcp.hlp b/usr.sbin/sysinstall/help/tcp.hlp
new file mode 100644
index 0000000..4c86198
--- /dev/null
+++ b/usr.sbin/sysinstall/help/tcp.hlp
@@ -0,0 +1,42 @@
+This screen allows you to set up your general network parameters
+(hostname, domain name, DNS server, etc) as well as the settings for a
+given interface (which was selected from the menu before this screen).
+
+PLIP/SLIP users - please read through to the end of this doc!
+
+You can move through the fields with the TAB, BACK-TAB and ENTER
+keys. To edit a field, use DELETE or BACKSPACE. You may also use ^A
+(control-A) to go to the beginning of the line, ^E (control-E) to go
+to the end, ^F (control-F) to go forward a character, ^B (control-B)
+to go backward one character, ^D (control-D) to delete the character
+under the cursor and ^K (control-K) to delete to the end of the line.
+Basically, the standard EMACS motion sequences.
+
+The "Extra options to ifconfig" field is kind of special (read: a
+hack :-):
+
+Any valid options to ifconfig can be specified here, so if you need
+to do something "special" to get your interface working, then here
+is the place to do it.
+
+If you're running SLIP or PLIP, you also need to use it for specifying
+the remote end of the link (simply type the foreign IP address in).
+In the specific case where you're running PLIP with a Linux host peer
+rather than a FreeBSD one, you also must add the "-link0" flag after the
+foreign address.
+
+If you're dealing with an ethernet adaptor with multiple media
+connectors (e.g. AUI, 10BT, 10B2, etc), you can use this field to
+specify which one to use. Examples of valid strings include:
+
+ "media 10base5/AUI" - Select the AUI port.
+ "media 10baseT/UTP" - Select the twisted pair port.
+ "media 10base2/BNC" - Select the BNC connector.
+ "media 100baseTX" - Select 100BaseT on a 100/10 dual adaptor.
+
+If you have a wireless interface and must specify arguments such as a
+WEP key here, you may use something like:
+
+ "wepmode on wepkey 0xFEEDFACE"
+
+When you're done with this form, select OK.
diff --git a/usr.sbin/sysinstall/help/usage.hlp b/usr.sbin/sysinstall/help/usage.hlp
new file mode 100644
index 0000000..e67d548
--- /dev/null
+++ b/usr.sbin/sysinstall/help/usage.hlp
@@ -0,0 +1,65 @@
+HOW TO USE THIS SYSTEM
+======================
+
+[press the PageDown key to go to the next screen when you finish
+ reading this one]
+
+The following keys are recognized in most of the dialogs you'll
+encounter during this installation:
+
+KEY ACTION
+--- ------
+SPACE Select or toggle the current item.
+ENTER Finish with a menu or item.
+UP ARROW Move to previous item (or up, in a text display box).
+DOWN ARROW Move to next item (or down, in a text display box).
+TAB Move to next item or group.
+RIGHT ARROW Move to next item or group (same as TAB).
+SHIFT-TAB Move to previous item or group.
+LEFT ARROW Move to previous item or group (same as SHIFT-TAB).
+PAGE UP In text display boxes, scrolls up one page.
+PAGE DOWN In text display boxes, scrolls down one page.
+F1 Display associated help text.
+
+If you see small "^(-)" or "v(+)" symbols at the edges of a menu, it
+means that there are more items above or below the current one that
+aren't being shown (due to insufficient screen space). In text
+display boxes, the amount of text above the current point will be
+displayed as a percentage in the lower right corner. Using the
+Up/Down arrow keys will cause the object to scroll by line. The
+PageUp and PageDown keys will scroll by entire screens.
+
+Selecting OK in a menu will confirm whatever action it's controlling.
+Selecting Cancel will cancel the operation and generally return you to
+the previous menu. Use TAB to move the cursor around and select the
+buttons.
+
+Most screens obey the Help key (F1) - USE IT! It generally offers useful
+context-specific hints on what to do at each stage of the installation,
+and if you're at all unsure about what to do at a given stage in the
+installation, hit F1!
+
+
+SPECIAL FEATURES:
+=================
+
+It is possible to select a menu item by typing the first character of
+its name, if unique. This will generally be an item number.
+
+The console driver contains a scroll-back buffer for reviewing things
+that may have scrolled off the screen. To use scroll-back, press the
+"Scroll Lock" key on your keyboard and use the arrow or Page Up/Page
+Down keys to move through the saved text. To leave scroll-back mode,
+press the Scroll Lock key again. This feature is most useful for
+reading back through your boot messages (go ahead, try it now!) though
+it's also useful when dealing with sub-shells or other "wizard modes"
+that don't use menus and tend to scroll their output off the top of
+the screen.
+
+FreeBSD also supports multiple "virtual consoles" which you can use to
+in order to have several active sessions at once. Use ALT-F<n> to
+switch between screens, where `F<n>' is the function key corresponding
+to the screen you wish to see. By default, the system comes with 8
+virtual consoles enabled - you can enable more by editing the
+/etc/ttys file and turning the "off" field to "on" in the relevant vty
+entries (up to 12).
diff --git a/usr.sbin/sysinstall/help/usermgmt.hlp b/usr.sbin/sysinstall/help/usermgmt.hlp
new file mode 100644
index 0000000..fbd7cac
--- /dev/null
+++ b/usr.sbin/sysinstall/help/usermgmt.hlp
@@ -0,0 +1,89 @@
+These screens allow you to add groups and users to your system.
+
+You can move through the fields with the TAB, BACK-TAB and ENTER
+keys. To edit a field, use DELETE or BACKSPACE. You may also use ^A
+(control-A) to go to the beginning of the line, ^E (control-E) to go
+to the end, ^F (control-F) to go forward a character, ^B (control-B)
+to go backward one character, ^D (control-D) to delete the character
+under the cursor and ^K (control-K) to delete to the end of the line.
+Basically, the standard EMACS motion sequences.
+
+When you're done with this form, select OK.
+
+Many of the settings get reasonable defaults if you leave them blank.
+The first time you have entered the name of the new group or user, the
+system will show you what it would chose for most of these fields.
+You are free to change them, of course.
+
+
+User groups
+===========
+
+It's certainly almost generally a good idea to first create a new
+group for your users. Common names for such a group are "users", or
+even simply "other". Group names are used to control file access
+permissions for users that belong to the same group. Several group
+names are already used for system files.
+
+The numerical user or group IDs are often nothing you want to care for
+explicitly. If you don't fill in these fields, the system will choose
+reasonable defaults. However, these numbers (rather than the
+associated names) are what the operating system actually uses to
+distinguish users and groups -- hence they should normally be unique
+to each person or group, respectively.
+
+(The initial membership list for a new group is currently
+unimplemented, sorry.)
+
+
+Users
+=====
+
+The user's login ID is a short (up to 15 characters) alphanumeric ID
+that the user must enter when logging into the system. It's often the
+initial letters of the user's name, and commonly used in lower case.
+It's also the local mail name for this user (though it's possible to
+also set up more descriptive mail alias names later).
+
+The user's login group determines which group access rights the user
+will initially get when logging in. If an additional list of groups is
+provided which the user will become a member of, (s)he will also be
+able to access files of those groups later without providing any
+additional password etc. Except for the "wheel" case mentioned below,
+the additional group membership list should normally not contain the
+login group again.
+
+The user's password can also be set here, and should be chosen with
+care - 6 or more characters, intermixing punctuation and numerics, and
+*not* a word from the dictionary or related to the username is a good
+password choice.
+
+Some of the system's groups have a special meaning. In particular,
+members of group "wheel" are the only people who are later allowed to
+become superuser using the command su(1). So if you're going to add a
+new user who should later perform administrative tasks, don't forget
+to add him to this group! (Well, ``he'' will most likely be yourself
+in the very first place. :)
+
+Also, members of group "operator" will by default get permissions for
+minor administrative operations, like performing system backups, or
+shutting down the system -- without first becoming superuser! So,
+take care when adding people to this group.
+
+The ``full name'' field serves as a comment only. It is also used by
+mail front ends to determine the real name of the user, hence you
+should actually fill in the first and last name of this user. By
+convention, this field can be divided into comma-separated subfields,
+where the office location, the work phone number, and the home phone
+number follow the full name of the user.
+
+The home directory is the directory in the filesystem where the user
+is being logged into, and where his personalized setup files (``dot
+files'', since they usually begin with a `.' and are not displayed by
+the ls(1) command by default) will be looked up. It is often created
+under /usr/home/ or /home/.
+
+Finally, the shell is the user's initial command interpreter. The
+default shell is /bin/sh, some users prefer the more historic
+/bin/csh. Other, often more user-friendly and comfortable shells can
+be found in the ports and packages collection.
diff --git a/usr.sbin/sysinstall/http.c b/usr.sbin/sysinstall/http.c
new file mode 100644
index 0000000..1355f08
--- /dev/null
+++ b/usr.sbin/sysinstall/http.c
@@ -0,0 +1,279 @@
+/*
+ * Copyright (c) 1999
+ * Philipp Mergenthaler <philipp.mergenthaler@stud.uni-karlsruhe.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sysinstall.h"
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/param.h>
+#include <netdb.h>
+
+extern const char *ftp_dirs[]; /* defined in ftp.c */
+
+static Boolean
+checkAccess(Boolean proxyCheckOnly)
+{
+/*
+ * Some proxies fetch files with certain extensions in "ascii mode" instead
+ * of "binary mode" for FTP. The FTP server then translates all LF to CRLF.
+ *
+ * You can force Squid to use binary mode by appending ";type=i" to the URL,
+ * which is what I do here. For other proxies, the LF->CRLF substitution
+ * is reverted in distExtract().
+ */
+
+ int rv, s, af;
+ bool el, found=FALSE; /* end of header line */
+ char *cp, buf[PATH_MAX], req[BUFSIZ];
+ struct addrinfo hints, *res, *res0;
+
+ af = variable_cmp(VAR_IPV6_ENABLE, "YES") ? AF_INET : AF_UNSPEC;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = af;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = 0;
+ if ((rv = getaddrinfo(variable_get(VAR_HTTP_HOST),
+ variable_get(VAR_HTTP_PORT), &hints, &res0)) != 0) {
+ msgConfirm("%s", gai_strerror(rv));
+ variable_unset(VAR_HTTP_HOST);
+ return FALSE;
+ }
+ s = -1;
+ for (res = res0; res; res = res->ai_next) {
+ if ((s = socket(res->ai_family, res->ai_socktype,
+ res->ai_protocol)) < 0)
+ continue;
+ if (connect(s, res->ai_addr, res->ai_addrlen) >= 0)
+ break;
+ close(s);
+ s = -1;
+ }
+ freeaddrinfo(res0);
+ if (s == -1) {
+ msgConfirm("Couldn't connect to proxy %s:%s",
+ variable_get(VAR_HTTP_HOST),variable_get(VAR_HTTP_PORT));
+ variable_unset(VAR_HTTP_HOST);
+ return FALSE;
+ }
+ if (proxyCheckOnly) {
+ close(s);
+ return TRUE;
+ }
+
+ msgNotify("Checking access to\n %s", variable_get(VAR_HTTP_PATH));
+ sprintf(req,"GET %s/ HTTP/1.0\r\n\r\n", variable_get(VAR_HTTP_PATH));
+ write(s,req,strlen(req));
+/*
+ * scan the headers of the response
+ * this is extremely quick'n dirty
+ *
+ */
+ bzero(buf, PATH_MAX);
+ cp=buf;
+ el=FALSE;
+ rv=read(s,cp,1);
+ variable_set2(VAR_HTTP_FTP_MODE,"",0);
+ while (rv>0) {
+ if ((*cp == '\012') && el) {
+ /* reached end of a header line */
+ if (!strncmp(buf,"HTTP",4)) {
+ if (strtol((char *)(buf+9),0,0) == 200) {
+ found = TRUE;
+ }
+ }
+
+ if (!strncmp(buf,"Server: ",8)) {
+ if (!strncmp(buf,"Server: Squid",13)) {
+ variable_set2(VAR_HTTP_FTP_MODE,";type=i",0);
+ } else {
+ variable_set2(VAR_HTTP_FTP_MODE,"",0);
+ }
+ }
+ /* ignore other headers */
+ /* check for "\015\012" at beginning of line, i.e. end of headers */
+ if ((cp-buf) == 1)
+ break;
+ cp=buf;
+ rv=read(s,cp,1);
+ } else {
+ el=FALSE;
+ if (*cp == '\015')
+ el=TRUE;
+ cp++;
+ rv=read(s,cp,1);
+ }
+ }
+ close(s);
+ return found;
+}
+
+Boolean
+mediaInitHTTP(Device *dev)
+{
+ bool found=FALSE; /* end of header line */
+ char *rel, req[BUFSIZ];
+ int fdir;
+
+ /*
+ * First verify the proxy access
+ */
+ checkAccess(TRUE);
+ while (variable_get(VAR_HTTP_HOST) == NULL) {
+ if (DITEM_STATUS(mediaSetHTTP(NULL)) == DITEM_FAILURE)
+ return FALSE;
+ checkAccess(TRUE);
+ }
+again:
+ /* If the release is specified as "__RELEASE" or "any", then just
+ * assume that the path the user gave is ok.
+ */
+ rel = variable_get(VAR_RELNAME);
+ /*
+ msgConfirm("rel: -%s-", rel);
+ */
+
+ if (strcmp(rel, "__RELEASE") && strcmp(rel, "any")) {
+ for (fdir = 0; ftp_dirs[fdir]; fdir++) {
+ sprintf(req, "%s/%s/%s", variable_get(VAR_FTP_PATH),
+ ftp_dirs[fdir], rel);
+ variable_set2(VAR_HTTP_PATH, req, 0);
+ if (checkAccess(FALSE)) {
+ found = TRUE;
+ break;
+ }
+ }
+ } else {
+ variable_set2(VAR_HTTP_PATH, variable_get(VAR_FTP_PATH), 0);
+ found = checkAccess(FALSE);
+ }
+ if (!found) {
+ msgConfirm("No such directory: %s\n"
+ "please check the URL and try again.", variable_get(VAR_HTTP_PATH));
+ variable_unset(VAR_HTTP_PATH);
+ dialog_clear_norefresh();
+ clear();
+ if (DITEM_STATUS(mediaSetHTTP(NULL)) != DITEM_FAILURE) goto again;
+ }
+ return found;
+}
+
+FILE *
+mediaGetHTTP(Device *dev, char *file, Boolean probe)
+{
+ FILE *fp;
+ int rv, s, af;
+ bool el; /* end of header line */
+ char *cp, buf[PATH_MAX], req[BUFSIZ];
+ struct addrinfo hints, *res, *res0;
+
+ af = variable_cmp(VAR_IPV6_ENABLE, "YES") ? AF_INET : AF_UNSPEC;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = af;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = 0;
+ if ((rv = getaddrinfo(variable_get(VAR_HTTP_HOST),
+ variable_get(VAR_HTTP_PORT), &hints, &res0)) != 0) {
+ msgConfirm("%s", gai_strerror(rv));
+ return NULL;
+ }
+ s = -1;
+ for (res = res0; res; res = res->ai_next) {
+ if ((s = socket(res->ai_family, res->ai_socktype,
+ res->ai_protocol)) < 0)
+ continue;
+ if (connect(s, res->ai_addr, res->ai_addrlen) >= 0)
+ break;
+ close(s);
+ s = -1;
+ }
+ freeaddrinfo(res0);
+ if (s == -1) {
+ msgConfirm("Couldn't connect to proxy %s:%s",
+ variable_get(VAR_HTTP_HOST),variable_get(VAR_HTTP_PORT));
+ return NULL;
+ }
+
+ sprintf(req,"GET %s/%s%s HTTP/1.0\r\n\r\n",
+ variable_get(VAR_HTTP_PATH), file, variable_get(VAR_HTTP_FTP_MODE));
+
+ if (isDebug()) {
+ msgDebug("sending http request: %s\n",req);
+ }
+ write(s,req,strlen(req));
+
+/*
+ * scan the headers of the response
+ * this is extremely quick'n dirty
+ *
+ */
+ cp=buf;
+ el=FALSE;
+ rv=read(s,cp,1);
+ while (rv>0) {
+ if ((*cp == '\012') && el) {
+ /* reached end of a header line */
+ if (!strncmp(buf,"HTTP",4)) {
+ rv=strtol((char *)(buf+9),0,0);
+ *(cp-1)='\0'; /* chop the CRLF off */
+ if (probe && (rv != 200)) {
+ return NULL;
+ } else if (rv >= 500) {
+ msgConfirm("Server error %s when sending %s, you could try an other server",buf, req);
+ return NULL;
+ } else if (rv == 404) {
+ msgConfirm("%s was not found, maybe directory or release-version are wrong?",req);
+ return NULL;
+ } else if (rv >= 400) {
+ msgConfirm("Client error %s, you could try an other server",buf);
+ return NULL;
+ } else if (rv >= 300) {
+ msgConfirm("Error %s,",buf);
+ return NULL;
+ } else if (rv != 200) {
+ msgConfirm("Error %s when sending %s, you could try an other server",buf, req);
+ return NULL;
+ }
+ }
+ /* ignore other headers */
+ /* check for "\015\012" at beginning of line, i.e. end of headers */
+ if ((cp-buf) == 1)
+ break;
+ cp=buf;
+ rv=read(s,cp,1);
+ } else {
+ el=FALSE;
+ if (*cp == '\015')
+ el=TRUE;
+ cp++;
+ rv=read(s,cp,1);
+ }
+ }
+ fp=fdopen(s,"r");
+ return fp;
+}
diff --git a/usr.sbin/sysinstall/index.c b/usr.sbin/sysinstall/index.c
new file mode 100644
index 0000000..60e8160
--- /dev/null
+++ b/usr.sbin/sysinstall/index.c
@@ -0,0 +1,892 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ncurses.h>
+#include <dialog.h>
+#include "sysinstall.h"
+
+/* Macros and magic values */
+#define MAX_MENU 12
+#define _MAX_DESC 55
+
+/* A structure holding the root, top and plist pointer at once */
+struct ListPtrs
+{
+ PkgNodePtr root; /* root of tree */
+ PkgNodePtr top; /* part of tree we handle */
+ PkgNodePtr plist; /* list of selected packages */
+};
+typedef struct ListPtrs* ListPtrsPtr;
+
+static void index_recorddeps(Boolean add, PkgNodePtr root, IndexEntryPtr ie);
+
+/* Shared between index_initialize() and the various clients of it */
+PkgNode Top, Plist;
+
+/* Smarter strdup */
+static inline char *
+_strdup(char *ptr)
+{
+ return ptr ? strdup(ptr) : NULL;
+}
+
+static char *descrs[] = {
+ "Package Selection", "To mark a package, move to it and press SPACE. If the package is\n"
+ "already marked, it will be unmarked or deleted (if installed).\n"
+ "Items marked with a `D' are dependencies which will be auto-loaded.\n"
+ "To search for a package by name, press ESC. To select a category,\n"
+ "press RETURN. NOTE: The All category selection creates a very large\n"
+ "submenu! If you select it, please be patient while it comes up.",
+ "Package Targets", "These are the packages you've selected for extraction.\n\n"
+ "If you're sure of these choices, select OK.\n"
+ "If not, select Cancel to go back to the package selection menu.\n",
+ "All", "All available packages in all categories.",
+ "accessibility", "Ports to help disabled users.",
+ "afterstep", "Ports to support the AfterStep window manager.",
+ "arabic", "Ported software for Arab countries.",
+ "archivers", "Utilities for archiving and unarchiving data.",
+ "astro", "Applications related to astronomy.",
+ "audio", "Audio utilities - most require a supported sound card.",
+ "benchmarks", "Utilities for measuring system performance.",
+ "biology", "Software related to biology.",
+ "cad", "Computer Aided Design utilities.",
+ "chinese", "Ported software for the Chinese market.",
+ "comms", "Communications utilities.",
+ "converters", "Format conversion utilities.",
+ "databases", "Database software.",
+ "deskutils", "Various Desktop utilities.",
+ "devel", "Software development utilities and libraries.",
+ "dns", "Domain Name Service tools.",
+ "editors", "Editors.",
+ "elisp", "Things related to Emacs Lisp.",
+ "emulators", "Utilities for emulating other operating systems.",
+ "finance", "Monetary, financial and related applications.",
+ "french", "Ported software for French countries.",
+ "ftp", "FTP client and server utilities.",
+ "games", "Various and sundry amusements.",
+ "german", "Ported software for Germanic countries.",
+ "geography", "Geography-related software.",
+ "gnome", "Components of the Gnome Desktop environment.",
+ "gnustep", "Software for GNUstep desktop environment.",
+ "graphics", "Graphics libraries and utilities.",
+ "haskell", "Software related to the Haskell language.",
+ "hamradio", "Software for amateur radio.",
+ "hebrew", "Ported software for Hebrew language.",
+ "hungarian", "Ported software for the Hungarian market.",
+ "ipv6", "IPv6 related software.",
+ "irc", "Internet Relay Chat utilities.",
+ "japanese", "Ported software for the Japanese market.",
+ "java", "Java language support.",
+ "kde", "Software for the K Desktop Environment.",
+ "kld", "Kernel loadable modules",
+ "korean", "Ported software for the Korean market.",
+ "lang", "Computer languages.",
+ "linux", "Linux programs that can run under binary compatibility.",
+ "lisp", "Software related to the Lisp language.",
+ "mail", "Electronic mail packages and utilities.",
+ "math", "Mathematical computation software.",
+ "mbone", "Applications and utilities for the MBONE.",
+ "misc", "Miscellaneous utilities.",
+ "multimedia", "Multimedia software.",
+ "net", "Networking utilities.",
+ "net-im", "Instant messaging software.",
+ "net-mgmt", "Network management tools.",
+ "net-p2p", "Peer to peer network applications.",
+ "news", "USENET News support software.",
+ "palm", "Software support for the Palm(tm) series.",
+ "parallel", "Applications dealing with parallelism in computing.",
+ "pear", "Software related to the Pear PHP framework.",
+ "perl5", "Utilities/modules for the PERL5 language.",
+ "plan9", "Software from the Plan9 operating system.",
+ "polish", "Ported software for the Polish market.",
+ "ports-mgmt", "Utilities for managing ports and packages.",
+ "portuguese", "Ported software for the Portuguese market.",
+ "print", "Utilities for dealing with printing.",
+ "python", "Software related to the Python language.",
+ "ruby", "Software related to the Ruby language.",
+ "rubygems", "Ports of RubyGems packages.",
+ "russian", "Ported software for the Russian market.",
+ "scheme", "Software related to the Scheme language.",
+ "science", "Scientific software.",
+ "security", "System security software.",
+ "shells", "Various shells (tcsh, bash, etc).",
+ "spanish", "Ported software for the Spanish market.",
+ "sysutils", "Various system utilities.",
+ "tcl", "TCL and packages that depend on it.",
+ "tcl80", "TCL v8.0 and packages that depend on it.",
+ "tcl82", "TCL v8.2 and packages that depend on it.",
+ "tcl83", "TCL v8.3 and packages that depend on it.",
+ "tcl84", "TCL v8.4 and packages that depend on it.",
+ "textproc", "Text processing/search utilities.",
+ "tk", "Tk and packages that depend on it.",
+ "tk80", "Tk8.0 and packages that depend on it.",
+ "tk82", "Tk8.2 and packages that depend on it.",
+ "tk83", "Tk8.3 and packages that depend on it.",
+ "tk84", "Tk8.4 and packages that depend on it.",
+ "tkstep80", "Ports to support the TkStep window manager.",
+ "ukrainian", "Ported software for the Ukrainian market.",
+ "vietnamese", "Ported software for the Vietnamese market.",
+ "windowmaker", "Ports to support the WindowMaker window manager.",
+ "www", "Web utilities (browsers, HTTP servers, etc).",
+ "x11", "X Window System based utilities.",
+ "x11-clocks", "X Window System based clocks.",
+ "x11-drivers", "X Window System drivers.",
+ "x11-fm", "X Window System based file managers.",
+ "x11-fonts", "X Window System fonts and font utilities.",
+ "x11-servers", "X Window System servers.",
+ "x11-themes", "X Window System themes.",
+ "x11-toolkits", "X Window System based development toolkits.",
+ "x11-wm", "X Window System window managers.",
+ "xfce", "Software related to the Xfce Desktop Environment.",
+ "zope", "Software related to the Zope platform.",
+ NULL, NULL,
+};
+
+static char *
+fetch_desc(char *name)
+{
+ int i;
+
+ for (i = 0; descrs[i]; i += 2) {
+ if (!strcmp(descrs[i], name))
+ return descrs[i + 1];
+ }
+ return "No description provided";
+}
+
+static PkgNodePtr
+new_pkg_node(char *name, node_type type)
+{
+ PkgNodePtr tmp = safe_malloc(sizeof(PkgNode));
+
+ tmp->name = _strdup(name);
+ tmp->type = type;
+ return tmp;
+}
+
+static char *
+strip(char *buf)
+{
+ int i;
+
+ for (i = 0; buf[i]; i++)
+ if (buf[i] == '\t' || buf[i] == '\n')
+ buf[i] = ' ';
+ return buf;
+}
+
+static IndexEntryPtr
+new_index(char *name, char *pathto, char *prefix, char *comment, char *descr, char *maint, char *deps, int volume)
+{
+ IndexEntryPtr tmp = safe_malloc(sizeof(IndexEntry));
+
+ tmp->name = _strdup(name);
+ tmp->path = _strdup(pathto);
+ tmp->prefix = _strdup(prefix);
+ tmp->comment = _strdup(comment);
+ tmp->descrfile = strip(_strdup(descr));
+ tmp->maintainer = _strdup(maint);
+ tmp->deps = _strdup(deps);
+ tmp->depc = 0;
+ tmp->installed = package_installed(name);
+ tmp->vol_checked = 0;
+ tmp->volume = volume;
+ if (volume != 0) {
+ have_volumes = TRUE;
+ if (low_volume == 0)
+ low_volume = volume;
+ else if (low_volume > volume)
+ low_volume = volume;
+ if (high_volume < volume)
+ high_volume = volume;
+ }
+ return tmp;
+}
+
+static void
+index_register(PkgNodePtr top, char *where, IndexEntryPtr ptr)
+{
+ PkgNodePtr p, q;
+
+ for (q = NULL, p = top->kids; p; p = p->next) {
+ if (!strcmp(p->name, where)) {
+ q = p;
+ break;
+ }
+ }
+ if (!p) {
+ /* Add new category */
+ q = new_pkg_node(where, PLACE);
+ q->desc = fetch_desc(where);
+ q->next = top->kids;
+ top->kids = q;
+ }
+ p = new_pkg_node(ptr->name, PACKAGE);
+ p->desc = ptr->comment;
+ p->data = ptr;
+ p->next = q->kids;
+ q->kids = p;
+}
+
+static int
+copy_to_sep(char *to, char *from, int sep)
+{
+ char *tok;
+
+ tok = strchr(from, sep);
+ if (!tok) {
+ *to = '\0';
+ return 0;
+ }
+ *tok = '\0';
+ strcpy(to, from);
+ return tok + 1 - from;
+}
+
+static int
+skip_to_sep(char *from, int sep)
+{
+ char *tok;
+
+ tok = strchr(from, sep);
+ if (!tok)
+ return 0;
+ *tok = '\0';
+ return tok + 1 - from;
+}
+
+static int
+readline(FILE *fp, char *buf, int max)
+{
+ int rv, i = 0;
+ char ch;
+
+ while ((rv = fread(&ch, 1, 1, fp)) == 1 && ch != '\n' && i < max)
+ buf[i++] = ch;
+ if (i < max)
+ buf[i] = '\0';
+ return rv;
+}
+
+/*
+ * XXX - this function should do error checking, and skip corrupted INDEX
+ * lines without a set number of '|' delimited fields.
+ */
+
+static int
+index_parse(FILE *fp, char *name, char *pathto, char *prefix, char *comment, char *descr, char *maint, char *cats, char *rdeps, int *volume)
+{
+ char line[10240 + 2048 * 7];
+ char junk[2048];
+ char volstr[2048];
+ char *cp;
+ int i;
+
+ i = readline(fp, line, sizeof line);
+ if (i <= 0)
+ return EOF;
+ cp = line;
+ cp += copy_to_sep(name, cp, '|'); /* package name */
+ cp += copy_to_sep(pathto, cp, '|'); /* ports directory */
+ cp += copy_to_sep(prefix, cp, '|'); /* prefix */
+ cp += copy_to_sep(comment, cp, '|'); /* comment */
+ cp += copy_to_sep(descr, cp, '|'); /* path to pkg-descr */
+ cp += copy_to_sep(maint, cp, '|'); /* maintainer */
+ cp += copy_to_sep(cats, cp, '|'); /* categories */
+ cp += skip_to_sep(cp, '|'); /* build deps - not used */
+ cp += copy_to_sep(rdeps, cp, '|'); /* run deps */
+ if (index(cp, '|'))
+ cp += skip_to_sep(cp, '|'); /* url - not used */
+ else {
+ strncpy(junk, cp, 1023);
+ *volume = 0;
+ return 0;
+ }
+ if (index(cp, '|'))
+ cp += skip_to_sep(cp, '|'); /* extract deps - not used */
+ if (index(cp, '|'))
+ cp += skip_to_sep(cp, '|'); /* patch deps - not used */
+ if (index(cp, '|'))
+ cp += skip_to_sep(cp, '|'); /* fetch deps - not used */
+ if (index(cp, '|'))
+ cp += copy_to_sep(volstr, cp, '|'); /* media volume */
+ else {
+ strncpy(volstr, cp, 1023);
+ }
+ *volume = atoi(volstr);
+ return 0;
+}
+
+int
+index_read(FILE *fp, PkgNodePtr papa)
+{
+ char name[127], pathto[255], prefix[255], comment[255], descr[127], maint[127], cats[511], deps[2048 * 8];
+ int volume;
+ PkgNodePtr i;
+
+ while (index_parse(fp, name, pathto, prefix, comment, descr, maint, cats, deps, &volume) != EOF) {
+ char *cp, *cp2, tmp[1024];
+ IndexEntryPtr idx;
+
+ idx = new_index(name, pathto, prefix, comment, descr, maint, deps, volume);
+ /* For now, we only add things to menus if they're in categories. Keywords are ignored */
+ for (cp = strcpy(tmp, cats); (cp2 = strchr(cp, ' ')) != NULL; cp = cp2 + 1) {
+ *cp2 = '\0';
+ index_register(papa, cp, idx);
+ }
+ index_register(papa, cp, idx);
+
+ /* Add to special "All" category */
+ index_register(papa, "All", idx);
+ }
+
+ /* Adjust dependency counts */
+ for (i = papa->kids; i != NULL; i = i->next)
+ if (strcmp(i->name, "All") == 0)
+ break;
+ for (i = i->kids; i != NULL; i = i->next)
+ if (((IndexEntryPtr)i->data)->installed)
+ index_recorddeps(TRUE, papa, i->data);
+
+ return 0;
+}
+
+void
+index_init(PkgNodePtr top, PkgNodePtr plist)
+{
+ if (top) {
+ top->next = top->kids = NULL;
+ top->name = "Package Selection";
+ top->type = PLACE;
+ top->desc = fetch_desc(top->name);
+ top->data = NULL;
+ }
+ if (plist) {
+ plist->next = plist->kids = NULL;
+ plist->name = "Package Targets";
+ plist->type = PLACE;
+ plist->desc = fetch_desc(plist->name);
+ plist->data = NULL;
+ }
+}
+
+void
+index_print(PkgNodePtr top, int level)
+{
+ int i;
+
+ while (top) {
+ for (i = 0; i < level; i++) putchar('\t');
+ printf("name [%s]: %s\n", top->type == PLACE ? "place" : "package", top->name);
+ for (i = 0; i < level; i++) putchar('\t');
+ printf("desc: %s\n", top->desc);
+ if (top->kids)
+ index_print(top->kids, level + 1);
+ top = top->next;
+ }
+}
+
+/* Swap one node for another */
+static void
+swap_nodes(PkgNodePtr a, PkgNodePtr b)
+{
+ PkgNode tmp;
+
+ tmp = *a;
+ *a = *b;
+ a->next = tmp.next;
+ tmp.next = b->next;
+ *b = tmp;
+}
+
+/* Use a disgustingly simplistic bubble sort to put our lists in order */
+void
+index_sort(PkgNodePtr top)
+{
+ PkgNodePtr p, q;
+
+ /* Sort everything at the top level */
+ for (p = top->kids; p; p = p->next) {
+ for (q = top->kids; q; q = q->next) {
+ if (q->next && strcmp(q->name, q->next->name) > 0)
+ swap_nodes(q, q->next);
+ }
+ }
+
+ /* Now sub-sort everything n levels down */
+ for (p = top->kids; p; p = p->next) {
+ if (p->kids)
+ index_sort(p);
+ }
+}
+
+/* Delete an entry out of the list it's in (only the plist, at present) */
+static void
+index_delete(PkgNodePtr n)
+{
+ if (n->next) {
+ PkgNodePtr p = n->next;
+
+ *n = *(n->next);
+ safe_free(p);
+ }
+ else /* Kludgy end sentinal */
+ n->name = NULL;
+}
+
+/*
+ * Search for a given node by name, returning the category in if
+ * tp is non-NULL.
+ */
+PkgNodePtr
+index_search(PkgNodePtr top, char *str, PkgNodePtr *tp)
+{
+ PkgNodePtr p, sp;
+
+ for (p = top->kids; p && p->name; p = p->next) {
+ if (p->type == PACKAGE) {
+ /* If tp == NULL, we're looking for an exact package match */
+ if (!tp && !strcmp(p->name, str))
+ return p;
+
+ /* If tp, we're looking for both a package and a pointer to the place it's in */
+ if (tp && !strncmp(p->name, str, strlen(str))) {
+ *tp = top;
+ return p;
+ }
+ }
+ else if (p->kids) {
+ /* The usual recursion-out-of-laziness ploy */
+ if ((sp = index_search(p, str, tp)) != NULL)
+ return sp;
+ }
+ }
+ if (p && !p->name)
+ p = NULL;
+ return p;
+}
+
+static int
+pkg_checked(dialogMenuItem *self)
+{
+ ListPtrsPtr lists = (ListPtrsPtr)self->aux;
+ PkgNodePtr kp = self->data, plist = lists->plist;
+ int i;
+
+ i = index_search(plist, kp->name, NULL) ? TRUE : FALSE;
+ if (kp->type == PACKAGE && plist) {
+ IndexEntryPtr ie = kp->data;
+ int markD, markX;
+
+ markD = ie->depc > 0; /* needed as dependency */
+ markX = i || ie->installed; /* selected or installed */
+ self->mark = markX ? 'X' : 'D';
+ return markD || markX;
+ } else
+ return FALSE;
+}
+
+static int
+pkg_fire(dialogMenuItem *self)
+{
+ int ret;
+ ListPtrsPtr lists = (ListPtrsPtr)self->aux;
+ PkgNodePtr sp, kp = self->data, plist = lists->plist;
+
+ if (!plist)
+ ret = DITEM_FAILURE;
+ else if (kp->type == PACKAGE) {
+ IndexEntryPtr ie = kp->data;
+
+ sp = index_search(plist, kp->name, NULL);
+ /* Not already selected? */
+ if (!sp) {
+ if (!ie->installed) {
+ PkgNodePtr np = (PkgNodePtr)safe_malloc(sizeof(PkgNode));
+
+ *np = *kp;
+ np->next = plist->kids;
+ plist->kids = np;
+ index_recorddeps(TRUE, lists->root, ie);
+ msgInfo("Added %s to selection list", kp->name);
+ }
+ else if (ie->depc == 0) {
+ if (!msgNoYes("Do you really want to delete %s from the system?", kp->name)) {
+ if (vsystem("pkg_delete %s %s", isDebug() ? "-v" : "", kp->name)) {
+ msgConfirm("Warning: pkg_delete of %s failed.\n Check debug output for details.", kp->name);
+ }
+ else {
+ ie->installed = 0;
+ index_recorddeps(FALSE, lists->root, ie);
+ }
+ }
+ }
+ else
+ msgConfirm("Warning: Package %s is needed by\n %d other installed package%s.",
+ kp->name, ie->depc, (ie->depc != 1) ? "s" : "");
+ }
+ else {
+ index_recorddeps(FALSE, lists->root, ie);
+ msgInfo("Removed %s from selection list", kp->name);
+ index_delete(sp);
+ }
+ ret = DITEM_SUCCESS;
+ /* Mark menu for redraw if we had dependencies */
+ if (strlen(ie->deps) > 0)
+ ret |= DITEM_REDRAW;
+ }
+ else { /* Not a package, must be a directory */
+ int p, s;
+
+ p = s = 0;
+ index_menu(lists->root, kp, plist, &p, &s);
+ ret = DITEM_SUCCESS | DITEM_CONTINUE;
+ }
+ return ret;
+}
+
+static void
+pkg_selected(dialogMenuItem *self, int is_selected)
+{
+ PkgNodePtr kp = self->data;
+
+ if (!is_selected || kp->type != PACKAGE)
+ return;
+ msgInfo("%s", kp->desc);
+}
+
+int
+index_menu(PkgNodePtr root, PkgNodePtr top, PkgNodePtr plist, int *pos, int *scroll)
+{
+ struct ListPtrs lists;
+ size_t maxname;
+ int n, rval;
+ int curr, max;
+ PkgNodePtr kp;
+ dialogMenuItem *nitems;
+ Boolean hasPackages;
+ WINDOW *w;
+
+ lists.root = root;
+ lists.top = top;
+ lists.plist = plist;
+
+ hasPackages = FALSE;
+ nitems = NULL;
+ n = maxname = 0;
+
+ /* Figure out if this menu is full of "leaves" or "branches" */
+ for (kp = top->kids; kp && kp->name; kp = kp->next) {
+ size_t len;
+
+ ++n;
+ if (kp->type == PACKAGE && plist) {
+ hasPackages = TRUE;
+ if ((len = strlen(kp->name)) > maxname)
+ maxname = len;
+ }
+ }
+ if (!n && plist) {
+ msgConfirm("The %s menu is empty.", top->name);
+ return DITEM_LEAVE_MENU;
+ }
+
+ w = savescr();
+ while (1) {
+ n = 0;
+ curr = max = 0;
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ kp = top->kids;
+ if (!hasPackages && plist) {
+ nitems = item_add(nitems, "OK", NULL, NULL, NULL, NULL, NULL, NULL, &curr, &max);
+ nitems = item_add(nitems, "Install", NULL, NULL, NULL, NULL, NULL, NULL, &curr, &max);
+ }
+ while (kp && kp->name) {
+ char buf[256];
+ IndexEntryPtr ie = kp->data;
+
+ /* Brutally adjust description to fit in menu */
+ if (kp->type == PACKAGE)
+ snprintf(buf, sizeof buf, "[%s]", ie->path ? ie->path : "External vendor");
+ else
+ SAFE_STRCPY(buf, kp->desc);
+ if (strlen(buf) > (_MAX_DESC - maxname))
+ buf[_MAX_DESC - maxname] = '\0';
+ nitems = item_add(nitems, kp->name, (char *)buf, pkg_checked,
+ pkg_fire, pkg_selected, kp, (int *)(&lists),
+ &curr, &max);
+ ++n;
+ kp = kp->next;
+ }
+ /* NULL delimiter so item_free() knows when to stop later */
+ nitems = item_add(nitems, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
+ &curr, &max);
+
+recycle:
+ dialog_clear_norefresh();
+ if (hasPackages)
+ rval = dialog_checklist(top->name, top->desc, -1, -1, n > MAX_MENU ? MAX_MENU : n, -n, nitems, NULL);
+ else
+ rval = dialog_menu(top->name, top->desc, -1, -1, n > MAX_MENU ? MAX_MENU : n, -n, nitems + (plist ? 2 : 0), (char *)plist, pos, scroll);
+ if (rval == -1 && plist) {
+ static char *cp;
+ PkgNodePtr menu;
+
+ /* Search */
+ if ((cp = msgGetInput(cp, "Search by package name. Please enter search string:")) != NULL) {
+ PkgNodePtr p = index_search(top, cp, &menu);
+
+ if (p) {
+ int pos, scroll;
+
+ /* These need to be set to point at the found item, actually. Hmmm! */
+ pos = scroll = 0;
+ index_menu(root, menu, plist, &pos, &scroll);
+ }
+ else
+ msgConfirm("Search string: %s yielded no hits.", cp);
+ }
+ goto recycle;
+ }
+ items_free(nitems, &curr, &max);
+ restorescr(w);
+ return rval ? DITEM_FAILURE : DITEM_SUCCESS;
+ }
+}
+
+int
+index_extract(Device *dev, PkgNodePtr top, PkgNodePtr who, Boolean depended,
+ int current_volume)
+{
+ int status = DITEM_SUCCESS;
+ Boolean notyet = FALSE;
+ PkgNodePtr tmp2;
+ IndexEntryPtr id = who->data;
+ WINDOW *w;
+
+ /*
+ * Short-circuit the package dependency checks. We're already
+ * maintaining a data structure of installed packages, so if a
+ * package is already installed, don't try to check to make sure
+ * that all of its dependencies are installed. At best this
+ * wastes a ton of cycles and can cause minor delays between
+ * package extraction. At worst it can cause an infinite loop with
+ * a certain faulty INDEX file.
+ */
+
+ if (id->installed == 1 || (have_volumes && id->vol_checked == current_volume))
+ return DITEM_SUCCESS;
+
+ w = savescr();
+ if (id && id->deps && strlen(id->deps)) {
+ char t[2048 * 8], *cp, *cp2;
+
+ SAFE_STRCPY(t, id->deps);
+ cp = t;
+ while (cp && DITEM_STATUS(status) == DITEM_SUCCESS) {
+ if ((cp2 = index(cp, ' ')) != NULL)
+ *cp2 = '\0';
+ if ((tmp2 = index_search(top, cp, NULL)) != NULL) {
+ status = index_extract(dev, top, tmp2, TRUE, current_volume);
+ if (DITEM_STATUS(status) != DITEM_SUCCESS) {
+ /* package probably on a future disc volume */
+ if (status & DITEM_CONTINUE) {
+ status = DITEM_SUCCESS;
+ notyet = TRUE;
+ } else if (variable_get(VAR_NO_CONFIRM))
+ msgNotify("Loading of dependent package %s failed", cp);
+ else
+ msgConfirm("Loading of dependent package %s failed", cp);
+ }
+ }
+ else if (!package_installed(cp)) {
+ if (variable_get(VAR_NO_CONFIRM))
+ msgNotify("Warning: %s is a required package but was not found.", cp);
+ else
+ msgConfirm("Warning: %s is a required package but was not found.", cp);
+ }
+ if (cp2)
+ cp = cp2 + 1;
+ else
+ cp = NULL;
+ }
+ }
+
+ /*
+ * If iterating through disc volumes one at a time indicate failure if
+ * dependency install failed due to package being on a higher volume
+ * numbered disc, but that we should continue anyway. Note that this
+ * package has already been processed for this disc volume so we don't
+ * need to do it again.
+ */
+
+ if (notyet) {
+ restorescr(w);
+ id->vol_checked = current_volume;
+ return DITEM_FAILURE | DITEM_CONTINUE;
+ }
+
+ /*
+ * Done with the deps? Try to load the real m'coy. If iterating
+ * through a multi-volume disc set fail the install if the package
+ * is on a higher numbered volume to cut down on disc switches the
+ * user needs to do, but indicate caller should continue processing
+ * despite error return. Note this package was processed for the
+ * current disc being checked.
+ */
+
+ if (DITEM_STATUS(status) == DITEM_SUCCESS) {
+ /* Prompt user if the package is not available on the current volume. */
+ if(mediaDevice->type == DEVICE_TYPE_CDROM) {
+ if (current_volume != 0 && id->volume > current_volume) {
+ restorescr(w);
+ id->vol_checked = current_volume;
+ return DITEM_FAILURE | DITEM_CONTINUE;
+ }
+ while (id->volume != dev->volume) {
+ if (!msgYesNo("This is disc #%d. Package %s is on disc #%d\n"
+ "Would you like to switch discs now?\n", dev->volume,
+ id->name, id->volume)) {
+ DEVICE_SHUTDOWN(mediaDevice);
+ msgConfirm("Please remove disc #%d from your drive, and add disc #%d\n",
+ dev->volume, id->volume);
+ DEVICE_INIT(mediaDevice);
+ } else {
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+ }
+ }
+ status = package_extract(dev, who->name, depended);
+ if (DITEM_STATUS(status) == DITEM_SUCCESS)
+ id->installed = 1;
+ }
+ restorescr(w);
+ return status;
+}
+
+static void
+index_recorddeps(Boolean add, PkgNodePtr root, IndexEntryPtr ie)
+{
+ char depends[1024 * 16], *space, *todo;
+ PkgNodePtr found;
+ IndexEntryPtr found_ie;
+
+ SAFE_STRCPY(depends, ie->deps);
+ for (todo = depends; todo != NULL; ) {
+ space = index(todo, ' ');
+ if (space != NULL)
+ *space = '\0';
+
+ if (strlen(todo) > 0) { /* only non-empty dependencies */
+ found = index_search(root, todo, NULL);
+ if (found != NULL) {
+ found_ie = found->data;
+ if (add)
+ ++found_ie->depc;
+ else
+ --found_ie->depc;
+ }
+ }
+
+ if (space != NULL)
+ todo = space + 1;
+ else
+ todo = NULL;
+ }
+}
+
+static Boolean index_initted;
+
+/* Read and initialize global index */
+int
+index_initialize(char *path)
+{
+ FILE *fp;
+ WINDOW *w = NULL;
+
+ if (!index_initted) {
+ w = savescr();
+ dialog_clear_norefresh();
+ have_volumes = FALSE;
+ low_volume = high_volume = 0;
+
+ /* Got any media? */
+ if (!mediaVerify()) {
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+
+ /* Does it move when you kick it? */
+ if (!DEVICE_INIT(mediaDevice)) {
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+
+ dialog_clear_norefresh();
+ msgNotify("Attempting to fetch %s file from selected media.", path);
+ fp = DEVICE_GET(mediaDevice, path, TRUE);
+ if (!fp) {
+ msgConfirm("Unable to get packages/INDEX file from selected media.\n\n"
+ "This may be because the packages collection is not available\n"
+ "on the distribution media you've chosen, most likely an FTP site\n"
+ "without the packages collection mirrored. Please verify that\n"
+ "your media, or your path to the media, is correct and try again.");
+ DEVICE_SHUTDOWN(mediaDevice);
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+ dialog_clear_norefresh();
+ msgNotify("Located INDEX, now reading package data from it...");
+ index_init(&Top, &Plist);
+ if (index_read(fp, &Top)) {
+ msgConfirm("I/O or format error on packages/INDEX file.\n"
+ "Please verify media (or path to media) and try again.");
+ fclose(fp);
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+ fclose(fp);
+ index_sort(&Top);
+ index_initted = TRUE;
+ restorescr(w);
+ }
+ return DITEM_SUCCESS;
+}
diff --git a/usr.sbin/sysinstall/install.c b/usr.sbin/sysinstall/install.c
new file mode 100644
index 0000000..a10ae10
--- /dev/null
+++ b/usr.sbin/sysinstall/install.c
@@ -0,0 +1,1278 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sysinstall.h"
+#include <ctype.h>
+#include <sys/consio.h>
+#include <sys/disklabel.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <sys/wait.h>
+#include <sys/uio.h>
+#include <sys/param.h>
+#define MSDOSFS
+#include <sys/mount.h>
+#include <ufs/ufs/ufsmount.h>
+#include <fs/msdosfs/msdosfsmount.h>
+#undef MSDOSFS
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <libdisk.h>
+#include <limits.h>
+#include <unistd.h>
+#include <termios.h>
+
+/* Hack for rsaref package add, which displays interactive license.
+ * Used by package.c
+ */
+int _interactiveHack;
+int FixItMode = 0;
+int NCpus;
+
+static void create_termcap(void);
+static void fixit_common(void);
+
+#define TERMCAP_FILE "/usr/share/misc/termcap"
+
+static void installConfigure(void);
+
+Boolean
+checkLabels(Boolean whinge)
+{
+ Device **devs;
+ Boolean status;
+ Disk *disk;
+ PartInfo *pi;
+ Chunk *c1, *c2;
+ int i;
+
+ /* Don't allow whinging if noWarn is set */
+ if (variable_get(VAR_NO_WARN))
+ whinge = FALSE;
+
+ status = TRUE;
+ HomeChunk = RootChunk = SwapChunk = NULL;
+ TmpChunk = UsrChunk = VarChunk = NULL;
+#ifdef __ia64__
+ EfiChunk = NULL;
+#endif
+
+ /* We don't need to worry about root/usr/swap if we're already multiuser */
+ if (!RunningAsInit)
+ return status;
+
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ /* First verify that we have a root device */
+ for (i = 0; devs[i]; i++) {
+ if (!devs[i]->enabled)
+ continue;
+ disk = (Disk *)devs[i]->private;
+ msgDebug("Scanning disk %s for root filesystem\n", disk->name);
+ if (!disk->chunks)
+ msgFatal("No chunk list found for %s!", disk->name);
+ for (c1 = disk->chunks->part; c1; c1 = c1->next) {
+#ifdef __ia64__
+ c2 = c1;
+#elif defined(__powerpc__)
+ if (c1->type == apple) {
+ for (c2 = c1->part; c2; c2 = c2->next) {
+#else
+ if (c1->type == freebsd) {
+ for (c2 = c1->part; c2; c2 = c2->next) {
+#endif
+
+ pi = (PartInfo *)c2->private_data;
+ if (c2->type == part && c2->subtype != FS_SWAP && pi != NULL) {
+ if (!strcmp(pi->mountpoint, "/")) {
+ if (RootChunk) {
+ if (whinge)
+ msgConfirm("WARNING: You have more than one root device set?!\n"
+ "Using the first one found.");
+ continue;
+ }
+ else {
+ RootChunk = c2;
+ if (isDebug())
+ msgDebug("Found rootdev at %s!\n", RootChunk->name);
+ }
+ }
+ else if (!strcmp(pi->mountpoint, "/usr")) {
+ if (UsrChunk) {
+ if (whinge)
+ msgConfirm("WARNING: You have more than one /usr filesystem.\n"
+ "Using the first one found.");
+ continue;
+ }
+ else {
+ UsrChunk = c2;
+ if (isDebug())
+ msgDebug("Found usrdev at %s!\n", UsrChunk->name);
+ }
+ }
+ else if (!strcmp(pi->mountpoint, "/var")) {
+ if (VarChunk) {
+ if (whinge)
+ msgConfirm("WARNING: You have more than one /var filesystem.\n"
+ "Using the first one found.");
+ continue;
+ }
+ else {
+ VarChunk = c2;
+ if (isDebug())
+ msgDebug("Found vardev at %s!\n", VarChunk->name);
+ }
+ } else if (!strcmp(pi->mountpoint, "/tmp")) {
+ if (TmpChunk) {
+ if (whinge)
+ msgConfirm("WARNING: You have more than one /tmp filesystem.\n"
+ "Using the first one found.");
+ continue;
+ }
+ else {
+ TmpChunk = c2;
+ if (isDebug())
+ msgDebug("Found tmpdev at %s!\n", TmpChunk->name);
+ }
+ } else if (!strcmp(pi->mountpoint, "/home")) {
+ if (HomeChunk) {
+ if (whinge)
+ msgConfirm("WARNING: You have more than one /home filesystem.\n"
+ "Using the first one found.");
+ continue;
+ }
+ else {
+ HomeChunk = c2;
+ if (isDebug())
+ msgDebug("Found homedev at %s!\n", HomeChunk->name);
+ }
+ }
+ }
+#ifndef __ia64__
+ }
+ }
+#endif
+ }
+ }
+
+ /* Now check for swap devices */
+ for (i = 0; devs[i]; i++) {
+ if (!devs[i]->enabled)
+ continue;
+ disk = (Disk *)devs[i]->private;
+ msgDebug("Scanning disk %s for swap partitions\n", disk->name);
+ if (!disk->chunks)
+ msgFatal("No chunk list found for %s!", disk->name);
+ for (c1 = disk->chunks->part; c1; c1 = c1->next) {
+
+#ifdef __ia64__
+ c2 = c1;
+#elif defined(__powerpc__)
+ if (c1->type == apple) {
+ for (c2 = c1->part; c2; c2 = c2->next) {
+#else
+ if (c1->type == freebsd) {
+ for (c2 = c1->part; c2; c2 = c2->next) {
+#endif
+ if (c2->type == part && c2->subtype == FS_SWAP && !SwapChunk) {
+ SwapChunk = c2;
+ if (isDebug())
+ msgDebug("Found swapdev at %s!\n", SwapChunk->name);
+ break;
+ }
+#ifndef __ia64__
+ }
+ }
+#endif
+ }
+ }
+
+#ifdef __ia64__
+ for (i = 0; devs[i] != NULL; i++) {
+ if (!devs[i]->enabled)
+ continue;
+ disk = (Disk *)devs[i]->private;
+ for (c1 = disk->chunks->part; c1 != NULL; c1 = c1->next) {
+ pi = (PartInfo *)c1->private_data;
+ if (c1->type == efi && pi != NULL && pi->mountpoint[0] == '/')
+ EfiChunk = c1;
+ }
+ }
+#endif
+
+ if (!RootChunk && whinge) {
+ msgConfirm("No root device found - you must label a partition as /\n"
+ "in the label editor.");
+ status = FALSE;
+ }
+ if (!SwapChunk && whinge) {
+ if (msgYesNo("No swap devices found - you should create at least one\n"
+ "swap partition. Without swap, the install will fail\n"
+ "if you do not have enough RAM. Continue anyway?"))
+ status = FALSE;
+ }
+#ifdef __ia64__
+ if (EfiChunk == NULL && whinge) {
+ if (msgYesNo("No (mounted) EFI system partition found. Is this what you want?"))
+ status = FALSE;
+ }
+#endif
+ return status;
+}
+
+static int
+installInitial(void)
+{
+ static Boolean alreadyDone = FALSE;
+ int status = DITEM_SUCCESS;
+
+ if (alreadyDone)
+ return DITEM_SUCCESS;
+
+ if (!variable_get(DISK_LABELLED)) {
+ msgConfirm("You need to assign disk labels before you can proceed with\n"
+ "the installation.");
+ return DITEM_FAILURE;
+ }
+ /* If it's labelled, assume it's also partitioned */
+ if (!variable_get(DISK_PARTITIONED))
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+
+ /* If we refuse to proceed, bail. */
+ dialog_clear_norefresh();
+ if (!variable_get(VAR_NO_WARN)) {
+ if (msgYesNo(
+ "Last Chance! Are you SURE you want continue the installation?\n\n"
+ "If you're running this on a disk with data you wish to save\n"
+ "then WE STRONGLY ENCOURAGE YOU TO MAKE PROPER BACKUPS before\n"
+ "proceeding!\n\n"
+ "We can take no responsibility for lost disk contents!") != 0)
+ return DITEM_FAILURE;
+ }
+
+ if (DITEM_STATUS(diskLabelCommit(NULL)) != DITEM_SUCCESS) {
+ msgConfirm("Couldn't make filesystems properly. Aborting.");
+ return DITEM_FAILURE;
+ }
+
+ if (!copySelf()) {
+ msgConfirm("installInitial: Couldn't clone the boot floppy onto the\n"
+ "root file system. Aborting!");
+ return DITEM_FAILURE;
+ }
+
+ if (!Restarting && chroot("/mnt") == -1) {
+ msgConfirm("installInitial: Unable to chroot to %s - this is bad!",
+ "/mnt");
+ return DITEM_FAILURE;
+ }
+
+ chdir("/");
+ variable_set2(RUNNING_ON_ROOT, "yes", 0);
+
+ /* Configure various files in /etc */
+ if (DITEM_STATUS(configResolv(NULL)) == DITEM_FAILURE)
+ status = DITEM_FAILURE;
+ if (DITEM_STATUS(configFstab(NULL)) == DITEM_FAILURE)
+ status = DITEM_FAILURE;
+
+ /* stick a helpful shell over on the 4th VTY */
+ if (!variable_get(VAR_NO_HOLOSHELL))
+ systemCreateHoloshell();
+
+ alreadyDone = TRUE;
+ return status;
+}
+
+int
+installFixitHoloShell(dialogMenuItem *self)
+{
+ FixItMode = 1;
+ systemCreateHoloshell();
+ FixItMode = 0;
+ return DITEM_SUCCESS;
+}
+
+int
+installFixitCDROM(dialogMenuItem *self)
+{
+ struct stat sb;
+ int need_eject;
+
+ if (!RunningAsInit)
+ return DITEM_SUCCESS;
+
+ variable_set2(SYSTEM_STATE, "fixit", 0);
+ (void)unlink("/mnt2");
+ (void)rmdir("/mnt2");
+
+ need_eject = 0;
+ CDROMInitQuiet = 1;
+ while (1) {
+ if (need_eject)
+ msgConfirm(
+ "Please insert a FreeBSD live filesystem CD/DVD and press return");
+ if (DITEM_STATUS(mediaSetCDROM(NULL)) != DITEM_SUCCESS
+ || !DEVICE_INIT(mediaDevice)) {
+ /* If we can't initialize it, it's probably not a FreeBSD CDROM so punt on it */
+ mediaClose();
+ if (need_eject && msgYesNo("Unable to mount the disc. Do you want to try again?") != 0)
+ return DITEM_FAILURE;
+ } else if (!file_readable("/dist/rescue/ldconfig")) {
+ mediaClose();
+ if (need_eject &&
+ msgYesNo("Unable to find a FreeBSD live filesystem. Do you want to try again?") != 0)
+ return DITEM_FAILURE;
+ } else
+ break;
+ CDROMInitQuiet = 0;
+ need_eject = 1;
+ }
+ CDROMInitQuiet = 0;
+
+ /* Since the fixit code expects everything to be in /mnt2, and the CDROM mounting stuff /dist, do
+ * a little kludge dance here..
+ */
+ if (symlink("/dist", "/mnt2")) {
+ msgConfirm("Unable to symlink /mnt2 to the disc mount point. Please report this\n"
+ "unexpected failure to freebsd-bugs@FreeBSD.org.");
+ return DITEM_FAILURE;
+ }
+
+ /*
+ * If /tmp points to /mnt2/tmp from a previous fixit floppy session, it's
+ * not very good for us if we point it to the CDROM now. Rather make it
+ * a directory in the root MFS then. Experienced admins will still be
+ * able to mount their disk's /tmp over this if they need.
+ */
+ if (lstat("/tmp", &sb) == 0 && (sb.st_mode & S_IFMT) == S_IFLNK)
+ (void)unlink("/tmp");
+ Mkdir("/tmp");
+
+ /*
+ * Since setuid binaries ignore LD_LIBRARY_PATH, we indeed need the
+ * ld.so.hints file. Fortunately, it's fairly small (~ 3 KB).
+ */
+ if (!file_readable("/var/run/ld.so.hints")) {
+ Mkdir("/var/run");
+ if (vsystem("/mnt2/rescue/ldconfig -s /mnt2/lib /mnt2/usr/lib")) {
+ msgConfirm("Warning: ldconfig could not create the ld.so hints file.\n"
+ "Dynamic executables from the disc likely won't work.");
+ }
+ }
+
+ /* Yet more iggly hardcoded pathnames. */
+ Mkdir("/libexec");
+ if (!file_readable("/libexec/ld.so") && file_readable("/mnt2/libexec/ld.so")) {
+ if (symlink("/mnt2/libexec/ld.so", "/libexec/ld.so"))
+ msgDebug("Couldn't link to ld.so - not necessarily a problem for ELF\n");
+ }
+ if (!file_readable("/libexec/ld-elf.so.1")) {
+ if (symlink("/mnt2/libexec/ld-elf.so.1", "/libexec/ld-elf.so.1")) {
+ msgConfirm("Warning: could not create the symlink for ld-elf.so.1\n"
+ "Dynamic executables from the disc likely won't work.");
+ }
+ }
+ /* optional nicety */
+ if (!file_readable("/usr/bin/vi"))
+ symlink("/mnt2/usr/bin/vi", "/usr/bin/vi");
+ fixit_common();
+ mediaClose();
+ if (need_eject)
+ msgConfirm("Please remove the FreeBSD fixit CDROM/DVD now.");
+ return DITEM_SUCCESS;
+}
+
+int
+installFixitFloppy(dialogMenuItem *self)
+{
+ struct ufs_args args;
+ extern char *distWanted;
+
+ if (!RunningAsInit)
+ return DITEM_SUCCESS;
+
+ /* Try to open the floppy drive */
+ if (DITEM_STATUS(mediaSetFloppy(NULL)) == DITEM_FAILURE || !mediaDevice) {
+ msgConfirm("Unable to set media device to floppy.");
+ mediaClose();
+ return DITEM_FAILURE;
+ }
+
+ memset(&args, 0, sizeof(args));
+ args.fspec = mediaDevice->devname;
+ mediaDevice->private = "/mnt2";
+ distWanted = NULL;
+ Mkdir("/mnt2");
+
+ variable_set2(SYSTEM_STATE, "fixit", 0);
+
+ while (1) {
+ if (!DEVICE_INIT(mediaDevice)) {
+ if (msgYesNo("The attempt to mount the fixit floppy failed, bad floppy\n"
+ "or unclean filesystem. Do you want to try again?"))
+ return DITEM_FAILURE;
+ }
+ else
+ break;
+ }
+ if (!directory_exists("/tmp"))
+ (void)symlink("/mnt2/tmp", "/tmp");
+ fixit_common();
+ mediaClose();
+ msgConfirm("Please remove the fixit floppy now.");
+ return DITEM_SUCCESS;
+}
+
+/*
+ * The common code for both fixit variants.
+ */
+static void
+fixit_common(void)
+{
+ pid_t child;
+ int waitstatus;
+
+ if (!directory_exists("/var/tmp/vi.recover")) {
+ if (DITEM_STATUS(Mkdir("/var/tmp/vi.recover")) != DITEM_SUCCESS) {
+ msgConfirm("Warning: Was unable to create a /var/tmp/vi.recover directory.\n"
+ "vi will kvetch and moan about it as a result but should still\n"
+ "be essentially usable.");
+ }
+ }
+ if (!directory_exists("/bin"))
+ (void)Mkdir("/bin");
+ (void)symlink("/stand/sh", "/bin/sh");
+ /* Link the /etc/ files */
+ if (DITEM_STATUS(Mkdir("/etc")) != DITEM_SUCCESS)
+ msgConfirm("Unable to create an /etc directory! Things are weird on this floppy..");
+ else if ((symlink("/mnt2/etc/spwd.db", "/etc/spwd.db") == -1 && errno != EEXIST) ||
+ (symlink("/mnt2/etc/protocols", "/etc/protocols") == -1 && errno != EEXIST) ||
+ (symlink("/mnt2/etc/group", "/etc/group") == -1 && errno != EEXIST) ||
+ (symlink("/mnt2/etc/services", "/etc/services") == -1 && errno != EEXIST))
+ msgConfirm("Couldn't symlink the /etc/ files! I'm not sure I like this..");
+ if (!file_readable(TERMCAP_FILE))
+ create_termcap();
+ if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0)
+ systemSuspendDialog(); /* must be before the fork() */
+ if (!(child = fork())) {
+ int i, fd;
+ struct termios foo;
+ extern int login_tty(int);
+
+ ioctl(0, TIOCNOTTY, NULL);
+ for (i = getdtablesize(); i >= 0; --i)
+ close(i);
+
+ if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0)
+ fd = open("/dev/console", O_RDWR);
+ else
+ fd = open("/dev/ttyv3", O_RDWR);
+ ioctl(0, TIOCSCTTY, &fd);
+ dup2(0, 1);
+ dup2(0, 2);
+ DebugFD = 2;
+ if (login_tty(fd) == -1)
+ msgDebug("fixit: I can't set the controlling terminal.\n");
+
+ signal(SIGTTOU, SIG_IGN);
+ if (tcgetattr(0, &foo) != -1) {
+ foo.c_cc[VERASE] = '\010';
+ if (tcsetattr(0, TCSANOW, &foo) == -1)
+ msgDebug("fixit shell: Unable to set erase character.\n");
+ }
+ else
+ msgDebug("fixit shell: Unable to get terminal attributes!\n");
+ setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin:/stand:"
+ "/mnt2/stand:/mnt2/bin:/mnt2/sbin:/mnt2/usr/bin:/mnt2/usr/sbin", 1);
+ if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0) {
+ printf("Waiting for fixit shell to exit.\n"
+ "When you are done, type ``exit'' to exit\n"
+ "the fixit shell and be returned here.\n\n");
+ fflush(stdout);
+ } else {
+ ioctl(fd, VT_ACTIVATE, 0);
+ }
+
+ /* use the .profile from the fixit medium */
+ setenv("HOME", "/mnt2", 1);
+ chdir("/mnt2");
+ execlp("sh", "-sh", (char *)0);
+ msgDebug("fixit shell: Failed to execute shell!\n");
+ _exit(1);;
+ }
+ else {
+ if (strcmp(variable_get(VAR_FIXIT_TTY), "standard") == 0) {
+ dialog_clear_norefresh();
+ msgNotify("Waiting for fixit shell to exit. Go to VTY4 now by\n"
+ "typing ALT-F4. When you are done, type ``exit'' to exit\n"
+ "the fixit shell and be returned here.\n");
+ }
+ (void)waitpid(child, &waitstatus, 0);
+ if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0)
+ systemResumeDialog();
+ else if (OnVTY) {
+ ioctl(0, VT_ACTIVATE, 0);
+ msgInfo(NULL);
+ }
+ }
+ dialog_clear();
+}
+
+
+int
+installExpress(dialogMenuItem *self)
+{
+ int i;
+
+ dialog_clear_norefresh();
+ variable_set2(SYSTEM_STATE, "express", 0);
+#ifdef WITH_SLICES
+ if (DITEM_STATUS((i = diskPartitionEditor(self))) == DITEM_FAILURE)
+ return i;
+#endif
+
+ if (DITEM_STATUS((i = diskLabelEditor(self))) == DITEM_FAILURE)
+ return i;
+
+ if (DITEM_STATUS((i = installCommit(self))) == DITEM_SUCCESS) {
+ i |= DITEM_LEAVE_MENU;
+
+ /* Give user the option of one last configuration spree */
+ installConfigure();
+ }
+ return i;
+}
+
+/* Standard mode installation */
+int
+installStandard(dialogMenuItem *self)
+{
+ int i, tries = 0;
+ Device **devs;
+
+ variable_set2(SYSTEM_STATE, "standard", 0);
+ dialog_clear_norefresh();
+#ifdef WITH_SLICES
+ msgConfirm("In the next menu, you will need to set up a DOS-style (\"fdisk\") partitioning\n"
+ "scheme for your hard disk. If you simply wish to devote all disk space\n"
+ "to FreeBSD (overwriting anything else that might be on the disk(s) selected)\n"
+ "then use the (A)ll command to select the default partitioning scheme followed\n"
+ "by a (Q)uit. If you wish to allocate only free space to FreeBSD, move to a\n"
+ "partition marked \"unused\" and use the (C)reate command.");
+
+nodisks:
+ if (DITEM_STATUS(diskPartitionEditor(self)) == DITEM_FAILURE)
+ return DITEM_FAILURE;
+
+ if (diskGetSelectCount(&devs) <= 0 && tries < 3) {
+ msgConfirm("You need to select some disks to operate on! Be sure to use SPACE\n"
+ "instead of RETURN in the disk selection menu when selecting a disk.");
+ ++tries;
+ goto nodisks;
+ }
+
+ msgConfirm("Now you need to create BSD partitions inside of the fdisk partition(s)\n"
+ "just created. If you have a reasonable amount of disk space (1GB or more)\n"
+ "and don't have any special requirements, simply use the (A)uto command to\n"
+ "allocate space automatically. If you have more specific needs or just don't\n"
+ "care for the layout chosen by (A)uto, press F1 for more information on\n"
+ "manual layout.");
+#else
+ msgConfirm("First you need to create BSD partitions on the disk which you are\n"
+ "installing to. If you have a reasonable amount of disk space (1GB or more)\n"
+ "and don't have any special requirements, simply use the (A)uto command to\n"
+ "allocate space automatically. If you have more specific needs or just don't\n"
+ "care for the layout chosen by (A)uto, press F1 for more information on\n"
+ "manual layout.");
+#endif
+
+ if (DITEM_STATUS(diskLabelEditor(self)) == DITEM_FAILURE)
+ return DITEM_FAILURE;
+
+ if (DITEM_STATUS((i = installCommit(self))) == DITEM_FAILURE) {
+ dialog_clear();
+ msgConfirm("Installation completed with some errors. You may wish to\n"
+ "scroll through the debugging messages on VTY1 with the\n"
+ "scroll-lock feature. You can also choose \"No\" at the next\n"
+ "prompt and go back into the installation menus to retry\n"
+ "whichever operations have failed.");
+ return i;
+
+ }
+ else {
+ dialog_clear();
+ msgConfirm("Congratulations! You now have FreeBSD installed on your system.\n\n"
+ "We will now move on to the final configuration questions.\n"
+ "For any option you do not wish to configure, simply select\n"
+ "No.\n\n"
+ "If you wish to re-enter this utility after the system is up, you\n"
+ "may do so by typing: /usr/sbin/sysinstall.");
+ }
+ if (mediaDevice->type != DEVICE_TYPE_FTP && mediaDevice->type != DEVICE_TYPE_NFS) {
+ if (!msgYesNo("Would you like to configure any Ethernet or SLIP/PPP network devices?")) {
+ Device *tmp = tcpDeviceSelect();
+
+ if (tmp && !((DevInfo *)tmp->private)->use_dhcp && !msgYesNo("Would you like to bring the %s interface up right now?", tmp->name))
+ if (!DEVICE_INIT(tmp))
+ msgConfirm("Initialization of %s device failed.", tmp->name);
+ }
+ dialog_clear_norefresh();
+ }
+
+ if (!msgNoYes("Do you want this machine to function as a network gateway?"))
+ variable_set2("gateway_enable", "YES", 1);
+
+ dialog_clear_norefresh();
+ if (!msgNoYes("Do you want to configure inetd and the network services that it provides?"))
+ configInetd(self);
+
+ dialog_clear_norefresh();
+ if (!msgNoYes("Would you like to enable SSH login?"))
+ variable_set2("sshd_enable", "YES", 1);
+
+ dialog_clear_norefresh();
+ if (!msgNoYes("Do you want to have anonymous FTP access to this machine?"))
+ configAnonFTP(self);
+
+ dialog_clear_norefresh();
+ if (!msgNoYes("Do you want to configure this machine as an NFS server?"))
+ configNFSServer(self);
+
+ dialog_clear_norefresh();
+ if (!msgNoYes("Do you want to configure this machine as an NFS client?"))
+ variable_set2("nfs_client_enable", "YES", 1);
+
+#ifdef WITH_SYSCONS
+ dialog_clear_norefresh();
+ if (!msgNoYes("Would you like to customize your system console settings?"))
+ dmenuOpenSimple(&MenuSyscons, FALSE);
+#endif
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to set this machine's time zone now?"))
+ systemExecute("tzsetup");
+
+#ifdef WITH_MICE
+ dialog_clear_norefresh();
+ if (!msgNoYes("Does this system have a PS/2, serial, or bus mouse?"))
+ dmenuOpenSimple(&MenuMouse, FALSE);
+#endif
+
+#ifdef __i386__
+ if (checkLoaderACPI() != 0) {
+ dialog_clear_norefresh();
+ if (!msgNoYes("ACPI was disabled during boot.\n"
+ "Would you like to disable it permanently?"))
+ (void)configLoaderACPI(1 /*disable*/);
+ }
+#endif
+
+ /* Now would be a good time to checkpoint the configuration data */
+ configRC_conf();
+ sync();
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("The FreeBSD package collection is a collection of thousands of ready-to-run\n"
+ "applications, from text editors to games to WEB servers and more. Would you\n"
+ "like to browse the collection now?")) {
+ (void)configPackages(self);
+ }
+
+ if (!msgYesNo("Would you like to add any initial user accounts to the system?\n"
+ "Adding at least one account for yourself at this stage is suggested\n"
+ "since working as the \"root\" user is dangerous (it is easy to do\n"
+ "things which adversely affect the entire system)."))
+ (void)configUsers(self);
+
+ msgConfirm("Now you must set the system manager's password.\n"
+ "This is the password you'll use to log in as \"root\".");
+ if (!systemExecute("passwd root"))
+ variable_set2("root_password", "YES", 0);
+
+ /* XXX Put whatever other nice configuration questions you'd like to ask the user here XXX */
+
+ /* Give user the option of one last configuration spree */
+ dialog_clear_norefresh();
+ installConfigure();
+ return DITEM_LEAVE_MENU;
+}
+
+/* The version of commit we call from the Install Custom menu */
+int
+installCustomCommit(dialogMenuItem *self)
+{
+ int i;
+
+ i = installCommit(self);
+ if (DITEM_STATUS(i) == DITEM_SUCCESS) {
+ /* Give user the option of one last configuration spree */
+ installConfigure();
+ return i;
+ }
+ else
+ msgConfirm("The commit operation completed with errors. Not\n"
+ "updating /etc files.");
+ return i;
+}
+
+/*
+ * What happens when we finally decide to going ahead with the installation.
+ *
+ * This is broken into multiple stages so that the user can do a full
+ * installation but come back here again to load more distributions,
+ * perhaps from a different media type. This would allow, for
+ * example, the user to load the majority of the system from CDROM and
+ * then use ftp to load a different dist.
+ */
+int
+installCommit(dialogMenuItem *self)
+{
+ int i;
+ char *str;
+
+ dialog_clear_norefresh();
+ if (!Dists)
+ distConfig(NULL);
+
+ if (!Dists) {
+ (void)dmenuOpenSimple(&MenuDistributions, FALSE);
+ /* select reasonable defaults if necessary */
+ if (!Dists)
+ Dists = _DIST_USER;
+ if (!KernelDists)
+ KernelDists = selectKernel();
+ }
+
+ if (!mediaVerify())
+ return DITEM_FAILURE;
+
+ str = variable_get(SYSTEM_STATE);
+ if (isDebug())
+ msgDebug("installCommit: System state is `%s'\n", str);
+
+ /* Installation stuff we wouldn't do to a running system */
+ if (RunningAsInit && DITEM_STATUS((i = installInitial())) == DITEM_FAILURE)
+ return i;
+
+try_media:
+ if (!DEVICE_INIT(mediaDevice)) {
+ if (!msgYesNo("Unable to initialize selected media. Would you like to\n"
+ "adjust your media configuration and try again?")) {
+ mediaDevice = NULL;
+ if (!mediaVerify())
+ return DITEM_FAILURE;
+ else
+ goto try_media;
+ }
+ else
+ return DITEM_FAILURE;
+ }
+
+ /* Now go get it all */
+ i = distExtractAll(self);
+
+ /* When running as init, *now* it's safe to grab the rc.foo vars */
+ installEnvironment();
+
+ variable_set2(SYSTEM_STATE, DITEM_STATUS(i) == DITEM_FAILURE ? "error-install" : "full-install", 0);
+
+ return i;
+}
+
+static void
+installConfigure(void)
+{
+ /* Final menu of last resort */
+ if (!msgNoYes("Visit the general configuration menu for a chance to set\n"
+ "any last options?"))
+ dmenuOpenSimple(&MenuConfigure, FALSE);
+ configRC_conf();
+ sync();
+}
+
+int
+installFixupBase(dialogMenuItem *self)
+{
+ FILE *fp;
+#ifdef __ia64__
+ const char *efi_mntpt;
+#endif
+
+ /* All of this is done only as init, just to be safe */
+ if (RunningAsInit) {
+#if defined(__i386__) || defined(__amd64__)
+ if ((fp = fopen("/boot/loader.conf", "a")) != NULL) {
+ if (!OnVTY) {
+ fprintf(fp, "# -- sysinstall generated deltas -- #\n");
+ fprintf(fp, "console=\"comconsole\"\n");
+ }
+ fclose(fp);
+ }
+#endif
+
+ /* BOGON #2: We leave /etc in a bad state */
+ chmod("/etc", 0755);
+
+ /* BOGON #3: No /var/db/mountdtab complains */
+ Mkdir("/var/db");
+ creat("/var/db/mountdtab", 0644);
+
+ /* BOGON #4: /compat created by default in root fs */
+ Mkdir("/usr/compat");
+ vsystem("ln -s usr/compat /compat");
+
+ /* BOGON #5: aliases database not built for bin */
+ vsystem("newaliases");
+
+ /* BOGON #6: Remove /stand (finally) */
+ vsystem("rm -rf /stand");
+
+ /* Now run all the mtree stuff to fix things up */
+ vsystem("mtree -deU -f /etc/mtree/BSD.root.dist -p /");
+ vsystem("mtree -deU -f /etc/mtree/BSD.var.dist -p /var");
+ vsystem("mtree -deU -f /etc/mtree/BSD.usr.dist -p /usr");
+
+#ifdef __ia64__
+ /* Move /boot to the the EFI partition and make /boot a link to it. */
+ efi_mntpt = (EfiChunk != NULL) ? ((PartInfo *)EfiChunk->private_data)->mountpoint : NULL;
+ if (efi_mntpt != NULL) {
+ vsystem("if [ ! -L /boot ]; then mv /boot %s; fi", efi_mntpt);
+ vsystem("if [ ! -e /boot ]; then ln -sf %s/boot /boot; fi",
+ efi_mntpt + 1); /* Skip leading '/' */
+ /* Make sure the kernel knows which partition is the root file system. */
+ vsystem("echo 'vfs.root.mountfrom=\"ufs:/dev/%s\"' >> /boot/loader.conf", RootChunk->name);
+ }
+#endif
+
+ /* Do all the last ugly work-arounds here */
+ }
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+int
+installFixupKernel(dialogMenuItem *self, int dists)
+{
+
+ /* All of this is done only as init, just to be safe */
+ if (RunningAsInit) {
+ /*
+ * Install something as /boot/kernel. Prefer SMP
+ * over generic--this should handle the case where
+ * both SMP and GENERIC are installed (otherwise we
+ * select the one kernel that was installed).
+ *
+ * NB: we assume any existing kernel has been saved
+ * already and the /boot/kernel we remove is empty.
+ */
+ vsystem("rm -rf /boot/kernel");
+#if WITH_SMP
+ if (dists & DIST_KERNEL_SMP)
+ vsystem("mv /boot/SMP /boot/kernel");
+ else
+#endif
+ vsystem("mv /boot/GENERIC /boot/kernel");
+ }
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+#define QUEUE_YES 1
+#define QUEUE_NO 0
+static int
+performNewfs(PartInfo *pi, char *dname, int queue)
+{
+ char buffer[LINE_MAX];
+
+ if (pi->do_newfs) {
+ switch(pi->newfs_type) {
+ case NEWFS_UFS:
+ snprintf(buffer, LINE_MAX, "%s %s %s %s %s",
+ NEWFS_UFS_CMD,
+ pi->newfs_data.newfs_ufs.softupdates ? "-U" : "",
+ pi->newfs_data.newfs_ufs.ufs1 ? "-O1" : "-O2",
+ pi->newfs_data.newfs_ufs.user_options,
+ dname);
+ break;
+
+ case NEWFS_MSDOS:
+ snprintf(buffer, LINE_MAX, "%s %s", NEWFS_MSDOS_CMD,
+ dname);
+ break;
+
+ case NEWFS_CUSTOM:
+ snprintf(buffer, LINE_MAX, "%s %s",
+ pi->newfs_data.newfs_custom.command, dname);
+ break;
+ }
+
+ if (queue == QUEUE_YES) {
+ command_shell_add(pi->mountpoint, buffer);
+ return (0);
+ } else
+ return (vsystem(buffer));
+ }
+ return (0);
+}
+
+/* Go newfs and/or mount all the filesystems we've been asked to */
+int
+installFilesystems(dialogMenuItem *self)
+{
+ int i;
+ Disk *disk;
+ Chunk *c1, *c2;
+ Device **devs;
+ PartInfo *root;
+ char dname[80];
+ Boolean upgrade = FALSE;
+
+ /* If we've already done this, bail out */
+ if (!variable_cmp(DISK_LABELLED, "written"))
+ return DITEM_SUCCESS;
+
+ upgrade = !variable_cmp(SYSTEM_STATE, "upgrade");
+ if (!checkLabels(TRUE))
+ return DITEM_FAILURE;
+
+ root = (RootChunk != NULL) ? (PartInfo *)RootChunk->private_data : NULL;
+
+ command_clear();
+ if (SwapChunk && RunningAsInit) {
+ /* As the very first thing, try to get ourselves some swap space */
+ sprintf(dname, "/dev/%s", SwapChunk->name);
+ if (!Fake && !file_readable(dname)) {
+ msgConfirm("Unable to find device node for %s in /dev!\n"
+ "The creation of filesystems will be aborted.", dname);
+ return DITEM_FAILURE;
+ }
+
+ if (!Fake) {
+ if (!swapon(dname)) {
+ dialog_clear_norefresh();
+ msgNotify("Added %s as initial swap device", dname);
+ }
+ else {
+ msgConfirm("WARNING! Unable to swap to %s: %s\n"
+ "This may cause the installation to fail at some point\n"
+ "if you don't have a lot of memory.", dname, strerror(errno));
+ }
+ }
+ }
+
+ if (RootChunk && RunningAsInit) {
+ /* Next, create and/or mount the root device */
+ sprintf(dname, "/dev/%s", RootChunk->name);
+ if (!Fake && !file_readable(dname)) {
+ msgConfirm("Unable to make device node for %s in /dev!\n"
+ "The creation of filesystems will be aborted.", dname);
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+ if (strcmp(root->mountpoint, "/"))
+ msgConfirm("Warning: %s is marked as a root partition but is mounted on %s", RootChunk->name, root->mountpoint);
+
+ if (root->do_newfs && (!upgrade ||
+ !msgNoYes("You are upgrading - are you SURE you want to newfs "
+ "the root partition?"))) {
+ int i;
+
+ dialog_clear_norefresh();
+ msgNotify("Making a new root filesystem on %s", dname);
+ i = performNewfs(root, dname, QUEUE_NO);
+ if (i) {
+ msgConfirm("Unable to make new root filesystem on %s!\n"
+ "Command returned status %d", dname, i);
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+ }
+ else {
+ if (!upgrade) {
+ msgConfirm("Warning: Using existing root partition.");
+ }
+ dialog_clear_norefresh();
+ msgNotify("Checking integrity of existing %s filesystem.", dname);
+ i = vsystem("fsck_ffs -y %s", dname);
+ if (i)
+ msgConfirm("Warning: fsck returned status of %d for %s.\n"
+ "This partition may be unsafe to use.", i, dname);
+ }
+
+ /*
+ * If soft updates was enabled in the editor but we didn't newfs,
+ * use tunefs to update the soft updates flag on the file system.
+ */
+ if (!root->do_newfs && root->newfs_type == NEWFS_UFS &&
+ root->newfs_data.newfs_ufs.softupdates) {
+ i = vsystem("tunefs -n enable %s", dname);
+ if (i)
+ msgConfirm("Warning: Unable to enable soft updates"
+ " for root file system on %s", dname);
+ }
+
+ /* Switch to block device */
+ sprintf(dname, "/dev/%s", RootChunk->name);
+ if (Mount("/mnt", dname)) {
+ msgConfirm("Unable to mount the root file system on %s! Giving up.", dname);
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+
+ /* Mount devfs for other partitions to mount */
+ Mkdir("/mnt/dev");
+ if (!Fake) {
+ struct iovec iov[4];
+
+ iov[0].iov_base = "fstype";
+ iov[0].iov_len = strlen(iov[0].iov_base) + 1;
+ iov[1].iov_base = "devfs";
+ 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 = "/mnt/dev";
+ iov[3].iov_len = strlen(iov[3].iov_base) + 1;
+ i = nmount(iov, 4, 0);
+
+ if (i) {
+ dialog_clear_norefresh();
+ msgConfirm("Unable to mount DEVFS (error %d)", errno);
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+ }
+ }
+
+ /* Now buzz through the rest of the partitions and mount them too */
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ for (i = 0; devs[i]; i++) {
+ if (!devs[i]->enabled)
+ continue;
+
+ disk = (Disk *)devs[i]->private;
+ if (!disk->chunks) {
+ msgConfirm("No chunk list found for %s!", disk->name);
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+ for (c1 = disk->chunks->part; c1; c1 = c1->next) {
+#ifdef __ia64__
+ if (c1->type == part) {
+ c2 = c1;
+ {
+#elif defined(__powerpc__)
+ if (c1->type == apple) {
+ for (c2 = c1->part; c2; c2 = c2->next) {
+#else
+ if (c1->type == freebsd) {
+ for (c2 = c1->part; c2; c2 = c2->next) {
+#endif
+ if (c2->type == part && c2->subtype != FS_SWAP && c2->private_data) {
+ PartInfo *tmp = (PartInfo *)c2->private_data;
+
+ /* Already did root */
+ if (c2 == RootChunk)
+ continue;
+
+ sprintf(dname, "%s/dev/%s",
+ RunningAsInit ? "/mnt" : "", c2->name);
+
+ if (tmp->do_newfs && (!upgrade ||
+ !msgNoYes("You are upgrading - are you SURE you"
+ " want to newfs /dev/%s?", c2->name)))
+ performNewfs(tmp, dname, QUEUE_YES);
+ else
+ command_shell_add(tmp->mountpoint,
+ "fsck_ffs -y %s/dev/%s", RunningAsInit ?
+ "/mnt" : "", c2->name);
+#if 0
+ if (tmp->soft)
+ command_shell_add(tmp->mountpoint,
+ "tunefs -n enable %s/dev/%s", RunningAsInit ?
+ "/mnt" : "", c2->name);
+#endif
+ command_func_add(tmp->mountpoint, Mount, c2->name);
+ }
+ else if (c2->type == part && c2->subtype == FS_SWAP) {
+ char fname[80];
+ int i;
+
+ if (c2 == SwapChunk)
+ continue;
+ sprintf(fname, "%s/dev/%s", RunningAsInit ? "/mnt" : "", c2->name);
+ i = (Fake || swapon(fname));
+ if (!i) {
+ dialog_clear_norefresh();
+ msgNotify("Added %s as an additional swap device", fname);
+ }
+ else {
+ msgConfirm("Unable to add %s as a swap device: %s", fname, strerror(errno));
+ }
+ }
+ }
+ }
+ else if (c1->type == fat && c1->private_data &&
+ (root->do_newfs || upgrade)) {
+ char name[FILENAME_MAX];
+
+ sprintf(name, "%s/%s", RunningAsInit ? "/mnt" : "", ((PartInfo *)c1->private_data)->mountpoint);
+ Mkdir(name);
+ }
+#if defined(__ia64__)
+ else if (c1->type == efi && c1->private_data) {
+ char bootdir[FILENAME_MAX];
+ PartInfo *pi = (PartInfo *)c1->private_data;
+ char *p;
+
+ sprintf(dname, "%s/dev/%s", RunningAsInit ? "/mnt" : "",
+ c1->name);
+
+ if (pi->do_newfs && (!upgrade ||
+ !msgNoYes("You are upgrading - are you SURE you want to "
+ "newfs /dev/%s?", c1->name)))
+ performNewfs(pi, dname, QUEUE_YES);
+
+ command_func_add(pi->mountpoint, Mount_msdosfs, c1->name);
+ }
+#endif
+ }
+ }
+
+ command_sort();
+ command_execute();
+ dialog_clear_norefresh();
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+/* Initialize various user-settable values to their defaults */
+int
+installVarDefaults(dialogMenuItem *self)
+{
+ char *cp, ncpus[10];
+
+ /* Set default startup options */
+ cp = getsysctlbyname("kern.osrelease");
+ variable_set2(VAR_RELNAME, cp, 0);
+ free(cp);
+ variable_set2(VAR_CPIO_VERBOSITY, "high", 0);
+ variable_set2(VAR_INSTALL_ROOT, "/", 0);
+ variable_set2(VAR_INSTALL_CFG, "install.cfg", 0);
+ cp = getenv("EDITOR");
+ if (!cp)
+ cp = "/usr/bin/ee";
+ variable_set2(VAR_EDITOR, cp, 0);
+ variable_set2(VAR_FTP_USER, "ftp", 0);
+ variable_set2(VAR_BROWSER_PACKAGE, "links", 0);
+ variable_set2(VAR_BROWSER_BINARY, "/usr/local/bin/links", 0);
+ variable_set2(VAR_FTP_STATE, "passive", 0);
+ variable_set2(VAR_NFS_SECURE, "NO", -1);
+ variable_set2(VAR_NFS_TCP, "NO", -1);
+ variable_set2(VAR_NFS_V3, "YES", -1);
+ if (OnVTY)
+ variable_set2(VAR_FIXIT_TTY, "standard", 0);
+ else
+ variable_set2(VAR_FIXIT_TTY, "serial", 0);
+ variable_set2(VAR_PKG_TMPDIR, "/var/tmp", 0);
+ variable_set2(VAR_MEDIA_TIMEOUT, itoa(MEDIA_TIMEOUT), 0);
+ if (getpid() != 1)
+ variable_set2(SYSTEM_STATE, "update", 0);
+ else
+ variable_set2(SYSTEM_STATE, "init", 0);
+ variable_set2(VAR_NEWFS_ARGS, "-b 16384 -f 2048", 0);
+ variable_set2(VAR_CONSTERM, "NO", 0);
+#if (defined(__i386__) && !defined(PC98)) || defined(__amd64__)
+ NCpus = acpi_detect();
+ if (NCpus == -1)
+ NCpus = biosmptable_detect();
+#endif
+ if (NCpus <= 0)
+ NCpus = 1;
+ snprintf(ncpus, sizeof(ncpus), "%u", NCpus);
+ variable_set2(VAR_NCPUS, ncpus, 0);
+ return DITEM_SUCCESS;
+}
+
+/* Load the environment up from various system configuration files */
+void
+installEnvironment(void)
+{
+ configEnvironmentRC_conf();
+ if (file_readable("/etc/resolv.conf"))
+ configEnvironmentResolv("/etc/resolv.conf");
+}
+
+/* Copy the boot floppy contents into /stand */
+Boolean
+copySelf(void)
+{
+ int i;
+
+ if (file_readable("/boot.help"))
+ vsystem("cp /boot.help /mnt");
+ msgWeHaveOutput("Copying the boot floppy to /stand on root filesystem");
+ i = vsystem("find -x /stand | cpio %s -pdum /mnt", cpioVerbosity());
+ if (i) {
+ msgConfirm("Copy returned error status of %d!", i);
+ return FALSE;
+ }
+
+ /* Copy the /etc files into their rightful place */
+ if (vsystem("cd /mnt/stand; find etc | cpio %s -pdum /mnt", cpioVerbosity())) {
+ msgConfirm("Couldn't copy up the /etc files!");
+ return TRUE;
+ }
+ return TRUE;
+}
+
+static void
+create_termcap(void)
+{
+ FILE *fp;
+
+ const char *caps[] = {
+ termcap_vt100, termcap_cons25, termcap_cons25_m, termcap_cons25r,
+ termcap_cons25r_m, termcap_cons25l1, termcap_cons25l1_m,
+ termcap_xterm, NULL,
+ };
+ const char **cp;
+
+ if (!file_readable(TERMCAP_FILE)) {
+ Mkdir("/usr/share/misc");
+ fp = fopen(TERMCAP_FILE, "w");
+ if (!fp) {
+ msgConfirm("Unable to initialize termcap file. Some screen-oriented\nutilities may not work.");
+ return;
+ }
+ cp = caps;
+ while (*cp)
+ fprintf(fp, "%s\n", *(cp++));
+ fclose(fp);
+ }
+}
diff --git a/usr.sbin/sysinstall/install.cfg b/usr.sbin/sysinstall/install.cfg
new file mode 100644
index 0000000..d894f06
--- /dev/null
+++ b/usr.sbin/sysinstall/install.cfg
@@ -0,0 +1,97 @@
+# This is a sample installation configuration file for my test machine,
+# crate.cdrom.com.
+# It is included here merely as a sort-of-documented example.
+#
+# $FreeBSD$
+
+# Turn on extra debugging.
+debug=yes
+
+################################
+# My host specific data
+hostname=crate.cdrom.com
+domainname=cdrom.com
+nameserver=204.216.27.3
+defaultrouter=204.216.27.228
+ipaddr=204.216.27.230
+netmask=255.255.255.240
+################################
+
+################################
+# Which installation device to use - ftp is pointed directly at my local
+# machine and the installation device is my WD8013 ethernet interface.
+_ftpPath=ftp://time.cdrom.com/pub
+netDev=ed0
+mediaSetFTP
+################################
+
+################################
+# Select which distributions we want.
+dists=base doc manpages info src sbase ssys kernels GENERIC
+distSetCustom
+################################
+
+################################
+# Now set the parameters for the partition editor on ad0. Set to use the
+# disk exclusively (could also be "all" to use the whole disk but
+# respecting the MBR or "free" to use only unallocated space for FreeBSD).
+disk=ad0
+partition=exclusive
+diskPartitionEditor
+
+# Uncomment this instead to use only the free space and install boot manager.
+#partition=free
+#bootManager=booteasy
+#diskPartitionEditor
+################################
+
+################################
+
+# Disk partitioning.
+# All sizes are expressed in 512 byte blocks!
+
+# A 512MB root partition
+ad0s1-1=ufs 1048576 /
+# And a 512MB swap partition
+ad0s1-2=swap 1048576 none
+# Followed by a /usr partition using all remaining space (size 0 = free space)
+# and with softupdates enabled (non-zero arg following mountpoint).
+ad0s1-3=ufs 0 /usr 1
+# Let's do it!
+diskLabelEditor
+
+################################
+
+################################
+# Now partition the 2nd disk with a 1GB /var and /usr/src using the
+# remainder of the disk.
+disk=ad1
+partition=exclusive
+diskPartitionEditor
+
+ad1s1-1=ufs 2097152 /var
+ad1s1-2=ufs 0 /usr/src
+diskLabelEditor
+################################
+
+################################
+# And the 3rd, adding a second 512MB of swap and the rest of the disk
+# for /tmp.
+disk=da0
+partition=exclusive
+diskPartitionEditor
+
+da0s1-1=swap 1048576 none
+da0s1-2=ufs 0 /tmp
+diskLabelEditor
+################################
+
+
+# OK, everything is set. Do it!
+installCommit
+
+# Install some packages at the end.
+package=bash-3.1.17
+packageAdd
+package=ncftp-3.2.0
+packageAdd
diff --git a/usr.sbin/sysinstall/installUpgrade.c b/usr.sbin/sysinstall/installUpgrade.c
new file mode 100644
index 0000000..1844c31
--- /dev/null
+++ b/usr.sbin/sysinstall/installUpgrade.c
@@ -0,0 +1,526 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sysinstall.h"
+#include <sys/disklabel.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/mount.h>
+
+static int installUpgradeNonInteractive(dialogMenuItem *self);
+
+typedef struct _hitList {
+ enum { JUST_COPY, CALL_HANDLER } action ;
+ char *name;
+ Boolean optional;
+ void (*handler)(struct _hitList *self);
+} HitList;
+
+/* These are the only meaningful files I know about */
+static HitList etc_files [] = {
+ { JUST_COPY, "Xaccel.ini", TRUE, NULL },
+ { JUST_COPY, "X11", TRUE, NULL },
+ { JUST_COPY, "adduser.conf", TRUE, NULL },
+ { JUST_COPY, "aliases", TRUE, NULL },
+ { JUST_COPY, "aliases.db", TRUE, NULL },
+ { JUST_COPY, "amd.map", TRUE, NULL },
+ { JUST_COPY, "auth.conf", TRUE, NULL },
+ { JUST_COPY, "crontab", TRUE, NULL },
+ { JUST_COPY, "csh.cshrc", TRUE, NULL },
+ { JUST_COPY, "csh.login", TRUE, NULL },
+ { JUST_COPY, "csh.logout", TRUE, NULL },
+ { JUST_COPY, "cvsupfile", TRUE, NULL },
+ { JUST_COPY, "devfs.conf", TRUE, NULL },
+ { JUST_COPY, "dhclient.conf", TRUE, NULL },
+ { JUST_COPY, "disktab", TRUE, NULL },
+ { JUST_COPY, "dumpdates", TRUE, NULL },
+ { JUST_COPY, "exports", TRUE, NULL },
+ { JUST_COPY, "fbtab", TRUE, NULL },
+ { JUST_COPY, "fstab", FALSE, NULL },
+ { JUST_COPY, "ftpusers", TRUE, NULL },
+ { JUST_COPY, "gettytab", TRUE, NULL },
+ { JUST_COPY, "gnats", TRUE, NULL },
+ { JUST_COPY, "group", FALSE, NULL },
+ { JUST_COPY, "hosts", TRUE, NULL },
+ { JUST_COPY, "hosts.allow", TRUE, NULL },
+ { JUST_COPY, "hosts.equiv", TRUE, NULL },
+ { JUST_COPY, "hosts.lpd", TRUE, NULL },
+ { JUST_COPY, "inetd.conf", TRUE, NULL },
+ { JUST_COPY, "localtime", TRUE, NULL },
+ { JUST_COPY, "login.access", TRUE, NULL },
+ { JUST_COPY, "login.conf", TRUE, NULL },
+ { JUST_COPY, "mail", TRUE, NULL },
+ { JUST_COPY, "mail.rc", TRUE, NULL },
+ { JUST_COPY, "mac.conf", TRUE, NULL },
+ { JUST_COPY, "make.conf", TRUE, NULL },
+ { JUST_COPY, "manpath.config", TRUE, NULL },
+ { JUST_COPY, "master.passwd", FALSE, NULL },
+ { JUST_COPY, "mergemaster.rc", TRUE, NULL },
+ { JUST_COPY, "motd", TRUE, NULL },
+ { JUST_COPY, "namedb", TRUE, NULL },
+ { JUST_COPY, "networks", TRUE, NULL },
+ { JUST_COPY, "newsyslog.conf", TRUE, NULL },
+ { JUST_COPY, "nsmb.conf", TRUE, NULL },
+ { JUST_COPY, "nsswitch.conf", TRUE, NULL },
+ { JUST_COPY, "ntp.conf", TRUE, NULL },
+ { JUST_COPY, "pam.conf", TRUE, NULL },
+ { JUST_COPY, "passwd", TRUE, NULL },
+ { JUST_COPY, "periodic", TRUE, NULL },
+ { JUST_COPY, "pf.conf", TRUE, NULL },
+ { JUST_COPY, "portsnap.conf", TRUE, NULL },
+ { JUST_COPY, "ppp", TRUE, NULL },
+ { JUST_COPY, "printcap", TRUE, NULL },
+ { JUST_COPY, "profile", TRUE, NULL },
+ { JUST_COPY, "protocols", TRUE, NULL },
+ { JUST_COPY, "pwd.db", TRUE, NULL },
+ { JUST_COPY, "rc.local", TRUE, NULL },
+ { JUST_COPY, "rc.firewall", TRUE, NULL },
+ { JUST_COPY, "rc.conf.local", TRUE, NULL },
+ { JUST_COPY, "remote", TRUE, NULL },
+ { JUST_COPY, "resolv.conf", TRUE, NULL },
+ { JUST_COPY, "rmt", TRUE, NULL },
+ { JUST_COPY, "sendmail.cf", TRUE, NULL },
+ { JUST_COPY, "sendmail.cw", TRUE, NULL },
+ { JUST_COPY, "services", TRUE, NULL },
+ { JUST_COPY, "shells", TRUE, NULL },
+ { JUST_COPY, "skeykeys", TRUE, NULL },
+ { JUST_COPY, "snmpd.config", TRUE, NULL },
+ { JUST_COPY, "spwd.db", TRUE, NULL },
+ { JUST_COPY, "src.conf", TRUE, NULL },
+ { JUST_COPY, "ssh", TRUE, NULL },
+ { JUST_COPY, "sysctl.conf", TRUE, NULL },
+ { JUST_COPY, "syslog.conf", TRUE, NULL },
+ { JUST_COPY, "ttys", TRUE, NULL },
+ { 0, NULL, FALSE, NULL },
+};
+
+static void
+traverseHitlist(HitList *h)
+{
+ system("rm -rf /etc/upgrade");
+ Mkdir("/etc/upgrade");
+ while (h->name) {
+ if (!file_readable(h->name)) {
+ if (!h->optional)
+ msgConfirm("Unable to find an old /etc/%s file! That is decidedly non-standard and\n"
+ "your upgraded system may function a little strangely as a result.", h->name);
+ }
+ else {
+ if (h->action == JUST_COPY) {
+ /* Move the just-loaded copy aside */
+ vsystem("mv /etc/%s /etc/upgrade/%s", h->name, h->name);
+
+ /* Copy the old one into its place */
+ msgNotify("Resurrecting %s..", h->name);
+ /* Do this with tar so that symlinks and such are preserved */
+ if (vsystem("tar cf - %s | tar xpf - -C /etc", h->name))
+ msgConfirm("Unable to resurrect your old /etc/%s! Hmmmm.", h->name);
+ }
+ else /* call handler */
+ h->handler(h);
+ }
+ ++h;
+ }
+}
+
+int
+installUpgrade(dialogMenuItem *self)
+{
+ char saved_etc[FILENAME_MAX];
+ Boolean extractingBin = TRUE;
+
+ if (variable_get(VAR_NONINTERACTIVE))
+ return installUpgradeNonInteractive(self);
+
+ variable_set2(SYSTEM_STATE, "upgrade", 0);
+ dialog_clear();
+
+ if (msgYesNo("Before beginning a binary upgrade, please review the upgrade instructions,\n"
+ "which are located in the \"Install\" document under the main documentation\n"
+ "menu. Given that you have read these instructions and understand the risks\n"
+ "and precautions involved, are you sure that you want to proceed with\n"
+ "this upgrade?") != 0)
+ return DITEM_FAILURE;
+
+ if (!Dists) {
+ msgConfirm("First, you must select some distribution components. The upgrade procedure\n"
+ "will only upgrade the distributions you select in the next set of menus.");
+ if (!dmenuOpenSimple(&MenuDistributions, FALSE) || !Dists)
+ return DITEM_FAILURE;
+ }
+ else if (!(Dists & DIST_BASE)) { /* No base selected? Not much of an upgrade.. */
+ if (msgYesNo("You didn't select the base distribution as one of the distributons to load.\n"
+ "This one is pretty vital to a successful upgrade. Are you SURE you don't\n"
+ "want to select the base distribution? Chose No to bring up the Distributions\n"
+ "menu again.") != 0) {
+ if (!dmenuOpenSimple(&MenuDistributions, FALSE))
+ return DITEM_FAILURE;
+ }
+ }
+
+ /* Still?! OK! They must know what they're doing.. */
+ if (!(Dists & DIST_BASE))
+ extractingBin = FALSE;
+
+ if (RunningAsInit) {
+ Device **devs;
+ int i, cnt;
+ char *cp;
+
+ cp = variable_get(VAR_DISK);
+ devs = deviceFind(cp, DEVICE_TYPE_DISK);
+ cnt = deviceCount(devs);
+ if (!cnt) {
+ msgConfirm("No disks found! Please verify that your disk controller is being\n"
+ "properly probed at boot time. See the Hardware Guide on the\n"
+ "Documentation menu for clues on diagnosing this type of problem.");
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+ else {
+ /* Enable all the drives before we start */
+ for (i = 0; i < cnt; i++)
+ devs[i]->enabled = TRUE;
+ }
+
+ msgConfirm("OK. First, we're going to go to the disk label editor. In this editor\n"
+ "you will be expected to Mount any partitions you're interested in\n"
+ "upgrading. DO NOT set the Newfs flag to Y on anything in the label editor\n"
+ "unless you're absolutely sure you know what you're doing! In this\n"
+ "instance, you'll be using the label editor as little more than a fancy\n"
+ "screen-oriented partition mounting tool.\n\n"
+ "Once you're done in the label editor, press Q to return here for the next\n"
+ "step.");
+
+ if (DITEM_STATUS(diskLabelEditor(self)) == DITEM_FAILURE) {
+ msgConfirm("The disk label editor returned an error status. Upgrade operation\n"
+ "aborted.");
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+
+ /* Don't write out MBR info */
+ variable_set2(DISK_PARTITIONED, "written", 0);
+ if (DITEM_STATUS(diskLabelCommit(self)) == DITEM_FAILURE) {
+ msgConfirm("Not all file systems were properly mounted. Upgrade operation\n"
+ "aborted.");
+ variable_unset(DISK_PARTITIONED);
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+
+ msgNotify("Updating /stand on root filesystem");
+ (void)vsystem("find -x /stand | cpio %s -pdum /mnt", cpioVerbosity());
+
+ if (DITEM_STATUS(chroot("/mnt")) == DITEM_FAILURE) {
+ msgConfirm("Unable to chroot to /mnt - something is wrong with the\n"
+ "root partition or the way it's mounted if this doesn't work.");
+ variable_unset(DISK_PARTITIONED);
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+ chdir("/");
+ installEnvironment();
+ systemCreateHoloshell();
+ }
+
+ saved_etc[0] = '\0';
+
+ /* Don't allow sources to be upgraded if we have src already */
+ if (directory_exists("/usr/src/") && (Dists & DIST_SRC)) {
+ Dists &= ~DIST_SRC;
+ SrcDists = 0;
+ msgConfirm("Warning: /usr/src exists and sources were selected as upgrade\n"
+ "targets. Unfortunately, this is not the way to upgrade your\n"
+ "sources - please use CTM or CVSup or some other method which\n"
+ "handles ``deletion events'', unlike this particular feature.\n\n"
+ "Your existing /usr/src will not be affected by this upgrade.\n");
+ }
+
+ if (extractingBin) {
+ while (!*saved_etc) {
+ char *cp = msgGetInput("/var/tmp/etc", "Under which directory do you wish to save your current /etc?");
+
+ if (!cp || !*cp || Mkdir(cp)) {
+ if (msgYesNo("Directory was not specified, was invalid or user selected Cancel.\n\n"
+ "Doing an upgrade without first backing up your /etc directory is a very\n"
+ "bad idea! Do you want to go back and specify the save directory again?") != 0)
+ break;
+ }
+ else {
+ SAFE_STRCPY(saved_etc, cp);
+ }
+ }
+
+ if (saved_etc[0]) {
+ msgNotify("Preserving /etc directory..");
+ if (vsystem("tar -cBpf - -C /etc . | tar --unlink -xBpf - -C %s", saved_etc))
+ if (msgYesNo("Unable to backup your /etc into %s.\n"
+ "Do you want to continue anyway?", saved_etc) != 0)
+ return DITEM_FAILURE;
+ msgNotify("Preserving /root directory..");
+ vsystem("tar -cBpf - -C / root | tar --unlink -xBpf - -C %s", saved_etc);
+ }
+
+ msgNotify("chflags'ing old binaries - please wait.");
+ (void)vsystem("chflags -R noschg /bin /sbin /lib /libexec /usr/bin /usr/sbin /usr/lib /usr/libexec /var/empty /boot/kernel*");
+
+ if (directory_exists("/boot/kernel")) {
+ if (directory_exists("/boot/kernel.prev")) {
+ msgNotify("Removing /boot/kernel.prev");
+ if (system("rm -fr /boot/kernel.prev")) {
+ msgConfirm("NOTICE: I'm trying to back up /boot/kernel to\n"
+ "/boot/kernel.prev, but /boot/kernel.prev exists and I\n"
+ "can't remove it. This means that the backup will, in\n"
+ "all probability, fail.");
+ }
+ }
+ msgNotify("Moving old kernel to /boot/kernel.prev");
+ if (system("mv /boot/kernel /boot/kernel.prev")) {
+ if (!msgYesNo("Hmmm! I couldn't move the old kernel over! Do you want to\n"
+ "treat this as a big problem and abort the upgrade? Due to the\n"
+ "way that this upgrade process works, you will have to reboot\n"
+ "and start over from the beginning. Select Yes to reboot now"))
+ systemShutdown(1);
+ }
+ else
+ msgConfirm("NOTICE: Your old kernel is in /boot/kernel.prev should this\n"
+ "upgrade fail for any reason and you need to boot your old\n"
+ "kernel.");
+ }
+ }
+
+media:
+ /* We do this very late, but we unfortunately need to back up /etc first */
+ if (!mediaVerify())
+ return DITEM_FAILURE;
+
+ if (!DEVICE_INIT(mediaDevice)) {
+ if (!msgYesNo("Couldn't initialize the media. Would you like\n"
+ "to adjust your media selection and try again?")) {
+ mediaDevice = NULL;
+ goto media;
+ }
+ else
+ return DITEM_FAILURE | DITEM_REDRAW | DITEM_RESTORE;
+ }
+
+ msgNotify("Beginning extraction of distributions.");
+ if (DITEM_STATUS(distExtractAll(self)) == DITEM_FAILURE) {
+ msgConfirm("Hmmmm. We couldn't even extract the base distribution. This upgrade\n"
+ "should be considered a failure and started from the beginning, sorry!\n"
+ "The system will reboot now.");
+ dialog_clear();
+ systemShutdown(1);
+ }
+ else if (Dists) {
+ if (!extractingBin || !(Dists & DIST_BASE)) {
+ msgNotify("The extraction process seems to have had some problems, but we got most\n"
+ "of the essentials. We'll treat this as a warning since it may have been\n"
+ "only non-essential distributions which failed to load.");
+ }
+ else {
+ msgConfirm("Hmmmm. We couldn't even extract the base distribution. This upgrade\n"
+ "should be considered a failure and started from the beginning, sorry!\n"
+ "The system will reboot now.");
+ dialog_clear();
+ systemShutdown(1);
+ }
+ }
+
+ if (extractingBin)
+ vsystem("disklabel -B `awk '$2~/\\/$/ {print substr($1, 6, 5)}' /etc/fstab`");
+ msgNotify("First stage of upgrade completed successfully!\n\n"
+ "Next comes stage 2, where we attempt to resurrect your /etc\n"
+ "directory!");
+
+ if (saved_etc && chdir(saved_etc)) {
+ msgConfirm("Unable to go to your saved /etc directory in %s?! Argh!\n"
+ "Something went seriously wrong! It's quite possible that\n"
+ "your former /etc is toast. I hope you didn't have any\n"
+ "important customizations you wanted to keep in there.. :(", saved_etc);
+ }
+ else {
+ /* Now try to resurrect the /etc files */
+ traverseHitlist(etc_files);
+ /* Resurrect the root dotfiles */
+ vsystem("tar -cBpf - root | tar -xBpf - -C / && rm -rf root");
+ }
+
+ msgConfirm("Upgrade completed! All of your old /etc files have been restored.\n"
+ "For your reference, the new /etc files are in /etc/upgrade/ in case\n"
+ "you wish to upgrade these files by hand (though that should not be\n"
+ "strictly necessary). If your root partition is specified in /etc/fstab\n"
+ "using the old \"compatibility\" slice, you may also wish to update it to\n"
+ "use a fully qualified slice name in order to avoid warnings on startup.\n\n"
+ "When you're ready to reboot into the new system, simply exit the installation.");
+ return DITEM_SUCCESS | DITEM_REDRAW | DITEM_RESTORE;
+}
+
+static int
+installUpgradeNonInteractive(dialogMenuItem *self)
+{
+ char *saved_etc;
+ Boolean extractingBin = TRUE;
+
+ variable_set2(SYSTEM_STATE, "upgrade", 0);
+
+ /* Make sure at least BIN is selected */
+ Dists |= DIST_BASE;
+
+ if (RunningAsInit) {
+ Device **devs;
+ int i, cnt;
+ char *cp;
+
+ cp = variable_get(VAR_DISK);
+ devs = deviceFind(cp, DEVICE_TYPE_DISK);
+ cnt = deviceCount(devs);
+ if (!cnt) {
+ msgConfirm("No disks found! Please verify that your disk controller is being\n"
+ "properly probed at boot time. See the Hardware Guide on the\n"
+ "Documentation menu for clues on diagnosing this type of problem.");
+ return DITEM_FAILURE;
+ }
+ else {
+ /* Enable all the drives before we start */
+ for (i = 0; i < cnt; i++)
+ devs[i]->enabled = TRUE;
+ }
+
+ msgConfirm("OK. First, we're going to go to the disk label editor. In this editor\n"
+ "you will be expected to Mount any partitions you're interested in\n"
+ "upgrading. DO NOT set the Newfs flag to Y on anything in the label editor\n"
+ "unless you're absolutely sure you know what you're doing! In this\n"
+ "instance, you'll be using the label editor as little more than a fancy\n"
+ "screen-oriented partition mounting tool.\n\n"
+ "Once you're done in the label editor, press Q to return here for the next\n"
+ "step.");
+
+ if (DITEM_STATUS(diskLabelEditor(self)) == DITEM_FAILURE) {
+ msgConfirm("The disk label editor returned an error status. Upgrade operation\n"
+ "aborted.");
+ return DITEM_FAILURE;
+ }
+
+ /* Don't write out MBR info */
+ variable_set2(DISK_PARTITIONED, "written", 0);
+ if (DITEM_STATUS(diskLabelCommit(self)) == DITEM_FAILURE) {
+ msgConfirm("Not all file systems were properly mounted. Upgrade operation\n"
+ "aborted.");
+ variable_unset(DISK_PARTITIONED);
+ return DITEM_FAILURE;
+ }
+
+ if (extractingBin) {
+ msgNotify("chflags'ing old binaries - please wait.");
+ (void)vsystem("chflags -R noschg /mnt/");
+ }
+ msgNotify("Updating /stand on root filesystem");
+ (void)vsystem("find -x /stand | cpio %s -pdum /mnt", cpioVerbosity());
+
+ if (DITEM_STATUS(chroot("/mnt")) == DITEM_FAILURE) {
+ msgConfirm("Unable to chroot to /mnt - something is wrong with the\n"
+ "root partition or the way it's mounted if this doesn't work.");
+ variable_unset(DISK_PARTITIONED);
+ return DITEM_FAILURE;
+ }
+ chdir("/");
+ systemCreateHoloshell();
+ }
+
+ if (!mediaVerify() || !DEVICE_INIT(mediaDevice)) {
+ msgNotify("Upgrade: Couldn't initialize media.");
+ return DITEM_FAILURE;
+ }
+
+ saved_etc = "/var/tmp/etc";
+ Mkdir(saved_etc);
+ msgNotify("Preserving /etc directory..");
+ if (vsystem("tar -cpBf - -C /etc . | tar -xpBf - -C %s", saved_etc)) {
+ msgNotify("Unable to backup your /etc into %s.", saved_etc);
+ return DITEM_FAILURE;
+ }
+
+ /*
+ * Back up the old kernel, leaving it in place in case we
+ * crash and reboot.
+ */
+ if (directory_exists("/boot/kernel")) {
+ if (directory_exists("/boot/kernel.prev")) {
+ msgNotify("Removing /boot/kernel.prev");
+ if (system("rm -fr /boot/kernel.prev")) {
+ msgConfirm("NOTICE: I'm trying to back up /boot/kernel to\n"
+ "/boot/kernel.prev, but /boot/kernel.prev exists and I\n"
+ "can't remove it. This means that the backup will, in\n"
+ "all probability, fail.");
+ }
+ }
+ msgNotify("Copying old kernel to /boot/kernel.prev");
+ vsystem("cp -Rp /boot/kernel /boot/kernel.prev");
+ }
+
+ msgNotify("Beginning extraction of distributions.");
+ if (DITEM_STATUS(distExtractAll(self)) == DITEM_FAILURE) {
+ msgConfirm("Hmmmm. We couldn't even extract the base distribution. This upgrade\n"
+ "should be considered a failure and started from the beginning, sorry!\n"
+ "The system will reboot now.");
+ dialog_clear();
+ systemShutdown(1);
+ }
+ else if (Dists) {
+ if (!(Dists & DIST_BASE)) {
+ msgNotify("The extraction process seems to have had some problems, but we got most\n"
+ "of the essentials. We'll treat this as a warning since it may have been\n"
+ "only non-essential distributions which failed to upgrade.");
+ }
+ else {
+ msgConfirm("Hmmmm. We couldn't even extract the base distribution. This upgrade\n"
+ "should be considered a failure and started from the beginning, sorry!\n"
+ "The system will reboot now.");
+ dialog_clear();
+ systemShutdown(1);
+ }
+ }
+
+ msgNotify("First stage of upgrade completed successfully.");
+ if (vsystem("tar -cpBf - -C %s . | tar --unlink -xpBf - -C /etc", saved_etc)) {
+ msgNotify("Unable to resurrect your old /etc!");
+ return DITEM_FAILURE;
+ }
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
diff --git a/usr.sbin/sysinstall/keymap.c b/usr.sbin/sysinstall/keymap.c
new file mode 100644
index 0000000..21b5e09
--- /dev/null
+++ b/usr.sbin/sysinstall/keymap.c
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 1996 Joerg Wunsch
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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.
+ *
+ * $FreeBSD$
+ *
+ */
+
+#include "sysinstall.h"
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/kbio.h>
+
+struct keymapInfo {
+ const char *name;
+ const struct keymap *map;
+};
+
+#include "keymap.h"
+
+/*
+ * keymap.h is being automatically generated by the Makefile. It
+ * contains definitions for all desired keymaps. Note that since we
+ * don't support font loading nor screen mapping during installation,
+ * we simply don't care for any other keys than the ASCII subset.
+ *
+ * Therefore, if no keymap with the exact name has been found in the
+ * first pass, we make a second pass over the table looking just for
+ * the language name only.
+ */
+
+#ifdef WITH_SYSCONS
+static int
+keymapSetDefault(const char *prefix)
+{
+ dialogMenuItem *items = MenuSysconsKeymap.items;
+ int i;
+ size_t plen = strlen(prefix);
+
+ for (i = 0; items[i].data; ++i)
+ if (!strncmp(prefix, items[i].data, plen))
+ return i;
+
+ return -1;
+}
+
+int
+keymapMenuSelect(dialogMenuItem *self)
+{
+ static const struct {
+ const char *country, *lang;
+ } map[] = {
+ {"dk", "danish"},
+ {"ee", "estonian"},
+ {"fi", "finnish"},
+ {"de", "german"},
+ {"is", "icelandic"},
+ {"no", "norwegian"},
+ {"pl", "pl_PL"},
+ {"es", "spanish"},
+ {"se", "swedish"},
+ {"ch", "swiss"},
+ {"gb", "uk"},
+ {"gg", "uk"},
+ {"ie", "uk"},
+ {"im", "uk"},
+ {"je", "uk"},
+ {NULL, NULL}
+ };
+ const char *country, *lang;
+ int i;
+ int choice, scroll, curr, max;
+ char prefix[16 + 1];
+
+ if ((country = variable_get(VAR_COUNTRY)) != NULL)
+ {
+ lang = country;
+ for (i = 0; map[i].country; ++i)
+ if (!strcmp(country, map[i].country))
+ {
+ lang = map[i].lang;
+ break;
+ }
+
+ snprintf(prefix, sizeof(prefix), "keymap=%s.iso", lang);
+ if ((choice = keymapSetDefault(prefix)) == -1)
+ {
+ snprintf(prefix, sizeof(prefix), "keymap=%s", lang);
+ if ((choice = keymapSetDefault(prefix)) == -1) {
+#ifdef PC98
+ snprintf(prefix, sizeof(prefix), "keymap=jp.pc98");
+#else
+ snprintf(prefix, sizeof(prefix), "keymap=us.iso");
+#endif
+ if ((choice = keymapSetDefault(prefix)) == -1)
+ choice = 0;
+ }
+ }
+
+ dmenuSetDefaultIndex(&MenuSysconsKeymap, &choice, &scroll, &curr, &max);
+ return dmenuOpen(&MenuSysconsKeymap, &choice, &scroll, &curr, &max, FALSE);
+ }
+ else
+ return dmenuOpenSimple(&MenuSysconsKeymap, FALSE) ? DITEM_SUCCESS :
+ DITEM_FAILURE;
+}
+#endif
+
+/*
+ * Return values:
+ *
+ * 0: OK
+ * -1: no appropriate keymap found
+ * -2: error installing map (other than ENXIO which means we're not on syscons)
+ */
+
+int
+loadKeymap(const char *lang)
+{
+ int passno, err;
+ char *llang;
+ size_t l;
+ struct keymapInfo *kip;
+
+ llang = strdup(lang);
+ if (llang == NULL)
+ abort();
+
+ for (passno = 0; passno < 2; passno++)
+ {
+ if (passno > 0)
+ {
+ /* make the match more fuzzy */
+ l = strspn(llang, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
+ llang[l] = '\0';
+ }
+
+ l = strlen(llang);
+
+ for (kip = keymapInfos; kip->name; kip++)
+ if (strncmp(kip->name, llang, l) == 0)
+ {
+ /* Yep, got it! */
+ err = ioctl(0, PIO_KEYMAP, kip->map);
+ free(llang);
+ return (err == -1 && errno != ENOTTY)? -2: 0;
+ }
+ }
+ free(llang);
+ return -1;
+}
diff --git a/usr.sbin/sysinstall/label.c b/usr.sbin/sysinstall/label.c
new file mode 100644
index 0000000..08690f6
--- /dev/null
+++ b/usr.sbin/sysinstall/label.c
@@ -0,0 +1,1690 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sysinstall.h"
+#include <ctype.h>
+#include <inttypes.h>
+#include <libdisk.h>
+#include <sys/disklabel.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+
+#define AUTO_HOME 0 /* do not create /home automatically */
+
+/*
+ * Everything to do with editing the contents of disk labels.
+ */
+
+/* A nice message we use a lot in the disklabel editor */
+#define MSG_NOT_APPLICABLE "That option is not applicable here"
+
+/* Where to start printing the freebsd slices */
+#define CHUNK_SLICE_START_ROW 2
+#define CHUNK_PART_START_ROW 11
+
+/* The smallest filesystem we're willing to create */
+#define FS_MIN_SIZE ONE_MEG
+
+/*
+ * Minimum partition sizes
+ */
+#if defined(__ia64__) || defined(__sparc64__) || defined(__amd64__)
+#define ROOT_MIN_SIZE 128
+#else
+#define ROOT_MIN_SIZE 118
+#endif
+#define SWAP_MIN_SIZE 32
+#define USR_MIN_SIZE 160
+#define VAR_MIN_SIZE 20
+#define TMP_MIN_SIZE 20
+#define HOME_MIN_SIZE 20
+
+/*
+ * Swap size limit for auto-partitioning (4G).
+ */
+#define SWAP_AUTO_LIMIT_SIZE 4096
+
+/*
+ * Default partition sizes. If we do not have sufficient disk space
+ * for this configuration we scale things relative to the NOM vs DEFAULT
+ * sizes. If the disk is larger then /home will get any remaining space.
+ */
+#define ROOT_DEFAULT_SIZE 512
+#define USR_DEFAULT_SIZE 8192
+#define VAR_DEFAULT_SIZE 1024
+#define TMP_DEFAULT_SIZE 512
+#define HOME_DEFAULT_SIZE USR_DEFAULT_SIZE
+
+/*
+ * Nominal partition sizes. These are used to scale the default sizes down
+ * when we have insufficient disk space. If this isn't sufficient we scale
+ * down using the MIN sizes instead.
+ */
+#define ROOT_NOMINAL_SIZE 256
+#define USR_NOMINAL_SIZE 1536
+#define VAR_NOMINAL_SIZE 128
+#define TMP_NOMINAL_SIZE 128
+#define HOME_NOMINAL_SIZE USR_NOMINAL_SIZE
+
+/* The bottom-most row we're allowed to scribble on */
+#define CHUNK_ROW_MAX 16
+
+
+/* All the chunks currently displayed on the screen */
+static struct {
+ struct chunk *c;
+ PartType type;
+} label_chunk_info[MAX_CHUNKS + 1];
+static int here;
+
+/*** with this value we try to track the most recently added label ***/
+static int label_focus = 0, pslice_focus = 0;
+
+static int diskLabel(Device *dev);
+static int diskLabelNonInteractive(Device *dev);
+static char *try_auto_label(Device **devs, Device *dev, int perc, int *req);
+
+static int
+labelHook(dialogMenuItem *selected)
+{
+ Device **devs = NULL;
+
+ devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("Unable to find disk %s!", selected->prompt);
+ return DITEM_FAILURE;
+ }
+ /* Toggle enabled status? */
+ if (!devs[0]->enabled) {
+ devs[0]->enabled = TRUE;
+ diskLabel(devs[0]);
+ }
+ else
+ devs[0]->enabled = FALSE;
+ return DITEM_SUCCESS;
+}
+
+static int
+labelCheck(dialogMenuItem *selected)
+{
+ Device **devs = NULL;
+
+ devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
+ if (!devs || devs[0]->enabled == FALSE)
+ return FALSE;
+ return TRUE;
+}
+
+int
+diskLabelEditor(dialogMenuItem *self)
+{
+ DMenu *menu;
+ Device **devs;
+ int i, cnt;
+
+ i = 0;
+ cnt = diskGetSelectCount(&devs);
+ if (cnt == -1) {
+ msgConfirm("No disks found! Please verify that your disk controller is being\n"
+ "properly probed at boot time. See the Hardware Guide on the\n"
+ "Documentation menu for clues on diagnosing this type of problem.");
+ return DITEM_FAILURE;
+ }
+ else if (cnt) {
+ /* Some are already selected */
+ if (variable_get(VAR_NONINTERACTIVE) &&
+ !variable_get(VAR_DISKINTERACTIVE))
+ i = diskLabelNonInteractive(NULL);
+ else
+ i = diskLabel(NULL);
+ }
+ else {
+ /* No disks are selected, fall-back case now */
+ cnt = deviceCount(devs);
+ if (cnt == 1) {
+ devs[0]->enabled = TRUE;
+ if (variable_get(VAR_NONINTERACTIVE) &&
+ !variable_get(VAR_DISKINTERACTIVE))
+ i = diskLabelNonInteractive(devs[0]);
+ else
+ i = diskLabel(devs[0]);
+ }
+ else {
+ menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, labelHook, labelCheck);
+ if (!menu) {
+ msgConfirm("No devices suitable for installation found!\n\n"
+ "Please verify that your disk controller (and attached drives)\n"
+ "were detected properly. This can be done by pressing the\n"
+ "[Scroll Lock] key and using the Arrow keys to move back to\n"
+ "the boot messages. Press [Scroll Lock] again to return.");
+ i = DITEM_FAILURE;
+ }
+ else {
+ i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE;
+ free(menu);
+ }
+ }
+ }
+ if (DITEM_STATUS(i) != DITEM_FAILURE) {
+ if (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ }
+ return i;
+}
+
+int
+diskLabelCommit(dialogMenuItem *self)
+{
+ char *cp;
+ int i;
+
+ /* Already done? */
+ if ((cp = variable_get(DISK_LABELLED)) && strcmp(cp, "yes"))
+ i = DITEM_SUCCESS;
+ else if (!cp) {
+ msgConfirm("You must assign disk labels before this option can be used.");
+ i = DITEM_FAILURE;
+ }
+ /* The routine will guard against redundant writes, just as this one does */
+ else if (DITEM_STATUS(diskPartitionWrite(self)) != DITEM_SUCCESS)
+ i = DITEM_FAILURE;
+ else if (DITEM_STATUS(installFilesystems(self)) != DITEM_SUCCESS)
+ i = DITEM_FAILURE;
+ else {
+ msgInfo("All filesystem information written successfully.");
+ variable_set2(DISK_LABELLED, "written", 0);
+ i = DITEM_SUCCESS;
+ }
+ return i;
+}
+
+/* See if we're already using a desired partition name */
+static Boolean
+check_conflict(char *name)
+{
+ int i;
+
+ for (i = 0; label_chunk_info[i].c; i++)
+ if ((label_chunk_info[i].type == PART_FILESYSTEM || label_chunk_info[i].type == PART_FAT
+ || label_chunk_info[i].type == PART_EFI) && label_chunk_info[i].c->private_data
+ && !strcmp(((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint, name))
+ return TRUE;
+ return FALSE;
+}
+
+/* How much space is in this FreeBSD slice? */
+static daddr_t
+space_free(struct chunk *c)
+{
+ struct chunk *c1;
+ daddr_t sz = c->size;
+
+ for (c1 = c->part; c1; c1 = c1->next) {
+ if (c1->type != unused)
+ sz -= c1->size;
+ }
+ if (sz < 0)
+ msgFatal("Partitions are larger than actual chunk??");
+ return sz;
+}
+
+/* Snapshot the current situation into the displayed chunks structure */
+static void
+record_label_chunks(Device **devs, Device *dev)
+{
+ int i, j, p;
+ struct chunk *c1, *c2;
+ Disk *d;
+
+ j = p = 0;
+ /* First buzz through and pick up the FreeBSD slices */
+ for (i = 0; devs[i]; i++) {
+ if ((dev && devs[i] != dev) || !devs[i]->enabled)
+ continue;
+ d = (Disk *)devs[i]->private;
+ if (!d->chunks)
+ msgFatal("No chunk list found for %s!", d->name);
+
+#ifdef __ia64__
+ label_chunk_info[j].type = PART_SLICE;
+ label_chunk_info[j].c = d->chunks;
+ j++;
+#endif
+
+ /* Put the slice entries first */
+ for (c1 = d->chunks->part; c1; c1 = c1->next) {
+ if (c1->type == freebsd) {
+ label_chunk_info[j].type = PART_SLICE;
+ label_chunk_info[j].c = c1;
+ ++j;
+ }
+#ifdef __powerpc__
+ if (c1->type == apple) {
+ label_chunk_info[j].type = PART_SLICE;
+ label_chunk_info[j].c = c1;
+ ++j;
+ }
+#endif
+ }
+ }
+
+ /* Now run through again and get the FreeBSD partition entries */
+ for (i = 0; devs[i]; i++) {
+ if (!devs[i]->enabled)
+ continue;
+ d = (Disk *)devs[i]->private;
+ /* Then buzz through and pick up the partitions */
+ for (c1 = d->chunks->part; c1; c1 = c1->next) {
+ if (c1->type == freebsd) {
+ for (c2 = c1->part; c2; c2 = c2->next) {
+ if (c2->type == part) {
+ if (c2->subtype == FS_SWAP)
+ label_chunk_info[j].type = PART_SWAP;
+ else
+ label_chunk_info[j].type = PART_FILESYSTEM;
+ label_chunk_info[j].c = c2;
+ ++j;
+ }
+ }
+ }
+ else if (c1->type == fat) {
+ label_chunk_info[j].type = PART_FAT;
+ label_chunk_info[j].c = c1;
+ ++j;
+ }
+#ifdef __ia64__
+ else if (c1->type == efi) {
+ label_chunk_info[j].type = PART_EFI;
+ label_chunk_info[j].c = c1;
+ ++j;
+ }
+ else if (c1->type == part) {
+ if (c1->subtype == FS_SWAP)
+ label_chunk_info[j].type = PART_SWAP;
+ else
+ label_chunk_info[j].type = PART_FILESYSTEM;
+ label_chunk_info[j].c = c1;
+ ++j;
+ }
+#endif
+#ifdef __powerpc__
+ else if (c1->type == apple) {
+ for (c2 = c1->part; c2; c2 = c2->next) {
+ if (c2->type == part) {
+ if (c2->subtype == FS_SWAP)
+ label_chunk_info[j].type = PART_SWAP;
+ else
+ label_chunk_info[j].type = PART_FILESYSTEM;
+ label_chunk_info[j].c = c2;
+ ++j;
+ }
+ }
+ }
+#endif
+ }
+ }
+ label_chunk_info[j].c = NULL;
+ if (here >= j) {
+ here = j ? j - 1 : 0;
+ }
+}
+
+/* A new partition entry */
+static PartInfo *
+new_part(PartType type, char *mpoint, Boolean newfs)
+{
+ PartInfo *pi;
+
+ if (!mpoint)
+ mpoint = (type == PART_EFI) ? "/efi" : "/change_me";
+
+ pi = (PartInfo *)safe_malloc(sizeof(PartInfo));
+ sstrncpy(pi->mountpoint, mpoint, FILENAME_MAX);
+
+ pi->do_newfs = newfs;
+
+ if (type == PART_EFI) {
+ pi->newfs_type = NEWFS_MSDOS;
+ } else {
+ pi->newfs_type = NEWFS_UFS;
+ strcpy(pi->newfs_data.newfs_ufs.user_options, "");
+ pi->newfs_data.newfs_ufs.acls = FALSE;
+ pi->newfs_data.newfs_ufs.multilabel = FALSE;
+ pi->newfs_data.newfs_ufs.softupdates = strcmp(mpoint, "/");
+#ifdef PC98
+ pi->newfs_data.newfs_ufs.ufs1 = TRUE;
+#else
+ pi->newfs_data.newfs_ufs.ufs1 = FALSE;
+#endif
+ }
+
+ return pi;
+}
+
+/* Get the mountpoint for a partition and save it away */
+static PartInfo *
+get_mountpoint(PartType type, struct chunk *old)
+{
+ char *val;
+ PartInfo *tmp;
+ Boolean newfs;
+
+ if (old && old->private_data)
+ tmp = old->private_data;
+ else
+ tmp = NULL;
+ val = (tmp != NULL) ? tmp->mountpoint : (type == PART_EFI) ? "/efi" : NULL;
+ val = msgGetInput(val, "Please specify a mount point for the partition");
+ if (!val || !*val) {
+ if (!old)
+ return NULL;
+ else {
+ free(old->private_data);
+ old->private_data = NULL;
+ }
+ return NULL;
+ }
+
+ /* Is it just the same value? */
+ if (tmp && !strcmp(tmp->mountpoint, val))
+ return NULL;
+
+ /* Did we use it already? */
+ if (check_conflict(val)) {
+ msgConfirm("You already have a mount point for %s assigned!", val);
+ return NULL;
+ }
+
+ /* Is it bogus? */
+ if (*val != '/') {
+ msgConfirm("Mount point must start with a / character");
+ return NULL;
+ }
+
+ /* Is it going to be mounted on root? */
+ if (!strcmp(val, "/")) {
+ if (old)
+ old->flags |= CHUNK_IS_ROOT;
+ }
+ else if (old)
+ old->flags &= ~CHUNK_IS_ROOT;
+
+ newfs = TRUE;
+ if (tmp) {
+ newfs = tmp->do_newfs;
+ safe_free(tmp);
+ }
+ val = string_skipwhite(string_prune(val));
+ tmp = new_part(type, val, newfs);
+ if (old) {
+ old->private_data = tmp;
+ old->private_free = safe_free;
+ }
+ return tmp;
+}
+
+/* Get the type of the new partiton */
+static PartType
+get_partition_type(void)
+{
+ char selection[20];
+ int i;
+ static unsigned char *fs_types[] = {
+#ifdef __ia64__
+ "EFI", "An EFI system partition",
+#endif
+ "FS", "A file system",
+ "Swap", "A swap partition.",
+ };
+ WINDOW *w = savescr();
+
+ i = dialog_menu("Please choose a partition type",
+ "If you want to use this partition for swap space, select Swap.\n"
+ "If you want to put a filesystem on it, choose FS.",
+ -1, -1,
+#ifdef __ia64__
+ 3, 3,
+#else
+ 2, 2,
+#endif
+ fs_types, selection, NULL, NULL);
+ restorescr(w);
+ if (!i) {
+#ifdef __ia64__
+ if (!strcmp(selection, "EFI"))
+ return PART_EFI;
+#endif
+ if (!strcmp(selection, "FS"))
+ return PART_FILESYSTEM;
+ else if (!strcmp(selection, "Swap"))
+ return PART_SWAP;
+ }
+ return PART_NONE;
+}
+
+/* If the user wants a special newfs command for this, set it */
+static void
+getNewfsCmd(PartInfo *p)
+{
+ char buffer[NEWFS_CMD_ARGS_MAX];
+ char *val;
+
+ switch (p->newfs_type) {
+ case NEWFS_UFS:
+ snprintf(buffer, NEWFS_CMD_ARGS_MAX, "%s %s %s %s",
+ NEWFS_UFS_CMD, p->newfs_data.newfs_ufs.softupdates ? "-U" : "",
+ p->newfs_data.newfs_ufs.ufs1 ? "-O1" : "-O2",
+ p->newfs_data.newfs_ufs.user_options);
+ break;
+ case NEWFS_MSDOS:
+ snprintf(buffer, NEWFS_CMD_ARGS_MAX, "%s", NEWFS_MSDOS_CMD);
+ break;
+ case NEWFS_CUSTOM:
+ strcpy(buffer, p->newfs_data.newfs_custom.command);
+ break;
+ }
+
+ val = msgGetInput(buffer,
+ "Please enter the newfs command and options you'd like to use in\n"
+ "creating this file system.");
+ if (val != NULL) {
+ p->newfs_type = NEWFS_CUSTOM;
+ strlcpy(p->newfs_data.newfs_custom.command, val, NEWFS_CMD_ARGS_MAX);
+ }
+}
+
+static void
+getNewfsOptionalArguments(PartInfo *p)
+{
+ char buffer[NEWFS_CMD_ARGS_MAX];
+ char *val;
+
+ /* Must be UFS, per argument checking in I/O routines. */
+
+ strlcpy(buffer, p->newfs_data.newfs_ufs.user_options,
+ NEWFS_CMD_ARGS_MAX);
+ val = msgGetInput(buffer,
+ "Please enter any additional UFS newfs options you'd like to\n"
+ "use in creating this file system.");
+ if (val != NULL)
+ strlcpy(p->newfs_data.newfs_ufs.user_options, val,
+ NEWFS_CMD_ARGS_MAX);
+}
+
+#define MAX_MOUNT_NAME 9
+
+#define PART_PART_COL 0
+#define PART_MOUNT_COL 10
+#define PART_SIZE_COL (PART_MOUNT_COL + MAX_MOUNT_NAME + 3)
+#define PART_NEWFS_COL (PART_SIZE_COL + 8)
+#define PART_OFF 38
+
+#define TOTAL_AVAIL_LINES (10)
+#define PSLICE_SHOWABLE (4)
+
+
+/* stick this all up on the screen */
+static void
+print_label_chunks(void)
+{
+ int i, j, srow, prow, pcol;
+ daddr_t sz;
+ char clrmsg[80];
+ int ChunkPartStartRow;
+ WINDOW *ChunkWin;
+
+ /********************************************************/
+ /*** These values are for controling screen resources ***/
+ /*** Each label line holds up to 2 labels, so beware! ***/
+ /*** strategy will be to try to always make sure the ***/
+ /*** highlighted label is in the active display area. ***/
+ /********************************************************/
+ int pslice_max, label_max;
+ int pslice_count, label_count, label_focus_found, pslice_focus_found;
+
+ attrset(A_REVERSE);
+ mvaddstr(0, 25, "FreeBSD Disklabel Editor");
+ attrset(A_NORMAL);
+
+ /*** Count the number of parition slices ***/
+ pslice_count = 0;
+ for (i = 0; label_chunk_info[i].c ; i++) {
+ if (label_chunk_info[i].type == PART_SLICE)
+ ++pslice_count;
+ }
+ pslice_max = pslice_count;
+
+ /*** 4 line max for partition slices ***/
+ if (pslice_max > PSLICE_SHOWABLE) {
+ pslice_max = PSLICE_SHOWABLE;
+ }
+ ChunkPartStartRow = CHUNK_SLICE_START_ROW + 3 + pslice_max;
+
+ /*** View partition slices modulo pslice_max ***/
+ label_max = TOTAL_AVAIL_LINES - pslice_max;
+
+ for (i = 0; i < 2; i++) {
+ mvaddstr(ChunkPartStartRow - 2, PART_PART_COL + (i * PART_OFF), "Part");
+ mvaddstr(ChunkPartStartRow - 1, PART_PART_COL + (i * PART_OFF), "----");
+
+ mvaddstr(ChunkPartStartRow - 2, PART_MOUNT_COL + (i * PART_OFF), "Mount");
+ mvaddstr(ChunkPartStartRow - 1, PART_MOUNT_COL + (i * PART_OFF), "-----");
+
+ mvaddstr(ChunkPartStartRow - 2, PART_SIZE_COL + (i * PART_OFF) + 3, "Size");
+ mvaddstr(ChunkPartStartRow - 1, PART_SIZE_COL + (i * PART_OFF) + 3, "----");
+
+ mvaddstr(ChunkPartStartRow - 2, PART_NEWFS_COL + (i * PART_OFF), "Newfs");
+ mvaddstr(ChunkPartStartRow - 1, PART_NEWFS_COL + (i * PART_OFF), "-----");
+ }
+ srow = CHUNK_SLICE_START_ROW;
+ prow = 0;
+ pcol = 0;
+
+ /*** these variables indicate that the focused item is shown currently ***/
+ label_focus_found = 0;
+ pslice_focus_found = 0;
+
+ label_count = 0;
+ pslice_count = 0;
+ mvprintw(CHUNK_SLICE_START_ROW - 1, 0, " ");
+ mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, " ");
+
+ ChunkWin = newwin(CHUNK_ROW_MAX - ChunkPartStartRow, 76, ChunkPartStartRow, 0);
+
+ wclear(ChunkWin);
+ /*** wrefresh(ChunkWin); ***/
+
+ for (i = 0; label_chunk_info[i].c; i++) {
+ /* Is it a slice entry displayed at the top? */
+ if (label_chunk_info[i].type == PART_SLICE) {
+ /*** This causes the new pslice to replace the previous display ***/
+ /*** focus must remain on the most recently active pslice ***/
+ if (pslice_count == pslice_max) {
+ if (pslice_focus_found) {
+ /*** This is where we can mark the more following ***/
+ attrset(A_BOLD);
+ mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, "***MORE***");
+ attrset(A_NORMAL);
+ continue;
+ }
+ else {
+ /*** this is where we set the more previous ***/
+ attrset(A_BOLD);
+ mvprintw(CHUNK_SLICE_START_ROW - 1, 0, "***MORE***");
+ attrset(A_NORMAL);
+ pslice_count = 0;
+ srow = CHUNK_SLICE_START_ROW;
+ }
+ }
+
+ sz = space_free(label_chunk_info[i].c);
+ if (i == here)
+ attrset(ATTR_SELECTED);
+ if (i == pslice_focus)
+ pslice_focus_found = -1;
+
+ if (label_chunk_info[i].c->type == whole) {
+ if (sz >= 100 * ONE_GIG)
+ mvprintw(srow++, 0,
+ "Disk: %s\t\tFree: %jd blocks (%jdGB)",
+ label_chunk_info[i].c->disk->name, (intmax_t)sz,
+ (intmax_t)(sz / ONE_GIG));
+ else
+ mvprintw(srow++, 0,
+ "Disk: %s\t\tFree: %jd blocks (%jdMB)",
+ label_chunk_info[i].c->disk->name, (intmax_t)sz,
+ (intmax_t)(sz / ONE_MEG));
+ } else {
+ if (sz >= 100 * ONE_GIG)
+ mvprintw(srow++, 0,
+ "Disk: %s\tPartition name: %s\tFree: %jd blocks (%jdGB)",
+ label_chunk_info[i].c->disk->name,
+ label_chunk_info[i].c->name,
+ (intmax_t)sz, (intmax_t)(sz / ONE_GIG));
+ else
+ mvprintw(srow++, 0,
+ "Disk: %s\tPartition name: %s\tFree: %jd blocks (%jdMB)",
+ label_chunk_info[i].c->disk->name,
+ label_chunk_info[i].c->name,
+ (intmax_t)sz, (intmax_t)(sz / ONE_MEG));
+ }
+ attrset(A_NORMAL);
+ clrtoeol();
+ move(0, 0);
+ /*** refresh(); ***/
+ ++pslice_count;
+ }
+ /* Otherwise it's a DOS, swap or filesystem entry in the Chunk window */
+ else {
+ char onestr[PART_OFF], num[10], *mountpoint, newfs[12];
+
+ /*
+ * We copy this into a blank-padded string so that it looks like
+ * a solid bar in reverse-video
+ */
+ memset(onestr, ' ', PART_OFF - 1);
+ onestr[PART_OFF - 1] = '\0';
+
+ /*** Track how many labels have been displayed ***/
+ if (label_count == ((label_max - 1 ) * 2)) {
+ if (label_focus_found) {
+ continue;
+ }
+ else {
+ label_count = 0;
+ prow = 0;
+ pcol = 0;
+ }
+ }
+
+ /* Go for two columns if we've written one full columns worth */
+ /*** if (prow == (CHUNK_ROW_MAX - ChunkPartStartRow)) ***/
+ if (label_count == label_max - 1) {
+ pcol = PART_OFF;
+ prow = 0;
+ }
+ memcpy(onestr + PART_PART_COL, label_chunk_info[i].c->name, strlen(label_chunk_info[i].c->name));
+ /* If it's a filesystem, display the mountpoint */
+ if (label_chunk_info[i].c->private_data && (label_chunk_info[i].type == PART_FILESYSTEM
+ || label_chunk_info[i].type == PART_FAT || label_chunk_info[i].type == PART_EFI))
+ mountpoint = ((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint;
+ else if (label_chunk_info[i].type == PART_SWAP)
+ mountpoint = "swap";
+ else
+ mountpoint = "<none>";
+
+ /* Now display the newfs field */
+ if (label_chunk_info[i].type == PART_FAT)
+ strcpy(newfs, "DOS");
+#if defined(__ia64__)
+ else if (label_chunk_info[i].type == PART_EFI) {
+ strcpy(newfs, "EFI");
+ if (label_chunk_info[i].c->private_data) {
+ strcat(newfs, " ");
+ PartInfo *pi = (PartInfo *)label_chunk_info[i].c->private_data;
+ strcat(newfs, pi->do_newfs ? " Y" : " N");
+ }
+ }
+#endif
+ else if (label_chunk_info[i].c->private_data && label_chunk_info[i].type == PART_FILESYSTEM) {
+ PartInfo *pi = (PartInfo *)label_chunk_info[i].c->private_data;
+
+ switch (pi->newfs_type) {
+ case NEWFS_UFS:
+ strcpy(newfs, NEWFS_UFS_STRING);
+ if (pi->newfs_data.newfs_ufs.ufs1)
+ strcat(newfs, "1");
+ else
+ strcat(newfs, "2");
+ if (pi->newfs_data.newfs_ufs.softupdates)
+ strcat(newfs, "+S");
+ else
+ strcat(newfs, " ");
+
+ break;
+ case NEWFS_MSDOS:
+ strcpy(newfs, "FAT");
+ break;
+ case NEWFS_CUSTOM:
+ strcpy(newfs, "CUST");
+ break;
+ }
+ strcat(newfs, pi->do_newfs ? " Y" : " N ");
+ }
+ else if (label_chunk_info[i].type == PART_SWAP)
+ strcpy(newfs, "SWAP");
+ else
+ strcpy(newfs, "*");
+ for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++)
+ onestr[PART_MOUNT_COL + j] = mountpoint[j];
+ if (label_chunk_info[i].c->size == 0)
+ snprintf(num, 10, "%5dMB", 0);
+ else if (label_chunk_info[i].c->size < (100 * ONE_GIG))
+ snprintf(num, 10, "%5jdMB",
+ (intmax_t)label_chunk_info[i].c->size / ONE_MEG);
+ else
+ snprintf(num, 10, "%5jdGB",
+ (intmax_t)label_chunk_info[i].c->size / ONE_GIG);
+ memcpy(onestr + PART_SIZE_COL, num, strlen(num));
+ memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs));
+ onestr[PART_NEWFS_COL + strlen(newfs)] = '\0';
+ if (i == label_focus) {
+ label_focus_found = -1;
+ wattrset(ChunkWin, A_BOLD);
+ }
+ if (i == here)
+ wattrset(ChunkWin, ATTR_SELECTED);
+
+ /*** lazy man's way of expensively padding this string ***/
+ while (strlen(onestr) < 37)
+ strcat(onestr, " ");
+
+ mvwaddstr(ChunkWin, prow, pcol, onestr);
+ wattrset(ChunkWin, A_NORMAL);
+ move(0, 0);
+ ++prow;
+ ++label_count;
+ }
+ }
+
+ /*** this will erase all the extra stuff ***/
+ memset(clrmsg, ' ', 37);
+ clrmsg[37] = '\0';
+
+ while (pslice_count < pslice_max) {
+ mvprintw(srow++, 0, clrmsg);
+ clrtoeol();
+ ++pslice_count;
+ }
+ while (label_count < (2 * (label_max - 1))) {
+ mvwaddstr(ChunkWin, prow++, pcol, clrmsg);
+ ++label_count;
+ if (prow == (label_max - 1)) {
+ prow = 0;
+ pcol = PART_OFF;
+ }
+ }
+ refresh();
+ wrefresh(ChunkWin);
+}
+
+static void
+print_command_summary(void)
+{
+ mvprintw(17, 0, "The following commands are valid here (upper or lower case):");
+ mvprintw(18, 0, "C = Create D = Delete M = Mount pt.");
+ if (!RunningAsInit)
+ mvprintw(18, 56, "W = Write");
+ mvprintw(19, 0, "N = Newfs Opts Q = Finish S = Toggle SoftUpdates Z = Custom Newfs");
+ mvprintw(20, 0, "T = Toggle Newfs U = Undo A = Auto Defaults R = Delete+Merge");
+ mvprintw(22, 0, "Use F1 or ? to get more help, arrow keys to select.");
+ move(0, 0);
+}
+
+static void
+clear_wins(void)
+{
+ clear();
+ print_label_chunks();
+}
+
+static int
+diskLabel(Device *dev)
+{
+ daddr_t sz;
+ int key = 0;
+ Boolean labeling;
+ char *msg = NULL;
+ PartInfo *p, *oldp;
+ PartType type;
+ Device **devs;
+ WINDOW *w = savescr();
+
+ label_focus = 0;
+ pslice_focus = 0;
+ here = 0;
+
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("No disks found!");
+ restorescr(w);
+ return DITEM_FAILURE;
+ }
+ labeling = TRUE;
+ keypad(stdscr, TRUE);
+ record_label_chunks(devs, dev);
+
+ clear();
+ while (labeling) {
+ char *cp;
+ int rflags = DELCHUNK_NORMAL;
+
+ print_label_chunks();
+ print_command_summary();
+ if (msg) {
+ attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
+ clrtoeol();
+ beep();
+ msg = NULL;
+ }
+ else {
+ move(23, 0);
+ clrtoeol();
+ }
+
+ refresh();
+ key = getch();
+ switch (toupper(key)) {
+ int i;
+ static char _msg[40];
+
+ case '\014': /* ^L */
+ clear_wins();
+ break;
+
+ case '\020': /* ^P */
+ case KEY_UP:
+ case '-':
+ if (here != 0)
+ --here;
+ else
+ while (label_chunk_info[here + 1].c)
+ ++here;
+ break;
+
+ case '\016': /* ^N */
+ case KEY_DOWN:
+ case '+':
+ case '\r':
+ case '\n':
+ if (label_chunk_info[here + 1].c)
+ ++here;
+ else
+ here = 0;
+ break;
+
+ case KEY_HOME:
+ here = 0;
+ break;
+
+ case KEY_END:
+ while (label_chunk_info[here + 1].c)
+ ++here;
+ break;
+
+ case KEY_F(1):
+ case '?':
+ systemDisplayHelp("partition");
+ clear_wins();
+ break;
+
+ case '1':
+ if (label_chunk_info[here].type == PART_FILESYSTEM) {
+ PartInfo *pi =
+ ((PartInfo *)label_chunk_info[here].c->private_data);
+
+ if ((pi != NULL) &&
+ (pi->newfs_type == NEWFS_UFS)) {
+ pi->newfs_data.newfs_ufs.ufs1 = true;
+ } else
+ msg = MSG_NOT_APPLICABLE;
+ } else
+ msg = MSG_NOT_APPLICABLE;
+ break;
+ break;
+
+ case '2':
+ if (label_chunk_info[here].type == PART_FILESYSTEM) {
+ PartInfo *pi =
+ ((PartInfo *)label_chunk_info[here].c->private_data);
+
+ if ((pi != NULL) &&
+ (pi->newfs_type == NEWFS_UFS)) {
+ pi->newfs_data.newfs_ufs.ufs1 = false;
+ } else
+ msg = MSG_NOT_APPLICABLE;
+ } else
+ msg = MSG_NOT_APPLICABLE;
+ break;
+ break;
+
+ case 'A':
+ if (label_chunk_info[here].type != PART_SLICE) {
+ msg = "You can only do this in a disk slice (at top of screen)";
+ break;
+ }
+ /*
+ * Generate standard partitions automatically. If we do not
+ * have sufficient space we attempt to scale-down the size
+ * of the partitions within certain bounds.
+ */
+ {
+ int perc;
+ int req = 0;
+
+ for (perc = 100; perc > 0; perc -= 5) {
+ req = 0; /* reset for each loop */
+ if ((msg = try_auto_label(devs, dev, perc, &req)) == NULL)
+ break;
+ }
+ if (msg) {
+ if (req) {
+ msgConfirm(msg);
+ clear_wins();
+ msg = NULL;
+ }
+ }
+ }
+ break;
+
+ case 'C':
+ if (label_chunk_info[here].type != PART_SLICE) {
+ msg = "You can only do this in a master partition (see top of screen)";
+ break;
+ }
+ sz = space_free(label_chunk_info[here].c);
+ if (sz <= FS_MIN_SIZE) {
+ msg = "Not enough space to create an additional FreeBSD partition";
+ break;
+ }
+ else {
+ char *val;
+ daddr_t size;
+ struct chunk *tmp;
+ char osize[80];
+ u_long flags = 0;
+
+#ifdef __powerpc__
+ /* Always use the maximum size for apple partitions */
+ if (label_chunk_info[here].c->type == apple)
+ size = sz;
+ else {
+#endif
+ sprintf(osize, "%jd", (intmax_t)sz);
+ val = msgGetInput(osize,
+ "Please specify the partition size in blocks or append a trailing G for\n"
+#ifdef __ia64__
+ "gigabytes, M for megabytes.\n"
+#else
+ "gigabytes, M for megabytes, or C for cylinders.\n"
+#endif
+ "%jd blocks (%jdMB) are free.",
+ (intmax_t)sz, (intmax_t)sz / ONE_MEG);
+ if (!val || (size = strtoimax(val, &cp, 0)) <= 0) {
+ clear_wins();
+ break;
+ }
+
+ if (*cp) {
+ if (toupper(*cp) == 'M')
+ size *= ONE_MEG;
+ else if (toupper(*cp) == 'G')
+ size *= ONE_GIG;
+#ifndef __ia64__
+ else if (toupper(*cp) == 'C')
+ size *= (label_chunk_info[here].c->disk->bios_hd * label_chunk_info[here].c->disk->bios_sect);
+#endif
+ }
+ if (size <= FS_MIN_SIZE) {
+ msgConfirm("The minimum filesystem size is %dMB", FS_MIN_SIZE / ONE_MEG);
+ clear_wins();
+ break;
+ }
+#ifdef __powerpc__
+ }
+#endif
+ type = get_partition_type();
+ if (type == PART_NONE) {
+ clear_wins();
+ beep();
+ break;
+ }
+
+ if (type == PART_FILESYSTEM || type == PART_EFI) {
+ if ((p = get_mountpoint(type, NULL)) == NULL) {
+ clear_wins();
+ beep();
+ break;
+ }
+ else if (!strcmp(p->mountpoint, "/")) {
+ if (type != PART_FILESYSTEM) {
+ clear_wins();
+ beep();
+ break;
+ }
+ else
+ flags |= CHUNK_IS_ROOT;
+ }
+ else
+ flags &= ~CHUNK_IS_ROOT;
+ }
+ else
+ p = NULL;
+
+ if ((flags & CHUNK_IS_ROOT) && (size < (ROOT_MIN_SIZE * ONE_MEG))) {
+ msgConfirm("Warning: This is smaller than the recommended size for a\n"
+ "root partition. For a variety of reasons, root\n"
+ "partitions should usually be at least %dMB in size", ROOT_MIN_SIZE);
+ }
+ tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, size,
+#ifdef __ia64__
+ (type == PART_EFI) ? efi : part,
+ (type == PART_EFI) ? 0 : (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
+#else
+ part, (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
+#endif
+ flags);
+ if (!tmp) {
+ msgConfirm("Unable to create the partition. Too big?");
+ clear_wins();
+ break;
+ }
+
+ tmp->private_data = p;
+ tmp->private_free = safe_free;
+ if (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ record_label_chunks(devs, dev);
+ clear_wins();
+ /* This is where we assign focus to new label so it shows. */
+ {
+ int i;
+ label_focus = -1;
+ for (i = 0; label_chunk_info[i].c; ++i) {
+ if (label_chunk_info[i].c == tmp) {
+ label_focus = i;
+ break;
+ }
+ }
+ if (label_focus == -1)
+ label_focus = i - 1;
+ }
+ }
+ break;
+
+ case KEY_DC:
+ case 'R': /* recover space (delete w/ recover) */
+ /*
+ * Delete the partition w/ space recovery.
+ */
+ rflags = DELCHUNK_RECOVER;
+ /* fall through */
+ case 'D': /* delete */
+ if (label_chunk_info[here].type == PART_SLICE) {
+ msg = MSG_NOT_APPLICABLE;
+ break;
+ }
+ else if (label_chunk_info[here].type == PART_FAT) {
+ msg = "Use the Disk Partition Editor to delete DOS partitions";
+ break;
+ }
+ Delete_Chunk2(label_chunk_info[here].c->disk, label_chunk_info[here].c, rflags);
+ if (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ record_label_chunks(devs, dev);
+ break;
+
+ case 'M': /* mount */
+ switch(label_chunk_info[here].type) {
+ case PART_SLICE:
+ msg = MSG_NOT_APPLICABLE;
+ break;
+
+ case PART_SWAP:
+ msg = "You don't need to specify a mountpoint for a swap partition.";
+ break;
+
+ case PART_FAT:
+ case PART_EFI:
+ case PART_FILESYSTEM:
+ oldp = label_chunk_info[here].c->private_data;
+ p = get_mountpoint(label_chunk_info[here].type, label_chunk_info[here].c);
+ if (p) {
+ if (!oldp)
+ p->do_newfs = FALSE;
+ if ((label_chunk_info[here].type == PART_FAT ||
+ label_chunk_info[here].type == PART_EFI) &&
+ (!strcmp(p->mountpoint, "/") ||
+ !strcmp(p->mountpoint, "/usr") ||
+ !strcmp(p->mountpoint, "/var"))) {
+ msgConfirm("%s is an invalid mount point for a DOS partition!", p->mountpoint);
+ strcpy(p->mountpoint, "/bogus");
+ }
+ }
+ if (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ record_label_chunks(devs, dev);
+ clear_wins();
+ break;
+
+ default:
+ msgFatal("Bogus partition under cursor???");
+ break;
+ }
+ break;
+
+ case 'N': /* Set newfs options */
+ if (label_chunk_info[here].c->private_data &&
+ ((PartInfo *)label_chunk_info[here].c->private_data)->do_newfs)
+ getNewfsOptionalArguments(
+ label_chunk_info[here].c->private_data);
+ else
+ msg = MSG_NOT_APPLICABLE;
+ clear_wins();
+ break;
+
+ case 'S': /* Toggle soft updates flag */
+ if (label_chunk_info[here].type == PART_FILESYSTEM) {
+ PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
+ if (pi != NULL &&
+ pi->newfs_type == NEWFS_UFS)
+ pi->newfs_data.newfs_ufs.softupdates =
+ !pi->newfs_data.newfs_ufs.softupdates;
+ else
+ msg = MSG_NOT_APPLICABLE;
+ }
+ else
+ msg = MSG_NOT_APPLICABLE;
+ break;
+
+ case 'T': /* Toggle newfs state */
+ if ((label_chunk_info[here].type == PART_FILESYSTEM ||
+ label_chunk_info[here].type == PART_EFI) &&
+ (label_chunk_info[here].c->private_data)) {
+ PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
+ if (!pi->do_newfs)
+ label_chunk_info[here].c->flags |= CHUNK_NEWFS;
+ else
+ label_chunk_info[here].c->flags &= ~CHUNK_NEWFS;
+
+ label_chunk_info[here].c->private_data =
+ new_part(label_chunk_info[here].type, pi ? pi->mountpoint : NULL, pi ? !pi->do_newfs
+ : TRUE);
+ if (pi != NULL &&
+ pi->newfs_type == NEWFS_UFS) {
+ PartInfo *pi_new = label_chunk_info[here].c->private_data;
+
+ pi_new->newfs_data.newfs_ufs = pi->newfs_data.newfs_ufs;
+ }
+ safe_free(pi);
+ label_chunk_info[here].c->private_free = safe_free;
+ if (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ }
+ else
+ msg = MSG_NOT_APPLICABLE;
+ break;
+
+ case 'U':
+ clear();
+ if (!variable_cmp(DISK_LABELLED, "written")) {
+ msgConfirm("You've already written out your changes -\n"
+ "it's too late to undo!");
+ }
+ else if (!msgNoYes("Are you SURE you want to Undo everything?")) {
+ variable_unset(DISK_PARTITIONED);
+ variable_unset(DISK_LABELLED);
+ for (i = 0; devs[i]; i++) {
+ Disk *d;
+
+ if (!devs[i]->enabled)
+ continue;
+ else if ((d = Open_Disk(devs[i]->name)) != NULL) {
+ Free_Disk(devs[i]->private);
+ devs[i]->private = d;
+#ifdef WITH_SLICES
+ diskPartition(devs[i]);
+#endif
+ }
+ }
+ record_label_chunks(devs, dev);
+ }
+ clear_wins();
+ break;
+
+ case 'W':
+ if (!variable_cmp(DISK_LABELLED, "written")) {
+ msgConfirm("You've already written out your changes - if you\n"
+ "wish to overwrite them, you'll have to restart\n"
+ "%s first.", ProgName);
+ }
+ else if (!msgNoYes("WARNING: This should only be used when modifying an EXISTING\n"
+ "installation. If you are installing FreeBSD for the first time\n"
+ "then you should simply type Q when you're finished here and your\n"
+ "changes will be committed in one batch automatically at the end of\n"
+ "these questions.\n\n"
+ "Are you absolutely sure you want to do this now?")) {
+ variable_set2(DISK_LABELLED, "yes", 0);
+ diskLabelCommit(NULL);
+ }
+ clear_wins();
+ break;
+
+ case 'Z': /* Set newfs command line */
+ if (label_chunk_info[here].c->private_data &&
+ ((PartInfo *)label_chunk_info[here].c->private_data)->do_newfs)
+ getNewfsCmd(label_chunk_info[here].c->private_data);
+ else
+ msg = MSG_NOT_APPLICABLE;
+ clear_wins();
+ break;
+
+#ifndef __ia64__
+ case '|':
+ if (!msgNoYes("Are you sure you want to go into Wizard mode?\n\n"
+ "This is an entirely undocumented feature which you are not\n"
+ "expected to understand!")) {
+ int i;
+ Device **devs;
+
+ dialog_clear();
+ end_dialog();
+ DialogActive = FALSE;
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("Can't find any disk devices!");
+ break;
+ }
+ for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) {
+ if (devs[i]->enabled)
+ slice_wizard(((Disk *)devs[i]->private));
+ }
+ if (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ DialogActive = TRUE;
+ record_label_chunks(devs, dev);
+ clear_wins();
+ }
+ else
+ msg = "A most prudent choice!";
+ break;
+#endif
+
+ case '\033': /* ESC */
+ case 'Q':
+ labeling = FALSE;
+ break;
+
+ default:
+ beep();
+ sprintf(_msg, "Invalid key %d - Type F1 or ? for help", key);
+ msg = _msg;
+ break;
+ }
+ if (label_chunk_info[here].type == PART_SLICE)
+ pslice_focus = here;
+ else
+ label_focus = here;
+ }
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+static __inline daddr_t
+requested_part_size(char *varName, daddr_t nom, int def, int perc)
+{
+ char *cp;
+ daddr_t sz;
+
+ if ((cp = variable_get(varName)) != NULL)
+ sz = strtoimax(cp, NULL, 0);
+ else
+ sz = nom + (def - nom) * perc / 100;
+ return(sz * ONE_MEG);
+}
+
+/*
+ * Attempt to auto-label the disk. 'perc' (0-100) scales
+ * the size of the various partitions within appropriate
+ * bounds (NOMINAL through DEFAULT sizes). The procedure
+ * succeeds of NULL is returned. A non-null return message
+ * is either a failure-status message (*req == 0), or
+ * a confirmation requestor (*req == 1). *req is 0 on
+ * entry to this call.
+ *
+ * As a special exception to the usual sizing rules, /var is given
+ * additional space equal to the amount of physical memory present
+ * if perc == 100 in order to ensure that users with large hard drives
+ * will have enough space to store a crashdump in /var/crash.
+ *
+ * We autolabel the following partitions: /, swap, /var, /tmp, /usr,
+ * and /home. /home receives any extra left over disk space.
+ */
+static char *
+try_auto_label(Device **devs, Device *dev, int perc, int *req)
+{
+ daddr_t sz;
+ Chunk *AutoHome, *AutoRoot, *AutoSwap;
+ Chunk *AutoTmp, *AutoUsr, *AutoVar;
+#ifdef __ia64__
+ Chunk *AutoEfi;
+#endif
+ int mib[2];
+ unsigned long physmem;
+ size_t size;
+ char *msg = NULL;
+
+ sz = space_free(label_chunk_info[here].c);
+ if (sz <= FS_MIN_SIZE)
+ return("Not enough free space to create a new partition in the slice");
+
+ (void)checkLabels(FALSE);
+ AutoHome = AutoRoot = AutoSwap = NULL;
+ AutoTmp = AutoUsr = AutoVar = NULL;
+
+#ifdef __ia64__
+ AutoEfi = NULL;
+ if (EfiChunk == NULL) {
+ sz = 100 * ONE_MEG;
+ AutoEfi = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, efi, 0, 0);
+ if (AutoEfi == NULL) {
+ *req = 1;
+ msg = "Unable to create the EFI system partition. Too big?";
+ goto done;
+ }
+ AutoEfi->private_data = new_part(PART_EFI, "/efi", TRUE);
+ AutoEfi->private_free = safe_free;
+ AutoEfi->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+#endif
+
+ if (RootChunk == NULL) {
+ sz = requested_part_size(VAR_ROOT_SIZE, ROOT_NOMINAL_SIZE, ROOT_DEFAULT_SIZE, perc);
+
+ AutoRoot = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_BSDFFS, CHUNK_IS_ROOT | CHUNK_AUTO_SIZE);
+ if (!AutoRoot) {
+ *req = 1;
+ msg = "Unable to create the root partition. Too big?";
+ goto done;
+ }
+ AutoRoot->private_data = new_part(PART_FILESYSTEM, "/", TRUE);
+ AutoRoot->private_free = safe_free;
+ AutoRoot->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+ if (SwapChunk == NULL) {
+ sz = requested_part_size(VAR_SWAP_SIZE, 0, 0, perc);
+ if (sz == 0) {
+ daddr_t nom;
+ daddr_t def;
+
+ mib[0] = CTL_HW;
+ mib[1] = HW_PHYSMEM;
+ size = sizeof physmem;
+ sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
+ def = 2 * (int)(physmem / 512);
+ if (def < SWAP_MIN_SIZE * ONE_MEG)
+ def = SWAP_MIN_SIZE * ONE_MEG;
+ if (def > SWAP_AUTO_LIMIT_SIZE * ONE_MEG)
+ def = SWAP_AUTO_LIMIT_SIZE * ONE_MEG;
+ nom = (int)(physmem / 512) / 8;
+ sz = nom + (def - nom) * perc / 100;
+ }
+ AutoSwap = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_SWAP, CHUNK_AUTO_SIZE);
+ if (!AutoSwap) {
+ *req = 1;
+ msg = "Unable to create the swap partition. Too big?";
+ goto done;
+ }
+ AutoSwap->private_data = 0;
+ AutoSwap->private_free = safe_free;
+ record_label_chunks(devs, dev);
+ }
+ if (VarChunk == NULL) {
+ /* Work out how much extra space we want for a crash dump */
+ unsigned long crashdumpsz;
+
+ mib[0] = CTL_HW;
+ mib[1] = HW_PHYSMEM;
+ size = sizeof(physmem);
+ sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
+
+ if (perc == 100)
+ crashdumpsz = physmem / 1048576;
+ else
+ crashdumpsz = 0;
+
+ sz = requested_part_size(VAR_VAR_SIZE, VAR_NOMINAL_SIZE, \
+ VAR_DEFAULT_SIZE + crashdumpsz, perc);
+
+ AutoVar = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_BSDFFS, CHUNK_AUTO_SIZE);
+ if (!AutoVar) {
+ *req = 1;
+ msg = "Not enough free space for /var - you will need to\n"
+ "partition your disk manually with a custom install!";
+ goto done;
+ }
+ AutoVar->private_data = new_part(PART_FILESYSTEM, "/var", TRUE);
+ AutoVar->private_free = safe_free;
+ AutoVar->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+ if (TmpChunk == NULL && !variable_get(VAR_NO_TMP)) {
+ sz = requested_part_size(VAR_TMP_SIZE, TMP_NOMINAL_SIZE, TMP_DEFAULT_SIZE, perc);
+
+ AutoTmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_BSDFFS, CHUNK_AUTO_SIZE);
+ if (!AutoTmp) {
+ *req = 1;
+ msg = "Not enough free space for /tmp - you will need to\n"
+ "partition your disk manually with a custom install!";
+ goto done;
+ }
+ AutoTmp->private_data = new_part(PART_FILESYSTEM, "/tmp", TRUE);
+ AutoTmp->private_free = safe_free;
+ AutoTmp->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+ if (UsrChunk == NULL && !variable_get(VAR_NO_USR)) {
+ sz = requested_part_size(VAR_USR_SIZE, USR_NOMINAL_SIZE, USR_DEFAULT_SIZE, perc);
+#if AUTO_HOME == 0
+ if (sz < space_free(label_chunk_info[here].c))
+ sz = space_free(label_chunk_info[here].c);
+#endif
+ if (sz) {
+ if (sz < (USR_MIN_SIZE * ONE_MEG)) {
+ *req = 1;
+ msg = "Not enough free space for /usr - you will need to\n"
+ "partition your disk manually with a custom install!";
+ }
+
+ AutoUsr = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_BSDFFS, CHUNK_AUTO_SIZE);
+ if (!AutoUsr) {
+ msg = "Unable to create the /usr partition. Not enough space?\n"
+ "You will need to partition your disk manually with a custom install!";
+ goto done;
+ }
+ AutoUsr->private_data = new_part(PART_FILESYSTEM, "/usr", TRUE);
+ AutoUsr->private_free = safe_free;
+ AutoUsr->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+ }
+#if AUTO_HOME == 1
+ if (HomeChunk == NULL && !variable_get(VAR_NO_HOME)) {
+ sz = requested_part_size(VAR_HOME_SIZE, HOME_NOMINAL_SIZE, HOME_DEFAULT_SIZE, perc);
+ if (sz < space_free(label_chunk_info[here].c))
+ sz = space_free(label_chunk_info[here].c);
+ if (sz) {
+ if (sz < (HOME_MIN_SIZE * ONE_MEG)) {
+ *req = 1;
+ msg = "Not enough free space for /home - you will need to\n"
+ "partition your disk manually with a custom install!";
+ goto done;
+ }
+
+ AutoHome = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c, sz, part,
+ FS_BSDFFS, CHUNK_AUTO_SIZE);
+ if (!AutoHome) {
+ msg = "Unable to create the /home partition. Not enough space?\n"
+ "You will need to partition your disk manually with a custom install!";
+ goto done;
+ }
+ AutoHome->private_data = new_part(PART_FILESYSTEM, "/home", TRUE);
+ AutoHome->private_free = safe_free;
+ AutoHome->flags |= CHUNK_NEWFS;
+ record_label_chunks(devs, dev);
+ }
+ }
+#endif
+
+ /* At this point, we're reasonably "labelled" */
+ if (variable_cmp(DISK_LABELLED, "written"))
+ variable_set2(DISK_LABELLED, "yes", 0);
+
+done:
+ if (msg) {
+ if (AutoRoot != NULL)
+ Delete_Chunk(AutoRoot->disk, AutoRoot);
+ if (AutoSwap != NULL)
+ Delete_Chunk(AutoSwap->disk, AutoSwap);
+ if (AutoVar != NULL)
+ Delete_Chunk(AutoVar->disk, AutoVar);
+ if (AutoTmp != NULL)
+ Delete_Chunk(AutoTmp->disk, AutoTmp);
+ if (AutoUsr != NULL)
+ Delete_Chunk(AutoUsr->disk, AutoUsr);
+ if (AutoHome != NULL)
+ Delete_Chunk(AutoHome->disk, AutoHome);
+ record_label_chunks(devs, dev);
+ }
+ return(msg);
+}
+
+static int
+diskLabelNonInteractive(Device *dev)
+{
+ char *cp;
+ PartType type;
+ PartInfo *p;
+ u_long flags;
+ int i, status;
+ Device **devs;
+ Disk *d;
+
+ status = DITEM_SUCCESS;
+ cp = variable_get(VAR_DISK);
+ if (!cp) {
+ msgConfirm("diskLabel: No disk selected - can't label automatically.");
+ return DITEM_FAILURE;
+ }
+ devs = deviceFind(cp, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("diskLabel: No disk device %s found!", cp);
+ return DITEM_FAILURE;
+ }
+ if (dev)
+ d = dev->private;
+ else
+ d = devs[0]->private;
+ record_label_chunks(devs, dev);
+ for (i = 0; label_chunk_info[i].c; i++) {
+ Chunk *c1 = label_chunk_info[i].c;
+
+ if (label_chunk_info[i].type == PART_SLICE) {
+ char name[512];
+ char typ[10], mpoint[50];
+ int entries;
+
+ for (entries = 1;; entries++) {
+ intmax_t sz;
+ int soft = 0;
+ snprintf(name, sizeof name, "%s-%d", c1->name, entries);
+ if ((cp = variable_get(name)) == NULL)
+ break;
+ if (sscanf(cp, "%s %jd %s %d", typ, &sz, mpoint, &soft) < 3) {
+ msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
+ status = DITEM_FAILURE;
+ break;
+ } else {
+ Chunk *tmp;
+
+ flags = 0;
+ if (!strcmp(typ, "swap")) {
+ type = PART_SWAP;
+ strcpy(mpoint, "SWAP");
+ } else {
+ type = PART_FILESYSTEM;
+ if (!strcmp(mpoint, "/"))
+ flags |= CHUNK_IS_ROOT;
+ }
+ if (!sz)
+ sz = space_free(c1);
+ if (sz > space_free(c1)) {
+ msgConfirm("Not enough free space to create partition: %s", mpoint);
+ status = DITEM_FAILURE;
+ break;
+ }
+ if (!(tmp = Create_Chunk_DWIM(d, c1, sz, part,
+ (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS, flags))) {
+ msgConfirm("Unable to create from partition spec: %s. Too big?", cp);
+ status = DITEM_FAILURE;
+ break;
+ } else {
+ PartInfo *pi;
+ pi = tmp->private_data = new_part(PART_FILESYSTEM, mpoint, TRUE);
+ tmp->private_free = safe_free;
+ pi->newfs_data.newfs_ufs.softupdates = soft;
+ }
+ }
+ }
+ } else {
+ /* Must be something we can set a mountpoint for */
+ cp = variable_get(c1->name);
+ if (cp) {
+ char mpoint[50], do_newfs[8];
+ Boolean newfs = FALSE;
+
+ do_newfs[0] = '\0';
+ if (sscanf(cp, "%s %s", mpoint, do_newfs) != 2) {
+ msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
+ status = DITEM_FAILURE;
+ break;
+ }
+ newfs = toupper(do_newfs[0]) == 'Y' ? TRUE : FALSE;
+ if (c1->private_data) {
+ p = c1->private_data;
+ p->do_newfs = newfs;
+ strcpy(p->mountpoint, mpoint);
+ }
+ else {
+ c1->private_data = new_part(PART_FILESYSTEM, mpoint, newfs);
+ c1->private_free = safe_free;
+ }
+ if (!strcmp(mpoint, "/"))
+ c1->flags |= CHUNK_IS_ROOT;
+ else
+ c1->flags &= ~CHUNK_IS_ROOT;
+ }
+ }
+ }
+ if (status == DITEM_SUCCESS)
+ variable_set2(DISK_LABELLED, "yes", 0);
+ return status;
+}
diff --git a/usr.sbin/sysinstall/list.h b/usr.sbin/sysinstall/list.h
new file mode 100644
index 0000000..8300173
--- /dev/null
+++ b/usr.sbin/sysinstall/list.h
@@ -0,0 +1,60 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated for what's essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * 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,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 PAUL TRAINA ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PAUL TRAINA OR HIS KILLER RATS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/* The structure */
+typedef struct _qelement {
+ struct _qelement *q_forw;
+ struct _qelement *q_back;
+} qelement;
+
+#define INITQUE(Xhead) { \
+ (Xhead).q_forw = &(Xhead); \
+ (Xhead).q_back = &(Xhead); \
+}
+
+#define EMPTYQUE(Xhead) \
+ ((Xhead).q_forw == &(Xhead))
+
+#define INSQUEUE(elem, pred) { \
+ register qelement *Xe = (qelement *) (elem); \
+ register qelement *Xp = (qelement *) (pred); \
+ Xp->q_forw = (Xe->q_forw = (Xe->q_back = Xp)->q_forw)->q_back = Xe; \
+}
+
+#define REMQUE(elem) { \
+ register qelement *Xe = (qelement *) (elem); \
+ (Xe->q_back->q_forw = Xe->q_forw)->q_back = Xe->q_back; \
+}
diff --git a/usr.sbin/sysinstall/main.c b/usr.sbin/sysinstall/main.c
new file mode 100644
index 0000000..47cbbcd
--- /dev/null
+++ b/usr.sbin/sysinstall/main.c
@@ -0,0 +1,204 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated for what's essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sysinstall.h"
+#include <sys/signal.h>
+#include <sys/fcntl.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+const char *StartName; /* Initial contents of argv[0] */
+const char *ProgName = "sysinstall";
+
+static void
+screech(int sig)
+{
+ msgDebug("\007Signal %d caught! That's bad!\n", sig);
+ longjmp(BailOut, sig);
+}
+
+int
+main(int argc, char **argv)
+{
+ int choice, scroll, curr, max, status;
+ char titlestr[80], *arch, *osrel, *ostype;
+ struct rlimit rlim;
+
+ /* Record name to be able to restart */
+ StartName = argv[0];
+
+ /* Catch fatal signals and complain about them if running as init */
+ if (getpid() == 1) {
+ signal(SIGBUS, screech);
+ signal(SIGSEGV, screech);
+ }
+ signal(SIGPIPE, SIG_IGN);
+
+ /* We don't work too well when running as non-root anymore */
+ if (geteuid() != 0) {
+ fprintf(stderr, "Error: This utility should only be run as root.\n");
+ return 1;
+ }
+
+ /*
+ * Given what it does sysinstall (and stuff sysinstall runs like
+ * pkg_add) shouldn't be subject to process limits. Better to just
+ * let them have what they think they need than have them blow
+ * their brains out during an install (in sometimes strange and
+ * mysterious ways).
+ */
+
+ rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
+ if (setrlimit(RLIMIT_DATA, &rlim) != 0)
+ fprintf(stderr, "Warning: setrlimit() of datasize failed.\n");
+ if (setrlimit(RLIMIT_STACK, &rlim) != 0)
+ fprintf(stderr, "Warning: setrlimit() of stacksize failed.\n");
+
+#ifdef PC98
+ {
+ /* XXX */
+ char *p = getenv("TERM");
+ if (p && strcmp(p, "cons25") == 0)
+ setenv("TERM", "cons25w", 1);
+ }
+#endif
+
+ /* Set up whatever things need setting up */
+ systemInitialize(argc, argv);
+
+ /* Set default flag and variable values */
+ installVarDefaults(NULL);
+ /* only when multi-user is it reasonable to do this here */
+ if (!RunningAsInit)
+ installEnvironment();
+
+ if (argc > 1 && !strcmp(argv[1], "-fake")) {
+ variable_set2(VAR_DEBUG, "YES", 0);
+ Fake = TRUE;
+ msgConfirm("I'll be just faking it from here on out, OK?");
+ }
+ if (argc > 1 && !strcmp(argv[1], "-restart"))
+ Restarting = TRUE;
+
+ /* Try to preserve our scroll-back buffer */
+ if (OnVTY) {
+ for (curr = 0; curr < 25; curr++)
+ putchar('\n');
+ }
+ /* Move stderr aside */
+ if (DebugFD)
+ dup2(DebugFD, 2);
+
+ /* Initialize driver modules, if we haven't already done so (ie,
+ the user hit Ctrl-C -> Restart. */
+ if (!pvariable_get("modulesInitialize")) {
+ moduleInitialize();
+ pvariable_set("modulesInitialize=1");
+ }
+
+ /* Probe for all relevant devices on the system */
+ deviceGetAll();
+
+ /* Prompt for the driver floppy if appropriate. */
+ if (!pvariable_get("driverFloppyCheck")) {
+ driverFloppyCheck();
+ pvariable_set("driverFloppyCheck=1");
+ }
+
+ /* First, see if we have any arguments to process (and argv[0] counts if it's not "sysinstall") */
+ if (!RunningAsInit) {
+ int i, start_arg;
+
+ if (!strstr(argv[0], "sysinstall"))
+ start_arg = 0;
+ else if (Fake || Restarting)
+ start_arg = 2;
+ else
+ start_arg = 1;
+ for (i = start_arg; i < argc; i++) {
+ if (DITEM_STATUS(dispatchCommand(argv[i])) != DITEM_SUCCESS)
+ systemShutdown(1);
+ }
+ if (argc > start_arg)
+ systemShutdown(0);
+ }
+ else
+ dispatch_load_file_int(TRUE);
+
+ status = setjmp(BailOut);
+ if (status) {
+ msgConfirm("A signal %d was caught - I'm saving what I can and shutting\n"
+ "down. If you can reproduce the problem, please turn Debug on\n"
+ "in the Options menu for the extra information it provides\n"
+ "in debugging problems like this.", status);
+ systemShutdown(status);
+ }
+
+ /* Get user's country and keymap */
+ if (RunningAsInit)
+ configCountry(NULL);
+
+ /* Add FreeBSD version info to the menu title */
+ arch = getsysctlbyname("hw.machine_arch");
+ osrel = getsysctlbyname("kern.osrelease");
+ ostype = getsysctlbyname("kern.ostype");
+ snprintf(titlestr, sizeof(titlestr), "%s/%s %s - %s", ostype, arch,
+ osrel, MenuInitial.title);
+ free(arch);
+ free(osrel);
+ free(ostype);
+ MenuInitial.title = titlestr;
+
+ /* Begin user dialog at outer menu */
+ dialog_clear();
+ while (1) {
+ choice = scroll = curr = max = 0;
+ dmenuOpen(&MenuInitial, &choice, &scroll, &curr, &max, TRUE);
+ if (getpid() != 1
+#if defined(__sparc64__)
+ || !msgNoYes("Are you sure you wish to exit? The system will halt.")
+#else
+ || !msgNoYes("Are you sure you wish to exit? The system will reboot.")
+#endif
+ )
+ break;
+ }
+
+ /* Say goodnight, Gracie */
+ systemShutdown(0);
+
+ return 0; /* We should never get here */
+}
diff --git a/usr.sbin/sysinstall/media.c b/usr.sbin/sysinstall/media.c
new file mode 100644
index 0000000..643f28b
--- /dev/null
+++ b/usr.sbin/sysinstall/media.c
@@ -0,0 +1,834 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sysinstall.h"
+#include <signal.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <resolv.h>
+
+static Boolean got_intr = FALSE;
+static Boolean ftp_skip_resolve = FALSE;
+
+/* timeout handler */
+static void
+handle_intr(int sig)
+{
+ msgDebug("User generated interrupt.\n");
+ got_intr = TRUE;
+}
+
+static int
+check_for_interrupt(void)
+{
+ if (got_intr) {
+ got_intr = FALSE;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static int
+genericHook(dialogMenuItem *self, DeviceType type)
+{
+ Device **devs;
+
+ devs = deviceFind(self->prompt, type);
+ if (devs)
+ mediaDevice = devs[0];
+ return (devs ? DITEM_LEAVE_MENU : DITEM_FAILURE);
+}
+
+static int
+cdromHook(dialogMenuItem *self)
+{
+ return genericHook(self, DEVICE_TYPE_CDROM);
+}
+
+static void
+kickstart_dns(void)
+{
+ static Boolean initted = FALSE;
+ int time;
+ char *cp;
+
+ cp = variable_get(VAR_MEDIA_TIMEOUT);
+ if (!cp)
+ time = MEDIA_TIMEOUT;
+ else
+ time = atoi(cp);
+ if (!time)
+ time = 100;
+ if (!initted) {
+ res_init();
+ _res.retry = 2; /* 2 times seems a reasonable number to me */
+ _res.retrans = time / 2; /* so spend half our alloted time on each try */
+ initted = TRUE;
+ }
+}
+
+char *
+cpioVerbosity()
+{
+ char *cp = variable_get(VAR_CPIO_VERBOSITY);
+
+ if (cp && !strcmp(cp, "high"))
+ return "-v";
+ else if (cp && !strcmp(cp, "medium"))
+ return "-V";
+ return "";
+}
+
+int
+mediaOpen(void)
+{
+ if (!mediaDevice || !mediaVerify() || !DEVICE_INIT(mediaDevice))
+ return DITEM_FAILURE;
+ return DITEM_SUCCESS;
+}
+
+void
+mediaClose(void)
+{
+ if (mediaDevice)
+ DEVICE_SHUTDOWN(mediaDevice);
+ mediaDevice = NULL;
+}
+
+/*
+ * Return 1 if we successfully found and set the installation type to
+ * be a CD.
+ */
+int
+mediaSetCDROM(dialogMenuItem *self)
+{
+ Device **devs;
+ int cnt;
+
+ mediaClose();
+ devs = deviceFind(NULL, DEVICE_TYPE_CDROM);
+ cnt = deviceCount(devs);
+ if (!cnt) {
+ if (self) /* Interactive? */
+ msgConfirm("No CD/DVD devices found! Please check that your system's\n"
+ "configuration is correct and that the CD/DVD drive is of a supported\n"
+ "type. For more information, consult the hardware guide\n"
+ "in the Doc menu.");
+ return DITEM_FAILURE | DITEM_CONTINUE;
+ }
+ else if (cnt > 1) {
+ DMenu *menu;
+ int status;
+
+ menu = deviceCreateMenu(&MenuMediaCDROM, DEVICE_TYPE_CDROM, cdromHook, NULL);
+ if (!menu)
+ msgFatal("Unable to create CDROM menu! Something is seriously wrong.");
+ status = dmenuOpenSimple(menu, FALSE);
+ free(menu);
+ if (!status)
+ return DITEM_FAILURE;
+ }
+ else
+ mediaDevice = devs[0];
+ return (mediaDevice ? DITEM_SUCCESS | DITEM_LEAVE_MENU : DITEM_FAILURE);
+}
+
+static int
+floppyHook(dialogMenuItem *self)
+{
+ return genericHook(self, DEVICE_TYPE_FLOPPY);
+}
+
+/*
+ * Return 1 if we successfully found and set the installation type to
+ * be a floppy
+ */
+int
+mediaSetFloppy(dialogMenuItem *self)
+{
+ Device **devs;
+ int cnt;
+
+ mediaClose();
+ devs = deviceFind(NULL, DEVICE_TYPE_FLOPPY);
+ cnt = deviceCount(devs);
+ if (!cnt) {
+ msgConfirm("No floppy devices found! Please check that your system's configuration\n"
+ "is correct. For more information, consult the hardware guide in the Doc\n"
+ "menu.");
+ return DITEM_FAILURE | DITEM_CONTINUE;
+ }
+ else if (cnt > 1) {
+ DMenu *menu;
+ int status;
+
+ menu = deviceCreateMenu(&MenuMediaFloppy, DEVICE_TYPE_FLOPPY, floppyHook, NULL);
+ if (!menu)
+ msgFatal("Unable to create Floppy menu! Something is seriously wrong.");
+ status = dmenuOpenSimple(menu, FALSE);
+ free(menu);
+ if (!status)
+ return DITEM_FAILURE;
+ }
+ else
+ mediaDevice = devs[0];
+ if (mediaDevice)
+ mediaDevice->private = NULL;
+ return (mediaDevice ? DITEM_LEAVE_MENU : DITEM_FAILURE);
+}
+
+static int
+DOSHook(dialogMenuItem *self)
+{
+ return genericHook(self, DEVICE_TYPE_DOS);
+}
+
+/*
+ * Return 1 if we successfully found and set the installation type to
+ * be a DOS partition.
+ */
+int
+mediaSetDOS(dialogMenuItem *self)
+{
+ Device **devs;
+ int cnt;
+
+ mediaClose();
+ devs = deviceFind(NULL, DEVICE_TYPE_DOS);
+ cnt = deviceCount(devs);
+ if (!cnt) {
+ msgConfirm("No DOS primary partitions found! This installation method is unavailable");
+ return DITEM_FAILURE | DITEM_CONTINUE;
+ }
+ else if (cnt > 1) {
+ DMenu *menu;
+ int status;
+
+ menu = deviceCreateMenu(&MenuMediaDOS, DEVICE_TYPE_DOS, DOSHook, NULL);
+ if (!menu)
+ msgFatal("Unable to create DOS menu! Something is seriously wrong.");
+ status = dmenuOpenSimple(menu, FALSE);
+ free(menu);
+ if (!status)
+ return DITEM_FAILURE;
+ }
+ else
+ mediaDevice = devs[0];
+ return (mediaDevice ? DITEM_LEAVE_MENU : DITEM_FAILURE);
+}
+
+/*
+ * Return 0 if we successfully found and set the installation type to
+ * be an ftp server
+ */
+int
+mediaSetFTP(dialogMenuItem *self)
+{
+ static Device ftpDevice;
+ char *cp, hbuf[MAXHOSTNAMELEN], *hostname, *dir;
+ struct addrinfo hints, *res;
+ int af;
+ size_t urllen;
+ extern int FtpPort;
+ static Device *networkDev = NULL;
+
+ mediaClose();
+ cp = variable_get(VAR_FTP_PATH);
+ /* If we've been through here before ... */
+ if (networkDev && cp && msgYesNo("Re-use old FTP site selection values?"))
+ cp = NULL;
+ if (!cp) {
+ if (!dmenuOpenSimple(&MenuMediaFTP, FALSE))
+ return DITEM_FAILURE;
+ else
+ cp = variable_get(VAR_FTP_PATH);
+ }
+ if (!cp)
+ return DITEM_FAILURE;
+ else if (!strcmp(cp, "other")) {
+ variable_set2(VAR_FTP_PATH, "ftp://", 0);
+ cp = variable_get_value(VAR_FTP_PATH, "Please specify the URL of a FreeBSD distribution on a\n"
+ "remote ftp site. This site must accept either anonymous\n"
+ "ftp or you should have set an ftp username and password\n"
+ "in the Options screen.\n\n"
+ "A URL looks like this: ftp://<hostname>/<path>\n"
+ "Where <path> is relative to the anonymous ftp directory or the\n"
+ "home directory of the user being logged in as.", 0);
+ if (!cp || !*cp || !strcmp(cp, "ftp://")) {
+ variable_unset(VAR_FTP_PATH);
+ return DITEM_FAILURE;
+ }
+ urllen = strlen(cp);
+ if (urllen >= sizeof(ftpDevice.name)) {
+ msgConfirm("Length of specified URL is %d characters. Allowable maximum is %d.",
+ urllen,sizeof(ftpDevice.name)-1);
+ variable_unset(VAR_FTP_PATH);
+ return DITEM_FAILURE;
+ }
+ }
+ if (strncmp("ftp://", cp, 6)) {
+ msgConfirm("Sorry, %s is an invalid URL!", cp);
+ variable_unset(VAR_FTP_PATH);
+ return DITEM_FAILURE;
+ }
+ SAFE_STRCPY(ftpDevice.name, cp);
+ SAFE_STRCPY(hbuf, cp + 6);
+ hostname = hbuf;
+
+ if (!networkDev || msgYesNo("You've already done the network configuration once,\n"
+ "would you like to skip over it now?") != 0) {
+ if (networkDev)
+ DEVICE_SHUTDOWN(networkDev);
+ if (!(networkDev = tcpDeviceSelect())) {
+ variable_unset(VAR_FTP_PATH);
+ return DITEM_FAILURE;
+ }
+ }
+ if (!DEVICE_INIT(networkDev)) {
+ if (isDebug())
+ msgDebug("mediaSetFTP: Net device init failed.\n");
+ variable_unset(VAR_FTP_PATH);
+ return DITEM_FAILURE;
+ }
+ if (*hostname == '[' && (cp = index(hostname + 1, ']')) != NULL &&
+ (*++cp == '\0' || *cp == '/' || *cp == ':')) {
+ ++hostname;
+ *(cp - 1) = '\0';
+ }
+ else
+ cp = index(hostname, ':');
+ if (cp != NULL && *cp == ':') {
+ *(cp++) = '\0';
+ FtpPort = strtol(cp, 0, 0);
+ }
+ else
+ FtpPort = 21;
+ if ((dir = index(cp ? cp : hostname, '/')) != NULL)
+ *(dir++) = '\0';
+ if (isDebug()) {
+ msgDebug("hostname = `%s'\n", hostname);
+ msgDebug("dir = `%s'\n", dir ? dir : "/");
+ msgDebug("port # = `%d'\n", FtpPort);
+ }
+ if (!ftp_skip_resolve && variable_get(VAR_NAMESERVER)) {
+ msgNotify("Looking up host %s.", hostname);
+ if (isDebug())
+ msgDebug("Starting DNS.\n");
+ kickstart_dns();
+ if (isDebug())
+ msgDebug("Looking up hostname, %s, using getaddrinfo(AI_NUMERICHOST).\n", hostname);
+ af = variable_cmp(VAR_IPV6_ENABLE, "YES") ? AF_INET : AF_UNSPEC;
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = af;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
+ if (getaddrinfo(hostname, NULL, &hints, &res) != 0) {
+ if (isDebug())
+ msgDebug("Looking up hostname, %s, using getaddrinfo().\n",
+ hostname);
+ hints.ai_flags = AI_PASSIVE;
+ if (getaddrinfo(hostname, NULL, &hints, &res) != 0) {
+ msgConfirm("Cannot resolve hostname `%s'! Are you sure that"
+ " your\nname server, gateway and network interface are"
+ " correctly configured?", hostname);
+ if (networkDev)
+ DEVICE_SHUTDOWN(networkDev);
+ networkDev = NULL;
+ variable_unset(VAR_FTP_PATH);
+ return DITEM_FAILURE;
+ }
+ }
+ freeaddrinfo(res);
+ if (isDebug())
+ msgDebug("Found DNS entry for %s successfully..\n", hostname);
+ }
+ variable_set2(VAR_FTP_HOST, hostname, 0);
+ variable_set2(VAR_FTP_DIR, dir ? dir : "/", 0);
+ variable_set2(VAR_FTP_PORT, itoa(FtpPort), 0);
+ ftpDevice.type = DEVICE_TYPE_FTP;
+ ftpDevice.init = mediaInitFTP;
+ ftpDevice.get = mediaGetFTP;
+ ftpDevice.shutdown = mediaShutdownFTP;
+ ftpDevice.private = networkDev;
+ mediaDevice = &ftpDevice;
+ return DITEM_SUCCESS | DITEM_LEAVE_MENU | DITEM_RESTORE;
+}
+
+int
+mediaSetFTPActive(dialogMenuItem *self)
+{
+ variable_set2(VAR_FTP_STATE, "active", 0);
+ return mediaSetFTP(self);
+}
+
+int
+mediaSetFTPPassive(dialogMenuItem *self)
+{
+ variable_set2(VAR_FTP_STATE, "passive", 0);
+ return mediaSetFTP(self);
+}
+
+int mediaSetHTTP(dialogMenuItem *self)
+{
+ Boolean tmp;
+ int result;
+ char *cp, *idx, hbuf[MAXHOSTNAMELEN], *hostname;
+ int HttpPort;
+ int what = DITEM_RESTORE;
+
+
+ tmp = ftp_skip_resolve;
+ ftp_skip_resolve = TRUE;
+ result = mediaSetFTP(self);
+ ftp_skip_resolve = tmp;
+
+ if (DITEM_STATUS(result) != DITEM_SUCCESS)
+ return result;
+
+ cp = variable_get_value(VAR_HTTP_PROXY,
+ "Please enter the address of the HTTP proxy in this format:\n"
+ " hostname:port (the ':port' is optional, default is 3128)",0);
+ if (!cp)
+ return DITEM_FAILURE;
+ SAFE_STRCPY(hbuf, cp);
+ hostname = hbuf;
+ if (*hostname == '[' && (idx = index(hostname + 1, ']')) != NULL &&
+ (*++idx == '\0' || *idx == ':')) {
+ ++hostname;
+ *(idx - 1) = '\0';
+ } else
+ idx = index(hostname, ':');
+ if (idx == NULL || *idx != ':')
+ HttpPort = 3128; /* try this as default */
+ else {
+ *(idx++) = '\0';
+ HttpPort = strtol(idx, 0, 0);
+ }
+
+ variable_set2(VAR_HTTP_HOST, hostname, 0);
+ variable_set2(VAR_HTTP_PORT, itoa(HttpPort), 0);
+ if (isDebug()) {
+ msgDebug("VAR_FTP_PATH : %s\n",variable_get(VAR_FTP_PATH));
+ msgDebug("VAR_HTTP_HOST, _PORT: %s:%s\n",variable_get(VAR_HTTP_HOST),
+ variable_get(VAR_HTTP_PORT));
+ }
+
+ /* mediaDevice has been set by mediaSetFTP(), overwrite partly: */
+ mediaDevice->type = DEVICE_TYPE_HTTP;
+ mediaDevice->init = mediaInitHTTP;
+ mediaDevice->get = mediaGetHTTP;
+ mediaDevice->shutdown = dummyShutdown;
+ return DITEM_SUCCESS | DITEM_LEAVE_MENU | what;
+}
+
+
+int
+mediaSetUFS(dialogMenuItem *self)
+{
+ static Device ufsDevice;
+ struct statfs st;
+ char *cp;
+
+ mediaClose();
+ cp = variable_get_value(VAR_UFS_PATH, "Enter a fully qualified pathname for the directory\n"
+ "containing the FreeBSD distribution files:", 0);
+ if (!cp)
+ return DITEM_FAILURE;
+
+ /* If they gave us a CDROM or something, try and pick a better name */
+ if (statfs(cp, &st))
+ strcpy(ufsDevice.name, "ufs");
+ else
+ strcpy(ufsDevice.name, st.f_fstypename);
+
+ ufsDevice.type = DEVICE_TYPE_UFS;
+ ufsDevice.init = dummyInit;
+ ufsDevice.get = mediaGetUFS;
+ ufsDevice.shutdown = dummyShutdown;
+ ufsDevice.private = strdup(cp);
+ mediaDevice = &ufsDevice;
+ return DITEM_LEAVE_MENU;
+}
+
+int
+mediaSetNFS(dialogMenuItem *self)
+{
+ static Device nfsDevice;
+ static Device *networkDev = NULL;
+ char *cp, *idx;
+ char hostname[MAXPATHLEN];
+ size_t pathlen;
+
+ mediaClose();
+ cp = variable_get_value(VAR_NFS_PATH, "Please enter the full NFS file specification for the remote\n"
+ "host and directory containing the FreeBSD distribution files.\n"
+ "This should be in the format: hostname:/some/freebsd/dir", 0);
+ if (!cp)
+ return DITEM_FAILURE;
+ SAFE_STRCPY(hostname, cp);
+ if (!(idx = index(hostname, ':'))) {
+ msgConfirm("Invalid NFS path specification. Must be of the form:\n"
+ "host:/full/pathname/to/FreeBSD/distdir");
+ return DITEM_FAILURE;
+ }
+ pathlen = strlen(hostname);
+ if (pathlen >= sizeof(nfsDevice.name)) {
+ msgConfirm("Length of specified NFS path is %d characters. Allowable maximum is %d.",
+ pathlen,sizeof(nfsDevice.name)-1);
+ variable_unset(VAR_NFS_PATH);
+ return DITEM_FAILURE;
+ }
+ SAFE_STRCPY(nfsDevice.name, hostname);
+ *idx = '\0';
+ if (!networkDev || msgYesNo("You've already done the network configuration once,\n"
+ "would you like to skip over it now?") != 0) {
+ if (networkDev)
+ DEVICE_SHUTDOWN(networkDev);
+ if (!(networkDev = tcpDeviceSelect()))
+ return DITEM_FAILURE;
+ }
+ if (!DEVICE_INIT(networkDev)) {
+ if (isDebug())
+ msgDebug("mediaSetNFS: Net device init failed\n");
+ }
+ if (variable_get(VAR_NAMESERVER)) {
+ kickstart_dns();
+ if ((inet_addr(hostname) == INADDR_NONE) && (gethostbyname(hostname) == NULL)) {
+ msgConfirm("Cannot resolve hostname `%s'! Are you sure that your\n"
+ "name server, gateway and network interface are correctly configured?", hostname);
+ if (networkDev)
+ DEVICE_SHUTDOWN(networkDev);
+ networkDev = NULL;
+ variable_unset(VAR_NFS_PATH);
+ return DITEM_FAILURE;
+ }
+ else {
+ if (isDebug())
+ msgDebug("Found DNS entry for %s successfully..\n", hostname);
+ }
+ }
+ variable_set2(VAR_NFS_HOST, hostname, 0);
+ nfsDevice.type = DEVICE_TYPE_NFS;
+ nfsDevice.init = mediaInitNFS;
+ nfsDevice.get = mediaGetNFS;
+ nfsDevice.shutdown = mediaShutdownNFS;
+ nfsDevice.private = networkDev;
+ mediaDevice = &nfsDevice;
+ return DITEM_LEAVE_MENU;
+}
+
+Boolean
+mediaExtractDistBegin(char *dir, int *fd, int *zpid, int *cpid)
+{
+ int i, pfd[2],qfd[2];
+
+ if (!dir)
+ dir = "/";
+ Mkdir(dir);
+ chdir(dir);
+ pipe(pfd);
+ pipe(qfd);
+ *zpid = fork();
+ if (!*zpid) {
+ char *unzipper = RunningAsInit ? "/stand/" UNZIPPER
+ : "/usr/bin/" UNZIPPER;
+
+ dup2(qfd[0], 0); close(qfd[0]);
+ dup2(pfd[1], 1); close(pfd[1]);
+ if (DebugFD != -1)
+ dup2(DebugFD, 2);
+ else {
+ close(2);
+ open("/dev/null", O_WRONLY);
+ }
+ close(qfd[1]);
+ close(pfd[0]);
+ i = execl(unzipper, unzipper, (char *)0);
+ if (isDebug())
+ msgDebug("%s command returns %d status\n", unzipper, i);
+ exit(i);
+ }
+ *fd = qfd[1];
+ close(qfd[0]);
+ *cpid = fork();
+ if (!*cpid) {
+ char *cpio = RunningAsInit ? "/stand/cpio" : "/usr/bin/cpio";
+
+ dup2(pfd[0], 0); close(pfd[0]);
+ close(pfd[1]);
+ close(qfd[1]);
+ if (DebugFD != -1) {
+ dup2(DebugFD, 1);
+ dup2(DebugFD, 2);
+ }
+ else {
+ close(1); open("/dev/null", O_WRONLY);
+ dup2(1, 2);
+ }
+ if (strlen(cpioVerbosity()))
+ i = execl(cpio, cpio, "-idum", cpioVerbosity(), (char *)0);
+ else
+ i = execl(cpio, cpio, "-idum", (char *)0);
+ if (isDebug())
+ msgDebug("%s command returns %d status\n", cpio, i);
+ exit(i);
+ }
+ close(pfd[0]);
+ close(pfd[1]);
+ return TRUE;
+}
+
+Boolean
+mediaExtractDistEnd(int zpid, int cpid)
+{
+ int i,j;
+
+ i = waitpid(zpid, &j, 0);
+ /* Don't check exit status - gunzip seems to return a bogus one! */
+ if (i < 0) {
+ if (isDebug())
+ msgDebug("wait for %s returned status of %d!\n", UNZIPPER, i);
+ return FALSE;
+ }
+ i = waitpid(cpid, &j, 0);
+ if (i < 0 || WEXITSTATUS(j)) {
+ if (isDebug())
+ msgDebug("cpio returned error status of %d!\n", WEXITSTATUS(j));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+Boolean
+mediaExtractDist(char *dir, char *dist, FILE *fp)
+{
+ int i, j, total, seconds, zpid, cpid, pfd[2], qfd[2];
+ char buf[BUFSIZ];
+ struct timeval start, stop;
+ struct sigaction new, old;
+
+ if (!dir)
+ dir = "/";
+
+ Mkdir(dir);
+ chdir(dir);
+ pipe(pfd); /* read end */
+ pipe(qfd); /* write end */
+ zpid = fork();
+ if (!zpid) {
+ char *unzipper = RunningAsInit ? "/stand/" UNZIPPER
+ : "/usr/bin/" UNZIPPER;
+
+ fclose(fp);
+ close(qfd[1]);
+ dup2(qfd[0], 0); close(qfd[0]);
+
+ close(pfd[0]);
+ dup2(pfd[1], 1); close(pfd[1]);
+
+ if (DebugFD != -1)
+ dup2(DebugFD, 2);
+ else {
+ close(2);
+ open("/dev/null", O_WRONLY);
+ }
+ i = execl(unzipper, unzipper, (char *)0);
+ if (isDebug())
+ msgDebug("%s command returns %d status\n", unzipper, i);
+ exit(i);
+ }
+ cpid = fork();
+ if (!cpid) {
+ char *cpio = RunningAsInit ? "/stand/cpio" : "/usr/bin/cpio";
+
+ close(pfd[1]);
+ dup2(pfd[0], 0); close(pfd[0]);
+ close (qfd[0]); close(qfd[1]);
+ fclose(fp);
+ if (DebugFD != -1) {
+ dup2(DebugFD, 1);
+ dup2(DebugFD, 2);
+ }
+ else {
+ dup2(open("/dev/null", O_WRONLY), 1);
+ dup2(1, 2);
+ }
+ if (strlen(cpioVerbosity()))
+ i = execl(cpio, cpio, "-idum", cpioVerbosity(), (char *)0);
+ else
+ i = execl(cpio, cpio, "-idum", "--block-size", (char *)0);
+ if (isDebug())
+ msgDebug("%s command returns %d status\n", cpio, i);
+ exit(i);
+ }
+ close(pfd[0]); close(pfd[1]);
+ close(qfd[0]);
+
+ total = 0;
+ (void)gettimeofday(&start, (struct timezone *)0);
+
+ /* Make ^C abort the current transfer rather than the whole show */
+ new.sa_handler = handle_intr;
+ new.sa_flags = 0;
+ (void)sigemptyset(&new.sa_mask);
+ sigaction(SIGINT, &new, &old);
+
+ while ((i = fread(buf, 1, BUFSIZ, fp)) > 0) {
+ if (check_for_interrupt()) {
+ msgConfirm("Failure to read from media: User interrupt.");
+ break;
+ }
+ if (write(qfd[1], buf, i) != i) {
+ msgConfirm("Write error on transfer to cpio process, try of %d bytes.", i);
+ break;
+ }
+ else {
+ (void)gettimeofday(&stop, (struct timezone *)0);
+ stop.tv_sec = stop.tv_sec - start.tv_sec;
+ stop.tv_usec = stop.tv_usec - start.tv_usec;
+ if (stop.tv_usec < 0)
+ stop.tv_sec--, stop.tv_usec += 1000000;
+ seconds = stop.tv_sec + (stop.tv_usec / 1000000.0);
+ if (!seconds)
+ seconds = 1;
+ total += i;
+ msgInfo("%10d bytes read from %s dist @ %.1f KB/sec.",
+ total, dist, (total / seconds) / 1024.0);
+ }
+ }
+ sigaction(SIGINT, &old, NULL); /* restore sigint */
+ close(qfd[1]);
+
+ i = waitpid(zpid, &j, 0);
+ /* Don't check exit status - gunzip seems to return a bogus one! */
+ if (i < 0) {
+ if (isDebug())
+ msgDebug("wait for %s returned status of %d!\n", UNZIPPER, i);
+ return FALSE;
+ }
+ i = waitpid(cpid, &j, 0);
+ if (i < 0 || WEXITSTATUS(j)) {
+ if (isDebug())
+ msgDebug("cpio returned error status of %d!\n", WEXITSTATUS(j));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+int
+mediaGetType(dialogMenuItem *self)
+{
+ return ((dmenuOpenSimple(&MenuMedia, FALSE) && mediaDevice) ? DITEM_SUCCESS : DITEM_FAILURE);
+}
+
+/* Return TRUE if all the media variables are set up correctly */
+Boolean
+mediaVerify(void)
+{
+ if (!mediaDevice)
+ return (DITEM_STATUS(mediaGetType(NULL)) == DITEM_SUCCESS);
+ return TRUE;
+}
+
+/* Set the FTP username and password fields */
+int
+mediaSetFTPUserPass(dialogMenuItem *self)
+{
+ char *pass;
+
+ if (variable_get_value(VAR_FTP_USER, "Please enter the username you wish to login as:", 0)) {
+ DialogInputAttrs |= DITEM_NO_ECHO;
+ pass = variable_get_value(VAR_FTP_PASS, "Please enter the password for this user:", 0);
+ DialogInputAttrs &= ~DITEM_NO_ECHO;
+ }
+ else
+ pass = NULL;
+ return (pass ? DITEM_SUCCESS : DITEM_FAILURE);
+}
+
+/* Set CPIO verbosity level */
+int
+mediaSetCPIOVerbosity(dialogMenuItem *self)
+{
+ char *cp = variable_get(VAR_CPIO_VERBOSITY);
+
+ if (!cp) {
+ msgConfirm("CPIO Verbosity is not set to anything!");
+ return DITEM_FAILURE;
+ }
+ else {
+ if (!strcmp(cp, "low"))
+ variable_set2(VAR_CPIO_VERBOSITY, "medium", 0);
+ else if (!strcmp(cp, "medium"))
+ variable_set2(VAR_CPIO_VERBOSITY, "high", 0);
+ else /* must be "high" - wrap around */
+ variable_set2(VAR_CPIO_VERBOSITY, "low", 0);
+ }
+ return DITEM_SUCCESS;
+}
+
+/* A generic open which follows a well-known "path" of places to look */
+FILE *
+mediaGenericGet(char *base, const char *file)
+{
+ char buf[PATH_MAX];
+
+ snprintf(buf, PATH_MAX, "%s/%s", base, file);
+ if (file_readable(buf))
+ return fopen(buf, "r");
+ snprintf(buf, PATH_MAX, "%s/FreeBSD/%s", base, file);
+ if (file_readable(buf))
+ return fopen(buf, "r");
+ snprintf(buf, PATH_MAX, "%s/releases/%s", base, file);
+ if (file_readable(buf))
+ return fopen(buf, "r");
+ snprintf(buf, PATH_MAX, "%s/%s/%s", base, variable_get(VAR_RELNAME), file);
+ if (file_readable(buf))
+ return fopen(buf, "r");
+ snprintf(buf, PATH_MAX, "%s/releases/%s/%s", base, variable_get(VAR_RELNAME), file);
+ return fopen(buf, "r");
+}
+
diff --git a/usr.sbin/sysinstall/menus.c b/usr.sbin/sysinstall/menus.c
new file mode 100644
index 0000000..9269216
--- /dev/null
+++ b/usr.sbin/sysinstall/menus.c
@@ -0,0 +1,2090 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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
+
+#include "sysinstall.h"
+
+/* Miscellaneous work routines for menus */
+static int
+setSrc(dialogMenuItem *self)
+{
+ Dists |= DIST_SRC;
+ SrcDists = DIST_SRC_ALL;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearSrc(dialogMenuItem *self)
+{
+ Dists &= ~DIST_SRC;
+ SrcDists = 0;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+setKernel(dialogMenuItem *self)
+{
+ Dists |= DIST_KERNEL;
+ KernelDists = DIST_KERNEL_ALL;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearKernel(dialogMenuItem *self)
+{
+ Dists &= ~DIST_KERNEL;
+ KernelDists = 0;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+#define _IS_SET(dist, set) (((dist) & (set)) == (set))
+
+#define IS_DEVELOPER(dist, extra) (_IS_SET(dist, _DIST_DEVELOPER | extra) || \
+ _IS_SET(dist, _DIST_DEVELOPER | extra))
+
+#define IS_USER(dist, extra) (_IS_SET(dist, _DIST_USER | extra) || \
+ _IS_SET(dist, _DIST_USER | extra))
+
+static int
+checkDistDeveloper(dialogMenuItem *self)
+{
+ return IS_DEVELOPER(Dists, 0) && _IS_SET(SrcDists, DIST_SRC_ALL);
+}
+
+static int
+checkDistKernDeveloper(dialogMenuItem *self)
+{
+ return IS_DEVELOPER(Dists, 0) && _IS_SET(SrcDists, DIST_SRC_SYS);
+}
+
+static int
+checkDistUser(dialogMenuItem *self)
+{
+ return IS_USER(Dists, 0);
+}
+
+static int
+checkDistMinimum(dialogMenuItem *self)
+{
+ return Dists == (DIST_BASE | DIST_KERNEL);
+}
+
+static int
+checkDistEverything(dialogMenuItem *self)
+{
+ return Dists == DIST_ALL &&
+ _IS_SET(SrcDists, DIST_SRC_ALL) &&
+ _IS_SET(KernelDists, DIST_KERNEL_ALL);
+}
+
+static int
+srcFlagCheck(dialogMenuItem *item)
+{
+ return SrcDists;
+}
+
+static int
+kernelFlagCheck(dialogMenuItem *item)
+{
+ return KernelDists;
+}
+
+static int
+checkTrue(dialogMenuItem *item)
+{
+ return TRUE;
+}
+
+/* All the system menus go here.
+ *
+ * Hardcoded things like version number strings will disappear from
+ * these menus just as soon as I add the code for doing inline variable
+ * expansion.
+ */
+
+DMenu MenuIndex = {
+ DMENU_NORMAL_TYPE,
+ "Glossary of functions",
+ "This menu contains an alphabetized index of the top level functions in\n"
+ "this program (sysinstall). Invoke an option by pressing [SPACE] or\n"
+ "[ENTER]. To exit, use [TAB] to move to the Cancel button.",
+ "Use PageUp or PageDown to move through this menu faster!",
+ NULL,
+ { { " Anon FTP", "Configure anonymous FTP logins.", dmenuVarCheck, configAnonFTP, NULL, "anon_ftp" },
+ { " Commit", "Commit any pending actions (dangerous!)", NULL, installCustomCommit },
+ { " Country", "Set the system's country", NULL, configCountry },
+#ifdef WITH_SYSCONS
+ { " Console settings", "Customize system console behavior.", NULL, dmenuSubmenu, NULL, &MenuSyscons },
+#endif
+ { " Configure", "The system configuration menu.", NULL, dmenuSubmenu, NULL, &MenuConfigure },
+ { " Defaults, Load (FDD)","Load default settings from floppy.", NULL, dispatch_load_floppy },
+ { " Defaults, Load (CD)", "Load default settings from CDROM.", NULL, dispatch_load_cdrom },
+ { " Defaults, Load", "Load default settings (all devices).", NULL, dispatch_load_menu },
+#ifdef WITH_MICE
+ { " Device, Mouse", "The mouse configuration menu.", NULL, dmenuSubmenu, NULL, &MenuMouse },
+#endif
+ { " Disklabel", "The disk Label editor", NULL, diskLabelEditor },
+ { " Dists, All", "Root of the distribution tree.", NULL, dmenuSubmenu, NULL, &MenuDistributions },
+ { " Dists, Basic", "Basic FreeBSD distribution menu.", NULL, dmenuSubmenu, NULL, &MenuSubDistributions },
+ { " Dists, Developer", "Select developer's distribution.", checkDistDeveloper, distSetDeveloper },
+ { " Dists, Src", "Src distribution menu.", NULL, dmenuSubmenu, NULL, &MenuSrcDistributions },
+ { " Dists, Kern Developer", "Select kernel developer's distribution.", checkDistKernDeveloper, distSetKernDeveloper },
+ { " Dists, User", "Select average user distribution.", checkDistUser, distSetUser },
+ { " Distributions, Adding", "Installing additional distribution sets", NULL, distExtractAll },
+ { " Documentation", "Installation instructions, README, etc.", NULL, dmenuSubmenu, NULL, &MenuDocumentation },
+ { " Doc, README", "The distribution README file.", NULL, dmenuDisplayFile, NULL, "README" },
+ { " Doc, Errata", "The distribution errata.", NULL, dmenuDisplayFile, NULL, "ERRATA" },
+ { " Doc, Hardware", "The distribution hardware guide.", NULL, dmenuDisplayFile, NULL, "HARDWARE" },
+ { " Doc, Copyright", "The distribution copyright notices.", NULL, dmenuDisplayFile, NULL, "COPYRIGHT" },
+ { " Doc, Release", "The distribution release notes.", NULL, dmenuDisplayFile, NULL, "RELNOTES" },
+ { " Doc, HTML", "The HTML documentation menu.", NULL, docBrowser },
+ { " Dump Vars", "(debugging) dump out internal variables.", NULL, dump_variables },
+ { " Emergency shell", "Start an Emergency Holographic shell.", NULL, installFixitHoloShell },
+#ifdef WITH_SLICES
+ { " Fdisk", "The disk Partition Editor", NULL, diskPartitionEditor },
+#endif
+ { " Fixit", "Repair mode with CDROM or fixit floppy.", NULL, dmenuSubmenu, NULL, &MenuFixit },
+ { " FTP sites", "The FTP mirror site listing.", NULL, dmenuSubmenu, NULL, &MenuMediaFTP },
+ { " Gateway", "Set flag to route packets between interfaces.", dmenuVarCheck, dmenuToggleVariable, NULL, "gateway=YES" },
+ { " HTML Docs", "The HTML documentation menu", NULL, docBrowser },
+ { " inetd Configuration", "Configure inetd and simple internet services.", dmenuVarCheck, configInetd, NULL, "inetd_enable=YES" },
+ { " Install, Standard", "A standard system installation.", NULL, installStandard },
+ { " Install, Express", "An express system installation.", NULL, installExpress },
+ { " Install, Custom", "The custom installation menu", NULL, dmenuSubmenu, NULL, &MenuInstallCustom },
+ { " Label", "The disk Label editor", NULL, diskLabelEditor },
+ { " Media", "Top level media selection menu.", NULL, dmenuSubmenu, NULL, &MenuMedia },
+ { " Media, NFS", "Select NFS installation media.", NULL, mediaSetNFS },
+ { " Media, Floppy", "Select floppy installation media.", NULL, mediaSetFloppy },
+ { " Media, CDROM/DVD", "Select CDROM/DVD installation media.", NULL, mediaSetCDROM },
+ { " Media, DOS", "Select DOS installation media.", NULL, mediaSetDOS },
+ { " Media, UFS", "Select UFS installation media.", NULL, mediaSetUFS },
+ { " Media, FTP", "Select FTP installation media.", NULL, mediaSetFTP },
+ { " Media, FTP Passive", "Select passive FTP installation media.", NULL, mediaSetFTPPassive },
+ { " Media, HTTP", "Select FTP via HTTP proxy installation media.", NULL, mediaSetHTTP },
+ { " Network Interfaces", "Configure network interfaces", NULL, tcpMenuSelect },
+ { " Networking Services", "The network services menu.", NULL, dmenuSubmenu, NULL, &MenuNetworking },
+ { " NFS, client", "Set NFS client flag.", dmenuVarCheck, dmenuToggleVariable, NULL, "nfs_client_enable=YES" },
+ { " NFS, server", "Set NFS server flag.", dmenuVarCheck, configNFSServer, NULL, "nfs_server_enable=YES" },
+ { " NTP Menu", "The NTP configuration menu.", NULL, dmenuSubmenu, NULL, &MenuNTP },
+ { " Options", "The options editor.", NULL, optionsEditor },
+ { " Packages", "The packages collection", NULL, configPackages },
+#ifdef WITH_SLICES
+ { " Partition", "The disk Slice (PC-style partition) Editor", NULL, diskPartitionEditor },
+#endif
+ { " PCNFSD", "Run authentication server for PC-NFS.", dmenuVarCheck, configPCNFSD, NULL, "pcnfsd" },
+ { " Root Password", "Set the system manager's password.", NULL, dmenuSystemCommand, NULL, "passwd root" },
+ { " Router", "Select routing daemon (default: routed)", NULL, configRouter, NULL, "router_enable" },
+ { " Security", "Configure system security options", NULL, dmenuSubmenu, NULL, &MenuSecurity },
+#ifdef WITH_SYSCONS
+ { " Syscons", "The system console configuration menu.", NULL, dmenuSubmenu, NULL, &MenuSyscons },
+#ifndef PC98
+ { " Syscons, Font", "The console screen font.", NULL, dmenuSubmenu, NULL, &MenuSysconsFont },
+#endif
+ { " Syscons, Keymap", "The console keymap configuration menu.", NULL, keymapMenuSelect },
+ { " Syscons, Keyrate", "The console key rate configuration menu.", NULL, dmenuSubmenu, NULL, &MenuSysconsKeyrate },
+ { " Syscons, Saver", "The console screen saver configuration menu.", NULL, dmenuSubmenu, NULL, &MenuSysconsSaver },
+#ifndef PC98
+ { " Syscons, Screenmap", "The console screenmap configuration menu.", NULL, dmenuSubmenu, NULL, &MenuSysconsScrnmap },
+ { " Syscons, Ttys", "The console terminal type menu.", NULL, dmenuSubmenu, NULL, &MenuSysconsTtys },
+#endif
+#endif /* WITH_SYSCONS */
+ { " Time Zone", "Set the system's time zone.", NULL, dmenuSystemCommand, NULL, "tzsetup" },
+ { " TTYs", "Configure system ttys.", NULL, configEtcTtys, NULL, "ttys" },
+ { " Upgrade", "Upgrade an existing system.", NULL, installUpgrade },
+ { " Usage", "Quick start - How to use this menu system.", NULL, dmenuDisplayFile, NULL, "usage" },
+ { " User Management", "Add user and group information.", NULL, dmenuSubmenu, NULL, &MenuUsermgmt },
+ { NULL } },
+};
+
+/* The country menu */
+#include "countries.h"
+
+/* The initial installation menu */
+DMenu MenuInitial = {
+ DMENU_NORMAL_TYPE,
+ "sysinstall Main Menu", /* title */
+ "Welcome to the FreeBSD installation and configuration tool. Please\n" /* prompt */
+ "select one of the options below by using the arrow keys or typing the\n"
+ "first character of the option name you're interested in. Invoke an\n"
+ "option with [SPACE] or [ENTER]. To exit, use [TAB] to move to Exit.",
+ NULL,
+ NULL,
+ { { " Select " },
+ { "X Exit Install", NULL, NULL, dmenuExit },
+ { " Usage", "Quick start - How to use this menu system", NULL, dmenuDisplayFile, NULL, "usage" },
+ { "Standard", "Begin a standard installation (recommended)", NULL, installStandard },
+ { "Express", "Begin a quick installation (for experts)", NULL, installExpress },
+ { " Custom", "Begin a custom installation (for experts)", NULL, dmenuSubmenu, NULL, &MenuInstallCustom },
+ { "Configure", "Do post-install configuration of FreeBSD", NULL, dmenuSubmenu, NULL, &MenuConfigure },
+ { "Doc", "Installation instructions, README, etc.", NULL, dmenuSubmenu, NULL, &MenuDocumentation },
+#ifdef WITH_SYSCONS
+ { "Keymap", "Select keyboard type", NULL, keymapMenuSelect },
+#endif
+ { "Options", "View/Set various installation options", NULL, optionsEditor },
+ { "Fixit", "Repair mode with CDROM/DVD/floppy or start shell", NULL, dmenuSubmenu, NULL, &MenuFixit },
+ { "Upgrade", "Upgrade an existing system", NULL, installUpgrade },
+ { "Load Config..","Load default install configuration", NULL, dispatch_load_menu },
+ { "Index", "Glossary of functions", NULL, dmenuSubmenu, NULL, &MenuIndex },
+ { NULL } },
+};
+
+/* The main documentation menu */
+DMenu MenuDocumentation = {
+ DMENU_NORMAL_TYPE,
+ "FreeBSD Documentation Menu",
+ "If you are at all unsure about the configuration of your hardware\n"
+ "or are looking to build a system specifically for FreeBSD, read the\n"
+ "Hardware guide! New users should also read the Install document for\n"
+ "a step-by-step tutorial on installing FreeBSD. For general information,\n"
+ "consult the README file.",
+ "Confused? Press F1 for help.",
+ "usage",
+ { { "X Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { "1 README", "A general description of FreeBSD. Read this!", NULL, dmenuDisplayFile, NULL, "README" },
+ { "2 Errata", "Late-breaking, post-release news.", NULL, dmenuDisplayFile, NULL, "ERRATA" },
+ { "3 Hardware", "The FreeBSD survival guide for PC hardware.", NULL, dmenuDisplayFile, NULL, "HARDWARE" },
+ { "4 Copyright", "The FreeBSD Copyright notices.", NULL, dmenuDisplayFile, NULL, "COPYRIGHT" },
+ { "5 Release" ,"The release notes for this version of FreeBSD.", NULL, dmenuDisplayFile, NULL, "RELNOTES" },
+ { "6 Shortcuts", "Creating shortcuts to sysinstall.", NULL, dmenuDisplayFile, NULL, "shortcuts" },
+ { "7 HTML Docs", "Go to the HTML documentation menu (post-install).", NULL, docBrowser },
+ { NULL } },
+};
+
+#ifdef WITH_MICE
+DMenu MenuMouseType = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+#ifdef PC98
+ "Select a protocol type for your mouse",
+ "If your mouse is attached to the bus mouse port, you should always choose\n"
+ "\"Auto\", regardless of the model and the brand of the mouse. All other\n"
+ "protocol types are for serial mice and should not be used with the bus\n"
+ "mouse. If you have a serial mouse and are not sure about its protocol,\n"
+ "you should also try \"Auto\". It may not work for the serial mouse if the\n"
+ "mouse does not support the PnP standard. But, it won't hurt. Many\n"
+ "2-button serial mice are compatible with \"Microsoft\" or \"MouseMan\".\n"
+ "3-button serial mice may be compatible with \"MouseSystems\" or \"MouseMan\".\n"
+ "If the serial mouse has a wheel, it may be compatible with \"IntelliMouse\".",
+ NULL,
+ NULL,
+ { { "1 Auto", "Bus mouse or PnP serial mouse",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=auto" },
+#else
+ "Select a protocol type for your mouse",
+ "If your mouse is attached to the PS/2 mouse port or the bus mouse port,\n"
+ "you should always choose \"Auto\", regardless of the model and the brand\n"
+ "of the mouse. All other protocol types are for serial mice and should\n"
+ "not be used with the PS/2 port mouse or the bus mouse. If you have\n"
+ "a serial mouse and are not sure about its protocol, you should also try\n"
+ "\"Auto\". It may not work for the serial mouse if the mouse does not\n"
+ "support the PnP standard. But, it won't hurt. Many 2-button serial mice\n"
+ "are compatible with \"Microsoft\" or \"MouseMan\". 3-button serial mice\n"
+ "may be compatible with \"MouseSystems\" or \"MouseMan\". If the serial\n"
+ "mouse has a wheel, it may be compatible with \"IntelliMouse\".",
+ NULL,
+ NULL,
+ { { "1 Auto", "Bus mouse, PS/2 style mouse or PnP serial mouse",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=auto" },
+#endif /* PC98 */
+ { "2 GlidePoint", "ALPS GlidePoint pad (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=glidepoint" },
+ { "3 Hitachi","Hitachi tablet (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=mmhittab" },
+ { "4 IntelliMouse", "Microsoft IntelliMouse (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=intellimouse" },
+ { "5 Logitech", "Logitech protocol (old models) (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=logitech" },
+ { "6 Microsoft", "Microsoft protocol (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=microsoft" },
+ { "7 MM Series","MM Series protocol (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=mmseries" },
+ { "8 MouseMan", "Logitech MouseMan/TrackMan models (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=mouseman" },
+ { "9 MouseSystems", "MouseSystems protocol (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=mousesystems" },
+ { "A ThinkingMouse","Kensington ThinkingMouse (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=thinkingmouse" },
+ { NULL } },
+};
+
+#ifdef PC98
+DMenu MenuMousePort = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Select your mouse port from the following menu",
+ "The built-in pointing device of laptop/notebook computers is usually\n"
+ "a BusMouse style device.",
+ NULL,
+ NULL,
+ {
+ { "1 BusMouse", "PC-98x1 bus mouse (/dev/mse0)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/mse0" },
+ { "2 COM1", "Serial mouse on COM1 (/dev/cuad0)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuad0" },
+ { "3 COM2", "Serial mouse on COM2 (/dev/cuad1)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuad1" },
+ { NULL } },
+};
+#else
+DMenu MenuMousePort = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Select your mouse port from the following menu",
+ "The built-in pointing device of laptop/notebook computers is usually\n"
+ "a PS/2 style device.",
+ NULL,
+ NULL,
+ { { "1 PS/2", "PS/2 style mouse (/dev/psm0)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/psm0" },
+ { "2 COM1", "Serial mouse on COM1 (/dev/cuad0)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuad0" },
+ { "3 COM2", "Serial mouse on COM2 (/dev/cuad1)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuad1" },
+ { "4 COM3", "Serial mouse on COM3 (/dev/cuad2)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuad2" },
+ { "5 COM4", "Serial mouse on COM4 (/dev/cuad3)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuad3" },
+ { "6 BusMouse", "Logitech, ATI or MS bus mouse (/dev/mse0)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/mse0" },
+ { NULL } },
+};
+#endif /* PC98 */
+
+DMenu MenuMouse = {
+ DMENU_NORMAL_TYPE,
+ "Please configure your mouse",
+ "You can cut and paste text in the text console by running the mouse\n"
+ "daemon. Specify a port and a protocol type of your mouse and enable\n"
+ "the mouse daemon. If you don't want this feature, select 6 to disable\n"
+ "the daemon.\n"
+ "Once you've enabled the mouse daemon, you can specify \"/dev/sysmouse\"\n"
+ "as your mouse device and \"SysMouse\" or \"MouseSystems\" as mouse\n"
+ "protocol when running the X configuration utility (see Configuration\n"
+ "menu).",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { "2 Enable", "Test and run the mouse daemon", NULL, mousedTest, NULL, NULL },
+ { "3 Type", "Select mouse protocol type", NULL, dmenuSubmenu, NULL, &MenuMouseType },
+ { "4 Port", "Select mouse port", NULL, dmenuSubmenu, NULL, &MenuMousePort },
+ { "5 Flags", "Set additional flags", dmenuVarCheck, setMouseFlags,
+ NULL, VAR_MOUSED_FLAGS "=" },
+ { "6 Disable", "Disable the mouse daemon", NULL, mousedDisable, NULL, NULL },
+ { NULL } },
+};
+#endif /* WITH_MICE */
+
+DMenu MenuMediaCDROM = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Choose a CD/DVD type",
+ "FreeBSD can be installed directly from a CD/DVD containing a valid\n"
+ "FreeBSD distribution. If you are seeing this menu it is because\n"
+ "more than one CD/DVD drive was found on your system. Please select one\n"
+ "of the following CD/DVD drives as your installation drive.",
+ NULL,
+ NULL,
+ { { NULL } },
+};
+
+DMenu MenuMediaFloppy = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Choose a Floppy drive",
+ "You have more than one floppy drive. Please choose which drive\n"
+ "you would like to use.",
+ NULL,
+ NULL,
+ { { NULL } },
+};
+
+DMenu MenuMediaDOS = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Choose a DOS partition",
+ "FreeBSD can be installed directly from a DOS partition\n"
+ "assuming, of course, that you have copied the relevant\n"
+ "distributions into your DOS partition before starting this\n"
+ "installation. If this is not the case then you should reboot\n"
+ "DOS at this time and copy the distributions you wish to install\n"
+ "into a \"FREEBSD\" subdirectory on one of your DOS partitions.\n"
+ "Otherwise, please select the DOS partition containing the FreeBSD\n"
+ "distribution files.",
+ NULL,
+ NULL,
+ { { NULL } },
+};
+
+DMenu MenuMediaFTP = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Please select a FreeBSD FTP distribution site",
+ "Please select the site closest to you or \"other\" if you'd like to\n"
+ "specify a different choice. Also note that not every site listed here\n"
+ "carries more than the base distribution kits. Only Primary sites are\n"
+ "guaranteed to carry the full range of possible distributions.",
+ "Select a site that's close!",
+ NULL,
+ { { "Main Site", "ftp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.freebsd.org" },
+ { "URL", "Specify some other ftp site by URL", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=other" },
+ { "Snapshots Server Japan", "snapshots.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://snapshots.jp.freebsd.org" },
+ { "Snapshots Server Sweden", "snapshots.se.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://snapshots.se.freebsd.org" },
+
+ { "IPv6 Main Site", "ftp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.freebsd.org" },
+ { " IPv6 Ireland", "ftp3.ie.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.ie.freebsd.org" },
+ { " IPv6 Israel", "ftp.il.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.il.freebsd.org" },
+ { " IPv6 Japan", "ftp2.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.jp.freebsd.org" },
+ { " IPv6 USA", "ftp4.us.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.us.freebsd.org" },
+ { " IPv6 Turkey", "ftp2.tr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.tr.freebsd.org" },
+
+ { "Primary", "ftp1.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp1.freebsd.org" },
+ { " Primary #2", "ftp2.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.freebsd.org" },
+ { " Primary #3", "ftp3.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.freebsd.org" },
+ { " Primary #4", "ftp4.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.freebsd.org" },
+ { " Primary #5", "ftp5.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.freebsd.org" },
+ { " Primary #6", "ftp6.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp6.freebsd.org" },
+ { " Primary #7", "ftp7.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp7.freebsd.org" },
+ { " Primary #8", "ftp8.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp8.freebsd.org" },
+ { " Primary #9", "ftp9.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp9.freebsd.org" },
+ { " Primary #10", "ftp10.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp10.freebsd.org" },
+ { " Primary #11", "ftp11.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp11.freebsd.org" },
+ { " Primary #12", "ftp12.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp12.freebsd.org" },
+ { " Primary #13", "ftp13.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp13.freebsd.org" },
+ { " Primary #14", "ftp14.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp14.freebsd.org" },
+
+ { "Argentina", "ftp.ar.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ar.freebsd.org" },
+
+ { "Australia", "ftp.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.au.freebsd.org" },
+ { " Australia #2","ftp2.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.au.freebsd.org" },
+ { " Australia #3","ftp3.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.au.freebsd.org" },
+
+ { "Austria","ftp.at.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.at.freebsd.org" },
+ { " Austria #2","ftp2.at.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.at.freebsd.org" },
+
+ { "Brazil", "ftp.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.br.freebsd.org" },
+ { " Brazil #2", "ftp2.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.br.freebsd.org" },
+ { " Brazil #3", "ftp3.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.br.freebsd.org" },
+ { " Brazil #4", "ftp4.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.br.freebsd.org" },
+ { " Brazil #5", "ftp5.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.br.freebsd.org" },
+ { " Brazil #6", "ftp6.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp6.br.freebsd.org" },
+ { " Brazil #7", "ftp7.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp7.br.freebsd.org" },
+
+ { "Canada", "ftp.ca.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ca.freebsd.org" },
+
+ { "China", "ftp.cn.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.cn.freebsd.org" },
+ { " China #2", "ftp2.cn.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.cn.freebsd.org" },
+
+ { "Croatia", "ftp.hr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.hr.freebsd.org" },
+
+ { "Czech Republic", "ftp.cz.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.cz.freebsd.org" },
+
+ { "Denmark", "ftp.dk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.dk.freebsd.org" },
+ { " Denmark #2", "ftp2.dk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.dk.freebsd.org" },
+
+ { "Estonia", "ftp.ee.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ee.freebsd.org" },
+
+ { "Finland", "ftp.fi.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.fi.freebsd.org" },
+
+ { "France", "ftp.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.fr.freebsd.org" },
+ { " France #2", "ftp2.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.fr.freebsd.org" },
+ { " France #3", "ftp3.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.fr.freebsd.org" },
+ { " France #5", "ftp5.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.fr.freebsd.org" },
+ { " France #6", "ftp6.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp6.fr.freebsd.org" },
+ { " France #8", "ftp8.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp8.fr.freebsd.org" },
+
+ { "Germany", "ftp.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.de.freebsd.org" },
+ { " Germany #2", "ftp2.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.de.freebsd.org" },
+ { " Germany #3", "ftp3.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.de.freebsd.org" },
+ { " Germany #4", "ftp4.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.de.freebsd.org" },
+ { " Germany #5", "ftp5.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.de.freebsd.org" },
+ { " Germany #6", "ftp6.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp6.de.freebsd.org" },
+ { " Germany #7", "ftp7.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp7.de.freebsd.org" },
+ { " Germany #8", "ftp8.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp8.de.freebsd.org" },
+
+ { "Greece", "ftp.gr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.gr.freebsd.org" },
+ { " Greece #2", "ftp2.gr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.gr.freebsd.org" },
+
+ { "Hungary", "ftp.hu.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.hu.freebsd.org" },
+
+ { "Iceland", "ftp.is.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.is.freebsd.org" },
+
+ { "Ireland", "ftp.ie.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ie.freebsd.org" },
+ { " Ireland #2", "ftp2.ie.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.ie.freebsd.org" },
+ { " Ireland #3", "ftp3.ie.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.ie.freebsd.org" },
+
+ { "Isreal", "ftp.il.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.il.freebsd.org" },
+
+ { "Italy", "ftp.it.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.it.freebsd.org" },
+
+ { "Japan", "ftp.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.jp.freebsd.org" },
+ { " Japan #2", "ftp2.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.jp.freebsd.org" },
+ { " Japan #3", "ftp3.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.jp.freebsd.org" },
+ { " Japan #4", "ftp4.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.jp.freebsd.org" },
+ { " Japan #5", "ftp5.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.jp.freebsd.org" },
+ { " Japan #6", "ftp6.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp6.jp.freebsd.org" },
+ { " Japan #7", "ftp7.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp7.jp.freebsd.org" },
+ { " Japan #8", "ftp8.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp8.jp.freebsd.org" },
+ { " Japan #9", "ftp9.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp9.jp.freebsd.org" },
+
+ { "Korea", "ftp.kr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.kr.freebsd.org" },
+ { " Korea #2", "ftp2.kr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.kr.freebsd.org" },
+
+ { "Lithuania", "ftp.lt.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.lt.freebsd.org" },
+
+ { "Netherlands", "ftp.nl.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.nl.freebsd.org" },
+ { " Netherlands #2", "ftp2.nl.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.nl.freebsd.org" },
+
+ { "Norway", "ftp.no.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.no.freebsd.org" },
+ { " Norway #3", "ftp3.no.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.no.freebsd.org" },
+
+ { "Poland", "ftp.pl.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.pl.freebsd.org" },
+ { " Poland #2", "ftp2.pl.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.pl.freebsd.org" },
+ { " Poland #5", "ftp5.pl.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.pl.freebsd.org" },
+
+ { "Portugal", "ftp.pt.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.pt.freebsd.org" },
+ { " Portugal #2", "ftp2.pt.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.pt.freebsd.org" },
+ { " Portugal #4", "ftp4.pt.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.pt.freebsd.org" },
+
+ { "Romania", "ftp.ro.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ro.freebsd.org" },
+
+ { "Russia", "ftp.ru.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ru.freebsd.org" },
+ { " Russia #2", "ftp2.ru.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.ru.freebsd.org" },
+ { " Russia #3", "ftp3.ru.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.ru.freebsd.org" },
+ { " Russia #4", "ftp4.ru.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.ru.freebsd.org" },
+
+ { "Singapore", "ftp.sg.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.sg.freebsd.org" },
+
+ { "Slovak Republic", "ftp.sk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.sk.freebsd.org" },
+
+ { "Slovenia", "ftp.si.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.si.freebsd.org" },
+ { " Slovenia #2", "ftp2.si.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.si.freebsd.org" },
+
+ { "South Africa", "ftp.za.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.za.freebsd.org" },
+ { " South Africa #2", "ftp2.za.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.za.freebsd.org" },
+ { " South Africa #3", "ftp3.za.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.za.freebsd.org" },
+ { " South Africa #4", "ftp4.za.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.za.freebsd.org" },
+
+ { "Spain", "ftp.es.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.es.freebsd.org" },
+ { " Spain #2", "ftp2.es.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.es.freebsd.org" },
+ { " Spain #3", "ftp3.es.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.es.freebsd.org" },
+
+ { "Sweden", "ftp.se.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.se.freebsd.org" },
+ { " Sweden #2", "ftp2.se.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.se.freebsd.org" },
+ { " Sweden #3", "ftp3.se.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.se.freebsd.org" },
+ { " Sweden #5", "ftp5.se.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.se.freebsd.org" },
+
+ { "Switzerland", "ftp.ch.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ch.freebsd.org" },
+ { " Switzerland #2", "ftp2.ch.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.ch.freebsd.org" },
+
+ { "Taiwan", "ftp.tw.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.tw.freebsd.org" },
+ { " Taiwan #2", "ftp2.tw.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.tw.freebsd.org" },
+ { " Taiwan #3", "ftp3.tw.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.tw.freebsd.org" },
+ { " Taiwan #4", "ftp4.tw.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.tw.freebsd.org" },
+ { " Taiwan #6", "ftp6.tw.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp6.tw.freebsd.org" },
+ { " Taiwan #11", "ftp11.tw.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp11.tw.freebsd.org" },
+
+ { "Turkey", "ftp.tr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.tr.freebsd.org" },
+ { " Turkey #2", "ftp2.tr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.tr.freebsd.org" },
+
+ { "UK", "ftp.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.uk.freebsd.org" },
+ { " UK #2", "ftp2.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.uk.freebsd.org" },
+ { " UK #3", "ftp3.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.uk.freebsd.org" },
+ { " UK #4", "ftp4.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.uk.freebsd.org" },
+ { " UK #5", "ftp5.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.uk.freebsd.org" },
+ { " UK #6", "ftp6.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp6.uk.freebsd.org" },
+
+ { "Ukraine", "ftp.ua.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ua.freebsd.org" },
+ { " Ukraine #2", "ftp2.ua.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.ua.freebsd.org" },
+ { " Ukraine #5", "ftp5.ua.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.ua.freebsd.org" },
+ { " Ukraine #6", "ftp6.ua.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp6.ua.freebsd.org" },
+ { " Ukraine #7", "ftp7.ua.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp7.ua.freebsd.org" },
+ { " Ukraine #8", "ftp8.ua.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp8.ua.freebsd.org" },
+
+ { "USA #1", "ftp1.us.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp1.us.freebsd.org" },
+ { " USA #2", "ftp2.us.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.us.freebsd.org" },
+ { " USA #3", "ftp3.us.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.us.freebsd.org" },
+ { " USA #4", "ftp4.us.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.us.freebsd.org" },
+ { " USA #5", "ftp5.us.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.us.freebsd.org" },
+ { " USA #6", "ftp6.us.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp6.us.freebsd.org" },
+ { " USA #7", "ftp7.us.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp7.us.freebsd.org" },
+ { " USA #8", "ftp8.us.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp8.us.freebsd.org" },
+ { " USA #9", "ftp9.us.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp9.us.freebsd.org" },
+ { " USA #10", "ftp10.us.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp10.us.freebsd.org" },
+ { " USA #11", "ftp11.us.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp11.us.freebsd.org" },
+ { " USA #12", "ftp12.us.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp12.us.freebsd.org" },
+ { " USA #13", "ftp13.us.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp13.us.freebsd.org" },
+ { " USA #14", "ftp14.us.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp14.us.freebsd.org" },
+ { " USA #15", "ftp15.us.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp15.us.freebsd.org" },
+
+ { NULL } }
+};
+
+DMenu MenuNetworkDevice = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Network interface information required",
+ "If you are using PPP over a serial device, as opposed to a direct\n"
+ "ethernet connection, then you may first need to dial your Internet\n"
+ "Service Provider using the ppp utility we provide for that purpose.\n"
+ "If you're using SLIP over a serial device then the expectation is\n"
+ "that you have a HARDWIRED connection.\n\n"
+ "You can also install over a parallel port using a special \"laplink\"\n"
+ "cable to another machine running FreeBSD.",
+ "Press F1 to read network configuration manual",
+ "network_device",
+ { { NULL } },
+};
+
+/* Prototype KLD load menu */
+DMenu MenuKLD = {
+ DMENU_NORMAL_TYPE,
+ "KLD Menu",
+ "Load a KLD from a floppy\n",
+ NULL,
+ NULL,
+ { { NULL } },
+};
+
+/* Prototype config file load menu */
+DMenu MenuConfig = {
+ DMENU_NORMAL_TYPE,
+ "Config Menu",
+ "Please select the device to load your configuration file from.\n"
+ "Note that a USB key will show up as daNs1.",
+ NULL,
+ NULL,
+ { { NULL } },
+};
+
+/* The media selection menu */
+DMenu MenuMedia = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Choose Installation Media",
+ "FreeBSD can be installed from a variety of different installation\n"
+ "media, ranging from floppies to an Internet FTP server. If you're\n"
+ "installing FreeBSD from a supported CD/DVD drive then this is generally\n"
+ "the best media to use if you have no overriding reason for using other\n"
+ "media.",
+ "Press F1 for more information on the various media types",
+ "media",
+ { { "1 CD/DVD", "Install from a FreeBSD CD/DVD", NULL, mediaSetCDROM },
+ { "2 FTP", "Install from an FTP server", NULL, mediaSetFTPActive },
+ { "3 FTP Passive", "Install from an FTP server through a firewall", NULL, mediaSetFTPPassive },
+ { "4 HTTP", "Install from an FTP server through a http proxy", NULL, mediaSetHTTP },
+ { "5 DOS", "Install from a DOS partition", NULL, mediaSetDOS },
+ { "6 NFS", "Install over NFS", NULL, mediaSetNFS },
+ { "7 File System", "Install from an existing filesystem", NULL, mediaSetUFS },
+ { "8 Floppy", "Install from a floppy disk set", NULL, mediaSetFloppy },
+ { "X Options", "Go to the Options screen", NULL, optionsEditor },
+ { NULL } },
+};
+
+/* The distributions menu */
+DMenu MenuDistributions = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Choose Distributions",
+ "As a convenience, we provide several \"canned\" distribution sets.\n"
+ "These select what we consider to be the most reasonable defaults for the\n"
+ "type of system in question. If you would prefer to pick and choose the\n"
+ "list of distributions yourself, simply select \"Custom\". You can also\n"
+ "pick a canned distribution set and then fine-tune it with the Custom item.\n\n"
+ "Choose an item by pressing [SPACE] or [ENTER]. When finished, choose the\n"
+ "Exit item or move to the OK button with [TAB].",
+ "Press F1 for more information on these options.",
+ "distributions",
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { "All", "All system sources and binaries",
+ checkDistEverything, distSetEverything, NULL, NULL, ' ', ' ', ' ' },
+ { "Reset", "Reset selected distribution list to nothing",
+ NULL, distReset, NULL, NULL, ' ', ' ', ' ' },
+ { "4 Developer", "Full sources, binaries and doc but no games",
+ checkDistDeveloper, distSetDeveloper },
+ { "5 Kern-Developer", "Full binaries and doc, kernel sources only",
+ checkDistKernDeveloper, distSetKernDeveloper },
+ { "6 User", "Average user - binaries and doc only",
+ checkDistUser, distSetUser },
+ { "A Minimal", "The smallest configuration possible",
+ checkDistMinimum, distSetMinimum },
+ { "B Custom", "Specify your own distribution set",
+ NULL, dmenuSubmenu, NULL, &MenuSubDistributions, '>', '>', '>' },
+ { NULL } },
+};
+
+DMenu MenuSubDistributions = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Select the distributions you wish to install.",
+ "Please check off the distributions you wish to install. At the\n"
+ "very minimum, this should be \"base\".",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { "All", "All system sources and binaries",
+ NULL, distSetEverything, NULL, NULL, ' ', ' ', ' ' },
+ { "Reset", "Reset all of the below",
+ NULL, distReset, NULL, NULL, ' ', ' ', ' ' },
+ { " base", "Binary base distribution (required)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_BASE },
+ { " kernels", "Binary kernel distributions (required)",
+ kernelFlagCheck,distSetKernel },
+ { " dict", "Spelling checker dictionary files",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_DICT },
+ { " doc", "Miscellaneous FreeBSD online docs",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_DOC },
+ { " games", "Games (non-commercial)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_GAMES },
+ { " info", "GNU info files",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_INFO },
+#ifdef __amd64__
+ { " lib32", "32-bit runtime compatibility libraries",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_LIB32 },
+#endif
+ { " man", "System manual pages - recommended",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_MANPAGES },
+ { " catman", "Preformatted system manual pages",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_CATPAGES },
+ { " proflibs", "Profiled versions of the libraries",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_PROFLIBS },
+ { " src", "Sources for everything",
+ srcFlagCheck, distSetSrc },
+ { " ports", "The FreeBSD Ports collection",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_PORTS },
+ { " local", "Local additions collection",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_LOCAL},
+ { NULL } },
+};
+
+DMenu MenuKernelDistributions = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Select the operating system kernels you wish to install.",
+ "Please check off those kernels you wish to install.\n",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { "All", "Select all of the below",
+ NULL, setKernel, NULL, NULL, ' ', ' ', ' ' },
+ { "Reset", "Reset all of the below",
+ NULL, clearKernel, NULL, NULL, ' ', ' ', ' ' },
+ { " GENERIC", "GENERIC kernel configuration",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &KernelDists, '[', 'X', ']', DIST_KERNEL_GENERIC },
+#ifdef WITH_SMP
+ { " SMP", "GENERIC symmetric multiprocessor kernel configuration",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &KernelDists, '[', 'X', ']', DIST_KERNEL_SMP },
+#endif
+ { NULL } },
+};
+
+DMenu MenuSrcDistributions = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Select the sub-components of src you wish to install.",
+ "Please check off those portions of the FreeBSD source tree\n"
+ "you wish to install.",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { "All", "Select all of the below",
+ NULL, setSrc, NULL, NULL, ' ', ' ', ' ' },
+ { "Reset", "Reset all of the below",
+ NULL, clearSrc, NULL, NULL, ' ', ' ', ' ' },
+ { " base", "top-level files in /usr/src",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_BASE },
+ { " cddl", "/usr/src/cddl (software from Sun)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_CDDL },
+ { " contrib", "/usr/src/contrib (contributed software)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_CONTRIB },
+ { " crypto", "/usr/src/crypto (contrib encryption sources)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_SCRYPTO },
+ { " gnu", "/usr/src/gnu (software from the GNU Project)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_GNU },
+ { " etc", "/usr/src/etc (miscellaneous system files)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_ETC },
+ { " games", "/usr/src/games (the obvious!)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_GAMES },
+ { " include", "/usr/src/include (header files)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_INCLUDE },
+ { " krb5", "/usr/src/kerberos5 (sources for Kerberos5)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_SKERBEROS5 },
+ { " lib", "/usr/src/lib (system libraries)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_LIB },
+ { " libexec", "/usr/src/libexec (system programs)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_LIBEXEC },
+ { " release", "/usr/src/release (release-generation tools)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_RELEASE },
+ { " rescue", "/usr/src/rescue (static rescue tools)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_RESCUE },
+ { " bin", "/usr/src/bin (system binaries)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_BIN },
+ { " sbin", "/usr/src/sbin (system binaries)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_SBIN },
+ { " secure", "/usr/src/secure (BSD encryption sources)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_SSECURE },
+ { " share", "/usr/src/share (documents and shared files)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_SHARE },
+ { " sys", "/usr/src/sys (FreeBSD kernel)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_SYS },
+ { " tools", "/usr/src/tools (miscellaneous tools)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_TOOLS },
+ { " ubin", "/usr/src/usr.bin (user binaries)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_UBIN },
+ { " usbin", "/usr/src/usr.sbin (aux system binaries)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_USBIN },
+ { NULL } },
+};
+
+DMenu MenuDiskDevices = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Select Drive(s)",
+ "Please select the drive, or drives, on which you wish to perform\n"
+ "this operation. If you are attempting to install a boot partition\n"
+ "on a drive other than the first one or have multiple operating\n"
+ "systems on your machine, you will have the option to install a boot\n"
+ "manager later. To select a drive, use the arrow keys to move to it\n"
+ "and press [SPACE] or [ENTER]. To de-select it, press it again.\n\n"
+ "Use [TAB] to get to the buttons and leave this menu.",
+ "Press F1 for important information regarding disk geometry!",
+ "drives",
+ { { NULL } },
+};
+
+DMenu MenuHTMLDoc = {
+ DMENU_NORMAL_TYPE,
+ "Select HTML Documentation pointer",
+ "Please select the body of documentation you're interested in, the main\n"
+ "ones right now being the FAQ and the Handbook. You can also choose \"other\"\n"
+ "to enter an arbitrary URL for browsing.",
+ "Press F1 for more help on what you see here.",
+ "html",
+ { { "X Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { "2 Handbook", "The FreeBSD Handbook.", NULL, docShowDocument },
+ { "3 FAQ", "The Frequently Asked Questions guide.", NULL, docShowDocument },
+ { "4 Home", "The Home Pages for the FreeBSD Project (requires net)", NULL, docShowDocument },
+ { "5 Other", "Enter a URL.", NULL, docShowDocument },
+ { NULL } },
+};
+
+/* The main installation menu */
+DMenu MenuInstallCustom = {
+ DMENU_NORMAL_TYPE,
+ "Choose Custom Installation Options",
+ "This is the custom installation menu. You may use this menu to specify\n"
+ "details on the type of distribution you wish to have, where you wish\n"
+ "to install it from and how you wish to allocate disk storage to FreeBSD.",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { "2 Options", "View/Set various installation options", NULL, optionsEditor },
+#ifndef WITH_SLICES
+ { "3 Label", "Label disk partitions", NULL, diskLabelEditor },
+ { "4 Distributions", "Select distribution(s) to extract", NULL, dmenuSubmenu, NULL, &MenuDistributions },
+ { "5 Media", "Choose the installation media type", NULL, dmenuSubmenu, NULL, &MenuMedia },
+ { "6 Commit", "Perform any pending Partition/Label/Extract actions", NULL, installCustomCommit },
+#else
+ { "3 Partition", "Allocate disk space for FreeBSD", NULL, diskPartitionEditor },
+ { "4 Label", "Label allocated disk partitions", NULL, diskLabelEditor },
+ { "5 Distributions", "Select distribution(s) to extract", NULL, dmenuSubmenu, NULL, &MenuDistributions },
+ { "6 Media", "Choose the installation media type", NULL, dmenuSubmenu, NULL, &MenuMedia },
+ { "7 Commit", "Perform any pending Partition/Label/Extract actions", NULL, installCustomCommit },
+#endif
+ { NULL } },
+};
+
+#if defined(__i386__) || defined(__amd64__)
+#ifdef PC98
+/* IPL type menu */
+DMenu MenuIPLType = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "overwrite me", /* will be disk specific label */
+ "If you want a FreeBSD Boot Manager, select \"BootMgr\". If you would\n"
+ "prefer your Boot Manager to remain untouched then select \"None\".\n\n",
+ "Press F1 to read about drive setup",
+ "drives",
+ { { "BootMgr", "Install the FreeBSD Boot Manager",
+ dmenuRadioCheck, dmenuSetValue, NULL, &BootMgr },
+ { "None", "Leave the IPL untouched",
+ dmenuRadioCheck, dmenuSetValue, NULL, &BootMgr, '(', '*', ')', 1 },
+ { NULL } },
+};
+#else
+/* MBR type menu */
+DMenu MenuMBRType = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "overwrite me", /* will be disk specific label */
+ "FreeBSD comes with a boot manager that allows you to easily\n"
+ "select between FreeBSD and any other operating systems on your machine\n"
+ "at boot time. If you have more than one drive and want to boot\n"
+ "from the second one, the boot manager will also make it possible\n"
+ "to do so (limitations in the PC BIOS usually prevent this otherwise).\n"
+ "If you will only have FreeBSD on the machine the boot manager is\n"
+ "not needed and it slows down the boot while offering you the choice\n"
+ "of which operating system to boot. If you do not want a boot\n"
+ "manager, or wish to replace an existing one, select \"standard\".\n"
+ "If you would prefer your Master Boot Record remain untouched then\n"
+ "select \"None\".\n\n"
+ " NOTE: PC-DOS users will almost certainly require \"None\"!",
+ "Press F1 to read about drive setup",
+ "drives",
+ { { "Standard", "Install a standard MBR (no boot manager)",
+ dmenuRadioCheck, dmenuSetValue, NULL, &BootMgr, '(', '*', ')', 1 },
+ { "BootMgr", "Install the FreeBSD Boot Manager",
+ dmenuRadioCheck, dmenuSetValue, NULL, &BootMgr, '(', '*', ')', 0 },
+ { "None", "Leave the Master Boot Record untouched",
+ dmenuRadioCheck, dmenuSetValue, NULL, &BootMgr, '(', '*', ')', 2 },
+ { NULL } },
+};
+#endif /* PC98 */
+#endif /* __i386__ */
+
+/* Final configuration menu */
+DMenu MenuConfigure = {
+ DMENU_NORMAL_TYPE,
+ "FreeBSD Configuration Menu", /* title */
+ "If you've already installed FreeBSD, you may use this menu to customize\n"
+ "it somewhat to suit your particular configuration. Most importantly,\n"
+ "you can use the Packages utility to load extra \"3rd party\"\n"
+ "software not provided in the base distributions.",
+ "Press F1 for more information on these options",
+ "configure",
+ { { "X Exit", "Exit this menu (returning to previous)",
+ NULL, dmenuExit },
+ { " Distributions", "Install additional distribution sets",
+ NULL, distExtractAll },
+ { " Packages", "Install pre-packaged software for FreeBSD",
+ NULL, configPackages },
+ { " Root Password", "Set the system manager's password",
+ NULL, dmenuSystemCommand, NULL, "passwd root" },
+#ifdef WITH_SLICES
+ { " Fdisk", "The disk Slice (PC-style partition) Editor",
+ NULL, diskPartitionEditor },
+#endif
+ { " Label", "The disk Label editor",
+ NULL, diskLabelEditor },
+ { " User Management", "Add user and group information",
+ NULL, dmenuSubmenu, NULL, &MenuUsermgmt },
+#ifdef WITH_SYSCONS
+ { " Console", "Customize system console behavior",
+ NULL, dmenuSubmenu, NULL, &MenuSyscons },
+#endif
+ { " Time Zone", "Set which time zone you're in",
+ NULL, dmenuSystemCommand, NULL, "tzsetup" },
+ { " Media", "Change the installation media type",
+ NULL, dmenuSubmenu, NULL, &MenuMedia },
+#ifdef WITH_MICE
+ { " Mouse", "Configure your mouse",
+ NULL, dmenuSubmenu, NULL, &MenuMouse },
+#endif
+ { " Networking", "Configure additional network services",
+ NULL, dmenuSubmenu, NULL, &MenuNetworking },
+ { " Security", "Configure system security options",
+ NULL, dmenuSubmenu, NULL, &MenuSecurity },
+ { " Startup", "Configure system startup options",
+ NULL, dmenuSubmenu, NULL, &MenuStartup },
+ { " TTYs", "Configure system ttys.",
+ NULL, configEtcTtys, NULL, "ttys" },
+ { " Options", "View/Set various installation options",
+ NULL, optionsEditor },
+ { " HTML Docs", "Go to the HTML documentation menu (post-install)",
+ NULL, docBrowser },
+ { " Load KLD", "Load a KLD from a floppy",
+ NULL, kldBrowser },
+ { NULL } },
+};
+
+DMenu MenuStartup = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Startup Services Menu",
+ "This menu allows you to configure various aspects of your system's\n"
+ "startup configuration. Use [SPACE] or [ENTER] to select items, and\n"
+ "[TAB] to move to the buttons. Select Exit to leave this menu.",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+#ifdef __i386__
+ { " APM", "Auto-power management services (typically laptops)",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "apm_enable=YES" },
+#endif
+ { " ", " -- ", NULL, NULL, NULL, NULL, ' ', ' ', ' ' },
+ { " Startup dirs", "Set the list of dirs to look for startup scripts",
+ dmenuVarCheck, dmenuISetVariable, NULL, "local_startup" },
+ { " named", "Run a local name server on this host",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "named_enable=YES" },
+ { " named flags", "Set default flags to named (if enabled)",
+ dmenuVarCheck, dmenuISetVariable, NULL, "named_flags" },
+ { " NIS client", "This host wishes to be an NIS client.",
+ dmenuVarCheck, configRpcBind, NULL, "nis_client_enable=YES" },
+ { " NIS domainname", "Set NIS domainname (if enabled)",
+ dmenuVarCheck, dmenuISetVariable, NULL, "nisdomainname" },
+ { " NIS server", "This host wishes to be an NIS server.",
+ dmenuVarCheck, configRpcBind, NULL, "nis_server_enable=YES" },
+ { " ", " -- ", NULL, NULL, NULL, NULL, ' ', ' ', ' ' },
+ { " Accounting", "This host wishes to run process accounting.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "accounting_enable=YES" },
+ { " lpd", "This host has a printer and wants to run lpd.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "lpd_enable=YES" },
+#ifdef __i386__
+ { " SCO", "This host wants to be able to run IBCS2 binaries.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "ibcs2_enable=YES" },
+ { " SVR4", "This host wants to be able to run SVR4 binaries.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "svr4_enable=YES" },
+#endif
+ { " quotas", "This host wishes to check quotas on startup.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "check_quotas=YES" },
+ { NULL } },
+};
+
+DMenu MenuNetworking = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Network Services Menu",
+ "You may have already configured one network device (and the other\n"
+ "various hostname/gateway/name server parameters) in the process\n"
+ "of installing FreeBSD. This menu allows you to configure other\n"
+ "aspects of your system's network configuration.",
+ NULL,
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { " Interfaces", "Configure additional network interfaces",
+ NULL, tcpMenuSelect },
+ { " AMD", "This machine wants to run the auto-mounter service",
+ dmenuVarCheck, configRpcBind, NULL, "amd_enable=YES" },
+ { " AMD Flags", "Set flags to AMD service (if enabled)",
+ dmenuVarCheck, dmenuISetVariable, NULL, "amd_flags" },
+ { " Anon FTP", "This machine wishes to allow anonymous FTP.",
+ dmenuVarCheck, configAnonFTP, NULL, "anon_ftp" },
+ { " Gateway", "This machine will route packets between interfaces",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "gateway_enable=YES" },
+ { " inetd", "This machine wants to run the inet daemon",
+ dmenuVarCheck, configInetd, NULL, "inetd_enable=YES" },
+ { " Mail", "This machine wants to run a Mail Transfer Agent",
+ NULL, dmenuSubmenu, NULL, &MenuMTA },
+ { " NFS client", "This machine will be an NFS client",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "nfs_client_enable=YES" },
+ { " NFS server", "This machine will be an NFS server",
+ dmenuVarCheck, configNFSServer, NULL, "nfs_server_enable=YES" },
+ { " Ntpdate", "Select a clock-synchronization server",
+ dmenuVarCheck, dmenuSubmenu, NULL, &MenuNTP, '[', 'X', ']',
+ (uintptr_t)"ntpdate_enable=YES" },
+ { " PCNFSD", "Run authentication server for clients with PC-NFS.",
+ dmenuVarCheck, configPCNFSD, NULL, "pcnfsd" },
+ { " rpcbind", "RPC port mapping daemon (formerly portmapper)",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "rpcbind_enable=YES" },
+ { " rpc.statd", "NFS status monitoring daemon",
+ dmenuVarCheck, configRpcBind, NULL, "rpc_statd_enable=YES" },
+ { " rpc.lockd", "NFS file locking daemon",
+ dmenuVarCheck, configRpcBind, NULL, "rpc_lockd_enable=YES" },
+ { " Routed", "Select routing daemon (default: routed)",
+ dmenuVarCheck, configRouter, NULL, "router_enable=YES" },
+ { " Rwhod", "This machine wants to run the rwho daemon",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "rwhod_enable=YES" },
+ { " sshd", "This machine wants to run the SSH daemon",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "sshd_enable=YES" },
+ { " TCP Extensions", "Allow RFC1323 and RFC1644 TCP extensions?",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "tcp_extensions=YES" },
+ { NULL } },
+};
+
+DMenu MenuMTA = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Mail Transfer Agent Selection",
+ "You can choose which Mail Transfer Agent (MTA) you wish to install and run.\n"
+ "Selecting Sendmail local disables sendmail's network socket for\n"
+ "incoming mail, but still enables sendmail for local and outbound mail.\n"
+ "The Postfix option will install the Postfix MTA from the ports\n"
+ "collection. The Exim option will install the Exim MTA from the ports\n"
+ "collection. To return to the previous menu, select Exit.",
+ NULL,
+ NULL,
+ {
+ { "Sendmail", "Use sendmail",
+ dmenuVarCheck, dmenuSetVariable, NULL, "sendmail_enable=YES" },
+ { "Sendmail local", "Use sendmail, but do not listen on the network",
+ dmenuVarCheck, dmenuSetVariable, NULL, "sendmail_enable=NO" },
+ { "Postfix", "Use the Postfix MTA",
+ NULL, configMTAPostfix, NULL, NULL },
+ { "Exim", "Use the Exim MTA",
+ NULL, configMTAExim, NULL, NULL },
+ { "None", "Do not install an MTA",
+ dmenuVarCheck, dmenuSetVariable, NULL, "sendmail_enable=NONE" },
+ { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+DMenu MenuNTP = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "NTPDATE Server Selection",
+ "There are a number of time synchronization servers available\n"
+ "for public use around the Internet. Please select one reasonably\n"
+ "close to you to have your system time synchronized accordingly.",
+ "These are the primary open-access NTP servers",
+ NULL,
+ { { "None", "No NTP server",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=NO,ntpdate_flags=none" },
+ { "Other", "Select a site not on this list",
+ dmenuVarsCheck, configNTP, NULL, NULL },
+ { "Worldwide", "pool.ntp.org",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=pool.ntp.org" },
+ { "Asia", "asia.pool.ntp.org",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=asia.pool.ntp.org" },
+ { "Europe", "europe.pool.ntp.org",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=europe.pool.ntp.org" },
+ { "Oceania", "oceania.pool.ntp.org",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=oceania.pool.ntp.org" },
+ { "North America", "north-america.pool.ntp.org",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=north-america.pool.ntp.org" },
+ { "Argentina", "tick.nap.com.ar",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tick.nap.com.ar" },
+ { "Argentina #2", "time.sinectis.com.ar",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.sinectis.com.ar" },
+ { "Argentina #3", "tock.nap.com.ar",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tock.nap.com.ar" },
+ { "Australia", "au.pool.ntp.org",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=au.pool.ntp.org" },
+ { "Australia #2", "augean.eleceng.adelaide.edu.au",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=augean.eleceng.adelaide.edu.au" },
+ { "Australia #3", "ntp.adelaide.edu.au",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.adelaide.edu.au" },
+ { "Australia #4", "ntp.saard.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.saard.net" },
+ { "Australia #5", "time.deakin.edu.au",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.deakin.edu.au" },
+ { "Belgium", "ntp1.belbone.be",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.belbone.be" },
+ { "Belgium #2", "ntp2.belbone.be",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.belbone.be" },
+ { "Brazil", "a.ntp.br",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=a.ntp.br" },
+ { "Brazil #2", "b.ntp.br",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=b.ntp.br" },
+ { "Brazil #3", "c.ntp.br",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=c.ntp.br" },
+ { "Brazil #4", "ntp.cais.rnp.br",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.cais.rnp.br" },
+ { "Brazil #5", "ntp1.pucpr.br",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.pucpr.br" },
+ { "Canada", "ca.pool.ntp.org",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ca.pool.ntp.org" },
+ { "Canada #2", "ntp.cpsc.ucalgary.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.cpsc.ucalgary.ca" },
+ { "Canada #3", "ntp1.cmc.ec.gc.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.cmc.ec.gc.ca" },
+ { "Canada #4", "ntp2.cmc.ec.gc.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.cmc.ec.gc.ca" },
+ { "Canada #5", "tick.utoronto.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tick.utoronto.ca" },
+ { "Canada #6", "time.chu.nrc.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.chu.nrc.ca" },
+ { "Canada #7", "time.nrc.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.nrc.ca" },
+ { "Canada #8", "timelord.uregina.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=timelord.uregina.ca" },
+ { "Canada #9", "tock.utoronto.ca",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tock.utoronto.ca" },
+ { "Czech", "ntp.karpo.cz",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.karpo.cz" },
+ { "Czech #2", "ntp.cgi.cz",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.cgi.cz" },
+ { "Denmark", "clock.netcetera.dk",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock.netcetera.dk" },
+ { "Denmark", "clock2.netcetera.dk",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock2.netcetera.dk" },
+ { "Spain", "slug.ctv.es",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=slug.ctv.es" },
+ { "Finland", "tick.keso.fi",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tick.keso.fi" },
+ { "Finland #2", "tock.keso.fi",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tock.keso.fi" },
+ { "France", "ntp.obspm.fr",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.obspm.fr" },
+ { "France #2", "ntp.univ-lyon1.fr",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.univ-lyon1.fr" },
+ { "France #3", "ntp.via.ecp.fr",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.via.ecp.fr" },
+ { "Croatia", "zg1.ntp.carnet.hr",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=zg1.ntp.carnet.hr" },
+ { "Croatia #2", "zg2.ntp.carnet.hr",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=zg2.ntp.carnet.hr" },
+ { "Croatia #3", "st.ntp.carnet.hr",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=st.ntp.carnet.hr" },
+ { "Croatia #4", "ri.ntp.carnet.hr",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ri.ntp.carnet.hr" },
+ { "Croatia #5", "os.ntp.carnet.hr",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=os.ntp.carnet.hr" },
+ { "Hungary", "time.kfki.hu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.kfki.hu" },
+ { "Indonesia", "ntp.kim.lipi.go.id",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.kim.lipi.go.id" },
+ { "Ireland", "ntp.maths.tcd.ie",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.maths.tcd.ie" },
+ { "Italy", "it.pool.ntp.org",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=it.pool.ntp.org" },
+ { "Japan", "ntp.jst.mfeed.ad.jp",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.jst.mfeed.ad.jp" },
+ { "Japan IPv6", "ntp1.v6.mfeed.ad.jp",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.v6.mfeed.ad.jp" },
+ { "Korea", "time.nuri.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.nuri.net" },
+ { "Mexico", "mx.pool.ntp.org",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=mx.pool.ntp.org" },
+ { "Netherlands", "ntp0.nl.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp0.nl.net" },
+ { "Netherlands #2", "ntp1.nl.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.nl.net" },
+ { "Netherlands #3", "ntp2.nl.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.nl.net" },
+ { "Norway", "fartein.ifi.uio.no",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=fartein.ifi.uio.no" },
+ { "Norway #2", "time.alcanet.no",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.alcanet.no" },
+ { "New Zealand", "ntp.massey.ac.nz",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.massey.ac.nz" },
+ { "New Zealand #2", "ntp.public.otago.ac.nz",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.public.otago.ac.nz" },
+ { "New Zealand #3", "tk1.ihug.co.nz",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tk1.ihug.co.nz" },
+ { "New Zealand #4", "ntp.waikato.ac.nz",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.waikato.ac.nz" },
+ { "Poland", "info.cyf-kr.edu.pl",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=info.cyf-kr.edu.pl" },
+ { "Romania", "ticks.roedu.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ticks.roedu.net" },
+ { "Russia", "ru.pool.ntp.org",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ru.pool.ntp.org" },
+ { "Russia #2", "ntp.psn.ru",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.psn.ru" },
+ { "Sweden", "se.pool.ntp.org",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=se.pool.ntp.org" },
+ { "Sweden #2", "ntp.lth.se",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.lth.se" },
+ { "Sweden #3", "ntp1.sp.se",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.sp.se" },
+ { "Sweden #4", "ntp2.sp.se",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.sp.se" },
+ { "Sweden #5", "ntp.kth.se",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.kth.se" },
+ { "Singapore", "sg.pool.ntp.org",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=sg.pool.ntp.org" },
+ { "Slovenia", "si.pool.ntp.org",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=si.pool.ntp.org" },
+ { "Slovenia #2", "sizif.mf.uni-lj.si",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=sizif.mf.uni-lj.si" },
+ { "Slovenia #3", "ntp1.arnes.si",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.arnes.si" },
+ { "Slovenia #4", "ntp2.arnes.si",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.arnes.si" },
+ { "Slovenia #5", "time.ijs.si",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.ijs.si" },
+ { "Scotland", "ntp.cs.strath.ac.uk",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.cs.strath.ac.uk" },
+ { "Taiwan", "time.stdtime.gov.tw",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.stdtime.gov.tw" },
+ { "Taiwan #2", "clock.stdtime.gov.tw",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock.stdtime.gov.tw" },
+ { "Taiwan #3", "tick.stdtime.gov.tw",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tick.stdtime.gov.tw" },
+ { "Taiwan #4", "tock.stdtime.gov.tw",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tock.stdtime.gov.tw" },
+ { "Taiwan #5", "watch.stdtime.gov.tw",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=watch.stdtime.gov.tw" },
+ { "United Kingdom", "uk.pool.ntp.org",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=uk.pool.ntp.org" },
+ { "United Kingdom #2", "ntp.exnet.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.exnet.com" },
+ { "United Kingdom #3", "ntp0.uk.uu.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp0.uk.uu.net" },
+ { "United Kingdom #4", "ntp1.uk.uu.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.uk.uu.net" },
+ { "United Kingdom #5", "ntp2.uk.uu.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.uk.uu.net" },
+ { "United Kingdom #6", "ntp2a.mcc.ac.uk",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2a.mcc.ac.uk" },
+ { "United Kingdom #7", "ntp2b.mcc.ac.uk",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2b.mcc.ac.uk" },
+ { "United Kingdom #8", "ntp2c.mcc.ac.uk",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2c.mcc.ac.uk" },
+ { "United Kingdom #9", "ntp2d.mcc.ac.uk",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2d.mcc.ac.uk" },
+ { "U.S.", "us.pool.ntp.org",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=us.pool.ntp.org" },
+ { "U.S. AR", "sushi.lyon.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=sushi.compsci.lyon.edu" },
+ { "U.S. AZ", "ntp.drydog.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.drydog.com" },
+ { "U.S. CA", "ntp.ucsd.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.ucsd.edu" },
+ { "U.S. CA #2", "ntp1.mainecoon.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.mainecoon.com" },
+ { "U.S. CA #3", "ntp2.mainecoon.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.mainecoon.com" },
+ { "U.S. CA #4", "reloj.kjsl.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=reloj.kjsl.com" },
+ { "U.S. CA #5", "time.five-ten-sg.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time.five-ten-sg.com" },
+ { "U.S. DE", "louie.udel.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=louie.udel.edu" },
+ { "U.S. GA", "ntp.shorty.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.shorty.com" },
+ { "U.S. GA #2", "rolex.usg.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=rolex.usg.edu" },
+ { "U.S. GA #3", "timex.usg.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=timex.usg.edu" },
+ { "U.S. IL", "ntp-0.cso.uiuc.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp-0.cso.uiuc.edu" },
+ { "U.S. IL #2", "ntp-1.cso.uiuc.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp-1.cso.uiuc.edu" },
+ { "U.S. IL #3", "ntp-1.mcs.anl.gov",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp-1.mcs.anl.gov" },
+ { "U.S. IL #4", "ntp-2.cso.uiuc.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp-2.cso.uiuc.edu" },
+ { "U.S. IL #5", "ntp-2.mcs.anl.gov",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp-2.mcs.anl.gov" },
+ { "U.S. IN", "gilbreth.ecn.purdue.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=gilbreth.ecn.purdue.edu" },
+ { "U.S. IN #2", "harbor.ecn.purdue.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=harbor.ecn.purdue.edu" },
+ { "U.S. IN #3", "molecule.ecn.purdue.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=molecule.ecn.purdue.edu" },
+ { "U.S. KS", "ntp1.kansas.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.kansas.net" },
+ { "U.S. KS #2", "ntp2.kansas.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.kansas.net" },
+ { "U.S. MA", "ntp.ourconcord.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.ourconcord.net" },
+ { "U.S. MA #2", "timeserver.cs.umb.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=timeserver.cs.umb.edu" },
+ { "U.S. MN", "ns.nts.umn.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ns.nts.umn.edu" },
+ { "U.S. MN #2", "nss.nts.umn.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=nss.nts.umn.edu" },
+ { "U.S. MO", "time-ext.missouri.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=time-ext.missouri.edu" },
+ { "U.S. MT", "chronos1.umt.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=chronos1.umt.edu" },
+ { "U.S. MT #2", "chronos2.umt.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=chronos2.umt.edu" },
+ { "U.S. MT #3", "chronos3.umt.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=chronos3.umt.edu" },
+ { "U.S. NC", "clock1.unc.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock1.unc.edu" },
+ { "U.S. NV", "cuckoo.nevada.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=cuckoo.nevada.edu" },
+ { "U.S. NV #2", "tick.cs.unlv.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tick.cs.unlv.edu" },
+ { "U.S. NV #3", "tock.cs.unlv.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tock.cs.unlv.edu" },
+ { "U.S. NY", "ntp0.cornell.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp0.cornell.edu" },
+ { "U.S. NY #2", "sundial.columbia.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=sundial.columbia.edu" },
+ { "U.S. NY #3", "timex.cs.columbia.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=timex.cs.columbia.edu" },
+ { "U.S. PA", "clock-1.cs.cmu.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock-1.cs.cmu.edu" },
+ { "U.S. PA #2", "clock-2.cs.cmu.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock-2.cs.cmu.edu" },
+ { "U.S. PA #3", "clock.psu.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock.psu.edu" },
+ { "U.S. PA #4", "fuzz.psc.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=fuzz.psc.edu" },
+ { "U.S. PA #5", "ntp-1.ece.cmu.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp-1.ece.cmu.edu" },
+ { "U.S. PA #6", "ntp-2.ece.cmu.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp-2.ece.cmu.edu" },
+ { "U.S. TX", "ntp.fnbhs.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.fnbhs.com" },
+ { "U.S. TX #2", "ntp.tmc.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.tmc.edu" },
+ { "U.S. TX #3", "ntp5.tamu.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp5.tamu.edu" },
+ { "U.S. TX #4", "tick.greyware.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tick.greyware.com" },
+ { "U.S. TX #5", "tock.greyware.com",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tock.greyware.com" },
+ { "U.S. VA", "ntp-1.vt.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp-1.vt.edu" },
+ { "U.S. VA #2", "ntp-2.vt.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp-2.vt.edu" },
+ { "U.S. VA #3", "ntp.cmr.gov",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.cmr.gov" },
+ { "U.S. VT", "ntp0.state.vt.us",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp0.state.vt.us" },
+ { "U.S. VT #2", "ntp1.state.vt.us",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.state.vt.us" },
+ { "U.S. VT #3", "ntp2.state.vt.us",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp2.state.vt.us" },
+ { "U.S. WA", "ntp.tcp-udp.net",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.tcp-udp.net" },
+ { "U.S. WI", "ntp1.cs.wisc.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp1.cs.wisc.edu" },
+ { "U.S. WI #2", "ntp3.cs.wisc.edu",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp3.cs.wisc.edu" },
+ { "South Africa", "ntp.cs.unp.ac.za",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.cs.unp.ac.za" },
+ { NULL } },
+};
+
+#ifdef WITH_SYSCONS
+DMenu MenuSyscons = {
+ DMENU_NORMAL_TYPE,
+ "System Console Configuration",
+ "The default system console driver for FreeBSD (syscons) has a\n"
+ "number of configuration options which may be set according to\n"
+ "your preference.\n\n"
+ "When you are done setting configuration options, select Cancel.",
+ "Configure your system console settings",
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+#ifdef PC98
+ { "2 Keymap", "Choose an alternate keyboard map", NULL, dmenuSubmenu, NULL, &MenuSysconsKeymap },
+ { "3 Repeat", "Set the rate at which keys repeat", NULL, dmenuSubmenu, NULL, &MenuSysconsKeyrate },
+ { "4 Saver", "Configure the screen saver", NULL, dmenuSubmenu, NULL, &MenuSysconsSaver },
+#else
+ { "2 Font", "Choose an alternate screen font", NULL, dmenuSubmenu, NULL, &MenuSysconsFont },
+ { "3 Keymap", "Choose an alternate keyboard map", NULL, dmenuSubmenu, NULL, &MenuSysconsKeymap },
+ { "4 Repeat", "Set the rate at which keys repeat", NULL, dmenuSubmenu, NULL, &MenuSysconsKeyrate },
+ { "5 Saver", "Configure the screen saver", NULL, dmenuSubmenu, NULL, &MenuSysconsSaver },
+ { "6 Screenmap", "Choose an alternate screenmap", NULL, dmenuSubmenu, NULL, &MenuSysconsScrnmap },
+ { "7 Ttys", "Choose console terminal type", NULL, dmenuSubmenu, NULL, &MenuSysconsTtys },
+#endif
+ { NULL } },
+};
+
+#ifdef PC98
+DMenu MenuSysconsKeymap = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Keymap",
+ "The default system console driver for FreeBSD (syscons) defaults\n"
+ "to a standard \"PC-98x1\" keyboard map. Users may wish to choose\n"
+ "one of the other keymaps below.\n"
+ "Note that sysinstall itself only uses the part of the keyboard map\n"
+ "which is required to generate the ANSI character subset, but your\n"
+ "choice of keymap will also be saved for later (fuller) use.",
+ "Choose a keyboard map",
+ NULL,
+ { { "Japanese PC-98x1", "Japanese PC-98x1 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=jp.pc98" },
+ { " Japanese PC-98x1 (ISO)", "Japanese PC-98x1 (ISO) keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=jp.pc98.iso" },
+ { NULL } },
+};
+#else
+DMenu MenuSysconsKeymap = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Keymap",
+ "The default system console driver for FreeBSD (syscons) defaults\n"
+ "to a standard \"American\" keyboard map. Users in other countries\n"
+ "(or with different keyboard preferences) may wish to choose one of\n"
+ "the other keymaps below.\n"
+ "Note that sysinstall itself only uses the part of the keyboard map\n"
+ "which is required to generate the ANSI character subset, but your\n"
+ "choice of keymap will also be saved for later (fuller) use.",
+ "Choose a keyboard map",
+ NULL,
+ { { "Belgian", "Belgian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=be.iso" },
+ { " Brazil CP850", "Brazil CP850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=br275.cp850" },
+ { " Brazil ISO (accent)", "Brazil ISO keymap (accent keys)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=br275.iso.acc" },
+ { " Brazil ISO", "Brazil ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=br275.iso" },
+ { " Bulgarian BDS", "Bulgarian BDS keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=bg.bds.ctrlcaps" },
+ { " Bulgarian Phonetic", "Bulgarian Phonetic keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=bg.phonetic.ctrlcaps" },
+ { "Central European ISO", "Central European ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=ce.iso2" },
+ { " Croatian ISO", "Croatian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=hr.iso" },
+ { " Czech ISO (accent)", "Czech ISO keymap (accent keys)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=cs.latin2.qwertz" },
+ { "Danish CP865", "Danish Code Page 865 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=danish.cp865" },
+ { " Danish ISO", "Danish ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=danish.iso" },
+ { "Estonian ISO", "Estonian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=estonian.iso" },
+ { " Estonian ISO 15", "Estonian ISO 8859-15 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=estonian.iso15" },
+ { " Estonian CP850", "Estonian Code Page 850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=estonian.cp850" },
+ { "Finnish CP850","Finnish Code Page 850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=finnish.cp850" },
+ { " Finnish ISO", "Finnish ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=finnish.iso" },
+ { " French ISO (accent)", "French ISO keymap (accent keys)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=fr.iso.acc" },
+ { " French ISO", "French ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=fr.iso" },
+ { "German CP850", "German Code Page 850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=german.cp850" },
+ { " German ISO", "German ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=german.iso" },
+ { " Greek 101", "Greek ISO keymap (101 keys)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=gr.us101.acc" },
+ { " Greek 104", "Greek ISO keymap (104 keys)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=el.iso07" },
+ { " Greek ELOT", "Greek ISO keymap (ELOT 1000)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=gr.elot.acc" },
+ { "Hungarian 101", "Hungarian ISO keymap (101 key)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=hu.iso2.101keys" },
+ { " Hungarian 102", "Hungarian ISO keymap (102 key)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=hu.iso2.102keys" },
+ { "Icelandic (accent)", "Icelandic ISO keymap (accent keys)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=icelandic.iso.acc" },
+ { " Icelandic", "Icelandic ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=icelandic.iso" },
+ { " Italian", "Italian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=it.iso" },
+ { "Japanese 106", "Japanese 106 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=jp.106" },
+ { "Latin American (accent)", "Latin American ISO keymap (accent keys)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=latinamerican.iso.acc" },
+ { " Latin American", "Latin American ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=latinamerican" },
+ { "Norway ISO", "Norwegian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=norwegian.iso" },
+ { "Polish ISO", "Polish ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=pl_PL.ISO8859-2" },
+ { " Portuguese (accent)", "Portuguese ISO keymap (accent keys)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=pt.iso.acc" },
+ { " Portuguese", "Portuguese ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=pt.iso" },
+ { "Russia KOI8-R", "Russian KOI8-R keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=ru.koi8-r" },
+ { "Slovak", "Slovak ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=sk.iso2" },
+ { "Slovenian", "Slovenian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=si.iso" },
+ { " Spanish (accent)", "Spanish ISO keymap (accent keys)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=spanish.iso.acc" },
+ { " Spanish", "Spanish ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=spanish.iso" },
+ { " Swedish CP850", "Swedish Code Page 850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swedish.cp850" },
+ { " Swedish ISO", "Swedish ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swedish.iso" },
+ { " Swiss French ISO (accent)", "Swiss French ISO keymap (accent keys)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swissfrench.iso.acc" },
+ { " Swiss French ISO", "Swiss French ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swissfrench.iso" },
+ { " Swiss French CP850", "Swiss French Code Page 850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swissfrench.cp850" },
+ { " Swiss German ISO (accent)", "Swiss German ISO keymap (accent keys)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swissgerman.iso.acc" },
+ { " Swiss German ISO", "Swiss German ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swissgerman.iso" },
+ { " Swiss German CP850", "Swiss German Code Page 850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swissgerman.cp850" },
+ { "UK CP850", "UK Code Page 850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=uk.cp850" },
+ { " UK ISO", "UK ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=uk.iso" },
+ { " Ukrainian KOI8-U", "Ukrainian KOI8-U keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=ua.koi8-u" },
+ { " Ukrainian KOI8-U+KOI8-R", "Ukrainian KOI8-U+KOI8-R keymap (alter)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=ua.koi8-u.shift.alt" },
+ { " USA CapsLock->Ctrl", "US standard (Caps as L-Control)", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=us.pc-ctrl" },
+ { " USA Dvorak", "US Dvorak keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=us.dvorak" },
+ { " USA Dvorak (left)", "US left handed Dvorak keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=us.dvorakl" },
+ { " USA Dvorak (right)", "US right handed Dvorak keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=us.dvorakr" },
+ { " USA Emacs", "US standard optimized for EMACS", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=us.emacs" },
+ { " USA ISO", "US ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=us.iso" },
+ { " USA UNIX", "US traditional UNIX-workstation", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=us.unix" },
+ { NULL } },
+};
+#endif /* PC98 */
+
+DMenu MenuSysconsKeyrate = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Keyboard Repeat Rate",
+ "This menu allows you to set the speed at which keys repeat\n"
+ "when held down.",
+ "Choose a keyboard repeat rate",
+ NULL,
+ { { "Slow", "Slow keyboard repeat rate", dmenuVarCheck, dmenuSetVariable, NULL, "keyrate=slow" },
+ { "Normal", "\"Normal\" keyboard repeat rate", dmenuVarCheck, dmenuSetVariable, NULL, "keyrate=normal" },
+ { "Fast", "Fast keyboard repeat rate", dmenuVarCheck, dmenuSetVariable, NULL, "keyrate=fast" },
+ { "Default", "Use default keyboard repeat rate", dmenuVarCheck, dmenuSetVariable, NULL, "keyrate=NO" },
+ { NULL } },
+};
+
+DMenu MenuSysconsSaver = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Screen Saver",
+ "By default, the console driver will not attempt to do anything\n"
+ "special with your screen when it's idle. If you expect to leave your\n"
+ "monitor switched on and idle for long periods of time then you should\n"
+ "probably enable one of these screen savers to prevent burn-in.",
+ "Choose a nifty-looking screen saver",
+ NULL,
+ { { "1 Blank", "Simply blank the screen",
+ dmenuVarCheck, configSaver, NULL, "saver=blank" },
+ { "2 Daemon", "\"BSD Daemon\" animated screen saver (text)",
+ dmenuVarCheck, configSaver, NULL, "saver=daemon" },
+ { "3 Fade", "Fade out effect screen saver",
+ dmenuVarCheck, configSaver, NULL, "saver=fade" },
+ { "4 Fire", "Flames effect screen saver",
+ dmenuVarCheck, configSaver, NULL, "saver=fire" },
+ { "5 Green", "\"Green\" power saving mode (if supported by monitor)",
+ dmenuVarCheck, configSaver, NULL, "saver=green" },
+ { "6 Logo", "\"BSD Daemon\" animated screen saver (graphics)",
+ dmenuVarCheck, configSaver, NULL, "saver=logo" },
+ { "7 Rain", "Rain drops screen saver",
+ dmenuVarCheck, configSaver, NULL, "saver=rain" },
+ { "8 Snake", "Draw a FreeBSD \"snake\" on your screen",
+ dmenuVarCheck, configSaver, NULL, "saver=snake" },
+ { "9 Star", "A \"twinkling stars\" effect",
+ dmenuVarCheck, configSaver, NULL, "saver=star" },
+ { "Warp", "A \"stars warping\" effect",
+ dmenuVarCheck, configSaver, NULL, "saver=warp" },
+ { "Dragon", "Dragon screensaver (graphics)",
+ dmenuVarCheck, configSaver, NULL, "saver=dragon" },
+ { "Timeout", "Set the screen saver timeout interval",
+ NULL, configSaverTimeout, NULL, NULL, ' ', ' ', ' ' },
+ { NULL } },
+};
+
+#ifndef PC98
+DMenu MenuSysconsScrnmap = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Screenmap",
+ "Unless you load a specific font, most PC hardware defaults to\n"
+ "displaying characters in the IBM 437 character set. However,\n"
+ "in the Unix world, this character set is very rarely used. Most\n"
+ "Western European countries, for example, prefer ISO 8859-1.\n"
+ "American users won't notice the difference since the bottom half\n"
+ "of all these character sets is ANSI anyway.\n"
+ "If your hardware is capable of downloading a new display font,\n"
+ "you should probably choose that option. However, for hardware\n"
+ "where this is not possible (e.g. monochrome adapters), a screen\n"
+ "map will give you the best approximation that your hardware can\n"
+ "display at all.",
+ "Choose a screen map",
+ NULL,
+ { { "1 None", "No screenmap, don't touch font", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=NO" },
+ { "2 ISO 8859-1 to IBM437", "W-Europe ISO 8859-1 to IBM 437 screenmap", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=iso-8859-1_to_cp437" },
+ { "3 ISO 8859-7 to IBM437", "Greek ISO 8859-7 to IBM 437 screenmap", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=iso-8859-7_to_cp437" },
+ { "4 US-ASCII to IBM437", "US-ASCII to IBM 437 screenmap", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=us-ascii_to_cp437" },
+ { "5 KOI8-R to IBM866", "Russian KOI8-R to IBM 866 screenmap", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=koi8-r2cp866" },
+ { "6 KOI8-U to IBM866u", "Ukrainian KOI8-U to IBM 866u screenmap", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=koi8-u2cp866u" },
+ { NULL } },
+};
+
+DMenu MenuSysconsTtys = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Terminal Type",
+ "For various console encodings, a corresponding terminal type\n"
+ "must be chosen in /etc/ttys.\n\n"
+ "WARNING: For compatibility reasons, only entries starting with\n"
+ "ttyv and terminal types starting with cons[0-9] can be changed\n"
+ "via this menu.\n",
+ "Choose a terminal type",
+ NULL,
+ { { "1 None", "Don't touch anything", dmenuVarCheck, dmenuSetVariable, NULL, VAR_CONSTERM "=NO" },
+ { "2 IBM437 (VGA default)", "cons25", dmenuVarCheck, dmenuSetVariable, NULL, VAR_CONSTERM "=cons25" },
+ { "3 ISO 8859-1", "cons25l1", dmenuVarCheck, dmenuSetVariable, NULL, VAR_CONSTERM "=cons25l1" },
+ { "4 ISO 8859-2", "cons25l2", dmenuVarCheck, dmenuSetVariable, NULL, VAR_CONSTERM "=cons25l2" },
+ { "5 ISO 8859-7", "cons25l7", dmenuVarCheck, dmenuSetVariable, NULL, VAR_CONSTERM "=cons25l7" },
+ { "6 KOI8-R", "cons25r", dmenuVarCheck, dmenuSetVariable, NULL, VAR_CONSTERM "=cons25r" },
+ { "7 KOI8-U", "cons25u", dmenuVarCheck, dmenuSetVariable, NULL, VAR_CONSTERM "=cons25u" },
+ { "8 US-ASCII", "cons25w", dmenuVarCheck, dmenuSetVariable, NULL, VAR_CONSTERM "=cons25w" },
+ { NULL } },
+};
+
+DMenu MenuSysconsFont = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Font",
+ "Most PC hardware defaults to displaying characters in the\n"
+ "IBM 437 character set. However, in the Unix world, this\n"
+ "character set is very rarely used. Most Western European\n"
+ "countries, for example, prefer ISO 8859-1.\n"
+ "American users won't notice the difference since the bottom half\n"
+ "of all these charactersets is ANSI anyway. However, they might\n"
+ "want to load a font anyway to use the 30- or 50-line displays.\n"
+ "If your hardware is capable of downloading a new display font,\n"
+ "you can select the appropriate font below.",
+ "Choose a font",
+ NULL,
+ { { "1 None", "Use hardware default font", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=NO,font8x14=NO,font8x16=NO" },
+ { "2 IBM 437", "English and others, VGA default", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp437-8x8,font8x14=cp437-8x14,font8x16=cp437-8x16" },
+ { "3 IBM 850", "Western Europe, IBM encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp850-8x8,font8x14=cp850-8x14,font8x16=cp850-8x16" },
+ { "4 IBM 865", "Norwegian, IBM encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp865-8x8,font8x14=cp865-8x14,font8x16=cp865-8x16" },
+ { "5 IBM 866", "Russian, IBM encoding (use with KOI8-R screenmap)", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp866-8x8,font8x14=cp866-8x14,font8x16=cp866b-8x16,mousechar_start=3" },
+ { "6 IBM 866u", "Ukrainian, IBM encoding (use with KOI8-U screenmap)", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp866u-8x8,font8x14=cp866u-8x14,font8x16=cp866u-8x16,mousechar_start=3" },
+ { "7 IBM 1251", "Cyrillic, MS Windows encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp1251-8x8,font8x14=cp1251-8x14,font8x16=cp1251-8x16,mousechar_start=3" },
+ { "8 ISO 8859-1", "Western Europe, ISO encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=iso-8x8,font8x14=iso-8x14,font8x16=iso-8x16" },
+ { "9 ISO 8859-2", "Eastern Europe, ISO encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=iso02-8x8,font8x14=iso02-8x14,font8x16=iso02-8x16" },
+ { "a ISO 8859-4", "Baltic, ISO encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=iso04-8x8,font8x14=iso04-8x14,font8x16=iso04-8x16" },
+ { "b ISO 8859-7", "Greek, ISO encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=iso07-8x8,font8x14=iso07-8x14,font8x16=iso07-8x16" },
+ { "c ISO 8859-8", "Hebrew, ISO encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=iso08-8x8,font8x14=iso08-8x14,font8x16=iso08-8x16" },
+ { "d ISO 8859-15", "Europe, ISO encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=iso15-8x8,font8x14=iso15-8x14,font8x16=iso15-8x16" },
+ { "e SWISS", "English, better resolution", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=swiss-8x8,font8x14=NO,font8x16=swiss-8x16" },
+ { NULL } },
+};
+#endif /* PC98 */
+#endif /* WITH_SYSCONS */
+
+DMenu MenuUsermgmt = {
+ DMENU_NORMAL_TYPE,
+ "User and group management",
+ "The submenus here allow to manipulate user groups and\n"
+ "login accounts.\n",
+ "Configure your user groups and users",
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { "User", "Add a new user to the system.", NULL, userAddUser },
+ { "Group", "Add a new user group to the system.", NULL, userAddGroup },
+ { NULL } },
+};
+
+DMenu MenuSecurity = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "System Security Options Menu",
+ "This menu allows you to configure aspects of the operating system security\n"
+ "policy. Please read the system documentation carefully before modifying\n"
+ "these settings, as they may cause service disruption if used improperly.\n"
+ "\n"
+ "Most settings will take affect only following a system reboot.",
+ "Configure system security options",
+ NULL,
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { " Securelevel", "Configure securelevels for the system",
+ NULL, configSecurelevel },
+#if 0
+ { " LOMAC", "Use Low Watermark Mandatory Access Control at boot",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "lomac_enable=YES" },
+#endif
+ { " NFS port", "Require that the NFS clients use reserved ports",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "nfs_reserved_port_only=YES" },
+ { NULL } },
+};
+
+DMenu MenuSecurelevel = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Securelevel Configuration Menu",
+ "This menu allows you to select the securelevel your system runs with.\n"
+ "When operating at a securelevel, certain root privileges are disabled,\n"
+ "which may increase resistance to exploits and protect system integrity.\n"
+ "In secure mode system flags may not be overriden by the root user,\n"
+ "access to direct kernel memory is limited, and kernel modules may not\n"
+ "be changed. In highly secure mode, mounted file systems may not be\n"
+ "modified on-disk, tampering with the system clock is prohibited. In\n"
+ "network secure mode configuration changes to firewalling are prohibited.\n",
+ "Select a securelevel to operate at - F1 for help",
+ "securelevel",
+ { { "X Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { "Disabled", "Disable securelevels", NULL, configSecurelevelDisabled, },
+ { "Secure", "Secure mode", NULL, configSecurelevelSecure },
+ { "Highly Secure", "Highly secure mode", NULL, configSecurelevelHighlySecure },
+ { "Network Secure", "Network secure mode", NULL, configSecurelevelNetworkSecure },
+ { NULL } }
+};
+
+DMenu MenuFixit = {
+ DMENU_NORMAL_TYPE,
+ "Please choose a fixit option",
+ "There are three ways of going into \"fixit\" mode:\n"
+ "- you can use the live filesystem CDROM/DVD, in which case there will be\n"
+ " full access to the complete set of FreeBSD commands and utilities,\n"
+ "- you can use the more limited (but perhaps customized) fixit floppy,\n"
+ "- or you can start an Emergency Holographic Shell now, which is\n"
+ " limited to the subset of commands that is already available right now.",
+ "Press F1 for more detailed repair instructions",
+ "fixit",
+{ { "X Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { "2 CDROM/DVD", "Use the \"live\" filesystem CDROM/DVD", NULL, installFixitCDROM },
+ { "3 Floppy", "Use a floppy generated from the fixit image", NULL, installFixitFloppy },
+ { "4 Shell", "Start an Emergency Holographic Shell", NULL, installFixitHoloShell },
+ { NULL } },
+};
diff --git a/usr.sbin/sysinstall/misc.c b/usr.sbin/sysinstall/misc.c
new file mode 100644
index 0000000..c978b76
--- /dev/null
+++ b/usr.sbin/sysinstall/misc.c
@@ -0,0 +1,553 @@
+/*
+ * Miscellaneous support routines..
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sysinstall.h"
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <ufs/ufs/ufsmount.h>
+#include <sys/reboot.h>
+#include <sys/disklabel.h>
+#include <fs/msdosfs/msdosfsmount.h>
+#include <sys/sysctl.h>
+
+/* Quick check to see if a file is readable */
+Boolean
+file_readable(char *fname)
+{
+ if (!access(fname, F_OK))
+ return TRUE;
+ return FALSE;
+}
+
+/* Quick check to see if a file is executable */
+Boolean
+file_executable(char *fname)
+{
+ if (!access(fname, X_OK))
+ return TRUE;
+ return FALSE;
+}
+
+/* Concatenate two strings into static storage */
+char *
+string_concat(char *one, char *two)
+{
+ static char tmp[FILENAME_MAX];
+
+ /* Yes, we're deliberately cavalier about not checking for overflow */
+ strcpy(tmp, one);
+ strcat(tmp, two);
+ return tmp;
+}
+
+/* sane strncpy() function */
+char *
+sstrncpy(char *dst, const char *src, int size)
+{
+ dst[size] = '\0';
+ return strncpy(dst, src, size);
+}
+
+/* Concatenate three strings into static storage */
+char *
+string_concat3(char *one, char *two, char *three)
+{
+ static char tmp[FILENAME_MAX];
+
+ /* Yes, we're deliberately cavalier about not checking for overflow */
+ strcpy(tmp, one);
+ strcat(tmp, two);
+ strcat(tmp, three);
+ return tmp;
+}
+
+/* Clip the whitespace off the end of a string */
+char *
+string_prune(char *str)
+{
+ int len = str ? strlen(str) : 0;
+
+ while (len && isspace(str[len - 1]))
+ str[--len] = '\0';
+ return str;
+}
+
+/* run the whitespace off the front of a string */
+char *
+string_skipwhite(char *str)
+{
+ while (*str && isspace(*str))
+ ++str;
+ return str;
+}
+
+/* copy optionally and allow second arg to be null */
+char *
+string_copy(char *s1, char *s2)
+{
+ if (!s1)
+ return NULL;
+ if (!s2)
+ s1[0] = '\0';
+ else
+ strcpy(s1, s2);
+ return s1;
+}
+
+/* convert an integer to a string, using a static buffer */
+char *
+itoa(int value)
+{
+ static char buf[13];
+
+ snprintf(buf, 12, "%d", value);
+ return buf;
+}
+
+Boolean
+directory_exists(const char *dirname)
+{
+ DIR *tptr;
+
+ if (!dirname)
+ return FALSE;
+ if (!strlen(dirname))
+ return FALSE;
+
+ tptr = opendir(dirname);
+ if (!tptr)
+ return (FALSE);
+
+ closedir(tptr);
+ return (TRUE);
+}
+
+char *
+pathBaseName(const char *path)
+{
+ char *pt;
+ char *ret = (char *)path;
+
+ pt = strrchr(path,(int)'/');
+
+ if (pt != 0) /* if there is a slash */
+ {
+ ret = ++pt; /* start the file after it */
+ }
+
+ return(ret);
+}
+
+/* A free guaranteed to take NULL ptrs */
+void
+safe_free(void *ptr)
+{
+ if (ptr)
+ free(ptr);
+}
+
+/* A malloc that checks errors */
+void *
+safe_malloc(size_t size)
+{
+ void *ptr;
+
+ if (size <= 0)
+ msgFatal("Invalid malloc size of %ld!", (long)size);
+ ptr = malloc(size);
+ if (!ptr)
+ msgFatal("Out of memory!");
+ bzero(ptr, size);
+ return ptr;
+}
+
+/* A realloc that checks errors */
+void *
+safe_realloc(void *orig, size_t size)
+{
+ void *ptr;
+
+ if (size <= 0)
+ msgFatal("Invalid realloc size of %ld!", (long)size);
+ ptr = reallocf(orig, size);
+ if (!ptr)
+ msgFatal("Out of memory!");
+ return ptr;
+}
+
+/* Create a path biased from the VAR_INSTALL_ROOT variable (if not /) */
+char *
+root_bias(char *path)
+{
+ static char tmp[FILENAME_MAX];
+ char *cp = variable_get(VAR_INSTALL_ROOT);
+
+ if (!strcmp(cp, "/"))
+ return path;
+ strcpy(tmp, variable_get(VAR_INSTALL_ROOT));
+ strcat(tmp, path);
+ return tmp;
+}
+
+/*
+ * These next routines are kind of specialized just for building item lists
+ * for dialog_menu().
+ */
+
+/* Add an item to an item list */
+dialogMenuItem *
+item_add(dialogMenuItem *list, char *prompt, char *title,
+ int (*checked)(dialogMenuItem *self),
+ int (*fire)(dialogMenuItem *self),
+ void (*selected)(dialogMenuItem *self, int is_selected),
+ void *data, int *aux, int *curr, int *max)
+{
+ dialogMenuItem *d;
+
+ if (*curr == *max) {
+ *max += 20;
+ list = (dialogMenuItem *)safe_realloc(list, sizeof(dialogMenuItem) * *max);
+ }
+ d = &list[(*curr)++];
+ bzero(d, sizeof(*d));
+ d->prompt = prompt ? strdup(prompt) : NULL;
+ d->title = title ? strdup(title) : NULL;
+ d->checked = checked;
+ d->fire = fire;
+ d->selected = selected;
+ d->data = data;
+ d->aux = (long)aux;
+ return list;
+}
+
+/* Toss the items out */
+void
+items_free(dialogMenuItem *list, int *curr, int *max)
+{
+ int i;
+
+ for (i = 0; list[i].prompt; i++) {
+ safe_free(list[i].prompt);
+ safe_free(list[i].title);
+ }
+ safe_free(list);
+ *curr = *max = 0;
+}
+
+int
+Mkdir(char *ipath)
+{
+ struct stat sb;
+ int final;
+ char *p, *path;
+
+ if (file_readable(ipath) || Fake)
+ return DITEM_SUCCESS;
+
+ path = strcpy(alloca(strlen(ipath) + 1), ipath);
+ if (isDebug())
+ msgDebug("mkdir(%s)\n", path);
+ p = path;
+ if (p[0] == '/') /* Skip leading '/'. */
+ ++p;
+ for (final = FALSE; !final; ++p) {
+ if (p[0] == '\0' || (p[0] == '/' && p[1] == '\0'))
+ final = TRUE;
+ else if (p[0] != '/')
+ continue;
+ *p = '\0';
+ if (stat(path, &sb)) {
+ if (errno != ENOENT) {
+ msgConfirm("Couldn't stat directory %s: %s", path, strerror(errno));
+ return DITEM_FAILURE;
+ }
+ if (isDebug())
+ msgDebug("mkdir(%s..)\n", path);
+ if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
+ msgConfirm("Couldn't create directory %s: %s", path,strerror(errno));
+ return DITEM_FAILURE;
+ }
+ }
+ *p = '/';
+ }
+ return DITEM_SUCCESS;
+}
+
+int
+Mkdir_command(char *key, void *dir)
+{
+ return (Mkdir((char*)dir));
+}
+
+int
+Mount(char *mountp, void *dev)
+{
+ struct ufs_args ufsargs;
+ char device[80];
+ char mountpoint[FILENAME_MAX];
+
+ if (Fake)
+ return DITEM_SUCCESS;
+
+ if (*((char *)dev) != '/') {
+ sprintf(device, "%s/dev/%s", RunningAsInit ? "/mnt" : "", (char *)dev);
+ sprintf(mountpoint, "%s%s", RunningAsInit ? "/mnt" : "", mountp);
+ }
+ else {
+ strcpy(device, dev);
+ strcpy(mountpoint, mountp);
+ }
+ memset(&ufsargs,0,sizeof ufsargs);
+
+ if (Mkdir(mountpoint)) {
+ msgConfirm("Unable to make directory mountpoint for %s!", mountpoint);
+ return DITEM_FAILURE;
+ }
+ if (isDebug())
+ msgDebug("mount %s %s\n", device, mountpoint);
+
+ ufsargs.fspec = device;
+ if (mount("ufs", mountpoint, RunningAsInit ? MNT_ASYNC | MNT_NOATIME : 0,
+ (caddr_t)&ufsargs) == -1) {
+ msgConfirm("Error mounting %s on %s : %s", device, mountpoint, strerror(errno));
+ return DITEM_FAILURE;
+ }
+ return DITEM_SUCCESS;
+}
+
+int
+Mount_msdosfs(char *mountp, void *dev)
+{
+ struct msdosfs_args mount_args;
+ char device[80];
+ char mountpoint[FILENAME_MAX];
+
+ if (Fake)
+ return DITEM_SUCCESS;
+
+ if (*((char *)dev) != '/') {
+ sprintf(device, "%s/dev/%s", RunningAsInit ? "/mnt" : "", (char *)dev);
+ sprintf(mountpoint, "%s%s", RunningAsInit ? "/mnt" : "", mountp);
+ }
+ else {
+ strcpy(device, dev);
+ strcpy(mountpoint, mountp);
+ }
+
+ if (Mkdir(mountpoint)) {
+ msgConfirm("Unable to make directory mountpoint for %s!", mountpoint);
+ return DITEM_FAILURE;
+ }
+ if (isDebug())
+ msgDebug("mount %s %s\n", device, mountpoint);
+
+ memset(&mount_args, 0, sizeof(mount_args));
+ mount_args.fspec = device;
+ mount_args.magic = MSDOSFS_ARGSMAGIC;
+ mount_args.mask = S_IRWXU | S_IRWXG | S_IRWXO;
+ if (mount("msdosfs", mountpoint, RunningAsInit ? MNT_ASYNC|MNT_NOATIME : 0,
+ (caddr_t)&mount_args) == -1) {
+ msgConfirm("Error mounting %s on %s : %s", device, mountpoint, strerror(errno));
+ return DITEM_FAILURE;
+ }
+ return DITEM_SUCCESS;
+}
+
+WINDOW *
+openLayoutDialog(char *helpfile, char *title, int x, int y, int width, int height)
+{
+ WINDOW *win;
+ static char help[FILENAME_MAX];
+
+ /* We need a curses window */
+ win = newwin(LINES, COLS, 0, 0);
+ if (win) {
+ /* Say where our help comes from */
+ if (helpfile) {
+ use_helpline("Press F1 for more information on this screen.");
+ use_helpfile(systemHelpFile(helpfile, help));
+ }
+ /* Setup a nice screen for us to splat stuff onto */
+ draw_box(win, y, x, height, width, dialog_attr, border_attr);
+ wattrset(win, dialog_attr);
+ mvwaddstr(win, y, x + (COLS - strlen(title)) / 2, title);
+ }
+ return win;
+}
+
+ComposeObj *
+initLayoutDialog(WINDOW *win, Layout *layout, int x, int y, int *max)
+{
+ ComposeObj *obj = NULL, *first;
+ int n;
+
+ /* Loop over the layout list, create the objects, and add them
+ onto the chain of objects that dialog uses for traversal*/
+
+ n = 0;
+ while (layout[n].help != NULL) {
+ int t = TYPE_OF_OBJ(layout[n].type);
+
+ switch (t) {
+ case STRINGOBJ:
+ layout[n].obj = NewStringObj(win, layout[n].prompt, layout[n].var,
+ layout[n].y + y, layout[n].x + x, layout[n].len, layout[n].maxlen);
+ ((StringObj *)layout[n].obj)->attr_mask = ATTR_OF_OBJ(layout[n].type);
+ break;
+
+ case BUTTONOBJ:
+ layout[n].obj = NewButtonObj(win, layout[n].prompt, layout[n].var, layout[n].y + y, layout[n].x + x);
+ break;
+
+ default:
+ msgFatal("Don't support this object yet!");
+ }
+ AddObj(&obj, t, (void *) layout[n].obj);
+ n++;
+ }
+ *max = n - 1;
+ /* Find the first object in the list */
+ for (first = obj; first->prev; first = first->prev);
+ return first;
+}
+
+int
+layoutDialogLoop(WINDOW *win, Layout *layout, ComposeObj **obj, int *n, int max, int *cbutton, int *cancel)
+{
+ char help_line[80];
+ int ret, i, len = strlen(layout[*n].help);
+
+ /* Display the help line at the bottom of the screen */
+ for (i = 0; i < 79; i++)
+ help_line[i] = (i < len) ? layout[*n].help[i] : ' ';
+ help_line[i] = '\0';
+ use_helpline(help_line);
+ display_helpline(win, LINES - 1, COLS - 1);
+ wrefresh(win);
+
+ /* Ask for libdialog to do its stuff */
+ ret = PollObj(obj);
+ /* Handle special case stuff that libdialog misses. Sigh */
+ switch (ret) {
+ case SEL_ESC: /* Bail out */
+ *cancel = TRUE;
+ return FALSE;
+
+ /* This doesn't work for list dialogs. Oh well. Perhaps
+ should special case the move from the OK button ``up''
+ to make it go to the interface list, but then it gets
+ awkward for the user to go back and correct screw up's
+ in the per-interface section */
+ case KEY_DOWN:
+ case SEL_CR:
+ case SEL_TAB:
+ if (*n < max)
+ ++*n;
+ else
+ *n = 0;
+ break;
+
+ /* The user has pressed enter over a button object */
+ case SEL_BUTTON:
+ if (cbutton && *cbutton)
+ *cancel = TRUE;
+ else
+ *cancel = FALSE;
+ return FALSE;
+
+ case KEY_UP:
+ case SEL_BACKTAB:
+ if (*n)
+ --*n;
+ else
+ *n = max;
+ break;
+
+ case KEY_F(1):
+ display_helpfile();
+
+ /* They tried some key combination we don't support - tootle them forcefully! */
+ default:
+ beep();
+ }
+ return TRUE;
+}
+
+WINDOW *
+savescr(void)
+{
+ WINDOW *w;
+
+ w = dupwin(newscr);
+ return w;
+}
+
+void
+restorescr(WINDOW *w)
+{
+ touchwin(w);
+ wrefresh(w);
+ delwin(w);
+}
+
+/*
+ * Get a sysctl variable as a string or "<unknown>" if sysctl fails.
+ * Caller must free returned string.
+ */
+char *
+getsysctlbyname(const char *sysctlname)
+{
+ char *buf;
+ size_t sz, buf_sz = 0;
+ const char unk_str[] = "<unknown>";
+
+ sysctlbyname(sysctlname, NULL, &buf_sz, NULL, 0);
+ buf_sz = MAX(sizeof(unk_str), buf_sz) + 1;
+ sz = buf_sz - 1;
+ buf = (char *)safe_malloc(buf_sz);
+
+ if (sysctlbyname(sysctlname, buf, &sz, NULL, 0) != -1)
+ buf[sz] = '\0';
+ else
+ strlcpy(buf, unk_str, buf_sz);
+
+ return buf;
+}
diff --git a/usr.sbin/sysinstall/modules.c b/usr.sbin/sysinstall/modules.c
new file mode 100644
index 0000000..211c51e
--- /dev/null
+++ b/usr.sbin/sysinstall/modules.c
@@ -0,0 +1,224 @@
+/*-
+ * Copyright (c) 2000 "HOSOKAWA, Tatsumi" <hosokawa@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 "sysinstall.h"
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/linker.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <fnmatch.h>
+#include <kenv.h>
+
+/* Prototypes */
+static int kldModuleFire(dialogMenuItem *self);
+
+#define MODULESDIR "/modules"
+#define DISTMOUNT "/dist"
+
+void
+moduleInitialize(void)
+{
+ int fd, len;
+ DIR *dirp;
+ struct dirent *dp;
+ char module[MAXPATHLEN], desc[MAXPATHLEN];
+ char desc_str[BUFSIZ];
+
+ if (!RunningAsInit && !Fake) {
+ /* It's not my job... */
+ return;
+ }
+
+ dirp = opendir(MODULESDIR);
+ if (dirp) {
+ while ((dp = readdir(dirp))) {
+ if (dp->d_namlen < (sizeof(".ko") - 1)) continue;
+ if (strcmp(dp->d_name + dp->d_namlen - (sizeof(".ko") - 1), ".ko") == 0) {
+ strcpy(module, MODULESDIR);
+ strcat(module, "/");
+ strcat(module, dp->d_name);
+ strcpy(desc, module);
+ len = strlen(desc);
+ strcpy(desc + (len - (sizeof(".ko") - 1)), ".dsc");
+ fd = open(module, O_RDONLY);
+ if (fd < 0) continue;
+ close(fd);
+ fd = open(desc, O_RDONLY);
+ if (fd < 0) {
+ desc_str[0] = 0;
+ }
+ else {
+ len = read(fd, desc_str, BUFSIZ);
+ close(fd);
+ if (len < BUFSIZ) desc_str[len] = 0;
+ }
+ if (desc_str[0])
+ msgDebug("Loading module %s (%s)\n", dp->d_name, desc_str);
+ else
+ msgDebug("Loading module %s\n", dp->d_name);
+ if (kldload(module) < 0 && errno != EEXIST) {
+ if (desc_str[0])
+ msgConfirm("Loading module %s failed\n%s", dp->d_name, desc_str);
+ else
+ msgConfirm("Loading module %s failed", dp->d_name);
+ }
+ }
+ if (strcmp(dp->d_name + dp->d_namlen - (sizeof(".ko.gz") - 1), ".ko.gz") == 0) {
+ snprintf(module, sizeof(module), "/tmp/%s", dp->d_name);
+ module[strlen(module) - sizeof(".gz")] = '\0';
+ snprintf(desc, sizeof(desc), "zcat < %s/%s > %s", MODULESDIR,
+ dp->d_name, module);
+ system(desc);
+ if (kldload(module) < 0 && errno != EEXIST) {
+ if (desc_str[0])
+ msgConfirm("Loading module %s failed\n%s", dp->d_name, desc_str);
+ else
+ msgConfirm("Loading module %s failed", dp->d_name);
+ }
+ unlink(module);
+ }
+ }
+ closedir(dirp);
+ }
+}
+
+void
+driverFloppyCheck(void)
+{
+ /* Prompt for the driver floppy if requested. */
+ if (kenv(KENV_GET, "driver_floppy", NULL, 0) >= 0 &&
+ !msgYesNo("Would you like to load kernel modules from the driver floppy?"))
+ (void)kldBrowser(NULL);
+}
+
+int
+kldBrowser(dialogMenuItem *self)
+{
+ DMenu *menu;
+ int i, what = DITEM_SUCCESS, msize, count;
+ DIR *dir;
+ struct dirent *de;
+ char *err;
+
+ err = NULL;
+
+ if (DITEM_STATUS(mediaSetFloppy(NULL)) == DITEM_FAILURE) {
+ err = "Unable to set media device to floppy.";
+ goto errout;
+ }
+
+ if (!DEVICE_INIT(mediaDevice)) {
+ err = "Unable to mount floppy filesystem.";
+ goto errout;
+ }
+
+ msize = sizeof(DMenu) + (sizeof(dialogMenuItem) * 2);
+ count = 0;
+ if ((menu = malloc(msize)) == NULL) {
+ err = "Failed to allocate memory for menu";
+ goto errout;
+ }
+
+ bcopy(&MenuKLD, menu, sizeof(DMenu));
+
+ bzero(&menu->items[count], sizeof(menu->items[0]));
+ menu->items[count].prompt = strdup("X Exit");
+ menu->items[count].title = strdup("Exit this menu (returning to previous)");
+ menu->items[count].fire = dmenuExit;
+ count++;
+
+ if ((dir = opendir(DISTMOUNT)) == NULL) {
+ err = "Couldn't open directory";
+ goto errout;
+ }
+
+ while ((de = readdir(dir)) != NULL) {
+ if (fnmatch("*.ko", de->d_name, FNM_CASEFOLD))
+ continue;
+
+ msize += sizeof(dialogMenuItem);
+ if ((menu = realloc(menu, msize)) == NULL) {
+ err = "Failed to allocate memory for menu item";
+ goto errout;
+ }
+
+ bzero(&menu->items[count], sizeof(menu->items[0]));
+ menu->items[count].fire = kldModuleFire;
+
+ menu->items[count].prompt = strdup(de->d_name);
+ menu->items[count].title = menu->items[count].prompt;
+
+ count++;
+ }
+
+ closedir(dir);
+
+ menu->items[count].prompt = NULL;
+ menu->items[count].title = NULL;
+
+ dmenuOpenSimple(menu, FALSE);
+
+ deviceRescan();
+
+ errout:
+ mediaClose();
+ for (i = 0; i < count; i++)
+ free(menu->items[i].prompt);
+
+ free(menu);
+
+ if (err != NULL) {
+ what |= DITEM_FAILURE;
+ if (!variable_get(VAR_NO_ERROR))
+ msgConfirm(err);
+ }
+
+ return (what);
+}
+
+static int
+kldModuleFire(dialogMenuItem *self) {
+ char fname[256];
+
+ bzero(fname, sizeof(fname));
+ snprintf(fname, sizeof(fname), "%s/%s", DISTMOUNT, self->prompt);
+
+ if (kldload(fname) < 0 && errno != EEXIST) {
+ if (!variable_get(VAR_NO_ERROR))
+ msgConfirm("Loading module %s failed\n", fname);
+ } else {
+ if (!variable_get(VAR_NO_ERROR))
+ msgConfirm("Loaded module %s OK", fname);
+ }
+
+ return(0);
+ }
diff --git a/usr.sbin/sysinstall/mouse.c b/usr.sbin/sysinstall/mouse.c
new file mode 100644
index 0000000..574fd72
--- /dev/null
+++ b/usr.sbin/sysinstall/mouse.c
@@ -0,0 +1,103 @@
+/*-
+ * Copyright (c) 1998 Kazutaka YOKOTA (yokota@zodiac.mech.utsunomiya-u.ac.jp)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 AUTHRO AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHRO OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sysinstall.h"
+#include <string.h>
+
+int
+mousedTest(dialogMenuItem *self)
+{
+ char *type;
+ char *port;
+ char *flags;
+ int ret;
+
+ type = variable_get(VAR_MOUSED_TYPE);
+ port = variable_get(VAR_MOUSED_PORT);
+ flags = variable_get(VAR_MOUSED_FLAGS);
+ if ((type == NULL) || (port == NULL)
+ || (strlen(type) <= 0) || (strlen(port) <= 0)
+ || (strcmp(type, "NO") == 0)) {
+ msgConfirm("Please select a mouse protocol and a port first.");
+ return DITEM_FAILURE;
+ }
+
+ msgNotify("Trying to start the mouse daemon...");
+ if (file_readable("/var/run/moused.pid"))
+ vsystem("kill `cat /var/run/moused.pid`");
+ systemExecute("vidcontrol -m on");
+ if (flags != NULL)
+ vsystem("moused -t %s -p %s %s", type, port, flags);
+ else
+ vsystem("moused -t %s -p %s", type, port);
+
+ ret = msgYesNo("Now move the mouse and see if it works.\n"
+ "(Note that buttons don't have any effect for now.)\n\n"
+ " Is the mouse cursor moving?\n");
+ systemExecute("vidcontrol -m off");
+ if (ret) {
+ if (file_readable("/var/run/moused.pid"))
+ vsystem("kill `cat /var/run/moused.pid`");
+ variable_set2(VAR_MOUSED, "NO", 1);
+ } else {
+ variable_set2(VAR_MOUSED, "YES", 1);
+ vsystem("ln -fs /dev/sysmouse /dev/mouse"); /* backwards compat */
+ }
+
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+int
+mousedDisable(dialogMenuItem *self)
+{
+ if (file_readable("/var/run/moused.pid"))
+ vsystem("kill `cat /var/run/moused.pid`");
+ variable_set2(VAR_MOUSED, "NO", 1);
+ variable_set2(VAR_MOUSED_TYPE, "NO", 1);
+ variable_unset(VAR_MOUSED_PORT);
+ variable_unset(VAR_MOUSED_FLAGS);
+ msgConfirm("The mouse daemon is disabled.");
+ return DITEM_SUCCESS;
+}
+
+int
+setMouseFlags(dialogMenuItem *self)
+{
+ int ret;
+ ret = variable_get_value(VAR_MOUSED_FLAGS,
+ "Please Specify the mouse daemon flags. If you would like to\n"
+ "emulate 3 buttons, use -3 here.\n", 1)
+ ? DITEM_SUCCESS : DITEM_FAILURE;
+ if (ret != DITEM_SUCCESS)
+ variable_unset(VAR_MOUSED_FLAGS);
+ return ret;
+}
+
diff --git a/usr.sbin/sysinstall/msg.c b/usr.sbin/sysinstall/msg.c
new file mode 100644
index 0000000..4625ce2
--- /dev/null
+++ b/usr.sbin/sysinstall/msg.c
@@ -0,0 +1,356 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sysinstall.h"
+#include <stdarg.h>
+#include <sys/ioctl.h>
+#include <sys/consio.h>
+
+Boolean
+isDebug(void)
+{
+ char *cp;
+
+ return (cp = variable_get(VAR_DEBUG)) && strcmp(cp, "no");
+}
+
+/* Whack up an informational message on the status line, in stand-out */
+void
+msgYap(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int attrs;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ attrs = getattrs(stdscr);
+ attrset(A_REVERSE);
+ mvaddstr(StatusLine, 0, errstr);
+ attrset(attrs);
+ refresh();
+}
+
+/* Whack up an informational message on the status line */
+void
+msgInfo(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int i, attrs;
+ char line[81];
+
+ attrs = getattrs(stdscr);
+ /* NULL is a special convention meaning "erase the old stuff" */
+ if (!fmt) {
+ move(StatusLine, 0);
+ clrtoeol();
+ return;
+ }
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ memset(line, ' ', 80);
+ for (i = 0; i < 80; i++) {
+ if (errstr[i])
+ line[i] = errstr[i];
+ else
+ break;
+ }
+ line[80] = '\0';
+ attrset(ATTR_TITLE);
+ mvaddstr(StatusLine, 0, line);
+ attrset(attrs);
+ move(StatusLine, 79);
+ refresh();
+}
+
+/* Whack up a warning on the status line */
+void
+msgWarn(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int attrs;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ strcpy(errstr, "Warning: ");
+ va_start(args, fmt);
+ vsnprintf((char *)(errstr + strlen(errstr)), FILENAME_MAX, fmt, args);
+ va_end(args);
+ attrs = getattrs(stdscr);
+ beep();
+ attrset(ATTR_TITLE);
+ mvaddstr(StatusLine, 0, errstr);
+ attrset(attrs);
+ refresh();
+ if (OnVTY && isDebug())
+ msgDebug("Warning message `%s'\n", errstr);
+}
+
+/* Whack up an error on the status line */
+void
+msgError(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int attrs;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ strcpy(errstr, "Error: ");
+ va_start(args, fmt);
+ vsnprintf((char *)(errstr + strlen(errstr)), FILENAME_MAX, fmt, args);
+ va_end(args);
+ beep();
+ attrs = getattrs(stdscr);
+ attrset(ATTR_TITLE);
+ mvaddstr(StatusLine, 0, errstr);
+ attrset(attrs);
+ refresh();
+ if (OnVTY && isDebug())
+ msgDebug("Error message `%s'\n", errstr);
+}
+
+/* Whack up a fatal error on the status line */
+void
+msgFatal(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int attrs;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ strcpy(errstr, "Fatal Error: ");
+ va_start(args, fmt);
+ vsnprintf((char *)(errstr + strlen(errstr)), FILENAME_MAX, fmt, args);
+ va_end(args);
+ beep();
+ attrs = getattrs(stdscr);
+ attrset(ATTR_TITLE);
+ mvaddstr(StatusLine, 0, errstr);
+ addstr(" - ");
+ addstr("PRESS ANY KEY TO ");
+ if (getpid() == 1)
+ addstr("REBOOT");
+ else
+ addstr("QUIT");
+ attrset(attrs);
+ refresh();
+ if (OnVTY)
+ msgDebug("Fatal error `%s'!\n", errstr);
+ getch();
+ systemShutdown(1);
+}
+
+/* Put up a message in a popup confirmation box */
+void
+msgConfirm(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ WINDOW *w = savescr();
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (OnVTY) {
+ ioctl(0, VT_ACTIVATE, 1);
+ msgInfo(NULL);
+ }
+ dialog_notify(errstr);
+ restorescr(w);
+}
+
+/* Put up a message in a popup information box */
+void
+msgNotify(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (isDebug())
+ msgDebug("Notify: %s\n", errstr);
+ dialog_msgbox(NULL, errstr, -1, -1, 0);
+}
+
+/* Put up a message in a popup yes/no box and return 0 for YES, 1 for NO */
+int
+msgYesNo(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int ret;
+ WINDOW *w = savescr();
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (OnVTY) {
+ ioctl(0, VT_ACTIVATE, 1); /* Switch back */
+ msgInfo(NULL);
+ }
+ if (variable_get(VAR_NONINTERACTIVE))
+ return 0; /* If non-interactive, return YES all the time */
+ ret = dialog_yesno("User Confirmation Requested", errstr, -1, -1);
+ restorescr(w);
+ return ret;
+}
+
+/* Put up a message in a popup no/yes box and return 0 for YES, 1 for NO */
+int
+msgNoYes(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int ret;
+ WINDOW *w = savescr();
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (OnVTY) {
+ ioctl(0, VT_ACTIVATE, 1); /* Switch back */
+ msgInfo(NULL);
+ }
+ if (variable_get(VAR_NONINTERACTIVE))
+ return 1; /* If non-interactive, return NO all the time */
+ ret = dialog_noyes("User Confirmation Requested", errstr, -1, -1);
+ restorescr(w);
+ return ret;
+}
+
+/* Put up a message in an input box and return the value */
+char *
+msgGetInput(char *buf, char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ static char input_buffer[256];
+ int rval;
+ WINDOW *w = savescr();
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (buf)
+ SAFE_STRCPY(input_buffer, buf);
+ else
+ input_buffer[0] = '\0';
+ if (OnVTY) {
+ ioctl(0, VT_ACTIVATE, 1); /* Switch back */
+ msgInfo(NULL);
+ }
+ rval = dialog_inputbox("Value Required", errstr, -1, -1, input_buffer);
+ restorescr(w);
+ if (!rval)
+ return input_buffer;
+ else
+ return NULL;
+}
+
+/* Write something to the debugging port */
+void
+msgDebug(char *fmt, ...)
+{
+ va_list args;
+ char *dbg;
+
+ if (DebugFD == -1)
+ return;
+ dbg = (char *)alloca(FILENAME_MAX);
+ strcpy(dbg, "DEBUG: ");
+ va_start(args, fmt);
+ vsnprintf((char *)(dbg + strlen(dbg)), FILENAME_MAX, fmt, args);
+ va_end(args);
+ write(DebugFD, dbg, strlen(dbg));
+}
+
+/* Tell the user there's some output to go look at */
+void
+msgWeHaveOutput(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ WINDOW *w = savescr();
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ msgDebug("Notify: %s\n", errstr);
+ dialog_clear_norefresh();
+ sleep(2);
+ dialog_msgbox(NULL, errstr, -1, -1, 0);
+ restorescr(w);
+}
+
+/* Simple versions of msgConfirm() and msgNotify() for calling from scripts */
+int
+msgSimpleConfirm(char *str)
+{
+ msgConfirm("%s", str);
+ return DITEM_SUCCESS;
+}
+
+int
+msgSimpleNotify(char *str)
+{
+ msgNotify("%s", str);
+ return DITEM_SUCCESS;
+}
diff --git a/usr.sbin/sysinstall/network.c b/usr.sbin/sysinstall/network.c
new file mode 100644
index 0000000..20a0430
--- /dev/null
+++ b/usr.sbin/sysinstall/network.c
@@ -0,0 +1,361 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/* These routines deal with getting things off of network media */
+
+#include "sysinstall.h"
+#include <signal.h>
+#include <termios.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+static Boolean networkInitialized;
+static pid_t startPPP(Device *devp);
+
+static pid_t pppPID;
+
+Boolean
+mediaInitNetwork(Device *dev)
+{
+ int i;
+ char *rp;
+ char *cp, ifconfig[255];
+ WINDOW *w;
+
+ if (!RunningAsInit || networkInitialized)
+ return TRUE;
+
+ if (isDebug())
+ msgDebug("Init routine called for network device %s.\n", dev->name);
+
+ if (!file_readable("/etc/resolv.conf")) {
+ if (DITEM_STATUS(configResolv(NULL)) == DITEM_FAILURE) {
+ msgConfirm("Can't seem to write out /etc/resolv.conf. Net cannot be used.");
+ return FALSE;
+ }
+ }
+
+ w = savescr();
+ dialog_clear_norefresh();
+
+ /* Old PPP process lying around? */
+ if (pppPID) {
+ msgConfirm("Killing previous PPP process %d.", pppPID);
+ kill(pppPID, SIGTERM);
+ pppPID = 0;
+ }
+ if (!strncmp("ppp", dev->name, 3)) { /* PPP? */
+ if (!(pppPID = startPPP(dev))) {
+ msgConfirm("Unable to start PPP! This installation method cannot be used.");
+ return FALSE;
+ }
+ networkInitialized = TRUE;
+ return TRUE;
+ }
+ else if (!strncmp("sl", dev->name, 2)) { /* SLIP? */
+ char *val;
+ char attach[256];
+
+ /* Cheesy slip attach */
+ snprintf(attach, 256, "slattach -a -h -l -s 9600 %s", dev->devname);
+ val = msgGetInput(attach,
+ "Warning: SLIP is rather poorly supported in this revision\n"
+ "of the installation due to the lack of a dialing utility.\n"
+ "If you can use PPP for this instead then you're much better\n"
+ "off doing so, otherwise SLIP works fairly well for *hardwired*\n"
+ "links. Please edit the following slattach command for\n"
+ "correctness (default here is: VJ compression, Hardware flow-\n"
+ "control, ignore carrier and 9600 baud data rate). When you're\n"
+ "ready, press [ENTER] to execute it.");
+ if (!val) {
+ msgConfirm("slattach command was empty. Try again!");
+ restorescr(w);
+ return FALSE;
+ }
+ else
+ SAFE_STRCPY(attach, val);
+ /*
+ * Doing this with vsystem() is actually bogus since we should be storing the pid of slattach
+ * for later killing. It's just too convenient to call vsystem(), however, rather than
+ * constructing a proper argument for exec() so we punt on doing slip right for now.
+ */
+ if (vsystem("%s", attach)) {
+ msgConfirm("slattach returned a bad status! Please verify that\n"
+ "the command is correct and try this operation again.");
+ restorescr(w);
+ return FALSE;
+ }
+ restorescr(w);
+ }
+
+ snprintf(ifconfig, 255, "%s%s", VAR_IFCONFIG, dev->name);
+ cp = variable_get(ifconfig);
+ if (cp) {
+ /*
+ * If this interface isn't a DHCP one, bring it up.
+ * If it is, then it's already up.
+ */
+ if (strstr(cp, "DHCP") == NULL) {
+ msgDebug("Not a DHCP interface.\n");
+ i = vsystem("ifconfig %s %s", dev->name, cp);
+ if (i) {
+ msgConfirm("Unable to configure the %s interface!\n"
+ "This installation method cannot be used.",
+ dev->name);
+ return FALSE;
+ }
+ rp = variable_get(VAR_GATEWAY);
+ if (!rp || *rp == '0') {
+ msgConfirm("No gateway has been set. You may be unable to access hosts\n"
+ "not on your local network");
+ }
+ else {
+ msgDebug("Adding default route to %s.\n", rp);
+ vsystem("route -n add default %s", rp);
+ }
+ } else {
+ msgDebug("A DHCP interface. Should already be up.\n");
+ }
+ } else if ((cp = variable_get(VAR_IPV6ADDR)) == NULL || *cp == '\0') {
+ msgConfirm("The %s device is not configured. You will need to do so\n"
+ "in the Networking configuration menu before proceeding.", dev->name);
+ return FALSE;
+ }
+
+ if (isDebug())
+ msgDebug("Network initialized successfully.\n");
+ networkInitialized = TRUE;
+ return TRUE;
+}
+
+void
+mediaShutdownNetwork(Device *dev)
+{
+ char *cp;
+
+ if (!RunningAsInit || !networkInitialized)
+ return;
+
+ msgDebug("Shutdown called for network device %s\n", dev->name);
+ /* Not a serial device? */
+ if (strncmp("sl", dev->name, 2) && strncmp("ppp", dev->name, 3)) {
+ int i;
+ char ifconfig[255];
+
+ snprintf(ifconfig, 255, "%s%s", VAR_IFCONFIG, dev->name);
+ cp = variable_get(ifconfig);
+ if (!cp)
+ return;
+ msgDebug("ifconfig %s down\n", dev->name);
+ i = vsystem("ifconfig %s down", dev->name);
+ if (i)
+ msgConfirm("Warning: Unable to down the %s interface properly", dev->name);
+ cp = variable_get(VAR_GATEWAY);
+ if (cp) {
+ msgDebug("Deleting default route.\n");
+ vsystem("route -n delete default");
+ }
+ }
+ else if (pppPID) {
+ msgConfirm("Killing previous PPP process %d.", pppPID);
+ kill(pppPID, SIGTERM);
+ pppPID = 0;
+ }
+ networkInitialized = FALSE;
+}
+
+/* Start PPP on the 3rd screen */
+static pid_t
+startPPP(Device *devp)
+{
+ int fd2, pulse;
+ FILE *fp;
+ char *val;
+ pid_t pid = 0;
+ char myaddr[16], provider[16], speed[16], authname[32], authkey[16];
+ char phone[16];
+ WINDOW *w = savescr();
+
+ /* These are needed to make ppp work */
+ Mkdir("/var/log");
+ Mkdir("/var/run");
+ Mkdir("/var/spool/lock");
+ Mkdir("/etc/ppp");
+
+ dialog_clear_norefresh();
+ if (!variable_get(VAR_SERIAL_SPEED))
+ variable_set2(VAR_SERIAL_SPEED, "115200", 0);
+ /* Get any important user values */
+ val = variable_get_value(VAR_SERIAL_SPEED,
+ "Enter the baud rate for your modem - this can be higher than the actual\n"
+ "maximum data rate since most modems can talk at one speed to the\n"
+ "computer and at another speed to the remote end.\n\n"
+ "If you're not sure what to put here, just select the default.", 0);
+ SAFE_STRCPY(speed, (val && *val) ? val : "115200");
+
+ val = variable_get(VAR_GATEWAY);
+ SAFE_STRCPY(provider, (val && *val) ? val : "0");
+
+ dialog_clear_norefresh();
+ val = msgGetInput(provider, "Enter the IP address of your service provider or 0 if you\n"
+ "don't know it and would prefer to negotiate it dynamically.");
+ SAFE_STRCPY(provider, (val && *val) ? val : "0");
+
+ if (devp->private && ((DevInfo *)devp->private)->ipaddr[0])
+ SAFE_STRCPY(myaddr, ((DevInfo *)devp->private)->ipaddr);
+ else
+ strcpy(myaddr, "0");
+
+ if (!Fake)
+ fp = fopen("/etc/ppp/ppp.linkup", "w");
+ else
+ fp = fopen("/dev/stderr", "w");
+ if (fp != NULL) {
+ fprintf(fp, "MYADDR:\n");
+ fprintf(fp, " delete ALL\n");
+ fprintf(fp, " add 0 0 HISADDR\n");
+ fchmod(fileno(fp), 0755);
+ fclose(fp);
+ }
+ if (!Fake)
+ fd2 = open("/etc/ppp/ppp.secret", O_CREAT);
+ else
+ fd2 = -1;
+ if (fd2 != -1) {
+ fchmod(fd2, 0700);
+ close(fd2);
+ }
+ if (!Fake)
+ fp = fopen("/etc/ppp/ppp.conf", "a");
+ else
+ fp = fopen("/dev/stderr", "w");
+ if (!fp) {
+ msgConfirm("Couldn't open /etc/ppp/ppp.conf file! This isn't going to work");
+ restorescr(w);
+ return 0;
+ }
+ authname[0] = '\0';
+ pulse = 0;
+ dialog_clear_norefresh();
+ if (!dialog_yesno("", "Does your ISP support PAP or CHAP ppp logins?", -1, -1)) {
+ val = msgGetInput(NULL, "Enter the name you use to login to your provider.");
+ SAFE_STRCPY(authname, val);
+ dialog_clear_norefresh();
+ val = msgGetInput(NULL, "Enter the password you use to login to your provider.");
+ SAFE_STRCPY(authkey, val);
+ dialog_clear_norefresh();
+ val = msgGetInput(NULL, "Enter the your provider's login phone number.");
+ SAFE_STRCPY(phone, val);
+ dialog_clear_norefresh();
+ pulse = dialog_yesno("", "Does your telephone line support tone dialing?", -1, -1);
+ }
+ fprintf(fp, "\ninstall:\n");
+ fprintf(fp, " set speed %s\n", speed);
+ fprintf(fp, " set device %s\n", devp->devname);
+ fprintf(fp, " set ifaddr %s %s 255.255.255.0 0.0.0.0\n", myaddr, provider);
+ fprintf(fp, " add! default HISADDR\n");
+ fprintf(fp, " set timeout 0\n");
+ fprintf(fp, " enable dns\n");
+ fprintf(fp, " set log local phase\n");
+ if(authname[0] != '\0'){
+ fprintf(fp, " set dial \"ABORT BUSY ABORT NO\\\\sCARRIER TIMEOUT 5 \\\"\\\" AT OK-AT-OK ATE1Q0 OK \\\\dATD%c\\\\T TIMEOUT 40 CONNECT\"\n", pulse ? 'P' : 'T');
+ fprintf(fp, " set login\n");
+ fprintf(fp, " set authname %s\n", authname);
+ fprintf(fp, " set authkey %s\n", authkey);
+ fprintf(fp, " set phone %s\n", phone);
+ }
+ if (fchmod(fileno(fp), 0600) != 0)
+ msgConfirm("Warning: Failed to fix permissions on /etc/ppp/ppp.conf !");
+ fclose(fp);
+
+ /* Make the ppp config persistent */
+ variable_set2(VAR_PPP_ENABLE, "YES", 0);
+ variable_set2(VAR_PPP_PROFILE, "install", 0);
+
+ if (!Fake && !file_readable("/dev/tun0")) {
+ msgConfirm("Warning: No /dev/tun0 device. PPP will not work!");
+ restorescr(w);
+ return 0;
+ }
+
+ if (isDebug())
+ msgDebug("About to start PPP on device %s @ %s baud. Provider = %s\n", devp->devname, speed, provider);
+
+ if (!Fake && !(pid = fork())) {
+ int i, fd;
+ struct termios foo;
+ extern int login_tty(int);
+
+ for (i = getdtablesize(); i >= 0; i--)
+ close(i);
+
+ /* We're going over to VTY2 */
+ fd = open("/dev/ttyv2", O_RDWR);
+ ioctl(0, TIOCSCTTY, &fd);
+ dup2(0, 1);
+ dup2(0, 2);
+ DebugFD = 2;
+ if (login_tty(fd) == -1)
+ msgDebug("ppp: Can't set the controlling terminal.\n");
+ signal(SIGTTOU, SIG_IGN);
+ if (tcgetattr(fd, &foo) != -1) {
+ foo.c_cc[VERASE] = '\010';
+ if (tcsetattr(fd, TCSANOW, &foo) == -1)
+ msgDebug("ppp: Unable to set the erase character.\n");
+ }
+ else
+ msgDebug("ppp: Unable to get the terminal attributes!\n");
+ execlp("ppp", "ppp", "install", (char *)NULL);
+ msgDebug("PPP process failed to exec!\n");
+ exit(1);
+ }
+ else {
+ dialog_clear_norefresh();
+ msgConfirm("NOTICE: The PPP command is now started on VTY3 (type ALT-F3 to\n"
+ "interact with it, ALT-F1 to switch back here). If you are using\n"
+ "a PAP or CHAP login simply enter \"dial\", otherwise you'll need\n"
+ "to use the \"term\" command which starts a terminal emulator\n"
+ "which you can use to talk to your modem and dial the service\n"
+ "provider. Once you're connected, come back to this screen and\n"
+ "press return.\n\n"
+ "DO NOT PRESS [ENTER] HERE UNTIL THE CONNECTION IS FULLY\n"
+ "ESTABLISHED!");
+ }
+ restorescr(w);
+ return pid;
+}
diff --git a/usr.sbin/sysinstall/nfs.c b/usr.sbin/sysinstall/nfs.c
new file mode 100644
index 0000000..87788ea
--- /dev/null
+++ b/usr.sbin/sysinstall/nfs.c
@@ -0,0 +1,101 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sysinstall.h"
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+
+#include <limits.h>
+
+Boolean NFSMounted;
+static char mountpoint[] = "/dist";
+
+Boolean
+mediaInitNFS(Device *dev)
+{
+ Device *netDevice = (Device *)dev->private;
+ WINDOW *w = savescr();
+
+ if (NFSMounted)
+ return TRUE;
+
+ if (!DEVICE_INIT(netDevice))
+ return FALSE;
+
+ if (Mkdir(mountpoint))
+ return FALSE;
+
+ msgNotify("Mounting %s over NFS on %s", dev->name, mountpoint);
+ if (vsystem("mount_nfs %s %s %s %s %s %s",
+ !variable_cmp(VAR_NFS_TCP, "YES") ? "-T" : "",
+ !variable_cmp(VAR_NFS_V3, "YES") ? "-3" : "",
+ !variable_cmp(VAR_SLOW_ETHER, "YES") ?
+ "-r 1024 -w 1024" : "-r 4096 -w 4096",
+ !variable_cmp(VAR_NFS_SECURE, "YES") ? "-P" : "",
+ dev->name, mountpoint)) {
+ msgConfirm("Error mounting %s on %s: %s.", dev->name, mountpoint, strerror(errno));
+ if (netDevice)
+ DEVICE_SHUTDOWN(netDevice);
+ restorescr(w);
+ return FALSE;
+ }
+ NFSMounted = TRUE;
+ if (isDebug())
+ msgDebug("Mounted NFS device %s onto %s\n", dev->name, mountpoint);
+ restorescr(w);
+ return TRUE;
+}
+
+FILE *
+mediaGetNFS(Device *dev, char *file, Boolean probe)
+{
+ return mediaGenericGet(mountpoint, file);
+}
+
+void
+mediaShutdownNFS(Device *dev)
+{
+ if (!NFSMounted)
+ return;
+
+ msgDebug("Unmounting NFS partition on %s\n", mountpoint);
+ if (unmount(mountpoint, MNT_FORCE) != 0)
+ msgConfirm("Could not unmount the NFS partition: %s", strerror(errno));
+ NFSMounted = FALSE;
+ return;
+}
diff --git a/usr.sbin/sysinstall/options.c b/usr.sbin/sysinstall/options.c
new file mode 100644
index 0000000..714ff4d
--- /dev/null
+++ b/usr.sbin/sysinstall/options.c
@@ -0,0 +1,333 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated for what's essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sysinstall.h"
+#include <ctype.h>
+#include <curses.h>
+#include <term.h>
+
+int fixitTtyWhich(dialogMenuItem *);
+
+static char *
+varCheck(Option *opt)
+{
+ char *cp = NULL;
+
+ if (opt->aux)
+ cp = variable_get((char *)opt->aux);
+ if (!cp)
+ return "NO";
+ return cp;
+}
+
+/* Show our little logo */
+static char *
+resetLogo(Option *opt)
+{
+ return "[RESET!]";
+}
+
+static char *
+mediaCheck(Option *opt)
+{
+ if (mediaDevice) {
+ switch(mediaDevice->type) {
+ case DEVICE_TYPE_UFS:
+ case DEVICE_TYPE_DISK:
+ return "File system";
+
+ case DEVICE_TYPE_FLOPPY:
+ return "Floppy";
+
+ case DEVICE_TYPE_FTP:
+ return "FTP";
+
+ case DEVICE_TYPE_CDROM:
+ return "CDROM";
+
+ case DEVICE_TYPE_DOS:
+ return "DOS";
+
+ case DEVICE_TYPE_NFS:
+ return "NFS";
+
+ case DEVICE_TYPE_NONE:
+ case DEVICE_TYPE_NETWORK:
+ case DEVICE_TYPE_ANY:
+ default:
+ return "<unknown>";
+ }
+ }
+ return "<not yet set>";
+}
+
+#define NEWFS_PROMPT "Please enter newfs(8) parameters:"
+#define RELNAME_PROMPT "Please specify the release you wish to load or\n\"any\" for a generic release install:"
+#define BPKG_PROMPT "Please specify the name of the HTML browser package:"
+#define BBIN_PROMPT "Please specify a full pathname to the HTML browser binary:"
+#define EDITOR_PROMPT "Please specify the name of the text editor you wish to use:"
+#define PKG_PROMPT "Please specify a temporary directory with lots of free space:"
+#define INSTROOT_PROMPT "Please specify a root directory if installing somewhere other than /"
+#define TIMEOUT_PROMPT "Please specify the number of seconds to wait for slow media:"
+
+static Option Options[] = {
+{ "NFS Secure", "NFS server talks only on a secure port",
+ OPT_IS_VAR, NULL, VAR_NFS_SECURE, varCheck },
+{ "NFS Slow", "User is using a slow PC or ethernet card",
+ OPT_IS_VAR, NULL, VAR_SLOW_ETHER, varCheck },
+{ "NFS TCP", "Use TCP protocol for NFS",
+ OPT_IS_VAR, NULL, VAR_NFS_TCP, varCheck },
+{ "NFS version 3", "Use NFS version 3",
+ OPT_IS_VAR, NULL, VAR_NFS_V3, varCheck },
+{ "Debugging", "Emit extra debugging output on VTY2 (ALT-F2)",
+ OPT_IS_VAR, NULL, VAR_DEBUG, varCheck },
+{ "No Warnings", "Don't Warn the user when a setting seems incorrect",
+ OPT_IS_VAR, NULL, VAR_NO_WARN, varCheck },
+{ "Yes to All", "Assume \"Yes\" answers to all non-critical dialogs",
+ OPT_IS_VAR, NULL, VAR_NO_CONFIRM, varCheck },
+{ "DHCP", "Attempt automatic DHCP configuration of interfaces",
+ OPT_IS_VAR, NULL, VAR_TRY_DHCP, varCheck },
+{ "IPv6", "Attempt IPv6 configuration of interfaces",
+ OPT_IS_VAR, NULL, VAR_TRY_RTSOL, varCheck },
+{ "FTP username", "Username and password to use instead of anonymous",
+ OPT_IS_FUNC, mediaSetFTPUserPass, VAR_FTP_USER, varCheck },
+{ "Editor", "Which text editor to use during installation",
+ OPT_IS_VAR, EDITOR_PROMPT, VAR_EDITOR, varCheck },
+{ "Extract Detail", "How verbosely to display file name information during extractions",
+ OPT_IS_FUNC, mediaSetCPIOVerbosity, VAR_CPIO_VERBOSITY, varCheck },
+{ "Release Name", "Which release to attempt to load from installation media",
+ OPT_IS_VAR, RELNAME_PROMPT, VAR_RELNAME, varCheck },
+{ "Install Root", "Which directory to unpack distributions or packages relative to",
+ OPT_IS_VAR, INSTROOT_PROMPT, VAR_INSTALL_ROOT, varCheck },
+{ "Browser package", "This is the browser package that will be used for viewing HTML docs",
+ OPT_IS_VAR, BPKG_PROMPT, VAR_BROWSER_PACKAGE, varCheck },
+{ "Browser Exec", "This is the path to the main binary of the browser package",
+ OPT_IS_VAR, BBIN_PROMPT, VAR_BROWSER_BINARY, varCheck },
+{ "Media Type", "The current installation media type.",
+ OPT_IS_FUNC, mediaGetType, VAR_MEDIA_TYPE, mediaCheck },
+{ "Media Timeout", "Timeout value in seconds for slow media.",
+ OPT_IS_VAR, TIMEOUT_PROMPT, VAR_MEDIA_TIMEOUT, varCheck },
+{ "Package Temp", "The directory where package temporary files should go",
+ OPT_IS_VAR, PKG_PROMPT, VAR_PKG_TMPDIR, varCheck },
+{ "Newfs Args", "Default parameters for newfs(8)",
+ OPT_IS_VAR, NEWFS_PROMPT, VAR_NEWFS_ARGS, varCheck },
+{ "Fixit Console", "Which tty to use for the Fixit action.",
+ OPT_IS_FUNC, fixitTtyWhich, VAR_FIXIT_TTY, varCheck },
+{ "Re-scan Devices", "Re-run sysinstall's initial device probe",
+ OPT_IS_FUNC, deviceRescan, NULL, NULL },
+{ "Use Defaults", "Reset all values to startup defaults",
+ OPT_IS_FUNC, installVarDefaults, NULL, resetLogo },
+{ NULL, NULL, 0, NULL, NULL, NULL },
+};
+
+#define OPT_START_ROW 4
+#define OPT_END_ROW 19
+#define OPT_NAME_COL 0
+#define OPT_VALUE_COL 16
+#define GROUP_OFFSET 40
+
+static char *
+value_of(Option opt)
+{
+ static char ival[40];
+
+ switch (opt.type) {
+ case OPT_IS_STRING:
+ return (char *)opt.data;
+
+ case OPT_IS_INT:
+ sprintf(ival, "%lu", (long)opt.data);
+ return ival;
+
+ case OPT_IS_FUNC:
+ case OPT_IS_VAR:
+ if (opt.check)
+ return opt.check(&opt);
+ else
+ return "<*>";
+ }
+ return "<unknown>";
+}
+
+static int
+fire(Option opt)
+{
+ int status = 0;
+
+ if (opt.type == OPT_IS_FUNC) {
+ int (*cp)(char *) = opt.data, rcode;
+
+ rcode = cp(NULL);
+ status = 1;
+ }
+ else if (opt.type == OPT_IS_VAR) {
+ if (opt.data) {
+ (void)variable_get_value(opt.aux, opt.data, -1);
+ status = 1;
+ }
+ else if (variable_get(opt.aux)) {
+ if (!variable_cmp(opt.aux, "YES"))
+ variable_set2(opt.aux, "NO", -1);
+ else
+ variable_set2(opt.aux, "YES", -1);
+ }
+ else
+ variable_set2(opt.aux, "YES", 0);
+ }
+ if (opt.check)
+ opt.check(&opt);
+ refresh();
+ return status;
+}
+
+int
+optionsEditor(dialogMenuItem *self)
+{
+ int i, optcol, optrow, key;
+ static int currOpt = 0;
+ WINDOW *w = savescr();
+
+ dialog_clear();
+ clear();
+
+ while (1) {
+ /* Whap up the header */
+ attrset(A_REVERSE); mvaddstr(0, 0, "Options Editor"); attrset(A_NORMAL);
+ for (i = 0; i < 2; i++) {
+ mvaddstr(OPT_START_ROW - 2, OPT_NAME_COL + (i * GROUP_OFFSET), "Name");
+ mvaddstr(OPT_START_ROW - 1, OPT_NAME_COL + (i * GROUP_OFFSET), "----");
+
+ mvaddstr(OPT_START_ROW - 2, OPT_VALUE_COL + (i * GROUP_OFFSET), "Value");
+ mvaddstr(OPT_START_ROW - 1, OPT_VALUE_COL + (i * GROUP_OFFSET), "-----");
+ }
+ /* And the footer */
+ mvprintw(OPT_END_ROW + 1, 0, "Use SPACE to select/toggle an option, arrow keys to move,");
+ mvprintw(OPT_END_ROW + 2, 0, "? or F1 for more help. When you're done, type Q to Quit.");
+
+ optrow = OPT_START_ROW;
+ optcol = OPT_NAME_COL;
+ for (i = 0; Options[i].name; i++) {
+ /* Names are painted somewhat gratuitously each time, but it's easier this way */
+ mvprintw(optrow, OPT_NAME_COL + optcol, Options[i].name);
+ if (currOpt == i)
+ attrset(ATTR_SELECTED);
+ mvprintw(optrow++, OPT_VALUE_COL + optcol, value_of(Options[i]));
+ if (currOpt == i)
+ attrset(A_NORMAL);
+ if (optrow == OPT_END_ROW) {
+ optrow = OPT_START_ROW;
+ optcol += GROUP_OFFSET;
+ }
+ clrtoeol();
+ }
+ attrset(ATTR_TITLE);
+ mvaddstr(OPT_END_ROW + 4, 0, Options[currOpt].desc);
+ attrset(A_NORMAL);
+ clrtoeol();
+ move(0, 14);
+ refresh();
+
+ /* Start the edit loop */
+ key = toupper(getch());
+ switch (key) {
+ case KEY_F(1):
+ case '?':
+ systemDisplayHelp("options");
+ clear();
+ break;
+
+ case '\020': /* ^P */
+ case KEY_UP:
+ if (currOpt)
+ --currOpt;
+ else
+ for (currOpt = 0; Options[currOpt + 1].name; currOpt++);
+ continue;
+
+ case '\016': /* ^N */
+ case KEY_DOWN:
+ if (Options[currOpt + 1].name)
+ ++currOpt;
+ else
+ currOpt = 0;
+ continue;
+
+ case KEY_HOME:
+ currOpt = 0;
+ continue;
+
+ case KEY_END:
+ while (Options[currOpt + 1].name)
+ ++currOpt;
+ continue;
+
+ case ' ':
+ if (fire(Options[currOpt]))
+ clear();
+ continue;
+
+ case '\033': /* ESC */
+ case 'Q':
+ clear();
+ dialog_clear();
+ restorescr(w);
+ return DITEM_SUCCESS | DITEM_CONTINUE;
+
+ default:
+ beep();
+ }
+ }
+ /* NOTREACHED */
+ return DITEM_SUCCESS;
+}
+
+int
+fixitTtyWhich(dialogMenuItem *self)
+{
+ char *cp = variable_get(VAR_FIXIT_TTY);
+
+ if (!cp) {
+ msgConfirm("The Fix-it TTY setting is not set to anything!");
+ return DITEM_FAILURE;
+ }
+ else {
+ if (!strcmp(cp, "standard"))
+ variable_set2(VAR_FIXIT_TTY, "serial", 0);
+ else /* must be "serial" - wrap around */
+ variable_set2(VAR_FIXIT_TTY, "standard", 0);
+ }
+ return DITEM_SUCCESS;
+}
diff --git a/usr.sbin/sysinstall/package.c b/usr.sbin/sysinstall/package.c
new file mode 100644
index 0000000..212b654
--- /dev/null
+++ b/usr.sbin/sysinstall/package.c
@@ -0,0 +1,270 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sysinstall.h"
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+static Boolean sigpipe_caught;
+
+static void
+catch_pipe(int sig)
+{
+ sigpipe_caught = TRUE;
+}
+
+extern PkgNode Top;
+
+/* Like package_extract, but assumes current media device and chases deps */
+int
+package_add(char *name)
+{
+ PkgNodePtr tmp;
+ int i, current, low, high;
+
+ if (!mediaVerify())
+ return DITEM_FAILURE;
+
+ if (!DEVICE_INIT(mediaDevice))
+ return DITEM_FAILURE;
+
+ i = index_initialize("packages/INDEX");
+ if (DITEM_STATUS(i) != DITEM_SUCCESS)
+ return i;
+
+ tmp = index_search(&Top, name, &tmp);
+ if (tmp) {
+ if (have_volumes) {
+ low = low_volume;
+ high = high_volume;
+ } else
+ low = high = 0;
+ for (current = low; current <= high; current++)
+ i = index_extract(mediaDevice, &Top, tmp, FALSE, current);
+ return i;
+ } else {
+ msgConfirm("Sorry, package %s was not found in the INDEX.", name);
+ return DITEM_FAILURE;
+ }
+}
+
+/* For use by dispatch */
+int
+packageAdd(dialogMenuItem *self)
+{
+ char *cp;
+
+ cp = variable_get(VAR_PACKAGE);
+ if (!cp) {
+ msgDebug("packageAdd: No package name passed in package variable\n");
+ return DITEM_FAILURE;
+ }
+ else
+ return package_add(cp);
+}
+
+Boolean
+package_installed(char *name)
+{
+ char fname[FILENAME_MAX];
+ int status /* = vsystem("pkg_info -e %s", name) */;
+
+ /* XXX KLUDGE ALERT! This makes evil assumptions about how XXX
+ * packages register themselves and should *really be done with
+ * `pkg_info -e <name>' except that this it's too slow for an
+ * item check routine.. :-(
+ */
+ snprintf(fname, FILENAME_MAX, "/var/db/pkg/%s", name);
+ status = access(fname, R_OK);
+ if (isDebug())
+ msgDebug("package check for %s returns %s.\n", name, status ? "failure" : "success");
+ return !status;
+}
+
+/* Extract a package based on a namespec and a media device */
+int
+package_extract(Device *dev, char *name, Boolean depended)
+{
+ char path[MAXPATHLEN];
+ const char *PkgExts[] = { "", ".tbz", ".tbz2", ".tgz" };
+ int last_msg, pathend, ret;
+ size_t ext;
+ FILE *fp;
+
+ last_msg = 0;
+
+ /* Check to make sure it's not already there */
+ if (package_installed(name))
+ return DITEM_SUCCESS;
+
+ if (!DEVICE_INIT(dev)) {
+ msgConfirm("Unable to initialize media type for package extract.");
+ return DITEM_FAILURE;
+ }
+
+ /* If necessary, initialize the ldconfig hints */
+ if (!file_readable("/var/run/ld-elf.so.hints"))
+ vsystem("ldconfig /usr/lib /usr/lib/compat /usr/local/lib /usr/X11R6/lib");
+
+ /* Be initially optimistic */
+ ret = DITEM_SUCCESS;
+ /* Make a couple of paranoid locations for temp files to live if user specified none */
+ if (!variable_get(VAR_PKG_TMPDIR)) {
+ /* Set it to a location with as much space as possible */
+ variable_set2(VAR_PKG_TMPDIR, "/var/tmp", 0);
+ }
+ Mkdir(variable_get(VAR_PKG_TMPDIR));
+ vsystem("chmod 1777 %s", variable_get(VAR_PKG_TMPDIR));
+
+ if (!index(name, '/')) {
+ if (!strpbrk(name, "-_"))
+ pathend = snprintf(path, sizeof path, "packages/Latest/%s", name);
+ else
+ pathend = snprintf(path, sizeof path, "packages/All/%s", name);
+ }
+ else
+ pathend = snprintf(path, sizeof path, "%s", name);
+
+ /* We have a path, call the device strategy routine to get the file */
+ for (ext = 0 ; ext < sizeof PkgExts / sizeof PkgExts[0]; ++ext) {
+ strlcpy(path + pathend, PkgExts[ext], sizeof path - pathend);
+ if ((fp = DEVICE_GET(dev, path, TRUE)))
+ break;
+ }
+
+ if (fp) {
+ int i = 0, tot, pfd[2];
+ pid_t pid;
+ WINDOW *w = savescr();
+
+ sigpipe_caught = FALSE;
+ signal(SIGPIPE, catch_pipe);
+
+ dialog_clear_norefresh();
+ msgNotify("Adding %s%s\nfrom %s", path, depended ? " (as a dependency)" : "", dev->name);
+ pipe(pfd);
+ pid = fork();
+ if (!pid) {
+ dup2(pfd[0], 0); close(pfd[0]);
+ dup2(DebugFD, 1); dup2(1, 2);
+ close(pfd[1]);
+
+ /* Prevent pkg_add from wanting to interact in bad ways */
+ setenv("BATCH", "t", 1);
+
+ if (isDebug())
+ i = execl("/usr/sbin/pkg_add", "/usr/sbin/pkg_add", "-v", "-",
+ (char *)0);
+ else
+ i = execl("/usr/sbin/pkg_add", "/usr/sbin/pkg_add", "-",
+ (char *)0);
+ }
+ else {
+ char buf[BUFSIZ];
+ struct timeval start, stop;
+
+ close(pfd[0]);
+ tot = 0;
+ (void)gettimeofday(&start, (struct timezone *)0);
+
+ while (!sigpipe_caught && (i = fread(buf, 1, BUFSIZ, fp)) > 0) {
+ int seconds;
+
+ tot += i;
+ /* Print statistics about how we're doing */
+ (void) gettimeofday(&stop, (struct timezone *)0);
+ stop.tv_sec = stop.tv_sec - start.tv_sec;
+ stop.tv_usec = stop.tv_usec - start.tv_usec;
+ if (stop.tv_usec < 0)
+ stop.tv_sec--, stop.tv_usec += 1000000;
+ seconds = stop.tv_sec + (stop.tv_usec / 1000000.0);
+ if (!seconds)
+ seconds = 1;
+ if (seconds != last_msg) {
+ last_msg = seconds;
+ msgInfo("%10d bytes read from package %s @ %4.1f KBytes/second", tot, name, (tot / seconds) / 1000.0);
+ }
+ /* Write it out */
+ if (sigpipe_caught || write(pfd[1], buf, i) != i) {
+ msgInfo("Write failure to pkg_add! Package may be corrupt.");
+ break;
+ }
+ }
+ close(pfd[1]);
+ fclose(fp);
+ if (sigpipe_caught)
+ msgInfo("pkg_add(1) apparently did not like the %s package.", name);
+ else if (i == -1)
+ msgInfo("I/O error while reading in the %s package.", name);
+ else
+ msgInfo("Package %s read successfully - waiting for pkg_add(1)", name);
+ refresh();
+ i = waitpid(pid, &tot, 0);
+ dialog_clear_norefresh();
+ if (sigpipe_caught || i < 0 || WEXITSTATUS(tot)) {
+ ret = DITEM_FAILURE;
+ if (variable_get(VAR_NO_CONFIRM))
+ msgNotify("Add of package %s aborted, error code %d -\n"
+ "Please check the debug screen for more info.", name, WEXITSTATUS(tot));
+ else
+ msgConfirm("Add of package %s aborted, error code %d -\n"
+ "Please check the debug screen for more info.", name, WEXITSTATUS(tot));
+ }
+ else
+ msgNotify("Package %s was added successfully", name);
+
+ /* Now catch any stragglers */
+ while (wait3(&tot, WNOHANG, NULL) > 0);
+
+ sleep(1);
+ restorescr(w);
+ }
+ }
+ else {
+ dialog_clear_norefresh();
+ if (variable_get(VAR_NO_CONFIRM))
+ msgNotify("Unable to fetch package %s from selected media.\n"
+ "No package add will be done.", name);
+ else
+ msgConfirm("Unable to fetch package %s from selected media.\n"
+ "No package add will be done.", name);
+ ret = DITEM_FAILURE;
+ }
+ signal(SIGPIPE, SIG_IGN);
+ return ret;
+}
diff --git a/usr.sbin/sysinstall/rtermcap.c b/usr.sbin/sysinstall/rtermcap.c
new file mode 100644
index 0000000..84b3feb
--- /dev/null
+++ b/usr.sbin/sysinstall/rtermcap.c
@@ -0,0 +1,15 @@
+#include <stdio.h>
+#include <termcap.h>
+
+int
+main(int argc, char **argv)
+{
+ char buf[4096];
+ int i;
+
+ if (argc < 2)
+ return 1;
+ i = tgetent(buf, argv[1]);
+ printf("%s",buf);
+ return 0;
+}
diff --git a/usr.sbin/sysinstall/sysinstall.8 b/usr.sbin/sysinstall/sysinstall.8
new file mode 100644
index 0000000..7cb55aa
--- /dev/null
+++ b/usr.sbin/sysinstall/sysinstall.8
@@ -0,0 +1,908 @@
+.\" Copyright (c) 1997
+.\" Jordan Hubbard <jkh@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 Jordan Hubbard AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL Jordan Hubbard OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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 18, 2007
+.Dt SYSINSTALL 8
+.Os
+.Sh NAME
+.Nm sysinstall
+.Nd system installation and configuration tool
+.Sh SYNOPSIS
+.Nm
+.Op Ar var=value
+.Op Ar function
+.Op Ar ...
+.Sh DESCRIPTION
+The
+.Nm
+utility is used for installing and configuring
+.Fx
+systems.
+It is the first utility invoked by the
+.Fx
+installation boot
+floppy and is also available as
+.Pa /usr/sbin/sysinstall
+on newly installed
+.Fx
+systems for use in later configuring the system.
+.Pp
+The
+.Nm
+utility is generally invoked without arguments for the default
+behavior, where the main installation/configuration menu is presented.
+.Pp
+On those occasions where it is deemed necessary to invoke a subsystem
+of sysinstall directly, however, it is also possible to do so by
+naming the appropriate function entry points on the command line.
+Since this action is essentially identical to running an installation
+script, each command-line argument corresponding to a line of script,
+the reader is encouraged to read the section on scripting for more
+information on this feature.
+.Sh NOTES
+The
+.Nm
+utility is essentially nothing more than a monolithic C program with
+the ability to write MBRs and disk labels (through the services
+of the
+.Xr libdisk 3
+library) and install distributions or packages onto new and
+existing
+.Fx
+systems.
+It also contains some extra intelligence
+for running as a replacement for
+.Xr init 8
+when it is invoked by the
+.Fx
+installation boot procedure.
+It
+assumes very little in the way of additional utility support and
+performs most file system operations by calling the relevant syscalls
+(such as
+.Xr mount 2 )
+directly.
+.Pp
+The
+.Nm
+utility currently uses the
+.Xr dialog 3
+library to do user interaction with simple ANSI line graphics, color
+support for which is enabled by either running on a syscons VTY or some
+other color-capable terminal emulator (newer versions of xterm will support
+color when using the
+.Dq xterm-color
+termcap entry).
+.Pp
+This product is currently at the end of its life cycle and will
+eventually be replaced.
+.Sh RUNNING SCRIPTS
+The
+.Nm
+utility may be either driven interactively through its various internal menus
+or run in batch mode, driven by an external script.
+Such a script may
+be loaded and executed in one of 3 ways:
+.Bl -tag -width Ds
+.It Sy "LOAD_CONFIG_FILE"
+If
+.Nm
+is compiled with LOAD_CONFIG_FILE set in the environment
+(or in the Makefile) to some value, then that value will
+be used as the filename to automatically look for and load
+when
+.Nm
+starts up and with no user interaction required.
+This option is aimed primarily at large sites who wish to create a
+single prototype install for multiple machines with largely identical
+configurations and/or installation options.
+.It Sy "MAIN MENU"
+If
+.Nm
+is run interactively, that is to say in the default manner, it will
+bring up a main menu which contains a "load config file" option.
+Selecting this option will prompt for the name of a script file which
+it then will attempt to load from a DOS or UFS formatted floppy.
+.It Sy "COMMAND LINE"
+Each command line argument is treated as a script directive
+when
+.Nm
+is run in multi-user mode.
+Execution ends either by explicit request
+(e.g.\& calling the
+.Ar shutdown
+directive), upon reaching the end of the argument list or on error.
+.Pp
+For example:
+.Bd -literal
+/usr/sbin/sysinstall _ftpPath=ftp://ziggy/pub/ mediaSetFTP configPackages
+.Ed
+.Pp
+Would initialize
+.Nm
+for FTP installation media (using the server `ziggy') and then
+bring up the package installation editor, exiting when finished.
+.El
+.Sh SCRIPT SYNTAX
+A script is a list of one or more directives, each directive taking
+the form of:
+.Pp
+.Ar var=value
+.Pp
+.Ar function
+.Pp
+or
+.Ar #somecomment
+.Pp
+Where
+.Ar var=value
+is the assignment of some internal
+.Nm
+variable, e.g.\& "ftpPass=FuNkYChiKn", and
+.Ar function
+is the name of an internal
+.Nm
+function, e.g.\& "mediaSetFTP", and
+.Ar #comment
+is a single-line comment for documentation purposes (ignored by
+sysinstall).
+Each directive must be by itself on a single line,
+functions taking their arguments by examining known variable names.
+This requires that you be sure to assign the relevant variables before
+calling a function which requires them.
+.Pp
+The
+.Ar noError
+variable can be assigned before each directive: this will cause any error
+detected while processing the directive itself to be ignored.
+The value of
+.Ar noError
+will automatically reset to the default "unassigned" every time a directive is
+processed.
+.Pp
+When and where a function depends on the settings of one or more variables
+will be noted in the following table:
+.Pp
+.Sy "Function Glossary" :
+.Pp
+.Bl -tag -width indent
+.It configAnonFTP
+Invoke the Anonymous FTP configuration menu.
+.Pp
+.Sy Variables :
+None
+.It configRouter
+Select which routing daemon you wish to use, potentially
+loading any required 3rd-party routing daemons as necessary.
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It router
+can be set to the name of the desired routing daemon,
+e.g.\&
+.Dq routed
+or
+.Dq gated ,
+otherwise it is prompted for.
+.El
+.It configNFSServer
+Configure host as an NFS server.
+.Pp
+.Sy Variables :
+None
+.It configNTP
+Configure host as a user of the Network Time Protocol.
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It ntpdate_flags
+The flags to
+.Xr ntpdate 8 ,
+that is to say the name of the server to sync from.
+.El
+.It configPCNFSD
+Configure host to support PC NFS.
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It pcnfsd_pkg
+The name of the PCNFSD package to load if necessary (defaults to hard coded
+version).
+.El
+.It configPackages
+Bring up the interactive package management menu.
+.Pp
+.Sy Variables :
+None
+.It configUsers
+Add users and/or groups to the system.
+.Pp
+.Sy Variables :
+None
+.It diskPartitionEditor
+Invokes the disk partition (MBR) editor.
+.Pp
+.Sy Variables :
+.Bl -tag -width findx
+.It geometry
+The disk geometry, as a cyls/heads/sectors formatted string.
+The word "sane" instructs
+.Nm
+to calculate a safe (not necessarily optimal) geometry if the
+current one has more than 65535 cylinders, more than 256 heads or
+more than 63 sectors per track (255 sectors on the PC98
+architecture).
+Default: no
+change to geometry.
+.It partition
+Set to disk partitioning type or size, its value being
+.Ar free
+in order to use only remaining free space for
+.Fx ,
+.Ar all
+to use the entire disk for
+.Fx
+but maintain a proper partition
+table,
+.Ar existing
+to use an existing
+.Fx
+partition (first found),
+.Ar exclusive
+to use the disk in
+.Dq dangerously dedicated
+mode or, finally,
+.Ar somenumber
+to allocate
+.Ar somenumber
+blocks of available free space to a new
+.Fx
+partition.
+Default: Interactive mode.
+.It bootManager
+is set to one of
+.Ar boot
+to signify the installation of a boot manager,
+.Ar standard
+to signify installation of a "standard" non-boot MGR DOS
+MBR or
+.Ar none
+to indicate that no change to the boot manager is desired.
+Default: none.
+.It diskInteractive
+If set, bring up the interactive disk partition editor.
+.El
+.Pp
+Note: Nothing is actually written to disk by this function, an explicit call to
+.Ar diskPartitionWrite
+being required for that to happen.
+.It diskPartitionWrite
+Causes any pending MBR changes (typically from the
+.Ar diskPartitionEditor
+function) to be written out.
+.Pp
+.Sy Variables :
+None
+.It diskLabelEditor
+Invokes the disk label editor.
+This is a bit trickier from a script
+since you need to essentially label everything inside each
+.Fx
+(type 0xA5) partition created by the
+.Ar diskPartitionEditor
+function, and that requires knowing a few rules about how things are
+laid out.
+When creating a script to automatically allocate disk space
+and partition it up, it is suggested that you first perform the
+installation interactively at least once and take careful notes as to
+what the slice names will be, then and only then hardwiring them into
+the script.
+.Pp
+For example, let's say you have a SCSI disk on which you have created a new
+.Fx
+partition in slice 2 (your DOS partition residing in slice 1).
+The slice name would be
+.Ar da0s2
+for the whole
+.Fx
+partition
+.Ar ( da0s1
+being your DOS primary
+partition).
+Now let's further assume that you have 4GB in this
+partition and you want to sub-partition that space into root, swap,
+var and usr file systems for
+.Fx .
+Your invocation of the
+.Ar diskLabelEditor
+function might involve setting the following variables:
+.Bl -tag -width findx
+.It Li "da0s2-1=ufs 2097152 /"
+A 1GB root file system (all sizes are in 512 byte blocks).
+.It Li "da0s2-2=swap 1048576 /"
+A 512MB swap partition.
+.It Li "da0s2-3=ufs 524288 /var"
+A 256MB /var file system.
+.It Li "da0s2-4=ufs 0 /usr 1"
+With the balance of free space (around 2.25GB) going to the /usr
+file system and with soft-updates enabled (the argument following
+the mount point, if non-zero, means to set the soft updates flag).
+.El
+.Pp
+One can also use the
+.Ar diskLabelEditor
+for mounting or erasing existing partitions as well as creating new
+ones.
+Using the previous example again, let's say that we also wanted
+to mount our DOS partition and make sure that an
+.Pa /etc/fstab
+entry is created for it in the new installation.
+Before calling the
+.Ar diskLabelEditor
+function, we simply add an additional line:
+.Pp
+.Dl "da0s1=/dos_c N"
+.Pp
+before the call.
+This tells the label editor that you want to mount
+the first slice on
+.Pa /dos_c
+and not to attempt to newfs it (not that
+.Nm
+would attempt this for a DOS partition in any case, but it could just
+as easily be an existing UFS partition being named here and the 2nd
+field is non-optional).
+.Pp
+You can also set the
+.Ar diskInteractive
+variable to request that the disk label editor use an interactive dialog
+to partition the disk instead of using variables to explicitly layout the
+disk as described above.
+.Pp
+Note: No file system data is actually written to disk until an
+explicit call to
+.Ar diskLabelCommit
+is made.
+.It diskLabelCommit
+Writes out all pending disklabel information and creates and/or mounts any
+file systems which have requests pending from the
+.Ar diskLabelEditor
+function.
+.Pp
+.Sy Variables :
+None
+.It distReset
+Resets all selected distributions to the empty set (no distributions selected).
+.Pp
+.Sy Variables :
+None
+.It distSetCustom
+Allows the selection of a custom distribution set (e.g.\& not just one of the
+existing "canned" sets) with no user interaction.
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It dists
+List of distributions to load.
+Possible distribution values are:
+.Bl -tag -width indentxx
+.It Li base
+The base binary distribution.
+.It Li generic
+The GENERIC kernel.
+.It Li smp
+A kernel suitable for multiple processor systems.
+.It Li doc
+Miscellaneous documentation
+.It Li games
+Games
+.It Li manpages
+Manual pages (unformatted)
+.It Li catpages
+Pre-formatted manual pages
+.It Li proflibs
+Profiled libraries for developers.
+.It Li dict
+Dictionary information (for tools like spell).
+.It Li info
+GNU info files and other extra docs.
+.It Li lib32
+(amd64 only)
+32-bit runtime compatibility libraries.
+.It Li ports
+The ports collection.
+.It Li ssecure
+/usr/src/secure
+.It Li sbase
+/usr/src/[top level files]
+.It Li scontrib
+/usr/src/contrib
+.It Li scrypto
+/usr/src/crypto
+.It Li sgnu
+/usr/src/gnu
+.It Li setc
+/usr/src/etc
+.It Li sgames
+/usr/src/games
+.It Li sinclude
+/usr/src/include
+.It Li skrb5
+/usr/src/kerberos5
+.It Li slib
+/usr/src/lib
+.It Li slibexec
+/usr/src/libexec
+.It Li srelease
+/usr/src/release
+.It Li srescue
+/usr/src/rescue
+.It Li stools
+/usr/src/tools
+.It Li sbin
+/usr/src/bin
+.It Li ssbin
+/usr/src/sbin
+.It Li sshare
+/usr/src/share
+.It Li ssys
+/usr/src/sys
+.It Li stools
+/usr/src/tools
+.It Li subin
+/usr/src/usr.bin
+.It Li susbin
+/usr/src/usr.sbin
+.It Li local
+Local additions collection.
+.El
+.El
+.It distSetDeveloper
+Selects the standard Developer's distribution set.
+.Pp
+.Sy Variables :
+None
+.It distSetUser
+Selects the standard user distribution set.
+.Pp
+.Sy Variables :
+None
+.It distSetMinimum
+Selects the very minimum distribution set.
+.Pp
+.Sy Variables :
+None
+.It distSetEverything
+Selects the full whack - all available distributions.
+.Pp
+.Sy Variables :
+None
+.It distSetSrc
+Interactively select source subcomponents.
+.Pp
+.Sy Variables :
+None
+.It distExtractAll
+Install all currently selected distributions (requires that
+media device also be selected).
+.Pp
+.Sy Variables :
+None
+.It docBrowser
+Install (if necessary) an HTML documentation browser and go to the
+HTML documentation submenu.
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It browserPackage
+The name of the browser package to try and install as necessary.
+Defaults to latest links package.
+.It browserBinary
+The name of the browser binary itself (if overriding the
+.Ar browserPackage
+variable).
+Defaults to links.
+.El
+.It installCommit
+Commit any and all pending changes to disk.
+This function
+is essentially shorthand for a number of more granular "commit"
+functions.
+.Pp
+.Sy Variables :
+None
+.It installConfigure
+Commit any rc.conf changes to disk.
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It keeprcconf
+Preserve existing rc.conf parameters.
+This is useful if you have a post-install script which modifies rc.conf.
+.El
+ .It installExpress
+Start an "express" installation, asking few questions of
+the user.
+.Pp
+.Sy Variables :
+None
+.It installStandard
+Start a "standard" installation, the most user-friendly
+installation type available.
+.Pp
+.Sy Variables :
+None
+.It installUpgrade
+Start an upgrade installation.
+.Pp
+.Sy Variables :
+None
+.It installFixitHoloShell
+Start up the "emergency holographic shell" over on VTY4
+if running as init.
+This will also happen automatically
+as part of the installation process unless
+.Ar noHoloShell
+is set.
+.Pp
+.Sy Variables :
+None
+.It installFixitCDROM
+Go into "fixit" mode, assuming a live file system CDROM
+currently in the drive.
+.Pp
+.Sy Variables :
+None
+.It installFixitFloppy
+Go into "fixit" mode, assuming an available fixit floppy
+disk (user will be prompted for it).
+.Pp
+.Sy Variables :
+None
+.It installFilesystems
+Do just the file system initialization part of an install.
+.Pp
+.Sy Variables :
+None
+.It installVarDefaults
+Initialize all variables to their defaults, overriding any
+previous settings.
+.Pp
+.Sy Variables :
+None
+.It loadConfig
+Sort of like an #include statement, it allows you to load one
+configuration file from another.
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It configFile
+The fully qualified pathname of the file to load.
+.El
+.It mediaOpen
+If a media device is set, mount it.
+.Pp
+.Sy Variables :
+None
+.It mediaClose
+If a media device is open, close it.
+.Pp
+.Sy Variables :
+None
+.It mediaSetCDROM
+Select a
+.Fx
+CDROM as the installation media.
+.Pp
+.Sy Variables :
+None
+.It mediaSetFloppy
+Select a pre-made floppy installation set as the installation media.
+.Pp
+.Sy Variables :
+None
+.It mediaSetDOS
+Select an existing DOS primary partition as the installation media.
+The first primary partition found is used (e.g.\& C:).
+.Pp
+.Sy Variables :
+None
+.It mediaSetFTP
+Select an FTP site as the installation media.
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It hostname
+The name of the host being installed (non-optional).
+.It domainname
+The domain name of the host being installed (optional).
+.It defaultrouter
+The default router for this host (non-optional).
+.It netDev
+Which host interface to use
+.Ar ( ed0
+or
+.Ar ep0 ,
+for example.
+Non-optional).
+.It netInteractive
+If set, bring up the interactive network setup form even
+if all relevant configuration variables are already set (optional).
+.It ipaddr
+The IP address for the selected host interface (non-optional).
+.It netmask
+The netmask for the selected host interface (non-optional).
+.It _ftpPath
+The fully qualified URL of the FTP site containing the
+.Fx
+distribution you are interested in, e.g.\&
+.Ar ftp://ftp.FreeBSD.org/pub/FreeBSD/ .
+.El
+.It mediaSetFTPActive
+Alias for
+.Ar mediaSetFTP
+using "active" FTP transfer mode.
+.Pp
+.Sy Variables :
+Same as for
+.Ar mediaSetFTP .
+.It mediaSetFTPPassive
+Alias for
+.Ar mediaSetFTP
+using "passive" FTP transfer mode.
+.Pp
+.Sy Variables :
+Same as for
+.Ar mediaSetFTP .
+.It mediaSetHTTP
+Alias for
+.Ar mediaSetFTP
+using an HTTP proxy.
+.Pp
+.Sy Variables :
+See
+.Ar mediaSetFTP ,
+plus
+.Bl -tag -width indent
+.It _httpPath
+The proxy to use (host:port) (non-optional).
+.El
+.It mediaSetUFS
+Select an existing UFS partition (mounted with the label editor) as
+the installation media.
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It ufs
+full /path to directory containing the
+.Fx
+distribution you are
+interested in.
+.El
+.It mediaSetNFS
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It hostname
+The name of the host being installed (non-optional).
+.It domainname
+The domain name of the host being installed (optional).
+.It defaultrouter
+The default router for this host (non-optional).
+.It netDev
+Which host interface to use
+.Ar ( ed0
+or
+.Ar ep0 ,
+for example.
+Non-optional).
+.It netInteractive
+If set, bring up the interactive network setup form even
+if all relevant configuration variables are already set (optional).
+.It ipaddr
+The IP address for the selected host interface (non-optional).
+.It netmask
+The netmask for the selected host interface (non-optional).
+.It nfs
+full hostname:/path specification for directory containing
+the
+.Fx
+distribution you are interested in.
+.El
+.It mediaSetFTPUserPass
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It ftpUser
+The username to log in as on the ftp server site.
+Default: ftp
+.It ftpPass
+The password to use for this username on the ftp
+server site.
+Default: user@host
+.El
+.It mediaSetCPIOVerbosity
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It cpioVerbose
+Can be used to set the verbosity of cpio extractions to low, medium or
+high.
+.El
+.It mediaGetType
+Interactively get the user to specify some type of media.
+.Pp
+.Sy Variables :
+None
+.It optionsEditor
+Invoke the interactive options editor.
+.Pp
+.Sy Variables :
+None
+.It packageAdd
+Try to fetch and add a package to the system (requires
+that a media type be set),
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It package
+The name of the package to add, e.g.\& bash-1.14.7 or ncftp-2.4.2.
+.El
+.It addGroup
+Invoke the interactive group editor.
+.Pp
+.Sy Variables :
+None
+.It addUser
+Invoke the interactive user editor.
+.Pp
+.Sy Variables :
+None
+.It shutdown
+Stop the script, terminate sysinstall and reboot the system.
+On the sparc64 platform, the system is halted rather than rebooted.
+.Pp
+.Sy Variables :
+None
+.It system
+Execute an arbitrary command with
+.Xr system 3
+.Pp
+.Sy Variables :
+.Bl -tag -width indent
+.It command
+The name of the command to execute.
+When running
+from a boot floppy, very minimal expectations should
+be made as to what is available until/unless a relatively
+full system installation has just been done.
+.El
+.It tcpMenuSelect
+Configure a network device.
+.Pp
+.Sy Variables :
+Same as for
+.Ar mediaSetFTP
+except that
+.Ar _ftpPath
+is not used.
+.El
+.Sh DISTRIBUTION MEDIA
+The following files can be used to affect the operation of
+.Nm
+when used during initial system installation.
+.Bl -tag -width ".Pa packages/INDEX"
+.It Pa cdrom.inf
+A text file of properties, listed one per line, that describe the
+contents of the media in use.
+The syntax for each line is simply
+.Dq Ar property No = Ar value .
+Currently, only the following properties are recognized.
+.Bl -tag -width ".Va CD_MACHINE_ARCH"
+.It Va CD_VERSION
+This property should be set to the
+.Fx
+version on the current
+media volume.
+For example,
+.Dq Li "CD_VERSION = 5.3" .
+.It Va CD_MACHINE_ARCH
+This property should be set to the architecture of the contents on
+this volume.
+This property is normally only used with
+.Fx
+products that contain
+CDs for different architectures, to provide better error messages if
+users try to install packages built for the wrong architecture.
+For example,
+.Dq Li "CD_MACHINE_ARCH = amd64" .
+.It Va CD_VOLUME
+In a multi-volume collection (such as the
+.Fx
+4-CD set), the
+.Pa ports/INDEX
+file on each disc should contain the full package index for the set.
+The last field of the
+.Pa INDEX
+file denotes which volume the package
+appears on, and the
+.Va CD_VOLUME
+property here defines the volume ID of the current disc.
+.El
+.It Pa packages/INDEX
+The package index file.
+Each package is listed on a separate line with additional meta-data
+such as the required dependencies.
+This index is generated by
+.Dq Li "make index"
+from the
+.Xr ports 7
+collection.
+When multi-volume support is enabled, an additional field should be
+added to each line indicating which media volume contains the given
+package.
+.El
+.Pp
+For information about building a full release of
+.Fx ,
+please see
+.Xr release 7 .
+.Sh FILES
+This utility may edit the contents of
+.Pa /etc/rc.conf ,
+.Pa /etc/hosts ,
+and
+.Pa /etc/resolv.conf
+as necessary to reflect changes in the network configuration.
+.Sh SEE ALSO
+If you have a reasonably complete source tree online, take
+a look at
+.Pa /usr/src/usr.sbin/sysinstall/install.cfg
+for a sample installation script.
+.Sh HISTORY
+This version of
+.Nm
+first appeared in
+.Fx 2.0 .
+.Sh AUTHORS
+.An Jordan K. Hubbard Aq jkh@FreeBSD.org
+.Sh BUGS
+Editing slice and partition tables on disks which are currently mounted by
+the system is not allowed.
+This is generally only a problem when
+.Nm
+is run on a system that is already installed.
+Use
+.Xr fdisk 8
+and
+.Xr bsdlabel 8
+for these tasks.
+.Pp
+This utility is a prototype which lasted several years past
+its expiration date and is greatly in need of death.
+.Pp
+There are a (great) number of undocumented variables.
+UTSL.
diff --git a/usr.sbin/sysinstall/sysinstall.h b/usr.sbin/sysinstall/sysinstall.h
new file mode 100644
index 0000000..6f860f5
--- /dev/null
+++ b/usr.sbin/sysinstall/sysinstall.h
@@ -0,0 +1,886 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 _SYSINSTALL_H_INCLUDE
+#define _SYSINSTALL_H_INCLUDE
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <setjmp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <dialog.h>
+#include "ui_objects.h"
+#include "dir.h"
+#include "colors.h"
+#include "dist.h"
+
+/*** Defines ***/
+
+#if defined(__i386__) || defined(__amd64__)
+#define WITH_SYSCONS
+#define WITH_MICE
+#endif
+
+#if defined(__i386__) || defined(__amd64__)
+#define WITH_SLICES
+#endif
+
+/* device limits */
+#define DEV_NAME_MAX 128 /* The maximum length of a device name */
+#define DEV_MAX 100 /* The maximum number of devices we'll deal with */
+#define INTERFACE_MAX 50 /* Maximum number of network interfaces we'll deal with */
+#define IO_ERROR -2 /* Status code for I/O error rather than normal EOF */
+
+/* Number of seconds to wait for data to come off even the slowest media */
+#define MEDIA_TIMEOUT 300
+
+/*
+ * I make some pretty gross assumptions about having a max of 50 chunks
+ * total - 8 slices and 42 partitions. I can't easily display many more
+ * than that on the screen at once!
+ *
+ * For 2.1 I'll revisit this and try to make it more dynamic, but since
+ * this will catch 99.99% of all possible cases, I'm not too worried.
+ */
+#define MAX_CHUNKS 40
+
+/* Internal environment variable names */
+#define DISK_PARTITIONED "_diskPartitioned"
+#define DISK_LABELLED "_diskLabelled"
+#define DISK_SELECTED "_diskSelected"
+#define SYSTEM_STATE "_systemState"
+#define RUNNING_ON_ROOT "_runningOnRoot"
+#define TCP_CONFIGURED "_tcpConfigured"
+
+/* Ones that can be tweaked from config files */
+#define VAR_BLANKTIME "blanktime"
+#define VAR_BOOTMGR "bootManager"
+#define VAR_BROWSER_BINARY "browserBinary"
+#define VAR_BROWSER_PACKAGE "browserPackage"
+#define VAR_COUNTRY "country"
+#define VAR_CPIO_VERBOSITY "cpioVerbose"
+#define VAR_DEBUG "debug"
+#define VAR_DESKSTYLE "_deskStyle"
+#define VAR_DISK "disk"
+#define VAR_DISKINTERACTIVE "diskInteractive"
+#define VAR_DISTS "dists"
+#define VAR_DIST_MAIN "distMain"
+#define VAR_DIST_SRC "distSRC"
+#define VAR_DIST_KERNEL "distKernel"
+#define VAR_DEDICATE_DISK "dedicateDisk"
+#define VAR_DOMAINNAME "domainname"
+#define VAR_EDITOR "editor"
+#define VAR_EXTRAS "ifconfig_"
+#define VAR_COMMAND "command"
+#define VAR_CONFIG_FILE "configFile"
+#define VAR_FIXIT_TTY "fixitTty"
+#define VAR_FTP_DIR "ftpDirectory"
+#define VAR_FTP_PASS "ftpPass"
+#define VAR_FTP_PATH "_ftpPath"
+#define VAR_FTP_PORT "ftpPort"
+#define VAR_FTP_STATE "ftpState"
+#define VAR_FTP_USER "ftpUser"
+#define VAR_FTP_HOST "ftpHost"
+#define VAR_HTTP_PATH "_httpPath"
+#define VAR_HTTP_PROXY "httpProxy"
+#define VAR_HTTP_PORT "httpPort"
+#define VAR_HTTP_HOST "httpHost"
+#define VAR_HTTP_FTP_MODE "httpFtpMode"
+#define VAR_GATEWAY "defaultrouter"
+#define VAR_GEOMETRY "geometry"
+#define VAR_HOSTNAME "hostname"
+#define VAR_IFCONFIG "ifconfig_"
+#define VAR_INSTALL_CFG "installConfig"
+#define VAR_INSTALL_ROOT "installRoot"
+#define VAR_IPADDR "ipaddr"
+#define VAR_IPV6_ENABLE "ipv6_enable"
+#define VAR_IPV6ADDR "ipv6addr"
+#define VAR_KERN_SECURELEVEL "kern_securelevel"
+#define VAR_KEYMAP "keymap"
+#define VAR_LABEL "label"
+#define VAR_LABEL_COUNT "labelCount"
+#define VAR_LINUX_ENABLE "linux_enable"
+#define VAR_MEDIA_TYPE "mediaType"
+#define VAR_MEDIA_TIMEOUT "MEDIA_TIMEOUT"
+#define VAR_MOUSED "moused_enable"
+#define VAR_MOUSED_FLAGS "moused_flags"
+#define VAR_MOUSED_PORT "moused_port"
+#define VAR_MOUSED_TYPE "moused_type"
+#define VAR_NAMESERVER "nameserver"
+#define VAR_NCPUS "ncpus"
+#define VAR_NETINTERACTIVE "netInteractive"
+#define VAR_NETMASK "netmask"
+#define VAR_NETWORK_DEVICE "netDev"
+#define VAR_NEWFS_ARGS "newfsArgs"
+#define VAR_NFS_PATH "nfs"
+#define VAR_NFS_HOST "nfsHost"
+#define VAR_NFS_V3 "nfs_use_v3"
+#define VAR_NFS_TCP "nfs_use_tcp"
+#define VAR_NFS_SECURE "nfs_reserved_port_only"
+#define VAR_NFS_SERVER "nfs_server_enable"
+#define VAR_NO_CONFIRM "noConfirm"
+#define VAR_NO_ERROR "noError"
+#define VAR_NO_HOLOSHELL "noHoloShell"
+#define VAR_NO_INET6 "noInet6"
+#define VAR_NO_WARN "noWarn"
+#define VAR_NO_USR "noUsr"
+#define VAR_NO_TMP "noTmp"
+#define VAR_NO_HOME "noHome"
+#define VAR_NONINTERACTIVE "nonInteractive"
+#define VAR_NOVELL "novell"
+#define VAR_RPCBIND_ENABLE "rpcbind_enable"
+#define VAR_NTPDATE_FLAGS "ntpdate_flags"
+#define VAR_PACKAGE "package"
+#define VAR_PARTITION "partition"
+#define VAR_PCNFSD "pcnfsd"
+#define VAR_PKG_TMPDIR "PKG_TMPDIR"
+#define VAR_PORTS_PATH "ports"
+#define VAR_PPP_ENABLE "ppp_enable"
+#define VAR_PPP_PROFILE "ppp_profile"
+#define VAR_RELNAME "releaseName"
+#define VAR_ROOT_SIZE "rootSize"
+#define VAR_ROUTER "router"
+#define VAR_ROUTER_ENABLE "router_enable"
+#define VAR_ROUTERFLAGS "router_flags"
+#define VAR_SENDMAIL_ENABLE "sendmail_enable"
+#define VAR_SERIAL_SPEED "serialSpeed"
+#define VAR_SLOW_ETHER "slowEthernetCard"
+#define VAR_SWAP_SIZE "swapSize"
+#define VAR_TRY_DHCP "tryDHCP"
+#define VAR_TRY_RTSOL "tryRTSOL"
+#define VAR_UFS_PATH "ufs"
+#define VAR_USR_SIZE "usrSize"
+#define VAR_VAR_SIZE "varSize"
+#define VAR_TMP_SIZE "tmpSize"
+#define VAR_HOME_SIZE "homeSize"
+#define VAR_TERM "TERM"
+#define VAR_CONSTERM "_consterm"
+#define VAR_KEEPRCCONF "keeprcconf"
+
+#ifdef PC98
+#define DEFAULT_COUNTRY "jp"
+#else
+#define DEFAULT_COUNTRY "us"
+#endif
+
+/* One MB worth of blocks */
+#define ONE_MEG 2048
+#define ONE_GIG (ONE_MEG * 1024)
+
+/* Which selection attributes to use */
+#define ATTR_SELECTED (ColorDisplay ? item_selected_attr : item_attr)
+#define ATTR_TITLE button_active_attr
+
+/* Handy strncpy() macro */
+#define SAFE_STRCPY(to, from) sstrncpy((to), (from), sizeof (to) - 1)
+
+/*** Types ***/
+typedef int Boolean;
+typedef struct disk Disk;
+typedef struct chunk Chunk;
+
+/* Bitfields for menu options */
+#define DMENU_NORMAL_TYPE 0x1 /* Normal dialog menu */
+#define DMENU_RADIO_TYPE 0x2 /* Radio dialog menu */
+#define DMENU_CHECKLIST_TYPE 0x4 /* Multiple choice menu */
+#define DMENU_SELECTION_RETURNS 0x8 /* Immediate return on item selection */
+
+typedef struct _dmenu {
+ int type; /* What sort of menu we are */
+ char *title; /* Our title */
+ char *prompt; /* Our prompt */
+ char *helpline; /* Line of help at bottom */
+ char *helpfile; /* Help file for "F1" */
+#if (__STDC_VERSION__ >= 199901L) || (__GNUC__ >= 3)
+ dialogMenuItem items[]; /* Array of menu items */
+#elif __GNUC__
+ dialogMenuItem items[0]; /* Array of menu items */
+#else
+#error "Create hack for C89 and K&R compilers."
+#endif
+} DMenu;
+
+/* An rc.conf variable */
+typedef struct _variable {
+ struct _variable *next;
+ char *name;
+ char *value;
+ int dirty;
+} Variable;
+
+#define NO_ECHO_OBJ(type) ((type) | (DITEM_NO_ECHO << 16))
+#define TYPE_OF_OBJ(type) ((type) & 0xff)
+#define ATTR_OF_OBJ(type) ((type) >> 16)
+
+/* A screen layout structure */
+typedef struct _layout {
+ int y; /* x & Y co-ordinates */
+ int x;
+ int len; /* The size of the dialog on the screen */
+ int maxlen; /* How much the user can type in ... */
+ char *prompt; /* The string for the prompt */
+ char *help; /* The display for the help line */
+ void *var; /* The var to set when this changes */
+ int type; /* The type of the dialog to create */
+ void *obj; /* The obj pointer returned by libdialog */
+} Layout;
+
+/* Layout array terminator. */
+#define LAYOUT_END { 0, 0, 0, 0, NULL, NULL, NULL, 0, NULL }
+
+typedef enum {
+ DEVICE_TYPE_NONE,
+ DEVICE_TYPE_DISK,
+ DEVICE_TYPE_FLOPPY,
+ DEVICE_TYPE_FTP,
+ DEVICE_TYPE_NETWORK,
+ DEVICE_TYPE_CDROM,
+ DEVICE_TYPE_DOS,
+ DEVICE_TYPE_UFS,
+ DEVICE_TYPE_NFS,
+ DEVICE_TYPE_ANY,
+ DEVICE_TYPE_HTTP,
+} DeviceType;
+
+/* CDROM mount codes */
+#define CD_UNMOUNTED 0
+#define CD_ALREADY_MOUNTED 1
+#define CD_WE_MOUNTED_IT 2
+
+/* A "device" from sysinstall's point of view */
+typedef struct _device {
+ char name[DEV_NAME_MAX];
+ char *description;
+ char *devname;
+ DeviceType type;
+ Boolean enabled;
+ Boolean (*init)(struct _device *dev);
+ FILE * (*get)(struct _device *dev, char *file, Boolean probe);
+ void (*shutdown)(struct _device *dev);
+ void *private;
+ unsigned int flags;
+ unsigned int volume;
+} Device;
+
+/* Some internal representations of partitions */
+typedef enum {
+ PART_NONE,
+ PART_SLICE,
+ PART_SWAP,
+ PART_FILESYSTEM,
+ PART_FAT,
+ PART_EFI
+} PartType;
+
+#define NEWFS_UFS_CMD "newfs"
+#define NEWFS_MSDOS_CMD "newfs_msdos"
+
+enum newfs_type { NEWFS_UFS, NEWFS_MSDOS, NEWFS_CUSTOM };
+#define NEWFS_UFS_STRING "UFS"
+#define NEWFS_MSDOS_STRING "FAT"
+#define NEWFS_CUSTOM_STRING "CST"
+
+/* The longest set of custom command line arguments we'll pass. */
+#define NEWFS_CMD_ARGS_MAX 256
+
+typedef struct _part_info {
+ char mountpoint[FILENAME_MAX];
+
+ /* Is invocation of newfs desired? */
+ Boolean do_newfs;
+
+ enum newfs_type newfs_type;
+ union {
+ struct {
+ char user_options[NEWFS_CMD_ARGS_MAX];
+ Boolean acls; /* unused */
+ Boolean multilabel; /* unused */
+ Boolean softupdates;
+ Boolean ufs1;
+ } newfs_ufs;
+ struct {
+ /* unused */
+ } newfs_msdos;
+ struct {
+ char command[NEWFS_CMD_ARGS_MAX];
+ } newfs_custom;
+ } newfs_data;
+} PartInfo;
+
+/* An option */
+typedef struct _opt {
+ char *name;
+ char *desc;
+ enum { OPT_IS_STRING, OPT_IS_INT, OPT_IS_FUNC, OPT_IS_VAR } type;
+ void *data;
+ void *aux;
+ char *(*check)(struct _opt *);
+} Option;
+
+/* Weird index nodey things we use for keeping track of package information */
+typedef enum { PACKAGE, PLACE } node_type; /* Types of nodes */
+
+typedef struct _pkgnode { /* A node in the reconstructed hierarchy */
+ struct _pkgnode *next; /* My next sibling */
+ node_type type; /* What am I? */
+ char *name; /* My name */
+ char *desc; /* My description (Hook) */
+ struct _pkgnode *kids; /* My little children */
+ void *data; /* A place to hang my data */
+} PkgNode;
+typedef PkgNode *PkgNodePtr;
+
+/* A single package */
+typedef struct _indexEntry { /* A single entry in an INDEX file */
+ char *name; /* name */
+ char *path; /* full path to port */
+ char *prefix; /* port prefix */
+ char *comment; /* one line description */
+ char *descrfile; /* path to description file */
+ char *deps; /* packages this depends on */
+ int depc; /* how many depend on me */
+ int installed; /* indicates if it is installed */
+ int vol_checked; /* disc volume last checked for */
+ char *maintainer; /* maintainer */
+ unsigned int volume; /* Volume of package */
+} IndexEntry;
+typedef IndexEntry *IndexEntryPtr;
+
+typedef int (*commandFunc)(char *key, void *data);
+
+#define HOSTNAME_FIELD_LEN 128
+#define IPADDR_FIELD_LEN 16
+#define EXTRAS_FIELD_LEN 128
+
+/* This is the structure that Network devices carry around in their private, erm, structures */
+typedef struct _devPriv {
+ int use_rtsol;
+ int use_dhcp;
+ char ipaddr[IPADDR_FIELD_LEN];
+ char netmask[IPADDR_FIELD_LEN];
+ char extras[EXTRAS_FIELD_LEN];
+} DevInfo;
+
+
+/*** Externs ***/
+extern jmp_buf BailOut; /* Used to get the heck out */
+extern int CDROMInitQuiet; /* Don't whine if mount(2) fails */
+extern int DebugFD; /* Where diagnostic output goes */
+extern Boolean Fake; /* Don't actually modify anything - testing */
+extern Boolean Restarting; /* Are we restarting sysinstall? */
+extern Boolean SystemWasInstalled; /* Did we install it? */
+extern Boolean RunningAsInit; /* Are we running stand-alone? */
+extern Boolean DialogActive; /* Is the dialog() stuff up? */
+extern Boolean ColorDisplay; /* Are we on a color display? */
+extern Boolean OnVTY; /* On a syscons VTY? */
+extern Boolean have_volumes; /* Media has multiple volumes */
+extern Variable *VarHead; /* The head of the variable chain */
+extern Device *mediaDevice; /* Where we're getting our distribution from */
+extern unsigned int Dists; /* Which distributions we want */
+extern unsigned int SrcDists; /* Which src distributions we want */
+extern unsigned int KernelDists; /* Which kernel dists we want */
+extern int BootMgr; /* Which boot manager to use */
+extern int StatusLine; /* Where to print our status messages */
+extern DMenu MenuCountry; /* Country menu */
+extern DMenu MenuInitial; /* Initial installation menu */
+extern DMenu MenuFixit; /* Fixit repair menu */
+#if defined(__i386__) || defined(__amd64__)
+#ifdef PC98
+extern DMenu MenuIPLType; /* Type of IPL to write on the disk */
+#else
+extern DMenu MenuMBRType; /* Type of MBR to write on the disk */
+#endif
+#endif
+extern DMenu MenuConfigure; /* Final configuration menu */
+extern DMenu MenuDocumentation; /* Documentation menu */
+extern DMenu MenuFTPOptions; /* FTP Installation options */
+extern DMenu MenuIndex; /* Index menu */
+extern DMenu MenuOptions; /* Installation options */
+extern DMenu MenuOptionsLanguage; /* Language options menu */
+extern DMenu MenuKLD; /* Prototype KLD menu */
+extern DMenu MenuConfig; /* Prototype config menu */
+extern DMenu MenuMedia; /* Media type menu */
+#ifdef WITH_MICE
+extern DMenu MenuMouse; /* Mouse type menu */
+#endif
+extern DMenu MenuMediaCDROM; /* CDROM media menu */
+extern DMenu MenuMediaDOS; /* DOS media menu */
+extern DMenu MenuMediaFloppy; /* Floppy media menu */
+extern DMenu MenuMediaFTP; /* FTP media menu */
+extern DMenu MenuNetworkDevice; /* Network device menu */
+extern DMenu MenuNTP; /* NTP time server menu */
+extern DMenu MenuSecurity; /* System security options menu */
+extern DMenu MenuSecurelevel; /* Securelevel menu */
+extern DMenu MenuStartup; /* Startup services menu */
+#ifdef WITH_SYSCONS
+extern DMenu MenuSyscons; /* System console configuration menu */
+extern DMenu MenuSysconsFont; /* System console font configuration menu */
+extern DMenu MenuSysconsKeymap; /* System console keymap configuration menu */
+extern DMenu MenuSysconsKeyrate; /* System console keyrate configuration menu */
+extern DMenu MenuSysconsSaver; /* System console saver configuration menu */
+extern DMenu MenuSysconsScrnmap; /* System console screenmap configuration menu */
+extern DMenu MenuSysconsTtys; /* System console terminal type menu */
+#endif
+extern DMenu MenuNetworking; /* Network configuration menu */
+extern DMenu MenuMTA; /* MTA selection menu */
+extern DMenu MenuInstallCustom; /* Custom Installation menu */
+extern DMenu MenuDistributions; /* Distribution menu */
+extern DMenu MenuDiskDevices; /* Disk type devices */
+extern DMenu MenuSubDistributions; /* Custom distribution menu */
+extern DMenu MenuSrcDistributions; /* Source distribution menu */
+extern DMenu MenuKernelDistributions;/* Kernel distribution menu */
+extern DMenu MenuHTMLDoc; /* HTML Documentation menu */
+extern DMenu MenuUsermgmt; /* User management menu */
+extern DMenu MenuFixit; /* Fixit floppy/CDROM/shell menu */
+extern int FixItMode; /* FixItMode starts shell on current device (ie Serial port) */
+extern const char * StartName; /* Which name we were started as */
+extern const char * ProgName; /* Program's proper name */
+extern int NCpus; /* # cpus on machine */
+extern int low_volume; /* Lowest volume number */
+extern int high_volume; /* Highest volume number */
+
+/* Important chunks. */
+extern Chunk *HomeChunk;
+extern Chunk *RootChunk;
+extern Chunk *SwapChunk;
+extern Chunk *TmpChunk;
+extern Chunk *UsrChunk;
+extern Chunk *VarChunk;
+#ifdef __ia64__
+extern Chunk *EfiChunk;
+#endif
+
+/* Stuff from libdialog which isn't properly declared outside */
+extern void display_helpfile(void);
+extern void display_helpline(WINDOW *w, int y, int width);
+
+/*** Prototypes ***/
+
+/* acpi.c */
+extern int acpi_detect(void);
+
+/* anonFTP.c */
+extern int configAnonFTP(dialogMenuItem *self);
+
+/* cdrom.c */
+extern Boolean mediaInitCDROM(Device *dev);
+extern FILE *mediaGetCDROM(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownCDROM(Device *dev);
+
+/* command.c */
+extern void command_clear(void);
+extern void command_sort(void);
+extern void command_execute(void);
+extern void command_shell_add(char *key, char *fmt, ...) __printflike(2, 3);
+extern void command_func_add(char *key, commandFunc func, void *data);
+
+/* config.c */
+extern void configEnvironmentRC_conf(void);
+extern void configEnvironmentResolv(char *config);
+extern void configRC_conf(void);
+extern int configFstab(dialogMenuItem *self);
+extern int configRC(dialogMenuItem *self);
+extern int configResolv(dialogMenuItem *self);
+extern int configPackages(dialogMenuItem *self);
+extern int configSaver(dialogMenuItem *self);
+extern int configSaverTimeout(dialogMenuItem *self);
+#ifdef WITH_LINUX
+extern int configLinux(dialogMenuItem *self);
+#endif
+extern int configNTP(dialogMenuItem *self);
+extern int configCountry(dialogMenuItem *self);
+extern int configUsers(dialogMenuItem *self);
+extern int configRouter(dialogMenuItem *self);
+extern int configPCNFSD(dialogMenuItem *self);
+extern int configInetd(dialogMenuItem *self);
+extern int configNFSServer(dialogMenuItem *self);
+extern int configMTAPostfix(dialogMenuItem *self);
+extern int configMTAExim(dialogMenuItem *self);
+extern int configRpcBind(dialogMenuItem *self);
+extern int configWriteRC_conf(dialogMenuItem *self);
+extern int configSecurelevel(dialogMenuItem *self);
+extern int configSecurelevelDisabled(dialogMenuItem *self);
+extern int configSecurelevelSecure(dialogMenuItem *self);
+extern int configSecurelevelHighlySecure(dialogMenuItem *self);
+extern int configSecurelevelNetworkSecure(dialogMenuItem *self);
+extern int configEtcTtys(dialogMenuItem *self);
+#ifdef __i386__
+extern int checkLoaderACPI(void);
+extern int configLoaderACPI(int);
+#endif
+
+/* devices.c */
+extern DMenu *deviceCreateMenu(DMenu *menu, DeviceType type, int (*hook)(dialogMenuItem *d),
+ int (*check)(dialogMenuItem *d));
+extern void deviceGetAll(void);
+extern void deviceReset(void);
+extern void deviceRescan(void);
+extern Device **deviceFind(char *name, DeviceType type);
+extern Device **deviceFindDescr(char *name, char *desc, DeviceType class);
+extern int deviceCount(Device **devs);
+extern Device *new_device(char *name);
+extern Device *deviceRegister(char *name, char *desc, char *devname, DeviceType type, Boolean enabled,
+ Boolean (*init)(Device *mediadev),
+ FILE * (*get)(Device *dev, char *file, Boolean probe),
+ void (*shutDown)(Device *mediadev),
+ void *private);
+extern Boolean dummyInit(Device *dev);
+extern FILE *dummyGet(Device *dev, char *dist, Boolean probe);
+extern void dummyShutdown(Device *dev);
+
+/* dhcp.c */
+extern int dhcpParseLeases(char *file, char *hostname, char *domain, char *nameserver,
+ char *ipaddr, char *gateway, char *netmask);
+
+/* disks.c */
+#ifdef WITH_SLICES
+extern void diskPartition(Device *dev);
+extern int diskPartitionEditor(dialogMenuItem *self);
+#endif
+extern int diskPartitionWrite(dialogMenuItem *self);
+extern int diskGetSelectCount(Device ***devs);
+
+/* dispatch.c */
+extern int dispatchCommand(char *command);
+extern int dispatch_load_floppy(dialogMenuItem *self);
+extern int dispatch_load_cdrom(dialogMenuItem *self);
+extern int dispatch_load_file_int(int);
+extern int dispatch_load_file(dialogMenuItem *self);
+extern int dispatch_load_menu(dialogMenuItem *self);
+
+
+/* dist.c */
+extern int distReset(dialogMenuItem *self);
+extern int distConfig(dialogMenuItem *self);
+extern int distSetCustom(dialogMenuItem *self);
+extern int distUnsetCustom(dialogMenuItem *self);
+extern int distSetDeveloper(dialogMenuItem *self);
+extern int distSetKernDeveloper(dialogMenuItem *self);
+extern int distSetUser(dialogMenuItem *self);
+extern int distSetMinimum(dialogMenuItem *self);
+extern int distSetEverything(dialogMenuItem *self);
+extern int distSetSrc(dialogMenuItem *self);
+extern int distSetKernel(dialogMenuItem *self);
+extern int distExtractAll(dialogMenuItem *self);
+extern int selectKernel(void);
+
+/* dmenu.c */
+extern int dmenuDisplayFile(dialogMenuItem *tmp);
+extern int dmenuSubmenu(dialogMenuItem *tmp);
+extern int dmenuSystemCommand(dialogMenuItem *tmp);
+extern int dmenuSystemCommandBox(dialogMenuItem *tmp);
+extern int dmenuExit(dialogMenuItem *tmp);
+extern int dmenuISetVariable(dialogMenuItem *tmp);
+extern int dmenuSetVariable(dialogMenuItem *tmp);
+extern int dmenuSetCountryVariable(dialogMenuItem *tmp);
+extern int dmenuSetKmapVariable(dialogMenuItem *tmp);
+extern int dmenuSetVariables(dialogMenuItem *tmp);
+extern int dmenuToggleVariable(dialogMenuItem *tmp);
+extern int dmenuSetFlag(dialogMenuItem *tmp);
+extern int dmenuSetValue(dialogMenuItem *tmp);
+extern int dmenuFindItem(DMenu *menu, const char *prompt, const char *title, void *data);
+extern void dmenuSetDefaultIndex(DMenu *menu, int *choice, int *scroll, int *curr, int *max);
+extern int dmenuSetDefaultItem(DMenu *menu, const char *prompt, const char *title, void *data,
+ int *choice, int *scroll, int *curr, int *max);
+extern Boolean dmenuOpen(DMenu *menu, int *choice, int *scroll, int *curr, int *max, Boolean buttons);
+extern Boolean dmenuOpenSimple(DMenu *menu, Boolean buttons);
+extern int dmenuVarCheck(dialogMenuItem *item);
+extern int dmenuVarsCheck(dialogMenuItem *item);
+extern int dmenuFlagCheck(dialogMenuItem *item);
+extern int dmenuRadioCheck(dialogMenuItem *item);
+
+/* doc.c */
+extern int docBrowser(dialogMenuItem *self);
+extern int docShowDocument(dialogMenuItem *self);
+
+/* dos.c */
+extern Boolean mediaCloseDOS(Device *dev, FILE *fp);
+extern Boolean mediaInitDOS(Device *dev);
+extern FILE *mediaGetDOS(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownDOS(Device *dev);
+
+/* floppy.c */
+extern int getRootFloppy(void);
+extern Boolean mediaInitFloppy(Device *dev);
+extern FILE *mediaGetFloppy(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownFloppy(Device *dev);
+
+/* ftp_strat.c */
+extern Boolean mediaCloseFTP(Device *dev, FILE *fp);
+extern Boolean mediaInitFTP(Device *dev);
+extern FILE *mediaGetFTP(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownFTP(Device *dev);
+
+/* http.c */
+extern Boolean mediaInitHTTP(Device *dev);
+extern FILE *mediaGetHTTP(Device *dev, char *file, Boolean probe);
+
+/* globals.c */
+extern void globalsInit(void);
+
+/* index.c */
+int index_read(FILE *fp, PkgNodePtr papa);
+int index_menu(PkgNodePtr root, PkgNodePtr top, PkgNodePtr plist, int *pos, int *scroll);
+void index_init(PkgNodePtr top, PkgNodePtr plist);
+void index_node_free(PkgNodePtr top, PkgNodePtr plist);
+void index_sort(PkgNodePtr top);
+void index_print(PkgNodePtr top, int level);
+int index_extract(Device *dev, PkgNodePtr top, PkgNodePtr who, Boolean depended, int current_volume);
+int index_initialize(char *path);
+PkgNodePtr index_search(PkgNodePtr top, char *str, PkgNodePtr *tp);
+
+/* install.c */
+extern Boolean checkLabels(Boolean whinge);
+extern int installCommit(dialogMenuItem *self);
+extern int installCustomCommit(dialogMenuItem *self);
+extern int installExpress(dialogMenuItem *self);
+extern int installStandard(dialogMenuItem *self);
+extern int installFixitHoloShell(dialogMenuItem *self);
+extern int installFixitCDROM(dialogMenuItem *self);
+extern int installFixitFloppy(dialogMenuItem *self);
+extern int installFixupBase(dialogMenuItem *self);
+extern int installFixupKernel(dialogMenuItem *self, int dists);
+extern int installUpgrade(dialogMenuItem *self);
+extern int installFilesystems(dialogMenuItem *self);
+extern int installVarDefaults(dialogMenuItem *self);
+extern void installEnvironment(void);
+extern Boolean copySelf(void);
+
+/* kget.c */
+extern int kget(char *out);
+
+/* keymap.c */
+extern int keymapMenuSelect(dialogMenuItem *self);
+extern int loadKeymap(const char *lang);
+
+/* label.c */
+extern int diskLabelEditor(dialogMenuItem *self);
+extern int diskLabelCommit(dialogMenuItem *self);
+
+/* makedevs.c (auto-generated) */
+extern const char termcap_ansi[];
+extern const char termcap_vt100[];
+extern const char termcap_cons25w[];
+extern const char termcap_cons25[];
+extern const char termcap_cons25_m[];
+extern const char termcap_cons25r[];
+extern const char termcap_cons25r_m[];
+extern const char termcap_cons25l1[];
+extern const char termcap_cons25l1_m[];
+extern const char termcap_xterm[];
+extern const u_char font_iso_8x16[];
+extern const u_char font_cp850_8x16[];
+extern const u_char font_cp866_8x16[];
+extern const u_char koi8_r2cp866[];
+extern u_char default_scrnmap[];
+
+/* media.c */
+extern char *cpioVerbosity(void);
+extern int mediaOpen(void);
+extern void mediaClose(void);
+extern int mediaTimeout(void);
+extern int mediaSetCDROM(dialogMenuItem *self);
+extern int mediaSetFloppy(dialogMenuItem *self);
+extern int mediaSetDOS(dialogMenuItem *self);
+extern int mediaSetFTP(dialogMenuItem *self);
+extern int mediaSetFTPActive(dialogMenuItem *self);
+extern int mediaSetFTPPassive(dialogMenuItem *self);
+extern int mediaSetHTTP(dialogMenuItem *self);
+extern int mediaSetUFS(dialogMenuItem *self);
+extern int mediaSetNFS(dialogMenuItem *self);
+extern int mediaSetFTPUserPass(dialogMenuItem *self);
+extern int mediaSetCPIOVerbosity(dialogMenuItem *self);
+extern int mediaGetType(dialogMenuItem *self);
+extern Boolean mediaExtractDist(char *dir, char *dist, FILE *fp);
+extern Boolean mediaExtractDistBegin(char *dir, int *fd, int *zpid, int *cpic);
+extern Boolean mediaExtractDistEnd(int zpid, int cpid);
+extern Boolean mediaVerify(void);
+extern FILE *mediaGenericGet(char *base, const char *file);
+
+/* misc.c */
+extern Boolean file_readable(char *fname);
+extern Boolean file_executable(char *fname);
+extern Boolean directory_exists(const char *dirname);
+extern char *root_bias(char *path);
+extern char *itoa(int value);
+extern char *string_concat(char *p1, char *p2);
+extern char *string_concat3(char *p1, char *p2, char *p3);
+extern char *string_prune(char *str);
+extern char *string_skipwhite(char *str);
+extern char *string_copy(char *s1, char *s2);
+extern char *pathBaseName(const char *path);
+extern void safe_free(void *ptr);
+extern void *safe_malloc(size_t size);
+extern void *safe_realloc(void *orig, size_t size);
+extern dialogMenuItem *item_add(dialogMenuItem *list, char *prompt, char *title,
+ int (*checked)(dialogMenuItem *self),
+ int (*fire)(dialogMenuItem *self),
+ void (*selected)(dialogMenuItem *self, int is_selected),
+ void *data, int *aux, int *curr, int *max);
+extern void items_free(dialogMenuItem *list, int *curr, int *max);
+extern int Mkdir(char *);
+extern int Mkdir_command(char *key, void *data);
+extern int Mount(char *, void *data);
+extern int Mount_msdosfs(char *mountp, void *devname);
+extern WINDOW *openLayoutDialog(char *helpfile, char *title, int x, int y, int width, int height);
+extern ComposeObj *initLayoutDialog(WINDOW *win, Layout *layout, int x, int y, int *max);
+extern int layoutDialogLoop(WINDOW *win, Layout *layout, ComposeObj **obj,
+ int *n, int max, int *cbutton, int *cancel);
+
+extern WINDOW *savescr(void);
+extern void restorescr(WINDOW *w);
+extern char *sstrncpy(char *dst, const char *src, int size);
+extern char *getsysctlbyname(const char *sysctlname);
+
+/* modules.c */
+extern void driverFloppyCheck(void);
+extern void moduleInitialize(void);
+extern int kldBrowser(dialogMenuItem *self);
+
+/* mouse.c */
+extern int mousedTest(dialogMenuItem *self);
+extern int mousedDisable(dialogMenuItem *self);
+extern int setMouseFlags(dialogMenuItem *self);
+
+/* mptable.c */
+extern int biosmptable_detect(void);
+
+/* msg.c */
+extern Boolean isDebug(void);
+extern void msgInfo(char *fmt, ...) __printf0like(1, 2);
+extern void msgYap(char *fmt, ...) __printflike(1, 2);
+extern void msgWarn(char *fmt, ...) __printflike(1, 2);
+extern void msgDebug(char *fmt, ...) __printflike(1, 2);
+extern void msgError(char *fmt, ...) __printflike(1, 2);
+extern void msgFatal(char *fmt, ...) __printflike(1, 2);
+extern void msgConfirm(char *fmt, ...) __printflike(1, 2);
+extern void msgNotify(char *fmt, ...) __printflike(1, 2);
+extern void msgWeHaveOutput(char *fmt, ...) __printflike(1, 2);
+extern int msgYesNo(char *fmt, ...) __printflike(1, 2);
+extern int msgNoYes(char *fmt, ...) __printflike(1, 2);
+extern char *msgGetInput(char *buf, char *fmt, ...) __printflike(2, 3);
+extern int msgSimpleConfirm(char *);
+extern int msgSimpleNotify(char *);
+
+/* network.c */
+extern Boolean mediaInitNetwork(Device *dev);
+extern void mediaShutdownNetwork(Device *dev);
+
+/* nfs.c */
+extern Boolean mediaInitNFS(Device *dev);
+extern FILE *mediaGetNFS(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownNFS(Device *dev);
+
+/* options.c */
+extern int optionsEditor(dialogMenuItem *self);
+
+/* package.c */
+extern int packageAdd(dialogMenuItem *self);
+extern int package_add(char *name);
+extern int package_extract(Device *dev, char *name, Boolean depended);
+extern Boolean package_installed(char *name);
+
+/* system.c */
+extern void systemInitialize(int argc, char **argv);
+extern void systemShutdown(int status);
+extern int execExecute(char *cmd, char *name);
+extern int systemExecute(char *cmd);
+extern void systemSuspendDialog(void);
+extern void systemResumeDialog(void);
+extern int systemDisplayHelp(char *file);
+extern char *systemHelpFile(char *file, char *buf);
+extern void systemChangeFont(const u_char font[]);
+extern void systemChangeLang(char *lang);
+extern void systemChangeTerminal(char *color, const u_char c_termcap[], char *mono, const u_char m_termcap[]);
+extern void systemChangeScreenmap(const u_char newmap[]);
+extern void systemCreateHoloshell(void);
+extern int vsystem(char *fmt, ...) __printflike(1, 2);
+
+/* tcpip.c */
+extern int tcpOpenDialog(Device *dev);
+extern int tcpMenuSelect(dialogMenuItem *self);
+extern Device *tcpDeviceSelect(void);
+
+/* termcap.c */
+extern int set_termcap(void);
+
+/* ttys.c */
+extern void configTtys(void);
+
+/* ufs.c */
+extern void mediaShutdownUFS(Device *dev);
+extern Boolean mediaInitUFS(Device *dev);
+extern FILE *mediaGetUFS(Device *dev, char *file, Boolean probe);
+
+/* user.c */
+extern int userAddGroup(dialogMenuItem *self);
+extern int userAddUser(dialogMenuItem *self);
+
+/* variable.c */
+extern void variable_set(char *var, int dirty);
+extern void variable_set2(char *name, char *value, int dirty);
+extern char *variable_get(char *var);
+extern int variable_cmp(char *var, char *value);
+extern void variable_unset(char *var);
+extern char *variable_get_value(char *var, char *prompt, int dirty);
+extern int variable_check(char *data);
+extern int variable_check2(char *data);
+extern int dump_variables(dialogMenuItem *self);
+extern void free_variables(void);
+extern void pvariable_set(char *var);
+extern char *pvariable_get(char *var);
+
+/* wizard.c */
+extern void slice_wizard(Disk *d);
+
+/*
+ * Macros. Please find a better place for us!
+ */
+#define DEVICE_INIT(d) ((d) != NULL ? (d)->init((d)) : (Boolean)0)
+#define DEVICE_GET(d, b, f) ((d) != NULL ? (d)->get((d), (b), (f)) : NULL)
+#define DEVICE_SHUTDOWN(d) ((d) != NULL ? (d)->shutdown((d)) : (void)0)
+
+#ifdef USE_GZIP
+#define UNZIPPER "gunzip"
+#else
+#define UNZIPPER "bunzip2"
+#endif
+
+#endif
+/* _SYSINSTALL_H_INCLUDE */
diff --git a/usr.sbin/sysinstall/system.c b/usr.sbin/sysinstall/system.c
new file mode 100644
index 0000000..a0e60d0
--- /dev/null
+++ b/usr.sbin/sysinstall/system.c
@@ -0,0 +1,549 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Jordan Hubbard
+ *
+ * My contributions are in the public domain.
+ *
+ * Parts of this file are also blatantly stolen from Poul-Henning Kamp's
+ * previous version of sysinstall, and as such fall under his "BEERWARE license"
+ * so buy him a beer if you like it! Buy him a beer for me, too!
+ * Heck, get him completely drunk and send me pictures! :-)
+ */
+
+#include "sysinstall.h"
+#include <signal.h>
+#include <termios.h>
+#include <sys/param.h>
+#include <sys/reboot.h>
+#include <sys/consio.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <ufs/ufs/ufsmount.h>
+
+
+/* Where we stick our temporary expanded doc file */
+#define DOC_TMP_DIR "/tmp/.doc"
+#define DOC_TMP_FILE "/tmp/.doc/doc.tmp"
+
+static pid_t ehs_pid;
+
+/*
+ * Handle interrupt signals - this probably won't work in all cases
+ * due to our having bogotified the internal state of dialog or curses,
+ * but we'll give it a try.
+ */
+static int
+intr_continue(dialogMenuItem *self)
+{
+ return DITEM_LEAVE_MENU;
+}
+
+static int
+intr_reboot(dialogMenuItem *self)
+{
+ systemShutdown(-1);
+ /* NOTREACHED */
+ return 0;
+}
+
+static int
+intr_restart(dialogMenuItem *self)
+{
+ int ret, fd, fdmax;
+
+ mediaClose();
+ free_variables();
+ fdmax = getdtablesize();
+ for (fd = 3; fd < fdmax; fd++)
+ close(fd);
+ ret = execl(StartName, StartName, "-restart", (char *)NULL);
+ msgDebug("execl failed (%s)\n", strerror(errno));
+ /* NOTREACHED */
+ return -1;
+}
+
+static dialogMenuItem intrmenu[] = {
+ { "Abort", "Abort the installation", NULL, intr_reboot },
+ { "Restart", "Restart the installation program", NULL, intr_restart },
+ { "Continue", "Continue the installation", NULL, intr_continue },
+};
+
+
+static void
+handle_intr(int sig)
+{
+ WINDOW *save = savescr();
+
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (OnVTY) {
+ ioctl(0, VT_ACTIVATE, 1); /* Switch back */
+ msgInfo(NULL);
+ }
+ (void)dialog_menu("Installation interrupt",
+ "Do you want to abort the installation?",
+ -1, -1, 3, -3, intrmenu, NULL, NULL, NULL);
+ restorescr(save);
+}
+
+#if 0
+/*
+ * Harvest children if we are init.
+ */
+static void
+reap_children(int sig)
+{
+ int errbak = errno;
+
+ while (waitpid(-1, NULL, WNOHANG) > 0)
+ ;
+ errno = errbak;
+}
+#endif
+
+/* Expand a file into a convenient location, nuking it each time */
+static char *
+expand(char *fname)
+{
+ char *unzipper = RunningAsInit ? "/stand/" UNZIPPER : "/usr/bin/" UNZIPPER;
+
+ if (!directory_exists(DOC_TMP_DIR)) {
+ Mkdir(DOC_TMP_DIR);
+ if (chown(DOC_TMP_DIR, 0, 0) < 0)
+ return NULL;
+ if (chmod(DOC_TMP_DIR, S_IRWXU) < 0)
+ return NULL;
+ }
+ else
+ unlink(DOC_TMP_FILE);
+ if (!file_readable(fname) || vsystem("%s < %s > %s", unzipper, fname,
+ DOC_TMP_FILE))
+ return NULL;
+ return DOC_TMP_FILE;
+}
+
+/* Initialize system defaults */
+void
+systemInitialize(int argc, char **argv)
+{
+ size_t i;
+ int boothowto;
+ sigset_t signalset;
+
+ signal(SIGINT, SIG_IGN);
+ globalsInit();
+
+ i = sizeof(boothowto);
+ if (!sysctlbyname("debug.boothowto", &boothowto, &i, NULL, 0) &&
+ (i == sizeof(boothowto)) && (boothowto & RB_VERBOSE))
+ variable_set2(VAR_DEBUG, "YES", 0);
+
+ /* Are we running as init? */
+ if (getpid() == 1) {
+ struct ufs_args ufs_args;
+ int fd;
+
+ RunningAsInit = 1;
+ setsid();
+ close(0);
+ fd = open("/dev/ttyv0", O_RDWR);
+ if (fd == -1) {
+ fd = open("/dev/console", O_RDWR); /* fallback */
+ variable_set2(VAR_FIXIT_TTY, "serial", 0); /* give fixit a hint */
+ } else
+ OnVTY = TRUE;
+ /*
+ * To make _sure_ we're on a VTY and don't have /dev/console switched
+ * away to a serial port or something, attempt to set the cursor appearance.
+ */
+ if (OnVTY) {
+ int fd2, type;
+
+ type = 0; /* normal */
+ if ((fd2 = open("/dev/console", O_RDWR)) != -1) {
+ if (ioctl(fd2, CONS_CURSORTYPE, &type) == -1) {
+ OnVTY = FALSE;
+ variable_set2(VAR_FIXIT_TTY, "serial", 0); /* Tell Fixit
+ the console
+ type */
+ close(fd); close(fd2);
+ open("/dev/console", O_RDWR);
+ }
+ else
+ close(fd2);
+ }
+ }
+ close(1); dup(0);
+ close(2); dup(0);
+ printf("%s running as init on %s\n", argv[0], OnVTY ? "vty0" : "serial console");
+ ioctl(0, TIOCSCTTY, (char *)NULL);
+ setlogin("root");
+ setenv("PATH", "/stand:/bin:/sbin:/usr/sbin:/usr/bin:/mnt/bin:/mnt/sbin:/mnt/usr/sbin:/mnt/usr/bin", 1);
+ setbuf(stdin, 0);
+ setbuf(stderr, 0);
+#if 0
+ signal(SIGCHLD, reap_children);
+#endif
+ memset(&ufs_args, 0, sizeof(ufs_args));
+ mount("ufs", "/", MNT_UPDATE, &ufs_args);
+ }
+ else {
+ char hname[256];
+
+ /* Initalize various things for a multi-user environment */
+ if (!gethostname(hname, sizeof hname))
+ variable_set2(VAR_HOSTNAME, hname, 0);
+ }
+
+ if (set_termcap() == -1) {
+ printf("Can't find terminal entry\n");
+ exit(-1);
+ }
+
+ /* XXX - libdialog has particularly bad return value checking */
+ init_dialog();
+
+ /* If we haven't crashed I guess dialog is running ! */
+ DialogActive = TRUE;
+
+ /* Make sure HOME is set for those utilities that need it */
+ if (!getenv("HOME"))
+ setenv("HOME", "/", 1);
+ signal(SIGINT, handle_intr);
+ /*
+ * Make sure we can be interrupted even if we were re-executed
+ * from an interrupt.
+ */
+ sigemptyset(&signalset);
+ sigaddset(&signalset, SIGINT);
+ sigprocmask(SIG_UNBLOCK, &signalset, NULL);
+
+ (void)vsystem("rm -rf %s", DOC_TMP_DIR);
+}
+
+/* Close down and prepare to exit */
+void
+systemShutdown(int status)
+{
+ /* If some media is open, close it down */
+ if (status >=0) {
+ if (mediaDevice != NULL && mediaDevice->type == DEVICE_TYPE_CDROM) {
+ mediaClose();
+ msgConfirm("Be sure to remove the media from the drive.");
+ } else
+ mediaClose();
+ }
+
+ /* write out any changes to rc.conf .. */
+ configRC_conf();
+
+ /* Shut down the dialog library */
+ if (DialogActive) {
+ end_dialog();
+ DialogActive = FALSE;
+ }
+
+ /* Shut down curses */
+ endwin();
+
+ /* If we have a temporary doc dir lying around, nuke it */
+ (void)vsystem("rm -rf %s", DOC_TMP_DIR);
+
+ /* REALLY exit! */
+ if (RunningAsInit) {
+ /* Put the console back */
+ ioctl(0, VT_ACTIVATE, 2);
+#if defined(__sparc64__)
+ reboot(RB_HALT);
+#else
+ reboot(RB_AUTOBOOT);
+#endif
+ }
+ else
+ exit(status);
+}
+
+/* Run some general command */
+int
+systemExecute(char *command)
+{
+ int status;
+ struct termios foo;
+ WINDOW *w = savescr();
+
+ dialog_clear();
+ dialog_update();
+ end_dialog();
+ DialogActive = FALSE;
+ if (tcgetattr(0, &foo) != -1) {
+ foo.c_cc[VERASE] = '\010';
+ tcsetattr(0, TCSANOW, &foo);
+ }
+ if (!Fake)
+ status = system(command);
+ else {
+ status = 0;
+ msgDebug("systemExecute: Faked execution of `%s'\n", command);
+ }
+ DialogActive = TRUE;
+ restorescr(w);
+ return status;
+}
+
+/* suspend/resume libdialog/curses screen */
+static WINDOW *oldW;
+
+void
+systemSuspendDialog(void)
+{
+
+ oldW = savescr();
+ dialog_clear();
+ dialog_update();
+ end_dialog();
+ DialogActive = FALSE;
+}
+
+void
+systemResumeDialog(void)
+{
+
+ DialogActive = TRUE;
+ restorescr(oldW);
+}
+
+/* Display a help file in a filebox */
+int
+systemDisplayHelp(char *file)
+{
+ char *fname = NULL;
+ char buf[FILENAME_MAX];
+ int ret = 0;
+ WINDOW *w = savescr();
+
+ fname = systemHelpFile(file, buf);
+ if (!fname) {
+ snprintf(buf, FILENAME_MAX, "The %s file is not provided on this particular floppy image.", file);
+ use_helpfile(NULL);
+ use_helpline(NULL);
+ dialog_mesgbox("Sorry!", buf, -1, -1);
+ ret = 1;
+ }
+ else {
+ use_helpfile(NULL);
+ use_helpline(NULL);
+ dialog_textbox(file, fname, LINES, COLS);
+ }
+ restorescr(w);
+ return ret;
+}
+
+char *
+systemHelpFile(char *file, char *buf)
+{
+ if (!file)
+ return NULL;
+ if (file[0] == '/')
+ return file;
+ snprintf(buf, FILENAME_MAX, "/stand/help/%s.hlp.gz", file);
+ if (file_readable(buf))
+ return expand(buf);
+ snprintf(buf, FILENAME_MAX, "/stand/help/%s.hlp", file);
+ if (file_readable(buf))
+ return expand(buf);
+ snprintf(buf, FILENAME_MAX, "/stand/help/%s.TXT.gz", file);
+ if (file_readable(buf))
+ return expand(buf);
+ snprintf(buf, FILENAME_MAX, "/stand/help/%s.TXT", file);
+ if (file_readable(buf))
+ return expand(buf);
+ snprintf(buf, FILENAME_MAX, "/usr/src/usr.sbin/%s/help/%s.hlp", ProgName,
+ file);
+ if (file_readable(buf))
+ return buf;
+ snprintf(buf, FILENAME_MAX, "/usr/src/usr.sbin/%s/help/%s.TXT", ProgName,
+ file);
+ if (file_readable(buf))
+ return buf;
+ return NULL;
+}
+
+void
+systemChangeTerminal(char *color, const u_char c_term[],
+ char *mono, const u_char m_term[])
+{
+ if (OnVTY) {
+ int setupterm(char *color, int, int *);
+
+ if (ColorDisplay) {
+ setenv("TERM", color, 1);
+ setenv("TERMCAP", c_term, 1);
+ reset_shell_mode();
+ setterm(color);
+ cbreak(); noecho();
+ }
+ else {
+ setenv("TERM", mono, 1);
+ setenv("TERMCAP", m_term, 1);
+ reset_shell_mode();
+ setterm(mono);
+ cbreak(); noecho();
+ }
+ }
+ clear();
+ refresh();
+ dialog_clear();
+}
+
+int
+vsystem(char *fmt, ...)
+{
+ va_list args;
+ int pstat;
+ pid_t pid;
+ int omask;
+ sig_t intsave, quitsave;
+ char *cmd;
+ int i;
+ struct stat sb;
+
+ cmd = (char *)alloca(FILENAME_MAX);
+ cmd[0] = '\0';
+ va_start(args, fmt);
+ vsnprintf(cmd, FILENAME_MAX, fmt, args);
+ va_end(args);
+
+ omask = sigblock(sigmask(SIGCHLD));
+ if (Fake) {
+ msgDebug("vsystem: Faked execution of `%s'\n", cmd);
+ return 0;
+ }
+ if (isDebug())
+ msgDebug("Executing command `%s'\n", cmd);
+ pid = fork();
+ if (pid == -1) {
+ (void)sigsetmask(omask);
+ i = 127;
+ }
+ else if (!pid) { /* Junior */
+ (void)sigsetmask(omask);
+ if (DebugFD != -1) {
+ dup2(DebugFD, 0);
+ dup2(DebugFD, 1);
+ dup2(DebugFD, 2);
+ }
+ else {
+ close(1); open("/dev/null", O_WRONLY);
+ dup2(1, 2);
+ }
+ if (stat("/stand/sh", &sb) == 0)
+ execl("/stand/sh", "/stand/sh", "-c", cmd, (char *)NULL);
+ else
+ execl("/bin/sh", "/bin/sh", "-c", cmd, (char *)NULL);
+ exit(1);
+ }
+ else {
+ intsave = signal(SIGINT, SIG_IGN);
+ quitsave = signal(SIGQUIT, SIG_IGN);
+ pid = waitpid(pid, &pstat, 0);
+ (void)sigsetmask(omask);
+ (void)signal(SIGINT, intsave);
+ (void)signal(SIGQUIT, quitsave);
+ i = (pid == -1) ? -1 : WEXITSTATUS(pstat);
+ if (isDebug())
+ msgDebug("Command `%s' returns status of %d\n", cmd, i);
+ }
+ return i;
+}
+
+void
+systemCreateHoloshell(void)
+{
+ int waitstatus;
+
+ if ((FixItMode || OnVTY) && RunningAsInit) {
+
+ if (ehs_pid != 0) {
+ int pstat;
+
+ if (kill(ehs_pid, 0) == 0) {
+
+ if (msgNoYes("There seems to be an emergency holographic shell\n"
+ "already running on VTY 4.\n\n"
+ "Kill it and start a new one?"))
+ return;
+
+ /* try cleaning up as much as possible */
+ (void) kill(ehs_pid, SIGHUP);
+ sleep(1);
+ (void) kill(ehs_pid, SIGKILL);
+ }
+
+ /* avoid too many zombies */
+ (void) waitpid(ehs_pid, &pstat, WNOHANG);
+ }
+
+ if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0)
+ systemSuspendDialog(); /* must be before the fork() */
+ if ((ehs_pid = fork()) == 0) {
+ int i, fd;
+ struct termios foo;
+ extern int login_tty(int);
+
+ ioctl(0, TIOCNOTTY, NULL);
+ for (i = getdtablesize(); i >= 0; --i)
+ close(i);
+ if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0)
+ fd = open("/dev/console", O_RDWR);
+ else
+ fd = open("/dev/ttyv3", O_RDWR);
+ ioctl(0, TIOCSCTTY, &fd);
+ dup2(0, 1);
+ dup2(0, 2);
+ DebugFD = 2;
+ if (login_tty(fd) == -1)
+ msgDebug("Doctor: I can't set the controlling terminal.\n");
+ signal(SIGTTOU, SIG_IGN);
+ if (tcgetattr(fd, &foo) != -1) {
+ foo.c_cc[VERASE] = '\010';
+ if (tcsetattr(fd, TCSANOW, &foo) == -1)
+ msgDebug("Doctor: I'm unable to set the erase character.\n");
+ }
+ else
+ msgDebug("Doctor: I'm unable to get the terminal attributes!\n");
+ if (strcmp(variable_get(VAR_FIXIT_TTY), "serial") == 0) {
+ printf("Type ``exit'' in this fixit shell to resume sysinstall.\n\n");
+ fflush(stdout);
+ }
+ execlp("sh", "-sh", 0);
+ msgDebug("Was unable to execute sh for Holographic shell!\n");
+ exit(1);
+ }
+ else {
+ if (strcmp(variable_get(VAR_FIXIT_TTY), "standard") == 0) {
+ WINDOW *w = savescr();
+
+ msgNotify("Starting an emergency holographic shell on VTY4");
+ sleep(2);
+ restorescr(w);
+ }
+ else {
+ (void)waitpid(ehs_pid, &waitstatus, 0); /* we only wait for
+ shell to finish
+ in serial mode
+ since there is no
+ virtual console */
+ systemResumeDialog();
+ }
+ }
+ }
+}
diff --git a/usr.sbin/sysinstall/tcpip.c b/usr.sbin/sysinstall/tcpip.c
new file mode 100644
index 0000000..18849fc
--- /dev/null
+++ b/usr.sbin/sysinstall/tcpip.c
@@ -0,0 +1,701 @@
+/*
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Gary J Palmer. All rights reserved.
+ * Copyright (c) 1996
+ * Jordan K. Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 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, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * All kinds of hacking also performed by jkh on this code. Don't
+ * blame Gary for every bogosity you see here.. :-)
+ *
+ * -jkh
+ */
+
+#include "sysinstall.h"
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <paths.h>
+
+/* The help file for the TCP/IP setup screen */
+#define TCP_HELPFILE "tcp"
+
+/* These are nasty, but they make the layout structure a lot easier ... */
+
+static char hostname[HOSTNAME_FIELD_LEN], domainname[HOSTNAME_FIELD_LEN],
+ gateway[IPADDR_FIELD_LEN], nameserver[INET6_ADDRSTRLEN];
+static int okbutton, cancelbutton;
+static char ipaddr[IPADDR_FIELD_LEN], netmask[IPADDR_FIELD_LEN], extras[EXTRAS_FIELD_LEN];
+static char ipv6addr[INET6_ADDRSTRLEN];
+
+/* What the screen size is meant to be */
+#define TCP_DIALOG_Y 0
+#define TCP_DIALOG_X 8
+#define TCP_DIALOG_WIDTH COLS - 16
+#define TCP_DIALOG_HEIGHT LINES - 2
+
+static Layout layout[] = {
+#define LAYOUT_HOSTNAME 0
+ { 1, 2, 25, HOSTNAME_FIELD_LEN - 1,
+ "Host:", "Your fully-qualified hostname, e.g. foo.bar.com",
+ hostname, STRINGOBJ, NULL },
+#define LAYOUT_DOMAINNAME 1
+ { 1, 35, 20, HOSTNAME_FIELD_LEN - 1,
+ "Domain:",
+ "The name of the domain that your machine is in, e.g. bar.com",
+ domainname, STRINGOBJ, NULL },
+#define LAYOUT_GATEWAY 2
+ { 5, 2, 18, IPADDR_FIELD_LEN - 1,
+ "IPv4 Gateway:",
+ "IPv4 address of host forwarding packets to non-local destinations",
+ gateway, STRINGOBJ, NULL },
+#define LAYOUT_NAMESERVER 3
+ { 5, 35, 18, INET6_ADDRSTRLEN - 1,
+ "Name server:", "IPv4 or IPv6 address of your local DNS server",
+ nameserver, STRINGOBJ, NULL },
+#define LAYOUT_IPADDR 4
+ { 10, 10, 18, IPADDR_FIELD_LEN - 1,
+ "IPv4 Address:",
+ "The IPv4 address to be used for this interface",
+ ipaddr, STRINGOBJ, NULL },
+#define LAYOUT_NETMASK 5
+ { 10, 35, 18, IPADDR_FIELD_LEN - 1,
+ "Netmask:",
+ "The netmask for this interface, e.g. 0xffffff00 for a class C network",
+ netmask, STRINGOBJ, NULL },
+#define LAYOUT_EXTRAS 6
+ { 14, 10, 37, HOSTNAME_FIELD_LEN - 1,
+ "Extra options to ifconfig (usually empty):",
+ "Any interface-specific options to ifconfig you would like to add",
+ extras, STRINGOBJ, NULL },
+#define LAYOUT_OKBUTTON 7
+ { 19, 15, 0, 0,
+ "OK", "Select this if you are happy with these settings",
+ &okbutton, BUTTONOBJ, NULL },
+#define LAYOUT_CANCELBUTTON 8
+ { 19, 35, 0, 0,
+ "CANCEL", "Select this if you wish to cancel this screen",
+ &cancelbutton, BUTTONOBJ, NULL },
+ LAYOUT_END,
+};
+
+#define _validByte(b) ((b) >= 0 && (b) <= 255)
+
+/* whine */
+static void
+feepout(char *msg)
+{
+ beep();
+ msgConfirm("%s", msg);
+}
+
+/* Verify IP address integrity */
+static int
+verifyIP(char *ip, unsigned long *mask, unsigned long *out)
+{
+ long a, b, c, d;
+ char *endptr, *endptr_prev;
+
+ unsigned long parsedip;
+ unsigned long max_addr = (255 << 24) | (255 << 16) | (255 << 8) | 255;
+
+ if (ip == NULL)
+ return 0;
+ a = strtol(ip, &endptr, 10);
+ if (endptr - ip == 0 || *endptr++ != '.')
+ return 0;
+ endptr_prev = endptr;
+ b = strtol(endptr, &endptr, 10);
+ if (endptr - endptr_prev == 0 || *endptr++ != '.')
+ return 0;
+ endptr_prev = endptr;
+ c = strtol(endptr, &endptr, 10);
+ if (endptr - endptr_prev == 0 || *endptr++ != '.')
+ return 0;
+ endptr_prev = endptr;
+ d = strtol(endptr, &endptr, 10);
+ if (*endptr != '\0' || endptr - endptr_prev == 0)
+ return 0;
+ if (!_validByte(a) || !_validByte(b) || !_validByte(c) || !_validByte(d))
+ return 0;
+ parsedip = (a << 24) | (b << 16) | (c << 8) | d;
+ if (out)
+ *out = parsedip;
+ /*
+ * The ip address must not be network or broadcast address.
+ */
+ if (mask && ((parsedip == (parsedip & *mask)) ||
+ (parsedip == ((parsedip & *mask) + max_addr - *mask))))
+ return 0;
+ return 1;
+}
+
+static int
+verifyIP6(char *ip)
+{
+ struct addrinfo hints, *res;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
+ if (getaddrinfo(ip, NULL, &hints, &res) == 0) {
+ freeaddrinfo(res);
+ return 1;
+ }
+ return 0;
+}
+
+/* Verify IPv4 netmask as being well-formed as
+ a 0x or AAA.BBB.CCC.DDD mask */
+static int
+verifyNetmask(const char *netmask, unsigned long *out)
+{
+ unsigned long mask;
+ long tmp;
+ char *endptr;
+
+ if (netmask[0] == '0' && (netmask[1] == 'x' || netmask[1] == 'X')) {
+ /* Parse out hex mask */
+ mask = strtoul(netmask, &endptr, 0);
+ if (*endptr != '\0')
+ return 0;
+ } else {
+ /* Parse out quad decimal mask */
+ tmp = strtoul(netmask, &endptr, 10);
+ if (!_validByte(tmp) || *endptr++ != '.')
+ return 0;
+ mask = tmp;
+ tmp = strtoul(endptr, &endptr, 10);
+ if (!_validByte(tmp) || *endptr++ != '.')
+ return 0;
+ mask = (mask << 8) + tmp;
+ tmp = strtoul(endptr, &endptr, 10);
+ if (!_validByte(tmp) || *endptr++ != '.')
+ return 0;
+ mask = (mask << 8) + tmp;
+ tmp = strtoul(endptr, &endptr, 10);
+ if (!_validByte(tmp) || *endptr++ != '\0')
+ return 0;
+ mask = (mask << 8) + tmp;
+ }
+ /* Verify that we have a continous netmask */
+ if ((((-mask & mask) - 1) | mask) != 0xffffffff)
+ return 0;
+ if (out)
+ *out = mask;
+ return 1;
+}
+
+static int
+verifyGW(char *gw, unsigned long *ip, unsigned long *mask)
+{
+ unsigned long parsedgw;
+
+ if (!verifyIP(gw, mask, &parsedgw))
+ return 0;
+ /* Gateway needs to be within the set of IPs reachable through the
+ interface */
+ if (ip && mask && ((parsedgw & *mask) != (*ip & *mask)))
+ return 0;
+ return 1;
+}
+
+/* Check for the settings on the screen - the per-interface stuff is
+ moved to the main handling code now to do it on the fly - sigh */
+static int
+verifySettings(void)
+{
+ unsigned long parsedip;
+ unsigned long parsednetmask;
+
+ if (!hostname[0])
+ feepout("Must specify a host name of some sort!");
+ else if (netmask[0] && !verifyNetmask(netmask, &parsednetmask))
+ feepout("Invalid netmask value");
+ else if (nameserver[0] && !verifyIP(nameserver, NULL, NULL) &&
+ !verifyIP6(nameserver))
+ feepout("Invalid name server IP address specified");
+ else if (ipaddr[0] && !verifyIP(ipaddr, &parsednetmask, &parsedip))
+ feepout("Invalid IPv4 address");
+ else if (gateway[0] && strcmp(gateway, "NO") &&
+ !verifyGW(gateway, ipaddr[0] ? &parsedip : NULL,
+ netmask[0] ? &parsednetmask : NULL))
+ feepout("Invalid gateway IPv4 address specified");
+ else
+ return 1;
+ return 0;
+}
+
+static void
+dhcpGetInfo(Device *devp)
+{
+ char leasefile[PATH_MAX];
+
+ snprintf(leasefile, sizeof(leasefile), "%sdhclient.leases.%s",
+ _PATH_VARDB, devp->name);
+ /* If it fails, do it the old-fashioned way */
+ if (dhcpParseLeases(leasefile, hostname, domainname,
+ nameserver, ipaddr, gateway, netmask) == -1) {
+ FILE *ifp;
+ char *cp, cmd[256], data[2048];
+ int i, j;
+
+ /* Bah, now we have to kludge getting the information from ifconfig */
+ snprintf(cmd, sizeof cmd, "ifconfig %s", devp->name);
+ ifp = popen(cmd, "r");
+ if (ifp) {
+ j = fread(data, 1, sizeof(data), ifp);
+ fclose(ifp);
+ if (j < 0) /* paranoia */
+ j = 0;
+ data[j] = '\0';
+ if (isDebug())
+ msgDebug("DHCP configured interface returns %s\n", data);
+ /* XXX This is gross as it assumes a certain ordering to
+ ifconfig's output! XXX */
+ if ((cp = strstr(data, "inet ")) != NULL) {
+ i = 0;
+ cp += 5; /* move over keyword */
+ while (*cp != ' ')
+ ipaddr[i++] = *(cp++);
+ ipaddr[i] = '\0';
+ if (!strncmp(++cp, "netmask", 7)) {
+ i = 0;
+ cp += 8;
+ while (*cp != ' ')
+ netmask[i++] = *(cp++);
+ netmask[i] = '\0';
+ }
+ }
+ }
+ }
+
+ /* If we didn't get a name server value, hunt for it in resolv.conf */
+ if (!nameserver[0] && file_readable("/etc/resolv.conf"))
+ configEnvironmentResolv("/etc/resolv.conf");
+ if (hostname[0])
+ variable_set2(VAR_HOSTNAME, hostname, 0);
+}
+
+static void
+rtsolGetInfo(Device *devp)
+{
+ FILE *ifp;
+ char *cp, cmd[256], data[2048];
+ int i;
+
+ snprintf(cmd, sizeof cmd, "ifconfig %s", devp->name);
+ if ((ifp = popen(cmd, "r")) == NULL)
+ return;
+ while (fgets(data, sizeof(data), ifp) != NULL) {
+ if (isDebug())
+ msgDebug("RTSOL configured interface returns %s\n", data);
+ if ((cp = strstr(data, "inet6 ")) != NULL) {
+ cp += 6; /* move over keyword */
+ if (strncmp(cp, "fe80:", 5)) {
+ i = 0;
+ while (*cp != ' ')
+ ipv6addr[i++] = *(cp++);
+ ipv6addr[i] = '\0';
+ }
+ }
+ }
+ fclose(ifp);
+}
+
+/* This is it - how to get TCP setup values */
+int
+tcpOpenDialog(Device *devp)
+{
+ WINDOW *ds_win, *save = NULL;
+ ComposeObj *obj = NULL;
+ int n = 0, filled = 0, cancel = FALSE;
+ int max, ret = DITEM_SUCCESS;
+ int use_dhcp = FALSE;
+ int use_rtsol = FALSE;
+ char *tmp;
+ char title[80];
+
+ save = savescr();
+ /* Initialise vars from previous device values */
+ if (devp->private) {
+ DevInfo *di = (DevInfo *)devp->private;
+
+ SAFE_STRCPY(ipaddr, di->ipaddr);
+ SAFE_STRCPY(netmask, di->netmask);
+ SAFE_STRCPY(extras, di->extras);
+ use_dhcp = di->use_dhcp;
+ use_rtsol = di->use_rtsol;
+ }
+ else { /* See if there are any defaults */
+ char *cp;
+ char *old_interactive = NULL;
+
+ /*
+ * This is a hack so that the dialogs below are interactive in a
+ * script if we have requested interactive behavior.
+ */
+ if (variable_get(VAR_NONINTERACTIVE) &&
+ variable_get(VAR_NETINTERACTIVE)) {
+ old_interactive = strdup(VAR_NONINTERACTIVE);
+ variable_unset(VAR_NONINTERACTIVE);
+ }
+
+
+ /*
+ * Try a RTSOL scan if such behavior is desired.
+ * If the variable was configured and is YES, do it.
+ * If it was configured to anything else, treat it as NO.
+ * Otherwise, ask the question interactively.
+ */
+ if (!variable_get(VAR_NO_INET6) &&
+ (!variable_cmp(VAR_TRY_RTSOL, "YES") ||
+ (variable_get(VAR_TRY_RTSOL)==0 && !msgNoYes("Do you want to try IPv6 configuration of the interface?")))) {
+ int i;
+ size_t len;
+
+ i = 0;
+ sysctlbyname("net.inet6.ip6.forwarding", NULL, 0, &i, sizeof(i));
+ i = 1;
+ sysctlbyname("net.inet6.ip6.accept_rtadv", NULL, 0, &i, sizeof(i));
+ vsystem("ifconfig %s up", devp->name);
+ len = sizeof(i);
+ sysctlbyname("net.inet6.ip6.dad_count", &i, &len, NULL, 0);
+ sleep(i + 1);
+ Mkdir("/var/run");
+ msgNotify("Scanning for RA servers...");
+ if (0 == vsystem("rtsol %s", devp->name)) {
+ len = sizeof(i);
+ sysctlbyname("net.inet6.ip6.dad_count", &i, &len, NULL, 0);
+ sleep(i + 1);
+ rtsolGetInfo(devp);
+ use_rtsol = TRUE;
+ } else
+ use_rtsol = FALSE;
+ }
+
+
+ /*
+ * First try a DHCP scan if such behavior is desired.
+ * If the variable was configured and is YES, do it.
+ * If it was configured to anything else, treat it as NO.
+ * Otherwise, ask the question interactively.
+ */
+ if (!variable_cmp(VAR_TRY_DHCP, "YES") ||
+ (variable_get(VAR_TRY_DHCP)==0 && !msgNoYes("Do you want to try DHCP configuration of the interface?"))) {
+ Mkdir("/var/db");
+ Mkdir("/var/run");
+ Mkdir("/tmp");
+ msgNotify("Scanning for DHCP servers...");
+ /* XXX clear any existing lease */
+ /* XXX limit protocol to N tries */
+ if (0 == vsystem("dhclient %s", devp->name)) {
+ dhcpGetInfo(devp);
+ use_dhcp = TRUE;
+ }
+ else
+ use_dhcp = FALSE;
+ }
+
+ /* Restore old VAR_NONINTERACTIVE if needed. */
+ if (old_interactive != NULL) {
+ variable_set2(VAR_NONINTERACTIVE, old_interactive, 0);
+ free(old_interactive);
+ }
+
+ /* Special hack so it doesn't show up oddly in the tcpip setup menu */
+ if (!strcmp(gateway, "NO"))
+ gateway[0] = '\0';
+
+ /* Get old IP address from variable space, if available */
+ if (!ipaddr[0]) {
+ if ((cp = variable_get(VAR_IPADDR)) != NULL)
+ SAFE_STRCPY(ipaddr, cp);
+ else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_IPADDR))) != NULL)
+ SAFE_STRCPY(ipaddr, cp);
+ }
+
+ /* Get old netmask from variable space, if available */
+ if (!netmask[0]) {
+ if ((cp = variable_get(VAR_NETMASK)) != NULL)
+ SAFE_STRCPY(netmask, cp);
+ else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_NETMASK))) != NULL)
+ SAFE_STRCPY(netmask, cp);
+ }
+
+ /* Get old extras string from variable space, if available */
+ if (!extras[0]) {
+ if ((cp = variable_get(VAR_EXTRAS)) != NULL)
+ SAFE_STRCPY(extras, cp);
+ else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_EXTRAS))) != NULL)
+ SAFE_STRCPY(extras, cp);
+ }
+ }
+
+ /* Look up values already recorded with the system, or blank the string variables ready to accept some new data */
+ if (!hostname[0]) {
+ tmp = variable_get(VAR_HOSTNAME);
+ if (tmp)
+ SAFE_STRCPY(hostname, tmp);
+ }
+ if (!domainname[0]) {
+ tmp = variable_get(VAR_DOMAINNAME);
+ if (tmp)
+ SAFE_STRCPY(domainname, tmp);
+ }
+ if (!gateway[0]) {
+ tmp = variable_get(VAR_GATEWAY);
+ if (tmp && strcmp(tmp, "NO"))
+ SAFE_STRCPY(gateway, tmp);
+ }
+ if (!nameserver[0]) {
+ tmp = variable_get(VAR_NAMESERVER);
+ if (tmp)
+ SAFE_STRCPY(nameserver, tmp);
+ }
+
+ /* If non-interactive, jump straight over the dialog crap and into config section */
+ if (variable_get(VAR_NONINTERACTIVE) &&
+ !variable_get(VAR_NETINTERACTIVE)) {
+ if (!hostname[0])
+ msgConfirm("WARNING: hostname variable not set and is a non-optional\n"
+ "parameter. Please add this to your installation script\n"
+ "or set the netInteractive variable (see sysinstall man page)");
+ else
+ goto netconfig;
+ }
+
+ /* Now do all the screen I/O */
+ dialog_clear_norefresh();
+
+ /* Modify the help line for PLIP config */
+ if (!strncmp(devp->name, "plip", 4))
+ layout[LAYOUT_EXTRAS].help =
+ "For PLIP configuration, you must enter the peer's IP address here.";
+
+ /* We need a curses window */
+ tmp = " Network Configuration ";
+ if (ipv6addr[0])
+ tmp = string_concat(tmp, "(IPv6 ready) ");
+ if (!(ds_win = openLayoutDialog(TCP_HELPFILE, tmp,
+ TCP_DIALOG_X, TCP_DIALOG_Y, TCP_DIALOG_WIDTH, TCP_DIALOG_HEIGHT))) {
+ beep();
+ msgConfirm("Cannot open TCP/IP dialog window!!");
+ restorescr(save);
+ return DITEM_FAILURE;
+ }
+
+ /* Draw interface configuration box */
+ draw_box(ds_win, TCP_DIALOG_Y + 9, TCP_DIALOG_X + 8, TCP_DIALOG_HEIGHT - 13, TCP_DIALOG_WIDTH - 17,
+ dialog_attr, border_attr);
+ wattrset(ds_win, dialog_attr);
+ sprintf(title, " Configuration for Interface %s ", devp->name);
+ mvwaddstr(ds_win, TCP_DIALOG_Y + 9, TCP_DIALOG_X + 14, title);
+
+ /* Some more initialisation before we go into the main input loop */
+ obj = initLayoutDialog(ds_win, layout, TCP_DIALOG_X, TCP_DIALOG_Y, &max);
+
+reenter:
+ cancelbutton = okbutton = 0;
+ while (layoutDialogLoop(ds_win, layout, &obj, &n, max, &cancelbutton, &cancel)) {
+ /* Prevent this from being irritating if user really means NO */
+ if (filled < 3) {
+ /* Insert a default value for the netmask, 0xffffff00 is
+ * the most appropriate one (entire class C, or subnetted
+ * class A/B network).
+ */
+ if (!netmask[0]) {
+ strcpy(netmask, "255.255.255.0");
+ RefreshStringObj(layout[LAYOUT_NETMASK].obj);
+ ++filled;
+ }
+ if (!index(hostname, '.') && domainname[0]) {
+ strcat(hostname, ".");
+ strcat(hostname, domainname);
+ RefreshStringObj(layout[LAYOUT_HOSTNAME].obj);
+ ++filled;
+ }
+ else if (((tmp = index(hostname, '.')) != NULL) && !domainname[0]) {
+ SAFE_STRCPY(domainname, tmp + 1);
+ RefreshStringObj(layout[LAYOUT_DOMAINNAME].obj);
+ ++filled;
+ }
+ }
+ }
+ if (!cancel && !verifySettings())
+ goto reenter;
+
+ /* Clear this crap off the screen */
+ delwin(ds_win);
+ dialog_clear_norefresh();
+ use_helpfile(NULL);
+
+ /* We actually need to inform the rest of sysinstall about this
+ data now if the user hasn't selected cancel. Save the stuff
+ out to the environment via the variable_set() mechanism */
+
+netconfig:
+ if (!cancel) {
+ DevInfo *di;
+ char temp[512], ifn[255];
+ int ipv4_enable = FALSE;
+
+ if (hostname[0]) {
+ variable_set2(VAR_HOSTNAME, hostname, 1);
+ sethostname(hostname, strlen(hostname));
+ }
+ if (domainname[0])
+ variable_set2(VAR_DOMAINNAME, domainname, 0);
+ if (gateway[0])
+ variable_set2(VAR_GATEWAY, gateway, use_dhcp ? 0 : 1);
+ if (nameserver[0])
+ variable_set2(VAR_NAMESERVER, nameserver, 0);
+ if (ipaddr[0])
+ variable_set2(VAR_IPADDR, ipaddr, 0);
+ if (ipv6addr[0])
+ variable_set2(VAR_IPV6ADDR, ipv6addr, 0);
+
+ if (!devp->private)
+ devp->private = (DevInfo *)safe_malloc(sizeof(DevInfo));
+ di = devp->private;
+ SAFE_STRCPY(di->ipaddr, ipaddr);
+ SAFE_STRCPY(di->netmask, netmask);
+ SAFE_STRCPY(di->extras, extras);
+ di->use_dhcp = use_dhcp;
+ di->use_rtsol = use_rtsol;
+
+ if (use_dhcp || ipaddr[0])
+ ipv4_enable = TRUE;
+ if (ipv4_enable) {
+ sprintf(ifn, "%s%s", VAR_IFCONFIG, devp->name);
+ if (use_dhcp) {
+ if (strlen(extras) > 0)
+ sprintf(temp, "DHCP %s", extras);
+ else
+ sprintf(temp, "DHCP");
+ } else
+ sprintf(temp, "inet %s %s netmask %s",
+ ipaddr, extras, netmask);
+ variable_set2(ifn, temp, 1);
+ }
+ if (use_rtsol)
+ variable_set2(VAR_IPV6_ENABLE, "YES", 1);
+ if (!use_dhcp)
+ configResolv(NULL); /* XXX this will do it on the MFS copy XXX */
+ ret = DITEM_SUCCESS;
+ }
+ else
+ ret = DITEM_FAILURE;
+ restorescr(save);
+ return ret;
+}
+
+static Device *NetDev;
+
+static int
+netHook(dialogMenuItem *self)
+{
+ Device **devs;
+
+ devs = deviceFindDescr(self->prompt, self->title, DEVICE_TYPE_NETWORK);
+ if (devs) {
+ if (DITEM_STATUS(tcpOpenDialog(devs[0])) != DITEM_FAILURE)
+ NetDev = devs[0];
+ else
+ NetDev = NULL;
+ }
+ return devs ? DITEM_LEAVE_MENU : DITEM_FAILURE;
+}
+
+/* Get a network device */
+Device *
+tcpDeviceSelect(void)
+{
+ DMenu *menu;
+ Device **devs, *rval;
+ int cnt;
+
+ devs = deviceFind(variable_get(VAR_NETWORK_DEVICE), DEVICE_TYPE_NETWORK);
+ cnt = deviceCount(devs);
+ rval = NULL;
+
+ if (!cnt) {
+ msgConfirm("No network devices available!");
+ return NULL;
+ }
+ else if ((!RunningAsInit) && (variable_check("NETWORK_CONFIGURED=NO") != TRUE)) {
+ if (!msgYesNo("Running multi-user, assume that the network is already configured?"))
+ return devs[0];
+ }
+ if (cnt == 1) {
+ if (DITEM_STATUS(tcpOpenDialog(devs[0]) == DITEM_SUCCESS))
+ rval = devs[0];
+ }
+ else if (variable_get(VAR_NONINTERACTIVE) && variable_get(VAR_NETWORK_DEVICE)) {
+ devs = deviceFind(variable_get(VAR_NETWORK_DEVICE), DEVICE_TYPE_NETWORK);
+ cnt = deviceCount(devs);
+ if (cnt) {
+ if (DITEM_STATUS(tcpOpenDialog(devs[0]) == DITEM_SUCCESS))
+ rval = devs[0];
+ }
+ }
+ else {
+ int status;
+
+ menu = deviceCreateMenu(&MenuNetworkDevice, DEVICE_TYPE_NETWORK, netHook, NULL);
+ if (!menu)
+ msgFatal("Unable to create network device menu! Argh!");
+ status = dmenuOpenSimple(menu, FALSE);
+ free(menu);
+ if (status)
+ rval = NetDev;
+ }
+ return rval;
+}
+
+/* Do it from a menu that doesn't care about status */
+int
+tcpMenuSelect(dialogMenuItem *self)
+{
+ Device *tmp;
+ WINDOW *save;
+
+ variable_set("NETWORK_CONFIGURED=NO",0);
+ tmp = tcpDeviceSelect();
+ variable_unset("NETWORK_CONFIGURED");
+ save = savescr();
+ if (tmp && tmp->private && !((DevInfo *)tmp->private)->use_dhcp && !msgYesNo("Would you like to bring the %s interface up right now?", tmp->name))
+ if (!DEVICE_INIT(tmp))
+ msgConfirm("Initialization of %s device failed.", tmp->name);
+ restorescr(save);
+ return DITEM_SUCCESS;
+}
diff --git a/usr.sbin/sysinstall/termcap.c b/usr.sbin/sysinstall/termcap.c
new file mode 100644
index 0000000..0df0a77
--- /dev/null
+++ b/usr.sbin/sysinstall/termcap.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 1994, Paul Richards.
+ *
+ * All rights reserved.
+ *
+ * This software may be used, modified, copied, distributed, and sold, in both
+ * source and binary form provided that the above copyright and these terms
+ * are retained, verbatim, as the first lines of this file. Under no
+ * circumstances is the author responsible for the proper functioning of this
+ * software, nor does the author assume any responsibility for damages
+ * incurred with its use.
+ *
+ * $FreeBSD$
+ */
+
+#include "sysinstall.h"
+#include <stdarg.h>
+#include <fcntl.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/consio.h>
+
+#define VTY_STATUS_LINE 24
+#define TTY_STATUS_LINE 23
+
+static void
+prompt_term(char **termp, char **termcapp)
+{
+ char str[80];
+ static struct {
+ const char *term, *termcap;
+ } lookup[] = { { "ansi", termcap_ansi },
+ { "vt100", termcap_vt100 },
+ { "cons25", termcap_cons25 },
+ { "cons25-m", termcap_cons25_m },
+ { "xterm", termcap_xterm },
+ { "cons25w", termcap_cons25w } }; /* must be last */
+
+ if (RunningAsInit) {
+ while (1) {
+ int i;
+
+ printf("\nThese are the predefined terminal types available to\n");
+ printf("sysinstall when running stand-alone. Please choose the\n");
+ printf("closest match for your particular terminal.\n\n");
+ printf("1 ...................... Standard ANSI terminal.\n");
+ printf("2 ...................... VT100 or compatible terminal.\n");
+ printf("3 ...................... FreeBSD system console (color).\n");
+ printf("4 ...................... FreeBSD system console (monochrome).\n\n");
+ printf("5 ...................... xterm terminal emulator.\n\n");
+ printf("Your choice: (1-5) ");
+ fflush(stdout);
+ fgets(str, sizeof(str), stdin);
+ i = str[0] - '0';
+ if (i > 0 && i < 6) {
+ *termp = (char *)lookup[i - 1].term;
+ *termcapp = (char *)lookup[i - 1].termcap;
+ break;
+ }
+ else
+ printf("\007Invalid choice, please try again.\n\n");
+ }
+ }
+ else {
+ printf("\nPlease set your TERM variable before running this program.\n");
+ printf("Defaulting to an ANSI compatible terminal - please press RETURN\n");
+ fgets(str, sizeof(str), stdin); /* Just to make it interactive */
+ *termp = (char *)"ansi";
+ *termcapp = (char *)termcap_ansi;
+ }
+}
+
+int
+set_termcap(void)
+{
+ char *term;
+ int stat;
+ struct winsize ws;
+
+ term = getenv("TERM");
+ stat = ioctl(STDERR_FILENO, GIO_COLOR, &ColorDisplay);
+
+ if (!RunningAsInit) {
+ if (isDebug())
+ DebugFD = open("sysinstall.debug", O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ else
+ DebugFD = -1;
+ if (DebugFD < 0)
+ DebugFD = open("/dev/null", O_RDWR, 0);
+ }
+
+ if (!OnVTY || (stat < 0)) {
+ if (!term) {
+ char *term, *termcap;
+
+ prompt_term(&term, &termcap);
+ if (setenv("TERM", term, 1) < 0)
+ return -1;
+ if (setenv("TERMCAP", termcap, 1) < 0)
+ return -1;
+ }
+ if (DebugFD < 0)
+ DebugFD = open("/dev/null", O_RDWR, 0);
+ }
+ else {
+ int i, on;
+
+ if (getpid() == 1) {
+ DebugFD = open("/dev/ttyv1", O_WRONLY);
+ if (DebugFD != -1) {
+ on = 1;
+ i = ioctl(DebugFD, TIOCCONS, (char *)&on);
+ msgDebug("ioctl(%d, TIOCCONS, NULL) = %d (%s)\n",
+ DebugFD, i, !i ? "success" : strerror(errno));
+ }
+ }
+
+#ifdef PC98
+ if (!term) {
+ if (setenv("TERM", "cons25w", 1) < 0)
+ return -1;
+ if (setenv("TERMCAP", termcap_cons25w, 1) < 0)
+ return -1;
+ }
+#else
+ if (ColorDisplay) {
+ if (!term) {
+ if (setenv("TERM", "cons25", 1) < 0)
+ return -1;
+ if (setenv("TERMCAP", termcap_cons25, 1) < 0)
+ return -1;
+ }
+ }
+ else {
+ if (!term) {
+ if (setenv("TERM", "cons25-m", 1) < 0)
+ return -1;
+ if (setenv("TERMCAP", termcap_cons25_m, 1) < 0)
+ return -1;
+ }
+ }
+#endif
+ }
+ if (ioctl(0, TIOCGWINSZ, &ws) == -1) {
+ msgDebug("Unable to get terminal size - errno %d\n", errno);
+ ws.ws_row = 0;
+ }
+ StatusLine = ws.ws_row ? ws.ws_row - 1: (OnVTY ? VTY_STATUS_LINE : TTY_STATUS_LINE);
+ return 0;
+}
diff --git a/usr.sbin/sysinstall/ttys.c b/usr.sbin/sysinstall/ttys.c
new file mode 100644
index 0000000..0de954c
--- /dev/null
+++ b/usr.sbin/sysinstall/ttys.c
@@ -0,0 +1,162 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 2001
+ * Andrey A. Chernov. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 ANDREY A. CHERNOV ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL ANDREY A. CHERNOV OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sysinstall.h"
+#include <sys/stat.h>
+#include <ctype.h>
+#include <ttyent.h>
+
+#define _X_EXTENSION ".XXXXXX"
+
+void
+configTtys(void)
+{
+ size_t len;
+ int t, tlen, changed;
+ FILE *fp, *np;
+ char sq, *line, *p, *q, *cp, *tptr;
+ char templ[sizeof(_PATH_TTYS) + sizeof(_X_EXTENSION) - 1];
+ struct ttyent *tnam;
+
+ if ((cp = variable_get(VAR_CONSTERM)) == NULL ||
+ strcmp(cp, "NO") == 0)
+ return;
+ if (!file_readable(_PATH_TTYS)) {
+ msgConfirm("%s not exist or not readable", _PATH_TTYS);
+ return;
+ }
+ if ((fp = fopen(_PATH_TTYS, "r")) == NULL) {
+ msgConfirm("Can't open %s for read: %s", _PATH_TTYS,
+ strerror(errno));
+ return;
+ }
+ strcpy(templ, _PATH_TTYS _X_EXTENSION);
+ if ((t = mkstemp(templ)) < 0) {
+ msgConfirm("Can't create %s: %s", templ, strerror(errno));
+ (void)fclose(fp);
+ return;
+ }
+ if (fchmod(t, 0644)) {
+ msgConfirm("Can't fchmod %s: %s", templ, strerror(errno));
+ (void)fclose(fp);
+ return;
+ }
+ if ((np = fdopen(t, "w")) == NULL) {
+ msgConfirm("Can't fdopen %s: %s", templ, strerror(errno));
+ (void)close(t);
+ (void)fclose(fp);
+ (void)unlink(templ);
+ return;
+ }
+ changed = 0;
+ while ((line = fgetln(fp, &len)) != NULL) {
+ p = line;
+ while (p < (line + len) && isspace((unsigned char)*p))
+ ++p;
+ if (strncmp(p, "ttyv", 4) != 0) {
+ dump:
+ if (fwrite(line, len, 1, np) != 1) {
+ wrerr:
+ msgConfirm("%s: write error: %s", templ, strerror(errno));
+ (void)fclose(fp);
+ (void)fclose(np);
+ (void)unlink(templ);
+ return;
+ }
+ } else {
+ q = p;
+ while(q < (line + len) && !isspace((unsigned char)*q))
+ ++q;
+ if (!isspace((unsigned char)*q))
+ goto dump;
+ sq = *q;
+ *q = '\0';
+ tnam = getttynam(p);
+ *q = sq;
+ if (tnam == NULL || tnam->ty_type == NULL ||
+ strcmp(tnam->ty_type, cp) == 0 ||
+ strncmp(tnam->ty_type, "cons", 4) != 0 ||
+ !isdigit((unsigned char)tnam->ty_type[4])
+ )
+ goto dump;
+ tlen = strlen(tnam->ty_type);
+ tptr = NULL;
+ p = ++q;
+ while(p < (line + len)) {
+ if (strncmp(p, tnam->ty_type, tlen) == 0) {
+ tptr = p;
+ break;
+ }
+ ++p;
+ }
+ if (tptr == NULL)
+ goto dump;
+ changed = 1;
+ if (fwrite(line, tptr - line, 1, np) != 1 ||
+ fputs(cp, np) ||
+ fwrite(tptr + tlen,
+ len - (tptr + tlen - line), 1, np) != 1)
+ goto wrerr;
+ }
+ }
+ if (!feof(fp)) {
+ msgConfirm("%s: read error: %s", _PATH_TTYS, strerror(errno));
+ (void)fclose(fp);
+ (void)fclose(np);
+ (void)unlink(templ);
+ return;
+ }
+ (void)fclose(fp);
+ if (fclose(np)) {
+ if (changed)
+ msgConfirm("%s: close error: %s", templ, strerror(errno));
+ else
+ variable_set2(VAR_CONSTERM, "NO", 0);
+ (void)unlink(templ);
+ return;
+ }
+ if (!changed) {
+ (void)unlink(templ);
+ variable_set2(VAR_CONSTERM, "NO", 0);
+ return;
+ }
+ if (rename(templ, _PATH_TTYS)) {
+ msgConfirm("Can't rename %s to %s: %s", templ, _PATH_TTYS,
+ strerror(errno));
+ return;
+ }
+ variable_set2(VAR_CONSTERM, "NO", 0);
+}
diff --git a/usr.sbin/sysinstall/ufs.c b/usr.sbin/sysinstall/ufs.c
new file mode 100644
index 0000000..c9515ae
--- /dev/null
+++ b/usr.sbin/sysinstall/ufs.c
@@ -0,0 +1,49 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ * Copyright (c) 1995
+ * Gary J Palmer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sysinstall.h"
+#include <sys/fcntl.h>
+#include <sys/param.h>
+
+/* No init or shutdown routines necessary - all done in mediaSetUFS() */
+
+FILE *
+mediaGetUFS(Device *dev, char *file, Boolean probe)
+{
+ return mediaGenericGet((char *)dev->private, file);
+}
diff --git a/usr.sbin/sysinstall/user.c b/usr.sbin/sysinstall/user.c
new file mode 100644
index 0000000..2ac2b8b
--- /dev/null
+++ b/usr.sbin/sysinstall/user.c
@@ -0,0 +1,752 @@
+/*
+ * $FreeBSD$
+ *
+ * Copyright (c) 1996
+ * Jörg Wunsch. All rights reserved.
+ *
+ * The basic structure has been taken from tcpip.c, which is:
+ *
+ * Copyright (c) 1995
+ * Gary J Palmer. All rights reserved.
+ * Jordan K Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 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, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (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 "sysinstall.h"
+#include <utmp.h>
+#include <ctype.h>
+#include <sys/param.h>
+#include <sysexits.h>
+
+/* The help file for the user mgmt screen */
+#define USER_HELPFILE "usermgmt"
+
+/* XXX should they be moved out to sysinstall.h? */
+#define GNAME_FIELD_LEN 32
+#define GID_FIELD_LEN 10
+#define GMEMB_FIELD_LEN 64
+
+#define UID_FIELD_LEN 10
+#define UGROUP_FIELD_LEN GNAME_FIELD_LEN
+#define GECOS_FIELD_LEN 64
+#define UMEMB_FIELD_LEN GMEMB_FIELD_LEN
+#define HOMEDIR_FIELD_LEN 48
+#define SHELL_FIELD_LEN 48
+#define PASSWD_FIELD_LEN 32
+
+/* These are nasty, but they make the layout structure a lot easier ... */
+
+static char gname[GNAME_FIELD_LEN],
+ gid[GID_FIELD_LEN],
+ gmemb[GMEMB_FIELD_LEN],
+ uname[UT_NAMESIZE + 1],
+ passwd[PASSWD_FIELD_LEN],
+ confpasswd[PASSWD_FIELD_LEN],
+ uid[UID_FIELD_LEN],
+ ugroup[UGROUP_FIELD_LEN],
+ gecos[GECOS_FIELD_LEN],
+ umemb[UMEMB_FIELD_LEN],
+ homedir[HOMEDIR_FIELD_LEN],
+ shell[SHELL_FIELD_LEN];
+#define CLEAR(v) memset(v, 0, sizeof v)
+
+static int okbutton, cancelbutton;
+
+
+/* What the screen size is meant to be */
+#define USER_DIALOG_Y 0
+#define USER_DIALOG_X 8
+#define USER_DIALOG_WIDTH COLS - 16
+#define USER_DIALOG_HEIGHT LINES - 1
+
+/* The group configuration menu. */
+static Layout groupLayout[] = {
+#define LAYOUT_GNAME 0
+ { 4, 10, 20, GNAME_FIELD_LEN - 1,
+ "Group name:", "The alphanumeric name of the new group (mandatory)",
+ gname, STRINGOBJ, NULL },
+#define LAYOUT_GID 1
+ { 4, 38, 10, GID_FIELD_LEN - 1,
+ "GID:", "The numerical ID for this group (leave blank for automatic choice)",
+ gid, STRINGOBJ, NULL },
+#define LAYOUT_GMEMB 2
+ { 11, 10, 40, GMEMB_FIELD_LEN - 1,
+ "Group members:", "Who belongs to this group (i.e., gets access rights for it)",
+ gmemb, STRINGOBJ, NULL },
+#define LAYOUT_OKBUTTON 3
+ { 18, 15, 0, 0,
+ "OK", "Select this if you are happy with these settings",
+ &okbutton, BUTTONOBJ, NULL },
+#define LAYOUT_CANCELBUTTON 4
+ { 18, 35, 0, 0,
+ "CANCEL", "Select this if you wish to cancel this screen",
+ &cancelbutton, BUTTONOBJ, NULL },
+ LAYOUT_END,
+};
+
+/* The user configuration menu. */
+static Layout userLayout[] = {
+#define LAYOUT_UNAME 0
+ { 2, 6, UT_NAMESIZE, UT_NAMESIZE + 1,
+ "Login ID:", "The login name of the new user (mandatory)",
+ uname, STRINGOBJ, NULL },
+#define LAYOUT_UID 1
+ { 2, 23, 8, UID_FIELD_LEN - 1,
+ "UID:", "The numerical ID for this user (leave blank for automatic choice)",
+ uid, STRINGOBJ, NULL },
+#define LAYOUT_UGROUP 2
+ { 2, 33, 8, UGROUP_FIELD_LEN - 1,
+ "Group:", "The login group name for this user (leave blank for automatic choice)",
+ ugroup, STRINGOBJ, NULL },
+#define LAYOUT_PASSWD 3
+ { 6, 6, 20, PASSWD_FIELD_LEN - 1,
+ "Password:", "The password for this user (enter this field with care!)",
+ passwd, NO_ECHO_OBJ(STRINGOBJ), NULL },
+#define LAYOUT_CONFPASSWD 4
+ { 6, 28, 20, PASSWD_FIELD_LEN - 1,
+ "Confirm Password:", "Confirm what you typed for the password",
+ confpasswd, NO_ECHO_OBJ(STRINGOBJ), NULL },
+#define LAYOUT_GECOS 5
+ { 10, 6, 33, GECOS_FIELD_LEN - 1,
+ "Full name:", "The user's full name (comment)",
+ gecos, STRINGOBJ, NULL },
+#define LAYOUT_UMEMB 6
+ { 10, 43, 15, UMEMB_FIELD_LEN - 1,
+ "Member groups:", "The groups this user belongs to (i.e. gets access rights for)",
+ umemb, STRINGOBJ, NULL },
+#define LAYOUT_HOMEDIR 7
+ { 14, 6, 20, HOMEDIR_FIELD_LEN - 1,
+ "Home directory:", "The user's home directory (leave blank for default)",
+ homedir, STRINGOBJ, NULL },
+#define LAYOUT_SHELL 8
+ { 14, 29, 29, SHELL_FIELD_LEN - 1,
+ "Login shell:", "The user's login shell (leave blank for default)",
+ shell, STRINGOBJ, NULL },
+#define LAYOUT_U_OKBUTTON 9
+ { 18, 15, 0, 0,
+ "OK", "Select this if you are happy with these settings",
+ &okbutton, BUTTONOBJ, NULL },
+#define LAYOUT_U_CANCELBUTTON 10
+ { 18, 35, 0, 0,
+ "CANCEL", "Select this if you wish to cancel this screen",
+ &cancelbutton, BUTTONOBJ, NULL },
+ LAYOUT_END,
+};
+
+/* whine */
+static void
+feepout(char *msg)
+{
+ beep();
+ dialog_notify(msg);
+}
+
+/* Check for the settings on the screen. */
+
+static int
+verifyGroupSettings(void)
+{
+ char tmp[256], *cp;
+ long lgid;
+
+ if (strlen(gname) == 0) {
+ feepout("The group name field must not be empty!");
+ return 0;
+ }
+ snprintf(tmp, 256, "pw group show -q -n %s > /dev/null", gname);
+ if (vsystem("%s", tmp) == 0) {
+ feepout("This group name is already in use.");
+ return 0;
+ }
+ if (strlen(gid) > 0) {
+ lgid = strtol(gid, &cp, 10);
+ if (lgid < 0 || lgid >= 65536 || (*cp != '\0' && !isspace(*cp))) {
+ feepout("The GID must be a number between 1 and 65535.");
+ return 0;
+ }
+ }
+ if (strlen(gmemb) > 0) {
+ if (strpbrk(gmemb, " \t") != NULL) {
+ feepout("The group member list must not contain any whitespace;\n"
+ "use commas to separate the names.");
+ return 0;
+ }
+#ifndef notyet /* XXX */
+ feepout("Sorry, the group member list feature\n"
+ "is currently not yet implemented.");
+ return 0;
+#endif
+ }
+
+ return 1;
+}
+
+/*
+ * Ask pw(8) to fill in the blanks for us.
+ * Works solely on the global variables.
+ */
+
+static void
+completeGroup(void)
+{
+ int pfd[2], i;
+ char tmp[256], *cp;
+ ssize_t l;
+ size_t amnt;
+ pid_t pid;
+ char *vec[4] =
+ {
+ "pw", "group", "next", 0
+ };
+
+ pipe (pfd);
+ if ((pid = fork()) == 0)
+ {
+ /* The kiddy. */
+ dup2(pfd[1], 1);
+ dup2(pfd[1], 2);
+ for (i = getdtablesize(); i > 2; i--)
+ close(i);
+
+ execv("/usr/sbin/pw", vec);
+ msgDebug("Cannot execv() /usr/sbin/pw.\n");
+ _exit(99);
+ }
+ else
+ {
+ /* The oldie. */
+ close(pfd[1]);
+ amnt = sizeof tmp;
+ i = 0;
+ while((l = read(pfd[0], &tmp[i], amnt)) > 0)
+ {
+ amnt -= l;
+ i += l;
+ if (amnt == 0)
+ {
+ close(pfd[0]);
+ break;
+ }
+ }
+ close(pfd[0]);
+ tmp[i] = '\0';
+ waitpid(pid, &i, 0);
+ if (WIFSIGNALED(i) || WEXITSTATUS(i) != 0)
+ /* ignore by now */
+ return;
+ if ((cp = strchr(tmp, '\n')) != NULL)
+ *cp = '\0';
+ strncpy(gid, tmp, sizeof gid);
+ }
+}
+
+static void
+addGroup(WINDOW *ds_win)
+{
+ char tmp[256];
+ int pfd[2], i;
+ ssize_t l;
+ size_t amnt;
+ pid_t pid;
+ char *vec[8] =
+ {
+ "pw", "group", "add", "-n", 0, "-g", 0, 0
+ };
+#define VEC_GNAME 4
+#define VEC_GID 6
+
+ msgNotify("Adding group \"%s\"...", gname);
+
+ pipe (pfd);
+ if ((pid = fork()) == 0)
+ {
+ /* The kiddy. */
+ dup2(pfd[1], 1);
+ dup2(pfd[1], 2);
+ for (i = getdtablesize(); i > 2; i--)
+ close(i);
+
+ vec[VEC_GNAME] = gname;
+
+ if (strlen(gid) > 0)
+ vec[VEC_GID] = gid;
+ else
+ vec[VEC_GID - 1] = 0;
+
+ execv("/usr/sbin/pw", vec);
+ msgDebug("Cannot execv() /usr/sbin/pw.\n");
+ _exit(99);
+ }
+ else
+ {
+ /* The oldie. */
+ close(pfd[1]);
+ amnt = sizeof tmp;
+ i = 0;
+ while((l = read(pfd[0], &tmp[i], amnt)) > 0)
+ {
+ amnt -= l;
+ i += l;
+ if (amnt == 0)
+ {
+ close(pfd[0]);
+ break;
+ }
+ }
+ close(pfd[0]);
+ tmp[i] = '\0';
+ waitpid(pid, &i, 0);
+ if (WIFSIGNALED(i))
+ msgDebug("pw(8) exited with signal %d.\n", WTERMSIG(i));
+ else if(WEXITSTATUS(i))
+ {
+ i = 0;
+ if(strncmp(tmp, "pw: ", 4) == 0)
+ i = 4;
+ tmp[sizeof tmp - 1] = '\0'; /* sanity */
+ msgConfirm("The `pw' command exited with an error status.\n"
+ "Its error message was:\n\n%s",
+ &tmp[i]);
+ }
+ }
+#undef VEC_GNAME
+#undef VEC_GID
+}
+
+int
+userAddGroup(dialogMenuItem *self)
+{
+ WINDOW *ds_win, *save;
+ ComposeObj *obj = NULL;
+ int n = 0, cancel = FALSE, ret;
+ int max, firsttime = TRUE;
+
+ if (RunningAsInit && !strstr(variable_get(SYSTEM_STATE), "install")) {
+ msgConfirm("This option may only be used after the system is installed, sorry!");
+ return DITEM_FAILURE;
+ }
+
+ save = savescr();
+ dialog_clear_norefresh();
+ /* We need a curses window */
+ if (!(ds_win = openLayoutDialog(USER_HELPFILE, " User and Group Management ",
+ USER_DIALOG_X, USER_DIALOG_Y, USER_DIALOG_WIDTH, USER_DIALOG_HEIGHT))) {
+ beep();
+ msgConfirm("Cannot open addgroup dialog window!!");
+ return(DITEM_FAILURE);
+ }
+
+ /* Draw a group entry box */
+ draw_box(ds_win, USER_DIALOG_Y + 2, USER_DIALOG_X + 8, USER_DIALOG_HEIGHT - 8,
+ USER_DIALOG_WIDTH - 17, dialog_attr, border_attr);
+ wattrset(ds_win, dialog_attr);
+ mvwaddstr(ds_win, USER_DIALOG_Y + 2, USER_DIALOG_X + 22, " Add a new group ");
+
+ CLEAR(gname);
+ CLEAR(gid);
+ CLEAR(gmemb);
+
+ /* Some more initialisation before we go into the main input loop */
+ obj = initLayoutDialog(ds_win, groupLayout, USER_DIALOG_X, USER_DIALOG_Y, &max);
+
+reenter:
+ cancelbutton = okbutton = 0;
+ if (firsttime) {
+ /* fill in the blanks, well, just the GID */
+ completeGroup();
+ RefreshStringObj(groupLayout[LAYOUT_GID].obj);
+ firsttime = FALSE;
+ }
+
+ while (layoutDialogLoop(ds_win, groupLayout, &obj, &n, max, &cancelbutton, &cancel));
+
+ if (!cancel && !verifyGroupSettings())
+ goto reenter;
+
+ /* Clear this crap off the screen */
+ delwin(ds_win);
+ dialog_clear_norefresh();
+ use_helpfile(NULL);
+
+ if (!cancel) {
+ addGroup(ds_win);
+ ret = DITEM_SUCCESS;
+ }
+ else
+ ret = DITEM_FAILURE;
+ restorescr(save);
+ return ret;
+}
+
+/* Check for the settings on the screen. */
+
+static int
+verifyUserSettings(WINDOW *ds_win)
+{
+ char tmp[256], *cp;
+ long luid;
+ WINDOW *save;
+ int rv;
+
+ if (strlen(uname) == 0) {
+ feepout("The user name field must not be empty!");
+ return 0;
+ }
+ snprintf(tmp, 256, "pw user show -q -n %s > /dev/null", uname);
+ if (vsystem("%s", tmp) == 0) {
+ feepout("This user name is already in use.");
+ return 0;
+ }
+ if (strlen(uid) > 0) {
+ luid = strtol(uid, &cp, 10);
+ if (luid < 0 || luid >= 65536 || (*cp != '\0' && !isspace(*cp))) {
+ feepout("The UID must be a number between 1 and 65535.");
+ return 0;
+ }
+ }
+ if (strcmp(passwd, confpasswd)) {
+ feepout("Passwords don't match");
+ return 0;
+ }
+ if ((homedir[0]!=0) && (homedir[0]!='/')) {
+ feepout("The pathname for home directories must begin with a '/'.");
+ return 0;
+ }
+ if (strlen(shell) > 0) {
+ setusershell();
+ while((cp = getusershell()) != NULL)
+ if (strcmp(cp, shell) == 0)
+ break;
+ endusershell();
+ if (cp == NULL) {
+ save = savescr();
+ rv = msgYesNo("Warning:\n\n"
+ "The requested shell \"%s\" is not\n"
+ "a valid user shell.\n\n"
+ "Use it anyway?\n", shell);
+ restorescr(save);
+ wrefresh(ds_win);
+ if (rv != DITEM_SUCCESS)
+ return 0;
+ }
+
+ }
+
+ if (strlen(umemb) > 0) {
+ if (strpbrk(umemb, " \t") != NULL) {
+ feepout("The member groups list must not contain any whitespace;\n"
+ "use commas to separate the names.");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * Ask pw(8) to fill in the blanks for us.
+ * Works solely on the global variables.
+ */
+
+static void
+completeUser(void)
+{
+ int pfd[2], i;
+ char tmp[256], *cp, *cp2;
+ ssize_t l;
+ size_t amnt;
+ pid_t pid;
+ char *vec[7] =
+ {
+ "pw", "user", "add", "-N", "-n", 0, 0
+ };
+#define VEC_UNAME 5
+
+ pipe (pfd);
+ if ((pid = fork()) == 0)
+ {
+ /* The kiddy. */
+ dup2(pfd[1], 1);
+ dup2(pfd[1], 2);
+ for (i = getdtablesize(); i > 2; i--)
+ close(i);
+
+ vec[VEC_UNAME] = uname;
+
+ execv("/usr/sbin/pw", vec);
+ msgDebug("Cannot execv() /usr/sbin/pw.\n");
+ _exit(99);
+ }
+ else
+ {
+ /* The oldie. */
+ close(pfd[1]);
+ amnt = sizeof tmp;
+ i = 0;
+ while((l = read(pfd[0], &tmp[i], amnt)) > 0)
+ {
+ amnt -= l;
+ i += l;
+ if (amnt == 0)
+ {
+ close(pfd[0]);
+ break;
+ }
+ }
+ close(pfd[0]);
+ tmp[i] = '\0';
+ waitpid(pid, &i, 0);
+ if (WIFSIGNALED(i) || WEXITSTATUS(i) != 0)
+ /* ignore by now */
+ return;
+ if ((cp = strchr(tmp, '\n')) != NULL)
+ *cp = '\0';
+ if ((cp = strchr(tmp, ':')) == NULL || (cp = strchr(++cp, ':')) == NULL)
+ return;
+ cp++;
+ if ((cp2 = strchr(cp, ':')) == NULL)
+ return;
+ *cp2++ = '\0';
+ strncpy(uid, cp, sizeof uid);
+ cp = cp2;
+ if ((cp2 = strchr(cp, ':')) == NULL)
+ return;
+ *cp2++ = '\0';
+#ifdef notyet /* XXX pw user add -g doesn't accept a numerical GID */
+ strncpy(ugroup, cp, sizeof ugroup);
+#endif
+ cp = cp2;
+ if ((cp2 = strchr(cp, ':')) == NULL || (cp2 = strchr(++cp2, ':')) == NULL ||
+ (cp = cp2 = strchr(++cp2, ':')) == NULL || (cp2 = strchr(++cp2, ':')) == NULL)
+ return;
+ *cp2++ = '\0';
+ cp++;
+ strncpy(gecos, cp, sizeof gecos);
+ cp = cp2;
+ if ((cp2 = strchr(cp, ':')) == NULL)
+ return;
+ *cp2++ = '\0';
+ if (*cp2)
+ strncpy(shell, cp2, sizeof shell);
+ }
+#undef VEC_UNAME
+}
+
+static void
+addUser(WINDOW *ds_win)
+{
+ char tmp[256], *msg;
+ int pfd[2], ipfd[2], i, j;
+ ssize_t l;
+ size_t amnt;
+ pid_t pid;
+ /*
+ * Maximal list:
+ * pw user add -m -n uname -g grp -u uid -c comment -d homedir -s shell -G grplist -h 0
+ */
+ char *vec[21] =
+ {
+ "pw", "user", "add", "-m", "-n", /* ... */
+ };
+#define VEC_UNAME 5
+
+ msgNotify("Adding user \"%s\"...", uname);
+
+ pipe (pfd);
+ pipe (ipfd);
+ if ((pid = fork()) == 0)
+ {
+ /* The kiddy. */
+ dup2(ipfd[0], 0);
+ dup2(pfd[1], 1);
+ dup2(pfd[1], 2);
+ for (i = getdtablesize(); i > 2; i--)
+ close(i);
+
+ vec[i = VEC_UNAME] = uname;
+ i++;
+#define ADDVEC(var, option) do { if (strlen(var) > 0) { vec[i++] = option; vec[i++] = var; } } while (0)
+ ADDVEC(ugroup, "-g");
+ ADDVEC(uid, "-u");
+ ADDVEC(gecos, "-c");
+ ADDVEC(homedir, "-d");
+ ADDVEC(shell, "-s");
+ ADDVEC(umemb, "-G");
+ if (passwd[0]) {
+ vec[i++] = "-h";
+ vec[i++] = "0";
+ }
+ vec[i] = 0;
+
+ execv("/usr/sbin/pw", vec);
+ msgDebug("Cannot execv() /usr/sbin/pw.\n");
+ _exit(99);
+ }
+ else
+ {
+ /* The oldie. */
+ close(pfd[1]);
+ close(ipfd[0]);
+
+ if (passwd[0])
+ write(ipfd[1], passwd, strlen(passwd));
+ close(ipfd[1]);
+ amnt = sizeof tmp;
+ i = 0;
+ while((l = read(pfd[0], &tmp[i], amnt)) > 0)
+ {
+ amnt -= l;
+ i += l;
+ if (amnt == 0)
+ {
+ close(pfd[0]);
+ break;
+ }
+ }
+ close(pfd[0]);
+ tmp[i] = '\0';
+ waitpid(pid, &i, 0);
+ if (WIFSIGNALED(i))
+ {
+ j = WTERMSIG(i);
+ msg = "The `pw' command exited with signal %d.\n";
+ goto sysfail;
+ }
+ else if((j = WEXITSTATUS(i)))
+ {
+ i = 0;
+ if(strncmp(tmp, "pw: ", 4) == 0)
+ i = 4;
+ tmp[sizeof tmp - 1] = '\0'; /* sanity */
+ if (j == EX_DATAERR || j == EX_NOUSER || j == EX_SOFTWARE)
+ msgConfirm("The `pw' command exited with an error status.\n"
+ "Its error message was:\n\n%s",
+ &tmp[i]);
+ else
+ {
+ msg = "The `pw' command exited with unexpected status %d.\n";
+ sysfail:
+ msgDebug(msg, j);
+ msgDebug("Command stdout and stderr was:\n\n%s", tmp);
+ msgConfirm(msg, j);
+ }
+ }
+ else if (!passwd[0])
+ msgConfirm("You will need to enter a password for this user\n"
+ "later, using the passwd(1) command from the shell.\n\n"
+ "The account for `%s' is currently still disabled.",
+ uname);
+ }
+#undef VEC_UNAME
+#undef ADDVEC
+}
+
+int
+userAddUser(dialogMenuItem *self)
+{
+ WINDOW *ds_win, *save;
+ ComposeObj *obj = NULL;
+ int n = 0, cancel = FALSE, ret;
+ int max, firsttime = TRUE, filled=0;
+
+ if (RunningAsInit && !strstr(variable_get(SYSTEM_STATE), "install")) {
+ msgConfirm("This option may only be used after the system is installed, sorry!");
+ return DITEM_FAILURE;
+ }
+
+ save = savescr();
+ dialog_clear_norefresh();
+
+ /* We need a curses window */
+ if (!(ds_win = openLayoutDialog(USER_HELPFILE, " User and Group Management ",
+ USER_DIALOG_X, USER_DIALOG_Y, USER_DIALOG_WIDTH, USER_DIALOG_HEIGHT))) {
+ beep();
+ msgConfirm("Cannot open adduser dialog window!!");
+ return(DITEM_FAILURE);
+ }
+
+ /* Draw a user entry box */
+ draw_box(ds_win, USER_DIALOG_Y + 1, USER_DIALOG_X + 3, USER_DIALOG_HEIGHT - 6,
+ USER_DIALOG_WIDTH - 6, dialog_attr, border_attr);
+ wattrset(ds_win, dialog_attr);
+ mvwaddstr(ds_win, USER_DIALOG_Y + 1, USER_DIALOG_X + 24, " Add a new user ");
+
+ CLEAR(uname);
+ CLEAR(uid);
+ CLEAR(ugroup);
+ CLEAR(gecos);
+ CLEAR(passwd);
+ CLEAR(confpasswd);
+ CLEAR(umemb);
+ CLEAR(homedir);
+ CLEAR(shell);
+
+ /* Some more initialisation before we go into the main input loop */
+ obj = initLayoutDialog(ds_win, userLayout, USER_DIALOG_X, USER_DIALOG_Y, &max);
+
+reenter:
+ cancelbutton = okbutton = 0;
+ if (firsttime) {
+ /* fill in the blanks, well, just the GID */
+ completeUser();
+ RefreshStringObj(userLayout[LAYOUT_UID].obj);
+ RefreshStringObj(userLayout[LAYOUT_UGROUP].obj);
+ RefreshStringObj(userLayout[LAYOUT_GECOS].obj);
+ RefreshStringObj(userLayout[LAYOUT_UMEMB].obj);
+ RefreshStringObj(userLayout[LAYOUT_HOMEDIR].obj);
+ RefreshStringObj(userLayout[LAYOUT_SHELL].obj);
+ firsttime = FALSE;
+ }
+
+ while (layoutDialogLoop(ds_win, userLayout, &obj, &n, max, &cancelbutton, &cancel)) {
+ /* Prevent this from being irritating if user really means NO */
+ if (filled < 3) {
+ if ((uname[0]) && !homedir[0]) {
+ SAFE_STRCPY(homedir,"/home/");
+ strcat(homedir,uname);
+ RefreshStringObj(userLayout[LAYOUT_HOMEDIR].obj);
+ ++filled;
+ }
+ }
+ };
+
+ if (!cancel && !verifyUserSettings(ds_win))
+ goto reenter;
+
+ /* Clear this crap off the screen */
+ delwin(ds_win);
+ dialog_clear_norefresh();
+ use_helpfile(NULL);
+
+ if (!cancel) {
+ addUser(ds_win);
+ ret = DITEM_SUCCESS;
+ }
+ else
+ ret = DITEM_FAILURE;
+ restorescr(save);
+ return ret;
+}
+
diff --git a/usr.sbin/sysinstall/variable.c b/usr.sbin/sysinstall/variable.c
new file mode 100644
index 0000000..3bc6aa7
--- /dev/null
+++ b/usr.sbin/sysinstall/variable.c
@@ -0,0 +1,329 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $FreeBSD$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ * Copyright (c) 2001
+ * Murray Stokely. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in 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 JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 "sysinstall.h"
+
+/* Routines for dealing with variable lists */
+
+static void
+make_variable(char *var, char *value, int dirty)
+{
+ Variable *vp;
+
+ /* Trim leading and trailing whitespace */
+ var = string_skipwhite(string_prune(var));
+
+ if (!var || !*var)
+ return;
+
+
+ /* Now search to see if it's already in the list */
+ for (vp = VarHead; vp; vp = vp->next) {
+ if (!strcmp(vp->name, var)) {
+ if (vp->dirty && !dirty)
+ return;
+ setenv(var, value, 1);
+ free(vp->value);
+ vp->value = strdup(value);
+ if (dirty != -1)
+ vp->dirty = dirty;
+ return;
+ }
+ }
+
+ setenv(var, value, 1);
+ /* No? Create a new one */
+ vp = (Variable *)safe_malloc(sizeof(Variable));
+ vp->name = strdup(var);
+ vp->value = strdup(value);
+ if (dirty == -1)
+ dirty = 0;
+ vp->dirty = dirty;
+ vp->next = VarHead;
+ VarHead = vp;
+}
+
+void
+variable_set(char *var, int dirty)
+{
+ char tmp[1024], *cp;
+
+ if (!var)
+ msgFatal("NULL variable name & value passed.");
+ else if (!*var)
+ msgDebug("Warning: Zero length name & value passed to variable_set()\n");
+ SAFE_STRCPY(tmp, var);
+ if ((cp = index(tmp, '=')) == NULL)
+ msgFatal("Invalid variable format: %s", var);
+ *(cp++) = '\0';
+ make_variable(tmp, string_skipwhite(cp), dirty);
+}
+
+void
+variable_set2(char *var, char *value, int dirty)
+{
+ if (!var || !value)
+ msgFatal("Null name or value passed to set_variable2(%s) = %s!",
+ var ? var : "", value ? value : "");
+ else if (!*var || !*value)
+ msgDebug("Warning: Zero length name or value passed to variable_set2(%s) = %s\n",
+ var, value);
+ make_variable(var, value, dirty);
+}
+
+char *
+variable_get(char *var)
+{
+ return getenv(var);
+}
+
+int
+variable_cmp(char *var, char *value)
+{
+ char *val;
+
+ if ((val = variable_get(var)))
+ return strcmp(val, value);
+ return -1;
+}
+
+void
+variable_unset(char *var)
+{
+ Variable *vp;
+ char name[512], *cp;
+
+ if ((cp = index(var, '=')) != NULL)
+ sstrncpy(name, var, cp - var);
+ else
+ SAFE_STRCPY(name, var);
+ unsetenv(name);
+ /* Now search to see if it's in our list, if we have one.. */
+ if (!VarHead)
+ return;
+ else if (!VarHead->next && !strcmp(VarHead->name, name)) {
+ safe_free(VarHead->name);
+ safe_free(VarHead->value);
+ free(VarHead);
+ VarHead = NULL;
+ }
+ else {
+ for (vp = VarHead; vp; vp = vp->next) {
+ if (!strcmp(vp->name, name)) {
+ Variable *save = vp->next;
+
+ safe_free(vp->name);
+ safe_free(vp->value);
+ *vp = *save;
+ safe_free(save);
+ break;
+ }
+ }
+ }
+}
+
+/* Prompt user for the name of a variable */
+char *
+variable_get_value(char *var, char *prompt, int dirty)
+{
+ char *cp;
+
+ cp = variable_get(var);
+ if (cp && variable_get(VAR_NONINTERACTIVE))
+ return cp;
+ else if ((cp = msgGetInput(cp, "%s", prompt)) != NULL)
+ variable_set2(var, cp, dirty);
+ else
+ cp = NULL;
+ return cp;
+}
+
+/* Check if value passed in data (in the form "variable=value") is
+ * valid, and it's status compared to the value of variable stored in
+ * env
+ *
+ * Possible return values :
+ * -3: Invalid line, the data string is NOT set as an env variable
+ * -2: Invalid line, the data string is set as an env variable
+ * -1: Invalid line
+ * 0: Valid line, is NOT equal to env version
+ * 1: Valid line, is equal to env version
+ * 2: Valid line, value empty - e.g. foo=""
+ * 3: Valid line, does not exist in env
+*/
+int
+variable_check2(char *data)
+{
+ char *cp, *cp2, *cp3, tmp[256];
+
+ if (data == NULL)
+ return -1;
+ SAFE_STRCPY(tmp, data);
+ if ((cp = index(tmp, '=')) != NULL) {
+ *(cp++) = '\0';
+ if (*cp == '"') { /* smash quotes if present */
+ ++cp;
+ if ((cp3 = index(cp, '"')) != NULL)
+ *cp3 = '\0';
+ }
+ else if ((cp3 = index(cp, ',')) != NULL)
+ *cp3 = '\0';
+ cp2 = variable_get(tmp);
+ if (cp2 != NULL) {
+ if (*cp == '\0')
+ return 2;
+ else
+ return strcmp(cp, cp2) == 0 ? 1 : 0;
+ }
+ else
+ return 3;
+ }
+ else
+ return variable_get(tmp) != NULL ? -2 : -3;
+}
+
+/* Check if the value passed in data (in the form "variable=value") is
+ equal to the value of variable stored in env */
+int
+variable_check(char *data)
+{
+ int ret;
+ ret = variable_check2(data);
+
+ switch(ret) {
+ case -2:
+ case 1:
+ case 2:
+ return TRUE;
+ /* NOT REACHED */
+ default:
+ return FALSE;
+ }
+}
+
+int
+dump_variables(dialogMenuItem *unused)
+{
+ FILE *fp;
+ Variable *vp;
+
+ if (isDebug())
+ msgDebug("Writing %s variables to file..\n", ProgName);
+
+ fp = fopen("/etc/sysinstall.vars", "w");
+ if (!fp) {
+ msgConfirm("Unable to write to /etc/%s.vars: %s",
+ ProgName, strerror(errno));
+ return DITEM_FAILURE;
+ }
+
+ for (vp = VarHead; vp; vp = vp->next)
+ fprintf(fp, "%s=\"%s\" (%d)\n", vp->name, vp->value, vp->dirty);
+
+ fclose(fp);
+
+ return DITEM_SUCCESS;
+}
+
+/* Free all of the variables, useful to really start over as when the
+ user selects "restart" from the interrupt menu. */
+void
+free_variables(void)
+{
+ Variable *vp;
+
+ /* Free the variables from our list, if we have one.. */
+ if (!VarHead)
+ return;
+ else if (!VarHead->next) {
+ unsetenv(VarHead->name);
+ safe_free(VarHead->name);
+ safe_free(VarHead->value);
+ free(VarHead);
+ VarHead = NULL;
+ }
+ else {
+ for (vp = VarHead; vp; ) {
+ Variable *save = vp;
+ unsetenv(vp->name);
+ safe_free(vp->name);
+ safe_free(vp->value);
+ vp = vp->next;
+ safe_free(save);
+ }
+ VarHead = NULL;
+ }
+}
+
+/*
+ * Persistent variables. The variables modified by these functions
+ * are not cleared between invocations of sysinstall. This is useful
+ * to allow the user to completely restart sysinstall, without having
+ * it load all of the modules again from the installation media which
+ * are still in memory.
+ */
+
+void
+pvariable_set(char *var)
+{
+ char *p;
+ char tmp[1024];
+
+ if (!var)
+ msgFatal("NULL variable name & value passed.");
+ else if (!*var)
+ msgDebug("Warning: Zero length name & value passed to variable_set()\n");
+ /* Add a trivial namespace to whatever name the caller chooses. */
+ SAFE_STRCPY(tmp, "SYSINSTALL_PVAR");
+ if (index(var, '=') == NULL)
+ msgFatal("Invalid variable format: %s", var);
+ strlcat(tmp, var, 1024);
+ p = strchr(tmp, '=');
+ *p = '\0';
+ setenv(tmp, p + 1, 1);
+}
+
+char *
+pvariable_get(char *var)
+{
+ char tmp[1024];
+
+ SAFE_STRCPY(tmp, "SYSINSTALL_PVAR");
+ strlcat(tmp, var, 1024);
+ return getenv(tmp);
+}
diff --git a/usr.sbin/sysinstall/wizard.c b/usr.sbin/sysinstall/wizard.c
new file mode 100644
index 0000000..c221e55
--- /dev/null
+++ b/usr.sbin/sysinstall/wizard.c
@@ -0,0 +1,201 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "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 "sysinstall.h"
+#include <fcntl.h>
+#include <err.h>
+#include <libdisk.h>
+
+static int
+scan_block(int fd, daddr_t block)
+{
+ u_char foo[512];
+
+ if (-1 == lseek(fd,block * 512,SEEK_SET))
+ err(1,"lseek");
+ if (512 != read(fd,foo, 512))
+ return 1;
+ return 0;
+}
+
+static void
+Scan_Disk(Disk *d)
+{
+ char device[64];
+ u_long l;
+ int i,j,fd;
+
+ strcpy(device,"/dev/");
+ strcat(device,d->name);
+
+ fd = open(device,O_RDWR);
+ if (fd < 0) {
+ msgWarn("open(%s) failed", device);
+ return;
+ }
+ for(i=-1,l=0;;l++) {
+ j = scan_block(fd,l);
+ if (j != i) {
+ if (i == -1) {
+ printf("%c: %lu.",j ? 'B' : 'G', l);
+ fflush(stdout);
+ } else if (i == 0) {
+ printf(".%lu\nB: %lu.",l-1,l);
+ fflush(stdout);
+ } else {
+ printf(".%lu\nG: %lu.",l-1,l);
+ fflush(stdout);
+ }
+ i = j;
+ }
+ }
+ close(fd);
+}
+
+void
+slice_wizard(Disk *d)
+{
+ Disk *db;
+ char myprompt[BUFSIZ];
+ char input[BUFSIZ];
+ char *p,*q=0;
+ char **cp,*cmds[200];
+ int ncmd,i;
+
+ systemSuspendDialog();
+ sprintf(myprompt,"%s> ", d->name);
+ while(1) {
+ printf("--==##==--\n");
+ Debug_Disk(d);
+ p = CheckRules(d);
+ if (p) {
+ printf("%s",p);
+ free(p);
+ }
+ printf("%s", myprompt);
+ fflush(stdout);
+ q = p = fgets(input,sizeof(input),stdin);
+ if(!p)
+ break;
+ for(cp = cmds; (*cp = strsep(&p, " \t\n")) != NULL;)
+ if (**cp != '\0')
+ cp++;
+ ncmd = cp - cmds;
+ if(!ncmd)
+ continue;
+ if (!strcasecmp(*cmds,"quit")) { break; }
+ if (!strcasecmp(*cmds,"exit")) { break; }
+ if (!strcasecmp(*cmds,"q")) { break; }
+ if (!strcasecmp(*cmds,"x")) { break; }
+ if (!strcasecmp(*cmds,"delete") && ncmd == 2) {
+ printf("delete = %d\n",
+ Delete_Chunk(d,
+ (struct chunk *)strtol(cmds[1],0,0)));
+ continue;
+ }
+ if (!strcasecmp(*cmds,"allfreebsd")) {
+ All_FreeBSD(d, 0);
+ continue;
+ }
+ if (!strcasecmp(*cmds,"dedicate")) {
+ All_FreeBSD(d, 1);
+ continue;
+ }
+ if (!strcasecmp(*cmds,"bios") && ncmd == 4) {
+ Set_Bios_Geom(d,
+ strtol(cmds[1],0,0),
+ strtol(cmds[2],0,0),
+ strtol(cmds[3],0,0));
+ continue;
+ }
+ if (!strcasecmp(*cmds,"list")) {
+ cp = Disk_Names();
+ printf("Disks:");
+ for(i=0;cp[i];i++) {
+ printf(" %s",cp[i]);
+ free(cp[i]);
+ }
+ free(cp);
+ continue;
+ }
+#ifdef PC98
+ if (!strcasecmp(*cmds,"create") && ncmd == 7) {
+ printf("Create=%d\n",
+ Create_Chunk(d,
+ strtol(cmds[1],0,0),
+ strtol(cmds[2],0,0),
+ strtol(cmds[3],0,0),
+ strtol(cmds[4],0,0),
+ strtol(cmds[5],0,0),
+ cmds[6]));
+ continue;
+ }
+#else
+ if (!strcasecmp(*cmds,"create") && ncmd == 6) {
+ printf("Create=%d\n",
+ Create_Chunk(d,
+ strtol(cmds[1],0,0),
+ strtol(cmds[2],0,0),
+ strtol(cmds[3],0,0),
+ strtol(cmds[4],0,0),
+ strtol(cmds[5],0,0), ""));
+ continue;
+ }
+#endif
+ if (!strcasecmp(*cmds,"read")) {
+ db = d;
+ if (ncmd > 1)
+ d = Open_Disk(cmds[1]);
+ else
+ d = Open_Disk(d->name);
+ if (d)
+ Free_Disk(db);
+ else
+ d = db;
+ continue;
+ }
+ if (!strcasecmp(*cmds,"scan")) {
+ Scan_Disk(d);
+ continue;
+ }
+ if (!strcasecmp(*cmds,"write")) {
+ printf("Write=%d\n",
+ Fake ? 0 : Write_Disk(d));
+ q = strdup(d->name);
+ Free_Disk(d);
+ d = Open_Disk(q);
+ continue;
+ }
+ if (strcasecmp(*cmds,"help"))
+ printf("\007ERROR\n");
+ printf("CMDS:\n");
+ printf("allfreebsd\t\t");
+ printf("dedicate\t\t");
+ printf("bios cyl hd sect\n");
+ printf("collapse [pointer]\t\t");
+#ifdef PC98
+ printf("create offset size enum subtype flags name\n");
+#else
+ printf("create offset size enum subtype flags\n");
+#endif
+ printf("subtype(part): swap=1, ffs=7\t\t");
+ printf("delete pointer\n");
+ printf("list\t\t");
+ printf("quit\n");
+ printf("read [disk]\t\t");
+ printf("scan\n");
+ printf("write\t\t");
+ printf("\n");
+
+ }
+ systemResumeDialog();
+}
diff --git a/usr.sbin/syslogd/Makefile b/usr.sbin/syslogd/Makefile
new file mode 100644
index 0000000..1682b49
--- /dev/null
+++ b/usr.sbin/syslogd/Makefile
@@ -0,0 +1,23 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+.PATH: ${.CURDIR}/../../usr.bin/wall
+
+PROG= syslogd
+MAN= syslog.conf.5 syslogd.8
+SRCS= syslogd.c ttymsg.c
+
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+
+WARNS?= 3
+
+.if ${MK_INET6_SUPPORT} != "no"
+CFLAGS+= -DINET6
+.endif
+
+CFLAGS+= -I${.CURDIR}/../../usr.bin/wall
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/syslogd/pathnames.h b/usr.sbin/syslogd/pathnames.h
new file mode 100644
index 0000000..24fbc4c
--- /dev/null
+++ b/usr.sbin/syslogd/pathnames.h
@@ -0,0 +1,37 @@
+/*
+ * 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/6/93
+ * $FreeBSD$
+ */
+
+#include <paths.h>
+
+#define _PATH_KLOG "/dev/klog"
+#define _PATH_LOGCONF "/etc/syslog.conf"
+#define _PATH_LOGPID "/var/run/syslog.pid"
diff --git a/usr.sbin/syslogd/syslog.conf.5 b/usr.sbin/syslogd/syslog.conf.5
new file mode 100644
index 0000000..3799961
--- /dev/null
+++ b/usr.sbin/syslogd/syslog.conf.5
@@ -0,0 +1,517 @@
+.\" 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.
+.\"
+.\" @(#)syslog.conf.5 8.1 (Berkeley) 6/9/93
+.\" $FreeBSD$
+.\"
+.Dd December 23, 2008
+.Dt SYSLOG.CONF 5
+.Os
+.Sh NAME
+.Nm syslog.conf
+.Nd
+.Xr syslogd 8
+configuration file
+.Sh DESCRIPTION
+The
+.Nm
+file is the configuration file for the
+.Xr syslogd 8
+program.
+It consists of
+blocks of lines separated by
+.Em program
+and
+.Em hostname
+specifications (separations appear alone on their lines),
+with each line containing two fields: the
+.Em selector
+field which specifies the types of messages and priorities to which the
+line applies, and an
+.Em action
+field which specifies the action to be taken if a message
+.Xr syslogd 8
+receives matches the selection criteria.
+The
+.Em selector
+field is separated from the
+.Em action
+field by one or more tab characters or spaces.
+.Pp
+Note that if you use spaces as separators, your
+.Nm
+might be incompatible with other Unices or Unix-like systems.
+This functionality was added for ease of configuration
+(e.g.\& it is possible to cut-and-paste into
+.Nm ) ,
+and to avoid possible mistakes.
+This change however preserves
+backwards compatibility with the old style of
+.Nm
+(i.e., tab characters only).
+.Pp
+The
+.Em selectors
+are encoded as a
+.Em facility ,
+a period
+.Pq Dq \&. ,
+an optional set of comparison flags
+.Pq Oo \&! Oc Op <=> ,
+and a
+.Em level ,
+with no intervening white-space.
+Both the
+.Em facility
+and the
+.Em level
+are case insensitive.
+.Pp
+The
+.Em facility
+describes the part of the system generating the message, and is one of
+the following keywords:
+.Cm auth , authpriv , console , cron , daemon , ftp , kern , lpr ,
+.Cm mail , mark , news , ntp , security , syslog , user , uucp ,
+and
+.Cm local0
+through
+.Cm local7 .
+These keywords (with the exception of mark) correspond to
+similar
+.Dq Dv LOG_
+values specified to the
+.Xr openlog 3
+and
+.Xr syslog 3
+library routines.
+.Pp
+The
+.Em comparison flags
+may be used to specify exactly what is logged.
+The default comparison is
+.Dq =>
+(or, if you prefer,
+.Dq >= ) ,
+which means that messages from the specified
+.Em facility
+list, and of a priority
+level equal to or greater than
+.Em level
+will be logged.
+Comparison flags beginning with
+.Dq Li \&!
+will have their logical sense inverted.
+Thus
+.Dq !=info
+means all levels except info and
+.Dq !notice
+has the same meaning as
+.Dq <notice .
+.Pp
+The
+.Em level
+describes the severity of the message, and is a keyword from the
+following ordered list (higher to lower):
+.Cm emerg , crit , alert , err , warning , notice , info
+and
+.Cm debug .
+These keywords correspond to
+similar
+.Dq Dv LOG_
+values specified to the
+.Xr syslog 3
+library routine.
+.Pp
+Each block of lines is separated from the previous block by a
+.Em program
+or
+.Em hostname
+specification.
+A block will only log messages corresponding to the most recent
+.Em program
+and
+.Em hostname
+specifications given.
+Thus, with a block which selects
+.Ql ppp
+as the
+.Em program ,
+directly followed by a block that selects messages from the
+.Em hostname
+.Ql dialhost ,
+the second block will only log messages
+from the
+.Xr ppp 8
+program on dialhost.
+.Pp
+A
+.Em program
+specification is a line beginning with
+.Ql #!prog
+or
+.Ql !prog
+(the former is for compatibility with the previous syslogd, if one is sharing
+.Nm
+files, for example)
+and the following blocks will be associated with calls to
+.Xr syslog 3
+from that specific program.
+A
+.Em program
+specification for
+.Ql foo
+will also match any message logged by the kernel with the prefix
+.Ql "foo: " .
+The
+.Ql #!+prog
+or
+.Ql !+prog
+specification works just like the previous one,
+and the
+.Ql #!-prog
+or
+.Ql !-prog
+specification will match any message but the ones from that
+program.
+Multiple programs may be listed, separated by commas:
+.Ql !prog1,prog2
+matches messages from either program, while
+.Ql !-prog1,prog2
+matches all messages but those from
+.Ql prog1
+or
+.Ql prog2 .
+.Pp
+A
+.Em hostname
+specification of the form
+.Ql #+hostname
+or
+.Ql +hostname
+means the following blocks will be applied to messages
+received from the specified hostname.
+Alternatively, the
+.Em hostname
+specification
+.Ql #-hostname
+or
+.Ql -hostname
+causes the following blocks to be applied to messages
+from any host but the one specified.
+If the hostname is given as
+.Ql @ ,
+the local hostname will be used.
+As for program specifications, multiple comma-separated
+values may be specified for hostname specifications.
+.Pp
+A
+.Em program
+or
+.Em hostname
+specification may be reset by giving the program or hostname as
+.Ql * .
+.Pp
+See
+.Xr syslog 3
+for further descriptions of both the
+.Em facility
+and
+.Em level
+keywords and their significance.
+It is preferred that selections be made on
+.Em facility
+rather than
+.Em program ,
+since the latter can easily vary in a networked environment.
+In some cases,
+though, an appropriate
+.Em facility
+simply does not exist.
+.Pp
+If a received message matches the specified
+.Em facility
+and is of the specified
+.Em level
+.Em (or a higher level) ,
+and the first word in the message after the date matches the
+.Em program ,
+the action specified in the
+.Em action
+field will be taken.
+.Pp
+Multiple
+.Em selectors
+may be specified for a single
+.Em action
+by separating them with semicolon
+.Pq Dq \&;
+characters.
+It is important to note, however, that each
+.Em selector
+can modify the ones preceding it.
+.Pp
+Multiple
+.Em facilities
+may be specified for a single
+.Em level
+by separating them with comma
+.Pq Dq \&,
+characters.
+.Pp
+An asterisk
+.Pq Dq *
+can be used to specify all
+.Em facilities ,
+all
+.Em levels ,
+or all
+.Em programs .
+.Pp
+The special
+.Em facility
+.Dq mark
+receives a message at priority
+.Dq info
+every 20 minutes
+(see
+.Xr syslogd 8 ) .
+This is not enabled by a
+.Em facility
+field containing an asterisk.
+.Pp
+The special
+.Em level
+.Dq none
+disables a particular
+.Em facility .
+.Pp
+The
+.Em action
+field of each line specifies the action to be taken when the
+.Em selector
+field selects a message.
+There are five forms:
+.Bl -bullet
+.It
+A pathname (beginning with a leading slash).
+Selected messages are appended to the file.
+.Pp
+To ensure that kernel messages are written to disk promptly,
+.Nm
+calls
+.Xr fsync 2
+after writing messages from the kernel.
+Other messages are not synced explicitly.
+You may prefix a pathname with the minus sign,
+.Dq - ,
+to forego syncing the specified file after every kernel message.
+Note that you might lose information if the system crashes
+immediately following a write attempt.
+Nevertheless, using the
+.Dq -
+option may improve performance,
+especially if the kernel is logging many messages.
+.It
+A hostname (preceded by an at
+.Pq Dq @
+sign).
+Selected messages are forwarded to the
+.Xr syslogd 8
+program on the named host.
+If a port number is added after a colon
+.Pq Ql :\&
+then that port will be used as the destination port
+rather than the usual syslog port.
+.It
+A comma separated list of users.
+Selected messages are written to those users
+if they are logged in.
+.It
+An asterisk.
+Selected messages are written to all logged-in users.
+.It
+A vertical bar
+.Pq Dq \&| ,
+followed by a command to pipe the selected
+messages to.
+The command is passed to
+.Xr sh 1
+for evaluation, so usual shell metacharacters or input/output
+redirection can occur.
+(Note however that redirecting
+.Xr stdio 3
+buffered output from the invoked command can cause additional delays,
+or even lost output data in case a logging subprocess exited with a
+signal.)
+The command itself runs with
+.Em stdout
+and
+.Em stderr
+redirected to
+.Pa /dev/null .
+Upon receipt of a
+.Dv SIGHUP ,
+.Xr syslogd 8
+will close the pipe to the process.
+If the process did not exit
+voluntarily, it will be sent a
+.Dv SIGTERM
+signal after a grace period of up to 60 seconds.
+.Pp
+The command will only be started once data arrives that should be piped
+to it.
+If it exited later, it will be restarted as necessary.
+So if it
+is desired that the subprocess should get exactly one line of input only
+(which can be very resource-consuming if there are a lot of messages
+flowing quickly), this can be achieved by exiting after just one line of
+input.
+If necessary, a script wrapper can be written to this effect.
+.Pp
+Unless the command is a full pipeline, it is probably useful to
+start the command with
+.Em exec
+so that the invoking shell process does not wait for the command to
+complete.
+Warning: the process is started under the UID invoking
+.Xr syslogd 8 ,
+normally the superuser.
+.El
+.Pp
+Blank lines and lines whose first non-blank character is a hash
+.Pq Dq #
+character are ignored.
+If
+.Ql #
+is placed in the middle of the line, the
+.Ql #
+character and the rest of the line after it is ignored.
+To prevent special meaning, the
+.Ql #
+character may be escaped with
+.Ql \e ;
+in this case preceding
+.Ql \e
+is removed and
+.Ql #
+is treated as an ordinary character.
+.Sh IMPLEMENTATION NOTES
+The
+.Dq kern
+facility is usually reserved for messages
+generated by the local kernel.
+Other messages logged with facility
+.Dq kern
+are usually translated to facility
+.Dq user .
+This translation can be disabled;
+see
+.Xr syslogd 8
+for details.
+.Sh FILES
+.Bl -tag -width /etc/syslog.conf -compact
+.It Pa /etc/syslog.conf
+.Xr syslogd 8
+configuration file
+.El
+.Sh EXAMPLES
+A configuration file might appear as follows:
+.Bd -literal
+# Log all kernel messages, authentication messages of
+# level notice or higher, and anything of level err or
+# higher to the console.
+# Don't log private authentication messages!
+*.err;kern.*;auth.notice;authpriv.none;mail.crit /dev/console
+
+# Log anything (except mail) of level info or higher.
+# Don't log private authentication messages!
+*.info;mail.none;authpriv.none /var/log/messages
+
+# Log daemon messages at debug level only
+daemon.=debug /var/log/daemon.debug
+
+# The authpriv file has restricted access.
+authpriv.* /var/log/secure
+
+# Log all the mail messages in one place.
+mail.* /var/log/maillog
+
+# Everybody gets emergency messages, plus log them on another
+# machine.
+*.emerg *
+*.emerg @arpa.berkeley.edu
+
+# Root and Eric get alert and higher messages.
+*.alert root,eric
+
+# Save mail and news errors of level err and higher in a
+# special file.
+uucp,news.crit /var/log/spoolerr
+
+# Pipe all authentication messages to a filter.
+auth.* |exec /usr/local/sbin/authfilter
+
+# Save ftpd transactions along with mail and news
+!ftpd
+*.* /var/log/spoolerr
+
+# Log all security messages to a separate file.
+security.* /var/log/security
+
+# Log all writes to /dev/console to a separate file.
+console.* /var/log/console.log
+
+# Log ipfw messages without syncing after every message.
+!ipfw
+*.* -/var/log/ipfw
+.Ed
+.Sh SEE ALSO
+.Xr syslog 3 ,
+.Xr syslogd 8
+.Sh BUGS
+The effects of multiple
+.Em selectors
+are sometimes not intuitive.
+For example
+.Dq mail.crit,*.err
+will select
+.Dq mail
+facility messages at the level of
+.Dq err
+or higher, not at the level of
+.Dq crit
+or higher.
+.Pp
+In networked environments, note that not all operating systems
+implement the same set of facilities.
+The facilities
+authpriv, cron, ftp, and ntp that are known to this implementation
+might be absent on the target system.
+Even worse, DEC UNIX uses
+facility number 10 (which is authpriv in this implementation) to
+log events for their AdvFS file system.
diff --git a/usr.sbin/syslogd/syslogd.8 b/usr.sbin/syslogd/syslogd.8
new file mode 100644
index 0000000..aae9b4f
--- /dev/null
+++ b/usr.sbin/syslogd/syslogd.8
@@ -0,0 +1,388 @@
+.\" Copyright (c) 1983, 1986, 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.
+.\"
+.\" @(#)syslogd.8 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\"
+.Dd May 13, 2008
+.Dt SYSLOGD 8
+.Os
+.Sh NAME
+.Nm syslogd
+.Nd log systems messages
+.Sh SYNOPSIS
+.Nm
+.Op Fl 468ACcdknosuv
+.Op Fl a Ar allowed_peer
+.Op Fl b Ar bind_address
+.Op Fl f Ar config_file
+.Op Fl l Oo Ar mode : Oc Ns Ar path
+.Op Fl m Ar mark_interval
+.Op Fl P Ar pid_file
+.Op Fl p Ar log_socket
+.Sh DESCRIPTION
+The
+.Nm
+utility reads and logs messages to the system console, log files, other
+machines and/or users as specified by its configuration file.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl 4
+Force
+.Nm
+to use IPv4 addresses only.
+.It Fl 6
+Force
+.Nm
+to use IPv6 addresses only.
+.It Fl 8
+Tells
+.Nm
+not to interfere with 8-bit data. Normally
+.Nm
+will replace C1 control characters
+.Pq ISO 8859 and Unicode characters
+with their
+.Dq M- Ns Em x
+equivalent.
+Note, this option does not change the way
+.Nm
+alters control characters
+.Pq see Xr iscntrl 3 .
+They will always be replaced with their
+.Dq ^ Ns Em x
+equivalent.
+.It Fl A
+Ordinarily,
+.Nm
+tries to send the message to only one address
+even if the host has more than one A or AAAA record.
+If this option is specified,
+.Nm
+tries to send the message to all addresses.
+.It Fl a Ar allowed_peer
+Allow
+.Ar allowed_peer
+to log to this
+.Nm
+using UDP datagrams.
+Multiple
+.Fl a
+options may be specified.
+.Pp
+The
+.Ar allowed_peer
+option may be any of the following:
+.Bl -tag -width "ipaddr/masklen[:service]XX"
+.It Xo
+.Sm off
+.Ar ipaddr
+.No / Ar masklen
+.Op : Ar service
+.Sm on
+.Xc
+Accept datagrams from
+.Ar ipaddr
+(in the usual dotted quad notation) with
+.Ar masklen
+bits being taken into account when doing the address comparison.
+.Ar ipaddr
+can be also IPv6 address by enclosing the address with
+.Ql \&[
+and
+.Ql \&] .
+If specified,
+.Ar service
+is the name or number of an UDP service (see
+.Xr services 5 )
+the source packet must belong to.
+A
+.Ar service
+of
+.Ql \&*
+allows packets being sent from any UDP port.
+The default
+.Ar service
+is
+.Ql syslog .
+If
+.Ar ipaddr
+is IPv4 address, a missing
+.Ar masklen
+will be substituted by the historic class A or class B netmasks if
+.Ar ipaddr
+belongs into the address range of class A or B, respectively, or
+by 24 otherwise.
+If
+.Ar ipaddr
+is IPv6 address, a missing
+.Ar masklen
+will be substituted by 128.
+.It Xo
+.Sm off
+.Ar domainname Op : Ar service
+.Sm on
+.Xc
+Accept datagrams where the reverse address lookup yields
+.Ar domainname
+for the sender address.
+The meaning of
+.Ar service
+is as explained above.
+.It Xo
+.Sm off
+.No * Ar domainname Op : Ar service
+.Sm on
+.Xc
+Same as before, except that any source host whose name
+.Em ends
+in
+.Ar domainname
+will get permission.
+.El
+.Pp
+The
+.Fl a
+options are ignored if the
+.Fl s
+option is also specified.
+.It Fl b Ar bind_address
+Specify one specific IP address or hostname to bind to.
+If a hostname is specified,
+the IPv4 or IPv6 address which corresponds to it is used.
+.It Fl C
+Create log files that do not exist (permission is set to
+.Li 0600 ) .
+.It Fl c
+Disable the compression of repeated instances of the same line
+into a single line of the form
+.Dq Li "last message repeated N times"
+when the output is a pipe to another program.
+If specified twice, disable this compression in all cases.
+.It Fl d
+Put
+.Nm
+into debugging mode.
+This is probably only of use to developers working on
+.Nm .
+.It Fl f
+Specify the pathname of an alternate configuration file;
+the default is
+.Pa /etc/syslog.conf .
+.It Fl k
+Disable the translation of
+messages received with facility
+.Dq kern
+to facility
+.Dq user .
+Usually the
+.Dq kern
+facility is reserved for messages read directly from
+.Pa /dev/klog .
+.It Fl m
+Select the number of minutes between
+.Dq mark
+messages; the default is 20 minutes.
+.It Fl n
+Disable dns query for every request.
+.It Fl o
+Prefix kernel messages with the full kernel boot file as determined by
+.Xr getbootfile 3 .
+Without this, the kernel message prefix is always
+.Dq Li kernel: .
+.It Fl p
+Specify the pathname of an alternate log socket to be used instead;
+the default is
+.Pa /var/run/log .
+.It Fl P
+Specify an alternative file in which to store the process ID.
+The default is
+.Pa /var/run/syslog.pid .
+.It Fl S
+Specify the pathname of an alternate log socket for privileged
+applications to be used instead; the default is
+.Pa /var/run/logpriv .
+.It Fl l
+Specify a location where
+.Nm
+should place an additional log socket.
+The primary use for this is to place additional log sockets in
+.Pa /var/run/log
+of various chroot filespaces.
+File permissions for socket can be specified in octal representation
+before socket name, delimited with a colon.
+Path to socket location must be absolute.
+.It Fl s
+Operate in secure mode.
+Do not log messages from remote machines.
+If
+specified twice, no network socket will be opened at all, which also
+disables logging to remote machines.
+.It Fl T
+Always use the local time and date for messages received from the network,
+instead of the timestamp field supplied in the message by the remote host.
+This is useful if some of the originating hosts can't keep time properly
+or are unable to generate a correct timestamp.
+.It Fl u
+Unique priority logging.
+Only log messages at the specified priority.
+Without this option, messages at the stated priority or higher are logged.
+This option changes the default comparison from
+.Dq =>
+to
+.Dq = .
+.It Fl v
+Verbose logging.
+If specified once, the numeric facility and priority are
+logged with each locally-written message.
+If specified more than once,
+the names of the facility and priority are logged with each locally-written
+message.
+.El
+.Pp
+The
+.Nm
+utility reads its configuration file when it starts up and whenever it
+receives a hangup signal.
+For information on the format of the configuration file,
+see
+.Xr syslog.conf 5 .
+.Pp
+The
+.Nm
+utility reads messages from the
+.Ux
+domain sockets
+.Pa /var/run/log
+and
+.Pa /var/run/logpriv ,
+from an Internet domain socket specified in
+.Pa /etc/services ,
+and from the special device
+.Pa /dev/klog
+(to read kernel messages).
+.Pp
+The
+.Nm
+utility creates its process ID file,
+by default
+.Pa /var/run/syslog.pid ,
+and stores its process
+ID there.
+This can be used to kill or reconfigure
+.Nm .
+.Pp
+The message sent to
+.Nm
+should consist of a single line.
+The message can contain a priority code, which should be a preceding
+decimal number in angle braces, for example,
+.Sq Aq 5 .
+This priority code should map into the priorities defined in the
+include file
+.In sys/syslog.h .
+.Pp
+For security reasons,
+.Nm
+will not append to log files that do not exist (unless
+.Fl C
+option is specified);
+therefore, they must be created manually before running
+.Nm .
+.Pp
+The date and time are taken from the received message.
+If the format of the timestamp field is incorrect,
+time obtained from the local host is used instead.
+This can be overriden by the
+.Fl T
+flag.
+.Sh FILES
+.Bl -tag -width /var/run/syslog.pid -compact
+.It Pa /etc/syslog.conf
+configuration file
+.It Pa /var/run/syslog.pid
+default process ID file
+.It Pa /var/run/log
+name of the
+.Ux
+domain datagram log socket
+.It Pa /var/run/logpriv
+.Ux
+socket for privileged applications
+.It Pa /dev/klog
+kernel log device
+.El
+.Sh SEE ALSO
+.Xr logger 1 ,
+.Xr syslog 3 ,
+.Xr services 5 ,
+.Xr syslog.conf 5 ,
+.Xr newsyslog 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.3 .
+.Pp
+The
+.Fl a ,
+.Fl s ,
+.Fl u ,
+and
+.Fl v
+options are
+.Fx 2.2
+extensions.
+.Sh BUGS
+The ability to log messages received in UDP packets is equivalent to
+an unauthenticated remote disk-filling service, and should probably be
+disabled by default.
+Some sort of
+.No inter- Ns Nm syslogd
+authentication mechanism ought to be worked out.
+To prevent the worst
+abuse, use of the
+.Fl a
+option is therefore highly recommended.
+.Pp
+The
+.Fl a
+matching algorithm does not pretend to be very efficient; use of numeric
+IP addresses is faster than domain name comparison.
+Since the allowed
+peer list is being walked linearly, peer groups where frequent messages
+are being anticipated from should be put early into the
+.Fl a
+list.
+.Pp
+The log socket was moved from
+.Pa /dev
+to ease the use of a read-only root file system.
+This may confuse
+some old binaries so that a symbolic link might be used for a
+transitional period.
diff --git a/usr.sbin/syslogd/syslogd.c b/usr.sbin/syslogd/syslogd.c
new file mode 100644
index 0000000..3e628a2
--- /dev/null
+++ b/usr.sbin/syslogd/syslogd.c
@@ -0,0 +1,2695 @@
+/*
+ * Copyright (c) 1983, 1988, 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) 1983, 1988, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)syslogd.c 8.3 (Berkeley) 4/4/94";
+#endif
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * syslogd -- log system messages
+ *
+ * This program implements a system log. It takes a series of lines.
+ * Each line may have a priority, signified as "<n>" as
+ * the first characters of the line. If this is
+ * not present, a default priority is used.
+ *
+ * To kill syslogd, send a signal 15 (terminate). A signal 1 (hup) will
+ * cause it to reread its configuration file.
+ *
+ * Defined Constants:
+ *
+ * MAXLINE -- the maximum line length that can be handled.
+ * DEFUPRI -- the default priority for user messages
+ * DEFSPRI -- the default priority for kernel messages
+ *
+ * Author: Eric Allman
+ * extensive changes by Ralph Campbell
+ * more extensive changes by Eric Allman (again)
+ * Extension to log by program name as well as facility and priority
+ * by Peter da Silva.
+ * -u and -v by Harlan Stenn.
+ * Priority comparison code by Harlan Stenn.
+ */
+
+#define MAXLINE 1024 /* maximum line length */
+#define MAXSVLINE 120 /* maximum saved line length */
+#define DEFUPRI (LOG_USER|LOG_NOTICE)
+#define DEFSPRI (LOG_KERN|LOG_CRIT)
+#define TIMERINTVL 30 /* interval for checking flush, mark */
+#define TTYMSGTIME 1 /* timeout passed to ttymsg */
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/syslimits.h>
+#include <sys/types.h>
+
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libutil.h>
+#include <limits.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <utmp.h>
+
+#include "pathnames.h"
+#include "ttymsg.h"
+
+#define SYSLOG_NAMES
+#include <sys/syslog.h>
+
+const char *ConfFile = _PATH_LOGCONF;
+const char *PidFile = _PATH_LOGPID;
+const char ctty[] = _PATH_CONSOLE;
+
+#define dprintf if (Debug) printf
+
+#define MAXUNAMES 20 /* maximum number of user names */
+
+/*
+ * Unix sockets.
+ * We have two default sockets, one with 666 permissions,
+ * and one for privileged programs.
+ */
+struct funix {
+ int s;
+ const char *name;
+ mode_t mode;
+ STAILQ_ENTRY(funix) next;
+};
+struct funix funix_secure = { -1, _PATH_LOG_PRIV, S_IRUSR | S_IWUSR,
+ { NULL } };
+struct funix funix_default = { -1, _PATH_LOG, DEFFILEMODE,
+ { &funix_secure } };
+
+STAILQ_HEAD(, funix) funixes = { &funix_default,
+ &(funix_secure.next.stqe_next) };
+
+/*
+ * Flags to logmsg().
+ */
+
+#define IGN_CONS 0x001 /* don't print on console */
+#define SYNC_FILE 0x002 /* do fsync on file after printing */
+#define ADDDATE 0x004 /* add a date to the message */
+#define MARK 0x008 /* this message is a mark */
+#define ISKERNEL 0x010 /* kernel generated message */
+
+/*
+ * This structure represents the files that will have log
+ * copies printed.
+ * We require f_file to be valid if f_type is F_FILE, F_CONSOLE, F_TTY
+ * or if f_type if F_PIPE and f_pid > 0.
+ */
+
+struct filed {
+ struct filed *f_next; /* next in linked list */
+ short f_type; /* entry type, see below */
+ short f_file; /* file descriptor */
+ time_t f_time; /* time this was last written */
+ char *f_host; /* host from which to recd. */
+ u_char f_pmask[LOG_NFACILITIES+1]; /* priority mask */
+ u_char f_pcmp[LOG_NFACILITIES+1]; /* compare priority */
+#define PRI_LT 0x1
+#define PRI_EQ 0x2
+#define PRI_GT 0x4
+ char *f_program; /* program this applies to */
+ union {
+ char f_uname[MAXUNAMES][UT_NAMESIZE+1];
+ struct {
+ char f_hname[MAXHOSTNAMELEN];
+ struct addrinfo *f_addr;
+
+ } f_forw; /* forwarding address */
+ char f_fname[MAXPATHLEN];
+ struct {
+ char f_pname[MAXPATHLEN];
+ pid_t f_pid;
+ } f_pipe;
+ } f_un;
+ char f_prevline[MAXSVLINE]; /* last message logged */
+ char f_lasttime[16]; /* time of last occurrence */
+ char f_prevhost[MAXHOSTNAMELEN]; /* host from which recd. */
+ int f_prevpri; /* pri of f_prevline */
+ int f_prevlen; /* length of f_prevline */
+ int f_prevcount; /* repetition cnt of prevline */
+ u_int f_repeatcount; /* number of "repeated" msgs */
+ int f_flags; /* file-specific flags */
+#define FFLAG_SYNC 0x01
+#define FFLAG_NEEDSYNC 0x02
+};
+
+/*
+ * Queue of about-to-be dead processes we should watch out for.
+ */
+
+TAILQ_HEAD(stailhead, deadq_entry) deadq_head;
+struct stailhead *deadq_headp;
+
+struct deadq_entry {
+ pid_t dq_pid;
+ int dq_timeout;
+ TAILQ_ENTRY(deadq_entry) dq_entries;
+};
+
+/*
+ * The timeout to apply to processes waiting on the dead queue. Unit
+ * of measure is `mark intervals', i.e. 20 minutes by default.
+ * Processes on the dead queue will be terminated after that time.
+ */
+
+#define DQ_TIMO_INIT 2
+
+typedef struct deadq_entry *dq_t;
+
+
+/*
+ * Struct to hold records of network addresses that are allowed to log
+ * to us.
+ */
+struct allowedpeer {
+ int isnumeric;
+ u_short port;
+ union {
+ struct {
+ struct sockaddr_storage addr;
+ struct sockaddr_storage mask;
+ } numeric;
+ char *name;
+ } u;
+#define a_addr u.numeric.addr
+#define a_mask u.numeric.mask
+#define a_name u.name
+};
+
+
+/*
+ * Intervals at which we flush out "message repeated" messages,
+ * in seconds after previous message is logged. After each flush,
+ * we move to the next interval until we reach the largest.
+ */
+int repeatinterval[] = { 30, 120, 600 }; /* # of secs before flush */
+#define MAXREPEAT ((sizeof(repeatinterval) / sizeof(repeatinterval[0])) - 1)
+#define REPEATTIME(f) ((f)->f_time + repeatinterval[(f)->f_repeatcount])
+#define BACKOFF(f) { if (++(f)->f_repeatcount > MAXREPEAT) \
+ (f)->f_repeatcount = MAXREPEAT; \
+ }
+
+/* values for f_type */
+#define F_UNUSED 0 /* unused entry */
+#define F_FILE 1 /* regular file */
+#define F_TTY 2 /* terminal */
+#define F_CONSOLE 3 /* console terminal */
+#define F_FORW 4 /* remote machine */
+#define F_USERS 5 /* list of users */
+#define F_WALL 6 /* everyone logged on */
+#define F_PIPE 7 /* pipe to program */
+
+const char *TypeNames[8] = {
+ "UNUSED", "FILE", "TTY", "CONSOLE",
+ "FORW", "USERS", "WALL", "PIPE"
+};
+
+static struct filed *Files; /* Log files that we write to */
+static struct filed consfile; /* Console */
+
+static int Debug; /* debug flag */
+static int resolve = 1; /* resolve hostname */
+static char LocalHostName[MAXHOSTNAMELEN]; /* our hostname */
+static const char *LocalDomain; /* our local domain name */
+static int *finet; /* Internet datagram socket */
+static int fklog = -1; /* /dev/klog */
+static int Initialized; /* set when we have initialized ourselves */
+static int MarkInterval = 20 * 60; /* interval between marks in seconds */
+static int MarkSeq; /* mark sequence number */
+static int SecureMode; /* when true, receive only unix domain socks */
+#ifdef INET6
+static int family = PF_UNSPEC; /* protocol family (IPv4, IPv6 or both) */
+#else
+static int family = PF_INET; /* protocol family (IPv4 only) */
+#endif
+static int mask_C1 = 1; /* mask characters from 0x80 - 0x9F */
+static int send_to_all; /* send message to all IPv4/IPv6 addresses */
+static int use_bootfile; /* log entire bootfile for every kern msg */
+static int no_compress; /* don't compress messages (1=pipes, 2=all) */
+static int logflags = O_WRONLY|O_APPEND; /* flags used to open log files */
+
+static char bootfile[MAXLINE+1]; /* booted kernel file */
+
+struct allowedpeer *AllowedPeers; /* List of allowed peers */
+static int NumAllowed; /* Number of entries in AllowedPeers */
+static int RemoteAddDate; /* Always set the date on remote messages */
+
+static int UniquePriority; /* Only log specified priority? */
+static int LogFacPri; /* Put facility and priority in log message: */
+ /* 0=no, 1=numeric, 2=names */
+static int KeepKernFac; /* Keep remotely logged kernel facility */
+static int needdofsync = 0; /* Are any file(s) waiting to be fsynced? */
+static struct pidfh *pfh;
+
+volatile sig_atomic_t MarkSet, WantDie;
+
+static int allowaddr(char *);
+static void cfline(const char *, struct filed *,
+ const char *, const char *);
+static const char *cvthname(struct sockaddr *);
+static void deadq_enter(pid_t, const char *);
+static int deadq_remove(pid_t);
+static int decode(const char *, CODE *);
+static void die(int);
+static void dodie(int);
+static void dofsync(void);
+static void domark(int);
+static void fprintlog(struct filed *, int, const char *);
+static int *socksetup(int, const char *);
+static void init(int);
+static void logerror(const char *);
+static void logmsg(int, const char *, const char *, int);
+static void log_deadchild(pid_t, int, const char *);
+static void markit(void);
+static int skip_message(const char *, const char *, int);
+static void printline(const char *, char *, int);
+static void printsys(char *);
+static int p_open(const char *, pid_t *);
+static void readklog(void);
+static void reapchild(int);
+static void usage(void);
+static int validate(struct sockaddr *, const char *);
+static void unmapped(struct sockaddr *);
+static void wallmsg(struct filed *, struct iovec *, const int iovlen);
+static int waitdaemon(int, int, int);
+static void timedout(int);
+static void double_rbuf(int);
+
+int
+main(int argc, char *argv[])
+{
+ int ch, i, fdsrmax = 0, l;
+ struct sockaddr_un sunx, fromunix;
+ struct sockaddr_storage frominet;
+ fd_set *fdsr = NULL;
+ char line[MAXLINE + 1];
+ const char *bindhostname, *hname;
+ struct timeval tv, *tvp;
+ struct sigaction sact;
+ struct funix *fx, *fx1;
+ sigset_t mask;
+ pid_t ppid = 1, spid;
+ socklen_t len;
+
+ bindhostname = NULL;
+ while ((ch = getopt(argc, argv, "468Aa:b:cCdf:kl:m:nop:P:sS:Tuv"))
+ != -1)
+ switch (ch) {
+ case '4':
+ family = PF_INET;
+ break;
+#ifdef INET6
+ case '6':
+ family = PF_INET6;
+ break;
+#endif
+ case '8':
+ mask_C1 = 0;
+ break;
+ case 'A':
+ send_to_all++;
+ break;
+ case 'a': /* allow specific network addresses only */
+ if (allowaddr(optarg) == -1)
+ usage();
+ break;
+ case 'b':
+ bindhostname = optarg;
+ break;
+ case 'c':
+ no_compress++;
+ break;
+ case 'C':
+ logflags |= O_CREAT;
+ break;
+ case 'd': /* debug */
+ Debug++;
+ break;
+ case 'f': /* configuration file */
+ ConfFile = optarg;
+ break;
+ case 'k': /* keep remote kern fac */
+ KeepKernFac = 1;
+ break;
+ case 'l':
+ {
+ long perml;
+ mode_t mode;
+ char *name, *ep;
+
+ if (optarg[0] == '/') {
+ mode = DEFFILEMODE;
+ name = optarg;
+ } else if ((name = strchr(optarg, ':')) != NULL) {
+ *name++ = '\0';
+ if (name[0] != '/')
+ errx(1, "socket name must be absolute "
+ "path");
+ if (isdigit(*optarg)) {
+ perml = strtol(optarg, &ep, 8);
+ if (*ep || perml < 0 ||
+ perml & ~(S_IRWXU|S_IRWXG|S_IRWXO))
+ errx(1, "invalid mode %s, exiting",
+ optarg);
+ mode = (mode_t )perml;
+ } else
+ errx(1, "invalid mode %s, exiting",
+ optarg);
+ } else /* doesn't begin with '/', and no ':' */
+ errx(1, "can't parse path %s", optarg);
+
+ if (strlen(name) >= sizeof(sunx.sun_path))
+ errx(1, "%s path too long, exiting", name);
+ if ((fx = malloc(sizeof(struct funix))) == NULL)
+ errx(1, "malloc failed");
+ fx->s = -1;
+ fx->name = name;
+ fx->mode = mode;
+ STAILQ_INSERT_TAIL(&funixes, fx, next);
+ break;
+ }
+ case 'm': /* mark interval */
+ MarkInterval = atoi(optarg) * 60;
+ break;
+ case 'n':
+ resolve = 0;
+ break;
+ case 'o':
+ use_bootfile = 1;
+ break;
+ case 'p': /* path */
+ if (strlen(optarg) >= sizeof(sunx.sun_path))
+ errx(1, "%s path too long, exiting", optarg);
+ funix_default.name = optarg;
+ break;
+ case 'P': /* path for alt. PID */
+ PidFile = optarg;
+ break;
+ case 's': /* no network mode */
+ SecureMode++;
+ break;
+ case 'S': /* path for privileged originator */
+ if (strlen(optarg) >= sizeof(sunx.sun_path))
+ errx(1, "%s path too long, exiting", optarg);
+ funix_secure.name = optarg;
+ break;
+ case 'T':
+ RemoteAddDate = 1;
+ break;
+ case 'u': /* only log specified priority */
+ UniquePriority++;
+ break;
+ case 'v': /* log facility and priority */
+ LogFacPri++;
+ break;
+ default:
+ usage();
+ }
+ if ((argc -= optind) != 0)
+ usage();
+
+ pfh = pidfile_open(PidFile, 0600, &spid);
+ if (pfh == NULL) {
+ if (errno == EEXIST)
+ errx(1, "syslogd already running, pid: %d", spid);
+ warn("cannot open pid file");
+ }
+
+ if (!Debug) {
+ ppid = waitdaemon(0, 0, 30);
+ if (ppid < 0) {
+ warn("could not become daemon");
+ pidfile_remove(pfh);
+ exit(1);
+ }
+ } else {
+ setlinebuf(stdout);
+ }
+
+ if (NumAllowed)
+ endservent();
+
+ consfile.f_type = F_CONSOLE;
+ (void)strlcpy(consfile.f_un.f_fname, ctty + sizeof _PATH_DEV - 1,
+ sizeof(consfile.f_un.f_fname));
+ (void)strlcpy(bootfile, getbootfile(), sizeof(bootfile));
+ (void)signal(SIGTERM, dodie);
+ (void)signal(SIGINT, Debug ? dodie : SIG_IGN);
+ (void)signal(SIGQUIT, Debug ? dodie : SIG_IGN);
+ /*
+ * We don't want the SIGCHLD and SIGHUP handlers to interfere
+ * with each other; they are likely candidates for being called
+ * simultaneously (SIGHUP closes pipe descriptor, process dies,
+ * SIGCHLD happens).
+ */
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGHUP);
+ sact.sa_handler = reapchild;
+ sact.sa_mask = mask;
+ sact.sa_flags = SA_RESTART;
+ (void)sigaction(SIGCHLD, &sact, NULL);
+ (void)signal(SIGALRM, domark);
+ (void)signal(SIGPIPE, SIG_IGN); /* We'll catch EPIPE instead. */
+ (void)alarm(TIMERINTVL);
+
+ TAILQ_INIT(&deadq_head);
+
+#ifndef SUN_LEN
+#define SUN_LEN(unp) (strlen((unp)->sun_path) + 2)
+#endif
+ STAILQ_FOREACH_SAFE(fx, &funixes, next, fx1) {
+ (void)unlink(fx->name);
+ memset(&sunx, 0, sizeof(sunx));
+ sunx.sun_family = AF_LOCAL;
+ (void)strlcpy(sunx.sun_path, fx->name, sizeof(sunx.sun_path));
+ fx->s = socket(PF_LOCAL, SOCK_DGRAM, 0);
+ if (fx->s < 0 ||
+ bind(fx->s, (struct sockaddr *)&sunx, SUN_LEN(&sunx)) < 0 ||
+ chmod(fx->name, fx->mode) < 0) {
+ (void)snprintf(line, sizeof line,
+ "cannot create %s", fx->name);
+ logerror(line);
+ dprintf("cannot create %s (%d)\n", fx->name, errno);
+ if (fx == &funix_default || fx == &funix_secure)
+ die(0);
+ else {
+ STAILQ_REMOVE(&funixes, fx, funix, next);
+ continue;
+ }
+ double_rbuf(fx->s);
+ }
+ }
+ if (SecureMode <= 1)
+ finet = socksetup(family, bindhostname);
+
+ if (finet) {
+ if (SecureMode) {
+ for (i = 0; i < *finet; i++) {
+ if (shutdown(finet[i+1], SHUT_RD) < 0) {
+ logerror("shutdown");
+ if (!Debug)
+ die(0);
+ }
+ }
+ } else {
+ dprintf("listening on inet and/or inet6 socket\n");
+ }
+ dprintf("sending on inet and/or inet6 socket\n");
+ }
+
+ if ((fklog = open(_PATH_KLOG, O_RDONLY, 0)) >= 0)
+ if (fcntl(fklog, F_SETFL, O_NONBLOCK) < 0)
+ fklog = -1;
+ if (fklog < 0)
+ dprintf("can't open %s (%d)\n", _PATH_KLOG, errno);
+
+ /* tuck my process id away */
+ pidfile_write(pfh);
+
+ dprintf("off & running....\n");
+
+ init(0);
+ /* prevent SIGHUP and SIGCHLD handlers from running in parallel */
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGCHLD);
+ sact.sa_handler = init;
+ sact.sa_mask = mask;
+ sact.sa_flags = SA_RESTART;
+ (void)sigaction(SIGHUP, &sact, NULL);
+
+ tvp = &tv;
+ tv.tv_sec = tv.tv_usec = 0;
+
+ if (fklog != -1 && fklog > fdsrmax)
+ fdsrmax = fklog;
+ if (finet && !SecureMode) {
+ for (i = 0; i < *finet; i++) {
+ if (finet[i+1] != -1 && finet[i+1] > fdsrmax)
+ fdsrmax = finet[i+1];
+ }
+ }
+ STAILQ_FOREACH(fx, &funixes, next)
+ if (fx->s > fdsrmax)
+ fdsrmax = fx->s;
+
+ fdsr = (fd_set *)calloc(howmany(fdsrmax+1, NFDBITS),
+ sizeof(fd_mask));
+ if (fdsr == NULL)
+ errx(1, "calloc fd_set");
+
+ for (;;) {
+ if (MarkSet)
+ markit();
+ if (WantDie)
+ die(WantDie);
+
+ bzero(fdsr, howmany(fdsrmax+1, NFDBITS) *
+ sizeof(fd_mask));
+
+ if (fklog != -1)
+ FD_SET(fklog, fdsr);
+ if (finet && !SecureMode) {
+ for (i = 0; i < *finet; i++) {
+ if (finet[i+1] != -1)
+ FD_SET(finet[i+1], fdsr);
+ }
+ }
+ STAILQ_FOREACH(fx, &funixes, next)
+ FD_SET(fx->s, fdsr);
+
+ i = select(fdsrmax+1, fdsr, NULL, NULL,
+ needdofsync ? &tv : tvp);
+ switch (i) {
+ case 0:
+ dofsync();
+ needdofsync = 0;
+ if (tvp) {
+ tvp = NULL;
+ if (ppid != 1)
+ kill(ppid, SIGALRM);
+ }
+ continue;
+ case -1:
+ if (errno != EINTR)
+ logerror("select");
+ continue;
+ }
+ if (fklog != -1 && FD_ISSET(fklog, fdsr))
+ readklog();
+ if (finet && !SecureMode) {
+ for (i = 0; i < *finet; i++) {
+ if (FD_ISSET(finet[i+1], fdsr)) {
+ len = sizeof(frominet);
+ l = recvfrom(finet[i+1], line, MAXLINE,
+ 0, (struct sockaddr *)&frominet,
+ &len);
+ if (l > 0) {
+ line[l] = '\0';
+ hname = cvthname((struct sockaddr *)&frominet);
+ unmapped((struct sockaddr *)&frominet);
+ if (validate((struct sockaddr *)&frominet, hname))
+ printline(hname, line, RemoteAddDate ? ADDDATE : 0);
+ } else if (l < 0 && errno != EINTR)
+ logerror("recvfrom inet");
+ }
+ }
+ }
+ STAILQ_FOREACH(fx, &funixes, next) {
+ if (FD_ISSET(fx->s, fdsr)) {
+ len = sizeof(fromunix);
+ l = recvfrom(fx->s, line, MAXLINE, 0,
+ (struct sockaddr *)&fromunix, &len);
+ if (l > 0) {
+ line[l] = '\0';
+ printline(LocalHostName, line, 0);
+ } else if (l < 0 && errno != EINTR)
+ logerror("recvfrom unix");
+ }
+ }
+ }
+ if (fdsr)
+ free(fdsr);
+}
+
+static void
+unmapped(struct sockaddr *sa)
+{
+ struct sockaddr_in6 *sin6;
+ struct sockaddr_in sin4;
+
+ if (sa->sa_family != AF_INET6)
+ return;
+ if (sa->sa_len != sizeof(struct sockaddr_in6) ||
+ sizeof(sin4) > sa->sa_len)
+ return;
+ sin6 = (struct sockaddr_in6 *)sa;
+ if (!IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr))
+ return;
+
+ memset(&sin4, 0, sizeof(sin4));
+ sin4.sin_family = AF_INET;
+ sin4.sin_len = sizeof(struct sockaddr_in);
+ memcpy(&sin4.sin_addr, &sin6->sin6_addr.s6_addr[12],
+ sizeof(sin4.sin_addr));
+ sin4.sin_port = sin6->sin6_port;
+
+ memcpy(sa, &sin4, sin4.sin_len);
+}
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "%s\n%s\n%s\n%s\n",
+ "usage: syslogd [-468ACcdknosTuv] [-a allowed_peer]",
+ " [-b bind_address] [-f config_file]",
+ " [-l [mode:]path] [-m mark_interval]",
+ " [-P pid_file] [-p log_socket]");
+ exit(1);
+}
+
+/*
+ * Take a raw input line, decode the message, and print the message
+ * on the appropriate log files.
+ */
+static void
+printline(const char *hname, char *msg, int flags)
+{
+ char *p, *q;
+ long n;
+ int c, pri;
+ char line[MAXLINE + 1];
+
+ /* test for special codes */
+ p = msg;
+ pri = DEFUPRI;
+ if (*p == '<') {
+ errno = 0;
+ n = strtol(p + 1, &q, 10);
+ if (*q == '>' && n >= 0 && n < INT_MAX && errno == 0) {
+ p = q + 1;
+ pri = n;
+ }
+ }
+ if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
+ pri = DEFUPRI;
+
+ /*
+ * Don't allow users to log kernel messages.
+ * NOTE: since LOG_KERN == 0 this will also match
+ * messages with no facility specified.
+ */
+ if ((pri & LOG_FACMASK) == LOG_KERN && !KeepKernFac)
+ pri = LOG_MAKEPRI(LOG_USER, LOG_PRI(pri));
+
+ q = line;
+
+ while ((c = (unsigned char)*p++) != '\0' &&
+ q < &line[sizeof(line) - 4]) {
+ if (mask_C1 && (c & 0x80) && c < 0xA0) {
+ c &= 0x7F;
+ *q++ = 'M';
+ *q++ = '-';
+ }
+ if (isascii(c) && iscntrl(c)) {
+ if (c == '\n') {
+ *q++ = ' ';
+ } else if (c == '\t') {
+ *q++ = '\t';
+ } else {
+ *q++ = '^';
+ *q++ = c ^ 0100;
+ }
+ } else {
+ *q++ = c;
+ }
+ }
+ *q = '\0';
+
+ logmsg(pri, line, hname, flags);
+}
+
+/*
+ * Read /dev/klog while data are available, split into lines.
+ */
+static void
+readklog(void)
+{
+ char *p, *q, line[MAXLINE + 1];
+ int len, i;
+
+ len = 0;
+ for (;;) {
+ i = read(fklog, line + len, MAXLINE - 1 - len);
+ if (i > 0) {
+ line[i + len] = '\0';
+ } else {
+ if (i < 0 && errno != EINTR && errno != EAGAIN) {
+ logerror("klog");
+ fklog = -1;
+ }
+ break;
+ }
+
+ for (p = line; (q = strchr(p, '\n')) != NULL; p = q + 1) {
+ *q = '\0';
+ printsys(p);
+ }
+ len = strlen(p);
+ if (len >= MAXLINE - 1) {
+ printsys(p);
+ len = 0;
+ }
+ if (len > 0)
+ memmove(line, p, len + 1);
+ }
+ if (len > 0)
+ printsys(line);
+}
+
+/*
+ * Take a raw input line from /dev/klog, format similar to syslog().
+ */
+static void
+printsys(char *msg)
+{
+ char *p, *q;
+ long n;
+ int flags, isprintf, pri;
+
+ flags = ISKERNEL | SYNC_FILE | ADDDATE; /* fsync after write */
+ p = msg;
+ pri = DEFSPRI;
+ isprintf = 1;
+ if (*p == '<') {
+ errno = 0;
+ n = strtol(p + 1, &q, 10);
+ if (*q == '>' && n >= 0 && n < INT_MAX && errno == 0) {
+ p = q + 1;
+ pri = n;
+ isprintf = 0;
+ }
+ }
+ /*
+ * Kernel printf's and LOG_CONSOLE messages have been displayed
+ * on the console already.
+ */
+ if (isprintf || (pri & LOG_FACMASK) == LOG_CONSOLE)
+ flags |= IGN_CONS;
+ if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
+ pri = DEFSPRI;
+ logmsg(pri, p, LocalHostName, flags);
+}
+
+static time_t now;
+
+/*
+ * Match a program or host name against a specification.
+ * Return a non-0 value if the message must be ignored
+ * based on the specification.
+ */
+static int
+skip_message(const char *name, const char *spec, int checkcase)
+{
+ const char *s;
+ char prev, next;
+ int exclude = 0;
+ /* Behaviour on explicit match */
+
+ if (spec == NULL)
+ return 0;
+ switch (*spec) {
+ case '-':
+ exclude = 1;
+ /*FALLTHROUGH*/
+ case '+':
+ spec++;
+ break;
+ default:
+ break;
+ }
+ if (checkcase)
+ s = strstr (spec, name);
+ else
+ s = strcasestr (spec, name);
+
+ if (s != NULL) {
+ prev = (s == spec ? ',' : *(s - 1));
+ next = *(s + strlen (name));
+
+ if (prev == ',' && (next == '\0' || next == ','))
+ /* Explicit match: skip iff the spec is an
+ exclusive one. */
+ return exclude;
+ }
+
+ /* No explicit match for this name: skip the message iff
+ the spec is an inclusive one. */
+ return !exclude;
+}
+
+/*
+ * Log a message to the appropriate log files, users, etc. based on
+ * the priority.
+ */
+static void
+logmsg(int pri, const char *msg, const char *from, int flags)
+{
+ struct filed *f;
+ int i, fac, msglen, omask, prilev;
+ const char *timestamp;
+ char prog[NAME_MAX+1];
+ char buf[MAXLINE+1];
+
+ dprintf("logmsg: pri %o, flags %x, from %s, msg %s\n",
+ pri, flags, from, msg);
+
+ omask = sigblock(sigmask(SIGHUP)|sigmask(SIGALRM));
+
+ /*
+ * Check to see if msg looks non-standard.
+ */
+ msglen = strlen(msg);
+ if (msglen < 16 || msg[3] != ' ' || msg[6] != ' ' ||
+ msg[9] != ':' || msg[12] != ':' || msg[15] != ' ')
+ flags |= ADDDATE;
+
+ (void)time(&now);
+ if (flags & ADDDATE) {
+ timestamp = ctime(&now) + 4;
+ } else {
+ timestamp = msg;
+ msg += 16;
+ msglen -= 16;
+ }
+
+ /* skip leading blanks */
+ while (isspace(*msg)) {
+ msg++;
+ msglen--;
+ }
+
+ /* extract facility and priority level */
+ if (flags & MARK)
+ fac = LOG_NFACILITIES;
+ else
+ fac = LOG_FAC(pri);
+
+ /* Check maximum facility number. */
+ if (fac > LOG_NFACILITIES) {
+ (void)sigsetmask(omask);
+ return;
+ }
+
+ prilev = LOG_PRI(pri);
+
+ /* extract program name */
+ for (i = 0; i < NAME_MAX; i++) {
+ if (!isprint(msg[i]) || msg[i] == ':' || msg[i] == '[' ||
+ msg[i] == '/' || isspace(msg[i]))
+ break;
+ prog[i] = msg[i];
+ }
+ prog[i] = 0;
+
+ /* add kernel prefix for kernel messages */
+ if (flags & ISKERNEL) {
+ snprintf(buf, sizeof(buf), "%s: %s",
+ use_bootfile ? bootfile : "kernel", msg);
+ msg = buf;
+ msglen = strlen(buf);
+ }
+
+ /* log the message to the particular outputs */
+ if (!Initialized) {
+ f = &consfile;
+ /*
+ * Open in non-blocking mode to avoid hangs during open
+ * and close(waiting for the port to drain).
+ */
+ f->f_file = open(ctty, O_WRONLY | O_NONBLOCK, 0);
+
+ if (f->f_file >= 0) {
+ (void)strlcpy(f->f_lasttime, timestamp,
+ sizeof(f->f_lasttime));
+ fprintlog(f, flags, msg);
+ (void)close(f->f_file);
+ }
+ (void)sigsetmask(omask);
+ return;
+ }
+ for (f = Files; f; f = f->f_next) {
+ /* skip messages that are incorrect priority */
+ if (!(((f->f_pcmp[fac] & PRI_EQ) && (f->f_pmask[fac] == prilev))
+ ||((f->f_pcmp[fac] & PRI_LT) && (f->f_pmask[fac] < prilev))
+ ||((f->f_pcmp[fac] & PRI_GT) && (f->f_pmask[fac] > prilev))
+ )
+ || f->f_pmask[fac] == INTERNAL_NOPRI)
+ continue;
+
+ /* skip messages with the incorrect hostname */
+ if (skip_message(from, f->f_host, 0))
+ continue;
+
+ /* skip messages with the incorrect program name */
+ if (skip_message(prog, f->f_program, 1))
+ continue;
+
+ /* skip message to console if it has already been printed */
+ if (f->f_type == F_CONSOLE && (flags & IGN_CONS))
+ continue;
+
+ /* don't output marks to recently written files */
+ if ((flags & MARK) && (now - f->f_time) < MarkInterval / 2)
+ continue;
+
+ /*
+ * suppress duplicate lines to this file
+ */
+ if (no_compress - (f->f_type != F_PIPE) < 1 &&
+ (flags & MARK) == 0 && msglen == f->f_prevlen &&
+ f->f_prevline && !strcmp(msg, f->f_prevline) &&
+ !strcasecmp(from, f->f_prevhost)) {
+ (void)strlcpy(f->f_lasttime, timestamp,
+ sizeof(f->f_lasttime));
+ f->f_prevcount++;
+ dprintf("msg repeated %d times, %ld sec of %d\n",
+ f->f_prevcount, (long)(now - f->f_time),
+ repeatinterval[f->f_repeatcount]);
+ /*
+ * If domark would have logged this by now,
+ * flush it now (so we don't hold isolated messages),
+ * but back off so we'll flush less often
+ * in the future.
+ */
+ if (now > REPEATTIME(f)) {
+ fprintlog(f, flags, (char *)NULL);
+ BACKOFF(f);
+ }
+ } else {
+ /* new line, save it */
+ if (f->f_prevcount)
+ fprintlog(f, 0, (char *)NULL);
+ f->f_repeatcount = 0;
+ f->f_prevpri = pri;
+ (void)strlcpy(f->f_lasttime, timestamp,
+ sizeof(f->f_lasttime));
+ (void)strlcpy(f->f_prevhost, from,
+ sizeof(f->f_prevhost));
+ if (msglen < MAXSVLINE) {
+ f->f_prevlen = msglen;
+ (void)strlcpy(f->f_prevline, msg, sizeof(f->f_prevline));
+ fprintlog(f, flags, (char *)NULL);
+ } else {
+ f->f_prevline[0] = 0;
+ f->f_prevlen = 0;
+ fprintlog(f, flags, msg);
+ }
+ }
+ }
+ (void)sigsetmask(omask);
+}
+
+static void
+dofsync(void)
+{
+ struct filed *f;
+
+ for (f = Files; f; f = f->f_next) {
+ if ((f->f_type == F_FILE) &&
+ (f->f_flags & FFLAG_NEEDSYNC)) {
+ f->f_flags &= ~FFLAG_NEEDSYNC;
+ (void)fsync(f->f_file);
+ }
+ }
+}
+
+#define IOV_SIZE 7
+static void
+fprintlog(struct filed *f, int flags, const char *msg)
+{
+ struct iovec iov[IOV_SIZE];
+ struct iovec *v;
+ struct addrinfo *r;
+ int i, l, lsent = 0;
+ char line[MAXLINE + 1], repbuf[80], greetings[200], *wmsg = NULL;
+ char nul[] = "", space[] = " ", lf[] = "\n", crlf[] = "\r\n";
+ const char *msgret;
+
+ v = iov;
+ if (f->f_type == F_WALL) {
+ v->iov_base = greetings;
+ /* The time displayed is not synchornized with the other log
+ * destinations (like messages). Following fragment was using
+ * ctime(&now), which was updating the time every 30 sec.
+ * With f_lasttime, time is synchronized correctly.
+ */
+ v->iov_len = snprintf(greetings, sizeof greetings,
+ "\r\n\7Message from syslogd@%s at %.24s ...\r\n",
+ f->f_prevhost, f->f_lasttime);
+ if (v->iov_len > 0)
+ v++;
+ v->iov_base = nul;
+ v->iov_len = 0;
+ v++;
+ } else {
+ v->iov_base = f->f_lasttime;
+ v->iov_len = strlen(f->f_lasttime);
+ v++;
+ v->iov_base = space;
+ v->iov_len = 1;
+ v++;
+ }
+
+ if (LogFacPri) {
+ static char fp_buf[30]; /* Hollow laugh */
+ int fac = f->f_prevpri & LOG_FACMASK;
+ int pri = LOG_PRI(f->f_prevpri);
+ const char *f_s = NULL;
+ char f_n[5]; /* Hollow laugh */
+ const char *p_s = NULL;
+ char p_n[5]; /* Hollow laugh */
+
+ if (LogFacPri > 1) {
+ CODE *c;
+
+ for (c = facilitynames; c->c_name; c++) {
+ if (c->c_val == fac) {
+ f_s = c->c_name;
+ break;
+ }
+ }
+ for (c = prioritynames; c->c_name; c++) {
+ if (c->c_val == pri) {
+ p_s = c->c_name;
+ break;
+ }
+ }
+ }
+ if (!f_s) {
+ snprintf(f_n, sizeof f_n, "%d", LOG_FAC(fac));
+ f_s = f_n;
+ }
+ if (!p_s) {
+ snprintf(p_n, sizeof p_n, "%d", pri);
+ p_s = p_n;
+ }
+ snprintf(fp_buf, sizeof fp_buf, "<%s.%s> ", f_s, p_s);
+ v->iov_base = fp_buf;
+ v->iov_len = strlen(fp_buf);
+ } else {
+ v->iov_base = nul;
+ v->iov_len = 0;
+ }
+ v++;
+
+ v->iov_base = f->f_prevhost;
+ v->iov_len = strlen(v->iov_base);
+ v++;
+ v->iov_base = space;
+ v->iov_len = 1;
+ v++;
+
+ if (msg) {
+ wmsg = strdup(msg); /* XXX iov_base needs a `const' sibling. */
+ if (wmsg == NULL) {
+ logerror("strdup");
+ exit(1);
+ }
+ v->iov_base = wmsg;
+ v->iov_len = strlen(msg);
+ } else if (f->f_prevcount > 1) {
+ v->iov_base = repbuf;
+ v->iov_len = snprintf(repbuf, sizeof repbuf,
+ "last message repeated %d times", f->f_prevcount);
+ } else if (f->f_prevline) {
+ v->iov_base = f->f_prevline;
+ v->iov_len = f->f_prevlen;
+ } else {
+ return;
+ }
+ v++;
+
+ dprintf("Logging to %s", TypeNames[f->f_type]);
+ f->f_time = now;
+
+ switch (f->f_type) {
+ int port;
+ case F_UNUSED:
+ dprintf("\n");
+ break;
+
+ case F_FORW:
+ port = (int)ntohs(((struct sockaddr_in *)
+ (f->f_un.f_forw.f_addr->ai_addr))->sin_port);
+ if (port != 514) {
+ dprintf(" %s:%d\n", f->f_un.f_forw.f_hname, port);
+ } else {
+ dprintf(" %s\n", f->f_un.f_forw.f_hname);
+ }
+ /* check for local vs remote messages */
+ if (strcasecmp(f->f_prevhost, LocalHostName))
+ l = snprintf(line, sizeof line - 1,
+ "<%d>%.15s Forwarded from %s: %s",
+ f->f_prevpri, (char *)iov[0].iov_base,
+ f->f_prevhost, (char *)iov[5].iov_base);
+ else
+ l = snprintf(line, sizeof line - 1, "<%d>%.15s %s",
+ f->f_prevpri, (char *)iov[0].iov_base,
+ (char *)iov[5].iov_base);
+ if (l < 0)
+ l = 0;
+ else if (l > MAXLINE)
+ l = MAXLINE;
+
+ if (finet) {
+ for (r = f->f_un.f_forw.f_addr; r; r = r->ai_next) {
+ for (i = 0; i < *finet; i++) {
+#if 0
+ /*
+ * should we check AF first, or just
+ * trial and error? FWD
+ */
+ if (r->ai_family ==
+ address_family_of(finet[i+1]))
+#endif
+ lsent = sendto(finet[i+1], line, l, 0,
+ r->ai_addr, r->ai_addrlen);
+ if (lsent == l)
+ break;
+ }
+ if (lsent == l && !send_to_all)
+ break;
+ }
+ dprintf("lsent/l: %d/%d\n", lsent, l);
+ if (lsent != l) {
+ int e = errno;
+ logerror("sendto");
+ errno = e;
+ switch (errno) {
+ case ENOBUFS:
+ case ENETDOWN:
+ case EHOSTUNREACH:
+ case EHOSTDOWN:
+ break;
+ /* case EBADF: */
+ /* case EACCES: */
+ /* case ENOTSOCK: */
+ /* case EFAULT: */
+ /* case EMSGSIZE: */
+ /* case EAGAIN: */
+ /* case ENOBUFS: */
+ /* case ECONNREFUSED: */
+ default:
+ dprintf("removing entry\n");
+ f->f_type = F_UNUSED;
+ break;
+ }
+ }
+ }
+ break;
+
+ case F_FILE:
+ dprintf(" %s\n", f->f_un.f_fname);
+ v->iov_base = lf;
+ v->iov_len = 1;
+ if (writev(f->f_file, iov, IOV_SIZE) < 0) {
+ /*
+ * If writev(2) fails for potentially transient errors
+ * like the filesystem being full, ignore it.
+ * Otherwise remove this logfile from the list.
+ */
+ if (errno != ENOSPC) {
+ int e = errno;
+ (void)close(f->f_file);
+ f->f_type = F_UNUSED;
+ errno = e;
+ logerror(f->f_un.f_fname);
+ }
+ } else if ((flags & SYNC_FILE) && (f->f_flags & FFLAG_SYNC)) {
+ f->f_flags |= FFLAG_NEEDSYNC;
+ needdofsync = 1;
+ }
+ break;
+
+ case F_PIPE:
+ dprintf(" %s\n", f->f_un.f_pipe.f_pname);
+ v->iov_base = lf;
+ v->iov_len = 1;
+ if (f->f_un.f_pipe.f_pid == 0) {
+ if ((f->f_file = p_open(f->f_un.f_pipe.f_pname,
+ &f->f_un.f_pipe.f_pid)) < 0) {
+ f->f_type = F_UNUSED;
+ logerror(f->f_un.f_pipe.f_pname);
+ break;
+ }
+ }
+ if (writev(f->f_file, iov, IOV_SIZE) < 0) {
+ int e = errno;
+ (void)close(f->f_file);
+ if (f->f_un.f_pipe.f_pid > 0)
+ deadq_enter(f->f_un.f_pipe.f_pid,
+ f->f_un.f_pipe.f_pname);
+ f->f_un.f_pipe.f_pid = 0;
+ errno = e;
+ logerror(f->f_un.f_pipe.f_pname);
+ }
+ break;
+
+ case F_CONSOLE:
+ if (flags & IGN_CONS) {
+ dprintf(" (ignored)\n");
+ break;
+ }
+ /* FALLTHROUGH */
+
+ case F_TTY:
+ dprintf(" %s%s\n", _PATH_DEV, f->f_un.f_fname);
+ v->iov_base = crlf;
+ v->iov_len = 2;
+
+ errno = 0; /* ttymsg() only sometimes returns an errno */
+ if ((msgret = ttymsg(iov, IOV_SIZE, f->f_un.f_fname, 10))) {
+ f->f_type = F_UNUSED;
+ logerror(msgret);
+ }
+ break;
+
+ case F_USERS:
+ case F_WALL:
+ dprintf("\n");
+ v->iov_base = crlf;
+ v->iov_len = 2;
+ wallmsg(f, iov, IOV_SIZE);
+ break;
+ }
+ f->f_prevcount = 0;
+ free(wmsg);
+}
+
+/*
+ * WALLMSG -- Write a message to the world at large
+ *
+ * Write the specified message to either the entire
+ * world, or a list of approved users.
+ */
+static void
+wallmsg(struct filed *f, struct iovec *iov, const int iovlen)
+{
+ static int reenter; /* avoid calling ourselves */
+ FILE *uf;
+ struct utmp ut;
+ int i;
+ const char *p;
+ char line[sizeof(ut.ut_line) + 1];
+
+ if (reenter++)
+ return;
+ if ((uf = fopen(_PATH_UTMP, "r")) == NULL) {
+ logerror(_PATH_UTMP);
+ reenter = 0;
+ return;
+ }
+ /* NOSTRICT */
+ while (fread((char *)&ut, sizeof(ut), 1, uf) == 1) {
+ if (ut.ut_name[0] == '\0')
+ continue;
+ /* We must use strncpy since ut_* may not be NUL terminated. */
+ strncpy(line, ut.ut_line, sizeof(line) - 1);
+ line[sizeof(line) - 1] = '\0';
+ if (f->f_type == F_WALL) {
+ if ((p = ttymsg(iov, iovlen, line, TTYMSGTIME)) !=
+ NULL) {
+ errno = 0; /* already in msg */
+ logerror(p);
+ }
+ continue;
+ }
+ /* should we send the message to this user? */
+ for (i = 0; i < MAXUNAMES; i++) {
+ if (!f->f_un.f_uname[i][0])
+ break;
+ if (!strncmp(f->f_un.f_uname[i], ut.ut_name,
+ UT_NAMESIZE)) {
+ if ((p = ttymsg(iov, iovlen, line, TTYMSGTIME))
+ != NULL) {
+ errno = 0; /* already in msg */
+ logerror(p);
+ }
+ break;
+ }
+ }
+ }
+ (void)fclose(uf);
+ reenter = 0;
+}
+
+static void
+reapchild(int signo __unused)
+{
+ int status;
+ pid_t pid;
+ struct filed *f;
+
+ while ((pid = wait3(&status, WNOHANG, (struct rusage *)NULL)) > 0) {
+ if (!Initialized)
+ /* Don't tell while we are initting. */
+ continue;
+
+ /* First, look if it's a process from the dead queue. */
+ if (deadq_remove(pid))
+ goto oncemore;
+
+ /* Now, look in list of active processes. */
+ for (f = Files; f; f = f->f_next)
+ if (f->f_type == F_PIPE &&
+ f->f_un.f_pipe.f_pid == pid) {
+ (void)close(f->f_file);
+ f->f_un.f_pipe.f_pid = 0;
+ log_deadchild(pid, status,
+ f->f_un.f_pipe.f_pname);
+ break;
+ }
+ oncemore:
+ continue;
+ }
+}
+
+/*
+ * Return a printable representation of a host address.
+ */
+static const char *
+cvthname(struct sockaddr *f)
+{
+ int error, hl;
+ sigset_t omask, nmask;
+ static char hname[NI_MAXHOST], ip[NI_MAXHOST];
+
+ error = getnameinfo((struct sockaddr *)f,
+ ((struct sockaddr *)f)->sa_len,
+ ip, sizeof ip, NULL, 0, NI_NUMERICHOST);
+ dprintf("cvthname(%s)\n", ip);
+
+ if (error) {
+ dprintf("Malformed from address %s\n", gai_strerror(error));
+ return ("???");
+ }
+ if (!resolve)
+ return (ip);
+
+ sigemptyset(&nmask);
+ sigaddset(&nmask, SIGHUP);
+ sigprocmask(SIG_BLOCK, &nmask, &omask);
+ error = getnameinfo((struct sockaddr *)f,
+ ((struct sockaddr *)f)->sa_len,
+ hname, sizeof hname, NULL, 0, NI_NAMEREQD);
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+ if (error) {
+ dprintf("Host name for your address (%s) unknown\n", ip);
+ return (ip);
+ }
+ hl = strlen(hname);
+ if (hl > 0 && hname[hl-1] == '.')
+ hname[--hl] = '\0';
+ trimdomain(hname, hl);
+ return (hname);
+}
+
+static void
+dodie(int signo)
+{
+
+ WantDie = signo;
+}
+
+static void
+domark(int signo __unused)
+{
+
+ MarkSet = 1;
+}
+
+/*
+ * Print syslogd errors some place.
+ */
+static void
+logerror(const char *type)
+{
+ char buf[512];
+ static int recursed = 0;
+
+ /* If there's an error while trying to log an error, give up. */
+ if (recursed)
+ return;
+ recursed++;
+ if (errno)
+ (void)snprintf(buf,
+ sizeof buf, "syslogd: %s: %s", type, strerror(errno));
+ else
+ (void)snprintf(buf, sizeof buf, "syslogd: %s", type);
+ errno = 0;
+ dprintf("%s\n", buf);
+ logmsg(LOG_SYSLOG|LOG_ERR, buf, LocalHostName, ADDDATE);
+ recursed--;
+}
+
+static void
+die(int signo)
+{
+ struct filed *f;
+ struct funix *fx;
+ int was_initialized;
+ char buf[100];
+
+ was_initialized = Initialized;
+ Initialized = 0; /* Don't log SIGCHLDs. */
+ for (f = Files; f != NULL; f = f->f_next) {
+ /* flush any pending output */
+ if (f->f_prevcount)
+ fprintlog(f, 0, (char *)NULL);
+ if (f->f_type == F_PIPE && f->f_un.f_pipe.f_pid > 0) {
+ (void)close(f->f_file);
+ f->f_un.f_pipe.f_pid = 0;
+ }
+ }
+ Initialized = was_initialized;
+ if (signo) {
+ dprintf("syslogd: exiting on signal %d\n", signo);
+ (void)snprintf(buf, sizeof(buf), "exiting on signal %d", signo);
+ errno = 0;
+ logerror(buf);
+ }
+ STAILQ_FOREACH(fx, &funixes, next)
+ (void)unlink(fx->name);
+ pidfile_remove(pfh);
+
+ exit(1);
+}
+
+/*
+ * INIT -- Initialize syslogd from configuration table
+ */
+static void
+init(int signo)
+{
+ int i;
+ FILE *cf;
+ struct filed *f, *next, **nextp;
+ char *p;
+ char cline[LINE_MAX];
+ char prog[NAME_MAX+1];
+ char host[MAXHOSTNAMELEN];
+ char oldLocalHostName[MAXHOSTNAMELEN];
+ char hostMsg[2*MAXHOSTNAMELEN+40];
+ char bootfileMsg[LINE_MAX];
+
+ dprintf("init\n");
+
+ /*
+ * Load hostname (may have changed).
+ */
+ if (signo != 0)
+ (void)strlcpy(oldLocalHostName, LocalHostName,
+ sizeof(oldLocalHostName));
+ if (gethostname(LocalHostName, sizeof(LocalHostName)))
+ err(EX_OSERR, "gethostname() failed");
+ if ((p = strchr(LocalHostName, '.')) != NULL) {
+ *p++ = '\0';
+ LocalDomain = p;
+ } else {
+ LocalDomain = "";
+ }
+
+ /*
+ * Close all open log files.
+ */
+ Initialized = 0;
+ for (f = Files; f != NULL; f = next) {
+ /* flush any pending output */
+ if (f->f_prevcount)
+ fprintlog(f, 0, (char *)NULL);
+
+ switch (f->f_type) {
+ case F_FILE:
+ case F_FORW:
+ case F_CONSOLE:
+ case F_TTY:
+ (void)close(f->f_file);
+ break;
+ case F_PIPE:
+ if (f->f_un.f_pipe.f_pid > 0) {
+ (void)close(f->f_file);
+ deadq_enter(f->f_un.f_pipe.f_pid,
+ f->f_un.f_pipe.f_pname);
+ }
+ f->f_un.f_pipe.f_pid = 0;
+ break;
+ }
+ next = f->f_next;
+ if (f->f_program) free(f->f_program);
+ if (f->f_host) free(f->f_host);
+ free((char *)f);
+ }
+ Files = NULL;
+ nextp = &Files;
+
+ /* open the configuration file */
+ if ((cf = fopen(ConfFile, "r")) == NULL) {
+ dprintf("cannot open %s\n", ConfFile);
+ *nextp = (struct filed *)calloc(1, sizeof(*f));
+ if (*nextp == NULL) {
+ logerror("calloc");
+ exit(1);
+ }
+ cfline("*.ERR\t/dev/console", *nextp, "*", "*");
+ (*nextp)->f_next = (struct filed *)calloc(1, sizeof(*f));
+ if ((*nextp)->f_next == NULL) {
+ logerror("calloc");
+ exit(1);
+ }
+ cfline("*.PANIC\t*", (*nextp)->f_next, "*", "*");
+ Initialized = 1;
+ return;
+ }
+
+ /*
+ * Foreach line in the conf table, open that file.
+ */
+ f = NULL;
+ (void)strlcpy(host, "*", sizeof(host));
+ (void)strlcpy(prog, "*", sizeof(prog));
+ while (fgets(cline, sizeof(cline), cf) != NULL) {
+ /*
+ * check for end-of-section, comments, strip off trailing
+ * spaces and newline character. #!prog is treated specially:
+ * following lines apply only to that program.
+ */
+ for (p = cline; isspace(*p); ++p)
+ continue;
+ if (*p == 0)
+ continue;
+ if (*p == '#') {
+ p++;
+ if (*p != '!' && *p != '+' && *p != '-')
+ continue;
+ }
+ if (*p == '+' || *p == '-') {
+ host[0] = *p++;
+ while (isspace(*p))
+ p++;
+ if ((!*p) || (*p == '*')) {
+ (void)strlcpy(host, "*", sizeof(host));
+ continue;
+ }
+ if (*p == '@')
+ p = LocalHostName;
+ for (i = 1; i < MAXHOSTNAMELEN - 1; i++) {
+ if (!isalnum(*p) && *p != '.' && *p != '-'
+ && *p != ',' && *p != ':' && *p != '%')
+ break;
+ host[i] = *p++;
+ }
+ host[i] = '\0';
+ continue;
+ }
+ if (*p == '!') {
+ p++;
+ while (isspace(*p)) p++;
+ if ((!*p) || (*p == '*')) {
+ (void)strlcpy(prog, "*", sizeof(prog));
+ continue;
+ }
+ for (i = 0; i < NAME_MAX; i++) {
+ if (!isprint(p[i]) || isspace(p[i]))
+ break;
+ prog[i] = p[i];
+ }
+ prog[i] = 0;
+ continue;
+ }
+ for (p = cline + 1; *p != '\0'; p++) {
+ if (*p != '#')
+ continue;
+ if (*(p - 1) == '\\') {
+ strcpy(p - 1, p);
+ p--;
+ continue;
+ }
+ *p = '\0';
+ break;
+ }
+ for (i = strlen(cline) - 1; i >= 0 && isspace(cline[i]); i--)
+ cline[i] = '\0';
+ f = (struct filed *)calloc(1, sizeof(*f));
+ if (f == NULL) {
+ logerror("calloc");
+ exit(1);
+ }
+ *nextp = f;
+ nextp = &f->f_next;
+ cfline(cline, f, prog, host);
+ }
+
+ /* close the configuration file */
+ (void)fclose(cf);
+
+ Initialized = 1;
+
+ if (Debug) {
+ int port;
+ for (f = Files; f; f = f->f_next) {
+ for (i = 0; i <= LOG_NFACILITIES; i++)
+ if (f->f_pmask[i] == INTERNAL_NOPRI)
+ printf("X ");
+ else
+ printf("%d ", f->f_pmask[i]);
+ printf("%s: ", TypeNames[f->f_type]);
+ switch (f->f_type) {
+ case F_FILE:
+ printf("%s", f->f_un.f_fname);
+ break;
+
+ case F_CONSOLE:
+ case F_TTY:
+ printf("%s%s", _PATH_DEV, f->f_un.f_fname);
+ break;
+
+ case F_FORW:
+ port = (int)ntohs(((struct sockaddr_in *)
+ (f->f_un.f_forw.f_addr->ai_addr))->sin_port);
+ if (port != 514) {
+ printf("%s:%d",
+ f->f_un.f_forw.f_hname, port);
+ } else {
+ printf("%s", f->f_un.f_forw.f_hname);
+ }
+ break;
+
+ case F_PIPE:
+ printf("%s", f->f_un.f_pipe.f_pname);
+ break;
+
+ case F_USERS:
+ for (i = 0; i < MAXUNAMES && *f->f_un.f_uname[i]; i++)
+ printf("%s, ", f->f_un.f_uname[i]);
+ break;
+ }
+ if (f->f_program)
+ printf(" (%s)", f->f_program);
+ printf("\n");
+ }
+ }
+
+ logmsg(LOG_SYSLOG|LOG_INFO, "syslogd: restart", LocalHostName, ADDDATE);
+ dprintf("syslogd: restarted\n");
+ /*
+ * Log a change in hostname, but only on a restart.
+ */
+ if (signo != 0 && strcmp(oldLocalHostName, LocalHostName) != 0) {
+ (void)snprintf(hostMsg, sizeof(hostMsg),
+ "syslogd: hostname changed, \"%s\" to \"%s\"",
+ oldLocalHostName, LocalHostName);
+ logmsg(LOG_SYSLOG|LOG_INFO, hostMsg, LocalHostName, ADDDATE);
+ dprintf("%s\n", hostMsg);
+ }
+ /*
+ * Log the kernel boot file if we aren't going to use it as
+ * the prefix, and if this is *not* a restart.
+ */
+ if (signo == 0 && !use_bootfile) {
+ (void)snprintf(bootfileMsg, sizeof(bootfileMsg),
+ "syslogd: kernel boot file is %s", bootfile);
+ logmsg(LOG_KERN|LOG_INFO, bootfileMsg, LocalHostName, ADDDATE);
+ dprintf("%s\n", bootfileMsg);
+ }
+}
+
+/*
+ * Crack a configuration file line
+ */
+static void
+cfline(const char *line, struct filed *f, const char *prog, const char *host)
+{
+ struct addrinfo hints, *res;
+ int error, i, pri, syncfile;
+ const char *p, *q;
+ char *bp;
+ char buf[MAXLINE], ebuf[100];
+
+ dprintf("cfline(\"%s\", f, \"%s\", \"%s\")\n", line, prog, host);
+
+ errno = 0; /* keep strerror() stuff out of logerror messages */
+
+ /* clear out file entry */
+ memset(f, 0, sizeof(*f));
+ for (i = 0; i <= LOG_NFACILITIES; i++)
+ f->f_pmask[i] = INTERNAL_NOPRI;
+
+ /* save hostname if any */
+ if (host && *host == '*')
+ host = NULL;
+ if (host) {
+ int hl;
+
+ f->f_host = strdup(host);
+ if (f->f_host == NULL) {
+ logerror("strdup");
+ exit(1);
+ }
+ hl = strlen(f->f_host);
+ if (hl > 0 && f->f_host[hl-1] == '.')
+ f->f_host[--hl] = '\0';
+ trimdomain(f->f_host, hl);
+ }
+
+ /* save program name if any */
+ if (prog && *prog == '*')
+ prog = NULL;
+ if (prog) {
+ f->f_program = strdup(prog);
+ if (f->f_program == NULL) {
+ logerror("strdup");
+ exit(1);
+ }
+ }
+
+ /* scan through the list of selectors */
+ for (p = line; *p && *p != '\t' && *p != ' ';) {
+ int pri_done;
+ int pri_cmp;
+ int pri_invert;
+
+ /* find the end of this facility name list */
+ for (q = p; *q && *q != '\t' && *q != ' ' && *q++ != '.'; )
+ continue;
+
+ /* get the priority comparison */
+ pri_cmp = 0;
+ pri_done = 0;
+ pri_invert = 0;
+ if (*q == '!') {
+ pri_invert = 1;
+ q++;
+ }
+ while (!pri_done) {
+ switch (*q) {
+ case '<':
+ pri_cmp |= PRI_LT;
+ q++;
+ break;
+ case '=':
+ pri_cmp |= PRI_EQ;
+ q++;
+ break;
+ case '>':
+ pri_cmp |= PRI_GT;
+ q++;
+ break;
+ default:
+ pri_done++;
+ break;
+ }
+ }
+
+ /* collect priority name */
+ for (bp = buf; *q && !strchr("\t,; ", *q); )
+ *bp++ = *q++;
+ *bp = '\0';
+
+ /* skip cruft */
+ while (strchr(",;", *q))
+ q++;
+
+ /* decode priority name */
+ if (*buf == '*') {
+ pri = LOG_PRIMASK;
+ pri_cmp = PRI_LT | PRI_EQ | PRI_GT;
+ } else {
+ /* Ignore trailing spaces. */
+ for (i = strlen(buf) - 1; i >= 0 && buf[i] == ' '; i--)
+ buf[i] = '\0';
+
+ pri = decode(buf, prioritynames);
+ if (pri < 0) {
+ (void)snprintf(ebuf, sizeof ebuf,
+ "unknown priority name \"%s\"", buf);
+ logerror(ebuf);
+ return;
+ }
+ }
+ if (!pri_cmp)
+ pri_cmp = (UniquePriority)
+ ? (PRI_EQ)
+ : (PRI_EQ | PRI_GT)
+ ;
+ if (pri_invert)
+ pri_cmp ^= PRI_LT | PRI_EQ | PRI_GT;
+
+ /* scan facilities */
+ while (*p && !strchr("\t.; ", *p)) {
+ for (bp = buf; *p && !strchr("\t,;. ", *p); )
+ *bp++ = *p++;
+ *bp = '\0';
+
+ if (*buf == '*') {
+ for (i = 0; i < LOG_NFACILITIES; i++) {
+ f->f_pmask[i] = pri;
+ f->f_pcmp[i] = pri_cmp;
+ }
+ } else {
+ i = decode(buf, facilitynames);
+ if (i < 0) {
+ (void)snprintf(ebuf, sizeof ebuf,
+ "unknown facility name \"%s\"",
+ buf);
+ logerror(ebuf);
+ return;
+ }
+ f->f_pmask[i >> 3] = pri;
+ f->f_pcmp[i >> 3] = pri_cmp;
+ }
+ while (*p == ',' || *p == ' ')
+ p++;
+ }
+
+ p = q;
+ }
+
+ /* skip to action part */
+ while (*p == '\t' || *p == ' ')
+ p++;
+
+ if (*p == '-') {
+ syncfile = 0;
+ p++;
+ } else
+ syncfile = 1;
+
+ switch (*p) {
+ case '@':
+ {
+ char *tp;
+ /*
+ * scan forward to see if there is a port defined.
+ * so we can't use strlcpy..
+ */
+ i = sizeof(f->f_un.f_forw.f_hname);
+ tp = f->f_un.f_forw.f_hname;
+ p++;
+
+ while (*p && (*p != ':') && (i-- > 0)) {
+ *tp++ = *p++;
+ }
+ *tp = '\0';
+ }
+ /* See if we copied a domain and have a port */
+ if (*p == ':')
+ p++;
+ else
+ p = NULL;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = family;
+ hints.ai_socktype = SOCK_DGRAM;
+ error = getaddrinfo(f->f_un.f_forw.f_hname,
+ p ? p : "syslog", &hints, &res);
+ if (error) {
+ logerror(gai_strerror(error));
+ break;
+ }
+ f->f_un.f_forw.f_addr = res;
+ f->f_type = F_FORW;
+ break;
+
+ case '/':
+ if ((f->f_file = open(p, logflags, 0600)) < 0) {
+ f->f_type = F_UNUSED;
+ logerror(p);
+ break;
+ }
+ if (syncfile)
+ f->f_flags |= FFLAG_SYNC;
+ if (isatty(f->f_file)) {
+ if (strcmp(p, ctty) == 0)
+ f->f_type = F_CONSOLE;
+ else
+ f->f_type = F_TTY;
+ (void)strlcpy(f->f_un.f_fname, p + sizeof(_PATH_DEV) - 1,
+ sizeof(f->f_un.f_fname));
+ } else {
+ (void)strlcpy(f->f_un.f_fname, p, sizeof(f->f_un.f_fname));
+ f->f_type = F_FILE;
+ }
+ break;
+
+ case '|':
+ f->f_un.f_pipe.f_pid = 0;
+ (void)strlcpy(f->f_un.f_pipe.f_pname, p + 1,
+ sizeof(f->f_un.f_pipe.f_pname));
+ f->f_type = F_PIPE;
+ break;
+
+ case '*':
+ f->f_type = F_WALL;
+ break;
+
+ default:
+ for (i = 0; i < MAXUNAMES && *p; i++) {
+ for (q = p; *q && *q != ','; )
+ q++;
+ (void)strncpy(f->f_un.f_uname[i], p, UT_NAMESIZE);
+ if ((q - p) > UT_NAMESIZE)
+ f->f_un.f_uname[i][UT_NAMESIZE] = '\0';
+ else
+ f->f_un.f_uname[i][q - p] = '\0';
+ while (*q == ',' || *q == ' ')
+ q++;
+ p = q;
+ }
+ f->f_type = F_USERS;
+ break;
+ }
+}
+
+
+/*
+ * Decode a symbolic name to a numeric value
+ */
+static int
+decode(const char *name, CODE *codetab)
+{
+ CODE *c;
+ char *p, buf[40];
+
+ if (isdigit(*name))
+ return (atoi(name));
+
+ for (p = buf; *name && p < &buf[sizeof(buf) - 1]; p++, name++) {
+ if (isupper(*name))
+ *p = tolower(*name);
+ else
+ *p = *name;
+ }
+ *p = '\0';
+ for (c = codetab; c->c_name; c++)
+ if (!strcmp(buf, c->c_name))
+ return (c->c_val);
+
+ return (-1);
+}
+
+static void
+markit(void)
+{
+ struct filed *f;
+ dq_t q, next;
+
+ now = time((time_t *)NULL);
+ MarkSeq += TIMERINTVL;
+ if (MarkSeq >= MarkInterval) {
+ logmsg(LOG_INFO, "-- MARK --",
+ LocalHostName, ADDDATE|MARK);
+ MarkSeq = 0;
+ }
+
+ for (f = Files; f; f = f->f_next) {
+ if (f->f_prevcount && now >= REPEATTIME(f)) {
+ dprintf("flush %s: repeated %d times, %d sec.\n",
+ TypeNames[f->f_type], f->f_prevcount,
+ repeatinterval[f->f_repeatcount]);
+ fprintlog(f, 0, (char *)NULL);
+ BACKOFF(f);
+ }
+ }
+
+ /* Walk the dead queue, and see if we should signal somebody. */
+ for (q = TAILQ_FIRST(&deadq_head); q != NULL; q = next) {
+ next = TAILQ_NEXT(q, dq_entries);
+
+ switch (q->dq_timeout) {
+ case 0:
+ /* Already signalled once, try harder now. */
+ if (kill(q->dq_pid, SIGKILL) != 0)
+ (void)deadq_remove(q->dq_pid);
+ break;
+
+ case 1:
+ /*
+ * Timed out on dead queue, send terminate
+ * signal. Note that we leave the removal
+ * from the dead queue to reapchild(), which
+ * will also log the event (unless the process
+ * didn't even really exist, in case we simply
+ * drop it from the dead queue).
+ */
+ if (kill(q->dq_pid, SIGTERM) != 0)
+ (void)deadq_remove(q->dq_pid);
+ /* FALLTHROUGH */
+
+ default:
+ q->dq_timeout--;
+ }
+ }
+ MarkSet = 0;
+ (void)alarm(TIMERINTVL);
+}
+
+/*
+ * fork off and become a daemon, but wait for the child to come online
+ * before returing to the parent, or we get disk thrashing at boot etc.
+ * Set a timer so we don't hang forever if it wedges.
+ */
+static int
+waitdaemon(int nochdir, int noclose, int maxwait)
+{
+ int fd;
+ int status;
+ pid_t pid, childpid;
+
+ switch (childpid = fork()) {
+ case -1:
+ return (-1);
+ case 0:
+ break;
+ default:
+ signal(SIGALRM, timedout);
+ alarm(maxwait);
+ while ((pid = wait3(&status, 0, NULL)) != -1) {
+ if (WIFEXITED(status))
+ errx(1, "child pid %d exited with return code %d",
+ pid, WEXITSTATUS(status));
+ if (WIFSIGNALED(status))
+ errx(1, "child pid %d exited on signal %d%s",
+ pid, WTERMSIG(status),
+ WCOREDUMP(status) ? " (core dumped)" :
+ "");
+ if (pid == childpid) /* it's gone... */
+ break;
+ }
+ exit(0);
+ }
+
+ if (setsid() == -1)
+ return (-1);
+
+ if (!nochdir)
+ (void)chdir("/");
+
+ if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
+ (void)dup2(fd, STDIN_FILENO);
+ (void)dup2(fd, STDOUT_FILENO);
+ (void)dup2(fd, STDERR_FILENO);
+ if (fd > 2)
+ (void)close (fd);
+ }
+ return (getppid());
+}
+
+/*
+ * We get a SIGALRM from the child when it's running and finished doing it's
+ * fsync()'s or O_SYNC writes for all the boot messages.
+ *
+ * We also get a signal from the kernel if the timer expires, so check to
+ * see what happened.
+ */
+static void
+timedout(int sig __unused)
+{
+ int left;
+ left = alarm(0);
+ signal(SIGALRM, SIG_DFL);
+ if (left == 0)
+ errx(1, "timed out waiting for child");
+ else
+ _exit(0);
+}
+
+/*
+ * Add `s' to the list of allowable peer addresses to accept messages
+ * from.
+ *
+ * `s' is a string in the form:
+ *
+ * [*]domainname[:{servicename|portnumber|*}]
+ *
+ * or
+ *
+ * netaddr/maskbits[:{servicename|portnumber|*}]
+ *
+ * Returns -1 on error, 0 if the argument was valid.
+ */
+static int
+allowaddr(char *s)
+{
+ char *cp1, *cp2;
+ struct allowedpeer ap;
+ struct servent *se;
+ int masklen = -1;
+ struct addrinfo hints, *res;
+ struct in_addr *addrp, *maskp;
+#ifdef INET6
+ int i;
+ u_int32_t *addr6p, *mask6p;
+#endif
+ char ip[NI_MAXHOST];
+
+#ifdef INET6
+ if (*s != '[' || (cp1 = strchr(s + 1, ']')) == NULL)
+#endif
+ cp1 = s;
+ if ((cp1 = strrchr(cp1, ':'))) {
+ /* service/port provided */
+ *cp1++ = '\0';
+ if (strlen(cp1) == 1 && *cp1 == '*')
+ /* any port allowed */
+ ap.port = 0;
+ else if ((se = getservbyname(cp1, "udp"))) {
+ ap.port = ntohs(se->s_port);
+ } else {
+ ap.port = strtol(cp1, &cp2, 0);
+ if (*cp2 != '\0')
+ return (-1); /* port not numeric */
+ }
+ } else {
+ if ((se = getservbyname("syslog", "udp")))
+ ap.port = ntohs(se->s_port);
+ else
+ /* sanity, should not happen */
+ ap.port = 514;
+ }
+
+ if ((cp1 = strchr(s, '/')) != NULL &&
+ strspn(cp1 + 1, "0123456789") == strlen(cp1 + 1)) {
+ *cp1 = '\0';
+ if ((masklen = atoi(cp1 + 1)) < 0)
+ return (-1);
+ }
+#ifdef INET6
+ if (*s == '[') {
+ cp2 = s + strlen(s) - 1;
+ if (*cp2 == ']') {
+ ++s;
+ *cp2 = '\0';
+ } else {
+ cp2 = NULL;
+ }
+ } else {
+ cp2 = NULL;
+ }
+#endif
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
+ if (getaddrinfo(s, NULL, &hints, &res) == 0) {
+ ap.isnumeric = 1;
+ memcpy(&ap.a_addr, res->ai_addr, res->ai_addrlen);
+ memset(&ap.a_mask, 0, sizeof(ap.a_mask));
+ ap.a_mask.ss_family = res->ai_family;
+ if (res->ai_family == AF_INET) {
+ ap.a_mask.ss_len = sizeof(struct sockaddr_in);
+ maskp = &((struct sockaddr_in *)&ap.a_mask)->sin_addr;
+ addrp = &((struct sockaddr_in *)&ap.a_addr)->sin_addr;
+ if (masklen < 0) {
+ /* use default netmask */
+ if (IN_CLASSA(ntohl(addrp->s_addr)))
+ maskp->s_addr = htonl(IN_CLASSA_NET);
+ else if (IN_CLASSB(ntohl(addrp->s_addr)))
+ maskp->s_addr = htonl(IN_CLASSB_NET);
+ else
+ maskp->s_addr = htonl(IN_CLASSC_NET);
+ } else if (masklen <= 32) {
+ /* convert masklen to netmask */
+ if (masklen == 0)
+ maskp->s_addr = 0;
+ else
+ maskp->s_addr = htonl(~((1 << (32 - masklen)) - 1));
+ } else {
+ freeaddrinfo(res);
+ return (-1);
+ }
+ /* Lose any host bits in the network number. */
+ addrp->s_addr &= maskp->s_addr;
+ }
+#ifdef INET6
+ else if (res->ai_family == AF_INET6 && masklen <= 128) {
+ ap.a_mask.ss_len = sizeof(struct sockaddr_in6);
+ if (masklen < 0)
+ masklen = 128;
+ mask6p = (u_int32_t *)&((struct sockaddr_in6 *)&ap.a_mask)->sin6_addr;
+ /* convert masklen to netmask */
+ while (masklen > 0) {
+ if (masklen < 32) {
+ *mask6p = htonl(~(0xffffffff >> masklen));
+ break;
+ }
+ *mask6p++ = 0xffffffff;
+ masklen -= 32;
+ }
+ /* Lose any host bits in the network number. */
+ mask6p = (u_int32_t *)&((struct sockaddr_in6 *)&ap.a_mask)->sin6_addr;
+ addr6p = (u_int32_t *)&((struct sockaddr_in6 *)&ap.a_addr)->sin6_addr;
+ for (i = 0; i < 4; i++)
+ addr6p[i] &= mask6p[i];
+ }
+#endif
+ else {
+ freeaddrinfo(res);
+ return (-1);
+ }
+ freeaddrinfo(res);
+ } else {
+ /* arg `s' is domain name */
+ ap.isnumeric = 0;
+ ap.a_name = s;
+ if (cp1)
+ *cp1 = '/';
+#ifdef INET6
+ if (cp2) {
+ *cp2 = ']';
+ --s;
+ }
+#endif
+ }
+
+ if (Debug) {
+ printf("allowaddr: rule %d: ", NumAllowed);
+ if (ap.isnumeric) {
+ printf("numeric, ");
+ getnameinfo((struct sockaddr *)&ap.a_addr,
+ ((struct sockaddr *)&ap.a_addr)->sa_len,
+ ip, sizeof ip, NULL, 0, NI_NUMERICHOST);
+ printf("addr = %s, ", ip);
+ getnameinfo((struct sockaddr *)&ap.a_mask,
+ ((struct sockaddr *)&ap.a_mask)->sa_len,
+ ip, sizeof ip, NULL, 0, NI_NUMERICHOST);
+ printf("mask = %s; ", ip);
+ } else {
+ printf("domainname = %s; ", ap.a_name);
+ }
+ printf("port = %d\n", ap.port);
+ }
+
+ if ((AllowedPeers = realloc(AllowedPeers,
+ ++NumAllowed * sizeof(struct allowedpeer)))
+ == NULL) {
+ logerror("realloc");
+ exit(1);
+ }
+ memcpy(&AllowedPeers[NumAllowed - 1], &ap, sizeof(struct allowedpeer));
+ return (0);
+}
+
+/*
+ * Validate that the remote peer has permission to log to us.
+ */
+static int
+validate(struct sockaddr *sa, const char *hname)
+{
+ int i;
+ size_t l1, l2;
+ char *cp, name[NI_MAXHOST], ip[NI_MAXHOST], port[NI_MAXSERV];
+ struct allowedpeer *ap;
+ struct sockaddr_in *sin4, *a4p = NULL, *m4p = NULL;
+#ifdef INET6
+ int j, reject;
+ struct sockaddr_in6 *sin6, *a6p = NULL, *m6p = NULL;
+#endif
+ struct addrinfo hints, *res;
+ u_short sport;
+
+ if (NumAllowed == 0)
+ /* traditional behaviour, allow everything */
+ return (1);
+
+ (void)strlcpy(name, hname, sizeof(name));
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_UNSPEC;
+ hints.ai_socktype = SOCK_DGRAM;
+ hints.ai_flags = AI_PASSIVE | AI_NUMERICHOST;
+ if (getaddrinfo(name, NULL, &hints, &res) == 0)
+ freeaddrinfo(res);
+ else if (strchr(name, '.') == NULL) {
+ strlcat(name, ".", sizeof name);
+ strlcat(name, LocalDomain, sizeof name);
+ }
+ if (getnameinfo(sa, sa->sa_len, ip, sizeof ip, port, sizeof port,
+ NI_NUMERICHOST | NI_NUMERICSERV) != 0)
+ return (0); /* for safety, should not occur */
+ dprintf("validate: dgram from IP %s, port %s, name %s;\n",
+ ip, port, name);
+ sport = atoi(port);
+
+ /* now, walk down the list */
+ for (i = 0, ap = AllowedPeers; i < NumAllowed; i++, ap++) {
+ if (ap->port != 0 && ap->port != sport) {
+ dprintf("rejected in rule %d due to port mismatch.\n", i);
+ continue;
+ }
+
+ if (ap->isnumeric) {
+ if (ap->a_addr.ss_family != sa->sa_family) {
+ dprintf("rejected in rule %d due to address family mismatch.\n", i);
+ continue;
+ }
+ if (ap->a_addr.ss_family == AF_INET) {
+ sin4 = (struct sockaddr_in *)sa;
+ a4p = (struct sockaddr_in *)&ap->a_addr;
+ m4p = (struct sockaddr_in *)&ap->a_mask;
+ if ((sin4->sin_addr.s_addr & m4p->sin_addr.s_addr)
+ != a4p->sin_addr.s_addr) {
+ dprintf("rejected in rule %d due to IP mismatch.\n", i);
+ continue;
+ }
+ }
+#ifdef INET6
+ else if (ap->a_addr.ss_family == AF_INET6) {
+ sin6 = (struct sockaddr_in6 *)sa;
+ a6p = (struct sockaddr_in6 *)&ap->a_addr;
+ m6p = (struct sockaddr_in6 *)&ap->a_mask;
+ if (a6p->sin6_scope_id != 0 &&
+ sin6->sin6_scope_id != a6p->sin6_scope_id) {
+ dprintf("rejected in rule %d due to scope mismatch.\n", i);
+ continue;
+ }
+ reject = 0;
+ for (j = 0; j < 16; j += 4) {
+ if ((*(u_int32_t *)&sin6->sin6_addr.s6_addr[j] & *(u_int32_t *)&m6p->sin6_addr.s6_addr[j])
+ != *(u_int32_t *)&a6p->sin6_addr.s6_addr[j]) {
+ ++reject;
+ break;
+ }
+ }
+ if (reject) {
+ dprintf("rejected in rule %d due to IP mismatch.\n", i);
+ continue;
+ }
+ }
+#endif
+ else
+ continue;
+ } else {
+ cp = ap->a_name;
+ l1 = strlen(name);
+ if (*cp == '*') {
+ /* allow wildmatch */
+ cp++;
+ l2 = strlen(cp);
+ if (l2 > l1 || memcmp(cp, &name[l1 - l2], l2) != 0) {
+ dprintf("rejected in rule %d due to name mismatch.\n", i);
+ continue;
+ }
+ } else {
+ /* exact match */
+ l2 = strlen(cp);
+ if (l2 != l1 || memcmp(cp, name, l1) != 0) {
+ dprintf("rejected in rule %d due to name mismatch.\n", i);
+ continue;
+ }
+ }
+ }
+ dprintf("accepted in rule %d.\n", i);
+ return (1); /* hooray! */
+ }
+ return (0);
+}
+
+/*
+ * Fairly similar to popen(3), but returns an open descriptor, as
+ * opposed to a FILE *.
+ */
+static int
+p_open(const char *prog, pid_t *rpid)
+{
+ int pfd[2], nulldesc, i;
+ pid_t pid;
+ sigset_t omask, mask;
+ char *argv[4]; /* sh -c cmd NULL */
+ char errmsg[200];
+
+ if (pipe(pfd) == -1)
+ return (-1);
+ if ((nulldesc = open(_PATH_DEVNULL, O_RDWR)) == -1)
+ /* we are royally screwed anyway */
+ return (-1);
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGALRM);
+ sigaddset(&mask, SIGHUP);
+ sigprocmask(SIG_BLOCK, &mask, &omask);
+ switch ((pid = fork())) {
+ case -1:
+ sigprocmask(SIG_SETMASK, &omask, 0);
+ close(nulldesc);
+ return (-1);
+
+ case 0:
+ argv[0] = strdup("sh");
+ argv[1] = strdup("-c");
+ argv[2] = strdup(prog);
+ argv[3] = NULL;
+ if (argv[0] == NULL || argv[1] == NULL || argv[2] == NULL) {
+ logerror("strdup");
+ exit(1);
+ }
+
+ alarm(0);
+ (void)setsid(); /* Avoid catching SIGHUPs. */
+
+ /*
+ * Throw away pending signals, and reset signal
+ * behaviour to standard values.
+ */
+ signal(SIGALRM, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ sigprocmask(SIG_SETMASK, &omask, 0);
+ signal(SIGPIPE, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+ signal(SIGALRM, SIG_DFL);
+ signal(SIGHUP, SIG_DFL);
+
+ dup2(pfd[0], STDIN_FILENO);
+ dup2(nulldesc, STDOUT_FILENO);
+ dup2(nulldesc, STDERR_FILENO);
+ for (i = getdtablesize(); i > 2; i--)
+ (void)close(i);
+
+ (void)execvp(_PATH_BSHELL, argv);
+ _exit(255);
+ }
+
+ sigprocmask(SIG_SETMASK, &omask, 0);
+ close(nulldesc);
+ close(pfd[0]);
+ /*
+ * Avoid blocking on a hung pipe. With O_NONBLOCK, we are
+ * supposed to get an EWOULDBLOCK on writev(2), which is
+ * caught by the logic above anyway, which will in turn close
+ * the pipe, and fork a new logging subprocess if necessary.
+ * The stale subprocess will be killed some time later unless
+ * it terminated itself due to closing its input pipe (so we
+ * get rid of really dead puppies).
+ */
+ if (fcntl(pfd[1], F_SETFL, O_NONBLOCK) == -1) {
+ /* This is bad. */
+ (void)snprintf(errmsg, sizeof errmsg,
+ "Warning: cannot change pipe to PID %d to "
+ "non-blocking behaviour.",
+ (int)pid);
+ logerror(errmsg);
+ }
+ *rpid = pid;
+ return (pfd[1]);
+}
+
+static void
+deadq_enter(pid_t pid, const char *name)
+{
+ dq_t p;
+ int status;
+
+ /*
+ * Be paranoid, if we can't signal the process, don't enter it
+ * into the dead queue (perhaps it's already dead). If possible,
+ * we try to fetch and log the child's status.
+ */
+ if (kill(pid, 0) != 0) {
+ if (waitpid(pid, &status, WNOHANG) > 0)
+ log_deadchild(pid, status, name);
+ return;
+ }
+
+ p = malloc(sizeof(struct deadq_entry));
+ if (p == NULL) {
+ logerror("malloc");
+ exit(1);
+ }
+
+ p->dq_pid = pid;
+ p->dq_timeout = DQ_TIMO_INIT;
+ TAILQ_INSERT_TAIL(&deadq_head, p, dq_entries);
+}
+
+static int
+deadq_remove(pid_t pid)
+{
+ dq_t q;
+
+ TAILQ_FOREACH(q, &deadq_head, dq_entries) {
+ if (q->dq_pid == pid) {
+ TAILQ_REMOVE(&deadq_head, q, dq_entries);
+ free(q);
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+static void
+log_deadchild(pid_t pid, int status, const char *name)
+{
+ int code;
+ char buf[256];
+ const char *reason;
+
+ errno = 0; /* Keep strerror() stuff out of logerror messages. */
+ if (WIFSIGNALED(status)) {
+ reason = "due to signal";
+ code = WTERMSIG(status);
+ } else {
+ reason = "with status";
+ code = WEXITSTATUS(status);
+ if (code == 0)
+ return;
+ }
+ (void)snprintf(buf, sizeof buf,
+ "Logging subprocess %d (%s) exited %s %d.",
+ pid, name, reason, code);
+ logerror(buf);
+}
+
+static int *
+socksetup(int af, const char *bindhostname)
+{
+ struct addrinfo hints, *res, *r;
+ int error, maxs, *s, *socks;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_PASSIVE;
+ hints.ai_family = af;
+ hints.ai_socktype = SOCK_DGRAM;
+ error = getaddrinfo(bindhostname, "syslog", &hints, &res);
+ if (error) {
+ logerror(gai_strerror(error));
+ errno = 0;
+ die(0);
+ }
+
+ /* Count max number of sockets we may open */
+ for (maxs = 0, r = res; r; r = r->ai_next, maxs++);
+ socks = malloc((maxs+1) * sizeof(int));
+ if (socks == NULL) {
+ logerror("couldn't allocate memory for sockets");
+ die(0);
+ }
+
+ *socks = 0; /* num of sockets counter at start of array */
+ s = socks + 1;
+ for (r = res; r; r = r->ai_next) {
+ int on = 1;
+ *s = socket(r->ai_family, r->ai_socktype, r->ai_protocol);
+ if (*s < 0) {
+ logerror("socket");
+ continue;
+ }
+ if (r->ai_family == AF_INET6) {
+ if (setsockopt(*s, IPPROTO_IPV6, IPV6_V6ONLY,
+ (char *)&on, sizeof (on)) < 0) {
+ logerror("setsockopt");
+ close(*s);
+ continue;
+ }
+ }
+ if (setsockopt(*s, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&on, sizeof (on)) < 0) {
+ logerror("setsockopt");
+ close(*s);
+ continue;
+ }
+ if (bind(*s, r->ai_addr, r->ai_addrlen) < 0) {
+ close(*s);
+ logerror("bind");
+ continue;
+ }
+
+ double_rbuf(*s);
+
+ (*socks)++;
+ s++;
+ }
+
+ if (*socks == 0) {
+ free(socks);
+ if (Debug)
+ return (NULL);
+ else
+ die(0);
+ }
+ if (res)
+ freeaddrinfo(res);
+
+ return (socks);
+}
+
+static void
+double_rbuf(int fd)
+{
+ socklen_t slen, len;
+
+ if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, &slen) == 0) {
+ len *= 2;
+ setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, slen);
+ }
+}
diff --git a/usr.sbin/tcpdchk/Makefile b/usr.sbin/tcpdchk/Makefile
new file mode 100644
index 0000000..2936984
--- /dev/null
+++ b/usr.sbin/tcpdchk/Makefile
@@ -0,0 +1,21 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+.PATH: ${.CURDIR}/../../contrib/tcp_wrappers
+
+PROG= tcpdchk
+MAN= tcpdchk.8
+SRCS= tcpdchk.c fakelog.c inetcf.c scaffold.c
+
+CFLAGS+=-DREAL_DAEMON_DIR=\"/usr/libexec\" \
+ -DSEVERITY=LOG_INFO -DRFC931_TIMEOUT=10 -DPROCESS_OPTIONS \
+ -DHOSTS_DENY=\"/etc/hosts.deny\" -DHOSTS_ALLOW=\"/etc/hosts.allow\"
+.if ${MK_INET6_SUPPORT} != "no"
+CFLAGS+=-DINET6
+.endif
+
+DPADD= ${LIBWRAP}
+LDADD= -lwrap
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/tcpdmatch/Makefile b/usr.sbin/tcpdmatch/Makefile
new file mode 100644
index 0000000..cbd61aa
--- /dev/null
+++ b/usr.sbin/tcpdmatch/Makefile
@@ -0,0 +1,20 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+.PATH: ${.CURDIR}/../../contrib/tcp_wrappers
+
+PROG= tcpdmatch
+MAN= tcpdmatch.8
+SRCS= tcpdmatch.c fakelog.c inetcf.c scaffold.c
+
+CFLAGS+=-DREAL_DAEMON_DIR=\"/usr/libexec\" \
+ -DSEVERITY=LOG_INFO -DRFC931_TIMEOUT=10
+.if ${MK_INET6_SUPPORT} != "no"
+CFLAGS+=-DINET6
+.endif
+
+DPADD= ${LIBWRAP}
+LDADD= -lwrap
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/tcpdrop/Makefile b/usr.sbin/tcpdrop/Makefile
new file mode 100644
index 0000000..44766eb
--- /dev/null
+++ b/usr.sbin/tcpdrop/Makefile
@@ -0,0 +1,8 @@
+# $OpenBSD: Makefile,v 1.1 2004/04/26 19:51:20 markus Exp $
+# $FreeBSD$
+
+PROG= tcpdrop
+MAN= tcpdrop.8
+WARNS?= 6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/tcpdrop/tcpdrop.8 b/usr.sbin/tcpdrop/tcpdrop.8
new file mode 100644
index 0000000..5fd3a43
--- /dev/null
+++ b/usr.sbin/tcpdrop/tcpdrop.8
@@ -0,0 +1,64 @@
+.\" $OpenBSD: tcpdrop.8,v 1.5 2004/05/24 13:57:31 jmc Exp $
+.\"
+.\" Copyright (c) 2004 Markus Friedl <markus@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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 21, 2004
+.Dt TCPDROP 8
+.Os
+.Sh NAME
+.Nm tcpdrop
+.Nd drop a TCP connection
+.Sh SYNOPSIS
+.Nm tcpdrop
+.Ar laddr
+.Ar lport
+.Ar faddr
+.Ar fport
+.Sh DESCRIPTION
+The
+.Nm
+command drops the TCP connection specified by the local address
+.Ar laddr ,
+port
+.Ar lport
+and the foreign address
+.Ar faddr ,
+port
+.Ar fport .
+Addresses and ports can be specified by name or numeric value.
+.Sh EXIT STATUS
+.Ex -std
+.Sh EXAMPLES
+If a connection to
+.Xr httpd 8 Pq Pa ports/www/apache2
+is causing congestion on a network link, one can drop the TCP session
+in charge:
+.Bd -literal -offset indent
+# sockstat -c | grep httpd
+www httpd 16525 3 tcp4 \e
+ 192.168.5.41:80 192.168.5.1:26747
+.Ed
+.Pp
+The following command will drop the connection:
+.Bd -literal -offset indent
+# tcpdrop 192.168.5.41 80 192.168.5.1 26747
+.Ed
+.Sh SEE ALSO
+.Xr netstat 1 ,
+.Xr sockstat 1
+.Sh AUTHORS
+.An Markus Friedl Aq markus@openbsd.org
diff --git a/usr.sbin/tcpdrop/tcpdrop.c b/usr.sbin/tcpdrop/tcpdrop.c
new file mode 100644
index 0000000..06ca0da
--- /dev/null
+++ b/usr.sbin/tcpdrop/tcpdrop.c
@@ -0,0 +1,91 @@
+/* $OpenBSD: tcpdrop.c,v 1.4 2004/05/22 23:55:22 deraadt Exp $ */
+
+/*-
+ * Copyright (c) 2004 Markus Friedl <markus@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.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <netinet/in.h>
+#include <netinet/tcp_var.h>
+
+#include <err.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * Drop a tcp connection.
+ */
+int
+main(int argc, char *argv[])
+{
+ struct addrinfo hints, *ail, *aif, *laddr, *faddr;
+ /* addrs[0] is a foreign socket, addrs[1] is a local one. */
+ struct sockaddr_storage addrs[2];
+ int mib[] = { CTL_NET, PF_INET, IPPROTO_TCP, TCPCTL_DROP };
+ int gaierr, rval = 0;
+ char fhbuf[NI_MAXHOST], fsbuf[NI_MAXSERV], lhbuf[NI_MAXHOST],
+ lsbuf[NI_MAXSERV];
+
+ if (argc != 5) {
+ fprintf(stderr, "usage: tcpdrop laddr lport faddr fport\n");
+ exit(1);
+ }
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ if ((gaierr = getaddrinfo(argv[1], argv[2], &hints, &laddr)) != 0)
+ errx(1, "%s port %s: %s", argv[1], argv[2],
+ gai_strerror(gaierr));
+ if ((gaierr = getaddrinfo(argv[3], argv[4], &hints, &faddr)) != 0) {
+ freeaddrinfo(laddr);
+ errx(1, "%s port %s: %s", argv[3], argv[4],
+ gai_strerror(gaierr));
+ }
+ for (ail = laddr; ail; ail = ail->ai_next) {
+ for (aif = faddr; aif; aif = aif->ai_next) {
+ if (ail->ai_family != aif->ai_family)
+ continue;
+ memcpy(&addrs[0], aif->ai_addr, aif->ai_addrlen);
+ memcpy(&addrs[1], ail->ai_addr, ail->ai_addrlen);
+ if (getnameinfo(aif->ai_addr, aif->ai_addrlen,
+ fhbuf, sizeof(fhbuf),
+ fsbuf, sizeof(fsbuf),
+ NI_NUMERICHOST | NI_NUMERICSERV) == -1)
+ err(1, "getnameinfo");
+ if (getnameinfo(ail->ai_addr, ail->ai_addrlen,
+ lhbuf, sizeof(lhbuf),
+ lsbuf, sizeof(lsbuf),
+ NI_NUMERICHOST | NI_NUMERICSERV) == -1)
+ err(1, "getnameinfo");
+ if (sysctl(mib, sizeof (mib) / sizeof (int), NULL,
+ NULL, &addrs, sizeof(addrs)) == -1) {
+ rval = 1;
+ warn("%s %s %s %s", lhbuf, lsbuf, fhbuf, fsbuf);
+ } else
+ printf("%s %s %s %s: dropped\n",
+ lhbuf, lsbuf, fhbuf, fsbuf);
+ }
+ }
+ freeaddrinfo(laddr);
+ freeaddrinfo(faddr);
+ exit(rval);
+}
diff --git a/usr.sbin/tcpdump/Makefile b/usr.sbin/tcpdump/Makefile
new file mode 100644
index 0000000..d183feb
--- /dev/null
+++ b/usr.sbin/tcpdump/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 0.1 (RGrimes) 4/4/93
+# $FreeBSD$
+
+SUBDIR= tcpdump
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/tcpdump/Makefile.inc b/usr.sbin/tcpdump/Makefile.inc
new file mode 100644
index 0000000..1e4d46f
--- /dev/null
+++ b/usr.sbin/tcpdump/Makefile.inc
@@ -0,0 +1,4 @@
+# @(#)Makefile.inc 5.1 (Berkeley) 5/11/90
+# $FreeBSD$
+
+BINDIR?= /usr/sbin
diff --git a/usr.sbin/tcpdump/tcpdump/Makefile b/usr.sbin/tcpdump/tcpdump/Makefile
new file mode 100644
index 0000000..9ea718a
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpdump/Makefile
@@ -0,0 +1,69 @@
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+TCPDUMP_DISTDIR?= ${.CURDIR}/../../../contrib/tcpdump
+.PATH: ${TCPDUMP_DISTDIR}
+
+PROG= tcpdump
+
+SRCS = addrtoname.c af.c cpack.c gmpls.c oui.c gmt2local.c ipproto.c \
+ nlpid.c l2vpn.c machdep.c parsenfsfh.c \
+ print-802_11.c print-ap1394.c print-ah.c print-arcnet.c \
+ print-aodv.c print-arp.c print-ascii.c print-atalk.c print-atm.c \
+ print-beep.c print-bfd.c print-bgp.c print-bootp.c print-cdp.c \
+ print-chdlc.c print-cip.c print-cnfp.c print-dccp.c print-decnet.c \
+ print-domain.c print-dvmrp.c print-enc.c print-egp.c \
+ print-eap.c print-eigrp.c\
+ print-esp.c print-ether.c print-fddi.c print-fr.c \
+ print-gre.c print-hsrp.c print-icmp.c print-igmp.c \
+ print-igrp.c print-ip.c print-ipcomp.c print-ipfc.c \
+ print-ipx.c print-isakmp.c print-isoclns.c print-juniper.c print-krb.c \
+ print-l2tp.c print-lane.c print-ldp.c print-llc.c \
+ print-lmp.c print-lspping.c \
+ print-lwres.c print-mobile.c print-mpls.c print-msdp.c \
+ print-nfs.c print-ntp.c print-null.c print-olsr.c print-ospf.c \
+ print-pgm.c print-pim.c print-ppp.c print-pppoe.c \
+ print-pptp.c print-radius.c print-raw.c print-rip.c \
+ print-rsvp.c print-rx.c print-sctp.c print-sip.c print-sl.c print-sll.c \
+ print-slow.c print-snmp.c print-stp.c print-sunatm.c print-sunrpc.c \
+ print-symantec.c print-syslog.c print-tcp.c print-telnet.c print-tftp.c \
+ print-timed.c print-token.c print-udp.c print-vjc.c print-vrrp.c \
+ print-wb.c print-zephyr.c setsignal.c tcpdump.c util.c \
+ print-smb.c smbutil.c \
+ version.c
+CLEANFILES+= version.c
+
+CFLAGS+= -I${.CURDIR} -I${TCPDUMP_DISTDIR}
+CFLAGS+= -DHAVE_CONFIG_H
+CFLAGS+= -D_U_="__attribute__((unused))"
+
+.if ${MK_INET6_SUPPORT} != "no"
+SRCS+= print-ip6.c print-ip6opts.c print-mobility.c print-ripng.c \
+ print-icmp6.c print-frag6.c print-rt6.c print-ospf6.c print-dhcp6.c
+CFLAGS+= -DINET6
+.endif
+.if ${MACHINE_ARCH} != "i386"
+CFLAGS+= -DLBL_ALIGN
+.endif
+
+DPADD= ${LIBL} ${LIBPCAP}
+LDADD= -ll -lpcap
+.if ${MK_OPENSSL} != "no" && !defined(RELEASE_CRUNCH)
+DPADD+= ${LIBCRYPTO}
+LDADD+= -lcrypto
+CFLAGS+= -I${DESTDIR}/usr/include/openssl
+CFLAGS+= -DHAVE_LIBCRYPTO -DHAVE_RC5_H -DHAVE_CAST_H -DHAVE_OPENSSL_EVP_H
+.endif
+
+.if ${MK_PF} != "no"
+SRCS+= print-pflog.c
+CFLAGS+= -DHAVE_NET_PFVAR_H
+.endif
+
+version.c: ${TCPDUMP_DISTDIR}/VERSION
+ rm -f version.c ; \
+ sed 's/.*/char version[] = "&";/' ${TCPDUMP_DISTDIR}/VERSION \
+ > version.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/tcpdump/tcpdump/config.h b/usr.sbin/tcpdump/tcpdump/config.h
new file mode 100644
index 0000000..b7b85a5
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpdump/config.h
@@ -0,0 +1,359 @@
+/* $FreeBSD$ */
+/* This is an edited copy of the config.h generated by configure. */
+
+/* config.h. Generated by configure. */
+/* config.h.in. Generated from configure.in by autoheader. */
+/* "generated automatically" means DO NOT MAKE CHANGES TO config.h.in --
+ * make them to acconfig.h and rerun autoheader */
+
+/* Define if you have SSLeay 0.9.0b with the buggy cast128. */
+/* #undef HAVE_BUGGY_CAST128 */
+
+/* Define if you enable IPv6 support */
+/* See Makefile */
+/* #undef INET6 */
+
+/* Define if you enable support for the libsmi. */
+/* #undef LIBSMI */
+
+/* Define if you have the <smi.h> header file. */
+/* #undef HAVE_SMI_H */
+
+/* define if you have struct __res_state_ext */
+/* #undef HAVE_RES_STATE_EXT */
+
+/* define if your struct __res_state has the nsort member */
+/* #undef HAVE_NEW_RES_STATE */
+
+/*
+ * define if struct ether_header.ether_dhost is a struct with ether_addr_octet
+ */
+/* #undef ETHER_HEADER_HAS_EA */
+
+/* define if struct ether_arp contains arp_xsha */
+/* #undef ETHER_ARP_HAS_X */
+
+/* define if you have the addrinfo function. */
+#define HAVE_ADDRINFO 1
+
+/* define if you need to include missing/addrinfoh.h. */
+/* #undef NEED_ADDRINFO_H */
+
+/* define ifyou have the h_errno variable. */
+#define HAVE_H_ERRNO 1
+
+/* define if IN6ADDRSZ is defined (XXX not used!) */
+#define HAVE_IN6ADDRSZ 1
+
+/* define if INADDRSZ is defined (XXX not used!) */
+#define HAVE_INADDRSZ 1
+
+/* define if this is a development version, to use additional prototypes. */
+/* #undef HAVE_OS_PROTO_H */
+
+/* define if <unistd.h> defines __P() */
+/* #undef HAVE_PORTABLE_PROTOTYPE */
+
+/* define if RES_USE_INET6 is defined */
+#define HAVE_RES_USE_INET6 1
+
+/* define if struct sockaddr has the sa_len member */
+#define HAVE_SOCKADDR_SA_LEN 1
+
+/* define if you have struct sockaddr_storage */
+#define HAVE_SOCKADDR_STORAGE 1
+
+/* define if you have both getipnodebyname() and getipnodebyaddr() */
+/* #undef USE_GETIPNODEBY */
+
+/* define if you have ether_ntohost() and it works */
+#define USE_ETHER_NTOHOST 1
+
+/* define if libpcap has pcap_version */
+/* #undef HAVE_PCAP_VERSION */
+
+/* define if libpcap has pcap_debug */
+/* #undef HAVE_PCAP_DEBUG */
+
+/* define if libpcap has yydebug */
+/* #undef HAVE_YYDEBUG */
+
+/* define if libpcap has pcap_list_datalinks() */
+#define HAVE_PCAP_LIST_DATALINKS 1
+
+/* define if libpcap has pcap_set_datalink() */
+#define HAVE_PCAP_SET_DATALINK 1
+
+/* define if libpcap has pcap_datalink_name_to_val() */
+#define HAVE_PCAP_DATALINK_NAME_TO_VAL 1
+
+/* define if libpcap has pcap_datalink_val_to_description() */
+#define HAVE_PCAP_DATALINK_VAL_TO_DESCRIPTION 1
+
+/* define if libpcap has pcap_dump_ftell() */
+#define HAVE_PCAP_DUMP_FTELL 1
+
+/* define if you have getrpcbynumber() */
+#define HAVE_GETRPCBYNUMBER 1
+
+/* define if unaligned memory accesses fail */
+/* #undef LBL_ALIGN */
+
+/* The successful return value from signal (?)XXX */
+#define RETSIGVAL
+
+/* Define this on IRIX */
+/* #undef _BSD_SIGNALS */
+
+/* For HP/UX ANSI compiler? */
+/* #undef _HPUX_SOURCE */
+
+/* AIX hack. */
+/* #undef _SUN */
+
+/* Workaround for missing 64-bit formats */
+/* #undef PRId64 */
+/* #undef PRIo64 */
+/* #undef PRIx64 */
+/* #undef PRIu64 */
+
+/* Whether or not to include the possibly-buggy SMB printer */
+#define TCPDUMP_DO_SMB 1
+
+/* Long story short: aclocal.m4 depends on autoconf 2.13
+ * implementation details wrt "const"; newer versions
+ * have different implementation details so for now we
+ * put "const" here. This may cause duplicate definitions
+ * in config.h but that should be OK since they're the same.
+ */
+/* #undef const */
+
+/* Define if you have the dnet_htoa function. */
+/* #undef HAVE_DNET_HTOA */
+
+/* Define if you have a dnet_htoa declaration in <netdnet/dnetdb.h>. */
+/* #undef HAVE_NETDNET_DNETDB_H_DNET_HTOA */
+
+/* define if should drop privileges by default */
+/* #undef WITH_USER */
+
+/* define if should chroot when dropping privileges */
+/* #undef WITH_CHROOT */
+
+/* Define to 1 if you have the `alarm' function. */
+#define HAVE_ALARM 1
+
+/* Define to 1 if you have the `bpf_dump' function. */
+#define HAVE_BPF_DUMP 1
+
+/* Define to 1 if you have the declaration of `ether_ntohost', and to 0 if you
+ don't. */
+#define HAVE_DECL_ETHER_NTOHOST 1
+
+/* Define to 1 if you have the `ether_ntohost' function. */
+#define HAVE_ETHER_NTOHOST 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the `getaddrinfo' function. */
+#define HAVE_GETADDRINFO 1
+
+/* Define to 1 if you have the `getnameinfo' function. */
+#define HAVE_GETNAMEINFO 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the `crypto' library (-lcrypto). */
+/* See Makefile */
+/* #undef HAVE_LIBCRYPTO */
+
+/* Define to 1 if you have the `rpc' library (-lrpc). */
+/* #undef HAVE_LIBRPC */
+
+/* Define to 1 if you have the `smi' library (-lsmi). */
+/* #undef HAVE_LIBSMI */
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the <netdnet/dnetdb.h> header file. */
+/* #undef HAVE_NETDNET_DNETDB_H */
+
+/* Define to 1 if you have the <netinet/ether.h> header file. */
+/* #undef HAVE_NETINET_ETHER_H */
+
+/* Define to 1 if you have the <netinet/if_ether.h> header file. */
+#define HAVE_NETINET_IF_ETHER_H 1
+
+/* Define to 1 if you have the <net/pfvar.h> header file. */
+/* See Makefile */
+/* #undef HAVE_NET_PFVAR_H */
+
+/* Define to 1 if you have the <openssl/evp.h> header file. */
+/* See Makefile */
+/* #undef HAVE_OPENSSL_EVP_H 1 */
+
+/* Define to 1 if you have the `pcap_breakloop' function. */
+#define HAVE_PCAP_BREAKLOOP 1
+
+/* Define to 1 if you have the `pcap_dump_flush' function. */
+#define HAVE_PCAP_DUMP_FLUSH 1
+
+/* Define to 1 if you have the `pcap_findalldevs' function. */
+#define HAVE_PCAP_FINDALLDEVS 1
+
+/* Define to 1 if the system has the type `pcap_if_t'. */
+#define HAVE_PCAP_IF_T 1
+
+/* Define to 1 if you have the `pcap_lib_version' function. */
+#define HAVE_PCAP_LIB_VERSION 1
+
+/* Define to 1 if you have the `pfopen' function. */
+/* #undef HAVE_PFOPEN */
+
+/* Define to 1 if you have the <rpc/rpcent.h> header file. */
+#define HAVE_RPC_RPCENT_H 1
+
+/* Define to 1 if you have the `setlinebuf' function. */
+#define HAVE_SETLINEBUF 1
+
+/* Define to 1 if you have the `sigaction' function. */
+#define HAVE_SIGACTION 1
+
+/* Define to 1 if you have the `sigset' function. */
+/* #undef HAVE_SIGSET */
+
+/* Define to 1 if you have the <smi.h> header file. */
+/* #undef HAVE_SMI_H */
+
+/* Define to 1 if you have the `snprintf' function. */
+#define HAVE_SNPRINTF 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the `strcasecmp' function. */
+#define HAVE_STRCASECMP 1
+
+/* Define to 1 if you have the `strdup' function. */
+#define HAVE_STRDUP 1
+
+/* Define to 1 if you have the `strftime' function. */
+#define HAVE_STRFTIME 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the `strlcat' function. */
+#define HAVE_STRLCAT 1
+
+/* Define to 1 if you have the `strlcpy' function. */
+#define HAVE_STRLCPY 1
+
+/* Define to 1 if you have the `strsep' function. */
+#define HAVE_STRSEP 1
+
+/* Define to 1 if the system has the type `struct ether_addr'. */
+/* #undef HAVE_STRUCT_ETHER_ADDR */
+
+/* Define to 1 if you have the <sys/bitypes.h> header file. */
+/* #undef HAVE_SYS_BITYPES_H */
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if you have the `vfprintf' function. */
+#define HAVE_VFPRINTF 1
+
+/* Define to 1 if you have the `vsnprintf' function. */
+#define HAVE_VSNPRINTF 1
+
+/* define if your compiler has __attribute__ */
+#define HAVE___ATTRIBUTE__ 1
+
+/* Define to 1 if netinet/ether.h declares `ether_ntohost' */
+/* #undef NETINET_ETHER_H_DECLARES_ETHER_NTOHOST */
+
+/* Define to 1 if netinet/if_ether.h declares `ether_ntohost' */
+#define NETINET_IF_ETHER_H_DECLARES_ETHER_NTOHOST
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME ""
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING ""
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION ""
+
+/* Define as the return type of signal handlers (`int' or `void'). */
+#define RETSIGTYPE void
+
+/* The size of `char', as computed by sizeof. */
+#define SIZEOF_CHAR 1
+
+/* The size of `int', as computed by sizeof. */
+#define SIZEOF_INT 4
+
+/* The size of `long', as computed by sizeof. */
+/* XXX: This is wrong, but possibly unused */
+#define SIZEOF_LONG 4
+
+/* The size of `long long', as computed by sizeof. */
+#define SIZEOF_LONG_LONG 8
+
+/* The size of `short', as computed by sizeof. */
+#define SIZEOF_SHORT 2
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
+#define TIME_WITH_SYS_TIME 1
+
+/* Define as token for inline if inlining supported */
+#define inline inline
+
+/* Define to `short' if int16_t not defined. */
+/* #undef int16_t */
+
+/* Define to `int' if int32_t not defined. */
+/* #undef int32_t */
+
+/* Define to `long long' if int64_t not defined. */
+/* #undef int64_t */
+
+/* Define to `signed char' if int8_t not defined. */
+/* #undef int8_t */
+
+/* Define to `unsigned short' if u_int16_t not defined. */
+/* #undef u_int16_t */
+
+/* Define to `unsigned int' if u_int32_t not defined. */
+/* #undef u_int32_t */
+
+/* Define to `unsigned long long' if u_int64_t not defined. */
+/* #undef u_int64_t */
+
+/* Define to `unsigned char' if u_int8_t not defined. */
+/* #undef u_int8_t */
diff --git a/usr.sbin/timed/Makefile b/usr.sbin/timed/Makefile
new file mode 100644
index 0000000..2f56510
--- /dev/null
+++ b/usr.sbin/timed/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+SUBDIR= timed timedc
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/timed/SMM.doc/timed/Makefile b/usr.sbin/timed/SMM.doc/timed/Makefile
new file mode 100644
index 0000000..9afa6c6
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timed/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.1 (Berkeley) 6/8/93
+# $FreeBSD$
+
+DIR= smm/12.timed
+SRCS= timed.ms
+MACROS= -ms
+PRINTER=Pdp
+
+paper.${PRINTER}: ${SRCS}
+ ${SOELIM} ${SRCS} | ${TBL} | ${ROFF} > ${.TARGET}
+
+.include <bsd.doc.mk>
diff --git a/usr.sbin/timed/SMM.doc/timed/date b/usr.sbin/timed/SMM.doc/timed/date
new file mode 100644
index 0000000..e4e4d58
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timed/date
@@ -0,0 +1,53 @@
+.\" 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.
+.\" 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.
+.\"
+.\" @(#)date 8.1 (Berkeley) 6/8/93
+.\"
+.ft B
+.TS
+center;
+ce | ce | ce | ce
+| c | c | c | s |
+| c s s s |.
+Byte 1 Byte 2 Byte 3 Byte 4
+=
+Type Version No. Sequence No.
+_
+Seconds of Time to Set
+_
+Microseconds of Time to Set
+_
+Machine Name
+_
+\&. . .
+_
+.TE
+.ft R
diff --git a/usr.sbin/timed/SMM.doc/timed/loop b/usr.sbin/timed/SMM.doc/timed/loop
new file mode 100644
index 0000000..11ccb4d
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timed/loop
@@ -0,0 +1,54 @@
+.\" 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.
+.\" 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.
+.\"
+.\" @(#)loop 8.1 (Berkeley) 6/8/93
+.\"
+.ft B
+.TS
+center;
+ce | ce | ce | ce
+| c | c | c | s |
+| c | c s s |
+| c s s s |.
+Byte 1 Byte 2 Byte 3 Byte 4
+=
+Type Version No. Sequence No.
+_
+Hop Count ( unused )
+_
+( unused )
+_
+Machine Name
+_
+\&. . .
+_
+.TE
+.ft R
diff --git a/usr.sbin/timed/SMM.doc/timed/spell.ok b/usr.sbin/timed/SMM.doc/timed/spell.ok
new file mode 100644
index 0000000..8ecfe15
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timed/spell.ok
@@ -0,0 +1,34 @@
+ACK
+ADJTIME
+Adjtime
+CS
+CSELT
+Candidature
+DATEACK
+DoD
+Gusella
+MASTERACK
+MASTERREQ
+MASTERUP
+MSITE
+MSITEREQ
+Protocol''SMM:22
+Riccardo
+SETDATE
+SETDATEREQ
+SETTIME
+SLAVEUP
+SMM:22
+Stefano
+TRACEOFF
+TRACEON
+TSP
+Timedc
+UDP
+USENIX
+Zatti
+candidature
+ce
+daemon
+daemons
+timedc
diff --git a/usr.sbin/timed/SMM.doc/timed/time b/usr.sbin/timed/SMM.doc/timed/time
new file mode 100644
index 0000000..619d171
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timed/time
@@ -0,0 +1,53 @@
+.\" 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.
+.\" 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.
+.\"
+.\" @(#)time 8.1 (Berkeley) 6/8/93
+.\"
+.ft B
+.TS
+center;
+ce | ce | ce | ce
+| c | c | c | s |
+| c s s s |.
+Byte 1 Byte 2 Byte 3 Byte 4
+=
+Type Version No. Sequence No.
+_
+Seconds of Adjustment
+_
+Microseconds of Adjustment
+_
+Machine Name
+_
+\&. . .
+_
+.TE
+.ft R
diff --git a/usr.sbin/timed/SMM.doc/timed/timed.ms b/usr.sbin/timed/SMM.doc/timed/timed.ms
new file mode 100644
index 0000000..412399a
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timed/timed.ms
@@ -0,0 +1,462 @@
+.\" $FreeBSD$
+.\"
+.\" 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.
+.\" 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.
+.\"
+.\" @(#)timed.ms 8.1 (Berkeley) 6/8/93
+.\"
+.TL
+The Berkeley
+.UX
+.br
+Time Synchronization Protocol
+.AU
+Riccardo Gusella, Stefano Zatti, and James M. Bloom
+.AI
+Computer Systems Research Group
+Computer Science Division
+Department of Electrical Engineering and Computer Science
+University of California, Berkeley
+Berkeley, CA 94720
+.FS
+This work was sponsored by the Defense Advanced Research Projects Agency
+(DoD), monitored by the Naval Electronics Systems
+Command under contract No. N00039-84-C-0089, and by the Italian CSELT
+Corporation.
+The views and conclusions contained in this document are those of the
+authors and should not be interpreted as representing official policies,
+either expressed or implied, of the Defense Research Projects Agency,
+of the US Government, or of CSELT.
+.FE
+.LP
+.OH 'The Berkeley UNIX Time Synchronization Protocol''SMM:12-%'
+.EH 'SMM:12-%''The Berkeley UNIX Time Synchronization Protocol'
+.SH
+Introduction
+.PP
+The Time Synchronization Protocol (TSP)
+has been designed for specific use by the program \fItimed\fP,
+a local area network clock synchronizer for
+the UNIX 4.3BSD operating
+system.
+Timed is built on the DARPA UDP protocol [4] and
+is based on a master slave scheme.
+.PP
+TSP serves a dual purpose.
+First, it supports messages for the synchronization of the clocks
+of the various hosts in a local area network.
+Second, it supports messages for the election that occurs
+among slave time daemons when, for any reason, the master disappears.
+The synchronization mechanism and the election procedure
+employed by the program timed are described
+in other documents [1,2,3].
+.PP
+Briefly, the synchronization software, which works in a
+local area network, consists of a collection of \fItime daemons\fP
+(one per machine) and is based on a master-slave
+structure.
+The present implementation keeps processor clocks synchronized
+within 20 milliseconds.
+A \fImaster time daemon\fP measures the time
+difference between the clock of the machine on which it
+is running and those of all other machines. The current implementation
+uses ICMP \fITime Stamp Requests\fP [5] to measure the clock difference
+between machines.
+The master computes the \fInetwork time\fP as the average of the
+times provided by nonfaulty clocks.\**
+.FS
+A clock is considered to be faulty when its value
+is more than a small specified
+interval apart from the majority of the clocks
+of the machines on the same network.
+See [1,2] for more details.
+.FE
+It then sends to each \fIslave time daemon\fP the
+correction that should be performed on the clock of its machine.
+This process is repeated periodically.
+Since the correction is expressed as a time difference rather than an
+absolute time, transmission delays do not interfere with synchronization.
+When a machine comes up and joins the network,
+it starts a slave time daemon, which
+will ask the master for the correct time and will reset the machine's clock
+before any user activity can begin.
+The time daemons therefore maintain a single network time in spite of
+the drift of clocks away from each other.
+.PP
+Additionally, a time daemon on gateway machines may run as
+a \fIsubmaster\fP.
+A submaster time daemon functions as a slave on one network that
+already has a master and as master on other networks.
+In addition, a submaster is responsible for propagating broadcast
+packets from one network to the other.
+.PP
+To ensure that service provided is continuous and reliable,
+it is necessary to implement an election algorithm that will elect a
+new master should the machine running the current master crash, the master
+terminate (for example, because of a run-time error), or the network be
+partitioned.
+Under our algorithm, slaves are able to realize when the master has
+stopped functioning and to elect a new master from among themselves.
+It is important to note that since the failure of the master results
+only in a gradual divergence of clock values, the election
+need not occur immediately.
+.PP
+All the communication occurring among time daemons uses the TSP
+protocol.
+While some messages need not be sent in a reliable way,
+most communication in TSP requires reliability not provided by the underlying
+protocol.
+Reliability is achieved by the use of acknowledgements, sequence numbers, and
+retransmission when message losses occur.
+When a message that requires acknowledgment is not acknowledged after
+multiple attempts,
+the time daemon that has sent the message will assume that the
+addressee is down.
+This document will not describe the details of how reliability is
+implemented, but will only point out when
+a message type requires a reliable transport mechanism.
+.PP
+The message format in TSP is the same for all message types;
+however, in some instances, one or more fields are not used.
+The next section describes the message format.
+The following sections describe
+in detail the different message types, their use and the contents
+of each field. NOTE: The message format is likely to change in
+future versions of timed.
+.sp 2
+.SH
+Message Format
+.PP
+All fields are based upon 8-bit bytes. Fields should be sent in
+network byte order if they are more than one byte long.
+The structure of a TSP message is the following:
+.IP 1)
+A one byte message type.
+.IP 2)
+A one byte version number, specifying the protocol version which the
+message uses.
+.IP 3)
+A two byte sequence number to be used for recognizing duplicate messages
+that occur when messages are retransmitted.
+.IP 4)
+Eight bytes of packet specific data. This field contains two 4 byte time
+values, a one byte hop count, or may be unused depending on the type
+of the packet.
+.IP 5)
+A zero-terminated string of up to 256 \s-2ASCII\s+2 characters with the name of
+the machine sending the message.
+.PP
+The following charts describe the message types,
+show their fields, and explain their usages.
+For the purpose of the following discussion, a time daemon can
+be considered to be in
+one of three states: slave, master, or candidate for election to master.
+Also, the term \fIbroadcast\fP refers to
+the sending of a message to all active time daemons.
+.sp 1
+.SH
+Adjtime Message
+.so time
+.LP
+Type: TSP_ADJTIME (1)
+.sp 1
+.PP
+The master sends this message to a slave to communicate
+the difference between
+the clock of the slave and
+the network time the master has just computed.
+The slave will accordingly
+adjust the time of its machine.
+This message requires an acknowledgment.
+.sp 1
+.SH
+Acknowledgment Message
+.so unused
+.LP
+Type: TSP_ACK (2)
+.sp 1
+.PP
+Both the master and the slaves use this message for
+acknowledgment only.
+It is used in several different contexts, for example
+in reply to an Adjtime message.
+.sp 1
+.SH
+Master Request Message
+.so unused
+.LP
+Type: TSP_MASTERREQ (3)
+.sp 1
+.PP
+A newly-started time daemon broadcasts this message to
+locate a master. No other action is implied by this packet.
+It requires a Master Acknowledgment.
+.sp 1
+.SH
+Master Acknowledgement
+.so unused
+.LP
+Type: TSP_MASTERACK (4)
+.sp 1
+.PP
+The master sends this message to acknowledge the Master Request message
+and the Conflict Resolution Message.
+.sp 1
+.SH
+Set Network Time Message
+.so date
+.LP
+Type: TSP_SETTIME (5)
+.sp 1
+.PP
+The master sends this message to slave time daemons to set their time.
+This packet is sent to newly started time daemons and when the network
+date is changed.
+It contains the master's time as an approximation of the network time.
+It requires an acknowledgment.
+The next
+synchronization round will eliminate the small time difference
+caused by the random delay in the communication channel.
+.sp 1
+.SH
+Master Active Message
+.so unused
+.LP
+Type: TSP_MASTERUP (6)
+.sp 1
+.PP
+The master broadcasts this message to
+solicit the names of the active slaves.
+Slaves will reply with a Slave Active message.
+.sp 1
+.SH
+Slave Active Message
+.so unused
+.LP
+Type: TSP_SLAVEUP (7)
+.sp 1
+.PP
+A slave sends this message to the master in answer to a Master Active message.
+This message is also sent when a new slave starts up to inform the master that
+it wants to be synchronized.
+.sp 1
+.SH
+Master Candidature Message
+.so unused
+.LP
+Type: TSP_ELECTION (8)
+.sp 1
+.PP
+A slave eligible to become a master broadcasts this message when its election
+timer expires.
+The message declares that the slave wishes to become the new master.
+.sp 1
+.SH
+Candidature Acceptance Message
+.so unused
+.LP
+Type: TSP_ACCEPT (9)
+.sp 1
+.PP
+A slave sends this message to accept the candidature of the time daemon
+that has broadcast an Election message.
+The candidate will add the slave's name to the list of machines that it
+will control should it become the master.
+.sp 1
+.SH
+Candidature Rejection Message
+.so unused
+.LP
+Type: TSP_REFUSE (10)
+.sp 1
+.PP
+After a slave accepts the candidature of a time daemon, it will reply
+to any election messages from other slaves
+with this message.
+This rejects any candidature other than the first received.
+.sp 1
+.SH
+Multiple Master Notification Message
+.so unused
+.LP
+Type: TSP_CONFLICT (11)
+.sp 1
+.PP
+When two or more masters reply to a Master Request message, the slave
+uses this message to inform one of them that more than one master exists.
+.sp 1
+.SH
+Conflict Resolution Message
+.so unused
+.LP
+Type: TSP_RESOLVE (12)
+.sp 1
+.PP
+A master which has been informed of the existence of other masters
+broadcasts this message to determine who the other masters are.
+.sp 1
+.SH
+Quit Message
+.so unused
+.LP
+Type: TSP_QUIT (13)
+.sp 1
+.PP
+This message is sent by the master in three different contexts:
+1) to a candidate that broadcasts a Master Candidature message,
+2) to another master when notified of its existence,
+3) to another master if a loop is detected.
+In all cases, the recipient time daemon will become a slave.
+This message requires an acknowledgement.
+.sp 1
+.SH
+Set Date Message
+.so date
+.LP
+Type: TSP_SETDATE (22)
+.sp 1
+.PP
+The program \fIdate\fP\|(1) sends this message to the local time daemon
+when a super-user wants to set the network date.
+If the local time daemon is the master, it will set the date;
+if it is a slave, it will communicate the desired date to the master.
+.sp 1
+.SH
+Set Date Request Message
+.so date
+.LP
+Type: TSP_SETDATEREQ (23)
+.sp 1
+.PP
+A slave that has received a Set Date message will communicate the
+desired date to the master using this message.
+.sp 1
+.SH
+Set Date Acknowledgment Message
+.so unused
+.LP
+Type: TSP_DATEACK (16)
+.sp 1
+.PP
+The master sends this message to a slave in acknowledgment of a
+Set Date Request Message.
+The same message is sent by the local time daemon to the program
+\fIdate(1)\fP to confirm that the network date has been set by the
+master.
+.sp 1
+.SH
+Start Tracing Message
+.so unused
+.LP
+Type: TSP_TRACEON (17)
+.sp 1
+.PP
+The controlling program \fItimedc\fP sends this message to the local
+time daemon to start the recording in a system file of
+all messages received.
+.sp 1
+.SH
+Stop Tracing Message
+.so unused
+.LP
+Type: TSP_TRACEOFF (18)
+.sp 1
+.PP
+\fITimedc\fP sends this message to the local
+time daemon to stop the recording of
+messages received.
+.sp 1
+.SH
+Master Site Message
+.so unused
+.LP
+Type: TSP_MSITE (19)
+.sp 1
+.PP
+\fITimedc\fP sends this message to the local time daemon to find out
+where the master is running.
+.sp 1
+.SH
+Remote Master Site Message
+.so unused
+.LP
+Type: TSP_MSITEREQ (20)
+.sp 1
+.PP
+A local time daemon broadcasts this message to find the location
+of the master.
+It then uses the Acknowledgement message to
+communicate this location to \fItimedc\fP.
+.sp 1
+.SH
+Test Message
+.so unused
+.LP
+Type: TSP_TEST (21)
+.sp 1
+.PP
+For testing purposes, \fItimedc\fP sends this message to a slave
+to cause its election timer to expire. NOTE: \fItimed\fP
+is not normally compiled to support this.
+.sp 1
+.SH
+.SH
+Loop Detection Message
+.so loop
+.LP
+Type: TSP_LOOP (24)
+.sp 1
+.PP
+This packet is initiated by all masters occasionally to attempt to detect loops.
+All submasters forward this packet onto the networks over which they are master.
+If a master receives a packet it sent out initially,
+it knows that a loop exists and tries to correct the problem.
+.SH
+References
+.IP 1.
+R. Gusella and S. Zatti,
+\fITEMPO: A Network Time Controller for Distributed Berkeley UNIX System\fP,
+USENIX Summer Conference Proceedings, Salt Lake City, June 1984.
+.IP 2.
+R. Gusella and S. Zatti, \fIClock Synchronization in a Local Area Network\fP,
+University of California, Berkeley, Technical Report, \fIto appear\fP.
+.IP 3.
+R. Gusella and S. Zatti,
+\fIAn Election Algorithm for a Distributed Clock Synchronization Program\fP,
+University of California, Berkeley, CS Technical Report #275, Dec. 1985.
+.IP 4.
+Postel, J., \fIUser Datagram Protocol\fP, RFC 768.
+Network Information Center, SRI International, Menlo Park, California,
+August 1980.
+.IP 5.
+Postel, J., \fIInternet Control Message Protocol\fP, RFC 792.
+Network Information Center, SRI International, Menlo Park, California,
+September 1981.
diff --git a/usr.sbin/timed/SMM.doc/timed/unused b/usr.sbin/timed/SMM.doc/timed/unused
new file mode 100644
index 0000000..adadfc3
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timed/unused
@@ -0,0 +1,53 @@
+.\" 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.
+.\" 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.
+.\"
+.\" @(#)unused 8.1 (Berkeley) 6/8/93
+.\"
+.ft B
+.TS
+center;
+ce | ce | ce | ce
+| c | c | c | s |
+| c s s s |.
+Byte 1 Byte 2 Byte 3 Byte 4
+=
+Type Version No. Sequence No.
+_
+( unused )
+_
+( unused )
+_
+Machine Name
+_
+\&. . .
+_
+.TE
+.ft R
diff --git a/usr.sbin/timed/SMM.doc/timedop/Makefile b/usr.sbin/timed/SMM.doc/timedop/Makefile
new file mode 100644
index 0000000..7d52a32
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timedop/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 6/8/93
+# $FreeBSD$
+
+DIR= smm/11.timedop
+SRCS= timed.ms
+MACROS= -ms
+
+.include <bsd.doc.mk>
diff --git a/usr.sbin/timed/SMM.doc/timedop/timed.ms b/usr.sbin/timed/SMM.doc/timedop/timed.ms
new file mode 100644
index 0000000..feea0b5
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timedop/timed.ms
@@ -0,0 +1,279 @@
+.\" 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.
+.\" 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.
+.\"
+.\" @(#)timed.ms 8.1 (Berkeley) 6/8/93
+.\"
+.TL
+Timed Installation and Operation Guide
+.AU
+Riccardo Gusella, Stefano Zatti, James M. Bloom
+.AI
+Computer Systems Research Group
+Computer Science Division
+Department of Electrical Engineering and Computer Science
+University of California, Berkeley
+Berkeley, CA 94720
+.AU
+Kirk Smith
+.AI
+Engineering Computer Network
+Department of Electrical Engineering
+Purdue University
+West Lafayette, IN 47906
+.FS
+This work was sponsored by the Defense Advanced Research Projects Agency
+(DoD), monitored by the Naval Electronics Systems
+Command under contract No. N00039-84-C-0089, and by the CSELT
+Corporation of Italy.
+The views and conclusions contained in this document are those of the
+authors and should not be interpreted as representing official policies,
+either expressed or implied, of the Defense Research Projects Agency,
+of the US Government, or of CSELT.
+.FE
+.LP
+.EH 'SMM:11-%''Timed Installation and Operation'
+.OH 'Timed Installation and Operation''SMM:11-%'
+.SH
+Introduction
+.PP
+The clock synchronization service for
+the UNIX 4.3BSD operating system is composed of a collection of
+time daemons (\fItimed\fP) running on the machines in a local
+area network.
+The algorithms implemented by the service is based on a master-slave scheme.
+The time daemons communicate with each other using the
+\fITime Synchronization Protocol\fP (TSP) which
+is built on the DARPA UDP protocol and described in detail in [4].
+.PP
+A time daemon has a twofold function.
+First, it supports the synchronization of the clocks
+of the various hosts in a local area network.
+Second, it starts (or takes part in) the election that occurs
+among slave time daemons when, for any reason, the master disappears.
+The synchronization mechanism and the election procedure
+employed by the program \fItimed\fP are described
+in other documents [1,2,3].
+The next paragraphs are a brief overview of how the time daemon works.
+This document is mainly concerned with the administrative and technical
+issues of running \fItimed\fP at a particular site.
+.PP
+A \fImaster time daemon\fP measures the time
+differences between the clock of the machine on which it
+is running and those of all other machines.
+The master computes the \fInetwork time\fP as the average of the
+times provided by nonfaulty clocks.\**
+.FS
+A clock is considered to be faulty when its value
+is more than a small specified
+interval apart from the majority of the clocks
+of the other machines [1,2].
+.FE
+It then sends to each \fIslave time daemon\fP the
+correction that should be performed on the clock of its machine.
+This process is repeated periodically.
+Since the correction is expressed as a time difference rather than an
+absolute time, transmission delays do not interfere with
+the accuracy of the synchronization.
+When a machine comes up and joins the network,
+it starts a slave time daemon which
+will ask the master for the correct time and will reset the machine's clock
+before any user activity can begin.
+The time daemons are able to maintain a single network time in spite of
+the drift of clocks away from each other.
+The present implementation keeps processor clocks synchronized
+within 20 milliseconds.
+.PP
+To ensure that the service provided is continuous and reliable,
+it is necessary to implement an election algorithm to elect a
+new master should the machine running the current master crash, the master
+terminate (for example, because of a run-time error), or
+the network be partitioned.
+Under our algorithm, slaves are able to realize when the master has
+stopped functioning and to elect a new master from among themselves.
+It is important to note that, since the failure of the master results
+only in a gradual divergence of clock values, the election
+need not occur immediately.
+.PP
+The machines that are gateways between distinct local area
+networks require particular care.
+A time daemon on such machines may act as a \fIsubmaster\fP.
+This artifact depends on the current inability of
+transmission protocols to broadcast a message on a network
+other than the one to which the broadcasting machine is connected.
+The submaster appears as a slave on one network, and as a master
+on one or more of the other networks to which it is connected.
+.PP
+A submaster classifies each network as one of three types.
+A \fIslave network\fP is a network on which the submaster acts as a slave.
+There can only be one slave network.
+A \fImaster network\fP is a network on which the submaster acts as a master.
+An \fIignored network\fP is any other network which already has a valid master.
+The submaster tries periodically to become master on an ignored
+network, but gives up immediately if a master already exists.
+.SH
+Guidelines
+.PP
+While the synchronization algorithm is quite general, the election
+one, requiring a broadcast mechanism, puts constraints on
+the kind of network on which time daemons can run.
+The time daemon will only work on networks with broadcast capability
+augmented with point-to-point links.
+Machines that are only connected to point-to-point,
+non-broadcast networks may not use the time daemon.
+.PP
+If we exclude submasters, there will normally be, at most, one master time
+daemon in a local area internetwork.
+During an election, only one of the slave time daemons
+will become the new master.
+However, because of the characteristics of its machine,
+a slave can be prevented from becoming the master.
+Therefore, a subset of machines must be designated as potential
+master time daemons.
+A master time daemon will require CPU resources
+proportional to the number of slaves, in general, more than
+a slave time daemon, so it may be advisable to limit master time
+daemons to machines with more powerful processors or lighter loads.
+Also, machines with inaccurate clocks should not be used as masters.
+This is a purely administrative decision: an organization may
+well allow all of its machines to run master time daemons.
+.PP
+At the administrative level, a time daemon on a machine
+with multiple network interfaces, may be told to ignore all
+but one network or to ignore one network.
+This is done with the \fI\-n network\fP and \fI\-i network\fP
+options respectively at start-up time.
+Typically, the time daemon would be instructed to ignore all but
+the networks belonging to the local administrative control.
+.PP
+There are some limitations to the current
+implementation of the time daemon.
+It is expected that these limitations will be removed in future releases.
+The constant NHOSTS in /usr/src/etc/timed/globals.h limits the
+maximum number of machines that may be directly controlled by one
+master time daemon.
+The current maximum is 29 (NHOSTS \- 1).
+The constant must be changed and the program recompiled if a site wishes to
+run \fItimed\fP on a larger (inter)network.
+.PP
+In addition, there is a \fIpathological situation\fP to
+be avoided at all costs, that might occur when
+time daemons run on multiply-connected local area networks.
+In this case, as we have seen, time daemons running on gateway machines
+will be submasters and they will act on some of those
+networks as master time daemons.
+Consider machines A and B that are both gateways between
+networks X and Y.
+If time daemons were started on both A and B without constraints, it would be
+possible for submaster time daemon A to be a slave on network X
+and the master on network Y, while submaster time daemon B is a slave on
+network Y and the master on network X.
+This \fIloop\fP of master time daemons will not function properly
+or guarantee a unique time on both networks, and will cause
+the submasters to use large amounts of system resources in the form
+of network bandwidth and CPU time.
+In fact, this kind of \fIloop\fP can also be generated with more
+than two master time daemons,
+when several local area networks are interconnected.
+.SH
+Installation
+.PP
+In order to start the time daemon on a given machine,
+the following lines should be
+added to the \fIlocal daemons\fP section in the file \fI/etc/rc.local\fP:
+.sp 2
+.in 1i
+.nf
+if [ -f /etc/timed ]; then
+ /etc/timed \fIflags\fP & echo -n ' timed' >/dev/console
+fi
+.fi
+.in -1i
+.sp
+.LP
+In any case, they must appear after the network
+is configured via ifconfig(8).
+.PP
+Also, the file \fI/etc/services\fP should contain the following
+line:
+.sp 2
+.ti 1i
+timed 525/udp timeserver
+.sp
+.LP
+The \fIflags\fP are:
+.IP "-n network" 13
+to consider the named network.
+.IP "-i network"
+to ignore the named network.
+.IP -t
+to place tracing information in \fI/usr/adm/timed.log\fP.
+.IP -M
+to allow this time daemon to become a master.
+A time daemon run without this option will be forced in the state of
+slave during an election.
+.SH
+Daily Operation
+.PP
+\fITimedc(8)\fP is used to control the operation of the time daemon.
+It may be used to:
+.IP \(bu
+measure the differences between machines' clocks,
+.IP \(bu
+find the location where the master \fItimed\fP is running,
+.IP \(bu
+cause election timers on several machines to expire at the same time,
+.IP \(bu
+enable or disable tracing of messages received by \fItimed\fP.
+.LP
+See the manual page on \fItimed\fP\|(8) and \fItimedc\fP\|(8)
+for more detailed information.
+.PP
+The \fIdate(1)\fP command can be used to set the network date.
+In order to set the time on a single machine, the \fI-n\fP flag
+can be given to date(1).
+.bp
+.SH
+References
+.IP 1.
+R. Gusella and S. Zatti,
+\fITEMPO: A Network Time Controller for Distributed Berkeley UNIX System\fP,
+USENIX Summer Conference Proceedings, Salt Lake City, June 1984.
+.IP 2.
+R. Gusella and S. Zatti, \fIClock Synchronization in a Local Area Network\fP,
+University of California, Berkeley, Technical Report, \fIto appear\fP.
+.IP 3.
+R. Gusella and S. Zatti,
+\fIAn Election Algorithm for a Distributed Clock Synchronization Program\fP,
+University of California, Berkeley, CS Technical Report #275, Dec. 1985.
+.IP 4.
+R. Gusella and S. Zatti,
+\fIThe Berkeley UNIX 4.3BSD Time Synchronization Protocol\fP,
+UNIX Programmer's Manual, 4.3 Berkeley Software Distribution, Volume 2c.
diff --git a/usr.sbin/timed/timed/CHANGES b/usr.sbin/timed/timed/CHANGES
new file mode 100644
index 0000000..424ead2
--- /dev/null
+++ b/usr.sbin/timed/timed/CHANGES
@@ -0,0 +1,145 @@
+# @(#)CHANGES 5.1 (Berkeley) 5/11/93
+# $FreeBSD$
+
+This new version is almost identical to the timed and timedc code
+that has been shipped for years by a workstation vendor.
+
+Among the many changes:
+
+improve `timedc msite` to accept a list of hostnames.
+
+change slave-masters to answer the packets generated by `timedc msite`
+ with the name of the real master, not their own. This makes it
+ possible to "chase the chain" of slave servers to the ultimate
+ master.
+
+much improve the log caused by `timedc trace on`:
+ -made `timed -t` work.
+ -suppression of repeated entries, which both slowed down the daemon
+ (sometimes catastrophically) and tended to make disks fill up
+ even more quickly.
+ -better time stamps on log entries
+ -more messages
+ -dump information about slaves, master, and so on each time
+ a message asking the log be turned on is received, and
+ when the log is turned off.
+ -fewer CPU cycles
+
+use a hash table to keep track of slaves, instead of the stupid linear
+ list. This becomes handy with hundreds of slaves, instead of
+ the original design limit of "a room with a few VAX's."
+
+separate the main protocol timer from that used to look for other networks
+ to master.
+
+time stamp packets received by the daemon, so that time corrections
+ are not made (even more) inaccurate by waiting in the internal,
+ timed queue while the daemon is processing other messages.
+
+made -n and -i work with subnets not named in /etc/networks
+
+compute the median of the measured clocks, instead of the average
+ of "good" times.
+
+vastly improve the accuracy of the clock difference measure by
+ `timedc clockdiff`.
+
+use adjtime() when possible, and directly set the clock only when
+ necessary.
+
+when the requested adjustment is small, perform only part of it, to
+ damp oscillations and improve the long term accuracy of the
+ adjustments.
+
+fix uncounted core-dumps on machines that do not allow dereferencing 0
+ in both the daemon and timedc.
+
+fix "master loop detection".
+
+fix several cases in which multi-homed masters could get into shouting
+ matches, consuming all available network bandwidth and CPU cycles
+ (which ever runs out first), and convincing all bystanders to stop
+ advancing their own clocks.
+
+refuse to behave badly when other machines do. Instead of arguing forever,
+ go off and sulk when other machines refuse to play by the rules.
+
+increase the maximum number of clients.
+
+add "-F host,host2,..." to "freerun" or "trust" only some hosts. This
+ is handy both when only some machines should be trusted to let
+ root use the `date` command to change time in the network.
+
+ It is also handy when one machine has some other way of adjusting
+ its clock, whether NTP or a direct radio or atomic connection.
+ "-F localhost" causes `timed` to "trust" only itself.
+
+ It is also handy to build a hierarchy of timed masters crossing
+ networks. The TSP protocol has no provision of "goodness of clock",
+ no natural way to completely heal network paritions. Judicious
+ use of -F or -G can cause each gateway to trust only itself and
+ machines closer to a central machine with a radio or atomic clock.
+
+add #ifdef code that supports NIS "netgroups" of trusted hosts, which
+ can be easier to administer than -F.
+
+add #ifdef code to compute an aged total adjustment. This can be used
+ in systems that can make long term changes in their system clock
+ frequency, e.g. "timetrim" in the Silicon Graphics kernel.
+
+
+Problems observed by others that are unresolved include:
+
+Practically any users can send to the master TSP messages and this
+ way corrupt the reliability of the system. Authentication
+ of messages should be provided. Unfortunately, that would
+ require changing the protocol with all of the implied
+ compatiblity problems. Fortunately, the new -F and -G args
+ can be used to cause the daemon to ignore time changes from
+ untrusted machines.
+
+MAN. The limit of 1013 on the number of slaves hosts should be doc'ed.
+
+ It should be dynamically allocated with no limit. On a
+ large network, one host could possibly master over many
+ more than 30 hosts. Given the timers in the code and
+ effectively in the protocol, and the time required by each
+ master to talk to each slave, it is not practical to have
+ more than 200-300 slaves. The master cannot keep up because
+ the slave-chatting is single-threaded. when the master
+ gets behind, slaves start demanding elections. To
+ significantly increase the number of slaves would require
+ multi-treading things, and given that a network with more
+ than 300 directly addressable machines has worse problems
+ than keep the time of day right, not worth worrying about.
+
+UGLY,CODE. timedc/cmds.c has a lots of repeated code in it.
+
+**** The first thing is that each command is set up as if it
+ were an individual program taking argc and argv. A more
+ conventional calling style should be used. I don't think
+ any of the routines take more than a couple arguments.
+
+UGLY. fxn definition syntax does't follow convention:
+ has type on same line.
+
+**** It needs to be fixed at least enough that tags
+ will work on it. An entire cleanup might be nice later, but
+ is noncritical.
+
+LOBBY(mildly),CODE: Would be very convenient if date(1) took a
+ +-<number> argument to set the time relatively. With
+ the advent of timed it is now reasonable to synchronize
+ with WWV, which is nearly impossible to do "by hand"
+ with just an absolute date, and scripts are too slow.
+ format could be +-nn...nn.ss, where the '.' is required
+ to remove ambiguity.
+
+**** If you want to do it go ahead. It sounds useful. As far as
+ syntax goes, the normal format for the date should work just
+ fine for this. If the date is preceded by a plus or minus,
+ the change is relative, otherwise it is absolute.
+
+
+Vernon Schryver.
+vjs@sgi.com
diff --git a/usr.sbin/timed/timed/Makefile b/usr.sbin/timed/timed/Makefile
new file mode 100644
index 0000000..af70b06
--- /dev/null
+++ b/usr.sbin/timed/timed/Makefile
@@ -0,0 +1,17 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+#
+# optional flags are: MEASURE TESTING DEBUG
+
+PROG= timed
+MAN= timed.8
+SRCS= acksend.c candidate.c correct.c master.c networkdelta.c readmsg.c \
+ slave.c timed.c byteorder.c measure.c cksum.c
+
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+
+WARNS?= 1
+
+.include "../../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/usr.sbin/timed/timed/acksend.c b/usr.sbin/timed/timed/acksend.c
new file mode 100644
index 0000000..8bde743
--- /dev/null
+++ b/usr.sbin/timed/timed/acksend.c
@@ -0,0 +1,132 @@
+/*-
+ * 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.
+ * 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
+#if 0
+static char sccsid[] = "@(#)acksend.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "globals.h"
+
+struct tsp *answer;
+
+extern u_short sequence;
+
+void
+xmit(type, seq, addr)
+ int type;
+ u_int seq;
+ struct sockaddr_in *addr;
+{
+ static struct tsp msg;
+
+ msg.tsp_type = type;
+ msg.tsp_seq = seq;
+ msg.tsp_vers = TSPVERSION;
+ (void)strcpy(msg.tsp_name, hostname);
+ bytenetorder(&msg);
+ if (sendto(sock, (char *)&msg, sizeof(struct tsp), 0,
+ (struct sockaddr*)addr, sizeof(struct sockaddr)) < 0) {
+ trace_sendto_err(addr->sin_addr);
+ }
+}
+
+
+/*
+ * Acksend implements reliable datagram transmission by using sequence
+ * numbers and retransmission when necessary.
+ * If `name' is ANYADDR, this routine implements reliable broadcast.
+ *
+ * Because this function calls readmsg(), none of its args may be in
+ * a message provided by readmsg().
+ */
+struct tsp *
+acksend(message, addr, name, ack, net, bad)
+ struct tsp *message; /* this message */
+ struct sockaddr_in *addr; /* to here */
+ char *name;
+ int ack; /* look for this ack */
+ struct netinfo *net; /* receive from this network */
+ int bad; /* 1=losing patience */
+{
+ struct timeval twait;
+ int count;
+ long msec;
+
+ message->tsp_vers = TSPVERSION;
+ message->tsp_seq = sequence;
+ if (trace) {
+ fprintf(fd, "acksend: to %s: ",
+ (name == ANYADDR ? "broadcast" : name));
+ print(message, addr);
+ }
+ bytenetorder(message);
+
+ msec = 200;
+ count = bad ? 1 : 5; /* 5 packets in 6.4 seconds */
+ answer = 0;
+ do {
+ if (!answer) {
+ /* do not go crazy transmitting just because the
+ * other guy cannot keep our sequence numbers
+ * straight.
+ */
+ if (sendto(sock, (char *)message, sizeof(struct tsp),
+ 0, (struct sockaddr*)addr,
+ sizeof(struct sockaddr)) < 0) {
+ trace_sendto_err(addr->sin_addr);
+ break;
+ }
+ }
+
+ mstotvround(&twait, msec);
+ answer = readmsg(ack, name, &twait, net);
+ if (answer != 0) {
+ if (answer->tsp_seq != sequence) {
+ if (trace)
+ fprintf(fd,"acksend: seq # %u!=%u\n",
+ answer->tsp_seq, sequence);
+ continue;
+ }
+ break;
+ }
+
+ msec *= 2;
+ } while (--count > 0);
+ sequence++;
+
+ return(answer);
+}
diff --git a/usr.sbin/timed/timed/byteorder.c b/usr.sbin/timed/timed/byteorder.c
new file mode 100644
index 0000000..038dd56
--- /dev/null
+++ b/usr.sbin/timed/timed/byteorder.c
@@ -0,0 +1,86 @@
+/*-
+ * 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.
+ * 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
+#if 0
+static char sccsid[] = "@(#)byteorder.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "globals.h"
+
+/*
+ * Two routines to do the necessary byte swapping for timed protocol
+ * messages. Protocol is defined in /usr/include/protocols/timed.h
+ */
+void
+bytenetorder(ptr)
+ struct tsp *ptr;
+{
+ ptr->tsp_seq = htons((u_short)ptr->tsp_seq);
+ switch (ptr->tsp_type) {
+
+ case TSP_SETTIME:
+ case TSP_ADJTIME:
+ case TSP_SETDATE:
+ case TSP_SETDATEREQ:
+ ptr->tsp_time.tv_sec = htonl((u_long)ptr->tsp_time.tv_sec);
+ ptr->tsp_time.tv_usec = htonl((u_long)ptr->tsp_time.tv_usec);
+ break;
+
+ default:
+ break; /* nothing more needed */
+ }
+}
+
+void
+bytehostorder(ptr)
+ struct tsp *ptr;
+{
+ ptr->tsp_seq = ntohs((u_short)ptr->tsp_seq);
+ switch (ptr->tsp_type) {
+
+ case TSP_SETTIME:
+ case TSP_ADJTIME:
+ case TSP_SETDATE:
+ case TSP_SETDATEREQ:
+ ptr->tsp_time.tv_sec = ntohl((u_long)ptr->tsp_time.tv_sec);
+ ptr->tsp_time.tv_usec = ntohl((u_long)ptr->tsp_time.tv_usec);
+ break;
+
+ default:
+ break; /* nothing more needed */
+ }
+}
diff --git a/usr.sbin/timed/timed/candidate.c b/usr.sbin/timed/timed/candidate.c
new file mode 100644
index 0000000..79f1f5a
--- /dev/null
+++ b/usr.sbin/timed/timed/candidate.c
@@ -0,0 +1,167 @@
+/*-
+ * 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.
+ * 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
+#if 0
+static char sccsid[] = "@(#)candidate.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "globals.h"
+
+/*
+ * `election' candidates a host as master: it is called by a slave
+ * which runs with the -M option set when its election timeout expires.
+ * Note the conservative approach: if a new timed comes up, or another
+ * candidate sends an election request, the candidature is withdrawn.
+ */
+int
+election(net)
+ struct netinfo *net;
+{
+ struct tsp *resp, msg;
+ struct timeval then, wait;
+ struct tsp *answer;
+ struct hosttbl *htp;
+ char loop_lim = 0;
+
+/* This code can get totally confused if it gets slightly behind. For
+ * example, if readmsg() has some QUIT messages waiting from the last
+ * round, we would send an ELECTION message, get the stale QUIT,
+ * and give up. This results in network storms when several machines
+ * do it at once.
+ */
+ wait.tv_sec = 0;
+ wait.tv_usec = 0;
+ while (0 != readmsg(TSP_REFUSE, ANYADDR, &wait, net)) {
+ if (trace)
+ fprintf(fd, "election: discarded stale REFUSE\n");
+ }
+ while (0 != readmsg(TSP_QUIT, ANYADDR, &wait, net)) {
+ if (trace)
+ fprintf(fd, "election: discarded stale QUIT\n");
+ }
+
+again:
+ syslog(LOG_INFO, "This machine is a candidate time master");
+ if (trace)
+ fprintf(fd, "This machine is a candidate time master\n");
+ msg.tsp_type = TSP_ELECTION;
+ msg.tsp_vers = TSPVERSION;
+ (void)strcpy(msg.tsp_name, hostname);
+ bytenetorder(&msg);
+ if (sendto(sock, (char *)&msg, sizeof(struct tsp), 0,
+ (struct sockaddr*)&net->dest_addr,
+ sizeof(struct sockaddr)) < 0) {
+ trace_sendto_err(net->dest_addr.sin_addr);
+ return(SLAVE);
+ }
+
+ (void)gettimeofday(&then, 0);
+ then.tv_sec += 3;
+ for (;;) {
+ (void)gettimeofday(&wait, 0);
+ timevalsub(&wait,&then,&wait);
+ resp = readmsg(TSP_ANY, ANYADDR, &wait, net);
+ if (!resp)
+ return(MASTER);
+
+ switch (resp->tsp_type) {
+
+ case TSP_ACCEPT:
+ (void)addmach(resp->tsp_name, &from,fromnet);
+ break;
+
+ case TSP_MASTERUP:
+ case TSP_MASTERREQ:
+ /*
+ * If another timedaemon is coming up at the same
+ * time, give up, and let it be the master.
+ */
+ if (++loop_lim < 5
+ && !good_host_name(resp->tsp_name)) {
+ (void)addmach(resp->tsp_name, &from,fromnet);
+ suppress(&from, resp->tsp_name, net);
+ goto again;
+ }
+ rmnetmachs(net);
+ return(SLAVE);
+
+ case TSP_QUIT:
+ case TSP_REFUSE:
+ /*
+ * Collision: change value of election timer
+ * using exponential backoff.
+ *
+ * Fooey.
+ * An exponential backoff on a delay starting at
+ * 6 to 15 minutes for a process that takes
+ * milliseconds is silly. It is particularly
+ * strange that the original code would increase
+ * the backoff without bound.
+ */
+ rmnetmachs(net);
+ return(SLAVE);
+
+ case TSP_ELECTION:
+ /* no master for another round */
+ htp = addmach(resp->tsp_name,&from,fromnet);
+ msg.tsp_type = TSP_REFUSE;
+ (void)strcpy(msg.tsp_name, hostname);
+ answer = acksend(&msg, &htp->addr, htp->name,
+ TSP_ACK, 0, htp->noanswer);
+ if (!answer) {
+ syslog(LOG_ERR, "error in election from %s",
+ htp->name);
+ }
+ break;
+
+ case TSP_SLAVEUP:
+ (void)addmach(resp->tsp_name, &from,fromnet);
+ break;
+
+ case TSP_SETDATE:
+ case TSP_SETDATEREQ:
+ break;
+
+ default:
+ if (trace) {
+ fprintf(fd, "candidate: ");
+ print(resp, &from);
+ }
+ break;
+ }
+ }
+}
diff --git a/usr.sbin/timed/timed/cksum.c b/usr.sbin/timed/timed/cksum.c
new file mode 100644
index 0000000..50a7da2
--- /dev/null
+++ b/usr.sbin/timed/timed/cksum.c
@@ -0,0 +1,87 @@
+/*-
+ * 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.
+ * 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
+#if 0
+static char sccsid[] = "@(#)cksum.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+/*
+ * I N _ C K S U M
+ *
+ * Checksum routine for Internet Protocol family headers (C Version)
+ *
+ * There is no profit in a specialized version of the checksum
+ * function for any machine where int's are 32 bits and shorts are 16.
+ *
+ * All timed packets are smaller than 32K shorts, so there is no need to
+ * worry about carries except at the end.
+ */
+int
+in_cksum(addr, len)
+ u_short *addr;
+ int len;
+{
+ register int nleft = len;
+ register u_short *w = addr;
+ register u_short answer;
+ register int sum = 0;
+
+ /*
+ * 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 )
+ sum += (*(u_char *)w) << 8;
+
+ /*
+ * 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);
+}
diff --git a/usr.sbin/timed/timed/correct.c b/usr.sbin/timed/timed/correct.c
new file mode 100644
index 0000000..d948dee
--- /dev/null
+++ b/usr.sbin/timed/timed/correct.c
@@ -0,0 +1,198 @@
+/*-
+ * 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.
+ * 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
+#if 0
+static char sccsid[] = "@(#)correct.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "globals.h"
+#include <math.h>
+#include <sys/types.h>
+#include <sys/times.h>
+
+static void adjclock(struct timeval *);
+
+/*
+ * sends to the slaves the corrections for their clocks after fixing our
+ * own
+ */
+void
+correct(avdelta)
+ long avdelta;
+{
+ struct hosttbl *htp;
+ int corr;
+ struct timeval adjlocal, tmptv;
+ struct tsp to;
+ struct tsp *answer;
+
+ mstotvround(&adjlocal, avdelta);
+
+ for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
+ if (htp->delta != HOSTDOWN) {
+ corr = avdelta - htp->delta;
+/* If the other machine is off in the weeds, set its time directly.
+ * If a slave gets the wrong day, the original code would simply
+ * fix the minutes. If you fix a network partition, you can get
+ * into such situations.
+ */
+ if (htp->need_set
+ || corr >= MAXADJ*1000
+ || corr <= -MAXADJ*1000) {
+ htp->need_set = 0;
+ (void)gettimeofday(&tmptv,0);
+ timevaladd(&tmptv, &adjlocal);
+ to.tsp_time.tv_sec = tmptv.tv_sec;
+ to.tsp_time.tv_usec = tmptv.tv_usec;
+ to.tsp_type = TSP_SETTIME;
+ } else {
+ tmptv.tv_sec = to.tsp_time.tv_sec;
+ tmptv.tv_usec = to.tsp_time.tv_usec;
+ mstotvround(&tmptv, corr);
+ to.tsp_time.tv_sec = tmptv.tv_sec;
+ to.tsp_time.tv_usec = tmptv.tv_usec;
+ to.tsp_type = TSP_ADJTIME;
+ }
+ (void)strcpy(to.tsp_name, hostname);
+ answer = acksend(&to, &htp->addr, htp->name,
+ TSP_ACK, 0, 0);
+ if (!answer) {
+ htp->delta = HOSTDOWN;
+ syslog(LOG_WARNING,
+ "no reply to time correction from %s",
+ htp->name);
+ if (++htp->noanswer >= LOSTHOST) {
+ if (trace) {
+ fprintf(fd,
+ "purging %s for not answering\n",
+ htp->name);
+ (void)fflush(fd);
+ }
+ htp = remmach(htp);
+ }
+ }
+ }
+ }
+
+ /*
+ * adjust our own clock now that we are not sending it out
+ */
+ adjclock(&adjlocal);
+}
+
+
+static void
+adjclock(corr)
+ struct timeval *corr;
+{
+ static int passes = 0;
+ static int smoother = 0;
+ long delta; /* adjustment in usec */
+ long ndelta;
+ struct timeval now;
+ struct timeval adj;
+
+ if (!timerisset(corr))
+ return;
+
+ adj = *corr;
+ if (adj.tv_sec < MAXADJ && adj.tv_sec > - MAXADJ) {
+ delta = adj.tv_sec*1000000 + adj.tv_usec;
+ /* If the correction is less than the minimum round
+ * trip time for an ICMP packet, and thus
+ * less than the likely error in the measurement,
+ * do not do the entire correction. Do half
+ * or a quarter of it.
+ */
+
+ if (delta > -MIN_ROUND*1000
+ && delta < MIN_ROUND*1000) {
+ if (smoother <= 4)
+ smoother++;
+ ndelta = delta >> smoother;
+ if (trace)
+ fprintf(fd,
+ "trimming delta %ld usec to %ld\n",
+ delta, ndelta);
+ adj.tv_usec = ndelta;
+ adj.tv_sec = 0;
+ } else if (smoother > 0) {
+ smoother--;
+ }
+ if (0 > adjtime(corr, 0)) {
+ syslog(LOG_ERR, "adjtime: %m");
+ }
+ if (passes > 1
+ && (delta < -BIG_ADJ || delta > BIG_ADJ)) {
+ smoother = 0;
+ passes = 0;
+ syslog(LOG_WARNING,
+ "large time adjustment of %+.3f sec",
+ delta/1000000.0);
+ }
+ } else {
+ syslog(LOG_WARNING,
+ "clock correction %ld sec too large to adjust",
+ adj.tv_sec);
+ (void) gettimeofday(&now, 0);
+ timevaladd(&now, corr);
+ if (settimeofday(&now, 0) < 0)
+ syslog(LOG_ERR, "settimeofday: %m");
+ }
+}
+
+
+/* adjust the time in a message by the time it
+ * spent in the queue
+ */
+void
+adj_msg_time(msg, now)
+ struct tsp *msg;
+ struct timeval *now;
+{
+ msg->tsp_time.tv_sec += (now->tv_sec - from_when.tv_sec);
+ msg->tsp_time.tv_usec += (now->tv_usec - from_when.tv_usec);
+
+ while (msg->tsp_time.tv_usec < 0) {
+ msg->tsp_time.tv_sec--;
+ msg->tsp_time.tv_usec += 1000000;
+ }
+ while (msg->tsp_time.tv_usec >= 1000000) {
+ msg->tsp_time.tv_sec++;
+ msg->tsp_time.tv_usec -= 1000000;
+ }
+}
diff --git a/usr.sbin/timed/timed/extern.h b/usr.sbin/timed/timed/extern.h
new file mode 100644
index 0000000..870ef9e
--- /dev/null
+++ b/usr.sbin/timed/timed/extern.h
@@ -0,0 +1,91 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ */
+
+struct hosttbl;
+struct netinfo;
+struct sockaddr_in;
+struct timeval;
+struct tsp;
+
+struct hosttbl *addmach(char *, struct sockaddr_in *, struct netinfo *);
+struct hosttbl *findhost(char *);
+struct hosttbl *remmach(struct hosttbl *);
+
+struct tsp *readmsg(int,
+ char *, struct timeval *, struct netinfo *);
+struct tsp *acksend(struct tsp *,
+ struct sockaddr_in *, char *, int, struct netinfo *, int);
+
+void addnetname(char *);
+void adj_msg_time(struct tsp *, struct timeval *);
+void bytehostorder(struct tsp *);
+void bytenetorder(struct tsp *);
+void byteorder(struct tsp *);
+long casual(long, long);
+int cksum(u_short *, int);
+void correct(long);
+char *date(void);
+void doquit(struct tsp *);
+int election(struct netinfo *);
+void get_goodgroup(int);
+int good_host_name(char *);
+void ignoreack(void);
+int in_cksum(u_short *, int);
+void lookformaster(struct netinfo *);
+void makeslave(struct netinfo *);
+int master(void);
+void masterack(void);
+void masterup(struct netinfo *);
+int measure(u_long, u_long, char *, struct sockaddr_in *, int);
+void msterup(struct netinfo *);
+void mstotvround(struct timeval *, long);
+long networkdelta(void);
+void newslave(struct tsp *);
+void print(struct tsp *, struct sockaddr_in *);
+void prthp(clock_t);
+void rmnetmachs(struct netinfo *);
+void setstatus(void);
+int slave(void);
+void slaveack(void);
+void spreadtime(void);
+void suppress(struct sockaddr_in *, char *, struct netinfo *);
+void synch(long);
+void timevaladd(struct timeval *, struct timeval *);
+void timevalsub(struct timeval *, struct timeval *, struct timeval *);
+void traceoff(char *);
+void traceon(void);
+void xmit(int, u_int, struct sockaddr_in *);
diff --git a/usr.sbin/timed/timed/globals.h b/usr.sbin/timed/timed/globals.h
new file mode 100644
index 0000000..206e7f5
--- /dev/null
+++ b/usr.sbin/timed/timed/globals.h
@@ -0,0 +1,172 @@
+/*-
+ * 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.
+ * 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.
+ *
+ * @(#)globals.h 8.1 (Berkeley) 6/6/93
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <protocols/timed.h>
+#define SECHR (60*60)
+#define SECDAY (24*SECHR)
+
+/* Best expected round trip for a measurement.
+ * This is essentially the number of milliseconds per CPU tick (CLK_TCK?).
+ * All delays shorter than this are usually reported as 0.
+ */
+#define MIN_ROUND ((1000-1)/CLK_TCK)
+
+
+#define SAMPLEINTVL 240 /* synch() freq for master in sec */
+#define MAXADJ 20 /* max adjtime() correction in sec */
+
+#define MAX_TRIM 3000000 /* max drift in nsec/sec, 0.3% */
+#define BIG_ADJ (MAX_TRIM/1000*SAMPLEINTVL*2) /* max good adj */
+
+#define MINTOUT 360 /* election delays, 6-15 minutes */
+#define MAXTOUT 900
+
+#define BAD_STATUS (-1)
+#define GOOD 1
+#define UNREACHABLE 2
+#define NONSTDTIME 3
+#define HOSTDOWN 0x7fffffff
+
+#define OFF 0
+#define ON 1
+
+#define MAX_HOPCNT 10 /* max value for tsp_hpcnt */
+
+#define LOSTHOST 3 /* forget after this many failures */
+
+#define VALID_RANGE (MAXADJ*1000) /* good times in milliseconds */
+#define GOOD_RANGE (MIN_ROUND*2)
+#define VGOOD_RANGE (MIN_ROUND-1)
+
+
+/*
+ * Global and per-network states.
+ */
+#define NOMASTER 0 /* no good master */
+#define SLAVE 1
+#define MASTER 2
+#define IGNORE 4
+#define ALL (SLAVE|MASTER|IGNORE)
+#define SUBMASTER (SLAVE|MASTER)
+
+#define NHOSTS 1013 /* max of hosts controlled by timed
+ * This must be a prime number.
+ */
+struct hosttbl {
+ struct hosttbl *h_bak; /* hash chain */
+ struct hosttbl *h_fwd;
+ struct hosttbl *l_bak; /* "sequential" list */
+ struct hosttbl *l_fwd;
+ struct netinfo *ntp;
+ struct sockaddr_in addr;
+ char name[MAXHOSTNAMELEN];
+ u_char head; /* 1=head of hash chain */
+ u_char good; /* 0=trusted host, for averaging */
+ u_char noanswer; /* count of failures to answer */
+ u_char need_set; /* need a SETTIME */
+ u_short seq;
+ long delta;
+};
+
+/* closed hash table with internal chaining */
+extern struct hosttbl hosttbl[NHOSTS+1];
+#define self hosttbl[0]
+#define hostname (self.name)
+
+
+struct netinfo {
+ struct netinfo *next;
+ struct in_addr net;
+ u_int32_t mask;
+ struct in_addr my_addr;
+ struct sockaddr_in dest_addr; /* broadcast addr or point-point */
+ long status;
+ struct timeval slvwait; /* delay before sending our time */
+ int quit_count; /* recent QUITs */
+};
+
+#include "extern.h"
+
+#define tvtomsround(tv) ((tv).tv_sec*1000 + ((tv).tv_usec + 500)/1000)
+
+extern struct netinfo *nettab;
+extern int status;
+extern int trace;
+extern int sock;
+extern struct sockaddr_in from;
+extern struct timeval from_when; /* when the last msg arrived */
+extern u_short sequence; /* TSP message sequence number */
+extern struct netinfo *fromnet, *slavenet;
+extern FILE *fd;
+extern long delay1, delay2;
+extern int nslavenets; /* nets were I could be a slave */
+extern int nmasternets; /* nets were I could be a master */
+extern int nignorednets; /* ignored nets */
+extern int nnets; /* nets I am connected to */
+
+
+#define trace_msg(msg) {if (trace) fprintf(fd, msg);}
+
+#define trace_sendto_err(addr) { \
+ int st_errno = errno; \
+ syslog(LOG_ERR, "%s %d: sendto %s: %m", \
+ __FILE__, __LINE__, inet_ntoa(addr)); \
+ if (trace) \
+ fprintf(fd, "%s %d: sendto %s: %d", __FILE__, __LINE__, \
+ inet_ntoa(addr), st_errno); \
+}
+
+
+# define max(a,b) (a<b ? b : a)
+# define min(a,b) (a>b ? b : a)
+# define abs(x) (x>=0 ? x : -(x))
diff --git a/usr.sbin/timed/timed/master.c b/usr.sbin/timed/timed/master.c
new file mode 100644
index 0000000..e36fbae
--- /dev/null
+++ b/usr.sbin/timed/timed/master.c
@@ -0,0 +1,852 @@
+/*-
+ * 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.
+ * 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
+#if 0
+static char sccsid[] = "@(#)master.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "globals.h"
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/times.h>
+#include <setjmp.h>
+#include "pathnames.h"
+
+extern int measure_delta;
+extern jmp_buf jmpenv;
+extern int Mflag;
+extern int justquit;
+
+static int dictate;
+static int slvcount; /* slaves listening to our clock */
+
+static void mchgdate(struct tsp *);
+
+extern void logwtmp(char *, char *, char *);
+
+/*
+ * The main function of `master' is to periodically compute the differences
+ * (deltas) between its clock and the clocks of the slaves, to compute the
+ * network average delta, and to send to the slaves the differences between
+ * their individual deltas and the network delta.
+ * While waiting, it receives messages from the slaves (i.e. requests for
+ * master's name, remote requests to set the network time, ...), and
+ * takes the appropriate action.
+ */
+int
+master()
+{
+ struct hosttbl *htp;
+ long pollingtime;
+#define POLLRATE 4
+ int polls;
+ struct timeval wait, ntime;
+ time_t tsp_time_sec;
+ struct tsp *msg, *answer, to;
+ char newdate[32];
+ struct sockaddr_in taddr;
+ char tname[MAXHOSTNAMELEN];
+ struct netinfo *ntp;
+ int i;
+
+ syslog(LOG_NOTICE, "This machine is master");
+ if (trace)
+ fprintf(fd, "This machine is master\n");
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ if (ntp->status == MASTER)
+ masterup(ntp);
+ }
+ (void)gettimeofday(&ntime, 0);
+ pollingtime = ntime.tv_sec+3;
+ if (justquit)
+ polls = 0;
+ else
+ polls = POLLRATE-1;
+
+/* Process all outstanding messages before spending the long time necessary
+ * to update all timers.
+ */
+loop:
+ (void)gettimeofday(&ntime, 0);
+ wait.tv_sec = pollingtime - ntime.tv_sec;
+ if (wait.tv_sec < 0)
+ wait.tv_sec = 0;
+ wait.tv_usec = 0;
+ msg = readmsg(TSP_ANY, ANYADDR, &wait, 0);
+ if (!msg) {
+ (void)gettimeofday(&ntime, 0);
+ if (ntime.tv_sec >= pollingtime) {
+ pollingtime = ntime.tv_sec + SAMPLEINTVL;
+ get_goodgroup(0);
+
+/* If a bogus master told us to quit, we can have decided to ignore a
+ * network. Therefore, periodically try to take over everything.
+ */
+ polls = (polls + 1) % POLLRATE;
+ if (0 == polls && nignorednets > 0) {
+ trace_msg("Looking for nets to re-master\n");
+ for (ntp = nettab; ntp; ntp = ntp->next) {
+ if (ntp->status == IGNORE
+ || ntp->status == NOMASTER) {
+ lookformaster(ntp);
+ if (ntp->status == MASTER) {
+ masterup(ntp);
+ polls = POLLRATE-1;
+ }
+ }
+ if (ntp->status == MASTER
+ && --ntp->quit_count < 0)
+ ntp->quit_count = 0;
+ }
+ if (polls != 0)
+ setstatus();
+ }
+
+ synch(0L);
+
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ to.tsp_type = TSP_LOOP;
+ to.tsp_vers = TSPVERSION;
+ to.tsp_seq = sequence++;
+ to.tsp_hopcnt = MAX_HOPCNT;
+ (void)strcpy(to.tsp_name, hostname);
+ bytenetorder(&to);
+ if (sendto(sock, (char *)&to,
+ sizeof(struct tsp), 0,
+ (struct sockaddr*)&ntp->dest_addr,
+ sizeof(ntp->dest_addr)) < 0) {
+ trace_sendto_err(ntp->dest_addr.sin_addr);
+ }
+ }
+ }
+
+
+ } else {
+ switch (msg->tsp_type) {
+
+ case TSP_MASTERREQ:
+ break;
+
+ case TSP_SLAVEUP:
+ newslave(msg);
+ break;
+
+ case TSP_SETDATE:
+ /*
+ * XXX check to see it is from ourself
+ */
+ tsp_time_sec = msg->tsp_time.tv_sec;
+ (void)strcpy(newdate, ctime(&tsp_time_sec));
+ if (!good_host_name(msg->tsp_name)) {
+ syslog(LOG_NOTICE,
+ "attempted date change by %s to %s",
+ msg->tsp_name, newdate);
+ spreadtime();
+ break;
+ }
+
+ mchgdate(msg);
+ (void)gettimeofday(&ntime, 0);
+ pollingtime = ntime.tv_sec + SAMPLEINTVL;
+ break;
+
+ case TSP_SETDATEREQ:
+ if (!fromnet || fromnet->status != MASTER)
+ break;
+ tsp_time_sec = msg->tsp_time.tv_sec;
+ (void)strcpy(newdate, ctime(&tsp_time_sec));
+ htp = findhost(msg->tsp_name);
+ if (htp == 0) {
+ syslog(LOG_ERR,
+ "attempted SET DATEREQ by uncontrolled %s to %s",
+ msg->tsp_name, newdate);
+ break;
+ }
+ if (htp->seq == msg->tsp_seq)
+ break;
+ htp->seq = msg->tsp_seq;
+ if (!htp->good) {
+ syslog(LOG_NOTICE,
+ "attempted SET DATEREQ by untrusted %s to %s",
+ msg->tsp_name, newdate);
+ spreadtime();
+ break;
+ }
+
+ mchgdate(msg);
+ (void)gettimeofday(&ntime, 0);
+ pollingtime = ntime.tv_sec + SAMPLEINTVL;
+ break;
+
+ case TSP_MSITE:
+ xmit(TSP_ACK, msg->tsp_seq, &from);
+ break;
+
+ case TSP_MSITEREQ:
+ break;
+
+ case TSP_TRACEON:
+ traceon();
+ break;
+
+ case TSP_TRACEOFF:
+ traceoff("Tracing ended at %s\n");
+ break;
+
+ case TSP_ELECTION:
+ if (!fromnet)
+ break;
+ if (fromnet->status == MASTER) {
+ pollingtime = 0;
+ (void)addmach(msg->tsp_name, &from,fromnet);
+ }
+ taddr = from;
+ (void)strcpy(tname, msg->tsp_name);
+ to.tsp_type = TSP_QUIT;
+ (void)strcpy(to.tsp_name, hostname);
+ answer = acksend(&to, &taddr, tname,
+ TSP_ACK, 0, 1);
+ if (answer == NULL) {
+ syslog(LOG_ERR, "election error by %s",
+ tname);
+ }
+ break;
+
+ case TSP_CONFLICT:
+ /*
+ * After a network partition, there can be
+ * more than one master: the first slave to
+ * come up will notify here the situation.
+ */
+ if (!fromnet || fromnet->status != MASTER)
+ break;
+ (void)strcpy(to.tsp_name, hostname);
+
+ /* The other master often gets into the same state,
+ * with boring results if we stay at it forever.
+ */
+ ntp = fromnet; /* (acksend() can leave fromnet=0 */
+ for (i = 0; i < 3; i++) {
+ to.tsp_type = TSP_RESOLVE;
+ (void)strcpy(to.tsp_name, hostname);
+ answer = acksend(&to, &ntp->dest_addr,
+ ANYADDR, TSP_MASTERACK,
+ ntp, 0);
+ if (!answer)
+ break;
+ htp = addmach(answer->tsp_name,&from,ntp);
+ to.tsp_type = TSP_QUIT;
+ msg = acksend(&to, &htp->addr, htp->name,
+ TSP_ACK, 0, htp->noanswer);
+ if (msg == NULL) {
+ syslog(LOG_ERR,
+ "no response from %s to CONFLICT-QUIT",
+ htp->name);
+ }
+ }
+ masterup(ntp);
+ pollingtime = 0;
+ break;
+
+ case TSP_RESOLVE:
+ if (!fromnet || fromnet->status != MASTER)
+ break;
+ /*
+ * do not want to call synch() while waiting
+ * to be killed!
+ */
+ (void)gettimeofday(&ntime, (struct timezone *)0);
+ pollingtime = ntime.tv_sec + SAMPLEINTVL;
+ break;
+
+ case TSP_QUIT:
+ doquit(msg); /* become a slave */
+ break;
+
+ case TSP_LOOP:
+ if (!fromnet || fromnet->status != MASTER
+ || !strcmp(msg->tsp_name, hostname))
+ break;
+ /*
+ * We should not have received this from a net
+ * we are master on. There must be two masters.
+ */
+ htp = addmach(msg->tsp_name, &from,fromnet);
+ to.tsp_type = TSP_QUIT;
+ (void)strcpy(to.tsp_name, hostname);
+ answer = acksend(&to, &htp->addr, htp->name,
+ TSP_ACK, 0, 1);
+ if (!answer) {
+ syslog(LOG_WARNING,
+ "loop breakage: no reply from %s=%s to QUIT",
+ htp->name, inet_ntoa(htp->addr.sin_addr));
+ (void)remmach(htp);
+ }
+
+ case TSP_TEST:
+ if (trace) {
+ fprintf(fd,
+ "\tnets = %d, masters = %d, slaves = %d, ignored = %d\n",
+ nnets, nmasternets, nslavenets, nignorednets);
+ setstatus();
+ }
+ pollingtime = 0;
+ polls = POLLRATE-1;
+ break;
+
+ default:
+ if (trace) {
+ fprintf(fd, "garbage message: ");
+ print(msg, &from);
+ }
+ break;
+ }
+ }
+ goto loop;
+}
+
+
+/*
+ * change the system date on the master
+ */
+static void
+mchgdate(msg)
+ struct tsp *msg;
+{
+ char tname[MAXHOSTNAMELEN];
+ char olddate[32];
+ struct timeval otime, ntime, tmptv;
+
+ (void)strcpy(tname, msg->tsp_name);
+
+ xmit(TSP_DATEACK, msg->tsp_seq, &from);
+
+ (void)strcpy(olddate, date());
+
+ /* adjust time for residence on the queue */
+ (void)gettimeofday(&otime, 0);
+ adj_msg_time(msg,&otime);
+
+ tmptv.tv_sec = msg->tsp_time.tv_sec;
+ tmptv.tv_usec = msg->tsp_time.tv_usec;
+ timevalsub(&ntime, &tmptv, &otime);
+ if (ntime.tv_sec < MAXADJ && ntime.tv_sec > -MAXADJ) {
+ /*
+ * do not change the clock if we can adjust it
+ */
+ dictate = 3;
+ synch(tvtomsround(ntime));
+ } else {
+ logwtmp("|", "date", "");
+ (void)settimeofday(&tmptv, 0);
+ logwtmp("{", "date", "");
+ spreadtime();
+ }
+
+ syslog(LOG_NOTICE, "date changed by %s from %s",
+ tname, olddate);
+}
+
+
+/*
+ * synchronize all of the slaves
+ */
+void
+synch(mydelta)
+ long mydelta;
+{
+ struct hosttbl *htp;
+ int measure_status;
+ struct timeval check, stop, wait;
+
+ if (slvcount > 0) {
+ if (trace)
+ fprintf(fd, "measurements starting at %s\n", date());
+ (void)gettimeofday(&check, 0);
+ for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
+ if (htp->noanswer != 0) {
+ measure_status = measure(500, 100,
+ htp->name,
+ &htp->addr,0);
+ } else {
+ measure_status = measure(3000, 100,
+ htp->name,
+ &htp->addr,0);
+ }
+ if (measure_status != GOOD) {
+ /* The slave did not respond. We have
+ * just wasted lots of time on it.
+ */
+ htp->delta = HOSTDOWN;
+ if (++htp->noanswer >= LOSTHOST) {
+ if (trace) {
+ fprintf(fd,
+ "purging %s for not answering ICMP\n",
+ htp->name);
+ (void)fflush(fd);
+ }
+ htp = remmach(htp);
+ }
+ } else {
+ htp->delta = measure_delta;
+ }
+ (void)gettimeofday(&stop, 0);
+ timevalsub(&stop, &stop, &check);
+ if (stop.tv_sec >= 1) {
+ if (trace)
+ (void)fflush(fd);
+ /*
+ * ack messages periodically
+ */
+ wait.tv_sec = 0;
+ wait.tv_usec = 0;
+ if (0 != readmsg(TSP_TRACEON,ANYADDR,
+ &wait,0))
+ traceon();
+ (void)gettimeofday(&check, 0);
+ }
+ }
+ if (trace)
+ fprintf(fd, "measurements finished at %s\n", date());
+ }
+ if (!(status & SLAVE)) {
+ if (!dictate) {
+ mydelta = networkdelta();
+ } else {
+ dictate--;
+ }
+ }
+ if (trace && (mydelta != 0 || (status & SLAVE)))
+ fprintf(fd,"local correction of %ld ms.\n", mydelta);
+ correct(mydelta);
+}
+
+/*
+ * sends the time to each slave after the master
+ * has received the command to set the network time
+ */
+void
+spreadtime()
+{
+ struct hosttbl *htp;
+ struct tsp to;
+ struct tsp *answer;
+ struct timeval tmptv;
+
+/* Do not listen to the consensus after forcing the time. This is because
+ * the consensus takes a while to reach the time we are dictating.
+ */
+ dictate = 2;
+ for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
+ to.tsp_type = TSP_SETTIME;
+ (void)strcpy(to.tsp_name, hostname);
+ (void)gettimeofday(&tmptv, 0);
+ to.tsp_time.tv_sec = tmptv.tv_sec;
+ to.tsp_time.tv_usec = tmptv.tv_usec;
+ answer = acksend(&to, &htp->addr, htp->name,
+ TSP_ACK, 0, htp->noanswer);
+ if (answer == 0) {
+ /* We client does not respond, then we have
+ * just wasted lots of time on it.
+ */
+ syslog(LOG_WARNING,
+ "no reply to SETTIME from %s", htp->name);
+ if (++htp->noanswer >= LOSTHOST) {
+ if (trace) {
+ fprintf(fd,
+ "purging %s for not answering",
+ htp->name);
+ (void)fflush(fd);
+ }
+ htp = remmach(htp);
+ }
+ }
+ }
+}
+
+void
+prthp(delta)
+ clock_t delta;
+{
+ static time_t next_time;
+ time_t this_time;
+ struct tms tm;
+ struct hosttbl *htp;
+ int length, l;
+ int i;
+
+ if (!fd) /* quit if tracing already off */
+ return;
+
+ this_time = times(&tm);
+ if ((time_t)(this_time + delta) < next_time)
+ return;
+ next_time = this_time + CLK_TCK;
+
+ fprintf(fd, "host table: %d entries at %s\n", slvcount, date());
+ htp = self.l_fwd;
+ length = 1;
+ for (i = 1; i <= slvcount; i++, htp = htp->l_fwd) {
+ l = strlen(htp->name) + 1;
+ if (length+l >= 80) {
+ fprintf(fd, "\n");
+ length = 0;
+ }
+ length += l;
+ fprintf(fd, " %s", htp->name);
+ }
+ fprintf(fd, "\n");
+}
+
+
+static struct hosttbl *newhost_hash;
+static struct hosttbl *lasthfree = &hosttbl[0];
+
+
+struct hosttbl * /* answer or 0 */
+findhost(name)
+ char *name;
+{
+ int i, j;
+ struct hosttbl *htp;
+ char *p;
+
+ j= 0;
+ for (p = name, i = 0; i < 8 && *p != '\0'; i++, p++)
+ j = (j << 2) ^ *p;
+ newhost_hash = &hosttbl[j % NHOSTS];
+
+ htp = newhost_hash;
+ if (htp->name[0] == '\0')
+ return(0);
+ do {
+ if (!strcmp(name, htp->name))
+ return(htp);
+ htp = htp->h_fwd;
+ } while (htp != newhost_hash);
+ return(0);
+}
+
+/*
+ * add a host to the list of controlled machines if not already there
+ */
+struct hosttbl *
+addmach(name, addr, ntp)
+ char *name;
+ struct sockaddr_in *addr;
+ struct netinfo *ntp;
+{
+ struct hosttbl *ret, *p, *b, *f;
+
+ ret = findhost(name);
+ if (ret == 0) {
+ if (slvcount >= NHOSTS) {
+ if (trace) {
+ fprintf(fd, "no more slots in host table\n");
+ prthp(CLK_TCK);
+ }
+ syslog(LOG_ERR, "no more slots in host table");
+ Mflag = 0;
+ longjmp(jmpenv, 2); /* give up and be a slave */
+ }
+
+ /* if our home hash slot is occupied, find a free entry
+ * in the hash table
+ */
+ if (newhost_hash->name[0] != '\0') {
+ do {
+ ret = lasthfree;
+ if (++lasthfree > &hosttbl[NHOSTS])
+ lasthfree = &hosttbl[1];
+ } while (ret->name[0] != '\0');
+
+ if (!newhost_hash->head) {
+ /* Move an interloper using our home. Use
+ * scratch pointers in case the new head is
+ * pointing to itself.
+ */
+ f = newhost_hash->h_fwd;
+ b = newhost_hash->h_bak;
+ f->h_bak = ret;
+ b->h_fwd = ret;
+ f = newhost_hash->l_fwd;
+ b = newhost_hash->l_bak;
+ f->l_bak = ret;
+ b->l_fwd = ret;
+ bcopy(newhost_hash,ret,sizeof(*ret));
+ ret = newhost_hash;
+ ret->head = 1;
+ ret->h_fwd = ret;
+ ret->h_bak = ret;
+ } else {
+ /* link to an existing chain in our home
+ */
+ ret->head = 0;
+ p = newhost_hash->h_bak;
+ ret->h_fwd = newhost_hash;
+ ret->h_bak = p;
+ p->h_fwd = ret;
+ newhost_hash->h_bak = ret;
+ }
+ } else {
+ ret = newhost_hash;
+ ret->head = 1;
+ ret->h_fwd = ret;
+ ret->h_bak = ret;
+ }
+ ret->addr = *addr;
+ ret->ntp = ntp;
+ (void)strncpy(ret->name, name, sizeof(ret->name));
+ ret->good = good_host_name(name);
+ ret->l_fwd = &self;
+ ret->l_bak = self.l_bak;
+ self.l_bak->l_fwd = ret;
+ self.l_bak = ret;
+ slvcount++;
+
+ ret->noanswer = 0;
+ ret->need_set = 1;
+
+ } else {
+ ret->noanswer = (ret->noanswer != 0);
+ }
+
+ /* need to clear sequence number anyhow */
+ ret->seq = 0;
+ return(ret);
+}
+
+/*
+ * remove the machine with the given index in the host table.
+ */
+struct hosttbl *
+remmach(htp)
+ struct hosttbl *htp;
+{
+ struct hosttbl *lprv, *hnxt, *f, *b;
+
+ if (trace)
+ fprintf(fd, "remove %s\n", htp->name);
+
+ /* get out of the lists */
+ htp->l_fwd->l_bak = lprv = htp->l_bak;
+ htp->l_bak->l_fwd = htp->l_fwd;
+ htp->h_fwd->h_bak = htp->h_bak;
+ htp->h_bak->h_fwd = hnxt = htp->h_fwd;
+
+ /* If we are in the home slot, pull up the chain */
+ if (htp->head && hnxt != htp) {
+ if (lprv == hnxt)
+ lprv = htp;
+
+ /* Use scratch pointers in case the new head is pointing to
+ * itself.
+ */
+ f = hnxt->h_fwd;
+ b = hnxt->h_bak;
+ f->h_bak = htp;
+ b->h_fwd = htp;
+ f = hnxt->l_fwd;
+ b = hnxt->l_bak;
+ f->l_bak = htp;
+ b->l_fwd = htp;
+ hnxt->head = 1;
+ bcopy(hnxt, htp, sizeof(*htp));
+ lasthfree = hnxt;
+ } else {
+ lasthfree = htp;
+ }
+
+ lasthfree->name[0] = '\0';
+ lasthfree->h_fwd = 0;
+ lasthfree->l_fwd = 0;
+ slvcount--;
+
+ return lprv;
+}
+
+
+/*
+ * Remove all the machines from the host table that exist on the given
+ * network. This is called when a master transitions to a slave on a
+ * given network.
+ */
+void
+rmnetmachs(ntp)
+ struct netinfo *ntp;
+{
+ struct hosttbl *htp;
+
+ if (trace)
+ prthp(CLK_TCK);
+ for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
+ if (ntp == htp->ntp)
+ htp = remmach(htp);
+ }
+ if (trace)
+ prthp(CLK_TCK);
+}
+
+void
+masterup(net)
+ struct netinfo *net;
+{
+ xmit(TSP_MASTERUP, 0, &net->dest_addr);
+
+ /*
+ * Do not tell new slaves our time for a while. This ensures
+ * we do not tell them to start using our time, before we have
+ * found a good master.
+ */
+ (void)gettimeofday(&net->slvwait, 0);
+}
+
+void
+newslave(msg)
+ struct tsp *msg;
+{
+ struct hosttbl *htp;
+ struct tsp *answer, to;
+ struct timeval now, tmptv;
+
+ if (!fromnet || fromnet->status != MASTER)
+ return;
+
+ htp = addmach(msg->tsp_name, &from,fromnet);
+ htp->seq = msg->tsp_seq;
+ if (trace)
+ prthp(0);
+
+ /*
+ * If we are stable, send our time to the slave.
+ * Do not go crazy if the date has been changed.
+ */
+ (void)gettimeofday(&now, 0);
+ if (now.tv_sec >= fromnet->slvwait.tv_sec+3
+ || now.tv_sec < fromnet->slvwait.tv_sec) {
+ to.tsp_type = TSP_SETTIME;
+ (void)strcpy(to.tsp_name, hostname);
+ (void)gettimeofday(&tmptv, 0);
+ to.tsp_time.tv_sec = tmptv.tv_sec;
+ to.tsp_time.tv_usec = tmptv.tv_usec;
+ answer = acksend(&to, &htp->addr,
+ htp->name, TSP_ACK,
+ 0, htp->noanswer);
+ if (answer) {
+ htp->need_set = 0;
+ } else {
+ syslog(LOG_WARNING,
+ "no reply to initial SETTIME from %s",
+ htp->name);
+ htp->noanswer = LOSTHOST;
+ }
+ }
+}
+
+
+/*
+ * react to a TSP_QUIT:
+ */
+void
+doquit(msg)
+ struct tsp *msg;
+{
+ if (fromnet->status == MASTER) {
+ if (!good_host_name(msg->tsp_name)) {
+ if (fromnet->quit_count <= 0) {
+ syslog(LOG_NOTICE,"untrusted %s told us QUIT",
+ msg->tsp_name);
+ suppress(&from, msg->tsp_name, fromnet);
+ fromnet->quit_count = 1;
+ return;
+ }
+ syslog(LOG_NOTICE, "untrusted %s told us QUIT twice",
+ msg->tsp_name);
+ fromnet->quit_count = 2;
+ fromnet->status = NOMASTER;
+ } else {
+ fromnet->status = SLAVE;
+ }
+ rmnetmachs(fromnet);
+ longjmp(jmpenv, 2); /* give up and be a slave */
+
+ } else {
+ if (!good_host_name(msg->tsp_name)) {
+ syslog(LOG_NOTICE, "untrusted %s told us QUIT",
+ msg->tsp_name);
+ fromnet->quit_count = 2;
+ }
+ }
+}
+
+void
+traceon()
+{
+ if (!fd) {
+ fd = fopen(_PATH_TIMEDLOG, "w");
+ if (!fd) {
+ trace = 0;
+ return;
+ }
+ fprintf(fd,"Tracing started at %s\n", date());
+ }
+ trace = 1;
+ get_goodgroup(1);
+ setstatus();
+ prthp(CLK_TCK);
+}
+
+
+void
+traceoff(msg)
+ char *msg;
+{
+ get_goodgroup(1);
+ setstatus();
+ prthp(CLK_TCK);
+ if (trace) {
+ fprintf(fd, msg, date());
+ (void)fclose(fd);
+ fd = 0;
+ }
+#ifdef GPROF
+ moncontrol(0);
+ _mcleanup();
+ moncontrol(1);
+#endif
+ trace = OFF;
+}
diff --git a/usr.sbin/timed/timed/measure.c b/usr.sbin/timed/timed/measure.c
new file mode 100644
index 0000000..3856a34
--- /dev/null
+++ b/usr.sbin/timed/timed/measure.c
@@ -0,0 +1,346 @@
+/*-
+ * 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.
+ * 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
+#if 0
+static char sccsid[] = "@(#)measure.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "globals.h"
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+
+#define MSEC_DAY (SECDAY*1000)
+
+#define PACKET_IN 1024
+
+#define MSGS 5 /* timestamps to average */
+#define TRIALS 10 /* max # of timestamps sent */
+
+extern int sock_raw;
+
+int measure_delta;
+
+static n_short seqno = 0;
+
+/*
+ * Measures the differences between machines' clocks using
+ * ICMP timestamp messages.
+ */
+int /* status val defined in globals.h */
+measure(maxmsec, wmsec, hname, addr, print)
+ u_long maxmsec; /* wait this many msec at most */
+ u_long wmsec; /* msec to wait for an answer */
+ char *hname;
+ struct sockaddr_in *addr;
+ int print; /* print complaints on stderr */
+{
+ int length;
+ int measure_status;
+ int rcvcount, trials;
+ int cc, count;
+ fd_set ready;
+ long sendtime, recvtime, histime1, histime2;
+ long idelta, odelta, total;
+ long min_idelta, min_odelta;
+ struct timeval tdone, tcur, ttrans, twait, tout;
+ u_char packet[PACKET_IN], opacket[64];
+ register struct icmp *icp = (struct icmp *) packet;
+ register struct icmp *oicp = (struct icmp *) opacket;
+ struct ip *ip = (struct ip *) packet;
+
+ min_idelta = min_odelta = 0x7fffffff;
+ measure_status = HOSTDOWN;
+ measure_delta = HOSTDOWN;
+ errno = 0;
+
+ /* open raw socket used to measure time differences */
+ if (sock_raw < 0) {
+ sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+ if (sock_raw < 0) {
+ syslog(LOG_ERR, "opening raw socket: %m");
+ goto quit;
+ }
+ }
+
+
+ /*
+ * empty the icmp input queue
+ */
+ FD_ZERO(&ready);
+ for (;;) {
+ tout.tv_sec = tout.tv_usec = 0;
+ FD_SET(sock_raw, &ready);
+ if (select(sock_raw+1, &ready, 0,0, &tout)) {
+ length = sizeof(struct sockaddr_in);
+ cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
+ 0,&length);
+ if (cc < 0)
+ goto quit;
+ continue;
+ }
+ break;
+ }
+
+ /*
+ * Choose the smallest transmission time in each of the two
+ * directions. Use these two latter quantities to compute the delta
+ * between the two clocks.
+ */
+
+ oicp->icmp_type = ICMP_TSTAMP;
+ oicp->icmp_code = 0;
+ oicp->icmp_id = getpid();
+ oicp->icmp_rtime = 0;
+ oicp->icmp_ttime = 0;
+ oicp->icmp_seq = seqno;
+
+ FD_ZERO(&ready);
+
+ (void)gettimeofday(&tdone, 0);
+ mstotvround(&tout, maxmsec);
+ timevaladd(&tdone, &tout); /* when we give up */
+
+ mstotvround(&twait, wmsec);
+
+ rcvcount = 0;
+ trials = 0;
+ while (rcvcount < MSGS) {
+ (void)gettimeofday(&tcur, 0);
+
+ /*
+ * keep sending until we have sent the max
+ */
+ if (trials < TRIALS) {
+ trials++;
+ oicp->icmp_otime = htonl((tcur.tv_sec % SECDAY) * 1000
+ + tcur.tv_usec / 1000);
+ oicp->icmp_cksum = 0;
+ oicp->icmp_cksum = in_cksum((u_short*)oicp,
+ sizeof(*oicp));
+
+ count = sendto(sock_raw, opacket, sizeof(*oicp), 0,
+ (struct sockaddr*)addr,
+ sizeof(struct sockaddr));
+ if (count < 0) {
+ if (measure_status == HOSTDOWN)
+ measure_status = UNREACHABLE;
+ goto quit;
+ }
+ ++oicp->icmp_seq;
+
+ ttrans = tcur;
+ timevaladd(&ttrans, &twait);
+ } else {
+ ttrans = tdone;
+ }
+
+ while (rcvcount < trials) {
+ timevalsub(&tout, &ttrans, &tcur);
+ if (tout.tv_sec < 0)
+ tout.tv_sec = 0;
+
+ FD_SET(sock_raw, &ready);
+ count = select(sock_raw+1, &ready, (fd_set *)0,
+ (fd_set *)0, &tout);
+ (void)gettimeofday(&tcur, (struct timezone *)0);
+ if (count <= 0)
+ break;
+
+ length = sizeof(struct sockaddr_in);
+ cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
+ 0,&length);
+ if (cc < 0)
+ goto quit;
+
+ /*
+ * got something. See if it is ours
+ */
+ icp = (struct icmp *)(packet + (ip->ip_hl << 2));
+ if (cc < sizeof(*ip)
+ || icp->icmp_type != ICMP_TSTAMPREPLY
+ || icp->icmp_id != oicp->icmp_id
+ || icp->icmp_seq < seqno
+ || icp->icmp_seq >= oicp->icmp_seq)
+ continue;
+
+
+ sendtime = ntohl(icp->icmp_otime);
+ recvtime = ((tcur.tv_sec % SECDAY) * 1000 +
+ tcur.tv_usec / 1000);
+
+ total = recvtime-sendtime;
+ if (total < 0) /* do not hassle midnight */
+ continue;
+
+ rcvcount++;
+ histime1 = ntohl(icp->icmp_rtime);
+ histime2 = ntohl(icp->icmp_ttime);
+ /*
+ * a host using a time format different from
+ * msec. since midnight UT (as per RFC792) should
+ * set the high order bit of the 32-bit time
+ * value it transmits.
+ */
+ if ((histime1 & 0x80000000) != 0) {
+ measure_status = NONSTDTIME;
+ goto quit;
+ }
+ measure_status = GOOD;
+
+ idelta = recvtime-histime2;
+ odelta = histime1-sendtime;
+
+ /* do not be confused by midnight */
+ if (idelta < -MSEC_DAY/2) idelta += MSEC_DAY;
+ else if (idelta > MSEC_DAY/2) idelta -= MSEC_DAY;
+
+ if (odelta < -MSEC_DAY/2) odelta += MSEC_DAY;
+ else if (odelta > MSEC_DAY/2) odelta -= MSEC_DAY;
+
+ /* save the quantization error so that we can get a
+ * measurement finer than our system clock.
+ */
+ if (total < MIN_ROUND) {
+ measure_delta = (odelta - idelta)/2;
+ goto quit;
+ }
+
+ if (idelta < min_idelta)
+ min_idelta = idelta;
+ if (odelta < min_odelta)
+ min_odelta = odelta;
+
+ measure_delta = (min_odelta - min_idelta)/2;
+ }
+
+ if (tcur.tv_sec > tdone.tv_sec
+ || (tcur.tv_sec == tdone.tv_sec
+ && tcur.tv_usec >= tdone.tv_usec))
+ break;
+ }
+
+quit:
+ seqno += TRIALS; /* allocate our sequence numbers */
+
+ /*
+ * If no answer is received for TRIALS consecutive times,
+ * the machine is assumed to be down
+ */
+ if (measure_status == GOOD) {
+ if (trace) {
+ fprintf(fd,
+ "measured delta %4d, %d trials to %-15s %s\n",
+ measure_delta, trials,
+ inet_ntoa(addr->sin_addr), hname);
+ }
+ } else if (print) {
+ if (errno != 0)
+ warn("measure %s", hname);
+ } else {
+ if (errno != 0) {
+ syslog(LOG_ERR, "measure %s: %m", hname);
+ } else {
+ syslog(LOG_ERR, "measure: %s did not respond", hname);
+ }
+ if (trace) {
+ fprintf(fd,
+ "measure: %s failed after %d trials\n",
+ hname, trials);
+ (void)fflush(fd);
+ }
+ }
+
+ return(measure_status);
+}
+
+
+
+
+
+/*
+ * round a number of milliseconds into a struct timeval
+ */
+void
+mstotvround(res, x)
+ struct timeval *res;
+ long x;
+{
+ if (x < 0)
+ x = -((-x + 3)/5);
+ else
+ x = (x+3)/5;
+ x *= 5;
+ res->tv_sec = x/1000;
+ res->tv_usec = (x-res->tv_sec*1000)*1000;
+ if (res->tv_usec < 0) {
+ res->tv_usec += 1000000;
+ res->tv_sec--;
+ }
+}
+
+void
+timevaladd(tv1, tv2)
+ struct timeval *tv1, *tv2;
+{
+ tv1->tv_sec += tv2->tv_sec;
+ tv1->tv_usec += tv2->tv_usec;
+ if (tv1->tv_usec >= 1000000) {
+ tv1->tv_sec++;
+ tv1->tv_usec -= 1000000;
+ }
+ if (tv1->tv_usec < 0) {
+ tv1->tv_sec--;
+ tv1->tv_usec += 1000000;
+ }
+}
+
+void
+timevalsub(res, tv1, tv2)
+ struct timeval *res, *tv1, *tv2;
+{
+ res->tv_sec = tv1->tv_sec - tv2->tv_sec;
+ res->tv_usec = tv1->tv_usec - tv2->tv_usec;
+ if (res->tv_usec >= 1000000) {
+ res->tv_sec++;
+ res->tv_usec -= 1000000;
+ }
+ if (res->tv_usec < 0) {
+ res->tv_sec--;
+ res->tv_usec += 1000000;
+ }
+}
diff --git a/usr.sbin/timed/timed/networkdelta.c b/usr.sbin/timed/timed/networkdelta.c
new file mode 100644
index 0000000..5919d62
--- /dev/null
+++ b/usr.sbin/timed/timed/networkdelta.c
@@ -0,0 +1,264 @@
+/*-
+ * 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.
+ * 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
+#if 0
+static char sccsid[] = "@(#)networkdelta.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "globals.h"
+
+static long median(float, float *, long *, long *, unsigned int);
+
+/*
+ * Compute a corrected date.
+ * Compute the median of the reasonable differences. First compute
+ * the median of all authorized differences, and then compute the
+ * median of all differences that are reasonably close to the first
+ * median.
+ *
+ * This differs from the original BSD implementation, which looked for
+ * the largest group of machines with essentially the same date.
+ * That assumed that machines with bad clocks would be uniformly
+ * distributed. Unfortunately, in real life networks, the distribution
+ * of machines is not uniform among models of machines, and the
+ * distribution of errors in clocks tends to be quite consistent
+ * for a given model. In other words, all model VI Supre Servres
+ * from GoFast Inc. tend to have about the same error.
+ * The original BSD implementation would chose the clock of the
+ * most common model, and discard all others.
+ *
+ * Therefore, get best we can do is to try to average over all
+ * of the machines in the network, while discarding "obviously"
+ * bad values.
+ */
+long
+networkdelta()
+{
+ struct hosttbl *htp;
+ long med;
+ long lodelta, hidelta;
+ long logood, higood;
+ long x[NHOSTS];
+ long *xp;
+ int numdelta;
+ float eps;
+
+ /*
+ * compute the median of the good values
+ */
+ med = 0;
+ numdelta = 1;
+ xp = &x[0];
+ *xp = 0; /* account for ourself */
+ for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
+ if (htp->good
+ && htp->noanswer == 0
+ && htp->delta != HOSTDOWN) {
+ med += htp->delta;
+ numdelta++;
+ *++xp = htp->delta;
+ }
+ }
+
+ /*
+ * If we are the only trusted time keeper, then do not change our
+ * clock. There may be another time keeping service active.
+ */
+ if (numdelta == 1)
+ return 0;
+
+ med /= numdelta;
+ eps = med - x[0];
+ if (trace)
+ fprintf(fd, "median of %d values starting at %ld is about ",
+ numdelta, med);
+ med = median(med, &eps, &x[0], xp+1, VALID_RANGE);
+
+ /*
+ * compute the median of all values near the good median
+ */
+ hidelta = med + GOOD_RANGE;
+ lodelta = med - GOOD_RANGE;
+ higood = med + VGOOD_RANGE;
+ logood = med - VGOOD_RANGE;
+ xp = &x[0];
+ htp = &self;
+ do {
+ if (htp->noanswer == 0
+ && htp->delta >= lodelta
+ && htp->delta <= hidelta
+ && (htp->good
+ || (htp->delta >= logood
+ && htp->delta <= higood))) {
+ *xp++ = htp->delta;
+ }
+ } while (&self != (htp = htp->l_fwd));
+
+ if (xp == &x[0]) {
+ if (trace)
+ fprintf(fd, "nothing close to median %ld\n", med);
+ return med;
+ }
+
+ if (xp == &x[1]) {
+ if (trace)
+ fprintf(fd, "only value near median is %ld\n", x[0]);
+ return x[0];
+ }
+
+ if (trace)
+ fprintf(fd, "median of %d values starting at %ld is ",
+ xp-&x[0], med);
+ return median(med, &eps, &x[0], xp, 1);
+}
+
+
+/*
+ * compute the median of an array of signed integers, using the idea
+ * in <<Numerical Recipes>>.
+ */
+static long
+median(float a, float *eps_ptr, long *x, long *xlim, unsigned int gnuf)
+ /* float a; */ /* initial guess for the median */
+ /* float *eps_ptr; */ /* spacing near the median */
+ /* long *x, *xlim; */ /* the data */
+ /* unsigned int gnuf; */ /* good enough estimate */
+{
+ long *xptr;
+ float ap = LONG_MAX; /* bounds on the median */
+ float am = -LONG_MAX;
+ float aa;
+ int npts; /* # of points above & below guess */
+ float xp; /* closet point above the guess */
+ float xm; /* closet point below the guess */
+ float eps;
+ float dum, sum, sumx;
+ int pass;
+#define AMP 1.5 /* smoothing constants */
+#define AFAC 1.5
+
+ eps = *eps_ptr;
+ if (eps < 1.0) {
+ eps = -eps;
+ if (eps < 1.0)
+ eps = 1.0;
+ }
+
+ for (pass = 1; ; pass++) { /* loop over the data */
+ sum = 0.0;
+ sumx = 0.0;
+ npts = 0;
+ xp = LONG_MAX;
+ xm = -LONG_MAX;
+
+ for (xptr = x; xptr != xlim; xptr++) {
+ float xx = *xptr;
+
+ dum = xx - a;
+ if (dum != 0.0) { /* avoid dividing by 0 */
+ if (dum > 0.0) {
+ npts++;
+ if (xx < xp)
+ xp = xx;
+ } else {
+ npts--;
+ if (xx > xm)
+ xm = xx;
+ dum = -dum;
+ }
+ dum = 1.0/(eps + dum);
+ sum += dum;
+ sumx += xx * dum;
+ }
+ }
+
+ if (ap-am < gnuf || sum == 0) {
+ if (trace)
+ fprintf(fd,
+ "%ld in %d passes; early out balance=%d\n",
+ (long)a, pass, npts);
+ return a; /* guess was good enough */
+ }
+
+ aa = (sumx/sum-a)*AMP;
+ if (npts >= 2) { /* guess was too low */
+ am = a;
+ aa = xp + max(0.0, aa);
+ if (aa > ap)
+ aa = (a + ap)/2;
+
+ } else if (npts <= -2) { /* guess was two high */
+ ap = a;
+ aa = xm + min(0.0, aa);
+ if (aa < am)
+ aa = (a + am)/2;
+
+ } else {
+ break; /* got it */
+ }
+
+ if (a == aa) {
+ if (trace)
+ fprintf(fd,
+ "%ld in %d passes; force out balance=%d\n",
+ (long)a, pass, npts);
+ return a;
+ }
+ eps = AFAC*abs(aa - a);
+ *eps_ptr = eps;
+ a = aa;
+ }
+
+ if (((x - xlim) % 2) != 0) { /* even number of points? */
+ if (npts == 0) /* yes, return an average */
+ a = (xp+xm)/2;
+ else if (npts > 0)
+ a = (a+xp)/2;
+ else
+ a = (xm+a)/2;
+
+ } else if (npts != 0) { /* odd number of points */
+ if (npts > 0)
+ a = xp;
+ else
+ a = xm;
+ }
+
+ if (trace)
+ fprintf(fd, "%ld in %d passes\n", (long)a, pass);
+ return a;
+}
diff --git a/usr.sbin/timed/timed/pathnames.h b/usr.sbin/timed/timed/pathnames.h
new file mode 100644
index 0000000..e5fdcde
--- /dev/null
+++ b/usr.sbin/timed/timed/pathnames.h
@@ -0,0 +1,41 @@
+/*-
+ * 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.
+ * 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.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ *
+ * $FreeBSD$
+ */
+
+#include <paths.h>
+
+#define _PATH_MASTERLOG "/var/log/timed.masterlog"
+#define _PATH_TIMEDLOG "/var/log/timed.log"
diff --git a/usr.sbin/timed/timed/readmsg.c b/usr.sbin/timed/timed/readmsg.c
new file mode 100644
index 0000000..74a6f5f
--- /dev/null
+++ b/usr.sbin/timed/timed/readmsg.c
@@ -0,0 +1,513 @@
+/*-
+ * 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.
+ * 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
+#if 0
+static char sccsid[] = "@(#)readmsg.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "globals.h"
+
+extern char *tsptype[];
+
+/*
+ * LOOKAT checks if the message is of the requested type and comes from
+ * the right machine, returning 1 in case of affirmative answer
+ */
+#define LOOKAT(msg, mtype, mfrom, netp, froms) \
+ (((mtype) == TSP_ANY || (mtype) == (msg).tsp_type) && \
+ ((mfrom) == 0 || !strcmp((mfrom), (msg).tsp_name)) && \
+ ((netp) == 0 || \
+ ((netp)->mask & (froms).sin_addr.s_addr) == (netp)->net.s_addr))
+
+struct timeval rtime, rwait, rtout;
+struct tsp msgin;
+static struct tsplist {
+ struct tsp info;
+ struct timeval when;
+ struct sockaddr_in addr;
+ struct tsplist *p;
+} msgslist;
+struct sockaddr_in from;
+struct netinfo *fromnet;
+struct timeval from_when;
+
+/*
+ * `readmsg' returns message `type' sent by `machfrom' if it finds it
+ * either in the receive queue, or in a linked list of previously received
+ * messages that it maintains.
+ * Otherwise it waits to see if the appropriate message arrives within
+ * `intvl' seconds. If not, it returns NULL.
+ */
+
+struct tsp *
+readmsg(type, machfrom, intvl, netfrom)
+ int type;
+ char *machfrom;
+ struct timeval *intvl;
+ struct netinfo *netfrom;
+{
+ int length;
+ fd_set ready;
+ static struct tsplist *head = &msgslist;
+ static struct tsplist *tail = &msgslist;
+ static int msgcnt = 0;
+ struct tsplist *prev;
+ register struct netinfo *ntp;
+ register struct tsplist *ptr;
+ ssize_t n;
+
+ if (trace) {
+ fprintf(fd, "readmsg: looking for %s from %s, %s\n",
+ tsptype[type], machfrom == NULL ? "ANY" : machfrom,
+ netfrom == NULL ? "ANYNET" : inet_ntoa(netfrom->net));
+ if (head->p != 0) {
+ length = 1;
+ for (ptr = head->p; ptr != 0; ptr = ptr->p) {
+ /* do not repeat the hundreds of messages */
+ if (++length > 3) {
+ if (ptr == tail) {
+ fprintf(fd,"\t ...%d skipped\n",
+ length);
+ } else {
+ continue;
+ }
+ }
+ fprintf(fd, length > 1 ? "\t" : "queue:\t");
+ print(&ptr->info, &ptr->addr);
+ }
+ }
+ }
+
+ ptr = head->p;
+ prev = head;
+
+ /*
+ * Look for the requested message scanning through the
+ * linked list. If found, return it and free the space
+ */
+
+ while (ptr != NULL) {
+ if (LOOKAT(ptr->info, type, machfrom, netfrom, ptr->addr)) {
+again:
+ msgin = ptr->info;
+ from = ptr->addr;
+ from_when = ptr->when;
+ prev->p = ptr->p;
+ if (ptr == tail)
+ tail = prev;
+ free((char *)ptr);
+ fromnet = NULL;
+ if (netfrom == NULL)
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ if ((ntp->mask & from.sin_addr.s_addr) ==
+ ntp->net.s_addr) {
+ fromnet = ntp;
+ break;
+ }
+ }
+ else
+ fromnet = netfrom;
+ if (trace) {
+ fprintf(fd, "readmsg: found ");
+ print(&msgin, &from);
+ }
+
+/* The protocol can get far behind. When it does, it gets
+ * hopelessly confused. So delete duplicate messages.
+ */
+ for (ptr = prev; (ptr = ptr->p) != NULL; prev = ptr) {
+ if (ptr->addr.sin_addr.s_addr
+ == from.sin_addr.s_addr
+ && ptr->info.tsp_type == msgin.tsp_type) {
+ if (trace)
+ fprintf(fd, "\tdup ");
+ goto again;
+ }
+ }
+ msgcnt--;
+ return(&msgin);
+ } else {
+ prev = ptr;
+ ptr = ptr->p;
+ }
+ }
+
+ /*
+ * If the message was not in the linked list, it may still be
+ * coming from the network. Set the timer and wait
+ * on a select to read the next incoming message: if it is the
+ * right one, return it, otherwise insert it in the linked list.
+ */
+
+ (void)gettimeofday(&rtout, 0);
+ timevaladd(&rtout, intvl);
+ FD_ZERO(&ready);
+ for (;;) {
+ (void)gettimeofday(&rtime, 0);
+ timevalsub(&rwait, &rtout, &rtime);
+ if (rwait.tv_sec < 0)
+ rwait.tv_sec = rwait.tv_usec = 0;
+ else if (rwait.tv_sec == 0
+ && rwait.tv_usec < 1000000/CLK_TCK)
+ rwait.tv_usec = 1000000/CLK_TCK;
+
+ if (trace) {
+ fprintf(fd, "readmsg: wait %ld.%6ld at %s\n",
+ rwait.tv_sec, rwait.tv_usec, date());
+ /* Notice a full disk, as we flush trace info.
+ * It is better to flush periodically than at
+ * every line because the tracing consists of bursts
+ * of many lines. Without care, tracing slows
+ * down the code enough to break the protocol.
+ */
+ if (rwait.tv_sec != 0
+ && EOF == fflush(fd))
+ traceoff("Tracing ended for cause at %s\n");
+ }
+
+ FD_SET(sock, &ready);
+ if (!select(sock+1, &ready, (fd_set *)0, (fd_set *)0,
+ &rwait)) {
+ if (rwait.tv_sec == 0 && rwait.tv_usec == 0)
+ return(0);
+ continue;
+ }
+ length = sizeof(from);
+ if ((n = recvfrom(sock, (char *)&msgin, sizeof(struct tsp), 0,
+ (struct sockaddr*)&from, &length)) < 0) {
+ syslog(LOG_ERR, "recvfrom: %m");
+ exit(1);
+ }
+ /*
+ * The 4.3BSD protocol spec had a 32-byte tsp_name field, and
+ * this is still OS-dependent. Demand that the packet is at
+ * least long enough to hold a 4.3BSD packet.
+ */
+ if (n < (ssize_t)(sizeof(struct tsp) - MAXHOSTNAMELEN + 32)) {
+ syslog(LOG_NOTICE,
+ "short packet (%u/%u bytes) from %s",
+ n, sizeof(struct tsp) - MAXHOSTNAMELEN + 32,
+ inet_ntoa(from.sin_addr));
+ continue;
+ }
+ (void)gettimeofday(&from_when, (struct timezone *)0);
+ bytehostorder(&msgin);
+
+ if (msgin.tsp_vers > TSPVERSION) {
+ if (trace) {
+ fprintf(fd,"readmsg: version mismatch\n");
+ /* should do a dump of the packet */
+ }
+ continue;
+ }
+
+ if (memchr(msgin.tsp_name,
+ '\0', sizeof msgin.tsp_name) == NULL) {
+ syslog(LOG_NOTICE, "hostname field not NUL terminated "
+ "in packet from %s", inet_ntoa(from.sin_addr));
+ continue;
+ }
+
+ fromnet = NULL;
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next)
+ if ((ntp->mask & from.sin_addr.s_addr) ==
+ ntp->net.s_addr) {
+ fromnet = ntp;
+ break;
+ }
+
+ /*
+ * drop packets from nets we are ignoring permanently
+ */
+ if (fromnet == NULL) {
+ /*
+ * The following messages may originate on
+ * this host with an ignored network address
+ */
+ if (msgin.tsp_type != TSP_TRACEON &&
+ msgin.tsp_type != TSP_SETDATE &&
+ msgin.tsp_type != TSP_MSITE &&
+ msgin.tsp_type != TSP_TEST &&
+ msgin.tsp_type != TSP_TRACEOFF) {
+ if (trace) {
+ fprintf(fd,"readmsg: discard null net ");
+ print(&msgin, &from);
+ }
+ continue;
+ }
+ }
+
+ /*
+ * Throw away messages coming from this machine,
+ * unless they are of some particular type.
+ * This gets rid of broadcast messages and reduces
+ * master processing time.
+ */
+ if (!strcmp(msgin.tsp_name, hostname)
+ && msgin.tsp_type != TSP_SETDATE
+ && msgin.tsp_type != TSP_TEST
+ && msgin.tsp_type != TSP_MSITE
+ && msgin.tsp_type != TSP_TRACEON
+ && msgin.tsp_type != TSP_TRACEOFF
+ && msgin.tsp_type != TSP_LOOP) {
+ if (trace) {
+ fprintf(fd, "readmsg: discard own ");
+ print(&msgin, &from);
+ }
+ continue;
+ }
+
+ /*
+ * Send acknowledgements here; this is faster and
+ * avoids deadlocks that would occur if acks were
+ * sent from a higher level routine. Different
+ * acknowledgements are necessary, depending on
+ * status.
+ */
+ if (fromnet == NULL) /* do not de-reference 0 */
+ ignoreack();
+ else if (fromnet->status == MASTER)
+ masterack();
+ else if (fromnet->status == SLAVE)
+ slaveack();
+ else
+ ignoreack();
+
+ if (LOOKAT(msgin, type, machfrom, netfrom, from)) {
+ if (trace) {
+ fprintf(fd, "readmsg: ");
+ print(&msgin, &from);
+ }
+ return(&msgin);
+ } else if (++msgcnt > NHOSTS*3) {
+
+/* The protocol gets hopelessly confused if it gets too far
+* behind. However, it seems able to recover from all cases of lost
+* packets. Therefore, if we are swamped, throw everything away.
+*/
+ if (trace)
+ fprintf(fd,
+ "readmsg: discarding %d msgs\n",
+ msgcnt);
+ msgcnt = 0;
+ while ((ptr=head->p) != NULL) {
+ head->p = ptr->p;
+ free((char *)ptr);
+ }
+ tail = head;
+ } else {
+ tail->p = (struct tsplist *)
+ malloc(sizeof(struct tsplist));
+ tail = tail->p;
+ tail->p = NULL;
+ tail->info = msgin;
+ tail->addr = from;
+ /* timestamp msgs so SETTIMEs are correct */
+ tail->when = from_when;
+ }
+ }
+}
+
+/*
+ * Send the necessary acknowledgements:
+ * only the type ACK is to be sent by a slave
+ */
+void
+slaveack()
+{
+ switch(msgin.tsp_type) {
+
+ case TSP_ADJTIME:
+ case TSP_SETTIME:
+ case TSP_ACCEPT:
+ case TSP_REFUSE:
+ case TSP_TRACEON:
+ case TSP_TRACEOFF:
+ case TSP_QUIT:
+ if (trace) {
+ fprintf(fd, "Slaveack: ");
+ print(&msgin, &from);
+ }
+ xmit(TSP_ACK,msgin.tsp_seq, &from);
+ break;
+
+ default:
+ if (trace) {
+ fprintf(fd, "Slaveack: no ack: ");
+ print(&msgin, &from);
+ }
+ break;
+ }
+}
+
+/*
+ * Certain packets may arrive from this machine on ignored networks.
+ * These packets should be acknowledged.
+ */
+void
+ignoreack()
+{
+ switch(msgin.tsp_type) {
+
+ case TSP_TRACEON:
+ case TSP_TRACEOFF:
+ case TSP_QUIT:
+ if (trace) {
+ fprintf(fd, "Ignoreack: ");
+ print(&msgin, &from);
+ }
+ xmit(TSP_ACK,msgin.tsp_seq, &from);
+ break;
+
+ default:
+ if (trace) {
+ fprintf(fd, "Ignoreack: no ack: ");
+ print(&msgin, &from);
+ }
+ break;
+ }
+}
+
+/*
+ * `masterack' sends the necessary acknowledgments
+ * to the messages received by a master
+ */
+void
+masterack()
+{
+ struct tsp resp;
+
+ resp = msgin;
+ resp.tsp_vers = TSPVERSION;
+ (void)strcpy(resp.tsp_name, hostname);
+
+ switch(msgin.tsp_type) {
+
+ case TSP_QUIT:
+ case TSP_TRACEON:
+ case TSP_TRACEOFF:
+ case TSP_MSITEREQ:
+ if (trace) {
+ fprintf(fd, "Masterack: ");
+ print(&msgin, &from);
+ }
+ xmit(TSP_ACK,msgin.tsp_seq, &from);
+ break;
+
+ case TSP_RESOLVE:
+ case TSP_MASTERREQ:
+ if (trace) {
+ fprintf(fd, "Masterack: ");
+ print(&msgin, &from);
+ }
+ xmit(TSP_MASTERACK,msgin.tsp_seq, &from);
+ break;
+
+ default:
+ if (trace) {
+ fprintf(fd,"Masterack: no ack: ");
+ print(&msgin, &from);
+ }
+ break;
+ }
+}
+
+/*
+ * Print a TSP message
+ */
+void
+print(msg, addr)
+ struct tsp *msg;
+ struct sockaddr_in *addr;
+{
+ char tm[26];
+ time_t tsp_time_sec;
+
+ if (msg->tsp_type >= TSPTYPENUMBER) {
+ fprintf(fd, "bad type (%u) on packet from %s\n",
+ msg->tsp_type, inet_ntoa(addr->sin_addr));
+ return;
+ }
+
+ switch (msg->tsp_type) {
+
+ case TSP_LOOP:
+ fprintf(fd, "%s %d %-6u #%d %-15s %s\n",
+ tsptype[msg->tsp_type],
+ msg->tsp_vers,
+ msg->tsp_seq,
+ msg->tsp_hopcnt,
+ inet_ntoa(addr->sin_addr),
+ msg->tsp_name);
+ break;
+
+ case TSP_SETTIME:
+ case TSP_SETDATE:
+ case TSP_SETDATEREQ:
+ tsp_time_sec = msg->tsp_time.tv_sec;
+ strncpy(tm, ctime(&tsp_time_sec)+3+1, sizeof(tm));
+ tm[15] = '\0'; /* ugh */
+ fprintf(fd, "%s %d %-6u %s %-15s %s\n",
+ tsptype[msg->tsp_type],
+ msg->tsp_vers,
+ msg->tsp_seq,
+ tm,
+ inet_ntoa(addr->sin_addr),
+ msg->tsp_name);
+ break;
+
+ case TSP_ADJTIME:
+ fprintf(fd, "%s %d %-6u (%ld,%ld) %-15s %s\n",
+ tsptype[msg->tsp_type],
+ msg->tsp_vers,
+ msg->tsp_seq,
+ msg->tsp_time.tv_sec,
+ msg->tsp_time.tv_usec,
+ inet_ntoa(addr->sin_addr),
+ msg->tsp_name);
+ break;
+
+ default:
+ fprintf(fd, "%s %d %-6u %-15s %s\n",
+ tsptype[msg->tsp_type],
+ msg->tsp_vers,
+ msg->tsp_seq,
+ inet_ntoa(addr->sin_addr),
+ msg->tsp_name);
+ break;
+ }
+}
diff --git a/usr.sbin/timed/timed/slave.c b/usr.sbin/timed/timed/slave.c
new file mode 100644
index 0000000..217fd82
--- /dev/null
+++ b/usr.sbin/timed/timed/slave.c
@@ -0,0 +1,693 @@
+/*-
+ * 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.
+ * 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
+#if 0
+static char sccsid[] = "@(#)slave.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "globals.h"
+#include <setjmp.h>
+#include "pathnames.h"
+
+extern jmp_buf jmpenv;
+extern int Mflag;
+extern int justquit;
+
+extern u_short sequence;
+
+static char master_name[MAXHOSTNAMELEN];
+static struct netinfo *old_slavenet;
+static int old_status;
+
+static void schgdate(struct tsp *, char *);
+static void setmaster(struct tsp *);
+static void answerdelay(void);
+
+extern void logwtmp(char *, char *, char *);
+
+int
+slave()
+{
+ int tries;
+ long electiontime, refusetime, looktime, looptime, adjtime;
+ u_short seq;
+ long fastelection;
+#define FASTTOUT 3
+ struct in_addr cadr;
+ struct timeval otime;
+ struct sockaddr_in taddr;
+ char tname[MAXHOSTNAMELEN];
+ struct tsp *msg, to;
+ struct timeval ntime, wait, tmptv;
+ time_t tsp_time_sec;
+ struct tsp *answer;
+ int timeout();
+ char olddate[32];
+ char newdate[32];
+ struct netinfo *ntp;
+ struct hosttbl *htp;
+
+
+ old_slavenet = 0;
+ seq = 0;
+ refusetime = 0;
+ adjtime = 0;
+
+ (void)gettimeofday(&ntime, 0);
+ electiontime = ntime.tv_sec + delay2;
+ fastelection = ntime.tv_sec + FASTTOUT;
+ if (justquit)
+ looktime = electiontime;
+ else
+ looktime = fastelection;
+ looptime = fastelection;
+
+ if (slavenet)
+ xmit(TSP_SLAVEUP, 0, &slavenet->dest_addr);
+ if (status & MASTER) {
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ if (ntp->status == MASTER)
+ masterup(ntp);
+ }
+ }
+
+loop:
+ get_goodgroup(0);
+ (void)gettimeofday(&ntime, (struct timezone *)0);
+ if (ntime.tv_sec > electiontime) {
+ if (trace)
+ fprintf(fd, "election timer expired\n");
+ longjmp(jmpenv, 1);
+ }
+
+ if (ntime.tv_sec >= looktime) {
+ if (trace)
+ fprintf(fd, "Looking for nets to master\n");
+
+ if (Mflag && nignorednets > 0) {
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ if (ntp->status == IGNORE
+ || ntp->status == NOMASTER) {
+ lookformaster(ntp);
+ if (ntp->status == MASTER) {
+ masterup(ntp);
+ } else if (ntp->status == MASTER) {
+ ntp->status = NOMASTER;
+ }
+ }
+ if (ntp->status == MASTER
+ && --ntp->quit_count < 0)
+ ntp->quit_count = 0;
+ }
+ makeslave(slavenet); /* prune extras */
+ setstatus();
+ }
+ (void)gettimeofday(&ntime, 0);
+ looktime = ntime.tv_sec + delay2;
+ }
+ if (ntime.tv_sec >= looptime) {
+ if (trace)
+ fprintf(fd, "Looking for loops\n");
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ if (ntp->status == MASTER) {
+ to.tsp_type = TSP_LOOP;
+ to.tsp_vers = TSPVERSION;
+ to.tsp_seq = sequence++;
+ to.tsp_hopcnt = MAX_HOPCNT;
+ (void)strcpy(to.tsp_name, hostname);
+ bytenetorder(&to);
+ if (sendto(sock, (char *)&to, sizeof(struct tsp), 0,
+ (struct sockaddr*)&ntp->dest_addr,
+ sizeof(ntp->dest_addr)) < 0) {
+ trace_sendto_err(ntp->dest_addr.sin_addr);
+ }
+ }
+ }
+ (void)gettimeofday(&ntime, 0);
+ looptime = ntime.tv_sec + delay2;
+ }
+
+ wait.tv_sec = min(electiontime,min(looktime,looptime)) - ntime.tv_sec;
+ if (wait.tv_sec < 0)
+ wait.tv_sec = 0;
+ wait.tv_sec += FASTTOUT;
+ wait.tv_usec = 0;
+ msg = readmsg(TSP_ANY, ANYADDR, &wait, 0);
+
+ if (msg != NULL) {
+ /*
+ * filter stuff not for us
+ */
+ switch (msg->tsp_type) {
+ case TSP_SETDATE:
+ case TSP_TRACEOFF:
+ case TSP_TRACEON:
+ /*
+ * XXX check to see they are from ourself
+ */
+ break;
+
+ case TSP_TEST:
+ case TSP_MSITE:
+ break;
+
+ case TSP_MASTERUP:
+ if (!fromnet) {
+ if (trace) {
+ fprintf(fd, "slave ignored: ");
+ print(msg, &from);
+ }
+ goto loop;
+ }
+ break;
+
+ default:
+ if (!fromnet
+ || fromnet->status == IGNORE
+ || fromnet->status == NOMASTER) {
+ if (trace) {
+ fprintf(fd, "slave ignored: ");
+ print(msg, &from);
+ }
+ goto loop;
+ }
+ break;
+ }
+
+
+ /*
+ * now process the message
+ */
+ switch (msg->tsp_type) {
+
+ case TSP_ADJTIME:
+ if (fromnet != slavenet)
+ break;
+ if (!good_host_name(msg->tsp_name)) {
+ syslog(LOG_NOTICE,
+ "attempted time adjustment by %s",
+ msg->tsp_name);
+ suppress(&from, msg->tsp_name, fromnet);
+ break;
+ }
+ /*
+ * Speed up loop detection in case we have a loop.
+ * Otherwise the clocks can race until the loop
+ * is found.
+ */
+ (void)gettimeofday(&otime, 0);
+ if (adjtime < otime.tv_sec)
+ looptime -= (looptime-otime.tv_sec)/2 + 1;
+
+ setmaster(msg);
+ if (seq != msg->tsp_seq) {
+ seq = msg->tsp_seq;
+ synch(tvtomsround(msg->tsp_time));
+ }
+ (void)gettimeofday(&ntime, 0);
+ electiontime = ntime.tv_sec + delay2;
+ fastelection = ntime.tv_sec + FASTTOUT;
+ adjtime = ntime.tv_sec + SAMPLEINTVL*2;
+ break;
+
+ case TSP_SETTIME:
+ if (fromnet != slavenet)
+ break;
+ if (seq == msg->tsp_seq)
+ break;
+ seq = msg->tsp_seq;
+
+ /* adjust time for residence on the queue */
+ (void)gettimeofday(&otime, 0);
+ adj_msg_time(msg,&otime);
+ /*
+ * the following line is necessary due to syslog
+ * calling ctime() which clobbers the static buffer
+ */
+ (void)strcpy(olddate, date());
+ tsp_time_sec = msg->tsp_time.tv_sec;
+ (void)strcpy(newdate, ctime(&tsp_time_sec));
+
+ if (!good_host_name(msg->tsp_name)) {
+ syslog(LOG_NOTICE,
+ "attempted time setting by untrusted %s to %s",
+ msg->tsp_name, newdate);
+ suppress(&from, msg->tsp_name, fromnet);
+ break;
+ }
+
+ setmaster(msg);
+ tmptv.tv_sec = msg->tsp_time.tv_sec;
+ tmptv.tv_usec = msg->tsp_time.tv_usec;
+ timevalsub(&ntime, &tmptv, &otime);
+ if (ntime.tv_sec < MAXADJ && ntime.tv_sec > -MAXADJ) {
+ /*
+ * do not change the clock if we can adjust it
+ */
+ synch(tvtomsround(ntime));
+ } else {
+ logwtmp("|", "date", "");
+ (void)settimeofday(&tmptv, 0);
+ logwtmp("{", "date", "");
+ syslog(LOG_NOTICE,
+ "date changed by %s from %s",
+ msg->tsp_name, olddate);
+ if (status & MASTER)
+ spreadtime();
+ }
+ (void)gettimeofday(&ntime, 0);
+ electiontime = ntime.tv_sec + delay2;
+ fastelection = ntime.tv_sec + FASTTOUT;
+
+/* This patches a bad protocol bug. Imagine a system with several networks,
+ * where there are a pair of redundant gateways between a pair of networks,
+ * each running timed. Assume that we start with a third machine mastering
+ * one of the networks, and one of the gateways mastering the other.
+ * Imagine that the third machine goes away and the non-master gateway
+ * decides to replace it. If things are timed just 'right,' we will have
+ * each gateway mastering one network for a little while. If a SETTIME
+ * message gets into the network at that time, perhaps from the newly
+ * masterful gateway as it was taking control, the SETTIME will loop
+ * forever. Each time a gateway receives it on its slave side, it will
+ * call spreadtime to forward it on its mastered network. We are now in
+ * a permanent loop, since the SETTIME msgs will keep any clock
+ * in the network from advancing. Normally, the 'LOOP' stuff will detect
+ * and correct the situation. However, with the clocks stopped, the
+ * 'looptime' timer cannot expire. While they are in this state, the
+ * masters will try to saturate the network with SETTIME packets.
+ */
+ looptime = ntime.tv_sec + (looptime-otime.tv_sec)/2-1;
+ break;
+
+ case TSP_MASTERUP:
+ if (slavenet && fromnet != slavenet)
+ break;
+ if (!good_host_name(msg->tsp_name)) {
+ suppress(&from, msg->tsp_name, fromnet);
+ if (electiontime > fastelection)
+ electiontime = fastelection;
+ break;
+ }
+ makeslave(fromnet);
+ setmaster(msg);
+ setstatus();
+ answerdelay();
+ xmit(TSP_SLAVEUP, 0, &from);
+ (void)gettimeofday(&ntime, 0);
+ electiontime = ntime.tv_sec + delay2;
+ fastelection = ntime.tv_sec + FASTTOUT;
+ refusetime = 0;
+ break;
+
+ case TSP_MASTERREQ:
+ if (fromnet->status != SLAVE)
+ break;
+ (void)gettimeofday(&ntime, 0);
+ electiontime = ntime.tv_sec + delay2;
+ break;
+
+ case TSP_SETDATE:
+ tsp_time_sec = msg->tsp_time.tv_sec;
+ (void)strcpy(newdate, ctime(&tsp_time_sec));
+ schgdate(msg, newdate);
+ break;
+
+ case TSP_SETDATEREQ:
+ if (fromnet->status != MASTER)
+ break;
+ tsp_time_sec = msg->tsp_time.tv_sec;
+ (void)strcpy(newdate, ctime(&tsp_time_sec));
+ htp = findhost(msg->tsp_name);
+ if (0 == htp) {
+ syslog(LOG_WARNING,
+ "DATEREQ from uncontrolled machine");
+ break;
+ }
+ if (!htp->good) {
+ syslog(LOG_WARNING,
+ "attempted date change by untrusted %s to %s",
+ htp->name, newdate);
+ spreadtime();
+ break;
+ }
+ schgdate(msg, newdate);
+ break;
+
+ case TSP_TRACEON:
+ traceon();
+ break;
+
+ case TSP_TRACEOFF:
+ traceoff("Tracing ended at %s\n");
+ break;
+
+ case TSP_SLAVEUP:
+ newslave(msg);
+ break;
+
+ case TSP_ELECTION:
+ if (fromnet->status == SLAVE) {
+ (void)gettimeofday(&ntime, 0);
+ electiontime = ntime.tv_sec + delay2;
+ fastelection = ntime.tv_sec + FASTTOUT;
+ seq = 0;
+ if (!good_host_name(msg->tsp_name)) {
+ syslog(LOG_NOTICE,
+ "suppress election of %s",
+ msg->tsp_name);
+ to.tsp_type = TSP_QUIT;
+ electiontime = fastelection;
+ } else if (cadr.s_addr != from.sin_addr.s_addr
+ && ntime.tv_sec < refusetime) {
+/* if the candidate has to repeat itself, the old code would refuse it
+ * the second time. That would prevent elections.
+ */
+ to.tsp_type = TSP_REFUSE;
+ } else {
+ cadr.s_addr = from.sin_addr.s_addr;
+ to.tsp_type = TSP_ACCEPT;
+ refusetime = ntime.tv_sec + 30;
+ }
+ taddr = from;
+ (void)strcpy(tname, msg->tsp_name);
+ (void)strcpy(to.tsp_name, hostname);
+ answerdelay();
+ if (!acksend(&to, &taddr, tname,
+ TSP_ACK, 0, 0))
+ syslog(LOG_WARNING,
+ "no answer from candidate %s\n",
+ tname);
+
+ } else { /* fromnet->status == MASTER */
+ htp = addmach(msg->tsp_name, &from,fromnet);
+ to.tsp_type = TSP_QUIT;
+ (void)strcpy(to.tsp_name, hostname);
+ if (!acksend(&to, &htp->addr, htp->name,
+ TSP_ACK, 0, htp->noanswer)) {
+ syslog(LOG_ERR,
+ "no reply from %s to ELECTION-QUIT",
+ htp->name);
+ (void)remmach(htp);
+ }
+ }
+ break;
+
+ case TSP_CONFLICT:
+ if (fromnet->status != MASTER)
+ break;
+ /*
+ * After a network partition, there can be
+ * more than one master: the first slave to
+ * come up will notify here the situation.
+ */
+ (void)strcpy(to.tsp_name, hostname);
+
+ /* The other master often gets into the same state,
+ * with boring results.
+ */
+ ntp = fromnet; /* (acksend() can leave fromnet=0 */
+ for (tries = 0; tries < 3; tries++) {
+ to.tsp_type = TSP_RESOLVE;
+ answer = acksend(&to, &ntp->dest_addr,
+ ANYADDR, TSP_MASTERACK,
+ ntp, 0);
+ if (answer == NULL)
+ break;
+ htp = addmach(answer->tsp_name,&from,ntp);
+ to.tsp_type = TSP_QUIT;
+ answer = acksend(&to, &htp->addr, htp->name,
+ TSP_ACK, 0, htp->noanswer);
+ if (!answer) {
+ syslog(LOG_WARNING,
+ "conflict error: no reply from %s to QUIT",
+ htp->name);
+ (void)remmach(htp);
+ }
+ }
+ masterup(ntp);
+ break;
+
+ case TSP_MSITE:
+ if (!slavenet)
+ break;
+ taddr = from;
+ to.tsp_type = TSP_MSITEREQ;
+ to.tsp_vers = TSPVERSION;
+ to.tsp_seq = 0;
+ (void)strcpy(to.tsp_name, hostname);
+ answer = acksend(&to, &slavenet->dest_addr,
+ ANYADDR, TSP_ACK,
+ slavenet, 0);
+ if (answer != NULL
+ && good_host_name(answer->tsp_name)) {
+ setmaster(answer);
+ to.tsp_type = TSP_ACK;
+ (void)strcpy(to.tsp_name, answer->tsp_name);
+ bytenetorder(&to);
+ if (sendto(sock, (char *)&to,
+ sizeof(struct tsp), 0,
+ (struct sockaddr*)&taddr,
+ sizeof(taddr)) < 0) {
+ trace_sendto_err(taddr.sin_addr);
+ }
+ }
+ break;
+
+ case TSP_MSITEREQ:
+ break;
+
+ case TSP_ACCEPT:
+ case TSP_REFUSE:
+ case TSP_RESOLVE:
+ break;
+
+ case TSP_QUIT:
+ doquit(msg); /* become a slave */
+ break;
+
+ case TSP_TEST:
+ electiontime = 0;
+ break;
+
+ case TSP_LOOP:
+ /* looking for loops of masters */
+ if (!(status & MASTER))
+ break;
+ if (fromnet->status == SLAVE) {
+ if (!strcmp(msg->tsp_name, hostname)) {
+ /*
+ * Someone forwarded our message back to
+ * us. There must be a loop. Tell the
+ * master of this network to quit.
+ *
+ * The other master often gets into
+ * the same state, with boring results.
+ */
+ ntp = fromnet;
+ for (tries = 0; tries < 3; tries++) {
+ to.tsp_type = TSP_RESOLVE;
+ answer = acksend(&to, &ntp->dest_addr,
+ ANYADDR, TSP_MASTERACK,
+ ntp,0);
+ if (answer == NULL)
+ break;
+ taddr = from;
+ (void)strcpy(tname, answer->tsp_name);
+ to.tsp_type = TSP_QUIT;
+ (void)strcpy(to.tsp_name, hostname);
+ if (!acksend(&to, &taddr, tname,
+ TSP_ACK, 0, 1)) {
+ syslog(LOG_ERR,
+ "no reply from %s to slave LOOP-QUIT",
+ tname);
+ } else {
+ electiontime = 0;
+ }
+ }
+ (void)gettimeofday(&ntime, 0);
+ looptime = ntime.tv_sec + FASTTOUT;
+ } else {
+ if (msg->tsp_hopcnt-- < 1)
+ break;
+ bytenetorder(msg);
+ for (ntp = nettab; ntp != 0; ntp = ntp->next) {
+ if (ntp->status == MASTER
+ && 0 > sendto(sock, (char *)msg,
+ sizeof(struct tsp), 0,
+ (struct sockaddr*)&ntp->dest_addr,
+ sizeof(ntp->dest_addr)))
+ trace_sendto_err(ntp->dest_addr.sin_addr);
+ }
+ }
+ } else { /* fromnet->status == MASTER */
+ /*
+ * We should not have received this from a net
+ * we are master on. There must be two masters,
+ * unless the packet was really from us.
+ */
+ if (from.sin_addr.s_addr
+ == fromnet->my_addr.s_addr) {
+ if (trace)
+ fprintf(fd,"discarding forwarded LOOP\n");
+ break;
+ }
+
+ /*
+ * The other master often gets into the same
+ * state, with boring results.
+ */
+ ntp = fromnet;
+ for (tries = 0; tries < 3; tries++) {
+ to.tsp_type = TSP_RESOLVE;
+ answer = acksend(&to, &ntp->dest_addr,
+ ANYADDR, TSP_MASTERACK,
+ ntp,0);
+ if (!answer)
+ break;
+ htp = addmach(answer->tsp_name,
+ &from,ntp);
+ to.tsp_type = TSP_QUIT;
+ (void)strcpy(to.tsp_name, hostname);
+ if (!acksend(&to,&htp->addr,htp->name,
+ TSP_ACK, 0, htp->noanswer)) {
+ syslog(LOG_ERR,
+ "no reply from %s to master LOOP-QUIT",
+ htp->name);
+ (void)remmach(htp);
+ }
+ }
+ (void)gettimeofday(&ntime, 0);
+ looptime = ntime.tv_sec + FASTTOUT;
+ }
+ break;
+ default:
+ if (trace) {
+ fprintf(fd, "garbage message: ");
+ print(msg, &from);
+ }
+ break;
+ }
+ }
+ goto loop;
+}
+
+
+/*
+ * tell the world who our master is
+ */
+static void
+setmaster(msg)
+ struct tsp *msg;
+{
+ if (slavenet
+ && (slavenet != old_slavenet
+ || strcmp(msg->tsp_name, master_name)
+ || old_status != status)) {
+ (void)strcpy(master_name, msg->tsp_name);
+ old_slavenet = slavenet;
+ old_status = status;
+
+ if (status & MASTER) {
+ syslog(LOG_NOTICE, "submaster to %s", master_name);
+ if (trace)
+ fprintf(fd, "submaster to %s\n", master_name);
+
+ } else {
+ syslog(LOG_NOTICE, "slave to %s", master_name);
+ if (trace)
+ fprintf(fd, "slave to %s\n", master_name);
+ }
+ }
+}
+
+
+
+/*
+ * handle date change request on a slave
+ */
+static void
+schgdate(msg, newdate)
+ struct tsp *msg;
+ char *newdate;
+{
+ struct tsp to;
+ u_short seq;
+ struct sockaddr_in taddr;
+ struct timeval otime;
+
+ if (!slavenet)
+ return; /* no where to forward */
+
+ taddr = from;
+ seq = msg->tsp_seq;
+
+ syslog(LOG_INFO,
+ "forwarding date change by %s to %s",
+ msg->tsp_name, newdate);
+
+ /* adjust time for residence on the queue */
+ (void)gettimeofday(&otime, 0);
+ adj_msg_time(msg, &otime);
+
+ to.tsp_type = TSP_SETDATEREQ;
+ to.tsp_time = msg->tsp_time;
+ (void)strcpy(to.tsp_name, hostname);
+ if (!acksend(&to, &slavenet->dest_addr,
+ ANYADDR, TSP_DATEACK,
+ slavenet, 0))
+ return; /* no answer */
+
+ xmit(TSP_DATEACK, seq, &taddr);
+}
+
+
+/*
+ * Used before answering a broadcast message to avoid network
+ * contention and likely collisions.
+ */
+static void
+answerdelay()
+{
+ struct timeval timeout;
+
+ timeout.tv_sec = 0;
+ timeout.tv_usec = delay1;
+
+ (void)select(0, (fd_set *)NULL, (fd_set *)NULL, (fd_set *)NULL,
+ &timeout);
+ return;
+}
diff --git a/usr.sbin/timed/timed/timed.8 b/usr.sbin/timed/timed/timed.8
new file mode 100644
index 0000000..f9fbd32
--- /dev/null
+++ b/usr.sbin/timed/timed/timed.8
@@ -0,0 +1,299 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.\" @(#)timed.8 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\"
+.Dd February 11, 2008
+.Dt TIMED 8
+.Os
+.Sh NAME
+.Nm timed
+.Nd time server daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl dtM
+.Op Fl i Ar network | Fl n Ar network
+.Op Fl F Ar host ...
+.Sh DESCRIPTION
+The
+.Nm
+utility is a time server daemon
+which is normally invoked at boot time from the
+.Xr rc.network 8
+file.
+It synchronizes the host's time with the time of other
+machines, which are also running
+.Nm ,
+in a local area network.
+These time servers will slow down the clocks of some machines
+and speed up the clocks of others to bring them to the average network time.
+The average network time is computed from measurements of clock differences
+using the
+.Tn ICMP
+timestamp request message.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d
+Enable debugging mode;
+do not detach from the terminal.
+.It Fl i Ar network
+Add
+.Ar network
+to the list of networks to ignore.
+All other networks
+to which the machine is directly connected
+are used by
+.Nm .
+This option may be specified multiple times
+to add more than one network to the list.
+.It Fl F Ar host ...
+.in +2
+.ti -2
+-
+Create a list of trusted hosts.
+.ti -2
+-
+Can take one or more parameters.
+.ti -2
+-
+.Nm
+will only accept trusted hosts as masters.
+If it finds an untrusted host claiming to be master,
+.Nm
+will suppress incoming messages from that host
+and call for a new election.
+.ti -2
+-
+Use real host names (resolvable by RDNS) not aliases (eg in
+.Xr named 8
+parlance: use A names, not C names).
+.ti -2
+-
+Use full names eg time1.domain.com not time1.
+.ti -2
+-
+.Fl F
+automatically includes the functionality of
+.Fl M
+( so
+.Fl M
+does not need to asserted).
+.ti -2
+-
+If
+.Fl F
+is not specified,
+all hosts on connected networks are treated as trustworthy.
+.in -2
+.It Fl M
+Allow this host to become a
+.Nm
+master if necessary.
+.It Fl n Ar network
+Add
+.Ar network
+to the list of allowed networks.
+All other networks
+to which the machine is directly connected
+are ignored by
+.Nm .
+This option may be specified multiple times
+to add more than one network to the list.
+.It Fl t
+Enable tracing of received messages
+and log to the file
+.Pa /var/log/timed.log .
+Tracing can be turned on or off while
+.Nm
+is running with the
+.Xr timedc 8
+utility.
+.El
+.Pp
+The
+.Fl n
+and
+.Fl i
+flags are mutually exclusive
+and require as arguments real networks to which
+the host is connected
+(see
+.Xr networks 5 ) .
+If neither flag is specified,
+.Nm
+will listen on all connected networks.
+.Pp
+A
+.Nm
+running without the
+.Fl M
+nor
+.Fl F
+flags will always remain a slave.
+If the
+.Fl F
+flag is not used,
+.Nm
+will treat all machines as trustworthy.
+.Pp
+The
+.Nm
+utility is based on a master-slave
+scheme.
+When
+.Nm
+is started on a machine, it asks the master for the network time
+and sets the host's clock to that time.
+After that, it accepts synchronization messages periodically sent by
+the master and calls
+.Xr adjtime 2
+to perform the needed corrections on the host's clock.
+.Pp
+It also communicates with
+.Xr date 1
+in order to set the date globally,
+and with
+.Xr timedc 8 ,
+a
+.Nm
+control utility.
+If the machine running the master becomes unreachable,
+the slaves will elect a new master
+from among those slaves
+which are running with at least one of the
+.Fl M
+and
+.Fl F
+flags.
+.Pp
+At startup
+.Nm
+normally checks for a master time server on each network to which
+it is connected, except as modified by the
+.Fl n
+and
+.Fl i
+options described above.
+It will request synchronization service from the first master server
+located.
+If permitted by the
+.Fl M
+or
+.Fl F
+flags, it will provide synchronization service on any attached networks
+on which no trusted master server was detected.
+Such a server propagates the time computed by the top-level master.
+The
+.Nm
+utility will periodically check for the presence of a master
+on those networks for which it is operating as a slave.
+If it finds that there are no trusted masters on a network,
+it will begin the election process on that network.
+.Pp
+One way to synchronize a group of machines is to use
+.Xr ntpd 8
+to
+synchronize the clock of one machine to a distant standard or a radio
+receiver and
+.Fl F Ar hostname
+to tell its
+.Nm
+to trust only itself.
+.Pp
+Messages printed by the kernel on the system console occur with
+interrupts disabled.
+This means that the clock stops while they are printing.
+A machine with many disk or network hardware problems and consequent
+messages cannot keep good time by itself.
+Each message typically causes
+the clock to lose a dozen milliseconds.
+A time daemon can correct the result.
+.Pp
+Messages in the system log about machines that failed to respond
+usually indicate machines that crashed or were turned off.
+Complaints about machines that failed to respond to initial time
+settings are often associated with
+.Dq multi-homed
+machines that looked for time masters on more than one network and eventually
+chose to become a slave on the other network.
+.Sh WARNINGS
+Temporal chaos will result if two or more time daemons attempt
+to adjust the same clock.
+If both
+.Nm
+and another time daemon are run on the same machine,
+ensure that the
+.Fl F
+flag is used, so that
+.Nm
+never attempts to adjust the local clock.
+.Pp
+The protocol is based on
+.Tn UDP/IP
+broadcasts.
+All machines within the range of a broadcast that are using the
+.Tn TSP
+protocol must cooperate.
+There cannot be more than a single administrative domain using the
+.Fl F
+flag among all machines reached by a broadcast packet.
+Failure to follow this rule is usually indicated by complaints concerning
+.Dq untrusted
+machines in the system log.
+.Sh FILES
+.Bl -tag -width /var/log/timed.masterlog -compact
+.It Pa /var/log/timed.log
+tracing file for
+.Nm
+.It Pa /var/log/timed.masterlog
+log file for master
+.Nm
+.El
+.Sh SEE ALSO
+.Xr date 1 ,
+.Xr adjtime 2 ,
+.Xr gettimeofday 2 ,
+.Xr icmp 4 ,
+.Xr networks 5 ,
+.Xr ntpd 8 ,
+.Xr timedc 8
+.Rs
+.%T "TSP: The Time Synchronization Protocol for UNIX 4.3BSD"
+.%A R. Gusella
+.%A S. Zatti
+.Re
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.3 .
diff --git a/usr.sbin/timed/timed/timed.c b/usr.sbin/timed/timed/timed.c
new file mode 100644
index 0000000..92a1733
--- /dev/null
+++ b/usr.sbin/timed/timed/timed.c
@@ -0,0 +1,851 @@
+/*-
+ * 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.
+ * 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) 1985, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#if 0
+#ifndef lint
+static char sccsid[] = "@(#)timed.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#define TSPTYPES
+#include "globals.h"
+#include <net/if.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <setjmp.h>
+#include "pathnames.h"
+#include <math.h>
+#include <sys/types.h>
+#include <sys/times.h>
+
+int trace = 0;
+int sock, sock_raw = -1;
+int status = 0;
+u_short sequence; /* sequence number */
+long delay1;
+long delay2;
+
+int nslavenets; /* nets were I could be a slave */
+int nmasternets; /* nets were I could be a master */
+int nignorednets; /* ignored nets */
+int nnets; /* nets I am connected to */
+
+FILE *fd; /* trace file FD */
+
+jmp_buf jmpenv;
+
+struct netinfo *nettab = 0;
+struct netinfo *slavenet;
+int Mflag;
+int justquit = 0;
+int debug;
+
+static struct nets {
+ char *name;
+ long net;
+ struct nets *next;
+} *nets = 0;
+
+struct hosttbl hosttbl[NHOSTS+1]; /* known hosts */
+
+static struct goodhost { /* hosts that we trust */
+ char name[MAXHOSTNAMELEN];
+ struct goodhost *next;
+ char perm;
+} *goodhosts;
+
+static char *goodgroup; /* net group of trusted hosts */
+static void checkignorednets(void);
+static void pickslavenet(struct netinfo *);
+static void add_good_host(char *, int);
+static void usage(void);
+
+/*
+ * The timedaemons synchronize the clocks of hosts in a local area network.
+ * One daemon runs as master, all the others as slaves. The master
+ * performs the task of computing clock differences and sends correction
+ * values to the slaves.
+ * Slaves start an election to choose a new master when the latter disappears
+ * because of a machine crash, network partition, or when killed.
+ * A resolution protocol is used to kill all but one of the masters
+ * that happen to exist in segments of a partitioned network when the
+ * network partition is fixed.
+ *
+ * Authors: Riccardo Gusella & Stefano Zatti
+ *
+ * overhauled at Silicon Graphics
+ */
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int on;
+ int ret;
+ int nflag, iflag;
+ struct timeval ntime;
+ struct servent *srvp;
+ char buf[BUFSIZ], *cp, *cplim;
+ struct ifconf ifc;
+ struct ifreq ifreq, ifreqf, *ifr;
+ register struct netinfo *ntp;
+ struct netinfo *ntip;
+ struct netinfo *savefromnet;
+ struct netent *nentp;
+ struct nets *nt;
+ struct sockaddr_in server;
+ u_short port;
+ int c;
+
+#ifdef lint
+ ntip = NULL;
+#endif
+
+ on = 1;
+ nflag = OFF;
+ iflag = OFF;
+
+
+ opterr = 0;
+ while ((c = getopt(argc, argv, "Mtdn:i:F:G:P:")) != -1) {
+ switch (c) {
+ case 'M':
+ Mflag = 1;
+ break;
+
+ case 't':
+ trace = 1;
+ break;
+
+ case 'n':
+ if (iflag) {
+ errx(1, "-i and -n make no sense together");
+ } else {
+ nflag = ON;
+ addnetname(optarg);
+ }
+ break;
+
+ case 'i':
+ if (nflag) {
+ errx(1, "-i and -n make no sense together");
+ } else {
+ iflag = ON;
+ addnetname(optarg);
+ }
+ break;
+
+ case 'F':
+ add_good_host(optarg,1);
+ while (optind < argc && argv[optind][0] != '-')
+ add_good_host(argv[optind++], 1);
+ break;
+
+ case 'd':
+ debug = 1;
+ break;
+ case 'G':
+ if (goodgroup != 0)
+ errx(1, "only one net group");
+ goodgroup = optarg;
+ break;
+
+ default:
+ usage();
+ break;
+ }
+ }
+ if (optind < argc)
+ usage();
+
+ /* If we care about which machine is the master, then we must
+ * be willing to be a master
+ */
+ if (0 != goodgroup || 0 != goodhosts)
+ Mflag = 1;
+
+ if (gethostname(hostname, sizeof(hostname) - 1) < 0)
+ err(1, "gethostname");
+ self.l_bak = &self;
+ self.l_fwd = &self;
+ self.h_bak = &self;
+ self.h_fwd = &self;
+ self.head = 1;
+ self.good = 1;
+
+ if (goodhosts != 0) /* trust ourself */
+ add_good_host(hostname,1);
+
+ srvp = getservbyname("timed", "udp");
+ if (srvp == 0)
+ errx(1, "timed/udp: unknown service");
+ port = srvp->s_port;
+ bzero(&server, sizeof(struct sockaddr_in));
+ server.sin_port = srvp->s_port;
+ server.sin_family = AF_INET;
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0)
+ err(1, "socket");
+ if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&on,
+ sizeof(on)) < 0)
+ err(1, "setsockopt");
+ if (bind(sock, (struct sockaddr*)&server, sizeof(server))) {
+ if (errno == EADDRINUSE)
+ warnx("time daemon already running");
+ else
+ warn("bind");
+ exit(1);
+ }
+
+ /* choose a unique seed for random number generation */
+ (void)gettimeofday(&ntime, 0);
+ srandom(ntime.tv_sec + ntime.tv_usec);
+
+ sequence = random(); /* initial seq number */
+
+ /* rounds kernel variable time to multiple of 5 ms. */
+ ntime.tv_sec = 0;
+ ntime.tv_usec = -((ntime.tv_usec/1000) % 5) * 1000;
+ (void)adjtime(&ntime, (struct timeval *)0);
+
+ for (nt = nets; nt; nt = nt->next) {
+ nentp = getnetbyname(nt->name);
+ if (nentp == 0) {
+ nt->net = inet_network(nt->name);
+ if (nt->net != INADDR_NONE)
+ nentp = getnetbyaddr(nt->net, AF_INET);
+ }
+ if (nentp != 0) {
+ nt->net = nentp->n_net;
+ } else if (nt->net == INADDR_NONE) {
+ errx(1, "unknown net %s", nt->name);
+ } else if (nt->net == INADDR_ANY) {
+ errx(1, "bad net %s", nt->name);
+ } else {
+ warnx("warning: %s unknown in /etc/networks",
+ nt->name);
+ }
+
+ if (0 == (nt->net & 0xff000000))
+ nt->net <<= 8;
+ if (0 == (nt->net & 0xff000000))
+ nt->net <<= 8;
+ if (0 == (nt->net & 0xff000000))
+ nt->net <<= 8;
+ }
+ ifc.ifc_len = sizeof(buf);
+ ifc.ifc_buf = buf;
+ if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0)
+ err(1, "get interface configuration");
+ ntp = NULL;
+#define size(p) max((p).sa_len, sizeof(p))
+ cplim = buf + ifc.ifc_len; /*skip over if's with big ifr_addr's */
+ for (cp = buf; cp < cplim;
+ cp += sizeof (ifr->ifr_name) + size(ifr->ifr_addr)) {
+ ifr = (struct ifreq *)cp;
+ if (ifr->ifr_addr.sa_family != AF_INET)
+ continue;
+ if (!ntp)
+ ntp = (struct netinfo*)malloc(sizeof(struct netinfo));
+ bzero(ntp,sizeof(*ntp));
+ ntp->my_addr=((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr;
+ ntp->status = NOMASTER;
+ ifreq = *ifr;
+ ifreqf = *ifr;
+
+ if (ioctl(sock, SIOCGIFFLAGS, (char *)&ifreqf) < 0) {
+ warn("get interface flags");
+ continue;
+ }
+ if ((ifreqf.ifr_flags & IFF_UP) == 0)
+ continue;
+ if ((ifreqf.ifr_flags & IFF_BROADCAST) == 0 &&
+ (ifreqf.ifr_flags & IFF_POINTOPOINT) == 0) {
+ continue;
+ }
+
+
+ if (ioctl(sock, SIOCGIFNETMASK, (char *)&ifreq) < 0) {
+ warn("get netmask");
+ continue;
+ }
+ ntp->mask = ((struct sockaddr_in *)
+ &ifreq.ifr_addr)->sin_addr.s_addr;
+
+ if (ifreqf.ifr_flags & IFF_BROADCAST) {
+ if (ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
+ warn("get broadaddr");
+ continue;
+ }
+ ntp->dest_addr = *(struct sockaddr_in *)&ifreq.ifr_broadaddr;
+ /* What if the broadcast address is all ones?
+ * So we cannot just mask ntp->dest_addr. */
+ ntp->net = ntp->my_addr;
+ ntp->net.s_addr &= ntp->mask;
+ } else {
+ if (ioctl(sock, SIOCGIFDSTADDR,
+ (char *)&ifreq) < 0) {
+ warn("get destaddr");
+ continue;
+ }
+ ntp->dest_addr = *(struct sockaddr_in *)&ifreq.ifr_dstaddr;
+ ntp->net = ntp->dest_addr.sin_addr;
+ }
+
+ ntp->dest_addr.sin_port = port;
+
+ for (nt = nets; nt; nt = nt->next) {
+ if (ntp->net.s_addr == htonl(nt->net))
+ break;
+ }
+ if ((nflag && !nt) || (iflag && nt))
+ continue;
+
+ ntp->next = NULL;
+ if (nettab == NULL) {
+ nettab = ntp;
+ } else {
+ ntip->next = ntp;
+ }
+ ntip = ntp;
+ ntp = NULL;
+ }
+ if (ntp)
+ (void) free((char *)ntp);
+ if (nettab == NULL)
+ errx(1, "no network usable");
+
+ /* microseconds to delay before responding to a broadcast */
+ delay1 = casual(1, 100*1000);
+
+ /* election timer delay in secs. */
+ delay2 = casual(MINTOUT, MAXTOUT);
+
+ if (!debug)
+ daemon(debug, 0);
+
+ if (trace)
+ traceon();
+ openlog("timed", LOG_CONS|LOG_PID, LOG_DAEMON);
+
+ /*
+ * keep returning here
+ */
+ ret = setjmp(jmpenv);
+ savefromnet = fromnet;
+ setstatus();
+
+ if (Mflag) {
+ switch (ret) {
+
+ case 0:
+ checkignorednets();
+ pickslavenet(0);
+ break;
+ case 1:
+ /* Just lost our master */
+ if (slavenet != 0)
+ slavenet->status = election(slavenet);
+ if (!slavenet || slavenet->status == MASTER) {
+ checkignorednets();
+ pickslavenet(0);
+ } else {
+ makeslave(slavenet); /* prune extras */
+ }
+ break;
+
+ case 2:
+ /* Just been told to quit */
+ justquit = 1;
+ pickslavenet(savefromnet);
+ break;
+ }
+
+ setstatus();
+ if (!(status & MASTER) && sock_raw != -1) {
+ /* sock_raw is not being used now */
+ (void)close(sock_raw);
+ sock_raw = -1;
+ }
+
+ if (status == MASTER)
+ master();
+ else
+ slave();
+
+ } else {
+ if (sock_raw != -1) {
+ (void)close(sock_raw);
+ sock_raw = -1;
+ }
+
+ if (ret) {
+ /* we just lost our master or were told to quit */
+ justquit = 1;
+ }
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ if (ntp->status == MASTER)
+ rmnetmachs(ntp);
+ ntp->status = NOMASTER;
+ }
+ checkignorednets();
+ pickslavenet(0);
+ setstatus();
+
+ slave();
+ }
+ /* NOTREACHED */
+ return(0);
+}
+
+static void
+usage()
+{
+#ifdef HAVENIS
+ fprintf(stderr,
+"usage: timed [-dtM] [-i net|-n net] [-F host1 host2 ...] [-G netgp]\n");
+#else
+ fprintf(stderr,
+"usage: timed [-dtM] [-i net|-n net] [-F host1 host2 ...]\n");
+#endif /* HAVENIS */
+ exit(1);
+}
+
+/*
+ * suppress an upstart, untrustworthy, self-appointed master
+ */
+void
+suppress(addr, name,net)
+ struct sockaddr_in *addr;
+ char *name;
+ struct netinfo *net;
+{
+ struct sockaddr_in tgt;
+ char tname[MAXHOSTNAMELEN];
+ struct tsp msg;
+ static struct timeval wait;
+
+ if (trace)
+ fprintf(fd, "suppress: %s\n", name);
+ tgt = *addr;
+ (void)strcpy(tname, name);
+
+ while (0 != readmsg(TSP_ANY, ANYADDR, &wait, net)) {
+ if (trace)
+ fprintf(fd, "suppress:\tdiscarded packet from %s\n",
+ name);
+ }
+
+ syslog(LOG_NOTICE, "suppressing false master %s", tname);
+ msg.tsp_type = TSP_QUIT;
+ (void)strcpy(msg.tsp_name, hostname);
+ (void)acksend(&msg, &tgt, tname, TSP_ACK, 0, 1);
+}
+
+void
+lookformaster(ntp)
+ struct netinfo *ntp;
+{
+ struct tsp resp, conflict, *answer;
+ struct timeval ntime;
+ char mastername[MAXHOSTNAMELEN];
+ struct sockaddr_in masteraddr;
+
+ get_goodgroup(0);
+ ntp->status = SLAVE;
+
+ /* look for master */
+ resp.tsp_type = TSP_MASTERREQ;
+ (void)strcpy(resp.tsp_name, hostname);
+ answer = acksend(&resp, &ntp->dest_addr, ANYADDR,
+ TSP_MASTERACK, ntp, 0);
+ if (answer != 0 && !good_host_name(answer->tsp_name)) {
+ suppress(&from, answer->tsp_name, ntp);
+ ntp->status = NOMASTER;
+ answer = 0;
+ }
+ if (answer == 0) {
+ /*
+ * Various conditions can cause conflict: races between
+ * two just started timedaemons when no master is
+ * present, or timedaemons started during an election.
+ * A conservative approach is taken. Give up and became a
+ * slave, postponing election of a master until first
+ * timer expires.
+ */
+ ntime.tv_sec = ntime.tv_usec = 0;
+ answer = readmsg(TSP_MASTERREQ, ANYADDR, &ntime, ntp);
+ if (answer != 0) {
+ if (!good_host_name(answer->tsp_name)) {
+ suppress(&from, answer->tsp_name, ntp);
+ ntp->status = NOMASTER;
+ }
+ return;
+ }
+
+ ntime.tv_sec = ntime.tv_usec = 0;
+ answer = readmsg(TSP_MASTERUP, ANYADDR, &ntime, ntp);
+ if (answer != 0) {
+ if (!good_host_name(answer->tsp_name)) {
+ suppress(&from, answer->tsp_name, ntp);
+ ntp->status = NOMASTER;
+ }
+ return;
+ }
+
+ ntime.tv_sec = ntime.tv_usec = 0;
+ answer = readmsg(TSP_ELECTION, ANYADDR, &ntime, ntp);
+ if (answer != 0) {
+ if (!good_host_name(answer->tsp_name)) {
+ suppress(&from, answer->tsp_name, ntp);
+ ntp->status = NOMASTER;
+ }
+ return;
+ }
+
+ if (Mflag)
+ ntp->status = MASTER;
+ else
+ ntp->status = NOMASTER;
+ return;
+ }
+
+ ntp->status = SLAVE;
+ (void)strcpy(mastername, answer->tsp_name);
+ masteraddr = from;
+
+ /*
+ * If network has been partitioned, there might be other
+ * masters; tell the one we have just acknowledged that
+ * it has to gain control over the others.
+ */
+ ntime.tv_sec = 0;
+ ntime.tv_usec = 300000;
+ answer = readmsg(TSP_MASTERACK, ANYADDR, &ntime, ntp);
+ /*
+ * checking also not to send CONFLICT to ack'ed master
+ * due to duplicated MASTERACKs
+ */
+ if (answer != NULL &&
+ strcmp(answer->tsp_name, mastername) != 0) {
+ conflict.tsp_type = TSP_CONFLICT;
+ (void)strcpy(conflict.tsp_name, hostname);
+ if (!acksend(&conflict, &masteraddr, mastername,
+ TSP_ACK, 0, 0)) {
+ syslog(LOG_ERR,
+ "error on sending TSP_CONFLICT");
+ }
+ }
+}
+
+/*
+ * based on the current network configuration, set the status, and count
+ * networks;
+ */
+void
+setstatus()
+{
+ struct netinfo *ntp;
+
+ status = 0;
+ nmasternets = nslavenets = nnets = nignorednets = 0;
+ if (trace)
+ fprintf(fd, "Net status:\n");
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ switch ((int)ntp->status) {
+ case MASTER:
+ nmasternets++;
+ break;
+ case SLAVE:
+ nslavenets++;
+ break;
+ case NOMASTER:
+ case IGNORE:
+ nignorednets++;
+ break;
+ }
+ if (trace) {
+ fprintf(fd, "\t%-16s", inet_ntoa(ntp->net));
+ switch ((int)ntp->status) {
+ case NOMASTER:
+ fprintf(fd, "NOMASTER\n");
+ break;
+ case MASTER:
+ fprintf(fd, "MASTER\n");
+ break;
+ case SLAVE:
+ fprintf(fd, "SLAVE\n");
+ break;
+ case IGNORE:
+ fprintf(fd, "IGNORE\n");
+ break;
+ default:
+ fprintf(fd, "invalid state %d\n",
+ (int)ntp->status);
+ break;
+ }
+ }
+ nnets++;
+ status |= ntp->status;
+ }
+ status &= ~IGNORE;
+ if (trace)
+ fprintf(fd,
+ "\tnets=%d masters=%d slaves=%d ignored=%d delay2=%ld\n",
+ nnets, nmasternets, nslavenets, nignorednets, delay2);
+}
+
+void
+makeslave(net)
+ struct netinfo *net;
+{
+ register struct netinfo *ntp;
+
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ if (ntp->status == SLAVE && ntp != net)
+ ntp->status = IGNORE;
+ }
+ slavenet = net;
+}
+
+/*
+ * Try to become master over ignored nets..
+ */
+static void
+checkignorednets()
+{
+ register struct netinfo *ntp;
+
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ if (!Mflag && ntp->status == SLAVE)
+ break;
+
+ if (ntp->status == IGNORE || ntp->status == NOMASTER) {
+ lookformaster(ntp);
+ if (!Mflag && ntp->status == SLAVE)
+ break;
+ }
+ }
+}
+
+/*
+ * choose a good network on which to be a slave
+ * The ignored networks must have already been checked.
+ * Take a hint about for a good network.
+ */
+static void
+pickslavenet(ntp)
+ struct netinfo *ntp;
+{
+ if (slavenet != 0 && slavenet->status == SLAVE) {
+ makeslave(slavenet); /* prune extras */
+ return;
+ }
+
+ if (ntp == 0 || ntp->status != SLAVE) {
+ for (ntp = nettab; ntp != 0; ntp = ntp->next) {
+ if (ntp->status == SLAVE)
+ break;
+ }
+ }
+ makeslave(ntp);
+}
+
+/*
+ * returns a random number in the range [inf, sup]
+ */
+long
+casual(inf, sup)
+ long inf, sup;
+{
+ double value;
+
+ value = ((double)(random() & 0x7fffffff)) / (0x7fffffff*1.0);
+ return(inf + (sup - inf)*value);
+}
+
+char *
+date()
+{
+ struct timeval tv;
+ time_t tv_sec;
+
+ (void)gettimeofday(&tv, (struct timezone *)0);
+ tv_sec = tv.tv_sec;
+ return (ctime(&tv_sec));
+}
+
+void
+addnetname(name)
+ char *name;
+{
+ register struct nets **netlist = &nets;
+
+ while (*netlist)
+ netlist = &((*netlist)->next);
+ *netlist = (struct nets *)malloc(sizeof **netlist);
+ if (*netlist == 0)
+ errx(1, "malloc failed");
+ bzero((char *)*netlist, sizeof(**netlist));
+ (*netlist)->name = name;
+}
+
+/* note a host as trustworthy */
+static void
+add_good_host(name, perm)
+ char *name;
+ int perm; /* 1=not part of the netgroup */
+{
+ register struct goodhost *ghp;
+ register struct hostent *hentp;
+
+ ghp = (struct goodhost*)malloc(sizeof(*ghp));
+ if (!ghp) {
+ syslog(LOG_ERR, "malloc failed");
+ exit(1);
+ }
+
+ bzero((char*)ghp, sizeof(*ghp));
+ (void)strncpy(&ghp->name[0], name, sizeof(ghp->name));
+ ghp->next = goodhosts;
+ ghp->perm = perm;
+ goodhosts = ghp;
+
+ hentp = gethostbyname(name);
+ if (0 == hentp && perm)
+ warnx("unknown host %s", name);
+}
+
+
+/* update our image of the net-group of trustworthy hosts
+ */
+void
+get_goodgroup(force)
+ int force;
+{
+# define NG_DELAY (30*60*CLK_TCK) /* 30 minutes */
+ static unsigned long last_update = -NG_DELAY;
+ unsigned long new_update;
+ struct goodhost *ghp, **ghpp;
+#ifdef HAVENIS
+ struct hosttbl *htp;
+ char *mach, *usr, *dom;
+#endif /* HAVENIS */
+ struct tms tm;
+
+
+ /* if no netgroup, then we are finished */
+ if (goodgroup == 0 || !Mflag)
+ return;
+
+ /* Do not chatter with the netgroup master too often.
+ */
+ new_update = times(&tm);
+ if (new_update < last_update + NG_DELAY
+ && !force)
+ return;
+ last_update = new_update;
+
+ /* forget the old temporary entries */
+ ghpp = &goodhosts;
+ while (0 != (ghp = *ghpp)) {
+ if (!ghp->perm) {
+ *ghpp = ghp->next;
+ free((char*)ghp);
+ } else {
+ ghpp = &ghp->next;
+ }
+ }
+
+#ifdef HAVENIS
+ /* quit now if we are not one of the trusted masters
+ */
+ if (!innetgr(goodgroup, &hostname[0], 0,0)) {
+ if (trace)
+ (void)fprintf(fd, "get_goodgroup: %s not in %s\n",
+ &hostname[0], goodgroup);
+ return;
+ }
+ if (trace)
+ (void)fprintf(fd, "get_goodgroup: %s in %s\n",
+ &hostname[0], goodgroup);
+
+ /* mark the entire netgroup as trusted */
+ (void)setnetgrent(goodgroup);
+ while (getnetgrent(&mach,&usr,&dom)) {
+ if (0 != mach)
+ add_good_host(mach,0);
+ }
+ (void)endnetgrent();
+
+ /* update list of slaves */
+ for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
+ htp->good = good_host_name(&htp->name[0]);
+ }
+#endif /* HAVENIS */
+}
+
+
+/* see if a machine is trustworthy
+ */
+int /* 1=trust hp to change our date */
+good_host_name(name)
+ char *name;
+{
+ register struct goodhost *ghp = goodhosts;
+ register char c;
+
+ if (!ghp || !Mflag) /* trust everyone if no one named */
+ return 1;
+
+ c = *name;
+ do {
+ if (c == ghp->name[0]
+ && !strcasecmp(name, ghp->name))
+ return 1; /* found him, so say so */
+ } while (0 != (ghp = ghp->next));
+
+ if (!strcasecmp(name,hostname)) /* trust ourself */
+ return 1;
+
+ return 0; /* did not find him */
+}
diff --git a/usr.sbin/timed/timedc/Makefile b/usr.sbin/timed/timedc/Makefile
new file mode 100644
index 0000000..89e303b
--- /dev/null
+++ b/usr.sbin/timed/timedc/Makefile
@@ -0,0 +1,15 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../timed
+
+PROG= timedc
+MAN= timedc.8
+SRCS= cmds.c cmdtab.c timedc.c byteorder.c measure.c cksum.c
+BINOWN= root
+BINMODE= 4555
+
+WARNS?= 1
+
+.include "../../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/usr.sbin/timed/timedc/cmds.c b/usr.sbin/timed/timedc/cmds.c
new file mode 100644
index 0000000..5d30fe3
--- /dev/null
+++ b/usr.sbin/timed/timedc/cmds.c
@@ -0,0 +1,556 @@
+/*-
+ * 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.
+ * 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 char sccsid[] = "@(#)cmds.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+#endif
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "timedc.h"
+#include <sys/file.h>
+
+#include <arpa/inet.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define TSPTYPES
+#include <protocols/timed.h>
+
+#define SECHR (60*60)
+#define SECDAY (24*SECHR)
+
+# define DATE_PROTO "udp"
+# define DATE_PORT "time"
+
+
+int sock;
+int sock_raw;
+char myname[MAXHOSTNAMELEN];
+struct hostent *hp;
+struct sockaddr_in server;
+struct sockaddr_in dayaddr;
+extern int measure_delta;
+
+void bytenetorder(struct tsp *);
+void bytehostorder(struct tsp *);
+
+
+#define BU (2208988800UL) /* seconds before UNIX epoch */
+
+
+/* compute the difference between our date and another machine
+ */
+static int /* difference in days from our time */
+daydiff(hostname)
+ char *hostname;
+{
+ int i;
+ int trials;
+ struct timeval tout, now;
+ fd_set ready;
+ struct sockaddr from;
+ int fromlen;
+ unsigned long sec;
+
+
+ /* wait 2 seconds between 10 tries */
+ tout.tv_sec = 2;
+ tout.tv_usec = 0;
+ for (trials = 0; trials < 10; trials++) {
+ /* ask for the time */
+ sec = 0;
+ if (sendto(sock, &sec, sizeof(sec), 0,
+ (struct sockaddr*)&dayaddr, sizeof(dayaddr)) < 0) {
+ warn("sendto(sock)");
+ return 0;
+ }
+
+ for (;;) {
+ FD_ZERO(&ready);
+ FD_SET(sock, &ready);
+ i = select(sock+1, &ready, (fd_set *)0,
+ (fd_set *)0, &tout);
+ if (i < 0) {
+ if (errno == EINTR)
+ continue;
+ warn("select(date read)");
+ return 0;
+ }
+ if (0 == i)
+ break;
+
+ fromlen = sizeof(from);
+ if (recvfrom(sock,&sec,sizeof(sec),0,
+ &from,&fromlen) < 0) {
+ warn("recvfrom(date read)");
+ return 0;
+ }
+
+ sec = ntohl(sec);
+ if (sec < BU) {
+ warnx("%s says it is before 1970: %lu",
+ hostname, sec);
+ return 0;
+ }
+ sec -= BU;
+
+ (void)gettimeofday(&now, (struct timezone*)0);
+ return (sec - now.tv_sec);
+ }
+ }
+
+ /* if we get here, we tried too many times */
+ warnx("%s will not tell us the date", hostname);
+ return 0;
+}
+
+
+/*
+ * Clockdiff computes the difference between the time of the machine on
+ * which it is called and the time of the machines given as argument.
+ * The time differences measured by clockdiff are obtained using a sequence
+ * of ICMP TSTAMP messages which are returned to the sender by the IP module
+ * in the remote machine.
+ * In order to compare clocks of machines in different time zones, the time
+ * is transmitted (as a 32-bit value) in milliseconds since midnight UT.
+ * If a hosts uses a different time format, it should set the high order
+ * bit of the 32-bit quantity it transmits.
+ * However, VMS apparently transmits the time in milliseconds since midnight
+ * local time (rather than GMT) without setting the high order bit.
+ * Furthermore, it does not understand daylight-saving time. This makes
+ * clockdiff behaving inconsistently with hosts running VMS.
+ *
+ * In order to reduce the sensitivity to the variance of message transmission
+ * time, clockdiff sends a sequence of messages. Yet, measures between
+ * two `distant' hosts can be affected by a small error. The error can,
+ * however, be reduced by increasing the number of messages sent in each
+ * measurement.
+ */
+void
+clockdiff(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int measure_status;
+ extern int measure(u_long, u_long, char *, struct sockaddr_in*, int);
+ register int avg_cnt;
+ register long avg;
+ struct servent *sp;
+
+ if (argc < 2) {
+ printf("usage: timedc clockdiff host ...\n");
+ return;
+ }
+
+ if (gethostname(myname, sizeof(myname) - 1) < 0)
+ err(1, "gethostname");
+
+ /* get the address for the date ready */
+ sp = getservbyname(DATE_PORT, DATE_PROTO);
+ if (!sp) {
+ warnx("%s/%s: unknown service", DATE_PORT, DATE_PROTO);
+ dayaddr.sin_port = 0;
+ } else {
+ dayaddr.sin_port = sp->s_port;
+ }
+
+ while (argc > 1) {
+ argc--; argv++;
+ hp = gethostbyname(*argv);
+ if (hp == NULL) {
+ warnx("%s: %s", *argv, hstrerror(h_errno));
+ continue;
+ }
+
+ server.sin_family = hp->h_addrtype;
+ bcopy(hp->h_addr, &server.sin_addr.s_addr, hp->h_length);
+ for (avg_cnt = 0, avg = 0; avg_cnt < 16; avg_cnt++) {
+ measure_status = measure(10000,100, *argv, &server, 1);
+ if (measure_status != GOOD)
+ break;
+ avg += measure_delta;
+ }
+ if (measure_status == GOOD)
+ measure_delta = avg/avg_cnt;
+
+ switch (measure_status) {
+ case HOSTDOWN:
+ printf("%s is down\n", hp->h_name);
+ continue;
+ case NONSTDTIME:
+ printf("%s transmits a non-standard time format\n",
+ hp->h_name);
+ continue;
+ case UNREACHABLE:
+ printf("%s is unreachable\n", hp->h_name);
+ continue;
+ }
+
+ /*
+ * Try to get the date only after using ICMP timestamps to
+ * get the time. This is because the date protocol
+ * is optional.
+ */
+ if (dayaddr.sin_port != 0) {
+ dayaddr.sin_family = hp->h_addrtype;
+ bcopy(hp->h_addr, &dayaddr.sin_addr.s_addr,
+ hp->h_length);
+ avg = daydiff(*argv);
+ if (avg > SECDAY) {
+ printf("time on %s is %ld days ahead %s\n",
+ hp->h_name, avg/SECDAY, myname);
+ continue;
+ } else if (avg < -SECDAY) {
+ printf("time on %s is %ld days behind %s\n",
+ hp->h_name, -avg/SECDAY, myname);
+ continue;
+ }
+ }
+
+ if (measure_delta > 0) {
+ printf("time on %s is %d ms. ahead of time on %s\n",
+ hp->h_name, measure_delta, myname);
+ } else if (measure_delta == 0) {
+ printf("%s and %s have the same time\n",
+ hp->h_name, myname);
+ } else {
+ printf("time on %s is %d ms. behind time on %s\n",
+ hp->h_name, -measure_delta, myname);
+ }
+ }
+ return;
+}
+
+
+/*
+ * finds location of master timedaemon
+ */
+void
+msite(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int cc;
+ fd_set ready;
+ struct sockaddr_in dest;
+ int i, length;
+ struct sockaddr_in from;
+ struct timeval tout;
+ struct tsp msg;
+ struct servent *srvp;
+ char *tgtname;
+
+ if (argc < 1) {
+ printf("usage: timedc msite [host ...]\n");
+ return;
+ }
+
+ srvp = getservbyname("timed", "udp");
+ if (srvp == 0) {
+ warnx("timed/udp: unknown service");
+ return;
+ }
+ dest.sin_port = srvp->s_port;
+ dest.sin_family = AF_INET;
+
+ if (gethostname(myname, sizeof(myname) - 1) < 0)
+ err(1, "gethostname");
+ i = 1;
+ do {
+ tgtname = (i >= argc) ? myname : argv[i];
+ hp = gethostbyname(tgtname);
+ if (hp == 0) {
+ warnx("%s: %s", tgtname, hstrerror(h_errno));
+ continue;
+ }
+ bcopy(hp->h_addr, &dest.sin_addr.s_addr, hp->h_length);
+
+ (void)strlcpy(msg.tsp_name, myname, sizeof(msg.tsp_name));
+ msg.tsp_type = TSP_MSITE;
+ msg.tsp_vers = TSPVERSION;
+ bytenetorder(&msg);
+ if (sendto(sock, &msg, sizeof(struct tsp), 0,
+ (struct sockaddr*)&dest,
+ sizeof(struct sockaddr)) < 0) {
+ warn("sendto");
+ continue;
+ }
+
+ tout.tv_sec = 15;
+ tout.tv_usec = 0;
+ FD_ZERO(&ready);
+ FD_SET(sock, &ready);
+ if (select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0,
+ &tout)) {
+ length = sizeof(from);
+ cc = recvfrom(sock, &msg, sizeof(struct tsp), 0,
+ (struct sockaddr *)&from, &length);
+ if (cc < 0) {
+ warn("recvfrom");
+ continue;
+ }
+ /*
+ * The 4.3BSD protocol spec had a 32-byte tsp_name field, and
+ * this is still OS-dependent. Demand that the packet is at
+ * least long enough to hold a 4.3BSD packet.
+ */
+ if (cc < (sizeof(struct tsp) - MAXHOSTNAMELEN + 32)) {
+ fprintf(stderr,
+ "short packet (%u/%u bytes) from %s\n",
+ cc, sizeof(struct tsp) - MAXHOSTNAMELEN + 32,
+ inet_ntoa(from.sin_addr));
+ continue;
+ }
+ bytehostorder(&msg);
+ if (msg.tsp_type == TSP_ACK) {
+ printf("master timedaemon at %s is %s\n",
+ tgtname, msg.tsp_name);
+ } else {
+ if (msg.tsp_type >= TSPTYPENUMBER)
+ printf("unknown ack received: %u\n",
+ msg.tsp_type);
+ else
+ printf("wrong ack received: %s\n",
+ tsptype[msg.tsp_type]);
+ }
+ } else {
+ printf("communication error with %s\n", tgtname);
+ }
+ } while (++i < argc);
+}
+
+/*
+ * quits timedc
+ */
+void
+quit()
+{
+ exit(0);
+}
+
+
+/*
+ * Causes the election timer to expire on the selected hosts
+ * It sends just one udp message per machine, relying on
+ * reliability of communication channel.
+ */
+void
+testing(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct servent *srvp;
+ struct sockaddr_in sin;
+ struct tsp msg;
+
+ if (argc < 2) {
+ printf("usage: timedc election host1 [host2 ...]\n");
+ return;
+ }
+
+ srvp = getservbyname("timed", "udp");
+ if (srvp == 0) {
+ warnx("timed/udp: unknown service");
+ return;
+ }
+
+ while (argc > 1) {
+ argc--; argv++;
+ hp = gethostbyname(*argv);
+ if (hp == NULL) {
+ warnx("%s: %s", *argv, hstrerror(h_errno));
+ argc--; argv++;
+ continue;
+ }
+ sin.sin_port = srvp->s_port;
+ sin.sin_family = hp->h_addrtype;
+ bcopy(hp->h_addr, &sin.sin_addr.s_addr, hp->h_length);
+
+ msg.tsp_type = TSP_TEST;
+ msg.tsp_vers = TSPVERSION;
+ if (gethostname(myname, sizeof(myname) - 1) < 0)
+ err(1, "gethostname");
+ (void)strlcpy(msg.tsp_name, myname, sizeof(msg.tsp_name));
+ bytenetorder(&msg);
+ if (sendto(sock, &msg, sizeof(struct tsp), 0,
+ (struct sockaddr*)&sin,
+ sizeof(struct sockaddr)) < 0) {
+ warn("sendto");
+ }
+ }
+}
+
+
+/*
+ * Enables or disables tracing on local timedaemon
+ */
+void
+tracing(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int onflag;
+ int length;
+ int cc;
+ fd_set ready;
+ struct sockaddr_in dest;
+ struct sockaddr_in from;
+ struct timeval tout;
+ struct tsp msg;
+ struct servent *srvp;
+
+ if (argc != 2) {
+ printf("usage: timedc trace { on | off }\n");
+ return;
+ }
+
+ srvp = getservbyname("timed", "udp");
+ if (srvp == 0) {
+ warnx("timed/udp: unknown service");
+ return;
+ }
+ dest.sin_port = srvp->s_port;
+ dest.sin_family = AF_INET;
+
+ if (gethostname(myname, sizeof(myname) - 1) < 0)
+ err(1, "gethostname");
+ hp = gethostbyname(myname);
+ bcopy(hp->h_addr, &dest.sin_addr.s_addr, hp->h_length);
+
+ if (strcmp(argv[1], "on") == 0) {
+ msg.tsp_type = TSP_TRACEON;
+ onflag = ON;
+ } else {
+ msg.tsp_type = TSP_TRACEOFF;
+ onflag = OFF;
+ }
+
+ (void)strcpy(msg.tsp_name, myname);
+ msg.tsp_vers = TSPVERSION;
+ bytenetorder(&msg);
+ if (sendto(sock, &msg, sizeof(struct tsp), 0,
+ (struct sockaddr*)&dest, sizeof(struct sockaddr)) < 0) {
+ warn("sendto");
+ return;
+ }
+
+ tout.tv_sec = 5;
+ tout.tv_usec = 0;
+ FD_ZERO(&ready);
+ FD_SET(sock, &ready);
+ if (select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout)) {
+ length = sizeof(from);
+ cc = recvfrom(sock, &msg, sizeof(struct tsp), 0,
+ (struct sockaddr *)&from, &length);
+ if (cc < 0) {
+ warn("recvfrom");
+ return;
+ }
+ /*
+ * The 4.3BSD protocol spec had a 32-byte tsp_name field, and
+ * this is still OS-dependent. Demand that the packet is at
+ * least long enough to hold a 4.3BSD packet.
+ */
+ if (cc < (sizeof(struct tsp) - MAXHOSTNAMELEN + 32)) {
+ fprintf(stderr, "short packet (%u/%u bytes) from %s\n",
+ cc, sizeof(struct tsp) - MAXHOSTNAMELEN + 32,
+ inet_ntoa(from.sin_addr));
+ return;
+ }
+ bytehostorder(&msg);
+ if (msg.tsp_type == TSP_ACK)
+ if (onflag)
+ printf("timed tracing enabled\n");
+ else
+ printf("timed tracing disabled\n");
+ else {
+ if (msg.tsp_type >= TSPTYPENUMBER)
+ printf("unknown ack received: %u\n",
+ msg.tsp_type);
+ else
+ printf("wrong ack received: %s\n",
+ tsptype[msg.tsp_type]);
+ }
+ } else
+ printf("communication error\n");
+}
+
+int
+priv_resources()
+{
+ int port;
+ struct sockaddr_in sin;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ warn("opening socket");
+ return(-1);
+ }
+
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = 0;
+ for (port = IPPORT_RESERVED - 1; port > IPPORT_RESERVED / 2; port--) {
+ sin.sin_port = htons((u_short)port);
+ if (bind(sock, (struct sockaddr*)&sin, sizeof (sin)) >= 0)
+ break;
+ if (errno != EADDRINUSE && errno != EADDRNOTAVAIL) {
+ warn("bind");
+ (void) close(sock);
+ return(-1);
+ }
+ }
+ if (port == IPPORT_RESERVED / 2) {
+ warnx("all reserved ports in use");
+ (void) close(sock);
+ return(-1);
+ }
+
+ sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+ if (sock_raw < 0) {
+ warn("opening raw socket");
+ (void) close(sock);
+ return(-1);
+ }
+ return(1);
+}
diff --git a/usr.sbin/timed/timedc/cmdtab.c b/usr.sbin/timed/timedc/cmdtab.c
new file mode 100644
index 0000000..8e4c3d5
--- /dev/null
+++ b/usr.sbin/timed/timedc/cmdtab.c
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)cmdtab.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "timedc.h"
+
+char clockdiffhelp[] = "measures clock differences between machines";
+char helphelp[] = "gets help on commands";
+char msitehelp[] = "finds location of master";
+char quithelp[] = "exits timedc";
+char testinghelp[] = "causes election timers to expire";
+char tracinghelp[] = "turns tracing on or off";
+
+struct cmd cmdtab[] = {
+ { "clockdiff", clockdiffhelp, clockdiff, 0 },
+ { "election", testinghelp, testing, 1 },
+ { "help", helphelp, help, 0 },
+ { "msite", msitehelp, msite, 0 },
+ { "quit", quithelp, quit, 0 },
+ { "trace", tracinghelp, tracing, 1 },
+ { "?", helphelp, help, 0 },
+};
+
+int NCMDS = sizeof (cmdtab) / sizeof (cmdtab[0]);
diff --git a/usr.sbin/timed/timedc/extern.h b/usr.sbin/timed/timedc/extern.h
new file mode 100644
index 0000000..e1dbfe5
--- /dev/null
+++ b/usr.sbin/timed/timedc/extern.h
@@ -0,0 +1,54 @@
+/* $FreeBSD$ */
+
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ */
+
+#if __STDC__
+struct tsp;
+#endif
+
+extern struct cmd cmdtab[];
+
+void bytehostorder(struct tsp *);
+void bytenetorder(struct tsp *);
+void clockdiff(int, char *[]);
+void help(int, char *[]);
+void intr(int);
+void makeargv(void);
+void msite(int, char *[]);
+int priv_resources(void);
+void quit(void);
+void testing(int, char *[]);
+void tracing(int, char *[]);
diff --git a/usr.sbin/timed/timedc/timedc.8 b/usr.sbin/timed/timedc/timedc.8
new file mode 100644
index 0000000..0f3fcea
--- /dev/null
+++ b/usr.sbin/timed/timedc/timedc.8
@@ -0,0 +1,145 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. 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.
+.\"
+.\" @(#)timedc.8 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\"
+.Dd June 6, 1993
+.Dt TIMEDC 8
+.Os
+.Sh NAME
+.Nm timedc
+.Nd timed control program
+.Sh SYNOPSIS
+.Nm
+.Op Ar command Op Ar argument ...
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to control the operation of the
+.Xr timed 8
+program.
+It may be used to:
+.Bl -bullet
+.It
+Measure the differences between machines' clocks,
+.It
+Find the location where the master time server is running,
+.It
+Enable or disable tracing of messages received by
+.Xr timed 8 ,
+and
+.It
+Perform various debugging actions.
+.El
+.Pp
+Without any arguments,
+.Nm
+will prompt for commands from the standard input.
+If arguments are supplied,
+.Nm
+interprets the first argument as a command and the remaining
+arguments as parameters to the command.
+The standard input
+may be redirected causing
+.Nm
+to read commands from a file.
+Commands may be abbreviated;
+recognized commands are:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ic \&? Op Ar command ...
+.Pp
+.It Ic help Op Ar command ...
+Print a short description of each command specified in the argument list,
+or, if no arguments are given, a list of the recognized commands.
+.Pp
+.It Ic clockdiff Ar host ...
+Compute the differences between the clock of the host machine
+and the clocks of the machines given as arguments.
+.Pp
+.It Ic msite Op Ar host ...
+Show the master time server for specified host(s).
+.Pp
+.It Xo
+.Ic trace
+.Li \&{ Ar on Li \&|
+.Ar off \&}
+.Xc
+Enable or disable the tracing of incoming messages to
+.Xr timed 8
+in the file
+.Pa /var/log/timed.log .
+.Pp
+.It Ic election Ar host1 Op Ar host2 ...
+Asks the daemon
+on the target host to reset its "election" timers and to ensure that
+a time master has been elected.
+.Pp
+.It Ic quit
+Exit from timedc.
+.El
+.Pp
+Other commands may be included for use in testing and debugging
+.Xr timed 8 ;
+the help command and
+the program source may be consulted for details.
+.Sh FILES
+.Bl -tag -width /var/log/timed.masterlog -compact
+.It Pa /var/log/timed.log
+tracing file for timed
+.It Pa /var/log/timed.masterlog
+log file for master timed
+.El
+.Sh DIAGNOSTICS
+.Bl -diag
+.It ?Ambiguous command
+abbreviation matches more than one command
+.It ?Invalid command
+no match found
+.It ?Privileged command
+command can be executed by root only
+.El
+.Sh SEE ALSO
+.Xr date 1 ,
+.Xr adjtime 2 ,
+.Xr icmp 4 ,
+.Xr timed 8
+.Rs
+.%T "TSP: The Time Synchronization Protocol for UNIX 4.3BSD"
+.%A R. Gusella
+.%A S. Zatti
+.Re
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.3 .
diff --git a/usr.sbin/timed/timedc/timedc.c b/usr.sbin/timed/timedc/timedc.c
new file mode 100644
index 0000000..0911946
--- /dev/null
+++ b/usr.sbin/timed/timedc/timedc.c
@@ -0,0 +1,261 @@
+/*-
+ * 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.
+ * 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) 1985, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)timedc.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "timedc.h"
+#include <ctype.h>
+#include <err.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+
+int trace = 0;
+FILE *fd = 0;
+int margc;
+int fromatty;
+#define MAX_MARGV 20
+char *margv[MAX_MARGV];
+char cmdline[200];
+jmp_buf toplevel;
+static struct cmd *getcmd(char *);
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register struct cmd *c;
+
+ openlog("timedc", LOG_ODELAY, LOG_AUTH);
+
+ /*
+ * security dictates!
+ */
+ if (priv_resources() < 0)
+ errx(1, "could not get privileged resources");
+ (void) setuid(getuid());
+
+ if (--argc > 0) {
+ c = getcmd(*++argv);
+ if (c == (struct cmd *)-1) {
+ printf("?Ambiguous command\n");
+ exit(1);
+ }
+ if (c == 0) {
+ printf("?Invalid command\n");
+ exit(1);
+ }
+ if (c->c_priv && getuid()) {
+ printf("?Privileged command\n");
+ exit(1);
+ }
+ (*c->c_handler)(argc, argv);
+ exit(0);
+ }
+
+ fromatty = isatty(fileno(stdin));
+ if (setjmp(toplevel))
+ putchar('\n');
+ (void) signal(SIGINT, intr);
+ for (;;) {
+ if (fromatty) {
+ printf("timedc> ");
+ (void) fflush(stdout);
+ }
+ if (fgets(cmdline, sizeof(cmdline), stdin) == 0)
+ quit();
+ if (cmdline[0] == 0)
+ break;
+ makeargv();
+ if (margv[0] == 0)
+ continue;
+ c = getcmd(margv[0]);
+ if (c == (struct cmd *)-1) {
+ printf("?Ambiguous command\n");
+ continue;
+ }
+ if (c == 0) {
+ printf("?Invalid command\n");
+ continue;
+ }
+ if (c->c_priv && getuid()) {
+ printf("?Privileged command\n");
+ continue;
+ }
+ (*c->c_handler)(margc, margv);
+ }
+ return 0;
+}
+
+void
+intr(signo)
+ int signo;
+{
+ if (!fromatty)
+ exit(0);
+ longjmp(toplevel, 1);
+}
+
+
+static struct cmd *
+getcmd(name)
+ char *name;
+{
+ register char *p, *q;
+ register struct cmd *c, *found;
+ register int nmatches, longest;
+ extern int NCMDS;
+
+ longest = 0;
+ nmatches = 0;
+ found = 0;
+ for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
+ p = c->c_name;
+ for (q = name; *q == *p++; q++)
+ if (*q == 0) /* exact match? */
+ return(c);
+ if (!*q) { /* the name was a prefix */
+ if (q - name > longest) {
+ longest = q - name;
+ nmatches = 1;
+ found = c;
+ } else if (q - name == longest)
+ nmatches++;
+ }
+ }
+ if (nmatches > 1)
+ return((struct cmd *)-1);
+ return(found);
+}
+
+/*
+ * Slice a string up into argc/argv.
+ */
+void
+makeargv()
+{
+ register char *cp;
+ register char **argp = margv;
+
+ margc = 0;
+ for (cp = cmdline; margc < MAX_MARGV - 1 && *cp; ) {
+ while (isspace(*cp))
+ cp++;
+ if (*cp == '\0')
+ break;
+ *argp++ = cp;
+ margc += 1;
+ while (*cp != '\0' && !isspace(*cp))
+ cp++;
+ if (*cp == '\0')
+ break;
+ *cp++ = '\0';
+ }
+ *argp++ = 0;
+}
+
+#define HELPINDENT (sizeof ("directory"))
+
+/*
+ * Help command.
+ */
+void
+help(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register struct cmd *c;
+
+ if (argc == 1) {
+ register int i, j, w;
+ int columns, width = 0, lines;
+ extern int NCMDS;
+
+ printf("Commands may be abbreviated. Commands are:\n\n");
+ for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
+ int len = strlen(c->c_name);
+
+ if (len > width)
+ width = len;
+ }
+ width = (width + 8) &~ 7;
+ columns = 80 / width;
+ if (columns == 0)
+ columns = 1;
+ lines = (NCMDS + columns - 1) / columns;
+ for (i = 0; i < lines; i++) {
+ for (j = 0; j < columns; j++) {
+ c = cmdtab + j * lines + i;
+ printf("%s", c->c_name);
+ if (c + lines >= &cmdtab[NCMDS]) {
+ printf("\n");
+ break;
+ }
+ w = strlen(c->c_name);
+ while (w < width) {
+ w = (w + 8) &~ 7;
+ putchar('\t');
+ }
+ }
+ }
+ return;
+ }
+ while (--argc > 0) {
+ register char *arg;
+ arg = *++argv;
+ c = getcmd(arg);
+ if (c == (struct cmd *)-1)
+ printf("?Ambiguous help command %s\n", arg);
+ else if (c == (struct cmd *)0)
+ printf("?Invalid help command %s\n", arg);
+ else
+ printf("%-*s\t%s\n", (int)HELPINDENT,
+ c->c_name, c->c_help);
+ }
+}
diff --git a/usr.sbin/timed/timedc/timedc.h b/usr.sbin/timed/timedc/timedc.h
new file mode 100644
index 0000000..f6ee8a6
--- /dev/null
+++ b/usr.sbin/timed/timedc/timedc.h
@@ -0,0 +1,63 @@
+/*-
+ * 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.
+ * 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.
+ *
+ * @(#)timedc.h 8.1 (Berkeley) 6/6/93
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+
+#define ON 1
+#define OFF 0
+
+#define GOOD 1
+#define UNREACHABLE 2
+#define NONSTDTIME 3
+#define HOSTDOWN 0x7fffffff
+
+struct cmd {
+ char *c_name; /* command name */
+ char *c_help; /* help message */
+ void (*c_handler)(); /* routine to do the work */
+ int c_priv; /* privileged command */
+};
+
+#include "extern.h"
diff --git a/usr.sbin/traceroute/Makefile b/usr.sbin/traceroute/Makefile
new file mode 100644
index 0000000..489c862
--- /dev/null
+++ b/usr.sbin/traceroute/Makefile
@@ -0,0 +1,40 @@
+# $FreeBSD$
+
+TRACEROUTE_DISTDIR?= ${.CURDIR}/../../contrib/traceroute
+.PATH: ${TRACEROUTE_DISTDIR}
+
+PROG= traceroute
+MAN= traceroute.8
+SRCS= as.c version.c traceroute.c ifaddrlist.c findsaddr-socket.c
+BINOWN= root
+BINMODE=4555
+CLEANFILES= version.c
+
+CFLAGS+= -DHAVE_SYS_SELECT_H=1 -DHAVE_SYS_SOCKIO_H=1 \
+ -DHAVE_NET_ROUTE_H=1 -DHAVE_NET_IF_DL_H=1 \
+ -DHAVE_STRERROR=1 -DHAVE_USLEEP=1 \
+ -DHAVE_SYS_SYSCTL_H=1 \
+ -DHAVE_SETLINEBUF=1 -DHAVE_RAW_OPTIONS=1 \
+ -DHAVE_SOCKADDR_SA_LEN=1 -DHAVE_ICMP_NEXTMTU=1
+.if !defined(TRACEROUTE_NO_IPSEC)
+CFLAGS+= -DIPSEC
+.endif
+# RTT Jitter on the internet these days means printing 3 decimal places on
+# > 1000ms times is plain useless. Uncomment this to enable variable precision
+# reporting, ie: print a variable precision from 0.001ms through 1000ms
+# CFLAGS+= -DSANE_PRECISION
+
+.if !defined(TRACEROUTE_NO_IPSEC)
+DPADD= ${LIBIPSEC}
+LDADD= -lipsec
+.endif
+
+CFLAGS+= -I${TRACEROUTE_DISTDIR}/lbl
+
+version.c: ${TRACEROUTE_DISTDIR}/VERSION
+ @rm -f ${.TARGET}
+ head -1 ${TRACEROUTE_DISTDIR}/VERSION | \
+ sed -e 's/.*/char version[] = "&";/' \
+ > ${.TARGET}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/traceroute6/Makefile b/usr.sbin/traceroute6/Makefile
new file mode 100644
index 0000000..6ff72d2
--- /dev/null
+++ b/usr.sbin/traceroute6/Makefile
@@ -0,0 +1,26 @@
+# 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$
+
+PROG= traceroute6
+MAN= traceroute6.8
+BINOWN= root
+BINMODE= 4555
+
+CFLAGS+= -DIPSEC -DUSE_RFC2292BIS -DHAVE_POLL
+
+DPADD= ${LIBIPSEC}
+LDADD= -lipsec
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/traceroute6/traceroute6.8 b/usr.sbin/traceroute6/traceroute6.8
new file mode 100644
index 0000000..b6116f0
--- /dev/null
+++ b/usr.sbin/traceroute6/traceroute6.8
@@ -0,0 +1,178 @@
+.\" $KAME: traceroute6.8,v 1.10 2004/06/06 12:35:15 suz 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 TRACEROUTE6 8
+.Os
+.\"
+.Sh NAME
+.Nm traceroute6
+.Nd "print the route IPv6 packets will take to a network node"
+.\"
+.Sh SYNOPSIS
+.Nm
+.Bk -words
+.Op Fl dIlnNrvU
+.Ek
+.Bk -words
+.Op Fl f Ar firsthop
+.Ek
+.Bk -words
+.Op Fl g Ar gateway
+.Ek
+.Bk -words
+.Op Fl m Ar hoplimit
+.Ek
+.Bk -words
+.Op Fl p Ar port
+.Ek
+.Bk -words
+.Op Fl q Ar probes
+.Ek
+.Bk -words
+.Op Fl s Ar src
+.Ek
+.Bk -words
+.Op Fl w Ar waittime
+.Ek
+.Bk -words
+.Ar target
+.Op Ar datalen
+.Ek
+.\"
+.Sh DESCRIPTION
+The
+.Nm
+utility
+uses the IPv6 protocol hop limit field to elicit an ICMPv6 TIME_EXCEEDED
+response from each gateway along the path to some host.
+.Pp
+The only mandatory parameter is the destination host name or IPv6 address.
+The default probe datagram carries 12 bytes of payload,
+in addition to the IPv6 header.
+The size of the payload can be specified by giving a length
+(in bytes)
+after the destination host name.
+.Pp
+Other options are:
+.Bl -tag -width Ds
+.It Fl d
+Debug mode.
+.It Fl f Ar firsthop
+Specify how many hops to skip in trace.
+.It Fl g Ar gateway
+Specify intermediate gateway
+.Nm (
+uses routing header).
+.It Fl I
+Use ICMP6 ECHO instead of UDP datagrams.
+.It Fl l
+Print both host hostnames and numeric addresses.
+Normally
+.Nm
+prints only hostnames if
+.Fl n
+is not specified, and only numeric addresses if
+.Fl n
+is specified.
+.It Fl m Ar hoplimit
+Specify maximum hoplimit, up to 255.
+The default is 30 hops.
+.It Fl n
+Do not resolve numeric address to hostname.
+.It Fl N
+Use a packet with no upper layer header for the probes,
+instead of UDP datagrams.
+.It Fl p Ar port
+Set UDP port number to
+.Ar port .
+.It Fl q Ar probes
+Set the number of probe per hop count to
+.Ar probes .
+.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-connected network,
+an error is returned.
+This option corresponds to the
+.Dv SO_DONTROUTE
+socket option;
+it 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 a routing daemon).
+.It Fl s Ar src
+.Ar Src
+specifies the source IPv6 address to be used.
+.It Fl U
+Use UDP datagrams for the probes.
+This is the default.
+.It Fl v
+Be verbose.
+.It Fl w Ar waittime
+Specify the delay time between probes.
+.El
+.Pp
+This program prints the route to the given destination and the round-trip
+time to each gateway, in the same manner as traceroute.
+.Pp
+Here is a list of possible annotations after the round-trip time for each gateway:
+.Bl -hang -offset indent
+.It !N
+Destination Unreachable - No Route to Host.
+.It !P
+Destination Unreachable - Administratively Prohibited.
+.It !S
+Destination Unreachable - Not a Neighbour.
+.It !A
+Destination Unreachable - Address Unreachable.
+.It !\&
+This is printed if the hop limit is <= 1 on a port unreachable message.
+This means that the packet got to the destination,
+but that the reply had a hop limit that was just large enough to
+allow it to get back to the source of the traceroute6.
+This was more interesting in the IPv4 case,
+where some IP stack bugs could be identified by this behaviour.
+.El
+.\"
+.Sh EXIT STATUS
+The
+.Nm
+utility will exit with 0 on success, and non-zero on errors.
+.\"
+.Sh SEE ALSO
+.Xr ping 8 ,
+.Xr ping6 8 ,
+.Xr traceroute 8
+.\"
+.Sh HISTORY
+The
+.Nm
+utility first appeared in WIDE hydrangea IPv6 protocol stack kit.
diff --git a/usr.sbin/traceroute6/traceroute6.c b/usr.sbin/traceroute6/traceroute6.c
new file mode 100644
index 0000000..d06502f
--- /dev/null
+++ b/usr.sbin/traceroute6/traceroute6.c
@@ -0,0 +1,1432 @@
+/* $KAME: traceroute6.c,v 1.68 2004/01/25 11:16:12 suz 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.
+ */
+
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Van Jacobson.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 char copyright[] =
+"@(#) Copyright (c) 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)traceroute.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+ * traceroute host - trace the route ip packets follow going to "host".
+ *
+ * Attempt to trace the route an ip packet would follow to some
+ * internet host. We find out intermediate hops by launching probe
+ * packets with a small ttl (time to live) then listening for an
+ * icmp "time exceeded" reply from a gateway. We start our probes
+ * with a ttl of one and increase by one until we get an icmp "port
+ * unreachable" (which means we got to "host") or hit a max (which
+ * defaults to 30 hops & can be changed with the -m flag). Three
+ * probes (change with -q flag) are sent at each ttl setting and a
+ * line is printed showing the ttl, address of the gateway and
+ * round trip time of each probe. If the probe answers come from
+ * different gateways, the address of each responding system will
+ * be printed. If there is no response within a 5 sec. timeout
+ * interval (changed with the -w flag), a "*" is printed for that
+ * probe.
+ *
+ * Probe packets are UDP format. We don't want the destination
+ * host to process them so the destination port is set to an
+ * unlikely value (if some clod on the destination is using that
+ * value, it can be changed with the -p flag).
+ *
+ * A sample use might be:
+ *
+ * [yak 71]% traceroute nis.nsf.net.
+ * traceroute to nis.nsf.net (35.1.1.48), 30 hops max, 56 byte packet
+ * 1 helios.ee.lbl.gov (128.3.112.1) 19 ms 19 ms 0 ms
+ * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms
+ * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 39 ms 19 ms
+ * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 39 ms
+ * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 39 ms 39 ms 39 ms
+ * 6 128.32.197.4 (128.32.197.4) 40 ms 59 ms 59 ms
+ * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 59 ms
+ * 8 129.140.70.13 (129.140.70.13) 99 ms 99 ms 80 ms
+ * 9 129.140.71.6 (129.140.71.6) 139 ms 239 ms 319 ms
+ * 10 129.140.81.7 (129.140.81.7) 220 ms 199 ms 199 ms
+ * 11 nic.merit.edu (35.1.1.48) 239 ms 239 ms 239 ms
+ *
+ * Note that lines 2 & 3 are the same. This is due to a buggy
+ * kernel on the 2nd hop system -- lbl-csam.arpa -- that forwards
+ * packets with a zero ttl.
+ *
+ * A more interesting example is:
+ *
+ * [yak 72]% traceroute allspice.lcs.mit.edu.
+ * traceroute to allspice.lcs.mit.edu (18.26.0.115), 30 hops max
+ * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms
+ * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 19 ms 19 ms
+ * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 19 ms
+ * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 19 ms 39 ms 39 ms
+ * 5 ccn-nerif22.Berkeley.EDU (128.32.168.22) 20 ms 39 ms 39 ms
+ * 6 128.32.197.4 (128.32.197.4) 59 ms 119 ms 39 ms
+ * 7 131.119.2.5 (131.119.2.5) 59 ms 59 ms 39 ms
+ * 8 129.140.70.13 (129.140.70.13) 80 ms 79 ms 99 ms
+ * 9 129.140.71.6 (129.140.71.6) 139 ms 139 ms 159 ms
+ * 10 129.140.81.7 (129.140.81.7) 199 ms 180 ms 300 ms
+ * 11 129.140.72.17 (129.140.72.17) 300 ms 239 ms 239 ms
+ * 12 * * *
+ * 13 128.121.54.72 (128.121.54.72) 259 ms 499 ms 279 ms
+ * 14 * * *
+ * 15 * * *
+ * 16 * * *
+ * 17 * * *
+ * 18 ALLSPICE.LCS.MIT.EDU (18.26.0.115) 339 ms 279 ms 279 ms
+ *
+ * (I start to see why I'm having so much trouble with mail to
+ * MIT.) Note that the gateways 12, 14, 15, 16 & 17 hops away
+ * either don't send ICMP "time exceeded" messages or send them
+ * with a ttl too small to reach us. 14 - 17 are running the
+ * MIT C Gateway code that doesn't send "time exceeded"s. God
+ * only knows what's going on with 12.
+ *
+ * The silent gateway 12 in the above may be the result of a bug in
+ * the 4.[23]BSD network code (and its derivatives): 4.x (x <= 3)
+ * sends an unreachable message using whatever ttl remains in the
+ * original datagram. Since, for gateways, the remaining ttl is
+ * zero, the icmp "time exceeded" is guaranteed to not make it back
+ * to us. The behavior of this bug is slightly more interesting
+ * when it appears on the destination system:
+ *
+ * 1 helios.ee.lbl.gov (128.3.112.1) 0 ms 0 ms 0 ms
+ * 2 lilac-dmc.Berkeley.EDU (128.32.216.1) 39 ms 19 ms 39 ms
+ * 3 lilac-dmc.Berkeley.EDU (128.32.216.1) 19 ms 39 ms 19 ms
+ * 4 ccngw-ner-cc.Berkeley.EDU (128.32.136.23) 39 ms 40 ms 19 ms
+ * 5 ccn-nerif35.Berkeley.EDU (128.32.168.35) 39 ms 39 ms 39 ms
+ * 6 csgw.Berkeley.EDU (128.32.133.254) 39 ms 59 ms 39 ms
+ * 7 * * *
+ * 8 * * *
+ * 9 * * *
+ * 10 * * *
+ * 11 * * *
+ * 12 * * *
+ * 13 rip.Berkeley.EDU (128.32.131.22) 59 ms ! 39 ms ! 39 ms !
+ *
+ * Notice that there are 12 "gateways" (13 is the final
+ * destination) and exactly the last half of them are "missing".
+ * What's really happening is that rip (a Sun-3 running Sun OS3.5)
+ * is using the ttl from our arriving datagram as the ttl in its
+ * icmp reply. So, the reply will time out on the return path
+ * (with no notice sent to anyone since icmp's aren't sent for
+ * icmp's) until we probe with a ttl that's at least twice the path
+ * length. I.e., rip is really only 7 hops away. A reply that
+ * returns with a ttl of 1 is a clue this problem exists.
+ * Traceroute prints a "!" after the time if the ttl is <= 1.
+ * Since vendors ship a lot of obsolete (DEC's Ultrix, Sun 3.x) or
+ * non-standard (HPUX) software, expect to see this problem
+ * frequently and/or take care picking the target host of your
+ * probes.
+ *
+ * Other possible annotations after the time are !H, !N, !P (got a host,
+ * network or protocol unreachable, respectively), !S or !F (source
+ * route failed or fragmentation needed -- neither of these should
+ * ever occur and the associated gateway is busted if you see one). If
+ * almost all the probes result in some kind of unreachable, traceroute
+ * will give up and exit.
+ *
+ * Notes
+ * -----
+ * This program must be run by root or be setuid. (I suggest that
+ * you *don't* make it setuid -- casual use could result in a lot
+ * of unnecessary traffic on our poor, congested nets.)
+ *
+ * This program requires a kernel mod that does not appear in any
+ * system available from Berkeley: A raw ip socket using proto
+ * IPPROTO_RAW must interpret the data sent as an ip datagram (as
+ * opposed to data to be wrapped in an ip datagram). See the README
+ * file that came with the source to this program for a description
+ * of the mods I made to /sys/netinet/raw_ip.c. Your mileage may
+ * vary. But, again, ANY 4.x (x < 4) BSD KERNEL WILL HAVE TO BE
+ * MODIFIED TO RUN THIS PROGRAM.
+ *
+ * The udp port usage may appear bizarre (well, ok, it is bizarre).
+ * The problem is that an icmp message only contains 8 bytes of
+ * data from the original datagram. 8 bytes is the size of a udp
+ * header so, if we want to associate replies with the original
+ * datagram, the necessary information must be encoded into the
+ * udp header (the ip id could be used but there's no way to
+ * interlock with the kernel's assignment of ip id's and, anyway,
+ * it would have taken a lot more kernel hacking to allow this
+ * code to set the ip id). So, to allow two or more users to
+ * use traceroute simultaneously, we use this task's pid as the
+ * source port (the high bit is set to move the port number out
+ * of the "likely" range). To keep track of which probe is being
+ * replied to (so times and/or hop counts don't get confused by a
+ * reply that was delayed in transit), we increment the destination
+ * port number before each probe.
+ *
+ * Don't use this as a coding example. I was trying to find a
+ * routing problem and this code sort-of popped out after 48 hours
+ * without sleep. I was amazed it ever compiled, much less ran.
+ *
+ * I stole the idea for this program from Steve Deering. Since
+ * the first release, I've learned that had I attended the right
+ * IETF working group meetings, I also could have stolen it from Guy
+ * Almes or Matt Mathis. I don't know (or care) who came up with
+ * the idea first. I envy the originators' perspicacity and I'm
+ * glad they didn't keep the idea a secret.
+ *
+ * Tim Seaver, Ken Adelman and C. Philip Wood provided bug fixes and/or
+ * enhancements to the original distribution.
+ *
+ * I've hacked up a round-trip-route version of this that works by
+ * sending a loose-source-routed udp datagram through the destination
+ * back to yourself. Unfortunately, SO many gateways botch source
+ * routing, the thing is almost worthless. Maybe one day...
+ *
+ * -- Van Jacobson (van@helios.ee.lbl.gov)
+ * Tue Dec 20 03:50:13 PST 1988
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+
+#include <netdb.h>
+#include <stdio.h>
+#include <err.h>
+#ifdef HAVE_POLL
+#include <poll.h>
+#endif
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+#include <netinet/udp.h>
+
+#ifdef IPSEC
+#include <net/route.h>
+#include <netipsec/ipsec.h>
+#endif
+
+#define DUMMY_PORT 10010
+
+#define MAXPACKET 65535 /* max ip packet size */
+
+#ifndef HAVE_GETIPNODEBYNAME
+#define getipnodebyname(x, y, z, u) gethostbyname2((x), (y))
+#define freehostent(x)
+#endif
+
+/*
+ * format of a (udp) probe packet.
+ */
+struct tv32 {
+ u_int32_t tv32_sec;
+ u_int32_t tv32_usec;
+};
+
+struct opacket {
+ u_char seq; /* sequence number of this packet */
+ u_char hops; /* hop limit of the packet */
+ u_char pad[2];
+ struct tv32 tv; /* time packet left */
+} __attribute__((__packed__));
+
+u_char packet[512]; /* last inbound (icmp) packet */
+struct opacket *outpacket; /* last output (udp) packet */
+
+int main(int, char *[]);
+int wait_for_reply(int, struct msghdr *);
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+int setpolicy(int so, char *policy);
+#endif
+#endif
+void send_probe(int, u_long);
+void *get_uphdr(struct ip6_hdr *, u_char *);
+int get_hoplim(struct msghdr *);
+double deltaT(struct timeval *, struct timeval *);
+char *pr_type(int);
+int packet_ok(struct msghdr *, int, int);
+void print(struct msghdr *, int);
+const char *inetname(struct sockaddr *);
+void usage(void);
+
+int rcvsock; /* receive (icmp) socket file descriptor */
+int sndsock; /* send (udp) socket file descriptor */
+
+struct msghdr rcvmhdr;
+struct iovec rcviov[2];
+int rcvhlim;
+struct in6_pktinfo *rcvpktinfo;
+
+struct sockaddr_in6 Src, Dst, Rcv;
+u_long datalen; /* How much data */
+#define ICMP6ECHOLEN 8
+/* XXX: 2064 = 127(max hops in type 0 rthdr) * sizeof(ip6_hdr) + 16(margin) */
+char rtbuf[2064];
+#ifdef USE_RFC2292BIS
+struct ip6_rthdr *rth;
+#endif
+struct cmsghdr *cmsg;
+
+char *source = 0;
+char *hostname;
+
+u_long nprobes = 3;
+u_long first_hop = 1;
+u_long max_hops = 30;
+u_int16_t srcport;
+u_int16_t port = 32768+666; /* start udp dest port # for probe packets */
+u_int16_t ident;
+int options; /* socket options */
+int verbose;
+int waittime = 5; /* time to wait for response (in seconds) */
+int nflag; /* print addresses numerically */
+int useproto = IPPROTO_UDP; /* protocol to use to send packet */
+int lflag; /* print both numerical address & hostname */
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int mib[4] = { CTL_NET, PF_INET6, IPPROTO_IPV6, IPV6CTL_DEFHLIM };
+ char hbuf[NI_MAXHOST], src0[NI_MAXHOST], *ep;
+ int ch, i, on = 1, seq, rcvcmsglen, error, minlen;
+ struct addrinfo hints, *res;
+ static u_char *rcvcmsgbuf;
+ u_long probe, hops, lport;
+ struct hostent *hp;
+ size_t size;
+ uid_t uid;
+
+ /*
+ * Receive ICMP
+ */
+ if ((rcvsock = socket(AF_INET6, SOCK_RAW, IPPROTO_ICMPV6)) < 0) {
+ perror("socket(ICMPv6)");
+ exit(5);
+ }
+
+ size = sizeof(i);
+ (void) sysctl(mib, sizeof(mib)/sizeof(mib[0]), &i, &size, NULL, 0);
+ max_hops = i;
+
+ /* specify to tell receiving interface */
+#ifdef IPV6_RECVPKTINFO
+ if (setsockopt(rcvsock, IPPROTO_IPV6, IPV6_RECVPKTINFO, &on,
+ sizeof(on)) < 0)
+ err(1, "setsockopt(IPV6_RECVPKTINFO)");
+#else /* old adv. API */
+ if (setsockopt(rcvsock, IPPROTO_IPV6, IPV6_PKTINFO, &on,
+ sizeof(on)) < 0)
+ err(1, "setsockopt(IPV6_PKTINFO)");
+#endif
+
+ /* specify to tell value of hoplimit field of received IP6 hdr */
+#ifdef IPV6_RECVHOPLIMIT
+ if (setsockopt(rcvsock, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &on,
+ sizeof(on)) < 0)
+ err(1, "setsockopt(IPV6_RECVHOPLIMIT)");
+#else /* old adv. API */
+ if (setsockopt(rcvsock, IPPROTO_IPV6, IPV6_HOPLIMIT, &on,
+ sizeof(on)) < 0)
+ err(1, "setsockopt(IPV6_HOPLIMIT)");
+#endif
+
+ seq = 0;
+
+ while ((ch = getopt(argc, argv, "df:g:Ilm:nNp:q:rs:Uvw:")) != -1)
+ switch (ch) {
+ case 'd':
+ options |= SO_DEBUG;
+ break;
+ case 'f':
+ ep = NULL;
+ errno = 0;
+ first_hop = strtoul(optarg, &ep, 0);
+ if (errno || !*optarg || *ep || first_hop > 255) {
+ fprintf(stderr,
+ "traceroute6: invalid min hoplimit.\n");
+ exit(1);
+ }
+ break;
+ case 'g':
+ hp = getipnodebyname(optarg, AF_INET6, 0, &h_errno);
+ if (hp == NULL) {
+ fprintf(stderr,
+ "traceroute6: unknown host %s\n", optarg);
+ exit(1);
+ }
+#ifdef USE_RFC2292BIS
+ if (rth == NULL) {
+ /*
+ * XXX: We can't detect the number of
+ * intermediate nodes yet.
+ */
+ if ((rth = inet6_rth_init((void *)rtbuf,
+ sizeof(rtbuf), IPV6_RTHDR_TYPE_0,
+ 0)) == NULL) {
+ fprintf(stderr,
+ "inet6_rth_init failed.\n");
+ exit(1);
+ }
+ }
+ if (inet6_rth_add((void *)rth,
+ (struct in6_addr *)hp->h_addr)) {
+ fprintf(stderr,
+ "inet6_rth_add failed for %s\n",
+ optarg);
+ exit(1);
+ }
+#else /* old advanced API */
+ if (cmsg == NULL)
+ cmsg = inet6_rthdr_init(rtbuf, IPV6_RTHDR_TYPE_0);
+ inet6_rthdr_add(cmsg, (struct in6_addr *)hp->h_addr,
+ IPV6_RTHDR_LOOSE);
+#endif
+ freehostent(hp);
+ break;
+ case 'I':
+ useproto = IPPROTO_ICMPV6;
+ ident = htons(getpid() & 0xffff); /* same as ping6 */
+ break;
+ case 'l':
+ lflag++;
+ break;
+ case 'm':
+ ep = NULL;
+ errno = 0;
+ max_hops = strtoul(optarg, &ep, 0);
+ if (errno || !*optarg || *ep || max_hops > 255) {
+ fprintf(stderr,
+ "traceroute6: invalid max hoplimit.\n");
+ exit(1);
+ }
+ break;
+ case 'n':
+ nflag++;
+ break;
+ case 'N':
+ useproto = IPPROTO_NONE;
+ break;
+ case 'p':
+ ep = NULL;
+ errno = 0;
+ lport = strtoul(optarg, &ep, 0);
+ if (errno || !*optarg || *ep) {
+ fprintf(stderr, "traceroute6: invalid port.\n");
+ exit(1);
+ }
+ if (lport == 0 || lport != (lport & 0xffff)) {
+ fprintf(stderr,
+ "traceroute6: port out of range.\n");
+ exit(1);
+ }
+ port = lport & 0xffff;
+ break;
+ case 'q':
+ ep = NULL;
+ errno = 0;
+ nprobes = strtoul(optarg, &ep, 0);
+ if (errno || !*optarg || *ep) {
+ fprintf(stderr,
+ "traceroute6: invalid nprobes.\n");
+ exit(1);
+ }
+ if (nprobes < 1) {
+ fprintf(stderr,
+ "traceroute6: nprobes must be >0.\n");
+ exit(1);
+ }
+ break;
+ case 'r':
+ options |= SO_DONTROUTE;
+ break;
+ case 's':
+ /*
+ * set the ip source address of the outbound
+ * probe (e.g., on a multi-homed host).
+ */
+ source = optarg;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'U':
+ useproto = IPPROTO_UDP;
+ break;
+ case 'w':
+ ep = NULL;
+ errno = 0;
+ waittime = strtoul(optarg, &ep, 0);
+ if (errno || !*optarg || *ep) {
+ fprintf(stderr,
+ "traceroute6: invalid wait time.\n");
+ exit(1);
+ }
+ if (waittime < 1) {
+ fprintf(stderr,
+ "traceroute6: wait must be >= 1 sec.\n");
+ exit(1);
+ }
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ /*
+ * Open socket to send probe packets.
+ */
+ switch (useproto) {
+ case IPPROTO_ICMPV6:
+ sndsock = rcvsock;
+ break;
+ case IPPROTO_UDP:
+ if ((sndsock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ perror("socket(SOCK_DGRAM)");
+ exit(5);
+ }
+ break;
+ case IPPROTO_NONE:
+ if ((sndsock = socket(AF_INET6, SOCK_RAW, IPPROTO_NONE)) < 0) {
+ perror("socket(SOCK_RAW)");
+ exit(5);
+ }
+ break;
+ default:
+ fprintf(stderr, "traceroute6: unknown probe protocol %d",
+ useproto);
+ exit(5);
+ }
+ if (max_hops < first_hop) {
+ fprintf(stderr,
+ "traceroute6: max hoplimit must be larger than first hoplimit.\n");
+ exit(1);
+ }
+
+ /* revoke privs */
+ uid = getuid();
+ if (setresuid(uid, uid, uid) == -1) {
+ perror("setresuid");
+ exit(1);
+ }
+
+
+ if (argc < 1 || argc > 2)
+ usage();
+
+#if 1
+ setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
+#else
+ setlinebuf(stdout);
+#endif
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = PF_INET6;
+ hints.ai_socktype = SOCK_RAW;
+ hints.ai_protocol = IPPROTO_ICMPV6;
+ hints.ai_flags = AI_CANONNAME;
+ error = getaddrinfo(*argv, NULL, &hints, &res);
+ if (error) {
+ fprintf(stderr,
+ "traceroute6: %s\n", gai_strerror(error));
+ exit(1);
+ }
+ if (res->ai_addrlen != sizeof(Dst)) {
+ fprintf(stderr,
+ "traceroute6: size of sockaddr mismatch\n");
+ exit(1);
+ }
+ memcpy(&Dst, res->ai_addr, res->ai_addrlen);
+ hostname = res->ai_canonname ? strdup(res->ai_canonname) : *argv;
+ if (!hostname) {
+ fprintf(stderr, "traceroute6: not enough core\n");
+ exit(1);
+ }
+ if (res->ai_next) {
+ if (getnameinfo(res->ai_addr, res->ai_addrlen, hbuf,
+ sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0)
+ strlcpy(hbuf, "?", sizeof(hbuf));
+ fprintf(stderr, "traceroute6: Warning: %s has multiple "
+ "addresses; using %s\n", hostname, hbuf);
+ }
+
+ if (*++argv) {
+ ep = NULL;
+ errno = 0;
+ datalen = strtoul(*argv, &ep, 0);
+ if (errno || !*argv || *ep) {
+ fprintf(stderr,
+ "traceroute6: invalid packet length.\n");
+ exit(1);
+ }
+ }
+ switch (useproto) {
+ case IPPROTO_ICMPV6:
+ minlen = ICMP6ECHOLEN + sizeof(struct tv32);
+ break;
+ case IPPROTO_UDP:
+ minlen = sizeof(struct opacket);
+ break;
+ case IPPROTO_NONE:
+ minlen = 0;
+ datalen = 0;
+ break;
+ default:
+ fprintf(stderr, "traceroute6: unknown probe protocol %d.\n",
+ useproto);
+ exit(1);
+ }
+ if (datalen < minlen)
+ datalen = minlen;
+ else if (datalen >= MAXPACKET) {
+ fprintf(stderr,
+ "traceroute6: packet size must be %d <= s < %ld.\n",
+ minlen, (long)MAXPACKET);
+ exit(1);
+ }
+ outpacket = (struct opacket *)malloc((unsigned)datalen);
+ if (!outpacket) {
+ perror("malloc");
+ exit(1);
+ }
+ (void) bzero((char *)outpacket, datalen);
+
+ /* initialize msghdr for receiving packets */
+ rcviov[0].iov_base = (caddr_t)packet;
+ rcviov[0].iov_len = sizeof(packet);
+ rcvmhdr.msg_name = (caddr_t)&Rcv;
+ rcvmhdr.msg_namelen = sizeof(Rcv);
+ rcvmhdr.msg_iov = rcviov;
+ rcvmhdr.msg_iovlen = 1;
+ rcvcmsglen = CMSG_SPACE(sizeof(struct in6_pktinfo)) +
+ CMSG_SPACE(sizeof(int));
+ if ((rcvcmsgbuf = malloc(rcvcmsglen)) == NULL) {
+ fprintf(stderr, "traceroute6: malloc failed\n");
+ exit(1);
+ }
+ rcvmhdr.msg_control = (caddr_t) rcvcmsgbuf;
+ rcvmhdr.msg_controllen = rcvcmsglen;
+
+ if (options & SO_DEBUG)
+ (void) setsockopt(rcvsock, SOL_SOCKET, SO_DEBUG,
+ (char *)&on, sizeof(on));
+ if (options & SO_DONTROUTE)
+ (void) setsockopt(rcvsock, SOL_SOCKET, SO_DONTROUTE,
+ (char *)&on, sizeof(on));
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+ /*
+ * do not raise error even if setsockopt fails, kernel may have ipsec
+ * turned off.
+ */
+ if (setpolicy(rcvsock, "in bypass") < 0)
+ errx(1, "%s", ipsec_strerror());
+ if (setpolicy(rcvsock, "out bypass") < 0)
+ errx(1, "%s", ipsec_strerror());
+#else
+ {
+ int level = IPSEC_LEVEL_NONE;
+
+ (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_ESP_TRANS_LEVEL, &level,
+ sizeof(level));
+ (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_ESP_NETWORK_LEVEL, &level,
+ sizeof(level));
+#ifdef IP_AUTH_TRANS_LEVEL
+ (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_AUTH_TRANS_LEVEL, &level,
+ sizeof(level));
+#else
+ (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_AUTH_LEVEL, &level,
+ sizeof(level));
+#endif
+#ifdef IP_AUTH_NETWORK_LEVEL
+ (void)setsockopt(rcvsock, IPPROTO_IPV6, IPV6_AUTH_NETWORK_LEVEL, &level,
+ sizeof(level));
+#endif
+ }
+#endif /*IPSEC_POLICY_IPSEC*/
+#endif /*IPSEC*/
+
+#ifdef SO_SNDBUF
+ i = datalen;
+ if (setsockopt(sndsock, SOL_SOCKET, SO_SNDBUF, (char *)&i,
+ sizeof(i)) < 0 && useproto != IPPROTO_NONE) {
+ perror("setsockopt(SO_SNDBUF)");
+ exit(6);
+ }
+#endif /* SO_SNDBUF */
+ if (options & SO_DEBUG)
+ (void) setsockopt(sndsock, SOL_SOCKET, SO_DEBUG,
+ (char *)&on, sizeof(on));
+ if (options & SO_DONTROUTE)
+ (void) setsockopt(sndsock, SOL_SOCKET, SO_DONTROUTE,
+ (char *)&on, sizeof(on));
+#ifdef USE_RFC2292BIS
+ if (rth) {/* XXX: there is no library to finalize the header... */
+ rth->ip6r_len = rth->ip6r_segleft * 2;
+ if (setsockopt(sndsock, IPPROTO_IPV6, IPV6_RTHDR,
+ (void *)rth, (rth->ip6r_len + 1) << 3)) {
+ fprintf(stderr, "setsockopt(IPV6_RTHDR): %s\n",
+ strerror(errno));
+ exit(1);
+ }
+ }
+#else /* old advanced API */
+ if (cmsg != NULL) {
+ inet6_rthdr_lasthop(cmsg, IPV6_RTHDR_LOOSE);
+ if (setsockopt(sndsock, IPPROTO_IPV6, IPV6_PKTOPTIONS,
+ rtbuf, cmsg->cmsg_len) < 0) {
+ fprintf(stderr, "setsockopt(IPV6_PKTOPTIONS): %s\n",
+ strerror(errno));
+ exit(1);
+ }
+ }
+#endif /* USE_RFC2292BIS */
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+ /*
+ * do not raise error even if setsockopt fails, kernel may have ipsec
+ * turned off.
+ */
+ if (setpolicy(sndsock, "in bypass") < 0)
+ errx(1, "%s", ipsec_strerror());
+ if (setpolicy(sndsock, "out bypass") < 0)
+ errx(1, "%s", ipsec_strerror());
+#else
+ {
+ int level = IPSEC_LEVEL_BYPASS;
+
+ (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_ESP_TRANS_LEVEL, &level,
+ sizeof(level));
+ (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_ESP_NETWORK_LEVEL, &level,
+ sizeof(level));
+#ifdef IP_AUTH_TRANS_LEVEL
+ (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_AUTH_TRANS_LEVEL, &level,
+ sizeof(level));
+#else
+ (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_AUTH_LEVEL, &level,
+ sizeof(level));
+#endif
+#ifdef IP_AUTH_NETWORK_LEVEL
+ (void)setsockopt(sndsock, IPPROTO_IPV6, IPV6_AUTH_NETWORK_LEVEL, &level,
+ sizeof(level));
+#endif
+ }
+#endif /*IPSEC_POLICY_IPSEC*/
+#endif /*IPSEC*/
+
+ /*
+ * Source selection
+ */
+ bzero(&Src, sizeof(Src));
+ if (source) {
+ struct addrinfo hints, *res;
+ int error;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET6;
+ hints.ai_socktype = SOCK_DGRAM; /*dummy*/
+ hints.ai_flags = AI_NUMERICHOST;
+ error = getaddrinfo(source, "0", &hints, &res);
+ if (error) {
+ printf("traceroute6: %s: %s\n", source,
+ gai_strerror(error));
+ exit(1);
+ }
+ if (res->ai_addrlen > sizeof(Src)) {
+ printf("traceroute6: %s: %s\n", source,
+ gai_strerror(error));
+ exit(1);
+ }
+ memcpy(&Src, res->ai_addr, res->ai_addrlen);
+ freeaddrinfo(res);
+ } else {
+ struct sockaddr_in6 Nxt;
+ int dummy;
+ socklen_t len;
+
+ Nxt = Dst;
+ Nxt.sin6_port = htons(DUMMY_PORT);
+ if (cmsg != NULL)
+ bcopy(inet6_rthdr_getaddr(cmsg, 1), &Nxt.sin6_addr,
+ sizeof(Nxt.sin6_addr));
+ if ((dummy = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
+ perror("socket");
+ exit(1);
+ }
+ if (connect(dummy, (struct sockaddr *)&Nxt, Nxt.sin6_len) < 0) {
+ perror("connect");
+ exit(1);
+ }
+ len = sizeof(Src);
+ if (getsockname(dummy, (struct sockaddr *)&Src, &len) < 0) {
+ perror("getsockname");
+ exit(1);
+ }
+ if (getnameinfo((struct sockaddr *)&Src, Src.sin6_len,
+ src0, sizeof(src0), NULL, 0, NI_NUMERICHOST)) {
+ fprintf(stderr, "getnameinfo failed for source\n");
+ exit(1);
+ }
+ source = src0;
+ close(dummy);
+ }
+
+ Src.sin6_port = htons(0);
+ if (bind(sndsock, (struct sockaddr *)&Src, Src.sin6_len) < 0) {
+ perror("bind");
+ exit(1);
+ }
+
+ {
+ socklen_t len;
+
+ len = sizeof(Src);
+ if (getsockname(sndsock, (struct sockaddr *)&Src, &len) < 0) {
+ perror("getsockname");
+ exit(1);
+ }
+ srcport = ntohs(Src.sin6_port);
+ }
+
+ /*
+ * Message to users
+ */
+ if (getnameinfo((struct sockaddr *)&Dst, Dst.sin6_len, hbuf,
+ sizeof(hbuf), NULL, 0, NI_NUMERICHOST))
+ strlcpy(hbuf, "(invalid)", sizeof(hbuf));
+ fprintf(stderr, "traceroute6");
+ fprintf(stderr, " to %s (%s)", hostname, hbuf);
+ if (source)
+ fprintf(stderr, " from %s", source);
+ fprintf(stderr, ", %lu hops max, %lu byte packets\n",
+ max_hops, datalen);
+ (void) fflush(stderr);
+
+ if (first_hop > 1)
+ printf("Skipping %lu intermediate hops\n", first_hop - 1);
+
+ /*
+ * Main loop
+ */
+ for (hops = first_hop; hops <= max_hops; ++hops) {
+ struct in6_addr lastaddr;
+ int got_there = 0;
+ int unreachable = 0;
+
+ printf("%2lu ", hops);
+ bzero(&lastaddr, sizeof(lastaddr));
+ for (probe = 0; probe < nprobes; ++probe) {
+ int cc;
+ struct timeval t1, t2;
+
+ (void) gettimeofday(&t1, NULL);
+ send_probe(++seq, hops);
+ while ((cc = wait_for_reply(rcvsock, &rcvmhdr))) {
+ (void) gettimeofday(&t2, NULL);
+ if ((i = packet_ok(&rcvmhdr, cc, seq))) {
+ if (!IN6_ARE_ADDR_EQUAL(&Rcv.sin6_addr,
+ &lastaddr)) {
+ if (probe > 0)
+ fputs("\n ", stdout);
+ print(&rcvmhdr, cc);
+ lastaddr = Rcv.sin6_addr;
+ }
+ printf(" %.3f ms", deltaT(&t1, &t2));
+ switch (i - 1) {
+ case ICMP6_DST_UNREACH_NOROUTE:
+ ++unreachable;
+ printf(" !N");
+ break;
+ case ICMP6_DST_UNREACH_ADMIN:
+ ++unreachable;
+ printf(" !P");
+ break;
+ case ICMP6_DST_UNREACH_NOTNEIGHBOR:
+ ++unreachable;
+ printf(" !S");
+ break;
+ case ICMP6_DST_UNREACH_ADDR:
+ ++unreachable;
+ printf(" !A");
+ break;
+ case ICMP6_DST_UNREACH_NOPORT:
+ if (rcvhlim >= 0 &&
+ rcvhlim <= 1)
+ printf(" !");
+ ++got_there;
+ break;
+ }
+ break;
+ }
+ }
+ if (cc == 0)
+ printf(" *");
+ (void) fflush(stdout);
+ }
+ putchar('\n');
+ if (got_there ||
+ (unreachable > 0 && unreachable >= ((nprobes + 1) / 2))) {
+ exit(0);
+ }
+ }
+
+ exit(0);
+}
+
+int
+wait_for_reply(sock, mhdr)
+ int sock;
+ struct msghdr *mhdr;
+{
+#ifdef HAVE_POLL
+ struct pollfd pfd[1];
+ int cc = 0;
+
+ pfd[0].fd = sock;
+ pfd[0].events = POLLIN;
+ pfd[0].revents = 0;
+
+ if (poll(pfd, 1, waittime * 1000) > 0)
+ cc = recvmsg(rcvsock, mhdr, 0);
+
+ return(cc);
+#else
+ fd_set *fdsp;
+ struct timeval wait;
+ int cc = 0, fdsn;
+
+ fdsn = howmany(sock + 1, NFDBITS) * sizeof(fd_mask);
+ if ((fdsp = (fd_set *)malloc(fdsn)) == NULL)
+ err(1, "malloc");
+ memset(fdsp, 0, fdsn);
+ FD_SET(sock, fdsp);
+ wait.tv_sec = waittime; wait.tv_usec = 0;
+
+ if (select(sock+1, fdsp, (fd_set *)0, (fd_set *)0, &wait) > 0)
+ cc = recvmsg(rcvsock, mhdr, 0);
+
+ free(fdsp);
+ return(cc);
+#endif
+}
+
+#ifdef IPSEC
+#ifdef IPSEC_POLICY_IPSEC
+int
+setpolicy(so, policy)
+ int so;
+ char *policy;
+{
+ char *buf;
+
+ buf = ipsec_set_policy(policy, strlen(policy));
+ if (buf == NULL) {
+ warnx("%s", ipsec_strerror());
+ return -1;
+ }
+ (void)setsockopt(so, IPPROTO_IPV6, IPV6_IPSEC_POLICY,
+ buf, ipsec_get_policylen(buf));
+
+ free(buf);
+
+ return 0;
+}
+#endif
+#endif
+
+void
+send_probe(seq, hops)
+ int seq;
+ u_long hops;
+{
+ struct icmp6_hdr *icp;
+ struct opacket *op;
+ struct timeval tv;
+ struct tv32 tv32;
+ int i;
+
+ i = hops;
+ if (setsockopt(sndsock, IPPROTO_IPV6, IPV6_UNICAST_HOPS,
+ (char *)&i, sizeof(i)) < 0) {
+ perror("setsockopt IPV6_UNICAST_HOPS");
+ }
+
+ Dst.sin6_port = htons(port + seq);
+ (void) gettimeofday(&tv, NULL);
+ tv32.tv32_sec = htonl(tv.tv_sec);
+ tv32.tv32_usec = htonl(tv.tv_usec);
+
+ switch (useproto) {
+ case IPPROTO_ICMPV6:
+ icp = (struct icmp6_hdr *)outpacket;
+
+ icp->icmp6_type = ICMP6_ECHO_REQUEST;
+ icp->icmp6_code = 0;
+ icp->icmp6_cksum = 0;
+ icp->icmp6_id = ident;
+ icp->icmp6_seq = htons(seq);
+ bcopy(&tv32, ((u_int8_t *)outpacket + ICMP6ECHOLEN),
+ sizeof(tv32));
+ break;
+ case IPPROTO_UDP:
+ op = outpacket;
+
+ op->seq = seq;
+ op->hops = hops;
+ bcopy(&tv32, &op->tv, sizeof tv32);
+ break;
+ case IPPROTO_NONE:
+ /* No space for anything. No harm as seq/tv32 are decorative. */
+ break;
+ default:
+ fprintf(stderr, "Unknown probe protocol %d.\n", useproto);
+ exit(1);
+ }
+
+ i = sendto(sndsock, (char *)outpacket, datalen, 0,
+ (struct sockaddr *)&Dst, Dst.sin6_len);
+ if (i < 0 || i != datalen) {
+ if (i < 0)
+ perror("sendto");
+ printf("traceroute6: wrote %s %lu chars, ret=%d\n",
+ hostname, datalen, i);
+ (void) fflush(stdout);
+ }
+}
+
+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_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_HOPLIMIT &&
+ cm->cmsg_len == CMSG_LEN(sizeof(int)))
+ return(*(int *)CMSG_DATA(cm));
+ }
+
+ return(-1);
+}
+
+double
+deltaT(t1p, t2p)
+ struct timeval *t1p, *t2p;
+{
+ double dt;
+
+ dt = (double)(t2p->tv_sec - t1p->tv_sec) * 1000.0 +
+ (double)(t2p->tv_usec - t1p->tv_usec) / 1000.0;
+ return (dt);
+}
+
+/*
+ * Convert an ICMP "type" field to a printable string.
+ */
+char *
+pr_type(t0)
+ int t0;
+{
+ u_char t = t0 & 0xff;
+ char *cp;
+
+ switch (t) {
+ case ICMP6_DST_UNREACH:
+ cp = "Destination Unreachable";
+ break;
+ case ICMP6_PACKET_TOO_BIG:
+ cp = "Packet Too Big";
+ break;
+ case ICMP6_TIME_EXCEEDED:
+ cp = "Time Exceeded";
+ break;
+ case ICMP6_PARAM_PROB:
+ cp = "Parameter Problem";
+ break;
+ case ICMP6_ECHO_REQUEST:
+ cp = "Echo Request";
+ break;
+ case ICMP6_ECHO_REPLY:
+ cp = "Echo Reply";
+ break;
+ case ICMP6_MEMBERSHIP_QUERY:
+ cp = "Group Membership Query";
+ break;
+ case ICMP6_MEMBERSHIP_REPORT:
+ cp = "Group Membership Report";
+ break;
+ case ICMP6_MEMBERSHIP_REDUCTION:
+ cp = "Group Membership Reduction";
+ break;
+ case ND_ROUTER_SOLICIT:
+ cp = "Router Solicitation";
+ break;
+ case ND_ROUTER_ADVERT:
+ cp = "Router Advertisement";
+ break;
+ case ND_NEIGHBOR_SOLICIT:
+ cp = "Neighbor Solicitation";
+ break;
+ case ND_NEIGHBOR_ADVERT:
+ cp = "Neighbor Advertisement";
+ break;
+ case ND_REDIRECT:
+ cp = "Redirect";
+ break;
+ default:
+ cp = "Unknown";
+ break;
+ }
+ return cp;
+}
+
+int
+packet_ok(mhdr, cc, seq)
+ struct msghdr *mhdr;
+ int cc;
+ int seq;
+{
+ struct icmp6_hdr *icp;
+ struct sockaddr_in6 *from = (struct sockaddr_in6 *)mhdr->msg_name;
+ u_char type, code;
+ char *buf = (char *)mhdr->msg_iov[0].iov_base;
+ struct cmsghdr *cm;
+ int *hlimp;
+ char hbuf[NI_MAXHOST];
+
+#ifdef OLDRAWSOCKET
+ int hlen;
+ struct ip6_hdr *ip;
+#endif
+
+#ifdef OLDRAWSOCKET
+ ip = (struct ip6_hdr *) buf;
+ hlen = sizeof(struct ip6_hdr);
+ if (cc < hlen + sizeof(struct icmp6_hdr)) {
+ if (verbose) {
+ if (getnameinfo((struct sockaddr *)from, from->sin6_len,
+ hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0)
+ strlcpy(hbuf, "invalid", sizeof(hbuf));
+ printf("packet too short (%d bytes) from %s\n", cc,
+ hbuf);
+ }
+ return (0);
+ }
+ cc -= hlen;
+ icp = (struct icmp6_hdr *)(buf + hlen);
+#else
+ if (cc < sizeof(struct icmp6_hdr)) {
+ if (verbose) {
+ if (getnameinfo((struct sockaddr *)from, from->sin6_len,
+ hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0)
+ strlcpy(hbuf, "invalid", sizeof(hbuf));
+ printf("data too short (%d bytes) from %s\n", cc, hbuf);
+ }
+ return(0);
+ }
+ icp = (struct icmp6_hdr *)buf;
+#endif
+ /* get optional information via advanced API */
+ rcvpktinfo = NULL;
+ hlimp = NULL;
+ for (cm = (struct cmsghdr *)CMSG_FIRSTHDR(mhdr); cm;
+ cm = (struct cmsghdr *)CMSG_NXTHDR(mhdr, cm)) {
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_PKTINFO &&
+ cm->cmsg_len ==
+ CMSG_LEN(sizeof(struct in6_pktinfo)))
+ rcvpktinfo = (struct in6_pktinfo *)(CMSG_DATA(cm));
+
+ if (cm->cmsg_level == IPPROTO_IPV6 &&
+ cm->cmsg_type == IPV6_HOPLIMIT &&
+ cm->cmsg_len == CMSG_LEN(sizeof(int)))
+ hlimp = (int *)CMSG_DATA(cm);
+ }
+ if (rcvpktinfo == NULL || hlimp == NULL) {
+ warnx("failed to get received hop limit or packet info");
+#if 0
+ return(0);
+#else
+ rcvhlim = 0; /*XXX*/
+#endif
+ }
+ else
+ rcvhlim = *hlimp;
+
+ type = icp->icmp6_type;
+ code = icp->icmp6_code;
+ if ((type == ICMP6_TIME_EXCEEDED && code == ICMP6_TIME_EXCEED_TRANSIT)
+ || type == ICMP6_DST_UNREACH) {
+ struct ip6_hdr *hip;
+ void *up;
+
+ hip = (struct ip6_hdr *)(icp + 1);
+ if ((up = get_uphdr(hip, (u_char *)(buf + cc))) == NULL) {
+ if (verbose)
+ warnx("failed to get upper layer header");
+ return(0);
+ }
+ switch (useproto) {
+ case IPPROTO_ICMPV6:
+ if (((struct icmp6_hdr *)up)->icmp6_id == ident &&
+ ((struct icmp6_hdr *)up)->icmp6_seq == htons(seq))
+ return (type == ICMP6_TIME_EXCEEDED ?
+ -1 : code + 1);
+ break;
+ case IPPROTO_UDP:
+ if (((struct udphdr *)up)->uh_sport == htons(srcport) &&
+ ((struct udphdr *)up)->uh_dport == htons(port + seq))
+ return (type == ICMP6_TIME_EXCEEDED ?
+ -1 : code + 1);
+ break;
+ case IPPROTO_NONE:
+ return (type == ICMP6_TIME_EXCEEDED ? -1 : code + 1);
+ default:
+ fprintf(stderr, "Unknown probe proto %d.\n", useproto);
+ break;
+ }
+ } else if (useproto == IPPROTO_ICMPV6 && type == ICMP6_ECHO_REPLY) {
+ if (icp->icmp6_id == ident &&
+ icp->icmp6_seq == htons(seq))
+ return (ICMP6_DST_UNREACH_NOPORT + 1);
+ }
+ if (verbose) {
+ char sbuf[NI_MAXHOST+1], dbuf[INET6_ADDRSTRLEN];
+ u_int8_t *p;
+ int i;
+
+ if (getnameinfo((struct sockaddr *)from, from->sin6_len,
+ sbuf, sizeof(sbuf), NULL, 0, NI_NUMERICHOST) != 0)
+ strlcpy(sbuf, "invalid", sizeof(sbuf));
+ printf("\n%d bytes from %s to %s", cc, sbuf,
+ rcvpktinfo ? inet_ntop(AF_INET6, &rcvpktinfo->ipi6_addr,
+ dbuf, sizeof(dbuf)) : "?");
+ printf(": icmp type %d (%s) code %d\n", type, pr_type(type),
+ icp->icmp6_code);
+ p = (u_int8_t *)(icp + 1);
+#define WIDTH 16
+ for (i = 0; i < cc; i++) {
+ if (i % WIDTH == 0)
+ printf("%04x:", i);
+ if (i % 4 == 0)
+ printf(" ");
+ printf("%02x", p[i]);
+ if (i % WIDTH == WIDTH - 1)
+ printf("\n");
+ }
+ if (cc % WIDTH != 0)
+ printf("\n");
+ }
+ return(0);
+}
+
+/*
+ * Increment pointer until find the UDP or ICMP header.
+ */
+void *
+get_uphdr(ip6, lim)
+ struct ip6_hdr *ip6;
+ u_char *lim;
+{
+ u_char *cp = (u_char *)ip6, nh;
+ int hlen;
+ static u_char none_hdr[1]; /* Fake pointer for IPPROTO_NONE. */
+
+ if (cp + sizeof(*ip6) > lim)
+ return(NULL);
+
+ nh = ip6->ip6_nxt;
+ cp += sizeof(struct ip6_hdr);
+
+ while (lim - cp >= (nh == IPPROTO_NONE ? 0 : 8)) {
+ switch (nh) {
+ case IPPROTO_ESP:
+ case IPPROTO_TCP:
+ return(NULL);
+ case IPPROTO_ICMPV6:
+ return(useproto == nh ? cp : NULL);
+ case IPPROTO_UDP:
+ return(useproto == nh ? cp : NULL);
+ case IPPROTO_NONE:
+ return(useproto == nh ? none_hdr : NULL);
+ case IPPROTO_FRAGMENT:
+ hlen = sizeof(struct ip6_frag);
+ nh = ((struct ip6_frag *)cp)->ip6f_nxt;
+ break;
+ case IPPROTO_AH:
+ hlen = (((struct ip6_ext *)cp)->ip6e_len + 2) << 2;
+ nh = ((struct ip6_ext *)cp)->ip6e_nxt;
+ break;
+ default:
+ hlen = (((struct ip6_ext *)cp)->ip6e_len + 1) << 3;
+ nh = ((struct ip6_ext *)cp)->ip6e_nxt;
+ break;
+ }
+
+ cp += hlen;
+ }
+
+ return(NULL);
+}
+
+void
+print(mhdr, cc)
+ struct msghdr *mhdr;
+ int cc;
+{
+ struct sockaddr_in6 *from = (struct sockaddr_in6 *)mhdr->msg_name;
+ char hbuf[NI_MAXHOST];
+
+ if (getnameinfo((struct sockaddr *)from, from->sin6_len,
+ hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0)
+ strlcpy(hbuf, "invalid", sizeof(hbuf));
+ if (nflag)
+ printf(" %s", hbuf);
+ else if (lflag)
+ printf(" %s (%s)", inetname((struct sockaddr *)from), hbuf);
+ else
+ printf(" %s", inetname((struct sockaddr *)from));
+
+ if (verbose) {
+#ifdef OLDRAWSOCKET
+ printf(" %d bytes to %s", cc,
+ rcvpktinfo ? inet_ntop(AF_INET6, &rcvpktinfo->ipi6_addr,
+ hbuf, sizeof(hbuf)) : "?");
+#else
+ printf(" %d bytes of data to %s", cc,
+ rcvpktinfo ? inet_ntop(AF_INET6, &rcvpktinfo->ipi6_addr,
+ hbuf, sizeof(hbuf)) : "?");
+#endif
+ }
+}
+
+/*
+ * Construct an Internet address representation.
+ * If the nflag has been supplied, give
+ * numeric value, otherwise try for symbolic name.
+ */
+const char *
+inetname(sa)
+ struct sockaddr *sa;
+{
+ static char line[NI_MAXHOST], domain[MAXHOSTNAMELEN + 1];
+ static int first = 1;
+ char *cp;
+
+ if (first && !nflag) {
+ first = 0;
+ if (gethostname(domain, sizeof(domain)) == 0 &&
+ (cp = strchr(domain, '.')))
+ (void) strlcpy(domain, cp + 1, sizeof(domain));
+ else
+ domain[0] = 0;
+ }
+ cp = NULL;
+ if (!nflag) {
+ if (getnameinfo(sa, sa->sa_len, line, sizeof(line), NULL, 0,
+ NI_NAMEREQD) == 0) {
+ if ((cp = strchr(line, '.')) &&
+ !strcmp(cp + 1, domain))
+ *cp = 0;
+ cp = line;
+ }
+ }
+ if (cp)
+ return cp;
+
+ if (getnameinfo(sa, sa->sa_len, line, sizeof(line), NULL, 0,
+ NI_NUMERICHOST) != 0)
+ strlcpy(line, "invalid", sizeof(line));
+ return line;
+}
+
+void
+usage()
+{
+
+ fprintf(stderr,
+"usage: traceroute6 [-dIlnNrUv] [-f firsthop] [-g gateway] [-m hoplimit]\n"
+" [-p port] [-q probes] [-s src] [-w waittime] target [datalen]\n");
+ exit(1);
+}
diff --git a/usr.sbin/trpt/Makefile b/usr.sbin/trpt/Makefile
new file mode 100644
index 0000000..5c62ce0
--- /dev/null
+++ b/usr.sbin/trpt/Makefile
@@ -0,0 +1,17 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+.include <bsd.own.mk>
+
+PROG= trpt
+MAN= trpt.8
+BINGRP= kmem
+BINMODE= 2555
+
+WARNS?= 4
+
+.if ${MK_INET6_SUPPORT} != "no"
+CFLAGS+= -DINET6
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/trpt/trpt.8 b/usr.sbin/trpt/trpt.8
new file mode 100644
index 0000000..19cae477
--- /dev/null
+++ b/usr.sbin/trpt/trpt.8
@@ -0,0 +1,149 @@
+.\" 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.
+.\"
+.\" @(#)trpt.8 8.2 (Berkeley) 12/11/93
+.\" $FreeBSD$
+.\"
+.Dd December 11, 1993
+.Dt TRPT 8
+.Os
+.Sh NAME
+.Nm trpt
+.Nd transliterate protocol trace
+.Sh SYNOPSIS
+.Nm
+.Op Fl a
+.Op Fl f
+.Op Fl j
+.Op Fl p Ar hex-address
+.Op Fl s
+.Op Fl t
+.Oo
+.Ar system Op Ar core
+.Oc
+.Sh DESCRIPTION
+The
+.Nm
+utility interrogates the buffer of
+.Tn TCP
+trace records created
+when a socket is marked for
+.Dq debugging
+(see
+.Xr setsockopt 2 ) ,
+and prints a readable description of these records.
+When no options are supplied,
+.Nm
+prints all the trace records found in the system
+grouped according to
+.Tn TCP
+connection protocol control
+block
+.Pq Tn PCB .
+.Pp
+The following options may be used to
+alter this behavior:
+.Bl -tag -width indent
+.It Fl a
+In addition to the normal output,
+print the values of the source and destination
+addresses for each packet recorded.
+.It Fl f
+Follow the trace as it occurs, waiting a short time for additional records
+each time the end of the log is reached.
+.It Fl j
+Just give a list of the protocol control block
+addresses for which there are trace records.
+.It Fl p
+Show only trace records associated with the protocol
+control block at the given address
+.Ar hex-address .
+.It Fl s
+In addition to the normal output,
+print a detailed description of the packet
+sequencing information.
+.It Fl t
+In addition to the normal output,
+print the values for all timers at each
+point in the trace.
+.El
+.Pp
+The recommended use of
+.Nm
+is as follows.
+Isolate the problem and enable debugging on the
+socket(s) involved in the connection.
+Find the address of the protocol control blocks
+associated with the sockets using the
+.Fl A
+option to
+.Xr netstat 1 .
+Then run
+.Nm
+with the
+.Fl p
+option, supplying the associated
+protocol control block addresses.
+The
+.Fl f
+option can be used to follow the trace log once the trace is located.
+If there are
+many sockets using the debugging option, the
+.Fl j
+option may be useful in checking to see if
+any trace records are present for the socket in
+question.
+.Pp
+If debugging is being performed on a system or
+core file other than the default, the last two
+arguments may be used to supplant the defaults.
+.Sh FILES
+.Bl -tag -width /boot/kernel/kernel -compact
+.It Pa /boot/kernel/kernel
+.It Pa /dev/kmem
+.El
+.Sh DIAGNOSTICS
+.Bl -diag
+.It no namelist
+When the system image does not
+contain the proper symbols to find the trace buffer;
+others which should be self explanatory.
+.El
+.Sh SEE ALSO
+.Xr netstat 1 ,
+.Xr setsockopt 2
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.2 .
+.Sh BUGS
+Should also print the data for each input or output,
+but this is not saved in the trace record.
+.Pp
+The output format is inscrutable and should be described
+here.
diff --git a/usr.sbin/trpt/trpt.c b/usr.sbin/trpt/trpt.c
new file mode 100644
index 0000000..fbdc4de
--- /dev/null
+++ b/usr.sbin/trpt/trpt.c
@@ -0,0 +1,463 @@
+/*
+ * 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.
+ */
+
+#if 0
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1983, 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)trpt.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#define PRUREQUESTS
+#include <sys/protosw.h>
+#include <sys/file.h>
+#include <sys/time.h>
+
+#include <net/route.h>
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#ifdef INET6
+#include <netinet/ip6.h>
+#endif
+#include <netinet/ip_var.h>
+#include <netinet/tcp.h>
+#define TCPSTATES
+#include <netinet/tcp_fsm.h>
+#include <netinet/tcp_seq.h>
+#define TCPTIMERS
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#include <netinet/tcpip.h>
+#define TANAMES
+#include <netinet/tcp_debug.h>
+
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <nlist.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+struct nlist nl[3];
+#define N_TCP_DEBUG 0
+#define N_TCP_DEBX 1
+
+static caddr_t tcp_pcbs[TCP_NDEBUG];
+static n_time ntime;
+static int aflag, kflag, memf, follow, sflag, tflag;
+
+void dotrace(caddr_t);
+void klseek(int, off_t, int);
+int numeric(const void *, const void *);
+void tcp_trace(short, short, struct tcpcb *, int, void *, struct tcphdr *, int);
+static void usage(void);
+
+int
+main(int argc, char **argv)
+{
+ int ch, i, jflag, npcbs;
+ const char *core, *syst;
+
+ nl[0].n_name = strdup("_tcp_debug");
+ nl[1].n_name = strdup("_tcp_debx");
+
+ jflag = npcbs = 0;
+ while ((ch = getopt(argc, argv, "afjp:st")) != -1)
+ switch (ch) {
+ case 'a':
+ ++aflag;
+ break;
+ case 'f':
+ ++follow;
+ setlinebuf(stdout);
+ break;
+ case 'j':
+ ++jflag;
+ break;
+ case 'p':
+ if (npcbs >= TCP_NDEBUG)
+ errx(1, "too many pcb's specified");
+ (void)sscanf(optarg, "%x", (int *)&tcp_pcbs[npcbs++]);
+ break;
+ case 's':
+ ++sflag;
+ break;
+ case 't':
+ ++tflag;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ core = _PATH_KMEM;
+ if (argc > 0) {
+ syst = *argv;
+ argc--, argv++;
+ if (argc > 0) {
+ core = *argv;
+ argc--, argv++;
+ ++kflag;
+ }
+ /*
+ * Discard setgid privileges if not the running kernel so that
+ * bad guys can't print interesting stuff from kernel memory.
+ */
+ setgid(getgid());
+ }
+ else
+ syst = getbootfile();
+
+ if (nlist(syst, nl) < 0 || !nl[0].n_value)
+ errx(1, "%s: no namelist", syst);
+ if ((memf = open(core, O_RDONLY)) < 0)
+ err(2, "%s", core);
+ setgid(getgid());
+ if (kflag)
+ errx(1, "can't do core files yet");
+ (void)klseek(memf, (off_t)nl[N_TCP_DEBX].n_value, L_SET);
+ if (read(memf, (char *)&tcp_debx, sizeof(tcp_debx)) !=
+ sizeof(tcp_debx))
+ err(3, "tcp_debx");
+ (void)klseek(memf, (off_t)nl[N_TCP_DEBUG].n_value, L_SET);
+ if (read(memf, (char *)tcp_debug, sizeof(tcp_debug)) !=
+ sizeof(tcp_debug))
+ err(3, "tcp_debug");
+ /*
+ * If no control blocks have been specified, figure
+ * out how many distinct one we have and summarize
+ * them in tcp_pcbs for sorting the trace records
+ * below.
+ */
+ if (!npcbs) {
+ for (i = 0; i < TCP_NDEBUG; i++) {
+ register struct tcp_debug *td = &tcp_debug[i];
+ register int j;
+
+ if (td->td_tcb == 0)
+ continue;
+ for (j = 0; j < npcbs; j++)
+ if (tcp_pcbs[j] == td->td_tcb)
+ break;
+ if (j >= npcbs)
+ tcp_pcbs[npcbs++] = td->td_tcb;
+ }
+ if (!npcbs)
+ exit(0);
+ }
+ qsort(tcp_pcbs, npcbs, sizeof(caddr_t), numeric);
+ if (jflag) {
+ for (i = 0;;) {
+ printf("%p", (void *)tcp_pcbs[i]);
+ if (++i == npcbs)
+ break;
+ fputs(", ", stdout);
+ }
+ putchar('\n');
+ }
+ else for (i = 0; i < npcbs; i++) {
+ printf("\n%p:\n", tcp_pcbs[i]);
+ dotrace(tcp_pcbs[i]);
+ }
+ exit(0);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: trpt [-afjst] [-p hex-address] [system [core]]\n");
+ exit(1);
+}
+
+void
+dotrace(tcpcb)
+ register caddr_t tcpcb;
+{
+ register struct tcp_debug *td;
+ register int i;
+ int prev_debx = tcp_debx, family;
+
+again: if (--tcp_debx < 0)
+ tcp_debx = TCP_NDEBUG - 1;
+ for (i = prev_debx % TCP_NDEBUG; i < TCP_NDEBUG; i++) {
+ td = &tcp_debug[i];
+ if (tcpcb && td->td_tcb != tcpcb)
+ continue;
+ ntime = ntohl(td->td_time);
+#ifdef INET6
+ family = td->td_family;
+#else
+ family = AF_INET;
+#endif
+ switch(family) {
+ case AF_INET:
+ tcp_trace(td->td_act, td->td_ostate,
+ &td->td_cb, td->td_family, &td->td_ti.ti_i,
+ &td->td_ti.ti_t, td->td_req);
+ break;
+#ifdef INET6
+ case AF_INET6:
+ tcp_trace(td->td_act, td->td_ostate,
+ &td->td_cb, td->td_family, &td->td_ti6.ip6,
+ &td->td_ti6.th, td->td_req);
+ break;
+#endif
+ }
+ if (i == tcp_debx)
+ goto done;
+ }
+ for (i = 0; i <= tcp_debx % TCP_NDEBUG; i++) {
+ td = &tcp_debug[i];
+ if (tcpcb && td->td_tcb != tcpcb)
+ continue;
+ ntime = ntohl(td->td_time);
+#ifdef INET6
+ family = td->td_family;
+#else
+ family = AF_INET;
+#endif
+ switch(family) {
+ case AF_INET:
+ tcp_trace(td->td_act, td->td_ostate,
+ &td->td_cb, td->td_family, &td->td_ti.ti_i,
+ &td->td_ti.ti_t, td->td_req);
+ break;
+#ifdef INET6
+ case AF_INET6:
+ tcp_trace(td->td_act, td->td_ostate,
+ &td->td_cb, td->td_family, &td->td_ti6.ip6,
+ &td->td_ti6.th, td->td_req);
+ break;
+#endif
+ }
+ }
+done: if (follow) {
+ prev_debx = tcp_debx + 1;
+ if (prev_debx >= TCP_NDEBUG)
+ prev_debx = 0;
+ do {
+ sleep(1);
+ (void)klseek(memf, (off_t)nl[N_TCP_DEBX].n_value, L_SET);
+ if (read(memf, (char *)&tcp_debx, sizeof(tcp_debx)) !=
+ sizeof(tcp_debx))
+ err(3, "tcp_debx");
+ } while (tcp_debx == prev_debx);
+ (void)klseek(memf, (off_t)nl[N_TCP_DEBUG].n_value, L_SET);
+ if (read(memf, (char *)tcp_debug, sizeof(tcp_debug)) !=
+ sizeof(tcp_debug))
+ err(3, "tcp_debug");
+ goto again;
+ }
+}
+
+/*
+ * Tcp debug routines
+ */
+/*ARGSUSED*/
+void
+tcp_trace(short act, short ostate, struct tcpcb *tp, int family __unused,
+ void *ip, struct tcphdr *th, int req)
+{
+ tcp_seq seq, ack;
+ int flags, len, win, timer;
+ struct ip *ip4;
+#ifdef INET6
+ int isipv6, nopkt = 1;
+ struct ip6_hdr *ip6;
+ char ntop_buf[INET6_ADDRSTRLEN];
+#endif
+
+#ifdef INET6
+ switch (family) {
+ case AF_INET:
+ nopkt = 0;
+ isipv6 = 0;
+ ip4 = (struct ip *)ip;
+ break;
+ case AF_INET6:
+ nopkt = 0;
+ isipv6 = 1;
+ ip6 = (struct ip6_hdr *)ip;
+ case 0:
+ default:
+ break;
+ }
+#else
+ ip4 = (struct ip *)ip;
+#endif
+ printf("%03ld %s:%s ", (long)((ntime/10) % 1000), tcpstates[ostate],
+ tanames[act]);
+ switch (act) {
+ case TA_INPUT:
+ case TA_OUTPUT:
+ case TA_DROP:
+#ifdef INET6
+ if (nopkt != 0)
+ break;
+#endif
+ if (aflag) {
+ printf("(src=%s,%u, ",
+
+#ifdef INET6
+ isipv6
+ ? inet_ntop(AF_INET6, &ip6->ip6_src, ntop_buf,
+ sizeof(ntop_buf)) :
+#endif
+ inet_ntoa(ip4->ip_src),
+ ntohs(th->th_sport));
+ printf("dst=%s,%u)",
+#ifdef INET6
+ isipv6
+ ? inet_ntop(AF_INET6, &ip6->ip6_dst, ntop_buf,
+ sizeof(ntop_buf)) :
+#endif
+ inet_ntoa(ip4->ip_dst),
+ ntohs(th->th_dport));
+ }
+ seq = th->th_seq;
+ ack = th->th_ack;
+
+ len =
+#ifdef INET6
+ isipv6 ? ip6->ip6_plen :
+#endif
+ ip4->ip_len;
+ win = th->th_win;
+ if (act == TA_OUTPUT) {
+ seq = ntohl(seq);
+ ack = ntohl(ack);
+ len = ntohs(len);
+ win = ntohs(win);
+ }
+ if (act == TA_OUTPUT)
+ len -= sizeof(struct tcphdr);
+ if (len)
+ printf("[%lx..%lx)", (u_long)seq, (u_long)(seq + len));
+ else
+ printf("%lx", (u_long)seq);
+ printf("@%lx", (u_long)ack);
+ if (win)
+ printf("(win=%x)", win);
+ flags = th->th_flags;
+ if (flags) {
+ const char *cp = "<";
+#define pf(flag, string) { \
+ if (th->th_flags&flag) { \
+ (void)printf("%s%s", cp, string); \
+ cp = ","; \
+ } \
+}
+ pf(TH_SYN, "SYN");
+ pf(TH_ACK, "ACK");
+ pf(TH_FIN, "FIN");
+ pf(TH_RST, "RST");
+ pf(TH_PUSH, "PUSH");
+ pf(TH_URG, "URG");
+ printf(">");
+ }
+ break;
+ case TA_USER:
+ timer = req >> 8;
+ req &= 0xff;
+ printf("%s", prurequests[req]);
+ if (req == PRU_SLOWTIMO || req == PRU_FASTTIMO)
+ printf("<%s>", tcptimers[timer]);
+ break;
+ }
+ printf(" -> %s", tcpstates[tp->t_state]);
+ /* print out internal state of tp !?! */
+ printf("\n");
+ if (sflag) {
+ printf("\trcv_nxt %lx rcv_wnd %lx snd_una %lx snd_nxt %lx snd_max %lx\n",
+ (u_long)tp->rcv_nxt, tp->rcv_wnd,
+ (u_long)tp->snd_una, (u_long)tp->snd_nxt,
+ (u_long)tp->snd_max);
+ printf("\tsnd_wl1 %lx snd_wl2 %lx snd_wnd %lx\n",
+ (u_long)tp->snd_wl1,
+ (u_long)tp->snd_wl2, tp->snd_wnd);
+ }
+ /* print out timers? */
+#if 0
+ /*
+ * XXX
+ * kernel now uses callouts, not integer time values.
+ */
+ if (tflag) {
+ register char *cp = "\t";
+ register int i;
+
+ for (i = 0; i < TCPT_NTIMERS; i++) {
+ if (tp->t_timer[i] == 0)
+ continue;
+ printf("%s%s=%d", cp, tcptimers[i], tp->t_timer[i]);
+ if (i == TCPT_REXMT)
+ printf(" (t_rxtshft=%d)", tp->t_rxtshift);
+ cp = ", ";
+ }
+ if (*cp != '\t')
+ putchar('\n');
+ }
+#endif
+}
+
+int
+numeric(v1, v2)
+ const void *v1, *v2;
+{
+ const caddr_t *c1 = v1, *c2 = v2;
+ return(*c1 - *c2);
+}
+
+void
+klseek(fd, base, off)
+ int fd, off;
+ off_t base;
+{
+ (void)lseek(fd, base, off);
+}
diff --git a/usr.sbin/tzsetup/Makefile b/usr.sbin/tzsetup/Makefile
new file mode 100644
index 0000000..0596967
--- /dev/null
+++ b/usr.sbin/tzsetup/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+PROG= tzsetup
+MAN= tzsetup.8
+
+CFLAGS+= -I${.CURDIR}
+
+DPADD= ${LIBDIALOG} ${LIBNCURSES}
+LDADD= -ldialog -lncurses
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/tzsetup/tzsetup.8 b/usr.sbin/tzsetup/tzsetup.8
new file mode 100644
index 0000000..562744c
--- /dev/null
+++ b/usr.sbin/tzsetup/tzsetup.8
@@ -0,0 +1,132 @@
+.\" Copyright (c) 1996 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.Dd January 24, 1996
+.Dt TZSETUP 8
+.Os
+.Sh NAME
+.Nm tzsetup
+.Nd set local timezone
+.Sh SYNOPSIS
+.Nm
+.Op Fl n
+.Op Ar default
+.Sh DESCRIPTION
+The
+.Nm
+utility reads a database of timezone information and presents a menu
+allowing the user to select a specific zone without knowing the details
+of the database layout.
+The selected zone is installed as the system
+default zone.
+The
+.Nm
+utility also determines whether any adjustment is necessary for systems where
+the hardware clock does not keep
+.Tn UTC .
+.Pp
+The following option is available:
+.Bl -tag -offset indent -width Fl
+.It Fl n
+Do not create or copy files.
+.El
+.Pp
+It is possible to short-circuit the menu system by specifying a
+.Ar default
+on the command line; this is intended mainly for pre-configured
+installation scripts.
+.Sh TIMEZONE DATABASE
+The contents of the timezone database are indexed by
+.Pa /usr/share/zoneinfo/zone.tab .
+This file lists, for each timezone data file, the
+.Tn ISO
+3166 territory code, approximate geographical coordinates
+(in
+.Tn ISO
+6709 format),
+and location within the territory.
+.Pp
+The maintainers of the database maintain the following policies:
+.Bl -enum -offset indent
+.It
+At least one zone for every country or inhabited geographical territory.
+.It
+One zone for every distinct, documented timezone history since the
+beginning of the
+.Ux
+epoch (January 1, 1970,
+.Tn GMT ) .
+.It
+Each zone is named for the most populous city therein.
+(Where possible,
+the database includes pre-1970 history for its city.)
+.El
+The source code to the database
+.Pq Pa /usr/src/share/zoneinfo/[a-z]*
+contains many additional comments and documentation references for the
+historically minded.
+.Sh FILES
+.Bl -tag -width /usr/share/zoneinfo/zone.tab -compact
+.It Pa /etc/localtime
+current time zone file
+.It Pa /etc/wall_cmos_clock
+see
+.Xr adjkerntz 8 .
+.It Pa /usr/share/misc/iso3166
+mapping of
+.Tn ISO
+3166 territory codes to names
+.It Pa /usr/share/zoneinfo
+directory for zoneinfo files
+.It Pa /usr/share/zoneinfo/zone.tab
+mapping of timezone file to country and location
+.El
+.Sh SEE ALSO
+.Xr date 1 ,
+.Xr adjtime 2 ,
+.Xr ctime 3 ,
+.Xr timezone 3 ,
+.Xr tzfile 5 ,
+.Xr adjkerntz 8 ,
+.Xr zdump 8 ,
+.Xr zic 8
+.Sh DISCLAIMER
+The representation of certain localities as being associated with certain
+countries and/or territories is for the purposes of identification only,
+and does not imply any endorsement or rejection on the part of the
+.Fx
+Project of the territorial claims of any entity.
+.Sh BUGS
+Programs which are already running when
+.Nm
+creates or updates
+.Pa /etc/localtime
+will not reflect the updated timezone.
+When the system is first configured for a
+.Pf non- Tn UTC
+hardware clock, it is necessary to run
+.Xr adjkerntz 8
+(which normally happens as a part of system startup) in order to update
+the kernel's idea of the correct timezone offset.
diff --git a/usr.sbin/tzsetup/tzsetup.c b/usr.sbin/tzsetup/tzsetup.c
new file mode 100644
index 0000000..c02bac1
--- /dev/null
+++ b/usr.sbin/tzsetup/tzsetup.c
@@ -0,0 +1,734 @@
+/*
+ * Copyright 1996 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that both the above copyright notice and this
+ * permission notice appear in all copies, that both the above
+ * copyright notice and this permission notice appear in all
+ * supporting documentation, and that the name of M.I.T. not be used
+ * in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. M.I.T. makes
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
+ * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Second attempt at a `tzmenu' program, using the separate description
+ * files provided in newer tzdata releases.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <dialog.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <sys/fcntl.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+
+#define _PATH_ZONETAB "/usr/share/zoneinfo/zone.tab"
+#define _PATH_ISO3166 "/usr/share/misc/iso3166"
+#define _PATH_ZONEINFO "/usr/share/zoneinfo"
+#define _PATH_LOCALTIME "/etc/localtime"
+#define _PATH_WALL_CMOS_CLOCK "/etc/wall_cmos_clock"
+
+static int reallydoit = 1;
+
+static void usage(void);
+static int continent_country_menu(dialogMenuItem *);
+static int set_zone_multi(dialogMenuItem *);
+static int set_zone_whole_country(dialogMenuItem *);
+static int set_zone_menu(dialogMenuItem *);
+
+struct continent {
+ dialogMenuItem *menu;
+ int nitems;
+ int ch;
+ int sc;
+};
+
+static struct continent africa, america, antarctica, arctic, asia, atlantic;
+static struct continent australia, europe, indian, pacific;
+
+static struct continent_names {
+ const char *name;
+ struct continent *continent;
+} continent_names[] = {
+ { "Africa", &africa },
+ { "America", &america },
+ { "Antarctica", &antarctica },
+ { "Arctic", &arctic },
+ { "Asia", &asia },
+ { "Atlantic", &atlantic },
+ { "Australia", &australia },
+ { "Europe", &europe },
+ { "Indian", &indian },
+ { "Pacific", &pacific }
+};
+
+static struct continent_items {
+ char prompt[2];
+ char title[30];
+} continent_items[] = {
+ { "1", "Africa" },
+ { "2", "America -- North and South" },
+ { "3", "Antarctica" },
+ { "4", "Arctic Ocean" },
+ { "5", "Asia" },
+ { "6", "Atlantic Ocean" },
+ { "7", "Australia" },
+ { "8", "Europe" },
+ { "9", "Indian Ocean" },
+ { "0", "Pacific Ocean" }
+};
+
+#define NCONTINENTS \
+ (int)((sizeof(continent_items)) / (sizeof(continent_items[0])))
+static dialogMenuItem continents[NCONTINENTS];
+
+#define OCEANP(x) ((x) == 3 || (x) == 5 || (x) == 8 || (x) == 9)
+
+static int
+continent_country_menu(dialogMenuItem *continent)
+{
+ char title[64], prompt[64];
+ struct continent *contp = continent->data;
+ int isocean = OCEANP(continent - continents);
+ int menulen;
+ int rv;
+
+ /* Short cut -- if there's only one country, don't post a menu. */
+ if (contp->nitems == 1)
+ return (contp->menu[0].fire(&contp->menu[0]));
+
+ /* It's amazing how much good grammar really matters... */
+ if (!isocean) {
+ snprintf(title, sizeof(title), "Countries in %s",
+ continent->title);
+ snprintf(prompt, sizeof(prompt), "Select a country or region");
+ } else {
+ snprintf(title, sizeof(title), "Islands and groups in the %s",
+ continent->title);
+ snprintf(prompt, sizeof(prompt), "Select an island or group");
+ }
+
+ menulen = contp->nitems < 16 ? contp->nitems : 16;
+ rv = dialog_menu(title, prompt, -1, -1, menulen, -contp->nitems,
+ contp->menu, 0, &contp->ch, &contp->sc);
+ if (rv == 0)
+ return (DITEM_LEAVE_MENU);
+ return (DITEM_RECREATE);
+}
+
+static struct continent *
+find_continent(const char *name)
+{
+ int i;
+
+ for (i = 0; i < NCONTINENTS; i++)
+ if (strcmp(name, continent_names[i].name) == 0)
+ return (continent_names[i].continent);
+ return (0);
+}
+
+struct country {
+ char *name;
+ char *tlc;
+ int nzones;
+ char *filename; /* use iff nzones < 0 */
+ struct continent *continent; /* use iff nzones < 0 */
+ TAILQ_HEAD(, zone) zones; /* use iff nzones > 0 */
+ dialogMenuItem *submenu; /* use iff nzones > 0 */
+};
+
+struct zone {
+ TAILQ_ENTRY(zone) link;
+ char *descr;
+ char *filename;
+ struct continent *continent;
+};
+
+/*
+ * This is the easiest organization... we use ISO 3166 country codes,
+ * of the two-letter variety, so we just size this array to suit.
+ * Beats worrying about dynamic allocation.
+ */
+#define NCOUNTRIES (26 * 26)
+static struct country countries[NCOUNTRIES];
+
+#define CODE2INT(s) ((s[0] - 'A') * 26 + (s[1] - 'A'))
+
+/*
+ * Read the ISO 3166 country code database in _PATH_ISO3166
+ * (/usr/share/misc/iso3166). On error, exit via err(3).
+ */
+static void
+read_iso3166_table(void)
+{
+ FILE *fp;
+ struct country *cp;
+ size_t len;
+ char *s, *t, *name;
+ int lineno;
+
+ fp = fopen(_PATH_ISO3166, "r");
+ if (!fp)
+ err(1, _PATH_ISO3166);
+ lineno = 0;
+
+ while ((s = fgetln(fp, &len)) != 0) {
+ lineno++;
+ if (s[len - 1] != '\n')
+ errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
+ s[len - 1] = '\0';
+ if (s[0] == '#' || strspn(s, " \t") == len - 1)
+ continue;
+
+ /* Isolate the two-letter code. */
+ t = strsep(&s, "\t");
+ if (t == 0 || strlen(t) != 2)
+ errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
+ if (t[0] < 'A' || t[0] > 'Z' || t[1] < 'A' || t[1] > 'Z')
+ errx(1, _PATH_ISO3166 ":%d: invalid code `%s'",
+ lineno, t);
+
+ /* Now skip past the three-letter and numeric codes. */
+ name = strsep(&s, "\t"); /* 3-let */
+ if (name == 0 || strlen(name) != 3)
+ errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
+ name = strsep(&s, "\t"); /* numeric */
+ if (name == 0 || strlen(name) != 3)
+ errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
+
+ name = s;
+
+ cp = &countries[CODE2INT(t)];
+ if (cp->name)
+ errx(1, _PATH_ISO3166
+ ":%d: country code `%s' multiply defined: %s",
+ lineno, t, cp->name);
+ cp->name = strdup(name);
+ if (cp->name == NULL)
+ errx(1, "malloc failed");
+ cp->tlc = strdup(t);
+ if (cp->tlc == NULL)
+ errx(1, "malloc failed");
+ }
+
+ fclose(fp);
+}
+
+static void
+add_zone_to_country(int lineno, const char *tlc, const char *descr,
+ const char *file, struct continent *cont)
+{
+ struct zone *zp;
+ struct country *cp;
+
+ if (tlc[0] < 'A' || tlc[0] > 'Z' || tlc[1] < 'A' || tlc[1] > 'Z')
+ errx(1, _PATH_ZONETAB ":%d: country code `%s' invalid",
+ lineno, tlc);
+
+ cp = &countries[CODE2INT(tlc)];
+ if (cp->name == 0)
+ errx(1, _PATH_ZONETAB ":%d: country code `%s' unknown",
+ lineno, tlc);
+
+ if (descr) {
+ if (cp->nzones < 0)
+ errx(1, _PATH_ZONETAB
+ ":%d: conflicting zone definition", lineno);
+
+ zp = malloc(sizeof(*zp));
+ if (zp == 0)
+ errx(1, "malloc(%zu)", sizeof(*zp));
+
+ if (cp->nzones == 0)
+ TAILQ_INIT(&cp->zones);
+
+ zp->descr = strdup(descr);
+ if (zp->descr == NULL)
+ errx(1, "malloc failed");
+ zp->filename = strdup(file);
+ if (zp->filename == NULL)
+ errx(1, "malloc failed");
+ zp->continent = cont;
+ TAILQ_INSERT_TAIL(&cp->zones, zp, link);
+ cp->nzones++;
+ } else {
+ if (cp->nzones > 0)
+ errx(1, _PATH_ZONETAB
+ ":%d: zone must have description", lineno);
+ if (cp->nzones < 0)
+ errx(1, _PATH_ZONETAB
+ ":%d: zone multiply defined", lineno);
+ cp->nzones = -1;
+ cp->filename = strdup(file);
+ if (cp->filename == NULL)
+ errx(1, "malloc failed");
+ cp->continent = cont;
+ }
+}
+
+/*
+ * This comparison function intentionally sorts all of the null-named
+ * ``countries''---i.e., the codes that don't correspond to a real
+ * country---to the end. Everything else is lexical by country name.
+ */
+static int
+compare_countries(const void *xa, const void *xb)
+{
+ const struct country *a = xa, *b = xb;
+
+ if (a->name == 0 && b->name == 0)
+ return (0);
+ if (a->name == 0 && b->name != 0)
+ return (1);
+ if (b->name == 0)
+ return (-1);
+
+ return (strcmp(a->name, b->name));
+}
+
+/*
+ * This must be done AFTER all zone descriptions are read, since it breaks
+ * CODE2INT().
+ */
+static void
+sort_countries(void)
+{
+
+ qsort(countries, NCOUNTRIES, sizeof(countries[0]), compare_countries);
+}
+
+static void
+read_zones(void)
+{
+ char contbuf[16];
+ FILE *fp;
+ struct continent *cont;
+ size_t len;
+ char *line, *tlc, *coord, *file, *descr, *p;
+ int lineno;
+
+ fp = fopen(_PATH_ZONETAB, "r");
+ if (!fp)
+ err(1, _PATH_ZONETAB);
+ lineno = 0;
+
+ while ((line = fgetln(fp, &len)) != 0) {
+ lineno++;
+ if (line[len - 1] != '\n')
+ errx(1, _PATH_ZONETAB ":%d: invalid format", lineno);
+ line[len - 1] = '\0';
+ if (line[0] == '#')
+ continue;
+
+ tlc = strsep(&line, "\t");
+ if (strlen(tlc) != 2)
+ errx(1, _PATH_ZONETAB ":%d: invalid country code `%s'",
+ lineno, tlc);
+ coord = strsep(&line, "\t");
+ file = strsep(&line, "\t");
+ p = strchr(file, '/');
+ if (p == 0)
+ errx(1, _PATH_ZONETAB ":%d: invalid zone name `%s'",
+ lineno, file);
+ contbuf[0] = '\0';
+ strncat(contbuf, file, p - file);
+ cont = find_continent(contbuf);
+ if (!cont)
+ errx(1, _PATH_ZONETAB ":%d: invalid region `%s'",
+ lineno, contbuf);
+
+ descr = (line != NULL && *line != '\0') ? line : NULL;
+
+ add_zone_to_country(lineno, tlc, descr, file, cont);
+ }
+ fclose(fp);
+}
+
+static void
+make_menus(void)
+{
+ struct country *cp;
+ struct zone *zp, *zp2;
+ struct continent *cont;
+ dialogMenuItem *dmi;
+ int i;
+
+ /*
+ * First, count up all the countries in each continent/ocean.
+ * Be careful to count those countries which have multiple zones
+ * only once for each. NB: some countries are in multiple
+ * continents/oceans.
+ */
+ for (cp = countries; cp->name; cp++) {
+ if (cp->nzones == 0)
+ continue;
+ if (cp->nzones < 0) {
+ cp->continent->nitems++;
+ } else {
+ TAILQ_FOREACH(zp, &cp->zones, link) {
+ cont = zp->continent;
+ for (zp2 = TAILQ_FIRST(&cp->zones);
+ zp2->continent != cont;
+ zp2 = TAILQ_NEXT(zp2, link))
+ ;
+ if (zp2 == zp)
+ zp->continent->nitems++;
+ }
+ }
+ }
+
+ /*
+ * Now allocate memory for the country menus and initialize
+ * continent menus. We set nitems back to zero so that we can
+ * use it for counting again when we actually build the menus.
+ */
+ memset(continents, 0, sizeof(continents));
+ for (i = 0; i < NCONTINENTS; i++) {
+ continent_names[i].continent->menu =
+ malloc(sizeof(dialogMenuItem) *
+ continent_names[i].continent->nitems);
+ if (continent_names[i].continent->menu == 0)
+ errx(1, "malloc for continent menu");
+ continent_names[i].continent->nitems = 0;
+ continents[i].prompt = continent_items[i].prompt;
+ continents[i].title = continent_items[i].title;
+ continents[i].fire = continent_country_menu;
+ continents[i].data = continent_names[i].continent;
+ }
+
+ /*
+ * Now that memory is allocated, create the menu items for
+ * each continent. For multiple-zone countries, also create
+ * the country's zone submenu.
+ */
+ for (cp = countries; cp->name; cp++) {
+ if (cp->nzones == 0)
+ continue;
+ if (cp->nzones < 0) {
+ dmi = &cp->continent->menu[cp->continent->nitems];
+ memset(dmi, 0, sizeof(*dmi));
+ asprintf(&dmi->prompt, "%d", ++cp->continent->nitems);
+ dmi->title = cp->name;
+ dmi->checked = 0;
+ dmi->fire = set_zone_whole_country;
+ dmi->selected = 0;
+ dmi->data = cp;
+ } else {
+ cp->submenu = malloc(cp->nzones * sizeof(*dmi));
+ if (cp->submenu == 0)
+ errx(1, "malloc for submenu");
+ cp->nzones = 0;
+ TAILQ_FOREACH(zp, &cp->zones, link) {
+ cont = zp->continent;
+ dmi = &cp->submenu[cp->nzones];
+ memset(dmi, 0, sizeof(*dmi));
+ asprintf(&dmi->prompt, "%d", ++cp->nzones);
+ dmi->title = zp->descr;
+ dmi->checked = 0;
+ dmi->fire = set_zone_multi;
+ dmi->selected = 0;
+ dmi->data = zp;
+
+ for (zp2 = TAILQ_FIRST(&cp->zones);
+ zp2->continent != cont;
+ zp2 = TAILQ_NEXT(zp2, link))
+ ;
+ if (zp2 != zp)
+ continue;
+
+ dmi = &cont->menu[cont->nitems];
+ memset(dmi, 0, sizeof(*dmi));
+ asprintf(&dmi->prompt, "%d", ++cont->nitems);
+ dmi->title = cp->name;
+ dmi->checked = 0;
+ dmi->fire = set_zone_menu;
+ dmi->selected = 0;
+ dmi->data = cp;
+ }
+ }
+ }
+}
+
+static int
+set_zone_menu(dialogMenuItem *dmi)
+{
+ char title[64], prompt[64];
+ struct country *cp = dmi->data;
+ int menulen;
+ int rv;
+
+ snprintf(title, sizeof(title), "%s Time Zones", cp->name);
+ snprintf(prompt, sizeof(prompt),
+ "Select a zone which observes the same time as your locality.");
+ menulen = cp->nzones < 16 ? cp->nzones : 16;
+ rv = dialog_menu(title, prompt, -1, -1, menulen, -cp->nzones,
+ cp->submenu, 0, 0, 0);
+ if (rv != 0)
+ return (DITEM_RECREATE);
+ return (DITEM_LEAVE_MENU);
+}
+
+static int
+install_zone_file(const char *filename)
+{
+ char buf[1024];
+ char title[64], prompt[64];
+ struct stat sb;
+ ssize_t len;
+ int fd1, fd2, copymode;
+
+ if (lstat(_PATH_LOCALTIME, &sb) < 0) {
+ /* Nothing there yet... */
+ copymode = 1;
+ } else if (S_ISLNK(sb.st_mode))
+ copymode = 0;
+ else
+ copymode = 1;
+
+#ifdef VERBOSE
+ if (copymode)
+ snprintf(prompt, sizeof(prompt),
+ "Copying %s to " _PATH_LOCALTIME, filename);
+ else
+ snprintf(prompt, sizeof(prompt),
+ "Creating symbolic link " _PATH_LOCALTIME " to %s",
+ filename);
+ dialog_notify(prompt);
+#endif
+
+ if (reallydoit) {
+ if (copymode) {
+ fd1 = open(filename, O_RDONLY, 0);
+ if (fd1 < 0) {
+ snprintf(title, sizeof(title), "Error");
+ snprintf(prompt, sizeof(prompt),
+ "Could not open %s: %s", filename,
+ strerror(errno));
+ dialog_mesgbox(title, prompt, 8, 72);
+ return (DITEM_FAILURE | DITEM_RECREATE);
+ }
+
+ unlink(_PATH_LOCALTIME);
+ fd2 = open(_PATH_LOCALTIME, O_CREAT | O_EXCL | O_WRONLY,
+ S_IRUSR | S_IRGRP | S_IROTH);
+ if (fd2 < 0) {
+ snprintf(title, sizeof(title), "Error");
+ snprintf(prompt, sizeof(prompt),
+ "Could not open " _PATH_LOCALTIME ": %s",
+ strerror(errno));
+ dialog_mesgbox(title, prompt, 8, 72);
+ return (DITEM_FAILURE | DITEM_RECREATE);
+ }
+
+ while ((len = read(fd1, buf, sizeof(buf))) > 0)
+ len = write(fd2, buf, len);
+
+ if (len == -1) {
+ snprintf(title, sizeof(title), "Error");
+ snprintf(prompt, sizeof(prompt),
+ "Error copying %s to " _PATH_LOCALTIME
+ ": %s", filename, strerror(errno));
+ dialog_mesgbox(title, prompt, 8, 72);
+ /* Better to leave none than a corrupt one. */
+ unlink(_PATH_LOCALTIME);
+ return (DITEM_FAILURE | DITEM_RECREATE);
+ }
+ close(fd1);
+ close(fd2);
+ } else {
+ if (access(filename, R_OK) != 0) {
+ snprintf(title, sizeof(title), "Error");
+ snprintf(prompt, sizeof(prompt),
+ "Cannot access %s: %s", filename,
+ strerror(errno));
+ dialog_mesgbox(title, prompt, 8, 72);
+ return (DITEM_FAILURE | DITEM_RECREATE);
+ }
+ unlink(_PATH_LOCALTIME);
+ if (symlink(filename, _PATH_LOCALTIME) < 0) {
+ snprintf(title, sizeof(title), "Error");
+ snprintf(prompt, sizeof(prompt),
+ "Cannot create symbolic link "
+ _PATH_LOCALTIME " to %s: %s", filename,
+ strerror(errno));
+ dialog_mesgbox(title, prompt, 8, 72);
+ return (DITEM_FAILURE | DITEM_RECREATE);
+ }
+ }
+ }
+
+#ifdef VERBOSE
+ snprintf(title, sizeof(title), "Done");
+ if (copymode)
+ snprintf(prompt, sizeof(prompt),
+ "Copied timezone file from %s to " _PATH_LOCALTIME,
+ filename);
+ else
+ snprintf(prompt, sizeof(prompt), "Created symbolic link from "
+ _PATH_LOCALTIME " to %s", filename);
+ dialog_mesgbox(title, prompt, 8, 72);
+#endif
+ return (DITEM_LEAVE_MENU);
+}
+
+static int
+confirm_zone(const char *filename)
+{
+ char title[64], prompt[64];
+ time_t t = time(0);
+ struct tm *tm;
+ int rv;
+
+ setenv("TZ", filename, 1);
+ tzset();
+ tm = localtime(&t);
+
+ snprintf(title, sizeof(title), "Confirmation");
+ snprintf(prompt, sizeof(prompt),
+ "Does the abbreviation `%s' look reasonable?", tm->tm_zone);
+ rv = !dialog_yesno(title, prompt, 5, 72);
+ return (rv);
+}
+
+static int
+set_zone_multi(dialogMenuItem *dmi)
+{
+ struct zone *zp = dmi->data;
+ char *fn;
+ int rv;
+
+ if (!confirm_zone(zp->filename))
+ return (DITEM_FAILURE | DITEM_RECREATE);
+
+ asprintf(&fn, "%s/%s", _PATH_ZONEINFO, zp->filename);
+ rv = install_zone_file(fn);
+ free(fn);
+ return (rv);
+}
+
+static int
+set_zone_whole_country(dialogMenuItem *dmi)
+{
+ struct country *cp = dmi->data;
+ char *fn;
+ int rv;
+
+ if (!confirm_zone(cp->filename))
+ return (DITEM_FAILURE | DITEM_RECREATE);
+
+ asprintf(&fn, "%s/%s", _PATH_ZONEINFO, cp->filename);
+ rv = install_zone_file(fn);
+ free(fn);
+ return (rv);
+}
+
+static void
+usage(void)
+{
+
+ fprintf(stderr, "usage: tzsetup [-n]\n");
+ exit(1);
+}
+
+#if defined(__sparc64__)
+#define DIALOG_UTC dialog_yesno
+#else
+#define DIALOG_UTC dialog_noyes
+#endif
+
+int
+main(int argc, char **argv)
+{
+ char title[64], prompt[128];
+ int c, fd;
+
+ while ((c = getopt(argc, argv, "n")) != -1) {
+ switch(c) {
+ case 'n':
+ reallydoit = 0;
+ break;
+
+ default:
+ usage();
+ }
+ }
+
+ if (argc - optind > 1)
+ usage();
+
+ /* Override the user-supplied umask. */
+ (void)umask(S_IWGRP | S_IWOTH);
+
+ read_iso3166_table();
+ read_zones();
+ sort_countries();
+ make_menus();
+
+ snprintf(title, sizeof(title),
+ "Select local or UTC (Greenwich Mean Time) clock");
+ snprintf(prompt, sizeof(prompt),
+ "Is this machine's CMOS clock set to UTC? "
+ "If it is set to local time,\n"
+ "or you don't know, please choose NO here!");
+ init_dialog();
+ if (!DIALOG_UTC(title, prompt, 7, 72)) {
+ if (reallydoit)
+ unlink(_PATH_WALL_CMOS_CLOCK);
+ } else {
+ if (reallydoit) {
+ fd = open(_PATH_WALL_CMOS_CLOCK,
+ O_WRONLY | O_CREAT | O_TRUNC,
+ S_IRUSR | S_IRGRP | S_IROTH);
+ if (fd < 0)
+ err(1, "create %s", _PATH_WALL_CMOS_CLOCK);
+ close(fd);
+ }
+ }
+ dialog_clear_norefresh();
+ if (optind == argc - 1) {
+ snprintf(title, sizeof(title), "Default timezone provided");
+ snprintf(prompt, sizeof(prompt),
+ "\nUse the default `%s' zone?", argv[optind]);
+ if (!dialog_yesno(title, prompt, 7, 72)) {
+ install_zone_file(argv[optind]);
+ dialog_clear();
+ end_dialog();
+ return (0);
+ }
+ dialog_clear_norefresh();
+ }
+ snprintf(title, sizeof(title), "Time Zone Selector");
+ snprintf(prompt, sizeof(prompt), "Select a region");
+ dialog_menu(title, prompt, -1, -1, NCONTINENTS, -NCONTINENTS,
+ continents, 0, NULL, NULL);
+
+ dialog_clear();
+ end_dialog();
+ return (0);
+}
diff --git a/usr.sbin/ugidfw/Makefile b/usr.sbin/ugidfw/Makefile
new file mode 100644
index 0000000..d89bc85
--- /dev/null
+++ b/usr.sbin/ugidfw/Makefile
@@ -0,0 +1,9 @@
+# $FreeBSD$
+
+PROG= ugidfw
+MAN= ugidfw.8
+
+DPADD= ${LIBUGIDFW}
+LDADD= -lugidfw
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ugidfw/ugidfw.8 b/usr.sbin/ugidfw/ugidfw.8
new file mode 100644
index 0000000..cdd4293
--- /dev/null
+++ b/usr.sbin/ugidfw/ugidfw.8
@@ -0,0 +1,361 @@
+.\" Copyright (c) 2002, 2004 Networks Associates Technology, Inc.
+.\" All rights reserved.
+.\"
+.\" This software was developed for the FreeBSD Project by Chris
+.\" Costello at Safeport Network Services 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 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 February 24, 2004
+.Dt UGIDFW 8
+.Os
+.Sh NAME
+.Nm ugidfw
+.Nd "firewall-like access controls for file system objects"
+.Sh SYNOPSIS
+.Nm
+.Cm add
+.Cm subject
+.Op Cm not
+.Oo
+.Op Cm \&!
+.Cm uid Ar uid | minuid:maxuid
+.Oc
+.Oo
+.Op Cm \&!
+.Cm gid Ar gid | mingid:maxgid
+.Oc
+.Oo
+.Op Cm \&!
+.Cm jailid Ad jailid
+.Oc
+.Cm object
+.Op Cm not
+.Oo
+.Op Cm \&!
+.Cm uid Ar uid | minuid:maxuid
+.Oc
+.Oo
+.Op Cm \&!
+.Cm gid Ar gid | mingid:maxgid
+.Oc
+.Oo
+.Op Cm \&!
+.Cm filesys Ad path
+.Oc
+.Oo
+.Op Cm \&!
+.Cm suid
+.Oc
+.Oo
+.Op Cm \&!
+.Cm sgid
+.Oc
+.Oo
+.Op Cm \&!
+.Cm uid_of_subject
+.Oc
+.Oo
+.Op Cm \&!
+.Cm gid_of_subject
+.Oc
+.Oo
+.Op Cm \&!
+.Cm type Ar ardbclsp
+.Oc
+.Cm mode
+.Ar arswxn
+.Nm
+.Cm list
+.Nm
+.Cm set
+.Ar rulenum
+.Cm subject
+.Op Cm not
+.Oo
+.Op Cm \&!
+.Cm uid Ar uid | minuid:maxuid
+.Oc
+.Oo
+.Op Cm \&!
+.Cm gid Ar gid | mingid:maxgid
+.Oc
+.Oo
+.Op Cm \&!
+.Cm jailid Ad jailid
+.Oc
+.Cm object
+.Op Cm not
+.Oo
+.Op Cm \&!
+.Cm uid Ar uid | minuid:maxuid
+.Oc
+.Oo
+.Op Cm \&!
+.Cm gid Ar gid | mingid:maxgid
+.Oc
+.Oo
+.Op Cm \&!
+.Cm filesys Ad path
+.Oc
+.Oo
+.Op Cm \&!
+.Cm suid
+.Oc
+.Oo
+.Op Cm \&!
+.Cm sgid
+.Oc
+.Oo
+.Op Cm \&!
+.Cm uid_of_subject
+.Oc
+.Oo
+.Op Cm \&!
+.Cm gid_of_subject
+.Oc
+.Oo
+.Op Cm \&!
+.Cm type Ar ardbclsp
+.Oc
+.Cm mode
+.Ar arswxn
+.Nm
+.Cm remove
+.Ar rulenum
+.Sh DESCRIPTION
+The
+.Nm
+utility provides an
+.Xr ipfw 8 Ns -like
+interface to manage access to file system objects by UID and GID,
+supported by the
+.Xr mac_bsdextended 4
+.Xr mac 9
+policy.
+.Pp
+The arguments are as follows:
+.Bl -tag -width indent -offset indent
+.It Xo
+.Cm add
+.Cm subject
+.Ar ...
+.Cm object
+.Ar ...
+.Cm mode
+.Ar arswxn
+.Xc
+Add a new rule, automatically selecting the rule number.
+See the description of
+.Cm set
+for syntax information.
+.It Cm list
+Produces a list of all the current
+.Nm
+rules in the system.
+.It Xo
+.Cm set Ar rulenum
+.Cm subject
+.Ar ...
+.Cm object
+.Ar ...
+.Cm mode
+.Ar arswxn
+.Xc
+Add a new rule or modify an existing rule.
+The arguments are as follows:
+.Bl -tag -width ".Ar rulenum"
+.It Ar rulenum
+Rule number.
+Entries with a lower rule number
+are applied first;
+placing the most frequently-matched rules at the beginning of the list
+(i.e., lower-numbered)
+will yield a slight performance increase.
+.It Xo
+.Cm subject
+.Op Cm not
+.Oo
+.Op Cm \&!
+.Cm uid Ar uid | minuid:maxuid
+.Oc
+.Oo
+.Op Cm \&!
+.Cm gid Ar gid | mingid:maxgid
+.Oc
+.Oo
+.Op Cm \&!
+.Cm jailid Ad jailid
+.Oc
+.Xc
+Subjects performing an operation must match all the conditions given.
+A leading
+.Cm not
+means that the subject should not match the remainder of the specification.
+A condition may be prefixed by
+.Cm \&!
+to indicate that particular condition must not match the subject.
+The subject can be required to have a particular
+.Ar uid
+and/or
+.Ar gid .
+A range of uids/gids can be specified,
+seperated by a colon.
+The subject can be required to be in a particular jail with the
+.Ar jailid .
+.It Xo
+.Cm object
+.Op Cm not
+.Oo
+.Op Cm \&!
+.Cm uid Ar uid | minuid:maxuid
+.Oc
+.Oo
+.Op Cm \&!
+.Cm gid Ar gid | mingid:maxgid
+.Oc
+.Oo
+.Op Cm \&!
+.Cm filesys Ad path
+.Oc
+.Oo
+.Op Cm \&!
+.Cm suid
+.Oc
+.Oo
+.Op Cm \&!
+.Cm sgid
+.Oc
+.Oo
+.Op Cm \&!
+.Cm uid_of_subject
+.Oc
+.Oo
+.Op Cm \&!
+.Cm gid_of_subject
+.Oc
+.Oo
+.Op Cm \&!
+.Cm type Ar ardbclsp
+.Oc
+.Xc
+The rule will apply only to objects matching all the specified conditions.
+A leading
+.Cm not
+means that the object should not match all the remaining conditions.
+A condition may be prefixed by
+.Cm \&!
+to indicate that particular condition must not match the object.
+Objects can be required to be owned by the user and/or group specified by
+.Ar uid
+and/or
+.Ar gid .
+A range of uids/gids can be specified, seperated by a colon.
+The object can be required to be in a particular filesystem by
+specifing the filesystem using
+.Cm filesys .
+Note,
+if the filesystem is unmounted and remounted,
+then the rule may need to be reapplied to ensure the correct filesystem
+id is used.
+The object can be required to have the
+.Cm suid
+or
+.Cm sgid
+bits set.
+The owner of the object can be required to match the
+.Cm uid_of_subject
+or the
+.Cm gid_of_subject
+attempting the operation.
+The type of the object can be restricted to a subset of
+the following types.
+.Pp
+.Bl -tag -width ".Cm w" -compact -offset indent
+.It Cm a
+any file type
+.It Cm r
+a regular file
+.It Cm d
+a directory
+.It Cm b
+a block special device
+.It Cm c
+a character special device
+.It Cm l
+a symbolic link
+.It Cm s
+a unix domain socket
+.It Cm p
+a named pipe (FIFO)
+.El
+.It Cm mode Ar arswxn
+Similar to
+.Xr chmod 1 ,
+each character represents an access mode.
+If the rule applies,
+the specified access permissions are enforced
+for the object.
+When a character is specified in the rule,
+the rule will allow for the operation.
+Conversely, not including it will cause the operation
+to be denied.
+The definitions of each character are as follows:
+.Pp
+.Bl -tag -width ".Cm w" -compact -offset indent
+.It Cm a
+administrative operations
+.It Cm r
+read access
+.It Cm s
+access to file attributes
+.It Cm w
+write access
+.It Cm x
+execute access
+.It Cm n
+none
+.El
+.El
+.It Cm remove Ar rulenum
+Disable and remove the rule with the specified rule number.
+.El
+.Sh SEE ALSO
+.Xr mac_bsdextended 4 ,
+.Xr mac 9
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 5.0 .
+.Sh AUTHORS
+This software was contributed to the
+.Fx
+Project by 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.
diff --git a/usr.sbin/ugidfw/ugidfw.c b/usr.sbin/ugidfw/ugidfw.c
new file mode 100644
index 0000000..977922a
--- /dev/null
+++ b/usr.sbin/ugidfw/ugidfw.c
@@ -0,0 +1,214 @@
+/*-
+ * Copyright (c) 2002, 2004 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.
+ *
+ * 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/errno.h>
+#include <sys/mount.h>
+#include <sys/time.h>
+#include <sys/sysctl.h>
+
+#include <security/mac_bsdextended/mac_bsdextended.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ugidfw.h>
+
+void add_rule(int argc, char *argv[]);
+void list_rules(void);
+void remove_rule(int argc, char *argv[]);
+void set_rule(int argc, char *argv[]);
+void usage(void);
+
+void
+usage(void)
+{
+
+ fprintf(stderr, "usage: ugidfw add [subject [not] [uid uid] [gid gid]]"
+ " [object [not] [uid uid] \\\n");
+ fprintf(stderr, " [gid gid]] mode arswxn\n");
+ fprintf(stderr, " ugidfw list\n");
+ fprintf(stderr, " ugidfw set rulenum [subject [not] [uid uid] [gid gid]]"
+ " [object [not] \\\n");
+ fprintf(stderr, " [uid uid] [gid gid]] mode arswxn\n");
+ fprintf(stderr, " ugidfw remove rulenum\n");
+
+ exit(1);
+}
+
+void
+add_rule(int argc, char *argv[])
+{
+ char errstr[BUFSIZ], charstr[BUFSIZ];
+ struct mac_bsdextended_rule rule;
+ int error, rulenum;
+
+ error = bsde_parse_rule(argc, argv, &rule, BUFSIZ, errstr);
+ if (error) {
+ warnx("%s", errstr);
+ return;
+ }
+
+ error = bsde_add_rule(&rulenum, &rule, BUFSIZ, errstr);
+ if (error) {
+ warnx("%s", errstr);
+ return;
+ }
+ if (bsde_rule_to_string(&rule, charstr, BUFSIZ) == -1)
+ warnx("Added rule, but unable to print string.");
+ else
+ printf("%d %s\n", rulenum, charstr);
+}
+
+void
+list_rules(void)
+{
+ char errstr[BUFSIZ], charstr[BUFSIZ];
+ struct mac_bsdextended_rule rule;
+ int error, i, rule_count, rule_slots;
+
+ rule_slots = bsde_get_rule_slots(BUFSIZ, errstr);
+ if (rule_slots == -1) {
+ warnx("unable to get rule slots; mac_bsdextended.ko "
+ "may not be loaded");
+ errx(1, "bsde_get_rule_slots: %s", errstr);
+ }
+
+ rule_count = bsde_get_rule_count(BUFSIZ, errstr);
+ if (rule_count == -1)
+ errx(1, "bsde_get_rule_count: %s", errstr);
+
+ printf("%d slots, %d rules\n", rule_slots, rule_count);
+
+ for (i = 0; i < rule_slots; i++) {
+ error = bsde_get_rule(i, &rule, BUFSIZ, errstr);
+ switch (error) {
+ case -2:
+ continue;
+ case -1:
+ warnx("rule %d: %s", i, errstr);
+ continue;
+ case 0:
+ break;
+ }
+
+ if (bsde_rule_to_string(&rule, charstr, BUFSIZ) == -1)
+ warnx("unable to translate rule %d to string", i);
+ else
+ printf("%d %s\n", i, charstr);
+ }
+}
+
+void
+set_rule(int argc, char *argv[])
+{
+ char errstr[BUFSIZ];
+ struct mac_bsdextended_rule rule;
+ long value;
+ int error, rulenum;
+ char *endp;
+
+ if (argc < 1)
+ usage();
+
+ value = strtol(argv[0], &endp, 10);
+ if (*endp != '\0')
+ usage();
+
+ if ((long) value != (int) value || value < 0)
+ usage();
+
+ rulenum = value;
+
+ error = bsde_parse_rule(argc - 1, argv + 1, &rule, BUFSIZ, errstr);
+ if (error) {
+ warnx("%s", errstr);
+ return;
+ }
+
+ error = bsde_set_rule(rulenum, &rule, BUFSIZ, errstr);
+ if (error) {
+ warnx("%s", errstr);
+ return;
+ }
+}
+
+void
+remove_rule(int argc, char *argv[])
+{
+ char errstr[BUFSIZ];
+ long value;
+ int error, rulenum;
+ char *endp;
+
+ if (argc != 1)
+ usage();
+
+ value = strtol(argv[0], &endp, 10);
+ if (*endp != '\0')
+ usage();
+
+ if ((long) value != (int) value || value < 0)
+ usage();
+
+ rulenum = value;
+
+ error = bsde_delete_rule(rulenum, BUFSIZ, errstr);
+ if (error)
+ warnx("%s", errstr);
+}
+
+int
+main(int argc, char *argv[])
+{
+
+ if (argc < 2)
+ usage();
+
+ if (strcmp("add", argv[1]) == 0) {
+ add_rule(argc-2, argv+2);
+ } else if (strcmp("list", argv[1]) == 0) {
+ if (argc != 2)
+ usage();
+ list_rules();
+ } else if (strcmp("set", argv[1]) == 0) {
+ set_rule(argc-2, argv+2);
+ } else if (strcmp("remove", argv[1]) == 0) {
+ remove_rule(argc-2, argv+2);
+ } else
+ usage();
+
+ return (0);
+}
diff --git a/usr.sbin/usbconfig/Makefile b/usr.sbin/usbconfig/Makefile
new file mode 100644
index 0000000..1cd0928
--- /dev/null
+++ b/usr.sbin/usbconfig/Makefile
@@ -0,0 +1,9 @@
+#
+# $FreeBSD$
+#
+PROG= usbconfig
+MAN= usbconfig.8
+SRCS= usbconfig.c dump.c
+LDADD+= -lusb
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/usbconfig/dump.c b/usr.sbin/usbconfig/dump.c
new file mode 100644
index 0000000..6a2fd02
--- /dev/null
+++ b/usr.sbin/usbconfig/dump.c
@@ -0,0 +1,322 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <err.h>
+#include <string.h>
+#include <pwd.h>
+#include <grp.h>
+#include <ctype.h>
+
+#include <libusb20.h>
+#include <libusb20_desc.h>
+
+#include "dump.h"
+
+#define DUMP0(n,type,field,...) dump_field(pdev, " ", #field, n->field);
+#define DUMP1(n,type,field,...) dump_field(pdev, " ", #field, n->field);
+#define DUMP2(n,type,field,...) dump_field(pdev, " ", #field, n->field);
+#define DUMP3(n,type,field,...) dump_field(pdev, " ", #field, n->field);
+
+const char *
+dump_mode(uint8_t value)
+{
+ if (value == LIBUSB20_MODE_HOST)
+ return ("HOST");
+ return ("DEVICE");
+}
+
+const char *
+dump_speed(uint8_t value)
+{
+ ; /* style fix */
+ switch (value) {
+ case LIBUSB20_SPEED_LOW:
+ return ("LOW (1.5Mbps)");
+ case LIBUSB20_SPEED_FULL:
+ return ("FULL (12Mbps)");
+ case LIBUSB20_SPEED_HIGH:
+ return ("HIGH (480Mbps)");
+ case LIBUSB20_SPEED_VARIABLE:
+ return ("VARIABLE (52-480Mbps)");
+ case LIBUSB20_SPEED_SUPER:
+ return ("SUPER (4.8Gbps)");
+ default:
+ break;
+ }
+ return ("unknown");
+}
+
+const char *
+dump_power_mode(uint8_t value)
+{
+ ; /* style fix */
+ switch (value) {
+ case LIBUSB20_POWER_OFF:
+ return ("OFF");
+ case LIBUSB20_POWER_ON:
+ return ("ON");
+ case LIBUSB20_POWER_SAVE:
+ return ("SAVE");
+ case LIBUSB20_POWER_SUSPEND:
+ return ("SUSPEND");
+ case LIBUSB20_POWER_RESUME:
+ return ("RESUME");
+ default:
+ return ("UNKNOWN");
+ }
+}
+
+static void
+dump_field(struct libusb20_device *pdev, const char *plevel,
+ const char *field, uint32_t value)
+{
+ uint8_t temp_string[256];
+
+ printf("%s%s = 0x%04x ", plevel, field, value);
+
+ if ((field[0] != 'i') || (field[1] == 'd')) {
+ printf("\n");
+ return;
+ }
+ if (value == 0) {
+ printf(" <no string>\n");
+ return;
+ }
+ if (libusb20_dev_req_string_simple_sync(pdev, value,
+ temp_string, sizeof(temp_string))) {
+ printf(" <retrieving string failed>\n");
+ return;
+ }
+ printf(" <%s>\n", temp_string);
+ return;
+}
+
+static void
+dump_extra(struct libusb20_me_struct *str, const char *plevel)
+{
+ const uint8_t *ptr;
+ uint8_t x;
+
+ ptr = NULL;
+
+ while ((ptr = libusb20_desc_foreach(str, ptr))) {
+ printf("\n" "%sAdditional Descriptor\n\n", plevel);
+ printf("%sbLength = 0x%02x\n", plevel, ptr[0]);
+ printf("%sbDescriptorType = 0x%02x\n", plevel, ptr[1]);
+ if (ptr[0] > 1)
+ printf("%sbDescriptorSubType = 0x%02x\n",
+ plevel, ptr[2]);
+ printf("%s RAW dump: ", plevel);
+ for (x = 0; x != ptr[0]; x++) {
+ if ((x % 8) == 0) {
+ printf("\n%s 0x%02x | ", plevel, x);
+ }
+ printf("0x%02x%s", ptr[x],
+ (x != (ptr[0] - 1)) ? ", " : (x % 8) ? "\n" : "");
+ }
+ printf("\n");
+ }
+ return;
+}
+
+static void
+dump_endpoint(struct libusb20_device *pdev,
+ struct libusb20_endpoint *ep)
+{
+ struct LIBUSB20_ENDPOINT_DESC_DECODED *edesc;
+
+ edesc = &ep->desc;
+ LIBUSB20_ENDPOINT_DESC(DUMP3, edesc);
+ dump_extra(&ep->extra, " " " " " ");
+ return;
+}
+
+static void
+dump_iface(struct libusb20_device *pdev,
+ struct libusb20_interface *iface)
+{
+ struct LIBUSB20_INTERFACE_DESC_DECODED *idesc;
+ uint8_t z;
+
+ idesc = &iface->desc;
+ LIBUSB20_INTERFACE_DESC(DUMP2, idesc);
+ dump_extra(&iface->extra, " " " " " ");
+
+ for (z = 0; z != iface->num_endpoints; z++) {
+ printf("\n Endpoint %u\n", z);
+ dump_endpoint(pdev, iface->endpoints + z);
+ }
+ return;
+}
+
+void
+dump_device_info(struct libusb20_device *pdev, uint8_t show_ifdrv)
+{
+ char buf[128];
+ uint8_t n;
+
+ printf("%s, cfg=%u md=%s spd=%s pwr=%s\n",
+ libusb20_dev_get_desc(pdev),
+ libusb20_dev_get_config_index(pdev),
+ dump_mode(libusb20_dev_get_mode(pdev)),
+ dump_speed(libusb20_dev_get_speed(pdev)),
+ dump_power_mode(libusb20_dev_get_power_mode(pdev)));
+
+ if (!show_ifdrv)
+ return;
+
+ for (n = 0; n != 255; n++) {
+ if (libusb20_dev_get_iface_desc(pdev, n, buf, sizeof(buf)))
+ break;
+ if (buf[0] == 0)
+ continue;
+ printf("ugen%u.%u.%u: %s\n",
+ libusb20_dev_get_bus_number(pdev),
+ libusb20_dev_get_address(pdev), n, buf);
+ }
+}
+
+void
+dump_be_quirk_names(struct libusb20_backend *pbe)
+{
+ struct libusb20_quirk q;
+ uint16_t x;
+ int error;
+
+ memset(&q, 0, sizeof(q));
+
+ printf("\nDumping list of supported quirks:\n\n");
+
+ for (x = 0; x != 0xFFFF; x++) {
+
+ error = libusb20_be_get_quirk_name(pbe, x, &q);
+ if (error) {
+ if (x == 0) {
+ printf("No quirk names - maybe the USB quirk "
+ "module has not been loaded.\n");
+ }
+ break;
+ }
+ if (strcmp(q.quirkname, "UQ_NONE"))
+ printf("%s\n", q.quirkname);
+ }
+ printf("\n");
+ return;
+}
+
+void
+dump_be_dev_quirks(struct libusb20_backend *pbe)
+{
+ struct libusb20_quirk q;
+ uint16_t x;
+ int error;
+
+ memset(&q, 0, sizeof(q));
+
+ printf("\nDumping current device quirks:\n\n");
+
+ for (x = 0; x != 0xFFFF; x++) {
+
+ error = libusb20_be_get_dev_quirk(pbe, x, &q);
+ if (error) {
+ if (x == 0) {
+ printf("No device quirks - maybe the USB quirk "
+ "module has not been loaded.\n");
+ }
+ break;
+ }
+ if (strcmp(q.quirkname, "UQ_NONE")) {
+ printf("VID=0x%04x PID=0x%04x REVLO=0x%04x "
+ "REVHI=0x%04x QUIRK=%s\n",
+ q.vid, q.pid, q.bcdDeviceLow,
+ q.bcdDeviceHigh, q.quirkname);
+ }
+ }
+ printf("\n");
+ return;
+}
+
+void
+dump_device_desc(struct libusb20_device *pdev)
+{
+ struct LIBUSB20_DEVICE_DESC_DECODED *ddesc;
+
+ ddesc = libusb20_dev_get_device_desc(pdev);
+ LIBUSB20_DEVICE_DESC(DUMP0, ddesc);
+ return;
+}
+
+void
+dump_config(struct libusb20_device *pdev, uint8_t all_cfg)
+{
+ struct LIBUSB20_CONFIG_DESC_DECODED *cdesc;
+ struct LIBUSB20_DEVICE_DESC_DECODED *ddesc;
+ struct libusb20_config *pcfg = NULL;
+ uint8_t cfg_index;
+ uint8_t cfg_index_end;
+ uint8_t x;
+ uint8_t y;
+
+ ddesc = libusb20_dev_get_device_desc(pdev);
+
+ if (all_cfg) {
+ cfg_index = 0;
+ cfg_index_end = ddesc->bNumConfigurations;
+ } else {
+ cfg_index = libusb20_dev_get_config_index(pdev);
+ cfg_index_end = cfg_index + 1;
+ }
+
+ for (; cfg_index != cfg_index_end; cfg_index++) {
+
+ pcfg = libusb20_dev_alloc_config(pdev, cfg_index);
+ if (!pcfg) {
+ continue;
+ }
+ printf("\n Configuration index %u\n\n", cfg_index);
+ cdesc = &(pcfg->desc);
+ LIBUSB20_CONFIG_DESC(DUMP1, cdesc);
+ dump_extra(&(pcfg->extra), " " " ");
+
+ for (x = 0; x != pcfg->num_interface; x++) {
+ printf("\n Interface %u\n", x);
+ dump_iface(pdev, pcfg->interface + x);
+ printf("\n");
+ for (y = 0; y != (pcfg->interface + x)->num_altsetting; y++) {
+ printf("\n Interface %u Alt %u\n", x, y + 1);
+ dump_iface(pdev,
+ (pcfg->interface + x)->altsetting + y);
+ printf("\n");
+ }
+ }
+ printf("\n");
+ free(pcfg);
+ }
+ return;
+}
diff --git a/usr.sbin/usbconfig/dump.h b/usr.sbin/usbconfig/dump.h
new file mode 100644
index 0000000..7fafdfd
--- /dev/null
+++ b/usr.sbin/usbconfig/dump.h
@@ -0,0 +1,34 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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.
+ */
+
+const char *dump_mode(uint8_t value);
+const char *dump_speed(uint8_t value);
+const char *dump_power_mode(uint8_t value);
+void dump_device_info(struct libusb20_device *pdev, uint8_t show_drv);
+void dump_be_quirk_names(struct libusb20_backend *pbe);
+void dump_be_dev_quirks(struct libusb20_backend *pbe);
+void dump_device_desc(struct libusb20_device *pdev);
+void dump_config(struct libusb20_device *pdev, uint8_t all_cfg);
diff --git a/usr.sbin/usbconfig/usbconfig.8 b/usr.sbin/usbconfig/usbconfig.8
new file mode 100644
index 0000000..ffdb4ce
--- /dev/null
+++ b/usr.sbin/usbconfig/usbconfig.8
@@ -0,0 +1,53 @@
+.\" $FreeBSD$
+.\"
+.\" Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd Sep 28, 2008
+.Dt USBCONFIG 8
+.Os
+.Sh NAME
+.Nm usbconfig
+.Nd configure the USB subsystem
+.Sh SYNOPSIS
+.Nm
+.Op Fl u Ar unit
+.Op Fl a Ar addr
+.Op cmds...
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to configure and dump information about the USB subsystem.
+.Pp
+The options are as follows:
+.Bl -tag -width " "
+.It Fl u Ar unit
+Limit device range to USB devices connected to the given USBUS unit.
+.It Fl a Ar addr
+Limit device range to the given USB device index.
+Should only be used in conjunction with the unit argument.
+.It Fl h
+Show help and available commands.
+.El
+.Sh SEE ALSO
+.Xr usb2_core 4
diff --git a/usr.sbin/usbconfig/usbconfig.c b/usr.sbin/usbconfig/usbconfig.c
new file mode 100644
index 0000000..810e183
--- /dev/null
+++ b/usr.sbin/usbconfig/usbconfig.c
@@ -0,0 +1,711 @@
+/* $FreeBSD$ */
+/*-
+ * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <err.h>
+#include <string.h>
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+#include <ctype.h>
+
+#include <libusb20_desc.h>
+#include <libusb20.h>
+
+#include "dump.h"
+
+struct options {
+ const char *quirkname;
+ void *buffer;
+ int template;
+ gid_t gid;
+ uid_t uid;
+ mode_t mode;
+ uint32_t got_any;
+ struct LIBUSB20_CONTROL_SETUP_DECODED setup;
+ uint16_t bus;
+ uint16_t addr;
+ uint16_t iface;
+ uint16_t vid;
+ uint16_t pid;
+ uint16_t lo_rev; /* inclusive */
+ uint16_t hi_rev; /* inclusive */
+ uint8_t string_index;
+ uint8_t config_index;
+ uint8_t alt_index;
+ uint8_t got_list:1;
+ uint8_t got_bus:1;
+ uint8_t got_addr:1;
+ uint8_t got_iface:1;
+ uint8_t got_set_config:1;
+ uint8_t got_set_alt:1;
+ uint8_t got_set_template:1;
+ uint8_t got_get_template:1;
+ uint8_t got_suspend:1;
+ uint8_t got_resume:1;
+ uint8_t got_reset:1;
+ uint8_t got_power_off:1;
+ uint8_t got_power_save:1;
+ uint8_t got_power_on:1;
+ uint8_t got_dump_device_quirks:1;
+ uint8_t got_dump_quirk_names:1;
+ uint8_t got_dump_device_desc:1;
+ uint8_t got_dump_curr_config:1;
+ uint8_t got_dump_all_config:1;
+ uint8_t got_dump_info:1;
+ uint8_t got_show_iface_driver:1;
+ uint8_t got_remove_device_quirk:1;
+ uint8_t got_add_device_quirk:1;
+ uint8_t got_dump_string:1;
+ uint8_t got_do_request:1;
+};
+
+struct token {
+ const char *name;
+ uint8_t value;
+ uint8_t narg;
+};
+
+enum {
+ T_UNIT,
+ T_ADDR,
+ T_IFACE,
+ T_SET_CONFIG,
+ T_SET_ALT,
+ T_SET_TEMPLATE,
+ T_GET_TEMPLATE,
+ T_ADD_DEVICE_QUIRK,
+ T_REMOVE_DEVICE_QUIRK,
+ T_SHOW_IFACE_DRIVER,
+ T_DUMP_QUIRK_NAMES,
+ T_DUMP_DEVICE_QUIRKS,
+ T_DUMP_DEVICE_DESC,
+ T_DUMP_CURR_CONFIG_DESC,
+ T_DUMP_ALL_CONFIG_DESC,
+ T_DUMP_STRING,
+ T_DUMP_INFO,
+ T_SUSPEND,
+ T_RESUME,
+ T_POWER_OFF,
+ T_POWER_SAVE,
+ T_POWER_ON,
+ T_RESET,
+ T_LIST,
+ T_DO_REQUEST,
+};
+
+static struct options options;
+
+static const struct token token[] = {
+ {"-u", T_UNIT, 1},
+ {"-a", T_ADDR, 1},
+ {"-i", T_IFACE, 1},
+ {"set_config", T_SET_CONFIG, 1},
+ {"set_alt", T_SET_ALT, 1},
+ {"set_template", T_SET_TEMPLATE, 1},
+ {"get_template", T_GET_TEMPLATE, 0},
+ {"add_dev_quirk_vplh", T_ADD_DEVICE_QUIRK, 5},
+ {"remove_dev_quirk_vplh", T_REMOVE_DEVICE_QUIRK, 5},
+ {"dump_quirk_names", T_DUMP_QUIRK_NAMES, 0},
+ {"dump_device_quirks", T_DUMP_DEVICE_QUIRKS, 0},
+ {"dump_device_desc", T_DUMP_DEVICE_DESC, 0},
+ {"dump_curr_config_desc", T_DUMP_CURR_CONFIG_DESC, 0},
+ {"dump_all_config_desc", T_DUMP_ALL_CONFIG_DESC, 0},
+ {"dump_string", T_DUMP_STRING, 1},
+ {"dump_info", T_DUMP_INFO, 0},
+ {"show_ifdrv", T_SHOW_IFACE_DRIVER, 0},
+ {"suspend", T_SUSPEND, 0},
+ {"resume", T_RESUME, 0},
+ {"power_off", T_POWER_OFF, 0},
+ {"power_save", T_POWER_SAVE, 0},
+ {"power_on", T_POWER_ON, 0},
+ {"reset", T_RESET, 0},
+ {"list", T_LIST, 0},
+ {"do_request", T_DO_REQUEST, 5},
+};
+
+static void
+be_dev_remove_quirk(struct libusb20_backend *pbe,
+ uint16_t vid, uint16_t pid, uint16_t lorev, uint16_t hirev,
+ const char *str)
+{
+ struct libusb20_quirk q;
+ int error;
+
+ memset(&q, 0, sizeof(q));
+
+ q.vid = vid;
+ q.pid = pid;
+ q.bcdDeviceLow = lorev;
+ q.bcdDeviceHigh = hirev;
+ strlcpy(q.quirkname, str, sizeof(q.quirkname));
+
+ error = libusb20_be_remove_dev_quirk(pbe, &q);
+ if (error) {
+ printf("Removing quirk '%s' failed, continuing.\n", str);
+ }
+ return;
+}
+
+static void
+be_dev_add_quirk(struct libusb20_backend *pbe,
+ uint16_t vid, uint16_t pid, uint16_t lorev, uint16_t hirev,
+ const char *str)
+{
+ struct libusb20_quirk q;
+ int error;
+
+ memset(&q, 0, sizeof(q));
+
+ q.vid = vid;
+ q.pid = pid;
+ q.bcdDeviceLow = lorev;
+ q.bcdDeviceHigh = hirev;
+ strlcpy(q.quirkname, str, sizeof(q.quirkname));
+
+ error = libusb20_be_add_dev_quirk(pbe, &q);
+ if (error) {
+ printf("Adding quirk '%s' failed, continuing.\n", str);
+ }
+ return;
+}
+
+static uint8_t
+get_token(const char *str, uint8_t narg)
+{
+ uint8_t n;
+
+ for (n = 0; n != (sizeof(token) / sizeof(token[0])); n++) {
+ if (strcasecmp(str, token[n].name) == 0) {
+ if (token[n].narg > narg) {
+ /* too few arguments */
+ break;
+ }
+ return (token[n].value);
+ }
+ }
+ return (0 - 1);
+}
+
+static uid_t
+num_id(const char *name, const char *type)
+{
+ uid_t val;
+ char *ep;
+
+ errno = 0;
+ val = strtoul(name, &ep, 0);
+ if (errno) {
+ err(1, "%s", name);
+ }
+ if (*ep != '\0') {
+ errx(1, "%s: illegal %s name", name, type);
+ }
+ return (val);
+}
+
+static int
+get_int(const char *s)
+{
+ int val;
+ char *ep;
+
+ errno = 0;
+ val = strtoul(s, &ep, 0);
+ if (errno) {
+ err(1, "%s", s);
+ }
+ if (*ep != '\0') {
+ errx(1, "illegal number: %s", s);
+ }
+ return val;
+}
+
+static void
+usage(void)
+{
+ printf(""
+ "usbconfig - configure the USB subsystem" "\n"
+ "usage: usbconfig -u <busnum> -a <devaddr> -i <ifaceindex> [cmds...]" "\n"
+ "commands:" "\n"
+ " set_config <cfg_index>" "\n"
+ " set_alt <alt_index>" "\n"
+ " set_template <template>" "\n"
+ " get_template" "\n"
+ " add_dev_quirk_vplh <vid> <pid> <lo_rev> <hi_rev> <quirk>" "\n"
+ " remove_dev_quirk_vplh <vid> <pid> <lo_rev> <hi_rev> <quirk>" "\n"
+ " dump_quirk_names" "\n"
+ " dump_device_quirks" "\n"
+ " dump_device_desc" "\n"
+ " dump_curr_config_desc" "\n"
+ " dump_all_config_desc" "\n"
+ " dump_string <index>" "\n"
+ " dump_info" "\n"
+ " show_ifdrv" "\n"
+ " suspend" "\n"
+ " resume" "\n"
+ " power_off" "\n"
+ " power_save" "\n"
+ " power_on" "\n"
+ " reset" "\n"
+ " list" "\n"
+ " do_request <bmReqTyp> <bReq> <wVal> <wIdx> <wLen> <data...>" "\n"
+ );
+ exit(1);
+}
+
+static void
+reset_options(struct options *opt)
+{
+ if (opt->buffer)
+ free(opt->buffer);
+ memset(opt, 0, sizeof(*opt));
+ return;
+}
+
+static void
+flush_command(struct libusb20_backend *pbe, struct options *opt)
+{
+ struct libusb20_device *pdev = NULL;
+ uint32_t matches = 0;
+ uint8_t dump_any;
+
+ /* check for invalid option combinations */
+ if ((opt->got_suspend +
+ opt->got_resume +
+ opt->got_reset +
+ opt->got_set_config +
+ opt->got_set_alt +
+ opt->got_power_save +
+ opt->got_power_on +
+ opt->got_power_off) > 1) {
+ err(1, "can only specify one of 'set_config', "
+ "'set_alt', 'reset', 'suspend', 'resume', "
+ "'power_save', 'power_on' and 'power_off' "
+ "at the same time!");
+ }
+ if (opt->got_dump_quirk_names) {
+ opt->got_any--;
+ dump_be_quirk_names(pbe);
+ }
+ if (opt->got_dump_device_quirks) {
+ opt->got_any--;
+ dump_be_dev_quirks(pbe);
+ }
+ if (opt->got_remove_device_quirk) {
+ opt->got_any--;
+ be_dev_remove_quirk(pbe,
+ opt->vid, opt->pid, opt->lo_rev, opt->hi_rev, opt->quirkname);
+ }
+ if (opt->got_add_device_quirk) {
+ opt->got_any--;
+ be_dev_add_quirk(pbe,
+ opt->vid, opt->pid, opt->lo_rev, opt->hi_rev, opt->quirkname);
+ }
+ if (opt->got_set_template) {
+ opt->got_any--;
+ if (libusb20_be_set_template(pbe, opt->template)) {
+ printf("Setting USB template %u failed, "
+ "continuing.\n", opt->template);
+ }
+ }
+ if (opt->got_get_template) {
+ opt->got_any--;
+ if (libusb20_be_get_template(pbe, &opt->template))
+ printf("USB template: <unknown>\n");
+ else
+ printf("USB template: %u\n", opt->template);
+ }
+ if (opt->got_any == 0) {
+ /*
+ * do not scan through all the devices if there are no valid
+ * options
+ */
+ goto done;
+ }
+ while ((pdev = libusb20_be_device_foreach(pbe, pdev))) {
+
+ if (opt->got_bus &&
+ (libusb20_dev_get_bus_number(pdev) != opt->bus)) {
+ continue;
+ }
+ if (opt->got_addr &&
+ (libusb20_dev_get_address(pdev) != opt->addr)) {
+ continue;
+ }
+ matches++;
+
+ if (libusb20_dev_open(pdev, 0)) {
+ err(1, "could not open device");
+ }
+ if (opt->got_dump_string) {
+ char *pbuf;
+
+ pbuf = malloc(256);
+ if (pbuf == NULL) {
+ err(1, "out of memory");
+ }
+ if (libusb20_dev_req_string_simple_sync(pdev,
+ opt->string_index, pbuf, 256)) {
+ printf("STRING_0x%02x = <read error>\n",
+ opt->string_index);
+ } else {
+ printf("STRING_0x%02x = <%s>\n",
+ opt->string_index, pbuf);
+ }
+ free(pbuf);
+ }
+ if (opt->got_do_request) {
+ uint16_t actlen;
+ uint16_t t;
+
+ if (libusb20_dev_request_sync(pdev, &opt->setup,
+ opt->buffer, &actlen, 5000 /* 5 seconds */ , 0)) {
+ printf("REQUEST = <ERROR>\n");
+ } else if (!(opt->setup.bmRequestType &
+ LIBUSB20_ENDPOINT_IN)) {
+ printf("REQUEST = <OK>\n");
+ } else {
+ t = actlen;
+ printf("REQUEST = <");
+ for (t = 0; t != actlen; t++) {
+ printf("0x%02x%s",
+ ((uint8_t *)opt->buffer)[t],
+ (t == (actlen - 1)) ? "" : " ");
+ }
+ printf("><");
+ for (t = 0; t != actlen; t++) {
+ char c;
+
+ c = ((uint8_t *)opt->buffer)[t];
+ if ((c != '<') &&
+ (c != '>') && isprint(c)) {
+ putchar(c);
+ }
+ }
+ printf(">\n");
+ }
+ }
+ if (opt->got_set_config) {
+ if (libusb20_dev_set_config_index(pdev,
+ opt->config_index)) {
+ err(1, "could not set config index");
+ }
+ }
+ if (opt->got_set_alt) {
+ if (libusb20_dev_set_alt_index(pdev, opt->iface,
+ opt->alt_index)) {
+ err(1, "could not set alternate setting");
+ }
+ }
+ if (opt->got_reset) {
+ if (libusb20_dev_reset(pdev)) {
+ err(1, "could not reset device");
+ }
+ }
+ if (opt->got_suspend) {
+ if (libusb20_dev_set_power_mode(pdev,
+ LIBUSB20_POWER_SUSPEND)) {
+ err(1, "could not set suspend");
+ }
+ }
+ if (opt->got_resume) {
+ if (libusb20_dev_set_power_mode(pdev,
+ LIBUSB20_POWER_RESUME)) {
+ err(1, "could not set resume");
+ }
+ }
+ if (opt->got_power_off) {
+ if (libusb20_dev_set_power_mode(pdev,
+ LIBUSB20_POWER_OFF)) {
+ err(1, "could not set power OFF");
+ }
+ }
+ if (opt->got_power_save) {
+ if (libusb20_dev_set_power_mode(pdev,
+ LIBUSB20_POWER_SAVE)) {
+ err(1, "could not set power SAVE");
+ }
+ }
+ if (opt->got_power_on) {
+ if (libusb20_dev_set_power_mode(pdev,
+ LIBUSB20_POWER_ON)) {
+ err(1, "could not set power ON");
+ }
+ }
+ dump_any =
+ (opt->got_dump_device_desc ||
+ opt->got_dump_curr_config ||
+ opt->got_dump_all_config ||
+ opt->got_dump_info);
+
+ if (opt->got_list || dump_any) {
+ dump_device_info(pdev,
+ opt->got_show_iface_driver);
+ }
+ if (opt->got_dump_device_desc) {
+ printf("\n");
+ dump_device_desc(pdev);
+ }
+ if (opt->got_dump_all_config) {
+ printf("\n");
+ dump_config(pdev, 1);
+ } else if (opt->got_dump_curr_config) {
+ printf("\n");
+ dump_config(pdev, 0);
+ }
+ if (dump_any) {
+ printf("\n");
+ }
+ if (libusb20_dev_close(pdev)) {
+ err(1, "could not close device");
+ }
+ }
+
+ if (matches == 0) {
+ printf("No device match or lack of permissions.\n");
+ }
+done:
+ reset_options(opt);
+
+ return;
+}
+
+int
+main(int argc, char **argv)
+{
+ struct libusb20_backend *pbe;
+ struct options *opt = &options;
+ int n;
+ int t;
+
+ if (argc < 1) {
+ usage();
+ }
+ pbe = libusb20_be_alloc_default();
+ if (pbe == NULL)
+ err(1, "could not access USB backend\n");
+
+ for (n = 1; n != argc; n++) {
+
+ /* get number of additional options */
+ t = (argc - n - 1);
+ if (t > 255)
+ t = 255;
+ switch (get_token(argv[n], t)) {
+ case T_ADD_DEVICE_QUIRK:
+ if (opt->got_add_device_quirk) {
+ flush_command(pbe, opt);
+ }
+ opt->vid = num_id(argv[n + 1], "Vendor ID");
+ opt->pid = num_id(argv[n + 2], "Product ID");
+ opt->lo_rev = num_id(argv[n + 3], "Low Revision");
+ opt->hi_rev = num_id(argv[n + 4], "High Revision");
+ opt->quirkname = argv[n + 5];
+ n += 5;
+
+ opt->got_add_device_quirk = 1;
+ opt->got_any++;
+ break;
+
+ case T_REMOVE_DEVICE_QUIRK:
+ if (opt->got_remove_device_quirk) {
+ flush_command(pbe, opt);
+ }
+ opt->vid = num_id(argv[n + 1], "Vendor ID");
+ opt->pid = num_id(argv[n + 2], "Product ID");
+ opt->lo_rev = num_id(argv[n + 3], "Low Revision");
+ opt->hi_rev = num_id(argv[n + 4], "High Revision");
+ opt->quirkname = argv[n + 5];
+ n += 5;
+ opt->got_remove_device_quirk = 1;
+ opt->got_any++;
+ break;
+
+ case T_DUMP_QUIRK_NAMES:
+ opt->got_dump_quirk_names = 1;
+ opt->got_any++;
+ break;
+
+ case T_DUMP_DEVICE_QUIRKS:
+ opt->got_dump_device_quirks = 1;
+ opt->got_any++;
+ break;
+
+ case T_SHOW_IFACE_DRIVER:
+ opt->got_show_iface_driver = 1;
+ break;
+
+ case T_UNIT:
+ if (opt->got_any) {
+ /* allow multiple commands on the same line */
+ flush_command(pbe, opt);
+ }
+ opt->bus = num_id(argv[n + 1], "busnum");
+ opt->got_bus = 1;
+ n++;
+ break;
+ case T_ADDR:
+ opt->addr = num_id(argv[n + 1], "addr");
+ opt->got_addr = 1;
+ n++;
+ break;
+ case T_IFACE:
+ opt->iface = num_id(argv[n + 1], "iface");
+ opt->got_iface = 1;
+ n++;
+ break;
+ case T_SET_CONFIG:
+ opt->config_index = num_id(argv[n + 1], "cfg_index");
+ opt->got_set_config = 1;
+ opt->got_any++;
+ n++;
+ break;
+ case T_SET_ALT:
+ opt->alt_index = num_id(argv[n + 1], "cfg_index");
+ opt->got_set_alt = 1;
+ opt->got_any++;
+ n++;
+ break;
+ case T_SET_TEMPLATE:
+ opt->template = get_int(argv[n + 1]);
+ opt->got_set_template = 1;
+ opt->got_any++;
+ n++;
+ break;
+ case T_GET_TEMPLATE:
+ opt->got_get_template = 1;
+ opt->got_any++;
+ break;
+ case T_DUMP_DEVICE_DESC:
+ opt->got_dump_device_desc = 1;
+ opt->got_any++;
+ break;
+ case T_DUMP_CURR_CONFIG_DESC:
+ opt->got_dump_curr_config = 1;
+ opt->got_any++;
+ break;
+ case T_DUMP_ALL_CONFIG_DESC:
+ opt->got_dump_all_config = 1;
+ opt->got_any++;
+ break;
+ case T_DUMP_INFO:
+ opt->got_dump_info = 1;
+ opt->got_any++;
+ break;
+ case T_DUMP_STRING:
+ if (opt->got_dump_string) {
+ flush_command(pbe, opt);
+ }
+ opt->string_index = num_id(argv[n + 1], "str_index");
+ opt->got_dump_string = 1;
+ opt->got_any++;
+ n++;
+ break;
+ case T_SUSPEND:
+ opt->got_suspend = 1;
+ opt->got_any++;
+ break;
+ case T_RESUME:
+ opt->got_resume = 1;
+ opt->got_any++;
+ break;
+ case T_POWER_OFF:
+ opt->got_power_off = 1;
+ opt->got_any++;
+ break;
+ case T_POWER_SAVE:
+ opt->got_power_save = 1;
+ opt->got_any++;
+ break;
+ case T_POWER_ON:
+ opt->got_power_on = 1;
+ opt->got_any++;
+ break;
+ case T_RESET:
+ opt->got_reset = 1;
+ opt->got_any++;
+ break;
+ case T_LIST:
+ opt->got_list = 1;
+ opt->got_any++;
+ break;
+ case T_DO_REQUEST:
+ if (opt->got_do_request) {
+ flush_command(pbe, opt);
+ }
+ LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &opt->setup);
+ opt->setup.bmRequestType = num_id(argv[n + 1], "bmReqTyp");
+ opt->setup.bRequest = num_id(argv[n + 2], "bReq");
+ opt->setup.wValue = num_id(argv[n + 3], "wVal");
+ opt->setup.wIndex = num_id(argv[n + 4], "wIndex");
+ opt->setup.wLength = num_id(argv[n + 5], "wLen");
+ if (opt->setup.wLength != 0) {
+ opt->buffer = malloc(opt->setup.wLength);
+ } else {
+ opt->buffer = NULL;
+ }
+
+ n += 5;
+
+ if (!(opt->setup.bmRequestType &
+ LIBUSB20_ENDPOINT_IN)) {
+ /* copy in data */
+ t = (argc - n - 1);
+ if (t < opt->setup.wLength) {
+ err(1, "request data missing");
+ }
+ t = opt->setup.wLength;
+ while (t--) {
+ ((uint8_t *)opt->buffer)[t] =
+ num_id(argv[n + t + 1], "req_data");
+ }
+ n += opt->setup.wLength;
+ }
+ opt->got_do_request = 1;
+ opt->got_any++;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+ if (opt->got_any) {
+ /* flush out last command */
+ flush_command(pbe, opt);
+ } else {
+ /* list all the devices */
+ opt->got_list = 1;
+ opt->got_any++;
+ flush_command(pbe, opt);
+ }
+ /* release data */
+ libusb20_be_free(pbe);
+
+ return (0);
+}
diff --git a/usr.sbin/usbdevs/Makefile b/usr.sbin/usbdevs/Makefile
new file mode 100644
index 0000000..985bc7a
--- /dev/null
+++ b/usr.sbin/usbdevs/Makefile
@@ -0,0 +1,9 @@
+# $NetBSD: Makefile,v 1.2 1998/07/12 20:40:45 augustss Exp $
+# FreeBSD $FreeBSD$
+
+PROG= usbdevs
+MAN= usbdevs.8
+
+WARNS?= 2
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/usbdevs/usbdevs.8 b/usr.sbin/usbdevs/usbdevs.8
new file mode 100644
index 0000000..3226dd5
--- /dev/null
+++ b/usr.sbin/usbdevs/usbdevs.8
@@ -0,0 +1,77 @@
+.\" $NetBSD: usbdevs.8,v 1.5 2000/10/15 12:44:11 bjh21 Exp $
+.\" Copyright (c) 1999 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" Author: Lennart Augustsson
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd May 24, 2004
+.Dt USBDEVS 8
+.Os
+.Sh NAME
+.Nm usbdevs
+.Nd show USB devices connected to the system
+.Sh SYNOPSIS
+.Nm
+.Op Fl a Ar addr
+.Op Fl d
+.Op Fl f Ar dev
+.Op Fl o
+.Op Fl v
+.Sh DESCRIPTION
+The
+.Nm
+utility prints a listing of all USB devices connected to the system
+with some information about each device.
+The indentation of each line indicates its distance from the root.
+.Pp
+The options are as follows:
+.Bl -tag -width ".Fl a Ar addr"
+.It Fl a Ar addr
+only print information about the device at the given address.
+.It Fl d
+Show the device drivers associated with each device.
+.It Fl f Ar dev
+only print information for the given USB controller.
+.It Fl o
+One-line output (only useful in combination with
+.Fl d ) .
+.It Fl v
+Be verbose.
+.El
+.Sh SEE ALSO
+.Xr usb 4
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Nx 1.4 .
diff --git a/usr.sbin/usbdevs/usbdevs.c b/usr.sbin/usbdevs/usbdevs.c
new file mode 100644
index 0000000..eea8167
--- /dev/null
+++ b/usr.sbin/usbdevs/usbdevs.c
@@ -0,0 +1,240 @@
+/* $NetBSD: usbdevs.c,v 1.22 2003/11/12 13:31:08 grant Exp $ */
+/* $FreeBSD$ */
+
+/*
+ * Copyright (c) 1998 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Lennart Augustsson (augustss@NetBSD.org).
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <err.h>
+#include <errno.h>
+#include <dev/usb/usb.h>
+#if defined(__FreeBSD__)
+#include <sys/ioctl.h>
+#endif
+
+#define USBDEV "/dev/usb"
+
+int verbose = 0;
+int showdevs = 0;
+int oneline = 0;
+
+void usage(void);
+void usbdev(int f, int a, int rec);
+void usbdump(int f);
+void dumpone(char *name, int f, int addr);
+int main(int, char **);
+
+void
+usage()
+{
+ fprintf(stderr, "usage: %s [-a addr] [-d] [-f dev] [-o] [-v]\n",
+ getprogname());
+ exit(1);
+}
+
+char done[USB_MAX_DEVICES];
+int indent;
+
+void
+usbdev(int f, int a, int rec)
+{
+ struct usb_device_info di;
+ int e, p, i;
+
+ di.udi_addr = a;
+ e = ioctl(f, USB_DEVICEINFO, &di);
+ if (e) {
+ if (errno != ENXIO)
+ printf("addr %d: I/O error\n", a);
+ return;
+ }
+ printf("addr %d: ", a);
+ done[a] = 1;
+ if (verbose) {
+ switch (di.udi_speed) {
+ case USB_SPEED_LOW: printf("low speed, "); break;
+ case USB_SPEED_FULL: printf("full speed, "); break;
+ case USB_SPEED_HIGH: printf("high speed, "); break;
+ default: break;
+ }
+ if (di.udi_power)
+ printf("power %d mA, ", di.udi_power);
+ else
+ printf("self powered, ");
+ if (di.udi_config)
+ printf("config %d, ", di.udi_config);
+ else
+ printf("unconfigured, ");
+ }
+ if (verbose) {
+ printf("%s(0x%04x), %s(0x%04x), rev %s",
+ di.udi_product, di.udi_productNo,
+ di.udi_vendor, di.udi_vendorNo, di.udi_release);
+ } else
+ printf("%s, %s", di.udi_product, di.udi_vendor);
+ if (!oneline)
+ printf("\n");
+ if (showdevs) {
+ for (i = 0; i < USB_MAX_DEVNAMES; i++) {
+ if (di.udi_devnames[i][0]) {
+ if (oneline)
+ printf(", device %s",
+ di.udi_devnames[i]);
+ else
+ printf("%*s %s\n", indent, "",
+ di.udi_devnames[i]);
+ }
+ }
+ }
+ if (oneline)
+ printf("\n");
+ if (!rec)
+ return;
+ for (p = 0; p < di.udi_nports; p++) {
+ int s = di.udi_ports[p];
+ if (s >= USB_MAX_DEVICES) {
+ if (verbose) {
+ printf("%*sport %d %s\n", indent+1, "", p+1,
+ s == USB_PORT_ENABLED ? "enabled" :
+ s == USB_PORT_SUSPENDED ? "suspended" :
+ s == USB_PORT_POWERED ? "powered" :
+ s == USB_PORT_DISABLED ? "disabled" :
+ "???");
+
+ }
+ continue;
+ }
+ indent++;
+ printf("%*s", indent, "");
+ if (verbose)
+ printf("port %d ", p+1);
+ if (s == 0)
+ printf("addr 0 should never happen!\n");
+ else
+ usbdev(f, s, 1);
+ indent--;
+ }
+}
+
+void
+usbdump(int f)
+{
+ int a;
+
+ for (a = 1; a < USB_MAX_DEVICES; a++) {
+ if (!done[a])
+ usbdev(f, a, 1);
+ }
+}
+
+void
+dumpone(char *name, int f, int addr)
+{
+ if (verbose)
+ printf("Controller %s:\n", name);
+ indent = 0;
+ memset(done, 0, sizeof done);
+ if (addr)
+ usbdev(f, addr, 0);
+ else
+ usbdump(f);
+}
+
+int
+main(int argc, char **argv)
+{
+ int ch, i, f;
+ char buf[50];
+ char *dev = 0;
+ int addr = 0;
+ int ncont;
+
+ while ((ch = getopt(argc, argv, "a:df:ov?")) != -1) {
+ switch(ch) {
+ case 'a':
+ addr = atoi(optarg);
+ break;
+ case 'd':
+ showdevs++;
+ break;
+ case 'f':
+ dev = optarg;
+ break;
+ case 'o':
+ oneline++;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (dev == 0) {
+ for (ncont = 0, i = 0; i < 10; i++) {
+ snprintf(buf, sizeof(buf), "%s%d", USBDEV, i);
+ f = open(buf, O_RDONLY);
+ if (f >= 0) {
+ dumpone(buf, f, addr);
+ close(f);
+ } else {
+ if (errno == ENOENT || errno == ENXIO)
+ continue;
+ warn("%s", buf);
+ }
+ ncont++;
+ }
+ if (verbose && ncont == 0)
+ printf("%s: no USB controllers found\n",
+ getprogname());
+ } else {
+ f = open(dev, O_RDONLY);
+ if (f >= 0)
+ dumpone(dev, f, addr);
+ else
+ err(1, "%s", dev);
+ }
+ exit(0);
+}
diff --git a/usr.sbin/vidcontrol/Makefile b/usr.sbin/vidcontrol/Makefile
new file mode 100644
index 0000000..074f941
--- /dev/null
+++ b/usr.sbin/vidcontrol/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= vidcontrol
+SRCS= vidcontrol.c decode.c
+
+WARNS?= 6
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/vidcontrol/decode.c b/usr.sbin/vidcontrol/decode.c
new file mode 100644
index 0000000..27691d2
--- /dev/null
+++ b/usr.sbin/vidcontrol/decode.c
@@ -0,0 +1,99 @@
+/*-
+ * Copyright (c) 1994 Søren Schmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must 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 <stdio.h>
+#include <string.h>
+#include "decode.h"
+
+int decode(FILE *fd, char *buffer, int len)
+{
+ int n, pos = 0, tpos;
+ char *bp, *p;
+ char tbuffer[3];
+ char temp[128];
+
+#define DEC(c) (((c) - ' ') & 0x3f)
+
+ do {
+ if (!fgets(temp, sizeof(temp), fd))
+ return(0);
+ } while (strncmp(temp, "begin ", 6));
+ sscanf(temp, "begin %o %s", (unsigned *)&n, temp);
+ bp = buffer;
+ for (;;) {
+ if (!fgets(p = temp, sizeof(temp), fd))
+ return(0);
+ if ((n = DEC(*p)) <= 0)
+ break;
+ for (++p; n > 0; p += 4, n -= 3) {
+ tpos = 0;
+ if (n >= 3) {
+ tbuffer[tpos++] = DEC(p[0])<<2 | DEC(p[1])>>4;
+ tbuffer[tpos++] = DEC(p[1])<<4 | DEC(p[2])>>2;
+ tbuffer[tpos++] = DEC(p[2])<<6 | DEC(p[3]);
+ }
+ else {
+ if (n >= 1) {
+ tbuffer[tpos++] =
+ DEC(p[0])<<2 | DEC(p[1])>>4;
+ }
+ if (n >= 2) {
+ tbuffer[tpos++] =
+ DEC(p[1])<<4 | DEC(p[2])>>2;
+ }
+ if (n >= 3) {
+ tbuffer[tpos++] =
+ DEC(p[2])<<6 | DEC(p[3]);
+ }
+ }
+ if (tpos == 0)
+ continue;
+ if (tpos + pos > len) {
+ tpos = len - pos;
+ /*
+ * Arrange return value > len to indicate
+ * overflow.
+ */
+ pos++;
+ }
+ bcopy(tbuffer, bp, tpos);
+ pos += tpos;
+ bp += tpos;
+ if (pos > len)
+ return(pos);
+ }
+ }
+ if (!fgets(temp, sizeof(temp), fd) || strcmp(temp, "end\n"))
+ return(0);
+ return(pos);
+}
diff --git a/usr.sbin/vidcontrol/decode.h b/usr.sbin/vidcontrol/decode.h
new file mode 100644
index 0000000..1f1cba4
--- /dev/null
+++ b/usr.sbin/vidcontrol/decode.h
@@ -0,0 +1,3 @@
+/* $FreeBSD$ */
+
+int decode(FILE *fd, char *buffer, int len);
diff --git a/usr.sbin/vidcontrol/path.h b/usr.sbin/vidcontrol/path.h
new file mode 100644
index 0000000..709acbc
--- /dev/null
+++ b/usr.sbin/vidcontrol/path.h
@@ -0,0 +1,4 @@
+#define KEYMAP_PATH "/usr/share/syscons/keymaps/"
+#define FONT_PATH "/usr/share/syscons/fonts/"
+#define SCRNMAP_PATH "/usr/share/syscons/scrnmaps/"
+
diff --git a/usr.sbin/vidcontrol/vidcontrol.1 b/usr.sbin/vidcontrol/vidcontrol.1
new file mode 100644
index 0000000..2abaa1e
--- /dev/null
+++ b/usr.sbin/vidcontrol/vidcontrol.1
@@ -0,0 +1,540 @@
+.\"
+.\" vidcontrol - a utility for manipulating the syscons video driver
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" @(#)vidcontrol.1
+.\" $FreeBSD$
+.\"
+.Dd December 23, 2006
+.Dt VIDCONTROL 1
+.Os
+.Sh NAME
+.Nm vidcontrol
+.Nd system console control and configuration utility
+.Sh SYNOPSIS
+.Nm
+.Op Fl CdLHPpx
+.Op Fl b Ar color
+.Op Fl c Ar appearance
+.Oo
+.Fl f
+.Op Ar size
+.Ar file
+.Oc
+.Op Fl g Ar geometry
+.Op Fl h Ar size
+.Op Fl i Cm adapter | mode
+.Op Fl l Ar screen_map
+.Op Fl M Ar char
+.Op Fl m Cm on | off
+.Op Fl r Ar foreground Ar background
+.Op Fl S Cm on | off
+.Op Fl s Ar number
+.Op Fl t Ar N | Cm off
+.Op Ar mode
+.Op Ar foreground Op Ar background
+.Op Cm show
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to set various options for the
+.Xr syscons 4
+console driver,
+such as video mode, colors, cursor shape, screen output map, font and screen
+saver timeout.
+.Pp
+The following command line options are supported:
+.Bl -tag -width indent
+.It Ar mode
+Select a new video mode.
+The modes currently recognized are:
+.Ar 80x25 ,
+.Ar 80x30 ,
+.Ar 80x43 ,
+.Ar 80x50 ,
+.Ar 80x60 ,
+.Ar 132x25 ,
+.Ar 132x30 ,
+.Ar 132x43 ,
+.Ar 132x50 ,
+.Ar 132x60 ,
+.Ar VGA_40x25 ,
+.Ar VGA_80x25 ,
+.Ar VGA_80x30 ,
+.Ar VGA_80x50 ,
+.Ar VGA_80x60 ,
+.Ar VGA_90x25 ,
+.Ar VGA_90x30 ,
+.Ar VGA_90x43 ,
+.Ar VGA_90x50 ,
+.Ar VGA_90x60 ,
+.Ar EGA_80x25 ,
+.Ar EGA_80x43 ,
+.Ar VESA_132x25 ,
+.Ar VESA_132x43 ,
+.Ar VESA_132x50 ,
+.Ar VESA_132x60 .
+.\"The graphic mode
+.\".Ar VGA_320x200
+.\"and
+The raster text mode
+.Ar VESA_800x600
+can also be chosen.
+Alternatively, a mode can be specified with its number by using a mode name of
+the form
+.Li MODE_ Ns Aq Ar NUMBER .
+A list of valid mode numbers can be obtained with the
+.Fl i Cm mode
+option.
+See
+.Sx Video Mode Support
+below.
+.It Ar foreground Op Ar background
+Change colors when displaying text.
+Specify the foreground color
+(e.g.\&
+.Dq vidcontrol white ) ,
+or both a foreground and background colors
+(e.g.\&
+.Dq vidcontrol yellow blue ) .
+Use the
+.Cm show
+command below to see available colors.
+.It Cm show
+See the supported colors on a given platform.
+.It Fl b Ar color
+Set border color to
+.Ar color .
+This option may not be always supported by the video driver.
+.It Fl C
+Clear the history buffer.
+.It Fl c Cm normal | blink | destructive
+Change the cursor appearance.
+The cursor is either an inverting block
+.Pq Cm normal
+that can optionally
+.Cm blink ,
+or it can be like the old hardware cursor
+.Pq Cm destructive .
+The latter is actually a simulation.
+.It Fl d
+Print out current output screen map.
+.It Xo
+.Fl f
+.Op Ar size
+.Ar file
+.Xc
+Load font
+.Ar file
+for
+.Ar size
+(currently, only
+.Cm 8x8 ,
+.Cm 8x14
+or
+.Cm 8x16 ) .
+The font file can be either uuencoded or in raw binary format.
+You can also use the menu-driven
+.Xr vidfont 1
+command to load the font of your choice.
+.Pp
+.Ar Size
+may be omitted, in this case
+.Nm
+will try to guess it from the size of font file.
+.Pp
+Note that older video cards, such as MDA and CGA, do not support
+software font.
+See also
+.Sx Video Mode Support
+and
+.Sx EXAMPLES
+below and the man page for
+.Xr syscons 4 .
+.It Fl g Ar geometry
+Set the
+.Ar geometry
+of the text mode for the modes with selectable
+geometry.
+Currently only raster modes, such as
+.Ar VESA_800x600 ,
+support this option.
+See also
+.Sx Video Mode Support
+and
+.Sx EXAMPLES
+below.
+.It Fl h Ar size
+Set the size of the history (scrollback) buffer to
+.Ar size
+lines.
+.It Fl i Cm adapter
+Shows info about the current video adapter.
+.It Fl i Cm mode
+Shows the possible video modes with the current video hardware.
+.It Fl l Ar screen_map
+Install screen output map file from
+.Ar screen_map .
+See also
+.Xr syscons 4 .
+.It Fl L
+Install default screen output map.
+.It Fl M Ar char
+Sets the base character used to render the mouse pointer to
+.Ar char .
+.It Fl m Cm on | off
+Switch the mouse pointer
+.Cm on
+or
+.Cm off .
+Used together with the
+.Xr moused 8
+daemon for text mode cut & paste functionality.
+.It Fl p
+Capture the current contents of the video buffer corresponding
+to the terminal device referred to by standard input.
+The
+.Nm
+utility writes contents of the video buffer to the standard
+output in a raw binary format.
+For details about that
+format see
+.Sx Format of Video Buffer Dump
+below.
+.It Fl P
+Same as
+.Fl p ,
+but dump contents of the video buffer in a plain text format
+ignoring nonprintable characters and information about text
+attributes.
+.It Fl H
+When used with
+.Fl p
+or
+.Fl P ,
+it instructs
+.Nm
+to dump full history buffer instead of visible portion of
+the video buffer only.
+.It Fl r Ar foreground background
+Change reverse mode colors to
+.Ar foreground
+and
+.Ar background .
+.It Fl S Cm on | off
+Turn vty switching on or off.
+When vty switching is off,
+attempts to switch to a different virtual terminal will fail.
+(The default is to permit vty switching.)
+This protection can be easily bypassed when the kernel is compiled with
+the
+.Dv DDB
+option.
+However, you probably should not compile the kernel debugger on a box which
+is supposed to be physically secure.
+.It Fl s Ar number
+Set the current vty to
+.Ar number .
+.It Fl t Ar N | Cm off
+Set the screensaver timeout to
+.Ar N
+seconds, or turns it
+.Cm off .
+.It Fl x
+Use hexadecimal digits for output.
+.El
+.Ss Video Mode Support
+Note that not all modes listed above may be supported by the video
+hardware.
+You can verify which mode is supported by the video hardware, using the
+.Fl i Cm mode
+option.
+.Pp
+The VESA BIOS support must be linked to the kernel
+or loaded as a KLD module if you wish to use VESA video modes
+or 132 column modes
+(see
+.Xr vga 4 ) .
+.Pp
+You need to compile your kernel with the
+.Ar VGA_WIDTH90
+option if you wish to use VGA 90 column modes
+(see
+.Xr vga 4 ) .
+.Pp
+Video modes other than 25 and 30 line modes may require specific size of font.
+Use
+.Fl f
+option above to load a font file to the kernel.
+If the required size of font has not been loaded to the kernel,
+.Nm
+will fail if the user attempts to set a new video mode.
+.Pp
+.Bl -column "25 line modes" "8x16 (VGA), 8x14 (EGA)" -compact
+.Sy Modes Ta Sy Font size
+.No 25 line modes Ta 8x16 (VGA), 8x14 (EGA)
+.No 30 line modes Ta 8x16
+.No 43 line modes Ta 8x8
+.No 50 line modes Ta 8x8
+.No 60 line modes Ta 8x8
+.El
+.Pp
+It is better to always load all three sizes (8x8, 8x14 and 8x16)
+of the same font.
+.Pp
+You may set variables in
+.Pa /etc/rc.conf
+or
+.Pa /etc/rc.conf.local
+so that desired font files will be automatically loaded
+when the system starts up.
+See below.
+.Pp
+If you want to use any of the raster text modes you need to recompile your
+kernel with the
+.Dv SC_PIXEL_MODE
+option.
+See
+.Xr syscons 4
+for more details on this kernel option.
+.Ss Format of Video Buffer Dump
+The
+.Nm
+utility uses the
+.Xr syscons 4
+.Dv CONS_SCRSHOT
+.Xr ioctl 2
+to capture the current contents of the video buffer.
+The
+.Nm
+utility writes version and additional information to the standard
+output, followed by the contents of the video buffer.
+.Pp
+VGA video memory is typically arranged in two byte tuples,
+one per character position.
+In each tuple, the first byte will be the character code,
+and the second byte is the character's color attribute.
+.Pp
+The VGA color attribute byte looks like this:
+.Pp
+.Bl -column "X:X" "<00000000>" "width" "bright foreground color"
+.Sy "bits# width meaning"
+.Li "7 <X0000000> 1 character blinking"
+.Li "6:4 <0XXX0000> 3 background color"
+.Li "3 <0000X000> 1 bright foreground color"
+.Li "2:0 <00000XXX> 3 foreground color"
+.El
+.Pp
+Here is a list of the three bit wide base colors:
+.Pp
+.Bl -hang -offset indent -compact
+.It 0
+Black
+.It 1
+Blue
+.It 2
+Green
+.It 3
+Cyan
+.It 4
+Red
+.It 5
+Magenta
+.It 6
+Brown
+.It 7
+Light Grey
+.El
+.Pp
+Base colors with bit 3 (the bright foreground flag) set:
+.Pp
+.Bl -hang -offset indent -compact
+.It 0
+Dark Grey
+.It 1
+Light Blue
+.It 2
+Light Green
+.It 3
+Light Cyan
+.It 4
+Light Red
+.It 5
+Light Magenta
+.It 6
+Yellow
+.It 7
+White
+.El
+.Pp
+For example, the two bytes
+.Pp
+.Dl "65 158"
+.Pp
+specify an uppercase A (character code 65), blinking
+(bit 7 set) in yellow (bits 3:0) on a blue background
+(bits 6:4).
+.Pp
+The
+.Nm
+output contains a small header which includes additional
+information which may be useful to utilities processing
+the output.
+.Pp
+The first 10 bytes are always arranged as follows:
+.Bl -column "Byte range" "Contents" -offset indent
+.It Sy "Byte Range Contents"
+.It "1 thru 8 Literal text" Dq Li SCRSHOT_
+.It "9 File format version number"
+.It "10 Remaining number of bytes in the header"
+.El
+.Pp
+Subsequent bytes depend on the version number.
+.Bl -column "Version" "13 and up" -offset indent
+.It Sy "Version Byte Meaning"
+.It "1 11 Terminal width, in characters"
+.It " 12 Terminal depth, in characters"
+.It " 13 and up The snapshot data"
+.El
+.Pp
+So a dump of an 80x25 screen would start (in hex)
+.Bd -literal -offset indent
+53 43 52 53 48 4f 54 5f 01 02 50 19
+----------------------- -- -- -- --
+ | | | | ` 25 decimal
+ | | | `--- 80 decimal
+ | | `------ 2 remaining bytes of header data
+ | `--------- File format version 1
+ `------------------------ Literal "SCRSHOT_"
+.Ed
+.Sh VIDEO OUTPUT CONFIGURATION
+.Ss Boot Time Configuration
+You may set the following variables in
+.Pa /etc/rc.conf
+or
+.Pa /etc/rc.conf.local
+in order to configure the video output at boot time.
+.Pp
+.Bl -tag -width foo_bar_var -compact
+.It Ar blanktime
+Sets the timeout value for the
+.Fl t
+option.
+.It Ar font8x16 , font8x14 , font8x8
+Specifies font files for the
+.Fl f
+option.
+.It Ar scrnmap
+Specifies a screen output map file for the
+.Fl l
+option.
+.El
+.Pp
+See
+.Xr rc.conf 5
+for more details.
+.Ss Driver Configuration
+The video card driver may let you change default configuration
+options, such as the default font, so that you do not need to set up
+the options at boot time.
+See video card driver manuals, (e.g.\&
+.Xr vga 4 )
+for details.
+.Sh FILES
+.Bl -tag -width /usr/share/syscons/scrnmaps/foo-bar -compact
+.It Pa /usr/share/syscons/fonts/*
+font files.
+.It Pa /usr/share/syscons/scrnmaps/*
+screen output map files.
+.El
+.Sh EXAMPLES
+If you want to load
+.Pa /usr/share/syscons/fonts/iso-8x16.fnt
+to the kernel, run
+.Nm
+as:
+.Pp
+.Dl vidcontrol -f 8x16 /usr/share/syscons/fonts/iso-8x16.fnt
+.Pp
+So long as the font file is in
+.Pa /usr/share/syscons/fonts ,
+you may abbreviate the file name as
+.Pa iso-8x16 :
+.Pp
+.Dl vidcontrol -f 8x16 iso-8x16
+.Pp
+Furthermore, you can also omit font size
+.Dq Li 8x16 :
+.Pp
+.Dl vidcontrol -f iso-8x16
+.Pp
+Moreover, the suffix specifying the font size can be also omitted; in
+this case,
+.Nm
+will use the size of the currently displayed font to construct the
+suffix:
+.Pp
+.Dl vidcontrol -f iso
+.Pp
+Likewise, you can also abbreviate the screen output map file name for
+the
+.Fl l
+option if the file is found in
+.Pa /usr/share/syscons/scrnmaps .
+.Pp
+.Dl vidcontrol -l iso-8859-1_to_cp437
+.Pp
+The above command will load
+.Pa /usr/share/syscons/scrnmaps/iso-8859-1_to_cp437.scm .
+.Pp
+The following command will set-up a 100x37 raster text mode (useful for
+some LCD models):
+.Pp
+.Dl vidcontrol -g 100x37 VESA_800x600
+.Pp
+The following command will capture the contents of the first virtual
+terminal video buffer, and redirect the output to the
+.Pa shot.scr
+file:
+.Pp
+.Dl vidcontrol -p < /dev/ttyv0 > shot.scr
+.Pp
+The following command will dump contents of the fourth virtual terminal
+video buffer
+to the standard output in the human readable format:
+.Pp
+.Dl vidcontrol -P < /dev/ttyv3
+.Sh SEE ALSO
+.Xr kbdcontrol 1 ,
+.Xr vidfont 1 ,
+.Xr keyboard 4 ,
+.Xr screen 4 ,
+.Xr syscons 4 ,
+.Xr vga 4 ,
+.Xr rc.conf 5 ,
+.Xr kldload 8 ,
+.Xr moused 8 ,
+.Xr watch 8
+.Pp
+The various
+.Pa scr2*
+utilities in the
+.Pa graphics
+and
+.Pa textproc
+categories of the
+.Em "Ports Collection" .
+.Sh AUTHORS
+.An S\(/oren Schmidt Aq sos@FreeBSD.org
+.An Sascha Wildner
+.Sh CONTRIBUTORS
+.An Maxim Sobolev Aq sobomax@FreeBSD.org ,
+.An Nik Clayton Aq nik@FreeBSD.org
diff --git a/usr.sbin/vidcontrol/vidcontrol.c b/usr.sbin/vidcontrol/vidcontrol.c
new file mode 100644
index 0000000..847ff3e
--- /dev/null
+++ b/usr.sbin/vidcontrol/vidcontrol.c
@@ -0,0 +1,1288 @@
+/*-
+ * Copyright (c) 1994-1996 Søren Schmidt
+ * All rights reserved.
+ *
+ * Portions of this software are based in part on the work of
+ * Sascha Wildner <saw@online.de> contributed to The DragonFly 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,
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must 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.
+ *
+ * $DragonFly: src/usr.sbin/vidcontrol/vidcontrol.c,v 1.10 2005/03/02 06:08:29 joerg Exp $
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/fbio.h>
+#include <sys/consio.h>
+#include <sys/errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "path.h"
+#include "decode.h"
+
+
+#define DATASIZE(x) ((x).w * (x).h * 256 / 8)
+
+/* Screen dump modes */
+#define DUMP_FMT_RAW 1
+#define DUMP_FMT_TXT 2
+/* Screen dump options */
+#define DUMP_FBF 0
+#define DUMP_ALL 1
+/* Screen dump file format revision */
+#define DUMP_FMT_REV 1
+
+char legal_colors[16][16] = {
+ "black", "blue", "green", "cyan",
+ "red", "magenta", "brown", "white",
+ "grey", "lightblue", "lightgreen", "lightcyan",
+ "lightred", "lightmagenta", "yellow", "lightwhite"
+};
+
+struct {
+ int active_vty;
+ vid_info_t console_info;
+ unsigned char screen_map[256];
+ int video_mode_number;
+ struct video_info video_mode_info;
+} cur_info;
+
+int hex = 0;
+int number;
+int vesa_cols;
+int vesa_rows;
+int font_height;
+int colors_changed;
+int video_mode_changed;
+int normal_fore_color, normal_back_color;
+int revers_fore_color, revers_back_color;
+char letter;
+struct vid_info info;
+struct video_info new_mode_info;
+
+
+/*
+ * Initialize revert data.
+ *
+ * NOTE: the following parameters are not yet saved/restored:
+ *
+ * screen saver timeout
+ * cursor type
+ * mouse character and mouse show/hide state
+ * vty switching on/off state
+ * history buffer size
+ * history contents
+ * font maps
+ */
+
+static void
+init(void)
+{
+ if (ioctl(0, VT_GETACTIVE, &cur_info.active_vty) == -1)
+ errc(1, errno, "getting active vty");
+
+ cur_info.console_info.size = sizeof(cur_info.console_info);
+
+ if (ioctl(0, CONS_GETINFO, &cur_info.console_info) == -1)
+ errc(1, errno, "getting console information");
+
+ if (ioctl(0, GIO_SCRNMAP, &cur_info.screen_map) == -1)
+ errc(1, errno, "getting screen map");
+
+ if (ioctl(0, CONS_GET, &cur_info.video_mode_number) == -1)
+ errc(1, errno, "getting video mode number");
+
+ cur_info.video_mode_info.vi_mode = cur_info.video_mode_number;
+
+ if (ioctl(0, CONS_MODEINFO, &cur_info.video_mode_info) == -1)
+ errc(1, errno, "getting video mode parameters");
+
+ normal_fore_color = cur_info.console_info.mv_norm.fore;
+ normal_back_color = cur_info.console_info.mv_norm.back;
+ revers_fore_color = cur_info.console_info.mv_rev.fore;
+ revers_back_color = cur_info.console_info.mv_rev.back;
+}
+
+
+/*
+ * If something goes wrong along the way we call revert() to go back to the
+ * console state we came from (which is assumed to be working).
+ *
+ * NOTE: please also read the comments of init().
+ */
+
+static void
+revert(void)
+{
+ int size[3];
+
+ ioctl(0, VT_ACTIVATE, cur_info.active_vty);
+
+ fprintf(stderr, "\033[=%dA", cur_info.console_info.mv_ovscan);
+ fprintf(stderr, "\033[=%dF", cur_info.console_info.mv_norm.fore);
+ fprintf(stderr, "\033[=%dG", cur_info.console_info.mv_norm.back);
+ fprintf(stderr, "\033[=%dH", cur_info.console_info.mv_rev.fore);
+ fprintf(stderr, "\033[=%dI", cur_info.console_info.mv_rev.back);
+
+ ioctl(0, PIO_SCRNMAP, &cur_info.screen_map);
+
+ if (cur_info.video_mode_number >= M_VESA_BASE)
+ ioctl(0, _IO('V', cur_info.video_mode_number - M_VESA_BASE),
+ NULL);
+ else
+ ioctl(0, _IO('S', cur_info.video_mode_number), NULL);
+
+ if (cur_info.video_mode_info.vi_flags & V_INFO_GRAPHICS) {
+ size[0] = cur_info.video_mode_info.vi_width / 8;
+ size[1] = cur_info.video_mode_info.vi_height /
+ cur_info.console_info.font_size;
+ size[2] = cur_info.console_info.font_size;
+
+ ioctl(0, KDRASTER, size);
+ }
+}
+
+
+/*
+ * Print a short usage string describing all options, then exit.
+ */
+
+static void
+usage(void)
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
+"usage: vidcontrol [-CdHLPpx] [-b color] [-c appearance] [-f [size] file]",
+" [-g geometry] [-h size] [-i adapter | mode] [-l screen_map]",
+" [-M char] [-m on | off] [-r foreground background]",
+" [-S on | off] [-s number] [-t N | off] [mode]",
+" [foreground [background]] [show]");
+ exit(1);
+}
+
+
+/*
+ * Retrieve the next argument from the command line (for options that require
+ * more than one argument).
+ */
+
+static char *
+nextarg(int ac, char **av, int *indp, int oc, int strict)
+{
+ if (*indp < ac)
+ return(av[(*indp)++]);
+
+ if (strict != 0) {
+ revert();
+ errx(1, "option requires two arguments -- %c", oc);
+ }
+
+ return(NULL);
+}
+
+
+/*
+ * Guess which file to open. Try to open each combination of a specified set
+ * of file name components.
+ */
+
+static FILE *
+openguess(const char *a[], const char *b[], const char *c[], const char *d[], char **name)
+{
+ FILE *f;
+ int i, j, k, l;
+
+ for (i = 0; a[i] != NULL; i++) {
+ for (j = 0; b[j] != NULL; j++) {
+ for (k = 0; c[k] != NULL; k++) {
+ for (l = 0; d[l] != NULL; l++) {
+ asprintf(name, "%s%s%s%s",
+ a[i], b[j], c[k], d[l]);
+
+ f = fopen(*name, "r");
+
+ if (f != NULL)
+ return (f);
+
+ free(*name);
+ }
+ }
+ }
+ }
+ return (NULL);
+}
+
+
+/*
+ * Load a screenmap from a file and set it.
+ */
+
+static void
+load_scrnmap(const char *filename)
+{
+ FILE *fd;
+ int size;
+ char *name;
+ scrmap_t scrnmap;
+ const char *a[] = {"", SCRNMAP_PATH, NULL};
+ const char *b[] = {filename, NULL};
+ const char *c[] = {"", ".scm", NULL};
+ const char *d[] = {"", NULL};
+
+ fd = openguess(a, b, c, d, &name);
+
+ if (fd == NULL) {
+ revert();
+ errx(1, "screenmap file not found");
+ }
+
+ size = sizeof(scrnmap);
+
+ if (decode(fd, (char *)&scrnmap, size) != size) {
+ rewind(fd);
+
+ if (fread(&scrnmap, 1, size, fd) != (size_t)size) {
+ warnx("bad screenmap file");
+ fclose(fd);
+ revert();
+ errx(1, "bad screenmap file");
+ }
+ }
+
+ if (ioctl(0, PIO_SCRNMAP, &scrnmap) == -1) {
+ revert();
+ errc(1, errno, "loading screenmap");
+ }
+
+ fclose(fd);
+}
+
+
+/*
+ * Set the default screenmap.
+ */
+
+static void
+load_default_scrnmap(void)
+{
+ scrmap_t scrnmap;
+ int i;
+
+ for (i=0; i<256; i++)
+ *((char*)&scrnmap + i) = i;
+
+ if (ioctl(0, PIO_SCRNMAP, &scrnmap) == -1) {
+ revert();
+ errc(1, errno, "loading default screenmap");
+ }
+}
+
+
+/*
+ * Print the current screenmap to stdout.
+ */
+
+static void
+print_scrnmap(void)
+{
+ unsigned char map[256];
+ size_t i;
+
+ if (ioctl(0, GIO_SCRNMAP, &map) == -1) {
+ revert();
+ errc(1, errno, "getting screenmap");
+ }
+ for (i=0; i<sizeof(map); i++) {
+ if (i != 0 && i % 16 == 0)
+ fprintf(stdout, "\n");
+
+ if (hex != 0)
+ fprintf(stdout, " %02x", map[i]);
+ else
+ fprintf(stdout, " %03d", map[i]);
+ }
+ fprintf(stdout, "\n");
+
+}
+
+
+/*
+ * Determine a file's size.
+ */
+
+static int
+fsize(FILE *file)
+{
+ struct stat sb;
+
+ if (fstat(fileno(file), &sb) == 0)
+ return sb.st_size;
+ else
+ return -1;
+}
+
+
+/*
+ * Load a font from file and set it.
+ */
+
+static void
+load_font(const char *type, const char *filename)
+{
+ FILE *fd;
+ int h, i, size, w;
+ unsigned long io = 0; /* silence stupid gcc(1) in the Wall mode */
+ char *name, *fontmap, size_sufx[6];
+ const char *a[] = {"", FONT_PATH, NULL};
+ const char *b[] = {filename, NULL};
+ const char *c[] = {"", size_sufx, NULL};
+ const char *d[] = {"", ".fnt", NULL};
+ vid_info_t _info;
+
+ struct sizeinfo {
+ int w;
+ int h;
+ unsigned long io;
+ } sizes[] = {{8, 16, PIO_FONT8x16},
+ {8, 14, PIO_FONT8x14},
+ {8, 8, PIO_FONT8x8},
+ {0, 0, 0}};
+
+ _info.size = sizeof(_info);
+ if (ioctl(0, CONS_GETINFO, &_info) == -1) {
+ revert();
+ warn("failed to obtain current video mode parameters");
+ return;
+ }
+
+ snprintf(size_sufx, sizeof(size_sufx), "-8x%d", _info.font_size);
+ fd = openguess(a, b, c, d, &name);
+
+ if (fd == NULL) {
+ revert();
+ errx(1, "%s: can't load font file", filename);
+ }
+
+ if (type != NULL) {
+ size = 0;
+ if (sscanf(type, "%dx%d", &w, &h) == 2) {
+ for (i = 0; sizes[i].w != 0; i++) {
+ if (sizes[i].w == w && sizes[i].h == h) {
+ size = DATASIZE(sizes[i]);
+ io = sizes[i].io;
+ font_height = sizes[i].h;
+ }
+ }
+ }
+ if (size == 0) {
+ fclose(fd);
+ revert();
+ errx(1, "%s: bad font size specification", type);
+ }
+ } else {
+ /* Apply heuristics */
+
+ int j;
+ int dsize[2];
+
+ size = DATASIZE(sizes[0]);
+ fontmap = (char*) malloc(size);
+ dsize[0] = decode(fd, fontmap, size);
+ dsize[1] = fsize(fd);
+ free(fontmap);
+
+ size = 0;
+ for (j = 0; j < 2; j++) {
+ for (i = 0; sizes[i].w != 0; i++) {
+ if (DATASIZE(sizes[i]) == dsize[j]) {
+ size = dsize[j];
+ io = sizes[i].io;
+ font_height = sizes[i].h;
+ j = 2; /* XXX */
+ break;
+ }
+ }
+ }
+
+ if (size == 0) {
+ fclose(fd);
+ revert();
+ errx(1, "%s: can't guess font size", filename);
+ }
+
+ rewind(fd);
+ }
+
+ fontmap = (char*) malloc(size);
+
+ if (decode(fd, fontmap, size) != size) {
+ rewind(fd);
+ if (fsize(fd) != size ||
+ fread(fontmap, 1, size, fd) != (size_t)size) {
+ warnx("%s: bad font file", filename);
+ fclose(fd);
+ free(fontmap);
+ revert();
+ errx(1, "%s: bad font file", filename);
+ }
+ }
+
+ if (ioctl(0, io, fontmap) == -1) {
+ revert();
+ errc(1, errno, "loading font");
+ }
+
+ fclose(fd);
+ free(fontmap);
+}
+
+
+/*
+ * Set the timeout for the screensaver.
+ */
+
+static void
+set_screensaver_timeout(char *arg)
+{
+ int nsec;
+
+ if (!strcmp(arg, "off")) {
+ nsec = 0;
+ } else {
+ nsec = atoi(arg);
+
+ if ((*arg == '\0') || (nsec < 1)) {
+ revert();
+ errx(1, "argument must be a positive number");
+ }
+ }
+
+ if (ioctl(0, CONS_BLANKTIME, &nsec) == -1) {
+ revert();
+ errc(1, errno, "setting screensaver period");
+ }
+}
+
+
+/*
+ * Set the cursor's shape/type.
+ */
+
+static void
+set_cursor_type(char *appearence)
+{
+ int type;
+
+ if (!strcmp(appearence, "normal"))
+ type = 0;
+ else if (!strcmp(appearence, "blink"))
+ type = 1;
+ else if (!strcmp(appearence, "destructive"))
+ type = 3;
+ else {
+ revert();
+ errx(1, "argument to -c must be normal, blink or destructive");
+ }
+
+ if (ioctl(0, CONS_CURSORTYPE, &type) == -1) {
+ revert();
+ errc(1, errno, "setting cursor type");
+ }
+}
+
+
+/*
+ * Set the video mode.
+ */
+
+static int
+video_mode(int argc, char **argv, int *mode_index)
+{
+ static struct {
+ const char *name;
+ unsigned long mode;
+ unsigned long mode_num;
+ } modes[] = {
+ { "80x25", SW_TEXT_80x25, M_TEXT_80x25 },
+ { "80x30", SW_TEXT_80x30, M_TEXT_80x30 },
+ { "80x43", SW_TEXT_80x43, M_TEXT_80x43 },
+ { "80x50", SW_TEXT_80x50, M_TEXT_80x50 },
+ { "80x60", SW_TEXT_80x60, M_TEXT_80x60 },
+ { "132x25", SW_TEXT_132x25, M_TEXT_132x25 },
+ { "132x30", SW_TEXT_132x30, M_TEXT_132x30 },
+ { "132x43", SW_TEXT_132x43, M_TEXT_132x43 },
+ { "132x50", SW_TEXT_132x50, M_TEXT_132x50 },
+ { "132x60", SW_TEXT_132x60, M_TEXT_132x60 },
+ { "VGA_40x25", SW_VGA_C40x25, M_VGA_C40x25 },
+ { "VGA_80x25", SW_VGA_C80x25, M_VGA_C80x25 },
+ { "VGA_80x30", SW_VGA_C80x30, M_VGA_C80x30 },
+ { "VGA_80x50", SW_VGA_C80x50, M_VGA_C80x50 },
+ { "VGA_80x60", SW_VGA_C80x60, M_VGA_C80x60 },
+#ifdef SW_VGA_C90x25
+ { "VGA_90x25", SW_VGA_C90x25, M_VGA_C90x25 },
+ { "VGA_90x30", SW_VGA_C90x30, M_VGA_C90x30 },
+ { "VGA_90x43", SW_VGA_C90x43, M_VGA_C90x43 },
+ { "VGA_90x50", SW_VGA_C90x50, M_VGA_C90x50 },
+ { "VGA_90x60", SW_VGA_C90x60, M_VGA_C90x60 },
+#endif
+ { "VGA_320x200", SW_VGA_CG320, M_CG320 },
+ { "EGA_80x25", SW_ENH_C80x25, M_ENH_C80x25 },
+ { "EGA_80x43", SW_ENH_C80x43, M_ENH_C80x43 },
+ { "VESA_132x25", SW_VESA_C132x25,M_VESA_C132x25 },
+ { "VESA_132x43", SW_VESA_C132x43,M_VESA_C132x43 },
+ { "VESA_132x50", SW_VESA_C132x50,M_VESA_C132x50 },
+ { "VESA_132x60", SW_VESA_C132x60,M_VESA_C132x60 },
+ { "VESA_800x600", SW_VESA_800x600,M_VESA_800x600 },
+ { NULL, 0, 0 },
+ };
+
+ int new_mode_num = 0;
+ unsigned long mode = 0;
+ int cur_mode;
+ int ioerr;
+ int size[3];
+ int i;
+
+ if (ioctl(0, CONS_GET, &cur_mode) < 0)
+ err(1, "cannot get the current video mode");
+
+ /*
+ * Parse the video mode argument...
+ */
+
+ if (*mode_index < argc) {
+ if (!strncmp(argv[*mode_index], "MODE_", 5)) {
+ if (!isdigit(argv[*mode_index][5]))
+ errx(1, "invalid video mode number");
+
+ new_mode_num = atoi(&argv[*mode_index][5]);
+ } else {
+ for (i = 0; modes[i].name != NULL; ++i) {
+ if (!strcmp(argv[*mode_index], modes[i].name)) {
+ mode = modes[i].mode;
+ new_mode_num = modes[i].mode_num;
+ break;
+ }
+ }
+
+ if (modes[i].name == NULL)
+ return EXIT_FAILURE;
+ if (ioctl(0, mode, NULL) < 0) {
+ warn("cannot set videomode");
+ return EXIT_FAILURE;
+ }
+ }
+
+ /*
+ * Collect enough information about the new video mode...
+ */
+
+ new_mode_info.vi_mode = new_mode_num;
+
+ if (ioctl(0, CONS_MODEINFO, &new_mode_info) == -1) {
+ revert();
+ errc(1, errno, "obtaining new video mode parameters");
+ }
+
+ if (mode == 0) {
+ if (new_mode_num >= M_VESA_BASE)
+ mode = _IO('V', new_mode_num - M_VESA_BASE);
+ else
+ mode = _IO('S', new_mode_num);
+ }
+
+ /*
+ * Try setting the new mode.
+ */
+
+ if (ioctl(0, mode, NULL) == -1) {
+ revert();
+ errc(1, errno, "setting video mode");
+ }
+
+ /*
+ * For raster modes it's not enough to just set the mode.
+ * We also need to explicitly set the raster mode.
+ */
+
+ if (new_mode_info.vi_flags & V_INFO_GRAPHICS) {
+ /* font size */
+
+ if (font_height == 0)
+ font_height = cur_info.console_info.font_size;
+
+ size[2] = font_height;
+
+ /* adjust columns */
+
+ if ((vesa_cols * 8 > new_mode_info.vi_width) ||
+ (vesa_cols <= 0)) {
+ size[0] = new_mode_info.vi_width / 8;
+ } else {
+ size[0] = vesa_cols;
+ }
+
+ /* adjust rows */
+
+ if ((vesa_rows * font_height > new_mode_info.vi_height) ||
+ (vesa_rows <= 0)) {
+ size[1] = new_mode_info.vi_height /
+ font_height;
+ } else {
+ size[1] = vesa_rows;
+ }
+
+ /* set raster mode */
+
+ if (ioctl(0, KDRASTER, size)) {
+ ioerr = errno;
+ if (cur_mode >= M_VESA_BASE)
+ ioctl(0,
+ _IO('V', cur_mode - M_VESA_BASE),
+ NULL);
+ else
+ ioctl(0, _IO('S', cur_mode), NULL);
+ revert();
+ warnc(ioerr, "cannot activate raster display");
+ return EXIT_FAILURE;
+ }
+ }
+
+ video_mode_changed = 1;
+
+ (*mode_index)++;
+ }
+ return EXIT_SUCCESS;
+}
+
+
+/*
+ * Return the number for a specified color name.
+ */
+
+static int
+get_color_number(char *color)
+{
+ int i;
+
+ for (i=0; i<16; i++) {
+ if (!strcmp(color, legal_colors[i]))
+ return i;
+ }
+ return -1;
+}
+
+
+/*
+ * Get normal text and background colors.
+ */
+
+static void
+get_normal_colors(int argc, char **argv, int *_index)
+{
+ int color;
+
+ if (*_index < argc && (color = get_color_number(argv[*_index])) != -1) {
+ (*_index)++;
+ fprintf(stderr, "\033[=%dF", color);
+ normal_fore_color=color;
+ colors_changed = 1;
+ if (*_index < argc
+ && (color = get_color_number(argv[*_index])) != -1
+ && color < 8) {
+ (*_index)++;
+ fprintf(stderr, "\033[=%dG", color);
+ normal_back_color=color;
+ }
+ }
+}
+
+
+/*
+ * Get reverse text and background colors.
+ */
+
+static void
+get_reverse_colors(int argc, char **argv, int *_index)
+{
+ int color;
+
+ if ((color = get_color_number(argv[*(_index)-1])) != -1) {
+ fprintf(stderr, "\033[=%dH", color);
+ revers_fore_color=color;
+ colors_changed = 1;
+ if (*_index < argc
+ && (color = get_color_number(argv[*_index])) != -1
+ && color < 8) {
+ (*_index)++;
+ fprintf(stderr, "\033[=%dI", color);
+ revers_back_color=color;
+ }
+ }
+}
+
+
+/*
+ * Set normal and reverse foreground and background colors.
+ */
+
+static void
+set_colors(void)
+{
+ fprintf(stderr, "\033[=%dF", normal_fore_color);
+ fprintf(stderr, "\033[=%dG", normal_back_color);
+ fprintf(stderr, "\033[=%dH", revers_fore_color);
+ fprintf(stderr, "\033[=%dI", revers_back_color);
+}
+
+
+/*
+ * Switch to virtual terminal #arg.
+ */
+
+static void
+set_console(char *arg)
+{
+ int n;
+
+ if(!arg || strspn(arg,"0123456789") != strlen(arg)) {
+ revert();
+ errx(1, "bad console number");
+ }
+
+ n = atoi(arg);
+
+ if (n < 1 || n > 16) {
+ revert();
+ errx(1, "console number out of range");
+ } else if (ioctl(0, VT_ACTIVATE, n) == -1) {
+ revert();
+ errc(1, errno, "switching vty");
+ }
+}
+
+
+/*
+ * Sets the border color.
+ */
+
+static void
+set_border_color(char *arg)
+{
+ int color;
+
+ if ((color = get_color_number(arg)) != -1) {
+ fprintf(stderr, "\033[=%dA", color);
+ }
+ else
+ usage();
+}
+
+static void
+set_mouse_char(char *arg)
+{
+ struct mouse_info mouse;
+ long l;
+
+ l = strtol(arg, NULL, 0);
+
+ if ((l < 0) || (l > UCHAR_MAX - 3)) {
+ revert();
+ warnx("argument to -M must be 0 through %d", UCHAR_MAX - 3);
+ return;
+ }
+
+ mouse.operation = MOUSE_MOUSECHAR;
+ mouse.u.mouse_char = (int)l;
+
+ if (ioctl(0, CONS_MOUSECTL, &mouse) == -1) {
+ revert();
+ errc(1, errno, "setting mouse character");
+ }
+}
+
+
+/*
+ * Show/hide the mouse.
+ */
+
+static void
+set_mouse(char *arg)
+{
+ struct mouse_info mouse;
+
+ if (!strcmp(arg, "on")) {
+ mouse.operation = MOUSE_SHOW;
+ } else if (!strcmp(arg, "off")) {
+ mouse.operation = MOUSE_HIDE;
+ } else {
+ revert();
+ errx(1, "argument to -m must be either on or off");
+ }
+
+ if (ioctl(0, CONS_MOUSECTL, &mouse) == -1) {
+ revert();
+ errc(1, errno, "%sing the mouse",
+ mouse.operation == MOUSE_SHOW ? "show" : "hid");
+ }
+}
+
+
+static void
+set_lockswitch(char *arg)
+{
+ int data;
+
+ if (!strcmp(arg, "off")) {
+ data = 0x01;
+ } else if (!strcmp(arg, "on")) {
+ data = 0x02;
+ } else {
+ revert();
+ errx(1, "argument to -S must be either on or off");
+ }
+
+ if (ioctl(0, VT_LOCKSWITCH, &data) == -1) {
+ revert();
+ errc(1, errno, "turning %s vty switching",
+ data == 0x01 ? "off" : "on");
+ }
+}
+
+
+/*
+ * Return the adapter name for a specified type.
+ */
+
+static const char
+*adapter_name(int type)
+{
+ static struct {
+ int type;
+ const char *name;
+ } names[] = {
+ { KD_MONO, "MDA" },
+ { KD_HERCULES, "Hercules" },
+ { KD_CGA, "CGA" },
+ { KD_EGA, "EGA" },
+ { KD_VGA, "VGA" },
+ { KD_PC98, "PC-98xx" },
+ { KD_TGA, "TGA" },
+ { -1, "Unknown" },
+ };
+
+ int i;
+
+ for (i = 0; names[i].type != -1; ++i)
+ if (names[i].type == type)
+ break;
+ return names[i].name;
+}
+
+
+/*
+ * Show graphics adapter information.
+ */
+
+static void
+show_adapter_info(void)
+{
+ struct video_adapter_info ad;
+
+ ad.va_index = 0;
+
+ if (ioctl(0, CONS_ADPINFO, &ad) == -1) {
+ revert();
+ errc(1, errno, "obtaining adapter information");
+ }
+
+ printf("fb%d:\n", ad.va_index);
+ printf(" %.*s%d, type:%s%s (%d), flags:0x%x\n",
+ (int)sizeof(ad.va_name), ad.va_name, ad.va_unit,
+ (ad.va_flags & V_ADP_VESA) ? "VESA " : "",
+ adapter_name(ad.va_type), ad.va_type, ad.va_flags);
+ printf(" initial mode:%d, current mode:%d, BIOS mode:%d\n",
+ ad.va_initial_mode, ad.va_mode, ad.va_initial_bios_mode);
+ printf(" frame buffer window:0x%zx, buffer size:0x%zx\n",
+ ad.va_window, ad.va_buffer_size);
+ printf(" window size:0x%zx, origin:0x%x\n",
+ ad.va_window_size, ad.va_window_orig);
+ printf(" display start address (%d, %d), scan line width:%d\n",
+ ad.va_disp_start.x, ad.va_disp_start.y, ad.va_line_width);
+ printf(" reserved:0x%zx\n", ad.va_unused0);
+}
+
+
+/*
+ * Show video mode information.
+ */
+
+static void
+show_mode_info(void)
+{
+ struct video_info _info;
+ char buf[80];
+ int mode;
+ int c;
+
+ printf(" mode# flags type size "
+ "font window linear buffer\n");
+ printf("---------------------------------------"
+ "---------------------------------------\n");
+
+ for (mode = 0; mode < M_VESA_MODE_MAX; ++mode) {
+ _info.vi_mode = mode;
+ if (ioctl(0, CONS_MODEINFO, &_info))
+ continue;
+ if (_info.vi_mode != mode)
+ continue;
+
+ printf("%3d (0x%03x)", mode, mode);
+ printf(" 0x%08x", _info.vi_flags);
+ if (_info.vi_flags & V_INFO_GRAPHICS) {
+ c = 'G';
+
+ snprintf(buf, sizeof(buf), "%dx%dx%d %d",
+ _info.vi_width, _info.vi_height,
+ _info.vi_depth, _info.vi_planes);
+ } else {
+ c = 'T';
+
+ snprintf(buf, sizeof(buf), "%dx%d",
+ _info.vi_width, _info.vi_height);
+ }
+
+ printf(" %c %-15s", c, buf);
+ snprintf(buf, sizeof(buf), "%dx%d",
+ _info.vi_cwidth, _info.vi_cheight);
+ printf(" %-5s", buf);
+ printf(" 0x%05zx %2dk %2dk",
+ _info.vi_window, (int)_info.vi_window_size/1024,
+ (int)_info.vi_window_gran/1024);
+ printf(" 0x%08zx %dk\n",
+ _info.vi_buffer, (int)_info.vi_buffer_size/1024);
+ }
+}
+
+
+static void
+show_info(char *arg)
+{
+ if (!strcmp(arg, "adapter")) {
+ show_adapter_info();
+ } else if (!strcmp(arg, "mode")) {
+ show_mode_info();
+ } else {
+ revert();
+ errx(1, "argument to -i must be either adapter or mode");
+ }
+}
+
+
+static void
+test_frame(void)
+{
+ int i, cur_mode, fore;
+
+ fore = 15;
+
+ if (ioctl(0, CONS_GET, &cur_mode) < 0)
+ err(1, "must be on a virtual console");
+ switch (cur_mode) {
+ case M_PC98_80x25:
+ case M_PC98_80x30:
+ fore = 7;
+ break;
+ }
+
+ fprintf(stdout, "\033[=0G\n\n");
+ for (i=0; i<8; i++) {
+ fprintf(stdout, "\033[=%dF\033[=0G %2d \033[=%dF%-16s"
+ "\033[=%dF\033[=0G %2d \033[=%dF%-16s "
+ "\033[=%dF %2d \033[=%dGBACKGROUND\033[=0G\n",
+ fore, i, i, legal_colors[i],
+ fore, i+8, i+8, legal_colors[i+8],
+ fore, i, i);
+ }
+ fprintf(stdout, "\033[=%dF\033[=%dG\033[=%dH\033[=%dI\n",
+ info.mv_norm.fore, info.mv_norm.back,
+ info.mv_rev.fore, info.mv_rev.back);
+}
+
+
+/*
+ * Snapshot the video memory of that terminal, using the CONS_SCRSHOT
+ * ioctl, and writes the results to stdout either in the special
+ * binary format (see manual page for details), or in the plain
+ * text format.
+ */
+
+static void
+dump_screen(int mode, int opt)
+{
+ scrshot_t shot;
+ vid_info_t _info;
+
+ _info.size = sizeof(_info);
+
+ if (ioctl(0, CONS_GETINFO, &_info) == -1) {
+ revert();
+ errc(1, errno, "obtaining current video mode parameters");
+ return;
+ }
+
+ shot.x = shot.y = 0;
+ shot.xsize = _info.mv_csz;
+ shot.ysize = _info.mv_rsz;
+ if (opt == DUMP_ALL)
+ shot.ysize += _info.mv_hsz;
+
+ shot.buf = alloca(shot.xsize * shot.ysize * sizeof(u_int16_t));
+ if (shot.buf == NULL) {
+ revert();
+ errx(1, "failed to allocate memory for dump");
+ }
+
+ if (ioctl(0, CONS_SCRSHOT, &shot) == -1) {
+ revert();
+ errc(1, errno, "dumping screen");
+ }
+
+ if (mode == DUMP_FMT_RAW) {
+ printf("SCRSHOT_%c%c%c%c", DUMP_FMT_REV, 2,
+ shot.xsize, shot.ysize);
+
+ fflush(stdout);
+
+ write(STDOUT_FILENO, shot.buf,
+ shot.xsize * shot.ysize * sizeof(u_int16_t));
+ } else {
+ char *line;
+ int x, y;
+ u_int16_t ch;
+
+ line = alloca(shot.xsize + 1);
+
+ if (line == NULL) {
+ revert();
+ errx(1, "failed to allocate memory for line buffer");
+ }
+
+ for (y = 0; y < shot.ysize; y++) {
+ for (x = 0; x < shot.xsize; x++) {
+ ch = shot.buf[x + (y * shot.xsize)];
+ ch &= 0xff;
+
+ if (isprint(ch) == 0)
+ ch = ' ';
+
+ line[x] = (char)ch;
+ }
+
+ /* Trim trailing spaces */
+
+ do {
+ line[x--] = '\0';
+ } while (line[x] == ' ' && x != 0);
+
+ puts(line);
+ }
+
+ fflush(stdout);
+ }
+}
+
+
+/*
+ * Set the console history buffer size.
+ */
+
+static void
+set_history(char *opt)
+{
+ int size;
+
+ size = atoi(opt);
+
+ if ((*opt == '\0') || size < 0) {
+ revert();
+ errx(1, "argument must be a positive number");
+ }
+
+ if (ioctl(0, CONS_HISTORY, &size) == -1) {
+ revert();
+ errc(1, errno, "setting history buffer size");
+ }
+}
+
+
+/*
+ * Clear the console history buffer.
+ */
+
+static void
+clear_history(void)
+{
+ if (ioctl(0, CONS_CLRHIST) == -1) {
+ revert();
+ errc(1, errno, "clearing history buffer");
+ }
+}
+
+
+int
+main(int argc, char **argv)
+{
+ char *font, *type;
+ int dumpmod, dumpopt, opt;
+ int reterr;
+
+ init();
+
+ info.size = sizeof(info);
+
+ if (ioctl(0, CONS_GETINFO, &info) == -1)
+ err(1, "must be on a virtual console");
+ dumpmod = 0;
+ dumpopt = DUMP_FBF;
+ while((opt = getopt(argc, argv, "b:Cc:df:g:h:Hi:l:LM:m:pPr:S:s:t:x")) != -1)
+ switch(opt) {
+ case 'b':
+ set_border_color(optarg);
+ break;
+ case 'C':
+ clear_history();
+ break;
+ case 'c':
+ set_cursor_type(optarg);
+ break;
+ case 'd':
+ print_scrnmap();
+ break;
+ case 'f':
+ type = optarg;
+ font = nextarg(argc, argv, &optind, 'f', 0);
+
+ if (font == NULL) {
+ type = NULL;
+ font = optarg;
+ }
+
+ load_font(type, font);
+ break;
+ case 'g':
+ if (sscanf(optarg, "%dx%d",
+ &vesa_cols, &vesa_rows) != 2) {
+ revert();
+ warnx("incorrect geometry: %s", optarg);
+ usage();
+ }
+ break;
+ case 'h':
+ set_history(optarg);
+ break;
+ case 'H':
+ dumpopt = DUMP_ALL;
+ break;
+ case 'i':
+ show_info(optarg);
+ break;
+ case 'l':
+ load_scrnmap(optarg);
+ break;
+ case 'L':
+ load_default_scrnmap();
+ break;
+ case 'M':
+ set_mouse_char(optarg);
+ break;
+ case 'm':
+ set_mouse(optarg);
+ break;
+ case 'p':
+ dumpmod = DUMP_FMT_RAW;
+ break;
+ case 'P':
+ dumpmod = DUMP_FMT_TXT;
+ break;
+ case 'r':
+ get_reverse_colors(argc, argv, &optind);
+ break;
+ case 'S':
+ set_lockswitch(optarg);
+ break;
+ case 's':
+ set_console(optarg);
+ break;
+ case 't':
+ set_screensaver_timeout(optarg);
+ break;
+ case 'x':
+ hex = 1;
+ break;
+ default:
+ usage();
+ }
+
+ if (dumpmod != 0)
+ dump_screen(dumpmod, dumpopt);
+ reterr = video_mode(argc, argv, &optind);
+ get_normal_colors(argc, argv, &optind);
+
+ if (optind < argc && !strcmp(argv[optind], "show")) {
+ test_frame();
+ optind++;
+ }
+
+ video_mode(argc, argv, &optind);
+
+ get_normal_colors(argc, argv, &optind);
+
+ if (colors_changed || video_mode_changed) {
+ if (!(new_mode_info.vi_flags & V_INFO_GRAPHICS)) {
+ if ((normal_back_color < 8) && (revers_back_color < 8)) {
+ set_colors();
+ } else {
+ revert();
+ errx(1, "bg color for text modes must be < 8");
+ }
+ } else {
+ set_colors();
+ }
+ }
+
+ if ((optind != argc) || (argc == 1))
+ usage();
+ return reterr;
+}
+
diff --git a/usr.sbin/vipw/Makefile b/usr.sbin/vipw/Makefile
new file mode 100644
index 0000000..69a9df9
--- /dev/null
+++ b/usr.sbin/vipw/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $FreeBSD$
+
+PROG= vipw
+MAN= vipw.8
+
+WARNS?= 4
+
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/vipw/vipw.8 b/usr.sbin/vipw/vipw.8
new file mode 100644
index 0000000..7162baa
--- /dev/null
+++ b/usr.sbin/vipw/vipw.8
@@ -0,0 +1,119 @@
+.\" 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.
+.\"
+.\" @(#)vipw.8 8.1 (Berkeley) 6/6/93
+.\" $FreeBSD$
+.\"
+.Dd June 6, 1993
+.Dt VIPW 8
+.Os
+.Sh NAME
+.Nm vipw
+.Nd edit the password file
+.Sh SYNOPSIS
+.Nm
+.Op Fl d Ar directory
+.Sh DESCRIPTION
+The
+.Nm
+utility edits the password file after setting the appropriate locks,
+and does any necessary processing after the password file is unlocked.
+If the password file is already locked for editing by another user,
+.Nm
+will ask you
+to try again later.
+The default editor for
+.Nm
+is
+.Xr vi 1 .
+.Pp
+When run without options,
+.Nm
+will work with the password files in
+.Pa /etc .
+The
+.Fl d
+option may be used to specify an alternative
+.Ar directory
+to work with.
+.Pp
+The
+.Nm
+utility performs a number of consistency checks on the password entries,
+and will not allow a password file with a
+.Dq mangled
+entry to be
+installed.
+If
+.Nm
+rejects the new password file, the user is prompted to re-enter
+the edit session.
+.Pp
+Once the information has been verified,
+.Nm
+uses
+.Xr pwd_mkdb 8
+to update the user database.
+This is run in the background, and,
+at very large sites could take several minutes.
+Until this update
+is completed, the password file is unavailable for other updates
+and the new information is not available to programs.
+.Sh ENVIRONMENT
+If the following environment variable exists it will be utilized by
+.Nm :
+.Bl -tag -width PW_SCAN_BIG_IDS
+.It Ev EDITOR
+The editor specified by the string
+.Ev EDITOR
+will be invoked instead of the default editor
+.Xr vi 1 .
+This can be used to allow a script to non-interactively modify the
+password file.
+.It Ev PW_SCAN_BIG_IDS
+See
+.Xr pwd_mkdb 8 .
+.El
+.Sh SEE ALSO
+.Xr chpass 1 ,
+.Xr passwd 1 ,
+.Xr passwd 5 ,
+.Xr adduser 8 ,
+.Xr pw 8 ,
+.Xr pwd_mkdb 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.0 .
+.Sh BUGS
+The mechanism for checking for password file modifications requires that
+.Ev EDITOR
+run for at least one second.
+Non-interactive editor scripts should invoke
+.Xr sleep 1
+or equivalent to ensure this happens.
diff --git a/usr.sbin/vipw/vipw.c b/usr.sbin/vipw/vipw.c
new file mode 100644
index 0000000..4a0a700
--- /dev/null
+++ b/usr.sbin/vipw/vipw.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 1987, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * Portions of this software were developed for the FreeBSD Project by
+ * ThinkSec AS 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.
+ * 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) 1987, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)vipw.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <libutil.h> /* must be after pwd.h */
+
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ const char *passwd_dir = NULL;
+ int ch, pfd, tfd;
+ char *line;
+ size_t len;
+
+ while ((ch = getopt(argc, argv, "d:")) != -1)
+ switch (ch) {
+ case 'd':
+ passwd_dir = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 0)
+ usage();
+
+ if (pw_init(passwd_dir, NULL) == -1)
+ err(1, "pw_init()");
+ if ((pfd = pw_lock()) == -1) {
+ pw_fini();
+ err(1, "pw_lock()");
+ }
+ if ((tfd = pw_tmp(pfd)) == -1) {
+ pw_fini();
+ err(1, "pw_tmp()");
+ }
+ (void)close(tfd);
+ /* Force umask for partial writes made in the edit phase */
+ (void)umask(077);
+
+ for (;;) {
+ switch (pw_edit(0)) {
+ case -1:
+ pw_fini();
+ err(1, "pw_edit()");
+ case 0:
+ pw_fini();
+ errx(0, "no changes made");
+ default:
+ break;
+ }
+ if (pw_mkdb(NULL) == 0) {
+ pw_fini();
+ errx(0, "password list updated");
+ }
+ printf("re-edit the password file? ");
+ fflush(stdout);
+ if ((line = fgetln(stdin, &len)) == NULL) {
+ pw_fini();
+ err(1, "fgetln()");
+ }
+ if (len > 0 && (*line == 'N' || *line == 'n'))
+ break;
+ }
+ pw_fini();
+ exit(0);
+}
+
+static void
+usage(void)
+{
+
+ (void)fprintf(stderr, "usage: vipw [-d directory]\n");
+ exit(1);
+}
diff --git a/usr.sbin/watch/Makefile b/usr.sbin/watch/Makefile
new file mode 100644
index 0000000..e8a8ea6
--- /dev/null
+++ b/usr.sbin/watch/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+PROG= watch
+MAN= watch.8
+
+WARNS?= 2
+
+DPADD= ${LIBTERMCAP}
+LDADD= -ltermcap
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/watch/watch.8 b/usr.sbin/watch/watch.8
new file mode 100644
index 0000000..99c6b6f
--- /dev/null
+++ b/usr.sbin/watch/watch.8
@@ -0,0 +1,120 @@
+.\"
+.\" $FreeBSD$
+.\"
+.Dd November 24, 2001
+.Dt WATCH 8
+.Os
+.Sh NAME
+.Nm watch
+.Nd snoop on another tty line
+.Sh SYNOPSIS
+.Nm
+.Op Fl cinotW
+.Op Fl f Ar snpdev
+.Op Ar tty
+.Sh DESCRIPTION
+The
+.Nm
+utility allows the user to examine all data coming through a specified tty
+using the
+.Xr snp 4
+device.
+If the
+.Xr snp 4
+device is not available,
+.Nm
+will attempt to load the module
+.Pq Nm snp .
+The
+.Nm
+utility writes to standard output.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl c
+Reconnect on close.
+If the tty observed by
+.Nm
+is closed, automatically reattach to the same tty.
+If this option is not specified,
+.Nm
+will request a new tty if running in interactive mode or exit if running
+without a controlling tty.
+.It Fl f Ar snpdev
+If this option is specified,
+.Nm
+will use
+.Ar snpdev
+as the
+.Xr snp 4
+device.
+Without this option,
+.Nm
+will attempt to find the next available
+.Xr snp 4
+device.
+.It Fl i
+Force interactive mode.
+Interactive mode is a default if
+.Nm
+is started from a tty.
+If output is redirected to a file, interactive mode can still be requested
+by specifying this option.
+.It Fl n
+Disable the ability to switch the watched tty interactively.
+This disables
+both change requests made with <control-X> as well as automatic prompting
+when the current tty is closed or overflows.
+In all cases where a prompt
+would be displayed,
+.Nm
+will exit.
+The reconnect flags are unaffected by
+this option.
+When this flag is used, <control-X> is passed through to the terminal.
+.It Fl o
+Reconnect on overflow.
+The behavior of
+.Nm
+if the observed tty overflows is similar to the behavior if the observed tty
+is closed.
+For more info see
+.Xr snp 4 .
+.It Fl t
+Print the date and time when observation of a given tty is started.
+.It Fl W
+Allow write access to observed tty.
+.It Ar tty
+Tty may be specified as a tty-style device, such as a pseudo tty device,
+a virtual console, or a serial line, etc.
+Names may be preceded by
+.Pa /dev/ .
+.El
+.Sh OPERATION
+While running in interactive mode, all user input is discarded except for:
+.Pp
+.Bl -tag -width "XXXX" -compact
+.It Sy "<control-G>"
+Exit
+.Nm .
+.It Sy "<control-W>"
+Clear screen.
+.It Sy "<control-X>"
+Change attached tty, unless this feature is disabled, in which case
+control-X is passed to the terminal as with other control characters.
+.El
+.Sh SEE ALSO
+.Xr pty 4 ,
+.Xr sio 4 ,
+.Xr snp 4 ,
+.Xr kldload 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 2.1 .
+.Sh AUTHORS
+.An Ugen J.S. Antsilevich Aq ugen@NetVision.net.il
+.Sh BUGS
+No terminal emulation is performed.
+All user output is reproduced as-is.
diff --git a/usr.sbin/watch/watch.c b/usr.sbin/watch/watch.c
new file mode 100644
index 0000000..388614e
--- /dev/null
+++ b/usr.sbin/watch/watch.c
@@ -0,0 +1,442 @@
+/*
+ * Copyright (c) 1995 Ugen J.S.Antsilevich
+ *
+ * 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.
+ *
+ * Snoop stuff.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/fcntl.h>
+#include <sys/filio.h>
+#include <sys/snoop.h>
+#include <sys/stat.h>
+#include <sys/linker.h>
+#include <sys/module.h>
+
+#include <err.h>
+#include <errno.h>
+#include <locale.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <termcap.h>
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+
+#define MSG_INIT "Snoop started."
+#define MSG_OFLOW "Snoop stopped due to overflow. Reconnecting."
+#define MSG_CLOSED "Snoop stopped due to tty close. Reconnecting."
+#define MSG_CHANGE "Snoop device change by user request."
+#define MSG_NOWRITE "Snoop device change due to write failure."
+
+
+#define DEV_NAME_LEN 1024 /* for /dev/ttyXX++ */
+#define MIN_SIZE 256
+
+#define CHR_SWITCH 24 /* Ctrl+X */
+#define CHR_CLEAR 23 /* Ctrl+V */
+
+static void clear(void);
+static void timestamp(const char *);
+static void set_tty(void);
+static void unset_tty(void);
+static void fatal(int, const char *);
+static int open_snp(void);
+static void cleanup(int);
+static void usage(void) __dead2;
+static void setup_scr(void);
+static void attach_snp(void);
+static void detach_snp(void);
+static void set_dev(const char *);
+static void ask_dev(char *, const char *);
+
+int opt_reconn_close = 0;
+int opt_reconn_oflow = 0;
+int opt_interactive = 1;
+int opt_timestamp = 0;
+int opt_write = 0;
+int opt_no_switch = 0;
+const char *opt_snpdev;
+
+char dev_name[DEV_NAME_LEN];
+int snp_io;
+int std_in = 0, std_out = 1;
+
+
+int clear_ok = 0;
+struct termios otty;
+char tbuf[1024], gbuf[1024];
+
+
+static void
+clear(void)
+{
+ if (clear_ok)
+ tputs(gbuf, 1, putchar);
+ fflush(stdout);
+}
+
+static void
+timestamp(const char *buf)
+{
+ time_t t;
+ char btmp[1024];
+ clear();
+ printf("\n---------------------------------------------\n");
+ t = time(NULL);
+ strftime(btmp, 1024, "Time: %d %b %H:%M", localtime(&t));
+ printf("%s\n", btmp);
+ printf("%s\n", buf);
+ printf("---------------------------------------------\n");
+ fflush(stdout);
+}
+
+static void
+set_tty(void)
+{
+ struct termios ntty;
+
+ tcgetattr (std_in, &otty);
+ ntty = otty;
+ ntty.c_lflag &= ~ICANON; /* disable canonical operation */
+ ntty.c_lflag &= ~ECHO;
+#ifdef FLUSHO
+ ntty.c_lflag &= ~FLUSHO;
+#endif
+#ifdef PENDIN
+ ntty.c_lflag &= ~PENDIN;
+#endif
+#ifdef IEXTEN
+ ntty.c_lflag &= ~IEXTEN;
+#endif
+ ntty.c_cc[VMIN] = 1; /* minimum of one character */
+ ntty.c_cc[VTIME] = 0; /* timeout value */
+
+ ntty.c_cc[VINTR] = 07; /* ^G */
+ ntty.c_cc[VQUIT] = 07; /* ^G */
+ tcsetattr (std_in, TCSANOW, &ntty);
+}
+
+static void
+unset_tty(void)
+{
+ tcsetattr (std_in, TCSANOW, &otty);
+}
+
+
+static void
+fatal(int error, const char *buf)
+{
+ unset_tty();
+ if (buf)
+ errx(error, "fatal: %s", buf);
+ else
+ exit(error);
+}
+
+static int
+open_snp(void)
+{
+ int f, mode;
+
+ if (opt_write)
+ mode = O_RDWR;
+ else
+ mode = O_RDONLY;
+
+ if (opt_snpdev == NULL)
+ f = open(_PATH_DEV "snp", mode);
+ else
+ f = open(opt_snpdev, mode);
+ if (f == -1)
+ fatal(EX_OSFILE, "cannot open snoop device");
+
+ return (f);
+}
+
+
+static void
+cleanup(int signo __unused)
+{
+ if (opt_timestamp)
+ timestamp("Logging Exited.");
+ close(snp_io);
+ unset_tty();
+ exit(EX_OK);
+}
+
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: watch [-ciotnW] [tty name]\n");
+ exit(EX_USAGE);
+}
+
+static void
+setup_scr(void)
+{
+ char *cbuf = gbuf, *term;
+ if (!opt_interactive)
+ return;
+ if ((term = getenv("TERM")))
+ if (tgetent(tbuf, term) == 1)
+ if (tgetstr("cl", &cbuf))
+ clear_ok = 1;
+ set_tty();
+ clear();
+}
+
+static void
+detach_snp(void)
+{
+ int fd;
+
+ fd = -1;
+ ioctl(snp_io, SNPSTTY, &fd);
+}
+
+static void
+attach_snp(void)
+{
+ int snp_tty;
+
+ snp_tty = open(dev_name, O_RDONLY | O_NONBLOCK);
+ if (snp_tty < 0)
+ fatal(EX_DATAERR, "can't open device");
+ if (ioctl(snp_io, SNPSTTY, &snp_tty) != 0)
+ fatal(EX_UNAVAILABLE, "cannot attach to tty");
+ close(snp_tty);
+ if (opt_timestamp)
+ timestamp("Logging Started.");
+}
+
+
+static void
+set_dev(const char *name)
+{
+ char buf[DEV_NAME_LEN];
+ struct stat sb;
+
+ if (strlen(name) > 5 && !strncmp(name, _PATH_DEV, sizeof _PATH_DEV - 1)) {
+ snprintf(buf, sizeof buf, "%s", name);
+ } else {
+ if (strlen(name) == 2)
+ sprintf(buf, "%s%s", _PATH_TTY, name);
+ else
+ sprintf(buf, "%s%s", _PATH_DEV, name);
+ }
+
+ if (*name == '\0' || stat(buf, &sb) < 0)
+ fatal(EX_DATAERR, "bad device name");
+
+ if ((sb.st_mode & S_IFMT) != S_IFCHR)
+ fatal(EX_DATAERR, "must be a character device");
+
+ strncpy(dev_name, buf, DEV_NAME_LEN);
+
+ attach_snp();
+}
+
+void
+ask_dev(char *dbuf, const char *msg)
+{
+ char buf[DEV_NAME_LEN];
+ int len;
+
+ clear();
+ unset_tty();
+
+ if (msg)
+ printf("%s\n", msg);
+ if (dbuf)
+ printf("Enter device name [%s]:", dbuf);
+ else
+ printf("Enter device name:");
+
+ if (fgets(buf, DEV_NAME_LEN - 1, stdin)) {
+ len = strlen(buf);
+ if (buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+ if (buf[0] != '\0' && buf[0] != ' ')
+ strcpy(dbuf, buf);
+ }
+ set_tty();
+}
+
+#define READB_LEN 5
+
+int
+main(int ac, char *av[])
+{
+ int ch, res, rv, nread;
+ size_t b_size = MIN_SIZE;
+ char *buf, chb[READB_LEN];
+ fd_set fd_s;
+
+ (void) setlocale(LC_TIME, "");
+
+ if (isatty(std_out))
+ opt_interactive = 1;
+ else
+ opt_interactive = 0;
+
+
+ while ((ch = getopt(ac, av, "Wciotnf:")) != -1)
+ switch (ch) {
+ case 'W':
+ opt_write = 1;
+ break;
+ case 'c':
+ opt_reconn_close = 1;
+ break;
+ case 'i':
+ opt_interactive = 1;
+ break;
+ case 'o':
+ opt_reconn_oflow = 1;
+ break;
+ case 't':
+ opt_timestamp = 1;
+ break;
+ case 'n':
+ opt_no_switch = 1;
+ break;
+ case 'f':
+ opt_snpdev = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+ if (modfind("snp") == -1)
+ if (kldload("snp") == -1 || modfind("snp") == -1)
+ warn("snp module not available");
+
+ signal(SIGINT, cleanup);
+
+ snp_io = open_snp();
+ setup_scr();
+
+ if (*(av += optind) == NULL) {
+ if (opt_interactive && !opt_no_switch)
+ ask_dev(dev_name, MSG_INIT);
+ else
+ fatal(EX_DATAERR, "no device name given");
+ } else
+ strncpy(dev_name, *av, DEV_NAME_LEN);
+
+ set_dev(dev_name);
+
+ if (!(buf = (char *) malloc(b_size)))
+ fatal(EX_UNAVAILABLE, "malloc failed");
+
+ FD_ZERO(&fd_s);
+
+ while (1) {
+ if (opt_interactive)
+ FD_SET(std_in, &fd_s);
+ FD_SET(snp_io, &fd_s);
+ res = select(snp_io + 1, &fd_s, NULL, NULL, NULL);
+ if (opt_interactive && FD_ISSET(std_in, &fd_s)) {
+
+ if ((res = ioctl(std_in, FIONREAD, &nread)) != 0)
+ fatal(EX_OSERR, "ioctl(FIONREAD)");
+ if (nread > READB_LEN)
+ nread = READB_LEN;
+ rv = read(std_in, chb, nread);
+ if (rv == -1 || rv != nread)
+ fatal(EX_IOERR, "read (stdin) failed");
+
+ switch (chb[0]) {
+ case CHR_CLEAR:
+ clear();
+ break;
+ case CHR_SWITCH:
+ if (!opt_no_switch) {
+ detach_snp();
+ ask_dev(dev_name, MSG_CHANGE);
+ set_dev(dev_name);
+ break;
+ }
+ default:
+ if (opt_write) {
+ rv = write(snp_io, chb, nread);
+ if (rv == -1 || rv != nread) {
+ detach_snp();
+ if (opt_no_switch)
+ fatal(EX_IOERR,
+ "write failed");
+ ask_dev(dev_name, MSG_NOWRITE);
+ set_dev(dev_name);
+ }
+ }
+
+ }
+ }
+ if (!FD_ISSET(snp_io, &fd_s))
+ continue;
+
+ if ((res = ioctl(snp_io, FIONREAD, &nread)) != 0)
+ fatal(EX_OSERR, "ioctl(FIONREAD)");
+
+ switch (nread) {
+ case SNP_OFLOW:
+ if (opt_reconn_oflow)
+ attach_snp();
+ else if (opt_interactive && !opt_no_switch) {
+ ask_dev(dev_name, MSG_OFLOW);
+ set_dev(dev_name);
+ } else
+ cleanup(-1);
+ break;
+ case SNP_DETACH:
+ case SNP_TTYCLOSE:
+ if (opt_reconn_close)
+ attach_snp();
+ else if (opt_interactive && !opt_no_switch) {
+ ask_dev(dev_name, MSG_CLOSED);
+ set_dev(dev_name);
+ } else
+ cleanup(-1);
+ break;
+ default:
+ if (nread < (b_size / 2) && (b_size / 2) > MIN_SIZE) {
+ free(buf);
+ if (!(buf = (char *) malloc(b_size / 2)))
+ fatal(EX_UNAVAILABLE, "malloc failed");
+ b_size = b_size / 2;
+ }
+ if (nread > b_size) {
+ b_size = (nread % 2) ? (nread + 1) : (nread);
+ free(buf);
+ if (!(buf = (char *) malloc(b_size)))
+ fatal(EX_UNAVAILABLE, "malloc failed");
+ }
+ rv = read(snp_io, buf, nread);
+ if (rv == -1 || rv != nread)
+ fatal(EX_IOERR, "read failed");
+ rv = write(std_out, buf, nread);
+ if (rv == -1 || rv != nread)
+ fatal(EX_IOERR, "write failed");
+ }
+ } /* While */
+ return(0);
+}
+
diff --git a/usr.sbin/watchdogd/Makefile b/usr.sbin/watchdogd/Makefile
new file mode 100644
index 0000000..08880e1
--- /dev/null
+++ b/usr.sbin/watchdogd/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+PROG= watchdogd
+LINKS= ${BINDIR}/watchdogd ${BINDIR}/watchdog
+MAN= watchdogd.8 watchdog.8
+WARNS?= 6
+
+LDADD= -lm -lutil
+DPADD= ${LIBM} ${LIBUTIL}
+
+.include <bsd.prog.mk>
+
+test: ${PROG}
+ ./${PROG} -t 1.0
diff --git a/usr.sbin/watchdogd/watchdog.8 b/usr.sbin/watchdogd/watchdog.8
new file mode 100644
index 0000000..9ca9fe0
--- /dev/null
+++ b/usr.sbin/watchdogd/watchdog.8
@@ -0,0 +1,73 @@
+.\" Copyright (c) 2004 Poul-Henning Kamp <phk@FreeBSD.org>
+.\" Copyright (c) 2003 Sean M. Kelly <smkelly@FreeBSD.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd September 2, 2006
+.Dt WATCHDOG 8
+.Os
+.Sh NAME
+.Nm watchdog
+.Nd watchdog control program
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl t Ar timeout
+.Sh DESCRIPTION
+The
+.Nm
+utility can be used to control the kernel's watchdog facility.
+.Pp
+The
+.Fl d
+option
+enables debugging.
+.Pp
+The
+.Fl t Ar timeout
+option
+specifies the desired timeout period in seconds, a value of
+zero will disable the watchdog.
+The default timeout is 16 seconds.
+.Sh SEE ALSO
+.Xr watchdog 4 ,
+.Xr watchdogd 8 ,
+.Xr watchdog 9
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 5.1 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility and manual page were written by
+.An Sean Kelly Aq smkelly@FreeBSD.org
+and
+.An Poul-Henning Kamp Aq phk@FreeBSD.org .
+.Pp
+Some contributions made by
+.An Jeff Roberson Aq jeff@FreeBSD.org .
diff --git a/usr.sbin/watchdogd/watchdogd.8 b/usr.sbin/watchdogd/watchdogd.8
new file mode 100644
index 0000000..b00fef1
--- /dev/null
+++ b/usr.sbin/watchdogd/watchdogd.8
@@ -0,0 +1,127 @@
+.\" Copyright (c) 2004 Poul-Henning Kamp <phk@FreeBSD.org>
+.\" Copyright (c) 2003 Sean M. Kelly <smkelly@FreeBSD.org>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd September 2, 2006
+.Dt WATCHDOGD 8
+.Os
+.Sh NAME
+.Nm watchdogd
+.Nd watchdog daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl e Ar cmd
+.Op Fl I Ar file
+.Op Fl s Ar sleep
+.Op Fl t Ar timeout
+.Sh DESCRIPTION
+The
+.Nm
+utility interfaces with the kernel's watchdog facility to ensure
+that the system is in a working state.
+If
+.Nm
+is unable to interface with the kernel over a specific timeout,
+the kernel will take actions to assist in debugging or restarting the computer.
+.Pp
+If
+.Fl e Ar cmd
+is specified,
+.Nm
+will attempt to execute this command with
+.Xr system 3 ,
+and only if the command returns with a zero exit code will the
+watchdog be reset.
+If
+.Fl e Ar cmd
+is not specified, the daemon will perform a trivial file system
+check instead.
+.Pp
+The
+.Fl s Ar sleep
+argument can be used to control the sleep period between each execution
+of the check and defaults to one second.
+.Pp
+The
+.Fl t Ar timeout
+specifies the desired timeout period in seconds.
+The default timeout is 16 seconds.
+.Pp
+One possible circumstance which will cause a watchdog timeout is an interrupt
+storm.
+If this occurs,
+.Nm
+will no longer execute and thus the kernel's watchdog routines will take
+action after a configurable timeout.
+.Pp
+Upon receiving the
+.Dv SIGTERM
+or
+.Dv SIGINT
+signals,
+.Nm
+will first instruct the kernel to no longer perform watchdog checks and then
+will terminate.
+.Pp
+The
+.Nm
+utility recognizes the following runtime options:
+.Bl -tag -width ".Fl I Ar file"
+.It Fl I Ar file
+Write the process ID of the
+.Nm
+utility in the specified file.
+.It Fl d
+Do not fork.
+When this option is specified,
+.Nm
+will not fork into the background at startup.
+.El
+.Sh FILES
+.Bl -tag -width ".Pa /var/run/watchdogd.pid" -compact
+.It Pa /var/run/watchdogd.pid
+.El
+.Sh SEE ALSO
+.Xr watchdog 4 ,
+.Xr watchdog 8 ,
+.Xr watchdog 9
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 5.1 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+utility and manual page were written by
+.An Sean Kelly Aq smkelly@FreeBSD.org
+and
+.An Poul-Henning Kamp Aq phk@FreeBSD.org .
+.Pp
+Some contributions made by
+.An Jeff Roberson Aq jeff@FreeBSD.org .
diff --git a/usr.sbin/watchdogd/watchdogd.c b/usr.sbin/watchdogd/watchdogd.c
new file mode 100644
index 0000000..6f0e840
--- /dev/null
+++ b/usr.sbin/watchdogd/watchdogd.c
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 2003-2004 Sean M. Kelly <smkelly@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE 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.
+ */
+
+/*
+ * Software watchdog daemon.
+ */
+
+#include <sys/types.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/rtprio.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/watchdog.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libutil.h>
+#include <math.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+static void parseargs(int, char *[]);
+static void sighandler(int);
+static void watchdog_loop(void);
+static int watchdog_init(void);
+static int watchdog_onoff(int onoff);
+static int watchdog_patpat(u_int timeout);
+static void usage(void);
+
+int debugging = 0;
+int end_program = 0;
+const char *pidfile = _PATH_VARRUN "watchdogd.pid";
+int reset_mib[3];
+size_t reset_miblen = 3;
+u_int timeout = WD_TO_16SEC;
+u_int passive = 0;
+int is_daemon = 0;
+int fd = -1;
+int nap = 1;
+char *test_cmd = NULL;
+
+/*
+ * Periodically pat the watchdog, preventing it from firing.
+ */
+int
+main(int argc, char *argv[])
+{
+ struct rtprio rtp;
+ struct pidfh *pfh;
+ pid_t otherpid;
+
+ if (getuid() != 0)
+ errx(EX_SOFTWARE, "not super user");
+
+ parseargs(argc, argv);
+
+ rtp.type = RTP_PRIO_REALTIME;
+ rtp.prio = 0;
+ if (rtprio(RTP_SET, 0, &rtp) == -1)
+ err(EX_OSERR, "rtprio");
+
+ if (watchdog_init() == -1)
+ errx(EX_SOFTWARE, "unable to initialize watchdog");
+
+ if (is_daemon) {
+ if (watchdog_onoff(1) == -1)
+ exit(EX_SOFTWARE);
+
+ pfh = pidfile_open(pidfile, 0600, &otherpid);
+ if (pfh == NULL) {
+ if (errno == EEXIST) {
+ errx(EX_SOFTWARE, "%s already running, pid: %d",
+ getprogname(), otherpid);
+ }
+ warn("Cannot open or create pidfile");
+ }
+
+ if (debugging == 0 && daemon(0, 0) == -1) {
+ watchdog_onoff(0);
+ pidfile_remove(pfh);
+ err(EX_OSERR, "daemon");
+ }
+
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGINT, sighandler);
+ signal(SIGTERM, sighandler);
+
+ pidfile_write(pfh);
+
+ watchdog_loop();
+
+ /* exiting */
+ pidfile_remove(pfh);
+ return (EX_OK);
+ } else {
+ if (passive)
+ timeout |= WD_PASSIVE;
+ else
+ timeout |= WD_ACTIVE;
+ if (watchdog_patpat(timeout) < 0)
+ err(EX_OSERR, "patting the dog");
+ return (EX_OK);
+ }
+}
+
+/*
+ * Catch signals and begin shutdown process.
+ */
+static void
+sighandler(int signum)
+{
+
+ if (signum == SIGINT || signum == SIGTERM)
+ end_program = 1;
+}
+
+/*
+ * Open the watchdog device.
+ */
+static int
+watchdog_init()
+{
+
+ fd = open("/dev/" _PATH_WATCHDOG, O_RDWR);
+ if (fd >= 0)
+ return (0);
+ warn("Could not open watchdog device");
+ return (-1);
+}
+
+/*
+ * Main program loop which is iterated every second.
+ */
+static void
+watchdog_loop(void)
+{
+ struct stat sb;
+ int failed;
+
+ while (end_program != 2) {
+ failed = 0;
+
+ if (test_cmd != NULL)
+ failed = system(test_cmd);
+ else
+ failed = stat("/etc", &sb);
+
+ if (failed == 0)
+ watchdog_patpat(timeout|WD_ACTIVE);
+ sleep(nap);
+
+ if (end_program != 0) {
+ if (watchdog_onoff(0) == 0) {
+ end_program = 2;
+ } else {
+ warnx("Could not stop the watchdog, not exitting");
+ end_program = 0;
+ }
+ }
+ }
+}
+
+/*
+ * Reset the watchdog timer. This function must be called periodically
+ * to keep the watchdog from firing.
+ */
+int
+watchdog_patpat(u_int t)
+{
+
+ return ioctl(fd, WDIOCPATPAT, &t);
+}
+
+/*
+ * Toggle the kernel's watchdog. This routine is used to enable and
+ * disable the watchdog.
+ */
+static int
+watchdog_onoff(int onoff)
+{
+
+ if (onoff)
+ return watchdog_patpat((timeout|WD_ACTIVE));
+ else
+ return watchdog_patpat(0);
+}
+
+/*
+ * Tell user how to use the program.
+ */
+static void
+usage()
+{
+ if (is_daemon)
+ fprintf(stderr, "usage: watchdogd [-d] [-e cmd] [-I file] [-s sleep] [-t timeout]\n");
+ else
+ fprintf(stderr, "usage: watchdog [-d] [-t timeout]\n");
+ exit(EX_USAGE);
+}
+
+/*
+ * Handle the few command line arguments supported.
+ */
+static void
+parseargs(int argc, char *argv[])
+{
+ int c;
+ char *p;
+ double a;
+
+ c = strlen(argv[0]);
+ if (argv[0][c - 1] == 'd')
+ is_daemon = 1;
+ while ((c = getopt(argc, argv,
+ is_daemon ? "I:de:s:t:?" : "dt:?")) != -1) {
+ switch (c) {
+ case 'I':
+ pidfile = optarg;
+ break;
+ case 'd':
+ debugging = 1;
+ break;
+ case 'e':
+ test_cmd = strdup(optarg);
+ break;
+#ifdef notyet
+ case 'p':
+ passive = 1;
+ break;
+#endif
+ case 's':
+ p = NULL;
+ errno = 0;
+ nap = strtol(optarg, &p, 0);
+ if ((p != NULL && *p != '\0') || errno != 0)
+ errx(EX_USAGE, "-s argument is not a number");
+ break;
+ case 't':
+ p = NULL;
+ errno = 0;
+ a = strtod(optarg, &p);
+ if ((p != NULL && *p != '\0') || errno != 0)
+ errx(EX_USAGE, "-t argument is not a number");
+ if (a < 0)
+ errx(EX_USAGE, "-t argument must be positive");
+ if (a == 0)
+ timeout = WD_TO_NEVER;
+ else
+ timeout = 1.0 + log(a * 1e9) / log(2.0);
+ if (debugging)
+ printf("Timeout is 2^%d nanoseconds\n",
+ timeout);
+ break;
+ case '?':
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ if (argc != optind)
+ errx(EX_USAGE, "extra arguments.");
+ if (is_daemon && timeout < WD_TO_1SEC)
+ errx(EX_USAGE, "-t argument is less than one second.");
+}
diff --git a/usr.sbin/wlandebug/Makefile b/usr.sbin/wlandebug/Makefile
new file mode 100644
index 0000000..5284257
--- /dev/null
+++ b/usr.sbin/wlandebug/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+PROG= wlandebug
+MAN= wlandebug.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/wlandebug/wlandebug.8 b/usr.sbin/wlandebug/wlandebug.8
new file mode 100644
index 0000000..ccb1d85
--- /dev/null
+++ b/usr.sbin/wlandebug/wlandebug.8
@@ -0,0 +1,185 @@
+.\" Copyright (c) 2007 Sam Leffler, Errno Consulting
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 May 19, 2008
+.Dt WLANDEBUG 8
+.Os
+.Sh NAME
+.Nm wlandebug
+.Nd "set/query 802.11 wireless debugging messages"
+.Sh SYNOPSIS
+.Nm
+.Op Fl d | Fl i Ar ifnet
+.Op Fl flag|+flag Ar ...
+.Sh DESCRIPTION
+The
+.Nm
+command is a tool for enabling and disabling
+debugging messages in the
+.Xr wlan 4
+module.
+Running
+.Nm
+without any options will display the current messages
+enabled for the specified network interface
+(by default, ``ath0').
+The default debugging level for new interfaces can be set
+by specifying the
+.Fl d
+option.
+When run as the super-user
+.Nm
+can be used to enable and/or disable debugging messages.
+.Pp
+To enable debugging messages of a certain
+.Ar type
+use
+.Ar +type ;
+to disable such messages use
+.Ar -type .
+Multiple messages can be enabled and disabled with a single command.
+.Pp
+Messages are organized in the following groups:
+.Bl -tag -width ".Ar dumppkts"
+.It Ar debug
+general debugging facilities; equivalent to setting the debug
+parameter with
+.Xr ifconfig 8 .
+.It Ar dumppkts
+dump packet contents on transmit and receive.
+.It Ar crypto
+crypto-related work.
+.It Ar input
+errors encountered during input handling.
+.It Ar xrate
+extended rate set handling (for 802.11g).
+.It Ar elemid
+information element processing in 802.11 management frames.
+.It Ar node
+management of per-station state.
+.It Ar assoc
+802.11 station association processing; particularly useful to
+see when stations join and leave a BSS.
+.It Ar auth
+802.11 station authentication processing.
+.It Ar scan
+scanning operation; especially useful for debugging problems
+with not locating an access point.
+.It Ar output
+errors encountered during output handling.
+.It Ar state
+.Xr wlan 4
+state machine operation.
+.It Ar power
+802.11 power save operation; in hostap mode this enables
+copious information about buffered frames for stations operating
+in power save mode.
+.It Ar dot1x
+802.1x operation; not presently meaningful as 802.1x protocol
+support is implemented in user mode by the
+.Xr hostapd 8
+program.
+.It Ar dot1xsm
+802.1x state machine operation; not presently meaningful as 802.1x protocol
+support is implemented in user mode by the
+.Xr hostapd 8
+program.
+.It Ar radius
+radius backend operation as it relates to 802.1x operation;
+not presently meaningful as 802.1x protocol
+support is implemented in user mode by the
+.Xr hostapd 8
+program.
+.It Ar raddump
+dump packets exchanged with the radius backend for 802.1x operation;
+not presently meaningful as 802.1x protocol
+support is implemented in user mode by the
+.Xr hostapd 8
+program.
+.It Ar radkeys
+include key contents when dumping packets exchanged with the
+radius backend for 802.1x operation;
+not presently meaningful as 802.1x protocol
+support is implemented in user mode by the
+.Xr hostapd 8
+program.
+.It Ar wpa
+trace operation of the WPA protocol;
+only partly meaningful as WPA protocol
+support is mostly implemented in user mode by the
+.Xr hostapd 8
+and
+.Xr wpa_supplicant 8
+programs.
+.It Ar acl
+trace operation of the Access Control List (ACL) support; see
+.Xr wlan_acl 4
+for more details.
+.It Ar wme
+trace operation of WME/WMM protocol processing.
+.It Ar superg
+trace operation of Atheros SuperG protocol processing.
+.It Ar doth
+trace operation of IEEE 802.11h protocol processing.
+.It Ar inact
+trace station inactivity processing; in particular,
+show when stations associated to an access point are dropped due to
+inactivity.
+.It Ar roam
+trace station mode roaming between access points.
+.It Ar rate
+trace transmit rate control operation.
+.El
+.Sh EXAMPLES
+The following might be used to debug basic station mode operation:
+.Pp
+.Dl "wlandebug -i ral0 scan+auth+assoc"
+.Pp
+it enables debug messages while scanning, authenticating to
+an access point, and associating to an access point.
+.Sh SEE ALSO
+.Xr ifconfig 8 ,
+.Xr wlanstats 8 ,
+.Xr athdebug 8 ,
+.Xr athstats 8 .
+.Sh NOTES
+Different wireless drivers support different debugging messages.
+Drivers such as
+.Xr ath 4
+and
+.Xr ral 4
+that depend on the
+.Xr wlan 4
+module for 802.11 protocol processing typically support
+most of the debugging messages while devices that
+implement parts of the 802.11 protocol in firmware do not.
+.Pp
+Some debugging messages are no longer meaningful
+because protocol processing has moved from the operating
+system to user mode programs such as
+.Xr hostapd 8
+and
+.Xr wpa_supplicant 8 .
diff --git a/usr.sbin/wlandebug/wlandebug.c b/usr.sbin/wlandebug/wlandebug.c
new file mode 100644
index 0000000..b342875
--- /dev/null
+++ b/usr.sbin/wlandebug/wlandebug.c
@@ -0,0 +1,243 @@
+/*-
+ * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that 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.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * wlandebug [-i interface] flags
+ * (default interface is wlan.0).
+ */
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <getopt.h>
+#include <string.h>
+#include <err.h>
+
+#define N(a) (sizeof(a)/sizeof(a[0]))
+
+const char *progname;
+
+#define IEEE80211_MSG_11N 0x80000000 /* 11n mode debug */
+#define IEEE80211_MSG_DEBUG 0x40000000 /* IFF_DEBUG equivalent */
+#define IEEE80211_MSG_DUMPPKTS 0x20000000 /* IFF_LINK2 equivalant */
+#define IEEE80211_MSG_CRYPTO 0x10000000 /* crypto work */
+#define IEEE80211_MSG_INPUT 0x08000000 /* input handling */
+#define IEEE80211_MSG_XRATE 0x04000000 /* rate set handling */
+#define IEEE80211_MSG_ELEMID 0x02000000 /* element id parsing */
+#define IEEE80211_MSG_NODE 0x01000000 /* node handling */
+#define IEEE80211_MSG_ASSOC 0x00800000 /* association handling */
+#define IEEE80211_MSG_AUTH 0x00400000 /* authentication handling */
+#define IEEE80211_MSG_SCAN 0x00200000 /* scanning */
+#define IEEE80211_MSG_OUTPUT 0x00100000 /* output handling */
+#define IEEE80211_MSG_STATE 0x00080000 /* state machine */
+#define IEEE80211_MSG_POWER 0x00040000 /* power save handling */
+#define IEEE80211_MSG_DOT1X 0x00020000 /* 802.1x authenticator */
+#define IEEE80211_MSG_DOT1XSM 0x00010000 /* 802.1x state machine */
+#define IEEE80211_MSG_RADIUS 0x00008000 /* 802.1x radius client */
+#define IEEE80211_MSG_RADDUMP 0x00004000 /* dump 802.1x radius packets */
+#define IEEE80211_MSG_RADKEYS 0x00002000 /* dump 802.1x keys */
+#define IEEE80211_MSG_WPA 0x00001000 /* WPA/RSN protocol */
+#define IEEE80211_MSG_ACL 0x00000800 /* ACL handling */
+#define IEEE80211_MSG_WME 0x00000400 /* WME protocol */
+#define IEEE80211_MSG_SUPERG 0x00000200 /* Atheros SuperG protocol */
+#define IEEE80211_MSG_DOTH 0x00000100 /* 802.11h support */
+#define IEEE80211_MSG_INACT 0x00000080 /* inactivity handling */
+#define IEEE80211_MSG_ROAM 0x00000040 /* sta-mode roaming */
+#define IEEE80211_MSG_RATECTL 0x00000020 /* tx rate control */
+#define IEEE80211_MSG_ACTION 0x00000010 /* action frame handling */
+#define IEEE80211_MSG_WDS 0x00000008 /* WDS handling */
+#define IEEE80211_MSG_IOCTL 0x00000004 /* ioctl handling */
+#define IEEE80211_MSG_TDMA 0x00000002 /* TDMA handling */
+
+static struct {
+ const char *name;
+ u_int bit;
+} flags[] = {
+ { "11n", IEEE80211_MSG_11N },
+ { "debug", IEEE80211_MSG_DEBUG },
+ { "dumppkts", IEEE80211_MSG_DUMPPKTS },
+ { "crypto", IEEE80211_MSG_CRYPTO },
+ { "input", IEEE80211_MSG_INPUT },
+ { "xrate", IEEE80211_MSG_XRATE },
+ { "elemid", IEEE80211_MSG_ELEMID },
+ { "node", IEEE80211_MSG_NODE },
+ { "assoc", IEEE80211_MSG_ASSOC },
+ { "auth", IEEE80211_MSG_AUTH },
+ { "scan", IEEE80211_MSG_SCAN },
+ { "output", IEEE80211_MSG_OUTPUT },
+ { "state", IEEE80211_MSG_STATE },
+ { "power", IEEE80211_MSG_POWER },
+ { "dot1x", IEEE80211_MSG_DOT1X },
+ { "dot1xsm", IEEE80211_MSG_DOT1XSM },
+ { "radius", IEEE80211_MSG_RADIUS },
+ { "raddump", IEEE80211_MSG_RADDUMP },
+ { "radkeys", IEEE80211_MSG_RADKEYS },
+ { "wpa", IEEE80211_MSG_WPA },
+ { "acl", IEEE80211_MSG_ACL },
+ { "wme", IEEE80211_MSG_WME },
+ { "superg", IEEE80211_MSG_SUPERG },
+ { "doth", IEEE80211_MSG_DOTH },
+ { "inact", IEEE80211_MSG_INACT },
+ { "roam", IEEE80211_MSG_ROAM },
+ { "rate", IEEE80211_MSG_RATECTL },
+ { "action", IEEE80211_MSG_ACTION },
+ { "wds", IEEE80211_MSG_WDS },
+ { "ioctl", IEEE80211_MSG_IOCTL },
+ { "tdma", IEEE80211_MSG_TDMA },
+};
+
+static u_int
+getflag(const char *name, int len)
+{
+ int i;
+
+ for (i = 0; i < N(flags); i++)
+ if (strncasecmp(flags[i].name, name, len) == 0)
+ return flags[i].bit;
+ return 0;
+}
+
+static void
+usage(void)
+{
+ int i;
+
+ fprintf(stderr, "usage: %s [-d | -i device] [flags]\n", progname);
+ fprintf(stderr, "where flags are:\n");
+ for (i = 0; i < N(flags); i++)
+ printf("%s\n", flags[i].name);
+ exit(-1);
+}
+
+static void
+setoid(char oid[], size_t oidlen, const char *wlan)
+{
+#ifdef __linux__
+ if (wlan)
+ snprintf(oid, oidlen, "net.%s.debug", wlan);
+#elif __FreeBSD__
+ if (wlan)
+ snprintf(oid, oidlen, "net.wlan.%s.debug", wlan+4);
+ else
+ snprintf(oid, oidlen, "net.wlan.debug");
+#elif __NetBSD__
+ if (wlan)
+ snprintf(oid, oidlen, "net.link.ieee80211.%s.debug", wlan);
+ else
+ snprintf(oid, oidlen, "net.link.ieee80211.debug");
+#else
+#error "No support for this system"
+#endif
+}
+
+int
+main(int argc, char *argv[])
+{
+ const char *cp, *tp;
+ const char *sep;
+ int op, i;
+ u_int32_t debug, ndebug;
+ size_t debuglen;
+ char oid[256];
+
+ progname = argv[0];
+ setoid(oid, sizeof(oid), "wlan0");
+ if (argc > 1) {
+ if (strcmp(argv[1], "-d") == 0) {
+ setoid(oid, sizeof(oid), NULL);
+ argc -= 1, argv += 1;
+ } else if (strcmp(argv[1], "-i") == 0) {
+ if (argc < 2)
+ errx(1, "missing interface name for -i option");
+ if (strncmp(argv[2], "wlan", 4) != 0)
+ errx(1, "expecting a wlan interface name");
+ setoid(oid, sizeof(oid), argv[2]);
+ argc -= 2, argv += 2;
+ } else if (strcmp(argv[1], "-?") == 0)
+ usage();
+ }
+
+ debuglen = sizeof(debug);
+ if (sysctlbyname(oid, &debug, &debuglen, NULL, 0) < 0)
+ err(1, "sysctl-get(%s)", oid);
+ ndebug = debug;
+ for (; argc > 1; argc--, argv++) {
+ cp = argv[1];
+ do {
+ u_int bit;
+
+ if (*cp == '-') {
+ cp++;
+ op = -1;
+ } else if (*cp == '+') {
+ cp++;
+ op = 1;
+ } else
+ op = 0;
+ for (tp = cp; *tp != '\0' && *tp != '+' && *tp != '-';)
+ tp++;
+ bit = getflag(cp, tp-cp);
+ if (op < 0)
+ ndebug &= ~bit;
+ else if (op > 0)
+ ndebug |= bit;
+ else {
+ if (bit == 0) {
+ int c = *cp;
+ if (isdigit(c))
+ bit = strtoul(cp, NULL, 0);
+ else
+ errx(1, "unknown flag %.*s",
+ (int)(tp-cp), cp);
+ }
+ ndebug = bit;
+ }
+ } while (*(cp = tp) != '\0');
+ }
+ if (debug != ndebug) {
+ printf("%s: 0x%x => ", oid, debug);
+ if (sysctlbyname(oid, NULL, NULL, &ndebug, sizeof(ndebug)) < 0)
+ err(1, "sysctl-set(%s)", oid);
+ printf("0x%x", ndebug);
+ debug = ndebug;
+ } else
+ printf("%s: 0x%x", oid, debug);
+ sep = "<";
+ for (i = 0; i < N(flags); i++)
+ if (debug & flags[i].bit) {
+ printf("%s%s", sep, flags[i].name);
+ sep = ",";
+ }
+ printf("%s\n", *sep != '<' ? ">" : "");
+ return 0;
+}
diff --git a/usr.sbin/wlconfig/Makefile b/usr.sbin/wlconfig/Makefile
new file mode 100644
index 0000000..8d3d0cc
--- /dev/null
+++ b/usr.sbin/wlconfig/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+PROG= wlconfig
+MAN= wlconfig.8
+MANSUBDIR= /i386
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/wlconfig/wlconfig.8 b/usr.sbin/wlconfig/wlconfig.8
new file mode 100644
index 0000000..99a2942
--- /dev/null
+++ b/usr.sbin/wlconfig/wlconfig.8
@@ -0,0 +1,143 @@
+.\" $FreeBSD$
+.\"
+.Dd December 26, 1996
+.Os
+.Dt WLCONFIG 8 i386
+.Sh NAME
+.Nm wlconfig
+.Nd read/write wavelan config parameters
+.Sh SYNOPSIS
+.Nm
+.Ar ifname
+.Op Ar param value ...
+.Sh DESCRIPTION
+The
+.Nm
+utility can be used to read and set parameters for the NCR/AT&T Wavelan
+radio LAN card.
+Various parameters stored in the non-volatile Parameter
+Storage Area (PSA) on the card can be modified with this program, replacing
+the DOS-based
+.Nm instconf.exe
+program.
+It can also be used to interrogate the optional signal
+strength cache which may have been compiled into the driver.
+.Pp
+The
+.Ar ifname
+parameter specifies the wavelan interface name (eg.
+.Pa wl0 ) .
+If no other arguments are supplied, the current contents of the PSA
+are interpreted and displayed.
+.Pp
+The
+.Ar param
+and
+.Ar value
+arguments can be used to change the value of several parameters.
+Any number of
+.Ar param value
+pairs may be supplied.
+.Bl -tag -width 15n -offset indent
+.It Va param
+.Va value
+.It irq
+IRQ value (used at next reset), may be one of 3,4,5,6,10,11,12,15.
+.It mac
+Local MAC value (ethernet address).
+.It macsel
+.Sq soft
+(as set by the
+.Sq mac
+parameter) or
+.Sq default
+(as set at the factory).
+.It nwid
+The NWID is a 2-byte parameter passed to the card's radio modem.
+NWIDs allow multiple logically discrete networks to operate
+independently whilst occupying the same airspace.
+Packets with a different NWID are simply ignored by the modem.
+In the hardware, NWIDs are stored long-term in non-volatile memory
+(called the PSA or programmable storage area), and are loaded by
+software into the radio modem when the driver is
+initialized.
+This sets the default NWID loaded at startup.
+.It currnwid
+This sets the current operating NWID (but does not save it to the
+PSA).
+.It cache
+The driver may maintain a per interface fixed size cache of signal strength,
+silence, and quality levels, which are indexed by sender MAC addresses.
+Input packets are stored in the cache, and when received, the values
+stored in the radio modem are interrogated and stored.
+There are also two sysctl values (iponly and multicast only) which
+can be used for filtering out some input packets.
+By default, the
+cache mechanism stores only non-unicast IP packets, but this can
+be changed with
+.Xr sysctl 8 .
+Each non-filtered
+input packet causes a cache update, hence one can monitor
+the antennae signal strength to a remote system.
+There are three commands that can be given as values:
+.Sq raw ,
+which prints out the raw signal strength data as found in the radio
+modem hardware value,
+.Sq scale ,
+which scales the raw hardware values to 0..100%, and
+.Sq zero
+which clears out the cache in case you want to store new samples.
+.El
+.Pp
+Note that if the IRQ on the Wavelan card is incorrect, the interface
+will be configured, but will not function.
+The
+.Nm
+utility should then be used to reconfigure the card to a sensible
+value.
+.Sh EXAMPLES
+Set the NWID to 0x1234:
+.Bd -literal -offset
+# wlconfig wl0 nwid 0x1234
+.Ed
+.Pp
+Show the current settings:
+.Bd -literal -offset
+# wlconfig wl0
+Board type : ISA
+Base address options : 0x300, 0x390, 0x3c0, 0x3e0
+Waitstates : 0
+Bus mode : ISA
+IRQ : 10
+Default MAC address : 08:00:0e:20:3d:4b
+Soft MAC address : 00:00:00:00:00:00
+Current MAC address : Default
+Adapter compatibility : PC-AT 2.4GHz
+Threshold preset : 1
+Call code required : NO
+Subband : 2425MHz
+Quality threshold : 3
+Hardware version : 0 (Rel1/Rel2)
+Network ID enable : YES
+NWID : 0xdead
+Datalink security : NO
+Databus width : 16 (variable)
+Configuration state : unconfigured
+CRC-16 : 0x3c26
+CRC status : OK
+.Ed
+.Pp
+Print a scaled version of the signal strength cache:
+.Bd -literal -offset
+# wlconfig wl0 cache scale
+.Ed
+.Sh SEE ALSO
+.Xr wl 4 ,
+.Xr sysctl 8
+.Sh HISTORY
+This implementation of the
+.Nm
+utility is completely new, written for Hilink Internet by
+.An Michael Smith ,
+and updated by
+.An Jim Binkley &c .
diff --git a/usr.sbin/wlconfig/wlconfig.c b/usr.sbin/wlconfig/wlconfig.c
new file mode 100644
index 0000000..f361b2c
--- /dev/null
+++ b/usr.sbin/wlconfig/wlconfig.c
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 1996
+ * Michael Smith. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 Michael Smith AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Michael Smith OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 */
+
+/*
+ * wlconfig.c
+ *
+ * utility to read out and change various WAVELAN parameters.
+ * Currently supports NWID and IRQ values.
+ *
+ * The NWID is used by 2 or more wavelan controllers to determine
+ * if packets should be received or not. It is a filter that
+ * is roughly analogous to the "channel" setting with a garage
+ * door controller. Two companies side by side with wavelan devices
+ * that could actually hear each other can use different NWIDs
+ * and ignore packets. In truth however, the air space is shared,
+ * and the NWID is a virtual filter.
+ *
+ * In the current set of wavelan drivers, ioctls changed only
+ * the runtime radio modem registers which act in a manner analogous
+ * to an ethernet transceiver. The ioctls do not change the
+ * stored nvram PSA (or parameter storage area). At boot, the PSA
+ * values are stored in the radio modem. Thus when the
+ * system reboots it will restore the wavelan NWID to the value
+ * stored in the PSA. The NCR/ATT dos utilities must be used to
+ * change the initial NWID values in the PSA. The wlconfig utility
+ * may be used to set a different NWID at runtime; this is only
+ * permitted while the interface is up and running.
+ *
+ * By contrast, the IRQ value can only be changed while the
+ * Wavelan card is down and unconfigured, and it will remain
+ * disabled after an IRQ change until reboot.
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <machine/if_wl_wavelan.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+
+/* translate IRQ bit to number */
+/* array for maping irq numbers to values for the irq parameter register */
+static int irqvals[16] = {
+ 0, 0, 0, 0x01, 0x02, 0x04, 0, 0x08, 0, 0, 0x10, 0x20, 0x40, 0, 0, 0x80
+};
+
+/* cache */
+static int w_sigitems; /* count of valid items */
+static struct w_sigcache wsc[MAXCACHEITEMS];
+
+int
+wlirq(int irqval)
+{
+ int irq;
+
+ for(irq = 0; irq < 16; irq++)
+ if(irqvals[irq] == irqval)
+ return(irq);
+ return 0;
+}
+
+char *compat_type[] = {
+ "PC-AT 915MHz",
+ "PC-MC 915MHz",
+ "PC-AT 2.4GHz",
+ "PC-MC 2.4GHz",
+ "PCCARD or 1/2 size AT, 915MHz or 2.4GHz"
+};
+
+char *subband[] = {
+ "915MHz/see WaveModem",
+ "2425MHz",
+ "2460MHz",
+ "2484MHz",
+ "2430.5MHz"
+};
+
+
+/*
+** print_psa
+**
+** Given a pointer to a PSA structure, print it out
+*/
+void
+print_psa(u_char *psa, int currnwid)
+{
+ int nwid;
+
+ /*
+ ** Work out what sort of board we have
+ */
+ if (psa[0] == 0x14) {
+ printf("Board type : Microchannel\n");
+ } else {
+ if (psa[1] == 0) {
+ printf("Board type : PCCARD\n");
+ } else {
+ printf("Board type : ISA");
+ if ((psa[4] == 0) &&
+ (psa[5] == 0) &&
+ (psa[6] == 0))
+ printf(" (DEC OEM)");
+ printf("\n");
+ printf("Base address options : 0x300, 0x%02x0, 0x%02x0, 0x%02x0\n",
+ (int)psa[1], (int)psa[2], (int)psa[3]);
+ printf("Waitstates : %d\n",psa[7] & 0xf);
+ printf("Bus mode : %s\n",(psa[7] & 0x10) ? "EISA" : "ISA");
+ printf("IRQ : %d\n",wlirq(psa[8]));
+ }
+ }
+ printf("Default MAC address : %02x:%02x:%02x:%02x:%02x:%02x\n",
+ psa[0x10],psa[0x11],psa[0x12],psa[0x13],psa[0x14],psa[0x15]);
+ printf("Soft MAC address : %02x:%02x:%02x:%02x:%02x:%02x\n",
+ psa[0x16],psa[0x17],psa[0x18],psa[0x19],psa[0x1a],psa[0x1b]);
+ printf("Current MAC address : %s\n",(psa[0x1c] & 0x1) ? "Soft" : "Default");
+ printf("Adapter compatibility : ");
+ if (psa[0x1d] < 5) {
+ printf("%s\n",compat_type[psa[0x1d]]);
+ } else {
+ printf("unknown\n");
+ }
+ printf("Threshold preset : %d\n",psa[0x1e]);
+ printf("Call code required : %s\n",(psa[0x1f] & 0x1) ? "YES" : "NO");
+ if (psa[0x1f] & 0x1)
+ printf("Call code : 0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
+ psa[0x30],psa[0x31],psa[0x32],psa[0x33],psa[0x34],psa[0x35],psa[0x36],psa[0x37]);
+ printf("Subband : %s\n",subband[psa[0x20] & 0xf]);
+ printf("Quality threshold : %d\n",psa[0x21]);
+ printf("Hardware version : %d (%s)\n",psa[0x22],psa[0x22] ? "Rel3" : "Rel1/Rel2");
+ printf("Network ID enable : %s\n",(psa[0x25] & 0x1) ? "YES" : "NO");
+ if (psa[0x25] & 0x1) {
+ nwid = (psa[0x23] << 8) + psa[0x24];
+ printf("NWID : 0x%04x\n",nwid);
+ if (nwid != currnwid) {
+ printf("Current NWID : 0x%04x\n",currnwid);
+ }
+ }
+ printf("Datalink security : %s\n",(psa[0x26] & 0x1) ? "YES" : "NO");
+ if (psa[0x26] & 0x1) {
+ printf("Encryption key : ");
+ if (psa[0x27] == 0) {
+ printf("DENIED\n");
+ } else {
+ printf("0x%02x%02x%02x%02x%02x%02x%02x%02x\n",
+ psa[0x27],psa[0x28],psa[0x29],psa[0x2a],psa[0x2b],psa[0x2c],psa[0x2d],psa[0x2e]);
+ }
+ }
+ printf("Databus width : %d (%s)\n",
+ (psa[0x2f] & 0x1) ? 16 : 8, (psa[0x2f] & 0x80) ? "fixed" : "variable");
+ printf("Configuration state : %sconfigured\n",(psa[0x38] & 0x1) ? "" : "un");
+ printf("CRC-16 : 0x%02x%02x\n",psa[0x3e],psa[0x3d]);
+ printf("CRC status : ");
+ switch(psa[0x3f]) {
+ case 0xaa:
+ printf("OK\n");
+ break;
+ case 0x55:
+ printf("BAD\n");
+ break;
+ default:
+ printf("Error\n");
+ break;
+ }
+}
+
+
+static void
+usage()
+{
+ fprintf(stderr,"usage: wlconfig ifname [param value ...]\n");
+ exit(1);
+}
+
+
+void
+get_cache(int sd, struct ifreq *ifr)
+{
+ /* get the cache count */
+ if (ioctl(sd, SIOCGWLCITEM, (caddr_t)ifr))
+ err(1, "SIOCGWLCITEM - get cache count");
+ w_sigitems = (int) ifr->ifr_data;
+
+ ifr->ifr_data = (caddr_t) &wsc;
+ /* get the cache */
+ if (ioctl(sd, SIOCGWLCACHE, (caddr_t)ifr))
+ err(1, "SIOCGWLCACHE - get cache count");
+}
+
+static int
+scale_value(int value, int max)
+{
+ double dmax = (double) max;
+ if (value > max)
+ return(100);
+ return((value/dmax) * 100);
+}
+
+static void
+dump_cache(int rawFlag)
+{
+ int i;
+ int signal, silence, quality;
+
+ if (rawFlag)
+ printf("signal range 0..63: silence 0..63: quality 0..15\n");
+ else
+ printf("signal range 0..100: silence 0..100: quality 0..100\n");
+
+ /* after you read it, loop through structure,i.e. wsc
+ * print each item:
+ */
+ for(i = 0; i < w_sigitems; i++) {
+ printf("[%d:%d]>\n", i+1, w_sigitems);
+ printf("\tip: %d.%d.%d.%d,",((wsc[i].ipsrc >> 0) & 0xff),
+ ((wsc[i].ipsrc >> 8) & 0xff),
+ ((wsc[i].ipsrc >> 16) & 0xff),
+ ((wsc[i].ipsrc >> 24) & 0xff));
+ printf(" mac: %02x:%02x:%02x:%02x:%02x:%02x\n",
+ wsc[i].macsrc[0]&0xff,
+ wsc[i].macsrc[1]&0xff,
+ wsc[i].macsrc[2]&0xff,
+ wsc[i].macsrc[3]&0xff,
+ wsc[i].macsrc[4]&0xff,
+ wsc[i].macsrc[5]&0xff);
+ if (rawFlag) {
+ signal = wsc[i].signal;
+ silence = wsc[i].silence;
+ quality = wsc[i].quality;
+ }
+ else {
+ signal = scale_value(wsc[i].signal, 63);
+ silence = scale_value(wsc[i].silence, 63);
+ quality = scale_value(wsc[i].quality, 15);
+ }
+ printf("\tsignal: %d, silence: %d, quality: %d, ",
+ signal,
+ silence,
+ quality);
+ printf("snr: %d\n", signal - silence);
+ }
+}
+
+#define raw_cache() dump_cache(1)
+#define scale_cache() dump_cache(0)
+
+int
+main(int argc, char *argv[])
+{
+ int sd;
+ struct ifreq ifr;
+ u_char psabuf[0x40];
+ int val, argind, i;
+ char *cp, *param, *value;
+ struct ether_addr *ea;
+ int work = 0;
+ int currnwid;
+
+ if ((argc < 2) || (argc % 2))
+ usage();
+
+ /* get a socket */
+ sd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sd < 0)
+ err(1,"socket");
+ strncpy(ifr.ifr_name, argv[1], sizeof(ifr.ifr_name));
+ ifr.ifr_addr.sa_family = AF_INET;
+
+ /* get the PSA */
+ ifr.ifr_data = (caddr_t)psabuf;
+ if (ioctl(sd, SIOCGWLPSA, (caddr_t)&ifr))
+ err(1,"get PSA");
+
+ /* get the current NWID */
+ if (ioctl(sd, SIOCGWLCNWID, (caddr_t)&ifr))
+ err(1,"get NWID");
+ currnwid = (int)ifr.ifr_data;
+
+ /* just dump and exit? */
+ if (argc == 2) {
+ print_psa(psabuf, currnwid);
+ exit(0);
+ }
+
+ /* loop reading arg pairs */
+ for (argind = 2; argind < argc; argind += 2) {
+
+ param = argv[argind];
+ value = argv[argind+1];
+
+ /* What to do? */
+
+ if (!strcasecmp(param,"currnwid")) { /* set current NWID */
+ val = strtol(value,&cp,0);
+ if ((val < 0) || (val > 0xffff) || (cp == value))
+ errx(1,"bad NWID '%s'",value);
+
+ ifr.ifr_data = (caddr_t)val;
+ if (ioctl(sd, SIOCSWLCNWID, (caddr_t)&ifr))
+ err(1,"set NWID (interface not up?)");
+ continue ;
+ }
+
+ if (!strcasecmp(param,"irq")) {
+ val = strtol(value,&cp,0);
+ val = irqvals[val];
+ if ((val == 0) || (cp == value))
+ errx(1,"bad IRQ '%s'",value);
+ psabuf[WLPSA_IRQNO] = (u_char)val;
+ work = 1;
+ continue;
+ }
+
+ if (!strcasecmp(param,"mac")) {
+ if ((ea = ether_aton(value)) == NULL)
+ errx(1,"bad ethernet address '%s'",value);
+ for (i = 0; i < 6; i++)
+ psabuf[WLPSA_LOCALMAC + i] = ea->octet[i];
+ work = 1;
+ continue;
+ }
+
+ if (!strcasecmp(param,"macsel")) {
+ if (!strcasecmp(value,"local")) {
+ psabuf[WLPSA_MACSEL] |= 0x1;
+ work = 1;
+ continue;
+ }
+ if (!strcasecmp(value,"universal")) {
+ psabuf[WLPSA_MACSEL] &= ~0x1;
+ work = 1;
+ continue;
+ }
+ errx(1,"bad macsel value '%s'",value);
+ }
+
+ if (!strcasecmp(param,"nwid")) {
+ val = strtol(value,&cp,0);
+ if ((val < 0) || (val > 0xffff) || (cp == value))
+ errx(1,"bad NWID '%s'",value);
+ psabuf[WLPSA_NWID] = (val >> 8) & 0xff;
+ psabuf[WLPSA_NWID+1] = val & 0xff;
+ work = 1;
+ continue;
+ }
+ if (!strcasecmp(param,"cache")) {
+
+ /* raw cache dump
+ */
+ if (!strcasecmp(value,"raw")) {
+ get_cache(sd, &ifr);
+ raw_cache();
+ continue;
+ }
+ /* scaled cache dump
+ */
+ else if (!strcasecmp(value,"scale")) {
+ get_cache(sd, &ifr);
+ scale_cache();
+ continue;
+ }
+ /* zero out cache
+ */
+ else if (!strcasecmp(value,"zero")) {
+ if (ioctl(sd, SIOCDWLCACHE, (caddr_t)&ifr))
+ err(1,"zero cache");
+ continue;
+ }
+ errx(1,"unknown value '%s'", value);
+ }
+ errx(1,"unknown parameter '%s'",param);
+ }
+ if (work) {
+ ifr.ifr_data = (caddr_t)psabuf;
+ if (ioctl(sd, SIOCSWLPSA, (caddr_t)&ifr))
+ err(1,"set PSA");
+ }
+ return(0);
+}
diff --git a/usr.sbin/wpa/Makefile b/usr.sbin/wpa/Makefile
new file mode 100644
index 0000000..5d746e9
--- /dev/null
+++ b/usr.sbin/wpa/Makefile
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+SUBDIR= wpa_supplicant wpa_cli wpa_passphrase
+SUBDIR+= hostapd hostapd_cli
+SUBDIR+= ndis_events
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/wpa/Makefile.inc b/usr.sbin/wpa/Makefile.inc
new file mode 100644
index 0000000..2f9b9ce
--- /dev/null
+++ b/usr.sbin/wpa/Makefile.inc
@@ -0,0 +1,27 @@
+# $FreeBSD$
+
+BINDIR?= /usr/sbin
+
+WPA_DISTDIR?= ${.CURDIR}/../../../contrib/wpa/
+WPA_SUPPLICANT_DISTDIR?=${WPA_DISTDIR}/wpa_supplicant
+HOSTAPD_DISTDIR?= ${WPA_DISTDIR}/hostapd
+
+.PATH.c:${.CURDIR}/.. \
+ ${WPA_DISTDIR}/src/common \
+ ${WPA_DISTDIR}/src/crypto \
+ ${WPA_DISTDIR}/src/eap_common \
+ ${WPA_DISTDIR}/src/eapol_supp \
+ ${WPA_DISTDIR}/src/l2_packet \
+ ${WPA_DISTDIR}/src/utils
+
+CFLAGS+=-I${.CURDIR}
+CFLAGS+=-I${WPA_DISTDIR}/src
+CFLAGS+=-I${WPA_DISTDIR}/src/common
+CFLAGS+=-I${WPA_DISTDIR}/src/crypto
+CFLAGS+=-I${WPA_DISTDIR}/src/l2_packet
+CFLAGS+=-I${WPA_DISTDIR}/src/utils
+
+CFLAGS+= -DCONFIG_CTRL_IFACE
+CFLAGS+= -DCONFIG_CTRL_IFACE_UNIX
+
+.include <bsd.own.mk>
diff --git a/usr.sbin/wpa/hostapd/Makefile b/usr.sbin/wpa/hostapd/Makefile
new file mode 100644
index 0000000..06db731
--- /dev/null
+++ b/usr.sbin/wpa/hostapd/Makefile
@@ -0,0 +1,114 @@
+# $FreeBSD$
+
+.include "${.CURDIR}/../Makefile.inc"
+
+.PATH.c:${HOSTAPD_DISTDIR} \
+ ${WPA_DISTDIR}/src/eap_server \
+ ${WPA_DISTDIR}/src/radius \
+
+PROG= hostapd
+SRCS= accounting.c aes.c aes_wrap.c ap_list.c beacon.c common.c \
+ config.c ctrl_iface.c drivers.c eapol_sm.c eap.c eap_common.c \
+ eap_identity.c eap_methods.c eloop.c hostapd.c \
+ hw_features.c ieee802_11.c ieee802_11_common.c ieee802_11_auth.c \
+ ieee802_1x.c ip_addr.c md5.c mlme.c pmksa_cache.c radius.c \
+ radius_client.c rc4.c sha1.c sta_info.c vlan_init.c wme.c \
+ wpa.c wpa_auth_ie.c wpa_common.c wpa_debug.c wpabuf.c
+SRCS+= l2_packet.c driver_freebsd.c os_unix.c
+
+MAN= hostapd.8 hostapd.conf.5
+
+.if ${MK_EXAMPLES} != "no"
+FILESDIR= ${SHAREDIR}/examples/hostapd
+.PATH: ${HOSTAPD_DISTDIR}
+FILES= hostapd.conf hostapd.eap_user hostapd.wpa_psk
+.endif
+
+CFLAGS+= -I${HOSTAPD_DISTDIR}
+
+CFLAGS+= -DCONFIG_DRIVER_BSD
+CFLAGS+= -DCONFIG_DRIVER_RADIUS_ACL
+.if ${MK_INET6} != "no"
+CFLAGS+= -DCONFIG_IPV6
+.endif
+CFLAGS+= -g
+DPADD+= ${LIBPCAP}
+LDADD+= -lpcap
+
+# User customizations for wpa_supplicant/hostapd build environment
+CFLAGS+=${HOSTAPD_CFLAGS}
+#DPADD+=${HOSTAPD_DPADD}
+LDADD+=${HOSTAPD_LDADD}
+#LDFLAGS+=${HOSTAPD_LDFLAGS}
+
+.if !empty(CFLAGS:M*-DEAP_SERVER)
+#SRCS+= eap.c eap_methods.c eap_identity.c
+
+.if ${MK_OPENSSL} != "no" && !defined(RELEASE_CRUNCH)
+
+CFLAGS+=-DEAP_TLS -DEAP_PEAP -DEAP_MSCHAPv2 -DEAP_PSK \
+ -DEAP_TLS_FUNCS -DEAP_TLS_OPENSSL
+SRCS+= crypto_openssl.c
+SRCS+= eap_tls.c eap_peap.c eap_peap_common.c eap_mschapv2.c \
+ eap_psk.c eap_psk_common.c \
+ eap_tls_common.c tls_openssl.c ms_funcs.c chap.c
+
+CFLAGS+=-DEAP_TTLS -DEAP_MD5
+SRCS+= eap_ttls.c eap_md5.c
+
+.if !empty(CFLAGS:M*-DEAP_GTC)
+SRCS+= eap_gtc.c
+.endif
+
+.if !empty(CFLAGS:M*-DEAP_AKA)
+NEED_SIM_COMMON= true
+SRCS+= eap_aka.c
+.endif
+
+.if !empty(CFLAGS:M*-DEAP_SIM)
+NEED_SIM_COMMON= true
+SRCS+= eap_sim.c
+.endif
+
+.if defined(NEED_SIM_COMMON)
+SRCS+= eap_sim_common.c eap_sim_db.c
+.endif
+
+.if !empty(CFLAGS:M*-DEAP_GPSK)
+CFLAGS+=-DEAP_GPSK_SHA256
+SRCS+= eap_gpsk.c eap_gpsk_common.c
+NEED_SHA256= true
+.endif
+
+.if !empty(CFLAGS:M*-DEAP_PAX)
+SRCS+= eap_pax.c eap_pax_common.c
+.endif
+
+.if !empty(CFLAGS:M*-DEAP_SAKE)
+SRCS+= eap_sake.c eap_sake_common.c
+.endif
+
+DPADD+= ${LIBSSL} ${LIBCRYPTO}
+LDADD+= -lssl -lcrypto
+.else
+NEED_TLS_NONE= true
+.endif
+
+.else
+NEED_TLS_NONE= true
+.endif
+
+.if defined(NEED_SHA256)
+CFLAGS+=-DINTERNAL_SHA256
+SRCS+= sha256.c
+.endif
+
+.if defined(NEED_TLS_NONE)
+CFLAGS+= -DEAP_TLS_NONE
+CFLAGS+= -DINTERNAL_AES
+CFLAGS+= -DINTERNAL_SHA1
+CFLAGS+= -DINTERNAL_MD5
+SRCS+= tls_none.c
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/wpa/hostapd/driver_freebsd.c b/usr.sbin/wpa/hostapd/driver_freebsd.c
new file mode 100644
index 0000000..b418306
--- /dev/null
+++ b/usr.sbin/wpa/hostapd/driver_freebsd.c
@@ -0,0 +1,921 @@
+/*
+ * Host AP - driver interaction with BSD net80211 layer
+ * Copyright (c) 2004, Sam Leffler <sam@errno.com>
+ * Copyright (c) 2004, 2Wire, Inc
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * $FreeBSD$
+ */
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+
+#include <net80211/ieee80211_ioctl.h>
+
+#undef RSN_VERSION
+#undef WPA_VERSION
+#undef WPA_OUI_TYPE
+#undef WME_OUI_TYPE
+
+#include "hostapd.h"
+#include "driver.h"
+#include "ieee802_1x.h"
+#include "ieee802_11_auth.h"
+#include "eloop.h"
+#include "sta_info.h"
+#include "l2_packet/l2_packet.h"
+
+#include "eapol_sm.h"
+#include "wpa.h"
+#include "radius/radius.h"
+#include "ieee802_11.h"
+#include "common.h"
+#include "hostap_common.h"
+
+struct bsd_driver_data {
+ struct hostapd_data *hapd; /* back pointer */
+
+ char iface[IFNAMSIZ + 1];
+ unsigned int ifindex; /* interface index */
+ struct l2_packet_data *sock_xmit; /* raw packet xmit socket */
+ int ioctl_sock; /* socket for ioctl() use */
+ int wext_sock; /* socket for wireless events */
+};
+
+static const struct wpa_driver_ops bsd_driver_ops;
+
+static int bsd_sta_deauth(void *priv, const u8 *addr, int reason_code);
+
+static int
+set80211var(struct bsd_driver_data *drv, int op, const void *arg, int arg_len)
+{
+ struct ieee80211req ireq;
+
+ memset(&ireq, 0, sizeof(ireq));
+ strncpy(ireq.i_name, drv->iface, IFNAMSIZ);
+ ireq.i_type = op;
+ ireq.i_len = arg_len;
+ ireq.i_data = (void *) arg;
+
+ if (ioctl(drv->ioctl_sock, SIOCS80211, &ireq) < 0) {
+ perror("ioctl[SIOCS80211]");
+ return -1;
+ }
+ return 0;
+}
+
+static int
+get80211var(struct bsd_driver_data *drv, int op, void *arg, int arg_len)
+{
+ struct ieee80211req ireq;
+
+ memset(&ireq, 0, sizeof(ireq));
+ strncpy(ireq.i_name, drv->iface, IFNAMSIZ);
+ ireq.i_type = op;
+ ireq.i_len = arg_len;
+ ireq.i_data = arg;
+
+ if (ioctl(drv->ioctl_sock, SIOCG80211, &ireq) < 0) {
+ perror("ioctl[SIOCG80211]");
+ return -1;
+ }
+ return ireq.i_len;
+}
+
+static int
+set80211param(struct bsd_driver_data *drv, int op, int arg)
+{
+ struct ieee80211req ireq;
+
+ memset(&ireq, 0, sizeof(ireq));
+ strncpy(ireq.i_name, drv->iface, IFNAMSIZ);
+ ireq.i_type = op;
+ ireq.i_val = arg;
+
+ if (ioctl(drv->ioctl_sock, SIOCS80211, &ireq) < 0) {
+ perror("ioctl[SIOCS80211]");
+ return -1;
+ }
+ return 0;
+}
+
+static const char *
+ether_sprintf(const u8 *addr)
+{
+ static char buf[sizeof(MACSTR)];
+
+ if (addr != NULL)
+ snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr));
+ else
+ snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0);
+ return buf;
+}
+
+static int
+bsd_set_iface_flags(void *priv, int flags)
+{
+ struct bsd_driver_data *drv = priv;
+ struct hostapd_data *hapd = drv->hapd;
+ struct ifreq ifr;
+
+ wpa_printf(MSG_DEBUG, "%s: flags=0x%x\n", __func__, flags);
+
+ if (drv->ioctl_sock < 0)
+ return -1;
+
+ memset(&ifr, 0, sizeof(ifr));
+ snprintf(ifr.ifr_name, IFNAMSIZ, "%s", drv->iface);
+
+ if (ioctl(drv->ioctl_sock, SIOCGIFFLAGS, &ifr) != 0) {
+ perror("ioctl[SIOCGIFFLAGS]");
+ return -1;
+ }
+
+ if (flags < 0) {
+ flags = -flags;
+ if ((ifr.ifr_flags & flags) == 0)
+ return 0;
+ ifr.ifr_flags &= ~flags;
+ } else {
+ if ((ifr.ifr_flags & flags) == flags)
+ return 0;
+ ifr.ifr_flags |= flags;
+ }
+
+ if (ioctl(drv->ioctl_sock, SIOCSIFFLAGS, &ifr) != 0) {
+ perror("ioctl[SIOCSIFFLAGS]");
+ return -1;
+ }
+
+ if (flags > 0) {
+ memset(&ifr, 0, sizeof(ifr));
+ snprintf(ifr.ifr_name, IFNAMSIZ, "%s", drv->iface);
+ ifr.ifr_mtu = HOSTAPD_MTU;
+ if (ioctl(drv->ioctl_sock, SIOCSIFMTU, &ifr) != 0) {
+ perror("ioctl[SIOCSIFMTU]");
+ printf("Setting MTU failed - trying to survive with "
+ "current value\n");
+ }
+ }
+
+ return 0;
+}
+
+static int
+bsd_commit(void *priv)
+{
+ return bsd_set_iface_flags(priv, IFF_UP);
+}
+
+static int
+bsd_set_ieee8021x(const char *ifname, void *priv, int enabled)
+{
+ struct bsd_driver_data *drv = priv;
+ struct hostapd_data *hapd = drv->hapd;
+ struct hostapd_bss_config *conf = hapd->conf;
+
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d\n", __func__, enabled);
+
+ if (!enabled) {
+ /* XXX restore state */
+ return set80211param(priv, IEEE80211_IOC_AUTHMODE,
+ IEEE80211_AUTH_AUTO);
+ }
+ if (!conf->wpa && !conf->ieee802_1x) {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER,
+ HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!");
+ return -1;
+ }
+ if (conf->wpa && set80211param(drv, IEEE80211_IOC_WPA, conf->wpa)) {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER,
+ HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!");
+ return -1;
+ }
+ if (set80211param(priv, IEEE80211_IOC_AUTHMODE,
+ (conf->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) {
+ hostapd_logger(hapd, NULL, HOSTAPD_MODULE_DRIVER,
+ HOSTAPD_LEVEL_WARNING, "Error enabling WPA/802.1X!");
+ return -1;
+ }
+ return 0;
+}
+
+static int
+bsd_set_privacy(const char *ifname, void *priv, int enabled)
+{
+ struct bsd_driver_data *drv = priv;
+ struct hostapd_data *hapd = drv->hapd;
+
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d\n", __func__, enabled);
+
+ return set80211param(priv, IEEE80211_IOC_PRIVACY, enabled);
+}
+
+static int
+bsd_set_sta_authorized(void *priv, const u8 *addr, int authorized)
+{
+ struct bsd_driver_data *drv = priv;
+ struct hostapd_data *hapd = drv->hapd;
+ struct ieee80211req_mlme mlme;
+
+ wpa_printf(MSG_DEBUG, "%s: addr=%s authorized=%d\n",
+ __func__, ether_sprintf(addr), authorized);
+
+ if (authorized)
+ mlme.im_op = IEEE80211_MLME_AUTHORIZE;
+ else
+ mlme.im_op = IEEE80211_MLME_UNAUTHORIZE;
+ mlme.im_reason = 0;
+ memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+ return set80211var(priv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme));
+}
+
+static int
+bsd_sta_set_flags(void *priv, const u8 *addr, int total_flags,
+ int flags_or, int flags_and)
+{
+ /* For now, only support setting Authorized flag */
+ if (flags_or & WLAN_STA_AUTHORIZED)
+ return bsd_set_sta_authorized(priv, addr, 1);
+ if (!(flags_and & WLAN_STA_AUTHORIZED))
+ return bsd_set_sta_authorized(priv, addr, 0);
+ return 0;
+}
+
+static int
+bsd_del_key(void *priv, const unsigned char *addr, int key_idx)
+{
+ struct bsd_driver_data *drv = priv;
+ struct hostapd_data *hapd = drv->hapd;
+ struct ieee80211req_del_key wk;
+
+ wpa_printf(MSG_DEBUG, "%s: addr=%s key_idx=%d\n",
+ __func__, ether_sprintf(addr), key_idx);
+
+ memset(&wk, 0, sizeof(wk));
+ if (addr != NULL) {
+ memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN);
+ wk.idk_keyix = (u_int8_t) IEEE80211_KEYIX_NONE; /* XXX */
+ } else {
+ wk.idk_keyix = key_idx;
+ }
+
+ return set80211var(priv, IEEE80211_IOC_DELKEY, &wk, sizeof(wk));
+}
+
+static int
+bsd_set_key(const char *ifname, void *priv, const char *alg,
+ const u8 *addr, int key_idx,
+ const u8 *key, size_t key_len, int txkey)
+{
+ struct bsd_driver_data *drv = priv;
+ struct hostapd_data *hapd = drv->hapd;
+ struct ieee80211req_key wk;
+ u_int8_t cipher;
+
+ if (strcmp(alg, "none") == 0)
+ return bsd_del_key(priv, addr, key_idx);
+
+ wpa_printf(MSG_DEBUG, "%s: alg=%s addr=%s key_idx=%d\n",
+ __func__, alg, ether_sprintf(addr), key_idx);
+
+ if (strcmp(alg, "WEP") == 0)
+ cipher = IEEE80211_CIPHER_WEP;
+ else if (strcmp(alg, "TKIP") == 0)
+ cipher = IEEE80211_CIPHER_TKIP;
+ else if (strcmp(alg, "CCMP") == 0)
+ cipher = IEEE80211_CIPHER_AES_CCM;
+ else {
+ printf("%s: unknown/unsupported algorithm %s\n",
+ __func__, alg);
+ return -1;
+ }
+
+ if (key_len > sizeof(wk.ik_keydata)) {
+ printf("%s: key length %d too big\n", __func__, key_len);
+ return -3;
+ }
+
+ memset(&wk, 0, sizeof(wk));
+ wk.ik_type = cipher;
+ if (addr == NULL) {
+ memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
+ wk.ik_keyix = key_idx;
+ wk.ik_flags = IEEE80211_KEY_XMIT
+ | IEEE80211_KEY_GROUP
+ | IEEE80211_KEY_DEFAULT;
+ } else {
+ memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
+ wk.ik_keyix = IEEE80211_KEYIX_NONE;
+ wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT;
+ }
+ wk.ik_keylen = key_len;
+ memcpy(wk.ik_keydata, key, key_len);
+
+ return set80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk));
+}
+
+
+static int
+bsd_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
+ u8 *seq)
+{
+ struct bsd_driver_data *drv = priv;
+ struct hostapd_data *hapd = drv->hapd;
+ struct ieee80211req_key wk;
+
+ wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d\n",
+ __func__, ether_sprintf(addr), idx);
+
+ memset(&wk, 0, sizeof(wk));
+ if (addr == NULL)
+ memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN);
+ else
+ memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
+ wk.ik_keyix = idx;
+
+ if (get80211var(drv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)) < 0) {
+ printf("Failed to get encryption.\n");
+ return -1;
+ } else {
+ /* NB: upper layer expects tsc in network order */
+ wk.ik_keytsc = htole64(wk.ik_keytsc);
+ memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc));
+ return 0;
+ }
+}
+
+
+static int
+bsd_flush(void *priv)
+{
+ u8 allsta[IEEE80211_ADDR_LEN];
+
+ memset(allsta, 0xff, IEEE80211_ADDR_LEN);
+ return bsd_sta_deauth(priv, allsta, IEEE80211_REASON_AUTH_LEAVE);
+}
+
+
+static int
+bsd_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data,
+ const u8 *addr)
+{
+ struct bsd_driver_data *drv = priv;
+ struct ieee80211req_sta_stats stats;
+
+ memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN);
+ if (get80211var(drv, IEEE80211_IOC_STA_STATS, &stats, sizeof(stats)) > 0) {
+ /* XXX? do packets counts include non-data frames? */
+ data->rx_packets = stats.is_stats.ns_rx_data;
+ data->rx_bytes = stats.is_stats.ns_rx_bytes;
+ data->tx_packets = stats.is_stats.ns_tx_data;
+ data->tx_bytes = stats.is_stats.ns_tx_bytes;
+ }
+ return 0;
+}
+
+static int
+bsd_sta_clear_stats(void *priv, const u8 *addr)
+{
+ struct bsd_driver_data *drv = priv;
+ struct hostapd_data *hapd = drv->hapd;
+ struct ieee80211req_sta_stats stats;
+
+ wpa_printf(MSG_DEBUG, "%s: addr=%s\n", __func__, ether_sprintf(addr));
+
+ /* zero station statistics */
+ memset(&stats, 0, sizeof(stats));
+ memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN);
+ return set80211var(drv, IEEE80211_IOC_STA_STATS, &stats, sizeof(stats));
+}
+
+static int
+bsd_set_opt_ie(const char *ifname, void *priv, const u8 *ie, size_t ie_len)
+{
+ struct bsd_driver_data *drv = priv;
+ struct hostapd_data *hapd = drv->hapd;
+ struct ieee80211req ireq;
+
+ memset(&ireq, 0, sizeof(ireq));
+ strncpy(ireq.i_name, drv->iface, IFNAMSIZ);
+ ireq.i_type = IEEE80211_IOC_APPIE;
+ ireq.i_val = IEEE80211_APPIE_WPA;
+ ireq.i_data = (void *) ie;
+ ireq.i_len = ie_len;
+
+ wpa_printf(MSG_DEBUG, "%s: set WPA+RSN ie (len %d)\n",
+ __func__, ie_len);
+ if (ioctl(drv->ioctl_sock, SIOCS80211, &ireq) < 0) {
+ printf("Unable to set WPA+RSN ie\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int
+bsd_sta_deauth(void *priv, const u8 *addr, int reason_code)
+{
+ struct bsd_driver_data *drv = priv;
+ struct hostapd_data *hapd = drv->hapd;
+ struct ieee80211req_mlme mlme;
+
+ wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d\n",
+ __func__, ether_sprintf(addr), reason_code);
+
+ mlme.im_op = IEEE80211_MLME_DEAUTH;
+ mlme.im_reason = reason_code;
+ memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+ return set80211var(priv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme));
+}
+
+static int
+bsd_sta_disassoc(void *priv, const u8 *addr, int reason_code)
+{
+ struct bsd_driver_data *drv = priv;
+ struct hostapd_data *hapd = drv->hapd;
+ struct ieee80211req_mlme mlme;
+
+ wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d\n",
+ __func__, ether_sprintf(addr), reason_code);
+
+ mlme.im_reason = reason_code;
+ memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+ return set80211var(priv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme));
+}
+
+static int
+bsd_del_sta(struct bsd_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
+{
+ struct hostapd_data *hapd = drv->hapd;
+ struct hostapd_bss_config *conf = hapd->conf;
+ struct sta_info *sta;
+
+ hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "deassociated");
+
+ sta = ap_get_sta(hapd, addr);
+ if (sta != NULL) {
+ sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+ if (conf->wpa)
+ wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC);
+ sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST;
+ ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
+ ap_free_sta(hapd, sta);
+ }
+ return 0;
+}
+
+static int
+bsd_new_sta(struct bsd_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
+{
+ struct hostapd_data *hapd = drv->hapd;
+ struct hostapd_bss_config *conf = hapd->conf;
+ struct sta_info *sta;
+ struct ieee80211req_wpaie ie;
+ int new_assoc, ielen, res;
+
+ hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO, "associated");
+
+ sta = ap_sta_add(hapd, addr);
+ if (sta == NULL)
+ return -1;
+ /*
+ * Fetch and validate any negotiated WPA/RSN parameters.
+ */
+ if (conf->wpa) {
+ memset(&ie, 0, sizeof(ie));
+ memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN);
+ if (get80211var(drv, IEEE80211_IOC_WPAIE, &ie, sizeof(ie)) < 0) {
+ printf("Failed to get WPA/RSN information element.\n");
+ return -1; /* XXX not right */
+ }
+ if (ie.wpa_ie[1] == 0) {
+ printf("No WPA/RSN information element for station!\n");
+ return -1; /* XXX not right */
+ }
+ if (sta->wpa_sm == NULL)
+ sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+ sta->addr);
+ if (sta->wpa_sm == NULL) {
+ printf("Failed to initialize WPA state machine\n");
+ return -1;
+ }
+ ielen = 2 + ie.wpa_ie[1];
+ res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
+ ie.wpa_ie, ielen, NULL, 0);
+ if (res != WPA_IE_OK) {
+ printf("WPA/RSN information element rejected? "
+ "(res %u)\n", res);
+ return -1;
+ }
+ }
+
+ /*
+ * Now that the internal station state is setup
+ * kick the authenticator into action.
+ */
+ new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
+ sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
+ wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC);
+ hostapd_new_assoc_sta(hapd, sta, !new_assoc);
+ ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
+
+ return 0;
+}
+
+#include <net/route.h>
+#include <net80211/ieee80211_freebsd.h>
+
+static void
+bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx)
+{
+ struct bsd_driver_data *drv = ctx;
+ struct hostapd_data *hapd = drv->hapd;
+ char buf[2048];
+ struct if_announcemsghdr *ifan;
+ struct rt_msghdr *rtm;
+ struct ieee80211_michael_event *mic;
+ struct ieee80211_join_event *join;
+ struct ieee80211_leave_event *leave;
+#ifdef CONFIG_DRIVER_RADIUS_ACL
+ struct ieee80211_auth_event *auth;
+#endif
+ int n;
+
+ n = read(sock, buf, sizeof(buf));
+ if (n < 0) {
+ if (errno != EINTR && errno != EAGAIN)
+ perror("read(PF_ROUTE)");
+ return;
+ }
+
+ rtm = (struct rt_msghdr *) buf;
+ if (rtm->rtm_version != RTM_VERSION) {
+ wpa_printf(MSG_DEBUG, "Routing message version %d not "
+ "understood\n", rtm->rtm_version);
+ return;
+ }
+ ifan = (struct if_announcemsghdr *) rtm;
+ if (ifan->ifan_index != drv->ifindex) {
+ wpa_printf(MSG_DEBUG, "Discard routing message to if#%d "
+ "(not for us %d)\n",
+ ifan->ifan_index, drv->ifindex);
+ return;
+ }
+ switch (rtm->rtm_type) {
+ case RTM_IEEE80211:
+ switch (ifan->ifan_what) {
+ case RTM_IEEE80211_ASSOC:
+ case RTM_IEEE80211_REASSOC:
+ case RTM_IEEE80211_DISASSOC:
+ case RTM_IEEE80211_SCAN:
+ break;
+ case RTM_IEEE80211_LEAVE:
+ leave = (struct ieee80211_leave_event *) &ifan[1];
+ bsd_del_sta(drv, leave->iev_addr);
+ break;
+ case RTM_IEEE80211_JOIN:
+#ifdef RTM_IEEE80211_REJOIN
+ case RTM_IEEE80211_REJOIN:
+#endif
+ join = (struct ieee80211_join_event *) &ifan[1];
+ bsd_new_sta(drv, join->iev_addr);
+ break;
+ case RTM_IEEE80211_REPLAY:
+ /* ignore */
+ break;
+ case RTM_IEEE80211_MICHAEL:
+ mic = (struct ieee80211_michael_event *) &ifan[1];
+ wpa_printf(MSG_DEBUG,
+ "Michael MIC failure wireless event: "
+ "keyix=%u src_addr=" MACSTR, mic->iev_keyix,
+ MAC2STR(mic->iev_src));
+ ieee80211_michael_mic_failure(hapd, mic->iev_src, 1);
+ break;
+#ifdef CONFIG_DRIVER_RADIUS_ACL
+ case RTM_IEEE80211_AUTH:
+ auth = (struct ieee80211_auth_event *) &ifan[1];
+ wpa_printf(MSG_DEBUG, "802.11 AUTH, STA = " MACSTR,
+ MAC2STR(auth->iev_addr));
+ n = hostapd_allowed_address(hapd, auth->iev_addr,
+ NULL, 0, NULL, NULL, NULL);
+ switch (n) {
+ case HOSTAPD_ACL_ACCEPT:
+ case HOSTAPD_ACL_REJECT:
+ hostapd_set_radius_acl_auth(hapd,
+ auth->iev_addr, n, 0);
+ wpa_printf(MSG_DEBUG,
+ "802.11 AUTH, STA = " MACSTR " hostapd says: %s",
+ MAC2STR(auth->iev_addr),
+ (n == HOSTAPD_ACL_ACCEPT ?
+ "ACCEPT" : "REJECT" ));
+ break;
+ case HOSTAPD_ACL_PENDING:
+ wpa_printf(MSG_DEBUG,
+ "802.11 AUTH, STA = " MACSTR " pending",
+ MAC2STR(auth->iev_addr));
+ break;
+ }
+ break;
+#endif /* CONFIG_DRIVER_RADIUS_ACL */
+ }
+ break;
+ }
+}
+
+static int
+bsd_wireless_event_init(void *priv)
+{
+ struct bsd_driver_data *drv = priv;
+ int s;
+
+ drv->wext_sock = -1;
+
+ s = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (s < 0) {
+ perror("socket(PF_ROUTE,SOCK_RAW)");
+ return -1;
+ }
+ eloop_register_read_sock(s, bsd_wireless_event_receive, drv, NULL);
+ drv->wext_sock = s;
+
+ return 0;
+}
+
+static void
+bsd_wireless_event_deinit(void *priv)
+{
+ struct bsd_driver_data *drv = priv;
+
+ if (drv != NULL) {
+ if (drv->wext_sock < 0)
+ return;
+ eloop_unregister_read_sock(drv->wext_sock);
+ close(drv->wext_sock);
+ }
+}
+
+
+static int
+bsd_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len,
+ int encrypt, const u8 *own_addr)
+{
+ struct bsd_driver_data *drv = priv;
+ struct hostapd_data *hapd = drv->hapd;
+ unsigned char buf[3000];
+ unsigned char *bp = buf;
+ struct l2_ethhdr *eth;
+ size_t len;
+ int status;
+
+ /*
+ * Prepend the Etherent header. If the caller left us
+ * space at the front we could just insert it but since
+ * we don't know we copy to a local buffer. Given the frequency
+ * and size of frames this probably doesn't matter.
+ */
+ len = data_len + sizeof(struct l2_ethhdr);
+ if (len > sizeof(buf)) {
+ bp = malloc(len);
+ if (bp == NULL) {
+ printf("EAPOL frame discarded, cannot malloc temp "
+ "buffer of size %u!\n", len);
+ return -1;
+ }
+ }
+ eth = (struct l2_ethhdr *) bp;
+ memcpy(eth->h_dest, addr, ETH_ALEN);
+ memcpy(eth->h_source, own_addr, ETH_ALEN);
+ eth->h_proto = htons(ETH_P_EAPOL);
+ memcpy(eth+1, data, data_len);
+
+ wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", bp, len);
+
+ status = l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, bp, len);
+
+ if (bp != buf)
+ free(bp);
+ return status;
+}
+
+static void
+handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
+{
+ struct bsd_driver_data *drv = ctx;
+ struct hostapd_data *hapd = drv->hapd;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, src_addr);
+ if (!sta || !(sta->flags & WLAN_STA_ASSOC)) {
+ printf("Data frame from not associated STA %s\n",
+ ether_sprintf(src_addr));
+ /* XXX cannot happen */
+ return;
+ }
+ ieee802_1x_receive(hapd, src_addr, buf + sizeof(struct l2_ethhdr),
+ len - sizeof(struct l2_ethhdr));
+}
+
+static int
+bsd_get_ssid(const char *ifname, void *priv, u8 *buf, int len)
+{
+ struct bsd_driver_data *drv = priv;
+ struct hostapd_data *hapd = drv->hapd;
+ int ssid_len = get80211var(priv, IEEE80211_IOC_SSID, buf, len);
+
+ wpa_printf(MSG_DEBUG, "%s: ssid=\"%.*s\"\n", __func__, ssid_len, buf);
+
+ return ssid_len;
+}
+
+static int
+bsd_set_ssid(const char *ifname, void *priv, const u8 *buf, int len)
+{
+ struct bsd_driver_data *drv = priv;
+ struct hostapd_data *hapd = drv->hapd;
+
+ wpa_printf(MSG_DEBUG, "%s: ssid=\"%.*s\"\n", __func__, len, buf);
+
+ return set80211var(priv, IEEE80211_IOC_SSID, buf, len);
+}
+
+static int
+bsd_set_countermeasures(void *priv, int enabled)
+{
+ struct bsd_driver_data *drv = priv;
+
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
+ return set80211param(drv, IEEE80211_IOC_COUNTERMEASURES, enabled);
+}
+
+#ifdef CONFIG_DRIVER_RADIUS_ACL
+static int
+bsd_set_radius_acl_auth(void *priv, const u8 *mac, int accepted,
+ u32 session_timeout)
+{
+ struct bsd_driver_data *drv = priv;
+ struct hostapd_data *hapd = drv->hapd;
+ struct ieee80211req_mlme mlme;
+
+ switch (accepted) {
+ case HOSTAPD_ACL_ACCEPT_TIMEOUT:
+ wpa_printf(MSG_DEBUG, "[%s] STA " MACSTR
+ " has been accepted by RADIUS ACL with timeout "
+ "of %d.\n", hapd->conf->iface, MAC2STR(mac),
+ session_timeout);
+ mlme.im_reason = IEEE80211_STATUS_SUCCESS;
+ break;
+ case HOSTAPD_ACL_ACCEPT:
+ wpa_printf(MSG_DEBUG, "[%s] STA " MACSTR
+ " has been accepted by RADIUS ACL.\n",
+ hapd->conf->iface, MAC2STR(mac));
+ mlme.im_reason = IEEE80211_STATUS_SUCCESS;
+ break;
+ case HOSTAPD_ACL_REJECT:
+ wpa_printf(MSG_DEBUG, "[%s] STA " MACSTR
+ " has been rejected by RADIUS ACL.\n",
+ hapd->conf->iface, MAC2STR(mac));
+ mlme.im_reason = IEEE80211_STATUS_UNSPECIFIED;
+ break;
+ default:
+ wpa_printf(MSG_ERROR, "[%s] STA " MACSTR
+ " has unknown status (%d) by RADIUS ACL. "
+ "Nothing to do...\n", hapd->conf->iface,
+ MAC2STR(mac), accepted);
+ return 0;
+ }
+ memset(&mlme, 0, sizeof(mlme));
+ mlme.im_op = IEEE80211_MLME_AUTH;
+ memcpy(mlme.im_macaddr, mac, IEEE80211_ADDR_LEN);
+ return set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme));
+}
+
+static int
+bsd_set_radius_acl_expire(void *priv, const u8 *mac)
+{
+ struct bsd_driver_data *drv = priv;
+ struct hostapd_data *hapd = drv->hapd;
+
+ /*
+ * The expiry of the MAC address from RADIUS ACL cache doesn't mean
+ * that we should kick off the client. Our current approach doesn't
+ * require adding/removing entries from an allow/deny list; so this
+ * function is likely unecessary
+ */
+ wpa_printf(MSG_DEBUG, "[%s] STA " MACSTR " radius acl cache "
+ "expired; nothing to do...", hapd->conf->iface,
+ MAC2STR(mac));
+ return 0;
+}
+#endif /* CONFIG_DRIVER_RADIUS_ACL */
+
+static void *
+bsd_init(struct hostapd_data *hapd)
+{
+ struct bsd_driver_data *drv;
+
+ drv = malloc(sizeof(struct bsd_driver_data));
+ if (drv == NULL) {
+ printf("Could not allocate memory for bsd driver data\n");
+ goto bad;
+ }
+
+ memset(drv, 0, sizeof(*drv));
+ drv->hapd = hapd;
+ drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (drv->ioctl_sock < 0) {
+ perror("socket[PF_INET,SOCK_DGRAM]");
+ goto bad;
+ }
+ memcpy(drv->iface, hapd->conf->iface, sizeof(drv->iface));
+ /*
+ * NB: We require the interface name be mappable to an index.
+ * This implies we do not support having wpa_supplicant
+ * wait for an interface to appear. This seems ok; that
+ * doesn't belong here; it's really the job of devd.
+ * XXXSCW: devd is FreeBSD-specific.
+ */
+ drv->ifindex = if_nametoindex(drv->iface);
+ if (drv->ifindex == 0) {
+ printf("%s: interface %s does not exist", __func__, drv->iface);
+ goto bad;
+ }
+
+ drv->sock_xmit = l2_packet_init(drv->iface, NULL, ETH_P_EAPOL,
+ handle_read, drv, 1);
+ if (drv->sock_xmit == NULL)
+ goto bad;
+ if (l2_packet_get_own_addr(drv->sock_xmit, hapd->own_addr))
+ goto bad;
+
+ bsd_set_iface_flags(drv, -IFF_UP); /* mark down during setup */
+
+ return drv;
+bad:
+ if (drv != NULL) {
+ if (drv->sock_xmit != NULL)
+ l2_packet_deinit(drv->sock_xmit);
+ if (drv->ioctl_sock >= 0)
+ close(drv->ioctl_sock);
+ free(drv);
+ }
+ return NULL;
+}
+
+
+static void
+bsd_deinit(void *priv)
+{
+ struct bsd_driver_data *drv = priv;
+
+ (void) bsd_set_iface_flags(drv, -IFF_UP);
+ if (drv->ioctl_sock >= 0)
+ close(drv->ioctl_sock);
+ if (drv->sock_xmit != NULL)
+ l2_packet_deinit(drv->sock_xmit);
+ free(drv);
+}
+
+const struct wpa_driver_ops wpa_driver_bsd_ops = {
+ .name = "bsd",
+ .init = bsd_init,
+ .deinit = bsd_deinit,
+ .set_ieee8021x = bsd_set_ieee8021x,
+ .set_privacy = bsd_set_privacy,
+ .set_encryption = bsd_set_key,
+ .get_seqnum = bsd_get_seqnum,
+ .flush = bsd_flush,
+ .set_generic_elem = bsd_set_opt_ie,
+ .wireless_event_init = bsd_wireless_event_init,
+ .wireless_event_deinit = bsd_wireless_event_deinit,
+ .sta_set_flags = bsd_sta_set_flags,
+ .read_sta_data = bsd_read_sta_driver_data,
+ .send_eapol = bsd_send_eapol,
+ .sta_disassoc = bsd_sta_disassoc,
+ .sta_deauth = bsd_sta_deauth,
+ .set_ssid = bsd_set_ssid,
+ .get_ssid = bsd_get_ssid,
+ .set_countermeasures = bsd_set_countermeasures,
+ .sta_clear_stats = bsd_sta_clear_stats,
+ .commit = bsd_commit,
+#ifdef CONFIG_DRIVER_RADIUS_ACL
+ .set_radius_acl_auth = bsd_set_radius_acl_auth,
+ .set_radius_acl_expire = bsd_set_radius_acl_expire,
+#endif
+};
diff --git a/usr.sbin/wpa/hostapd/hostapd.8 b/usr.sbin/wpa/hostapd/hostapd.8
new file mode 100644
index 0000000..5cb5e1c
--- /dev/null
+++ b/usr.sbin/wpa/hostapd/hostapd.8
@@ -0,0 +1,134 @@
+.\" Copyright (c) 2005 Sam Leffler <sam@errno.com>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd October 26, 2007
+.Dt HOSTAPD 8
+.Os
+.Sh NAME
+.Nm hostapd
+.Nd "authenticator for IEEE 802.11 networks"
+.Sh SYNOPSIS
+.Nm
+.Op Fl BdhKtv
+.Op Fl P Ar pidfile
+.Ar config-file ...
+.Sh DESCRIPTION
+The
+.Nm
+utility
+is an authenticator for IEEE 802.11 networks.
+It provides full support for WPA/IEEE 802.11i and
+can also act as an IEEE 802.1X Authenticator with a suitable
+backend Authentication Server (typically
+.Tn FreeRADIUS ) .
+The
+.Nm
+utility
+implements the authentication protocols that piggyback on top
+of the normal IEEE 802.11 protocol mechanisms.
+To use
+.Nm
+as an authenticator, the underlying device must support some
+basic functionality such as the ability to set security information
+in the 802.11 management frames.
+Beware that not all devices have this support.
+.Pp
+The
+.Nm
+utility
+is designed to be a
+.Dq daemon
+program that runs in the
+background and acts as the backend component controlling
+the wireless connection.
+It supports separate frontend programs such as the
+text-based frontend,
+.Xr hostapd_cli 8 .
+.Pp
+The following arguments must be specified on the command line:
+.Bl -tag -width indent
+.It Ar config-file
+Use the settings in the specified configuration file; the name of
+the specified wireless interface is contained in this file.
+See
+.Xr hostapd.conf 5
+for a description of the configuration file syntax.
+.Pp
+Changes to the configuration file can be reloaded by sending a
+.Dv SIGHUP
+to the
+.Nm
+processor or with the
+.Xr hostapd_cli 8
+utility, using
+.Dq Li "hostapd_cli reconfigure" .
+.El
+.Sh OPTIONS
+The options are as follows:
+.Bl -tag -width indent
+.It Fl d
+Enable debugging messages.
+If this option is supplied twice, more verbose messages are displayed.
+.It Fl h
+Show help text.
+.It Fl t
+Include timestamps in debugging output.
+.It Fl v
+Display version information on the terminal and exit.
+.It Fl B
+Detach from the controlling terminal and run as a daemon process
+in the background.
+.It Fl K
+Include key information in debugging output.
+.It Fl P Ar pidfile
+Store PID in
+.Ar pidfile .
+.El
+.Sh SEE ALSO
+.Xr ath 4 ,
+.Xr ipw 4 ,
+.Xr iwi 4 ,
+.Xr ral 4 ,
+.Xr ural 4 ,
+.Xr wi 4 ,
+.Xr hostapd.conf 5 ,
+.Xr hostapd_cli 8 ,
+.Xr ifconfig 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 6.0 .
+.Sh AUTHORS
+The
+.Nm
+utility was written by
+.An Jouni Malinen Aq jkmaline@cc.hut.fi .
+This manual page is derived from the
+.Pa README
+file included in the
+.Nm
+distribution.
diff --git a/usr.sbin/wpa/hostapd/hostapd.conf.5 b/usr.sbin/wpa/hostapd/hostapd.conf.5
new file mode 100644
index 0000000..96c8ad3
--- /dev/null
+++ b/usr.sbin/wpa/hostapd/hostapd.conf.5
@@ -0,0 +1,210 @@
+.\" Copyright (c) 2005 Sam Leffler <sam@errno.com>
+.\" Copyright (c) 2006 Rui Paulo
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 2, 2006
+.Dt HOSTAPD.CONF 5
+.Os
+.Sh NAME
+.Nm hostapd.conf
+.Nd configuration file for
+.Xr hostapd 8
+utility
+.Sh DESCRIPTION
+The
+.Xr hostapd 8
+utility
+is an authenticator for IEEE 802.11 networks.
+It provides full support for WPA/IEEE 802.11i and
+can also act as an IEEE 802.1X Authenticator with a suitable
+backend Authentication Server (typically
+.Tn FreeRADIUS ) .
+.Pp
+The configuration file consists of global parameters and domain
+specific configuration:
+.Bl -bullet -offset indent -compact
+.It
+IEEE 802.1X-2004
+.\" XXX not yet
+.\" .It
+.\" Integrated EAP server
+.\" .It
+.\" IEEE 802.11f - Inter-Access Point Protocol (IAPP)
+.It
+RADIUS client
+.It
+RADIUS authentication server
+.It
+WPA/IEEE 802.11i
+.El
+.Sh GLOBAL PARAMETERS
+The following parameters are recognized:
+.Bl -tag -width indent
+.It Va interface
+Interface name.
+Should be set in
+.Dq hostap
+mode.
+.It Va debug
+Debugging mode: 0 = no, 1 = minimal, 2 = verbose, 3 = msg dumps, 4 =
+excessive.
+.It Va dump_file
+Dump file for state information (on
+.Dv SIGUSR1 ) .
+.It Va ctrl_interface
+The pathname of the directory in which
+.Xr hostapd 8
+creates
+.Ux
+domain socket files for communication
+with frontend programs such as
+.Xr hostapd_cli 8 .
+.It Va ctrl_interface_group
+A group name or group ID to use in setting protection on the
+control interface file.
+This can be set to allow non-root users to access the
+control interface files.
+If no group is specified, the group ID of the control interface
+is not modified and will, typically, be the
+group ID of the directory in which the socket is created.
+.El
+.Sh IEEE 802.1X-2004 PARAMETERS
+The following parameters are recognized:
+.Bl -tag -width indent
+.It Va ieee8021x
+Require IEEE 802.1X authorization.
+.It Va eap_message
+Optional displayable message sent with EAP Request-Identity.
+.It Va wep_key_len_broadcast
+Key lengths for broadcast keys.
+.It Va wep_key_len_unicast
+Key lengths for unicast keys.
+.It Va wep_rekey_period
+Rekeying period in seconds.
+.It Va eapol_key_index_workaround
+EAPOL-Key index workaround (set bit7) for WinXP Supplicant.
+.It Va eap_reauth_period
+EAP reauthentication period in seconds.
+To disable reauthentication,
+use
+.Dq 0 .
+.\" XXX not yet
+.\" .It Va use_pae_group_addr
+.El
+.\" XXX not yet
+.\" .Sh IEEE 802.11f - IAPP PARAMETERS
+.\" The following parameters are recognized:
+.\" .Bl -tag -width indent
+.\" .It Va iapp_interface
+.\" Interface to be used for IAPP broadcast packets
+.\" .El
+.Sh RADIUS CLIENT PARAMETERS
+The following parameters are recognized:
+.Bl -tag -width indent
+.It Va own_ip_addr
+The own IP address of the access point (used as NAS-IP-Address).
+.It Va nas_identifier
+Optional NAS-Identifier string for RADIUS messages.
+.It Va auth_server_addr , auth_server_port , auth_server_shared_secret
+RADIUS authentication server parameters.
+Can be defined twice for secondary servers to be used if primary one
+does not reply to RADIUS packets.
+.It Va acct_server_addr , acct_server_port , acct_server_shared_secret
+RADIUS accounting server parameters.
+Can be defined twice for secondary servers to be used if primary one
+does not reply to RADIUS packets.
+.It Va radius_retry_primary_interval
+Retry interval for trying to return to the primary RADIUS server (in
+seconds).
+.It Va radius_acct_interim_interval
+Interim accounting update interval.
+If this is set (larger than 0) and acct_server is configured,
+.Xr hostapd 8
+will send interim accounting updates every N seconds.
+.El
+.Sh RADIUS AUTHENTICATION SERVER PARAMETERS
+The following parameters are recognized:
+.Bl -tag -width indent
+.It Va radius_server_clients
+File name of the RADIUS clients configuration for the RADIUS server.
+If this is commented out, RADIUS server is disabled.
+.It Va radius_server_auth_port
+The UDP port number for the RADIUS authentication server.
+.It Va radius_server_ipv6
+Use IPv6 with RADIUS server.
+.El
+.Sh WPA/IEEE 802.11i PARAMETERS
+The following parameters are recognized:
+.Bl -tag -width indent
+.It Va wpa
+Enable WPA.
+Setting this variable configures the AP to require WPA (either
+WPA-PSK or WPA-RADIUS/EAP based on other configuration).
+.It Va wpa_psk , wpa_passphrase
+WPA pre-shared keys for WPA-PSK.
+This can be either entered as a 256-bit secret in hex format (64 hex
+digits), wpa_psk, or as an ASCII passphrase (8..63 characters) that
+will be converted to PSK.
+This conversion uses SSID so the PSK changes when ASCII passphrase is
+used and the SSID is changed.
+.It Va wpa_psk_file
+Optionally, WPA PSKs can be read from a separate text file (containing a
+list of (PSK,MAC address) pairs.
+.It Va wpa_key_mgmt
+Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both).
+.It Va wpa_pairwise
+Set of accepted cipher suites (encryption algorithms) for pairwise keys
+(unicast packets).
+See the example file for more information.
+.It Va wpa_group_rekey
+Time interval for rekeying GTK (broadcast/multicast encryption keys) in
+seconds.
+.It Va wpa_strict_rekey
+Rekey GTK when any STA that possesses the current GTK is leaving the
+BSS.
+.It Va wpa_gmk_rekey
+Time interval for rekeying GMK (master key used internally to generate GTKs),
+in seconds.
+.El
+.Sh SEE ALSO
+.Xr hostapd 8 ,
+.Xr hostapd_cli 8
+.Sh HISTORY
+The
+.Nm
+manual page and
+.Xr hostapd 8
+functionality first appeared in
+.Fx 6.0 .
+.Sh AUTHORS
+This manual page is derived from the
+.Pa README
+and
+.Pa hostapd.conf
+files in the
+.Nm hostapd
+distribution provided by
+.An Jouni Malinen Aq jkmaline@cc.hut.fi .
diff --git a/usr.sbin/wpa/hostapd_cli/Makefile b/usr.sbin/wpa/hostapd_cli/Makefile
new file mode 100644
index 0000000..667134c
--- /dev/null
+++ b/usr.sbin/wpa/hostapd_cli/Makefile
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+.include "${.CURDIR}/../Makefile.inc"
+
+.PATH.c:${HOSTAPD_DISTDIR}
+
+PROG= hostapd_cli
+SRCS= hostapd_cli.c wpa_ctrl.c os_unix.c
+
+CFLAGS+= -DCONFIG_CTRL_IFACE
+CFLAGS+= -DCONFIG_CTRL_IFACE_UNIX
+
+MAN= hostapd_cli.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/wpa/hostapd_cli/hostapd_cli.8 b/usr.sbin/wpa/hostapd_cli/hostapd_cli.8
new file mode 100644
index 0000000..4362f9f
--- /dev/null
+++ b/usr.sbin/wpa/hostapd_cli/hostapd_cli.8
@@ -0,0 +1,112 @@
+.\" Copyright (c) 2005 Sam Leffler <sam@errno.com>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd June 16, 2005
+.Dt HOSTAPD_CLI 8
+.Os
+.Sh NAME
+.Nm hostapd_cli
+.Nd text-based frontend program for interacting with
+.Xr hostapd 8
+.Sh SYNOPSIS
+.Nm
+.Op Ar commands
+.Sh DESCRIPTION
+The
+.Nm
+utility
+is a text-based frontend program for interacting with
+.Xr hostapd 8 .
+It is used to query the current status.
+.Pp
+The
+.Nm
+utility
+can show the
+current authentication status,
+dot11 and dot1x MIBs, etc.
+.Pp
+The
+.Nm
+utility
+supports two modes: interactive and command line.
+Both modes share the same command set.
+.Pp
+Interactive mode is started when
+.Nm
+is executed without any parameters on the command line.
+Commands are then entered from the controlling terminal in
+response to the
+.Nm
+prompt.
+In command line mode, the same commands are
+entered as command line arguments.
+.Sh COMMANDS
+The following commands may be supplied on the command line
+or at a prompt when operating interactively.
+.Bl -tag -width indent
+.It Ic mib
+Report MIB variables (dot1x, dot11) for the current interface.
+.It Ic sta Ar addr
+Report the MIB variables for the associated station with MAC address
+.Ar addr .
+.It Ic all_sta
+Report the MIB variables for all associated stations.
+.It Ic help
+Show usage help.
+.It Ic interface Op Ar ifname
+Show available interfaces and/or set the current interface
+when multiple are available.
+.It Ic level Ar debug_level
+Change the debugging level in
+.Xr hostapd 8 .
+Larger numbers generate more messages.
+.It Ic license
+Display the full
+license for
+.Nm .
+.It Ic quit
+Exit
+.Nm .
+.El
+.Sh SEE ALSO
+.Xr hostapd.conf 5 ,
+.Xr hostapd 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 6.0 .
+.Sh AUTHORS
+The
+.Nm
+utility was written by
+.An Jouni Malinen Aq jkmaline@cc.hut.fi .
+This manual page is derived from the
+.Pa README
+file included in the
+.Nm hostapd
+distribution.
diff --git a/usr.sbin/wpa/l2_packet.c b/usr.sbin/wpa/l2_packet.c
new file mode 100644
index 0000000..6c4c6b3
--- /dev/null
+++ b/usr.sbin/wpa/l2_packet.c
@@ -0,0 +1,297 @@
+/*
+ * WPA Supplicant - Layer2 packet handling
+ * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi>
+ * Copyright (c) 2005, Sam Leffler <sam@errno.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * FreeBSD-specific implementation.
+ */
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <pcap.h>
+
+#include "common.h"
+#include "eloop.h"
+#include "l2_packet.h"
+
+static const u8 pae_group_addr[ETH_ALEN] =
+ { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+struct l2_packet_data {
+ pcap_t *pcap;
+ char ifname[100];
+ u8 own_addr[ETH_ALEN];
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len);
+ void *rx_callback_ctx;
+ int l2_hdr; /* whether to include layer 2 (Ethernet) header data
+ * buffers */
+};
+
+int
+l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr)
+{
+ memcpy(addr, l2->own_addr, ETH_ALEN);
+ return 0;
+}
+
+int
+l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
+{
+ pcap_if_t *devs, *dev;
+ struct pcap_addr *addr;
+ struct sockaddr_in *saddr;
+ int found = 0;
+ char err[PCAP_ERRBUF_SIZE + 1];
+
+ if (pcap_findalldevs(&devs, err) < 0) {
+ wpa_printf(MSG_DEBUG, "pcap_findalldevs: %s\n", err);
+ return -1;
+ }
+
+ for (dev = devs; dev && !found; dev = dev->next) {
+ if (strcmp(dev->name, l2->ifname) != 0)
+ continue;
+
+ addr = dev->addresses;
+ while (addr) {
+ saddr = (struct sockaddr_in *) addr->addr;
+ if (saddr && saddr->sin_family == AF_INET) {
+ snprintf(buf, len, "%s",
+ inet_ntoa(saddr->sin_addr));
+ found = 1;
+ break;
+ }
+ addr = addr->next;
+ }
+ }
+
+ pcap_freealldevs(devs);
+
+ return found ? 0 : -1;
+}
+
+void
+l2_packet_notify_auth_start(struct l2_packet_data *l2)
+{
+}
+
+int
+l2_packet_send(struct l2_packet_data *l2,
+ const u8 *dst_addr, u16 proto, const u8 *buf, size_t len)
+{
+ if (!l2->l2_hdr) {
+ int ret;
+ struct l2_ethhdr *eth = malloc(sizeof(*eth) + len);
+ if (eth == NULL)
+ return -1;
+ memcpy(eth->h_dest, dst_addr, ETH_ALEN);
+ memcpy(eth->h_source, l2->own_addr, ETH_ALEN);
+ eth->h_proto = htons(proto);
+ memcpy(eth + 1, buf, len);
+ ret = pcap_inject(l2->pcap, (u8 *) eth, len + sizeof(*eth));
+ free(eth);
+ return ret;
+ } else
+ return pcap_inject(l2->pcap, buf, len);
+}
+
+
+static void
+l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
+{
+ struct l2_packet_data *l2 = eloop_ctx;
+ pcap_t *pcap = sock_ctx;
+ struct pcap_pkthdr hdr;
+ const u_char *packet;
+ struct l2_ethhdr *ethhdr;
+ unsigned char *buf;
+ size_t len;
+
+ packet = pcap_next(pcap, &hdr);
+
+ if (packet == NULL || hdr.caplen < sizeof(*ethhdr))
+ return;
+
+ ethhdr = (struct l2_ethhdr *) packet;
+ if (l2->l2_hdr) {
+ buf = (unsigned char *) ethhdr;
+ len = hdr.caplen;
+ } else {
+ buf = (unsigned char *) (ethhdr + 1);
+ len = hdr.caplen - sizeof(*ethhdr);
+ }
+ l2->rx_callback(l2->rx_callback_ctx, ethhdr->h_source, buf, len);
+}
+
+static int
+l2_packet_init_libpcap(struct l2_packet_data *l2, unsigned short protocol)
+{
+ bpf_u_int32 pcap_maskp, pcap_netp;
+ char pcap_filter[200], pcap_err[PCAP_ERRBUF_SIZE];
+ struct bpf_program pcap_fp;
+
+ pcap_lookupnet(l2->ifname, &pcap_netp, &pcap_maskp, pcap_err);
+ l2->pcap = pcap_open_live(l2->ifname, 2500, 0, 10, pcap_err);
+ if (l2->pcap == NULL) {
+ fprintf(stderr, "pcap_open_live: %s\n", pcap_err);
+ fprintf(stderr, "ifname='%s'\n", l2->ifname);
+ return -1;
+ }
+ if (pcap_datalink(l2->pcap) != DLT_EN10MB &&
+ pcap_set_datalink(l2->pcap, DLT_EN10MB) < 0) {
+ fprintf(stderr, "pcap_set_datalink(DLT_EN10MB): %s\n",
+ pcap_geterr(l2->pcap));
+ return -1;
+ }
+ snprintf(pcap_filter, sizeof(pcap_filter),
+ "not ether src " MACSTR " and "
+ "( ether dst " MACSTR " or ether dst " MACSTR " ) and "
+ "ether proto 0x%x",
+ MAC2STR(l2->own_addr), /* do not receive own packets */
+ MAC2STR(l2->own_addr), MAC2STR(pae_group_addr),
+ protocol);
+ if (pcap_compile(l2->pcap, &pcap_fp, pcap_filter, 1, pcap_netp) < 0) {
+ fprintf(stderr, "pcap_compile: %s\n", pcap_geterr(l2->pcap));
+ return -1;
+ }
+
+ if (pcap_setfilter(l2->pcap, &pcap_fp) < 0) {
+ fprintf(stderr, "pcap_setfilter: %s\n", pcap_geterr(l2->pcap));
+ return -1;
+ }
+
+ pcap_freecode(&pcap_fp);
+ /*
+ * When libpcap uses BPF we must enable "immediate mode" to
+ * receive frames right away; otherwise the system may
+ * buffer them for us.
+ */
+ { unsigned int on = 1;
+ if (ioctl(pcap_fileno(l2->pcap), BIOCIMMEDIATE, &on) < 0) {
+ fprintf(stderr, "%s: cannot enable immediate mode on "
+ "interface %s: %s\n",
+ __func__, l2->ifname, strerror(errno));
+ /* XXX should we fail? */
+ }
+ }
+
+ eloop_register_read_sock(pcap_get_selectable_fd(l2->pcap),
+ l2_packet_receive, l2, l2->pcap);
+
+ return 0;
+}
+
+static void
+l2_packet_deinit_libpcap(struct l2_packet_data *l2)
+{
+ if (l2->pcap != NULL) {
+ eloop_unregister_read_sock(pcap_get_selectable_fd(l2->pcap));
+ pcap_close(l2->pcap);
+ l2->pcap = NULL;
+ }
+}
+
+static int
+eth_get(const char *device, u8 ea[ETH_ALEN])
+{
+ struct if_msghdr *ifm;
+ struct sockaddr_dl *sdl;
+ u_char *p, *buf;
+ size_t len;
+ int mib[] = { CTL_NET, AF_ROUTE, 0, AF_LINK, NET_RT_IFLIST, 0 };
+
+ if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0)
+ return -1;
+ if ((buf = malloc(len)) == NULL)
+ return -1;
+ if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
+ free(buf);
+ return -1;
+ }
+ for (p = buf; p < buf + len; p += ifm->ifm_msglen) {
+ ifm = (struct if_msghdr *)p;
+ sdl = (struct sockaddr_dl *)(ifm + 1);
+ if (ifm->ifm_type != RTM_IFINFO ||
+ (ifm->ifm_addrs & RTA_IFP) == 0)
+ continue;
+ if (sdl->sdl_family != AF_LINK || sdl->sdl_nlen == 0 ||
+ memcmp(sdl->sdl_data, device, sdl->sdl_nlen) != 0)
+ continue;
+ memcpy(ea, LLADDR(sdl), sdl->sdl_alen);
+ break;
+ }
+ free(buf);
+
+ if (p >= buf + len) {
+ errno = ESRCH;
+ return -1;
+ }
+ return 0;
+}
+
+struct l2_packet_data *
+l2_packet_init(const char *ifname, const u8 *own_addr, unsigned short protocol,
+ void (*rx_callback)(void *ctx, const u8 *src_addr,
+ const u8 *buf, size_t len),
+ void *rx_callback_ctx, int l2_hdr)
+{
+ struct l2_packet_data *l2;
+
+ l2 = malloc(sizeof(struct l2_packet_data));
+ if (l2 == NULL)
+ return NULL;
+ memset(l2, 0, sizeof(*l2));
+ strncpy(l2->ifname, ifname, sizeof(l2->ifname));
+ l2->rx_callback = rx_callback;
+ l2->rx_callback_ctx = rx_callback_ctx;
+ l2->l2_hdr = l2_hdr;
+
+ if (eth_get(l2->ifname, l2->own_addr) < 0) {
+ fprintf(stderr, "Failed to get link-level address for "
+ "interface '%s'.\n", l2->ifname);
+ free(l2);
+ return NULL;
+ }
+
+ if (l2_packet_init_libpcap(l2, protocol) != 0) {
+ free(l2);
+ return NULL;
+ }
+ return l2;
+}
+
+void
+l2_packet_deinit(struct l2_packet_data *l2)
+{
+ if (l2 != NULL) {
+ l2_packet_deinit_libpcap(l2);
+ free(l2);
+ }
+}
diff --git a/usr.sbin/wpa/ndis_events/Makefile b/usr.sbin/wpa/ndis_events/Makefile
new file mode 100644
index 0000000..07caf5a
--- /dev/null
+++ b/usr.sbin/wpa/ndis_events/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+PROG= ndis_events
+SRCS+= ndis_events.c
+
+MAN= ndis_events.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/wpa/ndis_events/ndis_events.8 b/usr.sbin/wpa/ndis_events/ndis_events.8
new file mode 100644
index 0000000..eacb017
--- /dev/null
+++ b/usr.sbin/wpa/ndis_events/ndis_events.8
@@ -0,0 +1,135 @@
+.\" Copyright (c) 2005
+.\" Bill Paul <wpaul@windriver.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 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd August 30, 2007
+.Dt NDIS_EVENTS 8
+.Os
+.Sh NAME
+.Nm ndis_events
+.Nd relay events from
+.Xr ndis 4
+drivers to
+.Xr wpa_supplicant 8
+.Sh SYNOPSIS
+.Nm
+.Op Fl a
+.Op Fl d
+.Op Fl v
+.Sh DESCRIPTION
+The
+.Nm
+utility listens for events generated by an
+.Xr ndis 4
+wireless network driver and relays them to
+.Xr wpa_supplicant 8
+for possible processing.
+The three event types that can occur
+are media connect and disconnect events, such as when a wireless
+interface joins or leaves a network, and media-specific events.
+In particular,
+.Xr ndis 4
+drivers that support WPA2 will generate media-specific events
+containing PMKID candidate information which
+.Xr wpa_supplicant 8
+needs in order to properly associate with WPA2-capable access points.
+.Pp
+The
+.Nm
+daemon works by listening for interface information events via
+a routing socket.
+When it detects an event that was generated by an
+.Xr ndis 4
+interface, it transmits it via UDP packet on the loopback interface,
+where
+.Xr wpa_supplicant 8
+is presumeably listening.
+The standard
+.Xr wpa_supplicant 8
+distribution includes its own version of this utility for use with
+.Tn Windows\[rg] .
+The
+.Fx
+version performs the same functions as the
+.Tn Windows\[rg]
+one, except that it uses an
+.Xr ioctl 4
+and routing socket interface instead of WMI.
+.Pp
+Note that a single instance of
+.Nm
+is sufficient to scan for events for any number of
+.Xr ndis 4
+interfaces in a system.
+.Sh OPTIONS
+The
+.Nm
+daemon supports the following options:
+.Bl -tag -width indent
+.It Fl a
+Process all events.
+By default,
+.Nm
+will only process and forward media-specific events, which contain
+PMKID candidate information, and not bother forwarding connect and
+disconnect events, since
+.Xr wpa_supplicant 8
+normally can determine the current link state on its own.
+In some
+cases, the additional connect and disconnect events only confuse it
+and make the association and authentication process take longer.
+.It Fl d
+Run in debug mode.
+This causes
+.Nm
+to run in the foreground and generate any output to the standard
+error instead of using the
+.Xr syslog 3
+facility.
+.It Fl v
+Run in verbose mode.
+This causes
+.Nm
+to emit notifications when it receives events.
+.El
+.Sh SEE ALSO
+.Xr ndis 4 ,
+.Xr wpa_supplicant 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 6.0 .
+.Sh AUTHORS
+The
+.Nm
+utility was written by
+.An Bill Paul Aq wpaul@windriver.com .
diff --git a/usr.sbin/wpa/ndis_events/ndis_events.c b/usr.sbin/wpa/ndis_events/ndis_events.c
new file mode 100644
index 0000000..d2dedff
--- /dev/null
+++ b/usr.sbin/wpa/ndis_events/ndis_events.c
@@ -0,0 +1,355 @@
+/*-
+ * Copyright (c) 2005
+ * Bill Paul <wpaul@windriver.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 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * This program simulates the behavior of the ndis_events utility
+ * supplied with wpa_supplicant for Windows. The original utility
+ * is designed to translate Windows WMI events. We don't have WMI,
+ * but we need to supply certain event info to wpa_supplicant in
+ * order to make WPA2 work correctly, so we fake up the interface.
+ */
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/sysctl.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_var.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <net/route.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <err.h>
+#include <syslog.h>
+#include <stdarg.h>
+
+static int verbose = 0;
+static int debug = 0;
+static int all_events = 0;
+
+#define PROGNAME "ndis_events"
+
+#define WPA_SUPPLICANT_PORT 9876
+#define NDIS_INDICATION_LEN 2048
+
+#define EVENT_CONNECT 0
+#define EVENT_DISCONNECT 1
+#define EVENT_MEDIA_SPECIFIC 2
+
+#define NDIS_STATUS_MEDIA_CONNECT 0x4001000B
+#define NDIS_STATUS_MEDIA_DISCONNECT 0x4001000C
+#define NDIS_STATUS_MEDIA_SPECIFIC_INDICATION 0x40010012
+
+struct ndis_evt {
+ uint32_t ne_sts;
+ uint32_t ne_len;
+#ifdef notdef
+ char ne_buf[1];
+#endif
+};
+
+static int find_ifname(int, char *);
+static int announce_event(char *, int, struct sockaddr_in *);
+static void usage(char *);
+
+static void
+dbgmsg(const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (debug)
+ vwarnx(fmt, ap);
+ else
+ vsyslog(LOG_ERR, fmt, ap);
+ va_end(ap);
+
+ return;
+}
+
+static int
+find_ifname(idx, name)
+ int idx;
+ char *name;
+{
+ int mib[6];
+ size_t needed;
+ struct if_msghdr *ifm;
+ struct sockaddr_dl *sdl;
+ char *buf, *lim, *next;
+
+ needed = 0;
+ 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)
+ return(EIO);
+
+ buf = malloc (needed);
+ if (buf == NULL)
+ return(ENOMEM);
+
+ if (sysctl (mib, 6, buf, &needed, NULL, 0) < 0) {
+ free(buf);
+ return(EIO);
+ }
+
+ lim = buf + needed;
+
+ next = buf;
+ while (next < lim) {
+ ifm = (struct if_msghdr *)next;
+ if (ifm->ifm_type == RTM_IFINFO) {
+ sdl = (struct sockaddr_dl *)(ifm + 1);
+ if (ifm->ifm_index == idx) {
+ strncpy(name, sdl->sdl_data, sdl->sdl_nlen);
+ name[sdl->sdl_nlen] = '\0';
+ free (buf);
+ return (0);
+ }
+ }
+ next += ifm->ifm_msglen;
+ }
+
+ free (buf);
+
+ return(ENOENT);
+}
+
+static int
+announce_event(ifname, sock, dst)
+ char *ifname;
+ int sock;
+ struct sockaddr_in *dst;
+{
+ int s;
+ char indication[NDIS_INDICATION_LEN];
+ struct ifreq ifr;
+ struct ndis_evt *e;
+ char buf[512], *pos, *end;
+ int len, type, _type;
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+
+ if (s < 0) {
+ dbgmsg("socket creation failed");
+ return(EINVAL);
+ }
+
+ bzero((char *)&ifr, sizeof(ifr));
+ e = (struct ndis_evt *)indication;
+ e->ne_len = NDIS_INDICATION_LEN - sizeof(struct ndis_evt);
+
+ strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
+ ifr.ifr_data = indication;
+
+ if (ioctl(s, SIOCGPRIVATE_0, &ifr) < 0) {
+ close(s);
+ if (verbose) {
+ if (errno == ENOENT)
+ dbgmsg("drained all events from %s",
+ ifname, errno);
+ else
+ dbgmsg("failed to read event info from %s: %d",
+ ifname, errno);
+ }
+ return(ENOENT);
+ }
+
+ if (e->ne_sts == NDIS_STATUS_MEDIA_CONNECT) {
+ type = EVENT_CONNECT;
+ if (verbose)
+ dbgmsg("Received a connect event for %s", ifname);
+ if (!all_events) {
+ close(s);
+ return(0);
+ }
+ }
+ if (e->ne_sts == NDIS_STATUS_MEDIA_DISCONNECT) {
+ type = EVENT_DISCONNECT;
+ if (verbose)
+ dbgmsg("Received a disconnect event for %s", ifname);
+ if (!all_events) {
+ close(s);
+ return(0);
+ }
+ }
+ if (e->ne_sts == NDIS_STATUS_MEDIA_SPECIFIC_INDICATION) {
+ type = EVENT_MEDIA_SPECIFIC;
+ if (verbose)
+ dbgmsg("Received a media-specific event for %s",
+ ifname);
+ }
+
+ end = buf + sizeof(buf);
+ _type = (int) type;
+ memcpy(buf, &_type, sizeof(_type));
+ pos = buf + sizeof(_type);
+
+ len = snprintf(pos + 1, end - pos - 1, "%s", ifname);
+ if (len < 0) {
+ close(s);
+ return(ENOSPC);
+ }
+ if (len > 255)
+ len = 255;
+ *pos = (unsigned char) len;
+ pos += 1 + len;
+ if (e->ne_len) {
+ if (e->ne_len > 255 || 1 + e->ne_len > end - pos) {
+ dbgmsg("Not enough room for send_event data (%d)\n",
+ e->ne_len);
+ close(s);
+ return(ENOSPC);
+ }
+ *pos++ = (unsigned char) e->ne_len;
+ memcpy(pos, (indication) + sizeof(struct ndis_evt), e->ne_len);
+ pos += e->ne_len;
+ }
+
+ len = sendto(sock, buf, pos - buf, 0, (struct sockaddr *) dst,
+ sizeof(struct sockaddr_in));
+
+ close(s);
+ return(0);
+}
+
+static void
+usage(progname)
+ char *progname;
+{
+ fprintf(stderr, "Usage: ndis_events [-a] [-d] [-v]\n", progname);
+ exit(1);
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int s, r, n;
+ struct sockaddr_in sin;
+ char msg[NDIS_INDICATION_LEN];
+ struct rt_msghdr *rtm;
+ struct if_msghdr *ifm;
+ char ifname[IFNAMSIZ];
+ int ch;
+
+ while ((ch = getopt(argc, argv, "dva")) != -1) {
+ switch(ch) {
+ case 'd':
+ debug++;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'a':
+ all_events++;
+ break;
+ default:
+ usage(PROGNAME);
+ break;
+ }
+ }
+
+ if (!debug && daemon(0, 0))
+ err(1, "failed to daemonize ourselves");
+
+ if (!debug)
+ openlog(PROGNAME, LOG_PID | LOG_CONS, LOG_DAEMON);
+
+ bzero((char *)&sin, sizeof(sin));
+
+ /* Create a datagram socket. */
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ dbgmsg("socket creation failed");
+ exit(1);
+ }
+
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = inet_addr("127.0.0.1");
+ sin.sin_port = htons(WPA_SUPPLICANT_PORT);
+
+ /* Create a routing socket. */
+
+ r = socket (PF_ROUTE, SOCK_RAW, 0);
+ if (r < 0) {
+ dbgmsg("routing socket creation failed");
+ exit(1);
+ }
+
+ /* Now sit and spin, waiting for events. */
+
+ if (verbose)
+ dbgmsg("Listening for events");
+
+ while (1) {
+ n = read(r, msg, NDIS_INDICATION_LEN);
+ rtm = (struct rt_msghdr *)msg;
+ if (rtm->rtm_type != RTM_IFINFO)
+ continue;
+ ifm = (struct if_msghdr *)msg;
+ if (find_ifname(ifm->ifm_index, ifname))
+ continue;
+ if (strstr(ifname, "ndis")) {
+ while(announce_event(ifname, s, &sin) == 0)
+ ;
+ } else {
+ if (verbose)
+ dbgmsg("Skipping ifinfo message from %s",
+ ifname);
+ }
+ }
+
+ /* NOTREACHED */
+ exit(0);
+}
diff --git a/usr.sbin/wpa/wpa_cli/Makefile b/usr.sbin/wpa/wpa_cli/Makefile
new file mode 100644
index 0000000..ae16a72
--- /dev/null
+++ b/usr.sbin/wpa/wpa_cli/Makefile
@@ -0,0 +1,19 @@
+# $FreeBSD$
+
+.include "${.CURDIR}/../Makefile.inc"
+
+.PATH.c:${WPA_SUPPLICANT_DISTDIR}
+
+PROG= wpa_cli
+SRCS= wpa_cli.c wpa_ctrl.c os_unix.c
+
+MAN= wpa_cli.8
+
+CFLAGS+= -DCONFIG_CTRL_IFACE
+CFLAGS+= -DCONFIG_CTRL_IFACE_UNIX
+
+#CFLAGS+= -DCONFIG_READLINE
+#LDADD+= -ledit -ltermcap
+#DPADD+= ${LIBEDIT} ${LIBTERMCAP}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/wpa/wpa_cli/wpa_cli.8 b/usr.sbin/wpa/wpa_cli/wpa_cli.8
new file mode 100644
index 0000000..27fc943
--- /dev/null
+++ b/usr.sbin/wpa/wpa_cli/wpa_cli.8
@@ -0,0 +1,222 @@
+.\" Copyright (c) 2005 Sam Leffler <sam@errno.com>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd June 16, 2005
+.Dt WPA_CLI 8
+.Os
+.Sh NAME
+.Nm wpa_cli
+.Nd "text-based frontend program for interacting with wpa_supplicant"
+.Sh SYNOPSIS
+.Nm
+.Op Ar commands
+.Sh DESCRIPTION
+The
+.Nm
+utility
+is a text-based frontend program for interacting with
+.Xr wpa_supplicant 8 .
+It is used to query current status,
+change configuration,
+trigger events,
+and
+request interactive user input.
+.Pp
+The
+.Nm
+utility
+can show the
+current authentication status,
+selected security
+mode, dot11 and dot1x MIBs, etc.
+In addition,
+.Nm
+can configure EAPOL state machine
+parameters and trigger events such as reassociation
+and IEEE 802.1X logoff/logon.
+.Pp
+The
+.Nm
+utility
+provides an interface to supply authentication information
+such as username and password when it is not provided in the
+.Xr wpa_supplicant.conf 5
+configuration file.
+This can be used, for example, to implement
+one-time passwords or generic token card
+authentication where the authentication is based on a
+challenge-response that uses an external device for generating the
+response.
+.Pp
+The
+.Nm
+utility
+supports two modes: interactive and command line.
+Both modes share the same command set and the main difference
+is in interactive mode providing access to unsolicited messages
+(event messages, username/password requests).
+.Pp
+Interactive mode is started when
+.Nm
+is executed without any parameters on the command line.
+Commands are then entered from the controlling terminal in
+response to the
+.Nm
+prompt.
+In command line mode, the same commands are
+entered as command line arguments.
+.Pp
+The control interface of
+.Xr wpa_supplicant 8
+can be configured to allow
+non-root user access by using the
+.Va ctrl_interface_group
+parameter
+in the
+.Xr wpa_supplicant.conf 5
+configuration file.
+This makes it possible to run
+.Nm
+with a normal user account.
+.Sh AUTHENTICATION PARAMETERS
+When
+.Xr wpa_supplicant 8
+needs authentication parameters, such as username and password,
+that are not present in the configuration file, it sends a
+request message to all attached frontend programs, e.g.,
+.Nm
+in interactive mode.
+The
+.Nm
+utility
+shows these requests with a
+.Dq Li CTRL-REQ- Ns Ao Ar type Ac Ns Li - Ns Ao Ar id Ac Ns Li : Ns Aq Ar text
+prefix, where
+.Aq Ar type
+is
+.Li IDENTITY , PASSWORD ,
+or
+.Li OTP
+(One-Time Password),
+.Aq Ar id
+is a unique identifier for the current network,
+.Aq Ar text
+is a description of the request.
+In the case of an
+.Li OTP
+(One-Time Password) request,
+it includes the challenge from the authentication server.
+.Pp
+A user must supply
+.Xr wpa_supplicant 8
+the needed parameters in response to these requests.
+.Pp
+For example,
+.Bd -literal -offset indent
+CTRL-REQ-PASSWORD-1:Password needed for SSID foobar
+> password 1 mysecretpassword
+
+Example request for generic token card challenge-response:
+
+CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar
+> otp 2 9876
+.Ed
+.Sh COMMANDS
+The following commands may be supplied on the command line
+or at a prompt when operating interactively.
+.Bl -tag -width indent
+.It Ic status
+Report the current WPA/EAPOL/EAP status for the current interface.
+.It Ic mib
+Report MIB variables (dot1x, dot11) for the current interface.
+.It Ic help
+Show usage help.
+.It Ic interface Op Ar ifname
+Show available interfaces and/or set the current interface
+when multiple are available.
+.It Ic level Ar debug_level
+Change the debugging level in
+.Xr wpa_supplicant 8 .
+Larger numbers generate more messages.
+.It Ic license
+Display the full
+license for
+.Nm .
+.It Ic logoff
+Send the IEEE 802.1X EAPOL state machine into the
+.Dq logoff
+state.
+.It Ic logon
+Send the IEEE 802.1X EAPOL state machine into the
+.Dq logon
+state.
+.It Ic set Op Ar settings
+Set variables.
+When no arguments are supplied, the known variables and their settings
+are displayed.
+.It Ic pmksa
+Show the contents of the PMKSA cache.
+.It Ic reassociate
+Force a reassociation to the current access point.
+.It Ic reconfigure
+Force
+.Xr wpa_supplicant 8
+to re-read its configuration file.
+.It Ic preauthenticate Ar BSSID
+Force preauthentication of the specified
+.Ar BSSID .
+.It Ic identity Ar network_id identity
+Configure an identity for an SSID.
+.It Ic password Ar network_id password
+Configure a password for an SSID.
+.It Ic otp Ar network_id password
+Configure a one-time password for an SSID.
+.It Ic terminate
+Force
+.Xr wpa_supplicant 8
+to terminate.
+.It Ic quit
+Exit
+.Nm .
+.El
+.Sh SEE ALSO
+.Xr wpa_supplicant.conf 5 ,
+.Xr wpa_supplicant 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 6.0 .
+.Sh AUTHORS
+The
+.Nm
+utility was written by
+.An Jouni Malinen Aq jkmaline@cc.hut.fi .
+This manual page is derived from the
+.Pa README
+file included in the
+.Nm wpa_supplicant
+distribution.
diff --git a/usr.sbin/wpa/wpa_passphrase/Makefile b/usr.sbin/wpa/wpa_passphrase/Makefile
new file mode 100644
index 0000000..027f1a1
--- /dev/null
+++ b/usr.sbin/wpa/wpa_passphrase/Makefile
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+.include "${.CURDIR}/../Makefile.inc"
+
+.PATH.c:${WPA_SUPPLICANT_DISTDIR}
+
+PROG= wpa_passphrase
+SRCS= wpa_passphrase.c sha1.c md5.c
+
+CFLAGS+= -DINTERNAL_SHA1
+CFLAGS+= -DINTERNAL_MD5
+
+MAN= wpa_passphrase.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/wpa/wpa_passphrase/wpa_passphrase.8 b/usr.sbin/wpa/wpa_passphrase/wpa_passphrase.8
new file mode 100644
index 0000000..fc8ade1
--- /dev/null
+++ b/usr.sbin/wpa/wpa_passphrase/wpa_passphrase.8
@@ -0,0 +1,66 @@
+.\" Copyright (c) 2006 Henrik Brix Andersen <henrik@brixandersen.dk>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 17, 2007
+.Dt WPA_PASSPHRASE 8
+.Os
+.Sh NAME
+.Nm wpa_passphrase
+.Nd "utility for generating a 256-bit pre-shared WPA key from an ASCII passphrase"
+.Sh SYNOPSIS
+.Nm
+.Aq Ar ssid
+.Op Ar passphrase
+.Sh DESCRIPTION
+The
+.Nm
+utility is a small program for generating a 256-bit pre-shared WPA key
+from an ASCII passphrase and a given SSID. The output is formatted for
+inclusion in
+.Xr wpa_supplicant.conf 5 .
+.Pp
+If
+.Nm
+is called with only an SSID as argument it will prompt for a
+passphrase on standard input.
+.Sh SEE ALSO
+.Xr wpa_supplicant.conf 5 ,
+.Xr wpa_supplicant 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 6.3 .
+.Sh AUTHORS
+The
+.Nm
+utility was written by
+.An Jouni Malinen
+.Aq jkmaline@cc.hut.fi .
+.Pp
+This manual page was written by
+.An Henrik Brix Andersen
+.Aq henrik@brixandersen.dk .
diff --git a/usr.sbin/wpa/wpa_supplicant/Makefile b/usr.sbin/wpa/wpa_supplicant/Makefile
new file mode 100644
index 0000000..7389e02
--- /dev/null
+++ b/usr.sbin/wpa/wpa_supplicant/Makefile
@@ -0,0 +1,150 @@
+# $FreeBSD$
+
+.include "${.CURDIR}/../Makefile.inc"
+
+.PATH.c:${WPA_SUPPLICANT_DISTDIR} \
+ ${WPA_DISTDIR}/src/drivers \
+ ${WPA_DISTDIR}/src/eap_peer \
+ ${WPA_DISTDIR}/src/rsn_supp
+
+PROG= wpa_supplicant
+SRCS= aes.c aes_wrap.c blacklist.c common.c config.c ctrl_iface.c \
+ ctrl_iface_unix.c drivers.c eloop.c events.c l2_packet.c main.c \
+ md5.c preauth.c pmksa_cache.c rc4.c scan.c scan_helpers.c sha1.c \
+ wpa.c wpa_common.c wpa_debug.c wpa_ie.c wpa_supplicant.c \
+ wpabuf.c wpas_glue.c \
+ driver_ndis.c Packet32.c \
+ driver_wired.c \
+ driver_freebsd.c os_unix.c
+
+MAN= wpa_supplicant.8 wpa_supplicant.conf.5
+
+.if ${MK_EXAMPLES} != "no"
+FILESDIR= ${SHAREDIR}/examples/etc
+.PATH: ${WPA_SUPPLICANT_DISTDIR}
+FILES= wpa_supplicant.conf
+.endif
+
+CFLAGS+=-I${WPA_SUPPLICANT_DISTDIR}
+CFLAGS+=-I${WPA_DISTDIR}/src/drivers
+CFLAGS+=-I${WPA_DISTDIR}/src/rsn_supp
+
+CFLAGS+= -DCONFIG_DRIVER_BSD
+CFLAGS+= -DCONFIG_DRIVER_NDIS
+CFLAGS+= -DCONFIG_DRIVER_WIRED
+CFLAGS+= -DCONFIG_TERMINATE_ONLASTIF
+CFLAGS+= -DCONFIG_DEBUG_SYSLOG
+CFLAGS+= -g
+DPADD+= ${LIBPCAP}
+LDADD+= -lpcap
+
+# NB: we only support wpa_supplicant.conf file
+SRCS+= config_file.c base64.c
+CFLAGS+=-DCONFIG_BACKEND_FILE
+
+# User customizations to the wpa_supplicant build environment
+CFLAGS+=${WPA_SUPPLICANT_CFLAGS}
+#DPADD+=${WPA_SUPPLICANT_DPADD}
+LDADD+=${WPA_SUPPLICANT_LDADD}
+#LDFLAGS+=${WPA_SUPPLICANT_LDFLAGS}
+
+.if ${MK_WPA_SUPPLICANT_EAPOL} != "no"
+SRCS+= eapol_supp_sm.c eap.c eap_common.c eap_methods.c
+CFLAGS+= -DIEEE8021X_EAPOL
+
+.if ${MK_OPENSSL} != "no" && !defined(RELEASE_CRUNCH)
+CFLAGS+=-DEAP_TLS -DEAP_PEAP -DEAP_MSCHAPv2 -DEAP_LEAP -DEAP_PSK \
+ -DEAP_TLV -DEAP_TLS_FUNCS -DEAP_TLS_OPENSSL
+SRCS+= chap.c crypto_openssl.c \
+ eap_leap.c \
+ eap_mschapv2.c \
+ eap_peap.c eap_peap_common.c \
+ eap_psk.c eap_psk_common.c \
+ eap_tls.c eap_tls_common.c \
+ mschapv2.c ms_funcs.c tls_openssl.c
+
+CFLAGS+=-DEAP_TTLS -DEAP_MD5
+SRCS+= eap_ttls.c eap_md5.c
+
+.if !empty(CFLAGS:M*-DEAP_GTC)
+SRCS+= eap_gtc.c
+.endif
+
+.if !empty(CFLAGS:M*-DEAP_OTP)
+SRCS+= eap_otp.c
+.endif
+
+.if !empty(CFLAGS:M*-DEAP_AKA)
+NEED_SIM_COMMON= true
+SRCS+= eap_aka.c
+.endif
+
+.if !empty(CFLAGS:M*-DEAP_SIM)
+NEED_SIM_COMMON= true
+SRCS+= eap_sim.c
+.endif
+
+.if defined(NEED_SIM_COMMON)
+SRCS+= eap_sim_common.c
+
+# PC/SC interface for smartcards (USIM, GSM SIM)
+# GSM/UMTS authentication algorithm (for EAP-SIM/EAP-AKA)
+# NB: requires devel/pcsc-lite
+#
+# WPA_SUPPLICANT_CFLAGS=-DEAP_AKA -DPCSC_FUNCS -I/usr/local/include/PCSC
+# WPA_SUPPLICANT_LDADD=-L/usr/local/lib
+#
+.if !empty(CFLAGS:M*-DPCSC_FUNCS)
+SRCS+= pcsc_funcs.c
+DPADD+=${LIBPTHREAD}
+LDADD+=-lpcsclite -lpthread
+.endif
+.endif
+
+.if !empty(CFLAGS:M*-DEAP_GPSK)
+CFLAGS+=-DEAP_GPSK_SHA256
+SRCS+= eap_gpsk.c eap_gpsk_common.c
+NEED_SHA256= true
+.endif
+
+.if !empty(CFLAGS:M*-DEAP_PAX)
+SRCS+= eap_pax.c eap_pax_common.c
+.endif
+
+.if !empty(CFLAGS:M*-DEAP_SAKE)
+SRCS+= eap_sake.c eap_sake_common.c
+.endif
+
+# NB: requires patch to openssl
+#CFLAGS+= -DEAP_FAST
+#SRCS+= eap_fast.c
+
+NEED_LIBSSL= true
+.else
+CFLAGS+= -DEAP_TLS_NONE
+SRCS+= tls_none.c
+.endif
+
+.endif
+
+#
+# Configure crypto/cipher support.
+#
+# EAPOL support requires openssl in which case we use their
+# cipher code. Otherwise we use our internal versions.
+#
+.if !defined(NEED_LIBSSL)
+CFLAGS+= -DINTERNAL_AES
+CFLAGS+= -DINTERNAL_SHA1
+CFLAGS+= -DINTERNAL_MD5
+.else
+DPADD+= ${LIBSSL} ${LIBCRYPTO}
+LDADD+= -lssl -lcrypto
+.endif
+
+.if defined(NEED_SHA256)
+CFLAGS+=-DINTERNAL_SHA256
+SRCS+= sha256.c
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/wpa/wpa_supplicant/Packet32.c b/usr.sbin/wpa/wpa_supplicant/Packet32.c
new file mode 100644
index 0000000..07da359
--- /dev/null
+++ b/usr.sbin/wpa/wpa_supplicant/Packet32.c
@@ -0,0 +1,415 @@
+/*-
+ * Copyright (c) 2005
+ * Bill Paul <wpaul@windriver.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 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/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * This file implements a small portion of the Winpcap API for the
+ * Windows NDIS interface in wpa_supplicant. It provides just enough
+ * routines to fool wpa_supplicant into thinking it's really running
+ * in a Windows environment.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/errno.h>
+#include <sys/sysctl.h>
+#include <sys/fcntl.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_var.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <net/route.h>
+
+#include <net80211/ieee80211_ioctl.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <pcap.h>
+
+#include "Packet32.h"
+
+#define OID_802_11_ADD_KEY 0x0d01011D
+
+typedef ULONGLONG NDIS_802_11_KEY_RSC;
+typedef UCHAR NDIS_802_11_MAC_ADDRESS[6];
+
+typedef struct NDIS_802_11_KEY {
+ ULONG Length;
+ ULONG KeyIndex;
+ ULONG KeyLength;
+ NDIS_802_11_MAC_ADDRESS BSSID;
+ NDIS_802_11_KEY_RSC KeyRSC;
+ UCHAR KeyMaterial[1];
+} NDIS_802_11_KEY;
+
+typedef struct NDIS_802_11_KEY_COMPAT {
+ ULONG Length;
+ ULONG KeyIndex;
+ ULONG KeyLength;
+ NDIS_802_11_MAC_ADDRESS BSSID;
+ UCHAR Pad[6]; /* Make struct layout match Windows. */
+ NDIS_802_11_KEY_RSC KeyRSC;
+#ifdef notdef
+ UCHAR KeyMaterial[1];
+#endif
+} NDIS_802_11_KEY_COMPAT;
+
+#define TRUE 1
+#define FALSE 0
+
+struct adapter {
+ int socket;
+ char name[IFNAMSIZ];
+ int prev_roaming;
+};
+
+PCHAR
+PacketGetVersion(void)
+{
+ return("FreeBSD WinPcap compatibility shim v1.0");
+}
+
+void *
+PacketOpenAdapter(CHAR *iface)
+{
+ struct adapter *a;
+ int s;
+ int ifflags;
+ struct ifreq ifr;
+ struct ieee80211req ireq;
+
+ s = socket(PF_INET, SOCK_DGRAM, 0);
+
+ if (s == -1)
+ return(NULL);
+
+ a = malloc(sizeof(struct adapter));
+ if (a == NULL)
+ return(NULL);
+
+ a->socket = s;
+ if (strncmp(iface, "\\Device\\NPF_", 12) == 0)
+ iface += 12;
+ else if (strncmp(iface, "\\DEVICE\\", 8) == 0)
+ iface += 8;
+ snprintf(a->name, IFNAMSIZ, "%s", iface);
+
+ /* Turn off net80211 roaming */
+ bzero((char *)&ireq, sizeof(ireq));
+ strncpy(ireq.i_name, iface, sizeof (ifr.ifr_name));
+ ireq.i_type = IEEE80211_IOC_ROAMING;
+ if (ioctl(a->socket, SIOCG80211, &ireq) == 0) {
+ a->prev_roaming = ireq.i_val;
+ ireq.i_val = IEEE80211_ROAMING_MANUAL;
+ if (ioctl(a->socket, SIOCS80211, &ireq) < 0)
+ fprintf(stderr,
+ "Could not set IEEE80211_ROAMING_MANUAL\n");
+ }
+
+ bzero((char *)&ifr, sizeof(ifr));
+ strncpy(ifr.ifr_name, iface, sizeof (ifr.ifr_name));
+ if (ioctl(a->socket, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
+ free(a);
+ close(s);
+ return(NULL);
+ }
+ ifr.ifr_flags |= IFF_UP;
+ if (ioctl(a->socket, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) {
+ free(a);
+ close(s);
+ return(NULL);
+ }
+
+ return(a);
+}
+
+int
+PacketRequest(void *iface, BOOLEAN set, PACKET_OID_DATA *oid)
+{
+ struct adapter *a;
+ uint32_t retval;
+ struct ifreq ifr;
+ NDIS_802_11_KEY *old;
+ NDIS_802_11_KEY_COMPAT *new;
+ PACKET_OID_DATA *o = NULL;
+
+ if (iface == NULL)
+ return(-1);
+
+ a = iface;
+ bzero((char *)&ifr, sizeof(ifr));
+
+ /*
+ * This hack is necessary to work around a difference
+ * betwee the GNU C and Microsoft C compilers. The NDIS_802_11_KEY
+ * structure has a uint64_t in it, right after an array of
+ * chars. The Microsoft compiler inserts padding right before
+ * the 64-bit value to align it on a 64-bit boundary, but
+ * GCC only aligns it on a 32-bit boundary. Trying to pass
+ * the GCC-formatted structure to an NDIS binary driver
+ * fails because some of the fields appear to be at the
+ * wrong offsets.
+ *
+ * To get around this, if we detect someone is trying to do
+ * a set operation on OID_802_11_ADD_KEY, we shuffle the data
+ * into a properly padded structure and pass that into the
+ * driver instead. This allows the driver_ndis.c code supplied
+ * with wpa_supplicant to work unmodified.
+ */
+
+ if (set == TRUE && oid->Oid == OID_802_11_ADD_KEY) {
+ old = (NDIS_802_11_KEY *)&oid->Data;
+ o = malloc(sizeof(PACKET_OID_DATA) +
+ sizeof(NDIS_802_11_KEY_COMPAT) + old->KeyLength);
+ if (o == NULL)
+ return(0);
+ bzero((char *)o, sizeof(PACKET_OID_DATA) +
+ sizeof(NDIS_802_11_KEY_COMPAT) + old->KeyLength);
+ o->Oid = oid->Oid;
+ o->Length = sizeof(NDIS_802_11_KEY_COMPAT) + old->KeyLength;
+ new = (NDIS_802_11_KEY_COMPAT *)&o->Data;
+ new->KeyRSC = old->KeyRSC;
+ new->Length = o->Length;
+ new->KeyIndex = old->KeyIndex;
+ new->KeyLength = old->KeyLength;
+ bcopy(old->BSSID, new->BSSID, sizeof(NDIS_802_11_MAC_ADDRESS));
+ bcopy(old->KeyMaterial, (char *)new +
+ sizeof(NDIS_802_11_KEY_COMPAT), new->KeyLength);
+ ifr.ifr_data = (caddr_t)o;
+ } else
+ ifr.ifr_data = (caddr_t)oid;
+
+ strlcpy(ifr.ifr_name, a->name, sizeof(ifr.ifr_name));
+
+ if (set == TRUE)
+ retval = ioctl(a->socket, SIOCSDRVSPEC, &ifr);
+ else
+ retval = ioctl(a->socket, SIOCGDRVSPEC, &ifr);
+
+ if (o != NULL)
+ free(o);
+
+ if (retval)
+ return(0);
+
+ return(1);
+}
+
+int
+PacketGetAdapterNames(CHAR *namelist, ULONG *len)
+{
+ int mib[6];
+ size_t needed;
+ struct if_msghdr *ifm;
+ struct sockaddr_dl *sdl;
+ char *buf, *lim, *next;
+ char *plist;
+ int spc;
+ int i, ifcnt = 0;
+
+ plist = namelist;
+ spc = 0;
+
+ bzero(plist, *len);
+
+ needed = 0;
+ 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)
+ return(FALSE);
+
+ buf = malloc (needed);
+ if (buf == NULL)
+ return(FALSE);
+
+ if (sysctl (mib, 6, buf, &needed, NULL, 0) < 0) {
+ free(buf);
+ return(FALSE);
+ }
+
+ lim = buf + needed;
+
+ /* Generate interface name list. */
+
+ next = buf;
+ while (next < lim) {
+ ifm = (struct if_msghdr *)next;
+ if (ifm->ifm_type == RTM_IFINFO) {
+ sdl = (struct sockaddr_dl *)(ifm + 1);
+ if (strnstr(sdl->sdl_data, "wlan", sdl->sdl_nlen)) {
+ if ((spc + sdl->sdl_nlen) > *len) {
+ free(buf);
+ return(FALSE);
+ }
+ strncpy(plist, sdl->sdl_data, sdl->sdl_nlen);
+ plist += (sdl->sdl_nlen + 1);
+ spc += (sdl->sdl_nlen + 1);
+ ifcnt++;
+ }
+ }
+ next += ifm->ifm_msglen;
+ }
+
+
+ /* Insert an extra "" as a spacer */
+
+ plist++;
+ spc++;
+
+ /*
+ * Now generate the interface description list. There
+ * must be a unique description for each interface, and
+ * they have to match what the ndis_events program will
+ * feed in later. To keep this simple, we just repeat
+ * the interface list over again.
+ */
+
+ next = buf;
+ while (next < lim) {
+ ifm = (struct if_msghdr *)next;
+ if (ifm->ifm_type == RTM_IFINFO) {
+ sdl = (struct sockaddr_dl *)(ifm + 1);
+ if (strnstr(sdl->sdl_data, "wlan", sdl->sdl_nlen)) {
+ if ((spc + sdl->sdl_nlen) > *len) {
+ free(buf);
+ return(FALSE);
+ }
+ strncpy(plist, sdl->sdl_data, sdl->sdl_nlen);
+ plist += (sdl->sdl_nlen + 1);
+ spc += (sdl->sdl_nlen + 1);
+ ifcnt++;
+ }
+ }
+ next += ifm->ifm_msglen;
+ }
+
+ free (buf);
+
+ *len = spc + 1;
+
+ return(TRUE);
+}
+
+void
+PacketCloseAdapter(void *iface)
+{
+ struct adapter *a;
+ struct ifreq ifr;
+ struct ieee80211req ireq;
+
+ if (iface == NULL)
+ return;
+
+ a = iface;
+
+ /* Reset net80211 roaming */
+ bzero((char *)&ireq, sizeof(ireq));
+ strncpy(ireq.i_name, a->name, sizeof (ifr.ifr_name));
+ ireq.i_type = IEEE80211_IOC_ROAMING;
+ ireq.i_val = a->prev_roaming;
+ ioctl(a->socket, SIOCS80211, &ireq);
+
+ bzero((char *)&ifr, sizeof(ifr));
+ strncpy(ifr.ifr_name, a->name, sizeof (ifr.ifr_name));
+ ioctl(a->socket, SIOCGIFFLAGS, (caddr_t)&ifr);
+ ifr.ifr_flags &= ~IFF_UP;
+ ioctl(a->socket, SIOCSIFFLAGS, (caddr_t)&ifr);
+ close(a->socket);
+ free(a);
+
+ return;
+}
+
+#if __FreeBSD_version < 600000
+
+/*
+ * The version of libpcap in FreeBSD 5.2.1 doesn't have these routines.
+ * Call me insane if you will, but I still run 5.2.1 on my laptop, and
+ * I'd like to use WPA there.
+ */
+
+int
+pcap_get_selectable_fd(pcap_t *p)
+{
+ return(pcap_fileno(p));
+}
+
+/*
+ * The old version of libpcap opens its BPF descriptor in read-only
+ * mode. We need to temporarily create a new one we can write to.
+ */
+
+int
+pcap_inject(pcap_t *p, const void *buf, size_t len)
+{
+ int fd;
+ int res, n = 0;
+ char device[sizeof "/dev/bpf0000000000"];
+ struct ifreq ifr;
+
+ /*
+ * Go through all the minors and find one that isn't in use.
+ */
+ do {
+ (void)snprintf(device, sizeof(device), "/dev/bpf%d", n++);
+ fd = open(device, O_RDWR);
+ } while (fd < 0 && errno == EBUSY);
+
+ if (fd == -1)
+ return(-1);
+
+ bzero((char *)&ifr, sizeof(ifr));
+ ioctl(pcap_fileno(p), BIOCGETIF, (caddr_t)&ifr);
+ ioctl(fd, BIOCSETIF, (caddr_t)&ifr);
+
+ res = write(fd, buf, len);
+
+ close(fd);
+
+ return(res);
+}
+#endif
diff --git a/usr.sbin/wpa/wpa_supplicant/Packet32.h b/usr.sbin/wpa/wpa_supplicant/Packet32.h
new file mode 100644
index 0000000..e0598e7
--- /dev/null
+++ b/usr.sbin/wpa/wpa_supplicant/Packet32.h
@@ -0,0 +1,67 @@
+/*-
+ * Copyright (c) 2005
+ * Bill Paul <wpaul@windriver.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 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _PACKET32_H_
+#define _PACKET32_H_
+
+#include <sys/types.h>
+#include <ntddndis.h>
+
+struct PACKET_OID_DATA {
+ uint32_t Oid;
+ uint32_t Length;
+ uint8_t Data[1];
+};
+
+
+typedef struct PACKET_OID_DATA PACKET_OID_DATA;
+
+extern PCHAR PacketGetVersion(void);
+extern void *PacketOpenAdapter(CHAR *);
+extern int PacketRequest(void *, BOOLEAN, PACKET_OID_DATA *);
+extern int PacketGetAdapterNames(CHAR *, ULONG *);
+extern void PacketCloseAdapter(void *);
+
+/*
+ * This is for backwards compatibility on FreeBSD 5.
+ */
+
+#ifndef SIOCGDRVSPEC
+#define SIOCSDRVSPEC _IOW('i', 123, struct ifreq) /* set driver-specific
+ parameters */
+#define SIOCGDRVSPEC _IOWR('i', 123, struct ifreq) /* get driver-specific
+ parameters */
+#endif
+
+#endif /* _PACKET32_H_ */
diff --git a/usr.sbin/wpa/wpa_supplicant/driver_freebsd.c b/usr.sbin/wpa/wpa_supplicant/driver_freebsd.c
new file mode 100644
index 0000000..ec8ae98
--- /dev/null
+++ b/usr.sbin/wpa/wpa_supplicant/driver_freebsd.c
@@ -0,0 +1,909 @@
+/*
+ * WPA Supplicant - driver interaction with BSD net80211 layer
+ * Copyright (c) 2004, Sam Leffler <sam@errno.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * $FreeBSD$
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+
+#include "common.h"
+#include "driver.h"
+#include "eloop.h"
+#include "l2_packet.h"
+#include "ieee802_11_defs.h"
+
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+
+#include <net80211/ieee80211_ioctl.h>
+
+struct wpa_driver_bsd_data {
+ int sock; /* open socket for 802.11 ioctls */
+ int route; /* routing socket for events */
+ char ifname[IFNAMSIZ+1]; /* interface name */
+ unsigned int ifindex; /* interface index */
+ void *ctx;
+ int prev_roaming; /* roaming state to restore on deinit */
+ int prev_privacy; /* privacy state to restore on deinit */
+ int prev_wpa; /* wpa state to restore on deinit */
+ int prev_scanvalid; /* scan valid to restore on deinit */
+ uint8_t lastssid[IEEE80211_NWID_LEN];
+ int lastssid_len;
+ uint32_t drivercaps; /* general driver capabilities */
+ uint32_t cryptocaps; /* hardware crypto support */
+};
+
+static int
+set80211var(struct wpa_driver_bsd_data *drv, int op, const void *arg, int arg_len)
+{
+ struct ieee80211req ireq;
+
+ memset(&ireq, 0, sizeof(ireq));
+ strncpy(ireq.i_name, drv->ifname, IFNAMSIZ);
+ ireq.i_type = op;
+ ireq.i_len = arg_len;
+ ireq.i_data = (void *) arg;
+
+ if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) {
+ fprintf(stderr, "ioctl[SIOCS80211, op %u, len %u]: %s\n",
+ op, arg_len, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int
+get80211var(struct wpa_driver_bsd_data *drv, int op, void *arg, int arg_len)
+{
+ struct ieee80211req ireq;
+
+ memset(&ireq, 0, sizeof(ireq));
+ strncpy(ireq.i_name, drv->ifname, IFNAMSIZ);
+ ireq.i_type = op;
+ ireq.i_len = arg_len;
+ ireq.i_data = arg;
+
+ if (ioctl(drv->sock, SIOCG80211, &ireq) < 0) {
+ fprintf(stderr, "ioctl[SIOCG80211, op %u, len %u]: %s\n",
+ op, arg_len, strerror(errno));
+ return -1;
+ }
+ return ireq.i_len;
+}
+
+static int
+set80211param(struct wpa_driver_bsd_data *drv, int op, int arg)
+{
+ struct ieee80211req ireq;
+
+ memset(&ireq, 0, sizeof(ireq));
+ strncpy(ireq.i_name, drv->ifname, IFNAMSIZ);
+ ireq.i_type = op;
+ ireq.i_val = arg;
+
+ if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) {
+ fprintf(stderr, "ioctl[SIOCS80211, op %u, arg 0x%x]: %s\n",
+ op, arg, strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int
+get80211param(struct wpa_driver_bsd_data *drv, int op)
+{
+ struct ieee80211req ireq;
+
+ memset(&ireq, 0, sizeof(ireq));
+ strncpy(ireq.i_name, drv->ifname, IFNAMSIZ);
+ ireq.i_type = op;
+
+ if (ioctl(drv->sock, SIOCG80211, &ireq) < 0) {
+ fprintf(stderr, "ioctl[SIOCG80211, op %u]: %s\n",
+ op, strerror(errno));
+ return -1;
+ }
+ return ireq.i_val;
+}
+
+static int
+getifflags(struct wpa_driver_bsd_data *drv, int *flags)
+{
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, drv->ifname, sizeof (ifr.ifr_name));
+ if (ioctl(drv->sock, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
+ perror("SIOCGIFFLAGS");
+ return errno;
+ }
+ *flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16);
+ return 0;
+}
+
+static int
+setifflags(struct wpa_driver_bsd_data *drv, int flags)
+{
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, drv->ifname, sizeof (ifr.ifr_name));
+ ifr.ifr_flags = flags & 0xffff;
+ ifr.ifr_flagshigh = flags >> 16;
+ if (ioctl(drv->sock, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) {
+ perror("SIOCSIFFLAGS");
+ return errno;
+ }
+ return 0;
+}
+
+static int
+wpa_driver_bsd_get_bssid(void *priv, u8 *bssid)
+{
+ struct wpa_driver_bsd_data *drv = priv;
+
+ return get80211var(drv, IEEE80211_IOC_BSSID,
+ bssid, IEEE80211_ADDR_LEN) < 0 ? -1 : 0;
+}
+
+#if 0
+static int
+wpa_driver_bsd_set_bssid(void *priv, const char *bssid)
+{
+ struct wpa_driver_bsd_data *drv = priv;
+
+ return set80211var(drv, IEEE80211_IOC_BSSID,
+ bssid, IEEE80211_ADDR_LEN);
+}
+#endif
+
+static int
+wpa_driver_bsd_get_ssid(void *priv, u8 *ssid)
+{
+ struct wpa_driver_bsd_data *drv = priv;
+
+ return get80211var(drv, IEEE80211_IOC_SSID,
+ ssid, IEEE80211_NWID_LEN);
+}
+
+static int
+wpa_driver_bsd_set_ssid(void *priv, const char *ssid,
+ size_t ssid_len)
+{
+ struct wpa_driver_bsd_data *drv = priv;
+
+ return set80211var(drv, IEEE80211_IOC_SSID, ssid, ssid_len);
+}
+
+static int
+wpa_driver_bsd_set_wpa_ie(struct wpa_driver_bsd_data *drv,
+ const char *wpa_ie, size_t wpa_ie_len)
+{
+ struct ieee80211req ireq;
+
+ memset(&ireq, 0, sizeof(ireq));
+ strncpy(ireq.i_name, drv->ifname, IFNAMSIZ);
+ ireq.i_type = IEEE80211_IOC_APPIE;
+ ireq.i_val = IEEE80211_APPIE_WPA;
+ ireq.i_len = wpa_ie_len;
+ ireq.i_data = (void *) wpa_ie;
+ if (ioctl(drv->sock, SIOCS80211, &ireq) < 0) {
+ fprintf(stderr,
+ "ioctl[IEEE80211_IOC_APPIE:IEEE80211_APPIE_WPA]: %s\n",
+ strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static int
+wpa_driver_bsd_set_wpa_internal(void *priv, int wpa, int privacy)
+{
+ struct wpa_driver_bsd_data *drv = priv;
+ int ret = 0;
+
+ wpa_printf(MSG_DEBUG, "%s: wpa=%d privacy=%d",
+ __FUNCTION__, wpa, privacy);
+
+ if (!wpa && wpa_driver_bsd_set_wpa_ie(drv, NULL, 0) < 0)
+ ret = -1;
+ if (set80211param(drv, IEEE80211_IOC_PRIVACY, privacy) < 0)
+ ret = -1;
+ if (set80211param(drv, IEEE80211_IOC_WPA, wpa) < 0)
+ ret = -1;
+
+ return ret;
+}
+
+static int
+wpa_driver_bsd_set_wpa(void *priv, int enabled)
+{
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled);
+
+ return wpa_driver_bsd_set_wpa_internal(priv, enabled ? 3 : 0, enabled);
+}
+
+static int
+wpa_driver_bsd_del_key(struct wpa_driver_bsd_data *drv, int key_idx,
+ const unsigned char *addr)
+{
+ struct ieee80211req_del_key wk;
+
+ memset(&wk, 0, sizeof(wk));
+ if (addr != NULL &&
+ bcmp(addr, "\xff\xff\xff\xff\xff\xff", IEEE80211_ADDR_LEN) != 0) {
+ struct ether_addr ea;
+
+ memcpy(&ea, addr, IEEE80211_ADDR_LEN);
+ wpa_printf(MSG_DEBUG, "%s: addr=%s keyidx=%d",
+ __func__, ether_ntoa(&ea), key_idx);
+ memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN);
+ wk.idk_keyix = (uint8_t) IEEE80211_KEYIX_NONE;
+ } else {
+ wpa_printf(MSG_DEBUG, "%s: keyidx=%d", __func__, key_idx);
+ wk.idk_keyix = key_idx;
+ }
+ return set80211var(drv, IEEE80211_IOC_DELKEY, &wk, sizeof(wk));
+}
+
+static int
+wpa_driver_bsd_set_key(void *priv, wpa_alg alg,
+ const unsigned char *addr, int key_idx, int set_tx,
+ const u8 *seq, size_t seq_len,
+ const u8 *key, size_t key_len)
+{
+ struct wpa_driver_bsd_data *drv = priv;
+ struct ieee80211req_key wk;
+ struct ether_addr ea;
+ char *alg_name;
+ u_int8_t cipher;
+
+ if (alg == WPA_ALG_NONE)
+ return wpa_driver_bsd_del_key(drv, key_idx, addr);
+
+ switch (alg) {
+ case WPA_ALG_WEP:
+ alg_name = "WEP";
+ cipher = IEEE80211_CIPHER_WEP;
+ break;
+ case WPA_ALG_TKIP:
+ alg_name = "TKIP";
+ cipher = IEEE80211_CIPHER_TKIP;
+ break;
+ case WPA_ALG_CCMP:
+ alg_name = "CCMP";
+ cipher = IEEE80211_CIPHER_AES_CCM;
+ break;
+ default:
+ wpa_printf(MSG_DEBUG, "%s: unknown/unsupported algorithm %d",
+ __func__, alg);
+ return -1;
+ }
+
+ memcpy(&ea, addr, IEEE80211_ADDR_LEN);
+ wpa_printf(MSG_DEBUG,
+ "%s: alg=%s addr=%s key_idx=%d set_tx=%d seq_len=%zu key_len=%zu",
+ __func__, alg_name, ether_ntoa(&ea), key_idx, set_tx,
+ seq_len, key_len);
+
+ if (seq_len > sizeof(u_int64_t)) {
+ wpa_printf(MSG_DEBUG, "%s: seq_len %zu too big",
+ __func__, seq_len);
+ return -2;
+ }
+ if (key_len > sizeof(wk.ik_keydata)) {
+ wpa_printf(MSG_DEBUG, "%s: key length %zu too big",
+ __func__, key_len);
+ return -3;
+ }
+
+ memset(&wk, 0, sizeof(wk));
+ wk.ik_type = cipher;
+ wk.ik_flags = IEEE80211_KEY_RECV;
+ if (set_tx)
+ wk.ik_flags |= IEEE80211_KEY_XMIT;
+ memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN);
+ /*
+ * Deduce whether group/global or unicast key by checking
+ * the address (yech). Note also that we can only mark global
+ * keys default; doing this for a unicast key is an error.
+ */
+ if (bcmp(addr, "\xff\xff\xff\xff\xff\xff", IEEE80211_ADDR_LEN) == 0) {
+ wk.ik_flags |= IEEE80211_KEY_GROUP;
+ wk.ik_keyix = key_idx;
+ } else {
+ wk.ik_keyix = (key_idx == 0 ? IEEE80211_KEYIX_NONE : key_idx);
+ }
+ if (wk.ik_keyix != IEEE80211_KEYIX_NONE && set_tx)
+ wk.ik_flags |= IEEE80211_KEY_DEFAULT;
+ wk.ik_keylen = key_len;
+ memcpy(&wk.ik_keyrsc, seq, seq_len);
+ wk.ik_keyrsc = le64toh(wk.ik_keyrsc);
+ memcpy(wk.ik_keydata, key, key_len);
+
+ return set80211var(drv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk));
+}
+
+static int
+wpa_driver_bsd_set_countermeasures(void *priv, int enabled)
+{
+ struct wpa_driver_bsd_data *drv = priv;
+
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+ return set80211param(drv, IEEE80211_IOC_COUNTERMEASURES, enabled);
+}
+
+
+static int
+wpa_driver_bsd_set_drop_unencrypted(void *priv, int enabled)
+{
+ struct wpa_driver_bsd_data *drv = priv;
+
+ wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+ return set80211param(drv, IEEE80211_IOC_DROPUNENCRYPTED, enabled);
+}
+
+static int
+wpa_driver_bsd_deauthenticate(void *priv, const u8 *addr, int reason_code)
+{
+ struct wpa_driver_bsd_data *drv = priv;
+ struct ieee80211req_mlme mlme;
+
+ drv->lastssid_len = 0;
+
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+ memset(&mlme, 0, sizeof(mlme));
+ mlme.im_op = IEEE80211_MLME_DEAUTH;
+ mlme.im_reason = reason_code;
+ memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+ return set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme));
+}
+
+static int
+wpa_driver_bsd_disassociate(void *priv, const u8 *addr, int reason_code)
+{
+ struct wpa_driver_bsd_data *drv = priv;
+ struct ieee80211req_mlme mlme;
+
+ drv->lastssid_len = 0;
+
+ wpa_printf(MSG_DEBUG, "%s", __func__);
+ memset(&mlme, 0, sizeof(mlme));
+ mlme.im_op = IEEE80211_MLME_DISASSOC;
+ mlme.im_reason = reason_code;
+ memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN);
+ return set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme));
+}
+
+static int
+wpa_driver_bsd_associate(void *priv, struct wpa_driver_associate_params *params)
+{
+ struct wpa_driver_bsd_data *drv = priv;
+ struct ieee80211req_mlme mlme;
+ int privacy;
+
+ wpa_printf(MSG_DEBUG,
+ "%s: ssid '%.*s' wpa ie len %u pairwise %u group %u key mgmt %u"
+ , __func__
+ , params->ssid_len, params->ssid
+ , params->wpa_ie_len
+ , params->pairwise_suite
+ , params->group_suite
+ , params->key_mgmt_suite
+ );
+
+ /* XXX error handling is wrong but unclear what to do... */
+ if (wpa_driver_bsd_set_wpa_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0)
+ return -1;
+
+ privacy = !(params->pairwise_suite == CIPHER_NONE &&
+ params->group_suite == CIPHER_NONE &&
+ params->key_mgmt_suite == KEY_MGMT_NONE &&
+ params->wpa_ie_len == 0);
+ wpa_printf(MSG_DEBUG, "%s: set PRIVACY %u", __func__, privacy);
+
+ if (set80211param(drv, IEEE80211_IOC_PRIVACY, privacy) < 0)
+ return -1;
+
+ if (params->wpa_ie_len &&
+ set80211param(drv, IEEE80211_IOC_WPA,
+ params->wpa_ie[0] == WLAN_EID_RSN ? 2 : 1) < 0)
+ return -1;
+
+ memset(&mlme, 0, sizeof(mlme));
+ mlme.im_op = IEEE80211_MLME_ASSOC;
+ if (params->ssid != NULL)
+ memcpy(mlme.im_ssid, params->ssid, params->ssid_len);
+ mlme.im_ssid_len = params->ssid_len;
+ if (params->bssid != NULL)
+ memcpy(mlme.im_macaddr, params->bssid, IEEE80211_ADDR_LEN);
+ if (set80211var(drv, IEEE80211_IOC_MLME, &mlme, sizeof(mlme)) < 0)
+ return -1;
+ memcpy(drv->lastssid, params->ssid, params->ssid_len);
+ drv->lastssid_len = params->ssid_len;
+ return 0;
+}
+
+static int
+wpa_driver_bsd_set_auth_alg(void *priv, int auth_alg)
+{
+ struct wpa_driver_bsd_data *drv = priv;
+ int authmode;
+
+ if ((auth_alg & AUTH_ALG_OPEN_SYSTEM) &&
+ (auth_alg & AUTH_ALG_SHARED_KEY))
+ authmode = IEEE80211_AUTH_AUTO;
+ else if (auth_alg & AUTH_ALG_SHARED_KEY)
+ authmode = IEEE80211_AUTH_SHARED;
+ else
+ authmode = IEEE80211_AUTH_OPEN;
+
+ wpa_printf(MSG_DEBUG, "%s alg 0x%x authmode %u",
+ __func__, auth_alg, authmode);
+
+ return set80211param(drv, IEEE80211_IOC_AUTHMODE, authmode);
+}
+
+static int
+wpa_driver_bsd_scan(void *priv, const u8 *ssid, size_t ssid_len)
+{
+ struct wpa_driver_bsd_data *drv = priv;
+ struct ieee80211_scan_req sr;
+ int flags;
+
+ /* XXX not true but easiest to perpetuate the myth */
+ /* NB: interface must be marked UP to do a scan */
+ if (getifflags(drv, &flags) != 0) {
+ wpa_printf(MSG_DEBUG, "%s did not mark interface UP", __func__);
+ return -1;
+ }
+ if ((flags & IFF_UP) == 0 && setifflags(drv, flags | IFF_UP) != 0) {
+ wpa_printf(MSG_DEBUG, "%s unable to mark interface UP",
+ __func__);
+ return -1;
+ }
+
+ memset(&sr, 0, sizeof(sr));
+ sr.sr_flags = IEEE80211_IOC_SCAN_ACTIVE
+ | IEEE80211_IOC_SCAN_ONCE
+ | IEEE80211_IOC_SCAN_NOJOIN
+ ;
+ sr.sr_duration = IEEE80211_IOC_SCAN_FOREVER;
+ if (ssid_len != 0) {
+ /* XXX ssid_len must be <= IEEE80211_NWID_LEN */
+ memcpy(sr.sr_ssid[sr.sr_nssid].ssid, ssid, ssid_len);
+ sr.sr_ssid[sr.sr_nssid].len = ssid_len;
+ sr.sr_nssid++;
+ }
+ if (drv->lastssid_len != 0 &&
+ (drv->lastssid_len != ssid_len ||
+ memcmp(drv->lastssid, ssid, ssid_len) != 0)) {
+ /*
+ * If we are scanning because we received a deauth
+ * and the scan cache is warm then we'll find the
+ * ap there and short circuit a full-blown scan.
+ */
+ memcpy(sr.sr_ssid[sr.sr_nssid].ssid, drv->lastssid,
+ drv->lastssid_len);
+ sr.sr_ssid[sr.sr_nssid].len = drv->lastssid_len;
+ sr.sr_nssid++;
+ /* NB: clear so we don't retry w/o associating first */
+ drv->lastssid_len = 0;
+ }
+ if (sr.sr_nssid != 0) /* NB: check scan cache first */
+ sr.sr_flags |= IEEE80211_IOC_SCAN_CHECK;
+
+ /* NB: net80211 delivers a scan complete event so no need to poll */
+ return set80211var(drv, IEEE80211_IOC_SCAN_REQ, &sr, sizeof(sr));
+}
+
+#include <net/route.h>
+#include <net80211/ieee80211_freebsd.h>
+
+static void
+wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx)
+{
+ struct wpa_driver_bsd_data *drv = sock_ctx;
+ char buf[2048];
+ struct if_announcemsghdr *ifan;
+ struct if_msghdr *ifm;
+ struct rt_msghdr *rtm;
+ union wpa_event_data event;
+ struct ieee80211_michael_event *mic;
+ int n;
+
+ n = read(sock, buf, sizeof(buf));
+ if (n < 0) {
+ if (errno != EINTR && errno != EAGAIN)
+ perror("read(PF_ROUTE)");
+ return;
+ }
+
+ rtm = (struct rt_msghdr *) buf;
+ if (rtm->rtm_version != RTM_VERSION) {
+ wpa_printf(MSG_DEBUG, "Routing message version %d not "
+ "understood\n", rtm->rtm_version);
+ return;
+ }
+ memset(&event, 0, sizeof(event));
+ switch (rtm->rtm_type) {
+ case RTM_IFANNOUNCE:
+ ifan = (struct if_announcemsghdr *) rtm;
+ if (ifan->ifan_index != drv->ifindex)
+ break;
+ strlcpy(event.interface_status.ifname, drv->ifname,
+ sizeof(event.interface_status.ifname));
+ switch (ifan->ifan_what) {
+ case IFAN_DEPARTURE:
+ event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
+ default:
+ return;
+ }
+ wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s",
+ event.interface_status.ifname,
+ ifan->ifan_what == IFAN_DEPARTURE ?
+ "removed" : "added");
+ wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event);
+ break;
+ case RTM_IEEE80211:
+ ifan = (struct if_announcemsghdr *) rtm;
+ if (ifan->ifan_index != drv->ifindex)
+ break;
+ switch (ifan->ifan_what) {
+ case RTM_IEEE80211_ASSOC:
+ case RTM_IEEE80211_REASSOC:
+ wpa_supplicant_event(ctx, EVENT_ASSOC, NULL);
+ break;
+ case RTM_IEEE80211_DISASSOC:
+ wpa_supplicant_event(ctx, EVENT_DISASSOC, NULL);
+ break;
+ case RTM_IEEE80211_SCAN:
+ wpa_supplicant_event(ctx, EVENT_SCAN_RESULTS, NULL);
+ break;
+ case RTM_IEEE80211_REPLAY:
+ /* ignore */
+ break;
+ case RTM_IEEE80211_MICHAEL:
+ mic = (struct ieee80211_michael_event *) &ifan[1];
+ wpa_printf(MSG_DEBUG,
+ "Michael MIC failure wireless event: "
+ "keyix=%u src_addr=" MACSTR, mic->iev_keyix,
+ MAC2STR(mic->iev_src));
+
+ memset(&event, 0, sizeof(event));
+ event.michael_mic_failure.unicast =
+ !IEEE80211_IS_MULTICAST(mic->iev_dst);
+ wpa_supplicant_event(ctx, EVENT_MICHAEL_MIC_FAILURE,
+ &event);
+ break;
+ }
+ break;
+ case RTM_IFINFO:
+ ifm = (struct if_msghdr *) rtm;
+ if (ifm->ifm_index != drv->ifindex)
+ break;
+ if ((rtm->rtm_flags & RTF_UP) == 0) {
+ strlcpy(event.interface_status.ifname, drv->ifname,
+ sizeof(event.interface_status.ifname));
+ event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
+ wpa_printf(MSG_DEBUG, "RTM_IFINFO: Interface '%s' DOWN",
+ event.interface_status.ifname);
+ wpa_supplicant_event(ctx, EVENT_INTERFACE_STATUS, &event);
+ }
+ break;
+ }
+}
+
+/* Compare function for sorting scan results. Return >0 if @b is consider
+ * better. */
+static int
+wpa_scan_result_compar(const void *a, const void *b)
+{
+ const struct wpa_scan_result *wa = a;
+ const struct wpa_scan_result *wb = b;
+
+ /* WPA/WPA2 support preferred */
+ if ((wb->wpa_ie_len || wb->rsn_ie_len) &&
+ !(wa->wpa_ie_len || wa->rsn_ie_len))
+ return 1;
+ if (!(wb->wpa_ie_len || wb->rsn_ie_len) &&
+ (wa->wpa_ie_len || wa->rsn_ie_len))
+ return -1;
+
+ /* privacy support preferred */
+ if ((wa->caps & IEEE80211_CAPINFO_PRIVACY) &&
+ (wb->caps & IEEE80211_CAPINFO_PRIVACY) == 0)
+ return 1;
+ if ((wa->caps & IEEE80211_CAPINFO_PRIVACY) == 0 &&
+ (wb->caps & IEEE80211_CAPINFO_PRIVACY))
+ return -1;
+
+ /* best/max rate preferred if signal level close enough XXX */
+ if (wa->maxrate != wb->maxrate && abs(wb->level - wa->level) < 5)
+ return wb->maxrate - wa->maxrate;
+
+ /* use freq for channel preference */
+
+ /* all things being equal, use signal level */
+ return wb->level - wa->level;
+}
+
+static int
+getmaxrate(const 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)
+ rate = maxrate;
+ }
+ return maxrate;
+}
+
+/* unalligned 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
+wpa_driver_bsd_get_scan_results(void *priv,
+ struct wpa_scan_result *results,
+ size_t max_size)
+{
+#define min(a,b) ((a)>(b)?(b):(a))
+ struct wpa_driver_bsd_data *drv = priv;
+ uint8_t buf[24*1024];
+ const uint8_t *cp, *vp;
+ const struct ieee80211req_scan_result *sr;
+ struct wpa_scan_result *wsr;
+ int len, ielen;
+
+ memset(results, 0, max_size * sizeof(struct wpa_scan_result));
+
+ len = get80211var(drv, IEEE80211_IOC_SCAN_RESULTS, buf, sizeof(buf));
+ if (len < 0)
+ return -1;
+ cp = buf;
+ wsr = results;
+ while (len >= sizeof(struct ieee80211req_scan_result)) {
+ sr = (const struct ieee80211req_scan_result *) cp;
+ memcpy(wsr->bssid, sr->isr_bssid, IEEE80211_ADDR_LEN);
+ wsr->ssid_len = sr->isr_ssid_len;
+ wsr->freq = sr->isr_freq;
+ wsr->noise = sr->isr_noise;
+ wsr->qual = sr->isr_rssi;
+ wsr->level = 0; /* XXX? */
+ wsr->caps = sr->isr_capinfo;
+ wsr->maxrate = getmaxrate(sr->isr_rates, sr->isr_nrates);
+ vp = ((u_int8_t *)sr) + sr->isr_ie_off;
+ memcpy(wsr->ssid, vp, sr->isr_ssid_len);
+ if (sr->isr_ie_len > 0) {
+ vp += sr->isr_ssid_len;
+ ielen = sr->isr_ie_len;
+ while (ielen > 0) {
+ switch (vp[0]) {
+ case IEEE80211_ELEMID_VENDOR:
+ if (!iswpaoui(vp))
+ break;
+ wsr->wpa_ie_len =
+ min(2+vp[1], SSID_MAX_WPA_IE_LEN);
+ memcpy(wsr->wpa_ie, vp, wsr->wpa_ie_len);
+ break;
+ case IEEE80211_ELEMID_RSN:
+ wsr->rsn_ie_len =
+ min(2+vp[1], SSID_MAX_WPA_IE_LEN);
+ memcpy(wsr->rsn_ie, vp, wsr->rsn_ie_len);
+ break;
+ }
+ ielen -= 2+vp[1];
+ vp += 2+vp[1];
+ }
+ }
+
+ cp += sr->isr_len, len -= sr->isr_len;
+ wsr++;
+ }
+ qsort(results, wsr - results, sizeof(struct wpa_scan_result),
+ wpa_scan_result_compar);
+
+ wpa_printf(MSG_DEBUG, "Received %d bytes of scan results (%d BSSes)",
+ len, wsr - results);
+
+ return wsr - results;
+#undef min
+}
+
+#define GETPARAM(drv, param, v) \
+ (((v) = get80211param(drv, param)) != -1)
+#define IEEE80211_C_BGSCAN 0x20000000
+
+/*
+ * Set the scan cache valid threshold to 1.5 x bg scan interval
+ * to force all scan requests to consult the cache unless they
+ * explicitly bypass it.
+ */
+static int
+setscanvalid(struct wpa_driver_bsd_data *drv)
+{
+ int bgscan, scanvalid;
+
+ if (!GETPARAM(drv, IEEE80211_IOC_SCANVALID, drv->prev_scanvalid) ||
+ !GETPARAM(drv, IEEE80211_IOC_BGSCAN_INTERVAL, bgscan))
+ return -1;
+ scanvalid = 3*bgscan/2;
+ return (drv->prev_scanvalid < scanvalid) ?
+ set80211param(drv, IEEE80211_IOC_SCANVALID, scanvalid) : 0;
+}
+
+static void *
+wpa_driver_bsd_init(void *ctx, const char *ifname)
+{
+ struct wpa_driver_bsd_data *drv;
+ struct ieee80211_devcaps_req devcaps;
+ int flags;
+
+ drv = malloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+ memset(drv, 0, sizeof(*drv));
+ /*
+ * NB: We require the interface name be mappable to an index.
+ * This implies we do not support having wpa_supplicant
+ * wait for an interface to appear. This seems ok; that
+ * doesn't belong here; it's really the job of devd.
+ */
+ drv->ifindex = if_nametoindex(ifname);
+ if (drv->ifindex == 0) {
+ wpa_printf(MSG_DEBUG, "%s: interface %s does not exist",
+ __func__, ifname);
+ goto fail1;
+ }
+ drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (drv->sock < 0)
+ goto fail1;
+ drv->ctx = ctx;
+ strncpy(drv->ifname, ifname, sizeof(drv->ifname));
+
+ /*
+ * Mark the interface as down to ensure wpa_supplicant has exclusive
+ * access to the net80211 state machine, do this before opening the
+ * route socket to avoid a false event that the interface disappeared.
+ */
+ if (getifflags(drv, &flags) == 0)
+ (void) setifflags(drv, flags &~ IFF_UP);
+
+ drv->route = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (drv->route < 0)
+ goto fail;
+ eloop_register_read_sock(drv->route,
+ wpa_driver_bsd_event_receive, ctx, drv);
+
+ if (get80211var(drv, IEEE80211_IOC_DEVCAPS, &devcaps, sizeof(devcaps)) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "%s: failed to get device capabilities: %s",
+ __func__, strerror(errno));
+ goto fail;
+ }
+ drv->drivercaps = devcaps.dc_drivercaps;
+ drv->cryptocaps = devcaps.dc_cryptocaps;
+
+ if (!GETPARAM(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)) {
+ wpa_printf(MSG_DEBUG, "%s: failed to get roaming state: %s",
+ __func__, strerror(errno));
+ goto fail;
+ }
+ if (!GETPARAM(drv, IEEE80211_IOC_PRIVACY, drv->prev_privacy)) {
+ wpa_printf(MSG_DEBUG, "%s: failed to get privacy state: %s",
+ __func__, strerror(errno));
+ goto fail;
+ }
+ if (!GETPARAM(drv, IEEE80211_IOC_WPA, drv->prev_wpa)) {
+ wpa_printf(MSG_DEBUG, "%s: failed to get wpa state: %s",
+ __func__, strerror(errno));
+ goto fail;
+ }
+ if (set80211param(drv, IEEE80211_IOC_ROAMING, IEEE80211_ROAMING_MANUAL) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: failed to set wpa_supplicant-based "
+ "roaming: %s", __func__, strerror(errno));
+ goto fail;
+ }
+ if (drv->drivercaps & IEEE80211_C_BGSCAN) {
+ /*
+ * Driver does background scanning; force the scan valid
+ * setting to 1.5 x bg scan interval so the scan cache is
+ * always consulted before we force a foreground scan.
+ */
+ if (setscanvalid(drv) < 0) {
+ wpa_printf(MSG_DEBUG,
+ "%s: warning, failed to set scanvalid, scanning "
+ "may be suboptimal: %s", __func__, strerror(errno));
+ }
+ }
+ if (set80211param(drv, IEEE80211_IOC_WPA, 1+2) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: failed to enable WPA support %s",
+ __func__, strerror(errno));
+ goto fail;
+ }
+
+ return drv;
+fail:
+ close(drv->sock);
+fail1:
+ free(drv);
+ return NULL;
+}
+#undef GETPARAM
+
+static void
+wpa_driver_bsd_deinit(void *priv)
+{
+ struct wpa_driver_bsd_data *drv = priv;
+ int flags;
+
+ /* NB: mark interface down */
+ if (getifflags(drv, &flags) == 0)
+ (void) setifflags(drv, flags &~ IFF_UP);
+
+ wpa_driver_bsd_set_wpa_internal(drv, drv->prev_wpa, drv->prev_privacy);
+ if (set80211param(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming) < 0) {
+ /* NB: don't whinge if device ejected or equivalent */
+ if (errno != ENXIO)
+ wpa_printf(MSG_DEBUG, "%s: failed to restore roaming "
+ "state", __func__);
+ }
+ if (drv->drivercaps & IEEE80211_C_BGSCAN) {
+ /* XXX check return value */
+ (void) set80211param(drv, IEEE80211_IOC_SCANVALID,
+ drv->prev_scanvalid);
+ }
+
+ (void) close(drv->route); /* ioctl socket */
+ (void) close(drv->sock); /* event socket */
+ free(drv);
+}
+
+
+struct wpa_driver_ops wpa_driver_bsd_ops = {
+ .name = "bsd",
+ .desc = "BSD 802.11 support (Atheros, etc.)",
+ .init = wpa_driver_bsd_init,
+ .deinit = wpa_driver_bsd_deinit,
+ .get_bssid = wpa_driver_bsd_get_bssid,
+ .get_ssid = wpa_driver_bsd_get_ssid,
+ .set_wpa = wpa_driver_bsd_set_wpa,
+ .set_key = wpa_driver_bsd_set_key,
+ .set_countermeasures = wpa_driver_bsd_set_countermeasures,
+ .set_drop_unencrypted = wpa_driver_bsd_set_drop_unencrypted,
+ .scan = wpa_driver_bsd_scan,
+ .get_scan_results = wpa_driver_bsd_get_scan_results,
+ .deauthenticate = wpa_driver_bsd_deauthenticate,
+ .disassociate = wpa_driver_bsd_disassociate,
+ .associate = wpa_driver_bsd_associate,
+ .set_auth_alg = wpa_driver_bsd_set_auth_alg,
+};
diff --git a/usr.sbin/wpa/wpa_supplicant/driver_wired.c b/usr.sbin/wpa/wpa_supplicant/driver_wired.c
new file mode 100644
index 0000000..67a0423
--- /dev/null
+++ b/usr.sbin/wpa/wpa_supplicant/driver_wired.c
@@ -0,0 +1,185 @@
+/*
+ * WPA Supplicant - wired Ethernet driver interface
+ * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Alternatively, this software may be distributed under the terms of BSD
+ * license.
+ *
+ * See README and COPYING for more details.
+ *
+ * $FreeBSD$
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+
+#include "common.h"
+#include "driver.h"
+#include "wpa.h"
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+struct wpa_driver_wired_data {
+ int sock;
+ char ifname[IFNAMSIZ + 1];
+ int multi;
+ int flags;
+ void *ctx;
+};
+
+static int
+getifflags(struct wpa_driver_wired_data *drv, int *flags)
+{
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, drv->ifname, sizeof (ifr.ifr_name));
+ if (ioctl(drv->sock, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
+ perror("SIOCGIFFLAGS");
+ return errno;
+ }
+ *flags = (ifr.ifr_flags & 0xffff) | (ifr.ifr_flagshigh << 16);
+ return 0;
+}
+
+static int
+setifflags(struct wpa_driver_wired_data *drv, int flags)
+{
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, drv->ifname, sizeof (ifr.ifr_name));
+ ifr.ifr_flags = flags & 0xffff;
+ ifr.ifr_flagshigh = flags >> 16;
+ if (ioctl(drv->sock, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) {
+ perror("SIOCSIFFLAGS");
+ return errno;
+ }
+ return 0;
+}
+
+static int
+wpa_driver_wired_get_ssid(void *priv, u8 *ssid)
+{
+ ssid[0] = 0;
+ return 0;
+}
+
+static int
+wpa_driver_wired_get_bssid(void *priv, u8 *bssid)
+{
+ /* Report PAE group address as the "BSSID" for wired connection. */
+ os_memcpy(bssid, pae_group_addr, ETH_ALEN);
+ return 0;
+}
+
+static int
+siocmulti(struct wpa_driver_wired_data *drv, int op, const u8 *addr)
+{
+ struct ifreq ifr;
+ struct sockaddr_dl *dlp;
+
+ os_memset(&ifr, 0, sizeof(ifr));
+ os_strncpy(ifr.ifr_name, drv->ifname, IFNAMSIZ);
+ dlp = (struct sockaddr_dl *) &ifr.ifr_addr;
+ dlp->sdl_len = sizeof(struct sockaddr_dl);
+ dlp->sdl_family = AF_LINK;
+ dlp->sdl_index = 0;
+ dlp->sdl_nlen = 0;
+ dlp->sdl_alen = ETH_ALEN;
+ dlp->sdl_slen = 0;
+ os_memcpy(LLADDR(dlp), addr, ETH_ALEN);
+ if (ioctl(drv->sock, op, (caddr_t) &ifr) < 0) {
+ wpa_printf(MSG_INFO, "ioctl[%s]: %s", op == SIOCADDMULTI ?
+ "SIOCADDMULTI" : "SIOCDELMULTI", strerror(errno));
+ return -1;
+ }
+ return 0;
+}
+
+static void *
+wpa_driver_wired_init(void *ctx, const char *ifname)
+{
+ struct wpa_driver_wired_data *drv;
+ int flags;
+
+ drv = os_zalloc(sizeof(*drv));
+ if (drv == NULL)
+ return NULL;
+ os_strncpy(drv->ifname, ifname, sizeof(drv->ifname));
+ drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+ if (drv->sock < 0)
+ goto fail1;
+ drv->ctx = ctx;
+
+ if (getifflags(drv, &drv->flags) < 0) {
+ wpa_printf(MSG_INFO, "%s: Unable to get interface flags",
+ __func__);
+ goto fail;
+ }
+ flags = drv->flags | IFF_UP; /* NB: force interface up */
+
+ /*
+ * Arrange to receive PAE mcast frames. Try to add an
+ * explicit mcast address. If that fails, fallback to
+ * the all multicast mechanism.
+ */
+ if (siocmulti(drv, SIOCADDMULTI, pae_group_addr) == 0) {
+ wpa_printf(MSG_DEBUG, "%s: Added PAE multicast address",
+ __func__);
+ drv->multi = 1;
+ } else if ((drv->flags & IFF_ALLMULTI) == 0)
+ flags |= IFF_ALLMULTI;
+
+ if (flags != drv->flags) {
+ if (setifflags(drv, flags) < 0) {
+ wpa_printf(MSG_INFO, "%s: Failed to set interface flags",
+ __func__);
+ goto fail;
+ }
+ if ((flags ^ drv->flags) & IFF_ALLMULTI)
+ wpa_printf(MSG_DEBUG, "%s: Enabled all-multi mode",
+ __func__);
+ }
+ return drv;
+fail:
+ close(drv->sock);
+fail1:
+ free(drv);
+ return NULL;
+}
+
+static void
+wpa_driver_wired_deinit(void *priv)
+{
+ struct wpa_driver_wired_data *drv = priv;
+
+ if (drv->multi) {
+ if (siocmulti(drv, SIOCDELMULTI, pae_group_addr) < 0) {
+ wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE "
+ "multicast " "group (SIOCDELMULTI)", __func__);
+ }
+ }
+ if (setifflags(drv, drv->flags) < 0) {
+ wpa_printf(MSG_INFO, "%s: Failed to restore interface flags",
+ __func__);
+ }
+ (void) close(drv->sock);
+ os_free(drv);
+}
+
+const struct wpa_driver_ops wpa_driver_wired_ops = {
+ .name = "wired",
+ .desc = "BSD wired Ethernet driver",
+ .get_ssid = wpa_driver_wired_get_ssid,
+ .get_bssid = wpa_driver_wired_get_bssid,
+ .init = wpa_driver_wired_init,
+ .deinit = wpa_driver_wired_deinit,
+};
diff --git a/usr.sbin/wpa/wpa_supplicant/ntddndis.h b/usr.sbin/wpa/wpa_supplicant/ntddndis.h
new file mode 100644
index 0000000..42e403d
--- /dev/null
+++ b/usr.sbin/wpa/wpa_supplicant/ntddndis.h
@@ -0,0 +1,31 @@
+#ifndef _NTDDNDIS_H_
+#define _NTDDNDIS_H_
+
+/*
+ * $FreeBSD$
+ */
+
+/*
+ * Fake up some of the Windows type definitions so that the NDIS
+ * interface module in wpa_supplicant will build.
+ */
+
+#define ULONG uint32_t
+#define USHORT uint16_t
+#define UCHAR uint8_t
+#define LONG int32_t
+#define SHORT int16_t
+#define CHAR int8_t
+#define ULONGLONG uint64_t
+#define LONGLONG int64_t
+#define BOOLEAN uint8_t
+typedef void * LPADAPTER;
+typedef char * PTSTR;
+typedef char * PCHAR;
+
+#define TRUE 1
+#define FALSE 0
+
+#define OID_802_3_CURRENT_ADDRESS 0x01010102
+
+#endif /* _NTDDNDIS_H_ */
diff --git a/usr.sbin/wpa/wpa_supplicant/wpa_supplicant.8 b/usr.sbin/wpa/wpa_supplicant/wpa_supplicant.8
new file mode 100644
index 0000000..3f01e89
--- /dev/null
+++ b/usr.sbin/wpa/wpa_supplicant/wpa_supplicant.8
@@ -0,0 +1,155 @@
+.\" Copyright (c) 2005 Sam Leffler <sam@errno.com>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 24, 2008
+.Dt WPA_SUPPLICANT 8
+.Os
+.Sh NAME
+.Nm wpa_supplicant
+.Nd "WPA/802.11i Supplicant for wireless network devices"
+.Sh SYNOPSIS
+.Nm
+.Op Fl BdehLqsvw
+.Fl i Ar ifname
+.Fl c Ar config-file
+.Sh DESCRIPTION
+The
+.Nm
+utility
+is an implementation of the WPA Supplicant component,
+i.e., the part that runs in the client stations.
+It implements WPA key negotiation with a WPA Authenticator
+and EAP authentication with an Authentication Server.
+In addition,
+.Nm
+controls the roaming and IEEE 802.11
+authentication/association support of the
+.Xr wlan 4
+module and can be used to configure static WEP keys
+based on identified networks.
+.Pp
+The
+.Nm
+utility
+is designed to be a
+.Dq daemon
+program that runs in the
+background and acts as the backend component controlling
+the wireless connection.
+It supports separate frontend programs such as the
+text-based
+.Xr wpa_cli 8
+program.
+.Pp
+The following arguments must be specified on the command line:
+.Bl -tag -width indent
+.It Fl i Ar ifname
+Use the specified wireless interface.
+.It Fl c Ar config-file
+Use the settings in the specified configuration file when managing
+the wireless interface.
+See
+.Xr wpa_supplicant.conf 5
+for a description of the configuration file syntax and contents.
+.Pp
+Changes to the configuration file can be reloaded by sending a
+.Dv SIGHUP
+to the
+.Nm
+process or with the
+.Xr wpa_cli 8
+utility, using
+.Dq Li "wpa_cli reconfigure" .
+.El
+.Sh OPTIONS
+The following options are available:
+.Bl -tag -width indent
+.It Fl d
+Enable debugging messages.
+If this option is supplied twice, more verbose messages are displayed.
+.It Fl e
+Use an external IEEE 802.1X Supplicant program and disable the
+internal Supplicant.
+This option is not normally used.
+.It Fl h
+Show help text.
+.It Fl q
+Decrease debugging verbosity (i.e., counteract the use of the
+.Fl d
+flag).
+.It Fl s
+Send log messages through
+.Xr syslog 3
+instead of to the terminal.
+.It Fl v
+Display version information on the terminal and exit.
+.It Fl w
+If the specified interface is not present, wait for it to be
+added; e.g.\& a cardbus device to be inserted.
+This option is not normally used; instead,
+.Xr devd 8
+should be configured to launch
+.Nm
+when a device is created.
+.It Fl B
+Detach from the controlling terminal and run as a daemon process
+in the background.
+.It Fl K
+Include key information in debugging output.
+.It Fl L
+Display the license for this program on the terminal and exit.
+.El
+.Sh SEE ALSO
+.Xr an 4 ,
+.Xr ath 4 ,
+.Xr ipw 4 ,
+.Xr iwi 4 ,
+.Xr ral 4 ,
+.Xr rum 4 ,
+.Xr ural 4 ,
+.Xr wi 4 ,
+.Xr wlan 4 ,
+.Xr wpi 4 ,
+.Xr zyd 4 ,
+.Xr wpa_supplicant.conf 5 ,
+.Xr devd 8 ,
+.Xr ifconfig 8 ,
+.Xr wpa_cli 8
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Fx 6.0 .
+.Sh AUTHORS
+The
+.Nm
+utility was written by
+.An Jouni Malinen Aq jkmaline@cc.hut.fi .
+This manual page is derived from the
+.Pa README
+file included in the
+.Nm
+distribution.
diff --git a/usr.sbin/wpa/wpa_supplicant/wpa_supplicant.conf.5 b/usr.sbin/wpa/wpa_supplicant/wpa_supplicant.conf.5
new file mode 100644
index 0000000..7f6852e
--- /dev/null
+++ b/usr.sbin/wpa/wpa_supplicant/wpa_supplicant.conf.5
@@ -0,0 +1,545 @@
+.\" Copyright (c) 2005 Sam Leffler <sam@errno.com>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd July 8, 2007
+.Dt WPA_SUPPLICANT.CONF 5
+.Os
+.Sh NAME
+.Nm wpa_supplicant.conf
+.Nd configuration file for
+.Xr wpa_supplicant 8
+.Sh DESCRIPTION
+The
+.Xr wpa_supplicant 8
+utility is an implementation of the WPA Supplicant component,
+i.e., the part that runs in the client stations.
+It implements WPA key negotiation with a WPA Authenticator
+and EAP authentication with Authentication Server using
+configuration information stored in a text file.
+.Pp
+The configuration file consists of optional global parameter
+settings and one or more network blocks, e.g.\&
+one for each used SSID.
+The
+.Xr wpa_supplicant 8
+utility
+will automatically select the best network based on the order of
+the network blocks in the configuration file, network security level
+(WPA/WPA2 is preferred), and signal strength.
+Comments are indicated with the
+.Ql #
+character; all text to the
+end of the line will be ignored.
+.Sh GLOBAL PARAMETERS
+Default parameters used by
+.Xr wpa_supplicant 8
+may be overridden by specifying
+.Pp
+.Dl parameter=value
+.Pp
+in the configuration file (note no spaces are allowed).
+Values with embedded spaces must be enclosed in quote marks.
+.Pp
+The following parameters are recognized:
+.Bl -tag -width indent
+.It Va ctrl_interface
+The pathname of the directory in which
+.Xr wpa_supplicant 8
+creates
+.Ux
+domain socket files for communication
+with frontend programs such as
+.Xr wpa_cli 8 .
+.It Va ctrl_interface_group
+A group name or group ID to use in setting protection on the
+control interface file.
+This can be set to allow non-root users to access the
+control interface files.
+If no group is specified, the group ID of the control interface
+is not modified and will, typically, be the
+group ID of the directory in which the socket is created.
+.It Va eapol_version
+The IEEE 802.1x/EAPOL protocol version to use; either 1 (default) or 2.
+The
+.Xr wpa_supplicant 8
+utility
+is implemented according to IEEE 802-1X-REV-d8 which defines
+EAPOL version to be 2.
+However, some access points do not work when presented with
+this version so by default
+.Xr wpa_supplicant 8
+will announce that it is using EAPOL version 1.
+If version 2 must be announced for correct operation with an
+access point, this value may be set to 2.
+.It Va ap_scan
+Access point scanning and selection control; one of 0, 1 (default), or 2.
+Only setting 1 should be used with the
+.Xr wlan 4
+module; the other settings are for use on other operating systems.
+.It Va fast_reauth
+EAP fast re-authentication; either 1 (default) or 0.
+Control fast re-authentication support in EAP methods that support it.
+.El
+.Sh NETWORK BLOCKS
+Each potential network/access point should have a
+.Dq "network block"
+that describes how to identify it and how to set up security.
+When multiple network blocks are listed in a configuration file,
+the highest priority one is selected for use or, if multiple networks
+with the same priority are identified, the first one listed in the
+configuration file is used.
+.Pp
+A network block description is of the form:
+.Bd -literal -offset indent
+network={
+ parameter=value
+ ...
+}
+.Ed
+.Pp
+(note the leading
+.Qq Li "network={"
+may have no spaces).
+The block specification contains one or more parameters
+from the following list:
+.Bl -tag -width indent
+.It Va ssid No (required)
+Network name (as announced by the access point).
+An
+.Tn ASCII
+or hex string enclosed in quotation marks.
+.It Va scan_ssid
+SSID scan technique; 0 (default) or 1.
+Technique 0 scans for the SSID using a broadcast Probe Request
+frame while 1 uses a directed Probe Request frame.
+Access points that cloak themselves by not broadcasting their SSID
+require technique 1, but beware that this scheme can cause scanning
+to take longer to complete.
+.It Va bssid
+Network BSSID (typically the MAC address of the access point).
+.It Va priority
+The priority of a network when selecting among multiple networks;
+a higher value means a network is more desirable.
+By default networks have priority 0.
+When multiple networks with the same priority are considered
+for selection, other information such as security policy and
+signal strength are used to select one.
+.It Va mode
+IEEE 802.11 operation mode; either 0 (infrastructure, default) or 1 (IBSS).
+Note that IBSS (adhoc) mode can only be used with
+.Va key_mgmt
+set to
+.Li NONE
+(plaintext and static WEP).
+.It Va proto
+List of acceptable protocols; one or more of:
+.Li WPA
+(IEEE 802.11i/D3.0)
+and
+.Li RSN
+(IEEE 802.11i).
+.Li WPA2
+is another name for
+.Li RSN .
+If not set this defaults to
+.Qq Li "WPA RSN" .
+.It Va key_mgmt
+List of acceptable key management protocols; one or more of:
+.Li WPA-PSK
+(WPA pre-shared key),
+.Li WPA-EAP
+(WPA using EAP authentication),
+.Li IEEE8021X
+(IEEE 802.1x using EAP authentication and,
+optionally, dynamically generated WEP keys),
+.Li NONE
+(plaintext or static WEP keys).
+If not set this defaults to
+.Qq Li "WPA-PSK WPA-EAP" .
+.It Va auth_alg
+List of allowed IEEE 802.11 authentication algorithms; one or more of:
+.Li OPEN
+(Open System authentication, required for WPA/WPA2),
+.Li SHARED
+(Shared Key authentication),
+.Li LEAP
+(LEAP/Network EAP).
+If not set automatic selection is used (Open System with LEAP
+enabled if LEAP is allowed as one of the EAP methods).
+.It Va pairwise
+List of acceptable pairwise (unicast) ciphers for WPA; one or more of:
+.Li CCMP
+(AES in Counter mode with CBC-MAC, RFC 3610, IEEE 802.11i/D7.0),
+.Li TKIP
+(Temporal Key Integrity Protocol, IEEE 802.11i/D7.0),
+.Li NONE
+(deprecated).
+If not set this defaults to
+.Qq Li "CCMP TKIP" .
+.It Va group
+List of acceptable group (multicast) ciphers for WPA; one or more of:
+.Li CCMP
+(AES in Counter mode with CBC-MAC, RFC 3610, IEEE 802.11i/D7.0),
+.Li TKIP
+(Temporal Key Integrity Protocol, IEEE 802.11i/D7.0),
+.Li WEP104
+(WEP with 104-bit key),
+.Li WEP40
+(WEP with 40-bit key).
+If not set this defaults to
+.Qq Li "CCMP TKIP WEP104 WEP40" .
+.It Va psk
+WPA preshared key used in WPA-PSK mode.
+The key is specified as 64 hex digits or as
+an 8-63 character
+.Tn ASCII
+passphrase.
+.Tn ASCII
+passphrases are dynamically converted to a 256-bit key at runtime
+using the network SSID, or they can be statically converted at
+configuration time using
+the
+.Xr wpa_passphrase 8
+utility.
+.It Va eapol_flags
+Dynamic WEP key usage for non-WPA mode, specified as a bit field.
+Bit 0 (1) forces dynamically generated unicast WEP keys to be used.
+Bit 1 (2) forces dynamically generated broadcast WEP keys to be used.
+By default this is set to 3 (use both).
+.It Va eap
+List of acceptable EAP methods; one or more of:
+.Li MD5
+(EAP-MD5, cannot be used with WPA,
+used only as a Phase 2 method with EAP-PEAP or EAP-TTLS),
+.Li MSCHAPV2
+(EAP-MSCHAPV2, cannot be used with WPA;
+used only as a Phase 2 method with EAP-PEAP or EAP-TTLS),
+.Li OTP
+(EAP-OTP, cannot be used with WPA;
+used only as a Phase 2 metod with EAP-PEAP or EAP-TTLS),
+.Li GTC
+(EAP-GTC, cannot be used with WPA;
+used only as a Phase 2 metod with EAP-PEAP or EAP-TTLS),
+.Li TLS
+(EAP-TLS, client and server certificate),
+.Li PEAP
+(EAP-PEAP, with tunneled EAP authentication),
+.Li TTLS
+(EAP-TTLS, with tunneled EAP or PAP/CHAP/MSCHAP/MSCHAPV2 authentication).
+If not set this defaults to all available methods compiled in to
+.Xr wpa_supplicant 8 .
+Note that by default
+.Xr wpa_supplicant 8
+is compiled with EAP support; see
+.Xr make.conf 5
+for the
+.Va NO_WPA_SUPPLICANT_EAPOL
+configuration variable that can be used to disable EAP support.
+.It Va identity
+Identity string for EAP.
+.It Va anonymous_identity
+Anonymous identity string for EAP (to be used as the unencrypted identity
+with EAP types that support different tunneled identities; e.g.\& EAP-TTLS).
+.It Va mixed_cell
+Configure whether networks that allow both plaintext and encryption
+are allowed when selecting a BSS from the scan results.
+By default this is set to 0 (disabled).
+.It Va password
+Password string for EAP.
+.It Va ca_cert
+Pathname to CA certificate file.
+This file can have one or more trusted CA certificates.
+If
+.Va ca_cert
+is not included, server certificates will not be verified (not recommended).
+.It Va client_cert
+Pathname to client certificate file (PEM/DER).
+.It Va private_key
+Pathname to a client private key file (PEM/DER/PFX).
+When a PKCS#12/PFX file is used, then
+.Va client_cert
+should not be specified as both the private key and certificate will be
+read from PKCS#12 file.
+.It Va private_key_passwd
+Password for any private key file.
+.It Va dh_file
+Pathname to a file holding DH/DSA parameters (in PEM format).
+This file holds parameters for an ephemeral DH key exchange.
+In most cases, the default RSA authentication does not use this configuration.
+However, it is possible to set up RSA to use an ephemeral DH key exchange.
+In addition, ciphers with
+DSA keys always use ephemeral DH keys.
+This can be used to achieve forward secrecy.
+If the
+.Va dh_file
+is in DSA parameters format, it will be automatically converted
+into DH params.
+.It Va subject_match
+Substring to be matched against the subject of the
+authentication server certificate.
+If this string is set, the server
+certificate is only accepted if it contains this string in the subject.
+The subject string is in following format:
+.Pp
+.Dl "/C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@example.com"
+.It Va phase1
+Phase1 (outer authentication, i.e., TLS tunnel) parameters
+(string with field-value pairs, e.g.,
+.Qq Li peapver=0
+or
+.Qq Li "peapver=1 peaplabel=1" ) .
+.Bl -inset
+.It Li peapver
+can be used to force which PEAP version (0 or 1) is used.
+.It Li peaplabel=1
+can be used to force new label,
+.Dq "client PEAP encryption" ,
+to be used during key derivation when PEAPv1 or newer.
+Most existing PEAPv1 implementations seem to be using the old label,
+.Dq Li "client EAP encryption" ,
+and
+.Xr wpa_supplicant 8
+is now using that as the
+default value.
+Some servers, e.g.,
+.Tn Radiator ,
+may require
+.Li peaplabel=1
+configuration to interoperate with PEAPv1; see
+.Pa eap_testing.txt
+for more details.
+.It Li peap_outer_success=0
+can be used to terminate PEAP authentication on
+tunneled EAP-Success.
+This is required with some RADIUS servers that
+implement
+.Pa draft-josefsson-pppext-eap-tls-eap-05.txt
+(e.g.,
+.Tn Lucent NavisRadius v4.4.0
+with PEAP in
+.Dq "IETF Draft 5"
+mode).
+.It Li include_tls_length=1
+can be used to force
+.Xr wpa_supplicant 8
+to include
+TLS Message Length field in all TLS messages even if they are not
+fragmented.
+.It Li sim_min_num_chal=3
+can be used to configure EAP-SIM to require three
+challenges (by default, it accepts 2 or 3)
+.It Li fast_provisioning=1
+option enables in-line provisioning of EAP-FAST
+credentials (PAC).
+.El
+.It Va phase2
+phase2: Phase2 (inner authentication with TLS tunnel) parameters
+(string with field-value pairs, e.g.,
+.Qq Li "auth=MSCHAPV2"
+for EAP-PEAP or
+.Qq Li "autheap=MSCHAPV2 autheap=MD5"
+for EAP-TTLS).
+.It Va ca_cert2
+Like
+.Va ca_cert
+but for EAP inner Phase 2.
+.It Va client_cert2
+Like
+.Va client_cert
+but for EAP inner Phase 2.
+.It Va private_key2
+Like
+.Va private_key
+but for EAP inner Phase 2.
+.It Va private_key2_passwd
+Like
+.Va private_key_passwd
+but for EAP inner Phase 2.
+.It Va dh_file2
+Like
+.Va dh_file
+but for EAP inner Phase 2.
+.It Va subject_match2
+Like
+.Va subject_match
+but for EAP inner Phase 2.
+.It Va eappsk
+16-byte pre-shared key in hex format for use with EAP-PSK.
+.It Va nai
+User NAI for use with EAP-PSK.
+.It Va server_nai
+Authentication Server NAI for use with EAP-PSK.
+.It Va pac_file
+Pathname to the file to use for PAC entries with EAP-FAST.
+The
+.Xr wpa_supplicant 8
+utility
+must be able to create this file and write updates to it when
+PAC is being provisioned or refreshed.
+.It Va eap_workaround
+Enable/disable EAP workarounds for various interoperability issues
+with misbehaving authentication servers.
+By default these workarounds are enabled.
+Strict EAP conformance can be configured by setting this to 0.
+.El
+.Sh CERTIFICATES
+Some EAP authentication methods require use of certificates.
+EAP-TLS uses both server- and client-side certificates,
+whereas EAP-PEAP and EAP-TTLS only require a server-side certificate.
+When a client certificate is used, a matching private key file must
+also be included in configuration.
+If the private key uses a passphrase, this
+has to be configured in the
+.Nm
+file as
+.Va private_key_passwd .
+.Pp
+The
+.Xr wpa_supplicant 8
+utility
+supports X.509 certificates in PEM and DER formats.
+User certificate and private key can be included in the same file.
+.Pp
+If the user certificate and private key is received in PKCS#12/PFX
+format, they need to be converted to a suitable PEM/DER format for
+use by
+.Xr wpa_supplicant 8 .
+This can be done using the
+.Xr openssl 1
+program, e.g.\& with the following commands:
+.Bd -literal
+# convert client certificate and private key to PEM format
+openssl pkcs12 -in example.pfx -out user.pem -clcerts
+# convert CA certificate (if included in PFX file) to PEM format
+openssl pkcs12 -in example.pfx -out ca.pem -cacerts -nokeys
+.Ed
+.Sh EXAMPLES
+WPA-Personal (PSK) as a home network and WPA-Enterprise with EAP-TLS
+as a work network:
+.Bd -literal
+# allow frontend (e.g., wpa_cli) to be used by all users in 'wheel' group
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+#
+# home network; allow all valid ciphers
+network={
+ ssid="home"
+ scan_ssid=1
+ key_mgmt=WPA-PSK
+ psk="very secret passphrase"
+}
+#
+# work network; use EAP-TLS with WPA; allow only CCMP and TKIP ciphers
+network={
+ ssid="work"
+ scan_ssid=1
+ key_mgmt=WPA-EAP
+ pairwise=CCMP TKIP
+ group=CCMP TKIP
+ eap=TLS
+ identity="user@example.com"
+ ca_cert="/etc/cert/ca.pem"
+ client_cert="/etc/cert/user.pem"
+ private_key="/etc/cert/user.prv"
+ private_key_passwd="password"
+}
+.Ed
+.Pp
+WPA-RADIUS/EAP-PEAP/MSCHAPv2 with RADIUS servers that use old peaplabel
+(e.g., Funk Odyssey and SBR, Meetinghouse Aegis, Interlink RAD-Series):
+.Bd -literal
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+network={
+ ssid="example"
+ scan_ssid=1
+ key_mgmt=WPA-EAP
+ eap=PEAP
+ identity="user@example.com"
+ password="foobar"
+ ca_cert="/etc/cert/ca.pem"
+ phase1="peaplabel=0"
+ phase2="auth=MSCHAPV2"
+}
+.Ed
+.Pp
+EAP-TTLS/EAP-MD5-Challenge configuration with anonymous identity for the
+unencrypted use.
+Real identity is sent only within an encrypted TLS tunnel.
+.Bd -literal
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+network={
+ ssid="example"
+ scan_ssid=1
+ key_mgmt=WPA-EAP
+ eap=TTLS
+ identity="user@example.com"
+ anonymous_identity="anonymous@example.com"
+ password="foobar"
+ ca_cert="/etc/cert/ca.pem"
+ phase2="auth=MD5"
+}
+.Ed
+.Pp
+Traditional WEP configuration with 104 bit key specified in hexadecimal.
+Note the WEP key is not quoted.
+.Bd -literal
+ctrl_interface=/var/run/wpa_supplicant
+ctrl_interface_group=wheel
+network={
+ ssid="example"
+ scan_ssid=1
+ key_mgmt=NONE
+ wep_tx_keyidx=0
+ wep_key0=42FEEDDEAFBABEDEAFBEEFAA55
+}
+.Ed
+.Sh FILES
+.Bl -tag -width ".Pa /usr/share/examples/etc/wpa_supplicant.conf" -compact
+.It Pa /etc/wpa_supplicant.conf
+.It Pa /usr/share/examples/etc/wpa_supplicant.conf
+.El
+.Sh SEE ALSO
+.Xr wpa_cli 8 ,
+.Xr wpa_passphrase 8 ,
+.Xr wpa_supplicant 8
+.Sh HISTORY
+The
+.Nm
+manual page and
+.Xr wpa_supplicant 8
+functionality first appeared in
+.Fx 6.0 .
+.Sh AUTHORS
+This manual page is derived from the
+.Pa README
+and
+.Pa wpa_supplicant.conf
+files in the
+.Nm wpa_supplicant
+distribution provided by
+.An Jouni Malinen Aq jkmaline@cc.hut.fi .
diff --git a/usr.sbin/yp_mkdb/Makefile b/usr.sbin/yp_mkdb/Makefile
new file mode 100644
index 0000000..973da6e
--- /dev/null
+++ b/usr.sbin/yp_mkdb/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../libexec/ypxfr ${.CURDIR}/../ypserv
+
+PROG= yp_mkdb
+MAN= yp_mkdb.8
+SRCS= yp_mkdb.c yp_dblookup.c yp_dbwrite.c
+
+CFLAGS+= -Dyp_error=warnx
+CFLAGS+= -I${.CURDIR}/../../libexec/ypxfr -I${.CURDIR}/../../usr.sbin/ypserv
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/yp_mkdb/yp_mkdb.8 b/usr.sbin/yp_mkdb/yp_mkdb.8
new file mode 100644
index 0000000..1251235
--- /dev/null
+++ b/usr.sbin/yp_mkdb/yp_mkdb.8
@@ -0,0 +1,209 @@
+.\" Copyright (c) 1995, 1996
+.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Bill Paul
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd March 12, 1996
+.Dt YP_MKDB 8
+.Os
+.Sh NAME
+.Nm yp_mkdb
+.Nd "generate the NIS databases"
+.Sh SYNOPSIS
+.Nm
+.Fl c
+.Nm
+.Fl u Ar dbname
+.Nm
+.Op Fl c
+.Op Fl b
+.Op Fl s
+.Op Fl f
+.Op Fl i Ar inputfile
+.Op Fl o Ar outputfile
+.Op Fl d Ar domainname
+.Op Fl m Ar mastername
+.Ar inputfile
+.Ar dbname
+.Sh DESCRIPTION
+The
+.Nm
+utility creates
+.Xr db 3
+style databases for use with
+.Fx Ns 's
+NIS server.
+The
+.Nm
+utility reads data from
+.Ar inputfile ,
+and writes it to
+.Ar dbname
+in
+.Xr db 3
+format (using the hash table method).
+The input should be in 'key data' format, which is to say
+two fields of
+.Tn ASCII
+data separated by white space.
+The first field
+is assumed to be the key, and everything else is assumed to be
+the data.
+These databases are typically stored in
+.Pa /var/yp/[domainname]
+where
+.Ar domainname
+is the name of the NIS domain being served.
+The
+.Nm
+utility is usually invoked by
+.Pa /var/yp/Makefile .
+The
+.Nm
+utility can also be used to dump an NIS database file so that its
+contents can be examined.
+For security reasons, all databases that
+.Nm
+creates are readable and writable by owner only (and usually the
+owner is root).
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl c
+Cause
+.Nm
+to send a YPPROC_CLEAR request to
+.Xr ypserv 8
+on the local host.
+This signal tells the server to close any open
+database descriptors and flush out its database cache.
+If used alone,
+this flag signals the server and does nothing else.
+If used as part
+of a database creation command,
+.Nm
+will send the signal only after the new database has been successfully
+created.
+.It Fl b
+Cause
+.Nm
+to add a special entry to the database with a key of
+.Em YP_INTERDOMAIN
+and an empty data field.
+If this key is present in a map, it alters the
+behavior of the 'match' procedure in
+.Xr ypserv 8
+slightly.
+If a match query fails (because the server could not find
+a record that matched the supplied key), and the
+.Em YP_INTERDOMAIN
+key exists within the queried map,
+.Xr ypserv 8
+will try to match the entry again using a DNS lookup.
+Note that this
+special behavior only applies to the
+.Em hosts
+maps.
+Using the
+.Fl b
+flag for other maps has no effect.
+.It Fl s
+This flag is used to add a special entry to the database with a key of
+.Em YP_SECURE
+and an empty data field.
+If this key is present in a map,
+.Xr ypserv 8
+will deny access to the map to any client that is not using a
+reserved port for its query.
+This is used mainly for the
+.Em master.passwd
+maps, which should be restricted to privileged access only.
+.It Fl f
+This flag is used to turn on filtering of lines in the source file
+input that start with ``+'' or ``-'' characters.
+These characters
+have special meaning for the
+.Pa group ,
+.Pa passwd
+and
+.Pa master.passwd
+maps and hence should not be allowed to appear in them as the first
+character of a key or datum.
+If the
+.Fl f
+flag is used,
+.Nm
+will reject any source line that starts with a ``+'' or ``-''
+character and issue a warning message displaying the line that
+was dropped.
+.It Fl u Ar dbname
+Dump (or 'unwind') an NIS database.
+This option can be used to
+inspect the contents of an existing NIS database.
+.It Fl i Ar inputfile
+When generating an NIS map, encode
+.Ar inputfile
+as a special entry in the database with a key of
+.Em YP_INPUT_FILE .
+.It Fl o Ar outputfile
+When generating an NIS map, encode
+.Ar outputfile
+as a special entry in the database with a key of
+.Em YP_OUTPUT_FILE .
+.It Fl d Ar domainname
+When generating an NIS map, encode
+.Ar domainname
+as a special entry in the database with a key of
+.Em YP_DOMAIN_NAME .
+.It Fl m Ar mastername
+When generating an NIS map, encode
+.Ar mastername
+as a special entry in the database with a key of
+.Em YP_MASTER_NAME .
+This entry in the database is frequently used by various NIS utilities
+to determine the name of an NIS master server for a domain.
+By default,
+.Nm
+assumes that the local host is the NIS master; the
+.Fl m
+option is used to override this default.
+.El
+.Sh FILES
+.Bl -tag -width /var/yp/Makefile -compact
+.It Pa /var/yp/Makefile
+the Makefile that calls
+.Nm
+to build the NIS databases
+.El
+.Sh SEE ALSO
+.Xr db 3 ,
+.Xr ypserv 8
+.Sh AUTHORS
+.An Bill Paul Aq wpaul@ctr.columbia.edu
diff --git a/usr.sbin/yp_mkdb/yp_mkdb.c b/usr.sbin/yp_mkdb/yp_mkdb.c
new file mode 100644
index 0000000..6c3014b
--- /dev/null
+++ b/usr.sbin/yp_mkdb/yp_mkdb.c
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/yp.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "yp_extern.h"
+#include "ypxfr_extern.h"
+
+char *yp_dir = ""; /* No particular default needed. */
+int _rpcpmstart = 0;
+int debug = 1;
+
+static void
+usage(void)
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n",
+ "usage: yp_mkdb -c",
+ " yp_mkdb -u dbname",
+ " yp_mkdb [-c] [-b] [-s] [-f] [-i inputfile] [-o outputfile]",
+ " [-d domainname ] [-m mastername] inputfile dbname");
+ exit(1);
+}
+
+#define PERM_SECURE (S_IRUSR|S_IWUSR)
+
+static DB *
+open_db(char *path, int flags)
+{
+ extern HASHINFO openinfo;
+
+ return(dbopen(path, flags, PERM_SECURE, DB_HASH, &openinfo));
+}
+
+static void
+unwind(char *map)
+{
+ DB *dbp;
+ DBT key, data;
+
+ dbp = open_db(map, O_RDONLY);
+
+ if (dbp == NULL)
+ err(1, "open_db(%s) failed", map);
+
+ key.data = NULL;
+ while (yp_next_record(dbp, &key, &data, 1, 1) == YP_TRUE)
+ printf("%.*s %.*s\n", key.size,key.data,data.size,data.data);
+
+ (void)(dbp->close)(dbp);
+ return;
+}
+
+int
+main(int argc, char *argv[])
+{
+ int ch;
+ int un = 0;
+ int clear = 0;
+ int filter_plusminus = 0;
+ char *infile = NULL;
+ char *map = NULL;
+ char *domain = NULL;
+ char *infilename = NULL;
+ char *outfilename = NULL;
+ char *mastername = NULL;
+ int interdom = 0;
+ int secure = 0;
+ DB *dbp;
+ DBT key, data;
+ char buf[10240];
+ char *keybuf, *datbuf;
+ FILE *ifp;
+ char hname[MAXHOSTNAMELEN + 2];
+
+ while ((ch = getopt(argc, argv, "uhcbsfd:i:o:m:")) != -1) {
+ switch (ch) {
+ case 'f':
+ filter_plusminus++;
+ break;
+ case 'u':
+ un++;
+ break;
+ case 'c':
+ clear++;
+ break;
+ case 'b':
+ interdom++;
+ break;
+ case 's':
+ secure++;
+ break;
+ case 'd':
+ domain = optarg;
+ break;
+ case 'i':
+ infilename = optarg;
+ break;
+ case 'o':
+ outfilename = optarg;
+ break;
+ case 'm':
+ mastername = optarg;
+ break;
+ case 'h':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (un) {
+ map = argv[0];
+ if (map == NULL)
+ usage();
+ unwind(map);
+ exit(0);
+
+ }
+
+ infile = argv[0];
+ map = argv[1];
+
+ if (infile == NULL || map == NULL) {
+ if (clear)
+ goto doclear;
+ usage();
+ }
+
+ if (mastername == NULL) {
+ if (gethostname((char *)&hname, sizeof(hname)) == -1)
+ err(1, "gethostname() failed");
+ mastername = (char *)&hname;
+ }
+
+ /*
+ * Note that while we can read from stdin, we can't
+ * write to stdout; the db library doesn't let you
+ * write to a file stream like that.
+ */
+
+ if (!strcmp(infile, "-")) {
+ ifp = stdin;
+ } else {
+ if ((ifp = fopen(infile, "r")) == NULL)
+ err(1, "failed to open %s", infile);
+ }
+
+ if ((dbp = open_db(map, O_RDWR|O_EXLOCK|O_EXCL|O_CREAT)) == NULL)
+ err(1, "open_db(%s) failed", map);
+
+ if (interdom) {
+ key.data = "YP_INTERDOMAIN";
+ key.size = sizeof("YP_INTERDOMAIN") - 1;
+ data.data = "";
+ data.size = 0;
+ yp_put_record(dbp, &key, &data, 0);
+ }
+
+ if (secure) {
+ key.data = "YP_SECURE";
+ key.size = sizeof("YP_SECURE") - 1;
+ data.data = "";
+ data.size = 0;
+ yp_put_record(dbp, &key, &data, 0);
+ }
+
+ key.data = "YP_MASTER_NAME";
+ key.size = sizeof("YP_MASTER_NAME") - 1;
+ data.data = mastername;
+ data.size = strlen(mastername);
+ yp_put_record(dbp, &key, &data, 0);
+
+ key.data = "YP_LAST_MODIFIED";
+ key.size = sizeof("YP_LAST_MODIFIED") - 1;
+ snprintf(buf, sizeof(buf), "%lu", time(NULL));
+ data.data = (char *)&buf;
+ data.size = strlen(buf);
+ yp_put_record(dbp, &key, &data, 0);
+
+ if (infilename) {
+ key.data = "YP_INPUT_FILE";
+ key.size = sizeof("YP_INPUT_FILE") - 1;
+ data.data = infilename;
+ data.size = strlen(infilename);
+ yp_put_record(dbp, &key, &data, 0);
+ }
+
+ if (outfilename) {
+ key.data = "YP_OUTPUT_FILE";
+ key.size = sizeof("YP_OUTPUT_FILE") - 1;
+ data.data = outfilename;
+ data.size = strlen(outfilename);
+ yp_put_record(dbp, &key, &data, 0);
+ }
+
+ if (domain) {
+ key.data = "YP_DOMAIN_NAME";
+ key.size = sizeof("YP_DOMAIN_NAME") - 1;
+ data.data = domain;
+ data.size = strlen(domain);
+ yp_put_record(dbp, &key, &data, 0);
+ }
+
+ while (fgets((char *)&buf, sizeof(buf), ifp)) {
+ char *sep = NULL;
+ int rval;
+
+ /* NUL terminate */
+ if ((sep = strchr(buf, '\n')))
+ *sep = '\0';
+
+ /* handle backslash line continuations */
+ while (buf[strlen(buf) - 1] == '\\') {
+ fgets((char *)&buf[strlen(buf) - 1],
+ sizeof(buf) - strlen(buf), ifp);
+ if ((sep = strchr(buf, '\n')))
+ *sep = '\0';
+ }
+
+ /* find the separation between the key and data */
+ if ((sep = strpbrk(buf, " \t")) == NULL) {
+ warnx("bad input -- no white space: %s", buf);
+ continue;
+ }
+
+ /* separate the strings */
+ keybuf = (char *)&buf;
+ datbuf = sep + 1;
+ *sep = '\0';
+
+ /* set datbuf to start at first non-whitespace character */
+ while (*datbuf == ' ' || *datbuf == '\t')
+ datbuf++;
+
+ /* Check for silliness. */
+ if (filter_plusminus) {
+ if (*keybuf == '+' || *keybuf == '-' ||
+ *datbuf == '+' || *datbuf == '-') {
+ warnx("bad character at "
+ "start of line: %s", buf);
+ continue;
+ }
+ }
+
+ if (strlen(keybuf) > YPMAXRECORD) {
+ warnx("key too long: %s", keybuf);
+ continue;
+ }
+
+ if (!strlen(keybuf)) {
+ warnx("no key -- check source file for blank lines");
+ continue;
+ }
+
+ if (strlen(datbuf) > YPMAXRECORD) {
+ warnx("data too long: %s", datbuf);
+ continue;
+ }
+
+ key.data = keybuf;
+ key.size = strlen(keybuf);
+ data.data = datbuf;
+ data.size = strlen(datbuf);
+
+ if ((rval = yp_put_record(dbp, &key, &data, 0)) != YP_TRUE) {
+ switch (rval) {
+ case YP_FALSE:
+ warnx("duplicate key '%s' - skipping", keybuf);
+ break;
+ case YP_BADDB:
+ default:
+ err(1,"failed to write new record - exiting");
+ break;
+ }
+ }
+
+ }
+
+ (void)(dbp->close)(dbp);
+
+doclear:
+
+ if (clear) {
+ char in = 0;
+ char *out = NULL;
+ int stat;
+ if ((stat = callrpc("localhost", YPPROG,YPVERS, YPPROC_CLEAR,
+ (xdrproc_t)xdr_void, &in,
+ (xdrproc_t)xdr_void, out)) != RPC_SUCCESS) {
+ warnx("failed to send 'clear' to local ypserv: %s",
+ clnt_sperrno((enum clnt_stat) stat));
+ }
+ }
+
+ exit(0);
+}
diff --git a/usr.sbin/ypbind/Makefile b/usr.sbin/ypbind/Makefile
new file mode 100644
index 0000000..915d001
--- /dev/null
+++ b/usr.sbin/ypbind/Makefile
@@ -0,0 +1,10 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $FreeBSD$
+
+PROG= ypbind
+MAN= ypbind.8
+SRCS= ypbind.c yp_ping.c
+
+CFLAGS+= -DDAEMON
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ypbind/yp_ping.c b/usr.sbin/ypbind/yp_ping.c
new file mode 100644
index 0000000..1453126
--- /dev/null
+++ b/usr.sbin/ypbind/yp_ping.c
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 1996, 1997
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#if 0
+#ifndef lint
+static char *sccsid = "@(#)from: clnt_udp.c 1.39 87/08/11 Copyr 1984 Sun Micro";
+static char *sccsid = "@(#)from: clnt_udp.c 2.2 88/08/01 4.0 RPCSRC";
+#endif
+#endif
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * clnt_udp.c, Implements a UDP/IP based, client side RPC.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/pmap_prot.h>
+#include <rpcsvc/yp.h>
+#include <sys/types.h>
+#include <sys/poll.h>
+#include <sys/socket.h>
+#include <sys/signal.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include <net/if.h>
+
+#include "yp_ping.h"
+
+/*
+ * pmap_getport.c
+ * Client interface to pmap rpc service.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+
+static struct timeval timeout = { 1, 0 };
+static struct timeval tottimeout = { 1, 0 };
+
+/*
+ * Find the mapped port for program,version.
+ * Calls the pmap service remotely to do the lookup.
+ * Returns 0 if no map exists.
+ */
+static u_short
+__pmap_getport(struct sockaddr_in *address, u_long program, u_long version,
+ u_int protocol)
+{
+ u_short port = 0;
+ int sock = -1;
+ register CLIENT *client;
+ struct pmap parms;
+
+ address->sin_port = htons(PMAPPORT);
+
+ client = clntudp_bufcreate(address, PMAPPROG,
+ PMAPVERS, timeout, &sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
+ if (client != (CLIENT *)NULL) {
+ parms.pm_prog = program;
+ parms.pm_vers = version;
+ parms.pm_prot = protocol;
+ parms.pm_port = 0; /* not needed or used */
+ if (CLNT_CALL(client, PMAPPROC_GETPORT,
+ (xdrproc_t)xdr_pmap, &parms,
+ (xdrproc_t)xdr_u_short, &port,
+ tottimeout) != RPC_SUCCESS){
+ rpc_createerr.cf_stat = RPC_PMAPFAILURE;
+ clnt_geterr(client, &rpc_createerr.cf_error);
+ } else if (port == 0) {
+ rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
+ }
+ CLNT_DESTROY(client);
+ }
+ if (sock != -1)
+ (void)close(sock);
+ address->sin_port = 0;
+ return (port);
+}
+
+/*
+ * Transmit to YPPROC_DOMAIN_NONACK, return immediately.
+ */
+static bool_t *
+ypproc_domain_nonack_2_send(domainname *argp, CLIENT *clnt)
+{
+ static bool_t clnt_res;
+ struct timeval TIMEOUT = { 0, 0 };
+
+ memset((char *)&clnt_res, 0, sizeof (clnt_res));
+ if (clnt_call(clnt, YPPROC_DOMAIN_NONACK,
+ (xdrproc_t) xdr_domainname, (caddr_t) argp,
+ (xdrproc_t) xdr_bool, (caddr_t) &clnt_res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&clnt_res);
+}
+
+/*
+ * Receive response from YPPROC_DOMAIN_NONACK asynchronously.
+ */
+static bool_t *
+ypproc_domain_nonack_2_recv(domainname *argp, CLIENT *clnt)
+{
+ static bool_t clnt_res;
+ struct timeval TIMEOUT = { 0, 0 };
+
+ memset((char *)&clnt_res, 0, sizeof (clnt_res));
+ if (clnt_call(clnt, YPPROC_DOMAIN_NONACK,
+ (xdrproc_t) NULL, (caddr_t) argp,
+ (xdrproc_t) xdr_bool, (caddr_t) &clnt_res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&clnt_res);
+}
+
+/*
+ * "We have the machine that goes 'ping!'" -- Monty Python
+ *
+ * This function blasts packets at the YPPROC_DOMAIN_NONACK procedures
+ * of the NIS servers listed in restricted_addrs structure.
+ * Whoever replies the fastest becomes our chosen server.
+ *
+ * Note: THIS IS NOT A BROADCAST OPERATION! We could use clnt_broadcast()
+ * for this, but that has the following problems:
+ * - We only get the address of the machine that replied in the
+ * 'eachresult' callback, and on multi-homed machines this can
+ * lead to confusion.
+ * - clnt_broadcast() only transmits to local networks, whereas with
+ * NIS+ you can have a perfectly good server located anywhere on or
+ * off the local network.
+ * - clnt_broadcast() blocks for an arbitrary amount of time which the
+ * caller can't control -- we want to avoid that.
+ *
+ * Also note that this has nothing to do with the NIS_PING procedure used
+ * for replica updates.
+ */
+
+struct ping_req {
+ struct sockaddr_in sin;
+ u_int32_t xid;
+};
+
+int
+__yp_ping(struct in_addr *restricted_addrs, int cnt, char *dom, short *port)
+{
+ struct timeval tv = { 5, 0 };
+ struct ping_req **reqs;
+ unsigned long i;
+ int async;
+ struct sockaddr_in sin, *any = NULL;
+ struct netbuf addr;
+ int winner = -1;
+ u_int32_t xid_seed, xid_lookup;
+ int sock, dontblock = 1;
+ CLIENT *clnt;
+ char *foo = dom;
+ int validsrvs = 0;
+
+ /* Set up handles. */
+ reqs = calloc(1, sizeof(struct ping_req *) * cnt);
+ xid_seed = time(NULL) ^ getpid();
+
+ for (i = 0; i < cnt; i++) {
+ bzero((char *)&sin, sizeof(sin));
+ sin.sin_family = AF_INET;
+ bcopy((char *)&restricted_addrs[i],
+ (char *)&sin.sin_addr, sizeof(struct in_addr));
+ sin.sin_port = htons(__pmap_getport(&sin, YPPROG,
+ YPVERS, IPPROTO_UDP));
+ if (sin.sin_port == 0)
+ continue;
+ reqs[i] = calloc(1, sizeof(struct ping_req));
+ bcopy((char *)&sin, (char *)&reqs[i]->sin, sizeof(sin));
+ any = &reqs[i]->sin;
+ reqs[i]->xid = xid_seed;
+ xid_seed++;
+ validsrvs++;
+ }
+
+ /* Make sure at least one server was assigned */
+ if (!validsrvs) {
+ free(reqs);
+ return(-1);
+ }
+
+ /* Create RPC handle */
+ sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ clnt = clntudp_create(any, YPPROG, YPVERS, tv, &sock);
+ if (clnt == NULL) {
+ close(sock);
+ for (i = 0; i < cnt; i++)
+ if (reqs[i] != NULL)
+ free(reqs[i]);
+ free(reqs);
+ return(-1);
+ }
+ clnt->cl_auth = authunix_create_default();
+ tv.tv_sec = 0;
+
+ clnt_control(clnt, CLSET_TIMEOUT, (char *)&tv);
+ async = TRUE;
+ clnt_control(clnt, CLSET_ASYNC, (char *)&async);
+ ioctl(sock, FIONBIO, &dontblock);
+
+ /* Transmit */
+ for (i = 0; i < cnt; i++) {
+ if (reqs[i] != NULL) {
+ clnt_control(clnt, CLSET_XID, (char *)&reqs[i]->xid);
+ addr.len = sizeof(reqs[i]->sin);
+ addr.buf = (char *) &reqs[i]->sin;
+ clnt_control(clnt, CLSET_SVC_ADDR, &addr);
+ ypproc_domain_nonack_2_send(&foo, clnt);
+ }
+ }
+
+ /* Receive reply */
+ ypproc_domain_nonack_2_recv(&foo, clnt);
+
+ /* Got a winner -- look him up. */
+ clnt_control(clnt, CLGET_XID, (char *)&xid_lookup);
+ for (i = 0; i < cnt; i++) {
+ if (reqs[i] != NULL && reqs[i]->xid == xid_lookup) {
+ winner = i;
+ *port = reqs[i]->sin.sin_port;
+ }
+ }
+
+ /* Shut everything down */
+ auth_destroy(clnt->cl_auth);
+ clnt_destroy(clnt);
+ close(sock);
+
+ for (i = 0; i < cnt; i++)
+ if (reqs[i] != NULL)
+ free(reqs[i]);
+ free(reqs);
+
+ return(winner);
+}
diff --git a/usr.sbin/ypbind/yp_ping.h b/usr.sbin/ypbind/yp_ping.h
new file mode 100644
index 0000000..80e6a78
--- /dev/null
+++ b/usr.sbin/ypbind/yp_ping.h
@@ -0,0 +1,5 @@
+/*
+ * $FreeBSD$
+ */
+
+extern int __yp_ping(struct in_addr *, int, char *, short *);
diff --git a/usr.sbin/ypbind/ypbind.8 b/usr.sbin/ypbind/ypbind.8
new file mode 100644
index 0000000..4608814
--- /dev/null
+++ b/usr.sbin/ypbind/ypbind.8
@@ -0,0 +1,202 @@
+.\" Copyright (c) 1991, 1993, 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$
+.\"
+.Dd April 9, 1995
+.Dt YPBIND 8
+.Os
+.Sh NAME
+.Nm ypbind
+.Nd "NIS domain binding daemon"
+.Sh SYNOPSIS
+.Nm
+.Op Fl ypset
+.Op Fl ypsetme
+.Op Fl s
+.Op Fl m
+.Oo
+.Fl S
+.Sm off
+.Ar domainname , server1 , server2 , ...
+.Sm on
+.Oc
+.Sh DESCRIPTION
+The
+.Nm
+utility is the process that maintains NIS binding information.
+At startup,
+it searches for an NIS server responsible for serving the system's
+default domain (as set by the
+.Xr domainname 1
+command) using network broadcasts.
+Once it receives a reply,
+it will store the address of the server and other
+information in a special file located in
+.Pa /var/yp/binding .
+The NIS routines in the standard C library can then use this file
+when processing NIS requests.
+There may be several such files
+since it is possible for an NIS client to be bound to more than
+one domain.
+.Pp
+After a binding has been established,
+.Nm
+will send DOMAIN_NONACK requests to the NIS server at one minute
+intervals.
+If it fails to receive a reply to one of these requests,
+.Nm
+assumes that the server is no longer running and resumes its network
+broadcasts until another binding is established.
+The
+.Nm
+utility will also log warning messages using the
+.Xr syslog 3
+facility each time it detects that a server has stopped responding,
+as well as when it has bound to a new server.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl ypset
+It is possible to force
+.Nm
+to bind to a particular NIS server host for a given domain by using the
+.Xr ypset 8
+command.
+However,
+.Nm
+refuses YPBINDPROC_SETDOM requests by default since it has no way of
+knowing exactly who is sending them.
+Using the
+.Fl ypset
+flag causes
+.Nm
+to accept YPBINDPROC_SETDOM requests from any host.
+This option should only
+be used for diagnostic purposes and only for limited periods since allowing
+arbitrary users to reset the binding of an NIS client poses a severe
+security risk.
+.It Fl ypsetme
+This is similar to the
+.Fl ypset
+flag, except that it only permits YPBINDPROC_SETDOM requests to be processed
+if they originated from the local host.
+.It Fl s
+Cause
+.Nm
+to run in secure mode: it will refuse to bind to any NIS server
+that is not running as root (i.e., that is not using privileged
+TCP ports).
+.It Fl S Xo
+.Sm off
+.Ar domainname , server1 , server2 , server3 , ...
+.Sm on
+.Xc
+Allow the system administrator to lock
+.Nm
+to a particular
+domain and group of NIS servers.
+Up to ten servers can be specified.
+There must not be any spaces between the commas in the domain/server
+specification.
+This option is used to insure that the system binds
+only to one domain and only to one of the specified servers, which
+is useful for systems that are both NIS servers and NIS
+clients: it provides a way to restrict what machines the system can
+bind to without the need for specifying the
+.Fl ypset
+or
+.Fl ypsetme
+options, which are often considered to be security holes.
+The specified
+servers must have valid entries in the local
+.Pa /etc/hosts
+file.
+IP addresses may be specified in place of hostnames.
+If
+.Nm
+cannot make sense out of the arguments, it will ignore
+the
+.Fl S
+flag and continue running normally.
+.Pp
+Note that
+.Nm
+will consider the domainname specified with the
+.Fl S
+flag to be the system default domain.
+.It Fl m
+Cause
+.Nm
+to use a 'many-cast' rather than a broadcast for choosing a server
+from the restricted mode server list.
+In many-cast mode,
+.Nm
+will transmit directly to the YPPROC_DOMAIN_NONACK procedure of the
+servers specified in the restricted list and bind to the server that
+responds the fastest.
+This mode of operation is useful for NIS clients on remote subnets
+where no local NIS servers are available.
+The
+.Fl m
+flag can only be used in conjunction with the
+.Fl S
+flag above (if used without the
+.Fl S
+flag, it has no effect).
+.El
+.Sh NOTES
+The
+.Nm
+utility will not make continuous attempts to keep secondary domains bound.
+If a server for a secondary domain fails to respond to a ping,
+.Nm
+will broadcast for a new server only once before giving up.
+If a
+client program attempts to reference the unbound domain,
+.Nm
+will try broadcasting again.
+By contrast,
+.Nm
+will automatically maintain a binding for the default domain whether
+client programs reference it ot not.
+.Sh FILES
+.Bl -tag -width /etc/rc.conf -compact
+.It Pa /var/yp/binding/[domainname].[version]
+the files used to hold binding information for each NIS domain
+.It Pa /etc/rc.conf
+system configuration file where the system default domain and
+ypbind startup options are specified
+.El
+.Sh SEE ALSO
+.Xr domainname 1 ,
+.Xr syslog 3 ,
+.Xr yp 8 ,
+.Xr ypserv 8 ,
+.Xr ypset 8
+.Sh AUTHORS
+.An Theo de Raadt Aq deraadt@fsa.ca
diff --git a/usr.sbin/ypbind/ypbind.c b/usr.sbin/ypbind/ypbind.c
new file mode 100644
index 0000000..a3a7dc3
--- /dev/null
+++ b/usr.sbin/ypbind/ypbind.c
@@ -0,0 +1,990 @@
+/*
+ * Copyright (c) 1992/3 Theo de Raadt <deraadt@fsa.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <sys/signal.h>
+#include <sys/socket.h>
+#include <sys/file.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <ctype.h>
+#include <dirent.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 <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/pmap_prot.h>
+#include <rpc/pmap_rmt.h>
+#include <rpc/rpc_com.h>
+#include <rpcsvc/yp.h>
+#include <rpcsvc/ypclnt.h>
+#include "yp_ping.h"
+
+#ifndef BINDINGDIR
+#define BINDINGDIR "/var/yp/binding"
+#endif
+
+#ifndef YPBINDLOCK
+#define YPBINDLOCK "/var/run/ypbind.lock"
+#endif
+
+struct _dom_binding {
+ struct _dom_binding *dom_pnext;
+ char dom_domain[YPMAXDOMAIN + 1];
+ struct sockaddr_in dom_server_addr;
+ long int dom_vers;
+ int dom_lockfd;
+ int dom_alive;
+ int dom_broadcast_pid;
+ int dom_pipe_fds[2];
+ int dom_default;
+};
+
+#define READFD ypdb->dom_pipe_fds[0]
+#define WRITEFD ypdb->dom_pipe_fds[1]
+#define BROADFD broad_domain->dom_pipe_fds[1]
+
+extern bool_t xdr_domainname(), xdr_ypbind_resp();
+extern bool_t xdr_ypreq_key(), xdr_ypresp_val();
+extern bool_t xdr_ypbind_setdom();
+
+void checkwork(void);
+void *ypbindproc_null_2_yp(SVCXPRT *, void *, CLIENT *);
+void *ypbindproc_setdom_2_yp(SVCXPRT *, struct ypbind_setdom *, CLIENT *);
+void rpc_received(char *, struct sockaddr_in *, int);
+void broadcast(struct _dom_binding *);
+int ping(struct _dom_binding *);
+int tell_parent(char *, struct sockaddr_in *);
+void handle_children(struct _dom_binding *);
+void reaper(int);
+void terminate(int);
+void yp_restricted_mode(char *);
+int verify(struct in_addr);
+
+char *domain_name;
+struct _dom_binding *ypbindlist;
+static struct _dom_binding *broad_domain;
+
+#define YPSET_NO 0
+#define YPSET_LOCAL 1
+#define YPSET_ALL 2
+int ypsetmode = YPSET_NO;
+int ypsecuremode = 0;
+int ppid;
+
+/*
+ * Special restricted mode variables: when in restricted mode, only the
+ * specified restricted_domain will be bound, and only the servers listed
+ * in restricted_addrs will be used for binding.
+ */
+#define RESTRICTED_SERVERS 10
+int yp_restricted = 0;
+int yp_manycast = 0;
+struct in_addr restricted_addrs[RESTRICTED_SERVERS];
+
+/* No more than MAX_CHILDREN child broadcasters at a time. */
+#ifndef MAX_CHILDREN
+#define MAX_CHILDREN 5
+#endif
+/* No more than MAX_DOMAINS simultaneous domains */
+#ifndef MAX_DOMAINS
+#define MAX_DOMAINS 200
+#endif
+/* RPC timeout value */
+#ifndef FAIL_THRESHOLD
+#define FAIL_THRESHOLD 20
+#endif
+
+/* Number of times to fish for a response froma particular set of hosts */
+#ifndef MAX_RETRIES
+#define MAX_RETRIES 30
+#endif
+
+int retries = 0;
+int children = 0;
+int domains = 0;
+int yplockfd;
+fd_set fdsr;
+
+SVCXPRT *udptransp, *tcptransp;
+
+void *
+ypbindproc_null_2_yp(SVCXPRT *transp, void *argp, CLIENT *clnt)
+{
+ static char res;
+
+ bzero(&res, sizeof(res));
+ return &res;
+}
+
+struct ypbind_resp *
+ypbindproc_domain_2_yp(SVCXPRT *transp, domainname *argp, CLIENT *clnt)
+{
+ static struct ypbind_resp res;
+ struct _dom_binding *ypdb;
+ char path[MAXPATHLEN];
+
+ bzero(&res, sizeof res);
+ res.ypbind_status = YPBIND_FAIL_VAL;
+ res.ypbind_resp_u.ypbind_error = YPBIND_ERR_NOSERV;
+
+ if (strchr(*argp, '/')) {
+ syslog(LOG_WARNING, "Domain name '%s' has embedded slash -- \
+rejecting.", *argp);
+ return(&res);
+ }
+
+ for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext) {
+ if (strcmp(ypdb->dom_domain, *argp) == 0)
+ break;
+ }
+
+ if (ypdb == NULL) {
+ if (yp_restricted) {
+ syslog(LOG_NOTICE, "Running in restricted mode -- request to bind domain \"%s\" rejected.\n", *argp);
+ return (&res);
+ }
+
+ if (domains >= MAX_DOMAINS) {
+ syslog(LOG_WARNING, "domain limit (%d) exceeded",
+ MAX_DOMAINS);
+ res.ypbind_resp_u.ypbind_error = YPBIND_ERR_RESC;
+ return (&res);
+ }
+ ypdb = (struct _dom_binding *)malloc(sizeof *ypdb);
+ if (ypdb == NULL) {
+ syslog(LOG_WARNING, "malloc: %m");
+ res.ypbind_resp_u.ypbind_error = YPBIND_ERR_RESC;
+ return (&res);
+ }
+ bzero(ypdb, sizeof *ypdb);
+ strncpy(ypdb->dom_domain, *argp, sizeof ypdb->dom_domain);
+ ypdb->dom_vers = YPVERS;
+ ypdb->dom_alive = 0;
+ ypdb->dom_default = 0;
+ ypdb->dom_lockfd = -1;
+ sprintf(path, "%s/%s.%ld", BINDINGDIR,
+ ypdb->dom_domain, ypdb->dom_vers);
+ unlink(path);
+ ypdb->dom_pnext = ypbindlist;
+ ypbindlist = ypdb;
+ domains++;
+ }
+
+ if (ping(ypdb)) {
+ return (&res);
+ }
+
+ res.ypbind_status = YPBIND_SUCC_VAL;
+ res.ypbind_resp_u.ypbind_error = 0; /* Success */
+ *(u_int32_t *)&res.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr =
+ ypdb->dom_server_addr.sin_addr.s_addr;
+ *(u_short *)&res.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port =
+ ypdb->dom_server_addr.sin_port;
+ /*printf("domain %s at %s/%d\n", ypdb->dom_domain,
+ inet_ntoa(ypdb->dom_server_addr.sin_addr),
+ ntohs(ypdb->dom_server_addr.sin_port));*/
+ return (&res);
+}
+
+void *
+ypbindproc_setdom_2_yp(SVCXPRT *transp, ypbind_setdom *argp, CLIENT *clnt)
+{
+ struct sockaddr_in *fromsin, bindsin;
+ static char *result = NULL;
+
+ if (strchr(argp->ypsetdom_domain, '/')) {
+ syslog(LOG_WARNING, "Domain name '%s' has embedded slash -- \
+rejecting.", argp->ypsetdom_domain);
+ return(NULL);
+ }
+ fromsin = svc_getcaller(transp);
+
+ switch (ypsetmode) {
+ case YPSET_LOCAL:
+ if (fromsin->sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
+ svcerr_noprog(transp);
+ return(NULL);
+ }
+ break;
+ case YPSET_ALL:
+ break;
+ case YPSET_NO:
+ default:
+ svcerr_noprog(transp);
+ return(NULL);
+ }
+
+ if (ntohs(fromsin->sin_port) >= IPPORT_RESERVED) {
+ svcerr_noprog(transp);
+ return(NULL);
+ }
+
+ if (argp->ypsetdom_vers != YPVERS) {
+ svcerr_noprog(transp);
+ return(NULL);
+ }
+
+ bzero(&bindsin, sizeof bindsin);
+ bindsin.sin_family = AF_INET;
+ bindsin.sin_addr.s_addr = *(u_int32_t *)argp->ypsetdom_binding.ypbind_binding_addr;
+ bindsin.sin_port = *(u_short *)argp->ypsetdom_binding.ypbind_binding_port;
+ rpc_received(argp->ypsetdom_domain, &bindsin, 1);
+
+ return((void *) &result);
+}
+
+void
+ypbindprog_2(struct svc_req *rqstp, register SVCXPRT *transp)
+{
+ union {
+ domainname ypbindproc_domain_2_arg;
+ struct ypbind_setdom ypbindproc_setdom_2_arg;
+ } argument;
+ struct authunix_parms *creds;
+ char *result;
+ bool_t (*xdr_argument)(), (*xdr_result)();
+ char *(*local)();
+
+ switch (rqstp->rq_proc) {
+ case YPBINDPROC_NULL:
+ xdr_argument = xdr_void;
+ xdr_result = xdr_void;
+ local = (char *(*)()) ypbindproc_null_2_yp;
+ break;
+
+ case YPBINDPROC_DOMAIN:
+ xdr_argument = xdr_domainname;
+ xdr_result = xdr_ypbind_resp;
+ local = (char *(*)()) ypbindproc_domain_2_yp;
+ break;
+
+ case YPBINDPROC_SETDOM:
+ switch (rqstp->rq_cred.oa_flavor) {
+ case AUTH_UNIX:
+ creds = (struct authunix_parms *)rqstp->rq_clntcred;
+ if (creds->aup_uid != 0) {
+ svcerr_auth(transp, AUTH_BADCRED);
+ return;
+ }
+ break;
+ default:
+ svcerr_auth(transp, AUTH_TOOWEAK);
+ return;
+ }
+
+ xdr_argument = xdr_ypbind_setdom;
+ xdr_result = xdr_void;
+ local = (char *(*)()) ypbindproc_setdom_2_yp;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+ bzero(&argument, sizeof(argument));
+ if (!svc_getargs(transp, (xdrproc_t)xdr_argument, &argument)) {
+ svcerr_decode(transp);
+ return;
+ }
+ result = (*local)(transp, &argument, rqstp);
+ if (result != NULL &&
+ !svc_sendreply(transp, (xdrproc_t)xdr_result, result)) {
+ svcerr_systemerr(transp);
+ }
+ return;
+}
+
+/* Jack the reaper */
+void
+reaper(int sig)
+{
+ int st;
+
+ while (wait3(&st, WNOHANG, NULL) > 0)
+ children--;
+}
+
+void
+terminate(int sig)
+{
+ struct _dom_binding *ypdb;
+ char path[MAXPATHLEN];
+
+ if (ppid != getpid())
+ exit(0);
+
+ for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext) {
+ close(ypdb->dom_lockfd);
+ if (ypdb->dom_broadcast_pid)
+ kill(ypdb->dom_broadcast_pid, SIGINT);
+ sprintf(path, "%s/%s.%ld", BINDINGDIR,
+ ypdb->dom_domain, ypdb->dom_vers);
+ unlink(path);
+ }
+ close(yplockfd);
+ unlink(YPBINDLOCK);
+ pmap_unset(YPBINDPROG, YPBINDVERS);
+ exit(0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct timeval tv;
+ int i;
+ DIR *dird;
+ struct dirent *dirp;
+ struct _dom_binding *ypdb, *next;
+
+ /* Check that another ypbind isn't already running. */
+ if ((yplockfd = (open(YPBINDLOCK, O_RDONLY|O_CREAT, 0444))) == -1)
+ err(1, "%s", YPBINDLOCK);
+
+ if (flock(yplockfd, LOCK_EX|LOCK_NB) == -1 && errno == EWOULDBLOCK)
+ errx(1, "another ypbind is already running. Aborting");
+
+ /* XXX domainname will be overriden if we use restricted mode */
+ yp_get_default_domain(&domain_name);
+ if (domain_name[0] == '\0')
+ errx(1, "domainname not set. Aborting");
+
+ for (i = 1; i<argc; i++) {
+ if (strcmp("-ypset", argv[i]) == 0)
+ ypsetmode = YPSET_ALL;
+ else if (strcmp("-ypsetme", argv[i]) == 0)
+ ypsetmode = YPSET_LOCAL;
+ else if (strcmp("-s", argv[i]) == 0)
+ ypsecuremode++;
+ else if (strcmp("-S", argv[i]) == 0 && argc > i)
+ yp_restricted_mode(argv[++i]);
+ else if (strcmp("-m", argv[i]) == 0)
+ yp_manycast++;
+ else
+ errx(1, "unknown option: %s", argv[i]);
+ }
+
+ /* blow away everything in BINDINGDIR (if it exists) */
+
+ if ((dird = opendir(BINDINGDIR)) != NULL) {
+ char path[MAXPATHLEN];
+ while ((dirp = readdir(dird)) != NULL)
+ if (strcmp(dirp->d_name, ".") &&
+ strcmp(dirp->d_name, "..")) {
+ sprintf(path,"%s/%s",BINDINGDIR,dirp->d_name);
+ unlink(path);
+ }
+ closedir(dird);
+ }
+
+#ifdef DAEMON
+ if (daemon(0,0))
+ err(1, "fork");
+#endif
+
+ pmap_unset(YPBINDPROG, YPBINDVERS);
+
+ udptransp = svcudp_create(RPC_ANYSOCK);
+ if (udptransp == NULL)
+ errx(1, "cannot create udp service");
+ if (!svc_register(udptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2,
+ IPPROTO_UDP))
+ errx(1, "unable to register (YPBINDPROG, YPBINDVERS, udp)");
+
+ tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0);
+ if (tcptransp == NULL)
+ errx(1, "cannot create tcp service");
+
+ if (!svc_register(tcptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2,
+ IPPROTO_TCP))
+ errx(1, "unable to register (YPBINDPROG, YPBINDVERS, tcp)");
+
+ /* build initial domain binding, make it "unsuccessful" */
+ ypbindlist = (struct _dom_binding *)malloc(sizeof *ypbindlist);
+ if (ypbindlist == NULL)
+ errx(1, "malloc");
+ bzero(ypbindlist, sizeof *ypbindlist);
+ strncpy(ypbindlist->dom_domain, domain_name, sizeof ypbindlist->dom_domain);
+ ypbindlist->dom_vers = YPVERS;
+ ypbindlist->dom_alive = 0;
+ ypbindlist->dom_lockfd = -1;
+ ypbindlist->dom_default = 1;
+ domains++;
+
+ signal(SIGCHLD, reaper);
+ signal(SIGTERM, terminate);
+
+ ppid = getpid(); /* Remember who we are. */
+
+ openlog(argv[0], LOG_PID, LOG_DAEMON);
+
+ /* Kick off the default domain */
+ broadcast(ypbindlist);
+
+ while (1) {
+ fdsr = svc_fdset;
+
+ tv.tv_sec = 60;
+ tv.tv_usec = 0;
+
+ switch (select(_rpc_dtablesize(), &fdsr, NULL, NULL, &tv)) {
+ case 0:
+ checkwork();
+ break;
+ case -1:
+ if (errno != EINTR)
+ syslog(LOG_WARNING, "select: %m");
+ break;
+ default:
+ for (ypdb = ypbindlist; ypdb; ypdb = next) {
+ next = ypdb->dom_pnext;
+ if (READFD > 0 && FD_ISSET(READFD, &fdsr)) {
+ handle_children(ypdb);
+ if (children == (MAX_CHILDREN - 1))
+ checkwork();
+ }
+ }
+ svc_getreqset(&fdsr);
+ break;
+ }
+ }
+
+ /* NOTREACHED */
+ exit(1);
+}
+
+void
+checkwork(void)
+{
+ struct _dom_binding *ypdb;
+
+ for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext)
+ ping(ypdb);
+}
+
+/* The clnt_broadcast() callback mechanism sucks. */
+
+/*
+ * Receive results from broadcaster. Don't worry about passing
+ * bogus info to rpc_received() -- it can handle it. Note that we
+ * must be sure to invalidate the dom_pipe_fds descriptors here:
+ * since descriptors can be re-used, we have to make sure we
+ * don't mistake one of the RPC descriptors for one of the pipes.
+ * What's weird is that forgetting to invalidate the pipe descriptors
+ * doesn't always result in an error (otherwise I would have caught
+ * the mistake much sooner), even though logically it should.
+ */
+void
+handle_children(struct _dom_binding *ypdb)
+{
+ char buf[YPMAXDOMAIN + 1];
+ struct sockaddr_in addr;
+ int d = 0, a = 0;
+ struct _dom_binding *y, *prev = NULL;
+ char path[MAXPATHLEN];
+
+ if ((d = read(READFD, &buf, sizeof(buf))) <= 0)
+ syslog(LOG_WARNING, "could not read from child: %m");
+
+ if ((a = read(READFD, &addr, sizeof(struct sockaddr_in))) < 0)
+ syslog(LOG_WARNING, "could not read from child: %m");
+
+ close(READFD);
+ FD_CLR(READFD, &fdsr);
+ FD_CLR(READFD, &svc_fdset);
+ READFD = WRITEFD = -1;
+ if (d > 0 && a > 0)
+ rpc_received(buf, &addr, 0);
+ else {
+ for (y = ypbindlist; y; y = y->dom_pnext) {
+ if (y == ypdb)
+ break;
+ prev = y;
+ }
+ switch (ypdb->dom_default) {
+ case 0:
+ if (prev == NULL)
+ ypbindlist = y->dom_pnext;
+ else
+ prev->dom_pnext = y->dom_pnext;
+ sprintf(path, "%s/%s.%ld", BINDINGDIR,
+ ypdb->dom_domain, YPVERS);
+ close(ypdb->dom_lockfd);
+ unlink(path);
+ free(ypdb);
+ domains--;
+ return;
+ case 1:
+ ypdb->dom_broadcast_pid = 0;
+ ypdb->dom_alive = 0;
+ broadcast(ypdb);
+ return;
+ default:
+ break;
+ }
+ }
+
+ return;
+}
+
+/*
+ * Send our dying words back to our parent before we perish.
+ */
+int
+tell_parent(char *dom, struct sockaddr_in *addr)
+{
+ char buf[YPMAXDOMAIN + 1];
+ struct timeval timeout;
+ fd_set fds;
+
+ timeout.tv_sec = 5;
+ timeout.tv_usec = 0;
+
+ sprintf(buf, "%s", broad_domain->dom_domain);
+ if (write(BROADFD, &buf, sizeof(buf)) < 0)
+ return(1);
+
+ /*
+ * Stay in sync with parent: wait for it to read our first
+ * message before sending the second.
+ */
+
+ FD_ZERO(&fds);
+ FD_SET(BROADFD, &fds);
+ if (select(FD_SETSIZE, NULL, &fds, NULL, &timeout) == -1)
+ return(1);
+ if (FD_ISSET(BROADFD, &fds)) {
+ if (write(BROADFD, addr, sizeof(struct sockaddr_in)) < 0)
+ return(1);
+ } else {
+ return(1);
+ }
+
+ close(BROADFD);
+ return (0);
+}
+
+bool_t broadcast_result(out, addr)
+bool_t *out;
+struct sockaddr_in *addr;
+{
+ if (retries >= MAX_RETRIES) {
+ bzero(addr, sizeof(struct sockaddr_in));
+ if (tell_parent(broad_domain->dom_domain, addr))
+ syslog(LOG_WARNING, "lost connection to parent");
+ return (TRUE);
+ }
+
+ if (yp_restricted && verify(addr->sin_addr)) {
+ retries++;
+ syslog(LOG_NOTICE, "NIS server at %s not in restricted mode access list -- rejecting.\n",inet_ntoa(addr->sin_addr));
+ return (FALSE);
+ } else {
+ if (tell_parent(broad_domain->dom_domain, addr))
+ syslog(LOG_WARNING, "lost connection to parent");
+ return (TRUE);
+ }
+}
+
+/*
+ * The right way to send RPC broadcasts.
+ * Use the clnt_broadcast() RPC service. Unfortunately, clnt_broadcast()
+ * blocks while waiting for replies, so we have to fork off separate
+ * broadcaster processes that do the waiting and then transmit their
+ * results back to the parent for processing. We also have to remember
+ * to save the name of the domain we're trying to bind in a global
+ * variable since clnt_broadcast() provides no way to pass things to
+ * the 'eachresult' callback function.
+ */
+void
+broadcast(struct _dom_binding *ypdb)
+{
+ bool_t out = FALSE;
+ enum clnt_stat stat;
+
+ if (children >= MAX_CHILDREN || ypdb->dom_broadcast_pid)
+ return;
+
+ if (pipe(ypdb->dom_pipe_fds) < 0) {
+ syslog(LOG_WARNING, "pipe: %m");
+ return;
+ }
+
+ if (ypdb->dom_vers == -1 && (long)ypdb->dom_server_addr.sin_addr.s_addr)
+ syslog(LOG_WARNING, "NIS server [%s] for domain \"%s\" not responding",
+ inet_ntoa(ypdb->dom_server_addr.sin_addr), ypdb->dom_domain);
+
+ broad_domain = ypdb;
+ flock(ypdb->dom_lockfd, LOCK_UN);
+
+ switch ((ypdb->dom_broadcast_pid = fork())) {
+ case 0:
+ close(READFD);
+ signal(SIGCHLD, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ break;
+ case -1:
+ syslog(LOG_WARNING, "fork: %m");
+ close(READFD);
+ close(WRITEFD);
+ return;
+ default:
+ close(WRITEFD);
+ FD_SET(READFD, &svc_fdset);
+ children++;
+ return;
+ }
+
+ /* Release all locks before doing anything else. */
+ while (ypbindlist) {
+ close(ypbindlist->dom_lockfd);
+ ypbindlist = ypbindlist->dom_pnext;
+ }
+ close(yplockfd);
+
+ /*
+ * Special 'many-cast' behavior. If we're in restricted mode,
+ * we have a list of possible server addresses to try. What
+ * we can do is transmit to each ypserv's YPPROC_DOMAIN_NONACK
+ * procedure and time the replies. Whoever replies fastest
+ * gets to be our server. Note that this is not a broadcast
+ * operation: we transmit uni-cast datagrams only.
+ */
+ if (yp_restricted && yp_manycast) {
+ short port;
+ int i;
+ struct sockaddr_in sin;
+
+ i = __yp_ping(restricted_addrs, yp_restricted,
+ ypdb->dom_domain, &port);
+ if (i == -1) {
+ bzero(&ypdb->dom_server_addr,
+ sizeof(struct sockaddr_in));
+ if (tell_parent(ypdb->dom_domain,
+ &ypdb->dom_server_addr))
+ syslog(LOG_WARNING, "lost connection to parent");
+ } else {
+ bzero(&sin, sizeof(struct sockaddr_in));
+ bcopy(&restricted_addrs[i],
+ &sin.sin_addr, sizeof(struct in_addr));
+ sin.sin_family = AF_INET;
+ sin.sin_port = port;
+ if (tell_parent(broad_domain->dom_domain, &sin))
+ syslog(LOG_WARNING,
+ "lost connection to parent");
+ }
+ _exit(0);
+ }
+
+ retries = 0;
+
+ {
+ char *ptr;
+
+ ptr = ypdb->dom_domain;
+ stat = clnt_broadcast(YPPROG, YPVERS, YPPROC_DOMAIN_NONACK,
+ (xdrproc_t)xdr_domainname, &ptr,
+ (xdrproc_t)xdr_bool, &out,
+ (resultproc_t)broadcast_result);
+ }
+
+ if (stat != RPC_SUCCESS) {
+ bzero(&ypdb->dom_server_addr,
+ sizeof(struct sockaddr_in));
+ if (tell_parent(ypdb->dom_domain, &ypdb->dom_server_addr))
+ syslog(LOG_WARNING, "lost connection to parent");
+ }
+
+ _exit(0);
+}
+
+/*
+ * The right way to check if a server is alive.
+ * Attempt to get a client handle pointing to the server and send a
+ * YPPROC_DOMAIN. If we can't get a handle or we get a reply of FALSE,
+ * we invalidate this binding entry and send out a broadcast to try to
+ * establish a new binding. Note that we treat non-default domains
+ * specially: once bound, we keep tabs on our server, but if it
+ * goes away and fails to respond after one round of broadcasting, we
+ * abandon it until a client specifically references it again. We make
+ * every effort to keep our default domain bound, however, since we
+ * need it to keep the system on its feet.
+ */
+int
+ping(struct _dom_binding *ypdb)
+{
+ bool_t out;
+ struct timeval interval, timeout;
+ enum clnt_stat stat;
+ int rpcsock = RPC_ANYSOCK;
+ CLIENT *client_handle;
+
+ interval.tv_sec = FAIL_THRESHOLD;
+ interval.tv_usec = 0;
+ timeout.tv_sec = FAIL_THRESHOLD;
+ timeout.tv_usec = 0;
+
+ if (ypdb->dom_broadcast_pid)
+ return(1);
+
+ if ((client_handle = clntudp_bufcreate(&ypdb->dom_server_addr,
+ YPPROG, YPVERS, interval, &rpcsock, RPCSMALLMSGSIZE,
+ RPCSMALLMSGSIZE)) == (CLIENT *)NULL) {
+ /* Can't get a handle: we're dead. */
+ ypdb->dom_alive = 0;
+ ypdb->dom_vers = -1;
+ broadcast(ypdb);
+ return(1);
+ }
+
+ {
+ char *ptr;
+
+ ptr = ypdb->dom_domain;
+
+ stat = clnt_call(client_handle, YPPROC_DOMAIN,
+ (xdrproc_t)xdr_domainname, &ptr,
+ (xdrproc_t)xdr_bool, &out, timeout);
+ if (stat != RPC_SUCCESS || out == FALSE) {
+ ypdb->dom_alive = 0;
+ ypdb->dom_vers = -1;
+ clnt_destroy(client_handle);
+ broadcast(ypdb);
+ return(1);
+ }
+ }
+
+ clnt_destroy(client_handle);
+ return(0);
+}
+
+void
+rpc_received(char *dom, struct sockaddr_in *raddrp, int force)
+{
+ struct _dom_binding *ypdb, *prev = NULL;
+ struct iovec iov[2];
+ struct ypbind_resp ybr;
+ char path[MAXPATHLEN];
+ int fd;
+
+ /*printf("returned from %s/%d about %s\n", inet_ntoa(raddrp->sin_addr),
+ ntohs(raddrp->sin_port), dom);*/
+
+ if (dom == NULL)
+ return;
+
+ for (ypdb = ypbindlist; ypdb; ypdb = ypdb->dom_pnext) {
+ if (strcmp(ypdb->dom_domain, dom) == 0)
+ break;
+ prev = ypdb;
+ }
+
+ if (ypdb && force) {
+ if (ypdb->dom_broadcast_pid) {
+ kill(ypdb->dom_broadcast_pid, SIGINT);
+ close(READFD);
+ FD_CLR(READFD, &fdsr);
+ FD_CLR(READFD, &svc_fdset);
+ READFD = WRITEFD = -1;
+ }
+ }
+
+ /* if in secure mode, check originating port number */
+ if ((ypsecuremode && (ntohs(raddrp->sin_port) >= IPPORT_RESERVED))) {
+ syslog(LOG_WARNING, "Rejected NIS server on [%s/%d] for domain %s.",
+ inet_ntoa(raddrp->sin_addr), ntohs(raddrp->sin_port),
+ dom);
+ if (ypdb != NULL) {
+ ypdb->dom_broadcast_pid = 0;
+ ypdb->dom_alive = 0;
+ }
+ return;
+ }
+
+ if (raddrp->sin_addr.s_addr == (long)0) {
+ switch (ypdb->dom_default) {
+ case 0:
+ if (prev == NULL)
+ ypbindlist = ypdb->dom_pnext;
+ else
+ prev->dom_pnext = ypdb->dom_pnext;
+ sprintf(path, "%s/%s.%ld", BINDINGDIR,
+ ypdb->dom_domain, YPVERS);
+ close(ypdb->dom_lockfd);
+ unlink(path);
+ free(ypdb);
+ domains--;
+ return;
+ case 1:
+ ypdb->dom_broadcast_pid = 0;
+ ypdb->dom_alive = 0;
+ broadcast(ypdb);
+ return;
+ default:
+ break;
+ }
+ }
+
+ if (ypdb == NULL) {
+ if (force == 0)
+ return;
+ ypdb = (struct _dom_binding *)malloc(sizeof *ypdb);
+ if (ypdb == NULL) {
+ syslog(LOG_WARNING, "malloc: %m");
+ return;
+ }
+ bzero(ypdb, sizeof *ypdb);
+ strncpy(ypdb->dom_domain, dom, sizeof ypdb->dom_domain);
+ ypdb->dom_lockfd = -1;
+ ypdb->dom_default = 0;
+ ypdb->dom_pnext = ypbindlist;
+ ypbindlist = ypdb;
+ }
+
+ /* We've recovered from a crash: inform the world. */
+ if (ypdb->dom_vers == -1 && ypdb->dom_server_addr.sin_addr.s_addr)
+ syslog(LOG_WARNING, "NIS server [%s] for domain \"%s\" OK",
+ inet_ntoa(raddrp->sin_addr), ypdb->dom_domain);
+
+ bcopy(raddrp, &ypdb->dom_server_addr,
+ sizeof ypdb->dom_server_addr);
+
+ ypdb->dom_vers = YPVERS;
+ ypdb->dom_alive = 1;
+ ypdb->dom_broadcast_pid = 0;
+
+ if (ypdb->dom_lockfd != -1)
+ close(ypdb->dom_lockfd);
+
+ sprintf(path, "%s/%s.%ld", BINDINGDIR,
+ ypdb->dom_domain, ypdb->dom_vers);
+#ifdef O_SHLOCK
+ if ((fd = open(path, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC, 0644)) == -1) {
+ (void)mkdir(BINDINGDIR, 0755);
+ if ((fd = open(path, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC, 0644)) == -1)
+ return;
+ }
+#else
+ if ((fd = open(path, O_CREAT|O_RDWR|O_TRUNC, 0644)) == -1) {
+ (void)mkdir(BINDINGDIR, 0755);
+ if ((fd = open(path, O_CREAT|O_RDWR|O_TRUNC, 0644)) == -1)
+ return;
+ }
+ flock(fd, LOCK_SH);
+#endif
+
+ /*
+ * ok, if BINDINGDIR exists, and we can create the binding file,
+ * then write to it..
+ */
+ ypdb->dom_lockfd = fd;
+
+ iov[0].iov_base = (char *)&(udptransp->xp_port);
+ iov[0].iov_len = sizeof udptransp->xp_port;
+ iov[1].iov_base = (char *)&ybr;
+ iov[1].iov_len = sizeof ybr;
+
+ bzero(&ybr, sizeof ybr);
+ ybr.ypbind_status = YPBIND_SUCC_VAL;
+ *(u_int32_t *)&ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr = raddrp->sin_addr.s_addr;
+ *(u_short *)&ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port = raddrp->sin_port;
+
+ if (writev(ypdb->dom_lockfd, iov, 2) != iov[0].iov_len + iov[1].iov_len) {
+ syslog(LOG_WARNING, "write: %m");
+ close(ypdb->dom_lockfd);
+ ypdb->dom_lockfd = -1;
+ return;
+ }
+}
+
+/*
+ * Check address against list of allowed servers. Return 0 if okay,
+ * 1 if not matched.
+ */
+int
+verify(struct in_addr addr)
+{
+ int i;
+
+ for (i = 0; i < RESTRICTED_SERVERS; i++)
+ if (!bcmp(&addr, &restricted_addrs[i], sizeof(struct in_addr)))
+ return(0);
+
+ return(1);
+}
+
+/*
+ * Try to set restricted mode. We default to normal mode if we can't
+ * resolve the specified hostnames.
+ */
+void
+yp_restricted_mode(char *args)
+{
+ struct hostent *h;
+ int i = 0;
+ char *s;
+
+ /* Find the restricted domain. */
+ if ((s = strsep(&args, ",")) == NULL)
+ return;
+ domain_name = s;
+
+ /* Get the addresses of the servers. */
+ while ((s = strsep(&args, ",")) != NULL && i < RESTRICTED_SERVERS) {
+ if ((h = gethostbyname(s)) == NULL)
+ return;
+ bcopy (h->h_addr_list[0], &restricted_addrs[i],
+ sizeof(struct in_addr));
+ i++;
+ }
+
+ /* ypset and ypsetme not allowed with restricted mode */
+ ypsetmode = YPSET_NO;
+
+ yp_restricted = i;
+ return;
+}
diff --git a/usr.sbin/yppoll/Makefile b/usr.sbin/yppoll/Makefile
new file mode 100644
index 0000000..63763316
--- /dev/null
+++ b/usr.sbin/yppoll/Makefile
@@ -0,0 +1,7 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $FreeBSD$
+
+PROG= yppoll
+MAN= yppoll.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/yppoll/yppoll.8 b/usr.sbin/yppoll/yppoll.8
new file mode 100644
index 0000000..ad90d90
--- /dev/null
+++ b/usr.sbin/yppoll/yppoll.8
@@ -0,0 +1,84 @@
+.\" Copyright (c) 1996 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Jason R. Thorpe.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd October 25, 1994
+.Dt YPPOLL 8
+.Os
+.Sh NAME
+.Nm yppoll
+.Nd ask version of YP map from YP server
+.Sh SYNOPSIS
+.Nm
+.Op Fl h Ar host
+.Op Fl d Ar domain
+.Ar mapname
+.Sh DESCRIPTION
+The
+.Nm
+utility
+asks a YP server process for the order number and which host is the master
+server for
+.Ar mapname .
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl h Ar host
+Ask the YP server process running on
+.Ar host
+for information about
+.Ar mapname .
+If
+.Ar host
+is not specified, the server polled is the default server returned by
+.Xr ypwhich 1 .
+.It Fl d Ar domain
+Use the YP domain
+.Ar domain
+instead of the default domain as returned by
+.Xr domainname 1 .
+.El
+.Sh SEE ALSO
+.Xr domainname 1 ,
+.Xr ypcat 1 ,
+.Xr ypmatch 1 ,
+.Xr ypwhich 1 ,
+.Xr yp 8 ,
+.Xr ypbind 8 ,
+.Xr ypset 8
+.Sh AUTHORS
+.An Theo De Raadt
+and
+.An John Brezak
diff --git a/usr.sbin/yppoll/yppoll.c b/usr.sbin/yppoll/yppoll.c
new file mode 100644
index 0000000..e35087c
--- /dev/null
+++ b/usr.sbin/yppoll/yppoll.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 1992/3 Theo de Raadt <deraadt@fsa.ca>
+ * Copyright (c) 1992/3 John Brezak
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. 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/types.h>
+#include <sys/socket.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <timeconv.h>
+#include <unistd.h>
+
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: yppoll [-h host] [-d domainname] mapname\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ char *domainname;
+ char *hostname = "localhost";
+ char *inmap, *master;
+ int order;
+ int c, r;
+ time_t t;
+
+ yp_get_default_domain(&domainname);
+
+ while ((c = getopt(argc, argv, "h:d:")) != -1)
+ switch (c) {
+ case 'd':
+ domainname = optarg;
+ break;
+ case 'h':
+ hostname = optarg;
+ break;
+ case '?':
+ usage();
+ /*NOTREACHED*/
+ }
+
+ if (optind + 1 != argc)
+ usage();
+
+ inmap = argv[optind];
+
+ r = yp_order(domainname, inmap, &order);
+ if (r != 0)
+ errx(1, "no such map %s. Reason: %s", inmap, yperr_string(r));
+ t = _int_to_time(order);
+ printf("Map %s has order number %d. %s", inmap, order, ctime(&t));
+ r = yp_master(domainname, inmap, &master);
+ if (r != 0)
+ errx(1, "no such map %s. Reason: %s", inmap, yperr_string(r));
+ printf("The master server is %s.\n", master);
+
+ exit(0);
+}
diff --git a/usr.sbin/yppush/Makefile b/usr.sbin/yppush/Makefile
new file mode 100644
index 0000000..7ce1c61
--- /dev/null
+++ b/usr.sbin/yppush/Makefile
@@ -0,0 +1,28 @@
+# $FreeBSD$
+
+RPCDIR= ${.CURDIR}/../../include/rpcsvc
+.PATH: ${RPCDIR} ${.CURDIR}/../../usr.sbin/ypserv \
+ ${.CURDIR}/../../libexec/ypxfr
+
+PROG= yppush
+MAN= yppush.8
+SRCS= ypxfr_getmap.c yp_dblookup.c yp_error.c ypxfr_misc.c yppush_main.c \
+ ${GENSRCS}
+GENSRCS=yp.h yp_clnt.c yppush_svc.c
+
+CFLAGS+= -I. -I${.CURDIR}/../../libexec/ypxfr
+
+RPCGEN= rpcgen -C
+
+CLEANFILES= ${GENSRCS}
+
+yppush_svc.c: yp.x
+ ${RPCGEN} -DYPPUSH_ONLY -m -o ${.TARGET} ${RPCDIR}/yp.x
+
+yp_clnt.c: yp.x
+ ${RPCGEN} -DYPSERV_ONLY -l -o ${.TARGET} ${RPCDIR}/yp.x
+
+yp.h: yp.x
+ ${RPCGEN} -h -o ${.TARGET} ${RPCDIR}/yp.x
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/yppush/yppush.8 b/usr.sbin/yppush/yppush.8
new file mode 100644
index 0000000..1acac3f
--- /dev/null
+++ b/usr.sbin/yppush/yppush.8
@@ -0,0 +1,180 @@
+.\" Copyright (c) 1991, 1993, 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$
+.\"
+.Dd February 5, 1995
+.Dt YPPUSH 8
+.Os
+.Sh NAME
+.Nm yppush
+.Nd "force propagation of updated NIS databases"
+.Sh SYNOPSIS
+.Nm
+.Op Fl d Ar domain
+.Op Fl t Ar timeout
+.Op Fl j Ar #parallel jobs
+.Op Fl h Ar host
+.Op Fl p Ar path
+.Op Fl v
+.Ar mapname
+.Sh DESCRIPTION
+The
+.Nm
+utility distributes updated NIS databases (or
+.Pa maps )
+from an NIS master server to NIS slave servers within an NIS
+domain.
+It is normally only run on the NIS master by
+.Pa /var/yp/Makefile
+whenever any of the NIS maps are updated.
+Note that
+.Pa /var/yp/Makefile
+does not invoke
+.Nm
+by default: the
+.Dq Li NOPUSH=True
+entry in the Makefile must first be commented out
+(the default
+.Fx
+configuration assumes a small network with only
+a single NIS server; in such a configuration,
+.Nm
+is not needed).
+.Pp
+By default,
+.Nm
+determines the names of the slave servers for a domain by searching the
+.Pa ypservers
+map.
+A destination host (or a list of hosts) can also be manually
+specified on the command line.
+Once it has a complete list of slave servers, it sends a 'map transfer'
+request to each slave, which in turn reads a copy of the map from
+the master NIS server using
+.Xr ypxfr 8 .
+Included within each request is the name of the map to be copied
+and some special information required by
+.Xr ypxfr 8
+to successfully 'callback' to
+.Nm
+and carry out the transfer.
+Any error messages
+.Nm
+receives from
+.Xr ypxfr 8
+via callback will be printed to stderr.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d Ar domain
+Specify a particular domain.
+The NIS domain of
+the local host system is used by default.
+If the local host's domain
+name is not set, the domain name must be specified with this flag.
+.It Fl t Ar timeout
+Specify a timeout value in seconds.
+This timeout
+controls how long
+.Nm
+will wait for a response from a slave server before sending a
+map transfer request to the next slave server in its list.
+.It Fl j Ar #parallel jobs
+The
+.Nm
+utility normally performs transfers serially, meaning that it will
+send a map transfer request to one slave server and then wait for
+it to respond before moving on to the next slave server.
+In environments
+with many slaves, it is more efficient to initiate several map transfers
+at once so that the transfers can take place in parallel.
+The
+.Fl j
+flag is used to specify the desired number of parallel jobs:
+.Nm
+will initiate the specified number of transfers immediately and
+listen for responses.
+If the number of specified parallel jobs is
+less than the number of slave servers,
+.Nm
+will initiate only the number of specified jobs and then wait
+for some of them to finish before starting any more.
+.Pp
+Note that
+.Nm
+handles callbacks asynchronously, which means that it will collect
+and display the callback information received from
+.Xr ypxfr 8
+as soon as it arrives, even it arrives before all of the map
+transfer requests have been sent.
+.It Fl h Ar host
+Can be used to transfer a map to a user-specified machine or
+group of machines instead of the list of servers contained in
+the
+.Pa ypservers
+map.
+A list of hosts can be specified by using multiple
+instances of the
+.Fl h
+flag.
+.It Fl p Ar path
+By default,
+.Nm
+expects all the local NIS maps to be stored under
+.Pa /var/yp .
+The
+.Fl p
+flag can be used to specify an alternate path in the event that
+the system administrator decides to store the NIS maps somewhere else.
+.It Fl v
+Verbose mode: it causes
+.Nm
+to print debugging messages as it runs.
+Specifying this flag twice
+makes
+.Nm
+even more verbose.
+.El
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /var/yp/[domainname]/ypservers
+the NIS ypservers map containing the names of all servers in
+a particular NIS domain
+.El
+.Sh SEE ALSO
+.Xr yp 8 ,
+.Xr ypserv 8 ,
+.Xr ypxfr 8
+.Sh AUTHORS
+.An Bill Paul Aq wpaul@ctr.columbia.edu
+.Sh BUGS
+The mechanism for transferring NIS maps in NIS v1 is different
+than that in NIS version 2.
+This version of
+.Nm
+has support for transferring maps to NIS v2 systems only.
diff --git a/usr.sbin/yppush/yppush_extern.h b/usr.sbin/yppush/yppush_extern.h
new file mode 100644
index 0000000..2a4fbcc
--- /dev/null
+++ b/usr.sbin/yppush/yppush_extern.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+/* Privately defined error codes. */
+#define YPPUSH_TIMEDOUT 255 /* Timed out trying to talk to ypserv */
+#define YPPUSH_NOHOST 254 /* No such host */
+#define YPPUSH_YPSERV 252 /* Failed to contact ypserv. */
+#define YPPUSH_PMAP 251 /* Portmapper failure. */
+#ifndef YPPUSH_RESPONSE_TIMEOUT
+#define YPPUSH_RESPONSE_TIMEOUT 5*60
+#endif
+extern int _rpc_dtablesize(void);
+extern void yppush_xfrrespprog_1(struct svc_req *, SVCXPRT *);
diff --git a/usr.sbin/yppush/yppush_main.c b/usr.sbin/yppush/yppush_main.c
new file mode 100644
index 0000000..ceb63bc
--- /dev/null
+++ b/usr.sbin/yppush/yppush_main.c
@@ -0,0 +1,621 @@
+/*
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/fcntl.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <rpc/rpc.h>
+#include <rpc/clnt.h>
+#include <rpc/pmap_clnt.h>
+#include <rpcsvc/yp.h>
+#include <rpcsvc/ypclnt.h>
+#include "ypxfr_extern.h"
+#include "yppush_extern.h"
+
+char *progname = "yppush";
+int debug = 1;
+int _rpcpmstart = 0;
+char *yp_dir = _PATH_YP;
+
+char *yppush_mapname = NULL; /* Map to transfer. */
+char *yppush_domain = NULL; /* Domain in which map resides. */
+char *yppush_master = NULL; /* Master NIS server for said domain. */
+int skip_master = 0; /* Do not attempt to push map to master. */
+int verbose = 0; /* Toggle verbose mode. */
+unsigned long yppush_transid = 0;
+int yppush_timeout = 80; /* Default timeout. */
+int yppush_jobs = 1; /* Number of allowed concurrent jobs. */
+int yppush_running_jobs = 0; /* Number of currently running jobs. */
+
+/* Structure for holding information about a running job. */
+struct jobs {
+ unsigned long tid;
+ int port;
+ ypxfrstat stat;
+ unsigned long prognum;
+ char *server;
+ char *map;
+ int polled;
+ struct jobs *next;
+};
+
+struct jobs *yppush_joblist; /* Linked list of running jobs. */
+
+static int yppush_svc_run(int);
+
+/*
+ * Local error messages.
+ */
+static const char *
+yppusherr_string(int err)
+{
+ switch (err) {
+ case YPPUSH_TIMEDOUT:
+ return("transfer or callback timed out");
+ case YPPUSH_YPSERV:
+ return("failed to contact ypserv");
+ case YPPUSH_NOHOST:
+ return("no such host");
+ case YPPUSH_PMAP:
+ return("portmapper failure");
+ default:
+ return("unknown error code");
+ }
+}
+
+/*
+ * Report state of a job.
+ */
+static int
+yppush_show_status(ypxfrstat status, unsigned long tid)
+{
+ struct jobs *job;
+
+ job = yppush_joblist;
+
+ while (job != NULL) {
+ if (job->tid == tid)
+ break;
+ job = job->next;
+ }
+
+ if (job == NULL) {
+ yp_error("warning: received callback with invalid transaction ID: %lu",
+ tid);
+ return (0);
+ }
+
+ if (job->polled) {
+ yp_error("warning: received callback with duplicate transaction ID: %lu",
+ tid);
+ return (0);
+ }
+
+ if (verbose > 1) {
+ yp_error("checking return status: transaction ID: %lu",
+ job->tid);
+ }
+
+ if (status != YPPUSH_SUCC || verbose) {
+ yp_error("transfer of map %s to server %s %s",
+ job->map, job->server, status == YPPUSH_SUCC ?
+ "succeeded" : "failed");
+ yp_error("status returned by ypxfr: %s", status > YPPUSH_AGE ?
+ yppusherr_string(status) :
+ ypxfrerr_string(status));
+ }
+
+ job->polled = 1;
+
+ svc_unregister(job->prognum, 1);
+
+ yppush_running_jobs--;
+ return(0);
+}
+
+/* Exit routine. */
+static void
+yppush_exit(int now)
+{
+ struct jobs *jptr;
+ int still_pending = 1;
+
+ /* Let all the information trickle in. */
+ while (!now && still_pending) {
+ jptr = yppush_joblist;
+ still_pending = 0;
+ while (jptr) {
+ if (jptr->polled == 0) {
+ still_pending++;
+ if (verbose > 1)
+ yp_error("%s has not responded",
+ jptr->server);
+ } else {
+ if (verbose > 1)
+ yp_error("%s has responded",
+ jptr->server);
+ }
+ jptr = jptr->next;
+ }
+ if (still_pending) {
+ if (verbose > 1)
+ yp_error("%d transfer%sstill pending",
+ still_pending,
+ still_pending > 1 ? "s " : " ");
+ if (yppush_svc_run (YPPUSH_RESPONSE_TIMEOUT) == 0) {
+ yp_error("timed out");
+ now = 1;
+ }
+ } else {
+ if (verbose)
+ yp_error("all transfers complete");
+ break;
+ }
+ }
+
+
+ /* All stats collected and reported -- kill all the stragglers. */
+ jptr = yppush_joblist;
+ while (jptr) {
+ if (!jptr->polled)
+ yp_error("warning: exiting with transfer \
+to %s (transid = %lu) still pending", jptr->server, jptr->tid);
+ svc_unregister(jptr->prognum, 1);
+ jptr = jptr->next;
+ }
+
+ exit(0);
+}
+
+/*
+ * Handler for 'normal' signals.
+ */
+
+static void
+handler(int sig)
+{
+ yppush_exit (1);
+ return;
+}
+
+/*
+ * Dispatch loop for callback RPC services.
+ * Return value:
+ * -1 error
+ * 0 timeout
+ * >0 request serviced
+ */
+static int
+yppush_svc_run(int timeout_secs)
+{
+ int rc;
+ fd_set readfds;
+ struct timeval timeout;
+
+ timeout.tv_usec = 0;
+ timeout.tv_sec = timeout_secs;
+
+retry:
+ readfds = svc_fdset;
+ rc = select(svc_maxfd + 1, &readfds, NULL, NULL, &timeout);
+ switch (rc) {
+ case -1:
+ if (errno == EINTR)
+ goto retry;
+ yp_error("select failed: %s", strerror(errno));
+ break;
+ case 0:
+ yp_error("select() timed out");
+ break;
+ default:
+ svc_getreqset(&readfds);
+ break;
+ }
+ return rc;
+}
+
+/*
+ * RPC service routines for callbacks.
+ */
+void *
+yppushproc_null_1_svc(void *argp, struct svc_req *rqstp)
+{
+ static char * result;
+ /* Do nothing -- RPC conventions call for all a null proc. */
+ return((void *) &result);
+}
+
+void *
+yppushproc_xfrresp_1_svc(yppushresp_xfr *argp, struct svc_req *rqstp)
+{
+ static char * result;
+ yppush_show_status(argp->status, argp->transid);
+ return((void *) &result);
+}
+
+/*
+ * Transmit a YPPROC_XFR request to ypserv.
+ */
+static int
+yppush_send_xfr(struct jobs *job)
+{
+ ypreq_xfr req;
+/* ypresp_xfr *resp; */
+ DBT key, data;
+ CLIENT *clnt;
+ struct rpc_err err;
+ struct timeval timeout;
+
+ timeout.tv_usec = 0;
+ timeout.tv_sec = 0;
+
+ /*
+ * The ypreq_xfr structure has a member of type map_parms,
+ * which seems to require the order number of the map.
+ * It isn't actually used at the other end (at least the
+ * FreeBSD ypserv doesn't use it) but we fill it in here
+ * for the sake of completeness.
+ */
+ key.data = "YP_LAST_MODIFIED";
+ key.size = sizeof ("YP_LAST_MODIFIED") - 1;
+
+ if (yp_get_record(yppush_domain, yppush_mapname, &key, &data,
+ 1) != YP_TRUE) {
+ yp_error("failed to read order number from %s: %s: %s",
+ yppush_mapname, yperr_string(yp_errno),
+ strerror(errno));
+ return(1);
+ }
+
+ /* Fill in the request arguments */
+ req.map_parms.ordernum = atoi(data.data);
+ req.map_parms.domain = yppush_domain;
+ req.map_parms.peer = yppush_master;
+ req.map_parms.map = job->map;
+ req.transid = job->tid;
+ req.prog = job->prognum;
+ req.port = job->port;
+
+ /* Get a handle to the remote ypserv. */
+ if ((clnt = clnt_create(job->server, YPPROG, YPVERS, "udp")) == NULL) {
+ yp_error("%s: %s",job->server,clnt_spcreateerror("couldn't \
+create udp handle to NIS server"));
+ switch (rpc_createerr.cf_stat) {
+ case RPC_UNKNOWNHOST:
+ job->stat = YPPUSH_NOHOST;
+ break;
+ case RPC_PMAPFAILURE:
+ job->stat = YPPUSH_PMAP;
+ break;
+ default:
+ job->stat = YPPUSH_RPC;
+ break;
+ }
+ return(1);
+ }
+
+ /*
+ * Reduce timeout to nothing since we may not
+ * get a response from ypserv and we don't want to block.
+ */
+ if (clnt_control(clnt, CLSET_TIMEOUT, (char *)&timeout) == FALSE)
+ yp_error("failed to set timeout on ypproc_xfr call");
+
+ /* Invoke the ypproc_xfr service. */
+ if (ypproc_xfr_2(&req, clnt) == NULL) {
+ clnt_geterr(clnt, &err);
+ if (err.re_status != RPC_SUCCESS &&
+ err.re_status != RPC_TIMEDOUT) {
+ yp_error("%s: %s", job->server, clnt_sperror(clnt,
+ "yp_xfr failed"));
+ job->stat = YPPUSH_YPSERV;
+ clnt_destroy(clnt);
+ return(1);
+ }
+ }
+
+ clnt_destroy(clnt);
+
+ return(0);
+}
+
+/*
+ * Main driver function. Register the callback service, add the transfer
+ * request to the internal list, send the YPPROC_XFR request to ypserv
+ * do other magic things.
+ */
+int
+yp_push(char *server, char *map, unsigned long tid)
+{
+ unsigned long prognum;
+ int sock = RPC_ANYSOCK;
+ SVCXPRT *xprt;
+ struct jobs *job;
+
+ /* Register the job in our linked list of jobs. */
+
+ /* First allocate job structure */
+ if ((job = (struct jobs *)malloc(sizeof (struct jobs))) == NULL) {
+ yp_error("malloc failed");
+ yppush_exit (1);
+ }
+
+ /*
+ * Register the callback service on the first free transient
+ * program number.
+ */
+ xprt = svcudp_create(sock);
+ for (prognum = 0x40000000; prognum < 0x5FFFFFFF; prognum++) {
+ if (svc_register(xprt, prognum, 1,
+ yppush_xfrrespprog_1, IPPROTO_UDP) == TRUE)
+ break;
+ }
+ if (prognum == 0x5FFFFFFF) {
+ yp_error ("can't register yppush_xfrrespprog_1");
+ yppush_exit (1);
+ }
+
+ /* Initialize the info for this job. */
+ job->stat = 0;
+ job->tid = tid;
+ job->port = xprt->xp_port;
+ job->server = strdup(server);
+ job->map = strdup(map);
+ job->prognum = prognum;
+ job->polled = 0;
+ job->next = yppush_joblist;
+ yppush_joblist = job;
+
+ if (verbose) {
+ yp_error("initiating transfer: %s -> %s (transid = %lu)",
+ yppush_mapname, server, tid);
+ }
+
+ /*
+ * Send the XFR request to ypserv. We don't have to wait for
+ * a response here since we handle them asynchronously.
+ */
+
+ if (yppush_send_xfr(job)){
+ /* Transfer request blew up. */
+ yppush_show_status(job->stat ? job->stat :
+ YPPUSH_YPSERV,job->tid);
+ } else {
+ if (verbose > 1)
+ yp_error("%s has been called", server);
+ }
+
+ return(0);
+}
+
+/*
+ * Called for each entry in the ypservers map from yp_get_map(), which
+ * is our private yp_all() routine.
+ */
+int
+yppush_foreach(int status, char *key, int keylen, char *val, int vallen,
+ char *data)
+{
+ char server[YPMAXRECORD + 2];
+
+ if (status != YP_TRUE)
+ return (status);
+
+ snprintf(server, sizeof(server), "%.*s", vallen, val);
+ if (skip_master && strcasecmp(server, yppush_master) == 0)
+ return (0);
+
+ /*
+ * Restrict the number of concurrent jobs: if yppush_jobs number
+ * of jobs have already been dispatched and are still pending,
+ * wait for one of them to finish so we can reuse its slot.
+ */
+ while (yppush_running_jobs >= yppush_jobs && (yppush_svc_run (yppush_timeout) > 0))
+ ;
+
+ /* Cleared for takeoff: set everything in motion. */
+ if (yp_push(server, yppush_mapname, yppush_transid))
+ return(yp_errno);
+
+ /* Bump the job counter and transaction ID. */
+ yppush_running_jobs++;
+ yppush_transid++;
+ return (0);
+}
+
+static void usage()
+{
+ fprintf (stderr, "%s\n%s\n",
+ "usage: yppush [-d domain] [-t timeout] [-j #parallel jobs] [-h host]",
+ " [-p path] mapname");
+ exit(1);
+}
+
+/*
+ * Entry point. (About time!)
+ */
+int
+main(int argc, char *argv[])
+{
+ int ch;
+ DBT key, data;
+ char myname[MAXHOSTNAMELEN];
+ struct hostlist {
+ char *name;
+ struct hostlist *next;
+ };
+ struct hostlist *yppush_hostlist = NULL;
+ struct hostlist *tmp;
+ struct sigaction sa;
+
+ while ((ch = getopt(argc, argv, "d:j:p:h:t:v")) != -1) {
+ switch (ch) {
+ case 'd':
+ yppush_domain = optarg;
+ break;
+ case 'j':
+ yppush_jobs = atoi(optarg);
+ if (yppush_jobs <= 0)
+ yppush_jobs = 1;
+ break;
+ case 'p':
+ yp_dir = optarg;
+ break;
+ case 'h': /* we can handle multiple hosts */
+ if ((tmp = (struct hostlist *)malloc(sizeof(struct hostlist))) == NULL) {
+ yp_error("malloc failed");
+ yppush_exit(1);
+ }
+ tmp->name = strdup(optarg);
+ tmp->next = yppush_hostlist;
+ yppush_hostlist = tmp;
+ break;
+ case 't':
+ yppush_timeout = atoi(optarg);
+ break;
+ case 'v':
+ verbose++;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ yppush_mapname = argv[0];
+
+ if (yppush_mapname == NULL) {
+ /* "No guts, no glory." */
+ usage();
+ }
+
+ /*
+ * If no domain was specified, try to find the default
+ * domain. If we can't find that, we're doomed and must bail.
+ */
+ if (yppush_domain == NULL) {
+ char *yppush_check_domain;
+ if (!yp_get_default_domain(&yppush_check_domain) &&
+ !_yp_check(&yppush_check_domain)) {
+ yp_error("no domain specified and NIS not running");
+ usage();
+ } else
+ yp_get_default_domain(&yppush_domain);
+ }
+
+ /* Check to see that we are the master for this map. */
+
+ if (gethostname ((char *)&myname, sizeof(myname))) {
+ yp_error("failed to get name of local host: %s",
+ strerror(errno));
+ yppush_exit(1);
+ }
+
+ key.data = "YP_MASTER_NAME";
+ key.size = sizeof("YP_MASTER_NAME") - 1;
+
+ if (yp_get_record(yppush_domain, yppush_mapname,
+ &key, &data, 1) != YP_TRUE) {
+ yp_error("couldn't open %s map: %s", yppush_mapname,
+ strerror(errno));
+ yppush_exit(1);
+ }
+
+ if (strncasecmp(myname, data.data, data.size) == 0) {
+ /* I am master server, and no explicit host list was
+ specified: do not push map to myself -- this will
+ fail with YPPUSH_AGE anyway. */
+ if (yppush_hostlist == NULL)
+ skip_master = 1;
+ } else {
+ yp_error("warning: this host is not the master for %s",
+ yppush_mapname);
+#ifdef NITPICKY
+ yppush_exit(1);
+#endif
+ }
+
+ yppush_master = malloc(data.size + 1);
+ strncpy(yppush_master, data.data, data.size);
+ yppush_master[data.size] = '\0';
+
+ /* Install some handy handlers. */
+ signal(SIGTERM, handler);
+ signal(SIGINT, handler);
+
+ /* set initial transaction ID */
+ yppush_transid = time((time_t *)NULL);
+
+ if (yppush_hostlist) {
+ /*
+ * Host list was specified on the command line:
+ * kick off the transfers by hand.
+ */
+ tmp = yppush_hostlist;
+ while (tmp) {
+ yppush_foreach(YP_TRUE, NULL, 0, tmp->name,
+ strlen(tmp->name), NULL);
+ tmp = tmp->next;
+ }
+ } else {
+ /*
+ * Do a yp_all() on the ypservers map and initiate a ypxfr
+ * for each one.
+ */
+ ypxfr_get_map("ypservers", yppush_domain,
+ "localhost", yppush_foreach);
+ }
+
+ if (verbose > 1)
+ yp_error("all jobs dispatched");
+
+ /* All done -- normal exit. */
+ yppush_exit(0);
+
+ /* Just in case. */
+ exit(0);
+}
diff --git a/usr.sbin/ypserv/Makefile b/usr.sbin/ypserv/Makefile
new file mode 100644
index 0000000..c1b2df3
--- /dev/null
+++ b/usr.sbin/ypserv/Makefile
@@ -0,0 +1,42 @@
+# $FreeBSD$
+
+RPCDIR= ${.CURDIR}/../../include/rpcsvc
+.PATH: ${RPCDIR}
+
+PROG= ypserv
+MAN= ypserv.8 ypinit.8
+SRCS= yp_svc.c yp_server.c yp_dblookup.c yp_dnslookup.c \
+ ypxfr_clnt.c yp.h yp_main.c yp_error.c yp_access.c yp_svc_udp.c
+
+CFLAGS+= -DDB_CACHE -DTCP_WRAPPER -I.
+
+DPADD= ${LIBWRAP}
+LDADD= -lwrap
+
+CLEANFILES= yp_svc.c ypxfr_clnt.c yp.h
+
+RPCGEN= rpcgen -I -C
+
+# We need to remove the 'static' keyword from _rpcsvcstate so that
+# yp_main.c can see it.
+yp_svc.c: yp.x
+ rm -f ${.TARGET}
+ ${RPCGEN} -DYPSERV_ONLY -m ${RPCDIR}/yp.x | \
+ sed s/"static int _rpcsvcstate"/"int _rpcsvcstate"/g > ${.TARGET}
+
+ypxfr_clnt.c: yp.x
+ ${RPCGEN} -DYPPUSH_ONLY -l -o ${.TARGET} ${RPCDIR}/yp.x
+
+yp.h: yp.x
+ ${RPCGEN} -h -o ${.TARGET} ${RPCDIR}/yp.x
+
+FILES= Makefile.yp
+FILESNAME= Makefile.dist
+FILESDIR= /var/yp
+SCRIPTS= ypinit.sh
+
+.if !exists(${DESTDIR}${FILESDIR}/Makefile)
+SYMLINKS= ${FILESNAME} ${FILESDIR}/Makefile
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ypserv/Makefile.yp b/usr.sbin/ypserv/Makefile.yp
new file mode 100644
index 0000000..407663d
--- /dev/null
+++ b/usr.sbin/ypserv/Makefile.yp
@@ -0,0 +1,607 @@
+#
+# Makefile for the NIS databases
+#
+# $FreeBSD$
+#
+# This Makefile should only be run on the NIS master server of a domain.
+# All updated maps will be pushed to all NIS slave servers listed in the
+# /var/yp/ypservers file. Please make sure that the hostnames of all
+# NIS servers in your domain are listed in /var/yp/ypservers.
+#
+# This Makefile can be modified to support more NIS maps if desired.
+#
+
+# If this machine is an NIS master, comment out this next line so
+# that changes to the NIS maps can be propagated to the slave servers.
+# (By default we assume that we are only serving a small domain with
+# only one server.)
+#
+NOPUSH = "True"
+
+# If you want to use a FreeBSD NIS server to serve non-FreeBSD clients
+# (i.e. clients who expect the password field in the passwd maps to be
+# valid) then uncomment this line. This will cause $YPDIR/passwd to
+# be generated with valid password fields. This is insecure: FreeBSD
+# normally only serves the master.passwd maps (which have real encrypted
+# passwords in them) to the superuser on other FreeBSD machines, but
+# non-FreeBSD clients (e.g. SunOS, Solaris (without NIS+), IRIX, HP-UX,
+# etc...) will only work properly in 'unsecure' mode.
+#
+#UNSECURE = "True"
+
+# The following line encodes the YP_INTERDOMAIN key into the hosts.byname
+# and hosts.byaddr maps so that ypserv(8) will do DNS lookups to resolve
+# hosts not in the current domain. Commenting this line out will disable
+# the DNS lookups.
+B=-b
+
+# Normally, the master.passwd.* maps are guarded against access from
+# non-privileged users. By commenting out the following line, the YP_SECURE
+# key will be removed from these maps, allowing anyone to access them.
+S=-s
+
+# These are commands which this Makefile needs to properly rebuild the
+# NIS databases. Don't change these unless you have a good reason. Also
+# be sure not to place an @ in front of /usr/bin/awk: it isn't necessary
+# and it'll break everything in sight.
+#
+AWK = /usr/bin/awk
+RM = @/bin/rm -f
+MV = @/bin/mv -f
+RMV = /bin/mv -f
+
+MKDB = /usr/sbin/yp_mkdb
+DBLOAD = $(MKDB) -m `hostname`
+MKNETID = /usr/libexec/mknetid
+NEWALIASES = /usr/bin/newaliases
+YPPUSH = /usr/sbin/yppush
+.if !defined(UPDATE_DOMAIN)
+DOMAIN = `/bin/domainname`
+.else
+DOMAIN = $(UPDATE_DOMAIN)
+.endif
+REVNETGROUP = /usr/libexec/revnetgroup
+TMP = `echo $@.$$$$`
+
+# It is advisable to create a separate directory to contain the
+# source files used to generate your NIS maps. If you intend to
+# support multiple domains, something like /src/dir/$DOMAIN
+# would work well.
+YPSRCDIR = /etc
+.if !defined(YP_DIR)
+YPDIR = /var/yp
+.else
+YPDIR = $(YP_DIR)
+.endif
+YPMAPDIR = $(YPDIR)/$(DOMAIN)
+
+# These are the files from which the NIS databases are built. You may edit
+# these to taste in the event that you wish to keep your NIS source files
+# separate from your NIS server's actual configuration files. Note that the
+# NIS passwd and master.passwd files are stored in /var/yp: the server's
+# real password database is not used by default. However, you may use
+# the real /etc/passwd and /etc/master.passwd files by:
+#
+#
+# - invoking yppasswdd with `-t /etc/master.passwd' (yppasswdd will do a
+# 'pwd_mkdb' as needed if /etc/master.passwd is thus specified).
+# - Specifying the location of the master.passwd file using the
+# MASTER_PASSWD variable, i.e.:
+#
+# # make MASTER_PASSWD=/path/to/some/other/master.passwd
+#
+# - (optionally): editing this Makefile to change the default location.
+#
+# To add a user, edit $(YPDIR)/master.passwd and type 'make'. The raw
+# passwd file will be generated from the master.passwd file automagically.
+#
+ETHERS = $(YPSRCDIR)/ethers # ethernet addresses (for rarpd)
+BOOTPARAMS= $(YPSRCDIR)/bootparams # for booting Sun boxes (bootparamd)
+HOSTS = $(YPSRCDIR)/hosts
+IPNODES = $(YPDIR)/ipnodes
+NETWORKS = $(YPSRCDIR)/networks
+PROTOCOLS = $(YPSRCDIR)/protocols
+RPC = $(YPSRCDIR)/rpc
+SERVICES = $(YPSRCDIR)/services
+SHELLS = $(YPSRCDIR)/shells
+GROUP = $(YPSRCDIR)/group
+ALIASES = $(YPSRCDIR)/mail/aliases
+NETGROUP = $(YPDIR)/netgroup
+PASSWD = $(YPDIR)/passwd
+.if !defined(MASTER_PASSWD)
+MASTER = $(YPDIR)/master.passwd
+.else
+MASTER = $(MASTER_PASSWD)
+.endif
+YPSERVERS = $(YPDIR)/ypservers # List of all NIS servers for a domain
+PUBLICKEY = $(YPSRCDIR)/publickey
+NETID = $(YPSRCDIR)/netid
+AMDHOST = $(YPSRCDIR)/amd.map
+
+# List of maps that are always built.
+# If you want to omit some of them, feel free to comment
+# them out from this list.
+TARGETS= servers hosts networks protocols rpc services shells group
+#TARGETS+= aliases
+
+# Sanity checks: filter out targets we can't build
+# Note that we don't build the ethers or boorparams maps by default
+# since /etc/ethers and /etc/bootparams are not likely to be present
+# on all systems.
+.if exists($(ETHERS))
+TARGETS+= ethers
+.else
+ETHERS= /dev/null
+.endif
+
+.if exists($(BOOTPARAMS))
+TARGETS+= bootparams
+.else
+BOOTPARAMS= /dev/null
+.endif
+
+.if exists($(NETGROUP))
+TARGETS+= netgrp
+.else
+NETGROUP= /dev/null
+.endif
+
+.if exists($(MASTER))
+TARGETS+= passwd master.passwd netid
+.else
+MASTER= /dev/null
+TARGETS+= nopass
+.endif
+
+.if exists($(PUBLICKEY))
+TARGETS+= publickey
+.else
+PUBLICKEY= /dev/null
+.endif
+
+.if exists($(AMDHOST))
+TARGETS+= amd.map
+.else
+AMDHOST= /dev/null
+.endif
+
+.if exists($(IPNODES))
+TARGETS+= ipnodes
+.else
+IPNODES= /dev/null
+.endif
+
+target:
+ @if [ ! -d $(DOMAIN) ]; then mkdir $(DOMAIN); fi; \
+ cd $(DOMAIN) ; echo "NIS Map update started on `date` for domain $(DOMAIN)" ; \
+ make -f ../Makefile all; echo "NIS Map update completed."
+
+all: $(TARGETS)
+
+ethers: ethers.byname ethers.byaddr
+bootparam: bootparams
+hosts: hosts.byname hosts.byaddr
+ipnodes: ipnodes.byname ipnodes.byaddr
+networks: networks.byaddr networks.byname
+protocols: protocols.bynumber protocols.byname
+rpc: rpc.byname rpc.bynumber
+services: services.byname
+passwd: passwd.byname passwd.byuid
+group: group.byname group.bygid
+netgrp: netgroup
+netid: netid.byname
+servers: ypservers
+publickey: publickey.byname
+aliases: mail.aliases
+
+master.passwd: master.passwd.byname master.passwd.byuid
+
+#
+# This is a special target used only when doing in-place updates with
+# rpc.yppasswdd. In this case, the maps will be updated by the rpc.yppasswdd
+# server and won't need to be remade. They will have to be pushed to the
+# slaves however. Calling this target implicitly insures that this will
+# happen.
+#
+pushpw:
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) master.passwd.byname ; fi
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) master.passwd.byuid ; fi
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) passwd.byname ; fi
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) passwd.byuid ; fi
+
+pushmap:
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $(PUSHMAP) ; fi
+
+nopass:
+ @echo ""
+ @echo " ********WARNING********"
+ @echo " Couldn't find the master.passwd source file. This file"
+ @echo " is needed to generate the master.passwd and passwd maps."
+ @echo " The default location is /var/yp/master.passwd. You should"
+ @echo " edit /var/yp/Makefile and set the MASTER variable to point"
+ @echo " to the source file you wish to use for building the passwd"
+ @echo " maps, or else invoke make(1) in the following manner:"
+ @echo ""
+ @echo " make MASTER_PASSWD=/path/to/master.passwd"
+ @echo ""
+
+mail.aliases: $(ALIASES)
+ @echo "Updating $@..."
+ @$(NEWALIASES) -oA$(ALIASES)
+ @$(MKDB) -u $(ALIASES).db \
+ | $(DBLOAD) -i $(ALIASES) -o $(YPMAPDIR)/$@ - $(TMP); \
+ $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+ypservers: $(YPSERVERS)
+ @echo "Updating $@..."
+ @$(AWK) '{ if ($$1 != "" && $$1 !~ "^#.*") print $$0"\t"$$0 }' \
+ $(YPSERVERS) \
+ | $(DBLOAD) -i $(YPSERVERS) -o $(YPMAPDIR)/$@ - $(TMP); \
+ $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+ethers.byname: $(ETHERS)
+ @echo "Updating $@..."
+.if ${ETHERS} == "/dev/null"
+ @echo "Ethers source file not found -- skipping"
+.else
+ @$(AWK) '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \
+ print $$2"\t"$$0 }' $(ETHERS) | $(DBLOAD) -i $(ETHERS) \
+ -o $(YPMAPDIR)/$@ - $(TMP); $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+.endif
+
+ethers.byaddr: $(ETHERS)
+ @echo "Updating $@..."
+.if ${ETHERS} == "/dev/null"
+ @echo "Ethers source file not found -- skipping"
+.else
+ @$(AWK) '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \
+ print $$1"\t"$$0 }' $(ETHERS) | $(DBLOAD) -i $(ETHERS) \
+ -o $(YPMAPDIR)/$@ - $(TMP); $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+.endif
+
+
+bootparams: $(BOOTPARAMS)
+ @echo "Updating $@..."
+.if ${BOOTPARAMS} == "/dev/null"
+ @echo "Bootparams source file not found -- skipping"
+.else
+ @$(AWK) '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \
+ print $$0 }' $(BOOTPARAMS) | $(DBLOAD) -i $(BOOTPARAMS) \
+ -o $(YPMAPDIR)/$@ - $(TMP); $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+.endif
+
+
+netgroup: $(NETGROUP) netgroup.byhost netgroup.byuser
+ @echo "Updating $@..."
+.if ${NETGROUP} == "/dev/null"
+ @echo "Netgroup source file not found -- skipping"
+.else
+ @$(AWK) '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \
+ print $$0 }' $(NETGROUP) | $(DBLOAD) -i $(NETGROUP) \
+ -o $(YPMAPDIR)/$@ - $(TMP); $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+.endif
+
+
+netgroup.byhost: $(NETGROUP)
+ @echo "Updating $@..."
+.if ${NETGROUP} == "/dev/null"
+ @echo "Netgroup source file not found -- skipping"
+.else
+ @$(REVNETGROUP) -h -f $(NETGROUP) | \
+ $(AWK) '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \
+ print $$0 }' | $(DBLOAD) -i $(NETGROUP) \
+ -o $(YPMAPDIR)/$@ - $(TMP); $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+.endif
+
+
+netgroup.byuser: $(NETGROUP)
+ @echo "Updating $@..."
+.if ${NETGROUP} == "/dev/null"
+ @echo "Netgroup source file not found -- skipping"
+.else
+ @$(REVNETGROUP) -u -f $(NETGROUP) | \
+ $(AWK) '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \
+ print $$0 }' | $(DBLOAD) -i $(NETGROUP) \
+ -o $(YPMAPDIR)/$@ - $(TMP); $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+.endif
+
+
+hosts.byname: $(HOSTS)
+ @echo "Updating $@..."
+ @$(AWK) '/^[0-9]/ { for (n=2; n<=NF && $$n !~ "^#.*"; n++) \
+ print $$n"\t"$$0 }' $(HOSTS) | $(DBLOAD) ${B} -i $(HOSTS) \
+ -o $(YPMAPDIR)/$@ - $(TMP); $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+hosts.byaddr: $(HOSTS)
+ @echo "Updating $@..."
+ @$(AWK) '$$1 !~ "^#.*" { print $$1"\t"$$0 }' $(HOSTS) \
+ | $(DBLOAD) ${B} -i $(HOSTS) -o $(YPMAPDIR)/$@ - $(TMP); \
+ $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+ipnodes.byname: $(IPNODES)
+ @echo "Updating $@..."
+.if ${IPNODES} == "/dev/null"
+ @echo "Ipnodes source file not found -- skipping"
+.else
+ @$(AWK) '/^[0-9a-fA-F:]/ { for (n=2; n<=NF && $$n !~ "^#.*"; n++) \
+ print $$n"\t"$$0 }' $(IPNODES) | $(DBLOAD) ${B} -i $(IPNODES) \
+ -o $(YPMAPDIR)/$@ - $(TMP); $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+.endif
+
+
+ipnodes.byaddr: $(IPNODES)
+ @echo "Updating $@..."
+.if ${IPNODES} == "/dev/null"
+ @echo "Ipnodes source file not found -- skipping"
+.else
+ @$(AWK) '$$1 !~ "^#.*" { print $$1"\t"$$0 }' $(IPNODES) \
+ | $(DBLOAD) ${B} -i $(IPNODES) -o $(YPMAPDIR)/$@ - $(TMP); \
+ $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+.endif
+
+
+networks.byname: $(NETWORKS)
+ @echo "Updating $@..."
+ @$(AWK) \
+ '$$1 !~ "^#.*" { print $$1"\t"$$0; \
+ for (n=3; n<=NF && $$n !~ "^#.*"; n++) \
+ print $$n"\t"$$0 \
+ }' $(NETWORKS) \
+ | $(DBLOAD) -i $(NETWORKS) -o $(YPMAPDIR)/$@ - $(TMP); \
+ $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+networks.byaddr: $(NETWORKS)
+ @echo "Updating $@..."
+ @$(AWK) '$$1 !~ "^#.*" { print $$2"\t"$$0 }' $(NETWORKS) \
+ | $(DBLOAD) -i $(NETWORKS) -o $(YPMAPDIR)/$@ - $(TMP); \
+ $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+protocols.byname: $(PROTOCOLS)
+ @echo "Updating $@..."
+ @$(AWK) \
+ '$$1 !~ "^#.*" { print $$1"\t"$$0; \
+ for (n=3; n<=NF && $$n !~ "^#.*"; n++) \
+ print $$n"\t"$$0 \
+ }' $(PROTOCOLS) | $(DBLOAD) -i $(PROTOCOLS) \
+ -o $(YPMAPDIR)/$@ - $(TMP); $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+protocols.bynumber: $(PROTOCOLS)
+ @echo "Updating $@..."
+ @$(AWK) '$$1 !~ "^#.*" { print $$2"\t"$$0 }' $(PROTOCOLS) \
+ | $(DBLOAD) -i $(PROTOCOLS) -o $(YPMAPDIR)/$@ - $(TMP); \
+ $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+rpc.byname: $(RPC)
+ @echo "Updating $@..."
+ @$(AWK) \
+ '$$1 !~ "^#.*" { print $$1"\t"$$0; \
+ for (n=3; n<=NF && $$n !~ "^#.*"; n++) \
+ print $$n"\t"$$0 \
+ }' $(RPC) | $(DBLOAD) -i $(RPC) -o $(YPMAPDIR)/$@ - $(TMP); \
+ $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+rpc.bynumber: $(RPC)
+ @echo "Updating $@..."
+ @$(AWK) '$$1 !~ "^#.*" { print $$2"\t"$$0 }' $(RPC) \
+ | $(DBLOAD) -i $(RPC) -o $(YPMAPDIR)/$@ - $(TMP); \
+ $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+services.byname: $(SERVICES)
+ @echo "Updating $@..."
+ @$(AWK) \
+ '$$1 !~ "^#.*" { for (n=1; n<=NF && $$n !~ "^#.*"; n++) { \
+ if (split($$2, t, "/")) { \
+ printf("%s/%s", $$n, t[2]) }; \
+ print "\t"$$0; \
+ if (n == 1) n = 2; \
+ } ; print $$2"\t"$$0 ; \
+ }' $(SERVICES) \
+ | $(DBLOAD) -i $(SERVICES) -o $(YPMAPDIR)/$@ - $(TMP); \
+ $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+shells: $(SHELLS)
+ @echo "Updating $@..."
+ @$(AWK) '{ if ($$1 != "" && $$1 !~ "^#.*") print $$0"\t"$$0 }' \
+ $(SHELLS) \
+ | $(DBLOAD) -i $(SHELLS) -o $(YPMAPDIR)/$@ - $(TMP); \
+ $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+publickey.byname: $(PUBLICKEY)
+ @echo "Updating $@..."
+.if ${PUBLICKEY} == "/dev/null"
+ @echo "Publickey source file not found -- skipping"
+.else
+ @$(AWK) '$$1 !~ "^#.*" { print $$1"\t"$$2 }' $(PUBLICKEY) \
+ | $(DBLOAD) -i $(PUBLICKEY) -o $(YPMAPDIR)/$@ - $(TMP); \
+ $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+.endif
+
+
+$(PASSWD): $(MASTER)
+ @echo "Creating new $@ file from $(MASTER)..."
+ @if [ ! $(UNSECURE) ]; then \
+ $(AWK) -F: '{if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \
+ print $$1":*:"$$3":"$$4":"$$8":"$$9":"$$10}' $(MASTER) \
+ > $(PASSWD) ; \
+ else \
+ $(AWK) -F: '{if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \
+ print $$1":"$$2":"$$3":"$$4":"$$8":"$$9":"$$10}' $(MASTER) \
+ > $(PASSWD) ; fi
+
+
+passwd.byname: $(PASSWD)
+ @echo "Updating $@..."
+ @$(AWK) -F: '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \
+ print $$1"\t"$$0 }' $(PASSWD) \
+ | $(DBLOAD) -f -i $(PASSWD) -o $(YPMAPDIR)/$@ - $(TMP); \
+ $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+passwd.byuid: $(PASSWD)
+ @echo "Updating $@..."
+ @$(AWK) -F: '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \
+ print $$3"\t"$$0 }' $(PASSWD) \
+ | $(DBLOAD) -f -i $(PASSWD) -o $(YPMAPDIR)/$@ - $(TMP); \
+ $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+group.byname: $(GROUP)
+ @echo "Updating $@..."
+ @$(AWK) -F: '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \
+ print $$1"\t"$$0 }' $(GROUP) \
+ | $(DBLOAD) -f -i $(GROUP) -o $(YPMAPDIR)/$@ - $(TMP); \
+ $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+group.bygid: $(GROUP)
+ @echo "Updating $@..."
+ @$(AWK) -F: '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \
+ print $$3"\t"$$0 }' $(GROUP) \
+ | $(DBLOAD) -f -i $(GROUP) -o $(YPMAPDIR)/$@ - $(TMP); \
+ $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+netid.byname: $(GROUP) $(PASSWD) $(HOSTS)
+ @echo "Updating $@..."
+ @$(MKNETID) -q -p $(PASSWD) -g $(GROUP) -h $(HOSTS) -n $(NETID) \
+ -d $(DOMAIN) | $(DBLOAD) -o $(YPMAPDIR)/$@ - $(TMP); \
+ $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+master.passwd.byname: $(MASTER)
+ @echo "Updating $@..."
+.if ${MASTER} == "/dev/null"
+ @echo "Master.passwd source file not found -- skipping"
+.else
+ @$(AWK) -F: '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \
+ print $$1"\t"$$0 }' $(MASTER) \
+ | $(DBLOAD) ${S} -f -i $(MASTER) -o $(YPMAPDIR)/$@ - $(TMP); \
+ $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+.endif
+
+
+master.passwd.byuid: $(MASTER)
+ @echo "Updating $@..."
+.if ${MASTER} == "/dev/null"
+ @echo "Master.passwd source file not found -- skipping"
+.else
+ @$(AWK) -F: '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \
+ print $$3"\t"$$0 }' $(MASTER) \
+ | $(DBLOAD) ${S} -f -i $(MASTER) -o $(YPMAPDIR)/$@ - $(TMP); \
+ $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+.endif
+
+
+amd.map: $(AMDHOST)
+ @echo "Updating $@..."
+ @$(AWK) '$$1 !~ "^#.*" { \
+ for (i = 1; i <= NF; i++) \
+ if (i == NF) { \
+ if (substr($$i, length($$i), 1) == "\\") \
+ printf("%s", substr($$i, 1, length($$i) - 1)); \
+ else \
+ printf("%s\n", $$i); \
+ } \
+ else \
+ printf("%s ", $$i); \
+ }' $(AMDHOST) | \
+ $(DBLOAD) -i $(AMDHOST) -o $(YPMAPDIR)/$@ - $(TMP); \
+ $(RMV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
diff --git a/usr.sbin/ypserv/yp_access.c b/usr.sbin/ypserv/yp_access.c
new file mode 100644
index 0000000..30ce740
--- /dev/null
+++ b/usr.sbin/ypserv/yp_access.c
@@ -0,0 +1,330 @@
+/*
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <stdlib.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/yp.h>
+#include <rpcsvc/yppasswd.h>
+#include <rpcsvc/ypxfrd.h>
+#include <sys/types.h>
+#include <limits.h>
+#include <db.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <paths.h>
+#include <errno.h>
+#include <sys/param.h>
+#include "yp_extern.h"
+#ifdef TCP_WRAPPER
+#include "tcpd.h"
+#endif
+
+extern int debug;
+
+ /* NIS v1 */
+const char *yp_procs[] = {
+ "ypoldproc_null",
+ "ypoldproc_domain",
+ "ypoldproc_domain_nonack",
+ "ypoldproc_match",
+ "ypoldproc_first",
+ "ypoldproc_next",
+ "ypoldproc_poll",
+ "ypoldproc_push",
+ "ypoldproc_get",
+ "badproc1", /* placeholder */
+ "badproc2", /* placeholder */
+ "badproc3", /* placeholder */
+
+ /* NIS v2 */
+ "ypproc_null",
+ "ypproc_domain",
+ "ypproc_domain_nonack",
+ "ypproc_match",
+ "ypproc_first",
+ "ypproc_next",
+ "ypproc_xfr",
+ "ypproc_clear",
+ "ypproc_all",
+ "ypproc_master",
+ "ypproc_order",
+ "ypproc_maplist"
+};
+
+struct securenet {
+ struct in_addr net;
+ struct in_addr mask;
+ struct securenet *next;
+};
+
+struct securenet *securenets;
+
+#define LINEBUFSZ 1024
+
+/*
+ * Read /var/yp/securenets file and initialize the securenets
+ * list. If the file doesn't exist, we set up a dummy entry that
+ * allows all hosts to connect.
+ */
+void
+load_securenets(void)
+{
+ FILE *fp;
+ char path[MAXPATHLEN + 2];
+ char linebuf[1024 + 2];
+ struct securenet *tmp;
+
+ /*
+ * If securenets is not NULL, we are being called to reload
+ * the list; free the existing list before re-reading the
+ * securenets file.
+ */
+ while (securenets) {
+ tmp = securenets->next;
+ free(securenets);
+ securenets = tmp;
+ }
+
+ snprintf(path, MAXPATHLEN, "%s/securenets", yp_dir);
+
+ if ((fp = fopen(path, "r")) == NULL) {
+ if (errno == ENOENT) {
+ securenets = (struct securenet *)malloc(sizeof(struct securenet));
+ securenets->net.s_addr = INADDR_ANY;
+ securenets->mask.s_addr = INADDR_ANY;
+ securenets->next = NULL;
+ return;
+ } else {
+ yp_error("fopen(%s) failed: %s", path, strerror(errno));
+ exit(1);
+ }
+ }
+
+ securenets = NULL;
+
+ while (fgets(linebuf, LINEBUFSZ, fp)) {
+ char addr1[20], addr2[20];
+
+ if ((linebuf[0] == '#')
+ || (strspn(linebuf, " \t\r\n") == strlen(linebuf)))
+ continue;
+ if (sscanf(linebuf, "%s %s", addr1, addr2) < 2) {
+ yp_error("badly formatted securenets entry: %s",
+ linebuf);
+ continue;
+ }
+
+ tmp = (struct securenet *)malloc(sizeof(struct securenet));
+
+ if (!inet_aton((char *)&addr1, (struct in_addr *)&tmp->net)) {
+ yp_error("badly formatted securenets entry: %s", addr1);
+ free(tmp);
+ continue;
+ }
+
+ if (!inet_aton((char *)&addr2, (struct in_addr *)&tmp->mask)) {
+ yp_error("badly formatted securenets entry: %s", addr2);
+ free(tmp);
+ continue;
+ }
+
+ tmp->next = securenets;
+ securenets = tmp;
+ }
+
+ fclose(fp);
+
+}
+
+/*
+ * Access control functions.
+ *
+ * yp_access() checks the mapname and client host address and watches for
+ * the following things:
+ *
+ * - If the client is referencing one of the master.passwd.* maps, it must
+ * be using a privileged port to make its RPC to us. If it is, then we can
+ * assume that the caller is root and allow the RPC to succeed. If it
+ * isn't access is denied.
+ *
+ * - The client's IP address is checked against the securenets rules.
+ * There are two kinds of securenets support: the built-in support,
+ * which is very simple and depends on the presence of a
+ * /var/yp/securenets file, and tcp-wrapper support, which requires
+ * Wietse Venema's libwrap.a and tcpd.h. (Since the tcp-wrapper
+ * package does not ship with FreeBSD, we use the built-in support
+ * by default. Users can recompile the server with the tcp-wrapper library
+ * if they already have it installed and want to use hosts.allow and
+ * hosts.deny to control access instead of having a separate securenets
+ * file.)
+ *
+ * If no /var/yp/securenets file is present, the host access checks
+ * are bypassed and all hosts are allowed to connect.
+ *
+ * The yp_validdomain() function checks the domain specified by the caller
+ * to make sure it's actually served by this server. This is more a sanity
+ * check than an a security check, but this seems to be the best place for
+ * it.
+ */
+
+#ifdef DB_CACHE
+int
+yp_access(const char *map, const char *domain, const struct svc_req *rqstp)
+#else
+int
+yp_access(const char *map, const struct svc_req *rqstp)
+#endif
+{
+ struct sockaddr_in *rqhost;
+ int status_securenets = 0;
+#ifdef TCP_WRAPPER
+ int status_tcpwrap;
+#endif
+ static unsigned long oldaddr = 0;
+ struct securenet *tmp;
+ const char *yp_procedure = NULL;
+ char procbuf[50];
+
+ if (rqstp->rq_prog != YPPASSWDPROG && rqstp->rq_prog != YPPROG) {
+ snprintf(procbuf, sizeof(procbuf), "#%lu/#%lu",
+ (unsigned long)rqstp->rq_prog,
+ (unsigned long)rqstp->rq_proc);
+ yp_procedure = (char *)&procbuf;
+ } else {
+ yp_procedure = rqstp->rq_prog == YPPASSWDPROG ?
+ "yppasswdprog_update" :
+ yp_procs[rqstp->rq_proc + (12 * (rqstp->rq_vers - 1))];
+ }
+
+ rqhost = svc_getcaller(rqstp->rq_xprt);
+
+ if (debug) {
+ yp_error("procedure %s called from %s:%d", yp_procedure,
+ inet_ntoa(rqhost->sin_addr),
+ ntohs(rqhost->sin_port));
+ if (map != NULL)
+ yp_error("client is referencing map \"%s\".", map);
+ }
+
+ /* Check the map name if one was supplied. */
+ if (map != NULL) {
+ if (strchr(map, '/')) {
+ yp_error("embedded slash in map name \"%s\" -- \
+possible spoof attempt from %s:%d",
+ map, inet_ntoa(rqhost->sin_addr),
+ ntohs(rqhost->sin_port));
+ return(1);
+ }
+#ifdef DB_CACHE
+ if ((yp_testflag((char *)map, (char *)domain, YP_SECURE) ||
+#else
+ if ((strstr(map, "master.passwd.") ||
+#endif
+ (rqstp->rq_prog == YPPROG &&
+ rqstp->rq_proc == YPPROC_XFR) ||
+ (rqstp->rq_prog == YPXFRD_FREEBSD_PROG &&
+ rqstp->rq_proc == YPXFRD_GETMAP)) &&
+ ntohs(rqhost->sin_port) >= IPPORT_RESERVED) {
+ yp_error("access to %s denied -- client %s:%d \
+not privileged", map, inet_ntoa(rqhost->sin_addr), ntohs(rqhost->sin_port));
+ return(1);
+ }
+ }
+
+#ifdef TCP_WRAPPER
+ status_tcpwrap = hosts_ctl("ypserv", STRING_UNKNOWN,
+ inet_ntoa(rqhost->sin_addr), "");
+#endif
+ tmp = securenets;
+ while (tmp) {
+ if (((rqhost->sin_addr.s_addr & ~tmp->mask.s_addr)
+ | tmp->net.s_addr) == rqhost->sin_addr.s_addr) {
+ status_securenets = 1;
+ break;
+ }
+ tmp = tmp->next;
+ }
+
+#ifdef TCP_WRAPPER
+ if (status_securenets == 0 || status_tcpwrap == 0) {
+#else
+ if (status_securenets == 0) {
+#endif
+ /*
+ * One of the following two events occured:
+ *
+ * (1) The /var/yp/securenets exists and the remote host does not
+ * match any of the networks specified in it.
+ * (2) The hosts.allow file has denied access and TCP_WRAPPER is
+ * defined.
+ *
+ * In either case deny access.
+ */
+ if (rqhost->sin_addr.s_addr != oldaddr) {
+ yp_error("connect from %s:%d to procedure %s refused",
+ inet_ntoa(rqhost->sin_addr),
+ ntohs(rqhost->sin_port),
+ yp_procedure);
+ oldaddr = rqhost->sin_addr.s_addr;
+ }
+ return(1);
+ }
+ return(0);
+
+}
+
+int
+yp_validdomain(const char *domain)
+{
+ struct stat statbuf;
+ char dompath[MAXPATHLEN + 2];
+
+ if (domain == NULL || strstr(domain, "binding") ||
+ !strcmp(domain, ".") || !strcmp(domain, "..") ||
+ strchr(domain, '/') || strlen(domain) > YPMAXDOMAIN)
+ return(1);
+
+ snprintf(dompath, sizeof(dompath), "%s/%s", yp_dir, domain);
+
+ if (stat(dompath, &statbuf) < 0 || !S_ISDIR(statbuf.st_mode))
+ return(1);
+
+
+ return(0);
+}
diff --git a/usr.sbin/ypserv/yp_dblookup.c b/usr.sbin/ypserv/yp_dblookup.c
new file mode 100644
index 0000000..61944dc
--- /dev/null
+++ b/usr.sbin/ypserv/yp_dblookup.c
@@ -0,0 +1,733 @@
+/*
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <db.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <rpcsvc/yp.h>
+#include "yp_extern.h"
+
+int ypdb_debug = 0;
+enum ypstat yp_errno = YP_TRUE;
+
+#define PERM_SECURE (S_IRUSR|S_IWUSR)
+HASHINFO openinfo = {
+ 4096, /* bsize */
+ 32, /* ffactor */
+ 256, /* nelem */
+ 2048 * 512, /* cachesize */
+ NULL, /* hash */
+ 0, /* lorder */
+};
+
+#ifdef DB_CACHE
+#include <sys/queue.h>
+
+#ifndef MAXDBS
+#define MAXDBS 20
+#endif
+
+static int numdbs = 0;
+
+struct dbent {
+ DB *dbp;
+ char *name;
+ char *key;
+ int size;
+ int flags;
+};
+
+static TAILQ_HEAD(circlehead, circleq_entry) qhead;
+
+struct circleq_entry {
+ struct dbent *dbptr;
+ TAILQ_ENTRY(circleq_entry) links;
+};
+
+/*
+ * Initialize the circular queue.
+ */
+void
+yp_init_dbs(void)
+{
+ TAILQ_INIT(&qhead);
+ return;
+}
+
+/*
+ * Dynamically allocate an entry for the circular queue.
+ * Return a NULL pointer on failure.
+ */
+static struct circleq_entry *
+yp_malloc_qent(void)
+{
+ register struct circleq_entry *q;
+
+ q = (struct circleq_entry *)malloc(sizeof(struct circleq_entry));
+ if (q == NULL) {
+ yp_error("failed to malloc() circleq entry");
+ return(NULL);
+ }
+ bzero((char *)q, sizeof(struct circleq_entry));
+ q->dbptr = (struct dbent *)malloc(sizeof(struct dbent));
+ if (q->dbptr == NULL) {
+ yp_error("failed to malloc() circleq entry");
+ free(q);
+ return(NULL);
+ }
+ bzero((char *)q->dbptr, sizeof(struct dbent));
+
+ return(q);
+}
+
+/*
+ * Free a previously allocated circular queue
+ * entry.
+ */
+static void
+yp_free_qent(struct circleq_entry *q)
+{
+ /*
+ * First, close the database. In theory, this is also
+ * supposed to free the resources allocated by the DB
+ * package, including the memory pointed to by q->dbptr->key.
+ * This means we don't have to free q->dbptr->key here.
+ */
+ if (q->dbptr->dbp) {
+ (void)(q->dbptr->dbp->close)(q->dbptr->dbp);
+ q->dbptr->dbp = NULL;
+ }
+ /*
+ * Then free the database name, which was strdup()'ed.
+ */
+ free(q->dbptr->name);
+
+ /*
+ * Free the rest of the dbent struct.
+ */
+ free(q->dbptr);
+ q->dbptr = NULL;
+
+ /*
+ * Free the circleq struct.
+ */
+ free(q);
+ q = NULL;
+
+ return;
+}
+
+/*
+ * Zorch a single entry in the dbent queue and release
+ * all its resources. (This always removes the last entry
+ * in the queue.)
+ */
+static void
+yp_flush(void)
+{
+ register struct circleq_entry *qptr;
+
+ qptr = TAILQ_LAST(&qhead, circlehead);
+ TAILQ_REMOVE(&qhead, qptr, links);
+ yp_free_qent(qptr);
+ numdbs--;
+
+ return;
+}
+
+/*
+ * Close all databases, erase all database names and empty the queue.
+ */
+void
+yp_flush_all(void)
+{
+ register struct circleq_entry *qptr;
+
+ while (!TAILQ_EMPTY(&qhead)) {
+ qptr = TAILQ_FIRST(&qhead); /* save this */
+ TAILQ_REMOVE(&qhead, qptr, links);
+ yp_free_qent(qptr);
+ }
+ numdbs = 0;
+
+ return;
+}
+
+static char *inter_string = "YP_INTERDOMAIN";
+static char *secure_string = "YP_SECURE";
+static int inter_sz = sizeof("YP_INTERDOMAIN") - 1;
+static int secure_sz = sizeof("YP_SECURE") - 1;
+
+static int
+yp_setflags(DB *dbp)
+{
+ DBT key = { NULL, 0 }, data = { NULL, 0 };
+ int flags = 0;
+
+ key.data = inter_string;
+ key.size = inter_sz;
+
+ if (!(dbp->get)(dbp, &key, &data, 0))
+ flags |= YP_INTERDOMAIN;
+
+ key.data = secure_string;
+ key.size = secure_sz;
+
+ if (!(dbp->get)(dbp, &key, &data, 0))
+ flags |= YP_SECURE;
+
+ return(flags);
+}
+
+int
+yp_testflag(char *map, char *domain, int flag)
+{
+ char buf[MAXPATHLEN + 2];
+ register struct circleq_entry *qptr;
+
+ if (map == NULL || domain == NULL)
+ return(0);
+
+ strcpy(buf, domain);
+ strcat(buf, "/");
+ strcat(buf, map);
+
+ TAILQ_FOREACH(qptr, &qhead, links) {
+ if (!strcmp(qptr->dbptr->name, buf)) {
+ if (qptr->dbptr->flags & flag)
+ return(1);
+ else
+ return(0);
+ }
+ }
+
+ if (yp_open_db_cache(domain, map, NULL, 0) == NULL)
+ return(0);
+
+ if (TAILQ_FIRST(&qhead)->dbptr->flags & flag)
+ return(1);
+
+ return(0);
+}
+
+/*
+ * Add a DB handle and database name to the cache. We only maintain
+ * fixed number of entries in the cache, so if we're asked to store
+ * a new entry when all our slots are already filled, we have to kick
+ * out the entry in the last slot to make room.
+ */
+static int
+yp_cache_db(DB *dbp, char *name, int size)
+{
+ register struct circleq_entry *qptr;
+
+ if (numdbs == MAXDBS) {
+ if (ypdb_debug)
+ yp_error("queue overflow -- releasing last slot");
+ yp_flush();
+ }
+
+ /*
+ * Allocate a new queue entry.
+ */
+
+ if ((qptr = yp_malloc_qent()) == NULL) {
+ yp_error("failed to allocate a new cache entry");
+ return(1);
+ }
+
+ qptr->dbptr->dbp = dbp;
+ qptr->dbptr->name = strdup(name);
+ qptr->dbptr->size = size;
+ qptr->dbptr->key = NULL;
+
+ qptr->dbptr->flags = yp_setflags(dbp);
+
+ TAILQ_INSERT_HEAD(&qhead, qptr, links);
+ numdbs++;
+
+ return(0);
+}
+
+/*
+ * Search the list for a database matching 'name.' If we find it,
+ * move it to the head of the list and return its DB handle. If
+ * not, just fail: yp_open_db_cache() will subsequently try to open
+ * the database itself and call yp_cache_db() to add it to the
+ * list.
+ *
+ * The search works like this:
+ *
+ * - The caller specifies the name of a database to locate. We try to
+ * find an entry in our queue with a matching name.
+ *
+ * - If the caller doesn't specify a key or size, we assume that the
+ * first entry that we encounter with a matching name is returned.
+ * This will result in matches regardless of the key/size values
+ * stored in the queue entry.
+ *
+ * - If the caller also specifies a key and length, we check to see
+ * if the key and length saved in the queue entry also matches.
+ * This lets us return a DB handle that's already positioned at the
+ * correct location within a database.
+ *
+ * - Once we have a match, it gets migrated to the top of the queue
+ * so that it will be easier to find if another request for
+ * the same database comes in later.
+ */
+static DB *
+yp_find_db(const char *name, const char *key, int size)
+{
+ register struct circleq_entry *qptr;
+
+ TAILQ_FOREACH(qptr, &qhead, links) {
+ if (!strcmp(qptr->dbptr->name, name)) {
+ if (size) {
+ if (size != qptr->dbptr->size ||
+ strncmp(qptr->dbptr->key, key, size))
+ continue;
+ } else {
+ if (qptr->dbptr->size)
+ continue;
+ }
+ if (qptr != TAILQ_FIRST(&qhead)) {
+ TAILQ_REMOVE(&qhead, qptr, links);
+ TAILQ_INSERT_HEAD(&qhead, qptr, links);
+ }
+ return(qptr->dbptr->dbp);
+ }
+ }
+
+ return(NULL);
+}
+
+/*
+ * Open a DB database and cache the handle for later use. We first
+ * check the cache to see if the required database is already open.
+ * If so, we fetch the handle from the cache. If not, we try to open
+ * the database and save the handle in the cache for later use.
+ */
+DB *
+yp_open_db_cache(const char *domain, const char *map, const char *key,
+ const int size)
+{
+ DB *dbp = NULL;
+ char buf[MAXPATHLEN + 2];
+/*
+ snprintf(buf, sizeof(buf), "%s/%s", domain, map);
+*/
+ yp_errno = YP_TRUE;
+
+ strcpy(buf, domain);
+ strcat(buf, "/");
+ strcat(buf, map);
+
+ if ((dbp = yp_find_db(buf, key, size)) != NULL) {
+ return(dbp);
+ } else {
+ if ((dbp = yp_open_db(domain, map)) != NULL) {
+ if (yp_cache_db(dbp, buf, size)) {
+ (void)(dbp->close)(dbp);
+ yp_errno = YP_YPERR;
+ return(NULL);
+ }
+ }
+ }
+
+ return (dbp);
+}
+#endif
+
+/*
+ * Open a DB database.
+ */
+DB *
+yp_open_db(const char *domain, const char *map)
+{
+ DB *dbp = NULL;
+ char buf[MAXPATHLEN + 2];
+
+ yp_errno = YP_TRUE;
+
+ if (map[0] == '.' || strchr(map, '/')) {
+ yp_errno = YP_BADARGS;
+ return (NULL);
+ }
+
+#ifdef DB_CACHE
+ if (yp_validdomain(domain)) {
+ yp_errno = YP_NODOM;
+ return(NULL);
+ }
+#endif
+ snprintf(buf, sizeof(buf), "%s/%s/%s", yp_dir, domain, map);
+
+#ifdef DB_CACHE
+again:
+#endif
+ dbp = dbopen(buf,O_RDONLY, PERM_SECURE, DB_HASH, NULL);
+
+ if (dbp == NULL) {
+ switch (errno) {
+#ifdef DB_CACHE
+ case ENFILE:
+ /*
+ * We ran out of file descriptors. Nuke an
+ * open one and try again.
+ */
+ yp_error("ran out of file descriptors");
+ yp_flush();
+ goto again;
+ break;
+#endif
+ case ENOENT:
+ yp_errno = YP_NOMAP;
+ break;
+ case EFTYPE:
+ yp_errno = YP_BADDB;
+ break;
+ default:
+ yp_errno = YP_YPERR;
+ break;
+ }
+ }
+
+ return (dbp);
+}
+
+/*
+ * Database access routines.
+ *
+ * - yp_get_record(): retrieve an arbitrary key/data pair given one key
+ * to match against.
+ *
+ * - yp_first_record(): retrieve first key/data base in a database.
+ *
+ * - yp_next_record(): retrieve key/data pair that sequentially follows
+ * the supplied key value in the database.
+ */
+
+#ifdef DB_CACHE
+int
+yp_get_record(DB *dbp, const DBT *key, DBT *data, int allow)
+#else
+int
+yp_get_record(const char *domain, const char *map,
+ const DBT *key, DBT *data, int allow)
+#endif
+{
+#ifndef DB_CACHE
+ DB *dbp;
+#endif
+ int rval = 0;
+#ifndef DB_CACHE
+ static unsigned char buf[YPMAXRECORD];
+#endif
+
+ if (ypdb_debug)
+ yp_error("looking up key [%.*s]",
+ (int)key->size, (char *)key->data);
+
+ /*
+ * Avoid passing back magic "YP_*" entries unless
+ * the caller specifically requested them by setting
+ * the 'allow' flag.
+ */
+ if (!allow && !strncmp(key->data, "YP_", 3))
+ return(YP_NOKEY);
+
+#ifndef DB_CACHE
+ if ((dbp = yp_open_db(domain, map)) == NULL) {
+ return(yp_errno);
+ }
+#endif
+
+ if ((rval = (dbp->get)(dbp, key, data, 0)) != 0) {
+#ifdef DB_CACHE
+ TAILQ_FIRST(&qhead)->dbptr->size = 0;
+#else
+ (void)(dbp->close)(dbp);
+#endif
+ if (rval == 1)
+ return(YP_NOKEY);
+ else
+ return(YP_BADDB);
+ }
+
+ if (ypdb_debug)
+ yp_error("result of lookup: key: [%.*s] data: [%.*s]",
+ (int)key->size, (char *)key->data,
+ (int)data->size, (char *)data->data);
+
+#ifdef DB_CACHE
+ if (TAILQ_FIRST(&qhead)->dbptr->size) {
+ TAILQ_FIRST(&qhead)->dbptr->key = "";
+ TAILQ_FIRST(&qhead)->dbptr->size = 0;
+ }
+#else
+ bcopy(data->data, &buf, data->size);
+ data->data = &buf;
+ (void)(dbp->close)(dbp);
+#endif
+
+ return(YP_TRUE);
+}
+
+int
+yp_first_record(const DB *dbp, DBT *key, DBT *data, int allow)
+{
+ int rval;
+#ifndef DB_CACHE
+ static unsigned char buf[YPMAXRECORD];
+#endif
+
+ if (ypdb_debug)
+ yp_error("retrieving first key in map");
+
+ if ((rval = (dbp->seq)(dbp,key,data,R_FIRST)) != 0) {
+#ifdef DB_CACHE
+ TAILQ_FIRST(&qhead)->dbptr->size = 0;
+#endif
+ if (rval == 1)
+ return(YP_NOKEY);
+ else
+ return(YP_BADDB);
+ }
+
+ /* Avoid passing back magic "YP_*" records. */
+ while (!strncmp(key->data, "YP_", 3) && !allow) {
+ if ((rval = (dbp->seq)(dbp,key,data,R_NEXT)) != 0) {
+#ifdef DB_CACHE
+ TAILQ_FIRST(&qhead)->dbptr->size = 0;
+#endif
+ if (rval == 1)
+ return(YP_NOKEY);
+ else
+ return(YP_BADDB);
+ }
+ }
+
+ if (ypdb_debug)
+ yp_error("result of lookup: key: [%.*s] data: [%.*s]",
+ (int)key->size, (char *)key->data,
+ (int)data->size, (char *)data->data);
+
+#ifdef DB_CACHE
+ if (TAILQ_FIRST(&qhead)->dbptr->size) {
+ TAILQ_FIRST(&qhead)->dbptr->key = key->data;
+ TAILQ_FIRST(&qhead)->dbptr->size = key->size;
+ }
+#else
+ bcopy(data->data, &buf, data->size);
+ data->data = &buf;
+#endif
+
+ return(YP_TRUE);
+}
+
+int
+yp_next_record(const DB *dbp, DBT *key, DBT *data, int all, int allow)
+{
+ static DBT lkey = { NULL, 0 };
+ static DBT ldata = { NULL, 0 };
+ int rval;
+#ifndef DB_CACHE
+ static unsigned char keybuf[YPMAXRECORD];
+ static unsigned char datbuf[YPMAXRECORD];
+#endif
+
+ if (key == NULL || !key->size || key->data == NULL) {
+ rval = yp_first_record(dbp,key,data,allow);
+ if (rval == YP_NOKEY)
+ return(YP_NOMORE);
+ else {
+#ifdef DB_CACHE
+ TAILQ_FIRST(&qhead)->dbptr->key = key->data;
+ TAILQ_FIRST(&qhead)->dbptr->size = key->size;
+#endif
+ return(rval);
+ }
+ }
+
+ if (ypdb_debug)
+ yp_error("retrieving next key, previous was: [%.*s]",
+ (int)key->size, (char *)key->data);
+
+ if (!all) {
+#ifdef DB_CACHE
+ if (TAILQ_FIRST(&qhead)->dbptr->key == NULL) {
+#endif
+ (dbp->seq)(dbp,&lkey,&ldata,R_FIRST);
+ while (key->size != lkey.size ||
+ strncmp(key->data, lkey.data,
+ (int)key->size))
+ if ((dbp->seq)(dbp,&lkey,&ldata,R_NEXT)) {
+#ifdef DB_CACHE
+ TAILQ_FIRST(&qhead)->dbptr->size = 0;
+#endif
+ return(YP_NOKEY);
+ }
+
+#ifdef DB_CACHE
+ }
+#endif
+ }
+
+ if ((dbp->seq)(dbp,key,data,R_NEXT)) {
+#ifdef DB_CACHE
+ TAILQ_FIRST(&qhead)->dbptr->size = 0;
+#endif
+ return(YP_NOMORE);
+ }
+
+ /* Avoid passing back magic "YP_*" records. */
+ while (!strncmp(key->data, "YP_", 3) && !allow)
+ if ((dbp->seq)(dbp,key,data,R_NEXT)) {
+#ifdef DB_CACHE
+ TAILQ_FIRST(&qhead)->dbptr->size = 0;
+#endif
+ return(YP_NOMORE);
+ }
+
+ if (ypdb_debug)
+ yp_error("result of lookup: key: [%.*s] data: [%.*s]",
+ (int)key->size, (char *)key->data,
+ (int)data->size, (char *)data->data);
+
+#ifdef DB_CACHE
+ if (TAILQ_FIRST(&qhead)->dbptr->size) {
+ TAILQ_FIRST(&qhead)->dbptr->key = key->data;
+ TAILQ_FIRST(&qhead)->dbptr->size = key->size;
+ }
+#else
+ bcopy(key->data, &keybuf, key->size);
+ lkey.data = &keybuf;
+ lkey.size = key->size;
+ bcopy(data->data, &datbuf, data->size);
+ data->data = &datbuf;
+#endif
+
+ return(YP_TRUE);
+}
+
+#ifdef DB_CACHE
+/*
+ * Database glue functions.
+ */
+
+static DB *yp_currmap_db = NULL;
+static int yp_allow_db = 0;
+
+ypstat
+yp_select_map(char *map, char *domain, keydat *key, int allow)
+{
+ if (key == NULL)
+ yp_currmap_db = yp_open_db_cache(domain, map, NULL, 0);
+ else
+ yp_currmap_db = yp_open_db_cache(domain, map,
+ key->keydat_val,
+ key->keydat_len);
+
+ yp_allow_db = allow;
+ return(yp_errno);
+}
+
+ypstat
+yp_getbykey(keydat *key, valdat *val)
+{
+ DBT db_key = { NULL, 0 }, db_val = { NULL, 0 };
+ ypstat rval;
+
+ db_key.data = key->keydat_val;
+ db_key.size = key->keydat_len;
+
+ rval = yp_get_record(yp_currmap_db,
+ &db_key, &db_val, yp_allow_db);
+
+ if (rval == YP_TRUE) {
+ val->valdat_val = db_val.data;
+ val->valdat_len = db_val.size;
+ }
+
+ return(rval);
+}
+
+ypstat
+yp_firstbykey(keydat *key, valdat *val)
+{
+ DBT db_key = { NULL, 0 }, db_val = { NULL, 0 };
+ ypstat rval;
+
+ rval = yp_first_record(yp_currmap_db, &db_key, &db_val, yp_allow_db);
+
+ if (rval == YP_TRUE) {
+ key->keydat_val = db_key.data;
+ key->keydat_len = db_key.size;
+ val->valdat_val = db_val.data;
+ val->valdat_len = db_val.size;
+ }
+
+ return(rval);
+}
+
+ypstat
+yp_nextbykey(keydat *key, valdat *val)
+{
+ DBT db_key = { NULL, 0 }, db_val = { NULL, 0 };
+ ypstat rval;
+
+ db_key.data = key->keydat_val;
+ db_key.size = key->keydat_len;
+
+ rval = yp_next_record(yp_currmap_db, &db_key, &db_val, 0, yp_allow_db);
+
+ if (rval == YP_TRUE) {
+ key->keydat_val = db_key.data;
+ key->keydat_len = db_key.size;
+ val->valdat_val = db_val.data;
+ val->valdat_len = db_val.size;
+ }
+
+ return(rval);
+}
+#endif
diff --git a/usr.sbin/ypserv/yp_dnslookup.c b/usr.sbin/ypserv/yp_dnslookup.c
new file mode 100644
index 0000000..4ea6a13
--- /dev/null
+++ b/usr.sbin/ypserv/yp_dnslookup.c
@@ -0,0 +1,551 @@
+/*
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * Do standard and reverse DNS lookups using the resolver library.
+ * Take care of all the dirty work here so the main program only has to
+ * pass us a pointer to an array of characters.
+ *
+ * We have to use direct resolver calls here otherwise the YP server
+ * could end up looping by calling itself over and over again until
+ * it disappeared up its own belly button.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/fcntl.h>
+#include <sys/queue.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <resolv.h>
+#include <unistd.h>
+
+#include <rpcsvc/yp.h>
+#include "yp_extern.h"
+
+static char *
+parse(struct hostent *hp)
+{
+ static char result[MAXHOSTNAMELEN * 2];
+ int i;
+ size_t len;
+ char addr[46];
+
+ if (hp == NULL)
+ return(NULL);
+
+ if (inet_ntop(hp->h_addrtype, hp->h_addr, addr, sizeof(addr)) == NULL)
+ return(NULL);
+
+ len = strlen(addr) + 1 + strlen(hp->h_name);
+ for (i = 0; hp->h_aliases[i]; i++)
+ len += strlen(hp->h_aliases[i]) + 1;
+ len++;
+
+ if (len > sizeof(result))
+ return(NULL);
+
+ bzero(result, sizeof(result));
+ snprintf(result, sizeof(result), "%s %s", addr, hp->h_name);
+ for (i = 0; hp->h_aliases[i]; i++) {
+ strcat(result, " ");
+ strcat(result, hp->h_aliases[i]);
+ }
+
+ return ((char *)&result);
+}
+
+#define MAXPACKET (64*1024)
+#define DEF_TTL 50
+
+#define BY_DNS_ID 1
+#define BY_RPC_XID 2
+
+extern struct hostent *__dns_getanswer(char *, int, char *, int);
+
+static TAILQ_HEAD(dns_qhead, circleq_dnsentry) qhead;
+
+struct circleq_dnsentry {
+ SVCXPRT *xprt;
+ unsigned long xid;
+ struct sockaddr_in client_addr;
+ unsigned long ypvers;
+ unsigned long id;
+ unsigned long ttl;
+ unsigned long type;
+ unsigned short prot_type;
+ char **domain;
+ char *name;
+ int addrtype;
+ int addrlen;
+ uint32_t addr[4]; /* IPv4 or IPv6 */
+ TAILQ_ENTRY(circleq_dnsentry) links;
+};
+
+static int pending = 0;
+
+int
+yp_init_resolver(void)
+{
+ TAILQ_INIT(&qhead);
+ if (!(_res.options & RES_INIT) && res_init() == -1) {
+ yp_error("res_init failed");
+ return(1);
+ }
+ if ((resfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+ yp_error("couldn't create socket");
+ return(1);
+ }
+ if (fcntl(resfd, F_SETFL, O_NONBLOCK) == -1) {
+ yp_error("couldn't make resolver socket non-blocking");
+ return(1);
+ }
+ return(0);
+}
+
+static struct
+circleq_dnsentry *yp_malloc_dnsent(void)
+{
+ register struct circleq_dnsentry *q;
+
+ q = (struct circleq_dnsentry *)malloc(sizeof(struct circleq_dnsentry));
+
+ if (q == NULL) {
+ yp_error("failed to malloc() circleq dns entry");
+ return(NULL);
+ }
+
+ return(q);
+}
+
+/*
+ * Transmit a query.
+ */
+static unsigned long
+yp_send_dns_query(char *name, int type)
+{
+ char buf[MAXPACKET];
+ int n;
+ HEADER *hptr;
+ int ns;
+ int rval;
+ unsigned long id;
+
+ bzero(buf, sizeof(buf));
+
+ n = res_mkquery(QUERY,name,C_IN,type,NULL,0,NULL,buf,sizeof(buf));
+
+ if (n <= 0) {
+ yp_error("res_mkquery failed for %s type %d", name, type);
+ return(0);
+ }
+
+ hptr = (HEADER *)&buf;
+ id = ntohs(hptr->id);
+
+ for (ns = 0; ns < _res.nscount; ns++) {
+ rval = sendto(resfd, buf, n, 0,
+ (struct sockaddr *)&_res.nsaddr_list[ns],
+ sizeof(struct sockaddr));
+ if (rval == -1) {
+ yp_error("sendto failed");
+ return(0);
+ }
+ }
+
+ return(id);
+}
+
+static struct circleq_dnsentry *
+yp_find_dnsqent(unsigned long id, int type)
+{
+ register struct circleq_dnsentry *q;
+
+ TAILQ_FOREACH(q, &qhead, links) {
+ switch (type) {
+ case BY_RPC_XID:
+ if (id == q->xid)
+ return(q);
+ break;
+ case BY_DNS_ID:
+ default:
+ if (id == q->id)
+ return(q);
+ break;
+ }
+ }
+ return (NULL);
+}
+
+static void
+yp_send_dns_reply(struct circleq_dnsentry *q, char *buf)
+{
+ ypresponse result_v1;
+ ypresp_val result_v2;
+ unsigned long xid;
+ struct sockaddr_in client_addr;
+ xdrproc_t xdrfunc;
+ char *result;
+
+ /*
+ * Set up correct reply struct and
+ * XDR filter depending on ypvers.
+ */
+ switch (q->ypvers) {
+ case YPVERS:
+ bzero((char *)&result_v2, sizeof(result_v2));
+
+ if (buf == NULL)
+ result_v2.stat = YP_NOKEY;
+ else {
+ result_v2.val.valdat_len = strlen(buf);
+ result_v2.val.valdat_val = buf;
+ result_v2.stat = YP_TRUE;
+ }
+ result = (char *)&result_v2;
+ xdrfunc = (xdrproc_t)xdr_ypresp_val;
+ break;
+ case YPOLDVERS:
+ /*
+ * The odds are we will _never_ execute this
+ * particular code, but we include it anyway
+ * for the sake of completeness.
+ */
+ bzero((char *)&result_v1, sizeof(result_v1));
+ result_v1.yp_resptype = YPRESP_VAL;
+
+#define YPVAL ypresponse_u.yp_resp_valtype
+ if (buf == NULL)
+ result_v1.YPVAL.stat = YP_NOKEY;
+ else {
+ result_v1.YPVAL.val.valdat_len = strlen(buf);
+ result_v1.YPVAL.val.valdat_val = buf;
+ result_v1.YPVAL.stat = YP_TRUE;
+ }
+ result = (char *)&result_v1;
+ xdrfunc = (xdrproc_t)xdr_ypresponse;
+ break;
+ default:
+ yp_error("bad YP program version (%lu)!", q->ypvers);
+ return;
+ break;
+ }
+
+ if (debug)
+ yp_error("sending dns reply to %s (%lu)",
+ inet_ntoa(q->client_addr.sin_addr), q->id);
+ /*
+ * XXX This is disgusting. There's basically one transport
+ * handle for UDP, but we're holding off on replying to a
+ * client until we're ready, by which time we may have received
+ * several other queries from other clients with different
+ * transaction IDs. So to make the delayed response thing work,
+ * we have to save the transaction ID and client address of
+ * each request, then jam them into the transport handle when
+ * we're ready to send a reply. Then after we've send the reply,
+ * we put the old transaction ID and remote address back the
+ * way we found 'em. This is _INCREDIBLY_ non-portable; it's
+ * not even supported by the RPC library.
+ */
+ /*
+ * XXX Don't frob the transaction ID for TCP handles.
+ */
+ if (q->prot_type == SOCK_DGRAM)
+ xid = svcudp_set_xid(q->xprt, q->xid);
+ client_addr = q->xprt->xp_raddr;
+ q->xprt->xp_raddr = q->client_addr;
+
+ if (!svc_sendreply(q->xprt, xdrfunc, result))
+ yp_error("svc_sendreply failed");
+
+ /*
+ * Now that we sent the reply,
+ * put the handle back the way it was.
+ */
+ if (q->prot_type == SOCK_DGRAM)
+ svcudp_set_xid(q->xprt, xid);
+ q->xprt->xp_raddr = client_addr;
+
+ return;
+}
+
+/*
+ * Decrement TTL on all queue entries, possibly nuking
+ * any that have been around too long without being serviced.
+ */
+void
+yp_prune_dnsq(void)
+{
+ register struct circleq_dnsentry *q, *n;
+
+ q = TAILQ_FIRST(&qhead);
+ while (q != NULL) {
+ q->ttl--;
+ n = TAILQ_NEXT(q, links);
+ if (!q->ttl) {
+ TAILQ_REMOVE(&qhead, q, links);
+ free(q->name);
+ free(q);
+ pending--;
+ }
+ q = n;
+ }
+
+ if (pending < 0)
+ pending = 0;
+
+ return;
+}
+
+/*
+ * Data is pending on the DNS socket; check for valid replies
+ * to our queries and dispatch them to waiting clients.
+ */
+void
+yp_run_dnsq(void)
+{
+ register struct circleq_dnsentry *q;
+ char buf[sizeof(HEADER) + MAXPACKET];
+ struct sockaddr_in sin;
+ socklen_t len;
+ int rval;
+ HEADER *hptr;
+ struct hostent *hent;
+
+ if (debug)
+ yp_error("running dns queue");
+
+ bzero(buf, sizeof(buf));
+
+ len = sizeof(struct sockaddr_in);
+ rval = recvfrom(resfd, buf, sizeof(buf), 0,
+ (struct sockaddr *)&sin, &len);
+
+ if (rval == -1) {
+ yp_error("recvfrom failed: %s", strerror(errno));
+ return;
+ }
+
+ /*
+ * We may have data left in the socket that represents
+ * replies to earlier queries that we don't care about
+ * anymore. If there are no lookups pending or the packet
+ * ID doesn't match any of the queue IDs, just drop it
+ * on the floor.
+ */
+ hptr = (HEADER *)&buf;
+ if (!pending ||
+ (q = yp_find_dnsqent(ntohs(hptr->id), BY_DNS_ID)) == NULL) {
+ /* ignore */
+ return;
+ }
+
+ if (debug)
+ yp_error("got dns reply from %s", inet_ntoa(sin.sin_addr));
+
+ hent = __dns_getanswer(buf, rval, q->name, q->type);
+
+ if (hent != NULL) {
+ if (q->type == T_PTR) {
+ hent->h_addr = (char *)q->addr;
+ hent->h_addrtype = q->addrtype;
+ hent->h_length = q->addrlen;
+ }
+ }
+
+ /* Got an answer ready for a client -- send it off. */
+ yp_send_dns_reply(q, parse(hent));
+ pending--;
+ TAILQ_REMOVE(&qhead, q, links);
+ free(q->name);
+ free(q);
+
+ /* Decrement TTLs on other entries while we're here. */
+ yp_prune_dnsq();
+
+ return;
+}
+
+/*
+ * Queue and transmit an asynchronous DNS hostname lookup.
+ */
+ypstat
+yp_async_lookup_name(struct svc_req *rqstp, char *name, int af)
+{
+ register struct circleq_dnsentry *q;
+ socklen_t len;
+ int type;
+
+ /* Check for SOCK_DGRAM or SOCK_STREAM -- we need to know later */
+ type = -1;
+ len = sizeof(type);
+ if (getsockopt(rqstp->rq_xprt->xp_fd, SOL_SOCKET,
+ SO_TYPE, &type, &len) == -1) {
+ yp_error("getsockopt failed: %s", strerror(errno));
+ return(YP_YPERR);
+ }
+
+ /* Avoid transmitting dupe requests. */
+ if (type == SOCK_DGRAM &&
+ yp_find_dnsqent(svcudp_get_xid(rqstp->rq_xprt),BY_RPC_XID) != NULL)
+ return(YP_TRUE);
+
+ if ((q = yp_malloc_dnsent()) == NULL)
+ return(YP_YPERR);
+
+ q->type = (af == AF_INET) ? T_A : T_AAAA;
+ q->ttl = DEF_TTL;
+ q->xprt = rqstp->rq_xprt;
+ q->ypvers = rqstp->rq_vers;
+ q->prot_type = type;
+ if (q->prot_type == SOCK_DGRAM)
+ q->xid = svcudp_get_xid(q->xprt);
+ q->client_addr = q->xprt->xp_raddr;
+ q->domain = _res.dnsrch;
+ q->id = yp_send_dns_query(name, q->type);
+
+ if (q->id == 0) {
+ yp_error("DNS query failed");
+ free(q);
+ return(YP_YPERR);
+ }
+
+ q->name = strdup(name);
+ TAILQ_INSERT_HEAD(&qhead, q, links);
+ pending++;
+
+ if (debug)
+ yp_error("queueing async DNS name lookup (%d)", q->id);
+
+ yp_prune_dnsq();
+ return(YP_TRUE);
+}
+
+/*
+ * Queue and transmit an asynchronous DNS IP address lookup.
+ */
+ypstat
+yp_async_lookup_addr(struct svc_req *rqstp, char *addr, int af)
+{
+ register struct circleq_dnsentry *q;
+ char buf[MAXHOSTNAMELEN], *qp;
+ uint32_t abuf[4]; /* IPv4 or IPv6 */
+ u_char *uaddr = (u_char *)abuf;
+ socklen_t len;
+ int type, n;
+
+ /* Check for SOCK_DGRAM or SOCK_STREAM -- we need to know later */
+ type = -1;
+ len = sizeof(type);
+ if (getsockopt(rqstp->rq_xprt->xp_fd, SOL_SOCKET,
+ SO_TYPE, &type, &len) == -1) {
+ yp_error("getsockopt failed: %s", strerror(errno));
+ return(YP_YPERR);
+ }
+
+ /* Avoid transmitting dupe requests. */
+ if (type == SOCK_DGRAM &&
+ yp_find_dnsqent(svcudp_get_xid(rqstp->rq_xprt),BY_RPC_XID) != NULL)
+ return(YP_TRUE);
+
+ if ((q = yp_malloc_dnsent()) == NULL)
+ return(YP_YPERR);
+
+ switch (af) {
+ case AF_INET:
+ if (inet_aton(addr, (struct in_addr *)uaddr) != 1)
+ return(YP_NOKEY);
+ snprintf(buf, sizeof(buf), "%u.%u.%u.%u.in-addr.arpa",
+ (uaddr[3] & 0xff), (uaddr[2] & 0xff),
+ (uaddr[1] & 0xff), (uaddr[0] & 0xff));
+ len = INADDRSZ;
+ break;
+ case AF_INET6:
+ if (inet_pton(af, addr, uaddr) != 1)
+ return(YP_NOKEY);
+ qp = buf;
+ for (n = IN6ADDRSZ - 1; n >= 0; n--) {
+ qp += (size_t)sprintf(qp, "%x.%x.", uaddr[n] & 0xf,
+ (uaddr[n] >> 4) & 0xf);
+ }
+ strlcat(buf, "ip6.arpa", sizeof(buf));
+ len = IN6ADDRSZ;
+ break;
+ default:
+ return(YP_YPERR);
+ }
+
+ if (debug)
+ yp_error("DNS address is: %s", buf);
+
+ q->type = T_PTR;
+ q->ttl = DEF_TTL;
+ q->xprt = rqstp->rq_xprt;
+ q->ypvers = rqstp->rq_vers;
+ q->domain = NULL;
+ q->prot_type = type;
+ if (q->prot_type == SOCK_DGRAM)
+ q->xid = svcudp_get_xid(q->xprt);
+ q->client_addr = q->xprt->xp_raddr;
+ q->id = yp_send_dns_query(buf, q->type);
+
+ if (q->id == 0) {
+ yp_error("DNS query failed");
+ free(q);
+ return(YP_YPERR);
+ }
+
+ memcpy(q->addr, uaddr, len);
+ q->addrlen = len;
+ q->addrtype = af;
+ q->name = strdup(buf);
+ TAILQ_INSERT_HEAD(&qhead, q, links);
+ pending++;
+
+ if (debug)
+ yp_error("queueing async DNS address lookup (%d)", q->id);
+
+ yp_prune_dnsq();
+ return(YP_TRUE);
+}
diff --git a/usr.sbin/ypserv/yp_error.c b/usr.sbin/ypserv/yp_error.c
new file mode 100644
index 0000000..8d488f3
--- /dev/null
+++ b/usr.sbin/ypserv/yp_error.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * error logging/reporting facilities
+ * stolen from /usr/libexec/mail.local via ypserv
+ */
+
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include "yp_extern.h"
+
+int debug;
+extern int _rpcpmstart;
+
+extern char *progname;
+
+static void __verr(const char *fmt, va_list ap) __printflike(1, 0);
+
+static void __verr(const char *fmt, va_list ap)
+{
+ if (debug && !_rpcpmstart) {
+ fprintf(stderr,"%s: ",progname);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ } else {
+ vsyslog(LOG_NOTICE, fmt, ap);
+ }
+}
+
+void
+yp_error(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ __verr(fmt,ap);
+ va_end(ap);
+}
diff --git a/usr.sbin/ypserv/yp_extern.h b/usr.sbin/ypserv/yp_extern.h
new file mode 100644
index 0000000..2e574b6
--- /dev/null
+++ b/usr.sbin/ypserv/yp_extern.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <db.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/yp.h>
+
+#ifndef _PATH_YP
+#define _PATH_YP "/var/yp/"
+#endif
+
+#ifndef _PATH_LIBEXEC
+#define _PATH_LIBEXEC "/usr/libexec/"
+#endif
+
+#ifndef MAX_CHILDREN
+#define MAX_CHILDREN 20
+#endif
+
+#define YP_SECURE 0x1
+#define YP_INTERDOMAIN 0x2
+
+/*
+ * External functions and variables.
+ */
+
+extern int debug;
+extern int ypdb_debug;
+extern int do_dns;
+extern int children;
+extern int resfd;
+extern char *progname;
+extern char *yp_dir;
+extern pid_t yp_pid;
+
+extern enum ypstat yp_errno;
+extern void yp_error(const char *, ...) __printflike(1, 2);
+#ifdef DB_CACHE
+extern int yp_get_record(DB *, const DBT *, DBT *, int);
+#else
+extern int yp_get_record(const char *, const char *, const DBT *, DBT *, int);
+#endif
+extern int yp_first_record(const DB *, DBT *, DBT *, int);
+extern int yp_next_record(const DB *, DBT *, DBT *, int, int);
+extern char *yp_dnsname(char *);
+extern char *yp_dnsaddr(const char *);
+#ifdef DB_CACHE
+extern int yp_access(const char *, const char *, const struct svc_req *);
+#else
+extern int yp_access(const char *, const struct svc_req *);
+#endif
+extern int yp_validdomain(const char *);
+extern DB *yp_open_db(const char *, const char *);
+extern DB *yp_open_db_cache(const char *, const char *, const char *, int);
+extern void yp_flush_all(void);
+extern void yp_init_dbs(void);
+extern int yp_testflag(char *, char *, int);
+extern void load_securenets(void);
+
+#ifdef DB_CACHE
+extern ypstat yp_select_map(char *, char *, keydat *, int);
+extern ypstat yp_getbykey(keydat *, valdat *);
+extern ypstat yp_firstbykey(keydat *, valdat *);
+extern ypstat yp_nextbykey(keydat *, valdat *);
+#endif
+
+extern unsigned long svcudp_set_xid(SVCXPRT *, unsigned long);
+extern unsigned long svcudp_get_xid(SVCXPRT *);
+
+#ifndef RESOLVER_TIMEOUT
+#define RESOLVER_TIMEOUT 3600
+#endif
+
+extern int yp_init_resolver(void);
+extern void yp_run_dnsq(void);
+extern void yp_prune_dnsq(void);
+extern ypstat yp_async_lookup_name(struct svc_req *, char *, int);
+extern ypstat yp_async_lookup_addr(struct svc_req *, char *, int);
diff --git a/usr.sbin/ypserv/yp_main.c b/usr.sbin/ypserv/yp_main.c
new file mode 100644
index 0000000..7ae15d9
--- /dev/null
+++ b/usr.sbin/ypserv/yp_main.c
@@ -0,0 +1,391 @@
+/*
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+/*
+ * ypserv startup function.
+ * We need out own main() since we have to do some additional work
+ * that rpcgen won't do for us. Most of this file was generated using
+ * rpcgen.new, and later modified.
+ */
+
+#include "yp.h"
+#include <err.h>
+#include <errno.h>
+#include <memory.h>
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h> /* getenv, exit */
+#include <string.h> /* strcmp */
+#include <syslog.h>
+#include <unistd.h>
+#include <rpc/pmap_clnt.h> /* for pmap_unset */
+#ifdef __cplusplus
+#include <sysent.h> /* getdtablesize, open */
+#endif /* __cplusplus */
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/wait.h>
+#include "yp_extern.h"
+#include <rpc/rpc.h>
+
+#ifndef SIG_PF
+#define SIG_PF void(*)(int)
+#endif
+
+#define _RPCSVC_CLOSEDOWN 120
+int _rpcpmstart; /* Started by a port monitor ? */
+static int _rpcfdtype;
+ /* Whether Stream or Datagram ? */
+ /* States a server can be in wrt request */
+
+#define _IDLE 0
+#define _SERVED 1
+#define _SERVING 2
+
+extern void ypprog_1(struct svc_req *, register SVCXPRT *);
+extern void ypprog_2(struct svc_req *, register SVCXPRT *);
+extern int _rpc_dtablesize(void);
+extern int _rpcsvcstate; /* Set when a request is serviced */
+char *progname = "ypserv";
+char *yp_dir = _PATH_YP;
+/*int debug = 0;*/
+int do_dns = 0;
+int resfd;
+
+struct socktype {
+ const char *st_name;
+ int st_type;
+};
+static struct socktype stlist[] = {
+ { "tcp", SOCK_STREAM },
+ { "udp", SOCK_DGRAM },
+ { NULL, 0 }
+};
+
+static
+void _msgout(char* msg)
+{
+ if (debug) {
+ if (_rpcpmstart)
+ syslog(LOG_ERR, "%s", msg);
+ else
+ warnx("%s", msg);
+ } else
+ syslog(LOG_ERR, "%s", msg);
+}
+
+pid_t yp_pid;
+
+static void
+yp_svc_run(void)
+{
+#ifdef FD_SETSIZE
+ fd_set readfds;
+#else
+ int readfds;
+#endif /* def FD_SETSIZE */
+ int fd_setsize = _rpc_dtablesize();
+ struct timeval timeout;
+
+ /* Establish the identity of the parent ypserv process. */
+ yp_pid = getpid();
+
+ for (;;) {
+#ifdef FD_SETSIZE
+ readfds = svc_fdset;
+#else
+ readfds = svc_fds;
+#endif /* def FD_SETSIZE */
+
+ FD_SET(resfd, &readfds);
+
+ timeout.tv_sec = RESOLVER_TIMEOUT;
+ timeout.tv_usec = 0;
+ switch (select(fd_setsize, &readfds, NULL, NULL,
+ &timeout)) {
+ case -1:
+ if (errno == EINTR) {
+ continue;
+ }
+ warn("svc_run: - select failed");
+ return;
+ case 0:
+ if (getpid() == yp_pid)
+ yp_prune_dnsq();
+ break;
+ default:
+ if (getpid() == yp_pid) {
+ if (FD_ISSET(resfd, &readfds)) {
+ yp_run_dnsq();
+ FD_CLR(resfd, &readfds);
+ }
+ svc_getreqset(&readfds);
+ }
+ }
+ if (yp_pid != getpid())
+ _exit(0);
+ }
+}
+
+static void
+unregister(void)
+{
+ (void) pmap_unset(YPPROG, YPVERS);
+ (void) pmap_unset(YPPROG, YPOLDVERS);
+}
+
+static void
+reaper(int sig)
+{
+ int status;
+ int saved_errno;
+
+ saved_errno = errno;
+
+ if (sig == SIGHUP) {
+ load_securenets();
+#ifdef DB_CACHE
+ yp_flush_all();
+#endif
+ errno = saved_errno;
+ return;
+ }
+
+ if (sig == SIGCHLD) {
+ while (wait3(&status, WNOHANG, NULL) > 0)
+ children--;
+ } else {
+ unregister();
+ exit(0);
+ }
+ errno = saved_errno;
+ return;
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: ypserv [-h] [-d] [-n] [-p path] [-P port]\n");
+ exit(1);
+}
+
+static void
+closedown(int sig)
+{
+ if (_rpcsvcstate == _IDLE) {
+ extern fd_set svc_fdset;
+ static int size;
+ int i, openfd;
+
+ if (_rpcfdtype == SOCK_DGRAM) {
+ unregister();
+ exit(0);
+ }
+ if (size == 0) {
+ size = getdtablesize();
+ }
+ for (i = 0, openfd = 0; i < size && openfd < 2; i++)
+ if (FD_ISSET(i, &svc_fdset))
+ openfd++;
+ if (openfd <= 1) {
+ unregister();
+ exit(0);
+ }
+ }
+ if (_rpcsvcstate == _SERVED)
+ _rpcsvcstate = _IDLE;
+
+ (void) signal(SIGALRM, (SIG_PF) closedown);
+ (void) alarm(_RPCSVC_CLOSEDOWN/2);
+}
+
+int
+main(int argc, char *argv[])
+{
+ register SVCXPRT *transp = NULL;
+ int sock;
+ int proto = 0;
+ struct sockaddr_in saddr;
+ socklen_t asize = sizeof (saddr);
+ int ch;
+ in_port_t yp_port = 0;
+ char *errstr;
+ struct socktype *st;
+
+ while ((ch = getopt(argc, argv, "hdnp:P:")) != -1) {
+ switch (ch) {
+ case 'd':
+ debug = ypdb_debug = 1;
+ break;
+ case 'n':
+ do_dns = 1;
+ break;
+ case 'p':
+ yp_dir = optarg;
+ break;
+ case 'P':
+ yp_port = (in_port_t)strtonum(optarg, 1, 65535,
+ (const char **)&errstr);
+ if (yp_port == 0 && errstr != NULL) {
+ _msgout("invalid port number provided");
+ exit(1);
+ }
+ break;
+ case 'h':
+ default:
+ usage();
+ }
+ }
+
+ load_securenets();
+ yp_init_resolver();
+#ifdef DB_CACHE
+ yp_init_dbs();
+#endif
+ if (getsockname(0, (struct sockaddr *)&saddr, &asize) == 0) {
+ int ssize = sizeof (int);
+
+ if (saddr.sin_family != AF_INET)
+ exit(1);
+ if (getsockopt(0, SOL_SOCKET, SO_TYPE,
+ (char *)&_rpcfdtype, &ssize) == -1)
+ exit(1);
+ sock = 0;
+ _rpcpmstart = 1;
+ proto = 0;
+ openlog("ypserv", LOG_PID, LOG_DAEMON);
+ } else {
+ if (!debug) {
+ if (daemon(0,0)) {
+ err(1,"cannot fork");
+ }
+ openlog("ypserv", LOG_PID, LOG_DAEMON);
+ }
+ sock = RPC_ANYSOCK;
+ (void) pmap_unset(YPPROG, YPVERS);
+ (void) pmap_unset(YPPROG, 1);
+ }
+
+ /*
+ * Initialize TCP/UDP sockets.
+ */
+ memset((char *)&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ saddr.sin_port = htons(yp_port);
+ for (st = stlist; st->st_name != NULL; st++) {
+ /* Do not bind the socket if the user didn't specify a port */
+ if (yp_port == 0)
+ break;
+
+ sock = socket(AF_INET, st->st_type, 0);
+ if (sock == -1) {
+ if ((asprintf(&errstr, "cannot create a %s socket",
+ st->st_name)) == -1)
+ err(1, "unexpected failure in asprintf()");
+ _msgout(errstr);
+ free((void *)errstr);
+ exit(1);
+ }
+ if (bind(sock, (struct sockaddr *) &saddr, sizeof(saddr))
+ == -1) {
+ if ((asprintf(&errstr, "cannot bind %s socket",
+ st->st_name)) == -1)
+ err(1, "unexpected failure in asprintf()");
+ _msgout(errstr);
+ free((void *)errstr);
+ exit(1);
+ }
+ errstr = NULL;
+ }
+
+ if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_DGRAM)) {
+ transp = svcudp_create(sock);
+ if (transp == NULL) {
+ _msgout("cannot create udp service");
+ exit(1);
+ }
+ if (!_rpcpmstart)
+ proto = IPPROTO_UDP;
+ if (!svc_register(transp, YPPROG, YPOLDVERS, ypprog_1, proto)) {
+ _msgout("unable to register (YPPROG, YPOLDVERS, udp)");
+ exit(1);
+ }
+ if (!svc_register(transp, YPPROG, YPVERS, ypprog_2, proto)) {
+ _msgout("unable to register (YPPROG, YPVERS, udp)");
+ exit(1);
+ }
+ }
+
+ if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_STREAM)) {
+ transp = svctcp_create(sock, 0, 0);
+ if (transp == NULL) {
+ _msgout("cannot create tcp service");
+ exit(1);
+ }
+ if (!_rpcpmstart)
+ proto = IPPROTO_TCP;
+ if (!svc_register(transp, YPPROG, YPOLDVERS, ypprog_1, proto)) {
+ _msgout("unable to register (YPPROG, YPOLDVERS, tcp)");
+ exit(1);
+ }
+ if (!svc_register(transp, YPPROG, YPVERS, ypprog_2, proto)) {
+ _msgout("unable to register (YPPROG, YPVERS, tcp)");
+ exit(1);
+ }
+ }
+
+ if (transp == (SVCXPRT *)NULL) {
+ _msgout("could not create a handle");
+ exit(1);
+ }
+ if (_rpcpmstart) {
+ (void) signal(SIGALRM, (SIG_PF) closedown);
+ (void) alarm(_RPCSVC_CLOSEDOWN/2);
+ }
+/*
+ * Make sure SIGPIPE doesn't blow us away while servicing TCP
+ * connections.
+ */
+ (void) signal(SIGPIPE, SIG_IGN);
+ (void) signal(SIGCHLD, (SIG_PF) reaper);
+ (void) signal(SIGTERM, (SIG_PF) reaper);
+ (void) signal(SIGINT, (SIG_PF) reaper);
+ (void) signal(SIGHUP, (SIG_PF) reaper);
+ yp_svc_run();
+ _msgout("svc_run returned");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.sbin/ypserv/yp_server.c b/usr.sbin/ypserv/yp_server.c
new file mode 100644
index 0000000..86c7d00
--- /dev/null
+++ b/usr.sbin/ypserv/yp_server.c
@@ -0,0 +1,982 @@
+/*
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "yp.h"
+#include "yp_extern.h"
+#include <dirent.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <rpc/rpc.h>
+
+int children = 0;
+
+#define MASTER_STRING "YP_MASTER_NAME"
+#define MASTER_SZ sizeof(MASTER_STRING) - 1
+#define ORDER_STRING "YP_LAST_MODIFIED"
+#define ORDER_SZ sizeof(ORDER_STRING) - 1
+
+static pid_t
+yp_fork(void)
+{
+ if (yp_pid != getpid()) {
+ yp_error("child %d trying to fork!", getpid());
+ errno = EEXIST;
+ return(-1);
+ }
+
+ return(fork());
+}
+
+/*
+ * NIS v2 support. This is where most of the action happens.
+ */
+
+void *
+ypproc_null_2_svc(void *argp, struct svc_req *rqstp)
+{
+ static char * result;
+ static char rval = 0;
+
+#ifdef DB_CACHE
+ if (yp_access(NULL, NULL, (struct svc_req *)rqstp))
+#else
+ if (yp_access(NULL, (struct svc_req *)rqstp))
+#endif
+ return(NULL);
+
+ result = &rval;
+
+ return((void *) &result);
+}
+
+bool_t *
+ypproc_domain_2_svc(domainname *argp, struct svc_req *rqstp)
+{
+ static bool_t result;
+
+#ifdef DB_CACHE
+ if (yp_access(NULL, NULL, (struct svc_req *)rqstp)) {
+#else
+ if (yp_access(NULL, (struct svc_req *)rqstp)) {
+#endif
+ result = FALSE;
+ return (&result);
+ }
+
+ if (argp == NULL || yp_validdomain(*argp))
+ result = FALSE;
+ else
+ result = TRUE;
+
+ return (&result);
+}
+
+bool_t *
+ypproc_domain_nonack_2_svc(domainname *argp, struct svc_req *rqstp)
+{
+ static bool_t result;
+
+#ifdef DB_CACHE
+ if (yp_access(NULL, NULL, (struct svc_req *)rqstp))
+#else
+ if (yp_access(NULL, (struct svc_req *)rqstp))
+#endif
+ return (NULL);
+
+ if (argp == NULL || yp_validdomain(*argp))
+ return (NULL);
+ else
+ result = TRUE;
+
+ return (&result);
+}
+
+ypresp_val *
+ypproc_match_2_svc(ypreq_key *argp, struct svc_req *rqstp)
+{
+ static ypresp_val result;
+
+ result.val.valdat_val = "";
+ result.val.valdat_len = 0;
+
+#ifdef DB_CACHE
+ if (yp_access(argp->map, argp->domain, (struct svc_req *)rqstp)) {
+#else
+ if (yp_access(argp->map, (struct svc_req *)rqstp)) {
+#endif
+ result.stat = YP_YPERR;
+ return (&result);
+ }
+
+ if (argp->domain == NULL || argp->map == NULL) {
+ result.stat = YP_BADARGS;
+ return (&result);
+ }
+
+ if (yp_select_map(argp->map, argp->domain, NULL, 1) != YP_TRUE) {
+ result.stat = yp_errno;
+ return(&result);
+ }
+
+ result.stat = yp_getbykey(&argp->key, &result.val);
+
+ /*
+ * Do DNS lookups for hosts maps if database lookup failed.
+ */
+
+#ifdef DB_CACHE
+ if (do_dns && result.stat != YP_TRUE &&
+ (yp_testflag(argp->map, argp->domain, YP_INTERDOMAIN) ||
+ (strstr(argp->map, "hosts") || strstr(argp->map, "ipnodes")))) {
+#else
+ if (do_dns && result.stat != YP_TRUE &&
+ (strstr(argp->map, "hosts") || strstr(argp->map, "ipnodes"))) {
+#endif
+ char nbuf[YPMAXRECORD];
+
+ /* NUL terminate! NUL terminate!! NUL TERMINATE!!! */
+ bcopy(argp->key.keydat_val, nbuf, argp->key.keydat_len);
+ nbuf[argp->key.keydat_len] = '\0';
+
+ if (debug)
+ yp_error("doing DNS lookup of %s", nbuf);
+
+ if (!strcmp(argp->map, "hosts.byname"))
+ result.stat = yp_async_lookup_name(rqstp, nbuf,
+ AF_INET);
+ else if (!strcmp(argp->map, "hosts.byaddr"))
+ result.stat = yp_async_lookup_addr(rqstp, nbuf,
+ AF_INET);
+ else if (!strcmp(argp->map, "ipnodes.byname"))
+ result.stat = yp_async_lookup_name(rqstp, nbuf,
+ AF_INET6);
+ else if (!strcmp(argp->map, "ipnodes.byaddr"))
+ result.stat = yp_async_lookup_addr(rqstp, nbuf,
+ AF_INET6);
+
+ if (result.stat == YP_TRUE)
+ return(NULL);
+ }
+
+ return (&result);
+}
+
+ypresp_key_val *
+ypproc_first_2_svc(ypreq_nokey *argp, struct svc_req *rqstp)
+{
+ static ypresp_key_val result;
+
+ result.val.valdat_val = result.key.keydat_val = "";
+ result.val.valdat_len = result.key.keydat_len = 0;
+
+#ifdef DB_CACHE
+ if (yp_access(argp->map, argp->domain, (struct svc_req *)rqstp)) {
+#else
+ if (yp_access(argp->map, (struct svc_req *)rqstp)) {
+#endif
+ result.stat = YP_YPERR;
+ return (&result);
+ }
+
+ if (argp->domain == NULL) {
+ result.stat = YP_BADARGS;
+ return (&result);
+ }
+
+ if (yp_select_map(argp->map, argp->domain, NULL, 0) != YP_TRUE) {
+ result.stat = yp_errno;
+ return(&result);
+ }
+
+ result.stat = yp_firstbykey(&result.key, &result.val);
+
+ return (&result);
+}
+
+ypresp_key_val *
+ypproc_next_2_svc(ypreq_key *argp, struct svc_req *rqstp)
+{
+ static ypresp_key_val result;
+
+ result.val.valdat_val = result.key.keydat_val = "";
+ result.val.valdat_len = result.key.keydat_len = 0;
+
+#ifdef DB_CACHE
+ if (yp_access(argp->map, argp->domain, (struct svc_req *)rqstp)) {
+#else
+ if (yp_access(argp->map, (struct svc_req *)rqstp)) {
+#endif
+ result.stat = YP_YPERR;
+ return (&result);
+ }
+
+ if (argp->domain == NULL || argp->map == NULL) {
+ result.stat = YP_BADARGS;
+ return (&result);
+ }
+
+ if (yp_select_map(argp->map, argp->domain, &argp->key, 0) != YP_TRUE) {
+ result.stat = yp_errno;
+ return(&result);
+ }
+
+ result.key.keydat_len = argp->key.keydat_len;
+ result.key.keydat_val = argp->key.keydat_val;
+
+ result.stat = yp_nextbykey(&result.key, &result.val);
+
+ return (&result);
+}
+
+static void
+ypxfr_callback(ypxfrstat rval, struct sockaddr_in *addr, unsigned int transid,
+ unsigned int prognum, unsigned long port)
+{
+ CLIENT *clnt;
+ int sock = RPC_ANYSOCK;
+ struct timeval timeout;
+ yppushresp_xfr ypxfr_resp;
+ struct rpc_err err;
+
+ timeout.tv_sec = 5;
+ timeout.tv_usec = 0;
+ addr->sin_port = htons(port);
+
+ if ((clnt = clntudp_create(addr,prognum,1,timeout,&sock)) == NULL) {
+ yp_error("%s: %s", inet_ntoa(addr->sin_addr),
+ clnt_spcreateerror("failed to establish callback handle"));
+ return;
+ }
+
+ ypxfr_resp.status = rval;
+ ypxfr_resp.transid = transid;
+
+ /* Turn the timeout off -- we don't want to block. */
+ timeout.tv_sec = 0;
+ if (clnt_control(clnt, CLSET_TIMEOUT, &timeout) == FALSE)
+ yp_error("failed to set timeout on ypproc_xfr callback");
+
+ if (yppushproc_xfrresp_1(&ypxfr_resp, clnt) == NULL) {
+ clnt_geterr(clnt, &err);
+ if (err.re_status != RPC_SUCCESS &&
+ err.re_status != RPC_TIMEDOUT)
+ yp_error("%s", clnt_sperror(clnt,
+ "ypxfr callback failed"));
+ }
+
+ clnt_destroy(clnt);
+ return;
+}
+
+#define YPXFR_RETURN(CODE) \
+ /* Order is important: send regular RPC reply, then callback */ \
+ result.xfrstat = CODE; \
+ svc_sendreply(rqstp->rq_xprt, (xdrproc_t)xdr_ypresp_xfr, &result); \
+ ypxfr_callback(CODE,rqhost,argp->transid, \
+ argp->prog,argp->port); \
+ return(NULL);
+
+ypresp_xfr *
+ypproc_xfr_2_svc(ypreq_xfr *argp, struct svc_req *rqstp)
+{
+ static ypresp_xfr result;
+ struct sockaddr_in *rqhost;
+ ypresp_master *mres;
+ ypreq_nokey mreq;
+
+ result.transid = argp->transid;
+ rqhost = svc_getcaller(rqstp->rq_xprt);
+
+#ifdef DB_CACHE
+ if (yp_access(argp->map_parms.map,
+ argp->map_parms.domain, (struct svc_req *)rqstp)) {
+#else
+ if (yp_access(argp->map_parms.map, (struct svc_req *)rqstp)) {
+#endif
+ YPXFR_RETURN(YPXFR_REFUSED)
+ }
+
+
+ if (argp->map_parms.domain == NULL) {
+ YPXFR_RETURN(YPXFR_BADARGS)
+ }
+
+ if (yp_validdomain(argp->map_parms.domain)) {
+ YPXFR_RETURN(YPXFR_NODOM)
+ }
+
+ /*
+ * Determine the master host ourselves. The caller may
+ * be up to no good. This has the side effect of verifying
+ * that the requested map and domain actually exist.
+ */
+
+ mreq.domain = argp->map_parms.domain;
+ mreq.map = argp->map_parms.map;
+
+ mres = ypproc_master_2_svc(&mreq, rqstp);
+
+ if (mres->stat != YP_TRUE) {
+ yp_error("couldn't find master for map %s@%s",
+ argp->map_parms.map,
+ argp->map_parms.domain);
+ yp_error("host at %s (%s) may be pulling my leg",
+ argp->map_parms.peer,
+ inet_ntoa(rqhost->sin_addr));
+ YPXFR_RETURN(YPXFR_REFUSED)
+ }
+
+ switch (yp_fork()) {
+ case 0:
+ {
+ char g[11], t[11], p[11];
+ char ypxfr_command[MAXPATHLEN + 2];
+
+ snprintf (ypxfr_command, sizeof(ypxfr_command), "%sypxfr", _PATH_LIBEXEC);
+ snprintf (t, sizeof(t), "%u", argp->transid);
+ snprintf (g, sizeof(g), "%u", argp->prog);
+ snprintf (p, sizeof(p), "%u", argp->port);
+ if (debug) {
+ close(0); close(1); close(2);
+ }
+ if (strcmp(yp_dir, _PATH_YP)) {
+ execl(ypxfr_command, "ypxfr",
+ "-d", argp->map_parms.domain,
+ "-h", mres->peer,
+ "-p", yp_dir, "-C", t,
+ g, inet_ntoa(rqhost->sin_addr),
+ p, argp->map_parms.map,
+ NULL);
+ } else {
+ execl(ypxfr_command, "ypxfr",
+ "-d", argp->map_parms.domain,
+ "-h", mres->peer,
+ "-C", t,
+ g, inet_ntoa(rqhost->sin_addr),
+ p, argp->map_parms.map,
+ NULL);
+ }
+ yp_error("ypxfr execl(%s): %s", ypxfr_command, strerror(errno));
+ YPXFR_RETURN(YPXFR_XFRERR)
+ /*
+ * Just to safe, prevent PR #10970 from biting us in
+ * the unlikely case that execing ypxfr fails. We don't
+ * want to have any child processes spawned from this
+ * child process.
+ */
+ _exit(0);
+ break;
+ }
+ case -1:
+ yp_error("ypxfr fork(): %s", strerror(errno));
+ YPXFR_RETURN(YPXFR_XFRERR)
+ break;
+ default:
+ result.xfrstat = YPXFR_SUCC;
+ children++;
+ break;
+ }
+
+ return (&result);
+}
+#undef YPXFR_RETURN
+
+void *
+ypproc_clear_2_svc(void *argp, struct svc_req *rqstp)
+{
+ static char * result;
+ static char rval = 0;
+
+#ifdef DB_CACHE
+ if (yp_access(NULL, NULL, (struct svc_req *)rqstp))
+#else
+ if (yp_access(NULL, (struct svc_req *)rqstp))
+#endif
+ return (NULL);
+#ifdef DB_CACHE
+ /* clear out the database cache */
+ yp_flush_all();
+#endif
+ /* Re-read the securenets database for the hell of it. */
+ load_securenets();
+
+ result = &rval;
+ return((void *) &result);
+}
+
+/*
+ * For ypproc_all, we have to send a stream of ypresp_all structures
+ * via TCP, but the XDR filter generated from the yp.x protocol
+ * definition file only serializes one such structure. This means that
+ * to send the whole stream, you need a wrapper which feeds all the
+ * records into the underlying XDR routine until it hits an 'EOF.'
+ * But to use the wrapper, you have to violate the boundaries between
+ * RPC layers by calling svc_sendreply() directly from the ypproc_all
+ * service routine instead of letting the RPC dispatcher do it.
+ *
+ * Bleah.
+ */
+
+/*
+ * Custom XDR routine for serialzing results of ypproc_all: keep
+ * reading from the database and spew until we run out of records
+ * or encounter an error.
+ */
+static bool_t
+xdr_my_ypresp_all(register XDR *xdrs, ypresp_all *objp)
+{
+ while (1) {
+ /* Get a record. */
+ if ((objp->ypresp_all_u.val.stat =
+ yp_nextbykey(&objp->ypresp_all_u.val.key,
+ &objp->ypresp_all_u.val.val)) == YP_TRUE) {
+ objp->more = TRUE;
+ } else {
+ objp->more = FALSE;
+ }
+
+ /* Serialize. */
+ if (!xdr_ypresp_all(xdrs, objp))
+ return(FALSE);
+ if (objp->more == FALSE)
+ return(TRUE);
+ }
+}
+
+ypresp_all *
+ypproc_all_2_svc(ypreq_nokey *argp, struct svc_req *rqstp)
+{
+ static ypresp_all result;
+
+ /*
+ * Set this here so that the client will be forced to make
+ * at least one attempt to read from us even if all we're
+ * doing is returning an error.
+ */
+ result.more = TRUE;
+ result.ypresp_all_u.val.key.keydat_len = 0;
+ result.ypresp_all_u.val.key.keydat_val = "";
+
+#ifdef DB_CACHE
+ if (yp_access(argp->map, argp->domain, (struct svc_req *)rqstp)) {
+#else
+ if (yp_access(argp->map, (struct svc_req *)rqstp)) {
+#endif
+ result.ypresp_all_u.val.stat = YP_YPERR;
+ return (&result);
+ }
+
+ if (argp->domain == NULL || argp->map == NULL) {
+ result.ypresp_all_u.val.stat = YP_BADARGS;
+ return (&result);
+ }
+
+ /*
+ * XXX If we hit the child limit, fail the request.
+ * If we don't, and the map is large, we could block for
+ * a long time in the parent.
+ */
+ if (children >= MAX_CHILDREN) {
+ result.ypresp_all_u.val.stat = YP_YPERR;
+ return(&result);
+ }
+
+ /*
+ * The ypproc_all procedure can take a while to complete.
+ * Best to handle it in a subprocess so the parent doesn't
+ * block. (Is there a better way to do this? Maybe with
+ * async socket I/O?)
+ */
+ if (!debug) {
+ switch (yp_fork()) {
+ case 0:
+ break;
+ case -1:
+ yp_error("ypall fork(): %s", strerror(errno));
+ result.ypresp_all_u.val.stat = YP_YPERR;
+ return(&result);
+ break;
+ default:
+ children++;
+ return (NULL);
+ break;
+ }
+ }
+
+ /*
+ * Fix for PR #10971: don't let the child ypserv share
+ * DB handles with the parent process.
+ */
+#ifdef DB_CACHE
+ yp_flush_all();
+#endif
+
+ if (yp_select_map(argp->map, argp->domain,
+ &result.ypresp_all_u.val.key, 0) != YP_TRUE) {
+ result.ypresp_all_u.val.stat = yp_errno;
+ return(&result);
+ }
+
+ /* Kick off the actual data transfer. */
+ svc_sendreply(rqstp->rq_xprt, (xdrproc_t)xdr_my_ypresp_all, &result);
+
+ /*
+ * Proper fix for PR #10970: exit here so that we don't risk
+ * having a child spawned from this sub-process.
+ */
+ _exit(0);
+}
+
+ypresp_master *
+ypproc_master_2_svc(ypreq_nokey *argp, struct svc_req *rqstp)
+{
+ static ypresp_master result;
+ static char ypvalbuf[YPMAXRECORD];
+ keydat key = { MASTER_SZ, MASTER_STRING };
+ valdat val;
+
+ result.peer = "";
+
+#ifdef DB_CACHE
+ if (yp_access(argp->map, argp->domain, (struct svc_req *)rqstp)) {
+#else
+ if (yp_access(argp->map, (struct svc_req *)rqstp)) {
+#endif
+ result.stat = YP_YPERR;
+ return(&result);
+ }
+
+ if (argp->domain == NULL) {
+ result.stat = YP_BADARGS;
+ return (&result);
+ }
+
+ if (yp_select_map(argp->map, argp->domain, &key, 1) != YP_TRUE) {
+ result.stat = yp_errno;
+ return(&result);
+ }
+
+ /*
+ * Note that we copy the data retrieved from the database to
+ * a private buffer and NUL terminate the buffer rather than
+ * terminating the data in place. We do this because by stuffing
+ * a '\0' into data.data, we will actually be corrupting memory
+ * allocated by the DB package. This is a bad thing now that we
+ * cache DB handles rather than closing the database immediately.
+ */
+ result.stat = yp_getbykey(&key, &val);
+ if (result.stat == YP_TRUE) {
+ bcopy(val.valdat_val, &ypvalbuf, val.valdat_len);
+ ypvalbuf[val.valdat_len] = '\0';
+ result.peer = ypvalbuf;
+ } else
+ result.peer = "";
+
+ return (&result);
+}
+
+ypresp_order *
+ypproc_order_2_svc(ypreq_nokey *argp, struct svc_req *rqstp)
+{
+ static ypresp_order result;
+ keydat key = { ORDER_SZ, ORDER_STRING };
+ valdat val;
+
+ result.ordernum = 0;
+
+#ifdef DB_CACHE
+ if (yp_access(argp->map, argp->domain, (struct svc_req *)rqstp)) {
+#else
+ if (yp_access(argp->map, (struct svc_req *)rqstp)) {
+#endif
+ result.stat = YP_YPERR;
+ return(&result);
+ }
+
+ if (argp->domain == NULL) {
+ result.stat = YP_BADARGS;
+ return (&result);
+ }
+
+ /*
+ * We could just check the timestamp on the map file,
+ * but that's a hack: we'll only know the last time the file
+ * was touched, not the last time the database contents were
+ * updated.
+ */
+
+ if (yp_select_map(argp->map, argp->domain, &key, 1) != YP_TRUE) {
+ result.stat = yp_errno;
+ return(&result);
+ }
+
+ result.stat = yp_getbykey(&key, &val);
+
+ if (result.stat == YP_TRUE)
+ result.ordernum = atoi(val.valdat_val);
+ else
+ result.ordernum = 0;
+
+ return (&result);
+}
+
+static void yp_maplist_free(struct ypmaplist *yp_maplist)
+{
+ register struct ypmaplist *next;
+
+ while (yp_maplist) {
+ next = yp_maplist->next;
+ free(yp_maplist->map);
+ free(yp_maplist);
+ yp_maplist = next;
+ }
+ return;
+}
+
+static struct ypmaplist *
+yp_maplist_create(const char *domain)
+{
+ char yp_mapdir[MAXPATHLEN + 2];
+ char yp_mapname[MAXPATHLEN + 2];
+ struct ypmaplist *cur = NULL;
+ struct ypmaplist *yp_maplist = NULL;
+ DIR *dird;
+ struct dirent *dirp;
+ struct stat statbuf;
+
+ snprintf(yp_mapdir, sizeof(yp_mapdir), "%s/%s", yp_dir, domain);
+
+ if ((dird = opendir(yp_mapdir)) == NULL) {
+ yp_error("opendir(%s) failed: %s", yp_mapdir, strerror(errno));
+ return(NULL);
+ }
+
+ while ((dirp = readdir(dird)) != NULL) {
+ if (strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, "..")) {
+ snprintf(yp_mapname, sizeof(yp_mapname), "%s/%s",
+ yp_mapdir,dirp->d_name);
+ if (stat(yp_mapname, &statbuf) < 0 ||
+ !S_ISREG(statbuf.st_mode))
+ continue;
+ if ((cur = (struct ypmaplist *)
+ malloc(sizeof(struct ypmaplist))) == NULL) {
+ yp_error("malloc() failed");
+ closedir(dird);
+ yp_maplist_free(yp_maplist);
+ return(NULL);
+ }
+ if ((cur->map = strdup(dirp->d_name)) == NULL) {
+ yp_error("strdup() failed: %s",strerror(errno));
+ closedir(dird);
+ yp_maplist_free(yp_maplist);
+ return(NULL);
+ }
+ cur->next = yp_maplist;
+ yp_maplist = cur;
+ if (debug)
+ yp_error("map: %s", yp_maplist->map);
+ }
+
+ }
+ closedir(dird);
+ return(yp_maplist);
+}
+
+ypresp_maplist *
+ypproc_maplist_2_svc(domainname *argp, struct svc_req *rqstp)
+{
+ static ypresp_maplist result = { 0, NULL };
+
+#ifdef DB_CACHE
+ if (yp_access(NULL, NULL, (struct svc_req *)rqstp)) {
+#else
+ if (yp_access(NULL, (struct svc_req *)rqstp)) {
+#endif
+ result.stat = YP_YPERR;
+ return(&result);
+ }
+
+ if (argp == NULL) {
+ result.stat = YP_BADARGS;
+ return (&result);
+ }
+
+ if (yp_validdomain(*argp)) {
+ result.stat = YP_NODOM;
+ return (&result);
+ }
+
+ /*
+ * We have to construct a linked list for the ypproc_maplist
+ * procedure using dynamically allocated memory. Since the XDR
+ * layer won't free this list for us, we have to deal with it
+ * ourselves. We call yp_maplist_free() first to free any
+ * previously allocated data we may have accumulated to insure
+ * that we have only one linked list in memory at any given
+ * time.
+ */
+
+ yp_maplist_free(result.maps);
+
+ if ((result.maps = yp_maplist_create(*argp)) == NULL) {
+ yp_error("yp_maplist_create failed");
+ result.stat = YP_YPERR;
+ return(&result);
+ } else
+ result.stat = YP_TRUE;
+
+ return (&result);
+}
+
+/*
+ * NIS v1 support. The nullproc, domain and domain_nonack
+ * functions from v1 are identical to those in v2, so all
+ * we have to do is hand off to them.
+ *
+ * The other functions are mostly just wrappers around their v2
+ * counterparts. For example, for the v1 'match' procedure, we
+ * crack open the argument structure, make a request to the v2
+ * 'match' function, repackage the data into a v1 response and
+ * then send it on its way.
+ *
+ * Note that we don't support the pull, push and get procedures.
+ * There's little documentation available to show what they
+ * do, and I suspect they're meant largely for map transfers
+ * between master and slave servers.
+ */
+
+void *
+ypoldproc_null_1_svc(void *argp, struct svc_req *rqstp)
+{
+ return(ypproc_null_2_svc(argp, rqstp));
+}
+
+bool_t *
+ypoldproc_domain_1_svc(domainname *argp, struct svc_req *rqstp)
+{
+ return(ypproc_domain_2_svc(argp, rqstp));
+}
+
+bool_t *
+ypoldproc_domain_nonack_1_svc(domainname *argp, struct svc_req *rqstp)
+{
+ return (ypproc_domain_nonack_2_svc(argp, rqstp));
+}
+
+/*
+ * the 'match' procedure sends a response of type YPRESP_VAL
+ */
+ypresponse *
+ypoldproc_match_1_svc(yprequest *argp, struct svc_req *rqstp)
+{
+ static ypresponse result;
+ ypresp_val *v2_result;
+
+ result.yp_resptype = YPRESP_VAL;
+ result.ypresponse_u.yp_resp_valtype.val.valdat_val = "";
+ result.ypresponse_u.yp_resp_valtype.val.valdat_len = 0;
+
+ if (argp->yp_reqtype != YPREQ_KEY) {
+ result.ypresponse_u.yp_resp_valtype.stat = YP_BADARGS;
+ return(&result);
+ }
+
+ v2_result = ypproc_match_2_svc(&argp->yprequest_u.yp_req_keytype,rqstp);
+ if (v2_result == NULL)
+ return(NULL);
+
+ bcopy(v2_result, &result.ypresponse_u.yp_resp_valtype,
+ sizeof(ypresp_val));
+
+ return (&result);
+}
+
+/*
+ * the 'first' procedure sends a response of type YPRESP_KEY_VAL
+ */
+ypresponse *
+ypoldproc_first_1_svc(yprequest *argp, struct svc_req *rqstp)
+{
+ static ypresponse result;
+ ypresp_key_val *v2_result;
+
+ result.yp_resptype = YPRESP_KEY_VAL;
+ result.ypresponse_u.yp_resp_key_valtype.val.valdat_val =
+ result.ypresponse_u.yp_resp_key_valtype.key.keydat_val = "";
+ result.ypresponse_u.yp_resp_key_valtype.val.valdat_len =
+ result.ypresponse_u.yp_resp_key_valtype.key.keydat_len = 0;
+
+ if (argp->yp_reqtype != YPREQ_NOKEY) {
+ result.ypresponse_u.yp_resp_key_valtype.stat = YP_BADARGS;
+ return(&result);
+ }
+
+ v2_result = ypproc_first_2_svc(&argp->yprequest_u.yp_req_nokeytype,
+ rqstp);
+ if (v2_result == NULL)
+ return(NULL);
+
+ bcopy(v2_result, &result.ypresponse_u.yp_resp_key_valtype,
+ sizeof(ypresp_key_val));
+
+ return (&result);
+}
+
+/*
+ * the 'next' procedure sends a response of type YPRESP_KEY_VAL
+ */
+ypresponse *
+ypoldproc_next_1_svc(yprequest *argp, struct svc_req *rqstp)
+{
+ static ypresponse result;
+ ypresp_key_val *v2_result;
+
+ result.yp_resptype = YPRESP_KEY_VAL;
+ result.ypresponse_u.yp_resp_key_valtype.val.valdat_val =
+ result.ypresponse_u.yp_resp_key_valtype.key.keydat_val = "";
+ result.ypresponse_u.yp_resp_key_valtype.val.valdat_len =
+ result.ypresponse_u.yp_resp_key_valtype.key.keydat_len = 0;
+
+ if (argp->yp_reqtype != YPREQ_KEY) {
+ result.ypresponse_u.yp_resp_key_valtype.stat = YP_BADARGS;
+ return(&result);
+ }
+
+ v2_result = ypproc_next_2_svc(&argp->yprequest_u.yp_req_keytype,rqstp);
+ if (v2_result == NULL)
+ return(NULL);
+
+ bcopy(v2_result, &result.ypresponse_u.yp_resp_key_valtype,
+ sizeof(ypresp_key_val));
+
+ return (&result);
+}
+
+/*
+ * the 'poll' procedure sends a response of type YPRESP_MAP_PARMS
+ */
+ypresponse *
+ypoldproc_poll_1_svc(yprequest *argp, struct svc_req *rqstp)
+{
+ static ypresponse result;
+ ypresp_master *v2_result1;
+ ypresp_order *v2_result2;
+
+ result.yp_resptype = YPRESP_MAP_PARMS;
+ result.ypresponse_u.yp_resp_map_parmstype.domain =
+ argp->yprequest_u.yp_req_nokeytype.domain;
+ result.ypresponse_u.yp_resp_map_parmstype.map =
+ argp->yprequest_u.yp_req_nokeytype.map;
+ /*
+ * Hmm... there is no 'status' value in the
+ * yp_resp_map_parmstype structure, so I have to
+ * guess at what to do to indicate a failure.
+ * I hope this is right.
+ */
+ result.ypresponse_u.yp_resp_map_parmstype.ordernum = 0;
+ result.ypresponse_u.yp_resp_map_parmstype.peer = "";
+
+ if (argp->yp_reqtype != YPREQ_MAP_PARMS) {
+ return(&result);
+ }
+
+ v2_result1 = ypproc_master_2_svc(&argp->yprequest_u.yp_req_nokeytype,
+ rqstp);
+ if (v2_result1 == NULL)
+ return(NULL);
+
+ if (v2_result1->stat != YP_TRUE) {
+ return(&result);
+ }
+
+ v2_result2 = ypproc_order_2_svc(&argp->yprequest_u.yp_req_nokeytype,
+ rqstp);
+ if (v2_result2 == NULL)
+ return(NULL);
+
+ if (v2_result2->stat != YP_TRUE) {
+ return(&result);
+ }
+
+ result.ypresponse_u.yp_resp_map_parmstype.peer =
+ v2_result1->peer;
+ result.ypresponse_u.yp_resp_map_parmstype.ordernum =
+ v2_result2->ordernum;
+
+ return (&result);
+}
+
+ypresponse *
+ypoldproc_push_1_svc(yprequest *argp, struct svc_req *rqstp)
+{
+ static ypresponse result;
+
+ /*
+ * Not implemented.
+ */
+
+ return (&result);
+}
+
+ypresponse *
+ypoldproc_pull_1_svc(yprequest *argp, struct svc_req *rqstp)
+{
+ static ypresponse result;
+
+ /*
+ * Not implemented.
+ */
+
+ return (&result);
+}
+
+ypresponse *
+ypoldproc_get_1_svc(yprequest *argp, struct svc_req *rqstp)
+{
+ static ypresponse result;
+
+ /*
+ * Not implemented.
+ */
+
+ return (&result);
+}
diff --git a/usr.sbin/ypserv/yp_svc_udp.c b/usr.sbin/ypserv/yp_svc_udp.c
new file mode 100644
index 0000000..33d058e
--- /dev/null
+++ b/usr.sbin/ypserv/yp_svc_udp.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <rpc/rpc.h>
+#include <rpc/svc_dg.h>
+#include "yp_extern.h"
+
+#define su_data(xprt) ((struct svc_dg_data *)(xprt->xp_p2))
+
+/*
+ * We need to be able to manually set the transaction ID in the
+ * UDP transport handle, but the standard library offers us no way
+ * to do that. Hence we need this garbage.
+ */
+
+unsigned long
+svcudp_get_xid(SVCXPRT *xprt)
+{
+ struct svc_dg_data *su;
+
+ if (xprt == NULL)
+ return(0);
+ su = su_data(xprt);
+ return(su->su_xid);
+}
+
+unsigned long
+svcudp_set_xid(SVCXPRT *xprt, unsigned long xid)
+{
+ struct svc_dg_data *su;
+ unsigned long old_xid;
+
+ if (xprt == NULL)
+ return(0);
+ su = su_data(xprt);
+ old_xid = su->su_xid;
+ su->su_xid = xid;
+ return(old_xid);
+}
diff --git a/usr.sbin/ypserv/ypinit.8 b/usr.sbin/ypserv/ypinit.8
new file mode 100644
index 0000000..b99a945
--- /dev/null
+++ b/usr.sbin/ypserv/ypinit.8
@@ -0,0 +1,198 @@
+.\" Copyright (c) 1997
+.\" 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.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd November 10, 1997
+.Dt YPINIT 8
+.Os
+.Sh NAME
+.Nm ypinit
+.Nd build and install NIS databases
+.Sh SYNOPSIS
+.Nm
+.Fl m
+.Op Ar domainname
+.Nm
+.Fl s
+.Ar master_server
+.Op Ar domainname
+.Nm
+.Fl u
+.Op Ar domainname
+.Sh DESCRIPTION
+The
+.Nm
+utility is a script which sets up databases on a Network Information Service
+(NIS)
+master or slave server.
+.Pp
+On a master server,
+.Nm
+creates the
+.Pa /var/yp/$DOMAINNAME
+directory, the
+.Pa /var/yp/ypservers
+file, and calls
+.Pa /var/yp/Makefile
+to create and populate an initial set of NIS maps.
+The maps are
+created from local source files using the
+.Xr yp_mkdb 8
+utility.
+The utility will prompt the user for a list of servers
+that support the specified domain; this list is used to populate
+the ypservers map.
+.Pp
+On a slave server,
+.Nm
+creates the
+.Pa /var/yp/$DOMAINNAME ,
+populates it with copies of the NIS maps from the master.
+The maps
+are obtained from the master using the
+.Xr ypxfr 8
+utility.
+The
+.Nm
+utility obtains the list of maps to transfer in one of two ways: if
+the system is configured as an NIS client and is bound to the master
+server,
+.Nm
+is able to use the
+.Xr ypwhich 1
+utility to obtain a list of maps exported by the master server.
+If the system is not configured as a client of the NIS master,
+.Nm
+uses a hardcoded list of maps, some of which may or may not actually
+exist on the master.
+The system administrator can edit the
+.Nm
+script and
+modify the map list if necessary.
+Otherwise, individual maps can
+be transfered manually from the master using
+.Xr ypxfr 8 .
+.Sh OPTIONS
+The
+.Nm
+utility supports the following options:
+.Bl -tag -width indent
+.It Fl m Op Ar domainname
+Set up a master server.
+By default,
+.Nm
+sets up a server for
+the system default domain.
+The user can override this default by specifying
+.Ar domainname
+explicitly.
+Maps are constructed from scratch using local files as templates using
+the
+.Xr yp_mkdb 8
+utility.
+.It Fl s Ar master_server Op Ar domainname
+Set up a slave server using
+.Ar master_name
+as the master.
+Maps are copied from
+.Ar master_server
+to the slave using
+.Xr ypxfr 8 .
+By default,
+.Nm
+sets up a server for
+the system default domain.
+The user can override this default by specifying
+.Ar domainname
+explicitly.
+.It Fl u Op Ar domainname
+Update the ypservers map on the master server.
+When a new slave
+server is added to a domain, its hostname must be added to the
+ypservers map so that
+.Xr yppush 8
+can propagate updates on the master to all of the slaves.
+.El
+.Sh FILES
+.Bl -tag -width /var/yp/master.passwd -compact
+.It Pa /etc/bootparams
+Bootparams source file
+.It Pa /etc/ethers
+Ethers data source file
+.It Pa /etc/group
+Group source file
+.It Pa /etc/hosts
+Hostname/IP address source file
+.It Pa /etc/netid
+RPC netid source file
+.It Pa /etc/networks
+Networks source file
+.It Pa /etc/protocols
+Protocols source file
+.It Pa /etc/publickey
+RPC public key/secret key source file
+.It Pa /etc/services
+Services data source file
+.It Pa /etc/shells
+Shells source file
+.It Pa /var/yp/master.passwd
+Passwd database source file
+.It Pa /var/yp/netgroup
+Netgroup data source file
+.It Pa /var/yp/ypservers
+Ypservers source file (generated by
+.Nm )
+.El
+.Sh SEE ALSO
+.Xr mknetid 8 ,
+.Xr revnetgroup 8 ,
+.Xr yp 8 ,
+.Xr yp_mkdb 8 ,
+.Xr yppush 8 ,
+.Xr ypserv 8 ,
+.Xr ypxfr 8
+.Sh HISTORY
+This version of
+.Nm
+is based on the
+.Nm
+script in
+.Ox .
+It first appeared in
+.Fx 3.0 .
+.Sh AUTHORS
+.An -nosplit
+The original script was written by
+.An Mats O Jansson Aq moj@stacken.kth.se .
+It was modified for
+.Fx
+by
+.An Bill Paul Aq wpaul@ctr.columbia.edu .
diff --git a/usr.sbin/ypserv/ypinit.sh b/usr.sbin/ypserv/ypinit.sh
new file mode 100644
index 0000000..1be7e0e
--- /dev/null
+++ b/usr.sbin/ypserv/ypinit.sh
@@ -0,0 +1,386 @@
+#!/bin/sh
+# $FreeBSD$
+#
+# ypinit.sh - setup a master or slave server.
+# (Taken from OpenBSD and modified for FreeBSD.)
+#
+DOMAINNAME=/bin/domainname
+HOSTNAME=/bin/hostname
+YPWHICH=/usr/bin/ypwhich
+YPXFR=/usr/libexec/ypxfr
+YP_DIR=/var/yp
+MAKEDBM=/usr/sbin/yp_mkdb
+MAPLIST="master.passwd.byname master.passwd.byuid passwd.byname passwd.byuid \
+ group.byname group.bygid hosts.byname hosts.byaddr services.byname \
+ rpc.byname rpc.bynumber networks.byname networks.byaddr netgroup \
+ netgroup.byuser netgroup.byhost netid.byname publickey.byname \
+ bootparams ethers.byname ethers.byaddr amd.host mail.aliases \
+ ypservers protocols.byname protocols.bynumber netmasks.byaddr"
+
+ERROR_EXISTS="NO"
+umask 077
+
+#set -xv
+
+ERROR=USAGE # assume usage error
+
+if [ $# -eq 1 ]
+then
+ if [ $1 = "-m" ] # ypinit -m
+ then
+ DOMAIN=`${DOMAINNAME}`
+ SERVERTYPE=MASTER
+ ERROR=
+ fi
+
+ if [ $1 = "-u" ] # ypinit -u
+ then
+ DOMAIN=`${DOMAINNAME}`
+ SERVERTYPE=UPDATE
+ ERROR=
+ fi
+fi
+
+if [ $# -eq 2 ]
+then
+ if [ $1 = "-m" ] # ypinit -m domainname
+ then
+ DOMAIN=${2}
+ SERVERTYPE=MASTER
+ ERROR=
+ fi
+
+ if [ $1 = "-s" ] # ypinit -s master_server
+ then
+ DOMAIN=`${DOMAINNAME}`
+ SERVERTYPE=SLAVE
+ MASTER=${2}
+ ERROR=
+ fi
+
+ if [ $1 = "-u" ] # ypinit -u domainname
+ then
+ DOMAIN=${2}
+ SERVERTYPE=UPDATE
+ ERROR=
+ fi
+fi
+
+if [ $# -eq 3 ]
+then
+ if [ $1 = "-s" ] # ypinit -s master_server domainname
+ then
+ DOMAIN=${3}
+ SERVERTYPE=SLAVE
+ MASTER=${2}
+ ERROR=
+ fi
+fi
+
+if [ "${ERROR}" = "USAGE" ]; then
+ cat << \__usage 1>&2
+usage: ypinit -m [domainname]
+ ypinit -s master_server [domainname]
+ ypinit -u [domainname]
+
+The `-m' flag builds a master YP server, and the `-s' flag builds
+a slave YP server. When building a slave YP server, `master_server'
+must be an existing, reachable YP server.
+The `-u' is for updating the ypservers map on a master server.
+__usage
+
+ exit 1
+fi
+
+# Check if domainname is set, don't accept an empty domainname
+if [ -z "${DOMAIN}" ]; then
+ cat << \__no_domain 1>&2
+The local host's YP domain name has not been set. Please set it with
+the domainname(1) command or pass the domain as an argument to ypinit(8).
+__no_domain
+
+ exit 1
+fi
+
+# Check if hostname is set, don't accept an empty hostname
+HOST=`${HOSTNAME}`
+if [ -z "${HOST}" ]; then
+ cat << \__no_hostname 1>&2
+The local host's hostname has not been set. Please set it with the
+hostname(1) command.
+__no_hostname
+
+ exit 1
+fi
+
+# Check if we have contact with master.
+# If we can't list the maps on the master, then we fake it with a
+# hard-coded list of maps. The FreeBSD ypxfr command will work even
+# if ypbind isn't running or if we are bound to ourselves instead of
+# the master (the slave should be bound to itself, but since it has
+# no maps yet, we can't get a maplist from it).
+if [ "${SERVERTYPE}" = "SLAVE" ];
+then
+ COUNT=`${YPWHICH} -d ${DOMAIN} -m 2>/dev/null | grep -i ${MASTER} | wc -l | tr -d " "`
+ if [ "$COUNT" = "0" ]
+ then
+ echo "Can't enumerate maps from ${MASTER}. Please check that it is running." 1>&2
+ echo "Note: using hardcoded maplist for map transfers." 1>&2
+ YPMAPLIST=${MAPLIST}
+ else
+ YPMAPLIST=`${YPWHICH} -d ${DOMAIN} -m | cut -d\ -f1`
+ fi
+ echo "" 1>&2
+fi
+
+# Check if user is root
+ID=`id -u`
+if [ "${ID}" != "0" ]; then
+ echo "You have to be the superuser to run this. Please login as root." 1>&2
+ exit 1
+fi
+
+# Check if the YP directory exists.
+
+if [ ! -d ${YP_DIR} -o -f ${YP_DIR} ]
+then
+ echo "The directory ${YP_DIR} doesn't exist. Restore it from the distribution." 1>&2
+ exit 1
+
+fi
+
+echo -n "Server Type: ${SERVERTYPE} Domain: ${DOMAIN}"
+if [ "${SERVERTYPE}" = "SLAVE" ]; then
+ echo -n " Master: ${MASTER}"
+fi
+echo ""
+
+if [ "${SERVERTYPE}" != "UPDATE" ];
+then
+ cat << \__notice1
+
+Creating an YP server will require that you answer a few questions.
+Questions will all be asked at the beginning of the procedure.
+
+__notice1
+
+ echo -n "Do you want this procedure to quit on non-fatal errors? [y/n: n] "
+ read DOEXIT
+
+ case ${DOEXIT} in
+ y*|Y*)
+ ERROR_EXIT="YES"
+ ;;
+
+ *) ERROR_EXIT="NO"
+ echo ""
+ echo "Ok, please remember to go back and redo manually whatever fails."
+ echo "If you don't, something might not work. "
+ ;;
+ esac
+
+ if [ -d "${YP_DIR}/${DOMAIN}" ]; then
+ echo ""
+ echo -n "Can we destroy the existing ${YP_DIR}/${DOMAIN} and its contents? [y/n: n] "
+ read KILL
+
+ ERROR=
+ case ${KILL} in
+ y*|Y*)
+ ERROR="DELETE"
+ ;;
+
+ *) ERROR=
+ ;;
+ esac
+
+ if [ "${ERROR}" = "DELETE" ]; then
+ if ! rm -rf ${YP_DIR}/${DOMAIN}; then
+ echo "Can't clean up old directory ${YP_DIR}/${DOMAIN}." 1>&2
+ exit 1
+ fi
+ else
+ echo "OK, please clean it up by hand and start again. Bye"
+ exit 0
+ fi
+ fi
+
+ if ! mkdir "${YP_DIR}/${DOMAIN}"; then
+ echo "Can't make new directory ${YP_DIR}/${DOMAIN}." 1>&2
+ exit 1
+ fi
+fi
+
+if [ "${SERVERTYPE}" = "MASTER" ];
+then
+
+ if [ ! -f ${YP_DIR}/Makefile ]
+ then
+ if [ ! -f ${YP_DIR}/Makefile.dist ]
+ then
+ echo "Can't find ${YP_DIR}/Makefile.dist. " 1>&2
+ exit 1
+ fi
+ cp ${YP_DIR}/Makefile.dist ${YP_DIR}/Makefile
+ fi
+
+fi
+
+if [ "${SERVERTYPE}" = "SLAVE" ];
+then
+
+ echo "There will be no further questions. The remainder of the procedure"
+ echo "should take a few minutes, to copy the databases from ${MASTER}."
+
+ for MAP in ${YPMAPLIST}
+ do
+ echo "Transfering ${MAP}..."
+ if ! ${YPXFR} -p ${YP_DIR} -h ${MASTER} -c -d ${DOMAIN} ${MAP}; then
+ echo "Can't transfer map ${MAP}." 1>&2
+ ERROR_EXISTS="YES"
+ if [ "${ERROR_EXIT}" = "YES" ]; then
+ exit 1
+ fi
+ fi
+ done
+
+ echo ""
+ if [ "${ERROR_EXISTS}" = "YES" ]; then
+ echo "${HOST} has been setup as an YP slave server with errors. " 1>&2
+ echo "Please remember fix any problem that occurred." 1>&2
+ else
+ echo "${HOST} has been setup as an YP slave server without any errors. "
+ fi
+
+ echo "Don't forget to update map ypservers on ${MASTER}."
+ exit 0
+fi
+
+LIST_OK="NO"
+
+while [ "${LIST_OK}" = "NO" ];
+do
+ if [ "${SERVERTYPE}" = "MASTER" ];
+ then
+ HOST_LIST="${HOST}"
+ echo ""
+ echo "At this point, we have to construct a list of this domains YP servers."
+ echo "${HOST} is already known as master server."
+ echo "Please continue to add any slave servers, one per line. When you are"
+ echo "done with the list, type a <control D>."
+ echo " master server : ${HOST}"
+ fi
+
+ if [ "${SERVERTYPE}" = "UPDATE" ];
+ then
+ HOST_LIST="${HOST}"
+ NEW_LIST=""
+ MASTER_NAME=""
+ SHORT_HOST=`echo ${HOST} | cut -d. -f1`
+ if [ -f ${YP_DIR}/${DOMAIN}/ypservers ];
+ then
+ for srv in `${MAKEDBM} -u ${YP_DIR}/${DOMAIN}/ypservers | grep -v "^YP" | tr "\t" " " | cut -d\ -f1`;
+ do
+ short_srv=`echo ${srv} | cut -d. -f1`
+ if [ "${SHORT_HOST}" != "${short_srv}" ]
+ then
+ if [ "${NEW_LIST}" = "" ];
+ then
+ NEW_LIST="${srv}"
+ else
+ NEW_LIST="${NEW_LIST} ${srv}"
+ fi
+ fi
+ done;
+ MASTER_NAME=`${MAKEDBM} -u ${YP_DIR}/${DOMAIN}/ypservers | grep "^YP_MASTER_NAME" | tr "\t" " " | cut -d\ -f2`
+ fi
+ echo ""
+ echo "Update the list of hosts running YP servers in domain ${DOMAIN}."
+ echo "Master for this domain is ${MASTER_NAME}."
+ echo ""
+ echo "First verify old servers, type \\ to remove a server."
+ echo "Then add new servers, one per line. When done type a <control D>."
+ echo ""
+ echo " master server : ${HOST}"
+ if [ "${NEW_LIST}" != "" ]; then
+ for node in $NEW_LIST; do
+ echo -n " verify host : [${node}] "
+ read verify
+ if [ "${verify}" != "\\" ]; then
+ HOST_LIST="${HOST_LIST} ${node}"
+ fi
+ done;
+ fi
+ fi
+
+ echo -n " next host to add: "
+
+ while read h
+ do
+ echo -n " next host to add: "
+ HOST_LIST="${HOST_LIST} ${h}"
+ done
+
+ echo ""
+ echo "The current list of NIS servers looks like this:"
+ echo ""
+
+ for h in `echo ${HOST_LIST}`;
+ do
+ echo ${h}
+ done
+
+ echo ""
+ echo -n "Is this correct? [y/n: y] "
+ read hlist_ok
+
+ case $hlist_ok in
+ n*) echo "Let's try the whole thing again...";;
+ N*) echo "Let's try the whole thing again...";;
+ *) LIST_OK="YES";;
+ esac
+
+done
+
+echo "Building ${YP_DIR}/${DOMAIN}/ypservers..."
+rm -f ${YP_DIR}/ypservers
+touch -f ${YP_DIR}/ypservers
+rm -f ${YP_DIR}/${DOMAIN}/ypservers
+for host in ${HOST_LIST};
+do
+ echo "${host} ${host}" >> ${YP_DIR}/ypservers
+ echo "${host} ${host}"
+done | ${MAKEDBM} - ${YP_DIR}/${DOMAIN}/ypservers
+
+if [ $? -ne 0 ]; then
+ echo "" 1>&2
+ echo "Couldn't build yp data base ${YP_DIR}/${DOMAIN}/ypservers." 1>&2
+ ERROR_EXISTS="YES"
+ if [ "${ERROR_EXIT}" = "YES" ]; then
+ exit 1
+ fi
+fi
+
+if [ "${SERVERTYPE}" = "MASTER" ]; then
+ CUR_PWD=`pwd`
+ cd ${YP_DIR}
+ echo "Running ${YP_DIR}/Makefile..."
+ if ! make NOPUSH=True UPDATE_DOMAIN=${DOMAIN} YP_DIR=${YP_DIR}; then
+ echo "" 1>&2
+ echo "Error running Makefile." 1>&2
+ ERROR_EXISTS="YES"
+ if [ "${ERROR_EXIT}" = "YES" ]; then
+ exit 1
+ fi
+ fi
+
+ cd ${CUR_PWD}
+
+ echo ""
+ if [ "${ERROR_EXISTS}" = "YES" ]; then
+ echo "${HOST} has been setup as an YP master server with errors. " 1>&2
+ echo "Please remember fix any problem that occurred." 1>&2
+ else
+ echo "${HOST} has been setup as an YP master server without any errors. "
+ fi
+fi
diff --git a/usr.sbin/ypserv/ypserv.8 b/usr.sbin/ypserv/ypserv.8
new file mode 100644
index 0000000..d4da3b7
--- /dev/null
+++ b/usr.sbin/ypserv/ypserv.8
@@ -0,0 +1,452 @@
+.\" Copyright (c) 1995
+.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Bill Paul.
+.\" 4. Neither the name of the author nor the names of any co-contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd February 3, 2008
+.Dt YPSERV 8
+.Os
+.Sh NAME
+.Nm ypserv
+.Nd NIS database server
+.Sh SYNOPSIS
+.Nm
+.Op Fl n
+.Op Fl d
+.Op Fl P Ar port
+.Op Fl p Ar path
+.Sh DESCRIPTION
+.Tn NIS
+is an RPC-based service designed to allow a number of UNIX-based
+machines to share a common set of configuration files.
+Rather than
+requiring a system administrator to update several copies of files
+such as
+.Pa /etc/hosts ,
+.Pa /etc/passwd
+and
+.Pa /etc/group ,
+which tend to require frequent changes in most environments,
+.Tn NIS
+allows groups of computers to share one set of data which can be
+updated from a single location.
+.Pp
+The
+.Nm
+utility is the server that distributes
+.Tn NIS
+databases to client systems within an
+.Tn NIS
+.Em domain .
+Each client in an
+.Tn NIS
+domain must have its domainname set to
+one of the domains served by
+.Nm
+using the
+.Xr domainname 1
+command.
+The clients must also run
+.Xr ypbind 8
+in order to attach to a particular server, since it is possible to
+have several servers within a single
+.Tn NIS
+domain.
+.Pp
+The databases distributed by
+.Nm
+are stored in
+.Pa /var/yp/[domainname]
+where
+.Pa domainname
+is the name of the domain being served.
+There can be several
+such directories with different domainnames, and you need only one
+.Nm
+daemon to handle them all.
+.Pp
+The databases, or
+.Pa maps
+as they are often called,
+are created by
+.Pa /var/yp/Makefile
+using several system files as source.
+The database files are in
+.Xr db 3
+format to help speed retrieval when there are many records involved.
+In
+.Fx ,
+the maps are always readable and writable only by root for security
+reasons.
+Technically this is only necessary for the password
+maps, but since the data in the other maps can be found in
+other world-readable files anyway, it does not hurt and it is considered
+good general practice.
+.Pp
+The
+.Nm
+utility is started by
+.Pa /etc/rc.d/ypserv
+if it has been enabled in
+.Pa /etc/rc.conf .
+.Sh SPECIAL FEATURES
+There are some problems associated with distributing a
+.Fx
+password
+database via
+.Tn NIS :
+.Fx
+normally only stores encrypted passwords
+in
+.Pa /etc/master.passwd ,
+which is readable and writable only by root.
+By turning this file
+into an
+.Tn NIS
+map, this security feature would be completely defeated.
+.Pp
+To make up for this, the
+.Fx
+version of
+.Nm
+handles the
+.Pa master.passwd.byname
+and
+.Pa master.passwd.byuid
+maps in a special way.
+When the server receives a request to access
+either of these two maps, it will check the TCP port from which the
+request originated and return an error if the port number is greater
+than 1023.
+Since only the superuser is allowed to bind to TCP ports
+with values less than 1024, the server can use this test to determine
+whether or not the access request came from a privileged user.
+Any requests made by non-privileged users are therefore rejected.
+.Pp
+Furthermore, the
+.Xr getpwent 3
+routines in the
+.Fx
+standard C library will only attempt to retrieve
+data from the
+.Pa master.passwd.byname
+and
+.Pa master.passwd.byuid
+maps for the superuser: if a normal user calls any of these functions,
+the standard
+.Pa passwd.byname
+and
+.Pa passwd.byuid
+maps will be accessed instead.
+The latter two maps are constructed by
+.Pa /var/yp/Makefile
+by parsing the
+.Pa master.passwd
+file and stripping out the password fields, and are therefore
+safe to pass on to unprivileged users.
+In this way, the shadow password
+aspect of the protected
+.Pa master.passwd
+database is maintained through
+.Tn NIS .
+.Sh NOTES
+.Ss Setting Up Master and Slave Servers
+.Xr ypinit 8
+is a convenient script that will help setup master and slave
+.Tn NIS
+servers.
+.Ss Limitations
+There are two problems inherent with password shadowing in
+.Tn NIS
+that users should
+be aware of:
+.Bl -enum -offset indent
+.It
+The
+.Sq TCP port less than 1024
+test is trivial to defeat for users with
+unrestricted access to machines on your network (even those machines
+which do not run UNIX-based operating systems).
+.It
+If you plan to use a
+.Fx
+system to serve
+.No non- Ns Fx
+clients that
+have no support for password shadowing (which is most of them), you
+will have to disable the password shadowing entirely by uncommenting the
+.Em UNSECURE=True
+entry in
+.Pa /var/yp/Makefile .
+This will cause the standard
+.Pa passwd.byname
+and
+.Pa passwd.byuid
+maps to be generated with valid encrypted password fields, which is
+necessary in order for
+.No non- Ns Fx
+clients to perform user
+authentication through
+.Tn NIS .
+.El
+.Pp
+.Ss Security
+In general, any remote user can issue an RPC to
+.Nm
+and retrieve the contents of your
+.Tn NIS
+maps, provided the remote user
+knows your domain name.
+To prevent such unauthorized transactions,
+.Nm
+supports a feature called
+.Pa securenets
+which can be used to restrict access to a given set of hosts.
+At startup,
+.Nm
+will attempt to load the securenets information from a file
+called
+.Pa /var/yp/securenets .
+(Note that this path varies depending on the path specified with
+the
+.Fl p
+option, which is explained below.)
+This file contains entries
+that consist of a network specification and a network mask separated
+by white space.
+Lines starting with
+.Dq \&#
+are considered to be comments.
+A
+sample securenets file might look like this:
+.Bd -unfilled -offset indent
+# allow connections from local host -- mandatory
+127.0.0.1 255.255.255.255
+# allow connections from any host
+# on the 192.168.128.0 network
+192.168.128.0 255.255.255.0
+# allow connections from any host
+# between 10.0.0.0 to 10.0.15.255
+10.0.0.0 255.255.240.0
+.Ed
+.Pp
+If
+.Nm
+receives a request from an address that matches one of these rules,
+it will process the request normally.
+If the address fails to match
+a rule, the request will be ignored and a warning message will be
+logged.
+If the
+.Pa /var/yp/securenets
+file does not exist,
+.Nm
+will allow connections from any host.
+.Pp
+The
+.Nm
+utility also has support for Wietse Venema's
+.Em tcpwrapper
+package.
+This allows the administrator to use the tcpwrapper
+configuration files
+.Pa ( /etc/hosts.allow
+and
+.Pa /etc/hosts.deny )
+for access control instead of
+.Pa /var/yp/securenets .
+.Pp
+Note: while both of these access control mechanisms provide some
+security, they, like the privileged port test, are both vulnerable
+to
+.Dq IP spoofing
+attacks.
+.Pp
+.Ss NIS v1 compatibility
+This version of
+.Nm
+has some support for serving
+.Tn NIS
+v1 clients.
+The
+.Fx
+.Tn NIS
+implementation only uses the
+.Tn NIS
+v2 protocol, however other implementations
+include support for the v1 protocol for backwards compatibility
+with older systems.
+The
+.Xr ypbind 8
+daemons supplied with these systems will try to establish a binding
+to an
+.Tn NIS
+v1 server even though they may never actually need it (and they may
+persist in broadcasting in search of one even after they receive a
+response from a v2 server).
+Note that while
+support for normal client calls is provided, this version of
+.Nm
+does not handle v1 map transfer requests; consequently, it cannot
+be used as a master or slave in conjunction with older
+.Tn NIS
+servers that
+only support the v1 protocol.
+Fortunately, there probably are not any
+such servers still in use today.
+.Ss NIS servers that are also NIS clients
+Care must be taken when running
+.Nm
+in a multi-server domain where the server machines are also
+.Tn NIS
+clients.
+It is generally a good idea to force the servers to
+bind to themselves rather than allowing them to broadcast bind
+requests and possibly become bound to each other: strange failure
+modes can result if one server goes down and
+others are dependent upon on it.
+(Eventually all the clients will
+time out and attempt to bind to other servers, but the delay
+involved can be considerable and the failure mode is still present
+since the servers might bind to each other all over again).
+.Pp
+Refer to the
+.Xr ypbind 8
+man page for details on how to force it to bind to a particular
+server.
+.Sh OPTIONS
+The following options are supported by
+.Nm :
+.Bl -tag -width flag
+.It Fl n
+This option affects the way
+.Nm
+handles yp_match requests for the
+.Pa hosts.byname
+and
+.Pa hosts.byaddress
+maps.
+By default, if
+.Nm
+cannot find an entry for a given host in its hosts maps, it will
+return an error and perform no further processing.
+With the
+.Fl n
+flag,
+.Nm
+will go one step further: rather than giving up immediately, it
+will try to resolve the hostname or address using a DNS nameserver
+query.
+If the query is successful,
+.Nm
+will construct a fake database record and return it to the client,
+thereby making it seem as though the client's yp_match request
+succeeded.
+.Pp
+This feature is provided for compatibility with SunOS 4.1.x,
+which has brain-damaged resolver functions in its standard C
+library that depend on
+.Tn NIS
+for hostname and address resolution.
+The
+.Fx
+resolver can be configured to do DNS
+queries directly, therefore it is not necessary to enable this
+option when serving only
+.Fx
+.Tn NIS
+clients.
+.It Fl d
+Cause the server to run in debugging mode.
+Normally,
+.Nm
+reports only unusual errors (access violations, file access failures)
+using the
+.Xr syslog 3
+facility.
+In debug mode, the server does not background
+itself and prints extra status messages to stderr for each
+request that it receives.
+Also, while running in debug mode,
+.Nm
+will not spawn any additional subprocesses as it normally does
+when handling yp_all requests or doing DNS lookups.
+(These actions
+often take a fair amount of time to complete and are therefore handled
+in subprocesses, allowing the parent server process to go on handling
+other requests.)
+This makes it easier to trace the server with
+a debugging tool.
+.It Fl P Ar port
+Force ypserv to bind to a specific TCP/UDP port, rather than selecting
+its own.
+.It Fl p Ar path
+Normally,
+.Nm
+assumes that all
+.Tn NIS
+maps are stored under
+.Pa /var/yp .
+The
+.Fl p
+flag may be used to specify an alternate
+.Tn NIS
+root path, allowing
+the system administrator to move the map files to a different place
+within the file system.
+.El
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /var/yp/[domainname]/[maps]
+the
+.Tn NIS
+maps
+.It Pa /etc/nsswitch.conf
+name switch configuration file
+.It Pa /var/yp/securenets
+host access control file
+.El
+.Sh SEE ALSO
+.Xr ypcat 1 ,
+.Xr db 3 ,
+.Xr hosts_access 5 ,
+.Xr rpc.yppasswdd 8 ,
+.Xr yp 8 ,
+.Xr ypbind 8 ,
+.Xr ypinit 8 ,
+.Xr yppush 8 ,
+.Xr ypxfr 8
+.Sh HISTORY
+This version of
+.Nm
+first appeared in
+.Fx 2.2 .
+.Sh AUTHORS
+.An Bill Paul Aq wpaul@ctr.columbia.edu
diff --git a/usr.sbin/ypset/Makefile b/usr.sbin/ypset/Makefile
new file mode 100644
index 0000000..10ed5f8
--- /dev/null
+++ b/usr.sbin/ypset/Makefile
@@ -0,0 +1,7 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $FreeBSD$
+
+PROG= ypset
+MAN= ypset.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ypset/ypset.8 b/usr.sbin/ypset/ypset.8
new file mode 100644
index 0000000..49ad6e0
--- /dev/null
+++ b/usr.sbin/ypset/ypset.8
@@ -0,0 +1,88 @@
+.\"
+.\" Copyright (c) 1994 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 by Jason Thorpe.
+.\" 4. Neither the name of the author 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 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 October 25, 1994
+.Dt YPSET 8
+.Os
+.Sh NAME
+.Nm ypset
+.Nd tell
+.Xr ypbind 8
+which YP server process to use
+.Sh SYNOPSIS
+.Nm
+.Op Fl h Ar host
+.Op Fl d Ar domain
+.Ar server
+.Sh DESCRIPTION
+The
+.Nm
+utility tells the
+.Xr ypbind 8
+process on the current machine which YP server process to communicate with.
+If
+.Ar server
+is down or is not running a YP server process, it is not discovered until
+a YP client process attempts to access a YP map, at which time
+.Xr ypbind 8
+tests the binding and takes appropriate action.
+.Pp
+The
+.Nm
+utility
+is most useful for binding a YP client that is not on the same broadcast
+network as the closest YP server, but can also be used for debugging
+a local network's YP configuration, testing specific YP client
+programs, or binding to a specific server when there are many servers on
+the local network supplying YP maps.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl h Ar host
+Set the YP binding on
+.Ar host
+instead of the local machine.
+.It Fl d Ar domain
+Use the YP domain
+.Ar domain
+instead of the default domain as returned by
+.Xr domainname 1 .
+.El
+.Sh SEE ALSO
+.Xr domainname 1 ,
+.Xr ypcat 1 ,
+.Xr ypmatch 1 ,
+.Xr yp 8 ,
+.Xr ypbind 8
+.Sh AUTHORS
+.An Theo De Raadt
diff --git a/usr.sbin/ypset/ypset.c b/usr.sbin/ypset/ypset.c
new file mode 100644
index 0000000..b63578a
--- /dev/null
+++ b/usr.sbin/ypset/ypset.c
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 1992/3 Theo de Raadt <deraadt@fsa.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <err.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <rpcsvc/yp.h>
+#include <rpcsvc/ypclnt.h>
+#include <arpa/inet.h>
+
+extern bool_t xdr_domainname();
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: ypset [-h host] [-d domain] server\n");
+ exit(1);
+}
+
+int
+bind_tohost(struct sockaddr_in *sin, char *dom, char *server)
+{
+ struct ypbind_setdom ypsd;
+ struct timeval tv;
+ struct hostent *hp;
+ CLIENT *client;
+ int sock, port;
+ int r;
+ unsigned long server_addr;
+
+ if ((port = htons(getrpcport(server, YPPROG, YPPROC_NULL, IPPROTO_UDP))) == 0)
+ errx(1, "%s not running ypserv", server);
+
+ bzero(&ypsd, sizeof ypsd);
+
+ if ((hp = gethostbyname (server)) != NULL) {
+ /* is this the most compatible way?? */
+ bcopy (hp->h_addr_list[0],
+ (u_long *)&ypsd.ypsetdom_binding.ypbind_binding_addr,
+ sizeof (unsigned long));
+ } else if ((long)(server_addr = inet_addr (server)) == -1) {
+ errx(1, "can't find address for %s", server);
+ } else
+ bcopy (&server_addr,
+ (u_long *)&ypsd.ypsetdom_binding.ypbind_binding_addr,
+ sizeof (server_addr));
+
+/* strncpy(ypsd.ypsetdom_domain, dom, sizeof ypsd.ypsetdom_domain); */
+ ypsd.ypsetdom_domain = dom;
+ *(u_long *)&ypsd.ypsetdom_binding.ypbind_binding_port = port;
+ ypsd.ypsetdom_vers = YPVERS;
+
+ tv.tv_sec = 15;
+ tv.tv_usec = 0;
+ sock = RPC_ANYSOCK;
+ client = clntudp_create(sin, YPBINDPROG, YPBINDVERS, tv, &sock);
+ if (client == NULL) {
+ warnx("can't yp_bind, reason: %s", yperr_string(YPERR_YPBIND));
+ return (YPERR_YPBIND);
+ }
+ client->cl_auth = authunix_create_default();
+
+ r = clnt_call(client, YPBINDPROC_SETDOM,
+ (xdrproc_t)xdr_ypbind_setdom, &ypsd,
+ (xdrproc_t)xdr_void, NULL, tv);
+ if (r) {
+ warnx("sorry, cannot ypset for domain %s on host - make sure ypbind was started with -ypset or -ypsetme", dom);
+ clnt_destroy(client);
+ return (YPERR_YPBIND);
+ }
+ clnt_destroy(client);
+ return (0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ struct sockaddr_in sin;
+ struct hostent *hent;
+ char *domainname;
+ int c;
+
+ yp_get_default_domain(&domainname);
+
+ bzero(&sin, sizeof sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+
+ while ((c = getopt(argc, argv, "h:d:")) != -1)
+ switch (c) {
+ case 'd':
+ domainname = optarg;
+ break;
+ case 'h':
+ if ((sin.sin_addr.s_addr = inet_addr(optarg)) == -1) {
+ hent = gethostbyname(optarg);
+ if (hent == NULL)
+ errx(1, "host %s unknown", optarg);
+ bcopy(hent->h_addr_list[0], &sin.sin_addr,
+ sizeof sin.sin_addr);
+ }
+ break;
+ default:
+ usage();
+ }
+
+ if (optind + 1 != argc)
+ usage();
+
+ if (bind_tohost(&sin, domainname, argv[optind]))
+ exit(1);
+ exit(0);
+}
diff --git a/usr.sbin/zic/Arts.htm b/usr.sbin/zic/Arts.htm
new file mode 100644
index 0000000..baa84ed
--- /dev/null
+++ b/usr.sbin/zic/Arts.htm
@@ -0,0 +1,178 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<!-- $FreeBSD$ -->
+<HTML>
+<HEAD>
+<TITLE>Time and the Arts</TITLE>
+</HEAD>
+<BODY>
+<H1>Time and the Arts</H1>
+<P>
+<H6>
+@(#)Arts.htm 7.18
+</H6>
+</P>
+<PRE>
+Data on recordings of "Save That Time," Russ Long, Serrob Publishing, BMI:
+--------------------------------------------------------------------------
+Artist: Karrin Allyson
+CD: I Didn't Know About You
+Copyright Date: 1993
+Label: Concord Jazz, Inc.
+ID: CCD-4543
+Track Time: 3:44
+Personnel: Karrin Allyson, vocal
+ Russ Long, piano
+ Gerald Spaits, bass
+ Todd Strait, drums
+Notes: CD notes "additional lyric by Karrin Allyson;
+ arranged by Russ Long and Karrin Allyson"
+ADO Rating: 1 star
+<A HREF="http://205.186.189.2/cgi-win/amg.exe?sql=1A_IDR|||175928">AMG Rating: 3.5 stars</A>
+Penguin Rating: 3.5 stars
+--------------------------------------------------------------------------
+Artist: Kevin Mahogany
+CD: Double Rainbow
+Copyright Date: 1993
+Label: Enja Records
+ID: ENJ-7097 2
+Track Time: 6:27
+Personnel: Kevin Mahogany, vocal
+ Kenny Barron, piano
+ Ray Drummond, bss
+ Ralph Moore, tenor saxophone
+ Lewis Nash, drums
+ADO Rating: 1.5 stars
+<A HREF="http://205.186.189.2/cgi-win/amg.exe?sql=1A_IDR|||262654">AMG Rating: unrated</A>
+Penguin Rating: 3 stars
+--------------------------------------------------------------------------
+Artist: Joe Williams
+CD: Here's to Life
+Copyright Date: 1994
+Label: Telarc International Corporation
+ID: CD-83357
+Track Time: 3:58
+Personnel: Joe Williams, vocal
+ The Robert Farnon [39 piece] Orchestra
+Notes: On-line information and samples available at
+ <A HREF="http://www.telarc.com/telarc/releases/release.req?ID=83357">http://telarc.dmn.com/telarc/releases/release.req?ID=83357</A>
+ADO Rating: black dot
+<A HREF="http://205.186.189.2/cgi-win/amg.exe?sql=1A_IDR|||194434">AMG Rating: 2 stars</A>
+Penguin Rating: 3 stars
+--------------------------------------------------------------------------
+Artist: Charles Fambrough
+CD: Keeper of the Spirit
+Copyright Date: 1995
+Label: AudioQuest Music
+ID: AQ-CD1033
+Track Time: 7:07
+Personnel: Charles Fambrough, bass
+ Joel Levine, tenor recorder
+ Edward Simon, piano
+ Lenny White, drums
+ Marion Simon, percussion
+Notes: On-line information and samples available at
+ <A HREF="http://wwmusic.com/~music/audioq/rel/1033.html">http://wwmusic.com/~music/audioq/rel/1033.html</A>
+ADO Rating: 2 stars
+<A HREF="http://205.186.189.2/cgi-win/AMG.exe?sql=1A_IDR|||224430">AMG Rating: unrated</A>
+Penguin Rating: 3 stars
+==========================================================================
+Also of note:
+--------------------------------------------------------------------------
+Artist: Holly Cole Trio
+CD: Blame It On My Youth
+Copyright Date: 1992
+Label: Manhattan
+ID: CDP 7 97349 2
+Total Time: 37:45
+Personnel: Holly Cole, voice
+ Aaron Davis, piano
+ David Piltch, string bass
+Notes: Lyrical reference to "Eastern Standard Time" in
+ Tom Waits' "Purple Avenue"
+ADO Rating: 2.5 stars
+<A HREF="http://205.186.189.2/cgi-win/AMG.exe?sql=1A_IDR|||157959">AMG Rating: 2 stars</A>
+Penguin Rating: unrated
+--------------------------------------------------------------------------
+Artist: Milt Hinton
+CD: Old Man Time
+Copyright Date: 1990
+Label: Chiaroscuro
+ID: CR(D) 310
+Total Time: 149:38 (two CDs)
+Personnel: Milt Hinton, bass
+ Doc Cheatham, Dizzy Gillespie, Clark Terry, trumpet
+ Al Grey, trombone
+ Eddie Barefield, Joe Camel (Flip Phillips), Buddy Tate,
+ clarinet and saxophone
+ John Bunch, Red Richards, Norman Simmons, Derek Smith,
+ Ralph Sutton, piano
+ Danny Barker, Al Casey, guitar
+ Gus Johnson, Gerryck King, Bob Rosengarden, Jackie Williams,
+ drums
+ Lionel Hampton, vibraphone
+ Cab Calloway, Joe Williams, vocal
+ Buck Clayton, arrangements
+Notes: tunes include Old Man Time, Time After Time,
+ Sometimes I'm Happy,
+ A Hot Time in the Old Town Tonight,
+ Four or Five Times, Now's the Time,
+ Time on My Hands, This Time It's Us,
+ and Good Time Charlie
+ On-line samples available at
+ <A HREF="http://www.globalmusic.com/labels/chiaroscuro/chiaro_cd_gallery.html">http://www.globalmusic.com/labels/chiaroscuro/chiaro_cd_gallery.html</A>
+ADO Rating: 3 stars
+<A HREF="http://205.186.189.2/cgi-win/AMG.exe?sql=1A_IDR|||162344">AMG Rating: 4.5 stars</A>
+Penguin Rating: 3 stars
+--------------------------------------------------------------------------
+Artist: Paul Broadbent
+CD: Pacific Standard Time
+Copyright Date: 1995
+Label: Concord Jazz, Inc.
+ID: CCD-4664
+Total Time: 62:42
+Personnel: Paul Broadbent, piano
+ Putter Smith, Bass
+ Frank Gibson, Jr., drums
+Notes: The CD cover features an analemma for equation of time fans
+ADO Rating: 1 star
+<A HREF="http://205.186.189.2/cgi-win/AMG.exe?sql=1A_IDR|||223722">AMG Rating: 3 stars</A>
+Penguin Rating: 3.5 stars
+--------------------------------------------------------------------------
+Artist: Anthony Braxton/Richard Teitelbaum
+CD: Silence/Time Zones
+Copyright Date: 1996
+Label: Black Lion
+ID: BLCD 760221
+Total Time: 72:58
+Personnel: Anthony Braxton, sopranino and alto saxophones,
+ contrebasse clarinet, miscellaneous instruments
+ Leo Smith, trumpet and miscellaneous instruments
+ Leroy Jenkins, violin and miscellaneous instruments
+ Richard Teitelbaum, modular moog and micromoog synthesizer
+ADO Rating: black dot
+<A HREF="http://205.186.189.2/cg/AMG_.exe?sql=A310757">AMG Rating: unrated</A>
+--------------------------------------------------------------------------
+Artist: Jules Verne
+Book: Le Tour du Monde en Quatre-Vingts Jours
+ (Around the World in Eighty Days)
+Notes: Wall-clock time plays a central role in the plot.
+ European readers of the 1870s clearly held the U.S. press in
+ deep contempt; the protagonists cross the U.S. without once
+ reading a paper.
+ An on-line French-language version of the book
+ "with illustrations from the original 1873 French-language edition"
+ is available at
+ <A HREF="http://fourmilab.ch/etexts/www/tdm80j">http://fourmilab.ch/etexts/www/tdm80j</A>
+ An on-line English-language translation of the book is available at
+ <A HREF="http://www.literature.org/Works/Jules-Verne/eighty">http://www.literature.org/Works/Jules-Verne/eighty</A>
+--------------------------------------------------------------------------
+Film: Bell Science - About Time
+Notes: The Frank Baxter/Richard Deacon extravaganza
+ Information on ordering is available at
+ <A HREF="http://www.videoflicks.com/VF/38/038332.htm">http://www.videoflicks.com/VF/38/038332.htm</A>
+--------------------------------------------------------------------------
+The syndicated comic strip "Dilbert" featured an all-too-rare example of
+time zone humor on 1998-03-14.
+</PRE>
+</BODY>
+</HTML>
diff --git a/usr.sbin/zic/Makefile b/usr.sbin/zic/Makefile
new file mode 100644
index 0000000..2496e82
--- /dev/null
+++ b/usr.sbin/zic/Makefile
@@ -0,0 +1,8 @@
+# $FreeBSD$
+
+# Vendor contact: tz@elsie.nci.nih.gov
+MAINTAINER= wollman@FreeBSD.org
+
+SUBDIR= zic zdump
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/zic/Makefile.inc b/usr.sbin/zic/Makefile.inc
new file mode 100644
index 0000000..b915adf
--- /dev/null
+++ b/usr.sbin/zic/Makefile.inc
@@ -0,0 +1,3 @@
+# $FreeBSD$
+
+.include "${.CURDIR}/../../Makefile.inc"
diff --git a/usr.sbin/zic/README b/usr.sbin/zic/README
new file mode 100644
index 0000000..985a511
--- /dev/null
+++ b/usr.sbin/zic/README
@@ -0,0 +1,80 @@
+@(#)README 7.11
+
+"What time is it?" -- Richard Deacon as The King
+"Any time you want it to be." -- Frank Baxter as The Scientist
+ (from the Bell System film "About Time")
+
+The 1989 update of the time zone package featured
+
+* POSIXization (including interpretation of POSIX-style TZ environment
+ variables, provided by Guy Harris),
+* ANSIfication (including versions of "mktime" and "difftime"),
+* SVIDulation (an "altzone" variable)
+* MACHination (the "gtime" function)
+* corrections to some time zone data (including corrections to the rules
+ for Great Britain and New Zealand)
+* reference data from the United States Naval Observatory for folks who
+ want to do additional time zones
+* and the 1989 data for Saudi Arabia.
+
+(Since this code will be treated as "part of the implementation" in some places
+and as "part of the application" in others, there's no good way to name
+functions, such as timegm, that are not part of the proposed ANSI C standard;
+such functions have kept their old, underscore-free names in this update.)
+
+And the "dysize" function has disappeared; it was present to allow compilation
+of the "date" command on old BSD systems, and a version of "date" is now
+provided in the package. The "date" command is not created when you "make all"
+since it may lack options provided by the version distributed with your
+operating system, or may not interact with the system in the same way the
+native version does.
+
+Since POSIX frowns on correct leap second handling, the default behavior of
+the "zic" command (in the absence of a "-L" option) has been changed to omit
+leap second information from its output files.
+
+Here is a recipe for acquiring, building, installing, and testing the
+tz distribution on a GNU/Linux or similar host.
+
+ mkdir tz
+ cd tz
+ wget 'ftp://elsie.nci.nih.gov/pub/tz*.tar.gz'
+ gzip -dc tzcode*.tar.gz | tar -xf -
+ gzip -dc tzdata*.tar.gz | tar -xf -
+
+Be sure to read the comments in "Makefile" and make any changes needed
+to make things right for your system, especially if you are using some
+platform other than GNU/Linux. Then run the following commands,
+substituting your desired installation directory for "$HOME/tzdir":
+
+ make TOPDIR=$HOME/tzdir install
+ $HOME/tzdir/etc/zdump -v America/Los_Angeles
+
+To use the new functions, use a "-ltz" option when compiling or linking.
+
+Historical local time information has been included here not because it
+is particularly useful, but rather to:
+
+* give an idea of the variety of local time rules that have
+ existed in the past and thus an idea of the variety that may be
+ expected in the future;
+
+* provide a test of the generality of the local time rule description
+ system.
+
+The information in the time zone data files is by no means authoritative;
+if you know that the rules are different from those in a file, by all means
+feel free to change file (and please send the changed version to
+tz@elsie.nci.nih.gov for use in the future). Europeans take note!
+
+Thanks to these Timezone Caballeros who've made major contributions to the
+time conversion package: Keith Bostic; Bob Devine; Paul Eggert; Robert Elz;
+Guy Harris; Mark Horton; John Mackin; and Bradley White. Thanks also to
+Michael Bloom, Art Neilson, Stephen Prince, John Sovereign, and Frank Wales
+for testing work, and to Gwillim Law for checking local mean time data.
+None of them are responsible for remaining errors.
+
+Look in the ~ftp/pub directory of elsie.nci.nih.gov
+for updated versions of these files.
+
+Please send comments or information to tz@elsie.nci.nih.gov.
diff --git a/usr.sbin/zic/Theory b/usr.sbin/zic/Theory
new file mode 100644
index 0000000..cbf53b9
--- /dev/null
+++ b/usr.sbin/zic/Theory
@@ -0,0 +1,552 @@
+@(#)Theory 7.15
+
+
+----- Outline -----
+
+ Time and date functions
+ Names of time zone regions
+ Time zone abbreviations
+ Calendrical issues
+ Time and time zones on Mars
+
+
+----- Time and date functions -----
+
+These time and date functions are upwards compatible with POSIX.1,
+an international standard for UNIX-like systems.
+As of this writing, the current edition of POSIX.1 is:
+
+ Information technology --Portable Operating System Interface (POSIX (R))
+ -- Part 1: System Application Program Interface (API) [C Language]
+ ISO/IEC 9945-1:1996
+ ANSI/IEEE Std 1003.1, 1996 Edition
+ 1996-07-12
+
+POSIX.1 has the following properties and limitations.
+
+* In POSIX.1, time display in a process is controlled by the
+ environment variable TZ. Unfortunately, the POSIX.1 TZ string takes
+ a form that is hard to describe and is error-prone in practice.
+ Also, POSIX.1 TZ strings can't deal with other (for example, Israeli)
+ daylight saving time rules, or situations where more than two
+ time zone abbreviations are used in an area.
+
+ The POSIX.1 TZ string takes the following form:
+
+ stdoffset[dst[offset],date[/time],date[/time]]
+
+ where:
+
+ std and dst
+ are 3 or more characters specifying the standard
+ and daylight saving time (DST) zone names.
+ offset
+ is of the form `[-]hh:[mm[:ss]]' and specifies the
+ offset west of UTC. The default DST offset is one hour
+ ahead of standard time.
+ date[/time],date[/time]
+ specifies the beginning and end of DST. If this is absent,
+ the system supplies its own rules for DST, and these can
+ differ from year to year; typically US DST rules are used.
+ time
+ takes the form `hh:[mm[:ss]]' and defaults to 02:00.
+ date
+ takes one of the following forms:
+ Jn (1<=n<=365)
+ origin-1 day number not counting February 29
+ n (0<=n<=365)
+ origin-0 day number counting February 29 if present
+ Mm.n.d (0[Sunday]<=d<=6[Saturday], 1<=n<=5, 1<=m<=12)
+ for the dth day of week n of month m of the year,
+ where week 1 is the first week in which day d appears,
+ and `5' stands for the last week in which day d appears
+ (which may be either the 4th or 5th week).
+
+* In POSIX.1, when a TZ value like "EST5EDT" is parsed,
+ typically the current US DST rules are used,
+ but this means that the US DST rules are compiled into each program
+ that does time conversion. This means that when US time conversion
+ rules change (as in the United States in 1987), all programs that
+ do time conversion must be recompiled to ensure proper results.
+
+* In POSIX.1, there's no tamper-proof way for a process to learn the
+ system's best idea of local wall clock. (This is important for
+ applications that an administrator wants used only at certain times--
+ without regard to whether the user has fiddled the "TZ" environment
+ variable. While an administrator can "do everything in UTC" to get
+ around the problem, doing so is inconvenient and precludes handling
+ daylight saving time shifts--as might be required to limit phone
+ calls to off-peak hours.)
+
+* POSIX.1 requires that systems ignore leap seconds.
+
+These are the extensions that have been made to the POSIX.1 functions:
+
+* The "TZ" environment variable is used in generating the name of a file
+ from which time zone information is read (or is interpreted a la
+ POSIX); "TZ" is no longer constrained to be a three-letter time zone
+ name followed by a number of hours and an optional three-letter
+ daylight time zone name. The daylight saving time rules to be used
+ for a particular time zone are encoded in the time zone file;
+ the format of the file allows U.S., Australian, and other rules to be
+ encoded, and allows for situations where more than two time zone
+ abbreviations are used.
+
+ It was recognized that allowing the "TZ" environment variable to
+ take on values such as "America/New_York" might cause "old" programs
+ (that expect "TZ" to have a certain form) to operate incorrectly;
+ consideration was given to using some other environment variable
+ (for example, "TIMEZONE") to hold the string used to generate the
+ time zone information file name. In the end, however, it was decided
+ to continue using "TZ": it is widely used for time zone purposes;
+ separately maintaining both "TZ" and "TIMEZONE" seemed a nuisance;
+ and systems where "new" forms of "TZ" might cause problems can simply
+ use TZ values such as "EST5EDT" which can be used both by
+ "new" programs (a la POSIX) and "old" programs (as zone names and
+ offsets).
+
+* To handle places where more than two time zone abbreviations are used,
+ the functions "localtime" and "gmtime" set tzname[tmp->tm_isdst]
+ (where "tmp" is the value the function returns) to the time zone
+ abbreviation to be used. This differs from POSIX.1, where the elements
+ of tzname are only changed as a result of calls to tzset.
+
+* Since the "TZ" environment variable can now be used to control time
+ conversion, the "daylight" and "timezone" variables are no longer
+ needed. (These variables are defined and set by "tzset"; however, their
+ values will not be used by "localtime.")
+
+* The "localtime" function has been set up to deliver correct results
+ for near-minimum or near-maximum time_t values. (A comment in the
+ source code tells how to get compatibly wrong results).
+
+* A function "tzsetwall" has been added to arrange for the system's
+ best approximation to local wall clock time to be delivered by
+ subsequent calls to "localtime." Source code for portable
+ applications that "must" run on local wall clock time should call
+ "tzsetwall();" if such code is moved to "old" systems that don't
+ provide tzsetwall, you won't be able to generate an executable program.
+ (These time zone functions also arrange for local wall clock time to be
+ used if tzset is called--directly or indirectly--and there's no "TZ"
+ environment variable; portable applications should not, however, rely
+ on this behavior since it's not the way SVR2 systems behave.)
+
+* These functions can account for leap seconds, thanks to Bradley White
+ (bww@k.cs.cmu.edu).
+
+Points of interest to folks with other systems:
+
+* This package is already part of many POSIX-compliant hosts,
+ including BSD, HP, Linux, Network Appliance, SCO, SGI, and Sun.
+ On such hosts, the primary use of this package
+ is to update obsolete time zone rule tables.
+ To do this, you may need to compile the time zone compiler
+ `zic' supplied with this package instead of using the system `zic',
+ since the format of zic's input changed slightly in late 1994,
+ and many vendors still do not support the new input format.
+
+* The UNIX Version 7 "timezone" function is not present in this package;
+ it's impossible to reliably map timezone's arguments (a "minutes west
+ of GMT" value and a "daylight saving time in effect" flag) to a
+ time zone abbreviation, and we refuse to guess.
+ Programs that in the past used the timezone function may now examine
+ tzname[localtime(&clock)->tm_isdst] to learn the correct time
+ zone abbreviation to use. Alternatively, use
+ localtime(&clock)->tm_zone if this has been enabled.
+
+* The 4.2BSD gettimeofday function is not used in this package.
+ This formerly let users obtain the current UTC offset and DST flag,
+ but this functionality was removed in later versions of BSD.
+
+* In SVR2, time conversion fails for near-minimum or near-maximum
+ time_t values when doing conversions for places that don't use UTC.
+ This package takes care to do these conversions correctly.
+
+The functions that are conditionally compiled if STD_INSPIRED is defined
+should, at this point, be looked on primarily as food for thought. They are
+not in any sense "standard compatible"--some are not, in fact, specified in
+*any* standard. They do, however, represent responses of various authors to
+standardization proposals.
+
+Other time conversion proposals, in particular the one developed by folks at
+Hewlett Packard, offer a wider selection of functions that provide capabilities
+beyond those provided here. The absence of such functions from this package
+is not meant to discourage the development, standardization, or use of such
+functions. Rather, their absence reflects the decision to make this package
+contain valid extensions to POSIX.1, to ensure its broad
+acceptability. If more powerful time conversion functions can be standardized,
+so much the better.
+
+
+----- Names of time zone rule files -----
+
+The time zone rule file naming conventions attempt to strike a balance
+among the following goals:
+
+ * Uniquely identify every national region where clocks have all
+ agreed since 1970. This is essential for the intended use: static
+ clocks keeping local civil time.
+
+ * Indicate to humans as to where that region is. This simplifes use.
+
+ * Be robust in the presence of political changes. This reduces the
+ number of updates and backward-compatibility hacks. For example,
+ names of countries are ordinarily not used, to avoid
+ incompatibilities when countries change their name
+ (e.g. Zaire->Congo) or when locations change countries
+ (e.g. Hong Kong from UK colony to China).
+
+ * Be portable to a wide variety of implementations.
+ This promotes use of the technology.
+
+ * Use a consistent naming convention over the entire world.
+ This simplifies both use and maintenance.
+
+This naming convention is not intended for use by inexperienced users
+to select TZ values by themselves (though they can of course examine
+and reuse existing settings). Distributors should provide
+documentation and/or a simple selection interface that explains the
+names; see the 'tzselect' program supplied with this distribution for
+one example.
+
+Names normally have the form AREA/LOCATION, where AREA is the name
+of a continent or ocean, and LOCATION is the name of a specific
+location within that region. North and South America share the same
+area, `America'. Typical names are `Africa/Cairo', `America/New_York',
+and `Pacific/Honolulu'.
+
+Here are the general rules used for choosing location names,
+in decreasing order of importance:
+
+ Use only valid POSIX file name components (i.e., the parts of
+ names other than `/'). Within a file name component,
+ use only ASCII letters, `.', `-' and `_'. Do not use
+ digits, as that might create an ambiguity with POSIX
+ TZ strings. A file name component must not exceed 14
+ characters or start with `-'. E.g., prefer `Brunei'
+ to `Bandar_Seri_Begawan'.
+ Include at least one location per time zone rule set per country.
+ One such location is enough. Use ISO 3166 (see the file
+ iso3166.tab) to help decide whether something is a country.
+ If all the clocks in a country's region have agreed since 1970,
+ don't bother to include more than one location
+ even if subregions' clocks disagreed before 1970.
+ Otherwise these tables would become annoyingly large.
+ If a name is ambiguous, use a less ambiguous alternative;
+ e.g. many cities are named San Jose and Georgetown, so
+ prefer `Costa_Rica' to `San_Jose' and `Guyana' to `Georgetown'.
+ Keep locations compact. Use cities or small islands, not countries
+ or regions, so that any future time zone changes do not split
+ locations into different time zones. E.g. prefer `Paris'
+ to `France', since France has had multiple time zones.
+ Use mainstream English spelling, e.g. prefer `Rome' to `Roma', and
+ prefer `Athens' to the true name (which uses Greek letters).
+ The POSIX file name restrictions encourage this rule.
+ Use the most populous among locations in a country's time zone,
+ e.g. prefer `Shanghai' to `Beijing'. Among locations with
+ similar populations, pick the best-known location,
+ e.g. prefer `Rome' to `Milan'.
+ Use the singular form, e.g. prefer `Canary' to `Canaries'.
+ Omit common suffixes like `_Islands' and `_City', unless that
+ would lead to ambiguity. E.g. prefer `Cayman' to
+ `Cayman_Islands' and `Guatemala' to `Guatemala_City',
+ but prefer `Mexico_City' to `Mexico' because the country
+ of Mexico has several time zones.
+ Use `_' to represent a space.
+ Omit `.' from abbreviations in names, e.g. prefer `St_Helena'
+ to `St._Helena'.
+ Do not change established names if they only marginally
+ violate the above rules. For example, don't change
+ the existing name `Rome' to `Milan' merely because
+ Milan's population has grown to be somewhat greater
+ than Rome's.
+ If a name is changed, put its old spelling in the `backward' file.
+
+The file `zone.tab' lists the geographical locations used to name
+time zone rule files.
+
+Older versions of this package used a different naming scheme,
+and these older names are still supported.
+See the file `backward' for most of these older names
+(e.g. `US/Eastern' instead of `America/New_York').
+The other old-fashioned names still supported are
+`WET', `CET', `MET', `EET' (see the file `europe'),
+and `Factory' (see the file `factory').
+
+
+----- Time zone abbreviations -----
+
+When this package is installed, it generates time zone abbreviations
+like `EST' to be compatible with human tradition and POSIX.1.
+Here are the general rules used for choosing time zone abbreviations,
+in decreasing order of importance:
+
+ Use abbreviations that consist of three or more ASCII letters.
+ Previous editions of this database also used characters like
+ ' ' and '?', but these characters have a special meaning to
+ the shell and cause commands like
+ set `date`
+ to have unexpected effects.
+ Previous editions of this rule required upper-case letters,
+ but the Congressman who introduced Chamorro Standard Time
+ preferred "ChST", so the rule has been relaxed.
+
+ This rule guarantees that all abbreviations could have
+ been specified by a POSIX.1 TZ string. POSIX.1
+ requires at least three characters for an
+ abbreviation. POSIX.1-1996 says that an abbreviation
+ cannot start with ':', and cannot contain ',', '-',
+ '+', NUL, or a digit. Draft 7 of POSIX 1003.1-200x
+ changes this rule to say that an abbreviation can
+ contain only '-', '+', and alphanumeric characters in
+ the current locale. To be portable to both sets of
+ rules, an abbreviation must therefore use only ASCII
+ letters, as these are the only letters that are
+ alphabetic in all locales.
+
+ Use abbreviations that are in common use among English-speakers,
+ e.g. `EST' for Eastern Standard Time in North America.
+ We assume that applications translate them to other languages
+ as part of the normal localization process; for example,
+ a French application might translate `EST' to `HNE'.
+
+ For zones whose times are taken from a city's longitude, use the
+ traditional xMT notation, e.g. `PMT' for Paris Mean Time.
+ The only name like this in current use is `GMT'.
+
+ If there is no common English abbreviation, abbreviate the English
+ translation of the usual phrase used by native speakers.
+ If this is not available or is a phrase mentioning the country
+ (e.g. ``Cape Verde Time''), then:
+
+ When a country has a single or principal time zone region,
+ append `T' to the country's ISO code, e.g. `CVT' for
+ Cape Verde Time. For summer time append `ST';
+ for double summer time append `DST'; etc.
+ When a country has multiple time zones, take the first three
+ letters of an English place name identifying each zone
+ and then append `T', `ST', etc. as before;
+ e.g. `VLAST' for VLAdivostok Summer Time.
+
+ Use "zzz" for locations while uninhabited. The mnemonic is that
+ these locations are, in some sense, asleep.
+
+Application writers should note that these abbreviations are ambiguous
+in practice: e.g. `EST' has a different meaning in Australia than
+it does in the United States. In new applications, it's often better
+to use numeric UTC offsets like `-0500' instead of time zone
+abbreviations like `EST'; this avoids the ambiguity.
+
+
+----- Calendrical issues -----
+
+Calendrical issues are a bit out of scope for a time zone database,
+but they indicate the sort of problems that we would run into if we
+extended the time zone database further into the past. An excellent
+resource in this area is Nachum Dershowitz and Edward M. Reingold,
+<a href="http://emr.cs.uiuc.edu/home/reingold/calendar-book/index.shtml">
+Calendrical Calculations
+</a>, Cambridge University Press (1997). Other information and
+sources are given below. They sometimes disagree.
+
+
+France
+
+Gregorian calendar adopted 1582-12-20.
+French Revolutionary calendar used 1793-11-24 through 1805-12-31,
+and (in Paris only) 1871-05-06 through 1871-05-23.
+
+
+Russia
+
+From Chris Carrier <72157.3334@CompuServe.COM> (1996-12-02):
+On 1929-10-01 the Soviet Union instituted an ``Eternal Calendar''
+with 30-day months plus 5 holidays, with a 5-day week.
+On 1931-12-01 it changed to a 6-day week; in 1934 it reverted to the
+Gregorian calendar while retaining the 6-day week; on 1940-06-27 it
+reverted to the 7-day week. With the 6-day week the usual days
+off were the 6th, 12th, 18th, 24th and 30th of the month.
+(Source: Evitiar Zerubavel, _The Seven Day Circle_)
+
+
+Mark Brader reported a similar story in "The Book of Calendars", edited
+by Frank Parise (1982, Facts on File, ISBN 0-8719-6467-8), page 377. But:
+
+From: Petteri Sulonen (via Usenet)
+Date: 14 Jan 1999 00:00:00 GMT
+Message-ID: <Petteri.Sulonen-1401991626030001@lapin-kulta.in.helsinki.fi>
+
+If your source is correct, how come documents between 1929 -- 1940 were
+still dated using the conventional, Gregorian calendar?
+
+I can post a scan of a document dated December 1, 1934, signed by
+Yenukidze, the secretary, on behalf of Kalinin, the President of the
+Executive Committee of the Supreme Soviet, if you like.
+
+
+
+Sweden (and Finland)
+
+From: msb@sq.com (Mark Brader)
+<a href="news:1996Jul6.012937.29190@sq.com">
+Subject: Re: Gregorian reform -- a part of locale?
+</a>
+Date: 1996-07-06
+
+In 1700, Denmark made the transition from Julian to Gregorian. Sweden
+decided to *start* a transition in 1700 as well, but rather than have one of
+those unsightly calendar gaps :-), they simply decreed that the next leap
+year after 1696 would be in 1744 -- putting the whole country on a calendar
+different from both Julian and Gregorian for a period of 40 years.
+
+However, in 1704 something went wrong and the plan was not carried through;
+they did, after all, have a leap year that year. And one in 1708. In 1712
+they gave it up and went back to Julian, putting 30 days in February that
+year!...
+
+Then in 1753, Sweden made the transition to Gregorian in the usual manner,
+getting there only 13 years behind the original schedule.
+
+(A previous posting of this story was challenged, and Swedish readers
+produced the following references to support it: "Tiderakning och historia"
+by Natanael Beckman (1924) and "Tid, en bok om tiderakning och
+kalendervasen" by Lars-Olof Lode'n (no date was given).)
+
+
+Grotefend's data
+
+From: "Michael Palmer" <mpalmer@netcom.com> [with one obvious typo fixed]
+Subject: Re: Gregorian Calendar (was Re: Another FHC related question
+Newsgroups: soc.genealogy.german
+Date: Tue, 9 Feb 1999 02:32:48 -800
+Message-ID: <199902091032.CAA09644@netcom10.netcom.com>
+
+The following is a(n incomplete) listing, arranged chronologically, of
+European states, with the date they converted from the Julian to the
+Gregorian calendar:
+
+04/15 Oct 1582 - Italy (with exceptions), Spain, Portugal, Poland (Roman
+ Catholics and Danzig only)
+09/20 Dec 1582 - France, Lorraine
+
+21 Dec 1582/
+ 01 Jan 1583 - Holland, Brabant, Flanders, Hennegau
+10/21 Feb 1583 - bishopric of Liege (L"uttich)
+13/24 Feb 1583 - bishopric of Augsburg
+04/15 Oct 1583 - electorate of Trier
+05/16 Oct 1583 - Bavaria, bishoprics of Freising, Eichstedt, Regensburg,
+ Salzburg, Brixen
+13/24 Oct 1583 - Austrian Oberelsass and Breisgau
+20/31 Oct 1583 - bishopric of Basel
+02/13 Nov 1583 - duchy of J"ulich-Berg
+02/13 Nov 1583 - electorate and city of K"oln
+04/15 Nov 1583 - bishopric of W"urzburg
+11/22 Nov 1583 - electorate of Mainz
+16/27 Nov 1583 - bishopric of Strassburg and the margraviate of Baden
+17/28 Nov 1583 - bishopric of M"unster and duchy of Cleve
+14/25 Dec 1583 - Steiermark
+
+06/17 Jan 1584 - Austria and Bohemia
+11/22 Jan 1584 - Luzern, Uri, Schwyz, Zug, Freiburg, Solothurn
+12/23 Jan 1584 - Silesia and the Lausitz
+22 Jan/
+ 02 Feb 1584 - Hungary (legally on 21 Oct 1587)
+ Jun 1584 - Unterwalden
+01/12 Jul 1584 - duchy of Westfalen
+
+16/27 Jun 1585 - bishopric of Paderborn
+
+14/25 Dec 1590 - Transylvania
+
+22 Aug/
+ 02 Sep 1612 - duchy of Prussia
+
+13/24 Dec 1614 - Pfalz-Neuburg
+
+ 1617 - duchy of Kurland (reverted to the Julian calendar in
+ 1796)
+
+ 1624 - bishopric of Osnabr"uck
+
+ 1630 - bishopric of Minden
+
+15/26 Mar 1631 - bishopric of Hildesheim
+
+ 1655 - Kanton Wallis
+
+05/16 Feb 1682 - city of Strassburg
+
+18 Feb/
+ 01 Mar 1700 - Protestant Germany (including Swedish possessions in
+ Germany), Denmark, Norway
+30 Jun/
+ 12 Jul 1700 - Gelderland, Zutphen
+10 Nov/
+ 12 Dec 1700 - Utrecht, Overijssel
+
+31 Dec 1700/
+ 12 Jan 1701 - Friesland, Groningen, Z"urich, Bern, Basel, Geneva,
+ Turgau, and Schaffhausen
+
+ 1724 - Glarus, Appenzell, and the city of St. Gallen
+
+01 Jan 1750 - Pisa and Florence
+
+02/14 Sep 1752 - Great Britain
+
+17 Feb/
+ 01 Mar 1753 - Sweden
+
+1760-1812 - Graub"unden
+
+The Russian empire (including Finland and the Baltic states) did not
+convert to the Gregorian calendar until the Soviet revolution of 1917.
+
+Source: H. Grotefend, _Taschenbuch der Zeitrechnung des deutschen
+Mittelalters und der Neuzeit_, herausgegeben von Dr. O. Grotefend
+(Hannover: Hahnsche Buchhandlung, 1941), pp. 26-28.
+
+
+----- Time and time zones on Mars -----
+
+Some people have adjusted their work schedules to fit Mars time.
+Dozens of special Mars watches were built for Jet Propulsion
+Laboratory workers who kept Mars time during the Mars Exploration
+Rovers mission (2004). These timepieces look like normal Seikos and
+Citizens but use Mars seconds rather than terrestrial seconds.
+
+A Mars solar day is called a "sol" and has a mean period equal to
+about 24 hours 39 minutes 35.244 seconds in terrestrial time. It is
+divided into a conventional 24-hour clock, so each Mars second equals
+about 1.02749125 terrestrial seconds.
+
+The prime meridian of Mars goes through the center of the crater
+Airy-0, named in honor of the British astronomer who built the
+Greenwich telescope that defines Earth's prime meridian. Mean solar
+time on the Mars prime meridian is called Mars Coordinated Time (MTC).
+
+Each landed mission on Mars has adopted a different reference for
+solar time keeping, so there is no real standard for Mars time zones.
+For example, the Mars Exploration Rover project (2004) defined two
+time zones "Local Solar Time A" and "Local Solar Time B" for its two
+missions, each zone designed so that its time equals local true solar
+time at approximately the middle of the nominal mission. Such a "time
+zone" is not particularly suited for any application other than the
+mission itself.
+
+Many calendars have been proposed for Mars, but none have achieved
+wide acceptance. Astronomers often use Mars Sol Date (MSD) which is a
+sequential count of Mars solar days elapsed since about 1873-12-29
+12:00 GMT.
+
+The tz database does not currently support Mars time, but it is
+documented here in the hopes that support will be added eventually.
+
+Sources:
+
+Michael Allison and Robert Schmunk,
+"Technical Notes on Mars Solar Time as Adopted by the Mars24 Sunclock"
+<http://www.giss.nasa.gov/tools/mars24/help/notes.html> (2004-03-15).
+
+Jia-Rui Chong, "Workdays Fit for a Martian", Los Angeles Times
+(2004-01-14), pp A1, A20-A21.
diff --git a/usr.sbin/zic/ialloc.c b/usr.sbin/zic/ialloc.c
new file mode 100644
index 0000000..a069032
--- /dev/null
+++ b/usr.sbin/zic/ialloc.c
@@ -0,0 +1,86 @@
+#ifndef lint
+#ifndef NOID
+static const char elsieid[] = "@(#)ialloc.c 8.29";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*LINTLIBRARY*/
+
+#include "private.h"
+
+#define nonzero(n) (((n) == 0) ? 1 : (n))
+
+char *
+imalloc(n)
+const int n;
+{
+ return malloc((size_t) nonzero(n));
+}
+
+char *
+icalloc(nelem, elsize)
+int nelem;
+int elsize;
+{
+ if (nelem == 0 || elsize == 0)
+ nelem = elsize = 1;
+ return calloc((size_t) nelem, (size_t) elsize);
+}
+
+void *
+irealloc(pointer, size)
+void * const pointer;
+const int size;
+{
+ if (pointer == NULL)
+ return imalloc(size);
+ return realloc((void *) pointer, (size_t) nonzero(size));
+}
+
+char *
+icatalloc(old, new)
+char * const old;
+const char * const new;
+{
+ register char * result;
+ register int oldsize, newsize;
+
+ newsize = (new == NULL) ? 0 : strlen(new);
+ if (old == NULL)
+ oldsize = 0;
+ else if (newsize == 0)
+ return old;
+ else oldsize = strlen(old);
+ if ((result = irealloc(old, oldsize + newsize + 1)) != NULL)
+ if (new != NULL)
+ (void) strcpy(result + oldsize, new);
+ return result;
+}
+
+char *
+icpyalloc(string)
+const char * const string;
+{
+ return icatalloc((char *) NULL, string);
+}
+
+void
+ifree(p)
+char * const p;
+{
+ if (p != NULL)
+ (void) free(p);
+}
+
+void
+icfree(p)
+char * const p;
+{
+ if (p != NULL)
+ (void) free(p);
+}
diff --git a/usr.sbin/zic/private.h b/usr.sbin/zic/private.h
new file mode 100644
index 0000000..315f33a
--- /dev/null
+++ b/usr.sbin/zic/private.h
@@ -0,0 +1,193 @@
+#ifndef PRIVATE_H
+
+#define PRIVATE_H
+
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
+*/
+
+/*
+ * FreeBSD modifications: separate libc's privates from zic's.
+ * This makes it easier when we need to update one but not the other.
+ * I have removed all of the ifdef spaghetti which is not relevant to
+ * zic from this file.
+ *
+ * $FreeBSD$
+ */
+
+/*
+** This header is for use ONLY with the time conversion code.
+** There is no guarantee that it will remain unchanged,
+** or that it will remain at all.
+** Do NOT copy it to any system include directory.
+** Thank you!
+*/
+
+/*
+** ID
+*/
+
+#ifndef lint
+#ifndef NOID
+static const char privatehid[] = "@(#)private.h 7.53";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+/*
+** Defaults for preprocessor symbols.
+** You can override these in your C compiler options, e.g. `-DHAVE_ADJTIME=0'.
+*/
+
+#ifndef HAVE_GETTEXT
+#define HAVE_GETTEXT 0
+#endif /* !defined HAVE_GETTEXT */
+
+#ifndef HAVE_STRERROR
+#define HAVE_STRERROR 1
+#endif /* !defined HAVE_STRERROR */
+
+#ifndef HAVE_SYMLINK
+#define HAVE_SYMLINK 1
+#endif /* !defined HAVE_SYMLINK */
+
+#ifndef HAVE_SYS_STAT_H
+#define HAVE_SYS_STAT_H 1
+#endif /* !defined HAVE_SYS_STAT_H */
+
+#ifndef HAVE_SYS_WAIT_H
+#define HAVE_SYS_WAIT_H 1
+#endif /* !defined HAVE_SYS_WAIT_H */
+
+#ifndef HAVE_UNISTD_H
+#define HAVE_UNISTD_H 1
+#endif /* !defined HAVE_UNISTD_H */
+
+/*
+** Nested includes
+*/
+
+#include "sys/types.h" /* for time_t */
+#include "stdio.h"
+#include "errno.h"
+#include "string.h"
+#include "limits.h" /* for CHAR_BIT */
+#include "time.h"
+#include "stdlib.h"
+
+#if HAVE_GETTEXT - 0
+#include "libintl.h"
+#endif /* HAVE_GETTEXT - 0 */
+
+#if HAVE_SYS_WAIT_H - 0
+#include <sys/wait.h> /* for WIFEXITED and WEXITSTATUS */
+#endif /* HAVE_SYS_WAIT_H - 0 */
+
+#if HAVE_UNISTD_H - 0
+#include "unistd.h" /* for F_OK and R_OK */
+#endif /* HAVE_UNISTD_H - 0 */
+
+#if !(HAVE_UNISTD_H - 0)
+#ifndef F_OK
+#define F_OK 0
+#endif /* !defined F_OK */
+#ifndef R_OK
+#define R_OK 4
+#endif /* !defined R_OK */
+#endif /* !(HAVE_UNISTD_H - 0) */
+
+/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
+#define is_digit(c) ((unsigned)(c) - '0' <= 9)
+
+#define P(x) x
+
+/*
+** Private function declarations.
+*/
+char * icalloc P((int nelem, int elsize));
+char * icatalloc P((char * old, const char * new));
+char * icpyalloc P((const char * string));
+char * imalloc P((int n));
+void * irealloc P((void * pointer, int size));
+void icfree P((char * pointer));
+void ifree P((char * pointer));
+char * scheck P((const char *string, const char *format));
+
+/*
+** Finally, some convenience items.
+*/
+
+#ifndef TRUE
+#define TRUE 1
+#endif /* !defined TRUE */
+
+#ifndef FALSE
+#define FALSE 0
+#endif /* !defined FALSE */
+
+#ifndef TYPE_BIT
+#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
+#endif /* !defined TYPE_BIT */
+
+#ifndef TYPE_SIGNED
+#define TYPE_SIGNED(type) (((type) -1) < 0)
+#endif /* !defined TYPE_SIGNED */
+
+#ifndef INT_STRLEN_MAXIMUM
+/*
+** 302 / 1000 is log10(2.0) rounded up.
+** Subtract one for the sign bit if the type is signed;
+** add one for integer division truncation;
+** add one more for a minus sign if the type is signed.
+*/
+#define INT_STRLEN_MAXIMUM(type) \
+ ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + 1 + TYPE_SIGNED(type))
+#endif /* !defined INT_STRLEN_MAXIMUM */
+
+/*
+** INITIALIZE(x)
+*/
+
+#ifndef GNUC_or_lint
+#ifdef lint
+#define GNUC_or_lint
+#endif /* defined lint */
+#ifndef lint
+#ifdef __GNUC__
+#define GNUC_or_lint
+#endif /* defined __GNUC__ */
+#endif /* !defined lint */
+#endif /* !defined GNUC_or_lint */
+
+#ifndef INITIALIZE
+#ifdef GNUC_or_lint
+#define INITIALIZE(x) ((x) = 0)
+#endif /* defined GNUC_or_lint */
+#ifndef GNUC_or_lint
+#define INITIALIZE(x)
+#endif /* !defined GNUC_or_lint */
+#endif /* !defined INITIALIZE */
+
+/*
+** For the benefit of GNU folk...
+** `_(MSGID)' uses the current locale's message library string for MSGID.
+** The default is to use gettext if available, and use MSGID otherwise.
+*/
+
+#ifndef _
+#if HAVE_GETTEXT - 0
+#define _(msgid) gettext(msgid)
+#else /* !(HAVE_GETTEXT - 0) */
+#define _(msgid) msgid
+#endif /* !(HAVE_GETTEXT - 0) */
+#endif /* !defined _ */
+
+#ifndef TZ_DOMAIN
+#define TZ_DOMAIN "tz"
+#endif /* !defined TZ_DOMAIN */
+
+/*
+** UNIX was a registered trademark of The Open Group in 2003.
+*/
+
+#endif /* !defined PRIVATE_H */
diff --git a/usr.sbin/zic/scheck.c b/usr.sbin/zic/scheck.c
new file mode 100644
index 0000000..692bf1a
--- /dev/null
+++ b/usr.sbin/zic/scheck.c
@@ -0,0 +1,64 @@
+#ifndef lint
+#ifndef NOID
+static const char elsieid[] = "@(#)scheck.c 8.15";
+#endif /* !defined lint */
+#endif /* !defined NOID */
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*LINTLIBRARY*/
+
+#include "private.h"
+
+char *
+scheck(string, format)
+const char * const string;
+const char * const format;
+{
+ register char * fbuf;
+ register const char * fp;
+ register char * tp;
+ register int c;
+ register char * result;
+ char dummy;
+ static char nada;
+
+ result = &nada;
+ if (string == NULL || format == NULL)
+ return result;
+ fbuf = imalloc((int) (2 * strlen(format) + 4));
+ if (fbuf == NULL)
+ return result;
+ fp = format;
+ tp = fbuf;
+ while ((*tp++ = c = *fp++) != '\0') {
+ if (c != '%')
+ continue;
+ if (*fp == '%') {
+ *tp++ = *fp++;
+ continue;
+ }
+ *tp++ = '*';
+ if (*fp == '*')
+ ++fp;
+ while (is_digit(*fp))
+ *tp++ = *fp++;
+ if (*fp == 'l' || *fp == 'h')
+ *tp++ = *fp++;
+ else if (*fp == '[')
+ do *tp++ = *fp++;
+ while (*fp != '\0' && *fp != ']');
+ if ((*tp++ = *fp++) == '\0')
+ break;
+ }
+ *(tp - 1) = '%';
+ *tp++ = 'c';
+ *tp = '\0';
+ if (sscanf(string, fbuf, &dummy) != 1)
+ result = (char *) format;
+ ifree(fbuf);
+ return result;
+}
diff --git a/usr.sbin/zic/tz-art.htm b/usr.sbin/zic/tz-art.htm
new file mode 100644
index 0000000..56f78ac
--- /dev/null
+++ b/usr.sbin/zic/tz-art.htm
@@ -0,0 +1,278 @@
+<?xml version="1.0" encoding="US-ASCII"?>
+<!DOCTYPE html
+PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+"DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-type" content='text/html; charset="US-ASCII"' />
+<title>Time and the Arts</title>
+</head>
+<body>
+<h1>Time and the Arts</h1>
+<address>
+@(#)tz-art.htm 7.53
+</address>
+<p>
+Please send corrections to this web page to the
+<a href="mailto:tz@elsie.nci.nih.gov">time zone mailing list</a>.</p>
+<p>
+See also <a href="tz-link.htm">Sources for Time Zone and Daylight Saving Time Data</a>.</p>
+<hr />
+<p>
+Data on recordings of "Save That Time," Russ Long, Serrob Publishing, BMI:</p>
+<table>
+<tr><td>Artist</td><td>Karrin Allyson</td></tr>
+<tr><td>CD</td><td>I Didn't Know About You</td></tr>
+<tr><td>Copyright Date</td><td>1993</td></tr>
+<tr><td>Label</td><td>Concord Jazz, Inc.</td></tr>
+<tr><td>ID</td><td>CCD-4543</td></tr>
+<tr><td>Track Time</td><td>3:44</td></tr>
+<tr><td>Personnel</td><td>Karrin Allyson, vocal;
+Russ Long, piano;
+Gerald Spaits, bass;
+Todd Strait, drums</td></tr>
+<tr><td>Notes</td><td>CD notes "additional lyric by Karrin Allyson;
+arranged by Russ Long and Karrin Allyson"</td></tr>
+<tr><td>ADO Rating</td><td>1 star</td></tr>
+<tr><td><a href="http://www.allmusic.com/cg/amg.dll?p=amg&amp;sql=A1fdovw9ta92k">AMG Rating</a></td><td>4 stars</td></tr>
+<tr><td>Penguin Rating</td><td>3.5 stars</td></tr>
+<tr><td>&nbsp;</td></tr>
+<tr><td>Artist</td><td>Kevin Mahogany</td></tr>
+<tr><td>CD</td><td>Double Rainbow</td></tr>
+<tr><td>Copyright Date</td><td>1993</td></tr>
+<tr><td>Label</td><td>Enja Records</td></tr>
+<tr><td>ID</td><td>ENJ-7097 2</td></tr>
+<tr><td>Track Time</td><td>6:27</td></tr>
+<tr><td>Personnel</td><td>Kevin Mahogany, vocal;
+Kenny Barron, piano;
+Ray Drummond, bass;
+Ralph Moore, tenor saxophone;
+Lewis Nash, drums</td></tr>
+<tr><td>ADO Rating</td><td>1.5 stars</td></tr>
+<tr><td><a href="http://www.allmusic.com/cg/amg.dll?p=amg&amp;sql=Akikbikzjbb19">AMG Rating</a></td><td>3 stars</td></tr>
+<tr><td>Penguin Rating</td><td>3 stars</td></tr>
+<tr><td>&nbsp;</td></tr>
+<tr><td>Artist</td><td>Joe Williams</td></tr>
+<tr><td>CD</td><td>Here's to Life</td></tr>
+<tr><td>Copyright Date</td><td>1994</td></tr>
+<tr><td>Label</td><td>Telarc International Corporation</td></tr>
+<tr><td>ID</td><td>CD-83357</td></tr>
+<tr><td>Track Time</td><td>3:58</td></tr>
+<tr><td>Personnel</td><td>Joe Williams, vocal
+The Robert Farnon [39 piece] Orchestra</td></tr>
+<tr><td>Notes</td><td>This CD is also available as part of a 3-CD package from
+Telarc, "Triple Play" (CD-83461)</td></tr>
+<tr><td>ADO Rating</td><td>black dot</td></tr>
+<tr><td><a href="http://www.allmusic.com/cg/amg.dll?p=amg&amp;sql=Amyyvad6kt8w1">AMG Rating</a></td><td>2 stars</td></tr>
+<tr><td>Penguin Rating</td><td>3 stars</td></tr>
+<tr><td>&nbsp;</td></tr>
+<tr><td>Artist</td><td>Charles Fambrough</td></tr>
+<tr><td>CD</td><td>Keeper of the Spirit</td></tr>
+<tr><td>Copyright Date</td><td>1995</td></tr>
+<tr><td>Label</td><td>AudioQuest Music</td></tr>
+<tr><td>ID</td><td>AQ-CD1033</td></tr>
+<tr><td>Track Time</td><td>7:07</td></tr>
+<tr><td>Personnel</td><td>Charles Fambrough, bass;
+Joel Levine, tenor recorder;
+Edward Simon, piano;
+Lenny White, drums;
+Marion Simon, percussion</td></tr>
+<tr><td>Notes</td><td>On-line information and samples available at
+<a href="http://wwmusic.com/~music/audioq/rel/1033.html">http://wwmusic.com/~music/audioq/rel/1033.html</a></td></tr>
+<tr><td>ADO Rating</td><td>2 stars</td></tr>
+<tr><td><a href="http://www.allmusic.com/cg/amg.dll?p=amg&amp;sql=A5rkcikcjbb89">AMG Rating</a></td><td>unrated</td></tr>
+<tr><td>Penguin Rating</td><td>3 stars</td></tr>
+</table>
+<hr />
+<p>Also of note:</p>
+<table>
+<tr><td>Artist</td><td>Holly Cole Trio</td></tr>
+<tr><td>CD</td><td>Blame It On My Youth</td></tr>
+<tr><td>Copyright Date</td><td>1992</td></tr>
+<tr><td>Label</td><td>Manhattan</td></tr>
+<tr><td>ID</td><td>CDP 7 97349 2</td></tr>
+<tr><td>Total Time</td><td>37:45</td></tr>
+<tr><td>Personnel</td><td>Holly Cole, voice;
+Aaron Davis, piano;
+David Piltch, string bass</td></tr>
+<tr><td>Notes</td><td>Lyrical reference to "Eastern Standard Time" in
+Tom Waits' "Purple Avenue"</td></tr>
+<tr><td>ADO Rating</td><td>2.5 stars</td></tr>
+<tr><td><a href="http://www.allmusic.com/cg/amg.dll?p=amg&amp;sql=A3a9ds37ya3dg">AMG Rating</a></td><td>3 stars</td></tr>
+<tr><td>Penguin Rating</td><td>unrated</td></tr>
+<tr><td>&nbsp;</td></tr>
+<tr><td>Artist</td><td>Milt Hinton</td></tr>
+<tr><td>CD</td><td>Old Man Time</td></tr>
+<tr><td>Copyright Date</td><td>1990</td></tr>
+<tr><td>Label</td><td>Chiaroscuro</td></tr>
+<tr><td>ID</td><td>CR(D) 310</td></tr>
+<tr><td>Total Time</td><td>149:38 (two CDs)</td></tr>
+<tr><td>Personnel</td><td>Milt Hinton, bass;
+Doc Cheatham, Dizzy Gillespie, Clark Terry, trumpet;
+Al Grey, trombone;
+Eddie Barefield, Joe Camel (Flip Phillips), Buddy Tate,
+clarinet and saxophone;
+John Bunch, Red Richards, Norman Simmons, Derek Smith,
+Ralph Sutton, piano;
+Danny Barker, Al Casey, guitar;
+Gus Johnson, Gerryck King, Bob Rosengarden, Jackie Williams,
+drums;
+Lionel Hampton, vibraphone;
+Cab Calloway, Joe Williams, vocal;
+Buck Clayton, arrangements</td></tr>
+<tr><td>Notes</td><td>tunes include Old Man Time, Time After Time,
+Sometimes I'm Happy,
+A Hot Time in the Old Town Tonight,
+Four or Five Times, Now's the Time,
+Time on My Hands, This Time It's Us,
+and Good Time Charlie
+On-line samples available at
+<a href="http://www.chiaroscurojazz.com/albuminfo.php4?albumid=49">http://www.chiaroscurojazz.com/albuminfo.php3?albumid=49</a></td></tr>
+<tr><td>ADO Rating</td><td>3 stars</td></tr>
+<tr><td><a href="http://www.allmusic.com/cg/amg.dll?p=amg&amp;sql=A1cbyxdab8ola">AMG Rating</a></td><td>4.5 stars</td></tr>
+<tr><td>Penguin Rating</td><td>3 stars</td></tr>
+<tr><td>&nbsp;</td></tr>
+<tr><td>Artist</td><td>Alan Broadbent</td></tr>
+<tr><td>CD</td><td>Pacific Standard Time</td></tr>
+<tr><td>Copyright Date</td><td>1995</td></tr>
+<tr><td>Label</td><td>Concord Jazz, Inc.</td></tr>
+<tr><td>ID</td><td>CCD-4664</td></tr>
+<tr><td>Total Time</td><td>62:42</td></tr>
+<tr><td>Personnel</td><td>Alan Broadbent, piano;
+Putter Smith, Bass;
+Frank Gibson, Jr., drums</td></tr>
+<tr><td>Notes</td><td>The CD cover features an analemma for equation-of-time fans</td></tr>
+<tr><td>ADO Rating</td><td>1 star</td></tr>
+<tr><td><a href="http://www.allmusic.com/cg/amg.dll?p=amg&amp;sql=Asl8zefuk8gfo">AMG Rating</a></td><td>4 stars</td></tr>
+<tr><td>Penguin Rating</td><td>3.5 stars</td></tr>
+<tr><td>&nbsp;</td></tr>
+<tr><td>Artist</td><td>Anthony Braxton/Richard Teitelbaum</td></tr>
+<tr><td>CD</td><td>Silence/Time Zones</td></tr>
+<tr><td>Copyright Date</td><td>1996</td></tr>
+<tr><td>Label</td><td>Black Lion</td></tr>
+<tr><td>ID</td><td>BLCD 760221</td></tr>
+<tr><td>Total Time</td><td>72:58</td></tr>
+<tr><td>Personnel</td><td>Anthony Braxton, sopranino and alto saxophones,
+contrebasse clarinet, miscellaneous instruments;
+Leo Smith, trumpet and miscellaneous instruments;
+Leroy Jenkins, violin and miscellaneous instruments;
+Richard Teitelbaum, modular moog and micromoog synthesizer</td></tr>
+<tr><td>ADO Rating</td><td>black dot</td></tr>
+<tr><td><a href="http://www.allmusic.com/cg/amg.dll?p=amg&amp;sql=A5bkvu3xjan1k">AMG Rating</a></td><td>unrated</td></tr>
+<tr><td>&nbsp;</td></tr>
+<tr><td>Artist</td><td>Jules Verne</td></tr>
+<tr><td>Book</td><td>Le Tour du Monde en Quatre-Vingts Jours
+(Around the World in Eighty Days)</td></tr>
+<tr><td>Notes</td><td>Wall-clock time plays a central role in the plot.
+European readers of the 1870s clearly held the U.S. press in
+deep contempt; the protagonists cross the U.S. without once
+reading a paper.
+An on-line French-language version of the book
+"with illustrations from the original 1873 French-language edition"
+is available at
+<a href="http://fourmilab.ch/etexts/www/tdm80j">http://fourmilab.ch/etexts/www/tdm80j</a>
+An on-line English-language translation of the book is available at
+<a href="http://www.literature.org/Works/Jules-Verne/eighty">http://www.literature.org/Works/Jules-Verne/eighty</a></td></tr>
+<tr><td>&nbsp;</td></tr>
+<tr><td>Film</td><td>Bell Science - About Time</td></tr>
+<tr><td>Notes</td><td>The Frank Baxter/Richard Deacon extravaganza
+Information on ordering is available at
+<a href="http://www.videoflicks.com/VF2/1035/1035893.ihtml">http://www.videoflicks.com/VF2/1035/1035893.ihtml</a></td></tr>
+</table>
+<hr />
+<ul>
+<li>
+An episode of "The Adventures of Superman" entitled "The Mysterious
+Cube," first aired 1958-02-24, had Superman convincing the controllers
+of WWV to broadcast time signals five minutes ahead of actual time;
+doing so got a crook trying to beat the statute of limitations to
+emerge a bit too early from the titular enclosure.
+</li>
+<li>
+The 1960s ITC television series "The Prisoner" included an episode
+entitled "The Chimes of Big Ben" in which our protagonist tumbled to
+the fraudulent nature of a Poland-to-England escape upon hearing "Big
+Ben" chiming on Polish local time.
+</li>
+<li>
+The series "Seinfeld" included an episode entitled "The Susie," first
+broadcast 1997-02-13, in which Kramer decides that daylight saving time
+isn't coming fast enough, so he sets his watch ahead an hour.
+</li>
+<li>
+The syndicated comic strip "Dilbert" featured an all-too-rare example of
+time zone humor on 1998-03-14.
+</li>
+<li>
+Surrealist artist Guy Billout's work "Date Line" appeared on page 103
+of the 1999-11 Atlantic Monthly.
+</li>
+<li>
+"Gloom, Gloom, Go Away" by Walter Kirn appeared on page 106 of Time
+Magazine's 2002-11-11 issue; among other things, it proposed
+year-round DST as a way of lessening wintertime despair.
+</li>
+<li>
+The "20 Hours in America" episode of "The West Wing," first aired 2002-09-25,
+saw White House staffers stranded in Indiana; they thought they had time to
+catch Air Force One but were done in by intra-Indiana local time changes.
+</li>
+<li>
+"In what time zone would you find New York City?" was a $200 question on
+the 1999-11-13 United States airing of "Who Wants to Be a Millionaire?"
+"In 1883, what industry led the movement to divide the U.S. into four time
+zones?" was a $32,000 question on the 2001-05-23 United States airing of
+"Who Wants to Be a Millionaire?" At this rate, the million-dollar time-zone
+question should have been asked 2002-06-04.
+</li>
+</ul>
+<hr />
+<ul>
+<li>
+"We're been using the five-cent nickle in this country since 1492.
+Now that's pretty near 100 years, daylight savings [sic]."
+(Groucho Marx as Captain Spaulding in "Animal Crackers", 1930,
+as noted by Will Fitzerald, wfitzgerald@ameritech.net)
+</li>
+<li>
+"Good news."
+"What did they do? Extend Daylight Saving Time year round?"
+(Professional tanner George Hamilton, in dialog from a
+May, 1999 episode of the syndicated television series "Baywatch")
+</li>
+<li>
+"A fundamental belief held by Americans is that if you are on land, you
+cannot be killed by a fish...So most Americans remain on land, believing
+they're safe. Unfortunately, this belief&mdash;like so many myths, such as that
+there's a reason for 'Daylight Saving Time'&mdash;is false."
+(Dave Barry column, 2000-07-02)
+</li>
+<li>
+"I once had sex for an hour and five minutes, but that was on the day
+when you turn the clocks ahead."
+(Garry Shandling, 52nd Annual Emmys, 2000-09-10)
+</li>
+<li>
+"Would it impress you if I told you I invented Daylight Savings Time?"
+("Sahjhan" to "Lilah" in dialog from the "Loyalty" episode of "Angel,"
+originally aired 2002-02-25)
+</li>
+<li>
+"I thought you said Tulsa was a three hour flight."
+"Well, you're forgetting about the time difference."
+("Chandler" and "Joey" in dialog from the episode of "Friends" first
+aired 2002-12-05)
+</li>
+<li>
+"Is that a pertinent fact,
+or are you trying to dazzle me with your command of time zones?"
+(Kelsey Grammer as "Frasier Crane")
+</li>
+<li>
+"Don't worry about the world coming to an end today.
+It is already tomorrow in Australia."
+(Charles M. Schulz, provided by Steve Summit)
+</li>
+</ul>
+</body>
+</html>
diff --git a/usr.sbin/zic/tz-link.htm b/usr.sbin/zic/tz-link.htm
new file mode 100644
index 0000000..0e63073
--- /dev/null
+++ b/usr.sbin/zic/tz-link.htm
@@ -0,0 +1,443 @@
+<?xml version="1.0" encoding="US-ASCII"?>
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<title>Sources for Time Zone and Daylight Saving Time Data</title>
+<link rel="schema.DC" href="http://purl.org/DC/elements/1.1/" />
+<meta http-equiv="Content-type" content='text/html; charset="US-ASCII"' />
+<meta name="DC.Creator" content="Eggert, Paul" />
+<meta name="DC.Contributor" content="Olson, Arthur David" />
+<meta name="DC.Date" content="2004-05-24" />
+<meta name="DC.Description"
+ content="Sources of information about time zones and daylight saving time" />
+<meta name="DC.Identifier" content="http://www.twinsun.com/tz/tz-link.htm" />
+<meta name="Keywords"
+ content="database,daylight saving,DST,time zone,timezone,tz,zoneinfo" />
+</head>
+<body>
+<h1>Sources for Time Zone and Daylight Saving Time Data</h1>
+<address>
+@(#)tz-link.htm 7.42
+</address>
+<p>
+Please send corrections to this web page to the
+<a href="mailto:tz@elsie.nci.nih.gov">time zone mailing list</a>.
+</p>
+<h2>The <code>tz</code> database</h2>
+<p>
+The public-domain time zone database contains code and data
+that represent the history of local time
+for many representative locations around the globe.
+It is updated periodically to reflect changes made by political bodies
+to UTC offsets and daylight-saving rules.
+This database (often called <code>tz</code> or <code>zoneinfo</code>)
+is used by several implementations,
+including
+<a href="http://www.gnu.org/software/libc/">the GNU C Library</a> used in
+<a href="http://www.linux.org/">GNU/Linux</a>,
+<a href="http://www.freebsd.org/">FreeBSD</a>,
+<a href="http://www.netbsd.org/">NetBSD</a>,
+<a href="http://www.openbsd.org/">OpenBSD</a>,
+<a href="http://www.cygwin.com/">Cygwin</a>,
+<a href="http://www.delorie.com/djgpp/">DJGPP</a>,
+<a href="http://www.hp.com/products1/unix/operating/">HP-UX</a>,
+<a href="http://www.sgi.com/developers/technology/irix/">IRIX</a>,
+<a href="http://www.apple.com/macosx/">Mac OS X</a>,
+<a href="http://h71000.www7.hp.com/">OpenVMS</a>,
+<a href="http://wwws.sun.com/software/solaris/">Solaris</a>,
+<a href="http://www.tru64unix.compaq.com/">Tru64</a>, and
+<a href="http://www.sco.com/products/unixware/">UnixWare</a>.</p>
+<p>
+Each location in the database represents a national region where all
+clocks keeping local time have agreed since 1970.
+Locations are identified by continent or ocean and then by the name of
+the location, which is typically the largest city within the region.
+For example, <code>America/New_York</code>
+represents most of the US eastern time zone;
+<code>America/Indianapolis</code> represents most of Indiana, which
+uses eastern time without daylight saving time (DST);
+<code>America/Detroit</code> represents most of Michigan, which uses
+eastern time but with different DST rules in 1975;
+and other entries represent smaller regions like Starke County,
+Kentucky, which switched from central to eastern time in 1991.
+To use the database, set the <code>TZ</code> environment variable to
+the location's full name, e.g., <code>TZ="America/New_York"</code>.</p>
+<p>
+In the <code>tz</code> database's
+<a href="ftp://elsie.nci.nih.gov/pub/">FTP distribution</a>,
+the code is in the file <code>tzcode<var>C</var>.tar.gz</code>,
+where <code><var>C</var></code> is the code's version;
+similarly, the data are in <code>tzdata<var>D</var>.tar.gz</code>,
+where <code><var>D</var></code> is the data's version.
+The following shell commands download
+these files to a GNU/Linux or similar host; see the downloaded
+<code>README</code> file for what to do next.</p>
+<pre style="margin-left: 2em"><code><a href="http://www.gnu.org/software/wget/">wget</a> 'ftp://elsie.nci.nih.gov/pub/tz*.tar.gz'
+<a href="http://www.gnu.org/software/gzip/">gzip</a> -dc tzcode*.tar.gz | <a href="http://www.gnu.org/software/tar/">tar</a> -xf -
+gzip -dc tzdata*.tar.gz | tar -xf -
+</code></pre>
+<p>
+The code lets you compile the <code>tz</code> source files into
+machine-readable binary files, one for each location. It also lets
+you read a <code>tz</code> binary file and interpret time stamps for that
+location.</p>
+<p>
+The data are by no means authoritative. If you find errors, please
+send changes to the <a href="mailto:tz@elsie.nci.nih.gov">time zone
+mailing list</a>. You can also <a
+href="mailto:tz-request@elsie.nci.nih.gov">subscribe</a> to the
+mailing list, retrieve the <a
+href="ftp://elsie.nci.nih.gov/pub/tzarchive.gz">archive of old
+messages</a> (in gzip compressed format), or retrieve <a
+href="ftp://munnari.oz.au/pub/oldtz/">archived older versions of code
+and data</a>.</p>
+<p>
+The Web has several other sources for time zone and daylight saving time data.
+Here are some recent links that may be of interest.
+</p>
+<h2>Web pages using recent versions of the <code>tz</code> database</h2>
+<ul>
+<li><a href="http://twiki.org/cgi-bin/xtra/tzdate">Date and Time Gateway</a>
+is a text-based point-and-click interface to tables of current time
+throughout the world.</li>
+<li>Fancier web interfaces, roughly in ascending order of complexity, include:
+<ul>
+<li><a href="http://www.hilink.com.au/times/">Local Times Around the
+World</a></li>
+<li><a href="http://www.convertit.com/Go/ConvertIt/World_Time/Current_Time.ASP">Current Time in 1000 Places</a></li>
+<li><a href="http://timezoneconverter.com/">Time Zone Converter</a></li>
+</ul></li>
+<li><a href="http://www.holidayfestival.com/">The Worldwide Holiday
+&amp; Festival Site</a> lists DST-related clock changes along with
+holidays.</li>
+<li><a href="http://www.timeanddate.com/worldclock/">The World Clock -
+Time Zones</a>
+is a web interface to a time zone database derived from
+<code>tz</code>'s.</li>
+</ul>
+<h2>Other time zone database formats</h2>
+<ul>
+<li>The <a href="ftp://ftp.rfc-editor.org/in-notes/rfc2445.txt">
+Internet Calendaring and Scheduling Core Object Specification
+(iCalendar)</a> specification published by the <a
+href="http://www.ietf.org/html.charters/calsch-charter.html">IETF
+Calendaring and Scheduling Working Group (calsch)</a> covers time zone
+data; see its VTIMEZONE calendar component.</li>
+<li>The <a
+href="http://lists.w3.org/Archives/Public/www-rdf-calendar/">www-rdf-calendar</a>
+list discusses <a href="http://www.w3.org/RDF/">RDF</a>-based calendar
+and group scheduling systems, and has a <a
+href="http://www.w3.org/2002/12/cal/#tzd">workspace on time zone
+data</a> converted from <code>tz</code>. An earlier <a
+href="http://www.w3.org/2000/01/foo">schema</a> was sketched out by <a
+href="http://www.w3.org/People/Berners-Lee/">Tim Berners-Lee</a>.</li>
+<li><a
+href="http://www.calsch.org/ietf/archives/draft-ietf-calsch-many-xcal-02.txt">XCal</a>
+was a draft <a href="http://www.w3.org/XML/">XML</a> document type
+definition that corresponded to iCalendar.</li>
+</ul>
+<h2>Other <code>tz</code> compilers</h2>
+<ul>
+<li><a href="http://www.dachaplin.dsl.pipex.com/vzic">Vzic iCalendar
+Timezone Converter</a> describes a program Vzic that compiles
+<code>tz</code> source into iCalendar-compatible VTIMEZONE files.
+Vzic is freely
+available under the <a href="http://www.gnu.org/copyleft/gpl.html">GNU
+General Public License (GPL)</a>.</li>
+<li><a
+href="http://search.cpan.org/dist/DateTime-TimeZone/">DateTime::TimeZone</a>
+contains a script <code>parse_olson</code> that compiles
+<code>tz</code> source into <a href="http://www.perl.org/">Perl</a>
+modules. It is part of the Perl <a
+href="http://datetime.perl.org/">DateTime Project</a>, which is freely
+available under both the GPL and the Perl <a
+href="http://www.perl.com/language/misc/Artistic.html">Artistic
+License</a>. DateTime::TimeZone also contains a script
+<code>tests_from_zdump</code> that generates test cases for each clock
+transition in the <code>tz</code> database.</li>
+<li><a href="http://oss.software.ibm.com/icu/">International Components for
+Unicode (ICU)</a> contains a C/C++ library for internationalization that
+has a compiler from <samp>tz</samp> source into an ICU-specific format.
+ICU is freely available under a BSD-style license.</li>
+<li><a href="http://joda-time.sourceforge.net/">Joda Time - Java date
+and time API</a> contains a class
+<code>org.joda.time.tz.ZoneInfoCompiler</code> that compiles
+<code>tz</code> source into a Joda-specific binary format. Joda Time
+is freely available under a BSD-style license.</li>
+</ul>
+<h2>Other <code>tz</code> binary file readers</h2>
+<ul>
+<li>The <a href="http://www.gnu.org/software/libc/">GNU C Library</a>
+has an independent, thread-safe implementation of
+a <code>tz</code> binary file reader.
+This library is freely available under the
+<a href="http://www.gnu.org/copyleft/lesser.html">
+GNU Lesser General Public License (LGPL)</a>,
+and is widely used in GNU/Linux systems.</li>
+<li><a href="http://www.bmsi.com/java/#TZ">ZoneInfo.java</a>
+is a <code>tz</code> binary file reader written in Java.
+It is freely available under the GNU LGPL.</li>
+<li><a href="http://s.keim.free.fr/tz/doc.html">Python time zones</a>
+is a <code>tz</code> binary file reader written in <a
+href="http://www.python.org/">Python</a>. It is freely available
+under a BSD-style license.</li>
+</ul>
+<h2>Other <code>tz</code>-based time zone conversion software</h2>
+<ul>
+<li><a href="http://java.sun.com/">Sun Java</a> releases since 1.4
+contain a copy of a recent <samp>tz</samp> database in a Java-specific
+format.</li>
+<li><a
+href="http://www1.tip.nl/~t876506/AboutTimeZonesHC.html">HyperCard
+time zones calculator</a> is a HyperCard stack.</li>
+<li><a
+href="http://www.cimmyt.org/timezone/">World Time Explorer</a> is a
+Microsoft Windows program.</li>
+</ul>
+<h2>Other time zone databases</h2>
+<ul>
+<li><a href="http://www.astro.com/cgi-bin/atlw3/aq.cgi?lang=e">Atlas Query
+- Astrodienst</a> is Astrodienst's Web version of Shanks's
+excellent time zone history atlases published in both <a
+href="http://astrocom.com/software/pcatlas.php">computer</a> and <a
+href="http://astrocom.com/books/xrefa.php#SHANKS">book</a> form by <a
+href="http://astrocom.com/">Astro Communications Services</a>.</li>
+<li><a href="http://worldtime.com/">WORLDTIME: interactive atlas,
+time info, public holidays</a>
+contains information on local time, sunrise and sunset,
+and public holidays in several hundred cities around the world.</li>
+<li><a href="http://www.worldtimeserver.com/">World Time Server</a>
+is another time zone database.</li>
+<li><a href="http://tycho.usno.navy.mil/tzones.html">World Time Zones</a>
+contains data from the Time Service Department of the US Naval Observatory
+(USNO), used as the source
+for the <code>usno*</code> files in the <code>tz</code> distribution.</li>
+<li><a href="http://www.airportcitycodes.com/aaa/">Airlines, Airplanes
+and Airports</a> lists current standard times for thousands of
+airports around the world. This seems to be derived from
+the <a href="http://www.iata.org/sked/publications/">Standard
+Schedules Information Manual (SSIM)</a> of the
+the <a href="http://www.iata.org/">International Air Transport
+Association</a>,
+which gives current time zone rules for
+all the airports served by commercial aviation.</li>
+</ul>
+<h2>Maps</h2>
+<ul>
+<li>The <a href="http://www.odci.gov/">United States Central
+Intelligence Agency (CIA)</a> publishes a <a
+href="http://www.odci.gov/cia/publications/factbook/reference_maps/pdf/time_zones.pdf">time
+zone map</a>; the
+<a
+href="http://www.lib.utexas.edu/maps/world.html">Perry-Casta&ntilde;eda
+Library Map Collection</a>
+of the University of Texas at Austin has copies of
+recent editions.
+The pictorial quality is good,
+but the maps do not indicate summer time,
+and parts of the data are a few years out of date.</li>
+<li><a href="http://worldtimezone.com/">World timezones map with
+current time</a>
+has several fancy time zone maps; it covers Russia particularly well.
+The maps' pictorial quality is not quite as good as the CIA's
+but the maps are more up to date.</li>
+</ul>
+<h2>Time zone boundaries</h2>
+<ul>
+<li><a href="http://home-4.tiscali.nl/~t876506/Multizones.html">Time
+zone boundaries for multizone countries</a> summarizes legal
+boundaries between time zones within countries.</li>
+<li>Manifold.net's <a
+href="http://www.manifold.net/download/freemaps.html">Free Maps and
+GIS Data</a> includes a Manifold-format map of world time zone
+boundaries distributed under the GPL. The GeoCommunity's <a
+href="http://software.geocomm.com/data/intl_timezones.html">International
+Time Zones</a> publishes the same data in other formats.</li>
+<li>The US Geological Survey's National Atlas of the United States
+publishes the <a href="http://www.nationalatlas.gov/timeznm.html">Time
+Zones of the United States</a> in the public domain.</li>
+<li>The GeoCommunity lists several commercial sources for <a
+href="http://spatialnews.geocomm.com/features/timezones/">International
+Time Zones and Time Zone Data</a>.</li>
+</ul>
+<h2>Civil time concepts and history</h2>
+<ul>
+<li><a href="http://physics.nist.gov/time">A Walk through Time</a>
+surveys the evolution of timekeeping.</li>
+<li><a href="http://webexhibits.org/daylightsaving/">About Daylight
+Saving Time - History, rationale, laws and dates</a>
+is an overall history of DST.</li>
+<li><a href="http://toi.iriti.cnr.it/">The
+Time of Internet</a>
+describes time zones and daylight saving time,
+with diagrams.
+The time zone map is out of date, however.</li>
+<li><a href="http://www.phys.uu.nl/~vgent/idl/idl.htm">A History of
+the International Date Line</a> tells the story of the most important
+time zone boundary.</li>
+<li><a href="http://www.statoids.com/tconcept.html">Basic Time
+Zone Concepts</a> discusses terminological issues behind time zones.</li>
+</ul>
+<h2>National histories of legal time</h2>
+<dl>
+<dt>Australia</dt>
+<dd>The Community Relations Division of the New South Wales (NSW)
+Attorney General's Department maintains a <a
+href="http://www.lawlink.nsw.gov.au/crd.nsf/pages/time2">history of
+daylight saving in NSW</a>.</dd>
+<dt>Austria</dt>
+<dd>The Federal Office of Metrology and Surveying publishes a
+table of <a href="http://www.metrologie.at/pdf/sommerzeit.pdf"
+hreflang="de">daylight saving time in Austria (in German)</a>.</dd>
+<dt>Belgium</dt>
+<dd>The Royal Observatory of Belgium maintains a table of <a
+href="http://www.astro.oma.be/GENERAL/INFO/nli001a.html"
+hreflang="nl">time in Belgium (in Dutch)</a>.</dd>
+<dt>Brazil</dt>
+<dd>The Time Service Department of the National Observatory
+records <a href="http://pcdsh01.on.br/DecHV.html"
+hreflang="pt-BR">Brazil's daylight saving time decrees (in
+Portuguese)</a>.</dd>
+<dt>Canada</dt>
+<dd>The Institute for National Measurement Standards publishes current
+and some older information about <a
+href="http://inms-ienm.nrc-cnrc.gc.ca/time_services/daylight_savings_e.html">Time
+Zones and Daylight Saving Time</a>.</dd>
+<dt>Chile</dt>
+<dd>WebExhibits publishes a <a
+href="http://webexhibits.org/daylightsaving/chile.html"
+hreflang="es">history of official time (in Spanish)</a> originally
+written by the Chilean Hydrographic and Oceanographic Service.</dd>
+<dt>Germany</dt>
+<dd>The National Institute for Science and Technology maintains the <a
+href="http://www.ptb.de/en/org/4/44/441/dars_e.htm">Realisation of
+Legal Time in Germany</a>.</dd>
+<dt>Israel</dt>
+<dd>The Interior Ministry periodically issues <a
+href="ftp://ftp.cs.huji.ac.il/pub/tz/announcements/"
+hreflang="he">announcements (in Hebrew)</a>.</dd>
+<dt>Mexico</dt>
+<dd>The Investigation and Analysis Service of the Mexican Library of
+Congress has published a <a
+href="http://www.cddhcu.gob.mx/bibliot/publica/inveyana/polisoc/horver/"
+hreflang="es">history of Mexican local time (in Spanish)</a>.</dd>
+<dt>Malaysia</dt>
+<dd>See Singapore below.</dd>
+<dt>Netherlands</dt>
+<dd><a href="http://www.phys.uu.nl/~vgent/wettijd/wettijd.htm"
+hreflang="nl">Legal time in the Netherlands (in Dutch)</a>
+covers the history of local time in the Netherlands from ancient times.</dd>
+<dt>New Zealand</dt>
+<dd>The Department of Internal Affairs maintains a brief history <a
+href="http://www.dia.govt.nz/diawebsite.nsf/wpg_URL/Resource-material-Information-We-Provide-About-Daylight-Saving">about
+daylight saving</a>. The privately-maintained <a
+href="http://www.astrologyhouse.co.nz/timechanges.htm">Time Changes in
+New Zealand</a> has more details.</dd>
+<dt>Singapore</dt>
+<dd><a
+href="http://www.math.nus.edu.sg/aslaksen/teaching/timezone.html">Why
+is Singapore in the "Wrong" Time Zone?</a> details the
+history of legal time in Singapore and Malaysia.</dd>
+<dt>United Kingdom</dt>
+<dd><a
+href="http://www.srcf.ucam.org/~jsm28/british-time/">History of
+legal time in Britain</a> discusses in detail the country
+with perhaps the best-documented history of clock adjustments.
+The National Physical Laboratory also maintains an <a
+href="http://www.npl.co.uk/time/summer_time_archive.html">archive
+of summer time dates</a>.</dd>
+</dl>
+<h2>Precision timekeeping</h2>
+<ul>
+<li><a
+href="http://literature.agilent.com/litwebbin/purl.cgi?org_id=tmo&amp;pub_id=5965-7984E">The
+Science of Timekeeping</a> is a thorough introduction
+to the theory and practice of precision timekeeping.</li>
+<li><a href="http://www.ntp.org/">NTP: The Network Time Protocol</a>
+discusses how to synchronize clocks of
+Internet hosts.</li>
+<li><a href="http://gauss.gge.unb.ca/GMT.UT.and.the.RGO.txt"
+charset="macintosh">A
+Few Facts Concerning GMT, UT, and the RGO</a>
+answers questions like "What is the difference between GMT and UTC?"</li>
+<li><a
+href="http://www.gb.nrao.edu/~rfisher/Ephemerides/times.html">Astronomical
+Times</a> explains more abstruse astronomical time scales like TT, TCG,
+and TDB.</li>
+<li>The <a href="http://www.iau.org/">IAU</a>'s <a
+href="http://www.iau-sofa.rl.ac.uk/">Standards Of Fundamental
+Astronomy</a> (SOFA) initiative publishes Fortran code for converting
+among time scales like TAI, TDB, TT and UTC.</li>
+<li><a href="http://www.jpl.nasa.gov/basics/bsf2-3.htm">Basics of
+Space Flight - Reference Systems - Time Conventions</a>
+briefly explains interplanetary space flight timekeeping.</li>
+<li><a
+href="http://www.giss.nasa.gov/tools/mars24/help/notes.html">Technical
+Notes on Mars Solar Time as Adopted by the Mars24 Sunclock</a> briefly
+describes Mars Coordinated Time (MTC) and the diverse local time
+scales used by each landed mission on Mars.</li>
+<li><a
+href="http://hpiers.obspm.fr/eop-pc/products/bulletins/bulletins.html">Bulletins
+maintained by the IERS EOP (PC)</a> contains official publications of
+the Earth Orientation Parameters Product Center of the
+International Earth Rotation Service, the committee that decides
+when leap seconds occur.</li>
+<li>The <a
+href="http://www.mail-archive.com/leapsecs@rom.usno.navy.mil/">Leap
+Second Discussion List</a> covers McCarthy and Klepczynski's proposal
+to discontinue leap seconds, published in <a
+href="http://www.gpsworld.com/">GPS World</a> <strong>10</strong>, 11
+(1999-11), 50&ndash;57 and discussed further in R. A. Nelson et al.,
+<a href="http://www.cl.cam.ac.uk/~mgk25/time/metrologia-leapsecond.pdf">The
+leap second: its history and possible future</a>,
+<a href="http://www.bipm.fr/metrologia/metrologia.html">Metrologia</a>
+<strong>38</strong> (2001), 509&ndash;529.
+<a href="http://www.ucolick.org/~sla/leapsecs/onlinebib.html">The
+Future of Leap Seconds</a> catalogs information about this
+contentious issue.</li>
+</ul>
+<h2>Time notation</h2>
+<ul>
+<li>
+<a href="http://www.cl.cam.ac.uk/~mgk25/iso-time.html">A Summary of
+the International Standard Date and Time Notation</a> is a good
+summary of ISO
+8601:1988 - Data elements and interchange formats - Information interchange
+- Representation of dates and times (which has been superseded by
+<a href="http://www.iso.org/iso/en/CatalogueDetailPage.CatalogueDetail?CSNUMBER=26780">ISO 8601:2000</a>).</li>
+<li>
+Section 3.3 of <a
+href="ftp://ftp.rfc-editor.org/in-notes/rfc2822.txt">Internet RFC 2822</a>
+specifies the time notation used in email and <a
+href="ftp://ftp.rfc-editor.org/in-notes/rfc2616.txt">HTTP</a> headers.</li>
+<li>
+<a href="ftp://ftp.rfc-editor.org/in-notes/rfc3339.txt">Internet RFC
+3339</a> specifies an ISO 8601 profile for use in new Internet
+protocols.</li>
+<li>
+<a href="http://www.exit109.com/~ghealton/y2k/yrexamples.html">The
+Best of Dates, the Worst of Dates</a> covers many problems encountered
+by software developers when handling dates and time stamps.</li>
+<li>
+Alphabetic time zone abbreviations should not be used as unique
+identifiers for UTC offsets as they are ambiguous in practice. For
+example, "EST" denotes 5 hours behind UTC in English-speaking North
+America, but it denotes 10 or 11 hours ahead of UTC in Australia;
+and French-speaking North Americans prefer "HNE" to "EST". For
+compatibility with <a href="http://www.pasc.org/#POSIX">POSIX</a> the
+<code>tz</code> database contains English abbreviations for all time
+stamps but in many cases these are merely inventions of the database
+maintainers.</li>
+</ul>
+<h2>Related indexes</h2>
+<ul>
+<li><a href="tz-art.htm">Time and the Arts</a></li>
+<li><a href="http://dmoz.org/Reference/Time/">Open Directory -
+Reference: Time</a></li>
+<li><a href="http://directory.google.com/Top/Reference/Time/">Google Directory - Reference &gt; Time</a></li>
+<li><a href="http://dir.yahoo.com/Science/Measurements_and_Units/Time/">Yahoo! Science &gt; Measurements and Units &gt; Time</a></li>
+</ul>
+</body>
+</html>
diff --git a/usr.sbin/zic/zdump.8 b/usr.sbin/zic/zdump.8
new file mode 100644
index 0000000..3a685fd
--- /dev/null
+++ b/usr.sbin/zic/zdump.8
@@ -0,0 +1,49 @@
+.\"
+.\" @(#)zdump.8 7.3
+.\" $FreeBSD$
+.\"
+.Dd June 20, 2004
+.Dt ZDUMP 8
+.Os
+.Sh NAME
+.Nm zdump
+.Nd timezone dumper
+.Sh SYNOPSIS
+.Nm
+.Op Fl -version
+.Op Fl v
+.Op Fl c Ar cutoffyear
+.Op Ar zonename ...
+.Sh DESCRIPTION
+The
+.Nm
+utility prints the current time in each
+.Ar zonename
+named on the command line.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl -version
+Output version information and exit.
+.It Fl v
+For each
+.Ar zonename
+on the command line,
+print the time at the lowest possible time value,
+the time one day after the lowest possible time value,
+the times both one second before and exactly at
+each detected time discontinuity,
+the time at one day less than the highest possible time value,
+and the time at the highest possible time value,
+Each line ends with
+.Em isdst=1
+if the given time is Daylight Saving Time or
+.Em isdst=0
+otherwise.
+.It Fl c Ar cutoffyear
+Cut off the verbose output near the start of the given year.
+.El
+.Sh "SEE ALSO"
+.Xr ctime 3 ,
+.Xr tzfile 5 ,
+.Xr zic 8
diff --git a/usr.sbin/zic/zdump.c b/usr.sbin/zic/zdump.c
new file mode 100644
index 0000000..980c8a8
--- /dev/null
+++ b/usr.sbin/zic/zdump.c
@@ -0,0 +1,375 @@
+static const char elsieid[] = "@(#)zdump.c 7.31";
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+/*
+** This code has been made independent of the rest of the time
+** conversion package to increase confidence in the verification it provides.
+** You can use this code to help in verifying other implementations.
+*/
+
+#include <err.h>
+#include <stdio.h> /* for stdout, stderr */
+#include <stdlib.h> /* for exit, malloc, atoi */
+#include <string.h> /* for strcpy */
+#include <sys/types.h> /* for time_t */
+#include <time.h> /* for struct tm */
+#include <unistd.h>
+
+#ifndef MAX_STRING_LENGTH
+#define MAX_STRING_LENGTH 1024
+#endif /* !defined MAX_STRING_LENGTH */
+
+#ifndef TRUE
+#define TRUE 1
+#endif /* !defined TRUE */
+
+#ifndef FALSE
+#define FALSE 0
+#endif /* !defined FALSE */
+
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0
+#endif /* !defined EXIT_SUCCESS */
+
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1
+#endif /* !defined EXIT_FAILURE */
+
+#ifndef SECSPERMIN
+#define SECSPERMIN 60
+#endif /* !defined SECSPERMIN */
+
+#ifndef MINSPERHOUR
+#define MINSPERHOUR 60
+#endif /* !defined MINSPERHOUR */
+
+#ifndef SECSPERHOUR
+#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
+#endif /* !defined SECSPERHOUR */
+
+#ifndef HOURSPERDAY
+#define HOURSPERDAY 24
+#endif /* !defined HOURSPERDAY */
+
+#ifndef EPOCH_YEAR
+#define EPOCH_YEAR 1970
+#endif /* !defined EPOCH_YEAR */
+
+#ifndef TM_YEAR_BASE
+#define TM_YEAR_BASE 1900
+#endif /* !defined TM_YEAR_BASE */
+
+#ifndef DAYSPERNYEAR
+#define DAYSPERNYEAR 365
+#endif /* !defined DAYSPERNYEAR */
+
+#ifndef isleap
+#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
+#endif /* !defined isleap */
+
+#if HAVE_GETTEXT - 0
+#include "locale.h" /* for setlocale */
+#include "libintl.h"
+#endif /* HAVE_GETTEXT - 0 */
+
+#ifndef GNUC_or_lint
+#ifdef lint
+#define GNUC_or_lint
+#endif /* defined lint */
+#ifndef lint
+#ifdef __GNUC__
+#define GNUC_or_lint
+#endif /* defined __GNUC__ */
+#endif /* !defined lint */
+#endif /* !defined GNUC_or_lint */
+
+#ifndef INITIALIZE
+#ifdef GNUC_or_lint
+#define INITIALIZE(x) ((x) = 0)
+#endif /* defined GNUC_or_lint */
+#ifndef GNUC_or_lint
+#define INITIALIZE(x)
+#endif /* !defined GNUC_or_lint */
+#endif /* !defined INITIALIZE */
+
+/*
+** For the benefit of GNU folk...
+** `_(MSGID)' uses the current locale's message library string for MSGID.
+** The default is to use gettext if available, and use MSGID otherwise.
+*/
+
+#ifndef _
+#if HAVE_GETTEXT - 0
+#define _(msgid) gettext(msgid)
+#else /* !(HAVE_GETTEXT - 0) */
+#define _(msgid) msgid
+#endif /* !(HAVE_GETTEXT - 0) */
+#endif /* !defined _ */
+
+#ifndef TZ_DOMAIN
+#define TZ_DOMAIN "tz"
+#endif /* !defined TZ_DOMAIN */
+
+#ifndef P
+#ifdef __STDC__
+#define P(x) x
+#endif /* defined __STDC__ */
+#ifndef __STDC__
+#define P(x) ()
+#endif /* !defined __STDC__ */
+#endif /* !defined P */
+
+extern char ** environ;
+extern char * tzname[2];
+
+static char * abbr P((struct tm * tmp));
+static long delta P((struct tm * newp, struct tm * oldp));
+static time_t hunt P((char * name, time_t lot, time_t hit));
+static size_t longest;
+static void show P((char * zone, time_t t, int v));
+static void usage(void);
+
+int
+main(argc, argv)
+int argc;
+char * argv[];
+{
+ register int i;
+ register int c;
+ register int vflag;
+ register char * cutoff;
+ register int cutyear;
+ register long cuttime;
+ char ** fakeenv;
+ time_t now;
+ time_t t;
+ time_t newt;
+ time_t hibit;
+ struct tm tm;
+ struct tm newtm;
+
+ INITIALIZE(cuttime);
+#if HAVE_GETTEXT - 0
+ (void) setlocale(LC_MESSAGES, "");
+#ifdef TZ_DOMAINDIR
+ (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
+#endif /* defined(TEXTDOMAINDIR) */
+ (void) textdomain(TZ_DOMAIN);
+#endif /* HAVE_GETTEXT - 0 */
+ for (i = 1; i < argc; ++i)
+ if (strcmp(argv[i], "--version") == 0) {
+ errx(EXIT_SUCCESS, "%s", elsieid);
+ }
+ vflag = 0;
+ cutoff = NULL;
+ while ((c = getopt(argc, argv, "c:v")) == 'c' || c == 'v')
+ if (c == 'v')
+ vflag = 1;
+ else cutoff = optarg;
+ if ((c != -1) ||
+ (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) {
+ usage();
+ }
+ if (cutoff != NULL) {
+ int y;
+
+ cutyear = atoi(cutoff);
+ cuttime = 0;
+ for (y = EPOCH_YEAR; y < cutyear; ++y)
+ cuttime += DAYSPERNYEAR + isleap(y);
+ cuttime *= SECSPERHOUR * HOURSPERDAY;
+ }
+ (void) time(&now);
+ longest = 0;
+ for (i = optind; i < argc; ++i)
+ if (strlen(argv[i]) > longest)
+ longest = strlen(argv[i]);
+ for (hibit = 1; (hibit << 1) != 0; hibit <<= 1)
+ continue;
+ {
+ register int from;
+ register int to;
+
+ for (i = 0; environ[i] != NULL; ++i)
+ continue;
+ fakeenv = (char **) malloc((size_t) ((i + 2) *
+ sizeof *fakeenv));
+ if (fakeenv == NULL ||
+ (fakeenv[0] = (char *) malloc((size_t) (longest +
+ 4))) == NULL)
+ errx(EXIT_FAILURE,
+ _("malloc() failed"));
+ to = 0;
+ (void) strcpy(fakeenv[to++], "TZ=");
+ for (from = 0; environ[from] != NULL; ++from)
+ if (strncmp(environ[from], "TZ=", 3) != 0)
+ fakeenv[to++] = environ[from];
+ fakeenv[to] = NULL;
+ environ = fakeenv;
+ }
+ for (i = optind; i < argc; ++i) {
+ static char buf[MAX_STRING_LENGTH];
+
+ (void) strcpy(&fakeenv[0][3], argv[i]);
+ if (!vflag) {
+ show(argv[i], now, FALSE);
+ continue;
+ }
+ /*
+ ** Get lowest value of t.
+ */
+ t = hibit;
+ if (t > 0) /* time_t is unsigned */
+ t = 0;
+ show(argv[i], t, TRUE);
+ t += SECSPERHOUR * HOURSPERDAY;
+ show(argv[i], t, TRUE);
+ tm = *localtime(&t);
+ (void) strncpy(buf, abbr(&tm), (sizeof buf) - 1);
+ for ( ; ; ) {
+ if (cutoff != NULL && t >= cuttime)
+ break;
+ newt = t + SECSPERHOUR * 12;
+ if (cutoff != NULL && newt >= cuttime)
+ break;
+ if (newt <= t)
+ break;
+ newtm = *localtime(&newt);
+ if (delta(&newtm, &tm) != (newt - t) ||
+ newtm.tm_isdst != tm.tm_isdst ||
+ strcmp(abbr(&newtm), buf) != 0) {
+ newt = hunt(argv[i], t, newt);
+ newtm = *localtime(&newt);
+ (void) strncpy(buf, abbr(&newtm),
+ (sizeof buf) - 1);
+ }
+ t = newt;
+ tm = newtm;
+ }
+ /*
+ ** Get highest value of t.
+ */
+ t = ~((time_t) 0);
+ if (t < 0) /* time_t is signed */
+ t &= ~hibit;
+ t -= SECSPERHOUR * HOURSPERDAY;
+ show(argv[i], t, TRUE);
+ t += SECSPERHOUR * HOURSPERDAY;
+ show(argv[i], t, TRUE);
+ }
+ if (fflush(stdout) || ferror(stdout))
+ errx(EXIT_FAILURE, _("error writing standard output"));
+ exit(EXIT_SUCCESS);
+
+ /* gcc -Wall pacifier */
+ for ( ; ; )
+ continue;
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+_("usage: zdump [--version] [-v] [-c cutoff] zonename ...\n"));
+ exit(EXIT_FAILURE);
+}
+
+static time_t
+hunt(name, lot, hit)
+char * name;
+time_t lot;
+time_t hit;
+{
+ time_t t;
+ struct tm lotm;
+ struct tm tm;
+ static char loab[MAX_STRING_LENGTH];
+
+ lotm = *localtime(&lot);
+ (void) strncpy(loab, abbr(&lotm), (sizeof loab) - 1);
+ while ((hit - lot) >= 2) {
+ t = lot / 2 + hit / 2;
+ if (t <= lot)
+ ++t;
+ else if (t >= hit)
+ --t;
+ tm = *localtime(&t);
+ if (delta(&tm, &lotm) == (t - lot) &&
+ tm.tm_isdst == lotm.tm_isdst &&
+ strcmp(abbr(&tm), loab) == 0) {
+ lot = t;
+ lotm = tm;
+ } else hit = t;
+ }
+ show(name, lot, TRUE);
+ show(name, hit, TRUE);
+ return hit;
+}
+
+/*
+** Thanks to Paul Eggert (eggert@twinsun.com) for logic used in delta.
+*/
+
+static long
+delta(newp, oldp)
+struct tm * newp;
+struct tm * oldp;
+{
+ long result;
+ int tmy;
+
+ if (newp->tm_year < oldp->tm_year)
+ return -delta(oldp, newp);
+ result = 0;
+ for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy)
+ result += DAYSPERNYEAR + isleap(tmy + TM_YEAR_BASE);
+ result += newp->tm_yday - oldp->tm_yday;
+ result *= HOURSPERDAY;
+ result += newp->tm_hour - oldp->tm_hour;
+ result *= MINSPERHOUR;
+ result += newp->tm_min - oldp->tm_min;
+ result *= SECSPERMIN;
+ result += newp->tm_sec - oldp->tm_sec;
+ return result;
+}
+
+static void
+show(zone, t, v)
+char * zone;
+time_t t;
+int v;
+{
+ struct tm * tmp;
+
+ (void) printf("%-*s ", (int) longest, zone);
+ if (v)
+ (void) printf("%.24s UTC = ", asctime(gmtime(&t)));
+ tmp = localtime(&t);
+ (void) printf("%.24s", asctime(tmp));
+ if (*abbr(tmp) != '\0')
+ (void) printf(" %s", abbr(tmp));
+ if (v) {
+ (void) printf(" isdst=%d", tmp->tm_isdst);
+#ifdef TM_GMTOFF
+ (void) printf(" gmtoff=%ld", tmp->TM_GMTOFF);
+#endif /* defined TM_GMTOFF */
+ }
+ (void) printf("\n");
+}
+
+static char *
+abbr(tmp)
+struct tm * tmp;
+{
+ register char * result;
+ static char nada;
+
+ if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1)
+ return &nada;
+ result = tzname[tmp->tm_isdst];
+ return (result == NULL) ? &nada : result;
+}
diff --git a/usr.sbin/zic/zdump/Makefile b/usr.sbin/zic/zdump/Makefile
new file mode 100644
index 0000000..3443b15
--- /dev/null
+++ b/usr.sbin/zic/zdump/Makefile
@@ -0,0 +1,13 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/..
+
+PROG= zdump
+MAN= zdump.8
+SRCS= zdump.c ialloc.c scheck.c
+
+CFLAGS+= -DTM_GMTOFF=tm_gmtoff -DTM_ZONE=tm_zone -DSTD_INSPIRED -DPCTS
+CFLAGS+= -DHAVE_LONG_DOUBLE -DTZDIR=\"/usr/share/zoneinfo\" -Demkdir=mkdir
+CFLAGS+= -I${.CURDIR}/.. -I${.CURDIR}/../../../lib/libc/stdtime
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/zic/zic.8 b/usr.sbin/zic/zic.8
new file mode 100644
index 0000000..76a5a88
--- /dev/null
+++ b/usr.sbin/zic/zic.8
@@ -0,0 +1,394 @@
+.\" $FreeBSD$
+.Dd June 20, 2004
+.Dt ZIC 8
+.Os
+.Sh NAME
+.Nm zic
+.Nd timezone compiler
+.Sh SYNOPSIS
+.Nm
+.Op Fl -version
+.Op Fl Dsv
+.Op Fl d Ar directory
+.Op Fl g Ar group
+.Op Fl L Ar leapsecondfilename
+.Op Fl l Ar localtime
+.Op Fl m Ar mode
+.Op Fl p Ar posixrules
+.Op Fl u Ar user
+.Op Fl y Ar command
+.Op Ar filename ...
+.Sh DESCRIPTION
+The
+.Nm
+utility reads text from the file(s) named on the command line
+and creates the time conversion information files specified in this input.
+If a
+.Ar filename
+is
+.Em - ,
+the standard input is read.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl -version
+Output version information and exit.
+.It Fl D
+Do not automatically create directories.
+If the input file(s) specify
+an output file in a directory which does not already exist, the
+default behavior is to attempt to create the directory.
+If
+.Fl D
+is specified,
+.Nm
+will instead error out immediately.
+.It Fl d Ar directory
+Create time conversion information files in the named directory rather than
+in the standard directory named below.
+.It Fl g Ar group
+After creating each output file, change its group ownership to the
+specified
+.Ar group
+(which can be either a name or a numeric group ID).
+.It Fl L Ar leapsecondfilename
+Read leap second information from the file with the given name.
+If this option is not used,
+no leap second information appears in output files.
+.It Fl l Ar timezone
+Use the given
+.Ar time zone
+as local time.
+The
+.Nm
+utility will act as if the input contained a link line of the form
+.Pp
+.D1 No "Link timezone localtime"
+.Pp
+(Note that this action has no effect on
+.Fx ,
+since the local time zone is specified in
+.Pa /etc/localtime
+and not
+.Pa /usr/share/zoneinfo/localtime . )
+.It Fl m Ar mode
+After creating each output file, change its access mode to
+.Ar mode .
+Both numeric and alphabetic modes are accepted
+(see
+.Xr chmod 1 ) .
+.It Fl p Ar timezone
+Use the given
+.Ar "time zone" Ns 's
+rules when handling POSIX-format
+time zone environment variables.
+The
+.Nm
+utility will act as if the input contained a link line of the form
+.Pp
+.D1 No "Link timezone posixrules"
+.It Fl u Ar user
+After creating each output file, change its owner to
+.Ar user
+(which can be either a name or a numeric user ID).
+.It Fl v
+Complain if a year that appears in a data file is outside the range
+of years representable by
+.Xr time 3
+values.
+.It Fl s
+Limit time values stored in output files to values that are the same
+whether they are taken to be signed or unsigned.
+You can use this option to generate SVVS-compatible files.
+.It Fl y Ar command
+Use the given
+.Ar command
+rather than
+.Em yearistype
+when checking year types (see below).
+.El
+.Pp
+Input lines are made up of fields.
+Fields are separated from one another by any number of white space characters.
+Leading and trailing white space on input lines is ignored.
+An unquoted sharp character (#) in the input introduces a comment which extends
+to the end of the line the sharp character appears on.
+White space characters and sharp characters may be enclosed in double quotes
+(") if they are to be used as part of a field.
+Any line that is blank (after comment stripping) is ignored.
+Non-blank lines are expected to be of one of three types:
+rule lines, zone lines, and link lines.
+.Pp
+A rule line has the form:
+.Dl "Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S"
+For example:
+.Dl "Rule US 1967 1973 \- Apr lastSun 2:00 1:00 D"
+.Pp
+The fields that make up a rule line are:
+.Bl -tag -width "LETTER/S" -offset indent
+.It NAME
+Give the (arbitrary) name of the set of rules this rule is part of.
+.It FROM
+Give the first year in which the rule applies.
+Any integer year can be supplied; the Gregorian calendar is assumed.
+The word
+.Em minimum
+(or an abbreviation) means the minimum year representable as an integer.
+The word
+.Em maximum
+(or an abbreviation) means the maximum year representable as an integer.
+Rules can describe times that are not representable as time values,
+with the unrepresentable times ignored; this allows rules to be portable
+among hosts with differing time value types.
+.It TO
+Give the final year in which the rule applies.
+In addition to
+.Em minimum
+and
+.Em maximum
+(as above),
+the word
+.Em only
+(or an abbreviation)
+may be used to repeat the value of the
+.Em FROM
+field.
+.It TYPE
+Give the type of year in which the rule applies.
+If
+.Em TYPE
+is
+.Em \-
+then the rule applies in all years between
+.Em FROM
+and
+.Em TO
+inclusive.
+If
+.Em TYPE
+is something else, then
+.Nm
+executes the command
+.Li yearistype Ar year Ar type
+to check the type of a year:
+an exit status of zero is taken to mean that the year is of the given type;
+an exit status of one is taken to mean that the year is not of the given type.
+.It IN
+Name the month in which the rule takes effect.
+Month names may be abbreviated.
+.It ON
+Give the day on which the rule takes effect.
+Recognized forms include:
+.Pp
+.Bl -tag -width lastSun -compact -offset indent
+.It \&5
+the fifth of the month
+.It lastSun
+the last Sunday in the month
+.It lastMon
+the last Monday in the month
+.It Sun>=8
+first Sunday on or after the eighth
+.It Sun<=25
+last Sunday on or before the 25th
+.El
+.Pp
+Names of days of the week may be abbreviated or spelled out in full.
+Note that there must be no spaces within the
+.Em ON
+field.
+.It AT
+Give the time of day at which the rule takes effect.
+Recognized forms include:
+.Pp
+.Bl -tag -width "\&1:28:14" -offset indent -compact
+.It 2
+time in hours
+.It 2:00
+time in hours and minutes
+.It 15:00
+24-hour format time (for times after noon)
+.It 1:28:14
+time in hours, minutes, and seconds
+.El
+.Pp
+where hour 0 is midnight at the start of the day,
+and hour 24 is midnight at the end of the day.
+Any of these forms may be followed by the letter
+.Sq Li w
+if the given time is local
+.Dq "wall clock"
+time,
+.Sq Li s
+if the given time is local
+.Dq standard
+time, or
+.Sq Li u
+(or
+.Sq Li g
+or
+.Sq Li z )
+if the given time is universal time;
+in the absence of an indicator,
+wall clock time is assumed.
+.It SAVE
+Give the amount of time to be added to local standard time when the rule is in
+effect.
+This field has the same format as the
+.Em AT
+field
+(although, of course, the
+.Sq Li w
+and
+.Sq Li s
+suffixes are not used).
+.It LETTER/S
+Give the
+.Dq "variable part"
+(for example, the
+.Dq S
+or
+.Dq D
+in
+.Dq EST
+or
+.Dq EDT )
+of time zone abbreviations to be used when this rule is in effect.
+If this field is
+.Em \- ,
+the variable part is null.
+.El
+.Pp
+A zone line has the form:
+.Dl "Zone NAME GMTOFF RULES/SAVE FORMAT [UNTIL]"
+For example:
+.Dl "Zone Australia/Adelaide 9:30 Aus CST 1971 Oct 31 2:00"
+The fields that make up a zone line are:
+.Bl -tag -width indent
+.It NAME
+The name of the time zone.
+This is the name used in creating the time conversion information file for the
+zone.
+.It GMTOFF
+The amount of time to add to UTC to get standard time in this zone.
+This field has the same format as the
+.Em AT
+and
+.Em SAVE
+fields of rule lines;
+begin the field with a minus sign if time must be subtracted from UTC.
+.It RULES/SAVE
+The name of the rule(s) that apply in the time zone or,
+alternately, an amount of time to add to local standard time.
+If this field is
+.Em \-
+then standard time always applies in the time zone.
+.It FORMAT
+The format for time zone abbreviations in this time zone.
+The pair of characters
+.Em %s
+is used to show where the
+.Dq "variable part"
+of the time zone abbreviation goes.
+Alternately,
+a slash (/)
+separates standard and daylight abbreviations.
+.It UNTIL
+The time at which the UTC offset or the rule(s) change for a location.
+It is specified as a year, a month, a day, and a time of day.
+If this is specified,
+the time zone information is generated from the given UTC offset
+and rule change until the time specified.
+The month, day, and time of day have the same format as the IN, ON, and AT
+columns of a rule; trailing columns can be omitted, and default to the
+earliest possible value for the missing columns.
+.Pp
+The next line must be a
+.Dq continuation
+line; this has the same form as a zone line except that the
+string
+.Dq Zone
+and the name are omitted, as the continuation line will
+place information starting at the time specified as the
+.Em UNTIL
+field in the previous line in the file used by the previous line.
+Continuation lines may contain an
+.Em UNTIL
+field, just as zone lines do, indicating that the next line is a further
+continuation.
+.El
+.Pp
+A link line has the form
+.Dl "Link LINK-FROM LINK-TO"
+For example:
+.Dl "Link Europe/Istanbul Asia/Istanbul"
+The
+.Em LINK-FROM
+field should appear as the
+.Em NAME
+field in some zone line;
+the
+.Em LINK-TO
+field is used as an alternate name for that zone.
+.Pp
+Except for continuation lines,
+lines may appear in any order in the input.
+.Pp
+Lines in the file that describes leap seconds have the following form:
+.Dl "Leap YEAR MONTH DAY HH:MM:SS CORR R/S"
+For example:
+.Dl "Leap 1974 Dec 31 23:59:60 + S"
+The
+.Em YEAR ,
+.Em MONTH ,
+.Em DAY ,
+and
+.Em HH:MM:SS
+fields tell when the leap second happened.
+The
+.Em CORR
+field
+should be
+.Dq +
+if a second was added
+or
+.Dq -
+if a second was skipped.
+.\" There's no need to document the following, since it's impossible for more
+.\" than one leap second to be inserted or deleted at a time.
+.\" The C Standard is in error in suggesting the possibility.
+.\" See Terry J Quinn, The BIPM and the accurate measure of time,
+.\" Proc IEEE 79, 7 (July 1991), 894-905.
+.\" or
+.\" .q ++
+.\" if two seconds were added
+.\" or
+.\" .q --
+.\" if two seconds were skipped.
+The
+.Em R/S
+field
+should be (an abbreviation of)
+.Dq Stationary
+if the leap second time given by the other fields should be interpreted as UTC
+or
+(an abbreviation of)
+.Dq Rolling
+if the leap second time given by the other fields should be interpreted as
+local wall clock time.
+.Sh NOTE
+For areas with more than two types of local time,
+you may need to use local standard time in the
+.Em AT
+field of the earliest transition time's rule to ensure that
+the earliest transition time recorded in the compiled file is correct.
+.Sh FILES
+.Bl -tag -width /usr/share/zoneinfo -compact
+.It /usr/share/zoneinfo
+standard directory used for created files
+.El
+.Sh "SEE ALSO"
+.Xr ctime 3 ,
+.Xr tzfile 5 ,
+.Xr zdump 8
+.\" @(#)zic.8 7.18
diff --git a/usr.sbin/zic/zic.c b/usr.sbin/zic/zic.c
new file mode 100644
index 0000000..10d79a8
--- /dev/null
+++ b/usr.sbin/zic/zic.c
@@ -0,0 +1,2254 @@
+static const char elsieid[] = "@(#)zic.c 7.116";
+
+#ifndef lint
+static const char rcsid[] =
+ "$FreeBSD$";
+#endif /* not lint */
+
+#include "private.h"
+#include "tzfile.h"
+#include <err.h>
+#include <locale.h>
+#include <sys/stat.h> /* for umask manifest constants */
+#include <sys/types.h>
+#include <unistd.h>
+
+#define MKDIR_UMASK (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
+
+/*
+** On some ancient hosts, predicates like `isspace(C)' are defined
+** only if isascii(C) || C == EOF. Modern hosts obey the C Standard,
+** which says they are defined only if C == ((unsigned char) C) || C == EOF.
+** Neither the C Standard nor POSIX require that `isascii' exist.
+** For portability, we check both ancient and modern requirements.
+** If isascii is not defined, the isascii check succeeds trivially.
+*/
+#include "ctype.h"
+#ifndef isascii
+#define isascii(x) 1
+#endif
+
+struct rule {
+ const char * r_filename;
+ int r_linenum;
+ const char * r_name;
+
+ int r_loyear; /* for example, 1986 */
+ int r_hiyear; /* for example, 1986 */
+ const char * r_yrtype;
+
+ int r_month; /* 0..11 */
+
+ int r_dycode; /* see below */
+ int r_dayofmonth;
+ int r_wday;
+
+ long r_tod; /* time from midnight */
+ int r_todisstd; /* above is standard time if TRUE */
+ /* or wall clock time if FALSE */
+ int r_todisgmt; /* above is GMT if TRUE */
+ /* or local time if FALSE */
+ long r_stdoff; /* offset from standard time */
+ const char * r_abbrvar; /* variable part of abbreviation */
+
+ int r_todo; /* a rule to do (used in outzone) */
+ time_t r_temp; /* used in outzone */
+};
+
+/*
+** r_dycode r_dayofmonth r_wday
+*/
+
+#define DC_DOM 0 /* 1..31 */ /* unused */
+#define DC_DOWGEQ 1 /* 1..31 */ /* 0..6 (Sun..Sat) */
+#define DC_DOWLEQ 2 /* 1..31 */ /* 0..6 (Sun..Sat) */
+
+struct zone {
+ const char * z_filename;
+ int z_linenum;
+
+ const char * z_name;
+ long z_gmtoff;
+ const char * z_rule;
+ const char * z_format;
+
+ long z_stdoff;
+
+ struct rule * z_rules;
+ int z_nrules;
+
+ struct rule z_untilrule;
+ time_t z_untiltime;
+};
+
+static void addtt P((time_t starttime, int type));
+static int addtype P((long gmtoff, const char * abbr, int isdst,
+ int ttisstd, int ttisgmt));
+static void leapadd P((time_t t, int positive, int rolling, int count));
+static void adjleap P((void));
+static void associate P((void));
+static int ciequal P((const char * ap, const char * bp));
+static void convert P((long val, char * buf));
+static void dolink P((const char * fromfile, const char * tofile));
+static void doabbr P((char * abbr, const char * format,
+ const char * letters, int isdst));
+static void eat P((const char * name, int num));
+static void eats P((const char * name, int num,
+ const char * rname, int rnum));
+static long eitol P((int i));
+static void error P((const char * message));
+static char ** getfields P((char * buf));
+static long gethms P((const char * string, const char * errstrng,
+ int signable));
+static void infile P((const char * filename));
+static void inleap P((char ** fields, int nfields));
+static void inlink P((char ** fields, int nfields));
+static void inrule P((char ** fields, int nfields));
+static int inzcont P((char ** fields, int nfields));
+static int inzone P((char ** fields, int nfields));
+static int inzsub P((char ** fields, int nfields, int iscont));
+static int itsabbr P((const char * abbr, const char * word));
+static int itsdir P((const char * name));
+static int lowerit P((int c));
+static char * memcheck P((char * tocheck));
+static int mkdirs P((char * filename));
+static void newabbr P((const char * abbr));
+static long oadd P((long t1, long t2));
+static void outzone P((const struct zone * zp, int ntzones));
+static void puttzcode P((long code, FILE * fp));
+static int rcomp P((const void * leftp, const void * rightp));
+static time_t rpytime P((const struct rule * rp, int wantedy));
+static void rulesub P((struct rule * rp,
+ const char * loyearp, const char * hiyearp,
+ const char * typep, const char * monthp,
+ const char * dayp, const char * timep));
+static void setboundaries P((void));
+static void setgroup P((gid_t *flag, const char *name));
+static void setuser P((uid_t *flag, const char *name));
+static time_t tadd P((time_t t1, long t2));
+static void usage P((void));
+static void writezone P((const char * name));
+static int yearistype P((int year, const char * type));
+
+#if !(HAVE_STRERROR - 0)
+static char * strerror P((int));
+#endif /* !(HAVE_STRERROR - 0) */
+
+static int charcnt;
+static int errors;
+static const char * filename;
+static int leapcnt;
+static int linenum;
+static time_t max_time;
+static int max_year;
+static int max_year_representable;
+static time_t min_time;
+static int min_year;
+static int min_year_representable;
+static int noise;
+static const char * rfilename;
+static int rlinenum;
+static int timecnt;
+static int typecnt;
+
+/*
+** Line codes.
+*/
+
+#define LC_RULE 0
+#define LC_ZONE 1
+#define LC_LINK 2
+#define LC_LEAP 3
+
+/*
+** Which fields are which on a Zone line.
+*/
+
+#define ZF_NAME 1
+#define ZF_GMTOFF 2
+#define ZF_RULE 3
+#define ZF_FORMAT 4
+#define ZF_TILYEAR 5
+#define ZF_TILMONTH 6
+#define ZF_TILDAY 7
+#define ZF_TILTIME 8
+#define ZONE_MINFIELDS 5
+#define ZONE_MAXFIELDS 9
+
+/*
+** Which fields are which on a Zone continuation line.
+*/
+
+#define ZFC_GMTOFF 0
+#define ZFC_RULE 1
+#define ZFC_FORMAT 2
+#define ZFC_TILYEAR 3
+#define ZFC_TILMONTH 4
+#define ZFC_TILDAY 5
+#define ZFC_TILTIME 6
+#define ZONEC_MINFIELDS 3
+#define ZONEC_MAXFIELDS 7
+
+/*
+** Which files are which on a Rule line.
+*/
+
+#define RF_NAME 1
+#define RF_LOYEAR 2
+#define RF_HIYEAR 3
+#define RF_COMMAND 4
+#define RF_MONTH 5
+#define RF_DAY 6
+#define RF_TOD 7
+#define RF_STDOFF 8
+#define RF_ABBRVAR 9
+#define RULE_FIELDS 10
+
+/*
+** Which fields are which on a Link line.
+*/
+
+#define LF_FROM 1
+#define LF_TO 2
+#define LINK_FIELDS 3
+
+/*
+** Which fields are which on a Leap line.
+*/
+
+#define LP_YEAR 1
+#define LP_MONTH 2
+#define LP_DAY 3
+#define LP_TIME 4
+#define LP_CORR 5
+#define LP_ROLL 6
+#define LEAP_FIELDS 7
+
+/*
+** Year synonyms.
+*/
+
+#define YR_MINIMUM 0
+#define YR_MAXIMUM 1
+#define YR_ONLY 2
+
+static struct rule * rules;
+static int nrules; /* number of rules */
+
+static struct zone * zones;
+static int nzones; /* number of zones */
+
+struct link {
+ const char * l_filename;
+ int l_linenum;
+ const char * l_from;
+ const char * l_to;
+};
+
+static struct link * links;
+static int nlinks;
+
+struct lookup {
+ const char * l_word;
+ const int l_value;
+};
+
+static struct lookup const * byword P((const char * string,
+ const struct lookup * lp));
+
+static struct lookup const line_codes[] = {
+ { "Rule", LC_RULE },
+ { "Zone", LC_ZONE },
+ { "Link", LC_LINK },
+ { "Leap", LC_LEAP },
+ { NULL, 0}
+};
+
+static struct lookup const mon_names[] = {
+ { "January", TM_JANUARY },
+ { "February", TM_FEBRUARY },
+ { "March", TM_MARCH },
+ { "April", TM_APRIL },
+ { "May", TM_MAY },
+ { "June", TM_JUNE },
+ { "July", TM_JULY },
+ { "August", TM_AUGUST },
+ { "September", TM_SEPTEMBER },
+ { "October", TM_OCTOBER },
+ { "November", TM_NOVEMBER },
+ { "December", TM_DECEMBER },
+ { NULL, 0 }
+};
+
+static struct lookup const wday_names[] = {
+ { "Sunday", TM_SUNDAY },
+ { "Monday", TM_MONDAY },
+ { "Tuesday", TM_TUESDAY },
+ { "Wednesday", TM_WEDNESDAY },
+ { "Thursday", TM_THURSDAY },
+ { "Friday", TM_FRIDAY },
+ { "Saturday", TM_SATURDAY },
+ { NULL, 0 }
+};
+
+static struct lookup const lasts[] = {
+ { "last-Sunday", TM_SUNDAY },
+ { "last-Monday", TM_MONDAY },
+ { "last-Tuesday", TM_TUESDAY },
+ { "last-Wednesday", TM_WEDNESDAY },
+ { "last-Thursday", TM_THURSDAY },
+ { "last-Friday", TM_FRIDAY },
+ { "last-Saturday", TM_SATURDAY },
+ { NULL, 0 }
+};
+
+static struct lookup const begin_years[] = {
+ { "minimum", YR_MINIMUM },
+ { "maximum", YR_MAXIMUM },
+ { NULL, 0 }
+};
+
+static struct lookup const end_years[] = {
+ { "minimum", YR_MINIMUM },
+ { "maximum", YR_MAXIMUM },
+ { "only", YR_ONLY },
+ { NULL, 0 }
+};
+
+static struct lookup const leap_types[] = {
+ { "Rolling", TRUE },
+ { "Stationary", FALSE },
+ { NULL, 0 }
+};
+
+static const int len_months[2][MONSPERYEAR] = {
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+};
+
+static const int len_years[2] = {
+ DAYSPERNYEAR, DAYSPERLYEAR
+};
+
+static struct attype {
+ time_t at;
+ unsigned char type;
+} attypes[TZ_MAX_TIMES];
+static long gmtoffs[TZ_MAX_TYPES];
+static char isdsts[TZ_MAX_TYPES];
+static unsigned char abbrinds[TZ_MAX_TYPES];
+static char ttisstds[TZ_MAX_TYPES];
+static char ttisgmts[TZ_MAX_TYPES];
+static char chars[TZ_MAX_CHARS];
+static time_t trans[TZ_MAX_LEAPS];
+static long corr[TZ_MAX_LEAPS];
+static char roll[TZ_MAX_LEAPS];
+
+/*
+** Memory allocation.
+*/
+
+static char *
+memcheck(ptr)
+char * const ptr;
+{
+ if (ptr == NULL)
+ errx(EXIT_FAILURE, _("memory exhausted"));
+ return ptr;
+}
+
+#define emalloc(size) memcheck(imalloc(size))
+#define erealloc(ptr, size) memcheck(irealloc((ptr), (size)))
+#define ecpyalloc(ptr) memcheck(icpyalloc(ptr))
+#define ecatalloc(oldp, newp) memcheck(icatalloc((oldp), (newp)))
+
+/*
+** Error handling.
+*/
+
+#if !(HAVE_STRERROR - 0)
+static char *
+strerror(errnum)
+int errnum;
+{
+ extern char * sys_errlist[];
+ extern int sys_nerr;
+
+ return (errnum > 0 && errnum <= sys_nerr) ?
+ sys_errlist[errnum] : _("Unknown system error");
+}
+#endif /* !(HAVE_STRERROR - 0) */
+
+static void
+eats(name, num, rname, rnum)
+const char * const name;
+const int num;
+const char * const rname;
+const int rnum;
+{
+ filename = name;
+ linenum = num;
+ rfilename = rname;
+ rlinenum = rnum;
+}
+
+static void
+eat(name, num)
+const char * const name;
+const int num;
+{
+ eats(name, num, (char *) NULL, -1);
+}
+
+static void
+error(string)
+const char * const string;
+{
+ /*
+ ** Match the format of "cc" to allow sh users to
+ ** zic ... 2>&1 | error -t "*" -v
+ ** on BSD systems.
+ */
+ (void) fprintf(stderr, _("\"%s\", line %d: %s"),
+ filename, linenum, string);
+ if (rfilename != NULL)
+ (void) fprintf(stderr, _(" (rule from \"%s\", line %d)"),
+ rfilename, rlinenum);
+ (void) fprintf(stderr, "\n");
+ ++errors;
+}
+
+static void
+warning(string)
+const char * const string;
+{
+ char * cp;
+
+ cp = ecpyalloc(_("warning: "));
+ cp = ecatalloc(cp, string);
+ error(cp);
+ ifree(cp);
+ --errors;
+}
+
+static void
+usage P((void))
+{
+ (void) fprintf(stderr, "%s\n%s\n",
+_("usage: zic [--version] [-s] [-v] [-l localtime] [-p posixrules] [-d directory]"),
+_(" [-L leapseconds] [-y yearistype] [filename ... ]"));
+ (void) exit(EXIT_FAILURE);
+}
+
+static const char * psxrules;
+static const char * lcltime;
+static const char * directory;
+static const char * leapsec;
+static const char * yitcommand;
+static int sflag = FALSE;
+static int Dflag;
+static uid_t uflag = (uid_t)-1;
+static gid_t gflag = (gid_t)-1;
+static mode_t mflag = (S_IRUSR | S_IRGRP | S_IROTH
+ | S_IWUSR);
+
+int
+main(argc, argv)
+int argc;
+char * argv[];
+{
+ register int i;
+ register int j;
+ register int c;
+
+#ifdef unix
+ (void) umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
+#endif /* defined unix */
+#if HAVE_GETTEXT - 0
+ (void) setlocale(LC_MESSAGES, "");
+#ifdef TZ_DOMAINDIR
+ (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
+#endif /* defined TEXTDOMAINDIR */
+ (void) textdomain(TZ_DOMAIN);
+#endif /* HAVE_GETTEXT - 0 */
+ for (i = 1; i < argc; ++i)
+ if (strcmp(argv[i], "--version") == 0) {
+ errx(EXIT_SUCCESS, "%s", elsieid);
+ }
+ while ((c = getopt(argc, argv, "Dd:g:l:m:p:L:u:vsy:")) != -1)
+ switch (c) {
+ default:
+ usage();
+ case 'D':
+ Dflag = 1;
+ break;
+ case 'd':
+ if (directory == NULL)
+ directory = optarg;
+ else
+ errx(EXIT_FAILURE,
+_("more than one -d option specified"));
+ break;
+ case 'g':
+ setgroup(&gflag, optarg);
+ break;
+ case 'l':
+ if (lcltime == NULL)
+ lcltime = optarg;
+ else
+ errx(EXIT_FAILURE,
+_("more than one -l option specified"));
+ break;
+ case 'm':
+ {
+ void *set = setmode(optarg);
+ if (set == NULL)
+ errx(EXIT_FAILURE,
+_("invalid file mode"));
+ mflag = getmode(set, mflag);
+ free(set);
+ break;
+ }
+ case 'p':
+ if (psxrules == NULL)
+ psxrules = optarg;
+ else
+ errx(EXIT_FAILURE,
+_("more than one -p option specified"));
+ break;
+ case 'u':
+ setuser(&uflag, optarg);
+ break;
+ case 'y':
+ if (yitcommand == NULL)
+ yitcommand = optarg;
+ else
+ errx(EXIT_FAILURE,
+_("more than one -y option specified"));
+ break;
+ case 'L':
+ if (leapsec == NULL)
+ leapsec = optarg;
+ else
+ errx(EXIT_FAILURE,
+_("more than one -L option specified"));
+ break;
+ case 'v':
+ noise = TRUE;
+ break;
+ case 's':
+ sflag = TRUE;
+ break;
+ }
+ if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
+ usage(); /* usage message by request */
+ if (directory == NULL)
+ directory = TZDIR;
+ if (yitcommand == NULL)
+ yitcommand = "yearistype";
+
+ setboundaries();
+
+ if (optind < argc && leapsec != NULL) {
+ infile(leapsec);
+ adjleap();
+ }
+
+ for (i = optind; i < argc; ++i)
+ infile(argv[i]);
+ if (errors)
+ (void) exit(EXIT_FAILURE);
+ associate();
+ for (i = 0; i < nzones; i = j) {
+ /*
+ ** Find the next non-continuation zone entry.
+ */
+ for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j)
+ continue;
+ outzone(&zones[i], j - i);
+ }
+ /*
+ ** Make links.
+ */
+ for (i = 0; i < nlinks; ++i) {
+ eat(links[i].l_filename, links[i].l_linenum);
+ dolink(links[i].l_from, links[i].l_to);
+ }
+ if (lcltime != NULL) {
+ eat("command line", 1);
+ dolink(lcltime, TZDEFAULT);
+ }
+ if (psxrules != NULL) {
+ eat("command line", 1);
+ dolink(psxrules, TZDEFRULES);
+ }
+ return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+static void
+dolink(fromfile, tofile)
+const char * const fromfile;
+const char * const tofile;
+{
+ register char * fromname;
+ register char * toname;
+
+ if (fromfile[0] == '/')
+ fromname = ecpyalloc(fromfile);
+ else {
+ fromname = ecpyalloc(directory);
+ fromname = ecatalloc(fromname, "/");
+ fromname = ecatalloc(fromname, fromfile);
+ }
+ if (tofile[0] == '/')
+ toname = ecpyalloc(tofile);
+ else {
+ toname = ecpyalloc(directory);
+ toname = ecatalloc(toname, "/");
+ toname = ecatalloc(toname, tofile);
+ }
+ /*
+ ** We get to be careful here since
+ ** there's a fair chance of root running us.
+ */
+ if (!itsdir(toname))
+ (void) remove(toname);
+ if (link(fromname, toname) != 0) {
+ int result;
+
+ if (mkdirs(toname) != 0)
+ (void) exit(EXIT_FAILURE);
+
+ result = link(fromname, toname);
+#if (HAVE_SYMLINK - 0)
+ if (result != 0 &&
+ access(fromname, F_OK) == 0 &&
+ !itsdir(fromname)) {
+ const char *s = tofile;
+ register char * symlinkcontents = NULL;
+ while ((s = strchr(s+1, '/')) != NULL)
+ symlinkcontents = ecatalloc(symlinkcontents, "../");
+ symlinkcontents = ecatalloc(symlinkcontents, fromfile);
+
+ result = symlink(symlinkcontents, toname);
+ if (result == 0)
+warning(_("hard link failed, symbolic link used"));
+ ifree(symlinkcontents);
+ }
+#endif
+ if (result != 0) {
+ err(EXIT_FAILURE, _("can't link from %s to %s"),
+ fromname, toname);
+ }
+ }
+ ifree(fromname);
+ ifree(toname);
+}
+
+#ifndef INT_MAX
+#define INT_MAX ((int) (((unsigned)~0)>>1))
+#endif /* !defined INT_MAX */
+
+#ifndef INT_MIN
+#define INT_MIN ((int) ~(((unsigned)~0)>>1))
+#endif /* !defined INT_MIN */
+
+/*
+** The tz file format currently allows at most 32-bit quantities.
+** This restriction should be removed before signed 32-bit values
+** wrap around in 2038, but unfortunately this will require a
+** change to the tz file format.
+*/
+
+#define MAX_BITS_IN_FILE 32
+#define TIME_T_BITS_IN_FILE ((TYPE_BIT(time_t) < MAX_BITS_IN_FILE) ? TYPE_BIT(time_t) : MAX_BITS_IN_FILE)
+
+static void
+setboundaries P((void))
+{
+ if (TYPE_SIGNED(time_t)) {
+ min_time = ~ (time_t) 0;
+ min_time <<= TIME_T_BITS_IN_FILE - 1;
+ max_time = ~ (time_t) 0 - min_time;
+ if (sflag)
+ min_time = 0;
+ } else {
+ min_time = 0;
+ max_time = 2 - sflag;
+ max_time <<= TIME_T_BITS_IN_FILE - 1;
+ --max_time;
+ }
+ min_year = TM_YEAR_BASE + gmtime(&min_time)->tm_year;
+ max_year = TM_YEAR_BASE + gmtime(&max_time)->tm_year;
+ min_year_representable = min_year;
+ max_year_representable = max_year;
+}
+
+static int
+itsdir(name)
+const char * const name;
+{
+ register char * myname;
+ register int accres;
+
+ myname = ecpyalloc(name);
+ myname = ecatalloc(myname, "/.");
+ accres = access(myname, F_OK);
+ ifree(myname);
+ return accres == 0;
+}
+
+/*
+** Associate sets of rules with zones.
+*/
+
+/*
+** Sort by rule name.
+*/
+
+static int
+rcomp(cp1, cp2)
+const void * cp1;
+const void * cp2;
+{
+ return strcmp(((const struct rule *) cp1)->r_name,
+ ((const struct rule *) cp2)->r_name);
+}
+
+static void
+associate P((void))
+{
+ register struct zone * zp;
+ register struct rule * rp;
+ register int base, out;
+ register int i, j;
+
+ if (nrules != 0) {
+ (void) qsort((void *) rules, (size_t) nrules,
+ (size_t) sizeof *rules, rcomp);
+ for (i = 0; i < nrules - 1; ++i) {
+ if (strcmp(rules[i].r_name,
+ rules[i + 1].r_name) != 0)
+ continue;
+ if (strcmp(rules[i].r_filename,
+ rules[i + 1].r_filename) == 0)
+ continue;
+ eat(rules[i].r_filename, rules[i].r_linenum);
+ warning(_("same rule name in multiple files"));
+ eat(rules[i + 1].r_filename, rules[i + 1].r_linenum);
+ warning(_("same rule name in multiple files"));
+ for (j = i + 2; j < nrules; ++j) {
+ if (strcmp(rules[i].r_name,
+ rules[j].r_name) != 0)
+ break;
+ if (strcmp(rules[i].r_filename,
+ rules[j].r_filename) == 0)
+ continue;
+ if (strcmp(rules[i + 1].r_filename,
+ rules[j].r_filename) == 0)
+ continue;
+ break;
+ }
+ i = j - 1;
+ }
+ }
+ for (i = 0; i < nzones; ++i) {
+ zp = &zones[i];
+ zp->z_rules = NULL;
+ zp->z_nrules = 0;
+ }
+ for (base = 0; base < nrules; base = out) {
+ rp = &rules[base];
+ for (out = base + 1; out < nrules; ++out)
+ if (strcmp(rp->r_name, rules[out].r_name) != 0)
+ break;
+ for (i = 0; i < nzones; ++i) {
+ zp = &zones[i];
+ if (strcmp(zp->z_rule, rp->r_name) != 0)
+ continue;
+ zp->z_rules = rp;
+ zp->z_nrules = out - base;
+ }
+ }
+ for (i = 0; i < nzones; ++i) {
+ zp = &zones[i];
+ if (zp->z_nrules == 0) {
+ /*
+ ** Maybe we have a local standard time offset.
+ */
+ eat(zp->z_filename, zp->z_linenum);
+ zp->z_stdoff = gethms(zp->z_rule, _("unruly zone"),
+ TRUE);
+ /*
+ ** Note, though, that if there's no rule,
+ ** a '%s' in the format is a bad thing.
+ */
+ if (strchr(zp->z_format, '%') != 0)
+ error(_("%s in ruleless zone"));
+ }
+ }
+ if (errors)
+ (void) exit(EXIT_FAILURE);
+}
+
+static void
+infile(name)
+const char * name;
+{
+ register FILE * fp;
+ register char ** fields;
+ register char * cp;
+ register const struct lookup * lp;
+ register int nfields;
+ register int wantcont;
+ register int num;
+ char buf[BUFSIZ];
+
+ if (strcmp(name, "-") == 0) {
+ name = _("standard input");
+ fp = stdin;
+ } else if ((fp = fopen(name, "r")) == NULL)
+ err(EXIT_FAILURE, _("can't open %s"), name);
+ wantcont = FALSE;
+ for (num = 1; ; ++num) {
+ eat(name, num);
+ if (fgets(buf, (int) sizeof buf, fp) != buf)
+ break;
+ cp = strchr(buf, '\n');
+ if (cp == NULL) {
+ error(_("line too long"));
+ (void) exit(EXIT_FAILURE);
+ }
+ *cp = '\0';
+ fields = getfields(buf);
+ nfields = 0;
+ while (fields[nfields] != NULL) {
+ static char nada;
+
+ if (strcmp(fields[nfields], "-") == 0)
+ fields[nfields] = &nada;
+ ++nfields;
+ }
+ if (nfields == 0) {
+ /* nothing to do */
+ } else if (wantcont) {
+ wantcont = inzcont(fields, nfields);
+ } else {
+ lp = byword(fields[0], line_codes);
+ if (lp == NULL)
+ error(_("input line of unknown type"));
+ else switch ((int) (lp->l_value)) {
+ case LC_RULE:
+ inrule(fields, nfields);
+ wantcont = FALSE;
+ break;
+ case LC_ZONE:
+ wantcont = inzone(fields, nfields);
+ break;
+ case LC_LINK:
+ inlink(fields, nfields);
+ wantcont = FALSE;
+ break;
+ case LC_LEAP:
+ if (name != leapsec)
+ warnx(
+_("leap line in non leap seconds file %s"), name);
+ else inleap(fields, nfields);
+ wantcont = FALSE;
+ break;
+ default: /* "cannot happen" */
+ errx(EXIT_FAILURE,
+_("panic: invalid l_value %d"), lp->l_value);
+ }
+ }
+ ifree((char *) fields);
+ }
+ if (ferror(fp))
+ errx(EXIT_FAILURE, _("error reading %s"), filename);
+ if (fp != stdin && fclose(fp))
+ err(EXIT_FAILURE, _("error closing %s"), filename);
+ if (wantcont)
+ error(_("expected continuation line not found"));
+}
+
+/*
+** Convert a string of one of the forms
+** h -h hh:mm -hh:mm hh:mm:ss -hh:mm:ss
+** into a number of seconds.
+** A null string maps to zero.
+** Call error with errstring and return zero on errors.
+*/
+
+static long
+gethms(string, errstring, signable)
+const char * string;
+const char * const errstring;
+const int signable;
+{
+ int hh, mm, ss, sign;
+
+ if (string == NULL || *string == '\0')
+ return 0;
+ if (!signable)
+ sign = 1;
+ else if (*string == '-') {
+ sign = -1;
+ ++string;
+ } else sign = 1;
+ if (sscanf(string, scheck(string, "%d"), &hh) == 1)
+ mm = ss = 0;
+ else if (sscanf(string, scheck(string, "%d:%d"), &hh, &mm) == 2)
+ ss = 0;
+ else if (sscanf(string, scheck(string, "%d:%d:%d"),
+ &hh, &mm, &ss) != 3) {
+ error(errstring);
+ return 0;
+ }
+ if ((hh < 0 || hh >= HOURSPERDAY ||
+ mm < 0 || mm >= MINSPERHOUR ||
+ ss < 0 || ss > SECSPERMIN) &&
+ !(hh == HOURSPERDAY && mm == 0 && ss == 0)) {
+ error(errstring);
+ return 0;
+ }
+ if (noise && hh == HOURSPERDAY)
+ warning(_("24:00 not handled by pre-1998 versions of zic"));
+ return eitol(sign) *
+ (eitol(hh * MINSPERHOUR + mm) *
+ eitol(SECSPERMIN) + eitol(ss));
+}
+
+static void
+inrule(fields, nfields)
+register char ** const fields;
+const int nfields;
+{
+ static struct rule r;
+
+ if (nfields != RULE_FIELDS) {
+ error(_("wrong number of fields on Rule line"));
+ return;
+ }
+ if (*fields[RF_NAME] == '\0') {
+ error(_("nameless rule"));
+ return;
+ }
+ r.r_filename = filename;
+ r.r_linenum = linenum;
+ r.r_stdoff = gethms(fields[RF_STDOFF], _("invalid saved time"), TRUE);
+ rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND],
+ fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]);
+ r.r_name = ecpyalloc(fields[RF_NAME]);
+ r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]);
+ rules = (struct rule *) (void *) erealloc((char *) rules,
+ (int) ((nrules + 1) * sizeof *rules));
+ rules[nrules++] = r;
+}
+
+static int
+inzone(fields, nfields)
+register char ** const fields;
+const int nfields;
+{
+ register int i;
+ static char * buf;
+
+ if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) {
+ error(_("wrong number of fields on Zone line"));
+ return FALSE;
+ }
+ if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL) {
+ buf = erealloc(buf, (int) (132 + strlen(TZDEFAULT)));
+ (void) sprintf(buf,
+_("\"Zone %s\" line and -l option are mutually exclusive"),
+ TZDEFAULT);
+ error(buf);
+ return FALSE;
+ }
+ if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) {
+ buf = erealloc(buf, (int) (132 + strlen(TZDEFRULES)));
+ (void) sprintf(buf,
+_("\"Zone %s\" line and -p option are mutually exclusive"),
+ TZDEFRULES);
+ error(buf);
+ return FALSE;
+ }
+ for (i = 0; i < nzones; ++i)
+ if (zones[i].z_name != NULL &&
+ strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) {
+ buf = erealloc(buf, (int) (132 +
+ strlen(fields[ZF_NAME]) +
+ strlen(zones[i].z_filename)));
+ (void) sprintf(buf,
+_("duplicate zone name %s (file \"%s\", line %d)"),
+ fields[ZF_NAME],
+ zones[i].z_filename,
+ zones[i].z_linenum);
+ error(buf);
+ return FALSE;
+ }
+ return inzsub(fields, nfields, FALSE);
+}
+
+static int
+inzcont(fields, nfields)
+register char ** const fields;
+const int nfields;
+{
+ if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) {
+ error(_("wrong number of fields on Zone continuation line"));
+ return FALSE;
+ }
+ return inzsub(fields, nfields, TRUE);
+}
+
+static int
+inzsub(fields, nfields, iscont)
+register char ** const fields;
+const int nfields;
+const int iscont;
+{
+ register char * cp;
+ static struct zone z;
+ register int i_gmtoff, i_rule, i_format;
+ register int i_untilyear, i_untilmonth;
+ register int i_untilday, i_untiltime;
+ register int hasuntil;
+
+ if (iscont) {
+ i_gmtoff = ZFC_GMTOFF;
+ i_rule = ZFC_RULE;
+ i_format = ZFC_FORMAT;
+ i_untilyear = ZFC_TILYEAR;
+ i_untilmonth = ZFC_TILMONTH;
+ i_untilday = ZFC_TILDAY;
+ i_untiltime = ZFC_TILTIME;
+ z.z_name = NULL;
+ } else {
+ i_gmtoff = ZF_GMTOFF;
+ i_rule = ZF_RULE;
+ i_format = ZF_FORMAT;
+ i_untilyear = ZF_TILYEAR;
+ i_untilmonth = ZF_TILMONTH;
+ i_untilday = ZF_TILDAY;
+ i_untiltime = ZF_TILTIME;
+ z.z_name = ecpyalloc(fields[ZF_NAME]);
+ }
+ z.z_filename = filename;
+ z.z_linenum = linenum;
+ z.z_gmtoff = gethms(fields[i_gmtoff], _("invalid UTC offset"), TRUE);
+ if ((cp = strchr(fields[i_format], '%')) != 0) {
+ if (*++cp != 's' || strchr(cp, '%') != 0) {
+ error(_("invalid abbreviation format"));
+ return FALSE;
+ }
+ }
+ z.z_rule = ecpyalloc(fields[i_rule]);
+ z.z_format = ecpyalloc(fields[i_format]);
+ hasuntil = nfields > i_untilyear;
+ if (hasuntil) {
+ z.z_untilrule.r_filename = filename;
+ z.z_untilrule.r_linenum = linenum;
+ rulesub(&z.z_untilrule,
+ fields[i_untilyear],
+ "only",
+ "",
+ (nfields > i_untilmonth) ?
+ fields[i_untilmonth] : "Jan",
+ (nfields > i_untilday) ? fields[i_untilday] : "1",
+ (nfields > i_untiltime) ? fields[i_untiltime] : "0");
+ z.z_untiltime = rpytime(&z.z_untilrule,
+ z.z_untilrule.r_loyear);
+ if (iscont && nzones > 0 &&
+ z.z_untiltime > min_time &&
+ z.z_untiltime < max_time &&
+ zones[nzones - 1].z_untiltime > min_time &&
+ zones[nzones - 1].z_untiltime < max_time &&
+ zones[nzones - 1].z_untiltime >= z.z_untiltime) {
+ error(_("Zone continuation line end time is not after end time of previous line"));
+ return FALSE;
+ }
+ }
+ zones = (struct zone *) (void *) erealloc((char *) zones,
+ (int) ((nzones + 1) * sizeof *zones));
+ zones[nzones++] = z;
+ /*
+ ** If there was an UNTIL field on this line,
+ ** there's more information about the zone on the next line.
+ */
+ return hasuntil;
+}
+
+static void
+inleap(fields, nfields)
+register char ** const fields;
+const int nfields;
+{
+ register const char * cp;
+ register const struct lookup * lp;
+ register int i, j;
+ int year, month, day;
+ long dayoff, tod;
+ time_t t;
+
+ if (nfields != LEAP_FIELDS) {
+ error(_("wrong number of fields on Leap line"));
+ return;
+ }
+ dayoff = 0;
+ cp = fields[LP_YEAR];
+ if (sscanf(cp, scheck(cp, "%d"), &year) != 1) {
+ /*
+ * Leapin' Lizards!
+ */
+ error(_("invalid leaping year"));
+ return;
+ }
+ j = EPOCH_YEAR;
+ while (j != year) {
+ if (year > j) {
+ i = len_years[isleap(j)];
+ ++j;
+ } else {
+ --j;
+ i = -len_years[isleap(j)];
+ }
+ dayoff = oadd(dayoff, eitol(i));
+ }
+ if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) {
+ error(_("invalid month name"));
+ return;
+ }
+ month = lp->l_value;
+ j = TM_JANUARY;
+ while (j != month) {
+ i = len_months[isleap(year)][j];
+ dayoff = oadd(dayoff, eitol(i));
+ ++j;
+ }
+ cp = fields[LP_DAY];
+ if (sscanf(cp, scheck(cp, "%d"), &day) != 1 ||
+ day <= 0 || day > len_months[isleap(year)][month]) {
+ error(_("invalid day of month"));
+ return;
+ }
+ dayoff = oadd(dayoff, eitol(day - 1));
+ if (dayoff < 0 && !TYPE_SIGNED(time_t)) {
+ error(_("time before zero"));
+ return;
+ }
+ if (dayoff < min_time / SECSPERDAY) {
+ error(_("time too small"));
+ return;
+ }
+ if (dayoff > max_time / SECSPERDAY) {
+ error(_("time too large"));
+ return;
+ }
+ t = (time_t) dayoff * SECSPERDAY;
+ tod = gethms(fields[LP_TIME], _("invalid time of day"), FALSE);
+ cp = fields[LP_CORR];
+ {
+ register int positive;
+ int count;
+
+ if (strcmp(cp, "") == 0) { /* infile() turns "-" into "" */
+ positive = FALSE;
+ count = 1;
+ } else if (strcmp(cp, "--") == 0) {
+ positive = FALSE;
+ count = 2;
+ } else if (strcmp(cp, "+") == 0) {
+ positive = TRUE;
+ count = 1;
+ } else if (strcmp(cp, "++") == 0) {
+ positive = TRUE;
+ count = 2;
+ } else {
+ error(_("illegal CORRECTION field on Leap line"));
+ return;
+ }
+ if ((lp = byword(fields[LP_ROLL], leap_types)) == NULL) {
+ error(_("illegal Rolling/Stationary field on Leap line"));
+ return;
+ }
+ leapadd(tadd(t, tod), positive, lp->l_value, count);
+ }
+}
+
+static void
+inlink(fields, nfields)
+register char ** const fields;
+const int nfields;
+{
+ struct link l;
+
+ if (nfields != LINK_FIELDS) {
+ error(_("wrong number of fields on Link line"));
+ return;
+ }
+ if (*fields[LF_FROM] == '\0') {
+ error(_("blank FROM field on Link line"));
+ return;
+ }
+ if (*fields[LF_TO] == '\0') {
+ error(_("blank TO field on Link line"));
+ return;
+ }
+ l.l_filename = filename;
+ l.l_linenum = linenum;
+ l.l_from = ecpyalloc(fields[LF_FROM]);
+ l.l_to = ecpyalloc(fields[LF_TO]);
+ links = (struct link *) (void *) erealloc((char *) links,
+ (int) ((nlinks + 1) * sizeof *links));
+ links[nlinks++] = l;
+}
+
+static void
+rulesub(rp, loyearp, hiyearp, typep, monthp, dayp, timep)
+register struct rule * const rp;
+const char * const loyearp;
+const char * const hiyearp;
+const char * const typep;
+const char * const monthp;
+const char * const dayp;
+const char * const timep;
+{
+ register const struct lookup * lp;
+ register const char * cp;
+ register char * dp;
+ register char * ep;
+
+ if ((lp = byword(monthp, mon_names)) == NULL) {
+ error(_("invalid month name"));
+ return;
+ }
+ rp->r_month = lp->l_value;
+ rp->r_todisstd = FALSE;
+ rp->r_todisgmt = FALSE;
+ dp = ecpyalloc(timep);
+ if (*dp != '\0') {
+ ep = dp + strlen(dp) - 1;
+ switch (lowerit(*ep)) {
+ case 's': /* Standard */
+ rp->r_todisstd = TRUE;
+ rp->r_todisgmt = FALSE;
+ *ep = '\0';
+ break;
+ case 'w': /* Wall */
+ rp->r_todisstd = FALSE;
+ rp->r_todisgmt = FALSE;
+ *ep = '\0';
+ break;
+ case 'g': /* Greenwich */
+ case 'u': /* Universal */
+ case 'z': /* Zulu */
+ rp->r_todisstd = TRUE;
+ rp->r_todisgmt = TRUE;
+ *ep = '\0';
+ break;
+ }
+ }
+ rp->r_tod = gethms(dp, _("invalid time of day"), FALSE);
+ ifree(dp);
+ /*
+ ** Year work.
+ */
+ cp = loyearp;
+ lp = byword(cp, begin_years);
+ if (lp != NULL) switch ((int) lp->l_value) {
+ case YR_MINIMUM:
+ rp->r_loyear = INT_MIN;
+ break;
+ case YR_MAXIMUM:
+ rp->r_loyear = INT_MAX;
+ break;
+ default: /* "cannot happen" */
+ errx(EXIT_FAILURE,
+ _("panic: invalid l_value %d"), lp->l_value);
+ } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1) {
+ error(_("invalid starting year"));
+ return;
+ } else if (noise) {
+ if (rp->r_loyear < min_year_representable)
+ warning(_("starting year too low to be represented"));
+ else if (rp->r_loyear > max_year_representable)
+ warning(_("starting year too high to be represented"));
+ }
+ cp = hiyearp;
+ if ((lp = byword(cp, end_years)) != NULL) switch ((int) lp->l_value) {
+ case YR_MINIMUM:
+ rp->r_hiyear = INT_MIN;
+ break;
+ case YR_MAXIMUM:
+ rp->r_hiyear = INT_MAX;
+ break;
+ case YR_ONLY:
+ rp->r_hiyear = rp->r_loyear;
+ break;
+ default: /* "cannot happen" */
+ errx(EXIT_FAILURE,
+ _("panic: invalid l_value %d"), lp->l_value);
+ } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1) {
+ error(_("invalid ending year"));
+ return;
+ } else if (noise) {
+ if (rp->r_loyear < min_year_representable)
+ warning(_("ending year too low to be represented"));
+ else if (rp->r_loyear > max_year_representable)
+ warning(_("ending year too high to be represented"));
+ }
+ if (rp->r_loyear > rp->r_hiyear) {
+ error(_("starting year greater than ending year"));
+ return;
+ }
+ if (*typep == '\0')
+ rp->r_yrtype = NULL;
+ else {
+ if (rp->r_loyear == rp->r_hiyear) {
+ error(_("typed single year"));
+ return;
+ }
+ rp->r_yrtype = ecpyalloc(typep);
+ }
+ if (rp->r_loyear < min_year && rp->r_loyear > 0)
+ min_year = rp->r_loyear;
+ /*
+ ** Day work.
+ ** Accept things such as:
+ ** 1
+ ** last-Sunday
+ ** Sun<=20
+ ** Sun>=7
+ */
+ dp = ecpyalloc(dayp);
+ if ((lp = byword(dp, lasts)) != NULL) {
+ rp->r_dycode = DC_DOWLEQ;
+ rp->r_wday = lp->l_value;
+ rp->r_dayofmonth = len_months[1][rp->r_month];
+ } else {
+ if ((ep = strchr(dp, '<')) != 0)
+ rp->r_dycode = DC_DOWLEQ;
+ else if ((ep = strchr(dp, '>')) != 0)
+ rp->r_dycode = DC_DOWGEQ;
+ else {
+ ep = dp;
+ rp->r_dycode = DC_DOM;
+ }
+ if (rp->r_dycode != DC_DOM) {
+ *ep++ = 0;
+ if (*ep++ != '=') {
+ error(_("invalid day of month"));
+ ifree(dp);
+ return;
+ }
+ if ((lp = byword(dp, wday_names)) == NULL) {
+ error(_("invalid weekday name"));
+ ifree(dp);
+ return;
+ }
+ rp->r_wday = lp->l_value;
+ }
+ if (sscanf(ep, scheck(ep, "%d"), &rp->r_dayofmonth) != 1 ||
+ rp->r_dayofmonth <= 0 ||
+ (rp->r_dayofmonth > len_months[1][rp->r_month])) {
+ error(_("invalid day of month"));
+ ifree(dp);
+ return;
+ }
+ }
+ ifree(dp);
+}
+
+static void
+convert(val, buf)
+const long val;
+char * const buf;
+{
+ register int i;
+ register long shift;
+
+ for (i = 0, shift = 24; i < 4; ++i, shift -= 8)
+ buf[i] = val >> shift;
+}
+
+static void
+puttzcode(val, fp)
+const long val;
+FILE * const fp;
+{
+ char buf[4];
+
+ convert(val, buf);
+ (void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp);
+}
+
+static int
+atcomp(avp, bvp)
+void * avp;
+void * bvp;
+{
+ if (((struct attype *) avp)->at < ((struct attype *) bvp)->at)
+ return -1;
+ else if (((struct attype *) avp)->at > ((struct attype *) bvp)->at)
+ return 1;
+ else return 0;
+}
+
+static void
+writezone(name)
+const char * const name;
+{
+ register FILE * fp;
+ register int i, j;
+ static char * fullname;
+ static struct tzhead tzh;
+ time_t ats[TZ_MAX_TIMES];
+ unsigned char types[TZ_MAX_TIMES];
+
+ /*
+ ** Sort.
+ */
+ if (timecnt > 1)
+ (void) qsort((void *) attypes, (size_t) timecnt,
+ (size_t) sizeof *attypes, atcomp);
+ /*
+ ** Optimize.
+ */
+ {
+ int fromi;
+ int toi;
+
+ toi = 0;
+ fromi = 0;
+ while (fromi < timecnt && attypes[fromi].at < min_time)
+ ++fromi;
+ if (isdsts[0] == 0)
+ while (fromi < timecnt && attypes[fromi].type == 0)
+ ++fromi; /* handled by default rule */
+ for ( ; fromi < timecnt; ++fromi) {
+ if (toi != 0
+ && ((attypes[fromi].at
+ + gmtoffs[attypes[toi - 1].type])
+ <= (attypes[toi - 1].at
+ + gmtoffs[toi == 1 ? 0
+ : attypes[toi - 2].type]))) {
+ attypes[toi - 1].type = attypes[fromi].type;
+ continue;
+ }
+ if (toi == 0 ||
+ attypes[toi - 1].type != attypes[fromi].type)
+ attypes[toi++] = attypes[fromi];
+ }
+ timecnt = toi;
+ }
+ /*
+ ** Transfer.
+ */
+ for (i = 0; i < timecnt; ++i) {
+ ats[i] = attypes[i].at;
+ types[i] = attypes[i].type;
+ }
+ fullname = erealloc(fullname,
+ (int) (strlen(directory) + 1 + strlen(name) + 1));
+ (void) sprintf(fullname, "%s/%s", directory, name);
+
+ /*
+ * Remove old file, if any, to snap links.
+ */
+ if (!itsdir(fullname) && remove(fullname) != 0 && errno != ENOENT)
+ err(EXIT_FAILURE, _("can't remove %s"), fullname);
+
+ if ((fp = fopen(fullname, "wb")) == NULL) {
+ if (mkdirs(fullname) != 0)
+ (void) exit(EXIT_FAILURE);
+ if ((fp = fopen(fullname, "wb")) == NULL)
+ err(EXIT_FAILURE, _("can't create %s"), fullname);
+ }
+ convert(eitol(typecnt), tzh.tzh_ttisgmtcnt);
+ convert(eitol(typecnt), tzh.tzh_ttisstdcnt);
+ convert(eitol(leapcnt), tzh.tzh_leapcnt);
+ convert(eitol(timecnt), tzh.tzh_timecnt);
+ convert(eitol(typecnt), tzh.tzh_typecnt);
+ convert(eitol(charcnt), tzh.tzh_charcnt);
+ (void) strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic);
+#define DO(field) (void) fwrite((void *) tzh.field, (size_t) sizeof tzh.field, (size_t) 1, fp)
+ DO(tzh_magic);
+ DO(tzh_reserved);
+ DO(tzh_ttisgmtcnt);
+ DO(tzh_ttisstdcnt);
+ DO(tzh_leapcnt);
+ DO(tzh_timecnt);
+ DO(tzh_typecnt);
+ DO(tzh_charcnt);
+#undef DO
+ for (i = 0; i < timecnt; ++i) {
+ j = leapcnt;
+ while (--j >= 0)
+ if (ats[i] >= trans[j]) {
+ ats[i] = tadd(ats[i], corr[j]);
+ break;
+ }
+ puttzcode((long) ats[i], fp);
+ }
+ if (timecnt > 0)
+ (void) fwrite((void *) types, (size_t) sizeof types[0],
+ (size_t) timecnt, fp);
+ for (i = 0; i < typecnt; ++i) {
+ puttzcode((long) gmtoffs[i], fp);
+ (void) putc(isdsts[i], fp);
+ (void) putc(abbrinds[i], fp);
+ }
+ if (charcnt != 0)
+ (void) fwrite((void *) chars, (size_t) sizeof chars[0],
+ (size_t) charcnt, fp);
+ for (i = 0; i < leapcnt; ++i) {
+ if (roll[i]) {
+ if (timecnt == 0 || trans[i] < ats[0]) {
+ j = 0;
+ while (isdsts[j])
+ if (++j >= typecnt) {
+ j = 0;
+ break;
+ }
+ } else {
+ j = 1;
+ while (j < timecnt && trans[i] >= ats[j])
+ ++j;
+ j = types[j - 1];
+ }
+ puttzcode((long) tadd(trans[i], -gmtoffs[j]), fp);
+ } else puttzcode((long) trans[i], fp);
+ puttzcode((long) corr[i], fp);
+ }
+ for (i = 0; i < typecnt; ++i)
+ (void) putc(ttisstds[i], fp);
+ for (i = 0; i < typecnt; ++i)
+ (void) putc(ttisgmts[i], fp);
+ if (ferror(fp) || fclose(fp))
+ errx(EXIT_FAILURE, _("error writing %s"), fullname);
+ if (chmod(fullname, mflag) < 0)
+ err(EXIT_FAILURE, _("cannot change mode of %s to %03o"),
+ fullname, (unsigned)mflag);
+ if ((uflag != (uid_t)-1 || gflag != (gid_t)-1)
+ && chown(fullname, uflag, gflag) < 0)
+ err(EXIT_FAILURE, _("cannot change ownership of %s"),
+ fullname);
+}
+
+static void
+doabbr(abbr, format, letters, isdst)
+char * const abbr;
+const char * const format;
+const char * const letters;
+const int isdst;
+{
+ if (strchr(format, '/') == NULL) {
+ if (letters == NULL)
+ (void) strcpy(abbr, format);
+ else (void) sprintf(abbr, format, letters);
+ } else if (isdst)
+ (void) strcpy(abbr, strchr(format, '/') + 1);
+ else {
+ (void) strcpy(abbr, format);
+ *strchr(abbr, '/') = '\0';
+ }
+}
+
+static void
+outzone(zpfirst, zonecount)
+const struct zone * const zpfirst;
+const int zonecount;
+{
+ register const struct zone * zp;
+ register struct rule * rp;
+ register int i, j;
+ register int usestart, useuntil;
+ register time_t starttime, untiltime;
+ register long gmtoff;
+ register long stdoff;
+ register int year;
+ register long startoff;
+ register int startttisstd;
+ register int startttisgmt;
+ register int type;
+ char startbuf[BUFSIZ];
+
+ INITIALIZE(untiltime);
+ INITIALIZE(starttime);
+ /*
+ ** Now. . .finally. . .generate some useful data!
+ */
+ timecnt = 0;
+ typecnt = 0;
+ charcnt = 0;
+ /*
+ ** Thanks to Earl Chew (earl@dnd.icp.nec.com.au)
+ ** for noting the need to unconditionally initialize startttisstd.
+ */
+ startttisstd = FALSE;
+ startttisgmt = FALSE;
+ for (i = 0; i < zonecount; ++i) {
+ /*
+ ** A guess that may well be corrected later.
+ */
+ stdoff = 0;
+ zp = &zpfirst[i];
+ usestart = i > 0 && (zp - 1)->z_untiltime > min_time;
+ useuntil = i < (zonecount - 1);
+ if (useuntil && zp->z_untiltime <= min_time)
+ continue;
+ gmtoff = zp->z_gmtoff;
+ eat(zp->z_filename, zp->z_linenum);
+ *startbuf = '\0';
+ startoff = zp->z_gmtoff;
+ if (zp->z_nrules == 0) {
+ stdoff = zp->z_stdoff;
+ doabbr(startbuf, zp->z_format,
+ (char *) NULL, stdoff != 0);
+ type = addtype(oadd(zp->z_gmtoff, stdoff),
+ startbuf, stdoff != 0, startttisstd,
+ startttisgmt);
+ if (usestart) {
+ addtt(starttime, type);
+ usestart = FALSE;
+ } else if (stdoff != 0)
+ addtt(min_time, type);
+ } else for (year = min_year; year <= max_year; ++year) {
+ if (useuntil && year > zp->z_untilrule.r_hiyear)
+ break;
+ /*
+ ** Mark which rules to do in the current year.
+ ** For those to do, calculate rpytime(rp, year);
+ */
+ for (j = 0; j < zp->z_nrules; ++j) {
+ rp = &zp->z_rules[j];
+ eats(zp->z_filename, zp->z_linenum,
+ rp->r_filename, rp->r_linenum);
+ rp->r_todo = year >= rp->r_loyear &&
+ year <= rp->r_hiyear &&
+ yearistype(year, rp->r_yrtype);
+ if (rp->r_todo)
+ rp->r_temp = rpytime(rp, year);
+ }
+ for ( ; ; ) {
+ register int k;
+ register time_t jtime, ktime;
+ register long offset;
+ char buf[BUFSIZ];
+
+ INITIALIZE(ktime);
+ if (useuntil) {
+ /*
+ ** Turn untiltime into UTC
+ ** assuming the current gmtoff and
+ ** stdoff values.
+ */
+ untiltime = zp->z_untiltime;
+ if (!zp->z_untilrule.r_todisgmt)
+ untiltime = tadd(untiltime,
+ -gmtoff);
+ if (!zp->z_untilrule.r_todisstd)
+ untiltime = tadd(untiltime,
+ -stdoff);
+ }
+ /*
+ ** Find the rule (of those to do, if any)
+ ** that takes effect earliest in the year.
+ */
+ k = -1;
+ for (j = 0; j < zp->z_nrules; ++j) {
+ rp = &zp->z_rules[j];
+ if (!rp->r_todo)
+ continue;
+ eats(zp->z_filename, zp->z_linenum,
+ rp->r_filename, rp->r_linenum);
+ offset = rp->r_todisgmt ? 0 : gmtoff;
+ if (!rp->r_todisstd)
+ offset = oadd(offset, stdoff);
+ jtime = rp->r_temp;
+ if (jtime == min_time ||
+ jtime == max_time)
+ continue;
+ jtime = tadd(jtime, -offset);
+ if (k < 0 || jtime < ktime) {
+ k = j;
+ ktime = jtime;
+ }
+ }
+ if (k < 0)
+ break; /* go on to next year */
+ rp = &zp->z_rules[k];
+ rp->r_todo = FALSE;
+ if (useuntil && ktime >= untiltime)
+ break;
+ stdoff = rp->r_stdoff;
+ if (usestart && ktime == starttime)
+ usestart = FALSE;
+ if (usestart) {
+ if (ktime < starttime) {
+ startoff = oadd(zp->z_gmtoff,
+ stdoff);
+ doabbr(startbuf, zp->z_format,
+ rp->r_abbrvar,
+ rp->r_stdoff != 0);
+ continue;
+ }
+ if (*startbuf == '\0' &&
+ startoff == oadd(zp->z_gmtoff,
+ stdoff)) {
+ doabbr(startbuf, zp->z_format,
+ rp->r_abbrvar,
+ rp->r_stdoff != 0);
+ }
+ }
+ eats(zp->z_filename, zp->z_linenum,
+ rp->r_filename, rp->r_linenum);
+ doabbr(buf, zp->z_format, rp->r_abbrvar,
+ rp->r_stdoff != 0);
+ offset = oadd(zp->z_gmtoff, rp->r_stdoff);
+ type = addtype(offset, buf, rp->r_stdoff != 0,
+ rp->r_todisstd, rp->r_todisgmt);
+ addtt(ktime, type);
+ }
+ }
+ if (usestart) {
+ if (*startbuf == '\0' &&
+ zp->z_format != NULL &&
+ strchr(zp->z_format, '%') == NULL &&
+ strchr(zp->z_format, '/') == NULL)
+ (void) strcpy(startbuf, zp->z_format);
+ eat(zp->z_filename, zp->z_linenum);
+ if (*startbuf == '\0')
+error(_("can't determine time zone abbreviation to use just after until time"));
+ else addtt(starttime,
+ addtype(startoff, startbuf,
+ startoff != zp->z_gmtoff,
+ startttisstd,
+ startttisgmt));
+ }
+ /*
+ ** Now we may get to set starttime for the next zone line.
+ */
+ if (useuntil) {
+ startttisstd = zp->z_untilrule.r_todisstd;
+ startttisgmt = zp->z_untilrule.r_todisgmt;
+ starttime = zp->z_untiltime;
+ if (!startttisstd)
+ starttime = tadd(starttime, -stdoff);
+ if (!startttisgmt)
+ starttime = tadd(starttime, -gmtoff);
+ }
+ }
+ writezone(zpfirst->z_name);
+}
+
+static void
+addtt(starttime, type)
+const time_t starttime;
+int type;
+{
+ if (starttime <= min_time ||
+ (timecnt == 1 && attypes[0].at < min_time)) {
+ gmtoffs[0] = gmtoffs[type];
+ isdsts[0] = isdsts[type];
+ ttisstds[0] = ttisstds[type];
+ ttisgmts[0] = ttisgmts[type];
+ if (abbrinds[type] != 0)
+ (void) strcpy(chars, &chars[abbrinds[type]]);
+ abbrinds[0] = 0;
+ charcnt = strlen(chars) + 1;
+ typecnt = 1;
+ timecnt = 0;
+ type = 0;
+ }
+ if (timecnt >= TZ_MAX_TIMES) {
+ error(_("too many transitions?!"));
+ (void) exit(EXIT_FAILURE);
+ }
+ attypes[timecnt].at = starttime;
+ attypes[timecnt].type = type;
+ ++timecnt;
+}
+
+static int
+addtype(gmtoff, abbr, isdst, ttisstd, ttisgmt)
+const long gmtoff;
+const char * const abbr;
+const int isdst;
+const int ttisstd;
+const int ttisgmt;
+{
+ register int i, j;
+
+ if (isdst != TRUE && isdst != FALSE) {
+ error(_("internal error - addtype called with bad isdst"));
+ (void) exit(EXIT_FAILURE);
+ }
+ if (ttisstd != TRUE && ttisstd != FALSE) {
+ error(_("internal error - addtype called with bad ttisstd"));
+ (void) exit(EXIT_FAILURE);
+ }
+ if (ttisgmt != TRUE && ttisgmt != FALSE) {
+ error(_("internal error - addtype called with bad ttisgmt"));
+ (void) exit(EXIT_FAILURE);
+ }
+ /*
+ ** See if there's already an entry for this zone type.
+ ** If so, just return its index.
+ */
+ for (i = 0; i < typecnt; ++i) {
+ if (gmtoff == gmtoffs[i] && isdst == isdsts[i] &&
+ strcmp(abbr, &chars[abbrinds[i]]) == 0 &&
+ ttisstd == ttisstds[i] &&
+ ttisgmt == ttisgmts[i])
+ return i;
+ }
+ /*
+ ** There isn't one; add a new one, unless there are already too
+ ** many.
+ */
+ if (typecnt >= TZ_MAX_TYPES) {
+ error(_("too many local time types"));
+ (void) exit(EXIT_FAILURE);
+ }
+ gmtoffs[i] = gmtoff;
+ isdsts[i] = isdst;
+ ttisstds[i] = ttisstd;
+ ttisgmts[i] = ttisgmt;
+
+ for (j = 0; j < charcnt; ++j)
+ if (strcmp(&chars[j], abbr) == 0)
+ break;
+ if (j == charcnt)
+ newabbr(abbr);
+ abbrinds[i] = j;
+ ++typecnt;
+ return i;
+}
+
+static void
+leapadd(t, positive, rolling, count)
+const time_t t;
+const int positive;
+const int rolling;
+int count;
+{
+ register int i, j;
+
+ if (leapcnt + (positive ? count : 1) > TZ_MAX_LEAPS) {
+ error(_("too many leap seconds"));
+ (void) exit(EXIT_FAILURE);
+ }
+ for (i = 0; i < leapcnt; ++i)
+ if (t <= trans[i]) {
+ if (t == trans[i]) {
+ error(_("repeated leap second moment"));
+ (void) exit(EXIT_FAILURE);
+ }
+ break;
+ }
+ do {
+ for (j = leapcnt; j > i; --j) {
+ trans[j] = trans[j - 1];
+ corr[j] = corr[j - 1];
+ roll[j] = roll[j - 1];
+ }
+ trans[i] = t;
+ corr[i] = positive ? 1L : eitol(-count);
+ roll[i] = rolling;
+ ++leapcnt;
+ } while (positive && --count != 0);
+}
+
+static void
+adjleap P((void))
+{
+ register int i;
+ register long last = 0;
+
+ /*
+ ** propagate leap seconds forward
+ */
+ for (i = 0; i < leapcnt; ++i) {
+ trans[i] = tadd(trans[i], last);
+ last = corr[i] += last;
+ }
+}
+
+static int
+yearistype(year, type)
+const int year;
+const char * const type;
+{
+ static char * buf;
+ int result;
+
+ if (type == NULL || *type == '\0')
+ return TRUE;
+ buf = erealloc(buf, (int) (132 + strlen(yitcommand) + strlen(type)));
+ (void) sprintf(buf, "%s %d %s", yitcommand, year, type);
+ result = system(buf);
+ if (WIFEXITED(result)) switch (WEXITSTATUS(result)) {
+ case 0:
+ return TRUE;
+ case 1:
+ return FALSE;
+ }
+ error(_("wild result from command execution"));
+ warnx(_("command was '%s', result was %d"), buf, result);
+ for ( ; ; )
+ (void) exit(EXIT_FAILURE);
+}
+
+static int
+lowerit(a)
+int a;
+{
+ a = (unsigned char) a;
+ return (isascii(a) && isupper(a)) ? tolower(a) : a;
+}
+
+static int
+ciequal(ap, bp) /* case-insensitive equality */
+register const char * ap;
+register const char * bp;
+{
+ while (lowerit(*ap) == lowerit(*bp++))
+ if (*ap++ == '\0')
+ return TRUE;
+ return FALSE;
+}
+
+static int
+itsabbr(abbr, word)
+register const char * abbr;
+register const char * word;
+{
+ if (lowerit(*abbr) != lowerit(*word))
+ return FALSE;
+ ++word;
+ while (*++abbr != '\0')
+ do {
+ if (*word == '\0')
+ return FALSE;
+ } while (lowerit(*word++) != lowerit(*abbr));
+ return TRUE;
+}
+
+static const struct lookup *
+byword(word, table)
+register const char * const word;
+register const struct lookup * const table;
+{
+ register const struct lookup * foundlp;
+ register const struct lookup * lp;
+
+ if (word == NULL || table == NULL)
+ return NULL;
+ /*
+ ** Look for exact match.
+ */
+ for (lp = table; lp->l_word != NULL; ++lp)
+ if (ciequal(word, lp->l_word))
+ return lp;
+ /*
+ ** Look for inexact match.
+ */
+ foundlp = NULL;
+ for (lp = table; lp->l_word != NULL; ++lp)
+ if (itsabbr(word, lp->l_word)) {
+ if (foundlp == NULL)
+ foundlp = lp;
+ else return NULL; /* multiple inexact matches */
+ }
+ return foundlp;
+}
+
+static char **
+getfields(cp)
+register char * cp;
+{
+ register char * dp;
+ register char ** array;
+ register int nsubs;
+
+ if (cp == NULL)
+ return NULL;
+ array = (char **) (void *)
+ emalloc((int) ((strlen(cp) + 1) * sizeof *array));
+ nsubs = 0;
+ for ( ; ; ) {
+ while (isascii(*cp) && isspace((unsigned char) *cp))
+ ++cp;
+ if (*cp == '\0' || *cp == '#')
+ break;
+ array[nsubs++] = dp = cp;
+ do {
+ if ((*dp = *cp++) != '"')
+ ++dp;
+ else while ((*dp = *cp++) != '"')
+ if (*dp != '\0')
+ ++dp;
+ else {
+ error(_("odd number of quotation marks"));
+ exit(EXIT_FAILURE);
+ }
+ } while (*cp != '\0' && *cp != '#' &&
+ (!isascii(*cp) || !isspace((unsigned char) *cp)));
+ if (isascii(*cp) && isspace((unsigned char) *cp))
+ ++cp;
+ *dp = '\0';
+ }
+ array[nsubs] = NULL;
+ return array;
+}
+
+static long
+oadd(t1, t2)
+const long t1;
+const long t2;
+{
+ register long t;
+
+ t = t1 + t2;
+ if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) {
+ error(_("time overflow"));
+ (void) exit(EXIT_FAILURE);
+ }
+ return t;
+}
+
+static time_t
+tadd(t1, t2)
+const time_t t1;
+const long t2;
+{
+ register time_t t;
+
+ if (t1 == max_time && t2 > 0)
+ return max_time;
+ if (t1 == min_time && t2 < 0)
+ return min_time;
+ t = t1 + t2;
+ if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) {
+ error(_("time overflow"));
+ (void) exit(EXIT_FAILURE);
+ }
+ return t;
+}
+
+/*
+** Given a rule, and a year, compute the date - in seconds since January 1,
+** 1970, 00:00 LOCAL time - in that year that the rule refers to.
+*/
+
+static time_t
+rpytime(rp, wantedy)
+register const struct rule * const rp;
+register const int wantedy;
+{
+ register int y, m, i;
+ register long dayoff; /* with a nod to Margaret O. */
+ register time_t t;
+
+ if (wantedy == INT_MIN)
+ return min_time;
+ if (wantedy == INT_MAX)
+ return max_time;
+ dayoff = 0;
+ m = TM_JANUARY;
+ y = EPOCH_YEAR;
+ while (wantedy != y) {
+ if (wantedy > y) {
+ i = len_years[isleap(y)];
+ ++y;
+ } else {
+ --y;
+ i = -len_years[isleap(y)];
+ }
+ dayoff = oadd(dayoff, eitol(i));
+ }
+ while (m != rp->r_month) {
+ i = len_months[isleap(y)][m];
+ dayoff = oadd(dayoff, eitol(i));
+ ++m;
+ }
+ i = rp->r_dayofmonth;
+ if (m == TM_FEBRUARY && i == 29 && !isleap(y)) {
+ if (rp->r_dycode == DC_DOWLEQ)
+ --i;
+ else {
+ error(_("use of 2/29 in non leap-year"));
+ (void) exit(EXIT_FAILURE);
+ }
+ }
+ --i;
+ dayoff = oadd(dayoff, eitol(i));
+ if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) {
+ register long wday;
+
+#define LDAYSPERWEEK ((long) DAYSPERWEEK)
+ wday = eitol(EPOCH_WDAY);
+ /*
+ ** Don't trust mod of negative numbers.
+ */
+ if (dayoff >= 0)
+ wday = (wday + dayoff) % LDAYSPERWEEK;
+ else {
+ wday -= ((-dayoff) % LDAYSPERWEEK);
+ if (wday < 0)
+ wday += LDAYSPERWEEK;
+ }
+ while (wday != eitol(rp->r_wday))
+ if (rp->r_dycode == DC_DOWGEQ) {
+ dayoff = oadd(dayoff, (long) 1);
+ if (++wday >= LDAYSPERWEEK)
+ wday = 0;
+ ++i;
+ } else {
+ dayoff = oadd(dayoff, (long) -1);
+ if (--wday < 0)
+ wday = LDAYSPERWEEK - 1;
+ --i;
+ }
+ if (i < 0 || i >= len_months[isleap(y)][m]) {
+ if (noise)
+ warning(_("rule goes past start/end of month--will not work with pre-2004 versions of zic"));
+ }
+ }
+ if (dayoff < 0 && !TYPE_SIGNED(time_t))
+ return min_time;
+ if (dayoff < min_time / SECSPERDAY)
+ return min_time;
+ if (dayoff > max_time / SECSPERDAY)
+ return max_time;
+ t = (time_t) dayoff * SECSPERDAY;
+ return tadd(t, rp->r_tod);
+}
+
+static void
+newabbr(string)
+const char * const string;
+{
+ register int i;
+
+ i = strlen(string) + 1;
+ if (charcnt + i > TZ_MAX_CHARS) {
+ error(_("too many, or too long, time zone abbreviations"));
+ (void) exit(EXIT_FAILURE);
+ }
+ (void) strcpy(&chars[charcnt], string);
+ charcnt += eitol(i);
+}
+
+static int
+mkdirs(argname)
+char * const argname;
+{
+ register char * name;
+ register char * cp;
+
+ if (argname == NULL || *argname == '\0' || Dflag)
+ return 0;
+ cp = name = ecpyalloc(argname);
+ while ((cp = strchr(cp + 1, '/')) != 0) {
+ *cp = '\0';
+#ifndef unix
+ /*
+ ** DOS drive specifier?
+ */
+ if (isalpha((unsigned char) name[0]) &&
+ name[1] == ':' && name[2] == '\0') {
+ *cp = '/';
+ continue;
+ }
+#endif /* !defined unix */
+ if (!itsdir(name)) {
+ /*
+ ** It doesn't seem to exist, so we try to create it.
+ ** Creation may fail because of the directory being
+ ** created by some other multiprocessor, so we get
+ ** to do extra checking.
+ */
+ if (mkdir(name, MKDIR_UMASK) != 0
+ && (errno != EEXIST || !itsdir(name))) {
+ warn(_("can't create directory %s"), name);
+ ifree(name);
+ return -1;
+ }
+ }
+ *cp = '/';
+ }
+ ifree(name);
+ return 0;
+}
+
+static long
+eitol(i)
+const int i;
+{
+ long l;
+
+ l = i;
+ if ((i < 0 && l >= 0) || (i == 0 && l != 0) || (i > 0 && l <= 0))
+ errx(EXIT_FAILURE, _("%d did not sign extend correctly"), i);
+ return l;
+}
+
+#include <grp.h>
+#include <pwd.h>
+
+static void
+setgroup(flag, name)
+ gid_t *flag;
+ const char *name;
+{
+ struct group *gr;
+
+ if (*flag != (gid_t)-1)
+ errx(EXIT_FAILURE, _("multiple -g flags specified"));
+
+ gr = getgrnam(name);
+ if (gr == 0) {
+ char *ep;
+ unsigned long ul;
+
+ ul = strtoul(name, &ep, 10);
+ if (ul == (unsigned long)(gid_t)ul && *ep == '\0') {
+ *flag = ul;
+ return;
+ }
+ errx(EXIT_FAILURE, _("group `%s' not found"), name);
+ }
+ *flag = gr->gr_gid;
+}
+
+static void
+setuser(flag, name)
+ uid_t *flag;
+ const char *name;
+{
+ struct passwd *pw;
+
+ if (*flag != (gid_t)-1)
+ errx(EXIT_FAILURE, _("multiple -u flags specified"));
+
+ pw = getpwnam(name);
+ if (pw == 0) {
+ char *ep;
+ unsigned long ul;
+
+ ul = strtoul(name, &ep, 10);
+ if (ul == (unsigned long)(gid_t)ul && *ep == '\0') {
+ *flag = ul;
+ return;
+ }
+ errx(EXIT_FAILURE, _("user `%s' not found"), name);
+ }
+ *flag = pw->pw_uid;
+}
+
+/*
+** UNIX was a registered trademark of The Open Group in 2003.
+*/
diff --git a/usr.sbin/zic/zic/Makefile b/usr.sbin/zic/zic/Makefile
new file mode 100644
index 0000000..3f3bc70
--- /dev/null
+++ b/usr.sbin/zic/zic/Makefile
@@ -0,0 +1,14 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/..
+
+PROG= zic
+MAN= zic.8
+SRCS= zic.c ialloc.c scheck.c
+
+CFLAGS+= -DTM_GMTOFF=tm_gmtoff -DTM_ZONE=tm_zone -DSTD_INSPIRED -DPCTS
+CFLAGS+= -DHAVE_LONG_DOUBLE -DTZDIR=\"/usr/share/zoneinfo\" -Demkdir=mkdir
+CFLAGS+= -DHAVE_STRERROR -DHAVE_UNISTD_H
+CFLAGS+= -I${.CURDIR}/.. -I${.CURDIR}/../../../lib/libc/stdtime
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/zzz/Makefile b/usr.sbin/zzz/Makefile
new file mode 100644
index 0000000..8c4a9be
--- /dev/null
+++ b/usr.sbin/zzz/Makefile
@@ -0,0 +1,6 @@
+# $FreeBSD$
+
+SCRIPTS=zzz.sh
+MAN= zzz.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/zzz/zzz.8 b/usr.sbin/zzz/zzz.8
new file mode 100644
index 0000000..583d5fd
--- /dev/null
+++ b/usr.sbin/zzz/zzz.8
@@ -0,0 +1,65 @@
+.\" Copyright (c) 2003 Nate Lawson
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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, 2003
+.Dt ZZZ 8
+.Os
+.Sh NAME
+.Nm zzz
+.Nd suspend an ACPI or APM system
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+The
+.Nm
+utility
+checks for
+.Tn ACPI
+or
+.Tn APM
+support and then suspends the system appropriately.
+For
+.Tn APM ,
+.Pp
+.Dl apm -z
+.Pp
+will be issued.
+For
+.Tn ACPI,
+the configured suspend state will be looked up, checked to see
+if it is supported and,
+.Pp
+.Dl acpiconf -s <state>
+.Pp
+will be issued.
+.Sh SEE ALSO
+.Xr acpi 4 ,
+.Xr apm 4 ,
+.Xr acpiconf 8 ,
+.Xr apm 8
+.Sh AUTHORS
+This manual page was written by
+.An Nate Lawson Aq njl@FreeBSD.org .
diff --git a/usr.sbin/zzz/zzz.sh b/usr.sbin/zzz/zzz.sh
new file mode 100644
index 0000000..ef9527b
--- /dev/null
+++ b/usr.sbin/zzz/zzz.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+#
+# Suspend the system using either ACPI or APM.
+# For APM, "apm -z" will be issued.
+# For ACPI, the configured suspend state will be looked up, checked to see
+# if it is supported, and "acpiconf -s <state>" will be issued.
+#
+# Mark Santcroos <marks@ripe.net>
+#
+# $FreeBSD$
+
+PATH=/sbin:/usr/sbin:/usr/bin:/bin
+
+ACPI_SUSPEND_STATE=hw.acpi.suspend_state
+ACPI_SUPPORTED_STATES=hw.acpi.supported_sleep_state
+APM_SUSPEND_DELAY=machdep.apm_suspend_delay
+
+# Check for ACPI support
+if sysctl $ACPI_SUSPEND_STATE >/dev/null 2>&1; then
+ # Get configured suspend state
+ SUSPEND_STATE=`sysctl -n $ACPI_SUSPEND_STATE `
+
+ # Get list of supported suspend states
+ SUPPORTED_STATES=`sysctl -n $ACPI_SUPPORTED_STATES `
+
+ # Check if the configured suspend state is supported by the system
+ if echo $SUPPORTED_STATES | grep $SUSPEND_STATE >/dev/null; then
+ # execute ACPI style suspend command
+ exec acpiconf -s $SUSPEND_STATE
+ else
+ echo -n "Requested suspend state $SUSPEND_STATE "
+ echo -n "is not supported. "
+ echo "Supported states: $SUPPORTED_STATES"
+ fi
+# Check for APM support
+elif sysctl $APM_SUSPEND_DELAY >/dev/null 2>&1; then
+ # Execute APM style suspend command
+ exec apm -z
+else
+ echo "Error: no ACPI or APM suspend support found."
+fi
+
+exit 1
OpenPOWER on IntegriCloud